summary refs log tree commit diff
path: root/sound
diff options
context:
space:
mode:
authorMark Brown <broonie@linaro.org>2014-03-23 14:00:41 +0000
committerMark Brown <broonie@linaro.org>2014-03-23 14:00:41 +0000
commitd66fa86956149a211db3d7ae9e9f2536b65ccde4 (patch)
tree013c71ec06c6d9710a183854ce1a0fe33530a0db /sound
parentebec909345bbb1e2d06cd0d94f65664edcc0f208 (diff)
parentdeeed33850c8a376addabbf971df433b2a1ba74c (diff)
downloadlinux-d66fa86956149a211db3d7ae9e9f2536b65ccde4.tar.gz
Merge tag 'asoc-v3.15' into asoc-next
ASoC: Updates for v3.15

Quite a busy release for ASoC this time, more on janitorial work than
exciting new features but welcome nontheless:

 - Lots of cleanups from Takashi for enumerations; the original API for
   these was error prone so he's refactored lots of code to use more
   modern APIs which avoid issues.
 - Elimination of the ASoC level wrappers for I2C and SPI moving us
   closer to converting to regmap completely and avoiding some
   randconfig hassle.
 - Provide both manually and transparently locked DAPM APIs rather than
   a mix of the two fixing some concurrency issues.
 - Start converting CODEC drivers to use separate bus interface drivers
   rather than having them all in one file helping avoid dependency
   issues.
 - DPCM support for Intel Haswell and Bay Trail platforms.
 - Lots of work on improvements for simple-card, DaVinci and the Renesas
   rcar drivers.
 - New drivers for Analog Devices ADAU1977, TI PCM512x and parts of the
   CSR SiRF SoC.

# gpg: Signature made Wed 12 Mar 2014 23:05:45 GMT using RSA key ID 7EA229BD
# gpg: Good signature from "Mark Brown <broonie@sirena.org.uk>"
# gpg:                 aka "Mark Brown <broonie@debian.org>"
# gpg:                 aka "Mark Brown <broonie@kernel.org>"
# gpg:                 aka "Mark Brown <broonie@tardis.ed.ac.uk>"
# gpg:                 aka "Mark Brown <broonie@linaro.org>"
# gpg:                 aka "Mark Brown <Mark.Brown@linaro.org>"
Diffstat (limited to 'sound')
-rw-r--r--sound/core/pcm_lib.c1
-rw-r--r--sound/soc/Kconfig1
-rw-r--r--sound/soc/Makefile1
-rw-r--r--sound/soc/atmel/Kconfig2
-rw-r--r--sound/soc/atmel/atmel_ssc_dai.c13
-rw-r--r--sound/soc/atmel/sam9g20_wm8731.c20
-rw-r--r--sound/soc/blackfin/Kconfig9
-rw-r--r--sound/soc/cirrus/Kconfig4
-rw-r--r--sound/soc/codecs/88pm860x-codec.c116
-rw-r--r--sound/soc/codecs/Kconfig183
-rw-r--r--sound/soc/codecs/Makefile33
-rw-r--r--sound/soc/codecs/ad1836.c4
-rw-r--r--sound/soc/codecs/ad193x-i2c.c54
-rw-r--r--sound/soc/codecs/ad193x-spi.c48
-rw-r--r--sound/soc/codecs/ad193x.c144
-rw-r--r--sound/soc/codecs/ad193x.h7
-rw-r--r--sound/soc/codecs/adau1373.c32
-rw-r--r--sound/soc/codecs/adau1977-i2c.c59
-rw-r--r--sound/soc/codecs/adau1977-spi.c76
-rw-r--r--sound/soc/codecs/adau1977.c1018
-rw-r--r--sound/soc/codecs/adau1977.h37
-rw-r--r--sound/soc/codecs/adav801.c53
-rw-r--r--sound/soc/codecs/adav803.c50
-rw-r--r--sound/soc/codecs/adav80x.c152
-rw-r--r--sound/soc/codecs/adav80x.h7
-rw-r--r--sound/soc/codecs/ak4104.c2
-rw-r--r--sound/soc/codecs/ak4641.c16
-rw-r--r--sound/soc/codecs/ak4671.c240
-rw-r--r--sound/soc/codecs/ak4671.h2
-rw-r--r--sound/soc/codecs/alc5623.c117
-rw-r--r--sound/soc/codecs/alc5632.c40
-rw-r--r--sound/soc/codecs/arizona.c325
-rw-r--r--sound/soc/codecs/cs4271.c63
-rw-r--r--sound/soc/codecs/cs42l51.c86
-rw-r--r--sound/soc/codecs/cs42l52.c92
-rw-r--r--sound/soc/codecs/cs42l73.c55
-rw-r--r--sound/soc/codecs/da7210.c20
-rw-r--r--sound/soc/codecs/da7213.c151
-rw-r--r--sound/soc/codecs/da732x.c167
-rw-r--r--sound/soc/codecs/da732x.h3
-rw-r--r--sound/soc/codecs/da9055.c92
-rw-r--r--sound/soc/codecs/lm49453.c16
-rw-r--r--sound/soc/codecs/max98088.c2
-rw-r--r--sound/soc/codecs/max98090.c172
-rw-r--r--sound/soc/codecs/max98095.c4
-rw-r--r--sound/soc/codecs/mc13783.c20
-rw-r--r--sound/soc/codecs/ml26124.c12
-rw-r--r--sound/soc/codecs/pcm1681.c15
-rw-r--r--sound/soc/codecs/pcm1792a.c33
-rw-r--r--sound/soc/codecs/pcm512x-i2c.c71
-rw-r--r--sound/soc/codecs/pcm512x-spi.c69
-rw-r--r--sound/soc/codecs/pcm512x.c589
-rw-r--r--sound/soc/codecs/pcm512x.h171
-rw-r--r--sound/soc/codecs/rt5631.c75
-rw-r--r--sound/soc/codecs/rt5640.c75
-rw-r--r--sound/soc/codecs/sgtl5000.c10
-rw-r--r--sound/soc/codecs/sirf-audio-codec.c533
-rw-r--r--sound/soc/codecs/sirf-audio-codec.h75
-rw-r--r--sound/soc/codecs/sn95031.c44
-rw-r--r--sound/soc/codecs/ssm2518.c14
-rw-r--r--sound/soc/codecs/ssm2602-i2c.c57
-rw-r--r--sound/soc/codecs/ssm2602-spi.c41
-rw-r--r--sound/soc/codecs/ssm2602.c173
-rw-r--r--sound/soc/codecs/ssm2602.h14
-rw-r--r--sound/soc/codecs/sta529.c2
-rw-r--r--sound/soc/codecs/stac9766.c38
-rw-r--r--sound/soc/codecs/tlv320aic23-i2c.c59
-rw-r--r--sound/soc/codecs/tlv320aic23-spi.c56
-rw-r--r--sound/soc/codecs/tlv320aic23.c71
-rw-r--r--sound/soc/codecs/tlv320aic23.h6
-rw-r--r--sound/soc/codecs/tlv320aic26.c5
-rw-r--r--sound/soc/codecs/tlv320aic32x4.c232
-rw-r--r--sound/soc/codecs/tlv320dac33.c34
-rw-r--r--sound/soc/codecs/twl4030.c101
-rw-r--r--sound/soc/codecs/twl6040.c17
-rw-r--r--sound/soc/codecs/uda1380.c40
-rw-r--r--sound/soc/codecs/wl1273.c9
-rw-r--r--sound/soc/codecs/wm2200.c18
-rw-r--r--sound/soc/codecs/wm5100.c36
-rw-r--r--sound/soc/codecs/wm5102.c28
-rw-r--r--sound/soc/codecs/wm5110.c19
-rw-r--r--sound/soc/codecs/wm8523.c4
-rw-r--r--sound/soc/codecs/wm8711.c2
-rw-r--r--sound/soc/codecs/wm8731.c4
-rw-r--r--sound/soc/codecs/wm8737.c50
-rw-r--r--sound/soc/codecs/wm8741.c34
-rw-r--r--sound/soc/codecs/wm8753.c5
-rw-r--r--sound/soc/codecs/wm8804.c2
-rw-r--r--sound/soc/codecs/wm8903.c108
-rw-r--r--sound/soc/codecs/wm8904.c77
-rw-r--r--sound/soc/codecs/wm8940.c20
-rw-r--r--sound/soc/codecs/wm8955.c11
-rw-r--r--sound/soc/codecs/wm8958-dsp2.c8
-rw-r--r--sound/soc/codecs/wm8961.c16
-rw-r--r--sound/soc/codecs/wm8962.c80
-rw-r--r--sound/soc/codecs/wm8974.c4
-rw-r--r--sound/soc/codecs/wm8978.c30
-rw-r--r--sound/soc/codecs/wm8983.c45
-rw-r--r--sound/soc/codecs/wm8985.c39
-rw-r--r--sound/soc/codecs/wm8988.c62
-rw-r--r--sound/soc/codecs/wm8990.c41
-rw-r--r--sound/soc/codecs/wm8991.c44
-rw-r--r--sound/soc/codecs/wm8993.c66
-rw-r--r--sound/soc/codecs/wm8994.c52
-rw-r--r--sound/soc/codecs/wm8995.c43
-rw-r--r--sound/soc/codecs/wm8996.c75
-rw-r--r--sound/soc/codecs/wm8997.c25
-rw-r--r--sound/soc/codecs/wm9081.c23
-rw-r--r--sound/soc/codecs/wm9705.c12
-rw-r--r--sound/soc/codecs/wm_adsp.c50
-rw-r--r--sound/soc/codecs/wm_hubs.c16
-rw-r--r--sound/soc/davinci/davinci-evm.c58
-rw-r--r--sound/soc/davinci/davinci-mcasp.c196
-rw-r--r--sound/soc/fsl/Kconfig12
-rw-r--r--sound/soc/fsl/eukrea-tlv320.c108
-rw-r--r--sound/soc/fsl/fsl_esai.c32
-rw-r--r--sound/soc/fsl/fsl_sai.c332
-rw-r--r--sound/soc/fsl/fsl_sai.h48
-rw-r--r--sound/soc/fsl/fsl_spdif.c9
-rw-r--r--sound/soc/fsl/imx-pcm-fiq.c7
-rw-r--r--sound/soc/fsl/wm1133-ev1.c11
-rw-r--r--sound/soc/generic/simple-card.c245
-rw-r--r--sound/soc/intel/Kconfig42
-rw-r--r--sound/soc/intel/Makefile27
-rw-r--r--sound/soc/intel/byt-rt5640.c187
-rw-r--r--sound/soc/intel/haswell.c235
-rw-r--r--sound/soc/intel/mfld_machine.c65
-rw-r--r--sound/soc/intel/sst-acpi.c284
-rw-r--r--sound/soc/intel/sst-baytrail-dsp.c372
-rw-r--r--sound/soc/intel/sst-baytrail-ipc.c867
-rw-r--r--sound/soc/intel/sst-baytrail-ipc.h69
-rw-r--r--sound/soc/intel/sst-baytrail-pcm.c422
-rw-r--r--sound/soc/intel/sst-dsp-priv.h309
-rw-r--r--sound/soc/intel/sst-dsp.c385
-rw-r--r--sound/soc/intel/sst-dsp.h233
-rw-r--r--sound/soc/intel/sst-firmware.c587
-rw-r--r--sound/soc/intel/sst-haswell-dsp.c517
-rw-r--r--sound/soc/intel/sst-haswell-ipc.c1785
-rw-r--r--sound/soc/intel/sst-haswell-ipc.h488
-rw-r--r--sound/soc/intel/sst-haswell-pcm.c872
-rw-r--r--sound/soc/intel/sst-mfld-dsp.h (renamed from sound/soc/intel/sst_dsp.h)8
-rw-r--r--sound/soc/intel/sst-mfld-platform.c (renamed from sound/soc/intel/sst_platform.c)8
-rw-r--r--sound/soc/intel/sst-mfld-platform.h (renamed from sound/soc/intel/sst_platform.h)4
-rw-r--r--sound/soc/omap/Kconfig4
-rw-r--r--sound/soc/omap/ams-delta.c55
-rw-r--r--sound/soc/omap/n810.c22
-rw-r--r--sound/soc/omap/rx51.c22
-rw-r--r--sound/soc/pxa/corgi.c49
-rw-r--r--sound/soc/pxa/e740_wm9705.c10
-rw-r--r--sound/soc/pxa/e750_wm9705.c10
-rw-r--r--sound/soc/pxa/e800_wm9712.c19
-rw-r--r--sound/soc/pxa/magician.c26
-rw-r--r--sound/soc/pxa/mioa701_wm9713.c19
-rw-r--r--sound/soc/pxa/poodle.c7
-rw-r--r--sound/soc/pxa/spitz.c58
-rw-r--r--sound/soc/pxa/tosa.c32
-rw-r--r--sound/soc/pxa/zylonite.c17
-rw-r--r--sound/soc/samsung/Kconfig2
-rw-r--r--sound/soc/samsung/h1940_uda1380.c7
-rw-r--r--sound/soc/samsung/neo1973_wm8753.c168
-rw-r--r--sound/soc/samsung/rx1950_uda1380.c5
-rw-r--r--sound/soc/samsung/smdk_wm8994.c2
-rw-r--r--sound/soc/samsung/tobermory.c2
-rw-r--r--sound/soc/sh/migor.c19
-rw-r--r--sound/soc/sh/rcar/Makefile2
-rw-r--r--sound/soc/sh/rcar/adg.c228
-rw-r--r--sound/soc/sh/rcar/core.c306
-rw-r--r--sound/soc/sh/rcar/gen.c98
-rw-r--r--sound/soc/sh/rcar/rsnd.h195
-rw-r--r--sound/soc/sh/rcar/scu.c384
-rw-r--r--sound/soc/sh/rcar/src.c687
-rw-r--r--sound/soc/sh/rcar/ssi.c332
-rw-r--r--sound/soc/sirf/Kconfig14
-rw-r--r--sound/soc/sirf/Makefile5
-rw-r--r--sound/soc/sirf/sirf-audio-port.c194
-rw-r--r--sound/soc/sirf/sirf-audio-port.h62
-rw-r--r--sound/soc/sirf/sirf-audio.c156
-rw-r--r--sound/soc/soc-cache.c13
-rw-r--r--sound/soc/soc-compress.c65
-rw-r--r--sound/soc/soc-core.c582
-rw-r--r--sound/soc/soc-dapm.c465
-rw-r--r--sound/soc/soc-pcm.c109
-rw-r--r--sound/soc/tegra/Kconfig2
183 files changed, 16499 insertions, 4478 deletions
diff --git a/sound/core/pcm_lib.c b/sound/core/pcm_lib.c
index a2104671f51d..5dcf88bed9b7 100644
--- a/sound/core/pcm_lib.c
+++ b/sound/core/pcm_lib.c
@@ -1242,6 +1242,7 @@ int snd_pcm_hw_constraint_mask64(struct snd_pcm_runtime *runtime, snd_pcm_hw_par
 		return -EINVAL;
 	return 0;
 }
+EXPORT_SYMBOL(snd_pcm_hw_constraint_mask64);
 
 /**
  * snd_pcm_hw_constraint_integer - apply an integer constraint to an interval
diff --git a/sound/soc/Kconfig b/sound/soc/Kconfig
index d62ce483a443..0060b31cc3f3 100644
--- a/sound/soc/Kconfig
+++ b/sound/soc/Kconfig
@@ -50,6 +50,7 @@ source "sound/soc/pxa/Kconfig"
 source "sound/soc/samsung/Kconfig"
 source "sound/soc/s6000/Kconfig"
 source "sound/soc/sh/Kconfig"
+source "sound/soc/sirf/Kconfig"
 source "sound/soc/spear/Kconfig"
 source "sound/soc/tegra/Kconfig"
 source "sound/soc/txx9/Kconfig"
diff --git a/sound/soc/Makefile b/sound/soc/Makefile
index 62a1822e77bf..5f1df02984f8 100644
--- a/sound/soc/Makefile
+++ b/sound/soc/Makefile
@@ -27,6 +27,7 @@ obj-$(CONFIG_SND_SOC)	+= pxa/
 obj-$(CONFIG_SND_SOC)	+= samsung/
 obj-$(CONFIG_SND_SOC)	+= s6000/
 obj-$(CONFIG_SND_SOC)	+= sh/
+obj-$(CONFIG_SND_SOC)	+= sirf/
 obj-$(CONFIG_SND_SOC)	+= spear/
 obj-$(CONFIG_SND_SOC)	+= tegra/
 obj-$(CONFIG_SND_SOC)	+= txx9/
diff --git a/sound/soc/atmel/Kconfig b/sound/soc/atmel/Kconfig
index e634eb78ed03..4789619a52d8 100644
--- a/sound/soc/atmel/Kconfig
+++ b/sound/soc/atmel/Kconfig
@@ -58,6 +58,6 @@ config SND_AT91_SOC_AFEB9260
 	depends on ARCH_AT91 && ATMEL_SSC && ARCH_AT91 && MACH_AFEB9260 && SND_ATMEL_SOC
 	select SND_ATMEL_SOC_PDC
 	select SND_ATMEL_SOC_SSC
-	select SND_SOC_TLV320AIC23
+	select SND_SOC_TLV320AIC23_I2C
 	help
 	  Say Y here to support sound on AFEB9260 board.
diff --git a/sound/soc/atmel/atmel_ssc_dai.c b/sound/soc/atmel/atmel_ssc_dai.c
index 1ead3c977a51..de433cfd044c 100644
--- a/sound/soc/atmel/atmel_ssc_dai.c
+++ b/sound/soc/atmel/atmel_ssc_dai.c
@@ -341,6 +341,7 @@ static int atmel_ssc_hw_params(struct snd_pcm_substream *substream,
 {
 	int id = dai->id;
 	struct atmel_ssc_info *ssc_p = &ssc_info[id];
+	struct ssc_device *ssc = ssc_p->ssc;
 	struct atmel_pcm_dma_params *dma_params;
 	int dir, channels, bits;
 	u32 tfmr, rfmr, tcmr, rcmr;
@@ -466,7 +467,8 @@ static int atmel_ssc_hw_params(struct snd_pcm_substream *substream,
 			| SSC_BF(RCMR_START, start_event)
 			| SSC_BF(RCMR_CKI, SSC_CKI_RISING)
 			| SSC_BF(RCMR_CKO, SSC_CKO_NONE)
-			| SSC_BF(RCMR_CKS, SSC_CKS_CLOCK);
+			| SSC_BF(RCMR_CKS, ssc->clk_from_rk_pin ?
+					   SSC_CKS_PIN : SSC_CKS_CLOCK);
 
 		rfmr =	  SSC_BF(RFMR_FSEDGE, SSC_FSEDGE_POSITIVE)
 			| SSC_BF(RFMR_FSOS, SSC_FSOS_NONE)
@@ -481,7 +483,8 @@ static int atmel_ssc_hw_params(struct snd_pcm_substream *substream,
 			| SSC_BF(TCMR_START, start_event)
 			| SSC_BF(TCMR_CKI, SSC_CKI_FALLING)
 			| SSC_BF(TCMR_CKO, SSC_CKO_NONE)
-			| SSC_BF(TCMR_CKS, SSC_CKS_PIN);
+			| SSC_BF(TCMR_CKS, ssc->clk_from_rk_pin ?
+					   SSC_CKS_CLOCK : SSC_CKS_PIN);
 
 		tfmr =	  SSC_BF(TFMR_FSEDGE, SSC_FSEDGE_POSITIVE)
 			| SSC_BF(TFMR_FSDEN, 0)
@@ -550,7 +553,8 @@ static int atmel_ssc_hw_params(struct snd_pcm_substream *substream,
 			| SSC_BF(RCMR_START, SSC_START_RISING_RF)
 			| SSC_BF(RCMR_CKI, SSC_CKI_RISING)
 			| SSC_BF(RCMR_CKO, SSC_CKO_NONE)
-			| SSC_BF(RCMR_CKS, SSC_CKS_PIN);
+			| SSC_BF(RCMR_CKS, ssc->clk_from_rk_pin ?
+					   SSC_CKS_PIN : SSC_CKS_CLOCK);
 
 		rfmr =	  SSC_BF(RFMR_FSEDGE, SSC_FSEDGE_POSITIVE)
 			| SSC_BF(RFMR_FSOS, SSC_FSOS_NONE)
@@ -565,7 +569,8 @@ static int atmel_ssc_hw_params(struct snd_pcm_substream *substream,
 			| SSC_BF(TCMR_START, SSC_START_RISING_RF)
 			| SSC_BF(TCMR_CKI, SSC_CKI_FALLING)
 			| SSC_BF(TCMR_CKO, SSC_CKO_NONE)
-			| SSC_BF(TCMR_CKS, SSC_CKS_PIN);
+			| SSC_BF(RCMR_CKS, ssc->clk_from_rk_pin ?
+					   SSC_CKS_CLOCK : SSC_CKS_PIN);
 
 		tfmr =	  SSC_BF(TFMR_FSEDGE, SSC_FSEDGE_POSITIVE)
 			| SSC_BF(TFMR_FSDEN, 0)
diff --git a/sound/soc/atmel/sam9g20_wm8731.c b/sound/soc/atmel/sam9g20_wm8731.c
index f15bff1548f8..174bd546c08b 100644
--- a/sound/soc/atmel/sam9g20_wm8731.c
+++ b/sound/soc/atmel/sam9g20_wm8731.c
@@ -155,25 +155,14 @@ static int at91sam9g20ek_wm8731_init(struct snd_soc_pcm_runtime *rtd)
 		return ret;
 	}
 
-	/* Add specific widgets */
-	snd_soc_dapm_new_controls(dapm, at91sam9g20ek_dapm_widgets,
-				  ARRAY_SIZE(at91sam9g20ek_dapm_widgets));
-	/* Set up specific audio path interconnects */
-	snd_soc_dapm_add_routes(dapm, intercon, ARRAY_SIZE(intercon));
-
 	/* not connected */
 	snd_soc_dapm_nc_pin(dapm, "RLINEIN");
 	snd_soc_dapm_nc_pin(dapm, "LLINEIN");
 
-#ifdef ENABLE_MIC_INPUT
-	snd_soc_dapm_enable_pin(dapm, "Int Mic");
-#else
-	snd_soc_dapm_nc_pin(dapm, "Int Mic");
+#ifndef ENABLE_MIC_INPUT
+	snd_soc_dapm_nc_pin(&rtd->card->dapm, "Int Mic");
 #endif
 
-	/* always connected */
-	snd_soc_dapm_enable_pin(dapm, "Ext Spk");
-
 	return 0;
 }
 
@@ -194,6 +183,11 @@ static struct snd_soc_card snd_soc_at91sam9g20ek = {
 	.dai_link = &at91sam9g20ek_dai,
 	.num_links = 1,
 	.set_bias_level = at91sam9g20ek_set_bias_level,
+
+	.dapm_widgets = at91sam9g20ek_dapm_widgets,
+	.num_dapm_widgets = ARRAY_SIZE(at91sam9g20ek_dapm_widgets),
+	.dapm_routes = intercon,
+	.num_dapm_routes = ARRAY_SIZE(intercon),
 };
 
 static int at91sam9g20ek_audio_probe(struct platform_device *pdev)
diff --git a/sound/soc/blackfin/Kconfig b/sound/soc/blackfin/Kconfig
index 4544d8eb1452..6347d5910138 100644
--- a/sound/soc/blackfin/Kconfig
+++ b/sound/soc/blackfin/Kconfig
@@ -14,7 +14,8 @@ config SND_BF5XX_SOC_SSM2602
 	depends on SND_BF5XX_I2S && SND_SOC_I2C_AND_SPI
 	select SND_BF5XX_SOC_I2S if !BF60x
 	select SND_BF6XX_SOC_I2S if BF60x
-	select SND_SOC_SSM2602
+	select SND_SOC_SSM2602_SPI if SPI_MASTER
+	select SND_SOC_SSM2602_I2C if I2C
 	help
 	  Say Y if you want to add support for the Analog Devices
 	  SSM2602 Audio Codec Add-On Card.
@@ -46,7 +47,8 @@ config SND_SOC_BFIN_EVAL_ADAV80X
 	tristate "Support for the EVAL-ADAV80X boards on Blackfin eval boards"
 	depends on SND_BF5XX_I2S && SND_SOC_I2C_AND_SPI
 	select SND_BF5XX_SOC_I2S
-	select SND_SOC_ADAV80X
+	select SND_SOC_ADAV801 if SPI_MASTER
+	select SND_SOC_ADAV803 if I2C
 	help
 	  Say Y if you want to add support for the Analog Devices EVAL-ADAV801 or
 	  EVAL-ADAV803 board connected to one of the Blackfin evaluation boards
@@ -67,7 +69,8 @@ config SND_BF5XX_SOC_AD193X
 	tristate "SoC AD193X Audio support for Blackfin"
 	depends on SND_BF5XX_I2S && SND_SOC_I2C_AND_SPI
 	select SND_BF5XX_SOC_I2S
-	select SND_SOC_AD193X
+	select SND_SOC_AD193X_I2C if I2C
+	select SND_SOC_AD193X_SPI if SPI_MASTER
 	help
 	  Say Y if you want to add support for AD193X codec on Blackfin.
 	  This driver supports AD1936, AD1937, AD1938 and AD1939.
diff --git a/sound/soc/cirrus/Kconfig b/sound/soc/cirrus/Kconfig
index 06f938deda15..5477c5475923 100644
--- a/sound/soc/cirrus/Kconfig
+++ b/sound/soc/cirrus/Kconfig
@@ -1,6 +1,6 @@
 config SND_EP93XX_SOC
 	tristate "SoC Audio support for the Cirrus Logic EP93xx series"
-	depends on (ARCH_EP93XX || COMPILE_TEST) && SND_SOC
+	depends on ARCH_EP93XX || COMPILE_TEST
 	select SND_SOC_GENERIC_DMAENGINE_PCM
 	help
 	  Say Y or M if you want to add support for codecs attached to
@@ -18,7 +18,7 @@ config SND_EP93XX_SOC_SNAPPERCL15
         tristate "SoC Audio support for Bluewater Systems Snapper CL15 module"
         depends on SND_EP93XX_SOC && MACH_SNAPPER_CL15
         select SND_EP93XX_SOC_I2S
-        select SND_SOC_TLV320AIC23
+        select SND_SOC_TLV320AIC23_I2C
         help
           Say Y or M here if you want to add support for I2S audio on the
           Bluewater Systems Snapper CL15 module.
diff --git a/sound/soc/codecs/88pm860x-codec.c b/sound/soc/codecs/88pm860x-codec.c
index 647a72cda005..8703244ee9fb 100644
--- a/sound/soc/codecs/88pm860x-codec.c
+++ b/sound/soc/codecs/88pm860x-codec.c
@@ -448,38 +448,38 @@ static const char *pm860x_opamp_texts[] = {"-50%", "-25%", "0%", "75%"};
 
 static const char *pm860x_pa_texts[] = {"-33%", "0%", "33%", "66%"};
 
-static const struct soc_enum pm860x_hs1_opamp_enum =
-	SOC_ENUM_SINGLE(PM860X_HS1_CTRL, 5, 4, pm860x_opamp_texts);
+static SOC_ENUM_SINGLE_DECL(pm860x_hs1_opamp_enum,
+			    PM860X_HS1_CTRL, 5, pm860x_opamp_texts);
 
-static const struct soc_enum pm860x_hs2_opamp_enum =
-	SOC_ENUM_SINGLE(PM860X_HS2_CTRL, 5, 4, pm860x_opamp_texts);
+static SOC_ENUM_SINGLE_DECL(pm860x_hs2_opamp_enum,
+			    PM860X_HS2_CTRL, 5, pm860x_opamp_texts);
 
-static const struct soc_enum pm860x_hs1_pa_enum =
-	SOC_ENUM_SINGLE(PM860X_HS1_CTRL, 3, 4, pm860x_pa_texts);
+static SOC_ENUM_SINGLE_DECL(pm860x_hs1_pa_enum,
+			    PM860X_HS1_CTRL, 3, pm860x_pa_texts);
 
-static const struct soc_enum pm860x_hs2_pa_enum =
-	SOC_ENUM_SINGLE(PM860X_HS2_CTRL, 3, 4, pm860x_pa_texts);
+static SOC_ENUM_SINGLE_DECL(pm860x_hs2_pa_enum,
+			    PM860X_HS2_CTRL, 3, pm860x_pa_texts);
 
-static const struct soc_enum pm860x_lo1_opamp_enum =
-	SOC_ENUM_SINGLE(PM860X_LO1_CTRL, 5, 4, pm860x_opamp_texts);
+static SOC_ENUM_SINGLE_DECL(pm860x_lo1_opamp_enum,
+			    PM860X_LO1_CTRL, 5, pm860x_opamp_texts);
 
-static const struct soc_enum pm860x_lo2_opamp_enum =
-	SOC_ENUM_SINGLE(PM860X_LO2_CTRL, 5, 4, pm860x_opamp_texts);
+static SOC_ENUM_SINGLE_DECL(pm860x_lo2_opamp_enum,
+			    PM860X_LO2_CTRL, 5, pm860x_opamp_texts);
 
-static const struct soc_enum pm860x_lo1_pa_enum =
-	SOC_ENUM_SINGLE(PM860X_LO1_CTRL, 3, 4, pm860x_pa_texts);
+static SOC_ENUM_SINGLE_DECL(pm860x_lo1_pa_enum,
+			    PM860X_LO1_CTRL, 3, pm860x_pa_texts);
 
-static const struct soc_enum pm860x_lo2_pa_enum =
-	SOC_ENUM_SINGLE(PM860X_LO2_CTRL, 3, 4, pm860x_pa_texts);
+static SOC_ENUM_SINGLE_DECL(pm860x_lo2_pa_enum,
+			    PM860X_LO2_CTRL, 3, pm860x_pa_texts);
 
-static const struct soc_enum pm860x_spk_pa_enum =
-	SOC_ENUM_SINGLE(PM860X_EAR_CTRL_1, 5, 4, pm860x_pa_texts);
+static SOC_ENUM_SINGLE_DECL(pm860x_spk_pa_enum,
+			    PM860X_EAR_CTRL_1, 5, pm860x_pa_texts);
 
-static const struct soc_enum pm860x_ear_pa_enum =
-	SOC_ENUM_SINGLE(PM860X_EAR_CTRL_2, 0, 4, pm860x_pa_texts);
+static SOC_ENUM_SINGLE_DECL(pm860x_ear_pa_enum,
+			    PM860X_EAR_CTRL_2, 0, pm860x_pa_texts);
 
-static const struct soc_enum pm860x_spk_ear_opamp_enum =
-	SOC_ENUM_SINGLE(PM860X_EAR_CTRL_1, 3, 4, pm860x_opamp_texts);
+static SOC_ENUM_SINGLE_DECL(pm860x_spk_ear_opamp_enum,
+			    PM860X_EAR_CTRL_1, 3, pm860x_opamp_texts);
 
 static const struct snd_kcontrol_new pm860x_snd_controls[] = {
 	SOC_DOUBLE_R_TLV("ADC Capture Volume", PM860X_ADC_ANA_2,
@@ -561,8 +561,8 @@ static const char *aif1_text[] = {
 	"PCM L", "PCM R",
 };
 
-static const struct soc_enum aif1_enum =
-	SOC_ENUM_SINGLE(PM860X_PCM_IFACE_3, 6, 2, aif1_text);
+static SOC_ENUM_SINGLE_DECL(aif1_enum,
+			    PM860X_PCM_IFACE_3, 6, aif1_text);
 
 static const struct snd_kcontrol_new aif1_mux =
 	SOC_DAPM_ENUM("PCM Mux", aif1_enum);
@@ -572,8 +572,8 @@ static const char *i2s_din_text[] = {
 	"DIN", "DIN1",
 };
 
-static const struct soc_enum i2s_din_enum =
-	SOC_ENUM_SINGLE(PM860X_I2S_IFACE_3, 1, 2, i2s_din_text);
+static SOC_ENUM_SINGLE_DECL(i2s_din_enum,
+			    PM860X_I2S_IFACE_3, 1, i2s_din_text);
 
 static const struct snd_kcontrol_new i2s_din_mux =
 	SOC_DAPM_ENUM("I2S DIN Mux", i2s_din_enum);
@@ -583,8 +583,8 @@ static const char *i2s_mic_text[] = {
 	"Ex PA", "ADC",
 };
 
-static const struct soc_enum i2s_mic_enum =
-	SOC_ENUM_SINGLE(PM860X_I2S_IFACE_3, 4, 2, i2s_mic_text);
+static SOC_ENUM_SINGLE_DECL(i2s_mic_enum,
+			    PM860X_I2S_IFACE_3, 4, i2s_mic_text);
 
 static const struct snd_kcontrol_new i2s_mic_mux =
 	SOC_DAPM_ENUM("I2S Mic Mux", i2s_mic_enum);
@@ -594,8 +594,8 @@ static const char *adcl_text[] = {
 	"ADCR", "ADCL",
 };
 
-static const struct soc_enum adcl_enum =
-	SOC_ENUM_SINGLE(PM860X_PCM_IFACE_3, 4, 2, adcl_text);
+static SOC_ENUM_SINGLE_DECL(adcl_enum,
+			    PM860X_PCM_IFACE_3, 4, adcl_text);
 
 static const struct snd_kcontrol_new adcl_mux =
 	SOC_DAPM_ENUM("ADC Left Mux", adcl_enum);
@@ -605,8 +605,8 @@ static const char *adcr_text[] = {
 	"ADCL", "ADCR",
 };
 
-static const struct soc_enum adcr_enum =
-	SOC_ENUM_SINGLE(PM860X_PCM_IFACE_3, 2, 2, adcr_text);
+static SOC_ENUM_SINGLE_DECL(adcr_enum,
+			    PM860X_PCM_IFACE_3, 2, adcr_text);
 
 static const struct snd_kcontrol_new adcr_mux =
 	SOC_DAPM_ENUM("ADC Right Mux", adcr_enum);
@@ -616,8 +616,8 @@ static const char *adcr_ec_text[] = {
 	"ADCR", "EC",
 };
 
-static const struct soc_enum adcr_ec_enum =
-	SOC_ENUM_SINGLE(PM860X_ADC_EN_2, 3, 2, adcr_ec_text);
+static SOC_ENUM_SINGLE_DECL(adcr_ec_enum,
+			    PM860X_ADC_EN_2, 3, adcr_ec_text);
 
 static const struct snd_kcontrol_new adcr_ec_mux =
 	SOC_DAPM_ENUM("ADCR EC Mux", adcr_ec_enum);
@@ -627,8 +627,8 @@ static const char *ec_text[] = {
 	"Left", "Right", "Left + Right",
 };
 
-static const struct soc_enum ec_enum =
-	SOC_ENUM_SINGLE(PM860X_EC_PATH, 1, 3, ec_text);
+static SOC_ENUM_SINGLE_DECL(ec_enum,
+			    PM860X_EC_PATH, 1, ec_text);
 
 static const struct snd_kcontrol_new ec_mux =
 	SOC_DAPM_ENUM("EC Mux", ec_enum);
@@ -638,36 +638,36 @@ static const char *dac_text[] = {
 };
 
 /* DAC Headset 1 Mux / Mux10 */
-static const struct soc_enum dac_hs1_enum =
-	SOC_ENUM_SINGLE(PM860X_ANA_INPUT_SEL_1, 0, 4, dac_text);
+static SOC_ENUM_SINGLE_DECL(dac_hs1_enum,
+			    PM860X_ANA_INPUT_SEL_1, 0, dac_text);
 
 static const struct snd_kcontrol_new dac_hs1_mux =
 	SOC_DAPM_ENUM("DAC HS1 Mux", dac_hs1_enum);
 
 /* DAC Headset 2 Mux / Mux11 */
-static const struct soc_enum dac_hs2_enum =
-	SOC_ENUM_SINGLE(PM860X_ANA_INPUT_SEL_1, 2, 4, dac_text);
+static SOC_ENUM_SINGLE_DECL(dac_hs2_enum,
+			    PM860X_ANA_INPUT_SEL_1, 2, dac_text);
 
 static const struct snd_kcontrol_new dac_hs2_mux =
 	SOC_DAPM_ENUM("DAC HS2 Mux", dac_hs2_enum);
 
 /* DAC Lineout 1 Mux / Mux12 */
-static const struct soc_enum dac_lo1_enum =
-	SOC_ENUM_SINGLE(PM860X_ANA_INPUT_SEL_1, 4, 4, dac_text);
+static SOC_ENUM_SINGLE_DECL(dac_lo1_enum,
+			    PM860X_ANA_INPUT_SEL_1, 4, dac_text);
 
 static const struct snd_kcontrol_new dac_lo1_mux =
 	SOC_DAPM_ENUM("DAC LO1 Mux", dac_lo1_enum);
 
 /* DAC Lineout 2 Mux / Mux13 */
-static const struct soc_enum dac_lo2_enum =
-	SOC_ENUM_SINGLE(PM860X_ANA_INPUT_SEL_1, 6, 4, dac_text);
+static SOC_ENUM_SINGLE_DECL(dac_lo2_enum,
+			    PM860X_ANA_INPUT_SEL_1, 6, dac_text);
 
 static const struct snd_kcontrol_new dac_lo2_mux =
 	SOC_DAPM_ENUM("DAC LO2 Mux", dac_lo2_enum);
 
 /* DAC Spearker Earphone Mux / Mux14 */
-static const struct soc_enum dac_spk_ear_enum =
-	SOC_ENUM_SINGLE(PM860X_ANA_INPUT_SEL_2, 0, 4, dac_text);
+static SOC_ENUM_SINGLE_DECL(dac_spk_ear_enum,
+			    PM860X_ANA_INPUT_SEL_2, 0, dac_text);
 
 static const struct snd_kcontrol_new dac_spk_ear_mux =
 	SOC_DAPM_ENUM("DAC SP Mux", dac_spk_ear_enum);
@@ -677,29 +677,29 @@ static const char *in_text[] = {
 	"Digital", "Analog",
 };
 
-static const struct soc_enum hs1_enum =
-	SOC_ENUM_SINGLE(PM860X_ANA_TO_ANA, 0, 2, in_text);
+static SOC_ENUM_SINGLE_DECL(hs1_enum,
+			    PM860X_ANA_TO_ANA, 0, in_text);
 
 static const struct snd_kcontrol_new hs1_mux =
 	SOC_DAPM_ENUM("Headset1 Mux", hs1_enum);
 
 /* Headset 2 Mux / Mux16 */
-static const struct soc_enum hs2_enum =
-	SOC_ENUM_SINGLE(PM860X_ANA_TO_ANA, 1, 2, in_text);
+static SOC_ENUM_SINGLE_DECL(hs2_enum,
+			    PM860X_ANA_TO_ANA, 1, in_text);
 
 static const struct snd_kcontrol_new hs2_mux =
 	SOC_DAPM_ENUM("Headset2 Mux", hs2_enum);
 
 /* Lineout 1 Mux / Mux17 */
-static const struct soc_enum lo1_enum =
-	SOC_ENUM_SINGLE(PM860X_ANA_TO_ANA, 2, 2, in_text);
+static SOC_ENUM_SINGLE_DECL(lo1_enum,
+			    PM860X_ANA_TO_ANA, 2, in_text);
 
 static const struct snd_kcontrol_new lo1_mux =
 	SOC_DAPM_ENUM("Lineout1 Mux", lo1_enum);
 
 /* Lineout 2 Mux / Mux18 */
-static const struct soc_enum lo2_enum =
-	SOC_ENUM_SINGLE(PM860X_ANA_TO_ANA, 3, 2, in_text);
+static SOC_ENUM_SINGLE_DECL(lo2_enum,
+			    PM860X_ANA_TO_ANA, 3, in_text);
 
 static const struct snd_kcontrol_new lo2_mux =
 	SOC_DAPM_ENUM("Lineout2 Mux", lo2_enum);
@@ -709,8 +709,8 @@ static const char *spk_text[] = {
 	"Earpiece", "Speaker",
 };
 
-static const struct soc_enum spk_enum =
-	SOC_ENUM_SINGLE(PM860X_ANA_TO_ANA, 6, 2, spk_text);
+static SOC_ENUM_SINGLE_DECL(spk_enum,
+			    PM860X_ANA_TO_ANA, 6, spk_text);
 
 static const struct snd_kcontrol_new spk_demux =
 	SOC_DAPM_ENUM("Speaker Earpiece Demux", spk_enum);
@@ -720,8 +720,8 @@ static const char *mic_text[] = {
 	"Mic 1", "Mic 2",
 };
 
-static const struct soc_enum mic_enum =
-	SOC_ENUM_SINGLE(PM860X_ADC_ANA_4, 4, 2, mic_text);
+static SOC_ENUM_SINGLE_DECL(mic_enum,
+			    PM860X_ADC_ANA_4, 4, mic_text);
 
 static const struct snd_kcontrol_new mic_mux =
 	SOC_DAPM_ENUM("MIC Mux", mic_enum);
diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig
index 983d087aa92a..32d7a6f04b7d 100644
--- a/sound/soc/codecs/Kconfig
+++ b/sound/soc/codecs/Kconfig
@@ -8,6 +8,8 @@ config SND_SOC_I2C_AND_SPI
 	default y if I2C=y
 	default y if SPI_MASTER=y
 
+menu "CODEC drivers"
+
 config SND_SOC_ALL_CODECS
 	tristate "Build all ASoC CODEC drivers"
 	depends on COMPILE_TEST
@@ -16,15 +18,20 @@ config SND_SOC_ALL_CODECS
 	select SND_SOC_AB8500_CODEC if ABX500_CORE
 	select SND_SOC_AC97_CODEC if SND_SOC_AC97_BUS
 	select SND_SOC_AD1836 if SPI_MASTER
-	select SND_SOC_AD193X if SND_SOC_I2C_AND_SPI
+	select SND_SOC_AD193X_SPI if SPI_MASTER
+	select SND_SOC_AD193X_I2C if I2C
 	select SND_SOC_AD1980 if SND_SOC_AC97_BUS
 	select SND_SOC_AD73311
 	select SND_SOC_ADAU1373 if I2C
-	select SND_SOC_ADAV80X if SND_SOC_I2C_AND_SPI
+	select SND_SOC_ADAV801 if SPI_MASTER
+	select SND_SOC_ADAV803 if I2C
+	select SND_SOC_ADAU1977_SPI if SPI_MASTER
+	select SND_SOC_ADAU1977_I2C if I2C
 	select SND_SOC_ADAU1701 if I2C
 	select SND_SOC_ADS117X
 	select SND_SOC_AK4104 if SPI_MASTER
 	select SND_SOC_AK4535 if I2C
+	select SND_SOC_AK4554
 	select SND_SOC_AK4641 if I2C
 	select SND_SOC_AK4642 if I2C
 	select SND_SOC_AK4671 if I2C
@@ -59,19 +66,24 @@ config SND_SOC_ALL_CODECS
 	select SND_SOC_PCM1681 if I2C
 	select SND_SOC_PCM1792A if SPI_MASTER
 	select SND_SOC_PCM3008
+	select SND_SOC_PCM512x_I2C if I2C
+	select SND_SOC_PCM512x_SPI if SPI_MASTER
 	select SND_SOC_RT5631 if I2C
 	select SND_SOC_RT5640 if I2C
 	select SND_SOC_SGTL5000 if I2C
 	select SND_SOC_SI476X if MFD_SI476X_CORE
+	select SND_SOC_SIRF_AUDIO_CODEC
 	select SND_SOC_SN95031 if INTEL_SCU_IPC
 	select SND_SOC_SPDIF
 	select SND_SOC_SSM2518 if I2C
-	select SND_SOC_SSM2602 if SND_SOC_I2C_AND_SPI
+	select SND_SOC_SSM2602_SPI if SPI_MASTER
+	select SND_SOC_SSM2602_I2C 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_TAS5086 if I2C
-	select SND_SOC_TLV320AIC23 if I2C
+	select SND_SOC_TLV320AIC23_I2C if I2C
+	select SND_SOC_TLV320AIC23_SPI if SPI_MASTER
 	select SND_SOC_TLV320AIC26 if SPI_MASTER
 	select SND_SOC_TLV320AIC32X4 if I2C
 	select SND_SOC_TLV320AIC3X if I2C
@@ -182,6 +194,14 @@ config SND_SOC_AD1836
 config SND_SOC_AD193X
 	tristate
 
+config SND_SOC_AD193X_SPI
+	tristate
+	select SND_SOC_AD193X
+
+config SND_SOC_AD193X_I2C
+	tristate
+	select SND_SOC_AD193X
+
 config SND_SOC_AD1980
 	tristate
 
@@ -189,41 +209,66 @@ config SND_SOC_AD73311
 	tristate
 
 config SND_SOC_ADAU1701
+	tristate "Analog Devices ADAU1701 CODEC"
+	depends on I2C
 	select SND_SOC_SIGMADSP
-	tristate
 
 config SND_SOC_ADAU1373
 	tristate
 
+config SND_SOC_ADAU1977
+	tristate
+
+config SND_SOC_ADAU1977_SPI
+	tristate
+	select SND_SOC_ADAU1977
+	select REGMAP_SPI
+
+config SND_SOC_ADAU1977_I2C
+	tristate
+	select SND_SOC_ADAU1977
+	select REGMAP_I2C
+
 config SND_SOC_ADAV80X
 	tristate
 
+config SND_SOC_ADAV801
+	tristate
+	select SND_SOC_ADAV80X
+
+config SND_SOC_ADAV803
+	tristate
+	select SND_SOC_ADAV80X
+
 config SND_SOC_ADS117X
 	tristate
 
 config SND_SOC_AK4104
-	tristate
+	tristate "AKM AK4104 CODEC"
+	depends on SPI_MASTER
 
 config SND_SOC_AK4535
 	tristate
 
 config SND_SOC_AK4554
-	tristate
+	tristate "AKM AK4554 CODEC"
 
 config SND_SOC_AK4641
 	tristate
 
 config SND_SOC_AK4642
-	tristate
+	tristate "AKM AK4642 CODEC"
+	depends on I2C
 
 config SND_SOC_AK4671
 	tristate
 
 config SND_SOC_AK5386
-	tristate
+	tristate "AKM AK5638 CODEC"
 
 config SND_SOC_ALC5623
        tristate
+
 config SND_SOC_ALC5632
 	tristate
 
@@ -234,14 +279,17 @@ config SND_SOC_CS42L51
 	tristate
 
 config SND_SOC_CS42L52
-	tristate
+	tristate "Cirrus Logic CS42L52 CODEC"
+	depends on I2C
 
 config SND_SOC_CS42L73
-	tristate
+	tristate "Cirrus Logic CS42L73 CODEC"
+	depends on I2C
 
 # Cirrus Logic CS4270 Codec
 config SND_SOC_CS4270
-	tristate
+	tristate "Cirrus Logic CS4270 CODEC"
+	depends on I2C
 
 # Cirrus Logic CS4270 Codec VD = 3.3V Errata
 # Select if you are affected by the errata where the part will not function
@@ -252,7 +300,8 @@ config SND_SOC_CS4270_VD33_ERRATA
 	depends on SND_SOC_CS4270
 
 config SND_SOC_CS4271
-	tristate
+	tristate "Cirrus Logic CS4271 CODEC"
+	depends on SND_SOC_I2C_AND_SPI
 
 config SND_SOC_CX20442
 	tristate
@@ -283,6 +332,9 @@ config SND_SOC_BT_SCO
 config SND_SOC_DMIC
 	tristate
 
+config SND_SOC_HDMI_CODEC
+       tristate "HDMI stub CODEC"
+
 config SND_SOC_ISABELLE
         tristate
 
@@ -301,18 +353,32 @@ config SND_SOC_MAX98095
 config SND_SOC_MAX9850
 	tristate
 
-config SND_SOC_HDMI_CODEC
-       tristate
-
 config SND_SOC_PCM1681
-       tristate
+	tristate "Texas Instruments PCM1681 CODEC"
+	depends on I2C
 
 config SND_SOC_PCM1792A
-       tristate
+	tristate "Texas Instruments PCM1792A CODEC"
+	depends on SPI_MASTER
 
 config SND_SOC_PCM3008
        tristate
 
+config SND_SOC_PCM512x
+	tristate
+
+config SND_SOC_PCM512x_I2C
+	tristate "Texas Instruments PCM512x CODECs - I2C"
+	depends on I2C
+	select SND_SOC_PCM512x
+	select REGMAP_I2C
+
+config SND_SOC_PCM512x_SPI
+	tristate "Texas Instruments PCM512x CODECs - SPI"
+	depends on SPI_MASTER
+	select SND_SOC_PCM512x
+	select REGMAP_SPI
+
 config SND_SOC_RT5631
 	tristate
 
@@ -321,7 +387,8 @@ config SND_SOC_RT5640
 
 #Freescale sgtl5000 codec
 config SND_SOC_SGTL5000
-	tristate
+	tristate "Freescale SGTL5000 CODEC"
+	depends on I2C
 
 config SND_SOC_SI476X
 	tristate
@@ -330,11 +397,15 @@ config SND_SOC_SIGMADSP
 	tristate
 	select CRC32
 
+config SND_SOC_SIRF_AUDIO_CODEC
+	tristate "SiRF SoC internal audio codec"
+	select REGMAP_MMIO
+
 config SND_SOC_SN95031
 	tristate
 
 config SND_SOC_SPDIF
-	tristate
+	tristate "S/PDIF CODEC"
 
 config SND_SOC_SSM2518
 	tristate
@@ -342,6 +413,14 @@ config SND_SOC_SSM2518
 config SND_SOC_SSM2602
 	tristate
 
+config SND_SOC_SSM2602_SPI
+	select SND_SOC_SSM2602
+	tristate
+
+config SND_SOC_SSM2602_I2C
+	select SND_SOC_SSM2602
+	tristate
+
 config SND_SOC_STA32X
 	tristate
 
@@ -352,11 +431,20 @@ config SND_SOC_STAC9766
 	tristate
 
 config SND_SOC_TAS5086
-	tristate
+	tristate "Texas Instruments TAS5086 speaker amplifier"
+	depends on I2C
 
 config SND_SOC_TLV320AIC23
 	tristate
 
+config SND_SOC_TLV320AIC23_I2C
+	tristate
+	select SND_SOC_TLV320AIC23
+
+config SND_SOC_TLV320AIC23_SPI
+	tristate
+	select SND_SOC_TLV320AIC23
+
 config SND_SOC_TLV320AIC26
 	tristate
 	depends on SPI
@@ -365,7 +453,8 @@ config SND_SOC_TLV320AIC32X4
 	tristate
 
 config SND_SOC_TLV320AIC3X
-	tristate
+	tristate "Texas Instruments TLV320AIC3x CODECs"
+	depends on I2C
 
 config SND_SOC_TLV320DAC33
 	tristate
@@ -414,55 +503,69 @@ config SND_SOC_WM8400
 	tristate
 
 config SND_SOC_WM8510
-	tristate
+	tristate "Wolfson Microelectronics WM8510 CODEC"
+	depends on SND_SOC_I2C_AND_SPI
 
 config SND_SOC_WM8523
-	tristate
+	tristate "Wolfson Microelectronics WM8523 DAC"
+	depends on I2C
 
 config SND_SOC_WM8580
-	tristate
+	tristate "Wolfson Microelectronics WM8523 CODEC"
+	depends on I2C
 
 config SND_SOC_WM8711
-	tristate
+	tristate "Wolfson Microelectronics WM8711 CODEC"
+	depends on SND_SOC_I2C_AND_SPI
 
 config SND_SOC_WM8727
 	tristate
 
 config SND_SOC_WM8728
-	tristate
+	tristate "Wolfson Microelectronics WM8728 DAC"
+	depends on SND_SOC_I2C_AND_SPI
 
 config SND_SOC_WM8731
-	tristate
+	tristate "Wolfson Microelectronics WM8731 CODEC"
+	depends on SND_SOC_I2C_AND_SPI
 
 config SND_SOC_WM8737
-	tristate
+	tristate "Wolfson Microelectronics WM8737 ADC"
+	depends on SND_SOC_I2C_AND_SPI
 
 config SND_SOC_WM8741
-	tristate
+	tristate "Wolfson Microelectronics WM8737 DAC"
+	depends on SND_SOC_I2C_AND_SPI
 
 config SND_SOC_WM8750
-	tristate
+	tristate "Wolfson Microelectronics WM8750 CODEC"
+	depends on SND_SOC_I2C_AND_SPI
 
 config SND_SOC_WM8753
-	tristate
+	tristate "Wolfson Microelectronics WM8753 CODEC"
+	depends on SND_SOC_I2C_AND_SPI
 
 config SND_SOC_WM8770
-	tristate
+	tristate "Wolfson Microelectronics WM8770 CODEC"
+	depends on SPI_MASTER
 
 config SND_SOC_WM8776
-	tristate
+	tristate "Wolfson Microelectronics WM8776 CODEC"
+	depends on SND_SOC_I2C_AND_SPI
 
 config SND_SOC_WM8782
 	tristate
 
 config SND_SOC_WM8804
-	tristate
+	tristate "Wolfson Microelectronics WM8804 S/PDIF transceiver"
+	depends on SND_SOC_I2C_AND_SPI
 
 config SND_SOC_WM8900
 	tristate
 
 config SND_SOC_WM8903
-	tristate
+	tristate "Wolfson Microelectronics WM8903 CODEC"
+	depends on I2C
 
 config SND_SOC_WM8904
 	tristate
@@ -480,7 +583,8 @@ config SND_SOC_WM8961
 	tristate
 
 config SND_SOC_WM8962
-	tristate
+	tristate "Wolfson Microelectronics WM8962 CODEC"
+	depends on I2C
 
 config SND_SOC_WM8971
 	tristate
@@ -553,4 +657,7 @@ config SND_SOC_ML26124
 	tristate
 
 config SND_SOC_TPA6130A2
-	tristate
+	tristate "Texas Instruments TPA6130A2 headphone amplifier"
+	depends on I2C
+
+endmenu
diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile
index bc126764a44d..cb46c4c78dc2 100644
--- a/sound/soc/codecs/Makefile
+++ b/sound/soc/codecs/Makefile
@@ -3,11 +3,18 @@ snd-soc-ab8500-codec-objs := ab8500-codec.o
 snd-soc-ac97-objs := ac97.o
 snd-soc-ad1836-objs := ad1836.o
 snd-soc-ad193x-objs := ad193x.o
+snd-soc-ad193x-spi-objs := ad193x-spi.o
+snd-soc-ad193x-i2c-objs := ad193x-i2c.o
 snd-soc-ad1980-objs := ad1980.o
 snd-soc-ad73311-objs := ad73311.o
 snd-soc-adau1701-objs := adau1701.o
 snd-soc-adau1373-objs := adau1373.o
+snd-soc-adau1977-objs := adau1977.o
+snd-soc-adau1977-spi-objs := adau1977-spi.o
+snd-soc-adau1977-i2c-objs := adau1977-i2c.o
 snd-soc-adav80x-objs := adav80x.o
+snd-soc-adav801-objs := adav801.o
+snd-soc-adav803-objs := adav803.o
 snd-soc-ads117x-objs := ads117x.o
 snd-soc-ak4104-objs := ak4104.o
 snd-soc-ak4535-objs := ak4535.o
@@ -46,6 +53,9 @@ snd-soc-hdmi-codec-objs := hdmi.o
 snd-soc-pcm1681-objs := pcm1681.o
 snd-soc-pcm1792a-codec-objs := pcm1792a.o
 snd-soc-pcm3008-objs := pcm3008.o
+snd-soc-pcm512x-objs := pcm512x.o
+snd-soc-pcm512x-i2c-objs := pcm512x-i2c.o
+snd-soc-pcm512x-spi-objs := pcm512x-spi.o
 snd-soc-rt5631-objs := rt5631.o
 snd-soc-rt5640-objs := rt5640.o
 snd-soc-sgtl5000-objs := sgtl5000.o
@@ -53,19 +63,24 @@ snd-soc-alc5623-objs := alc5623.o
 snd-soc-alc5632-objs := alc5632.o
 snd-soc-sigmadsp-objs := sigmadsp.o
 snd-soc-si476x-objs := si476x.o
+snd-soc-sirf-audio-codec-objs := sirf-audio-codec.o
 snd-soc-sn95031-objs := sn95031.o
 snd-soc-spdif-tx-objs := spdif_transmitter.o
 snd-soc-spdif-rx-objs := spdif_receiver.o
 snd-soc-ssm2518-objs := ssm2518.o
 snd-soc-ssm2602-objs := ssm2602.o
+snd-soc-ssm2602-spi-objs := ssm2602-spi.o
+snd-soc-ssm2602-i2c-objs := ssm2602-i2c.o
 snd-soc-sta32x-objs := sta32x.o
 snd-soc-sta529-objs := sta529.o
 snd-soc-stac9766-objs := stac9766.o
 snd-soc-tas5086-objs := tas5086.o
 snd-soc-tlv320aic23-objs := tlv320aic23.o
+snd-soc-tlv320aic23-i2c-objs := tlv320aic23-i2c.o
+snd-soc-tlv320aic23-spi-objs := tlv320aic23-spi.o
 snd-soc-tlv320aic26-objs := tlv320aic26.o
-snd-soc-tlv320aic3x-objs := tlv320aic3x.o
 snd-soc-tlv320aic32x4-objs := tlv320aic32x4.o
+snd-soc-tlv320aic3x-objs := tlv320aic3x.o
 snd-soc-tlv320dac33-objs := tlv320dac33.o
 snd-soc-twl4030-objs := twl4030.o
 snd-soc-twl6040-objs := twl6040.o
@@ -134,11 +149,18 @@ 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_AD1836)	+= snd-soc-ad1836.o
 obj-$(CONFIG_SND_SOC_AD193X)	+= snd-soc-ad193x.o
+obj-$(CONFIG_SND_SOC_AD193X_SPI)	+= snd-soc-ad193x-spi.o
+obj-$(CONFIG_SND_SOC_AD193X_I2C)	+= snd-soc-ad193x-i2c.o
 obj-$(CONFIG_SND_SOC_AD1980)	+= snd-soc-ad1980.o
 obj-$(CONFIG_SND_SOC_AD73311) += snd-soc-ad73311.o
 obj-$(CONFIG_SND_SOC_ADAU1373)	+= snd-soc-adau1373.o
+obj-$(CONFIG_SND_SOC_ADAU1977)		+= snd-soc-adau1977.o
+obj-$(CONFIG_SND_SOC_ADAU1977_SPI)	+= snd-soc-adau1977-spi.o
+obj-$(CONFIG_SND_SOC_ADAU1977_I2C)	+= snd-soc-adau1977-i2c.o
 obj-$(CONFIG_SND_SOC_ADAU1701)  += snd-soc-adau1701.o
 obj-$(CONFIG_SND_SOC_ADAV80X)  += snd-soc-adav80x.o
+obj-$(CONFIG_SND_SOC_ADAV801)  += snd-soc-adav801.o
+obj-$(CONFIG_SND_SOC_ADAV803)  += snd-soc-adav803.o
 obj-$(CONFIG_SND_SOC_ADS117X)	+= snd-soc-ads117x.o
 obj-$(CONFIG_SND_SOC_AK4104)	+= snd-soc-ak4104.o
 obj-$(CONFIG_SND_SOC_AK4535)	+= snd-soc-ak4535.o
@@ -179,6 +201,9 @@ obj-$(CONFIG_SND_SOC_HDMI_CODEC) += snd-soc-hdmi-codec.o
 obj-$(CONFIG_SND_SOC_PCM1681)	+= snd-soc-pcm1681.o
 obj-$(CONFIG_SND_SOC_PCM1792A)	+= snd-soc-pcm1792a-codec.o
 obj-$(CONFIG_SND_SOC_PCM3008)	+= snd-soc-pcm3008.o
+obj-$(CONFIG_SND_SOC_PCM512x)	+= snd-soc-pcm512x.o
+obj-$(CONFIG_SND_SOC_PCM512x_I2C)	+= snd-soc-pcm512x-i2c.o
+obj-$(CONFIG_SND_SOC_PCM512x_SPI)	+= snd-soc-pcm512x-spi.o
 obj-$(CONFIG_SND_SOC_RT5631)	+= snd-soc-rt5631.o
 obj-$(CONFIG_SND_SOC_RT5640)	+= snd-soc-rt5640.o
 obj-$(CONFIG_SND_SOC_SGTL5000)  += snd-soc-sgtl5000.o
@@ -188,14 +213,18 @@ obj-$(CONFIG_SND_SOC_SN95031)	+=snd-soc-sn95031.o
 obj-$(CONFIG_SND_SOC_SPDIF)	+= snd-soc-spdif-rx.o snd-soc-spdif-tx.o
 obj-$(CONFIG_SND_SOC_SSM2518)	+= snd-soc-ssm2518.o
 obj-$(CONFIG_SND_SOC_SSM2602)	+= snd-soc-ssm2602.o
+obj-$(CONFIG_SND_SOC_SSM2602_SPI)	+= snd-soc-ssm2602-spi.o
+obj-$(CONFIG_SND_SOC_SSM2602_I2C)	+= snd-soc-ssm2602-i2c.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_TAS5086)	+= snd-soc-tas5086.o
 obj-$(CONFIG_SND_SOC_TLV320AIC23)	+= snd-soc-tlv320aic23.o
+obj-$(CONFIG_SND_SOC_TLV320AIC23_I2C)	+= snd-soc-tlv320aic23-i2c.o
+obj-$(CONFIG_SND_SOC_TLV320AIC23_SPI)	+= snd-soc-tlv320aic23-spi.o
 obj-$(CONFIG_SND_SOC_TLV320AIC26)	+= snd-soc-tlv320aic26.o
-obj-$(CONFIG_SND_SOC_TLV320AIC3X)	+= snd-soc-tlv320aic3x.o
 obj-$(CONFIG_SND_SOC_TLV320AIC32X4)     += snd-soc-tlv320aic32x4.o
+obj-$(CONFIG_SND_SOC_TLV320AIC3X)	+= snd-soc-tlv320aic3x.o
 obj-$(CONFIG_SND_SOC_TLV320DAC33)	+= snd-soc-tlv320dac33.o
 obj-$(CONFIG_SND_SOC_TWL4030)	+= snd-soc-twl4030.o
 obj-$(CONFIG_SND_SOC_TWL6040)	+= snd-soc-twl6040.o
diff --git a/sound/soc/codecs/ad1836.c b/sound/soc/codecs/ad1836.c
index 77f459868579..685998dd086e 100644
--- a/sound/soc/codecs/ad1836.c
+++ b/sound/soc/codecs/ad1836.c
@@ -40,8 +40,8 @@ struct ad1836_priv {
  */
 static const char *ad1836_deemp[] = {"None", "44.1kHz", "32kHz", "48kHz"};
 
-static const struct soc_enum ad1836_deemp_enum =
-	SOC_ENUM_SINGLE(AD1836_DAC_CTRL1, 8, 4, ad1836_deemp);
+static SOC_ENUM_SINGLE_DECL(ad1836_deemp_enum,
+			    AD1836_DAC_CTRL1, 8, ad1836_deemp);
 
 #define AD1836_DAC_VOLUME(x) \
 	SOC_DOUBLE_R("DAC" #x " Playback Volume", AD1836_DAC_L_VOL(x), \
diff --git a/sound/soc/codecs/ad193x-i2c.c b/sound/soc/codecs/ad193x-i2c.c
new file mode 100644
index 000000000000..df3a1a415825
--- /dev/null
+++ b/sound/soc/codecs/ad193x-i2c.c
@@ -0,0 +1,54 @@
+/*
+ * AD1936/AD1937 audio driver
+ *
+ * Copyright 2014 Analog Devices Inc.
+ *
+ * Licensed under the GPL-2.
+ */
+
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/regmap.h>
+
+#include <sound/soc.h>
+
+#include "ad193x.h"
+
+static const struct i2c_device_id ad193x_id[] = {
+	{ "ad1936", 0 },
+	{ "ad1937", 0 },
+	{ }
+};
+MODULE_DEVICE_TABLE(i2c, ad193x_id);
+
+static int ad193x_i2c_probe(struct i2c_client *client,
+			    const struct i2c_device_id *id)
+{
+	struct regmap_config config;
+
+	config = ad193x_regmap_config;
+	config.val_bits = 8;
+	config.reg_bits = 8;
+
+	return ad193x_probe(&client->dev, devm_regmap_init_i2c(client, &config));
+}
+
+static int ad193x_i2c_remove(struct i2c_client *client)
+{
+	snd_soc_unregister_codec(&client->dev);
+	return 0;
+}
+
+static struct i2c_driver ad193x_i2c_driver = {
+	.driver = {
+		.name = "ad193x",
+	},
+	.probe    = ad193x_i2c_probe,
+	.remove   = ad193x_i2c_remove,
+	.id_table = ad193x_id,
+};
+module_i2c_driver(ad193x_i2c_driver);
+
+MODULE_DESCRIPTION("ASoC AD1936/AD1937 audio CODEC driver");
+MODULE_AUTHOR("Barry Song <21cnbao@gmail.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/ad193x-spi.c b/sound/soc/codecs/ad193x-spi.c
new file mode 100644
index 000000000000..390cef9b9dc2
--- /dev/null
+++ b/sound/soc/codecs/ad193x-spi.c
@@ -0,0 +1,48 @@
+/*
+ * AD1938/AD1939 audio driver
+ *
+ * Copyright 2014 Analog Devices Inc.
+ *
+ * Licensed under the GPL-2.
+ */
+
+#include <linux/module.h>
+#include <linux/spi/spi.h>
+#include <linux/regmap.h>
+
+#include <sound/soc.h>
+
+#include "ad193x.h"
+
+static int ad193x_spi_probe(struct spi_device *spi)
+{
+	struct regmap_config config;
+
+	config = ad193x_regmap_config;
+	config.val_bits = 8;
+	config.reg_bits = 16;
+	config.read_flag_mask = 0x09;
+	config.write_flag_mask = 0x08;
+
+	return ad193x_probe(&spi->dev, devm_regmap_init_spi(spi, &config));
+}
+
+static int ad193x_spi_remove(struct spi_device *spi)
+{
+	snd_soc_unregister_codec(&spi->dev);
+	return 0;
+}
+
+static struct spi_driver ad193x_spi_driver = {
+	.driver = {
+		.name	= "ad193x",
+		.owner	= THIS_MODULE,
+	},
+	.probe		= ad193x_spi_probe,
+	.remove		= ad193x_spi_remove,
+};
+module_spi_driver(ad193x_spi_driver);
+
+MODULE_DESCRIPTION("ASoC AD1938/AD1939 audio CODEC driver");
+MODULE_AUTHOR("Barry Song <21cnbao@gmail.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/ad193x.c b/sound/soc/codecs/ad193x.c
index 5a42dca535b7..9381a767e75f 100644
--- a/sound/soc/codecs/ad193x.c
+++ b/sound/soc/codecs/ad193x.c
@@ -6,12 +6,10 @@
  * Licensed under the GPL-2 or later.
  */
 
-#include <linux/init.h>
 #include <linux/module.h>
 #include <linux/kernel.h>
 #include <linux/device.h>
-#include <linux/i2c.h>
-#include <linux/spi/spi.h>
+#include <linux/regmap.h>
 #include <linux/slab.h>
 #include <sound/core.h>
 #include <sound/pcm.h>
@@ -19,6 +17,7 @@
 #include <sound/initval.h>
 #include <sound/soc.h>
 #include <sound/tlv.h>
+
 #include "ad193x.h"
 
 /* codec private data */
@@ -32,8 +31,8 @@ struct ad193x_priv {
  */
 static const char * const ad193x_deemp[] = {"None", "48kHz", "44.1kHz", "32kHz"};
 
-static const struct soc_enum ad193x_deemp_enum =
-	SOC_ENUM_SINGLE(AD193X_DAC_CTRL2, 1, 4, ad193x_deemp);
+static SOC_ENUM_SINGLE_DECL(ad193x_deemp_enum, AD193X_DAC_CTRL2, 1,
+			    ad193x_deemp);
 
 static const DECLARE_TLV_DB_MINMAX(adau193x_tlv, -9563, 0);
 
@@ -320,7 +319,7 @@ static struct snd_soc_dai_driver ad193x_dai = {
 	.ops = &ad193x_dai_ops,
 };
 
-static int ad193x_probe(struct snd_soc_codec *codec)
+static int ad193x_codec_probe(struct snd_soc_codec *codec)
 {
 	struct ad193x_priv *ad193x = snd_soc_codec_get_drvdata(codec);
 	int ret;
@@ -352,7 +351,7 @@ static int ad193x_probe(struct snd_soc_codec *codec)
 }
 
 static struct snd_soc_codec_driver soc_codec_dev_ad193x = {
-	.probe = 	ad193x_probe,
+	.probe = ad193x_codec_probe,
 	.controls = ad193x_snd_controls,
 	.num_controls = ARRAY_SIZE(ad193x_snd_controls),
 	.dapm_widgets = ad193x_dapm_widgets,
@@ -366,140 +365,31 @@ static bool adau193x_reg_volatile(struct device *dev, unsigned int reg)
 	return false;
 }
 
-#if defined(CONFIG_SPI_MASTER)
-
-static const struct regmap_config ad193x_spi_regmap_config = {
-	.val_bits = 8,
-	.reg_bits = 16,
-	.read_flag_mask = 0x09,
-	.write_flag_mask = 0x08,
-
+const struct regmap_config ad193x_regmap_config = {
 	.max_register = AD193X_NUM_REGS - 1,
 	.volatile_reg = adau193x_reg_volatile,
 };
+EXPORT_SYMBOL_GPL(ad193x_regmap_config);
 
-static int ad193x_spi_probe(struct spi_device *spi)
+int ad193x_probe(struct device *dev, struct regmap *regmap)
 {
 	struct ad193x_priv *ad193x;
 
-	ad193x = devm_kzalloc(&spi->dev, sizeof(struct ad193x_priv),
-			      GFP_KERNEL);
-	if (ad193x == NULL)
-		return -ENOMEM;
-
-	ad193x->regmap = devm_regmap_init_spi(spi, &ad193x_spi_regmap_config);
-	if (IS_ERR(ad193x->regmap))
-		return PTR_ERR(ad193x->regmap);
-
-	spi_set_drvdata(spi, ad193x);
-
-	return snd_soc_register_codec(&spi->dev, &soc_codec_dev_ad193x,
-			&ad193x_dai, 1);
-}
-
-static int ad193x_spi_remove(struct spi_device *spi)
-{
-	snd_soc_unregister_codec(&spi->dev);
-	return 0;
-}
-
-static struct spi_driver ad193x_spi_driver = {
-	.driver = {
-		.name	= "ad193x",
-		.owner	= THIS_MODULE,
-	},
-	.probe		= ad193x_spi_probe,
-	.remove		= ad193x_spi_remove,
-};
-#endif
-
-#if IS_ENABLED(CONFIG_I2C)
-
-static const struct regmap_config ad193x_i2c_regmap_config = {
-	.val_bits = 8,
-	.reg_bits = 8,
-
-	.max_register = AD193X_NUM_REGS - 1,
-	.volatile_reg = adau193x_reg_volatile,
-};
-
-static const struct i2c_device_id ad193x_id[] = {
-	{ "ad1936", 0 },
-	{ "ad1937", 0 },
-	{ }
-};
-MODULE_DEVICE_TABLE(i2c, ad193x_id);
+	if (IS_ERR(regmap))
+		return PTR_ERR(regmap);
 
-static int ad193x_i2c_probe(struct i2c_client *client,
-			    const struct i2c_device_id *id)
-{
-	struct ad193x_priv *ad193x;
-
-	ad193x = devm_kzalloc(&client->dev, sizeof(struct ad193x_priv),
-			      GFP_KERNEL);
+	ad193x = devm_kzalloc(dev, sizeof(*ad193x), GFP_KERNEL);
 	if (ad193x == NULL)
 		return -ENOMEM;
 
-	ad193x->regmap = devm_regmap_init_i2c(client, &ad193x_i2c_regmap_config);
-	if (IS_ERR(ad193x->regmap))
-		return PTR_ERR(ad193x->regmap);
-
-	i2c_set_clientdata(client, ad193x);
-
-	return snd_soc_register_codec(&client->dev, &soc_codec_dev_ad193x,
-			&ad193x_dai, 1);
-}
-
-static int ad193x_i2c_remove(struct i2c_client *client)
-{
-	snd_soc_unregister_codec(&client->dev);
-	return 0;
-}
+	ad193x->regmap = regmap;
 
-static struct i2c_driver ad193x_i2c_driver = {
-	.driver = {
-		.name = "ad193x",
-	},
-	.probe    = ad193x_i2c_probe,
-	.remove   = ad193x_i2c_remove,
-	.id_table = ad193x_id,
-};
-#endif
-
-static int __init ad193x_modinit(void)
-{
-	int ret;
-
-#if IS_ENABLED(CONFIG_I2C)
-	ret =  i2c_add_driver(&ad193x_i2c_driver);
-	if (ret != 0) {
-		printk(KERN_ERR "Failed to register AD193X I2C driver: %d\n",
-				ret);
-	}
-#endif
-
-#if defined(CONFIG_SPI_MASTER)
-	ret = spi_register_driver(&ad193x_spi_driver);
-	if (ret != 0) {
-		printk(KERN_ERR "Failed to register AD193X SPI driver: %d\n",
-				ret);
-	}
-#endif
-	return ret;
-}
-module_init(ad193x_modinit);
-
-static void __exit ad193x_modexit(void)
-{
-#if defined(CONFIG_SPI_MASTER)
-	spi_unregister_driver(&ad193x_spi_driver);
-#endif
+	dev_set_drvdata(dev, ad193x);
 
-#if IS_ENABLED(CONFIG_I2C)
-	i2c_del_driver(&ad193x_i2c_driver);
-#endif
+	return snd_soc_register_codec(dev, &soc_codec_dev_ad193x,
+		&ad193x_dai, 1);
 }
-module_exit(ad193x_modexit);
+EXPORT_SYMBOL_GPL(ad193x_probe);
 
 MODULE_DESCRIPTION("ASoC ad193x driver");
 MODULE_AUTHOR("Barry Song <21cnbao@gmail.com>");
diff --git a/sound/soc/codecs/ad193x.h b/sound/soc/codecs/ad193x.h
index 473388049992..ab9a998f15be 100644
--- a/sound/soc/codecs/ad193x.h
+++ b/sound/soc/codecs/ad193x.h
@@ -9,6 +9,13 @@
 #ifndef __AD193X_H__
 #define __AD193X_H__
 
+#include <linux/regmap.h>
+
+struct device;
+
+extern const struct regmap_config ad193x_regmap_config;
+int ad193x_probe(struct device *dev, struct regmap *regmap);
+
 #define AD193X_PLL_CLK_CTRL0    0x00
 #define AD193X_PLL_POWERDOWN           0x01
 #define AD193X_PLL_INPUT_MASK   0x6
diff --git a/sound/soc/codecs/adau1373.c b/sound/soc/codecs/adau1373.c
index eb836ed5271f..5223800775ad 100644
--- a/sound/soc/codecs/adau1373.c
+++ b/sound/soc/codecs/adau1373.c
@@ -345,15 +345,15 @@ static const char *adau1373_fdsp_sel_text[] = {
 	"Channel 5",
 };
 
-static const SOC_ENUM_SINGLE_DECL(adau1373_drc1_channel_enum,
+static SOC_ENUM_SINGLE_DECL(adau1373_drc1_channel_enum,
 	ADAU1373_FDSP_SEL1, 4, adau1373_fdsp_sel_text);
-static const SOC_ENUM_SINGLE_DECL(adau1373_drc2_channel_enum,
+static SOC_ENUM_SINGLE_DECL(adau1373_drc2_channel_enum,
 	ADAU1373_FDSP_SEL1, 0, adau1373_fdsp_sel_text);
-static const SOC_ENUM_SINGLE_DECL(adau1373_drc3_channel_enum,
+static SOC_ENUM_SINGLE_DECL(adau1373_drc3_channel_enum,
 	ADAU1373_FDSP_SEL2, 0, adau1373_fdsp_sel_text);
-static const SOC_ENUM_SINGLE_DECL(adau1373_hpf_channel_enum,
+static SOC_ENUM_SINGLE_DECL(adau1373_hpf_channel_enum,
 	ADAU1373_FDSP_SEL3, 0, adau1373_fdsp_sel_text);
-static const SOC_ENUM_SINGLE_DECL(adau1373_bass_channel_enum,
+static SOC_ENUM_SINGLE_DECL(adau1373_bass_channel_enum,
 	ADAU1373_FDSP_SEL4, 4, adau1373_fdsp_sel_text);
 
 static const char *adau1373_hpf_cutoff_text[] = {
@@ -362,7 +362,7 @@ static const char *adau1373_hpf_cutoff_text[] = {
 	"800Hz",
 };
 
-static const SOC_ENUM_SINGLE_DECL(adau1373_hpf_cutoff_enum,
+static SOC_ENUM_SINGLE_DECL(adau1373_hpf_cutoff_enum,
 	ADAU1373_HPF_CTRL, 3, adau1373_hpf_cutoff_text);
 
 static const char *adau1373_bass_lpf_cutoff_text[] = {
@@ -388,14 +388,14 @@ static const unsigned int adau1373_bass_tlv[] = {
 	5, 7, TLV_DB_SCALE_ITEM(1400, 150, 0),
 };
 
-static const SOC_ENUM_SINGLE_DECL(adau1373_bass_lpf_cutoff_enum,
+static SOC_ENUM_SINGLE_DECL(adau1373_bass_lpf_cutoff_enum,
 	ADAU1373_BASS1, 5, adau1373_bass_lpf_cutoff_text);
 
-static const SOC_VALUE_ENUM_SINGLE_DECL(adau1373_bass_clip_level_enum,
+static SOC_VALUE_ENUM_SINGLE_DECL(adau1373_bass_clip_level_enum,
 	ADAU1373_BASS1, 2, 7, adau1373_bass_clip_level_text,
 	adau1373_bass_clip_level_values);
 
-static const SOC_ENUM_SINGLE_DECL(adau1373_bass_hpf_cutoff_enum,
+static SOC_ENUM_SINGLE_DECL(adau1373_bass_hpf_cutoff_enum,
 	ADAU1373_BASS1, 0, adau1373_bass_hpf_cutoff_text);
 
 static const char *adau1373_3d_level_text[] = {
@@ -409,9 +409,9 @@ static const char *adau1373_3d_cutoff_text[] = {
 	"0.16875 fs", "0.27083 fs"
 };
 
-static const SOC_ENUM_SINGLE_DECL(adau1373_3d_level_enum,
+static SOC_ENUM_SINGLE_DECL(adau1373_3d_level_enum,
 	ADAU1373_3D_CTRL1, 4, adau1373_3d_level_text);
-static const SOC_ENUM_SINGLE_DECL(adau1373_3d_cutoff_enum,
+static SOC_ENUM_SINGLE_DECL(adau1373_3d_cutoff_enum,
 	ADAU1373_3D_CTRL1, 0, adau1373_3d_cutoff_text);
 
 static const unsigned int adau1373_3d_tlv[] = {
@@ -427,11 +427,11 @@ static const char *adau1373_lr_mux_text[] = {
 	"Stereo",
 };
 
-static const SOC_ENUM_SINGLE_DECL(adau1373_lineout1_lr_mux_enum,
+static SOC_ENUM_SINGLE_DECL(adau1373_lineout1_lr_mux_enum,
 	ADAU1373_OUTPUT_CTRL, 4, adau1373_lr_mux_text);
-static const SOC_ENUM_SINGLE_DECL(adau1373_lineout2_lr_mux_enum,
+static SOC_ENUM_SINGLE_DECL(adau1373_lineout2_lr_mux_enum,
 	ADAU1373_OUTPUT_CTRL, 6, adau1373_lr_mux_text);
-static const SOC_ENUM_SINGLE_DECL(adau1373_speaker_lr_mux_enum,
+static SOC_ENUM_SINGLE_DECL(adau1373_speaker_lr_mux_enum,
 	ADAU1373_LS_CTRL, 4, adau1373_lr_mux_text);
 
 static const struct snd_kcontrol_new adau1373_controls[] = {
@@ -576,8 +576,8 @@ static const char *adau1373_decimator_text[] = {
 	"DMIC1",
 };
 
-static const struct soc_enum adau1373_decimator_enum =
-	SOC_ENUM_SINGLE(0, 0, 2, adau1373_decimator_text);
+static SOC_ENUM_SINGLE_VIRT_DECL(adau1373_decimator_enum,
+	adau1373_decimator_text);
 
 static const struct snd_kcontrol_new adau1373_decimator_mux =
 	SOC_DAPM_ENUM_VIRT("Decimator Mux", adau1373_decimator_enum);
diff --git a/sound/soc/codecs/adau1977-i2c.c b/sound/soc/codecs/adau1977-i2c.c
new file mode 100644
index 000000000000..9700e8c838c9
--- /dev/null
+++ b/sound/soc/codecs/adau1977-i2c.c
@@ -0,0 +1,59 @@
+/*
+ * ADAU1977/ADAU1978/ADAU1979 driver
+ *
+ * Copyright 2014 Analog Devices Inc.
+ *  Author: Lars-Peter Clausen <lars@metafoo.de>
+ *
+ * Licensed under the GPL-2.
+ */
+
+#include <linux/i2c.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+#include <sound/soc.h>
+
+#include "adau1977.h"
+
+static int adau1977_i2c_probe(struct i2c_client *client,
+	const struct i2c_device_id *id)
+{
+	struct regmap_config config;
+
+	config = adau1977_regmap_config;
+	config.val_bits = 8;
+	config.reg_bits = 8;
+
+	return adau1977_probe(&client->dev,
+		devm_regmap_init_i2c(client, &config),
+		id->driver_data, NULL);
+}
+
+static int adau1977_i2c_remove(struct i2c_client *client)
+{
+	snd_soc_unregister_codec(&client->dev);
+	return 0;
+}
+
+static const struct i2c_device_id adau1977_i2c_ids[] = {
+	{ "adau1977", ADAU1977 },
+	{ "adau1978", ADAU1978 },
+	{ "adau1979", ADAU1978 },
+	{ }
+};
+MODULE_DEVICE_TABLE(i2c, adau1977_i2c_ids);
+
+static struct i2c_driver adau1977_i2c_driver = {
+	.driver = {
+		.name = "adau1977",
+		.owner = THIS_MODULE,
+	},
+	.probe = adau1977_i2c_probe,
+	.remove = adau1977_i2c_remove,
+	.id_table = adau1977_i2c_ids,
+};
+module_i2c_driver(adau1977_i2c_driver);
+
+MODULE_DESCRIPTION("ASoC ADAU1977/ADAU1978/ADAU1979 driver");
+MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/adau1977-spi.c b/sound/soc/codecs/adau1977-spi.c
new file mode 100644
index 000000000000..b05cf5da3a94
--- /dev/null
+++ b/sound/soc/codecs/adau1977-spi.c
@@ -0,0 +1,76 @@
+/*
+ * ADAU1977/ADAU1978/ADAU1979 driver
+ *
+ * Copyright 2014 Analog Devices Inc.
+ *  Author: Lars-Peter Clausen <lars@metafoo.de>
+ *
+ * Licensed under the GPL-2.
+ */
+
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+#include <linux/spi/spi.h>
+#include <sound/soc.h>
+
+#include "adau1977.h"
+
+static void adau1977_spi_switch_mode(struct device *dev)
+{
+	struct spi_device *spi = to_spi_device(dev);
+
+	/*
+	 * To get the device into SPI mode CLATCH has to be pulled low three
+	 * times.  Do this by issuing three dummy reads.
+	 */
+	spi_w8r8(spi, 0x00);
+	spi_w8r8(spi, 0x00);
+	spi_w8r8(spi, 0x00);
+}
+
+static int adau1977_spi_probe(struct spi_device *spi)
+{
+	const struct spi_device_id *id = spi_get_device_id(spi);
+	struct regmap_config config;
+
+	if (!id)
+		return -EINVAL;
+
+	config = adau1977_regmap_config;
+	config.val_bits = 8;
+	config.reg_bits = 16;
+	config.read_flag_mask = 0x1;
+
+	return adau1977_probe(&spi->dev,
+		devm_regmap_init_spi(spi, &config),
+		id->driver_data, adau1977_spi_switch_mode);
+}
+
+static int adau1977_spi_remove(struct spi_device *spi)
+{
+	snd_soc_unregister_codec(&spi->dev);
+	return 0;
+}
+
+static const struct spi_device_id adau1977_spi_ids[] = {
+	{ "adau1977", ADAU1977 },
+	{ "adau1978", ADAU1978 },
+	{ "adau1979", ADAU1978 },
+	{ }
+};
+MODULE_DEVICE_TABLE(spi, adau1977_spi_ids);
+
+static struct spi_driver adau1977_spi_driver = {
+	.driver = {
+		.name = "adau1977",
+		.owner = THIS_MODULE,
+	},
+	.probe = adau1977_spi_probe,
+	.remove = adau1977_spi_remove,
+	.id_table = adau1977_spi_ids,
+};
+module_spi_driver(adau1977_spi_driver);
+
+MODULE_DESCRIPTION("ASoC ADAU1977/ADAU1978/ADAU1979 driver");
+MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/adau1977.c b/sound/soc/codecs/adau1977.c
new file mode 100644
index 000000000000..fd55da7cb9d4
--- /dev/null
+++ b/sound/soc/codecs/adau1977.c
@@ -0,0 +1,1018 @@
+/*
+ * ADAU1977/ADAU1978/ADAU1979 driver
+ *
+ * Copyright 2014 Analog Devices Inc.
+ *  Author: Lars-Peter Clausen <lars@metafoo.de>
+ *
+ * Licensed under the GPL-2.
+ */
+
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/gpio/consumer.h>
+#include <linux/i2c.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/platform_data/adau1977.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.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/tlv.h>
+
+#include "adau1977.h"
+
+#define ADAU1977_REG_POWER		0x00
+#define ADAU1977_REG_PLL		0x01
+#define ADAU1977_REG_BOOST		0x02
+#define ADAU1977_REG_MICBIAS		0x03
+#define ADAU1977_REG_BLOCK_POWER_SAI	0x04
+#define ADAU1977_REG_SAI_CTRL0		0x05
+#define ADAU1977_REG_SAI_CTRL1		0x06
+#define ADAU1977_REG_CMAP12		0x07
+#define ADAU1977_REG_CMAP34		0x08
+#define ADAU1977_REG_SAI_OVERTEMP	0x09
+#define ADAU1977_REG_POST_ADC_GAIN(x)	(0x0a + (x))
+#define ADAU1977_REG_MISC_CONTROL	0x0e
+#define ADAU1977_REG_DIAG_CONTROL	0x10
+#define ADAU1977_REG_STATUS(x)		(0x11 + (x))
+#define ADAU1977_REG_DIAG_IRQ1		0x15
+#define ADAU1977_REG_DIAG_IRQ2		0x16
+#define ADAU1977_REG_ADJUST1		0x17
+#define ADAU1977_REG_ADJUST2		0x18
+#define ADAU1977_REG_ADC_CLIP		0x19
+#define ADAU1977_REG_DC_HPF_CAL		0x1a
+
+#define ADAU1977_POWER_RESET			BIT(7)
+#define ADAU1977_POWER_PWUP			BIT(0)
+
+#define ADAU1977_PLL_CLK_S			BIT(4)
+#define ADAU1977_PLL_MCS_MASK			0x7
+
+#define ADAU1977_MICBIAS_MB_VOLTS_MASK		0xf0
+#define ADAU1977_MICBIAS_MB_VOLTS_OFFSET	4
+
+#define ADAU1977_BLOCK_POWER_SAI_LR_POL		BIT(7)
+#define ADAU1977_BLOCK_POWER_SAI_BCLK_EDGE	BIT(6)
+#define ADAU1977_BLOCK_POWER_SAI_LDO_EN		BIT(5)
+
+#define ADAU1977_SAI_CTRL0_FMT_MASK		(0x3 << 6)
+#define ADAU1977_SAI_CTRL0_FMT_I2S		(0x0 << 6)
+#define ADAU1977_SAI_CTRL0_FMT_LJ		(0x1 << 6)
+#define ADAU1977_SAI_CTRL0_FMT_RJ_24BIT		(0x2 << 6)
+#define ADAU1977_SAI_CTRL0_FMT_RJ_16BIT		(0x3 << 6)
+
+#define ADAU1977_SAI_CTRL0_SAI_MASK		(0x7 << 3)
+#define ADAU1977_SAI_CTRL0_SAI_I2S		(0x0 << 3)
+#define ADAU1977_SAI_CTRL0_SAI_TDM_2		(0x1 << 3)
+#define ADAU1977_SAI_CTRL0_SAI_TDM_4		(0x2 << 3)
+#define ADAU1977_SAI_CTRL0_SAI_TDM_8		(0x3 << 3)
+#define ADAU1977_SAI_CTRL0_SAI_TDM_16		(0x4 << 3)
+
+#define ADAU1977_SAI_CTRL0_FS_MASK		(0x7)
+#define ADAU1977_SAI_CTRL0_FS_8000_12000	(0x0)
+#define ADAU1977_SAI_CTRL0_FS_16000_24000	(0x1)
+#define ADAU1977_SAI_CTRL0_FS_32000_48000	(0x2)
+#define ADAU1977_SAI_CTRL0_FS_64000_96000	(0x3)
+#define ADAU1977_SAI_CTRL0_FS_128000_192000	(0x4)
+
+#define ADAU1977_SAI_CTRL1_SLOT_WIDTH_MASK	(0x3 << 5)
+#define ADAU1977_SAI_CTRL1_SLOT_WIDTH_32	(0x0 << 5)
+#define ADAU1977_SAI_CTRL1_SLOT_WIDTH_24	(0x1 << 5)
+#define ADAU1977_SAI_CTRL1_SLOT_WIDTH_16	(0x2 << 5)
+#define ADAU1977_SAI_CTRL1_DATA_WIDTH_MASK	(0x1 << 4)
+#define ADAU1977_SAI_CTRL1_DATA_WIDTH_16BIT	(0x1 << 4)
+#define ADAU1977_SAI_CTRL1_DATA_WIDTH_24BIT	(0x0 << 4)
+#define ADAU1977_SAI_CTRL1_LRCLK_PULSE		BIT(3)
+#define ADAU1977_SAI_CTRL1_MSB			BIT(2)
+#define ADAU1977_SAI_CTRL1_BCLKRATE_16		(0x1 << 1)
+#define ADAU1977_SAI_CTRL1_BCLKRATE_32		(0x0 << 1)
+#define ADAU1977_SAI_CTRL1_BCLKRATE_MASK	(0x1 << 1)
+#define ADAU1977_SAI_CTRL1_MASTER		BIT(0)
+
+#define ADAU1977_SAI_OVERTEMP_DRV_C(x)		BIT(4 + (x))
+#define ADAU1977_SAI_OVERTEMP_DRV_HIZ		BIT(3)
+
+#define ADAU1977_MISC_CONTROL_SUM_MODE_MASK	(0x3 << 6)
+#define ADAU1977_MISC_CONTROL_SUM_MODE_1CH	(0x2 << 6)
+#define ADAU1977_MISC_CONTROL_SUM_MODE_2CH	(0x1 << 6)
+#define ADAU1977_MISC_CONTROL_SUM_MODE_4CH	(0x0 << 6)
+#define ADAU1977_MISC_CONTROL_MMUTE		BIT(4)
+#define ADAU1977_MISC_CONTROL_DC_CAL		BIT(0)
+
+#define ADAU1977_CHAN_MAP_SECOND_SLOT_OFFSET	4
+#define ADAU1977_CHAN_MAP_FIRST_SLOT_OFFSET	0
+
+struct adau1977 {
+	struct regmap *regmap;
+	bool right_j;
+	unsigned int sysclk;
+	enum adau1977_sysclk_src sysclk_src;
+	struct gpio_desc *reset_gpio;
+	enum adau1977_type type;
+
+	struct regulator *avdd_reg;
+	struct regulator *dvdd_reg;
+
+	struct snd_pcm_hw_constraint_list constraints;
+
+	struct device *dev;
+	void (*switch_mode)(struct device *dev);
+
+	unsigned int max_master_fs;
+	unsigned int slot_width;
+	bool enabled;
+	bool master;
+};
+
+static const struct reg_default adau1977_reg_defaults[] = {
+	{ 0x00, 0x00 },
+	{ 0x01, 0x41 },
+	{ 0x02, 0x4a },
+	{ 0x03, 0x7d },
+	{ 0x04, 0x3d },
+	{ 0x05, 0x02 },
+	{ 0x06, 0x00 },
+	{ 0x07, 0x10 },
+	{ 0x08, 0x32 },
+	{ 0x09, 0xf0 },
+	{ 0x0a, 0xa0 },
+	{ 0x0b, 0xa0 },
+	{ 0x0c, 0xa0 },
+	{ 0x0d, 0xa0 },
+	{ 0x0e, 0x02 },
+	{ 0x10, 0x0f },
+	{ 0x15, 0x20 },
+	{ 0x16, 0x00 },
+	{ 0x17, 0x00 },
+	{ 0x18, 0x00 },
+	{ 0x1a, 0x00 },
+};
+
+static const DECLARE_TLV_DB_MINMAX_MUTE(adau1977_adc_gain, -3562, 6000);
+
+static const struct snd_soc_dapm_widget adau1977_micbias_dapm_widgets[] = {
+	SND_SOC_DAPM_SUPPLY("MICBIAS", ADAU1977_REG_MICBIAS,
+		3, 0, NULL, 0)
+};
+
+static const struct snd_soc_dapm_widget adau1977_dapm_widgets[] = {
+	SND_SOC_DAPM_SUPPLY("Vref", ADAU1977_REG_BLOCK_POWER_SAI,
+		4, 0, NULL, 0),
+
+	SND_SOC_DAPM_ADC("ADC1", "Capture", ADAU1977_REG_BLOCK_POWER_SAI, 0, 0),
+	SND_SOC_DAPM_ADC("ADC2", "Capture", ADAU1977_REG_BLOCK_POWER_SAI, 1, 0),
+	SND_SOC_DAPM_ADC("ADC3", "Capture", ADAU1977_REG_BLOCK_POWER_SAI, 2, 0),
+	SND_SOC_DAPM_ADC("ADC4", "Capture", ADAU1977_REG_BLOCK_POWER_SAI, 3, 0),
+
+	SND_SOC_DAPM_INPUT("AIN1"),
+	SND_SOC_DAPM_INPUT("AIN2"),
+	SND_SOC_DAPM_INPUT("AIN3"),
+	SND_SOC_DAPM_INPUT("AIN4"),
+
+	SND_SOC_DAPM_OUTPUT("VREF"),
+};
+
+static const struct snd_soc_dapm_route adau1977_dapm_routes[] = {
+	{ "ADC1", NULL, "AIN1" },
+	{ "ADC2", NULL, "AIN2" },
+	{ "ADC3", NULL, "AIN3" },
+	{ "ADC4", NULL, "AIN4" },
+
+	{ "ADC1", NULL, "Vref" },
+	{ "ADC2", NULL, "Vref" },
+	{ "ADC3", NULL, "Vref" },
+	{ "ADC4", NULL, "Vref" },
+
+	{ "VREF", NULL, "Vref" },
+};
+
+#define ADAU1977_VOLUME(x) \
+	SOC_SINGLE_TLV("ADC" #x " Capture Volume", \
+		ADAU1977_REG_POST_ADC_GAIN((x) - 1), \
+		0, 255, 1, adau1977_adc_gain)
+
+#define ADAU1977_HPF_SWITCH(x) \
+	SOC_SINGLE("ADC" #x " Highpass-Filter Capture Switch", \
+		ADAU1977_REG_DC_HPF_CAL, (x) - 1, 1, 0)
+
+#define ADAU1977_DC_SUB_SWITCH(x) \
+	SOC_SINGLE("ADC" #x " DC Substraction Capture Switch", \
+		ADAU1977_REG_DC_HPF_CAL, (x) + 3, 1, 0)
+
+static const struct snd_kcontrol_new adau1977_snd_controls[] = {
+	ADAU1977_VOLUME(1),
+	ADAU1977_VOLUME(2),
+	ADAU1977_VOLUME(3),
+	ADAU1977_VOLUME(4),
+
+	ADAU1977_HPF_SWITCH(1),
+	ADAU1977_HPF_SWITCH(2),
+	ADAU1977_HPF_SWITCH(3),
+	ADAU1977_HPF_SWITCH(4),
+
+	ADAU1977_DC_SUB_SWITCH(1),
+	ADAU1977_DC_SUB_SWITCH(2),
+	ADAU1977_DC_SUB_SWITCH(3),
+	ADAU1977_DC_SUB_SWITCH(4),
+};
+
+static int adau1977_reset(struct adau1977 *adau1977)
+{
+	int ret;
+
+	/*
+	 * The reset bit is obviously volatile, but we need to be able to cache
+	 * the other bits in the register, so we can't just mark the whole
+	 * register as volatile. Since this is the only place where we'll ever
+	 * touch the reset bit just bypass the cache for this operation.
+	 */
+	regcache_cache_bypass(adau1977->regmap, true);
+	ret = regmap_write(adau1977->regmap, ADAU1977_REG_POWER,
+			ADAU1977_POWER_RESET);
+	regcache_cache_bypass(adau1977->regmap, false);
+	if (ret)
+		return ret;
+
+	return ret;
+}
+
+/*
+ * Returns the appropriate setting for ths FS field in the CTRL0 register
+ * depending on the rate.
+ */
+static int adau1977_lookup_fs(unsigned int rate)
+{
+	if (rate >= 8000 && rate <= 12000)
+		return ADAU1977_SAI_CTRL0_FS_8000_12000;
+	else if (rate >= 16000 && rate <= 24000)
+		return ADAU1977_SAI_CTRL0_FS_16000_24000;
+	else if (rate >= 32000 && rate <= 48000)
+		return ADAU1977_SAI_CTRL0_FS_32000_48000;
+	else if (rate >= 64000 && rate <= 96000)
+		return ADAU1977_SAI_CTRL0_FS_64000_96000;
+	else if (rate >= 128000 && rate <= 192000)
+		return ADAU1977_SAI_CTRL0_FS_128000_192000;
+	else
+		return -EINVAL;
+}
+
+static int adau1977_lookup_mcs(struct adau1977 *adau1977, unsigned int rate,
+	unsigned int fs)
+{
+	unsigned int mcs;
+
+	/*
+	 * rate = sysclk / (512 * mcs_lut[mcs]) * 2**fs
+	 * => mcs_lut[mcs] = sysclk / (512 * rate) * 2**fs
+	 * => mcs_lut[mcs] = sysclk / ((512 / 2**fs) * rate)
+	 */
+
+	rate *= 512 >> fs;
+
+	if (adau1977->sysclk % rate != 0)
+		return -EINVAL;
+
+	mcs = adau1977->sysclk / rate;
+
+	/* The factors configured by MCS are 1, 2, 3, 4, 6 */
+	if (mcs < 1 || mcs > 6 || mcs == 5)
+		return -EINVAL;
+
+	mcs = mcs - 1;
+	if (mcs == 5)
+		mcs = 4;
+
+	return mcs;
+}
+
+static int adau1977_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 adau1977 *adau1977 = snd_soc_codec_get_drvdata(codec);
+	unsigned int rate = params_rate(params);
+	unsigned int slot_width;
+	unsigned int ctrl0, ctrl0_mask;
+	unsigned int ctrl1;
+	int mcs, fs;
+	int ret;
+
+	fs = adau1977_lookup_fs(rate);
+	if (fs < 0)
+		return fs;
+
+	if (adau1977->sysclk_src == ADAU1977_SYSCLK_SRC_MCLK) {
+		mcs = adau1977_lookup_mcs(adau1977, rate, fs);
+		if (mcs < 0)
+			return mcs;
+	} else {
+		mcs = 0;
+	}
+
+	ctrl0_mask = ADAU1977_SAI_CTRL0_FS_MASK;
+	ctrl0 = fs;
+
+	if (adau1977->right_j) {
+		switch (params_width(params)) {
+		case 16:
+			ctrl0 |= ADAU1977_SAI_CTRL0_FMT_RJ_16BIT;
+			break;
+		case 24:
+			ctrl0 |= ADAU1977_SAI_CTRL0_FMT_RJ_24BIT;
+			break;
+		default:
+			return -EINVAL;
+		}
+		ctrl0_mask |= ADAU1977_SAI_CTRL0_FMT_MASK;
+	}
+
+	if (adau1977->master) {
+		switch (params_width(params)) {
+		case 16:
+			ctrl1 = ADAU1977_SAI_CTRL1_DATA_WIDTH_16BIT;
+			slot_width = 16;
+			break;
+		case 24:
+		case 32:
+			ctrl1 = ADAU1977_SAI_CTRL1_DATA_WIDTH_24BIT;
+			slot_width = 32;
+			break;
+		default:
+			return -EINVAL;
+		}
+
+		/* In TDM mode there is a fixed slot width */
+		if (adau1977->slot_width)
+			slot_width = adau1977->slot_width;
+
+		if (slot_width == 16)
+			ctrl1 |= ADAU1977_SAI_CTRL1_BCLKRATE_16;
+		else
+			ctrl1 |= ADAU1977_SAI_CTRL1_BCLKRATE_32;
+
+		ret = regmap_update_bits(adau1977->regmap,
+			ADAU1977_REG_SAI_CTRL1,
+			ADAU1977_SAI_CTRL1_DATA_WIDTH_MASK |
+			ADAU1977_SAI_CTRL1_BCLKRATE_MASK,
+			ctrl1);
+		if (ret < 0)
+			return ret;
+	}
+
+	ret = regmap_update_bits(adau1977->regmap, ADAU1977_REG_SAI_CTRL0,
+				ctrl0_mask, ctrl0);
+	if (ret < 0)
+		return ret;
+
+	return regmap_update_bits(adau1977->regmap, ADAU1977_REG_PLL,
+				ADAU1977_PLL_MCS_MASK, mcs);
+}
+
+static int adau1977_power_disable(struct adau1977 *adau1977)
+{
+	int ret = 0;
+
+	if (!adau1977->enabled)
+		return 0;
+
+	ret = regmap_update_bits(adau1977->regmap, ADAU1977_REG_POWER,
+		ADAU1977_POWER_PWUP, 0);
+	if (ret)
+		return ret;
+
+	regcache_mark_dirty(adau1977->regmap);
+
+	if (adau1977->reset_gpio)
+		gpiod_set_value_cansleep(adau1977->reset_gpio, 0);
+
+	regcache_cache_only(adau1977->regmap, true);
+
+	regulator_disable(adau1977->avdd_reg);
+	if (adau1977->dvdd_reg)
+		regulator_disable(adau1977->dvdd_reg);
+
+	adau1977->enabled = false;
+
+	return 0;
+}
+
+static int adau1977_power_enable(struct adau1977 *adau1977)
+{
+	unsigned int val;
+	int ret = 0;
+
+	if (adau1977->enabled)
+		return 0;
+
+	ret = regulator_enable(adau1977->avdd_reg);
+	if (ret)
+		return ret;
+
+	if (adau1977->dvdd_reg) {
+		ret = regulator_enable(adau1977->dvdd_reg);
+		if (ret)
+			goto err_disable_avdd;
+	}
+
+	if (adau1977->reset_gpio)
+		gpiod_set_value_cansleep(adau1977->reset_gpio, 1);
+
+	regcache_cache_only(adau1977->regmap, false);
+
+	if (adau1977->switch_mode)
+		adau1977->switch_mode(adau1977->dev);
+
+	ret = adau1977_reset(adau1977);
+	if (ret)
+		goto err_disable_dvdd;
+
+	ret = regmap_update_bits(adau1977->regmap, ADAU1977_REG_POWER,
+		ADAU1977_POWER_PWUP, ADAU1977_POWER_PWUP);
+	if (ret)
+		goto err_disable_dvdd;
+
+	ret = regcache_sync(adau1977->regmap);
+	if (ret)
+		goto err_disable_dvdd;
+
+	/*
+	 * The PLL register is not affected by the software reset. It is
+	 * possible that the value of the register was changed to the
+	 * default value while we were in cache only mode. In this case
+	 * regcache_sync will skip over it and we have to manually sync
+	 * it.
+	 */
+	ret = regmap_read(adau1977->regmap, ADAU1977_REG_PLL, &val);
+	if (ret)
+		goto err_disable_dvdd;
+
+	if (val == 0x41) {
+		regcache_cache_bypass(adau1977->regmap, true);
+		ret = regmap_write(adau1977->regmap, ADAU1977_REG_PLL,
+			0x41);
+		if (ret)
+			goto err_disable_dvdd;
+		regcache_cache_bypass(adau1977->regmap, false);
+	}
+
+	adau1977->enabled = true;
+
+	return ret;
+
+err_disable_dvdd:
+	if (adau1977->dvdd_reg)
+		regulator_disable(adau1977->dvdd_reg);
+err_disable_avdd:
+		regulator_disable(adau1977->avdd_reg);
+	return ret;
+}
+
+static int adau1977_set_bias_level(struct snd_soc_codec *codec,
+	enum snd_soc_bias_level level)
+{
+	struct adau1977 *adau1977 = snd_soc_codec_get_drvdata(codec);
+	int ret = 0;
+
+	switch (level) {
+	case SND_SOC_BIAS_ON:
+		break;
+	case SND_SOC_BIAS_PREPARE:
+		break;
+	case SND_SOC_BIAS_STANDBY:
+		if (codec->dapm.bias_level == SND_SOC_BIAS_OFF)
+			ret = adau1977_power_enable(adau1977);
+		break;
+	case SND_SOC_BIAS_OFF:
+		ret = adau1977_power_disable(adau1977);
+		break;
+	}
+
+	if (ret)
+		return ret;
+
+	codec->dapm.bias_level = level;
+
+	return 0;
+}
+
+static int adau1977_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask,
+	unsigned int rx_mask, int slots, int width)
+{
+	struct adau1977 *adau1977 = snd_soc_codec_get_drvdata(dai->codec);
+	unsigned int ctrl0, ctrl1, drv;
+	unsigned int slot[4];
+	unsigned int i;
+	int ret;
+
+	if (slots == 0) {
+		/* 0 = No fixed slot width */
+		adau1977->slot_width = 0;
+		adau1977->max_master_fs = 192000;
+		return regmap_update_bits(adau1977->regmap,
+			ADAU1977_REG_SAI_CTRL0, ADAU1977_SAI_CTRL0_SAI_MASK,
+			ADAU1977_SAI_CTRL0_SAI_I2S);
+	}
+
+	if (rx_mask == 0 || tx_mask != 0)
+		return -EINVAL;
+
+	drv = 0;
+	for (i = 0; i < 4; i++) {
+		slot[i] = __ffs(rx_mask);
+		drv |= ADAU1977_SAI_OVERTEMP_DRV_C(i);
+		rx_mask &= ~(1 << slot[i]);
+		if (slot[i] >= slots)
+			return -EINVAL;
+		if (rx_mask == 0)
+			break;
+	}
+
+	if (rx_mask != 0)
+		return -EINVAL;
+
+	switch (width) {
+	case 16:
+		ctrl1 = ADAU1977_SAI_CTRL1_SLOT_WIDTH_16;
+		break;
+	case 24:
+		/* We can only generate 16 bit or 32 bit wide slots */
+		if (adau1977->master)
+			return -EINVAL;
+		ctrl1 = ADAU1977_SAI_CTRL1_SLOT_WIDTH_24;
+		break;
+	case 32:
+		ctrl1 = ADAU1977_SAI_CTRL1_SLOT_WIDTH_32;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	switch (slots) {
+	case 2:
+		ctrl0 = ADAU1977_SAI_CTRL0_SAI_TDM_2;
+		break;
+	case 4:
+		ctrl0 = ADAU1977_SAI_CTRL0_SAI_TDM_4;
+		break;
+	case 8:
+		ctrl0 = ADAU1977_SAI_CTRL0_SAI_TDM_8;
+		break;
+	case 16:
+		ctrl0 = ADAU1977_SAI_CTRL0_SAI_TDM_16;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	ret = regmap_update_bits(adau1977->regmap, ADAU1977_REG_SAI_OVERTEMP,
+		ADAU1977_SAI_OVERTEMP_DRV_C(0) |
+		ADAU1977_SAI_OVERTEMP_DRV_C(1) |
+		ADAU1977_SAI_OVERTEMP_DRV_C(2) |
+		ADAU1977_SAI_OVERTEMP_DRV_C(3), drv);
+	if (ret)
+		return ret;
+
+	ret = regmap_write(adau1977->regmap, ADAU1977_REG_CMAP12,
+		(slot[1] << ADAU1977_CHAN_MAP_SECOND_SLOT_OFFSET) |
+		(slot[0] << ADAU1977_CHAN_MAP_FIRST_SLOT_OFFSET));
+	if (ret)
+		return ret;
+
+	ret = regmap_write(adau1977->regmap, ADAU1977_REG_CMAP34,
+		(slot[3] << ADAU1977_CHAN_MAP_SECOND_SLOT_OFFSET) |
+		(slot[2] << ADAU1977_CHAN_MAP_FIRST_SLOT_OFFSET));
+	if (ret)
+		return ret;
+
+	ret = regmap_update_bits(adau1977->regmap, ADAU1977_REG_SAI_CTRL0,
+		ADAU1977_SAI_CTRL0_SAI_MASK, ctrl0);
+	if (ret)
+		return ret;
+
+	ret = regmap_update_bits(adau1977->regmap, ADAU1977_REG_SAI_CTRL1,
+		ADAU1977_SAI_CTRL1_SLOT_WIDTH_MASK, ctrl1);
+	if (ret)
+		return ret;
+
+	adau1977->slot_width = width;
+
+	/* In master mode the maximum bitclock is 24.576 MHz */
+	adau1977->max_master_fs = min(192000, 24576000 / width / slots);
+
+	return 0;
+}
+
+static int adau1977_mute(struct snd_soc_dai *dai, int mute, int stream)
+{
+	struct adau1977 *adau1977 = snd_soc_codec_get_drvdata(dai->codec);
+	unsigned int val;
+
+	if (mute)
+		val = ADAU1977_MISC_CONTROL_MMUTE;
+	else
+		val = 0;
+
+	return regmap_update_bits(adau1977->regmap, ADAU1977_REG_MISC_CONTROL,
+			ADAU1977_MISC_CONTROL_MMUTE, val);
+}
+
+static int adau1977_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+	struct adau1977 *adau1977 = snd_soc_codec_get_drvdata(dai->codec);
+	unsigned int ctrl0 = 0, ctrl1 = 0, block_power = 0;
+	bool invert_lrclk;
+	int ret;
+
+	switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+	case SND_SOC_DAIFMT_CBS_CFS:
+		adau1977->master = false;
+		break;
+	case SND_SOC_DAIFMT_CBM_CFM:
+		ctrl1 |= ADAU1977_SAI_CTRL1_MASTER;
+		adau1977->master = true;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+	case SND_SOC_DAIFMT_NB_NF:
+		invert_lrclk = false;
+		break;
+	case SND_SOC_DAIFMT_IB_NF:
+		block_power |= ADAU1977_BLOCK_POWER_SAI_BCLK_EDGE;
+		invert_lrclk = false;
+		break;
+	case SND_SOC_DAIFMT_NB_IF:
+		invert_lrclk = true;
+		break;
+	case SND_SOC_DAIFMT_IB_IF:
+		block_power |= ADAU1977_BLOCK_POWER_SAI_BCLK_EDGE;
+		invert_lrclk = true;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	adau1977->right_j = false;
+	switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+	case SND_SOC_DAIFMT_I2S:
+		ctrl0 |= ADAU1977_SAI_CTRL0_FMT_I2S;
+		break;
+	case SND_SOC_DAIFMT_LEFT_J:
+		ctrl0 |= ADAU1977_SAI_CTRL0_FMT_LJ;
+		invert_lrclk = !invert_lrclk;
+		break;
+	case SND_SOC_DAIFMT_RIGHT_J:
+		ctrl0 |= ADAU1977_SAI_CTRL0_FMT_RJ_24BIT;
+		adau1977->right_j = true;
+		invert_lrclk = !invert_lrclk;
+		break;
+	case SND_SOC_DAIFMT_DSP_A:
+		ctrl1 |= ADAU1977_SAI_CTRL1_LRCLK_PULSE;
+		ctrl0 |= ADAU1977_SAI_CTRL0_FMT_I2S;
+		invert_lrclk = false;
+		break;
+	case SND_SOC_DAIFMT_DSP_B:
+		ctrl1 |= ADAU1977_SAI_CTRL1_LRCLK_PULSE;
+		ctrl0 |= ADAU1977_SAI_CTRL0_FMT_LJ;
+		invert_lrclk = false;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	if (invert_lrclk)
+		block_power |= ADAU1977_BLOCK_POWER_SAI_LR_POL;
+
+	ret = regmap_update_bits(adau1977->regmap, ADAU1977_REG_BLOCK_POWER_SAI,
+		ADAU1977_BLOCK_POWER_SAI_LR_POL |
+		ADAU1977_BLOCK_POWER_SAI_BCLK_EDGE, block_power);
+	if (ret)
+		return ret;
+
+	ret = regmap_update_bits(adau1977->regmap, ADAU1977_REG_SAI_CTRL0,
+		ADAU1977_SAI_CTRL0_FMT_MASK,
+		ctrl0);
+	if (ret)
+		return ret;
+
+	return regmap_update_bits(adau1977->regmap, ADAU1977_REG_SAI_CTRL1,
+		ADAU1977_SAI_CTRL1_MASTER | ADAU1977_SAI_CTRL1_LRCLK_PULSE,
+		ctrl1);
+}
+
+static int adau1977_startup(struct snd_pcm_substream *substream,
+	struct snd_soc_dai *dai)
+{
+	struct adau1977 *adau1977 = snd_soc_codec_get_drvdata(dai->codec);
+	u64 formats = 0;
+
+	if (adau1977->slot_width == 16)
+		formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S16_BE;
+	else if (adau1977->right_j || adau1977->slot_width == 24)
+		formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S16_BE |
+			SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S24_BE;
+
+	snd_pcm_hw_constraint_list(substream->runtime, 0,
+		SNDRV_PCM_HW_PARAM_RATE, &adau1977->constraints);
+
+	if (adau1977->master)
+		snd_pcm_hw_constraint_minmax(substream->runtime,
+			SNDRV_PCM_HW_PARAM_RATE, 8000, adau1977->max_master_fs);
+
+	if (formats != 0)
+		snd_pcm_hw_constraint_mask64(substream->runtime,
+			SNDRV_PCM_HW_PARAM_FORMAT, formats);
+
+	return 0;
+}
+
+static int adau1977_set_tristate(struct snd_soc_dai *dai, int tristate)
+{
+	struct adau1977 *adau1977 = snd_soc_codec_get_drvdata(dai->codec);
+	unsigned int val;
+
+	if (tristate)
+		val = ADAU1977_SAI_OVERTEMP_DRV_HIZ;
+	else
+		val = 0;
+
+	return regmap_update_bits(adau1977->regmap, ADAU1977_REG_SAI_OVERTEMP,
+		ADAU1977_SAI_OVERTEMP_DRV_HIZ, val);
+}
+
+static const struct snd_soc_dai_ops adau1977_dai_ops = {
+	.startup	= adau1977_startup,
+	.hw_params	= adau1977_hw_params,
+	.mute_stream	= adau1977_mute,
+	.set_fmt	= adau1977_set_dai_fmt,
+	.set_tdm_slot	= adau1977_set_tdm_slot,
+	.set_tristate	= adau1977_set_tristate,
+};
+
+static struct snd_soc_dai_driver adau1977_dai = {
+	.name = "adau1977-hifi",
+	.capture = {
+		.stream_name = "Capture",
+		.channels_min = 1,
+		.channels_max = 4,
+		.rates = SNDRV_PCM_RATE_KNOT,
+		.formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE |
+		    SNDRV_PCM_FMTBIT_S32_LE,
+		.sig_bits = 24,
+	},
+	.ops = &adau1977_dai_ops,
+};
+
+static const unsigned int adau1977_rates[] = {
+	8000, 16000, 32000, 64000, 128000,
+	11025, 22050, 44100, 88200, 172400,
+	12000, 24000, 48000, 96000, 192000,
+};
+
+#define ADAU1977_RATE_CONSTRAINT_MASK_32000 0x001f
+#define ADAU1977_RATE_CONSTRAINT_MASK_44100 0x03e0
+#define ADAU1977_RATE_CONSTRAINT_MASK_48000 0x7c00
+/* All rates >= 32000 */
+#define ADAU1977_RATE_CONSTRAINT_MASK_LRCLK 0x739c
+
+static bool adau1977_check_sysclk(unsigned int mclk, unsigned int base_freq)
+{
+	unsigned int mcs;
+
+	if (mclk % (base_freq * 128) != 0)
+		return false;
+
+	mcs = mclk / (128 * base_freq);
+	if (mcs < 1 || mcs > 6 || mcs == 5)
+		return false;
+
+	return true;
+}
+
+static int adau1977_set_sysclk(struct snd_soc_codec *codec,
+	int clk_id, int source, unsigned int freq, int dir)
+{
+	struct adau1977 *adau1977 = snd_soc_codec_get_drvdata(codec);
+	unsigned int mask = 0;
+	unsigned int clk_src;
+	unsigned int ret;
+
+	if (dir != SND_SOC_CLOCK_IN)
+		return -EINVAL;
+
+	if (clk_id != ADAU1977_SYSCLK)
+		return -EINVAL;
+
+	switch (source) {
+	case ADAU1977_SYSCLK_SRC_MCLK:
+		clk_src = 0;
+		break;
+	case ADAU1977_SYSCLK_SRC_LRCLK:
+		clk_src = ADAU1977_PLL_CLK_S;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	if (freq != 0 && source == ADAU1977_SYSCLK_SRC_MCLK) {
+		if (freq < 4000000 || freq > 36864000)
+			return -EINVAL;
+
+		if (adau1977_check_sysclk(freq, 32000))
+			mask |= ADAU1977_RATE_CONSTRAINT_MASK_32000;
+		if (adau1977_check_sysclk(freq, 44100))
+			mask |= ADAU1977_RATE_CONSTRAINT_MASK_44100;
+		if (adau1977_check_sysclk(freq, 48000))
+			mask |= ADAU1977_RATE_CONSTRAINT_MASK_48000;
+
+		if (mask == 0)
+			return -EINVAL;
+	} else if (source == ADAU1977_SYSCLK_SRC_LRCLK) {
+		mask = ADAU1977_RATE_CONSTRAINT_MASK_LRCLK;
+	}
+
+	ret = regmap_update_bits(adau1977->regmap, ADAU1977_REG_PLL,
+		ADAU1977_PLL_CLK_S, clk_src);
+	if (ret)
+		return ret;
+
+	adau1977->constraints.mask = mask;
+	adau1977->sysclk_src = source;
+	adau1977->sysclk = freq;
+
+	return 0;
+}
+
+static int adau1977_codec_probe(struct snd_soc_codec *codec)
+{
+	struct adau1977 *adau1977 = snd_soc_codec_get_drvdata(codec);
+	int ret;
+
+	switch (adau1977->type) {
+	case ADAU1977:
+		ret = snd_soc_dapm_new_controls(&codec->dapm,
+			adau1977_micbias_dapm_widgets,
+			ARRAY_SIZE(adau1977_micbias_dapm_widgets));
+		if (ret < 0)
+			return ret;
+		break;
+	default:
+		break;
+	}
+
+	return 0;
+}
+
+static struct snd_soc_codec_driver adau1977_codec_driver = {
+	.probe = adau1977_codec_probe,
+	.set_bias_level = adau1977_set_bias_level,
+	.set_sysclk = adau1977_set_sysclk,
+	.idle_bias_off = true,
+
+	.controls = adau1977_snd_controls,
+	.num_controls = ARRAY_SIZE(adau1977_snd_controls),
+	.dapm_widgets = adau1977_dapm_widgets,
+	.num_dapm_widgets = ARRAY_SIZE(adau1977_dapm_widgets),
+	.dapm_routes = adau1977_dapm_routes,
+	.num_dapm_routes = ARRAY_SIZE(adau1977_dapm_routes),
+};
+
+static int adau1977_setup_micbias(struct adau1977 *adau1977)
+{
+	struct adau1977_platform_data *pdata = adau1977->dev->platform_data;
+	unsigned int micbias;
+
+	if (pdata) {
+		micbias = pdata->micbias;
+		if (micbias > ADAU1977_MICBIAS_9V0)
+			return -EINVAL;
+
+	} else {
+		micbias = ADAU1977_MICBIAS_8V5;
+	}
+
+	return regmap_update_bits(adau1977->regmap, ADAU1977_REG_MICBIAS,
+		ADAU1977_MICBIAS_MB_VOLTS_MASK,
+		micbias << ADAU1977_MICBIAS_MB_VOLTS_OFFSET);
+}
+
+int adau1977_probe(struct device *dev, struct regmap *regmap,
+	enum adau1977_type type, void (*switch_mode)(struct device *dev))
+{
+	unsigned int power_off_mask;
+	struct adau1977 *adau1977;
+	int ret;
+
+	if (IS_ERR(regmap))
+		return PTR_ERR(regmap);
+
+	adau1977 = devm_kzalloc(dev, sizeof(*adau1977), GFP_KERNEL);
+	if (adau1977 == NULL)
+		return -ENOMEM;
+
+	adau1977->dev = dev;
+	adau1977->type = type;
+	adau1977->regmap = regmap;
+	adau1977->switch_mode = switch_mode;
+	adau1977->max_master_fs = 192000;
+
+	adau1977->constraints.list = adau1977_rates;
+	adau1977->constraints.count = ARRAY_SIZE(adau1977_rates);
+
+	adau1977->avdd_reg = devm_regulator_get(dev, "AVDD");
+	if (IS_ERR(adau1977->avdd_reg))
+		return PTR_ERR(adau1977->avdd_reg);
+
+	adau1977->dvdd_reg = devm_regulator_get_optional(dev, "DVDD");
+	if (IS_ERR(adau1977->dvdd_reg)) {
+		if (PTR_ERR(adau1977->dvdd_reg) != -ENODEV)
+			return PTR_ERR(adau1977->dvdd_reg);
+		adau1977->dvdd_reg = NULL;
+	}
+
+	adau1977->reset_gpio = devm_gpiod_get(dev, "reset");
+	if (IS_ERR(adau1977->reset_gpio)) {
+		ret = PTR_ERR(adau1977->reset_gpio);
+		if (ret != -ENOENT && ret != -ENOSYS)
+			return PTR_ERR(adau1977->reset_gpio);
+		adau1977->reset_gpio = NULL;
+	}
+
+	dev_set_drvdata(dev, adau1977);
+
+	if (adau1977->reset_gpio) {
+		ret = gpiod_direction_output(adau1977->reset_gpio, 0);
+		if (ret)
+			return ret;
+		ndelay(100);
+	}
+
+	ret = adau1977_power_enable(adau1977);
+	if (ret)
+		return ret;
+
+	if (type == ADAU1977) {
+		ret = adau1977_setup_micbias(adau1977);
+		if (ret)
+			goto err_poweroff;
+	}
+
+	if (adau1977->dvdd_reg)
+		power_off_mask = ~0;
+	else
+		power_off_mask = ~ADAU1977_BLOCK_POWER_SAI_LDO_EN;
+
+	ret = regmap_update_bits(adau1977->regmap, ADAU1977_REG_BLOCK_POWER_SAI,
+				power_off_mask, 0x00);
+	if (ret)
+		goto err_poweroff;
+
+	ret = adau1977_power_disable(adau1977);
+	if (ret)
+		return ret;
+
+	return snd_soc_register_codec(dev, &adau1977_codec_driver,
+			&adau1977_dai, 1);
+
+err_poweroff:
+	adau1977_power_disable(adau1977);
+	return ret;
+
+}
+EXPORT_SYMBOL_GPL(adau1977_probe);
+
+static bool adau1977_register_volatile(struct device *dev, unsigned int reg)
+{
+	switch (reg) {
+	case ADAU1977_REG_STATUS(0):
+	case ADAU1977_REG_STATUS(1):
+	case ADAU1977_REG_STATUS(2):
+	case ADAU1977_REG_STATUS(3):
+	case ADAU1977_REG_ADC_CLIP:
+		return true;
+	}
+
+	return false;
+}
+
+const struct regmap_config adau1977_regmap_config = {
+	.max_register = ADAU1977_REG_DC_HPF_CAL,
+	.volatile_reg = adau1977_register_volatile,
+
+	.cache_type = REGCACHE_RBTREE,
+	.reg_defaults = adau1977_reg_defaults,
+	.num_reg_defaults = ARRAY_SIZE(adau1977_reg_defaults),
+};
+EXPORT_SYMBOL_GPL(adau1977_regmap_config);
+
+MODULE_DESCRIPTION("ASoC ADAU1977/ADAU1978/ADAU1979 driver");
+MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/adau1977.h b/sound/soc/codecs/adau1977.h
new file mode 100644
index 000000000000..95e714345a86
--- /dev/null
+++ b/sound/soc/codecs/adau1977.h
@@ -0,0 +1,37 @@
+/*
+ * ADAU1977/ADAU1978/ADAU1979 driver
+ *
+ * Copyright 2014 Analog Devices Inc.
+ *  Author: Lars-Peter Clausen <lars@metafoo.de>
+ *
+ * Licensed under the GPL-2.
+ */
+
+#ifndef __SOUND_SOC_CODECS_ADAU1977_H__
+#define __SOUND_SOC_CODECS_ADAU1977_H__
+
+#include <linux/regmap.h>
+
+struct device;
+
+enum adau1977_type {
+	ADAU1977,
+	ADAU1978,
+	ADAU1979,
+};
+
+int adau1977_probe(struct device *dev, struct regmap *regmap,
+	enum adau1977_type type, void (*switch_mode)(struct device *dev));
+
+extern const struct regmap_config adau1977_regmap_config;
+
+enum adau1977_clk_id {
+	ADAU1977_SYSCLK,
+};
+
+enum adau1977_sysclk_src {
+	ADAU1977_SYSCLK_SRC_MCLK,
+	ADAU1977_SYSCLK_SRC_LRCLK,
+};
+
+#endif
diff --git a/sound/soc/codecs/adav801.c b/sound/soc/codecs/adav801.c
new file mode 100644
index 000000000000..790fce33ab10
--- /dev/null
+++ b/sound/soc/codecs/adav801.c
@@ -0,0 +1,53 @@
+/*
+ * ADAV801 audio driver
+ *
+ * Copyright 2014 Analog Devices Inc.
+ *
+ * Licensed under the GPL-2.
+ */
+
+#include <linux/module.h>
+#include <linux/spi/spi.h>
+#include <linux/regmap.h>
+
+#include <sound/soc.h>
+
+#include "adav80x.h"
+
+static const struct spi_device_id adav80x_spi_id[] = {
+	{ "adav801", 0 },
+	{ }
+};
+MODULE_DEVICE_TABLE(spi, adav80x_spi_id);
+
+static int adav80x_spi_probe(struct spi_device *spi)
+{
+	struct regmap_config config;
+
+	config = adav80x_regmap_config;
+	config.read_flag_mask = 0x01;
+
+	return adav80x_bus_probe(&spi->dev, devm_regmap_init_spi(spi, &config));
+}
+
+static int adav80x_spi_remove(struct spi_device *spi)
+{
+	snd_soc_unregister_codec(&spi->dev);
+	return 0;
+}
+
+static struct spi_driver adav80x_spi_driver = {
+	.driver = {
+		.name	= "adav801",
+		.owner	= THIS_MODULE,
+	},
+	.probe		= adav80x_spi_probe,
+	.remove		= adav80x_spi_remove,
+	.id_table	= adav80x_spi_id,
+};
+module_spi_driver(adav80x_spi_driver);
+
+MODULE_DESCRIPTION("ASoC ADAV801 driver");
+MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
+MODULE_AUTHOR("Yi Li <yi.li@analog.com>>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/adav803.c b/sound/soc/codecs/adav803.c
new file mode 100644
index 000000000000..66d9fce34e62
--- /dev/null
+++ b/sound/soc/codecs/adav803.c
@@ -0,0 +1,50 @@
+/*
+ * ADAV803 audio driver
+ *
+ * Copyright 2014 Analog Devices Inc.
+ *
+ * Licensed under the GPL-2.
+ */
+
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/regmap.h>
+
+#include <sound/soc.h>
+
+#include "adav80x.h"
+
+static const struct i2c_device_id adav803_id[] = {
+	{ "adav803", 0 },
+	{ }
+};
+MODULE_DEVICE_TABLE(i2c, adav803_id);
+
+static int adav803_probe(struct i2c_client *client,
+			     const struct i2c_device_id *id)
+{
+	return adav80x_bus_probe(&client->dev,
+		devm_regmap_init_i2c(client, &adav80x_regmap_config));
+}
+
+static int adav803_remove(struct i2c_client *client)
+{
+	snd_soc_unregister_codec(&client->dev);
+	return 0;
+}
+
+static struct i2c_driver adav803_driver = {
+	.driver = {
+		.name = "adav803",
+		.owner = THIS_MODULE,
+	},
+	.probe = adav803_probe,
+	.remove = adav803_remove,
+	.id_table = adav803_id,
+};
+module_i2c_driver(adav803_driver);
+
+MODULE_DESCRIPTION("ASoC ADAV803 driver");
+MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
+MODULE_AUTHOR("Yi Li <yi.li@analog.com>>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/adav80x.c b/sound/soc/codecs/adav80x.c
index f78b27a7c461..7470831ba756 100644
--- a/sound/soc/codecs/adav80x.c
+++ b/sound/soc/codecs/adav80x.c
@@ -8,17 +8,15 @@
  * Licensed under the GPL-2 or later.
  */
 
-#include <linux/init.h>
 #include <linux/module.h>
 #include <linux/kernel.h>
-#include <linux/i2c.h>
-#include <linux/spi/spi.h>
+#include <linux/regmap.h>
 #include <linux/slab.h>
-#include <sound/core.h>
+
 #include <sound/pcm.h>
 #include <sound/pcm_params.h>
-#include <sound/tlv.h>
 #include <sound/soc.h>
+#include <sound/tlv.h>
 
 #include "adav80x.h"
 
@@ -541,6 +539,7 @@ static int adav80x_set_sysclk(struct snd_soc_codec *codec,
 			      unsigned int freq, int dir)
 {
 	struct adav80x *adav80x = snd_soc_codec_get_drvdata(codec);
+	struct snd_soc_dapm_context *dapm = &codec->dapm;
 
 	if (dir == SND_SOC_CLOCK_IN) {
 		switch (clk_id) {
@@ -573,7 +572,7 @@ static int adav80x_set_sysclk(struct snd_soc_codec *codec,
 			regmap_write(adav80x->regmap, ADAV80X_ICLK_CTRL2,
 				iclk_ctrl2);
 
-			snd_soc_dapm_sync(&codec->dapm);
+			snd_soc_dapm_sync(dapm);
 		}
 	} else {
 		unsigned int mask;
@@ -600,17 +599,21 @@ static int adav80x_set_sysclk(struct snd_soc_codec *codec,
 			adav80x->sysclk_pd[clk_id] = false;
 		}
 
+		snd_soc_dapm_mutex_lock(dapm);
+
 		if (adav80x->sysclk_pd[0])
-			snd_soc_dapm_disable_pin(&codec->dapm, "PLL1");
+			snd_soc_dapm_disable_pin_unlocked(dapm, "PLL1");
 		else
-			snd_soc_dapm_force_enable_pin(&codec->dapm, "PLL1");
+			snd_soc_dapm_force_enable_pin_unlocked(dapm, "PLL1");
 
 		if (adav80x->sysclk_pd[1] || adav80x->sysclk_pd[2])
-			snd_soc_dapm_disable_pin(&codec->dapm, "PLL2");
+			snd_soc_dapm_disable_pin_unlocked(dapm, "PLL2");
 		else
-			snd_soc_dapm_force_enable_pin(&codec->dapm, "PLL2");
+			snd_soc_dapm_force_enable_pin_unlocked(dapm, "PLL2");
 
-		snd_soc_dapm_sync(&codec->dapm);
+		snd_soc_dapm_sync_unlocked(dapm);
+
+		snd_soc_dapm_mutex_unlock(dapm);
 	}
 
 	return 0;
@@ -722,7 +725,7 @@ static int adav80x_dai_startup(struct snd_pcm_substream *substream,
 	struct snd_soc_codec *codec = dai->codec;
 	struct adav80x *adav80x = snd_soc_codec_get_drvdata(codec);
 
-	if (!codec->active || !adav80x->rate)
+	if (!snd_soc_codec_is_active(codec) || !adav80x->rate)
 		return 0;
 
 	return snd_pcm_hw_constraint_minmax(substream->runtime,
@@ -735,7 +738,7 @@ static void adav80x_dai_shutdown(struct snd_pcm_substream *substream,
 	struct snd_soc_codec *codec = dai->codec;
 	struct adav80x *adav80x = snd_soc_codec_get_drvdata(codec);
 
-	if (!codec->active)
+	if (!snd_soc_codec_is_active(codec))
 		adav80x->rate = 0;
 }
 
@@ -864,39 +867,26 @@ static struct snd_soc_codec_driver adav80x_codec_driver = {
 	.num_dapm_routes = ARRAY_SIZE(adav80x_dapm_routes),
 };
 
-static int adav80x_bus_probe(struct device *dev, struct regmap *regmap)
+int adav80x_bus_probe(struct device *dev, struct regmap *regmap)
 {
 	struct adav80x *adav80x;
-	int ret;
 
 	if (IS_ERR(regmap))
 		return PTR_ERR(regmap);
 
-	adav80x = kzalloc(sizeof(*adav80x), GFP_KERNEL);
+	adav80x = devm_kzalloc(dev, sizeof(*adav80x), GFP_KERNEL);
 	if (!adav80x)
 		return -ENOMEM;
 
-
 	dev_set_drvdata(dev, adav80x);
 	adav80x->regmap = regmap;
 
-	ret = snd_soc_register_codec(dev, &adav80x_codec_driver,
+	return snd_soc_register_codec(dev, &adav80x_codec_driver,
 		adav80x_dais, ARRAY_SIZE(adav80x_dais));
-	if (ret)
-		kfree(adav80x);
-
-	return ret;
 }
+EXPORT_SYMBOL_GPL(adav80x_bus_probe);
 
-static int adav80x_bus_remove(struct device *dev)
-{
-	snd_soc_unregister_codec(dev);
-	kfree(dev_get_drvdata(dev));
-	return 0;
-}
-
-#if defined(CONFIG_SPI_MASTER)
-static const struct regmap_config adav80x_spi_regmap_config = {
+const struct regmap_config adav80x_regmap_config = {
 	.val_bits = 8,
 	.pad_bits = 1,
 	.reg_bits = 7,
@@ -908,105 +898,7 @@ static const struct regmap_config adav80x_spi_regmap_config = {
 	.reg_defaults = adav80x_reg_defaults,
 	.num_reg_defaults = ARRAY_SIZE(adav80x_reg_defaults),
 };
-
-static const struct spi_device_id adav80x_spi_id[] = {
-	{ "adav801", 0 },
-	{ }
-};
-MODULE_DEVICE_TABLE(spi, adav80x_spi_id);
-
-static int adav80x_spi_probe(struct spi_device *spi)
-{
-	return adav80x_bus_probe(&spi->dev,
-		devm_regmap_init_spi(spi, &adav80x_spi_regmap_config));
-}
-
-static int adav80x_spi_remove(struct spi_device *spi)
-{
-	return adav80x_bus_remove(&spi->dev);
-}
-
-static struct spi_driver adav80x_spi_driver = {
-	.driver = {
-		.name	= "adav801",
-		.owner	= THIS_MODULE,
-	},
-	.probe		= adav80x_spi_probe,
-	.remove		= adav80x_spi_remove,
-	.id_table	= adav80x_spi_id,
-};
-#endif
-
-#if IS_ENABLED(CONFIG_I2C)
-static const struct regmap_config adav80x_i2c_regmap_config = {
-	.val_bits = 8,
-	.pad_bits = 1,
-	.reg_bits = 7,
-
-	.max_register = ADAV80X_PLL_OUTE,
-
-	.cache_type = REGCACHE_RBTREE,
-	.reg_defaults = adav80x_reg_defaults,
-	.num_reg_defaults = ARRAY_SIZE(adav80x_reg_defaults),
-};
-
-static const struct i2c_device_id adav80x_i2c_id[] = {
-	{ "adav803", 0 },
-	{ }
-};
-MODULE_DEVICE_TABLE(i2c, adav80x_i2c_id);
-
-static int adav80x_i2c_probe(struct i2c_client *client,
-			     const struct i2c_device_id *id)
-{
-	return adav80x_bus_probe(&client->dev,
-		devm_regmap_init_i2c(client, &adav80x_i2c_regmap_config));
-}
-
-static int adav80x_i2c_remove(struct i2c_client *client)
-{
-	return adav80x_bus_remove(&client->dev);
-}
-
-static struct i2c_driver adav80x_i2c_driver = {
-	.driver = {
-		.name = "adav803",
-		.owner = THIS_MODULE,
-	},
-	.probe = adav80x_i2c_probe,
-	.remove = adav80x_i2c_remove,
-	.id_table = adav80x_i2c_id,
-};
-#endif
-
-static int __init adav80x_init(void)
-{
-	int ret = 0;
-
-#if IS_ENABLED(CONFIG_I2C)
-	ret = i2c_add_driver(&adav80x_i2c_driver);
-	if (ret)
-		return ret;
-#endif
-
-#if defined(CONFIG_SPI_MASTER)
-	ret = spi_register_driver(&adav80x_spi_driver);
-#endif
-
-	return ret;
-}
-module_init(adav80x_init);
-
-static void __exit adav80x_exit(void)
-{
-#if IS_ENABLED(CONFIG_I2C)
-	i2c_del_driver(&adav80x_i2c_driver);
-#endif
-#if defined(CONFIG_SPI_MASTER)
-	spi_unregister_driver(&adav80x_spi_driver);
-#endif
-}
-module_exit(adav80x_exit);
+EXPORT_SYMBOL_GPL(adav80x_regmap_config);
 
 MODULE_DESCRIPTION("ASoC ADAV80x driver");
 MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
diff --git a/sound/soc/codecs/adav80x.h b/sound/soc/codecs/adav80x.h
index adb0fc76d4e3..8a1d7c09dca5 100644
--- a/sound/soc/codecs/adav80x.h
+++ b/sound/soc/codecs/adav80x.h
@@ -9,6 +9,13 @@
 #ifndef _ADAV80X_H
 #define _ADAV80X_H
 
+#include <linux/regmap.h>
+
+struct device;
+
+extern const struct regmap_config adav80x_regmap_config;
+int adav80x_bus_probe(struct device *dev, struct regmap *regmap);
+
 enum adav80x_pll_src {
 	ADAV80X_PLL_SRC_XIN,
 	ADAV80X_PLL_SRC_XTAL,
diff --git a/sound/soc/codecs/ak4104.c b/sound/soc/codecs/ak4104.c
index b4819dcd4f4d..10adf25d4c14 100644
--- a/sound/soc/codecs/ak4104.c
+++ b/sound/soc/codecs/ak4104.c
@@ -174,8 +174,6 @@ static int ak4104_probe(struct snd_soc_codec *codec)
 	struct ak4104_private *ak4104 = snd_soc_codec_get_drvdata(codec);
 	int ret;
 
-	codec->control_data = ak4104->regmap;
-
 	/* set power-up and non-reset bits */
 	ret = regmap_update_bits(ak4104->regmap, AK4104_REG_CONTROL1,
 				 AK4104_CONTROL1_PW | AK4104_CONTROL1_RSTN,
diff --git a/sound/soc/codecs/ak4641.c b/sound/soc/codecs/ak4641.c
index 94cbe508dd37..684b56f2856a 100644
--- a/sound/soc/codecs/ak4641.c
+++ b/sound/soc/codecs/ak4641.c
@@ -113,14 +113,14 @@ static const DECLARE_TLV_DB_SCALE(alc_tlv, -800, 50, 0);
 static const DECLARE_TLV_DB_SCALE(aux_in_tlv, -2100, 300, 0);
 
 
-static const struct soc_enum ak4641_mono_out_enum =
-	SOC_ENUM_SINGLE(AK4641_SIG1, 6, 2, ak4641_mono_out);
-static const struct soc_enum ak4641_hp_out_enum =
-	SOC_ENUM_SINGLE(AK4641_MODE2, 2, 2, ak4641_hp_out);
-static const struct soc_enum ak4641_mic_select_enum =
-	SOC_ENUM_SINGLE(AK4641_MIC, 1, 2, ak4641_mic_select);
-static const struct soc_enum ak4641_mic_or_dac_enum =
-	SOC_ENUM_SINGLE(AK4641_BTIF, 4, 2, ak4641_mic_or_dac);
+static SOC_ENUM_SINGLE_DECL(ak4641_mono_out_enum,
+			    AK4641_SIG1, 6, ak4641_mono_out);
+static SOC_ENUM_SINGLE_DECL(ak4641_hp_out_enum,
+			    AK4641_MODE2, 2, ak4641_hp_out);
+static SOC_ENUM_SINGLE_DECL(ak4641_mic_select_enum,
+			    AK4641_MIC, 1, ak4641_mic_select);
+static SOC_ENUM_SINGLE_DECL(ak4641_mic_or_dac_enum,
+			    AK4641_BTIF, 4, ak4641_mic_or_dac);
 
 static const struct snd_kcontrol_new ak4641_snd_controls[] = {
 	SOC_ENUM("Mono 1 Output", ak4641_mono_out_enum),
diff --git a/sound/soc/codecs/ak4671.c b/sound/soc/codecs/ak4671.c
index 25bdf6ad4a54..deb2b44669de 100644
--- a/sound/soc/codecs/ak4671.c
+++ b/sound/soc/codecs/ak4671.c
@@ -15,6 +15,7 @@
 #include <linux/init.h>
 #include <linux/i2c.h>
 #include <linux/delay.h>
+#include <linux/regmap.h>
 #include <linux/slab.h>
 #include <sound/soc.h>
 #include <sound/initval.h>
@@ -23,104 +24,99 @@
 #include "ak4671.h"
 
 
-/* codec private data */
-struct ak4671_priv {
-	enum snd_soc_control_type control_type;
-};
-
 /* ak4671 register cache & default register settings */
-static const u8 ak4671_reg[AK4671_CACHEREGNUM] = {
-	0x00,	/* AK4671_AD_DA_POWER_MANAGEMENT	(0x00)	*/
-	0xf6,	/* AK4671_PLL_MODE_SELECT0		(0x01)	*/
-	0x00,	/* AK4671_PLL_MODE_SELECT1		(0x02)	*/
-	0x02,	/* AK4671_FORMAT_SELECT			(0x03)	*/
-	0x00,	/* AK4671_MIC_SIGNAL_SELECT		(0x04)	*/
-	0x55,	/* AK4671_MIC_AMP_GAIN			(0x05)	*/
-	0x00,	/* AK4671_MIXING_POWER_MANAGEMENT0	(0x06)	*/
-	0x00,	/* AK4671_MIXING_POWER_MANAGEMENT1	(0x07)	*/
-	0xb5,	/* AK4671_OUTPUT_VOLUME_CONTROL		(0x08)	*/
-	0x00,	/* AK4671_LOUT1_SIGNAL_SELECT		(0x09)	*/
-	0x00,	/* AK4671_ROUT1_SIGNAL_SELECT		(0x0a)	*/
-	0x00,	/* AK4671_LOUT2_SIGNAL_SELECT		(0x0b)	*/
-	0x00,	/* AK4671_ROUT2_SIGNAL_SELECT		(0x0c)	*/
-	0x00,	/* AK4671_LOUT3_SIGNAL_SELECT		(0x0d)	*/
-	0x00,	/* AK4671_ROUT3_SIGNAL_SELECT		(0x0e)	*/
-	0x00,	/* AK4671_LOUT1_POWER_MANAGERMENT	(0x0f)	*/
-	0x00,	/* AK4671_LOUT2_POWER_MANAGERMENT	(0x10)	*/
-	0x80,	/* AK4671_LOUT3_POWER_MANAGERMENT	(0x11)	*/
-	0x91,	/* AK4671_LCH_INPUT_VOLUME_CONTROL	(0x12)	*/
-	0x91,	/* AK4671_RCH_INPUT_VOLUME_CONTROL	(0x13)	*/
-	0xe1,	/* AK4671_ALC_REFERENCE_SELECT		(0x14)	*/
-	0x00,	/* AK4671_DIGITAL_MIXING_CONTROL	(0x15)	*/
-	0x00,	/* AK4671_ALC_TIMER_SELECT		(0x16)	*/
-	0x00,	/* AK4671_ALC_MODE_CONTROL		(0x17)	*/
-	0x02,	/* AK4671_MODE_CONTROL1			(0x18)	*/
-	0x01,	/* AK4671_MODE_CONTROL2			(0x19)	*/
-	0x18,	/* AK4671_LCH_OUTPUT_VOLUME_CONTROL	(0x1a)	*/
-	0x18,	/* AK4671_RCH_OUTPUT_VOLUME_CONTROL	(0x1b)	*/
-	0x00,	/* AK4671_SIDETONE_A_CONTROL		(0x1c)	*/
-	0x02,	/* AK4671_DIGITAL_FILTER_SELECT		(0x1d)	*/
-	0x00,	/* AK4671_FIL3_COEFFICIENT0		(0x1e)	*/
-	0x00,	/* AK4671_FIL3_COEFFICIENT1		(0x1f)	*/
-	0x00,	/* AK4671_FIL3_COEFFICIENT2		(0x20)	*/
-	0x00,	/* AK4671_FIL3_COEFFICIENT3		(0x21)	*/
-	0x00,	/* AK4671_EQ_COEFFICIENT0		(0x22)	*/
-	0x00,	/* AK4671_EQ_COEFFICIENT1		(0x23)	*/
-	0x00,	/* AK4671_EQ_COEFFICIENT2		(0x24)	*/
-	0x00,	/* AK4671_EQ_COEFFICIENT3		(0x25)	*/
-	0x00,	/* AK4671_EQ_COEFFICIENT4		(0x26)	*/
-	0x00,	/* AK4671_EQ_COEFFICIENT5		(0x27)	*/
-	0xa9,	/* AK4671_FIL1_COEFFICIENT0		(0x28)	*/
-	0x1f,	/* AK4671_FIL1_COEFFICIENT1		(0x29)	*/
-	0xad,	/* AK4671_FIL1_COEFFICIENT2		(0x2a)	*/
-	0x20,	/* AK4671_FIL1_COEFFICIENT3		(0x2b)	*/
-	0x00,	/* AK4671_FIL2_COEFFICIENT0		(0x2c)	*/
-	0x00,	/* AK4671_FIL2_COEFFICIENT1		(0x2d)	*/
-	0x00,	/* AK4671_FIL2_COEFFICIENT2		(0x2e)	*/
-	0x00,	/* AK4671_FIL2_COEFFICIENT3		(0x2f)	*/
-	0x00,	/* AK4671_DIGITAL_FILTER_SELECT2	(0x30)	*/
-	0x00,	/* this register not used			*/
-	0x00,	/* AK4671_E1_COEFFICIENT0		(0x32)	*/
-	0x00,	/* AK4671_E1_COEFFICIENT1		(0x33)	*/
-	0x00,	/* AK4671_E1_COEFFICIENT2		(0x34)	*/
-	0x00,	/* AK4671_E1_COEFFICIENT3		(0x35)	*/
-	0x00,	/* AK4671_E1_COEFFICIENT4		(0x36)	*/
-	0x00,	/* AK4671_E1_COEFFICIENT5		(0x37)	*/
-	0x00,	/* AK4671_E2_COEFFICIENT0		(0x38)	*/
-	0x00,	/* AK4671_E2_COEFFICIENT1		(0x39)	*/
-	0x00,	/* AK4671_E2_COEFFICIENT2		(0x3a)	*/
-	0x00,	/* AK4671_E2_COEFFICIENT3		(0x3b)	*/
-	0x00,	/* AK4671_E2_COEFFICIENT4		(0x3c)	*/
-	0x00,	/* AK4671_E2_COEFFICIENT5		(0x3d)	*/
-	0x00,	/* AK4671_E3_COEFFICIENT0		(0x3e)	*/
-	0x00,	/* AK4671_E3_COEFFICIENT1		(0x3f)	*/
-	0x00,	/* AK4671_E3_COEFFICIENT2		(0x40)	*/
-	0x00,	/* AK4671_E3_COEFFICIENT3		(0x41)	*/
-	0x00,	/* AK4671_E3_COEFFICIENT4		(0x42)	*/
-	0x00,	/* AK4671_E3_COEFFICIENT5		(0x43)	*/
-	0x00,	/* AK4671_E4_COEFFICIENT0		(0x44)	*/
-	0x00,	/* AK4671_E4_COEFFICIENT1		(0x45)	*/
-	0x00,	/* AK4671_E4_COEFFICIENT2		(0x46)	*/
-	0x00,	/* AK4671_E4_COEFFICIENT3		(0x47)	*/
-	0x00,	/* AK4671_E4_COEFFICIENT4		(0x48)	*/
-	0x00,	/* AK4671_E4_COEFFICIENT5		(0x49)	*/
-	0x00,	/* AK4671_E5_COEFFICIENT0		(0x4a)	*/
-	0x00,	/* AK4671_E5_COEFFICIENT1		(0x4b)	*/
-	0x00,	/* AK4671_E5_COEFFICIENT2		(0x4c)	*/
-	0x00,	/* AK4671_E5_COEFFICIENT3		(0x4d)	*/
-	0x00,	/* AK4671_E5_COEFFICIENT4		(0x4e)	*/
-	0x00,	/* AK4671_E5_COEFFICIENT5		(0x4f)	*/
-	0x88,	/* AK4671_EQ_CONTROL_250HZ_100HZ	(0x50)	*/
-	0x88,	/* AK4671_EQ_CONTROL_3500HZ_1KHZ	(0x51)	*/
-	0x08,	/* AK4671_EQ_CONTRO_10KHZ		(0x52)	*/
-	0x00,	/* AK4671_PCM_IF_CONTROL0		(0x53)	*/
-	0x00,	/* AK4671_PCM_IF_CONTROL1		(0x54)	*/
-	0x00,	/* AK4671_PCM_IF_CONTROL2		(0x55)	*/
-	0x18,	/* AK4671_DIGITAL_VOLUME_B_CONTROL	(0x56)	*/
-	0x18,	/* AK4671_DIGITAL_VOLUME_C_CONTROL	(0x57)	*/
-	0x00,	/* AK4671_SIDETONE_VOLUME_CONTROL	(0x58)	*/
-	0x00,	/* AK4671_DIGITAL_MIXING_CONTROL2	(0x59)	*/
-	0x00,	/* AK4671_SAR_ADC_CONTROL		(0x5a)	*/
+static const struct reg_default ak4671_reg_defaults[] = {
+	{ 0x00, 0x00 },	/* AK4671_AD_DA_POWER_MANAGEMENT	(0x00)	*/
+	{ 0x01, 0xf6 },	/* AK4671_PLL_MODE_SELECT0		(0x01)	*/
+	{ 0x02, 0x00 },	/* AK4671_PLL_MODE_SELECT1		(0x02)	*/
+	{ 0x03, 0x02 },	/* AK4671_FORMAT_SELECT			(0x03)	*/
+	{ 0x04, 0x00 },	/* AK4671_MIC_SIGNAL_SELECT		(0x04)	*/
+	{ 0x05, 0x55 },	/* AK4671_MIC_AMP_GAIN			(0x05)	*/
+	{ 0x06, 0x00 },	/* AK4671_MIXING_POWER_MANAGEMENT0	(0x06)	*/
+	{ 0x07, 0x00 },	/* AK4671_MIXING_POWER_MANAGEMENT1	(0x07)	*/
+	{ 0x08, 0xb5 },	/* AK4671_OUTPUT_VOLUME_CONTROL		(0x08)	*/
+	{ 0x09, 0x00 },	/* AK4671_LOUT1_SIGNAL_SELECT		(0x09)	*/
+	{ 0x0a, 0x00 },	/* AK4671_ROUT1_SIGNAL_SELECT		(0x0a)	*/
+	{ 0x0b, 0x00 },	/* AK4671_LOUT2_SIGNAL_SELECT		(0x0b)	*/
+	{ 0x0c, 0x00 },	/* AK4671_ROUT2_SIGNAL_SELECT		(0x0c)	*/
+	{ 0x0d, 0x00 },	/* AK4671_LOUT3_SIGNAL_SELECT		(0x0d)	*/
+	{ 0x0e, 0x00 },	/* AK4671_ROUT3_SIGNAL_SELECT		(0x0e)	*/
+	{ 0x0f, 0x00 },	/* AK4671_LOUT1_POWER_MANAGERMENT	(0x0f)	*/
+	{ 0x10, 0x00 },	/* AK4671_LOUT2_POWER_MANAGERMENT	(0x10)	*/
+	{ 0x11, 0x80 },	/* AK4671_LOUT3_POWER_MANAGERMENT	(0x11)	*/
+	{ 0x12, 0x91 },	/* AK4671_LCH_INPUT_VOLUME_CONTROL	(0x12)	*/
+	{ 0x13, 0x91 },	/* AK4671_RCH_INPUT_VOLUME_CONTROL	(0x13)	*/
+	{ 0x14, 0xe1 },	/* AK4671_ALC_REFERENCE_SELECT		(0x14)	*/
+	{ 0x15, 0x00 },	/* AK4671_DIGITAL_MIXING_CONTROL	(0x15)	*/
+	{ 0x16, 0x00 },	/* AK4671_ALC_TIMER_SELECT		(0x16)	*/
+	{ 0x17, 0x00 },	/* AK4671_ALC_MODE_CONTROL		(0x17)	*/
+	{ 0x18, 0x02 },	/* AK4671_MODE_CONTROL1			(0x18)	*/
+	{ 0x19, 0x01 },	/* AK4671_MODE_CONTROL2			(0x19)	*/
+	{ 0x1a, 0x18 },	/* AK4671_LCH_OUTPUT_VOLUME_CONTROL	(0x1a)	*/
+	{ 0x1b, 0x18 },	/* AK4671_RCH_OUTPUT_VOLUME_CONTROL	(0x1b)	*/
+	{ 0x1c, 0x00 },	/* AK4671_SIDETONE_A_CONTROL		(0x1c)	*/
+	{ 0x1d, 0x02 },	/* AK4671_DIGITAL_FILTER_SELECT		(0x1d)	*/
+	{ 0x1e, 0x00 },	/* AK4671_FIL3_COEFFICIENT0		(0x1e)	*/
+	{ 0x1f, 0x00 },	/* AK4671_FIL3_COEFFICIENT1		(0x1f)	*/
+	{ 0x20, 0x00 },	/* AK4671_FIL3_COEFFICIENT2		(0x20)	*/
+	{ 0x21, 0x00 },	/* AK4671_FIL3_COEFFICIENT3		(0x21)	*/
+	{ 0x22, 0x00 },	/* AK4671_EQ_COEFFICIENT0		(0x22)	*/
+	{ 0x23, 0x00 },	/* AK4671_EQ_COEFFICIENT1		(0x23)	*/
+	{ 0x24, 0x00 },	/* AK4671_EQ_COEFFICIENT2		(0x24)	*/
+	{ 0x25, 0x00 },	/* AK4671_EQ_COEFFICIENT3		(0x25)	*/
+	{ 0x26, 0x00 },	/* AK4671_EQ_COEFFICIENT4		(0x26)	*/
+	{ 0x27, 0x00 },	/* AK4671_EQ_COEFFICIENT5		(0x27)	*/
+	{ 0x28, 0xa9 },	/* AK4671_FIL1_COEFFICIENT0		(0x28)	*/
+	{ 0x29, 0x1f },	/* AK4671_FIL1_COEFFICIENT1		(0x29)	*/
+	{ 0x2a, 0xad },	/* AK4671_FIL1_COEFFICIENT2		(0x2a)	*/
+	{ 0x2b, 0x20 },	/* AK4671_FIL1_COEFFICIENT3		(0x2b)	*/
+	{ 0x2c, 0x00 },	/* AK4671_FIL2_COEFFICIENT0		(0x2c)	*/
+	{ 0x2d, 0x00 },	/* AK4671_FIL2_COEFFICIENT1		(0x2d)	*/
+	{ 0x2e, 0x00 },	/* AK4671_FIL2_COEFFICIENT2		(0x2e)	*/
+	{ 0x2f, 0x00 },	/* AK4671_FIL2_COEFFICIENT3		(0x2f)	*/
+	{ 0x30, 0x00 },	/* AK4671_DIGITAL_FILTER_SELECT2	(0x30)	*/
+
+	{ 0x32, 0x00 },	/* AK4671_E1_COEFFICIENT0		(0x32)	*/
+	{ 0x33, 0x00 },	/* AK4671_E1_COEFFICIENT1		(0x33)	*/
+	{ 0x34, 0x00 },	/* AK4671_E1_COEFFICIENT2		(0x34)	*/
+	{ 0x35, 0x00 },	/* AK4671_E1_COEFFICIENT3		(0x35)	*/
+	{ 0x36, 0x00 },	/* AK4671_E1_COEFFICIENT4		(0x36)	*/
+	{ 0x37, 0x00 },	/* AK4671_E1_COEFFICIENT5		(0x37)	*/
+	{ 0x38, 0x00 },	/* AK4671_E2_COEFFICIENT0		(0x38)	*/
+	{ 0x39, 0x00 },	/* AK4671_E2_COEFFICIENT1		(0x39)	*/
+	{ 0x3a, 0x00 },	/* AK4671_E2_COEFFICIENT2		(0x3a)	*/
+	{ 0x3b, 0x00 },	/* AK4671_E2_COEFFICIENT3		(0x3b)	*/
+	{ 0x3c, 0x00 },	/* AK4671_E2_COEFFICIENT4		(0x3c)	*/
+	{ 0x3d, 0x00 },	/* AK4671_E2_COEFFICIENT5		(0x3d)	*/
+	{ 0x3e, 0x00 },	/* AK4671_E3_COEFFICIENT0		(0x3e)	*/
+	{ 0x3f, 0x00 },	/* AK4671_E3_COEFFICIENT1		(0x3f)	*/
+	{ 0x40, 0x00 },	/* AK4671_E3_COEFFICIENT2		(0x40)	*/
+	{ 0x41, 0x00 },	/* AK4671_E3_COEFFICIENT3		(0x41)	*/
+	{ 0x42, 0x00 },	/* AK4671_E3_COEFFICIENT4		(0x42)	*/
+	{ 0x43, 0x00 },	/* AK4671_E3_COEFFICIENT5		(0x43)	*/
+	{ 0x44, 0x00 },	/* AK4671_E4_COEFFICIENT0		(0x44)	*/
+	{ 0x45, 0x00 },	/* AK4671_E4_COEFFICIENT1		(0x45)	*/
+	{ 0x46, 0x00 },	/* AK4671_E4_COEFFICIENT2		(0x46)	*/
+	{ 0x47, 0x00 },	/* AK4671_E4_COEFFICIENT3		(0x47)	*/
+	{ 0x48, 0x00 },	/* AK4671_E4_COEFFICIENT4		(0x48)	*/
+	{ 0x49, 0x00 },	/* AK4671_E4_COEFFICIENT5		(0x49)	*/
+	{ 0x4a, 0x00 },	/* AK4671_E5_COEFFICIENT0		(0x4a)	*/
+	{ 0x4b, 0x00 },	/* AK4671_E5_COEFFICIENT1		(0x4b)	*/
+	{ 0x4c, 0x00 },	/* AK4671_E5_COEFFICIENT2		(0x4c)	*/
+	{ 0x4d, 0x00 },	/* AK4671_E5_COEFFICIENT3		(0x4d)	*/
+	{ 0x4e, 0x00 },	/* AK4671_E5_COEFFICIENT4		(0x4e)	*/
+	{ 0x4f, 0x00 },	/* AK4671_E5_COEFFICIENT5		(0x4f)	*/
+	{ 0x50, 0x88 },	/* AK4671_EQ_CONTROL_250HZ_100HZ	(0x50)	*/
+	{ 0x51, 0x88 },	/* AK4671_EQ_CONTROL_3500HZ_1KHZ	(0x51)	*/
+	{ 0x52, 0x08 },	/* AK4671_EQ_CONTRO_10KHZ		(0x52)	*/
+	{ 0x53, 0x00 },	/* AK4671_PCM_IF_CONTROL0		(0x53)	*/
+	{ 0x54, 0x00 },	/* AK4671_PCM_IF_CONTROL1		(0x54)	*/
+	{ 0x55, 0x00 },	/* AK4671_PCM_IF_CONTROL2		(0x55)	*/
+	{ 0x56, 0x18 },	/* AK4671_DIGITAL_VOLUME_B_CONTROL	(0x56)	*/
+	{ 0x57, 0x18 },	/* AK4671_DIGITAL_VOLUME_C_CONTROL	(0x57)	*/
+	{ 0x58, 0x00 },	/* AK4671_SIDETONE_VOLUME_CONTROL	(0x58)	*/
+	{ 0x59, 0x00 },	/* AK4671_DIGITAL_MIXING_CONTROL2	(0x59)	*/
+	{ 0x5a, 0x00 },	/* AK4671_SAR_ADC_CONTROL		(0x5a)	*/
 };
 
 /*
@@ -241,19 +237,17 @@ static const struct snd_kcontrol_new ak4671_rout3_mixer_controls[] = {
 /* Input MUXs */
 static const char *ak4671_lin_mux_texts[] =
 		{"LIN1", "LIN2", "LIN3", "LIN4"};
-static const struct soc_enum ak4671_lin_mux_enum =
-	SOC_ENUM_SINGLE(AK4671_MIC_SIGNAL_SELECT, 0,
-			ARRAY_SIZE(ak4671_lin_mux_texts),
-			ak4671_lin_mux_texts);
+static SOC_ENUM_SINGLE_DECL(ak4671_lin_mux_enum,
+			    AK4671_MIC_SIGNAL_SELECT, 0,
+			    ak4671_lin_mux_texts);
 static const struct snd_kcontrol_new ak4671_lin_mux_control =
 	SOC_DAPM_ENUM("Route", ak4671_lin_mux_enum);
 
 static const char *ak4671_rin_mux_texts[] =
 		{"RIN1", "RIN2", "RIN3", "RIN4"};
-static const struct soc_enum ak4671_rin_mux_enum =
-	SOC_ENUM_SINGLE(AK4671_MIC_SIGNAL_SELECT, 2,
-			ARRAY_SIZE(ak4671_rin_mux_texts),
-			ak4671_rin_mux_texts);
+static SOC_ENUM_SINGLE_DECL(ak4671_rin_mux_enum,
+			    AK4671_MIC_SIGNAL_SELECT, 2,
+			    ak4671_rin_mux_texts);
 static const struct snd_kcontrol_new ak4671_rin_mux_control =
 	SOC_DAPM_ENUM("Route", ak4671_rin_mux_enum);
 
@@ -619,18 +613,14 @@ static struct snd_soc_dai_driver ak4671_dai = {
 
 static int ak4671_probe(struct snd_soc_codec *codec)
 {
-	struct ak4671_priv *ak4671 = snd_soc_codec_get_drvdata(codec);
 	int ret;
 
-	ret = snd_soc_codec_set_cache_io(codec, 8, 8, ak4671->control_type);
+	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;
 	}
 
-	snd_soc_add_codec_controls(codec, ak4671_snd_controls,
-			     ARRAY_SIZE(ak4671_snd_controls));
-
 	ak4671_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
 
 	return ret;
@@ -646,28 +636,36 @@ static struct snd_soc_codec_driver soc_codec_dev_ak4671 = {
 	.probe = ak4671_probe,
 	.remove = ak4671_remove,
 	.set_bias_level = ak4671_set_bias_level,
-	.reg_cache_size = AK4671_CACHEREGNUM,
-	.reg_word_size = sizeof(u8),
-	.reg_cache_default = ak4671_reg,
+	.controls = ak4671_snd_controls,
+	.num_controls = ARRAY_SIZE(ak4671_snd_controls),
 	.dapm_widgets = ak4671_dapm_widgets,
 	.num_dapm_widgets = ARRAY_SIZE(ak4671_dapm_widgets),
 	.dapm_routes = ak4671_intercon,
 	.num_dapm_routes = ARRAY_SIZE(ak4671_intercon),
 };
 
+static const struct regmap_config ak4671_regmap = {
+	.reg_bits = 8,
+	.val_bits = 8,
+
+	.max_register = AK4671_SAR_ADC_CONTROL,
+	.reg_defaults = ak4671_reg_defaults,
+	.num_reg_defaults = ARRAY_SIZE(ak4671_reg_defaults),
+	.cache_type = REGCACHE_RBTREE,
+};
+
 static int ak4671_i2c_probe(struct i2c_client *client,
 			    const struct i2c_device_id *id)
 {
-	struct ak4671_priv *ak4671;
+	struct regmap *regmap;
 	int ret;
 
-	ak4671 = devm_kzalloc(&client->dev, sizeof(struct ak4671_priv),
-			      GFP_KERNEL);
-	if (ak4671 == NULL)
-		return -ENOMEM;
-
-	i2c_set_clientdata(client, ak4671);
-	ak4671->control_type = SND_SOC_I2C;
+	regmap = devm_regmap_init_i2c(client, &ak4671_regmap);
+	if (IS_ERR(regmap)) {
+		ret = PTR_ERR(regmap);
+		dev_err(&client->dev, "Failed to create regmap: %d\n", ret);
+		return ret;
+	}
 
 	ret = snd_soc_register_codec(&client->dev,
 			&soc_codec_dev_ak4671, &ak4671_dai, 1);
diff --git a/sound/soc/codecs/ak4671.h b/sound/soc/codecs/ak4671.h
index 61cb7ab7552c..394a34d3f50a 100644
--- a/sound/soc/codecs/ak4671.h
+++ b/sound/soc/codecs/ak4671.h
@@ -105,8 +105,6 @@
 #define AK4671_DIGITAL_MIXING_CONTROL2		0x59
 #define AK4671_SAR_ADC_CONTROL			0x5a
 
-#define AK4671_CACHEREGNUM			(AK4671_SAR_ADC_CONTROL + 1)
-
 /* Bitfield Definitions */
 
 /* AK4671_AD_DA_POWER_MANAGEMENT (0x00) Fields */
diff --git a/sound/soc/codecs/alc5623.c b/sound/soc/codecs/alc5623.c
index d3036283482a..ed506253a914 100644
--- a/sound/soc/codecs/alc5623.c
+++ b/sound/soc/codecs/alc5623.c
@@ -21,6 +21,7 @@
 #include <linux/delay.h>
 #include <linux/pm.h>
 #include <linux/i2c.h>
+#include <linux/regmap.h>
 #include <linux/slab.h>
 #include <sound/core.h>
 #include <sound/pcm.h>
@@ -38,26 +39,13 @@ MODULE_PARM_DESC(caps_charge, "ALC5623 cap charge time (msecs)");
 
 /* codec private data */
 struct alc5623_priv {
-	enum snd_soc_control_type control_type;
+	struct regmap *regmap;
 	u8 id;
 	unsigned int sysclk;
-	u16 reg_cache[ALC5623_VENDOR_ID2+2];
 	unsigned int add_ctrl;
 	unsigned int jack_det_ctrl;
 };
 
-static void alc5623_fill_cache(struct snd_soc_codec *codec)
-{
-	int i, step = codec->driver->reg_cache_step;
-	u16 *cache = codec->reg_cache;
-
-	/* not really efficient ... */
-	codec->cache_bypass = 1;
-	for (i = 0 ; i < codec->driver->reg_cache_size ; i += step)
-		cache[i] = snd_soc_read(codec, i);
-	codec->cache_bypass = 0;
-}
-
 static inline int alc5623_reset(struct snd_soc_codec *codec)
 {
 	return snd_soc_write(codec, ALC5623_RESET, 0);
@@ -228,32 +216,37 @@ static const char *alc5623_aux_out_input_sel[] = {
 		"Vmid", "HPOut Mix", "Speaker Mix", "Mono Mix"};
 
 /* auxout output mux */
-static const struct soc_enum alc5623_aux_out_input_enum =
-SOC_ENUM_SINGLE(ALC5623_OUTPUT_MIXER_CTRL, 6, 4, alc5623_aux_out_input_sel);
+static SOC_ENUM_SINGLE_DECL(alc5623_aux_out_input_enum,
+			    ALC5623_OUTPUT_MIXER_CTRL, 6,
+			    alc5623_aux_out_input_sel);
 static const struct snd_kcontrol_new alc5623_auxout_mux_controls =
 SOC_DAPM_ENUM("Route", alc5623_aux_out_input_enum);
 
 /* speaker output mux */
-static const struct soc_enum alc5623_spkout_input_enum =
-SOC_ENUM_SINGLE(ALC5623_OUTPUT_MIXER_CTRL, 10, 4, alc5623_spkout_input_sel);
+static SOC_ENUM_SINGLE_DECL(alc5623_spkout_input_enum,
+			    ALC5623_OUTPUT_MIXER_CTRL, 10,
+			    alc5623_spkout_input_sel);
 static const struct snd_kcontrol_new alc5623_spkout_mux_controls =
 SOC_DAPM_ENUM("Route", alc5623_spkout_input_enum);
 
 /* headphone left output mux */
-static const struct soc_enum alc5623_hpl_out_input_enum =
-SOC_ENUM_SINGLE(ALC5623_OUTPUT_MIXER_CTRL, 9, 2, alc5623_hpl_out_input_sel);
+static SOC_ENUM_SINGLE_DECL(alc5623_hpl_out_input_enum,
+			    ALC5623_OUTPUT_MIXER_CTRL, 9,
+			    alc5623_hpl_out_input_sel);
 static const struct snd_kcontrol_new alc5623_hpl_out_mux_controls =
 SOC_DAPM_ENUM("Route", alc5623_hpl_out_input_enum);
 
 /* headphone right output mux */
-static const struct soc_enum alc5623_hpr_out_input_enum =
-SOC_ENUM_SINGLE(ALC5623_OUTPUT_MIXER_CTRL, 8, 2, alc5623_hpr_out_input_sel);
+static SOC_ENUM_SINGLE_DECL(alc5623_hpr_out_input_enum,
+			    ALC5623_OUTPUT_MIXER_CTRL, 8,
+			    alc5623_hpr_out_input_sel);
 static const struct snd_kcontrol_new alc5623_hpr_out_mux_controls =
 SOC_DAPM_ENUM("Route", alc5623_hpr_out_input_enum);
 
 /* speaker output N select */
-static const struct soc_enum alc5623_spk_n_sour_enum =
-SOC_ENUM_SINGLE(ALC5623_OUTPUT_MIXER_CTRL, 14, 4, alc5623_spk_n_sour_sel);
+static SOC_ENUM_SINGLE_DECL(alc5623_spk_n_sour_enum,
+			    ALC5623_OUTPUT_MIXER_CTRL, 14,
+			    alc5623_spk_n_sour_sel);
 static const struct snd_kcontrol_new alc5623_spkoutn_mux_controls =
 SOC_DAPM_ENUM("Route", alc5623_spk_n_sour_enum);
 
@@ -338,8 +331,9 @@ SND_SOC_DAPM_VMID("Vmid"),
 };
 
 static const char *alc5623_amp_names[] = {"AB Amp", "D Amp"};
-static const struct soc_enum alc5623_amp_enum =
-	SOC_ENUM_SINGLE(ALC5623_OUTPUT_MIXER_CTRL, 13, 2, alc5623_amp_names);
+static SOC_ENUM_SINGLE_DECL(alc5623_amp_enum,
+			    ALC5623_OUTPUT_MIXER_CTRL, 13,
+			    alc5623_amp_names);
 static const struct snd_kcontrol_new alc5623_amp_mux_controls =
 	SOC_DAPM_ENUM("Route", alc5623_amp_enum);
 
@@ -869,18 +863,28 @@ static struct snd_soc_dai_driver alc5623_dai = {
 
 static int alc5623_suspend(struct snd_soc_codec *codec)
 {
+	struct alc5623_priv *alc5623 = snd_soc_codec_get_drvdata(codec);
+
 	alc5623_set_bias_level(codec, SND_SOC_BIAS_OFF);
+	regcache_cache_only(alc5623->regmap, true);
+
 	return 0;
 }
 
 static int alc5623_resume(struct snd_soc_codec *codec)
 {
-	int i, step = codec->driver->reg_cache_step;
-	u16 *cache = codec->reg_cache;
+	struct alc5623_priv *alc5623 = snd_soc_codec_get_drvdata(codec);
+	int ret;
 
 	/* Sync reg_cache with the hardware */
-	for (i = 2 ; i < codec->driver->reg_cache_size ; i += step)
-		snd_soc_write(codec, i, cache[i]);
+	regcache_cache_only(alc5623->regmap, false);
+	ret = regcache_sync(alc5623->regmap);
+	if (ret != 0) {
+		dev_err(codec->dev, "Failed to sync register cache: %d\n",
+			ret);
+		regcache_cache_only(alc5623->regmap, true);
+		return ret;
+	}
 
 	alc5623_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
 
@@ -900,14 +904,14 @@ static int alc5623_probe(struct snd_soc_codec *codec)
 	struct snd_soc_dapm_context *dapm = &codec->dapm;
 	int ret;
 
-	ret = snd_soc_codec_set_cache_io(codec, 8, 16, alc5623->control_type);
+	codec->control_data = alc5623->regmap;
+	ret = snd_soc_codec_set_cache_io(codec, 8, 16, SND_SOC_REGMAP);
 	if (ret < 0) {
 		dev_err(codec->dev, "Failed to set cache I/O: %d\n", ret);
 		return ret;
 	}
 
 	alc5623_reset(codec);
-	alc5623_fill_cache(codec);
 
 	/* power on device */
 	alc5623_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
@@ -980,9 +984,15 @@ static struct snd_soc_codec_driver soc_codec_device_alc5623 = {
 	.suspend = alc5623_suspend,
 	.resume = alc5623_resume,
 	.set_bias_level = alc5623_set_bias_level,
-	.reg_cache_size = ALC5623_VENDOR_ID2+2,
-	.reg_word_size = sizeof(u16),
-	.reg_cache_step = 2,
+};
+
+static const struct regmap_config alc5623_regmap = {
+	.reg_bits = 8,
+	.val_bits = 16,
+	.reg_stride = 2,
+
+	.max_register = ALC5623_VENDOR_ID2,
+	.cache_type = REGCACHE_RBTREE,
 };
 
 /*
@@ -996,19 +1006,32 @@ static int alc5623_i2c_probe(struct i2c_client *client,
 {
 	struct alc5623_platform_data *pdata;
 	struct alc5623_priv *alc5623;
-	int ret, vid1, vid2;
+	unsigned int vid1, vid2;
+	int ret;
 
-	vid1 = i2c_smbus_read_word_data(client, ALC5623_VENDOR_ID1);
-	if (vid1 < 0) {
-		dev_err(&client->dev, "failed to read I2C\n");
-		return -EIO;
+	alc5623 = devm_kzalloc(&client->dev, sizeof(struct alc5623_priv),
+			       GFP_KERNEL);
+	if (alc5623 == NULL)
+		return -ENOMEM;
+
+	alc5623->regmap = devm_regmap_init_i2c(client, &alc5623_regmap);
+	if (IS_ERR(alc5623->regmap)) {
+		ret = PTR_ERR(alc5623->regmap);
+		dev_err(&client->dev, "Failed to initialise I/O: %d\n", ret);
+		return ret;
+	}
+
+	ret = regmap_read(alc5623->regmap, ALC5623_VENDOR_ID1, &vid1);
+	if (ret < 0) {
+		dev_err(&client->dev, "failed to read vendor ID1: %d\n", ret);
+		return ret;
 	}
 	vid1 = ((vid1 & 0xff) << 8) | (vid1 >> 8);
 
-	vid2 = i2c_smbus_read_byte_data(client, ALC5623_VENDOR_ID2);
-	if (vid2 < 0) {
-		dev_err(&client->dev, "failed to read I2C\n");
-		return -EIO;
+	ret = regmap_read(alc5623->regmap, ALC5623_VENDOR_ID2, &vid2);
+	if (ret < 0) {
+		dev_err(&client->dev, "failed to read vendor ID2: %d\n", ret);
+		return ret;
 	}
 
 	if ((vid1 != 0x10ec) || (vid2 != id->driver_data)) {
@@ -1021,11 +1044,6 @@ static int alc5623_i2c_probe(struct i2c_client *client,
 
 	dev_dbg(&client->dev, "Found codec id : alc56%02x\n", vid2);
 
-	alc5623 = devm_kzalloc(&client->dev, sizeof(struct alc5623_priv),
-			       GFP_KERNEL);
-	if (alc5623 == NULL)
-		return -ENOMEM;
-
 	pdata = client->dev.platform_data;
 	if (pdata) {
 		alc5623->add_ctrl = pdata->add_ctrl;
@@ -1048,7 +1066,6 @@ static int alc5623_i2c_probe(struct i2c_client *client,
 	}
 
 	i2c_set_clientdata(client, alc5623);
-	alc5623->control_type = SND_SOC_I2C;
 
 	ret =  snd_soc_register_codec(&client->dev,
 		&soc_codec_device_alc5623, &alc5623_dai, 1);
diff --git a/sound/soc/codecs/alc5632.c b/sound/soc/codecs/alc5632.c
index fb001c56cf8d..d885056ad8f2 100644
--- a/sound/soc/codecs/alc5632.c
+++ b/sound/soc/codecs/alc5632.c
@@ -293,51 +293,59 @@ static const char * const alc5632_i2s_out_sel[] = {
 		"ADC LR", "Voice Stereo Digital"};
 
 /* auxout output mux */
-static const struct soc_enum alc5632_aux_out_input_enum =
-SOC_ENUM_SINGLE(ALC5632_OUTPUT_MIXER_CTRL, 6, 4, alc5632_aux_out_input_sel);
+static SOC_ENUM_SINGLE_DECL(alc5632_aux_out_input_enum,
+			    ALC5632_OUTPUT_MIXER_CTRL, 6,
+			    alc5632_aux_out_input_sel);
 static const struct snd_kcontrol_new alc5632_auxout_mux_controls =
 SOC_DAPM_ENUM("AuxOut Mux", alc5632_aux_out_input_enum);
 
 /* speaker output mux */
-static const struct soc_enum alc5632_spkout_input_enum =
-SOC_ENUM_SINGLE(ALC5632_OUTPUT_MIXER_CTRL, 10, 4, alc5632_spkout_input_sel);
+static SOC_ENUM_SINGLE_DECL(alc5632_spkout_input_enum,
+			    ALC5632_OUTPUT_MIXER_CTRL, 10,
+			    alc5632_spkout_input_sel);
 static const struct snd_kcontrol_new alc5632_spkout_mux_controls =
 SOC_DAPM_ENUM("SpeakerOut Mux", alc5632_spkout_input_enum);
 
 /* headphone left output mux */
-static const struct soc_enum alc5632_hpl_out_input_enum =
-SOC_ENUM_SINGLE(ALC5632_OUTPUT_MIXER_CTRL, 9, 2, alc5632_hpl_out_input_sel);
+static SOC_ENUM_SINGLE_DECL(alc5632_hpl_out_input_enum,
+			    ALC5632_OUTPUT_MIXER_CTRL, 9,
+			    alc5632_hpl_out_input_sel);
 static const struct snd_kcontrol_new alc5632_hpl_out_mux_controls =
 SOC_DAPM_ENUM("Left Headphone Mux", alc5632_hpl_out_input_enum);
 
 /* headphone right output mux */
-static const struct soc_enum alc5632_hpr_out_input_enum =
-SOC_ENUM_SINGLE(ALC5632_OUTPUT_MIXER_CTRL, 8, 2, alc5632_hpr_out_input_sel);
+static SOC_ENUM_SINGLE_DECL(alc5632_hpr_out_input_enum,
+			    ALC5632_OUTPUT_MIXER_CTRL, 8,
+			    alc5632_hpr_out_input_sel);
 static const struct snd_kcontrol_new alc5632_hpr_out_mux_controls =
 SOC_DAPM_ENUM("Right Headphone Mux", alc5632_hpr_out_input_enum);
 
 /* speaker output N select */
-static const struct soc_enum alc5632_spk_n_sour_enum =
-SOC_ENUM_SINGLE(ALC5632_OUTPUT_MIXER_CTRL, 14, 4, alc5632_spk_n_sour_sel);
+static SOC_ENUM_SINGLE_DECL(alc5632_spk_n_sour_enum,
+			    ALC5632_OUTPUT_MIXER_CTRL, 14,
+			    alc5632_spk_n_sour_sel);
 static const struct snd_kcontrol_new alc5632_spkoutn_mux_controls =
 SOC_DAPM_ENUM("SpeakerOut N Mux", alc5632_spk_n_sour_enum);
 
 /* speaker amplifier */
 static const char *alc5632_amp_names[] = {"AB Amp", "D Amp"};
-static const struct soc_enum alc5632_amp_enum =
-	SOC_ENUM_SINGLE(ALC5632_OUTPUT_MIXER_CTRL, 13, 2, alc5632_amp_names);
+static SOC_ENUM_SINGLE_DECL(alc5632_amp_enum,
+			    ALC5632_OUTPUT_MIXER_CTRL, 13,
+			    alc5632_amp_names);
 static const struct snd_kcontrol_new alc5632_amp_mux_controls =
 	SOC_DAPM_ENUM("AB-D Amp Mux", alc5632_amp_enum);
 
 /* ADC output select */
-static const struct soc_enum alc5632_adcr_func_enum =
-	SOC_ENUM_SINGLE(ALC5632_DAC_FUNC_SELECT, 5, 2, alc5632_adcr_func_sel);
+static SOC_ENUM_SINGLE_DECL(alc5632_adcr_func_enum,
+			    ALC5632_DAC_FUNC_SELECT, 5,
+			    alc5632_adcr_func_sel);
 static const struct snd_kcontrol_new alc5632_adcr_func_controls =
 	SOC_DAPM_ENUM("ADCR Mux", alc5632_adcr_func_enum);
 
 /* I2S out select */
-static const struct soc_enum alc5632_i2s_out_enum =
-	SOC_ENUM_SINGLE(ALC5632_I2S_OUT_CTL, 5, 2, alc5632_i2s_out_sel);
+static SOC_ENUM_SINGLE_DECL(alc5632_i2s_out_enum,
+			    ALC5632_I2S_OUT_CTL, 5,
+			    alc5632_i2s_out_sel);
 static const struct snd_kcontrol_new alc5632_i2s_out_controls =
 	SOC_DAPM_ENUM("I2SOut Mux", alc5632_i2s_out_enum);
 
diff --git a/sound/soc/codecs/arizona.c b/sound/soc/codecs/arizona.c
index e4295fee8f13..29e198f57d4c 100644
--- a/sound/soc/codecs/arizona.c
+++ b/sound/soc/codecs/arizona.c
@@ -53,6 +53,14 @@
 #define ARIZONA_AIF_RX_ENABLES                  0x1A
 #define ARIZONA_AIF_FORCE_WRITE                 0x1B
 
+#define ARIZONA_FLL_VCO_CORNER 141900000
+#define ARIZONA_FLL_MAX_FREF   13500000
+#define ARIZONA_FLL_MIN_FVCO   90000000
+#define ARIZONA_FLL_MAX_FRATIO 16
+#define ARIZONA_FLL_MAX_REFDIV 8
+#define ARIZONA_FLL_MIN_OUTDIV 2
+#define ARIZONA_FLL_MAX_OUTDIV 7
+
 #define arizona_fll_err(_fll, fmt, ...) \
 	dev_err(_fll->arizona->dev, "FLL%d: " fmt, _fll->id, ##__VA_ARGS__)
 #define arizona_fll_warn(_fll, fmt, ...) \
@@ -542,67 +550,76 @@ static const char *arizona_vol_ramp_text[] = {
 	"15ms/6dB", "30ms/6dB",
 };
 
-const struct soc_enum arizona_in_vd_ramp =
-	SOC_ENUM_SINGLE(ARIZONA_INPUT_VOLUME_RAMP,
-			ARIZONA_IN_VD_RAMP_SHIFT, 7, arizona_vol_ramp_text);
+SOC_ENUM_SINGLE_DECL(arizona_in_vd_ramp,
+		     ARIZONA_INPUT_VOLUME_RAMP,
+		     ARIZONA_IN_VD_RAMP_SHIFT,
+		     arizona_vol_ramp_text);
 EXPORT_SYMBOL_GPL(arizona_in_vd_ramp);
 
-const struct soc_enum arizona_in_vi_ramp =
-	SOC_ENUM_SINGLE(ARIZONA_INPUT_VOLUME_RAMP,
-			ARIZONA_IN_VI_RAMP_SHIFT, 7, arizona_vol_ramp_text);
+SOC_ENUM_SINGLE_DECL(arizona_in_vi_ramp,
+		     ARIZONA_INPUT_VOLUME_RAMP,
+		     ARIZONA_IN_VI_RAMP_SHIFT,
+		     arizona_vol_ramp_text);
 EXPORT_SYMBOL_GPL(arizona_in_vi_ramp);
 
-const struct soc_enum arizona_out_vd_ramp =
-	SOC_ENUM_SINGLE(ARIZONA_OUTPUT_VOLUME_RAMP,
-			ARIZONA_OUT_VD_RAMP_SHIFT, 7, arizona_vol_ramp_text);
+SOC_ENUM_SINGLE_DECL(arizona_out_vd_ramp,
+		     ARIZONA_OUTPUT_VOLUME_RAMP,
+		     ARIZONA_OUT_VD_RAMP_SHIFT,
+		     arizona_vol_ramp_text);
 EXPORT_SYMBOL_GPL(arizona_out_vd_ramp);
 
-const struct soc_enum arizona_out_vi_ramp =
-	SOC_ENUM_SINGLE(ARIZONA_OUTPUT_VOLUME_RAMP,
-			ARIZONA_OUT_VI_RAMP_SHIFT, 7, arizona_vol_ramp_text);
+SOC_ENUM_SINGLE_DECL(arizona_out_vi_ramp,
+		     ARIZONA_OUTPUT_VOLUME_RAMP,
+		     ARIZONA_OUT_VI_RAMP_SHIFT,
+		     arizona_vol_ramp_text);
 EXPORT_SYMBOL_GPL(arizona_out_vi_ramp);
 
 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);
+SOC_ENUM_SINGLE_DECL(arizona_lhpf1_mode,
+		     ARIZONA_HPLPF1_1,
+		     ARIZONA_LHPF1_MODE_SHIFT,
+		     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);
+SOC_ENUM_SINGLE_DECL(arizona_lhpf2_mode,
+		     ARIZONA_HPLPF2_1,
+		     ARIZONA_LHPF2_MODE_SHIFT,
+		     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);
+SOC_ENUM_SINGLE_DECL(arizona_lhpf3_mode,
+		     ARIZONA_HPLPF3_1,
+		     ARIZONA_LHPF3_MODE_SHIFT,
+		     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);
+SOC_ENUM_SINGLE_DECL(arizona_lhpf4_mode,
+		     ARIZONA_HPLPF4_1,
+		     ARIZONA_LHPF4_MODE_SHIFT,
+		     arizona_lhpf_mode_text);
 EXPORT_SYMBOL_GPL(arizona_lhpf4_mode);
 
 static const char *arizona_ng_hold_text[] = {
 	"30ms", "120ms", "250ms", "500ms",
 };
 
-const struct soc_enum arizona_ng_hold =
-	SOC_ENUM_SINGLE(ARIZONA_NOISE_GATE_CONTROL, ARIZONA_NGATE_HOLD_SHIFT,
-			4, arizona_ng_hold_text);
+SOC_ENUM_SINGLE_DECL(arizona_ng_hold,
+		     ARIZONA_NOISE_GATE_CONTROL,
+		     ARIZONA_NGATE_HOLD_SHIFT,
+		     arizona_ng_hold_text);
 EXPORT_SYMBOL_GPL(arizona_ng_hold);
 
 static const char * const arizona_in_hpf_cut_text[] = {
 	"2.5Hz", "5Hz", "10Hz", "20Hz", "40Hz"
 };
 
-const struct soc_enum arizona_in_hpf_cut_enum =
-	SOC_ENUM_SINGLE(ARIZONA_HPF_CONTROL, ARIZONA_IN_HPF_CUT_SHIFT,
-			ARRAY_SIZE(arizona_in_hpf_cut_text),
-			arizona_in_hpf_cut_text);
+SOC_ENUM_SINGLE_DECL(arizona_in_hpf_cut_enum,
+		     ARIZONA_HPF_CONTROL,
+		     ARIZONA_IN_HPF_CUT_SHIFT,
+		     arizona_in_hpf_cut_text);
 EXPORT_SYMBOL_GPL(arizona_in_hpf_cut_enum);
 
 static const char * const arizona_in_dmic_osr_text[] = {
@@ -1377,74 +1394,147 @@ struct arizona_fll_cfg {
 	int gain;
 };
 
-static int arizona_calc_fll(struct arizona_fll *fll,
-			    struct arizona_fll_cfg *cfg,
-			    unsigned int Fref,
-			    unsigned int Fout)
+static int arizona_validate_fll(struct arizona_fll *fll,
+				unsigned int Fref,
+				unsigned int Fout)
 {
-	unsigned int target, div, gcd_fll;
-	int i, ratio;
+	unsigned int Fvco_min;
+
+	if (Fref / ARIZONA_FLL_MAX_REFDIV > ARIZONA_FLL_MAX_FREF) {
+		arizona_fll_err(fll,
+				"Can't scale %dMHz in to <=13.5MHz\n",
+				Fref);
+		return -EINVAL;
+	}
 
-	arizona_fll_dbg(fll, "Fref=%u Fout=%u\n", Fref, Fout);
+	Fvco_min = ARIZONA_FLL_MIN_FVCO * fll->vco_mult;
+	if (Fout * ARIZONA_FLL_MAX_OUTDIV < Fvco_min) {
+		arizona_fll_err(fll, "No FLL_OUTDIV for Fout=%uHz\n",
+				Fout);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int arizona_find_fratio(unsigned int Fref, int *fratio)
+{
+	int i;
+
+	/* Find an appropriate FLL_FRATIO */
+	for (i = 0; i < ARRAY_SIZE(fll_fratios); i++) {
+		if (fll_fratios[i].min <= Fref && Fref <= fll_fratios[i].max) {
+			if (fratio)
+				*fratio = fll_fratios[i].fratio;
+			return fll_fratios[i].ratio;
+		}
+	}
+
+	return -EINVAL;
+}
+
+static int arizona_calc_fratio(struct arizona_fll *fll,
+			       struct arizona_fll_cfg *cfg,
+			       unsigned int target,
+			       unsigned int Fref, bool sync)
+{
+	int init_ratio, ratio;
+	int refdiv, div;
 
-	/* Fref must be <=13.5MHz */
+	/* Fref must be <=13.5MHz, find initial refdiv */
 	div = 1;
 	cfg->refdiv = 0;
-	while ((Fref / div) > 13500000) {
+	while (Fref > ARIZONA_FLL_MAX_FREF) {
 		div *= 2;
+		Fref /= 2;
 		cfg->refdiv++;
 
-		if (div > 8) {
-			arizona_fll_err(fll,
-					"Can't scale %dMHz in to <=13.5MHz\n",
-					Fref);
+		if (div > ARIZONA_FLL_MAX_REFDIV)
 			return -EINVAL;
+	}
+
+	/* Find an appropriate FLL_FRATIO */
+	init_ratio = arizona_find_fratio(Fref, &cfg->fratio);
+	if (init_ratio < 0) {
+		arizona_fll_err(fll, "Unable to find FRATIO for Fref=%uHz\n",
+				Fref);
+		return init_ratio;
+	}
+
+	switch (fll->arizona->type) {
+	case WM5110:
+		if (fll->arizona->rev < 3 || sync)
+			return init_ratio;
+		break;
+	default:
+		return init_ratio;
+	}
+
+	cfg->fratio = init_ratio - 1;
+
+	/* Adjust FRATIO/refdiv to avoid integer mode if possible */
+	refdiv = cfg->refdiv;
+
+	while (div <= ARIZONA_FLL_MAX_REFDIV) {
+		for (ratio = init_ratio; ratio <= ARIZONA_FLL_MAX_FRATIO;
+		     ratio++) {
+			if (target % (ratio * Fref)) {
+				cfg->refdiv = refdiv;
+				cfg->fratio = ratio - 1;
+				return ratio;
+			}
 		}
+
+		for (ratio = init_ratio - 1; ratio >= 0; ratio--) {
+			if (ARIZONA_FLL_VCO_CORNER / (fll->vco_mult * ratio) <
+			    Fref)
+				break;
+
+			if (target % (ratio * Fref)) {
+				cfg->refdiv = refdiv;
+				cfg->fratio = ratio - 1;
+				return ratio;
+			}
+		}
+
+		div *= 2;
+		Fref /= 2;
+		refdiv++;
+		init_ratio = arizona_find_fratio(Fref, NULL);
 	}
 
-	/* Apply the division for our remaining calculations */
-	Fref /= div;
+	arizona_fll_warn(fll, "Falling back to integer mode operation\n");
+	return cfg->fratio + 1;
+}
+
+static int arizona_calc_fll(struct arizona_fll *fll,
+			    struct arizona_fll_cfg *cfg,
+			    unsigned int Fref, bool sync)
+{
+	unsigned int target, div, gcd_fll;
+	int i, ratio;
+
+	arizona_fll_dbg(fll, "Fref=%u Fout=%u\n", Fref, fll->fout);
 
 	/* Fvco should be over the targt; don't check the upper bound */
-	div = 1;
-	while (Fout * div < 90000000 * fll->vco_mult) {
+	div = ARIZONA_FLL_MIN_OUTDIV;
+	while (fll->fout * div < ARIZONA_FLL_MIN_FVCO * fll->vco_mult) {
 		div++;
-		if (div > 7) {
-			arizona_fll_err(fll, "No FLL_OUTDIV for Fout=%uHz\n",
-					Fout);
+		if (div > ARIZONA_FLL_MAX_OUTDIV)
 			return -EINVAL;
-		}
 	}
-	target = Fout * div / fll->vco_mult;
+	target = fll->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;
-	}
+	/* Find an appropriate FLL_FRATIO and refdiv */
+	ratio = arizona_calc_fratio(fll, cfg, target, Fref, sync);
+	if (ratio < 0)
+		return ratio;
 
-	for (i = 0; i < ARRAY_SIZE(fll_gains); i++) {
-		if (fll_gains[i].min <= Fref && Fref <= fll_gains[i].max) {
-			cfg->gain = fll_gains[i].gain;
-			break;
-		}
-	}
-	if (i == ARRAY_SIZE(fll_gains)) {
-		arizona_fll_err(fll, "Unable to find gain for Fref=%uHz\n",
-				Fref);
-		return -EINVAL;
-	}
+	/* Apply the division for our remaining calculations */
+	Fref = Fref / (1 << cfg->refdiv);
 
 	cfg->n = target / (ratio * Fref);
 
@@ -1469,6 +1559,18 @@ static int arizona_calc_fll(struct arizona_fll *fll,
 		cfg->lambda >>= 1;
 	}
 
+	for (i = 0; i < ARRAY_SIZE(fll_gains); i++) {
+		if (fll_gains[i].min <= Fref && Fref <= fll_gains[i].max) {
+			cfg->gain = fll_gains[i].gain;
+			break;
+		}
+	}
+	if (i == ARRAY_SIZE(fll_gains)) {
+		arizona_fll_err(fll, "Unable to find gain for Fref=%uHz\n",
+				Fref);
+		return -EINVAL;
+	}
+
 	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",
@@ -1496,14 +1598,18 @@ static void arizona_apply_fll(struct arizona *arizona, unsigned int base,
 				 cfg->refdiv << ARIZONA_FLL1_CLK_REF_DIV_SHIFT |
 				 source << ARIZONA_FLL1_CLK_REF_SRC_SHIFT);
 
-	if (sync)
-		regmap_update_bits_async(arizona->regmap, base + 0x7,
-					 ARIZONA_FLL1_GAIN_MASK,
-					 cfg->gain << ARIZONA_FLL1_GAIN_SHIFT);
-	else
-		regmap_update_bits_async(arizona->regmap, base + 0x9,
-					 ARIZONA_FLL1_GAIN_MASK,
-					 cfg->gain << ARIZONA_FLL1_GAIN_SHIFT);
+	if (sync) {
+		regmap_update_bits(arizona->regmap, base + 0x7,
+				   ARIZONA_FLL1_GAIN_MASK,
+				   cfg->gain << ARIZONA_FLL1_GAIN_SHIFT);
+	} else {
+		regmap_update_bits(arizona->regmap, base + 0x5,
+				   ARIZONA_FLL1_OUTDIV_MASK,
+				   cfg->outdiv << ARIZONA_FLL1_OUTDIV_SHIFT);
+		regmap_update_bits(arizona->regmap, base + 0x9,
+				   ARIZONA_FLL1_GAIN_MASK,
+				   cfg->gain << ARIZONA_FLL1_GAIN_SHIFT);
+	}
 
 	regmap_update_bits_async(arizona->regmap, base + 2,
 				 ARIZONA_FLL1_CTRL_UPD | ARIZONA_FLL1_N_MASK,
@@ -1526,13 +1632,12 @@ static bool arizona_is_enabled_fll(struct arizona_fll *fll)
 	return reg & ARIZONA_FLL1_ENA;
 }
 
-static void arizona_enable_fll(struct arizona_fll *fll,
-			      struct arizona_fll_cfg *ref,
-			      struct arizona_fll_cfg *sync)
+static void arizona_enable_fll(struct arizona_fll *fll)
 {
 	struct arizona *arizona = fll->arizona;
 	int ret;
 	bool use_sync = false;
+	struct arizona_fll_cfg cfg;
 
 	/*
 	 * If we have both REFCLK and SYNCCLK then enable both,
@@ -1540,23 +1645,21 @@ static void arizona_enable_fll(struct arizona_fll *fll,
 	 */
 	if (fll->ref_src >= 0 && fll->ref_freq &&
 	    fll->ref_src != fll->sync_src) {
-		regmap_update_bits_async(arizona->regmap, fll->base + 5,
-					 ARIZONA_FLL1_OUTDIV_MASK,
-					 ref->outdiv << ARIZONA_FLL1_OUTDIV_SHIFT);
+		arizona_calc_fll(fll, &cfg, fll->ref_freq, false);
 
-		arizona_apply_fll(arizona, fll->base, ref, fll->ref_src,
+		arizona_apply_fll(arizona, fll->base, &cfg, fll->ref_src,
 				  false);
 		if (fll->sync_src >= 0) {
-			arizona_apply_fll(arizona, fll->base + 0x10, sync,
+			arizona_calc_fll(fll, &cfg, fll->sync_freq, true);
+
+			arizona_apply_fll(arizona, fll->base + 0x10, &cfg,
 					  fll->sync_src, true);
 			use_sync = true;
 		}
 	} else if (fll->sync_src >= 0) {
-		regmap_update_bits_async(arizona->regmap, fll->base + 5,
-					 ARIZONA_FLL1_OUTDIV_MASK,
-					 sync->outdiv << ARIZONA_FLL1_OUTDIV_SHIFT);
+		arizona_calc_fll(fll, &cfg, fll->sync_freq, false);
 
-		arizona_apply_fll(arizona, fll->base, sync,
+		arizona_apply_fll(arizona, fll->base, &cfg,
 				  fll->sync_src, false);
 
 		regmap_update_bits_async(arizona->regmap, fll->base + 0x11,
@@ -1618,32 +1721,22 @@ static void arizona_disable_fll(struct arizona_fll *fll)
 int arizona_set_fll_refclk(struct arizona_fll *fll, int source,
 			   unsigned int Fref, unsigned int Fout)
 {
-	struct arizona_fll_cfg ref, sync;
 	int ret;
 
 	if (fll->ref_src == source && fll->ref_freq == Fref)
 		return 0;
 
-	if (fll->fout) {
-		if (Fref > 0) {
-			ret = arizona_calc_fll(fll, &ref, Fref, fll->fout);
-			if (ret != 0)
-				return ret;
-		}
-
-		if (fll->sync_src >= 0) {
-			ret = arizona_calc_fll(fll, &sync, fll->sync_freq,
-					       fll->fout);
-			if (ret != 0)
-				return ret;
-		}
+	if (fll->fout && Fref > 0) {
+		ret = arizona_validate_fll(fll, Fref, fll->fout);
+		if (ret != 0)
+			return ret;
 	}
 
 	fll->ref_src = source;
 	fll->ref_freq = Fref;
 
 	if (fll->fout && Fref > 0) {
-		arizona_enable_fll(fll, &ref, &sync);
+		arizona_enable_fll(fll);
 	}
 
 	return 0;
@@ -1653,7 +1746,6 @@ EXPORT_SYMBOL_GPL(arizona_set_fll_refclk);
 int arizona_set_fll(struct arizona_fll *fll, int source,
 		    unsigned int Fref, unsigned int Fout)
 {
-	struct arizona_fll_cfg ref, sync;
 	int ret;
 
 	if (fll->sync_src == source &&
@@ -1662,13 +1754,12 @@ int arizona_set_fll(struct arizona_fll *fll, int source,
 
 	if (Fout) {
 		if (fll->ref_src >= 0) {
-			ret = arizona_calc_fll(fll, &ref, fll->ref_freq,
-					       Fout);
+			ret = arizona_validate_fll(fll, fll->ref_freq, Fout);
 			if (ret != 0)
 				return ret;
 		}
 
-		ret = arizona_calc_fll(fll, &sync, Fref, Fout);
+		ret = arizona_validate_fll(fll, Fref, Fout);
 		if (ret != 0)
 			return ret;
 	}
@@ -1678,7 +1769,7 @@ int arizona_set_fll(struct arizona_fll *fll, int source,
 	fll->fout = Fout;
 
 	if (Fout) {
-		arizona_enable_fll(fll, &ref, &sync);
+		arizona_enable_fll(fll);
 	} else {
 		arizona_disable_fll(fll);
 	}
diff --git a/sound/soc/codecs/cs4271.c b/sound/soc/codecs/cs4271.c
index ce05fd93dc74..aef4965750c7 100644
--- a/sound/soc/codecs/cs4271.c
+++ b/sound/soc/codecs/cs4271.c
@@ -159,7 +159,6 @@ static bool cs4271_volatile_reg(struct device *dev, unsigned int reg)
 }
 
 struct cs4271_private {
-	/* SND_SOC_I2C or SND_SOC_SPI */
 	unsigned int			mclk;
 	bool				master;
 	bool				deemph;
@@ -540,14 +539,10 @@ static int cs4271_probe(struct snd_soc_codec *codec)
 	struct cs4271_private *cs4271 = snd_soc_codec_get_drvdata(codec);
 	struct cs4271_platform_data *cs4271plat = codec->dev->platform_data;
 	int ret;
-	int gpio_nreset = -EINVAL;
 	bool amutec_eq_bmutec = false;
 
 #ifdef CONFIG_OF
 	if (of_match_device(cs4271_dt_ids, codec->dev)) {
-		gpio_nreset = of_get_named_gpio(codec->dev->of_node,
-						"reset-gpio", 0);
-
 		if (of_get_property(codec->dev->of_node,
 				     "cirrus,amutec-eq-bmutec", NULL))
 			amutec_eq_bmutec = true;
@@ -559,27 +554,19 @@ static int cs4271_probe(struct snd_soc_codec *codec)
 #endif
 
 	if (cs4271plat) {
-		if (gpio_is_valid(cs4271plat->gpio_nreset))
-			gpio_nreset = cs4271plat->gpio_nreset;
-
 		amutec_eq_bmutec = cs4271plat->amutec_eq_bmutec;
 		cs4271->enable_soft_reset = cs4271plat->enable_soft_reset;
 	}
 
-	if (gpio_nreset >= 0)
-		if (devm_gpio_request(codec->dev, gpio_nreset, "CS4271 Reset"))
-			gpio_nreset = -EINVAL;
-	if (gpio_nreset >= 0) {
+	if (gpio_is_valid(cs4271->gpio_nreset)) {
 		/* Reset codec */
-		gpio_direction_output(gpio_nreset, 0);
+		gpio_direction_output(cs4271->gpio_nreset, 0);
 		udelay(1);
-		gpio_set_value(gpio_nreset, 1);
+		gpio_set_value(cs4271->gpio_nreset, 1);
 		/* Give the codec time to wake up */
 		udelay(1);
 	}
 
-	cs4271->gpio_nreset = gpio_nreset;
-
 	ret = regmap_update_bits(cs4271->regmap, CS4271_MODE2,
 				 CS4271_MODE2_PDN | CS4271_MODE2_CPEN,
 				 CS4271_MODE2_PDN | CS4271_MODE2_CPEN);
@@ -625,6 +612,36 @@ static struct snd_soc_codec_driver soc_codec_dev_cs4271 = {
 	.num_dapm_routes	= ARRAY_SIZE(cs4271_dapm_routes),
 };
 
+static int cs4271_common_probe(struct device *dev,
+			       struct cs4271_private **c)
+{
+	struct cs4271_platform_data *cs4271plat = dev->platform_data;
+	struct cs4271_private *cs4271;
+
+	cs4271 = devm_kzalloc(dev, sizeof(*cs4271), GFP_KERNEL);
+	if (!cs4271)
+		return -ENOMEM;
+
+	if (of_match_device(cs4271_dt_ids, dev))
+		cs4271->gpio_nreset =
+			of_get_named_gpio(dev->of_node, "reset-gpio", 0);
+
+	if (cs4271plat)
+		cs4271->gpio_nreset = cs4271plat->gpio_nreset;
+
+	if (gpio_is_valid(cs4271->gpio_nreset)) {
+		int ret;
+
+		ret = devm_gpio_request(dev, cs4271->gpio_nreset,
+					"CS4271 Reset");
+		if (ret < 0)
+			return ret;
+	}
+
+	*c = cs4271;
+	return 0;
+}
+
 #if defined(CONFIG_SPI_MASTER)
 
 static const struct regmap_config cs4271_spi_regmap = {
@@ -644,10 +661,11 @@ static const struct regmap_config cs4271_spi_regmap = {
 static int cs4271_spi_probe(struct spi_device *spi)
 {
 	struct cs4271_private *cs4271;
+	int ret;
 
-	cs4271 = devm_kzalloc(&spi->dev, sizeof(*cs4271), GFP_KERNEL);
-	if (!cs4271)
-		return -ENOMEM;
+	ret = cs4271_common_probe(&spi->dev, &cs4271);
+	if (ret < 0)
+		return ret;
 
 	spi_set_drvdata(spi, cs4271);
 	cs4271->regmap = devm_regmap_init_spi(spi, &cs4271_spi_regmap);
@@ -698,10 +716,11 @@ static int cs4271_i2c_probe(struct i2c_client *client,
 			    const struct i2c_device_id *id)
 {
 	struct cs4271_private *cs4271;
+	int ret;
 
-	cs4271 = devm_kzalloc(&client->dev, sizeof(*cs4271), GFP_KERNEL);
-	if (!cs4271)
-		return -ENOMEM;
+	ret = cs4271_common_probe(&client->dev, &cs4271);
+	if (ret < 0)
+		return ret;
 
 	i2c_set_clientdata(client, cs4271);
 	cs4271->regmap = devm_regmap_init_i2c(client, &cs4271_i2c_regmap);
diff --git a/sound/soc/codecs/cs42l51.c b/sound/soc/codecs/cs42l51.c
index 7a272fa90b39..828157779057 100644
--- a/sound/soc/codecs/cs42l51.c
+++ b/sound/soc/codecs/cs42l51.c
@@ -30,6 +30,7 @@
 #include <sound/pcm_params.h>
 #include <sound/pcm.h>
 #include <linux/i2c.h>
+#include <linux/regmap.h>
 
 #include "cs42l51.h"
 
@@ -40,7 +41,6 @@ enum master_slave_mode {
 };
 
 struct cs42l51_private {
-	enum snd_soc_control_type control_type;
 	unsigned int mclk;
 	unsigned int audio_mode;	/* The mode (I2S or left-justified) */
 	enum master_slave_mode func;
@@ -52,24 +52,6 @@ struct cs42l51_private {
 		SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S20_3BE | \
 		SNDRV_PCM_FMTBIT_S24_LE  | SNDRV_PCM_FMTBIT_S24_BE)
 
-static int cs42l51_fill_cache(struct snd_soc_codec *codec)
-{
-	u8 *cache = codec->reg_cache + 1;
-	struct i2c_client *i2c_client = to_i2c_client(codec->dev);
-	s32 length;
-
-	length = i2c_smbus_read_i2c_block_data(i2c_client,
-			CS42L51_FIRSTREG | 0x80, CS42L51_NUMREGS, cache);
-	if (length != CS42L51_NUMREGS) {
-		dev_err(&i2c_client->dev,
-				"I2C read failure, addr=0x%x (ret=%d vs %d)\n",
-				i2c_client->addr, length, CS42L51_NUMREGS);
-		return -EIO;
-	}
-
-	return 0;
-}
-
 static int cs42l51_get_chan_mix(struct snd_kcontrol *kcontrol,
 			struct snd_ctl_elem_value *ucontrol)
 {
@@ -134,8 +116,7 @@ static const char *chan_mix[] = {
 	"R L",
 };
 
-static const struct soc_enum cs42l51_chan_mix =
-	SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(chan_mix), chan_mix);
+static SOC_ENUM_SINGLE_EXT_DECL(cs42l51_chan_mix, chan_mix);
 
 static const struct snd_kcontrol_new cs42l51_snd_controls[] = {
 	SOC_DOUBLE_R_SX_TLV("PCM Playback Volume",
@@ -191,22 +172,22 @@ static int cs42l51_pdn_event(struct snd_soc_dapm_widget *w,
 
 static const char *cs42l51_dac_names[] = {"Direct PCM",
 	"DSP PCM", "ADC"};
-static const struct soc_enum cs42l51_dac_mux_enum =
-	SOC_ENUM_SINGLE(CS42L51_DAC_CTL, 6, 3, cs42l51_dac_names);
+static SOC_ENUM_SINGLE_DECL(cs42l51_dac_mux_enum,
+			    CS42L51_DAC_CTL, 6, cs42l51_dac_names);
 static const struct snd_kcontrol_new cs42l51_dac_mux_controls =
 	SOC_DAPM_ENUM("Route", cs42l51_dac_mux_enum);
 
 static const char *cs42l51_adcl_names[] = {"AIN1 Left", "AIN2 Left",
 	"MIC Left", "MIC+preamp Left"};
-static const struct soc_enum cs42l51_adcl_mux_enum =
-	SOC_ENUM_SINGLE(CS42L51_ADC_INPUT, 4, 4, cs42l51_adcl_names);
+static SOC_ENUM_SINGLE_DECL(cs42l51_adcl_mux_enum,
+			    CS42L51_ADC_INPUT, 4, cs42l51_adcl_names);
 static const struct snd_kcontrol_new cs42l51_adcl_mux_controls =
 	SOC_DAPM_ENUM("Route", cs42l51_adcl_mux_enum);
 
 static const char *cs42l51_adcr_names[] = {"AIN1 Right", "AIN2 Right",
 	"MIC Right", "MIC+preamp Right"};
-static const struct soc_enum cs42l51_adcr_mux_enum =
-	SOC_ENUM_SINGLE(CS42L51_ADC_INPUT, 6, 4, cs42l51_adcr_names);
+static SOC_ENUM_SINGLE_DECL(cs42l51_adcr_mux_enum,
+			    CS42L51_ADC_INPUT, 6, cs42l51_adcr_names);
 static const struct snd_kcontrol_new cs42l51_adcr_mux_controls =
 	SOC_DAPM_ENUM("Route", cs42l51_adcr_mux_enum);
 
@@ -504,16 +485,9 @@ static struct snd_soc_dai_driver cs42l51_dai = {
 
 static int cs42l51_probe(struct snd_soc_codec *codec)
 {
-	struct cs42l51_private *cs42l51 = snd_soc_codec_get_drvdata(codec);
 	int ret, reg;
 
-	ret = cs42l51_fill_cache(codec);
-	if (ret < 0) {
-		dev_err(codec->dev, "failed to fill register cache\n");
-		return ret;
-	}
-
-	ret = snd_soc_codec_set_cache_io(codec, 8, 8, cs42l51->control_type);
+	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;
@@ -537,8 +511,6 @@ static int cs42l51_probe(struct snd_soc_codec *codec)
 
 static struct snd_soc_codec_driver soc_codec_device_cs42l51 = {
 	.probe = cs42l51_probe,
-	.reg_cache_size = CS42L51_NUMREGS + 1,
-	.reg_word_size = sizeof(u8),
 
 	.controls = cs42l51_snd_controls,
 	.num_controls = ARRAY_SIZE(cs42l51_snd_controls),
@@ -548,38 +520,53 @@ static struct snd_soc_codec_driver soc_codec_device_cs42l51 = {
 	.num_dapm_routes = ARRAY_SIZE(cs42l51_routes),
 };
 
+static const struct regmap_config cs42l51_regmap = {
+	.reg_bits = 8,
+	.val_bits = 8,
+
+	.max_register = CS42L51_CHARGE_FREQ,
+	.cache_type = REGCACHE_RBTREE,
+};
+
 static int cs42l51_i2c_probe(struct i2c_client *i2c_client,
 	const struct i2c_device_id *id)
 {
 	struct cs42l51_private *cs42l51;
+	struct regmap *regmap;
+	unsigned int val;
 	int ret;
 
+	regmap = devm_regmap_init_i2c(i2c_client, &cs42l51_regmap);
+	if (IS_ERR(regmap)) {
+		ret = PTR_ERR(regmap);
+		dev_err(&i2c_client->dev, "Failed to create regmap: %d\n",
+			ret);
+		return ret;
+	}
+
 	/* Verify that we have a CS42L51 */
-	ret = i2c_smbus_read_byte_data(i2c_client, CS42L51_CHIP_REV_ID);
+	ret = regmap_read(regmap, CS42L51_CHIP_REV_ID, &val);
 	if (ret < 0) {
 		dev_err(&i2c_client->dev, "failed to read I2C\n");
 		goto error;
 	}
 
-	if ((ret != CS42L51_MK_CHIP_REV(CS42L51_CHIP_ID, CS42L51_CHIP_REV_A)) &&
-	    (ret != CS42L51_MK_CHIP_REV(CS42L51_CHIP_ID, CS42L51_CHIP_REV_B))) {
-		dev_err(&i2c_client->dev, "Invalid chip id\n");
+	if ((val != CS42L51_MK_CHIP_REV(CS42L51_CHIP_ID, CS42L51_CHIP_REV_A)) &&
+	    (val != CS42L51_MK_CHIP_REV(CS42L51_CHIP_ID, CS42L51_CHIP_REV_B))) {
+		dev_err(&i2c_client->dev, "Invalid chip id: %x\n", val);
 		ret = -ENODEV;
 		goto error;
 	}
 
 	dev_info(&i2c_client->dev, "found device cs42l51 rev %d\n",
-				ret & 7);
+		 val & 7);
 
 	cs42l51 = devm_kzalloc(&i2c_client->dev, sizeof(struct cs42l51_private),
 			       GFP_KERNEL);
-	if (!cs42l51) {
-		dev_err(&i2c_client->dev, "could not allocate codec\n");
+	if (!cs42l51)
 		return -ENOMEM;
-	}
 
 	i2c_set_clientdata(i2c_client, cs42l51);
-	cs42l51->control_type = SND_SOC_I2C;
 
 	ret =  snd_soc_register_codec(&i2c_client->dev,
 			&soc_codec_device_cs42l51, &cs42l51_dai, 1);
@@ -599,10 +586,17 @@ static const struct i2c_device_id cs42l51_id[] = {
 };
 MODULE_DEVICE_TABLE(i2c, cs42l51_id);
 
+static const struct of_device_id cs42l51_of_match[] = {
+	{ .compatible = "cirrus,cs42l51", },
+	{ }
+};
+MODULE_DEVICE_TABLE(of, cs42l51_of_match);
+
 static struct i2c_driver cs42l51_i2c_driver = {
 	.driver = {
 		.name = "cs42l51-codec",
 		.owner = THIS_MODULE,
+		.of_match_table = cs42l51_of_match,
 	},
 	.id_table = cs42l51_id,
 	.probe = cs42l51_i2c_probe,
diff --git a/sound/soc/codecs/cs42l52.c b/sound/soc/codecs/cs42l52.c
index 1102ced9b20e..ea7938d9e13a 100644
--- a/sound/soc/codecs/cs42l52.c
+++ b/sound/soc/codecs/cs42l52.c
@@ -210,13 +210,11 @@ static const char * const cs42l52_adca_text[] = {
 static const char * const cs42l52_adcb_text[] = {
 	"Input1B", "Input2B", "Input3B", "Input4B", "PGA Input Right"};
 
-static const struct soc_enum adca_enum =
-	SOC_ENUM_SINGLE(CS42L52_ADC_PGA_A, 5,
-		ARRAY_SIZE(cs42l52_adca_text), cs42l52_adca_text);
+static SOC_ENUM_SINGLE_DECL(adca_enum,
+			    CS42L52_ADC_PGA_A, 5, cs42l52_adca_text);
 
-static const struct soc_enum adcb_enum =
-	SOC_ENUM_SINGLE(CS42L52_ADC_PGA_B, 5,
-		ARRAY_SIZE(cs42l52_adcb_text), cs42l52_adcb_text);
+static SOC_ENUM_SINGLE_DECL(adcb_enum,
+			    CS42L52_ADC_PGA_B, 5, cs42l52_adcb_text);
 
 static const struct snd_kcontrol_new adca_mux =
 	SOC_DAPM_ENUM("Left ADC Input Capture Mux", adca_enum);
@@ -229,26 +227,22 @@ static const char * const mic_bias_level_text[] = {
 	"0.8 +VA", "0.83 +VA", "0.91 +VA"
 };
 
-static const struct soc_enum mic_bias_level_enum =
-	SOC_ENUM_SINGLE(CS42L52_IFACE_CTL2, 0,
-			ARRAY_SIZE(mic_bias_level_text), mic_bias_level_text);
+static SOC_ENUM_SINGLE_DECL(mic_bias_level_enum,
+			    CS42L52_IFACE_CTL2, 0, mic_bias_level_text);
 
 static const char * const cs42l52_mic_text[] = { "MIC1", "MIC2" };
 
-static const struct soc_enum mica_enum =
-	SOC_ENUM_SINGLE(CS42L52_MICA_CTL, 5,
-			ARRAY_SIZE(cs42l52_mic_text), cs42l52_mic_text);
+static SOC_ENUM_SINGLE_DECL(mica_enum,
+			    CS42L52_MICA_CTL, 5, cs42l52_mic_text);
 
-static const struct soc_enum micb_enum =
-	SOC_ENUM_SINGLE(CS42L52_MICB_CTL, 5,
-			ARRAY_SIZE(cs42l52_mic_text), cs42l52_mic_text);
+static SOC_ENUM_SINGLE_DECL(micb_enum,
+			    CS42L52_MICB_CTL, 5, cs42l52_mic_text);
 
 static const char * const digital_output_mux_text[] = {"ADC", "DSP"};
 
-static const struct soc_enum digital_output_mux_enum =
-	SOC_ENUM_SINGLE(CS42L52_ADC_MISC_CTL, 6,
-			ARRAY_SIZE(digital_output_mux_text),
-			digital_output_mux_text);
+static SOC_ENUM_SINGLE_DECL(digital_output_mux_enum,
+			    CS42L52_ADC_MISC_CTL, 6,
+			    digital_output_mux_text);
 
 static const struct snd_kcontrol_new digital_output_mux =
 	SOC_DAPM_ENUM("Digital Output Mux", digital_output_mux_enum);
@@ -258,18 +252,18 @@ static const char * const hp_gain_num_text[] = {
 	"0.7099", "0.8399", "1.000", "1.1430"
 };
 
-static const struct soc_enum hp_gain_enum =
-	SOC_ENUM_SINGLE(CS42L52_PB_CTL1, 5,
-		ARRAY_SIZE(hp_gain_num_text), hp_gain_num_text);
+static SOC_ENUM_SINGLE_DECL(hp_gain_enum,
+			    CS42L52_PB_CTL1, 5,
+			    hp_gain_num_text);
 
 static const char * const beep_pitch_text[] = {
 	"C4", "C5", "D5", "E5", "F5", "G5", "A5", "B5",
 	"C6", "D6", "E6", "F6", "G6", "A6", "B6", "C7"
 };
 
-static const struct soc_enum beep_pitch_enum =
-	SOC_ENUM_SINGLE(CS42L52_BEEP_FREQ, 4,
-			ARRAY_SIZE(beep_pitch_text), beep_pitch_text);
+static SOC_ENUM_SINGLE_DECL(beep_pitch_enum,
+			    CS42L52_BEEP_FREQ, 4,
+			    beep_pitch_text);
 
 static const char * const beep_ontime_text[] = {
 	"86 ms", "430 ms", "780 ms", "1.20 s", "1.50 s",
@@ -277,66 +271,66 @@ static const char * const beep_ontime_text[] = {
 	"3.50 s", "3.80 s", "4.20 s", "4.50 s", "4.80 s", "5.20 s"
 };
 
-static const struct soc_enum beep_ontime_enum =
-	SOC_ENUM_SINGLE(CS42L52_BEEP_FREQ, 0,
-			ARRAY_SIZE(beep_ontime_text), beep_ontime_text);
+static SOC_ENUM_SINGLE_DECL(beep_ontime_enum,
+			    CS42L52_BEEP_FREQ, 0,
+			    beep_ontime_text);
 
 static const char * const beep_offtime_text[] = {
 	"1.23 s", "2.58 s", "3.90 s", "5.20 s",
 	"6.60 s", "8.05 s", "9.35 s", "10.80 s"
 };
 
-static const struct soc_enum beep_offtime_enum =
-	SOC_ENUM_SINGLE(CS42L52_BEEP_VOL, 5,
-			ARRAY_SIZE(beep_offtime_text), beep_offtime_text);
+static SOC_ENUM_SINGLE_DECL(beep_offtime_enum,
+			    CS42L52_BEEP_VOL, 5,
+			    beep_offtime_text);
 
 static const char * const beep_config_text[] = {
 	"Off", "Single", "Multiple", "Continuous"
 };
 
-static const struct soc_enum beep_config_enum =
-	SOC_ENUM_SINGLE(CS42L52_BEEP_TONE_CTL, 6,
-			ARRAY_SIZE(beep_config_text), beep_config_text);
+static SOC_ENUM_SINGLE_DECL(beep_config_enum,
+			    CS42L52_BEEP_TONE_CTL, 6,
+			    beep_config_text);
 
 static const char * const beep_bass_text[] = {
 	"50 Hz", "100 Hz", "200 Hz", "250 Hz"
 };
 
-static const struct soc_enum beep_bass_enum =
-	SOC_ENUM_SINGLE(CS42L52_BEEP_TONE_CTL, 1,
-			ARRAY_SIZE(beep_bass_text), beep_bass_text);
+static SOC_ENUM_SINGLE_DECL(beep_bass_enum,
+			    CS42L52_BEEP_TONE_CTL, 1,
+			    beep_bass_text);
 
 static const char * const beep_treble_text[] = {
 	"5 kHz", "7 kHz", "10 kHz", " 15 kHz"
 };
 
-static const struct soc_enum beep_treble_enum =
-	SOC_ENUM_SINGLE(CS42L52_BEEP_TONE_CTL, 3,
-			ARRAY_SIZE(beep_treble_text), beep_treble_text);
+static SOC_ENUM_SINGLE_DECL(beep_treble_enum,
+			    CS42L52_BEEP_TONE_CTL, 3,
+			    beep_treble_text);
 
 static const char * const ng_threshold_text[] = {
 	"-34dB", "-37dB", "-40dB", "-43dB",
 	"-46dB", "-52dB", "-58dB", "-64dB"
 };
 
-static const struct soc_enum ng_threshold_enum =
-	SOC_ENUM_SINGLE(CS42L52_NOISE_GATE_CTL, 2,
-		ARRAY_SIZE(ng_threshold_text), ng_threshold_text);
+static SOC_ENUM_SINGLE_DECL(ng_threshold_enum,
+			    CS42L52_NOISE_GATE_CTL, 2,
+			    ng_threshold_text);
 
 static const char * const cs42l52_ng_delay_text[] = {
 	"50ms", "100ms", "150ms", "200ms"};
 
-static const struct soc_enum ng_delay_enum =
-	SOC_ENUM_SINGLE(CS42L52_NOISE_GATE_CTL, 0,
-		ARRAY_SIZE(cs42l52_ng_delay_text), cs42l52_ng_delay_text);
+static SOC_ENUM_SINGLE_DECL(ng_delay_enum,
+			    CS42L52_NOISE_GATE_CTL, 0,
+			    cs42l52_ng_delay_text);
 
 static const char * const cs42l52_ng_type_text[] = {
 	"Apply Specific", "Apply All"
 };
 
-static const struct soc_enum ng_type_enum =
-	SOC_ENUM_SINGLE(CS42L52_NOISE_GATE_CTL, 6,
-		ARRAY_SIZE(cs42l52_ng_type_text), cs42l52_ng_type_text);
+static SOC_ENUM_SINGLE_DECL(ng_type_enum,
+			    CS42L52_NOISE_GATE_CTL, 6,
+			    cs42l52_ng_type_text);
 
 static const char * const left_swap_text[] = {
 	"Left", "LR 2", "Right"};
diff --git a/sound/soc/codecs/cs42l73.c b/sound/soc/codecs/cs42l73.c
index 7b95f7cbc515..e5778c015c8d 100644
--- a/sound/soc/codecs/cs42l73.c
+++ b/sound/soc/codecs/cs42l73.c
@@ -278,13 +278,13 @@ static const DECLARE_TLV_DB_SCALE(attn_tlv, -6300, 100, 1);
 static const char * const cs42l73_pgaa_text[] = { "Line A", "Mic 1" };
 static const char * const cs42l73_pgab_text[] = { "Line B", "Mic 2" };
 
-static const struct soc_enum pgaa_enum =
-	SOC_ENUM_SINGLE(CS42L73_ADCIPC, 3,
-		ARRAY_SIZE(cs42l73_pgaa_text), cs42l73_pgaa_text);
+static SOC_ENUM_SINGLE_DECL(pgaa_enum,
+			    CS42L73_ADCIPC, 3,
+			    cs42l73_pgaa_text);
 
-static const struct soc_enum pgab_enum =
-	SOC_ENUM_SINGLE(CS42L73_ADCIPC, 7,
-		ARRAY_SIZE(cs42l73_pgab_text), cs42l73_pgab_text);
+static SOC_ENUM_SINGLE_DECL(pgab_enum,
+			    CS42L73_ADCIPC, 7,
+			    cs42l73_pgab_text);
 
 static const struct snd_kcontrol_new pgaa_mux =
 	SOC_DAPM_ENUM("Left Analog Input Capture Mux", pgaa_enum);
@@ -309,9 +309,9 @@ static const struct snd_kcontrol_new input_right_mixer[] = {
 static const char * const cs42l73_ng_delay_text[] = {
 	"50ms", "100ms", "150ms", "200ms" };
 
-static const struct soc_enum ng_delay_enum =
-	SOC_ENUM_SINGLE(CS42L73_NGCAB, 0,
-		ARRAY_SIZE(cs42l73_ng_delay_text), cs42l73_ng_delay_text);
+static SOC_ENUM_SINGLE_DECL(ng_delay_enum,
+			    CS42L73_NGCAB, 0,
+			    cs42l73_ng_delay_text);
 
 static const char * const cs42l73_mono_mix_texts[] = {
 	"Left", "Right", "Mono Mix"};
@@ -357,19 +357,19 @@ static const struct snd_kcontrol_new esl_xsp_mixer =
 static const char * const cs42l73_ip_swap_text[] = {
 	"Stereo", "Mono A", "Mono B", "Swap A-B"};
 
-static const struct soc_enum ip_swap_enum =
-	SOC_ENUM_SINGLE(CS42L73_MIOPC, 6,
-		ARRAY_SIZE(cs42l73_ip_swap_text), cs42l73_ip_swap_text);
+static SOC_ENUM_SINGLE_DECL(ip_swap_enum,
+			    CS42L73_MIOPC, 6,
+			    cs42l73_ip_swap_text);
 
 static const char * const cs42l73_spo_mixer_text[] = {"Mono", "Stereo"};
 
-static const struct soc_enum vsp_output_mux_enum =
-	SOC_ENUM_SINGLE(CS42L73_MIXERCTL, 5,
-		ARRAY_SIZE(cs42l73_spo_mixer_text), cs42l73_spo_mixer_text);
+static SOC_ENUM_SINGLE_DECL(vsp_output_mux_enum,
+			    CS42L73_MIXERCTL, 5,
+			    cs42l73_spo_mixer_text);
 
-static const struct soc_enum xsp_output_mux_enum =
-	SOC_ENUM_SINGLE(CS42L73_MIXERCTL, 4,
-		ARRAY_SIZE(cs42l73_spo_mixer_text), cs42l73_spo_mixer_text);
+static SOC_ENUM_SINGLE_DECL(xsp_output_mux_enum,
+			    CS42L73_MIXERCTL, 4,
+			    cs42l73_spo_mixer_text);
 
 static const struct snd_kcontrol_new vsp_output_mux =
 	SOC_DAPM_ENUM("Route", vsp_output_mux_enum);
@@ -1108,7 +1108,7 @@ static int cs42l73_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
 	return 0;
 }
 
-static u32 cs42l73_asrc_rates[] = {
+static const unsigned int cs42l73_asrc_rates[] = {
 	8000, 11025, 12000, 16000, 22050,
 	24000, 32000, 44100, 48000
 };
@@ -1241,7 +1241,7 @@ static int cs42l73_set_tristate(struct snd_soc_dai *dai, int tristate)
 					0x7F, tristate << 7);
 }
 
-static struct snd_pcm_hw_constraint_list constraints_12_24 = {
+static const struct snd_pcm_hw_constraint_list constraints_12_24 = {
 	.count  = ARRAY_SIZE(cs42l73_asrc_rates),
 	.list   = cs42l73_asrc_rates,
 };
@@ -1255,9 +1255,6 @@ static int cs42l73_pcm_startup(struct snd_pcm_substream *substream,
 	return 0;
 }
 
-/* SNDRV_PCM_RATE_KNOT -> 12000, 24000 Hz, limit with constraint list */
-#define CS42L73_RATES (SNDRV_PCM_RATE_8000_48000 | SNDRV_PCM_RATE_KNOT)
-
 
 #define CS42L73_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
 	SNDRV_PCM_FMTBIT_S24_LE)
@@ -1278,14 +1275,14 @@ static struct snd_soc_dai_driver cs42l73_dai[] = {
 			.stream_name = "XSP Playback",
 			.channels_min = 1,
 			.channels_max = 2,
-			.rates = CS42L73_RATES,
+			.rates = SNDRV_PCM_RATE_KNOT,
 			.formats = CS42L73_FORMATS,
 		},
 		.capture = {
 			.stream_name = "XSP Capture",
 			.channels_min = 1,
 			.channels_max = 2,
-			.rates = CS42L73_RATES,
+			.rates = SNDRV_PCM_RATE_KNOT,
 			.formats = CS42L73_FORMATS,
 		},
 		.ops = &cs42l73_ops,
@@ -1298,14 +1295,14 @@ static struct snd_soc_dai_driver cs42l73_dai[] = {
 			.stream_name = "ASP Playback",
 			.channels_min = 2,
 			.channels_max = 2,
-			.rates = CS42L73_RATES,
+			.rates = SNDRV_PCM_RATE_KNOT,
 			.formats = CS42L73_FORMATS,
 		},
 		.capture = {
 			.stream_name = "ASP Capture",
 			.channels_min = 2,
 			.channels_max = 2,
-			.rates = CS42L73_RATES,
+			.rates = SNDRV_PCM_RATE_KNOT,
 			.formats = CS42L73_FORMATS,
 		},
 		.ops = &cs42l73_ops,
@@ -1318,14 +1315,14 @@ static struct snd_soc_dai_driver cs42l73_dai[] = {
 			.stream_name = "VSP Playback",
 			.channels_min = 1,
 			.channels_max = 2,
-			.rates = CS42L73_RATES,
+			.rates = SNDRV_PCM_RATE_KNOT,
 			.formats = CS42L73_FORMATS,
 		},
 		.capture = {
 			.stream_name = "VSP Capture",
 			.channels_min = 1,
 			.channels_max = 2,
-			.rates = CS42L73_RATES,
+			.rates = SNDRV_PCM_RATE_KNOT,
 			.formats = CS42L73_FORMATS,
 		},
 		.ops = &cs42l73_ops,
diff --git a/sound/soc/codecs/da7210.c b/sound/soc/codecs/da7210.c
index e62e294a8033..01e55fc72307 100644
--- a/sound/soc/codecs/da7210.c
+++ b/sound/soc/codecs/da7210.c
@@ -307,29 +307,29 @@ static const char * const da7210_hpf_cutoff_txt[] = {
 	"Fs/8192*pi", "Fs/4096*pi", "Fs/2048*pi", "Fs/1024*pi"
 };
 
-static const struct soc_enum da7210_dac_hpf_cutoff =
-	SOC_ENUM_SINGLE(DA7210_DAC_HPF, 0, 4, da7210_hpf_cutoff_txt);
+static SOC_ENUM_SINGLE_DECL(da7210_dac_hpf_cutoff,
+			    DA7210_DAC_HPF, 0, da7210_hpf_cutoff_txt);
 
-static const struct soc_enum da7210_adc_hpf_cutoff =
-	SOC_ENUM_SINGLE(DA7210_ADC_HPF, 0, 4, da7210_hpf_cutoff_txt);
+static SOC_ENUM_SINGLE_DECL(da7210_adc_hpf_cutoff,
+			    DA7210_ADC_HPF, 0, da7210_hpf_cutoff_txt);
 
 /* ADC and DAC voice (8kHz) high pass cutoff value */
 static const char * const da7210_vf_cutoff_txt[] = {
 	"2.5Hz", "25Hz", "50Hz", "100Hz", "150Hz", "200Hz", "300Hz", "400Hz"
 };
 
-static const struct soc_enum da7210_dac_vf_cutoff =
-	SOC_ENUM_SINGLE(DA7210_DAC_HPF, 4, 8, da7210_vf_cutoff_txt);
+static SOC_ENUM_SINGLE_DECL(da7210_dac_vf_cutoff,
+			    DA7210_DAC_HPF, 4, da7210_vf_cutoff_txt);
 
-static const struct soc_enum da7210_adc_vf_cutoff =
-	SOC_ENUM_SINGLE(DA7210_ADC_HPF, 4, 8, da7210_vf_cutoff_txt);
+static SOC_ENUM_SINGLE_DECL(da7210_adc_vf_cutoff,
+			    DA7210_ADC_HPF, 4, da7210_vf_cutoff_txt);
 
 static const char *da7210_hp_mode_txt[] = {
 	"Class H", "Class G"
 };
 
-static const struct soc_enum da7210_hp_mode_sel =
-	SOC_ENUM_SINGLE(DA7210_HP_CFG, 0, 2, da7210_hp_mode_txt);
+static SOC_ENUM_SINGLE_DECL(da7210_hp_mode_sel,
+			    DA7210_HP_CFG, 0, da7210_hp_mode_txt);
 
 /* ALC can be enabled only if noise suppression is disabled */
 static int da7210_put_alc_sw(struct snd_kcontrol *kcontrol,
diff --git a/sound/soc/codecs/da7213.c b/sound/soc/codecs/da7213.c
index 0c77e7ad7423..439d10387f10 100644
--- a/sound/soc/codecs/da7213.c
+++ b/sound/soc/codecs/da7213.c
@@ -63,30 +63,30 @@ static const char * const da7213_voice_hpf_corner_txt[] = {
 	"2.5Hz", "25Hz", "50Hz", "100Hz", "150Hz", "200Hz", "300Hz", "400Hz"
 };
 
-static const struct soc_enum da7213_dac_voice_hpf_corner =
-	SOC_ENUM_SINGLE(DA7213_DAC_FILTERS1, DA7213_VOICE_HPF_CORNER_SHIFT,
-			DA7213_VOICE_HPF_CORNER_MAX,
-			da7213_voice_hpf_corner_txt);
+static SOC_ENUM_SINGLE_DECL(da7213_dac_voice_hpf_corner,
+			    DA7213_DAC_FILTERS1,
+			    DA7213_VOICE_HPF_CORNER_SHIFT,
+			    da7213_voice_hpf_corner_txt);
 
-static const struct soc_enum da7213_adc_voice_hpf_corner =
-	SOC_ENUM_SINGLE(DA7213_ADC_FILTERS1, DA7213_VOICE_HPF_CORNER_SHIFT,
-			DA7213_VOICE_HPF_CORNER_MAX,
-			da7213_voice_hpf_corner_txt);
+static SOC_ENUM_SINGLE_DECL(da7213_adc_voice_hpf_corner,
+			    DA7213_ADC_FILTERS1,
+			    DA7213_VOICE_HPF_CORNER_SHIFT,
+			    da7213_voice_hpf_corner_txt);
 
 /* ADC and DAC high pass filter cutoff value */
 static const char * const da7213_audio_hpf_corner_txt[] = {
 	"Fs/24000", "Fs/12000", "Fs/6000", "Fs/3000"
 };
 
-static const struct soc_enum da7213_dac_audio_hpf_corner =
-	SOC_ENUM_SINGLE(DA7213_DAC_FILTERS1, DA7213_AUDIO_HPF_CORNER_SHIFT,
-			DA7213_AUDIO_HPF_CORNER_MAX,
-			da7213_audio_hpf_corner_txt);
+static SOC_ENUM_SINGLE_DECL(da7213_dac_audio_hpf_corner,
+			    DA7213_DAC_FILTERS1
+			    , DA7213_AUDIO_HPF_CORNER_SHIFT,
+			    da7213_audio_hpf_corner_txt);
 
-static const struct soc_enum da7213_adc_audio_hpf_corner =
-	SOC_ENUM_SINGLE(DA7213_ADC_FILTERS1, DA7213_AUDIO_HPF_CORNER_SHIFT,
-			DA7213_AUDIO_HPF_CORNER_MAX,
-			da7213_audio_hpf_corner_txt);
+static SOC_ENUM_SINGLE_DECL(da7213_adc_audio_hpf_corner,
+			    DA7213_ADC_FILTERS1,
+			    DA7213_AUDIO_HPF_CORNER_SHIFT,
+			    da7213_audio_hpf_corner_txt);
 
 /* Gain ramping rate value */
 static const char * const da7213_gain_ramp_rate_txt[] = {
@@ -94,52 +94,50 @@ static const char * const da7213_gain_ramp_rate_txt[] = {
 	"nominal rate / 32"
 };
 
-static const struct soc_enum da7213_gain_ramp_rate =
-	SOC_ENUM_SINGLE(DA7213_GAIN_RAMP_CTRL, DA7213_GAIN_RAMP_RATE_SHIFT,
-			DA7213_GAIN_RAMP_RATE_MAX, da7213_gain_ramp_rate_txt);
+static SOC_ENUM_SINGLE_DECL(da7213_gain_ramp_rate,
+			    DA7213_GAIN_RAMP_CTRL,
+			    DA7213_GAIN_RAMP_RATE_SHIFT,
+			    da7213_gain_ramp_rate_txt);
 
 /* DAC noise gate setup time value */
 static const char * const da7213_dac_ng_setup_time_txt[] = {
 	"256 samples", "512 samples", "1024 samples", "2048 samples"
 };
 
-static const struct soc_enum da7213_dac_ng_setup_time =
-	SOC_ENUM_SINGLE(DA7213_DAC_NG_SETUP_TIME,
-			DA7213_DAC_NG_SETUP_TIME_SHIFT,
-			DA7213_DAC_NG_SETUP_TIME_MAX,
-			da7213_dac_ng_setup_time_txt);
+static SOC_ENUM_SINGLE_DECL(da7213_dac_ng_setup_time,
+			    DA7213_DAC_NG_SETUP_TIME,
+			    DA7213_DAC_NG_SETUP_TIME_SHIFT,
+			    da7213_dac_ng_setup_time_txt);
 
 /* DAC noise gate rampup rate value */
 static const char * const da7213_dac_ng_rampup_txt[] = {
 	"0.02 ms/dB", "0.16 ms/dB"
 };
 
-static const struct soc_enum da7213_dac_ng_rampup_rate =
-	SOC_ENUM_SINGLE(DA7213_DAC_NG_SETUP_TIME,
-			DA7213_DAC_NG_RAMPUP_RATE_SHIFT,
-			DA7213_DAC_NG_RAMP_RATE_MAX,
-			da7213_dac_ng_rampup_txt);
+static SOC_ENUM_SINGLE_DECL(da7213_dac_ng_rampup_rate,
+			    DA7213_DAC_NG_SETUP_TIME,
+			    DA7213_DAC_NG_RAMPUP_RATE_SHIFT,
+			    da7213_dac_ng_rampup_txt);
 
 /* DAC noise gate rampdown rate value */
 static const char * const da7213_dac_ng_rampdown_txt[] = {
 	"0.64 ms/dB", "20.48 ms/dB"
 };
 
-static const struct soc_enum da7213_dac_ng_rampdown_rate =
-	SOC_ENUM_SINGLE(DA7213_DAC_NG_SETUP_TIME,
-			DA7213_DAC_NG_RAMPDN_RATE_SHIFT,
-			DA7213_DAC_NG_RAMP_RATE_MAX,
-			da7213_dac_ng_rampdown_txt);
+static SOC_ENUM_SINGLE_DECL(da7213_dac_ng_rampdown_rate,
+			    DA7213_DAC_NG_SETUP_TIME,
+			    DA7213_DAC_NG_RAMPDN_RATE_SHIFT,
+			    da7213_dac_ng_rampdown_txt);
 
 /* DAC soft mute rate value */
 static const char * const da7213_dac_soft_mute_rate_txt[] = {
 	"1", "2", "4", "8", "16", "32", "64"
 };
 
-static const struct soc_enum da7213_dac_soft_mute_rate =
-	SOC_ENUM_SINGLE(DA7213_DAC_FILTERS5, DA7213_DAC_SOFTMUTE_RATE_SHIFT,
-			DA7213_DAC_SOFTMUTE_RATE_MAX,
-			da7213_dac_soft_mute_rate_txt);
+static SOC_ENUM_SINGLE_DECL(da7213_dac_soft_mute_rate,
+			    DA7213_DAC_FILTERS5,
+			    DA7213_DAC_SOFTMUTE_RATE_SHIFT,
+			    da7213_dac_soft_mute_rate_txt);
 
 /* ALC Attack Rate select */
 static const char * const da7213_alc_attack_rate_txt[] = {
@@ -147,9 +145,10 @@ static const char * const da7213_alc_attack_rate_txt[] = {
 	"5632/fs", "11264/fs", "22528/fs", "45056/fs", "90112/fs", "180224/fs"
 };
 
-static const struct soc_enum da7213_alc_attack_rate =
-	SOC_ENUM_SINGLE(DA7213_ALC_CTRL2, DA7213_ALC_ATTACK_SHIFT,
-			DA7213_ALC_ATTACK_MAX, da7213_alc_attack_rate_txt);
+static SOC_ENUM_SINGLE_DECL(da7213_alc_attack_rate,
+			    DA7213_ALC_CTRL2,
+			    DA7213_ALC_ATTACK_SHIFT,
+			    da7213_alc_attack_rate_txt);
 
 /* ALC Release Rate select */
 static const char * const da7213_alc_release_rate_txt[] = {
@@ -157,9 +156,10 @@ static const char * const da7213_alc_release_rate_txt[] = {
 	"11264/fs", "22528/fs", "45056/fs", "90112/fs", "180224/fs"
 };
 
-static const struct soc_enum da7213_alc_release_rate =
-	SOC_ENUM_SINGLE(DA7213_ALC_CTRL2, DA7213_ALC_RELEASE_SHIFT,
-			DA7213_ALC_RELEASE_MAX, da7213_alc_release_rate_txt);
+static SOC_ENUM_SINGLE_DECL(da7213_alc_release_rate,
+			    DA7213_ALC_CTRL2,
+			    DA7213_ALC_RELEASE_SHIFT,
+			    da7213_alc_release_rate_txt);
 
 /* ALC Hold Time select */
 static const char * const da7213_alc_hold_time_txt[] = {
@@ -168,22 +168,25 @@ static const char * const da7213_alc_hold_time_txt[] = {
 	"253952/fs", "507904/fs", "1015808/fs", "2031616/fs"
 };
 
-static const struct soc_enum da7213_alc_hold_time =
-	SOC_ENUM_SINGLE(DA7213_ALC_CTRL3, DA7213_ALC_HOLD_SHIFT,
-			DA7213_ALC_HOLD_MAX, da7213_alc_hold_time_txt);
+static SOC_ENUM_SINGLE_DECL(da7213_alc_hold_time,
+			    DA7213_ALC_CTRL3,
+			    DA7213_ALC_HOLD_SHIFT,
+			    da7213_alc_hold_time_txt);
 
 /* ALC Input Signal Tracking rate select */
 static const char * const da7213_alc_integ_rate_txt[] = {
 	"1/4", "1/16", "1/256", "1/65536"
 };
 
-static const struct soc_enum da7213_alc_integ_attack_rate =
-	SOC_ENUM_SINGLE(DA7213_ALC_CTRL3, DA7213_ALC_INTEG_ATTACK_SHIFT,
-			DA7213_ALC_INTEG_MAX, da7213_alc_integ_rate_txt);
+static SOC_ENUM_SINGLE_DECL(da7213_alc_integ_attack_rate,
+			    DA7213_ALC_CTRL3,
+			    DA7213_ALC_INTEG_ATTACK_SHIFT,
+			    da7213_alc_integ_rate_txt);
 
-static const struct soc_enum da7213_alc_integ_release_rate =
-	SOC_ENUM_SINGLE(DA7213_ALC_CTRL3, DA7213_ALC_INTEG_RELEASE_SHIFT,
-			DA7213_ALC_INTEG_MAX, da7213_alc_integ_rate_txt);
+static SOC_ENUM_SINGLE_DECL(da7213_alc_integ_release_rate,
+			    DA7213_ALC_CTRL3,
+			    DA7213_ALC_INTEG_RELEASE_SHIFT,
+			    da7213_alc_integ_rate_txt);
 
 
 /*
@@ -584,15 +587,17 @@ static const char * const da7213_mic_amp_in_sel_txt[] = {
 	"Differential", "MIC_P", "MIC_N"
 };
 
-static const struct soc_enum da7213_mic_1_amp_in_sel =
-	SOC_ENUM_SINGLE(DA7213_MIC_1_CTRL, DA7213_MIC_AMP_IN_SEL_SHIFT,
-			DA7213_MIC_AMP_IN_SEL_MAX, da7213_mic_amp_in_sel_txt);
+static SOC_ENUM_SINGLE_DECL(da7213_mic_1_amp_in_sel,
+			    DA7213_MIC_1_CTRL,
+			    DA7213_MIC_AMP_IN_SEL_SHIFT,
+			    da7213_mic_amp_in_sel_txt);
 static const struct snd_kcontrol_new da7213_mic_1_amp_in_sel_mux =
 	SOC_DAPM_ENUM("Mic 1 Amp Source MUX", da7213_mic_1_amp_in_sel);
 
-static const struct soc_enum da7213_mic_2_amp_in_sel =
-	SOC_ENUM_SINGLE(DA7213_MIC_2_CTRL, DA7213_MIC_AMP_IN_SEL_SHIFT,
-			DA7213_MIC_AMP_IN_SEL_MAX, da7213_mic_amp_in_sel_txt);
+static SOC_ENUM_SINGLE_DECL(da7213_mic_2_amp_in_sel,
+			    DA7213_MIC_2_CTRL,
+			    DA7213_MIC_AMP_IN_SEL_SHIFT,
+			    da7213_mic_amp_in_sel_txt);
 static const struct snd_kcontrol_new da7213_mic_2_amp_in_sel_mux =
 	SOC_DAPM_ENUM("Mic 2 Amp Source MUX", da7213_mic_2_amp_in_sel);
 
@@ -601,15 +606,17 @@ static const char * const da7213_dai_src_txt[] = {
 	"ADC Left", "ADC Right", "DAI Input Left", "DAI Input Right"
 };
 
-static const struct soc_enum da7213_dai_l_src =
-	SOC_ENUM_SINGLE(DA7213_DIG_ROUTING_DAI, DA7213_DAI_L_SRC_SHIFT,
-			DA7213_DAI_SRC_MAX, da7213_dai_src_txt);
+static SOC_ENUM_SINGLE_DECL(da7213_dai_l_src,
+			    DA7213_DIG_ROUTING_DAI,
+			    DA7213_DAI_L_SRC_SHIFT,
+			    da7213_dai_src_txt);
 static const struct snd_kcontrol_new da7213_dai_l_src_mux =
 	SOC_DAPM_ENUM("DAI Left Source MUX", da7213_dai_l_src);
 
-static const struct soc_enum da7213_dai_r_src =
-	SOC_ENUM_SINGLE(DA7213_DIG_ROUTING_DAI, DA7213_DAI_R_SRC_SHIFT,
-			DA7213_DAI_SRC_MAX, da7213_dai_src_txt);
+static SOC_ENUM_SINGLE_DECL(da7213_dai_r_src,
+			    DA7213_DIG_ROUTING_DAI,
+			    DA7213_DAI_R_SRC_SHIFT,
+			    da7213_dai_src_txt);
 static const struct snd_kcontrol_new da7213_dai_r_src_mux =
 	SOC_DAPM_ENUM("DAI Right Source MUX", da7213_dai_r_src);
 
@@ -619,15 +626,17 @@ static const char * const da7213_dac_src_txt[] = {
 	"DAI Input Right"
 };
 
-static const struct soc_enum da7213_dac_l_src =
-	SOC_ENUM_SINGLE(DA7213_DIG_ROUTING_DAC, DA7213_DAC_L_SRC_SHIFT,
-			DA7213_DAC_SRC_MAX, da7213_dac_src_txt);
+static SOC_ENUM_SINGLE_DECL(da7213_dac_l_src,
+			    DA7213_DIG_ROUTING_DAC,
+			    DA7213_DAC_L_SRC_SHIFT,
+			    da7213_dac_src_txt);
 static const struct snd_kcontrol_new da7213_dac_l_src_mux =
 	SOC_DAPM_ENUM("DAC Left Source MUX", da7213_dac_l_src);
 
-static const struct soc_enum da7213_dac_r_src =
-	SOC_ENUM_SINGLE(DA7213_DIG_ROUTING_DAC, DA7213_DAC_R_SRC_SHIFT,
-			DA7213_DAC_SRC_MAX, da7213_dac_src_txt);
+static SOC_ENUM_SINGLE_DECL(da7213_dac_r_src,
+			    DA7213_DIG_ROUTING_DAC,
+			    DA7213_DAC_R_SRC_SHIFT,
+			    da7213_dac_src_txt);
 static const struct snd_kcontrol_new da7213_dac_r_src_mux =
 	SOC_DAPM_ENUM("DAC Right Source MUX", da7213_dac_r_src);
 
diff --git a/sound/soc/codecs/da732x.c b/sound/soc/codecs/da732x.c
index f4d965ebc29e..4d1c302f5a76 100644
--- a/sound/soc/codecs/da732x.c
+++ b/sound/soc/codecs/da732x.c
@@ -269,81 +269,65 @@ static const char *da732x_hpf_voice[] = {
 	"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 SOC_ENUM_SINGLE_DECL(da732x_dac1_hpf_mode_enum,
+			    DA732X_REG_DAC1_HPF, DA732X_HPF_MODE_SHIFT,
+			    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 SOC_ENUM_SINGLE_DECL(da732x_dac2_hpf_mode_enum,
+			    DA732X_REG_DAC2_HPF, DA732X_HPF_MODE_SHIFT,
+			    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 SOC_ENUM_SINGLE_DECL(da732x_dac3_hpf_mode_enum,
+			    DA732X_REG_DAC3_HPF, DA732X_HPF_MODE_SHIFT,
+			    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 SOC_ENUM_SINGLE_DECL(da732x_adc1_hpf_mode_enum,
+			    DA732X_REG_ADC1_HPF, DA732X_HPF_MODE_SHIFT,
+			    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 SOC_ENUM_SINGLE_DECL(da732x_adc2_hpf_mode_enum,
+			    DA732X_REG_ADC2_HPF, DA732X_HPF_MODE_SHIFT,
+			    da732x_hpf_mode);
 
-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 SOC_ENUM_SINGLE_DECL(da732x_dac1_hp_filter_enum,
+			    DA732X_REG_DAC1_HPF, DA732X_HPF_MUSIC_SHIFT,
+			    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 SOC_ENUM_SINGLE_DECL(da732x_dac2_hp_filter_enum,
+			    DA732X_REG_DAC2_HPF, DA732X_HPF_MUSIC_SHIFT,
+			    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 SOC_ENUM_SINGLE_DECL(da732x_dac3_hp_filter_enum,
+			    DA732X_REG_DAC3_HPF, DA732X_HPF_MUSIC_SHIFT,
+			    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 SOC_ENUM_SINGLE_DECL(da732x_adc1_hp_filter_enum,
+			    DA732X_REG_ADC1_HPF, DA732X_HPF_MUSIC_SHIFT,
+			    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 SOC_ENUM_SINGLE_DECL(da732x_adc2_hp_filter_enum,
+			    DA732X_REG_ADC2_HPF, DA732X_HPF_MUSIC_SHIFT,
+			    da732x_hpf_music);
 
-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 SOC_ENUM_SINGLE_DECL(da732x_dac1_voice_filter_enum,
+			    DA732X_REG_DAC1_HPF, DA732X_HPF_VOICE_SHIFT,
+			    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 SOC_ENUM_SINGLE_DECL(da732x_dac2_voice_filter_enum,
+			    DA732X_REG_DAC2_HPF, DA732X_HPF_VOICE_SHIFT,
+			    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 SOC_ENUM_SINGLE_DECL(da732x_dac3_voice_filter_enum,
+			    DA732X_REG_DAC3_HPF, DA732X_HPF_VOICE_SHIFT,
+			    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 SOC_ENUM_SINGLE_DECL(da732x_adc1_voice_filter_enum,
+			    DA732X_REG_ADC1_HPF, DA732X_HPF_VOICE_SHIFT,
+			    da732x_hpf_voice);
 
+static SOC_ENUM_SINGLE_DECL(da732x_adc2_voice_filter_enum,
+			    DA732X_REG_ADC2_HPF, DA732X_HPF_VOICE_SHIFT,
+			    da732x_hpf_voice);
 
 static int da732x_hpf_set(struct snd_kcontrol *kcontrol,
 			  struct snd_ctl_elem_value *ucontrol)
@@ -714,65 +698,65 @@ static const char *enable_text[] = {
 };
 
 /* 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 SOC_ENUM_SINGLE_DECL(adc1l_enum,
+			    DA732X_REG_INP_MUX, DA732X_ADC1L_MUX_SEL_SHIFT,
+			    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 SOC_ENUM_SINGLE_DECL(adc1r_enum,
+			    DA732X_REG_INP_MUX, DA732X_ADC1R_MUX_SEL_SHIFT,
+			    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 SOC_ENUM_SINGLE_DECL(adc2l_enum,
+			    DA732X_REG_INP_MUX, DA732X_ADC2L_MUX_SEL_SHIFT,
+			    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 SOC_ENUM_SINGLE_DECL(adc2r_enum,
+			    DA732X_REG_INP_MUX, DA732X_ADC2R_MUX_SEL_SHIFT,
+			    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 SOC_ENUM_SINGLE_DECL(da732x_hp_left_output,
+			    DA732X_REG_HPL, DA732X_HP_OUT_DAC_EN_SHIFT,
+			    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 SOC_ENUM_SINGLE_DECL(da732x_hp_right_output,
+			    DA732X_REG_HPR, DA732X_HP_OUT_DAC_EN_SHIFT,
+			    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 SOC_ENUM_SINGLE_DECL(da732x_speaker_output,
+			    DA732X_REG_LIN3, DA732X_LOUT_DAC_EN_SHIFT,
+			    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 SOC_ENUM_SINGLE_DECL(da732x_lout4_output,
+			    DA732X_REG_LIN4, DA732X_LOUT_DAC_EN_SHIFT,
+			    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 SOC_ENUM_SINGLE_DECL(da732x_lout2_output,
+			    DA732X_REG_LIN2, DA732X_LOUT_DAC_EN_SHIFT,
+			    enable_text);
 
 static const struct snd_kcontrol_new lout2_mux =
 	SOC_DAPM_ENUM("LOUT2 Switch", da732x_lout2_output);
@@ -1499,8 +1483,8 @@ static int da732x_set_bias_level(struct snd_soc_codec *codec,
 
 			da732x_hp_dc_offset_cancellation(codec);
 
-			regcache_cache_only(codec->control_data, false);
-			regcache_sync(codec->control_data);
+			regcache_cache_only(da732x->regmap, false);
+			regcache_sync(da732x->regmap);
 		} else {
 			snd_soc_update_bits(codec, DA732X_REG_BIAS_EN,
 					    DA732X_BIAS_BOOST_MASK,
@@ -1511,7 +1495,7 @@ static int da732x_set_bias_level(struct snd_soc_codec *codec,
 		}
 		break;
 	case SND_SOC_BIAS_OFF:
-		regcache_cache_only(codec->control_data, true);
+		regcache_cache_only(da732x->regmap, true);
 		da732x_set_charge_pump(codec, DA732X_DISABLE_CP);
 		snd_soc_update_bits(codec, DA732X_REG_BIAS_EN, DA732X_BIAS_EN,
 				    DA732X_BIAS_DIS);
@@ -1566,7 +1550,6 @@ static struct snd_soc_codec_driver soc_codec_dev_da732x = {
 	.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 int da732x_i2c_probe(struct i2c_client *i2c,
diff --git a/sound/soc/codecs/da732x.h b/sound/soc/codecs/da732x.h
index c8ce5475de22..1dceafeec415 100644
--- a/sound/soc/codecs/da732x.h
+++ b/sound/soc/codecs/da732x.h
@@ -113,9 +113,6 @@
 #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,
diff --git a/sound/soc/codecs/da9055.c b/sound/soc/codecs/da9055.c
index 422812613a28..f118daa91234 100644
--- a/sound/soc/codecs/da9055.c
+++ b/sound/soc/codecs/da9055.c
@@ -18,6 +18,8 @@
 #include <linux/regmap.h>
 #include <linux/slab.h>
 #include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
 #include <sound/pcm.h>
 #include <sound/pcm_params.h>
 #include <sound/soc.h>
@@ -321,22 +323,22 @@ static const char * const da9055_hpf_cutoff_txt[] = {
 	"Fs/24000", "Fs/12000", "Fs/6000", "Fs/3000"
 };
 
-static const struct soc_enum da9055_dac_hpf_cutoff =
-	SOC_ENUM_SINGLE(DA9055_DAC_FILTERS1, 4, 4, da9055_hpf_cutoff_txt);
+static SOC_ENUM_SINGLE_DECL(da9055_dac_hpf_cutoff,
+			    DA9055_DAC_FILTERS1, 4, da9055_hpf_cutoff_txt);
 
-static const struct soc_enum da9055_adc_hpf_cutoff =
-	SOC_ENUM_SINGLE(DA9055_ADC_FILTERS1, 4, 4, da9055_hpf_cutoff_txt);
+static SOC_ENUM_SINGLE_DECL(da9055_adc_hpf_cutoff,
+			    DA9055_ADC_FILTERS1, 4, da9055_hpf_cutoff_txt);
 
 /* ADC and DAC voice mode (8kHz) high pass cutoff value */
 static const char * const da9055_vf_cutoff_txt[] = {
 	"2.5Hz", "25Hz", "50Hz", "100Hz", "150Hz", "200Hz", "300Hz", "400Hz"
 };
 
-static const struct soc_enum da9055_dac_vf_cutoff =
-	SOC_ENUM_SINGLE(DA9055_DAC_FILTERS1, 0, 8, da9055_vf_cutoff_txt);
+static SOC_ENUM_SINGLE_DECL(da9055_dac_vf_cutoff,
+			    DA9055_DAC_FILTERS1, 0, da9055_vf_cutoff_txt);
 
-static const struct soc_enum da9055_adc_vf_cutoff =
-	SOC_ENUM_SINGLE(DA9055_ADC_FILTERS1, 0, 8, da9055_vf_cutoff_txt);
+static SOC_ENUM_SINGLE_DECL(da9055_adc_vf_cutoff,
+			    DA9055_ADC_FILTERS1, 0, da9055_vf_cutoff_txt);
 
 /* Gain ramping rate value */
 static const char * const da9055_gain_ramping_txt[] = {
@@ -344,44 +346,44 @@ static const char * const da9055_gain_ramping_txt[] = {
 	"nominal rate / 8"
 };
 
-static const struct soc_enum da9055_gain_ramping_rate =
-	SOC_ENUM_SINGLE(DA9055_GAIN_RAMP_CTRL, 0, 4, da9055_gain_ramping_txt);
+static SOC_ENUM_SINGLE_DECL(da9055_gain_ramping_rate,
+			    DA9055_GAIN_RAMP_CTRL, 0, da9055_gain_ramping_txt);
 
 /* DAC noise gate setup time value */
 static const char * const da9055_dac_ng_setup_time_txt[] = {
 	"256 samples", "512 samples", "1024 samples", "2048 samples"
 };
 
-static const struct soc_enum da9055_dac_ng_setup_time =
-	SOC_ENUM_SINGLE(DA9055_DAC_NG_SETUP_TIME, 0, 4,
-			da9055_dac_ng_setup_time_txt);
+static SOC_ENUM_SINGLE_DECL(da9055_dac_ng_setup_time,
+			    DA9055_DAC_NG_SETUP_TIME, 0,
+			    da9055_dac_ng_setup_time_txt);
 
 /* DAC noise gate rampup rate value */
 static const char * const da9055_dac_ng_rampup_txt[] = {
 	"0.02 ms/dB", "0.16 ms/dB"
 };
 
-static const struct soc_enum da9055_dac_ng_rampup_rate =
-	SOC_ENUM_SINGLE(DA9055_DAC_NG_SETUP_TIME, 2, 2,
-			da9055_dac_ng_rampup_txt);
+static SOC_ENUM_SINGLE_DECL(da9055_dac_ng_rampup_rate,
+			    DA9055_DAC_NG_SETUP_TIME, 2,
+			    da9055_dac_ng_rampup_txt);
 
 /* DAC noise gate rampdown rate value */
 static const char * const da9055_dac_ng_rampdown_txt[] = {
 	"0.64 ms/dB", "20.48 ms/dB"
 };
 
-static const struct soc_enum da9055_dac_ng_rampdown_rate =
-	SOC_ENUM_SINGLE(DA9055_DAC_NG_SETUP_TIME, 3, 2,
-			da9055_dac_ng_rampdown_txt);
+static SOC_ENUM_SINGLE_DECL(da9055_dac_ng_rampdown_rate,
+			    DA9055_DAC_NG_SETUP_TIME, 3,
+			    da9055_dac_ng_rampdown_txt);
 
 /* DAC soft mute rate value */
 static const char * const da9055_dac_soft_mute_rate_txt[] = {
 	"1", "2", "4", "8", "16", "32", "64"
 };
 
-static const struct soc_enum da9055_dac_soft_mute_rate =
-	SOC_ENUM_SINGLE(DA9055_DAC_FILTERS5, 4, 7,
-			da9055_dac_soft_mute_rate_txt);
+static SOC_ENUM_SINGLE_DECL(da9055_dac_soft_mute_rate,
+			    DA9055_DAC_FILTERS5, 4,
+			    da9055_dac_soft_mute_rate_txt);
 
 /* DAC routing select */
 static const char * const da9055_dac_src_txt[] = {
@@ -389,40 +391,40 @@ static const char * const da9055_dac_src_txt[] = {
 	"AIF input right"
 };
 
-static const struct soc_enum da9055_dac_l_src =
-	SOC_ENUM_SINGLE(DA9055_DIG_ROUTING_DAC, 0, 4, da9055_dac_src_txt);
+static SOC_ENUM_SINGLE_DECL(da9055_dac_l_src,
+			    DA9055_DIG_ROUTING_DAC, 0, da9055_dac_src_txt);
 
-static const struct soc_enum da9055_dac_r_src =
-	SOC_ENUM_SINGLE(DA9055_DIG_ROUTING_DAC, 4, 4, da9055_dac_src_txt);
+static SOC_ENUM_SINGLE_DECL(da9055_dac_r_src,
+			    DA9055_DIG_ROUTING_DAC, 4, da9055_dac_src_txt);
 
 /* MIC PGA Left source select */
 static const char * const da9055_mic_l_src_txt[] = {
 	"MIC1_P_N", "MIC1_P", "MIC1_N", "MIC2_L"
 };
 
-static const struct soc_enum da9055_mic_l_src =
-	SOC_ENUM_SINGLE(DA9055_MIXIN_L_SELECT, 4, 4, da9055_mic_l_src_txt);
+static SOC_ENUM_SINGLE_DECL(da9055_mic_l_src,
+			    DA9055_MIXIN_L_SELECT, 4, da9055_mic_l_src_txt);
 
 /* MIC PGA Right source select */
 static const char * const da9055_mic_r_src_txt[] = {
 	"MIC2_R_L", "MIC2_R", "MIC2_L"
 };
 
-static const struct soc_enum da9055_mic_r_src =
-	SOC_ENUM_SINGLE(DA9055_MIXIN_R_SELECT, 4, 3, da9055_mic_r_src_txt);
+static SOC_ENUM_SINGLE_DECL(da9055_mic_r_src,
+			    DA9055_MIXIN_R_SELECT, 4, da9055_mic_r_src_txt);
 
 /* ALC Input Signal Tracking rate select */
 static const char * const da9055_signal_tracking_rate_txt[] = {
 	"1/4", "1/16", "1/256", "1/65536"
 };
 
-static const struct soc_enum da9055_integ_attack_rate =
-	SOC_ENUM_SINGLE(DA9055_ALC_CTRL3, 4, 4,
-			da9055_signal_tracking_rate_txt);
+static SOC_ENUM_SINGLE_DECL(da9055_integ_attack_rate,
+			    DA9055_ALC_CTRL3, 4,
+			    da9055_signal_tracking_rate_txt);
 
-static const struct soc_enum da9055_integ_release_rate =
-	SOC_ENUM_SINGLE(DA9055_ALC_CTRL3, 6, 4,
-			da9055_signal_tracking_rate_txt);
+static SOC_ENUM_SINGLE_DECL(da9055_integ_release_rate,
+			    DA9055_ALC_CTRL3, 6,
+			    da9055_signal_tracking_rate_txt);
 
 /* ALC Attack Rate select */
 static const char * const da9055_attack_rate_txt[] = {
@@ -430,8 +432,8 @@ static const char * const da9055_attack_rate_txt[] = {
 	"5632/fs", "11264/fs", "22528/fs", "45056/fs", "90112/fs", "180224/fs"
 };
 
-static const struct soc_enum da9055_attack_rate =
-	SOC_ENUM_SINGLE(DA9055_ALC_CTRL2, 0, 13, da9055_attack_rate_txt);
+static SOC_ENUM_SINGLE_DECL(da9055_attack_rate,
+			    DA9055_ALC_CTRL2, 0, da9055_attack_rate_txt);
 
 /* ALC Release Rate select */
 static const char * const da9055_release_rate_txt[] = {
@@ -439,8 +441,8 @@ static const char * const da9055_release_rate_txt[] = {
 	"11264/fs", "22528/fs", "45056/fs", "90112/fs", "180224/fs"
 };
 
-static const struct soc_enum da9055_release_rate =
-	SOC_ENUM_SINGLE(DA9055_ALC_CTRL2, 4, 11, da9055_release_rate_txt);
+static SOC_ENUM_SINGLE_DECL(da9055_release_rate,
+			    DA9055_ALC_CTRL2, 4, da9055_release_rate_txt);
 
 /* ALC Hold Time select */
 static const char * const da9055_hold_time_txt[] = {
@@ -449,8 +451,8 @@ static const char * const da9055_hold_time_txt[] = {
 	"253952/fs", "507904/fs", "1015808/fs", "2031616/fs"
 };
 
-static const struct soc_enum da9055_hold_time =
-	SOC_ENUM_SINGLE(DA9055_ALC_CTRL3, 0, 16, da9055_hold_time_txt);
+static SOC_ENUM_SINGLE_DECL(da9055_hold_time,
+			    DA9055_ALC_CTRL3, 0, da9055_hold_time_txt);
 
 static int da9055_get_alc_data(struct snd_soc_codec *codec, u8 reg_val)
 {
@@ -1536,11 +1538,17 @@ static const struct i2c_device_id da9055_i2c_id[] = {
 };
 MODULE_DEVICE_TABLE(i2c, da9055_i2c_id);
 
+static const struct of_device_id da9055_of_match[] = {
+	{ .compatible = "dlg,da9055-codec", },
+	{ }
+};
+
 /* I2C codec control layer */
 static struct i2c_driver da9055_i2c_driver = {
 	.driver = {
 		.name = "da9055-codec",
 		.owner = THIS_MODULE,
+		.of_match_table = of_match_ptr(da9055_of_match),
 	},
 	.probe		= da9055_i2c_probe,
 	.remove		= da9055_remove,
diff --git a/sound/soc/codecs/lm49453.c b/sound/soc/codecs/lm49453.c
index e19490cfb3a8..6b7fe5e54881 100644
--- a/sound/soc/codecs/lm49453.c
+++ b/sound/soc/codecs/lm49453.c
@@ -195,18 +195,18 @@ struct lm49453_priv {
 
 static const char *lm49453_mic2mode_text[] = {"Single Ended", "Differential"};
 
-static const SOC_ENUM_SINGLE_DECL(lm49453_mic2mode_enum, LM49453_P0_MICR_REG, 5,
-				  lm49453_mic2mode_text);
+static SOC_ENUM_SINGLE_DECL(lm49453_mic2mode_enum, LM49453_P0_MICR_REG, 5,
+			    lm49453_mic2mode_text);
 
 static const char *lm49453_dmic_cfg_text[] = {"DMICDAT1", "DMICDAT2"};
 
-static const SOC_ENUM_SINGLE_DECL(lm49453_dmic12_cfg_enum,
-				  LM49453_P0_DIGITAL_MIC1_CONFIG_REG,
-				  7, lm49453_dmic_cfg_text);
+static SOC_ENUM_SINGLE_DECL(lm49453_dmic12_cfg_enum,
+			    LM49453_P0_DIGITAL_MIC1_CONFIG_REG, 7,
+			    lm49453_dmic_cfg_text);
 
-static const SOC_ENUM_SINGLE_DECL(lm49453_dmic34_cfg_enum,
-				  LM49453_P0_DIGITAL_MIC2_CONFIG_REG,
-				  7, lm49453_dmic_cfg_text);
+static SOC_ENUM_SINGLE_DECL(lm49453_dmic34_cfg_enum,
+			    LM49453_P0_DIGITAL_MIC2_CONFIG_REG, 7,
+			    lm49453_dmic_cfg_text);
 
 /* MUX Controls */
 static const char *lm49453_adcl_mux_text[] = { "MIC1", "Aux_L" };
diff --git a/sound/soc/codecs/max98088.c b/sound/soc/codecs/max98088.c
index ee660e2d3df3..bb1ecfc4459b 100644
--- a/sound/soc/codecs/max98088.c
+++ b/sound/soc/codecs/max98088.c
@@ -1849,7 +1849,7 @@ static void max98088_handle_eq_pdata(struct snd_soc_codec *codec)
 
        /* Now point the soc_enum to .texts array items */
        max98088->eq_enum.texts = max98088->eq_texts;
-       max98088->eq_enum.max = max98088->eq_textcnt;
+       max98088->eq_enum.items = max98088->eq_textcnt;
 
        ret = snd_soc_add_codec_controls(codec, controls, ARRAY_SIZE(controls));
        if (ret != 0)
diff --git a/sound/soc/codecs/max98090.c b/sound/soc/codecs/max98090.c
index 9f714ea86613..f363de19be07 100644
--- a/sound/soc/codecs/max98090.c
+++ b/sound/soc/codecs/max98090.c
@@ -513,65 +513,75 @@ static const char *max98090_perf_pwr_text[] =
 static const char *max98090_pwr_perf_text[] =
 	{ "Low Power", "High Performance" };
 
-static const struct soc_enum max98090_vcmbandgap_enum =
-	SOC_ENUM_SINGLE(M98090_REG_BIAS_CONTROL, M98090_VCM_MODE_SHIFT,
-		ARRAY_SIZE(max98090_pwr_perf_text), max98090_pwr_perf_text);
+static SOC_ENUM_SINGLE_DECL(max98090_vcmbandgap_enum,
+			    M98090_REG_BIAS_CONTROL,
+			    M98090_VCM_MODE_SHIFT,
+			    max98090_pwr_perf_text);
 
 static const char *max98090_osr128_text[] = { "64*fs", "128*fs" };
 
-static const struct soc_enum max98090_osr128_enum =
-	SOC_ENUM_SINGLE(M98090_REG_ADC_CONTROL, M98090_OSR128_SHIFT,
-		ARRAY_SIZE(max98090_osr128_text), max98090_osr128_text);
+static SOC_ENUM_SINGLE_DECL(max98090_osr128_enum,
+			    M98090_REG_ADC_CONTROL,
+			    M98090_OSR128_SHIFT,
+			    max98090_osr128_text);
 
 static const char *max98090_mode_text[] = { "Voice", "Music" };
 
-static const struct soc_enum max98090_mode_enum =
-	SOC_ENUM_SINGLE(M98090_REG_FILTER_CONFIG, M98090_MODE_SHIFT,
-		ARRAY_SIZE(max98090_mode_text), max98090_mode_text);
+static SOC_ENUM_SINGLE_DECL(max98090_mode_enum,
+			    M98090_REG_FILTER_CONFIG,
+			    M98090_MODE_SHIFT,
+			    max98090_mode_text);
 
-static const struct soc_enum max98090_filter_dmic34mode_enum =
-	SOC_ENUM_SINGLE(M98090_REG_FILTER_CONFIG,
-		M98090_FLT_DMIC34MODE_SHIFT,
-		ARRAY_SIZE(max98090_mode_text), max98090_mode_text);
+static SOC_ENUM_SINGLE_DECL(max98090_filter_dmic34mode_enum,
+			    M98090_REG_FILTER_CONFIG,
+			    M98090_FLT_DMIC34MODE_SHIFT,
+			    max98090_mode_text);
 
 static const char *max98090_drcatk_text[] =
 	{ "0.5ms", "1ms", "5ms", "10ms", "25ms", "50ms", "100ms", "200ms" };
 
-static const struct soc_enum max98090_drcatk_enum =
-	SOC_ENUM_SINGLE(M98090_REG_DRC_TIMING, M98090_DRCATK_SHIFT,
-		ARRAY_SIZE(max98090_drcatk_text), max98090_drcatk_text);
+static SOC_ENUM_SINGLE_DECL(max98090_drcatk_enum,
+			    M98090_REG_DRC_TIMING,
+			    M98090_DRCATK_SHIFT,
+			    max98090_drcatk_text);
 
 static const char *max98090_drcrls_text[] =
 	{ "8s", "4s", "2s", "1s", "0.5s", "0.25s", "0.125s", "0.0625s" };
 
-static const struct soc_enum max98090_drcrls_enum =
-	SOC_ENUM_SINGLE(M98090_REG_DRC_TIMING, M98090_DRCRLS_SHIFT,
-		ARRAY_SIZE(max98090_drcrls_text), max98090_drcrls_text);
+static SOC_ENUM_SINGLE_DECL(max98090_drcrls_enum,
+			    M98090_REG_DRC_TIMING,
+			    M98090_DRCRLS_SHIFT,
+			    max98090_drcrls_text);
 
 static const char *max98090_alccmp_text[] =
 	{ "1:1", "1:1.5", "1:2", "1:4", "1:INF" };
 
-static const struct soc_enum max98090_alccmp_enum =
-	SOC_ENUM_SINGLE(M98090_REG_DRC_COMPRESSOR, M98090_DRCCMP_SHIFT,
-		ARRAY_SIZE(max98090_alccmp_text), max98090_alccmp_text);
+static SOC_ENUM_SINGLE_DECL(max98090_alccmp_enum,
+			    M98090_REG_DRC_COMPRESSOR,
+			    M98090_DRCCMP_SHIFT,
+			    max98090_alccmp_text);
 
 static const char *max98090_drcexp_text[] = { "1:1", "2:1", "3:1" };
 
-static const struct soc_enum max98090_drcexp_enum =
-	SOC_ENUM_SINGLE(M98090_REG_DRC_EXPANDER, M98090_DRCEXP_SHIFT,
-		ARRAY_SIZE(max98090_drcexp_text), max98090_drcexp_text);
+static SOC_ENUM_SINGLE_DECL(max98090_drcexp_enum,
+			    M98090_REG_DRC_EXPANDER,
+			    M98090_DRCEXP_SHIFT,
+			    max98090_drcexp_text);
 
-static const struct soc_enum max98090_dac_perfmode_enum =
-	SOC_ENUM_SINGLE(M98090_REG_DAC_CONTROL, M98090_PERFMODE_SHIFT,
-		ARRAY_SIZE(max98090_perf_pwr_text), max98090_perf_pwr_text);
+static SOC_ENUM_SINGLE_DECL(max98090_dac_perfmode_enum,
+			    M98090_REG_DAC_CONTROL,
+			    M98090_PERFMODE_SHIFT,
+			    max98090_perf_pwr_text);
 
-static const struct soc_enum max98090_dachp_enum =
-	SOC_ENUM_SINGLE(M98090_REG_DAC_CONTROL, M98090_DACHP_SHIFT,
-		ARRAY_SIZE(max98090_pwr_perf_text), max98090_pwr_perf_text);
+static SOC_ENUM_SINGLE_DECL(max98090_dachp_enum,
+			    M98090_REG_DAC_CONTROL,
+			    M98090_DACHP_SHIFT,
+			    max98090_pwr_perf_text);
 
-static const struct soc_enum max98090_adchp_enum =
-	SOC_ENUM_SINGLE(M98090_REG_ADC_CONTROL, M98090_ADCHP_SHIFT,
-		ARRAY_SIZE(max98090_pwr_perf_text), max98090_pwr_perf_text);
+static SOC_ENUM_SINGLE_DECL(max98090_adchp_enum,
+			    M98090_REG_ADC_CONTROL,
+			    M98090_ADCHP_SHIFT,
+			    max98090_pwr_perf_text);
 
 static const struct snd_kcontrol_new max98090_snd_controls[] = {
 	SOC_ENUM("MIC Bias VCM Bandgap", max98090_vcmbandgap_enum),
@@ -842,39 +852,42 @@ static int max98090_micinput_event(struct snd_soc_dapm_widget *w,
 
 static const char *mic1_mux_text[] = { "IN12", "IN56" };
 
-static const struct soc_enum mic1_mux_enum =
-	SOC_ENUM_SINGLE(M98090_REG_INPUT_MODE, M98090_EXTMIC1_SHIFT,
-		ARRAY_SIZE(mic1_mux_text), mic1_mux_text);
+static SOC_ENUM_SINGLE_DECL(mic1_mux_enum,
+			    M98090_REG_INPUT_MODE,
+			    M98090_EXTMIC1_SHIFT,
+			    mic1_mux_text);
 
 static const struct snd_kcontrol_new max98090_mic1_mux =
 	SOC_DAPM_ENUM("MIC1 Mux", mic1_mux_enum);
 
 static const char *mic2_mux_text[] = { "IN34", "IN56" };
 
-static const struct soc_enum mic2_mux_enum =
-	SOC_ENUM_SINGLE(M98090_REG_INPUT_MODE, M98090_EXTMIC2_SHIFT,
-		ARRAY_SIZE(mic2_mux_text), mic2_mux_text);
+static SOC_ENUM_SINGLE_DECL(mic2_mux_enum,
+			    M98090_REG_INPUT_MODE,
+			    M98090_EXTMIC2_SHIFT,
+			    mic2_mux_text);
 
 static const struct snd_kcontrol_new max98090_mic2_mux =
 	SOC_DAPM_ENUM("MIC2 Mux", mic2_mux_enum);
 
 static const char *dmic_mux_text[] = { "ADC", "DMIC" };
 
-static const struct soc_enum dmic_mux_enum =
-	SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(dmic_mux_text), dmic_mux_text);
+static SOC_ENUM_SINGLE_VIRT_DECL(dmic_mux_enum, dmic_mux_text);
 
 static const struct snd_kcontrol_new max98090_dmic_mux =
 	SOC_DAPM_ENUM_VIRT("DMIC Mux", dmic_mux_enum);
 
 static const char *max98090_micpre_text[] = { "Off", "On" };
 
-static const struct soc_enum max98090_pa1en_enum =
-	SOC_ENUM_SINGLE(M98090_REG_MIC1_INPUT_LEVEL, M98090_MIC_PA1EN_SHIFT,
-		ARRAY_SIZE(max98090_micpre_text), max98090_micpre_text);
+static SOC_ENUM_SINGLE_DECL(max98090_pa1en_enum,
+			    M98090_REG_MIC1_INPUT_LEVEL,
+			    M98090_MIC_PA1EN_SHIFT,
+			    max98090_micpre_text);
 
-static const struct soc_enum max98090_pa2en_enum =
-	SOC_ENUM_SINGLE(M98090_REG_MIC2_INPUT_LEVEL, M98090_MIC_PA2EN_SHIFT,
-		ARRAY_SIZE(max98090_micpre_text), max98090_micpre_text);
+static SOC_ENUM_SINGLE_DECL(max98090_pa2en_enum,
+			    M98090_REG_MIC2_INPUT_LEVEL,
+			    M98090_MIC_PA2EN_SHIFT,
+			    max98090_micpre_text);
 
 /* LINEA mixer switch */
 static const struct snd_kcontrol_new max98090_linea_mixer_controls[] = {
@@ -938,13 +951,15 @@ static const struct snd_kcontrol_new max98090_right_adc_mixer_controls[] = {
 
 static const char *lten_mux_text[] = { "Normal", "Loopthrough" };
 
-static const struct soc_enum ltenl_mux_enum =
-	SOC_ENUM_SINGLE(M98090_REG_IO_CONFIGURATION, M98090_LTEN_SHIFT,
-		ARRAY_SIZE(lten_mux_text), lten_mux_text);
+static SOC_ENUM_SINGLE_DECL(ltenl_mux_enum,
+			    M98090_REG_IO_CONFIGURATION,
+			    M98090_LTEN_SHIFT,
+			    lten_mux_text);
 
-static const struct soc_enum ltenr_mux_enum =
-	SOC_ENUM_SINGLE(M98090_REG_IO_CONFIGURATION, M98090_LTEN_SHIFT,
-		ARRAY_SIZE(lten_mux_text), lten_mux_text);
+static SOC_ENUM_SINGLE_DECL(ltenr_mux_enum,
+			    M98090_REG_IO_CONFIGURATION,
+			    M98090_LTEN_SHIFT,
+			    lten_mux_text);
 
 static const struct snd_kcontrol_new max98090_ltenl_mux =
 	SOC_DAPM_ENUM("LTENL Mux", ltenl_mux_enum);
@@ -954,13 +969,15 @@ static const struct snd_kcontrol_new max98090_ltenr_mux =
 
 static const char *lben_mux_text[] = { "Normal", "Loopback" };
 
-static const struct soc_enum lbenl_mux_enum =
-	SOC_ENUM_SINGLE(M98090_REG_IO_CONFIGURATION, M98090_LBEN_SHIFT,
-		ARRAY_SIZE(lben_mux_text), lben_mux_text);
+static SOC_ENUM_SINGLE_DECL(lbenl_mux_enum,
+			    M98090_REG_IO_CONFIGURATION,
+			    M98090_LBEN_SHIFT,
+			    lben_mux_text);
 
-static const struct soc_enum lbenr_mux_enum =
-	SOC_ENUM_SINGLE(M98090_REG_IO_CONFIGURATION, M98090_LBEN_SHIFT,
-		ARRAY_SIZE(lben_mux_text), lben_mux_text);
+static SOC_ENUM_SINGLE_DECL(lbenr_mux_enum,
+			    M98090_REG_IO_CONFIGURATION,
+			    M98090_LBEN_SHIFT,
+			    lben_mux_text);
 
 static const struct snd_kcontrol_new max98090_lbenl_mux =
 	SOC_DAPM_ENUM("LBENL Mux", lbenl_mux_enum);
@@ -972,13 +989,15 @@ static const char *stenl_mux_text[] = { "Normal", "Sidetone Left" };
 
 static const char *stenr_mux_text[] = { "Normal", "Sidetone Right" };
 
-static const struct soc_enum stenl_mux_enum =
-	SOC_ENUM_SINGLE(M98090_REG_ADC_SIDETONE, M98090_DSTSL_SHIFT,
-		ARRAY_SIZE(stenl_mux_text), stenl_mux_text);
+static SOC_ENUM_SINGLE_DECL(stenl_mux_enum,
+			    M98090_REG_ADC_SIDETONE,
+			    M98090_DSTSL_SHIFT,
+			    stenl_mux_text);
 
-static const struct soc_enum stenr_mux_enum =
-	SOC_ENUM_SINGLE(M98090_REG_ADC_SIDETONE, M98090_DSTSR_SHIFT,
-		ARRAY_SIZE(stenr_mux_text), stenr_mux_text);
+static SOC_ENUM_SINGLE_DECL(stenr_mux_enum,
+			    M98090_REG_ADC_SIDETONE,
+			    M98090_DSTSR_SHIFT,
+			    stenr_mux_text);
 
 static const struct snd_kcontrol_new max98090_stenl_mux =
 	SOC_DAPM_ENUM("STENL Mux", stenl_mux_enum);
@@ -1086,9 +1105,10 @@ static const struct snd_kcontrol_new max98090_right_rcv_mixer_controls[] = {
 
 static const char *linmod_mux_text[] = { "Left Only", "Left and Right" };
 
-static const struct soc_enum linmod_mux_enum =
-	SOC_ENUM_SINGLE(M98090_REG_LOUTR_MIXER, M98090_LINMOD_SHIFT,
-		ARRAY_SIZE(linmod_mux_text), linmod_mux_text);
+static SOC_ENUM_SINGLE_DECL(linmod_mux_enum,
+			    M98090_REG_LOUTR_MIXER,
+			    M98090_LINMOD_SHIFT,
+			    linmod_mux_text);
 
 static const struct snd_kcontrol_new max98090_linmod_mux =
 	SOC_DAPM_ENUM("LINMOD Mux", linmod_mux_enum);
@@ -1098,16 +1118,18 @@ static const char *mixhpsel_mux_text[] = { "DAC Only", "HP Mixer" };
 /*
  * This is a mux as it selects the HP output, but to DAPM it is a Mixer enable
  */
-static const struct soc_enum mixhplsel_mux_enum =
-	SOC_ENUM_SINGLE(M98090_REG_HP_CONTROL, M98090_MIXHPLSEL_SHIFT,
-		ARRAY_SIZE(mixhpsel_mux_text), mixhpsel_mux_text);
+static SOC_ENUM_SINGLE_DECL(mixhplsel_mux_enum,
+			    M98090_REG_HP_CONTROL,
+			    M98090_MIXHPLSEL_SHIFT,
+			    mixhpsel_mux_text);
 
 static const struct snd_kcontrol_new max98090_mixhplsel_mux =
 	SOC_DAPM_ENUM("MIXHPLSEL Mux", mixhplsel_mux_enum);
 
-static const struct soc_enum mixhprsel_mux_enum =
-	SOC_ENUM_SINGLE(M98090_REG_HP_CONTROL, M98090_MIXHPRSEL_SHIFT,
-		ARRAY_SIZE(mixhpsel_mux_text), mixhpsel_mux_text);
+static SOC_ENUM_SINGLE_DECL(mixhprsel_mux_enum,
+			    M98090_REG_HP_CONTROL,
+			    M98090_MIXHPRSEL_SHIFT,
+			    mixhpsel_mux_text);
 
 static const struct snd_kcontrol_new max98090_mixhprsel_mux =
 	SOC_DAPM_ENUM("MIXHPRSEL Mux", mixhprsel_mux_enum);
diff --git a/sound/soc/codecs/max98095.c b/sound/soc/codecs/max98095.c
index 3ba1170ebb53..5bce9cde4a6d 100644
--- a/sound/soc/codecs/max98095.c
+++ b/sound/soc/codecs/max98095.c
@@ -1861,7 +1861,7 @@ static void max98095_handle_eq_pdata(struct snd_soc_codec *codec)
 
 	/* Now point the soc_enum to .texts array items */
 	max98095->eq_enum.texts = max98095->eq_texts;
-	max98095->eq_enum.max = max98095->eq_textcnt;
+	max98095->eq_enum.items = max98095->eq_textcnt;
 
 	ret = snd_soc_add_codec_controls(codec, controls, ARRAY_SIZE(controls));
 	if (ret != 0)
@@ -2016,7 +2016,7 @@ static void max98095_handle_bq_pdata(struct snd_soc_codec *codec)
 
 	/* Now point the soc_enum to .texts array items */
 	max98095->bq_enum.texts = max98095->bq_texts;
-	max98095->bq_enum.max = max98095->bq_textcnt;
+	max98095->bq_enum.items = max98095->bq_textcnt;
 
 	ret = snd_soc_add_codec_controls(codec, controls, ARRAY_SIZE(controls));
 	if (ret != 0)
diff --git a/sound/soc/codecs/mc13783.c b/sound/soc/codecs/mc13783.c
index 582c2bbd42cb..ec89b8f90a64 100644
--- a/sound/soc/codecs/mc13783.c
+++ b/sound/soc/codecs/mc13783.c
@@ -408,8 +408,7 @@ static const char * const adcl_enum_text[] = {
 	"MC1L", "RXINL",
 };
 
-static const struct soc_enum adcl_enum =
-	SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(adcl_enum_text), adcl_enum_text);
+static SOC_ENUM_SINGLE_VIRT_DECL(adcl_enum, adcl_enum_text);
 
 static const struct snd_kcontrol_new left_input_mux =
 	SOC_DAPM_ENUM_VIRT("Route", adcl_enum);
@@ -418,8 +417,7 @@ static const char * const adcr_enum_text[] = {
 	"MC1R", "MC2", "RXINR", "TXIN",
 };
 
-static const struct soc_enum adcr_enum =
-	SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(adcr_enum_text), adcr_enum_text);
+static SOC_ENUM_SINGLE_VIRT_DECL(adcr_enum, adcr_enum_text);
 
 static const struct snd_kcontrol_new right_input_mux =
 	SOC_DAPM_ENUM_VIRT("Route", adcr_enum);
@@ -430,8 +428,8 @@ static const struct snd_kcontrol_new samp_ctl =
 static const char * const speaker_amp_source_text[] = {
 	"CODEC", "Right"
 };
-static const SOC_ENUM_SINGLE_DECL(speaker_amp_source, MC13783_AUDIO_RX0, 4,
-				  speaker_amp_source_text);
+static SOC_ENUM_SINGLE_DECL(speaker_amp_source, MC13783_AUDIO_RX0, 4,
+			    speaker_amp_source_text);
 static const struct snd_kcontrol_new speaker_amp_source_mux =
 	SOC_DAPM_ENUM("Speaker Amp Source MUX", speaker_amp_source);
 
@@ -439,8 +437,8 @@ static const char * const headset_amp_source_text[] = {
 	"CODEC", "Mixer"
 };
 
-static const SOC_ENUM_SINGLE_DECL(headset_amp_source, MC13783_AUDIO_RX0, 11,
-				  headset_amp_source_text);
+static SOC_ENUM_SINGLE_DECL(headset_amp_source, MC13783_AUDIO_RX0, 11,
+			    headset_amp_source_text);
 static const struct snd_kcontrol_new headset_amp_source_mux =
 	SOC_DAPM_ENUM("Headset Amp Source MUX", headset_amp_source);
 
@@ -580,9 +578,9 @@ static struct snd_soc_dapm_route mc13783_routes[] = {
 static const char * const mc13783_3d_mixer[] = {"Stereo", "Phase Mix",
 						"Mono", "Mono Mix"};
 
-static const struct soc_enum mc13783_enum_3d_mixer =
-	SOC_ENUM_SINGLE(MC13783_AUDIO_RX1, 16, ARRAY_SIZE(mc13783_3d_mixer),
-			mc13783_3d_mixer);
+static SOC_ENUM_SINGLE_DECL(mc13783_enum_3d_mixer,
+			    MC13783_AUDIO_RX1, 16,
+			    mc13783_3d_mixer);
 
 static struct snd_kcontrol_new mc13783_control_list[] = {
 	SOC_SINGLE("Loudspeaker enable", MC13783_AUDIO_RX0, 5, 1, 0),
diff --git a/sound/soc/codecs/ml26124.c b/sound/soc/codecs/ml26124.c
index 185fa3bc3052..577fb8776ce7 100644
--- a/sound/soc/codecs/ml26124.c
+++ b/sound/soc/codecs/ml26124.c
@@ -73,11 +73,11 @@ static const DECLARE_TLV_DB_SCALE(ngth, -7650, 150, 0);
 static const char * const ml26124_companding[] = {"16bit PCM", "u-law",
 						  "A-law"};
 
-static const struct soc_enum ml26124_adc_companding_enum
-	= SOC_ENUM_SINGLE(ML26124_SAI_TRANS_CTL, 6, 3, ml26124_companding);
+static SOC_ENUM_SINGLE_DECL(ml26124_adc_companding_enum,
+			    ML26124_SAI_TRANS_CTL, 6, ml26124_companding);
 
-static const struct soc_enum ml26124_dac_companding_enum
-	= SOC_ENUM_SINGLE(ML26124_SAI_RCV_CTL, 6, 3, ml26124_companding);
+static SOC_ENUM_SINGLE_DECL(ml26124_dac_companding_enum,
+			    ML26124_SAI_RCV_CTL, 6, ml26124_companding);
 
 static const struct snd_kcontrol_new ml26124_snd_controls[] = {
 	SOC_SINGLE_TLV("Capture Digital Volume", ML26124_RECORD_DIG_VOL, 0,
@@ -136,8 +136,8 @@ static const struct snd_kcontrol_new ml26124_output_mixer_controls[] = {
 static const char * const ml26124_input_select[] = {"Analog MIC SingleEnded in",
 				"Digital MIC in", "Analog MIC Differential in"};
 
-static const struct soc_enum ml26124_insel_enum =
-	SOC_ENUM_SINGLE(ML26124_MIC_IF_CTL, 0, 3, ml26124_input_select);
+static SOC_ENUM_SINGLE_DECL(ml26124_insel_enum,
+			    ML26124_MIC_IF_CTL, 0, ml26124_input_select);
 
 static const struct snd_kcontrol_new ml26124_input_mux_controls =
 	SOC_DAPM_ENUM("Input Select", ml26124_insel_enum);
diff --git a/sound/soc/codecs/pcm1681.c b/sound/soc/codecs/pcm1681.c
index 73f9c3630e2c..e427544183d7 100644
--- a/sound/soc/codecs/pcm1681.c
+++ b/sound/soc/codecs/pcm1681.c
@@ -172,16 +172,21 @@ static int pcm1681_hw_params(struct snd_pcm_substream *substream,
 	struct snd_soc_codec *codec = dai->codec;
 	struct pcm1681_private *priv = snd_soc_codec_get_drvdata(codec);
 	int val = 0, ret;
-	int pcm_format = params_format(params);
 
 	priv->rate = params_rate(params);
 
 	switch (priv->format & SND_SOC_DAIFMT_FORMAT_MASK) {
 	case SND_SOC_DAIFMT_RIGHT_J:
-		if (pcm_format == SNDRV_PCM_FORMAT_S24_LE)
-			val = 0x00;
-		else if (pcm_format == SNDRV_PCM_FORMAT_S16_LE)
-			val = 0x03;
+		switch (params_width(params)) {
+		case 24:
+			val = 0;
+			break;
+		case 16:
+			val = 3;
+			break;
+		default:
+			return -EINVAL;
+		}
 		break;
 	case SND_SOC_DAIFMT_I2S:
 		val = 0x04;
diff --git a/sound/soc/codecs/pcm1792a.c b/sound/soc/codecs/pcm1792a.c
index 7146653a8e16..3a80ba4452df 100644
--- a/sound/soc/codecs/pcm1792a.c
+++ b/sound/soc/codecs/pcm1792a.c
@@ -107,24 +107,35 @@ static int pcm1792a_hw_params(struct snd_pcm_substream *substream,
 	struct snd_soc_codec *codec = dai->codec;
 	struct pcm1792a_private *priv = snd_soc_codec_get_drvdata(codec);
 	int val = 0, ret;
-	int pcm_format = params_format(params);
 
 	priv->rate = params_rate(params);
 
 	switch (priv->format & SND_SOC_DAIFMT_FORMAT_MASK) {
 	case SND_SOC_DAIFMT_RIGHT_J:
-		if (pcm_format == SNDRV_PCM_FORMAT_S24_LE ||
-		    pcm_format == SNDRV_PCM_FORMAT_S32_LE)
-			val = 0x02;
-		else if (pcm_format == SNDRV_PCM_FORMAT_S16_LE)
-			val = 0x00;
+		switch (params_width(params)) {
+		case 24:
+		case 32:
+			val = 2;
+			break;
+		case 16:
+			val = 0;
+			break;
+		default:
+			return -EINVAL;
+		}
 		break;
 	case SND_SOC_DAIFMT_I2S:
-		if (pcm_format == SNDRV_PCM_FORMAT_S24_LE ||
-		    pcm_format == SNDRV_PCM_FORMAT_S32_LE)
-			val = 0x05;
-		else if (pcm_format == SNDRV_PCM_FORMAT_S16_LE)
-			val = 0x04;
+		switch (params_width(params)) {
+		case 24:
+		case 32:
+			val = 5;
+			break;
+		case 16:
+			val = 4;
+			break;
+		default:
+			return -EINVAL;
+		}
 		break;
 	default:
 		dev_err(codec->dev, "Invalid DAI format\n");
diff --git a/sound/soc/codecs/pcm512x-i2c.c b/sound/soc/codecs/pcm512x-i2c.c
new file mode 100644
index 000000000000..4d62230bd378
--- /dev/null
+++ b/sound/soc/codecs/pcm512x-i2c.c
@@ -0,0 +1,71 @@
+/*
+ * Driver for the PCM512x CODECs
+ *
+ * Author:	Mark Brown <broonie@linaro.org>
+ *		Copyright 2014 Linaro Ltd
+ *
+ * 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.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/i2c.h>
+
+#include "pcm512x.h"
+
+static int pcm512x_i2c_probe(struct i2c_client *i2c,
+			     const struct i2c_device_id *id)
+{
+	struct regmap *regmap;
+
+	regmap = devm_regmap_init_i2c(i2c, &pcm512x_regmap);
+	if (IS_ERR(regmap))
+		return PTR_ERR(regmap);
+
+	return pcm512x_probe(&i2c->dev, regmap);
+}
+
+static int pcm512x_i2c_remove(struct i2c_client *i2c)
+{
+	pcm512x_remove(&i2c->dev);
+	return 0;
+}
+
+static const struct i2c_device_id pcm512x_i2c_id[] = {
+	{ "pcm5121", },
+	{ "pcm5122", },
+	{ }
+};
+MODULE_DEVICE_TABLE(i2c, pcm512x_i2c_id);
+
+static const struct of_device_id pcm512x_of_match[] = {
+	{ .compatible = "ti,pcm5121", },
+	{ .compatible = "ti,pcm5122", },
+	{ }
+};
+MODULE_DEVICE_TABLE(of, pcm512x_of_match);
+
+static struct i2c_driver pcm512x_i2c_driver = {
+	.probe 		= pcm512x_i2c_probe,
+	.remove 	= pcm512x_i2c_remove,
+	.id_table	= pcm512x_i2c_id,
+	.driver		= {
+		.name	= "pcm512x",
+		.owner	= THIS_MODULE,
+		.of_match_table = pcm512x_of_match,
+		.pm     = &pcm512x_pm_ops,
+	},
+};
+
+module_i2c_driver(pcm512x_i2c_driver);
+
+MODULE_DESCRIPTION("ASoC PCM512x codec driver - I2C");
+MODULE_AUTHOR("Mark Brown <broonie@linaro.org>");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/pcm512x-spi.c b/sound/soc/codecs/pcm512x-spi.c
new file mode 100644
index 000000000000..f297058c0038
--- /dev/null
+++ b/sound/soc/codecs/pcm512x-spi.c
@@ -0,0 +1,69 @@
+/*
+ * Driver for the PCM512x CODECs
+ *
+ * Author:	Mark Brown <broonie@linaro.org>
+ *		Copyright 2014 Linaro Ltd
+ *
+ * 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.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/spi/spi.h>
+
+#include "pcm512x.h"
+
+static int pcm512x_spi_probe(struct spi_device *spi)
+{
+	struct regmap *regmap;
+	int ret;
+
+	regmap = devm_regmap_init_spi(spi, &pcm512x_regmap);
+	if (IS_ERR(regmap)) {
+		ret = PTR_ERR(regmap);
+		return ret;
+	}
+
+	return pcm512x_probe(&spi->dev, regmap);
+}
+
+static int pcm512x_spi_remove(struct spi_device *spi)
+{
+	pcm512x_remove(&spi->dev);
+	return 0;
+}
+
+static const struct spi_device_id pcm512x_spi_id[] = {
+	{ "pcm5121", },
+	{ "pcm5122", },
+	{ },
+};
+MODULE_DEVICE_TABLE(spi, pcm512x_spi_id);
+
+static const struct of_device_id pcm512x_of_match[] = {
+	{ .compatible = "ti,pcm5121", },
+	{ .compatible = "ti,pcm5122", },
+	{ }
+};
+MODULE_DEVICE_TABLE(of, pcm512x_of_match);
+
+static struct spi_driver pcm512x_spi_driver = {
+	.probe		= pcm512x_spi_probe,
+	.remove		= pcm512x_spi_remove,
+	.id_table	= pcm512x_spi_id,
+	.driver = {
+		.name	= "pcm512x",
+		.owner	= THIS_MODULE,
+		.of_match_table = pcm512x_of_match,
+		.pm     = &pcm512x_pm_ops,
+	},
+};
+
+module_spi_driver(pcm512x_spi_driver);
diff --git a/sound/soc/codecs/pcm512x.c b/sound/soc/codecs/pcm512x.c
new file mode 100644
index 000000000000..4b4c0c7bb918
--- /dev/null
+++ b/sound/soc/codecs/pcm512x.c
@@ -0,0 +1,589 @@
+/*
+ * Driver for the PCM512x CODECs
+ *
+ * Author:	Mark Brown <broonie@linaro.org>
+ *		Copyright 2014 Linaro Ltd
+ *
+ * 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.
+ */
+
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/clk.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/tlv.h>
+
+#include "pcm512x.h"
+
+#define PCM512x_NUM_SUPPLIES 3
+static const char * const pcm512x_supply_names[PCM512x_NUM_SUPPLIES] = {
+	"AVDD",
+	"DVDD",
+	"CPVDD",
+};
+
+struct pcm512x_priv {
+	struct regmap *regmap;
+	struct clk *sclk;
+	struct regulator_bulk_data supplies[PCM512x_NUM_SUPPLIES];
+	struct notifier_block supply_nb[PCM512x_NUM_SUPPLIES];
+};
+
+/*
+ * We can't use the same notifier block for more than one supply and
+ * there's no way I can see to get from a callback to the caller
+ * except container_of().
+ */
+#define PCM512x_REGULATOR_EVENT(n) \
+static int pcm512x_regulator_event_##n(struct notifier_block *nb, \
+				      unsigned long event, void *data)    \
+{ \
+	struct pcm512x_priv *pcm512x = container_of(nb, struct pcm512x_priv, \
+						    supply_nb[n]); \
+	if (event & REGULATOR_EVENT_DISABLE) { \
+		regcache_mark_dirty(pcm512x->regmap);	\
+		regcache_cache_only(pcm512x->regmap, true);	\
+	} \
+	return 0; \
+}
+
+PCM512x_REGULATOR_EVENT(0)
+PCM512x_REGULATOR_EVENT(1)
+PCM512x_REGULATOR_EVENT(2)
+
+static const struct reg_default pcm512x_reg_defaults[] = {
+	{ PCM512x_RESET,             0x00 },
+	{ PCM512x_POWER,             0x00 },
+	{ PCM512x_MUTE,              0x00 },
+	{ PCM512x_DSP,               0x00 },
+	{ PCM512x_PLL_REF,           0x00 },
+	{ PCM512x_DAC_ROUTING,       0x11 },
+	{ PCM512x_DSP_PROGRAM,       0x01 },
+	{ PCM512x_CLKDET,            0x00 },
+	{ PCM512x_AUTO_MUTE,         0x00 },
+	{ PCM512x_ERROR_DETECT,      0x00 },
+	{ PCM512x_DIGITAL_VOLUME_1,  0x00 },
+	{ PCM512x_DIGITAL_VOLUME_2,  0x30 },
+	{ PCM512x_DIGITAL_VOLUME_3,  0x30 },
+	{ PCM512x_DIGITAL_MUTE_1,    0x22 },
+	{ PCM512x_DIGITAL_MUTE_2,    0x00 },
+	{ PCM512x_DIGITAL_MUTE_3,    0x07 },
+	{ PCM512x_OUTPUT_AMPLITUDE,  0x00 },
+	{ PCM512x_ANALOG_GAIN_CTRL,  0x00 },
+	{ PCM512x_UNDERVOLTAGE_PROT, 0x00 },
+	{ PCM512x_ANALOG_MUTE_CTRL,  0x00 },
+	{ PCM512x_ANALOG_GAIN_BOOST, 0x00 },
+	{ PCM512x_VCOM_CTRL_1,       0x00 },
+	{ PCM512x_VCOM_CTRL_2,       0x01 },
+};
+
+static bool pcm512x_readable(struct device *dev, unsigned int reg)
+{
+	switch (reg) {
+	case PCM512x_RESET:
+	case PCM512x_POWER:
+	case PCM512x_MUTE:
+	case PCM512x_PLL_EN:
+	case PCM512x_SPI_MISO_FUNCTION:
+	case PCM512x_DSP:
+	case PCM512x_GPIO_EN:
+	case PCM512x_BCLK_LRCLK_CFG:
+	case PCM512x_DSP_GPIO_INPUT:
+	case PCM512x_MASTER_MODE:
+	case PCM512x_PLL_REF:
+	case PCM512x_PLL_COEFF_0:
+	case PCM512x_PLL_COEFF_1:
+	case PCM512x_PLL_COEFF_2:
+	case PCM512x_PLL_COEFF_3:
+	case PCM512x_PLL_COEFF_4:
+	case PCM512x_DSP_CLKDIV:
+	case PCM512x_DAC_CLKDIV:
+	case PCM512x_NCP_CLKDIV:
+	case PCM512x_OSR_CLKDIV:
+	case PCM512x_MASTER_CLKDIV_1:
+	case PCM512x_MASTER_CLKDIV_2:
+	case PCM512x_FS_SPEED_MODE:
+	case PCM512x_IDAC_1:
+	case PCM512x_IDAC_2:
+	case PCM512x_ERROR_DETECT:
+	case PCM512x_I2S_1:
+	case PCM512x_I2S_2:
+	case PCM512x_DAC_ROUTING:
+	case PCM512x_DSP_PROGRAM:
+	case PCM512x_CLKDET:
+	case PCM512x_AUTO_MUTE:
+	case PCM512x_DIGITAL_VOLUME_1:
+	case PCM512x_DIGITAL_VOLUME_2:
+	case PCM512x_DIGITAL_VOLUME_3:
+	case PCM512x_DIGITAL_MUTE_1:
+	case PCM512x_DIGITAL_MUTE_2:
+	case PCM512x_DIGITAL_MUTE_3:
+	case PCM512x_GPIO_OUTPUT_1:
+	case PCM512x_GPIO_OUTPUT_2:
+	case PCM512x_GPIO_OUTPUT_3:
+	case PCM512x_GPIO_OUTPUT_4:
+	case PCM512x_GPIO_OUTPUT_5:
+	case PCM512x_GPIO_OUTPUT_6:
+	case PCM512x_GPIO_CONTROL_1:
+	case PCM512x_GPIO_CONTROL_2:
+	case PCM512x_OVERFLOW:
+	case PCM512x_RATE_DET_1:
+	case PCM512x_RATE_DET_2:
+	case PCM512x_RATE_DET_3:
+	case PCM512x_RATE_DET_4:
+	case PCM512x_ANALOG_MUTE_DET:
+	case PCM512x_GPIN:
+	case PCM512x_DIGITAL_MUTE_DET:
+	case PCM512x_OUTPUT_AMPLITUDE:
+	case PCM512x_ANALOG_GAIN_CTRL:
+	case PCM512x_UNDERVOLTAGE_PROT:
+	case PCM512x_ANALOG_MUTE_CTRL:
+	case PCM512x_ANALOG_GAIN_BOOST:
+	case PCM512x_VCOM_CTRL_1:
+	case PCM512x_VCOM_CTRL_2:
+	case PCM512x_CRAM_CTRL:
+		return true;
+	default:
+		/* There are 256 raw register addresses */
+		return reg < 0xff;
+	}
+}
+
+static bool pcm512x_volatile(struct device *dev, unsigned int reg)
+{
+	switch (reg) {
+	case PCM512x_PLL_EN:
+	case PCM512x_OVERFLOW:
+	case PCM512x_RATE_DET_1:
+	case PCM512x_RATE_DET_2:
+	case PCM512x_RATE_DET_3:
+	case PCM512x_RATE_DET_4:
+	case PCM512x_ANALOG_MUTE_DET:
+	case PCM512x_GPIN:
+	case PCM512x_DIGITAL_MUTE_DET:
+	case PCM512x_CRAM_CTRL:
+		return true;
+	default:
+		/* There are 256 raw register addresses */
+		return reg < 0xff;
+	}
+}
+
+static const DECLARE_TLV_DB_SCALE(digital_tlv, -10350, 50, 1);
+static const DECLARE_TLV_DB_SCALE(analog_tlv, -600, 600, 0);
+static const DECLARE_TLV_DB_SCALE(boost_tlv, 0, 80, 0);
+
+static const char * const pcm512x_dsp_program_texts[] = {
+	"FIR interpolation with de-emphasis",
+	"Low latency IIR with de-emphasis",
+	"Fixed process flow",
+	"High attenuation with de-emphasis",
+	"Ringing-less low latency FIR",
+};
+
+static const unsigned int pcm512x_dsp_program_values[] = {
+	1,
+	2,
+	3,
+	5,
+	7,
+};
+
+static SOC_VALUE_ENUM_SINGLE_DECL(pcm512x_dsp_program,
+				  PCM512x_DSP_PROGRAM, 0, 0x1f,
+				  pcm512x_dsp_program_texts,
+				  pcm512x_dsp_program_values);
+
+static const char * const pcm512x_clk_missing_text[] = {
+	"1s", "2s", "3s", "4s", "5s", "6s", "7s", "8s"
+};
+
+static const struct soc_enum pcm512x_clk_missing =
+	SOC_ENUM_SINGLE(PCM512x_CLKDET, 0,  8, pcm512x_clk_missing_text);
+
+static const char * const pcm512x_autom_text[] = {
+	"21ms", "106ms", "213ms", "533ms", "1.07s", "2.13s", "5.33s", "10.66s"
+};
+
+static const struct soc_enum pcm512x_autom_l =
+	SOC_ENUM_SINGLE(PCM512x_AUTO_MUTE, PCM512x_ATML_SHIFT, 8,
+			pcm512x_autom_text);
+
+static const struct soc_enum pcm512x_autom_r =
+	SOC_ENUM_SINGLE(PCM512x_AUTO_MUTE, PCM512x_ATMR_SHIFT, 8,
+			pcm512x_autom_text);
+
+static const char * const pcm512x_ramp_rate_text[] = {
+	"1 sample/update", "2 samples/update", "4 samples/update",
+	"Immediate"
+};
+
+static const struct soc_enum pcm512x_vndf =
+	SOC_ENUM_SINGLE(PCM512x_DIGITAL_MUTE_1, PCM512x_VNDF_SHIFT, 4,
+			pcm512x_ramp_rate_text);
+
+static const struct soc_enum pcm512x_vnuf =
+	SOC_ENUM_SINGLE(PCM512x_DIGITAL_MUTE_1, PCM512x_VNUF_SHIFT, 4,
+			pcm512x_ramp_rate_text);
+
+static const struct soc_enum pcm512x_vedf =
+	SOC_ENUM_SINGLE(PCM512x_DIGITAL_MUTE_2, PCM512x_VEDF_SHIFT, 4,
+			pcm512x_ramp_rate_text);
+
+static const char * const pcm512x_ramp_step_text[] = {
+	"4dB/step", "2dB/step", "1dB/step", "0.5dB/step"
+};
+
+static const struct soc_enum pcm512x_vnds =
+	SOC_ENUM_SINGLE(PCM512x_DIGITAL_MUTE_1, PCM512x_VNDS_SHIFT, 4,
+			pcm512x_ramp_step_text);
+
+static const struct soc_enum pcm512x_vnus =
+	SOC_ENUM_SINGLE(PCM512x_DIGITAL_MUTE_1, PCM512x_VNUS_SHIFT, 4,
+			pcm512x_ramp_step_text);
+
+static const struct soc_enum pcm512x_veds =
+	SOC_ENUM_SINGLE(PCM512x_DIGITAL_MUTE_2, PCM512x_VEDS_SHIFT, 4,
+			pcm512x_ramp_step_text);
+
+static const struct snd_kcontrol_new pcm512x_controls[] = {
+SOC_DOUBLE_R_TLV("Playback Digital Volume", PCM512x_DIGITAL_VOLUME_2,
+		 PCM512x_DIGITAL_VOLUME_3, 0, 255, 1, digital_tlv),
+SOC_DOUBLE_TLV("Playback Volume", PCM512x_ANALOG_GAIN_CTRL,
+	       PCM512x_LAGN_SHIFT, PCM512x_RAGN_SHIFT, 1, 1, analog_tlv),
+SOC_DOUBLE_TLV("Playback Boost Volume", PCM512x_ANALOG_GAIN_BOOST,
+	       PCM512x_AGBL_SHIFT, PCM512x_AGBR_SHIFT, 1, 0, boost_tlv),
+SOC_DOUBLE("Playback Digital Switch", PCM512x_MUTE, PCM512x_RQML_SHIFT,
+	   PCM512x_RQMR_SHIFT, 1, 1),
+
+SOC_SINGLE("Deemphasis Switch", PCM512x_DSP, PCM512x_DEMP_SHIFT, 1, 1),
+SOC_VALUE_ENUM("DSP Program", pcm512x_dsp_program),
+
+SOC_ENUM("Clock Missing Period", pcm512x_clk_missing),
+SOC_ENUM("Auto Mute Time Left", pcm512x_autom_l),
+SOC_ENUM("Auto Mute Time Right", pcm512x_autom_r),
+SOC_SINGLE("Auto Mute Mono Switch", PCM512x_DIGITAL_MUTE_3,
+	   PCM512x_ACTL_SHIFT, 1, 0),
+SOC_DOUBLE("Auto Mute Switch", PCM512x_DIGITAL_MUTE_3, PCM512x_AMLE_SHIFT,
+	   PCM512x_AMLR_SHIFT, 1, 0),
+
+SOC_ENUM("Volume Ramp Down Rate", pcm512x_vndf),
+SOC_ENUM("Volume Ramp Down Step", pcm512x_vnds),
+SOC_ENUM("Volume Ramp Up Rate", pcm512x_vnuf),
+SOC_ENUM("Volume Ramp Up Step", pcm512x_vnus),
+SOC_ENUM("Volume Ramp Down Emergency Rate", pcm512x_vedf),
+SOC_ENUM("Volume Ramp Down Emergency Step", pcm512x_veds),
+};
+
+static const struct snd_soc_dapm_widget pcm512x_dapm_widgets[] = {
+SND_SOC_DAPM_DAC("DACL", NULL, SND_SOC_NOPM, 0, 0),
+SND_SOC_DAPM_DAC("DACR", NULL, SND_SOC_NOPM, 0, 0),
+
+SND_SOC_DAPM_OUTPUT("OUTL"),
+SND_SOC_DAPM_OUTPUT("OUTR"),
+};
+
+static const struct snd_soc_dapm_route pcm512x_dapm_routes[] = {
+	{ "DACL", NULL, "Playback" },
+	{ "DACR", NULL, "Playback" },
+
+	{ "OUTL", NULL, "DACL" },
+	{ "OUTR", NULL, "DACR" },
+};
+
+static int pcm512x_set_bias_level(struct snd_soc_codec *codec,
+				  enum snd_soc_bias_level level)
+{
+	struct pcm512x_priv *pcm512x = dev_get_drvdata(codec->dev);
+	int ret;
+
+	switch (level) {
+	case SND_SOC_BIAS_ON:
+	case SND_SOC_BIAS_PREPARE:
+		break;
+
+	case SND_SOC_BIAS_STANDBY:
+		ret = regmap_update_bits(pcm512x->regmap, PCM512x_POWER,
+					 PCM512x_RQST, 0);
+		if (ret != 0) {
+			dev_err(codec->dev, "Failed to remove standby: %d\n",
+				ret);
+			return ret;
+		}
+		break;
+
+	case SND_SOC_BIAS_OFF:
+		ret = regmap_update_bits(pcm512x->regmap, PCM512x_POWER,
+					 PCM512x_RQST, PCM512x_RQST);
+		if (ret != 0) {
+			dev_err(codec->dev, "Failed to request standby: %d\n",
+				ret);
+			return ret;
+		}
+		break;
+	}
+
+	codec->dapm.bias_level = level;
+
+	return 0;
+}
+
+static struct snd_soc_dai_driver pcm512x_dai = {
+	.name = "pcm512x-hifi",
+	.playback = {
+		.stream_name = "Playback",
+		.channels_min = 2,
+		.channels_max = 2,
+		.rates = SNDRV_PCM_RATE_8000_192000,
+		.formats = SNDRV_PCM_FMTBIT_S16_LE |
+			   SNDRV_PCM_FMTBIT_S24_LE |
+			   SNDRV_PCM_FMTBIT_S32_LE
+	},
+};
+
+static struct snd_soc_codec_driver pcm512x_codec_driver = {
+	.set_bias_level = pcm512x_set_bias_level,
+	.idle_bias_off = true,
+
+	.controls = pcm512x_controls,
+	.num_controls = ARRAY_SIZE(pcm512x_controls),
+	.dapm_widgets = pcm512x_dapm_widgets,
+	.num_dapm_widgets = ARRAY_SIZE(pcm512x_dapm_widgets),
+	.dapm_routes = pcm512x_dapm_routes,
+	.num_dapm_routes = ARRAY_SIZE(pcm512x_dapm_routes),
+};
+
+static const struct regmap_range_cfg pcm512x_range = {
+	.name = "Pages", .range_min = PCM512x_VIRT_BASE,
+	.range_max = PCM512x_MAX_REGISTER,
+	.selector_reg = PCM512x_PAGE,
+	.selector_mask = 0xff,
+	.window_start = 0, .window_len = 0x100,
+};
+
+const struct regmap_config pcm512x_regmap = {
+	.reg_bits = 8,
+	.val_bits = 8,
+
+	.readable_reg = pcm512x_readable,
+	.volatile_reg = pcm512x_volatile,
+
+	.ranges = &pcm512x_range,
+	.num_ranges = 1,
+
+	.max_register = PCM512x_MAX_REGISTER,
+	.reg_defaults = pcm512x_reg_defaults,
+	.num_reg_defaults = ARRAY_SIZE(pcm512x_reg_defaults),
+	.cache_type = REGCACHE_RBTREE,
+};
+EXPORT_SYMBOL_GPL(pcm512x_regmap);
+
+int pcm512x_probe(struct device *dev, struct regmap *regmap)
+{
+	struct pcm512x_priv *pcm512x;
+	int i, ret;
+
+	pcm512x = devm_kzalloc(dev, sizeof(struct pcm512x_priv), GFP_KERNEL);
+	if (!pcm512x)
+		return -ENOMEM;
+
+	dev_set_drvdata(dev, pcm512x);
+	pcm512x->regmap = regmap;
+
+	for (i = 0; i < ARRAY_SIZE(pcm512x->supplies); i++)
+		pcm512x->supplies[i].supply = pcm512x_supply_names[i];
+
+	ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(pcm512x->supplies),
+				      pcm512x->supplies);
+	if (ret != 0) {
+		dev_err(dev, "Failed to get supplies: %d\n", ret);
+		return ret;
+	}
+
+	pcm512x->supply_nb[0].notifier_call = pcm512x_regulator_event_0;
+	pcm512x->supply_nb[1].notifier_call = pcm512x_regulator_event_1;
+	pcm512x->supply_nb[2].notifier_call = pcm512x_regulator_event_2;
+
+	for (i = 0; i < ARRAY_SIZE(pcm512x->supplies); i++) {
+		ret = regulator_register_notifier(pcm512x->supplies[i].consumer,
+						  &pcm512x->supply_nb[i]);
+		if (ret != 0) {
+			dev_err(dev,
+				"Failed to register regulator notifier: %d\n",
+				ret);
+		}
+	}
+
+	ret = regulator_bulk_enable(ARRAY_SIZE(pcm512x->supplies),
+				    pcm512x->supplies);
+	if (ret != 0) {
+		dev_err(dev, "Failed to enable supplies: %d\n", ret);
+		return ret;
+	}
+
+	/* Reset the device, verifying I/O in the process for I2C */
+	ret = regmap_write(regmap, PCM512x_RESET,
+			   PCM512x_RSTM | PCM512x_RSTR);
+	if (ret != 0) {
+		dev_err(dev, "Failed to reset device: %d\n", ret);
+		goto err;
+	}
+
+	ret = regmap_write(regmap, PCM512x_RESET, 0);
+	if (ret != 0) {
+		dev_err(dev, "Failed to reset device: %d\n", ret);
+		goto err;
+	}
+
+	pcm512x->sclk = devm_clk_get(dev, NULL);
+	if (IS_ERR(pcm512x->sclk)) {
+		if (PTR_ERR(pcm512x->sclk) == -EPROBE_DEFER)
+			return -EPROBE_DEFER;
+
+		dev_info(dev, "No SCLK, using BCLK: %ld\n",
+			 PTR_ERR(pcm512x->sclk));
+
+		/* Disable reporting of missing SCLK as an error */
+		regmap_update_bits(regmap, PCM512x_ERROR_DETECT,
+				   PCM512x_IDCH, PCM512x_IDCH);
+
+		/* Switch PLL input to BCLK */
+		regmap_update_bits(regmap, PCM512x_PLL_REF,
+				   PCM512x_SREF, PCM512x_SREF);
+	} else {
+		ret = clk_prepare_enable(pcm512x->sclk);
+		if (ret != 0) {
+			dev_err(dev, "Failed to enable SCLK: %d\n", ret);
+			return ret;
+		}
+	}
+
+	/* Default to standby mode */
+	ret = regmap_update_bits(pcm512x->regmap, PCM512x_POWER,
+				 PCM512x_RQST, PCM512x_RQST);
+	if (ret != 0) {
+		dev_err(dev, "Failed to request standby: %d\n",
+			ret);
+		goto err_clk;
+	}
+
+	pm_runtime_set_active(dev);
+	pm_runtime_enable(dev);
+	pm_runtime_idle(dev);
+
+	ret = snd_soc_register_codec(dev, &pcm512x_codec_driver,
+				    &pcm512x_dai, 1);
+	if (ret != 0) {
+		dev_err(dev, "Failed to register CODEC: %d\n", ret);
+		goto err_pm;
+	}
+
+	return 0;
+
+err_pm:
+	pm_runtime_disable(dev);
+err_clk:
+	if (!IS_ERR(pcm512x->sclk))
+		clk_disable_unprepare(pcm512x->sclk);
+err:
+	regulator_bulk_disable(ARRAY_SIZE(pcm512x->supplies),
+				     pcm512x->supplies);
+	return ret;
+}
+EXPORT_SYMBOL_GPL(pcm512x_probe);
+
+void pcm512x_remove(struct device *dev)
+{
+	struct pcm512x_priv *pcm512x = dev_get_drvdata(dev);
+
+	snd_soc_unregister_codec(dev);
+	pm_runtime_disable(dev);
+	if (!IS_ERR(pcm512x->sclk))
+		clk_disable_unprepare(pcm512x->sclk);
+	regulator_bulk_disable(ARRAY_SIZE(pcm512x->supplies),
+			       pcm512x->supplies);
+}
+EXPORT_SYMBOL_GPL(pcm512x_remove);
+
+static int pcm512x_suspend(struct device *dev)
+{
+	struct pcm512x_priv *pcm512x = dev_get_drvdata(dev);
+	int ret;
+
+	ret = regmap_update_bits(pcm512x->regmap, PCM512x_POWER,
+				 PCM512x_RQPD, PCM512x_RQPD);
+	if (ret != 0) {
+		dev_err(dev, "Failed to request power down: %d\n", ret);
+		return ret;
+	}
+
+	ret = regulator_bulk_disable(ARRAY_SIZE(pcm512x->supplies),
+				     pcm512x->supplies);
+	if (ret != 0) {
+		dev_err(dev, "Failed to disable supplies: %d\n", ret);
+		return ret;
+	}
+
+	if (!IS_ERR(pcm512x->sclk))
+		clk_disable_unprepare(pcm512x->sclk);
+
+	return 0;
+}
+
+static int pcm512x_resume(struct device *dev)
+{
+	struct pcm512x_priv *pcm512x = dev_get_drvdata(dev);
+	int ret;
+
+	if (!IS_ERR(pcm512x->sclk)) {
+		ret = clk_prepare_enable(pcm512x->sclk);
+		if (ret != 0) {
+			dev_err(dev, "Failed to enable SCLK: %d\n", ret);
+			return ret;
+		}
+	}
+
+	ret = regulator_bulk_enable(ARRAY_SIZE(pcm512x->supplies),
+				    pcm512x->supplies);
+	if (ret != 0) {
+		dev_err(dev, "Failed to enable supplies: %d\n", ret);
+		return ret;
+	}
+
+	regcache_cache_only(pcm512x->regmap, false);
+	ret = regcache_sync(pcm512x->regmap);
+	if (ret != 0) {
+		dev_err(dev, "Failed to sync cache: %d\n", ret);
+		return ret;
+	}
+
+	ret = regmap_update_bits(pcm512x->regmap, PCM512x_POWER,
+				 PCM512x_RQPD, 0);
+	if (ret != 0) {
+		dev_err(dev, "Failed to remove power down: %d\n", ret);
+		return ret;
+	}
+
+	return 0;
+}
+
+const struct dev_pm_ops pcm512x_pm_ops = {
+	SET_RUNTIME_PM_OPS(pcm512x_suspend, pcm512x_resume, NULL)
+};
+EXPORT_SYMBOL_GPL(pcm512x_pm_ops);
+
+MODULE_DESCRIPTION("ASoC PCM512x codec driver");
+MODULE_AUTHOR("Mark Brown <broonie@linaro.org>");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/pcm512x.h b/sound/soc/codecs/pcm512x.h
new file mode 100644
index 000000000000..6ee76aaca09a
--- /dev/null
+++ b/sound/soc/codecs/pcm512x.h
@@ -0,0 +1,171 @@
+/*
+ * Driver for the PCM512x CODECs
+ *
+ * Author:	Mark Brown <broonie@linaro.org>
+ *		Copyright 2014 Linaro Ltd
+ *
+ * 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.
+ */
+
+#ifndef _SND_SOC_PCM512X
+#define _SND_SOC_PCM512X
+
+#include <linux/pm.h>
+#include <linux/regmap.h>
+
+#define PCM512x_VIRT_BASE 0x100
+#define PCM512x_PAGE_LEN  0x100
+#define PCM512x_PAGE_BASE(n)  (PCM512x_VIRT_BASE + (PCM512x_PAGE_LEN * n))
+
+#define PCM512x_PAGE              0
+
+#define PCM512x_RESET             (PCM512x_PAGE_BASE(0) +   1)
+#define PCM512x_POWER             (PCM512x_PAGE_BASE(0) +   2)
+#define PCM512x_MUTE              (PCM512x_PAGE_BASE(0) +   3)
+#define PCM512x_PLL_EN            (PCM512x_PAGE_BASE(0) +   4)
+#define PCM512x_SPI_MISO_FUNCTION (PCM512x_PAGE_BASE(0) +   6)
+#define PCM512x_DSP               (PCM512x_PAGE_BASE(0) +   7)
+#define PCM512x_GPIO_EN           (PCM512x_PAGE_BASE(0) +   8)
+#define PCM512x_BCLK_LRCLK_CFG    (PCM512x_PAGE_BASE(0) +   9)
+#define PCM512x_DSP_GPIO_INPUT    (PCM512x_PAGE_BASE(0) +  10)
+#define PCM512x_MASTER_MODE       (PCM512x_PAGE_BASE(0) +  12)
+#define PCM512x_PLL_REF           (PCM512x_PAGE_BASE(0) +  13)
+#define PCM512x_PLL_COEFF_0       (PCM512x_PAGE_BASE(0) +  20)
+#define PCM512x_PLL_COEFF_1       (PCM512x_PAGE_BASE(0) +  21)
+#define PCM512x_PLL_COEFF_2       (PCM512x_PAGE_BASE(0) +  22)
+#define PCM512x_PLL_COEFF_3       (PCM512x_PAGE_BASE(0) +  23)
+#define PCM512x_PLL_COEFF_4       (PCM512x_PAGE_BASE(0) +  24)
+#define PCM512x_DSP_CLKDIV        (PCM512x_PAGE_BASE(0) +  27)
+#define PCM512x_DAC_CLKDIV        (PCM512x_PAGE_BASE(0) +  28)
+#define PCM512x_NCP_CLKDIV        (PCM512x_PAGE_BASE(0) +  29)
+#define PCM512x_OSR_CLKDIV        (PCM512x_PAGE_BASE(0) +  30)
+#define PCM512x_MASTER_CLKDIV_1   (PCM512x_PAGE_BASE(0) +  32)
+#define PCM512x_MASTER_CLKDIV_2   (PCM512x_PAGE_BASE(0) +  33)
+#define PCM512x_FS_SPEED_MODE     (PCM512x_PAGE_BASE(0) +  34)
+#define PCM512x_IDAC_1            (PCM512x_PAGE_BASE(0) +  35)
+#define PCM512x_IDAC_2            (PCM512x_PAGE_BASE(0) +  36)
+#define PCM512x_ERROR_DETECT      (PCM512x_PAGE_BASE(0) +  37)
+#define PCM512x_I2S_1             (PCM512x_PAGE_BASE(0) +  40)
+#define PCM512x_I2S_2             (PCM512x_PAGE_BASE(0) +  41)
+#define PCM512x_DAC_ROUTING       (PCM512x_PAGE_BASE(0) +  42)
+#define PCM512x_DSP_PROGRAM       (PCM512x_PAGE_BASE(0) +  43)
+#define PCM512x_CLKDET            (PCM512x_PAGE_BASE(0) +  44)
+#define PCM512x_AUTO_MUTE         (PCM512x_PAGE_BASE(0) +  59)
+#define PCM512x_DIGITAL_VOLUME_1  (PCM512x_PAGE_BASE(0) +  60)
+#define PCM512x_DIGITAL_VOLUME_2  (PCM512x_PAGE_BASE(0) +  61)
+#define PCM512x_DIGITAL_VOLUME_3  (PCM512x_PAGE_BASE(0) +  62)
+#define PCM512x_DIGITAL_MUTE_1    (PCM512x_PAGE_BASE(0) +  63)
+#define PCM512x_DIGITAL_MUTE_2    (PCM512x_PAGE_BASE(0) +  64)
+#define PCM512x_DIGITAL_MUTE_3    (PCM512x_PAGE_BASE(0) +  65)
+#define PCM512x_GPIO_OUTPUT_1     (PCM512x_PAGE_BASE(0) +  80)
+#define PCM512x_GPIO_OUTPUT_2     (PCM512x_PAGE_BASE(0) +  81)
+#define PCM512x_GPIO_OUTPUT_3     (PCM512x_PAGE_BASE(0) +  82)
+#define PCM512x_GPIO_OUTPUT_4     (PCM512x_PAGE_BASE(0) +  83)
+#define PCM512x_GPIO_OUTPUT_5     (PCM512x_PAGE_BASE(0) +  84)
+#define PCM512x_GPIO_OUTPUT_6     (PCM512x_PAGE_BASE(0) +  85)
+#define PCM512x_GPIO_CONTROL_1    (PCM512x_PAGE_BASE(0) +  86)
+#define PCM512x_GPIO_CONTROL_2    (PCM512x_PAGE_BASE(0) +  87)
+#define PCM512x_OVERFLOW          (PCM512x_PAGE_BASE(0) +  90)
+#define PCM512x_RATE_DET_1        (PCM512x_PAGE_BASE(0) +  91)
+#define PCM512x_RATE_DET_2        (PCM512x_PAGE_BASE(0) +  92)
+#define PCM512x_RATE_DET_3        (PCM512x_PAGE_BASE(0) +  93)
+#define PCM512x_RATE_DET_4        (PCM512x_PAGE_BASE(0) +  94)
+#define PCM512x_ANALOG_MUTE_DET   (PCM512x_PAGE_BASE(0) + 108)
+#define PCM512x_GPIN              (PCM512x_PAGE_BASE(0) + 119)
+#define PCM512x_DIGITAL_MUTE_DET  (PCM512x_PAGE_BASE(0) + 120)
+
+#define PCM512x_OUTPUT_AMPLITUDE  (PCM512x_PAGE_BASE(1) +   1)
+#define PCM512x_ANALOG_GAIN_CTRL  (PCM512x_PAGE_BASE(1) +   2)
+#define PCM512x_UNDERVOLTAGE_PROT (PCM512x_PAGE_BASE(1) +   5)
+#define PCM512x_ANALOG_MUTE_CTRL  (PCM512x_PAGE_BASE(1) +   6)
+#define PCM512x_ANALOG_GAIN_BOOST (PCM512x_PAGE_BASE(1) +   7)
+#define PCM512x_VCOM_CTRL_1       (PCM512x_PAGE_BASE(1) +   8)
+#define PCM512x_VCOM_CTRL_2       (PCM512x_PAGE_BASE(1) +   9)
+
+#define PCM512x_CRAM_CTRL         (PCM512x_PAGE_BASE(44) +  1)
+
+#define PCM512x_MAX_REGISTER      (PCM512x_PAGE_BASE(44) +  1)
+
+/* Page 0, Register 1 - reset */
+#define PCM512x_RSTR (1 << 0)
+#define PCM512x_RSTM (1 << 4)
+
+/* Page 0, Register 2 - power */
+#define PCM512x_RQPD       (1 << 0)
+#define PCM512x_RQPD_SHIFT 0
+#define PCM512x_RQST       (1 << 4)
+#define PCM512x_RQST_SHIFT 4
+
+/* Page 0, Register 3 - mute */
+#define PCM512x_RQMR_SHIFT 0
+#define PCM512x_RQML_SHIFT 4
+
+/* Page 0, Register 4 - PLL */
+#define PCM512x_PLCE       (1 << 0)
+#define PCM512x_RLCE_SHIFT 0
+#define PCM512x_PLCK       (1 << 4)
+#define PCM512x_PLCK_SHIFT 4
+
+/* Page 0, Register 7 - DSP */
+#define PCM512x_SDSL       (1 << 0)
+#define PCM512x_SDSL_SHIFT 0
+#define PCM512x_DEMP       (1 << 4)
+#define PCM512x_DEMP_SHIFT 4
+
+/* Page 0, Register 13 - PLL reference */
+#define PCM512x_SREF (1 << 4)
+
+/* Page 0, Register 37 - Error detection */
+#define PCM512x_IPLK (1 << 0)
+#define PCM512x_DCAS (1 << 1)
+#define PCM512x_IDCM (1 << 2)
+#define PCM512x_IDCH (1 << 3)
+#define PCM512x_IDSK (1 << 4)
+#define PCM512x_IDBK (1 << 5)
+#define PCM512x_IDFS (1 << 6)
+
+/* Page 0, Register 42 - DAC routing */
+#define PCM512x_AUPR_SHIFT 0
+#define PCM512x_AUPL_SHIFT 4
+
+/* Page 0, Register 59 - auto mute */
+#define PCM512x_ATMR_SHIFT 0
+#define PCM512x_ATML_SHIFT 4
+
+/* Page 0, Register 63 - ramp rates */
+#define PCM512x_VNDF_SHIFT 6
+#define PCM512x_VNDS_SHIFT 4
+#define PCM512x_VNUF_SHIFT 2
+#define PCM512x_VNUS_SHIFT 0
+
+/* Page 0, Register 64 - emergency ramp rates */
+#define PCM512x_VEDF_SHIFT 6
+#define PCM512x_VEDS_SHIFT 4
+
+/* Page 0, Register 65 - Digital mute enables */
+#define PCM512x_ACTL_SHIFT 2
+#define PCM512x_AMLE_SHIFT 1
+#define PCM512x_AMLR_SHIFT 0
+
+/* Page 1, Register 2 - analog volume control */
+#define PCM512x_RAGN_SHIFT 0
+#define PCM512x_LAGN_SHIFT 4
+
+/* Page 1, Register 7 - analog boost control */
+#define PCM512x_AGBR_SHIFT 0
+#define PCM512x_AGBL_SHIFT 4
+
+extern const struct dev_pm_ops pcm512x_pm_ops;
+extern const struct regmap_config pcm512x_regmap;
+
+int pcm512x_probe(struct device *dev, struct regmap *regmap);
+void pcm512x_remove(struct device *dev);
+
+#endif
diff --git a/sound/soc/codecs/rt5631.c b/sound/soc/codecs/rt5631.c
index 912c9cbc2724..ce199d375209 100644
--- a/sound/soc/codecs/rt5631.c
+++ b/sound/soc/codecs/rt5631.c
@@ -210,26 +210,22 @@ static int rt5631_dmic_put(struct snd_kcontrol *kcontrol,
 static const char *rt5631_input_mode[] = {
 	"Single ended", "Differential"};
 
-static const SOC_ENUM_SINGLE_DECL(
-	rt5631_mic1_mode_enum, RT5631_MIC_CTRL_1,
-	RT5631_MIC1_DIFF_INPUT_SHIFT, rt5631_input_mode);
+static SOC_ENUM_SINGLE_DECL(rt5631_mic1_mode_enum, RT5631_MIC_CTRL_1,
+			    RT5631_MIC1_DIFF_INPUT_SHIFT, rt5631_input_mode);
 
-static const SOC_ENUM_SINGLE_DECL(
-	rt5631_mic2_mode_enum, RT5631_MIC_CTRL_1,
-	RT5631_MIC2_DIFF_INPUT_SHIFT, rt5631_input_mode);
+static SOC_ENUM_SINGLE_DECL(rt5631_mic2_mode_enum, RT5631_MIC_CTRL_1,
+			    RT5631_MIC2_DIFF_INPUT_SHIFT, rt5631_input_mode);
 
 /* MONO Input Type */
-static const SOC_ENUM_SINGLE_DECL(
-	rt5631_monoin_mode_enum, RT5631_MONO_INPUT_VOL,
-	RT5631_MONO_DIFF_INPUT_SHIFT, rt5631_input_mode);
+static SOC_ENUM_SINGLE_DECL(rt5631_monoin_mode_enum, RT5631_MONO_INPUT_VOL,
+			    RT5631_MONO_DIFF_INPUT_SHIFT, rt5631_input_mode);
 
 /* SPK Ratio Gain Control */
 static const char *rt5631_spk_ratio[] = {"1.00x", "1.09x", "1.27x", "1.44x",
 			"1.56x", "1.68x", "1.99x", "2.34x"};
 
-static const SOC_ENUM_SINGLE_DECL(
-	rt5631_spk_ratio_enum, RT5631_GEN_PUR_CTRL_REG,
-	RT5631_SPK_AMP_RATIO_CTRL_SHIFT, rt5631_spk_ratio);
+static SOC_ENUM_SINGLE_DECL(rt5631_spk_ratio_enum, RT5631_GEN_PUR_CTRL_REG,
+			    RT5631_SPK_AMP_RATIO_CTRL_SHIFT, rt5631_spk_ratio);
 
 static const struct snd_kcontrol_new rt5631_snd_controls[] = {
 	/* MIC */
@@ -759,9 +755,8 @@ static const struct snd_kcontrol_new rt5631_monomix_mixer_controls[] = {
 /* Left SPK Volume Input */
 static const char *rt5631_spkvoll_sel[] = {"Vmid", "SPKMIXL"};
 
-static const SOC_ENUM_SINGLE_DECL(
-	rt5631_spkvoll_enum, RT5631_SPK_OUT_VOL,
-	RT5631_L_EN_SHIFT, rt5631_spkvoll_sel);
+static SOC_ENUM_SINGLE_DECL(rt5631_spkvoll_enum, RT5631_SPK_OUT_VOL,
+			    RT5631_L_EN_SHIFT, rt5631_spkvoll_sel);
 
 static const struct snd_kcontrol_new rt5631_spkvoll_mux_control =
 	SOC_DAPM_ENUM("Left SPKVOL SRC", rt5631_spkvoll_enum);
@@ -769,9 +764,8 @@ static const struct snd_kcontrol_new rt5631_spkvoll_mux_control =
 /* Left HP Volume Input */
 static const char *rt5631_hpvoll_sel[] = {"Vmid", "OUTMIXL"};
 
-static const SOC_ENUM_SINGLE_DECL(
-	rt5631_hpvoll_enum, RT5631_HP_OUT_VOL,
-	RT5631_L_EN_SHIFT, rt5631_hpvoll_sel);
+static SOC_ENUM_SINGLE_DECL(rt5631_hpvoll_enum, RT5631_HP_OUT_VOL,
+			    RT5631_L_EN_SHIFT, rt5631_hpvoll_sel);
 
 static const struct snd_kcontrol_new rt5631_hpvoll_mux_control =
 	SOC_DAPM_ENUM("Left HPVOL SRC", rt5631_hpvoll_enum);
@@ -779,9 +773,8 @@ static const struct snd_kcontrol_new rt5631_hpvoll_mux_control =
 /* Left Out Volume Input */
 static const char *rt5631_outvoll_sel[] = {"Vmid", "OUTMIXL"};
 
-static const SOC_ENUM_SINGLE_DECL(
-	rt5631_outvoll_enum, RT5631_MONO_AXO_1_2_VOL,
-	RT5631_L_EN_SHIFT, rt5631_outvoll_sel);
+static SOC_ENUM_SINGLE_DECL(rt5631_outvoll_enum, RT5631_MONO_AXO_1_2_VOL,
+			    RT5631_L_EN_SHIFT, rt5631_outvoll_sel);
 
 static const struct snd_kcontrol_new rt5631_outvoll_mux_control =
 	SOC_DAPM_ENUM("Left OUTVOL SRC", rt5631_outvoll_enum);
@@ -789,9 +782,8 @@ static const struct snd_kcontrol_new rt5631_outvoll_mux_control =
 /* Right Out Volume Input */
 static const char *rt5631_outvolr_sel[] = {"Vmid", "OUTMIXR"};
 
-static const SOC_ENUM_SINGLE_DECL(
-	rt5631_outvolr_enum, RT5631_MONO_AXO_1_2_VOL,
-	RT5631_R_EN_SHIFT, rt5631_outvolr_sel);
+static SOC_ENUM_SINGLE_DECL(rt5631_outvolr_enum, RT5631_MONO_AXO_1_2_VOL,
+			    RT5631_R_EN_SHIFT, rt5631_outvolr_sel);
 
 static const struct snd_kcontrol_new rt5631_outvolr_mux_control =
 	SOC_DAPM_ENUM("Right OUTVOL SRC", rt5631_outvolr_enum);
@@ -799,9 +791,8 @@ static const struct snd_kcontrol_new rt5631_outvolr_mux_control =
 /* Right HP Volume Input */
 static const char *rt5631_hpvolr_sel[] = {"Vmid", "OUTMIXR"};
 
-static const SOC_ENUM_SINGLE_DECL(
-	rt5631_hpvolr_enum, RT5631_HP_OUT_VOL,
-	RT5631_R_EN_SHIFT, rt5631_hpvolr_sel);
+static SOC_ENUM_SINGLE_DECL(rt5631_hpvolr_enum, RT5631_HP_OUT_VOL,
+			    RT5631_R_EN_SHIFT, rt5631_hpvolr_sel);
 
 static const struct snd_kcontrol_new rt5631_hpvolr_mux_control =
 	SOC_DAPM_ENUM("Right HPVOL SRC", rt5631_hpvolr_enum);
@@ -809,9 +800,8 @@ static const struct snd_kcontrol_new rt5631_hpvolr_mux_control =
 /* Right SPK Volume Input */
 static const char *rt5631_spkvolr_sel[] = {"Vmid", "SPKMIXR"};
 
-static const SOC_ENUM_SINGLE_DECL(
-	rt5631_spkvolr_enum, RT5631_SPK_OUT_VOL,
-	RT5631_R_EN_SHIFT, rt5631_spkvolr_sel);
+static SOC_ENUM_SINGLE_DECL(rt5631_spkvolr_enum, RT5631_SPK_OUT_VOL,
+			    RT5631_R_EN_SHIFT, rt5631_spkvolr_sel);
 
 static const struct snd_kcontrol_new rt5631_spkvolr_mux_control =
 	SOC_DAPM_ENUM("Right SPKVOL SRC", rt5631_spkvolr_enum);
@@ -820,9 +810,8 @@ static const struct snd_kcontrol_new rt5631_spkvolr_mux_control =
 static const char *rt5631_spol_src_sel[] = {
 	"SPOLMIX", "MONOIN_RX", "VDAC", "DACL"};
 
-static const SOC_ENUM_SINGLE_DECL(
-	rt5631_spol_src_enum, RT5631_SPK_MONO_HP_OUT_CTRL,
-	RT5631_SPK_L_MUX_SEL_SHIFT, rt5631_spol_src_sel);
+static SOC_ENUM_SINGLE_DECL(rt5631_spol_src_enum, RT5631_SPK_MONO_HP_OUT_CTRL,
+			    RT5631_SPK_L_MUX_SEL_SHIFT, rt5631_spol_src_sel);
 
 static const struct snd_kcontrol_new rt5631_spol_mux_control =
 	SOC_DAPM_ENUM("SPOL SRC", rt5631_spol_src_enum);
@@ -831,9 +820,8 @@ static const struct snd_kcontrol_new rt5631_spol_mux_control =
 static const char *rt5631_spor_src_sel[] = {
 	"SPORMIX", "MONOIN_RX", "VDAC", "DACR"};
 
-static const SOC_ENUM_SINGLE_DECL(
-	rt5631_spor_src_enum, RT5631_SPK_MONO_HP_OUT_CTRL,
-	RT5631_SPK_R_MUX_SEL_SHIFT, rt5631_spor_src_sel);
+static SOC_ENUM_SINGLE_DECL(rt5631_spor_src_enum, RT5631_SPK_MONO_HP_OUT_CTRL,
+			    RT5631_SPK_R_MUX_SEL_SHIFT, rt5631_spor_src_sel);
 
 static const struct snd_kcontrol_new rt5631_spor_mux_control =
 	SOC_DAPM_ENUM("SPOR SRC", rt5631_spor_src_enum);
@@ -841,9 +829,8 @@ static const struct snd_kcontrol_new rt5631_spor_mux_control =
 /* MONO Input */
 static const char *rt5631_mono_src_sel[] = {"MONOMIX", "MONOIN_RX", "VDAC"};
 
-static const SOC_ENUM_SINGLE_DECL(
-	rt5631_mono_src_enum, RT5631_SPK_MONO_HP_OUT_CTRL,
-	RT5631_MONO_MUX_SEL_SHIFT, rt5631_mono_src_sel);
+static SOC_ENUM_SINGLE_DECL(rt5631_mono_src_enum, RT5631_SPK_MONO_HP_OUT_CTRL,
+			    RT5631_MONO_MUX_SEL_SHIFT, rt5631_mono_src_sel);
 
 static const struct snd_kcontrol_new rt5631_mono_mux_control =
 	SOC_DAPM_ENUM("MONO SRC", rt5631_mono_src_enum);
@@ -851,9 +838,8 @@ static const struct snd_kcontrol_new rt5631_mono_mux_control =
 /* Left HPO Input */
 static const char *rt5631_hpl_src_sel[] = {"Left HPVOL", "Left DAC"};
 
-static const SOC_ENUM_SINGLE_DECL(
-	rt5631_hpl_src_enum, RT5631_SPK_MONO_HP_OUT_CTRL,
-	RT5631_HP_L_MUX_SEL_SHIFT, rt5631_hpl_src_sel);
+static SOC_ENUM_SINGLE_DECL(rt5631_hpl_src_enum, RT5631_SPK_MONO_HP_OUT_CTRL,
+			    RT5631_HP_L_MUX_SEL_SHIFT, rt5631_hpl_src_sel);
 
 static const struct snd_kcontrol_new rt5631_hpl_mux_control =
 	SOC_DAPM_ENUM("HPL SRC", rt5631_hpl_src_enum);
@@ -861,9 +847,8 @@ static const struct snd_kcontrol_new rt5631_hpl_mux_control =
 /* Right HPO Input */
 static const char *rt5631_hpr_src_sel[] = {"Right HPVOL", "Right DAC"};
 
-static const SOC_ENUM_SINGLE_DECL(
-	rt5631_hpr_src_enum, RT5631_SPK_MONO_HP_OUT_CTRL,
-	RT5631_HP_R_MUX_SEL_SHIFT, rt5631_hpr_src_sel);
+static SOC_ENUM_SINGLE_DECL(rt5631_hpr_src_enum, RT5631_SPK_MONO_HP_OUT_CTRL,
+			    RT5631_HP_R_MUX_SEL_SHIFT, rt5631_hpr_src_sel);
 
 static const struct snd_kcontrol_new rt5631_hpr_mux_control =
 	SOC_DAPM_ENUM("HPR SRC", rt5631_hpr_src_enum);
diff --git a/sound/soc/codecs/rt5640.c b/sound/soc/codecs/rt5640.c
index 886924934aa5..1a1e1150237d 100644
--- a/sound/soc/codecs/rt5640.c
+++ b/sound/soc/codecs/rt5640.c
@@ -361,25 +361,24 @@ static unsigned int bst_tlv[] = {
 static const char * const rt5640_data_select[] = {
 	"Normal", "left copy to right", "right copy to left", "Swap"};
 
-static const SOC_ENUM_SINGLE_DECL(rt5640_if1_dac_enum, RT5640_DIG_INF_DATA,
-				RT5640_IF1_DAC_SEL_SFT, rt5640_data_select);
+static SOC_ENUM_SINGLE_DECL(rt5640_if1_dac_enum, RT5640_DIG_INF_DATA,
+			    RT5640_IF1_DAC_SEL_SFT, rt5640_data_select);
 
-static const SOC_ENUM_SINGLE_DECL(rt5640_if1_adc_enum, RT5640_DIG_INF_DATA,
-				RT5640_IF1_ADC_SEL_SFT, rt5640_data_select);
+static SOC_ENUM_SINGLE_DECL(rt5640_if1_adc_enum, RT5640_DIG_INF_DATA,
+			    RT5640_IF1_ADC_SEL_SFT, rt5640_data_select);
 
-static const SOC_ENUM_SINGLE_DECL(rt5640_if2_dac_enum, RT5640_DIG_INF_DATA,
-				RT5640_IF2_DAC_SEL_SFT, rt5640_data_select);
+static SOC_ENUM_SINGLE_DECL(rt5640_if2_dac_enum, RT5640_DIG_INF_DATA,
+			    RT5640_IF2_DAC_SEL_SFT, rt5640_data_select);
 
-static const SOC_ENUM_SINGLE_DECL(rt5640_if2_adc_enum, RT5640_DIG_INF_DATA,
-				RT5640_IF2_ADC_SEL_SFT, rt5640_data_select);
+static SOC_ENUM_SINGLE_DECL(rt5640_if2_adc_enum, RT5640_DIG_INF_DATA,
+			    RT5640_IF2_ADC_SEL_SFT, rt5640_data_select);
 
 /* Class D speaker gain ratio */
 static const char * const rt5640_clsd_spk_ratio[] = {"1.66x", "1.83x", "1.94x",
 	"2x", "2.11x", "2.22x", "2.33x", "2.44x", "2.55x", "2.66x", "2.77x"};
 
-static const SOC_ENUM_SINGLE_DECL(
-	rt5640_clsd_spk_ratio_enum, RT5640_CLS_D_OUT,
-	RT5640_CLSD_RATIO_SFT, rt5640_clsd_spk_ratio);
+static SOC_ENUM_SINGLE_DECL(rt5640_clsd_spk_ratio_enum, RT5640_CLS_D_OUT,
+			    RT5640_CLSD_RATIO_SFT, rt5640_clsd_spk_ratio);
 
 static const struct snd_kcontrol_new rt5640_snd_controls[] = {
 	/* Speaker Output Volume */
@@ -753,9 +752,8 @@ static const char * const rt5640_stereo_adc1_src[] = {
 	"DIG MIX", "ADC"
 };
 
-static const SOC_ENUM_SINGLE_DECL(
-	rt5640_stereo_adc1_enum, RT5640_STO_ADC_MIXER,
-	RT5640_ADC_1_SRC_SFT, rt5640_stereo_adc1_src);
+static SOC_ENUM_SINGLE_DECL(rt5640_stereo_adc1_enum, RT5640_STO_ADC_MIXER,
+			    RT5640_ADC_1_SRC_SFT, rt5640_stereo_adc1_src);
 
 static const struct snd_kcontrol_new rt5640_sto_adc_1_mux =
 	SOC_DAPM_ENUM("Stereo ADC1 Mux", rt5640_stereo_adc1_enum);
@@ -764,9 +762,8 @@ static const char * const rt5640_stereo_adc2_src[] = {
 	"DMIC1", "DMIC2", "DIG MIX"
 };
 
-static const SOC_ENUM_SINGLE_DECL(
-	rt5640_stereo_adc2_enum, RT5640_STO_ADC_MIXER,
-	RT5640_ADC_2_SRC_SFT, rt5640_stereo_adc2_src);
+static SOC_ENUM_SINGLE_DECL(rt5640_stereo_adc2_enum, RT5640_STO_ADC_MIXER,
+			    RT5640_ADC_2_SRC_SFT, rt5640_stereo_adc2_src);
 
 static const struct snd_kcontrol_new rt5640_sto_adc_2_mux =
 	SOC_DAPM_ENUM("Stereo ADC2 Mux", rt5640_stereo_adc2_enum);
@@ -776,9 +773,8 @@ static const char * const rt5640_mono_adc_l1_src[] = {
 	"Mono DAC MIXL", "ADCL"
 };
 
-static const SOC_ENUM_SINGLE_DECL(
-	rt5640_mono_adc_l1_enum, RT5640_MONO_ADC_MIXER,
-	RT5640_MONO_ADC_L1_SRC_SFT, rt5640_mono_adc_l1_src);
+static SOC_ENUM_SINGLE_DECL(rt5640_mono_adc_l1_enum, RT5640_MONO_ADC_MIXER,
+			    RT5640_MONO_ADC_L1_SRC_SFT, rt5640_mono_adc_l1_src);
 
 static const struct snd_kcontrol_new rt5640_mono_adc_l1_mux =
 	SOC_DAPM_ENUM("Mono ADC1 left source", rt5640_mono_adc_l1_enum);
@@ -787,9 +783,8 @@ static const char * const rt5640_mono_adc_l2_src[] = {
 	"DMIC L1", "DMIC L2", "Mono DAC MIXL"
 };
 
-static const SOC_ENUM_SINGLE_DECL(
-	rt5640_mono_adc_l2_enum, RT5640_MONO_ADC_MIXER,
-	RT5640_MONO_ADC_L2_SRC_SFT, rt5640_mono_adc_l2_src);
+static SOC_ENUM_SINGLE_DECL(rt5640_mono_adc_l2_enum, RT5640_MONO_ADC_MIXER,
+			    RT5640_MONO_ADC_L2_SRC_SFT, rt5640_mono_adc_l2_src);
 
 static const struct snd_kcontrol_new rt5640_mono_adc_l2_mux =
 	SOC_DAPM_ENUM("Mono ADC2 left source", rt5640_mono_adc_l2_enum);
@@ -798,9 +793,8 @@ static const char * const rt5640_mono_adc_r1_src[] = {
 	"Mono DAC MIXR", "ADCR"
 };
 
-static const SOC_ENUM_SINGLE_DECL(
-	rt5640_mono_adc_r1_enum, RT5640_MONO_ADC_MIXER,
-	RT5640_MONO_ADC_R1_SRC_SFT, rt5640_mono_adc_r1_src);
+static SOC_ENUM_SINGLE_DECL(rt5640_mono_adc_r1_enum, RT5640_MONO_ADC_MIXER,
+			    RT5640_MONO_ADC_R1_SRC_SFT, rt5640_mono_adc_r1_src);
 
 static const struct snd_kcontrol_new rt5640_mono_adc_r1_mux =
 	SOC_DAPM_ENUM("Mono ADC1 right source", rt5640_mono_adc_r1_enum);
@@ -809,9 +803,8 @@ static const char * const rt5640_mono_adc_r2_src[] = {
 	"DMIC R1", "DMIC R2", "Mono DAC MIXR"
 };
 
-static const SOC_ENUM_SINGLE_DECL(
-	rt5640_mono_adc_r2_enum, RT5640_MONO_ADC_MIXER,
-	RT5640_MONO_ADC_R2_SRC_SFT, rt5640_mono_adc_r2_src);
+static SOC_ENUM_SINGLE_DECL(rt5640_mono_adc_r2_enum, RT5640_MONO_ADC_MIXER,
+			    RT5640_MONO_ADC_R2_SRC_SFT, rt5640_mono_adc_r2_src);
 
 static const struct snd_kcontrol_new rt5640_mono_adc_r2_mux =
 	SOC_DAPM_ENUM("Mono ADC2 right source", rt5640_mono_adc_r2_enum);
@@ -826,9 +819,9 @@ static int rt5640_dac_l2_values[] = {
 	3,
 };
 
-static const SOC_VALUE_ENUM_SINGLE_DECL(
-	rt5640_dac_l2_enum, RT5640_DSP_PATH2, RT5640_DAC_L2_SEL_SFT,
-	0x3, rt5640_dac_l2_src, rt5640_dac_l2_values);
+static SOC_VALUE_ENUM_SINGLE_DECL(rt5640_dac_l2_enum,
+				  RT5640_DSP_PATH2, RT5640_DAC_L2_SEL_SFT,
+				  0x3, rt5640_dac_l2_src, rt5640_dac_l2_values);
 
 static const struct snd_kcontrol_new rt5640_dac_l2_mux =
 	SOC_DAPM_VALUE_ENUM("DAC2 left channel source", rt5640_dac_l2_enum);
@@ -841,9 +834,9 @@ static int rt5640_dac_r2_values[] = {
 	0,
 };
 
-static const SOC_VALUE_ENUM_SINGLE_DECL(
-	rt5640_dac_r2_enum, RT5640_DSP_PATH2, RT5640_DAC_R2_SEL_SFT,
-	0x3, rt5640_dac_r2_src, rt5640_dac_r2_values);
+static SOC_VALUE_ENUM_SINGLE_DECL(rt5640_dac_r2_enum,
+				  RT5640_DSP_PATH2, RT5640_DAC_R2_SEL_SFT,
+				  0x3, rt5640_dac_r2_src, rt5640_dac_r2_values);
 
 static const struct snd_kcontrol_new rt5640_dac_r2_mux =
 	SOC_DAPM_ENUM("DAC2 right channel source", rt5640_dac_r2_enum);
@@ -860,9 +853,10 @@ static int rt5640_dai_iis_map_values[] = {
 	7,
 };
 
-static const SOC_VALUE_ENUM_SINGLE_DECL(
-	rt5640_dai_iis_map_enum, RT5640_I2S1_SDP, RT5640_I2S_IF_SFT,
-	0x7, rt5640_dai_iis_map, rt5640_dai_iis_map_values);
+static SOC_VALUE_ENUM_SINGLE_DECL(rt5640_dai_iis_map_enum,
+				  RT5640_I2S1_SDP, RT5640_I2S_IF_SFT,
+				  0x7, rt5640_dai_iis_map,
+				  rt5640_dai_iis_map_values);
 
 static const struct snd_kcontrol_new rt5640_dai_mux =
 	SOC_DAPM_VALUE_ENUM("DAI select", rt5640_dai_iis_map_enum);
@@ -872,9 +866,8 @@ static const char * const rt5640_sdi_sel[] = {
 	"IF1", "IF2"
 };
 
-static const SOC_ENUM_SINGLE_DECL(
-	rt5640_sdi_sel_enum, RT5640_I2S2_SDP,
-	RT5640_I2S2_SDI_SFT, rt5640_sdi_sel);
+static SOC_ENUM_SINGLE_DECL(rt5640_sdi_sel_enum, RT5640_I2S2_SDP,
+			    RT5640_I2S2_SDI_SFT, rt5640_sdi_sel);
 
 static const struct snd_kcontrol_new rt5640_sdi_mux =
 	SOC_DAPM_ENUM("SDI select", rt5640_sdi_sel_enum);
diff --git a/sound/soc/codecs/sgtl5000.c b/sound/soc/codecs/sgtl5000.c
index 0fcbe90f3ef2..ab4754a7a88c 100644
--- a/sound/soc/codecs/sgtl5000.c
+++ b/sound/soc/codecs/sgtl5000.c
@@ -187,8 +187,9 @@ static const char *adc_mux_text[] = {
 	"MIC_IN", "LINE_IN"
 };
 
-static const struct soc_enum adc_enum =
-SOC_ENUM_SINGLE(SGTL5000_CHIP_ANA_CTRL, 2, 2, adc_mux_text);
+static SOC_ENUM_SINGLE_DECL(adc_enum,
+			    SGTL5000_CHIP_ANA_CTRL, 2,
+			    adc_mux_text);
 
 static const struct snd_kcontrol_new adc_mux =
 SOC_DAPM_ENUM("Capture Mux", adc_enum);
@@ -198,8 +199,9 @@ static const char *dac_mux_text[] = {
 	"DAC", "LINE_IN"
 };
 
-static const struct soc_enum dac_enum =
-SOC_ENUM_SINGLE(SGTL5000_CHIP_ANA_CTRL, 6, 2, dac_mux_text);
+static SOC_ENUM_SINGLE_DECL(dac_enum,
+			    SGTL5000_CHIP_ANA_CTRL, 6,
+			    dac_mux_text);
 
 static const struct snd_kcontrol_new dac_mux =
 SOC_DAPM_ENUM("Headphone Mux", dac_enum);
diff --git a/sound/soc/codecs/sirf-audio-codec.c b/sound/soc/codecs/sirf-audio-codec.c
new file mode 100644
index 000000000000..90e3a228bae4
--- /dev/null
+++ b/sound/soc/codecs/sirf-audio-codec.c
@@ -0,0 +1,533 @@
+/*
+ * SiRF audio codec driver
+ *
+ * Copyright (c) 2011 Cambridge Silicon Radio Limited, a CSR plc group company.
+ *
+ * Licensed under GPLv2 or later.
+ */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/regmap.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/initval.h>
+#include <sound/tlv.h>
+#include <sound/soc.h>
+#include <sound/dmaengine_pcm.h>
+
+#include "sirf-audio-codec.h"
+
+struct sirf_audio_codec {
+	struct clk *clk;
+	struct regmap *regmap;
+	u32 reg_ctrl0, reg_ctrl1;
+};
+
+static const char * const input_mode_mux[] = {"Single-ended",
+	"Differential"};
+
+static const struct soc_enum input_mode_mux_enum =
+	SOC_ENUM_SINGLE(AUDIO_IC_CODEC_CTRL1, 4, 2, input_mode_mux);
+
+static const struct snd_kcontrol_new sirf_audio_codec_input_mode_control =
+	SOC_DAPM_ENUM("Route", input_mode_mux_enum);
+
+static const DECLARE_TLV_DB_SCALE(playback_vol_tlv, -12400, 100, 0);
+static const DECLARE_TLV_DB_SCALE(capture_vol_tlv_prima2, 500, 100, 0);
+static const DECLARE_TLV_DB_RANGE(capture_vol_tlv_atlas6,
+	0, 7, TLV_DB_SCALE_ITEM(-100, 100, 0),
+	0x22, 0x3F, TLV_DB_SCALE_ITEM(700, 100, 0),
+);
+
+static struct snd_kcontrol_new volume_controls_atlas6[] = {
+	SOC_DOUBLE_TLV("Playback Volume", AUDIO_IC_CODEC_CTRL0, 21, 14,
+			0x7F, 0, playback_vol_tlv),
+	SOC_DOUBLE_TLV("Capture Volume", AUDIO_IC_CODEC_CTRL1, 16, 10,
+			0x3F, 0, capture_vol_tlv_atlas6),
+};
+
+static struct snd_kcontrol_new volume_controls_prima2[] = {
+	SOC_DOUBLE_TLV("Speaker Volume", AUDIO_IC_CODEC_CTRL0, 21, 14,
+			0x7F, 0, playback_vol_tlv),
+	SOC_DOUBLE_TLV("Capture Volume", AUDIO_IC_CODEC_CTRL1, 15, 10,
+			0x1F, 0, capture_vol_tlv_prima2),
+};
+
+static struct snd_kcontrol_new left_input_path_controls[] = {
+	SOC_DAPM_SINGLE("Line Left Switch", AUDIO_IC_CODEC_CTRL1, 6, 1, 0),
+	SOC_DAPM_SINGLE("Mic Left Switch", AUDIO_IC_CODEC_CTRL1, 3, 1, 0),
+};
+
+static struct snd_kcontrol_new right_input_path_controls[] = {
+	SOC_DAPM_SINGLE("Line Right Switch", AUDIO_IC_CODEC_CTRL1, 5, 1, 0),
+	SOC_DAPM_SINGLE("Mic Right Switch", AUDIO_IC_CODEC_CTRL1, 2, 1, 0),
+};
+
+static struct snd_kcontrol_new left_dac_to_hp_left_amp_switch_control =
+	SOC_DAPM_SINGLE("Switch", AUDIO_IC_CODEC_CTRL0, 9, 1, 0);
+
+static struct snd_kcontrol_new left_dac_to_hp_right_amp_switch_control =
+	SOC_DAPM_SINGLE("Switch", AUDIO_IC_CODEC_CTRL0, 8, 1, 0);
+
+static struct snd_kcontrol_new right_dac_to_hp_left_amp_switch_control =
+	SOC_DAPM_SINGLE("Switch", AUDIO_IC_CODEC_CTRL0, 7, 1, 0);
+
+static struct snd_kcontrol_new right_dac_to_hp_right_amp_switch_control =
+	SOC_DAPM_SINGLE("Switch", AUDIO_IC_CODEC_CTRL0, 6, 1, 0);
+
+static struct snd_kcontrol_new left_dac_to_speaker_lineout_switch_control =
+	SOC_DAPM_SINGLE("Switch", AUDIO_IC_CODEC_CTRL0, 11, 1, 0);
+
+static struct snd_kcontrol_new right_dac_to_speaker_lineout_switch_control =
+	SOC_DAPM_SINGLE("Switch", AUDIO_IC_CODEC_CTRL0, 10, 1, 0);
+
+/* After enable adc, Delay 200ms to avoid pop noise */
+static int adc_enable_delay_event(struct snd_soc_dapm_widget *w,
+		struct snd_kcontrol *kcontrol, int event)
+{
+	switch (event) {
+	case SND_SOC_DAPM_POST_PMU:
+		msleep(200);
+		break;
+	default:
+		break;
+	}
+
+	return 0;
+}
+
+static void enable_and_reset_codec(struct regmap *regmap,
+		u32 codec_enable_bits, u32 codec_reset_bits)
+{
+	regmap_update_bits(regmap, AUDIO_IC_CODEC_CTRL1,
+			codec_enable_bits | codec_reset_bits,
+			codec_enable_bits | ~codec_reset_bits);
+	msleep(20);
+	regmap_update_bits(regmap, AUDIO_IC_CODEC_CTRL1,
+			codec_reset_bits, codec_reset_bits);
+}
+
+static int atlas6_codec_enable_and_reset_event(struct snd_soc_dapm_widget *w,
+		struct snd_kcontrol *kcontrol, int event)
+{
+#define ATLAS6_CODEC_ENABLE_BITS (1 << 29)
+#define ATLAS6_CODEC_RESET_BITS (1 << 28)
+	struct sirf_audio_codec *sirf_audio_codec = dev_get_drvdata(w->codec->dev);
+	switch (event) {
+	case SND_SOC_DAPM_PRE_PMU:
+		enable_and_reset_codec(sirf_audio_codec->regmap,
+			ATLAS6_CODEC_ENABLE_BITS, ATLAS6_CODEC_RESET_BITS);
+		break;
+	case SND_SOC_DAPM_POST_PMD:
+		regmap_update_bits(sirf_audio_codec->regmap,
+			AUDIO_IC_CODEC_CTRL1, ATLAS6_CODEC_ENABLE_BITS,
+			~ATLAS6_CODEC_ENABLE_BITS);
+		break;
+	default:
+		break;
+	}
+
+	return 0;
+}
+
+static int prima2_codec_enable_and_reset_event(struct snd_soc_dapm_widget *w,
+		struct snd_kcontrol *kcontrol, int event)
+{
+#define PRIMA2_CODEC_ENABLE_BITS (1 << 27)
+#define PRIMA2_CODEC_RESET_BITS (1 << 26)
+	struct sirf_audio_codec *sirf_audio_codec = dev_get_drvdata(w->codec->dev);
+	switch (event) {
+	case SND_SOC_DAPM_POST_PMU:
+		enable_and_reset_codec(sirf_audio_codec->regmap,
+			PRIMA2_CODEC_ENABLE_BITS, PRIMA2_CODEC_RESET_BITS);
+		break;
+	case SND_SOC_DAPM_POST_PMD:
+		regmap_update_bits(sirf_audio_codec->regmap,
+			AUDIO_IC_CODEC_CTRL1, PRIMA2_CODEC_ENABLE_BITS,
+			~PRIMA2_CODEC_ENABLE_BITS);
+		break;
+	default:
+		break;
+	}
+
+	return 0;
+}
+
+static const struct snd_soc_dapm_widget atlas6_output_driver_dapm_widgets[] = {
+	SND_SOC_DAPM_OUT_DRV("HP Left Driver", AUDIO_IC_CODEC_CTRL1,
+			25, 0, NULL, 0),
+	SND_SOC_DAPM_OUT_DRV("HP Right Driver", AUDIO_IC_CODEC_CTRL1,
+			26, 0, NULL, 0),
+	SND_SOC_DAPM_OUT_DRV("Speaker Driver", AUDIO_IC_CODEC_CTRL1,
+			27, 0, NULL, 0),
+};
+
+static const struct snd_soc_dapm_widget prima2_output_driver_dapm_widgets[] = {
+	SND_SOC_DAPM_OUT_DRV("HP Left Driver", AUDIO_IC_CODEC_CTRL1,
+			23, 0, NULL, 0),
+	SND_SOC_DAPM_OUT_DRV("HP Right Driver", AUDIO_IC_CODEC_CTRL1,
+			24, 0, NULL, 0),
+	SND_SOC_DAPM_OUT_DRV("Speaker Driver", AUDIO_IC_CODEC_CTRL1,
+			25, 0, NULL, 0),
+};
+
+static const struct snd_soc_dapm_widget atlas6_codec_clock_dapm_widget =
+	SND_SOC_DAPM_SUPPLY("codecclk", SND_SOC_NOPM, 0, 0,
+			atlas6_codec_enable_and_reset_event,
+			SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD);
+
+static const struct snd_soc_dapm_widget prima2_codec_clock_dapm_widget =
+	SND_SOC_DAPM_SUPPLY("codecclk", SND_SOC_NOPM, 0, 0,
+			prima2_codec_enable_and_reset_event,
+			SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD);
+
+static const struct snd_soc_dapm_widget sirf_audio_codec_dapm_widgets[] = {
+	SND_SOC_DAPM_DAC("DAC left", NULL, AUDIO_IC_CODEC_CTRL0, 1, 0),
+	SND_SOC_DAPM_DAC("DAC right", NULL, AUDIO_IC_CODEC_CTRL0, 0, 0),
+	SND_SOC_DAPM_SWITCH("Left dac to hp left amp", SND_SOC_NOPM, 0, 0,
+			&left_dac_to_hp_left_amp_switch_control),
+	SND_SOC_DAPM_SWITCH("Left dac to hp right amp", SND_SOC_NOPM, 0, 0,
+			&left_dac_to_hp_right_amp_switch_control),
+	SND_SOC_DAPM_SWITCH("Right dac to hp left amp", SND_SOC_NOPM, 0, 0,
+			&right_dac_to_hp_left_amp_switch_control),
+	SND_SOC_DAPM_SWITCH("Right dac to hp right amp", SND_SOC_NOPM, 0, 0,
+			&right_dac_to_hp_right_amp_switch_control),
+	SND_SOC_DAPM_OUT_DRV("HP amp left driver", AUDIO_IC_CODEC_CTRL0, 3, 0,
+			NULL, 0),
+	SND_SOC_DAPM_OUT_DRV("HP amp right driver", AUDIO_IC_CODEC_CTRL0, 3, 0,
+			NULL, 0),
+
+	SND_SOC_DAPM_SWITCH("Left dac to speaker lineout", SND_SOC_NOPM, 0, 0,
+			&left_dac_to_speaker_lineout_switch_control),
+	SND_SOC_DAPM_SWITCH("Right dac to speaker lineout", SND_SOC_NOPM, 0, 0,
+			&right_dac_to_speaker_lineout_switch_control),
+	SND_SOC_DAPM_OUT_DRV("Speaker amp driver", AUDIO_IC_CODEC_CTRL0, 4, 0,
+			NULL, 0),
+
+	SND_SOC_DAPM_OUTPUT("HPOUTL"),
+	SND_SOC_DAPM_OUTPUT("HPOUTR"),
+	SND_SOC_DAPM_OUTPUT("SPKOUT"),
+
+	SND_SOC_DAPM_ADC_E("ADC left", NULL, AUDIO_IC_CODEC_CTRL1, 8, 0,
+			adc_enable_delay_event, SND_SOC_DAPM_POST_PMU),
+	SND_SOC_DAPM_ADC_E("ADC right", NULL, AUDIO_IC_CODEC_CTRL1, 7, 0,
+			adc_enable_delay_event, SND_SOC_DAPM_POST_PMU),
+	SND_SOC_DAPM_MIXER("Left PGA mixer", AUDIO_IC_CODEC_CTRL1, 1, 0,
+		&left_input_path_controls[0],
+		ARRAY_SIZE(left_input_path_controls)),
+	SND_SOC_DAPM_MIXER("Right PGA mixer", AUDIO_IC_CODEC_CTRL1, 0, 0,
+		&right_input_path_controls[0],
+		ARRAY_SIZE(right_input_path_controls)),
+
+	SND_SOC_DAPM_MUX("Mic input mode mux", SND_SOC_NOPM, 0, 0,
+			&sirf_audio_codec_input_mode_control),
+	SND_SOC_DAPM_MICBIAS("Mic Bias", AUDIO_IC_CODEC_PWR, 3, 0),
+	SND_SOC_DAPM_INPUT("MICIN1"),
+	SND_SOC_DAPM_INPUT("MICIN2"),
+	SND_SOC_DAPM_INPUT("LINEIN1"),
+	SND_SOC_DAPM_INPUT("LINEIN2"),
+
+	SND_SOC_DAPM_SUPPLY("HSL Phase Opposite", AUDIO_IC_CODEC_CTRL0,
+			30, 0, NULL, 0),
+};
+
+static const struct snd_soc_dapm_route sirf_audio_codec_map[] = {
+	{"SPKOUT", NULL, "Speaker Driver"},
+	{"Speaker Driver", NULL, "Speaker amp driver"},
+	{"Speaker amp driver", NULL, "Left dac to speaker lineout"},
+	{"Speaker amp driver", NULL, "Right dac to speaker lineout"},
+	{"Left dac to speaker lineout", "Switch", "DAC left"},
+	{"Right dac to speaker lineout", "Switch", "DAC right"},
+	{"HPOUTL", NULL, "HP Left Driver"},
+	{"HPOUTR", NULL, "HP Right Driver"},
+	{"HP Left Driver", NULL, "HP amp left driver"},
+	{"HP Right Driver", NULL, "HP amp right driver"},
+	{"HP amp left driver", NULL, "Right dac to hp left amp"},
+	{"HP amp right driver", NULL , "Right dac to hp right amp"},
+	{"HP amp left driver", NULL, "Left dac to hp left amp"},
+	{"HP amp right driver", NULL , "Right dac to hp right amp"},
+	{"Right dac to hp left amp", "Switch", "DAC left"},
+	{"Right dac to hp right amp", "Switch", "DAC right"},
+	{"Left dac to hp left amp", "Switch", "DAC left"},
+	{"Left dac to hp right amp", "Switch", "DAC right"},
+	{"DAC left", NULL, "codecclk"},
+	{"DAC right", NULL, "codecclk"},
+	{"DAC left", NULL, "Playback"},
+	{"DAC right", NULL, "Playback"},
+	{"DAC left", NULL, "HSL Phase Opposite"},
+	{"DAC right", NULL, "HSL Phase Opposite"},
+
+	{"Capture", NULL, "ADC left"},
+	{"Capture", NULL, "ADC right"},
+	{"ADC left", NULL, "codecclk"},
+	{"ADC right", NULL, "codecclk"},
+	{"ADC left", NULL, "Left PGA mixer"},
+	{"ADC right", NULL, "Right PGA mixer"},
+	{"Left PGA mixer", "Line Left Switch", "LINEIN2"},
+	{"Right PGA mixer", "Line Right Switch", "LINEIN1"},
+	{"Left PGA mixer", "Mic Left Switch", "MICIN2"},
+	{"Right PGA mixer", "Mic Right Switch", "Mic input mode mux"},
+	{"Mic input mode mux", "Single-ended", "MICIN1"},
+	{"Mic input mode mux", "Differential", "MICIN1"},
+};
+
+static int sirf_audio_codec_trigger(struct snd_pcm_substream *substream,
+		int cmd,
+		struct snd_soc_dai *dai)
+{
+	int playback = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
+	struct snd_soc_codec *codec = dai->codec;
+	u32 val = 0;
+
+	/*
+	 * This is a workaround, When stop playback,
+	 * need disable HP amp, avoid the current noise.
+	 */
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_STOP:
+	case SNDRV_PCM_TRIGGER_SUSPEND:
+	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+		break;
+	case SNDRV_PCM_TRIGGER_START:
+	case SNDRV_PCM_TRIGGER_RESUME:
+	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+		if (playback)
+			val = IC_HSLEN | IC_HSREN;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	if (playback)
+		snd_soc_update_bits(codec, AUDIO_IC_CODEC_CTRL0,
+			IC_HSLEN | IC_HSREN, val);
+	return 0;
+}
+
+struct snd_soc_dai_ops sirf_audio_codec_dai_ops = {
+	.trigger = sirf_audio_codec_trigger,
+};
+
+struct snd_soc_dai_driver sirf_audio_codec_dai = {
+	.name = "sirf-audio-codec",
+	.playback = {
+		.stream_name = "Playback",
+		.channels_min = 2,
+		.channels_max = 2,
+		.rates = SNDRV_PCM_RATE_48000,
+		.formats = SNDRV_PCM_FMTBIT_S16_LE,
+	},
+	.capture = {
+		.stream_name = "Capture",
+		.channels_min = 1,
+		.channels_max = 2,
+		.rates = SNDRV_PCM_RATE_48000,
+		.formats = SNDRV_PCM_FMTBIT_S16_LE,
+	},
+	.ops = &sirf_audio_codec_dai_ops,
+};
+
+static int sirf_audio_codec_probe(struct snd_soc_codec *codec)
+{
+	int ret;
+	struct snd_soc_dapm_context *dapm = &codec->dapm;
+	struct sirf_audio_codec *sirf_audio_codec = snd_soc_codec_get_drvdata(codec);
+
+	pm_runtime_enable(codec->dev);
+	codec->control_data = sirf_audio_codec->regmap;
+
+	ret = snd_soc_codec_set_cache_io(codec, 0, 0, SND_SOC_REGMAP);
+	if (ret != 0) {
+		dev_err(codec->dev, "Failed to set cache I/O: %d\n", ret);
+		return ret;
+	}
+
+	if (of_device_is_compatible(codec->dev->of_node, "sirf,prima2-audio-codec")) {
+		snd_soc_dapm_new_controls(dapm,
+			prima2_output_driver_dapm_widgets,
+			ARRAY_SIZE(prima2_output_driver_dapm_widgets));
+		snd_soc_dapm_new_controls(dapm,
+			&prima2_codec_clock_dapm_widget, 1);
+		return snd_soc_add_codec_controls(codec,
+			volume_controls_prima2,
+			ARRAY_SIZE(volume_controls_prima2));
+	}
+	if (of_device_is_compatible(codec->dev->of_node, "sirf,atlas6-audio-codec")) {
+		snd_soc_dapm_new_controls(dapm,
+			atlas6_output_driver_dapm_widgets,
+			ARRAY_SIZE(atlas6_output_driver_dapm_widgets));
+		snd_soc_dapm_new_controls(dapm,
+			&atlas6_codec_clock_dapm_widget, 1);
+		return snd_soc_add_codec_controls(codec,
+			volume_controls_atlas6,
+			ARRAY_SIZE(volume_controls_atlas6));
+	}
+
+	return -EINVAL;
+}
+
+static int sirf_audio_codec_remove(struct snd_soc_codec *codec)
+{
+	pm_runtime_disable(codec->dev);
+	return 0;
+}
+
+static struct snd_soc_codec_driver soc_codec_device_sirf_audio_codec = {
+	.probe = sirf_audio_codec_probe,
+	.remove = sirf_audio_codec_remove,
+	.dapm_widgets = sirf_audio_codec_dapm_widgets,
+	.num_dapm_widgets = ARRAY_SIZE(sirf_audio_codec_dapm_widgets),
+	.dapm_routes = sirf_audio_codec_map,
+	.num_dapm_routes = ARRAY_SIZE(sirf_audio_codec_map),
+	.idle_bias_off = true,
+};
+
+static const struct of_device_id sirf_audio_codec_of_match[] = {
+	{ .compatible = "sirf,prima2-audio-codec" },
+	{ .compatible = "sirf,atlas6-audio-codec" },
+	{}
+};
+MODULE_DEVICE_TABLE(of, sirf_audio_codec_of_match);
+
+static const struct regmap_config sirf_audio_codec_regmap_config = {
+	.reg_bits = 32,
+	.reg_stride = 4,
+	.val_bits = 32,
+	.max_register = AUDIO_IC_CODEC_CTRL3,
+	.cache_type = REGCACHE_NONE,
+};
+
+static int sirf_audio_codec_driver_probe(struct platform_device *pdev)
+{
+	int ret;
+	struct sirf_audio_codec *sirf_audio_codec;
+	void __iomem *base;
+	struct resource *mem_res;
+	const struct of_device_id *match;
+
+	match = of_match_node(sirf_audio_codec_of_match, pdev->dev.of_node);
+
+	sirf_audio_codec = devm_kzalloc(&pdev->dev,
+		sizeof(struct sirf_audio_codec), GFP_KERNEL);
+	if (!sirf_audio_codec)
+		return -ENOMEM;
+
+	platform_set_drvdata(pdev, sirf_audio_codec);
+
+	mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	base = devm_ioremap_resource(&pdev->dev, mem_res);
+	if (base == NULL)
+		return -ENOMEM;
+
+	sirf_audio_codec->regmap = devm_regmap_init_mmio(&pdev->dev, base,
+					    &sirf_audio_codec_regmap_config);
+	if (IS_ERR(sirf_audio_codec->regmap))
+		return PTR_ERR(sirf_audio_codec->regmap);
+
+	sirf_audio_codec->clk = devm_clk_get(&pdev->dev, NULL);
+	if (IS_ERR(sirf_audio_codec->clk)) {
+		dev_err(&pdev->dev, "Get clock failed.\n");
+		return PTR_ERR(sirf_audio_codec->clk);
+	}
+
+	ret = clk_prepare_enable(sirf_audio_codec->clk);
+	if (ret) {
+		dev_err(&pdev->dev, "Enable clock failed.\n");
+		return ret;
+	}
+
+	ret = snd_soc_register_codec(&(pdev->dev),
+			&soc_codec_device_sirf_audio_codec,
+			&sirf_audio_codec_dai, 1);
+	if (ret) {
+		dev_err(&pdev->dev, "Register Audio Codec dai failed.\n");
+		goto err_clk_put;
+	}
+
+	/*
+	 * Always open charge pump, if not, when the charge pump closed the
+	 * adc will not stable
+	 */
+	regmap_update_bits(sirf_audio_codec->regmap, AUDIO_IC_CODEC_CTRL0,
+		IC_CPFREQ, IC_CPFREQ);
+
+	if (of_device_is_compatible(pdev->dev.of_node, "sirf,atlas6-audio-codec"))
+		regmap_update_bits(sirf_audio_codec->regmap,
+				AUDIO_IC_CODEC_CTRL0, IC_CPEN, IC_CPEN);
+	return 0;
+
+err_clk_put:
+	clk_disable_unprepare(sirf_audio_codec->clk);
+	return ret;
+}
+
+static int sirf_audio_codec_driver_remove(struct platform_device *pdev)
+{
+	struct sirf_audio_codec *sirf_audio_codec = platform_get_drvdata(pdev);
+
+	clk_disable_unprepare(sirf_audio_codec->clk);
+	snd_soc_unregister_codec(&(pdev->dev));
+
+	return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int sirf_audio_codec_suspend(struct device *dev)
+{
+	struct sirf_audio_codec *sirf_audio_codec = dev_get_drvdata(dev);
+
+	regmap_read(sirf_audio_codec->regmap, AUDIO_IC_CODEC_CTRL0,
+		&sirf_audio_codec->reg_ctrl0);
+	regmap_read(sirf_audio_codec->regmap, AUDIO_IC_CODEC_CTRL1,
+		&sirf_audio_codec->reg_ctrl1);
+	clk_disable_unprepare(sirf_audio_codec->clk);
+
+	return 0;
+}
+
+static int sirf_audio_codec_resume(struct device *dev)
+{
+	struct sirf_audio_codec *sirf_audio_codec = dev_get_drvdata(dev);
+	int ret;
+
+	ret = clk_prepare_enable(sirf_audio_codec->clk);
+	if (ret)
+		return ret;
+
+	regmap_write(sirf_audio_codec->regmap, AUDIO_IC_CODEC_CTRL0,
+		sirf_audio_codec->reg_ctrl0);
+	regmap_write(sirf_audio_codec->regmap, AUDIO_IC_CODEC_CTRL1,
+		sirf_audio_codec->reg_ctrl1);
+
+	return 0;
+}
+#endif
+
+static const struct dev_pm_ops sirf_audio_codec_pm_ops = {
+	SET_SYSTEM_SLEEP_PM_OPS(sirf_audio_codec_suspend, sirf_audio_codec_resume)
+};
+
+static struct platform_driver sirf_audio_codec_driver = {
+	.driver = {
+		.name = "sirf-audio-codec",
+		.owner = THIS_MODULE,
+		.of_match_table = sirf_audio_codec_of_match,
+		.pm = &sirf_audio_codec_pm_ops,
+	},
+	.probe = sirf_audio_codec_driver_probe,
+	.remove = sirf_audio_codec_driver_remove,
+};
+
+module_platform_driver(sirf_audio_codec_driver);
+
+MODULE_DESCRIPTION("SiRF audio codec driver");
+MODULE_AUTHOR("RongJun Ying <Rongjun.Ying@csr.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/sirf-audio-codec.h b/sound/soc/codecs/sirf-audio-codec.h
new file mode 100644
index 000000000000..d4c187b8e54a
--- /dev/null
+++ b/sound/soc/codecs/sirf-audio-codec.h
@@ -0,0 +1,75 @@
+/*
+ * SiRF inner codec controllers define
+ *
+ * Copyright (c) 2011 Cambridge Silicon Radio Limited, a CSR plc group company.
+ *
+ * Licensed under GPLv2 or later.
+ */
+
+#ifndef _SIRF_AUDIO_CODEC_H
+#define _SIRF_AUDIO_CODEC_H
+
+
+#define AUDIO_IC_CODEC_PWR			(0x00E0)
+#define AUDIO_IC_CODEC_CTRL0			(0x00E4)
+#define AUDIO_IC_CODEC_CTRL1			(0x00E8)
+#define AUDIO_IC_CODEC_CTRL2			(0x00EC)
+#define AUDIO_IC_CODEC_CTRL3			(0x00F0)
+
+#define MICBIASEN		(1 << 3)
+
+#define IC_RDACEN		(1 << 0)
+#define IC_LDACEN		(1 << 1)
+#define IC_HSREN		(1 << 2)
+#define IC_HSLEN		(1 << 3)
+#define IC_SPEN			(1 << 4)
+#define IC_CPEN			(1 << 5)
+
+#define IC_HPRSELR		(1 << 6)
+#define IC_HPLSELR		(1 << 7)
+#define IC_HPRSELL		(1 << 8)
+#define IC_HPLSELL		(1 << 9)
+#define IC_SPSELR		(1 << 10)
+#define IC_SPSELL		(1 << 11)
+
+#define IC_MONOR		(1 << 12)
+#define IC_MONOL		(1 << 13)
+
+#define IC_RXOSRSEL		(1 << 28)
+#define IC_CPFREQ		(1 << 29)
+#define IC_HSINVEN		(1 << 30)
+
+#define IC_MICINREN		(1 << 0)
+#define IC_MICINLEN		(1 << 1)
+#define IC_MICIN1SEL		(1 << 2)
+#define IC_MICIN2SEL		(1 << 3)
+#define IC_MICDIFSEL		(1 << 4)
+#define	IC_LINEIN1SEL		(1 << 5)
+#define	IC_LINEIN2SEL		(1 << 6)
+#define	IC_RADCEN		(1 << 7)
+#define	IC_LADCEN		(1 << 8)
+#define	IC_ALM			(1 << 9)
+
+#define IC_DIGMICEN             (1 << 22)
+#define IC_DIGMICFREQ           (1 << 23)
+#define IC_ADC14B_12            (1 << 24)
+#define IC_FIRDAC_HSL_EN        (1 << 25)
+#define IC_FIRDAC_HSR_EN        (1 << 26)
+#define IC_FIRDAC_LOUT_EN       (1 << 27)
+#define IC_POR                  (1 << 28)
+#define IC_CODEC_CLK_EN         (1 << 29)
+#define IC_HP_3DB_BOOST         (1 << 30)
+
+#define IC_ADC_LEFT_GAIN_SHIFT	16
+#define IC_ADC_RIGHT_GAIN_SHIFT 10
+#define IC_ADC_GAIN_MASK	0x3F
+#define IC_MIC_MAX_GAIN		0x39
+
+#define IC_RXPGAR_MASK		0x3F
+#define IC_RXPGAR_SHIFT		14
+#define IC_RXPGAL_MASK		0x3F
+#define IC_RXPGAL_SHIFT		21
+#define IC_RXPGAR		0x7B
+#define IC_RXPGAL		0x7B
+
+#endif /*__SIRF_AUDIO_CODEC_H*/
diff --git a/sound/soc/codecs/sn95031.c b/sound/soc/codecs/sn95031.c
index 13045f2af4d3..bca7d02b362a 100644
--- a/sound/soc/codecs/sn95031.c
+++ b/sound/soc/codecs/sn95031.c
@@ -312,14 +312,14 @@ static int sn95031_dmic56_event(struct snd_soc_dapm_widget *w,
 /* mux controls */
 static const char *sn95031_mic_texts[] = { "AMIC", "LineIn" };
 
-static const struct soc_enum sn95031_micl_enum =
-	SOC_ENUM_SINGLE(SN95031_ADCCONFIG, 1, 2, sn95031_mic_texts);
+static SOC_ENUM_SINGLE_DECL(sn95031_micl_enum,
+			    SN95031_ADCCONFIG, 1, sn95031_mic_texts);
 
 static const struct snd_kcontrol_new sn95031_micl_mux_control =
 	SOC_DAPM_ENUM("Route", sn95031_micl_enum);
 
-static const struct soc_enum sn95031_micr_enum =
-	SOC_ENUM_SINGLE(SN95031_ADCCONFIG, 3, 2, sn95031_mic_texts);
+static SOC_ENUM_SINGLE_DECL(sn95031_micr_enum,
+			    SN95031_ADCCONFIG, 3, sn95031_mic_texts);
 
 static const struct snd_kcontrol_new sn95031_micr_mux_control =
 	SOC_DAPM_ENUM("Route", sn95031_micr_enum);
@@ -328,26 +328,26 @@ static const char *sn95031_input_texts[] = {	"DMIC1", "DMIC2", "DMIC3",
 						"DMIC4", "DMIC5", "DMIC6",
 						"ADC Left", "ADC Right" };
 
-static const struct soc_enum sn95031_input1_enum =
-	SOC_ENUM_SINGLE(SN95031_AUDIOMUX12, 0, 8, sn95031_input_texts);
+static SOC_ENUM_SINGLE_DECL(sn95031_input1_enum,
+			    SN95031_AUDIOMUX12, 0, sn95031_input_texts);
 
 static const struct snd_kcontrol_new sn95031_input1_mux_control =
 	SOC_DAPM_ENUM("Route", sn95031_input1_enum);
 
-static const struct soc_enum sn95031_input2_enum =
-	SOC_ENUM_SINGLE(SN95031_AUDIOMUX12, 4, 8, sn95031_input_texts);
+static SOC_ENUM_SINGLE_DECL(sn95031_input2_enum,
+			    SN95031_AUDIOMUX12, 4, sn95031_input_texts);
 
 static const struct snd_kcontrol_new sn95031_input2_mux_control =
 	SOC_DAPM_ENUM("Route", sn95031_input2_enum);
 
-static const struct soc_enum sn95031_input3_enum =
-	SOC_ENUM_SINGLE(SN95031_AUDIOMUX34, 0, 8, sn95031_input_texts);
+static SOC_ENUM_SINGLE_DECL(sn95031_input3_enum,
+			    SN95031_AUDIOMUX34, 0, sn95031_input_texts);
 
 static const struct snd_kcontrol_new sn95031_input3_mux_control =
 	SOC_DAPM_ENUM("Route", sn95031_input3_enum);
 
-static const struct soc_enum sn95031_input4_enum =
-	SOC_ENUM_SINGLE(SN95031_AUDIOMUX34, 4, 8, sn95031_input_texts);
+static SOC_ENUM_SINGLE_DECL(sn95031_input4_enum,
+			    SN95031_AUDIOMUX34, 4, sn95031_input_texts);
 
 static const struct snd_kcontrol_new sn95031_input4_mux_control =
 	SOC_DAPM_ENUM("Route", sn95031_input4_enum);
@@ -359,19 +359,19 @@ static const char *sn95031_micmode_text[] = {"Single Ended", "Differential"};
 /* 0dB to 30dB in 10dB steps */
 static const DECLARE_TLV_DB_SCALE(mic_tlv, 0, 10, 0);
 
-static const struct soc_enum sn95031_micmode1_enum =
-	SOC_ENUM_SINGLE(SN95031_MICAMP1, 1, 2, sn95031_micmode_text);
-static const struct soc_enum sn95031_micmode2_enum =
-	SOC_ENUM_SINGLE(SN95031_MICAMP2, 1, 2, sn95031_micmode_text);
+static SOC_ENUM_SINGLE_DECL(sn95031_micmode1_enum,
+			    SN95031_MICAMP1, 1, sn95031_micmode_text);
+static SOC_ENUM_SINGLE_DECL(sn95031_micmode2_enum,
+			    SN95031_MICAMP2, 1, sn95031_micmode_text);
 
 static const char *sn95031_dmic_cfg_text[] = {"GPO", "DMIC"};
 
-static const struct soc_enum sn95031_dmic12_cfg_enum =
-	SOC_ENUM_SINGLE(SN95031_DMICMUX, 0, 2, sn95031_dmic_cfg_text);
-static const struct soc_enum sn95031_dmic34_cfg_enum =
-	SOC_ENUM_SINGLE(SN95031_DMICMUX, 1, 2, sn95031_dmic_cfg_text);
-static const struct soc_enum sn95031_dmic56_cfg_enum =
-	SOC_ENUM_SINGLE(SN95031_DMICMUX, 2, 2, sn95031_dmic_cfg_text);
+static SOC_ENUM_SINGLE_DECL(sn95031_dmic12_cfg_enum,
+			    SN95031_DMICMUX, 0, sn95031_dmic_cfg_text);
+static SOC_ENUM_SINGLE_DECL(sn95031_dmic34_cfg_enum,
+			    SN95031_DMICMUX, 1, sn95031_dmic_cfg_text);
+static SOC_ENUM_SINGLE_DECL(sn95031_dmic56_cfg_enum,
+			    SN95031_DMICMUX, 2, sn95031_dmic_cfg_text);
 
 static const struct snd_kcontrol_new sn95031_snd_controls[] = {
 	SOC_ENUM("Mic1Mode Capture Route", sn95031_micmode1_enum),
diff --git a/sound/soc/codecs/ssm2518.c b/sound/soc/codecs/ssm2518.c
index cc8debce752f..806f3d826ffb 100644
--- a/sound/soc/codecs/ssm2518.c
+++ b/sound/soc/codecs/ssm2518.c
@@ -169,19 +169,19 @@ static const char * const ssm2518_drc_hold_time_text[] = {
 	"682.24 ms", "1364 ms",
 };
 
-static const SOC_ENUM_SINGLE_DECL(ssm2518_drc_peak_detector_attack_time_enum,
+static SOC_ENUM_SINGLE_DECL(ssm2518_drc_peak_detector_attack_time_enum,
 	SSM2518_REG_DRC_2, 4, ssm2518_drc_peak_detector_attack_time_text);
-static const SOC_ENUM_SINGLE_DECL(ssm2518_drc_peak_detector_release_time_enum,
+static SOC_ENUM_SINGLE_DECL(ssm2518_drc_peak_detector_release_time_enum,
 	SSM2518_REG_DRC_2, 0, ssm2518_drc_peak_detector_release_time_text);
-static const SOC_ENUM_SINGLE_DECL(ssm2518_drc_attack_time_enum,
+static SOC_ENUM_SINGLE_DECL(ssm2518_drc_attack_time_enum,
 	SSM2518_REG_DRC_6, 4, ssm2518_drc_peak_detector_attack_time_text);
-static const SOC_ENUM_SINGLE_DECL(ssm2518_drc_decay_time_enum,
+static SOC_ENUM_SINGLE_DECL(ssm2518_drc_decay_time_enum,
 	SSM2518_REG_DRC_6, 0, ssm2518_drc_peak_detector_release_time_text);
-static const SOC_ENUM_SINGLE_DECL(ssm2518_drc_hold_time_enum,
+static SOC_ENUM_SINGLE_DECL(ssm2518_drc_hold_time_enum,
 	SSM2518_REG_DRC_7, 4, ssm2518_drc_hold_time_text);
-static const SOC_ENUM_SINGLE_DECL(ssm2518_drc_noise_gate_hold_time_enum,
+static SOC_ENUM_SINGLE_DECL(ssm2518_drc_noise_gate_hold_time_enum,
 	SSM2518_REG_DRC_7, 0, ssm2518_drc_hold_time_text);
-static const SOC_ENUM_SINGLE_DECL(ssm2518_drc_rms_averaging_time_enum,
+static SOC_ENUM_SINGLE_DECL(ssm2518_drc_rms_averaging_time_enum,
 	SSM2518_REG_DRC_9, 0, ssm2518_drc_peak_detector_release_time_text);
 
 static const struct snd_kcontrol_new ssm2518_snd_controls[] = {
diff --git a/sound/soc/codecs/ssm2602-i2c.c b/sound/soc/codecs/ssm2602-i2c.c
new file mode 100644
index 000000000000..abd63d537173
--- /dev/null
+++ b/sound/soc/codecs/ssm2602-i2c.c
@@ -0,0 +1,57 @@
+/*
+ * SSM2602/SSM2603/SSM2604 I2C audio driver
+ *
+ * Copyright 2014 Analog Devices Inc.
+ *
+ * Licensed under the GPL-2.
+ */
+
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/regmap.h>
+
+#include <sound/soc.h>
+
+#include "ssm2602.h"
+
+/*
+ * ssm2602 2 wire address is determined by GPIO5
+ * state during powerup.
+ *    low  = 0x1a
+ *    high = 0x1b
+ */
+static int ssm2602_i2c_probe(struct i2c_client *client,
+			     const struct i2c_device_id *id)
+{
+	return ssm2602_probe(&client->dev, id->driver_data,
+		devm_regmap_init_i2c(client, &ssm2602_regmap_config));
+}
+
+static int ssm2602_i2c_remove(struct i2c_client *client)
+{
+	snd_soc_unregister_codec(&client->dev);
+	return 0;
+}
+
+static const struct i2c_device_id ssm2602_i2c_id[] = {
+	{ "ssm2602", SSM2602 },
+	{ "ssm2603", SSM2602 },
+	{ "ssm2604", SSM2604 },
+	{ }
+};
+MODULE_DEVICE_TABLE(i2c, ssm2602_i2c_id);
+
+static struct i2c_driver ssm2602_i2c_driver = {
+	.driver = {
+		.name = "ssm2602",
+		.owner = THIS_MODULE,
+	},
+	.probe = ssm2602_i2c_probe,
+	.remove = ssm2602_i2c_remove,
+	.id_table = ssm2602_i2c_id,
+};
+module_i2c_driver(ssm2602_i2c_driver);
+
+MODULE_DESCRIPTION("ASoC SSM2602/SSM2603/SSM2604 I2C driver");
+MODULE_AUTHOR("Cliff Cai");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/ssm2602-spi.c b/sound/soc/codecs/ssm2602-spi.c
new file mode 100644
index 000000000000..2bf55e24a7bb
--- /dev/null
+++ b/sound/soc/codecs/ssm2602-spi.c
@@ -0,0 +1,41 @@
+/*
+ * SSM2602 SPI audio driver
+ *
+ * Copyright 2014 Analog Devices Inc.
+ *
+ * Licensed under the GPL-2.
+ */
+
+#include <linux/module.h>
+#include <linux/spi/spi.h>
+#include <linux/regmap.h>
+
+#include <sound/soc.h>
+
+#include "ssm2602.h"
+
+static int ssm2602_spi_probe(struct spi_device *spi)
+{
+	return ssm2602_probe(&spi->dev, SSM2602,
+		devm_regmap_init_spi(spi, &ssm2602_regmap_config));
+}
+
+static int ssm2602_spi_remove(struct spi_device *spi)
+{
+	snd_soc_unregister_codec(&spi->dev);
+	return 0;
+}
+
+static struct spi_driver ssm2602_spi_driver = {
+	.driver = {
+		.name	= "ssm2602",
+		.owner	= THIS_MODULE,
+	},
+	.probe		= ssm2602_spi_probe,
+	.remove		= ssm2602_spi_remove,
+};
+module_spi_driver(ssm2602_spi_driver);
+
+MODULE_DESCRIPTION("ASoC SSM2602 SPI driver");
+MODULE_AUTHOR("Cliff Cai");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/ssm2602.c b/sound/soc/codecs/ssm2602.c
index af76bbd1b24f..12947096897c 100644
--- a/sound/soc/codecs/ssm2602.c
+++ b/sound/soc/codecs/ssm2602.c
@@ -27,32 +27,20 @@
  */
 
 #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/spi/spi.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/initval.h>
 #include <sound/tlv.h>
 
 #include "ssm2602.h"
 
-enum ssm2602_type {
-	SSM2602,
-	SSM2604,
-};
-
 /* codec private data */
 struct ssm2602_priv {
 	unsigned int sysclk;
-	struct snd_pcm_hw_constraint_list *sysclk_constraints;
+	const struct snd_pcm_hw_constraint_list *sysclk_constraints;
 
 	struct regmap *regmap;
 
@@ -75,15 +63,16 @@ static const u16 ssm2602_reg[SSM2602_CACHEREGNUM] = {
 
 /*Appending several "None"s just for OSS mixer use*/
 static const char *ssm2602_input_select[] = {
-	"Line", "Mic", "None", "None", "None",
-	"None", "None", "None",
+	"Line", "Mic",
 };
 
 static const char *ssm2602_deemph[] = {"None", "32Khz", "44.1Khz", "48Khz"};
 
 static const struct soc_enum ssm2602_enum[] = {
-	SOC_ENUM_SINGLE(SSM2602_APANA, 2, 2, ssm2602_input_select),
-	SOC_ENUM_SINGLE(SSM2602_APDIGI, 1, 4, ssm2602_deemph),
+	SOC_ENUM_SINGLE(SSM2602_APANA, 2, ARRAY_SIZE(ssm2602_input_select),
+			ssm2602_input_select),
+	SOC_ENUM_SINGLE(SSM2602_APDIGI, 1, ARRAY_SIZE(ssm2602_deemph),
+			ssm2602_deemph),
 };
 
 static const unsigned int ssm260x_outmix_tlv[] = {
@@ -197,7 +186,7 @@ static const unsigned int ssm2602_rates_12288000[] = {
 	8000, 16000, 32000, 48000, 96000,
 };
 
-static struct snd_pcm_hw_constraint_list ssm2602_constraints_12288000 = {
+static const struct snd_pcm_hw_constraint_list ssm2602_constraints_12288000 = {
 	.list = ssm2602_rates_12288000,
 	.count = ARRAY_SIZE(ssm2602_rates_12288000),
 };
@@ -206,7 +195,7 @@ static const unsigned int ssm2602_rates_11289600[] = {
 	8000, 44100, 88200,
 };
 
-static struct snd_pcm_hw_constraint_list ssm2602_constraints_11289600 = {
+static const struct snd_pcm_hw_constraint_list ssm2602_constraints_11289600 = {
 	.list = ssm2602_rates_11289600,
 	.count = ARRAY_SIZE(ssm2602_rates_11289600),
 };
@@ -529,7 +518,7 @@ static int ssm2602_resume(struct snd_soc_codec *codec)
 	return 0;
 }
 
-static int ssm2602_probe(struct snd_soc_codec *codec)
+static int ssm2602_codec_probe(struct snd_soc_codec *codec)
 {
 	struct ssm2602_priv *ssm2602 = snd_soc_codec_get_drvdata(codec);
 	struct snd_soc_dapm_context *dapm = &codec->dapm;
@@ -554,7 +543,7 @@ static int ssm2602_probe(struct snd_soc_codec *codec)
 			ARRAY_SIZE(ssm2602_routes));
 }
 
-static int ssm2604_probe(struct snd_soc_codec *codec)
+static int ssm2604_codec_probe(struct snd_soc_codec *codec)
 {
 	struct snd_soc_dapm_context *dapm = &codec->dapm;
 	int ret;
@@ -568,7 +557,7 @@ static int ssm2604_probe(struct snd_soc_codec *codec)
 			ARRAY_SIZE(ssm2604_routes));
 }
 
-static int ssm260x_probe(struct snd_soc_codec *codec)
+static int ssm260x_codec_probe(struct snd_soc_codec *codec)
 {
 	struct ssm2602_priv *ssm2602 = snd_soc_codec_get_drvdata(codec);
 	int ret;
@@ -597,10 +586,10 @@ static int ssm260x_probe(struct snd_soc_codec *codec)
 
 	switch (ssm2602->type) {
 	case SSM2602:
-		ret = ssm2602_probe(codec);
+		ret = ssm2602_codec_probe(codec);
 		break;
 	case SSM2604:
-		ret = ssm2604_probe(codec);
+		ret = ssm2604_codec_probe(codec);
 		break;
 	}
 
@@ -620,7 +609,7 @@ static int ssm2602_remove(struct snd_soc_codec *codec)
 }
 
 static struct snd_soc_codec_driver soc_codec_dev_ssm2602 = {
-	.probe =	ssm260x_probe,
+	.probe =	ssm260x_codec_probe,
 	.remove =	ssm2602_remove,
 	.suspend =	ssm2602_suspend,
 	.resume =	ssm2602_resume,
@@ -639,7 +628,7 @@ static bool ssm2602_register_volatile(struct device *dev, unsigned int reg)
 	return reg == SSM2602_RESET;
 }
 
-static const struct regmap_config ssm2602_regmap_config = {
+const struct regmap_config ssm2602_regmap_config = {
 	.val_bits = 9,
 	.reg_bits = 7,
 
@@ -650,134 +639,28 @@ static const struct regmap_config ssm2602_regmap_config = {
 	.reg_defaults_raw = ssm2602_reg,
 	.num_reg_defaults_raw = ARRAY_SIZE(ssm2602_reg),
 };
+EXPORT_SYMBOL_GPL(ssm2602_regmap_config);
 
-#if defined(CONFIG_SPI_MASTER)
-static int ssm2602_spi_probe(struct spi_device *spi)
+int ssm2602_probe(struct device *dev, enum ssm2602_type type,
+	struct regmap *regmap)
 {
 	struct ssm2602_priv *ssm2602;
-	int ret;
-
-	ssm2602 = devm_kzalloc(&spi->dev, sizeof(struct ssm2602_priv),
-			       GFP_KERNEL);
-	if (ssm2602 == NULL)
-		return -ENOMEM;
-
-	spi_set_drvdata(spi, ssm2602);
-	ssm2602->type = SSM2602;
-
-	ssm2602->regmap = devm_regmap_init_spi(spi, &ssm2602_regmap_config);
-	if (IS_ERR(ssm2602->regmap))
-		return PTR_ERR(ssm2602->regmap);
-
-	ret = snd_soc_register_codec(&spi->dev,
-			&soc_codec_dev_ssm2602, &ssm2602_dai, 1);
-	return ret;
-}
 
-static int ssm2602_spi_remove(struct spi_device *spi)
-{
-	snd_soc_unregister_codec(&spi->dev);
-	return 0;
-}
-
-static struct spi_driver ssm2602_spi_driver = {
-	.driver = {
-		.name	= "ssm2602",
-		.owner	= THIS_MODULE,
-	},
-	.probe		= ssm2602_spi_probe,
-	.remove		= ssm2602_spi_remove,
-};
-#endif
-
-#if IS_ENABLED(CONFIG_I2C)
-/*
- * ssm2602 2 wire address is determined by GPIO5
- * state during powerup.
- *    low  = 0x1a
- *    high = 0x1b
- */
-static int ssm2602_i2c_probe(struct i2c_client *i2c,
-			     const struct i2c_device_id *id)
-{
-	struct ssm2602_priv *ssm2602;
-	int ret;
+	if (IS_ERR(regmap))
+		return PTR_ERR(regmap);
 
-	ssm2602 = devm_kzalloc(&i2c->dev, sizeof(struct ssm2602_priv),
-			       GFP_KERNEL);
+	ssm2602 = devm_kzalloc(dev, sizeof(*ssm2602), GFP_KERNEL);
 	if (ssm2602 == NULL)
 		return -ENOMEM;
 
-	i2c_set_clientdata(i2c, ssm2602);
-	ssm2602->type = id->driver_data;
-
-	ssm2602->regmap = devm_regmap_init_i2c(i2c, &ssm2602_regmap_config);
-	if (IS_ERR(ssm2602->regmap))
-		return PTR_ERR(ssm2602->regmap);
-
-	ret = snd_soc_register_codec(&i2c->dev,
-			&soc_codec_dev_ssm2602, &ssm2602_dai, 1);
-	return ret;
-}
-
-static int ssm2602_i2c_remove(struct i2c_client *client)
-{
-	snd_soc_unregister_codec(&client->dev);
-	return 0;
-}
-
-static const struct i2c_device_id ssm2602_i2c_id[] = {
-	{ "ssm2602", SSM2602 },
-	{ "ssm2603", SSM2602 },
-	{ "ssm2604", SSM2604 },
-	{ }
-};
-MODULE_DEVICE_TABLE(i2c, ssm2602_i2c_id);
-
-/* corgi i2c codec control layer */
-static struct i2c_driver ssm2602_i2c_driver = {
-	.driver = {
-		.name = "ssm2602",
-		.owner = THIS_MODULE,
-	},
-	.probe = ssm2602_i2c_probe,
-	.remove = ssm2602_i2c_remove,
-	.id_table = ssm2602_i2c_id,
-};
-#endif
-
-
-static int __init ssm2602_modinit(void)
-{
-	int ret = 0;
-
-#if defined(CONFIG_SPI_MASTER)
-	ret = spi_register_driver(&ssm2602_spi_driver);
-	if (ret)
-		return ret;
-#endif
-
-#if IS_ENABLED(CONFIG_I2C)
-	ret = i2c_add_driver(&ssm2602_i2c_driver);
-	if (ret)
-		return ret;
-#endif
-
-	return ret;
-}
-module_init(ssm2602_modinit);
-
-static void __exit ssm2602_exit(void)
-{
-#if defined(CONFIG_SPI_MASTER)
-	spi_unregister_driver(&ssm2602_spi_driver);
-#endif
+	dev_set_drvdata(dev, ssm2602);
+	ssm2602->type = SSM2602;
+	ssm2602->regmap = regmap;
 
-#if IS_ENABLED(CONFIG_I2C)
-	i2c_del_driver(&ssm2602_i2c_driver);
-#endif
+	return snd_soc_register_codec(dev, &soc_codec_dev_ssm2602,
+		&ssm2602_dai, 1);
 }
-module_exit(ssm2602_exit);
+EXPORT_SYMBOL_GPL(ssm2602_probe);
 
 MODULE_DESCRIPTION("ASoC SSM2602/SSM2603/SSM2604 driver");
 MODULE_AUTHOR("Cliff Cai");
diff --git a/sound/soc/codecs/ssm2602.h b/sound/soc/codecs/ssm2602.h
index fbd07d7b73ca..747538847689 100644
--- a/sound/soc/codecs/ssm2602.h
+++ b/sound/soc/codecs/ssm2602.h
@@ -28,6 +28,20 @@
 #ifndef _SSM2602_H
 #define _SSM2602_H
 
+#include <linux/regmap.h>
+
+struct device;
+
+enum ssm2602_type {
+	SSM2602,
+	SSM2604,
+};
+
+extern const struct regmap_config ssm2602_regmap_config;
+
+int ssm2602_probe(struct device *dev, enum ssm2602_type type,
+	struct regmap *regmap);
+
 /* SSM2602 Codec Register definitions */
 
 #define SSM2602_LINVOL   0x00
diff --git a/sound/soc/codecs/sta529.c b/sound/soc/codecs/sta529.c
index 40c07be9b581..f15b0e37274c 100644
--- a/sound/soc/codecs/sta529.c
+++ b/sound/soc/codecs/sta529.c
@@ -141,7 +141,7 @@ static const char *pwm_mode_text[] = { "Binary", "Headphone", "Ternary",
 
 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 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,
diff --git a/sound/soc/codecs/stac9766.c b/sound/soc/codecs/stac9766.c
index a5455c1aea42..53b810d23fea 100644
--- a/sound/soc/codecs/stac9766.c
+++ b/sound/soc/codecs/stac9766.c
@@ -62,25 +62,25 @@ static const char *stac9766_boost1[] = {"0dB", "10dB"};
 static const char *stac9766_boost2[] = {"0dB", "20dB"};
 static const char *stac9766_stereo_mic[] = {"Off", "On"};
 
-static const struct soc_enum stac9766_record_enum =
-	SOC_ENUM_DOUBLE(AC97_REC_SEL, 8, 0, 8, stac9766_record_mux);
-static const struct soc_enum stac9766_mono_enum =
-	SOC_ENUM_SINGLE(AC97_GENERAL_PURPOSE, 9, 2, stac9766_mono_mux);
-static const struct soc_enum stac9766_mic_enum =
-	SOC_ENUM_SINGLE(AC97_GENERAL_PURPOSE, 8, 2, stac9766_mic_mux);
-static const struct soc_enum stac9766_SPDIF_enum =
-	SOC_ENUM_SINGLE(AC97_STAC_DA_CONTROL, 1, 2, stac9766_SPDIF_mux);
-static const struct soc_enum stac9766_popbypass_enum =
-	SOC_ENUM_SINGLE(AC97_GENERAL_PURPOSE, 15, 2, stac9766_popbypass_mux);
-static const struct soc_enum stac9766_record_all_enum =
-	SOC_ENUM_SINGLE(AC97_STAC_ANALOG_SPECIAL, 12, 2,
-			stac9766_record_all_mux);
-static const struct soc_enum stac9766_boost1_enum =
-	SOC_ENUM_SINGLE(AC97_MIC, 6, 2, stac9766_boost1); /* 0/10dB */
-static const struct soc_enum stac9766_boost2_enum =
-	SOC_ENUM_SINGLE(AC97_STAC_ANALOG_SPECIAL, 2, 2, stac9766_boost2); /* 0/20dB */
-static const struct soc_enum stac9766_stereo_mic_enum =
-	SOC_ENUM_SINGLE(AC97_STAC_STEREO_MIC, 2, 1, stac9766_stereo_mic);
+static SOC_ENUM_DOUBLE_DECL(stac9766_record_enum,
+			    AC97_REC_SEL, 8, 0, stac9766_record_mux);
+static SOC_ENUM_SINGLE_DECL(stac9766_mono_enum,
+			    AC97_GENERAL_PURPOSE, 9, stac9766_mono_mux);
+static SOC_ENUM_SINGLE_DECL(stac9766_mic_enum,
+			    AC97_GENERAL_PURPOSE, 8, stac9766_mic_mux);
+static SOC_ENUM_SINGLE_DECL(stac9766_SPDIF_enum,
+			    AC97_STAC_DA_CONTROL, 1, stac9766_SPDIF_mux);
+static SOC_ENUM_SINGLE_DECL(stac9766_popbypass_enum,
+			    AC97_GENERAL_PURPOSE, 15, stac9766_popbypass_mux);
+static SOC_ENUM_SINGLE_DECL(stac9766_record_all_enum,
+			    AC97_STAC_ANALOG_SPECIAL, 12,
+			    stac9766_record_all_mux);
+static SOC_ENUM_SINGLE_DECL(stac9766_boost1_enum,
+			    AC97_MIC, 6, stac9766_boost1); /* 0/10dB */
+static SOC_ENUM_SINGLE_DECL(stac9766_boost2_enum,
+			    AC97_STAC_ANALOG_SPECIAL, 2, stac9766_boost2); /* 0/20dB */
+static SOC_ENUM_SINGLE_DECL(stac9766_stereo_mic_enum,
+			    AC97_STAC_STEREO_MIC, 2, stac9766_stereo_mic);
 
 static const DECLARE_TLV_DB_LINEAR(master_tlv, -4600, 0);
 static const DECLARE_TLV_DB_LINEAR(record_tlv, 0, 2250);
diff --git a/sound/soc/codecs/tlv320aic23-i2c.c b/sound/soc/codecs/tlv320aic23-i2c.c
new file mode 100644
index 000000000000..20fc46092c2c
--- /dev/null
+++ b/sound/soc/codecs/tlv320aic23-i2c.c
@@ -0,0 +1,59 @@
+/*
+ * ALSA SoC TLV320AIC23 codec driver I2C interface
+ *
+ * Author:      Arun KS, <arunks@mistralsolutions.com>
+ * Copyright:   (C) 2008 Mistral Solutions Pvt Ltd.,
+ *
+ * Based on sound/soc/codecs/wm8731.c by Richard Purdie
+ *
+ * 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/i2c.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+#include <sound/soc.h>
+
+#include "tlv320aic23.h"
+
+static int tlv320aic23_i2c_probe(struct i2c_client *i2c,
+				 const struct i2c_device_id *i2c_id)
+{
+	struct regmap *regmap;
+
+	if (!i2c_check_functionality(i2c->adapter, I2C_FUNC_SMBUS_BYTE_DATA))
+		return -EINVAL;
+
+	regmap = devm_regmap_init_i2c(i2c, &tlv320aic23_regmap);
+	return tlv320aic23_probe(&i2c->dev, regmap);
+}
+
+static int __exit tlv320aic23_i2c_remove(struct i2c_client *i2c)
+{
+	snd_soc_unregister_codec(&i2c->dev);
+	return 0;
+}
+
+static const struct i2c_device_id tlv320aic23_id[] = {
+	{"tlv320aic23", 0},
+	{}
+};
+
+MODULE_DEVICE_TABLE(i2c, tlv320aic23_id);
+
+static struct i2c_driver tlv320aic23_i2c_driver = {
+	.driver = {
+		   .name = "tlv320aic23-codec",
+		   },
+	.probe = tlv320aic23_i2c_probe,
+	.remove = __exit_p(tlv320aic23_i2c_remove),
+	.id_table = tlv320aic23_id,
+};
+
+module_i2c_driver(tlv320aic23_i2c_driver);
+
+MODULE_DESCRIPTION("ASoC TLV320AIC23 codec driver I2C");
+MODULE_AUTHOR("Arun KS <arunks@mistralsolutions.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/tlv320aic23-spi.c b/sound/soc/codecs/tlv320aic23-spi.c
new file mode 100644
index 000000000000..3b387e41d75d
--- /dev/null
+++ b/sound/soc/codecs/tlv320aic23-spi.c
@@ -0,0 +1,56 @@
+/*
+ * ALSA SoC TLV320AIC23 codec driver SPI interface
+ *
+ * Author:      Arun KS, <arunks@mistralsolutions.com>
+ * Copyright:   (C) 2008 Mistral Solutions Pvt Ltd.,
+ *
+ * Based on sound/soc/codecs/wm8731.c by Richard Purdie
+ *
+ * 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/regmap.h>
+#include <linux/spi/spi.h>
+#include <sound/soc.h>
+
+#include "tlv320aic23.h"
+
+static int aic23_spi_probe(struct spi_device *spi)
+{
+	int ret;
+	struct regmap *regmap;
+
+	dev_dbg(&spi->dev, "probing tlv320aic23 spi device\n");
+
+	spi->mode = SPI_MODE_0;
+	ret = spi_setup(spi);
+	if (ret < 0)
+		return ret;
+
+	regmap = devm_regmap_init_spi(spi, &tlv320aic23_regmap);
+	return tlv320aic23_probe(&spi->dev, regmap);
+}
+
+static int aic23_spi_remove(struct spi_device *spi)
+{
+	snd_soc_unregister_codec(&spi->dev);
+	return 0;
+}
+
+static struct spi_driver aic23_spi = {
+	.driver = {
+		.name = "tlv320aic23",
+		.owner = THIS_MODULE,
+	},
+	.probe = aic23_spi_probe,
+	.remove = aic23_spi_remove,
+};
+
+module_spi_driver(aic23_spi);
+
+MODULE_DESCRIPTION("ASoC TLV320AIC23 codec driver SPI");
+MODULE_AUTHOR("Arun KS <arunks@mistralsolutions.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/tlv320aic23.c b/sound/soc/codecs/tlv320aic23.c
index 5d430cc56f51..dc9a52fcb39a 100644
--- a/sound/soc/codecs/tlv320aic23.c
+++ b/sound/soc/codecs/tlv320aic23.c
@@ -23,7 +23,6 @@
 #include <linux/init.h>
 #include <linux/delay.h>
 #include <linux/pm.h>
-#include <linux/i2c.h>
 #include <linux/regmap.h>
 #include <linux/slab.h>
 #include <sound/core.h>
@@ -51,7 +50,7 @@ static const struct reg_default tlv320aic23_reg[] = {
 	{  9, 0x0000 },
 };
 
-static const struct regmap_config tlv320aic23_regmap = {
+const struct regmap_config tlv320aic23_regmap = {
 	.reg_bits = 7,
 	.val_bits = 9,
 
@@ -60,20 +59,21 @@ static const struct regmap_config tlv320aic23_regmap = {
 	.num_reg_defaults = ARRAY_SIZE(tlv320aic23_reg),
 	.cache_type = REGCACHE_RBTREE,
 };
+EXPORT_SYMBOL(tlv320aic23_regmap);
 
 static const char *rec_src_text[] = { "Line", "Mic" };
 static const char *deemph_text[] = {"None", "32Khz", "44.1Khz", "48Khz"};
 
-static const struct soc_enum rec_src_enum =
-	SOC_ENUM_SINGLE(TLV320AIC23_ANLG, 2, 2, rec_src_text);
+static SOC_ENUM_SINGLE_DECL(rec_src_enum,
+			    TLV320AIC23_ANLG, 2, rec_src_text);
 
 static const struct snd_kcontrol_new tlv320aic23_rec_src_mux_controls =
 SOC_DAPM_ENUM("Input Select", rec_src_enum);
 
-static const struct soc_enum tlv320aic23_rec_src =
-	SOC_ENUM_SINGLE(TLV320AIC23_ANLG, 2, 2, rec_src_text);
-static const struct soc_enum tlv320aic23_deemph =
-	SOC_ENUM_SINGLE(TLV320AIC23_DIGT, 1, 4, deemph_text);
+static SOC_ENUM_SINGLE_DECL(tlv320aic23_rec_src,
+			    TLV320AIC23_ANLG, 2, rec_src_text);
+static SOC_ENUM_SINGLE_DECL(tlv320aic23_deemph,
+			    TLV320AIC23_DIGT, 1, deemph_text);
 
 static const DECLARE_TLV_DB_SCALE(out_gain_tlv, -12100, 100, 0);
 static const DECLARE_TLV_DB_SCALE(input_gain_tlv, -1725, 75, 0);
@@ -400,7 +400,7 @@ static void tlv320aic23_shutdown(struct snd_pcm_substream *substream,
 	struct aic23 *aic23 = snd_soc_codec_get_drvdata(codec);
 
 	/* deactivate */
-	if (!codec->active) {
+	if (!snd_soc_codec_is_active(codec)) {
 		udelay(50);
 		snd_soc_write(codec, TLV320AIC23_ACTIVE, 0x0);
 	}
@@ -557,7 +557,7 @@ static int tlv320aic23_resume(struct snd_soc_codec *codec)
 	return 0;
 }
 
-static int tlv320aic23_probe(struct snd_soc_codec *codec)
+static int tlv320aic23_codec_probe(struct snd_soc_codec *codec)
 {
 	int ret;
 
@@ -604,7 +604,7 @@ static int tlv320aic23_remove(struct snd_soc_codec *codec)
 }
 
 static struct snd_soc_codec_driver soc_codec_dev_tlv320aic23 = {
-	.probe = tlv320aic23_probe,
+	.probe = tlv320aic23_codec_probe,
 	.remove = tlv320aic23_remove,
 	.suspend = tlv320aic23_suspend,
 	.resume = tlv320aic23_resume,
@@ -617,56 +617,25 @@ static struct snd_soc_codec_driver soc_codec_dev_tlv320aic23 = {
 	.num_dapm_routes = ARRAY_SIZE(tlv320aic23_intercon),
 };
 
-/*
- * If the i2c layer weren't so broken, we could pass this kind of data
- * around
- */
-static int tlv320aic23_codec_probe(struct i2c_client *i2c,
-				   const struct i2c_device_id *i2c_id)
+int tlv320aic23_probe(struct device *dev, struct regmap *regmap)
 {
 	struct aic23 *aic23;
-	int ret;
 
-	if (!i2c_check_functionality(i2c->adapter, I2C_FUNC_SMBUS_BYTE_DATA))
-		return -EINVAL;
+	if (IS_ERR(regmap))
+		return PTR_ERR(regmap);
 
-	aic23 = devm_kzalloc(&i2c->dev, sizeof(struct aic23), GFP_KERNEL);
+	aic23 = devm_kzalloc(dev, sizeof(struct aic23), GFP_KERNEL);
 	if (aic23 == NULL)
 		return -ENOMEM;
 
-	aic23->regmap = devm_regmap_init_i2c(i2c, &tlv320aic23_regmap);
-	if (IS_ERR(aic23->regmap))
-		return PTR_ERR(aic23->regmap);
+	aic23->regmap = regmap;
 
-	i2c_set_clientdata(i2c, aic23);
+	dev_set_drvdata(dev, aic23);
 
-	ret =  snd_soc_register_codec(&i2c->dev,
-			&soc_codec_dev_tlv320aic23, &tlv320aic23_dai, 1);
-	return ret;
-}
-static int __exit tlv320aic23_i2c_remove(struct i2c_client *i2c)
-{
-	snd_soc_unregister_codec(&i2c->dev);
-	return 0;
+	return snd_soc_register_codec(dev, &soc_codec_dev_tlv320aic23,
+				      &tlv320aic23_dai, 1);
 }
-
-static const struct i2c_device_id tlv320aic23_id[] = {
-	{"tlv320aic23", 0},
-	{}
-};
-
-MODULE_DEVICE_TABLE(i2c, tlv320aic23_id);
-
-static struct i2c_driver tlv320aic23_i2c_driver = {
-	.driver = {
-		   .name = "tlv320aic23-codec",
-		   },
-	.probe = tlv320aic23_codec_probe,
-	.remove = __exit_p(tlv320aic23_i2c_remove),
-	.id_table = tlv320aic23_id,
-};
-
-module_i2c_driver(tlv320aic23_i2c_driver);
+EXPORT_SYMBOL(tlv320aic23_probe);
 
 MODULE_DESCRIPTION("ASoC TLV320AIC23 codec driver");
 MODULE_AUTHOR("Arun KS <arunks@mistralsolutions.com>");
diff --git a/sound/soc/codecs/tlv320aic23.h b/sound/soc/codecs/tlv320aic23.h
index e804120bd3da..3a7235a04a89 100644
--- a/sound/soc/codecs/tlv320aic23.h
+++ b/sound/soc/codecs/tlv320aic23.h
@@ -12,6 +12,12 @@
 #ifndef _TLV320AIC23_H
 #define _TLV320AIC23_H
 
+struct device;
+struct regmap_config;
+
+extern const struct regmap_config tlv320aic23_regmap;
+int tlv320aic23_probe(struct device *dev, struct regmap *regmap);
+
 /* Codec TLV320AIC23 */
 #define TLV320AIC23_LINVOL		0x00
 #define TLV320AIC23_RINVOL		0x01
diff --git a/sound/soc/codecs/tlv320aic26.c b/sound/soc/codecs/tlv320aic26.c
index 94a658fa6d97..ff5f23d482b7 100644
--- a/sound/soc/codecs/tlv320aic26.c
+++ b/sound/soc/codecs/tlv320aic26.c
@@ -238,8 +238,9 @@ static struct snd_soc_dai_driver aic26_dai = {
  * ALSA controls
  */
 static const char *aic26_capture_src_text[] = {"Mic", "Aux"};
-static const struct soc_enum aic26_capture_src_enum =
-	SOC_ENUM_SINGLE(AIC26_REG_AUDIO_CTRL1, 12, 2, aic26_capture_src_text);
+static SOC_ENUM_SINGLE_DECL(aic26_capture_src_enum,
+			    AIC26_REG_AUDIO_CTRL1, 12,
+			    aic26_capture_src_text);
 
 static const struct snd_kcontrol_new aic26_snd_controls[] = {
 	/* Output */
diff --git a/sound/soc/codecs/tlv320aic32x4.c b/sound/soc/codecs/tlv320aic32x4.c
index 688151ba309a..c6bd7e75352d 100644
--- a/sound/soc/codecs/tlv320aic32x4.c
+++ b/sound/soc/codecs/tlv320aic32x4.c
@@ -29,9 +29,12 @@
 #include <linux/delay.h>
 #include <linux/pm.h>
 #include <linux/gpio.h>
+#include <linux/of_gpio.h>
 #include <linux/i2c.h>
 #include <linux/cdev.h>
 #include <linux/slab.h>
+#include <linux/clk.h>
+#include <linux/regulator/consumer.h>
 
 #include <sound/tlv320aic32x4.h>
 #include <sound/core.h>
@@ -66,20 +69,32 @@ struct aic32x4_priv {
 	u32 micpga_routing;
 	bool swapdacs;
 	int rstn_gpio;
+	struct clk *mclk;
+
+	struct regulator *supply_ldo;
+	struct regulator *supply_iov;
+	struct regulator *supply_dv;
+	struct regulator *supply_av;
 };
 
-/* 0dB min, 1dB steps */
-static DECLARE_TLV_DB_SCALE(tlv_step_1, 0, 100, 0);
 /* 0dB min, 0.5dB steps */
 static DECLARE_TLV_DB_SCALE(tlv_step_0_5, 0, 50, 0);
+/* -63.5dB min, 0.5dB steps */
+static DECLARE_TLV_DB_SCALE(tlv_pcm, -6350, 50, 0);
+/* -6dB min, 1dB steps */
+static DECLARE_TLV_DB_SCALE(tlv_driver_gain, -600, 100, 0);
+/* -12dB min, 0.5dB steps */
+static DECLARE_TLV_DB_SCALE(tlv_adc_vol, -1200, 50, 0);
 
 static const struct snd_kcontrol_new aic32x4_snd_controls[] = {
-	SOC_DOUBLE_R_TLV("PCM Playback Volume", AIC32X4_LDACVOL,
-			AIC32X4_RDACVOL, 0, 0x30, 0, tlv_step_0_5),
-	SOC_DOUBLE_R_TLV("HP Driver Gain Volume", AIC32X4_HPLGAIN,
-			AIC32X4_HPRGAIN, 0, 0x1D, 0, tlv_step_1),
-	SOC_DOUBLE_R_TLV("LO Driver Gain Volume", AIC32X4_LOLGAIN,
-			AIC32X4_LORGAIN, 0, 0x1D, 0, tlv_step_1),
+	SOC_DOUBLE_R_S_TLV("PCM Playback Volume", AIC32X4_LDACVOL,
+			AIC32X4_RDACVOL, 0, -0x7f, 0x30, 7, 0, tlv_pcm),
+	SOC_DOUBLE_R_S_TLV("HP Driver Gain Volume", AIC32X4_HPLGAIN,
+			AIC32X4_HPRGAIN, 0, -0x6, 0x1d, 5, 0,
+			tlv_driver_gain),
+	SOC_DOUBLE_R_S_TLV("LO Driver Gain Volume", AIC32X4_LOLGAIN,
+			AIC32X4_LORGAIN, 0, -0x6, 0x1d, 5, 0,
+			tlv_driver_gain),
 	SOC_DOUBLE_R("HP DAC Playback Switch", AIC32X4_HPLGAIN,
 			AIC32X4_HPRGAIN, 6, 0x01, 1),
 	SOC_DOUBLE_R("LO DAC Playback Switch", AIC32X4_LOLGAIN,
@@ -90,8 +105,8 @@ static const struct snd_kcontrol_new aic32x4_snd_controls[] = {
 	SOC_SINGLE("ADCFGA Left Mute Switch", AIC32X4_ADCFGA, 7, 1, 0),
 	SOC_SINGLE("ADCFGA Right Mute Switch", AIC32X4_ADCFGA, 3, 1, 0),
 
-	SOC_DOUBLE_R_TLV("ADC Level Volume", AIC32X4_LADCVOL,
-			AIC32X4_RADCVOL, 0, 0x28, 0, tlv_step_0_5),
+	SOC_DOUBLE_R_S_TLV("ADC Level Volume", AIC32X4_LADCVOL,
+			AIC32X4_RADCVOL, 0, -0x18, 0x28, 6, 0, tlv_adc_vol),
 	SOC_DOUBLE_R_TLV("PGA Level Volume", AIC32X4_LMICPGAVOL,
 			AIC32X4_RMICPGAVOL, 0, 0x5f, 0, tlv_step_0_5),
 
@@ -480,8 +495,18 @@ static int aic32x4_mute(struct snd_soc_dai *dai, int mute)
 static int aic32x4_set_bias_level(struct snd_soc_codec *codec,
 				  enum snd_soc_bias_level level)
 {
+	struct aic32x4_priv *aic32x4 = snd_soc_codec_get_drvdata(codec);
+	int ret;
+
 	switch (level) {
 	case SND_SOC_BIAS_ON:
+		/* Switch on master clock */
+		ret = clk_prepare_enable(aic32x4->mclk);
+		if (ret) {
+			dev_err(codec->dev, "Failed to enable master clock\n");
+			return ret;
+		}
+
 		/* Switch on PLL */
 		snd_soc_update_bits(codec, AIC32X4_PLLPR,
 				    AIC32X4_PLLEN, AIC32X4_PLLEN);
@@ -509,29 +534,32 @@ static int aic32x4_set_bias_level(struct snd_soc_codec *codec,
 	case SND_SOC_BIAS_PREPARE:
 		break;
 	case SND_SOC_BIAS_STANDBY:
-		/* Switch off PLL */
-		snd_soc_update_bits(codec, AIC32X4_PLLPR,
-				    AIC32X4_PLLEN, 0);
+		/* Switch off BCLK_N Divider */
+		snd_soc_update_bits(codec, AIC32X4_BCLKN,
+				    AIC32X4_BCLKEN, 0);
 
-		/* Switch off NDAC Divider */
-		snd_soc_update_bits(codec, AIC32X4_NDAC,
-				    AIC32X4_NDACEN, 0);
+		/* Switch off MADC Divider */
+		snd_soc_update_bits(codec, AIC32X4_MADC,
+				    AIC32X4_MADCEN, 0);
+
+		/* Switch off NADC Divider */
+		snd_soc_update_bits(codec, AIC32X4_NADC,
+				    AIC32X4_NADCEN, 0);
 
 		/* Switch off MDAC Divider */
 		snd_soc_update_bits(codec, AIC32X4_MDAC,
 				    AIC32X4_MDACEN, 0);
 
-		/* Switch off NADC Divider */
-		snd_soc_update_bits(codec, AIC32X4_NADC,
-				    AIC32X4_NADCEN, 0);
+		/* Switch off NDAC Divider */
+		snd_soc_update_bits(codec, AIC32X4_NDAC,
+				    AIC32X4_NDACEN, 0);
 
-		/* Switch off MADC Divider */
-		snd_soc_update_bits(codec, AIC32X4_MADC,
-				    AIC32X4_MADCEN, 0);
+		/* Switch off PLL */
+		snd_soc_update_bits(codec, AIC32X4_PLLPR,
+				    AIC32X4_PLLEN, 0);
 
-		/* Switch off BCLK_N Divider */
-		snd_soc_update_bits(codec, AIC32X4_BCLKN,
-				    AIC32X4_BCLKEN, 0);
+		/* Switch off master clock */
+		clk_disable_unprepare(aic32x4->mclk);
 		break;
 	case SND_SOC_BIAS_OFF:
 		break;
@@ -588,7 +616,7 @@ static int aic32x4_probe(struct snd_soc_codec *codec)
 
 	snd_soc_codec_set_cache_io(codec, 8, 8, SND_SOC_REGMAP);
 
-	if (aic32x4->rstn_gpio >= 0) {
+	if (gpio_is_valid(aic32x4->rstn_gpio)) {
 		ndelay(10);
 		gpio_set_value(aic32x4->rstn_gpio, 1);
 	}
@@ -663,11 +691,122 @@ static struct snd_soc_codec_driver soc_codec_dev_aic32x4 = {
 	.num_dapm_routes = ARRAY_SIZE(aic32x4_dapm_routes),
 };
 
+static int aic32x4_parse_dt(struct aic32x4_priv *aic32x4,
+		struct device_node *np)
+{
+	aic32x4->swapdacs = false;
+	aic32x4->micpga_routing = 0;
+	aic32x4->rstn_gpio = of_get_named_gpio(np, "reset-gpios", 0);
+
+	return 0;
+}
+
+static void aic32x4_disable_regulators(struct aic32x4_priv *aic32x4)
+{
+	regulator_disable(aic32x4->supply_iov);
+
+	if (!IS_ERR(aic32x4->supply_ldo))
+		regulator_disable(aic32x4->supply_ldo);
+
+	if (!IS_ERR(aic32x4->supply_dv))
+		regulator_disable(aic32x4->supply_dv);
+
+	if (!IS_ERR(aic32x4->supply_av))
+		regulator_disable(aic32x4->supply_av);
+}
+
+static int aic32x4_setup_regulators(struct device *dev,
+		struct aic32x4_priv *aic32x4)
+{
+	int ret = 0;
+
+	aic32x4->supply_ldo = devm_regulator_get_optional(dev, "ldoin");
+	aic32x4->supply_iov = devm_regulator_get(dev, "iov");
+	aic32x4->supply_dv = devm_regulator_get_optional(dev, "dv");
+	aic32x4->supply_av = devm_regulator_get_optional(dev, "av");
+
+	/* Check if the regulator requirements are fulfilled */
+
+	if (IS_ERR(aic32x4->supply_iov)) {
+		dev_err(dev, "Missing supply 'iov'\n");
+		return PTR_ERR(aic32x4->supply_iov);
+	}
+
+	if (IS_ERR(aic32x4->supply_ldo)) {
+		if (PTR_ERR(aic32x4->supply_ldo) == -EPROBE_DEFER)
+			return -EPROBE_DEFER;
+
+		if (IS_ERR(aic32x4->supply_dv)) {
+			dev_err(dev, "Missing supply 'dv' or 'ldoin'\n");
+			return PTR_ERR(aic32x4->supply_dv);
+		}
+		if (IS_ERR(aic32x4->supply_av)) {
+			dev_err(dev, "Missing supply 'av' or 'ldoin'\n");
+			return PTR_ERR(aic32x4->supply_av);
+		}
+	} else {
+		if (IS_ERR(aic32x4->supply_dv) &&
+				PTR_ERR(aic32x4->supply_dv) == -EPROBE_DEFER)
+			return -EPROBE_DEFER;
+		if (IS_ERR(aic32x4->supply_av) &&
+				PTR_ERR(aic32x4->supply_av) == -EPROBE_DEFER)
+			return -EPROBE_DEFER;
+	}
+
+	ret = regulator_enable(aic32x4->supply_iov);
+	if (ret) {
+		dev_err(dev, "Failed to enable regulator iov\n");
+		return ret;
+	}
+
+	if (!IS_ERR(aic32x4->supply_ldo)) {
+		ret = regulator_enable(aic32x4->supply_ldo);
+		if (ret) {
+			dev_err(dev, "Failed to enable regulator ldo\n");
+			goto error_ldo;
+		}
+	}
+
+	if (!IS_ERR(aic32x4->supply_dv)) {
+		ret = regulator_enable(aic32x4->supply_dv);
+		if (ret) {
+			dev_err(dev, "Failed to enable regulator dv\n");
+			goto error_dv;
+		}
+	}
+
+	if (!IS_ERR(aic32x4->supply_av)) {
+		ret = regulator_enable(aic32x4->supply_av);
+		if (ret) {
+			dev_err(dev, "Failed to enable regulator av\n");
+			goto error_av;
+		}
+	}
+
+	if (!IS_ERR(aic32x4->supply_ldo) && IS_ERR(aic32x4->supply_av))
+		aic32x4->power_cfg |= AIC32X4_PWR_AIC32X4_LDO_ENABLE;
+
+	return 0;
+
+error_av:
+	if (!IS_ERR(aic32x4->supply_dv))
+		regulator_disable(aic32x4->supply_dv);
+
+error_dv:
+	if (!IS_ERR(aic32x4->supply_ldo))
+		regulator_disable(aic32x4->supply_ldo);
+
+error_ldo:
+	regulator_disable(aic32x4->supply_iov);
+	return ret;
+}
+
 static int aic32x4_i2c_probe(struct i2c_client *i2c,
 			     const struct i2c_device_id *id)
 {
 	struct aic32x4_pdata *pdata = i2c->dev.platform_data;
 	struct aic32x4_priv *aic32x4;
+	struct device_node *np = i2c->dev.of_node;
 	int ret;
 
 	aic32x4 = devm_kzalloc(&i2c->dev, sizeof(struct aic32x4_priv),
@@ -686,6 +825,12 @@ static int aic32x4_i2c_probe(struct i2c_client *i2c,
 		aic32x4->swapdacs = pdata->swapdacs;
 		aic32x4->micpga_routing = pdata->micpga_routing;
 		aic32x4->rstn_gpio = pdata->rstn_gpio;
+	} else if (np) {
+		ret = aic32x4_parse_dt(aic32x4, np);
+		if (ret) {
+			dev_err(&i2c->dev, "Failed to parse DT node\n");
+			return ret;
+		}
 	} else {
 		aic32x4->power_cfg = 0;
 		aic32x4->swapdacs = false;
@@ -693,20 +838,44 @@ static int aic32x4_i2c_probe(struct i2c_client *i2c,
 		aic32x4->rstn_gpio = -1;
 	}
 
-	if (aic32x4->rstn_gpio >= 0) {
+	aic32x4->mclk = devm_clk_get(&i2c->dev, "mclk");
+	if (IS_ERR(aic32x4->mclk)) {
+		dev_err(&i2c->dev, "Failed getting the mclk. The current implementation does not support the usage of this codec without mclk\n");
+		return PTR_ERR(aic32x4->mclk);
+	}
+
+	if (gpio_is_valid(aic32x4->rstn_gpio)) {
 		ret = devm_gpio_request_one(&i2c->dev, aic32x4->rstn_gpio,
 				GPIOF_OUT_INIT_LOW, "tlv320aic32x4 rstn");
 		if (ret != 0)
 			return ret;
 	}
 
+	ret = aic32x4_setup_regulators(&i2c->dev, aic32x4);
+	if (ret) {
+		dev_err(&i2c->dev, "Failed to setup regulators\n");
+		return ret;
+	}
+
 	ret = snd_soc_register_codec(&i2c->dev,
 			&soc_codec_dev_aic32x4, &aic32x4_dai, 1);
-	return ret;
+	if (ret) {
+		dev_err(&i2c->dev, "Failed to register codec\n");
+		aic32x4_disable_regulators(aic32x4);
+		return ret;
+	}
+
+	i2c_set_clientdata(i2c, aic32x4);
+
+	return 0;
 }
 
 static int aic32x4_i2c_remove(struct i2c_client *client)
 {
+	struct aic32x4_priv *aic32x4 = i2c_get_clientdata(client);
+
+	aic32x4_disable_regulators(aic32x4);
+
 	snd_soc_unregister_codec(&client->dev);
 	return 0;
 }
@@ -717,10 +886,17 @@ static const struct i2c_device_id aic32x4_i2c_id[] = {
 };
 MODULE_DEVICE_TABLE(i2c, aic32x4_i2c_id);
 
+static const struct of_device_id aic32x4_of_id[] = {
+	{ .compatible = "ti,tlv320aic32x4", },
+	{ /* senitel */ }
+};
+MODULE_DEVICE_TABLE(of, aic32x4_of_id);
+
 static struct i2c_driver aic32x4_i2c_driver = {
 	.driver = {
 		.name = "tlv320aic32x4",
 		.owner = THIS_MODULE,
+		.of_match_table = aic32x4_of_id,
 	},
 	.probe =    aic32x4_i2c_probe,
 	.remove =   aic32x4_i2c_remove,
diff --git a/sound/soc/codecs/tlv320dac33.c b/sound/soc/codecs/tlv320dac33.c
index 4f358393d6d6..793516146670 100644
--- a/sound/soc/codecs/tlv320dac33.c
+++ b/sound/soc/codecs/tlv320dac33.c
@@ -461,7 +461,7 @@ static int dac33_set_fifo_mode(struct snd_kcontrol *kcontrol,
 	if (dac33->fifo_mode == ucontrol->value.integer.value[0])
 		return 0;
 	/* Do not allow changes while stream is running*/
-	if (codec->active)
+	if (snd_soc_codec_is_active(codec))
 		return -EPERM;
 
 	if (ucontrol->value.integer.value[0] < 0 ||
@@ -478,9 +478,7 @@ static const char *dac33_fifo_mode_texts[] = {
 	"Bypass", "Mode 1", "Mode 7"
 };
 
-static const struct soc_enum dac33_fifo_mode_enum =
-	SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(dac33_fifo_mode_texts),
-			    dac33_fifo_mode_texts);
+static SOC_ENUM_SINGLE_EXT_DECL(dac33_fifo_mode_enum, dac33_fifo_mode_texts);
 
 /* L/R Line Output Gain */
 static const char *lr_lineout_gain_texts[] = {
@@ -488,15 +486,13 @@ static const char *lr_lineout_gain_texts[] = {
 	"Line 0dB DAC 12dB", "Line 6dB DAC 18dB",
 };
 
-static const struct soc_enum l_lineout_gain_enum =
-	SOC_ENUM_SINGLE(DAC33_LDAC_PWR_CTRL, 0,
-			ARRAY_SIZE(lr_lineout_gain_texts),
-			lr_lineout_gain_texts);
+static SOC_ENUM_SINGLE_DECL(l_lineout_gain_enum,
+			    DAC33_LDAC_PWR_CTRL, 0,
+			    lr_lineout_gain_texts);
 
-static const struct soc_enum r_lineout_gain_enum =
-	SOC_ENUM_SINGLE(DAC33_RDAC_PWR_CTRL, 0,
-			ARRAY_SIZE(lr_lineout_gain_texts),
-			lr_lineout_gain_texts);
+static SOC_ENUM_SINGLE_DECL(r_lineout_gain_enum,
+			    DAC33_RDAC_PWR_CTRL, 0,
+			    lr_lineout_gain_texts);
 
 /*
  * DACL/R digital volume control:
@@ -534,18 +530,16 @@ static const struct snd_kcontrol_new dac33_dapm_abypassr_control =
 /* LOP L/R invert selection */
 static const char *dac33_lr_lom_texts[] = {"DAC", "LOP"};
 
-static const struct soc_enum dac33_left_lom_enum =
-	SOC_ENUM_SINGLE(DAC33_OUT_AMP_CTRL, 3,
-			ARRAY_SIZE(dac33_lr_lom_texts),
-			dac33_lr_lom_texts);
+static SOC_ENUM_SINGLE_DECL(dac33_left_lom_enum,
+			    DAC33_OUT_AMP_CTRL, 3,
+			    dac33_lr_lom_texts);
 
 static const struct snd_kcontrol_new dac33_dapm_left_lom_control =
 SOC_DAPM_ENUM("Route", dac33_left_lom_enum);
 
-static const struct soc_enum dac33_right_lom_enum =
-	SOC_ENUM_SINGLE(DAC33_OUT_AMP_CTRL, 2,
-			ARRAY_SIZE(dac33_lr_lom_texts),
-			dac33_lr_lom_texts);
+static SOC_ENUM_SINGLE_DECL(dac33_right_lom_enum,
+			    DAC33_OUT_AMP_CTRL, 2,
+			    dac33_lr_lom_texts);
 
 static const struct snd_kcontrol_new dac33_dapm_right_lom_control =
 SOC_DAPM_ENUM("Route", dac33_right_lom_enum);
diff --git a/sound/soc/codecs/twl4030.c b/sound/soc/codecs/twl4030.c
index 00665ada23e2..975e0f760ac1 100644
--- a/sound/soc/codecs/twl4030.c
+++ b/sound/soc/codecs/twl4030.c
@@ -415,10 +415,9 @@ static const struct snd_kcontrol_new twl4030_dapm_carkitr_controls[] = {
 static const char *twl4030_handsfreel_texts[] =
 		{"Voice", "AudioL1", "AudioL2", "AudioR2"};
 
-static const struct soc_enum twl4030_handsfreel_enum =
-	SOC_ENUM_SINGLE(TWL4030_REG_HFL_CTL, 0,
-			ARRAY_SIZE(twl4030_handsfreel_texts),
-			twl4030_handsfreel_texts);
+static SOC_ENUM_SINGLE_DECL(twl4030_handsfreel_enum,
+			    TWL4030_REG_HFL_CTL, 0,
+			    twl4030_handsfreel_texts);
 
 static const struct snd_kcontrol_new twl4030_dapm_handsfreel_control =
 SOC_DAPM_ENUM("Route", twl4030_handsfreel_enum);
@@ -431,10 +430,9 @@ static const struct snd_kcontrol_new twl4030_dapm_handsfreelmute_control =
 static const char *twl4030_handsfreer_texts[] =
 		{"Voice", "AudioR1", "AudioR2", "AudioL2"};
 
-static const struct soc_enum twl4030_handsfreer_enum =
-	SOC_ENUM_SINGLE(TWL4030_REG_HFR_CTL, 0,
-			ARRAY_SIZE(twl4030_handsfreer_texts),
-			twl4030_handsfreer_texts);
+static SOC_ENUM_SINGLE_DECL(twl4030_handsfreer_enum,
+			    TWL4030_REG_HFR_CTL, 0,
+			    twl4030_handsfreer_texts);
 
 static const struct snd_kcontrol_new twl4030_dapm_handsfreer_control =
 SOC_DAPM_ENUM("Route", twl4030_handsfreer_enum);
@@ -448,10 +446,9 @@ static const struct snd_kcontrol_new twl4030_dapm_handsfreermute_control =
 static const char *twl4030_vibra_texts[] =
 		{"AudioL1", "AudioR1", "AudioL2", "AudioR2"};
 
-static const struct soc_enum twl4030_vibra_enum =
-	SOC_ENUM_SINGLE(TWL4030_REG_VIBRA_CTL, 2,
-			ARRAY_SIZE(twl4030_vibra_texts),
-			twl4030_vibra_texts);
+static SOC_ENUM_SINGLE_DECL(twl4030_vibra_enum,
+			    TWL4030_REG_VIBRA_CTL, 2,
+			    twl4030_vibra_texts);
 
 static const struct snd_kcontrol_new twl4030_dapm_vibra_control =
 SOC_DAPM_ENUM("Route", twl4030_vibra_enum);
@@ -460,10 +457,9 @@ SOC_DAPM_ENUM("Route", twl4030_vibra_enum);
 static const char *twl4030_vibrapath_texts[] =
 		{"Local vibrator", "Audio"};
 
-static const struct soc_enum twl4030_vibrapath_enum =
-	SOC_ENUM_SINGLE(TWL4030_REG_VIBRA_CTL, 4,
-			ARRAY_SIZE(twl4030_vibrapath_texts),
-			twl4030_vibrapath_texts);
+static SOC_ENUM_SINGLE_DECL(twl4030_vibrapath_enum,
+			    TWL4030_REG_VIBRA_CTL, 4,
+			    twl4030_vibrapath_texts);
 
 static const struct snd_kcontrol_new twl4030_dapm_vibrapath_control =
 SOC_DAPM_ENUM("Route", twl4030_vibrapath_enum);
@@ -490,10 +486,9 @@ static const struct snd_kcontrol_new twl4030_dapm_analogrmic_controls[] = {
 static const char *twl4030_micpathtx1_texts[] =
 		{"Analog", "Digimic0"};
 
-static const struct soc_enum twl4030_micpathtx1_enum =
-	SOC_ENUM_SINGLE(TWL4030_REG_ADCMICSEL, 0,
-			ARRAY_SIZE(twl4030_micpathtx1_texts),
-			twl4030_micpathtx1_texts);
+static SOC_ENUM_SINGLE_DECL(twl4030_micpathtx1_enum,
+			    TWL4030_REG_ADCMICSEL, 0,
+			    twl4030_micpathtx1_texts);
 
 static const struct snd_kcontrol_new twl4030_dapm_micpathtx1_control =
 SOC_DAPM_ENUM("Route", twl4030_micpathtx1_enum);
@@ -502,10 +497,9 @@ SOC_DAPM_ENUM("Route", twl4030_micpathtx1_enum);
 static const char *twl4030_micpathtx2_texts[] =
 		{"Analog", "Digimic1"};
 
-static const struct soc_enum twl4030_micpathtx2_enum =
-	SOC_ENUM_SINGLE(TWL4030_REG_ADCMICSEL, 2,
-			ARRAY_SIZE(twl4030_micpathtx2_texts),
-			twl4030_micpathtx2_texts);
+static SOC_ENUM_SINGLE_DECL(twl4030_micpathtx2_enum,
+			    TWL4030_REG_ADCMICSEL, 2,
+			    twl4030_micpathtx2_texts);
 
 static const struct snd_kcontrol_new twl4030_dapm_micpathtx2_control =
 SOC_DAPM_ENUM("Route", twl4030_micpathtx2_enum);
@@ -955,19 +949,15 @@ static const char *twl4030_op_modes_texts[] = {
 	"Option 2 (voice/audio)", "Option 1 (audio)"
 };
 
-static const struct soc_enum twl4030_op_modes_enum =
-	SOC_ENUM_SINGLE(TWL4030_REG_CODEC_MODE, 0,
-			ARRAY_SIZE(twl4030_op_modes_texts),
-			twl4030_op_modes_texts);
+static SOC_ENUM_SINGLE_DECL(twl4030_op_modes_enum,
+			    TWL4030_REG_CODEC_MODE, 0,
+			    twl4030_op_modes_texts);
 
 static int snd_soc_put_twl4030_opmode_enum_double(struct snd_kcontrol *kcontrol,
 	struct snd_ctl_elem_value *ucontrol)
 {
 	struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
 	struct twl4030_priv *twl4030 = snd_soc_codec_get_drvdata(codec);
-	struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
-	unsigned short val;
-	unsigned short mask;
 
 	if (twl4030->configured) {
 		dev_err(codec->dev,
@@ -975,19 +965,7 @@ static int snd_soc_put_twl4030_opmode_enum_double(struct snd_kcontrol *kcontrol,
 		return -EBUSY;
 	}
 
-	if (ucontrol->value.enumerated.item[0] > e->max - 1)
-		return -EINVAL;
-
-	val = ucontrol->value.enumerated.item[0] << e->shift_l;
-	mask = e->mask << e->shift_l;
-	if (e->shift_l != e->shift_r) {
-		if (ucontrol->value.enumerated.item[1] > e->max - 1)
-			return -EINVAL;
-		val |= ucontrol->value.enumerated.item[1] << e->shift_r;
-		mask |= e->mask << e->shift_r;
-	}
-
-	return snd_soc_update_bits(codec, e->reg, mask, val);
+	return snd_soc_put_enum_double(kcontrol, ucontrol);
 }
 
 /*
@@ -1044,10 +1022,9 @@ static const char *twl4030_avadc_clk_priority_texts[] = {
 	"Voice high priority", "HiFi high priority"
 };
 
-static const struct soc_enum twl4030_avadc_clk_priority_enum =
-	SOC_ENUM_SINGLE(TWL4030_REG_AVADC_CTL, 2,
-			ARRAY_SIZE(twl4030_avadc_clk_priority_texts),
-			twl4030_avadc_clk_priority_texts);
+static SOC_ENUM_SINGLE_DECL(twl4030_avadc_clk_priority_enum,
+			    TWL4030_REG_AVADC_CTL, 2,
+			    twl4030_avadc_clk_priority_texts);
 
 static const char *twl4030_rampdelay_texts[] = {
 	"27/20/14 ms", "55/40/27 ms", "109/81/55 ms", "218/161/109 ms",
@@ -1055,40 +1032,36 @@ static const char *twl4030_rampdelay_texts[] = {
 	"3495/2581/1748 ms"
 };
 
-static const struct soc_enum twl4030_rampdelay_enum =
-	SOC_ENUM_SINGLE(TWL4030_REG_HS_POPN_SET, 2,
-			ARRAY_SIZE(twl4030_rampdelay_texts),
-			twl4030_rampdelay_texts);
+static SOC_ENUM_SINGLE_DECL(twl4030_rampdelay_enum,
+			    TWL4030_REG_HS_POPN_SET, 2,
+			    twl4030_rampdelay_texts);
 
 /* Vibra H-bridge direction mode */
 static const char *twl4030_vibradirmode_texts[] = {
 	"Vibra H-bridge direction", "Audio data MSB",
 };
 
-static const struct soc_enum twl4030_vibradirmode_enum =
-	SOC_ENUM_SINGLE(TWL4030_REG_VIBRA_CTL, 5,
-			ARRAY_SIZE(twl4030_vibradirmode_texts),
-			twl4030_vibradirmode_texts);
+static SOC_ENUM_SINGLE_DECL(twl4030_vibradirmode_enum,
+			    TWL4030_REG_VIBRA_CTL, 5,
+			    twl4030_vibradirmode_texts);
 
 /* Vibra H-bridge direction */
 static const char *twl4030_vibradir_texts[] = {
 	"Positive polarity", "Negative polarity",
 };
 
-static const struct soc_enum twl4030_vibradir_enum =
-	SOC_ENUM_SINGLE(TWL4030_REG_VIBRA_CTL, 1,
-			ARRAY_SIZE(twl4030_vibradir_texts),
-			twl4030_vibradir_texts);
+static SOC_ENUM_SINGLE_DECL(twl4030_vibradir_enum,
+			    TWL4030_REG_VIBRA_CTL, 1,
+			    twl4030_vibradir_texts);
 
 /* Digimic Left and right swapping */
 static const char *twl4030_digimicswap_texts[] = {
 	"Not swapped", "Swapped",
 };
 
-static const struct soc_enum twl4030_digimicswap_enum =
-	SOC_ENUM_SINGLE(TWL4030_REG_MISC_SET_1, 0,
-			ARRAY_SIZE(twl4030_digimicswap_texts),
-			twl4030_digimicswap_texts);
+static SOC_ENUM_SINGLE_DECL(twl4030_digimicswap_enum,
+			    TWL4030_REG_MISC_SET_1, 0,
+			    twl4030_digimicswap_texts);
 
 static const struct snd_kcontrol_new twl4030_snd_controls[] = {
 	/* Codec operation mode control */
diff --git a/sound/soc/codecs/twl6040.c b/sound/soc/codecs/twl6040.c
index 0afe8bef6765..bd3a20647fdf 100644
--- a/sound/soc/codecs/twl6040.c
+++ b/sound/soc/codecs/twl6040.c
@@ -81,7 +81,7 @@ struct twl6040_data {
 };
 
 /* set of rates for each pll: low-power and high-performance */
-static unsigned int lp_rates[] = {
+static const unsigned int lp_rates[] = {
 	8000,
 	11250,
 	16000,
@@ -93,7 +93,7 @@ static unsigned int lp_rates[] = {
 	96000,
 };
 
-static unsigned int hp_rates[] = {
+static const unsigned int hp_rates[] = {
 	8000,
 	16000,
 	32000,
@@ -101,7 +101,7 @@ static unsigned int hp_rates[] = {
 	96000,
 };
 
-static struct snd_pcm_hw_constraint_list sysclk_constraints[] = {
+static const struct snd_pcm_hw_constraint_list sysclk_constraints[] = {
 	{ .count = ARRAY_SIZE(lp_rates), .list = lp_rates, },
 	{ .count = ARRAY_SIZE(hp_rates), .list = hp_rates, },
 };
@@ -392,8 +392,10 @@ static const char *twl6040_amicr_texts[] =
 	{"Headset Mic", "Sub Mic", "Aux/FM Right", "Off"};
 
 static const struct soc_enum twl6040_enum[] = {
-	SOC_ENUM_SINGLE(TWL6040_REG_MICLCTL, 3, 4, twl6040_amicl_texts),
-	SOC_ENUM_SINGLE(TWL6040_REG_MICRCTL, 3, 4, twl6040_amicr_texts),
+	SOC_ENUM_SINGLE(TWL6040_REG_MICLCTL, 3,
+			ARRAY_SIZE(twl6040_amicl_texts), twl6040_amicl_texts),
+	SOC_ENUM_SINGLE(TWL6040_REG_MICRCTL, 3,
+			ARRAY_SIZE(twl6040_amicr_texts), twl6040_amicr_texts),
 };
 
 static const char *twl6040_hs_texts[] = {
@@ -476,9 +478,8 @@ static const char *twl6040_power_mode_texts[] = {
 	"Low-Power", "High-Performance",
 };
 
-static const struct soc_enum twl6040_power_mode_enum =
-	SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(twl6040_power_mode_texts),
-			twl6040_power_mode_texts);
+static SOC_ENUM_SINGLE_EXT_DECL(twl6040_power_mode_enum,
+				twl6040_power_mode_texts);
 
 static int twl6040_headset_power_get_enum(struct snd_kcontrol *kcontrol,
 	struct snd_ctl_elem_value *ucontrol)
diff --git a/sound/soc/codecs/uda1380.c b/sound/soc/codecs/uda1380.c
index 726df6d43c2b..4dadaa8ad46c 100644
--- a/sound/soc/codecs/uda1380.c
+++ b/sound/soc/codecs/uda1380.c
@@ -108,7 +108,7 @@ static int uda1380_write(struct snd_soc_codec *codec, unsigned int reg,
 	/* the interpolator & decimator regs must only be written when the
 	 * codec DAI is active.
 	 */
-	if (!codec->active && (reg >= UDA1380_MVOL))
+	if (!snd_soc_codec_is_active(codec) && (reg >= UDA1380_MVOL))
 		return 0;
 	pr_debug("uda1380: hw write %x val %x\n", reg, value);
 	if (codec->hw_write(codec->control_data, data, 3) == 3) {
@@ -237,25 +237,27 @@ static const char *uda1380_os_setting[] = {
 };
 
 static const struct soc_enum uda1380_deemp_enum[] = {
-	SOC_ENUM_SINGLE(UDA1380_DEEMP, 8, 5, uda1380_deemp),
-	SOC_ENUM_SINGLE(UDA1380_DEEMP, 0, 5, uda1380_deemp),
+	SOC_ENUM_SINGLE(UDA1380_DEEMP, 8, ARRAY_SIZE(uda1380_deemp),
+			uda1380_deemp),
+	SOC_ENUM_SINGLE(UDA1380_DEEMP, 0, ARRAY_SIZE(uda1380_deemp),
+			uda1380_deemp),
 };
-static const struct soc_enum uda1380_input_sel_enum =
-	SOC_ENUM_SINGLE(UDA1380_ADC, 2, 4, uda1380_input_sel);		/* SEL_MIC, SEL_LNA */
-static const struct soc_enum uda1380_output_sel_enum =
-	SOC_ENUM_SINGLE(UDA1380_PM, 7, 2, uda1380_output_sel);		/* R02_EN_AVC */
-static const struct soc_enum uda1380_spf_enum =
-	SOC_ENUM_SINGLE(UDA1380_MODE, 14, 4, uda1380_spf_mode);		/* M */
-static const struct soc_enum uda1380_capture_sel_enum =
-	SOC_ENUM_SINGLE(UDA1380_IFACE, 6, 2, uda1380_capture_sel);	/* SEL_SOURCE */
-static const struct soc_enum uda1380_sel_ns_enum =
-	SOC_ENUM_SINGLE(UDA1380_MIXER, 14, 2, uda1380_sel_ns);		/* SEL_NS */
-static const struct soc_enum uda1380_mix_enum =
-	SOC_ENUM_SINGLE(UDA1380_MIXER, 12, 4, uda1380_mix_control);	/* MIX, MIX_POS */
-static const struct soc_enum uda1380_sdet_enum =
-	SOC_ENUM_SINGLE(UDA1380_MIXER, 4, 4, uda1380_sdet_setting);	/* SD_VALUE */
-static const struct soc_enum uda1380_os_enum =
-	SOC_ENUM_SINGLE(UDA1380_MIXER, 0, 3, uda1380_os_setting);	/* OS */
+static SOC_ENUM_SINGLE_DECL(uda1380_input_sel_enum,
+			    UDA1380_ADC, 2, uda1380_input_sel);		/* SEL_MIC, SEL_LNA */
+static SOC_ENUM_SINGLE_DECL(uda1380_output_sel_enum,
+			    UDA1380_PM, 7, uda1380_output_sel);		/* R02_EN_AVC */
+static SOC_ENUM_SINGLE_DECL(uda1380_spf_enum,
+			    UDA1380_MODE, 14, uda1380_spf_mode);		/* M */
+static SOC_ENUM_SINGLE_DECL(uda1380_capture_sel_enum,
+			    UDA1380_IFACE, 6, uda1380_capture_sel);	/* SEL_SOURCE */
+static SOC_ENUM_SINGLE_DECL(uda1380_sel_ns_enum,
+			    UDA1380_MIXER, 14, uda1380_sel_ns);		/* SEL_NS */
+static SOC_ENUM_SINGLE_DECL(uda1380_mix_enum,
+			    UDA1380_MIXER, 12, uda1380_mix_control);	/* MIX, MIX_POS */
+static SOC_ENUM_SINGLE_DECL(uda1380_sdet_enum,
+			    UDA1380_MIXER, 4, uda1380_sdet_setting);	/* SD_VALUE */
+static SOC_ENUM_SINGLE_DECL(uda1380_os_enum,
+			    UDA1380_MIXER, 0, uda1380_os_setting);	/* OS */
 
 /*
  * from -48 dB in 1.5 dB steps (mute instead of -49.5 dB)
diff --git a/sound/soc/codecs/wl1273.c b/sound/soc/codecs/wl1273.c
index b7ab2ef567c8..6be5f80b65f1 100644
--- a/sound/soc/codecs/wl1273.c
+++ b/sound/soc/codecs/wl1273.c
@@ -197,7 +197,7 @@ static int snd_wl1273_set_audio_route(struct snd_kcontrol *kcontrol,
 		return 0;
 
 	/* Do not allow changes while stream is running */
-	if (codec->active)
+	if (snd_soc_codec_is_active(codec))
 		return -EPERM;
 
 	if (ucontrol->value.integer.value[0] < 0 ||
@@ -209,8 +209,7 @@ static int snd_wl1273_set_audio_route(struct snd_kcontrol *kcontrol,
 	return 1;
 }
 
-static const struct soc_enum wl1273_enum =
-	SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(wl1273_audio_route), wl1273_audio_route);
+static SOC_ENUM_SINGLE_EXT_DECL(wl1273_enum, wl1273_audio_route);
 
 static int snd_wl1273_fm_audio_get(struct snd_kcontrol *kcontrol,
 				   struct snd_ctl_elem_value *ucontrol)
@@ -247,9 +246,7 @@ static int snd_wl1273_fm_audio_put(struct snd_kcontrol *kcontrol,
 
 static const char * const wl1273_audio_strings[] = { "Digital", "Analog" };
 
-static const struct soc_enum wl1273_audio_enum =
-	SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(wl1273_audio_strings),
-			    wl1273_audio_strings);
+static SOC_ENUM_SINGLE_EXT_DECL(wl1273_audio_enum, wl1273_audio_strings);
 
 static int snd_wl1273_fm_volume_get(struct snd_kcontrol *kcontrol,
 				    struct snd_ctl_elem_value *ucontrol)
diff --git a/sound/soc/codecs/wm2200.c b/sound/soc/codecs/wm2200.c
index 57ba315d0c84..1e0a083d8345 100644
--- a/sound/soc/codecs/wm2200.c
+++ b/sound/soc/codecs/wm2200.c
@@ -1113,11 +1113,10 @@ static const char *wm2200_rxanc_input_sel_texts[] = {
 	"None", "IN1", "IN2", "IN3",
 };
 
-static const struct soc_enum wm2200_rxanc_input_sel =
-	SOC_ENUM_SINGLE(WM2200_RXANC_SRC,
-			WM2200_IN_RXANC_SEL_SHIFT,
-			ARRAY_SIZE(wm2200_rxanc_input_sel_texts),
-			wm2200_rxanc_input_sel_texts);
+static SOC_ENUM_SINGLE_DECL(wm2200_rxanc_input_sel,
+			    WM2200_RXANC_SRC,
+			    WM2200_IN_RXANC_SEL_SHIFT,
+			    wm2200_rxanc_input_sel_texts);
 
 static const struct snd_kcontrol_new wm2200_snd_controls[] = {
 SOC_SINGLE("IN1 High Performance Switch", WM2200_IN1L_CONTROL,
@@ -1288,11 +1287,10 @@ static const char *wm2200_aec_loopback_texts[] = {
 	"OUT1L", "OUT1R", "OUT2L", "OUT2R",
 };
 
-static const struct soc_enum wm2200_aec_loopback =
-	SOC_ENUM_SINGLE(WM2200_DAC_AEC_CONTROL_1,
-			WM2200_AEC_LOOPBACK_SRC_SHIFT,
-			ARRAY_SIZE(wm2200_aec_loopback_texts),
-			wm2200_aec_loopback_texts);
+static SOC_ENUM_SINGLE_DECL(wm2200_aec_loopback,
+			    WM2200_DAC_AEC_CONTROL_1,
+			    WM2200_AEC_LOOPBACK_SRC_SHIFT,
+			    wm2200_aec_loopback_texts);
 
 static const struct snd_kcontrol_new wm2200_aec_loopback_mux =
 	SOC_DAPM_ENUM("AEC Loopback", wm2200_aec_loopback);
diff --git a/sound/soc/codecs/wm5100.c b/sound/soc/codecs/wm5100.c
index 4e3e31aaf509..d3fa65fd9e85 100644
--- a/sound/soc/codecs/wm5100.c
+++ b/sound/soc/codecs/wm5100.c
@@ -506,21 +506,21 @@ static const char *wm5100_lhpf_mode_text[] = {
 	"Low-pass", "High-pass"
 };
 
-static const struct soc_enum wm5100_lhpf1_mode =
-	SOC_ENUM_SINGLE(WM5100_HPLPF1_1, WM5100_LHPF1_MODE_SHIFT, 2,
-			wm5100_lhpf_mode_text);
+static SOC_ENUM_SINGLE_DECL(wm5100_lhpf1_mode,
+			    WM5100_HPLPF1_1, WM5100_LHPF1_MODE_SHIFT,
+			    wm5100_lhpf_mode_text);
 
-static const struct soc_enum wm5100_lhpf2_mode =
-	SOC_ENUM_SINGLE(WM5100_HPLPF2_1, WM5100_LHPF2_MODE_SHIFT, 2,
-			wm5100_lhpf_mode_text);
+static SOC_ENUM_SINGLE_DECL(wm5100_lhpf2_mode,
+			    WM5100_HPLPF2_1, WM5100_LHPF2_MODE_SHIFT,
+			    wm5100_lhpf_mode_text);
 
-static const struct soc_enum wm5100_lhpf3_mode =
-	SOC_ENUM_SINGLE(WM5100_HPLPF3_1, WM5100_LHPF3_MODE_SHIFT, 2,
-			wm5100_lhpf_mode_text);
+static SOC_ENUM_SINGLE_DECL(wm5100_lhpf3_mode,
+			    WM5100_HPLPF3_1, WM5100_LHPF3_MODE_SHIFT,
+			    wm5100_lhpf_mode_text);
 
-static const struct soc_enum wm5100_lhpf4_mode =
-	SOC_ENUM_SINGLE(WM5100_HPLPF4_1, WM5100_LHPF4_MODE_SHIFT, 2,
-			wm5100_lhpf_mode_text);
+static SOC_ENUM_SINGLE_DECL(wm5100_lhpf4_mode,
+			    WM5100_HPLPF4_1, WM5100_LHPF4_MODE_SHIFT,
+			    wm5100_lhpf_mode_text);
 
 static const struct snd_kcontrol_new wm5100_snd_controls[] = {
 SOC_SINGLE("IN1 High Performance Switch", WM5100_IN1L_CONTROL,
@@ -2100,6 +2100,7 @@ static void wm5100_micd_irq(struct wm5100_priv *wm5100)
 int wm5100_detect(struct snd_soc_codec *codec, struct snd_soc_jack *jack)
 {
 	struct wm5100_priv *wm5100 = snd_soc_codec_get_drvdata(codec);
+	struct snd_soc_dapm_context *dapm = &codec->dapm;
 
 	if (jack) {
 		wm5100->jack = jack;
@@ -2117,9 +2118,14 @@ int wm5100_detect(struct snd_soc_codec *codec, struct snd_soc_jack *jack)
 				    WM5100_ACCDET_RATE_MASK);
 
 		/* We need the charge pump to power MICBIAS */
-		snd_soc_dapm_force_enable_pin(&codec->dapm, "CP2");
-		snd_soc_dapm_force_enable_pin(&codec->dapm, "SYSCLK");
-		snd_soc_dapm_sync(&codec->dapm);
+		snd_soc_dapm_mutex_lock(dapm);
+
+		snd_soc_dapm_force_enable_pin_unlocked(dapm, "CP2");
+		snd_soc_dapm_force_enable_pin_unlocked(dapm, "SYSCLK");
+
+		snd_soc_dapm_sync_unlocked(dapm);
+
+		snd_soc_dapm_mutex_unlock(dapm);
 
 		/* We start off just enabling microphone detection - even a
 		 * plain headphone will trigger detection.
diff --git a/sound/soc/codecs/wm5102.c b/sound/soc/codecs/wm5102.c
index ce9c8e14d4bd..34109050ceed 100644
--- a/sound/soc/codecs/wm5102.c
+++ b/sound/soc/codecs/wm5102.c
@@ -582,7 +582,7 @@ static int wm5102_sysclk_ev(struct snd_soc_dapm_widget *w,
 {
 	struct snd_soc_codec *codec = w->codec;
 	struct arizona *arizona = dev_get_drvdata(codec->dev->parent);
-	struct regmap *regmap = codec->control_data;
+	struct regmap *regmap = arizona->regmap;
 	const struct reg_default *patch = NULL;
 	int i, patch_size;
 
@@ -622,13 +622,16 @@ static const unsigned int wm5102_osr_val[] = {
 
 static const struct soc_enum wm5102_hpout_osr[] = {
 	SOC_VALUE_ENUM_SINGLE(ARIZONA_OUTPUT_PATH_CONFIG_1L,
-			      ARIZONA_OUT1_OSR_SHIFT, 0x7, 3,
+			      ARIZONA_OUT1_OSR_SHIFT, 0x7,
+			      ARRAY_SIZE(wm5102_osr_text),
 			      wm5102_osr_text, wm5102_osr_val),
 	SOC_VALUE_ENUM_SINGLE(ARIZONA_OUTPUT_PATH_CONFIG_2L,
-			      ARIZONA_OUT2_OSR_SHIFT, 0x7, 3,
+			      ARIZONA_OUT2_OSR_SHIFT, 0x7,
+			      ARRAY_SIZE(wm5102_osr_text),
 			      wm5102_osr_text, wm5102_osr_val),
 	SOC_VALUE_ENUM_SINGLE(ARIZONA_OUTPUT_PATH_CONFIG_3L,
-			      ARIZONA_OUT3_OSR_SHIFT, 0x7, 3,
+			      ARIZONA_OUT3_OSR_SHIFT, 0x7,
+			      ARRAY_SIZE(wm5102_osr_text),
 			      wm5102_osr_text, wm5102_osr_val),
 };
 
@@ -685,15 +688,8 @@ 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),
 
-SND_SOC_BYTES_MASK("EQ1 Coefficients", ARIZONA_EQ1_1, 21,
-		   ARIZONA_EQ1_ENA_MASK),
-SND_SOC_BYTES_MASK("EQ2 Coefficients", ARIZONA_EQ2_1, 21,
-		   ARIZONA_EQ2_ENA_MASK),
-SND_SOC_BYTES_MASK("EQ3 Coefficients", ARIZONA_EQ3_1, 21,
-		   ARIZONA_EQ3_ENA_MASK),
-SND_SOC_BYTES_MASK("EQ4 Coefficients", ARIZONA_EQ4_1, 21,
-		   ARIZONA_EQ4_ENA_MASK),
-
+SND_SOC_BYTES("EQ1 Coefficients", ARIZONA_EQ1_3, 19),
+SOC_SINGLE("EQ1 Mode Switch", ARIZONA_EQ1_2, ARIZONA_EQ1_B1_MODE, 1, 0),
 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,
@@ -705,6 +701,8 @@ SOC_SINGLE_TLV("EQ1 B4 Volume", ARIZONA_EQ1_2, ARIZONA_EQ1_B4_GAIN_SHIFT,
 SOC_SINGLE_TLV("EQ1 B5 Volume", ARIZONA_EQ1_2, ARIZONA_EQ1_B5_GAIN_SHIFT,
 	       24, 0, eq_tlv),
 
+SND_SOC_BYTES("EQ2 Coefficients", ARIZONA_EQ2_3, 19),
+SOC_SINGLE("EQ2 Mode Switch", ARIZONA_EQ2_2, ARIZONA_EQ2_B1_MODE, 1, 0),
 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,
@@ -716,6 +714,8 @@ SOC_SINGLE_TLV("EQ2 B4 Volume", ARIZONA_EQ2_2, ARIZONA_EQ2_B4_GAIN_SHIFT,
 SOC_SINGLE_TLV("EQ2 B5 Volume", ARIZONA_EQ2_2, ARIZONA_EQ2_B5_GAIN_SHIFT,
 	       24, 0, eq_tlv),
 
+SND_SOC_BYTES("EQ3 Coefficients", ARIZONA_EQ3_3, 19),
+SOC_SINGLE("EQ3 Mode Switch", ARIZONA_EQ3_2, ARIZONA_EQ3_B1_MODE, 1, 0),
 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,
@@ -727,6 +727,8 @@ SOC_SINGLE_TLV("EQ3 B4 Volume", ARIZONA_EQ3_2, ARIZONA_EQ3_B4_GAIN_SHIFT,
 SOC_SINGLE_TLV("EQ3 B5 Volume", ARIZONA_EQ3_2, ARIZONA_EQ3_B5_GAIN_SHIFT,
 	       24, 0, eq_tlv),
 
+SND_SOC_BYTES("EQ4 Coefficients", ARIZONA_EQ4_3, 19),
+SOC_SINGLE("EQ4 Mode Switch", ARIZONA_EQ4_2, ARIZONA_EQ4_B1_MODE, 1, 0),
 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,
diff --git a/sound/soc/codecs/wm5110.c b/sound/soc/codecs/wm5110.c
index 2c3c962d9a85..d7bf8848174a 100644
--- a/sound/soc/codecs/wm5110.c
+++ b/sound/soc/codecs/wm5110.c
@@ -136,7 +136,7 @@ static int wm5110_sysclk_ev(struct snd_soc_dapm_widget *w,
 {
 	struct snd_soc_codec *codec = w->codec;
 	struct arizona *arizona = dev_get_drvdata(codec->dev->parent);
-	struct regmap *regmap = codec->control_data;
+	struct regmap *regmap = arizona->regmap;
 	const struct reg_default *patch = NULL;
 	int i, patch_size;
 
@@ -247,15 +247,8 @@ 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),
 
-SND_SOC_BYTES_MASK("EQ1 Coefficients", ARIZONA_EQ1_1, 21,
-		   ARIZONA_EQ1_ENA_MASK),
-SND_SOC_BYTES_MASK("EQ2 Coefficients", ARIZONA_EQ2_1, 21,
-		   ARIZONA_EQ2_ENA_MASK),
-SND_SOC_BYTES_MASK("EQ3 Coefficients", ARIZONA_EQ3_1, 21,
-		   ARIZONA_EQ3_ENA_MASK),
-SND_SOC_BYTES_MASK("EQ4 Coefficients", ARIZONA_EQ4_1, 21,
-		   ARIZONA_EQ4_ENA_MASK),
-
+SND_SOC_BYTES("EQ1 Coefficients", ARIZONA_EQ1_3, 19),
+SOC_SINGLE("EQ1 Mode Switch", ARIZONA_EQ1_2, ARIZONA_EQ1_B1_MODE, 1, 0),
 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,
@@ -267,6 +260,8 @@ SOC_SINGLE_TLV("EQ1 B4 Volume", ARIZONA_EQ1_2, ARIZONA_EQ1_B4_GAIN_SHIFT,
 SOC_SINGLE_TLV("EQ1 B5 Volume", ARIZONA_EQ1_2, ARIZONA_EQ1_B5_GAIN_SHIFT,
 	       24, 0, eq_tlv),
 
+SND_SOC_BYTES("EQ2 Coefficients", ARIZONA_EQ2_3, 19),
+SOC_SINGLE("EQ2 Mode Switch", ARIZONA_EQ2_2, ARIZONA_EQ2_B1_MODE, 1, 0),
 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,
@@ -278,6 +273,8 @@ SOC_SINGLE_TLV("EQ2 B4 Volume", ARIZONA_EQ2_2, ARIZONA_EQ2_B4_GAIN_SHIFT,
 SOC_SINGLE_TLV("EQ2 B5 Volume", ARIZONA_EQ2_2, ARIZONA_EQ2_B5_GAIN_SHIFT,
 	       24, 0, eq_tlv),
 
+SND_SOC_BYTES("EQ3 Coefficients", ARIZONA_EQ3_3, 19),
+SOC_SINGLE("EQ3 Mode Switch", ARIZONA_EQ3_2, ARIZONA_EQ3_B1_MODE, 1, 0),
 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,
@@ -289,6 +286,8 @@ SOC_SINGLE_TLV("EQ3 B4 Volume", ARIZONA_EQ3_2, ARIZONA_EQ3_B4_GAIN_SHIFT,
 SOC_SINGLE_TLV("EQ3 B5 Volume", ARIZONA_EQ3_2, ARIZONA_EQ3_B5_GAIN_SHIFT,
 	       24, 0, eq_tlv),
 
+SND_SOC_BYTES("EQ4 Coefficients", ARIZONA_EQ4_3, 19),
+SOC_SINGLE("EQ4 Mode Switch", ARIZONA_EQ4_2, ARIZONA_EQ4_B1_MODE, 1, 0),
 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,
diff --git a/sound/soc/codecs/wm8523.c b/sound/soc/codecs/wm8523.c
index 74d106dc7667..5dfd571b1a03 100644
--- a/sound/soc/codecs/wm8523.c
+++ b/sound/soc/codecs/wm8523.c
@@ -75,8 +75,8 @@ static const char *wm8523_zd_count_text[] = {
 	"2048",
 };
 
-static const struct soc_enum wm8523_zc_count =
-	SOC_ENUM_SINGLE(WM8523_ZERO_DETECT, 0, 2, wm8523_zd_count_text);
+static SOC_ENUM_SINGLE_DECL(wm8523_zc_count, WM8523_ZERO_DETECT, 0,
+			    wm8523_zd_count_text);
 
 static const struct snd_kcontrol_new wm8523_controls[] = {
 SOC_DOUBLE_R_TLV("Playback Volume", WM8523_DAC_GAINL, WM8523_DAC_GAINR,
diff --git a/sound/soc/codecs/wm8711.c b/sound/soc/codecs/wm8711.c
index d99f948c513c..6efcc40a7cb3 100644
--- a/sound/soc/codecs/wm8711.c
+++ b/sound/soc/codecs/wm8711.c
@@ -201,7 +201,7 @@ static void wm8711_shutdown(struct snd_pcm_substream *substream,
 	struct snd_soc_codec *codec = dai->codec;
 
 	/* deactivate */
-	if (!codec->active) {
+	if (!snd_soc_codec_is_active(codec)) {
 		udelay(50);
 		snd_soc_write(codec, WM8711_ACTIVE, 0x0);
 	}
diff --git a/sound/soc/codecs/wm8731.c b/sound/soc/codecs/wm8731.c
index 029720366ff8..d9655f981df1 100644
--- a/sound/soc/codecs/wm8731.c
+++ b/sound/soc/codecs/wm8731.c
@@ -83,8 +83,8 @@ static bool wm8731_writeable(struct device *dev, unsigned int reg)
 
 static const char *wm8731_input_select[] = {"Line In", "Mic"};
 
-static const struct soc_enum wm8731_insel_enum =
-	SOC_ENUM_SINGLE(WM8731_APANA, 2, 2, wm8731_input_select);
+static SOC_ENUM_SINGLE_DECL(wm8731_insel_enum,
+			    WM8731_APANA, 2, wm8731_input_select);
 
 static int wm8731_deemph[] = { 0, 32000, 44100, 48000 };
 
diff --git a/sound/soc/codecs/wm8737.c b/sound/soc/codecs/wm8737.c
index 2f167a8ca01b..ecc4e8725d5b 100644
--- a/sound/soc/codecs/wm8737.c
+++ b/sound/soc/codecs/wm8737.c
@@ -99,29 +99,29 @@ static const char *micbias_enum_text[] = {
 	"100%",
 };
 
-static const struct soc_enum micbias_enum =
-	SOC_ENUM_SINGLE(WM8737_MIC_PREAMP_CONTROL, 0, 4, micbias_enum_text);
+static SOC_ENUM_SINGLE_DECL(micbias_enum,
+			    WM8737_MIC_PREAMP_CONTROL, 0, micbias_enum_text);
 
 static const char *low_cutoff_text[] = {
 	"Low", "High"
 };
 
-static const struct soc_enum low_3d =
-	SOC_ENUM_SINGLE(WM8737_3D_ENHANCE, 6, 2, low_cutoff_text);
+static SOC_ENUM_SINGLE_DECL(low_3d,
+			    WM8737_3D_ENHANCE, 6, low_cutoff_text);
 
 static const char *high_cutoff_text[] = {
 	"High", "Low"
 };
 
-static const struct soc_enum high_3d =
-	SOC_ENUM_SINGLE(WM8737_3D_ENHANCE, 5, 2, high_cutoff_text);
+static SOC_ENUM_SINGLE_DECL(high_3d,
+			    WM8737_3D_ENHANCE, 5, high_cutoff_text);
 
 static const char *alc_fn_text[] = {
 	"Disabled", "Right", "Left", "Stereo"
 };
 
-static const struct soc_enum alc_fn =
-	SOC_ENUM_SINGLE(WM8737_ALC1, 7, 4, alc_fn_text);
+static SOC_ENUM_SINGLE_DECL(alc_fn,
+			    WM8737_ALC1, 7, alc_fn_text);
 
 static const char *alc_hold_text[] = {
 	"0", "2.67ms", "5.33ms", "10.66ms", "21.32ms", "42.64ms", "85.28ms",
@@ -129,24 +129,24 @@ static const char *alc_hold_text[] = {
 	"10.916s", "21.832s", "43.691s"
 };
 
-static const struct soc_enum alc_hold =
-	SOC_ENUM_SINGLE(WM8737_ALC2, 0, 16, alc_hold_text);
+static SOC_ENUM_SINGLE_DECL(alc_hold,
+			    WM8737_ALC2, 0, alc_hold_text);
 
 static const char *alc_atk_text[] = {
 	"8.4ms", "16.8ms", "33.6ms", "67.2ms", "134.4ms", "268.8ms", "537.6ms",
 	"1.075s", "2.15s", "4.3s", "8.6s"
 };
 
-static const struct soc_enum alc_atk =
-	SOC_ENUM_SINGLE(WM8737_ALC3, 0, 11, alc_atk_text);
+static SOC_ENUM_SINGLE_DECL(alc_atk,
+			    WM8737_ALC3, 0, alc_atk_text);
 
 static const char *alc_dcy_text[] = {
 	"33.6ms", "67.2ms", "134.4ms", "268.8ms", "537.6ms", "1.075s", "2.15s",
 	"4.3s", "8.6s", "17.2s", "34.41s"
 };
 
-static const struct soc_enum alc_dcy =
-	SOC_ENUM_SINGLE(WM8737_ALC3, 4, 11, alc_dcy_text);
+static SOC_ENUM_SINGLE_DECL(alc_dcy,
+			    WM8737_ALC3, 4, alc_dcy_text);
 
 static const struct snd_kcontrol_new wm8737_snd_controls[] = {
 SOC_DOUBLE_R_TLV("Mic Boost Volume", WM8737_AUDIO_PATH_L, WM8737_AUDIO_PATH_R,
@@ -191,8 +191,8 @@ static const char *linsel_text[] = {
 	"LINPUT1", "LINPUT2", "LINPUT3", "LINPUT1 DC",
 };
 
-static const struct soc_enum linsel_enum =
-	SOC_ENUM_SINGLE(WM8737_AUDIO_PATH_L, 7, 4, linsel_text);
+static SOC_ENUM_SINGLE_DECL(linsel_enum,
+			    WM8737_AUDIO_PATH_L, 7, linsel_text);
 
 static const struct snd_kcontrol_new linsel_mux =
 	SOC_DAPM_ENUM("LINSEL", linsel_enum);
@@ -202,8 +202,8 @@ static const char *rinsel_text[] = {
 	"RINPUT1", "RINPUT2", "RINPUT3", "RINPUT1 DC",
 };
 
-static const struct soc_enum rinsel_enum =
-	SOC_ENUM_SINGLE(WM8737_AUDIO_PATH_R, 7, 4, rinsel_text);
+static SOC_ENUM_SINGLE_DECL(rinsel_enum,
+			    WM8737_AUDIO_PATH_R, 7, rinsel_text);
 
 static const struct snd_kcontrol_new rinsel_mux =
 	SOC_DAPM_ENUM("RINSEL", rinsel_enum);
@@ -212,15 +212,15 @@ static const char *bypass_text[] = {
 	"Direct", "Preamp"
 };
 
-static const struct soc_enum lbypass_enum =
-	SOC_ENUM_SINGLE(WM8737_MIC_PREAMP_CONTROL, 2, 2, bypass_text);
+static SOC_ENUM_SINGLE_DECL(lbypass_enum,
+			    WM8737_MIC_PREAMP_CONTROL, 2, bypass_text);
 
 static const struct snd_kcontrol_new lbypass_mux =
 	SOC_DAPM_ENUM("Left Bypass", lbypass_enum);
 
 
-static const struct soc_enum rbypass_enum =
-	SOC_ENUM_SINGLE(WM8737_MIC_PREAMP_CONTROL, 3, 2, bypass_text);
+static SOC_ENUM_SINGLE_DECL(rbypass_enum,
+			    WM8737_MIC_PREAMP_CONTROL, 3, bypass_text);
 
 static const struct snd_kcontrol_new rbypass_mux =
 	SOC_DAPM_ENUM("Left Bypass", rbypass_enum);
@@ -644,7 +644,7 @@ static const struct regmap_config wm8737_regmap = {
 	.volatile_reg = wm8737_volatile,
 };
 
-#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+#if IS_ENABLED(CONFIG_I2C)
 static int wm8737_i2c_probe(struct i2c_client *i2c,
 			    const struct i2c_device_id *id)
 {
@@ -758,7 +758,7 @@ static struct spi_driver wm8737_spi_driver = {
 static int __init wm8737_modinit(void)
 {
 	int ret;
-#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+#if IS_ENABLED(CONFIG_I2C)
 	ret = i2c_add_driver(&wm8737_i2c_driver);
 	if (ret != 0) {
 		printk(KERN_ERR "Failed to register WM8737 I2C driver: %d\n",
@@ -781,7 +781,7 @@ static void __exit wm8737_exit(void)
 #if defined(CONFIG_SPI_MASTER)
 	spi_unregister_driver(&wm8737_spi_driver);
 #endif
-#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+#if IS_ENABLED(CONFIG_I2C)
 	i2c_del_driver(&wm8737_i2c_driver);
 #endif
 }
diff --git a/sound/soc/codecs/wm8741.c b/sound/soc/codecs/wm8741.c
index 2895c8d3b5e4..dd02ebf88015 100644
--- a/sound/soc/codecs/wm8741.c
+++ b/sound/soc/codecs/wm8741.c
@@ -44,7 +44,7 @@ struct wm8741_priv {
 	struct regmap *regmap;
 	struct regulator_bulk_data supplies[WM8741_NUM_SUPPLIES];
 	unsigned int sysclk;
-	struct snd_pcm_hw_constraint_list *sysclk_constraints;
+	const struct snd_pcm_hw_constraint_list *sysclk_constraints;
 };
 
 static const struct reg_default wm8741_reg_defaults[] = {
@@ -122,74 +122,74 @@ static struct {
 	{ 6, 768 },
 };
 
-static unsigned int rates_11289[] = {
+static const unsigned int rates_11289[] = {
 	44100, 88235,
 };
 
-static struct snd_pcm_hw_constraint_list constraints_11289 = {
+static const struct snd_pcm_hw_constraint_list constraints_11289 = {
 	.count	= ARRAY_SIZE(rates_11289),
 	.list	= rates_11289,
 };
 
-static unsigned int rates_12288[] = {
+static const unsigned int rates_12288[] = {
 	32000, 48000, 96000,
 };
 
-static struct snd_pcm_hw_constraint_list constraints_12288 = {
+static const struct snd_pcm_hw_constraint_list constraints_12288 = {
 	.count	= ARRAY_SIZE(rates_12288),
 	.list	= rates_12288,
 };
 
-static unsigned int rates_16384[] = {
+static const unsigned int rates_16384[] = {
 	32000,
 };
 
-static struct snd_pcm_hw_constraint_list constraints_16384 = {
+static const struct snd_pcm_hw_constraint_list constraints_16384 = {
 	.count	= ARRAY_SIZE(rates_16384),
 	.list	= rates_16384,
 };
 
-static unsigned int rates_16934[] = {
+static const unsigned int rates_16934[] = {
 	44100, 88235,
 };
 
-static struct snd_pcm_hw_constraint_list constraints_16934 = {
+static const struct snd_pcm_hw_constraint_list constraints_16934 = {
 	.count	= ARRAY_SIZE(rates_16934),
 	.list	= rates_16934,
 };
 
-static unsigned int rates_18432[] = {
+static const unsigned int rates_18432[] = {
 	48000, 96000,
 };
 
-static struct snd_pcm_hw_constraint_list constraints_18432 = {
+static const struct snd_pcm_hw_constraint_list constraints_18432 = {
 	.count	= ARRAY_SIZE(rates_18432),
 	.list	= rates_18432,
 };
 
-static unsigned int rates_22579[] = {
+static const unsigned int rates_22579[] = {
 	44100, 88235, 1764000
 };
 
-static struct snd_pcm_hw_constraint_list constraints_22579 = {
+static const struct snd_pcm_hw_constraint_list constraints_22579 = {
 	.count	= ARRAY_SIZE(rates_22579),
 	.list	= rates_22579,
 };
 
-static unsigned int rates_24576[] = {
+static const unsigned int rates_24576[] = {
 	32000, 48000, 96000, 192000
 };
 
-static struct snd_pcm_hw_constraint_list constraints_24576 = {
+static const struct snd_pcm_hw_constraint_list constraints_24576 = {
 	.count	= ARRAY_SIZE(rates_24576),
 	.list	= rates_24576,
 };
 
-static unsigned int rates_36864[] = {
+static const unsigned int rates_36864[] = {
 	48000, 96000, 19200
 };
 
-static struct snd_pcm_hw_constraint_list constraints_36864 = {
+static const struct snd_pcm_hw_constraint_list constraints_36864 = {
 	.count	= ARRAY_SIZE(rates_36864),
 	.list	= rates_36864,
 };
diff --git a/sound/soc/codecs/wm8753.c b/sound/soc/codecs/wm8753.c
index be85da93a268..6a6855d8b8ea 100644
--- a/sound/soc/codecs/wm8753.c
+++ b/sound/soc/codecs/wm8753.c
@@ -251,7 +251,7 @@ static int wm8753_set_dai(struct snd_kcontrol *kcontrol,
 	if (wm8753->dai_func == ucontrol->value.integer.value[0])
 		return 0;
 
-	if (codec->active)
+	if (snd_soc_codec_is_active(codec))
 		return -EBUSY;
 
 	ioctl = snd_soc_read(codec, WM8753_IOCTL);
@@ -1314,7 +1314,7 @@ static int wm8753_mute(struct snd_soc_dai *dai, int mute)
 	/* the digital mute covers the HiFi and Voice DAC's on the WM8753.
 	 * make sure we check if they are not both active when we mute */
 	if (mute && wm8753->dai_func == 1) {
-		if (!codec->active)
+		if (!snd_soc_codec_is_active(codec))
 			snd_soc_write(codec, WM8753_DAC, mute_reg | 0x8);
 	} else {
 		if (mute)
@@ -1440,7 +1440,6 @@ static void wm8753_work(struct work_struct *work)
 static int wm8753_suspend(struct snd_soc_codec *codec)
 {
 	wm8753_set_bias_level(codec, SND_SOC_BIAS_OFF);
-	codec->cache_sync = 1;
 	return 0;
 }
 
diff --git a/sound/soc/codecs/wm8804.c b/sound/soc/codecs/wm8804.c
index 9bc8206a6807..72d12bbe1a56 100644
--- a/sound/soc/codecs/wm8804.c
+++ b/sound/soc/codecs/wm8804.c
@@ -92,7 +92,7 @@ WM8804_REGULATOR_EVENT(0)
 WM8804_REGULATOR_EVENT(1)
 
 static const char *txsrc_text[] = { "S/PDIF RX", "AIF" };
-static const SOC_ENUM_SINGLE_EXT_DECL(txsrc, txsrc_text);
+static SOC_ENUM_SINGLE_EXT_DECL(txsrc, txsrc_text);
 
 static const struct snd_kcontrol_new wm8804_snd_controls[] = {
 	SOC_ENUM_EXT("Input Source", txsrc, txsrc_get, txsrc_put),
diff --git a/sound/soc/codecs/wm8903.c b/sound/soc/codecs/wm8903.c
index eebcb1da3b7b..b82b70a3b3d3 100644
--- a/sound/soc/codecs/wm8903.c
+++ b/sound/soc/codecs/wm8903.c
@@ -489,28 +489,28 @@ static const char *hpf_mode_text[] = {
 	"Hi-fi", "Voice 1", "Voice 2", "Voice 3"
 };
 
-static const struct soc_enum hpf_mode =
-	SOC_ENUM_SINGLE(WM8903_ADC_DIGITAL_0, 5, 4, hpf_mode_text);
+static SOC_ENUM_SINGLE_DECL(hpf_mode,
+			    WM8903_ADC_DIGITAL_0, 5, hpf_mode_text);
 
 static const char *osr_text[] = {
 	"Low power", "High performance"
 };
 
-static const struct soc_enum adc_osr =
-	SOC_ENUM_SINGLE(WM8903_ANALOGUE_ADC_0, 0, 2, osr_text);
+static SOC_ENUM_SINGLE_DECL(adc_osr,
+			    WM8903_ANALOGUE_ADC_0, 0, osr_text);
 
-static const struct soc_enum dac_osr =
-	SOC_ENUM_SINGLE(WM8903_DAC_DIGITAL_1, 0, 2, osr_text);
+static SOC_ENUM_SINGLE_DECL(dac_osr,
+			    WM8903_DAC_DIGITAL_1, 0, osr_text);
 
 static const char *drc_slope_text[] = {
 	"1", "1/2", "1/4", "1/8", "1/16", "0"
 };
 
-static const struct soc_enum drc_slope_r0 =
-	SOC_ENUM_SINGLE(WM8903_DRC_2, 3, 6, drc_slope_text);
+static SOC_ENUM_SINGLE_DECL(drc_slope_r0,
+			    WM8903_DRC_2, 3, drc_slope_text);
 
-static const struct soc_enum drc_slope_r1 =
-	SOC_ENUM_SINGLE(WM8903_DRC_2, 0, 6, drc_slope_text);
+static SOC_ENUM_SINGLE_DECL(drc_slope_r1,
+			    WM8903_DRC_2, 0, drc_slope_text);
 
 static const char *drc_attack_text[] = {
 	"instantaneous",
@@ -518,125 +518,125 @@ static const char *drc_attack_text[] = {
 	"46.4ms", "92.8ms", "185.6ms"
 };
 
-static const struct soc_enum drc_attack =
-	SOC_ENUM_SINGLE(WM8903_DRC_1, 12, 11, drc_attack_text);
+static SOC_ENUM_SINGLE_DECL(drc_attack,
+			    WM8903_DRC_1, 12, drc_attack_text);
 
 static const char *drc_decay_text[] = {
 	"186ms", "372ms", "743ms", "1.49s", "2.97s", "5.94s", "11.89s",
 	"23.87s", "47.56s"
 };
 
-static const struct soc_enum drc_decay =
-	SOC_ENUM_SINGLE(WM8903_DRC_1, 8, 9, drc_decay_text);
+static SOC_ENUM_SINGLE_DECL(drc_decay,
+			    WM8903_DRC_1, 8, drc_decay_text);
 
 static const char *drc_ff_delay_text[] = {
 	"5 samples", "9 samples"
 };
 
-static const struct soc_enum drc_ff_delay =
-	SOC_ENUM_SINGLE(WM8903_DRC_0, 5, 2, drc_ff_delay_text);
+static SOC_ENUM_SINGLE_DECL(drc_ff_delay,
+			    WM8903_DRC_0, 5, drc_ff_delay_text);
 
 static const char *drc_qr_decay_text[] = {
 	"0.725ms", "1.45ms", "5.8ms"
 };
 
-static const struct soc_enum drc_qr_decay =
-	SOC_ENUM_SINGLE(WM8903_DRC_1, 4, 3, drc_qr_decay_text);
+static SOC_ENUM_SINGLE_DECL(drc_qr_decay,
+			    WM8903_DRC_1, 4, drc_qr_decay_text);
 
 static const char *drc_smoothing_text[] = {
 	"Low", "Medium", "High"
 };
 
-static const struct soc_enum drc_smoothing =
-	SOC_ENUM_SINGLE(WM8903_DRC_0, 11, 3, drc_smoothing_text);
+static SOC_ENUM_SINGLE_DECL(drc_smoothing,
+			    WM8903_DRC_0, 11, drc_smoothing_text);
 
 static const char *soft_mute_text[] = {
 	"Fast (fs/2)", "Slow (fs/32)"
 };
 
-static const struct soc_enum soft_mute =
-	SOC_ENUM_SINGLE(WM8903_DAC_DIGITAL_1, 10, 2, soft_mute_text);
+static SOC_ENUM_SINGLE_DECL(soft_mute,
+			    WM8903_DAC_DIGITAL_1, 10, soft_mute_text);
 
 static const char *mute_mode_text[] = {
 	"Hard", "Soft"
 };
 
-static const struct soc_enum mute_mode =
-	SOC_ENUM_SINGLE(WM8903_DAC_DIGITAL_1, 9, 2, mute_mode_text);
+static SOC_ENUM_SINGLE_DECL(mute_mode,
+			    WM8903_DAC_DIGITAL_1, 9, mute_mode_text);
 
 static const char *companding_text[] = {
 	"ulaw", "alaw"
 };
 
-static const struct soc_enum dac_companding =
-	SOC_ENUM_SINGLE(WM8903_AUDIO_INTERFACE_0, 0, 2, companding_text);
+static SOC_ENUM_SINGLE_DECL(dac_companding,
+			    WM8903_AUDIO_INTERFACE_0, 0, companding_text);
 
-static const struct soc_enum adc_companding =
-	SOC_ENUM_SINGLE(WM8903_AUDIO_INTERFACE_0, 2, 2, companding_text);
+static SOC_ENUM_SINGLE_DECL(adc_companding,
+			    WM8903_AUDIO_INTERFACE_0, 2, companding_text);
 
 static const char *input_mode_text[] = {
 	"Single-Ended", "Differential Line", "Differential Mic"
 };
 
-static const struct soc_enum linput_mode_enum =
-	SOC_ENUM_SINGLE(WM8903_ANALOGUE_LEFT_INPUT_1, 0, 3, input_mode_text);
+static SOC_ENUM_SINGLE_DECL(linput_mode_enum,
+			    WM8903_ANALOGUE_LEFT_INPUT_1, 0, input_mode_text);
 
-static const struct soc_enum rinput_mode_enum =
-	SOC_ENUM_SINGLE(WM8903_ANALOGUE_RIGHT_INPUT_1, 0, 3, input_mode_text);
+static SOC_ENUM_SINGLE_DECL(rinput_mode_enum,
+			    WM8903_ANALOGUE_RIGHT_INPUT_1, 0, input_mode_text);
 
 static const char *linput_mux_text[] = {
 	"IN1L", "IN2L", "IN3L"
 };
 
-static const struct soc_enum linput_enum =
-	SOC_ENUM_SINGLE(WM8903_ANALOGUE_LEFT_INPUT_1, 2, 3, linput_mux_text);
+static SOC_ENUM_SINGLE_DECL(linput_enum,
+			    WM8903_ANALOGUE_LEFT_INPUT_1, 2, linput_mux_text);
 
-static const struct soc_enum linput_inv_enum =
-	SOC_ENUM_SINGLE(WM8903_ANALOGUE_LEFT_INPUT_1, 4, 3, linput_mux_text);
+static SOC_ENUM_SINGLE_DECL(linput_inv_enum,
+			    WM8903_ANALOGUE_LEFT_INPUT_1, 4, linput_mux_text);
 
 static const char *rinput_mux_text[] = {
 	"IN1R", "IN2R", "IN3R"
 };
 
-static const struct soc_enum rinput_enum =
-	SOC_ENUM_SINGLE(WM8903_ANALOGUE_RIGHT_INPUT_1, 2, 3, rinput_mux_text);
+static SOC_ENUM_SINGLE_DECL(rinput_enum,
+			    WM8903_ANALOGUE_RIGHT_INPUT_1, 2, rinput_mux_text);
 
-static const struct soc_enum rinput_inv_enum =
-	SOC_ENUM_SINGLE(WM8903_ANALOGUE_RIGHT_INPUT_1, 4, 3, rinput_mux_text);
+static SOC_ENUM_SINGLE_DECL(rinput_inv_enum,
+			    WM8903_ANALOGUE_RIGHT_INPUT_1, 4, rinput_mux_text);
 
 
 static const char *sidetone_text[] = {
 	"None", "Left", "Right"
 };
 
-static const struct soc_enum lsidetone_enum =
-	SOC_ENUM_SINGLE(WM8903_DAC_DIGITAL_0, 2, 3, sidetone_text);
+static SOC_ENUM_SINGLE_DECL(lsidetone_enum,
+			    WM8903_DAC_DIGITAL_0, 2, sidetone_text);
 
-static const struct soc_enum rsidetone_enum =
-	SOC_ENUM_SINGLE(WM8903_DAC_DIGITAL_0, 0, 3, sidetone_text);
+static SOC_ENUM_SINGLE_DECL(rsidetone_enum,
+			    WM8903_DAC_DIGITAL_0, 0, sidetone_text);
 
 static const char *adcinput_text[] = {
 	"ADC", "DMIC"
 };
 
-static const struct soc_enum adcinput_enum =
-	SOC_ENUM_SINGLE(WM8903_CLOCK_RATE_TEST_4, 9, 2, adcinput_text);
+static SOC_ENUM_SINGLE_DECL(adcinput_enum,
+			    WM8903_CLOCK_RATE_TEST_4, 9, adcinput_text);
 
 static const char *aif_text[] = {
 	"Left", "Right"
 };
 
-static const struct soc_enum lcapture_enum =
-	SOC_ENUM_SINGLE(WM8903_AUDIO_INTERFACE_0, 7, 2, aif_text);
+static SOC_ENUM_SINGLE_DECL(lcapture_enum,
+			    WM8903_AUDIO_INTERFACE_0, 7, aif_text);
 
-static const struct soc_enum rcapture_enum =
-	SOC_ENUM_SINGLE(WM8903_AUDIO_INTERFACE_0, 6, 2, aif_text);
+static SOC_ENUM_SINGLE_DECL(rcapture_enum,
+			    WM8903_AUDIO_INTERFACE_0, 6, aif_text);
 
-static const struct soc_enum lplay_enum =
-	SOC_ENUM_SINGLE(WM8903_AUDIO_INTERFACE_0, 5, 2, aif_text);
+static SOC_ENUM_SINGLE_DECL(lplay_enum,
+			    WM8903_AUDIO_INTERFACE_0, 5, aif_text);
 
-static const struct soc_enum rplay_enum =
-	SOC_ENUM_SINGLE(WM8903_AUDIO_INTERFACE_0, 4, 2, aif_text);
+static SOC_ENUM_SINGLE_DECL(rplay_enum,
+			    WM8903_AUDIO_INTERFACE_0, 4, aif_text);
 
 static const struct snd_kcontrol_new wm8903_snd_controls[] = {
 
diff --git a/sound/soc/codecs/wm8904.c b/sound/soc/codecs/wm8904.c
index 53bbfac6a83a..27299cda0e99 100644
--- a/sound/soc/codecs/wm8904.c
+++ b/sound/soc/codecs/wm8904.c
@@ -552,18 +552,20 @@ static const char *input_mode_text[] = {
 	"Single-Ended", "Differential Line", "Differential Mic"
 };
 
-static const struct soc_enum lin_mode =
-	SOC_ENUM_SINGLE(WM8904_ANALOGUE_LEFT_INPUT_1, 0, 3, input_mode_text);
+static SOC_ENUM_SINGLE_DECL(lin_mode,
+			    WM8904_ANALOGUE_LEFT_INPUT_1, 0,
+			    input_mode_text);
 
-static const struct soc_enum rin_mode =
-	SOC_ENUM_SINGLE(WM8904_ANALOGUE_RIGHT_INPUT_1, 0, 3, input_mode_text);
+static SOC_ENUM_SINGLE_DECL(rin_mode,
+			    WM8904_ANALOGUE_RIGHT_INPUT_1, 0,
+			    input_mode_text);
 
 static const char *hpf_mode_text[] = {
 	"Hi-fi", "Voice 1", "Voice 2", "Voice 3"
 };
 
-static const struct soc_enum hpf_mode =
-	SOC_ENUM_SINGLE(WM8904_ADC_DIGITAL_0, 5, 4, hpf_mode_text);
+static SOC_ENUM_SINGLE_DECL(hpf_mode, WM8904_ADC_DIGITAL_0, 5,
+			    hpf_mode_text);
 
 static int wm8904_adc_osr_put(struct snd_kcontrol *kcontrol,
 			      struct snd_ctl_elem_value *ucontrol)
@@ -611,8 +613,7 @@ static const char *drc_path_text[] = {
 	"ADC", "DAC"
 };
 
-static const struct soc_enum drc_path =
-	SOC_ENUM_SINGLE(WM8904_DRC_0, 14, 2, drc_path_text);
+static SOC_ENUM_SINGLE_DECL(drc_path, WM8904_DRC_0, 14, drc_path_text);
 
 static const struct snd_kcontrol_new wm8904_dac_snd_controls[] = {
 SOC_SINGLE_TLV("Digital Playback Boost Volume", 
@@ -858,14 +859,14 @@ static const char *lin_text[] = {
 	"IN1L", "IN2L", "IN3L"
 };
 
-static const struct soc_enum lin_enum =
-	SOC_ENUM_SINGLE(WM8904_ANALOGUE_LEFT_INPUT_1, 2, 3, lin_text);
+static SOC_ENUM_SINGLE_DECL(lin_enum, WM8904_ANALOGUE_LEFT_INPUT_1, 2,
+			    lin_text);
 
 static const struct snd_kcontrol_new lin_mux =
 	SOC_DAPM_ENUM("Left Capture Mux", lin_enum);
 
-static const struct soc_enum lin_inv_enum =
-	SOC_ENUM_SINGLE(WM8904_ANALOGUE_LEFT_INPUT_1, 4, 3, lin_text);
+static SOC_ENUM_SINGLE_DECL(lin_inv_enum, WM8904_ANALOGUE_LEFT_INPUT_1, 4,
+			    lin_text);
 
 static const struct snd_kcontrol_new lin_inv_mux =
 	SOC_DAPM_ENUM("Left Capture Inveting Mux", lin_inv_enum);
@@ -874,14 +875,14 @@ static const char *rin_text[] = {
 	"IN1R", "IN2R", "IN3R"
 };
 
-static const struct soc_enum rin_enum =
-	SOC_ENUM_SINGLE(WM8904_ANALOGUE_RIGHT_INPUT_1, 2, 3, rin_text);
+static SOC_ENUM_SINGLE_DECL(rin_enum, WM8904_ANALOGUE_RIGHT_INPUT_1, 2,
+			    rin_text);
 
 static const struct snd_kcontrol_new rin_mux =
 	SOC_DAPM_ENUM("Right Capture Mux", rin_enum);
 
-static const struct soc_enum rin_inv_enum =
-	SOC_ENUM_SINGLE(WM8904_ANALOGUE_RIGHT_INPUT_1, 4, 3, rin_text);
+static SOC_ENUM_SINGLE_DECL(rin_inv_enum, WM8904_ANALOGUE_RIGHT_INPUT_1, 4,
+			    rin_text);
 
 static const struct snd_kcontrol_new rin_inv_mux =
 	SOC_DAPM_ENUM("Right Capture Inveting Mux", rin_inv_enum);
@@ -890,26 +891,26 @@ static const char *aif_text[] = {
 	"Left", "Right"
 };
 
-static const struct soc_enum aifoutl_enum =
-	SOC_ENUM_SINGLE(WM8904_AUDIO_INTERFACE_0, 7, 2, aif_text);
+static SOC_ENUM_SINGLE_DECL(aifoutl_enum, WM8904_AUDIO_INTERFACE_0, 7,
+			    aif_text);
 
 static const struct snd_kcontrol_new aifoutl_mux =
 	SOC_DAPM_ENUM("AIFOUTL Mux", aifoutl_enum);
 
-static const struct soc_enum aifoutr_enum =
-	SOC_ENUM_SINGLE(WM8904_AUDIO_INTERFACE_0, 6, 2, aif_text);
+static SOC_ENUM_SINGLE_DECL(aifoutr_enum, WM8904_AUDIO_INTERFACE_0, 6,
+			    aif_text);
 
 static const struct snd_kcontrol_new aifoutr_mux =
 	SOC_DAPM_ENUM("AIFOUTR Mux", aifoutr_enum);
 
-static const struct soc_enum aifinl_enum =
-	SOC_ENUM_SINGLE(WM8904_AUDIO_INTERFACE_0, 5, 2, aif_text);
+static SOC_ENUM_SINGLE_DECL(aifinl_enum, WM8904_AUDIO_INTERFACE_0, 5,
+			    aif_text);
 
 static const struct snd_kcontrol_new aifinl_mux =
 	SOC_DAPM_ENUM("AIFINL Mux", aifinl_enum);
 
-static const struct soc_enum aifinr_enum =
-	SOC_ENUM_SINGLE(WM8904_AUDIO_INTERFACE_0, 4, 2, aif_text);
+static SOC_ENUM_SINGLE_DECL(aifinr_enum, WM8904_AUDIO_INTERFACE_0, 4,
+			    aif_text);
 
 static const struct snd_kcontrol_new aifinr_mux =
 	SOC_DAPM_ENUM("AIFINR Mux", aifinr_enum);
@@ -991,26 +992,26 @@ static const char *out_mux_text[] = {
 	"DAC", "Bypass"
 };
 
-static const struct soc_enum hpl_enum =
-	SOC_ENUM_SINGLE(WM8904_ANALOGUE_OUT12_ZC, 3, 2, out_mux_text);
+static SOC_ENUM_SINGLE_DECL(hpl_enum, WM8904_ANALOGUE_OUT12_ZC, 3,
+			    out_mux_text);
 
 static const struct snd_kcontrol_new hpl_mux =
 	SOC_DAPM_ENUM("HPL Mux", hpl_enum);
 
-static const struct soc_enum hpr_enum =
-	SOC_ENUM_SINGLE(WM8904_ANALOGUE_OUT12_ZC, 2, 2, out_mux_text);
+static SOC_ENUM_SINGLE_DECL(hpr_enum, WM8904_ANALOGUE_OUT12_ZC, 2,
+			    out_mux_text);
 
 static const struct snd_kcontrol_new hpr_mux =
 	SOC_DAPM_ENUM("HPR Mux", hpr_enum);
 
-static const struct soc_enum linel_enum =
-	SOC_ENUM_SINGLE(WM8904_ANALOGUE_OUT12_ZC, 1, 2, out_mux_text);
+static SOC_ENUM_SINGLE_DECL(linel_enum, WM8904_ANALOGUE_OUT12_ZC, 1,
+			    out_mux_text);
 
 static const struct snd_kcontrol_new linel_mux =
 	SOC_DAPM_ENUM("LINEL Mux", linel_enum);
 
-static const struct soc_enum liner_enum =
-	SOC_ENUM_SINGLE(WM8904_ANALOGUE_OUT12_ZC, 0, 2, out_mux_text);
+static SOC_ENUM_SINGLE_DECL(liner_enum, WM8904_ANALOGUE_OUT12_ZC, 0,
+			    out_mux_text);
 
 static const struct snd_kcontrol_new liner_mux =
 	SOC_DAPM_ENUM("LINER Mux", liner_enum);
@@ -1019,14 +1020,14 @@ static const char *sidetone_text[] = {
 	"None", "Left", "Right"
 };
 
-static const struct soc_enum dacl_sidetone_enum =
-	SOC_ENUM_SINGLE(WM8904_DAC_DIGITAL_0, 2, 3, sidetone_text);
+static SOC_ENUM_SINGLE_DECL(dacl_sidetone_enum, WM8904_DAC_DIGITAL_0, 2,
+			    sidetone_text);
 
 static const struct snd_kcontrol_new dacl_sidetone_mux =
 	SOC_DAPM_ENUM("Left Sidetone Mux", dacl_sidetone_enum);
 
-static const struct soc_enum dacr_sidetone_enum =
-	SOC_ENUM_SINGLE(WM8904_DAC_DIGITAL_0, 0, 3, sidetone_text);
+static SOC_ENUM_SINGLE_DECL(dacr_sidetone_enum, WM8904_DAC_DIGITAL_0, 0,
+			    sidetone_text);
 
 static const struct snd_kcontrol_new dacr_sidetone_mux =
 	SOC_DAPM_ENUM("Right Sidetone Mux", dacr_sidetone_enum);
@@ -1981,7 +1982,7 @@ static void wm8904_handle_retune_mobile_pdata(struct snd_soc_codec *codec)
 	dev_dbg(codec->dev, "Allocated %d unique ReTune Mobile names\n",
 		wm8904->num_retune_mobile_texts);
 
-	wm8904->retune_mobile_enum.max = wm8904->num_retune_mobile_texts;
+	wm8904->retune_mobile_enum.items = wm8904->num_retune_mobile_texts;
 	wm8904->retune_mobile_enum.texts = wm8904->retune_mobile_texts;
 
 	ret = snd_soc_add_codec_controls(codec, &control, 1);
@@ -2022,7 +2023,7 @@ static void wm8904_handle_pdata(struct snd_soc_codec *codec)
 		for (i = 0; i < pdata->num_drc_cfgs; i++)
 			wm8904->drc_texts[i] = pdata->drc_cfgs[i].name;
 
-		wm8904->drc_enum.max = pdata->num_drc_cfgs;
+		wm8904->drc_enum.items = pdata->num_drc_cfgs;
 		wm8904->drc_enum.texts = wm8904->drc_texts;
 
 		ret = snd_soc_add_codec_controls(codec, &control, 1);
diff --git a/sound/soc/codecs/wm8940.c b/sound/soc/codecs/wm8940.c
index b404c26c1753..87f032d0d19f 100644
--- a/sound/soc/codecs/wm8940.c
+++ b/sound/soc/codecs/wm8940.c
@@ -154,22 +154,22 @@ static const struct reg_default wm8940_reg_defaults[] = {
 };
 
 static const char *wm8940_companding[] = { "Off", "NC", "u-law", "A-law" };
-static const struct soc_enum wm8940_adc_companding_enum
-= SOC_ENUM_SINGLE(WM8940_COMPANDINGCTL, 1, 4, wm8940_companding);
-static const struct soc_enum wm8940_dac_companding_enum
-= SOC_ENUM_SINGLE(WM8940_COMPANDINGCTL, 3, 4, wm8940_companding);
+static SOC_ENUM_SINGLE_DECL(wm8940_adc_companding_enum,
+			    WM8940_COMPANDINGCTL, 1, wm8940_companding);
+static SOC_ENUM_SINGLE_DECL(wm8940_dac_companding_enum,
+			    WM8940_COMPANDINGCTL, 3, wm8940_companding);
 
 static const char *wm8940_alc_mode_text[] = {"ALC", "Limiter"};
-static const struct soc_enum wm8940_alc_mode_enum
-= SOC_ENUM_SINGLE(WM8940_ALC3, 8, 2, wm8940_alc_mode_text);
+static SOC_ENUM_SINGLE_DECL(wm8940_alc_mode_enum,
+			    WM8940_ALC3, 8, wm8940_alc_mode_text);
 
 static const char *wm8940_mic_bias_level_text[] = {"0.9", "0.65"};
-static const struct soc_enum wm8940_mic_bias_level_enum
-= SOC_ENUM_SINGLE(WM8940_INPUTCTL, 8, 2, wm8940_mic_bias_level_text);
+static SOC_ENUM_SINGLE_DECL(wm8940_mic_bias_level_enum,
+			    WM8940_INPUTCTL, 8, wm8940_mic_bias_level_text);
 
 static const char *wm8940_filter_mode_text[] = {"Audio", "Application"};
-static const struct soc_enum wm8940_filter_mode_enum
-= SOC_ENUM_SINGLE(WM8940_ADC, 7, 2, wm8940_filter_mode_text);
+static SOC_ENUM_SINGLE_DECL(wm8940_filter_mode_enum,
+			    WM8940_ADC, 7, wm8940_filter_mode_text);
 
 static DECLARE_TLV_DB_SCALE(wm8940_spk_vol_tlv, -5700, 100, 1);
 static DECLARE_TLV_DB_SCALE(wm8940_att_tlv, -1000, 1000, 0);
diff --git a/sound/soc/codecs/wm8955.c b/sound/soc/codecs/wm8955.c
index 82c8ba975720..d4dcaecc8a5f 100644
--- a/sound/soc/codecs/wm8955.c
+++ b/sound/soc/codecs/wm8955.c
@@ -416,22 +416,21 @@ static const char *bass_mode_text[] = {
 	"Linear", "Adaptive",
 };
 
-static const struct soc_enum bass_mode =
-	SOC_ENUM_SINGLE(WM8955_BASS_CONTROL, 7, 2, bass_mode_text);
+static SOC_ENUM_SINGLE_DECL(bass_mode, WM8955_BASS_CONTROL, 7, bass_mode_text);
 
 static const char *bass_cutoff_text[] = {
 	"Low", "High"
 };
 
-static const struct soc_enum bass_cutoff =
-	SOC_ENUM_SINGLE(WM8955_BASS_CONTROL, 6, 2, bass_cutoff_text);
+static SOC_ENUM_SINGLE_DECL(bass_cutoff, WM8955_BASS_CONTROL, 6,
+			    bass_cutoff_text);
 
 static const char *treble_cutoff_text[] = {
 	"High", "Low"
 };
 
-static const struct soc_enum treble_cutoff =
-	SOC_ENUM_SINGLE(WM8955_TREBLE_CONTROL, 6, 2, treble_cutoff_text);
+static SOC_ENUM_SINGLE_DECL(treble_cutoff, WM8955_TREBLE_CONTROL, 2,
+			    treble_cutoff_text);
 
 static const DECLARE_TLV_DB_SCALE(digital_tlv, -12750, 50, 1);
 static const DECLARE_TLV_DB_SCALE(atten_tlv, -600, 600, 0);
diff --git a/sound/soc/codecs/wm8958-dsp2.c b/sound/soc/codecs/wm8958-dsp2.c
index d4248e00160e..7ac2e511403c 100644
--- a/sound/soc/codecs/wm8958-dsp2.c
+++ b/sound/soc/codecs/wm8958-dsp2.c
@@ -944,7 +944,7 @@ void wm8958_dsp2_init(struct snd_soc_codec *codec)
 		for (i = 0; i < pdata->num_mbc_cfgs; i++)
 			wm8994->mbc_texts[i] = pdata->mbc_cfgs[i].name;
 
-		wm8994->mbc_enum.max = pdata->num_mbc_cfgs;
+		wm8994->mbc_enum.items = pdata->num_mbc_cfgs;
 		wm8994->mbc_enum.texts = wm8994->mbc_texts;
 
 		ret = snd_soc_add_codec_controls(wm8994->hubs.codec,
@@ -973,7 +973,7 @@ void wm8958_dsp2_init(struct snd_soc_codec *codec)
 		for (i = 0; i < pdata->num_vss_cfgs; i++)
 			wm8994->vss_texts[i] = pdata->vss_cfgs[i].name;
 
-		wm8994->vss_enum.max = pdata->num_vss_cfgs;
+		wm8994->vss_enum.items = pdata->num_vss_cfgs;
 		wm8994->vss_enum.texts = wm8994->vss_texts;
 
 		ret = snd_soc_add_codec_controls(wm8994->hubs.codec,
@@ -1003,7 +1003,7 @@ void wm8958_dsp2_init(struct snd_soc_codec *codec)
 		for (i = 0; i < pdata->num_vss_hpf_cfgs; i++)
 			wm8994->vss_hpf_texts[i] = pdata->vss_hpf_cfgs[i].name;
 
-		wm8994->vss_hpf_enum.max = pdata->num_vss_hpf_cfgs;
+		wm8994->vss_hpf_enum.items = pdata->num_vss_hpf_cfgs;
 		wm8994->vss_hpf_enum.texts = wm8994->vss_hpf_texts;
 
 		ret = snd_soc_add_codec_controls(wm8994->hubs.codec,
@@ -1034,7 +1034,7 @@ void wm8958_dsp2_init(struct snd_soc_codec *codec)
 		for (i = 0; i < pdata->num_enh_eq_cfgs; i++)
 			wm8994->enh_eq_texts[i] = pdata->enh_eq_cfgs[i].name;
 
-		wm8994->enh_eq_enum.max = pdata->num_enh_eq_cfgs;
+		wm8994->enh_eq_enum.items = pdata->num_enh_eq_cfgs;
 		wm8994->enh_eq_enum.texts = wm8994->enh_eq_texts;
 
 		ret = snd_soc_add_codec_controls(wm8994->hubs.codec,
diff --git a/sound/soc/codecs/wm8961.c b/sound/soc/codecs/wm8961.c
index 900328e28a15..ce8fa6e01cb4 100644
--- a/sound/soc/codecs/wm8961.c
+++ b/sound/soc/codecs/wm8961.c
@@ -317,15 +317,15 @@ static const char *adc_hpf_text[] = {
 	"Hi-fi", "Voice 1", "Voice 2", "Voice 3",
 };
 
-static const struct soc_enum adc_hpf =
-	SOC_ENUM_SINGLE(WM8961_ADC_DAC_CONTROL_2, 7, 4, adc_hpf_text);
+static SOC_ENUM_SINGLE_DECL(adc_hpf,
+			    WM8961_ADC_DAC_CONTROL_2, 7, adc_hpf_text);
 
 static const char *dac_deemph_text[] = {
 	"None", "32kHz", "44.1kHz", "48kHz",
 };
 
-static const struct soc_enum dac_deemph =
-	SOC_ENUM_SINGLE(WM8961_ADC_DAC_CONTROL_1, 1, 4, dac_deemph_text);
+static SOC_ENUM_SINGLE_DECL(dac_deemph,
+			    WM8961_ADC_DAC_CONTROL_1, 1, dac_deemph_text);
 
 static const DECLARE_TLV_DB_SCALE(out_tlv, -12100, 100, 1);
 static const DECLARE_TLV_DB_SCALE(hp_sec_tlv, -700, 100, 0);
@@ -385,11 +385,11 @@ static const char *sidetone_text[] = {
 	"None", "Left", "Right"
 };
 
-static const struct soc_enum dacl_sidetone =
-	SOC_ENUM_SINGLE(WM8961_DSP_SIDETONE_0, 2, 3, sidetone_text);
+static SOC_ENUM_SINGLE_DECL(dacl_sidetone,
+			    WM8961_DSP_SIDETONE_0, 2, sidetone_text);
 
-static const struct soc_enum dacr_sidetone =
-	SOC_ENUM_SINGLE(WM8961_DSP_SIDETONE_1, 2, 3, sidetone_text);
+static SOC_ENUM_SINGLE_DECL(dacr_sidetone,
+			    WM8961_DSP_SIDETONE_1, 2, sidetone_text);
 
 static const struct snd_kcontrol_new dacl_mux =
 	SOC_DAPM_ENUM("DACL Sidetone", dacl_sidetone);
diff --git a/sound/soc/codecs/wm8962.c b/sound/soc/codecs/wm8962.c
index 97db3b45b411..62af9dc59fc5 100644
--- a/sound/soc/codecs/wm8962.c
+++ b/sound/soc/codecs/wm8962.c
@@ -1479,7 +1479,9 @@ static const DECLARE_TLV_DB_SCALE(eq_tlv, -1200, 100, 0);
 
 static int wm8962_dsp2_write_config(struct snd_soc_codec *codec)
 {
-	return regcache_sync_region(codec->control_data,
+	struct wm8962_priv *wm8962 = snd_soc_codec_get_drvdata(codec);
+
+	return regcache_sync_region(wm8962->regmap,
 				    WM8962_HDBASS_AI_1, WM8962_MAX_REGISTER);
 }
 
@@ -1658,16 +1660,16 @@ static const char *cap_hpf_mode_text[] = {
 	"Hi-fi", "Application"
 };
 
-static const struct soc_enum cap_hpf_mode =
-	SOC_ENUM_SINGLE(WM8962_ADC_DAC_CONTROL_2, 10, 2, cap_hpf_mode_text);
+static SOC_ENUM_SINGLE_DECL(cap_hpf_mode,
+			    WM8962_ADC_DAC_CONTROL_2, 10, cap_hpf_mode_text);
 
 
 static const char *cap_lhpf_mode_text[] = {
 	"LPF", "HPF"
 };
 
-static const struct soc_enum cap_lhpf_mode =
-	SOC_ENUM_SINGLE(WM8962_LHPF1, 1, 2, cap_lhpf_mode_text);
+static SOC_ENUM_SINGLE_DECL(cap_lhpf_mode,
+			    WM8962_LHPF1, 1, cap_lhpf_mode_text);
 
 static const struct snd_kcontrol_new wm8962_snd_controls[] = {
 SOC_DOUBLE("Input Mixer Switch", WM8962_INPUT_MIXER_CONTROL_1, 3, 2, 1, 1),
@@ -2014,40 +2016,40 @@ static int dsp2_event(struct snd_soc_dapm_widget *w,
 
 static const char *st_text[] = { "None", "Left", "Right" };
 
-static const struct soc_enum str_enum =
-	SOC_ENUM_SINGLE(WM8962_DAC_DSP_MIXING_1, 2, 3, st_text);
+static SOC_ENUM_SINGLE_DECL(str_enum,
+			    WM8962_DAC_DSP_MIXING_1, 2, st_text);
 
 static const struct snd_kcontrol_new str_mux =
 	SOC_DAPM_ENUM("Right Sidetone", str_enum);
 
-static const struct soc_enum stl_enum =
-	SOC_ENUM_SINGLE(WM8962_DAC_DSP_MIXING_2, 2, 3, st_text);
+static SOC_ENUM_SINGLE_DECL(stl_enum,
+			    WM8962_DAC_DSP_MIXING_2, 2, st_text);
 
 static const struct snd_kcontrol_new stl_mux =
 	SOC_DAPM_ENUM("Left Sidetone", stl_enum);
 
 static const char *outmux_text[] = { "DAC", "Mixer" };
 
-static const struct soc_enum spkoutr_enum =
-	SOC_ENUM_SINGLE(WM8962_SPEAKER_MIXER_2, 7, 2, outmux_text);
+static SOC_ENUM_SINGLE_DECL(spkoutr_enum,
+			    WM8962_SPEAKER_MIXER_2, 7, outmux_text);
 
 static const struct snd_kcontrol_new spkoutr_mux =
 	SOC_DAPM_ENUM("SPKOUTR Mux", spkoutr_enum);
 
-static const struct soc_enum spkoutl_enum =
-	SOC_ENUM_SINGLE(WM8962_SPEAKER_MIXER_1, 7, 2, outmux_text);
+static SOC_ENUM_SINGLE_DECL(spkoutl_enum,
+			    WM8962_SPEAKER_MIXER_1, 7, outmux_text);
 
 static const struct snd_kcontrol_new spkoutl_mux =
 	SOC_DAPM_ENUM("SPKOUTL Mux", spkoutl_enum);
 
-static const struct soc_enum hpoutr_enum =
-	SOC_ENUM_SINGLE(WM8962_HEADPHONE_MIXER_2, 7, 2, outmux_text);
+static SOC_ENUM_SINGLE_DECL(hpoutr_enum,
+			    WM8962_HEADPHONE_MIXER_2, 7, outmux_text);
 
 static const struct snd_kcontrol_new hpoutr_mux =
 	SOC_DAPM_ENUM("HPOUTR Mux", hpoutr_enum);
 
-static const struct soc_enum hpoutl_enum =
-	SOC_ENUM_SINGLE(WM8962_HEADPHONE_MIXER_1, 7, 2, outmux_text);
+static SOC_ENUM_SINGLE_DECL(hpoutl_enum,
+			    WM8962_HEADPHONE_MIXER_1, 7, outmux_text);
 
 static const struct snd_kcontrol_new hpoutl_mux =
 	SOC_DAPM_ENUM("HPOUTL Mux", hpoutl_enum);
@@ -2884,9 +2886,13 @@ static int wm8962_set_fll(struct snd_soc_codec *codec, int fll_id, int source,
 	snd_soc_write(codec, WM8962_FLL_CONTROL_7, fll_div.lambda);
 	snd_soc_write(codec, WM8962_FLL_CONTROL_8, fll_div.n);
 
-	try_wait_for_completion(&wm8962->fll_lock);
+	reinit_completion(&wm8962->fll_lock);
 
-	pm_runtime_get_sync(codec->dev);
+	ret = pm_runtime_get_sync(codec->dev);
+	if (ret < 0) {
+		dev_err(codec->dev, "Failed to resume device: %d\n", ret);
+		return ret;
+	}
 
 	snd_soc_update_bits(codec, WM8962_FLL_CONTROL_1,
 			    WM8962_FLL_FRAC | WM8962_FLL_REFCLK_SRC_MASK |
@@ -2894,8 +2900,6 @@ static int wm8962_set_fll(struct snd_soc_codec *codec, int fll_id, int source,
 
 	dev_dbg(codec->dev, "FLL configured for %dHz->%dHz\n", Fref, Fout);
 
-	ret = 0;
-
 	/* This should be a massive overestimate but go even
 	 * higher if we'll error out
 	 */
@@ -2909,14 +2913,17 @@ static int wm8962_set_fll(struct snd_soc_codec *codec, int fll_id, int source,
 
 	if (timeout == 0 && wm8962->irq) {
 		dev_err(codec->dev, "FLL lock timed out");
-		ret = -ETIMEDOUT;
+		snd_soc_update_bits(codec, WM8962_FLL_CONTROL_1,
+				    WM8962_FLL_ENA, 0);
+		pm_runtime_put(codec->dev);
+		return -ETIMEDOUT;
 	}
 
 	wm8962->fll_fref = Fref;
 	wm8962->fll_fout = Fout;
 	wm8962->fll_src = source;
 
-	return ret;
+	return 0;
 }
 
 static int wm8962_mute(struct snd_soc_dai *dai, int mute)
@@ -3003,9 +3010,16 @@ static irqreturn_t wm8962_irq(int irq, void *data)
 	unsigned int active;
 	int reg, ret;
 
+	ret = pm_runtime_get_sync(dev);
+	if (ret < 0) {
+		dev_err(dev, "Failed to resume: %d\n", ret);
+		return IRQ_NONE;
+	}
+
 	ret = regmap_read(wm8962->regmap, WM8962_INTERRUPT_STATUS_2_MASK,
 			  &mask);
 	if (ret != 0) {
+		pm_runtime_put(dev);
 		dev_err(dev, "Failed to read interrupt mask: %d\n",
 			ret);
 		return IRQ_NONE;
@@ -3013,14 +3027,17 @@ static irqreturn_t wm8962_irq(int irq, void *data)
 
 	ret = regmap_read(wm8962->regmap, WM8962_INTERRUPT_STATUS_2, &active);
 	if (ret != 0) {
+		pm_runtime_put(dev);
 		dev_err(dev, "Failed to read interrupt: %d\n", ret);
 		return IRQ_NONE;
 	}
 
 	active &= ~mask;
 
-	if (!active)
+	if (!active) {
+		pm_runtime_put(dev);
 		return IRQ_NONE;
+	}
 
 	/* Acknowledge the interrupts */
 	ret = regmap_write(wm8962->regmap, WM8962_INTERRUPT_STATUS_2, active);
@@ -3070,6 +3087,8 @@ static irqreturn_t wm8962_irq(int irq, void *data)
 				   msecs_to_jiffies(250));
 	}
 
+	pm_runtime_put(dev);
+
 	return IRQ_HANDLED;
 }
 
@@ -3089,6 +3108,7 @@ static irqreturn_t wm8962_irq(int irq, void *data)
 int wm8962_mic_detect(struct snd_soc_codec *codec, struct snd_soc_jack *jack)
 {
 	struct wm8962_priv *wm8962 = snd_soc_codec_get_drvdata(codec);
+	struct snd_soc_dapm_context *dapm = &codec->dapm;
 	int irq_mask, enable;
 
 	wm8962->jack = jack;
@@ -3109,14 +3129,18 @@ int wm8962_mic_detect(struct snd_soc_codec *codec, struct snd_soc_jack *jack)
 	snd_soc_jack_report(wm8962->jack, 0,
 			    SND_JACK_MICROPHONE | SND_JACK_BTN_0);
 
+	snd_soc_dapm_mutex_lock(dapm);
+
 	if (jack) {
-		snd_soc_dapm_force_enable_pin(&codec->dapm, "SYSCLK");
-		snd_soc_dapm_force_enable_pin(&codec->dapm, "MICBIAS");
+		snd_soc_dapm_force_enable_pin_unlocked(dapm, "SYSCLK");
+		snd_soc_dapm_force_enable_pin_unlocked(dapm, "MICBIAS");
 	} else {
-		snd_soc_dapm_disable_pin(&codec->dapm, "SYSCLK");
-		snd_soc_dapm_disable_pin(&codec->dapm, "MICBIAS");
+		snd_soc_dapm_disable_pin_unlocked(dapm, "SYSCLK");
+		snd_soc_dapm_disable_pin_unlocked(dapm, "MICBIAS");
 	}
 
+	snd_soc_dapm_mutex_unlock(dapm);
+
 	return 0;
 }
 EXPORT_SYMBOL_GPL(wm8962_mic_detect);
diff --git a/sound/soc/codecs/wm8974.c b/sound/soc/codecs/wm8974.c
index 15f45c7bd833..6e16c4306461 100644
--- a/sound/soc/codecs/wm8974.c
+++ b/sound/soc/codecs/wm8974.c
@@ -84,8 +84,8 @@ static const struct soc_enum wm8974_enum[] = {
 
 static const char *wm8974_auxmode_text[] = { "Buffer", "Mixer" };
 
-static const struct soc_enum wm8974_auxmode =
-	SOC_ENUM_SINGLE(WM8974_INPUT,  3, 2, wm8974_auxmode_text);
+static SOC_ENUM_SINGLE_DECL(wm8974_auxmode,
+			    WM8974_INPUT,  3, wm8974_auxmode_text);
 
 static const DECLARE_TLV_DB_SCALE(digital_tlv, -12750, 50, 1);
 static const DECLARE_TLV_DB_SCALE(eq_tlv, -1200, 100, 0);
diff --git a/sound/soc/codecs/wm8978.c b/sound/soc/codecs/wm8978.c
index d8fc531c0e59..a9e2f465c331 100644
--- a/sound/soc/codecs/wm8978.c
+++ b/sound/soc/codecs/wm8978.c
@@ -117,21 +117,21 @@ static const char *wm8978_eq5[] = {"5.3kHz", "6.9kHz", "9kHz", "11.7kHz"};
 static const char *wm8978_alc3[] = {"ALC", "Limiter"};
 static const char *wm8978_alc1[] = {"Off", "Right", "Left", "Both"};
 
-static const SOC_ENUM_SINGLE_DECL(adc_compand, WM8978_COMPANDING_CONTROL, 1,
-				  wm8978_companding);
-static const SOC_ENUM_SINGLE_DECL(dac_compand, WM8978_COMPANDING_CONTROL, 3,
-				  wm8978_companding);
-static const SOC_ENUM_SINGLE_DECL(eqmode, WM8978_EQ1, 8, wm8978_eqmode);
-static const SOC_ENUM_SINGLE_DECL(eq1, WM8978_EQ1, 5, wm8978_eq1);
-static const SOC_ENUM_SINGLE_DECL(eq2bw, WM8978_EQ2, 8, wm8978_bw);
-static const SOC_ENUM_SINGLE_DECL(eq2, WM8978_EQ2, 5, wm8978_eq2);
-static const SOC_ENUM_SINGLE_DECL(eq3bw, WM8978_EQ3, 8, wm8978_bw);
-static const SOC_ENUM_SINGLE_DECL(eq3, WM8978_EQ3, 5, wm8978_eq3);
-static const SOC_ENUM_SINGLE_DECL(eq4bw, WM8978_EQ4, 8, wm8978_bw);
-static const SOC_ENUM_SINGLE_DECL(eq4, WM8978_EQ4, 5, wm8978_eq4);
-static const SOC_ENUM_SINGLE_DECL(eq5, WM8978_EQ5, 5, wm8978_eq5);
-static const SOC_ENUM_SINGLE_DECL(alc3, WM8978_ALC_CONTROL_3, 8, wm8978_alc3);
-static const SOC_ENUM_SINGLE_DECL(alc1, WM8978_ALC_CONTROL_1, 7, wm8978_alc1);
+static SOC_ENUM_SINGLE_DECL(adc_compand, WM8978_COMPANDING_CONTROL, 1,
+			    wm8978_companding);
+static SOC_ENUM_SINGLE_DECL(dac_compand, WM8978_COMPANDING_CONTROL, 3,
+			    wm8978_companding);
+static SOC_ENUM_SINGLE_DECL(eqmode, WM8978_EQ1, 8, wm8978_eqmode);
+static SOC_ENUM_SINGLE_DECL(eq1, WM8978_EQ1, 5, wm8978_eq1);
+static SOC_ENUM_SINGLE_DECL(eq2bw, WM8978_EQ2, 8, wm8978_bw);
+static SOC_ENUM_SINGLE_DECL(eq2, WM8978_EQ2, 5, wm8978_eq2);
+static SOC_ENUM_SINGLE_DECL(eq3bw, WM8978_EQ3, 8, wm8978_bw);
+static SOC_ENUM_SINGLE_DECL(eq3, WM8978_EQ3, 5, wm8978_eq3);
+static SOC_ENUM_SINGLE_DECL(eq4bw, WM8978_EQ4, 8, wm8978_bw);
+static SOC_ENUM_SINGLE_DECL(eq4, WM8978_EQ4, 5, wm8978_eq4);
+static SOC_ENUM_SINGLE_DECL(eq5, WM8978_EQ5, 5, wm8978_eq5);
+static SOC_ENUM_SINGLE_DECL(alc3, WM8978_ALC_CONTROL_3, 8, wm8978_alc3);
+static SOC_ENUM_SINGLE_DECL(alc1, WM8978_ALC_CONTROL_1, 7, wm8978_alc1);
 
 static const DECLARE_TLV_DB_SCALE(digital_tlv, -12750, 50, 1);
 static const DECLARE_TLV_DB_SCALE(eq_tlv, -1200, 100, 0);
diff --git a/sound/soc/codecs/wm8983.c b/sound/soc/codecs/wm8983.c
index aa41ba0dfff4..58f0551eed2d 100644
--- a/sound/soc/codecs/wm8983.c
+++ b/sound/soc/codecs/wm8983.c
@@ -205,49 +205,44 @@ static const DECLARE_TLV_DB_SCALE(bypass_tlv, -1500, 300, 0);
 static const DECLARE_TLV_DB_SCALE(pga_boost_tlv, 0, 2000, 0);
 
 static const char *alc_sel_text[] = { "Off", "Right", "Left", "Stereo" };
-static const SOC_ENUM_SINGLE_DECL(alc_sel, WM8983_ALC_CONTROL_1, 7,
-				  alc_sel_text);
+static SOC_ENUM_SINGLE_DECL(alc_sel, WM8983_ALC_CONTROL_1, 7, alc_sel_text);
 
 static const char *alc_mode_text[] = { "ALC", "Limiter" };
-static const SOC_ENUM_SINGLE_DECL(alc_mode, WM8983_ALC_CONTROL_3, 8,
-				  alc_mode_text);
+static SOC_ENUM_SINGLE_DECL(alc_mode, WM8983_ALC_CONTROL_3, 8, alc_mode_text);
 
 static const char *filter_mode_text[] = { "Audio", "Application" };
-static const SOC_ENUM_SINGLE_DECL(filter_mode, WM8983_ADC_CONTROL, 7,
-				  filter_mode_text);
+static SOC_ENUM_SINGLE_DECL(filter_mode, WM8983_ADC_CONTROL, 7,
+			    filter_mode_text);
 
 static const char *eq_bw_text[] = { "Narrow", "Wide" };
 static const char *eqmode_text[] = { "Capture", "Playback" };
-static const SOC_ENUM_SINGLE_EXT_DECL(eqmode, eqmode_text);
+static SOC_ENUM_SINGLE_EXT_DECL(eqmode, eqmode_text);
 
 static const char *eq1_cutoff_text[] = {
 	"80Hz", "105Hz", "135Hz", "175Hz"
 };
-static const SOC_ENUM_SINGLE_DECL(eq1_cutoff, WM8983_EQ1_LOW_SHELF, 5,
-				  eq1_cutoff_text);
+static SOC_ENUM_SINGLE_DECL(eq1_cutoff, WM8983_EQ1_LOW_SHELF, 5,
+			    eq1_cutoff_text);
 static const char *eq2_cutoff_text[] = {
 	"230Hz", "300Hz", "385Hz", "500Hz"
 };
-static const SOC_ENUM_SINGLE_DECL(eq2_bw, WM8983_EQ2_PEAK_1, 8, eq_bw_text);
-static const SOC_ENUM_SINGLE_DECL(eq2_cutoff, WM8983_EQ2_PEAK_1, 5,
-				  eq2_cutoff_text);
+static SOC_ENUM_SINGLE_DECL(eq2_bw, WM8983_EQ2_PEAK_1, 8, eq_bw_text);
+static SOC_ENUM_SINGLE_DECL(eq2_cutoff, WM8983_EQ2_PEAK_1, 5, eq2_cutoff_text);
 static const char *eq3_cutoff_text[] = {
 	"650Hz", "850Hz", "1.1kHz", "1.4kHz"
 };
-static const SOC_ENUM_SINGLE_DECL(eq3_bw, WM8983_EQ3_PEAK_2, 8, eq_bw_text);
-static const SOC_ENUM_SINGLE_DECL(eq3_cutoff, WM8983_EQ3_PEAK_2, 5,
-				  eq3_cutoff_text);
+static SOC_ENUM_SINGLE_DECL(eq3_bw, WM8983_EQ3_PEAK_2, 8, eq_bw_text);
+static SOC_ENUM_SINGLE_DECL(eq3_cutoff, WM8983_EQ3_PEAK_2, 5, eq3_cutoff_text);
 static const char *eq4_cutoff_text[] = {
 	"1.8kHz", "2.4kHz", "3.2kHz", "4.1kHz"
 };
-static const SOC_ENUM_SINGLE_DECL(eq4_bw, WM8983_EQ4_PEAK_3, 8, eq_bw_text);
-static const SOC_ENUM_SINGLE_DECL(eq4_cutoff, WM8983_EQ4_PEAK_3, 5,
-				  eq4_cutoff_text);
+static SOC_ENUM_SINGLE_DECL(eq4_bw, WM8983_EQ4_PEAK_3, 8, eq_bw_text);
+static SOC_ENUM_SINGLE_DECL(eq4_cutoff, WM8983_EQ4_PEAK_3, 5, eq4_cutoff_text);
 static const char *eq5_cutoff_text[] = {
 	"5.3kHz", "6.9kHz", "9kHz", "11.7kHz"
 };
-static const SOC_ENUM_SINGLE_DECL(eq5_cutoff, WM8983_EQ5_HIGH_SHELF, 5,
-				  eq5_cutoff_text);
+static SOC_ENUM_SINGLE_DECL(eq5_cutoff, WM8983_EQ5_HIGH_SHELF, 5,
+			    eq5_cutoff_text);
 
 static const char *depth_3d_text[] = {
 	"Off",
@@ -267,8 +262,8 @@ static const char *depth_3d_text[] = {
 	"93.3%",
 	"100%"
 };
-static const SOC_ENUM_SINGLE_DECL(depth_3d, WM8983_3D_CONTROL, 0,
-				  depth_3d_text);
+static SOC_ENUM_SINGLE_DECL(depth_3d, WM8983_3D_CONTROL, 0,
+			    depth_3d_text);
 
 static const struct snd_kcontrol_new wm8983_snd_controls[] = {
 	SOC_SINGLE("Digital Loopback Switch", WM8983_COMPANDING_CONTROL,
@@ -1129,7 +1124,7 @@ static struct spi_driver wm8983_spi_driver = {
 };
 #endif
 
-#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+#if IS_ENABLED(CONFIG_I2C)
 static int wm8983_i2c_probe(struct i2c_client *i2c,
 			    const struct i2c_device_id *id)
 {
@@ -1182,7 +1177,7 @@ static int __init wm8983_modinit(void)
 {
 	int ret = 0;
 
-#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+#if IS_ENABLED(CONFIG_I2C)
 	ret = i2c_add_driver(&wm8983_i2c_driver);
 	if (ret) {
 		printk(KERN_ERR "Failed to register wm8983 I2C driver: %d\n",
@@ -1202,7 +1197,7 @@ module_init(wm8983_modinit);
 
 static void __exit wm8983_exit(void)
 {
-#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+#if IS_ENABLED(CONFIG_I2C)
 	i2c_del_driver(&wm8983_i2c_driver);
 #endif
 #if defined(CONFIG_SPI_MASTER)
diff --git a/sound/soc/codecs/wm8985.c b/sound/soc/codecs/wm8985.c
index 271b517911a4..d786f2b39764 100644
--- a/sound/soc/codecs/wm8985.c
+++ b/sound/soc/codecs/wm8985.c
@@ -226,52 +226,48 @@ static const DECLARE_TLV_DB_SCALE(bypass_tlv, -1500, 300, 0);
 static const DECLARE_TLV_DB_SCALE(pga_boost_tlv, 0, 2000, 0);
 
 static const char *alc_sel_text[] = { "Off", "Right", "Left", "Stereo" };
-static const SOC_ENUM_SINGLE_DECL(alc_sel, WM8985_ALC_CONTROL_1, 7,
-				  alc_sel_text);
+static SOC_ENUM_SINGLE_DECL(alc_sel, WM8985_ALC_CONTROL_1, 7, alc_sel_text);
 
 static const char *alc_mode_text[] = { "ALC", "Limiter" };
-static const SOC_ENUM_SINGLE_DECL(alc_mode, WM8985_ALC_CONTROL_3, 8,
-				  alc_mode_text);
+static SOC_ENUM_SINGLE_DECL(alc_mode, WM8985_ALC_CONTROL_3, 8, alc_mode_text);
 
 static const char *filter_mode_text[] = { "Audio", "Application" };
-static const SOC_ENUM_SINGLE_DECL(filter_mode, WM8985_ADC_CONTROL, 7,
-				  filter_mode_text);
+static SOC_ENUM_SINGLE_DECL(filter_mode, WM8985_ADC_CONTROL, 7,
+			    filter_mode_text);
 
 static const char *eq_bw_text[] = { "Narrow", "Wide" };
 static const char *eqmode_text[] = { "Capture", "Playback" };
-static const SOC_ENUM_SINGLE_EXT_DECL(eqmode, eqmode_text);
+static SOC_ENUM_SINGLE_EXT_DECL(eqmode, eqmode_text);
 
 static const char *eq1_cutoff_text[] = {
 	"80Hz", "105Hz", "135Hz", "175Hz"
 };
-static const SOC_ENUM_SINGLE_DECL(eq1_cutoff, WM8985_EQ1_LOW_SHELF, 5,
-				  eq1_cutoff_text);
+static SOC_ENUM_SINGLE_DECL(eq1_cutoff, WM8985_EQ1_LOW_SHELF, 5,
+			    eq1_cutoff_text);
 static const char *eq2_cutoff_text[] = {
 	"230Hz", "300Hz", "385Hz", "500Hz"
 };
-static const SOC_ENUM_SINGLE_DECL(eq2_bw, WM8985_EQ2_PEAK_1, 8, eq_bw_text);
-static const SOC_ENUM_SINGLE_DECL(eq2_cutoff, WM8985_EQ2_PEAK_1, 5,
-				  eq2_cutoff_text);
+static SOC_ENUM_SINGLE_DECL(eq2_bw, WM8985_EQ2_PEAK_1, 8, eq_bw_text);
+static SOC_ENUM_SINGLE_DECL(eq2_cutoff, WM8985_EQ2_PEAK_1, 5, eq2_cutoff_text);
 static const char *eq3_cutoff_text[] = {
 	"650Hz", "850Hz", "1.1kHz", "1.4kHz"
 };
-static const SOC_ENUM_SINGLE_DECL(eq3_bw, WM8985_EQ3_PEAK_2, 8, eq_bw_text);
-static const SOC_ENUM_SINGLE_DECL(eq3_cutoff, WM8985_EQ3_PEAK_2, 5,
-				  eq3_cutoff_text);
+static SOC_ENUM_SINGLE_DECL(eq3_bw, WM8985_EQ3_PEAK_2, 8, eq_bw_text);
+static SOC_ENUM_SINGLE_DECL(eq3_cutoff, WM8985_EQ3_PEAK_2, 5,
+			    eq3_cutoff_text);
 static const char *eq4_cutoff_text[] = {
 	"1.8kHz", "2.4kHz", "3.2kHz", "4.1kHz"
 };
-static const SOC_ENUM_SINGLE_DECL(eq4_bw, WM8985_EQ4_PEAK_3, 8, eq_bw_text);
-static const SOC_ENUM_SINGLE_DECL(eq4_cutoff, WM8985_EQ4_PEAK_3, 5,
-				  eq4_cutoff_text);
+static SOC_ENUM_SINGLE_DECL(eq4_bw, WM8985_EQ4_PEAK_3, 8, eq_bw_text);
+static SOC_ENUM_SINGLE_DECL(eq4_cutoff, WM8985_EQ4_PEAK_3, 5, eq4_cutoff_text);
 static const char *eq5_cutoff_text[] = {
 	"5.3kHz", "6.9kHz", "9kHz", "11.7kHz"
 };
-static const SOC_ENUM_SINGLE_DECL(eq5_cutoff, WM8985_EQ5_HIGH_SHELF, 5,
+static SOC_ENUM_SINGLE_DECL(eq5_cutoff, WM8985_EQ5_HIGH_SHELF, 5,
 				  eq5_cutoff_text);
 
 static const char *speaker_mode_text[] = { "Class A/B", "Class D" };
-static const SOC_ENUM_SINGLE_DECL(speaker_mode, 0x17, 8, speaker_mode_text);
+static SOC_ENUM_SINGLE_DECL(speaker_mode, 0x17, 8, speaker_mode_text);
 
 static const char *depth_3d_text[] = {
 	"Off",
@@ -291,8 +287,7 @@ static const char *depth_3d_text[] = {
 	"93.3%",
 	"100%"
 };
-static const SOC_ENUM_SINGLE_DECL(depth_3d, WM8985_3D_CONTROL, 0,
-				  depth_3d_text);
+static SOC_ENUM_SINGLE_DECL(depth_3d, WM8985_3D_CONTROL, 0, depth_3d_text);
 
 static const struct snd_kcontrol_new wm8985_snd_controls[] = {
 	SOC_SINGLE("Digital Loopback Switch", WM8985_COMPANDING_CONTROL,
diff --git a/sound/soc/codecs/wm8988.c b/sound/soc/codecs/wm8988.c
index a55e1c2c382e..0277a76c6bef 100644
--- a/sound/soc/codecs/wm8988.c
+++ b/sound/soc/codecs/wm8988.c
@@ -116,7 +116,7 @@ static bool wm8988_writeable(struct device *dev, unsigned int reg)
 struct wm8988_priv {
 	struct regmap *regmap;
 	unsigned int sysclk;
-	struct snd_pcm_hw_constraint_list *sysclk_constraints;
+	const struct snd_pcm_hw_constraint_list *sysclk_constraints;
 };
 
 #define wm8988_reset(c)	snd_soc_write(c, WM8988_RESET, 0)
@@ -126,46 +126,46 @@ struct wm8988_priv {
  */
 
 static const char *bass_boost_txt[] = {"Linear Control", "Adaptive Boost"};
-static const struct soc_enum bass_boost =
-	SOC_ENUM_SINGLE(WM8988_BASS, 7, 2, bass_boost_txt);
+static SOC_ENUM_SINGLE_DECL(bass_boost,
+			    WM8988_BASS, 7, bass_boost_txt);
 
 static const char *bass_filter_txt[] = { "130Hz @ 48kHz", "200Hz @ 48kHz" };
-static const struct soc_enum bass_filter =
-	SOC_ENUM_SINGLE(WM8988_BASS, 6, 2, bass_filter_txt);
+static SOC_ENUM_SINGLE_DECL(bass_filter,
+			    WM8988_BASS, 6, bass_filter_txt);
 
 static const char *treble_txt[] = {"8kHz", "4kHz"};
-static const struct soc_enum treble =
-	SOC_ENUM_SINGLE(WM8988_TREBLE, 6, 2, treble_txt);
+static SOC_ENUM_SINGLE_DECL(treble,
+			    WM8988_TREBLE, 6, treble_txt);
 
 static const char *stereo_3d_lc_txt[] = {"200Hz", "500Hz"};
-static const struct soc_enum stereo_3d_lc =
-	SOC_ENUM_SINGLE(WM8988_3D, 5, 2, stereo_3d_lc_txt);
+static SOC_ENUM_SINGLE_DECL(stereo_3d_lc,
+			    WM8988_3D, 5, stereo_3d_lc_txt);
 
 static const char *stereo_3d_uc_txt[] = {"2.2kHz", "1.5kHz"};
-static const struct soc_enum stereo_3d_uc =
-	SOC_ENUM_SINGLE(WM8988_3D, 6, 2, stereo_3d_uc_txt);
+static SOC_ENUM_SINGLE_DECL(stereo_3d_uc,
+			    WM8988_3D, 6, stereo_3d_uc_txt);
 
 static const char *stereo_3d_func_txt[] = {"Capture", "Playback"};
-static const struct soc_enum stereo_3d_func =
-	SOC_ENUM_SINGLE(WM8988_3D, 7, 2, stereo_3d_func_txt);
+static SOC_ENUM_SINGLE_DECL(stereo_3d_func,
+			    WM8988_3D, 7, stereo_3d_func_txt);
 
 static const char *alc_func_txt[] = {"Off", "Right", "Left", "Stereo"};
-static const struct soc_enum alc_func =
-	SOC_ENUM_SINGLE(WM8988_ALC1, 7, 4, alc_func_txt);
+static SOC_ENUM_SINGLE_DECL(alc_func,
+			    WM8988_ALC1, 7, alc_func_txt);
 
 static const char *ng_type_txt[] = {"Constant PGA Gain",
 				    "Mute ADC Output"};
-static const struct soc_enum ng_type =
-	SOC_ENUM_SINGLE(WM8988_NGATE, 1, 2, ng_type_txt);
+static SOC_ENUM_SINGLE_DECL(ng_type,
+			    WM8988_NGATE, 1, ng_type_txt);
 
 static const char *deemph_txt[] = {"None", "32Khz", "44.1Khz", "48Khz"};
-static const struct soc_enum deemph =
-	SOC_ENUM_SINGLE(WM8988_ADCDAC, 1, 4, deemph_txt);
+static SOC_ENUM_SINGLE_DECL(deemph,
+			    WM8988_ADCDAC, 1, deemph_txt);
 
 static const char *adcpol_txt[] = {"Normal", "L Invert", "R Invert",
 				   "L + R Invert"};
-static const struct soc_enum adcpol =
-	SOC_ENUM_SINGLE(WM8988_ADCDAC, 5, 4, adcpol_txt);
+static SOC_ENUM_SINGLE_DECL(adcpol,
+			    WM8988_ADCDAC, 5, adcpol_txt);
 
 static const DECLARE_TLV_DB_SCALE(pga_tlv, -1725, 75, 0);
 static const DECLARE_TLV_DB_SCALE(adc_tlv, -9750, 50, 1);
@@ -317,16 +317,16 @@ static const struct snd_kcontrol_new wm8988_right_pga_controls =
 
 /* Differential Mux */
 static const char *wm8988_diff_sel[] = {"Line 1", "Line 2"};
-static const struct soc_enum diffmux =
-	SOC_ENUM_SINGLE(WM8988_ADCIN, 8, 2, wm8988_diff_sel);
+static SOC_ENUM_SINGLE_DECL(diffmux,
+			    WM8988_ADCIN, 8, wm8988_diff_sel);
 static const struct snd_kcontrol_new wm8988_diffmux_controls =
 	SOC_DAPM_ENUM("Route", diffmux);
 
 /* Mono ADC Mux */
 static const char *wm8988_mono_mux[] = {"Stereo", "Mono (Left)",
 	"Mono (Right)", "Digital Mono"};
-static const struct soc_enum monomux =
-	SOC_ENUM_SINGLE(WM8988_ADCIN, 6, 4, wm8988_mono_mux);
+static SOC_ENUM_SINGLE_DECL(monomux,
+			    WM8988_ADCIN, 6, wm8988_mono_mux);
 static const struct snd_kcontrol_new wm8988_monomux_controls =
 	SOC_DAPM_ENUM("Route", monomux);
 
@@ -521,30 +521,30 @@ static inline int get_coeff(int mclk, int rate)
 
 /* The set of rates we can generate from the above for each SYSCLK */
 
-static unsigned int rates_12288[] = {
+static const unsigned int rates_12288[] = {
 	8000, 12000, 16000, 24000, 24000, 32000, 48000, 96000,
 };
 
-static struct snd_pcm_hw_constraint_list constraints_12288 = {
+static const struct snd_pcm_hw_constraint_list constraints_12288 = {
 	.count	= ARRAY_SIZE(rates_12288),
 	.list	= rates_12288,
 };
 
-static unsigned int rates_112896[] = {
+static const unsigned int rates_112896[] = {
 	8000, 11025, 22050, 44100,
 };
 
-static struct snd_pcm_hw_constraint_list constraints_112896 = {
+static const struct snd_pcm_hw_constraint_list constraints_112896 = {
 	.count	= ARRAY_SIZE(rates_112896),
 	.list	= rates_112896,
 };
 
-static unsigned int rates_12[] = {
+static const unsigned int rates_12[] = {
 	8000, 11025, 12000, 16000, 22050, 2400, 32000, 41100, 48000,
 	48000, 88235, 96000,
 };
 
-static struct snd_pcm_hw_constraint_list constraints_12 = {
+static const struct snd_pcm_hw_constraint_list constraints_12 = {
 	.count	= ARRAY_SIZE(rates_12),
 	.list	= rates_12,
 };
diff --git a/sound/soc/codecs/wm8990.c b/sound/soc/codecs/wm8990.c
index 0ccd4d8d043b..33f53ab1e7b0 100644
--- a/sound/soc/codecs/wm8990.c
+++ b/sound/soc/codecs/wm8990.c
@@ -157,26 +157,23 @@ static int wm899x_outpga_put_volsw_vu(struct snd_kcontrol *kcontrol,
 static const char *wm8990_digital_sidetone[] =
 	{"None", "Left ADC", "Right ADC", "Reserved"};
 
-static const struct soc_enum wm8990_left_digital_sidetone_enum =
-SOC_ENUM_SINGLE(WM8990_DIGITAL_SIDE_TONE,
-	WM8990_ADC_TO_DACL_SHIFT,
-	WM8990_ADC_TO_DACL_MASK,
-	wm8990_digital_sidetone);
-
-static const struct soc_enum wm8990_right_digital_sidetone_enum =
-SOC_ENUM_SINGLE(WM8990_DIGITAL_SIDE_TONE,
-	WM8990_ADC_TO_DACR_SHIFT,
-	WM8990_ADC_TO_DACR_MASK,
-	wm8990_digital_sidetone);
+static SOC_ENUM_SINGLE_DECL(wm8990_left_digital_sidetone_enum,
+			    WM8990_DIGITAL_SIDE_TONE,
+			    WM8990_ADC_TO_DACL_SHIFT,
+			    wm8990_digital_sidetone);
+
+static SOC_ENUM_SINGLE_DECL(wm8990_right_digital_sidetone_enum,
+			    WM8990_DIGITAL_SIDE_TONE,
+			    WM8990_ADC_TO_DACR_SHIFT,
+			    wm8990_digital_sidetone);
 
 static const char *wm8990_adcmode[] =
 	{"Hi-fi mode", "Voice mode 1", "Voice mode 2", "Voice mode 3"};
 
-static const struct soc_enum wm8990_right_adcmode_enum =
-SOC_ENUM_SINGLE(WM8990_ADC_CTRL,
-	WM8990_ADC_HPF_CUT_SHIFT,
-	WM8990_ADC_HPF_CUT_MASK,
-	wm8990_adcmode);
+static SOC_ENUM_SINGLE_DECL(wm8990_right_adcmode_enum,
+			    WM8990_ADC_CTRL,
+			    WM8990_ADC_HPF_CUT_SHIFT,
+			    wm8990_adcmode);
 
 static const struct snd_kcontrol_new wm8990_snd_controls[] = {
 /* INMIXL */
@@ -475,9 +472,9 @@ SOC_DAPM_SINGLE("RINPGA34 Switch", WM8990_INPUT_MIXER3, WM8990_L34MNB_BIT,
 static const char *wm8990_ainlmux[] =
 	{"INMIXL Mix", "RXVOICE Mix", "DIFFINL Mix"};
 
-static const struct soc_enum wm8990_ainlmux_enum =
-SOC_ENUM_SINGLE(WM8990_INPUT_MIXER1, WM8990_AINLMODE_SHIFT,
-	ARRAY_SIZE(wm8990_ainlmux), wm8990_ainlmux);
+static SOC_ENUM_SINGLE_DECL(wm8990_ainlmux_enum,
+			    WM8990_INPUT_MIXER1, WM8990_AINLMODE_SHIFT,
+			    wm8990_ainlmux);
 
 static const struct snd_kcontrol_new wm8990_dapm_ainlmux_controls =
 SOC_DAPM_ENUM("Route", wm8990_ainlmux_enum);
@@ -488,9 +485,9 @@ SOC_DAPM_ENUM("Route", wm8990_ainlmux_enum);
 static const char *wm8990_ainrmux[] =
 	{"INMIXR Mix", "RXVOICE Mix", "DIFFINR Mix"};
 
-static const struct soc_enum wm8990_ainrmux_enum =
-SOC_ENUM_SINGLE(WM8990_INPUT_MIXER1, WM8990_AINRMODE_SHIFT,
-	ARRAY_SIZE(wm8990_ainrmux), wm8990_ainrmux);
+static SOC_ENUM_SINGLE_DECL(wm8990_ainrmux_enum,
+			    WM8990_INPUT_MIXER1, WM8990_AINRMODE_SHIFT,
+			    wm8990_ainrmux);
 
 static const struct snd_kcontrol_new wm8990_dapm_ainrmux_controls =
 SOC_DAPM_ENUM("Route", wm8990_ainrmux_enum);
diff --git a/sound/soc/codecs/wm8991.c b/sound/soc/codecs/wm8991.c
index dba0306c42a5..32d219570cca 100644
--- a/sound/soc/codecs/wm8991.c
+++ b/sound/soc/codecs/wm8991.c
@@ -171,26 +171,23 @@ static int wm899x_outpga_put_volsw_vu(struct snd_kcontrol *kcontrol,
 static const char *wm8991_digital_sidetone[] =
 {"None", "Left ADC", "Right ADC", "Reserved"};
 
-static const struct soc_enum wm8991_left_digital_sidetone_enum =
-	SOC_ENUM_SINGLE(WM8991_DIGITAL_SIDE_TONE,
-			WM8991_ADC_TO_DACL_SHIFT,
-			WM8991_ADC_TO_DACL_MASK,
-			wm8991_digital_sidetone);
-
-static const struct soc_enum wm8991_right_digital_sidetone_enum =
-	SOC_ENUM_SINGLE(WM8991_DIGITAL_SIDE_TONE,
-			WM8991_ADC_TO_DACR_SHIFT,
-			WM8991_ADC_TO_DACR_MASK,
-			wm8991_digital_sidetone);
+static SOC_ENUM_SINGLE_DECL(wm8991_left_digital_sidetone_enum,
+			    WM8991_DIGITAL_SIDE_TONE,
+			    WM8991_ADC_TO_DACL_SHIFT,
+			    wm8991_digital_sidetone);
+
+static SOC_ENUM_SINGLE_DECL(wm8991_right_digital_sidetone_enum,
+			    WM8991_DIGITAL_SIDE_TONE,
+			    WM8991_ADC_TO_DACR_SHIFT,
+			    wm8991_digital_sidetone);
 
 static const char *wm8991_adcmode[] =
 {"Hi-fi mode", "Voice mode 1", "Voice mode 2", "Voice mode 3"};
 
-static const struct soc_enum wm8991_right_adcmode_enum =
-	SOC_ENUM_SINGLE(WM8991_ADC_CTRL,
-			WM8991_ADC_HPF_CUT_SHIFT,
-			WM8991_ADC_HPF_CUT_MASK,
-			wm8991_adcmode);
+static SOC_ENUM_SINGLE_DECL(wm8991_right_adcmode_enum,
+			    WM8991_ADC_CTRL,
+			    WM8991_ADC_HPF_CUT_SHIFT,
+			    wm8991_adcmode);
 
 static const struct snd_kcontrol_new wm8991_snd_controls[] = {
 	/* INMIXL */
@@ -486,9 +483,9 @@ static const struct snd_kcontrol_new wm8991_dapm_inmixr_controls[] = {
 static const char *wm8991_ainlmux[] =
 {"INMIXL Mix", "RXVOICE Mix", "DIFFINL Mix"};
 
-static const struct soc_enum wm8991_ainlmux_enum =
-	SOC_ENUM_SINGLE(WM8991_INPUT_MIXER1, WM8991_AINLMODE_SHIFT,
-			ARRAY_SIZE(wm8991_ainlmux), wm8991_ainlmux);
+static SOC_ENUM_SINGLE_DECL(wm8991_ainlmux_enum,
+			    WM8991_INPUT_MIXER1, WM8991_AINLMODE_SHIFT,
+			    wm8991_ainlmux);
 
 static const struct snd_kcontrol_new wm8991_dapm_ainlmux_controls =
 	SOC_DAPM_ENUM("Route", wm8991_ainlmux_enum);
@@ -499,9 +496,9 @@ static const struct snd_kcontrol_new wm8991_dapm_ainlmux_controls =
 static const char *wm8991_ainrmux[] =
 {"INMIXR Mix", "RXVOICE Mix", "DIFFINR Mix"};
 
-static const struct soc_enum wm8991_ainrmux_enum =
-	SOC_ENUM_SINGLE(WM8991_INPUT_MIXER1, WM8991_AINRMODE_SHIFT,
-			ARRAY_SIZE(wm8991_ainrmux), wm8991_ainrmux);
+static SOC_ENUM_SINGLE_DECL(wm8991_ainrmux_enum,
+			    WM8991_INPUT_MIXER1, WM8991_AINRMODE_SHIFT,
+			    wm8991_ainrmux);
 
 static const struct snd_kcontrol_new wm8991_dapm_ainrmux_controls =
 	SOC_DAPM_ENUM("Route", wm8991_ainrmux_enum);
@@ -1251,11 +1248,8 @@ static int wm8991_remove(struct snd_soc_codec *codec)
 
 static int wm8991_probe(struct snd_soc_codec *codec)
 {
-	struct wm8991_priv *wm8991;
 	int ret;
 
-	wm8991 = snd_soc_codec_get_drvdata(codec);
-
 	ret = snd_soc_codec_set_cache_io(codec, 8, 16, SND_SOC_REGMAP);
 	if (ret < 0) {
 		dev_err(codec->dev, "Failed to set cache i/o: %d\n", ret);
diff --git a/sound/soc/codecs/wm8993.c b/sound/soc/codecs/wm8993.c
index 2ee23a39622c..7b0630a076fa 100644
--- a/sound/soc/codecs/wm8993.c
+++ b/sound/soc/codecs/wm8993.c
@@ -646,8 +646,8 @@ static const char *dac_deemph_text[] = {
 	"48kHz",
 };
 
-static const struct soc_enum dac_deemph =
-	SOC_ENUM_SINGLE(WM8993_DAC_CTRL, 4, 4, dac_deemph_text);
+static SOC_ENUM_SINGLE_DECL(dac_deemph,
+			    WM8993_DAC_CTRL, 4, dac_deemph_text);
 
 static const char *adc_hpf_text[] = {
 	"Hi-Fi",
@@ -656,16 +656,16 @@ static const char *adc_hpf_text[] = {
 	"Voice 3",
 };
 
-static const struct soc_enum adc_hpf =
-	SOC_ENUM_SINGLE(WM8993_ADC_CTRL, 5, 4, adc_hpf_text);
+static SOC_ENUM_SINGLE_DECL(adc_hpf,
+			    WM8993_ADC_CTRL, 5, adc_hpf_text);
 
 static const char *drc_path_text[] = {
 	"ADC",
 	"DAC"
 };
 
-static const struct soc_enum drc_path =
-	SOC_ENUM_SINGLE(WM8993_DRC_CONTROL_1, 14, 2, drc_path_text);
+static SOC_ENUM_SINGLE_DECL(drc_path,
+			    WM8993_DRC_CONTROL_1, 14, drc_path_text);
 
 static const char *drc_r0_text[] = {
 	"1",
@@ -676,8 +676,8 @@ static const char *drc_r0_text[] = {
 	"0",
 };
 
-static const struct soc_enum drc_r0 =
-	SOC_ENUM_SINGLE(WM8993_DRC_CONTROL_3, 8, 6, drc_r0_text);
+static SOC_ENUM_SINGLE_DECL(drc_r0,
+			    WM8993_DRC_CONTROL_3, 8, drc_r0_text);
 
 static const char *drc_r1_text[] = {
 	"1",
@@ -687,8 +687,8 @@ static const char *drc_r1_text[] = {
 	"0",
 };
 
-static const struct soc_enum drc_r1 =
-	SOC_ENUM_SINGLE(WM8993_DRC_CONTROL_4, 13, 5, drc_r1_text);
+static SOC_ENUM_SINGLE_DECL(drc_r1,
+			    WM8993_DRC_CONTROL_4, 13, drc_r1_text);
 
 static const char *drc_attack_text[] = {
 	"Reserved",
@@ -705,8 +705,8 @@ static const char *drc_attack_text[] = {
 	"185.6ms",
 };
 
-static const struct soc_enum drc_attack =
-	SOC_ENUM_SINGLE(WM8993_DRC_CONTROL_2, 12, 12, drc_attack_text);
+static SOC_ENUM_SINGLE_DECL(drc_attack,
+			    WM8993_DRC_CONTROL_2, 12, drc_attack_text);
 
 static const char *drc_decay_text[] = {
 	"186ms",
@@ -720,16 +720,16 @@ static const char *drc_decay_text[] = {
 	"47.56ms",
 };
 
-static const struct soc_enum drc_decay =
-	SOC_ENUM_SINGLE(WM8993_DRC_CONTROL_2, 8, 9, drc_decay_text);
+static SOC_ENUM_SINGLE_DECL(drc_decay,
+			    WM8993_DRC_CONTROL_2, 8, drc_decay_text);
 
 static const char *drc_ff_text[] = {
 	"5 samples",
 	"9 samples",
 };
 
-static const struct soc_enum drc_ff =
-	SOC_ENUM_SINGLE(WM8993_DRC_CONTROL_3, 7, 2, drc_ff_text);
+static SOC_ENUM_SINGLE_DECL(drc_ff,
+			    WM8993_DRC_CONTROL_3, 7, drc_ff_text);
 
 static const char *drc_qr_rate_text[] = {
 	"0.725ms",
@@ -737,8 +737,8 @@ static const char *drc_qr_rate_text[] = {
 	"5.8ms",
 };
 
-static const struct soc_enum drc_qr_rate =
-	SOC_ENUM_SINGLE(WM8993_DRC_CONTROL_3, 0, 3, drc_qr_rate_text);
+static SOC_ENUM_SINGLE_DECL(drc_qr_rate,
+			    WM8993_DRC_CONTROL_3, 0, drc_qr_rate_text);
 
 static const char *drc_smooth_text[] = {
 	"Low",
@@ -746,8 +746,8 @@ static const char *drc_smooth_text[] = {
 	"High",
 };
 
-static const struct soc_enum drc_smooth =
-	SOC_ENUM_SINGLE(WM8993_DRC_CONTROL_1, 4, 3, drc_smooth_text);
+static SOC_ENUM_SINGLE_DECL(drc_smooth,
+			    WM8993_DRC_CONTROL_1, 4, drc_smooth_text);
 
 static const struct snd_kcontrol_new wm8993_snd_controls[] = {
 SOC_DOUBLE_TLV("Digital Sidetone Volume", WM8993_DIGITAL_SIDE_TONE,
@@ -841,26 +841,26 @@ static const char *aif_text[] = {
 	"Left", "Right"
 };
 
-static const struct soc_enum aifoutl_enum =
-	SOC_ENUM_SINGLE(WM8993_AUDIO_INTERFACE_1, 15, 2, aif_text);
+static SOC_ENUM_SINGLE_DECL(aifoutl_enum,
+			    WM8993_AUDIO_INTERFACE_1, 15, aif_text);
 
 static const struct snd_kcontrol_new aifoutl_mux =
 	SOC_DAPM_ENUM("AIFOUTL Mux", aifoutl_enum);
 
-static const struct soc_enum aifoutr_enum =
-	SOC_ENUM_SINGLE(WM8993_AUDIO_INTERFACE_1, 14, 2, aif_text);
+static SOC_ENUM_SINGLE_DECL(aifoutr_enum,
+			    WM8993_AUDIO_INTERFACE_1, 14, aif_text);
 
 static const struct snd_kcontrol_new aifoutr_mux =
 	SOC_DAPM_ENUM("AIFOUTR Mux", aifoutr_enum);
 
-static const struct soc_enum aifinl_enum =
-	SOC_ENUM_SINGLE(WM8993_AUDIO_INTERFACE_2, 15, 2, aif_text);
+static SOC_ENUM_SINGLE_DECL(aifinl_enum,
+			    WM8993_AUDIO_INTERFACE_2, 15, aif_text);
 
 static const struct snd_kcontrol_new aifinl_mux =
 	SOC_DAPM_ENUM("AIFINL Mux", aifinl_enum);
 
-static const struct soc_enum aifinr_enum =
-	SOC_ENUM_SINGLE(WM8993_AUDIO_INTERFACE_2, 14, 2, aif_text);
+static SOC_ENUM_SINGLE_DECL(aifinr_enum,
+			    WM8993_AUDIO_INTERFACE_2, 14, aif_text);
 
 static const struct snd_kcontrol_new aifinr_mux =
 	SOC_DAPM_ENUM("AIFINR Mux", aifinr_enum);
@@ -869,14 +869,14 @@ static const char *sidetone_text[] = {
 	"None", "Left", "Right"
 };
 
-static const struct soc_enum sidetonel_enum =
-	SOC_ENUM_SINGLE(WM8993_DIGITAL_SIDE_TONE, 2, 3, sidetone_text);
+static SOC_ENUM_SINGLE_DECL(sidetonel_enum,
+			    WM8993_DIGITAL_SIDE_TONE, 2, sidetone_text);
 
 static const struct snd_kcontrol_new sidetonel_mux =
 	SOC_DAPM_ENUM("Left Sidetone", sidetonel_enum);
 
-static const struct soc_enum sidetoner_enum =
-	SOC_ENUM_SINGLE(WM8993_DIGITAL_SIDE_TONE, 0, 3, sidetone_text);
+static SOC_ENUM_SINGLE_DECL(sidetoner_enum,
+			    WM8993_DIGITAL_SIDE_TONE, 0, sidetone_text);
 
 static const struct snd_kcontrol_new sidetoner_mux =
 	SOC_DAPM_ENUM("Right Sidetone", sidetoner_enum);
@@ -1559,8 +1559,6 @@ static int wm8993_probe(struct snd_soc_codec *codec)
 
 static int wm8993_remove(struct snd_soc_codec *codec)
 {
-	struct wm8993_priv *wm8993 = snd_soc_codec_get_drvdata(codec);
-
 	wm8993_set_bias_level(codec, SND_SOC_BIAS_OFF);
 	return 0;
 }
diff --git a/sound/soc/codecs/wm8994.c b/sound/soc/codecs/wm8994.c
index adb72063d44e..79854cb7feb6 100644
--- a/sound/soc/codecs/wm8994.c
+++ b/sound/soc/codecs/wm8994.c
@@ -1344,8 +1344,7 @@ static const char *adc_mux_text[] = {
 	"DMIC",
 };
 
-static SOC_ENUM_SINGLE_DECL(adc_enum,
-			    0, 0, adc_mux_text);
+static SOC_ENUM_SINGLE_VIRT_DECL(adc_enum, adc_mux_text);
 
 static const struct snd_kcontrol_new adcl_mux =
 	SOC_DAPM_ENUM_VIRT("ADCL Mux", adc_enum);
@@ -2554,43 +2553,52 @@ static int wm8994_set_bias_level(struct snd_soc_codec *codec,
 int wm8994_vmid_mode(struct snd_soc_codec *codec, enum wm8994_vmid_mode mode)
 {
 	struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
+	struct snd_soc_dapm_context *dapm = &codec->dapm;
 
 	switch (mode) {
 	case WM8994_VMID_NORMAL:
+		snd_soc_dapm_mutex_lock(dapm);
+
 		if (wm8994->hubs.lineout1_se) {
-			snd_soc_dapm_disable_pin(&codec->dapm,
-						 "LINEOUT1N Driver");
-			snd_soc_dapm_disable_pin(&codec->dapm,
-						 "LINEOUT1P Driver");
+			snd_soc_dapm_disable_pin_unlocked(dapm,
+							  "LINEOUT1N Driver");
+			snd_soc_dapm_disable_pin_unlocked(dapm,
+							  "LINEOUT1P Driver");
 		}
 		if (wm8994->hubs.lineout2_se) {
-			snd_soc_dapm_disable_pin(&codec->dapm,
-						 "LINEOUT2N Driver");
-			snd_soc_dapm_disable_pin(&codec->dapm,
-						 "LINEOUT2P Driver");
+			snd_soc_dapm_disable_pin_unlocked(dapm,
+							  "LINEOUT2N Driver");
+			snd_soc_dapm_disable_pin_unlocked(dapm,
+							  "LINEOUT2P Driver");
 		}
 
 		/* Do the sync with the old mode to allow it to clean up */
-		snd_soc_dapm_sync(&codec->dapm);
+		snd_soc_dapm_sync_unlocked(dapm);
 		wm8994->vmid_mode = mode;
+
+		snd_soc_dapm_mutex_unlock(dapm);
 		break;
 
 	case WM8994_VMID_FORCE:
+		snd_soc_dapm_mutex_lock(dapm);
+
 		if (wm8994->hubs.lineout1_se) {
-			snd_soc_dapm_force_enable_pin(&codec->dapm,
-						      "LINEOUT1N Driver");
-			snd_soc_dapm_force_enable_pin(&codec->dapm,
-						      "LINEOUT1P Driver");
+			snd_soc_dapm_force_enable_pin_unlocked(dapm,
+							       "LINEOUT1N Driver");
+			snd_soc_dapm_force_enable_pin_unlocked(dapm,
+							       "LINEOUT1P Driver");
 		}
 		if (wm8994->hubs.lineout2_se) {
-			snd_soc_dapm_force_enable_pin(&codec->dapm,
-						      "LINEOUT2N Driver");
-			snd_soc_dapm_force_enable_pin(&codec->dapm,
-						      "LINEOUT2P Driver");
+			snd_soc_dapm_force_enable_pin_unlocked(dapm,
+							       "LINEOUT2N Driver");
+			snd_soc_dapm_force_enable_pin_unlocked(dapm,
+							       "LINEOUT2P Driver");
 		}
 
 		wm8994->vmid_mode = mode;
-		snd_soc_dapm_sync(&codec->dapm);
+		snd_soc_dapm_sync_unlocked(dapm);
+
+		snd_soc_dapm_mutex_unlock(dapm);
 		break;
 
 	default:
@@ -3242,7 +3250,7 @@ static void wm8994_handle_retune_mobile_pdata(struct wm8994_priv *wm8994)
 	dev_dbg(codec->dev, "Allocated %d unique ReTune Mobile names\n",
 		wm8994->num_retune_mobile_texts);
 
-	wm8994->retune_mobile_enum.max = wm8994->num_retune_mobile_texts;
+	wm8994->retune_mobile_enum.items = wm8994->num_retune_mobile_texts;
 	wm8994->retune_mobile_enum.texts = wm8994->retune_mobile_texts;
 
 	ret = snd_soc_add_codec_controls(wm8994->hubs.codec, controls,
@@ -3298,7 +3306,7 @@ static void wm8994_handle_pdata(struct wm8994_priv *wm8994)
 		for (i = 0; i < pdata->num_drc_cfgs; i++)
 			wm8994->drc_texts[i] = pdata->drc_cfgs[i].name;
 
-		wm8994->drc_enum.max = pdata->num_drc_cfgs;
+		wm8994->drc_enum.items = pdata->num_drc_cfgs;
 		wm8994->drc_enum.texts = wm8994->drc_texts;
 
 		ret = snd_soc_add_codec_controls(wm8994->hubs.codec, controls,
diff --git a/sound/soc/codecs/wm8995.c b/sound/soc/codecs/wm8995.c
index 4300caff1783..ddb197dc1d53 100644
--- a/sound/soc/codecs/wm8995.c
+++ b/sound/soc/codecs/wm8995.c
@@ -423,24 +423,24 @@ static const char *in1l_text[] = {
 	"Differential", "Single-ended IN1LN", "Single-ended IN1LP"
 };
 
-static const SOC_ENUM_SINGLE_DECL(in1l_enum, WM8995_LEFT_LINE_INPUT_CONTROL,
-				  2, in1l_text);
+static SOC_ENUM_SINGLE_DECL(in1l_enum, WM8995_LEFT_LINE_INPUT_CONTROL,
+			    2, in1l_text);
 
 static const char *in1r_text[] = {
 	"Differential", "Single-ended IN1RN", "Single-ended IN1RP"
 };
 
-static const SOC_ENUM_SINGLE_DECL(in1r_enum, WM8995_LEFT_LINE_INPUT_CONTROL,
-				  0, in1r_text);
+static SOC_ENUM_SINGLE_DECL(in1r_enum, WM8995_LEFT_LINE_INPUT_CONTROL,
+			    0, in1r_text);
 
 static const char *dmic_src_text[] = {
 	"DMICDAT1", "DMICDAT2", "DMICDAT3"
 };
 
-static const SOC_ENUM_SINGLE_DECL(dmic_src1_enum, WM8995_POWER_MANAGEMENT_5,
-				  8, dmic_src_text);
-static const SOC_ENUM_SINGLE_DECL(dmic_src2_enum, WM8995_POWER_MANAGEMENT_5,
-				  6, dmic_src_text);
+static SOC_ENUM_SINGLE_DECL(dmic_src1_enum, WM8995_POWER_MANAGEMENT_5,
+			    8, dmic_src_text);
+static SOC_ENUM_SINGLE_DECL(dmic_src2_enum, WM8995_POWER_MANAGEMENT_5,
+			    6, dmic_src_text);
 
 static const struct snd_kcontrol_new wm8995_snd_controls[] = {
 	SOC_DOUBLE_R_TLV("DAC1 Volume", WM8995_DAC1_LEFT_VOLUME,
@@ -561,10 +561,8 @@ static int hp_supply_event(struct snd_soc_dapm_widget *w,
 			   struct snd_kcontrol *kcontrol, int event)
 {
 	struct snd_soc_codec *codec;
-	struct wm8995_priv *wm8995;
 
 	codec = w->codec;
-	wm8995 = snd_soc_codec_get_drvdata(codec);
 
 	switch (event) {
 	case SND_SOC_DAPM_PRE_PMU:
@@ -783,14 +781,12 @@ static const char *sidetone_text[] = {
 	"ADC/DMIC1", "DMIC2",
 };
 
-static const struct soc_enum sidetone1_enum =
-	SOC_ENUM_SINGLE(WM8995_SIDETONE, 0, 2, sidetone_text);
+static SOC_ENUM_SINGLE_DECL(sidetone1_enum, WM8995_SIDETONE, 0, sidetone_text);
 
 static const struct snd_kcontrol_new sidetone1_mux =
 	SOC_DAPM_ENUM("Left Sidetone Mux", sidetone1_enum);
 
-static const struct soc_enum sidetone2_enum =
-	SOC_ENUM_SINGLE(WM8995_SIDETONE, 1, 2, sidetone_text);
+static SOC_ENUM_SINGLE_DECL(sidetone2_enum, WM8995_SIDETONE, 1, sidetone_text);
 
 static const struct snd_kcontrol_new sidetone2_mux =
 	SOC_DAPM_ENUM("Right Sidetone Mux", sidetone2_enum);
@@ -886,8 +882,7 @@ static const char *adc_mux_text[] = {
 	"DMIC",
 };
 
-static const struct soc_enum adc_enum =
-	SOC_ENUM_SINGLE(0, 0, 2, adc_mux_text);
+static SOC_ENUM_SINGLE_VIRT_DECL(adc_enum, adc_mux_text);
 
 static const struct snd_kcontrol_new adcl_mux =
 	SOC_DAPM_ENUM_VIRT("ADCL Mux", adc_enum);
@@ -899,14 +894,14 @@ static const char *spk_src_text[] = {
 	"DAC1L", "DAC1R", "DAC2L", "DAC2R"
 };
 
-static const SOC_ENUM_SINGLE_DECL(spk1l_src_enum, WM8995_LEFT_PDM_SPEAKER_1,
-				  0, spk_src_text);
-static const SOC_ENUM_SINGLE_DECL(spk1r_src_enum, WM8995_RIGHT_PDM_SPEAKER_1,
-				  0, spk_src_text);
-static const SOC_ENUM_SINGLE_DECL(spk2l_src_enum, WM8995_LEFT_PDM_SPEAKER_2,
-				  0, spk_src_text);
-static const SOC_ENUM_SINGLE_DECL(spk2r_src_enum, WM8995_RIGHT_PDM_SPEAKER_2,
-				  0, spk_src_text);
+static SOC_ENUM_SINGLE_DECL(spk1l_src_enum, WM8995_LEFT_PDM_SPEAKER_1,
+			    0, spk_src_text);
+static SOC_ENUM_SINGLE_DECL(spk1r_src_enum, WM8995_RIGHT_PDM_SPEAKER_1,
+			    0, spk_src_text);
+static SOC_ENUM_SINGLE_DECL(spk2l_src_enum, WM8995_LEFT_PDM_SPEAKER_2,
+			    0, spk_src_text);
+static SOC_ENUM_SINGLE_DECL(spk2r_src_enum, WM8995_RIGHT_PDM_SPEAKER_2,
+			    0, spk_src_text);
 
 static const struct snd_kcontrol_new spk1l_mux =
 	SOC_DAPM_ENUM("SPK1L SRC", spk1l_src_enum);
diff --git a/sound/soc/codecs/wm8996.c b/sound/soc/codecs/wm8996.c
index 1a7655b0aa22..c8244af7d56a 100644
--- a/sound/soc/codecs/wm8996.c
+++ b/sound/soc/codecs/wm8996.c
@@ -311,28 +311,28 @@ static const char *sidetone_hpf_text[] = {
 	"2.9kHz", "1.5kHz", "735Hz", "403Hz", "196Hz", "98Hz", "49Hz"
 };
 
-static const struct soc_enum sidetone_hpf =
-	SOC_ENUM_SINGLE(WM8996_SIDETONE, 7, 7, sidetone_hpf_text);
+static SOC_ENUM_SINGLE_DECL(sidetone_hpf,
+			    WM8996_SIDETONE, 7, sidetone_hpf_text);
 
 static const char *hpf_mode_text[] = {
 	"HiFi", "Custom", "Voice"
 };
 
-static const struct soc_enum dsp1tx_hpf_mode =
-	SOC_ENUM_SINGLE(WM8996_DSP1_TX_FILTERS, 3, 3, hpf_mode_text);
+static SOC_ENUM_SINGLE_DECL(dsp1tx_hpf_mode,
+			    WM8996_DSP1_TX_FILTERS, 3, hpf_mode_text);
 
-static const struct soc_enum dsp2tx_hpf_mode =
-	SOC_ENUM_SINGLE(WM8996_DSP2_TX_FILTERS, 3, 3, hpf_mode_text);
+static SOC_ENUM_SINGLE_DECL(dsp2tx_hpf_mode,
+			    WM8996_DSP2_TX_FILTERS, 3, hpf_mode_text);
 
 static const char *hpf_cutoff_text[] = {
 	"50Hz", "75Hz", "100Hz", "150Hz", "200Hz", "300Hz", "400Hz"
 };
 
-static const struct soc_enum dsp1tx_hpf_cutoff =
-	SOC_ENUM_SINGLE(WM8996_DSP1_TX_FILTERS, 0, 7, hpf_cutoff_text);
+static SOC_ENUM_SINGLE_DECL(dsp1tx_hpf_cutoff,
+			    WM8996_DSP1_TX_FILTERS, 0, hpf_cutoff_text);
 
-static const struct soc_enum dsp2tx_hpf_cutoff =
-	SOC_ENUM_SINGLE(WM8996_DSP2_TX_FILTERS, 0, 7, hpf_cutoff_text);
+static SOC_ENUM_SINGLE_DECL(dsp2tx_hpf_cutoff,
+			    WM8996_DSP2_TX_FILTERS, 0, hpf_cutoff_text);
 
 static void wm8996_set_retune_mobile(struct snd_soc_codec *codec, int block)
 {
@@ -780,14 +780,14 @@ static const char *sidetone_text[] = {
 	"IN1", "IN2",
 };
 
-static const struct soc_enum left_sidetone_enum =
-	SOC_ENUM_SINGLE(WM8996_SIDETONE, 0, 2, sidetone_text);
+static SOC_ENUM_SINGLE_DECL(left_sidetone_enum,
+			    WM8996_SIDETONE, 0, sidetone_text);
 
 static const struct snd_kcontrol_new left_sidetone =
 	SOC_DAPM_ENUM("Left Sidetone", left_sidetone_enum);
 
-static const struct soc_enum right_sidetone_enum =
-	SOC_ENUM_SINGLE(WM8996_SIDETONE, 1, 2, sidetone_text);
+static SOC_ENUM_SINGLE_DECL(right_sidetone_enum,
+			    WM8996_SIDETONE, 1, sidetone_text);
 
 static const struct snd_kcontrol_new right_sidetone =
 	SOC_DAPM_ENUM("Right Sidetone", right_sidetone_enum);
@@ -796,14 +796,14 @@ static const char *spk_text[] = {
 	"DAC1L", "DAC1R", "DAC2L", "DAC2R"
 };
 
-static const struct soc_enum spkl_enum =
-	SOC_ENUM_SINGLE(WM8996_LEFT_PDM_SPEAKER, 0, 4, spk_text);
+static SOC_ENUM_SINGLE_DECL(spkl_enum,
+			    WM8996_LEFT_PDM_SPEAKER, 0, spk_text);
 
 static const struct snd_kcontrol_new spkl_mux =
 	SOC_DAPM_ENUM("SPKL", spkl_enum);
 
-static const struct soc_enum spkr_enum =
-	SOC_ENUM_SINGLE(WM8996_RIGHT_PDM_SPEAKER, 0, 4, spk_text);
+static SOC_ENUM_SINGLE_DECL(spkr_enum,
+			    WM8996_RIGHT_PDM_SPEAKER, 0, spk_text);
 
 static const struct snd_kcontrol_new spkr_mux =
 	SOC_DAPM_ENUM("SPKR", spkr_enum);
@@ -812,8 +812,8 @@ static const char *dsp1rx_text[] = {
 	"AIF1", "AIF2"
 };
 
-static const struct soc_enum dsp1rx_enum =
-	SOC_ENUM_SINGLE(WM8996_POWER_MANAGEMENT_8, 0, 2, dsp1rx_text);
+static SOC_ENUM_SINGLE_DECL(dsp1rx_enum,
+			    WM8996_POWER_MANAGEMENT_8, 0, dsp1rx_text);
 
 static const struct snd_kcontrol_new dsp1rx =
 	SOC_DAPM_ENUM("DSP1RX", dsp1rx_enum);
@@ -822,8 +822,8 @@ static const char *dsp2rx_text[] = {
 	 "AIF2", "AIF1"
 };
 
-static const struct soc_enum dsp2rx_enum =
-	SOC_ENUM_SINGLE(WM8996_POWER_MANAGEMENT_8, 4, 2, dsp2rx_text);
+static SOC_ENUM_SINGLE_DECL(dsp2rx_enum,
+			    WM8996_POWER_MANAGEMENT_8, 4, dsp2rx_text);
 
 static const struct snd_kcontrol_new dsp2rx =
 	SOC_DAPM_ENUM("DSP2RX", dsp2rx_enum);
@@ -832,8 +832,8 @@ static const char *aif2tx_text[] = {
 	"DSP2", "DSP1", "AIF1"
 };
 
-static const struct soc_enum aif2tx_enum =
-	SOC_ENUM_SINGLE(WM8996_POWER_MANAGEMENT_8, 6, 3, aif2tx_text);
+static SOC_ENUM_SINGLE_DECL(aif2tx_enum,
+			    WM8996_POWER_MANAGEMENT_8, 6, aif2tx_text);
 
 static const struct snd_kcontrol_new aif2tx =
 	SOC_DAPM_ENUM("AIF2TX", aif2tx_enum);
@@ -842,14 +842,14 @@ static const char *inmux_text[] = {
 	"ADC", "DMIC1", "DMIC2"
 };
 
-static const struct soc_enum in1_enum =
-	SOC_ENUM_SINGLE(WM8996_POWER_MANAGEMENT_7, 0, 3, inmux_text);
+static SOC_ENUM_SINGLE_DECL(in1_enum,
+			    WM8996_POWER_MANAGEMENT_7, 0, inmux_text);
 
 static const struct snd_kcontrol_new in1_mux =
 	SOC_DAPM_ENUM("IN1 Mux", in1_enum);
 
-static const struct soc_enum in2_enum =
-	SOC_ENUM_SINGLE(WM8996_POWER_MANAGEMENT_7, 4, 3, inmux_text);
+static SOC_ENUM_SINGLE_DECL(in2_enum,
+			    WM8996_POWER_MANAGEMENT_7, 4, inmux_text);
 
 static const struct snd_kcontrol_new in2_mux =
 	SOC_DAPM_ENUM("IN2 Mux", in2_enum);
@@ -1608,8 +1608,8 @@ static int wm8996_set_bias_level(struct snd_soc_codec *codec,
 				msleep(5);
 			}
 
-			regcache_cache_only(codec->control_data, false);
-			regcache_sync(codec->control_data);
+			regcache_cache_only(wm8996->regmap, false);
+			regcache_sync(wm8996->regmap);
 		}
 
 		/* Bypass the MICBIASes for lowest power */
@@ -1620,10 +1620,10 @@ static int wm8996_set_bias_level(struct snd_soc_codec *codec,
 		break;
 
 	case SND_SOC_BIAS_OFF:
-		regcache_cache_only(codec->control_data, true);
+		regcache_cache_only(wm8996->regmap, true);
 		if (wm8996->pdata.ldo_ena >= 0) {
 			gpio_set_value_cansleep(wm8996->pdata.ldo_ena, 0);
-			regcache_cache_only(codec->control_data, true);
+			regcache_cache_only(wm8996->regmap, true);
 		}
 		regulator_bulk_disable(ARRAY_SIZE(wm8996->supplies),
 				       wm8996->supplies);
@@ -2251,6 +2251,7 @@ int wm8996_detect(struct snd_soc_codec *codec, struct snd_soc_jack *jack,
 		  wm8996_polarity_fn polarity_cb)
 {
 	struct wm8996_priv *wm8996 = snd_soc_codec_get_drvdata(codec);
+	struct snd_soc_dapm_context *dapm = &codec->dapm;
 
 	wm8996->jack = jack;
 	wm8996->detecting = true;
@@ -2267,8 +2268,12 @@ int wm8996_detect(struct snd_soc_codec *codec, struct snd_soc_jack *jack,
 			    WM8996_MICB2_DISCH, 0);
 
 	/* LDO2 powers the microphones, SYSCLK clocks detection */
-	snd_soc_dapm_force_enable_pin(&codec->dapm, "LDO2");
-	snd_soc_dapm_force_enable_pin(&codec->dapm, "SYSCLK");
+	snd_soc_dapm_mutex_lock(dapm);
+
+	snd_soc_dapm_force_enable_pin_unlocked(dapm, "LDO2");
+	snd_soc_dapm_force_enable_pin_unlocked(dapm, "SYSCLK");
+
+	snd_soc_dapm_mutex_unlock(dapm);
 
 	/* We start off just enabling microphone detection - even a
 	 * plain headphone will trigger detection.
@@ -2595,7 +2600,7 @@ static void wm8996_retune_mobile_pdata(struct snd_soc_codec *codec)
 	dev_dbg(codec->dev, "Allocated %d unique ReTune Mobile names\n",
 		wm8996->num_retune_mobile_texts);
 
-	wm8996->retune_mobile_enum.max = wm8996->num_retune_mobile_texts;
+	wm8996->retune_mobile_enum.items = wm8996->num_retune_mobile_texts;
 	wm8996->retune_mobile_enum.texts = wm8996->retune_mobile_texts;
 
 	ret = snd_soc_add_codec_controls(codec, controls, ARRAY_SIZE(controls));
diff --git a/sound/soc/codecs/wm8997.c b/sound/soc/codecs/wm8997.c
index 555115ee2159..e10f44d7fdb7 100644
--- a/sound/soc/codecs/wm8997.c
+++ b/sound/soc/codecs/wm8997.c
@@ -86,7 +86,7 @@ static int wm8997_sysclk_ev(struct snd_soc_dapm_widget *w,
 {
 	struct snd_soc_codec *codec = w->codec;
 	struct arizona *arizona = dev_get_drvdata(codec->dev->parent);
-	struct regmap *regmap = codec->control_data;
+	struct regmap *regmap = arizona->regmap;
 	const struct reg_default *patch = NULL;
 	int i, patch_size;
 
@@ -123,10 +123,12 @@ static const unsigned int wm8997_osr_val[] = {
 
 static const struct soc_enum wm8997_hpout_osr[] = {
 	SOC_VALUE_ENUM_SINGLE(ARIZONA_OUTPUT_PATH_CONFIG_1L,
-			      ARIZONA_OUT1_OSR_SHIFT, 0x7, 3,
+			      ARIZONA_OUT1_OSR_SHIFT, 0x7,
+			      ARRAY_SIZE(wm8997_osr_text),
 			      wm8997_osr_text, wm8997_osr_val),
 	SOC_VALUE_ENUM_SINGLE(ARIZONA_OUTPUT_PATH_CONFIG_3L,
-			      ARIZONA_OUT3_OSR_SHIFT, 0x7, 3,
+			      ARIZONA_OUT3_OSR_SHIFT, 0x7,
+			      ARRAY_SIZE(wm8997_osr_text),
 			      wm8997_osr_text, wm8997_osr_val),
 };
 
@@ -170,15 +172,8 @@ 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),
 
-SND_SOC_BYTES_MASK("EQ1 Coefficients", ARIZONA_EQ1_1, 21,
-		   ARIZONA_EQ1_ENA_MASK),
-SND_SOC_BYTES_MASK("EQ2 Coefficients", ARIZONA_EQ2_1, 21,
-		   ARIZONA_EQ2_ENA_MASK),
-SND_SOC_BYTES_MASK("EQ3 Coefficients", ARIZONA_EQ3_1, 21,
-		   ARIZONA_EQ3_ENA_MASK),
-SND_SOC_BYTES_MASK("EQ4 Coefficients", ARIZONA_EQ4_1, 21,
-		   ARIZONA_EQ4_ENA_MASK),
-
+SND_SOC_BYTES("EQ1 Coefficients", ARIZONA_EQ1_3, 19),
+SOC_SINGLE("EQ1 Mode Switch", ARIZONA_EQ1_2, ARIZONA_EQ1_B1_MODE, 1, 0),
 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,
@@ -190,6 +185,8 @@ SOC_SINGLE_TLV("EQ1 B4 Volume", ARIZONA_EQ1_2, ARIZONA_EQ1_B4_GAIN_SHIFT,
 SOC_SINGLE_TLV("EQ1 B5 Volume", ARIZONA_EQ1_2, ARIZONA_EQ1_B5_GAIN_SHIFT,
 	       24, 0, eq_tlv),
 
+SND_SOC_BYTES("EQ2 Coefficients", ARIZONA_EQ2_3, 19),
+SOC_SINGLE("EQ2 Mode Switch", ARIZONA_EQ2_2, ARIZONA_EQ2_B1_MODE, 1, 0),
 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,
@@ -201,6 +198,8 @@ SOC_SINGLE_TLV("EQ2 B4 Volume", ARIZONA_EQ2_2, ARIZONA_EQ2_B4_GAIN_SHIFT,
 SOC_SINGLE_TLV("EQ2 B5 Volume", ARIZONA_EQ2_2, ARIZONA_EQ2_B5_GAIN_SHIFT,
 	       24, 0, eq_tlv),
 
+SND_SOC_BYTES("EQ3 Coefficients", ARIZONA_EQ3_3, 19),
+SOC_SINGLE("EQ3 Mode Switch", ARIZONA_EQ3_2, ARIZONA_EQ3_B1_MODE, 1, 0),
 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,
@@ -212,6 +211,8 @@ SOC_SINGLE_TLV("EQ3 B4 Volume", ARIZONA_EQ3_2, ARIZONA_EQ3_B4_GAIN_SHIFT,
 SOC_SINGLE_TLV("EQ3 B5 Volume", ARIZONA_EQ3_2, ARIZONA_EQ3_B5_GAIN_SHIFT,
 	       24, 0, eq_tlv),
 
+SND_SOC_BYTES("EQ4 Coefficients", ARIZONA_EQ4_3, 19),
+SOC_SINGLE("EQ4 Mode Switch", ARIZONA_EQ4_2, ARIZONA_EQ4_B1_MODE, 1, 0),
 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,
diff --git a/sound/soc/codecs/wm9081.c b/sound/soc/codecs/wm9081.c
index 0982c1d38ec4..721cee71d5fc 100644
--- a/sound/soc/codecs/wm9081.c
+++ b/sound/soc/codecs/wm9081.c
@@ -268,8 +268,7 @@ static const char *drc_high_text[] = {
 	"0",
 };
 
-static const struct soc_enum drc_high =
-	SOC_ENUM_SINGLE(WM9081_DRC_3, 3, 6, drc_high_text);
+static SOC_ENUM_SINGLE_DECL(drc_high, WM9081_DRC_3, 3, drc_high_text);
 
 static const char *drc_low_text[] = {
 	"1",
@@ -279,8 +278,7 @@ static const char *drc_low_text[] = {
 	"0",
 };
 
-static const struct soc_enum drc_low =
-	SOC_ENUM_SINGLE(WM9081_DRC_3, 0, 5, drc_low_text);
+static SOC_ENUM_SINGLE_DECL(drc_low, WM9081_DRC_3, 0, drc_low_text);
 
 static const char *drc_atk_text[] = {
 	"181us",
@@ -297,8 +295,7 @@ static const char *drc_atk_text[] = {
 	"185.6ms",
 };
 
-static const struct soc_enum drc_atk =
-	SOC_ENUM_SINGLE(WM9081_DRC_2, 12, 12, drc_atk_text);
+static SOC_ENUM_SINGLE_DECL(drc_atk, WM9081_DRC_2, 12, drc_atk_text);
 
 static const char *drc_dcy_text[] = {
 	"186ms",
@@ -312,8 +309,7 @@ static const char *drc_dcy_text[] = {
 	"47.56s",
 };
 
-static const struct soc_enum drc_dcy =
-	SOC_ENUM_SINGLE(WM9081_DRC_2, 8, 9, drc_dcy_text);
+static SOC_ENUM_SINGLE_DECL(drc_dcy, WM9081_DRC_2, 8, drc_dcy_text);
 
 static const char *drc_qr_dcy_text[] = {
 	"0.725ms",
@@ -321,8 +317,7 @@ static const char *drc_qr_dcy_text[] = {
 	"5.8ms",
 };
 
-static const struct soc_enum drc_qr_dcy =
-	SOC_ENUM_SINGLE(WM9081_DRC_2, 4, 3, drc_qr_dcy_text);
+static SOC_ENUM_SINGLE_DECL(drc_qr_dcy, WM9081_DRC_2, 4, drc_qr_dcy_text);
 
 static const char *dac_deemph_text[] = {
 	"None",
@@ -331,16 +326,16 @@ static const char *dac_deemph_text[] = {
 	"48kHz",
 };
 
-static const struct soc_enum dac_deemph =
-	SOC_ENUM_SINGLE(WM9081_DAC_DIGITAL_2, 1, 4, dac_deemph_text);
+static SOC_ENUM_SINGLE_DECL(dac_deemph, WM9081_DAC_DIGITAL_2, 1,
+			    dac_deemph_text);
 
 static const char *speaker_mode_text[] = {
 	"Class D",
 	"Class AB",
 };
 
-static const struct soc_enum speaker_mode =
-	SOC_ENUM_SINGLE(WM9081_ANALOGUE_SPEAKER_2, 6, 2, speaker_mode_text);
+static SOC_ENUM_SINGLE_DECL(speaker_mode, WM9081_ANALOGUE_SPEAKER_2, 6,
+			    speaker_mode_text);
 
 static int speaker_mode_get(struct snd_kcontrol *kcontrol,
 			    struct snd_ctl_elem_value *ucontrol)
diff --git a/sound/soc/codecs/wm9705.c b/sound/soc/codecs/wm9705.c
index 70ce6793c5bd..c0b7f45dfa37 100644
--- a/sound/soc/codecs/wm9705.c
+++ b/sound/soc/codecs/wm9705.c
@@ -67,12 +67,12 @@ static const char *wm9705_mic[] = {"Mic 1", "Mic 2"};
 static const char *wm9705_rec_sel[] = {"Mic", "CD", "NC", "NC",
 	"Line", "Stereo Mix", "Mono Mix", "Phone"};
 
-static const struct soc_enum wm9705_enum_mic =
-	SOC_ENUM_SINGLE(AC97_GENERAL_PURPOSE, 8, 2, wm9705_mic);
-static const struct soc_enum wm9705_enum_rec_l =
-	SOC_ENUM_SINGLE(AC97_REC_SEL, 8, 8, wm9705_rec_sel);
-static const struct soc_enum wm9705_enum_rec_r =
-	SOC_ENUM_SINGLE(AC97_REC_SEL, 0, 8, wm9705_rec_sel);
+static SOC_ENUM_SINGLE_DECL(wm9705_enum_mic,
+			    AC97_GENERAL_PURPOSE, 8, wm9705_mic);
+static SOC_ENUM_SINGLE_DECL(wm9705_enum_rec_l,
+			    AC97_REC_SEL, 8, wm9705_rec_sel);
+static SOC_ENUM_SINGLE_DECL(wm9705_enum_rec_r,
+			    AC97_REC_SEL, 0, wm9705_rec_sel);
 
 /* Headphone Mixer */
 static const struct snd_kcontrol_new wm9705_hp_mixer_controls[] = {
diff --git a/sound/soc/codecs/wm_adsp.c b/sound/soc/codecs/wm_adsp.c
index 444626fcab40..bb5f7b4e3ebb 100644
--- a/sound/soc/codecs/wm_adsp.c
+++ b/sound/soc/codecs/wm_adsp.c
@@ -684,24 +684,38 @@ static int wm_adsp_load(struct wm_adsp *dsp)
 		}
 
 		if (reg) {
-			buf = wm_adsp_buf_alloc(region->data,
-						le32_to_cpu(region->len),
-						&buf_list);
-			if (!buf) {
-				adsp_err(dsp, "Out of memory\n");
-				ret = -ENOMEM;
-				goto out_fw;
-			}
+			size_t to_write = PAGE_SIZE;
+			size_t remain = le32_to_cpu(region->len);
+			const u8 *data = region->data;
+
+			while (remain > 0) {
+				if (remain < PAGE_SIZE)
+					to_write = remain;
+
+				buf = wm_adsp_buf_alloc(data,
+							to_write,
+							&buf_list);
+				if (!buf) {
+					adsp_err(dsp, "Out of memory\n");
+					ret = -ENOMEM;
+					goto out_fw;
+				}
 
-			ret = regmap_raw_write_async(regmap, reg, buf->buf,
-						     le32_to_cpu(region->len));
-			if (ret != 0) {
-				adsp_err(dsp,
-					"%s.%d: Failed to write %d bytes at %d in %s: %d\n",
-					file, regions,
-					le32_to_cpu(region->len), offset,
-					region_name, ret);
-				goto out_fw;
+				ret = regmap_raw_write_async(regmap, reg,
+							     buf->buf,
+							     to_write);
+				if (ret != 0) {
+					adsp_err(dsp,
+						"%s.%d: Failed to write %zd bytes at %d in %s: %d\n",
+						file, regions,
+						to_write, offset,
+						region_name, ret);
+					goto out_fw;
+				}
+
+				data += to_write;
+				reg += to_write / 2;
+				remain -= to_write;
 			}
 		}
 
@@ -1679,6 +1693,8 @@ int wm_adsp2_event(struct snd_soc_dapm_widget *w,
 			list_del(&alg_region->list);
 			kfree(alg_region);
 		}
+
+		adsp_dbg(dsp, "Shutdown complete\n");
 		break;
 
 	default:
diff --git a/sound/soc/codecs/wm_hubs.c b/sound/soc/codecs/wm_hubs.c
index b371066dd5bc..b6209662ab13 100644
--- a/sound/soc/codecs/wm_hubs.c
+++ b/sound/soc/codecs/wm_hubs.c
@@ -50,16 +50,16 @@ static const char *speaker_ref_text[] = {
 	"VMID",
 };
 
-static const struct soc_enum speaker_ref =
-	SOC_ENUM_SINGLE(WM8993_SPEAKER_MIXER, 8, 2, speaker_ref_text);
+static SOC_ENUM_SINGLE_DECL(speaker_ref,
+			    WM8993_SPEAKER_MIXER, 8, speaker_ref_text);
 
 static const char *speaker_mode_text[] = {
 	"Class D",
 	"Class AB",
 };
 
-static const struct soc_enum speaker_mode =
-	SOC_ENUM_SINGLE(WM8993_SPKMIXR_ATTENUATION, 8, 2, speaker_mode_text);
+static SOC_ENUM_SINGLE_DECL(speaker_mode,
+			    WM8993_SPKMIXR_ATTENUATION, 8, speaker_mode_text);
 
 static void wait_for_dc_servo(struct snd_soc_codec *codec, unsigned int op)
 {
@@ -735,15 +735,15 @@ static const char *hp_mux_text[] = {
 	"DAC",
 };
 
-static const struct soc_enum hpl_enum =
-	SOC_ENUM_SINGLE(WM8993_OUTPUT_MIXER1, 8, 2, hp_mux_text);
+static SOC_ENUM_SINGLE_DECL(hpl_enum,
+			    WM8993_OUTPUT_MIXER1, 8, hp_mux_text);
 
 const struct snd_kcontrol_new wm_hubs_hpl_mux =
 	WM_HUBS_ENUM_W("Left Headphone Mux", hpl_enum);
 EXPORT_SYMBOL_GPL(wm_hubs_hpl_mux);
 
-static const struct soc_enum hpr_enum =
-	SOC_ENUM_SINGLE(WM8993_OUTPUT_MIXER2, 8, 2, hp_mux_text);
+static SOC_ENUM_SINGLE_DECL(hpr_enum,
+			    WM8993_OUTPUT_MIXER2, 8, hp_mux_text);
 
 const struct snd_kcontrol_new wm_hubs_hpr_mux =
 	WM_HUBS_ENUM_W("Right Headphone Mux", hpr_enum);
diff --git a/sound/soc/davinci/davinci-evm.c b/sound/soc/davinci/davinci-evm.c
index 5e3bc3c6801a..621e9a997d4c 100644
--- a/sound/soc/davinci/davinci-evm.c
+++ b/sound/soc/davinci/davinci-evm.c
@@ -17,6 +17,7 @@
 #include <linux/platform_data/edma.h>
 #include <linux/i2c.h>
 #include <linux/of_platform.h>
+#include <linux/clk.h>
 #include <sound/core.h>
 #include <sound/pcm.h>
 #include <sound/soc.h>
@@ -30,9 +31,34 @@
 #include "davinci-i2s.h"
 
 struct snd_soc_card_drvdata_davinci {
+	struct clk *mclk;
 	unsigned sysclk;
 };
 
+static int evm_startup(struct snd_pcm_substream *substream)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct snd_soc_card *soc_card = rtd->codec->card;
+	struct snd_soc_card_drvdata_davinci *drvdata =
+		snd_soc_card_get_drvdata(soc_card);
+
+	if (drvdata->mclk)
+		return clk_prepare_enable(drvdata->mclk);
+
+	return 0;
+}
+
+static void evm_shutdown(struct snd_pcm_substream *substream)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct snd_soc_card *soc_card = rtd->codec->card;
+	struct snd_soc_card_drvdata_davinci *drvdata =
+		snd_soc_card_get_drvdata(soc_card);
+
+	if (drvdata->mclk)
+		clk_disable_unprepare(drvdata->mclk);
+}
+
 static int evm_hw_params(struct snd_pcm_substream *substream,
 			 struct snd_pcm_hw_params *params)
 {
@@ -59,6 +85,8 @@ static int evm_hw_params(struct snd_pcm_substream *substream,
 }
 
 static struct snd_soc_ops evm_ops = {
+	.startup = evm_startup,
+	.shutdown = evm_shutdown,
 	.hw_params = evm_hw_params,
 };
 
@@ -348,6 +376,7 @@ static int davinci_evm_probe(struct platform_device *pdev)
 		of_match_device(of_match_ptr(davinci_evm_dt_ids), &pdev->dev);
 	struct snd_soc_dai_link *dai = (struct snd_soc_dai_link *) match->data;
 	struct snd_soc_card_drvdata_davinci *drvdata = NULL;
+	struct clk *mclk;
 	int ret = 0;
 
 	evm_soc_card.dai_link = dai;
@@ -367,13 +396,38 @@ static int davinci_evm_probe(struct platform_device *pdev)
 	if (ret)
 		return ret;
 
+	mclk = devm_clk_get(&pdev->dev, "mclk");
+	if (PTR_ERR(mclk) == -EPROBE_DEFER) {
+		return -EPROBE_DEFER;
+	} else if (IS_ERR(mclk)) {
+		dev_dbg(&pdev->dev, "mclk not found.\n");
+		mclk = NULL;
+	}
+
 	drvdata = devm_kzalloc(&pdev->dev, sizeof(*drvdata), GFP_KERNEL);
 	if (!drvdata)
 		return -ENOMEM;
 
+	drvdata->mclk = mclk;
+
 	ret = of_property_read_u32(np, "ti,codec-clock-rate", &drvdata->sysclk);
-	if (ret < 0)
-		return -EINVAL;
+
+	if (ret < 0) {
+		if (!drvdata->mclk) {
+			dev_err(&pdev->dev,
+				"No clock or clock rate defined.\n");
+			return -EINVAL;
+		}
+		drvdata->sysclk = clk_get_rate(drvdata->mclk);
+	} else if (drvdata->mclk) {
+		unsigned int requestd_rate = drvdata->sysclk;
+		clk_set_rate(drvdata->mclk, drvdata->sysclk);
+		drvdata->sysclk = clk_get_rate(drvdata->mclk);
+		if (drvdata->sysclk != requestd_rate)
+			dev_warn(&pdev->dev,
+				 "Could not get requested rate %u using %u.\n",
+				 requestd_rate, drvdata->sysclk);
+	}
 
 	snd_soc_card_set_drvdata(&evm_soc_card, drvdata);
 	ret = devm_snd_soc_register_card(&pdev->dev, &evm_soc_card);
diff --git a/sound/soc/davinci/davinci-mcasp.c b/sound/soc/davinci/davinci-mcasp.c
index 670afa29e30d..b0ae0677f023 100644
--- a/sound/soc/davinci/davinci-mcasp.c
+++ b/sound/soc/davinci/davinci-mcasp.c
@@ -37,6 +37,16 @@
 #include "davinci-pcm.h"
 #include "davinci-mcasp.h"
 
+struct davinci_mcasp_context {
+	u32	txfmtctl;
+	u32	rxfmtctl;
+	u32	txfmt;
+	u32	rxfmt;
+	u32	aclkxctl;
+	u32	aclkrctl;
+	u32	pdir;
+};
+
 struct davinci_mcasp {
 	struct davinci_pcm_dma_params dma_params[2];
 	struct snd_dmaengine_dai_dma_data dma_data[2];
@@ -53,6 +63,9 @@ struct davinci_mcasp {
 	u16	bclk_lrclk_ratio;
 	int	streams;
 
+	int	sysclk_freq;
+	bool	bclk_master;
+
 	/* McASP FIFO related */
 	u8	txnumevt;
 	u8	rxnumevt;
@@ -60,15 +73,7 @@ struct davinci_mcasp {
 	bool	dat_port;
 
 #ifdef CONFIG_PM_SLEEP
-	struct {
-		u32	txfmtctl;
-		u32	rxfmtctl;
-		u32	txfmt;
-		u32	rxfmt;
-		u32	aclkxctl;
-		u32	aclkrctl;
-		u32	pdir;
-	} context;
+	struct davinci_mcasp_context context;
 #endif
 };
 
@@ -294,6 +299,7 @@ static int davinci_mcasp_set_dai_fmt(struct snd_soc_dai *cpu_dai,
 
 		mcasp_set_bits(mcasp, DAVINCI_MCASP_PDIR_REG, ACLKX | ACLKR);
 		mcasp_set_bits(mcasp, DAVINCI_MCASP_PDIR_REG, AFSX | AFSR);
+		mcasp->bclk_master = 1;
 		break;
 	case SND_SOC_DAIFMT_CBM_CFS:
 		/* codec is clock master and frame slave */
@@ -305,6 +311,7 @@ static int davinci_mcasp_set_dai_fmt(struct snd_soc_dai *cpu_dai,
 
 		mcasp_clr_bits(mcasp, DAVINCI_MCASP_PDIR_REG, ACLKX | ACLKR);
 		mcasp_set_bits(mcasp, DAVINCI_MCASP_PDIR_REG, AFSX | AFSR);
+		mcasp->bclk_master = 0;
 		break;
 	case SND_SOC_DAIFMT_CBM_CFM:
 		/* codec is clock and frame master */
@@ -316,6 +323,7 @@ static int davinci_mcasp_set_dai_fmt(struct snd_soc_dai *cpu_dai,
 
 		mcasp_clr_bits(mcasp, DAVINCI_MCASP_PDIR_REG,
 			       ACLKX | AHCLKX | AFSX | ACLKR | AHCLKR | AFSR);
+		mcasp->bclk_master = 0;
 		break;
 
 	default:
@@ -410,6 +418,8 @@ static int davinci_mcasp_set_sysclk(struct snd_soc_dai *dai, int clk_id,
 		mcasp_clr_bits(mcasp, DAVINCI_MCASP_PDIR_REG, AHCLKX);
 	}
 
+	mcasp->sysclk_freq = freq;
+
 	return 0;
 }
 
@@ -603,20 +613,23 @@ static int davinci_mcasp_hw_params(struct snd_pcm_substream *substream,
 	u8 fifo_level;
 	u8 slots = mcasp->tdm_slots;
 	u8 active_serializers;
-	int channels;
+	int channels = params_channels(params);
 	int ret;
-	struct snd_interval *pcm_channels = hw_param_interval(params,
-					SNDRV_PCM_HW_PARAM_CHANNELS);
-	channels = pcm_channels->min;
 
-	active_serializers = (channels + slots - 1) / slots;
+	/* If mcasp is BCLK master we need to set BCLK divider */
+	if (mcasp->bclk_master) {
+		unsigned int bclk_freq = snd_soc_params_to_bclk(params);
+		if (mcasp->sysclk_freq % bclk_freq != 0) {
+			dev_err(mcasp->dev, "Can't produce requred BCLK\n");
+			return -EINVAL;
+		}
+		davinci_mcasp_set_clkdiv(
+			cpu_dai, 1, mcasp->sysclk_freq / bclk_freq);
+	}
 
-	if (mcasp_common_hw_param(mcasp, substream->stream, channels) == -EINVAL)
-		return -EINVAL;
-	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
-		fifo_level = mcasp->txnumevt * active_serializers;
-	else
-		fifo_level = mcasp->rxnumevt * active_serializers;
+	ret = mcasp_common_hw_param(mcasp, substream->stream, channels);
+	if (ret)
+		return ret;
 
 	if (mcasp->op_mode == DAVINCI_MCASP_DIT_MODE)
 		ret = mcasp_dit_hw_param(mcasp);
@@ -658,6 +671,13 @@ static int davinci_mcasp_hw_params(struct snd_pcm_substream *substream,
 		return -EINVAL;
 	}
 
+	/* Calculate FIFO level */
+	active_serializers = (channels + slots - 1) / slots;
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+		fifo_level = mcasp->txnumevt * active_serializers;
+	else
+		fifo_level = mcasp->rxnumevt * active_serializers;
+
 	if (mcasp->version == MCASP_VERSION_2 && !fifo_level)
 		dma_params->acnt = 4;
 	else
@@ -719,6 +739,43 @@ static const struct snd_soc_dai_ops davinci_mcasp_dai_ops = {
 	.set_sysclk	= davinci_mcasp_set_sysclk,
 };
 
+#ifdef CONFIG_PM_SLEEP
+static int davinci_mcasp_suspend(struct snd_soc_dai *dai)
+{
+	struct davinci_mcasp *mcasp = snd_soc_dai_get_drvdata(dai);
+	struct davinci_mcasp_context *context = &mcasp->context;
+
+	context->txfmtctl = mcasp_get_reg(mcasp, DAVINCI_MCASP_TXFMCTL_REG);
+	context->rxfmtctl = mcasp_get_reg(mcasp, DAVINCI_MCASP_RXFMCTL_REG);
+	context->txfmt = mcasp_get_reg(mcasp, DAVINCI_MCASP_TXFMT_REG);
+	context->rxfmt = mcasp_get_reg(mcasp, DAVINCI_MCASP_RXFMT_REG);
+	context->aclkxctl = mcasp_get_reg(mcasp, DAVINCI_MCASP_ACLKXCTL_REG);
+	context->aclkrctl = mcasp_get_reg(mcasp, DAVINCI_MCASP_ACLKRCTL_REG);
+	context->pdir = mcasp_get_reg(mcasp, DAVINCI_MCASP_PDIR_REG);
+
+	return 0;
+}
+
+static int davinci_mcasp_resume(struct snd_soc_dai *dai)
+{
+	struct davinci_mcasp *mcasp = snd_soc_dai_get_drvdata(dai);
+	struct davinci_mcasp_context *context = &mcasp->context;
+
+	mcasp_set_reg(mcasp, DAVINCI_MCASP_TXFMCTL_REG, context->txfmtctl);
+	mcasp_set_reg(mcasp, DAVINCI_MCASP_RXFMCTL_REG, context->rxfmtctl);
+	mcasp_set_reg(mcasp, DAVINCI_MCASP_TXFMT_REG, context->txfmt);
+	mcasp_set_reg(mcasp, DAVINCI_MCASP_RXFMT_REG, context->rxfmt);
+	mcasp_set_reg(mcasp, DAVINCI_MCASP_ACLKXCTL_REG, context->aclkxctl);
+	mcasp_set_reg(mcasp, DAVINCI_MCASP_ACLKRCTL_REG, context->aclkrctl);
+	mcasp_set_reg(mcasp, DAVINCI_MCASP_PDIR_REG, context->pdir);
+
+	return 0;
+}
+#else
+#define davinci_mcasp_suspend NULL
+#define davinci_mcasp_resume NULL
+#endif
+
 #define DAVINCI_MCASP_RATES	SNDRV_PCM_RATE_8000_192000
 
 #define DAVINCI_MCASP_PCM_FMTS (SNDRV_PCM_FMTBIT_S8 | \
@@ -735,6 +792,8 @@ static const struct snd_soc_dai_ops davinci_mcasp_dai_ops = {
 static struct snd_soc_dai_driver davinci_mcasp_dai[] = {
 	{
 		.name		= "davinci-mcasp.0",
+		.suspend	= davinci_mcasp_suspend,
+		.resume		= davinci_mcasp_resume,
 		.playback	= {
 			.channels_min	= 2,
 			.channels_max	= 32 * 16,
@@ -768,28 +827,28 @@ static const struct snd_soc_component_driver davinci_mcasp_component = {
 };
 
 /* Some HW specific values and defaults. The rest is filled in from DT. */
-static struct snd_platform_data dm646x_mcasp_pdata = {
+static struct davinci_mcasp_pdata dm646x_mcasp_pdata = {
 	.tx_dma_offset = 0x400,
 	.rx_dma_offset = 0x400,
 	.asp_chan_q = EVENTQ_0,
 	.version = MCASP_VERSION_1,
 };
 
-static struct snd_platform_data da830_mcasp_pdata = {
+static struct davinci_mcasp_pdata da830_mcasp_pdata = {
 	.tx_dma_offset = 0x2000,
 	.rx_dma_offset = 0x2000,
 	.asp_chan_q = EVENTQ_0,
 	.version = MCASP_VERSION_2,
 };
 
-static struct snd_platform_data am33xx_mcasp_pdata = {
+static struct davinci_mcasp_pdata am33xx_mcasp_pdata = {
 	.tx_dma_offset = 0,
 	.rx_dma_offset = 0,
 	.asp_chan_q = EVENTQ_0,
 	.version = MCASP_VERSION_3,
 };
 
-static struct snd_platform_data dra7_mcasp_pdata = {
+static struct davinci_mcasp_pdata dra7_mcasp_pdata = {
 	.tx_dma_offset = 0x200,
 	.rx_dma_offset = 0x284,
 	.asp_chan_q = EVENTQ_0,
@@ -857,11 +916,11 @@ err1:
 	return ret;
 }
 
-static struct snd_platform_data *davinci_mcasp_set_pdata_from_of(
+static struct davinci_mcasp_pdata *davinci_mcasp_set_pdata_from_of(
 						struct platform_device *pdev)
 {
 	struct device_node *np = pdev->dev.of_node;
-	struct snd_platform_data *pdata = NULL;
+	struct davinci_mcasp_pdata *pdata = NULL;
 	const struct of_device_id *match =
 			of_match_device(mcasp_dt_ids, &pdev->dev);
 	struct of_phandle_args dma_spec;
@@ -874,7 +933,7 @@ static struct snd_platform_data *davinci_mcasp_set_pdata_from_of(
 		pdata = pdev->dev.platform_data;
 		return pdata;
 	} else if (match) {
-		pdata = (struct snd_platform_data *) match->data;
+		pdata = (struct davinci_mcasp_pdata*) match->data;
 	} else {
 		/* control shouldn't reach here. something is wrong */
 		ret = -EINVAL;
@@ -966,9 +1025,9 @@ nodata:
 
 static int davinci_mcasp_probe(struct platform_device *pdev)
 {
-	struct davinci_pcm_dma_params *dma_data;
+	struct davinci_pcm_dma_params *dma_params;
 	struct resource *mem, *ioarea, *res, *dat;
-	struct snd_platform_data *pdata;
+	struct davinci_mcasp_pdata *pdata;
 	struct davinci_mcasp *mcasp;
 	int ret;
 
@@ -1035,41 +1094,41 @@ static int davinci_mcasp_probe(struct platform_device *pdev)
 	if (dat)
 		mcasp->dat_port = true;
 
-	dma_data = &mcasp->dma_params[SNDRV_PCM_STREAM_PLAYBACK];
-	dma_data->asp_chan_q = pdata->asp_chan_q;
-	dma_data->ram_chan_q = pdata->ram_chan_q;
-	dma_data->sram_pool = pdata->sram_pool;
-	dma_data->sram_size = pdata->sram_size_playback;
+	dma_params = &mcasp->dma_params[SNDRV_PCM_STREAM_PLAYBACK];
+	dma_params->asp_chan_q = pdata->asp_chan_q;
+	dma_params->ram_chan_q = pdata->ram_chan_q;
+	dma_params->sram_pool = pdata->sram_pool;
+	dma_params->sram_size = pdata->sram_size_playback;
 	if (dat)
-		dma_data->dma_addr = dat->start;
+		dma_params->dma_addr = dat->start;
 	else
-		dma_data->dma_addr = mem->start + pdata->tx_dma_offset;
+		dma_params->dma_addr = mem->start + pdata->tx_dma_offset;
 
 	/* Unconditional dmaengine stuff */
-	mcasp->dma_data[SNDRV_PCM_STREAM_PLAYBACK].addr = dma_data->dma_addr;
+	mcasp->dma_data[SNDRV_PCM_STREAM_PLAYBACK].addr = dma_params->dma_addr;
 
 	res = platform_get_resource(pdev, IORESOURCE_DMA, 0);
 	if (res)
-		dma_data->channel = res->start;
+		dma_params->channel = res->start;
 	else
-		dma_data->channel = pdata->tx_dma_channel;
+		dma_params->channel = pdata->tx_dma_channel;
 
-	dma_data = &mcasp->dma_params[SNDRV_PCM_STREAM_CAPTURE];
-	dma_data->asp_chan_q = pdata->asp_chan_q;
-	dma_data->ram_chan_q = pdata->ram_chan_q;
-	dma_data->sram_pool = pdata->sram_pool;
-	dma_data->sram_size = pdata->sram_size_capture;
+	dma_params = &mcasp->dma_params[SNDRV_PCM_STREAM_CAPTURE];
+	dma_params->asp_chan_q = pdata->asp_chan_q;
+	dma_params->ram_chan_q = pdata->ram_chan_q;
+	dma_params->sram_pool = pdata->sram_pool;
+	dma_params->sram_size = pdata->sram_size_capture;
 	if (dat)
-		dma_data->dma_addr = dat->start;
+		dma_params->dma_addr = dat->start;
 	else
-		dma_data->dma_addr = mem->start + pdata->rx_dma_offset;
+		dma_params->dma_addr = mem->start + pdata->rx_dma_offset;
 
 	/* Unconditional dmaengine stuff */
-	mcasp->dma_data[SNDRV_PCM_STREAM_CAPTURE].addr = dma_data->dma_addr;
+	mcasp->dma_data[SNDRV_PCM_STREAM_CAPTURE].addr = dma_params->dma_addr;
 
 	if (mcasp->version < MCASP_VERSION_3) {
 		mcasp->fifo_base = DAVINCI_MCASP_V2_AFIFO_BASE;
-		/* dma_data->dma_addr is pointing to the data port address */
+		/* dma_params->dma_addr is pointing to the data port address */
 		mcasp->dat_port = true;
 	} else {
 		mcasp->fifo_base = DAVINCI_MCASP_V3_AFIFO_BASE;
@@ -1077,9 +1136,9 @@ static int davinci_mcasp_probe(struct platform_device *pdev)
 
 	res = platform_get_resource(pdev, IORESOURCE_DMA, 1);
 	if (res)
-		dma_data->channel = res->start;
+		dma_params->channel = res->start;
 	else
-		dma_data->channel = pdata->rx_dma_channel;
+		dma_params->channel = pdata->rx_dma_channel;
 
 	/* Unconditional dmaengine stuff */
 	mcasp->dma_data[SNDRV_PCM_STREAM_PLAYBACK].filter_data = "tx";
@@ -1127,49 +1186,12 @@ static int davinci_mcasp_remove(struct platform_device *pdev)
 	return 0;
 }
 
-#ifdef CONFIG_PM_SLEEP
-static int davinci_mcasp_suspend(struct device *dev)
-{
-	struct davinci_mcasp *mcasp = dev_get_drvdata(dev);
-
-	mcasp->context.txfmtctl = mcasp_get_reg(mcasp, DAVINCI_MCASP_TXFMCTL_REG);
-	mcasp->context.rxfmtctl = mcasp_get_reg(mcasp, DAVINCI_MCASP_RXFMCTL_REG);
-	mcasp->context.txfmt = mcasp_get_reg(mcasp, DAVINCI_MCASP_TXFMT_REG);
-	mcasp->context.rxfmt = mcasp_get_reg(mcasp, DAVINCI_MCASP_RXFMT_REG);
-	mcasp->context.aclkxctl = mcasp_get_reg(mcasp, DAVINCI_MCASP_ACLKXCTL_REG);
-	mcasp->context.aclkrctl = mcasp_get_reg(mcasp, DAVINCI_MCASP_ACLKRCTL_REG);
-	mcasp->context.pdir = mcasp_get_reg(mcasp, DAVINCI_MCASP_PDIR_REG);
-
-	return 0;
-}
-
-static int davinci_mcasp_resume(struct device *dev)
-{
-	struct davinci_mcasp *mcasp = dev_get_drvdata(dev);
-
-	mcasp_set_reg(mcasp, DAVINCI_MCASP_TXFMCTL_REG, mcasp->context.txfmtctl);
-	mcasp_set_reg(mcasp, DAVINCI_MCASP_RXFMCTL_REG, mcasp->context.rxfmtctl);
-	mcasp_set_reg(mcasp, DAVINCI_MCASP_TXFMT_REG, mcasp->context.txfmt);
-	mcasp_set_reg(mcasp, DAVINCI_MCASP_RXFMT_REG, mcasp->context.rxfmt);
-	mcasp_set_reg(mcasp, DAVINCI_MCASP_ACLKXCTL_REG, mcasp->context.aclkxctl);
-	mcasp_set_reg(mcasp, DAVINCI_MCASP_ACLKRCTL_REG, mcasp->context.aclkrctl);
-	mcasp_set_reg(mcasp, DAVINCI_MCASP_PDIR_REG, mcasp->context.pdir);
-
-	return 0;
-}
-#endif
-
-SIMPLE_DEV_PM_OPS(davinci_mcasp_pm_ops,
-		  davinci_mcasp_suspend,
-		  davinci_mcasp_resume);
-
 static struct platform_driver davinci_mcasp_driver = {
 	.probe		= davinci_mcasp_probe,
 	.remove		= davinci_mcasp_remove,
 	.driver		= {
 		.name	= "davinci-mcasp",
 		.owner	= THIS_MODULE,
-		.pm	= &davinci_mcasp_pm_ops,
 		.of_match_table = mcasp_dt_ids,
 	},
 };
diff --git a/sound/soc/fsl/Kconfig b/sound/soc/fsl/Kconfig
index 07f8f141727d..597962ec28fa 100644
--- a/sound/soc/fsl/Kconfig
+++ b/sound/soc/fsl/Kconfig
@@ -1,5 +1,6 @@
 config SND_SOC_FSL_SAI
 	tristate
+	select REGMAP_MMIO
 	select SND_SOC_GENERIC_DMAENGINE_PCM
 
 config SND_SOC_FSL_SSI
@@ -7,9 +8,11 @@ config SND_SOC_FSL_SSI
 
 config SND_SOC_FSL_SPDIF
 	tristate
+	select REGMAP_MMIO
 
 config SND_SOC_FSL_ESAI
 	tristate
+	select REGMAP_MMIO
 
 config SND_SOC_FSL_UTILS
 	tristate
@@ -168,12 +171,14 @@ config SND_SOC_EUKREA_TLV320
 	depends on MACH_EUKREA_MBIMX27_BASEBOARD \
 		|| MACH_EUKREA_MBIMXSD25_BASEBOARD \
 		|| MACH_EUKREA_MBIMXSD35_BASEBOARD \
-		|| MACH_EUKREA_MBIMXSD51_BASEBOARD
+		|| MACH_EUKREA_MBIMXSD51_BASEBOARD \
+		|| (OF && ARM)
 	depends on I2C
-	select SND_SOC_TLV320AIC23
-	select SND_SOC_IMX_PCM_FIQ
+	select SND_SOC_TLV320AIC23_I2C
 	select SND_SOC_IMX_AUDMUX
 	select SND_SOC_IMX_SSI
+	select SND_SOC_FSL_SSI
+	select SND_SOC_IMX_PCM_DMA
 	help
 	  Enable I2S based access to the TLV320AIC23B codec attached
 	  to the SSI interface
@@ -204,7 +209,6 @@ config SND_SOC_IMX_SPDIF
 	tristate "SoC Audio support for i.MX boards with S/PDIF"
 	select SND_SOC_IMX_PCM_DMA
 	select SND_SOC_FSL_SPDIF
-	select REGMAP_MMIO
 	help
 	  SoC Audio support for i.MX boards with S/PDIF
 	  Say Y if you want to add support for SoC audio on an i.MX board with
diff --git a/sound/soc/fsl/eukrea-tlv320.c b/sound/soc/fsl/eukrea-tlv320.c
index 5983740be123..eb093d5b85c4 100644
--- a/sound/soc/fsl/eukrea-tlv320.c
+++ b/sound/soc/fsl/eukrea-tlv320.c
@@ -15,8 +15,11 @@
  *
  */
 
+#include <linux/errno.h>
 #include <linux/module.h>
 #include <linux/moduleparam.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
 #include <linux/device.h>
 #include <linux/i2c.h>
 #include <sound/core.h>
@@ -26,6 +29,7 @@
 
 #include "../codecs/tlv320aic23.h"
 #include "imx-ssi.h"
+#include "fsl_ssi.h"
 #include "imx-audmux.h"
 
 #define CODEC_CLOCK 12000000
@@ -41,7 +45,8 @@ static int eukrea_tlv320_hw_params(struct snd_pcm_substream *substream,
 	ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S |
 				  SND_SOC_DAIFMT_NB_NF |
 				  SND_SOC_DAIFMT_CBM_CFM);
-	if (ret) {
+	/* fsl_ssi lacks the set_fmt ops. */
+	if (ret && ret != -ENOTSUPP) {
 		dev_err(cpu_dai->dev,
 			"Failed to set the cpu dai format.\n");
 		return ret;
@@ -63,11 +68,13 @@ static int eukrea_tlv320_hw_params(struct snd_pcm_substream *substream,
 			"Failed to set the codec sysclk.\n");
 		return ret;
 	}
+
 	snd_soc_dai_set_tdm_slot(cpu_dai, 0xffffffc, 0xffffffc, 2, 0);
 
 	ret = snd_soc_dai_set_sysclk(cpu_dai, IMX_SSP_SYS_CLK, 0,
 				SND_SOC_CLOCK_IN);
-	if (ret) {
+	/* fsl_ssi lacks the set_sysclk ops */
+	if (ret && ret != -EINVAL) {
 		dev_err(cpu_dai->dev,
 			"Can't set the IMX_SSP_SYS_CLK CPU system clock.\n");
 		return ret;
@@ -84,14 +91,10 @@ static struct snd_soc_dai_link eukrea_tlv320_dai = {
 	.name		= "tlv320aic23",
 	.stream_name	= "TLV320AIC23",
 	.codec_dai_name	= "tlv320aic23-hifi",
-	.platform_name	= "imx-ssi.0",
-	.codec_name	= "tlv320aic23-codec.0-001a",
-	.cpu_dai_name	= "imx-ssi.0",
 	.ops		= &eukrea_tlv320_snd_ops,
 };
 
 static struct snd_soc_card eukrea_tlv320 = {
-	.name		= "cpuimx-audio",
 	.owner		= THIS_MODULE,
 	.dai_link	= &eukrea_tlv320_dai,
 	.num_links	= 1,
@@ -101,8 +104,65 @@ static int eukrea_tlv320_probe(struct platform_device *pdev)
 {
 	int ret;
 	int int_port = 0, ext_port;
+	struct device_node *np = pdev->dev.of_node;
+	struct device_node *ssi_np, *codec_np;
 
-	if (machine_is_eukrea_cpuimx27()) {
+	eukrea_tlv320.dev = &pdev->dev;
+	if (np) {
+		ret = snd_soc_of_parse_card_name(&eukrea_tlv320,
+						 "eukrea,model");
+		if (ret) {
+			dev_err(&pdev->dev,
+				"eukrea,model node missing or invalid.\n");
+			goto err;
+		}
+
+		ssi_np = of_parse_phandle(pdev->dev.of_node,
+					  "ssi-controller", 0);
+		if (!ssi_np) {
+			dev_err(&pdev->dev,
+				"ssi-controller missing or invalid.\n");
+			ret = -ENODEV;
+			goto err;
+		}
+
+		codec_np = of_parse_phandle(ssi_np, "codec-handle", 0);
+		if (codec_np)
+			eukrea_tlv320_dai.codec_of_node = codec_np;
+		else
+			dev_err(&pdev->dev, "codec-handle node missing or invalid.\n");
+
+		ret = of_property_read_u32(np, "fsl,mux-int-port", &int_port);
+		if (ret) {
+			dev_err(&pdev->dev,
+				"fsl,mux-int-port node missing or invalid.\n");
+			return ret;
+		}
+		ret = of_property_read_u32(np, "fsl,mux-ext-port", &ext_port);
+		if (ret) {
+			dev_err(&pdev->dev,
+				"fsl,mux-ext-port node missing or invalid.\n");
+			return ret;
+		}
+
+		/*
+		 * The port numbering in the hardware manual starts at 1, while
+		 * the audmux API expects it starts at 0.
+		 */
+		int_port--;
+		ext_port--;
+
+		eukrea_tlv320_dai.cpu_of_node = ssi_np;
+		eukrea_tlv320_dai.platform_of_node = ssi_np;
+	} else {
+		eukrea_tlv320_dai.cpu_dai_name = "imx-ssi.0";
+		eukrea_tlv320_dai.platform_name = "imx-ssi.0";
+		eukrea_tlv320_dai.codec_name = "tlv320aic23-codec.0-001a";
+		eukrea_tlv320.name = "cpuimx-audio";
+	}
+
+	if (machine_is_eukrea_cpuimx27() ||
+	    of_find_compatible_node(NULL, NULL, "fsl,imx21-audmux")) {
 		imx_audmux_v1_configure_port(MX27_AUDMUX_HPCR1_SSI0,
 			IMX_AUDMUX_V1_PCR_SYN |
 			IMX_AUDMUX_V1_PCR_TFSDIR |
@@ -119,8 +179,12 @@ static int eukrea_tlv320_probe(struct platform_device *pdev)
 		);
 	} else if (machine_is_eukrea_cpuimx25sd() ||
 		   machine_is_eukrea_cpuimx35sd() ||
-		   machine_is_eukrea_cpuimx51sd()) {
-		ext_port = machine_is_eukrea_cpuimx25sd() ? 4 : 3;
+		   machine_is_eukrea_cpuimx51sd() ||
+		   of_find_compatible_node(NULL, NULL, "fsl,imx31-audmux")) {
+		if (!np)
+			ext_port = machine_is_eukrea_cpuimx25sd() ?
+				4 : 3;
+
 		imx_audmux_v2_configure_port(int_port,
 			IMX_AUDMUX_V2_PTCR_SYN |
 			IMX_AUDMUX_V2_PTCR_TFSDIR |
@@ -134,14 +198,27 @@ static int eukrea_tlv320_probe(struct platform_device *pdev)
 			IMX_AUDMUX_V2_PDCR_RXDSEL(int_port)
 		);
 	} else {
-		/* return happy. We might run on a totally different machine */
-		return 0;
+		if (np) {
+			/* The eukrea,asoc-tlv320 driver was explicitely
+			 * requested (through the device tree).
+			 */
+			dev_err(&pdev->dev,
+				"Missing or invalid audmux DT node.\n");
+			return -ENODEV;
+		} else {
+			/* Return happy.
+			 * We might run on a totally different machine.
+			 */
+			return 0;
+		}
 	}
 
-	eukrea_tlv320.dev = &pdev->dev;
 	ret = snd_soc_register_card(&eukrea_tlv320);
+err:
 	if (ret)
 		dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n", ret);
+	if (np)
+		of_node_put(ssi_np);
 
 	return ret;
 }
@@ -153,10 +230,17 @@ static int eukrea_tlv320_remove(struct platform_device *pdev)
 	return 0;
 }
 
+static const struct of_device_id imx_tlv320_dt_ids[] = {
+	{ .compatible = "eukrea,asoc-tlv320"},
+	{ /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, imx_tlv320_dt_ids);
+
 static struct platform_driver eukrea_tlv320_driver = {
 	.driver = {
 		.name = "eukrea_tlv320",
 		.owner = THIS_MODULE,
+		.of_match_table = imx_tlv320_dt_ids,
 	},
 	.probe = eukrea_tlv320_probe,
 	.remove = eukrea_tlv320_remove,
diff --git a/sound/soc/fsl/fsl_esai.c b/sound/soc/fsl/fsl_esai.c
index c84026c99134..0ba37005ab04 100644
--- a/sound/soc/fsl/fsl_esai.c
+++ b/sound/soc/fsl/fsl_esai.c
@@ -431,17 +431,26 @@ static int fsl_esai_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
 static int fsl_esai_startup(struct snd_pcm_substream *substream,
 			    struct snd_soc_dai *dai)
 {
+	int ret;
 	struct fsl_esai *esai_priv = snd_soc_dai_get_drvdata(dai);
 
 	/*
 	 * Some platforms might use the same bit to gate all three or two of
 	 * clocks, so keep all clocks open/close at the same time for safety
 	 */
-	clk_prepare_enable(esai_priv->coreclk);
-	if (!IS_ERR(esai_priv->extalclk))
-		clk_prepare_enable(esai_priv->extalclk);
-	if (!IS_ERR(esai_priv->fsysclk))
-		clk_prepare_enable(esai_priv->fsysclk);
+	ret = clk_prepare_enable(esai_priv->coreclk);
+	if (ret)
+		return ret;
+	if (!IS_ERR(esai_priv->extalclk)) {
+		ret = clk_prepare_enable(esai_priv->extalclk);
+		if (ret)
+			goto err_extalck;
+	}
+	if (!IS_ERR(esai_priv->fsysclk)) {
+		ret = clk_prepare_enable(esai_priv->fsysclk);
+		if (ret)
+			goto err_fsysclk;
+	}
 
 	if (!dai->active) {
 		/* Reset Port C */
@@ -463,6 +472,14 @@ static int fsl_esai_startup(struct snd_pcm_substream *substream,
 	}
 
 	return 0;
+
+err_fsysclk:
+	if (!IS_ERR(esai_priv->extalclk))
+		clk_disable_unprepare(esai_priv->extalclk);
+err_extalck:
+	clk_disable_unprepare(esai_priv->coreclk);
+
+	return ret;
 }
 
 static int fsl_esai_hw_params(struct snd_pcm_substream *substream,
@@ -661,7 +678,7 @@ static bool fsl_esai_writeable_reg(struct device *dev, unsigned int reg)
 	}
 }
 
-static const struct regmap_config fsl_esai_regmap_config = {
+static struct regmap_config fsl_esai_regmap_config = {
 	.reg_bits = 32,
 	.reg_stride = 4,
 	.val_bits = 32,
@@ -687,6 +704,9 @@ static int fsl_esai_probe(struct platform_device *pdev)
 	esai_priv->pdev = pdev;
 	strcpy(esai_priv->name, np->name);
 
+	if (of_property_read_bool(np, "big-endian"))
+		fsl_esai_regmap_config.val_format_endian = REGMAP_ENDIAN_BIG;
+
 	/* Get the addresses and IRQ */
 	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 	regs = devm_ioremap_resource(&pdev->dev, res);
diff --git a/sound/soc/fsl/fsl_sai.c b/sound/soc/fsl/fsl_sai.c
index cdd3fa830704..c4a423111673 100644
--- a/sound/soc/fsl/fsl_sai.c
+++ b/sound/soc/fsl/fsl_sai.c
@@ -15,6 +15,7 @@
 #include <linux/dmaengine.h>
 #include <linux/module.h>
 #include <linux/of_address.h>
+#include <linux/regmap.h>
 #include <linux/slab.h>
 #include <sound/core.h>
 #include <sound/dmaengine_pcm.h>
@@ -22,34 +23,6 @@
 
 #include "fsl_sai.h"
 
-static inline u32 sai_readl(struct fsl_sai *sai,
-		const void __iomem *addr)
-{
-	u32 val;
-
-	val = __raw_readl(addr);
-
-	if (likely(sai->big_endian_regs))
-		val = be32_to_cpu(val);
-	else
-		val = le32_to_cpu(val);
-	rmb();
-
-	return val;
-}
-
-static inline void sai_writel(struct fsl_sai *sai,
-		u32 val, void __iomem *addr)
-{
-	wmb();
-	if (likely(sai->big_endian_regs))
-		val = cpu_to_be32(val);
-	else
-		val = cpu_to_le32(val);
-
-	__raw_writel(val, addr);
-}
-
 static int fsl_sai_set_dai_sysclk_tr(struct snd_soc_dai *cpu_dai,
 		int clk_id, unsigned int freq, int fsl_dir)
 {
@@ -61,7 +34,8 @@ static int fsl_sai_set_dai_sysclk_tr(struct snd_soc_dai *cpu_dai,
 	else
 		reg_cr2 = FSL_SAI_RCR2;
 
-	val_cr2 = sai_readl(sai, sai->base + reg_cr2);
+	regmap_read(sai->regmap, reg_cr2, &val_cr2);
+
 	val_cr2 &= ~FSL_SAI_CR2_MSEL_MASK;
 
 	switch (clk_id) {
@@ -81,7 +55,7 @@ static int fsl_sai_set_dai_sysclk_tr(struct snd_soc_dai *cpu_dai,
 		return -EINVAL;
 	}
 
-	sai_writel(sai, val_cr2, sai->base + reg_cr2);
+	regmap_write(sai->regmap, reg_cr2, val_cr2);
 
 	return 0;
 }
@@ -89,32 +63,22 @@ static int fsl_sai_set_dai_sysclk_tr(struct snd_soc_dai *cpu_dai,
 static int fsl_sai_set_dai_sysclk(struct snd_soc_dai *cpu_dai,
 		int clk_id, unsigned int freq, int dir)
 {
-	struct fsl_sai *sai = snd_soc_dai_get_drvdata(cpu_dai);
 	int ret;
 
 	if (dir == SND_SOC_CLOCK_IN)
 		return 0;
 
-	ret = clk_prepare_enable(sai->clk);
-	if (ret)
-		return ret;
-
 	ret = fsl_sai_set_dai_sysclk_tr(cpu_dai, clk_id, freq,
 					FSL_FMT_TRANSMITTER);
 	if (ret) {
 		dev_err(cpu_dai->dev, "Cannot set tx sysclk: %d\n", ret);
-		goto err_clk;
+		return ret;
 	}
 
 	ret = fsl_sai_set_dai_sysclk_tr(cpu_dai, clk_id, freq,
 					FSL_FMT_RECEIVER);
-	if (ret) {
+	if (ret)
 		dev_err(cpu_dai->dev, "Cannot set rx sysclk: %d\n", ret);
-		goto err_clk;
-	}
-
-err_clk:
-	clk_disable_unprepare(sai->clk);
 
 	return ret;
 }
@@ -133,43 +97,84 @@ static int fsl_sai_set_dai_fmt_tr(struct snd_soc_dai *cpu_dai,
 		reg_cr4 = FSL_SAI_RCR4;
 	}
 
-	val_cr2 = sai_readl(sai, sai->base + reg_cr2);
-	val_cr4 = sai_readl(sai, sai->base + reg_cr4);
+	regmap_read(sai->regmap, reg_cr2, &val_cr2);
+	regmap_read(sai->regmap, reg_cr4, &val_cr4);
 
 	if (sai->big_endian_data)
 		val_cr4 &= ~FSL_SAI_CR4_MF;
 	else
 		val_cr4 |= FSL_SAI_CR4_MF;
 
+	/* DAI mode */
 	switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
 	case SND_SOC_DAIFMT_I2S:
+		/*
+		 * Frame low, 1clk before data, one word length for frame sync,
+		 * frame sync starts one serial clock cycle earlier,
+		 * that is, together with the last bit of the previous
+		 * data word.
+		 */
+		val_cr2 &= ~FSL_SAI_CR2_BCP;
+		val_cr4 |= FSL_SAI_CR4_FSE | FSL_SAI_CR4_FSP;
+		break;
+	case SND_SOC_DAIFMT_LEFT_J:
+		/*
+		 * Frame high, one word length for frame sync,
+		 * frame sync asserts with the first bit of the frame.
+		 */
+		val_cr2 &= ~FSL_SAI_CR2_BCP;
+		val_cr4 &= ~(FSL_SAI_CR4_FSE | FSL_SAI_CR4_FSP);
+		break;
+	case SND_SOC_DAIFMT_DSP_A:
+		/*
+		 * Frame high, 1clk before data, one bit for frame sync,
+		 * frame sync starts one serial clock cycle earlier,
+		 * that is, together with the last bit of the previous
+		 * data word.
+		 */
+		val_cr2 &= ~FSL_SAI_CR2_BCP;
+		val_cr4 &= ~FSL_SAI_CR4_FSP;
 		val_cr4 |= FSL_SAI_CR4_FSE;
+		sai->is_dsp_mode = true;
+		break;
+	case SND_SOC_DAIFMT_DSP_B:
+		/*
+		 * Frame high, one bit for frame sync,
+		 * frame sync asserts with the first bit of the frame.
+		 */
+		val_cr2 &= ~FSL_SAI_CR2_BCP;
+		val_cr4 &= ~(FSL_SAI_CR4_FSE | FSL_SAI_CR4_FSP);
+		sai->is_dsp_mode = true;
 		break;
+	case SND_SOC_DAIFMT_RIGHT_J:
+		/* To be done */
 	default:
 		return -EINVAL;
 	}
 
+	/* DAI clock inversion */
 	switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
 	case SND_SOC_DAIFMT_IB_IF:
-		val_cr4 |= FSL_SAI_CR4_FSP;
-		val_cr2 &= ~FSL_SAI_CR2_BCP;
+		/* Invert both clocks */
+		val_cr2 ^= FSL_SAI_CR2_BCP;
+		val_cr4 ^= FSL_SAI_CR4_FSP;
 		break;
 	case SND_SOC_DAIFMT_IB_NF:
-		val_cr4 &= ~FSL_SAI_CR4_FSP;
-		val_cr2 &= ~FSL_SAI_CR2_BCP;
+		/* Invert bit clock */
+		val_cr2 ^= FSL_SAI_CR2_BCP;
 		break;
 	case SND_SOC_DAIFMT_NB_IF:
-		val_cr4 |= FSL_SAI_CR4_FSP;
-		val_cr2 |= FSL_SAI_CR2_BCP;
+		/* Invert frame clock */
+		val_cr4 ^= FSL_SAI_CR4_FSP;
 		break;
 	case SND_SOC_DAIFMT_NB_NF:
-		val_cr4 &= ~FSL_SAI_CR4_FSP;
-		val_cr2 |= FSL_SAI_CR2_BCP;
+		/* Nothing to do for both normal cases */
 		break;
 	default:
 		return -EINVAL;
 	}
 
+	/* DAI clock master masks */
 	switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
 	case SND_SOC_DAIFMT_CBS_CFS:
 		val_cr2 |= FSL_SAI_CR2_BCD_MSTR;
@@ -179,39 +184,37 @@ static int fsl_sai_set_dai_fmt_tr(struct snd_soc_dai *cpu_dai,
 		val_cr2 &= ~FSL_SAI_CR2_BCD_MSTR;
 		val_cr4 &= ~FSL_SAI_CR4_FSD_MSTR;
 		break;
+	case SND_SOC_DAIFMT_CBS_CFM:
+		val_cr2 |= FSL_SAI_CR2_BCD_MSTR;
+		val_cr4 &= ~FSL_SAI_CR4_FSD_MSTR;
+		break;
+	case SND_SOC_DAIFMT_CBM_CFS:
+		val_cr2 &= ~FSL_SAI_CR2_BCD_MSTR;
+		val_cr4 |= FSL_SAI_CR4_FSD_MSTR;
+		break;
 	default:
 		return -EINVAL;
 	}
 
-	sai_writel(sai, val_cr2, sai->base + reg_cr2);
-	sai_writel(sai, val_cr4, sai->base + reg_cr4);
+	regmap_write(sai->regmap, reg_cr2, val_cr2);
+	regmap_write(sai->regmap, reg_cr4, val_cr4);
 
 	return 0;
 }
 
 static int fsl_sai_set_dai_fmt(struct snd_soc_dai *cpu_dai, unsigned int fmt)
 {
-	struct fsl_sai *sai = snd_soc_dai_get_drvdata(cpu_dai);
 	int ret;
 
-	ret = clk_prepare_enable(sai->clk);
-	if (ret)
-		return ret;
-
 	ret = fsl_sai_set_dai_fmt_tr(cpu_dai, fmt, FSL_FMT_TRANSMITTER);
 	if (ret) {
 		dev_err(cpu_dai->dev, "Cannot set tx format: %d\n", ret);
-		goto err_clk;
+		return ret;
 	}
 
 	ret = fsl_sai_set_dai_fmt_tr(cpu_dai, fmt, FSL_FMT_RECEIVER);
-	if (ret) {
+	if (ret)
 		dev_err(cpu_dai->dev, "Cannot set rx format: %d\n", ret);
-		goto err_clk;
-	}
-
-err_clk:
-	clk_disable_unprepare(sai->clk);
 
 	return ret;
 }
@@ -235,16 +238,19 @@ static int fsl_sai_hw_params(struct snd_pcm_substream *substream,
 		reg_mr = FSL_SAI_RMR;
 	}
 
-	val_cr4 = sai_readl(sai, sai->base + reg_cr4);
+	regmap_read(sai->regmap, reg_cr4, &val_cr4);
+	regmap_read(sai->regmap, reg_cr4, &val_cr5);
+
 	val_cr4 &= ~FSL_SAI_CR4_SYWD_MASK;
 	val_cr4 &= ~FSL_SAI_CR4_FRSZ_MASK;
 
-	val_cr5 = sai_readl(sai, sai->base + reg_cr5);
 	val_cr5 &= ~FSL_SAI_CR5_WNW_MASK;
 	val_cr5 &= ~FSL_SAI_CR5_W0W_MASK;
 	val_cr5 &= ~FSL_SAI_CR5_FBT_MASK;
 
-	val_cr4 |= FSL_SAI_CR4_SYWD(word_width);
+	if (!sai->is_dsp_mode)
+		val_cr4 |= FSL_SAI_CR4_SYWD(word_width);
+
 	val_cr5 |= FSL_SAI_CR5_WNW(word_width);
 	val_cr5 |= FSL_SAI_CR5_W0W(word_width);
 
@@ -257,9 +263,9 @@ static int fsl_sai_hw_params(struct snd_pcm_substream *substream,
 	val_cr4 |= FSL_SAI_CR4_FRSZ(channels);
 	val_mr = ~0UL - ((1 << channels) - 1);
 
-	sai_writel(sai, val_cr4, sai->base + reg_cr4);
-	sai_writel(sai, val_cr5, sai->base + reg_cr5);
-	sai_writel(sai, val_mr, sai->base + reg_mr);
+	regmap_write(sai->regmap, reg_cr4, val_cr4);
+	regmap_write(sai->regmap, reg_cr5, val_cr5);
+	regmap_write(sai->regmap, reg_mr, val_mr);
 
 	return 0;
 }
@@ -268,44 +274,42 @@ static int fsl_sai_trigger(struct snd_pcm_substream *substream, int cmd,
 		struct snd_soc_dai *cpu_dai)
 {
 	struct fsl_sai *sai = snd_soc_dai_get_drvdata(cpu_dai);
-	u32 tcsr, rcsr, val_cr2, val_cr3, reg_cr3;
-
-	val_cr2 = sai_readl(sai, sai->base + FSL_SAI_TCR2);
-	val_cr2 &= ~FSL_SAI_CR2_SYNC;
-	sai_writel(sai, val_cr2, sai->base + FSL_SAI_TCR2);
+	u32 tcsr, rcsr;
 
-	val_cr2 = sai_readl(sai, sai->base + FSL_SAI_RCR2);
-	val_cr2 |= FSL_SAI_CR2_SYNC;
-	sai_writel(sai, val_cr2, sai->base + FSL_SAI_RCR2);
+	/*
+	 * The transmitter bit clock and frame sync are to be
+	 * used by both the transmitter and receiver.
+	 */
+	regmap_update_bits(sai->regmap, FSL_SAI_TCR2, FSL_SAI_CR2_SYNC,
+			   ~FSL_SAI_CR2_SYNC);
+	regmap_update_bits(sai->regmap, FSL_SAI_RCR2, FSL_SAI_CR2_SYNC,
+			   FSL_SAI_CR2_SYNC);
 
-	tcsr = sai_readl(sai, sai->base + FSL_SAI_TCSR);
-	rcsr = sai_readl(sai, sai->base + FSL_SAI_RCSR);
+	regmap_read(sai->regmap, FSL_SAI_TCSR, &tcsr);
+	regmap_read(sai->regmap, FSL_SAI_RCSR, &rcsr);
 
 	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
 		tcsr |= FSL_SAI_CSR_FRDE;
 		rcsr &= ~FSL_SAI_CSR_FRDE;
-		reg_cr3 = FSL_SAI_TCR3;
 	} else {
 		rcsr |= FSL_SAI_CSR_FRDE;
 		tcsr &= ~FSL_SAI_CSR_FRDE;
-		reg_cr3 = FSL_SAI_RCR3;
 	}
 
-	val_cr3 = sai_readl(sai, sai->base + reg_cr3);
-
+	/*
+	 * It is recommended that the transmitter is the last enabled
+	 * and the first disabled.
+	 */
 	switch (cmd) {
 	case SNDRV_PCM_TRIGGER_START:
 	case SNDRV_PCM_TRIGGER_RESUME:
 	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
 		tcsr |= FSL_SAI_CSR_TERE;
 		rcsr |= FSL_SAI_CSR_TERE;
-		val_cr3 |= FSL_SAI_CR3_TRCE;
 
-		sai_writel(sai, val_cr3, sai->base + reg_cr3);
-		sai_writel(sai, rcsr, sai->base + FSL_SAI_RCSR);
-		sai_writel(sai, tcsr, sai->base + FSL_SAI_TCSR);
+		regmap_write(sai->regmap, FSL_SAI_RCSR, rcsr);
+		regmap_write(sai->regmap, FSL_SAI_TCSR, tcsr);
 		break;
-
 	case SNDRV_PCM_TRIGGER_STOP:
 	case SNDRV_PCM_TRIGGER_SUSPEND:
 	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
@@ -314,11 +318,8 @@ static int fsl_sai_trigger(struct snd_pcm_substream *substream, int cmd,
 			rcsr &= ~FSL_SAI_CSR_TERE;
 		}
 
-		val_cr3 &= ~FSL_SAI_CR3_TRCE;
-
-		sai_writel(sai, tcsr, sai->base + FSL_SAI_TCSR);
-		sai_writel(sai, rcsr, sai->base + FSL_SAI_RCSR);
-		sai_writel(sai, val_cr3, sai->base + reg_cr3);
+		regmap_write(sai->regmap, FSL_SAI_TCSR, tcsr);
+		regmap_write(sai->regmap, FSL_SAI_RCSR, rcsr);
 		break;
 	default:
 		return -EINVAL;
@@ -331,16 +332,32 @@ static int fsl_sai_startup(struct snd_pcm_substream *substream,
 		struct snd_soc_dai *cpu_dai)
 {
 	struct fsl_sai *sai = snd_soc_dai_get_drvdata(cpu_dai);
+	u32 reg;
+
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+		reg = FSL_SAI_TCR3;
+	else
+		reg = FSL_SAI_RCR3;
+
+	regmap_update_bits(sai->regmap, reg, FSL_SAI_CR3_TRCE,
+			   FSL_SAI_CR3_TRCE);
 
-	return clk_prepare_enable(sai->clk);
+	return 0;
 }
 
 static void fsl_sai_shutdown(struct snd_pcm_substream *substream,
 		struct snd_soc_dai *cpu_dai)
 {
 	struct fsl_sai *sai = snd_soc_dai_get_drvdata(cpu_dai);
+	u32 reg;
+
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+		reg = FSL_SAI_TCR3;
+	else
+		reg = FSL_SAI_RCR3;
 
-	clk_disable_unprepare(sai->clk);
+	regmap_update_bits(sai->regmap, reg, FSL_SAI_CR3_TRCE,
+			   ~FSL_SAI_CR3_TRCE);
 }
 
 static const struct snd_soc_dai_ops fsl_sai_pcm_dai_ops = {
@@ -355,18 +372,13 @@ static const struct snd_soc_dai_ops fsl_sai_pcm_dai_ops = {
 static int fsl_sai_dai_probe(struct snd_soc_dai *cpu_dai)
 {
 	struct fsl_sai *sai = dev_get_drvdata(cpu_dai->dev);
-	int ret;
 
-	ret = clk_prepare_enable(sai->clk);
-	if (ret)
-		return ret;
-
-	sai_writel(sai, 0x0, sai->base + FSL_SAI_RCSR);
-	sai_writel(sai, 0x0, sai->base + FSL_SAI_TCSR);
-	sai_writel(sai, FSL_SAI_MAXBURST_TX * 2, sai->base + FSL_SAI_TCR1);
-	sai_writel(sai, FSL_SAI_MAXBURST_RX - 1, sai->base + FSL_SAI_RCR1);
-
-	clk_disable_unprepare(sai->clk);
+	regmap_update_bits(sai->regmap, FSL_SAI_TCSR, 0xffffffff, 0x0);
+	regmap_update_bits(sai->regmap, FSL_SAI_RCSR, 0xffffffff, 0x0);
+	regmap_update_bits(sai->regmap, FSL_SAI_TCR1, FSL_SAI_CR1_RFW_MASK,
+			   FSL_SAI_MAXBURST_TX * 2);
+	regmap_update_bits(sai->regmap, FSL_SAI_RCR1, FSL_SAI_CR1_RFW_MASK,
+			   FSL_SAI_MAXBURST_RX - 1);
 
 	snd_soc_dai_init_dma_data(cpu_dai, &sai->dma_params_tx,
 				&sai->dma_params_rx);
@@ -397,26 +409,109 @@ static const struct snd_soc_component_driver fsl_component = {
 	.name           = "fsl-sai",
 };
 
+static bool fsl_sai_readable_reg(struct device *dev, unsigned int reg)
+{
+	switch (reg) {
+	case FSL_SAI_TCSR:
+	case FSL_SAI_TCR1:
+	case FSL_SAI_TCR2:
+	case FSL_SAI_TCR3:
+	case FSL_SAI_TCR4:
+	case FSL_SAI_TCR5:
+	case FSL_SAI_TFR:
+	case FSL_SAI_TMR:
+	case FSL_SAI_RCSR:
+	case FSL_SAI_RCR1:
+	case FSL_SAI_RCR2:
+	case FSL_SAI_RCR3:
+	case FSL_SAI_RCR4:
+	case FSL_SAI_RCR5:
+	case FSL_SAI_RDR:
+	case FSL_SAI_RFR:
+	case FSL_SAI_RMR:
+		return true;
+	default:
+		return false;
+	}
+}
+
+static bool fsl_sai_volatile_reg(struct device *dev, unsigned int reg)
+{
+	switch (reg) {
+	case FSL_SAI_TFR:
+	case FSL_SAI_RFR:
+	case FSL_SAI_TDR:
+	case FSL_SAI_RDR:
+		return true;
+	default:
+		return false;
+	}
+
+}
+
+static bool fsl_sai_writeable_reg(struct device *dev, unsigned int reg)
+{
+	switch (reg) {
+	case FSL_SAI_TCSR:
+	case FSL_SAI_TCR1:
+	case FSL_SAI_TCR2:
+	case FSL_SAI_TCR3:
+	case FSL_SAI_TCR4:
+	case FSL_SAI_TCR5:
+	case FSL_SAI_TDR:
+	case FSL_SAI_TMR:
+	case FSL_SAI_RCSR:
+	case FSL_SAI_RCR1:
+	case FSL_SAI_RCR2:
+	case FSL_SAI_RCR3:
+	case FSL_SAI_RCR4:
+	case FSL_SAI_RCR5:
+	case FSL_SAI_RMR:
+		return true;
+	default:
+		return false;
+	}
+}
+
+static struct regmap_config fsl_sai_regmap_config = {
+	.reg_bits = 32,
+	.reg_stride = 4,
+	.val_bits = 32,
+
+	.max_register = FSL_SAI_RMR,
+	.readable_reg = fsl_sai_readable_reg,
+	.volatile_reg = fsl_sai_volatile_reg,
+	.writeable_reg = fsl_sai_writeable_reg,
+};
+
 static int fsl_sai_probe(struct platform_device *pdev)
 {
 	struct device_node *np = pdev->dev.of_node;
 	struct fsl_sai *sai;
 	struct resource *res;
+	void __iomem *base;
 	int ret;
 
 	sai = devm_kzalloc(&pdev->dev, sizeof(*sai), GFP_KERNEL);
 	if (!sai)
 		return -ENOMEM;
 
+	sai->big_endian_regs = of_property_read_bool(np, "big-endian-regs");
+	if (sai->big_endian_regs)
+		fsl_sai_regmap_config.val_format_endian = REGMAP_ENDIAN_BIG;
+
+	sai->big_endian_data = of_property_read_bool(np, "big-endian-data");
+
 	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-	sai->base = devm_ioremap_resource(&pdev->dev, res);
-	if (IS_ERR(sai->base))
-		return PTR_ERR(sai->base);
-
-	sai->clk = devm_clk_get(&pdev->dev, "sai");
-	if (IS_ERR(sai->clk)) {
-		dev_err(&pdev->dev, "Cannot get SAI's clock\n");
-		return PTR_ERR(sai->clk);
+	base = devm_ioremap_resource(&pdev->dev, res);
+	if (IS_ERR(base))
+		return PTR_ERR(base);
+
+	sai->regmap = devm_regmap_init_mmio_clk(&pdev->dev,
+			"sai", base, &fsl_sai_regmap_config);
+	if (IS_ERR(sai->regmap)) {
+		dev_err(&pdev->dev, "regmap init failed\n");
+		return PTR_ERR(sai->regmap);
 	}
 
 	sai->dma_params_rx.addr = res->start + FSL_SAI_RDR;
@@ -424,9 +519,6 @@ static int fsl_sai_probe(struct platform_device *pdev)
 	sai->dma_params_rx.maxburst = FSL_SAI_MAXBURST_RX;
 	sai->dma_params_tx.maxburst = FSL_SAI_MAXBURST_TX;
 
-	sai->big_endian_regs = of_property_read_bool(np, "big-endian-regs");
-	sai->big_endian_data = of_property_read_bool(np, "big-endian-data");
-
 	platform_set_drvdata(pdev, sai);
 
 	ret = devm_snd_soc_register_component(&pdev->dev, &fsl_component,
diff --git a/sound/soc/fsl/fsl_sai.h b/sound/soc/fsl/fsl_sai.h
index 41bb62e69361..e432260be598 100644
--- a/sound/soc/fsl/fsl_sai.h
+++ b/sound/soc/fsl/fsl_sai.h
@@ -15,31 +15,36 @@
 			 SNDRV_PCM_FMTBIT_S20_3LE |\
 			 SNDRV_PCM_FMTBIT_S24_LE)
 
+/* SAI Register Map Register */
+#define FSL_SAI_TCSR	0x00 /* SAI Transmit Control */
+#define FSL_SAI_TCR1	0x04 /* SAI Transmit Configuration 1 */
+#define FSL_SAI_TCR2	0x08 /* SAI Transmit Configuration 2 */
+#define FSL_SAI_TCR3	0x0c /* SAI Transmit Configuration 3 */
+#define FSL_SAI_TCR4	0x10 /* SAI Transmit Configuration 4 */
+#define FSL_SAI_TCR5	0x14 /* SAI Transmit Configuration 5 */
+#define FSL_SAI_TDR	0x20 /* SAI Transmit Data */
+#define FSL_SAI_TFR	0x40 /* SAI Transmit FIFO */
+#define FSL_SAI_TMR	0x60 /* SAI Transmit Mask */
+#define FSL_SAI_RCSR	0x80 /* SAI Receive Control */
+#define FSL_SAI_RCR1	0x84 /* SAI Receive Configuration 1 */
+#define FSL_SAI_RCR2	0x88 /* SAI Receive Configuration 2 */
+#define FSL_SAI_RCR3	0x8c /* SAI Receive Configuration 3 */
+#define FSL_SAI_RCR4	0x90 /* SAI Receive Configuration 4 */
+#define FSL_SAI_RCR5	0x94 /* SAI Receive Configuration 5 */
+#define FSL_SAI_RDR	0xa0 /* SAI Receive Data */
+#define FSL_SAI_RFR	0xc0 /* SAI Receive FIFO */
+#define FSL_SAI_RMR	0xe0 /* SAI Receive Mask */
+
 /* SAI Transmit/Recieve Control Register */
-#define FSL_SAI_TCSR		0x00
-#define FSL_SAI_RCSR		0x80
 #define FSL_SAI_CSR_TERE	BIT(31)
 #define FSL_SAI_CSR_FWF		BIT(17)
 #define FSL_SAI_CSR_FRIE	BIT(8)
 #define FSL_SAI_CSR_FRDE	BIT(0)
 
-/* SAI Transmit Data/FIFO/MASK Register */
-#define FSL_SAI_TDR		0x20
-#define FSL_SAI_TFR		0x40
-#define FSL_SAI_TMR		0x60
-
-/* SAI Recieve Data/FIFO/MASK Register */
-#define FSL_SAI_RDR		0xa0
-#define FSL_SAI_RFR		0xc0
-#define FSL_SAI_RMR		0xe0
-
 /* SAI Transmit and Recieve Configuration 1 Register */
-#define FSL_SAI_TCR1		0x04
-#define FSL_SAI_RCR1		0x84
+#define FSL_SAI_CR1_RFW_MASK	0x1f
 
 /* SAI Transmit and Recieve Configuration 2 Register */
-#define FSL_SAI_TCR2		0x08
-#define FSL_SAI_RCR2		0x88
 #define FSL_SAI_CR2_SYNC	BIT(30)
 #define FSL_SAI_CR2_MSEL_MASK	(0xff << 26)
 #define FSL_SAI_CR2_MSEL_BUS	0
@@ -50,15 +55,11 @@
 #define FSL_SAI_CR2_BCD_MSTR	BIT(24)
 
 /* SAI Transmit and Recieve Configuration 3 Register */
-#define FSL_SAI_TCR3		0x0c
-#define FSL_SAI_RCR3		0x8c
 #define FSL_SAI_CR3_TRCE	BIT(16)
 #define FSL_SAI_CR3_WDFL(x)	(x)
 #define FSL_SAI_CR3_WDFL_MASK	0x1f
 
 /* SAI Transmit and Recieve Configuration 4 Register */
-#define FSL_SAI_TCR4		0x10
-#define FSL_SAI_RCR4		0x90
 #define FSL_SAI_CR4_FRSZ(x)	(((x) - 1) << 16)
 #define FSL_SAI_CR4_FRSZ_MASK	(0x1f << 16)
 #define FSL_SAI_CR4_SYWD(x)	(((x) - 1) << 8)
@@ -69,8 +70,6 @@
 #define FSL_SAI_CR4_FSD_MSTR	BIT(0)
 
 /* SAI Transmit and Recieve Configuration 5 Register */
-#define FSL_SAI_TCR5		0x14
-#define FSL_SAI_RCR5		0x94
 #define FSL_SAI_CR5_WNW(x)	(((x) - 1) << 24)
 #define FSL_SAI_CR5_WNW_MASK	(0x1f << 24)
 #define FSL_SAI_CR5_W0W(x)	(((x) - 1) << 16)
@@ -100,12 +99,11 @@
 #define FSL_SAI_MAXBURST_RX 6
 
 struct fsl_sai {
-	struct clk *clk;
-
-	void __iomem *base;
+	struct regmap *regmap;
 
 	bool big_endian_regs;
 	bool big_endian_data;
+	bool is_dsp_mode;
 
 	struct snd_dmaengine_dai_dma_data dma_params_rx;
 	struct snd_dmaengine_dai_dma_data dma_params_tx;
diff --git a/sound/soc/fsl/fsl_spdif.c b/sound/soc/fsl/fsl_spdif.c
index 4d075f1abe78..6452ca83d889 100644
--- a/sound/soc/fsl/fsl_spdif.c
+++ b/sound/soc/fsl/fsl_spdif.c
@@ -911,8 +911,8 @@ static int fsl_spdif_dai_probe(struct snd_soc_dai *dai)
 {
 	struct fsl_spdif_priv *spdif_private = snd_soc_dai_get_drvdata(dai);
 
-	dai->playback_dma_data = &spdif_private->dma_params_tx;
-	dai->capture_dma_data = &spdif_private->dma_params_rx;
+	snd_soc_dai_init_dma_data(dai, &spdif_private->dma_params_tx,
+				  &spdif_private->dma_params_rx);
 
 	snd_soc_add_dai_controls(dai, fsl_spdif_ctrls, ARRAY_SIZE(fsl_spdif_ctrls));
 
@@ -985,7 +985,7 @@ static bool fsl_spdif_writeable_reg(struct device *dev, unsigned int reg)
 	}
 }
 
-static const struct regmap_config fsl_spdif_regmap_config = {
+static struct regmap_config fsl_spdif_regmap_config = {
 	.reg_bits = 32,
 	.reg_stride = 4,
 	.val_bits = 32,
@@ -1105,6 +1105,9 @@ static int fsl_spdif_probe(struct platform_device *pdev)
 	memcpy(&spdif_priv->cpu_dai_drv, &fsl_spdif_dai, sizeof(fsl_spdif_dai));
 	spdif_priv->cpu_dai_drv.name = spdif_priv->name;
 
+	if (of_property_read_bool(np, "big-endian"))
+		fsl_spdif_regmap_config.val_format_endian = REGMAP_ENDIAN_BIG;
+
 	/* Get the addresses and IRQ */
 	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 	regs = devm_ioremap_resource(&pdev->dev, res);
diff --git a/sound/soc/fsl/imx-pcm-fiq.c b/sound/soc/fsl/imx-pcm-fiq.c
index 6553202dd48c..7abf6a079574 100644
--- a/sound/soc/fsl/imx-pcm-fiq.c
+++ b/sound/soc/fsl/imx-pcm-fiq.c
@@ -270,18 +270,17 @@ static int imx_pcm_new(struct snd_soc_pcm_runtime *rtd)
 		ret = imx_pcm_preallocate_dma_buffer(pcm,
 			SNDRV_PCM_STREAM_PLAYBACK);
 		if (ret)
-			goto out;
+			return ret;
 	}
 
 	if (pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream) {
 		ret = imx_pcm_preallocate_dma_buffer(pcm,
 			SNDRV_PCM_STREAM_CAPTURE);
 		if (ret)
-			goto out;
+			return ret;
 	}
 
-out:
-	return ret;
+	return 0;
 }
 
 static int ssi_irq = 0;
diff --git a/sound/soc/fsl/wm1133-ev1.c b/sound/soc/fsl/wm1133-ev1.c
index fce63252bdbb..804749a6c61e 100644
--- a/sound/soc/fsl/wm1133-ev1.c
+++ b/sound/soc/fsl/wm1133-ev1.c
@@ -214,12 +214,6 @@ static int wm1133_ev1_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_new_controls(dapm, wm1133_ev1_widgets,
-				  ARRAY_SIZE(wm1133_ev1_widgets));
-
-	snd_soc_dapm_add_routes(dapm, wm1133_ev1_map,
-				ARRAY_SIZE(wm1133_ev1_map));
-
 	/* Headphone jack detection */
 	snd_soc_jack_new(codec, "Headphone", SND_JACK_HEADPHONE, &hp_jack);
 	snd_soc_jack_add_pins(&hp_jack, ARRAY_SIZE(hp_jack_pins),
@@ -257,6 +251,11 @@ static struct snd_soc_card wm1133_ev1 = {
 	.owner = THIS_MODULE,
 	.dai_link = &wm1133_ev1_dai,
 	.num_links = 1,
+
+	.dapm_widgets = wm1133_ev1_widgets,
+	.num_dapm_widgets = ARRAY_SIZE(wm1133_ev1_widgets),
+	.dapm_routes = wm1133_ev1_map,
+	.num_dapm_routes = ARRAY_SIZE(wm1133_ev1_map),
 };
 
 static struct platform_device *wm1133_ev1_snd_device;
diff --git a/sound/soc/generic/simple-card.c b/sound/soc/generic/simple-card.c
index 2a1b1b5b5221..5dd47691ba41 100644
--- a/sound/soc/generic/simple-card.c
+++ b/sound/soc/generic/simple-card.c
@@ -9,48 +9,73 @@
  * published by the Free Software Foundation.
  */
 #include <linux/clk.h>
+#include <linux/device.h>
 #include <linux/module.h>
 #include <linux/of.h>
 #include <linux/platform_device.h>
 #include <linux/string.h>
 #include <sound/simple_card.h>
+#include <sound/soc-dai.h>
+#include <sound/soc.h>
+
+struct simple_card_data {
+	struct snd_soc_card snd_card;
+	unsigned int daifmt;
+	struct asoc_simple_dai cpu_dai;
+	struct asoc_simple_dai codec_dai;
+	struct snd_soc_dai_link snd_link;
+};
 
 static int __asoc_simple_card_dai_init(struct snd_soc_dai *dai,
-				       struct asoc_simple_dai *set,
-				       unsigned int daifmt)
+				       struct asoc_simple_dai *set)
 {
-	int ret = 0;
+	int ret;
 
-	daifmt |= set->fmt;
+	if (set->fmt) {
+		ret = snd_soc_dai_set_fmt(dai, set->fmt);
+		if (ret && ret != -ENOTSUPP) {
+			dev_err(dai->dev, "simple-card: set_fmt error\n");
+			goto err;
+		}
+	}
 
-	if (daifmt)
-		ret = snd_soc_dai_set_fmt(dai, daifmt);
+	if (set->sysclk) {
+		ret = snd_soc_dai_set_sysclk(dai, 0, set->sysclk, 0);
+		if (ret && ret != -ENOTSUPP) {
+			dev_err(dai->dev, "simple-card: set_sysclk error\n");
+			goto err;
+		}
+	}
 
-	if (ret == -ENOTSUPP) {
-		dev_dbg(dai->dev, "ASoC: set_fmt is not supported\n");
-		ret = 0;
+	if (set->slots) {
+		ret = snd_soc_dai_set_tdm_slot(dai, 0, 0,
+						set->slots,
+						set->slot_width);
+		if (ret && ret != -ENOTSUPP) {
+			dev_err(dai->dev, "simple-card: set_tdm_slot error\n");
+			goto err;
+		}
 	}
 
-	if (!ret && set->sysclk)
-		ret = snd_soc_dai_set_sysclk(dai, 0, set->sysclk, 0);
+	ret = 0;
 
+err:
 	return ret;
 }
 
 static int asoc_simple_card_dai_init(struct snd_soc_pcm_runtime *rtd)
 {
-	struct asoc_simple_card_info *info =
+	struct simple_card_data *priv =
 				snd_soc_card_get_drvdata(rtd->card);
 	struct snd_soc_dai *codec = rtd->codec_dai;
 	struct snd_soc_dai *cpu = rtd->cpu_dai;
-	unsigned int daifmt = info->daifmt;
 	int ret;
 
-	ret = __asoc_simple_card_dai_init(codec, &info->codec_dai, daifmt);
+	ret = __asoc_simple_card_dai_init(codec, &priv->codec_dai);
 	if (ret < 0)
 		return ret;
 
-	ret = __asoc_simple_card_dai_init(cpu, &info->cpu_dai, daifmt);
+	ret = __asoc_simple_card_dai_init(cpu, &priv->cpu_dai);
 	if (ret < 0)
 		return ret;
 
@@ -59,9 +84,12 @@ static int asoc_simple_card_dai_init(struct snd_soc_pcm_runtime *rtd)
 
 static int
 asoc_simple_card_sub_parse_of(struct device_node *np,
+			      unsigned int daifmt,
 			      struct asoc_simple_dai *dai,
-			      struct device_node **node)
+			      const struct device_node **p_node,
+			      const char **name)
 {
+	struct device_node *node;
 	struct clk *clk;
 	int ret;
 
@@ -69,21 +97,28 @@ asoc_simple_card_sub_parse_of(struct device_node *np,
 	 * get node via "sound-dai = <&phandle port>"
 	 * it will be used as xxx_of_node on soc_bind_dai_link()
 	 */
-	*node = of_parse_phandle(np, "sound-dai", 0);
-	if (!*node)
+	node = of_parse_phandle(np, "sound-dai", 0);
+	if (!node)
 		return -ENODEV;
+	*p_node = node;
 
 	/* get dai->name */
-	ret = snd_soc_of_get_dai_name(np, &dai->name);
+	ret = snd_soc_of_get_dai_name(np, name);
 	if (ret < 0)
 		goto parse_error;
 
+	/* parse TDM slot */
+	ret = snd_soc_of_parse_tdm_slot(np, &dai->slots, &dai->slot_width);
+	if (ret)
+		goto parse_error;
+
 	/*
 	 * bitclock-inversion, frame-inversion
 	 * bitclock-master,    frame-master
 	 * and specific "format" if it has
 	 */
 	dai->fmt = snd_soc_of_parse_daifmt(np, NULL);
+	dai->fmt |= daifmt;
 
 	/*
 	 * dai->sysclk come from
@@ -104,7 +139,7 @@ asoc_simple_card_sub_parse_of(struct device_node *np,
 				     "system-clock-frequency",
 				     &dai->sysclk);
 	} else {
-		clk = of_clk_get(*node, 0);
+		clk = of_clk_get(node, 0);
 		if (!IS_ERR(clk))
 			dai->sysclk = clk_get_rate(clk);
 	}
@@ -112,29 +147,38 @@ asoc_simple_card_sub_parse_of(struct device_node *np,
 	ret = 0;
 
 parse_error:
-	of_node_put(*node);
+	of_node_put(node);
 
 	return ret;
 }
 
 static int asoc_simple_card_parse_of(struct device_node *node,
-				     struct asoc_simple_card_info *info,
-				     struct device *dev,
-				     struct device_node **of_cpu,
-				     struct device_node **of_codec,
-				     struct device_node **of_platform)
+				     struct simple_card_data *priv,
+				     struct device *dev)
 {
+	struct snd_soc_dai_link *dai_link = priv->snd_card.dai_link;
 	struct device_node *np;
 	char *name;
 	int ret;
 
+	/* parsing the card name from DT */
+	snd_soc_of_parse_card_name(&priv->snd_card, "simple-audio-card,name");
+
 	/* get CPU/CODEC common format via simple-audio-card,format */
-	info->daifmt = snd_soc_of_parse_daifmt(node, "simple-audio-card,") &
+	priv->daifmt = snd_soc_of_parse_daifmt(node, "simple-audio-card,") &
 		(SND_SOC_DAIFMT_FORMAT_MASK | SND_SOC_DAIFMT_INV_MASK);
 
+	/* off-codec widgets */
+	if (of_property_read_bool(node, "simple-audio-card,widgets")) {
+		ret = snd_soc_of_parse_audio_simple_widgets(&priv->snd_card,
+					"simple-audio-card,widgets");
+		if (ret)
+			return ret;
+	}
+
 	/* DAPM routes */
 	if (of_property_read_bool(node, "simple-audio-card,routing")) {
-		ret = snd_soc_of_parse_audio_routing(&info->snd_card,
+		ret = snd_soc_of_parse_audio_routing(&priv->snd_card,
 					"simple-audio-card,routing");
 		if (ret)
 			return ret;
@@ -144,9 +188,10 @@ static int asoc_simple_card_parse_of(struct device_node *node,
 	ret = -EINVAL;
 	np = of_get_child_by_name(node, "simple-audio-card,cpu");
 	if (np)
-		ret = asoc_simple_card_sub_parse_of(np,
-						  &info->cpu_dai,
-						  of_cpu);
+		ret = asoc_simple_card_sub_parse_of(np, priv->daifmt,
+						  &priv->cpu_dai,
+						  &dai_link->cpu_of_node,
+						  &dai_link->cpu_dai_name);
 	if (ret < 0)
 		return ret;
 
@@ -154,114 +199,126 @@ static int asoc_simple_card_parse_of(struct device_node *node,
 	ret = -EINVAL;
 	np = of_get_child_by_name(node, "simple-audio-card,codec");
 	if (np)
-		ret = asoc_simple_card_sub_parse_of(np,
-						  &info->codec_dai,
-						  of_codec);
+		ret = asoc_simple_card_sub_parse_of(np, priv->daifmt,
+						  &priv->codec_dai,
+						  &dai_link->codec_of_node,
+						  &dai_link->codec_dai_name);
 	if (ret < 0)
 		return ret;
 
-	if (!info->cpu_dai.name || !info->codec_dai.name)
+	if (!dai_link->cpu_dai_name || !dai_link->codec_dai_name)
 		return -EINVAL;
 
 	/* card name is created from CPU/CODEC dai name */
 	name = devm_kzalloc(dev,
-			    strlen(info->cpu_dai.name)   +
-			    strlen(info->codec_dai.name) + 2,
+			    strlen(dai_link->cpu_dai_name)   +
+			    strlen(dai_link->codec_dai_name) + 2,
 			    GFP_KERNEL);
-	sprintf(name, "%s-%s", info->cpu_dai.name, info->codec_dai.name);
-	info->name = info->card = name;
+	sprintf(name, "%s-%s", dai_link->cpu_dai_name,
+				dai_link->codec_dai_name);
+	if (!priv->snd_card.name)
+		priv->snd_card.name = name;
+	dai_link->name = dai_link->stream_name = name;
 
 	/* simple-card assumes platform == cpu */
-	*of_platform = *of_cpu;
+	dai_link->platform_of_node = dai_link->cpu_of_node;
 
-	dev_dbg(dev, "card-name : %s\n", info->card);
-	dev_dbg(dev, "platform : %04x\n", info->daifmt);
+	dev_dbg(dev, "card-name : %s\n", name);
+	dev_dbg(dev, "platform : %04x\n", priv->daifmt);
 	dev_dbg(dev, "cpu : %s / %04x / %d\n",
-		info->cpu_dai.name,
-		info->cpu_dai.fmt,
-		info->cpu_dai.sysclk);
+		dai_link->cpu_dai_name,
+		priv->cpu_dai.fmt,
+		priv->cpu_dai.sysclk);
 	dev_dbg(dev, "codec : %s / %04x / %d\n",
-		info->codec_dai.name,
-		info->codec_dai.fmt,
-		info->codec_dai.sysclk);
+		dai_link->codec_dai_name,
+		priv->codec_dai.fmt,
+		priv->codec_dai.sysclk);
+
+	/*
+	 * soc_bind_dai_link() will check cpu name
+	 * after of_node matching if dai_link has cpu_dai_name.
+	 * but, it will never match if name was created by fmt_single_name()
+	 * remove cpu_dai_name to escape name matching.
+	 * see
+	 *	fmt_single_name()
+	 *	fmt_multiple_name()
+	 */
+	dai_link->cpu_dai_name = NULL;
 
 	return 0;
 }
 
 static int asoc_simple_card_probe(struct platform_device *pdev)
 {
-	struct asoc_simple_card_info *cinfo;
+	struct simple_card_data *priv;
+	struct snd_soc_dai_link *dai_link;
 	struct device_node *np = pdev->dev.of_node;
-	struct device_node *of_cpu, *of_codec, *of_platform;
 	struct device *dev = &pdev->dev;
 	int ret;
 
-	cinfo		= NULL;
-	of_cpu		= NULL;
-	of_codec	= NULL;
-	of_platform	= NULL;
-
-	cinfo = devm_kzalloc(dev, sizeof(*cinfo), GFP_KERNEL);
-	if (!cinfo)
+	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+	if (!priv)
 		return -ENOMEM;
 
+	/*
+	 * init snd_soc_card
+	 */
+	priv->snd_card.owner = THIS_MODULE;
+	priv->snd_card.dev = dev;
+	dai_link = &priv->snd_link;
+	priv->snd_card.dai_link = dai_link;
+	priv->snd_card.num_links = 1;
+
 	if (np && of_device_is_available(np)) {
-		cinfo->snd_card.dev = dev;
 
-		ret = asoc_simple_card_parse_of(np, cinfo, dev,
-						&of_cpu,
-						&of_codec,
-						&of_platform);
+		ret = asoc_simple_card_parse_of(np, priv, dev);
 		if (ret < 0) {
 			if (ret != -EPROBE_DEFER)
 				dev_err(dev, "parse error %d\n", ret);
 			return ret;
 		}
 	} else {
-		if (!dev->platform_data) {
+		struct asoc_simple_card_info *cinfo;
+
+		cinfo = dev->platform_data;
+		if (!cinfo) {
 			dev_err(dev, "no info for asoc-simple-card\n");
 			return -EINVAL;
 		}
 
-		memcpy(cinfo, dev->platform_data, sizeof(*cinfo));
-		cinfo->snd_card.dev = dev;
-	}
+		if (!cinfo->name	||
+		    !cinfo->codec_dai.name	||
+		    !cinfo->codec	||
+		    !cinfo->platform	||
+		    !cinfo->cpu_dai.name) {
+			dev_err(dev, "insufficient asoc_simple_card_info settings\n");
+			return -EINVAL;
+		}
 
-	if (!cinfo->name	||
-	    !cinfo->card	||
-	    !cinfo->codec_dai.name	||
-	    !(cinfo->codec		|| of_codec)	||
-	    !(cinfo->platform		|| of_platform)	||
-	    !(cinfo->cpu_dai.name	|| of_cpu)) {
-		dev_err(dev, "insufficient asoc_simple_card_info settings\n");
-		return -EINVAL;
+		priv->snd_card.name	= (cinfo->card) ? cinfo->card : cinfo->name;
+		dai_link->name		= cinfo->name;
+		dai_link->stream_name	= cinfo->name;
+		dai_link->platform_name	= cinfo->platform;
+		dai_link->codec_name	= cinfo->codec;
+		dai_link->cpu_dai_name	= cinfo->cpu_dai.name;
+		dai_link->codec_dai_name = cinfo->codec_dai.name;
+		memcpy(&priv->cpu_dai, &cinfo->cpu_dai,
+						sizeof(priv->cpu_dai));
+		memcpy(&priv->codec_dai, &cinfo->codec_dai,
+						sizeof(priv->codec_dai));
+
+		priv->cpu_dai.fmt	|= cinfo->daifmt;
+		priv->codec_dai.fmt	|= cinfo->daifmt;
 	}
 
 	/*
 	 * init snd_soc_dai_link
 	 */
-	cinfo->snd_link.name		= cinfo->name;
-	cinfo->snd_link.stream_name	= cinfo->name;
-	cinfo->snd_link.cpu_dai_name	= cinfo->cpu_dai.name;
-	cinfo->snd_link.platform_name	= cinfo->platform;
-	cinfo->snd_link.codec_name	= cinfo->codec;
-	cinfo->snd_link.codec_dai_name	= cinfo->codec_dai.name;
-	cinfo->snd_link.cpu_of_node	= of_cpu;
-	cinfo->snd_link.codec_of_node	= of_codec;
-	cinfo->snd_link.platform_of_node = of_platform;
-	cinfo->snd_link.init		= asoc_simple_card_dai_init;
-
-	/*
-	 * init snd_soc_card
-	 */
-	cinfo->snd_card.name		= cinfo->card;
-	cinfo->snd_card.owner		= THIS_MODULE;
-	cinfo->snd_card.dai_link	= &cinfo->snd_link;
-	cinfo->snd_card.num_links	= 1;
+	dai_link->init = asoc_simple_card_dai_init;
 
-	snd_soc_card_set_drvdata(&cinfo->snd_card, cinfo);
+	snd_soc_card_set_drvdata(&priv->snd_card, priv);
 
-	return devm_snd_soc_register_card(&pdev->dev, &cinfo->snd_card);
+	return devm_snd_soc_register_card(&pdev->dev, &priv->snd_card);
 }
 
 static const struct of_device_id asoc_simple_of_match[] = {
diff --git a/sound/soc/intel/Kconfig b/sound/soc/intel/Kconfig
index 61c10bf503d2..4577b69fcf2c 100644
--- a/sound/soc/intel/Kconfig
+++ b/sound/soc/intel/Kconfig
@@ -2,12 +2,50 @@ config SND_MFLD_MACHINE
 	tristate "SOC Machine Audio driver for Intel Medfield MID platform"
 	depends on INTEL_SCU_IPC
 	select SND_SOC_SN95031
-	select SND_SST_PLATFORM
+	select SND_SST_MFLD_PLATFORM
 	help
           This adds support for ASoC machine driver for Intel(R) MID Medfield platform
           used as alsa device in audio substem in Intel(R) MID devices
           Say Y if you have such a device
           If unsure select "N".
 
-config SND_SST_PLATFORM
+config SND_SST_MFLD_PLATFORM
 	tristate
+
+config SND_SOC_INTEL_SST
+	tristate "ASoC support for Intel(R) Smart Sound Technology"
+	select SND_SOC_INTEL_SST_ACPI if ACPI
+	depends on (X86 || COMPILE_TEST)
+	help
+          This adds support for Intel(R) Smart Sound Technology (SST).
+          Say Y if you have such a device
+          If unsure select "N".
+
+config SND_SOC_INTEL_SST_ACPI
+	tristate
+
+config SND_SOC_INTEL_HASWELL
+	tristate
+
+config SND_SOC_INTEL_BAYTRAIL
+	tristate
+
+config SND_SOC_INTEL_HASWELL_MACH
+	tristate "ASoC Audio DSP support for Intel Haswell Lynxpoint"
+	depends on SND_SOC_INTEL_SST && X86_INTEL_LPSS
+	select SND_SOC_INTEL_HASWELL
+	select SND_SOC_RT5640
+	help
+	  This adds support for the Lynxpoint Audio DSP on Intel(R) Haswell
+	  Ultrabook platforms.
+	  Say Y if you have such a device
+	  If unsure select "N".
+
+config SND_SOC_INTEL_BYT_RT5640_MACH
+	tristate "ASoC Audio driver for Intel Baytrail with RT5640 codec"
+	depends on SND_SOC_INTEL_SST && X86_INTEL_LPSS
+	select SND_SOC_INTEL_BAYTRAIL
+	select SND_SOC_RT5640
+	help
+	  This adds audio driver for Intel Baytrail platform based boards
+	  with the RT5640 audio codec.
diff --git a/sound/soc/intel/Makefile b/sound/soc/intel/Makefile
index 639883339465..edeb79ae3dff 100644
--- a/sound/soc/intel/Makefile
+++ b/sound/soc/intel/Makefile
@@ -1,5 +1,28 @@
-snd-soc-sst-platform-objs := sst_platform.o
+# Core support
+snd-soc-sst-dsp-objs := sst-dsp.o sst-firmware.o
+snd-soc-sst-acpi-objs := sst-acpi.o
+
+snd-soc-sst-mfld-platform-objs := sst-mfld-platform.o
 snd-soc-mfld-machine-objs := mfld_machine.o
 
-obj-$(CONFIG_SND_SST_PLATFORM) += snd-soc-sst-platform.o
+obj-$(CONFIG_SND_SST_MFLD_PLATFORM) += snd-soc-sst-mfld-platform.o
 obj-$(CONFIG_SND_MFLD_MACHINE) += snd-soc-mfld-machine.o
+
+obj-$(CONFIG_SND_SOC_INTEL_SST) += snd-soc-sst-dsp.o
+obj-$(CONFIG_SND_SOC_INTEL_SST_ACPI) += snd-soc-sst-acpi.o
+
+# Platform Support
+snd-soc-sst-haswell-pcm-objs := \
+	sst-haswell-ipc.o sst-haswell-pcm.o sst-haswell-dsp.o
+snd-soc-sst-baytrail-pcm-objs := \
+	sst-baytrail-ipc.o sst-baytrail-pcm.o sst-baytrail-dsp.o
+
+obj-$(CONFIG_SND_SOC_INTEL_HASWELL) += snd-soc-sst-haswell-pcm.o
+obj-$(CONFIG_SND_SOC_INTEL_BAYTRAIL) += snd-soc-sst-baytrail-pcm.o
+
+# Machine support
+snd-soc-sst-haswell-objs := haswell.o
+snd-soc-sst-byt-rt5640-mach-objs := byt-rt5640.o
+
+obj-$(CONFIG_SND_SOC_INTEL_HASWELL_MACH) += snd-soc-sst-haswell.o
+obj-$(CONFIG_SND_SOC_INTEL_BYT_RT5640_MACH) += snd-soc-sst-byt-rt5640-mach.o
diff --git a/sound/soc/intel/byt-rt5640.c b/sound/soc/intel/byt-rt5640.c
new file mode 100644
index 000000000000..eff97c8e5218
--- /dev/null
+++ b/sound/soc/intel/byt-rt5640.c
@@ -0,0 +1,187 @@
+/*
+ * Intel Baytrail SST RT5640 machine driver
+ * Copyright (c) 2014, Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/acpi.h>
+#include <linux/device.h>
+#include <linux/slab.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/jack.h>
+#include "../codecs/rt5640.h"
+
+#include "sst-dsp.h"
+
+static const struct snd_soc_dapm_widget byt_rt5640_widgets[] = {
+	SND_SOC_DAPM_HP("Headphone", NULL),
+	SND_SOC_DAPM_MIC("Headset Mic", NULL),
+	SND_SOC_DAPM_MIC("Internal Mic", NULL),
+	SND_SOC_DAPM_SPK("Speaker", NULL),
+};
+
+static const struct snd_soc_dapm_route byt_rt5640_audio_map[] = {
+	{"IN2P", NULL, "Headset Mic"},
+	{"IN2N", NULL, "Headset Mic"},
+	{"DMIC1", NULL, "Internal Mic"},
+	{"Headphone", NULL, "HPOL"},
+	{"Headphone", NULL, "HPOR"},
+	{"Speaker", NULL, "SPOLP"},
+	{"Speaker", NULL, "SPOLN"},
+	{"Speaker", NULL, "SPORP"},
+	{"Speaker", NULL, "SPORN"},
+};
+
+static const struct snd_kcontrol_new byt_rt5640_controls[] = {
+	SOC_DAPM_PIN_SWITCH("Headphone"),
+	SOC_DAPM_PIN_SWITCH("Headset Mic"),
+	SOC_DAPM_PIN_SWITCH("Internal Mic"),
+	SOC_DAPM_PIN_SWITCH("Speaker"),
+};
+
+static int byt_rt5640_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;
+	int ret;
+
+	ret = snd_soc_dai_set_sysclk(codec_dai, RT5640_SCLK_S_PLL1,
+				     params_rate(params) * 256,
+				     SND_SOC_CLOCK_IN);
+	if (ret < 0) {
+		dev_err(codec_dai->dev, "can't set codec clock %d\n", ret);
+		return ret;
+	}
+	ret = snd_soc_dai_set_pll(codec_dai, 0, RT5640_PLL1_S_BCLK1,
+				  params_rate(params) * 64,
+				  params_rate(params) * 256);
+	if (ret < 0) {
+		dev_err(codec_dai->dev, "can't set codec pll: %d\n", ret);
+		return ret;
+	}
+	return 0;
+}
+
+static int byt_rt5640_init(struct snd_soc_pcm_runtime *runtime)
+{
+	int ret;
+	struct snd_soc_codec *codec = runtime->codec;
+	struct snd_soc_dapm_context *dapm = &codec->dapm;
+	struct snd_soc_card *card = runtime->card;
+
+	card->dapm.idle_bias_off = true;
+
+	ret = snd_soc_add_card_controls(card, byt_rt5640_controls,
+					ARRAY_SIZE(byt_rt5640_controls));
+	if (ret) {
+		dev_err(card->dev, "unable to add card controls\n");
+		return ret;
+	}
+
+	snd_soc_dapm_ignore_suspend(dapm, "HPOL");
+	snd_soc_dapm_ignore_suspend(dapm, "HPOR");
+
+	snd_soc_dapm_ignore_suspend(dapm, "SPOLP");
+	snd_soc_dapm_ignore_suspend(dapm, "SPOLN");
+	snd_soc_dapm_ignore_suspend(dapm, "SPORP");
+	snd_soc_dapm_ignore_suspend(dapm, "SPORN");
+
+	snd_soc_dapm_enable_pin(dapm, "Headset Mic");
+	snd_soc_dapm_enable_pin(dapm, "Headphone");
+	snd_soc_dapm_enable_pin(dapm, "Speaker");
+	snd_soc_dapm_enable_pin(dapm, "Internal Mic");
+
+	snd_soc_dapm_sync(dapm);
+	return ret;
+}
+
+static struct snd_soc_ops byt_rt5640_ops = {
+	.hw_params = byt_rt5640_hw_params,
+};
+
+static struct snd_soc_dai_link byt_rt5640_dais[] = {
+	{
+		.name = "Baytrail Audio",
+		.stream_name = "Audio",
+		.cpu_dai_name = "Front-cpu-dai",
+		.codec_dai_name = "rt5640-aif1",
+		.codec_name = "i2c-10EC5640:00",
+		.platform_name = "baytrail-pcm-audio",
+		.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
+			   SND_SOC_DAIFMT_CBS_CFS,
+		.init = byt_rt5640_init,
+		.ignore_suspend = 1,
+		.ops = &byt_rt5640_ops,
+	},
+	{
+		.name = "Baytrail Voice",
+		.stream_name = "Voice",
+		.cpu_dai_name = "Mic1-cpu-dai",
+		.codec_dai_name = "rt5640-aif1",
+		.codec_name = "i2c-10EC5640:00",
+		.platform_name = "baytrail-pcm-audio",
+		.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
+			   SND_SOC_DAIFMT_CBS_CFS,
+		.init = NULL,
+		.ignore_suspend = 1,
+		.ops = &byt_rt5640_ops,
+	},
+};
+
+static struct snd_soc_card byt_rt5640_card = {
+	.name = "byt-rt5640",
+	.dai_link = byt_rt5640_dais,
+	.num_links = ARRAY_SIZE(byt_rt5640_dais),
+	.dapm_widgets = byt_rt5640_widgets,
+	.num_dapm_widgets = ARRAY_SIZE(byt_rt5640_widgets),
+	.dapm_routes = byt_rt5640_audio_map,
+	.num_dapm_routes = ARRAY_SIZE(byt_rt5640_audio_map),
+};
+
+static int byt_rt5640_probe(struct platform_device *pdev)
+{
+	struct snd_soc_card *card = &byt_rt5640_card;
+	struct device *dev = &pdev->dev;
+
+	card->dev = &pdev->dev;
+	dev_set_drvdata(dev, card);
+	return snd_soc_register_card(card);
+}
+
+static int byt_rt5640_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 byt_rt5640_audio = {
+	.probe = byt_rt5640_probe,
+	.remove = byt_rt5640_remove,
+	.driver = {
+		.name = "byt-rt5640",
+		.owner = THIS_MODULE,
+	},
+};
+module_platform_driver(byt_rt5640_audio)
+
+MODULE_DESCRIPTION("ASoC Intel(R) Baytrail Machine driver");
+MODULE_AUTHOR("Omair Md Abdullah, Jarkko Nikula");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:byt-rt5640");
diff --git a/sound/soc/intel/haswell.c b/sound/soc/intel/haswell.c
new file mode 100644
index 000000000000..54345a2a7386
--- /dev/null
+++ b/sound/soc/intel/haswell.c
@@ -0,0 +1,235 @@
+/*
+ * Intel Haswell Lynxpoint SST Audio
+ *
+ * Copyright (C) 2013, Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License version
+ * 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+#include <sound/pcm_params.h>
+
+#include "sst-dsp.h"
+#include "sst-haswell-ipc.h"
+
+#include "../codecs/rt5640.h"
+
+/* Haswell ULT platforms have a Headphone and Mic jack */
+static const struct snd_soc_dapm_widget haswell_widgets[] = {
+	SND_SOC_DAPM_HP("Headphones", NULL),
+	SND_SOC_DAPM_MIC("Mic", NULL),
+};
+
+static const struct snd_soc_dapm_route haswell_rt5640_map[] = {
+
+	{"Headphones", NULL, "HPOR"},
+	{"Headphones", NULL, "HPOL"},
+	{"IN2P", NULL, "Mic"},
+
+	/* CODEC BE connections */
+	{"SSP0 CODEC IN", NULL, "AIF1 Capture"},
+	{"AIF1 Playback", NULL, "SSP0 CODEC OUT"},
+};
+
+static int haswell_ssp0_fixup(struct snd_soc_pcm_runtime *rtd,
+			struct snd_pcm_hw_params *params)
+{
+	struct snd_interval *rate = hw_param_interval(params,
+			SNDRV_PCM_HW_PARAM_RATE);
+	struct snd_interval *channels = hw_param_interval(params,
+						SNDRV_PCM_HW_PARAM_CHANNELS);
+
+	/* The ADSP will covert the FE rate to 48k, stereo */
+	rate->min = rate->max = 48000;
+	channels->min = channels->max = 2;
+
+	/* set SSP0 to 16 bit */
+	snd_mask_set(&params->masks[SNDRV_PCM_HW_PARAM_FORMAT -
+				    SNDRV_PCM_HW_PARAM_FIRST_MASK],
+				    SNDRV_PCM_FORMAT_S16_LE);
+	return 0;
+}
+
+static int haswell_rt5640_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;
+	int ret;
+
+	ret = snd_soc_dai_set_sysclk(codec_dai, RT5640_SCLK_S_MCLK, 12288000,
+		SND_SOC_CLOCK_IN);
+
+	if (ret < 0) {
+		dev_err(rtd->dev, "can't set codec sysclk configuration\n");
+		return ret;
+	}
+
+	/* set correct codec filter for DAI format and clock config */
+	snd_soc_update_bits(rtd->codec, 0x83, 0xffff, 0x8000);
+
+	return ret;
+}
+
+static struct snd_soc_ops haswell_rt5640_ops = {
+	.hw_params = haswell_rt5640_hw_params,
+};
+
+static int haswell_rtd_init(struct snd_soc_pcm_runtime *rtd)
+{
+	struct snd_soc_codec *codec = rtd->codec;
+	struct snd_soc_dapm_context *dapm = &codec->dapm;
+	struct sst_pdata *pdata = dev_get_platdata(rtd->platform->dev);
+	struct sst_hsw *haswell = pdata->dsp;
+	int ret;
+
+	/* Set ADSP SSP port settings */
+	ret = sst_hsw_device_set_config(haswell, SST_HSW_DEVICE_SSP_0,
+		SST_HSW_DEVICE_MCLK_FREQ_24_MHZ,
+		SST_HSW_DEVICE_CLOCK_MASTER, 9);
+	if (ret < 0) {
+		dev_err(rtd->dev, "failed to set device config\n");
+		return ret;
+	}
+
+	/* always connected */
+	snd_soc_dapm_enable_pin(dapm, "Headphones");
+	snd_soc_dapm_enable_pin(dapm, "Mic");
+
+	return 0;
+}
+
+static struct snd_soc_dai_link haswell_rt5640_dais[] = {
+	/* Front End DAI links */
+	{
+		.name = "System",
+		.stream_name = "System Playback",
+		.cpu_dai_name = "System Pin",
+		.platform_name = "haswell-pcm-audio",
+		.dynamic = 1,
+		.codec_name = "snd-soc-dummy",
+		.codec_dai_name = "snd-soc-dummy-dai",
+		.init = haswell_rtd_init,
+		.trigger = {SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST},
+		.dpcm_playback = 1,
+	},
+	{
+		.name = "Offload0",
+		.stream_name = "Offload0 Playback",
+		.cpu_dai_name = "Offload0 Pin",
+		.platform_name = "haswell-pcm-audio",
+		.dynamic = 1,
+		.codec_name = "snd-soc-dummy",
+		.codec_dai_name = "snd-soc-dummy-dai",
+		.trigger = {SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST},
+		.dpcm_playback = 1,
+	},
+	{
+		.name = "Offload1",
+		.stream_name = "Offload1 Playback",
+		.cpu_dai_name = "Offload1 Pin",
+		.platform_name = "haswell-pcm-audio",
+		.dynamic = 1,
+		.codec_name = "snd-soc-dummy",
+		.codec_dai_name = "snd-soc-dummy-dai",
+		.trigger = {SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST},
+		.dpcm_playback = 1,
+	},
+	{
+		.name = "Loopback",
+		.stream_name = "Loopback",
+		.cpu_dai_name = "Loopback Pin",
+		.platform_name = "haswell-pcm-audio",
+		.dynamic = 0,
+		.codec_name = "snd-soc-dummy",
+		.codec_dai_name = "snd-soc-dummy-dai",
+		.trigger = {SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST},
+		.dpcm_capture = 1,
+	},
+	{
+		.name = "Capture",
+		.stream_name = "Capture",
+		.cpu_dai_name = "Capture Pin",
+		.platform_name = "haswell-pcm-audio",
+		.dynamic = 1,
+		.codec_name = "snd-soc-dummy",
+		.codec_dai_name = "snd-soc-dummy-dai",
+		.trigger = {SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST},
+		.dpcm_capture = 1,
+	},
+
+	/* Back End DAI links */
+	{
+		/* SSP0 - Codec */
+		.name = "Codec",
+		.be_id = 0,
+		.cpu_dai_name = "snd-soc-dummy-dai",
+		.platform_name = "snd-soc-dummy",
+		.no_pcm = 1,
+		.codec_name = "i2c-INT33CA:00",
+		.codec_dai_name = "rt5640-aif1",
+		.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
+			SND_SOC_DAIFMT_CBS_CFS,
+		.ignore_suspend = 1,
+		.ignore_pmdown_time = 1,
+		.be_hw_params_fixup = haswell_ssp0_fixup,
+		.ops = &haswell_rt5640_ops,
+		.dpcm_playback = 1,
+		.dpcm_capture = 1,
+	},
+};
+
+/* audio machine driver for Haswell Lynxpoint DSP + RT5640 */
+static struct snd_soc_card haswell_rt5640 = {
+	.name = "haswell-rt5640",
+	.owner = THIS_MODULE,
+	.dai_link = haswell_rt5640_dais,
+	.num_links = ARRAY_SIZE(haswell_rt5640_dais),
+	.dapm_widgets = haswell_widgets,
+	.num_dapm_widgets = ARRAY_SIZE(haswell_widgets),
+	.dapm_routes = haswell_rt5640_map,
+	.num_dapm_routes = ARRAY_SIZE(haswell_rt5640_map),
+	.fully_routed = true,
+};
+
+static int haswell_audio_probe(struct platform_device *pdev)
+{
+	haswell_rt5640.dev = &pdev->dev;
+
+	return snd_soc_register_card(&haswell_rt5640);
+}
+
+static int haswell_audio_remove(struct platform_device *pdev)
+{
+	snd_soc_unregister_card(&haswell_rt5640);
+	return 0;
+}
+
+static struct platform_driver haswell_audio = {
+	.probe = haswell_audio_probe,
+	.remove = haswell_audio_remove,
+	.driver = {
+		.name = "haswell-audio",
+		.owner = THIS_MODULE,
+	},
+};
+
+module_platform_driver(haswell_audio)
+
+/* Module information */
+MODULE_AUTHOR("Liam Girdwood, Xingchao Wang");
+MODULE_DESCRIPTION("Intel SST Audio for Haswell Lynxpoint");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:haswell-audio");
diff --git a/sound/soc/intel/mfld_machine.c b/sound/soc/intel/mfld_machine.c
index d3d4c32434f7..0cef32e9d402 100644
--- a/sound/soc/intel/mfld_machine.c
+++ b/sound/soc/intel/mfld_machine.c
@@ -101,20 +101,27 @@ static int headset_set_switch(struct snd_kcontrol *kcontrol,
 	struct snd_ctl_elem_value *ucontrol)
 {
 	struct snd_soc_codec *codec =  snd_kcontrol_chip(kcontrol);
+	struct snd_soc_dapm_context *dapm = &codec->dapm;
 
 	if (ucontrol->value.integer.value[0] == hs_switch)
 		return 0;
 
+	snd_soc_dapm_mutex_lock(dapm);
+
 	if (ucontrol->value.integer.value[0]) {
 		pr_debug("hs_set HS path\n");
-		snd_soc_dapm_enable_pin(&codec->dapm, "Headphones");
-		snd_soc_dapm_disable_pin(&codec->dapm, "EPOUT");
+		snd_soc_dapm_enable_pin_unlocked(dapm, "Headphones");
+		snd_soc_dapm_disable_pin_unlocked(dapm, "EPOUT");
 	} else {
 		pr_debug("hs_set EP path\n");
-		snd_soc_dapm_disable_pin(&codec->dapm, "Headphones");
-		snd_soc_dapm_enable_pin(&codec->dapm, "EPOUT");
+		snd_soc_dapm_disable_pin_unlocked(dapm, "Headphones");
+		snd_soc_dapm_enable_pin_unlocked(dapm, "EPOUT");
 	}
-	snd_soc_dapm_sync(&codec->dapm);
+
+	snd_soc_dapm_sync_unlocked(dapm);
+
+	snd_soc_dapm_mutex_unlock(dapm);
+
 	hs_switch = ucontrol->value.integer.value[0];
 
 	return 0;
@@ -122,18 +129,20 @@ static int headset_set_switch(struct snd_kcontrol *kcontrol,
 
 static void lo_enable_out_pins(struct snd_soc_codec *codec)
 {
-	snd_soc_dapm_enable_pin(&codec->dapm, "IHFOUTL");
-	snd_soc_dapm_enable_pin(&codec->dapm, "IHFOUTR");
-	snd_soc_dapm_enable_pin(&codec->dapm, "LINEOUTL");
-	snd_soc_dapm_enable_pin(&codec->dapm, "LINEOUTR");
-	snd_soc_dapm_enable_pin(&codec->dapm, "VIB1OUT");
-	snd_soc_dapm_enable_pin(&codec->dapm, "VIB2OUT");
+	struct snd_soc_dapm_context *dapm = &codec->dapm;
+
+	snd_soc_dapm_enable_pin_unlocked(dapm, "IHFOUTL");
+	snd_soc_dapm_enable_pin_unlocked(dapm, "IHFOUTR");
+	snd_soc_dapm_enable_pin_unlocked(dapm, "LINEOUTL");
+	snd_soc_dapm_enable_pin_unlocked(dapm, "LINEOUTR");
+	snd_soc_dapm_enable_pin_unlocked(dapm, "VIB1OUT");
+	snd_soc_dapm_enable_pin_unlocked(dapm, "VIB2OUT");
 	if (hs_switch) {
-		snd_soc_dapm_enable_pin(&codec->dapm, "Headphones");
-		snd_soc_dapm_disable_pin(&codec->dapm, "EPOUT");
+		snd_soc_dapm_enable_pin_unlocked(dapm, "Headphones");
+		snd_soc_dapm_disable_pin_unlocked(dapm, "EPOUT");
 	} else {
-		snd_soc_dapm_disable_pin(&codec->dapm, "Headphones");
-		snd_soc_dapm_enable_pin(&codec->dapm, "EPOUT");
+		snd_soc_dapm_disable_pin_unlocked(dapm, "Headphones");
+		snd_soc_dapm_enable_pin_unlocked(dapm, "EPOUT");
 	}
 }
 
@@ -148,44 +157,52 @@ static int lo_set_switch(struct snd_kcontrol *kcontrol,
 	struct snd_ctl_elem_value *ucontrol)
 {
 	struct snd_soc_codec *codec =  snd_kcontrol_chip(kcontrol);
+	struct snd_soc_dapm_context *dapm = &codec->dapm;
 
 	if (ucontrol->value.integer.value[0] == lo_dac)
 		return 0;
 
+	snd_soc_dapm_mutex_lock(dapm);
+
 	/* we dont want to work with last state of lineout so just enable all
 	 * pins and then disable pins not required
 	 */
 	lo_enable_out_pins(codec);
+
 	switch (ucontrol->value.integer.value[0]) {
 	case 0:
 		pr_debug("set vibra path\n");
-		snd_soc_dapm_disable_pin(&codec->dapm, "VIB1OUT");
-		snd_soc_dapm_disable_pin(&codec->dapm, "VIB2OUT");
+		snd_soc_dapm_disable_pin_unlocked(dapm, "VIB1OUT");
+		snd_soc_dapm_disable_pin_unlocked(dapm, "VIB2OUT");
 		snd_soc_update_bits(codec, SN95031_LOCTL, 0x66, 0);
 		break;
 
 	case 1:
 		pr_debug("set hs  path\n");
-		snd_soc_dapm_disable_pin(&codec->dapm, "Headphones");
-		snd_soc_dapm_disable_pin(&codec->dapm, "EPOUT");
+		snd_soc_dapm_disable_pin_unlocked(dapm, "Headphones");
+		snd_soc_dapm_disable_pin_unlocked(dapm, "EPOUT");
 		snd_soc_update_bits(codec, SN95031_LOCTL, 0x66, 0x22);
 		break;
 
 	case 2:
 		pr_debug("set spkr path\n");
-		snd_soc_dapm_disable_pin(&codec->dapm, "IHFOUTL");
-		snd_soc_dapm_disable_pin(&codec->dapm, "IHFOUTR");
+		snd_soc_dapm_disable_pin_unlocked(dapm, "IHFOUTL");
+		snd_soc_dapm_disable_pin_unlocked(dapm, "IHFOUTR");
 		snd_soc_update_bits(codec, SN95031_LOCTL, 0x66, 0x44);
 		break;
 
 	case 3:
 		pr_debug("set null path\n");
-		snd_soc_dapm_disable_pin(&codec->dapm, "LINEOUTL");
-		snd_soc_dapm_disable_pin(&codec->dapm, "LINEOUTR");
+		snd_soc_dapm_disable_pin_unlocked(dapm, "LINEOUTL");
+		snd_soc_dapm_disable_pin_unlocked(dapm, "LINEOUTR");
 		snd_soc_update_bits(codec, SN95031_LOCTL, 0x66, 0x66);
 		break;
 	}
-	snd_soc_dapm_sync(&codec->dapm);
+
+	snd_soc_dapm_sync_unlocked(dapm);
+
+	snd_soc_dapm_mutex_unlock(dapm);
+
 	lo_dac = ucontrol->value.integer.value[0];
 	return 0;
 }
diff --git a/sound/soc/intel/sst-acpi.c b/sound/soc/intel/sst-acpi.c
new file mode 100644
index 000000000000..5d06eecb6198
--- /dev/null
+++ b/sound/soc/intel/sst-acpi.c
@@ -0,0 +1,284 @@
+/*
+ * Intel SST loader on ACPI systems
+ *
+ * Copyright (C) 2013, Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License version
+ * 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/acpi.h>
+#include <linux/device.h>
+#include <linux/firmware.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+
+#include "sst-dsp.h"
+
+#define SST_LPT_DSP_DMA_ADDR_OFFSET	0x0F0000
+#define SST_WPT_DSP_DMA_ADDR_OFFSET	0x0FE000
+#define SST_LPT_DSP_DMA_SIZE		(1024 - 1)
+
+/* Descriptor for SST ASoC machine driver */
+struct sst_acpi_mach {
+	/* ACPI ID for the matching machine driver. Audio codec for instance */
+	const u8 id[ACPI_ID_LEN];
+	/* machine driver name */
+	const char *drv_name;
+	/* firmware file name */
+	const char *fw_filename;
+};
+
+/* Descriptor for setting up SST platform data */
+struct sst_acpi_desc {
+	const char *drv_name;
+	struct sst_acpi_mach *machines;
+	/* Platform resource indexes. Must set to -1 if not used */
+	int resindex_lpe_base;
+	int resindex_pcicfg_base;
+	int resindex_fw_base;
+	int irqindex_host_ipc;
+	int resindex_dma_base;
+	/* Unique number identifying the SST core on platform */
+	int sst_id;
+	/* DMA only valid when resindex_dma_base != -1*/
+	int dma_engine;
+	int dma_size;
+};
+
+struct sst_acpi_priv {
+	struct platform_device *pdev_mach;
+	struct platform_device *pdev_pcm;
+	struct sst_pdata sst_pdata;
+	struct sst_acpi_desc *desc;
+	struct sst_acpi_mach *mach;
+};
+
+static void sst_acpi_fw_cb(const struct firmware *fw, void *context)
+{
+	struct platform_device *pdev = context;
+	struct device *dev = &pdev->dev;
+	struct sst_acpi_priv *sst_acpi = platform_get_drvdata(pdev);
+	struct sst_pdata *sst_pdata = &sst_acpi->sst_pdata;
+	struct sst_acpi_desc *desc = sst_acpi->desc;
+	struct sst_acpi_mach *mach = sst_acpi->mach;
+
+	sst_pdata->fw = fw;
+	if (!fw) {
+		dev_err(dev, "Cannot load firmware %s\n", mach->fw_filename);
+		return;
+	}
+
+	/* register PCM and DAI driver */
+	sst_acpi->pdev_pcm =
+		platform_device_register_data(dev, desc->drv_name, -1,
+					      sst_pdata, sizeof(*sst_pdata));
+	if (IS_ERR(sst_acpi->pdev_pcm)) {
+		dev_err(dev, "Cannot register device %s. Error %d\n",
+			desc->drv_name, (int)PTR_ERR(sst_acpi->pdev_pcm));
+	}
+
+	return;
+}
+
+static acpi_status sst_acpi_mach_match(acpi_handle handle, u32 level,
+				       void *context, void **ret)
+{
+	*(bool *)context = true;
+	return AE_OK;
+}
+
+static struct sst_acpi_mach *sst_acpi_find_machine(
+	struct sst_acpi_mach *machines)
+{
+	struct sst_acpi_mach *mach;
+	bool found = false;
+
+	for (mach = machines; mach->id[0]; mach++)
+		if (ACPI_SUCCESS(acpi_get_devices(mach->id,
+						  sst_acpi_mach_match,
+						  &found, NULL)) && found)
+			return mach;
+
+	return NULL;
+}
+
+static int sst_acpi_probe(struct platform_device *pdev)
+{
+	const struct acpi_device_id *id;
+	struct device *dev = &pdev->dev;
+	struct sst_acpi_priv *sst_acpi;
+	struct sst_pdata *sst_pdata;
+	struct sst_acpi_mach *mach;
+	struct sst_acpi_desc *desc;
+	struct resource *mmio;
+	int ret = 0;
+
+	sst_acpi = devm_kzalloc(dev, sizeof(*sst_acpi), GFP_KERNEL);
+	if (sst_acpi == NULL)
+		return -ENOMEM;
+
+	id = acpi_match_device(dev->driver->acpi_match_table, dev);
+	if (!id)
+		return -ENODEV;
+
+	desc = (struct sst_acpi_desc *)id->driver_data;
+	mach = sst_acpi_find_machine(desc->machines);
+	if (mach == NULL) {
+		dev_err(dev, "No matching ASoC machine driver found\n");
+		return -ENODEV;
+	}
+
+	sst_pdata = &sst_acpi->sst_pdata;
+	sst_pdata->id = desc->sst_id;
+	sst_acpi->desc = desc;
+	sst_acpi->mach = mach;
+
+	if (desc->resindex_dma_base >= 0) {
+		sst_pdata->dma_engine = desc->dma_engine;
+		sst_pdata->dma_base = desc->resindex_dma_base;
+		sst_pdata->dma_size = desc->dma_size;
+	}
+
+	if (desc->irqindex_host_ipc >= 0)
+		sst_pdata->irq = platform_get_irq(pdev, desc->irqindex_host_ipc);
+
+	if (desc->resindex_lpe_base >= 0) {
+		mmio = platform_get_resource(pdev, IORESOURCE_MEM,
+					     desc->resindex_lpe_base);
+		if (mmio) {
+			sst_pdata->lpe_base = mmio->start;
+			sst_pdata->lpe_size = resource_size(mmio);
+		}
+	}
+
+	if (desc->resindex_pcicfg_base >= 0) {
+		mmio = platform_get_resource(pdev, IORESOURCE_MEM,
+					     desc->resindex_pcicfg_base);
+		if (mmio) {
+			sst_pdata->pcicfg_base = mmio->start;
+			sst_pdata->pcicfg_size = resource_size(mmio);
+		}
+	}
+
+	if (desc->resindex_fw_base >= 0) {
+		mmio = platform_get_resource(pdev, IORESOURCE_MEM,
+					     desc->resindex_fw_base);
+		if (mmio) {
+			sst_pdata->fw_base = mmio->start;
+			sst_pdata->fw_size = resource_size(mmio);
+		}
+	}
+
+	platform_set_drvdata(pdev, sst_acpi);
+
+	/* register machine driver */
+	sst_acpi->pdev_mach =
+		platform_device_register_data(dev, mach->drv_name, -1,
+					      sst_pdata, sizeof(*sst_pdata));
+	if (IS_ERR(sst_acpi->pdev_mach))
+		return PTR_ERR(sst_acpi->pdev_mach);
+
+	/* continue SST probing after firmware is loaded */
+	ret = request_firmware_nowait(THIS_MODULE, true, mach->fw_filename,
+				      dev, GFP_KERNEL, pdev, sst_acpi_fw_cb);
+	if (ret)
+		platform_device_unregister(sst_acpi->pdev_mach);
+
+	return ret;
+}
+
+static int sst_acpi_remove(struct platform_device *pdev)
+{
+	struct sst_acpi_priv *sst_acpi = platform_get_drvdata(pdev);
+	struct sst_pdata *sst_pdata = &sst_acpi->sst_pdata;
+
+	platform_device_unregister(sst_acpi->pdev_mach);
+	if (!IS_ERR_OR_NULL(sst_acpi->pdev_pcm))
+		platform_device_unregister(sst_acpi->pdev_pcm);
+	release_firmware(sst_pdata->fw);
+
+	return 0;
+}
+
+static struct sst_acpi_mach haswell_machines[] = {
+	{ "INT33CA", "haswell-audio", "intel/IntcSST1.bin" },
+	{}
+};
+
+static struct sst_acpi_desc sst_acpi_haswell_desc = {
+	.drv_name = "haswell-pcm-audio",
+	.machines = haswell_machines,
+	.resindex_lpe_base = 0,
+	.resindex_pcicfg_base = 1,
+	.resindex_fw_base = -1,
+	.irqindex_host_ipc = 0,
+	.sst_id = SST_DEV_ID_LYNX_POINT,
+	.dma_engine = SST_DMA_TYPE_DW,
+	.resindex_dma_base = SST_LPT_DSP_DMA_ADDR_OFFSET,
+	.dma_size = SST_LPT_DSP_DMA_SIZE,
+};
+
+static struct sst_acpi_mach broadwell_machines[] = {
+	{ "INT343A", "broadwell-audio", "intel/IntcSST2.bin" },
+	{}
+};
+
+static struct sst_acpi_desc sst_acpi_broadwell_desc = {
+	.drv_name = "haswell-pcm-audio",
+	.machines = broadwell_machines,
+	.resindex_lpe_base = 0,
+	.resindex_pcicfg_base = 1,
+	.resindex_fw_base = -1,
+	.irqindex_host_ipc = 0,
+	.sst_id = SST_DEV_ID_WILDCAT_POINT,
+	.dma_engine = SST_DMA_TYPE_DW,
+	.resindex_dma_base = SST_WPT_DSP_DMA_ADDR_OFFSET,
+	.dma_size = SST_LPT_DSP_DMA_SIZE,
+};
+
+static struct sst_acpi_mach baytrail_machines[] = {
+	{ "10EC5640", "byt-rt5640", "intel/fw_sst_0f28.bin-i2s_master" },
+	{}
+};
+
+static struct sst_acpi_desc sst_acpi_baytrail_desc = {
+	.drv_name = "baytrail-pcm-audio",
+	.machines = baytrail_machines,
+	.resindex_lpe_base = 0,
+	.resindex_pcicfg_base = 1,
+	.resindex_fw_base = 2,
+	.irqindex_host_ipc = 5,
+	.sst_id = SST_DEV_ID_BYT,
+	.resindex_dma_base = -1,
+};
+
+static struct acpi_device_id sst_acpi_match[] = {
+	{ "INT33C8", (unsigned long)&sst_acpi_haswell_desc },
+	{ "INT3438", (unsigned long)&sst_acpi_broadwell_desc },
+	{ "80860F28", (unsigned long)&sst_acpi_baytrail_desc },
+	{ }
+};
+MODULE_DEVICE_TABLE(acpi, sst_acpi_match);
+
+static struct platform_driver sst_acpi_driver = {
+	.probe = sst_acpi_probe,
+	.remove = sst_acpi_remove,
+	.driver = {
+		.name = "sst-acpi",
+		.owner = THIS_MODULE,
+		.acpi_match_table = ACPI_PTR(sst_acpi_match),
+	},
+};
+module_platform_driver(sst_acpi_driver);
+
+MODULE_AUTHOR("Jarkko Nikula <jarkko.nikula@linux.intel.com>");
+MODULE_DESCRIPTION("Intel SST loader on ACPI systems");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/intel/sst-baytrail-dsp.c b/sound/soc/intel/sst-baytrail-dsp.c
new file mode 100644
index 000000000000..a50bf7fc0e3a
--- /dev/null
+++ b/sound/soc/intel/sst-baytrail-dsp.c
@@ -0,0 +1,372 @@
+/*
+ * Intel Baytrail SST DSP driver
+ * Copyright (c) 2014, Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ */
+
+#include <linux/delay.h>
+#include <linux/fs.h>
+#include <linux/slab.h>
+#include <linux/device.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/dma-mapping.h>
+#include <linux/platform_device.h>
+#include <linux/firmware.h>
+
+#include "sst-dsp.h"
+#include "sst-dsp-priv.h"
+#include "sst-baytrail-ipc.h"
+
+#define SST_BYT_FW_SIGNATURE_SIZE	4
+#define SST_BYT_FW_SIGN			"$SST"
+
+#define SST_BYT_IRAM_OFFSET	0xC0000
+#define SST_BYT_DRAM_OFFSET	0x100000
+#define SST_BYT_SHIM_OFFSET	0x140000
+
+enum sst_ram_type {
+	SST_BYT_IRAM	= 1,
+	SST_BYT_DRAM	= 2,
+	SST_BYT_CACHE	= 3,
+};
+
+struct dma_block_info {
+	enum sst_ram_type	type;	/* IRAM/DRAM */
+	u32			size;	/* Bytes */
+	u32			ram_offset; /* Offset in I/DRAM */
+	u32			rsvd;	/* Reserved field */
+};
+
+struct fw_header {
+	unsigned char signature[SST_BYT_FW_SIGNATURE_SIZE];
+	u32 file_size; /* size of fw minus this header */
+	u32 modules; /*  # of modules */
+	u32 file_format; /* version of header format */
+	u32 reserved[4];
+};
+
+struct sst_byt_fw_module_header {
+	unsigned char signature[SST_BYT_FW_SIGNATURE_SIZE];
+	u32 mod_size; /* size of module */
+	u32 blocks; /* # of blocks */
+	u32 type; /* codec type, pp lib */
+	u32 entry_point;
+};
+
+static int sst_byt_parse_module(struct sst_dsp *dsp, struct sst_fw *fw,
+				struct sst_byt_fw_module_header *module)
+{
+	struct dma_block_info *block;
+	struct sst_module *mod;
+	struct sst_module_data block_data;
+	struct sst_module_template template;
+	int count;
+
+	memset(&template, 0, sizeof(template));
+	template.id = module->type;
+	template.entry = module->entry_point;
+	template.p.type = SST_MEM_DRAM;
+	template.p.data_type = SST_DATA_P;
+	template.s.type = SST_MEM_DRAM;
+	template.s.data_type = SST_DATA_S;
+
+	mod = sst_module_new(fw, &template, NULL);
+	if (mod == NULL)
+		return -ENOMEM;
+
+	block = (void *)module + sizeof(*module);
+
+	for (count = 0; count < module->blocks; count++) {
+
+		if (block->size <= 0) {
+			dev_err(dsp->dev, "block %d size invalid\n", count);
+			return -EINVAL;
+		}
+
+		switch (block->type) {
+		case SST_BYT_IRAM:
+			block_data.offset = block->ram_offset +
+					    dsp->addr.iram_offset;
+			block_data.type = SST_MEM_IRAM;
+			break;
+		case SST_BYT_DRAM:
+			block_data.offset = block->ram_offset +
+					    dsp->addr.dram_offset;
+			block_data.type = SST_MEM_DRAM;
+			break;
+		case SST_BYT_CACHE:
+			block_data.offset = block->ram_offset +
+					    (dsp->addr.fw_ext - dsp->addr.lpe);
+			block_data.type = SST_MEM_CACHE;
+			break;
+		default:
+			dev_err(dsp->dev, "wrong ram type 0x%x in block0x%x\n",
+				block->type, count);
+			return -EINVAL;
+		}
+
+		block_data.size = block->size;
+		block_data.data_type = SST_DATA_M;
+		block_data.data = (void *)block + sizeof(*block);
+
+		sst_module_insert_fixed_block(mod, &block_data);
+
+		block = (void *)block + sizeof(*block) + block->size;
+	}
+	return 0;
+}
+
+static int sst_byt_parse_fw_image(struct sst_fw *sst_fw)
+{
+	struct fw_header *header;
+	struct sst_byt_fw_module_header *module;
+	struct sst_dsp *dsp = sst_fw->dsp;
+	int ret, count;
+
+	/* Read the header information from the data pointer */
+	header = (struct fw_header *)sst_fw->dma_buf;
+
+	/* verify FW */
+	if ((strncmp(header->signature, SST_BYT_FW_SIGN, 4) != 0) ||
+	    (sst_fw->size != header->file_size + sizeof(*header))) {
+		/* Invalid FW signature */
+		dev_err(dsp->dev, "Invalid FW sign/filesize mismatch\n");
+		return -EINVAL;
+	}
+
+	dev_dbg(dsp->dev,
+		"header sign=%4s size=0x%x modules=0x%x fmt=0x%x size=%zu\n",
+		header->signature, header->file_size, header->modules,
+		header->file_format, sizeof(*header));
+
+	module = (void *)sst_fw->dma_buf + sizeof(*header);
+	for (count = 0; count < header->modules; count++) {
+		/* module */
+		ret = sst_byt_parse_module(dsp, sst_fw, module);
+		if (ret < 0) {
+			dev_err(dsp->dev, "invalid module %d\n", count);
+			return ret;
+		}
+		module = (void *)module + sizeof(*module) + module->mod_size;
+	}
+
+	return 0;
+}
+
+static void sst_byt_dump_shim(struct sst_dsp *sst)
+{
+	int i;
+	u64 reg;
+
+	for (i = 0; i <= 0xF0; i += 8) {
+		reg = sst_dsp_shim_read64_unlocked(sst, i);
+		if (reg)
+			dev_dbg(sst->dev, "shim 0x%2.2x value 0x%16.16llx\n",
+				i, reg);
+	}
+
+	for (i = 0x00; i <= 0xff; i += 4) {
+		reg = readl(sst->addr.pci_cfg + i);
+		if (reg)
+			dev_dbg(sst->dev, "pci 0x%2.2x value 0x%8.8x\n",
+				i, (u32)reg);
+	}
+}
+
+static irqreturn_t sst_byt_irq(int irq, void *context)
+{
+	struct sst_dsp *sst = (struct sst_dsp *) context;
+	u64 isrx;
+	irqreturn_t ret = IRQ_NONE;
+
+	spin_lock(&sst->spinlock);
+
+	isrx = sst_dsp_shim_read64_unlocked(sst, SST_ISRX);
+	if (isrx & SST_ISRX_DONE) {
+		/* ADSP has processed the message request from IA */
+		sst_dsp_shim_update_bits64_unlocked(sst, SST_IPCX,
+						    SST_BYT_IPCX_DONE, 0);
+		ret = IRQ_WAKE_THREAD;
+	}
+	if (isrx & SST_BYT_ISRX_REQUEST) {
+		/* mask message request from ADSP and do processing later */
+		sst_dsp_shim_update_bits64_unlocked(sst, SST_IMRX,
+						    SST_BYT_IMRX_REQUEST,
+						    SST_BYT_IMRX_REQUEST);
+		ret = IRQ_WAKE_THREAD;
+	}
+
+	spin_unlock(&sst->spinlock);
+
+	return ret;
+}
+
+static void sst_byt_boot(struct sst_dsp *sst)
+{
+	int tries = 10;
+
+	/* release stall and wait to unstall */
+	sst_dsp_shim_update_bits64(sst, SST_CSR, SST_BYT_CSR_STALL, 0x0);
+	while (tries--) {
+		if (!(sst_dsp_shim_read64(sst, SST_CSR) &
+		      SST_BYT_CSR_PWAITMODE))
+			break;
+		msleep(100);
+	}
+	if (tries < 0) {
+		dev_err(sst->dev, "unable to start DSP\n");
+		sst_byt_dump_shim(sst);
+	}
+}
+
+static void sst_byt_reset(struct sst_dsp *sst)
+{
+	/* put DSP into reset, set reset vector and stall */
+	sst_dsp_shim_update_bits64(sst, SST_CSR,
+		SST_BYT_CSR_RST | SST_BYT_CSR_VECTOR_SEL | SST_BYT_CSR_STALL,
+		SST_BYT_CSR_RST | SST_BYT_CSR_VECTOR_SEL | SST_BYT_CSR_STALL);
+
+	udelay(10);
+
+	/* take DSP out of reset and keep stalled for FW loading */
+	sst_dsp_shim_update_bits64(sst, SST_CSR, SST_BYT_CSR_RST, 0);
+}
+
+struct sst_adsp_memregion {
+	u32 start;
+	u32 end;
+	int blocks;
+	enum sst_mem_type type;
+};
+
+/* BYT test stuff */
+static const struct sst_adsp_memregion byt_region[] = {
+	{0xC0000, 0x100000, 8, SST_MEM_IRAM}, /* I-SRAM - 8 * 32kB */
+	{0x100000, 0x140000, 8, SST_MEM_DRAM}, /* D-SRAM0 - 8 * 32kB */
+};
+
+static int sst_byt_resource_map(struct sst_dsp *sst, struct sst_pdata *pdata)
+{
+	sst->addr.lpe_base = pdata->lpe_base;
+	sst->addr.lpe = ioremap(pdata->lpe_base, pdata->lpe_size);
+	if (!sst->addr.lpe)
+		return -ENODEV;
+
+	/* ADSP PCI MMIO config space */
+	sst->addr.pci_cfg = ioremap(pdata->pcicfg_base, pdata->pcicfg_size);
+	if (!sst->addr.pci_cfg) {
+		iounmap(sst->addr.lpe);
+		return -ENODEV;
+	}
+
+	/* SST Extended FW allocation */
+	sst->addr.fw_ext = ioremap(pdata->fw_base, pdata->fw_size);
+	if (!sst->addr.fw_ext) {
+		iounmap(sst->addr.pci_cfg);
+		iounmap(sst->addr.lpe);
+		return -ENODEV;
+	}
+
+	/* SST Shim */
+	sst->addr.shim = sst->addr.lpe + sst->addr.shim_offset;
+
+	sst_dsp_mailbox_init(sst, SST_BYT_MAILBOX_OFFSET + 0x204,
+			     SST_BYT_IPC_MAX_PAYLOAD_SIZE,
+			     SST_BYT_MAILBOX_OFFSET,
+			     SST_BYT_IPC_MAX_PAYLOAD_SIZE);
+
+	sst->irq = pdata->irq;
+
+	return 0;
+}
+
+static int sst_byt_init(struct sst_dsp *sst, struct sst_pdata *pdata)
+{
+	const struct sst_adsp_memregion *region;
+	struct device *dev;
+	int ret = -ENODEV, i, j, region_count;
+	u32 offset, size;
+
+	dev = sst->dev;
+
+	switch (sst->id) {
+	case SST_DEV_ID_BYT:
+		region = byt_region;
+		region_count = ARRAY_SIZE(byt_region);
+		sst->addr.iram_offset = SST_BYT_IRAM_OFFSET;
+		sst->addr.dram_offset = SST_BYT_DRAM_OFFSET;
+		sst->addr.shim_offset = SST_BYT_SHIM_OFFSET;
+		break;
+	default:
+		dev_err(dev, "failed to get mem resources\n");
+		return ret;
+	}
+
+	ret = sst_byt_resource_map(sst, pdata);
+	if (ret < 0) {
+		dev_err(dev, "failed to map resources\n");
+		return ret;
+	}
+
+	/*
+	 * save the physical address of extended firmware block in the first
+	 * 4 bytes of the mailbox
+	 */
+	memcpy_toio(sst->addr.lpe + SST_BYT_MAILBOX_OFFSET,
+	       &pdata->fw_base, sizeof(u32));
+
+	ret = dma_coerce_mask_and_coherent(dev, DMA_BIT_MASK(32));
+	if (ret)
+		return ret;
+
+	/* enable Interrupt from both sides */
+	sst_dsp_shim_update_bits64(sst, SST_IMRX, 0x3, 0x0);
+	sst_dsp_shim_update_bits64(sst, SST_IMRD, 0x3, 0x0);
+
+	/* register DSP memory blocks - ideally we should get this from ACPI */
+	for (i = 0; i < region_count; i++) {
+		offset = region[i].start;
+		size = (region[i].end - region[i].start) / region[i].blocks;
+
+		/* register individual memory blocks */
+		for (j = 0; j < region[i].blocks; j++) {
+			sst_mem_block_register(sst, offset, size,
+					       region[i].type, NULL, j, sst);
+			offset += size;
+		}
+	}
+
+	return 0;
+}
+
+static void sst_byt_free(struct sst_dsp *sst)
+{
+	sst_mem_block_unregister_all(sst);
+	iounmap(sst->addr.lpe);
+	iounmap(sst->addr.pci_cfg);
+	iounmap(sst->addr.fw_ext);
+}
+
+struct sst_ops sst_byt_ops = {
+	.reset = sst_byt_reset,
+	.boot = sst_byt_boot,
+	.write = sst_shim32_write,
+	.read = sst_shim32_read,
+	.write64 = sst_shim32_write64,
+	.read64 = sst_shim32_read64,
+	.ram_read = sst_memcpy_fromio_32,
+	.ram_write = sst_memcpy_toio_32,
+	.irq_handler = sst_byt_irq,
+	.init = sst_byt_init,
+	.free = sst_byt_free,
+	.parse_fw = sst_byt_parse_fw_image,
+};
diff --git a/sound/soc/intel/sst-baytrail-ipc.c b/sound/soc/intel/sst-baytrail-ipc.c
new file mode 100644
index 000000000000..d0eaeee21be4
--- /dev/null
+++ b/sound/soc/intel/sst-baytrail-ipc.c
@@ -0,0 +1,867 @@
+/*
+ * Intel Baytrail SST IPC Support
+ * Copyright (c) 2014, Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ */
+
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/list.h>
+#include <linux/device.h>
+#include <linux/wait.h>
+#include <linux/spinlock.h>
+#include <linux/workqueue.h>
+#include <linux/export.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/list.h>
+#include <linux/platform_device.h>
+#include <linux/kthread.h>
+#include <linux/firmware.h>
+#include <linux/io.h>
+#include <asm/div64.h>
+
+#include "sst-baytrail-ipc.h"
+#include "sst-dsp.h"
+#include "sst-dsp-priv.h"
+
+/* IPC message timeout */
+#define IPC_TIMEOUT_MSECS	300
+#define IPC_BOOT_MSECS		200
+
+#define IPC_EMPTY_LIST_SIZE	8
+
+/* IPC header bits */
+#define IPC_HEADER_MSG_ID_MASK	0xff
+#define IPC_HEADER_MSG_ID(x)	((x) & IPC_HEADER_MSG_ID_MASK)
+#define IPC_HEADER_STR_ID_SHIFT	8
+#define IPC_HEADER_STR_ID_MASK	0x1f
+#define IPC_HEADER_STR_ID(x)	(((x) & 0x1f) << IPC_HEADER_STR_ID_SHIFT)
+#define IPC_HEADER_LARGE_SHIFT	13
+#define IPC_HEADER_LARGE(x)	(((x) & 0x1) << IPC_HEADER_LARGE_SHIFT)
+#define IPC_HEADER_DATA_SHIFT	16
+#define IPC_HEADER_DATA_MASK	0x3fff
+#define IPC_HEADER_DATA(x)	(((x) & 0x3fff) << IPC_HEADER_DATA_SHIFT)
+
+/* mask for differentiating between notification and reply message */
+#define IPC_NOTIFICATION	(0x1 << 7)
+
+/* I2L Stream config/control msgs */
+#define IPC_IA_ALLOC_STREAM	0x20
+#define IPC_IA_FREE_STREAM	0x21
+#define IPC_IA_PAUSE_STREAM	0x24
+#define IPC_IA_RESUME_STREAM	0x25
+#define IPC_IA_DROP_STREAM	0x26
+#define IPC_IA_START_STREAM	0x30
+
+/* notification messages */
+#define IPC_IA_FW_INIT_CMPLT	0x81
+#define IPC_SST_PERIOD_ELAPSED	0x97
+
+/* IPC messages between host and ADSP */
+struct sst_byt_address_info {
+	u32 addr;
+	u32 size;
+} __packed;
+
+struct sst_byt_str_type {
+	u8 codec_type;
+	u8 str_type;
+	u8 operation;
+	u8 protected_str;
+	u8 time_slots;
+	u8 reserved;
+	u16 result;
+} __packed;
+
+struct sst_byt_pcm_params {
+	u8 num_chan;
+	u8 pcm_wd_sz;
+	u8 use_offload_path;
+	u8 reserved;
+	u32 sfreq;
+	u8 channel_map[8];
+} __packed;
+
+struct sst_byt_frames_info {
+	u16 num_entries;
+	u16 rsrvd;
+	u32 frag_size;
+	struct sst_byt_address_info ring_buf_info[8];
+} __packed;
+
+struct sst_byt_alloc_params {
+	struct sst_byt_str_type str_type;
+	struct sst_byt_pcm_params pcm_params;
+	struct sst_byt_frames_info frame_info;
+} __packed;
+
+struct sst_byt_alloc_response {
+	struct sst_byt_str_type str_type;
+	u8 reserved[88];
+} __packed;
+
+struct sst_byt_start_stream_params {
+	u32 byte_offset;
+} __packed;
+
+struct sst_byt_tstamp {
+	u64 ring_buffer_counter;
+	u64 hardware_counter;
+	u64 frames_decoded;
+	u64 bytes_decoded;
+	u64 bytes_copied;
+	u32 sampling_frequency;
+	u32 channel_peak[8];
+} __packed;
+
+/* driver internal IPC message structure */
+struct ipc_message {
+	struct list_head list;
+	u64 header;
+
+	/* direction wrt host CPU */
+	char tx_data[SST_BYT_IPC_MAX_PAYLOAD_SIZE];
+	size_t tx_size;
+	char rx_data[SST_BYT_IPC_MAX_PAYLOAD_SIZE];
+	size_t rx_size;
+
+	wait_queue_head_t waitq;
+	bool complete;
+	bool wait;
+	int errno;
+};
+
+struct sst_byt_stream;
+struct sst_byt;
+
+/* stream infomation */
+struct sst_byt_stream {
+	struct list_head node;
+
+	/* configuration */
+	struct sst_byt_alloc_params request;
+	struct sst_byt_alloc_response reply;
+
+	/* runtime info */
+	struct sst_byt *byt;
+	int str_id;
+	bool commited;
+	bool running;
+
+	/* driver callback */
+	u32 (*notify_position)(struct sst_byt_stream *stream, void *data);
+	void *pdata;
+};
+
+/* SST Baytrail IPC data */
+struct sst_byt {
+	struct device *dev;
+	struct sst_dsp *dsp;
+
+	/* stream */
+	struct list_head stream_list;
+
+	/* boot */
+	wait_queue_head_t boot_wait;
+	bool boot_complete;
+
+	/* IPC messaging */
+	struct list_head tx_list;
+	struct list_head rx_list;
+	struct list_head empty_list;
+	wait_queue_head_t wait_txq;
+	struct task_struct *tx_thread;
+	struct kthread_worker kworker;
+	struct kthread_work kwork;
+	struct ipc_message *msg;
+};
+
+static inline u64 sst_byt_header(int msg_id, int data, bool large, int str_id)
+{
+	u64 header;
+
+	header = IPC_HEADER_MSG_ID(msg_id) |
+		 IPC_HEADER_STR_ID(str_id) |
+		 IPC_HEADER_LARGE(large) |
+		 IPC_HEADER_DATA(data) |
+		 SST_BYT_IPCX_BUSY;
+
+	return header;
+}
+
+static inline u16 sst_byt_header_msg_id(u64 header)
+{
+	return header & IPC_HEADER_MSG_ID_MASK;
+}
+
+static inline u8 sst_byt_header_str_id(u64 header)
+{
+	return (header >> IPC_HEADER_STR_ID_SHIFT) & IPC_HEADER_STR_ID_MASK;
+}
+
+static inline u16 sst_byt_header_data(u64 header)
+{
+	return (header >> IPC_HEADER_DATA_SHIFT) & IPC_HEADER_DATA_MASK;
+}
+
+static struct sst_byt_stream *sst_byt_get_stream(struct sst_byt *byt,
+						 int stream_id)
+{
+	struct sst_byt_stream *stream;
+
+	list_for_each_entry(stream, &byt->stream_list, node) {
+		if (stream->str_id == stream_id)
+			return stream;
+	}
+
+	return NULL;
+}
+
+static void sst_byt_ipc_shim_dbg(struct sst_byt *byt, const char *text)
+{
+	struct sst_dsp *sst = byt->dsp;
+	u64 isr, ipcd, imrx, ipcx;
+
+	ipcx = sst_dsp_shim_read64_unlocked(sst, SST_IPCX);
+	isr = sst_dsp_shim_read64_unlocked(sst, SST_ISRX);
+	ipcd = sst_dsp_shim_read64_unlocked(sst, SST_IPCD);
+	imrx = sst_dsp_shim_read64_unlocked(sst, SST_IMRX);
+
+	dev_err(byt->dev,
+		"ipc: --%s-- ipcx 0x%llx isr 0x%llx ipcd 0x%llx imrx 0x%llx\n",
+		text, ipcx, isr, ipcd, imrx);
+}
+
+/* locks held by caller */
+static struct ipc_message *sst_byt_msg_get_empty(struct sst_byt *byt)
+{
+	struct ipc_message *msg = NULL;
+
+	if (!list_empty(&byt->empty_list)) {
+		msg = list_first_entry(&byt->empty_list,
+				       struct ipc_message, list);
+		list_del(&msg->list);
+	}
+
+	return msg;
+}
+
+static void sst_byt_ipc_tx_msgs(struct kthread_work *work)
+{
+	struct sst_byt *byt =
+		container_of(work, struct sst_byt, kwork);
+	struct ipc_message *msg;
+	u64 ipcx;
+	unsigned long flags;
+
+	spin_lock_irqsave(&byt->dsp->spinlock, flags);
+	if (list_empty(&byt->tx_list)) {
+		spin_unlock_irqrestore(&byt->dsp->spinlock, flags);
+		return;
+	}
+
+	/* if the DSP is busy we will TX messages after IRQ */
+	ipcx = sst_dsp_shim_read64_unlocked(byt->dsp, SST_IPCX);
+	if (ipcx & SST_BYT_IPCX_BUSY) {
+		spin_unlock_irqrestore(&byt->dsp->spinlock, flags);
+		return;
+	}
+
+	msg = list_first_entry(&byt->tx_list, struct ipc_message, list);
+
+	list_move(&msg->list, &byt->rx_list);
+
+	/* send the message */
+	if (msg->header & IPC_HEADER_LARGE(true))
+		sst_dsp_outbox_write(byt->dsp, msg->tx_data, msg->tx_size);
+	sst_dsp_shim_write64_unlocked(byt->dsp, SST_IPCX, msg->header);
+
+	spin_unlock_irqrestore(&byt->dsp->spinlock, flags);
+}
+
+static inline void sst_byt_tx_msg_reply_complete(struct sst_byt *byt,
+						 struct ipc_message *msg)
+{
+	msg->complete = true;
+
+	if (!msg->wait)
+		list_add_tail(&msg->list, &byt->empty_list);
+	else
+		wake_up(&msg->waitq);
+}
+
+static int sst_byt_tx_wait_done(struct sst_byt *byt, struct ipc_message *msg,
+				void *rx_data)
+{
+	unsigned long flags;
+	int ret;
+
+	/* wait for DSP completion */
+	ret = wait_event_timeout(msg->waitq, msg->complete,
+				 msecs_to_jiffies(IPC_TIMEOUT_MSECS));
+
+	spin_lock_irqsave(&byt->dsp->spinlock, flags);
+	if (ret == 0) {
+		list_del(&msg->list);
+		sst_byt_ipc_shim_dbg(byt, "message timeout");
+
+		ret = -ETIMEDOUT;
+	} else {
+
+		/* copy the data returned from DSP */
+		if (msg->rx_size)
+			memcpy(rx_data, msg->rx_data, msg->rx_size);
+		ret = msg->errno;
+	}
+
+	list_add_tail(&msg->list, &byt->empty_list);
+	spin_unlock_irqrestore(&byt->dsp->spinlock, flags);
+	return ret;
+}
+
+static int sst_byt_ipc_tx_message(struct sst_byt *byt, u64 header,
+				  void *tx_data, size_t tx_bytes,
+				  void *rx_data, size_t rx_bytes, int wait)
+{
+	unsigned long flags;
+	struct ipc_message *msg;
+
+	spin_lock_irqsave(&byt->dsp->spinlock, flags);
+
+	msg = sst_byt_msg_get_empty(byt);
+	if (msg == NULL) {
+		spin_unlock_irqrestore(&byt->dsp->spinlock, flags);
+		return -EBUSY;
+	}
+
+	msg->header = header;
+	msg->tx_size = tx_bytes;
+	msg->rx_size = rx_bytes;
+	msg->wait = wait;
+	msg->errno = 0;
+	msg->complete = false;
+
+	if (tx_bytes) {
+		/* msg content = lower 32-bit of the header + data */
+		*(u32 *)msg->tx_data = (u32)(header & (u32)-1);
+		memcpy(msg->tx_data + sizeof(u32), tx_data, tx_bytes);
+		msg->tx_size += sizeof(u32);
+	}
+
+	list_add_tail(&msg->list, &byt->tx_list);
+	spin_unlock_irqrestore(&byt->dsp->spinlock, flags);
+
+	queue_kthread_work(&byt->kworker, &byt->kwork);
+
+	if (wait)
+		return sst_byt_tx_wait_done(byt, msg, rx_data);
+	else
+		return 0;
+}
+
+static inline int sst_byt_ipc_tx_msg_wait(struct sst_byt *byt, u64 header,
+					  void *tx_data, size_t tx_bytes,
+					  void *rx_data, size_t rx_bytes)
+{
+	return sst_byt_ipc_tx_message(byt, header, tx_data, tx_bytes,
+				      rx_data, rx_bytes, 1);
+}
+
+static inline int sst_byt_ipc_tx_msg_nowait(struct sst_byt *byt, u64 header,
+						void *tx_data, size_t tx_bytes)
+{
+	return sst_byt_ipc_tx_message(byt, header, tx_data, tx_bytes,
+				      NULL, 0, 0);
+}
+
+static struct ipc_message *sst_byt_reply_find_msg(struct sst_byt *byt,
+						  u64 header)
+{
+	struct ipc_message *msg = NULL, *_msg;
+	u64 mask;
+
+	/* match reply to message sent based on msg and stream IDs */
+	mask = IPC_HEADER_MSG_ID_MASK |
+	       IPC_HEADER_STR_ID_MASK << IPC_HEADER_STR_ID_SHIFT;
+	header &= mask;
+
+	if (list_empty(&byt->rx_list)) {
+		dev_err(byt->dev,
+			"ipc: rx list is empty but received 0x%llx\n", header);
+		goto out;
+	}
+
+	list_for_each_entry(_msg, &byt->rx_list, list) {
+		if ((_msg->header & mask) == header) {
+			msg = _msg;
+			break;
+		}
+	}
+
+out:
+	return msg;
+}
+
+static void sst_byt_stream_update(struct sst_byt *byt, struct ipc_message *msg)
+{
+	struct sst_byt_stream *stream;
+	u64 header = msg->header;
+	u8 stream_id = sst_byt_header_str_id(header);
+	u8 stream_msg = sst_byt_header_msg_id(header);
+
+	stream = sst_byt_get_stream(byt, stream_id);
+	if (stream == NULL)
+		return;
+
+	switch (stream_msg) {
+	case IPC_IA_DROP_STREAM:
+	case IPC_IA_PAUSE_STREAM:
+	case IPC_IA_FREE_STREAM:
+		stream->running = false;
+		break;
+	case IPC_IA_START_STREAM:
+	case IPC_IA_RESUME_STREAM:
+		stream->running = true;
+		break;
+	}
+}
+
+static int sst_byt_process_reply(struct sst_byt *byt, u64 header)
+{
+	struct ipc_message *msg;
+
+	msg = sst_byt_reply_find_msg(byt, header);
+	if (msg == NULL)
+		return 1;
+
+	if (header & IPC_HEADER_LARGE(true)) {
+		msg->rx_size = sst_byt_header_data(header);
+		sst_dsp_inbox_read(byt->dsp, msg->rx_data, msg->rx_size);
+	}
+
+	/* update any stream states */
+	sst_byt_stream_update(byt, msg);
+
+	list_del(&msg->list);
+	/* wake up */
+	sst_byt_tx_msg_reply_complete(byt, msg);
+
+	return 1;
+}
+
+static void sst_byt_fw_ready(struct sst_byt *byt, u64 header)
+{
+	dev_dbg(byt->dev, "ipc: DSP is ready 0x%llX\n", header);
+
+	byt->boot_complete = true;
+	wake_up(&byt->boot_wait);
+}
+
+static int sst_byt_process_notification(struct sst_byt *byt,
+					unsigned long *flags)
+{
+	struct sst_dsp *sst = byt->dsp;
+	struct sst_byt_stream *stream;
+	u64 header;
+	u8 msg_id, stream_id;
+	int handled = 1;
+
+	header = sst_dsp_shim_read64_unlocked(sst, SST_IPCD);
+	msg_id = sst_byt_header_msg_id(header);
+
+	switch (msg_id) {
+	case IPC_SST_PERIOD_ELAPSED:
+		stream_id = sst_byt_header_str_id(header);
+		stream = sst_byt_get_stream(byt, stream_id);
+		if (stream && stream->running && stream->notify_position) {
+			spin_unlock_irqrestore(&sst->spinlock, *flags);
+			stream->notify_position(stream, stream->pdata);
+			spin_lock_irqsave(&sst->spinlock, *flags);
+		}
+		break;
+	case IPC_IA_FW_INIT_CMPLT:
+		sst_byt_fw_ready(byt, header);
+		break;
+	}
+
+	return handled;
+}
+
+static irqreturn_t sst_byt_irq_thread(int irq, void *context)
+{
+	struct sst_dsp *sst = (struct sst_dsp *) context;
+	struct sst_byt *byt = sst_dsp_get_thread_context(sst);
+	u64 header;
+	unsigned long flags;
+
+	spin_lock_irqsave(&sst->spinlock, flags);
+
+	header = sst_dsp_shim_read64_unlocked(sst, SST_IPCD);
+	if (header & SST_BYT_IPCD_BUSY) {
+		if (header & IPC_NOTIFICATION) {
+			/* message from ADSP */
+			sst_byt_process_notification(byt, &flags);
+		} else {
+			/* reply from ADSP */
+			sst_byt_process_reply(byt, header);
+		}
+		/*
+		 * clear IPCD BUSY bit and set DONE bit. Tell DSP we have
+		 * processed the message and can accept new. Clear data part
+		 * of the header
+		 */
+		sst_dsp_shim_update_bits64_unlocked(sst, SST_IPCD,
+			SST_BYT_IPCD_DONE | SST_BYT_IPCD_BUSY |
+			IPC_HEADER_DATA(IPC_HEADER_DATA_MASK),
+			SST_BYT_IPCD_DONE);
+		/* unmask message request interrupts */
+		sst_dsp_shim_update_bits64_unlocked(sst, SST_IMRX,
+			SST_BYT_IMRX_REQUEST, 0);
+	}
+
+	spin_unlock_irqrestore(&sst->spinlock, flags);
+
+	/* continue to send any remaining messages... */
+	queue_kthread_work(&byt->kworker, &byt->kwork);
+
+	return IRQ_HANDLED;
+}
+
+/* stream API */
+struct sst_byt_stream *sst_byt_stream_new(struct sst_byt *byt, int id,
+	u32 (*notify_position)(struct sst_byt_stream *stream, void *data),
+	void *data)
+{
+	struct sst_byt_stream *stream;
+
+	stream = kzalloc(sizeof(*stream), GFP_KERNEL);
+	if (stream == NULL)
+		return NULL;
+
+	list_add(&stream->node, &byt->stream_list);
+	stream->notify_position = notify_position;
+	stream->pdata = data;
+	stream->byt = byt;
+	stream->str_id = id;
+
+	return stream;
+}
+
+int sst_byt_stream_set_bits(struct sst_byt *byt, struct sst_byt_stream *stream,
+			    int bits)
+{
+	stream->request.pcm_params.pcm_wd_sz = bits;
+	return 0;
+}
+
+int sst_byt_stream_set_channels(struct sst_byt *byt,
+				struct sst_byt_stream *stream, u8 channels)
+{
+	stream->request.pcm_params.num_chan = channels;
+	return 0;
+}
+
+int sst_byt_stream_set_rate(struct sst_byt *byt, struct sst_byt_stream *stream,
+			    unsigned int rate)
+{
+	stream->request.pcm_params.sfreq = rate;
+	return 0;
+}
+
+/* stream sonfiguration */
+int sst_byt_stream_type(struct sst_byt *byt, struct sst_byt_stream *stream,
+			int codec_type, int stream_type, int operation)
+{
+	stream->request.str_type.codec_type = codec_type;
+	stream->request.str_type.str_type = stream_type;
+	stream->request.str_type.operation = operation;
+	stream->request.str_type.time_slots = 0xc;
+
+	return 0;
+}
+
+int sst_byt_stream_buffer(struct sst_byt *byt, struct sst_byt_stream *stream,
+			  uint32_t buffer_addr, uint32_t buffer_size)
+{
+	stream->request.frame_info.num_entries = 1;
+	stream->request.frame_info.ring_buf_info[0].addr = buffer_addr;
+	stream->request.frame_info.ring_buf_info[0].size = buffer_size;
+	/* calculate bytes per 4 ms fragment */
+	stream->request.frame_info.frag_size =
+		stream->request.pcm_params.sfreq *
+		stream->request.pcm_params.num_chan *
+		stream->request.pcm_params.pcm_wd_sz / 8 *
+		4 / 1000;
+	return 0;
+}
+
+int sst_byt_stream_commit(struct sst_byt *byt, struct sst_byt_stream *stream)
+{
+	struct sst_byt_alloc_params *str_req = &stream->request;
+	struct sst_byt_alloc_response *reply = &stream->reply;
+	u64 header;
+	int ret;
+
+	header = sst_byt_header(IPC_IA_ALLOC_STREAM,
+				sizeof(*str_req) + sizeof(u32),
+				true, stream->str_id);
+	ret = sst_byt_ipc_tx_msg_wait(byt, header, str_req, sizeof(*str_req),
+				      reply, sizeof(*reply));
+	if (ret < 0) {
+		dev_err(byt->dev, "ipc: error stream commit failed\n");
+		return ret;
+	}
+
+	stream->commited = true;
+
+	return 0;
+}
+
+int sst_byt_stream_free(struct sst_byt *byt, struct sst_byt_stream *stream)
+{
+	u64 header;
+	int ret = 0;
+
+	if (!stream->commited)
+		goto out;
+
+	header = sst_byt_header(IPC_IA_FREE_STREAM, 0, false, stream->str_id);
+	ret = sst_byt_ipc_tx_msg_wait(byt, header, NULL, 0, NULL, 0);
+	if (ret < 0) {
+		dev_err(byt->dev, "ipc: free stream %d failed\n",
+			stream->str_id);
+		return -EAGAIN;
+	}
+
+	stream->commited = false;
+out:
+	list_del(&stream->node);
+	kfree(stream);
+
+	return ret;
+}
+
+static int sst_byt_stream_operations(struct sst_byt *byt, int type,
+				     int stream_id, int wait)
+{
+	struct sst_byt_start_stream_params start_stream;
+	u64 header;
+	void *tx_msg = NULL;
+	size_t size = 0;
+
+	if (type != IPC_IA_START_STREAM) {
+		header = sst_byt_header(type, 0, false, stream_id);
+	} else {
+		start_stream.byte_offset = 0;
+		header = sst_byt_header(IPC_IA_START_STREAM,
+					sizeof(start_stream) + sizeof(u32),
+					true, stream_id);
+		tx_msg = &start_stream;
+		size = sizeof(start_stream);
+	}
+
+	if (wait)
+		return sst_byt_ipc_tx_msg_wait(byt, header,
+					       tx_msg, size, NULL, 0);
+	else
+		return sst_byt_ipc_tx_msg_nowait(byt, header, tx_msg, size);
+}
+
+/* stream ALSA trigger operations */
+int sst_byt_stream_start(struct sst_byt *byt, struct sst_byt_stream *stream)
+{
+	int ret;
+
+	ret = sst_byt_stream_operations(byt, IPC_IA_START_STREAM,
+					stream->str_id, 0);
+	if (ret < 0)
+		dev_err(byt->dev, "ipc: error failed to start stream %d\n",
+			stream->str_id);
+
+	return ret;
+}
+
+int sst_byt_stream_stop(struct sst_byt *byt, struct sst_byt_stream *stream)
+{
+	int ret;
+
+	/* don't stop streams that are not commited */
+	if (!stream->commited)
+		return 0;
+
+	ret = sst_byt_stream_operations(byt, IPC_IA_DROP_STREAM,
+					stream->str_id, 0);
+	if (ret < 0)
+		dev_err(byt->dev, "ipc: error failed to stop stream %d\n",
+			stream->str_id);
+	return ret;
+}
+
+int sst_byt_stream_pause(struct sst_byt *byt, struct sst_byt_stream *stream)
+{
+	int ret;
+
+	ret = sst_byt_stream_operations(byt, IPC_IA_PAUSE_STREAM,
+					stream->str_id, 0);
+	if (ret < 0)
+		dev_err(byt->dev, "ipc: error failed to pause stream %d\n",
+			stream->str_id);
+
+	return ret;
+}
+
+int sst_byt_stream_resume(struct sst_byt *byt, struct sst_byt_stream *stream)
+{
+	int ret;
+
+	ret = sst_byt_stream_operations(byt, IPC_IA_RESUME_STREAM,
+					stream->str_id, 0);
+	if (ret < 0)
+		dev_err(byt->dev, "ipc: error failed to resume stream %d\n",
+			stream->str_id);
+
+	return ret;
+}
+
+int sst_byt_get_dsp_position(struct sst_byt *byt,
+			     struct sst_byt_stream *stream, int buffer_size)
+{
+	struct sst_dsp *sst = byt->dsp;
+	struct sst_byt_tstamp fw_tstamp;
+	u8 str_id = stream->str_id;
+	u32 tstamp_offset;
+
+	tstamp_offset = SST_BYT_TIMESTAMP_OFFSET + str_id * sizeof(fw_tstamp);
+	memcpy_fromio(&fw_tstamp,
+		      sst->addr.lpe + tstamp_offset, sizeof(fw_tstamp));
+
+	return do_div(fw_tstamp.ring_buffer_counter, buffer_size);
+}
+
+static int msg_empty_list_init(struct sst_byt *byt)
+{
+	struct ipc_message *msg;
+	int i;
+
+	byt->msg = kzalloc(sizeof(*msg) * IPC_EMPTY_LIST_SIZE, GFP_KERNEL);
+	if (byt->msg == NULL)
+		return -ENOMEM;
+
+	for (i = 0; i < IPC_EMPTY_LIST_SIZE; i++) {
+		init_waitqueue_head(&byt->msg[i].waitq);
+		list_add(&byt->msg[i].list, &byt->empty_list);
+	}
+
+	return 0;
+}
+
+struct sst_dsp *sst_byt_get_dsp(struct sst_byt *byt)
+{
+	return byt->dsp;
+}
+
+static struct sst_dsp_device byt_dev = {
+	.thread = sst_byt_irq_thread,
+	.ops = &sst_byt_ops,
+};
+
+int sst_byt_dsp_init(struct device *dev, struct sst_pdata *pdata)
+{
+	struct sst_byt *byt;
+	struct sst_fw *byt_sst_fw;
+	int err;
+
+	dev_dbg(dev, "initialising Byt DSP IPC\n");
+
+	byt = devm_kzalloc(dev, sizeof(*byt), GFP_KERNEL);
+	if (byt == NULL)
+		return -ENOMEM;
+
+	byt->dev = dev;
+	INIT_LIST_HEAD(&byt->stream_list);
+	INIT_LIST_HEAD(&byt->tx_list);
+	INIT_LIST_HEAD(&byt->rx_list);
+	INIT_LIST_HEAD(&byt->empty_list);
+	init_waitqueue_head(&byt->boot_wait);
+	init_waitqueue_head(&byt->wait_txq);
+
+	err = msg_empty_list_init(byt);
+	if (err < 0)
+		return -ENOMEM;
+
+	/* start the IPC message thread */
+	init_kthread_worker(&byt->kworker);
+	byt->tx_thread = kthread_run(kthread_worker_fn,
+				     &byt->kworker,
+				     dev_name(byt->dev));
+	if (IS_ERR(byt->tx_thread)) {
+		err = PTR_ERR(byt->tx_thread);
+		dev_err(byt->dev, "error failed to create message TX task\n");
+		goto err_free_msg;
+	}
+	init_kthread_work(&byt->kwork, sst_byt_ipc_tx_msgs);
+
+	byt_dev.thread_context = byt;
+
+	/* init SST shim */
+	byt->dsp = sst_dsp_new(dev, &byt_dev, pdata);
+	if (byt->dsp == NULL) {
+		err = -ENODEV;
+		goto err_free_msg;
+	}
+
+	/* keep the DSP in reset state for base FW loading */
+	sst_dsp_reset(byt->dsp);
+
+	byt_sst_fw = sst_fw_new(byt->dsp, pdata->fw, byt);
+	if (byt_sst_fw  == NULL) {
+		err = -ENODEV;
+		dev_err(dev, "error: failed to load firmware\n");
+		goto fw_err;
+	}
+
+	/* wait for DSP boot completion */
+	sst_dsp_boot(byt->dsp);
+	err = wait_event_timeout(byt->boot_wait, byt->boot_complete,
+				 msecs_to_jiffies(IPC_BOOT_MSECS));
+	if (err == 0) {
+		err = -EIO;
+		dev_err(byt->dev, "ipc: error DSP boot timeout\n");
+		goto boot_err;
+	}
+
+	pdata->dsp = byt;
+
+	return 0;
+
+boot_err:
+	sst_dsp_reset(byt->dsp);
+	sst_fw_free(byt_sst_fw);
+fw_err:
+	sst_dsp_free(byt->dsp);
+err_free_msg:
+	kfree(byt->msg);
+
+	return err;
+}
+EXPORT_SYMBOL_GPL(sst_byt_dsp_init);
+
+void sst_byt_dsp_free(struct device *dev, struct sst_pdata *pdata)
+{
+	struct sst_byt *byt = pdata->dsp;
+
+	sst_dsp_reset(byt->dsp);
+	sst_fw_free_all(byt->dsp);
+	sst_dsp_free(byt->dsp);
+	kfree(byt->msg);
+}
+EXPORT_SYMBOL_GPL(sst_byt_dsp_free);
diff --git a/sound/soc/intel/sst-baytrail-ipc.h b/sound/soc/intel/sst-baytrail-ipc.h
new file mode 100644
index 000000000000..f172b6440fa9
--- /dev/null
+++ b/sound/soc/intel/sst-baytrail-ipc.h
@@ -0,0 +1,69 @@
+/*
+ * Intel Baytrail SST IPC Support
+ * Copyright (c) 2014, Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ */
+
+#ifndef __SST_BYT_IPC_H
+#define __SST_BYT_IPC_H
+
+#include <linux/types.h>
+
+struct sst_byt;
+struct sst_byt_stream;
+struct sst_pdata;
+extern struct sst_ops sst_byt_ops;
+
+
+#define SST_BYT_MAILBOX_OFFSET		0x144000
+#define SST_BYT_TIMESTAMP_OFFSET	(SST_BYT_MAILBOX_OFFSET + 0x800)
+
+/**
+ * Upfront defined maximum message size that is
+ * expected by the in/out communication pipes in FW.
+ */
+#define SST_BYT_IPC_MAX_PAYLOAD_SIZE	200
+
+/* stream API */
+struct sst_byt_stream *sst_byt_stream_new(struct sst_byt *byt, int id,
+	uint32_t (*get_write_position)(struct sst_byt_stream *stream,
+				       void *data),
+	void *data);
+
+/* stream configuration */
+int sst_byt_stream_set_bits(struct sst_byt *byt, struct sst_byt_stream *stream,
+			    int bits);
+int sst_byt_stream_set_channels(struct sst_byt *byt,
+				struct sst_byt_stream *stream, u8 channels);
+int sst_byt_stream_set_rate(struct sst_byt *byt, struct sst_byt_stream *stream,
+			    unsigned int rate);
+int sst_byt_stream_type(struct sst_byt *byt, struct sst_byt_stream *stream,
+			int codec_type, int stream_type, int operation);
+int sst_byt_stream_buffer(struct sst_byt *byt, struct sst_byt_stream *stream,
+			  uint32_t buffer_addr, uint32_t buffer_size);
+int sst_byt_stream_commit(struct sst_byt *byt, struct sst_byt_stream *stream);
+int sst_byt_stream_free(struct sst_byt *byt, struct sst_byt_stream *stream);
+
+/* stream ALSA trigger operations */
+int sst_byt_stream_start(struct sst_byt *byt, struct sst_byt_stream *stream);
+int sst_byt_stream_stop(struct sst_byt *byt, struct sst_byt_stream *stream);
+int sst_byt_stream_pause(struct sst_byt *byt, struct sst_byt_stream *stream);
+int sst_byt_stream_resume(struct sst_byt *byt, struct sst_byt_stream *stream);
+
+int sst_byt_get_dsp_position(struct sst_byt *byt,
+			     struct sst_byt_stream *stream, int buffer_size);
+
+/* init */
+int sst_byt_dsp_init(struct device *dev, struct sst_pdata *pdata);
+void sst_byt_dsp_free(struct device *dev, struct sst_pdata *pdata);
+struct sst_dsp *sst_byt_get_dsp(struct sst_byt *byt);
+
+#endif
diff --git a/sound/soc/intel/sst-baytrail-pcm.c b/sound/soc/intel/sst-baytrail-pcm.c
new file mode 100644
index 000000000000..6d101f3813b4
--- /dev/null
+++ b/sound/soc/intel/sst-baytrail-pcm.c
@@ -0,0 +1,422 @@
+/*
+ * Intel Baytrail SST PCM Support
+ * Copyright (c) 2014, Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ */
+
+#include <linux/module.h>
+#include <linux/dma-mapping.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include "sst-baytrail-ipc.h"
+#include "sst-dsp-priv.h"
+#include "sst-dsp.h"
+
+#define BYT_PCM_COUNT		2
+
+static const struct snd_pcm_hardware sst_byt_pcm_hardware = {
+	.info			= SNDRV_PCM_INFO_MMAP |
+				  SNDRV_PCM_INFO_MMAP_VALID |
+				  SNDRV_PCM_INFO_INTERLEAVED |
+				  SNDRV_PCM_INFO_PAUSE |
+				  SNDRV_PCM_INFO_RESUME,
+	.formats		= SNDRV_PCM_FMTBIT_S16_LE |
+				  SNDRV_PCM_FORMAT_S24_LE,
+	.period_bytes_min	= 384,
+	.period_bytes_max	= 48000,
+	.periods_min		= 2,
+	.periods_max		= 250,
+	.buffer_bytes_max	= 96000,
+};
+
+/* private data for each PCM DSP stream */
+struct sst_byt_pcm_data {
+	struct sst_byt_stream *stream;
+	struct snd_pcm_substream *substream;
+	struct mutex mutex;
+};
+
+/* private data for the driver */
+struct sst_byt_priv_data {
+	/* runtime DSP */
+	struct sst_byt *byt;
+
+	/* DAI data */
+	struct sst_byt_pcm_data pcm[BYT_PCM_COUNT];
+};
+
+/* this may get called several times by oss emulation */
+static int sst_byt_pcm_hw_params(struct snd_pcm_substream *substream,
+				 struct snd_pcm_hw_params *params)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct sst_byt_priv_data *pdata =
+		snd_soc_platform_get_drvdata(rtd->platform);
+	struct sst_byt_pcm_data *pcm_data = snd_soc_pcm_get_drvdata(rtd);
+	struct sst_byt *byt = pdata->byt;
+	u32 rate, bits;
+	u8 channels;
+	int ret, playback = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK);
+
+	dev_dbg(rtd->dev, "PCM: hw_params, pcm_data %p\n", pcm_data);
+
+	ret = sst_byt_stream_type(byt, pcm_data->stream,
+				  1, 1, !playback);
+	if (ret < 0) {
+		dev_err(rtd->dev, "failed to set stream format %d\n", ret);
+		return ret;
+	}
+
+	rate = params_rate(params);
+	ret = sst_byt_stream_set_rate(byt, pcm_data->stream, rate);
+	if (ret < 0) {
+		dev_err(rtd->dev, "could not set rate %d\n", rate);
+		return ret;
+	}
+
+	bits = snd_pcm_format_width(params_format(params));
+	ret = sst_byt_stream_set_bits(byt, pcm_data->stream, bits);
+	if (ret < 0) {
+		dev_err(rtd->dev, "could not set formats %d\n",
+			params_rate(params));
+		return ret;
+	}
+
+	channels = (u8)(params_channels(params) & 0xF);
+	ret = sst_byt_stream_set_channels(byt, pcm_data->stream, channels);
+	if (ret < 0) {
+		dev_err(rtd->dev, "could not set channels %d\n",
+			params_rate(params));
+		return ret;
+	}
+
+	snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(params));
+
+	ret = sst_byt_stream_buffer(byt, pcm_data->stream,
+				    substream->dma_buffer.addr,
+				    params_buffer_bytes(params));
+	if (ret < 0) {
+		dev_err(rtd->dev, "PCM: failed to set DMA buffer %d\n", ret);
+		return ret;
+	}
+
+	ret = sst_byt_stream_commit(byt, pcm_data->stream);
+	if (ret < 0) {
+		dev_err(rtd->dev, "PCM: failed stream commit %d\n", ret);
+		return ret;
+	}
+
+	return 0;
+}
+
+static int sst_byt_pcm_hw_free(struct snd_pcm_substream *substream)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+
+	dev_dbg(rtd->dev, "PCM: hw_free\n");
+	snd_pcm_lib_free_pages(substream);
+
+	return 0;
+}
+
+static int sst_byt_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct sst_byt_priv_data *pdata =
+		snd_soc_platform_get_drvdata(rtd->platform);
+	struct sst_byt_pcm_data *pcm_data = snd_soc_pcm_get_drvdata(rtd);
+	struct sst_byt *byt = pdata->byt;
+
+	dev_dbg(rtd->dev, "PCM: trigger %d\n", cmd);
+
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+		sst_byt_stream_start(byt, pcm_data->stream);
+		break;
+	case SNDRV_PCM_TRIGGER_RESUME:
+	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+		sst_byt_stream_resume(byt, pcm_data->stream);
+		break;
+	case SNDRV_PCM_TRIGGER_STOP:
+		sst_byt_stream_stop(byt, pcm_data->stream);
+		break;
+	case SNDRV_PCM_TRIGGER_SUSPEND:
+	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+		sst_byt_stream_pause(byt, pcm_data->stream);
+		break;
+	default:
+		break;
+	}
+
+	return 0;
+}
+
+static u32 byt_notify_pointer(struct sst_byt_stream *stream, void *data)
+{
+	struct sst_byt_pcm_data *pcm_data = data;
+	struct snd_pcm_substream *substream = pcm_data->substream;
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	u32 pos;
+
+	pos = frames_to_bytes(runtime,
+			      (runtime->control->appl_ptr %
+			       runtime->buffer_size));
+
+	dev_dbg(rtd->dev, "PCM: App pointer %d bytes\n", pos);
+
+	snd_pcm_period_elapsed(substream);
+	return pos;
+}
+
+static snd_pcm_uframes_t sst_byt_pcm_pointer(struct snd_pcm_substream *substream)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct sst_byt_priv_data *pdata =
+		snd_soc_platform_get_drvdata(rtd->platform);
+	struct sst_byt_pcm_data *pcm_data = snd_soc_pcm_get_drvdata(rtd);
+	struct sst_byt *byt = pdata->byt;
+	snd_pcm_uframes_t offset;
+	int pos;
+
+	pos = sst_byt_get_dsp_position(byt, pcm_data->stream,
+				       snd_pcm_lib_buffer_bytes(substream));
+	offset = bytes_to_frames(runtime, pos);
+
+	dev_dbg(rtd->dev, "PCM: DMA pointer %zu bytes\n",
+		frames_to_bytes(runtime, (u32)offset));
+	return offset;
+}
+
+static int sst_byt_pcm_open(struct snd_pcm_substream *substream)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct sst_byt_priv_data *pdata =
+		snd_soc_platform_get_drvdata(rtd->platform);
+	struct sst_byt_pcm_data *pcm_data = snd_soc_pcm_get_drvdata(rtd);
+	struct sst_byt *byt = pdata->byt;
+
+	dev_dbg(rtd->dev, "PCM: open\n");
+
+	pcm_data = &pdata->pcm[rtd->cpu_dai->id];
+	mutex_lock(&pcm_data->mutex);
+
+	snd_soc_pcm_set_drvdata(rtd, pcm_data);
+	pcm_data->substream = substream;
+
+	snd_soc_set_runtime_hwparams(substream, &sst_byt_pcm_hardware);
+
+	pcm_data->stream = sst_byt_stream_new(byt, rtd->cpu_dai->id + 1,
+					      byt_notify_pointer, pcm_data);
+	if (pcm_data->stream == NULL) {
+		dev_err(rtd->dev, "failed to create stream\n");
+		mutex_unlock(&pcm_data->mutex);
+		return -EINVAL;
+	}
+
+	mutex_unlock(&pcm_data->mutex);
+	return 0;
+}
+
+static int sst_byt_pcm_close(struct snd_pcm_substream *substream)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct sst_byt_priv_data *pdata =
+		snd_soc_platform_get_drvdata(rtd->platform);
+	struct sst_byt_pcm_data *pcm_data = snd_soc_pcm_get_drvdata(rtd);
+	struct sst_byt *byt = pdata->byt;
+	int ret;
+
+	dev_dbg(rtd->dev, "PCM: close\n");
+
+	mutex_lock(&pcm_data->mutex);
+	ret = sst_byt_stream_free(byt, pcm_data->stream);
+	if (ret < 0) {
+		dev_dbg(rtd->dev, "Free stream fail\n");
+		goto out;
+	}
+	pcm_data->stream = NULL;
+
+out:
+	mutex_unlock(&pcm_data->mutex);
+	return ret;
+}
+
+static int sst_byt_pcm_mmap(struct snd_pcm_substream *substream,
+			    struct vm_area_struct *vma)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+
+	dev_dbg(rtd->dev, "PCM: mmap\n");
+	return snd_pcm_lib_default_mmap(substream, vma);
+}
+
+static struct snd_pcm_ops sst_byt_pcm_ops = {
+	.open		= sst_byt_pcm_open,
+	.close		= sst_byt_pcm_close,
+	.ioctl		= snd_pcm_lib_ioctl,
+	.hw_params	= sst_byt_pcm_hw_params,
+	.hw_free	= sst_byt_pcm_hw_free,
+	.trigger	= sst_byt_pcm_trigger,
+	.pointer	= sst_byt_pcm_pointer,
+	.mmap		= sst_byt_pcm_mmap,
+};
+
+static void sst_byt_pcm_free(struct snd_pcm *pcm)
+{
+	snd_pcm_lib_preallocate_free_for_all(pcm);
+}
+
+static int sst_byt_pcm_new(struct snd_soc_pcm_runtime *rtd)
+{
+	struct snd_pcm *pcm = rtd->pcm;
+	size_t size;
+	int ret = 0;
+
+	ret = dma_coerce_mask_and_coherent(rtd->card->dev, DMA_BIT_MASK(32));
+	if (ret)
+		return ret;
+
+	if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream ||
+	    pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream) {
+		size = sst_byt_pcm_hardware.buffer_bytes_max;
+		ret = snd_pcm_lib_preallocate_pages_for_all(pcm,
+							    SNDRV_DMA_TYPE_DEV,
+							    rtd->card->dev,
+							    size, size);
+		if (ret) {
+			dev_err(rtd->dev, "dma buffer allocation failed %d\n",
+				ret);
+			return ret;
+		}
+	}
+
+	return ret;
+}
+
+static struct snd_soc_dai_driver byt_dais[] = {
+	{
+		.name  = "Front-cpu-dai",
+		.playback = {
+			.stream_name = "System Playback",
+			.channels_min = 2,
+			.channels_max = 2,
+			.rates = SNDRV_PCM_RATE_48000,
+			.formats = SNDRV_PCM_FMTBIT_S24_3LE |
+				   SNDRV_PCM_FMTBIT_S16_LE,
+		},
+	},
+	{
+		.name  = "Mic1-cpu-dai",
+		.capture = {
+			.stream_name = "Analog Capture",
+			.channels_min = 2,
+			.channels_max = 2,
+			.rates = SNDRV_PCM_RATE_48000,
+			.formats = SNDRV_PCM_FMTBIT_S16_LE,
+		},
+	},
+};
+
+static int sst_byt_pcm_probe(struct snd_soc_platform *platform)
+{
+	struct sst_pdata *plat_data = dev_get_platdata(platform->dev);
+	struct sst_byt_priv_data *priv_data;
+	int i;
+
+	if (!plat_data)
+		return -ENODEV;
+
+	priv_data = devm_kzalloc(platform->dev, sizeof(*priv_data),
+				 GFP_KERNEL);
+	priv_data->byt = plat_data->dsp;
+	snd_soc_platform_set_drvdata(platform, priv_data);
+
+	for (i = 0; i < ARRAY_SIZE(byt_dais); i++)
+		mutex_init(&priv_data->pcm[i].mutex);
+
+	return 0;
+}
+
+static int sst_byt_pcm_remove(struct snd_soc_platform *platform)
+{
+	return 0;
+}
+
+static struct snd_soc_platform_driver byt_soc_platform = {
+	.probe		= sst_byt_pcm_probe,
+	.remove		= sst_byt_pcm_remove,
+	.ops		= &sst_byt_pcm_ops,
+	.pcm_new	= sst_byt_pcm_new,
+	.pcm_free	= sst_byt_pcm_free,
+};
+
+static const struct snd_soc_component_driver byt_dai_component = {
+	.name		= "byt-dai",
+};
+
+static int sst_byt_pcm_dev_probe(struct platform_device *pdev)
+{
+	struct sst_pdata *sst_pdata = dev_get_platdata(&pdev->dev);
+	int ret;
+
+	ret = sst_byt_dsp_init(&pdev->dev, sst_pdata);
+	if (ret < 0)
+		return -ENODEV;
+
+	ret = snd_soc_register_platform(&pdev->dev, &byt_soc_platform);
+	if (ret < 0)
+		goto err_plat;
+
+	ret = snd_soc_register_component(&pdev->dev, &byt_dai_component,
+					 byt_dais, ARRAY_SIZE(byt_dais));
+	if (ret < 0)
+		goto err_comp;
+
+	return 0;
+
+err_comp:
+	snd_soc_unregister_platform(&pdev->dev);
+err_plat:
+	sst_byt_dsp_free(&pdev->dev, sst_pdata);
+	return ret;
+}
+
+static int sst_byt_pcm_dev_remove(struct platform_device *pdev)
+{
+	struct sst_pdata *sst_pdata = dev_get_platdata(&pdev->dev);
+
+	snd_soc_unregister_platform(&pdev->dev);
+	snd_soc_unregister_component(&pdev->dev);
+	sst_byt_dsp_free(&pdev->dev, sst_pdata);
+
+	return 0;
+}
+
+static struct platform_driver sst_byt_pcm_driver = {
+	.driver = {
+		.name = "baytrail-pcm-audio",
+		.owner = THIS_MODULE,
+	},
+
+	.probe = sst_byt_pcm_dev_probe,
+	.remove = sst_byt_pcm_dev_remove,
+};
+module_platform_driver(sst_byt_pcm_driver);
+
+MODULE_AUTHOR("Jarkko Nikula");
+MODULE_DESCRIPTION("Baytrail PCM");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:baytrail-pcm-audio");
diff --git a/sound/soc/intel/sst-dsp-priv.h b/sound/soc/intel/sst-dsp-priv.h
new file mode 100644
index 000000000000..fe8e81aad646
--- /dev/null
+++ b/sound/soc/intel/sst-dsp-priv.h
@@ -0,0 +1,309 @@
+/*
+ * Intel Smart Sound Technology
+ *
+ * Copyright (C) 2013, Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License version
+ * 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef __SOUND_SOC_SST_DSP_PRIV_H
+#define __SOUND_SOC_SST_DSP_PRIV_H
+
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/interrupt.h>
+#include <linux/firmware.h>
+
+struct sst_mem_block;
+struct sst_module;
+struct sst_fw;
+
+/*
+ * DSP Operations exported by platform Audio DSP driver.
+ */
+struct sst_ops {
+	/* DSP core boot / reset */
+	void (*boot)(struct sst_dsp *);
+	void (*reset)(struct sst_dsp *);
+
+	/* Shim IO */
+	void (*write)(void __iomem *addr, u32 offset, u32 value);
+	u32 (*read)(void __iomem *addr, u32 offset);
+	void (*write64)(void __iomem *addr, u32 offset, u64 value);
+	u64 (*read64)(void __iomem *addr, u32 offset);
+
+	/* DSP I/DRAM IO */
+	void (*ram_read)(struct sst_dsp *sst, void  *dest, void __iomem *src,
+		size_t bytes);
+	void (*ram_write)(struct sst_dsp *sst, void __iomem *dest, void *src,
+		size_t bytes);
+
+	void (*dump)(struct sst_dsp *);
+
+	/* IRQ handlers */
+	irqreturn_t (*irq_handler)(int irq, void *context);
+
+	/* SST init and free */
+	int (*init)(struct sst_dsp *sst, struct sst_pdata *pdata);
+	void (*free)(struct sst_dsp *sst);
+
+	/* FW module parser/loader */
+	int (*parse_fw)(struct sst_fw *sst_fw);
+};
+
+/*
+ * Audio DSP memory offsets and addresses.
+ */
+struct sst_addr {
+	u32 lpe_base;
+	u32 shim_offset;
+	u32 iram_offset;
+	u32 dram_offset;
+	void __iomem *lpe;
+	void __iomem *shim;
+	void __iomem *pci_cfg;
+	void __iomem *fw_ext;
+};
+
+/*
+ * Audio DSP Mailbox configuration.
+ */
+struct sst_mailbox {
+	void __iomem *in_base;
+	void __iomem *out_base;
+	size_t in_size;
+	size_t out_size;
+};
+
+/*
+ * Audio DSP Firmware data types.
+ */
+enum sst_data_type {
+	SST_DATA_M	= 0, /* module block data */
+	SST_DATA_P	= 1, /* peristant data (text, data) */
+	SST_DATA_S	= 2, /* scratch data (usually buffers) */
+};
+
+/*
+ * Audio DSP memory block types.
+ */
+enum sst_mem_type {
+	SST_MEM_IRAM = 0,
+	SST_MEM_DRAM = 1,
+	SST_MEM_ANY  = 2,
+	SST_MEM_CACHE= 3,
+};
+
+/*
+ * Audio DSP Generic Firmware File.
+ *
+ * SST Firmware files can consist of 1..N modules. This generic structure is
+ * used to manage each firmware file and it's modules regardless of SST firmware
+ * type. A SST driver may load multiple FW files.
+ */
+struct sst_fw {
+	struct sst_dsp *dsp;
+
+	/* base addresses of FW file data */
+	dma_addr_t dmable_fw_paddr;	/* physical address of fw data */
+	void *dma_buf;			/* virtual address of fw data */
+	u32 size;			/* size of fw data */
+
+	/* lists */
+	struct list_head list;		/* DSP list of FW */
+	struct list_head module_list;	/* FW list of modules */
+
+	void *private;			/* core doesn't touch this */
+};
+
+/*
+ * Audio DSP Generic Module data.
+ *
+ * This is used to dsecribe any sections of persistent (text and data) and
+ * scratch (buffers) of module data in ADSP memory space.
+ */
+struct sst_module_data {
+
+	enum sst_mem_type type;		/* destination memory type */
+	enum sst_data_type data_type;	/* type of module data */
+
+	u32 size;		/* size in bytes */
+	u32 offset;		/* offset in FW file */
+	u32 data_offset;	/* offset in ADSP memory space */
+	void *data;		/* module data */
+};
+
+/*
+ * Audio DSP Generic Module Template.
+ *
+ * Used to define and register a new FW module. This data is extracted from
+ * FW module header information.
+ */
+struct sst_module_template {
+	u32 id;
+	u32 entry;			/* entry point */
+	struct sst_module_data s;	/* scratch data */
+	struct sst_module_data p;	/* peristant data */
+};
+
+/*
+ * Audio DSP Generic Module.
+ *
+ * Each Firmware file can consist of 1..N modules. A module can span multiple
+ * ADSP memory blocks. The simplest FW will be a file with 1 module.
+ */
+struct sst_module {
+	struct sst_dsp *dsp;
+	struct sst_fw *sst_fw;		/* parent FW we belong too */
+
+	/* module configuration */
+	u32 id;
+	u32 entry;			/* module entry point */
+	u32 offset;			/* module offset in firmware file */
+	u32 size;			/* module size */
+	struct sst_module_data s;	/* scratch data */
+	struct sst_module_data p;	/* peristant data */
+
+	/* runtime */
+	u32 usage_count;		/* can be unloaded if count == 0 */
+	void *private;			/* core doesn't touch this */
+
+	/* lists */
+	struct list_head block_list;	/* Module list of blocks in use */
+	struct list_head list;		/* DSP list of modules */
+	struct list_head list_fw;	/* FW list of modules */
+};
+
+/*
+ * SST Memory Block operations.
+ */
+struct sst_block_ops {
+	int (*enable)(struct sst_mem_block *block);
+	int (*disable)(struct sst_mem_block *block);
+};
+
+/*
+ * SST Generic Memory Block.
+ *
+ * SST ADP  memory has multiple IRAM and DRAM blocks. Some ADSP blocks can be
+ * power gated.
+ */
+struct sst_mem_block {
+	struct sst_dsp *dsp;
+	struct sst_module *module;	/* module that uses this block */
+
+	/* block config */
+	u32 offset;			/* offset from base */
+	u32 size;			/* block size */
+	u32 index;			/* block index 0..N */
+	enum sst_mem_type type;		/* block memory type IRAM/DRAM */
+	struct sst_block_ops *ops;	/* block operations, if any */
+
+	/* block status */
+	enum sst_data_type data_type;	/* data type held in this block */
+	u32 bytes_used;			/* bytes in use by modules */
+	void *private;			/* generic core does not touch this */
+	int users;			/* number of modules using this block */
+
+	/* block lists */
+	struct list_head module_list;	/* Module list of blocks */
+	struct list_head list;		/* Map list of free/used blocks */
+};
+
+/*
+ * Generic SST Shim Interface.
+ */
+struct sst_dsp {
+
+	/* runtime */
+	struct sst_dsp_device *sst_dev;
+	spinlock_t spinlock;	/* IPC locking */
+	struct mutex mutex;	/* DSP FW lock */
+	struct device *dev;
+	void *thread_context;
+	int irq;
+	u32 id;
+
+	/* list of free and used ADSP memory blocks */
+	struct list_head used_block_list;
+	struct list_head free_block_list;
+
+	/* operations */
+	struct sst_ops *ops;
+
+	/* debug FS */
+	struct dentry *debugfs_root;
+
+	/* base addresses */
+	struct sst_addr addr;
+
+	/* mailbox */
+	struct sst_mailbox mailbox;
+
+	/* SST FW files loaded and their modules */
+	struct list_head module_list;
+	struct list_head fw_list;
+
+	/* platform data */
+	struct sst_pdata *pdata;
+
+	/* DMA FW loading */
+	struct sst_dma *dma;
+	bool fw_use_dma;
+};
+
+/* Size optimised DRAM/IRAM memcpy */
+static inline void sst_dsp_write(struct sst_dsp *sst, void *src,
+	u32 dest_offset, size_t bytes)
+{
+	sst->ops->ram_write(sst, sst->addr.lpe + dest_offset, src, bytes);
+}
+
+static inline void sst_dsp_read(struct sst_dsp *sst, void *dest,
+	u32 src_offset, size_t bytes)
+{
+	sst->ops->ram_read(sst, dest, sst->addr.lpe + src_offset, bytes);
+}
+
+static inline void *sst_dsp_get_thread_context(struct sst_dsp *sst)
+{
+	return sst->thread_context;
+}
+
+/* Create/Free FW files - can contain multiple modules */
+struct sst_fw *sst_fw_new(struct sst_dsp *dsp,
+	const struct firmware *fw, void *private);
+void sst_fw_free(struct sst_fw *sst_fw);
+void sst_fw_free_all(struct sst_dsp *dsp);
+
+/* Create/Free firmware modules */
+struct sst_module *sst_module_new(struct sst_fw *sst_fw,
+	struct sst_module_template *template, void *private);
+void sst_module_free(struct sst_module *sst_module);
+int sst_module_insert(struct sst_module *sst_module);
+int sst_module_remove(struct sst_module *sst_module);
+int sst_module_insert_fixed_block(struct sst_module *module,
+	struct sst_module_data *data);
+struct sst_module *sst_module_get_from_id(struct sst_dsp *dsp, u32 id);
+
+/* allocate/free pesistent/scratch memory regions managed by drv */
+struct sst_module *sst_mem_block_alloc_scratch(struct sst_dsp *dsp);
+void sst_mem_block_free_scratch(struct sst_dsp *dsp,
+	struct sst_module *scratch);
+int sst_block_module_remove(struct sst_module *module);
+
+/* Register the DSPs memory blocks - would be nice to read from ACPI */
+struct sst_mem_block *sst_mem_block_register(struct sst_dsp *dsp, u32 offset,
+	u32 size, enum sst_mem_type type, struct sst_block_ops *ops, u32 index,
+	void *private);
+void sst_mem_block_unregister_all(struct sst_dsp *dsp);
+
+#endif
diff --git a/sound/soc/intel/sst-dsp.c b/sound/soc/intel/sst-dsp.c
new file mode 100644
index 000000000000..0c129fd85ecf
--- /dev/null
+++ b/sound/soc/intel/sst-dsp.c
@@ -0,0 +1,385 @@
+/*
+ * Intel Smart Sound Technology (SST) DSP Core Driver
+ *
+ * Copyright (C) 2013, Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License version
+ * 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/slab.h>
+#include <linux/export.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/io.h>
+
+#include "sst-dsp.h"
+#include "sst-dsp-priv.h"
+
+#define CREATE_TRACE_POINTS
+#include <trace/events/intel-sst.h>
+
+/* Internal generic low-level SST IO functions - can be overidden */
+void sst_shim32_write(void __iomem *addr, u32 offset, u32 value)
+{
+	writel(value, addr + offset);
+}
+EXPORT_SYMBOL_GPL(sst_shim32_write);
+
+u32 sst_shim32_read(void __iomem *addr, u32 offset)
+{
+	return readl(addr + offset);
+}
+EXPORT_SYMBOL_GPL(sst_shim32_read);
+
+void sst_shim32_write64(void __iomem *addr, u32 offset, u64 value)
+{
+	memcpy_toio(addr + offset, &value, sizeof(value));
+}
+EXPORT_SYMBOL_GPL(sst_shim32_write64);
+
+u64 sst_shim32_read64(void __iomem *addr, u32 offset)
+{
+	u64 val;
+
+	memcpy_fromio(&val, addr + offset, sizeof(val));
+	return val;
+}
+EXPORT_SYMBOL_GPL(sst_shim32_read64);
+
+static inline void _sst_memcpy_toio_32(volatile u32 __iomem *dest,
+	u32 *src, size_t bytes)
+{
+	int i, words = bytes >> 2;
+
+	for (i = 0; i < words; i++)
+		writel(src[i], dest + i);
+}
+
+static inline void _sst_memcpy_fromio_32(u32 *dest,
+	const volatile __iomem u32 *src, size_t bytes)
+{
+	int i, words = bytes >> 2;
+
+	for (i = 0; i < words; i++)
+		dest[i] = readl(src + i);
+}
+
+void sst_memcpy_toio_32(struct sst_dsp *sst,
+	void __iomem *dest, void *src, size_t bytes)
+{
+	_sst_memcpy_toio_32(dest, src, bytes);
+}
+EXPORT_SYMBOL_GPL(sst_memcpy_toio_32);
+
+void sst_memcpy_fromio_32(struct sst_dsp *sst, void *dest,
+	void __iomem *src, size_t bytes)
+{
+	_sst_memcpy_fromio_32(dest, src, bytes);
+}
+EXPORT_SYMBOL_GPL(sst_memcpy_fromio_32);
+
+/* Public API */
+void sst_dsp_shim_write(struct sst_dsp *sst, u32 offset, u32 value)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&sst->spinlock, flags);
+	sst->ops->write(sst->addr.shim, offset, value);
+	spin_unlock_irqrestore(&sst->spinlock, flags);
+}
+EXPORT_SYMBOL_GPL(sst_dsp_shim_write);
+
+u32 sst_dsp_shim_read(struct sst_dsp *sst, u32 offset)
+{
+	unsigned long flags;
+	u32 val;
+
+	spin_lock_irqsave(&sst->spinlock, flags);
+	val = sst->ops->read(sst->addr.shim, offset);
+	spin_unlock_irqrestore(&sst->spinlock, flags);
+
+	return val;
+}
+EXPORT_SYMBOL_GPL(sst_dsp_shim_read);
+
+void sst_dsp_shim_write64(struct sst_dsp *sst, u32 offset, u64 value)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&sst->spinlock, flags);
+	sst->ops->write64(sst->addr.shim, offset, value);
+	spin_unlock_irqrestore(&sst->spinlock, flags);
+}
+EXPORT_SYMBOL_GPL(sst_dsp_shim_write64);
+
+u64 sst_dsp_shim_read64(struct sst_dsp *sst, u32 offset)
+{
+	unsigned long flags;
+	u64 val;
+
+	spin_lock_irqsave(&sst->spinlock, flags);
+	val = sst->ops->read64(sst->addr.shim, offset);
+	spin_unlock_irqrestore(&sst->spinlock, flags);
+
+	return val;
+}
+EXPORT_SYMBOL_GPL(sst_dsp_shim_read64);
+
+void sst_dsp_shim_write_unlocked(struct sst_dsp *sst, u32 offset, u32 value)
+{
+	sst->ops->write(sst->addr.shim, offset, value);
+}
+EXPORT_SYMBOL_GPL(sst_dsp_shim_write_unlocked);
+
+u32 sst_dsp_shim_read_unlocked(struct sst_dsp *sst, u32 offset)
+{
+	return sst->ops->read(sst->addr.shim, offset);
+}
+EXPORT_SYMBOL_GPL(sst_dsp_shim_read_unlocked);
+
+void sst_dsp_shim_write64_unlocked(struct sst_dsp *sst, u32 offset, u64 value)
+{
+	sst->ops->write64(sst->addr.shim, offset, value);
+}
+EXPORT_SYMBOL_GPL(sst_dsp_shim_write64_unlocked);
+
+u64 sst_dsp_shim_read64_unlocked(struct sst_dsp *sst, u32 offset)
+{
+	return sst->ops->read64(sst->addr.shim, offset);
+}
+EXPORT_SYMBOL_GPL(sst_dsp_shim_read64_unlocked);
+
+int sst_dsp_shim_update_bits_unlocked(struct sst_dsp *sst, u32 offset,
+				u32 mask, u32 value)
+{
+	bool change;
+	unsigned int old, new;
+	u32 ret;
+
+	ret = sst_dsp_shim_read_unlocked(sst, offset);
+
+	old = ret;
+	new = (old & (~mask)) | (value & mask);
+
+	change = (old != new);
+	if (change)
+		sst_dsp_shim_write_unlocked(sst, offset, new);
+
+	return change;
+}
+EXPORT_SYMBOL_GPL(sst_dsp_shim_update_bits_unlocked);
+
+int sst_dsp_shim_update_bits64_unlocked(struct sst_dsp *sst, u32 offset,
+				u64 mask, u64 value)
+{
+	bool change;
+	u64 old, new;
+
+	old = sst_dsp_shim_read64_unlocked(sst, offset);
+
+	new = (old & (~mask)) | (value & mask);
+
+	change = (old != new);
+	if (change)
+		sst_dsp_shim_write64_unlocked(sst, offset, new);
+
+	return change;
+}
+EXPORT_SYMBOL_GPL(sst_dsp_shim_update_bits64_unlocked);
+
+int sst_dsp_shim_update_bits(struct sst_dsp *sst, u32 offset,
+				u32 mask, u32 value)
+{
+	unsigned long flags;
+	bool change;
+
+	spin_lock_irqsave(&sst->spinlock, flags);
+	change = sst_dsp_shim_update_bits_unlocked(sst, offset, mask, value);
+	spin_unlock_irqrestore(&sst->spinlock, flags);
+	return change;
+}
+EXPORT_SYMBOL_GPL(sst_dsp_shim_update_bits);
+
+int sst_dsp_shim_update_bits64(struct sst_dsp *sst, u32 offset,
+				u64 mask, u64 value)
+{
+	unsigned long flags;
+	bool change;
+
+	spin_lock_irqsave(&sst->spinlock, flags);
+	change = sst_dsp_shim_update_bits64_unlocked(sst, offset, mask, value);
+	spin_unlock_irqrestore(&sst->spinlock, flags);
+	return change;
+}
+EXPORT_SYMBOL_GPL(sst_dsp_shim_update_bits64);
+
+void sst_dsp_dump(struct sst_dsp *sst)
+{
+	sst->ops->dump(sst);
+}
+EXPORT_SYMBOL_GPL(sst_dsp_dump);
+
+void sst_dsp_reset(struct sst_dsp *sst)
+{
+	sst->ops->reset(sst);
+}
+EXPORT_SYMBOL_GPL(sst_dsp_reset);
+
+int sst_dsp_boot(struct sst_dsp *sst)
+{
+	sst->ops->boot(sst);
+	return 0;
+}
+EXPORT_SYMBOL_GPL(sst_dsp_boot);
+
+void sst_dsp_ipc_msg_tx(struct sst_dsp *dsp, u32 msg)
+{
+	sst_dsp_shim_write_unlocked(dsp, SST_IPCX, msg | SST_IPCX_BUSY);
+	trace_sst_ipc_msg_tx(msg);
+}
+EXPORT_SYMBOL_GPL(sst_dsp_ipc_msg_tx);
+
+u32 sst_dsp_ipc_msg_rx(struct sst_dsp *dsp)
+{
+	u32 msg;
+
+	msg = sst_dsp_shim_read_unlocked(dsp, SST_IPCX);
+	trace_sst_ipc_msg_rx(msg);
+
+	return msg;
+}
+EXPORT_SYMBOL_GPL(sst_dsp_ipc_msg_rx);
+
+int sst_dsp_mailbox_init(struct sst_dsp *sst, u32 inbox_offset, size_t inbox_size,
+	u32 outbox_offset, size_t outbox_size)
+{
+	sst->mailbox.in_base = sst->addr.lpe + inbox_offset;
+	sst->mailbox.out_base = sst->addr.lpe + outbox_offset;
+	sst->mailbox.in_size = inbox_size;
+	sst->mailbox.out_size = outbox_size;
+	return 0;
+}
+EXPORT_SYMBOL_GPL(sst_dsp_mailbox_init);
+
+void sst_dsp_outbox_write(struct sst_dsp *sst, void *message, size_t bytes)
+{
+	u32 i;
+
+	trace_sst_ipc_outbox_write(bytes);
+
+	memcpy_toio(sst->mailbox.out_base, message, bytes);
+
+	for (i = 0; i < bytes; i += 4)
+		trace_sst_ipc_outbox_wdata(i, *(u32 *)(message + i));
+}
+EXPORT_SYMBOL_GPL(sst_dsp_outbox_write);
+
+void sst_dsp_outbox_read(struct sst_dsp *sst, void *message, size_t bytes)
+{
+	u32 i;
+
+	trace_sst_ipc_outbox_read(bytes);
+
+	memcpy_fromio(message, sst->mailbox.out_base, bytes);
+
+	for (i = 0; i < bytes; i += 4)
+		trace_sst_ipc_outbox_rdata(i, *(u32 *)(message + i));
+}
+EXPORT_SYMBOL_GPL(sst_dsp_outbox_read);
+
+void sst_dsp_inbox_write(struct sst_dsp *sst, void *message, size_t bytes)
+{
+	u32 i;
+
+	trace_sst_ipc_inbox_write(bytes);
+
+	memcpy_toio(sst->mailbox.in_base, message, bytes);
+
+	for (i = 0; i < bytes; i += 4)
+		trace_sst_ipc_inbox_wdata(i, *(u32 *)(message + i));
+}
+EXPORT_SYMBOL_GPL(sst_dsp_inbox_write);
+
+void sst_dsp_inbox_read(struct sst_dsp *sst, void *message, size_t bytes)
+{
+	u32 i;
+
+	trace_sst_ipc_inbox_read(bytes);
+
+	memcpy_fromio(message, sst->mailbox.in_base, bytes);
+
+	for (i = 0; i < bytes; i += 4)
+		trace_sst_ipc_inbox_rdata(i, *(u32 *)(message + i));
+}
+EXPORT_SYMBOL_GPL(sst_dsp_inbox_read);
+
+struct sst_dsp *sst_dsp_new(struct device *dev,
+	struct sst_dsp_device *sst_dev, struct sst_pdata *pdata)
+{
+	struct sst_dsp *sst;
+	int err;
+
+	dev_dbg(dev, "initialising audio DSP id 0x%x\n", pdata->id);
+
+	sst = devm_kzalloc(dev, sizeof(*sst), GFP_KERNEL);
+	if (sst == NULL)
+		return NULL;
+
+	spin_lock_init(&sst->spinlock);
+	mutex_init(&sst->mutex);
+	sst->dev = dev;
+	sst->thread_context = sst_dev->thread_context;
+	sst->sst_dev = sst_dev;
+	sst->id = pdata->id;
+	sst->irq = pdata->irq;
+	sst->ops = sst_dev->ops;
+	sst->pdata = pdata;
+	INIT_LIST_HEAD(&sst->used_block_list);
+	INIT_LIST_HEAD(&sst->free_block_list);
+	INIT_LIST_HEAD(&sst->module_list);
+	INIT_LIST_HEAD(&sst->fw_list);
+
+	/* Initialise SST Audio DSP */
+	if (sst->ops->init) {
+		err = sst->ops->init(sst, pdata);
+		if (err < 0)
+			return NULL;
+	}
+
+	/* Register the ISR */
+	err = request_threaded_irq(sst->irq, sst->ops->irq_handler,
+		sst_dev->thread, IRQF_SHARED, "AudioDSP", sst);
+	if (err)
+		goto irq_err;
+
+	return sst;
+
+irq_err:
+	if (sst->ops->free)
+		sst->ops->free(sst);
+
+	return NULL;
+}
+EXPORT_SYMBOL_GPL(sst_dsp_new);
+
+void sst_dsp_free(struct sst_dsp *sst)
+{
+	free_irq(sst->irq, sst);
+	if (sst->ops->free)
+		sst->ops->free(sst);
+}
+EXPORT_SYMBOL_GPL(sst_dsp_free);
+
+/* Module information */
+MODULE_AUTHOR("Liam Girdwood");
+MODULE_DESCRIPTION("Intel SST Core");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/intel/sst-dsp.h b/sound/soc/intel/sst-dsp.h
new file mode 100644
index 000000000000..74052b59485c
--- /dev/null
+++ b/sound/soc/intel/sst-dsp.h
@@ -0,0 +1,233 @@
+/*
+ * Intel Smart Sound Technology (SST) Core
+ *
+ * Copyright (C) 2013, Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License version
+ * 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef __SOUND_SOC_SST_DSP_H
+#define __SOUND_SOC_SST_DSP_H
+
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/interrupt.h>
+
+/* SST Device IDs  */
+#define SST_DEV_ID_LYNX_POINT		0x33C8
+#define SST_DEV_ID_WILDCAT_POINT	0x3438
+#define SST_DEV_ID_BYT			0x0F28
+
+/* Supported SST DMA Devices */
+#define SST_DMA_TYPE_DW		1
+#define SST_DMA_TYPE_MID	2
+
+/* SST Shim register map
+ * The register naming can differ between products. Some products also
+ * contain extra functionality.
+ */
+#define SST_CSR			0x00
+#define SST_PISR		0x08
+#define SST_PIMR		0x10
+#define SST_ISRX		0x18
+#define SST_ISRD		0x20
+#define SST_IMRX		0x28
+#define SST_IMRD		0x30
+#define SST_IPCX		0x38 /* IPC IA -> SST */
+#define SST_IPCD		0x40 /* IPC SST -> IA */
+#define SST_ISRSC		0x48
+#define SST_ISRLPESC		0x50
+#define SST_IMRSC		0x58
+#define SST_IMRLPESC		0x60
+#define SST_IPCSC		0x68
+#define SST_IPCLPESC		0x70
+#define SST_CLKCTL		0x78
+#define SST_CSR2		0x80
+#define SST_LTRC		0xE0
+#define SST_HDMC		0xE8
+#define SST_DBGO		0xF0
+
+#define SST_SHIM_SIZE		0x100
+#define SST_PWMCTRL             0x1000
+
+/* SST Shim Register bits
+ * The register bit naming can differ between products. Some products also
+ * contain extra functionality.
+ */
+
+/* CSR / CS */
+#define SST_CSR_RST		(0x1 << 1)
+#define SST_CSR_SBCS0		(0x1 << 2)
+#define SST_CSR_SBCS1		(0x1 << 3)
+#define SST_CSR_DCS(x)		(x << 4)
+#define SST_CSR_DCS_MASK	(0x7 << 4)
+#define SST_CSR_STALL		(0x1 << 10)
+#define SST_CSR_S0IOCS		(0x1 << 21)
+#define SST_CSR_S1IOCS		(0x1 << 23)
+#define SST_CSR_LPCS		(0x1 << 31)
+#define SST_BYT_CSR_RST		(0x1 << 0)
+#define SST_BYT_CSR_VECTOR_SEL	(0x1 << 1)
+#define SST_BYT_CSR_STALL	(0x1 << 2)
+#define SST_BYT_CSR_PWAITMODE	(0x1 << 3)
+
+/*  ISRX / ISC */
+#define SST_ISRX_BUSY		(0x1 << 1)
+#define SST_ISRX_DONE		(0x1 << 0)
+#define SST_BYT_ISRX_REQUEST	(0x1 << 1)
+
+/*  ISRD / ISD */
+#define SST_ISRD_BUSY		(0x1 << 1)
+#define SST_ISRD_DONE		(0x1 << 0)
+
+/* IMRX / IMC */
+#define SST_IMRX_BUSY		(0x1 << 1)
+#define SST_IMRX_DONE		(0x1 << 0)
+#define SST_BYT_IMRX_REQUEST	(0x1 << 1)
+
+/*  IPCX / IPCC */
+#define	SST_IPCX_DONE		(0x1 << 30)
+#define	SST_IPCX_BUSY		(0x1 << 31)
+#define SST_BYT_IPCX_DONE	((u64)0x1 << 62)
+#define SST_BYT_IPCX_BUSY	((u64)0x1 << 63)
+
+/*  IPCD */
+#define	SST_IPCD_DONE		(0x1 << 30)
+#define	SST_IPCD_BUSY		(0x1 << 31)
+#define SST_BYT_IPCD_DONE	((u64)0x1 << 62)
+#define SST_BYT_IPCD_BUSY	((u64)0x1 << 63)
+
+/* CLKCTL */
+#define SST_CLKCTL_SMOS(x)	(x << 24)
+#define SST_CLKCTL_MASK		(3 << 24)
+#define SST_CLKCTL_DCPLCG	(1 << 18)
+#define SST_CLKCTL_SCOE1	(1 << 17)
+#define SST_CLKCTL_SCOE0	(1 << 16)
+
+/* CSR2 / CS2 */
+#define SST_CSR2_SDFD_SSP0	(1 << 1)
+#define SST_CSR2_SDFD_SSP1	(1 << 2)
+
+/* LTRC */
+#define SST_LTRC_VAL(x)		(x << 0)
+
+/* HDMC */
+#define SST_HDMC_HDDA0(x)	(x << 0)
+#define SST_HDMC_HDDA1(x)	(x << 7)
+
+
+/* SST Vendor Defined Registers and bits */
+#define SST_VDRTCTL0		0xa0
+#define SST_VDRTCTL1		0xa4
+#define SST_VDRTCTL2		0xa8
+#define SST_VDRTCTL3		0xaC
+
+/* VDRTCTL0 */
+#define SST_VDRTCL0_DSRAMPGE_SHIFT	16
+#define SST_VDRTCL0_DSRAMPGE_MASK	(0xffff << SST_VDRTCL0_DSRAMPGE_SHIFT)
+#define SST_VDRTCL0_ISRAMPGE_SHIFT	6
+#define SST_VDRTCL0_ISRAMPGE_MASK	(0x3ff << SST_VDRTCL0_ISRAMPGE_SHIFT)
+
+struct sst_dsp;
+
+/*
+ * SST Device.
+ *
+ * This structure is populated by the SST core driver.
+ */
+struct sst_dsp_device {
+	/* Mandatory fields */
+	struct sst_ops *ops;
+	irqreturn_t (*thread)(int irq, void *context);
+	void *thread_context;
+};
+
+/*
+ * SST Platform Data.
+ */
+struct sst_pdata {
+	/* ACPI data */
+	u32 lpe_base;
+	u32 lpe_size;
+	u32 pcicfg_base;
+	u32 pcicfg_size;
+	u32 fw_base;
+	u32 fw_size;
+	int irq;
+
+	/* Firmware */
+	const struct firmware *fw;
+
+	/* DMA */
+	u32 dma_base;
+	u32 dma_size;
+	int dma_engine;
+
+	/* DSP */
+	u32 id;
+	void *dsp;
+};
+
+/* Initialization */
+struct sst_dsp *sst_dsp_new(struct device *dev,
+	struct sst_dsp_device *sst_dev, struct sst_pdata *pdata);
+void sst_dsp_free(struct sst_dsp *sst);
+
+/* SHIM Read / Write */
+void sst_dsp_shim_write(struct sst_dsp *sst, u32 offset, u32 value);
+u32 sst_dsp_shim_read(struct sst_dsp *sst, u32 offset);
+int sst_dsp_shim_update_bits(struct sst_dsp *sst, u32 offset,
+				u32 mask, u32 value);
+void sst_dsp_shim_write64(struct sst_dsp *sst, u32 offset, u64 value);
+u64 sst_dsp_shim_read64(struct sst_dsp *sst, u32 offset);
+int sst_dsp_shim_update_bits64(struct sst_dsp *sst, u32 offset,
+				u64 mask, u64 value);
+
+/* SHIM Read / Write Unlocked for callers already holding sst lock */
+void sst_dsp_shim_write_unlocked(struct sst_dsp *sst, u32 offset, u32 value);
+u32 sst_dsp_shim_read_unlocked(struct sst_dsp *sst, u32 offset);
+int sst_dsp_shim_update_bits_unlocked(struct sst_dsp *sst, u32 offset,
+				u32 mask, u32 value);
+void sst_dsp_shim_write64_unlocked(struct sst_dsp *sst, u32 offset, u64 value);
+u64 sst_dsp_shim_read64_unlocked(struct sst_dsp *sst, u32 offset);
+int sst_dsp_shim_update_bits64_unlocked(struct sst_dsp *sst, u32 offset,
+					u64 mask, u64 value);
+
+/* Internal generic low-level SST IO functions - can be overidden */
+void sst_shim32_write(void __iomem *addr, u32 offset, u32 value);
+u32 sst_shim32_read(void __iomem *addr, u32 offset);
+void sst_shim32_write64(void __iomem *addr, u32 offset, u64 value);
+u64 sst_shim32_read64(void __iomem *addr, u32 offset);
+void sst_memcpy_toio_32(struct sst_dsp *sst,
+			void __iomem *dest, void *src, size_t bytes);
+void sst_memcpy_fromio_32(struct sst_dsp *sst,
+			  void *dest, void __iomem *src, size_t bytes);
+
+/* DSP reset & boot */
+void sst_dsp_reset(struct sst_dsp *sst);
+int sst_dsp_boot(struct sst_dsp *sst);
+
+/* Msg IO */
+void sst_dsp_ipc_msg_tx(struct sst_dsp *dsp, u32 msg);
+u32 sst_dsp_ipc_msg_rx(struct sst_dsp *dsp);
+
+/* Mailbox management */
+int sst_dsp_mailbox_init(struct sst_dsp *dsp, u32 inbox_offset,
+	size_t inbox_size, u32 outbox_offset, size_t outbox_size);
+void sst_dsp_inbox_write(struct sst_dsp *dsp, void *message, size_t bytes);
+void sst_dsp_inbox_read(struct sst_dsp *dsp, void *message, size_t bytes);
+void sst_dsp_outbox_write(struct sst_dsp *dsp, void *message, size_t bytes);
+void sst_dsp_outbox_read(struct sst_dsp *dsp, void *message, size_t bytes);
+void sst_dsp_mailbox_dump(struct sst_dsp *dsp, size_t bytes);
+
+/* Debug */
+void sst_dsp_dump(struct sst_dsp *sst);
+
+#endif
diff --git a/sound/soc/intel/sst-firmware.c b/sound/soc/intel/sst-firmware.c
new file mode 100644
index 000000000000..f7687107cf7f
--- /dev/null
+++ b/sound/soc/intel/sst-firmware.c
@@ -0,0 +1,587 @@
+/*
+ * Intel SST Firmware Loader
+ *
+ * Copyright (C) 2013, Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License version
+ * 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/sched.h>
+#include <linux/firmware.h>
+#include <linux/export.h>
+#include <linux/platform_device.h>
+#include <linux/dma-mapping.h>
+#include <linux/dmaengine.h>
+#include <linux/pci.h>
+
+#include <asm/page.h>
+#include <asm/pgtable.h>
+
+#include "sst-dsp.h"
+#include "sst-dsp-priv.h"
+
+static void sst_memcpy32(volatile void __iomem *dest, void *src, u32 bytes)
+{
+	u32 i;
+
+	/* copy one 32 bit word at a time as 64 bit access is not supported */
+	for (i = 0; i < bytes; i += 4)
+		memcpy_toio(dest + i, src + i, 4);
+}
+
+/* create new generic firmware object */
+struct sst_fw *sst_fw_new(struct sst_dsp *dsp, 
+	const struct firmware *fw, void *private)
+{
+	struct sst_fw *sst_fw;
+	int err;
+
+	if (!dsp->ops->parse_fw)
+		return NULL;
+
+	sst_fw = kzalloc(sizeof(*sst_fw), GFP_KERNEL);
+	if (sst_fw == NULL)
+		return NULL;
+
+	sst_fw->dsp = dsp;
+	sst_fw->private = private;
+	sst_fw->size = fw->size;
+
+	err = dma_coerce_mask_and_coherent(dsp->dev, DMA_BIT_MASK(32));
+	if (err < 0) {
+		kfree(sst_fw);
+		return NULL;
+	}
+
+	/* allocate DMA buffer to store FW data */
+	sst_fw->dma_buf = dma_alloc_coherent(dsp->dev, sst_fw->size,
+				&sst_fw->dmable_fw_paddr, GFP_DMA | GFP_KERNEL);
+	if (!sst_fw->dma_buf) {
+		dev_err(dsp->dev, "error: DMA alloc failed\n");
+		kfree(sst_fw);
+		return NULL;
+	}
+
+	/* copy FW data to DMA-able memory */
+	memcpy((void *)sst_fw->dma_buf, (void *)fw->data, fw->size);
+
+	/* call core specific FW paser to load FW data into DSP */
+	err = dsp->ops->parse_fw(sst_fw);
+	if (err < 0) {
+		dev_err(dsp->dev, "error: parse fw failed %d\n", err);
+		goto parse_err;
+	}
+
+	mutex_lock(&dsp->mutex);
+	list_add(&sst_fw->list, &dsp->fw_list);
+	mutex_unlock(&dsp->mutex);
+
+	return sst_fw;
+
+parse_err:
+	dma_free_coherent(dsp->dev, sst_fw->size,
+				sst_fw->dma_buf,
+				sst_fw->dmable_fw_paddr);
+	kfree(sst_fw);
+	return NULL;
+}
+EXPORT_SYMBOL_GPL(sst_fw_new);
+
+/* free single firmware object */
+void sst_fw_free(struct sst_fw *sst_fw)
+{
+	struct sst_dsp *dsp = sst_fw->dsp;
+
+	mutex_lock(&dsp->mutex);
+	list_del(&sst_fw->list);
+	mutex_unlock(&dsp->mutex);
+
+	dma_free_coherent(dsp->dev, sst_fw->size, sst_fw->dma_buf,
+			sst_fw->dmable_fw_paddr);
+	kfree(sst_fw);
+}
+EXPORT_SYMBOL_GPL(sst_fw_free);
+
+/* free all firmware objects */
+void sst_fw_free_all(struct sst_dsp *dsp)
+{
+	struct sst_fw *sst_fw, *t;
+
+	mutex_lock(&dsp->mutex);
+	list_for_each_entry_safe(sst_fw, t, &dsp->fw_list, list) {
+
+		list_del(&sst_fw->list);
+		dma_free_coherent(dsp->dev, sst_fw->size, sst_fw->dma_buf,
+			sst_fw->dmable_fw_paddr);
+		kfree(sst_fw);
+	}
+	mutex_unlock(&dsp->mutex);
+}
+EXPORT_SYMBOL_GPL(sst_fw_free_all);
+
+/* create a new SST generic module from FW template */
+struct sst_module *sst_module_new(struct sst_fw *sst_fw,
+	struct sst_module_template *template, void *private)
+{
+	struct sst_dsp *dsp = sst_fw->dsp;
+	struct sst_module *sst_module;
+
+	sst_module = kzalloc(sizeof(*sst_module), GFP_KERNEL);
+	if (sst_module == NULL)
+		return NULL;
+
+	sst_module->id = template->id;
+	sst_module->dsp = dsp;
+	sst_module->sst_fw = sst_fw;
+
+	memcpy(&sst_module->s, &template->s, sizeof(struct sst_module_data));
+	memcpy(&sst_module->p, &template->p, sizeof(struct sst_module_data));
+
+	INIT_LIST_HEAD(&sst_module->block_list);
+
+	mutex_lock(&dsp->mutex);
+	list_add(&sst_module->list, &dsp->module_list);
+	mutex_unlock(&dsp->mutex);
+
+	return sst_module;
+}
+EXPORT_SYMBOL_GPL(sst_module_new);
+
+/* free firmware module and remove from available list */
+void sst_module_free(struct sst_module *sst_module)
+{
+	struct sst_dsp *dsp = sst_module->dsp;
+
+	mutex_lock(&dsp->mutex);
+	list_del(&sst_module->list);
+	mutex_unlock(&dsp->mutex);
+
+	kfree(sst_module);
+}
+EXPORT_SYMBOL_GPL(sst_module_free);
+
+static struct sst_mem_block *find_block(struct sst_dsp *dsp, int type,
+	u32 offset)
+{
+	struct sst_mem_block *block;
+
+	list_for_each_entry(block, &dsp->free_block_list, list) {
+		if (block->type == type && block->offset == offset)
+			return block;
+	}
+
+	return NULL;
+}
+
+static int block_alloc_contiguous(struct sst_module *module,
+	struct sst_module_data *data, u32 offset, int size)
+{
+	struct list_head tmp = LIST_HEAD_INIT(tmp);
+	struct sst_dsp *dsp = module->dsp;
+	struct sst_mem_block *block;
+
+	while (size > 0) {
+		block = find_block(dsp, data->type, offset);
+		if (!block) {
+			list_splice(&tmp, &dsp->free_block_list);
+			return -ENOMEM;
+		}
+
+		list_move_tail(&block->list, &tmp);
+		offset += block->size;
+		size -= block->size;
+	}
+
+	list_splice(&tmp, &dsp->used_block_list);
+	return 0;
+}
+
+/* allocate free DSP blocks for module data - callers hold locks */
+static int block_alloc(struct sst_module *module,
+	struct sst_module_data *data)
+{
+	struct sst_dsp *dsp = module->dsp;
+	struct sst_mem_block *block, *tmp;
+	int ret = 0;
+
+	if (data->size == 0)
+		return 0;
+
+	/* find first free whole blocks that can hold module */
+	list_for_each_entry_safe(block, tmp, &dsp->free_block_list, list) {
+
+		/* ignore blocks with wrong type */
+		if (block->type != data->type)
+			continue;
+
+		if (data->size > block->size)
+			continue;
+
+		data->offset = block->offset;
+		block->data_type = data->data_type;
+		block->bytes_used = data->size % block->size;
+		list_add(&block->module_list, &module->block_list);
+		list_move(&block->list, &dsp->used_block_list);
+		dev_dbg(dsp->dev, " *module %d added block %d:%d\n",
+			module->id, block->type, block->index);
+		return 0;
+	}
+
+	/* then find free multiple blocks that can hold module */
+	list_for_each_entry_safe(block, tmp, &dsp->free_block_list, list) {
+
+		/* ignore blocks with wrong type */
+		if (block->type != data->type)
+			continue;
+
+		/* do we span > 1 blocks */
+		if (data->size > block->size) {
+			ret = block_alloc_contiguous(module, data,
+				block->offset + block->size,
+				data->size - block->size);
+			if (ret == 0)
+				return ret;
+		}
+	}
+
+	/* not enough free block space */
+	return -ENOMEM;
+}
+
+/* remove module from memory - callers hold locks */
+static void block_module_remove(struct sst_module *module)
+{
+	struct sst_mem_block *block, *tmp;
+	struct sst_dsp *dsp = module->dsp;
+	int err;
+
+	/* disable each block  */
+	list_for_each_entry(block, &module->block_list, module_list) {
+
+		if (block->ops && block->ops->disable) {
+			err = block->ops->disable(block);
+			if (err < 0)
+				dev_err(dsp->dev,
+					"error: cant disable block %d:%d\n",
+					block->type, block->index);
+		}
+	}
+
+	/* mark each block as free */
+	list_for_each_entry_safe(block, tmp, &module->block_list, module_list) {
+		list_del(&block->module_list);
+		list_move(&block->list, &dsp->free_block_list);
+	}
+}
+
+/* prepare the memory block to receive data from host - callers hold locks */
+static int block_module_prepare(struct sst_module *module)
+{
+	struct sst_mem_block *block;
+	int ret = 0;
+
+	/* enable each block so that's it'e ready for module P/S data */
+	list_for_each_entry(block, &module->block_list, module_list) {
+
+		if (block->ops && block->ops->enable) {
+			ret = block->ops->enable(block);
+			if (ret < 0) {
+				dev_err(module->dsp->dev,
+					"error: cant disable block %d:%d\n",
+					block->type, block->index);
+				goto err;
+			}
+		}
+	}
+	return ret;
+
+err:
+	list_for_each_entry(block, &module->block_list, module_list) {
+		if (block->ops && block->ops->disable)
+			block->ops->disable(block);
+	}
+	return ret;
+}
+
+/* allocate memory blocks for static module addresses - callers hold locks */
+static int block_alloc_fixed(struct sst_module *module,
+	struct sst_module_data *data)
+{
+	struct sst_dsp *dsp = module->dsp;
+	struct sst_mem_block *block, *tmp;
+	u32 end = data->offset + data->size, block_end;
+	int err;
+
+	/* only IRAM/DRAM blocks are managed */
+	if (data->type != SST_MEM_IRAM && data->type != SST_MEM_DRAM)
+		return 0;
+
+	/* are blocks already attached to this module */
+	list_for_each_entry_safe(block, tmp, &module->block_list, module_list) {
+
+		/* force compacting mem blocks of the same data_type */
+		if (block->data_type != data->data_type)
+			continue;
+
+		block_end = block->offset + block->size;
+
+		/* find block that holds section */
+		if (data->offset >= block->offset && end < block_end)
+			return 0;
+
+		/* does block span more than 1 section */
+		if (data->offset >= block->offset && data->offset < block_end) {
+
+			err = block_alloc_contiguous(module, data,
+				block->offset + block->size,
+				data->size - block->size + data->offset - block->offset);
+			if (err < 0)
+				return -ENOMEM;
+
+			/* module already owns blocks */
+			return 0;
+		}
+	}
+
+	/* find first free blocks that can hold section in free list */
+	list_for_each_entry_safe(block, tmp, &dsp->free_block_list, list) {
+		block_end = block->offset + block->size;
+
+		/* find block that holds section */
+		if (data->offset >= block->offset && end < block_end) {
+
+			/* add block */
+			block->data_type = data->data_type;
+			list_move(&block->list, &dsp->used_block_list);
+			list_add(&block->module_list, &module->block_list);
+			return 0;
+		}
+
+		/* does block span more than 1 section */
+		if (data->offset >= block->offset && data->offset < block_end) {
+
+			err = block_alloc_contiguous(module, data,
+				block->offset + block->size,
+				data->size - block->size);
+			if (err < 0)
+				return -ENOMEM;
+
+			/* add block */
+			block->data_type = data->data_type;
+			list_move(&block->list, &dsp->used_block_list);
+			list_add(&block->module_list, &module->block_list);
+			return 0;
+		}
+
+	}
+
+	return -ENOMEM;
+}
+
+/* Load fixed module data into DSP memory blocks */
+int sst_module_insert_fixed_block(struct sst_module *module,
+	struct sst_module_data *data)
+{
+	struct sst_dsp *dsp = module->dsp;
+	int ret;
+
+	mutex_lock(&dsp->mutex);
+
+	/* alloc blocks that includes this section */
+	ret = block_alloc_fixed(module, data);
+	if (ret < 0) {
+		dev_err(dsp->dev,
+			"error: no free blocks for section at offset 0x%x size 0x%x\n",
+			data->offset, data->size);
+		mutex_unlock(&dsp->mutex);
+		return -ENOMEM;
+	}
+
+	/* prepare DSP blocks for module copy */
+	ret = block_module_prepare(module);
+	if (ret < 0) {
+		dev_err(dsp->dev, "error: fw module prepare failed\n");
+		goto err;
+	}
+
+	/* copy partial module data to blocks */
+	sst_memcpy32(dsp->addr.lpe + data->offset, data->data, data->size);
+
+	mutex_unlock(&dsp->mutex);
+	return ret;
+
+err:
+	block_module_remove(module);
+	mutex_unlock(&dsp->mutex);
+	return ret;
+}
+EXPORT_SYMBOL_GPL(sst_module_insert_fixed_block);
+
+/* Unload entire module from DSP memory */
+int sst_block_module_remove(struct sst_module *module)
+{
+	struct sst_dsp *dsp = module->dsp;
+
+	mutex_lock(&dsp->mutex);
+	block_module_remove(module);
+	mutex_unlock(&dsp->mutex);
+	return 0;
+}
+EXPORT_SYMBOL_GPL(sst_block_module_remove);
+
+/* register a DSP memory block for use with FW based modules */
+struct sst_mem_block *sst_mem_block_register(struct sst_dsp *dsp, u32 offset,
+	u32 size, enum sst_mem_type type, struct sst_block_ops *ops, u32 index,
+	void *private)
+{
+	struct sst_mem_block *block;
+
+	block = kzalloc(sizeof(*block), GFP_KERNEL);
+	if (block == NULL)
+		return NULL;
+
+	block->offset = offset;
+	block->size = size;
+	block->index = index;
+	block->type = type;
+	block->dsp = dsp;
+	block->private = private;
+	block->ops = ops;
+
+	mutex_lock(&dsp->mutex);
+	list_add(&block->list, &dsp->free_block_list);
+	mutex_unlock(&dsp->mutex);
+
+	return block;
+}
+EXPORT_SYMBOL_GPL(sst_mem_block_register);
+
+/* unregister all DSP memory blocks */
+void sst_mem_block_unregister_all(struct sst_dsp *dsp)
+{
+	struct sst_mem_block *block, *tmp;
+
+	mutex_lock(&dsp->mutex);
+
+	/* unregister used blocks */
+	list_for_each_entry_safe(block, tmp, &dsp->used_block_list, list) {
+		list_del(&block->list);
+		kfree(block);
+	}
+
+	/* unregister free blocks */
+	list_for_each_entry_safe(block, tmp, &dsp->free_block_list, list) {
+		list_del(&block->list);
+		kfree(block);
+	}
+
+	mutex_unlock(&dsp->mutex);
+}
+EXPORT_SYMBOL_GPL(sst_mem_block_unregister_all);
+
+/* allocate scratch buffer blocks */
+struct sst_module *sst_mem_block_alloc_scratch(struct sst_dsp *dsp)
+{
+	struct sst_module *sst_module, *scratch;
+	struct sst_mem_block *block, *tmp;
+	u32 block_size;
+	int ret = 0;
+
+	scratch = kzalloc(sizeof(struct sst_module), GFP_KERNEL);
+	if (scratch == NULL)
+		return NULL;
+
+	mutex_lock(&dsp->mutex);
+
+	/* calculate required scratch size */
+	list_for_each_entry(sst_module, &dsp->module_list, list) {
+		if (scratch->s.size > sst_module->s.size)
+			scratch->s.size = scratch->s.size;
+		else
+			scratch->s.size = sst_module->s.size;
+	}
+
+	dev_dbg(dsp->dev, "scratch buffer required is %d bytes\n",
+		scratch->s.size);
+
+	/* init scratch module */
+	scratch->dsp = dsp;
+	scratch->s.type = SST_MEM_DRAM;
+	scratch->s.data_type = SST_DATA_S;
+	INIT_LIST_HEAD(&scratch->block_list);
+
+	/* check free blocks before looking at used blocks for space */
+	if (!list_empty(&dsp->free_block_list))
+		block = list_first_entry(&dsp->free_block_list,
+			struct sst_mem_block, list);
+	else
+		block = list_first_entry(&dsp->used_block_list,
+			struct sst_mem_block, list);
+	block_size = block->size;
+
+	/* allocate blocks for module scratch buffers */
+	dev_dbg(dsp->dev, "allocating scratch blocks\n");
+	ret = block_alloc(scratch, &scratch->s);
+	if (ret < 0) {
+		dev_err(dsp->dev, "error: can't alloc scratch blocks\n");
+		goto err;
+	}
+
+	/* assign the same offset of scratch to each module */
+	list_for_each_entry(sst_module, &dsp->module_list, list)
+		sst_module->s.offset = scratch->s.offset;
+
+	mutex_unlock(&dsp->mutex);
+	return scratch;
+
+err:
+	list_for_each_entry_safe(block, tmp, &scratch->block_list, module_list)
+		list_del(&block->module_list);
+	mutex_unlock(&dsp->mutex);
+	return NULL;
+}
+EXPORT_SYMBOL_GPL(sst_mem_block_alloc_scratch);
+
+/* free all scratch blocks */
+void sst_mem_block_free_scratch(struct sst_dsp *dsp,
+	struct sst_module *scratch)
+{
+	struct sst_mem_block *block, *tmp;
+
+	mutex_lock(&dsp->mutex);
+
+	list_for_each_entry_safe(block, tmp, &scratch->block_list, module_list)
+		list_del(&block->module_list);
+
+	mutex_unlock(&dsp->mutex);
+}
+EXPORT_SYMBOL_GPL(sst_mem_block_free_scratch);
+
+/* get a module from it's unique ID */
+struct sst_module *sst_module_get_from_id(struct sst_dsp *dsp, u32 id)
+{
+	struct sst_module *module;
+
+	mutex_lock(&dsp->mutex);
+
+	list_for_each_entry(module, &dsp->module_list, list) {
+		if (module->id == id) {
+			mutex_unlock(&dsp->mutex);
+			return module;
+		}
+	}
+
+	mutex_unlock(&dsp->mutex);
+	return NULL;
+}
+EXPORT_SYMBOL_GPL(sst_module_get_from_id);
diff --git a/sound/soc/intel/sst-haswell-dsp.c b/sound/soc/intel/sst-haswell-dsp.c
new file mode 100644
index 000000000000..f5ebf36af889
--- /dev/null
+++ b/sound/soc/intel/sst-haswell-dsp.c
@@ -0,0 +1,517 @@
+/*
+ * Intel Haswell SST DSP driver
+ *
+ * Copyright (C) 2013, Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License version
+ * 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/delay.h>
+#include <linux/fs.h>
+#include <linux/slab.h>
+#include <linux/device.h>
+#include <linux/sched.h>
+#include <linux/export.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/dma-mapping.h>
+#include <linux/platform_device.h>
+#include <linux/pci.h>
+#include <linux/firmware.h>
+#include <linux/pm_runtime.h>
+
+#include <linux/acpi.h>
+#include <acpi/acpi_bus.h>
+
+#include "sst-dsp.h"
+#include "sst-dsp-priv.h"
+#include "sst-haswell-ipc.h"
+
+#include <trace/events/hswadsp.h>
+
+#define SST_HSW_FW_SIGNATURE_SIZE	4
+#define SST_HSW_FW_SIGN			"$SST"
+#define SST_HSW_FW_LIB_SIGN		"$LIB"
+
+#define SST_WPT_SHIM_OFFSET	0xFB000
+#define SST_LP_SHIM_OFFSET	0xE7000
+#define SST_WPT_IRAM_OFFSET	0xA0000
+#define SST_LP_IRAM_OFFSET	0x80000
+
+#define SST_SHIM_PM_REG		0x84
+
+#define SST_HSW_IRAM	1
+#define SST_HSW_DRAM	2
+#define SST_HSW_REGS	3
+
+struct dma_block_info {
+	__le32 type;		/* IRAM/DRAM */
+	__le32 size;		/* Bytes */
+	__le32 ram_offset;	/* Offset in I/DRAM */
+	__le32 rsvd;		/* Reserved field */
+} __attribute__((packed));
+
+struct fw_module_info {
+	__le32 persistent_size;
+	__le32 scratch_size;
+} __attribute__((packed));
+
+struct fw_header {
+	unsigned char signature[SST_HSW_FW_SIGNATURE_SIZE]; /* FW signature */
+	__le32 file_size;		/* size of fw minus this header */
+	__le32 modules;		/*  # of modules */
+	__le32 file_format;	/* version of header format */
+	__le32 reserved[4];
+} __attribute__((packed));
+
+struct fw_module_header {
+	unsigned char signature[SST_HSW_FW_SIGNATURE_SIZE]; /* module signature */
+	__le32 mod_size;	/* size of module */
+	__le32 blocks;	/* # of blocks */
+	__le16 padding;
+	__le16 type;	/* codec type, pp lib */
+	__le32 entry_point;
+	struct fw_module_info info;
+} __attribute__((packed));
+
+static void hsw_free(struct sst_dsp *sst);
+
+static int hsw_parse_module(struct sst_dsp *dsp, struct sst_fw *fw,
+	struct fw_module_header *module)
+{
+	struct dma_block_info *block;
+	struct sst_module *mod;
+	struct sst_module_data block_data;
+	struct sst_module_template template;
+	int count;
+	void __iomem *ram;
+
+	/* TODO: allowed module types need to be configurable */
+	if (module->type != SST_HSW_MODULE_BASE_FW
+		&& module->type != SST_HSW_MODULE_PCM_SYSTEM
+		&& module->type != SST_HSW_MODULE_PCM
+		&& module->type != SST_HSW_MODULE_PCM_REFERENCE
+		&& module->type != SST_HSW_MODULE_PCM_CAPTURE
+		&& module->type != SST_HSW_MODULE_LPAL)
+		return 0;
+
+	dev_dbg(dsp->dev, "new module sign 0x%s size 0x%x blocks 0x%x type 0x%x\n",
+		module->signature, module->mod_size,
+		module->blocks, module->type);
+	dev_dbg(dsp->dev, " entrypoint 0x%x\n", module->entry_point);
+	dev_dbg(dsp->dev, " persistent 0x%x scratch 0x%x\n",
+		module->info.persistent_size, module->info.scratch_size);
+
+	memset(&template, 0, sizeof(template));
+	template.id = module->type;
+	template.entry = module->entry_point;
+	template.p.size = module->info.persistent_size;
+	template.p.type = SST_MEM_DRAM;
+	template.p.data_type = SST_DATA_P;
+	template.s.size = module->info.scratch_size;
+	template.s.type = SST_MEM_DRAM;
+	template.s.data_type = SST_DATA_S;
+
+	mod = sst_module_new(fw, &template, NULL);
+	if (mod == NULL)
+		return -ENOMEM;
+
+	block = (void *)module + sizeof(*module);
+
+	for (count = 0; count < module->blocks; count++) {
+
+		if (block->size <= 0) {
+			dev_err(dsp->dev,
+				"error: block %d size invalid\n", count);
+			sst_module_free(mod);
+			return -EINVAL;
+		}
+
+		switch (block->type) {
+		case SST_HSW_IRAM:
+			ram = dsp->addr.lpe;
+			block_data.offset =
+				block->ram_offset + dsp->addr.iram_offset;
+			block_data.type = SST_MEM_IRAM;
+			break;
+		case SST_HSW_DRAM:
+			ram = dsp->addr.lpe;
+			block_data.offset = block->ram_offset;
+			block_data.type = SST_MEM_DRAM;
+			break;
+		default:
+			dev_err(dsp->dev, "error: bad type 0x%x for block 0x%x\n",
+				block->type, count);
+			sst_module_free(mod);
+			return -EINVAL;
+		}
+
+		block_data.size = block->size;
+		block_data.data_type = SST_DATA_M;
+		block_data.data = (void *)block + sizeof(*block);
+		block_data.data_offset = block_data.data - fw->dma_buf;
+
+		dev_dbg(dsp->dev, "copy firmware block %d type 0x%x "
+			"size 0x%x ==> ram %p offset 0x%x\n",
+			count, block->type, block->size, ram,
+			block->ram_offset);
+
+		sst_module_insert_fixed_block(mod, &block_data);
+
+		block = (void *)block + sizeof(*block) + block->size;
+	}
+	return 0;
+}
+
+static int hsw_parse_fw_image(struct sst_fw *sst_fw)
+{
+	struct fw_header *header;
+	struct sst_module *scratch;
+	struct fw_module_header *module;
+	struct sst_dsp *dsp = sst_fw->dsp;
+	struct sst_hsw *hsw = sst_fw->private;
+	int ret, count;
+
+	/* Read the header information from the data pointer */
+	header = (struct fw_header *)sst_fw->dma_buf;
+
+	/* verify FW */
+	if ((strncmp(header->signature, SST_HSW_FW_SIGN, 4) != 0) ||
+		(sst_fw->size != header->file_size + sizeof(*header))) {
+		dev_err(dsp->dev, "error: invalid fw sign/filesize mismatch\n");
+		return -EINVAL;
+	}
+
+	dev_dbg(dsp->dev, "header size=0x%x modules=0x%x fmt=0x%x size=%zu\n",
+		header->file_size, header->modules,
+		header->file_format, sizeof(*header));
+
+	/* parse each module */
+	module = (void *)sst_fw->dma_buf + sizeof(*header);
+	for (count = 0; count < header->modules; count++) {
+
+		/* module */
+		ret = hsw_parse_module(dsp, sst_fw, module);
+		if (ret < 0) {
+			dev_err(dsp->dev, "error: invalid module %d\n", count);
+			return ret;
+		}
+		module = (void *)module + sizeof(*module) + module->mod_size;
+	}
+
+	/* allocate persistent/scratch mem regions */
+	scratch = sst_mem_block_alloc_scratch(dsp);
+	if (scratch == NULL)
+		return -ENOMEM;
+
+	sst_hsw_set_scratch_module(hsw, scratch);
+
+	return 0;
+}
+
+static irqreturn_t hsw_irq(int irq, void *context)
+{
+	struct sst_dsp *sst = (struct sst_dsp *) context;
+	u32 isr;
+	int ret = IRQ_NONE;
+
+	spin_lock(&sst->spinlock);
+
+	/* Interrupt arrived, check src */
+	isr = sst_dsp_shim_read_unlocked(sst, SST_ISRX);
+	if (isr & SST_ISRX_DONE) {
+		trace_sst_irq_done(isr,
+			sst_dsp_shim_read_unlocked(sst, SST_IMRX));
+
+		/* Mask Done interrupt before return */
+		sst_dsp_shim_update_bits_unlocked(sst, SST_IMRX,
+			SST_IMRX_DONE, SST_IMRX_DONE);
+		ret = IRQ_WAKE_THREAD;
+	}
+
+	if (isr & SST_ISRX_BUSY) {
+		trace_sst_irq_busy(isr,
+			sst_dsp_shim_read_unlocked(sst, SST_IMRX));
+
+		/* Mask Busy interrupt before return */
+		sst_dsp_shim_update_bits_unlocked(sst, SST_IMRX,
+			SST_IMRX_BUSY, SST_IMRX_BUSY);
+		ret = IRQ_WAKE_THREAD;
+	}
+
+	spin_unlock(&sst->spinlock);
+	return ret;
+}
+
+static void hsw_boot(struct sst_dsp *sst)
+{
+	/* select SSP1 19.2MHz base clock, SSP clock 0, turn off Low Power Clock */
+	sst_dsp_shim_update_bits_unlocked(sst, SST_CSR,
+		SST_CSR_S1IOCS | SST_CSR_SBCS1 | SST_CSR_LPCS, 0x0);
+
+	/* stall DSP core, set clk to 192/96Mhz */
+	sst_dsp_shim_update_bits_unlocked(sst,
+		SST_CSR, SST_CSR_STALL | SST_CSR_DCS_MASK,
+		SST_CSR_STALL | SST_CSR_DCS(4));
+
+	/* Set 24MHz MCLK, prevent local clock gating, enable SSP0 clock */
+	sst_dsp_shim_update_bits_unlocked(sst, SST_CLKCTL,
+		SST_CLKCTL_MASK | SST_CLKCTL_DCPLCG | SST_CLKCTL_SCOE0,
+		SST_CLKCTL_MASK | SST_CLKCTL_DCPLCG | SST_CLKCTL_SCOE0);
+
+	/* disable DMA finish function for SSP0 & SSP1 */
+	sst_dsp_shim_update_bits_unlocked(sst, SST_CSR2, SST_CSR2_SDFD_SSP1,
+		SST_CSR2_SDFD_SSP1);
+
+	/* enable DMA engine 0,1 all channels to access host memory */
+	sst_dsp_shim_update_bits_unlocked(sst, SST_HDMC,
+		SST_HDMC_HDDA1(0xff)  | SST_HDMC_HDDA0(0xff),
+		SST_HDMC_HDDA1(0xff) | SST_HDMC_HDDA0(0xff));
+
+	/* disable all clock gating */
+	writel(0x0, sst->addr.pci_cfg + SST_VDRTCTL2);
+
+	/* set DSP to RUN */
+	sst_dsp_shim_update_bits_unlocked(sst, SST_CSR, SST_CSR_STALL, 0x0);
+}
+
+static void hsw_reset(struct sst_dsp *sst)
+{
+	/* put DSP into reset and stall */
+	sst_dsp_shim_update_bits_unlocked(sst, SST_CSR,
+		SST_CSR_RST | SST_CSR_STALL, SST_CSR_RST | SST_CSR_STALL);
+
+	/* keep in reset for 10ms */
+	mdelay(10);
+
+	/* take DSP out of reset and keep stalled for FW loading */
+	sst_dsp_shim_update_bits_unlocked(sst, SST_CSR,
+		SST_CSR_RST | SST_CSR_STALL, SST_CSR_STALL);
+}
+
+struct sst_adsp_memregion {
+	u32 start;
+	u32 end;
+	int blocks;
+	enum sst_mem_type type;
+};
+
+/* lynx point ADSP mem regions */
+static const struct sst_adsp_memregion lp_region[] = {
+	{0x00000, 0x40000, 8, SST_MEM_DRAM}, /* D-SRAM0 - 8 * 32kB */
+	{0x40000, 0x80000, 8, SST_MEM_DRAM}, /* D-SRAM1 - 8 * 32kB */
+	{0x80000, 0xE0000, 12, SST_MEM_IRAM}, /* I-SRAM - 12 * 32kB */
+};
+
+/* wild cat point ADSP mem regions */
+static const struct sst_adsp_memregion wpt_region[] = {
+	{0x00000, 0x40000, 8, SST_MEM_DRAM}, /* D-SRAM0 - 8 * 32kB */
+	{0x40000, 0x80000, 8, SST_MEM_DRAM}, /* D-SRAM1 - 8 * 32kB */
+	{0x80000, 0xA0000, 4, SST_MEM_DRAM}, /* D-SRAM2 - 4 * 32kB */
+	{0xA0000, 0xF0000, 10, SST_MEM_IRAM}, /* I-SRAM - 10 * 32kB */
+};
+
+static int hsw_acpi_resource_map(struct sst_dsp *sst, struct sst_pdata *pdata)
+{
+	/* ADSP DRAM & IRAM */
+	sst->addr.lpe_base = pdata->lpe_base;
+	sst->addr.lpe = ioremap(pdata->lpe_base, pdata->lpe_size);
+	if (!sst->addr.lpe)
+		return -ENODEV;
+
+	/* ADSP PCI MMIO config space */
+	sst->addr.pci_cfg = ioremap(pdata->pcicfg_base, pdata->pcicfg_size);
+	if (!sst->addr.pci_cfg) {
+		iounmap(sst->addr.lpe);
+		return -ENODEV;
+	}
+
+	/* SST Shim */
+	sst->addr.shim = sst->addr.lpe + sst->addr.shim_offset;
+	return 0;
+}
+
+static u32 hsw_block_get_bit(struct sst_mem_block *block)
+{
+	u32 bit = 0, shift = 0;
+
+	switch (block->type) {
+	case SST_MEM_DRAM:
+		shift = 16;
+		break;
+	case SST_MEM_IRAM:
+		shift = 6;
+		break;
+	default:
+		return 0;
+	}
+
+	bit = 1 << (block->index + shift);
+
+	return bit;
+}
+
+/* enable 32kB memory block - locks held by caller */
+static int hsw_block_enable(struct sst_mem_block *block)
+{
+	struct sst_dsp *sst = block->dsp;
+	u32 bit, val;
+
+	if (block->users++ > 0)
+		return 0;
+
+	dev_dbg(block->dsp->dev, " enabled block %d:%d at offset 0x%x\n",
+		block->type, block->index, block->offset);
+
+	val = readl(sst->addr.pci_cfg + SST_VDRTCTL0);
+	bit = hsw_block_get_bit(block);
+	writel(val & ~bit, sst->addr.pci_cfg + SST_VDRTCTL0);
+
+	/* wait 18 DSP clock ticks */
+	udelay(10);
+
+	return 0;
+}
+
+/* disable 32kB memory block - locks held by caller */
+static int hsw_block_disable(struct sst_mem_block *block)
+{
+	struct sst_dsp *sst = block->dsp;
+	u32 bit, val;
+
+	if (--block->users > 0)
+		return 0;
+
+	dev_dbg(block->dsp->dev, " disabled block %d:%d at offset 0x%x\n",
+		block->type, block->index, block->offset);
+
+	val = readl(sst->addr.pci_cfg + SST_VDRTCTL0);
+	bit = hsw_block_get_bit(block);
+	writel(val | bit, sst->addr.pci_cfg + SST_VDRTCTL0);
+
+	return 0;
+}
+
+static struct sst_block_ops sst_hsw_ops = {
+	.enable = hsw_block_enable,
+	.disable = hsw_block_disable,
+};
+
+static int hsw_enable_shim(struct sst_dsp *sst)
+{
+	int tries = 10;
+	u32 reg;
+
+	/* enable shim */
+	reg = readl(sst->addr.pci_cfg + SST_SHIM_PM_REG);
+	writel(reg & ~0x3, sst->addr.pci_cfg + SST_SHIM_PM_REG);
+
+	/* check that ADSP shim is enabled */
+	while (tries--) {
+		reg = sst_dsp_shim_read_unlocked(sst, SST_CSR);
+		if (reg != 0xffffffff)
+			return 0;
+
+		msleep(1);
+	}
+
+	return -ENODEV;
+}
+
+static int hsw_init(struct sst_dsp *sst, struct sst_pdata *pdata)
+{
+	const struct sst_adsp_memregion *region;
+	struct device *dev;
+	int ret = -ENODEV, i, j, region_count;
+	u32 offset, size;
+
+	dev = sst->dev;
+
+	switch (sst->id) {
+	case SST_DEV_ID_LYNX_POINT:
+		region = lp_region;
+		region_count = ARRAY_SIZE(lp_region);
+		sst->addr.iram_offset = SST_LP_IRAM_OFFSET;
+		sst->addr.shim_offset = SST_LP_SHIM_OFFSET;
+		break;
+	case SST_DEV_ID_WILDCAT_POINT:
+		region = wpt_region;
+		region_count = ARRAY_SIZE(wpt_region);
+		sst->addr.iram_offset = SST_WPT_IRAM_OFFSET;
+		sst->addr.shim_offset = SST_WPT_SHIM_OFFSET;
+		break;
+	default:
+		dev_err(dev, "error: failed to get mem resources\n");
+		return ret;
+	}
+
+	ret = hsw_acpi_resource_map(sst, pdata);
+	if (ret < 0) {
+		dev_err(dev, "error: failed to map resources\n");
+		return ret;
+	}
+
+	/* enable the DSP SHIM */
+	ret = hsw_enable_shim(sst);
+	if (ret < 0) {
+		dev_err(dev, "error: failed to set DSP D0 and reset SHIM\n");
+		return ret;
+	}
+
+	ret = dma_coerce_mask_and_coherent(dev, DMA_BIT_MASK(32));
+	if (ret)
+		return ret;
+
+	/* Enable Interrupt from both sides */
+	sst_dsp_shim_update_bits_unlocked(sst, SST_IMRX, 0x3, 0x0);
+	sst_dsp_shim_update_bits_unlocked(sst, SST_IMRD,
+		(0x3 | 0x1 << 16 | 0x3 << 21), 0x0);
+
+	/* register DSP memory blocks - ideally we should get this from ACPI */
+	for (i = 0; i < region_count; i++) {
+		offset = region[i].start;
+		size = (region[i].end - region[i].start) / region[i].blocks;
+
+		/* register individual memory blocks */
+		for (j = 0; j < region[i].blocks; j++) {
+			sst_mem_block_register(sst, offset, size,
+				region[i].type, &sst_hsw_ops, j, sst);
+			offset += size;
+		}
+	}
+
+	/* set default power gating mask */
+	writel(0x0, sst->addr.pci_cfg + SST_VDRTCTL0);
+
+	return 0;
+}
+
+static void hsw_free(struct sst_dsp *sst)
+{
+	sst_mem_block_unregister_all(sst);
+	iounmap(sst->addr.lpe);
+	iounmap(sst->addr.pci_cfg);
+}
+
+struct sst_ops haswell_ops = {
+	.reset = hsw_reset,
+	.boot = hsw_boot,
+	.write = sst_shim32_write,
+	.read = sst_shim32_read,
+	.write64 = sst_shim32_write64,
+	.read64 = sst_shim32_read64,
+	.ram_read = sst_memcpy_fromio_32,
+	.ram_write = sst_memcpy_toio_32,
+	.irq_handler = hsw_irq,
+	.init = hsw_init,
+	.free = hsw_free,
+	.parse_fw = hsw_parse_fw_image,
+};
diff --git a/sound/soc/intel/sst-haswell-ipc.c b/sound/soc/intel/sst-haswell-ipc.c
new file mode 100644
index 000000000000..f46bb4ddde6f
--- /dev/null
+++ b/sound/soc/intel/sst-haswell-ipc.c
@@ -0,0 +1,1785 @@
+/*
+ *  Intel SST Haswell/Broadwell IPC Support
+ *
+ * Copyright (C) 2013, Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License version
+ * 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/list.h>
+#include <linux/device.h>
+#include <linux/wait.h>
+#include <linux/spinlock.h>
+#include <linux/workqueue.h>
+#include <linux/export.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/sched.h>
+#include <linux/list.h>
+#include <linux/platform_device.h>
+#include <linux/kthread.h>
+#include <linux/firmware.h>
+#include <linux/dma-mapping.h>
+#include <linux/debugfs.h>
+
+#include "sst-haswell-ipc.h"
+#include "sst-dsp.h"
+#include "sst-dsp-priv.h"
+
+/* Global Message - Generic */
+#define IPC_GLB_TYPE_SHIFT	24
+#define IPC_GLB_TYPE_MASK	(0x1f << IPC_GLB_TYPE_SHIFT)
+#define IPC_GLB_TYPE(x)		(x << IPC_GLB_TYPE_SHIFT)
+
+/* Global Message - Reply */
+#define IPC_GLB_REPLY_SHIFT	0
+#define IPC_GLB_REPLY_MASK	(0x1f << IPC_GLB_REPLY_SHIFT)
+#define IPC_GLB_REPLY_TYPE(x)	(x << IPC_GLB_REPLY_TYPE_SHIFT)
+
+/* Stream Message - Generic */
+#define IPC_STR_TYPE_SHIFT	20
+#define IPC_STR_TYPE_MASK	(0xf << IPC_STR_TYPE_SHIFT)
+#define IPC_STR_TYPE(x)		(x << IPC_STR_TYPE_SHIFT)
+#define IPC_STR_ID_SHIFT	16
+#define IPC_STR_ID_MASK		(0xf << IPC_STR_ID_SHIFT)
+#define IPC_STR_ID(x)		(x << IPC_STR_ID_SHIFT)
+
+/* Stream Message - Reply */
+#define IPC_STR_REPLY_SHIFT	0
+#define IPC_STR_REPLY_MASK	(0x1f << IPC_STR_REPLY_SHIFT)
+
+/* Stream Stage Message - Generic */
+#define IPC_STG_TYPE_SHIFT	12
+#define IPC_STG_TYPE_MASK	(0xf << IPC_STG_TYPE_SHIFT)
+#define IPC_STG_TYPE(x)		(x << IPC_STG_TYPE_SHIFT)
+#define IPC_STG_ID_SHIFT	10
+#define IPC_STG_ID_MASK		(0x3 << IPC_STG_ID_SHIFT)
+#define IPC_STG_ID(x)		(x << IPC_STG_ID_SHIFT)
+
+/* Stream Stage Message - Reply */
+#define IPC_STG_REPLY_SHIFT	0
+#define IPC_STG_REPLY_MASK	(0x1f << IPC_STG_REPLY_SHIFT)
+
+/* Debug Log Message - Generic */
+#define IPC_LOG_OP_SHIFT	20
+#define IPC_LOG_OP_MASK		(0xf << IPC_LOG_OP_SHIFT)
+#define IPC_LOG_OP_TYPE(x)	(x << IPC_LOG_OP_SHIFT)
+#define IPC_LOG_ID_SHIFT	16
+#define IPC_LOG_ID_MASK		(0xf << IPC_LOG_ID_SHIFT)
+#define IPC_LOG_ID(x)		(x << IPC_LOG_ID_SHIFT)
+
+/* IPC message timeout (msecs) */
+#define IPC_TIMEOUT_MSECS	300
+#define IPC_BOOT_MSECS		200
+#define IPC_MSG_WAIT		0
+#define IPC_MSG_NOWAIT		1
+
+/* Firmware Ready Message */
+#define IPC_FW_READY		(0x1 << 29)
+#define IPC_STATUS_MASK		(0x3 << 30)
+
+#define IPC_EMPTY_LIST_SIZE	8
+#define IPC_MAX_STREAMS		4
+
+/* Mailbox */
+#define IPC_MAX_MAILBOX_BYTES	256
+
+/* Global Message - Types and Replies */
+enum ipc_glb_type {
+	IPC_GLB_GET_FW_VERSION = 0,		/* Retrieves firmware version */
+	IPC_GLB_PERFORMANCE_MONITOR = 1,	/* Performance monitoring actions */
+	IPC_GLB_ALLOCATE_STREAM = 3,		/* Request to allocate new stream */
+	IPC_GLB_FREE_STREAM = 4,		/* Request to free stream */
+	IPC_GLB_GET_FW_CAPABILITIES = 5,	/* Retrieves firmware capabilities */
+	IPC_GLB_STREAM_MESSAGE = 6,		/* Message directed to stream or its stages */
+	/* Request to store firmware context during D0->D3 transition */
+	IPC_GLB_REQUEST_DUMP = 7,
+	/* Request to restore firmware context during D3->D0 transition */
+	IPC_GLB_RESTORE_CONTEXT = 8,
+	IPC_GLB_GET_DEVICE_FORMATS = 9,		/* Set device format */
+	IPC_GLB_SET_DEVICE_FORMATS = 10,	/* Get device format */
+	IPC_GLB_SHORT_REPLY = 11,
+	IPC_GLB_ENTER_DX_STATE = 12,
+	IPC_GLB_GET_MIXER_STREAM_INFO = 13,	/* Request mixer stream params */
+	IPC_GLB_DEBUG_LOG_MESSAGE = 14,		/* Message to or from the debug logger. */
+	IPC_GLB_REQUEST_TRANSFER = 16, 		/* < Request Transfer for host */
+	IPC_GLB_MAX_IPC_MESSAGE_TYPE = 17,	/* Maximum message number */
+};
+
+enum ipc_glb_reply {
+	IPC_GLB_REPLY_SUCCESS = 0,		/* The operation was successful. */
+	IPC_GLB_REPLY_ERROR_INVALID_PARAM = 1,	/* Invalid parameter was passed. */
+	IPC_GLB_REPLY_UNKNOWN_MESSAGE_TYPE = 2,	/* Uknown message type was resceived. */
+	IPC_GLB_REPLY_OUT_OF_RESOURCES = 3,	/* No resources to satisfy the request. */
+	IPC_GLB_REPLY_BUSY = 4,			/* The system or resource is busy. */
+	IPC_GLB_REPLY_PENDING = 5,		/* The action was scheduled for processing.  */
+	IPC_GLB_REPLY_FAILURE = 6,		/* Critical error happened. */
+	IPC_GLB_REPLY_INVALID_REQUEST = 7,	/* Request can not be completed. */
+	IPC_GLB_REPLY_STAGE_UNINITIALIZED = 8,	/* Processing stage was uninitialized. */
+	IPC_GLB_REPLY_NOT_FOUND = 9,		/* Required resource can not be found. */
+	IPC_GLB_REPLY_SOURCE_NOT_STARTED = 10,	/* Source was not started. */
+};
+
+/* Stream Message - Types */
+enum ipc_str_operation {
+	IPC_STR_RESET = 0,
+	IPC_STR_PAUSE = 1,
+	IPC_STR_RESUME = 2,
+	IPC_STR_STAGE_MESSAGE = 3,
+	IPC_STR_NOTIFICATION = 4,
+	IPC_STR_MAX_MESSAGE
+};
+
+/* Stream Stage Message Types */
+enum ipc_stg_operation {
+	IPC_STG_GET_VOLUME = 0,
+	IPC_STG_SET_VOLUME,
+	IPC_STG_SET_WRITE_POSITION,
+	IPC_STG_SET_FX_ENABLE,
+	IPC_STG_SET_FX_DISABLE,
+	IPC_STG_SET_FX_GET_PARAM,
+	IPC_STG_SET_FX_SET_PARAM,
+	IPC_STG_SET_FX_GET_INFO,
+	IPC_STG_MUTE_LOOPBACK,
+	IPC_STG_MAX_MESSAGE
+};
+
+/* Stream Stage Message Types For Notification*/
+enum ipc_stg_operation_notify {
+	IPC_POSITION_CHANGED = 0,
+	IPC_STG_GLITCH,
+	IPC_STG_MAX_NOTIFY
+};
+
+enum ipc_glitch_type {
+	IPC_GLITCH_UNDERRUN = 1,
+	IPC_GLITCH_DECODER_ERROR,
+	IPC_GLITCH_DOUBLED_WRITE_POS,
+	IPC_GLITCH_MAX
+};
+
+/* Debug Control */
+enum ipc_debug_operation {
+	IPC_DEBUG_ENABLE_LOG = 0,
+	IPC_DEBUG_DISABLE_LOG = 1,
+	IPC_DEBUG_REQUEST_LOG_DUMP = 2,
+	IPC_DEBUG_NOTIFY_LOG_DUMP = 3,
+	IPC_DEBUG_MAX_DEBUG_LOG
+};
+
+/* Firmware Ready */
+struct sst_hsw_ipc_fw_ready {
+	u32 inbox_offset;
+	u32 outbox_offset;
+	u32 inbox_size;
+	u32 outbox_size;
+	u32 fw_info_size;
+	u8 fw_info[1];
+} __attribute__((packed));
+
+struct ipc_message {
+	struct list_head list;
+	u32 header;
+
+	/* direction wrt host CPU */
+	char tx_data[IPC_MAX_MAILBOX_BYTES];
+	size_t tx_size;
+	char rx_data[IPC_MAX_MAILBOX_BYTES];
+	size_t rx_size;
+
+	wait_queue_head_t waitq;
+	bool pending;
+	bool complete;
+	bool wait;
+	int errno;
+};
+
+struct sst_hsw_stream;
+struct sst_hsw;
+
+/* Stream infomation */
+struct sst_hsw_stream {
+	/* configuration */
+	struct sst_hsw_ipc_stream_alloc_req request;
+	struct sst_hsw_ipc_stream_alloc_reply reply;
+	struct sst_hsw_ipc_stream_free_req free_req;
+
+	/* Mixer info */
+	u32 mute_volume[SST_HSW_NO_CHANNELS];
+	u32 mute[SST_HSW_NO_CHANNELS];
+
+	/* runtime info */
+	struct sst_hsw *hsw;
+	int host_id;
+	bool commited;
+	bool running;
+
+	/* Notification work */
+	struct work_struct notify_work;
+	u32 header;
+
+	/* Position info from DSP */
+	struct sst_hsw_ipc_stream_set_position wpos;
+	struct sst_hsw_ipc_stream_get_position rpos;
+	struct sst_hsw_ipc_stream_glitch_position glitch;
+
+	/* Volume info */
+	struct sst_hsw_ipc_volume_req vol_req;
+
+	/* driver callback */
+	u32 (*notify_position)(struct sst_hsw_stream *stream, void *data);
+	void *pdata;
+
+	struct list_head node;
+};
+
+/* FW log ring information */
+struct sst_hsw_log_stream {
+	dma_addr_t dma_addr;
+	unsigned char *dma_area;
+	unsigned char *ring_descr;
+	int pages;
+	int size;
+
+	/* Notification work */
+	struct work_struct notify_work;
+	wait_queue_head_t readers_wait_q;
+	struct mutex rw_mutex;
+
+	u32 last_pos;
+	u32 curr_pos;
+	u32 reader_pos;
+
+	/* fw log config */
+	u32 config[SST_HSW_FW_LOG_CONFIG_DWORDS];
+
+	struct sst_hsw *hsw;
+};
+
+/* SST Haswell IPC data */
+struct sst_hsw {
+	struct device *dev;
+	struct sst_dsp *dsp;
+	struct platform_device *pdev_pcm;
+
+	/* FW config */
+	struct sst_hsw_ipc_fw_ready fw_ready;
+	struct sst_hsw_ipc_fw_version version;
+	struct sst_module *scratch;
+	bool fw_done;
+
+	/* stream */
+	struct list_head stream_list;
+
+	/* global mixer */
+	struct sst_hsw_ipc_stream_info_reply mixer_info;
+	enum sst_hsw_volume_curve curve_type;
+	u32 curve_duration;
+	u32 mute[SST_HSW_NO_CHANNELS];
+	u32 mute_volume[SST_HSW_NO_CHANNELS];
+
+	/* DX */
+	struct sst_hsw_ipc_dx_reply dx;
+
+	/* boot */
+	wait_queue_head_t boot_wait;
+	bool boot_complete;
+	bool shutdown;
+
+	/* IPC messaging */
+	struct list_head tx_list;
+	struct list_head rx_list;
+	struct list_head empty_list;
+	wait_queue_head_t wait_txq;
+	struct task_struct *tx_thread;
+	struct kthread_worker kworker;
+	struct kthread_work kwork;
+	bool pending;
+	struct ipc_message *msg;
+
+	/* FW log stream */
+	struct sst_hsw_log_stream log_stream;
+};
+
+#define CREATE_TRACE_POINTS
+#include <trace/events/hswadsp.h>
+
+static inline u32 msg_get_global_type(u32 msg)
+{
+	return (msg & IPC_GLB_TYPE_MASK) >> IPC_GLB_TYPE_SHIFT;
+}
+
+static inline u32 msg_get_global_reply(u32 msg)
+{
+	return (msg & IPC_GLB_REPLY_MASK) >> IPC_GLB_REPLY_SHIFT;
+}
+
+static inline u32 msg_get_stream_type(u32 msg)
+{
+	return (msg & IPC_STR_TYPE_MASK) >>  IPC_STR_TYPE_SHIFT;
+}
+
+static inline u32 msg_get_stage_type(u32 msg)
+{
+	return (msg & IPC_STG_TYPE_MASK) >>  IPC_STG_TYPE_SHIFT;
+}
+
+static inline u32 msg_set_stage_type(u32 msg, u32 type)
+{
+	return (msg & ~IPC_STG_TYPE_MASK) +
+		(type << IPC_STG_TYPE_SHIFT);
+}
+
+static inline u32 msg_get_stream_id(u32 msg)
+{
+	return (msg & IPC_STR_ID_MASK) >>  IPC_STR_ID_SHIFT;
+}
+
+static inline u32 msg_get_notify_reason(u32 msg)
+{
+	return (msg & IPC_STG_TYPE_MASK) >> IPC_STG_TYPE_SHIFT;
+}
+
+u32 create_channel_map(enum sst_hsw_channel_config config)
+{
+	switch (config) {
+	case SST_HSW_CHANNEL_CONFIG_MONO:
+		return (0xFFFFFFF0 | SST_HSW_CHANNEL_CENTER);
+	case SST_HSW_CHANNEL_CONFIG_STEREO:
+		return (0xFFFFFF00 | SST_HSW_CHANNEL_LEFT
+			| (SST_HSW_CHANNEL_RIGHT << 4));
+	case SST_HSW_CHANNEL_CONFIG_2_POINT_1:
+		return (0xFFFFF000 | SST_HSW_CHANNEL_LEFT
+			| (SST_HSW_CHANNEL_RIGHT << 4)
+			| (SST_HSW_CHANNEL_LFE << 8 ));
+	case SST_HSW_CHANNEL_CONFIG_3_POINT_0:
+		return (0xFFFFF000 | SST_HSW_CHANNEL_LEFT
+			| (SST_HSW_CHANNEL_CENTER << 4)
+			| (SST_HSW_CHANNEL_RIGHT << 8));
+	case SST_HSW_CHANNEL_CONFIG_3_POINT_1:
+		return (0xFFFF0000 | SST_HSW_CHANNEL_LEFT
+			| (SST_HSW_CHANNEL_CENTER << 4)
+			| (SST_HSW_CHANNEL_RIGHT << 8)
+			| (SST_HSW_CHANNEL_LFE << 12));
+	case SST_HSW_CHANNEL_CONFIG_QUATRO:
+		return (0xFFFF0000 | SST_HSW_CHANNEL_LEFT
+			| (SST_HSW_CHANNEL_RIGHT << 4)
+			| (SST_HSW_CHANNEL_LEFT_SURROUND << 8)
+			| (SST_HSW_CHANNEL_RIGHT_SURROUND << 12));
+	case SST_HSW_CHANNEL_CONFIG_4_POINT_0:
+		return (0xFFFF0000 | SST_HSW_CHANNEL_LEFT
+			| (SST_HSW_CHANNEL_CENTER << 4)
+			| (SST_HSW_CHANNEL_RIGHT << 8)
+			| (SST_HSW_CHANNEL_CENTER_SURROUND << 12));
+	case SST_HSW_CHANNEL_CONFIG_5_POINT_0:
+		return (0xFFF00000 | SST_HSW_CHANNEL_LEFT
+			| (SST_HSW_CHANNEL_CENTER << 4)
+			| (SST_HSW_CHANNEL_RIGHT << 8)
+			| (SST_HSW_CHANNEL_LEFT_SURROUND << 12)
+			| (SST_HSW_CHANNEL_RIGHT_SURROUND << 16));
+	case SST_HSW_CHANNEL_CONFIG_5_POINT_1:
+		return (0xFF000000 | SST_HSW_CHANNEL_CENTER
+			| (SST_HSW_CHANNEL_LEFT << 4)
+			| (SST_HSW_CHANNEL_RIGHT << 8)
+			| (SST_HSW_CHANNEL_LEFT_SURROUND << 12)
+			| (SST_HSW_CHANNEL_RIGHT_SURROUND << 16)
+			| (SST_HSW_CHANNEL_LFE << 20));
+	case SST_HSW_CHANNEL_CONFIG_DUAL_MONO:
+		return (0xFFFFFF00 | SST_HSW_CHANNEL_LEFT
+			| (SST_HSW_CHANNEL_LEFT << 4));
+	default:
+		return 0xFFFFFFFF;
+	}
+}
+
+static struct sst_hsw_stream *get_stream_by_id(struct sst_hsw *hsw,
+	int stream_id)
+{
+	struct sst_hsw_stream *stream;
+
+	list_for_each_entry(stream, &hsw->stream_list, node) {
+		if (stream->reply.stream_hw_id == stream_id)
+			return stream;
+	}
+
+	return NULL;
+}
+
+static void ipc_shim_dbg(struct sst_hsw *hsw, const char *text)
+{
+	struct sst_dsp *sst = hsw->dsp;
+	u32 isr, ipcd, imrx, ipcx;
+
+	ipcx = sst_dsp_shim_read_unlocked(sst, SST_IPCX);
+	isr = sst_dsp_shim_read_unlocked(sst, SST_ISRX);
+	ipcd = sst_dsp_shim_read_unlocked(sst, SST_IPCD);
+	imrx = sst_dsp_shim_read_unlocked(sst, SST_IMRX);
+
+	dev_err(hsw->dev, "ipc: --%s-- ipcx 0x%8.8x isr 0x%8.8x ipcd 0x%8.8x imrx 0x%8.8x\n",
+		text, ipcx, isr, ipcd, imrx);
+}
+
+/* locks held by caller */
+static struct ipc_message *msg_get_empty(struct sst_hsw *hsw)
+{
+	struct ipc_message *msg = NULL;
+
+	if (!list_empty(&hsw->empty_list)) {
+		msg = list_first_entry(&hsw->empty_list, struct ipc_message,
+			list);
+		list_del(&msg->list);
+	}
+
+	return msg;
+}
+
+static void ipc_tx_msgs(struct kthread_work *work)
+{
+	struct sst_hsw *hsw =
+		container_of(work, struct sst_hsw, kwork);
+	struct ipc_message *msg;
+	unsigned long flags;
+	u32 ipcx;
+
+	spin_lock_irqsave(&hsw->dsp->spinlock, flags);
+
+	if (list_empty(&hsw->tx_list) || hsw->pending) {
+		spin_unlock_irqrestore(&hsw->dsp->spinlock, flags);
+		return;
+	}
+
+	/* if the DSP is busy we will TX messages after IRQ */
+	ipcx = sst_dsp_shim_read_unlocked(hsw->dsp, SST_IPCX);
+	if (ipcx & SST_IPCX_BUSY) {
+		spin_unlock_irqrestore(&hsw->dsp->spinlock, flags);
+		return;
+	}
+
+	msg = list_first_entry(&hsw->tx_list, struct ipc_message, list);
+
+	list_move(&msg->list, &hsw->rx_list);
+
+	/* send the message */
+	sst_dsp_outbox_write(hsw->dsp, msg->tx_data, msg->tx_size);
+	sst_dsp_ipc_msg_tx(hsw->dsp, msg->header | SST_IPCX_BUSY);
+
+	spin_unlock_irqrestore(&hsw->dsp->spinlock, flags);
+}
+
+/* locks held by caller */
+static void tx_msg_reply_complete(struct sst_hsw *hsw, struct ipc_message *msg)
+{
+	msg->complete = true;
+	trace_ipc_reply("completed", msg->header);
+
+	if (!msg->wait)
+		list_add_tail(&msg->list, &hsw->empty_list);
+	else
+		wake_up(&msg->waitq);
+}
+
+static int tx_wait_done(struct sst_hsw *hsw, struct ipc_message *msg,
+	void *rx_data)
+{
+	unsigned long flags;
+	int ret;
+
+	/* wait for DSP completion (in all cases atm inc pending) */
+	ret = wait_event_timeout(msg->waitq, msg->complete,
+		msecs_to_jiffies(IPC_TIMEOUT_MSECS));
+
+	spin_lock_irqsave(&hsw->dsp->spinlock, flags);
+	if (ret == 0) {
+		ipc_shim_dbg(hsw, "message timeout");
+
+		trace_ipc_error("error message timeout for", msg->header);
+		ret = -ETIMEDOUT;
+	} else {
+
+		/* copy the data returned from DSP */
+		if (msg->rx_size)
+			memcpy(rx_data, msg->rx_data, msg->rx_size);
+		ret = msg->errno;
+	}
+
+	list_add_tail(&msg->list, &hsw->empty_list);
+	spin_unlock_irqrestore(&hsw->dsp->spinlock, flags);
+	return ret;
+}
+
+static int ipc_tx_message(struct sst_hsw *hsw, u32 header, void *tx_data,
+	size_t tx_bytes, void *rx_data, size_t rx_bytes, int wait)
+{
+	struct ipc_message *msg;
+	unsigned long flags;
+
+	spin_lock_irqsave(&hsw->dsp->spinlock, flags);
+
+	msg = msg_get_empty(hsw);
+	if (msg == NULL) {
+		spin_unlock_irqrestore(&hsw->dsp->spinlock, flags);
+		return -EBUSY;
+	}
+
+	if (tx_bytes)
+		memcpy(msg->tx_data, tx_data, tx_bytes);
+
+	msg->header = header;
+	msg->tx_size = tx_bytes;
+	msg->rx_size = rx_bytes;
+	msg->wait = wait;
+	msg->errno = 0;
+	msg->pending = false;
+	msg->complete = false;
+
+	list_add_tail(&msg->list, &hsw->tx_list);
+	spin_unlock_irqrestore(&hsw->dsp->spinlock, flags);
+
+	queue_kthread_work(&hsw->kworker, &hsw->kwork);
+
+	if (wait)
+		return tx_wait_done(hsw, msg, rx_data);
+	else
+		return 0;
+}
+
+static inline int ipc_tx_message_wait(struct sst_hsw *hsw, u32 header,
+	void *tx_data, size_t tx_bytes, void *rx_data, size_t rx_bytes)
+{
+	return ipc_tx_message(hsw, header, tx_data, tx_bytes, rx_data,
+		rx_bytes, 1);
+}
+
+static inline int ipc_tx_message_nowait(struct sst_hsw *hsw, u32 header,
+	void *tx_data, size_t tx_bytes)
+{
+	return ipc_tx_message(hsw, header, tx_data, tx_bytes, NULL, 0, 0);
+}
+
+static void hsw_fw_ready(struct sst_hsw *hsw, u32 header)
+{
+	struct sst_hsw_ipc_fw_ready fw_ready;
+	u32 offset;
+
+	offset = (header & 0x1FFFFFFF) << 3;
+
+	dev_dbg(hsw->dev, "ipc: DSP is ready 0x%8.8x offset %d\n",
+		header, offset);
+
+	/* copy data from the DSP FW ready offset */
+	sst_dsp_read(hsw->dsp, &fw_ready, offset, sizeof(fw_ready));
+
+	sst_dsp_mailbox_init(hsw->dsp, fw_ready.inbox_offset,
+		fw_ready.inbox_size, fw_ready.outbox_offset,
+		fw_ready.outbox_size);
+
+	hsw->boot_complete = true;
+	wake_up(&hsw->boot_wait);
+
+	dev_dbg(hsw->dev, " mailbox upstream 0x%x - size 0x%x\n",
+		fw_ready.inbox_offset, fw_ready.inbox_size);
+	dev_dbg(hsw->dev, " mailbox downstream 0x%x - size 0x%x\n",
+		fw_ready.outbox_offset, fw_ready.outbox_size);
+}
+
+static void hsw_notification_work(struct work_struct *work)
+{
+	struct sst_hsw_stream *stream = container_of(work,
+			struct sst_hsw_stream, notify_work);
+	struct sst_hsw_ipc_stream_glitch_position *glitch = &stream->glitch;
+	struct sst_hsw_ipc_stream_get_position *pos = &stream->rpos;
+	struct sst_hsw *hsw = stream->hsw;
+	u32 reason;
+
+	reason = msg_get_notify_reason(stream->header);
+
+	switch (reason) {
+	case IPC_STG_GLITCH:
+		trace_ipc_notification("DSP stream under/overrun",
+			stream->reply.stream_hw_id);
+		sst_dsp_inbox_read(hsw->dsp, glitch, sizeof(*glitch));
+
+		dev_err(hsw->dev, "glitch %d pos 0x%x write pos 0x%x\n",
+			glitch->glitch_type, glitch->present_pos,
+			glitch->write_pos);
+		break;
+
+	case IPC_POSITION_CHANGED:
+		trace_ipc_notification("DSP stream position changed for",
+			stream->reply.stream_hw_id);
+		sst_dsp_inbox_read(hsw->dsp, pos, sizeof(pos));
+
+		if (stream->notify_position)
+			stream->notify_position(stream, stream->pdata);
+
+		break;
+	default:
+		dev_err(hsw->dev, "error: unknown notification 0x%x\n",
+			stream->header);
+		break;
+	}
+
+	/* tell DSP that notification has been handled */
+	sst_dsp_shim_update_bits_unlocked(hsw->dsp, SST_IPCD,
+		SST_IPCD_BUSY | SST_IPCD_DONE, SST_IPCD_DONE);
+
+	/* unmask busy interrupt */
+	sst_dsp_shim_update_bits_unlocked(hsw->dsp, SST_IMRX, SST_IMRX_BUSY, 0);
+}
+
+static struct ipc_message *reply_find_msg(struct sst_hsw *hsw, u32 header)
+{
+	struct ipc_message *msg;
+
+	/* clear reply bits & status bits */
+	header &= ~(IPC_STATUS_MASK | IPC_GLB_REPLY_MASK);
+
+	if (list_empty(&hsw->rx_list)) {
+		dev_err(hsw->dev, "error: rx list empty but received 0x%x\n",
+			header);
+		return NULL;
+	}
+
+	list_for_each_entry(msg, &hsw->rx_list, list) {
+		if (msg->header == header)
+			return msg;
+	}
+
+	return NULL;
+}
+
+static void hsw_stream_update(struct sst_hsw *hsw, struct ipc_message *msg)
+{
+	struct sst_hsw_stream *stream;
+	u32 header = msg->header & ~(IPC_STATUS_MASK | IPC_GLB_REPLY_MASK);
+	u32 stream_id = msg_get_stream_id(header);
+	u32 stream_msg = msg_get_stream_type(header);
+
+	stream = get_stream_by_id(hsw, stream_id);
+	if (stream == NULL)
+		return;
+
+	switch (stream_msg) {
+	case IPC_STR_STAGE_MESSAGE:
+	case IPC_STR_NOTIFICATION:
+	case IPC_STR_RESET:
+		break;
+	case IPC_STR_PAUSE:
+		stream->running = false;
+		trace_ipc_notification("stream paused",
+			stream->reply.stream_hw_id);
+		break;
+	case IPC_STR_RESUME:
+		stream->running = true;
+		trace_ipc_notification("stream running",
+			stream->reply.stream_hw_id);
+		break;
+	}
+}
+
+static int hsw_process_reply(struct sst_hsw *hsw, u32 header)
+{
+	struct ipc_message *msg;
+	u32 reply = msg_get_global_reply(header);
+
+	trace_ipc_reply("processing -->", header);
+
+	msg = reply_find_msg(hsw, header);
+	if (msg == NULL) {
+		trace_ipc_error("error: can't find message header", header);
+		return -EIO;
+	}
+
+	/* first process the header */
+	switch (reply) {
+	case IPC_GLB_REPLY_PENDING:
+		trace_ipc_pending_reply("received", header);
+		msg->pending = true;
+		hsw->pending = true;
+		return 1;
+	case IPC_GLB_REPLY_SUCCESS:
+		if (msg->pending) {
+			trace_ipc_pending_reply("completed", header);
+			sst_dsp_inbox_read(hsw->dsp, msg->rx_data,
+				msg->rx_size);
+			hsw->pending = false;
+		} else {
+			/* copy data from the DSP */
+			sst_dsp_outbox_read(hsw->dsp, msg->rx_data,
+				msg->rx_size);
+		}
+		break;
+	/* these will be rare - but useful for debug */
+	case IPC_GLB_REPLY_UNKNOWN_MESSAGE_TYPE:
+		trace_ipc_error("error: unknown message type", header);
+		msg->errno = -EBADMSG;
+		break;
+	case IPC_GLB_REPLY_OUT_OF_RESOURCES:
+		trace_ipc_error("error: out of resources", header);
+		msg->errno = -ENOMEM;
+		break;
+	case IPC_GLB_REPLY_BUSY:
+		trace_ipc_error("error: reply busy", header);
+		msg->errno = -EBUSY;
+		break;
+	case IPC_GLB_REPLY_FAILURE:
+		trace_ipc_error("error: reply failure", header);
+		msg->errno = -EINVAL;
+		break;
+	case IPC_GLB_REPLY_STAGE_UNINITIALIZED:
+		trace_ipc_error("error: stage uninitialized", header);
+		msg->errno = -EINVAL;
+		break;
+	case IPC_GLB_REPLY_NOT_FOUND:
+		trace_ipc_error("error: reply not found", header);
+		msg->errno = -EINVAL;
+		break;
+	case IPC_GLB_REPLY_SOURCE_NOT_STARTED:
+		trace_ipc_error("error: source not started", header);
+		msg->errno = -EINVAL;
+		break;
+	case IPC_GLB_REPLY_INVALID_REQUEST:
+		trace_ipc_error("error: invalid request", header);
+		msg->errno = -EINVAL;
+		break;
+	case IPC_GLB_REPLY_ERROR_INVALID_PARAM:
+		trace_ipc_error("error: invalid parameter", header);
+		msg->errno = -EINVAL;
+		break;
+	default:
+		trace_ipc_error("error: unknown reply", header);
+		msg->errno = -EINVAL;
+		break;
+	}
+
+	/* update any stream states */
+	hsw_stream_update(hsw, msg);
+
+	/* wake up and return the error if we have waiters on this message ? */
+	list_del(&msg->list);
+	tx_msg_reply_complete(hsw, msg);
+
+	return 1;
+}
+
+static int hsw_stream_message(struct sst_hsw *hsw, u32 header)
+{
+	u32 stream_msg, stream_id, stage_type;
+	struct sst_hsw_stream *stream;
+	int handled = 0;
+
+	stream_msg = msg_get_stream_type(header);
+	stream_id = msg_get_stream_id(header);
+	stage_type = msg_get_stage_type(header);
+
+	stream = get_stream_by_id(hsw, stream_id);
+	if (stream == NULL)
+		return handled;
+
+	stream->header = header;
+
+	switch (stream_msg) {
+	case IPC_STR_STAGE_MESSAGE:
+		dev_err(hsw->dev, "error: stage msg not implemented 0x%8.8x\n",
+			header);
+		break;
+	case IPC_STR_NOTIFICATION:
+		schedule_work(&stream->notify_work);
+		break;
+	default:
+		/* handle pending message complete request */
+		handled = hsw_process_reply(hsw, header);
+		break;
+	}
+
+	return handled;
+}
+
+static int hsw_log_message(struct sst_hsw *hsw, u32 header)
+{
+	u32 operation = (header & IPC_LOG_OP_MASK) >>  IPC_LOG_OP_SHIFT;
+	struct sst_hsw_log_stream *stream = &hsw->log_stream;
+	int ret = 1;
+
+	if (operation != IPC_DEBUG_REQUEST_LOG_DUMP) {
+		dev_err(hsw->dev,
+			"error: log msg not implemented 0x%8.8x\n", header);
+		return 0;
+	}
+
+	mutex_lock(&stream->rw_mutex);
+	stream->last_pos = stream->curr_pos;
+	sst_dsp_inbox_read(
+		hsw->dsp, &stream->curr_pos, sizeof(stream->curr_pos));
+	mutex_unlock(&stream->rw_mutex);
+
+	schedule_work(&stream->notify_work);
+
+	return ret;
+}
+
+static int hsw_process_notification(struct sst_hsw *hsw)
+{
+	struct sst_dsp *sst = hsw->dsp;
+	u32 type, header;
+	int handled = 1;
+
+	header = sst_dsp_shim_read_unlocked(sst, SST_IPCD);
+	type = msg_get_global_type(header);
+
+	trace_ipc_request("processing -->", header);
+
+	/* FW Ready is a special case */
+	if (!hsw->boot_complete && header & IPC_FW_READY) {
+		hsw_fw_ready(hsw, header);
+		return handled;
+	}
+
+	switch (type) {
+	case IPC_GLB_GET_FW_VERSION:
+	case IPC_GLB_ALLOCATE_STREAM:
+	case IPC_GLB_FREE_STREAM:
+	case IPC_GLB_GET_FW_CAPABILITIES:
+	case IPC_GLB_REQUEST_DUMP:
+	case IPC_GLB_GET_DEVICE_FORMATS:
+	case IPC_GLB_SET_DEVICE_FORMATS:
+	case IPC_GLB_ENTER_DX_STATE:
+	case IPC_GLB_GET_MIXER_STREAM_INFO:
+	case IPC_GLB_MAX_IPC_MESSAGE_TYPE:
+	case IPC_GLB_RESTORE_CONTEXT:
+	case IPC_GLB_SHORT_REPLY:
+		dev_err(hsw->dev, "error: message type %d header 0x%x\n",
+			type, header);
+		break;
+	case IPC_GLB_STREAM_MESSAGE:
+		handled = hsw_stream_message(hsw, header);
+		break;
+	case IPC_GLB_DEBUG_LOG_MESSAGE:
+		handled = hsw_log_message(hsw, header);
+		break;
+	default:
+		dev_err(hsw->dev, "error: unexpected type %d hdr 0x%8.8x\n",
+			type, header);
+		break;
+	}
+
+	return handled;
+}
+
+static irqreturn_t hsw_irq_thread(int irq, void *context)
+{
+	struct sst_dsp *sst = (struct sst_dsp *) context;
+	struct sst_hsw *hsw = sst_dsp_get_thread_context(sst);
+	u32 ipcx, ipcd;
+	int handled;
+	unsigned long flags;
+
+	spin_lock_irqsave(&sst->spinlock, flags);
+
+	ipcx = sst_dsp_ipc_msg_rx(hsw->dsp);
+	ipcd = sst_dsp_shim_read_unlocked(sst, SST_IPCD);
+
+	/* reply message from DSP */
+	if (ipcx & SST_IPCX_DONE) {
+
+		/* Handle Immediate reply from DSP Core */
+		handled = hsw_process_reply(hsw, ipcx);
+
+		if (handled > 0) {
+			/* clear DONE bit - tell DSP we have completed */
+			sst_dsp_shim_update_bits_unlocked(sst, SST_IPCX,
+				SST_IPCX_DONE, 0);
+
+			/* unmask Done interrupt */
+			sst_dsp_shim_update_bits_unlocked(sst, SST_IMRX,
+				SST_IMRX_DONE, 0);
+		}
+	}
+
+	/* new message from DSP */
+	if (ipcd & SST_IPCD_BUSY) {
+
+		/* Handle Notification and Delayed reply from DSP Core */
+		handled = hsw_process_notification(hsw);
+
+		/* clear BUSY bit and set DONE bit - accept new messages */
+		if (handled > 0) {
+			sst_dsp_shim_update_bits_unlocked(sst, SST_IPCD,
+				SST_IPCD_BUSY | SST_IPCD_DONE, SST_IPCD_DONE);
+
+			/* unmask busy interrupt */
+			sst_dsp_shim_update_bits_unlocked(sst, SST_IMRX,
+				SST_IMRX_BUSY, 0);
+		}
+	}
+
+	spin_unlock_irqrestore(&sst->spinlock, flags);
+
+	/* continue to send any remaining messages... */
+	queue_kthread_work(&hsw->kworker, &hsw->kwork);
+
+	return IRQ_HANDLED;
+}
+
+int sst_hsw_fw_get_version(struct sst_hsw *hsw,
+	struct sst_hsw_ipc_fw_version *version)
+{
+	int ret;
+
+	ret = ipc_tx_message_wait(hsw, IPC_GLB_TYPE(IPC_GLB_GET_FW_VERSION),
+		NULL, 0, version, sizeof(*version));
+	if (ret < 0)
+		dev_err(hsw->dev, "error: get version failed\n");
+
+	return ret;
+}
+
+/* Mixer Controls */
+int sst_hsw_stream_mute(struct sst_hsw *hsw, struct sst_hsw_stream *stream,
+	u32 stage_id, u32 channel)
+{
+	int ret;
+
+	ret = sst_hsw_stream_get_volume(hsw, stream, stage_id, channel,
+		&stream->mute_volume[channel]);
+	if (ret < 0)
+		return ret;
+
+	ret = sst_hsw_stream_set_volume(hsw, stream, stage_id, channel, 0);
+	if (ret < 0) {
+		dev_err(hsw->dev, "error: can't unmute stream %d channel %d\n",
+			stream->reply.stream_hw_id, channel);
+		return ret;
+	}
+
+	stream->mute[channel] = 1;
+	return 0;
+}
+
+int sst_hsw_stream_unmute(struct sst_hsw *hsw, struct sst_hsw_stream *stream,
+	u32 stage_id, u32 channel)
+
+{
+	int ret;
+
+	stream->mute[channel] = 0;
+	ret = sst_hsw_stream_set_volume(hsw, stream, stage_id, channel,
+		stream->mute_volume[channel]);
+	if (ret < 0) {
+		dev_err(hsw->dev, "error: can't unmute stream %d channel %d\n",
+			stream->reply.stream_hw_id, channel);
+		return ret;
+	}
+
+	return 0;
+}
+
+int sst_hsw_stream_get_volume(struct sst_hsw *hsw, struct sst_hsw_stream *stream,
+	u32 stage_id, u32 channel, u32 *volume)
+{
+	if (channel > 1)
+		return -EINVAL;
+
+	sst_dsp_read(hsw->dsp, volume,
+		stream->reply.volume_register_address[channel], sizeof(volume));
+
+	return 0;
+}
+
+int sst_hsw_stream_set_volume_curve(struct sst_hsw *hsw,
+	struct sst_hsw_stream *stream, u64 curve_duration,
+	enum sst_hsw_volume_curve curve)
+{
+	/* curve duration in steps of 100ns */
+	stream->vol_req.curve_duration = curve_duration;
+	stream->vol_req.curve_type = curve;
+
+	return 0;
+}
+
+/* stream volume */
+int sst_hsw_stream_set_volume(struct sst_hsw *hsw,
+	struct sst_hsw_stream *stream, u32 stage_id, u32 channel, u32 volume)
+{
+	struct sst_hsw_ipc_volume_req *req;
+	u32 header;
+	int ret;
+
+	trace_ipc_request("set stream volume", stream->reply.stream_hw_id);
+
+	if (channel > 1)
+		return -EINVAL;
+
+	if (stream->mute[channel]) {
+		stream->mute_volume[channel] = volume;
+		return 0;
+	}
+
+	header = IPC_GLB_TYPE(IPC_GLB_STREAM_MESSAGE) |
+		IPC_STR_TYPE(IPC_STR_STAGE_MESSAGE);
+	header |= (stream->reply.stream_hw_id << IPC_STR_ID_SHIFT);
+	header |= (IPC_STG_SET_VOLUME << IPC_STG_TYPE_SHIFT);
+	header |= (stage_id << IPC_STG_ID_SHIFT);
+
+	req = &stream->vol_req;
+	req->channel = channel;
+	req->target_volume = volume;
+
+	ret = ipc_tx_message_wait(hsw, header, req, sizeof(*req), NULL, 0);
+	if (ret < 0) {
+		dev_err(hsw->dev, "error: set stream volume failed\n");
+		return ret;
+	}
+
+	return 0;
+}
+
+int sst_hsw_mixer_mute(struct sst_hsw *hsw, u32 stage_id, u32 channel)
+{
+	int ret;
+
+	ret = sst_hsw_mixer_get_volume(hsw, stage_id, channel,
+		&hsw->mute_volume[channel]);
+	if (ret < 0)
+		return ret;
+
+	ret = sst_hsw_mixer_set_volume(hsw, stage_id, channel, 0);
+	if (ret < 0) {
+		dev_err(hsw->dev, "error: failed to unmute mixer channel %d\n",
+			channel);
+		return ret;
+	}
+
+	hsw->mute[channel] = 1;
+	return 0;
+}
+
+int sst_hsw_mixer_unmute(struct sst_hsw *hsw, u32 stage_id, u32 channel)
+{
+	int ret;
+
+	ret = sst_hsw_mixer_set_volume(hsw, stage_id, channel,
+		hsw->mixer_info.volume_register_address[channel]);
+	if (ret < 0) {
+		dev_err(hsw->dev, "error: failed to unmute mixer channel %d\n",
+			channel);
+		return ret;
+	}
+
+	hsw->mute[channel] = 0;
+	return 0;
+}
+
+int sst_hsw_mixer_get_volume(struct sst_hsw *hsw, u32 stage_id, u32 channel,
+	u32 *volume)
+{
+	if (channel > 1)
+		return -EINVAL;
+
+	sst_dsp_read(hsw->dsp, volume,
+		hsw->mixer_info.volume_register_address[channel],
+		sizeof(*volume));
+
+	return 0;
+}
+
+int sst_hsw_mixer_set_volume_curve(struct sst_hsw *hsw,
+	 u64 curve_duration, enum sst_hsw_volume_curve curve)
+{
+	/* curve duration in steps of 100ns */
+	hsw->curve_duration = curve_duration;
+	hsw->curve_type = curve;
+
+	return 0;
+}
+
+/* global mixer volume */
+int sst_hsw_mixer_set_volume(struct sst_hsw *hsw, u32 stage_id, u32 channel,
+	u32 volume)
+{
+	struct sst_hsw_ipc_volume_req req;
+	u32 header;
+	int ret;
+
+	trace_ipc_request("set mixer volume", volume);
+
+	/* set both at same time ? */
+	if (channel == 2) {
+		if (hsw->mute[0] && hsw->mute[1]) {
+			hsw->mute_volume[0] = hsw->mute_volume[1] = volume;
+			return 0;
+		} else if (hsw->mute[0])
+			req.channel = 1;
+		else if (hsw->mute[1])
+			req.channel = 0;
+		else
+			req.channel = 0xffffffff;
+	} else {
+		/* set only 1 channel */
+		if (hsw->mute[channel]) {
+			hsw->mute_volume[channel] = volume;
+			return 0;
+		}
+		req.channel = channel;
+	}
+
+	header = IPC_GLB_TYPE(IPC_GLB_STREAM_MESSAGE) |
+		IPC_STR_TYPE(IPC_STR_STAGE_MESSAGE);
+	header |= (hsw->mixer_info.mixer_hw_id << IPC_STR_ID_SHIFT);
+	header |= (IPC_STG_SET_VOLUME << IPC_STG_TYPE_SHIFT);
+	header |= (stage_id << IPC_STG_ID_SHIFT);
+
+	req.curve_duration = hsw->curve_duration;
+	req.curve_type = hsw->curve_type;
+	req.target_volume = volume;
+
+	ret = ipc_tx_message_wait(hsw, header, &req, sizeof(req), NULL, 0);
+	if (ret < 0) {
+		dev_err(hsw->dev, "error: set mixer volume failed\n");
+		return ret;
+	}
+
+	return 0;
+}
+
+/* Stream API */
+struct sst_hsw_stream *sst_hsw_stream_new(struct sst_hsw *hsw, int id,
+	u32 (*notify_position)(struct sst_hsw_stream *stream, void *data),
+	void *data)
+{
+	struct sst_hsw_stream *stream;
+
+	stream = kzalloc(sizeof(*stream), GFP_KERNEL);
+	if (stream == NULL)
+		return NULL;
+
+	list_add(&stream->node, &hsw->stream_list);
+	stream->notify_position = notify_position;
+	stream->pdata = data;
+	stream->hsw = hsw;
+	stream->host_id = id;
+
+	/* work to process notification messages */
+	INIT_WORK(&stream->notify_work, hsw_notification_work);
+
+	return stream;
+}
+
+int sst_hsw_stream_free(struct sst_hsw *hsw, struct sst_hsw_stream *stream)
+{
+	u32 header;
+	int ret = 0;
+
+	/* dont free DSP streams that are not commited */
+	if (!stream->commited)
+		goto out;
+
+	trace_ipc_request("stream free", stream->host_id);
+
+	stream->free_req.stream_id = stream->reply.stream_hw_id;
+	header = IPC_GLB_TYPE(IPC_GLB_FREE_STREAM);
+
+	ret = ipc_tx_message_wait(hsw, header, &stream->free_req,
+		sizeof(stream->free_req), NULL, 0);
+	if (ret < 0) {
+		dev_err(hsw->dev, "error: free stream %d failed\n",
+			stream->free_req.stream_id);
+		return -EAGAIN;
+	}
+
+	trace_hsw_stream_free_req(stream, &stream->free_req);
+
+out:
+	list_del(&stream->node);
+	kfree(stream);
+
+	return ret;
+}
+
+int sst_hsw_stream_set_bits(struct sst_hsw *hsw,
+	struct sst_hsw_stream *stream, enum sst_hsw_bitdepth bits)
+{
+	if (stream->commited) {
+		dev_err(hsw->dev, "error: stream committed for set bits\n");
+		return -EINVAL;
+	}
+
+	stream->request.format.bitdepth = bits;
+	return 0;
+}
+
+int sst_hsw_stream_set_channels(struct sst_hsw *hsw,
+	struct sst_hsw_stream *stream, int channels)
+{
+	if (stream->commited) {
+		dev_err(hsw->dev, "error: stream committed for set channels\n");
+		return -EINVAL;
+	}
+
+	/* stereo is only supported atm */
+	if (channels != 2)
+		return -EINVAL;
+
+	stream->request.format.ch_num = channels;
+	return 0;
+}
+
+int sst_hsw_stream_set_rate(struct sst_hsw *hsw,
+	struct sst_hsw_stream *stream, int rate)
+{
+	if (stream->commited) {
+		dev_err(hsw->dev, "error: stream committed for set rate\n");
+		return -EINVAL;
+	}
+
+	stream->request.format.frequency = rate;
+	return 0;
+}
+
+int sst_hsw_stream_set_map_config(struct sst_hsw *hsw,
+	struct sst_hsw_stream *stream, u32 map,
+	enum sst_hsw_channel_config config)
+{
+	if (stream->commited) {
+		dev_err(hsw->dev, "error: stream committed for set map\n");
+		return -EINVAL;
+	}
+
+	stream->request.format.map = map;
+	stream->request.format.config = config;
+	return 0;
+}
+
+int sst_hsw_stream_set_style(struct sst_hsw *hsw,
+	struct sst_hsw_stream *stream, enum sst_hsw_interleaving style)
+{
+	if (stream->commited) {
+		dev_err(hsw->dev, "error: stream committed for set style\n");
+		return -EINVAL;
+	}
+
+	stream->request.format.style = style;
+	return 0;
+}
+
+int sst_hsw_stream_set_valid(struct sst_hsw *hsw,
+	struct sst_hsw_stream *stream, u32 bits)
+{
+	if (stream->commited) {
+		dev_err(hsw->dev, "error: stream committed for set valid bits\n");
+		return -EINVAL;
+	}
+
+	stream->request.format.valid_bit = bits;
+	return 0;
+}
+
+/* Stream Configuration */
+int sst_hsw_stream_format(struct sst_hsw *hsw, struct sst_hsw_stream *stream,
+	enum sst_hsw_stream_path_id path_id,
+	enum sst_hsw_stream_type stream_type,
+	enum sst_hsw_stream_format format_id)
+{
+	if (stream->commited) {
+		dev_err(hsw->dev, "error: stream committed for set format\n");
+		return -EINVAL;
+	}
+
+	stream->request.path_id = path_id;
+	stream->request.stream_type = stream_type;
+	stream->request.format_id = format_id;
+
+	trace_hsw_stream_alloc_request(stream, &stream->request);
+
+	return 0;
+}
+
+int sst_hsw_stream_buffer(struct sst_hsw *hsw, struct sst_hsw_stream *stream,
+	u32 ring_pt_address, u32 num_pages,
+	u32 ring_size, u32 ring_offset, u32 ring_first_pfn)
+{
+	if (stream->commited) {
+		dev_err(hsw->dev, "error: stream committed for buffer\n");
+		return -EINVAL;
+	}
+
+	stream->request.ringinfo.ring_pt_address = ring_pt_address;
+	stream->request.ringinfo.num_pages = num_pages;
+	stream->request.ringinfo.ring_size = ring_size;
+	stream->request.ringinfo.ring_offset = ring_offset;
+	stream->request.ringinfo.ring_first_pfn = ring_first_pfn;
+
+	trace_hsw_stream_buffer(stream);
+
+	return 0;
+}
+
+int sst_hsw_stream_set_module_info(struct sst_hsw *hsw,
+	struct sst_hsw_stream *stream, enum sst_hsw_module_id module_id,
+	u32 entry_point)
+{
+	struct sst_hsw_module_map *map = &stream->request.map;
+
+	if (stream->commited) {
+		dev_err(hsw->dev, "error: stream committed for set module\n");
+		return -EINVAL;
+	}
+
+	/* only support initial module atm */
+	map->module_entries_count = 1;
+	map->module_entries[0].module_id = module_id;
+	map->module_entries[0].entry_point = entry_point;
+
+	return 0;
+}
+
+int sst_hsw_stream_set_pmemory_info(struct sst_hsw *hsw,
+	struct sst_hsw_stream *stream, u32 offset, u32 size)
+{
+	if (stream->commited) {
+		dev_err(hsw->dev, "error: stream committed for set pmem\n");
+		return -EINVAL;
+	}
+
+	stream->request.persistent_mem.offset = offset;
+	stream->request.persistent_mem.size = size;
+
+	return 0;
+}
+
+int sst_hsw_stream_set_smemory_info(struct sst_hsw *hsw,
+	struct sst_hsw_stream *stream, u32 offset, u32 size)
+{
+	if (stream->commited) {
+		dev_err(hsw->dev, "error: stream committed for set smem\n");
+		return -EINVAL;
+	}
+
+	stream->request.scratch_mem.offset = offset;
+	stream->request.scratch_mem.size = size;
+
+	return 0;
+}
+
+int sst_hsw_stream_commit(struct sst_hsw *hsw, struct sst_hsw_stream *stream)
+{
+	struct sst_hsw_ipc_stream_alloc_req *str_req = &stream->request;
+	struct sst_hsw_ipc_stream_alloc_reply *reply = &stream->reply;
+	u32 header;
+	int ret;
+
+	trace_ipc_request("stream alloc", stream->host_id);
+
+	header = IPC_GLB_TYPE(IPC_GLB_ALLOCATE_STREAM);
+
+	ret = ipc_tx_message_wait(hsw, header, str_req, sizeof(*str_req),
+		reply, sizeof(*reply));
+	if (ret < 0) {
+		dev_err(hsw->dev, "error: stream commit failed\n");
+		return ret;
+	}
+
+	stream->commited = 1;
+	trace_hsw_stream_alloc_reply(stream);
+
+	return 0;
+}
+
+/* Stream Information - these calls could be inline but we want the IPC
+ ABI to be opaque to client PCM drivers to cope with any future ABI changes */
+int sst_hsw_stream_get_hw_id(struct sst_hsw *hsw,
+	struct sst_hsw_stream *stream)
+{
+	return stream->reply.stream_hw_id;
+}
+
+int sst_hsw_stream_get_mixer_id(struct sst_hsw *hsw,
+	struct sst_hsw_stream *stream)
+{
+	return stream->reply.mixer_hw_id;
+}
+
+u32 sst_hsw_stream_get_read_reg(struct sst_hsw *hsw,
+	struct sst_hsw_stream *stream)
+{
+	return stream->reply.read_position_register_address;
+}
+
+u32 sst_hsw_stream_get_pointer_reg(struct sst_hsw *hsw,
+	struct sst_hsw_stream *stream)
+{
+	return stream->reply.presentation_position_register_address;
+}
+
+u32 sst_hsw_stream_get_peak_reg(struct sst_hsw *hsw,
+	struct sst_hsw_stream *stream, u32 channel)
+{
+	if (channel >= 2)
+		return 0;
+
+	return stream->reply.peak_meter_register_address[channel];
+}
+
+u32 sst_hsw_stream_get_vol_reg(struct sst_hsw *hsw,
+	struct sst_hsw_stream *stream, u32 channel)
+{
+	if (channel >= 2)
+		return 0;
+
+	return stream->reply.volume_register_address[channel];
+}
+
+int sst_hsw_mixer_get_info(struct sst_hsw *hsw)
+{
+	struct sst_hsw_ipc_stream_info_reply *reply;
+	u32 header;
+	int ret;
+
+	reply = &hsw->mixer_info;
+	header = IPC_GLB_TYPE(IPC_GLB_GET_MIXER_STREAM_INFO);
+
+	trace_ipc_request("get global mixer info", 0);
+
+	ret = ipc_tx_message_wait(hsw, header, NULL, 0, reply, sizeof(*reply));
+	if (ret < 0) {
+		dev_err(hsw->dev, "error: get stream info failed\n");
+		return ret;
+	}
+
+	trace_hsw_mixer_info_reply(reply);
+
+	return 0;
+}
+
+/* Send stream command */
+static int sst_hsw_stream_operations(struct sst_hsw *hsw, int type,
+	int stream_id, int wait)
+{
+	u32 header;
+
+	header = IPC_GLB_TYPE(IPC_GLB_STREAM_MESSAGE) | IPC_STR_TYPE(type);
+	header |= (stream_id << IPC_STR_ID_SHIFT);
+
+	if (wait)
+		return ipc_tx_message_wait(hsw, header, NULL, 0, NULL, 0);
+	else
+		return ipc_tx_message_nowait(hsw, header, NULL, 0);
+}
+
+/* Stream ALSA trigger operations */
+int sst_hsw_stream_pause(struct sst_hsw *hsw, struct sst_hsw_stream *stream,
+	int wait)
+{
+	int ret;
+
+	trace_ipc_request("stream pause", stream->reply.stream_hw_id);
+
+	ret = sst_hsw_stream_operations(hsw, IPC_STR_PAUSE,
+		stream->reply.stream_hw_id, wait);
+	if (ret < 0)
+		dev_err(hsw->dev, "error: failed to pause stream %d\n",
+			stream->reply.stream_hw_id);
+
+	return ret;
+}
+
+int sst_hsw_stream_resume(struct sst_hsw *hsw, struct sst_hsw_stream *stream,
+	int wait)
+{
+	int ret;
+
+	trace_ipc_request("stream resume", stream->reply.stream_hw_id);
+
+	ret = sst_hsw_stream_operations(hsw, IPC_STR_RESUME,
+		stream->reply.stream_hw_id, wait);
+	if (ret < 0)
+		dev_err(hsw->dev, "error: failed to resume stream %d\n",
+			stream->reply.stream_hw_id);
+
+	return ret;
+}
+
+int sst_hsw_stream_reset(struct sst_hsw *hsw, struct sst_hsw_stream *stream)
+{
+	int ret, tries = 10;
+
+	/* dont reset streams that are not commited */
+	if (!stream->commited)
+		return 0;
+
+	/* wait for pause to complete before we reset the stream */
+	while (stream->running && tries--)
+		msleep(1);
+	if (!tries) {
+		dev_err(hsw->dev, "error: reset stream %d still running\n",
+			stream->reply.stream_hw_id);
+		return -EINVAL;
+	}
+
+	trace_ipc_request("stream reset", stream->reply.stream_hw_id);
+
+	ret = sst_hsw_stream_operations(hsw, IPC_STR_RESET,
+		stream->reply.stream_hw_id, 1);
+	if (ret < 0)
+		dev_err(hsw->dev, "error: failed to reset stream %d\n",
+			stream->reply.stream_hw_id);
+	return ret;
+}
+
+/* Stream pointer positions */
+int sst_hsw_get_dsp_position(struct sst_hsw *hsw,
+	struct sst_hsw_stream *stream)
+{
+	return stream->rpos.position;
+}
+
+int sst_hsw_stream_set_write_position(struct sst_hsw *hsw,
+	struct sst_hsw_stream *stream, u32 stage_id, u32 position)
+{
+	u32 header;
+	int ret;
+
+	trace_stream_write_position(stream->reply.stream_hw_id, position);
+
+	header = IPC_GLB_TYPE(IPC_GLB_STREAM_MESSAGE) |
+		IPC_STR_TYPE(IPC_STR_STAGE_MESSAGE);
+	header |= (stream->reply.stream_hw_id << IPC_STR_ID_SHIFT);
+	header |= (IPC_STG_SET_WRITE_POSITION << IPC_STG_TYPE_SHIFT);
+	header |= (stage_id << IPC_STG_ID_SHIFT);
+	stream->wpos.position = position;
+
+	ret = ipc_tx_message_nowait(hsw, header, &stream->wpos,
+		sizeof(stream->wpos));
+	if (ret < 0)
+		dev_err(hsw->dev, "error: stream %d set position %d failed\n",
+			stream->reply.stream_hw_id, position);
+
+	return ret;
+}
+
+/* physical BE config */
+int sst_hsw_device_set_config(struct sst_hsw *hsw,
+	enum sst_hsw_device_id dev, enum sst_hsw_device_mclk mclk,
+	enum sst_hsw_device_mode mode, u32 clock_divider)
+{
+	struct sst_hsw_ipc_device_config_req config;
+	u32 header;
+	int ret;
+
+	trace_ipc_request("set device config", dev);
+
+	config.ssp_interface = dev;
+	config.clock_frequency = mclk;
+	config.mode = mode;
+	config.clock_divider = clock_divider;
+
+	trace_hsw_device_config_req(&config);
+
+	header = IPC_GLB_TYPE(IPC_GLB_SET_DEVICE_FORMATS);
+
+	ret = ipc_tx_message_wait(hsw, header, &config, sizeof(config),
+		NULL, 0);
+	if (ret < 0)
+		dev_err(hsw->dev, "error: set device formats failed\n");
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(sst_hsw_device_set_config);
+
+/* DX Config */
+int sst_hsw_dx_set_state(struct sst_hsw *hsw,
+	enum sst_hsw_dx_state state, struct sst_hsw_ipc_dx_reply *dx)
+{
+	u32 header, state_;
+	int ret;
+
+	header = IPC_GLB_TYPE(IPC_GLB_ENTER_DX_STATE);
+	state_ = state;
+
+	trace_ipc_request("PM enter Dx state", state);
+
+	ret = ipc_tx_message_wait(hsw, header, &state_, sizeof(state_),
+		dx, sizeof(dx));
+	if (ret < 0) {
+		dev_err(hsw->dev, "ipc: error set dx state %d failed\n", state);
+		return ret;
+	}
+
+	dev_dbg(hsw->dev, "ipc: got %d entry numbers for state %d\n",
+		dx->entries_no, state);
+
+	memcpy(&hsw->dx, dx, sizeof(*dx));
+	return 0;
+}
+
+/* Used to save state into hsw->dx_reply */
+int sst_hsw_dx_get_state(struct sst_hsw *hsw, u32 item,
+	u32 *offset, u32 *size, u32 *source)
+{
+	struct sst_hsw_ipc_dx_memory_item *dx_mem;
+	struct sst_hsw_ipc_dx_reply *dx_reply;
+	int entry_no;
+
+	dx_reply = &hsw->dx;
+	entry_no = dx_reply->entries_no;
+
+	trace_ipc_request("PM get Dx state", entry_no);
+
+	if (item >= entry_no)
+		return -EINVAL;
+
+	dx_mem = &dx_reply->mem_info[item];
+	*offset = dx_mem->offset;
+	*size = dx_mem->size;
+	*source = dx_mem->source;
+
+	return 0;
+}
+
+static int msg_empty_list_init(struct sst_hsw *hsw)
+{
+	int i;
+
+	hsw->msg = kzalloc(sizeof(struct ipc_message) *
+		IPC_EMPTY_LIST_SIZE, GFP_KERNEL);
+	if (hsw->msg == NULL)
+		return -ENOMEM;
+
+	for (i = 0; i < IPC_EMPTY_LIST_SIZE; i++) {
+		init_waitqueue_head(&hsw->msg[i].waitq);
+		list_add(&hsw->msg[i].list, &hsw->empty_list);
+	}
+
+	return 0;
+}
+
+void sst_hsw_set_scratch_module(struct sst_hsw *hsw,
+	struct sst_module *scratch)
+{
+	hsw->scratch = scratch;
+}
+
+struct sst_dsp *sst_hsw_get_dsp(struct sst_hsw *hsw)
+{
+	return hsw->dsp;
+}
+
+static struct sst_dsp_device hsw_dev = {
+	.thread = hsw_irq_thread,
+	.ops = &haswell_ops,
+};
+
+int sst_hsw_dsp_init(struct device *dev, struct sst_pdata *pdata)
+{
+	struct sst_hsw_ipc_fw_version version;
+	struct sst_hsw *hsw;
+	struct sst_fw *hsw_sst_fw;
+	int ret;
+
+	dev_dbg(dev, "initialising Audio DSP IPC\n");
+
+	hsw = devm_kzalloc(dev, sizeof(*hsw), GFP_KERNEL);
+	if (hsw == NULL)
+		return -ENOMEM;
+
+	hsw->dev = dev;
+	INIT_LIST_HEAD(&hsw->stream_list);
+	INIT_LIST_HEAD(&hsw->tx_list);
+	INIT_LIST_HEAD(&hsw->rx_list);
+	INIT_LIST_HEAD(&hsw->empty_list);
+	init_waitqueue_head(&hsw->boot_wait);
+	init_waitqueue_head(&hsw->wait_txq);
+
+	ret = msg_empty_list_init(hsw);
+	if (ret < 0)
+		goto list_err;
+
+	/* start the IPC message thread */
+	init_kthread_worker(&hsw->kworker);
+	hsw->tx_thread = kthread_run(kthread_worker_fn,
+					   &hsw->kworker,
+					   dev_name(hsw->dev));
+	if (IS_ERR(hsw->tx_thread)) {
+		ret = PTR_ERR(hsw->tx_thread);
+		dev_err(hsw->dev, "error: failed to create message TX task\n");
+		goto list_err;
+	}
+	init_kthread_work(&hsw->kwork, ipc_tx_msgs);
+
+	hsw_dev.thread_context = hsw;
+
+	/* init SST shim */
+	hsw->dsp = sst_dsp_new(dev, &hsw_dev, pdata);
+	if (hsw->dsp == NULL) {
+		ret = -ENODEV;
+		goto list_err;
+	}
+
+	/* keep the DSP in reset state for base FW loading */
+	sst_dsp_reset(hsw->dsp);
+
+	hsw_sst_fw = sst_fw_new(hsw->dsp, pdata->fw, hsw);
+
+	if (hsw_sst_fw == NULL) {
+		ret = -ENODEV;
+		dev_err(dev, "error: failed to load firmware\n");
+		goto fw_err;
+	}
+
+	/* wait for DSP boot completion */
+	sst_dsp_boot(hsw->dsp);
+	ret = wait_event_timeout(hsw->boot_wait, hsw->boot_complete,
+		msecs_to_jiffies(IPC_BOOT_MSECS));
+	if (ret == 0) {
+		ret = -EIO;
+		dev_err(hsw->dev, "error: ADSP boot timeout\n");
+		goto boot_err;
+	}
+
+	/* get the FW version */
+	sst_hsw_fw_get_version(hsw, &version);
+	dev_info(hsw->dev, "FW loaded: type %d - version: %d.%d build %d\n",
+		version.type, version.major, version.minor, version.build);
+
+	/* get the globalmixer */
+	ret = sst_hsw_mixer_get_info(hsw);
+	if (ret < 0) {
+		dev_err(hsw->dev, "error: failed to get stream info\n");
+		goto boot_err;
+	}
+
+	pdata->dsp = hsw;
+	return 0;
+
+boot_err:
+	sst_dsp_reset(hsw->dsp);
+	sst_fw_free(hsw_sst_fw);
+fw_err:
+	sst_dsp_free(hsw->dsp);
+	kfree(hsw->msg);
+list_err:
+	return ret;
+}
+EXPORT_SYMBOL_GPL(sst_hsw_dsp_init);
+
+void sst_hsw_dsp_free(struct device *dev, struct sst_pdata *pdata)
+{
+	struct sst_hsw *hsw = pdata->dsp;
+
+	sst_dsp_reset(hsw->dsp);
+	sst_fw_free_all(hsw->dsp);
+	sst_dsp_free(hsw->dsp);
+	kfree(hsw->scratch);
+	kfree(hsw->msg);
+}
+EXPORT_SYMBOL_GPL(sst_hsw_dsp_free);
diff --git a/sound/soc/intel/sst-haswell-ipc.h b/sound/soc/intel/sst-haswell-ipc.h
new file mode 100644
index 000000000000..d517929ccc38
--- /dev/null
+++ b/sound/soc/intel/sst-haswell-ipc.h
@@ -0,0 +1,488 @@
+/*
+ * Intel SST Haswell/Broadwell IPC Support
+ *
+ * Copyright (C) 2013, Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License version
+ * 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef __SST_HASWELL_IPC_H
+#define __SST_HASWELL_IPC_H
+
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/platform_device.h>
+
+#define SST_HSW_NO_CHANNELS		2
+#define SST_HSW_MAX_DX_REGIONS		14
+
+#define SST_HSW_FW_LOG_CONFIG_DWORDS	12
+#define SST_HSW_GLOBAL_LOG		15
+
+/**
+ * Upfront defined maximum message size that is
+ * expected by the in/out communication pipes in FW.
+ */
+#define SST_HSW_IPC_MAX_PAYLOAD_SIZE	400
+#define SST_HSW_MAX_INFO_SIZE		64
+#define SST_HSW_BUILD_HASH_LENGTH	40
+
+struct sst_hsw;
+struct sst_hsw_stream;
+struct sst_hsw_log_stream;
+struct sst_pdata;
+struct sst_module;
+extern struct sst_ops haswell_ops;
+
+/* Stream Allocate Path ID */
+enum sst_hsw_stream_path_id {
+	SST_HSW_STREAM_PATH_SSP0_OUT = 0,
+	SST_HSW_STREAM_PATH_SSP0_IN = 1,
+	SST_HSW_STREAM_PATH_MAX_PATH_ID = 2,
+};
+
+/* Stream Allocate Stream Type */
+enum sst_hsw_stream_type {
+	SST_HSW_STREAM_TYPE_RENDER = 0,
+	SST_HSW_STREAM_TYPE_SYSTEM = 1,
+	SST_HSW_STREAM_TYPE_CAPTURE = 2,
+	SST_HSW_STREAM_TYPE_LOOPBACK = 3,
+	SST_HSW_STREAM_TYPE_MAX_STREAM_TYPE = 4,
+};
+
+/* Stream Allocate Stream Format */
+enum sst_hsw_stream_format {
+	SST_HSW_STREAM_FORMAT_PCM_FORMAT = 0,
+	SST_HSW_STREAM_FORMAT_MP3_FORMAT = 1,
+	SST_HSW_STREAM_FORMAT_AAC_FORMAT = 2,
+	SST_HSW_STREAM_FORMAT_MAX_FORMAT_ID = 3,
+};
+
+/* Device ID */
+enum sst_hsw_device_id {
+	SST_HSW_DEVICE_SSP_0   = 0,
+	SST_HSW_DEVICE_SSP_1   = 1,
+};
+
+/* Device Master Clock Frequency */
+enum sst_hsw_device_mclk {
+	SST_HSW_DEVICE_MCLK_OFF         = 0,
+	SST_HSW_DEVICE_MCLK_FREQ_6_MHZ  = 1,
+	SST_HSW_DEVICE_MCLK_FREQ_12_MHZ = 2,
+	SST_HSW_DEVICE_MCLK_FREQ_24_MHZ = 3,
+};
+
+/* Device Clock Master */
+enum sst_hsw_device_mode {
+	SST_HSW_DEVICE_CLOCK_SLAVE   = 0,
+	SST_HSW_DEVICE_CLOCK_MASTER  = 1,
+};
+
+/* DX Power State */
+enum sst_hsw_dx_state {
+	SST_HSW_DX_STATE_D0     = 0,
+	SST_HSW_DX_STATE_D1     = 1,
+	SST_HSW_DX_STATE_D3     = 3,
+	SST_HSW_DX_STATE_MAX	= 3,
+};
+
+/* Audio stream stage IDs */
+enum sst_hsw_fx_stage_id {
+	SST_HSW_STAGE_ID_WAVES = 0,
+	SST_HSW_STAGE_ID_DTS   = 1,
+	SST_HSW_STAGE_ID_DOLBY = 2,
+	SST_HSW_STAGE_ID_BOOST = 3,
+	SST_HSW_STAGE_ID_MAX_FX_ID
+};
+
+/* DX State Type */
+enum sst_hsw_dx_type {
+	SST_HSW_DX_TYPE_FW_IMAGE = 0,
+	SST_HSW_DX_TYPE_MEMORY_DUMP = 1
+};
+
+/* Volume Curve Type*/
+enum sst_hsw_volume_curve {
+	SST_HSW_VOLUME_CURVE_NONE = 0,
+	SST_HSW_VOLUME_CURVE_FADE = 1
+};
+
+/* Sample ordering */
+enum sst_hsw_interleaving {
+	SST_HSW_INTERLEAVING_PER_CHANNEL = 0,
+	SST_HSW_INTERLEAVING_PER_SAMPLE  = 1,
+};
+
+/* Channel indices */
+enum sst_hsw_channel_index {
+	SST_HSW_CHANNEL_LEFT            = 0,
+	SST_HSW_CHANNEL_CENTER          = 1,
+	SST_HSW_CHANNEL_RIGHT           = 2,
+	SST_HSW_CHANNEL_LEFT_SURROUND   = 3,
+	SST_HSW_CHANNEL_CENTER_SURROUND = 3,
+	SST_HSW_CHANNEL_RIGHT_SURROUND  = 4,
+	SST_HSW_CHANNEL_LFE             = 7,
+	SST_HSW_CHANNEL_INVALID         = 0xF,
+};
+
+/* List of supported channel maps. */
+enum sst_hsw_channel_config {
+	SST_HSW_CHANNEL_CONFIG_MONO      = 0, /* mono only. */
+	SST_HSW_CHANNEL_CONFIG_STEREO    = 1, /* L & R. */
+	SST_HSW_CHANNEL_CONFIG_2_POINT_1 = 2, /* L, R & LFE; PCM only. */
+	SST_HSW_CHANNEL_CONFIG_3_POINT_0 = 3, /* L, C & R; MP3 & AAC only. */
+	SST_HSW_CHANNEL_CONFIG_3_POINT_1 = 4, /* L, C, R & LFE; PCM only. */
+	SST_HSW_CHANNEL_CONFIG_QUATRO    = 5, /* L, R, Ls & Rs; PCM only. */
+	SST_HSW_CHANNEL_CONFIG_4_POINT_0 = 6, /* L, C, R & Cs; MP3 & AAC only. */
+	SST_HSW_CHANNEL_CONFIG_5_POINT_0 = 7, /* L, C, R, Ls & Rs. */
+	SST_HSW_CHANNEL_CONFIG_5_POINT_1 = 8, /* L, C, R, Ls, Rs & LFE. */
+	SST_HSW_CHANNEL_CONFIG_DUAL_MONO = 9, /* One channel replicated in two. */
+	SST_HSW_CHANNEL_CONFIG_INVALID,
+};
+
+/* List of supported bit depths. */
+enum sst_hsw_bitdepth {
+	SST_HSW_DEPTH_8BIT  = 8,
+	SST_HSW_DEPTH_16BIT = 16,
+	SST_HSW_DEPTH_24BIT = 24, /* Default. */
+	SST_HSW_DEPTH_32BIT = 32,
+	SST_HSW_DEPTH_INVALID = 33,
+};
+
+enum sst_hsw_module_id {
+	SST_HSW_MODULE_BASE_FW = 0x0,
+	SST_HSW_MODULE_MP3     = 0x1,
+	SST_HSW_MODULE_AAC_5_1 = 0x2,
+	SST_HSW_MODULE_AAC_2_0 = 0x3,
+	SST_HSW_MODULE_SRC     = 0x4,
+	SST_HSW_MODULE_WAVES   = 0x5,
+	SST_HSW_MODULE_DOLBY   = 0x6,
+	SST_HSW_MODULE_BOOST   = 0x7,
+	SST_HSW_MODULE_LPAL    = 0x8,
+	SST_HSW_MODULE_DTS     = 0x9,
+	SST_HSW_MODULE_PCM_CAPTURE = 0xA,
+	SST_HSW_MODULE_PCM_SYSTEM = 0xB,
+	SST_HSW_MODULE_PCM_REFERENCE = 0xC,
+	SST_HSW_MODULE_PCM = 0xD,
+	SST_HSW_MODULE_BLUETOOTH_RENDER_MODULE = 0xE,
+	SST_HSW_MODULE_BLUETOOTH_CAPTURE_MODULE = 0xF,
+	SST_HSW_MAX_MODULE_ID,
+};
+
+enum sst_hsw_performance_action {
+	SST_HSW_PERF_START = 0,
+	SST_HSW_PERF_STOP = 1,
+};
+
+/* SST firmware module info */
+struct sst_hsw_module_info {
+	u8 name[SST_HSW_MAX_INFO_SIZE];
+	u8 version[SST_HSW_MAX_INFO_SIZE];
+} __attribute__((packed));
+
+/* Module entry point */
+struct sst_hsw_module_entry {
+	enum sst_hsw_module_id module_id;
+	u32 entry_point;
+} __attribute__((packed));
+
+/* Module map - alignement matches DSP */
+struct sst_hsw_module_map {
+	u8 module_entries_count;
+	struct sst_hsw_module_entry module_entries[1];
+} __attribute__((packed));
+
+struct sst_hsw_memory_info {
+	u32 offset;
+	u32 size;
+} __attribute__((packed));
+
+struct sst_hsw_fx_enable {
+	struct sst_hsw_module_map module_map;
+	struct sst_hsw_memory_info persistent_mem;
+} __attribute__((packed));
+
+struct sst_hsw_get_fx_param {
+	u32 parameter_id;
+	u32 param_size;
+} __attribute__((packed));
+
+struct sst_hsw_perf_action {
+	u32 action;
+} __attribute__((packed));
+
+struct sst_hsw_perf_data {
+	u64 timestamp;
+	u64 cycles;
+	u64 datatime;
+} __attribute__((packed));
+
+/* FW version */
+struct sst_hsw_ipc_fw_version {
+	u8 build;
+	u8 minor;
+	u8 major;
+	u8 type;
+	u8 fw_build_hash[SST_HSW_BUILD_HASH_LENGTH];
+	u32 fw_log_providers_hash;
+} __attribute__((packed));
+
+/* Stream ring info */
+struct sst_hsw_ipc_stream_ring {
+	u32 ring_pt_address;
+	u32 num_pages;
+	u32 ring_size;
+	u32 ring_offset;
+	u32 ring_first_pfn;
+} __attribute__((packed));
+
+/* Debug Dump Log Enable Request */
+struct sst_hsw_ipc_debug_log_enable_req {
+	struct sst_hsw_ipc_stream_ring ringinfo;
+	u32 config[SST_HSW_FW_LOG_CONFIG_DWORDS];
+} __attribute__((packed));
+
+/* Debug Dump Log Reply */
+struct sst_hsw_ipc_debug_log_reply {
+	u32 log_buffer_begining;
+	u32 log_buffer_size;
+} __attribute__((packed));
+
+/* Stream glitch position */
+struct sst_hsw_ipc_stream_glitch_position {
+	u32 glitch_type;
+	u32 present_pos;
+	u32 write_pos;
+} __attribute__((packed));
+
+/* Stream get position */
+struct sst_hsw_ipc_stream_get_position {
+	u32 position;
+	u32 fw_cycle_count;
+} __attribute__((packed));
+
+/* Stream set position */
+struct sst_hsw_ipc_stream_set_position {
+	u32 position;
+	u32 end_of_buffer;
+} __attribute__((packed));
+
+/* Stream Free Request */
+struct sst_hsw_ipc_stream_free_req {
+	u8 stream_id;
+	u8 reserved[3];
+} __attribute__((packed));
+
+/* Set Volume Request */
+struct sst_hsw_ipc_volume_req {
+	u32 channel;
+	u32 target_volume;
+	u64 curve_duration;
+	u32 curve_type;
+} __attribute__((packed));
+
+/* Device Configuration Request */
+struct sst_hsw_ipc_device_config_req {
+	u32 ssp_interface;
+	u32 clock_frequency;
+	u32 mode;
+	u16 clock_divider;
+	u16 reserved;
+} __attribute__((packed));
+
+/* Audio Data formats */
+struct sst_hsw_audio_data_format_ipc {
+	u32 frequency;
+	u32 bitdepth;
+	u32 map;
+	u32 config;
+	u32 style;
+	u8 ch_num;
+	u8 valid_bit;
+	u8 reserved[2];
+} __attribute__((packed));
+
+/* Stream Allocate Request */
+struct sst_hsw_ipc_stream_alloc_req {
+	u8 path_id;
+	u8 stream_type;
+	u8 format_id;
+	u8 reserved;
+	struct sst_hsw_audio_data_format_ipc format;
+	struct sst_hsw_ipc_stream_ring ringinfo;
+	struct sst_hsw_module_map map;
+	struct sst_hsw_memory_info persistent_mem;
+	struct sst_hsw_memory_info scratch_mem;
+	u32 number_of_notifications;
+} __attribute__((packed));
+
+/* Stream Allocate Reply */
+struct sst_hsw_ipc_stream_alloc_reply {
+	u32 stream_hw_id;
+	u32 mixer_hw_id; // returns rate ????
+	u32 read_position_register_address;
+	u32 presentation_position_register_address;
+	u32 peak_meter_register_address[SST_HSW_NO_CHANNELS];
+	u32 volume_register_address[SST_HSW_NO_CHANNELS];
+} __attribute__((packed));
+
+/* Get Mixer Stream Info */
+struct sst_hsw_ipc_stream_info_reply {
+	u32 mixer_hw_id;
+	u32 peak_meter_register_address[SST_HSW_NO_CHANNELS];
+	u32 volume_register_address[SST_HSW_NO_CHANNELS];
+} __attribute__((packed));
+
+/* DX State Request */
+struct sst_hsw_ipc_dx_req {
+	u8 state;
+	u8 reserved[3];
+} __attribute__((packed));
+
+/* DX State Reply Memory Info Item */
+struct sst_hsw_ipc_dx_memory_item {
+	u32 offset;
+	u32 size;
+	u32 source;
+} __attribute__((packed));
+
+/* DX State Reply */
+struct sst_hsw_ipc_dx_reply {
+	u32 entries_no;
+	struct sst_hsw_ipc_dx_memory_item mem_info[SST_HSW_MAX_DX_REGIONS];
+} __attribute__((packed));
+
+struct sst_hsw_ipc_fw_version;
+
+/* SST Init & Free */
+struct sst_hsw *sst_hsw_new(struct device *dev, const u8 *fw, size_t fw_length,
+	u32 fw_offset);
+void sst_hsw_free(struct sst_hsw *hsw);
+int sst_hsw_fw_get_version(struct sst_hsw *hsw,
+	struct sst_hsw_ipc_fw_version *version);
+u32 create_channel_map(enum sst_hsw_channel_config config);
+
+/* Stream Mixer Controls - */
+int sst_hsw_stream_mute(struct sst_hsw *hsw, struct sst_hsw_stream *stream,
+	u32 stage_id, u32 channel);
+int sst_hsw_stream_unmute(struct sst_hsw *hsw, struct sst_hsw_stream *stream,
+	u32 stage_id, u32 channel);
+
+int sst_hsw_stream_set_volume(struct sst_hsw *hsw,
+	struct sst_hsw_stream *stream, u32 stage_id, u32 channel, u32 volume);
+int sst_hsw_stream_get_volume(struct sst_hsw *hsw,
+	struct sst_hsw_stream *stream, u32 stage_id, u32 channel, u32 *volume);
+
+int sst_hsw_stream_set_volume_curve(struct sst_hsw *hsw,
+	struct sst_hsw_stream *stream, u64 curve_duration,
+	enum sst_hsw_volume_curve curve);
+
+/* Global Mixer Controls - */
+int sst_hsw_mixer_mute(struct sst_hsw *hsw, u32 stage_id, u32 channel);
+int sst_hsw_mixer_unmute(struct sst_hsw *hsw, u32 stage_id, u32 channel);
+
+int sst_hsw_mixer_set_volume(struct sst_hsw *hsw, u32 stage_id, u32 channel,
+	u32 volume);
+int sst_hsw_mixer_get_volume(struct sst_hsw *hsw, u32 stage_id, u32 channel,
+	u32 *volume);
+
+int sst_hsw_mixer_set_volume_curve(struct sst_hsw *hsw,
+	u64 curve_duration, enum sst_hsw_volume_curve curve);
+
+/* Stream API */
+struct sst_hsw_stream *sst_hsw_stream_new(struct sst_hsw *hsw, int id,
+	u32 (*get_write_position)(struct sst_hsw_stream *stream, void *data),
+	void *data);
+
+int sst_hsw_stream_free(struct sst_hsw *hsw, struct sst_hsw_stream *stream);
+
+/* Stream Configuration */
+int sst_hsw_stream_format(struct sst_hsw *hsw, struct sst_hsw_stream *stream,
+	enum sst_hsw_stream_path_id path_id,
+	enum sst_hsw_stream_type stream_type,
+	enum sst_hsw_stream_format format_id);
+
+int sst_hsw_stream_buffer(struct sst_hsw *hsw, struct sst_hsw_stream *stream,
+	u32 ring_pt_address, u32 num_pages,
+	u32 ring_size, u32 ring_offset, u32 ring_first_pfn);
+
+int sst_hsw_stream_commit(struct sst_hsw *hsw, struct sst_hsw_stream *stream);
+
+int sst_hsw_stream_set_valid(struct sst_hsw *hsw, struct sst_hsw_stream *stream,
+	u32 bits);
+int sst_hsw_stream_set_rate(struct sst_hsw *hsw, struct sst_hsw_stream *stream,
+	int rate);
+int sst_hsw_stream_set_bits(struct sst_hsw *hsw, struct sst_hsw_stream *stream,
+	enum sst_hsw_bitdepth bits);
+int sst_hsw_stream_set_channels(struct sst_hsw *hsw,
+	struct sst_hsw_stream *stream, int channels);
+int sst_hsw_stream_set_map_config(struct sst_hsw *hsw,
+	struct sst_hsw_stream *stream, u32 map,
+	enum sst_hsw_channel_config config);
+int sst_hsw_stream_set_style(struct sst_hsw *hsw, struct sst_hsw_stream *stream,
+	enum sst_hsw_interleaving style);
+int sst_hsw_stream_set_module_info(struct sst_hsw *hsw,
+	struct sst_hsw_stream *stream, enum sst_hsw_module_id module_id,
+	u32 entry_point);
+int sst_hsw_stream_set_pmemory_info(struct sst_hsw *hsw,
+	struct sst_hsw_stream *stream, u32 offset, u32 size);
+int sst_hsw_stream_set_smemory_info(struct sst_hsw *hsw,
+	struct sst_hsw_stream *stream, u32 offset, u32 size);
+int sst_hsw_stream_get_hw_id(struct sst_hsw *hsw,
+	struct sst_hsw_stream *stream);
+int sst_hsw_stream_get_mixer_id(struct sst_hsw *hsw,
+	struct sst_hsw_stream *stream);
+u32 sst_hsw_stream_get_read_reg(struct sst_hsw *hsw,
+	struct sst_hsw_stream *stream);
+u32 sst_hsw_stream_get_pointer_reg(struct sst_hsw *hsw,
+	struct sst_hsw_stream *stream);
+u32 sst_hsw_stream_get_peak_reg(struct sst_hsw *hsw,
+	struct sst_hsw_stream *stream, u32 channel);
+u32 sst_hsw_stream_get_vol_reg(struct sst_hsw *hsw,
+	struct sst_hsw_stream *stream, u32 channel);
+int sst_hsw_mixer_get_info(struct sst_hsw *hsw);
+
+/* Stream ALSA trigger operations */
+int sst_hsw_stream_pause(struct sst_hsw *hsw, struct sst_hsw_stream *stream,
+	int wait);
+int sst_hsw_stream_resume(struct sst_hsw *hsw, struct sst_hsw_stream *stream,
+	int wait);
+int sst_hsw_stream_reset(struct sst_hsw *hsw, struct sst_hsw_stream *stream);
+
+/* Stream pointer positions */
+int sst_hsw_stream_get_read_pos(struct sst_hsw *hsw,
+	struct sst_hsw_stream *stream, u32 *position);
+int sst_hsw_stream_get_write_pos(struct sst_hsw *hsw,
+	struct sst_hsw_stream *stream, u32 *position);
+int sst_hsw_stream_set_write_position(struct sst_hsw *hsw,
+	struct sst_hsw_stream *stream, u32 stage_id, u32 position);
+int sst_hsw_get_dsp_position(struct sst_hsw *hsw,
+	struct sst_hsw_stream *stream);
+
+/* HW port config */
+int sst_hsw_device_set_config(struct sst_hsw *hsw,
+	enum sst_hsw_device_id dev, enum sst_hsw_device_mclk mclk,
+	enum sst_hsw_device_mode mode, u32 clock_divider);
+
+/* DX Config */
+int sst_hsw_dx_set_state(struct sst_hsw *hsw,
+	enum sst_hsw_dx_state state, struct sst_hsw_ipc_dx_reply *dx);
+int sst_hsw_dx_get_state(struct sst_hsw *hsw, u32 item,
+	u32 *offset, u32 *size, u32 *source);
+
+/* init */
+int sst_hsw_dsp_init(struct device *dev, struct sst_pdata *pdata);
+void sst_hsw_dsp_free(struct device *dev, struct sst_pdata *pdata);
+struct sst_dsp *sst_hsw_get_dsp(struct sst_hsw *hsw);
+void sst_hsw_set_scratch_module(struct sst_hsw *hsw,
+	struct sst_module *scratch);
+
+#endif
diff --git a/sound/soc/intel/sst-haswell-pcm.c b/sound/soc/intel/sst-haswell-pcm.c
new file mode 100644
index 000000000000..0a32dd13a23d
--- /dev/null
+++ b/sound/soc/intel/sst-haswell-pcm.c
@@ -0,0 +1,872 @@
+/*
+ * Intel SST Haswell/Broadwell PCM Support
+ *
+ * Copyright (C) 2013, Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License version
+ * 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/dma-mapping.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <asm/page.h>
+#include <asm/pgtable.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/dmaengine_pcm.h>
+#include <sound/soc.h>
+#include <sound/tlv.h>
+#include <sound/compress_driver.h>
+
+#include "sst-haswell-ipc.h"
+#include "sst-dsp-priv.h"
+#include "sst-dsp.h"
+
+#define HSW_PCM_COUNT		6
+#define HSW_VOLUME_MAX		0x7FFFFFFF	/* 0dB */
+
+/* simple volume table */
+static const u32 volume_map[] = {
+	HSW_VOLUME_MAX >> 30,
+	HSW_VOLUME_MAX >> 29,
+	HSW_VOLUME_MAX >> 28,
+	HSW_VOLUME_MAX >> 27,
+	HSW_VOLUME_MAX >> 26,
+	HSW_VOLUME_MAX >> 25,
+	HSW_VOLUME_MAX >> 24,
+	HSW_VOLUME_MAX >> 23,
+	HSW_VOLUME_MAX >> 22,
+	HSW_VOLUME_MAX >> 21,
+	HSW_VOLUME_MAX >> 20,
+	HSW_VOLUME_MAX >> 19,
+	HSW_VOLUME_MAX >> 18,
+	HSW_VOLUME_MAX >> 17,
+	HSW_VOLUME_MAX >> 16,
+	HSW_VOLUME_MAX >> 15,
+	HSW_VOLUME_MAX >> 14,
+	HSW_VOLUME_MAX >> 13,
+	HSW_VOLUME_MAX >> 12,
+	HSW_VOLUME_MAX >> 11,
+	HSW_VOLUME_MAX >> 10,
+	HSW_VOLUME_MAX >> 9,
+	HSW_VOLUME_MAX >> 8,
+	HSW_VOLUME_MAX >> 7,
+	HSW_VOLUME_MAX >> 6,
+	HSW_VOLUME_MAX >> 5,
+	HSW_VOLUME_MAX >> 4,
+	HSW_VOLUME_MAX >> 3,
+	HSW_VOLUME_MAX >> 2,
+	HSW_VOLUME_MAX >> 1,
+	HSW_VOLUME_MAX >> 0,
+};
+
+#define HSW_PCM_PERIODS_MAX	64
+#define HSW_PCM_PERIODS_MIN	2
+
+static const struct snd_pcm_hardware hsw_pcm_hardware = {
+	.info			= SNDRV_PCM_INFO_MMAP |
+				  SNDRV_PCM_INFO_MMAP_VALID |
+				  SNDRV_PCM_INFO_INTERLEAVED |
+				  SNDRV_PCM_INFO_PAUSE |
+				  SNDRV_PCM_INFO_RESUME |
+				  SNDRV_PCM_INFO_NO_PERIOD_WAKEUP,
+	.formats		= SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FORMAT_S24_LE |
+				  SNDRV_PCM_FMTBIT_S32_LE,
+	.period_bytes_min	= PAGE_SIZE,
+	.period_bytes_max	= (HSW_PCM_PERIODS_MAX / HSW_PCM_PERIODS_MIN) * PAGE_SIZE,
+	.periods_min		= HSW_PCM_PERIODS_MIN,
+	.periods_max		= HSW_PCM_PERIODS_MAX,
+	.buffer_bytes_max	= HSW_PCM_PERIODS_MAX * PAGE_SIZE,
+};
+
+/* private data for each PCM DSP stream */
+struct hsw_pcm_data {
+	int dai_id;
+	struct sst_hsw_stream *stream;
+	u32 volume[2];
+	struct snd_pcm_substream *substream;
+	struct snd_compr_stream *cstream;
+	unsigned int wpos;
+	struct mutex mutex;
+};
+
+/* private data for the driver */
+struct hsw_priv_data {
+	/* runtime DSP */
+	struct sst_hsw *hsw;
+
+	/* page tables */
+	unsigned char *pcm_pg[HSW_PCM_COUNT][2];
+
+	/* DAI data */
+	struct hsw_pcm_data pcm[HSW_PCM_COUNT];
+};
+
+static inline u32 hsw_mixer_to_ipc(unsigned int value)
+{
+	if (value >= ARRAY_SIZE(volume_map))
+		return volume_map[0];
+	else
+		return volume_map[value];
+}
+
+static inline unsigned int hsw_ipc_to_mixer(u32 value)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(volume_map); i++) {
+		if (volume_map[i] >= value)
+			return i;
+	}
+
+	return i - 1;
+}
+
+static int hsw_stream_volume_put(struct snd_kcontrol *kcontrol,
+				struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_platform *platform = snd_kcontrol_chip(kcontrol);
+	struct soc_mixer_control *mc =
+		(struct soc_mixer_control *)kcontrol->private_value;
+	struct hsw_priv_data *pdata =
+		snd_soc_platform_get_drvdata(platform);
+	struct hsw_pcm_data *pcm_data = &pdata->pcm[mc->reg];
+	struct sst_hsw *hsw = pdata->hsw;
+	u32 volume;
+
+	mutex_lock(&pcm_data->mutex);
+
+	if (!pcm_data->stream) {
+		pcm_data->volume[0] =
+			hsw_mixer_to_ipc(ucontrol->value.integer.value[0]);
+		pcm_data->volume[1] =
+			hsw_mixer_to_ipc(ucontrol->value.integer.value[1]);
+		mutex_unlock(&pcm_data->mutex);
+		return 0;
+	}
+
+	if (ucontrol->value.integer.value[0] ==
+		ucontrol->value.integer.value[1]) {
+		volume = hsw_mixer_to_ipc(ucontrol->value.integer.value[0]);
+		sst_hsw_stream_set_volume(hsw, pcm_data->stream, 0, 2, volume);
+	} else {
+		volume = hsw_mixer_to_ipc(ucontrol->value.integer.value[0]);
+		sst_hsw_stream_set_volume(hsw, pcm_data->stream, 0, 0, volume);
+		volume = hsw_mixer_to_ipc(ucontrol->value.integer.value[1]);
+		sst_hsw_stream_set_volume(hsw, pcm_data->stream, 0, 1, volume);
+	}
+
+	mutex_unlock(&pcm_data->mutex);
+	return 0;
+}
+
+static int hsw_stream_volume_get(struct snd_kcontrol *kcontrol,
+				struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_platform *platform = snd_kcontrol_chip(kcontrol);
+	struct soc_mixer_control *mc =
+		(struct soc_mixer_control *)kcontrol->private_value;
+	struct hsw_priv_data *pdata =
+		snd_soc_platform_get_drvdata(platform);
+	struct hsw_pcm_data *pcm_data = &pdata->pcm[mc->reg];
+	struct sst_hsw *hsw = pdata->hsw;
+	u32 volume;
+
+	mutex_lock(&pcm_data->mutex);
+
+	if (!pcm_data->stream) {
+		ucontrol->value.integer.value[0] =
+			hsw_ipc_to_mixer(pcm_data->volume[0]);
+		ucontrol->value.integer.value[1] =
+			hsw_ipc_to_mixer(pcm_data->volume[1]);
+		mutex_unlock(&pcm_data->mutex);
+		return 0;
+	}
+
+	sst_hsw_stream_get_volume(hsw, pcm_data->stream, 0, 0, &volume);
+	ucontrol->value.integer.value[0] = hsw_ipc_to_mixer(volume);
+	sst_hsw_stream_get_volume(hsw, pcm_data->stream, 0, 1, &volume);
+	ucontrol->value.integer.value[1] = hsw_ipc_to_mixer(volume);
+	mutex_unlock(&pcm_data->mutex);
+
+	return 0;
+}
+
+static int hsw_volume_put(struct snd_kcontrol *kcontrol,
+				struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_platform *platform = snd_kcontrol_chip(kcontrol);
+	struct hsw_priv_data *pdata = snd_soc_platform_get_drvdata(platform);
+	struct sst_hsw *hsw = pdata->hsw;
+	u32 volume;
+
+	if (ucontrol->value.integer.value[0] ==
+		ucontrol->value.integer.value[1]) {
+
+		volume = hsw_mixer_to_ipc(ucontrol->value.integer.value[0]);
+		sst_hsw_mixer_set_volume(hsw, 0, 2, volume);
+
+	} else {
+		volume = hsw_mixer_to_ipc(ucontrol->value.integer.value[0]);
+		sst_hsw_mixer_set_volume(hsw, 0, 0, volume);
+
+		volume = hsw_mixer_to_ipc(ucontrol->value.integer.value[1]);
+		sst_hsw_mixer_set_volume(hsw, 0, 1, volume);
+	}
+
+	return 0;
+}
+
+static int hsw_volume_get(struct snd_kcontrol *kcontrol,
+				struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_platform *platform = snd_kcontrol_chip(kcontrol);
+	struct hsw_priv_data *pdata = snd_soc_platform_get_drvdata(platform);
+	struct sst_hsw *hsw = pdata->hsw;
+	unsigned int volume = 0;
+
+	sst_hsw_mixer_get_volume(hsw, 0, 0, &volume);
+	ucontrol->value.integer.value[0] = hsw_ipc_to_mixer(volume);
+
+	sst_hsw_mixer_get_volume(hsw, 0, 1, &volume);
+	ucontrol->value.integer.value[1] = hsw_ipc_to_mixer(volume);
+
+	return 0;
+}
+
+/* TLV used by both global and stream volumes */
+static const DECLARE_TLV_DB_SCALE(hsw_vol_tlv, -9000, 300, 1);
+
+/* System Pin has no volume control */
+static const struct snd_kcontrol_new hsw_volume_controls[] = {
+	/* Global DSP volume */
+	SOC_DOUBLE_EXT_TLV("Master Playback Volume", 0, 0, 8,
+		ARRAY_SIZE(volume_map) -1, 0,
+		hsw_volume_get, hsw_volume_put, hsw_vol_tlv),
+	/* Offload 0 volume */
+	SOC_DOUBLE_EXT_TLV("Media0 Playback Volume", 1, 0, 8,
+		ARRAY_SIZE(volume_map), 0,
+		hsw_stream_volume_get, hsw_stream_volume_put, hsw_vol_tlv),
+	/* Offload 1 volume */
+	SOC_DOUBLE_EXT_TLV("Media1 Playback Volume", 2, 0, 8,
+		ARRAY_SIZE(volume_map), 0,
+		hsw_stream_volume_get, hsw_stream_volume_put, hsw_vol_tlv),
+	/* Loopback volume */
+	SOC_DOUBLE_EXT_TLV("Loopback Capture Volume", 3, 0, 8,
+		ARRAY_SIZE(volume_map), 0,
+		hsw_stream_volume_get, hsw_stream_volume_put, hsw_vol_tlv),
+	/* Mic Capture volume */
+	SOC_DOUBLE_EXT_TLV("Mic Capture Volume", 4, 0, 8,
+		ARRAY_SIZE(volume_map), 0,
+		hsw_stream_volume_get, hsw_stream_volume_put, hsw_vol_tlv),
+};
+
+/* Create DMA buffer page table for DSP */
+static int create_adsp_page_table(struct hsw_priv_data *pdata,
+	struct snd_soc_pcm_runtime *rtd,
+	unsigned char *dma_area, size_t size, int pcm, int stream)
+{
+	int i, pages;
+
+	if (size % PAGE_SIZE)
+		pages = (size / PAGE_SIZE) + 1;
+	else
+		pages = size / PAGE_SIZE;
+
+	dev_dbg(rtd->dev, "generating page table for %p size 0x%zu pages %d\n",
+		dma_area, size, pages);
+
+	for (i = 0; i < pages; i++) {
+		u32 idx = (((i << 2) + i)) >> 1;
+		u32 pfn = (virt_to_phys(dma_area + i * PAGE_SIZE)) >> PAGE_SHIFT;
+		u32 *pg_table;
+
+		dev_dbg(rtd->dev, "pfn i %i idx %d pfn %x\n", i, idx, pfn);
+
+		pg_table = (u32*)(pdata->pcm_pg[pcm][stream] + idx);
+
+		if (i & 1)
+			*pg_table |= (pfn << 4);
+		else
+			*pg_table |= pfn;
+	}
+
+	return 0;
+}
+
+/* this may get called several times by oss emulation */
+static int hsw_pcm_hw_params(struct snd_pcm_substream *substream,
+			      struct snd_pcm_hw_params *params)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct hsw_priv_data *pdata =
+		snd_soc_platform_get_drvdata(rtd->platform);
+	struct hsw_pcm_data *pcm_data = snd_soc_pcm_get_drvdata(rtd);
+	struct sst_hsw *hsw = pdata->hsw;
+	struct sst_module *module_data;
+	struct sst_dsp *dsp;
+	enum sst_hsw_stream_type stream_type;
+	enum sst_hsw_stream_path_id path_id;
+	u32 rate, bits, map, pages, module_id;
+	u8 channels;
+	int ret;
+
+	/* stream direction */
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+		path_id = SST_HSW_STREAM_PATH_SSP0_OUT;
+	else
+		path_id = SST_HSW_STREAM_PATH_SSP0_IN;
+
+	/* DSP stream type depends on DAI ID */
+	switch (rtd->cpu_dai->id) {
+	case 0:
+		stream_type = SST_HSW_STREAM_TYPE_SYSTEM;
+		module_id = SST_HSW_MODULE_PCM_SYSTEM;
+		break;
+	case 1:
+	case 2:
+		stream_type = SST_HSW_STREAM_TYPE_RENDER;
+		module_id = SST_HSW_MODULE_PCM;
+		break;
+	case 3:
+		/* path ID needs to be OUT for loopback */
+		stream_type = SST_HSW_STREAM_TYPE_LOOPBACK;
+		path_id = SST_HSW_STREAM_PATH_SSP0_OUT;
+		module_id = SST_HSW_MODULE_PCM_REFERENCE;
+		break;
+	case 4:
+		stream_type = SST_HSW_STREAM_TYPE_CAPTURE;
+		module_id = SST_HSW_MODULE_PCM_CAPTURE;
+		break;
+	default:
+		dev_err(rtd->dev, "error: invalid DAI ID %d\n",
+			rtd->cpu_dai->id);
+		return -EINVAL;
+	};
+
+	ret = sst_hsw_stream_format(hsw, pcm_data->stream,
+		path_id, stream_type, SST_HSW_STREAM_FORMAT_PCM_FORMAT);
+	if (ret < 0) {
+		dev_err(rtd->dev, "error: failed to set format %d\n", ret);
+		return ret;
+	}
+
+	rate = params_rate(params);
+	ret = sst_hsw_stream_set_rate(hsw, pcm_data->stream, rate);
+	if (ret < 0) {
+		dev_err(rtd->dev, "error: could not set rate %d\n", rate);
+		return ret;
+	}
+
+	switch (params_format(params)) {
+	case SNDRV_PCM_FORMAT_S16_LE:
+		bits = SST_HSW_DEPTH_16BIT;
+		sst_hsw_stream_set_valid(hsw, pcm_data->stream, 16);
+		break;
+	case SNDRV_PCM_FORMAT_S24_LE:
+		bits = SST_HSW_DEPTH_24BIT;
+		sst_hsw_stream_set_valid(hsw, pcm_data->stream, 32);
+		break;
+	default:
+		dev_err(rtd->dev, "error: invalid format %d\n",
+			params_format(params));
+		return -EINVAL;
+	}
+
+	ret = sst_hsw_stream_set_bits(hsw, pcm_data->stream, bits);
+	if (ret < 0) {
+		dev_err(rtd->dev, "error: could not set bits %d\n", bits);
+		return ret;
+	}
+
+	/* we only support stereo atm */
+	channels = params_channels(params);
+	if (channels != 2) {
+		dev_err(rtd->dev, "error: invalid channels %d\n", channels);
+		return -EINVAL;
+	}
+
+	map = create_channel_map(SST_HSW_CHANNEL_CONFIG_STEREO);
+	sst_hsw_stream_set_map_config(hsw, pcm_data->stream,
+			map, SST_HSW_CHANNEL_CONFIG_STEREO);
+
+	ret = sst_hsw_stream_set_channels(hsw, pcm_data->stream, channels);
+	if (ret < 0) {
+		dev_err(rtd->dev, "error: could not set channels %d\n",
+			channels);
+		return ret;
+	}
+
+	ret = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(params));
+	if (ret < 0) {
+		dev_err(rtd->dev, "error: could not allocate %d bytes for PCM %d\n",
+			params_buffer_bytes(params), ret);
+		return ret;
+	}
+
+	ret = create_adsp_page_table(pdata, rtd, runtime->dma_area,
+		runtime->dma_bytes, rtd->cpu_dai->id, substream->stream);
+	if (ret < 0)
+		return ret;
+
+	sst_hsw_stream_set_style(hsw, pcm_data->stream,
+		SST_HSW_INTERLEAVING_PER_CHANNEL);
+
+	if (runtime->dma_bytes % PAGE_SIZE)
+		pages = (runtime->dma_bytes / PAGE_SIZE) + 1;
+	else
+		pages = runtime->dma_bytes / PAGE_SIZE;
+
+	ret = sst_hsw_stream_buffer(hsw, pcm_data->stream,
+		virt_to_phys(pdata->pcm_pg[rtd->cpu_dai->id][substream->stream]),
+		pages, runtime->dma_bytes, 0,
+		(u32)(virt_to_phys(runtime->dma_area) >> PAGE_SHIFT));
+	if (ret < 0) {
+		dev_err(rtd->dev, "error: failed to set DMA buffer %d\n", ret);
+		return ret;
+	}
+
+	dsp = sst_hsw_get_dsp(hsw);
+
+	module_data = sst_module_get_from_id(dsp, module_id);
+	if (module_data == NULL) {
+		dev_err(rtd->dev, "error: failed to get module config\n");
+		return -EINVAL;
+	}
+
+	/* we use hardcoded memory offsets atm, will be updated for new FW */
+	if (stream_type == SST_HSW_STREAM_TYPE_CAPTURE) {
+		sst_hsw_stream_set_module_info(hsw, pcm_data->stream,
+			SST_HSW_MODULE_PCM_CAPTURE, module_data->entry);
+		sst_hsw_stream_set_pmemory_info(hsw, pcm_data->stream,
+			0x449400, 0x4000);
+		sst_hsw_stream_set_smemory_info(hsw, pcm_data->stream,
+			0x400000, 0);
+	} else { /* stream_type == SST_HSW_STREAM_TYPE_SYSTEM */
+		sst_hsw_stream_set_module_info(hsw, pcm_data->stream,
+			SST_HSW_MODULE_PCM_SYSTEM, module_data->entry);
+
+		sst_hsw_stream_set_pmemory_info(hsw, pcm_data->stream,
+			module_data->offset, module_data->size);
+		sst_hsw_stream_set_pmemory_info(hsw, pcm_data->stream,
+			0x44d400, 0x3800);
+
+		sst_hsw_stream_set_smemory_info(hsw, pcm_data->stream,
+			module_data->offset, module_data->size);
+		sst_hsw_stream_set_smemory_info(hsw, pcm_data->stream,
+			0x400000, 0);
+	}
+
+	ret = sst_hsw_stream_commit(hsw, pcm_data->stream);
+	if (ret < 0) {
+		dev_err(rtd->dev, "error: failed to commit stream %d\n", ret);
+		return ret;
+	}
+
+	ret = sst_hsw_stream_pause(hsw, pcm_data->stream, 1);
+	if (ret < 0)
+		dev_err(rtd->dev, "error: failed to pause %d\n", ret);
+
+	return 0;
+}
+
+static int hsw_pcm_hw_free(struct snd_pcm_substream *substream)
+{
+	snd_pcm_lib_free_pages(substream);
+	return 0;
+}
+
+static int hsw_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct hsw_priv_data *pdata =
+		snd_soc_platform_get_drvdata(rtd->platform);
+	struct hsw_pcm_data *pcm_data = snd_soc_pcm_get_drvdata(rtd);
+	struct sst_hsw *hsw = pdata->hsw;
+
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+	case SNDRV_PCM_TRIGGER_RESUME:
+	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+		sst_hsw_stream_resume(hsw, pcm_data->stream, 0);
+		break;
+	case SNDRV_PCM_TRIGGER_STOP:
+	case SNDRV_PCM_TRIGGER_SUSPEND:
+	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+		sst_hsw_stream_pause(hsw, pcm_data->stream, 0);
+		break;
+	default:
+		break;
+	}
+
+	return 0;
+}
+
+static u32 hsw_notify_pointer(struct sst_hsw_stream *stream, void *data)
+{
+	struct hsw_pcm_data *pcm_data = data;
+	struct snd_pcm_substream *substream = pcm_data->substream;
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	u32 pos;
+
+	pos = frames_to_bytes(runtime,
+		(runtime->control->appl_ptr % runtime->buffer_size));
+
+	dev_dbg(rtd->dev, "PCM: App pointer %d bytes\n", pos);
+
+	/* let alsa know we have play a period */
+	snd_pcm_period_elapsed(substream);
+	return pos;
+}
+
+static snd_pcm_uframes_t hsw_pcm_pointer(struct snd_pcm_substream *substream)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct hsw_priv_data *pdata =
+		snd_soc_platform_get_drvdata(rtd->platform);
+	struct hsw_pcm_data *pcm_data = snd_soc_pcm_get_drvdata(rtd);
+	struct sst_hsw *hsw = pdata->hsw;
+	snd_pcm_uframes_t offset;
+
+	offset = bytes_to_frames(runtime,
+		sst_hsw_get_dsp_position(hsw, pcm_data->stream));
+
+	dev_dbg(rtd->dev, "PCM: DMA pointer %zu bytes\n",
+		frames_to_bytes(runtime, (u32)offset));
+	return offset;
+}
+
+static int hsw_pcm_open(struct snd_pcm_substream *substream)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct hsw_priv_data *pdata =
+		snd_soc_platform_get_drvdata(rtd->platform);
+	struct hsw_pcm_data *pcm_data;
+	struct sst_hsw *hsw = pdata->hsw;
+
+	pcm_data = &pdata->pcm[rtd->cpu_dai->id];
+
+	mutex_lock(&pcm_data->mutex);
+
+	snd_soc_pcm_set_drvdata(rtd, pcm_data);
+	pcm_data->substream = substream;
+
+	snd_soc_set_runtime_hwparams(substream, &hsw_pcm_hardware);
+
+	pcm_data->stream = sst_hsw_stream_new(hsw, rtd->cpu_dai->id,
+		hsw_notify_pointer, pcm_data);
+	if (pcm_data->stream == NULL) {
+		dev_err(rtd->dev, "error: failed to create stream\n");
+		mutex_unlock(&pcm_data->mutex);
+		return -EINVAL;
+	}
+
+	/* Set previous saved volume */
+	sst_hsw_stream_set_volume(hsw, pcm_data->stream, 0,
+			0, pcm_data->volume[0]);
+	sst_hsw_stream_set_volume(hsw, pcm_data->stream, 0,
+			1, pcm_data->volume[1]);
+
+	mutex_unlock(&pcm_data->mutex);
+	return 0;
+}
+
+static int hsw_pcm_close(struct snd_pcm_substream *substream)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct hsw_priv_data *pdata =
+		snd_soc_platform_get_drvdata(rtd->platform);
+	struct hsw_pcm_data *pcm_data = snd_soc_pcm_get_drvdata(rtd);
+	struct sst_hsw *hsw = pdata->hsw;
+	int ret;
+
+	mutex_lock(&pcm_data->mutex);
+	ret = sst_hsw_stream_reset(hsw, pcm_data->stream);
+	if (ret < 0) {
+		dev_dbg(rtd->dev, "error: reset stream failed %d\n", ret);
+		goto out;
+	}
+
+	ret = sst_hsw_stream_free(hsw, pcm_data->stream);
+	if (ret < 0) {
+		dev_dbg(rtd->dev, "error: free stream failed %d\n", ret);
+		goto out;
+	}
+	pcm_data->stream = NULL;
+
+out:
+	mutex_unlock(&pcm_data->mutex);
+	return ret;
+}
+
+static struct snd_pcm_ops hsw_pcm_ops = {
+	.open		= hsw_pcm_open,
+	.close		= hsw_pcm_close,
+	.ioctl		= snd_pcm_lib_ioctl,
+	.hw_params	= hsw_pcm_hw_params,
+	.hw_free	= hsw_pcm_hw_free,
+	.trigger	= hsw_pcm_trigger,
+	.pointer	= hsw_pcm_pointer,
+	.mmap		= snd_pcm_lib_default_mmap,
+};
+
+static void hsw_pcm_free(struct snd_pcm *pcm)
+{
+	snd_pcm_lib_preallocate_free_for_all(pcm);
+}
+
+static int hsw_pcm_new(struct snd_soc_pcm_runtime *rtd)
+{
+	struct snd_pcm *pcm = rtd->pcm;
+	int ret = 0;
+
+	ret = dma_coerce_mask_and_coherent(rtd->card->dev, DMA_BIT_MASK(32));
+	if (ret)
+		return ret;
+
+	if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream ||
+			pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream) {
+		ret = snd_pcm_lib_preallocate_pages_for_all(pcm,
+			SNDRV_DMA_TYPE_DEV,
+			rtd->card->dev,
+			hsw_pcm_hardware.buffer_bytes_max,
+			hsw_pcm_hardware.buffer_bytes_max);
+		if (ret) {
+			dev_err(rtd->dev, "dma buffer allocation failed %d\n",
+				ret);
+			return ret;
+		}
+	}
+
+	return ret;
+}
+
+#define HSW_FORMATS \
+	(SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S16_LE |\
+	 SNDRV_PCM_FMTBIT_S32_LE)
+
+static struct snd_soc_dai_driver hsw_dais[] = {
+	{
+		.name  = "System Pin",
+		.playback = {
+			.stream_name = "System Playback",
+			.channels_min = 2,
+			.channels_max = 2,
+			.rates = SNDRV_PCM_RATE_48000,
+			.formats = SNDRV_PCM_FMTBIT_S16_LE,
+		},
+	},
+	{
+		/* PCM */
+		.name  = "Offload0 Pin",
+		.playback = {
+			.stream_name = "Offload0 Playback",
+			.channels_min = 2,
+			.channels_max = 2,
+			.rates = SNDRV_PCM_RATE_8000_192000,
+			.formats = HSW_FORMATS,
+		},
+	},
+	{
+		/* PCM */
+		.name  = "Offload1 Pin",
+		.playback = {
+			.stream_name = "Offload1 Playback",
+			.channels_min = 2,
+			.channels_max = 2,
+			.rates = SNDRV_PCM_RATE_8000_192000,
+			.formats = HSW_FORMATS,
+		},
+	},
+	{
+		.name  = "Loopback Pin",
+		.capture = {
+			.stream_name = "Loopback Capture",
+			.channels_min = 2,
+			.channels_max = 2,
+			.rates = SNDRV_PCM_RATE_8000_192000,
+			.formats = HSW_FORMATS,
+		},
+	},
+	{
+		.name  = "Capture Pin",
+		.capture = {
+			.stream_name = "Analog Capture",
+			.channels_min = 2,
+			.channels_max = 2,
+			.rates = SNDRV_PCM_RATE_8000_192000,
+			.formats = HSW_FORMATS,
+		},
+	},
+};
+
+static const struct snd_soc_dapm_widget widgets[] = {
+
+	/* Backend DAIs  */
+	SND_SOC_DAPM_AIF_IN("SSP0 CODEC IN", NULL, 0, SND_SOC_NOPM, 0, 0),
+	SND_SOC_DAPM_AIF_OUT("SSP0 CODEC OUT", NULL, 0, SND_SOC_NOPM, 0, 0),
+	SND_SOC_DAPM_AIF_IN("SSP1 BT IN", NULL, 0, SND_SOC_NOPM, 0, 0),
+	SND_SOC_DAPM_AIF_OUT("SSP1 BT OUT", NULL, 0, SND_SOC_NOPM, 0, 0),
+
+	/* Global Playback Mixer */
+	SND_SOC_DAPM_MIXER("Playback VMixer", SND_SOC_NOPM, 0, 0, NULL, 0),
+};
+
+static const struct snd_soc_dapm_route graph[] = {
+
+	/* Playback Mixer */
+	{"Playback VMixer", NULL, "System Playback"},
+	{"Playback VMixer", NULL, "Offload0 Playback"},
+	{"Playback VMixer", NULL, "Offload1 Playback"},
+
+	{"SSP0 CODEC OUT", NULL, "Playback VMixer"},
+
+	{"Analog Capture", NULL, "SSP0 CODEC IN"},
+};
+
+static int hsw_pcm_probe(struct snd_soc_platform *platform)
+{
+	struct sst_pdata *pdata = dev_get_platdata(platform->dev);
+	struct hsw_priv_data *priv_data;
+	int i;
+
+	if (!pdata)
+		return -ENODEV;
+
+	priv_data = devm_kzalloc(platform->dev, sizeof(*priv_data), GFP_KERNEL);
+	priv_data->hsw = pdata->dsp;
+	snd_soc_platform_set_drvdata(platform, priv_data);
+
+	/* allocate DSP buffer page tables */
+	for (i = 0; i < ARRAY_SIZE(hsw_dais); i++) {
+
+		mutex_init(&priv_data->pcm[i].mutex);
+
+		/* playback */
+		if (hsw_dais[i].playback.channels_min) {
+			priv_data->pcm_pg[i][0] = kzalloc(PAGE_SIZE, GFP_DMA);
+			if (priv_data->pcm_pg[i][0] == NULL)
+				goto err;
+		}
+
+		/* capture */
+		if (hsw_dais[i].capture.channels_min) {
+			priv_data->pcm_pg[i][1] = kzalloc(PAGE_SIZE, GFP_DMA);
+			if (priv_data->pcm_pg[i][1] == NULL)
+				goto err;
+		}
+	}
+
+	return 0;
+
+err:
+	for (;i >= 0; i--) {
+		if (hsw_dais[i].playback.channels_min)
+			kfree(priv_data->pcm_pg[i][0]);
+		if (hsw_dais[i].capture.channels_min)
+			kfree(priv_data->pcm_pg[i][1]);
+	}
+	return -ENOMEM;
+}
+
+static int hsw_pcm_remove(struct snd_soc_platform *platform)
+{
+	struct hsw_priv_data *priv_data =
+		snd_soc_platform_get_drvdata(platform);
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(hsw_dais); i++) {
+		if (hsw_dais[i].playback.channels_min)
+			kfree(priv_data->pcm_pg[i][0]);
+		if (hsw_dais[i].capture.channels_min)
+			kfree(priv_data->pcm_pg[i][1]);
+	}
+
+	return 0;
+}
+
+static struct snd_soc_platform_driver hsw_soc_platform = {
+	.probe		= hsw_pcm_probe,
+	.remove		= hsw_pcm_remove,
+	.ops		= &hsw_pcm_ops,
+	.pcm_new	= hsw_pcm_new,
+	.pcm_free	= hsw_pcm_free,
+	.controls	= hsw_volume_controls,
+	.num_controls	= ARRAY_SIZE(hsw_volume_controls),
+	.dapm_widgets	= widgets,
+	.num_dapm_widgets	= ARRAY_SIZE(widgets),
+	.dapm_routes	= graph,
+	.num_dapm_routes	= ARRAY_SIZE(graph),
+};
+
+static const struct snd_soc_component_driver hsw_dai_component = {
+	.name		= "haswell-dai",
+};
+
+static int hsw_pcm_dev_probe(struct platform_device *pdev)
+{
+	struct sst_pdata *sst_pdata = dev_get_platdata(&pdev->dev);
+	int ret;
+
+	ret = sst_hsw_dsp_init(&pdev->dev, sst_pdata);
+	if (ret < 0)
+		return -ENODEV;
+
+	ret = snd_soc_register_platform(&pdev->dev, &hsw_soc_platform);
+	if (ret < 0)
+		goto err_plat;
+
+	ret = snd_soc_register_component(&pdev->dev, &hsw_dai_component,
+		hsw_dais, ARRAY_SIZE(hsw_dais));
+	if (ret < 0)
+		goto err_comp;
+
+	return 0;
+
+err_comp:
+	snd_soc_unregister_platform(&pdev->dev);
+err_plat:
+	sst_hsw_dsp_free(&pdev->dev, sst_pdata);
+	return 0;
+}
+
+static int hsw_pcm_dev_remove(struct platform_device *pdev)
+{
+	struct sst_pdata *sst_pdata = dev_get_platdata(&pdev->dev);
+
+	snd_soc_unregister_platform(&pdev->dev);
+	snd_soc_unregister_component(&pdev->dev);
+	sst_hsw_dsp_free(&pdev->dev, sst_pdata);
+
+	return 0;
+}
+
+static struct platform_driver hsw_pcm_driver = {
+	.driver = {
+		.name = "haswell-pcm-audio",
+		.owner = THIS_MODULE,
+	},
+
+	.probe = hsw_pcm_dev_probe,
+	.remove = hsw_pcm_dev_remove,
+};
+module_platform_driver(hsw_pcm_driver);
+
+MODULE_AUTHOR("Liam Girdwood, Xingchao Wang");
+MODULE_DESCRIPTION("Haswell/Lynxpoint + Broadwell/Wildcatpoint PCM");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:haswell-pcm-audio");
diff --git a/sound/soc/intel/sst_dsp.h b/sound/soc/intel/sst-mfld-dsp.h
index 0fce1de284ff..3b63edc04b7f 100644
--- a/sound/soc/intel/sst_dsp.h
+++ b/sound/soc/intel/sst-mfld-dsp.h
@@ -1,7 +1,7 @@
-#ifndef __SST_DSP_H__
-#define __SST_DSP_H__
+#ifndef __SST_MFLD_DSP_H__
+#define __SST_MFLD_DSP_H__
 /*
- *  sst_dsp.h - Intel SST Driver for audio engine
+ *  sst_mfld_dsp.h - Intel SST Driver for audio engine
  *
  *  Copyright (C) 2008-12 Intel Corporation
  *  Authors:	Vinod Koul <vinod.koul@linux.intel.com>
@@ -131,4 +131,4 @@ struct snd_sst_params {
 	struct snd_sst_alloc_params_ext aparams;
 };
 
-#endif /* __SST_DSP_H__ */
+#endif /* __SST_MFLD_DSP_H__ */
diff --git a/sound/soc/intel/sst_platform.c b/sound/soc/intel/sst-mfld-platform.c
index f465a8180863..840306c2ef14 100644
--- a/sound/soc/intel/sst_platform.c
+++ b/sound/soc/intel/sst-mfld-platform.c
@@ -1,5 +1,5 @@
 /*
- *  sst_platform.c - Intel MID Platform driver
+ *  sst_mfld_platform.c - Intel MID Platform driver
  *
  *  Copyright (C) 2010-2013 Intel Corp
  *  Author: Vinod Koul <vinod.koul@intel.com>
@@ -33,7 +33,7 @@
 #include <sound/pcm_params.h>
 #include <sound/soc.h>
 #include <sound/compress_driver.h>
-#include "sst_platform.h"
+#include "sst-mfld-platform.h"
 
 static struct sst_device *sst;
 static DEFINE_MUTEX(sst_lock);
@@ -709,7 +709,7 @@ static int sst_platform_remove(struct platform_device *pdev)
 
 static struct platform_driver sst_platform_driver = {
 	.driver		= {
-		.name		= "sst-platform",
+		.name		= "sst-mfld-platform",
 		.owner		= THIS_MODULE,
 	},
 	.probe		= sst_platform_probe,
@@ -722,4 +722,4 @@ MODULE_DESCRIPTION("ASoC Intel(R) MID Platform driver");
 MODULE_AUTHOR("Vinod Koul <vinod.koul@intel.com>");
 MODULE_AUTHOR("Harsha Priya <priya.harsha@intel.com>");
 MODULE_LICENSE("GPL v2");
-MODULE_ALIAS("platform:sst-platform");
+MODULE_ALIAS("platform:sst-mfld-platform");
diff --git a/sound/soc/intel/sst_platform.h b/sound/soc/intel/sst-mfld-platform.h
index bee64fb7d2ef..0c4e2ddcecb1 100644
--- a/sound/soc/intel/sst_platform.h
+++ b/sound/soc/intel/sst-mfld-platform.h
@@ -1,5 +1,5 @@
 /*
- *  sst_platform.h - Intel MID Platform driver header file
+ *  sst_mfld_platform.h - Intel MID Platform driver header file
  *
  *  Copyright (C) 2010 Intel Corp
  *  Author: Vinod Koul <vinod.koul@intel.com>
@@ -27,7 +27,7 @@
 #ifndef __SST_PLATFORMDRV_H__
 #define __SST_PLATFORMDRV_H__
 
-#include "sst_dsp.h"
+#include "sst-mfld-dsp.h"
 
 #define SST_MONO		1
 #define SST_STEREO		2
diff --git a/sound/soc/omap/Kconfig b/sound/soc/omap/Kconfig
index 22ad9c5654b5..e00659351a4e 100644
--- a/sound/soc/omap/Kconfig
+++ b/sound/soc/omap/Kconfig
@@ -58,7 +58,7 @@ config SND_OMAP_SOC_OSK5912
 	tristate "SoC Audio support for omap osk5912"
 	depends on SND_OMAP_SOC && MACH_OMAP_OSK && I2C
 	select SND_OMAP_SOC_MCBSP
-	select SND_SOC_TLV320AIC23
+	select SND_SOC_TLV320AIC23_I2C
 	help
 	  Say Y if you want to add support for SoC audio on osk5912.
 
@@ -66,7 +66,7 @@ config SND_OMAP_SOC_AM3517EVM
 	tristate "SoC Audio support for OMAP3517 / AM3517 EVM"
 	depends on SND_OMAP_SOC && MACH_OMAP3517EVM && I2C
 	select SND_OMAP_SOC_MCBSP
-	select SND_SOC_TLV320AIC23
+	select SND_SOC_TLV320AIC23_I2C
 	help
 	  Say Y if you want to add support for SoC audio on the OMAP3517 / AM3517
 	  EVM.
diff --git a/sound/soc/omap/ams-delta.c b/sound/soc/omap/ams-delta.c
index 629446482a91..f141435b0b4a 100644
--- a/sound/soc/omap/ams-delta.c
+++ b/sound/soc/omap/ams-delta.c
@@ -103,60 +103,62 @@ static int ams_delta_set_audio_mode(struct snd_kcontrol *kcontrol,
 	if (!codec->hw_write)
 		return -EUNATCH;
 
-	if (ucontrol->value.enumerated.item[0] >= control->max)
+	if (ucontrol->value.enumerated.item[0] >= control->items)
 		return -EINVAL;
 
-	mutex_lock(&codec->mutex);
+	snd_soc_dapm_mutex_lock(dapm);
 
 	/* Translate selection to bitmap */
 	pins = ams_delta_audio_mode_pins[ucontrol->value.enumerated.item[0]];
 
 	/* Setup pins after corresponding bits if changed */
 	pin = !!(pins & (1 << AMS_DELTA_MOUTHPIECE));
+
 	if (pin != snd_soc_dapm_get_pin_status(dapm, "Mouthpiece")) {
 		changed = 1;
 		if (pin)
-			snd_soc_dapm_enable_pin(dapm, "Mouthpiece");
+			snd_soc_dapm_enable_pin_unlocked(dapm, "Mouthpiece");
 		else
-			snd_soc_dapm_disable_pin(dapm, "Mouthpiece");
+			snd_soc_dapm_disable_pin_unlocked(dapm, "Mouthpiece");
 	}
 	pin = !!(pins & (1 << AMS_DELTA_EARPIECE));
 	if (pin != snd_soc_dapm_get_pin_status(dapm, "Earpiece")) {
 		changed = 1;
 		if (pin)
-			snd_soc_dapm_enable_pin(dapm, "Earpiece");
+			snd_soc_dapm_enable_pin_unlocked(dapm, "Earpiece");
 		else
-			snd_soc_dapm_disable_pin(dapm, "Earpiece");
+			snd_soc_dapm_disable_pin_unlocked(dapm, "Earpiece");
 	}
 	pin = !!(pins & (1 << AMS_DELTA_MICROPHONE));
 	if (pin != snd_soc_dapm_get_pin_status(dapm, "Microphone")) {
 		changed = 1;
 		if (pin)
-			snd_soc_dapm_enable_pin(dapm, "Microphone");
+			snd_soc_dapm_enable_pin_unlocked(dapm, "Microphone");
 		else
-			snd_soc_dapm_disable_pin(dapm, "Microphone");
+			snd_soc_dapm_disable_pin_unlocked(dapm, "Microphone");
 	}
 	pin = !!(pins & (1 << AMS_DELTA_SPEAKER));
 	if (pin != snd_soc_dapm_get_pin_status(dapm, "Speaker")) {
 		changed = 1;
 		if (pin)
-			snd_soc_dapm_enable_pin(dapm, "Speaker");
+			snd_soc_dapm_enable_pin_unlocked(dapm, "Speaker");
 		else
-			snd_soc_dapm_disable_pin(dapm, "Speaker");
+			snd_soc_dapm_disable_pin_unlocked(dapm, "Speaker");
 	}
 	pin = !!(pins & (1 << AMS_DELTA_AGC));
 	if (pin != ams_delta_audio_agc) {
 		ams_delta_audio_agc = pin;
 		changed = 1;
 		if (pin)
-			snd_soc_dapm_enable_pin(dapm, "AGCIN");
+			snd_soc_dapm_enable_pin_unlocked(dapm, "AGCIN");
 		else
-			snd_soc_dapm_disable_pin(dapm, "AGCIN");
+			snd_soc_dapm_disable_pin_unlocked(dapm, "AGCIN");
 	}
+
 	if (changed)
-		snd_soc_dapm_sync(dapm);
+		snd_soc_dapm_sync_unlocked(dapm);
 
-	mutex_unlock(&codec->mutex);
+	snd_soc_dapm_mutex_unlock(dapm);
 
 	return changed;
 }
@@ -194,13 +196,11 @@ static int ams_delta_get_audio_mode(struct snd_kcontrol *kcontrol,
 	return 0;
 }
 
-static const struct soc_enum ams_delta_audio_enum[] = {
-	SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(ams_delta_audio_mode),
-						ams_delta_audio_mode),
-};
+static const SOC_ENUM_SINGLE_EXT_DECL(ams_delta_audio_enum,
+				      ams_delta_audio_mode);
 
 static const struct snd_kcontrol_new ams_delta_audio_controls[] = {
-	SOC_ENUM_EXT("Audio Mode", ams_delta_audio_enum[0],
+	SOC_ENUM_EXT("Audio Mode", ams_delta_audio_enum,
 			ams_delta_get_audio_mode, ams_delta_set_audio_mode),
 };
 
@@ -315,12 +315,17 @@ static void cx81801_close(struct tty_struct *tty)
 	v253_ops.close(tty);
 
 	/* Revert back to default audio input/output constellation */
-	snd_soc_dapm_disable_pin(dapm, "Mouthpiece");
-	snd_soc_dapm_enable_pin(dapm, "Earpiece");
-	snd_soc_dapm_enable_pin(dapm, "Microphone");
-	snd_soc_dapm_disable_pin(dapm, "Speaker");
-	snd_soc_dapm_disable_pin(dapm, "AGCIN");
-	snd_soc_dapm_sync(dapm);
+	snd_soc_dapm_mutex_lock(dapm);
+
+	snd_soc_dapm_disable_pin_unlocked(dapm, "Mouthpiece");
+	snd_soc_dapm_enable_pin_unlocked(dapm, "Earpiece");
+	snd_soc_dapm_enable_pin_unlocked(dapm, "Microphone");
+	snd_soc_dapm_disable_pin_unlocked(dapm, "Speaker");
+	snd_soc_dapm_disable_pin_unlocked(dapm, "AGCIN");
+
+	snd_soc_dapm_sync_unlocked(dapm);
+
+	snd_soc_dapm_mutex_unlock(codec);
 }
 
 /* Line discipline .hangup() */
diff --git a/sound/soc/omap/n810.c b/sound/soc/omap/n810.c
index d163e18d85d4..fd4d9c809e50 100644
--- a/sound/soc/omap/n810.c
+++ b/sound/soc/omap/n810.c
@@ -68,26 +68,30 @@ static void n810_ext_control(struct snd_soc_dapm_context *dapm)
 		break;
 	}
 
+	snd_soc_dapm_mutex_lock(dapm);
+
 	if (n810_spk_func)
-		snd_soc_dapm_enable_pin(dapm, "Ext Spk");
+		snd_soc_dapm_enable_pin_unlocked(dapm, "Ext Spk");
 	else
-		snd_soc_dapm_disable_pin(dapm, "Ext Spk");
+		snd_soc_dapm_disable_pin_unlocked(dapm, "Ext Spk");
 
 	if (hp)
-		snd_soc_dapm_enable_pin(dapm, "Headphone Jack");
+		snd_soc_dapm_enable_pin_unlocked(dapm, "Headphone Jack");
 	else
-		snd_soc_dapm_disable_pin(dapm, "Headphone Jack");
+		snd_soc_dapm_disable_pin_unlocked(dapm, "Headphone Jack");
 	if (line1l)
-		snd_soc_dapm_enable_pin(dapm, "LINE1L");
+		snd_soc_dapm_enable_pin_unlocked(dapm, "LINE1L");
 	else
-		snd_soc_dapm_disable_pin(dapm, "LINE1L");
+		snd_soc_dapm_disable_pin_unlocked(dapm, "LINE1L");
 
 	if (n810_dmic_func)
-		snd_soc_dapm_enable_pin(dapm, "DMic");
+		snd_soc_dapm_enable_pin_unlocked(dapm, "DMic");
 	else
-		snd_soc_dapm_disable_pin(dapm, "DMic");
+		snd_soc_dapm_disable_pin_unlocked(dapm, "DMic");
+
+	snd_soc_dapm_sync_unlocked(dapm);
 
-	snd_soc_dapm_sync(dapm);
+	snd_soc_dapm_mutex_unlock(dapm);
 }
 
 static int n810_startup(struct snd_pcm_substream *substream)
diff --git a/sound/soc/omap/rx51.c b/sound/soc/omap/rx51.c
index 611179c3bca4..7fb3d4b10370 100644
--- a/sound/soc/omap/rx51.c
+++ b/sound/soc/omap/rx51.c
@@ -74,26 +74,30 @@ static void rx51_ext_control(struct snd_soc_dapm_context *dapm)
 		break;
 	}
 
+	snd_soc_dapm_mutex_lock(dapm);
+
 	if (rx51_spk_func)
-		snd_soc_dapm_enable_pin(dapm, "Ext Spk");
+		snd_soc_dapm_enable_pin_unlocked(dapm, "Ext Spk");
 	else
-		snd_soc_dapm_disable_pin(dapm, "Ext Spk");
+		snd_soc_dapm_disable_pin_unlocked(dapm, "Ext Spk");
 	if (rx51_dmic_func)
-		snd_soc_dapm_enable_pin(dapm, "DMic");
+		snd_soc_dapm_enable_pin_unlocked(dapm, "DMic");
 	else
-		snd_soc_dapm_disable_pin(dapm, "DMic");
+		snd_soc_dapm_disable_pin_unlocked(dapm, "DMic");
 	if (hp)
-		snd_soc_dapm_enable_pin(dapm, "Headphone Jack");
+		snd_soc_dapm_enable_pin_unlocked(dapm, "Headphone Jack");
 	else
-		snd_soc_dapm_disable_pin(dapm, "Headphone Jack");
+		snd_soc_dapm_disable_pin_unlocked(dapm, "Headphone Jack");
 	if (hs)
-		snd_soc_dapm_enable_pin(dapm, "HS Mic");
+		snd_soc_dapm_enable_pin_unlocked(dapm, "HS Mic");
 	else
-		snd_soc_dapm_disable_pin(dapm, "HS Mic");
+		snd_soc_dapm_disable_pin_unlocked(dapm, "HS Mic");
 
 	gpio_set_value(RX51_TVOUT_SEL_GPIO, tvout);
 
-	snd_soc_dapm_sync(dapm);
+	snd_soc_dapm_sync_unlocked(dapm);
+
+	snd_soc_dapm_mutex_unlock(dapm);
 }
 
 static int rx51_startup(struct snd_pcm_substream *substream)
diff --git a/sound/soc/pxa/corgi.c b/sound/soc/pxa/corgi.c
index 1853d41034bf..5a88136aa800 100644
--- a/sound/soc/pxa/corgi.c
+++ b/sound/soc/pxa/corgi.c
@@ -47,64 +47,63 @@ static int corgi_spk_func;
 
 static void corgi_ext_control(struct snd_soc_dapm_context *dapm)
 {
+	snd_soc_dapm_mutex_lock(dapm);
+
 	/* set up jack connection */
 	switch (corgi_jack_func) {
 	case CORGI_HP:
 		/* set = unmute headphone */
 		gpio_set_value(CORGI_GPIO_MUTE_L, 1);
 		gpio_set_value(CORGI_GPIO_MUTE_R, 1);
-		snd_soc_dapm_disable_pin(dapm, "Mic Jack");
-		snd_soc_dapm_disable_pin(dapm, "Line Jack");
-		snd_soc_dapm_enable_pin(dapm, "Headphone Jack");
-		snd_soc_dapm_disable_pin(dapm, "Headset Jack");
+		snd_soc_dapm_disable_pin_unlocked(dapm, "Mic Jack");
+		snd_soc_dapm_disable_pin_unlocked(dapm, "Line Jack");
+		snd_soc_dapm_enable_pin_unlocked(dapm, "Headphone Jack");
+		snd_soc_dapm_disable_pin_unlocked(dapm, "Headset Jack");
 		break;
 	case CORGI_MIC:
 		/* reset = mute headphone */
 		gpio_set_value(CORGI_GPIO_MUTE_L, 0);
 		gpio_set_value(CORGI_GPIO_MUTE_R, 0);
-		snd_soc_dapm_enable_pin(dapm, "Mic Jack");
-		snd_soc_dapm_disable_pin(dapm, "Line Jack");
-		snd_soc_dapm_disable_pin(dapm, "Headphone Jack");
-		snd_soc_dapm_disable_pin(dapm, "Headset Jack");
+		snd_soc_dapm_enable_pin_unlocked(dapm, "Mic Jack");
+		snd_soc_dapm_disable_pin_unlocked(dapm, "Line Jack");
+		snd_soc_dapm_disable_pin_unlocked(dapm, "Headphone Jack");
+		snd_soc_dapm_disable_pin_unlocked(dapm, "Headset Jack");
 		break;
 	case CORGI_LINE:
 		gpio_set_value(CORGI_GPIO_MUTE_L, 0);
 		gpio_set_value(CORGI_GPIO_MUTE_R, 0);
-		snd_soc_dapm_disable_pin(dapm, "Mic Jack");
-		snd_soc_dapm_enable_pin(dapm, "Line Jack");
-		snd_soc_dapm_disable_pin(dapm, "Headphone Jack");
-		snd_soc_dapm_disable_pin(dapm, "Headset Jack");
+		snd_soc_dapm_disable_pin_unlocked(dapm, "Mic Jack");
+		snd_soc_dapm_enable_pin_unlocked(dapm, "Line Jack");
+		snd_soc_dapm_disable_pin_unlocked(dapm, "Headphone Jack");
+		snd_soc_dapm_disable_pin_unlocked(dapm, "Headset Jack");
 		break;
 	case CORGI_HEADSET:
 		gpio_set_value(CORGI_GPIO_MUTE_L, 0);
 		gpio_set_value(CORGI_GPIO_MUTE_R, 1);
-		snd_soc_dapm_enable_pin(dapm, "Mic Jack");
-		snd_soc_dapm_disable_pin(dapm, "Line Jack");
-		snd_soc_dapm_disable_pin(dapm, "Headphone Jack");
-		snd_soc_dapm_enable_pin(dapm, "Headset Jack");
+		snd_soc_dapm_enable_pin_unlocked(dapm, "Mic Jack");
+		snd_soc_dapm_disable_pin_unlocked(dapm, "Line Jack");
+		snd_soc_dapm_disable_pin_unlocked(dapm, "Headphone Jack");
+		snd_soc_dapm_enable_pin_unlocked(dapm, "Headset Jack");
 		break;
 	}
 
 	if (corgi_spk_func == CORGI_SPK_ON)
-		snd_soc_dapm_enable_pin(dapm, "Ext Spk");
+		snd_soc_dapm_enable_pin_unlocked(dapm, "Ext Spk");
 	else
-		snd_soc_dapm_disable_pin(dapm, "Ext Spk");
+		snd_soc_dapm_disable_pin_unlocked(dapm, "Ext Spk");
 
 	/* signal a DAPM event */
-	snd_soc_dapm_sync(dapm);
+	snd_soc_dapm_sync_unlocked(dapm);
+
+	snd_soc_dapm_mutex_unlock(dapm);
 }
 
 static int corgi_startup(struct snd_pcm_substream *substream)
 {
 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
-	struct snd_soc_codec *codec = rtd->codec;
-
-	mutex_lock(&codec->mutex);
 
 	/* check the jack status at stream startup */
-	corgi_ext_control(&codec->dapm);
-
-	mutex_unlock(&codec->mutex);
+	corgi_ext_control(&rtd->card->dapm);
 
 	return 0;
 }
diff --git a/sound/soc/pxa/e740_wm9705.c b/sound/soc/pxa/e740_wm9705.c
index 44b5c09d296b..c29fedab2f49 100644
--- a/sound/soc/pxa/e740_wm9705.c
+++ b/sound/soc/pxa/e740_wm9705.c
@@ -103,11 +103,6 @@ static int e740_ac97_init(struct snd_soc_pcm_runtime *rtd)
 	snd_soc_dapm_nc_pin(dapm, "PCBEEP");
 	snd_soc_dapm_nc_pin(dapm, "MIC2");
 
-	snd_soc_dapm_new_controls(dapm, e740_dapm_widgets,
-					ARRAY_SIZE(e740_dapm_widgets));
-
-	snd_soc_dapm_add_routes(dapm, audio_map, ARRAY_SIZE(audio_map));
-
 	return 0;
 }
 
@@ -136,6 +131,11 @@ static struct snd_soc_card e740 = {
 	.owner = THIS_MODULE,
 	.dai_link = e740_dai,
 	.num_links = ARRAY_SIZE(e740_dai),
+
+	.dapm_widgets = e740_dapm_widgets,
+	.num_dapm_widgets = ARRAY_SIZE(e740_dapm_widgets),
+	.dapm_routes = audio_map,
+	.num_dapm_routes = ARRAY_SIZE(audio_map),
 };
 
 static struct gpio e740_audio_gpios[] = {
diff --git a/sound/soc/pxa/e750_wm9705.c b/sound/soc/pxa/e750_wm9705.c
index c34e447eb991..ee36aba88063 100644
--- a/sound/soc/pxa/e750_wm9705.c
+++ b/sound/soc/pxa/e750_wm9705.c
@@ -85,11 +85,6 @@ static int e750_ac97_init(struct snd_soc_pcm_runtime *rtd)
 	snd_soc_dapm_nc_pin(dapm, "PCBEEP");
 	snd_soc_dapm_nc_pin(dapm, "MIC2");
 
-	snd_soc_dapm_new_controls(dapm, e750_dapm_widgets,
-					ARRAY_SIZE(e750_dapm_widgets));
-
-	snd_soc_dapm_add_routes(dapm, audio_map, ARRAY_SIZE(audio_map));
-
 	return 0;
 }
 
@@ -119,6 +114,11 @@ static struct snd_soc_card e750 = {
 	.owner = THIS_MODULE,
 	.dai_link = e750_dai,
 	.num_links = ARRAY_SIZE(e750_dai),
+
+	.dapm_widgets = e750_dapm_widgets,
+	.num_dapm_widgets = ARRAY_SIZE(e750_dapm_widgets),
+	.dapm_routes = audio_map,
+	.num_dapm_routes = ARRAY_SIZE(audio_map),
 };
 
 static struct gpio e750_audio_gpios[] = {
diff --git a/sound/soc/pxa/e800_wm9712.c b/sound/soc/pxa/e800_wm9712.c
index 3137f800b43f..24c2078ce70b 100644
--- a/sound/soc/pxa/e800_wm9712.c
+++ b/sound/soc/pxa/e800_wm9712.c
@@ -71,19 +71,6 @@ static const struct snd_soc_dapm_route audio_map[] = {
 	{"MIC2", NULL, "Mic (Internal2)"},
 };
 
-static int e800_ac97_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_new_controls(dapm, e800_dapm_widgets,
-					ARRAY_SIZE(e800_dapm_widgets));
-
-	snd_soc_dapm_add_routes(dapm, audio_map, ARRAY_SIZE(audio_map));
-
-	return 0;
-}
-
 static struct snd_soc_dai_link e800_dai[] = {
 	{
 		.name = "AC97",
@@ -92,7 +79,6 @@ static struct snd_soc_dai_link e800_dai[] = {
 		.codec_dai_name = "wm9712-hifi",
 		.platform_name = "pxa-pcm-audio",
 		.codec_name = "wm9712-codec",
-		.init = e800_ac97_init,
 	},
 	{
 		.name = "AC97 Aux",
@@ -109,6 +95,11 @@ static struct snd_soc_card e800 = {
 	.owner = THIS_MODULE,
 	.dai_link = e800_dai,
 	.num_links = ARRAY_SIZE(e800_dai),
+
+	.dapm_widgets = e800_dapm_widgets,
+	.num_dapm_widgets = ARRAY_SIZE(e800_dapm_widgets),
+	.dapm_routes = audio_map,
+	.num_dapm_routes = ARRAY_SIZE(audio_map),
 };
 
 static struct gpio e800_audio_gpios[] = {
diff --git a/sound/soc/pxa/magician.c b/sound/soc/pxa/magician.c
index aace19e0fe2c..41ab6678b65d 100644
--- a/sound/soc/pxa/magician.c
+++ b/sound/soc/pxa/magician.c
@@ -45,27 +45,31 @@ static void magician_ext_control(struct snd_soc_codec *codec)
 {
 	struct snd_soc_dapm_context *dapm = &codec->dapm;
 
+	snd_soc_dapm_mutex_lock(dapm);
+
 	if (magician_spk_switch)
-		snd_soc_dapm_enable_pin(dapm, "Speaker");
+		snd_soc_dapm_enable_pin_unlocked(dapm, "Speaker");
 	else
-		snd_soc_dapm_disable_pin(dapm, "Speaker");
+		snd_soc_dapm_disable_pin_unlocked(dapm, "Speaker");
 	if (magician_hp_switch)
-		snd_soc_dapm_enable_pin(dapm, "Headphone Jack");
+		snd_soc_dapm_enable_pin_unlocked(dapm, "Headphone Jack");
 	else
-		snd_soc_dapm_disable_pin(dapm, "Headphone Jack");
+		snd_soc_dapm_disable_pin_unlocked(dapm, "Headphone Jack");
 
 	switch (magician_in_sel) {
 	case MAGICIAN_MIC:
-		snd_soc_dapm_disable_pin(dapm, "Headset Mic");
-		snd_soc_dapm_enable_pin(dapm, "Call Mic");
+		snd_soc_dapm_disable_pin_unlocked(dapm, "Headset Mic");
+		snd_soc_dapm_enable_pin_unlocked(dapm, "Call Mic");
 		break;
 	case MAGICIAN_MIC_EXT:
-		snd_soc_dapm_disable_pin(dapm, "Call Mic");
-		snd_soc_dapm_enable_pin(dapm, "Headset Mic");
+		snd_soc_dapm_disable_pin_unlocked(dapm, "Call Mic");
+		snd_soc_dapm_enable_pin_unlocked(dapm, "Headset Mic");
 		break;
 	}
 
-	snd_soc_dapm_sync(dapm);
+	snd_soc_dapm_sync_unlocked(dapm);
+
+	snd_soc_dapm_mutex_unlock(dapm);
 }
 
 static int magician_startup(struct snd_pcm_substream *substream)
@@ -73,13 +77,9 @@ static int magician_startup(struct snd_pcm_substream *substream)
 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
 	struct snd_soc_codec *codec = rtd->codec;
 
-	mutex_lock(&codec->mutex);
-
 	/* check the jack status at stream startup */
 	magician_ext_control(codec);
 
-	mutex_unlock(&codec->mutex);
-
 	return 0;
 }
 
diff --git a/sound/soc/pxa/mioa701_wm9713.c b/sound/soc/pxa/mioa701_wm9713.c
index 160c5245448f..595eee341e90 100644
--- a/sound/soc/pxa/mioa701_wm9713.c
+++ b/sound/soc/pxa/mioa701_wm9713.c
@@ -127,16 +127,8 @@ static const struct snd_soc_dapm_route audio_map[] = {
 static int mioa701_wm9713_init(struct snd_soc_pcm_runtime *rtd)
 {
 	struct snd_soc_codec *codec = rtd->codec;
-	struct snd_soc_dapm_context *dapm = &codec->dapm;
 	unsigned short reg;
 
-	/* Add mioa701 specific widgets */
-	snd_soc_dapm_new_controls(dapm, mioa701_dapm_widgets,
-				  ARRAY_SIZE(mioa701_dapm_widgets));
-
-	/* Set up mioa701 specific audio path audio_mapnects */
-	snd_soc_dapm_add_routes(dapm, audio_map, ARRAY_SIZE(audio_map));
-
 	/* Prepare GPIO8 for rear speaker amplifier */
 	reg = codec->driver->read(codec, AC97_GPIO_CFG);
 	codec->driver->write(codec, AC97_GPIO_CFG, reg | 0x0100);
@@ -145,12 +137,6 @@ static int mioa701_wm9713_init(struct snd_soc_pcm_runtime *rtd)
 	reg = codec->driver->read(codec, AC97_3D_CONTROL);
 	codec->driver->write(codec, AC97_3D_CONTROL, reg | 0xc000);
 
-	snd_soc_dapm_enable_pin(dapm, "Front Speaker");
-	snd_soc_dapm_enable_pin(dapm, "Rear Speaker");
-	snd_soc_dapm_enable_pin(dapm, "Front Mic");
-	snd_soc_dapm_enable_pin(dapm, "GSM Line In");
-	snd_soc_dapm_enable_pin(dapm, "GSM Line Out");
-
 	return 0;
 }
 
@@ -183,6 +169,11 @@ static struct snd_soc_card mioa701 = {
 	.owner = THIS_MODULE,
 	.dai_link = mioa701_dai,
 	.num_links = ARRAY_SIZE(mioa701_dai),
+
+	.dapm_widgets = mioa701_dapm_widgets,
+	.num_dapm_widgets = ARRAY_SIZE(mioa701_dapm_widgets),
+	.dapm_routes = audio_map,
+	.num_dapm_routes = ARRAY_SIZE(audio_map),
 };
 
 static int mioa701_wm9713_probe(struct platform_device *pdev)
diff --git a/sound/soc/pxa/poodle.c b/sound/soc/pxa/poodle.c
index c93e138d8dc3..c6bdc6c0eff6 100644
--- a/sound/soc/pxa/poodle.c
+++ b/sound/soc/pxa/poodle.c
@@ -74,14 +74,9 @@ static void poodle_ext_control(struct snd_soc_dapm_context *dapm)
 static int poodle_startup(struct snd_pcm_substream *substream)
 {
 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
-	struct snd_soc_codec *codec = rtd->codec;
-
-	mutex_lock(&codec->mutex);
 
 	/* check the jack status at stream startup */
-	poodle_ext_control(&codec->dapm);
-
-	mutex_unlock(&codec->mutex);
+	poodle_ext_control(&rtd->card->dapm);
 
 	return 0;
 }
diff --git a/sound/soc/pxa/spitz.c b/sound/soc/pxa/spitz.c
index fc052d8247ff..1373b017a951 100644
--- a/sound/soc/pxa/spitz.c
+++ b/sound/soc/pxa/spitz.c
@@ -46,74 +46,74 @@ static int spitz_mic_gpio;
 
 static void spitz_ext_control(struct snd_soc_dapm_context *dapm)
 {
+	snd_soc_dapm_mutex_lock(dapm);
+
 	if (spitz_spk_func == SPITZ_SPK_ON)
-		snd_soc_dapm_enable_pin(dapm, "Ext Spk");
+		snd_soc_dapm_enable_pin_unlocked(dapm, "Ext Spk");
 	else
-		snd_soc_dapm_disable_pin(dapm, "Ext Spk");
+		snd_soc_dapm_disable_pin_unlocked(dapm, "Ext Spk");
 
 	/* set up jack connection */
 	switch (spitz_jack_func) {
 	case SPITZ_HP:
 		/* enable and unmute hp jack, disable mic bias */
-		snd_soc_dapm_disable_pin(dapm, "Headset Jack");
-		snd_soc_dapm_disable_pin(dapm, "Mic Jack");
-		snd_soc_dapm_disable_pin(dapm, "Line Jack");
-		snd_soc_dapm_enable_pin(dapm, "Headphone Jack");
+		snd_soc_dapm_disable_pin_unlocked(dapm, "Headset Jack");
+		snd_soc_dapm_disable_pin_unlocked(dapm, "Mic Jack");
+		snd_soc_dapm_disable_pin_unlocked(dapm, "Line Jack");
+		snd_soc_dapm_enable_pin_unlocked(dapm, "Headphone Jack");
 		gpio_set_value(SPITZ_GPIO_MUTE_L, 1);
 		gpio_set_value(SPITZ_GPIO_MUTE_R, 1);
 		break;
 	case SPITZ_MIC:
 		/* enable mic jack and bias, mute hp */
-		snd_soc_dapm_disable_pin(dapm, "Headphone Jack");
-		snd_soc_dapm_disable_pin(dapm, "Headset Jack");
-		snd_soc_dapm_disable_pin(dapm, "Line Jack");
-		snd_soc_dapm_enable_pin(dapm, "Mic Jack");
+		snd_soc_dapm_disable_pin_unlocked(dapm, "Headphone Jack");
+		snd_soc_dapm_disable_pin_unlocked(dapm, "Headset Jack");
+		snd_soc_dapm_disable_pin_unlocked(dapm, "Line Jack");
+		snd_soc_dapm_enable_pin_unlocked(dapm, "Mic Jack");
 		gpio_set_value(SPITZ_GPIO_MUTE_L, 0);
 		gpio_set_value(SPITZ_GPIO_MUTE_R, 0);
 		break;
 	case SPITZ_LINE:
 		/* enable line jack, disable mic bias and mute hp */
-		snd_soc_dapm_disable_pin(dapm, "Headphone Jack");
-		snd_soc_dapm_disable_pin(dapm, "Headset Jack");
-		snd_soc_dapm_disable_pin(dapm, "Mic Jack");
-		snd_soc_dapm_enable_pin(dapm, "Line Jack");
+		snd_soc_dapm_disable_pin_unlocked(dapm, "Headphone Jack");
+		snd_soc_dapm_disable_pin_unlocked(dapm, "Headset Jack");
+		snd_soc_dapm_disable_pin_unlocked(dapm, "Mic Jack");
+		snd_soc_dapm_enable_pin_unlocked(dapm, "Line Jack");
 		gpio_set_value(SPITZ_GPIO_MUTE_L, 0);
 		gpio_set_value(SPITZ_GPIO_MUTE_R, 0);
 		break;
 	case SPITZ_HEADSET:
 		/* enable and unmute headset jack enable mic bias, mute L hp */
-		snd_soc_dapm_disable_pin(dapm, "Headphone Jack");
-		snd_soc_dapm_enable_pin(dapm, "Mic Jack");
-		snd_soc_dapm_disable_pin(dapm, "Line Jack");
-		snd_soc_dapm_enable_pin(dapm, "Headset Jack");
+		snd_soc_dapm_disable_pin_unlocked(dapm, "Headphone Jack");
+		snd_soc_dapm_enable_pin_unlocked(dapm, "Mic Jack");
+		snd_soc_dapm_disable_pin_unlocked(dapm, "Line Jack");
+		snd_soc_dapm_enable_pin_unlocked(dapm, "Headset Jack");
 		gpio_set_value(SPITZ_GPIO_MUTE_L, 0);
 		gpio_set_value(SPITZ_GPIO_MUTE_R, 1);
 		break;
 	case SPITZ_HP_OFF:
 
 		/* jack removed, everything off */
-		snd_soc_dapm_disable_pin(dapm, "Headphone Jack");
-		snd_soc_dapm_disable_pin(dapm, "Headset Jack");
-		snd_soc_dapm_disable_pin(dapm, "Mic Jack");
-		snd_soc_dapm_disable_pin(dapm, "Line Jack");
+		snd_soc_dapm_disable_pin_unlocked(dapm, "Headphone Jack");
+		snd_soc_dapm_disable_pin_unlocked(dapm, "Headset Jack");
+		snd_soc_dapm_disable_pin_unlocked(dapm, "Mic Jack");
+		snd_soc_dapm_disable_pin_unlocked(dapm, "Line Jack");
 		gpio_set_value(SPITZ_GPIO_MUTE_L, 0);
 		gpio_set_value(SPITZ_GPIO_MUTE_R, 0);
 		break;
 	}
-	snd_soc_dapm_sync(dapm);
+
+	snd_soc_dapm_sync_unlocked(dapm);
+
+	snd_soc_dapm_mutex_unlock(dapm);
 }
 
 static int spitz_startup(struct snd_pcm_substream *substream)
 {
 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
-	struct snd_soc_codec *codec = rtd->codec;
-
-	mutex_lock(&codec->mutex);
 
 	/* check the jack status at stream startup */
-	spitz_ext_control(&codec->dapm);
-
-	mutex_unlock(&codec->mutex);
+	spitz_ext_control(&rtd->card->dapm);
 
 	return 0;
 }
diff --git a/sound/soc/pxa/tosa.c b/sound/soc/pxa/tosa.c
index 1d9c2ed223bc..cead1658d10a 100644
--- a/sound/soc/pxa/tosa.c
+++ b/sound/soc/pxa/tosa.c
@@ -48,31 +48,35 @@ static void tosa_ext_control(struct snd_soc_codec *codec)
 {
 	struct snd_soc_dapm_context *dapm = &codec->dapm;
 
+	snd_soc_dapm_mutex_lock(dapm);
+
 	/* set up jack connection */
 	switch (tosa_jack_func) {
 	case TOSA_HP:
-		snd_soc_dapm_disable_pin(dapm, "Mic (Internal)");
-		snd_soc_dapm_enable_pin(dapm, "Headphone Jack");
-		snd_soc_dapm_disable_pin(dapm, "Headset Jack");
+		snd_soc_dapm_disable_pin_unlocked(dapm, "Mic (Internal)");
+		snd_soc_dapm_enable_pin_unlocked(dapm, "Headphone Jack");
+		snd_soc_dapm_disable_pin_unlocked(dapm, "Headset Jack");
 		break;
 	case TOSA_MIC_INT:
-		snd_soc_dapm_enable_pin(dapm, "Mic (Internal)");
-		snd_soc_dapm_disable_pin(dapm, "Headphone Jack");
-		snd_soc_dapm_disable_pin(dapm, "Headset Jack");
+		snd_soc_dapm_enable_pin_unlocked(dapm, "Mic (Internal)");
+		snd_soc_dapm_disable_pin_unlocked(dapm, "Headphone Jack");
+		snd_soc_dapm_disable_pin_unlocked(dapm, "Headset Jack");
 		break;
 	case TOSA_HEADSET:
-		snd_soc_dapm_disable_pin(dapm, "Mic (Internal)");
-		snd_soc_dapm_disable_pin(dapm, "Headphone Jack");
-		snd_soc_dapm_enable_pin(dapm, "Headset Jack");
+		snd_soc_dapm_disable_pin_unlocked(dapm, "Mic (Internal)");
+		snd_soc_dapm_disable_pin_unlocked(dapm, "Headphone Jack");
+		snd_soc_dapm_enable_pin_unlocked(dapm, "Headset Jack");
 		break;
 	}
 
 	if (tosa_spk_func == TOSA_SPK_ON)
-		snd_soc_dapm_enable_pin(dapm, "Speaker");
+		snd_soc_dapm_enable_pin_unlocked(dapm, "Speaker");
 	else
-		snd_soc_dapm_disable_pin(dapm, "Speaker");
+		snd_soc_dapm_disable_pin_unlocked(dapm, "Speaker");
+
+	snd_soc_dapm_sync_unlocked(dapm);
 
-	snd_soc_dapm_sync(dapm);
+	snd_soc_dapm_mutex_unlock(dapm);
 }
 
 static int tosa_startup(struct snd_pcm_substream *substream)
@@ -80,13 +84,9 @@ static int tosa_startup(struct snd_pcm_substream *substream)
 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
 	struct snd_soc_codec *codec = rtd->codec;
 
-	mutex_lock(&codec->mutex);
-
 	/* check the jack status at stream startup */
 	tosa_ext_control(codec);
 
-	mutex_unlock(&codec->mutex);
-
 	return 0;
 }
 
diff --git a/sound/soc/pxa/zylonite.c b/sound/soc/pxa/zylonite.c
index db8aadf8932d..23bf991e95d5 100644
--- a/sound/soc/pxa/zylonite.c
+++ b/sound/soc/pxa/zylonite.c
@@ -71,22 +71,10 @@ static const struct snd_soc_dapm_route audio_map[] = {
 
 static int zylonite_wm9713_init(struct snd_soc_pcm_runtime *rtd)
 {
-	struct snd_soc_codec *codec = rtd->codec;
-	struct snd_soc_dapm_context *dapm = &codec->dapm;
-
 	if (clk_pout)
 		snd_soc_dai_set_pll(rtd->codec_dai, 0, 0,
 				    clk_get_rate(pout), 0);
 
-	snd_soc_dapm_new_controls(dapm, zylonite_dapm_widgets,
-				  ARRAY_SIZE(zylonite_dapm_widgets));
-
-	snd_soc_dapm_add_routes(dapm, audio_map, ARRAY_SIZE(audio_map));
-
-	/* Static setup for now */
-	snd_soc_dapm_enable_pin(dapm, "Headphone");
-	snd_soc_dapm_enable_pin(dapm, "Headset Earpiece");
-
 	return 0;
 }
 
@@ -256,6 +244,11 @@ static struct snd_soc_card zylonite = {
 	.resume_pre = &zylonite_resume_pre,
 	.dai_link = zylonite_dai,
 	.num_links = ARRAY_SIZE(zylonite_dai),
+
+	.dapm_widgets = zylonite_dapm_widgets,
+	.num_dapm_widgets = ARRAY_SIZE(zylonite_dapm_widgets),
+	.dapm_routes = audio_map,
+	.num_dapm_routes = ARRAY_SIZE(audio_map),
 };
 
 static struct platform_device *zylonite_snd_ac97_device;
diff --git a/sound/soc/samsung/Kconfig b/sound/soc/samsung/Kconfig
index 350757400391..f2e289180e46 100644
--- a/sound/soc/samsung/Kconfig
+++ b/sound/soc/samsung/Kconfig
@@ -117,7 +117,7 @@ config SND_SOC_SAMSUNG_SIMTEC_TLV320AIC23
 	tristate "SoC I2S Audio support for TLV320AIC23 on Simtec boards"
 	depends on SND_SOC_SAMSUNG && ARCH_S3C24XX
 	select SND_S3C24XX_I2S
-	select SND_SOC_TLV320AIC23
+	select SND_SOC_TLV320AIC23_I2C
 	select SND_SOC_SAMSUNG_SIMTEC
 
 config SND_SOC_SAMSUNG_SIMTEC_HERMES
diff --git a/sound/soc/samsung/h1940_uda1380.c b/sound/soc/samsung/h1940_uda1380.c
index fbced589d077..88b09e022503 100644
--- a/sound/soc/samsung/h1940_uda1380.c
+++ b/sound/soc/samsung/h1940_uda1380.c
@@ -66,10 +66,6 @@ static int h1940_startup(struct snd_pcm_substream *substream)
 {
 	struct snd_pcm_runtime *runtime = substream->runtime;
 
-	runtime->hw.rate_min = hw_rates.list[0];
-	runtime->hw.rate_max = hw_rates.list[hw_rates.count - 1];
-	runtime->hw.rates = SNDRV_PCM_RATE_KNOT;
-
 	return snd_pcm_hw_constraint_list(runtime, 0,
 					SNDRV_PCM_HW_PARAM_RATE,
 					&hw_rates);
@@ -94,7 +90,7 @@ static int h1940_hw_params(struct snd_pcm_substream *substream,
 			div++;
 		break;
 	default:
-		dev_err(&rtd->dev, "%s: rate %d is not supported\n",
+		dev_err(rtd->dev, "%s: rate %d is not supported\n",
 			__func__, rate);
 		return -EINVAL;
 	}
@@ -181,7 +177,6 @@ static int h1940_uda1380_init(struct snd_soc_pcm_runtime *rtd)
 {
 	struct snd_soc_codec *codec = rtd->codec;
 	struct snd_soc_dapm_context *dapm = &codec->dapm;
-	int err;
 
 	snd_soc_dapm_enable_pin(dapm, "Headphone Jack");
 	snd_soc_dapm_enable_pin(dapm, "Speaker");
diff --git a/sound/soc/samsung/neo1973_wm8753.c b/sound/soc/samsung/neo1973_wm8753.c
index 98a04c11202d..b0800337b79e 100644
--- a/sound/soc/samsung/neo1973_wm8753.c
+++ b/sound/soc/samsung/neo1973_wm8753.c
@@ -192,44 +192,6 @@ static struct snd_soc_ops neo1973_voice_ops = {
 	.hw_free = neo1973_voice_hw_free,
 };
 
-/* Shared routes and controls */
-
-static const struct snd_soc_dapm_widget neo1973_wm8753_dapm_widgets[] = {
-	SND_SOC_DAPM_LINE("GSM Line Out", NULL),
-	SND_SOC_DAPM_LINE("GSM Line In", NULL),
-	SND_SOC_DAPM_MIC("Headset Mic", NULL),
-	SND_SOC_DAPM_MIC("Handset Mic", NULL),
-};
-
-static const struct snd_soc_dapm_route neo1973_wm8753_routes[] = {
-	/* Connections to the GSM Module */
-	{"GSM Line Out", NULL, "MONO1"},
-	{"GSM Line Out", NULL, "MONO2"},
-	{"RXP", NULL, "GSM Line In"},
-	{"RXN", NULL, "GSM Line In"},
-
-	/* Connections to Headset */
-	{"MIC1", NULL, "Mic Bias"},
-	{"Mic Bias", NULL, "Headset Mic"},
-
-	/* Call Mic */
-	{"MIC2", NULL, "Mic Bias"},
-	{"MIC2N", NULL, "Mic Bias"},
-	{"Mic Bias", NULL, "Handset Mic"},
-
-	/* Connect the ALC pins */
-	{"ACIN", NULL, "ACOP"},
-};
-
-static const struct snd_kcontrol_new neo1973_wm8753_controls[] = {
-	SOC_DAPM_PIN_SWITCH("GSM Line Out"),
-	SOC_DAPM_PIN_SWITCH("GSM Line In"),
-	SOC_DAPM_PIN_SWITCH("Headset Mic"),
-	SOC_DAPM_PIN_SWITCH("Handset Mic"),
-};
-
-/* GTA02 specific routes and controls */
-
 static int gta02_speaker_enabled;
 
 static int lm4853_set_spk(struct snd_kcontrol *kcontrol,
@@ -257,7 +219,34 @@ static int lm4853_event(struct snd_soc_dapm_widget *w,
 	return 0;
 }
 
-static const struct snd_soc_dapm_route neo1973_gta02_routes[] = {
+static const struct snd_soc_dapm_widget neo1973_wm8753_dapm_widgets[] = {
+	SND_SOC_DAPM_LINE("GSM Line Out", NULL),
+	SND_SOC_DAPM_LINE("GSM Line In", NULL),
+	SND_SOC_DAPM_MIC("Headset Mic", NULL),
+	SND_SOC_DAPM_MIC("Handset Mic", NULL),
+	SND_SOC_DAPM_SPK("Handset Spk", NULL),
+	SND_SOC_DAPM_SPK("Stereo Out", lm4853_event),
+};
+
+static const struct snd_soc_dapm_route neo1973_wm8753_routes[] = {
+	/* Connections to the GSM Module */
+	{"GSM Line Out", NULL, "MONO1"},
+	{"GSM Line Out", NULL, "MONO2"},
+	{"RXP", NULL, "GSM Line In"},
+	{"RXN", NULL, "GSM Line In"},
+
+	/* Connections to Headset */
+	{"MIC1", NULL, "Mic Bias"},
+	{"Mic Bias", NULL, "Headset Mic"},
+
+	/* Call Mic */
+	{"MIC2", NULL, "Mic Bias"},
+	{"MIC2N", NULL, "Mic Bias"},
+	{"Mic Bias", NULL, "Handset Mic"},
+
+	/* Connect the ALC pins */
+	{"ACIN", NULL, "ACOP"},
+
 	/* Connections to the amp */
 	{"Stereo Out", NULL, "LOUT1"},
 	{"Stereo Out", NULL, "ROUT1"},
@@ -267,7 +256,11 @@ static const struct snd_soc_dapm_route neo1973_gta02_routes[] = {
 	{"Handset Spk", NULL, "ROUT2"},
 };
 
-static const struct snd_kcontrol_new neo1973_gta02_wm8753_controls[] = {
+static const struct snd_kcontrol_new neo1973_wm8753_controls[] = {
+	SOC_DAPM_PIN_SWITCH("GSM Line Out"),
+	SOC_DAPM_PIN_SWITCH("GSM Line In"),
+	SOC_DAPM_PIN_SWITCH("Headset Mic"),
+	SOC_DAPM_PIN_SWITCH("Handset Mic"),
 	SOC_DAPM_PIN_SWITCH("Handset Spk"),
 	SOC_DAPM_PIN_SWITCH("Stereo Out"),
 
@@ -276,86 +269,32 @@ static const struct snd_kcontrol_new neo1973_gta02_wm8753_controls[] = {
 		lm4853_set_spk),
 };
 
-static const struct snd_soc_dapm_widget neo1973_gta02_wm8753_dapm_widgets[] = {
-	SND_SOC_DAPM_SPK("Handset Spk", NULL),
-	SND_SOC_DAPM_SPK("Stereo Out", lm4853_event),
-};
-
-static int neo1973_gta02_wm8753_init(struct snd_soc_codec *codec)
-{
-	struct snd_soc_dapm_context *dapm = &codec->dapm;
-	int ret;
-
-	ret = snd_soc_dapm_new_controls(dapm, neo1973_gta02_wm8753_dapm_widgets,
-			ARRAY_SIZE(neo1973_gta02_wm8753_dapm_widgets));
-	if (ret)
-		return ret;
-
-	ret = snd_soc_dapm_add_routes(dapm, neo1973_gta02_routes,
-			ARRAY_SIZE(neo1973_gta02_routes));
-	if (ret)
-		return ret;
-
-	ret = snd_soc_add_card_controls(codec->card, neo1973_gta02_wm8753_controls,
-			ARRAY_SIZE(neo1973_gta02_wm8753_controls));
-	if (ret)
-		return ret;
-
-	snd_soc_dapm_disable_pin(dapm, "Stereo Out");
-	snd_soc_dapm_disable_pin(dapm, "Handset Spk");
-	snd_soc_dapm_ignore_suspend(dapm, "Stereo Out");
-	snd_soc_dapm_ignore_suspend(dapm, "Handset Spk");
-
-	return 0;
-}
-
 static int neo1973_wm8753_init(struct snd_soc_pcm_runtime *rtd)
 {
 	struct snd_soc_codec *codec = rtd->codec;
-	struct snd_soc_dapm_context *dapm = &codec->dapm;
-	int ret;
+	struct snd_soc_card *card = rtd->card;
 
 	/* set up NC codec pins */
-	snd_soc_dapm_nc_pin(dapm, "OUT3");
-	snd_soc_dapm_nc_pin(dapm, "OUT4");
-	snd_soc_dapm_nc_pin(dapm, "LINE1");
-	snd_soc_dapm_nc_pin(dapm, "LINE2");
-
-	/* Add neo1973 specific widgets */
-	ret = snd_soc_dapm_new_controls(dapm, neo1973_wm8753_dapm_widgets,
-			ARRAY_SIZE(neo1973_wm8753_dapm_widgets));
-	if (ret)
-		return ret;
-
-	/* add neo1973 specific controls */
-	ret = snd_soc_add_card_controls(rtd->card, neo1973_wm8753_controls,
-			ARRAY_SIZE(neo1973_wm8753_controls));
-	if (ret)
-		return ret;
-
-	/* set up neo1973 specific audio routes */
-	ret = snd_soc_dapm_add_routes(dapm, neo1973_wm8753_routes,
-			ARRAY_SIZE(neo1973_wm8753_routes));
-	if (ret)
-		return ret;
+	snd_soc_dapm_nc_pin(&codec->dapm, "OUT3");
+	snd_soc_dapm_nc_pin(&codec->dapm, "OUT4");
+	snd_soc_dapm_nc_pin(&codec->dapm, "LINE1");
+	snd_soc_dapm_nc_pin(&codec->dapm, "LINE2");
 
 	/* set endpoints to default off mode */
-	snd_soc_dapm_disable_pin(dapm, "GSM Line Out");
-	snd_soc_dapm_disable_pin(dapm, "GSM Line In");
-	snd_soc_dapm_disable_pin(dapm, "Headset Mic");
-	snd_soc_dapm_disable_pin(dapm, "Handset Mic");
+	snd_soc_dapm_disable_pin(&card->dapm, "GSM Line Out");
+	snd_soc_dapm_disable_pin(&card->dapm, "GSM Line In");
+	snd_soc_dapm_disable_pin(&card->dapm, "Headset Mic");
+	snd_soc_dapm_disable_pin(&card->dapm, "Handset Mic");
+	snd_soc_dapm_disable_pin(&card->dapm, "Stereo Out");
+	snd_soc_dapm_disable_pin(&card->dapm, "Handset Spk");
 
 	/* allow audio paths from the GSM modem to run during suspend */
-	snd_soc_dapm_ignore_suspend(dapm, "GSM Line Out");
-	snd_soc_dapm_ignore_suspend(dapm, "GSM Line In");
-	snd_soc_dapm_ignore_suspend(dapm, "Headset Mic");
-	snd_soc_dapm_ignore_suspend(dapm, "Handset Mic");
-
-	if (machine_is_neo1973_gta02()) {
-		ret = neo1973_gta02_wm8753_init(codec);
-		if (ret)
-			return ret;
-	}
+	snd_soc_dapm_ignore_suspend(&card->dapm, "GSM Line Out");
+	snd_soc_dapm_ignore_suspend(&card->dapm, "GSM Line In");
+	snd_soc_dapm_ignore_suspend(&card->dapm, "Headset Mic");
+	snd_soc_dapm_ignore_suspend(&card->dapm, "Handset Mic");
+	snd_soc_dapm_ignore_suspend(&card->dapm, "Stereo Out");
+	snd_soc_dapm_ignore_suspend(&card->dapm, "Handset Spk");
 
 	return 0;
 }
@@ -409,6 +348,13 @@ static struct snd_soc_card neo1973 = {
 	.num_aux_devs = ARRAY_SIZE(neo1973_aux_devs),
 	.codec_conf = neo1973_codec_conf,
 	.num_configs = ARRAY_SIZE(neo1973_codec_conf),
+
+	.controls = neo1973_wm8753_controls,
+	.num_controls = ARRAY_SIZE(neo1973_wm8753_controls),
+	.dapm_widgets = neo1973_wm8753_dapm_widgets,
+	.num_dapm_widgets = ARRAY_SIZE(neo1973_wm8753_dapm_widgets),
+	.dapm_routes = neo1973_wm8753_routes,
+	.num_dapm_routes = ARRAY_SIZE(neo1973_wm8753_routes),
 };
 
 static struct platform_device *neo1973_snd_device;
diff --git a/sound/soc/samsung/rx1950_uda1380.c b/sound/soc/samsung/rx1950_uda1380.c
index 06ebdc061770..2982d9e7f268 100644
--- a/sound/soc/samsung/rx1950_uda1380.c
+++ b/sound/soc/samsung/rx1950_uda1380.c
@@ -131,10 +131,6 @@ static int rx1950_startup(struct snd_pcm_substream *substream)
 {
 	struct snd_pcm_runtime *runtime = substream->runtime;
 
-	runtime->hw.rate_min = hw_rates.list[0];
-	runtime->hw.rate_max = hw_rates.list[hw_rates.count - 1];
-	runtime->hw.rates = SNDRV_PCM_RATE_KNOT;
-
 	return snd_pcm_hw_constraint_list(runtime, 0,
 					SNDRV_PCM_HW_PARAM_RATE,
 					&hw_rates);
@@ -226,7 +222,6 @@ static int rx1950_uda1380_init(struct snd_soc_pcm_runtime *rtd)
 {
 	struct snd_soc_codec *codec = rtd->codec;
 	struct snd_soc_dapm_context *dapm = &codec->dapm;
-	int err;
 
 	snd_soc_dapm_enable_pin(dapm, "Headphone Jack");
 	snd_soc_dapm_enable_pin(dapm, "Speaker");
diff --git a/sound/soc/samsung/smdk_wm8994.c b/sound/soc/samsung/smdk_wm8994.c
index d38ae98e2f32..682eb4f7ba0c 100644
--- a/sound/soc/samsung/smdk_wm8994.c
+++ b/sound/soc/samsung/smdk_wm8994.c
@@ -202,7 +202,7 @@ static int smdk_audio_probe(struct platform_device *pdev)
 
 static struct platform_driver smdk_audio_driver = {
 	.driver		= {
-		.name	= "smdk-audio-wm8894",
+		.name	= "smdk-audio-wm8994",
 		.owner	= THIS_MODULE,
 		.of_match_table = of_match_ptr(samsung_wm8994_of_match),
 		.pm	= &snd_soc_pm_ops,
diff --git a/sound/soc/samsung/tobermory.c b/sound/soc/samsung/tobermory.c
index f21ff608a819..1807b75ccc12 100644
--- a/sound/soc/samsung/tobermory.c
+++ b/sound/soc/samsung/tobermory.c
@@ -44,6 +44,8 @@ static int tobermory_set_bias_level(struct snd_soc_card *card,
 						     SND_SOC_CLOCK_IN);
 			if (ret < 0) {
 				pr_err("Failed to set SYSCLK: %d\n", ret);
+				snd_soc_dai_set_pll(codec_dai, WM8962_FLL,
+						    0, 0, 0);
 				return ret;
 			}
 		}
diff --git a/sound/soc/sh/migor.c b/sound/soc/sh/migor.c
index 5014a884afee..c58c2529f103 100644
--- a/sound/soc/sh/migor.c
+++ b/sound/soc/sh/migor.c
@@ -136,19 +136,6 @@ static const struct snd_soc_dapm_route audio_map[] = {
 	{ "Mic Bias", NULL, "External Microphone" },
 };
 
-static int migor_dai_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_new_controls(dapm, migor_dapm_widgets,
-				  ARRAY_SIZE(migor_dapm_widgets));
-
-	snd_soc_dapm_add_routes(dapm, audio_map, ARRAY_SIZE(audio_map));
-
-	return 0;
-}
-
 /* migor digital audio interface glue - connects codec <--> CPU */
 static struct snd_soc_dai_link migor_dai = {
 	.name = "wm8978",
@@ -158,7 +145,6 @@ static struct snd_soc_dai_link migor_dai = {
 	.platform_name = "siu-pcm-audio",
 	.codec_name = "wm8978.0-001a",
 	.ops = &migor_dai_ops,
-	.init = migor_dai_init,
 };
 
 /* migor audio machine driver */
@@ -167,6 +153,11 @@ static struct snd_soc_card snd_soc_migor = {
 	.owner = THIS_MODULE,
 	.dai_link = &migor_dai,
 	.num_links = 1,
+
+	.dapm_widgets = migor_dapm_widgets,
+	.num_dapm_widgets = ARRAY_SIZE(migor_dapm_widgets),
+	.dapm_routes = audio_map,
+	.num_dapm_routes = ARRAY_SIZE(audio_map),
 };
 
 static struct platform_device *migor_snd_device;
diff --git a/sound/soc/sh/rcar/Makefile b/sound/soc/sh/rcar/Makefile
index 0ff492df7929..7d0051ced838 100644
--- a/sound/soc/sh/rcar/Makefile
+++ b/sound/soc/sh/rcar/Makefile
@@ -1,2 +1,2 @@
-snd-soc-rcar-objs	:= core.o gen.o scu.o adg.o ssi.o
+snd-soc-rcar-objs	:= core.o gen.o src.o adg.o ssi.o
 obj-$(CONFIG_SND_SOC_RCAR)	+= snd-soc-rcar.o
\ No newline at end of file
diff --git a/sound/soc/sh/rcar/adg.c b/sound/soc/sh/rcar/adg.c
index a53235c4d1b0..953f1cce982d 100644
--- a/sound/soc/sh/rcar/adg.c
+++ b/sound/soc/sh/rcar/adg.c
@@ -25,15 +25,165 @@ struct rsnd_adg {
 };
 
 #define for_each_rsnd_clk(pos, adg, i)		\
-	for (i = 0, (pos) = adg->clk[i];	\
-	     i < CLKMAX;			\
-	     i++, (pos) = adg->clk[i])
+	for (i = 0;				\
+	     (i < CLKMAX) &&			\
+	     ((pos) = adg->clk[i]);		\
+	     i++)
 #define rsnd_priv_to_adg(priv) ((struct rsnd_adg *)(priv)->adg)
 
-static int rsnd_adg_set_convert_clk_gen1(struct rsnd_priv *priv,
-					 struct rsnd_mod *mod,
-					 unsigned int src_rate,
-					 unsigned int dst_rate)
+
+static u32 rsnd_adg_ssi_ws_timing_gen2(struct rsnd_dai_stream *io)
+{
+	struct rsnd_mod *mod = rsnd_io_to_mod_ssi(io);
+	struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
+	int id = rsnd_mod_id(mod);
+	int ws = id;
+
+	if (rsnd_ssi_is_pin_sharing(rsnd_ssi_mod_get(priv, id))) {
+		switch (id) {
+		case 1:
+		case 2:
+			ws = 0;
+			break;
+		case 4:
+			ws = 3;
+			break;
+		case 8:
+			ws = 7;
+			break;
+		}
+	}
+
+	return (0x6 + ws) << 8;
+}
+
+static int rsnd_adg_set_src_timsel_gen2(struct rsnd_dai *rdai,
+					struct rsnd_mod *mod,
+					struct rsnd_dai_stream *io,
+					u32 timsel)
+{
+	int is_play = rsnd_dai_is_play(rdai, io);
+	int id = rsnd_mod_id(mod);
+	int shift = (id % 2) ? 16 : 0;
+	u32 mask, ws;
+	u32 in, out;
+
+	ws = rsnd_adg_ssi_ws_timing_gen2(io);
+
+	in  = (is_play) ? timsel : ws;
+	out = (is_play) ? ws     : timsel;
+
+	in   = in	<< shift;
+	out  = out	<< shift;
+	mask = 0xffff	<< shift;
+
+	switch (id / 2) {
+	case 0:
+		rsnd_mod_bset(mod, SRCIN_TIMSEL0,  mask, in);
+		rsnd_mod_bset(mod, SRCOUT_TIMSEL0, mask, out);
+		break;
+	case 1:
+		rsnd_mod_bset(mod, SRCIN_TIMSEL1,  mask, in);
+		rsnd_mod_bset(mod, SRCOUT_TIMSEL1, mask, out);
+		break;
+	case 2:
+		rsnd_mod_bset(mod, SRCIN_TIMSEL2,  mask, in);
+		rsnd_mod_bset(mod, SRCOUT_TIMSEL2, mask, out);
+		break;
+	case 3:
+		rsnd_mod_bset(mod, SRCIN_TIMSEL3,  mask, in);
+		rsnd_mod_bset(mod, SRCOUT_TIMSEL3, mask, out);
+		break;
+	case 4:
+		rsnd_mod_bset(mod, SRCIN_TIMSEL4,  mask, in);
+		rsnd_mod_bset(mod, SRCOUT_TIMSEL4, mask, out);
+		break;
+	}
+
+	return 0;
+}
+
+int rsnd_adg_set_convert_clk_gen2(struct rsnd_mod *mod,
+				  struct rsnd_dai *rdai,
+				  struct rsnd_dai_stream *io,
+				  unsigned int src_rate,
+				  unsigned int dst_rate)
+{
+	struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
+	struct rsnd_adg *adg = rsnd_priv_to_adg(priv);
+	struct device *dev = rsnd_priv_to_dev(priv);
+	int idx, sel, div, step, ret;
+	u32 val, en;
+	unsigned int min, diff;
+	unsigned int sel_rate [] = {
+		clk_get_rate(adg->clk[CLKA]),	/* 0000: CLKA */
+		clk_get_rate(adg->clk[CLKB]),	/* 0001: CLKB */
+		clk_get_rate(adg->clk[CLKC]),	/* 0010: CLKC */
+		adg->rbga_rate_for_441khz_div_6,/* 0011: RBGA */
+		adg->rbgb_rate_for_48khz_div_6,	/* 0100: RBGB */
+	};
+
+	min = ~0;
+	val = 0;
+	en = 0;
+	for (sel = 0; sel < ARRAY_SIZE(sel_rate); sel++) {
+		idx = 0;
+		step = 2;
+
+		if (!sel_rate[sel])
+			continue;
+
+		for (div = 2; div <= 98304; div += step) {
+			diff = abs(src_rate - sel_rate[sel] / div);
+			if (min > diff) {
+				val = (sel << 8) | idx;
+				min = diff;
+				en = 1 << (sel + 1); /* fixme */
+			}
+
+			/*
+			 * step of 0_0000 / 0_0001 / 0_1101
+			 * are out of order
+			 */
+			if ((idx > 2) && (idx % 2))
+				step *= 2;
+			if (idx == 0x1c) {
+				div += step;
+				step *= 2;
+			}
+			idx++;
+		}
+	}
+
+	if (min == ~0) {
+		dev_err(dev, "no Input clock\n");
+		return -EIO;
+	}
+
+	ret = rsnd_adg_set_src_timsel_gen2(rdai, mod, io, val);
+	if (ret < 0) {
+		dev_err(dev, "timsel error\n");
+		return ret;
+	}
+
+	rsnd_mod_bset(mod, DIV_EN, en, en);
+
+	return 0;
+}
+
+int rsnd_adg_set_convert_timing_gen2(struct rsnd_mod *mod,
+				     struct rsnd_dai *rdai,
+				     struct rsnd_dai_stream *io)
+{
+	u32 val = rsnd_adg_ssi_ws_timing_gen2(io);
+
+	return rsnd_adg_set_src_timsel_gen2(rdai, mod, io, val);
+}
+
+int rsnd_adg_set_convert_clk_gen1(struct rsnd_priv *priv,
+				  struct rsnd_mod *mod,
+				  unsigned int src_rate,
+				  unsigned int dst_rate)
 {
 	struct rsnd_adg *adg = rsnd_priv_to_adg(priv);
 	struct device *dev = rsnd_priv_to_dev(priv);
@@ -91,18 +241,6 @@ find_rate:
 	return 0;
 }
 
-int rsnd_adg_set_convert_clk(struct rsnd_priv *priv,
-			     struct rsnd_mod *mod,
-			     unsigned int src_rate,
-			     unsigned int dst_rate)
-{
-	if (rsnd_is_gen1(priv))
-		return rsnd_adg_set_convert_clk_gen1(priv, mod,
-						     src_rate, dst_rate);
-
-	return -EINVAL;
-}
-
 static void rsnd_adg_set_ssi_clk(struct rsnd_mod *mod, u32 val)
 {
 	int id = rsnd_mod_id(mod);
@@ -254,13 +392,13 @@ static void rsnd_adg_ssi_clk_init(struct rsnd_priv *priv, struct rsnd_adg *adg)
 }
 
 int rsnd_adg_probe(struct platform_device *pdev,
-		   struct rcar_snd_info *info,
 		   struct rsnd_priv *priv)
 {
 	struct rsnd_adg *adg;
 	struct device *dev = rsnd_priv_to_dev(priv);
-	struct clk *clk;
+	struct clk *clk, *clk_orig;
 	int i;
+	bool use_old_style = false;
 
 	adg = devm_kzalloc(dev, sizeof(*adg), GFP_KERNEL);
 	if (!adg) {
@@ -268,10 +406,39 @@ int rsnd_adg_probe(struct platform_device *pdev,
 		return -ENOMEM;
 	}
 
-	adg->clk[CLKA] = clk_get(NULL, "audio_clk_a");
-	adg->clk[CLKB] = clk_get(NULL, "audio_clk_b");
-	adg->clk[CLKC] = clk_get(NULL, "audio_clk_c");
-	adg->clk[CLKI] = clk_get(NULL, "audio_clk_internal");
+	clk_orig	= devm_clk_get(dev, NULL);
+	adg->clk[CLKA]	= devm_clk_get(dev, "clk_a");
+	adg->clk[CLKB]	= devm_clk_get(dev, "clk_b");
+	adg->clk[CLKC]	= devm_clk_get(dev, "clk_c");
+	adg->clk[CLKI]	= devm_clk_get(dev, "clk_i");
+
+	/*
+	 * It request device dependent audio clock.
+	 * But above all clks will indicate rsnd module clock
+	 * if platform doesn't it
+	 */
+	for_each_rsnd_clk(clk, adg, i) {
+		if (clk_orig == clk) {
+			dev_warn(dev,
+				 "doesn't have device dependent clock, use independent clock\n");
+			use_old_style = true;
+			break;
+		}
+	}
+
+	/*
+	 * note:
+	 * these exist in order to keep compatible with
+	 * platform which has device independent audio clock,
+	 * but will be removed soon
+	 */
+	if (use_old_style) {
+		adg->clk[CLKA] = devm_clk_get(NULL, "audio_clk_a");
+		adg->clk[CLKB] = devm_clk_get(NULL, "audio_clk_b");
+		adg->clk[CLKC] = devm_clk_get(NULL, "audio_clk_c");
+		adg->clk[CLKI] = devm_clk_get(NULL, "audio_clk_internal");
+	}
+
 	for_each_rsnd_clk(clk, adg, i) {
 		if (IS_ERR(clk)) {
 			dev_err(dev, "Audio clock failed\n");
@@ -287,14 +454,3 @@ int rsnd_adg_probe(struct platform_device *pdev,
 
 	return 0;
 }
-
-void rsnd_adg_remove(struct platform_device *pdev,
-		     struct rsnd_priv *priv)
-{
-	struct rsnd_adg *adg = priv->adg;
-	struct clk *clk;
-	int i;
-
-	for_each_rsnd_clk(clk, adg, i)
-		clk_put(clk);
-}
diff --git a/sound/soc/sh/rcar/core.c b/sound/soc/sh/rcar/core.c
index 3a4fe9d0d4f2..d836e8a9fdce 100644
--- a/sound/soc/sh/rcar/core.c
+++ b/sound/soc/sh/rcar/core.c
@@ -73,13 +73,13 @@
  *   |  +- ssi[2]
  *   |  ...
  *   |
- *   | ** these control scu
+ *   | ** these control src
  *   |
- *   +- scu
+ *   +- src
  *      |
- *      +- scu[0]
- *      +- scu[1]
- *      +- scu[2]
+ *      +- src[0]
+ *      +- src[1]
+ *      +- src[2]
  *      ...
  *
  *
@@ -107,6 +107,11 @@
 	(!(priv->info->func) ? 0 :		\
 	 priv->info->func(param))
 
+#define rsnd_is_enable_path(io, name) \
+	((io)->info ? (io)->info->name : NULL)
+#define rsnd_info_id(priv, io, name) \
+	((io)->info->name - priv->info->name##_info)
+
 /*
  *	rsnd_mod functions
  */
@@ -121,17 +126,19 @@ char *rsnd_mod_name(struct rsnd_mod *mod)
 void rsnd_mod_init(struct rsnd_priv *priv,
 		   struct rsnd_mod *mod,
 		   struct rsnd_mod_ops *ops,
+		   enum rsnd_mod_type type,
 		   int id)
 {
 	mod->priv	= priv;
 	mod->id		= id;
 	mod->ops	= ops;
-	INIT_LIST_HEAD(&mod->list);
+	mod->type	= type;
 }
 
 /*
  *	rsnd_dma functions
  */
+static void __rsnd_dma_start(struct rsnd_dma *dma);
 static void rsnd_dma_continue(struct rsnd_dma *dma)
 {
 	/* push next A or B plane */
@@ -142,8 +149,9 @@ static void rsnd_dma_continue(struct rsnd_dma *dma)
 void rsnd_dma_start(struct rsnd_dma *dma)
 {
 	/* push both A and B plane*/
+	dma->offset = 0;
 	dma->submit_loop = 2;
-	schedule_work(&dma->work);
+	__rsnd_dma_start(dma);
 }
 
 void rsnd_dma_stop(struct rsnd_dma *dma)
@@ -156,12 +164,26 @@ void rsnd_dma_stop(struct rsnd_dma *dma)
 static void rsnd_dma_complete(void *data)
 {
 	struct rsnd_dma *dma = (struct rsnd_dma *)data;
-	struct rsnd_priv *priv = dma->priv;
+	struct rsnd_mod *mod = rsnd_dma_to_mod(dma);
+	struct rsnd_priv *priv = rsnd_mod_to_priv(rsnd_dma_to_mod(dma));
+	struct rsnd_dai_stream *io = rsnd_mod_to_io(mod);
 	unsigned long flags;
 
 	rsnd_lock(priv, flags);
 
-	dma->complete(dma);
+	/*
+	 * Renesas sound Gen1 needs 1 DMAC,
+	 * Gen2 needs 2 DMAC.
+	 * In Gen2 case, it are Audio-DMAC, and Audio-DMAC-peri-peri.
+	 * But, Audio-DMAC-peri-peri doesn't have interrupt,
+	 * and this driver is assuming that here.
+	 *
+	 * If Audio-DMAC-peri-peri has interrpt,
+	 * rsnd_dai_pointer_update() will be called twice,
+	 * ant it will breaks io->byte_pos
+	 */
+
+	rsnd_dai_pointer_update(io, io->byte_per_period);
 
 	if (dma->submit_loop)
 		rsnd_dma_continue(dma);
@@ -169,20 +191,23 @@ static void rsnd_dma_complete(void *data)
 	rsnd_unlock(priv, flags);
 }
 
-static void rsnd_dma_do_work(struct work_struct *work)
+static void __rsnd_dma_start(struct rsnd_dma *dma)
 {
-	struct rsnd_dma *dma = container_of(work, struct rsnd_dma, work);
-	struct rsnd_priv *priv = dma->priv;
+	struct rsnd_mod *mod = rsnd_dma_to_mod(dma);
+	struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
+	struct rsnd_dai_stream *io = rsnd_mod_to_io(mod);
+	struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io);
 	struct device *dev = rsnd_priv_to_dev(priv);
 	struct dma_async_tx_descriptor *desc;
 	dma_addr_t buf;
-	size_t len;
+	size_t len = io->byte_per_period;
 	int i;
 
 	for (i = 0; i < dma->submit_loop; i++) {
 
-		if (dma->inquiry(dma, &buf, &len) < 0)
-			return;
+		buf = runtime->dma_addr +
+			rsnd_dai_pointer_offset(io, dma->offset + len);
+		dma->offset = len;
 
 		desc = dmaengine_prep_slave_single(
 			dma->chan, buf, len, dma->dir,
@@ -204,16 +229,20 @@ static void rsnd_dma_do_work(struct work_struct *work)
 	}
 }
 
+static void rsnd_dma_do_work(struct work_struct *work)
+{
+	struct rsnd_dma *dma = container_of(work, struct rsnd_dma, work);
+
+	__rsnd_dma_start(dma);
+}
+
 int rsnd_dma_available(struct rsnd_dma *dma)
 {
 	return !!dma->chan;
 }
 
 int rsnd_dma_init(struct rsnd_priv *priv, struct rsnd_dma *dma,
-		  int is_play, int id,
-		  int (*inquiry)(struct rsnd_dma *dma,
-				  dma_addr_t *buf, int *len),
-		  int (*complete)(struct rsnd_dma *dma))
+		  int is_play, int id)
 {
 	struct device *dev = rsnd_priv_to_dev(priv);
 	struct dma_slave_config cfg;
@@ -246,9 +275,6 @@ int rsnd_dma_init(struct rsnd_priv *priv, struct rsnd_dma *dma,
 		goto rsnd_dma_init_err;
 
 	dma->dir = is_play ? DMA_TO_DEVICE : DMA_FROM_DEVICE;
-	dma->priv = priv;
-	dma->inquiry = inquiry;
-	dma->complete = complete;
 	INIT_WORK(&dma->work, rsnd_dma_do_work);
 
 	return 0;
@@ -271,26 +297,42 @@ void  rsnd_dma_quit(struct rsnd_priv *priv,
 /*
  *	rsnd_dai functions
  */
-#define rsnd_dai_call(rdai, io, fn)			\
-({							\
-	struct rsnd_mod *mod, *n;			\
-	int ret = 0;					\
-	for_each_rsnd_mod(mod, n, io) {			\
-		ret = rsnd_mod_call(mod, fn, rdai, io);	\
-		if (ret < 0)				\
-			break;				\
-	}						\
-	ret;						\
+#define __rsnd_mod_call(mod, func, rdai, io)			\
+({								\
+	struct rsnd_priv *priv = rsnd_mod_to_priv(mod);		\
+	struct device *dev = rsnd_priv_to_dev(priv);		\
+	dev_dbg(dev, "%s [%d] %s\n",				\
+		rsnd_mod_name(mod), rsnd_mod_id(mod), #func);	\
+	(mod)->ops->func(mod, rdai, io);			\
+})
+
+#define rsnd_mod_call(mod, func, rdai, io)	\
+	(!(mod) ? -ENODEV :			\
+	 !((mod)->ops->func) ? 0 :		\
+	 __rsnd_mod_call(mod, func, (rdai), (io)))
+
+#define rsnd_dai_call(rdai, io, fn)				\
+({								\
+	struct rsnd_mod *mod;					\
+	int ret = 0, i;						\
+	for (i = 0; i < RSND_MOD_MAX; i++) {			\
+		mod = (io)->mod[i];				\
+		if (!mod)					\
+			continue;				\
+		ret = rsnd_mod_call(mod, fn, (rdai), (io));	\
+		if (ret < 0)					\
+			break;					\
+	}							\
+	ret;							\
 })
 
-int rsnd_dai_connect(struct rsnd_dai *rdai,
-		     struct rsnd_mod *mod,
-		     struct rsnd_dai_stream *io)
+static int rsnd_dai_connect(struct rsnd_mod *mod,
+			    struct rsnd_dai_stream *io)
 {
 	if (!mod)
 		return -EIO;
 
-	if (!list_empty(&mod->list)) {
+	if (io->mod[mod->type]) {
 		struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
 		struct device *dev = rsnd_priv_to_dev(priv);
 
@@ -300,14 +342,8 @@ int rsnd_dai_connect(struct rsnd_dai *rdai,
 		return -EIO;
 	}
 
-	list_add_tail(&mod->list, &io->head);
-
-	return 0;
-}
-
-int rsnd_dai_disconnect(struct rsnd_mod *mod)
-{
-	list_del_init(&mod->list);
+	io->mod[mod->type] = mod;
+	mod->io = io;
 
 	return 0;
 }
@@ -316,7 +352,7 @@ int rsnd_dai_id(struct rsnd_priv *priv, struct rsnd_dai *rdai)
 {
 	int id = rdai - priv->rdai;
 
-	if ((id < 0) || (id >= rsnd_dai_nr(priv)))
+	if ((id < 0) || (id >= rsnd_rdai_nr(priv)))
 		return -EINVAL;
 
 	return id;
@@ -324,7 +360,7 @@ int rsnd_dai_id(struct rsnd_priv *priv, struct rsnd_dai *rdai)
 
 struct rsnd_dai *rsnd_dai_get(struct rsnd_priv *priv, int id)
 {
-	if ((id < 0) || (id >= rsnd_dai_nr(priv)))
+	if ((id < 0) || (id >= rsnd_rdai_nr(priv)))
 		return NULL;
 
 	return priv->rdai + id;
@@ -382,10 +418,6 @@ static int rsnd_dai_stream_init(struct rsnd_dai_stream *io,
 {
 	struct snd_pcm_runtime *runtime = substream->runtime;
 
-	if (!list_empty(&io->head))
-		return -EIO;
-
-	INIT_LIST_HEAD(&io->head);
 	io->substream		= substream;
 	io->byte_pos		= 0;
 	io->period_pos		= 0;
@@ -440,10 +472,6 @@ static int rsnd_soc_dai_trigger(struct snd_pcm_substream *substream, int cmd,
 		if (ret < 0)
 			goto dai_trigger_end;
 
-		ret = rsnd_gen_path_init(priv, rdai, io);
-		if (ret < 0)
-			goto dai_trigger_end;
-
 		ret = rsnd_dai_call(rdai, io, init);
 		if (ret < 0)
 			goto dai_trigger_end;
@@ -461,10 +489,6 @@ static int rsnd_soc_dai_trigger(struct snd_pcm_substream *substream, int cmd,
 		if (ret < 0)
 			goto dai_trigger_end;
 
-		ret = rsnd_gen_path_exit(priv, rdai, io);
-		if (ret < 0)
-			goto dai_trigger_end;
-
 		ret = rsnd_platform_call(priv, dai, stop, ssi_id);
 		if (ret < 0)
 			goto dai_trigger_end;
@@ -540,24 +564,86 @@ static const struct snd_soc_dai_ops rsnd_soc_dai_ops = {
 	.set_fmt	= rsnd_soc_dai_set_fmt,
 };
 
+static int rsnd_path_init(struct rsnd_priv *priv,
+			  struct rsnd_dai *rdai,
+			  struct rsnd_dai_stream *io)
+{
+	struct rsnd_mod *mod;
+	struct rsnd_dai_platform_info *dai_info = rdai->info;
+	int ret;
+	int ssi_id = -1;
+	int src_id = -1;
+
+	/*
+	 * Gen1 is created by SRU/SSI, and this SRU is base module of
+	 * Gen2's SCU/SSIU/SSI. (Gen2 SCU/SSIU came from SRU)
+	 *
+	 * Easy image is..
+	 *	Gen1 SRU = Gen2 SCU + SSIU + etc
+	 *
+	 * Gen2 SCU path is very flexible, but, Gen1 SRU (SCU parts) is
+	 * using fixed path.
+	 */
+	if (dai_info) {
+		if (rsnd_is_enable_path(io, ssi))
+			ssi_id = rsnd_info_id(priv, io, ssi);
+		if (rsnd_is_enable_path(io, src))
+			src_id = rsnd_info_id(priv, io, src);
+	} else {
+		/* get SSI's ID */
+		mod = rsnd_ssi_mod_get_frm_dai(priv,
+					       rsnd_dai_id(priv, rdai),
+					       rsnd_dai_is_play(rdai, io));
+		if (!mod)
+			return 0;
+		ssi_id = src_id = rsnd_mod_id(mod);
+	}
+
+	ret = 0;
+
+	/* SRC */
+	if (src_id >= 0) {
+		mod = rsnd_src_mod_get(priv, src_id);
+		ret = rsnd_dai_connect(mod, io);
+		if (ret < 0)
+			return ret;
+	}
+
+	/* SSI */
+	if (ssi_id >= 0) {
+		mod = rsnd_ssi_mod_get(priv, ssi_id);
+		ret = rsnd_dai_connect(mod, io);
+		if (ret < 0)
+			return ret;
+	}
+
+	return ret;
+}
+
 static int rsnd_dai_probe(struct platform_device *pdev,
-			  struct rcar_snd_info *info,
 			  struct rsnd_priv *priv)
 {
 	struct snd_soc_dai_driver *drv;
+	struct rcar_snd_info *info = rsnd_priv_to_info(priv);
 	struct rsnd_dai *rdai;
 	struct rsnd_mod *pmod, *cmod;
 	struct device *dev = rsnd_priv_to_dev(priv);
-	int dai_nr;
+	int dai_nr = info->dai_info_nr;
 	int i;
 
-	/* get max dai nr */
-	for (dai_nr = 0; dai_nr < 32; dai_nr++) {
-		pmod = rsnd_ssi_mod_get_frm_dai(priv, dai_nr, 1);
-		cmod = rsnd_ssi_mod_get_frm_dai(priv, dai_nr, 0);
+	/*
+	 * dai_nr should be set via dai_info_nr,
+	 * but allow it to keeping compatible
+	 */
+	if (!dai_nr) {
+		/* get max dai nr */
+		for (dai_nr = 0; dai_nr < 32; dai_nr++) {
+			pmod = rsnd_ssi_mod_get_frm_dai(priv, dai_nr, 1);
+			cmod = rsnd_ssi_mod_get_frm_dai(priv, dai_nr, 0);
 
-		if (!pmod && !cmod)
-			break;
+			if (!pmod && !cmod)
+				break;
+		}
 	}
 
 	if (!dai_nr) {
@@ -572,7 +658,13 @@ static int rsnd_dai_probe(struct platform_device *pdev,
 		return -ENOMEM;
 	}
 
+	priv->rdai_nr	= dai_nr;
+	priv->daidrv	= drv;
+	priv->rdai	= rdai;
+
 	for (i = 0; i < dai_nr; i++) {
+		if (info->dai_info)
+			rdai[i].info = &info->dai_info[i];
 
 		pmod = rsnd_ssi_mod_get_frm_dai(priv, i, 1);
 		cmod = rsnd_ssi_mod_get_frm_dai(priv, i, 0);
@@ -580,9 +672,6 @@ static int rsnd_dai_probe(struct platform_device *pdev,
 		/*
 		 *	init rsnd_dai
 		 */
-		INIT_LIST_HEAD(&rdai[i].playback.head);
-		INIT_LIST_HEAD(&rdai[i].capture.head);
-
 		snprintf(rdai[i].name, RSND_DAI_NAME_SIZE, "rsnd-dai.%d", i);
 
 		/*
@@ -595,12 +684,20 @@ static int rsnd_dai_probe(struct platform_device *pdev,
 			drv[i].playback.formats		= RSND_FMTS;
 			drv[i].playback.channels_min	= 2;
 			drv[i].playback.channels_max	= 2;
+
+			if (info->dai_info)
+				rdai[i].playback.info = &info->dai_info[i].playback;
+			rsnd_path_init(priv, &rdai[i], &rdai[i].playback);
 		}
 		if (cmod) {
 			drv[i].capture.rates		= RSND_RATES;
 			drv[i].capture.formats		= RSND_FMTS;
 			drv[i].capture.channels_min	= 2;
 			drv[i].capture.channels_max	= 2;
+
+			if (info->dai_info)
+				rdai[i].capture.info = &info->dai_info[i].capture;
+			rsnd_path_init(priv, &rdai[i], &rdai[i].capture);
 		}
 
 		dev_dbg(dev, "%s (%s/%s)\n", rdai[i].name,
@@ -608,18 +705,9 @@ static int rsnd_dai_probe(struct platform_device *pdev,
 			cmod ? "capture" : "  --   ");
 	}
 
-	priv->dai_nr	= dai_nr;
-	priv->daidrv	= drv;
-	priv->rdai	= rdai;
-
 	return 0;
 }
 
-static void rsnd_dai_remove(struct platform_device *pdev,
-			  struct rsnd_priv *priv)
-{
-}
-
 /*
  *		pcm ops
  */
@@ -713,7 +801,16 @@ static int rsnd_probe(struct platform_device *pdev)
 	struct rcar_snd_info *info;
 	struct rsnd_priv *priv;
 	struct device *dev = &pdev->dev;
-	int ret;
+	struct rsnd_dai *rdai;
+	int (*probe_func[])(struct platform_device *pdev,
+			    struct rsnd_priv *priv) = {
+		rsnd_gen_probe,
+		rsnd_ssi_probe,
+		rsnd_src_probe,
+		rsnd_adg_probe,
+		rsnd_dai_probe,
+	};
+	int ret, i;
 
 	info = pdev->dev.platform_data;
 	if (!info) {
@@ -737,25 +834,21 @@ static int rsnd_probe(struct platform_device *pdev)
 	/*
 	 *	init each module
 	 */
-	ret = rsnd_gen_probe(pdev, info, priv);
-	if (ret < 0)
-		return ret;
-
-	ret = rsnd_scu_probe(pdev, info, priv);
-	if (ret < 0)
-		return ret;
+	for (i = 0; i < ARRAY_SIZE(probe_func); i++) {
+		ret = probe_func[i](pdev, priv);
+		if (ret)
+			return ret;
+	}
 
-	ret = rsnd_adg_probe(pdev, info, priv);
-	if (ret < 0)
-		return ret;
+	for_each_rsnd_dai(rdai, priv, i) {
+		ret = rsnd_dai_call(rdai, &rdai->playback, probe);
+		if (ret)
+			return ret;
 
-	ret = rsnd_ssi_probe(pdev, info, priv);
-	if (ret < 0)
-		return ret;
-
-	ret = rsnd_dai_probe(pdev, info, priv);
-	if (ret < 0)
-		return ret;
+		ret = rsnd_dai_call(rdai, &rdai->capture, probe);
+		if (ret)
+			return ret;
+	}
 
 	/*
 	 *	asoc register
@@ -767,7 +860,7 @@ static int rsnd_probe(struct platform_device *pdev)
 	}
 
 	ret = snd_soc_register_component(dev, &rsnd_soc_component,
-					 priv->daidrv, rsnd_dai_nr(priv));
+					 priv->daidrv, rsnd_rdai_nr(priv));
 	if (ret < 0) {
 		dev_err(dev, "cannot snd dai register\n");
 		goto exit_snd_soc;
@@ -789,17 +882,20 @@ exit_snd_soc:
 static int rsnd_remove(struct platform_device *pdev)
 {
 	struct rsnd_priv *priv = dev_get_drvdata(&pdev->dev);
+	struct rsnd_dai *rdai;
+	int ret, i;
 
 	pm_runtime_disable(&pdev->dev);
 
-	/*
-	 *	remove each module
-	 */
-	rsnd_ssi_remove(pdev, priv);
-	rsnd_adg_remove(pdev, priv);
-	rsnd_scu_remove(pdev, priv);
-	rsnd_dai_remove(pdev, priv);
-	rsnd_gen_remove(pdev, priv);
+	for_each_rsnd_dai(rdai, priv, i) {
+		ret = rsnd_dai_call(rdai, &rdai->playback, remove);
+		if (ret)
+			return ret;
+
+		ret = rsnd_dai_call(rdai, &rdai->capture, remove);
+		if (ret)
+			return ret;
+	}
 
 	return 0;
 }
diff --git a/sound/soc/sh/rcar/gen.c b/sound/soc/sh/rcar/gen.c
index add088bd4b2a..9094970dbdfb 100644
--- a/sound/soc/sh/rcar/gen.c
+++ b/sound/soc/sh/rcar/gen.c
@@ -155,62 +155,6 @@ static int rsnd_gen_regmap_init(struct rsnd_priv *priv,
 	return 0;
 }
 
-int rsnd_gen_path_init(struct rsnd_priv *priv,
-		       struct rsnd_dai *rdai,
-		       struct rsnd_dai_stream *io)
-{
-	struct rsnd_mod *mod;
-	int ret;
-	int id;
-
-	/*
-	 * Gen1 is created by SRU/SSI, and this SRU is base module of
-	 * Gen2's SCU/SSIU/SSI. (Gen2 SCU/SSIU came from SRU)
-	 *
-	 * Easy image is..
-	 *	Gen1 SRU = Gen2 SCU + SSIU + etc
-	 *
-	 * Gen2 SCU path is very flexible, but, Gen1 SRU (SCU parts) is
-	 * using fixed path.
-	 *
-	 * Then, SSI id = SCU id here
-	 */
-
-	/* get SSI's ID */
-	mod = rsnd_ssi_mod_get_frm_dai(priv,
-				       rsnd_dai_id(priv, rdai),
-				       rsnd_dai_is_play(rdai, io));
-	id = rsnd_mod_id(mod);
-
-	/* SSI */
-	mod = rsnd_ssi_mod_get(priv, id);
-	ret = rsnd_dai_connect(rdai, mod, io);
-	if (ret < 0)
-		return ret;
-
-	/* SCU */
-	mod = rsnd_scu_mod_get(priv, id);
-	ret = rsnd_dai_connect(rdai, mod, io);
-
-	return ret;
-}
-
-int rsnd_gen_path_exit(struct rsnd_priv *priv,
-		       struct rsnd_dai *rdai,
-		       struct rsnd_dai_stream *io)
-{
-	struct rsnd_mod *mod, *n;
-	int ret = 0;
-
-	/*
-	 * remove all mod from rdai
-	 */
-	for_each_rsnd_mod(mod, n, io)
-		ret |= rsnd_dai_disconnect(mod);
-
-	return ret;
-}
-
 /*
  *		Gen2
  */
@@ -229,14 +173,40 @@ static int rsnd_gen2_regmap_init(struct rsnd_priv *priv, struct rsnd_gen *gen)
 		RSND_GEN2_S_REG(gen, SSIU,	SSI_MODE0,	0x800),
 		RSND_GEN2_S_REG(gen, SSIU,	SSI_MODE1,	0x804),
 		/* FIXME: it needs SSI_MODE2/3 in the future */
+		RSND_GEN2_M_REG(gen, SSIU,	SSI_BUSIF_MODE,	0x0,	0x80),
+		RSND_GEN2_M_REG(gen, SSIU,	SSI_BUSIF_ADINR,0x4,	0x80),
+		RSND_GEN2_M_REG(gen, SSIU,	SSI_CTRL,	0x10,	0x80),
 		RSND_GEN2_M_REG(gen, SSIU,	INT_ENABLE,	0x18,	0x80),
 
+		RSND_GEN2_M_REG(gen, SCU,	SRC_BUSIF_MODE,	0x0,	0x20),
+		RSND_GEN2_M_REG(gen, SCU,	SRC_ROUTE_MODE0,0xc,	0x20),
+		RSND_GEN2_M_REG(gen, SCU,	SRC_CTRL,	0x10,	0x20),
+		RSND_GEN2_M_REG(gen, SCU,	SRC_SWRSR,	0x200,	0x40),
+		RSND_GEN2_M_REG(gen, SCU,	SRC_SRCIR,	0x204,	0x40),
+		RSND_GEN2_M_REG(gen, SCU,	SRC_ADINR,	0x214,	0x40),
+		RSND_GEN2_M_REG(gen, SCU,	SRC_IFSCR,	0x21c,	0x40),
+		RSND_GEN2_M_REG(gen, SCU,	SRC_IFSVR,	0x220,	0x40),
+		RSND_GEN2_M_REG(gen, SCU,	SRC_SRCCR,	0x224,	0x40),
+		RSND_GEN2_M_REG(gen, SCU,	SRC_BSDSR,	0x22c,	0x40),
+		RSND_GEN2_M_REG(gen, SCU,	SRC_BSISR,	0x238,	0x40),
+
 		RSND_GEN2_S_REG(gen, ADG,	BRRA,		0x00),
 		RSND_GEN2_S_REG(gen, ADG,	BRRB,		0x04),
 		RSND_GEN2_S_REG(gen, ADG,	SSICKR,		0x08),
 		RSND_GEN2_S_REG(gen, ADG,	AUDIO_CLK_SEL0,	0x0c),
 		RSND_GEN2_S_REG(gen, ADG,	AUDIO_CLK_SEL1,	0x10),
 		RSND_GEN2_S_REG(gen, ADG,	AUDIO_CLK_SEL2,	0x14),
+		RSND_GEN2_S_REG(gen, ADG,	DIV_EN,		0x30),
+		RSND_GEN2_S_REG(gen, ADG,	SRCIN_TIMSEL0,	0x34),
+		RSND_GEN2_S_REG(gen, ADG,	SRCIN_TIMSEL1,	0x38),
+		RSND_GEN2_S_REG(gen, ADG,	SRCIN_TIMSEL2,	0x3c),
+		RSND_GEN2_S_REG(gen, ADG,	SRCIN_TIMSEL3,	0x40),
+		RSND_GEN2_S_REG(gen, ADG,	SRCIN_TIMSEL4,	0x44),
+		RSND_GEN2_S_REG(gen, ADG,	SRCOUT_TIMSEL0,	0x48),
+		RSND_GEN2_S_REG(gen, ADG,	SRCOUT_TIMSEL1,	0x4c),
+		RSND_GEN2_S_REG(gen, ADG,	SRCOUT_TIMSEL2,	0x50),
+		RSND_GEN2_S_REG(gen, ADG,	SRCOUT_TIMSEL3,	0x54),
+		RSND_GEN2_S_REG(gen, ADG,	SRCOUT_TIMSEL4,	0x58),
 
 		RSND_GEN2_M_REG(gen, SSI,	SSICR,		0x00,	0x40),
 		RSND_GEN2_M_REG(gen, SSI,	SSISR,		0x04,	0x40),
@@ -249,7 +219,6 @@ static int rsnd_gen2_regmap_init(struct rsnd_priv *priv, struct rsnd_gen *gen)
 }
 
 static int rsnd_gen2_probe(struct platform_device *pdev,
-			   struct rcar_snd_info *info,
 			   struct rsnd_priv *priv)
 {
 	struct device *dev = rsnd_priv_to_dev(priv);
@@ -283,7 +252,7 @@ static int rsnd_gen2_probe(struct platform_device *pdev,
 		return ret;
 
 	dev_dbg(dev, "Gen2 device probed\n");
-	dev_dbg(dev, "SRU  : %08x => %p\n", scu_res->start,
+	dev_dbg(dev, "SCU  : %08x => %p\n", scu_res->start,
 		gen->base[RSND_GEN2_SCU]);
 	dev_dbg(dev, "ADG  : %08x => %p\n", adg_res->start,
 		gen->base[RSND_GEN2_ADG]);
@@ -317,7 +286,7 @@ static int rsnd_gen1_regmap_init(struct rsnd_priv *priv, struct rsnd_gen *gen)
 		RSND_GEN1_S_REG(gen, SRU,	SRC_ROUTE_CTRL,	0xc0),
 		RSND_GEN1_S_REG(gen, SRU,	SSI_MODE0,	0xD0),
 		RSND_GEN1_S_REG(gen, SRU,	SSI_MODE1,	0xD4),
-		RSND_GEN1_M_REG(gen, SRU,	BUSIF_MODE,	0x20,	0x4),
+		RSND_GEN1_M_REG(gen, SRU,	SRC_BUSIF_MODE,	0x20,	0x4),
 		RSND_GEN1_M_REG(gen, SRU,	SRC_ROUTE_MODE0,0x50,	0x8),
 		RSND_GEN1_M_REG(gen, SRU,	SRC_SWRSR,	0x200,	0x40),
 		RSND_GEN1_M_REG(gen, SRU,	SRC_SRCIR,	0x204,	0x40),
@@ -347,7 +316,6 @@ static int rsnd_gen1_regmap_init(struct rsnd_priv *priv, struct rsnd_gen *gen)
 }
 
 static int rsnd_gen1_probe(struct platform_device *pdev,
-			   struct rcar_snd_info *info,
 			   struct rsnd_priv *priv)
 {
 	struct device *dev = rsnd_priv_to_dev(priv);
@@ -392,7 +360,6 @@ static int rsnd_gen1_probe(struct platform_device *pdev,
  *		Gen
  */
 int rsnd_gen_probe(struct platform_device *pdev,
-		   struct rcar_snd_info *info,
 		   struct rsnd_priv *priv)
 {
 	struct device *dev = rsnd_priv_to_dev(priv);
@@ -409,17 +376,12 @@ int rsnd_gen_probe(struct platform_device *pdev,
 
 	ret = -ENODEV;
 	if (rsnd_is_gen1(priv))
-		ret = rsnd_gen1_probe(pdev, info, priv);
+		ret = rsnd_gen1_probe(pdev, priv);
 	else if (rsnd_is_gen2(priv))
-		ret = rsnd_gen2_probe(pdev, info, priv);
+		ret = rsnd_gen2_probe(pdev, priv);
 
 	if (ret < 0)
 		dev_err(dev, "unknown generation R-Car sound device\n");
 
 	return ret;
 }
-
-void rsnd_gen_remove(struct platform_device *pdev,
-		     struct rsnd_priv *priv)
-{
-}
diff --git a/sound/soc/sh/rcar/rsnd.h b/sound/soc/sh/rcar/rsnd.h
index 4ca66cd899c8..c46e0afa54ae 100644
--- a/sound/soc/sh/rcar/rsnd.h
+++ b/sound/soc/sh/rcar/rsnd.h
@@ -32,15 +32,9 @@
  */
 enum rsnd_reg {
 	/* SRU/SCU/SSIU */
-	RSND_REG_SRC_ROUTE_SEL,		/* for Gen1 */
-	RSND_REG_SRC_TMG_SEL0,		/* for Gen1 */
-	RSND_REG_SRC_TMG_SEL1,		/* for Gen1 */
-	RSND_REG_SRC_TMG_SEL2,		/* for Gen1 */
-	RSND_REG_SRC_ROUTE_CTRL,	/* for Gen1 */
 	RSND_REG_SSI_MODE0,
 	RSND_REG_SSI_MODE1,
-	RSND_REG_BUSIF_MODE,
-	RSND_REG_INT_ENABLE,		/* for Gen2 */
+	RSND_REG_SRC_BUSIF_MODE,
 	RSND_REG_SRC_ROUTE_MODE0,
 	RSND_REG_SRC_SWRSR,
 	RSND_REG_SRC_SRCIR,
@@ -48,7 +42,6 @@ enum rsnd_reg {
 	RSND_REG_SRC_IFSCR,
 	RSND_REG_SRC_IFSVR,
 	RSND_REG_SRC_SRCCR,
-	RSND_REG_SRC_MNFSR,
 
 	/* ADG */
 	RSND_REG_BRRA,
@@ -56,10 +49,6 @@ enum rsnd_reg {
 	RSND_REG_SSICKR,
 	RSND_REG_AUDIO_CLK_SEL0,
 	RSND_REG_AUDIO_CLK_SEL1,
-	RSND_REG_AUDIO_CLK_SEL2,
-	RSND_REG_AUDIO_CLK_SEL3,	/* for Gen1 */
-	RSND_REG_AUDIO_CLK_SEL4,	/* for Gen1 */
-	RSND_REG_AUDIO_CLK_SEL5,	/* for Gen1 */
 
 	/* SSI */
 	RSND_REG_SSICR,
@@ -68,9 +57,62 @@ enum rsnd_reg {
 	RSND_REG_SSIRDR,
 	RSND_REG_SSIWSR,
 
+	/* SHARE see below */
+	RSND_REG_SHARE01,
+	RSND_REG_SHARE02,
+	RSND_REG_SHARE03,
+	RSND_REG_SHARE04,
+	RSND_REG_SHARE05,
+	RSND_REG_SHARE06,
+	RSND_REG_SHARE07,
+	RSND_REG_SHARE08,
+	RSND_REG_SHARE09,
+	RSND_REG_SHARE10,
+	RSND_REG_SHARE11,
+	RSND_REG_SHARE12,
+	RSND_REG_SHARE13,
+	RSND_REG_SHARE14,
+	RSND_REG_SHARE15,
+	RSND_REG_SHARE16,
+	RSND_REG_SHARE17,
+	RSND_REG_SHARE18,
+	RSND_REG_SHARE19,
+
 	RSND_REG_MAX,
 };
 
+/* Gen1 only */
+#define RSND_REG_SRC_ROUTE_SEL		RSND_REG_SHARE01
+#define RSND_REG_SRC_TMG_SEL0		RSND_REG_SHARE02
+#define RSND_REG_SRC_TMG_SEL1		RSND_REG_SHARE03
+#define RSND_REG_SRC_TMG_SEL2		RSND_REG_SHARE04
+#define RSND_REG_SRC_ROUTE_CTRL		RSND_REG_SHARE05
+#define RSND_REG_SRC_MNFSR		RSND_REG_SHARE06
+#define RSND_REG_AUDIO_CLK_SEL3		RSND_REG_SHARE07
+#define RSND_REG_AUDIO_CLK_SEL4		RSND_REG_SHARE08
+#define RSND_REG_AUDIO_CLK_SEL5		RSND_REG_SHARE09
+
+/* Gen2 only */
+#define RSND_REG_SRC_CTRL		RSND_REG_SHARE01
+#define RSND_REG_SSI_CTRL		RSND_REG_SHARE02
+#define RSND_REG_SSI_BUSIF_MODE		RSND_REG_SHARE03
+#define RSND_REG_SSI_BUSIF_ADINR	RSND_REG_SHARE04
+#define RSND_REG_INT_ENABLE		RSND_REG_SHARE05
+#define RSND_REG_SRC_BSDSR		RSND_REG_SHARE06
+#define RSND_REG_SRC_BSISR		RSND_REG_SHARE07
+#define RSND_REG_DIV_EN			RSND_REG_SHARE08
+#define RSND_REG_SRCIN_TIMSEL0		RSND_REG_SHARE09
+#define RSND_REG_SRCIN_TIMSEL1		RSND_REG_SHARE10
+#define RSND_REG_SRCIN_TIMSEL2		RSND_REG_SHARE11
+#define RSND_REG_SRCIN_TIMSEL3		RSND_REG_SHARE12
+#define RSND_REG_SRCIN_TIMSEL4		RSND_REG_SHARE13
+#define RSND_REG_SRCOUT_TIMSEL0		RSND_REG_SHARE14
+#define RSND_REG_SRCOUT_TIMSEL1		RSND_REG_SHARE15
+#define RSND_REG_SRCOUT_TIMSEL2		RSND_REG_SHARE16
+#define RSND_REG_SRCOUT_TIMSEL3		RSND_REG_SHARE17
+#define RSND_REG_SRCOUT_TIMSEL4		RSND_REG_SHARE18
+#define RSND_REG_AUDIO_CLK_SEL2		RSND_REG_SHARE19
+
 struct rsnd_priv;
 struct rsnd_mod;
 struct rsnd_dai;
@@ -96,24 +138,20 @@ void rsnd_bset(struct rsnd_priv *priv, struct rsnd_mod *mod, enum rsnd_reg reg,
  *	R-Car DMA
  */
 struct rsnd_dma {
-	struct rsnd_priv	*priv;
 	struct sh_dmae_slave	slave;
 	struct work_struct	work;
 	struct dma_chan		*chan;
 	enum dma_data_direction dir;
-	int (*inquiry)(struct rsnd_dma *dma, dma_addr_t *buf, int *len);
-	int (*complete)(struct rsnd_dma *dma);
 
 	int submit_loop;
+	int offset; /* it cares A/B plane */
 };
 
 void rsnd_dma_start(struct rsnd_dma *dma);
 void rsnd_dma_stop(struct rsnd_dma *dma);
 int rsnd_dma_available(struct rsnd_dma *dma);
 int rsnd_dma_init(struct rsnd_priv *priv, struct rsnd_dma *dma,
-	int is_play, int id,
-	int (*inquiry)(struct rsnd_dma *dma, dma_addr_t *buf, int *len),
-	int (*complete)(struct rsnd_dma *dma));
+	int is_play, int id);
 void  rsnd_dma_quit(struct rsnd_priv *priv,
 		    struct rsnd_dma *dma);
 
@@ -121,9 +159,20 @@ void  rsnd_dma_quit(struct rsnd_priv *priv,
 /*
  *	R-Car sound mod
  */
+enum rsnd_mod_type {
+	RSND_MOD_SRC = 0,
+	RSND_MOD_SSI,
+	RSND_MOD_MAX,
+};
 
 struct rsnd_mod_ops {
 	char *name;
+	int (*probe)(struct rsnd_mod *mod,
+		     struct rsnd_dai *rdai,
+		     struct rsnd_dai_stream *io);
+	int (*remove)(struct rsnd_mod *mod,
+		      struct rsnd_dai *rdai,
+		      struct rsnd_dai_stream *io);
 	int (*init)(struct rsnd_mod *mod,
 		    struct rsnd_dai *rdai,
 		    struct rsnd_dai_stream *io);
@@ -138,28 +187,26 @@ struct rsnd_mod_ops {
 		    struct rsnd_dai_stream *io);
 };
 
+struct rsnd_dai_stream;
 struct rsnd_mod {
 	int id;
+	enum rsnd_mod_type type;
 	struct rsnd_priv *priv;
 	struct rsnd_mod_ops *ops;
-	struct list_head list; /* connect to rsnd_dai playback/capture */
 	struct rsnd_dma dma;
+	struct rsnd_dai_stream *io;
 };
 
 #define rsnd_mod_to_priv(mod) ((mod)->priv)
 #define rsnd_mod_to_dma(mod) (&(mod)->dma)
 #define rsnd_dma_to_mod(_dma) container_of((_dma), struct rsnd_mod, dma)
+#define rsnd_mod_to_io(mod) ((mod)->io)
 #define rsnd_mod_id(mod) ((mod)->id)
-#define for_each_rsnd_mod(pos, n, io)	\
-	list_for_each_entry_safe(pos, n, &(io)->head, list)
-#define rsnd_mod_call(mod, func, rdai, io)	\
-	(!(mod) ? -ENODEV :			\
-	 !((mod)->ops->func) ? 0 :		\
-	 (mod)->ops->func(mod, rdai, io))
 
 void rsnd_mod_init(struct rsnd_priv *priv,
 		   struct rsnd_mod *mod,
 		   struct rsnd_mod_ops *ops,
+		   enum rsnd_mod_type type,
 		   int id);
 char *rsnd_mod_name(struct rsnd_mod *mod);
 
@@ -168,13 +215,16 @@ char *rsnd_mod_name(struct rsnd_mod *mod);
  */
 #define RSND_DAI_NAME_SIZE	16
 struct rsnd_dai_stream {
-	struct list_head head; /* head of rsnd_mod list */
 	struct snd_pcm_substream *substream;
+	struct rsnd_mod *mod[RSND_MOD_MAX];
+	struct rsnd_dai_path_info *info; /* rcar_snd.h */
 	int byte_pos;
 	int period_pos;
 	int byte_per_period;
 	int next_period_byte;
 };
+#define rsnd_io_to_mod_ssi(io)	((io)->mod[RSND_MOD_SSI])
+#define rsnd_io_to_mod_src(io)	((io)->mod[RSND_MOD_SRC])
 
 struct rsnd_dai {
 	char name[RSND_DAI_NAME_SIZE];
@@ -189,16 +239,14 @@ struct rsnd_dai {
 	unsigned int data_alignment:1;
 };
 
-#define rsnd_dai_nr(priv) ((priv)->dai_nr)
+#define rsnd_rdai_nr(priv) ((priv)->rdai_nr)
 #define for_each_rsnd_dai(rdai, priv, i)		\
-	for (i = 0, (rdai) = rsnd_dai_get(priv, i);	\
-	     i < rsnd_dai_nr(priv);			\
-	     i++, (rdai) = rsnd_dai_get(priv, i))
+	for (i = 0;					\
+	     (i < rsnd_rdai_nr(priv)) &&		\
+	     ((rdai) = rsnd_dai_get(priv, i));		\
+	     i++)
 
 struct rsnd_dai *rsnd_dai_get(struct rsnd_priv *priv, int id);
-int rsnd_dai_disconnect(struct rsnd_mod *mod);
-int rsnd_dai_connect(struct rsnd_dai *rdai, struct rsnd_mod *mod,
-		     struct rsnd_dai_stream *io);
 int rsnd_dai_is_play(struct rsnd_dai *rdai, struct rsnd_dai_stream *io);
 int rsnd_dai_id(struct rsnd_priv *priv, struct rsnd_dai *rdai);
 #define rsnd_dai_get_platform_info(rdai) ((rdai)->info)
@@ -206,21 +254,13 @@ int rsnd_dai_id(struct rsnd_priv *priv, struct rsnd_dai *rdai);
 
 void rsnd_dai_pointer_update(struct rsnd_dai_stream *io, int cnt);
 int rsnd_dai_pointer_offset(struct rsnd_dai_stream *io, int additional);
+#define rsnd_dai_is_clk_master(rdai) ((rdai)->clk_master)
 
 /*
  *	R-Car Gen1/Gen2
  */
 int rsnd_gen_probe(struct platform_device *pdev,
-		   struct rcar_snd_info *info,
 		   struct rsnd_priv *priv);
-void rsnd_gen_remove(struct platform_device *pdev,
-		     struct rsnd_priv *priv);
-int rsnd_gen_path_init(struct rsnd_priv *priv,
-		       struct rsnd_dai *rdai,
-		       struct rsnd_dai_stream *io);
-int rsnd_gen_path_exit(struct rsnd_priv *priv,
-		       struct rsnd_dai *rdai,
-		       struct rsnd_dai_stream *io);
 void __iomem *rsnd_gen_reg_get(struct rsnd_priv *priv,
 			       struct rsnd_mod *mod,
 			       enum rsnd_reg reg);
@@ -233,14 +273,19 @@ void __iomem *rsnd_gen_reg_get(struct rsnd_priv *priv,
 int rsnd_adg_ssi_clk_stop(struct rsnd_mod *mod);
 int rsnd_adg_ssi_clk_try_start(struct rsnd_mod *mod, unsigned int rate);
 int rsnd_adg_probe(struct platform_device *pdev,
-		   struct rcar_snd_info *info,
-		   struct rsnd_priv *priv);
-void rsnd_adg_remove(struct platform_device *pdev,
 		   struct rsnd_priv *priv);
-int rsnd_adg_set_convert_clk(struct rsnd_priv *priv,
-			     struct rsnd_mod *mod,
-			     unsigned int src_rate,
-			     unsigned int dst_rate);
+int rsnd_adg_set_convert_clk_gen1(struct rsnd_priv *priv,
+				  struct rsnd_mod *mod,
+				  unsigned int src_rate,
+				  unsigned int dst_rate);
+int rsnd_adg_set_convert_clk_gen2(struct rsnd_mod *mod,
+				  struct rsnd_dai *rdai,
+				  struct rsnd_dai_stream *io,
+				  unsigned int src_rate,
+				  unsigned int dst_rate);
+int rsnd_adg_set_convert_timing_gen2(struct rsnd_mod *mod,
+				     struct rsnd_dai *rdai,
+				     struct rsnd_dai_stream *io);
 
 /*
  *	R-Car sound priv
@@ -257,10 +302,10 @@ struct rsnd_priv {
 	void *gen;
 
 	/*
-	 * below value will be filled on rsnd_scu_probe()
+	 * below value will be filled on rsnd_src_probe()
 	 */
-	void *scu;
-	int scu_nr;
+	void *src;
+	int src_nr;
 
 	/*
 	 * below value will be filled on rsnd_adg_probe()
@@ -270,46 +315,62 @@ struct rsnd_priv {
 	/*
 	 * below value will be filled on rsnd_ssi_probe()
 	 */
-	void *ssiu;
+	void *ssi;
+	int ssi_nr;
 
 	/*
 	 * below value will be filled on rsnd_dai_probe()
 	 */
 	struct snd_soc_dai_driver *daidrv;
 	struct rsnd_dai *rdai;
-	int dai_nr;
+	int rdai_nr;
 };
 
 #define rsnd_priv_to_dev(priv)	((priv)->dev)
+#define rsnd_priv_to_info(priv)	((priv)->info)
 #define rsnd_lock(priv, flags) spin_lock_irqsave(&priv->lock, flags)
 #define rsnd_unlock(priv, flags) spin_unlock_irqrestore(&priv->lock, flags)
 
+#define rsnd_info_is_playback(priv, type)				\
+({									\
+	struct rcar_snd_info *info = rsnd_priv_to_info(priv);		\
+	int i, is_play = 0;						\
+	for (i = 0; i < info->dai_info_nr; i++) {			\
+		if (info->dai_info[i].playback.type == (type)->info) {	\
+			is_play = 1;					\
+			break;						\
+		}							\
+	}								\
+	is_play;							\
+})
+
 /*
- *	R-Car SCU
+ *	R-Car SRC
  */
-int rsnd_scu_probe(struct platform_device *pdev,
-		   struct rcar_snd_info *info,
+int rsnd_src_probe(struct platform_device *pdev,
 		   struct rsnd_priv *priv);
-void rsnd_scu_remove(struct platform_device *pdev,
-		     struct rsnd_priv *priv);
-struct rsnd_mod *rsnd_scu_mod_get(struct rsnd_priv *priv, int id);
-bool rsnd_scu_hpbif_is_enable(struct rsnd_mod *mod);
-unsigned int rsnd_scu_get_ssi_rate(struct rsnd_priv *priv,
-				   struct rsnd_mod *ssi_mod,
+struct rsnd_mod *rsnd_src_mod_get(struct rsnd_priv *priv, int id);
+unsigned int rsnd_src_get_ssi_rate(struct rsnd_priv *priv,
+				   struct rsnd_dai_stream *io,
 				   struct snd_pcm_runtime *runtime);
+int rsnd_src_ssi_mode_init(struct rsnd_mod *ssi_mod,
+			   struct rsnd_dai *rdai,
+			   struct rsnd_dai_stream *io);
+int rsnd_src_enable_ssi_irq(struct rsnd_mod *ssi_mod,
+			    struct rsnd_dai *rdai,
+			    struct rsnd_dai_stream *io);
 
-#define rsnd_scu_nr(priv) ((priv)->scu_nr)
+#define rsnd_src_nr(priv) ((priv)->src_nr)
 
 /*
  *	R-Car SSI
  */
 int rsnd_ssi_probe(struct platform_device *pdev,
-		   struct rcar_snd_info *info,
-		   struct rsnd_priv *priv);
-void rsnd_ssi_remove(struct platform_device *pdev,
 		   struct rsnd_priv *priv);
 struct rsnd_mod *rsnd_ssi_mod_get(struct rsnd_priv *priv, int id);
 struct rsnd_mod *rsnd_ssi_mod_get_frm_dai(struct rsnd_priv *priv,
 					  int dai_id, int is_play);
+int rsnd_ssi_is_pin_sharing(struct rsnd_mod *mod);
+int rsnd_ssi_is_play(struct rsnd_mod *mod);
 
 #endif
diff --git a/sound/soc/sh/rcar/scu.c b/sound/soc/sh/rcar/scu.c
deleted file mode 100644
index 9bb08bb1d455..000000000000
--- a/sound/soc/sh/rcar/scu.c
+++ /dev/null
@@ -1,384 +0,0 @@
-/*
- * Renesas R-Car SCU support
- *
- * Copyright (C) 2013 Renesas Solutions Corp.
- * Kuninori Morimoto <kuninori.morimoto.gx@renesas.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 "rsnd.h"
-
-struct rsnd_scu {
-	struct rsnd_scu_platform_info *info; /* rcar_snd.h */
-	struct rsnd_mod mod;
-	struct clk *clk;
-};
-
-#define rsnd_scu_mode_flags(p) ((p)->info->flags)
-#define rsnd_scu_convert_rate(p) ((p)->info->convert_rate)
-
-#define RSND_SCU_NAME_SIZE 16
-
-/*
- * ADINR
- */
-#define OTBL_24		(0 << 16)
-#define OTBL_22		(2 << 16)
-#define OTBL_20		(4 << 16)
-#define OTBL_18		(6 << 16)
-#define OTBL_16		(8 << 16)
-
-/*
- *		image of SRC (Sampling Rate Converter)
- *
- * 96kHz   <-> +-----+	48kHz	+-----+	 48kHz	+-------+
- * 48kHz   <-> | SRC | <------>	| SSI |	<----->	| codec |
- * 44.1kHz <-> +-----+		+-----+		+-------+
- * ...
- *
- */
-
-#define rsnd_mod_to_scu(_mod)	\
-	container_of((_mod), struct rsnd_scu, mod)
-
-#define for_each_rsnd_scu(pos, priv, i)					\
-	for ((i) = 0;							\
-	     ((i) < rsnd_scu_nr(priv)) &&				\
-		     ((pos) = (struct rsnd_scu *)(priv)->scu + i);	\
-	     i++)
-
-/* Gen1 only */
-static int rsnd_src_set_route_if_gen1(struct rsnd_priv *priv,
-			      struct rsnd_mod *mod,
-			      struct rsnd_dai *rdai,
-			      struct rsnd_dai_stream *io)
-{
-	struct scu_route_config {
-		u32 mask;
-		int shift;
-	} routes[] = {
-		{ 0xF,  0, }, /* 0 */
-		{ 0xF,  4, }, /* 1 */
-		{ 0xF,  8, }, /* 2 */
-		{ 0x7, 12, }, /* 3 */
-		{ 0x7, 16, }, /* 4 */
-		{ 0x7, 20, }, /* 5 */
-		{ 0x7, 24, }, /* 6 */
-		{ 0x3, 28, }, /* 7 */
-		{ 0x3, 30, }, /* 8 */
-	};
-	struct rsnd_scu *scu = rsnd_mod_to_scu(mod);
-	u32 mask;
-	u32 val;
-	int shift;
-	int id;
-
-	/*
-	 * Gen1 only
-	 */
-	if (!rsnd_is_gen1(priv))
-		return 0;
-
-	id = rsnd_mod_id(mod);
-	if (id < 0 || id >= ARRAY_SIZE(routes))
-		return -EIO;
-
-	/*
-	 * SRC_ROUTE_SELECT
-	 */
-	val = rsnd_dai_is_play(rdai, io) ? 0x1 : 0x2;
-	val = val		<< routes[id].shift;
-	mask = routes[id].mask	<< routes[id].shift;
-
-	rsnd_mod_bset(mod, SRC_ROUTE_SEL, mask, val);
-
-	/*
-	 * SRC_TIMING_SELECT
-	 */
-	shift	= (id % 4) * 8;
-	mask	= 0x1F << shift;
-
-	/*
-	 * ADG is used as source clock if SRC was used,
-	 * then, SSI WS is used as destination clock.
-	 * SSI WS is used as source clock if SRC is not used
-	 * (when playback, source/destination become reverse when capture)
-	 */
-	if (rsnd_scu_convert_rate(scu))	/* use ADG */
-		val = 0;
-	else if (8 == id)		/* use SSI WS, but SRU8 is special */
-		val = id << shift;
-	else				/* use SSI WS */
-		val = (id + 1) << shift;
-
-	switch (id / 4) {
-	case 0:
-		rsnd_mod_bset(mod, SRC_TMG_SEL0, mask, val);
-		break;
-	case 1:
-		rsnd_mod_bset(mod, SRC_TMG_SEL1, mask, val);
-		break;
-	case 2:
-		rsnd_mod_bset(mod, SRC_TMG_SEL2, mask, val);
-		break;
-	}
-
-	return 0;
-}
-
-unsigned int rsnd_scu_get_ssi_rate(struct rsnd_priv *priv,
-				   struct rsnd_mod *ssi_mod,
-				   struct snd_pcm_runtime *runtime)
-{
-	struct rsnd_scu *scu;
-	unsigned int rate;
-
-	/* this function is assuming SSI id = SCU id here */
-	scu = rsnd_mod_to_scu(rsnd_scu_mod_get(priv, rsnd_mod_id(ssi_mod)));
-
-	/*
-	 * return convert rate if SRC is used,
-	 * otherwise, return runtime->rate as usual
-	 */
-	rate = rsnd_scu_convert_rate(scu);
-	if (!rate)
-		rate = runtime->rate;
-
-	return rate;
-}
-
-static int rsnd_scu_convert_rate_ctrl(struct rsnd_priv *priv,
-			      struct rsnd_mod *mod,
-			      struct rsnd_dai *rdai,
-			      struct rsnd_dai_stream *io)
-{
-	struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io);
-	struct rsnd_scu *scu = rsnd_mod_to_scu(mod);
-	u32 convert_rate = rsnd_scu_convert_rate(scu);
-	u32 adinr = runtime->channels;
-
-	/* set/clear soft reset */
-	rsnd_mod_write(mod, SRC_SWRSR, 0);
-	rsnd_mod_write(mod, SRC_SWRSR, 1);
-
-	/* Initialize the operation of the SRC internal circuits */
-	rsnd_mod_write(mod, SRC_SRCIR, 1);
-
-	/* Set channel number and output bit length */
-	switch (runtime->sample_bits) {
-	case 16:
-		adinr |= OTBL_16;
-		break;
-	case 32:
-		adinr |= OTBL_24;
-		break;
-	default:
-		return -EIO;
-	}
-	rsnd_mod_write(mod, SRC_ADINR, adinr);
-
-	if (convert_rate) {
-		u32 fsrate = 0x0400000 / convert_rate * runtime->rate;
-		int ret;
-
-		/* Enable the initial value of IFS */
-		rsnd_mod_write(mod, SRC_IFSCR, 1);
-
-		/* Set initial value of IFS */
-		rsnd_mod_write(mod, SRC_IFSVR, fsrate);
-
-		/* Select SRC mode (fixed value) */
-		rsnd_mod_write(mod, SRC_SRCCR, 0x00010110);
-
-		/* Set the restriction value of the FS ratio (98%) */
-		rsnd_mod_write(mod, SRC_MNFSR, fsrate / 100 * 98);
-
-		if (rsnd_is_gen1(priv)) {
-			/* no SRC_BFSSR settings, since SRC_SRCCR::BUFMD is 0 */
-		}
-
-		/* set convert clock */
-		ret = rsnd_adg_set_convert_clk(priv, mod,
-					       runtime->rate,
-					       convert_rate);
-		if (ret < 0)
-			return ret;
-	}
-
-	/* Cancel the initialization and operate the SRC function */
-	rsnd_mod_write(mod, SRC_SRCIR, 0);
-
-	/* use DMA transfer */
-	rsnd_mod_write(mod, BUSIF_MODE, 1);
-
-	return 0;
-}
-
-static int rsnd_scu_transfer_start(struct rsnd_priv *priv,
-				   struct rsnd_mod *mod,
-				   struct rsnd_dai *rdai,
-				   struct rsnd_dai_stream *io)
-{
-	struct rsnd_scu *scu = rsnd_mod_to_scu(mod);
-	int id = rsnd_mod_id(mod);
-	u32 val;
-
-	if (rsnd_is_gen1(priv)) {
-		val = (1 << id);
-		rsnd_mod_bset(mod, SRC_ROUTE_CTRL, val, val);
-	}
-
-	if (rsnd_scu_convert_rate(scu))
-		rsnd_mod_write(mod, SRC_ROUTE_MODE0, 1);
-
-	return 0;
-}
-
-static int rsnd_scu_transfer_stop(struct rsnd_priv *priv,
-				  struct rsnd_mod *mod,
-				  struct rsnd_dai *rdai,
-				  struct rsnd_dai_stream *io)
-{
-	struct rsnd_scu *scu = rsnd_mod_to_scu(mod);
-	int id = rsnd_mod_id(mod);
-	u32 mask;
-
-	if (rsnd_is_gen1(priv)) {
-		mask = (1 << id);
-		rsnd_mod_bset(mod, SRC_ROUTE_CTRL, mask, 0);
-	}
-
-	if (rsnd_scu_convert_rate(scu))
-		rsnd_mod_write(mod, SRC_ROUTE_MODE0, 0);
-
-	return 0;
-}
-
-bool rsnd_scu_hpbif_is_enable(struct rsnd_mod *mod)
-{
-	struct rsnd_scu *scu = rsnd_mod_to_scu(mod);
-	u32 flags = rsnd_scu_mode_flags(scu);
-
-	return !!(flags & RSND_SCU_USE_HPBIF);
-}
-
-static int rsnd_scu_start(struct rsnd_mod *mod,
-			  struct rsnd_dai *rdai,
-			  struct rsnd_dai_stream *io)
-{
-	struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
-	struct rsnd_scu *scu = rsnd_mod_to_scu(mod);
-	struct device *dev = rsnd_priv_to_dev(priv);
-	int ret;
-
-	/*
-	 * SCU will be used if it has RSND_SCU_USE_HPBIF flags
-	 */
-	if (!rsnd_scu_hpbif_is_enable(mod)) {
-		/* it use PIO transter */
-		dev_dbg(dev, "%s%d is not used\n",
-			rsnd_mod_name(mod), rsnd_mod_id(mod));
-
-		return 0;
-	}
-
-	clk_enable(scu->clk);
-
-	/* it use DMA transter */
-
-	ret = rsnd_src_set_route_if_gen1(priv, mod, rdai, io);
-	if (ret < 0)
-		return ret;
-
-	ret = rsnd_scu_convert_rate_ctrl(priv, mod, rdai, io);
-	if (ret < 0)
-		return ret;
-
-	ret = rsnd_scu_transfer_start(priv, mod, rdai, io);
-	if (ret < 0)
-		return ret;
-
-	dev_dbg(dev, "%s%d start\n", rsnd_mod_name(mod), rsnd_mod_id(mod));
-
-	return 0;
-}
-
-static int rsnd_scu_stop(struct rsnd_mod *mod,
-			  struct rsnd_dai *rdai,
-			  struct rsnd_dai_stream *io)
-{
-	struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
-	struct rsnd_scu *scu = rsnd_mod_to_scu(mod);
-
-	if (!rsnd_scu_hpbif_is_enable(mod))
-		return 0;
-
-	rsnd_scu_transfer_stop(priv, mod, rdai, io);
-
-	clk_disable(scu->clk);
-
-	return 0;
-}
-
-static struct rsnd_mod_ops rsnd_scu_ops = {
-	.name	= "scu",
-	.start	= rsnd_scu_start,
-	.stop	= rsnd_scu_stop,
-};
-
-struct rsnd_mod *rsnd_scu_mod_get(struct rsnd_priv *priv, int id)
-{
-	if (WARN_ON(id < 0 || id >= rsnd_scu_nr(priv)))
-		id = 0;
-
-	return &((struct rsnd_scu *)(priv->scu) + id)->mod;
-}
-
-int rsnd_scu_probe(struct platform_device *pdev,
-		   struct rcar_snd_info *info,
-		   struct rsnd_priv *priv)
-{
-	struct device *dev = rsnd_priv_to_dev(priv);
-	struct rsnd_scu *scu;
-	struct clk *clk;
-	char name[RSND_SCU_NAME_SIZE];
-	int i, nr;
-
-	/*
-	 * init SCU
-	 */
-	nr	= info->scu_info_nr;
-	scu	= devm_kzalloc(dev, sizeof(*scu) * nr, GFP_KERNEL);
-	if (!scu) {
-		dev_err(dev, "SCU allocate failed\n");
-		return -ENOMEM;
-	}
-
-	priv->scu_nr	= nr;
-	priv->scu	= scu;
-
-	for_each_rsnd_scu(scu, priv, i) {
-		snprintf(name, RSND_SCU_NAME_SIZE, "scu.%d", i);
-
-		clk = devm_clk_get(dev, name);
-		if (IS_ERR(clk))
-			return PTR_ERR(clk);
-
-		rsnd_mod_init(priv, &scu->mod,
-			      &rsnd_scu_ops, i);
-		scu->info = &info->scu_info[i];
-		scu->clk = clk;
-
-		dev_dbg(dev, "SCU%d probed\n", i);
-	}
-	dev_dbg(dev, "scu probed\n");
-
-	return 0;
-}
-
-void rsnd_scu_remove(struct platform_device *pdev,
-		     struct rsnd_priv *priv)
-{
-}
diff --git a/sound/soc/sh/rcar/src.c b/sound/soc/sh/rcar/src.c
new file mode 100644
index 000000000000..ea6a214985d0
--- /dev/null
+++ b/sound/soc/sh/rcar/src.c
@@ -0,0 +1,687 @@
+/*
+ * Renesas R-Car SRC support
+ *
+ * Copyright (C) 2013 Renesas Solutions Corp.
+ * Kuninori Morimoto <kuninori.morimoto.gx@renesas.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 "rsnd.h"
+
+struct rsnd_src {
+	struct rsnd_src_platform_info *info; /* rcar_snd.h */
+	struct rsnd_mod mod;
+	struct clk *clk;
+};
+
+#define RSND_SRC_NAME_SIZE 16
+
+/*
+ * ADINR
+ */
+#define OTBL_24		(0 << 16)
+#define OTBL_22		(2 << 16)
+#define OTBL_20		(4 << 16)
+#define OTBL_18		(6 << 16)
+#define OTBL_16		(8 << 16)
+
+#define rsnd_src_mode_flags(p) ((p)->info->flags)
+#define rsnd_src_convert_rate(p) ((p)->info->convert_rate)
+#define rsnd_mod_to_src(_mod)				\
+	container_of((_mod), struct rsnd_src, mod)
+#define rsnd_src_hpbif_is_enable(src)	\
+	(rsnd_src_mode_flags(src) & RSND_SCU_USE_HPBIF)
+#define rsnd_src_dma_available(src) \
+	rsnd_dma_available(rsnd_mod_to_dma(&(src)->mod))
+
+#define for_each_rsnd_src(pos, priv, i)				\
+	for ((i) = 0;						\
+	     ((i) < rsnd_src_nr(priv)) &&			\
+	     ((pos) = (struct rsnd_src *)(priv)->src + i);	\
+	     i++)
+
+
+/*
+ *		image of SRC (Sampling Rate Converter)
+ *
+ * 96kHz   <-> +-----+	48kHz	+-----+	 48kHz	+-------+
+ * 48kHz   <-> | SRC | <------>	| SSI |	<----->	| codec |
+ * 44.1kHz <-> +-----+		+-----+		+-------+
+ * ...
+ *
+ */
+
+/*
+ * src.c is caring...
+ *
+ * Gen1
+ *
+ * [mem] -> [SRU] -> [SSI]
+ *        |--------|
+ *
+ * Gen2
+ *
+ * [mem] -> [SRC] -> [SSIU] -> [SSI]
+ *        |-----------------|
+ */
+
+/*
+ *	How to use SRC bypass mode for debugging
+ *
+ * SRC has bypass mode, and it is useful for debugging.
+ * In Gen2 case,
+ * SRCm_MODE controls whether SRC is used or not
+ * SSI_MODE0 controls whether SSIU which receives SRC data
+ * is used or not.
+ * Both SRCm_MODE/SSI_MODE0 settings are needed if you use SRC,
+ * but SRC bypass mode needs SSI_MODE0 only.
+ *
+ * This driver request
+ * struct rsnd_src_platform_info {
+ *	u32 flags;
+ *	u32 convert_rate;
+ * }
+ *
+ * rsnd_src_hpbif_is_enable() will be true
+ * if flags had RSND_SRC_USE_HPBIF,
+ * and it controls whether SSIU is used or not.
+ *
+ * rsnd_src_convert_rate() indicates
+ * above convert_rate, and it controls
+ * whether SRC is used or not.
+ *
+ * ex) doesn't use SRC
+ * struct rsnd_src_platform_info info = {
+ *	.flags = 0,
+ *	.convert_rate = 0,
+ * };
+ *
+ * ex) uses SRC
+ * struct rsnd_src_platform_info info = {
+ *	.flags = RSND_SRC_USE_HPBIF,
+ *	.convert_rate = 48000,
+ * };
+ *
+ * ex) uses SRC bypass mode
+ * struct rsnd_src_platform_info info = {
+ *	.flags = RSND_SRC_USE_HPBIF,
+ *	.convert_rate = 0,
+ * };
+ *
+ */
+
+/*
+ *		Gen1/Gen2 common functions
+ */
+int rsnd_src_ssi_mode_init(struct rsnd_mod *ssi_mod,
+			   struct rsnd_dai *rdai,
+			   struct rsnd_dai_stream *io)
+{
+	struct rsnd_priv *priv = rsnd_mod_to_priv(ssi_mod);
+	struct rsnd_mod *src_mod = rsnd_io_to_mod_src(io);
+	struct rcar_snd_info *info = rsnd_priv_to_info(priv);
+	int ssi_id = rsnd_mod_id(ssi_mod);
+	int has_src = 0;
+
+	/*
+	 * SSI_MODE0
+	 */
+	if (info->dai_info) {
+		has_src = !!src_mod;
+	} else {
+		struct rsnd_src *src = rsnd_mod_to_src(src_mod);
+		has_src = rsnd_src_hpbif_is_enable(src);
+	}
+
+	rsnd_mod_bset(ssi_mod, SSI_MODE0, (1 << ssi_id),
+		      has_src ? 0 : (1 << ssi_id));
+
+	/*
+	 * SSI_MODE1
+	 */
+	if (rsnd_ssi_is_pin_sharing(ssi_mod)) {
+		int shift = -1;
+		switch (ssi_id) {
+		case 1:
+			shift = 0;
+			break;
+		case 2:
+			shift = 2;
+			break;
+		case 4:
+			shift = 16;
+			break;
+		}
+
+		if (shift >= 0)
+			rsnd_mod_bset(ssi_mod, SSI_MODE1,
+				      0x3 << shift,
+				      rsnd_dai_is_clk_master(rdai) ?
+				      0x2 << shift : 0x1 << shift);
+	}
+
+	return 0;
+}
+
+int rsnd_src_enable_ssi_irq(struct rsnd_mod *ssi_mod,
+			    struct rsnd_dai *rdai,
+			    struct rsnd_dai_stream *io)
+{
+	struct rsnd_priv *priv = rsnd_mod_to_priv(ssi_mod);
+
+	/* enable PIO interrupt if Gen2 */
+	if (rsnd_is_gen2(priv))
+		rsnd_mod_write(ssi_mod, INT_ENABLE, 0x0f000000);
+
+	return 0;
+}
+
+unsigned int rsnd_src_get_ssi_rate(struct rsnd_priv *priv,
+				   struct rsnd_dai_stream *io,
+				   struct snd_pcm_runtime *runtime)
+{
+	struct rsnd_src *src;
+	unsigned int rate;
+
+	src = rsnd_mod_to_src(rsnd_io_to_mod_src(io));
+
+	/*
+	 * return convert rate if SRC is used,
+	 * otherwise, return runtime->rate as usual
+	 */
+	rate = rsnd_src_convert_rate(src);
+	if (!rate)
+		rate = runtime->rate;
+
+	return rate;
+}
+
+static int rsnd_src_set_convert_rate(struct rsnd_mod *mod,
+				     struct rsnd_dai *rdai,
+				     struct rsnd_dai_stream *io)
+{
+	struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io);
+	struct rsnd_src *src = rsnd_mod_to_src(mod);
+	u32 convert_rate = rsnd_src_convert_rate(src);
+	u32 adinr = runtime->channels;
+	u32 fsrate = 0;
+
+	if (convert_rate)
+		fsrate = 0x0400000 / convert_rate * runtime->rate;
+
+	/* set/clear soft reset */
+	rsnd_mod_write(mod, SRC_SWRSR, 0);
+	rsnd_mod_write(mod, SRC_SWRSR, 1);
+
+	/*
+	 * Initialize the operation of the SRC internal circuits
+	 * see rsnd_src_start()
+	 */
+	rsnd_mod_write(mod, SRC_SRCIR, 1);
+
+	/* Set channel number and output bit length */
+	switch (runtime->sample_bits) {
+	case 16:
+		adinr |= OTBL_16;
+		break;
+	case 32:
+		adinr |= OTBL_24;
+		break;
+	default:
+		return -EIO;
+	}
+	rsnd_mod_write(mod, SRC_ADINR, adinr);
+
+	/* Enable the initial value of IFS */
+	if (fsrate) {
+		rsnd_mod_write(mod, SRC_IFSCR, 1);
+
+		/* Set initial value of IFS */
+		rsnd_mod_write(mod, SRC_IFSVR, fsrate);
+	}
+
+	/* use DMA transfer */
+	rsnd_mod_write(mod, SRC_BUSIF_MODE, 1);
+
+	return 0;
+}
+
+static int rsnd_src_init(struct rsnd_mod *mod,
+			 struct rsnd_dai *rdai,
+			 struct rsnd_dai_stream *io)
+{
+	struct rsnd_src *src = rsnd_mod_to_src(mod);
+
+	clk_enable(src->clk);
+
+	return 0;
+}
+
+static int rsnd_src_quit(struct rsnd_mod *mod,
+			 struct rsnd_dai *rdai,
+			 struct rsnd_dai_stream *io)
+{
+	struct rsnd_src *src = rsnd_mod_to_src(mod);
+
+	clk_disable(src->clk);
+
+	return 0;
+}
+
+static int rsnd_src_start(struct rsnd_mod *mod,
+			  struct rsnd_dai *rdai,
+			  struct rsnd_dai_stream *io)
+{
+	struct rsnd_src *src = rsnd_mod_to_src(mod);
+
+	/*
+	 * Cancel the initialization and operate the SRC function
+	 * see rsnd_src_set_convert_rate()
+	 */
+	rsnd_mod_write(mod, SRC_SRCIR, 0);
+
+	if (rsnd_src_convert_rate(src))
+		rsnd_mod_write(mod, SRC_ROUTE_MODE0, 1);
+
+	return 0;
+}
+
+
+static int rsnd_src_stop(struct rsnd_mod *mod,
+			 struct rsnd_dai *rdai,
+			 struct rsnd_dai_stream *io)
+{
+	struct rsnd_src *src = rsnd_mod_to_src(mod);
+
+	if (rsnd_src_convert_rate(src))
+		rsnd_mod_write(mod, SRC_ROUTE_MODE0, 0);
+
+	return 0;
+}
+
+static struct rsnd_mod_ops rsnd_src_non_ops = {
+	.name	= "src (non)",
+};
+
+/*
+ *		Gen1 functions
+ */
+static int rsnd_src_set_route_gen1(struct rsnd_mod *mod,
+				   struct rsnd_dai *rdai,
+				   struct rsnd_dai_stream *io)
+{
+	struct src_route_config {
+		u32 mask;
+		int shift;
+	} routes[] = {
+		{ 0xF,  0, }, /* 0 */
+		{ 0xF,  4, }, /* 1 */
+		{ 0xF,  8, }, /* 2 */
+		{ 0x7, 12, }, /* 3 */
+		{ 0x7, 16, }, /* 4 */
+		{ 0x7, 20, }, /* 5 */
+		{ 0x7, 24, }, /* 6 */
+		{ 0x3, 28, }, /* 7 */
+		{ 0x3, 30, }, /* 8 */
+	};
+	u32 mask;
+	u32 val;
+	int id;
+
+	id = rsnd_mod_id(mod);
+	if (id < 0 || id >= ARRAY_SIZE(routes))
+		return -EIO;
+
+	/*
+	 * SRC_ROUTE_SELECT
+	 */
+	val = rsnd_dai_is_play(rdai, io) ? 0x1 : 0x2;
+	val = val		<< routes[id].shift;
+	mask = routes[id].mask	<< routes[id].shift;
+
+	rsnd_mod_bset(mod, SRC_ROUTE_SEL, mask, val);
+
+	return 0;
+}
+
+static int rsnd_src_set_convert_timing_gen1(struct rsnd_mod *mod,
+					    struct rsnd_dai *rdai,
+					    struct rsnd_dai_stream *io)
+{
+	struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
+	struct rsnd_src *src = rsnd_mod_to_src(mod);
+	struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io);
+	u32 convert_rate = rsnd_src_convert_rate(src);
+	u32 mask;
+	u32 val;
+	int shift;
+	int id = rsnd_mod_id(mod);
+	int ret;
+
+	/*
+	 * SRC_TIMING_SELECT
+	 */
+	shift	= (id % 4) * 8;
+	mask	= 0x1F << shift;
+
+	/*
+	 * ADG is used as source clock if SRC was used,
+	 * then, SSI WS is used as destination clock.
+	 * SSI WS is used as source clock if SRC is not used
+	 * (when playback, source/destination become reverse when capture)
+	 */
+	ret = 0;
+	if (convert_rate) {
+		/* use ADG */
+		val = 0;
+		ret = rsnd_adg_set_convert_clk_gen1(priv, mod,
+						    runtime->rate,
+						    convert_rate);
+	} else if (8 == id) {
+		/* use SSI WS, but SRU8 is special */
+		val = id << shift;
+	} else {
+		/* use SSI WS */
+		val = (id + 1) << shift;
+	}
+
+	if (ret < 0)
+		return ret;
+
+	switch (id / 4) {
+	case 0:
+		rsnd_mod_bset(mod, SRC_TMG_SEL0, mask, val);
+		break;
+	case 1:
+		rsnd_mod_bset(mod, SRC_TMG_SEL1, mask, val);
+		break;
+	case 2:
+		rsnd_mod_bset(mod, SRC_TMG_SEL2, mask, val);
+		break;
+	}
+
+	return 0;
+}
+
+static int rsnd_src_set_convert_rate_gen1(struct rsnd_mod *mod,
+					  struct rsnd_dai *rdai,
+					  struct rsnd_dai_stream *io)
+{
+	int ret;
+
+	ret = rsnd_src_set_convert_rate(mod, rdai, io);
+	if (ret < 0)
+		return ret;
+
+	/* Select SRC mode (fixed value) */
+	rsnd_mod_write(mod, SRC_SRCCR, 0x00010110);
+
+	/* Set the restriction value of the FS ratio (98%) */
+	rsnd_mod_write(mod, SRC_MNFSR,
+		       rsnd_mod_read(mod, SRC_IFSVR) / 100 * 98);
+
+	/* no SRC_BFSSR settings, since SRC_SRCCR::BUFMD is 0 */
+
+	return 0;
+}
+
+static int rsnd_src_init_gen1(struct rsnd_mod *mod,
+			      struct rsnd_dai *rdai,
+			      struct rsnd_dai_stream *io)
+{
+	int ret;
+
+	ret = rsnd_src_init(mod, rdai, io);
+	if (ret < 0)
+		return ret;
+
+	ret = rsnd_src_set_route_gen1(mod, rdai, io);
+	if (ret < 0)
+		return ret;
+
+	ret = rsnd_src_set_convert_rate_gen1(mod, rdai, io);
+	if (ret < 0)
+		return ret;
+
+	ret = rsnd_src_set_convert_timing_gen1(mod, rdai, io);
+	if (ret < 0)
+		return ret;
+
+	return 0;
+}
+
+static int rsnd_src_start_gen1(struct rsnd_mod *mod,
+			       struct rsnd_dai *rdai,
+			       struct rsnd_dai_stream *io)
+{
+	int id = rsnd_mod_id(mod);
+
+	rsnd_mod_bset(mod, SRC_ROUTE_CTRL, (1 << id), (1 << id));
+
+	return rsnd_src_start(mod, rdai, io);
+}
+
+static int rsnd_src_stop_gen1(struct rsnd_mod *mod,
+			      struct rsnd_dai *rdai,
+			      struct rsnd_dai_stream *io)
+{
+	int id = rsnd_mod_id(mod);
+
+	rsnd_mod_bset(mod, SRC_ROUTE_CTRL, (1 << id), 0);
+
+	return rsnd_src_stop(mod, rdai, io);
+}
+
+static struct rsnd_mod_ops rsnd_src_gen1_ops = {
+	.name	= "sru (gen1)",
+	.init	= rsnd_src_init_gen1,
+	.quit	= rsnd_src_quit,
+	.start	= rsnd_src_start_gen1,
+	.stop	= rsnd_src_stop_gen1,
+};
+
+/*
+ *		Gen2 functions
+ */
+static int rsnd_src_set_convert_rate_gen2(struct rsnd_mod *mod,
+					  struct rsnd_dai *rdai,
+					  struct rsnd_dai_stream *io)
+{
+	int ret;
+
+	ret = rsnd_src_set_convert_rate(mod, rdai, io);
+	if (ret < 0)
+		return ret;
+
+	rsnd_mod_write(mod, SSI_BUSIF_ADINR, rsnd_mod_read(mod, SRC_ADINR));
+	rsnd_mod_write(mod, SSI_BUSIF_MODE,  rsnd_mod_read(mod, SRC_BUSIF_MODE));
+
+	rsnd_mod_write(mod, SRC_SRCCR, 0x00011110);
+
+	rsnd_mod_write(mod, SRC_BSDSR, 0x01800000);
+	rsnd_mod_write(mod, SRC_BSISR, 0x00100060);
+
+	return 0;
+}
+
+static int rsnd_src_set_convert_timing_gen2(struct rsnd_mod *mod,
+					    struct rsnd_dai *rdai,
+					    struct rsnd_dai_stream *io)
+{
+	struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io);
+	struct rsnd_src *src = rsnd_mod_to_src(mod);
+	u32 convert_rate = rsnd_src_convert_rate(src);
+	int ret;
+
+	if (convert_rate)
+		ret = rsnd_adg_set_convert_clk_gen2(mod, rdai, io,
+						    runtime->rate,
+						    convert_rate);
+	else
+		ret = rsnd_adg_set_convert_timing_gen2(mod, rdai, io);
+
+	return ret;
+}
+
+static int rsnd_src_probe_gen2(struct rsnd_mod *mod,
+			       struct rsnd_dai *rdai,
+			       struct rsnd_dai_stream *io)
+{
+	struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
+	struct rcar_snd_info *info = rsnd_priv_to_info(priv);
+	struct rsnd_src *src = rsnd_mod_to_src(mod);
+	struct rsnd_mod *ssi = rsnd_ssi_mod_get(priv, rsnd_mod_id(mod));
+	struct device *dev = rsnd_priv_to_dev(priv);
+	int ret;
+	int is_play;
+
+	if (info->dai_info)
+		is_play = rsnd_info_is_playback(priv, src);
+	else
+		is_play = rsnd_ssi_is_play(ssi);
+
+	ret = rsnd_dma_init(priv,
+			    rsnd_mod_to_dma(mod),
+			    is_play,
+			    src->info->dma_id);
+	if (ret < 0)
+		dev_err(dev, "SRC DMA failed\n");
+
+	return ret;
+}
+
+static int rsnd_src_remove_gen2(struct rsnd_mod *mod,
+				struct rsnd_dai *rdai,
+				struct rsnd_dai_stream *io)
+{
+	rsnd_dma_quit(rsnd_mod_to_priv(mod), rsnd_mod_to_dma(mod));
+
+	return 0;
+}
+
+static int rsnd_src_init_gen2(struct rsnd_mod *mod,
+			      struct rsnd_dai *rdai,
+			      struct rsnd_dai_stream *io)
+{
+	int ret;
+
+	ret = rsnd_src_init(mod, rdai, io);
+	if (ret < 0)
+		return ret;
+
+	ret = rsnd_src_set_convert_rate_gen2(mod, rdai, io);
+	if (ret < 0)
+		return ret;
+
+	ret = rsnd_src_set_convert_timing_gen2(mod, rdai, io);
+	if (ret < 0)
+		return ret;
+
+	return 0;
+}
+
+static int rsnd_src_start_gen2(struct rsnd_mod *mod,
+			       struct rsnd_dai *rdai,
+			       struct rsnd_dai_stream *io)
+{
+	struct rsnd_src *src = rsnd_mod_to_src(mod);
+
+	rsnd_dma_start(rsnd_mod_to_dma(&src->mod));
+
+	rsnd_mod_write(mod, SSI_CTRL, 0x1);
+	rsnd_mod_write(mod, SRC_CTRL, 0x11);
+
+	return rsnd_src_start(mod, rdai, io);
+}
+
+static int rsnd_src_stop_gen2(struct rsnd_mod *mod,
+			      struct rsnd_dai *rdai,
+			      struct rsnd_dai_stream *io)
+{
+	struct rsnd_src *src = rsnd_mod_to_src(mod);
+
+	rsnd_mod_write(mod, SSI_CTRL, 0);
+	rsnd_mod_write(mod, SRC_CTRL, 0);
+
+	rsnd_dma_stop(rsnd_mod_to_dma(&src->mod));
+
+	return rsnd_src_stop(mod, rdai, io);
+}
+
+static struct rsnd_mod_ops rsnd_src_gen2_ops = {
+	.name	= "src (gen2)",
+	.probe	= rsnd_src_probe_gen2,
+	.remove	= rsnd_src_remove_gen2,
+	.init	= rsnd_src_init_gen2,
+	.quit	= rsnd_src_quit,
+	.start	= rsnd_src_start_gen2,
+	.stop	= rsnd_src_stop_gen2,
+};
+
+struct rsnd_mod *rsnd_src_mod_get(struct rsnd_priv *priv, int id)
+{
+	if (WARN_ON(id < 0 || id >= rsnd_src_nr(priv)))
+		id = 0;
+
+	return &((struct rsnd_src *)(priv->src) + id)->mod;
+}
+
+int rsnd_src_probe(struct platform_device *pdev,
+		   struct rsnd_priv *priv)
+{
+	struct rcar_snd_info *info = rsnd_priv_to_info(priv);
+	struct device *dev = rsnd_priv_to_dev(priv);
+	struct rsnd_src *src;
+	struct rsnd_mod_ops *ops;
+	struct clk *clk;
+	char name[RSND_SRC_NAME_SIZE];
+	int i, nr;
+
+	/*
+	 * init SRC
+	 */
+	nr	= info->src_info_nr;
+	if (!nr)
+		return 0;
+
+	src	= devm_kzalloc(dev, sizeof(*src) * nr, GFP_KERNEL);
+	if (!src) {
+		dev_err(dev, "SRC allocate failed\n");
+		return -ENOMEM;
+	}
+
+	priv->src_nr	= nr;
+	priv->src	= src;
+
+	for_each_rsnd_src(src, priv, i) {
+		snprintf(name, RSND_SRC_NAME_SIZE, "src.%d", i);
+
+		clk = devm_clk_get(dev, name);
+		if (IS_ERR(clk)) {
+			snprintf(name, RSND_SRC_NAME_SIZE, "scu.%d", i);
+			clk = devm_clk_get(dev, name);
+		}
+
+		if (IS_ERR(clk))
+			return PTR_ERR(clk);
+
+		src->info = &info->src_info[i];
+		src->clk = clk;
+
+		ops = &rsnd_src_non_ops;
+		if (rsnd_src_hpbif_is_enable(src)) {
+			if (rsnd_is_gen1(priv))
+				ops = &rsnd_src_gen1_ops;
+			if (rsnd_is_gen2(priv))
+				ops = &rsnd_src_gen2_ops;
+		}
+
+		rsnd_mod_init(priv, &src->mod, ops, RSND_MOD_SRC, i);
+
+		dev_dbg(dev, "SRC%d probed\n", i);
+	}
+
+	return 0;
+}
diff --git a/sound/soc/sh/rcar/ssi.c b/sound/soc/sh/rcar/ssi.c
index 4b8cf7ca9d19..633b23d209b9 100644
--- a/sound/soc/sh/rcar/ssi.c
+++ b/sound/soc/sh/rcar/ssi.c
@@ -64,108 +64,29 @@ struct rsnd_ssi {
 	struct rsnd_mod mod;
 
 	struct rsnd_dai *rdai;
-	struct rsnd_dai_stream *io;
 	u32 cr_own;
 	u32 cr_clk;
 	u32 cr_etc;
 	int err;
-	int dma_offset;
 	unsigned int usrcnt;
 	unsigned int rate;
 };
 
-struct rsnd_ssiu {
-	u32 ssi_mode0;
-	u32 ssi_mode1;
-
-	int ssi_nr;
-	struct rsnd_ssi *ssi;
-};
-
 #define for_each_rsnd_ssi(pos, priv, i)					\
 	for (i = 0;							\
 	     (i < rsnd_ssi_nr(priv)) &&					\
-		((pos) = ((struct rsnd_ssiu *)((priv)->ssiu))->ssi + i); \
+		((pos) = ((struct rsnd_ssi *)(priv)->ssi + i));		\
 	     i++)
 
-#define rsnd_ssi_nr(priv) (((struct rsnd_ssiu *)((priv)->ssiu))->ssi_nr)
+#define rsnd_ssi_nr(priv) ((priv)->ssi_nr)
 #define rsnd_mod_to_ssi(_mod) container_of((_mod), struct rsnd_ssi, mod)
 #define rsnd_dma_to_ssi(dma)  rsnd_mod_to_ssi(rsnd_dma_to_mod(dma))
 #define rsnd_ssi_pio_available(ssi) ((ssi)->info->pio_irq > 0)
 #define rsnd_ssi_dma_available(ssi) \
 	rsnd_dma_available(rsnd_mod_to_dma(&(ssi)->mod))
 #define rsnd_ssi_clk_from_parent(ssi) ((ssi)->parent)
-#define rsnd_rdai_is_clk_master(rdai) ((rdai)->clk_master)
 #define rsnd_ssi_mode_flags(p) ((p)->info->flags)
 #define rsnd_ssi_dai_id(ssi) ((ssi)->info->dai_id)
-#define rsnd_ssi_to_ssiu(ssi)\
-	(((struct rsnd_ssiu *)((ssi) - rsnd_mod_id(&(ssi)->mod))) - 1)
-
-static void rsnd_ssi_mode_set(struct rsnd_priv *priv,
-			      struct rsnd_dai *rdai,
-			      struct rsnd_ssi *ssi)
-{
-	struct device *dev = rsnd_priv_to_dev(priv);
-	struct rsnd_mod *scu;
-	struct rsnd_ssiu *ssiu = rsnd_ssi_to_ssiu(ssi);
-	int id = rsnd_mod_id(&ssi->mod);
-	u32 flags;
-	u32 val;
-
-	scu   = rsnd_scu_mod_get(priv, rsnd_mod_id(&ssi->mod));
-
-	/*
-	 * SSI_MODE0
-	 */
-
-	/* see also BUSIF_MODE */
-	if (rsnd_scu_hpbif_is_enable(scu)) {
-		ssiu->ssi_mode0 &= ~(1 << id);
-		dev_dbg(dev, "SSI%d uses DEPENDENT mode\n", id);
-	} else {
-		ssiu->ssi_mode0 |= (1 << id);
-		dev_dbg(dev, "SSI%d uses INDEPENDENT mode\n", id);
-	}
-
-	/*
-	 * SSI_MODE1
-	 */
-#define ssi_parent_set(p, sync, adg, ext)		\
-	do {						\
-		ssi->parent = ssiu->ssi + p;		\
-		if (rsnd_rdai_is_clk_master(rdai))	\
-			val = adg;			\
-		else					\
-			val = ext;			\
-		if (flags & RSND_SSI_SYNC)		\
-			val |= sync;			\
-	} while (0)
-
-	flags = rsnd_ssi_mode_flags(ssi);
-	if (flags & RSND_SSI_CLK_PIN_SHARE) {
-
-		val = 0;
-		switch (id) {
-		case 1:
-			ssi_parent_set(0, (1 << 4), (0x2 << 0), (0x1 << 0));
-			break;
-		case 2:
-			ssi_parent_set(0, (1 << 4), (0x2 << 2), (0x1 << 2));
-			break;
-		case 4:
-			ssi_parent_set(3, (1 << 20), (0x2 << 16), (0x1 << 16));
-			break;
-		case 8:
-			ssi_parent_set(7, 0, 0, 0);
-			break;
-		}
-
-		ssiu->ssi_mode1 |= val;
-	}
-
-	rsnd_mod_write(&ssi->mod, SSI_MODE0, ssiu->ssi_mode0);
-	rsnd_mod_write(&ssi->mod, SSI_MODE1, ssiu->ssi_mode1);
-}
 
 static void rsnd_ssi_status_check(struct rsnd_mod *mod,
 				  u32 bit)
@@ -200,7 +121,7 @@ static int rsnd_ssi_master_clk_start(struct rsnd_ssi *ssi,
 		1, 2, 4, 8, 16, 6, 12,
 	};
 	unsigned int main_rate;
-	unsigned int rate = rsnd_scu_get_ssi_rate(priv, &ssi->mod, runtime);
+	unsigned int rate = rsnd_src_get_ssi_rate(priv, io, runtime);
 
 	/*
 	 * Find best clock, and try to start ADG
@@ -252,7 +173,7 @@ static void rsnd_ssi_hw_start(struct rsnd_ssi *ssi,
 	if (0 == ssi->usrcnt) {
 		clk_enable(ssi->clk);
 
-		if (rsnd_rdai_is_clk_master(rdai)) {
+		if (rsnd_dai_is_clk_master(rdai)) {
 			if (rsnd_ssi_clk_from_parent(ssi))
 				rsnd_ssi_hw_start(ssi->parent, rdai, io);
 			else
@@ -302,7 +223,7 @@ static void rsnd_ssi_hw_stop(struct rsnd_ssi *ssi,
 		rsnd_mod_write(&ssi->mod, SSICR, cr);	/* disabled all */
 		rsnd_ssi_status_check(&ssi->mod, IIRQ);
 
-		if (rsnd_rdai_is_clk_master(rdai)) {
+		if (rsnd_dai_is_clk_master(rdai)) {
 			if (rsnd_ssi_clk_from_parent(ssi))
 				rsnd_ssi_hw_stop(ssi->parent, rdai);
 			else
@@ -323,8 +244,6 @@ static int rsnd_ssi_init(struct rsnd_mod *mod,
 			 struct rsnd_dai_stream *io)
 {
 	struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
-	struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
-	struct device *dev = rsnd_priv_to_dev(priv);
 	struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io);
 	u32 cr;
 
@@ -365,13 +284,10 @@ static int rsnd_ssi_init(struct rsnd_mod *mod,
 	 * set ssi parameter
 	 */
 	ssi->rdai	= rdai;
-	ssi->io		= io;
 	ssi->cr_own	= cr;
 	ssi->err	= -1; /* ignore 1st error */
 
-	rsnd_ssi_mode_set(priv, rdai, ssi);
-
-	dev_dbg(dev, "%s.%d init\n", rsnd_mod_name(mod), rsnd_mod_id(mod));
+	rsnd_src_ssi_mode_init(mod, rdai, io);
 
 	return 0;
 }
@@ -384,13 +300,10 @@ static int rsnd_ssi_quit(struct rsnd_mod *mod,
 	struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
 	struct device *dev = rsnd_priv_to_dev(priv);
 
-	dev_dbg(dev, "%s.%d quit\n", rsnd_mod_name(mod), rsnd_mod_id(mod));
-
 	if (ssi->err > 0)
 		dev_warn(dev, "ssi under/over flow err = %d\n", ssi->err);
 
 	ssi->rdai	= NULL;
-	ssi->io		= NULL;
 	ssi->cr_own	= 0;
 	ssi->err	= 0;
 
@@ -414,8 +327,9 @@ static void rsnd_ssi_record_error(struct rsnd_ssi *ssi, u32 status)
 static irqreturn_t rsnd_ssi_pio_interrupt(int irq, void *data)
 {
 	struct rsnd_ssi *ssi = data;
-	struct rsnd_dai_stream *io = ssi->io;
-	u32 status = rsnd_mod_read(&ssi->mod, SSISR);
+	struct rsnd_mod *mod = &ssi->mod;
+	struct rsnd_dai_stream *io = rsnd_mod_to_io(mod);
+	u32 status = rsnd_mod_read(mod, SSISR);
 	irqreturn_t ret = IRQ_NONE;
 
 	if (io && (status & DIRQ)) {
@@ -432,9 +346,9 @@ static irqreturn_t rsnd_ssi_pio_interrupt(int irq, void *data)
 		 * see rsnd_ssi_init()
 		 */
 		if (rsnd_dai_is_play(rdai, io))
-			rsnd_mod_write(&ssi->mod, SSITDR, *buf);
+			rsnd_mod_write(mod, SSITDR, *buf);
 		else
-			*buf = rsnd_mod_read(&ssi->mod, SSIRDR);
+			*buf = rsnd_mod_read(mod, SSIRDR);
 
 		rsnd_dai_pointer_update(io, sizeof(*buf));
 
@@ -444,25 +358,39 @@ static irqreturn_t rsnd_ssi_pio_interrupt(int irq, void *data)
 	return ret;
 }
 
-static int rsnd_ssi_pio_start(struct rsnd_mod *mod,
+static int rsnd_ssi_pio_probe(struct rsnd_mod *mod,
 			      struct rsnd_dai *rdai,
 			      struct rsnd_dai_stream *io)
 {
 	struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
-	struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
 	struct device *dev = rsnd_priv_to_dev(priv);
+	struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
+	int irq = ssi->info->pio_irq;
+	int ret;
+
+	ret = devm_request_irq(dev, irq,
+			       rsnd_ssi_pio_interrupt,
+			       IRQF_SHARED,
+			       dev_name(dev), ssi);
+	if (ret)
+		dev_err(dev, "SSI request interrupt failed\n");
+
+	return ret;
+}
+
+static int rsnd_ssi_pio_start(struct rsnd_mod *mod,
+			      struct rsnd_dai *rdai,
+			      struct rsnd_dai_stream *io)
+{
+	struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
 
 	/* enable PIO IRQ */
 	ssi->cr_etc = UIEN | OIEN | DIEN;
 
-	/* enable PIO interrupt if gen2 */
-	if (rsnd_is_gen2(priv))
-		rsnd_mod_write(&ssi->mod, INT_ENABLE, 0x0f000000);
+	rsnd_src_enable_ssi_irq(mod, rdai, io);
 
 	rsnd_ssi_hw_start(ssi, rdai, io);
 
-	dev_dbg(dev, "%s.%d start\n", rsnd_mod_name(mod), rsnd_mod_id(mod));
-
 	return 0;
 }
 
@@ -470,12 +398,8 @@ static int rsnd_ssi_pio_stop(struct rsnd_mod *mod,
 			     struct rsnd_dai *rdai,
 			     struct rsnd_dai_stream *io)
 {
-	struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
-	struct device *dev = rsnd_priv_to_dev(priv);
 	struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
 
-	dev_dbg(dev, "%s.%d stop\n", rsnd_mod_name(mod), rsnd_mod_id(mod));
-
 	ssi->cr_etc = 0;
 
 	rsnd_ssi_hw_stop(ssi, rdai);
@@ -485,35 +409,46 @@ static int rsnd_ssi_pio_stop(struct rsnd_mod *mod,
 
 static struct rsnd_mod_ops rsnd_ssi_pio_ops = {
 	.name	= "ssi (pio)",
+	.probe	= rsnd_ssi_pio_probe,
 	.init	= rsnd_ssi_init,
 	.quit	= rsnd_ssi_quit,
 	.start	= rsnd_ssi_pio_start,
 	.stop	= rsnd_ssi_pio_stop,
 };
 
-static int rsnd_ssi_dma_inquiry(struct rsnd_dma *dma, dma_addr_t *buf, int *len)
+static int rsnd_ssi_dma_probe(struct rsnd_mod *mod,
+			  struct rsnd_dai *rdai,
+			  struct rsnd_dai_stream *io)
 {
-	struct rsnd_ssi *ssi = rsnd_dma_to_ssi(dma);
-	struct rsnd_dai_stream *io = ssi->io;
-	struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io);
+	struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
+	struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
+	struct rcar_snd_info *info = rsnd_priv_to_info(priv);
+	struct device *dev = rsnd_priv_to_dev(priv);
+	int dma_id = ssi->info->dma_id;
+	int is_play;
+	int ret;
 
-	*len = io->byte_per_period;
-	*buf = runtime->dma_addr +
-		rsnd_dai_pointer_offset(io, ssi->dma_offset + *len);
-	ssi->dma_offset = *len; /* it cares A/B plane */
+	if (info->dai_info)
+		is_play = rsnd_info_is_playback(priv, ssi);
+	else
+		is_play = rsnd_ssi_is_play(&ssi->mod);
 
-	return 0;
-}
+	ret = rsnd_dma_init(
+		priv, rsnd_mod_to_dma(mod),
+		is_play,
+		dma_id);
 
-static int rsnd_ssi_dma_complete(struct rsnd_dma *dma)
-{
-	struct rsnd_ssi *ssi = rsnd_dma_to_ssi(dma);
-	struct rsnd_dai_stream *io = ssi->io;
-	u32 status = rsnd_mod_read(&ssi->mod, SSISR);
+	if (ret < 0)
+		dev_err(dev, "SSI DMA failed\n");
 
-	rsnd_ssi_record_error(ssi, status);
+	return ret;
+}
 
-	rsnd_dai_pointer_update(ssi->io, io->byte_per_period);
+static int rsnd_ssi_dma_remove(struct rsnd_mod *mod,
+			       struct rsnd_dai *rdai,
+			       struct rsnd_dai_stream *io)
+{
+	rsnd_dma_quit(rsnd_mod_to_priv(mod), rsnd_mod_to_dma(mod));
 
 	return 0;
 }
@@ -527,14 +462,13 @@ static int rsnd_ssi_dma_start(struct rsnd_mod *mod,
 
 	/* enable DMA transfer */
 	ssi->cr_etc = DMEN;
-	ssi->dma_offset = 0;
 
 	rsnd_dma_start(dma);
 
 	rsnd_ssi_hw_start(ssi, ssi->rdai, io);
 
 	/* enable WS continue */
-	if (rsnd_rdai_is_clk_master(rdai))
+	if (rsnd_dai_is_clk_master(rdai))
 		rsnd_mod_write(&ssi->mod, SSIWSR, CONT);
 
 	return 0;
@@ -549,6 +483,8 @@ static int rsnd_ssi_dma_stop(struct rsnd_mod *mod,
 
 	ssi->cr_etc = 0;
 
+	rsnd_ssi_record_error(ssi, rsnd_mod_read(mod, SSISR));
+
 	rsnd_ssi_hw_stop(ssi, rdai);
 
 	rsnd_dma_stop(dma);
@@ -558,6 +494,8 @@ static int rsnd_ssi_dma_stop(struct rsnd_mod *mod,
 
 static struct rsnd_mod_ops rsnd_ssi_dma_ops = {
 	.name	= "ssi (dma)",
+	.probe	= rsnd_ssi_dma_probe,
+	.remove	= rsnd_ssi_dma_remove,
 	.init	= rsnd_ssi_init,
 	.quit	= rsnd_ssi_quit,
 	.start	= rsnd_ssi_dma_start,
@@ -567,24 +505,8 @@ static struct rsnd_mod_ops rsnd_ssi_dma_ops = {
 /*
  *		Non SSI
  */
-static int rsnd_ssi_non(struct rsnd_mod *mod,
-			struct rsnd_dai *rdai,
-			struct rsnd_dai_stream *io)
-{
-	struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
-	struct device *dev = rsnd_priv_to_dev(priv);
-
-	dev_dbg(dev, "%s\n", __func__);
-
-	return 0;
-}
-
 static struct rsnd_mod_ops rsnd_ssi_non_ops = {
 	.name	= "ssi (non)",
-	.init	= rsnd_ssi_non,
-	.quit	= rsnd_ssi_non,
-	.start	= rsnd_ssi_non,
-	.stop	= rsnd_ssi_non,
 };
 
 /*
@@ -593,16 +515,30 @@ static struct rsnd_mod_ops rsnd_ssi_non_ops = {
 struct rsnd_mod *rsnd_ssi_mod_get_frm_dai(struct rsnd_priv *priv,
 					  int dai_id, int is_play)
 {
+	struct rsnd_dai_platform_info *dai_info = NULL;
+	struct rsnd_dai_path_info *path_info = NULL;
+	struct rsnd_ssi_platform_info *target_info = NULL;
 	struct rsnd_ssi *ssi;
 	int i, has_play;
 
+	if (priv->rdai)
+		dai_info = priv->rdai[dai_id].info;
+	if (dai_info)
+		path_info = (is_play) ? &dai_info->playback : &dai_info->capture;
+	if (path_info)
+		target_info = path_info->ssi;
+
 	is_play = !!is_play;
 
 	for_each_rsnd_ssi(ssi, priv, i) {
+		if (target_info == ssi->info)
+			return &ssi->mod;
+
+		/* for compatible */
 		if (rsnd_ssi_dai_id(ssi) != dai_id)
 			continue;
 
-		has_play = !!(rsnd_ssi_mode_flags(ssi) & RSND_SSI_PLAY);
+		has_play = rsnd_ssi_is_play(&ssi->mod);
 
 		if (is_play == has_play)
 			return &ssi->mod;
@@ -616,36 +552,66 @@ struct rsnd_mod *rsnd_ssi_mod_get(struct rsnd_priv *priv, int id)
 	if (WARN_ON(id < 0 || id >= rsnd_ssi_nr(priv)))
 		id = 0;
 
-	return &(((struct rsnd_ssiu *)(priv->ssiu))->ssi + id)->mod;
+	return &((struct rsnd_ssi *)(priv->ssi) + id)->mod;
+}
+
+int rsnd_ssi_is_pin_sharing(struct rsnd_mod *mod)
+{
+	struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
+
+	return !!(rsnd_ssi_mode_flags(ssi) & RSND_SSI_CLK_PIN_SHARE);
+}
+
+int rsnd_ssi_is_play(struct rsnd_mod *mod)
+{
+	struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
+
+	return !!(rsnd_ssi_mode_flags(ssi) & RSND_SSI_PLAY);
+}
+
+static void rsnd_ssi_parent_clk_setup(struct rsnd_priv *priv, struct rsnd_ssi *ssi)
+{
+	if (!rsnd_ssi_is_pin_sharing(&ssi->mod))
+		return;
+
+	switch (rsnd_mod_id(&ssi->mod)) {
+	case 1:
+	case 2:
+		ssi->parent = rsnd_mod_to_ssi(rsnd_ssi_mod_get(priv, 0));
+		break;
+	case 4:
+		ssi->parent = rsnd_mod_to_ssi(rsnd_ssi_mod_get(priv, 3));
+		break;
+	case 8:
+		ssi->parent = rsnd_mod_to_ssi(rsnd_ssi_mod_get(priv, 7));
+		break;
+	}
 }
 
 int rsnd_ssi_probe(struct platform_device *pdev,
-		   struct rcar_snd_info *info,
 		   struct rsnd_priv *priv)
 {
+	struct rcar_snd_info *info = rsnd_priv_to_info(priv);
 	struct rsnd_ssi_platform_info *pinfo;
 	struct device *dev = rsnd_priv_to_dev(priv);
 	struct rsnd_mod_ops *ops;
 	struct clk *clk;
-	struct rsnd_ssiu *ssiu;
 	struct rsnd_ssi *ssi;
 	char name[RSND_SSI_NAME_SIZE];
-	int i, nr, ret;
+	int i, nr;
 
 	/*
 	 *	init SSI
 	 */
 	nr	= info->ssi_info_nr;
-	ssiu	= devm_kzalloc(dev, sizeof(*ssiu) + (sizeof(*ssi) * nr),
-			       GFP_KERNEL);
-	if (!ssiu) {
+	ssi	= devm_kzalloc(dev, sizeof(*ssi) * nr, GFP_KERNEL);
+	if (!ssi) {
 		dev_err(dev, "SSI allocate failed\n");
 		return -ENOMEM;
 	}
 
-	priv->ssiu	= ssiu;
-	ssiu->ssi	= (struct rsnd_ssi *)(ssiu + 1);
-	ssiu->ssi_nr	= nr;
+	priv->ssi	= ssi;
+	priv->ssi_nr	= nr;
 
 	for_each_rsnd_ssi(ssi, priv, i) {
 		pinfo = &info->ssi_info[i];
@@ -660,61 +626,15 @@ int rsnd_ssi_probe(struct platform_device *pdev,
 		ssi->clk	= clk;
 
 		ops = &rsnd_ssi_non_ops;
+		if (pinfo->dma_id > 0)
+			ops = &rsnd_ssi_dma_ops;
+		else if (rsnd_ssi_pio_available(ssi))
+			ops = &rsnd_ssi_pio_ops;
 
-		/*
-		 * SSI DMA case
-		 */
-		if (pinfo->dma_id > 0) {
-			ret = rsnd_dma_init(
-				priv, rsnd_mod_to_dma(&ssi->mod),
-				(rsnd_ssi_mode_flags(ssi) & RSND_SSI_PLAY),
-				pinfo->dma_id,
-				rsnd_ssi_dma_inquiry,
-				rsnd_ssi_dma_complete);
-			if (ret < 0)
-				dev_info(dev, "SSI DMA failed. try PIO transter\n");
-			else
-				ops	= &rsnd_ssi_dma_ops;
-
-			dev_dbg(dev, "SSI%d use DMA transfer\n", i);
-		}
-
-		/*
-		 * SSI PIO case
-		 */
-		if (!rsnd_ssi_dma_available(ssi) &&
-		     rsnd_ssi_pio_available(ssi)) {
-			ret = devm_request_irq(dev, pinfo->pio_irq,
-					       &rsnd_ssi_pio_interrupt,
-					       IRQF_SHARED,
-					       dev_name(dev), ssi);
-			if (ret) {
-				dev_err(dev, "SSI request interrupt failed\n");
-				return ret;
-			}
-
-			ops	= &rsnd_ssi_pio_ops;
+		rsnd_mod_init(priv, &ssi->mod, ops, RSND_MOD_SSI, i);
 
-			dev_dbg(dev, "SSI%d use PIO transfer\n", i);
-		}
-
-		rsnd_mod_init(priv, &ssi->mod, ops, i);
+		rsnd_ssi_parent_clk_setup(priv, ssi);
 	}
 
-	dev_dbg(dev, "ssi probed\n");
-
 	return 0;
 }
-
-void rsnd_ssi_remove(struct platform_device *pdev,
-		   struct rsnd_priv *priv)
-{
-	struct rsnd_ssi *ssi;
-	int i;
-
-	for_each_rsnd_ssi(ssi, priv, i) {
-		if (rsnd_ssi_dma_available(ssi))
-			rsnd_dma_quit(priv, rsnd_mod_to_dma(&ssi->mod));
-	}
-
-}
diff --git a/sound/soc/sirf/Kconfig b/sound/soc/sirf/Kconfig
new file mode 100644
index 000000000000..89e89429b04a
--- /dev/null
+++ b/sound/soc/sirf/Kconfig
@@ -0,0 +1,14 @@
+config SND_SOC_SIRF
+	tristate "SoC Audio for the SiRF SoC chips"
+	depends on ARCH_SIRF || COMPILE_TEST
+	select SND_SOC_GENERIC_DMAENGINE_PCM
+
+config SND_SOC_SIRF_AUDIO
+	tristate "SoC Audio support for SiRF internal audio codec"
+	depends on SND_SOC_SIRF
+	select SND_SOC_SIRF_AUDIO_CODEC
+	select SND_SOC_SIRF_AUDIO_PORT
+
+config SND_SOC_SIRF_AUDIO_PORT
+	select REGMAP_MMIO
+	tristate
diff --git a/sound/soc/sirf/Makefile b/sound/soc/sirf/Makefile
new file mode 100644
index 000000000000..913b93231d4e
--- /dev/null
+++ b/sound/soc/sirf/Makefile
@@ -0,0 +1,5 @@
+snd-soc-sirf-audio-objs := sirf-audio.o
+snd-soc-sirf-audio-port-objs := sirf-audio-port.o
+
+obj-$(CONFIG_SND_SOC_SIRF_AUDIO) += snd-soc-sirf-audio.o
+obj-$(CONFIG_SND_SOC_SIRF_AUDIO_PORT) += snd-soc-sirf-audio-port.o
diff --git a/sound/soc/sirf/sirf-audio-port.c b/sound/soc/sirf/sirf-audio-port.c
new file mode 100644
index 000000000000..b04a53f2b4f6
--- /dev/null
+++ b/sound/soc/sirf/sirf-audio-port.c
@@ -0,0 +1,194 @@
+/*
+ * SiRF Audio port driver
+ *
+ * Copyright (c) 2011 Cambridge Silicon Radio Limited, a CSR plc group company.
+ *
+ * Licensed under GPLv2 or later.
+ */
+#include <linux/module.h>
+#include <linux/io.h>
+#include <linux/regmap.h>
+#include <sound/soc.h>
+#include <sound/dmaengine_pcm.h>
+
+#include "sirf-audio-port.h"
+
+struct sirf_audio_port {
+	struct regmap *regmap;
+	struct snd_dmaengine_dai_dma_data playback_dma_data;
+	struct snd_dmaengine_dai_dma_data capture_dma_data;
+};
+
+static void sirf_audio_port_tx_enable(struct sirf_audio_port *port)
+{
+	regmap_update_bits(port->regmap, AUDIO_PORT_IC_TXFIFO_OP,
+		AUDIO_FIFO_RESET, AUDIO_FIFO_RESET);
+	regmap_write(port->regmap, AUDIO_PORT_IC_TXFIFO_INT_MSK, 0);
+	regmap_write(port->regmap, AUDIO_PORT_IC_TXFIFO_OP, 0);
+	regmap_update_bits(port->regmap, AUDIO_PORT_IC_TXFIFO_OP,
+		AUDIO_FIFO_START, AUDIO_FIFO_START);
+	regmap_update_bits(port->regmap, AUDIO_PORT_IC_CODEC_TX_CTRL,
+		IC_TX_ENABLE, IC_TX_ENABLE);
+}
+
+static void sirf_audio_port_tx_disable(struct sirf_audio_port *port)
+{
+	regmap_write(port->regmap, AUDIO_PORT_IC_TXFIFO_OP, 0);
+	regmap_update_bits(port->regmap, AUDIO_PORT_IC_CODEC_TX_CTRL,
+		IC_TX_ENABLE, ~IC_TX_ENABLE);
+}
+
+static void sirf_audio_port_rx_enable(struct sirf_audio_port *port,
+	int channels)
+{
+	regmap_update_bits(port->regmap, AUDIO_PORT_IC_RXFIFO_OP,
+		AUDIO_FIFO_RESET, AUDIO_FIFO_RESET);
+	regmap_write(port->regmap, AUDIO_PORT_IC_RXFIFO_INT_MSK, 0);
+	regmap_write(port->regmap, AUDIO_PORT_IC_RXFIFO_OP, 0);
+	regmap_update_bits(port->regmap, AUDIO_PORT_IC_RXFIFO_OP,
+		AUDIO_FIFO_START, AUDIO_FIFO_START);
+	if (channels == 1)
+		regmap_update_bits(port->regmap, AUDIO_PORT_IC_CODEC_RX_CTRL,
+			IC_RX_ENABLE_MONO, IC_RX_ENABLE_MONO);
+	else
+		regmap_update_bits(port->regmap, AUDIO_PORT_IC_CODEC_RX_CTRL,
+			IC_RX_ENABLE_STEREO, IC_RX_ENABLE_STEREO);
+}
+
+static void sirf_audio_port_rx_disable(struct sirf_audio_port *port)
+{
+	regmap_update_bits(port->regmap, AUDIO_PORT_IC_CODEC_RX_CTRL,
+			IC_RX_ENABLE_STEREO, ~IC_RX_ENABLE_STEREO);
+}
+
+static int sirf_audio_port_dai_probe(struct snd_soc_dai *dai)
+{
+	struct sirf_audio_port *port = snd_soc_dai_get_drvdata(dai);
+	snd_soc_dai_init_dma_data(dai, &port->playback_dma_data,
+			&port->capture_dma_data);
+	return 0;
+}
+
+static int sirf_audio_port_trigger(struct snd_pcm_substream *substream, int cmd,
+	struct snd_soc_dai *dai)
+{
+	struct sirf_audio_port *port = snd_soc_dai_get_drvdata(dai);
+	int playback = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
+
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_STOP:
+	case SNDRV_PCM_TRIGGER_SUSPEND:
+	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+		if (playback)
+			sirf_audio_port_tx_disable(port);
+		else
+			sirf_audio_port_rx_disable(port);
+		break;
+	case SNDRV_PCM_TRIGGER_START:
+	case SNDRV_PCM_TRIGGER_RESUME:
+	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+		if (playback)
+			sirf_audio_port_tx_enable(port);
+		else
+			sirf_audio_port_rx_enable(port,
+				substream->runtime->channels);
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static const struct snd_soc_dai_ops sirf_audio_port_dai_ops = {
+	.trigger = sirf_audio_port_trigger,
+};
+
+static struct snd_soc_dai_driver sirf_audio_port_dai = {
+	.probe = sirf_audio_port_dai_probe,
+	.name = "sirf-audio-port",
+	.id = 0,
+	.playback = {
+		.channels_min = 2,
+		.channels_max = 2,
+		.rates = SNDRV_PCM_RATE_48000,
+		.formats = SNDRV_PCM_FMTBIT_S16_LE,
+	},
+	.capture = {
+		.channels_min = 1,
+		.channels_max = 2,
+		.rates = SNDRV_PCM_RATE_48000,
+		.formats = SNDRV_PCM_FMTBIT_S16_LE,
+	},
+	.ops = &sirf_audio_port_dai_ops,
+};
+
+static const struct snd_soc_component_driver sirf_audio_port_component = {
+	.name       = "sirf-audio-port",
+};
+
+static const struct regmap_config sirf_audio_port_regmap_config = {
+	.reg_bits = 32,
+	.reg_stride = 4,
+	.val_bits = 32,
+	.max_register = AUDIO_PORT_IC_RXFIFO_INT_MSK,
+	.cache_type = REGCACHE_NONE,
+};
+
+static int sirf_audio_port_probe(struct platform_device *pdev)
+{
+	int ret;
+	struct sirf_audio_port *port;
+	void __iomem *base;
+	struct resource *mem_res;
+
+	port = devm_kzalloc(&pdev->dev,
+			sizeof(struct sirf_audio_port), GFP_KERNEL);
+	if (!port)
+		return -ENOMEM;
+
+	mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!mem_res) {
+		dev_err(&pdev->dev, "no mem resource?\n");
+		return -ENODEV;
+	}
+
+	base = devm_ioremap(&pdev->dev, mem_res->start,
+			resource_size(mem_res));
+	if (base == NULL)
+		return -ENOMEM;
+
+	port->regmap = devm_regmap_init_mmio(&pdev->dev, base,
+					    &sirf_audio_port_regmap_config);
+	if (IS_ERR(port->regmap))
+		return PTR_ERR(port->regmap);
+
+	ret = devm_snd_soc_register_component(&pdev->dev,
+			&sirf_audio_port_component, &sirf_audio_port_dai, 1);
+	if (ret)
+		return ret;
+
+	platform_set_drvdata(pdev, port);
+	return devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, 0);
+}
+
+static const struct of_device_id sirf_audio_port_of_match[] = {
+	{ .compatible = "sirf,audio-port", },
+	{}
+};
+MODULE_DEVICE_TABLE(of, sirf_audio_port_of_match);
+
+static struct platform_driver sirf_audio_port_driver = {
+	.driver = {
+		.name = "sirf-audio-port",
+		.owner = THIS_MODULE,
+		.of_match_table = sirf_audio_port_of_match,
+	},
+	.probe = sirf_audio_port_probe,
+};
+
+module_platform_driver(sirf_audio_port_driver);
+
+MODULE_DESCRIPTION("SiRF Audio Port driver");
+MODULE_AUTHOR("RongJun Ying <Rongjun.Ying@csr.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/sirf/sirf-audio-port.h b/sound/soc/sirf/sirf-audio-port.h
new file mode 100644
index 000000000000..f32dc54f4499
--- /dev/null
+++ b/sound/soc/sirf/sirf-audio-port.h
@@ -0,0 +1,62 @@
+/*
+ * SiRF Audio port controllers define
+ *
+ * Copyright (c) 2011 Cambridge Silicon Radio Limited, a CSR plc group company.
+ *
+ * Licensed under GPLv2 or later.
+ */
+
+#ifndef _SIRF_AUDIO_PORT_H
+#define _SIRF_AUDIO_PORT_H
+
+#define AUDIO_PORT_TX_FIFO_LEVEL_CHECK_MASK     0x3F
+#define AUDIO_PORT_TX_FIFO_SC_OFFSET    0
+#define AUDIO_PORT_TX_FIFO_LC_OFFSET    10
+#define AUDIO_PORT_TX_FIFO_HC_OFFSET    20
+
+#define TX_FIFO_SC(x)           (((x) & AUDIO_PORT_TX_FIFO_LEVEL_CHECK_MASK) \
+				<< AUDIO_PORT_TX_FIFO_SC_OFFSET)
+#define TX_FIFO_LC(x)           (((x) & AUDIO_PORT_TX_FIFO_LEVEL_CHECK_MASK) \
+				<< AUDIO_PORT_TX_FIFO_LC_OFFSET)
+#define TX_FIFO_HC(x)           (((x) & AUDIO_PORT_TX_FIFO_LEVEL_CHECK_MASK) \
+				<< AUDIO_PORT_TX_FIFO_HC_OFFSET)
+
+#define AUDIO_PORT_RX_FIFO_LEVEL_CHECK_MASK     0x0F
+#define AUDIO_PORT_RX_FIFO_SC_OFFSET    0
+#define AUDIO_PORT_RX_FIFO_LC_OFFSET    10
+#define AUDIO_PORT_RX_FIFO_HC_OFFSET    20
+
+#define RX_FIFO_SC(x)           (((x) & AUDIO_PORT_RX_FIFO_LEVEL_CHECK_MASK) \
+				<< AUDIO_PORT_RX_FIFO_SC_OFFSET)
+#define RX_FIFO_LC(x)           (((x) & AUDIO_PORT_RX_FIFO_LEVEL_CHECK_MASK) \
+				<< AUDIO_PORT_RX_FIFO_LC_OFFSET)
+#define RX_FIFO_HC(x)           (((x) & AUDIO_PORT_RX_FIFO_LEVEL_CHECK_MASK) \
+				<< AUDIO_PORT_RX_FIFO_HC_OFFSET)
+#define AUDIO_PORT_IC_CODEC_TX_CTRL		(0x00F4)
+#define AUDIO_PORT_IC_CODEC_RX_CTRL		(0x00F8)
+
+#define AUDIO_PORT_IC_TXFIFO_OP			(0x00FC)
+#define AUDIO_PORT_IC_TXFIFO_LEV_CHK		(0x0100)
+#define AUDIO_PORT_IC_TXFIFO_STS		(0x0104)
+#define AUDIO_PORT_IC_TXFIFO_INT		(0x0108)
+#define AUDIO_PORT_IC_TXFIFO_INT_MSK		(0x010C)
+
+#define AUDIO_PORT_IC_RXFIFO_OP			(0x0110)
+#define AUDIO_PORT_IC_RXFIFO_LEV_CHK		(0x0114)
+#define AUDIO_PORT_IC_RXFIFO_STS		(0x0118)
+#define AUDIO_PORT_IC_RXFIFO_INT		(0x011C)
+#define AUDIO_PORT_IC_RXFIFO_INT_MSK		(0x0120)
+
+#define AUDIO_FIFO_START		(1 << 0)
+#define AUDIO_FIFO_RESET		(1 << 1)
+
+#define AUDIO_FIFO_FULL			(1 << 0)
+#define AUDIO_FIFO_EMPTY		(1 << 1)
+#define AUDIO_FIFO_OFLOW		(1 << 2)
+#define AUDIO_FIFO_UFLOW		(1 << 3)
+
+#define IC_TX_ENABLE		(0x03)
+#define IC_RX_ENABLE_MONO	(0x01)
+#define IC_RX_ENABLE_STEREO	(0x03)
+
+#endif /*__SIRF_AUDIO_PORT_H*/
diff --git a/sound/soc/sirf/sirf-audio.c b/sound/soc/sirf/sirf-audio.c
new file mode 100644
index 000000000000..ecef51021653
--- /dev/null
+++ b/sound/soc/sirf/sirf-audio.c
@@ -0,0 +1,156 @@
+/*
+ * SiRF audio card driver
+ *
+ * Copyright (c) 2011 Cambridge Silicon Radio Limited, a CSR plc group company.
+ *
+ * Licensed under GPLv2 or later.
+ */
+
+#include <linux/platform_device.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/gpio.h>
+#include <linux/of_gpio.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+
+struct sirf_audio_card {
+	unsigned int            gpio_hp_pa;
+	unsigned int            gpio_spk_pa;
+};
+
+static int sirf_audio_hp_event(struct snd_soc_dapm_widget *w,
+				struct snd_kcontrol *ctrl, int event)
+{
+	struct snd_soc_dapm_context *dapm = w->dapm;
+	struct snd_soc_card *card = dapm->card;
+	struct sirf_audio_card *sirf_audio_card = snd_soc_card_get_drvdata(card);
+	int on = !SND_SOC_DAPM_EVENT_OFF(event);
+	if (gpio_is_valid(sirf_audio_card->gpio_hp_pa))
+		gpio_set_value(sirf_audio_card->gpio_hp_pa, on);
+	return 0;
+}
+
+static int sirf_audio_spk_event(struct snd_soc_dapm_widget *w,
+				struct snd_kcontrol *ctrl, int event)
+{
+	struct snd_soc_dapm_context *dapm = w->dapm;
+	struct snd_soc_card *card = dapm->card;
+	struct sirf_audio_card *sirf_audio_card = snd_soc_card_get_drvdata(card);
+	int on = !SND_SOC_DAPM_EVENT_OFF(event);
+
+	if (gpio_is_valid(sirf_audio_card->gpio_spk_pa))
+		gpio_set_value(sirf_audio_card->gpio_spk_pa, on);
+
+	return 0;
+}
+static const struct snd_soc_dapm_widget sirf_audio_dapm_widgets[] = {
+	SND_SOC_DAPM_HP("Hp", sirf_audio_hp_event),
+	SND_SOC_DAPM_SPK("Ext Spk", sirf_audio_spk_event),
+	SND_SOC_DAPM_MIC("Ext Mic", NULL),
+};
+
+static const struct snd_soc_dapm_route intercon[] = {
+	{"Hp", NULL, "HPOUTL"},
+	{"Hp", NULL, "HPOUTR"},
+	{"Ext Spk", NULL, "SPKOUT"},
+	{"MICIN1", NULL, "Mic Bias"},
+	{"Mic Bias", NULL, "Ext Mic"},
+};
+
+/* Digital audio interface glue - connects codec <--> CPU */
+static struct snd_soc_dai_link sirf_audio_dai_link[] = {
+	{
+		.name = "SiRF audio card",
+		.stream_name = "SiRF audio HiFi",
+		.codec_dai_name = "sirf-audio-codec",
+	},
+};
+
+/* Audio machine driver */
+static struct snd_soc_card snd_soc_sirf_audio_card = {
+	.name = "SiRF audio card",
+	.owner = THIS_MODULE,
+	.dai_link = sirf_audio_dai_link,
+	.num_links = ARRAY_SIZE(sirf_audio_dai_link),
+	.dapm_widgets = sirf_audio_dapm_widgets,
+	.num_dapm_widgets = ARRAY_SIZE(sirf_audio_dapm_widgets),
+	.dapm_routes = intercon,
+	.num_dapm_routes = ARRAY_SIZE(intercon),
+};
+
+static int sirf_audio_probe(struct platform_device *pdev)
+{
+	struct snd_soc_card *card = &snd_soc_sirf_audio_card;
+	struct sirf_audio_card *sirf_audio_card;
+	int ret;
+
+	sirf_audio_card = devm_kzalloc(&pdev->dev, sizeof(struct sirf_audio_card),
+			GFP_KERNEL);
+	if (sirf_audio_card == NULL)
+		return -ENOMEM;
+
+	sirf_audio_dai_link[0].cpu_of_node =
+		of_parse_phandle(pdev->dev.of_node, "sirf,audio-platform", 0);
+	sirf_audio_dai_link[0].platform_of_node =
+		of_parse_phandle(pdev->dev.of_node, "sirf,audio-platform", 0);
+	sirf_audio_dai_link[0].codec_of_node =
+		of_parse_phandle(pdev->dev.of_node, "sirf,audio-codec", 0);
+	sirf_audio_card->gpio_spk_pa = of_get_named_gpio(pdev->dev.of_node,
+			"spk-pa-gpios", 0);
+	sirf_audio_card->gpio_hp_pa =  of_get_named_gpio(pdev->dev.of_node,
+			"hp-pa-gpios", 0);
+	if (gpio_is_valid(sirf_audio_card->gpio_spk_pa)) {
+		ret = devm_gpio_request_one(&pdev->dev,
+				sirf_audio_card->gpio_spk_pa,
+				GPIOF_OUT_INIT_LOW, "SPA_PA_SD");
+		if (ret) {
+			dev_err(&pdev->dev,
+				"Failed to request GPIO_%d for reset: %d\n",
+				sirf_audio_card->gpio_spk_pa, ret);
+			return ret;
+		}
+	}
+	if (gpio_is_valid(sirf_audio_card->gpio_hp_pa)) {
+		ret = devm_gpio_request_one(&pdev->dev,
+				sirf_audio_card->gpio_hp_pa,
+				GPIOF_OUT_INIT_LOW, "HP_PA_SD");
+		if (ret) {
+			dev_err(&pdev->dev,
+				"Failed to request GPIO_%d for reset: %d\n",
+				sirf_audio_card->gpio_hp_pa, ret);
+			return ret;
+		}
+	}
+
+	card->dev = &pdev->dev;
+	snd_soc_card_set_drvdata(card, sirf_audio_card);
+
+	ret = devm_snd_soc_register_card(&pdev->dev, card);
+	if (ret)
+		dev_err(&pdev->dev, "snd_soc_register_card() failed:%d\n", ret);
+
+	return ret;
+}
+
+static const struct of_device_id sirf_audio_of_match[] = {
+	{.compatible = "sirf,sirf-audio-card", },
+	{ },
+};
+MODULE_DEVICE_TABLE(of, sirf_audio_of_match);
+
+static struct platform_driver sirf_audio_driver = {
+	.driver = {
+		.name = "sirf-audio-card",
+		.owner = THIS_MODULE,
+		.pm = &snd_soc_pm_ops,
+		.of_match_table = sirf_audio_of_match,
+	},
+	.probe = sirf_audio_probe,
+};
+module_platform_driver(sirf_audio_driver);
+
+MODULE_AUTHOR("RongJun Ying <RongJun.Ying@csr.com>");
+MODULE_DESCRIPTION("ALSA SoC SIRF audio card driver");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/soc-cache.c b/sound/soc/soc-cache.c
index 375dc6dfba4e..bfed3e4c45ff 100644
--- a/sound/soc/soc-cache.c
+++ b/sound/soc/soc-cache.c
@@ -96,8 +96,7 @@ int snd_soc_cache_exit(struct snd_soc_codec *codec)
 {
 	dev_dbg(codec->dev, "ASoC: Destroying cache for %s codec\n",
 			codec->name);
-	if (!codec->reg_cache)
-		return 0;
+
 	kfree(codec->reg_cache);
 	codec->reg_cache = NULL;
 	return 0;
@@ -117,8 +116,9 @@ int snd_soc_cache_read(struct snd_soc_codec *codec,
 		return -EINVAL;
 
 	mutex_lock(&codec->cache_rw_mutex);
-	*value = snd_soc_get_cache_val(codec->reg_cache, reg,
-				       codec->driver->reg_word_size);
+	if (!ZERO_OR_NULL_PTR(codec->reg_cache))
+		*value = snd_soc_get_cache_val(codec->reg_cache, reg,
+					       codec->driver->reg_word_size);
 	mutex_unlock(&codec->cache_rw_mutex);
 
 	return 0;
@@ -136,8 +136,9 @@ int snd_soc_cache_write(struct snd_soc_codec *codec,
 			unsigned int reg, unsigned int value)
 {
 	mutex_lock(&codec->cache_rw_mutex);
-	snd_soc_set_cache_val(codec->reg_cache, reg, value,
-			      codec->driver->reg_word_size);
+	if (!ZERO_OR_NULL_PTR(codec->reg_cache))
+		snd_soc_set_cache_val(codec->reg_cache, reg, value,
+				      codec->driver->reg_word_size);
 	mutex_unlock(&codec->cache_rw_mutex);
 
 	return 0;
diff --git a/sound/soc/soc-compress.c b/sound/soc/soc-compress.c
index 5e9690c85d8f..91083e6a6b38 100644
--- a/sound/soc/soc-compress.c
+++ b/sound/soc/soc-compress.c
@@ -30,8 +30,6 @@ static int soc_compr_open(struct snd_compr_stream *cstream)
 {
 	struct snd_soc_pcm_runtime *rtd = cstream->private_data;
 	struct snd_soc_platform *platform = rtd->platform;
-	struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
-	struct snd_soc_dai *codec_dai = rtd->codec_dai;
 	int ret = 0;
 
 	mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass);
@@ -52,17 +50,7 @@ static int soc_compr_open(struct snd_compr_stream *cstream)
 		}
 	}
 
-	if (cstream->direction == SND_COMPRESS_PLAYBACK) {
-		cpu_dai->playback_active++;
-		codec_dai->playback_active++;
-	} else {
-		cpu_dai->capture_active++;
-		codec_dai->capture_active++;
-	}
-
-	cpu_dai->active++;
-	codec_dai->active++;
-	rtd->codec->active++;
+	snd_soc_runtime_activate(rtd, cstream->direction);
 
 	mutex_unlock(&rtd->pcm_mutex);
 
@@ -81,8 +69,6 @@ static int soc_compr_open_fe(struct snd_compr_stream *cstream)
 	struct snd_soc_pcm_runtime *fe = cstream->private_data;
 	struct snd_pcm_substream *fe_substream = fe->pcm->streams[0].substream;
 	struct snd_soc_platform *platform = fe->platform;
-	struct snd_soc_dai *cpu_dai = fe->cpu_dai;
-	struct snd_soc_dai *codec_dai = fe->codec_dai;
 	struct snd_soc_dpcm *dpcm;
 	struct snd_soc_dapm_widget_list *list;
 	int stream;
@@ -140,17 +126,7 @@ static int soc_compr_open_fe(struct snd_compr_stream *cstream)
 	fe->dpcm[stream].state = SND_SOC_DPCM_STATE_OPEN;
 	fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_NO;
 
-	if (cstream->direction == SND_COMPRESS_PLAYBACK) {
-		cpu_dai->playback_active++;
-		codec_dai->playback_active++;
-	} else {
-		cpu_dai->capture_active++;
-		codec_dai->capture_active++;
-	}
-
-	cpu_dai->active++;
-	codec_dai->active++;
-	fe->codec->active++;
+	snd_soc_runtime_activate(fe, stream);
 
 	mutex_unlock(&fe->card->mutex);
 
@@ -202,23 +178,18 @@ static int soc_compr_free(struct snd_compr_stream *cstream)
 	struct snd_soc_platform *platform = rtd->platform;
 	struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
 	struct snd_soc_dai *codec_dai = rtd->codec_dai;
-	struct snd_soc_codec *codec = rtd->codec;
+	int stream;
 
 	mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass);
 
-	if (cstream->direction == SND_COMPRESS_PLAYBACK) {
-		cpu_dai->playback_active--;
-		codec_dai->playback_active--;
-	} else {
-		cpu_dai->capture_active--;
-		codec_dai->capture_active--;
-	}
+	if (cstream->direction == SND_COMPRESS_PLAYBACK)
+		stream = SNDRV_PCM_STREAM_PLAYBACK;
+	else
+		stream = SNDRV_PCM_STREAM_CAPTURE;
 
-	snd_soc_dai_digital_mute(codec_dai, 1, cstream->direction);
+	snd_soc_runtime_deactivate(rtd, stream);
 
-	cpu_dai->active--;
-	codec_dai->active--;
-	codec->active--;
+	snd_soc_dai_digital_mute(codec_dai, 1, cstream->direction);
 
 	if (!cpu_dai->active)
 		cpu_dai->rate = 0;
@@ -235,8 +206,7 @@ static int soc_compr_free(struct snd_compr_stream *cstream)
 	cpu_dai->runtime = NULL;
 
 	if (cstream->direction == SND_COMPRESS_PLAYBACK) {
-		if (!rtd->pmdown_time || codec->ignore_pmdown_time ||
-		    rtd->dai_link->ignore_pmdown_time) {
+		if (snd_soc_runtime_ignore_pmdown_time(rtd)) {
 			snd_soc_dapm_stream_event(rtd,
 					SNDRV_PCM_STREAM_PLAYBACK,
 					SND_SOC_DAPM_STREAM_STOP);
@@ -261,26 +231,17 @@ static int soc_compr_free_fe(struct snd_compr_stream *cstream)
 {
 	struct snd_soc_pcm_runtime *fe = cstream->private_data;
 	struct snd_soc_platform *platform = fe->platform;
-	struct snd_soc_dai *cpu_dai = fe->cpu_dai;
-	struct snd_soc_dai *codec_dai = fe->codec_dai;
 	struct snd_soc_dpcm *dpcm;
 	int stream, ret;
 
 	mutex_lock_nested(&fe->card->mutex, SND_SOC_CARD_CLASS_RUNTIME);
 
-	if (cstream->direction == SND_COMPRESS_PLAYBACK) {
+	if (cstream->direction == SND_COMPRESS_PLAYBACK)
 		stream = SNDRV_PCM_STREAM_PLAYBACK;
-		cpu_dai->playback_active--;
-		codec_dai->playback_active--;
-	} else {
+	else
 		stream = SNDRV_PCM_STREAM_CAPTURE;
-		cpu_dai->capture_active--;
-		codec_dai->capture_active--;
-	}
 
-	cpu_dai->active--;
-	codec_dai->active--;
-	fe->codec->active--;
+	snd_soc_runtime_deactivate(fe, stream);
 
 	fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_FE;
 
diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c
index fe1df50805a3..359c2849b364 100644
--- a/sound/soc/soc-core.c
+++ b/sound/soc/soc-core.c
@@ -56,7 +56,6 @@ EXPORT_SYMBOL_GPL(snd_soc_debugfs_root);
 #endif
 
 static DEFINE_MUTEX(client_mutex);
-static LIST_HEAD(dai_list);
 static LIST_HEAD(platform_list);
 static LIST_HEAD(codec_list);
 static LIST_HEAD(component_list);
@@ -370,18 +369,22 @@ static ssize_t dai_list_read_file(struct file *file, char __user *user_buf,
 {
 	char *buf = kmalloc(PAGE_SIZE, GFP_KERNEL);
 	ssize_t len, ret = 0;
+	struct snd_soc_component *component;
 	struct snd_soc_dai *dai;
 
 	if (!buf)
 		return -ENOMEM;
 
-	list_for_each_entry(dai, &dai_list, list) {
-		len = snprintf(buf + ret, PAGE_SIZE - ret, "%s\n", dai->name);
-		if (len >= 0)
-			ret += len;
-		if (ret > PAGE_SIZE) {
-			ret = PAGE_SIZE;
-			break;
+	list_for_each_entry(component, &component_list, list) {
+		list_for_each_entry(dai, &component->dai_list, list) {
+			len = snprintf(buf + ret, PAGE_SIZE - ret, "%s\n",
+				dai->name);
+			if (len >= 0)
+				ret += len;
+			if (ret > PAGE_SIZE) {
+				ret = PAGE_SIZE;
+				break;
+			}
 		}
 	}
 
@@ -855,6 +858,7 @@ static int soc_bind_dai_link(struct snd_soc_card *card, int num)
 {
 	struct snd_soc_dai_link *dai_link = &card->dai_link[num];
 	struct snd_soc_pcm_runtime *rtd = &card->rtd[num];
+	struct snd_soc_component *component;
 	struct snd_soc_codec *codec;
 	struct snd_soc_platform *platform;
 	struct snd_soc_dai *codec_dai, *cpu_dai;
@@ -863,18 +867,20 @@ static int soc_bind_dai_link(struct snd_soc_card *card, int num)
 	dev_dbg(card->dev, "ASoC: binding %s at idx %d\n", dai_link->name, num);
 
 	/* Find CPU DAI from registered DAIs*/
-	list_for_each_entry(cpu_dai, &dai_list, list) {
+	list_for_each_entry(component, &component_list, list) {
 		if (dai_link->cpu_of_node &&
-		    (cpu_dai->dev->of_node != dai_link->cpu_of_node))
+			component->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))
+			strcmp(dev_name(component->dev), dai_link->cpu_name))
 			continue;
+		list_for_each_entry(cpu_dai, &component->dai_list, list) {
+			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;
+		}
 	}
 
 	if (!rtd->cpu_dai) {
@@ -899,12 +905,10 @@ static int soc_bind_dai_link(struct snd_soc_card *card, int num)
 		 * CODEC found, so find CODEC DAI from registered DAIs from
 		 * this CODEC
 		 */
-		list_for_each_entry(codec_dai, &dai_list, list) {
-			if (codec->dev == codec_dai->dev &&
-				!strcmp(codec_dai->name,
-					dai_link->codec_dai_name)) {
-
+		list_for_each_entry(codec_dai, &codec->component.dai_list, list) {
+			if (!strcmp(codec_dai->name, dai_link->codec_dai_name)) {
 				rtd->codec_dai = codec_dai;
+				break;
 			}
 		}
 
@@ -1128,12 +1132,8 @@ static int soc_probe_codec(struct snd_soc_card *card,
 					  driver->num_dapm_widgets);
 
 	/* Create DAPM widgets for each DAI stream */
-	list_for_each_entry(dai, &dai_list, list) {
-		if (dai->dev != codec->dev)
-			continue;
-
+	list_for_each_entry(dai, &codec->component.dai_list, list)
 		snd_soc_dapm_new_dai_widgets(&codec->dapm, dai);
-	}
 
 	codec->dapm.idle_bias_off = driver->idle_bias_off;
 
@@ -1180,6 +1180,7 @@ static int soc_probe_platform(struct snd_soc_card *card,
 {
 	int ret = 0;
 	const struct snd_soc_platform_driver *driver = platform->driver;
+	struct snd_soc_component *component;
 	struct snd_soc_dai *dai;
 
 	platform->card = card;
@@ -1195,11 +1196,11 @@ static int soc_probe_platform(struct snd_soc_card *card,
 			driver->dapm_widgets, driver->num_dapm_widgets);
 
 	/* Create DAPM widgets for each DAI stream */
-	list_for_each_entry(dai, &dai_list, list) {
-		if (dai->dev != platform->dev)
+	list_for_each_entry(component, &component_list, list) {
+		if (component->dev != platform->dev)
 			continue;
-
-		snd_soc_dapm_new_dai_widgets(&platform->dapm, dai);
+		list_for_each_entry(dai, &component->dai_list, list)
+			snd_soc_dapm_new_dai_widgets(&platform->dapm, dai);
 	}
 
 	platform->dapm.idle_bias_off = 1;
@@ -2571,10 +2572,10 @@ int snd_soc_info_enum_double(struct snd_kcontrol *kcontrol,
 
 	uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
 	uinfo->count = e->shift_l == e->shift_r ? 1 : 2;
-	uinfo->value.enumerated.items = e->max;
+	uinfo->value.enumerated.items = e->items;
 
-	if (uinfo->value.enumerated.item > e->max - 1)
-		uinfo->value.enumerated.item = e->max - 1;
+	if (uinfo->value.enumerated.item >= e->items)
+		uinfo->value.enumerated.item = e->items - 1;
 	strlcpy(uinfo->value.enumerated.name,
 		e->texts[uinfo->value.enumerated.item],
 		sizeof(uinfo->value.enumerated.name));
@@ -2596,14 +2597,18 @@ int snd_soc_get_enum_double(struct snd_kcontrol *kcontrol,
 {
 	struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
 	struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
-	unsigned int val;
+	unsigned int val, item;
+	unsigned int reg_val;
 
-	val = snd_soc_read(codec, e->reg);
-	ucontrol->value.enumerated.item[0]
-		= (val >> e->shift_l) & e->mask;
-	if (e->shift_l != e->shift_r)
-		ucontrol->value.enumerated.item[1] =
-			(val >> e->shift_r) & e->mask;
+	reg_val = snd_soc_read(codec, e->reg);
+	val = (reg_val >> e->shift_l) & e->mask;
+	item = snd_soc_enum_val_to_item(e, val);
+	ucontrol->value.enumerated.item[0] = item;
+	if (e->shift_l != e->shift_r) {
+		val = (reg_val >> e->shift_l) & e->mask;
+		item = snd_soc_enum_val_to_item(e, val);
+		ucontrol->value.enumerated.item[1] = item;
+	}
 
 	return 0;
 }
@@ -2623,17 +2628,18 @@ int snd_soc_put_enum_double(struct snd_kcontrol *kcontrol,
 {
 	struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
 	struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+	unsigned int *item = ucontrol->value.enumerated.item;
 	unsigned int val;
 	unsigned int mask;
 
-	if (ucontrol->value.enumerated.item[0] > e->max - 1)
+	if (item[0] >= e->items)
 		return -EINVAL;
-	val = ucontrol->value.enumerated.item[0] << e->shift_l;
+	val = snd_soc_enum_item_to_val(e, item[0]) << e->shift_l;
 	mask = e->mask << e->shift_l;
 	if (e->shift_l != e->shift_r) {
-		if (ucontrol->value.enumerated.item[1] > e->max - 1)
+		if (item[1] >= e->items)
 			return -EINVAL;
-		val |= ucontrol->value.enumerated.item[1] << e->shift_r;
+		val |= snd_soc_enum_item_to_val(e, item[1]) << e->shift_r;
 		mask |= e->mask << e->shift_r;
 	}
 
@@ -2642,78 +2648,46 @@ int snd_soc_put_enum_double(struct snd_kcontrol *kcontrol,
 EXPORT_SYMBOL_GPL(snd_soc_put_enum_double);
 
 /**
- * snd_soc_get_value_enum_double - semi enumerated double mixer get callback
- * @kcontrol: mixer control
- * @ucontrol: control element information
- *
- * Callback to get the value of a double semi enumerated mixer.
+ * snd_soc_read_signed - Read a codec register and interprete as signed value
+ * @codec: codec
+ * @reg: Register to read
+ * @mask: Mask to use after shifting the register value
+ * @shift: Right shift of register value
+ * @sign_bit: Bit that describes if a number is negative or not.
  *
- * Semi enumerated mixer: the enumerated items are referred as values. Can be
- * used for handling bitfield coded enumeration for example.
+ * This functions reads a codec register. The register value is shifted right
+ * by 'shift' bits and masked with the given 'mask'. Afterwards it translates
+ * the given registervalue into a signed integer if sign_bit is non-zero.
  *
- * Returns 0 for success.
+ * Returns the register value as signed int.
  */
-int snd_soc_get_value_enum_double(struct snd_kcontrol *kcontrol,
-	struct snd_ctl_elem_value *ucontrol)
+static int snd_soc_read_signed(struct snd_soc_codec *codec, unsigned int reg,
+		unsigned int mask, unsigned int shift, unsigned int sign_bit)
 {
-	struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
-	struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
-	unsigned int reg_val, val, mux;
+	int ret;
+	unsigned int val;
 
-	reg_val = snd_soc_read(codec, e->reg);
-	val = (reg_val >> e->shift_l) & e->mask;
-	for (mux = 0; mux < e->max; mux++) {
-		if (val == e->values[mux])
-			break;
-	}
-	ucontrol->value.enumerated.item[0] = mux;
-	if (e->shift_l != e->shift_r) {
-		val = (reg_val >> e->shift_r) & e->mask;
-		for (mux = 0; mux < e->max; mux++) {
-			if (val == e->values[mux])
-				break;
-		}
-		ucontrol->value.enumerated.item[1] = mux;
-	}
+	val = (snd_soc_read(codec, reg) >> shift) & mask;
 
-	return 0;
-}
-EXPORT_SYMBOL_GPL(snd_soc_get_value_enum_double);
+	if (!sign_bit)
+		return val;
 
-/**
- * snd_soc_put_value_enum_double - semi enumerated double mixer put callback
- * @kcontrol: mixer control
- * @ucontrol: control element information
- *
- * Callback to set the value of a double semi enumerated mixer.
- *
- * Semi enumerated mixer: the enumerated items are referred as values. Can be
- * used for handling bitfield coded enumeration for example.
- *
- * Returns 0 for success.
- */
-int snd_soc_put_value_enum_double(struct snd_kcontrol *kcontrol,
-	struct snd_ctl_elem_value *ucontrol)
-{
-	struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
-	struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
-	unsigned int val;
-	unsigned int mask;
+	/* non-negative number */
+	if (!(val & BIT(sign_bit)))
+		return val;
 
-	if (ucontrol->value.enumerated.item[0] > e->max - 1)
-		return -EINVAL;
-	val = e->values[ucontrol->value.enumerated.item[0]] << e->shift_l;
-	mask = e->mask << e->shift_l;
-	if (e->shift_l != e->shift_r) {
-		if (ucontrol->value.enumerated.item[1] > e->max - 1)
-			return -EINVAL;
-		val |= e->values[ucontrol->value.enumerated.item[1]] << e->shift_r;
-		mask |= e->mask << e->shift_r;
-	}
+	ret = val;
 
-	return snd_soc_update_bits_locked(codec, e->reg, mask, val);
+	/*
+	 * The register most probably does not contain a full-sized int.
+	 * Instead we have an arbitrary number of bits in a signed
+	 * representation which has to be translated into a full-sized int.
+	 * This is done by filling up all bits above the sign-bit.
+	 */
+	ret |= ~((int)(BIT(sign_bit) - 1));
+
+	return ret;
 }
-EXPORT_SYMBOL_GPL(snd_soc_put_value_enum_double);
 
 /**
  * snd_soc_info_volsw - single mixer info callback
@@ -2743,7 +2717,7 @@ int snd_soc_info_volsw(struct snd_kcontrol *kcontrol,
 
 	uinfo->count = snd_soc_volsw_is_stereo(mc) ? 2 : 1;
 	uinfo->value.integer.min = 0;
-	uinfo->value.integer.max = platform_max;
+	uinfo->value.integer.max = platform_max - mc->min;
 	return 0;
 }
 EXPORT_SYMBOL_GPL(snd_soc_info_volsw);
@@ -2769,11 +2743,16 @@ int snd_soc_get_volsw(struct snd_kcontrol *kcontrol,
 	unsigned int shift = mc->shift;
 	unsigned int rshift = mc->rshift;
 	int max = mc->max;
+	int min = mc->min;
+	int sign_bit = mc->sign_bit;
 	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 (sign_bit)
+		mask = BIT(sign_bit + 1) - 1;
+
+	ucontrol->value.integer.value[0] = snd_soc_read_signed(codec, reg, mask,
+			shift, sign_bit) - min;
 	if (invert)
 		ucontrol->value.integer.value[0] =
 			max - ucontrol->value.integer.value[0];
@@ -2781,10 +2760,12 @@ int snd_soc_get_volsw(struct snd_kcontrol *kcontrol,
 	if (snd_soc_volsw_is_stereo(mc)) {
 		if (reg == reg2)
 			ucontrol->value.integer.value[1] =
-				(snd_soc_read(codec, reg) >> rshift) & mask;
+				snd_soc_read_signed(codec, reg, mask, rshift,
+						sign_bit) - min;
 		else
 			ucontrol->value.integer.value[1] =
-				(snd_soc_read(codec, reg2) >> shift) & mask;
+				snd_soc_read_signed(codec, reg2, mask, shift,
+						sign_bit) - min;
 		if (invert)
 			ucontrol->value.integer.value[1] =
 				max - ucontrol->value.integer.value[1];
@@ -2815,20 +2796,25 @@ int snd_soc_put_volsw(struct snd_kcontrol *kcontrol,
 	unsigned int shift = mc->shift;
 	unsigned int rshift = mc->rshift;
 	int max = mc->max;
+	int min = mc->min;
+	unsigned int sign_bit = mc->sign_bit;
 	unsigned int mask = (1 << fls(max)) - 1;
 	unsigned int invert = mc->invert;
 	int err;
-	bool type_2r = 0;
+	bool type_2r = false;
 	unsigned int val2 = 0;
 	unsigned int val, val_mask;
 
-	val = (ucontrol->value.integer.value[0] & mask);
+	if (sign_bit)
+		mask = BIT(sign_bit + 1) - 1;
+
+	val = ((ucontrol->value.integer.value[0] + min) & mask);
 	if (invert)
 		val = max - val;
 	val_mask = mask << shift;
 	val = val << shift;
 	if (snd_soc_volsw_is_stereo(mc)) {
-		val2 = (ucontrol->value.integer.value[1] & mask);
+		val2 = ((ucontrol->value.integer.value[1] + min) & mask);
 		if (invert)
 			val2 = max - val2;
 		if (reg == reg2) {
@@ -2836,7 +2822,7 @@ int snd_soc_put_volsw(struct snd_kcontrol *kcontrol,
 			val |= val2 << rshift;
 		} else {
 			val2 = val2 << shift;
-			type_2r = 1;
+			type_2r = true;
 		}
 	}
 	err = snd_soc_update_bits_locked(codec, reg, val_mask, val);
@@ -3234,7 +3220,7 @@ int snd_soc_bytes_put(struct snd_kcontrol *kcontrol,
 	struct soc_bytes *params = (void *)kcontrol->private_value;
 	struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
 	int ret, len;
-	unsigned int val;
+	unsigned int val, mask;
 	void *data;
 
 	if (!codec->using_regmap)
@@ -3264,12 +3250,36 @@ int snd_soc_bytes_put(struct snd_kcontrol *kcontrol,
 			((u8 *)data)[0] |= val;
 			break;
 		case 2:
-			((u16 *)data)[0] &= cpu_to_be16(~params->mask);
-			((u16 *)data)[0] |= cpu_to_be16(val);
+			mask = ~params->mask;
+			ret = regmap_parse_val(codec->control_data,
+							&mask, &mask);
+			if (ret != 0)
+				goto out;
+
+			((u16 *)data)[0] &= mask;
+
+			ret = regmap_parse_val(codec->control_data,
+							&val, &val);
+			if (ret != 0)
+				goto out;
+
+			((u16 *)data)[0] |= val;
 			break;
 		case 4:
-			((u32 *)data)[0] &= cpu_to_be32(~params->mask);
-			((u32 *)data)[0] |= cpu_to_be32(val);
+			mask = ~params->mask;
+			ret = regmap_parse_val(codec->control_data,
+							&mask, &mask);
+			if (ret != 0)
+				goto out;
+
+			((u32 *)data)[0] &= mask;
+
+			ret = regmap_parse_val(codec->control_data,
+							&val, &val);
+			if (ret != 0)
+				goto out;
+
+			((u32 *)data)[0] |= val;
 			break;
 		default:
 			ret = -EINVAL;
@@ -3609,6 +3619,30 @@ int snd_soc_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
 EXPORT_SYMBOL_GPL(snd_soc_dai_set_fmt);
 
 /**
+ * snd_soc_of_xlate_tdm_slot - generate tx/rx slot mask.
+ * @slots: Number of slots in use.
+ * @tx_mask: bitmask representing active TX slots.
+ * @rx_mask: bitmask representing active RX slots.
+ *
+ * Generates the TDM tx and rx slot default masks for DAI.
+ */
+static int snd_soc_of_xlate_tdm_slot_mask(unsigned int slots,
+					  unsigned int *tx_mask,
+					  unsigned int *rx_mask)
+{
+	if (*tx_mask || *rx_mask)
+		return 0;
+
+	if (!slots)
+		return -EINVAL;
+
+	*tx_mask = (1 << slots) - 1;
+	*rx_mask = (1 << slots) - 1;
+
+	return 0;
+}
+
+/**
  * snd_soc_dai_set_tdm_slot - configure DAI TDM.
  * @dai: DAI
  * @tx_mask: bitmask representing active TX slots.
@@ -3622,11 +3656,17 @@ EXPORT_SYMBOL_GPL(snd_soc_dai_set_fmt);
 int snd_soc_dai_set_tdm_slot(struct snd_soc_dai *dai,
 	unsigned int tx_mask, unsigned int rx_mask, int slots, int slot_width)
 {
+	if (dai->driver && dai->driver->ops->of_xlate_tdm_slot_mask)
+		dai->driver->ops->of_xlate_tdm_slot_mask(slots,
+						&tx_mask, &rx_mask);
+	else
+		snd_soc_of_xlate_tdm_slot_mask(slots, &tx_mask, &rx_mask);
+
 	if (dai->driver && dai->driver->ops->set_tdm_slot)
 		return dai->driver->ops->set_tdm_slot(dai, tx_mask, rx_mask,
 				slots, slot_width);
 	else
-		return -EINVAL;
+		return -ENOTSUPP;
 }
 EXPORT_SYMBOL_GPL(snd_soc_dai_set_tdm_slot);
 
@@ -3882,95 +3922,42 @@ static inline char *fmt_multiple_name(struct device *dev,
 }
 
 /**
- * snd_soc_register_dai - Register a DAI with the ASoC core
+ * snd_soc_unregister_dai - Unregister DAIs from the ASoC core
  *
- * @dai: DAI to register
+ * @component: The component for which the DAIs should be unregistered
  */
-static int snd_soc_register_dai(struct device *dev,
-		struct snd_soc_dai_driver *dai_drv)
+static void snd_soc_unregister_dais(struct snd_soc_component *component)
 {
-	struct snd_soc_codec *codec;
-	struct snd_soc_dai *dai;
-
-	dev_dbg(dev, "ASoC: dai register %s\n", dev_name(dev));
+	struct snd_soc_dai *dai, *_dai;
 
-	dai = kzalloc(sizeof(struct snd_soc_dai), GFP_KERNEL);
-	if (dai == NULL)
-		return -ENOMEM;
-
-	/* create DAI component name */
-	dai->name = fmt_single_name(dev, &dai->id);
-	if (dai->name == NULL) {
+	list_for_each_entry_safe(dai, _dai, &component->dai_list, list) {
+		dev_dbg(component->dev, "ASoC: Unregistered DAI '%s'\n",
+			dai->name);
+		list_del(&dai->list);
+		kfree(dai->name);
 		kfree(dai);
-		return -ENOMEM;
-	}
-
-	dai->dev = dev;
-	dai->driver = dai_drv;
-	dai->dapm.dev = dev;
-	if (!dai->driver->ops)
-		dai->driver->ops = &null_dai_ops;
-
-	mutex_lock(&client_mutex);
-
-	list_for_each_entry(codec, &codec_list, list) {
-		if (codec->dev == dev) {
-			dev_dbg(dev, "ASoC: Mapped DAI %s to CODEC %s\n",
-				dai->name, codec->name);
-			dai->codec = codec;
-			break;
-		}
 	}
-
-	if (!dai->codec)
-		dai->dapm.idle_bias_off = 1;
-
-	list_add(&dai->list, &dai_list);
-
-	mutex_unlock(&client_mutex);
-
-	dev_dbg(dev, "ASoC: Registered DAI '%s'\n", dai->name);
-
-	return 0;
 }
 
 /**
- * snd_soc_unregister_dai - Unregister a DAI from the ASoC core
+ * snd_soc_register_dais - Register a DAI with the ASoC core
  *
- * @dai: DAI to unregister
- */
-static void snd_soc_unregister_dai(struct device *dev)
-{
-	struct snd_soc_dai *dai;
-
-	list_for_each_entry(dai, &dai_list, list) {
-		if (dev == dai->dev)
-			goto found;
-	}
-	return;
-
-found:
-	mutex_lock(&client_mutex);
-	list_del(&dai->list);
-	mutex_unlock(&client_mutex);
-
-	dev_dbg(dev, "ASoC: Unregistered DAI '%s'\n", dai->name);
-	kfree(dai->name);
-	kfree(dai);
-}
-
-/**
- * snd_soc_register_dais - Register multiple DAIs with the ASoC core
- *
- * @dai: Array of DAIs to register
+ * @component: The component the DAIs are registered for
+ * @codec: The CODEC that the DAIs are registered for, NULL if the component is
+ *         not a CODEC.
+ * @dai_drv: DAI driver to use for the DAIs
  * @count: Number of DAIs
+ * @legacy_dai_naming: Use the legacy naming scheme and let the DAI inherit the
+ *                     parent's name.
  */
-static int snd_soc_register_dais(struct device *dev,
-		struct snd_soc_dai_driver *dai_drv, size_t count)
+static int snd_soc_register_dais(struct snd_soc_component *component,
+	struct snd_soc_codec *codec, struct snd_soc_dai_driver *dai_drv,
+	size_t count, bool legacy_dai_naming)
 {
-	struct snd_soc_codec *codec;
+	struct device *dev = component->dev;
 	struct snd_soc_dai *dai;
-	int i, ret = 0;
+	unsigned int i;
+	int ret;
 
 	dev_dbg(dev, "ASoC: dai register %s #%Zu\n", dev_name(dev), count);
 
@@ -3982,70 +3969,54 @@ static int snd_soc_register_dais(struct device *dev,
 			goto err;
 		}
 
-		/* create DAI component name */
-		dai->name = fmt_multiple_name(dev, &dai_drv[i]);
+		/*
+		 * Back in the old days when we still had component-less DAIs,
+		 * instead of having a static name, component-less DAIs would
+		 * inherit the name of the parent device so it is possible to
+		 * register multiple instances of the DAI. We still need to keep
+		 * the same naming style even though those DAIs are not
+		 * component-less anymore.
+		 */
+		if (count == 1 && legacy_dai_naming) {
+			dai->name = fmt_single_name(dev, &dai->id);
+		} else {
+			dai->name = fmt_multiple_name(dev, &dai_drv[i]);
+			if (dai_drv[i].id)
+				dai->id = dai_drv[i].id;
+			else
+				dai->id = i;
+		}
 		if (dai->name == NULL) {
 			kfree(dai);
-			ret = -EINVAL;
+			ret = -ENOMEM;
 			goto err;
 		}
 
+		dai->component = component;
+		dai->codec = codec;
 		dai->dev = dev;
 		dai->driver = &dai_drv[i];
-		if (dai->driver->id)
-			dai->id = dai->driver->id;
-		else
-			dai->id = i;
 		dai->dapm.dev = dev;
 		if (!dai->driver->ops)
 			dai->driver->ops = &null_dai_ops;
 
-		mutex_lock(&client_mutex);
-
-		list_for_each_entry(codec, &codec_list, list) {
-			if (codec->dev == dev) {
-				dev_dbg(dev,
-					"ASoC: Mapped DAI %s to CODEC %s\n",
-					dai->name, codec->name);
-				dai->codec = codec;
-				break;
-			}
-		}
-
 		if (!dai->codec)
 			dai->dapm.idle_bias_off = 1;
 
-		list_add(&dai->list, &dai_list);
+		list_add(&dai->list, &component->dai_list);
 
-		mutex_unlock(&client_mutex);
-
-		dev_dbg(dai->dev, "ASoC: Registered DAI '%s'\n", dai->name);
+		dev_dbg(dev, "ASoC: Registered DAI '%s'\n", dai->name);
 	}
 
 	return 0;
 
 err:
-	for (i--; i >= 0; i--)
-		snd_soc_unregister_dai(dev);
+	snd_soc_unregister_dais(component);
 
 	return ret;
 }
 
 /**
- * snd_soc_unregister_dais - Unregister multiple DAIs from the ASoC core
- *
- * @dai: Array of DAIs to unregister
- * @count: Number of DAIs
- */
-static void snd_soc_unregister_dais(struct device *dev, size_t count)
-{
-	int i;
-
-	for (i = 0; i < count; i++)
-		snd_soc_unregister_dai(dev);
-}
-
-/**
  * snd_soc_register_component - Register a component with the ASoC core
  *
  */
@@ -4053,6 +4024,7 @@ static int
 __snd_soc_register_component(struct device *dev,
 			     struct snd_soc_component *cmpnt,
 			     const struct snd_soc_component_driver *cmpnt_drv,
+			     struct snd_soc_codec *codec,
 			     struct snd_soc_dai_driver *dai_drv,
 			     int num_dai, bool allow_single_dai)
 {
@@ -4075,20 +4047,10 @@ __snd_soc_register_component(struct device *dev,
 	cmpnt->driver	= cmpnt_drv;
 	cmpnt->dai_drv	= dai_drv;
 	cmpnt->num_dai	= num_dai;
+	INIT_LIST_HEAD(&cmpnt->dai_list);
 
-	/*
-	 * snd_soc_register_dai()  uses fmt_single_name(), and
-	 * snd_soc_register_dais() uses fmt_multiple_name()
-	 * for dai->name which is used for name based matching
-	 *
-	 * this function is used from cpu/codec.
-	 * allow_single_dai flag can ignore "codec" driver reworking
-	 * since it had been used snd_soc_register_dais(),
-	 */
-	if ((1 == num_dai) && allow_single_dai)
-		ret = snd_soc_register_dai(dev, dai_drv);
-	else
-		ret = snd_soc_register_dais(dev, dai_drv, num_dai);
+	ret = snd_soc_register_dais(cmpnt, codec, dai_drv, num_dai,
+		allow_single_dai);
 	if (ret < 0) {
 		dev_err(dev, "ASoC: Failed to regster DAIs: %d\n", ret);
 		goto error_component_name;
@@ -4121,7 +4083,9 @@ int snd_soc_register_component(struct device *dev,
 		return -ENOMEM;
 	}
 
-	return __snd_soc_register_component(dev, cmpnt, cmpnt_drv,
+	cmpnt->ignore_pmdown_time = true;
+
+	return __snd_soc_register_component(dev, cmpnt, cmpnt_drv, NULL,
 					    dai_drv, num_dai, true);
 }
 EXPORT_SYMBOL_GPL(snd_soc_register_component);
@@ -4141,7 +4105,7 @@ void snd_soc_unregister_component(struct device *dev)
 	return;
 
 found:
-	snd_soc_unregister_dais(dev, cmpnt->num_dai);
+	snd_soc_unregister_dais(cmpnt);
 
 	mutex_lock(&client_mutex);
 	list_del(&cmpnt->list);
@@ -4319,7 +4283,7 @@ int snd_soc_register_codec(struct device *dev,
 	codec->volatile_register = codec_drv->volatile_register;
 	codec->readable_register = codec_drv->readable_register;
 	codec->writable_register = codec_drv->writable_register;
-	codec->ignore_pmdown_time = codec_drv->ignore_pmdown_time;
+	codec->component.ignore_pmdown_time = codec_drv->ignore_pmdown_time;
 	codec->dapm.bias_level = SND_SOC_BIAS_OFF;
 	codec->dapm.dev = dev;
 	codec->dapm.codec = codec;
@@ -4342,7 +4306,7 @@ int snd_soc_register_codec(struct device *dev,
 	/* register component */
 	ret = __snd_soc_register_component(dev, &codec->component,
 					   &codec_drv->component_driver,
-					   dai_drv, num_dai, false);
+					   codec, dai_drv, num_dai, false);
 	if (ret < 0) {
 		dev_err(codec->dev, "ASoC: Failed to regster component: %d\n", ret);
 		goto fail_codec_name;
@@ -4417,6 +4381,122 @@ int snd_soc_of_parse_card_name(struct snd_soc_card *card,
 }
 EXPORT_SYMBOL_GPL(snd_soc_of_parse_card_name);
 
+static const struct snd_soc_dapm_widget simple_widgets[] = {
+	SND_SOC_DAPM_MIC("Microphone", NULL),
+	SND_SOC_DAPM_LINE("Line", NULL),
+	SND_SOC_DAPM_HP("Headphone", NULL),
+	SND_SOC_DAPM_SPK("Speaker", NULL),
+};
+
+int snd_soc_of_parse_audio_simple_widgets(struct snd_soc_card *card,
+					  const char *propname)
+{
+	struct device_node *np = card->dev->of_node;
+	struct snd_soc_dapm_widget *widgets;
+	const char *template, *wname;
+	int i, j, num_widgets, ret;
+
+	num_widgets = of_property_count_strings(np, propname);
+	if (num_widgets < 0) {
+		dev_err(card->dev,
+			"ASoC: Property '%s' does not exist\n",	propname);
+		return -EINVAL;
+	}
+	if (num_widgets & 1) {
+		dev_err(card->dev,
+			"ASoC: Property '%s' length is not even\n", propname);
+		return -EINVAL;
+	}
+
+	num_widgets /= 2;
+	if (!num_widgets) {
+		dev_err(card->dev, "ASoC: Property '%s's length is zero\n",
+			propname);
+		return -EINVAL;
+	}
+
+	widgets = devm_kcalloc(card->dev, num_widgets, sizeof(*widgets),
+			       GFP_KERNEL);
+	if (!widgets) {
+		dev_err(card->dev,
+			"ASoC: Could not allocate memory for widgets\n");
+		return -ENOMEM;
+	}
+
+	for (i = 0; i < num_widgets; i++) {
+		ret = of_property_read_string_index(np, propname,
+			2 * i, &template);
+		if (ret) {
+			dev_err(card->dev,
+				"ASoC: Property '%s' index %d read error:%d\n",
+				propname, 2 * i, ret);
+			return -EINVAL;
+		}
+
+		for (j = 0; j < ARRAY_SIZE(simple_widgets); j++) {
+			if (!strncmp(template, simple_widgets[j].name,
+				     strlen(simple_widgets[j].name))) {
+				widgets[i] = simple_widgets[j];
+				break;
+			}
+		}
+
+		if (j >= ARRAY_SIZE(simple_widgets)) {
+			dev_err(card->dev,
+				"ASoC: DAPM widget '%s' is not supported\n",
+				template);
+			return -EINVAL;
+		}
+
+		ret = of_property_read_string_index(np, propname,
+						    (2 * i) + 1,
+						    &wname);
+		if (ret) {
+			dev_err(card->dev,
+				"ASoC: Property '%s' index %d read error:%d\n",
+				propname, (2 * i) + 1, ret);
+			return -EINVAL;
+		}
+
+		widgets[i].name = wname;
+	}
+
+	card->dapm_widgets = widgets;
+	card->num_dapm_widgets = num_widgets;
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(snd_soc_of_parse_audio_simple_widgets);
+
+int snd_soc_of_parse_tdm_slot(struct device_node *np,
+			      unsigned int *slots,
+			      unsigned int *slot_width)
+{
+	u32 val;
+	int ret;
+
+	if (of_property_read_bool(np, "dai-tdm-slot-num")) {
+		ret = of_property_read_u32(np, "dai-tdm-slot-num", &val);
+		if (ret)
+			return ret;
+
+		if (slots)
+			*slots = val;
+	}
+
+	if (of_property_read_bool(np, "dai-tdm-slot-width")) {
+		ret = of_property_read_u32(np, "dai-tdm-slot-width", &val);
+		if (ret)
+			return ret;
+
+		if (slot_width)
+			*slot_width = val;
+	}
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(snd_soc_of_parse_tdm_slot);
+
 int snd_soc_of_parse_audio_routing(struct snd_soc_card *card,
 				   const char *propname)
 {
diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c
index b9dc6acbba8c..c8a780d0d057 100644
--- a/sound/soc/soc-dapm.c
+++ b/sound/soc/soc-dapm.c
@@ -70,8 +70,6 @@ static int dapm_up_seq[] = {
 	[snd_soc_dapm_aif_out] = 4,
 	[snd_soc_dapm_mic] = 5,
 	[snd_soc_dapm_mux] = 6,
-	[snd_soc_dapm_virt_mux] = 6,
-	[snd_soc_dapm_value_mux] = 6,
 	[snd_soc_dapm_dac] = 7,
 	[snd_soc_dapm_switch] = 8,
 	[snd_soc_dapm_mixer] = 8,
@@ -102,8 +100,6 @@ static int dapm_down_seq[] = {
 	[snd_soc_dapm_mic] = 7,
 	[snd_soc_dapm_micbias] = 8,
 	[snd_soc_dapm_mux] = 9,
-	[snd_soc_dapm_virt_mux] = 9,
-	[snd_soc_dapm_value_mux] = 9,
 	[snd_soc_dapm_aif_in] = 10,
 	[snd_soc_dapm_aif_out] = 10,
 	[snd_soc_dapm_dai_in] = 10,
@@ -115,6 +111,12 @@ static int dapm_down_seq[] = {
 	[snd_soc_dapm_post] = 14,
 };
 
+static void dapm_assert_locked(struct snd_soc_dapm_context *dapm)
+{
+	if (dapm->card && dapm->card->instantiated)
+		lockdep_assert_held(&dapm->card->dapm_mutex);
+}
+
 static void pop_wait(u32 pop_time)
 {
 	if (pop_time)
@@ -146,15 +148,16 @@ static bool dapm_dirty_widget(struct snd_soc_dapm_widget *w)
 	return !list_empty(&w->dirty);
 }
 
-void dapm_mark_dirty(struct snd_soc_dapm_widget *w, const char *reason)
+static void dapm_mark_dirty(struct snd_soc_dapm_widget *w, const char *reason)
 {
+	dapm_assert_locked(w->dapm);
+
 	if (!dapm_dirty_widget(w)) {
 		dev_vdbg(w->dapm->dev, "Marking %s dirty due to %s\n",
 			 w->name, reason);
 		list_add_tail(&w->dirty, &w->dapm->card->dapm_dirty);
 	}
 }
-EXPORT_SYMBOL_GPL(dapm_mark_dirty);
 
 void dapm_mark_io_dirty(struct snd_soc_dapm_context *dapm)
 {
@@ -361,6 +364,8 @@ static void dapm_reset(struct snd_soc_card *card)
 {
 	struct snd_soc_dapm_widget *w;
 
+	lockdep_assert_held(&card->dapm_mutex);
+
 	memset(&card->dapm_stats, 0, sizeof(card->dapm_stats));
 
 	list_for_each_entry(w, &card->widgets, list) {
@@ -386,7 +391,8 @@ static int soc_widget_read(struct snd_soc_dapm_widget *w, int reg,
 	return -1;
 }
 
-static int soc_widget_write(struct snd_soc_dapm_widget *w, int reg, int val)
+static int soc_widget_write(struct snd_soc_dapm_widget *w, int reg,
+	unsigned int val)
 {
 	if (w->codec)
 		return snd_soc_write(w->codec, reg, val);
@@ -498,131 +504,40 @@ out:
 	return ret;
 }
 
-/* set up initial codec paths */
-static void dapm_set_path_status(struct snd_soc_dapm_widget *w,
-	struct snd_soc_dapm_path *p, int i)
+/* connect mux widget to its interconnecting audio paths */
+static int dapm_connect_mux(struct snd_soc_dapm_context *dapm,
+	struct snd_soc_dapm_widget *src, struct snd_soc_dapm_widget *dest,
+	struct snd_soc_dapm_path *path, const char *control_name,
+	const struct snd_kcontrol_new *kcontrol)
 {
-	switch (w->id) {
-	case snd_soc_dapm_switch:
-	case snd_soc_dapm_mixer:
-	case snd_soc_dapm_mixer_named_ctl: {
-		int val;
-		struct soc_mixer_control *mc = (struct soc_mixer_control *)
-			w->kcontrol_news[i].private_value;
-		int reg = mc->reg;
-		unsigned int shift = mc->shift;
-		int max = mc->max;
-		unsigned int mask = (1 << fls(max)) - 1;
-		unsigned int invert = mc->invert;
-
-		if (reg != SND_SOC_NOPM) {
-			soc_widget_read(w, reg, &val);
-			val = (val >> shift) & mask;
-			if (invert)
-				val = max - val;
-			p->connect = !!val;
-		} else {
-			p->connect = 0;
-		}
-
-	}
-	break;
-	case snd_soc_dapm_mux: {
-		struct soc_enum *e = (struct soc_enum *)
-			w->kcontrol_news[i].private_value;
-		int val, item;
-
-		soc_widget_read(w, e->reg, &val);
-		item = (val >> e->shift_l) & e->mask;
-
-		if (item < e->max && !strcmp(p->name, e->texts[item]))
-			p->connect = 1;
-		else
-			p->connect = 0;
-	}
-	break;
-	case snd_soc_dapm_virt_mux: {
-		struct soc_enum *e = (struct soc_enum *)
-			w->kcontrol_news[i].private_value;
+	struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+	unsigned int val, item;
+	int i;
 
-		p->connect = 0;
+	if (e->reg != SND_SOC_NOPM) {
+		soc_widget_read(dest, e->reg, &val);
+		val = (val >> e->shift_l) & e->mask;
+		item = snd_soc_enum_val_to_item(e, val);
+	} else {
 		/* since a virtual mux has no backing registers to
 		 * decide which path to connect, it will try to match
 		 * with the first enumeration.  This is to ensure
 		 * that the default mux choice (the first) will be
 		 * correctly powered up during initialization.
 		 */
-		if (!strcmp(p->name, e->texts[0]))
-			p->connect = 1;
+		item = 0;
 	}
-	break;
-	case snd_soc_dapm_value_mux: {
-		struct soc_enum *e = (struct soc_enum *)
-			w->kcontrol_news[i].private_value;
-		int val, item;
 
-		soc_widget_read(w, e->reg, &val);
-		val = (val >> e->shift_l) & e->mask;
-		for (item = 0; item < e->max; item++) {
-			if (val == e->values[item])
-				break;
-		}
-
-		if (item < e->max && !strcmp(p->name, e->texts[item]))
-			p->connect = 1;
-		else
-			p->connect = 0;
-	}
-	break;
-	/* does not affect routing - always connected */
-	case snd_soc_dapm_pga:
-	case snd_soc_dapm_out_drv:
-	case snd_soc_dapm_output:
-	case snd_soc_dapm_adc:
-	case snd_soc_dapm_input:
-	case snd_soc_dapm_siggen:
-	case snd_soc_dapm_dac:
-	case snd_soc_dapm_micbias:
-	case snd_soc_dapm_vmid:
-	case snd_soc_dapm_supply:
-	case snd_soc_dapm_regulator_supply:
-	case snd_soc_dapm_clock_supply:
-	case snd_soc_dapm_aif_in:
-	case snd_soc_dapm_aif_out:
-	case snd_soc_dapm_dai_in:
-	case snd_soc_dapm_dai_out:
-	case snd_soc_dapm_hp:
-	case snd_soc_dapm_mic:
-	case snd_soc_dapm_spk:
-	case snd_soc_dapm_line:
-	case snd_soc_dapm_dai_link:
-	case snd_soc_dapm_kcontrol:
-		p->connect = 1;
-	break;
-	/* does affect routing - dynamically connected */
-	case snd_soc_dapm_pre:
-	case snd_soc_dapm_post:
-		p->connect = 0;
-	break;
-	}
-}
-
-/* connect mux widget to its interconnecting audio paths */
-static int dapm_connect_mux(struct snd_soc_dapm_context *dapm,
-	struct snd_soc_dapm_widget *src, struct snd_soc_dapm_widget *dest,
-	struct snd_soc_dapm_path *path, const char *control_name,
-	const struct snd_kcontrol_new *kcontrol)
-{
-	struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
-	int i;
-
-	for (i = 0; i < e->max; i++) {
+	for (i = 0; i < e->items; i++) {
 		if (!(strcmp(control_name, e->texts[i]))) {
 			list_add(&path->list, &dapm->card->paths);
 			list_add(&path->list_sink, &dest->sources);
 			list_add(&path->list_source, &src->sinks);
 			path->name = (char*)e->texts[i];
-			dapm_set_path_status(dest, path, 0);
+			if (i == item)
+				path->connect = 1;
+			else
+				path->connect = 0;
 			return 0;
 		}
 	}
@@ -630,6 +545,30 @@ static int dapm_connect_mux(struct snd_soc_dapm_context *dapm,
 	return -ENODEV;
 }
 
+/* set up initial codec paths */
+static void dapm_set_mixer_path_status(struct snd_soc_dapm_widget *w,
+	struct snd_soc_dapm_path *p, int i)
+{
+	struct soc_mixer_control *mc = (struct soc_mixer_control *)
+		w->kcontrol_news[i].private_value;
+	unsigned int reg = mc->reg;
+	unsigned int shift = mc->shift;
+	unsigned int max = mc->max;
+	unsigned int mask = (1 << fls(max)) - 1;
+	unsigned int invert = mc->invert;
+	unsigned int val;
+
+	if (reg != SND_SOC_NOPM) {
+		soc_widget_read(w, reg, &val);
+		val = (val >> shift) & mask;
+		if (invert)
+			val = max - val;
+		p->connect = !!val;
+	} else {
+		p->connect = 0;
+	}
+}
+
 /* connect mixer widget to its interconnecting audio paths */
 static int dapm_connect_mixer(struct snd_soc_dapm_context *dapm,
 	struct snd_soc_dapm_widget *src, struct snd_soc_dapm_widget *dest,
@@ -644,7 +583,7 @@ static int dapm_connect_mixer(struct snd_soc_dapm_context *dapm,
 			list_add(&path->list_sink, &dest->sources);
 			list_add(&path->list_source, &src->sinks);
 			path->name = dest->kcontrol_news[i].name;
-			dapm_set_path_status(dest, path, i);
+			dapm_set_mixer_path_status(dest, path, i);
 			return 0;
 		}
 	}
@@ -723,8 +662,6 @@ static int dapm_create_or_share_mixmux_kcontrol(struct snd_soc_dapm_widget *w,
 				kcname_in_long_name = true;
 				break;
 			case snd_soc_dapm_mux:
-			case snd_soc_dapm_virt_mux:
-			case snd_soc_dapm_value_mux:
 				wname_in_long_name = true;
 				kcname_in_long_name = false;
 				break;
@@ -1823,6 +1760,8 @@ static int dapm_power_widgets(struct snd_soc_card *card, int event)
 	ASYNC_DOMAIN_EXCLUSIVE(async_domain);
 	enum snd_soc_bias_level bias;
 
+	lockdep_assert_held(&card->dapm_mutex);
+
 	trace_snd_soc_dapm_start(card);
 
 	list_for_each_entry(d, &card->dapm_list, list) {
@@ -1897,10 +1836,14 @@ static int dapm_power_widgets(struct snd_soc_card *card, int event)
 
 	trace_snd_soc_dapm_walk_done(card);
 
-	/* Run all the bias changes in parallel */
-	list_for_each_entry(d, &card->dapm_list, list)
-		async_schedule_domain(dapm_pre_sequence_async, d,
-					&async_domain);
+	/* Run card bias changes at first */
+	dapm_pre_sequence_async(&card->dapm, 0);
+	/* Run other bias changes in parallel */
+	list_for_each_entry(d, &card->dapm_list, list) {
+		if (d != &card->dapm)
+			async_schedule_domain(dapm_pre_sequence_async, d,
+						&async_domain);
+	}
 	async_synchronize_full_domain(&async_domain);
 
 	list_for_each_entry(w, &down_list, power_list) {
@@ -1920,10 +1863,14 @@ static int dapm_power_widgets(struct snd_soc_card *card, int event)
 	dapm_seq_run(card, &up_list, event, true);
 
 	/* Run all the bias changes in parallel */
-	list_for_each_entry(d, &card->dapm_list, list)
-		async_schedule_domain(dapm_post_sequence_async, d,
-					&async_domain);
+	list_for_each_entry(d, &card->dapm_list, list) {
+		if (d != &card->dapm)
+			async_schedule_domain(dapm_post_sequence_async, d,
+						&async_domain);
+	}
 	async_synchronize_full_domain(&async_domain);
+	/* Run card bias changes at last */
+	dapm_post_sequence_async(&card->dapm, 0);
 
 	/* do we need to notify any clients that DAPM event is complete */
 	list_for_each_entry(d, &card->dapm_list, list) {
@@ -2110,6 +2057,8 @@ static int soc_dapm_mux_update_power(struct snd_soc_card *card,
 	struct snd_soc_dapm_path *path;
 	int found = 0;
 
+	lockdep_assert_held(&card->dapm_mutex);
+
 	/* find dapm widget path assoc with kcontrol */
 	dapm_kcontrol_for_each_path(path, kcontrol) {
 		if (!path->name || !e->texts[mux])
@@ -2160,6 +2109,8 @@ static int soc_dapm_mixer_update_power(struct snd_soc_card *card,
 	struct snd_soc_dapm_path *path;
 	int found = 0;
 
+	lockdep_assert_held(&card->dapm_mutex);
+
 	/* find dapm widget path assoc with kcontrol */
 	dapm_kcontrol_for_each_path(path, kcontrol) {
 		found = 1;
@@ -2325,6 +2276,8 @@ static int snd_soc_dapm_set_pin(struct snd_soc_dapm_context *dapm,
 {
 	struct snd_soc_dapm_widget *w = dapm_find_widget(dapm, pin, true);
 
+	dapm_assert_locked(dapm);
+
 	if (!w) {
 		dev_err(dapm->dev, "ASoC: DAPM unknown pin %s\n", pin);
 		return -EINVAL;
@@ -2341,18 +2294,18 @@ static int snd_soc_dapm_set_pin(struct snd_soc_dapm_context *dapm,
 }
 
 /**
- * snd_soc_dapm_sync - scan and power dapm paths
+ * snd_soc_dapm_sync_unlocked - scan and power dapm paths
  * @dapm: DAPM context
  *
  * Walks all dapm audio paths and powers widgets according to their
  * stream or path usage.
  *
+ * Requires external locking.
+ *
  * Returns 0 for success.
  */
-int snd_soc_dapm_sync(struct snd_soc_dapm_context *dapm)
+int snd_soc_dapm_sync_unlocked(struct snd_soc_dapm_context *dapm)
 {
-	int ret;
-
 	/*
 	 * Suppress early reports (eg, jacks syncing their state) to avoid
 	 * silly DAPM runs during card startup.
@@ -2360,8 +2313,25 @@ int snd_soc_dapm_sync(struct snd_soc_dapm_context *dapm)
 	if (!dapm->card || !dapm->card->instantiated)
 		return 0;
 
+	return dapm_power_widgets(dapm->card, SND_SOC_DAPM_STREAM_NOP);
+}
+EXPORT_SYMBOL_GPL(snd_soc_dapm_sync_unlocked);
+
+/**
+ * snd_soc_dapm_sync - scan and power dapm paths
+ * @dapm: DAPM context
+ *
+ * Walks all dapm audio paths and powers widgets according to their
+ * stream or path usage.
+ *
+ * Returns 0 for success.
+ */
+int snd_soc_dapm_sync(struct snd_soc_dapm_context *dapm)
+{
+	int ret;
+
 	mutex_lock_nested(&dapm->card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME);
-	ret = dapm_power_widgets(dapm->card, SND_SOC_DAPM_STREAM_NOP);
+	ret = snd_soc_dapm_sync_unlocked(dapm);
 	mutex_unlock(&dapm->card->dapm_mutex);
 	return ret;
 }
@@ -2444,8 +2414,6 @@ static int snd_soc_dapm_add_path(struct snd_soc_dapm_context *dapm,
 		path->connect = 1;
 		return 0;
 	case snd_soc_dapm_mux:
-	case snd_soc_dapm_virt_mux:
-	case snd_soc_dapm_value_mux:
 		ret = dapm_connect_mux(dapm, wsource, wsink, path, control,
 			&wsink->kcontrol_news[0]);
 		if (ret != 0)
@@ -2772,8 +2740,6 @@ int snd_soc_dapm_new_widgets(struct snd_soc_card *card)
 			dapm_new_mixer(w);
 			break;
 		case snd_soc_dapm_mux:
-		case snd_soc_dapm_virt_mux:
-		case snd_soc_dapm_value_mux:
 			dapm_new_mux(w);
 			break;
 		case snd_soc_dapm_pga:
@@ -2935,213 +2901,75 @@ int snd_soc_dapm_get_enum_double(struct snd_kcontrol *kcontrol,
 {
 	struct snd_soc_codec *codec = snd_soc_dapm_kcontrol_codec(kcontrol);
 	struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
-	unsigned int val;
-
-	val = snd_soc_read(codec, e->reg);
-	ucontrol->value.enumerated.item[0] = (val >> e->shift_l) & e->mask;
-	if (e->shift_l != e->shift_r)
-		ucontrol->value.enumerated.item[1] =
-			(val >> e->shift_r) & e->mask;
-
-	return 0;
-}
-EXPORT_SYMBOL_GPL(snd_soc_dapm_get_enum_double);
-
-/**
- * snd_soc_dapm_put_enum_double - dapm enumerated double mixer set callback
- * @kcontrol: mixer control
- * @ucontrol: control element information
- *
- * Callback to set the value of a dapm enumerated double mixer control.
- *
- * Returns 0 for success.
- */
-int snd_soc_dapm_put_enum_double(struct snd_kcontrol *kcontrol,
-	struct snd_ctl_elem_value *ucontrol)
-{
-	struct snd_soc_codec *codec = snd_soc_dapm_kcontrol_codec(kcontrol);
-	struct snd_soc_card *card = codec->card;
-	struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
-	unsigned int val, mux, change;
-	unsigned int mask;
-	struct snd_soc_dapm_update update;
-	int ret = 0;
-
-	if (ucontrol->value.enumerated.item[0] > e->max - 1)
-		return -EINVAL;
-	mux = ucontrol->value.enumerated.item[0];
-	val = mux << e->shift_l;
-	mask = e->mask << e->shift_l;
-	if (e->shift_l != e->shift_r) {
-		if (ucontrol->value.enumerated.item[1] > e->max - 1)
-			return -EINVAL;
-		val |= ucontrol->value.enumerated.item[1] << e->shift_r;
-		mask |= e->mask << e->shift_r;
-	}
-
-	mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME);
-
-	change = snd_soc_test_bits(codec, e->reg, mask, val);
-	if (change) {
-		update.kcontrol = kcontrol;
-		update.reg = e->reg;
-		update.mask = mask;
-		update.val = val;
-		card->update = &update;
-
-		ret = soc_dapm_mux_update_power(card, kcontrol, mux, e);
-
-		card->update = NULL;
-	}
-
-	mutex_unlock(&card->dapm_mutex);
-
-	if (ret > 0)
-		soc_dpcm_runtime_update(card);
-
-	return change;
-}
-EXPORT_SYMBOL_GPL(snd_soc_dapm_put_enum_double);
-
-/**
- * snd_soc_dapm_get_enum_virt - Get virtual DAPM mux
- * @kcontrol: mixer control
- * @ucontrol: control element information
- *
- * Returns 0 for success.
- */
-int snd_soc_dapm_get_enum_virt(struct snd_kcontrol *kcontrol,
-			       struct snd_ctl_elem_value *ucontrol)
-{
-	ucontrol->value.enumerated.item[0] = dapm_kcontrol_get_value(kcontrol);
-	return 0;
-}
-EXPORT_SYMBOL_GPL(snd_soc_dapm_get_enum_virt);
-
-/**
- * snd_soc_dapm_put_enum_virt - Set virtual DAPM mux
- * @kcontrol: mixer control
- * @ucontrol: control element information
- *
- * Returns 0 for success.
- */
-int snd_soc_dapm_put_enum_virt(struct snd_kcontrol *kcontrol,
-			       struct snd_ctl_elem_value *ucontrol)
-{
-	struct snd_soc_codec *codec = snd_soc_dapm_kcontrol_codec(kcontrol);
-	struct snd_soc_card *card = codec->card;
-	unsigned int value;
-	struct soc_enum *e =
-		(struct soc_enum *)kcontrol->private_value;
-	int change;
-	int ret = 0;
-
-	if (ucontrol->value.enumerated.item[0] >= e->max)
-		return -EINVAL;
+	unsigned int reg_val, val;
 
-	mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME);
-
-	value = ucontrol->value.enumerated.item[0];
-	change = dapm_kcontrol_set_value(kcontrol, value);
-	if (change)
-		ret = soc_dapm_mux_update_power(card, kcontrol, value, e);
-
-	mutex_unlock(&card->dapm_mutex);
-
-	if (ret > 0)
-		soc_dpcm_runtime_update(card);
-
-	return change;
-}
-EXPORT_SYMBOL_GPL(snd_soc_dapm_put_enum_virt);
-
-/**
- * snd_soc_dapm_get_value_enum_double - dapm semi enumerated double mixer get
- *					callback
- * @kcontrol: mixer control
- * @ucontrol: control element information
- *
- * Callback to get the value of a dapm semi enumerated double mixer control.
- *
- * Semi enumerated mixer: the enumerated items are referred as values. Can be
- * used for handling bitfield coded enumeration for example.
- *
- * Returns 0 for success.
- */
-int snd_soc_dapm_get_value_enum_double(struct snd_kcontrol *kcontrol,
-	struct snd_ctl_elem_value *ucontrol)
-{
-	struct snd_soc_codec *codec = snd_soc_dapm_kcontrol_codec(kcontrol);
-	struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
-	unsigned int reg_val, val, mux;
+	if (e->reg != SND_SOC_NOPM)
+		reg_val = snd_soc_read(codec, e->reg);
+	else
+		reg_val = dapm_kcontrol_get_value(kcontrol);
 
-	reg_val = snd_soc_read(codec, e->reg);
 	val = (reg_val >> e->shift_l) & e->mask;
-	for (mux = 0; mux < e->max; mux++) {
-		if (val == e->values[mux])
-			break;
-	}
-	ucontrol->value.enumerated.item[0] = mux;
+	ucontrol->value.enumerated.item[0] = snd_soc_enum_val_to_item(e, val);
 	if (e->shift_l != e->shift_r) {
 		val = (reg_val >> e->shift_r) & e->mask;
-		for (mux = 0; mux < e->max; mux++) {
-			if (val == e->values[mux])
-				break;
-		}
-		ucontrol->value.enumerated.item[1] = mux;
+		val = snd_soc_enum_val_to_item(e, val);
+		ucontrol->value.enumerated.item[1] = val;
 	}
 
 	return 0;
 }
-EXPORT_SYMBOL_GPL(snd_soc_dapm_get_value_enum_double);
+EXPORT_SYMBOL_GPL(snd_soc_dapm_get_enum_double);
 
 /**
- * snd_soc_dapm_put_value_enum_double - dapm semi enumerated double mixer set
- *					callback
+ * snd_soc_dapm_put_enum_double - dapm enumerated double mixer set callback
  * @kcontrol: mixer control
  * @ucontrol: control element information
  *
- * Callback to set the value of a dapm semi enumerated double mixer control.
- *
- * Semi enumerated mixer: the enumerated items are referred as values. Can be
- * used for handling bitfield coded enumeration for example.
+ * Callback to set the value of a dapm enumerated double mixer control.
  *
  * Returns 0 for success.
  */
-int snd_soc_dapm_put_value_enum_double(struct snd_kcontrol *kcontrol,
+int snd_soc_dapm_put_enum_double(struct snd_kcontrol *kcontrol,
 	struct snd_ctl_elem_value *ucontrol)
 {
 	struct snd_soc_codec *codec = snd_soc_dapm_kcontrol_codec(kcontrol);
 	struct snd_soc_card *card = codec->card;
 	struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
-	unsigned int val, mux, change;
+	unsigned int *item = ucontrol->value.enumerated.item;
+	unsigned int val, change;
 	unsigned int mask;
 	struct snd_soc_dapm_update update;
 	int ret = 0;
 
-	if (ucontrol->value.enumerated.item[0] > e->max - 1)
+	if (item[0] >= e->items)
 		return -EINVAL;
-	mux = ucontrol->value.enumerated.item[0];
-	val = e->values[ucontrol->value.enumerated.item[0]] << e->shift_l;
+
+	val = snd_soc_enum_item_to_val(e, item[0]) << e->shift_l;
 	mask = e->mask << e->shift_l;
 	if (e->shift_l != e->shift_r) {
-		if (ucontrol->value.enumerated.item[1] > e->max - 1)
+		if (item[1] > e->items)
 			return -EINVAL;
-		val |= e->values[ucontrol->value.enumerated.item[1]] << e->shift_r;
+		val |= snd_soc_enum_item_to_val(e, item[1]) << e->shift_l;
 		mask |= e->mask << e->shift_r;
 	}
 
 	mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME);
 
-	change = snd_soc_test_bits(codec, e->reg, mask, val);
+	if (e->reg != SND_SOC_NOPM)
+		change = snd_soc_test_bits(codec, e->reg, mask, val);
+	else
+		change = dapm_kcontrol_set_value(kcontrol, val);
+
 	if (change) {
-		update.kcontrol = kcontrol;
-		update.reg = e->reg;
-		update.mask = mask;
-		update.val = val;
-		card->update = &update;
+		if (e->reg != SND_SOC_NOPM) {
+			update.kcontrol = kcontrol;
+			update.reg = e->reg;
+			update.mask = mask;
+			update.val = val;
+			card->update = &update;
+		}
 
-		ret = soc_dapm_mux_update_power(card, kcontrol, mux, e);
+		ret = soc_dapm_mux_update_power(card, kcontrol, item[0], e);
 
 		card->update = NULL;
 	}
@@ -3153,7 +2981,7 @@ int snd_soc_dapm_put_value_enum_double(struct snd_kcontrol *kcontrol,
 
 	return change;
 }
-EXPORT_SYMBOL_GPL(snd_soc_dapm_put_value_enum_double);
+EXPORT_SYMBOL_GPL(snd_soc_dapm_put_enum_double);
 
 /**
  * snd_soc_dapm_info_pin_switch - Info for a pin switch
@@ -3283,8 +3111,6 @@ snd_soc_dapm_new_control(struct snd_soc_dapm_context *dapm,
 		w->power_check = dapm_generic_check_power;
 		break;
 	case snd_soc_dapm_mux:
-	case snd_soc_dapm_virt_mux:
-	case snd_soc_dapm_value_mux:
 		w->power_check = dapm_generic_check_power;
 		break;
 	case snd_soc_dapm_dai_out:
@@ -4098,7 +3924,7 @@ void snd_soc_dapm_free(struct snd_soc_dapm_context *dapm)
 }
 EXPORT_SYMBOL_GPL(snd_soc_dapm_free);
 
-static void soc_dapm_shutdown_codec(struct snd_soc_dapm_context *dapm)
+static void soc_dapm_shutdown_dapm(struct snd_soc_dapm_context *dapm)
 {
 	struct snd_soc_card *card = dapm->card;
 	struct snd_soc_dapm_widget *w;
@@ -4138,14 +3964,21 @@ static void soc_dapm_shutdown_codec(struct snd_soc_dapm_context *dapm)
  */
 void snd_soc_dapm_shutdown(struct snd_soc_card *card)
 {
-	struct snd_soc_codec *codec;
+	struct snd_soc_dapm_context *dapm;
 
-	list_for_each_entry(codec, &card->codec_dev_list, card_list) {
-		soc_dapm_shutdown_codec(&codec->dapm);
-		if (codec->dapm.bias_level == SND_SOC_BIAS_STANDBY)
-			snd_soc_dapm_set_bias_level(&codec->dapm,
-						    SND_SOC_BIAS_OFF);
+	list_for_each_entry(dapm, &card->dapm_list, list) {
+		if (dapm != &card->dapm) {
+			soc_dapm_shutdown_dapm(dapm);
+			if (dapm->bias_level == SND_SOC_BIAS_STANDBY)
+				snd_soc_dapm_set_bias_level(dapm,
+							    SND_SOC_BIAS_OFF);
+		}
 	}
+
+	soc_dapm_shutdown_dapm(&card->dapm);
+	if (card->dapm.bias_level == SND_SOC_BIAS_STANDBY)
+		snd_soc_dapm_set_bias_level(&card->dapm,
+					    SND_SOC_BIAS_OFF);
 }
 
 /* Module information */
diff --git a/sound/soc/soc-pcm.c b/sound/soc/soc-pcm.c
index 47e1ce771e65..2cedf09f6d96 100644
--- a/sound/soc/soc-pcm.c
+++ b/sound/soc/soc-pcm.c
@@ -35,6 +35,86 @@
 #define DPCM_MAX_BE_USERS	8
 
 /**
+ * snd_soc_runtime_activate() - Increment active count for PCM runtime components
+ * @rtd: ASoC PCM runtime that is activated
+ * @stream: Direction of the PCM stream
+ *
+ * Increments the active count for all the DAIs and components attached to a PCM
+ * runtime. Should typically be called when a stream is opened.
+ *
+ * Must be called with the rtd->pcm_mutex being held
+ */
+void snd_soc_runtime_activate(struct snd_soc_pcm_runtime *rtd, int stream)
+{
+	struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+	struct snd_soc_dai *codec_dai = rtd->codec_dai;
+
+	lockdep_assert_held(&rtd->pcm_mutex);
+
+	if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
+		cpu_dai->playback_active++;
+		codec_dai->playback_active++;
+	} else {
+		cpu_dai->capture_active++;
+		codec_dai->capture_active++;
+	}
+
+	cpu_dai->active++;
+	codec_dai->active++;
+	cpu_dai->component->active++;
+	codec_dai->component->active++;
+}
+
+/**
+ * snd_soc_runtime_deactivate() - Decrement active count for PCM runtime components
+ * @rtd: ASoC PCM runtime that is deactivated
+ * @stream: Direction of the PCM stream
+ *
+ * Decrements the active count for all the DAIs and components attached to a PCM
+ * runtime. Should typically be called when a stream is closed.
+ *
+ * Must be called with the rtd->pcm_mutex being held
+ */
+void snd_soc_runtime_deactivate(struct snd_soc_pcm_runtime *rtd, int stream)
+{
+	struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+	struct snd_soc_dai *codec_dai = rtd->codec_dai;
+
+	lockdep_assert_held(&rtd->pcm_mutex);
+
+	if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
+		cpu_dai->playback_active--;
+		codec_dai->playback_active--;
+	} else {
+		cpu_dai->capture_active--;
+		codec_dai->capture_active--;
+	}
+
+	cpu_dai->active--;
+	codec_dai->active--;
+	cpu_dai->component->active--;
+	codec_dai->component->active--;
+}
+
+/**
+ * snd_soc_runtime_ignore_pmdown_time() - Check whether to ignore the power down delay
+ * @rtd: The ASoC PCM runtime that should be checked.
+ *
+ * This function checks whether the power down delay should be ignored for a
+ * specific PCM runtime. Returns true if the delay is 0, if it the DAI link has
+ * been configured to ignore the delay, or if none of the components benefits
+ * from having the delay.
+ */
+bool snd_soc_runtime_ignore_pmdown_time(struct snd_soc_pcm_runtime *rtd)
+{
+	if (!rtd->pmdown_time || rtd->dai_link->ignore_pmdown_time)
+		return true;
+
+	return rtd->cpu_dai->component->ignore_pmdown_time &&
+			rtd->codec_dai->component->ignore_pmdown_time;
+}
+
+/**
  * snd_soc_set_runtime_hwparams - set the runtime hardware parameters
  * @substream: the pcm substream
  * @hw: the hardware parameters
@@ -378,16 +458,9 @@ static int soc_pcm_open(struct snd_pcm_substream *substream)
 		 runtime->hw.rate_max);
 
 dynamic:
-	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
-		cpu_dai->playback_active++;
-		codec_dai->playback_active++;
-	} else {
-		cpu_dai->capture_active++;
-		codec_dai->capture_active++;
-	}
-	cpu_dai->active++;
-	codec_dai->active++;
-	rtd->codec->active++;
+
+	snd_soc_runtime_activate(rtd, substream->stream);
+
 	mutex_unlock(&rtd->pcm_mutex);
 	return 0;
 
@@ -459,21 +532,10 @@ static int soc_pcm_close(struct snd_pcm_substream *substream)
 	struct snd_soc_platform *platform = rtd->platform;
 	struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
 	struct snd_soc_dai *codec_dai = rtd->codec_dai;
-	struct snd_soc_codec *codec = rtd->codec;
 
 	mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass);
 
-	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
-		cpu_dai->playback_active--;
-		codec_dai->playback_active--;
-	} else {
-		cpu_dai->capture_active--;
-		codec_dai->capture_active--;
-	}
-
-	cpu_dai->active--;
-	codec_dai->active--;
-	codec->active--;
+	snd_soc_runtime_deactivate(rtd, substream->stream);
 
 	/* clear the corresponding DAIs rate when inactive */
 	if (!cpu_dai->active)
@@ -496,8 +558,7 @@ static int soc_pcm_close(struct snd_pcm_substream *substream)
 	cpu_dai->runtime = NULL;
 
 	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
-		if (!rtd->pmdown_time || codec->ignore_pmdown_time ||
-		    rtd->dai_link->ignore_pmdown_time) {
+		if (snd_soc_runtime_ignore_pmdown_time(rtd)) {
 			/* powered down playback stream now */
 			snd_soc_dapm_stream_event(rtd,
 						  SNDRV_PCM_STREAM_PLAYBACK,
diff --git a/sound/soc/tegra/Kconfig b/sound/soc/tegra/Kconfig
index 9f9c1856f822..31198cf7f88d 100644
--- a/sound/soc/tegra/Kconfig
+++ b/sound/soc/tegra/Kconfig
@@ -105,7 +105,7 @@ config SND_SOC_TEGRA_TRIMSLICE
 	tristate "SoC Audio support for TrimSlice board"
 	depends on SND_SOC_TEGRA && I2C
 	select SND_SOC_TEGRA20_I2S if ARCH_TEGRA_2x_SOC
-	select SND_SOC_TLV320AIC23
+	select SND_SOC_TLV320AIC23_I2C
 	help
 	  Say Y or M here if you want to add support for SoC audio on the
 	  TrimSlice platform.