summary refs log tree commit diff
path: root/sound
diff options
context:
space:
mode:
authorMark Brown <broonie@kernel.org>2015-06-22 10:24:19 +0100
committerMark Brown <broonie@kernel.org>2015-06-22 10:24:19 +0100
commit208a128f6b19eedd1819cb1b19f29dc99ca1d27e (patch)
tree5d95c61efeb06ed6ec827267bc05e89498836bec /sound
parentd21504d4c993838b31d970d392b1b78eb33cfd61 (diff)
parent11e688862c4c8162119a4ca51c3326555966c8bb (diff)
downloadlinux-208a128f6b19eedd1819cb1b19f29dc99ca1d27e.tar.gz
Merge tag 'asoc-v4.2' into asoc-next
ASoC: Updates for v4.2

The big thing this release has been Liam's addition of topology support
to the core.  We've also seen quite a bit of driver work and the
continuation of Lars' refactoring for component support.

 - Support for loading ASoC topology maps from firmware, intended to be
   used to allow self-describing DSP firmware images to be built which
   can map controls added by the DSP to userspace without the kernel
   needing to know about individual DSP firmwares.
 - Lots of refactoring to avoid direct access to snd_soc_codec where
   it's not needed supporting future refactoring.
 - Big refactoring and cleanup serieses for the Wolfson ADSP and TI
   TAS2552 drivers.
 - Support for TI TAS571x power amplifiers.
 - Support for Qualcomm APQ8016 and ZTE ZX296702 SoCs.
 - Support for x86 systems with RT5650 and Qualcomm Storm.

# gpg: Signature made Mon 08 Jun 2015 18:48:37 BST using RSA key ID 5D5487D0
# gpg: Oops: keyid_from_fingerprint: no pubkey
# 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/soc/Kconfig1
-rw-r--r--sound/soc/Makefile2
-rw-r--r--sound/soc/atmel/Kconfig20
-rw-r--r--sound/soc/atmel/Makefile8
-rw-r--r--sound/soc/atmel/atmel-pcm-dma.c3
-rw-r--r--sound/soc/atmel/sam9g20_wm8731.c10
-rw-r--r--sound/soc/au1x/db1200.c2
-rw-r--r--sound/soc/cirrus/ep93xx-pcm.c1
-rw-r--r--sound/soc/codecs/88pm860x-codec.c3
-rw-r--r--sound/soc/codecs/Kconfig12
-rw-r--r--sound/soc/codecs/Makefile2
-rw-r--r--sound/soc/codecs/ab8500-codec.c20
-rw-r--r--sound/soc/codecs/ac97.c8
-rw-r--r--sound/soc/codecs/ad1836.c2
-rw-r--r--sound/soc/codecs/adau1373.c1
-rw-r--r--sound/soc/codecs/adau1701.c126
-rw-r--r--sound/soc/codecs/adau1761.c27
-rw-r--r--sound/soc/codecs/adau1781.c10
-rw-r--r--sound/soc/codecs/adau17x1.c20
-rw-r--r--sound/soc/codecs/adau1977.c14
-rw-r--r--sound/soc/codecs/adav80x.c11
-rw-r--r--sound/soc/codecs/ak4535.c1
-rw-r--r--sound/soc/codecs/ak4641.c3
-rw-r--r--sound/soc/codecs/ak4642.c1
-rw-r--r--sound/soc/codecs/ak4671.c1
-rw-r--r--sound/soc/codecs/alc5623.c3
-rw-r--r--sound/soc/codecs/alc5632.c1
-rw-r--r--sound/soc/codecs/arizona.c174
-rw-r--r--sound/soc/codecs/arizona.h17
-rw-r--r--sound/soc/codecs/bt-sco.c11
-rw-r--r--sound/soc/codecs/cq93vc.c1
-rw-r--r--sound/soc/codecs/cs35l32.c1
-rw-r--r--sound/soc/codecs/cs4265.c1
-rw-r--r--sound/soc/codecs/cs42l52.c5
-rw-r--r--sound/soc/codecs/cs42l56.c5
-rw-r--r--sound/soc/codecs/cs42l73.c3
-rw-r--r--sound/soc/codecs/cs42xx8.c2
-rw-r--r--sound/soc/codecs/cx20442.c6
-rw-r--r--sound/soc/codecs/da7213.c3
-rw-r--r--sound/soc/codecs/da732x.c4
-rw-r--r--sound/soc/codecs/da9055.c3
-rw-r--r--sound/soc/codecs/es8328.c3
-rw-r--r--sound/soc/codecs/isabelle.c2
-rw-r--r--sound/soc/codecs/jz4740.c4
-rw-r--r--sound/soc/codecs/lm4857.c114
-rw-r--r--sound/soc/codecs/lm49453.c4
-rw-r--r--sound/soc/codecs/max98088.c3
-rw-r--r--sound/soc/codecs/max98090.c34
-rw-r--r--sound/soc/codecs/max98095.c24
-rw-r--r--sound/soc/codecs/max98357a.c3
-rw-r--r--sound/soc/codecs/max9850.c3
-rw-r--r--sound/soc/codecs/ml26124.c3
-rw-r--r--sound/soc/codecs/pcm512x.c8
-rw-r--r--sound/soc/codecs/rt286.c33
-rw-r--r--sound/soc/codecs/rt5631.c5
-rw-r--r--sound/soc/codecs/rt5640.c16
-rw-r--r--sound/soc/codecs/rt5645.c1081
-rw-r--r--sound/soc/codecs/rt5645.h30
-rw-r--r--sound/soc/codecs/rt5651.c5
-rw-r--r--sound/soc/codecs/rt5670.c26
-rw-r--r--sound/soc/codecs/rt5677.c157
-rw-r--r--sound/soc/codecs/rt5677.h15
-rw-r--r--sound/soc/codecs/sgtl5000.c56
-rw-r--r--sound/soc/codecs/sirf-audio-codec.c2
-rw-r--r--sound/soc/codecs/sn95031.c12
-rw-r--r--sound/soc/codecs/ssm2518.c9
-rw-r--r--sound/soc/codecs/ssm2602.c5
-rw-r--r--sound/soc/codecs/ssm4567.c9
-rw-r--r--sound/soc/codecs/sta32x.c19
-rw-r--r--sound/soc/codecs/sta350.c9
-rw-r--r--sound/soc/codecs/sta529.c8
-rw-r--r--sound/soc/codecs/stac9766.c1
-rw-r--r--sound/soc/codecs/tas2552.c312
-rw-r--r--sound/soc/codecs/tas2552.h90
-rw-r--r--sound/soc/codecs/tas571x.c514
-rw-r--r--sound/soc/codecs/tas571x.h33
-rw-r--r--sound/soc/codecs/tlv320aic23.c1
-rw-r--r--sound/soc/codecs/tlv320aic31xx.c11
-rw-r--r--sound/soc/codecs/tlv320aic32x4.c1
-rw-r--r--sound/soc/codecs/tlv320aic3x.c10
-rw-r--r--sound/soc/codecs/tlv320dac33.c5
-rw-r--r--sound/soc/codecs/ts3a227e.c15
-rw-r--r--sound/soc/codecs/twl4030.c3
-rw-r--r--sound/soc/codecs/twl6040.c9
-rw-r--r--sound/soc/codecs/uda134x.c4
-rw-r--r--sound/soc/codecs/uda1380.c6
-rw-r--r--sound/soc/codecs/wm0010.c6
-rw-r--r--sound/soc/codecs/wm1250-ev1.c2
-rw-r--r--sound/soc/codecs/wm5100.c12
-rw-r--r--sound/soc/codecs/wm5102.c60
-rw-r--r--sound/soc/codecs/wm5110.c9
-rw-r--r--sound/soc/codecs/wm8350.c3
-rw-r--r--sound/soc/codecs/wm8400.c3
-rw-r--r--sound/soc/codecs/wm8510.c3
-rw-r--r--sound/soc/codecs/wm8523.c3
-rw-r--r--sound/soc/codecs/wm8580.c3
-rw-r--r--sound/soc/codecs/wm8711.c3
-rw-r--r--sound/soc/codecs/wm8728.c3
-rw-r--r--sound/soc/codecs/wm8731.c8
-rw-r--r--sound/soc/codecs/wm8737.c5
-rw-r--r--sound/soc/codecs/wm8741.c129
-rw-r--r--sound/soc/codecs/wm8741.h10
-rw-r--r--sound/soc/codecs/wm8750.c3
-rw-r--r--sound/soc/codecs/wm8753.c3
-rw-r--r--sound/soc/codecs/wm8770.c3
-rw-r--r--sound/soc/codecs/wm8776.c3
-rw-r--r--sound/soc/codecs/wm8804.c2
-rw-r--r--sound/soc/codecs/wm8900.c9
-rw-r--r--sound/soc/codecs/wm8903.c4
-rw-r--r--sound/soc/codecs/wm8904.c5
-rw-r--r--sound/soc/codecs/wm8940.c6
-rw-r--r--sound/soc/codecs/wm8955.c5
-rw-r--r--sound/soc/codecs/wm8960.c125
-rw-r--r--sound/soc/codecs/wm8961.c6
-rw-r--r--sound/soc/codecs/wm8962.c21
-rw-r--r--sound/soc/codecs/wm8971.c3
-rw-r--r--sound/soc/codecs/wm8974.c3
-rw-r--r--sound/soc/codecs/wm8978.c7
-rw-r--r--sound/soc/codecs/wm8983.c3
-rw-r--r--sound/soc/codecs/wm8985.c3
-rw-r--r--sound/soc/codecs/wm8988.c3
-rw-r--r--sound/soc/codecs/wm8990.c5
-rw-r--r--sound/soc/codecs/wm8991.c3
-rw-r--r--sound/soc/codecs/wm8993.c12
-rw-r--r--sound/soc/codecs/wm8994.c68
-rw-r--r--sound/soc/codecs/wm8995.c6
-rw-r--r--sound/soc/codecs/wm8996.c23
-rw-r--r--sound/soc/codecs/wm8997.c16
-rw-r--r--sound/soc/codecs/wm9081.c4
-rw-r--r--sound/soc/codecs/wm9090.c6
-rw-r--r--sound/soc/codecs/wm9712.c3
-rw-r--r--sound/soc/codecs/wm9713.c3
-rw-r--r--sound/soc/codecs/wm_adsp.c1210
-rw-r--r--sound/soc/codecs/wm_adsp.h19
-rw-r--r--sound/soc/codecs/wm_hubs.c4
-rw-r--r--sound/soc/codecs/wmfw.h44
-rw-r--r--sound/soc/davinci/davinci-mcasp.c167
-rw-r--r--sound/soc/fsl/fsl_dma.c4
-rw-r--r--sound/soc/fsl/fsl_sai.c144
-rw-r--r--sound/soc/fsl/fsl_sai.h9
-rw-r--r--sound/soc/fsl/fsl_spdif.c10
-rw-r--r--sound/soc/fsl/fsl_ssi.c7
-rw-r--r--sound/soc/fsl/imx-audmux.c2
-rw-r--r--sound/soc/fsl/imx-mc13783.c6
-rw-r--r--sound/soc/generic/simple-card.c16
-rw-r--r--sound/soc/intel/Kconfig17
-rw-r--r--sound/soc/intel/atom/sst-atom-controls.c167
-rw-r--r--sound/soc/intel/atom/sst-atom-controls.h9
-rw-r--r--sound/soc/intel/atom/sst-mfld-platform-pcm.c47
-rw-r--r--sound/soc/intel/atom/sst-mfld-platform.h2
-rw-r--r--sound/soc/intel/atom/sst/sst_acpi.c4
-rw-r--r--sound/soc/intel/baytrail/sst-baytrail-ipc.c11
-rw-r--r--sound/soc/intel/boards/Makefile2
-rw-r--r--sound/soc/intel/boards/cht_bsw_max98090_ti.c318
-rw-r--r--sound/soc/intel/boards/cht_bsw_rt5645.c118
-rw-r--r--sound/soc/intel/common/sst-ipc.c34
-rw-r--r--sound/soc/intel/common/sst-ipc.h7
-rw-r--r--sound/soc/intel/haswell/sst-haswell-ipc.c12
-rw-r--r--sound/soc/intel/haswell/sst-haswell-pcm.c31
-rw-r--r--sound/soc/omap/Kconfig5
-rw-r--r--sound/soc/omap/omap-twl4030.c3
-rw-r--r--sound/soc/omap/rx51.c30
-rw-r--r--sound/soc/pxa/brownstone.c25
-rw-r--r--sound/soc/pxa/poodle.c19
-rw-r--r--sound/soc/pxa/tosa.c13
-rw-r--r--sound/soc/pxa/z2.c9
-rw-r--r--sound/soc/qcom/Kconfig19
-rw-r--r--sound/soc/qcom/Makefile4
-rw-r--r--sound/soc/qcom/lpass-apq8016.c242
-rw-r--r--sound/soc/qcom/lpass-cpu.c240
-rw-r--r--sound/soc/qcom/lpass-ipq806x.c109
-rw-r--r--sound/soc/qcom/lpass-lpaif-reg.h (renamed from sound/soc/qcom/lpass-lpaif-ipq806x.h)92
-rw-r--r--sound/soc/qcom/lpass-platform.c202
-rw-r--r--sound/soc/qcom/lpass.h51
-rw-r--r--sound/soc/samsung/Kconfig15
-rw-r--r--sound/soc/samsung/i2s.c2
-rw-r--r--sound/soc/samsung/lowland.c2
-rw-r--r--sound/soc/samsung/smartq_wm8987.c6
-rw-r--r--sound/soc/samsung/smdk_wm8994.c3
-rw-r--r--sound/soc/samsung/speyside.c2
-rw-r--r--sound/soc/sh/rcar/core.c32
-rw-r--r--sound/soc/sh/rcar/rsnd.h3
-rw-r--r--sound/soc/sh/rcar/rsrc-card.c1
-rw-r--r--sound/soc/sh/rcar/src.c11
-rw-r--r--sound/soc/sh/rcar/ssi.c70
-rw-r--r--sound/soc/soc-core.c67
-rw-r--r--sound/soc/soc-dapm.c349
-rw-r--r--sound/soc/soc-generic-dmaengine-pcm.c25
-rw-r--r--sound/soc/soc-jack.c9
-rw-r--r--sound/soc/soc-pcm.c47
-rw-r--r--sound/soc/soc-topology.c1826
-rw-r--r--sound/soc/ux500/ux500_pcm.c1
-rw-r--r--sound/soc/zte/Kconfig17
-rw-r--r--sound/soc/zte/Makefile2
-rw-r--r--sound/soc/zte/zx296702-i2s.c436
-rw-r--r--sound/soc/zte/zx296702-spdif.c365
196 files changed, 8455 insertions, 2214 deletions
diff --git a/sound/soc/Kconfig b/sound/soc/Kconfig
index 3ba52da18bc6..e2828e101433 100644
--- a/sound/soc/Kconfig
+++ b/sound/soc/Kconfig
@@ -57,6 +57,7 @@ source "sound/soc/tegra/Kconfig"
 source "sound/soc/txx9/Kconfig"
 source "sound/soc/ux500/Kconfig"
 source "sound/soc/xtensa/Kconfig"
+source "sound/soc/zte/Kconfig"
 
 # Supported codecs
 source "sound/soc/codecs/Kconfig"
diff --git a/sound/soc/Makefile b/sound/soc/Makefile
index 974ba708b482..a0e1ee6b507d 100644
--- a/sound/soc/Makefile
+++ b/sound/soc/Makefile
@@ -1,5 +1,6 @@
 snd-soc-core-objs := soc-core.o soc-dapm.o soc-jack.o soc-cache.o soc-utils.o
 snd-soc-core-objs += soc-pcm.o soc-compress.o soc-io.o soc-devres.o soc-ops.o
+snd-soc-core-objs += soc-topology.o
 
 ifneq ($(CONFIG_SND_SOC_GENERIC_DMAENGINE_PCM),)
 snd-soc-core-objs += soc-generic-dmaengine-pcm.o
@@ -38,3 +39,4 @@ obj-$(CONFIG_SND_SOC)	+= tegra/
 obj-$(CONFIG_SND_SOC)	+= txx9/
 obj-$(CONFIG_SND_SOC)	+= ux500/
 obj-$(CONFIG_SND_SOC)	+= xtensa/
+obj-$(CONFIG_SND_SOC)	+= zte/
diff --git a/sound/soc/atmel/Kconfig b/sound/soc/atmel/Kconfig
index e7d08806f3e9..c3152072d682 100644
--- a/sound/soc/atmel/Kconfig
+++ b/sound/soc/atmel/Kconfig
@@ -6,27 +6,22 @@ config SND_ATMEL_SOC
 	  the ATMEL SSC interface. You will also need
 	  to select the audio interfaces to support below.
 
+if SND_ATMEL_SOC
+
 config SND_ATMEL_SOC_PDC
-	tristate
-	depends on SND_ATMEL_SOC
+	bool
 
 config SND_ATMEL_SOC_DMA
-	tristate
-	depends on SND_ATMEL_SOC
+	bool
 	select SND_SOC_GENERIC_DMAENGINE_PCM
 
 config SND_ATMEL_SOC_SSC
 	tristate
-	depends on SND_ATMEL_SOC
-	help
-	  Say Y or M if you want to add support for codecs the
-	  ATMEL SSC interface. You will also needs to select the individual
-	  machine drivers to support below.
 
 config SND_AT91_SOC_SAM9G20_WM8731
 	tristate "SoC Audio support for WM8731-based At91sam9g20 evaluation board"
 	depends on ARCH_AT91 || COMPILE_TEST
-	depends on ATMEL_SSC && SND_ATMEL_SOC && SND_SOC_I2C_AND_SPI
+	depends on ATMEL_SSC && SND_SOC_I2C_AND_SPI
 	select SND_ATMEL_SOC_PDC
 	select SND_ATMEL_SOC_SSC
 	select SND_SOC_WM8731
@@ -37,7 +32,7 @@ config SND_AT91_SOC_SAM9G20_WM8731
 config SND_ATMEL_SOC_WM8904
 	tristate "Atmel ASoC driver for boards using WM8904 codec"
 	depends on ARCH_AT91 || COMPILE_TEST
-	depends on ATMEL_SSC && SND_ATMEL_SOC && I2C
+	depends on ATMEL_SSC && I2C
 	select SND_ATMEL_SOC_SSC
 	select SND_ATMEL_SOC_DMA
 	select SND_SOC_WM8904
@@ -48,10 +43,11 @@ config SND_ATMEL_SOC_WM8904
 config SND_AT91_SOC_SAM9X5_WM8731
 	tristate "SoC Audio support for WM8731-based at91sam9x5 board"
 	depends on ARCH_AT91 || COMPILE_TEST
-	depends on ATMEL_SSC && SND_ATMEL_SOC && SND_SOC_I2C_AND_SPI
+	depends on ATMEL_SSC && SND_SOC_I2C_AND_SPI
 	select SND_ATMEL_SOC_SSC
 	select SND_ATMEL_SOC_DMA
 	select SND_SOC_WM8731
 	help
 	  Say Y if you want to add support for audio SoC on an
 	  at91sam9x5 based board that is using WM8731 codec.
+endif
diff --git a/sound/soc/atmel/Makefile b/sound/soc/atmel/Makefile
index b327e5cc8de3..4fa7ac91f972 100644
--- a/sound/soc/atmel/Makefile
+++ b/sound/soc/atmel/Makefile
@@ -1,10 +1,8 @@
 # AT91 Platform Support
-snd-soc-atmel-pcm-pdc-objs := atmel-pcm-pdc.o
-snd-soc-atmel-pcm-dma-objs := atmel-pcm-dma.o
-snd-soc-atmel_ssc_dai-objs := atmel_ssc_dai.o
+snd-soc-atmel-pcm-$(CONFIG_SND_ATMEL_SOC_PDC) := atmel-pcm-pdc.o
+snd-soc-atmel-pcm-$(CONFIG_SND_ATMEL_SOC_DMA) += atmel-pcm-dma.o
+snd-soc-atmel_ssc_dai-objs := atmel_ssc_dai.o $(snd-soc-atmel-pcm-y)
 
-obj-$(CONFIG_SND_ATMEL_SOC_PDC) += snd-soc-atmel-pcm-pdc.o
-obj-$(CONFIG_SND_ATMEL_SOC_DMA) += snd-soc-atmel-pcm-dma.o
 obj-$(CONFIG_SND_ATMEL_SOC_SSC) += snd-soc-atmel_ssc_dai.o
 
 # AT91 Machine Support
diff --git a/sound/soc/atmel/atmel-pcm-dma.c b/sound/soc/atmel/atmel-pcm-dma.c
index b6625c8c411b..dd57a9eac171 100644
--- a/sound/soc/atmel/atmel-pcm-dma.c
+++ b/sound/soc/atmel/atmel-pcm-dma.c
@@ -124,8 +124,7 @@ static const struct snd_dmaengine_pcm_config atmel_dmaengine_pcm_config = {
 
 int atmel_pcm_dma_platform_register(struct device *dev)
 {
-	return snd_dmaengine_pcm_register(dev, &atmel_dmaengine_pcm_config,
-			SND_DMAENGINE_PCM_FLAG_NO_RESIDUE);
+	return snd_dmaengine_pcm_register(dev, &atmel_dmaengine_pcm_config, 0);
 }
 EXPORT_SYMBOL(atmel_pcm_dma_platform_register);
 
diff --git a/sound/soc/atmel/sam9g20_wm8731.c b/sound/soc/atmel/sam9g20_wm8731.c
index 8de836165cf2..d7469cdd90dc 100644
--- a/sound/soc/atmel/sam9g20_wm8731.c
+++ b/sound/soc/atmel/sam9g20_wm8731.c
@@ -95,8 +95,9 @@ static const struct snd_soc_dapm_widget at91sam9g20ek_dapm_widgets[] = {
 
 static const struct snd_soc_dapm_route intercon[] = {
 
-	/* speaker connected to LHPOUT */
+	/* speaker connected to LHPOUT/RHPOUT */
 	{"Ext Spk", NULL, "LHPOUT"},
+	{"Ext Spk", NULL, "RHPOUT"},
 
 	/* mic is connected to Mic Jack, with WM8731 Mic Bias */
 	{"MICIN", NULL, "Mic Bias"},
@@ -108,9 +109,7 @@ static const struct snd_soc_dapm_route intercon[] = {
  */
 static int at91sam9g20ek_wm8731_init(struct snd_soc_pcm_runtime *rtd)
 {
-	struct snd_soc_codec *codec = rtd->codec;
 	struct snd_soc_dai *codec_dai = rtd->codec_dai;
-	struct snd_soc_dapm_context *dapm = &codec->dapm;
 	int ret;
 
 	printk(KERN_DEBUG
@@ -124,10 +123,6 @@ static int at91sam9g20ek_wm8731_init(struct snd_soc_pcm_runtime *rtd)
 		return ret;
 	}
 
-	/* not connected */
-	snd_soc_dapm_nc_pin(dapm, "RLINEIN");
-	snd_soc_dapm_nc_pin(dapm, "LLINEIN");
-
 #ifndef ENABLE_MIC_INPUT
 	snd_soc_dapm_nc_pin(&rtd->card->dapm, "Int Mic");
 #endif
@@ -158,6 +153,7 @@ static struct snd_soc_card snd_soc_at91sam9g20ek = {
 	.num_dapm_widgets = ARRAY_SIZE(at91sam9g20ek_dapm_widgets),
 	.dapm_routes = intercon,
 	.num_dapm_routes = ARRAY_SIZE(intercon),
+	.fully_routed = true,
 };
 
 static int at91sam9g20ek_audio_probe(struct platform_device *pdev)
diff --git a/sound/soc/au1x/db1200.c b/sound/soc/au1x/db1200.c
index c75995f2779c..58c3164802b8 100644
--- a/sound/soc/au1x/db1200.c
+++ b/sound/soc/au1x/db1200.c
@@ -21,7 +21,7 @@
 #include "../codecs/wm8731.h"
 #include "psc.h"
 
-static struct platform_device_id db1200_pids[] = {
+static const struct platform_device_id db1200_pids[] = {
 	{
 		.name		= "db1200-ac97",
 		.driver_data	= 0,
diff --git a/sound/soc/cirrus/ep93xx-pcm.c b/sound/soc/cirrus/ep93xx-pcm.c
index 5f664471d99e..67a73330db5e 100644
--- a/sound/soc/cirrus/ep93xx-pcm.c
+++ b/sound/soc/cirrus/ep93xx-pcm.c
@@ -60,7 +60,6 @@ int devm_ep93xx_pcm_platform_register(struct device *dev)
 {
 	return devm_snd_dmaengine_pcm_register(dev,
 		&ep93xx_dmaengine_pcm_config,
-		SND_DMAENGINE_PCM_FLAG_NO_RESIDUE |
 		SND_DMAENGINE_PCM_FLAG_NO_DT |
 		SND_DMAENGINE_PCM_FLAG_COMPAT);
 }
diff --git a/sound/soc/codecs/88pm860x-codec.c b/sound/soc/codecs/88pm860x-codec.c
index f62da48eda9a..38b3dad9d48a 100644
--- a/sound/soc/codecs/88pm860x-codec.c
+++ b/sound/soc/codecs/88pm860x-codec.c
@@ -1140,7 +1140,7 @@ static int pm860x_set_bias_level(struct snd_soc_codec *codec,
 		break;
 
 	case SND_SOC_BIAS_STANDBY:
-		if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) {
+		if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) {
 			/* Enable Audio PLL & Audio section */
 			data = AUDIO_PLL | AUDIO_SECTION_ON;
 			pm860x_reg_write(pm860x->i2c, REG_MISC2, data);
@@ -1156,7 +1156,6 @@ static int pm860x_set_bias_level(struct snd_soc_codec *codec,
 		pm860x_set_bits(pm860x->i2c, REG_MISC2, data, 0);
 		break;
 	}
-	codec->dapm.bias_level = level;
 	return 0;
 }
 
diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig
index 061c46587628..9b36011a814e 100644
--- a/sound/soc/codecs/Kconfig
+++ b/sound/soc/codecs/Kconfig
@@ -16,7 +16,7 @@ config SND_SOC_ALL_CODECS
 	select SND_SOC_88PM860X if MFD_88PM860X
 	select SND_SOC_L3
 	select SND_SOC_AB8500_CODEC if ABX500_CORE
-	select SND_SOC_AC97_CODEC if SND_SOC_AC97_BUS
+	select SND_SOC_AC97_CODEC
 	select SND_SOC_AD1836 if SPI_MASTER
 	select SND_SOC_AD193X_SPI if SPI_MASTER
 	select SND_SOC_AD193X_I2C if I2C
@@ -54,7 +54,7 @@ config SND_SOC_ALL_CODECS
 	select SND_SOC_CS4271_SPI if SPI_MASTER
 	select SND_SOC_CS42XX8_I2C if I2C
 	select SND_SOC_CX20442 if TTY
-	select SND_SOC_DA7210 if I2C
+	select SND_SOC_DA7210 if SND_SOC_I2C_AND_SPI
 	select SND_SOC_DA7213 if I2C
 	select SND_SOC_DA732X if I2C
 	select SND_SOC_DA9055 if I2C
@@ -104,6 +104,7 @@ config SND_SOC_ALL_CODECS
 	select SND_SOC_STAC9766 if SND_SOC_AC97_BUS
 	select SND_SOC_TAS2552 if I2C
 	select SND_SOC_TAS5086 if I2C
+	select SND_SOC_TAS571X if I2C
 	select SND_SOC_TFA9879 if I2C
 	select SND_SOC_TLV320AIC23_I2C if I2C
 	select SND_SOC_TLV320AIC23_SPI if SPI_MASTER
@@ -211,8 +212,9 @@ config SND_SOC_AB8500_CODEC
 	tristate
 
 config SND_SOC_AC97_CODEC
-	tristate
+	tristate "Build generic ASoC AC97 CODEC driver"
 	select SND_AC97_CODEC
+	select SND_SOC_AC97_BUS
 
 config SND_SOC_AD1836
 	tristate
@@ -611,6 +613,10 @@ config SND_SOC_TAS5086
 	tristate "Texas Instruments TAS5086 speaker amplifier"
 	depends on I2C
 
+config SND_SOC_TAS571X
+	tristate "Texas Instruments TAS5711/TAS5717/TAS5719 power amplifiers"
+	depends on I2C
+
 config SND_SOC_TFA9879
 	tristate "NXP Semiconductors TFA9879 amplifier"
 	depends on I2C
diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile
index abe2d7edf65c..3dcf5ac85e89 100644
--- a/sound/soc/codecs/Makefile
+++ b/sound/soc/codecs/Makefile
@@ -106,6 +106,7 @@ snd-soc-sta350-objs := sta350.o
 snd-soc-sta529-objs := sta529.o
 snd-soc-stac9766-objs := stac9766.o
 snd-soc-tas5086-objs := tas5086.o
+snd-soc-tas571x-objs := tas571x.o
 snd-soc-tfa9879-objs := tfa9879.o
 snd-soc-tlv320aic23-objs := tlv320aic23.o
 snd-soc-tlv320aic23-i2c-objs := tlv320aic23-i2c.o
@@ -288,6 +289,7 @@ obj-$(CONFIG_SND_SOC_STA529)   += snd-soc-sta529.o
 obj-$(CONFIG_SND_SOC_STAC9766)	+= snd-soc-stac9766.o
 obj-$(CONFIG_SND_SOC_TAS2552)	+= snd-soc-tas2552.o
 obj-$(CONFIG_SND_SOC_TAS5086)	+= snd-soc-tas5086.o
+obj-$(CONFIG_SND_SOC_TAS571X)	+= snd-soc-tas571x.o
 obj-$(CONFIG_SND_SOC_TFA9879)	+= snd-soc-tfa9879.o
 obj-$(CONFIG_SND_SOC_TLV320AIC23)	+= snd-soc-tlv320aic23.o
 obj-$(CONFIG_SND_SOC_TLV320AIC23_I2C)	+= snd-soc-tlv320aic23-i2c.o
diff --git a/sound/soc/codecs/ab8500-codec.c b/sound/soc/codecs/ab8500-codec.c
index 88ca9cb0ce79..c7d243db010a 100644
--- a/sound/soc/codecs/ab8500-codec.c
+++ b/sound/soc/codecs/ab8500-codec.c
@@ -1209,6 +1209,7 @@ static int anc_status_control_put(struct snd_kcontrol *kcontrol,
 				struct snd_ctl_elem_value *ucontrol)
 {
 	struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+	struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
 	struct ab8500_codec_drvdata *drvdata = dev_get_drvdata(codec->dev);
 	struct device *dev = codec->dev;
 	bool apply_fir, apply_iir;
@@ -1234,15 +1235,14 @@ static int anc_status_control_put(struct snd_kcontrol *kcontrol,
 	apply_fir = req == ANC_APPLY_FIR || req == ANC_APPLY_FIR_IIR;
 	apply_iir = req == ANC_APPLY_IIR || req == ANC_APPLY_FIR_IIR;
 
-	status = snd_soc_dapm_force_enable_pin(&codec->dapm,
-					"ANC Configure Input");
+	status = snd_soc_dapm_force_enable_pin(dapm, "ANC Configure Input");
 	if (status < 0) {
 		dev_err(dev,
 			"%s: ERROR: Failed to enable power (status = %d)!\n",
 			__func__, status);
 		goto cleanup;
 	}
-	snd_soc_dapm_sync(&codec->dapm);
+	snd_soc_dapm_sync(dapm);
 
 	anc_configure(codec, apply_fir, apply_iir);
 
@@ -1259,8 +1259,8 @@ static int anc_status_control_put(struct snd_kcontrol *kcontrol,
 			drvdata->anc_status =  ANC_IIR_CONFIGURED;
 	}
 
-	status = snd_soc_dapm_disable_pin(&codec->dapm, "ANC Configure Input");
-	snd_soc_dapm_sync(&codec->dapm);
+	status = snd_soc_dapm_disable_pin(dapm, "ANC Configure Input");
+	snd_soc_dapm_sync(dapm);
 
 cleanup:
 	mutex_unlock(&drvdata->ctrl_lock);
@@ -1947,6 +1947,7 @@ static int ab8500_audio_init_audioblock(struct snd_soc_codec *codec)
 static int ab8500_audio_setup_mics(struct snd_soc_codec *codec,
 			struct amic_settings *amics)
 {
+	struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
 	u8 value8;
 	unsigned int value;
 	int status;
@@ -1973,15 +1974,15 @@ static int ab8500_audio_setup_mics(struct snd_soc_codec *codec,
 	dev_dbg(codec->dev, "%s: Mic 1a regulator: %s\n", __func__,
 		amic_micbias_str(amics->mic1a_micbias));
 	route = &ab8500_dapm_routes_mic1a_vamicx[amics->mic1a_micbias];
-	status = snd_soc_dapm_add_routes(&codec->dapm, route, 1);
+	status = snd_soc_dapm_add_routes(dapm, route, 1);
 	dev_dbg(codec->dev, "%s: Mic 1b regulator: %s\n", __func__,
 		amic_micbias_str(amics->mic1b_micbias));
 	route = &ab8500_dapm_routes_mic1b_vamicx[amics->mic1b_micbias];
-	status |= snd_soc_dapm_add_routes(&codec->dapm, route, 1);
+	status |= snd_soc_dapm_add_routes(dapm, route, 1);
 	dev_dbg(codec->dev, "%s: Mic 2 regulator: %s\n", __func__,
 		amic_micbias_str(amics->mic2_micbias));
 	route = &ab8500_dapm_routes_mic2_vamicx[amics->mic2_micbias];
-	status |= snd_soc_dapm_add_routes(&codec->dapm, route, 1);
+	status |= snd_soc_dapm_add_routes(dapm, route, 1);
 	if (status < 0) {
 		dev_err(codec->dev,
 			"%s: Failed to add AMic-regulator DAPM-routes (%d).\n",
@@ -2461,6 +2462,7 @@ static void ab8500_codec_of_probe(struct device *dev, struct device_node *np,
 
 static int ab8500_codec_probe(struct snd_soc_codec *codec)
 {
+	struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
 	struct device *dev = codec->dev;
 	struct device_node *np = dev->of_node;
 	struct ab8500_codec_drvdata *drvdata = dev_get_drvdata(dev);
@@ -2541,7 +2543,7 @@ static int ab8500_codec_probe(struct snd_soc_codec *codec)
 		&ab8500_filter_controls[AB8500_FILTER_SID_FIR].private_value;
 	drvdata->sid_fir_values = (long *)fc->value;
 
-	(void)snd_soc_dapm_disable_pin(&codec->dapm, "ANC Configure Input");
+	snd_soc_dapm_disable_pin(dapm, "ANC Configure Input");
 
 	mutex_init(&drvdata->ctrl_lock);
 
diff --git a/sound/soc/codecs/ac97.c b/sound/soc/codecs/ac97.c
index d0ac723eee32..5b3224c63943 100644
--- a/sound/soc/codecs/ac97.c
+++ b/sound/soc/codecs/ac97.c
@@ -44,10 +44,6 @@ static int ac97_prepare(struct snd_pcm_substream *substream,
 	return snd_ac97_set_rate(ac97, reg, substream->runtime->rate);
 }
 
-#define STD_AC97_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |\
-		SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_44100 |\
-		SNDRV_PCM_RATE_48000)
-
 static const struct snd_soc_dai_ops ac97_dai_ops = {
 	.prepare	= ac97_prepare,
 };
@@ -58,13 +54,13 @@ static struct snd_soc_dai_driver ac97_dai = {
 		.stream_name = "AC97 Playback",
 		.channels_min = 1,
 		.channels_max = 2,
-		.rates = STD_AC97_RATES,
+		.rates = SNDRV_PCM_RATE_KNOT,
 		.formats = SND_SOC_STD_AC97_FMTS,},
 	.capture = {
 		.stream_name = "AC97 Capture",
 		.channels_min = 1,
 		.channels_max = 2,
-		.rates = STD_AC97_RATES,
+		.rates = SNDRV_PCM_RATE_KNOT,
 		.formats = SND_SOC_STD_AC97_FMTS,},
 	.ops = &ac97_dai_ops,
 };
diff --git a/sound/soc/codecs/ad1836.c b/sound/soc/codecs/ad1836.c
index 685998dd086e..95f0bec26a1b 100644
--- a/sound/soc/codecs/ad1836.c
+++ b/sound/soc/codecs/ad1836.c
@@ -251,7 +251,7 @@ static int ad1836_resume(struct snd_soc_codec *codec)
 static int ad1836_probe(struct snd_soc_codec *codec)
 {
 	struct ad1836_priv *ad1836 = snd_soc_codec_get_drvdata(codec);
-	struct snd_soc_dapm_context *dapm = &codec->dapm;
+	struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
 	int num_dacs, num_adcs;
 	int ret = 0;
 	int i;
diff --git a/sound/soc/codecs/adau1373.c b/sound/soc/codecs/adau1373.c
index 783dcb57043a..a43160254929 100644
--- a/sound/soc/codecs/adau1373.c
+++ b/sound/soc/codecs/adau1373.c
@@ -1444,7 +1444,6 @@ static int adau1373_set_bias_level(struct snd_soc_codec *codec,
 			ADAU1373_PWDN_CTRL3_PWR_EN, 0);
 		break;
 	}
-	codec->dapm.bias_level = level;
 	return 0;
 }
 
diff --git a/sound/soc/codecs/adau1701.c b/sound/soc/codecs/adau1701.c
index d4e219b6b98f..ff7f846e3b76 100644
--- a/sound/soc/codecs/adau1701.c
+++ b/sound/soc/codecs/adau1701.c
@@ -16,6 +16,7 @@
 #include <linux/of.h>
 #include <linux/of_gpio.h>
 #include <linux/of_device.h>
+#include <linux/regulator/consumer.h>
 #include <linux/regmap.h>
 #include <sound/core.h>
 #include <sound/pcm.h>
@@ -101,6 +102,10 @@
 
 #define ADAU1701_FIRMWARE "adau1701.bin"
 
+static const char * const supply_names[] = {
+	"dvdd", "avdd"
+};
+
 struct adau1701 {
 	int gpio_nreset;
 	int gpio_pll_mode[2];
@@ -112,6 +117,7 @@ struct adau1701 {
 	u8 pin_config[12];
 
 	struct sigmadsp *sigmadsp;
+	struct regulator_bulk_data supplies[ARRAY_SIZE(supply_names)];
 };
 
 static const struct snd_kcontrol_new adau1701_controls[] = {
@@ -565,7 +571,6 @@ static int adau1701_set_bias_level(struct snd_soc_codec *codec,
 		break;
 	}
 
-	codec->dapm.bias_level = level;
 	return 0;
 }
 
@@ -669,6 +674,13 @@ static int adau1701_probe(struct snd_soc_codec *codec)
 	if (ret)
 		return ret;
 
+	ret = regulator_bulk_enable(ARRAY_SIZE(adau1701->supplies),
+				    adau1701->supplies);
+	if (ret < 0) {
+		dev_err(codec->dev, "Failed to enable regulators: %d\n", ret);
+		return ret;
+	}
+
 	/*
 	 * Let the pll_clkdiv variable default to something that won't happen
 	 * at runtime. That way, we can postpone the firmware download from
@@ -680,7 +692,7 @@ static int adau1701_probe(struct snd_soc_codec *codec)
 	/* initalize with pre-configured pll mode settings */
 	ret = adau1701_reset(codec, adau1701->pll_clkdiv, 0);
 	if (ret < 0)
-		return ret;
+		goto exit_regulators_disable;
 
 	/* set up pin config */
 	val = 0;
@@ -696,10 +708,60 @@ static int adau1701_probe(struct snd_soc_codec *codec)
 	regmap_write(adau1701->regmap, ADAU1701_PINCONF_1, val);
 
 	return 0;
+
+exit_regulators_disable:
+
+	regulator_bulk_disable(ARRAY_SIZE(adau1701->supplies), adau1701->supplies);
+	return ret;
 }
 
+static int adau1701_remove(struct snd_soc_codec *codec)
+{
+	struct adau1701 *adau1701 = snd_soc_codec_get_drvdata(codec);
+
+	if (gpio_is_valid(adau1701->gpio_nreset))
+		gpio_set_value_cansleep(adau1701->gpio_nreset, 0);
+
+	regulator_bulk_disable(ARRAY_SIZE(adau1701->supplies), adau1701->supplies);
+
+	return 0;
+}
+
+#ifdef CONFIG_PM
+static int adau1701_suspend(struct snd_soc_codec *codec)
+{
+	struct adau1701 *adau1701 = snd_soc_codec_get_drvdata(codec);
+
+	regulator_bulk_disable(ARRAY_SIZE(adau1701->supplies),
+			       adau1701->supplies);
+
+	return 0;
+}
+
+static int adau1701_resume(struct snd_soc_codec *codec)
+{
+	struct adau1701 *adau1701 = snd_soc_codec_get_drvdata(codec);
+	int ret;
+
+        ret = regulator_bulk_enable(ARRAY_SIZE(adau1701->supplies),
+				    adau1701->supplies);
+	if (ret < 0) {
+		dev_err(codec->dev, "Failed to enable regulators: %d\n", ret);
+		return ret;
+	}
+
+	return adau1701_reset(codec, adau1701->pll_clkdiv, 0);
+}
+#else
+#define adau1701_resume 	NULL
+#define adau1701_suspend 	NULL
+#endif /* CONFIG_PM */
+
 static struct snd_soc_codec_driver adau1701_codec_drv = {
 	.probe			= adau1701_probe,
+	.remove			= adau1701_remove,
+	.resume			= adau1701_resume,
+	.suspend		= adau1701_suspend,
 	.set_bias_level		= adau1701_set_bias_level,
 	.idle_bias_off		= true,
 
@@ -730,32 +792,58 @@ static int adau1701_i2c_probe(struct i2c_client *client,
 	struct device *dev = &client->dev;
 	int gpio_nreset = -EINVAL;
 	int gpio_pll_mode[2] = { -EINVAL, -EINVAL };
-	int ret;
+	int ret, i;
 
 	adau1701 = devm_kzalloc(dev, sizeof(*adau1701), GFP_KERNEL);
 	if (!adau1701)
 		return -ENOMEM;
 
+	for (i = 0; i < ARRAY_SIZE(supply_names); i++)
+		adau1701->supplies[i].supply = supply_names[i];
+
+	ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(adau1701->supplies),
+			adau1701->supplies);
+	if (ret < 0) {
+		dev_err(dev, "Failed to get regulators: %d\n", ret);
+		return ret;
+	}
+
+	ret = regulator_bulk_enable(ARRAY_SIZE(adau1701->supplies),
+			adau1701->supplies);
+	if (ret < 0) {
+		dev_err(dev, "Failed to enable regulators: %d\n", ret);
+		return ret;
+	}
+
 	adau1701->client = client;
 	adau1701->regmap = devm_regmap_init(dev, NULL, client,
 					    &adau1701_regmap);
-	if (IS_ERR(adau1701->regmap))
-		return PTR_ERR(adau1701->regmap);
+	if (IS_ERR(adau1701->regmap)) {
+		ret = PTR_ERR(adau1701->regmap);
+		goto exit_regulators_disable;
+	}
+
 
 	if (dev->of_node) {
 		gpio_nreset = of_get_named_gpio(dev->of_node, "reset-gpio", 0);
-		if (gpio_nreset < 0 && gpio_nreset != -ENOENT)
-			return gpio_nreset;
+		if (gpio_nreset < 0 && gpio_nreset != -ENOENT) {
+			ret = gpio_nreset;
+			goto exit_regulators_disable;
+		}
 
 		gpio_pll_mode[0] = of_get_named_gpio(dev->of_node,
 						   "adi,pll-mode-gpios", 0);
-		if (gpio_pll_mode[0] < 0 && gpio_pll_mode[0] != -ENOENT)
-			return gpio_pll_mode[0];
+		if (gpio_pll_mode[0] < 0 && gpio_pll_mode[0] != -ENOENT) {
+			ret = gpio_pll_mode[0];
+			goto exit_regulators_disable;
+		}
 
 		gpio_pll_mode[1] = of_get_named_gpio(dev->of_node,
 						   "adi,pll-mode-gpios", 1);
-		if (gpio_pll_mode[1] < 0 && gpio_pll_mode[1] != -ENOENT)
-			return gpio_pll_mode[1];
+		if (gpio_pll_mode[1] < 0 && gpio_pll_mode[1] != -ENOENT) {
+			ret = gpio_pll_mode[1];
+			goto exit_regulators_disable;
+		}
 
 		of_property_read_u32(dev->of_node, "adi,pll-clkdiv",
 				     &adau1701->pll_clkdiv);
@@ -769,7 +857,7 @@ static int adau1701_i2c_probe(struct i2c_client *client,
 		ret = devm_gpio_request_one(dev, gpio_nreset, GPIOF_OUT_INIT_LOW,
 					    "ADAU1701 Reset");
 		if (ret < 0)
-			return ret;
+			goto exit_regulators_disable;
 	}
 
 	if (gpio_is_valid(gpio_pll_mode[0]) &&
@@ -778,13 +866,13 @@ static int adau1701_i2c_probe(struct i2c_client *client,
 					    GPIOF_OUT_INIT_LOW,
 					    "ADAU1701 PLL mode 0");
 		if (ret < 0)
-			return ret;
+			goto exit_regulators_disable;
 
 		ret = devm_gpio_request_one(dev, gpio_pll_mode[1],
 					    GPIOF_OUT_INIT_LOW,
 					    "ADAU1701 PLL mode 1");
 		if (ret < 0)
-			return ret;
+			goto exit_regulators_disable;
 	}
 
 	adau1701->gpio_nreset = gpio_nreset;
@@ -795,11 +883,17 @@ static int adau1701_i2c_probe(struct i2c_client *client,
 
 	adau1701->sigmadsp = devm_sigmadsp_init_i2c(client,
 		&adau1701_sigmadsp_ops, ADAU1701_FIRMWARE);
-	if (IS_ERR(adau1701->sigmadsp))
-		return PTR_ERR(adau1701->sigmadsp);
+	if (IS_ERR(adau1701->sigmadsp)) {
+		ret = PTR_ERR(adau1701->sigmadsp);
+		goto exit_regulators_disable;
+	}
 
 	ret = snd_soc_register_codec(&client->dev, &adau1701_codec_drv,
 			&adau1701_dai, 1);
+
+exit_regulators_disable:
+
+	regulator_bulk_disable(ARRAY_SIZE(adau1701->supplies), adau1701->supplies);
 	return ret;
 }
 
diff --git a/sound/soc/codecs/adau1761.c b/sound/soc/codecs/adau1761.c
index a1baeee160f4..2f12477e539e 100644
--- a/sound/soc/codecs/adau1761.c
+++ b/sound/soc/codecs/adau1761.c
@@ -466,7 +466,6 @@ static int adau1761_set_bias_level(struct snd_soc_codec *codec,
 		break;
 
 	}
-	codec->dapm.bias_level = level;
 	return 0;
 }
 
@@ -483,6 +482,7 @@ static enum adau1761_output_mode adau1761_get_lineout_mode(
 
 static int adau1761_setup_digmic_jackdetect(struct snd_soc_codec *codec)
 {
+	struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
 	struct adau1761_platform_data *pdata = codec->dev->platform_data;
 	struct adau *adau = snd_soc_codec_get_drvdata(codec);
 	enum adau1761_digmic_jackdet_pin_mode mode;
@@ -515,21 +515,18 @@ static int adau1761_setup_digmic_jackdetect(struct snd_soc_codec *codec)
 		if (ret)
 			return ret;
 	case ADAU1761_DIGMIC_JACKDET_PIN_MODE_NONE: /* fallthrough */
-		ret = snd_soc_dapm_add_routes(&codec->dapm,
-			adau1761_no_dmic_routes,
+		ret = snd_soc_dapm_add_routes(dapm, adau1761_no_dmic_routes,
 			ARRAY_SIZE(adau1761_no_dmic_routes));
 		if (ret)
 			return ret;
 		break;
 	case ADAU1761_DIGMIC_JACKDET_PIN_MODE_DIGMIC:
-		ret = snd_soc_dapm_new_controls(&codec->dapm,
-			adau1761_dmic_widgets,
+		ret = snd_soc_dapm_new_controls(dapm, adau1761_dmic_widgets,
 			ARRAY_SIZE(adau1761_dmic_widgets));
 		if (ret)
 			return ret;
 
-		ret = snd_soc_dapm_add_routes(&codec->dapm,
-			adau1761_dmic_routes,
+		ret = snd_soc_dapm_add_routes(dapm, adau1761_dmic_routes,
 			ARRAY_SIZE(adau1761_dmic_routes));
 		if (ret)
 			return ret;
@@ -547,6 +544,7 @@ static int adau1761_setup_digmic_jackdetect(struct snd_soc_codec *codec)
 
 static int adau1761_setup_headphone_mode(struct snd_soc_codec *codec)
 {
+	struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
 	struct adau *adau = snd_soc_codec_get_drvdata(codec);
 	struct adau1761_platform_data *pdata = codec->dev->platform_data;
 	enum adau1761_output_mode mode;
@@ -577,12 +575,12 @@ static int adau1761_setup_headphone_mode(struct snd_soc_codec *codec)
 	}
 
 	if (mode == ADAU1761_OUTPUT_MODE_HEADPHONE_CAPLESS) {
-		ret = snd_soc_dapm_new_controls(&codec->dapm,
+		ret = snd_soc_dapm_new_controls(dapm,
 			adau1761_capless_dapm_widgets,
 			ARRAY_SIZE(adau1761_capless_dapm_widgets));
 		if (ret)
 			return ret;
-		ret = snd_soc_dapm_add_routes(&codec->dapm,
+		ret = snd_soc_dapm_add_routes(dapm,
 			adau1761_capless_dapm_routes,
 			ARRAY_SIZE(adau1761_capless_dapm_routes));
 	} else {
@@ -590,12 +588,12 @@ static int adau1761_setup_headphone_mode(struct snd_soc_codec *codec)
 			ARRAY_SIZE(adau1761_mono_controls));
 		if (ret)
 			return ret;
-		ret = snd_soc_dapm_new_controls(&codec->dapm,
+		ret = snd_soc_dapm_new_controls(dapm,
 			adau1761_mono_dapm_widgets,
 			ARRAY_SIZE(adau1761_mono_dapm_widgets));
 		if (ret)
 			return ret;
-		ret = snd_soc_dapm_add_routes(&codec->dapm,
+		ret = snd_soc_dapm_add_routes(dapm,
 			adau1761_mono_dapm_routes,
 			ARRAY_SIZE(adau1761_mono_dapm_routes));
 	}
@@ -640,6 +638,7 @@ static bool adau1761_readable_register(struct device *dev, unsigned int reg)
 
 static int adau1761_codec_probe(struct snd_soc_codec *codec)
 {
+	struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
 	struct adau1761_platform_data *pdata = codec->dev->platform_data;
 	struct adau *adau = snd_soc_codec_get_drvdata(codec);
 	int ret;
@@ -692,14 +691,12 @@ static int adau1761_codec_probe(struct snd_soc_codec *codec)
 		return ret;
 
 	if (adau->type == ADAU1761) {
-		ret = snd_soc_dapm_new_controls(&codec->dapm,
-			adau1761_dapm_widgets,
+		ret = snd_soc_dapm_new_controls(dapm, adau1761_dapm_widgets,
 			ARRAY_SIZE(adau1761_dapm_widgets));
 		if (ret)
 			return ret;
 
-		ret = snd_soc_dapm_add_routes(&codec->dapm,
-			adau1761_dapm_routes,
+		ret = snd_soc_dapm_add_routes(dapm, adau1761_dapm_routes,
 			ARRAY_SIZE(adau1761_dapm_routes));
 		if (ret)
 			return ret;
diff --git a/sound/soc/codecs/adau1781.c b/sound/soc/codecs/adau1781.c
index 35581f43fa6d..fde9068550a6 100644
--- a/sound/soc/codecs/adau1781.c
+++ b/sound/soc/codecs/adau1781.c
@@ -339,7 +339,6 @@ static int adau1781_set_bias_level(struct snd_soc_codec *codec,
 		break;
 	}
 
-	codec->dapm.bias_level = level;
 	return 0;
 }
 
@@ -383,6 +382,7 @@ static int adau1781_set_input_mode(struct adau *adau, unsigned int reg,
 
 static int adau1781_codec_probe(struct snd_soc_codec *codec)
 {
+	struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
 	struct adau1781_platform_data *pdata = dev_get_platdata(codec->dev);
 	struct adau *adau = snd_soc_codec_get_drvdata(codec);
 	int ret;
@@ -403,19 +403,17 @@ static int adau1781_codec_probe(struct snd_soc_codec *codec)
 	}
 
 	if (pdata && pdata->use_dmic) {
-		ret = snd_soc_dapm_new_controls(&codec->dapm,
+		ret = snd_soc_dapm_new_controls(dapm,
 			adau1781_dmic_dapm_widgets,
 			ARRAY_SIZE(adau1781_dmic_dapm_widgets));
 		if (ret)
 			return ret;
-		ret = snd_soc_dapm_add_routes(&codec->dapm,
-			adau1781_dmic_dapm_routes,
+		ret = snd_soc_dapm_add_routes(dapm, adau1781_dmic_dapm_routes,
 			ARRAY_SIZE(adau1781_dmic_dapm_routes));
 		if (ret)
 			return ret;
 	} else {
-		ret = snd_soc_dapm_add_routes(&codec->dapm,
-			adau1781_adc_dapm_routes,
+		ret = snd_soc_dapm_add_routes(dapm, adau1781_adc_dapm_routes,
 			ARRAY_SIZE(adau1781_adc_dapm_routes));
 		if (ret)
 			return ret;
diff --git a/sound/soc/codecs/adau17x1.c b/sound/soc/codecs/adau17x1.c
index fa2e690e51c8..fcf05b254ecd 100644
--- a/sound/soc/codecs/adau17x1.c
+++ b/sound/soc/codecs/adau17x1.c
@@ -155,6 +155,7 @@ static int adau17x1_dsp_mux_enum_put(struct snd_kcontrol *kcontrol,
 	struct snd_ctl_elem_value *ucontrol)
 {
 	struct snd_soc_codec *codec = snd_soc_dapm_kcontrol_codec(kcontrol);
+	struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
 	struct adau *adau = snd_soc_codec_get_drvdata(codec);
 	struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
 	struct snd_soc_dapm_update update;
@@ -188,7 +189,7 @@ static int adau17x1_dsp_mux_enum_put(struct snd_kcontrol *kcontrol,
 		update.reg = reg;
 		update.val = val;
 
-		snd_soc_dapm_mux_update_power(&codec->dapm, kcontrol,
+		snd_soc_dapm_mux_update_power(dapm, kcontrol,
 				ucontrol->value.enumerated.item[0], e, &update);
 	}
 
@@ -444,8 +445,8 @@ static int adau17x1_set_dai_pll(struct snd_soc_dai *dai, int pll_id,
 static int adau17x1_set_dai_sysclk(struct snd_soc_dai *dai,
 		int clk_id, unsigned int freq, int dir)
 {
+	struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(dai->codec);
 	struct adau *adau = snd_soc_codec_get_drvdata(dai->codec);
-	struct snd_soc_dapm_context *dapm = &dai->codec->dapm;
 
 	switch (clk_id) {
 	case ADAU17X1_CLK_SRC_MCLK:
@@ -804,6 +805,7 @@ EXPORT_SYMBOL_GPL(adau17x1_setup_firmware);
 
 int adau17x1_add_widgets(struct snd_soc_codec *codec)
 {
+	struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
 	struct adau *adau = snd_soc_codec_get_drvdata(codec);
 	int ret;
 
@@ -811,14 +813,13 @@ int adau17x1_add_widgets(struct snd_soc_codec *codec)
 		ARRAY_SIZE(adau17x1_controls));
 	if (ret)
 		return ret;
-	ret = snd_soc_dapm_new_controls(&codec->dapm, adau17x1_dapm_widgets,
+	ret = snd_soc_dapm_new_controls(dapm, adau17x1_dapm_widgets,
 		ARRAY_SIZE(adau17x1_dapm_widgets));
 	if (ret)
 		return ret;
 
 	if (adau17x1_has_dsp(adau)) {
-		ret = snd_soc_dapm_new_controls(&codec->dapm,
-			adau17x1_dsp_dapm_widgets,
+		ret = snd_soc_dapm_new_controls(dapm, adau17x1_dsp_dapm_widgets,
 			ARRAY_SIZE(adau17x1_dsp_dapm_widgets));
 		if (ret)
 			return ret;
@@ -840,21 +841,20 @@ EXPORT_SYMBOL_GPL(adau17x1_add_widgets);
 
 int adau17x1_add_routes(struct snd_soc_codec *codec)
 {
+	struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
 	struct adau *adau = snd_soc_codec_get_drvdata(codec);
 	int ret;
 
-	ret = snd_soc_dapm_add_routes(&codec->dapm, adau17x1_dapm_routes,
+	ret = snd_soc_dapm_add_routes(dapm, adau17x1_dapm_routes,
 		ARRAY_SIZE(adau17x1_dapm_routes));
 	if (ret)
 		return ret;
 
 	if (adau17x1_has_dsp(adau)) {
-		ret = snd_soc_dapm_add_routes(&codec->dapm,
-			adau17x1_dsp_dapm_routes,
+		ret = snd_soc_dapm_add_routes(dapm, adau17x1_dsp_dapm_routes,
 			ARRAY_SIZE(adau17x1_dsp_dapm_routes));
 	} else {
-		ret = snd_soc_dapm_add_routes(&codec->dapm,
-			adau17x1_no_dsp_dapm_routes,
+		ret = snd_soc_dapm_add_routes(dapm, adau17x1_no_dsp_dapm_routes,
 			ARRAY_SIZE(adau17x1_no_dsp_dapm_routes));
 	}
 	return ret;
diff --git a/sound/soc/codecs/adau1977.c b/sound/soc/codecs/adau1977.c
index 7ad8e156e2df..9bdd15f408c1 100644
--- a/sound/soc/codecs/adau1977.c
+++ b/sound/soc/codecs/adau1977.c
@@ -202,7 +202,7 @@ static const struct snd_soc_dapm_route adau1977_dapm_routes[] = {
 		ADAU1977_REG_DC_HPF_CAL, (x) - 1, 1, 0)
 
 #define ADAU1977_DC_SUB_SWITCH(x) \
-	SOC_SINGLE("ADC" #x " DC Substraction Capture Switch", \
+	SOC_SINGLE("ADC" #x " DC Subtraction Capture Switch", \
 		ADAU1977_REG_DC_HPF_CAL, (x) + 3, 1, 0)
 
 static const struct snd_kcontrol_new adau1977_snd_controls[] = {
@@ -485,7 +485,7 @@ static int adau1977_set_bias_level(struct snd_soc_codec *codec,
 	case SND_SOC_BIAS_PREPARE:
 		break;
 	case SND_SOC_BIAS_STANDBY:
-		if (codec->dapm.bias_level == SND_SOC_BIAS_OFF)
+		if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF)
 			ret = adau1977_power_enable(adau1977);
 		break;
 	case SND_SOC_BIAS_OFF:
@@ -493,12 +493,7 @@ static int adau1977_set_bias_level(struct snd_soc_codec *codec,
 		break;
 	}
 
-	if (ret)
-		return ret;
-
-	codec->dapm.bias_level = level;
-
-	return 0;
+	return ret;
 }
 
 static int adau1977_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask,
@@ -853,12 +848,13 @@ static int adau1977_set_sysclk(struct snd_soc_codec *codec,
 
 static int adau1977_codec_probe(struct snd_soc_codec *codec)
 {
+	struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(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,
+		ret = snd_soc_dapm_new_controls(dapm,
 			adau1977_micbias_dapm_widgets,
 			ARRAY_SIZE(adau1977_micbias_dapm_widgets));
 		if (ret < 0)
diff --git a/sound/soc/codecs/adav80x.c b/sound/soc/codecs/adav80x.c
index 4373ada95648..36d842570745 100644
--- a/sound/soc/codecs/adav80x.c
+++ b/sound/soc/codecs/adav80x.c
@@ -539,7 +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;
+	struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
 
 	if (dir == SND_SOC_CLOCK_IN) {
 		switch (clk_id) {
@@ -622,6 +622,7 @@ static int adav80x_set_sysclk(struct snd_soc_codec *codec,
 static int adav80x_set_pll(struct snd_soc_codec *codec, int pll_id,
 		int source, unsigned int freq_in, unsigned int freq_out)
 {
+	struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
 	struct adav80x *adav80x = snd_soc_codec_get_drvdata(codec);
 	unsigned int pll_ctrl1 = 0;
 	unsigned int pll_ctrl2 = 0;
@@ -687,7 +688,7 @@ static int adav80x_set_pll(struct snd_soc_codec *codec, int pll_id,
 
 		adav80x->pll_src = source;
 
-		snd_soc_dapm_sync(&codec->dapm);
+		snd_soc_dapm_sync(dapm);
 	}
 
 	return 0;
@@ -714,7 +715,6 @@ static int adav80x_set_bias_level(struct snd_soc_codec *codec,
 		break;
 	}
 
-	codec->dapm.bias_level = level;
 	return 0;
 }
 
@@ -801,11 +801,12 @@ static struct snd_soc_dai_driver adav80x_dais[] = {
 
 static int adav80x_probe(struct snd_soc_codec *codec)
 {
+	struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
 	struct adav80x *adav80x = snd_soc_codec_get_drvdata(codec);
 
 	/* Force PLLs on for SYSCLK output */
-	snd_soc_dapm_force_enable_pin(&codec->dapm, "PLL1");
-	snd_soc_dapm_force_enable_pin(&codec->dapm, "PLL2");
+	snd_soc_dapm_force_enable_pin(dapm, "PLL1");
+	snd_soc_dapm_force_enable_pin(dapm, "PLL2");
 
 	/* Power down S/PDIF receiver, since it is currently not supported */
 	regmap_write(adav80x->regmap, ADAV80X_PLL_OUTE, 0x20);
diff --git a/sound/soc/codecs/ak4535.c b/sound/soc/codecs/ak4535.c
index 9130d916f2f4..8670861e5bec 100644
--- a/sound/soc/codecs/ak4535.c
+++ b/sound/soc/codecs/ak4535.c
@@ -341,7 +341,6 @@ static int ak4535_set_bias_level(struct snd_soc_codec *codec,
 		snd_soc_update_bits(codec, AK4535_PM1, 0x80, 0);
 		break;
 	}
-	codec->dapm.bias_level = level;
 	return 0;
 }
 
diff --git a/sound/soc/codecs/ak4641.c b/sound/soc/codecs/ak4641.c
index 81b54a270bd8..2d0ff4595ea0 100644
--- a/sound/soc/codecs/ak4641.c
+++ b/sound/soc/codecs/ak4641.c
@@ -412,7 +412,7 @@ static int ak4641_set_bias_level(struct snd_soc_codec *codec,
 		snd_soc_update_bits(codec, AK4641_DAC, 0x20, 0x20);
 		break;
 	case SND_SOC_BIAS_STANDBY:
-		if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) {
+		if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) {
 			if (pdata && gpio_is_valid(pdata->gpio_power))
 				gpio_set_value(pdata->gpio_power, 1);
 			mdelay(1);
@@ -439,7 +439,6 @@ static int ak4641_set_bias_level(struct snd_soc_codec *codec,
 		regcache_mark_dirty(ak4641->regmap);
 		break;
 	}
-	codec->dapm.bias_level = level;
 	return 0;
 }
 
diff --git a/sound/soc/codecs/ak4642.c b/sound/soc/codecs/ak4642.c
index 13585e88f597..7c0f6552c229 100644
--- a/sound/soc/codecs/ak4642.c
+++ b/sound/soc/codecs/ak4642.c
@@ -482,7 +482,6 @@ static int ak4642_set_bias_level(struct snd_soc_codec *codec,
 		snd_soc_update_bits(codec, PW_MGMT1, PMVCM, PMVCM);
 		break;
 	}
-	codec->dapm.bias_level = level;
 
 	return 0;
 }
diff --git a/sound/soc/codecs/ak4671.c b/sound/soc/codecs/ak4671.c
index 2a58b1dccd2f..0e59063aeb6f 100644
--- a/sound/soc/codecs/ak4671.c
+++ b/sound/soc/codecs/ak4671.c
@@ -577,7 +577,6 @@ static int ak4671_set_bias_level(struct snd_soc_codec *codec,
 		snd_soc_write(codec, AK4671_AD_DA_POWER_MANAGEMENT, 0x00);
 		break;
 	}
-	codec->dapm.bias_level = level;
 	return 0;
 }
 
diff --git a/sound/soc/codecs/alc5623.c b/sound/soc/codecs/alc5623.c
index 0e357996864b..0fc24e0d518c 100644
--- a/sound/soc/codecs/alc5623.c
+++ b/sound/soc/codecs/alc5623.c
@@ -826,7 +826,6 @@ static int alc5623_set_bias_level(struct snd_soc_codec *codec,
 		snd_soc_write(codec, ALC5623_PWR_MANAG_ADD1, 0);
 		break;
 	}
-	codec->dapm.bias_level = level;
 	return 0;
 }
 
@@ -894,7 +893,7 @@ static int alc5623_resume(struct snd_soc_codec *codec)
 static int alc5623_probe(struct snd_soc_codec *codec)
 {
 	struct alc5623_priv *alc5623 = snd_soc_codec_get_drvdata(codec);
-	struct snd_soc_dapm_context *dapm = &codec->dapm;
+	struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
 
 	alc5623_reset(codec);
 
diff --git a/sound/soc/codecs/alc5632.c b/sound/soc/codecs/alc5632.c
index db3283abbe18..607a63b9705f 100644
--- a/sound/soc/codecs/alc5632.c
+++ b/sound/soc/codecs/alc5632.c
@@ -1000,7 +1000,6 @@ static int alc5632_set_bias_level(struct snd_soc_codec *codec,
 				ALC5632_PWR_MANAG_ADD1_MASK, 0);
 		break;
 	}
-	codec->dapm.bias_level = level;
 	return 0;
 }
 
diff --git a/sound/soc/codecs/arizona.c b/sound/soc/codecs/arizona.c
index eff4b4d512b7..88f6df21ad95 100644
--- a/sound/soc/codecs/arizona.c
+++ b/sound/soc/codecs/arizona.c
@@ -208,11 +208,12 @@ static const struct snd_soc_dapm_widget arizona_spkr =
 
 int arizona_init_spk(struct snd_soc_codec *codec)
 {
+	struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
 	struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec);
 	struct arizona *arizona = priv->arizona;
 	int ret;
 
-	ret = snd_soc_dapm_new_controls(&codec->dapm, &arizona_spkl, 1);
+	ret = snd_soc_dapm_new_controls(dapm, &arizona_spkl, 1);
 	if (ret != 0)
 		return ret;
 
@@ -220,8 +221,7 @@ int arizona_init_spk(struct snd_soc_codec *codec)
 	case WM8997:
 		break;
 	default:
-		ret = snd_soc_dapm_new_controls(&codec->dapm,
-						&arizona_spkr, 1);
+		ret = snd_soc_dapm_new_controls(dapm, &arizona_spkr, 1);
 		if (ret != 0)
 			return ret;
 		break;
@@ -258,13 +258,14 @@ static const struct snd_soc_dapm_route arizona_mono_routes[] = {
 
 int arizona_init_mono(struct snd_soc_codec *codec)
 {
+	struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
 	struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec);
 	struct arizona *arizona = priv->arizona;
 	int i;
 
 	for (i = 0; i < ARIZONA_MAX_OUTPUT; ++i) {
 		if (arizona->pdata.out_mono[i])
-			snd_soc_dapm_add_routes(&codec->dapm,
+			snd_soc_dapm_add_routes(dapm,
 						&arizona_mono_routes[i], 1);
 	}
 
@@ -274,6 +275,7 @@ EXPORT_SYMBOL_GPL(arizona_init_mono);
 
 int arizona_init_gpio(struct snd_soc_codec *codec)
 {
+	struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
 	struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec);
 	struct arizona *arizona = priv->arizona;
 	int i;
@@ -281,23 +283,21 @@ int arizona_init_gpio(struct snd_soc_codec *codec)
 	switch (arizona->type) {
 	case WM5110:
 	case WM8280:
-		snd_soc_dapm_disable_pin(&codec->dapm, "DRC2 Signal Activity");
+		snd_soc_dapm_disable_pin(dapm, "DRC2 Signal Activity");
 		break;
 	default:
 		break;
 	}
 
-	snd_soc_dapm_disable_pin(&codec->dapm, "DRC1 Signal Activity");
+	snd_soc_dapm_disable_pin(dapm, "DRC1 Signal Activity");
 
 	for (i = 0; i < ARRAY_SIZE(arizona->pdata.gpio_defaults); i++) {
 		switch (arizona->pdata.gpio_defaults[i] & ARIZONA_GPN_FN_MASK) {
 		case ARIZONA_GP_FN_DRC1_SIGNAL_DETECT:
-			snd_soc_dapm_enable_pin(&codec->dapm,
-						"DRC1 Signal Activity");
+			snd_soc_dapm_enable_pin(dapm, "DRC1 Signal Activity");
 			break;
 		case ARIZONA_GP_FN_DRC2_SIGNAL_DETECT:
-			snd_soc_dapm_enable_pin(&codec->dapm,
-						"DRC2 Signal Activity");
+			snd_soc_dapm_enable_pin(dapm, "DRC2 Signal Activity");
 			break;
 		default:
 			break;
@@ -851,6 +851,134 @@ int arizona_hp_ev(struct snd_soc_dapm_widget *w,
 }
 EXPORT_SYMBOL_GPL(arizona_hp_ev);
 
+static int arizona_dvfs_enable(struct snd_soc_codec *codec)
+{
+	const struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec);
+	struct arizona *arizona = priv->arizona;
+	int ret;
+
+	ret = regulator_set_voltage(arizona->dcvdd, 1800000, 1800000);
+	if (ret) {
+		dev_err(codec->dev, "Failed to boost DCVDD: %d\n", ret);
+		return ret;
+	}
+
+	ret = regmap_update_bits(arizona->regmap,
+				 ARIZONA_DYNAMIC_FREQUENCY_SCALING_1,
+				 ARIZONA_SUBSYS_MAX_FREQ,
+				 ARIZONA_SUBSYS_MAX_FREQ);
+	if (ret) {
+		dev_err(codec->dev, "Failed to enable subsys max: %d\n", ret);
+		regulator_set_voltage(arizona->dcvdd, 1200000, 1800000);
+		return ret;
+	}
+
+	return 0;
+}
+
+static int arizona_dvfs_disable(struct snd_soc_codec *codec)
+{
+	const struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec);
+	struct arizona *arizona = priv->arizona;
+	int ret;
+
+	ret = regmap_update_bits(arizona->regmap,
+				 ARIZONA_DYNAMIC_FREQUENCY_SCALING_1,
+				 ARIZONA_SUBSYS_MAX_FREQ, 0);
+	if (ret) {
+		dev_err(codec->dev, "Failed to disable subsys max: %d\n", ret);
+		return ret;
+	}
+
+	ret = regulator_set_voltage(arizona->dcvdd, 1200000, 1800000);
+	if (ret) {
+		dev_err(codec->dev, "Failed to unboost DCVDD: %d\n", ret);
+		return ret;
+	}
+
+	return 0;
+}
+
+int arizona_dvfs_up(struct snd_soc_codec *codec, unsigned int flags)
+{
+	struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec);
+	int ret = 0;
+
+	mutex_lock(&priv->dvfs_lock);
+
+	if (!priv->dvfs_cached && !priv->dvfs_reqs) {
+		ret = arizona_dvfs_enable(codec);
+		if (ret)
+			goto err;
+	}
+
+	priv->dvfs_reqs |= flags;
+err:
+	mutex_unlock(&priv->dvfs_lock);
+	return ret;
+}
+EXPORT_SYMBOL_GPL(arizona_dvfs_up);
+
+int arizona_dvfs_down(struct snd_soc_codec *codec, unsigned int flags)
+{
+	struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec);
+	unsigned int old_reqs;
+	int ret = 0;
+
+	mutex_lock(&priv->dvfs_lock);
+
+	old_reqs = priv->dvfs_reqs;
+	priv->dvfs_reqs &= ~flags;
+
+	if (!priv->dvfs_cached && old_reqs && !priv->dvfs_reqs)
+		ret = arizona_dvfs_disable(codec);
+
+	mutex_unlock(&priv->dvfs_lock);
+	return ret;
+}
+EXPORT_SYMBOL_GPL(arizona_dvfs_down);
+
+int arizona_dvfs_sysclk_ev(struct snd_soc_dapm_widget *w,
+			   struct snd_kcontrol *kcontrol, int event)
+{
+	struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+	struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec);
+	int ret = 0;
+
+	mutex_lock(&priv->dvfs_lock);
+
+	switch (event) {
+	case SND_SOC_DAPM_POST_PMU:
+		if (priv->dvfs_reqs)
+			ret = arizona_dvfs_enable(codec);
+
+		priv->dvfs_cached = false;
+		break;
+	case SND_SOC_DAPM_PRE_PMD:
+		/* We must ensure DVFS is disabled before the codec goes into
+		 * suspend so that we are never in an illegal state of DVFS
+		 * enabled without enough DCVDD
+		 */
+		priv->dvfs_cached = true;
+
+		if (priv->dvfs_reqs)
+			ret = arizona_dvfs_disable(codec);
+		break;
+	default:
+		break;
+	}
+
+	mutex_unlock(&priv->dvfs_lock);
+	return ret;
+}
+EXPORT_SYMBOL_GPL(arizona_dvfs_sysclk_ev);
+
+void arizona_init_dvfs(struct arizona_priv *priv)
+{
+	mutex_init(&priv->dvfs_lock);
+}
+EXPORT_SYMBOL_GPL(arizona_init_dvfs);
+
 static unsigned int arizona_sysclk_48k_rates[] = {
 	6144000,
 	12288000,
@@ -1266,7 +1394,7 @@ static int arizona_hw_params_rate(struct snd_pcm_substream *substream,
 	struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec);
 	struct arizona_dai_priv *dai_priv = &priv->dai[dai->id - 1];
 	int base = dai->driver->base;
-	int i, sr_val;
+	int i, sr_val, ret;
 
 	/*
 	 * We will need to be more flexible than this in future,
@@ -1282,6 +1410,23 @@ static int arizona_hw_params_rate(struct snd_pcm_substream *substream,
 	}
 	sr_val = i;
 
+	switch (priv->arizona->type) {
+	case WM5102:
+	case WM8997:
+		if (arizona_sr_vals[sr_val] >= 88200)
+			ret = arizona_dvfs_up(codec, ARIZONA_DVFS_SR1_RQ);
+		else
+			ret = arizona_dvfs_down(codec, ARIZONA_DVFS_SR1_RQ);
+
+		if (ret) {
+			arizona_aif_err(dai, "Failed to change DVFS %d\n", ret);
+			return ret;
+		}
+		break;
+	default:
+		break;
+	}
+
 	switch (dai_priv->clk) {
 	case ARIZONA_CLK_SYSCLK:
 		switch (priv->arizona->type) {
@@ -1474,6 +1619,7 @@ static int arizona_dai_set_sysclk(struct snd_soc_dai *dai,
 				  int clk_id, unsigned int freq, int dir)
 {
 	struct snd_soc_codec *codec = dai->codec;
+	struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
 	struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec);
 	struct arizona_dai_priv *dai_priv = &priv->dai[dai->id - 1];
 	struct snd_soc_dapm_route routes[2];
@@ -1504,15 +1650,15 @@ static int arizona_dai_set_sysclk(struct snd_soc_dai *dai,
 
 	routes[0].source = arizona_dai_clk_str(dai_priv->clk);
 	routes[1].source = arizona_dai_clk_str(dai_priv->clk);
-	snd_soc_dapm_del_routes(&codec->dapm, routes, ARRAY_SIZE(routes));
+	snd_soc_dapm_del_routes(dapm, routes, ARRAY_SIZE(routes));
 
 	routes[0].source = arizona_dai_clk_str(clk_id);
 	routes[1].source = arizona_dai_clk_str(clk_id);
-	snd_soc_dapm_add_routes(&codec->dapm, routes, ARRAY_SIZE(routes));
+	snd_soc_dapm_add_routes(dapm, routes, ARRAY_SIZE(routes));
 
 	dai_priv->clk = clk_id;
 
-	return snd_soc_dapm_sync(&codec->dapm);
+	return snd_soc_dapm_sync(dapm);
 }
 
 static int arizona_set_tristate(struct snd_soc_dai *dai, int tristate)
diff --git a/sound/soc/codecs/arizona.h b/sound/soc/codecs/arizona.h
index 11ff899b0272..84e119a56515 100644
--- a/sound/soc/codecs/arizona.h
+++ b/sound/soc/codecs/arizona.h
@@ -60,6 +60,9 @@
 #define ARIZONA_MAX_DAI  6
 #define ARIZONA_MAX_ADSP 4
 
+#define ARIZONA_DVFS_SR1_RQ	0x001
+#define ARIZONA_DVFS_ADSP1_RQ	0x100
+
 struct arizona;
 struct wm_adsp;
 
@@ -84,6 +87,10 @@ struct arizona_priv {
 
 	unsigned int spk_ena:2;
 	unsigned int spk_ena_pending:1;
+
+	unsigned int dvfs_reqs;
+	struct mutex dvfs_lock;
+	bool dvfs_cached;
 };
 
 #define ARIZONA_NUM_MIXER_INPUTS 103
@@ -107,8 +114,8 @@ extern int arizona_mixer_values[ARIZONA_NUM_MIXER_INPUTS];
 			     arizona_mixer_tlv)
 
 #define ARIZONA_MUX_ENUM_DECL(name, reg) \
-	SOC_VALUE_ENUM_SINGLE_DECL(name, reg, 0, 0xff,			\
-				   arizona_mixer_texts, arizona_mixer_values)
+	SOC_VALUE_ENUM_SINGLE_AUTODISABLE_DECL( \
+		name, reg, 0, 0xff, arizona_mixer_texts, arizona_mixer_values)
 
 #define ARIZONA_MUX_CTL_DECL(name) \
 	const struct snd_kcontrol_new name##_mux =	\
@@ -245,6 +252,12 @@ struct arizona_fll {
 	char clock_ok_name[ARIZONA_FLL_NAME_LEN];
 };
 
+extern int arizona_dvfs_up(struct snd_soc_codec *codec, unsigned int flags);
+extern int arizona_dvfs_down(struct snd_soc_codec *codec, unsigned int flags);
+extern int arizona_dvfs_sysclk_ev(struct snd_soc_dapm_widget *w,
+				  struct snd_kcontrol *kcontrol, int event);
+extern void arizona_init_dvfs(struct arizona_priv *priv);
+
 extern int arizona_init_fll(struct arizona *arizona, int id, int base,
 			    int lock_irq, int ok_irq, struct arizona_fll *fll);
 extern int arizona_set_fll_refclk(struct arizona_fll *fll, int source,
diff --git a/sound/soc/codecs/bt-sco.c b/sound/soc/codecs/bt-sco.c
index e7238b8904bc..b084ad113e96 100644
--- a/sound/soc/codecs/bt-sco.c
+++ b/sound/soc/codecs/bt-sco.c
@@ -63,7 +63,7 @@ static int bt_sco_remove(struct platform_device *pdev)
 	return 0;
 }
 
-static struct platform_device_id bt_sco_driver_ids[] = {
+static const struct platform_device_id bt_sco_driver_ids[] = {
 	{
 		.name		= "dfbmcs320",
 	},
@@ -74,9 +74,18 @@ static struct platform_device_id bt_sco_driver_ids[] = {
 };
 MODULE_DEVICE_TABLE(platform, bt_sco_driver_ids);
 
+#if defined(CONFIG_OF)
+static const struct of_device_id bt_sco_codec_of_match[] = {
+	{ .compatible = "delta,dfbmcs320", },
+	{},
+};
+MODULE_DEVICE_TABLE(of, bt_sco_codec_of_match);
+#endif
+
 static struct platform_driver bt_sco_driver = {
 	.driver = {
 		.name = "bt-sco",
+		.of_match_table = of_match_ptr(bt_sco_codec_of_match),
 	},
 	.probe = bt_sco_probe,
 	.remove = bt_sco_remove,
diff --git a/sound/soc/codecs/cq93vc.c b/sound/soc/codecs/cq93vc.c
index d6dedd4eab29..1c895a53001d 100644
--- a/sound/soc/codecs/cq93vc.c
+++ b/sound/soc/codecs/cq93vc.c
@@ -92,7 +92,6 @@ static int cq93vc_set_bias_level(struct snd_soc_codec *codec,
 			     DAVINCI_VC_REG12_POWER_ALL_OFF);
 		break;
 	}
-	codec->dapm.bias_level = level;
 
 	return 0;
 }
diff --git a/sound/soc/codecs/cs35l32.c b/sound/soc/codecs/cs35l32.c
index 60598b230341..8f40025b7e7c 100644
--- a/sound/soc/codecs/cs35l32.c
+++ b/sound/soc/codecs/cs35l32.c
@@ -13,7 +13,6 @@
 
 #include <linux/module.h>
 #include <linux/moduleparam.h>
-#include <linux/version.h>
 #include <linux/kernel.h>
 #include <linux/init.h>
 #include <linux/delay.h>
diff --git a/sound/soc/codecs/cs4265.c b/sound/soc/codecs/cs4265.c
index cac48ddf3ba6..d7ec4756e45b 100644
--- a/sound/soc/codecs/cs4265.c
+++ b/sound/soc/codecs/cs4265.c
@@ -503,7 +503,6 @@ static int cs4265_set_bias_level(struct snd_soc_codec *codec,
 			CS4265_PWRCTL_PDN);
 		break;
 	}
-	codec->dapm.bias_level = level;
 	return 0;
 }
 
diff --git a/sound/soc/codecs/cs42l52.c b/sound/soc/codecs/cs42l52.c
index 1589e7a881d8..4de52c9957ac 100644
--- a/sound/soc/codecs/cs42l52.c
+++ b/sound/soc/codecs/cs42l52.c
@@ -897,7 +897,7 @@ static int cs42l52_set_bias_level(struct snd_soc_codec *codec,
 				    CS42L52_PWRCTL1_PDN_CODEC, 0);
 		break;
 	case SND_SOC_BIAS_STANDBY:
-		if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) {
+		if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) {
 			regcache_cache_only(cs42l52->regmap, false);
 			regcache_sync(cs42l52->regmap);
 		}
@@ -908,7 +908,6 @@ static int cs42l52_set_bias_level(struct snd_soc_codec *codec,
 		regcache_cache_only(cs42l52->regmap, true);
 		break;
 	}
-	codec->dapm.bias_level = level;
 
 	return 0;
 }
@@ -956,7 +955,7 @@ static void cs42l52_beep_work(struct work_struct *work)
 	struct cs42l52_private *cs42l52 =
 		container_of(work, struct cs42l52_private, beep_work);
 	struct snd_soc_codec *codec = cs42l52->codec;
-	struct snd_soc_dapm_context *dapm = &codec->dapm;
+	struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
 	int i;
 	int val = 0;
 	int best = 0;
diff --git a/sound/soc/codecs/cs42l56.c b/sound/soc/codecs/cs42l56.c
index cbc654fe48c7..1e11ba45a79f 100644
--- a/sound/soc/codecs/cs42l56.c
+++ b/sound/soc/codecs/cs42l56.c
@@ -953,7 +953,7 @@ static int cs42l56_set_bias_level(struct snd_soc_codec *codec,
 				    CS42L56_PDN_ALL_MASK, 0);
 		break;
 	case SND_SOC_BIAS_STANDBY:
-		if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) {
+		if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) {
 			regcache_cache_only(cs42l56->regmap, false);
 			regcache_sync(cs42l56->regmap);
 			ret = regulator_bulk_enable(ARRAY_SIZE(cs42l56->supplies),
@@ -978,7 +978,6 @@ static int cs42l56_set_bias_level(struct snd_soc_codec *codec,
 						    cs42l56->supplies);
 		break;
 	}
-	codec->dapm.bias_level = level;
 
 	return 0;
 }
@@ -1026,7 +1025,7 @@ static void cs42l56_beep_work(struct work_struct *work)
 	struct cs42l56_private *cs42l56 =
 		container_of(work, struct cs42l56_private, beep_work);
 	struct snd_soc_codec *codec = cs42l56->codec;
-	struct snd_soc_dapm_context *dapm = &codec->dapm;
+	struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
 	int i;
 	int val = 0;
 	int best = 0;
diff --git a/sound/soc/codecs/cs42l73.c b/sound/soc/codecs/cs42l73.c
index 8ecedba79606..b7853b9d3a60 100644
--- a/sound/soc/codecs/cs42l73.c
+++ b/sound/soc/codecs/cs42l73.c
@@ -1208,7 +1208,7 @@ static int cs42l73_set_bias_level(struct snd_soc_codec *codec,
 		break;
 
 	case SND_SOC_BIAS_STANDBY:
-		if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) {
+		if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) {
 			regcache_cache_only(cs42l73->regmap, false);
 			regcache_sync(cs42l73->regmap);
 		}
@@ -1228,7 +1228,6 @@ static int cs42l73_set_bias_level(struct snd_soc_codec *codec,
 		snd_soc_update_bits(codec, CS42L73_DMMCC, CS42L73_MCLKDIS, 1);
 		break;
 	}
-	codec->dapm.bias_level = level;
 	return 0;
 }
 
diff --git a/sound/soc/codecs/cs42xx8.c b/sound/soc/codecs/cs42xx8.c
index 670ebfe12903..e1d46862e81f 100644
--- a/sound/soc/codecs/cs42xx8.c
+++ b/sound/soc/codecs/cs42xx8.c
@@ -380,7 +380,7 @@ EXPORT_SYMBOL_GPL(cs42xx8_regmap_config);
 static int cs42xx8_codec_probe(struct snd_soc_codec *codec)
 {
 	struct cs42xx8_priv *cs42xx8 = snd_soc_codec_get_drvdata(codec);
-	struct snd_soc_dapm_context *dapm = &codec->dapm;
+	struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
 
 	switch (cs42xx8->drvdata->num_adcs) {
 	case 3:
diff --git a/sound/soc/codecs/cx20442.c b/sound/soc/codecs/cx20442.c
index 0f334bc1b63c..d6f4abbbf8a7 100644
--- a/sound/soc/codecs/cx20442.c
+++ b/sound/soc/codecs/cx20442.c
@@ -333,7 +333,7 @@ static int cx20442_set_bias_level(struct snd_soc_codec *codec,
 
 	switch (level) {
 	case SND_SOC_BIAS_PREPARE:
-		if (codec->dapm.bias_level != SND_SOC_BIAS_STANDBY)
+		if (snd_soc_codec_get_bias_level(codec) != SND_SOC_BIAS_STANDBY)
 			break;
 		if (IS_ERR(cx20442->por))
 			err = PTR_ERR(cx20442->por);
@@ -341,7 +341,7 @@ static int cx20442_set_bias_level(struct snd_soc_codec *codec,
 			err = regulator_enable(cx20442->por);
 		break;
 	case SND_SOC_BIAS_STANDBY:
-		if (codec->dapm.bias_level != SND_SOC_BIAS_PREPARE)
+		if (snd_soc_codec_get_bias_level(codec) != SND_SOC_BIAS_PREPARE)
 			break;
 		if (IS_ERR(cx20442->por))
 			err = PTR_ERR(cx20442->por);
@@ -351,8 +351,6 @@ static int cx20442_set_bias_level(struct snd_soc_codec *codec,
 	default:
 		break;
 	}
-	if (!err)
-		codec->dapm.bias_level = level;
 
 	return err;
 }
diff --git a/sound/soc/codecs/da7213.c b/sound/soc/codecs/da7213.c
index 9ec577f0edb4..238e48a3a4fe 100644
--- a/sound/soc/codecs/da7213.c
+++ b/sound/soc/codecs/da7213.c
@@ -1374,7 +1374,7 @@ static int da7213_set_bias_level(struct snd_soc_codec *codec,
 	case SND_SOC_BIAS_PREPARE:
 		break;
 	case SND_SOC_BIAS_STANDBY:
-		if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) {
+		if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) {
 			/* Enable VMID reference & master bias */
 			snd_soc_update_bits(codec, DA7213_REFERENCES,
 					    DA7213_VMID_EN | DA7213_BIAS_EN,
@@ -1387,7 +1387,6 @@ static int da7213_set_bias_level(struct snd_soc_codec *codec,
 				    DA7213_VMID_EN | DA7213_BIAS_EN, 0);
 		break;
 	}
-	codec->dapm.bias_level = level;
 	return 0;
 }
 
diff --git a/sound/soc/codecs/da732x.c b/sound/soc/codecs/da732x.c
index 911c26c705fc..207523686bd5 100644
--- a/sound/soc/codecs/da732x.c
+++ b/sound/soc/codecs/da732x.c
@@ -1432,7 +1432,7 @@ static int da732x_set_bias_level(struct snd_soc_codec *codec,
 	case SND_SOC_BIAS_PREPARE:
 		break;
 	case SND_SOC_BIAS_STANDBY:
-		if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) {
+		if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) {
 			/* Init Codec */
 			snd_soc_write(codec, DA732X_REG_REF1,
 				      DA732X_VMID_FASTCHG);
@@ -1502,8 +1502,6 @@ static int da732x_set_bias_level(struct snd_soc_codec *codec,
 		break;
 	}
 
-	codec->dapm.bias_level = level;
-
 	return 0;
 }
 
diff --git a/sound/soc/codecs/da9055.c b/sound/soc/codecs/da9055.c
index ad19cc56702b..66bb446473b8 100644
--- a/sound/soc/codecs/da9055.c
+++ b/sound/soc/codecs/da9055.c
@@ -1364,7 +1364,7 @@ static int da9055_set_bias_level(struct snd_soc_codec *codec,
 	case SND_SOC_BIAS_PREPARE:
 		break;
 	case SND_SOC_BIAS_STANDBY:
-		if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) {
+		if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) {
 			/* Enable VMID reference & master bias */
 			snd_soc_update_bits(codec, DA9055_REFERENCES,
 					    DA9055_VMID_EN | DA9055_BIAS_EN,
@@ -1377,7 +1377,6 @@ static int da9055_set_bias_level(struct snd_soc_codec *codec,
 				    DA9055_VMID_EN | DA9055_BIAS_EN, 0);
 		break;
 	}
-	codec->dapm.bias_level = level;
 	return 0;
 }
 
diff --git a/sound/soc/codecs/es8328.c b/sound/soc/codecs/es8328.c
index c5f35a07e8e4..6a091016e0fc 100644
--- a/sound/soc/codecs/es8328.c
+++ b/sound/soc/codecs/es8328.c
@@ -536,7 +536,7 @@ static int es8328_set_bias_level(struct snd_soc_codec *codec,
 		break;
 
 	case SND_SOC_BIAS_STANDBY:
-		if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) {
+		if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) {
 			snd_soc_update_bits(codec, ES8328_CONTROL1,
 					ES8328_CONTROL1_VMIDSEL_MASK |
 					ES8328_CONTROL1_ENREF,
@@ -566,7 +566,6 @@ static int es8328_set_bias_level(struct snd_soc_codec *codec,
 				0);
 		break;
 	}
-	codec->dapm.bias_level = level;
 	return 0;
 }
 
diff --git a/sound/soc/codecs/isabelle.c b/sound/soc/codecs/isabelle.c
index 3a89ce66d51d..ebd90283c960 100644
--- a/sound/soc/codecs/isabelle.c
+++ b/sound/soc/codecs/isabelle.c
@@ -909,8 +909,6 @@ static int isabelle_set_bias_level(struct snd_soc_codec *codec,
 		break;
 	}
 
-	codec->dapm.bias_level = level;
-
 	return 0;
 }
 
diff --git a/sound/soc/codecs/jz4740.c b/sound/soc/codecs/jz4740.c
index 933f4476d76c..9363fdbca9cd 100644
--- a/sound/soc/codecs/jz4740.c
+++ b/sound/soc/codecs/jz4740.c
@@ -258,7 +258,7 @@ static int jz4740_codec_set_bias_level(struct snd_soc_codec *codec,
 		break;
 	case SND_SOC_BIAS_STANDBY:
 		/* The only way to clear the suspend flag is to reset the codec */
-		if (codec->dapm.bias_level == SND_SOC_BIAS_OFF)
+		if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF)
 			jz4740_codec_wakeup(regmap);
 
 		mask = JZ4740_CODEC_1_VREF_DISABLE |
@@ -281,8 +281,6 @@ static int jz4740_codec_set_bias_level(struct snd_soc_codec *codec,
 		break;
 	}
 
-	codec->dapm.bias_level = level;
-
 	return 0;
 }
 
diff --git a/sound/soc/codecs/lm4857.c b/sound/soc/codecs/lm4857.c
index a924bb9d7886..99ffc49aa779 100644
--- a/sound/soc/codecs/lm4857.c
+++ b/sound/soc/codecs/lm4857.c
@@ -23,11 +23,6 @@
 #include <sound/soc.h>
 #include <sound/tlv.h>
 
-struct lm4857 {
-	struct regmap *regmap;
-	uint8_t mode;
-};
-
 static const struct reg_default lm4857_default_regs[] = {
 	{ 0x0, 0x00 },
 	{ 0x1, 0x00 },
@@ -46,66 +41,33 @@ static const struct reg_default lm4857_default_regs[] = {
 #define LM4857_WAKEUP 5
 #define LM4857_EPGAIN 4
 
-static int lm4857_get_mode(struct snd_kcontrol *kcontrol,
-	struct snd_ctl_elem_value *ucontrol)
-{
-	struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
-	struct lm4857 *lm4857 = snd_soc_codec_get_drvdata(codec);
-
-	ucontrol->value.integer.value[0] = lm4857->mode;
-
-	return 0;
-}
-
-static int lm4857_set_mode(struct snd_kcontrol *kcontrol,
-	struct snd_ctl_elem_value *ucontrol)
-{
-	struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
-	struct lm4857 *lm4857 = snd_soc_codec_get_drvdata(codec);
-	uint8_t value = ucontrol->value.integer.value[0];
-
-	lm4857->mode = value;
-
-	if (codec->dapm.bias_level == SND_SOC_BIAS_ON)
-		regmap_update_bits(lm4857->regmap, LM4857_CTRL, 0x0F, value + 6);
-
-	return 1;
-}
-
-static int lm4857_set_bias_level(struct snd_soc_codec *codec,
-				 enum snd_soc_bias_level level)
-{
-	struct lm4857 *lm4857 = snd_soc_codec_get_drvdata(codec);
-
-	switch (level) {
-	case SND_SOC_BIAS_ON:
-		regmap_update_bits(lm4857->regmap, LM4857_CTRL, 0x0F,
-			lm4857->mode + 6);
-		break;
-	case SND_SOC_BIAS_STANDBY:
-		regmap_update_bits(lm4857->regmap, LM4857_CTRL, 0x0F, 0);
-		break;
-	default:
-		break;
-	}
-
-	codec->dapm.bias_level = level;
-
-	return 0;
-}
+static const unsigned int lm4857_mode_values[] = {
+	0,
+	6,
+	7,
+	8,
+	9,
+};
 
-static const char *lm4857_mode[] = {
+static const char * const lm4857_mode_texts[] = {
+	"Off",
 	"Earpiece",
 	"Loudspeaker",
 	"Loudspeaker + Headphone",
 	"Headphone",
 };
 
-static SOC_ENUM_SINGLE_EXT_DECL(lm4857_mode_enum, lm4857_mode);
+static SOC_VALUE_ENUM_SINGLE_AUTODISABLE_DECL(lm4857_mode_enum,
+	LM4857_CTRL, 0, 0xf, lm4857_mode_texts, lm4857_mode_values);
+
+static const struct snd_kcontrol_new lm4857_mode_ctrl =
+	SOC_DAPM_ENUM("Mode", lm4857_mode_enum);
 
 static const struct snd_soc_dapm_widget lm4857_dapm_widgets[] = {
 	SND_SOC_DAPM_INPUT("IN"),
 
+	SND_SOC_DAPM_DEMUX("Mode", SND_SOC_NOPM, 0, 0, &lm4857_mode_ctrl),
+
 	SND_SOC_DAPM_OUTPUT("LS"),
 	SND_SOC_DAPM_OUTPUT("HP"),
 	SND_SOC_DAPM_OUTPUT("EP"),
@@ -127,24 +89,18 @@ static const struct snd_kcontrol_new lm4857_controls[] = {
 		LM4857_WAKEUP, 1, 0),
 	SOC_SINGLE("Earpiece 6dB Playback Switch", LM4857_CTRL,
 		LM4857_EPGAIN, 1, 0),
-
-	SOC_ENUM_EXT("Mode", lm4857_mode_enum,
-		lm4857_get_mode, lm4857_set_mode),
 };
 
-/* There is a demux between the input signal and the output signals.
- * Currently there is no easy way to model it in ASoC and since it does not make
- * much of a difference in practice simply connect the input direclty to the
- * outputs. */
 static const struct snd_soc_dapm_route lm4857_routes[] = {
-	{"LS", NULL, "IN"},
-	{"HP", NULL, "IN"},
-	{"EP", NULL, "IN"},
+	{ "Mode", NULL, "IN" },
+	{ "LS", "Loudspeaker", "Mode" },
+	{ "LS", "Loudspeaker + Headphone", "Mode" },
+	{ "HP", "Headphone", "Mode" },
+	{ "HP", "Loudspeaker + Headphone", "Mode" },
+	{ "EP", "Earpiece", "Mode" },
 };
 
-static struct snd_soc_codec_driver soc_codec_dev_lm4857 = {
-	.set_bias_level = lm4857_set_bias_level,
-
+static struct snd_soc_component_driver lm4857_component_driver = {
 	.controls = lm4857_controls,
 	.num_controls = ARRAY_SIZE(lm4857_controls),
 	.dapm_widgets = lm4857_dapm_widgets,
@@ -167,25 +123,14 @@ static const struct regmap_config lm4857_regmap_config = {
 static int lm4857_i2c_probe(struct i2c_client *i2c,
 			    const struct i2c_device_id *id)
 {
-	struct lm4857 *lm4857;
-
-	lm4857 = devm_kzalloc(&i2c->dev, sizeof(*lm4857), GFP_KERNEL);
-	if (!lm4857)
-		return -ENOMEM;
-
-	i2c_set_clientdata(i2c, lm4857);
-
-	lm4857->regmap = devm_regmap_init_i2c(i2c, &lm4857_regmap_config);
-	if (IS_ERR(lm4857->regmap))
-		return PTR_ERR(lm4857->regmap);
+	struct regmap *regmap;
 
-	return snd_soc_register_codec(&i2c->dev, &soc_codec_dev_lm4857, NULL, 0);
-}
+	regmap = devm_regmap_init_i2c(i2c, &lm4857_regmap_config);
+	if (IS_ERR(regmap))
+		return PTR_ERR(regmap);
 
-static int lm4857_i2c_remove(struct i2c_client *i2c)
-{
-	snd_soc_unregister_codec(&i2c->dev);
-	return 0;
+	return devm_snd_soc_register_component(&i2c->dev,
+		&lm4857_component_driver, NULL, 0);
 }
 
 static const struct i2c_device_id lm4857_i2c_id[] = {
@@ -200,7 +145,6 @@ static struct i2c_driver lm4857_i2c_driver = {
 		.owner = THIS_MODULE,
 	},
 	.probe = lm4857_i2c_probe,
-	.remove = lm4857_i2c_remove,
 	.id_table = lm4857_i2c_id,
 };
 
diff --git a/sound/soc/codecs/lm49453.c b/sound/soc/codecs/lm49453.c
index c4dfde9bdf1c..6600aa0a33dc 100644
--- a/sound/soc/codecs/lm49453.c
+++ b/sound/soc/codecs/lm49453.c
@@ -1271,7 +1271,7 @@ static int lm49453_set_bias_level(struct snd_soc_codec *codec,
 		break;
 
 	case SND_SOC_BIAS_STANDBY:
-		if (codec->dapm.bias_level == SND_SOC_BIAS_OFF)
+		if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF)
 			regcache_sync(lm49453->regmap);
 
 		snd_soc_update_bits(codec, LM49453_P0_PMC_SETUP_REG,
@@ -1284,8 +1284,6 @@ static int lm49453_set_bias_level(struct snd_soc_codec *codec,
 		break;
 	}
 
-	codec->dapm.bias_level = level;
-
 	return 0;
 }
 
diff --git a/sound/soc/codecs/max98088.c b/sound/soc/codecs/max98088.c
index 805b3f8cd39d..d0f45348bfbb 100644
--- a/sound/soc/codecs/max98088.c
+++ b/sound/soc/codecs/max98088.c
@@ -1571,7 +1571,7 @@ static int max98088_set_bias_level(struct snd_soc_codec *codec,
 		break;
 
 	case SND_SOC_BIAS_STANDBY:
-		if (codec->dapm.bias_level == SND_SOC_BIAS_OFF)
+		if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF)
 			regcache_sync(max98088->regmap);
 
 		snd_soc_update_bits(codec, M98088_REG_4C_PWR_EN_IN,
@@ -1584,7 +1584,6 @@ static int max98088_set_bias_level(struct snd_soc_codec *codec,
 		regcache_mark_dirty(max98088->regmap);
 		break;
 	}
-	codec->dapm.bias_level = level;
 	return 0;
 }
 
diff --git a/sound/soc/codecs/max98090.c b/sound/soc/codecs/max98090.c
index 3e33ef2acf3c..679f0a0f7039 100644
--- a/sound/soc/codecs/max98090.c
+++ b/sound/soc/codecs/max98090.c
@@ -1500,7 +1500,7 @@ static const struct snd_soc_dapm_route max98091_dapm_routes[] = {
 static int max98090_add_widgets(struct snd_soc_codec *codec)
 {
 	struct max98090_priv *max98090 = snd_soc_codec_get_drvdata(codec);
-	struct snd_soc_dapm_context *dapm = &codec->dapm;
+	struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
 
 	snd_soc_add_codec_controls(codec, max98090_snd_controls,
 		ARRAY_SIZE(max98090_snd_controls));
@@ -1798,16 +1798,17 @@ static int max98090_set_bias_level(struct snd_soc_codec *codec,
 		 * away from ON. Disable the clock in that case, otherwise
 		 * enable it.
 		 */
-		if (!IS_ERR(max98090->mclk)) {
-			if (codec->dapm.bias_level == SND_SOC_BIAS_ON)
-				clk_disable_unprepare(max98090->mclk);
-			else
-				clk_prepare_enable(max98090->mclk);
-		}
+		if (IS_ERR(max98090->mclk))
+			break;
+
+		if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_ON)
+			clk_disable_unprepare(max98090->mclk);
+		else
+			clk_prepare_enable(max98090->mclk);
 		break;
 
 	case SND_SOC_BIAS_STANDBY:
-		if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) {
+		if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) {
 			ret = regcache_sync(max98090->regmap);
 			if (ret != 0) {
 				dev_err(codec->dev,
@@ -1824,7 +1825,6 @@ static int max98090_set_bias_level(struct snd_soc_codec *codec,
 		regcache_mark_dirty(max98090->regmap);
 		break;
 	}
-	codec->dapm.bias_level = level;
 	return 0;
 }
 
@@ -2187,7 +2187,6 @@ static void max98090_jack_work(struct work_struct *work)
 		struct max98090_priv,
 		jack_work.work);
 	struct snd_soc_codec *codec = max98090->codec;
-	struct snd_soc_dapm_context *dapm = &codec->dapm;
 	int status = 0;
 	int reg;
 
@@ -2266,8 +2265,6 @@ static void max98090_jack_work(struct work_struct *work)
 
 	snd_soc_jack_report(max98090->jack, status,
 			    SND_JACK_HEADSET | SND_JACK_BTN_0);
-
-	snd_soc_dapm_sync(dapm);
 }
 
 static irqreturn_t max98090_interrupt(int irq, void *data)
@@ -2422,6 +2419,8 @@ static int max98090_probe(struct snd_soc_codec *codec)
 	struct max98090_cdata *cdata;
 	enum max98090_type devtype;
 	int ret = 0;
+	int err;
+	unsigned int micbias;
 
 	dev_dbg(codec->dev, "max98090_probe\n");
 
@@ -2506,8 +2505,17 @@ static int max98090_probe(struct snd_soc_codec *codec)
 	snd_soc_write(codec, M98090_REG_BIAS_CONTROL,
 		M98090_VCM_MODE_MASK);
 
+	err = device_property_read_u32(codec->dev, "maxim,micbias", &micbias);
+	if (err) {
+		micbias = M98090_MBVSEL_2V8;
+		dev_info(codec->dev, "use default 2.8v micbias\n");
+	} else if (micbias < M98090_MBVSEL_2V2 || micbias > M98090_MBVSEL_2V8) {
+		dev_err(codec->dev, "micbias out of range 0x%x\n", micbias);
+		micbias = M98090_MBVSEL_2V8;
+	}
+
 	snd_soc_update_bits(codec, M98090_REG_MIC_BIAS_VOLTAGE,
-		M98090_MBVSEL_MASK, M98090_MBVSEL_2V8);
+		M98090_MBVSEL_MASK, micbias);
 
 	max98090_add_widgets(codec);
 
diff --git a/sound/soc/codecs/max98095.c b/sound/soc/codecs/max98095.c
index 8fba0c3db798..9a46d3dcf703 100644
--- a/sound/soc/codecs/max98095.c
+++ b/sound/soc/codecs/max98095.c
@@ -1650,16 +1650,17 @@ static int max98095_set_bias_level(struct snd_soc_codec *codec,
 		 * away from ON. Disable the clock in that case, otherwise
 		 * enable it.
 		 */
-		if (!IS_ERR(max98095->mclk)) {
-			if (codec->dapm.bias_level == SND_SOC_BIAS_ON)
-				clk_disable_unprepare(max98095->mclk);
-			else
-				clk_prepare_enable(max98095->mclk);
-		}
+		if (IS_ERR(max98095->mclk))
+			break;
+
+		if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_ON)
+			clk_disable_unprepare(max98095->mclk);
+		else
+			clk_prepare_enable(max98095->mclk);
 		break;
 
 	case SND_SOC_BIAS_STANDBY:
-		if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) {
+		if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) {
 			ret = regcache_sync(max98095->regmap);
 
 			if (ret != 0) {
@@ -1678,7 +1679,6 @@ static int max98095_set_bias_level(struct snd_soc_codec *codec,
 		regcache_mark_dirty(max98095->regmap);
 		break;
 	}
-	codec->dapm.bias_level = level;
 	return 0;
 }
 
@@ -2198,7 +2198,7 @@ static int max98095_suspend(struct snd_soc_codec *codec)
 	if (max98095->headphone_jack || max98095->mic_jack)
 		max98095_jack_detect_disable(codec);
 
-	max98095_set_bias_level(codec, SND_SOC_BIAS_OFF);
+	snd_soc_codec_force_bias_level(codec, SND_SOC_BIAS_OFF);
 
 	return 0;
 }
@@ -2208,7 +2208,7 @@ static int max98095_resume(struct snd_soc_codec *codec)
 	struct max98095_priv *max98095 = snd_soc_codec_get_drvdata(codec);
 	struct i2c_client *client = to_i2c_client(codec->dev);
 
-	max98095_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+	snd_soc_codec_force_bias_level(codec, SND_SOC_BIAS_STANDBY);
 
 	if (max98095->headphone_jack || max98095->mic_jack) {
 		max98095_jack_detect_enable(codec);
@@ -2301,8 +2301,8 @@ static int max98095_probe(struct snd_soc_codec *codec)
 		/* register an audio interrupt */
 		ret = request_threaded_irq(client->irq, NULL,
 			max98095_report_jack,
-			IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING,
-			"max98095", codec);
+			IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING |
+			IRQF_ONESHOT, "max98095", codec);
 		if (ret) {
 			dev_err(codec->dev, "Failed to request IRQ: %d\n", ret);
 			goto err_access;
diff --git a/sound/soc/codecs/max98357a.c b/sound/soc/codecs/max98357a.c
index bf3e933ee895..3a2fda08a893 100644
--- a/sound/soc/codecs/max98357a.c
+++ b/sound/soc/codecs/max98357a.c
@@ -60,13 +60,12 @@ static int max98357a_codec_probe(struct snd_soc_codec *codec)
 {
 	struct gpio_desc *sdmode;
 
-	sdmode = devm_gpiod_get(codec->dev, "sdmode");
+	sdmode = devm_gpiod_get(codec->dev, "sdmode", GPIOD_OUT_LOW);
 	if (IS_ERR(sdmode)) {
 		dev_err(codec->dev, "%s() unable to get sdmode GPIO: %ld\n",
 				__func__, PTR_ERR(sdmode));
 		return PTR_ERR(sdmode);
 	}
-	gpiod_direction_output(sdmode, 0);
 	snd_soc_codec_set_drvdata(codec, sdmode);
 
 	return 0;
diff --git a/sound/soc/codecs/max9850.c b/sound/soc/codecs/max9850.c
index 10f8e47ce2c2..481d58f1cb3f 100644
--- a/sound/soc/codecs/max9850.c
+++ b/sound/soc/codecs/max9850.c
@@ -252,7 +252,7 @@ static int max9850_set_bias_level(struct snd_soc_codec *codec,
 	case SND_SOC_BIAS_PREPARE:
 		break;
 	case SND_SOC_BIAS_STANDBY:
-		if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) {
+		if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) {
 			ret = regcache_sync(max9850->regmap);
 			if (ret) {
 				dev_err(codec->dev,
@@ -264,7 +264,6 @@ static int max9850_set_bias_level(struct snd_soc_codec *codec,
 	case SND_SOC_BIAS_OFF:
 		break;
 	}
-	codec->dapm.bias_level = level;
 	return 0;
 }
 
diff --git a/sound/soc/codecs/ml26124.c b/sound/soc/codecs/ml26124.c
index 711f55039522..62dda2488f14 100644
--- a/sound/soc/codecs/ml26124.c
+++ b/sound/soc/codecs/ml26124.c
@@ -523,7 +523,7 @@ static int ml26124_set_bias_level(struct snd_soc_codec *codec,
 		break;
 	case SND_SOC_BIAS_STANDBY:
 		/* VMID ON */
-		if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) {
+		if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) {
 			snd_soc_update_bits(codec, ML26124_PW_REF_PW_MNG,
 					    ML26124_VMID, ML26124_VMID);
 			msleep(500);
@@ -536,7 +536,6 @@ static int ml26124_set_bias_level(struct snd_soc_codec *codec,
 				    ML26124_VMID, 0);
 		break;
 	}
-	codec->dapm.bias_level = level;
 	return 0;
 }
 
diff --git a/sound/soc/codecs/pcm512x.c b/sound/soc/codecs/pcm512x.c
index e12764d15431..de16429f0a43 100644
--- a/sound/soc/codecs/pcm512x.c
+++ b/sound/soc/codecs/pcm512x.c
@@ -242,7 +242,7 @@ static int pcm512x_overclock_pll_put(struct snd_kcontrol *kcontrol,
 	struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
 	struct pcm512x_priv *pcm512x = snd_soc_codec_get_drvdata(codec);
 
-	switch (codec->dapm.bias_level) {
+	switch (snd_soc_codec_get_bias_level(codec)) {
 	case SND_SOC_BIAS_OFF:
 	case SND_SOC_BIAS_STANDBY:
 		break;
@@ -270,7 +270,7 @@ static int pcm512x_overclock_dsp_put(struct snd_kcontrol *kcontrol,
 	struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
 	struct pcm512x_priv *pcm512x = snd_soc_codec_get_drvdata(codec);
 
-	switch (codec->dapm.bias_level) {
+	switch (snd_soc_codec_get_bias_level(codec)) {
 	case SND_SOC_BIAS_OFF:
 	case SND_SOC_BIAS_STANDBY:
 		break;
@@ -298,7 +298,7 @@ static int pcm512x_overclock_dac_put(struct snd_kcontrol *kcontrol,
 	struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
 	struct pcm512x_priv *pcm512x = snd_soc_codec_get_drvdata(codec);
 
-	switch (codec->dapm.bias_level) {
+	switch (snd_soc_codec_get_bias_level(codec)) {
 	case SND_SOC_BIAS_OFF:
 	case SND_SOC_BIAS_STANDBY:
 		break;
@@ -641,8 +641,6 @@ static int pcm512x_set_bias_level(struct snd_soc_codec *codec,
 		break;
 	}
 
-	codec->dapm.bias_level = level;
-
 	return 0;
 }
 
diff --git a/sound/soc/codecs/rt286.c b/sound/soc/codecs/rt286.c
index 0fcda35a3a93..c6cca0639e0d 100644
--- a/sound/soc/codecs/rt286.c
+++ b/sound/soc/codecs/rt286.c
@@ -301,6 +301,7 @@ static int rt286_support_power_controls[] = {
 
 static int rt286_jack_detect(struct rt286_priv *rt286, bool *hp, bool *mic)
 {
+	struct snd_soc_dapm_context *dapm;
 	unsigned int val, buf;
 
 	*hp = false;
@@ -308,6 +309,9 @@ static int rt286_jack_detect(struct rt286_priv *rt286, bool *hp, bool *mic)
 
 	if (!rt286->codec)
 		return -EINVAL;
+
+	dapm = snd_soc_codec_get_dapm(rt286->codec);
+
 	if (rt286->pdata.cbj_en) {
 		regmap_read(rt286->regmap, RT286_GET_HP_SENSE, &buf);
 		*hp = buf & 0x80000000;
@@ -316,14 +320,11 @@ static int rt286_jack_detect(struct rt286_priv *rt286, bool *hp, bool *mic)
 			regmap_update_bits(rt286->regmap,
 				RT286_DC_GAIN, 0x200, 0x200);
 
-			snd_soc_dapm_force_enable_pin(&rt286->codec->dapm,
-							"HV");
-			snd_soc_dapm_force_enable_pin(&rt286->codec->dapm,
-							"VREF");
+			snd_soc_dapm_force_enable_pin(dapm, "HV");
+			snd_soc_dapm_force_enable_pin(dapm, "VREF");
 			/* power LDO1 */
-			snd_soc_dapm_force_enable_pin(&rt286->codec->dapm,
-							"LDO1");
-			snd_soc_dapm_sync(&rt286->codec->dapm);
+			snd_soc_dapm_force_enable_pin(dapm, "LDO1");
+			snd_soc_dapm_sync(dapm);
 
 			regmap_write(rt286->regmap, RT286_SET_MIC1, 0x24);
 			msleep(50);
@@ -360,11 +361,11 @@ static int rt286_jack_detect(struct rt286_priv *rt286, bool *hp, bool *mic)
 		*mic = buf & 0x80000000;
 	}
 
-	snd_soc_dapm_disable_pin(&rt286->codec->dapm, "HV");
-	snd_soc_dapm_disable_pin(&rt286->codec->dapm, "VREF");
+	snd_soc_dapm_disable_pin(dapm, "HV");
+	snd_soc_dapm_disable_pin(dapm, "VREF");
 	if (!*hp)
-		snd_soc_dapm_disable_pin(&rt286->codec->dapm, "LDO1");
-	snd_soc_dapm_sync(&rt286->codec->dapm);
+		snd_soc_dapm_disable_pin(dapm, "LDO1");
+	snd_soc_dapm_sync(dapm);
 
 	return 0;
 }
@@ -391,6 +392,7 @@ static void rt286_jack_detect_work(struct work_struct *work)
 
 int rt286_mic_detect(struct snd_soc_codec *codec, struct snd_soc_jack *jack)
 {
+	struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
 	struct rt286_priv *rt286 = snd_soc_codec_get_drvdata(codec);
 
 	rt286->jack = jack;
@@ -398,7 +400,7 @@ int rt286_mic_detect(struct snd_soc_codec *codec, struct snd_soc_jack *jack)
 	if (jack) {
 		/* enable IRQ */
 		if (rt286->jack->status & SND_JACK_HEADPHONE)
-			snd_soc_dapm_force_enable_pin(&codec->dapm, "LDO1");
+			snd_soc_dapm_force_enable_pin(dapm, "LDO1");
 		regmap_update_bits(rt286->regmap, RT286_IRQ_CTRL, 0x2, 0x2);
 		/* Send an initial empty report */
 		snd_soc_jack_report(rt286->jack, rt286->jack->status,
@@ -406,9 +408,9 @@ int rt286_mic_detect(struct snd_soc_codec *codec, struct snd_soc_jack *jack)
 	} else {
 		/* disable IRQ */
 		regmap_update_bits(rt286->regmap, RT286_IRQ_CTRL, 0x2, 0x0);
-		snd_soc_dapm_disable_pin(&codec->dapm, "LDO1");
+		snd_soc_dapm_disable_pin(dapm, "LDO1");
 	}
-	snd_soc_dapm_sync(&codec->dapm);
+	snd_soc_dapm_sync(dapm);
 
 	return 0;
 }
@@ -985,7 +987,7 @@ static int rt286_set_bias_level(struct snd_soc_codec *codec,
 {
 	switch (level) {
 	case SND_SOC_BIAS_PREPARE:
-		if (SND_SOC_BIAS_STANDBY == codec->dapm.bias_level) {
+		if (SND_SOC_BIAS_STANDBY == snd_soc_codec_get_bias_level(codec)) {
 			snd_soc_write(codec,
 				RT286_SET_AUDIO_POWER, AC_PWRST_D0);
 			snd_soc_update_bits(codec,
@@ -1012,7 +1014,6 @@ static int rt286_set_bias_level(struct snd_soc_codec *codec,
 	default:
 		break;
 	}
-	codec->dapm.bias_level = level;
 
 	return 0;
 }
diff --git a/sound/soc/codecs/rt5631.c b/sound/soc/codecs/rt5631.c
index 2c10d77727af..058167c80d71 100644
--- a/sound/soc/codecs/rt5631.c
+++ b/sound/soc/codecs/rt5631.c
@@ -1546,7 +1546,7 @@ static int rt5631_set_bias_level(struct snd_soc_codec *codec,
 		break;
 
 	case SND_SOC_BIAS_STANDBY:
-		if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) {
+		if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) {
 			snd_soc_update_bits(codec, RT5631_PWR_MANAG_ADD3,
 				RT5631_PWR_VREF | RT5631_PWR_MAIN_BIAS,
 				RT5631_PWR_VREF | RT5631_PWR_MAIN_BIAS);
@@ -1569,7 +1569,6 @@ static int rt5631_set_bias_level(struct snd_soc_codec *codec,
 	default:
 		break;
 	}
-	codec->dapm.bias_level = level;
 
 	return 0;
 }
@@ -1615,7 +1614,7 @@ static int rt5631_probe(struct snd_soc_codec *codec)
 			RT5631_DMIC_R_CH_LATCH_RISING);
 	}
 
-	codec->dapm.bias_level = SND_SOC_BIAS_STANDBY;
+	snd_soc_codec_init_bias_level(codec, SND_SOC_BIAS_STANDBY);
 
 	return 0;
 }
diff --git a/sound/soc/codecs/rt5640.c b/sound/soc/codecs/rt5640.c
index 178e55d4d481..f40752a6c242 100644
--- a/sound/soc/codecs/rt5640.c
+++ b/sound/soc/codecs/rt5640.c
@@ -1870,7 +1870,7 @@ static int rt5640_set_bias_level(struct snd_soc_codec *codec,
 {
 	switch (level) {
 	case SND_SOC_BIAS_STANDBY:
-		if (SND_SOC_BIAS_OFF == codec->dapm.bias_level) {
+		if (SND_SOC_BIAS_OFF == snd_soc_codec_get_bias_level(codec)) {
 			snd_soc_update_bits(codec, RT5640_PWR_ANLG1,
 				RT5640_PWR_VREF1 | RT5640_PWR_MB |
 				RT5640_PWR_BG | RT5640_PWR_VREF2,
@@ -1902,7 +1902,6 @@ static int rt5640_set_bias_level(struct snd_soc_codec *codec,
 	default:
 		break;
 	}
-	codec->dapm.bias_level = level;
 
 	return 0;
 }
@@ -1935,11 +1934,12 @@ EXPORT_SYMBOL_GPL(rt5640_dmic_enable);
 
 static int rt5640_probe(struct snd_soc_codec *codec)
 {
+	struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
 	struct rt5640_priv *rt5640 = snd_soc_codec_get_drvdata(codec);
 
 	rt5640->codec = codec;
 
-	rt5640_set_bias_level(codec, SND_SOC_BIAS_OFF);
+	snd_soc_codec_force_bias_level(codec, SND_SOC_BIAS_OFF);
 
 	snd_soc_update_bits(codec, RT5640_DUMMY1, 0x0301, 0x0301);
 	snd_soc_update_bits(codec, RT5640_MICBIAS, 0x0030, 0x0030);
@@ -1951,18 +1951,18 @@ static int rt5640_probe(struct snd_soc_codec *codec)
 		snd_soc_add_codec_controls(codec,
 			rt5640_specific_snd_controls,
 			ARRAY_SIZE(rt5640_specific_snd_controls));
-		snd_soc_dapm_new_controls(&codec->dapm,
+		snd_soc_dapm_new_controls(dapm,
 			rt5640_specific_dapm_widgets,
 			ARRAY_SIZE(rt5640_specific_dapm_widgets));
-		snd_soc_dapm_add_routes(&codec->dapm,
+		snd_soc_dapm_add_routes(dapm,
 			rt5640_specific_dapm_routes,
 			ARRAY_SIZE(rt5640_specific_dapm_routes));
 		break;
 	case RT5640_ID_5639:
-		snd_soc_dapm_new_controls(&codec->dapm,
+		snd_soc_dapm_new_controls(dapm,
 			rt5639_specific_dapm_widgets,
 			ARRAY_SIZE(rt5639_specific_dapm_widgets));
-		snd_soc_dapm_add_routes(&codec->dapm,
+		snd_soc_dapm_add_routes(dapm,
 			rt5639_specific_dapm_routes,
 			ARRAY_SIZE(rt5639_specific_dapm_routes));
 		break;
@@ -1991,7 +1991,7 @@ static int rt5640_suspend(struct snd_soc_codec *codec)
 {
 	struct rt5640_priv *rt5640 = snd_soc_codec_get_drvdata(codec);
 
-	rt5640_set_bias_level(codec, SND_SOC_BIAS_OFF);
+	snd_soc_codec_force_bias_level(codec, SND_SOC_BIAS_OFF);
 	rt5640_reset(codec);
 	regcache_cache_only(rt5640->regmap, true);
 	regcache_mark_dirty(rt5640->regmap);
diff --git a/sound/soc/codecs/rt5645.c b/sound/soc/codecs/rt5645.c
index c82301484156..d5f0f5680d3b 100644
--- a/sound/soc/codecs/rt5645.c
+++ b/sound/soc/codecs/rt5645.c
@@ -18,7 +18,9 @@
 #include <linux/platform_device.h>
 #include <linux/spi/spi.h>
 #include <linux/gpio.h>
+#include <linux/gpio/consumer.h>
 #include <linux/acpi.h>
+#include <linux/dmi.h>
 #include <sound/core.h>
 #include <sound/pcm.h>
 #include <sound/pcm_params.h>
@@ -415,9 +417,9 @@ static bool rt5645_readable_register(struct device *dev, unsigned int reg)
 }
 
 static const DECLARE_TLV_DB_SCALE(out_vol_tlv, -4650, 150, 0);
-static const DECLARE_TLV_DB_SCALE(dac_vol_tlv, -65625, 375, 0);
+static const DECLARE_TLV_DB_SCALE(dac_vol_tlv, -6525, 75, 0);
 static const DECLARE_TLV_DB_SCALE(in_vol_tlv, -3450, 150, 0);
-static const DECLARE_TLV_DB_SCALE(adc_vol_tlv, -17625, 375, 0);
+static const DECLARE_TLV_DB_SCALE(adc_vol_tlv, -1725, 75, 0);
 static const DECLARE_TLV_DB_SCALE(adc_bst_tlv, 0, 1200, 0);
 
 /* {0, +20, +24, +30, +35, +40, +44, +50, +52} dB */
@@ -432,30 +434,6 @@ static unsigned int bst_tlv[] = {
 	8, 8, TLV_DB_SCALE_ITEM(5200, 0, 0),
 };
 
-static const char * const rt5645_tdm_data_swap_select[] = {
-	"L/R", "R/L", "L/L", "R/R"
-};
-
-static SOC_ENUM_SINGLE_DECL(rt5645_tdm_adc_slot0_1_enum,
-	RT5645_TDM_CTRL_1, 6, rt5645_tdm_data_swap_select);
-
-static SOC_ENUM_SINGLE_DECL(rt5645_tdm_adc_slot2_3_enum,
-	RT5645_TDM_CTRL_1, 4, rt5645_tdm_data_swap_select);
-
-static SOC_ENUM_SINGLE_DECL(rt5645_tdm_adc_slot4_5_enum,
-	RT5645_TDM_CTRL_1, 2, rt5645_tdm_data_swap_select);
-
-static SOC_ENUM_SINGLE_DECL(rt5645_tdm_adc_slot6_7_enum,
-	RT5645_TDM_CTRL_1, 0, rt5645_tdm_data_swap_select);
-
-static const char * const rt5645_tdm_adc_data_select[] = {
-	"1/2/R", "2/1/R", "R/1/2", "R/2/1"
-};
-
-static SOC_ENUM_SINGLE_DECL(rt5645_tdm_adc_sel_enum,
-				RT5645_TDM_CTRL_1, 8,
-				rt5645_tdm_adc_data_select);
-
 static const struct snd_kcontrol_new rt5645_snd_controls[] = {
 	/* Speaker Output Volume */
 	SOC_DOUBLE("Speaker Channel Switch", RT5645_SPK_VOL,
@@ -481,9 +459,9 @@ static const struct snd_kcontrol_new rt5645_snd_controls[] = {
 	SOC_DOUBLE("DAC2 Playback Switch", RT5645_DAC_CTRL,
 		RT5645_M_DAC_L2_VOL_SFT, RT5645_M_DAC_R2_VOL_SFT, 1, 1),
 	SOC_DOUBLE_TLV("DAC1 Playback Volume", RT5645_DAC1_DIG_VOL,
-		RT5645_L_VOL_SFT, RT5645_R_VOL_SFT, 175, 0, dac_vol_tlv),
+		RT5645_L_VOL_SFT + 1, RT5645_R_VOL_SFT + 1, 87, 0, dac_vol_tlv),
 	SOC_DOUBLE_TLV("Mono DAC Playback Volume", RT5645_DAC2_DIG_VOL,
-		RT5645_L_VOL_SFT, RT5645_R_VOL_SFT, 175, 0, dac_vol_tlv),
+		RT5645_L_VOL_SFT + 1, RT5645_R_VOL_SFT + 1, 87, 0, dac_vol_tlv),
 
 	/* IN1/IN2 Control */
 	SOC_SINGLE_TLV("IN1 Boost", RT5645_IN1_CTRL1,
@@ -499,11 +477,11 @@ static const struct snd_kcontrol_new rt5645_snd_controls[] = {
 	SOC_DOUBLE("ADC Capture Switch", RT5645_STO1_ADC_DIG_VOL,
 		RT5645_L_MUTE_SFT, RT5645_R_MUTE_SFT, 1, 1),
 	SOC_DOUBLE_TLV("ADC Capture Volume", RT5645_STO1_ADC_DIG_VOL,
-		RT5645_L_VOL_SFT, RT5645_R_VOL_SFT, 127, 0, adc_vol_tlv),
+		RT5645_L_VOL_SFT + 1, RT5645_R_VOL_SFT + 1, 63, 0, adc_vol_tlv),
 	SOC_DOUBLE("Mono ADC Capture Switch", RT5645_MONO_ADC_DIG_VOL,
 		RT5645_L_MUTE_SFT, RT5645_R_MUTE_SFT, 1, 1),
 	SOC_DOUBLE_TLV("Mono ADC Capture Volume", RT5645_MONO_ADC_DIG_VOL,
-		RT5645_L_VOL_SFT, RT5645_R_VOL_SFT, 127, 0, adc_vol_tlv),
+		RT5645_L_VOL_SFT + 1, RT5645_R_VOL_SFT + 1, 63, 0, adc_vol_tlv),
 
 	/* ADC Boost Volume Control */
 	SOC_DOUBLE_TLV("STO1 ADC Boost Gain", RT5645_ADC_BST_VOL1,
@@ -516,17 +494,6 @@ static const struct snd_kcontrol_new rt5645_snd_controls[] = {
 	/* I2S2 function select */
 	SOC_SINGLE("I2S2 Func Switch", RT5645_GPIO_CTRL1, RT5645_I2S2_SEL_SFT,
 		1, 1),
-
-	/* TDM */
-	SOC_ENUM("TDM Adc Slot0 1 Data", rt5645_tdm_adc_slot0_1_enum),
-	SOC_ENUM("TDM Adc Slot2 3 Data", rt5645_tdm_adc_slot2_3_enum),
-	SOC_ENUM("TDM Adc Slot4 5 Data", rt5645_tdm_adc_slot4_5_enum),
-	SOC_ENUM("TDM Adc Slot6 7 Data", rt5645_tdm_adc_slot6_7_enum),
-	SOC_ENUM("TDM IF1 ADC DATA Sel", rt5645_tdm_adc_sel_enum),
-	SOC_SINGLE("TDM IF1_DAC1_L Sel", RT5645_TDM_CTRL_3, 12, 7, 0),
-	SOC_SINGLE("TDM IF1_DAC1_R Sel", RT5645_TDM_CTRL_3, 8, 7, 0),
-	SOC_SINGLE("TDM IF1_DAC2_L Sel", RT5645_TDM_CTRL_3, 4, 7, 0),
-	SOC_SINGLE("TDM IF1_DAC2_R Sel", RT5645_TDM_CTRL_3, 0, 7, 0),
 };
 
 /**
@@ -1093,7 +1060,8 @@ static const struct snd_kcontrol_new rt5645_mono_adc_r2_mux =
 
 /* MX-77 [9:8] */
 static const char * const rt5645_if1_adc_in_src[] = {
-	"IF_ADC1", "IF_ADC2", "VAD_ADC"
+	"IF_ADC1/IF_ADC2/VAD_ADC", "IF_ADC2/IF_ADC1/VAD_ADC",
+	"VAD_ADC/IF_ADC1/IF_ADC2", "VAD_ADC/IF_ADC2/IF_ADC1"
 };
 
 static SOC_ENUM_SINGLE_DECL(
@@ -1103,6 +1071,140 @@ static SOC_ENUM_SINGLE_DECL(
 static const struct snd_kcontrol_new rt5645_if1_adc_in_mux =
 	SOC_DAPM_ENUM("IF1 ADC IN source", rt5645_if1_adc_in_enum);
 
+/* MX-78 [4:0] */
+static const char * const rt5650_if1_adc_in_src[] = {
+	"IF_ADC1/IF_ADC2/DAC_REF/Null",
+	"IF_ADC1/IF_ADC2/Null/DAC_REF",
+	"IF_ADC1/DAC_REF/IF_ADC2/Null",
+	"IF_ADC1/DAC_REF/Null/IF_ADC2",
+	"IF_ADC1/Null/DAC_REF/IF_ADC2",
+	"IF_ADC1/Null/IF_ADC2/DAC_REF",
+
+	"IF_ADC2/IF_ADC1/DAC_REF/Null",
+	"IF_ADC2/IF_ADC1/Null/DAC_REF",
+	"IF_ADC2/DAC_REF/IF_ADC1/Null",
+	"IF_ADC2/DAC_REF/Null/IF_ADC1",
+	"IF_ADC2/Null/DAC_REF/IF_ADC1",
+	"IF_ADC2/Null/IF_ADC1/DAC_REF",
+
+	"DAC_REF/IF_ADC1/IF_ADC2/Null",
+	"DAC_REF/IF_ADC1/Null/IF_ADC2",
+	"DAC_REF/IF_ADC2/IF_ADC1/Null",
+	"DAC_REF/IF_ADC2/Null/IF_ADC1",
+	"DAC_REF/Null/IF_ADC1/IF_ADC2",
+	"DAC_REF/Null/IF_ADC2/IF_ADC1",
+
+	"Null/IF_ADC1/IF_ADC2/DAC_REF",
+	"Null/IF_ADC1/DAC_REF/IF_ADC2",
+	"Null/IF_ADC2/IF_ADC1/DAC_REF",
+	"Null/IF_ADC2/DAC_REF/IF_ADC1",
+	"Null/DAC_REF/IF_ADC1/IF_ADC2",
+	"Null/DAC_REF/IF_ADC2/IF_ADC1",
+};
+
+static SOC_ENUM_SINGLE_DECL(
+	rt5650_if1_adc_in_enum, RT5645_TDM_CTRL_2,
+	0, rt5650_if1_adc_in_src);
+
+static const struct snd_kcontrol_new rt5650_if1_adc_in_mux =
+	SOC_DAPM_ENUM("IF1 ADC IN source", rt5650_if1_adc_in_enum);
+
+/* MX-78 [15:14][13:12][11:10] */
+static const char * const rt5645_tdm_adc_swap_select[] = {
+	"L/R", "R/L", "L/L", "R/R"
+};
+
+static SOC_ENUM_SINGLE_DECL(rt5650_tdm_adc_slot0_1_enum,
+	RT5645_TDM_CTRL_2, 14, rt5645_tdm_adc_swap_select);
+
+static const struct snd_kcontrol_new rt5650_if1_adc1_in_mux =
+	SOC_DAPM_ENUM("IF1 ADC1 IN source", rt5650_tdm_adc_slot0_1_enum);
+
+static SOC_ENUM_SINGLE_DECL(rt5650_tdm_adc_slot2_3_enum,
+	RT5645_TDM_CTRL_2, 12, rt5645_tdm_adc_swap_select);
+
+static const struct snd_kcontrol_new rt5650_if1_adc2_in_mux =
+	SOC_DAPM_ENUM("IF1 ADC2 IN source", rt5650_tdm_adc_slot2_3_enum);
+
+static SOC_ENUM_SINGLE_DECL(rt5650_tdm_adc_slot4_5_enum,
+	RT5645_TDM_CTRL_2, 10, rt5645_tdm_adc_swap_select);
+
+static const struct snd_kcontrol_new rt5650_if1_adc3_in_mux =
+	SOC_DAPM_ENUM("IF1 ADC3 IN source", rt5650_tdm_adc_slot4_5_enum);
+
+/* MX-77 [7:6][5:4][3:2] */
+static SOC_ENUM_SINGLE_DECL(rt5645_tdm_adc_slot0_1_enum,
+	RT5645_TDM_CTRL_1, 6, rt5645_tdm_adc_swap_select);
+
+static const struct snd_kcontrol_new rt5645_if1_adc1_in_mux =
+	SOC_DAPM_ENUM("IF1 ADC1 IN source", rt5645_tdm_adc_slot0_1_enum);
+
+static SOC_ENUM_SINGLE_DECL(rt5645_tdm_adc_slot2_3_enum,
+	RT5645_TDM_CTRL_1, 4, rt5645_tdm_adc_swap_select);
+
+static const struct snd_kcontrol_new rt5645_if1_adc2_in_mux =
+	SOC_DAPM_ENUM("IF1 ADC2 IN source", rt5645_tdm_adc_slot2_3_enum);
+
+static SOC_ENUM_SINGLE_DECL(rt5645_tdm_adc_slot4_5_enum,
+	RT5645_TDM_CTRL_1, 2, rt5645_tdm_adc_swap_select);
+
+static const struct snd_kcontrol_new rt5645_if1_adc3_in_mux =
+	SOC_DAPM_ENUM("IF1 ADC3 IN source", rt5645_tdm_adc_slot4_5_enum);
+
+/* MX-79 [14:12][10:8][6:4][2:0] */
+static const char * const rt5645_tdm_dac_swap_select[] = {
+	"Slot0", "Slot1", "Slot2", "Slot3"
+};
+
+static SOC_ENUM_SINGLE_DECL(rt5645_tdm_dac0_enum,
+	RT5645_TDM_CTRL_3, 12, rt5645_tdm_dac_swap_select);
+
+static const struct snd_kcontrol_new rt5645_if1_dac0_tdm_sel_mux =
+	SOC_DAPM_ENUM("IF1 DAC0 source", rt5645_tdm_dac0_enum);
+
+static SOC_ENUM_SINGLE_DECL(rt5645_tdm_dac1_enum,
+	RT5645_TDM_CTRL_3, 8, rt5645_tdm_dac_swap_select);
+
+static const struct snd_kcontrol_new rt5645_if1_dac1_tdm_sel_mux =
+	SOC_DAPM_ENUM("IF1 DAC1 source", rt5645_tdm_dac1_enum);
+
+static SOC_ENUM_SINGLE_DECL(rt5645_tdm_dac2_enum,
+	RT5645_TDM_CTRL_3, 4, rt5645_tdm_dac_swap_select);
+
+static const struct snd_kcontrol_new rt5645_if1_dac2_tdm_sel_mux =
+	SOC_DAPM_ENUM("IF1 DAC2 source", rt5645_tdm_dac2_enum);
+
+static SOC_ENUM_SINGLE_DECL(rt5645_tdm_dac3_enum,
+	RT5645_TDM_CTRL_3, 0, rt5645_tdm_dac_swap_select);
+
+static const struct snd_kcontrol_new rt5645_if1_dac3_tdm_sel_mux =
+	SOC_DAPM_ENUM("IF1 DAC3 source", rt5645_tdm_dac3_enum);
+
+/* MX-7a [14:12][10:8][6:4][2:0] */
+static SOC_ENUM_SINGLE_DECL(rt5650_tdm_dac0_enum,
+	RT5650_TDM_CTRL_4, 12, rt5645_tdm_dac_swap_select);
+
+static const struct snd_kcontrol_new rt5650_if1_dac0_tdm_sel_mux =
+	SOC_DAPM_ENUM("IF1 DAC0 source", rt5650_tdm_dac0_enum);
+
+static SOC_ENUM_SINGLE_DECL(rt5650_tdm_dac1_enum,
+	RT5650_TDM_CTRL_4, 8, rt5645_tdm_dac_swap_select);
+
+static const struct snd_kcontrol_new rt5650_if1_dac1_tdm_sel_mux =
+	SOC_DAPM_ENUM("IF1 DAC1 source", rt5650_tdm_dac1_enum);
+
+static SOC_ENUM_SINGLE_DECL(rt5650_tdm_dac2_enum,
+	RT5650_TDM_CTRL_4, 4, rt5645_tdm_dac_swap_select);
+
+static const struct snd_kcontrol_new rt5650_if1_dac2_tdm_sel_mux =
+	SOC_DAPM_ENUM("IF1 DAC2 source", rt5650_tdm_dac2_enum);
+
+static SOC_ENUM_SINGLE_DECL(rt5650_tdm_dac3_enum,
+	RT5650_TDM_CTRL_4, 0, rt5645_tdm_dac_swap_select);
+
+static const struct snd_kcontrol_new rt5650_if1_dac3_tdm_sel_mux =
+	SOC_DAPM_ENUM("IF1 DAC3 source", rt5650_tdm_dac3_enum);
+
 /* MX-2d [3] [2] */
 static const char * const rt5650_a_dac1_src[] = {
 	"DAC1", "Stereo DAC Mixer"
@@ -1227,52 +1329,79 @@ static void hp_amp_power(struct snd_soc_codec *codec, int on)
 
 	if (on) {
 		if (hp_amp_power_count <= 0) {
-			/* depop parameters */
-			snd_soc_update_bits(codec, RT5645_DEPOP_M2,
-				RT5645_DEPOP_MASK, RT5645_DEPOP_MAN);
-			snd_soc_write(codec, RT5645_DEPOP_M1, 0x000d);
-			regmap_write(rt5645->regmap, RT5645_PR_BASE +
-				RT5645_HP_DCC_INT1, 0x9f01);
-			mdelay(150);
-			/* headphone amp power on */
-			snd_soc_update_bits(codec, RT5645_PWR_ANLG1,
-				RT5645_PWR_FV1 | RT5645_PWR_FV2 , 0);
-			snd_soc_update_bits(codec, RT5645_PWR_VOL,
-				RT5645_PWR_HV_L | RT5645_PWR_HV_R,
-				RT5645_PWR_HV_L | RT5645_PWR_HV_R);
-			snd_soc_update_bits(codec, RT5645_PWR_ANLG1,
-				RT5645_PWR_HP_L | RT5645_PWR_HP_R |
-				RT5645_PWR_HA,
-				RT5645_PWR_HP_L | RT5645_PWR_HP_R |
-				RT5645_PWR_HA);
-			mdelay(5);
-			snd_soc_update_bits(codec, RT5645_PWR_ANLG1,
-				RT5645_PWR_FV1 | RT5645_PWR_FV2,
-				RT5645_PWR_FV1 | RT5645_PWR_FV2);
-
-			snd_soc_update_bits(codec, RT5645_DEPOP_M1,
-				RT5645_HP_CO_MASK | RT5645_HP_SG_MASK,
-				RT5645_HP_CO_EN | RT5645_HP_SG_EN);
-			regmap_write(rt5645->regmap, RT5645_PR_BASE +
-				0x14, 0x1aaa);
-			regmap_write(rt5645->regmap, RT5645_PR_BASE +
-				0x24, 0x0430);
+			if (rt5645->codec_type == CODEC_TYPE_RT5650) {
+				snd_soc_write(codec, RT5645_CHARGE_PUMP,
+					0x0e06);
+				snd_soc_write(codec, RT5645_DEPOP_M1, 0x001d);
+				regmap_write(rt5645->regmap, RT5645_PR_BASE +
+					0x3e, 0x7400);
+				snd_soc_write(codec, RT5645_DEPOP_M3, 0x0737);
+				regmap_write(rt5645->regmap, RT5645_PR_BASE +
+					RT5645_MAMP_INT_REG2, 0xfc00);
+				snd_soc_write(codec, RT5645_DEPOP_M2, 0x1140);
+			} else {
+				/* depop parameters */
+				snd_soc_update_bits(codec, RT5645_DEPOP_M2,
+					RT5645_DEPOP_MASK, RT5645_DEPOP_MAN);
+				snd_soc_write(codec, RT5645_DEPOP_M1, 0x000d);
+				regmap_write(rt5645->regmap, RT5645_PR_BASE +
+					RT5645_HP_DCC_INT1, 0x9f01);
+				mdelay(150);
+				/* headphone amp power on */
+				snd_soc_update_bits(codec, RT5645_PWR_ANLG1,
+					RT5645_PWR_FV1 | RT5645_PWR_FV2, 0);
+				snd_soc_update_bits(codec, RT5645_PWR_VOL,
+					RT5645_PWR_HV_L | RT5645_PWR_HV_R,
+					RT5645_PWR_HV_L | RT5645_PWR_HV_R);
+				snd_soc_update_bits(codec, RT5645_PWR_ANLG1,
+					RT5645_PWR_HP_L | RT5645_PWR_HP_R |
+					RT5645_PWR_HA,
+					RT5645_PWR_HP_L | RT5645_PWR_HP_R |
+					RT5645_PWR_HA);
+				mdelay(5);
+				snd_soc_update_bits(codec, RT5645_PWR_ANLG1,
+					RT5645_PWR_FV1 | RT5645_PWR_FV2,
+					RT5645_PWR_FV1 | RT5645_PWR_FV2);
+
+				snd_soc_update_bits(codec, RT5645_DEPOP_M1,
+					RT5645_HP_CO_MASK | RT5645_HP_SG_MASK,
+					RT5645_HP_CO_EN | RT5645_HP_SG_EN);
+				regmap_write(rt5645->regmap, RT5645_PR_BASE +
+					0x14, 0x1aaa);
+				regmap_write(rt5645->regmap, RT5645_PR_BASE +
+					0x24, 0x0430);
+			}
 		}
 		hp_amp_power_count++;
 	} else {
 		hp_amp_power_count--;
 		if (hp_amp_power_count <= 0) {
-			snd_soc_update_bits(codec, RT5645_DEPOP_M1,
-				RT5645_HP_SG_MASK | RT5645_HP_L_SMT_MASK |
-				RT5645_HP_R_SMT_MASK, RT5645_HP_SG_DIS |
-				RT5645_HP_L_SMT_DIS | RT5645_HP_R_SMT_DIS);
-			/* headphone amp power down */
-			snd_soc_write(codec, RT5645_DEPOP_M1, 0x0000);
-			snd_soc_update_bits(codec, RT5645_PWR_ANLG1,
-				RT5645_PWR_HP_L | RT5645_PWR_HP_R |
-				RT5645_PWR_HA, 0);
-			snd_soc_update_bits(codec, RT5645_DEPOP_M2,
-				RT5645_DEPOP_MASK, 0);
+			if (rt5645->codec_type == CODEC_TYPE_RT5650) {
+				regmap_write(rt5645->regmap, RT5645_PR_BASE +
+					0x3e, 0x7400);
+				snd_soc_write(codec, RT5645_DEPOP_M3, 0x0737);
+				regmap_write(rt5645->regmap, RT5645_PR_BASE +
+					RT5645_MAMP_INT_REG2, 0xfc00);
+				snd_soc_write(codec, RT5645_DEPOP_M2, 0x1140);
+				msleep(100);
+				snd_soc_write(codec, RT5645_DEPOP_M1, 0x0001);
+
+			} else {
+				snd_soc_update_bits(codec, RT5645_DEPOP_M1,
+					RT5645_HP_SG_MASK |
+					RT5645_HP_L_SMT_MASK |
+					RT5645_HP_R_SMT_MASK,
+					RT5645_HP_SG_DIS |
+					RT5645_HP_L_SMT_DIS |
+					RT5645_HP_R_SMT_DIS);
+				/* headphone amp power down */
+				snd_soc_write(codec, RT5645_DEPOP_M1, 0x0000);
+				snd_soc_update_bits(codec, RT5645_PWR_ANLG1,
+					RT5645_PWR_HP_L | RT5645_PWR_HP_R |
+					RT5645_PWR_HA, 0);
+				snd_soc_update_bits(codec, RT5645_DEPOP_M2,
+					RT5645_DEPOP_MASK, 0);
+			}
 		}
 	}
 }
@@ -1287,56 +1416,52 @@ static int rt5645_hp_event(struct snd_soc_dapm_widget *w,
 	case SND_SOC_DAPM_POST_PMU:
 		hp_amp_power(codec, 1);
 		/* headphone unmute sequence */
-		if (rt5645->codec_type == CODEC_TYPE_RT5650) {
-			snd_soc_write(codec, RT5645_DEPOP_M3, 0x0737);
-		} else {
+		if (rt5645->codec_type == CODEC_TYPE_RT5645) {
 			snd_soc_update_bits(codec, RT5645_DEPOP_M3,
 				RT5645_CP_FQ1_MASK | RT5645_CP_FQ2_MASK |
 				RT5645_CP_FQ3_MASK,
 				(RT5645_CP_FQ_192_KHZ << RT5645_CP_FQ1_SFT) |
 				(RT5645_CP_FQ_12_KHZ << RT5645_CP_FQ2_SFT) |
 				(RT5645_CP_FQ_192_KHZ << RT5645_CP_FQ3_SFT));
+			regmap_write(rt5645->regmap, RT5645_PR_BASE +
+				RT5645_MAMP_INT_REG2, 0xfc00);
+			snd_soc_update_bits(codec, RT5645_DEPOP_M1,
+				RT5645_SMT_TRIG_MASK, RT5645_SMT_TRIG_EN);
+			snd_soc_update_bits(codec, RT5645_DEPOP_M1,
+				RT5645_RSTN_MASK, RT5645_RSTN_EN);
+			snd_soc_update_bits(codec, RT5645_DEPOP_M1,
+				RT5645_RSTN_MASK | RT5645_HP_L_SMT_MASK |
+				RT5645_HP_R_SMT_MASK, RT5645_RSTN_DIS |
+				RT5645_HP_L_SMT_EN | RT5645_HP_R_SMT_EN);
+			msleep(40);
+			snd_soc_update_bits(codec, RT5645_DEPOP_M1,
+				RT5645_HP_SG_MASK | RT5645_HP_L_SMT_MASK |
+				RT5645_HP_R_SMT_MASK, RT5645_HP_SG_DIS |
+				RT5645_HP_L_SMT_DIS | RT5645_HP_R_SMT_DIS);
 		}
-		regmap_write(rt5645->regmap,
-			RT5645_PR_BASE + RT5645_MAMP_INT_REG2, 0xfc00);
-		snd_soc_update_bits(codec, RT5645_DEPOP_M1,
-			RT5645_SMT_TRIG_MASK, RT5645_SMT_TRIG_EN);
-		snd_soc_update_bits(codec, RT5645_DEPOP_M1,
-			RT5645_RSTN_MASK, RT5645_RSTN_EN);
-		snd_soc_update_bits(codec, RT5645_DEPOP_M1,
-			RT5645_RSTN_MASK | RT5645_HP_L_SMT_MASK |
-			RT5645_HP_R_SMT_MASK, RT5645_RSTN_DIS |
-			RT5645_HP_L_SMT_EN | RT5645_HP_R_SMT_EN);
-		msleep(40);
-		snd_soc_update_bits(codec, RT5645_DEPOP_M1,
-			RT5645_HP_SG_MASK | RT5645_HP_L_SMT_MASK |
-			RT5645_HP_R_SMT_MASK, RT5645_HP_SG_DIS |
-			RT5645_HP_L_SMT_DIS | RT5645_HP_R_SMT_DIS);
 		break;
 
 	case SND_SOC_DAPM_PRE_PMD:
 		/* headphone mute sequence */
-		if (rt5645->codec_type == CODEC_TYPE_RT5650) {
-			snd_soc_write(codec, RT5645_DEPOP_M3, 0x0737);
-		} else {
+		if (rt5645->codec_type == CODEC_TYPE_RT5645) {
 			snd_soc_update_bits(codec, RT5645_DEPOP_M3,
 				RT5645_CP_FQ1_MASK | RT5645_CP_FQ2_MASK |
 				RT5645_CP_FQ3_MASK,
 				(RT5645_CP_FQ_96_KHZ << RT5645_CP_FQ1_SFT) |
 				(RT5645_CP_FQ_12_KHZ << RT5645_CP_FQ2_SFT) |
 				(RT5645_CP_FQ_96_KHZ << RT5645_CP_FQ3_SFT));
+			regmap_write(rt5645->regmap, RT5645_PR_BASE +
+				RT5645_MAMP_INT_REG2, 0xfc00);
+			snd_soc_update_bits(codec, RT5645_DEPOP_M1,
+				RT5645_HP_SG_MASK, RT5645_HP_SG_EN);
+			snd_soc_update_bits(codec, RT5645_DEPOP_M1,
+				RT5645_RSTP_MASK, RT5645_RSTP_EN);
+			snd_soc_update_bits(codec, RT5645_DEPOP_M1,
+				RT5645_RSTP_MASK | RT5645_HP_L_SMT_MASK |
+				RT5645_HP_R_SMT_MASK, RT5645_RSTP_DIS |
+				RT5645_HP_L_SMT_EN | RT5645_HP_R_SMT_EN);
+			msleep(30);
 		}
-		regmap_write(rt5645->regmap,
-			RT5645_PR_BASE + RT5645_MAMP_INT_REG2, 0xfc00);
-		snd_soc_update_bits(codec, RT5645_DEPOP_M1,
-			RT5645_HP_SG_MASK, RT5645_HP_SG_EN);
-		snd_soc_update_bits(codec, RT5645_DEPOP_M1,
-			RT5645_RSTP_MASK, RT5645_RSTP_EN);
-		snd_soc_update_bits(codec, RT5645_DEPOP_M1,
-			RT5645_RSTP_MASK | RT5645_HP_L_SMT_MASK |
-			RT5645_HP_R_SMT_MASK, RT5645_RSTP_DIS |
-			RT5645_HP_L_SMT_EN | RT5645_HP_R_SMT_EN);
-		msleep(30);
 		hp_amp_power(codec, 0);
 		break;
 
@@ -1571,20 +1696,50 @@ static const struct snd_soc_dapm_widget rt5645_dapm_widgets[] = {
 	SND_SOC_DAPM_PGA("IF1_ADC4", SND_SOC_NOPM, 0, 0, NULL, 0),
 
 	/* IF1 2 Mux */
-	SND_SOC_DAPM_MUX("IF1 ADC Mux", SND_SOC_NOPM,
+	SND_SOC_DAPM_MUX("RT5645 IF1 ADC1 Swap Mux", SND_SOC_NOPM,
+		0, 0, &rt5645_if1_adc1_in_mux),
+	SND_SOC_DAPM_MUX("RT5645 IF1 ADC2 Swap Mux", SND_SOC_NOPM,
+		0, 0, &rt5645_if1_adc2_in_mux),
+	SND_SOC_DAPM_MUX("RT5645 IF1 ADC3 Swap Mux", SND_SOC_NOPM,
+		0, 0, &rt5645_if1_adc3_in_mux),
+	SND_SOC_DAPM_MUX("RT5645 IF1 ADC Mux", SND_SOC_NOPM,
 		0, 0, &rt5645_if1_adc_in_mux),
+
+	SND_SOC_DAPM_MUX("RT5650 IF1 ADC1 Swap Mux", SND_SOC_NOPM,
+		0, 0, &rt5650_if1_adc1_in_mux),
+	SND_SOC_DAPM_MUX("RT5650 IF1 ADC2 Swap Mux", SND_SOC_NOPM,
+		0, 0, &rt5650_if1_adc2_in_mux),
+	SND_SOC_DAPM_MUX("RT5650 IF1 ADC3 Swap Mux", SND_SOC_NOPM,
+		0, 0, &rt5650_if1_adc3_in_mux),
+	SND_SOC_DAPM_MUX("RT5650 IF1 ADC Mux", SND_SOC_NOPM,
+		0, 0, &rt5650_if1_adc_in_mux),
+
 	SND_SOC_DAPM_MUX("IF2 ADC Mux", SND_SOC_NOPM,
 		0, 0, &rt5645_if2_adc_in_mux),
 
 	/* Digital Interface */
 	SND_SOC_DAPM_SUPPLY("I2S1", RT5645_PWR_DIG1,
 		RT5645_PWR_I2S1_BIT, 0, NULL, 0),
+	SND_SOC_DAPM_PGA("IF1 DAC0", SND_SOC_NOPM, 0, 0, NULL, 0),
 	SND_SOC_DAPM_PGA("IF1 DAC1", SND_SOC_NOPM, 0, 0, NULL, 0),
 	SND_SOC_DAPM_PGA("IF1 DAC2", SND_SOC_NOPM, 0, 0, NULL, 0),
-	SND_SOC_DAPM_PGA("IF1 DAC1 L", SND_SOC_NOPM, 0, 0, NULL, 0),
-	SND_SOC_DAPM_PGA("IF1 DAC1 R", SND_SOC_NOPM, 0, 0, NULL, 0),
-	SND_SOC_DAPM_PGA("IF1 DAC2 L", SND_SOC_NOPM, 0, 0, NULL, 0),
-	SND_SOC_DAPM_PGA("IF1 DAC2 R", SND_SOC_NOPM, 0, 0, NULL, 0),
+	SND_SOC_DAPM_PGA("IF1 DAC3", SND_SOC_NOPM, 0, 0, NULL, 0),
+	SND_SOC_DAPM_MUX("RT5645 IF1 DAC1 L Mux", SND_SOC_NOPM, 0, 0,
+		&rt5645_if1_dac0_tdm_sel_mux),
+	SND_SOC_DAPM_MUX("RT5645 IF1 DAC1 R Mux", SND_SOC_NOPM, 0, 0,
+		&rt5645_if1_dac1_tdm_sel_mux),
+	SND_SOC_DAPM_MUX("RT5645 IF1 DAC2 L Mux", SND_SOC_NOPM, 0, 0,
+		&rt5645_if1_dac2_tdm_sel_mux),
+	SND_SOC_DAPM_MUX("RT5645 IF1 DAC2 R Mux", SND_SOC_NOPM, 0, 0,
+		&rt5645_if1_dac3_tdm_sel_mux),
+	SND_SOC_DAPM_MUX("RT5650 IF1 DAC1 L Mux", SND_SOC_NOPM, 0, 0,
+		&rt5650_if1_dac0_tdm_sel_mux),
+	SND_SOC_DAPM_MUX("RT5650 IF1 DAC1 R Mux", SND_SOC_NOPM, 0, 0,
+		&rt5650_if1_dac1_tdm_sel_mux),
+	SND_SOC_DAPM_MUX("RT5650 IF1 DAC2 L Mux", SND_SOC_NOPM, 0, 0,
+		&rt5650_if1_dac2_tdm_sel_mux),
+	SND_SOC_DAPM_MUX("RT5650 IF1 DAC2 R Mux", SND_SOC_NOPM, 0, 0,
+		&rt5650_if1_dac3_tdm_sel_mux),
 	SND_SOC_DAPM_PGA("IF1 ADC", SND_SOC_NOPM, 0, 0, NULL, 0),
 	SND_SOC_DAPM_PGA("IF1 ADC L", SND_SOC_NOPM, 0, 0, NULL, 0),
 	SND_SOC_DAPM_PGA("IF1 ADC R", SND_SOC_NOPM, 0, 0, NULL, 0),
@@ -1848,42 +2003,32 @@ static const struct snd_soc_dapm_route rt5645_dapm_routes[] = {
 	{ "IF_ADC2", NULL, "Mono ADC MIXR" },
 	{ "VAD_ADC", NULL, "VAD ADC Mux" },
 
-	{ "IF1 ADC Mux", "IF_ADC1", "IF_ADC1" },
-	{ "IF1 ADC Mux", "IF_ADC2", "IF_ADC2" },
-	{ "IF1 ADC Mux", "VAD_ADC", "VAD_ADC" },
-
 	{ "IF2 ADC Mux", "IF_ADC1", "IF_ADC1" },
 	{ "IF2 ADC Mux", "IF_ADC2", "IF_ADC2" },
 	{ "IF2 ADC Mux", "VAD_ADC", "VAD_ADC" },
 
 	{ "IF1 ADC", NULL, "I2S1" },
-	{ "IF1 ADC", NULL, "IF1 ADC Mux" },
 	{ "IF2 ADC", NULL, "I2S2" },
 	{ "IF2 ADC", NULL, "IF2 ADC Mux" },
 
-	{ "AIF1TX", NULL, "IF1 ADC" },
-	{ "AIF1TX", NULL, "IF2 ADC" },
 	{ "AIF2TX", NULL, "IF2 ADC" },
 
+	{ "IF1 DAC0", NULL, "AIF1RX" },
 	{ "IF1 DAC1", NULL, "AIF1RX" },
 	{ "IF1 DAC2", NULL, "AIF1RX" },
+	{ "IF1 DAC3", NULL, "AIF1RX" },
 	{ "IF2 DAC", NULL, "AIF2RX" },
 
+	{ "IF1 DAC0", NULL, "I2S1" },
 	{ "IF1 DAC1", NULL, "I2S1" },
 	{ "IF1 DAC2", NULL, "I2S1" },
+	{ "IF1 DAC3", NULL, "I2S1" },
 	{ "IF2 DAC", NULL, "I2S2" },
 
-	{ "IF1 DAC2 L", NULL, "IF1 DAC2" },
-	{ "IF1 DAC2 R", NULL, "IF1 DAC2" },
-	{ "IF1 DAC1 L", NULL, "IF1 DAC1" },
-	{ "IF1 DAC1 R", NULL, "IF1 DAC1" },
 	{ "IF2 DAC L", NULL, "IF2 DAC" },
 	{ "IF2 DAC R", NULL, "IF2 DAC" },
 
-	{ "DAC1 L Mux", "IF1 DAC", "IF1 DAC1 L" },
 	{ "DAC1 L Mux", "IF2 DAC", "IF2 DAC L" },
-
-	{ "DAC1 R Mux", "IF1 DAC", "IF1 DAC1 R" },
 	{ "DAC1 R Mux", "IF2 DAC", "IF2 DAC R" },
 
 	{ "DAC1 MIXL", "Stereo ADC Switch", "Stereo1 ADC MIXL" },
@@ -1893,14 +2038,12 @@ static const struct snd_soc_dapm_route rt5645_dapm_routes[] = {
 	{ "DAC1 MIXR", "DAC1 Switch", "DAC1 R Mux" },
 	{ "DAC1 MIXR", NULL, "dac stereo1 filter" },
 
-	{ "DAC L2 Mux", "IF1 DAC", "IF1 DAC2 L" },
 	{ "DAC L2 Mux", "IF2 DAC", "IF2 DAC L" },
 	{ "DAC L2 Mux", "Mono ADC", "Mono ADC MIXL" },
 	{ "DAC L2 Mux", "VAD_ADC", "VAD_ADC" },
 	{ "DAC L2 Volume", NULL, "DAC L2 Mux" },
 	{ "DAC L2 Volume", NULL, "dac mono left filter" },
 
-	{ "DAC R2 Mux", "IF1 DAC", "IF1 DAC2 R" },
 	{ "DAC R2 Mux", "IF2 DAC", "IF2 DAC R" },
 	{ "DAC R2 Mux", "Mono ADC", "Mono ADC MIXR" },
 	{ "DAC R2 Mux", "Haptic", "Haptic Generator" },
@@ -2038,6 +2181,80 @@ static const struct snd_soc_dapm_route rt5650_specific_dapm_routes[] = {
 	{ "DAC R1", NULL, "A DAC1 R Mux" },
 	{ "DAC L2", NULL, "A DAC2 L Mux" },
 	{ "DAC R2", NULL, "A DAC2 R Mux" },
+
+	{ "RT5650 IF1 ADC1 Swap Mux", "L/R", "IF_ADC1" },
+	{ "RT5650 IF1 ADC1 Swap Mux", "R/L", "IF_ADC1" },
+	{ "RT5650 IF1 ADC1 Swap Mux", "L/L", "IF_ADC1" },
+	{ "RT5650 IF1 ADC1 Swap Mux", "R/R", "IF_ADC1" },
+
+	{ "RT5650 IF1 ADC2 Swap Mux", "L/R", "IF_ADC2" },
+	{ "RT5650 IF1 ADC2 Swap Mux", "R/L", "IF_ADC2" },
+	{ "RT5650 IF1 ADC2 Swap Mux", "L/L", "IF_ADC2" },
+	{ "RT5650 IF1 ADC2 Swap Mux", "R/R", "IF_ADC2" },
+
+	{ "RT5650 IF1 ADC3 Swap Mux", "L/R", "VAD_ADC" },
+	{ "RT5650 IF1 ADC3 Swap Mux", "R/L", "VAD_ADC" },
+	{ "RT5650 IF1 ADC3 Swap Mux", "L/L", "VAD_ADC" },
+	{ "RT5650 IF1 ADC3 Swap Mux", "R/R", "VAD_ADC" },
+
+	{ "IF1 ADC", NULL, "RT5650 IF1 ADC1 Swap Mux" },
+	{ "IF1 ADC", NULL, "RT5650 IF1 ADC2 Swap Mux" },
+	{ "IF1 ADC", NULL, "RT5650 IF1 ADC3 Swap Mux" },
+
+	{ "RT5650 IF1 ADC Mux", "IF_ADC1/IF_ADC2/DAC_REF/Null", "IF1 ADC" },
+	{ "RT5650 IF1 ADC Mux", "IF_ADC1/IF_ADC2/Null/DAC_REF", "IF1 ADC" },
+	{ "RT5650 IF1 ADC Mux", "IF_ADC1/DAC_REF/IF_ADC2/Null", "IF1 ADC" },
+	{ "RT5650 IF1 ADC Mux", "IF_ADC1/DAC_REF/Null/IF_ADC2", "IF1 ADC" },
+	{ "RT5650 IF1 ADC Mux", "IF_ADC1/Null/DAC_REF/IF_ADC2", "IF1 ADC" },
+	{ "RT5650 IF1 ADC Mux", "IF_ADC1/Null/IF_ADC2/DAC_REF", "IF1 ADC" },
+
+	{ "RT5650 IF1 ADC Mux", "IF_ADC2/IF_ADC1/DAC_REF/Null", "IF1 ADC" },
+	{ "RT5650 IF1 ADC Mux", "IF_ADC2/IF_ADC1/Null/DAC_REF", "IF1 ADC" },
+	{ "RT5650 IF1 ADC Mux", "IF_ADC2/DAC_REF/IF_ADC1/Null", "IF1 ADC" },
+	{ "RT5650 IF1 ADC Mux", "IF_ADC2/DAC_REF/Null/IF_ADC1", "IF1 ADC" },
+	{ "RT5650 IF1 ADC Mux", "IF_ADC2/Null/DAC_REF/IF_ADC1", "IF1 ADC" },
+	{ "RT5650 IF1 ADC Mux", "IF_ADC2/Null/IF_ADC1/DAC_REF", "IF1 ADC" },
+
+	{ "RT5650 IF1 ADC Mux", "DAC_REF/IF_ADC1/IF_ADC2/Null", "IF1 ADC" },
+	{ "RT5650 IF1 ADC Mux", "DAC_REF/IF_ADC1/Null/IF_ADC2", "IF1 ADC" },
+	{ "RT5650 IF1 ADC Mux", "DAC_REF/IF_ADC2/IF_ADC1/Null", "IF1 ADC" },
+	{ "RT5650 IF1 ADC Mux", "DAC_REF/IF_ADC2/Null/IF_ADC1", "IF1 ADC" },
+	{ "RT5650 IF1 ADC Mux", "DAC_REF/Null/IF_ADC1/IF_ADC2", "IF1 ADC" },
+	{ "RT5650 IF1 ADC Mux", "DAC_REF/Null/IF_ADC2/IF_ADC1", "IF1 ADC" },
+
+	{ "RT5650 IF1 ADC Mux", "Null/IF_ADC1/IF_ADC2/DAC_REF", "IF1 ADC" },
+	{ "RT5650 IF1 ADC Mux", "Null/IF_ADC1/DAC_REF/IF_ADC2", "IF1 ADC" },
+	{ "RT5650 IF1 ADC Mux", "Null/IF_ADC2/IF_ADC1/DAC_REF", "IF1 ADC" },
+	{ "RT5650 IF1 ADC Mux", "Null/IF_ADC2/DAC_REF/IF_ADC1", "IF1 ADC" },
+	{ "RT5650 IF1 ADC Mux", "Null/DAC_REF/IF_ADC1/IF_ADC2", "IF1 ADC" },
+	{ "RT5650 IF1 ADC Mux", "Null/DAC_REF/IF_ADC2/IF_ADC1", "IF1 ADC" },
+	{ "AIF1TX", NULL, "RT5650 IF1 ADC Mux" },
+
+	{ "RT5650 IF1 DAC1 L Mux", "Slot0", "IF1 DAC0" },
+	{ "RT5650 IF1 DAC1 L Mux", "Slot1", "IF1 DAC1" },
+	{ "RT5650 IF1 DAC1 L Mux", "Slot2", "IF1 DAC2" },
+	{ "RT5650 IF1 DAC1 L Mux", "Slot3", "IF1 DAC3" },
+
+	{ "RT5650 IF1 DAC1 R Mux", "Slot0", "IF1 DAC0" },
+	{ "RT5650 IF1 DAC1 R Mux", "Slot1", "IF1 DAC1" },
+	{ "RT5650 IF1 DAC1 R Mux", "Slot2", "IF1 DAC2" },
+	{ "RT5650 IF1 DAC1 R Mux", "Slot3", "IF1 DAC3" },
+
+	{ "RT5650 IF1 DAC2 L Mux", "Slot0", "IF1 DAC0" },
+	{ "RT5650 IF1 DAC2 L Mux", "Slot1", "IF1 DAC1" },
+	{ "RT5650 IF1 DAC2 L Mux", "Slot2", "IF1 DAC2" },
+	{ "RT5650 IF1 DAC2 L Mux", "Slot3", "IF1 DAC3" },
+
+	{ "RT5650 IF1 DAC2 R Mux", "Slot0", "IF1 DAC0" },
+	{ "RT5650 IF1 DAC2 R Mux", "Slot1", "IF1 DAC1" },
+	{ "RT5650 IF1 DAC2 R Mux", "Slot2", "IF1 DAC2" },
+	{ "RT5650 IF1 DAC2 R Mux", "Slot3", "IF1 DAC3" },
+
+	{ "DAC1 L Mux", "IF1 DAC", "RT5650 IF1 DAC1 L Mux" },
+	{ "DAC1 R Mux", "IF1 DAC", "RT5650 IF1 DAC1 R Mux" },
+
+	{ "DAC L2 Mux", "IF1 DAC", "RT5650 IF1 DAC2 L Mux" },
+	{ "DAC R2 Mux", "IF1 DAC", "RT5650 IF1 DAC2 R Mux" },
 };
 
 static const struct snd_soc_dapm_route rt5645_specific_dapm_routes[] = {
@@ -2045,6 +2262,57 @@ static const struct snd_soc_dapm_route rt5645_specific_dapm_routes[] = {
 	{ "DAC R1", NULL, "Stereo DAC MIXR" },
 	{ "DAC L2", NULL, "Mono DAC MIXL" },
 	{ "DAC R2", NULL, "Mono DAC MIXR" },
+
+	{ "RT5645 IF1 ADC1 Swap Mux", "L/R", "IF_ADC1" },
+	{ "RT5645 IF1 ADC1 Swap Mux", "R/L", "IF_ADC1" },
+	{ "RT5645 IF1 ADC1 Swap Mux", "L/L", "IF_ADC1" },
+	{ "RT5645 IF1 ADC1 Swap Mux", "R/R", "IF_ADC1" },
+
+	{ "RT5645 IF1 ADC2 Swap Mux", "L/R", "IF_ADC2" },
+	{ "RT5645 IF1 ADC2 Swap Mux", "R/L", "IF_ADC2" },
+	{ "RT5645 IF1 ADC2 Swap Mux", "L/L", "IF_ADC2" },
+	{ "RT5645 IF1 ADC2 Swap Mux", "R/R", "IF_ADC2" },
+
+	{ "RT5645 IF1 ADC3 Swap Mux", "L/R", "VAD_ADC" },
+	{ "RT5645 IF1 ADC3 Swap Mux", "R/L", "VAD_ADC" },
+	{ "RT5645 IF1 ADC3 Swap Mux", "L/L", "VAD_ADC" },
+	{ "RT5645 IF1 ADC3 Swap Mux", "R/R", "VAD_ADC" },
+
+	{ "IF1 ADC", NULL, "RT5645 IF1 ADC1 Swap Mux" },
+	{ "IF1 ADC", NULL, "RT5645 IF1 ADC2 Swap Mux" },
+	{ "IF1 ADC", NULL, "RT5645 IF1 ADC3 Swap Mux" },
+
+	{ "RT5645 IF1 ADC Mux", "IF_ADC1/IF_ADC2/VAD_ADC", "IF1 ADC" },
+	{ "RT5645 IF1 ADC Mux", "IF_ADC2/IF_ADC1/VAD_ADC", "IF1 ADC" },
+	{ "RT5645 IF1 ADC Mux", "VAD_ADC/IF_ADC1/IF_ADC2", "IF1 ADC" },
+	{ "RT5645 IF1 ADC Mux", "VAD_ADC/IF_ADC2/IF_ADC1", "IF1 ADC" },
+	{ "AIF1TX", NULL, "RT5645 IF1 ADC Mux" },
+
+	{ "RT5645 IF1 DAC1 L Mux", "Slot0", "IF1 DAC0" },
+	{ "RT5645 IF1 DAC1 L Mux", "Slot1", "IF1 DAC1" },
+	{ "RT5645 IF1 DAC1 L Mux", "Slot2", "IF1 DAC2" },
+	{ "RT5645 IF1 DAC1 L Mux", "Slot3", "IF1 DAC3" },
+
+	{ "RT5645 IF1 DAC1 R Mux", "Slot0", "IF1 DAC0" },
+	{ "RT5645 IF1 DAC1 R Mux", "Slot1", "IF1 DAC1" },
+	{ "RT5645 IF1 DAC1 R Mux", "Slot2", "IF1 DAC2" },
+	{ "RT5645 IF1 DAC1 R Mux", "Slot3", "IF1 DAC3" },
+
+	{ "RT5645 IF1 DAC2 L Mux", "Slot0", "IF1 DAC0" },
+	{ "RT5645 IF1 DAC2 L Mux", "Slot1", "IF1 DAC1" },
+	{ "RT5645 IF1 DAC2 L Mux", "Slot2", "IF1 DAC2" },
+	{ "RT5645 IF1 DAC2 L Mux", "Slot3", "IF1 DAC3" },
+
+	{ "RT5645 IF1 DAC2 R Mux", "Slot0", "IF1 DAC0" },
+	{ "RT5645 IF1 DAC2 R Mux", "Slot1", "IF1 DAC1" },
+	{ "RT5645 IF1 DAC2 R Mux", "Slot2", "IF1 DAC2" },
+	{ "RT5645 IF1 DAC2 R Mux", "Slot3", "IF1 DAC3" },
+
+	{ "DAC1 L Mux", "IF1 DAC", "RT5645 IF1 DAC1 L Mux" },
+	{ "DAC1 R Mux", "IF1 DAC", "RT5645 IF1 DAC1 R Mux" },
+
+	{ "DAC L2 Mux", "IF1 DAC", "RT5645 IF1 DAC2 L Mux" },
+	{ "DAC R2 Mux", "IF1 DAC", "RT5645 IF1 DAC2 R Mux" },
 };
 
 static int rt5645_hw_params(struct snd_pcm_substream *substream,
@@ -2102,9 +2370,8 @@ static int rt5645_hw_params(struct snd_pcm_substream *substream,
 
 	switch (dai->id) {
 	case RT5645_AIF1:
-		mask_clk = RT5645_I2S_BCLK_MS1_MASK | RT5645_I2S_PD1_MASK;
-		val_clk = bclk_ms << RT5645_I2S_BCLK_MS1_SFT |
-			pre_div << RT5645_I2S_PD1_SFT;
+		mask_clk = RT5645_I2S_PD1_MASK;
+		val_clk = pre_div << RT5645_I2S_PD1_SFT;
 		snd_soc_update_bits(codec, RT5645_I2S1_SDP,
 			(0x3 << dl_sft), (val_len << dl_sft));
 		snd_soc_update_bits(codec, RT5645_ADDA_CLK1, mask_clk, val_clk);
@@ -2369,6 +2636,8 @@ static int rt5645_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask,
 static int rt5645_set_bias_level(struct snd_soc_codec *codec,
 			enum snd_soc_bias_level level)
 {
+	struct rt5645_priv *rt5645 = snd_soc_codec_get_drvdata(codec);
+
 	switch (level) {
 	case SND_SOC_BIAS_PREPARE:
 		if (SND_SOC_BIAS_STANDBY == codec->dapm.bias_level) {
@@ -2399,8 +2668,9 @@ static int rt5645_set_bias_level(struct snd_soc_codec *codec,
 
 	case SND_SOC_BIAS_OFF:
 		snd_soc_write(codec, RT5645_DEPOP_M2, 0x1100);
-		snd_soc_update_bits(codec, RT5645_GEN_CTRL1,
-				RT5645_DIG_GATE_CTRL, 0);
+		if (!rt5645->en_button_func)
+			snd_soc_update_bits(codec, RT5645_GEN_CTRL1,
+					RT5645_DIG_GATE_CTRL, 0);
 		snd_soc_update_bits(codec, RT5645_PWR_ANLG1,
 				RT5645_PWR_VREF1 | RT5645_PWR_MB |
 				RT5645_PWR_BG | RT5645_PWR_VREF2 |
@@ -2410,72 +2680,222 @@ static int rt5645_set_bias_level(struct snd_soc_codec *codec,
 	default:
 		break;
 	}
-	codec->dapm.bias_level = level;
 
 	return 0;
 }
 
-static int rt5645_jack_detect(struct snd_soc_codec *codec)
+static int rt5650_calibration(struct rt5645_priv *rt5645)
 {
-	struct rt5645_priv *rt5645 = snd_soc_codec_get_drvdata(codec);
-	int gpio_state, jack_type = 0;
-	unsigned int val;
+	int val, i;
+	int ret = -1;
 
-	if (!gpio_is_valid(rt5645->pdata.hp_det_gpio)) {
-		dev_err(codec->dev, "invalid gpio\n");
-		return -EINVAL;
+	regcache_cache_bypass(rt5645->regmap, true);
+	regmap_write(rt5645->regmap, RT5645_RESET, 0);
+	regmap_write(rt5645->regmap, RT5645_GEN_CTRL3, 0x0800);
+	regmap_write(rt5645->regmap, RT5645_PR_BASE + RT5645_CHOP_DAC_ADC,
+		0x3600);
+	regmap_write(rt5645->regmap, RT5645_PR_BASE + 0x25, 0x7000);
+	regmap_write(rt5645->regmap, RT5645_I2S1_SDP, 0x8008);
+	/* headset type */
+	regmap_write(rt5645->regmap, RT5645_GEN_CTRL1, 0x2061);
+	regmap_write(rt5645->regmap, RT5645_CHARGE_PUMP, 0x0006);
+	regmap_write(rt5645->regmap, RT5645_PWR_ANLG1, 0x2012);
+	regmap_write(rt5645->regmap, RT5645_PWR_MIXER, 0x0002);
+	regmap_write(rt5645->regmap, RT5645_PWR_VOL, 0x0020);
+	regmap_write(rt5645->regmap, RT5645_JD_CTRL3, 0x00f0);
+	regmap_write(rt5645->regmap, RT5645_IN1_CTRL1, 0x0006);
+	regmap_write(rt5645->regmap, RT5645_IN1_CTRL2, 0x1827);
+	regmap_write(rt5645->regmap, RT5645_IN1_CTRL2, 0x0827);
+	msleep(400);
+	/* Inline command */
+	regmap_write(rt5645->regmap, RT5645_DEPOP_M1, 0x0001);
+	regmap_write(rt5645->regmap, RT5650_4BTN_IL_CMD2, 0xc000);
+	regmap_write(rt5645->regmap, RT5650_4BTN_IL_CMD1, 0x0008);
+	/* Calbration */
+	regmap_write(rt5645->regmap, RT5645_GLB_CLK, 0x8000);
+	regmap_write(rt5645->regmap, RT5645_DEPOP_M1, 0x0000);
+	regmap_write(rt5645->regmap, RT5650_4BTN_IL_CMD2, 0xc000);
+	regmap_write(rt5645->regmap, RT5650_4BTN_IL_CMD1, 0x0008);
+	regmap_write(rt5645->regmap, RT5645_PWR_DIG2, 0x8800);
+	regmap_write(rt5645->regmap, RT5645_PWR_ANLG1, 0xe8fa);
+	regmap_write(rt5645->regmap, RT5645_PWR_ANLG2, 0x8c04);
+	regmap_write(rt5645->regmap, RT5645_DEPOP_M2, 0x3100);
+	regmap_write(rt5645->regmap, RT5645_CHARGE_PUMP, 0x0e06);
+	regmap_write(rt5645->regmap, RT5645_BASS_BACK, 0x8a13);
+	regmap_write(rt5645->regmap, RT5645_GEN_CTRL3, 0x0820);
+	regmap_write(rt5645->regmap, RT5645_DEPOP_M1, 0x000d);
+	/* Power on and Calbration */
+	regmap_write(rt5645->regmap, RT5645_PR_BASE + RT5645_HP_DCC_INT1,
+		0x9f01);
+	msleep(200);
+	for (i = 0; i < 5; i++) {
+		regmap_read(rt5645->regmap, RT5645_PR_BASE + 0x7a, &val);
+		if (val != 0 && val != 0x3f3f) {
+			ret = 0;
+			break;
+		}
+		msleep(50);
 	}
-	gpio_state = gpio_get_value(rt5645->pdata.hp_det_gpio);
+	pr_debug("%s: PR-7A = 0x%x\n", __func__, val);
+
+	/* mute */
+	regmap_write(rt5645->regmap, RT5645_PR_BASE + 0x3e, 0x7400);
+	regmap_write(rt5645->regmap, RT5645_DEPOP_M3, 0x0737);
+	regmap_write(rt5645->regmap, RT5645_PR_BASE + RT5645_MAMP_INT_REG2,
+		0xfc00);
+	regmap_write(rt5645->regmap, RT5645_DEPOP_M2, 0x1140);
+	regmap_write(rt5645->regmap, RT5645_DEPOP_M1, 0x0000);
+	regmap_write(rt5645->regmap, RT5645_GEN_CTRL2, 0x4020);
+	regmap_write(rt5645->regmap, RT5645_PWR_ANLG2, 0x0006);
+	regmap_write(rt5645->regmap, RT5645_PWR_DIG2, 0x0000);
+	msleep(350);
+
+	regcache_cache_bypass(rt5645->regmap, false);
+
+	return ret;
+}
 
-	dev_dbg(codec->dev, "gpio = %d(%d)\n", rt5645->pdata.hp_det_gpio,
-		gpio_state);
+static void rt5645_enable_push_button_irq(struct snd_soc_codec *codec,
+	bool enable)
+{
+	struct rt5645_priv *rt5645 = snd_soc_codec_get_drvdata(codec);
 
-	if ((rt5645->pdata.gpio_hp_det_active_high && gpio_state) ||
-		(!rt5645->pdata.gpio_hp_det_active_high && !gpio_state)) {
-		snd_soc_dapm_force_enable_pin(&codec->dapm, "micbias1");
-		snd_soc_dapm_force_enable_pin(&codec->dapm, "micbias2");
-		snd_soc_dapm_force_enable_pin(&codec->dapm, "LDO2");
-		snd_soc_dapm_force_enable_pin(&codec->dapm, "Mic Det Power");
-		snd_soc_dapm_sync(&codec->dapm);
+	if (enable) {
+		snd_soc_dapm_force_enable_pin_unlocked(&codec->dapm,
+							"ADC L power");
+		snd_soc_dapm_force_enable_pin_unlocked(&codec->dapm,
+							"ADC R power");
+		snd_soc_dapm_force_enable_pin_unlocked(&codec->dapm,
+							"LDO2");
+		snd_soc_dapm_force_enable_pin_unlocked(&codec->dapm,
+							"Mic Det Power");
+		snd_soc_dapm_sync_unlocked(&codec->dapm);
+		snd_soc_update_bits(codec,
+					RT5645_INT_IRQ_ST, 0x8, 0x8);
+		snd_soc_update_bits(codec,
+					RT5650_4BTN_IL_CMD2, 0x8000, 0x8000);
+		snd_soc_read(codec, RT5650_4BTN_IL_CMD1);
+		pr_debug("%s read %x = %x\n", __func__, RT5650_4BTN_IL_CMD1,
+			snd_soc_read(codec, RT5650_4BTN_IL_CMD1));
+	} else {
+		snd_soc_update_bits(codec, RT5650_4BTN_IL_CMD2, 0x8000, 0x0);
+		snd_soc_update_bits(codec, RT5645_INT_IRQ_ST, 0x8, 0x0);
+		snd_soc_dapm_disable_pin_unlocked(&codec->dapm,
+							"ADC L power");
+		snd_soc_dapm_disable_pin_unlocked(&codec->dapm,
+							"ADC R power");
+		if (rt5645->pdata.jd_mode == 0)
+			snd_soc_dapm_disable_pin_unlocked(&codec->dapm,
+								"LDO2");
+		snd_soc_dapm_disable_pin_unlocked(&codec->dapm,
+							"Mic Det Power");
+		snd_soc_dapm_sync_unlocked(&codec->dapm);
+	}
+}
 
-		snd_soc_write(codec, RT5645_IN1_CTRL1, 0x0006);
-		snd_soc_write(codec, RT5645_JD_CTRL3, 0x00b0);
+static int rt5645_jack_detect(struct snd_soc_codec *codec, int jack_insert)
+{
+	struct rt5645_priv *rt5645 = snd_soc_codec_get_drvdata(codec);
+	unsigned int val;
 
-		snd_soc_update_bits(codec, RT5645_IN1_CTRL2,
-			RT5645_CBJ_MN_JD, 0);
-		snd_soc_update_bits(codec, RT5645_IN1_CTRL2,
-			RT5645_CBJ_MN_JD, RT5645_CBJ_MN_JD);
+	if (jack_insert) {
+		regmap_write(rt5645->regmap, RT5645_CHARGE_PUMP, 0x0006);
+
+		if (codec->component.card->instantiated) {
+			/* for jack type detect */
+			snd_soc_dapm_force_enable_pin(&codec->dapm, "LDO2");
+			snd_soc_dapm_force_enable_pin(&codec->dapm,
+				"Mic Det Power");
+			snd_soc_dapm_sync(&codec->dapm);
+		} else {
+			/* Power up necessary bits for JD if dapm is
+			   not ready yet */
+			regmap_update_bits(rt5645->regmap, RT5645_PWR_ANLG1,
+				RT5645_PWR_MB | RT5645_PWR_VREF2,
+				RT5645_PWR_MB | RT5645_PWR_VREF2);
+			regmap_update_bits(rt5645->regmap, RT5645_PWR_MIXER,
+				RT5645_PWR_LDO2, RT5645_PWR_LDO2);
+			regmap_update_bits(rt5645->regmap, RT5645_PWR_VOL,
+				RT5645_PWR_MIC_DET, RT5645_PWR_MIC_DET);
+		}
 
-		msleep(400);
-		val = snd_soc_read(codec, RT5645_IN1_CTRL3) & 0x7;
+		regmap_write(rt5645->regmap, RT5645_JD_CTRL3, 0x00f0);
+		regmap_write(rt5645->regmap, RT5645_IN1_CTRL1, 0x0006);
+		regmap_update_bits(rt5645->regmap,
+				   RT5645_IN1_CTRL2, 0x1000, 0x1000);
+		msleep(100);
+		regmap_update_bits(rt5645->regmap,
+				   RT5645_IN1_CTRL2, 0x1000, 0x0000);
+
+		msleep(450);
+		regmap_read(rt5645->regmap, RT5645_IN1_CTRL3, &val);
+		val &= 0x7;
 		dev_dbg(codec->dev, "val = %d\n", val);
 
-		if (val == 1 || val == 2)
-			jack_type = SND_JACK_HEADSET;
-		else
-			jack_type = SND_JACK_HEADPHONE;
+		if (val == 1 || val == 2) {
+			rt5645->jack_type = SND_JACK_HEADSET;
+			if (rt5645->en_button_func) {
+				rt5645_enable_push_button_irq(codec, true);
+			}
+		} else {
+			if (codec->component.card->instantiated) {
+				snd_soc_dapm_disable_pin(&codec->dapm,
+					"Mic Det Power");
+				snd_soc_dapm_sync(&codec->dapm);
+			} else
+				regmap_update_bits(rt5645->regmap,
+					RT5645_PWR_VOL, RT5645_PWR_MIC_DET, 0);
+			rt5645->jack_type = SND_JACK_HEADPHONE;
+		}
 
-		snd_soc_dapm_disable_pin(&codec->dapm, "micbias1");
-		snd_soc_dapm_disable_pin(&codec->dapm, "micbias2");
-		if (rt5645->pdata.jd_mode == 0)
-			snd_soc_dapm_disable_pin(&codec->dapm, "LDO2");
-		snd_soc_dapm_disable_pin(&codec->dapm, "Mic Det Power");
-		snd_soc_dapm_sync(&codec->dapm);
+	} else { /* jack out */
+		rt5645->jack_type = 0;
+		if (rt5645->en_button_func)
+			rt5645_enable_push_button_irq(codec, false);
+		else {
+			if (codec->component.card->instantiated) {
+				if (rt5645->pdata.jd_mode == 0)
+					snd_soc_dapm_disable_pin(&codec->dapm,
+						"LDO2");
+				snd_soc_dapm_disable_pin(&codec->dapm,
+					"Mic Det Power");
+				snd_soc_dapm_sync(&codec->dapm);
+			} else {
+				if (rt5645->pdata.jd_mode == 0)
+					regmap_update_bits(rt5645->regmap,
+						RT5645_PWR_MIXER,
+						RT5645_PWR_LDO2, 0);
+				regmap_update_bits(rt5645->regmap,
+					RT5645_PWR_VOL, RT5645_PWR_MIC_DET, 0);
+			}
+		}
 	}
 
-	snd_soc_jack_report(rt5645->hp_jack, jack_type, SND_JACK_HEADPHONE);
-	snd_soc_jack_report(rt5645->mic_jack, jack_type, SND_JACK_MICROPHONE);
-	return 0;
+	return rt5645->jack_type;
 }
 
+static int rt5645_irq_detection(struct rt5645_priv *rt5645);
+static irqreturn_t rt5645_irq(int irq, void *data);
+
 int rt5645_set_jack_detect(struct snd_soc_codec *codec,
-	struct snd_soc_jack *hp_jack, struct snd_soc_jack *mic_jack)
+	struct snd_soc_jack *hp_jack, struct snd_soc_jack *mic_jack,
+	struct snd_soc_jack *btn_jack)
 {
 	struct rt5645_priv *rt5645 = snd_soc_codec_get_drvdata(codec);
 
 	rt5645->hp_jack = hp_jack;
 	rt5645->mic_jack = mic_jack;
-	rt5645_jack_detect(codec);
+	rt5645->btn_jack = btn_jack;
+	if (rt5645->btn_jack && rt5645->codec_type == CODEC_TYPE_RT5650) {
+		rt5645->en_button_func = true;
+		regmap_update_bits(rt5645->regmap, RT5645_GPIO_CTRL1,
+				RT5645_GP1_PIN_IRQ, RT5645_GP1_PIN_IRQ);
+		regmap_update_bits(rt5645->regmap, RT5645_DEPOP_M1,
+				RT5645_HP_CB_MASK, RT5645_HP_CB_PU);
+		regmap_update_bits(rt5645->regmap, RT5645_GEN_CTRL1,
+				RT5645_DIG_GATE_CTRL, RT5645_DIG_GATE_CTRL);
+	}
+	rt5645_irq(0, rt5645);
 
 	return 0;
 }
@@ -2486,7 +2906,7 @@ static void rt5645_jack_detect_work(struct work_struct *work)
 	struct rt5645_priv *rt5645 =
 		container_of(work, struct rt5645_priv, jack_detect_work.work);
 
-	rt5645_jack_detect(rt5645->codec);
+	rt5645_irq_detection(rt5645);
 }
 
 static irqreturn_t rt5645_irq(int irq, void *data)
@@ -2499,6 +2919,126 @@ static irqreturn_t rt5645_irq(int irq, void *data)
 	return IRQ_HANDLED;
 }
 
+static int rt5645_button_detect(struct snd_soc_codec *codec)
+{
+	int btn_type, val;
+
+	val = snd_soc_read(codec, RT5650_4BTN_IL_CMD1);
+	pr_debug("val=0x%x\n", val);
+	btn_type = val & 0xfff0;
+	snd_soc_write(codec, RT5650_4BTN_IL_CMD1, val);
+
+	return btn_type;
+}
+
+static int rt5645_irq_detection(struct rt5645_priv *rt5645)
+{
+	int val, btn_type, gpio_state = 0, report = 0;
+
+	switch (rt5645->pdata.jd_mode) {
+	case 0: /* Not using rt5645 JD */
+		if (gpio_is_valid(rt5645->pdata.hp_det_gpio)) {
+			gpio_state = gpio_get_value(rt5645->pdata.hp_det_gpio);
+			dev_dbg(rt5645->codec->dev, "gpio = %d(%d)\n",
+				rt5645->pdata.hp_det_gpio, gpio_state);
+		}
+		if ((rt5645->pdata.gpio_hp_det_active_high && gpio_state) ||
+			(!rt5645->pdata.gpio_hp_det_active_high &&
+			 !gpio_state)) {
+			report = rt5645_jack_detect(rt5645->codec, 1);
+		} else {
+			report = rt5645_jack_detect(rt5645->codec, 0);
+		}
+		snd_soc_jack_report(rt5645->hp_jack,
+				    report, SND_JACK_HEADPHONE);
+		snd_soc_jack_report(rt5645->mic_jack,
+				    report, SND_JACK_MICROPHONE);
+		return report;
+	case 1: /* 2 port */
+		val = snd_soc_read(rt5645->codec, RT5645_A_JD_CTRL1) & 0x0070;
+		break;
+	default: /* 1 port */
+		val = snd_soc_read(rt5645->codec, RT5645_A_JD_CTRL1) & 0x0020;
+		break;
+
+	}
+
+	switch (val) {
+	/* jack in */
+	case 0x30: /* 2 port */
+	case 0x0: /* 1 port or 2 port */
+		if (rt5645->jack_type == 0) {
+			report = rt5645_jack_detect(rt5645->codec, 1);
+			/* for push button and jack out */
+			break;
+		}
+		btn_type = 0;
+		if (snd_soc_read(rt5645->codec, RT5645_INT_IRQ_ST) & 0x4) {
+			/* button pressed */
+			report = SND_JACK_HEADSET;
+			btn_type = rt5645_button_detect(rt5645->codec);
+			/* rt5650 can report three kinds of button behavior,
+			   one click, double click and hold. However,
+			   currently we will report button pressed/released
+			   event. So all the three button behaviors are
+			   treated as button pressed. */
+			switch (btn_type) {
+			case 0x8000:
+			case 0x4000:
+			case 0x2000:
+				report |= SND_JACK_BTN_0;
+				break;
+			case 0x1000:
+			case 0x0800:
+			case 0x0400:
+				report |= SND_JACK_BTN_1;
+				break;
+			case 0x0200:
+			case 0x0100:
+			case 0x0080:
+				report |= SND_JACK_BTN_2;
+				break;
+			case 0x0040:
+			case 0x0020:
+			case 0x0010:
+				report |= SND_JACK_BTN_3;
+				break;
+			case 0x0000: /* unpressed */
+				break;
+			default:
+				dev_err(rt5645->codec->dev,
+					"Unexpected button code 0x%04x\n",
+					btn_type);
+				break;
+			}
+		}
+		if (btn_type == 0)/* button release */
+			report =  rt5645->jack_type;
+
+		break;
+	/* jack out */
+	case 0x70: /* 2 port */
+	case 0x10: /* 2 port */
+	case 0x20: /* 1 port */
+		report = 0;
+		snd_soc_update_bits(rt5645->codec,
+				    RT5645_INT_IRQ_ST, 0x1, 0x0);
+		rt5645_jack_detect(rt5645->codec, 0);
+		break;
+	default:
+		break;
+	}
+
+	snd_soc_jack_report(rt5645->hp_jack, report, SND_JACK_HEADPHONE);
+	snd_soc_jack_report(rt5645->mic_jack, report, SND_JACK_MICROPHONE);
+	if (rt5645->en_button_func)
+		snd_soc_jack_report(rt5645->btn_jack,
+			report, SND_JACK_BTN_0 | SND_JACK_BTN_1 |
+				SND_JACK_BTN_2 | SND_JACK_BTN_3);
+
+	return report;
+}
+
 static int rt5645_probe(struct snd_soc_codec *codec)
 {
 	struct rt5645_priv *rt5645 = snd_soc_codec_get_drvdata(codec);
@@ -2521,12 +3061,10 @@ static int rt5645_probe(struct snd_soc_codec *codec)
 		break;
 	}
 
-	rt5645_set_bias_level(codec, SND_SOC_BIAS_OFF);
-
-	snd_soc_update_bits(codec, RT5645_CHARGE_PUMP, 0x0300, 0x0200);
+	snd_soc_codec_force_bias_level(codec, SND_SOC_BIAS_OFF);
 
 	/* for JD function */
-	if (rt5645->pdata.en_jd_func) {
+	if (rt5645->pdata.jd_mode) {
 		snd_soc_dapm_force_enable_pin(&codec->dapm, "JD Power");
 		snd_soc_dapm_force_enable_pin(&codec->dapm, "LDO2");
 		snd_soc_dapm_sync(&codec->dapm);
@@ -2666,6 +3204,32 @@ static struct acpi_device_id rt5645_acpi_match[] = {
 MODULE_DEVICE_TABLE(acpi, rt5645_acpi_match);
 #endif
 
+static struct rt5645_platform_data *rt5645_pdata;
+
+static struct rt5645_platform_data strago_platform_data = {
+	.dmic1_data_pin = RT5645_DMIC1_DISABLE,
+	.dmic2_data_pin = RT5645_DMIC_DATA_IN2P,
+	.jd_mode = 3,
+};
+
+static int strago_quirk_cb(const struct dmi_system_id *id)
+{
+	rt5645_pdata = &strago_platform_data;
+
+	return 1;
+}
+
+static struct dmi_system_id dmi_platform_intel_braswell[] = {
+	{
+		.ident = "Intel Strago",
+		.callback = strago_quirk_cb,
+		.matches = {
+			DMI_MATCH(DMI_PRODUCT_NAME, "Strago"),
+		},
+	},
+	{ }
+};
+
 static int rt5645_i2c_probe(struct i2c_client *i2c,
 		    const struct i2c_device_id *id)
 {
@@ -2673,6 +3237,7 @@ static int rt5645_i2c_probe(struct i2c_client *i2c,
 	struct rt5645_priv *rt5645;
 	int ret;
 	unsigned int val;
+	struct gpio_desc *gpiod;
 
 	rt5645 = devm_kzalloc(&i2c->dev, sizeof(struct rt5645_priv),
 				GFP_KERNEL);
@@ -2682,8 +3247,23 @@ static int rt5645_i2c_probe(struct i2c_client *i2c,
 	rt5645->i2c = i2c;
 	i2c_set_clientdata(i2c, rt5645);
 
-	if (pdata)
+	if (pdata) {
 		rt5645->pdata = *pdata;
+	} else {
+		if (dmi_check_system(dmi_platform_intel_braswell)) {
+			rt5645->pdata = *rt5645_pdata;
+			gpiod = devm_gpiod_get_index(&i2c->dev, "rt5645", 0);
+
+			if (IS_ERR(gpiod) || gpiod_direction_input(gpiod)) {
+				rt5645->pdata.hp_det_gpio = -1;
+				dev_err(&i2c->dev, "failed to initialize gpiod\n");
+			} else {
+				rt5645->pdata.hp_det_gpio = desc_to_gpio(gpiod);
+				rt5645->pdata.gpio_hp_det_active_high
+						= !gpiod_is_active_low(gpiod);
+			}
+		}
+	}
 
 	rt5645->regmap = devm_regmap_init_i2c(i2c, &rt5645_regmap);
 	if (IS_ERR(rt5645->regmap)) {
@@ -2709,6 +3289,13 @@ static int rt5645_i2c_probe(struct i2c_client *i2c,
 		return -ENODEV;
 	}
 
+	if (rt5645->codec_type == CODEC_TYPE_RT5650) {
+		ret = rt5650_calibration(rt5645);
+
+		if (ret < 0)
+			pr_err("calibration failed!\n");
+	}
+
 	regmap_write(rt5645->regmap, RT5645_RESET, 0);
 
 	ret = regmap_register_patch(rt5645->regmap, init_list,
@@ -2728,84 +3315,76 @@ static int rt5645_i2c_probe(struct i2c_client *i2c,
 		regmap_update_bits(rt5645->regmap, RT5645_IN2_CTRL,
 					RT5645_IN_DF2, RT5645_IN_DF2);
 
-	if (rt5645->pdata.dmic_en) {
+	if (rt5645->pdata.dmic1_data_pin || rt5645->pdata.dmic2_data_pin) {
 		regmap_update_bits(rt5645->regmap, RT5645_GPIO_CTRL1,
 			RT5645_GP2_PIN_MASK, RT5645_GP2_PIN_DMIC1_SCL);
+	}
+	switch (rt5645->pdata.dmic1_data_pin) {
+	case RT5645_DMIC_DATA_IN2N:
+		regmap_update_bits(rt5645->regmap, RT5645_DMIC_CTRL1,
+			RT5645_DMIC_1_DP_MASK, RT5645_DMIC_1_DP_IN2N);
+		break;
 
-		switch (rt5645->pdata.dmic1_data_pin) {
-		case RT5645_DMIC_DATA_IN2N:
-			regmap_update_bits(rt5645->regmap, RT5645_DMIC_CTRL1,
-				RT5645_DMIC_1_DP_MASK, RT5645_DMIC_1_DP_IN2N);
-			break;
-
-		case RT5645_DMIC_DATA_GPIO5:
-			regmap_update_bits(rt5645->regmap, RT5645_DMIC_CTRL1,
-				RT5645_DMIC_1_DP_MASK, RT5645_DMIC_1_DP_GPIO5);
-			regmap_update_bits(rt5645->regmap, RT5645_GPIO_CTRL1,
-				RT5645_GP5_PIN_MASK, RT5645_GP5_PIN_DMIC1_SDA);
-			break;
-
-		case RT5645_DMIC_DATA_GPIO11:
-			regmap_update_bits(rt5645->regmap, RT5645_DMIC_CTRL1,
-				RT5645_DMIC_1_DP_MASK, RT5645_DMIC_1_DP_GPIO11);
-			regmap_update_bits(rt5645->regmap, RT5645_GPIO_CTRL1,
-				RT5645_GP11_PIN_MASK,
-				RT5645_GP11_PIN_DMIC1_SDA);
-			break;
+	case RT5645_DMIC_DATA_GPIO5:
+		regmap_update_bits(rt5645->regmap, RT5645_DMIC_CTRL1,
+			RT5645_DMIC_1_DP_MASK, RT5645_DMIC_1_DP_GPIO5);
+		regmap_update_bits(rt5645->regmap, RT5645_GPIO_CTRL1,
+			RT5645_GP5_PIN_MASK, RT5645_GP5_PIN_DMIC1_SDA);
+		break;
 
-		default:
-			break;
-		}
+	case RT5645_DMIC_DATA_GPIO11:
+		regmap_update_bits(rt5645->regmap, RT5645_DMIC_CTRL1,
+			RT5645_DMIC_1_DP_MASK, RT5645_DMIC_1_DP_GPIO11);
+		regmap_update_bits(rt5645->regmap, RT5645_GPIO_CTRL1,
+			RT5645_GP11_PIN_MASK,
+			RT5645_GP11_PIN_DMIC1_SDA);
+		break;
 
-		switch (rt5645->pdata.dmic2_data_pin) {
-		case RT5645_DMIC_DATA_IN2P:
-			regmap_update_bits(rt5645->regmap, RT5645_DMIC_CTRL1,
-				RT5645_DMIC_2_DP_MASK, RT5645_DMIC_2_DP_IN2P);
-			break;
+	default:
+		break;
+	}
 
-		case RT5645_DMIC_DATA_GPIO6:
-			regmap_update_bits(rt5645->regmap, RT5645_DMIC_CTRL1,
-				RT5645_DMIC_2_DP_MASK, RT5645_DMIC_2_DP_GPIO6);
-			regmap_update_bits(rt5645->regmap, RT5645_GPIO_CTRL1,
-				RT5645_GP6_PIN_MASK, RT5645_GP6_PIN_DMIC2_SDA);
-			break;
+	switch (rt5645->pdata.dmic2_data_pin) {
+	case RT5645_DMIC_DATA_IN2P:
+		regmap_update_bits(rt5645->regmap, RT5645_DMIC_CTRL1,
+			RT5645_DMIC_2_DP_MASK, RT5645_DMIC_2_DP_IN2P);
+		break;
 
-		case RT5645_DMIC_DATA_GPIO10:
-			regmap_update_bits(rt5645->regmap, RT5645_DMIC_CTRL1,
-				RT5645_DMIC_2_DP_MASK, RT5645_DMIC_2_DP_GPIO10);
-			regmap_update_bits(rt5645->regmap, RT5645_GPIO_CTRL1,
-				RT5645_GP10_PIN_MASK,
-				RT5645_GP10_PIN_DMIC2_SDA);
-			break;
+	case RT5645_DMIC_DATA_GPIO6:
+		regmap_update_bits(rt5645->regmap, RT5645_DMIC_CTRL1,
+			RT5645_DMIC_2_DP_MASK, RT5645_DMIC_2_DP_GPIO6);
+		regmap_update_bits(rt5645->regmap, RT5645_GPIO_CTRL1,
+			RT5645_GP6_PIN_MASK, RT5645_GP6_PIN_DMIC2_SDA);
+		break;
 
-		case RT5645_DMIC_DATA_GPIO12:
-			regmap_update_bits(rt5645->regmap, RT5645_DMIC_CTRL1,
-				RT5645_DMIC_2_DP_MASK, RT5645_DMIC_2_DP_GPIO12);
-			regmap_update_bits(rt5645->regmap, RT5645_GPIO_CTRL1,
-				RT5645_GP12_PIN_MASK,
-				RT5645_GP12_PIN_DMIC2_SDA);
-			break;
+	case RT5645_DMIC_DATA_GPIO10:
+		regmap_update_bits(rt5645->regmap, RT5645_DMIC_CTRL1,
+			RT5645_DMIC_2_DP_MASK, RT5645_DMIC_2_DP_GPIO10);
+		regmap_update_bits(rt5645->regmap, RT5645_GPIO_CTRL1,
+			RT5645_GP10_PIN_MASK,
+			RT5645_GP10_PIN_DMIC2_SDA);
+		break;
 
-		default:
-			break;
-		}
+	case RT5645_DMIC_DATA_GPIO12:
+		regmap_update_bits(rt5645->regmap, RT5645_DMIC_CTRL1,
+			RT5645_DMIC_2_DP_MASK, RT5645_DMIC_2_DP_GPIO12);
+		regmap_update_bits(rt5645->regmap, RT5645_GPIO_CTRL1,
+			RT5645_GP12_PIN_MASK,
+			RT5645_GP12_PIN_DMIC2_SDA);
+		break;
 
+	default:
+		break;
 	}
 
-	if (rt5645->pdata.en_jd_func) {
+	if (rt5645->pdata.jd_mode) {
 		regmap_update_bits(rt5645->regmap, RT5645_GEN_CTRL3,
-			RT5645_IRQ_CLK_GATE_CTRL | RT5645_MICINDET_MANU,
-			RT5645_IRQ_CLK_GATE_CTRL | RT5645_MICINDET_MANU);
+				   RT5645_IRQ_CLK_GATE_CTRL,
+				   RT5645_IRQ_CLK_GATE_CTRL);
 		regmap_update_bits(rt5645->regmap, RT5645_IN1_CTRL1,
-			RT5645_CBJ_BST1_EN, RT5645_CBJ_BST1_EN);
-		regmap_update_bits(rt5645->regmap, RT5645_JD_CTRL3,
-			RT5645_JD_CBJ_EN | RT5645_JD_CBJ_POL,
-			RT5645_JD_CBJ_EN | RT5645_JD_CBJ_POL);
+				   RT5645_CBJ_BST1_EN, RT5645_CBJ_BST1_EN);
 		regmap_update_bits(rt5645->regmap, RT5645_MICBIAS,
-			RT5645_IRQ_CLK_INT, RT5645_IRQ_CLK_INT);
-	}
-
-	if (rt5645->pdata.jd_mode) {
+				   RT5645_IRQ_CLK_INT, RT5645_IRQ_CLK_INT);
 		regmap_update_bits(rt5645->regmap, RT5645_IRQ_CTRL2,
 				   RT5645_IRQ_JD_1_1_EN, RT5645_IRQ_JD_1_1_EN);
 		regmap_update_bits(rt5645->regmap, RT5645_GEN_CTRL3,
diff --git a/sound/soc/codecs/rt5645.h b/sound/soc/codecs/rt5645.h
index db78e9462876..9ec4e899795d 100644
--- a/sound/soc/codecs/rt5645.h
+++ b/sound/soc/codecs/rt5645.h
@@ -105,6 +105,7 @@
 #define RT5645_TDM_CTRL_1			0x77
 #define RT5645_TDM_CTRL_2			0x78
 #define RT5645_TDM_CTRL_3			0x79
+#define RT5650_TDM_CTRL_4			0x7a
 
 /* Function - Analog */
 #define RT5645_GLB_CLK				0x80
@@ -942,10 +943,6 @@
 #define RT5645_I2S2_SDI_I2S2			(0x1 << 6)
 
 /* ADC/DAC Clock Control 1 (0x73) */
-#define RT5645_I2S_BCLK_MS1_MASK		(0x1 << 15)
-#define RT5645_I2S_BCLK_MS1_SFT			15
-#define RT5645_I2S_BCLK_MS1_32			(0x0 << 15)
-#define RT5645_I2S_BCLK_MS1_64			(0x1 << 15)
 #define RT5645_I2S_PD1_MASK			(0x7 << 12)
 #define RT5645_I2S_PD1_SFT			12
 #define RT5645_I2S_PD1_1			(0x0 << 12)
@@ -1067,13 +1064,14 @@
 #define RT5645_SCLK_SRC_SFT			14
 #define RT5645_SCLK_SRC_MCLK			(0x0 << 14)
 #define RT5645_SCLK_SRC_PLL1			(0x1 << 14)
-#define RT5645_SCLK_SRC_RCCLK			(0x2 << 14) /* 15MHz */
-#define RT5645_PLL1_SRC_MASK			(0x3 << 12)
-#define RT5645_PLL1_SRC_SFT			12
-#define RT5645_PLL1_SRC_MCLK			(0x0 << 12)
-#define RT5645_PLL1_SRC_BCLK1			(0x1 << 12)
-#define RT5645_PLL1_SRC_BCLK2			(0x2 << 12)
-#define RT5645_PLL1_SRC_BCLK3			(0x3 << 12)
+#define RT5645_SCLK_SRC_RCCLK			(0x2 << 14)
+#define RT5645_PLL1_SRC_MASK			(0x7 << 11)
+#define RT5645_PLL1_SRC_SFT			11
+#define RT5645_PLL1_SRC_MCLK			(0x0 << 11)
+#define RT5645_PLL1_SRC_BCLK1			(0x1 << 11)
+#define RT5645_PLL1_SRC_BCLK2			(0x2 << 11)
+#define RT5645_PLL1_SRC_BCLK3			(0x3 << 11)
+#define RT5645_PLL1_SRC_RCCLK			(0x4 << 11)
 #define RT5645_PLL1_PD_MASK			(0x1 << 3)
 #define RT5645_PLL1_PD_SFT			3
 #define RT5645_PLL1_PD_1			(0x0 << 3)
@@ -2147,6 +2145,7 @@ enum {
 };
 
 enum {
+	RT5645_DMIC1_DISABLE,
 	RT5645_DMIC_DATA_IN2P,
 	RT5645_DMIC_DATA_GPIO6,
 	RT5645_DMIC_DATA_GPIO10,
@@ -2154,6 +2153,7 @@ enum {
 };
 
 enum {
+	RT5645_DMIC2_DISABLE,
 	RT5645_DMIC_DATA_IN2N,
 	RT5645_DMIC_DATA_GPIO5,
 	RT5645_DMIC_DATA_GPIO11,
@@ -2184,6 +2184,7 @@ struct rt5645_priv {
 	struct i2c_client *i2c;
 	struct snd_soc_jack *hp_jack;
 	struct snd_soc_jack *mic_jack;
+	struct snd_soc_jack *btn_jack;
 	struct delayed_work jack_detect_work;
 
 	int codec_type;
@@ -2196,9 +2197,12 @@ struct rt5645_priv {
 	int pll_src;
 	int pll_in;
 	int pll_out;
+
+	int jack_type;
+	bool en_button_func;
 };
 
 int rt5645_set_jack_detect(struct snd_soc_codec *codec,
-	struct snd_soc_jack *hp_jack, struct snd_soc_jack *mic_jack);
-
+	struct snd_soc_jack *hp_jack, struct snd_soc_jack *mic_jack,
+	struct snd_soc_jack *btn_jack);
 #endif /* __RT5645_H__ */
diff --git a/sound/soc/codecs/rt5651.c b/sound/soc/codecs/rt5651.c
index 9f4c7be6d798..a3506e193abc 100644
--- a/sound/soc/codecs/rt5651.c
+++ b/sound/soc/codecs/rt5651.c
@@ -1571,7 +1571,7 @@ static int rt5651_set_bias_level(struct snd_soc_codec *codec,
 {
 	switch (level) {
 	case SND_SOC_BIAS_PREPARE:
-		if (SND_SOC_BIAS_STANDBY == codec->dapm.bias_level) {
+		if (SND_SOC_BIAS_STANDBY == snd_soc_codec_get_bias_level(codec)) {
 			snd_soc_update_bits(codec, RT5651_PWR_ANLG1,
 				RT5651_PWR_VREF1 | RT5651_PWR_MB |
 				RT5651_PWR_BG | RT5651_PWR_VREF2,
@@ -1604,7 +1604,6 @@ static int rt5651_set_bias_level(struct snd_soc_codec *codec,
 	default:
 		break;
 	}
-	codec->dapm.bias_level = level;
 
 	return 0;
 }
@@ -1625,7 +1624,7 @@ static int rt5651_probe(struct snd_soc_codec *codec)
 		RT5651_PWR_FV1 | RT5651_PWR_FV2,
 		RT5651_PWR_FV1 | RT5651_PWR_FV2);
 
-	rt5651_set_bias_level(codec, SND_SOC_BIAS_OFF);
+	snd_soc_codec_force_bias_level(codec, SND_SOC_BIAS_OFF);
 
 	return 0;
 }
diff --git a/sound/soc/codecs/rt5670.c b/sound/soc/codecs/rt5670.c
index cc7f84a150a7..840dd6d0003a 100644
--- a/sound/soc/codecs/rt5670.c
+++ b/sound/soc/codecs/rt5670.c
@@ -416,12 +416,12 @@ static bool rt5670_readable_register(struct device *dev, unsigned int reg)
 static int rt5670_headset_detect(struct snd_soc_codec *codec, int jack_insert)
 {
 	int val;
+	struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
 	struct rt5670_priv *rt5670 = snd_soc_codec_get_drvdata(codec);
 
 	if (jack_insert) {
-		snd_soc_dapm_force_enable_pin(&codec->dapm,
-						       "Mic Det Power");
-		snd_soc_dapm_sync(&codec->dapm);
+		snd_soc_dapm_force_enable_pin(dapm, "Mic Det Power");
+		snd_soc_dapm_sync(dapm);
 		snd_soc_update_bits(codec, RT5670_GEN_CTRL3, 0x4, 0x0);
 		snd_soc_update_bits(codec, RT5670_CJ_CTRL2,
 			RT5670_CBJ_DET_MODE | RT5670_CBJ_MN_JD,
@@ -447,15 +447,15 @@ static int rt5670_headset_detect(struct snd_soc_codec *codec, int jack_insert)
 		} else {
 			snd_soc_update_bits(codec, RT5670_GEN_CTRL3, 0x4, 0x4);
 			rt5670->jack_type = SND_JACK_HEADPHONE;
-			snd_soc_dapm_disable_pin(&codec->dapm, "Mic Det Power");
-			snd_soc_dapm_sync(&codec->dapm);
+			snd_soc_dapm_disable_pin(dapm, "Mic Det Power");
+			snd_soc_dapm_sync(dapm);
 		}
 	} else {
 		snd_soc_update_bits(codec, RT5670_INT_IRQ_ST, 0x8, 0x0);
 		snd_soc_update_bits(codec, RT5670_GEN_CTRL3, 0x4, 0x4);
 		rt5670->jack_type = 0;
-		snd_soc_dapm_disable_pin(&codec->dapm, "Mic Det Power");
-		snd_soc_dapm_sync(&codec->dapm);
+		snd_soc_dapm_disable_pin(dapm, "Mic Det Power");
+		snd_soc_dapm_sync(dapm);
 	}
 
 	return rt5670->jack_type;
@@ -2603,7 +2603,7 @@ static int rt5670_set_bias_level(struct snd_soc_codec *codec,
 
 	switch (level) {
 	case SND_SOC_BIAS_PREPARE:
-		if (SND_SOC_BIAS_STANDBY == codec->dapm.bias_level) {
+		if (SND_SOC_BIAS_STANDBY == snd_soc_codec_get_bias_level(codec)) {
 			snd_soc_update_bits(codec, RT5670_PWR_ANLG1,
 				RT5670_PWR_VREF1 | RT5670_PWR_MB |
 				RT5670_PWR_BG | RT5670_PWR_VREF2,
@@ -2647,30 +2647,30 @@ static int rt5670_set_bias_level(struct snd_soc_codec *codec,
 	default:
 		break;
 	}
-	codec->dapm.bias_level = level;
 
 	return 0;
 }
 
 static int rt5670_probe(struct snd_soc_codec *codec)
 {
+	struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
 	struct rt5670_priv *rt5670 = snd_soc_codec_get_drvdata(codec);
 
 	switch (snd_soc_read(codec, RT5670_RESET) & RT5670_ID_MASK) {
 	case RT5670_ID_5670:
 	case RT5670_ID_5671:
-		snd_soc_dapm_new_controls(&codec->dapm,
+		snd_soc_dapm_new_controls(dapm,
 			rt5670_specific_dapm_widgets,
 			ARRAY_SIZE(rt5670_specific_dapm_widgets));
-		snd_soc_dapm_add_routes(&codec->dapm,
+		snd_soc_dapm_add_routes(dapm,
 			rt5670_specific_dapm_routes,
 			ARRAY_SIZE(rt5670_specific_dapm_routes));
 		break;
 	case RT5670_ID_5672:
-		snd_soc_dapm_new_controls(&codec->dapm,
+		snd_soc_dapm_new_controls(dapm,
 			rt5672_specific_dapm_widgets,
 			ARRAY_SIZE(rt5672_specific_dapm_widgets));
-		snd_soc_dapm_add_routes(&codec->dapm,
+		snd_soc_dapm_add_routes(dapm,
 			rt5672_specific_dapm_routes,
 			ARRAY_SIZE(rt5672_specific_dapm_routes));
 		break;
diff --git a/sound/soc/codecs/rt5677.c b/sound/soc/codecs/rt5677.c
index 169aa471ffbd..31d969ac1192 100644
--- a/sound/soc/codecs/rt5677.c
+++ b/sound/soc/codecs/rt5677.c
@@ -820,7 +820,7 @@ static int rt5677_dsp_vad_put(struct snd_kcontrol *kcontrol,
 
 	rt5677->dsp_vad_en = !!ucontrol->value.integer.value[0];
 
-	if (codec->dapm.bias_level == SND_SOC_BIAS_OFF)
+	if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF)
 		rt5677_set_dsp_vad(codec, rt5677->dsp_vad_en);
 
 	return 0;
@@ -1060,6 +1060,7 @@ int rt5677_sel_asrc_clk_src(struct snd_soc_codec *codec,
 	unsigned int asrc5_mask = 0, asrc5_value = 0;
 	unsigned int asrc6_mask = 0, asrc6_value = 0;
 	unsigned int asrc7_mask = 0, asrc7_value = 0;
+	unsigned int asrc8_mask = 0, asrc8_value = 0;
 
 	switch (clk_src) {
 	case RT5677_CLK_SEL_SYS:
@@ -1196,10 +1197,108 @@ int rt5677_sel_asrc_clk_src(struct snd_soc_codec *codec,
 		regmap_update_bits(rt5677->regmap, RT5677_ASRC_7, asrc7_mask,
 			asrc7_value);
 
+	/* ASRC 8 */
+	if (filter_mask & RT5677_I2S1_SOURCE) {
+		asrc8_mask |= RT5677_I2S1_CLK_SEL_MASK;
+		asrc8_value = (asrc8_value & ~RT5677_I2S1_CLK_SEL_MASK)
+			| ((clk_src - 1) << RT5677_I2S1_CLK_SEL_SFT);
+	}
+
+	if (filter_mask & RT5677_I2S2_SOURCE) {
+		asrc8_mask |= RT5677_I2S2_CLK_SEL_MASK;
+		asrc8_value = (asrc8_value & ~RT5677_I2S2_CLK_SEL_MASK)
+			| ((clk_src - 1) << RT5677_I2S2_CLK_SEL_SFT);
+	}
+
+	if (filter_mask & RT5677_I2S3_SOURCE) {
+		asrc8_mask |= RT5677_I2S3_CLK_SEL_MASK;
+		asrc8_value = (asrc8_value & ~RT5677_I2S3_CLK_SEL_MASK)
+			| ((clk_src - 1) << RT5677_I2S3_CLK_SEL_SFT);
+	}
+
+	if (filter_mask & RT5677_I2S4_SOURCE) {
+		asrc8_mask |= RT5677_I2S4_CLK_SEL_MASK;
+		asrc8_value = (asrc8_value & ~RT5677_I2S4_CLK_SEL_MASK)
+			| ((clk_src - 1) << RT5677_I2S4_CLK_SEL_SFT);
+	}
+
+	if (asrc8_mask)
+		regmap_update_bits(rt5677->regmap, RT5677_ASRC_8, asrc8_mask,
+			asrc8_value);
+
 	return 0;
 }
 EXPORT_SYMBOL_GPL(rt5677_sel_asrc_clk_src);
 
+static int rt5677_dmic_use_asrc(struct snd_soc_dapm_widget *source,
+			 struct snd_soc_dapm_widget *sink)
+{
+	struct snd_soc_codec *codec = snd_soc_dapm_to_codec(source->dapm);
+	struct rt5677_priv *rt5677 = snd_soc_codec_get_drvdata(codec);
+	unsigned int asrc_setting;
+
+	switch (source->shift) {
+	case 11:
+		regmap_read(rt5677->regmap, RT5677_ASRC_5, &asrc_setting);
+		asrc_setting = (asrc_setting & RT5677_AD_STO1_CLK_SEL_MASK) >>
+				RT5677_AD_STO1_CLK_SEL_SFT;
+		if (asrc_setting >= RT5677_CLK_SEL_I2S1_ASRC &&
+			asrc_setting <= RT5677_CLK_SEL_I2S6_ASRC)
+			return 1;
+		break;
+
+	case 10:
+		regmap_read(rt5677->regmap, RT5677_ASRC_5, &asrc_setting);
+		asrc_setting = (asrc_setting & RT5677_AD_STO2_CLK_SEL_MASK) >>
+				RT5677_AD_STO2_CLK_SEL_SFT;
+		if (asrc_setting >= RT5677_CLK_SEL_I2S1_ASRC &&
+			asrc_setting <= RT5677_CLK_SEL_I2S6_ASRC)
+			return 1;
+		break;
+
+	case 9:
+		regmap_read(rt5677->regmap, RT5677_ASRC_5, &asrc_setting);
+		asrc_setting = (asrc_setting & RT5677_AD_STO3_CLK_SEL_MASK) >>
+				RT5677_AD_STO3_CLK_SEL_SFT;
+		if (asrc_setting >= RT5677_CLK_SEL_I2S1_ASRC &&
+			asrc_setting <= RT5677_CLK_SEL_I2S6_ASRC)
+			return 1;
+		break;
+
+	case 8:
+		regmap_read(rt5677->regmap, RT5677_ASRC_5, &asrc_setting);
+		asrc_setting = (asrc_setting & RT5677_AD_STO4_CLK_SEL_MASK) >>
+			RT5677_AD_STO4_CLK_SEL_SFT;
+		if (asrc_setting >= RT5677_CLK_SEL_I2S1_ASRC &&
+			asrc_setting <= RT5677_CLK_SEL_I2S6_ASRC)
+			return 1;
+		break;
+
+	case 7:
+		regmap_read(rt5677->regmap, RT5677_ASRC_6, &asrc_setting);
+		asrc_setting = (asrc_setting & RT5677_AD_MONOL_CLK_SEL_MASK) >>
+			RT5677_AD_MONOL_CLK_SEL_SFT;
+		if (asrc_setting >= RT5677_CLK_SEL_I2S1_ASRC &&
+			asrc_setting <= RT5677_CLK_SEL_I2S6_ASRC)
+			return 1;
+		break;
+
+	case 6:
+		regmap_read(rt5677->regmap, RT5677_ASRC_6, &asrc_setting);
+		asrc_setting = (asrc_setting & RT5677_AD_MONOR_CLK_SEL_MASK) >>
+			RT5677_AD_MONOR_CLK_SEL_SFT;
+		if (asrc_setting >= RT5677_CLK_SEL_I2S1_ASRC &&
+			asrc_setting <= RT5677_CLK_SEL_I2S6_ASRC)
+			return 1;
+		break;
+
+	default:
+		break;
+	}
+
+	return 0;
+}
+
 /* Digital Mixer */
 static const struct snd_kcontrol_new rt5677_sto1_adc_l_mix[] = {
 	SOC_DAPM_SINGLE("ADC1 Switch", RT5677_STO1_ADC_MIXER,
@@ -2479,7 +2578,7 @@ static int rt5677_vref_event(struct snd_soc_dapm_widget *w,
 
 	switch (event) {
 	case SND_SOC_DAPM_POST_PMU:
-		if (codec->dapm.bias_level != SND_SOC_BIAS_ON &&
+		if (snd_soc_codec_get_bias_level(codec) != SND_SOC_BIAS_ON &&
 			!rt5677->is_vref_slow) {
 			mdelay(20);
 			regmap_update_bits(rt5677->regmap, RT5677_PWR_ANLG1,
@@ -3057,12 +3156,12 @@ static const struct snd_soc_dapm_widget rt5677_dapm_widgets[] = {
 };
 
 static const struct snd_soc_dapm_route rt5677_dapm_routes[] = {
-	{ "Stereo1 DMIC Mux", NULL, "DMIC STO1 ASRC", can_use_asrc },
-	{ "Stereo2 DMIC Mux", NULL, "DMIC STO2 ASRC", can_use_asrc },
-	{ "Stereo3 DMIC Mux", NULL, "DMIC STO3 ASRC", can_use_asrc },
-	{ "Stereo4 DMIC Mux", NULL, "DMIC STO4 ASRC", can_use_asrc },
-	{ "Mono DMIC L Mux", NULL, "DMIC MONO L ASRC", can_use_asrc },
-	{ "Mono DMIC R Mux", NULL, "DMIC MONO R ASRC", can_use_asrc },
+	{ "Stereo1 DMIC Mux", NULL, "DMIC STO1 ASRC", rt5677_dmic_use_asrc },
+	{ "Stereo2 DMIC Mux", NULL, "DMIC STO2 ASRC", rt5677_dmic_use_asrc },
+	{ "Stereo3 DMIC Mux", NULL, "DMIC STO3 ASRC", rt5677_dmic_use_asrc },
+	{ "Stereo4 DMIC Mux", NULL, "DMIC STO4 ASRC", rt5677_dmic_use_asrc },
+	{ "Mono DMIC L Mux", NULL, "DMIC MONO L ASRC", rt5677_dmic_use_asrc },
+	{ "Mono DMIC R Mux", NULL, "DMIC MONO R ASRC", rt5677_dmic_use_asrc },
 	{ "I2S1", NULL, "I2S1 ASRC", can_use_asrc},
 	{ "I2S2", NULL, "I2S2 ASRC", can_use_asrc},
 	{ "I2S3", NULL, "I2S3 ASRC", can_use_asrc},
@@ -4353,7 +4452,7 @@ static int rt5677_set_bias_level(struct snd_soc_codec *codec,
 		break;
 
 	case SND_SOC_BIAS_PREPARE:
-		if (codec->dapm.bias_level == SND_SOC_BIAS_STANDBY) {
+		if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_STANDBY) {
 			rt5677_set_dsp_vad(codec, false);
 
 			regmap_update_bits(rt5677->regmap, RT5677_PWR_ANLG1,
@@ -4395,7 +4494,6 @@ static int rt5677_set_bias_level(struct snd_soc_codec *codec,
 	default:
 		break;
 	}
-	codec->dapm.bias_level = level;
 
 	return 0;
 }
@@ -4606,22 +4704,23 @@ static void rt5677_free_gpio(struct i2c_client *i2c)
 
 static int rt5677_probe(struct snd_soc_codec *codec)
 {
+	struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
 	struct rt5677_priv *rt5677 = snd_soc_codec_get_drvdata(codec);
 	int i;
 
 	rt5677->codec = codec;
 
 	if (rt5677->pdata.dmic2_clk_pin == RT5677_DMIC_CLK2) {
-		snd_soc_dapm_add_routes(&codec->dapm,
+		snd_soc_dapm_add_routes(dapm,
 			rt5677_dmic2_clk_2,
 			ARRAY_SIZE(rt5677_dmic2_clk_2));
 	} else { /*use dmic1 clock by default*/
-		snd_soc_dapm_add_routes(&codec->dapm,
+		snd_soc_dapm_add_routes(dapm,
 			rt5677_dmic2_clk_1,
 			ARRAY_SIZE(rt5677_dmic2_clk_1));
 	}
 
-	rt5677_set_bias_level(codec, SND_SOC_BIAS_OFF);
+	snd_soc_codec_force_bias_level(codec, SND_SOC_BIAS_OFF);
 
 	regmap_write(rt5677->regmap, RT5677_DIG_MISC, 0x0020);
 	regmap_write(rt5677->regmap, RT5677_PWR_DSP2, 0x0c00);
@@ -4667,6 +4766,8 @@ static int rt5677_remove(struct snd_soc_codec *codec)
 	regmap_write(rt5677->regmap, RT5677_RESET, 0x10ec);
 	if (gpio_is_valid(rt5677->pow_ldo2))
 		gpio_set_value_cansleep(rt5677->pow_ldo2, 0);
+	if (gpio_is_valid(rt5677->reset_pin))
+		gpio_set_value_cansleep(rt5677->reset_pin, 0);
 
 	return 0;
 }
@@ -4682,6 +4783,8 @@ static int rt5677_suspend(struct snd_soc_codec *codec)
 
 		if (gpio_is_valid(rt5677->pow_ldo2))
 			gpio_set_value_cansleep(rt5677->pow_ldo2, 0);
+		if (gpio_is_valid(rt5677->reset_pin))
+			gpio_set_value_cansleep(rt5677->reset_pin, 0);
 	}
 
 	return 0;
@@ -4692,10 +4795,13 @@ static int rt5677_resume(struct snd_soc_codec *codec)
 	struct rt5677_priv *rt5677 = snd_soc_codec_get_drvdata(codec);
 
 	if (!rt5677->dsp_vad_en) {
-		if (gpio_is_valid(rt5677->pow_ldo2)) {
+		if (gpio_is_valid(rt5677->pow_ldo2))
 			gpio_set_value_cansleep(rt5677->pow_ldo2, 1);
+		if (gpio_is_valid(rt5677->reset_pin))
+			gpio_set_value_cansleep(rt5677->reset_pin, 1);
+		if (gpio_is_valid(rt5677->pow_ldo2) ||
+		    gpio_is_valid(rt5677->reset_pin))
 			msleep(10);
-		}
 
 		regcache_cache_only(rt5677->regmap, false);
 		regcache_sync(rt5677->regmap);
@@ -4933,6 +5039,8 @@ static int rt5677_parse_dt(struct rt5677_priv *rt5677, struct device_node *np)
 
 	rt5677->pow_ldo2 = of_get_named_gpio(np,
 					"realtek,pow-ldo2-gpio", 0);
+	rt5677->reset_pin = of_get_named_gpio(np,
+					"realtek,reset-gpio", 0);
 
 	/*
 	 * POW_LDO2 is optional (it may be statically tied on the board).
@@ -4943,6 +5051,9 @@ static int rt5677_parse_dt(struct rt5677_priv *rt5677, struct device_node *np)
 	if (!gpio_is_valid(rt5677->pow_ldo2) &&
 			(rt5677->pow_ldo2 != -ENOENT))
 		return rt5677->pow_ldo2;
+	if (!gpio_is_valid(rt5677->reset_pin) &&
+			(rt5677->reset_pin != -ENOENT))
+		return rt5677->reset_pin;
 
 	of_property_read_u8_array(np, "realtek,gpio-config",
 		rt5677->pdata.gpio_config, RT5677_GPIO_NUM);
@@ -5044,6 +5155,7 @@ static int rt5677_i2c_probe(struct i2c_client *i2c,
 		}
 	} else {
 		rt5677->pow_ldo2 = -EINVAL;
+		rt5677->reset_pin = -EINVAL;
 	}
 
 	if (gpio_is_valid(rt5677->pow_ldo2)) {
@@ -5055,6 +5167,21 @@ static int rt5677_i2c_probe(struct i2c_client *i2c,
 				rt5677->pow_ldo2, ret);
 			return ret;
 		}
+	}
+
+	if (gpio_is_valid(rt5677->reset_pin)) {
+		ret = devm_gpio_request_one(&i2c->dev, rt5677->reset_pin,
+					    GPIOF_OUT_INIT_HIGH,
+					    "RT5677 RESET");
+		if (ret < 0) {
+			dev_err(&i2c->dev, "Failed to request RESET %d: %d\n",
+				rt5677->reset_pin, ret);
+			return ret;
+		}
+	}
+
+	if (gpio_is_valid(rt5677->pow_ldo2) ||
+	    gpio_is_valid(rt5677->reset_pin)) {
 		/* Wait a while until I2C bus becomes available. The datasheet
 		 * does not specify the exact we should wait but startup
 		 * sequence mentiones at least a few milliseconds.
diff --git a/sound/soc/codecs/rt5677.h b/sound/soc/codecs/rt5677.h
index 9dceb41d18ea..7eca38a23255 100644
--- a/sound/soc/codecs/rt5677.h
+++ b/sound/soc/codecs/rt5677.h
@@ -1446,6 +1446,16 @@
 #define RT5677_DSP_OB_4_7_CLK_SEL_MASK		(0xf << 8)
 #define RT5677_DSP_OB_4_7_CLK_SEL_SFT		8
 
+/* ASRC Control 8 (0x8a) */
+#define RT5677_I2S1_CLK_SEL_MASK		(0xf << 12)
+#define RT5677_I2S1_CLK_SEL_SFT			12
+#define RT5677_I2S2_CLK_SEL_MASK		(0xf << 8)
+#define RT5677_I2S2_CLK_SEL_SFT			8
+#define RT5677_I2S3_CLK_SEL_MASK		(0xf << 4)
+#define RT5677_I2S3_CLK_SEL_SFT			4
+#define RT5677_I2S4_CLK_SEL_MASK		(0xf)
+#define RT5677_I2S4_CLK_SEL_SFT			0
+
 /* VAD Function Control 4 (0x9f) */
 #define RT5677_VAD_SRC_MASK			(0x7 << 8)
 #define RT5677_VAD_SRC_SFT			8
@@ -1744,6 +1754,10 @@ enum {
 	RT5677_AD_MONO_R_FILTER = (0x1 << 12),
 	RT5677_DSP_OB_0_3_FILTER = (0x1 << 13),
 	RT5677_DSP_OB_4_7_FILTER = (0x1 << 14),
+	RT5677_I2S1_SOURCE = (0x1 << 15),
+	RT5677_I2S2_SOURCE = (0x1 << 16),
+	RT5677_I2S3_SOURCE = (0x1 << 17),
+	RT5677_I2S4_SOURCE = (0x1 << 18),
 };
 
 struct rt5677_priv {
@@ -1762,6 +1776,7 @@ struct rt5677_priv {
 	int pll_in;
 	int pll_out;
 	int pow_ldo2; /* POW_LDO2 pin */
+	int reset_pin; /* RESET pin */
 	enum rt5677_type type;
 #ifdef CONFIG_GPIOLIB
 	struct gpio_chip gpio_chip;
diff --git a/sound/soc/codecs/sgtl5000.c b/sound/soc/codecs/sgtl5000.c
index 3593a1496056..e673f6ceb521 100644
--- a/sound/soc/codecs/sgtl5000.c
+++ b/sound/soc/codecs/sgtl5000.c
@@ -948,7 +948,7 @@ static int sgtl5000_set_bias_level(struct snd_soc_codec *codec,
 	case SND_SOC_BIAS_PREPARE:
 		break;
 	case SND_SOC_BIAS_STANDBY:
-		if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) {
+		if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) {
 			ret = regulator_bulk_enable(
 						ARRAY_SIZE(sgtl5000->supplies),
 						sgtl5000->supplies);
@@ -979,7 +979,6 @@ static int sgtl5000_set_bias_level(struct snd_soc_codec *codec,
 		break;
 	}
 
-	codec->dapm.bias_level = level;
 	return 0;
 }
 
@@ -1092,6 +1091,19 @@ static bool sgtl5000_readable(struct device *dev, unsigned int reg)
 }
 
 /*
+ * This precalculated table contains all (vag_val * 100 / lo_calcntrl) results
+ * to select an appropriate lo_vol_* in SGTL5000_CHIP_LINE_OUT_VOL
+ * The calculatation was done for all possible register values which
+ * is the array index and the following formula: 10^((idx−15)/40) * 100
+ */
+static const u8 vol_quot_table[] = {
+	42, 45, 47, 50, 53, 56, 60, 63,
+	67, 71, 75, 79, 84, 89, 94, 100,
+	106, 112, 119, 126, 133, 141, 150, 158,
+	168, 178, 188, 200, 211, 224, 237, 251
+};
+
+/*
  * sgtl5000 has 3 internal power supplies:
  * 1. VAG, normally set to vdda/2
  * 2. charge pump, set to different value
@@ -1111,6 +1123,10 @@ static int sgtl5000_set_power_regs(struct snd_soc_codec *codec)
 	u16 ana_pwr;
 	u16 lreg_ctrl;
 	int vag;
+	int lo_vag;
+	int vol_quot;
+	int lo_vol;
+	size_t i;
 	struct sgtl5000_priv *sgtl5000 = snd_soc_codec_get_drvdata(codec);
 
 	vdda  = regulator_get_voltage(sgtl5000->supplies[VDDA].consumer);
@@ -1198,23 +1214,45 @@ static int sgtl5000_set_power_regs(struct snd_soc_codec *codec)
 			SGTL5000_ANA_GND_MASK, vag << SGTL5000_ANA_GND_SHIFT);
 
 	/* set line out VAG to vddio / 2, in range (0.8v, 1.675v) */
-	vag = vddio / 2;
-	if (vag <= SGTL5000_LINE_OUT_GND_BASE)
-		vag = 0;
-	else if (vag >= SGTL5000_LINE_OUT_GND_BASE +
+	lo_vag = vddio / 2;
+	if (lo_vag <= SGTL5000_LINE_OUT_GND_BASE)
+		lo_vag = 0;
+	else if (lo_vag >= SGTL5000_LINE_OUT_GND_BASE +
 		SGTL5000_LINE_OUT_GND_STP * SGTL5000_LINE_OUT_GND_MAX)
-		vag = SGTL5000_LINE_OUT_GND_MAX;
+		lo_vag = SGTL5000_LINE_OUT_GND_MAX;
 	else
-		vag = (vag - SGTL5000_LINE_OUT_GND_BASE) /
+		lo_vag = (lo_vag - SGTL5000_LINE_OUT_GND_BASE) /
 		    SGTL5000_LINE_OUT_GND_STP;
 
 	snd_soc_update_bits(codec, SGTL5000_CHIP_LINE_OUT_CTRL,
 			SGTL5000_LINE_OUT_CURRENT_MASK |
 			SGTL5000_LINE_OUT_GND_MASK,
-			vag << SGTL5000_LINE_OUT_GND_SHIFT |
+			lo_vag << SGTL5000_LINE_OUT_GND_SHIFT |
 			SGTL5000_LINE_OUT_CURRENT_360u <<
 				SGTL5000_LINE_OUT_CURRENT_SHIFT);
 
+	/*
+	 * Set lineout output level in range (0..31)
+	 * the same value is used for right and left channel
+	 *
+	 * Searching for a suitable index solving this formula:
+	 * idx = 40 * log10(vag_val / lo_cagcntrl) + 15
+	 */
+	vol_quot = (vag * 100) / lo_vag;
+	lo_vol = 0;
+	for (i = 0; i < ARRAY_SIZE(vol_quot_table); i++) {
+		if (vol_quot >= vol_quot_table[i])
+			lo_vol = i;
+		else
+			break;
+	}
+
+	snd_soc_update_bits(codec, SGTL5000_CHIP_LINE_OUT_VOL,
+		SGTL5000_LINE_OUT_VOL_RIGHT_MASK |
+		SGTL5000_LINE_OUT_VOL_LEFT_MASK,
+		lo_vol << SGTL5000_LINE_OUT_VOL_RIGHT_SHIFT |
+		lo_vol << SGTL5000_LINE_OUT_VOL_LEFT_SHIFT);
+
 	return 0;
 }
 
diff --git a/sound/soc/codecs/sirf-audio-codec.c b/sound/soc/codecs/sirf-audio-codec.c
index 0a8e43c98a07..29cb44256044 100644
--- a/sound/soc/codecs/sirf-audio-codec.c
+++ b/sound/soc/codecs/sirf-audio-codec.c
@@ -395,7 +395,7 @@ struct snd_soc_dai_driver sirf_audio_codec_dai = {
 
 static int sirf_audio_codec_probe(struct snd_soc_codec *codec)
 {
-	struct snd_soc_dapm_context *dapm = &codec->dapm;
+	struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
 
 	pm_runtime_enable(codec->dev);
 
diff --git a/sound/soc/codecs/sn95031.c b/sound/soc/codecs/sn95031.c
index 7947c0ebb1ed..3a7de0159f24 100644
--- a/sound/soc/codecs/sn95031.c
+++ b/sound/soc/codecs/sn95031.c
@@ -194,7 +194,7 @@ static int sn95031_set_vaud_bias(struct snd_soc_codec *codec,
 		break;
 
 	case SND_SOC_BIAS_PREPARE:
-		if (codec->dapm.bias_level == SND_SOC_BIAS_STANDBY) {
+		if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_STANDBY) {
 			pr_debug("vaud_bias powering up pll\n");
 			/* power up the pll */
 			snd_soc_write(codec, SN95031_AUDPLLCTRL, BIT(5));
@@ -205,17 +205,22 @@ static int sn95031_set_vaud_bias(struct snd_soc_codec *codec,
 		break;
 
 	case SND_SOC_BIAS_STANDBY:
-		if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) {
+		switch (snd_soc_codec_get_bias_level(codec)) {
+		case SND_SOC_BIAS_OFF:
 			pr_debug("vaud_bias power up rail\n");
 			/* power up the rail */
 			snd_soc_write(codec, SN95031_VAUD,
 					BIT(2)|BIT(1)|BIT(0));
 			msleep(1);
-		} else if (codec->dapm.bias_level == SND_SOC_BIAS_PREPARE) {
+			break;
+		case SND_SOC_BIAS_PREPARE:
 			/* turn off pcm */
 			pr_debug("vaud_bias power dn pcm\n");
 			snd_soc_update_bits(codec, SN95031_PCM2C2, BIT(0), 0);
 			snd_soc_write(codec, SN95031_AUDPLLCTRL, 0);
+			break;
+		default:
+			break;
 		}
 		break;
 
@@ -226,7 +231,6 @@ static int sn95031_set_vaud_bias(struct snd_soc_codec *codec,
 		break;
 	}
 
-	codec->dapm.bias_level = level;
 	return 0;
 }
 
diff --git a/sound/soc/codecs/ssm2518.c b/sound/soc/codecs/ssm2518.c
index 67ea55adb307..f30de7639bb9 100644
--- a/sound/soc/codecs/ssm2518.c
+++ b/sound/soc/codecs/ssm2518.c
@@ -510,7 +510,7 @@ static int ssm2518_set_bias_level(struct snd_soc_codec *codec,
 	case SND_SOC_BIAS_PREPARE:
 		break;
 	case SND_SOC_BIAS_STANDBY:
-		if (codec->dapm.bias_level == SND_SOC_BIAS_OFF)
+		if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF)
 			ret = ssm2518_set_power(ssm2518, true);
 		break;
 	case SND_SOC_BIAS_OFF:
@@ -518,12 +518,7 @@ static int ssm2518_set_bias_level(struct snd_soc_codec *codec,
 		break;
 	}
 
-	if (ret)
-		return ret;
-
-	codec->dapm.bias_level = level;
-
-	return 0;
+	return ret;
 }
 
 static int ssm2518_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask,
diff --git a/sound/soc/codecs/ssm2602.c b/sound/soc/codecs/ssm2602.c
index 314eaece1b7d..69a773aeb13d 100644
--- a/sound/soc/codecs/ssm2602.c
+++ b/sound/soc/codecs/ssm2602.c
@@ -473,7 +473,6 @@ static int ssm2602_set_bias_level(struct snd_soc_codec *codec,
 		break;
 
 	}
-	codec->dapm.bias_level = level;
 	return 0;
 }
 
@@ -524,8 +523,8 @@ static int ssm2602_resume(struct snd_soc_codec *codec)
 
 static int ssm2602_codec_probe(struct snd_soc_codec *codec)
 {
+	struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
 	struct ssm2602_priv *ssm2602 = snd_soc_codec_get_drvdata(codec);
-	struct snd_soc_dapm_context *dapm = &codec->dapm;
 	int ret;
 
 	regmap_update_bits(ssm2602->regmap, SSM2602_LOUT1V,
@@ -549,7 +548,7 @@ static int ssm2602_codec_probe(struct snd_soc_codec *codec)
 
 static int ssm2604_codec_probe(struct snd_soc_codec *codec)
 {
-	struct snd_soc_dapm_context *dapm = &codec->dapm;
+	struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
 	int ret;
 
 	ret = snd_soc_dapm_new_controls(dapm, ssm2604_dapm_widgets,
diff --git a/sound/soc/codecs/ssm4567.c b/sound/soc/codecs/ssm4567.c
index a984485108cd..938d2cb6d78b 100644
--- a/sound/soc/codecs/ssm4567.c
+++ b/sound/soc/codecs/ssm4567.c
@@ -353,7 +353,7 @@ static int ssm4567_set_bias_level(struct snd_soc_codec *codec,
 	case SND_SOC_BIAS_PREPARE:
 		break;
 	case SND_SOC_BIAS_STANDBY:
-		if (codec->dapm.bias_level == SND_SOC_BIAS_OFF)
+		if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF)
 			ret = ssm4567_set_power(ssm4567, true);
 		break;
 	case SND_SOC_BIAS_OFF:
@@ -361,12 +361,7 @@ static int ssm4567_set_bias_level(struct snd_soc_codec *codec,
 		break;
 	}
 
-	if (ret)
-		return ret;
-
-	codec->dapm.bias_level = level;
-
-	return 0;
+	return ret;
 }
 
 static const struct snd_soc_dai_ops ssm4567_dai_ops = {
diff --git a/sound/soc/codecs/sta32x.c b/sound/soc/codecs/sta32x.c
index 007a0e3bc273..60eff36260cb 100644
--- a/sound/soc/codecs/sta32x.c
+++ b/sound/soc/codecs/sta32x.c
@@ -819,7 +819,7 @@ static int sta32x_set_bias_level(struct snd_soc_codec *codec,
 		break;
 
 	case SND_SOC_BIAS_STANDBY:
-		if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) {
+		if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) {
 			ret = regulator_bulk_enable(ARRAY_SIZE(sta32x->supplies),
 						    sta32x->supplies);
 			if (ret != 0) {
@@ -854,7 +854,6 @@ static int sta32x_set_bias_level(struct snd_soc_codec *codec,
 				       sta32x->supplies);
 		break;
 	}
-	codec->dapm.bias_level = level;
 	return 0;
 }
 
@@ -970,7 +969,7 @@ static int sta32x_probe(struct snd_soc_codec *codec)
 	if (sta32x->pdata->needs_esd_watchdog)
 		INIT_DELAYED_WORK(&sta32x->watchdog_work, sta32x_watchdog);
 
-	sta32x_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+	snd_soc_codec_force_bias_level(codec, SND_SOC_BIAS_STANDBY);
 	/* Bias level configuration will have done an extra enable */
 	regulator_bulk_disable(ARRAY_SIZE(sta32x->supplies), sta32x->supplies);
 
@@ -1096,16 +1095,10 @@ static int sta32x_i2c_probe(struct i2c_client *i2c,
 #endif
 
 	/* GPIOs */
-	sta32x->gpiod_nreset = devm_gpiod_get(dev, "reset");
-	if (IS_ERR(sta32x->gpiod_nreset)) {
-		ret = PTR_ERR(sta32x->gpiod_nreset);
-		if (ret != -ENOENT && ret != -ENOSYS)
-			return ret;
-
-		sta32x->gpiod_nreset = NULL;
-	} else {
-		gpiod_direction_output(sta32x->gpiod_nreset, 0);
-	}
+	sta32x->gpiod_nreset = devm_gpiod_get_optional(dev, "reset",
+						       GPIOD_OUT_LOW);
+	if (IS_ERR(sta32x->gpiod_nreset))
+		return PTR_ERR(sta32x->gpiod_nreset);
 
 	/* regulators */
 	for (i = 0; i < ARRAY_SIZE(sta32x->supplies); i++)
diff --git a/sound/soc/codecs/sta350.c b/sound/soc/codecs/sta350.c
index 669e3228241e..bd819a3f205a 100644
--- a/sound/soc/codecs/sta350.c
+++ b/sound/soc/codecs/sta350.c
@@ -853,7 +853,7 @@ static int sta350_set_bias_level(struct snd_soc_codec *codec,
 		break;
 
 	case SND_SOC_BIAS_STANDBY:
-		if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) {
+		if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) {
 			ret = regulator_bulk_enable(
 				ARRAY_SIZE(sta350->supplies),
 				sta350->supplies);
@@ -890,7 +890,6 @@ static int sta350_set_bias_level(struct snd_soc_codec *codec,
 				       sta350->supplies);
 		break;
 	}
-	codec->dapm.bias_level = level;
 	return 0;
 }
 
@@ -1037,7 +1036,7 @@ static int sta350_probe(struct snd_soc_codec *codec)
 	sta350->coef_shadow[60] = 0x400000;
 	sta350->coef_shadow[61] = 0x400000;
 
-	sta350_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+	snd_soc_codec_force_bias_level(codec, SND_SOC_BIAS_STANDBY);
 	/* Bias level configuration will have done an extra enable */
 	regulator_bulk_disable(ARRAY_SIZE(sta350->supplies), sta350->supplies);
 
@@ -1218,8 +1217,8 @@ static int sta350_i2c_probe(struct i2c_client *i2c,
 	if (IS_ERR(sta350->gpiod_nreset))
 		return PTR_ERR(sta350->gpiod_nreset);
 
-	sta350->gpiod_power_down = devm_gpiod_get(dev, "power-down",
-						  GPIOD_OUT_LOW);
+	sta350->gpiod_power_down = devm_gpiod_get_optional(dev, "power-down",
+							   GPIOD_OUT_LOW);
 	if (IS_ERR(sta350->gpiod_power_down))
 		return PTR_ERR(sta350->gpiod_power_down);
 
diff --git a/sound/soc/codecs/sta529.c b/sound/soc/codecs/sta529.c
index b0f436d10125..4f70378b2cfb 100644
--- a/sound/soc/codecs/sta529.c
+++ b/sound/soc/codecs/sta529.c
@@ -165,7 +165,7 @@ static int sta529_set_bias_level(struct snd_soc_codec *codec, enum
 				FFX_CLK_ENB);
 		break;
 	case SND_SOC_BIAS_STANDBY:
-		if (codec->dapm.bias_level == SND_SOC_BIAS_OFF)
+		if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF)
 			regcache_sync(sta529->regmap);
 		snd_soc_update_bits(codec, STA529_FFXCFG0,
 					POWER_CNTLMSAK, POWER_STDBY);
@@ -179,12 +179,6 @@ static int sta529_set_bias_level(struct snd_soc_codec *codec, enum
 		break;
 	}
 
-	/*
-	 * store the label for powers down audio subsystem for suspend.This is
-	 * used by soc core layer
-	 */
-	codec->dapm.bias_level = level;
-
 	return 0;
 
 }
diff --git a/sound/soc/codecs/stac9766.c b/sound/soc/codecs/stac9766.c
index 7f939aec5a7f..ed4cca7f6779 100644
--- a/sound/soc/codecs/stac9766.c
+++ b/sound/soc/codecs/stac9766.c
@@ -236,7 +236,6 @@ static int stac9766_set_bias_level(struct snd_soc_codec *codec,
 		stac9766_ac97_write(codec, AC97_POWERDOWN, 0xffff);
 		break;
 	}
-	codec->dapm.bias_level = level;
 	return 0;
 }
 
diff --git a/sound/soc/codecs/tas2552.c b/sound/soc/codecs/tas2552.c
index dfb4ff5cc9ea..891e2c529df3 100644
--- a/sound/soc/codecs/tas2552.c
+++ b/sound/soc/codecs/tas2552.c
@@ -34,6 +34,7 @@
 #include <sound/soc-dapm.h>
 #include <sound/tlv.h>
 #include <sound/tas2552-plat.h>
+#include <dt-bindings/sound/tas2552.h>
 
 #include "tas2552.h"
 
@@ -45,7 +46,7 @@ static struct reg_default tas2552_reg_defs[] = {
 	{TAS2552_PDM_CFG, 0x01},
 	{TAS2552_PGA_GAIN, 0x00},
 	{TAS2552_BOOST_PT_CTRL, 0x0f},
-	{TAS2552_RESERVED_0D, 0x00},
+	{TAS2552_RESERVED_0D, 0xbe},
 	{TAS2552_LIMIT_RATE_HYS, 0x08},
 	{TAS2552_CFG_2, 0xef},
 	{TAS2552_SER_CTRL_1, 0x00},
@@ -75,20 +76,45 @@ struct tas2552_data {
 	struct regulator_bulk_data supplies[TAS2552_NUM_SUPPLIES];
 	struct gpio_desc *enable_gpio;
 	unsigned char regs[TAS2552_VBAT_DATA];
-	unsigned int mclk;
-};
+	unsigned int pll_clkin;
+	unsigned int pdm_clk;
 
-/* Input mux controls */
-static const char *tas2552_input_texts[] = {
-	"Digital", "Analog"
+	unsigned int dai_fmt;
+	unsigned int tdm_delay;
 };
 
+static int tas2552_post_event(struct snd_soc_dapm_widget *w,
+			      struct snd_kcontrol *kcontrol, int event)
+{
+	struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+
+	switch (event) {
+	case SND_SOC_DAPM_POST_PMU:
+		snd_soc_write(codec, TAS2552_RESERVED_0D, 0xc0);
+		snd_soc_update_bits(codec, TAS2552_LIMIT_RATE_HYS, (1 << 5),
+				    (1 << 5));
+		snd_soc_update_bits(codec, TAS2552_CFG_2, 1, 0);
+		snd_soc_update_bits(codec, TAS2552_CFG_1, TAS2552_SWS, 0);
+		break;
+	case SND_SOC_DAPM_POST_PMD:
+		snd_soc_update_bits(codec, TAS2552_CFG_1, TAS2552_SWS,
+				    TAS2552_SWS);
+		snd_soc_update_bits(codec, TAS2552_CFG_2, 1, 1);
+		snd_soc_update_bits(codec, TAS2552_LIMIT_RATE_HYS, (1 << 5), 0);
+		snd_soc_write(codec, TAS2552_RESERVED_0D, 0xbe);
+		break;
+	}
+	return 0;
+}
+
+/* Input mux controls */
+static const char * const tas2552_input_texts[] = {
+	"Digital", "Analog" };
 static SOC_ENUM_SINGLE_DECL(tas2552_input_mux_enum, TAS2552_CFG_3, 7,
 			    tas2552_input_texts);
 
-static const struct snd_kcontrol_new tas2552_input_mux_control[] = {
-	SOC_DAPM_ENUM("Input selection", tas2552_input_mux_enum)
-};
+static const struct snd_kcontrol_new tas2552_input_mux_control =
+	SOC_DAPM_ENUM("Route", tas2552_input_mux_enum);
 
 static const struct snd_soc_dapm_widget tas2552_dapm_widgets[] =
 {
@@ -96,12 +122,13 @@ static const struct snd_soc_dapm_widget tas2552_dapm_widgets[] =
 
 	/* MUX Controls */
 	SND_SOC_DAPM_MUX("Input selection", SND_SOC_NOPM, 0, 0,
-				tas2552_input_mux_control),
+			 &tas2552_input_mux_control),
 
 	SND_SOC_DAPM_AIF_IN("DAC IN", "DAC Playback", 0, SND_SOC_NOPM, 0, 0),
 	SND_SOC_DAPM_DAC("DAC", NULL, SND_SOC_NOPM, 0, 0),
 	SND_SOC_DAPM_OUT_DRV("ClassD", TAS2552_CFG_2, 7, 0, NULL, 0),
 	SND_SOC_DAPM_SUPPLY("PLL", TAS2552_CFG_2, 3, 0, NULL, 0),
+	SND_SOC_DAPM_POST("Post Event", tas2552_post_event),
 
 	SND_SOC_DAPM_OUTPUT("OUT")
 };
@@ -118,15 +145,16 @@ static const struct snd_soc_dapm_route tas2552_audio_map[] = {
 #ifdef CONFIG_PM
 static void tas2552_sw_shutdown(struct tas2552_data *tas_data, int sw_shutdown)
 {
-	u8 cfg1_reg;
+	u8 cfg1_reg = 0;
+
+	if (!tas_data->codec)
+		return;
 
 	if (sw_shutdown)
-		cfg1_reg = 0;
-	else
-		cfg1_reg = TAS2552_SWS_MASK;
+		cfg1_reg = TAS2552_SWS;
 
-	snd_soc_update_bits(tas_data->codec, TAS2552_CFG_1,
-						 TAS2552_SWS_MASK, cfg1_reg);
+	snd_soc_update_bits(tas_data->codec, TAS2552_CFG_1, TAS2552_SWS,
+			    cfg1_reg);
 }
 #endif
 
@@ -138,15 +166,92 @@ static int tas2552_hw_params(struct snd_pcm_substream *substream,
 	struct tas2552_data *tas2552 = dev_get_drvdata(codec->dev);
 	int sample_rate, pll_clk;
 	int d;
+	int cpf;
 	u8 p, j;
+	u8 ser_ctrl1_reg, wclk_rate;
 
-	if (!tas2552->mclk)
+	switch (params_width(params)) {
+	case 16:
+		ser_ctrl1_reg = TAS2552_WORDLENGTH_16BIT;
+		cpf = 32 + tas2552->tdm_delay;
+		break;
+	case 20:
+		ser_ctrl1_reg = TAS2552_WORDLENGTH_20BIT;
+		cpf = 64 + tas2552->tdm_delay;
+		break;
+	case 24:
+		ser_ctrl1_reg = TAS2552_WORDLENGTH_24BIT;
+		cpf = 64 + tas2552->tdm_delay;
+		break;
+	case 32:
+		ser_ctrl1_reg = TAS2552_WORDLENGTH_32BIT;
+		cpf = 64 + tas2552->tdm_delay;
+		break;
+	default:
+		dev_err(codec->dev, "Not supported sample size: %d\n",
+			params_width(params));
+		return -EINVAL;
+	}
+
+	if (cpf <= 32)
+		ser_ctrl1_reg |= TAS2552_CLKSPERFRAME_32;
+	else if (cpf <= 64)
+		ser_ctrl1_reg |= TAS2552_CLKSPERFRAME_64;
+	else if (cpf <= 128)
+		ser_ctrl1_reg |= TAS2552_CLKSPERFRAME_128;
+	else
+		ser_ctrl1_reg |= TAS2552_CLKSPERFRAME_256;
+
+	snd_soc_update_bits(codec, TAS2552_SER_CTRL_1,
+			    TAS2552_WORDLENGTH_MASK | TAS2552_CLKSPERFRAME_MASK,
+			    ser_ctrl1_reg);
+
+	switch (params_rate(params)) {
+	case 8000:
+		wclk_rate = TAS2552_WCLK_FREQ_8KHZ;
+		break;
+	case 11025:
+	case 12000:
+		wclk_rate = TAS2552_WCLK_FREQ_11_12KHZ;
+		break;
+	case 16000:
+		wclk_rate = TAS2552_WCLK_FREQ_16KHZ;
+		break;
+	case 22050:
+	case 24000:
+		wclk_rate = TAS2552_WCLK_FREQ_22_24KHZ;
+		break;
+	case 32000:
+		wclk_rate = TAS2552_WCLK_FREQ_32KHZ;
+		break;
+	case 44100:
+	case 48000:
+		wclk_rate = TAS2552_WCLK_FREQ_44_48KHZ;
+		break;
+	case 88200:
+	case 96000:
+		wclk_rate = TAS2552_WCLK_FREQ_88_96KHZ;
+		break;
+	case 176400:
+	case 192000:
+		wclk_rate = TAS2552_WCLK_FREQ_176_192KHZ;
+		break;
+	default:
+		dev_err(codec->dev, "Not supported sample rate: %d\n",
+			params_rate(params));
+		return -EINVAL;
+	}
+
+	snd_soc_update_bits(codec, TAS2552_CFG_3, TAS2552_WCLK_FREQ_MASK,
+			    wclk_rate);
+
+	if (!tas2552->pll_clkin)
 		return -EINVAL;
 
 	snd_soc_update_bits(codec, TAS2552_CFG_2, TAS2552_PLL_ENABLE, 0);
 
-	if (tas2552->mclk == TAS2552_245MHZ_CLK ||
-		tas2552->mclk == TAS2552_225MHZ_CLK) {
+	if (tas2552->pll_clkin == TAS2552_245MHZ_CLK ||
+	    tas2552->pll_clkin == TAS2552_225MHZ_CLK) {
 		/* By pass the PLL configuration */
 		snd_soc_update_bits(codec, TAS2552_PLL_CTRL_2,
 				    TAS2552_PLL_BYPASS_MASK,
@@ -170,8 +275,8 @@ static int tas2552_hw_params(struct snd_pcm_substream *substream,
 			return -EINVAL;
 		}
 
-		j = (pll_clk * 2 * (1 << p)) / tas2552->mclk;
-		d = (pll_clk * 2 * (1 << p)) % tas2552->mclk;
+		j = (pll_clk * 2 * (1 << p)) / tas2552->pll_clkin;
+		d = (pll_clk * 2 * (1 << p)) % tas2552->pll_clkin;
 
 		snd_soc_update_bits(codec, TAS2552_PLL_CTRL_1,
 				TAS2552_PLL_J_MASK, j);
@@ -185,56 +290,74 @@ static int tas2552_hw_params(struct snd_pcm_substream *substream,
 	return 0;
 }
 
+#define TAS2552_DAI_FMT_MASK	(TAS2552_BCLKDIR | \
+				 TAS2552_WCLKDIR | \
+				 TAS2552_DATAFORMAT_MASK)
+static int tas2552_prepare(struct snd_pcm_substream *substream,
+			   struct snd_soc_dai *dai)
+{
+	struct snd_soc_codec *codec = dai->codec;
+	struct tas2552_data *tas2552 = snd_soc_codec_get_drvdata(codec);
+	int delay = 0;
+
+	/* TDM slot selection only valid in DSP_A/_B mode */
+	if (tas2552->dai_fmt == SND_SOC_DAIFMT_DSP_A)
+		delay += (tas2552->tdm_delay + 1);
+	else if (tas2552->dai_fmt == SND_SOC_DAIFMT_DSP_B)
+		delay += tas2552->tdm_delay;
+
+	/* Configure data delay */
+	snd_soc_write(codec, TAS2552_SER_CTRL_2, delay);
+
+	return 0;
+}
+
 static int tas2552_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
 {
 	struct snd_soc_codec *codec = dai->codec;
+	struct tas2552_data *tas2552 = dev_get_drvdata(codec->dev);
 	u8 serial_format;
-	u8 serial_control_mask;
 
 	switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
 	case SND_SOC_DAIFMT_CBS_CFS:
 		serial_format = 0x00;
 		break;
 	case SND_SOC_DAIFMT_CBS_CFM:
-		serial_format = TAS2552_WORD_CLK_MASK;
+		serial_format = TAS2552_WCLKDIR;
 		break;
 	case SND_SOC_DAIFMT_CBM_CFS:
-		serial_format = TAS2552_BIT_CLK_MASK;
+		serial_format = TAS2552_BCLKDIR;
 		break;
 	case SND_SOC_DAIFMT_CBM_CFM:
-		serial_format = (TAS2552_BIT_CLK_MASK | TAS2552_WORD_CLK_MASK);
+		serial_format = (TAS2552_BCLKDIR | TAS2552_WCLKDIR);
 		break;
 	default:
 		dev_vdbg(codec->dev, "DAI Format master is not found\n");
 		return -EINVAL;
 	}
 
-	serial_control_mask = TAS2552_BIT_CLK_MASK | TAS2552_WORD_CLK_MASK;
-
-	switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
-	case SND_SOC_DAIFMT_I2S:
-		serial_format &= TAS2552_DAIFMT_I2S_MASK;
+	switch (fmt & (SND_SOC_DAIFMT_FORMAT_MASK |
+		       SND_SOC_DAIFMT_INV_MASK)) {
+	case (SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF):
 		break;
-	case SND_SOC_DAIFMT_DSP_A:
-		serial_format |= TAS2552_DAIFMT_DSP;
+	case (SND_SOC_DAIFMT_DSP_A | SND_SOC_DAIFMT_IB_NF):
+	case (SND_SOC_DAIFMT_DSP_B | SND_SOC_DAIFMT_IB_NF):
+		serial_format |= TAS2552_DATAFORMAT_DSP;
 		break;
-	case SND_SOC_DAIFMT_RIGHT_J:
-		serial_format |= TAS2552_DAIFMT_RIGHT_J;
+	case (SND_SOC_DAIFMT_RIGHT_J | SND_SOC_DAIFMT_NB_NF):
+		serial_format |= TAS2552_DATAFORMAT_RIGHT_J;
 		break;
-	case SND_SOC_DAIFMT_LEFT_J:
-		serial_format |= TAS2552_DAIFMT_LEFT_J;
+	case (SND_SOC_DAIFMT_LEFT_J | SND_SOC_DAIFMT_NB_NF):
+		serial_format |= TAS2552_DATAFORMAT_LEFT_J;
 		break;
 	default:
 		dev_vdbg(codec->dev, "DAI Format is not found\n");
 		return -EINVAL;
 	}
+	tas2552->dai_fmt = fmt & SND_SOC_DAIFMT_FORMAT_MASK;
 
-	if (fmt & SND_SOC_DAIFMT_FORMAT_MASK)
-		serial_control_mask |= TAS2552_DATA_FORMAT_MASK;
-
-	snd_soc_update_bits(codec, TAS2552_SER_CTRL_1, serial_control_mask,
-						serial_format);
-
+	snd_soc_update_bits(codec, TAS2552_SER_CTRL_1, TAS2552_DAI_FMT_MASK,
+			    serial_format);
 	return 0;
 }
 
@@ -243,23 +366,75 @@ static int tas2552_set_dai_sysclk(struct snd_soc_dai *dai, int clk_id,
 {
 	struct snd_soc_codec *codec = dai->codec;
 	struct tas2552_data *tas2552 = dev_get_drvdata(codec->dev);
+	u8 reg, mask, val;
+
+	switch (clk_id) {
+	case TAS2552_PLL_CLKIN_MCLK:
+	case TAS2552_PLL_CLKIN_BCLK:
+	case TAS2552_PLL_CLKIN_IVCLKIN:
+	case TAS2552_PLL_CLKIN_1_8_FIXED:
+		mask = TAS2552_PLL_SRC_MASK;
+		val = (clk_id << 3) & mask; /* bit 4:5 in the register */
+		reg = TAS2552_CFG_1;
+		tas2552->pll_clkin = freq;
+		break;
+	case TAS2552_PDM_CLK_PLL:
+	case TAS2552_PDM_CLK_IVCLKIN:
+	case TAS2552_PDM_CLK_BCLK:
+	case TAS2552_PDM_CLK_MCLK:
+		mask = TAS2552_PDM_CLK_SEL_MASK;
+		val = (clk_id >> 1) & mask; /* bit 0:1 in the register */
+		reg = TAS2552_PDM_CFG;
+		tas2552->pdm_clk = freq;
+		break;
+	default:
+		dev_err(codec->dev, "Invalid clk id: %d\n", clk_id);
+		return -EINVAL;
+	}
+
+	snd_soc_update_bits(codec, reg, mask, val);
+
+	return 0;
+}
+
+static int tas2552_set_dai_tdm_slot(struct snd_soc_dai *dai,
+				    unsigned int tx_mask, unsigned int rx_mask,
+				    int slots, int slot_width)
+{
+	struct snd_soc_codec *codec = dai->codec;
+	struct tas2552_data *tas2552 = snd_soc_codec_get_drvdata(codec);
+	unsigned int lsb;
+
+	if (unlikely(!tx_mask)) {
+		dev_err(codec->dev, "tx masks need to be non 0\n");
+		return -EINVAL;
+	}
+
+	/* TDM based on DSP mode requires slots to be adjacent */
+	lsb = __ffs(tx_mask);
+	if ((lsb + 1) != __fls(tx_mask)) {
+		dev_err(codec->dev, "Invalid mask, slots must be adjacent\n");
+		return -EINVAL;
+	}
+
+	tas2552->tdm_delay = lsb * slot_width;
 
-	tas2552->mclk = freq;
+	/* DOUT in high-impedance on inactive bit clocks */
+	snd_soc_update_bits(codec, TAS2552_DOUT,
+			    TAS2552_SDOUT_TRISTATE, TAS2552_SDOUT_TRISTATE);
 
 	return 0;
 }
 
 static int tas2552_mute(struct snd_soc_dai *dai, int mute)
 {
-	u8 cfg1_reg;
+	u8 cfg1_reg = 0;
 	struct snd_soc_codec *codec = dai->codec;
 
 	if (mute)
-		cfg1_reg = TAS2552_MUTE_MASK;
-	else
-		cfg1_reg = ~TAS2552_MUTE_MASK;
+		cfg1_reg |= TAS2552_MUTE;
 
-	snd_soc_update_bits(codec, TAS2552_CFG_1, TAS2552_MUTE_MASK, cfg1_reg);
+	snd_soc_update_bits(codec, TAS2552_CFG_1, TAS2552_MUTE, cfg1_reg);
 
 	return 0;
 }
@@ -269,7 +444,7 @@ static int tas2552_runtime_suspend(struct device *dev)
 {
 	struct tas2552_data *tas2552 = dev_get_drvdata(dev);
 
-	tas2552_sw_shutdown(tas2552, 0);
+	tas2552_sw_shutdown(tas2552, 1);
 
 	regcache_cache_only(tas2552->regmap, true);
 	regcache_mark_dirty(tas2552->regmap);
@@ -287,7 +462,7 @@ static int tas2552_runtime_resume(struct device *dev)
 	if (tas2552->enable_gpio)
 		gpiod_set_value(tas2552->enable_gpio, 1);
 
-	tas2552_sw_shutdown(tas2552, 1);
+	tas2552_sw_shutdown(tas2552, 0);
 
 	regcache_cache_only(tas2552->regmap, false);
 	regcache_sync(tas2552->regmap);
@@ -303,8 +478,10 @@ static const struct dev_pm_ops tas2552_pm = {
 
 static struct snd_soc_dai_ops tas2552_speaker_dai_ops = {
 	.hw_params	= tas2552_hw_params,
+	.prepare	= tas2552_prepare,
 	.set_sysclk	= tas2552_set_dai_sysclk,
 	.set_fmt	= tas2552_set_dai_fmt,
+	.set_tdm_slot	= tas2552_set_dai_tdm_slot,
 	.digital_mute = tas2552_mute,
 };
 
@@ -330,16 +507,11 @@ static struct snd_soc_dai_driver tas2552_dai[] = {
 /*
  * DAC digital volumes. From -7 to 24 dB in 1 dB steps
  */
-static DECLARE_TLV_DB_SCALE(dac_tlv, -7, 100, 24);
+static DECLARE_TLV_DB_SCALE(dac_tlv, -7, 100, 0);
 
 static const struct snd_kcontrol_new tas2552_snd_controls[] = {
 	SOC_SINGLE_TLV("Speaker Driver Playback Volume",
-			 TAS2552_PGA_GAIN, 0, 0x1f, 1, dac_tlv),
-	SOC_DAPM_SINGLE("Playback AMP", SND_SOC_NOPM, 0, 1, 0),
-};
-
-static const struct reg_default tas2552_init_regs[] = {
-	{ TAS2552_RESERVED_0D, 0xc0 },
+			 TAS2552_PGA_GAIN, 0, 0x1f, 0, dac_tlv),
 };
 
 static int tas2552_codec_probe(struct snd_soc_codec *codec)
@@ -368,31 +540,19 @@ static int tas2552_codec_probe(struct snd_soc_codec *codec)
 		goto probe_fail;
 	}
 
-	snd_soc_write(codec, TAS2552_CFG_1, TAS2552_MUTE_MASK |
-				TAS2552_PLL_SRC_BCLK);
+	snd_soc_update_bits(codec, TAS2552_CFG_1, TAS2552_MUTE, TAS2552_MUTE);
 	snd_soc_write(codec, TAS2552_CFG_3, TAS2552_I2S_OUT_SEL |
-				TAS2552_DIN_SRC_SEL_AVG_L_R | TAS2552_88_96KHZ);
+					    TAS2552_DIN_SRC_SEL_AVG_L_R);
 	snd_soc_write(codec, TAS2552_DOUT, TAS2552_PDM_DATA_I);
 	snd_soc_write(codec, TAS2552_OUTPUT_DATA, TAS2552_PDM_DATA_V_I | 0x8);
-	snd_soc_write(codec, TAS2552_PDM_CFG, TAS2552_PDM_BCLK_SEL);
 	snd_soc_write(codec, TAS2552_BOOST_PT_CTRL, TAS2552_APT_DELAY_200 |
 				TAS2552_APT_THRESH_2_1_7);
 
-	ret = regmap_register_patch(tas2552->regmap, tas2552_init_regs,
-					    ARRAY_SIZE(tas2552_init_regs));
-	if (ret != 0) {
-		dev_err(codec->dev, "Failed to write init registers: %d\n",
-			ret);
-		goto patch_fail;
-	}
-
 	snd_soc_write(codec, TAS2552_CFG_2, TAS2552_BOOST_EN |
 				  TAS2552_APT_EN | TAS2552_LIM_EN);
 
 	return 0;
 
-patch_fail:
-	pm_runtime_put(codec->dev);
 probe_fail:
 	if (tas2552->enable_gpio)
 		gpiod_set_value(tas2552->enable_gpio, 0);
@@ -454,6 +614,8 @@ static struct snd_soc_codec_driver soc_codec_dev_tas2552 = {
 	.remove = tas2552_codec_remove,
 	.suspend =	tas2552_suspend,
 	.resume = tas2552_resume,
+	.ignore_pmdown_time = true,
+
 	.controls = tas2552_snd_controls,
 	.num_controls = ARRAY_SIZE(tas2552_snd_controls),
 	.dapm_widgets = tas2552_dapm_widgets,
@@ -486,8 +648,12 @@ static int tas2552_probe(struct i2c_client *client,
 		return -ENOMEM;
 
 	data->enable_gpio = devm_gpiod_get(dev, "enable", GPIOD_OUT_LOW);
-	if (IS_ERR(data->enable_gpio))
-		return PTR_ERR(data->enable_gpio);
+	if (IS_ERR(data->enable_gpio)) {
+		if (PTR_ERR(data->enable_gpio) == -EPROBE_DEFER)
+			return -EPROBE_DEFER;
+
+		data->enable_gpio = NULL;;
+	}
 
 	data->tas2552_client = client;
 	data->regmap = devm_regmap_init_i2c(client, &tas2552_regmap_config);
diff --git a/sound/soc/codecs/tas2552.h b/sound/soc/codecs/tas2552.h
index 6cea8f31bf88..bbb820495516 100644
--- a/sound/soc/codecs/tas2552.h
+++ b/sound/soc/codecs/tas2552.h
@@ -45,10 +45,14 @@
 #define TAS2552_MAX_REG			0x20
 
 /* CFG1 Register Masks */
-#define TAS2552_MUTE_MASK		(1 << 2)
-#define TAS2552_SWS_MASK		(1 << 1)
-#define TAS2552_WCLK_MASK		0x07
-#define TAS2552_CLASSD_EN_MASK	(1 << 7)
+#define TAS2552_DEV_RESET		(1 << 0)
+#define TAS2552_SWS			(1 << 1)
+#define TAS2552_MUTE			(1 << 2)
+#define TAS2552_PLL_SRC_MCLK		(0x0 << 4)
+#define TAS2552_PLL_SRC_BCLK		(0x1 << 4)
+#define TAS2552_PLL_SRC_IVCLKIN		(0x2 << 4)
+#define TAS2552_PLL_SRC_1_8_FIXED 	(0x3 << 4)
+#define TAS2552_PLL_SRC_MASK	 	TAS2552_PLL_SRC_1_8_FIXED
 
 /* CFG2 Register Masks */
 #define TAS2552_CLASSD_EN		(1 << 7)
@@ -59,38 +63,44 @@
 #define TAS2552_IVSENSE_EN		(1 << 1)
 
 /* CFG3 Register Masks */
-#define TAS2552_WORD_CLK_MASK		(1 << 7)
-#define TAS2552_BIT_CLK_MASK		(1 << 6)
-#define TAS2552_DATA_FORMAT_MASK	(0x11 << 2)
-
-#define TAS2552_DAIFMT_I2S_MASK		0xf3
-#define TAS2552_DAIFMT_DSP			(1 << 3)
-#define TAS2552_DAIFMT_RIGHT_J		(1 << 4)
-#define TAS2552_DAIFMT_LEFT_J		(0x11 << 3)
-
-#define TAS2552_PLL_SRC_MCLK	0x00
-#define TAS2552_PLL_SRC_BCLK	(1 << 3)
-#define TAS2552_PLL_SRC_IVCLKIN	(1 << 4)
-#define TAS2552_PLL_SRC_1_8_FIXED (0x11 << 3)
-
-#define TAS2552_DIN_SRC_SEL_MUTED	0x00
-#define TAS2552_DIN_SRC_SEL_LEFT	(1 << 4)
-#define TAS2552_DIN_SRC_SEL_RIGHT	(1 << 5)
-#define TAS2552_DIN_SRC_SEL_AVG_L_R	(0x11 << 4)
-
+#define TAS2552_WCLK_FREQ_8KHZ		(0x0 << 0)
+#define TAS2552_WCLK_FREQ_11_12KHZ	(0x1 << 0)
+#define TAS2552_WCLK_FREQ_16KHZ		(0x2 << 0)
+#define TAS2552_WCLK_FREQ_22_24KHZ	(0x3 << 0)
+#define TAS2552_WCLK_FREQ_32KHZ		(0x4 << 0)
+#define TAS2552_WCLK_FREQ_44_48KHZ	(0x5 << 0)
+#define TAS2552_WCLK_FREQ_88_96KHZ	(0x6 << 0)
+#define TAS2552_WCLK_FREQ_176_192KHZ	(0x7 << 0)
+#define TAS2552_WCLK_FREQ_MASK		TAS2552_WCLK_FREQ_176_192KHZ
+#define TAS2552_DIN_SRC_SEL_MUTED	(0x0 << 3)
+#define TAS2552_DIN_SRC_SEL_LEFT	(0x1 << 3)
+#define TAS2552_DIN_SRC_SEL_RIGHT	(0x2 << 3)
+#define TAS2552_DIN_SRC_SEL_AVG_L_R	(0x3 << 3)
 #define TAS2552_PDM_IN_SEL		(1 << 5)
 #define TAS2552_I2S_OUT_SEL		(1 << 6)
-#define TAS2552_ANALOG_IN_SEL	(1 << 7)
-
-/* CFG3 WCLK Dividers */
-#define TAS2552_8KHZ		0x00
-#define TAS2552_11_12KHZ	(1 << 1)
-#define TAS2552_16KHZ		(1 << 2)
-#define TAS2552_22_24KHZ	(1 << 3)
-#define TAS2552_32KHZ		(1 << 4)
-#define TAS2552_44_48KHZ	(1 << 5)
-#define TAS2552_88_96KHZ	(1 << 6)
-#define TAS2552_176_192KHZ	(1 << 7)
+#define TAS2552_ANALOG_IN_SEL		(1 << 7)
+
+/* DOUT Register Masks */
+#define TAS2552_SDOUT_TRISTATE		(1 << 2)
+
+/* Serial Interface Control Register Masks */
+#define TAS2552_WORDLENGTH_16BIT	(0x0 << 0)
+#define TAS2552_WORDLENGTH_20BIT	(0x1 << 0)
+#define TAS2552_WORDLENGTH_24BIT	(0x2 << 0)
+#define TAS2552_WORDLENGTH_32BIT	(0x3 << 0)
+#define TAS2552_WORDLENGTH_MASK		TAS2552_WORDLENGTH_32BIT
+#define TAS2552_DATAFORMAT_I2S		(0x0 << 2)
+#define TAS2552_DATAFORMAT_DSP		(0x1 << 2)
+#define TAS2552_DATAFORMAT_RIGHT_J	(0x2 << 2)
+#define TAS2552_DATAFORMAT_LEFT_J	(0x3 << 2)
+#define TAS2552_DATAFORMAT_MASK		TAS2552_DATAFORMAT_LEFT_J
+#define TAS2552_CLKSPERFRAME_32		(0x0 << 4)
+#define TAS2552_CLKSPERFRAME_64		(0x1 << 4)
+#define TAS2552_CLKSPERFRAME_128	(0x2 << 4)
+#define TAS2552_CLKSPERFRAME_256	(0x3 << 4)
+#define TAS2552_CLKSPERFRAME_MASK	TAS2552_CLKSPERFRAME_256
+#define TAS2552_BCLKDIR			(1 << 6)
+#define TAS2552_WCLKDIR			(1 << 7)
 
 /* OUTPUT_DATA register */
 #define TAS2552_PDM_DATA_I		0x00
@@ -99,12 +109,12 @@
 #define TAS2552_PDM_DATA_V_I	(0x11 << 6)
 
 /* PDM CFG Register */
-#define TAS2552_PDM_DATA_ES_RISE 0x4
-
-#define TAS2552_PDM_PLL_CLK_SEL 0x00
-#define TAS2552_PDM_IV_CLK_SEL	(1 << 1)
-#define TAS2552_PDM_BCLK_SEL	(1 << 2)
-#define TAS2552_PDM_MCLK_SEL	(1 << 3)
+#define TAS2552_PDM_CLK_SEL_PLL		(0x0 << 0)
+#define TAS2552_PDM_CLK_SEL_IVCLKIN	(0x1 << 0)
+#define TAS2552_PDM_CLK_SEL_BCLK	(0x2 << 0)
+#define TAS2552_PDM_CLK_SEL_MCLK	(0x3 << 0)
+#define TAS2552_PDM_CLK_SEL_MASK	TAS2552_PDM_CLK_SEL_MCLK
+#define TAS2552_PDM_DATA_ES	 	(1 << 2)
 
 /* Boost pass-through register */
 #define TAS2552_APT_DELAY_50	0x00
diff --git a/sound/soc/codecs/tas571x.c b/sound/soc/codecs/tas571x.c
new file mode 100644
index 000000000000..85bcc374c8e8
--- /dev/null
+++ b/sound/soc/codecs/tas571x.c
@@ -0,0 +1,514 @@
+/*
+ * TAS571x amplifier audio driver
+ *
+ * Copyright (C) 2015 Google, Inc.
+ * Copyright (c) 2013 Daniel Mack <zonque@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/gpio/consumer.h>
+#include <linux/i2c.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+#include <linux/stddef.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/tlv.h>
+
+#include "tas571x.h"
+
+#define TAS571X_MAX_SUPPLIES		6
+
+struct tas571x_chip {
+	const char			*const *supply_names;
+	int				num_supply_names;
+	const struct snd_kcontrol_new	*controls;
+	int				num_controls;
+	const struct regmap_config	*regmap_config;
+	int				vol_reg_size;
+};
+
+struct tas571x_private {
+	const struct tas571x_chip	*chip;
+	struct regmap			*regmap;
+	struct regulator_bulk_data	supplies[TAS571X_MAX_SUPPLIES];
+	struct clk			*mclk;
+	unsigned int			format;
+	struct gpio_desc		*reset_gpio;
+	struct gpio_desc		*pdn_gpio;
+	struct snd_soc_codec_driver	codec_driver;
+};
+
+static int tas571x_register_size(struct tas571x_private *priv, unsigned int reg)
+{
+	switch (reg) {
+	case TAS571X_MVOL_REG:
+	case TAS571X_CH1_VOL_REG:
+	case TAS571X_CH2_VOL_REG:
+		return priv->chip->vol_reg_size;
+	default:
+		return 1;
+	}
+}
+
+static int tas571x_reg_write(void *context, unsigned int reg,
+			     unsigned int value)
+{
+	struct i2c_client *client = context;
+	struct tas571x_private *priv = i2c_get_clientdata(client);
+	unsigned int i, size;
+	uint8_t buf[5];
+	int ret;
+
+	size = tas571x_register_size(priv, reg);
+	buf[0] = reg;
+
+	for (i = size; i >= 1; --i) {
+		buf[i] = value;
+		value >>= 8;
+	}
+
+	ret = i2c_master_send(client, buf, size + 1);
+	if (ret == size + 1)
+		return 0;
+	else if (ret < 0)
+		return ret;
+	else
+		return -EIO;
+}
+
+static int tas571x_reg_read(void *context, unsigned int reg,
+			    unsigned int *value)
+{
+	struct i2c_client *client = context;
+	struct tas571x_private *priv = i2c_get_clientdata(client);
+	uint8_t send_buf, recv_buf[4];
+	struct i2c_msg msgs[2];
+	unsigned int size;
+	unsigned int i;
+	int ret;
+
+	size = tas571x_register_size(priv, reg);
+	send_buf = reg;
+
+	msgs[0].addr = client->addr;
+	msgs[0].len = sizeof(send_buf);
+	msgs[0].buf = &send_buf;
+	msgs[0].flags = 0;
+
+	msgs[1].addr = client->addr;
+	msgs[1].len = size;
+	msgs[1].buf = recv_buf;
+	msgs[1].flags = I2C_M_RD;
+
+	ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs));
+	if (ret < 0)
+		return ret;
+	else if (ret != ARRAY_SIZE(msgs))
+		return -EIO;
+
+	*value = 0;
+
+	for (i = 0; i < size; i++) {
+		*value <<= 8;
+		*value |= recv_buf[i];
+	}
+
+	return 0;
+}
+
+static int tas571x_set_dai_fmt(struct snd_soc_dai *dai, unsigned int format)
+{
+	struct tas571x_private *priv = snd_soc_codec_get_drvdata(dai->codec);
+
+	priv->format = format;
+
+	return 0;
+}
+
+static int tas571x_hw_params(struct snd_pcm_substream *substream,
+			     struct snd_pcm_hw_params *params,
+			     struct snd_soc_dai *dai)
+{
+	struct tas571x_private *priv = snd_soc_codec_get_drvdata(dai->codec);
+	u32 val;
+
+	switch (priv->format & SND_SOC_DAIFMT_FORMAT_MASK) {
+	case SND_SOC_DAIFMT_RIGHT_J:
+		val = 0x00;
+		break;
+	case SND_SOC_DAIFMT_I2S:
+		val = 0x03;
+		break;
+	case SND_SOC_DAIFMT_LEFT_J:
+		val = 0x06;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	if (params_width(params) >= 24)
+		val += 2;
+	else if (params_width(params) >= 20)
+		val += 1;
+
+	return regmap_update_bits(priv->regmap, TAS571X_SDI_REG,
+				  TAS571X_SDI_FMT_MASK, val);
+}
+
+static int tas571x_set_bias_level(struct snd_soc_codec *codec,
+				  enum snd_soc_bias_level level)
+{
+	struct tas571x_private *priv = snd_soc_codec_get_drvdata(codec);
+	int ret;
+
+	switch (level) {
+	case SND_SOC_BIAS_ON:
+		break;
+	case SND_SOC_BIAS_PREPARE:
+		break;
+	case SND_SOC_BIAS_STANDBY:
+		if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) {
+			if (!IS_ERR(priv->mclk)) {
+				ret = clk_prepare_enable(priv->mclk);
+				if (ret) {
+					dev_err(codec->dev,
+						"Failed to enable master clock: %d\n",
+						ret);
+					return ret;
+				}
+			}
+
+			gpiod_set_value(priv->pdn_gpio, 0);
+			usleep_range(5000, 6000);
+
+			regcache_cache_only(priv->regmap, false);
+			ret = regcache_sync(priv->regmap);
+			if (ret)
+				return ret;
+		}
+		break;
+	case SND_SOC_BIAS_OFF:
+		regcache_cache_only(priv->regmap, true);
+		gpiod_set_value(priv->pdn_gpio, 1);
+
+		if (!IS_ERR(priv->mclk))
+			clk_disable_unprepare(priv->mclk);
+		break;
+	}
+
+	return 0;
+}
+
+static const struct snd_soc_dai_ops tas571x_dai_ops = {
+	.set_fmt	= tas571x_set_dai_fmt,
+	.hw_params	= tas571x_hw_params,
+};
+
+static const char *const tas5711_supply_names[] = {
+	"AVDD",
+	"DVDD",
+	"PVDD_A",
+	"PVDD_B",
+	"PVDD_C",
+	"PVDD_D",
+};
+
+static const DECLARE_TLV_DB_SCALE(tas5711_volume_tlv, -10350, 50, 1);
+
+static const struct snd_kcontrol_new tas5711_controls[] = {
+	SOC_SINGLE_TLV("Master Volume",
+		       TAS571X_MVOL_REG,
+		       0, 0xff, 1, tas5711_volume_tlv),
+	SOC_DOUBLE_R_TLV("Speaker Volume",
+			 TAS571X_CH1_VOL_REG,
+			 TAS571X_CH2_VOL_REG,
+			 0, 0xff, 1, tas5711_volume_tlv),
+	SOC_DOUBLE("Speaker Switch",
+		   TAS571X_SOFT_MUTE_REG,
+		   TAS571X_SOFT_MUTE_CH1_SHIFT, TAS571X_SOFT_MUTE_CH2_SHIFT,
+		   1, 1),
+};
+
+static const struct reg_default tas5711_reg_defaults[] = {
+	{ 0x04, 0x05 },
+	{ 0x05, 0x40 },
+	{ 0x06, 0x00 },
+	{ 0x07, 0xff },
+	{ 0x08, 0x30 },
+	{ 0x09, 0x30 },
+	{ 0x1b, 0x82 },
+};
+
+static const struct regmap_config tas5711_regmap_config = {
+	.reg_bits			= 8,
+	.val_bits			= 32,
+	.max_register			= 0xff,
+	.reg_read			= tas571x_reg_read,
+	.reg_write			= tas571x_reg_write,
+	.reg_defaults			= tas5711_reg_defaults,
+	.num_reg_defaults		= ARRAY_SIZE(tas5711_reg_defaults),
+	.cache_type			= REGCACHE_RBTREE,
+};
+
+static const struct tas571x_chip tas5711_chip = {
+	.supply_names			= tas5711_supply_names,
+	.num_supply_names		= ARRAY_SIZE(tas5711_supply_names),
+	.controls			= tas5711_controls,
+	.num_controls			= ARRAY_SIZE(tas5711_controls),
+	.regmap_config			= &tas5711_regmap_config,
+	.vol_reg_size			= 1,
+};
+
+static const char *const tas5717_supply_names[] = {
+	"AVDD",
+	"DVDD",
+	"HPVDD",
+	"PVDD_AB",
+	"PVDD_CD",
+};
+
+static const DECLARE_TLV_DB_SCALE(tas5717_volume_tlv, -10375, 25, 0);
+
+static const struct snd_kcontrol_new tas5717_controls[] = {
+	/* MVOL LSB is ignored - see comments in tas571x_i2c_probe() */
+	SOC_SINGLE_TLV("Master Volume",
+		       TAS571X_MVOL_REG, 1, 0x1ff, 1,
+		       tas5717_volume_tlv),
+	SOC_DOUBLE_R_TLV("Speaker Volume",
+			 TAS571X_CH1_VOL_REG, TAS571X_CH2_VOL_REG,
+			 1, 0x1ff, 1, tas5717_volume_tlv),
+	SOC_DOUBLE("Speaker Switch",
+		   TAS571X_SOFT_MUTE_REG,
+		   TAS571X_SOFT_MUTE_CH1_SHIFT, TAS571X_SOFT_MUTE_CH2_SHIFT,
+		   1, 1),
+};
+
+static const struct reg_default tas5717_reg_defaults[] = {
+	{ 0x04, 0x05 },
+	{ 0x05, 0x40 },
+	{ 0x06, 0x00 },
+	{ 0x07, 0x03ff },
+	{ 0x08, 0x00c0 },
+	{ 0x09, 0x00c0 },
+	{ 0x1b, 0x82 },
+};
+
+static const struct regmap_config tas5717_regmap_config = {
+	.reg_bits			= 8,
+	.val_bits			= 32,
+	.max_register			= 0xff,
+	.reg_read			= tas571x_reg_read,
+	.reg_write			= tas571x_reg_write,
+	.reg_defaults			= tas5717_reg_defaults,
+	.num_reg_defaults		= ARRAY_SIZE(tas5717_reg_defaults),
+	.cache_type			= REGCACHE_RBTREE,
+};
+
+/* This entry is reused for tas5719 as the software interface is identical. */
+static const struct tas571x_chip tas5717_chip = {
+	.supply_names			= tas5717_supply_names,
+	.num_supply_names		= ARRAY_SIZE(tas5717_supply_names),
+	.controls			= tas5717_controls,
+	.num_controls			= ARRAY_SIZE(tas5717_controls),
+	.regmap_config			= &tas5717_regmap_config,
+	.vol_reg_size			= 2,
+};
+
+static const struct snd_soc_dapm_widget tas571x_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("OUT_A"),
+	SND_SOC_DAPM_OUTPUT("OUT_B"),
+	SND_SOC_DAPM_OUTPUT("OUT_C"),
+	SND_SOC_DAPM_OUTPUT("OUT_D"),
+};
+
+static const struct snd_soc_dapm_route tas571x_dapm_routes[] = {
+	{ "DACL",  NULL, "Playback" },
+	{ "DACR",  NULL, "Playback" },
+
+	{ "OUT_A", NULL, "DACL" },
+	{ "OUT_B", NULL, "DACL" },
+	{ "OUT_C", NULL, "DACR" },
+	{ "OUT_D", NULL, "DACR" },
+};
+
+static const struct snd_soc_codec_driver tas571x_codec = {
+	.set_bias_level = tas571x_set_bias_level,
+	.idle_bias_off = true,
+
+	.dapm_widgets = tas571x_dapm_widgets,
+	.num_dapm_widgets = ARRAY_SIZE(tas571x_dapm_widgets),
+	.dapm_routes = tas571x_dapm_routes,
+	.num_dapm_routes = ARRAY_SIZE(tas571x_dapm_routes),
+};
+
+static struct snd_soc_dai_driver tas571x_dai = {
+	.name = "tas571x-hifi",
+	.playback = {
+		.stream_name = "Playback",
+		.channels_min = 2,
+		.channels_max = 2,
+		.rates = SNDRV_PCM_RATE_8000_48000,
+		.formats = SNDRV_PCM_FMTBIT_S32_LE |
+			   SNDRV_PCM_FMTBIT_S24_LE |
+			   SNDRV_PCM_FMTBIT_S16_LE,
+	},
+	.ops = &tas571x_dai_ops,
+};
+
+static const struct of_device_id tas571x_of_match[];
+
+static int tas571x_i2c_probe(struct i2c_client *client,
+			     const struct i2c_device_id *id)
+{
+	struct tas571x_private *priv;
+	struct device *dev = &client->dev;
+	const struct of_device_id *of_id;
+	int i, ret;
+
+	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
+	i2c_set_clientdata(client, priv);
+
+	of_id = of_match_device(tas571x_of_match, dev);
+	if (!of_id) {
+		dev_err(dev, "Unknown device type\n");
+		return -EINVAL;
+	}
+	priv->chip = of_id->data;
+
+	priv->mclk = devm_clk_get(dev, "mclk");
+	if (IS_ERR(priv->mclk) && PTR_ERR(priv->mclk) != -ENOENT) {
+		dev_err(dev, "Failed to request mclk: %ld\n",
+			PTR_ERR(priv->mclk));
+		return PTR_ERR(priv->mclk);
+	}
+
+	BUG_ON(priv->chip->num_supply_names > TAS571X_MAX_SUPPLIES);
+	for (i = 0; i < priv->chip->num_supply_names; i++)
+		priv->supplies[i].supply = priv->chip->supply_names[i];
+
+	ret = devm_regulator_bulk_get(dev, priv->chip->num_supply_names,
+				      priv->supplies);
+	if (ret) {
+		dev_err(dev, "Failed to get supplies: %d\n", ret);
+		return ret;
+	}
+	ret = regulator_bulk_enable(priv->chip->num_supply_names,
+				    priv->supplies);
+	if (ret) {
+		dev_err(dev, "Failed to enable supplies: %d\n", ret);
+		return ret;
+	}
+
+	priv->regmap = devm_regmap_init(dev, NULL, client,
+					priv->chip->regmap_config);
+	if (IS_ERR(priv->regmap))
+		return PTR_ERR(priv->regmap);
+
+	priv->pdn_gpio = devm_gpiod_get_optional(dev, "pdn", GPIOD_OUT_LOW);
+	if (IS_ERR(priv->pdn_gpio)) {
+		dev_err(dev, "error requesting pdn_gpio: %ld\n",
+			PTR_ERR(priv->pdn_gpio));
+		return PTR_ERR(priv->pdn_gpio);
+	}
+
+	priv->reset_gpio = devm_gpiod_get_optional(dev, "reset",
+						   GPIOD_OUT_HIGH);
+	if (IS_ERR(priv->reset_gpio)) {
+		dev_err(dev, "error requesting reset_gpio: %ld\n",
+			PTR_ERR(priv->reset_gpio));
+		return PTR_ERR(priv->reset_gpio);
+	} else if (priv->reset_gpio) {
+		/* pulse the active low reset line for ~100us */
+		usleep_range(100, 200);
+		gpiod_set_value(priv->reset_gpio, 0);
+		usleep_range(12000, 20000);
+	}
+
+	ret = regmap_write(priv->regmap, TAS571X_OSC_TRIM_REG, 0);
+	if (ret)
+		return ret;
+
+	ret = regmap_update_bits(priv->regmap, TAS571X_SYS_CTRL_2_REG,
+				 TAS571X_SYS_CTRL_2_SDN_MASK, 0);
+	if (ret)
+		return ret;
+
+	memcpy(&priv->codec_driver, &tas571x_codec, sizeof(priv->codec_driver));
+	priv->codec_driver.controls = priv->chip->controls;
+	priv->codec_driver.num_controls = priv->chip->num_controls;
+
+	if (priv->chip->vol_reg_size == 2) {
+		/*
+		 * The master volume defaults to 0x3ff (mute), but we ignore
+		 * (zero) the LSB because the hardware step size is 0.125 dB
+		 * and TLV_DB_SCALE_ITEM has a resolution of 0.01 dB.
+		 */
+		ret = regmap_update_bits(priv->regmap, TAS571X_MVOL_REG, 1, 0);
+		if (ret)
+			return ret;
+	}
+
+	regcache_cache_only(priv->regmap, true);
+	gpiod_set_value(priv->pdn_gpio, 1);
+
+	return snd_soc_register_codec(&client->dev, &priv->codec_driver,
+				      &tas571x_dai, 1);
+}
+
+static int tas571x_i2c_remove(struct i2c_client *client)
+{
+	struct tas571x_private *priv = i2c_get_clientdata(client);
+
+	snd_soc_unregister_codec(&client->dev);
+	regulator_bulk_disable(priv->chip->num_supply_names, priv->supplies);
+
+	return 0;
+}
+
+static const struct of_device_id tas571x_of_match[] = {
+	{ .compatible = "ti,tas5711", .data = &tas5711_chip, },
+	{ .compatible = "ti,tas5717", .data = &tas5717_chip, },
+	{ .compatible = "ti,tas5719", .data = &tas5717_chip, },
+	{ }
+};
+MODULE_DEVICE_TABLE(of, tas571x_of_match);
+
+static const struct i2c_device_id tas571x_i2c_id[] = {
+	{ "tas5711", 0 },
+	{ "tas5717", 0 },
+	{ "tas5719", 0 },
+	{ }
+};
+MODULE_DEVICE_TABLE(i2c, tas571x_i2c_id);
+
+static struct i2c_driver tas571x_i2c_driver = {
+	.driver = {
+		.name = "tas571x",
+		.of_match_table = of_match_ptr(tas571x_of_match),
+	},
+	.probe = tas571x_i2c_probe,
+	.remove = tas571x_i2c_remove,
+	.id_table = tas571x_i2c_id,
+};
+module_i2c_driver(tas571x_i2c_driver);
+
+MODULE_DESCRIPTION("ASoC TAS571x driver");
+MODULE_AUTHOR("Kevin Cernekee <cernekee@chromium.org>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/tas571x.h b/sound/soc/codecs/tas571x.h
new file mode 100644
index 000000000000..0aee471232cd
--- /dev/null
+++ b/sound/soc/codecs/tas571x.h
@@ -0,0 +1,33 @@
+/*
+ * TAS571x amplifier audio driver
+ *
+ * Copyright (C) 2015 Google, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef _TAS571X_H
+#define _TAS571X_H
+
+/* device registers */
+#define TAS571X_SDI_REG			0x04
+#define TAS571X_SDI_FMT_MASK		0x0f
+
+#define TAS571X_SYS_CTRL_2_REG		0x05
+#define TAS571X_SYS_CTRL_2_SDN_MASK	0x40
+
+#define TAS571X_SOFT_MUTE_REG		0x06
+#define TAS571X_SOFT_MUTE_CH1_SHIFT	0
+#define TAS571X_SOFT_MUTE_CH2_SHIFT	1
+#define TAS571X_SOFT_MUTE_CH3_SHIFT	2
+
+#define TAS571X_MVOL_REG		0x07
+#define TAS571X_CH1_VOL_REG		0x08
+#define TAS571X_CH2_VOL_REG		0x09
+
+#define TAS571X_OSC_TRIM_REG		0x1b
+
+#endif /* _TAS571X_H */
diff --git a/sound/soc/codecs/tlv320aic23.c b/sound/soc/codecs/tlv320aic23.c
index cc17e7e5126e..cd8c02b6e4de 100644
--- a/sound/soc/codecs/tlv320aic23.c
+++ b/sound/soc/codecs/tlv320aic23.c
@@ -506,7 +506,6 @@ static int tlv320aic23_set_bias_level(struct snd_soc_codec *codec,
 		snd_soc_write(codec, TLV320AIC23_PWR, 0x1ff);
 		break;
 	}
-	codec->dapm.bias_level = level;
 	return 0;
 }
 
diff --git a/sound/soc/codecs/tlv320aic31xx.c b/sound/soc/codecs/tlv320aic31xx.c
index c86dd9aae157..c4c960f592a1 100644
--- a/sound/soc/codecs/tlv320aic31xx.c
+++ b/sound/soc/codecs/tlv320aic31xx.c
@@ -646,7 +646,7 @@ static int aic31xx_add_controls(struct snd_soc_codec *codec)
 
 static int aic31xx_add_widgets(struct snd_soc_codec *codec)
 {
-	struct snd_soc_dapm_context *dapm = &codec->dapm;
+	struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
 	struct aic31xx_priv *aic31xx = snd_soc_codec_get_drvdata(codec);
 	int ret = 0;
 
@@ -1027,17 +1027,17 @@ static int aic31xx_set_bias_level(struct snd_soc_codec *codec,
 				  enum snd_soc_bias_level level)
 {
 	dev_dbg(codec->dev, "## %s: %d -> %d\n", __func__,
-		codec->dapm.bias_level, level);
+		snd_soc_codec_get_bias_level(codec), level);
 
 	switch (level) {
 	case SND_SOC_BIAS_ON:
 		break;
 	case SND_SOC_BIAS_PREPARE:
-		if (codec->dapm.bias_level == SND_SOC_BIAS_STANDBY)
+		if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_STANDBY)
 			aic31xx_clk_on(codec);
 		break;
 	case SND_SOC_BIAS_STANDBY:
-		switch (codec->dapm.bias_level) {
+		switch (snd_soc_codec_get_bias_level(codec)) {
 		case SND_SOC_BIAS_OFF:
 			aic31xx_power_on(codec);
 			break;
@@ -1049,11 +1049,10 @@ static int aic31xx_set_bias_level(struct snd_soc_codec *codec,
 		}
 		break;
 	case SND_SOC_BIAS_OFF:
-		if (codec->dapm.bias_level == SND_SOC_BIAS_STANDBY)
+		if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_STANDBY)
 			aic31xx_power_off(codec);
 		break;
 	}
-	codec->dapm.bias_level = level;
 
 	return 0;
 }
diff --git a/sound/soc/codecs/tlv320aic32x4.c b/sound/soc/codecs/tlv320aic32x4.c
index 015467ed606b..ad6cb90e5f9b 100644
--- a/sound/soc/codecs/tlv320aic32x4.c
+++ b/sound/soc/codecs/tlv320aic32x4.c
@@ -564,7 +564,6 @@ static int aic32x4_set_bias_level(struct snd_soc_codec *codec,
 	case SND_SOC_BIAS_OFF:
 		break;
 	}
-	codec->dapm.bias_level = level;
 	return 0;
 }
 
diff --git a/sound/soc/codecs/tlv320aic3x.c b/sound/soc/codecs/tlv320aic3x.c
index 51c4713ac6e3..a7cf19b53fb2 100644
--- a/sound/soc/codecs/tlv320aic3x.c
+++ b/sound/soc/codecs/tlv320aic3x.c
@@ -147,6 +147,7 @@ static int snd_soc_dapm_put_volsw_aic3x(struct snd_kcontrol *kcontrol,
 					struct snd_ctl_elem_value *ucontrol)
 {
 	struct snd_soc_codec *codec = snd_soc_dapm_kcontrol_codec(kcontrol);
+	struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
 	struct soc_mixer_control *mc =
 		(struct soc_mixer_control *)kcontrol->private_value;
 	unsigned int reg = mc->reg;
@@ -179,7 +180,7 @@ static int snd_soc_dapm_put_volsw_aic3x(struct snd_kcontrol *kcontrol,
 		update.mask = mask;
 		update.val = val;
 
-		snd_soc_dapm_mixer_update_power(&codec->dapm, kcontrol, connect,
+		snd_soc_dapm_mixer_update_power(dapm, kcontrol, connect,
 			&update);
 	}
 
@@ -979,7 +980,7 @@ static const struct snd_soc_dapm_route intercon_3007[] = {
 static int aic3x_add_widgets(struct snd_soc_codec *codec)
 {
 	struct aic3x_priv *aic3x = snd_soc_codec_get_drvdata(codec);
-	struct snd_soc_dapm_context *dapm = &codec->dapm;
+	struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
 
 	switch (aic3x->model) {
 	case AIC3X_MODEL_3X:
@@ -1384,7 +1385,7 @@ static int aic3x_set_bias_level(struct snd_soc_codec *codec,
 	case SND_SOC_BIAS_ON:
 		break;
 	case SND_SOC_BIAS_PREPARE:
-		if (codec->dapm.bias_level == SND_SOC_BIAS_STANDBY &&
+		if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_STANDBY &&
 		    aic3x->master) {
 			/* enable pll */
 			snd_soc_update_bits(codec, AIC3X_PLL_PROGA_REG,
@@ -1394,7 +1395,7 @@ static int aic3x_set_bias_level(struct snd_soc_codec *codec,
 	case SND_SOC_BIAS_STANDBY:
 		if (!aic3x->power)
 			aic3x_set_power(codec, 1);
-		if (codec->dapm.bias_level == SND_SOC_BIAS_PREPARE &&
+		if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_PREPARE &&
 		    aic3x->master) {
 			/* disable pll */
 			snd_soc_update_bits(codec, AIC3X_PLL_PROGA_REG,
@@ -1406,7 +1407,6 @@ static int aic3x_set_bias_level(struct snd_soc_codec *codec,
 			aic3x_set_power(codec, 0);
 		break;
 	}
-	codec->dapm.bias_level = level;
 
 	return 0;
 }
diff --git a/sound/soc/codecs/tlv320dac33.c b/sound/soc/codecs/tlv320dac33.c
index 4e3e607dec13..d67a311f0e75 100644
--- a/sound/soc/codecs/tlv320dac33.c
+++ b/sound/soc/codecs/tlv320dac33.c
@@ -633,7 +633,7 @@ static int dac33_set_bias_level(struct snd_soc_codec *codec,
 	case SND_SOC_BIAS_PREPARE:
 		break;
 	case SND_SOC_BIAS_STANDBY:
-		if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) {
+		if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) {
 			/* Coming from OFF, switch on the codec */
 			ret = dac33_hard_power(codec, 1);
 			if (ret != 0)
@@ -644,14 +644,13 @@ static int dac33_set_bias_level(struct snd_soc_codec *codec,
 		break;
 	case SND_SOC_BIAS_OFF:
 		/* Do not power off, when the codec is already off */
-		if (codec->dapm.bias_level == SND_SOC_BIAS_OFF)
+		if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF)
 			return 0;
 		ret = dac33_hard_power(codec, 0);
 		if (ret != 0)
 			return ret;
 		break;
 	}
-	codec->dapm.bias_level = level;
 
 	return 0;
 }
diff --git a/sound/soc/codecs/ts3a227e.c b/sound/soc/codecs/ts3a227e.c
index 9fd80ac1897f..12232d7db4c5 100644
--- a/sound/soc/codecs/ts3a227e.c
+++ b/sound/soc/codecs/ts3a227e.c
@@ -254,12 +254,13 @@ static const struct regmap_config ts3a227e_regmap_config = {
 	.num_reg_defaults = ARRAY_SIZE(ts3a227e_reg_defaults),
 };
 
-static int ts3a227e_parse_dt(struct ts3a227e *ts3a227e, struct device_node *np)
+static int ts3a227e_parse_device_property(struct ts3a227e *ts3a227e,
+				struct device *dev)
 {
 	u32 micbias;
 	int err;
 
-	err = of_property_read_u32(np, "ti,micbias", &micbias);
+	err = device_property_read_u32(dev, "ti,micbias", &micbias);
 	if (!err) {
 		regmap_update_bits(ts3a227e->regmap, TS3A227E_REG_SETTING_3,
 			MICBIAS_SETTING_MASK,
@@ -287,12 +288,10 @@ static int ts3a227e_i2c_probe(struct i2c_client *i2c,
 	if (IS_ERR(ts3a227e->regmap))
 		return PTR_ERR(ts3a227e->regmap);
 
-	if (dev->of_node) {
-		ret = ts3a227e_parse_dt(ts3a227e, dev->of_node);
-		if (ret) {
-			dev_err(dev, "Failed to parse device tree: %d\n", ret);
-			return ret;
-		}
+	ret = ts3a227e_parse_device_property(ts3a227e, dev);
+	if (ret) {
+		dev_err(dev, "Failed to parse device property: %d\n", ret);
+		return ret;
 	}
 
 	ret = devm_request_threaded_irq(dev, i2c->irq, NULL, ts3a227e_interrupt,
diff --git a/sound/soc/codecs/twl4030.c b/sound/soc/codecs/twl4030.c
index d04693e9cf9f..90f5f04eca2d 100644
--- a/sound/soc/codecs/twl4030.c
+++ b/sound/soc/codecs/twl4030.c
@@ -1588,14 +1588,13 @@ static int twl4030_set_bias_level(struct snd_soc_codec *codec,
 	case SND_SOC_BIAS_PREPARE:
 		break;
 	case SND_SOC_BIAS_STANDBY:
-		if (codec->dapm.bias_level == SND_SOC_BIAS_OFF)
+		if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF)
 			twl4030_codec_enable(codec, 1);
 		break;
 	case SND_SOC_BIAS_OFF:
 		twl4030_codec_enable(codec, 0);
 		break;
 	}
-	codec->dapm.bias_level = level;
 
 	return 0;
 }
diff --git a/sound/soc/codecs/twl6040.c b/sound/soc/codecs/twl6040.c
index aeec27b6f1af..4cad8929d262 100644
--- a/sound/soc/codecs/twl6040.c
+++ b/sound/soc/codecs/twl6040.c
@@ -533,7 +533,7 @@ static int twl6040_pll_put_enum(struct snd_kcontrol *kcontrol,
 
 int twl6040_get_dl1_gain(struct snd_soc_codec *codec)
 {
-	struct snd_soc_dapm_context *dapm = &codec->dapm;
+	struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
 
 	if (snd_soc_dapm_get_pin_status(dapm, "EP"))
 		return -1; /* -1dB */
@@ -853,8 +853,6 @@ static int twl6040_set_bias_level(struct snd_soc_codec *codec,
 		break;
 	}
 
-	codec->dapm.bias_level = level;
-
 	return 0;
 }
 
@@ -1123,14 +1121,15 @@ static int twl6040_probe(struct snd_soc_codec *codec)
 	mutex_init(&priv->mutex);
 
 	ret = request_threaded_irq(priv->plug_irq, NULL,
-					twl6040_audio_handler, IRQF_NO_SUSPEND,
+					twl6040_audio_handler,
+					IRQF_NO_SUSPEND | IRQF_ONESHOT,
 					"twl6040_irq_plug", codec);
 	if (ret) {
 		dev_err(codec->dev, "PLUG IRQ request failed: %d\n", ret);
 		return ret;
 	}
 
-	twl6040_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+	snd_soc_codec_force_bias_level(codec, SND_SOC_BIAS_STANDBY);
 	twl6040_init_chip(codec);
 
 	return 0;
diff --git a/sound/soc/codecs/uda134x.c b/sound/soc/codecs/uda134x.c
index f883308c00de..913edf283239 100644
--- a/sound/soc/codecs/uda134x.c
+++ b/sound/soc/codecs/uda134x.c
@@ -350,7 +350,6 @@ static int uda134x_set_bias_level(struct snd_soc_codec *codec,
 			pd->power(0);
 		break;
 	}
-	codec->dapm.bias_level = level;
 	return 0;
 }
 
@@ -478,6 +477,7 @@ static struct snd_soc_dai_driver uda134x_dai = {
 
 static int uda134x_soc_probe(struct snd_soc_codec *codec)
 {
+	struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
 	struct uda134x_priv *uda134x;
 	struct uda134x_platform_data *pd = codec->component.card->dev->platform_data;
 	const struct snd_soc_dapm_widget *widgets;
@@ -526,7 +526,7 @@ static int uda134x_soc_probe(struct snd_soc_codec *codec)
 		num_widgets = ARRAY_SIZE(uda1340_dapm_widgets);
 	}
 
-	ret = snd_soc_dapm_new_controls(&codec->dapm, widgets, num_widgets);
+	ret = snd_soc_dapm_new_controls(dapm, widgets, num_widgets);
 	if (ret) {
 		printk(KERN_ERR "%s failed to register dapm controls: %d",
 			__func__, ret);
diff --git a/sound/soc/codecs/uda1380.c b/sound/soc/codecs/uda1380.c
index c3c33bd0df1c..6e159f59d219 100644
--- a/sound/soc/codecs/uda1380.c
+++ b/sound/soc/codecs/uda1380.c
@@ -590,9 +590,6 @@ static int uda1380_set_bias_level(struct snd_soc_codec *codec,
 	int reg;
 	struct uda1380_platform_data *pdata = codec->dev->platform_data;
 
-	if (codec->dapm.bias_level == level)
-		return 0;
-
 	switch (level) {
 	case SND_SOC_BIAS_ON:
 	case SND_SOC_BIAS_PREPARE:
@@ -600,7 +597,7 @@ static int uda1380_set_bias_level(struct snd_soc_codec *codec,
 		uda1380_write(codec, UDA1380_PM, R02_PON_BIAS | pm);
 		break;
 	case SND_SOC_BIAS_STANDBY:
-		if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) {
+		if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) {
 			if (gpio_is_valid(pdata->gpio_power)) {
 				gpio_set_value(pdata->gpio_power, 1);
 				mdelay(1);
@@ -623,7 +620,6 @@ static int uda1380_set_bias_level(struct snd_soc_codec *codec,
 		for (reg = UDA1380_MVOL; reg < UDA1380_CACHEREGNUM; reg++)
 			set_bit(reg - 0x10, &uda1380_cache_dirty);
 	}
-	codec->dapm.bias_level = level;
 	return 0;
 }
 
diff --git a/sound/soc/codecs/wm0010.c b/sound/soc/codecs/wm0010.c
index f37989ec7cba..6560a66b3f35 100644
--- a/sound/soc/codecs/wm0010.c
+++ b/sound/soc/codecs/wm0010.c
@@ -751,13 +751,13 @@ static int wm0010_set_bias_level(struct snd_soc_codec *codec,
 
 	switch (level) {
 	case SND_SOC_BIAS_ON:
-		if (codec->dapm.bias_level == SND_SOC_BIAS_PREPARE)
+		if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_PREPARE)
 			wm0010_boot(codec);
 		break;
 	case SND_SOC_BIAS_PREPARE:
 		break;
 	case SND_SOC_BIAS_STANDBY:
-		if (codec->dapm.bias_level == SND_SOC_BIAS_PREPARE) {
+		if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_PREPARE) {
 			mutex_lock(&wm0010->lock);
 			wm0010_halt(codec);
 			mutex_unlock(&wm0010->lock);
@@ -767,8 +767,6 @@ static int wm0010_set_bias_level(struct snd_soc_codec *codec,
 		break;
 	}
 
-	codec->dapm.bias_level = level;
-
 	return 0;
 }
 
diff --git a/sound/soc/codecs/wm1250-ev1.c b/sound/soc/codecs/wm1250-ev1.c
index 8011f75fb6cb..048f00568260 100644
--- a/sound/soc/codecs/wm1250-ev1.c
+++ b/sound/soc/codecs/wm1250-ev1.c
@@ -61,8 +61,6 @@ static int wm1250_ev1_set_bias_level(struct snd_soc_codec *codec,
 		break;
 	}
 
-	codec->dapm.bias_level = level;
-
 	return 0;
 }
 
diff --git a/sound/soc/codecs/wm5100.c b/sound/soc/codecs/wm5100.c
index 96740379b711..4c10cd88c1af 100644
--- a/sound/soc/codecs/wm5100.c
+++ b/sound/soc/codecs/wm5100.c
@@ -2101,7 +2101,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;
+	struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
 
 	if (jack) {
 		wm5100->jack = jack;
@@ -2336,6 +2336,7 @@ static void wm5100_free_gpio(struct i2c_client *i2c)
 
 static int wm5100_probe(struct snd_soc_codec *codec)
 {
+	struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
 	struct i2c_client *i2c = to_i2c_client(codec->dev);
 	struct wm5100_priv *wm5100 = snd_soc_codec_get_drvdata(codec);
 	int ret, i;
@@ -2353,8 +2354,7 @@ static int wm5100_probe(struct snd_soc_codec *codec)
 	/* TODO: check if we're symmetric */
 
 	if (i2c->irq)
-		snd_soc_dapm_new_controls(&codec->dapm,
-					  wm5100_dapm_widgets_noirq,
+		snd_soc_dapm_new_controls(dapm, wm5100_dapm_widgets_noirq,
 					  ARRAY_SIZE(wm5100_dapm_widgets_noirq));
 
 	if (wm5100->pdata.hp_pol) {
@@ -2570,11 +2570,13 @@ static int wm5100_i2c_probe(struct i2c_client *i2c,
 
 		if (irq_flags & (IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING))
 			ret = request_threaded_irq(i2c->irq, NULL,
-						   wm5100_edge_irq, irq_flags,
+						   wm5100_edge_irq,
+						   irq_flags | IRQF_ONESHOT,
 						   "wm5100", wm5100);
 		else
 			ret = request_threaded_irq(i2c->irq, NULL, wm5100_irq,
-						   irq_flags, "wm5100",
+						   irq_flags | IRQF_ONESHOT,
+						   "wm5100",
 						   wm5100);
 
 		if (ret != 0) {
diff --git a/sound/soc/codecs/wm5102.c b/sound/soc/codecs/wm5102.c
index d476221dba51..d8959e31853d 100644
--- a/sound/soc/codecs/wm5102.c
+++ b/sound/soc/codecs/wm5102.c
@@ -605,12 +605,56 @@ static int wm5102_sysclk_ev(struct snd_soc_dapm_widget *w,
 				regmap_write_async(regmap, patch[i].reg,
 						   patch[i].def);
 		break;
+	case SND_SOC_DAPM_PRE_PMD:
+		break;
+	default:
+		return 0;
+	}
+
+	return arizona_dvfs_sysclk_ev(w, kcontrol, event);
+}
+
+static int wm5102_adsp_power_ev(struct snd_soc_dapm_widget *w,
+		   struct snd_kcontrol *kcontrol, int event)
+{
+	struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+	struct arizona *arizona = dev_get_drvdata(codec->dev->parent);
+	unsigned int v;
+	int ret;
+
+	switch (event) {
+	case SND_SOC_DAPM_PRE_PMU:
+		ret = regmap_read(arizona->regmap, ARIZONA_SYSTEM_CLOCK_1, &v);
+		if (ret != 0) {
+			dev_err(codec->dev,
+				"Failed to read SYSCLK state: %d\n", ret);
+			return -EIO;
+		}
+
+		v = (v & ARIZONA_SYSCLK_FREQ_MASK) >> ARIZONA_SYSCLK_FREQ_SHIFT;
+
+		if (v >= 3) {
+			ret = arizona_dvfs_up(codec, ARIZONA_DVFS_ADSP1_RQ);
+			if (ret) {
+				dev_err(codec->dev,
+					"Failed to raise DVFS: %d\n", ret);
+				return ret;
+			}
+		}
+		break;
+
+	case SND_SOC_DAPM_POST_PMD:
+		ret = arizona_dvfs_down(codec, ARIZONA_DVFS_ADSP1_RQ);
+		if (ret)
+			dev_warn(codec->dev,
+				 "Failed to lower DVFS: %d\n", ret);
+		break;
 
 	default:
 		break;
 	}
 
-	return 0;
+	return wm_adsp2_early_event(w, kcontrol, event);
 }
 
 static int wm5102_out_comp_coeff_get(struct snd_kcontrol *kcontrol,
@@ -1036,7 +1080,8 @@ static const struct snd_kcontrol_new wm5102_aec_loopback_mux =
 
 static const struct snd_soc_dapm_widget wm5102_dapm_widgets[] = {
 SND_SOC_DAPM_SUPPLY("SYSCLK", ARIZONA_SYSTEM_CLOCK_1, ARIZONA_SYSCLK_ENA_SHIFT,
-		    0, wm5102_sysclk_ev, SND_SOC_DAPM_POST_PMU),
+		    0, wm5102_sysclk_ev,
+		    SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
 SND_SOC_DAPM_SUPPLY("ASYNCCLK", ARIZONA_ASYNC_CLOCK_1,
 		    ARIZONA_ASYNC_CLK_ENA_SHIFT, 0, NULL, 0),
 SND_SOC_DAPM_SUPPLY("OPCLK", ARIZONA_OUTPUT_SYSTEM_CLOCK,
@@ -1367,7 +1412,7 @@ ARIZONA_MUX_WIDGETS(ISRC2DEC2, "ISRC2DEC2"),
 ARIZONA_MUX_WIDGETS(ISRC2INT1, "ISRC2INT1"),
 ARIZONA_MUX_WIDGETS(ISRC2INT2, "ISRC2INT2"),
 
-WM_ADSP2("DSP1", 0),
+WM_ADSP2_E("DSP1", 0, wm5102_adsp_power_ev),
 
 SND_SOC_DAPM_OUTPUT("HPOUT1L"),
 SND_SOC_DAPM_OUTPUT("HPOUT1R"),
@@ -1827,6 +1872,7 @@ static struct snd_soc_dai_driver wm5102_dai[] = {
 
 static int wm5102_codec_probe(struct snd_soc_codec *codec)
 {
+	struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
 	struct wm5102_priv *priv = snd_soc_codec_get_drvdata(codec);
 	int ret;
 
@@ -1837,9 +1883,9 @@ static int wm5102_codec_probe(struct snd_soc_codec *codec)
 	arizona_init_spk(codec);
 	arizona_init_gpio(codec);
 
-	snd_soc_dapm_disable_pin(&codec->dapm, "HAPTICS");
+	snd_soc_dapm_disable_pin(dapm, "HAPTICS");
 
-	priv->core.arizona->dapm = &codec->dapm;
+	priv->core.arizona->dapm = dapm;
 
 	return 0;
 }
@@ -1909,6 +1955,8 @@ static int wm5102_probe(struct platform_device *pdev)
 	wm5102->core.arizona = arizona;
 	wm5102->core.num_inputs = 6;
 
+	arizona_init_dvfs(&wm5102->core);
+
 	wm5102->core.adsp[0].part = "wm5102";
 	wm5102->core.adsp[0].num = 1;
 	wm5102->core.adsp[0].type = WMFW_ADSP2;
@@ -1918,7 +1966,7 @@ static int wm5102_probe(struct platform_device *pdev)
 	wm5102->core.adsp[0].mem = wm5102_dsp1_regions;
 	wm5102->core.adsp[0].num_mems = ARRAY_SIZE(wm5102_dsp1_regions);
 
-	ret = wm_adsp2_init(&wm5102->core.adsp[0], true);
+	ret = wm_adsp2_init(&wm5102->core.adsp[0]);
 	if (ret != 0)
 		return ret;
 
diff --git a/sound/soc/codecs/wm5110.c b/sound/soc/codecs/wm5110.c
index 3ee6cfd0578b..14a7739d6c09 100644
--- a/sound/soc/codecs/wm5110.c
+++ b/sound/soc/codecs/wm5110.c
@@ -1598,10 +1598,11 @@ static struct snd_soc_dai_driver wm5110_dai[] = {
 
 static int wm5110_codec_probe(struct snd_soc_codec *codec)
 {
+	struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
 	struct wm5110_priv *priv = snd_soc_codec_get_drvdata(codec);
 	int ret;
 
-	priv->core.arizona->dapm = &codec->dapm;
+	priv->core.arizona->dapm = dapm;
 
 	arizona_init_spk(codec);
 	arizona_init_gpio(codec);
@@ -1611,9 +1612,7 @@ static int wm5110_codec_probe(struct snd_soc_codec *codec)
 	if (ret != 0)
 		return ret;
 
-	snd_soc_dapm_disable_pin(&codec->dapm, "HAPTICS");
-
-	priv->core.arizona->dapm = &codec->dapm;
+	snd_soc_dapm_disable_pin(dapm, "HAPTICS");
 
 	return 0;
 }
@@ -1697,7 +1696,7 @@ static int wm5110_probe(struct platform_device *pdev)
 		wm5110->core.adsp[i].num_mems
 			= ARRAY_SIZE(wm5110_dsp1_regions);
 
-		ret = wm_adsp2_init(&wm5110->core.adsp[i], false);
+		ret = wm_adsp2_init(&wm5110->core.adsp[i]);
 		if (ret != 0)
 			return ret;
 	}
diff --git a/sound/soc/codecs/wm8350.c b/sound/soc/codecs/wm8350.c
index c65e5a75fc1a..41c62c1e62db 100644
--- a/sound/soc/codecs/wm8350.c
+++ b/sound/soc/codecs/wm8350.c
@@ -1102,7 +1102,7 @@ static int wm8350_set_bias_level(struct snd_soc_codec *codec,
 		break;
 
 	case SND_SOC_BIAS_STANDBY:
-		if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) {
+		if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) {
 			ret = regulator_bulk_enable(ARRAY_SIZE(priv->supplies),
 						    priv->supplies);
 			if (ret != 0)
@@ -1235,7 +1235,6 @@ static int wm8350_set_bias_level(struct snd_soc_codec *codec,
 				       priv->supplies);
 		break;
 	}
-	codec->dapm.bias_level = level;
 	return 0;
 }
 
diff --git a/sound/soc/codecs/wm8400.c b/sound/soc/codecs/wm8400.c
index b0d84e552fca..d7555085e7f4 100644
--- a/sound/soc/codecs/wm8400.c
+++ b/sound/soc/codecs/wm8400.c
@@ -1145,7 +1145,7 @@ static int wm8400_set_bias_level(struct snd_soc_codec *codec,
 		break;
 
 	case SND_SOC_BIAS_STANDBY:
-		if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) {
+		if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) {
 			ret = regulator_bulk_enable(ARRAY_SIZE(power),
 						    &power[0]);
 			if (ret != 0) {
@@ -1232,7 +1232,6 @@ static int wm8400_set_bias_level(struct snd_soc_codec *codec,
 		break;
 	}
 
-	codec->dapm.bias_level = level;
 	return 0;
 }
 
diff --git a/sound/soc/codecs/wm8510.c b/sound/soc/codecs/wm8510.c
index 8736ad094b24..dac5beb4d023 100644
--- a/sound/soc/codecs/wm8510.c
+++ b/sound/soc/codecs/wm8510.c
@@ -519,7 +519,7 @@ static int wm8510_set_bias_level(struct snd_soc_codec *codec,
 	case SND_SOC_BIAS_STANDBY:
 		power1 |= WM8510_POWER1_BIASEN | WM8510_POWER1_BUFIOEN;
 
-		if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) {
+		if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) {
 			regcache_sync(wm8510->regmap);
 
 			/* Initial cap charge at VMID 5k */
@@ -538,7 +538,6 @@ static int wm8510_set_bias_level(struct snd_soc_codec *codec,
 		break;
 	}
 
-	codec->dapm.bias_level = level;
 	return 0;
 }
 
diff --git a/sound/soc/codecs/wm8523.c b/sound/soc/codecs/wm8523.c
index b1cc94f5fc4b..8c5b9df3e542 100644
--- a/sound/soc/codecs/wm8523.c
+++ b/sound/soc/codecs/wm8523.c
@@ -308,7 +308,7 @@ static int wm8523_set_bias_level(struct snd_soc_codec *codec,
 		break;
 
 	case SND_SOC_BIAS_STANDBY:
-		if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) {
+		if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) {
 			ret = regulator_bulk_enable(ARRAY_SIZE(wm8523->supplies),
 						    wm8523->supplies);
 			if (ret != 0) {
@@ -344,7 +344,6 @@ static int wm8523_set_bias_level(struct snd_soc_codec *codec,
 				       wm8523->supplies);
 		break;
 	}
-	codec->dapm.bias_level = level;
 	return 0;
 }
 
diff --git a/sound/soc/codecs/wm8580.c b/sound/soc/codecs/wm8580.c
index 0a887c5ec83a..759a7928ac3e 100644
--- a/sound/soc/codecs/wm8580.c
+++ b/sound/soc/codecs/wm8580.c
@@ -795,7 +795,7 @@ static int wm8580_set_bias_level(struct snd_soc_codec *codec,
 		break;
 
 	case SND_SOC_BIAS_STANDBY:
-		if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) {
+		if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) {
 			/* Power up and get individual control of the DACs */
 			snd_soc_update_bits(codec, WM8580_PWRDN1,
 					    WM8580_PWRDN1_PWDN |
@@ -812,7 +812,6 @@ static int wm8580_set_bias_level(struct snd_soc_codec *codec,
 				    WM8580_PWRDN1_PWDN, WM8580_PWRDN1_PWDN);
 		break;
 	}
-	codec->dapm.bias_level = level;
 	return 0;
 }
 
diff --git a/sound/soc/codecs/wm8711.c b/sound/soc/codecs/wm8711.c
index 121e46d53779..cc8251f09f8a 100644
--- a/sound/soc/codecs/wm8711.c
+++ b/sound/soc/codecs/wm8711.c
@@ -310,7 +310,7 @@ static int wm8711_set_bias_level(struct snd_soc_codec *codec,
 	case SND_SOC_BIAS_PREPARE:
 		break;
 	case SND_SOC_BIAS_STANDBY:
-		if (codec->dapm.bias_level == SND_SOC_BIAS_OFF)
+		if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF)
 			regcache_sync(wm8711->regmap);
 
 		snd_soc_write(codec, WM8711_PWR, reg | 0x0040);
@@ -320,7 +320,6 @@ static int wm8711_set_bias_level(struct snd_soc_codec *codec,
 		snd_soc_write(codec, WM8711_PWR, 0xffff);
 		break;
 	}
-	codec->dapm.bias_level = level;
 	return 0;
 }
 
diff --git a/sound/soc/codecs/wm8728.c b/sound/soc/codecs/wm8728.c
index 55c7fb4fc786..f1a173e6ec33 100644
--- a/sound/soc/codecs/wm8728.c
+++ b/sound/soc/codecs/wm8728.c
@@ -170,7 +170,7 @@ static int wm8728_set_bias_level(struct snd_soc_codec *codec,
 	case SND_SOC_BIAS_ON:
 	case SND_SOC_BIAS_PREPARE:
 	case SND_SOC_BIAS_STANDBY:
-		if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) {
+		if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) {
 			/* Power everything up... */
 			reg = snd_soc_read(codec, WM8728_DACCTL);
 			snd_soc_write(codec, WM8728_DACCTL, reg & ~0x4);
@@ -185,7 +185,6 @@ static int wm8728_set_bias_level(struct snd_soc_codec *codec,
 		snd_soc_write(codec, WM8728_DACCTL, reg | 0x4);
 		break;
 	}
-	codec->dapm.bias_level = level;
 	return 0;
 }
 
diff --git a/sound/soc/codecs/wm8731.c b/sound/soc/codecs/wm8731.c
index 2245b6a32f3d..915ea11ad4b6 100644
--- a/sound/soc/codecs/wm8731.c
+++ b/sound/soc/codecs/wm8731.c
@@ -387,6 +387,7 @@ static int wm8731_set_dai_sysclk(struct snd_soc_dai *codec_dai,
 		int clk_id, unsigned int freq, int dir)
 {
 	struct snd_soc_codec *codec = codec_dai->codec;
+	struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
 	struct wm8731_priv *wm8731 = snd_soc_codec_get_drvdata(codec);
 
 	switch (clk_id) {
@@ -421,7 +422,7 @@ static int wm8731_set_dai_sysclk(struct snd_soc_dai *codec_dai,
 
 	wm8731->sysclk = freq;
 
-	snd_soc_dapm_sync(&codec->dapm);
+	snd_soc_dapm_sync(dapm);
 
 	return 0;
 }
@@ -501,7 +502,7 @@ static int wm8731_set_bias_level(struct snd_soc_codec *codec,
 	case SND_SOC_BIAS_PREPARE:
 		break;
 	case SND_SOC_BIAS_STANDBY:
-		if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) {
+		if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) {
 			ret = regulator_bulk_enable(ARRAY_SIZE(wm8731->supplies),
 						    wm8731->supplies);
 			if (ret != 0)
@@ -523,7 +524,6 @@ static int wm8731_set_bias_level(struct snd_soc_codec *codec,
 		regcache_mark_dirty(wm8731->regmap);
 		break;
 	}
-	codec->dapm.bias_level = level;
 	return 0;
 }
 
@@ -599,7 +599,7 @@ static int wm8731_probe(struct snd_soc_codec *codec)
 		goto err_regulator_enable;
 	}
 
-	wm8731_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+	snd_soc_codec_force_bias_level(codec, SND_SOC_BIAS_STANDBY);
 
 	/* Latch the update bits */
 	snd_soc_update_bits(codec, WM8731_LOUT1V, 0x100, 0);
diff --git a/sound/soc/codecs/wm8737.c b/sound/soc/codecs/wm8737.c
index 51171e457fa4..6ad606fd8b69 100644
--- a/sound/soc/codecs/wm8737.c
+++ b/sound/soc/codecs/wm8737.c
@@ -469,7 +469,7 @@ static int wm8737_set_bias_level(struct snd_soc_codec *codec,
 		break;
 
 	case SND_SOC_BIAS_STANDBY:
-		if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) {
+		if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) {
 			ret = regulator_bulk_enable(ARRAY_SIZE(wm8737->supplies),
 						    wm8737->supplies);
 			if (ret != 0) {
@@ -512,7 +512,6 @@ static int wm8737_set_bias_level(struct snd_soc_codec *codec,
 		break;
 	}
 
-	codec->dapm.bias_level = level;
 	return 0;
 }
 
@@ -562,7 +561,7 @@ static int wm8737_probe(struct snd_soc_codec *codec)
 	snd_soc_update_bits(codec, WM8737_RIGHT_PGA_VOLUME, WM8737_RVU,
 			    WM8737_RVU);
 
-	wm8737_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+	snd_soc_codec_force_bias_level(codec, SND_SOC_BIAS_STANDBY);
 
 	/* Bias level configuration will have done an extra enable */
 	regulator_bulk_disable(ARRAY_SIZE(wm8737->supplies), wm8737->supplies);
diff --git a/sound/soc/codecs/wm8741.c b/sound/soc/codecs/wm8741.c
index 9e71c768966f..09ff01f2fc1e 100644
--- a/sound/soc/codecs/wm8741.c
+++ b/sound/soc/codecs/wm8741.c
@@ -41,6 +41,7 @@ static const char *wm8741_supply_names[WM8741_NUM_SUPPLIES] = {
 
 /* codec private data */
 struct wm8741_priv {
+	struct wm8741_platform_data pdata;
 	struct regmap *regmap;
 	struct regulator_bulk_data supplies[WM8741_NUM_SUPPLIES];
 	unsigned int sysclk;
@@ -87,13 +88,27 @@ static int wm8741_reset(struct snd_soc_codec *codec)
 static const DECLARE_TLV_DB_SCALE(dac_tlv_fine, -12700, 13, 0);
 static const DECLARE_TLV_DB_SCALE(dac_tlv, -12700, 400, 0);
 
-static const struct snd_kcontrol_new wm8741_snd_controls[] = {
+static const struct snd_kcontrol_new wm8741_snd_controls_stereo[] = {
 SOC_DOUBLE_R_TLV("Fine Playback Volume", WM8741_DACLLSB_ATTENUATION,
 		 WM8741_DACRLSB_ATTENUATION, 1, 255, 1, dac_tlv_fine),
 SOC_DOUBLE_R_TLV("Playback Volume", WM8741_DACLMSB_ATTENUATION,
 		 WM8741_DACRMSB_ATTENUATION, 0, 511, 1, dac_tlv),
 };
 
+static const struct snd_kcontrol_new wm8741_snd_controls_mono_left[] = {
+SOC_SINGLE_TLV("Fine Playback Volume", WM8741_DACLLSB_ATTENUATION,
+		 1, 255, 1, dac_tlv_fine),
+SOC_SINGLE_TLV("Playback Volume", WM8741_DACLMSB_ATTENUATION,
+		 0, 511, 1, dac_tlv),
+};
+
+static const struct snd_kcontrol_new wm8741_snd_controls_mono_right[] = {
+SOC_SINGLE_TLV("Fine Playback Volume", WM8741_DACRLSB_ATTENUATION,
+		1, 255, 1, dac_tlv_fine),
+SOC_SINGLE_TLV("Playback Volume", WM8741_DACRMSB_ATTENUATION,
+		0, 511, 1, dac_tlv),
+};
+
 static const struct snd_soc_dapm_widget wm8741_dapm_widgets[] = {
 SND_SOC_DAPM_DAC("DACL", "Playback", SND_SOC_NOPM, 0, 0),
 SND_SOC_DAPM_DAC("DACR", "Playback", SND_SOC_NOPM, 0, 0),
@@ -398,7 +413,7 @@ static struct snd_soc_dai_driver wm8741_dai = {
 	.name = "wm8741",
 	.playback = {
 		.stream_name = "Playback",
-		.channels_min = 2,  /* Mono modes not yet supported */
+		.channels_min = 2,
 		.channels_max = 2,
 		.rates = WM8741_RATES,
 		.formats = WM8741_FORMATS,
@@ -416,6 +431,65 @@ static int wm8741_resume(struct snd_soc_codec *codec)
 #define wm8741_resume NULL
 #endif
 
+static int wm8741_configure(struct snd_soc_codec *codec)
+{
+	struct wm8741_priv *wm8741 = snd_soc_codec_get_drvdata(codec);
+
+	/* Configure differential mode */
+	switch (wm8741->pdata.diff_mode) {
+	case WM8741_DIFF_MODE_STEREO:
+	case WM8741_DIFF_MODE_STEREO_REVERSED:
+	case WM8741_DIFF_MODE_MONO_LEFT:
+	case WM8741_DIFF_MODE_MONO_RIGHT:
+		snd_soc_update_bits(codec, WM8741_MODE_CONTROL_2,
+				WM8741_DIFF_MASK,
+				wm8741->pdata.diff_mode << WM8741_DIFF_SHIFT);
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	/* Change some default settings - latch VU */
+	snd_soc_update_bits(codec, WM8741_DACLLSB_ATTENUATION,
+			WM8741_UPDATELL, WM8741_UPDATELL);
+	snd_soc_update_bits(codec, WM8741_DACLMSB_ATTENUATION,
+			WM8741_UPDATELM, WM8741_UPDATELM);
+	snd_soc_update_bits(codec, WM8741_DACRLSB_ATTENUATION,
+			WM8741_UPDATERL, WM8741_UPDATERL);
+	snd_soc_update_bits(codec, WM8741_DACRMSB_ATTENUATION,
+			WM8741_UPDATERM, WM8741_UPDATERM);
+
+	return 0;
+}
+
+static int wm8741_add_controls(struct snd_soc_codec *codec)
+{
+	struct wm8741_priv *wm8741 = snd_soc_codec_get_drvdata(codec);
+
+	switch (wm8741->pdata.diff_mode) {
+	case WM8741_DIFF_MODE_STEREO:
+	case WM8741_DIFF_MODE_STEREO_REVERSED:
+		snd_soc_add_codec_controls(codec,
+				wm8741_snd_controls_stereo,
+				ARRAY_SIZE(wm8741_snd_controls_stereo));
+		break;
+	case WM8741_DIFF_MODE_MONO_LEFT:
+		snd_soc_add_codec_controls(codec,
+				wm8741_snd_controls_mono_left,
+				ARRAY_SIZE(wm8741_snd_controls_mono_left));
+		break;
+	case WM8741_DIFF_MODE_MONO_RIGHT:
+		snd_soc_add_codec_controls(codec,
+				wm8741_snd_controls_mono_right,
+				ARRAY_SIZE(wm8741_snd_controls_mono_right));
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
 static int wm8741_probe(struct snd_soc_codec *codec)
 {
 	struct wm8741_priv *wm8741 = snd_soc_codec_get_drvdata(codec);
@@ -434,15 +508,17 @@ static int wm8741_probe(struct snd_soc_codec *codec)
 		goto err_enable;
 	}
 
-	/* Change some default settings - latch VU */
-	snd_soc_update_bits(codec, WM8741_DACLLSB_ATTENUATION,
-			    WM8741_UPDATELL, WM8741_UPDATELL);
-	snd_soc_update_bits(codec, WM8741_DACLMSB_ATTENUATION,
-			    WM8741_UPDATELM, WM8741_UPDATELM);
-	snd_soc_update_bits(codec, WM8741_DACRLSB_ATTENUATION,
-			    WM8741_UPDATERL, WM8741_UPDATERL);
-	snd_soc_update_bits(codec, WM8741_DACRMSB_ATTENUATION,
-			    WM8741_UPDATERM, WM8741_UPDATERM);
+	ret = wm8741_configure(codec);
+	if (ret < 0) {
+		dev_err(codec->dev, "Failed to change default settings\n");
+		goto err_enable;
+	}
+
+	ret = wm8741_add_controls(codec);
+	if (ret < 0) {
+		dev_err(codec->dev, "Failed to add controls\n");
+		goto err_enable;
+	}
 
 	dev_dbg(codec->dev, "Successful registration\n");
 	return ret;
@@ -467,8 +543,6 @@ static struct snd_soc_codec_driver soc_codec_dev_wm8741 = {
 	.remove =	wm8741_remove,
 	.resume =	wm8741_resume,
 
-	.controls = wm8741_snd_controls,
-	.num_controls = ARRAY_SIZE(wm8741_snd_controls),
 	.dapm_widgets = wm8741_dapm_widgets,
 	.num_dapm_widgets = ARRAY_SIZE(wm8741_dapm_widgets),
 	.dapm_routes = wm8741_dapm_routes,
@@ -493,6 +567,23 @@ static const struct regmap_config wm8741_regmap = {
 	.readable_reg = wm8741_readable,
 };
 
+static int wm8741_set_pdata(struct device *dev, struct wm8741_priv *wm8741)
+{
+	const struct wm8741_platform_data *pdata = dev_get_platdata(dev);
+	u32 diff_mode;
+
+	if (dev->of_node) {
+		if (of_property_read_u32(dev->of_node, "diff-mode", &diff_mode)
+				>= 0)
+			wm8741->pdata.diff_mode = diff_mode;
+	} else {
+		if (pdata != NULL)
+			memcpy(&wm8741->pdata, pdata, sizeof(wm8741->pdata));
+	}
+
+	return 0;
+}
+
 #if IS_ENABLED(CONFIG_I2C)
 static int wm8741_i2c_probe(struct i2c_client *i2c,
 			    const struct i2c_device_id *id)
@@ -522,6 +613,12 @@ static int wm8741_i2c_probe(struct i2c_client *i2c,
 		return ret;
 	}
 
+	ret = wm8741_set_pdata(&i2c->dev, wm8741);
+	if (ret != 0) {
+		dev_err(&i2c->dev, "Failed to set pdata: %d\n", ret);
+		return ret;
+	}
+
 	i2c_set_clientdata(i2c, wm8741);
 
 	ret = snd_soc_register_codec(&i2c->dev,
@@ -582,6 +679,12 @@ static int wm8741_spi_probe(struct spi_device *spi)
 		return ret;
 	}
 
+	ret = wm8741_set_pdata(&spi->dev, wm8741);
+	if (ret != 0) {
+		dev_err(&spi->dev, "Failed to set pdata: %d\n", ret);
+		return ret;
+	}
+
 	spi_set_drvdata(spi, wm8741);
 
 	ret = snd_soc_register_codec(&spi->dev,
diff --git a/sound/soc/codecs/wm8741.h b/sound/soc/codecs/wm8741.h
index 56c1b1d4a681..c8835f65f342 100644
--- a/sound/soc/codecs/wm8741.h
+++ b/sound/soc/codecs/wm8741.h
@@ -194,6 +194,12 @@
 #define WM8741_DITHER_SHIFT                          0  /* DITHER - [1:0] */
 #define WM8741_DITHER_WIDTH                          2  /* DITHER - [1:0] */
 
+/* DIFF field values */
+#define WM8741_DIFF_MODE_STEREO                      0  /* stereo normal */
+#define WM8741_DIFF_MODE_STEREO_REVERSED             2  /* stereo reversed */
+#define WM8741_DIFF_MODE_MONO_LEFT                   1  /* mono left */
+#define WM8741_DIFF_MODE_MONO_RIGHT                  3  /* mono right */
+
 /*
  * R32 (0x20) - ADDITONAL_CONTROL_1
  */
@@ -208,4 +214,8 @@
 
 #define  WM8741_SYSCLK 0
 
+struct wm8741_platform_data {
+	u32 diff_mode;   /* Differential Output Mode */
+};
+
 #endif
diff --git a/sound/soc/codecs/wm8750.c b/sound/soc/codecs/wm8750.c
index eb0a1644ba11..56d89b0865fa 100644
--- a/sound/soc/codecs/wm8750.c
+++ b/sound/soc/codecs/wm8750.c
@@ -634,7 +634,7 @@ static int wm8750_set_bias_level(struct snd_soc_codec *codec,
 	case SND_SOC_BIAS_PREPARE:
 		break;
 	case SND_SOC_BIAS_STANDBY:
-		if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) {
+		if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) {
 			snd_soc_cache_sync(codec);
 
 			/* Set VMID to 5k */
@@ -651,7 +651,6 @@ static int wm8750_set_bias_level(struct snd_soc_codec *codec,
 		snd_soc_write(codec, WM8750_PWR1, 0x0001);
 		break;
 	}
-	codec->dapm.bias_level = level;
 	return 0;
 }
 
diff --git a/sound/soc/codecs/wm8753.c b/sound/soc/codecs/wm8753.c
index c50a5959345f..feb2997a377a 100644
--- a/sound/soc/codecs/wm8753.c
+++ b/sound/soc/codecs/wm8753.c
@@ -1352,7 +1352,7 @@ static int wm8753_set_bias_level(struct snd_soc_codec *codec,
 		flush_delayed_work(&wm8753->charge_work);
 		break;
 	case SND_SOC_BIAS_STANDBY:
-		if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) {
+		if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) {
 			/* set vmid to 5k for quick power up */
 			snd_soc_write(codec, WM8753_PWR1, pwr_reg | 0x01c1);
 			schedule_delayed_work(&wm8753->charge_work,
@@ -1367,7 +1367,6 @@ static int wm8753_set_bias_level(struct snd_soc_codec *codec,
 		snd_soc_write(codec, WM8753_PWR1, 0x0001);
 		break;
 	}
-	codec->dapm.bias_level = level;
 	return 0;
 }
 
diff --git a/sound/soc/codecs/wm8770.c b/sound/soc/codecs/wm8770.c
index 53e977da2f86..66c1f151071d 100644
--- a/sound/soc/codecs/wm8770.c
+++ b/sound/soc/codecs/wm8770.c
@@ -510,7 +510,7 @@ static int wm8770_set_bias_level(struct snd_soc_codec *codec,
 	case SND_SOC_BIAS_PREPARE:
 		break;
 	case SND_SOC_BIAS_STANDBY:
-		if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) {
+		if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) {
 			ret = regulator_bulk_enable(ARRAY_SIZE(wm8770->supplies),
 						    wm8770->supplies);
 			if (ret) {
@@ -534,7 +534,6 @@ static int wm8770_set_bias_level(struct snd_soc_codec *codec,
 		break;
 	}
 
-	codec->dapm.bias_level = level;
 	return 0;
 }
 
diff --git a/sound/soc/codecs/wm8776.c b/sound/soc/codecs/wm8776.c
index c13050b77931..ece9b4456767 100644
--- a/sound/soc/codecs/wm8776.c
+++ b/sound/soc/codecs/wm8776.c
@@ -344,7 +344,7 @@ static int wm8776_set_bias_level(struct snd_soc_codec *codec,
 	case SND_SOC_BIAS_PREPARE:
 		break;
 	case SND_SOC_BIAS_STANDBY:
-		if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) {
+		if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) {
 			regcache_sync(wm8776->regmap);
 
 			/* Disable the global powerdown; DAPM does the rest */
@@ -357,7 +357,6 @@ static int wm8776_set_bias_level(struct snd_soc_codec *codec,
 		break;
 	}
 
-	codec->dapm.bias_level = level;
 	return 0;
 }
 
diff --git a/sound/soc/codecs/wm8804.c b/sound/soc/codecs/wm8804.c
index 1e403f67cf16..c195c2e8af07 100644
--- a/sound/soc/codecs/wm8804.c
+++ b/sound/soc/codecs/wm8804.c
@@ -162,7 +162,7 @@ static int txsrc_put(struct snd_kcontrol *kcontrol,
 		     struct snd_ctl_elem_value *ucontrol)
 {
 	struct snd_soc_codec *codec = snd_soc_dapm_kcontrol_codec(kcontrol);
-	struct snd_soc_dapm_context *dapm = &codec->dapm;
+	struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
 	struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
 	unsigned int val = ucontrol->value.enumerated.item[0] << e->shift_l;
 	unsigned int mask = 1 << e->shift_l;
diff --git a/sound/soc/codecs/wm8900.c b/sound/soc/codecs/wm8900.c
index fdb765600a10..f3759ec5a863 100644
--- a/sound/soc/codecs/wm8900.c
+++ b/sound/soc/codecs/wm8900.c
@@ -1049,7 +1049,7 @@ static int wm8900_set_bias_level(struct snd_soc_codec *codec,
 
 	case SND_SOC_BIAS_STANDBY:
 		/* Charge capacitors if initial power up */
-		if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) {
+		if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) {
 			/* STARTUP_BIAS_ENA on */
 			snd_soc_write(codec, WM8900_REG_POWER1,
 				     WM8900_REG_POWER1_STARTUP_BIAS_ENA);
@@ -1117,7 +1117,6 @@ static int wm8900_set_bias_level(struct snd_soc_codec *codec,
 			     WM8900_REG_POWER2_SYSCLK_ENA);
 		break;
 	}
-	codec->dapm.bias_level = level;
 	return 0;
 }
 
@@ -1138,7 +1137,7 @@ static int wm8900_suspend(struct snd_soc_codec *codec)
 	wm8900->fll_out = fll_out;
 	wm8900->fll_in = fll_in;
 
-	wm8900_set_bias_level(codec, SND_SOC_BIAS_OFF);
+	snd_soc_codec_force_bias_level(codec, SND_SOC_BIAS_OFF);
 
 	return 0;
 }
@@ -1156,7 +1155,7 @@ static int wm8900_resume(struct snd_soc_codec *codec)
 		return ret;
 	}
 
-	wm8900_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+	snd_soc_codec_force_bias_level(codec, SND_SOC_BIAS_STANDBY);
 
 	/* Restart the FLL? */
 	if (wm8900->fll_out) {
@@ -1189,7 +1188,7 @@ static int wm8900_probe(struct snd_soc_codec *codec)
 	wm8900_reset(codec);
 
 	/* Turn the chip on */
-	wm8900_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+	snd_soc_codec_force_bias_level(codec, SND_SOC_BIAS_STANDBY);
 
 	/* Latch the volume update bits */
 	snd_soc_update_bits(codec, WM8900_REG_LINVOL, 0x100, 0x100);
diff --git a/sound/soc/codecs/wm8903.c b/sound/soc/codecs/wm8903.c
index 04b04f8e147c..b5322c1544fb 100644
--- a/sound/soc/codecs/wm8903.c
+++ b/sound/soc/codecs/wm8903.c
@@ -1105,7 +1105,7 @@ static int wm8903_set_bias_level(struct snd_soc_codec *codec,
 		break;
 
 	case SND_SOC_BIAS_STANDBY:
-		if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) {
+		if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) {
 			snd_soc_update_bits(codec, WM8903_BIAS_CONTROL_0,
 					    WM8903_POBCTRL | WM8903_ISEL_MASK |
 					    WM8903_STARTUP_BIAS_ENA |
@@ -1200,8 +1200,6 @@ static int wm8903_set_bias_level(struct snd_soc_codec *codec,
 		break;
 	}
 
-	codec->dapm.bias_level = level;
-
 	return 0;
 }
 
diff --git a/sound/soc/codecs/wm8904.c b/sound/soc/codecs/wm8904.c
index 215e93c1ddf0..265a4a58a2d1 100644
--- a/sound/soc/codecs/wm8904.c
+++ b/sound/soc/codecs/wm8904.c
@@ -1168,7 +1168,7 @@ static const struct snd_soc_dapm_route wm8912_intercon[] = {
 static int wm8904_add_widgets(struct snd_soc_codec *codec)
 {
 	struct wm8904_priv *wm8904 = snd_soc_codec_get_drvdata(codec);
-	struct snd_soc_dapm_context *dapm = &codec->dapm;
+	struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
 
 	snd_soc_dapm_new_controls(dapm, wm8904_core_dapm_widgets,
 				  ARRAY_SIZE(wm8904_core_dapm_widgets));
@@ -1852,7 +1852,7 @@ static int wm8904_set_bias_level(struct snd_soc_codec *codec,
 		break;
 
 	case SND_SOC_BIAS_STANDBY:
-		if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) {
+		if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) {
 			ret = regulator_bulk_enable(ARRAY_SIZE(wm8904->supplies),
 						    wm8904->supplies);
 			if (ret != 0) {
@@ -1907,7 +1907,6 @@ static int wm8904_set_bias_level(struct snd_soc_codec *codec,
 		clk_disable_unprepare(wm8904->mclk);
 		break;
 	}
-	codec->dapm.bias_level = level;
 	return 0;
 }
 
diff --git a/sound/soc/codecs/wm8940.c b/sound/soc/codecs/wm8940.c
index e4142b4309eb..98ef0ba5c2a4 100644
--- a/sound/soc/codecs/wm8940.c
+++ b/sound/soc/codecs/wm8940.c
@@ -492,7 +492,7 @@ static int wm8940_set_bias_level(struct snd_soc_codec *codec,
 		ret = snd_soc_write(codec, WM8940_POWER1, pwr_reg | 0x1);
 		break;
 	case SND_SOC_BIAS_STANDBY:
-		if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) {
+		if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) {
 			ret = regcache_sync(wm8940->regmap);
 			if (ret < 0) {
 				dev_err(codec->dev, "Failed to sync cache: %d\n", ret);
@@ -510,8 +510,6 @@ static int wm8940_set_bias_level(struct snd_soc_codec *codec,
 		break;
 	}
 
-	codec->dapm.bias_level = level;
-
 	return ret;
 }
 
@@ -707,7 +705,7 @@ static int wm8940_probe(struct snd_soc_codec *codec)
 		return ret;
 	}
 
-	wm8940_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+	snd_soc_codec_force_bias_level(codec, SND_SOC_BIAS_STANDBY);
 
 	ret = snd_soc_write(codec, WM8940_POWER1, 0x180);
 	if (ret < 0)
diff --git a/sound/soc/codecs/wm8955.c b/sound/soc/codecs/wm8955.c
index 03e04bf6c5ba..2d591c24704b 100644
--- a/sound/soc/codecs/wm8955.c
+++ b/sound/soc/codecs/wm8955.c
@@ -785,7 +785,7 @@ static int wm8955_set_bias_level(struct snd_soc_codec *codec,
 		break;
 
 	case SND_SOC_BIAS_STANDBY:
-		if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) {
+		if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) {
 			ret = regulator_bulk_enable(ARRAY_SIZE(wm8955->supplies),
 						    wm8955->supplies);
 			if (ret != 0) {
@@ -838,7 +838,6 @@ static int wm8955_set_bias_level(struct snd_soc_codec *codec,
 				       wm8955->supplies);
 		break;
 	}
-	codec->dapm.bias_level = level;
 	return 0;
 }
 
@@ -929,7 +928,7 @@ static int wm8955_probe(struct snd_soc_codec *codec)
 					    WM8955_DMEN, WM8955_DMEN);
 	}
 
-	wm8955_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+	snd_soc_codec_force_bias_level(codec, SND_SOC_BIAS_STANDBY);
 
 	/* Bias level configuration will have done an extra enable */
 	regulator_bulk_disable(ARRAY_SIZE(wm8955->supplies), wm8955->supplies);
diff --git a/sound/soc/codecs/wm8960.c b/sound/soc/codecs/wm8960.c
index 8d7f63253440..94c5c4681ce5 100644
--- a/sound/soc/codecs/wm8960.c
+++ b/sound/soc/codecs/wm8960.c
@@ -127,6 +127,8 @@ struct wm8960_priv {
 	struct snd_soc_dapm_widget *out3;
 	bool deemph;
 	int playback_fs;
+	int bclk;
+	int sysclk;
 	struct wm8960_data pdata;
 };
 
@@ -445,7 +447,7 @@ static int wm8960_add_widgets(struct snd_soc_codec *codec)
 {
 	struct wm8960_priv *wm8960 = snd_soc_codec_get_drvdata(codec);
 	struct wm8960_data *pdata = &wm8960->pdata;
-	struct snd_soc_dapm_context *dapm = &codec->dapm;
+	struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
 	struct snd_soc_dapm_widget *w;
 
 	snd_soc_dapm_new_controls(dapm, wm8960_dapm_widgets,
@@ -476,7 +478,7 @@ static int wm8960_add_widgets(struct snd_soc_codec *codec)
 	 * and save the result.
 	 */
 	list_for_each_entry(w, &codec->component.card->widgets, list) {
-		if (w->dapm != &codec->dapm)
+		if (w->dapm != dapm)
 			continue;
 		if (strcmp(w->name, "LOUT1 PGA") == 0)
 			wm8960->lout1 = w;
@@ -563,6 +565,72 @@ static struct {
 	{  8000, 5 },
 };
 
+/* Multiply 256 for internal 256 div */
+static const int dac_divs[] = { 256, 384, 512, 768, 1024, 1408, 1536 };
+
+/* Multiply 10 to eliminate decimials */
+static const int bclk_divs[] = {
+	10, 15, 20, 30, 40, 55, 60, 80, 110,
+	120, 160, 220, 240, 320, 320, 320
+};
+
+static void wm8960_configure_clocking(struct snd_soc_codec *codec,
+		bool tx, int lrclk)
+{
+	struct wm8960_priv *wm8960 = snd_soc_codec_get_drvdata(codec);
+	u16 iface1 = snd_soc_read(codec, WM8960_IFACE1);
+	u16 iface2 = snd_soc_read(codec, WM8960_IFACE2);
+	u32 sysclk;
+	int i, j;
+
+	if (!(iface1 & (1<<6))) {
+		dev_dbg(codec->dev,
+			"Codec is slave mode, no need to configure clock\n");
+		return;
+	}
+
+	if (!wm8960->sysclk) {
+		dev_dbg(codec->dev, "No SYSCLK configured\n");
+		return;
+	}
+
+	if (!wm8960->bclk || !lrclk) {
+		dev_dbg(codec->dev, "No audio clocks configured\n");
+		return;
+	}
+
+	for (i = 0; i < ARRAY_SIZE(dac_divs); ++i) {
+		if (wm8960->sysclk == lrclk * dac_divs[i]) {
+			for (j = 0; j < ARRAY_SIZE(bclk_divs); ++j) {
+				sysclk = wm8960->bclk * bclk_divs[j] / 10;
+				if (wm8960->sysclk == sysclk)
+					break;
+			}
+			if(j != ARRAY_SIZE(bclk_divs))
+				break;
+		}
+	}
+
+	if (i == ARRAY_SIZE(dac_divs)) {
+		dev_err(codec->dev, "Unsupported sysclk %d\n", wm8960->sysclk);
+		return;
+	}
+
+	/*
+	 * configure frame clock. If ADCLRC configure as GPIO pin, DACLRC
+	 * pin is used as a frame clock for ADCs and DACs.
+	 */
+	if (iface2 & (1<<6))
+		snd_soc_update_bits(codec, WM8960_CLOCK1, 0x7 << 3, i << 3);
+	else if (tx)
+		snd_soc_update_bits(codec, WM8960_CLOCK1, 0x7 << 3, i << 3);
+	else if (!tx)
+		snd_soc_update_bits(codec, WM8960_CLOCK1, 0x7 << 6, i << 6);
+
+	/* configure bit clock */
+	snd_soc_update_bits(codec, WM8960_CLOCK2, 0xf, j);
+}
+
 static int wm8960_hw_params(struct snd_pcm_substream *substream,
 			    struct snd_pcm_hw_params *params,
 			    struct snd_soc_dai *dai)
@@ -570,8 +638,13 @@ static int wm8960_hw_params(struct snd_pcm_substream *substream,
 	struct snd_soc_codec *codec = dai->codec;
 	struct wm8960_priv *wm8960 = snd_soc_codec_get_drvdata(codec);
 	u16 iface = snd_soc_read(codec, WM8960_IFACE1) & 0xfff3;
+	bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
 	int i;
 
+	wm8960->bclk = snd_soc_params_to_bclk(params);
+	if (params_channels(params) == 1)
+		wm8960->bclk *= 2;
+
 	/* bit size */
 	switch (params_width(params)) {
 	case 16:
@@ -582,6 +655,12 @@ static int wm8960_hw_params(struct snd_pcm_substream *substream,
 	case 24:
 		iface |= 0x0008;
 		break;
+	case 32:
+		/* right justify mode does not support 32 word length */
+		if ((iface & 0x3) != 0) {
+			iface |= 0x000c;
+			break;
+		}
 	default:
 		dev_err(codec->dev, "unsupported width %d\n",
 			params_width(params));
@@ -602,6 +681,9 @@ static int wm8960_hw_params(struct snd_pcm_substream *substream,
 
 	/* set iface */
 	snd_soc_write(codec, WM8960_IFACE1, iface);
+
+	wm8960_configure_clocking(codec, tx, params_rate(params));
+
 	return 0;
 }
 
@@ -627,7 +709,7 @@ static int wm8960_set_bias_level_out3(struct snd_soc_codec *codec,
 		break;
 
 	case SND_SOC_BIAS_PREPARE:
-		switch (codec->dapm.bias_level) {
+		switch (snd_soc_codec_get_bias_level(codec)) {
 		case SND_SOC_BIAS_STANDBY:
 			if (!IS_ERR(wm8960->mclk)) {
 				ret = clk_prepare_enable(wm8960->mclk);
@@ -655,7 +737,7 @@ static int wm8960_set_bias_level_out3(struct snd_soc_codec *codec,
 		break;
 
 	case SND_SOC_BIAS_STANDBY:
-		if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) {
+		if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) {
 			regcache_sync(wm8960->regmap);
 
 			/* Enable anti-pop features */
@@ -691,8 +773,6 @@ static int wm8960_set_bias_level_out3(struct snd_soc_codec *codec,
 		break;
 	}
 
-	codec->dapm.bias_level = level;
-
 	return 0;
 }
 
@@ -707,7 +787,7 @@ static int wm8960_set_bias_level_capless(struct snd_soc_codec *codec,
 		break;
 
 	case SND_SOC_BIAS_PREPARE:
-		switch (codec->dapm.bias_level) {
+		switch (snd_soc_codec_get_bias_level(codec)) {
 		case SND_SOC_BIAS_STANDBY:
 			/* Enable anti pop mode */
 			snd_soc_update_bits(codec, WM8960_APOP1,
@@ -778,7 +858,7 @@ static int wm8960_set_bias_level_capless(struct snd_soc_codec *codec,
 		break;
 
 	case SND_SOC_BIAS_STANDBY:
-		switch (codec->dapm.bias_level) {
+		switch (snd_soc_codec_get_bias_level(codec)) {
 		case SND_SOC_BIAS_PREPARE:
 			/* Disable HP discharge */
 			snd_soc_update_bits(codec, WM8960_APOP2,
@@ -802,8 +882,6 @@ static int wm8960_set_bias_level_capless(struct snd_soc_codec *codec,
 		break;
 	}
 
-	codec->dapm.bias_level = level;
-
 	return 0;
 }
 
@@ -950,11 +1028,35 @@ static int wm8960_set_bias_level(struct snd_soc_codec *codec,
 	return wm8960->set_bias_level(codec, level);
 }
 
+static int wm8960_set_dai_sysclk(struct snd_soc_dai *dai, int clk_id,
+					unsigned int freq, int dir)
+{
+	struct snd_soc_codec *codec = dai->codec;
+	struct wm8960_priv *wm8960 = snd_soc_codec_get_drvdata(codec);
+
+	switch (clk_id) {
+	case WM8960_SYSCLK_MCLK:
+		snd_soc_update_bits(codec, WM8960_CLOCK1,
+					0x1, WM8960_SYSCLK_MCLK);
+		break;
+	case WM8960_SYSCLK_PLL:
+		snd_soc_update_bits(codec, WM8960_CLOCK1,
+					0x1, WM8960_SYSCLK_PLL);
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	wm8960->sysclk = freq;
+
+	return 0;
+}
+
 #define WM8960_RATES SNDRV_PCM_RATE_8000_48000
 
 #define WM8960_FORMATS \
 	(SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \
-	SNDRV_PCM_FMTBIT_S24_LE)
+	SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
 
 static const struct snd_soc_dai_ops wm8960_dai_ops = {
 	.hw_params = wm8960_hw_params,
@@ -962,6 +1064,7 @@ static const struct snd_soc_dai_ops wm8960_dai_ops = {
 	.set_fmt = wm8960_set_dai_fmt,
 	.set_clkdiv = wm8960_set_dai_clkdiv,
 	.set_pll = wm8960_set_dai_pll,
+	.set_sysclk = wm8960_set_dai_sysclk,
 };
 
 static struct snd_soc_dai_driver wm8960_dai = {
diff --git a/sound/soc/codecs/wm8961.c b/sound/soc/codecs/wm8961.c
index 95e2c1bfc809..a057662632ff 100644
--- a/sound/soc/codecs/wm8961.c
+++ b/sound/soc/codecs/wm8961.c
@@ -758,7 +758,7 @@ static int wm8961_set_bias_level(struct snd_soc_codec *codec,
 		break;
 
 	case SND_SOC_BIAS_PREPARE:
-		if (codec->dapm.bias_level == SND_SOC_BIAS_STANDBY) {
+		if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_STANDBY) {
 			/* Enable bias generation */
 			reg = snd_soc_read(codec, WM8961_ANTI_POP);
 			reg |= WM8961_BUFIOEN | WM8961_BUFDCOPEN;
@@ -773,7 +773,7 @@ static int wm8961_set_bias_level(struct snd_soc_codec *codec,
 		break;
 
 	case SND_SOC_BIAS_STANDBY:
-		if (codec->dapm.bias_level == SND_SOC_BIAS_PREPARE) {
+		if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_PREPARE) {
 			/* VREF off */
 			reg = snd_soc_read(codec, WM8961_PWR_MGMT_1);
 			reg &= ~WM8961_VREF;
@@ -795,8 +795,6 @@ static int wm8961_set_bias_level(struct snd_soc_codec *codec,
 		break;
 	}
 
-	codec->dapm.bias_level = level;
-
 	return 0;
 }
 
diff --git a/sound/soc/codecs/wm8962.c b/sound/soc/codecs/wm8962.c
index 118b0034ba23..c5748fd4f296 100644
--- a/sound/soc/codecs/wm8962.c
+++ b/sound/soc/codecs/wm8962.c
@@ -2361,7 +2361,7 @@ static int wm8962_add_widgets(struct snd_soc_codec *codec)
 {
 	struct wm8962_priv *wm8962 = snd_soc_codec_get_drvdata(codec);
 	struct wm8962_pdata *pdata = &wm8962->pdata;
-	struct snd_soc_dapm_context *dapm = &codec->dapm;
+	struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
 
 	snd_soc_add_codec_controls(codec, wm8962_snd_controls,
 			     ARRAY_SIZE(wm8962_snd_controls));
@@ -2446,13 +2446,13 @@ static void wm8962_configure_bclk(struct snd_soc_codec *codec)
 	 * So we here provisionally enable it and then disable it afterward
 	 * if current bias_level hasn't reached SND_SOC_BIAS_ON.
 	 */
-	if (codec->dapm.bias_level != SND_SOC_BIAS_ON)
+	if (snd_soc_codec_get_bias_level(codec) != SND_SOC_BIAS_ON)
 		snd_soc_update_bits(codec, WM8962_CLOCKING2,
 				WM8962_SYSCLK_ENA_MASK, WM8962_SYSCLK_ENA);
 
 	dspclk = snd_soc_read(codec, WM8962_CLOCKING1);
 
-	if (codec->dapm.bias_level != SND_SOC_BIAS_ON)
+	if (snd_soc_codec_get_bias_level(codec) != SND_SOC_BIAS_ON)
 		snd_soc_update_bits(codec, WM8962_CLOCKING2,
 				WM8962_SYSCLK_ENA_MASK, 0);
 
@@ -2510,9 +2510,6 @@ static void wm8962_configure_bclk(struct snd_soc_codec *codec)
 static int wm8962_set_bias_level(struct snd_soc_codec *codec,
 				 enum snd_soc_bias_level level)
 {
-	if (level == codec->dapm.bias_level)
-		return 0;
-
 	switch (level) {
 	case SND_SOC_BIAS_ON:
 		break;
@@ -2530,7 +2527,7 @@ static int wm8962_set_bias_level(struct snd_soc_codec *codec,
 		snd_soc_update_bits(codec, WM8962_PWR_MGMT_1,
 				    WM8962_VMID_SEL_MASK, 0x100);
 
-		if (codec->dapm.bias_level == SND_SOC_BIAS_OFF)
+		if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF)
 			msleep(100);
 		break;
 
@@ -2538,7 +2535,6 @@ static int wm8962_set_bias_level(struct snd_soc_codec *codec,
 		break;
 	}
 
-	codec->dapm.bias_level = level;
 	return 0;
 }
 
@@ -2614,7 +2610,7 @@ static int wm8962_hw_params(struct snd_pcm_substream *substream,
 	dev_dbg(codec->dev, "hw_params set BCLK %dHz LRCLK %dHz\n",
 		wm8962->bclk, wm8962->lrclk);
 
-	if (codec->dapm.bias_level == SND_SOC_BIAS_ON)
+	if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_ON)
 		wm8962_configure_bclk(codec);
 
 	return 0;
@@ -3118,7 +3114,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;
+	struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
 	int irq_mask, enable;
 
 	wm8962->jack = jack;
@@ -3164,7 +3160,7 @@ static void wm8962_beep_work(struct work_struct *work)
 	struct wm8962_priv *wm8962 =
 		container_of(work, struct wm8962_priv, beep_work);
 	struct snd_soc_codec *codec = wm8962->codec;
-	struct snd_soc_dapm_context *dapm = &codec->dapm;
+	struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
 	int i;
 	int reg = 0;
 	int best = 0;
@@ -3415,6 +3411,7 @@ static void wm8962_free_gpio(struct snd_soc_codec *codec)
 
 static int wm8962_probe(struct snd_soc_codec *codec)
 {
+	struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
 	int ret;
 	struct wm8962_priv *wm8962 = snd_soc_codec_get_drvdata(codec);
 	int i;
@@ -3462,7 +3459,7 @@ static int wm8962_probe(struct snd_soc_codec *codec)
 	}
 	if (!dmicclk || !dmicdat) {
 		dev_dbg(codec->dev, "DMIC not in use, disabling\n");
-		snd_soc_dapm_nc_pin(&codec->dapm, "DMICDAT");
+		snd_soc_dapm_nc_pin(dapm, "DMICDAT");
 	}
 	if (dmicclk != dmicdat)
 		dev_warn(codec->dev, "DMIC GPIOs partially configured\n");
diff --git a/sound/soc/codecs/wm8971.c b/sound/soc/codecs/wm8971.c
index f9cbabdc6238..b51184c4e816 100644
--- a/sound/soc/codecs/wm8971.c
+++ b/sound/soc/codecs/wm8971.c
@@ -577,7 +577,7 @@ static int wm8971_set_bias_level(struct snd_soc_codec *codec,
 		flush_delayed_work(&wm8971->charge_work);
 		break;
 	case SND_SOC_BIAS_STANDBY:
-		if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) {
+		if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) {
 			snd_soc_cache_sync(codec);
 			/* charge output caps - set vmid to 5k for quick power up */
 			snd_soc_write(codec, WM8971_PWR1, pwr_reg | 0x01c0);
@@ -594,7 +594,6 @@ static int wm8971_set_bias_level(struct snd_soc_codec *codec,
 		snd_soc_write(codec, WM8971_PWR1, 0x0001);
 		break;
 	}
-	codec->dapm.bias_level = level;
 	return 0;
 }
 
diff --git a/sound/soc/codecs/wm8974.c b/sound/soc/codecs/wm8974.c
index ff0e4646b934..33b16a7ba82e 100644
--- a/sound/soc/codecs/wm8974.c
+++ b/sound/soc/codecs/wm8974.c
@@ -514,7 +514,7 @@ static int wm8974_set_bias_level(struct snd_soc_codec *codec,
 	case SND_SOC_BIAS_STANDBY:
 		power1 |= WM8974_POWER1_BIASEN | WM8974_POWER1_BUFIOEN;
 
-		if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) {
+		if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) {
 			regcache_sync(dev_get_regmap(codec->dev, NULL));
 
 			/* Initial cap charge at VMID 5k */
@@ -533,7 +533,6 @@ static int wm8974_set_bias_level(struct snd_soc_codec *codec,
 		break;
 	}
 
-	codec->dapm.bias_level = level;
 	return 0;
 }
 
diff --git a/sound/soc/codecs/wm8978.c b/sound/soc/codecs/wm8978.c
index cf7032911721..cfc8cdf49970 100644
--- a/sound/soc/codecs/wm8978.c
+++ b/sound/soc/codecs/wm8978.c
@@ -868,7 +868,7 @@ static int wm8978_set_bias_level(struct snd_soc_codec *codec,
 		/* bit 3: enable bias, bit 2: enable I/O tie off buffer */
 		power1 |= 0xc;
 
-		if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) {
+		if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) {
 			/* Initial cap charge at VMID 5k */
 			snd_soc_write(codec, WM8978_POWER_MANAGEMENT_1,
 				      power1 | 0x3);
@@ -888,7 +888,6 @@ static int wm8978_set_bias_level(struct snd_soc_codec *codec,
 
 	dev_dbg(codec->dev, "%s: %d, %x\n", __func__, level, power1);
 
-	codec->dapm.bias_level = level;
 	return 0;
 }
 
@@ -928,7 +927,7 @@ static int wm8978_suspend(struct snd_soc_codec *codec)
 {
 	struct wm8978_priv *wm8978 = snd_soc_codec_get_drvdata(codec);
 
-	wm8978_set_bias_level(codec, SND_SOC_BIAS_OFF);
+	snd_soc_codec_force_bias_level(codec, SND_SOC_BIAS_OFF);
 	/* Also switch PLL off */
 	snd_soc_write(codec, WM8978_POWER_MANAGEMENT_1, 0);
 
@@ -944,7 +943,7 @@ static int wm8978_resume(struct snd_soc_codec *codec)
 	/* Sync reg_cache with the hardware */
 	regcache_sync(wm8978->regmap);
 
-	wm8978_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+	snd_soc_codec_force_bias_level(codec, SND_SOC_BIAS_STANDBY);
 
 	if (wm8978->f_pllout)
 		/* Switch PLL on */
diff --git a/sound/soc/codecs/wm8983.c b/sound/soc/codecs/wm8983.c
index 5d1cf08a72b8..2fdd2c6cc09d 100644
--- a/sound/soc/codecs/wm8983.c
+++ b/sound/soc/codecs/wm8983.c
@@ -915,7 +915,7 @@ static int wm8983_set_bias_level(struct snd_soc_codec *codec,
 				    1 << WM8983_VMIDSEL_SHIFT);
 		break;
 	case SND_SOC_BIAS_STANDBY:
-		if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) {
+		if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) {
 			ret = regcache_sync(wm8983->regmap);
 			if (ret < 0) {
 				dev_err(codec->dev, "Failed to sync cache: %d\n", ret);
@@ -963,7 +963,6 @@ static int wm8983_set_bias_level(struct snd_soc_codec *codec,
 		break;
 	}
 
-	codec->dapm.bias_level = level;
 	return 0;
 }
 
diff --git a/sound/soc/codecs/wm8985.c b/sound/soc/codecs/wm8985.c
index 0b3b54c9971d..8a85f5004d41 100644
--- a/sound/soc/codecs/wm8985.c
+++ b/sound/soc/codecs/wm8985.c
@@ -897,7 +897,7 @@ static int wm8985_set_bias_level(struct snd_soc_codec *codec,
 				    1 << WM8985_VMIDSEL_SHIFT);
 		break;
 	case SND_SOC_BIAS_STANDBY:
-		if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) {
+		if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) {
 			ret = regulator_bulk_enable(ARRAY_SIZE(wm8985->supplies),
 						    wm8985->supplies);
 			if (ret) {
@@ -957,7 +957,6 @@ static int wm8985_set_bias_level(struct snd_soc_codec *codec,
 		break;
 	}
 
-	codec->dapm.bias_level = level;
 	return 0;
 }
 
diff --git a/sound/soc/codecs/wm8988.c b/sound/soc/codecs/wm8988.c
index 24968aa8618a..f13a995af277 100644
--- a/sound/soc/codecs/wm8988.c
+++ b/sound/soc/codecs/wm8988.c
@@ -738,7 +738,7 @@ static int wm8988_set_bias_level(struct snd_soc_codec *codec,
 		break;
 
 	case SND_SOC_BIAS_STANDBY:
-		if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) {
+		if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) {
 			regcache_sync(wm8988->regmap);
 
 			/* VREF, VMID=2x5k */
@@ -756,7 +756,6 @@ static int wm8988_set_bias_level(struct snd_soc_codec *codec,
 		snd_soc_write(codec, WM8988_PWR1, 0x0000);
 		break;
 	}
-	codec->dapm.bias_level = level;
 	return 0;
 }
 
diff --git a/sound/soc/codecs/wm8990.c b/sound/soc/codecs/wm8990.c
index c93bffcb3cfb..1993fd2a6f15 100644
--- a/sound/soc/codecs/wm8990.c
+++ b/sound/soc/codecs/wm8990.c
@@ -1124,7 +1124,7 @@ static int wm8990_set_bias_level(struct snd_soc_codec *codec,
 		break;
 
 	case SND_SOC_BIAS_STANDBY:
-		if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) {
+		if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) {
 			ret = regcache_sync(wm8990->regmap);
 			if (ret < 0) {
 				dev_err(codec->dev, "Failed to sync cache: %d\n", ret);
@@ -1227,7 +1227,6 @@ static int wm8990_set_bias_level(struct snd_soc_codec *codec,
 		break;
 	}
 
-	codec->dapm.bias_level = level;
 	return 0;
 }
 
@@ -1281,7 +1280,7 @@ static int wm8990_probe(struct snd_soc_codec *codec)
 	wm8990_reset(codec);
 
 	/* charge output caps */
-	wm8990_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+	snd_soc_codec_force_bias_level(codec, SND_SOC_BIAS_STANDBY);
 
 	snd_soc_update_bits(codec, WM8990_AUDIO_INTERFACE_4,
 			    WM8990_ALRCGPIO1, WM8990_ALRCGPIO1);
diff --git a/sound/soc/codecs/wm8991.c b/sound/soc/codecs/wm8991.c
index 49df0dc607e6..44a677720828 100644
--- a/sound/soc/codecs/wm8991.c
+++ b/sound/soc/codecs/wm8991.c
@@ -1131,7 +1131,7 @@ static int wm8991_set_bias_level(struct snd_soc_codec *codec,
 		break;
 
 	case SND_SOC_BIAS_STANDBY:
-		if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) {
+		if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) {
 			regcache_sync(wm8991->regmap);
 			/* Enable all output discharge bits */
 			snd_soc_write(codec, WM8991_ANTIPOP1, WM8991_DIS_LLINE |
@@ -1224,7 +1224,6 @@ static int wm8991_set_bias_level(struct snd_soc_codec *codec,
 		break;
 	}
 
-	codec->dapm.bias_level = level;
 	return 0;
 }
 
diff --git a/sound/soc/codecs/wm8993.c b/sound/soc/codecs/wm8993.c
index 2e70a270eb28..8a8db8605dc2 100644
--- a/sound/soc/codecs/wm8993.c
+++ b/sound/soc/codecs/wm8993.c
@@ -992,7 +992,7 @@ static int wm8993_set_bias_level(struct snd_soc_codec *codec,
 		break;
 
 	case SND_SOC_BIAS_STANDBY:
-		if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) {
+		if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) {
 			ret = regulator_bulk_enable(ARRAY_SIZE(wm8993->supplies),
 						    wm8993->supplies);
 			if (ret != 0)
@@ -1065,8 +1065,6 @@ static int wm8993_set_bias_level(struct snd_soc_codec *codec,
 		break;
 	}
 
-	codec->dapm.bias_level = level;
-
 	return 0;
 }
 
@@ -1485,7 +1483,7 @@ static struct snd_soc_dai_driver wm8993_dai = {
 static int wm8993_probe(struct snd_soc_codec *codec)
 {
 	struct wm8993_priv *wm8993 = snd_soc_codec_get_drvdata(codec);
-	struct snd_soc_dapm_context *dapm = &codec->dapm;
+	struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
 
 	wm8993->hubs_data.hp_startup_mode = 1;
 	wm8993->hubs_data.dcs_codes_l = -2;
@@ -1539,7 +1537,7 @@ static int wm8993_probe(struct snd_soc_codec *codec)
 	 * VMID as an output and can disable it.
 	 */
 	if (wm8993->pdata.lineout1_diff && wm8993->pdata.lineout2_diff)
-		codec->dapm.idle_bias_off = 1;
+		dapm->idle_bias_off = 1;
 
 	return 0;
 
@@ -1563,7 +1561,7 @@ static int wm8993_suspend(struct snd_soc_codec *codec)
 	wm8993->fll_fout = fll_fout;
 	wm8993->fll_fref = fll_fref;
 
-	wm8993_set_bias_level(codec, SND_SOC_BIAS_OFF);
+	snd_soc_codec_force_bias_level(codec, SND_SOC_BIAS_OFF);
 
 	return 0;
 }
@@ -1573,7 +1571,7 @@ static int wm8993_resume(struct snd_soc_codec *codec)
 	struct wm8993_priv *wm8993 = snd_soc_codec_get_drvdata(codec);
 	int ret;
 
-	wm8993_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+	snd_soc_codec_force_bias_level(codec, SND_SOC_BIAS_STANDBY);
 
 	/* Restart the FLL? */
 	if (wm8993->fll_fout) {
diff --git a/sound/soc/codecs/wm8994.c b/sound/soc/codecs/wm8994.c
index a1c04dab6684..962e1d31a629 100644
--- a/sound/soc/codecs/wm8994.c
+++ b/sound/soc/codecs/wm8994.c
@@ -212,6 +212,7 @@ static int configure_aif_clock(struct snd_soc_codec *codec, int aif)
 
 static int configure_clock(struct snd_soc_codec *codec)
 {
+	struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
 	struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
 	int change, new;
 
@@ -239,7 +240,7 @@ static int configure_clock(struct snd_soc_codec *codec)
 	change = snd_soc_update_bits(codec, WM8994_CLOCKING_1,
 				     WM8994_SYSCLK_SRC, new);
 	if (change)
-		snd_soc_dapm_sync(&codec->dapm);
+		snd_soc_dapm_sync(dapm);
 
 	wm8958_micd_set_rate(codec);
 
@@ -2492,12 +2493,12 @@ static int wm8994_set_bias_level(struct snd_soc_codec *codec,
 			break;
 		}
 
-		if (codec->dapm.bias_level == SND_SOC_BIAS_STANDBY)
+		if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_STANDBY)
 			active_reference(codec);
 		break;
 
 	case SND_SOC_BIAS_STANDBY:
-		if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) {
+		if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) {
 			switch (control->type) {
 			case WM8958:
 				if (control->revision == 0) {
@@ -2521,7 +2522,7 @@ static int wm8994_set_bias_level(struct snd_soc_codec *codec,
 					    WM8994_LINEOUT2_DISCH);
 		}
 
-		if (codec->dapm.bias_level == SND_SOC_BIAS_PREPARE)
+		if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_PREPARE)
 			active_dereference(codec);
 
 		/* MICBIAS into bypass mode on newer devices */
@@ -2541,20 +2542,18 @@ static int wm8994_set_bias_level(struct snd_soc_codec *codec,
 		break;
 
 	case SND_SOC_BIAS_OFF:
-		if (codec->dapm.bias_level == SND_SOC_BIAS_STANDBY)
+		if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_STANDBY)
 			wm8994->cur_fw = NULL;
 		break;
 	}
 
-	codec->dapm.bias_level = level;
-
 	return 0;
 }
 
 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;
+	struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
 
 	switch (mode) {
 	case WM8994_VMID_NORMAL:
@@ -3163,7 +3162,7 @@ static int wm8994_codec_suspend(struct snd_soc_codec *codec)
 				 i + 1, ret);
 	}
 
-	wm8994_set_bias_level(codec, SND_SOC_BIAS_OFF);
+	snd_soc_codec_force_bias_level(codec, SND_SOC_BIAS_OFF);
 
 	return 0;
 }
@@ -3356,6 +3355,7 @@ static void wm8994_handle_pdata(struct wm8994_priv *wm8994)
 int wm8994_mic_detect(struct snd_soc_codec *codec, struct snd_soc_jack *jack,
 		      int micbias)
 {
+	struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
 	struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
 	struct wm8994_micdet *micdet;
 	struct wm8994 *control = wm8994->wm8994;
@@ -3370,20 +3370,16 @@ int wm8994_mic_detect(struct snd_soc_codec *codec, struct snd_soc_jack *jack,
 	case 1:
 		micdet = &wm8994->micdet[0];
 		if (jack)
-			ret = snd_soc_dapm_force_enable_pin(&codec->dapm,
-							    "MICBIAS1");
+			ret = snd_soc_dapm_force_enable_pin(dapm, "MICBIAS1");
 		else
-			ret = snd_soc_dapm_disable_pin(&codec->dapm,
-						       "MICBIAS1");
+			ret = snd_soc_dapm_disable_pin(dapm, "MICBIAS1");
 		break;
 	case 2:
 		micdet = &wm8994->micdet[1];
 		if (jack)
-			ret = snd_soc_dapm_force_enable_pin(&codec->dapm,
-							    "MICBIAS1");
+			ret = snd_soc_dapm_force_enable_pin(dapm, "MICBIAS1");
 		else
-			ret = snd_soc_dapm_disable_pin(&codec->dapm,
-						       "MICBIAS1");
+			ret = snd_soc_dapm_disable_pin(dapm, "MICBIAS1");
 		break;
 	default:
 		dev_warn(codec->dev, "Invalid MICBIAS %d\n", micbias);
@@ -3415,7 +3411,7 @@ int wm8994_mic_detect(struct snd_soc_codec *codec, struct snd_soc_jack *jack,
 			    WM8994_MIC2_DET_DB_MASK | WM8994_MIC2_SHRT_DB_MASK,
 			    WM8994_MIC1_DET_DB | WM8994_MIC1_SHRT_DB);
 
-	snd_soc_dapm_sync(&codec->dapm);
+	snd_soc_dapm_sync(dapm);
 
 	return 0;
 }
@@ -3505,6 +3501,7 @@ static irqreturn_t wm8994_mic_irq(int irq, void *data)
 /* Should be called with accdet_lock held */
 static void wm1811_micd_stop(struct snd_soc_codec *codec)
 {
+	struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
 	struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
 
 	if (!wm8994->jackdet)
@@ -3515,8 +3512,7 @@ static void wm1811_micd_stop(struct snd_soc_codec *codec)
 	wm1811_jackdet_set_mode(codec, WM1811_JACKDET_MODE_JACK);
 
 	if (wm8994->wm8994->pdata.jd_ext_cap)
-		snd_soc_dapm_disable_pin(&codec->dapm,
-					 "MICBIAS2");
+		snd_soc_dapm_disable_pin(dapm, "MICBIAS2");
 }
 
 static void wm8958_button_det(struct snd_soc_codec *codec, u16 status)
@@ -3625,14 +3621,14 @@ static void wm1811_mic_work(struct work_struct *work)
 						  mic_work.work);
 	struct wm8994 *control = wm8994->wm8994;
 	struct snd_soc_codec *codec = wm8994->hubs.codec;
+	struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
 
 	pm_runtime_get_sync(codec->dev);
 
 	/* If required for an external cap force MICBIAS on */
 	if (control->pdata.jd_ext_cap) {
-		snd_soc_dapm_force_enable_pin(&codec->dapm,
-					      "MICBIAS2");
-		snd_soc_dapm_sync(&codec->dapm);
+		snd_soc_dapm_force_enable_pin(dapm, "MICBIAS2");
+		snd_soc_dapm_sync(dapm);
 	}
 
 	mutex_lock(&wm8994->accdet_lock);
@@ -3664,6 +3660,7 @@ static irqreturn_t wm1811_jackdet_irq(int irq, void *data)
 	struct wm8994_priv *wm8994 = data;
 	struct wm8994 *control = wm8994->wm8994;
 	struct snd_soc_codec *codec = wm8994->hubs.codec;
+	struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
 	int reg, delay;
 	bool present;
 
@@ -3724,7 +3721,7 @@ static irqreturn_t wm1811_jackdet_irq(int irq, void *data)
 
 	/* Turn off MICBIAS if it was on for an external cap */
 	if (control->pdata.jd_ext_cap && !present)
-		snd_soc_dapm_disable_pin(&codec->dapm, "MICBIAS2");
+		snd_soc_dapm_disable_pin(dapm, "MICBIAS2");
 
 	if (present)
 		snd_soc_jack_report(wm8994->micdet[0].jack,
@@ -3770,6 +3767,7 @@ int wm8958_mic_detect(struct snd_soc_codec *codec, struct snd_soc_jack *jack,
 		      wm1811_micdet_cb det_cb, void *det_cb_data,
 		      wm1811_mic_id_cb id_cb, void *id_cb_data)
 {
+	struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
 	struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
 	struct wm8994 *control = wm8994->wm8994;
 	u16 micd_lvl_sel;
@@ -3783,8 +3781,8 @@ int wm8958_mic_detect(struct snd_soc_codec *codec, struct snd_soc_jack *jack,
 	}
 
 	if (jack) {
-		snd_soc_dapm_force_enable_pin(&codec->dapm, "CLK_SYS");
-		snd_soc_dapm_sync(&codec->dapm);
+		snd_soc_dapm_force_enable_pin(dapm, "CLK_SYS");
+		snd_soc_dapm_sync(dapm);
 
 		wm8994->micdet[0].jack = jack;
 
@@ -3819,7 +3817,7 @@ int wm8958_mic_detect(struct snd_soc_codec *codec, struct snd_soc_jack *jack,
 		snd_soc_update_bits(codec, WM8958_MIC_DETECT_2,
 				    WM8958_MICD_LVL_SEL_MASK, micd_lvl_sel);
 
-		WARN_ON(codec->dapm.bias_level > SND_SOC_BIAS_STANDBY);
+		WARN_ON(snd_soc_codec_get_bias_level(codec) > SND_SOC_BIAS_STANDBY);
 
 		/*
 		 * If we can use jack detection start off with that,
@@ -3846,8 +3844,8 @@ int wm8958_mic_detect(struct snd_soc_codec *codec, struct snd_soc_jack *jack,
 		snd_soc_update_bits(codec, WM8958_MIC_DETECT_1,
 				    WM8958_MICD_ENA, 0);
 		wm1811_jackdet_set_mode(codec, WM1811_JACKDET_MODE_NONE);
-		snd_soc_dapm_disable_pin(&codec->dapm, "CLK_SYS");
-		snd_soc_dapm_sync(&codec->dapm);
+		snd_soc_dapm_disable_pin(dapm, "CLK_SYS");
+		snd_soc_dapm_sync(dapm);
 	}
 
 	return 0;
@@ -3985,9 +3983,9 @@ static irqreturn_t wm8994_temp_shut(int irq, void *data)
 
 static int wm8994_codec_probe(struct snd_soc_codec *codec)
 {
+	struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
 	struct wm8994 *control = dev_get_drvdata(codec->dev->parent);
 	struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
-	struct snd_soc_dapm_context *dapm = &codec->dapm;
 	unsigned int reg;
 	int ret, i;
 
@@ -4018,7 +4016,7 @@ static int wm8994_codec_probe(struct snd_soc_codec *codec)
 	wm8994->micdet_irq = control->pdata.micdet_irq;
 
 	/* By default use idle_bias_off, will override for WM8994 */
-	codec->dapm.idle_bias_off = 1;
+	dapm->idle_bias_off = 1;
 
 	/* Set revision-specific configuration */
 	switch (control->type) {
@@ -4026,7 +4024,7 @@ static int wm8994_codec_probe(struct snd_soc_codec *codec)
 		/* Single ended line outputs should have VMID on. */
 		if (!control->pdata.lineout1_diff ||
 		    !control->pdata.lineout2_diff)
-			codec->dapm.idle_bias_off = 0;
+			dapm->idle_bias_off = 0;
 
 		switch (control->revision) {
 		case 2:
@@ -4086,7 +4084,8 @@ static int wm8994_codec_probe(struct snd_soc_codec *codec)
 		if (wm8994->micdet_irq)
 			ret = request_threaded_irq(wm8994->micdet_irq, NULL,
 						   wm8994_mic_irq,
-						   IRQF_TRIGGER_RISING,
+						   IRQF_TRIGGER_RISING |
+						   IRQF_ONESHOT,
 						   "Mic1 detect",
 						   wm8994);
 		 else
@@ -4134,7 +4133,8 @@ static int wm8994_codec_probe(struct snd_soc_codec *codec)
 		if (wm8994->micdet_irq) {
 			ret = request_threaded_irq(wm8994->micdet_irq, NULL,
 						   wm8958_mic_irq,
-						   IRQF_TRIGGER_RISING,
+						   IRQF_TRIGGER_RISING |
+						   IRQF_ONESHOT,
 						   "Mic detect",
 						   wm8994);
 			if (ret != 0)
diff --git a/sound/soc/codecs/wm8995.c b/sound/soc/codecs/wm8995.c
index 66103c2b012e..687c4dd7ec99 100644
--- a/sound/soc/codecs/wm8995.c
+++ b/sound/soc/codecs/wm8995.c
@@ -721,6 +721,7 @@ static int configure_aif_clock(struct snd_soc_codec *codec, int aif)
 
 static int configure_clock(struct snd_soc_codec *codec)
 {
+	struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
 	struct wm8995_priv *wm8995;
 	int change, new;
 
@@ -751,7 +752,7 @@ static int configure_clock(struct snd_soc_codec *codec)
 	if (!change)
 		return 0;
 
-	snd_soc_dapm_sync(&codec->dapm);
+	snd_soc_dapm_sync(dapm);
 
 	return 0;
 }
@@ -1965,7 +1966,7 @@ static int wm8995_set_bias_level(struct snd_soc_codec *codec,
 	case SND_SOC_BIAS_PREPARE:
 		break;
 	case SND_SOC_BIAS_STANDBY:
-		if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) {
+		if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) {
 			ret = regulator_bulk_enable(ARRAY_SIZE(wm8995->supplies),
 						    wm8995->supplies);
 			if (ret)
@@ -1990,7 +1991,6 @@ static int wm8995_set_bias_level(struct snd_soc_codec *codec,
 		break;
 	}
 
-	codec->dapm.bias_level = level;
 	return 0;
 }
 
diff --git a/sound/soc/codecs/wm8996.c b/sound/soc/codecs/wm8996.c
index 308748a022c5..3dd063f682b2 100644
--- a/sound/soc/codecs/wm8996.c
+++ b/sound/soc/codecs/wm8996.c
@@ -1590,7 +1590,7 @@ static int wm8996_set_bias_level(struct snd_soc_codec *codec,
 		break;
 
 	case SND_SOC_BIAS_STANDBY:
-		if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) {
+		if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) {
 			ret = regulator_bulk_enable(ARRAY_SIZE(wm8996->supplies),
 						    wm8996->supplies);
 			if (ret != 0) {
@@ -1628,8 +1628,6 @@ static int wm8996_set_bias_level(struct snd_soc_codec *codec,
 		break;
 	}
 
-	codec->dapm.bias_level = level;
-
 	return 0;
 }
 
@@ -2247,7 +2245,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;
+	struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
 
 	wm8996->jack = jack;
 	wm8996->detecting = true;
@@ -2292,6 +2290,7 @@ EXPORT_SYMBOL_GPL(wm8996_detect);
 
 static void wm8996_hpdet_irq(struct snd_soc_codec *codec)
 {
+	struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
 	struct wm8996_priv *wm8996 = snd_soc_codec_get_drvdata(codec);
 	int val, reg, report;
 
@@ -2345,12 +2344,14 @@ out:
 	snd_soc_update_bits(codec, WM8996_MIC_DETECT_1, WM8996_MICD_ENA,
 			    WM8996_MICD_ENA);
 
-	snd_soc_dapm_disable_pin(&codec->dapm, "Bandgap");
-	snd_soc_dapm_sync(&codec->dapm);
+	snd_soc_dapm_disable_pin(dapm, "Bandgap");
+	snd_soc_dapm_sync(dapm);
 }
 
 static void wm8996_hpdet_start(struct snd_soc_codec *codec)
 {
+	struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
+
 	/* Unclamp the output, we can't measure while we're shorting it */
 	snd_soc_update_bits(codec, WM8996_ANALOGUE_HP_1,
 			    WM8996_HPOUT1L_RMV_SHORT |
@@ -2359,8 +2360,8 @@ static void wm8996_hpdet_start(struct snd_soc_codec *codec)
 			    WM8996_HPOUT1R_RMV_SHORT);
 
 	/* We need bandgap for HPDET */
-	snd_soc_dapm_force_enable_pin(&codec->dapm, "Bandgap");
-	snd_soc_dapm_sync(&codec->dapm);
+	snd_soc_dapm_force_enable_pin(dapm, "Bandgap");
+	snd_soc_dapm_sync(dapm);
 
 	/* Go into headphone detect left mode */
 	snd_soc_update_bits(codec, WM8996_MIC_DETECT_1, WM8996_MICD_ENA, 0);
@@ -2646,10 +2647,12 @@ static int wm8996_probe(struct snd_soc_codec *codec)
 		if (irq_flags & (IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING))
 			ret = request_threaded_irq(i2c->irq, NULL,
 						   wm8996_edge_irq,
-						   irq_flags, "wm8996", codec);
+						   irq_flags | IRQF_ONESHOT,
+						   "wm8996", codec);
 		else
 			ret = request_threaded_irq(i2c->irq, NULL, wm8996_irq,
-						   irq_flags, "wm8996", codec);
+						   irq_flags | IRQF_ONESHOT,
+						   "wm8996", codec);
 
 		if (ret == 0) {
 			/* Unmask the interrupt */
diff --git a/sound/soc/codecs/wm8997.c b/sound/soc/codecs/wm8997.c
index e7c81baefe66..4134dc7e1243 100644
--- a/sound/soc/codecs/wm8997.c
+++ b/sound/soc/codecs/wm8997.c
@@ -106,11 +106,13 @@ static int wm8997_sysclk_ev(struct snd_soc_dapm_widget *w,
 				regmap_write_async(regmap, patch[i].reg,
 						   patch[i].def);
 		break;
-	default:
+	case SND_SOC_DAPM_PRE_PMD:
 		break;
+	default:
+		return 0;
 	}
 
-	return 0;
+	return arizona_dvfs_sysclk_ev(w, kcontrol, event);
 }
 
 static const char *wm8997_osr_text[] = {
@@ -409,7 +411,8 @@ static const struct snd_kcontrol_new wm8997_aec_loopback_mux =
 
 static const struct snd_soc_dapm_widget wm8997_dapm_widgets[] = {
 SND_SOC_DAPM_SUPPLY("SYSCLK", ARIZONA_SYSTEM_CLOCK_1, ARIZONA_SYSCLK_ENA_SHIFT,
-		    0, wm8997_sysclk_ev, SND_SOC_DAPM_POST_PMU),
+		    0, wm8997_sysclk_ev,
+		    SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
 SND_SOC_DAPM_SUPPLY("ASYNCCLK", ARIZONA_ASYNC_CLOCK_1,
 		    ARIZONA_ASYNC_CLK_ENA_SHIFT, 0, NULL, 0),
 SND_SOC_DAPM_SUPPLY("OPCLK", ARIZONA_OUTPUT_SYSTEM_CLOCK,
@@ -1055,13 +1058,14 @@ static struct snd_soc_dai_driver wm8997_dai[] = {
 
 static int wm8997_codec_probe(struct snd_soc_codec *codec)
 {
+	struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
 	struct wm8997_priv *priv = snd_soc_codec_get_drvdata(codec);
 
 	arizona_init_spk(codec);
 
-	snd_soc_dapm_disable_pin(&codec->dapm, "HAPTICS");
+	snd_soc_dapm_disable_pin(dapm, "HAPTICS");
 
-	priv->core.arizona->dapm = &codec->dapm;
+	priv->core.arizona->dapm = dapm;
 
 	return 0;
 }
@@ -1126,6 +1130,8 @@ static int wm8997_probe(struct platform_device *pdev)
 	wm8997->core.arizona = arizona;
 	wm8997->core.num_inputs = 4;
 
+	arizona_init_dvfs(&wm8997->core);
+
 	for (i = 0; i < ARRAY_SIZE(wm8997->fll); i++)
 		wm8997->fll[i].vco_mult = 1;
 
diff --git a/sound/soc/codecs/wm9081.c b/sound/soc/codecs/wm9081.c
index 13a3f335ea5b..8a8b1c0f9142 100644
--- a/sound/soc/codecs/wm9081.c
+++ b/sound/soc/codecs/wm9081.c
@@ -838,7 +838,7 @@ static int wm9081_set_bias_level(struct snd_soc_codec *codec,
 
 	case SND_SOC_BIAS_STANDBY:
 		/* Initial cold start */
-		if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) {
+		if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) {
 			regcache_cache_only(wm9081->regmap, false);
 			regcache_sync(wm9081->regmap);
 
@@ -898,8 +898,6 @@ static int wm9081_set_bias_level(struct snd_soc_codec *codec,
 		break;
 	}
 
-	codec->dapm.bias_level = level;
-
 	return 0;
 }
 
diff --git a/sound/soc/codecs/wm9090.c b/sound/soc/codecs/wm9090.c
index 60d243c904f5..13d23fc797db 100644
--- a/sound/soc/codecs/wm9090.c
+++ b/sound/soc/codecs/wm9090.c
@@ -425,7 +425,7 @@ static const struct snd_soc_dapm_route audio_map_in2_diff[] = {
 static int wm9090_add_controls(struct snd_soc_codec *codec)
 {
 	struct wm9090_priv *wm9090 = snd_soc_codec_get_drvdata(codec);
-	struct snd_soc_dapm_context *dapm = &codec->dapm;
+	struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
 	int i;
 
 	snd_soc_dapm_new_controls(dapm, wm9090_dapm_widgets,
@@ -496,7 +496,7 @@ static int wm9090_set_bias_level(struct snd_soc_codec *codec,
 		break;
 
 	case SND_SOC_BIAS_STANDBY:
-		if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) {
+		if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) {
 			/* Restore the register cache */
 			regcache_sync(wm9090->regmap);
 		}
@@ -515,8 +515,6 @@ static int wm9090_set_bias_level(struct snd_soc_codec *codec,
 		break;
 	}
 
-	codec->dapm.bias_level = level;
-
 	return 0;
 }
 
diff --git a/sound/soc/codecs/wm9712.c b/sound/soc/codecs/wm9712.c
index 98c9525bd751..1fda104dfc45 100644
--- a/sound/soc/codecs/wm9712.c
+++ b/sound/soc/codecs/wm9712.c
@@ -610,7 +610,6 @@ static int wm9712_set_bias_level(struct snd_soc_codec *codec,
 		ac97_write(codec, AC97_POWERDOWN, 0xffff);
 		break;
 	}
-	codec->dapm.bias_level = level;
 	return 0;
 }
 
@@ -646,7 +645,7 @@ static int wm9712_soc_resume(struct snd_soc_codec *codec)
 	if (ret < 0)
 		return ret;
 
-	wm9712_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+	snd_soc_codec_force_bias_level(codec, SND_SOC_BIAS_STANDBY);
 
 	if (ret == 0) {
 		/* Sync reg_cache with the hardware after cold reset */
diff --git a/sound/soc/codecs/wm9713.c b/sound/soc/codecs/wm9713.c
index 1b20b8d2b15d..89cd2d6f57c0 100644
--- a/sound/soc/codecs/wm9713.c
+++ b/sound/soc/codecs/wm9713.c
@@ -1171,7 +1171,6 @@ static int wm9713_set_bias_level(struct snd_soc_codec *codec,
 		ac97_write(codec, AC97_POWERDOWN, 0xffff);
 		break;
 	}
-	codec->dapm.bias_level = level;
 	return 0;
 }
 
@@ -1201,7 +1200,7 @@ static int wm9713_soc_resume(struct snd_soc_codec *codec)
 	if (ret < 0)
 		return ret;
 
-	wm9713_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+	snd_soc_codec_force_bias_level(codec, SND_SOC_BIAS_STANDBY);
 
 	/* do we need to re-start the PLL ? */
 	if (wm9713->pll_in)
diff --git a/sound/soc/codecs/wm_adsp.c b/sound/soc/codecs/wm_adsp.c
index d01c2095452f..b62ffd0c133e 100644
--- a/sound/soc/codecs/wm_adsp.c
+++ b/sound/soc/codecs/wm_adsp.c
@@ -121,6 +121,11 @@
 #define ADSP2_WDMA_CONFIG_2 0x31
 #define ADSP2_RDMA_CONFIG_1 0x34
 
+#define ADSP2_SCRATCH0        0x40
+#define ADSP2_SCRATCH1        0x41
+#define ADSP2_SCRATCH2        0x42
+#define ADSP2_SCRATCH3        0x43
+
 /*
  * ADSP2 Control
  */
@@ -229,16 +234,18 @@ struct wm_coeff_ctl_ops {
 
 struct wm_coeff_ctl {
 	const char *name;
-	struct wm_adsp_alg_region region;
+	const char *fw_name;
+	struct wm_adsp_alg_region alg_region;
 	struct wm_coeff_ctl_ops ops;
-	struct wm_adsp *adsp;
-	void *private;
+	struct wm_adsp *dsp;
 	unsigned int enabled:1;
 	struct list_head list;
 	void *cache;
+	unsigned int offset;
 	size_t len;
 	unsigned int set:1;
 	struct snd_kcontrol *kcontrol;
+	unsigned int flags;
 };
 
 static int wm_adsp_fw_get(struct snd_kcontrol *kcontrol,
@@ -246,9 +253,9 @@ static int wm_adsp_fw_get(struct snd_kcontrol *kcontrol,
 {
 	struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
 	struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
-	struct wm_adsp *adsp = snd_soc_codec_get_drvdata(codec);
+	struct wm_adsp *dsp = snd_soc_codec_get_drvdata(codec);
 
-	ucontrol->value.integer.value[0] = adsp[e->shift_l].fw;
+	ucontrol->value.integer.value[0] = dsp[e->shift_l].fw;
 
 	return 0;
 }
@@ -258,18 +265,18 @@ static int wm_adsp_fw_put(struct snd_kcontrol *kcontrol,
 {
 	struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
 	struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
-	struct wm_adsp *adsp = snd_soc_codec_get_drvdata(codec);
+	struct wm_adsp *dsp = snd_soc_codec_get_drvdata(codec);
 
-	if (ucontrol->value.integer.value[0] == adsp[e->shift_l].fw)
+	if (ucontrol->value.integer.value[0] == dsp[e->shift_l].fw)
 		return 0;
 
 	if (ucontrol->value.integer.value[0] >= WM_ADSP_NUM_FW)
 		return -EINVAL;
 
-	if (adsp[e->shift_l].running)
+	if (dsp[e->shift_l].running)
 		return -EBUSY;
 
-	adsp[e->shift_l].fw = ucontrol->value.integer.value[0];
+	dsp[e->shift_l].fw = ucontrol->value.integer.value[0];
 
 	return 0;
 }
@@ -340,28 +347,47 @@ static struct wm_adsp_region const *wm_adsp_find_region(struct wm_adsp *dsp,
 	return NULL;
 }
 
-static unsigned int wm_adsp_region_to_reg(struct wm_adsp_region const *region,
+static unsigned int wm_adsp_region_to_reg(struct wm_adsp_region const *mem,
 					  unsigned int offset)
 {
-	if (WARN_ON(!region))
+	if (WARN_ON(!mem))
 		return offset;
-	switch (region->type) {
+	switch (mem->type) {
 	case WMFW_ADSP1_PM:
-		return region->base + (offset * 3);
+		return mem->base + (offset * 3);
 	case WMFW_ADSP1_DM:
-		return region->base + (offset * 2);
+		return mem->base + (offset * 2);
 	case WMFW_ADSP2_XM:
-		return region->base + (offset * 2);
+		return mem->base + (offset * 2);
 	case WMFW_ADSP2_YM:
-		return region->base + (offset * 2);
+		return mem->base + (offset * 2);
 	case WMFW_ADSP1_ZM:
-		return region->base + (offset * 2);
+		return mem->base + (offset * 2);
 	default:
 		WARN(1, "Unknown memory region type");
 		return offset;
 	}
 }
 
+static void wm_adsp2_show_fw_status(struct wm_adsp *dsp)
+{
+	u16 scratch[4];
+	int ret;
+
+	ret = regmap_raw_read(dsp->regmap, dsp->base + ADSP2_SCRATCH0,
+				scratch, sizeof(scratch));
+	if (ret) {
+		adsp_err(dsp, "Failed to read SCRATCH regs: %d\n", ret);
+		return;
+	}
+
+	adsp_dbg(dsp, "FW SCRATCH 0:0x%x 1:0x%x 2:0x%x 3:0x%x\n",
+		 be16_to_cpu(scratch[0]),
+		 be16_to_cpu(scratch[1]),
+		 be16_to_cpu(scratch[2]),
+		 be16_to_cpu(scratch[3]));
+}
+
 static int wm_coeff_info(struct snd_kcontrol *kcontrol,
 			 struct snd_ctl_elem_info *uinfo)
 {
@@ -372,40 +398,39 @@ static int wm_coeff_info(struct snd_kcontrol *kcontrol,
 	return 0;
 }
 
-static int wm_coeff_write_control(struct snd_kcontrol *kcontrol,
+static int wm_coeff_write_control(struct wm_coeff_ctl *ctl,
 				  const void *buf, size_t len)
 {
-	struct wm_coeff_ctl *ctl = (struct wm_coeff_ctl *)kcontrol->private_value;
-	struct wm_adsp_alg_region *region = &ctl->region;
+	struct wm_adsp_alg_region *alg_region = &ctl->alg_region;
 	const struct wm_adsp_region *mem;
-	struct wm_adsp *adsp = ctl->adsp;
+	struct wm_adsp *dsp = ctl->dsp;
 	void *scratch;
 	int ret;
 	unsigned int reg;
 
-	mem = wm_adsp_find_region(adsp, region->type);
+	mem = wm_adsp_find_region(dsp, alg_region->type);
 	if (!mem) {
-		adsp_err(adsp, "No base for region %x\n",
-			 region->type);
+		adsp_err(dsp, "No base for region %x\n",
+			 alg_region->type);
 		return -EINVAL;
 	}
 
-	reg = ctl->region.base;
+	reg = ctl->alg_region.base + ctl->offset;
 	reg = wm_adsp_region_to_reg(mem, reg);
 
 	scratch = kmemdup(buf, ctl->len, GFP_KERNEL | GFP_DMA);
 	if (!scratch)
 		return -ENOMEM;
 
-	ret = regmap_raw_write(adsp->regmap, reg, scratch,
+	ret = regmap_raw_write(dsp->regmap, reg, scratch,
 			       ctl->len);
 	if (ret) {
-		adsp_err(adsp, "Failed to write %zu bytes to %x: %d\n",
+		adsp_err(dsp, "Failed to write %zu bytes to %x: %d\n",
 			 ctl->len, reg, ret);
 		kfree(scratch);
 		return ret;
 	}
-	adsp_dbg(adsp, "Wrote %zu bytes to %x\n", ctl->len, reg);
+	adsp_dbg(dsp, "Wrote %zu bytes to %x\n", ctl->len, reg);
 
 	kfree(scratch);
 
@@ -424,42 +449,41 @@ static int wm_coeff_put(struct snd_kcontrol *kcontrol,
 	if (!ctl->enabled)
 		return 0;
 
-	return wm_coeff_write_control(kcontrol, p, ctl->len);
+	return wm_coeff_write_control(ctl, p, ctl->len);
 }
 
-static int wm_coeff_read_control(struct snd_kcontrol *kcontrol,
+static int wm_coeff_read_control(struct wm_coeff_ctl *ctl,
 				 void *buf, size_t len)
 {
-	struct wm_coeff_ctl *ctl = (struct wm_coeff_ctl *)kcontrol->private_value;
-	struct wm_adsp_alg_region *region = &ctl->region;
+	struct wm_adsp_alg_region *alg_region = &ctl->alg_region;
 	const struct wm_adsp_region *mem;
-	struct wm_adsp *adsp = ctl->adsp;
+	struct wm_adsp *dsp = ctl->dsp;
 	void *scratch;
 	int ret;
 	unsigned int reg;
 
-	mem = wm_adsp_find_region(adsp, region->type);
+	mem = wm_adsp_find_region(dsp, alg_region->type);
 	if (!mem) {
-		adsp_err(adsp, "No base for region %x\n",
-			 region->type);
+		adsp_err(dsp, "No base for region %x\n",
+			 alg_region->type);
 		return -EINVAL;
 	}
 
-	reg = ctl->region.base;
+	reg = ctl->alg_region.base + ctl->offset;
 	reg = wm_adsp_region_to_reg(mem, reg);
 
 	scratch = kmalloc(ctl->len, GFP_KERNEL | GFP_DMA);
 	if (!scratch)
 		return -ENOMEM;
 
-	ret = regmap_raw_read(adsp->regmap, reg, scratch, ctl->len);
+	ret = regmap_raw_read(dsp->regmap, reg, scratch, ctl->len);
 	if (ret) {
-		adsp_err(adsp, "Failed to read %zu bytes from %x: %d\n",
+		adsp_err(dsp, "Failed to read %zu bytes from %x: %d\n",
 			 ctl->len, reg, ret);
 		kfree(scratch);
 		return ret;
 	}
-	adsp_dbg(adsp, "Read %zu bytes from %x\n", ctl->len, reg);
+	adsp_dbg(dsp, "Read %zu bytes from %x\n", ctl->len, reg);
 
 	memcpy(buf, scratch, ctl->len);
 	kfree(scratch);
@@ -473,17 +497,25 @@ static int wm_coeff_get(struct snd_kcontrol *kcontrol,
 	struct wm_coeff_ctl *ctl = (struct wm_coeff_ctl *)kcontrol->private_value;
 	char *p = ucontrol->value.bytes.data;
 
+	if (ctl->flags & WMFW_CTL_FLAG_VOLATILE) {
+		if (ctl->enabled)
+			return wm_coeff_read_control(ctl, p, ctl->len);
+		else
+			return -EPERM;
+	}
+
 	memcpy(p, ctl->cache, ctl->len);
+
 	return 0;
 }
 
 struct wmfw_ctl_work {
-	struct wm_adsp *adsp;
+	struct wm_adsp *dsp;
 	struct wm_coeff_ctl *ctl;
 	struct work_struct work;
 };
 
-static int wmfw_add_ctl(struct wm_adsp *adsp, struct wm_coeff_ctl *ctl)
+static int wmfw_add_ctl(struct wm_adsp *dsp, struct wm_coeff_ctl *ctl)
 {
 	struct snd_kcontrol_new *kcontrol;
 	int ret;
@@ -502,17 +534,25 @@ static int wmfw_add_ctl(struct wm_adsp *adsp, struct wm_coeff_ctl *ctl)
 	kcontrol->put = wm_coeff_put;
 	kcontrol->private_value = (unsigned long)ctl;
 
-	ret = snd_soc_add_card_controls(adsp->card,
+	if (ctl->flags) {
+		if (ctl->flags & WMFW_CTL_FLAG_WRITEABLE)
+			kcontrol->access |= SNDRV_CTL_ELEM_ACCESS_WRITE;
+		if (ctl->flags & WMFW_CTL_FLAG_READABLE)
+			kcontrol->access |= SNDRV_CTL_ELEM_ACCESS_READ;
+		if (ctl->flags & WMFW_CTL_FLAG_VOLATILE)
+			kcontrol->access |= SNDRV_CTL_ELEM_ACCESS_VOLATILE;
+	}
+
+	ret = snd_soc_add_card_controls(dsp->card,
 					kcontrol, 1);
 	if (ret < 0)
 		goto err_kcontrol;
 
 	kfree(kcontrol);
 
-	ctl->kcontrol = snd_soc_card_get_kcontrol(adsp->card,
+	ctl->kcontrol = snd_soc_card_get_kcontrol(dsp->card,
 						  ctl->name);
 
-	list_add(&ctl->list, &adsp->ctl_list);
 	return 0;
 
 err_kcontrol:
@@ -520,6 +560,358 @@ err_kcontrol:
 	return ret;
 }
 
+static int wm_coeff_init_control_caches(struct wm_adsp *dsp)
+{
+	struct wm_coeff_ctl *ctl;
+	int ret;
+
+	list_for_each_entry(ctl, &dsp->ctl_list, list) {
+		if (!ctl->enabled || ctl->set)
+			continue;
+		if (ctl->flags & WMFW_CTL_FLAG_VOLATILE)
+			continue;
+
+		ret = wm_coeff_read_control(ctl,
+					    ctl->cache,
+					    ctl->len);
+		if (ret < 0)
+			return ret;
+	}
+
+	return 0;
+}
+
+static int wm_coeff_sync_controls(struct wm_adsp *dsp)
+{
+	struct wm_coeff_ctl *ctl;
+	int ret;
+
+	list_for_each_entry(ctl, &dsp->ctl_list, list) {
+		if (!ctl->enabled)
+			continue;
+		if (ctl->set && !(ctl->flags & WMFW_CTL_FLAG_VOLATILE)) {
+			ret = wm_coeff_write_control(ctl,
+						     ctl->cache,
+						     ctl->len);
+			if (ret < 0)
+				return ret;
+		}
+	}
+
+	return 0;
+}
+
+static void wm_adsp_ctl_work(struct work_struct *work)
+{
+	struct wmfw_ctl_work *ctl_work = container_of(work,
+						      struct wmfw_ctl_work,
+						      work);
+
+	wmfw_add_ctl(ctl_work->dsp, ctl_work->ctl);
+	kfree(ctl_work);
+}
+
+static int wm_adsp_create_control(struct wm_adsp *dsp,
+				  const struct wm_adsp_alg_region *alg_region,
+				  unsigned int offset, unsigned int len,
+				  const char *subname, unsigned int subname_len,
+				  unsigned int flags)
+{
+	struct wm_coeff_ctl *ctl;
+	struct wmfw_ctl_work *ctl_work;
+	char name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN];
+	char *region_name;
+	int ret;
+
+	if (flags & WMFW_CTL_FLAG_SYS)
+		return 0;
+
+	switch (alg_region->type) {
+	case WMFW_ADSP1_PM:
+		region_name = "PM";
+		break;
+	case WMFW_ADSP1_DM:
+		region_name = "DM";
+		break;
+	case WMFW_ADSP2_XM:
+		region_name = "XM";
+		break;
+	case WMFW_ADSP2_YM:
+		region_name = "YM";
+		break;
+	case WMFW_ADSP1_ZM:
+		region_name = "ZM";
+		break;
+	default:
+		adsp_err(dsp, "Unknown region type: %d\n", alg_region->type);
+		return -EINVAL;
+	}
+
+	switch (dsp->fw_ver) {
+	case 0:
+	case 1:
+		snprintf(name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN, "DSP%d %s %x",
+			 dsp->num, region_name, alg_region->alg);
+		break;
+	default:
+		ret = snprintf(name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN,
+				"DSP%d%c %.12s %x", dsp->num, *region_name,
+				wm_adsp_fw_text[dsp->fw], alg_region->alg);
+
+		/* Truncate the subname from the start if it is too long */
+		if (subname) {
+			int avail = SNDRV_CTL_ELEM_ID_NAME_MAXLEN - ret - 2;
+			int skip = 0;
+
+			if (subname_len > avail)
+				skip = subname_len - avail;
+
+			snprintf(name + ret,
+				 SNDRV_CTL_ELEM_ID_NAME_MAXLEN - ret, " %.*s",
+				 subname_len - skip, subname + skip);
+		}
+		break;
+	}
+
+	list_for_each_entry(ctl, &dsp->ctl_list,
+			    list) {
+		if (!strcmp(ctl->name, name)) {
+			if (!ctl->enabled)
+				ctl->enabled = 1;
+			return 0;
+		}
+	}
+
+	ctl = kzalloc(sizeof(*ctl), GFP_KERNEL);
+	if (!ctl)
+		return -ENOMEM;
+	ctl->fw_name = wm_adsp_fw_text[dsp->fw];
+	ctl->alg_region = *alg_region;
+	ctl->name = kmemdup(name, strlen(name) + 1, GFP_KERNEL);
+	if (!ctl->name) {
+		ret = -ENOMEM;
+		goto err_ctl;
+	}
+	ctl->enabled = 1;
+	ctl->set = 0;
+	ctl->ops.xget = wm_coeff_get;
+	ctl->ops.xput = wm_coeff_put;
+	ctl->dsp = dsp;
+
+	ctl->flags = flags;
+	ctl->offset = offset;
+	if (len > 512) {
+		adsp_warn(dsp, "Truncating control %s from %d\n",
+			  ctl->name, len);
+		len = 512;
+	}
+	ctl->len = len;
+	ctl->cache = kzalloc(ctl->len, GFP_KERNEL);
+	if (!ctl->cache) {
+		ret = -ENOMEM;
+		goto err_ctl_name;
+	}
+
+	list_add(&ctl->list, &dsp->ctl_list);
+
+	ctl_work = kzalloc(sizeof(*ctl_work), GFP_KERNEL);
+	if (!ctl_work) {
+		ret = -ENOMEM;
+		goto err_ctl_cache;
+	}
+
+	ctl_work->dsp = dsp;
+	ctl_work->ctl = ctl;
+	INIT_WORK(&ctl_work->work, wm_adsp_ctl_work);
+	schedule_work(&ctl_work->work);
+
+	return 0;
+
+err_ctl_cache:
+	kfree(ctl->cache);
+err_ctl_name:
+	kfree(ctl->name);
+err_ctl:
+	kfree(ctl);
+
+	return ret;
+}
+
+struct wm_coeff_parsed_alg {
+	int id;
+	const u8 *name;
+	int name_len;
+	int ncoeff;
+};
+
+struct wm_coeff_parsed_coeff {
+	int offset;
+	int mem_type;
+	const u8 *name;
+	int name_len;
+	int ctl_type;
+	int flags;
+	int len;
+};
+
+static int wm_coeff_parse_string(int bytes, const u8 **pos, const u8 **str)
+{
+	int length;
+
+	switch (bytes) {
+	case 1:
+		length = **pos;
+		break;
+	case 2:
+		length = le16_to_cpu(*((__le16 *)*pos));
+		break;
+	default:
+		return 0;
+	}
+
+	if (str)
+		*str = *pos + bytes;
+
+	*pos += ((length + bytes) + 3) & ~0x03;
+
+	return length;
+}
+
+static int wm_coeff_parse_int(int bytes, const u8 **pos)
+{
+	int val = 0;
+
+	switch (bytes) {
+	case 2:
+		val = le16_to_cpu(*((__le16 *)*pos));
+		break;
+	case 4:
+		val = le32_to_cpu(*((__le32 *)*pos));
+		break;
+	default:
+		break;
+	}
+
+	*pos += bytes;
+
+	return val;
+}
+
+static inline void wm_coeff_parse_alg(struct wm_adsp *dsp, const u8 **data,
+				      struct wm_coeff_parsed_alg *blk)
+{
+	const struct wmfw_adsp_alg_data *raw;
+
+	switch (dsp->fw_ver) {
+	case 0:
+	case 1:
+		raw = (const struct wmfw_adsp_alg_data *)*data;
+		*data = raw->data;
+
+		blk->id = le32_to_cpu(raw->id);
+		blk->name = raw->name;
+		blk->name_len = strlen(raw->name);
+		blk->ncoeff = le32_to_cpu(raw->ncoeff);
+		break;
+	default:
+		blk->id = wm_coeff_parse_int(sizeof(raw->id), data);
+		blk->name_len = wm_coeff_parse_string(sizeof(u8), data,
+						      &blk->name);
+		wm_coeff_parse_string(sizeof(u16), data, NULL);
+		blk->ncoeff = wm_coeff_parse_int(sizeof(raw->ncoeff), data);
+		break;
+	}
+
+	adsp_dbg(dsp, "Algorithm ID: %#x\n", blk->id);
+	adsp_dbg(dsp, "Algorithm name: %.*s\n", blk->name_len, blk->name);
+	adsp_dbg(dsp, "# of coefficient descriptors: %#x\n", blk->ncoeff);
+}
+
+static inline void wm_coeff_parse_coeff(struct wm_adsp *dsp, const u8 **data,
+					struct wm_coeff_parsed_coeff *blk)
+{
+	const struct wmfw_adsp_coeff_data *raw;
+	const u8 *tmp;
+	int length;
+
+	switch (dsp->fw_ver) {
+	case 0:
+	case 1:
+		raw = (const struct wmfw_adsp_coeff_data *)*data;
+		*data = *data + sizeof(raw->hdr) + le32_to_cpu(raw->hdr.size);
+
+		blk->offset = le16_to_cpu(raw->hdr.offset);
+		blk->mem_type = le16_to_cpu(raw->hdr.type);
+		blk->name = raw->name;
+		blk->name_len = strlen(raw->name);
+		blk->ctl_type = le16_to_cpu(raw->ctl_type);
+		blk->flags = le16_to_cpu(raw->flags);
+		blk->len = le32_to_cpu(raw->len);
+		break;
+	default:
+		tmp = *data;
+		blk->offset = wm_coeff_parse_int(sizeof(raw->hdr.offset), &tmp);
+		blk->mem_type = wm_coeff_parse_int(sizeof(raw->hdr.type), &tmp);
+		length = wm_coeff_parse_int(sizeof(raw->hdr.size), &tmp);
+		blk->name_len = wm_coeff_parse_string(sizeof(u8), &tmp,
+						      &blk->name);
+		wm_coeff_parse_string(sizeof(u8), &tmp, NULL);
+		wm_coeff_parse_string(sizeof(u16), &tmp, NULL);
+		blk->ctl_type = wm_coeff_parse_int(sizeof(raw->ctl_type), &tmp);
+		blk->flags = wm_coeff_parse_int(sizeof(raw->flags), &tmp);
+		blk->len = wm_coeff_parse_int(sizeof(raw->len), &tmp);
+
+		*data = *data + sizeof(raw->hdr) + length;
+		break;
+	}
+
+	adsp_dbg(dsp, "\tCoefficient type: %#x\n", blk->mem_type);
+	adsp_dbg(dsp, "\tCoefficient offset: %#x\n", blk->offset);
+	adsp_dbg(dsp, "\tCoefficient name: %.*s\n", blk->name_len, blk->name);
+	adsp_dbg(dsp, "\tCoefficient flags: %#x\n", blk->flags);
+	adsp_dbg(dsp, "\tALSA control type: %#x\n", blk->ctl_type);
+	adsp_dbg(dsp, "\tALSA control len: %#x\n", blk->len);
+}
+
+static int wm_adsp_parse_coeff(struct wm_adsp *dsp,
+			       const struct wmfw_region *region)
+{
+	struct wm_adsp_alg_region alg_region = {};
+	struct wm_coeff_parsed_alg alg_blk;
+	struct wm_coeff_parsed_coeff coeff_blk;
+	const u8 *data = region->data;
+	int i, ret;
+
+	wm_coeff_parse_alg(dsp, &data, &alg_blk);
+	for (i = 0; i < alg_blk.ncoeff; i++) {
+		wm_coeff_parse_coeff(dsp, &data, &coeff_blk);
+
+		switch (coeff_blk.ctl_type) {
+		case SNDRV_CTL_ELEM_TYPE_BYTES:
+			break;
+		default:
+			adsp_err(dsp, "Unknown control type: %d\n",
+				 coeff_blk.ctl_type);
+			return -EINVAL;
+		}
+
+		alg_region.type = coeff_blk.mem_type;
+		alg_region.alg = alg_blk.id;
+
+		ret = wm_adsp_create_control(dsp, &alg_region,
+					     coeff_blk.offset,
+					     coeff_blk.len,
+					     coeff_blk.name,
+					     coeff_blk.name_len,
+					     coeff_blk.flags);
+		if (ret < 0)
+			adsp_err(dsp, "Failed to create control: %.*s, %d\n",
+				 coeff_blk.name_len, coeff_blk.name, ret);
+	}
+
+	return 0;
+}
+
 static int wm_adsp_load(struct wm_adsp *dsp)
 {
 	LIST_HEAD(buf_list);
@@ -568,12 +960,22 @@ static int wm_adsp_load(struct wm_adsp *dsp)
 		goto out_fw;
 	}
 
-	if (header->ver != 0) {
+	switch (header->ver) {
+	case 0:
+		adsp_warn(dsp, "%s: Depreciated file format %d\n",
+			  file, header->ver);
+		break;
+	case 1:
+	case 2:
+		break;
+	default:
 		adsp_err(dsp, "%s: unknown file format %d\n",
 			 file, header->ver);
 		goto out_fw;
 	}
+
 	adsp_info(dsp, "Firmware version: %d\n", header->ver);
+	dsp->fw_ver = header->ver;
 
 	if (header->core != dsp->type) {
 		adsp_err(dsp, "%s: invalid core %d != %d\n",
@@ -638,6 +1040,12 @@ static int wm_adsp_load(struct wm_adsp *dsp)
 			text = kzalloc(le32_to_cpu(region->len) + 1,
 				       GFP_KERNEL);
 			break;
+		case WMFW_ALGORITHM_DATA:
+			region_name = "Algorithm";
+			ret = wm_adsp_parse_coeff(dsp, region);
+			if (ret != 0)
+				goto out_fw;
+			break;
 		case WMFW_INFO_TEXT:
 			region_name = "Information";
 			text = kzalloc(le32_to_cpu(region->len) + 1,
@@ -730,444 +1138,316 @@ out:
 	return ret;
 }
 
-static int wm_coeff_init_control_caches(struct wm_adsp *adsp)
+static void wm_adsp_ctl_fixup_base(struct wm_adsp *dsp,
+				  const struct wm_adsp_alg_region *alg_region)
 {
 	struct wm_coeff_ctl *ctl;
-	int ret;
 
-	list_for_each_entry(ctl, &adsp->ctl_list, list) {
-		if (!ctl->enabled || ctl->set)
-			continue;
-		ret = wm_coeff_read_control(ctl->kcontrol,
-					    ctl->cache,
-					    ctl->len);
-		if (ret < 0)
-			return ret;
+	list_for_each_entry(ctl, &dsp->ctl_list, list) {
+		if (ctl->fw_name == wm_adsp_fw_text[dsp->fw] &&
+		    alg_region->alg == ctl->alg_region.alg &&
+		    alg_region->type == ctl->alg_region.type) {
+			ctl->alg_region.base = alg_region->base;
+		}
 	}
-
-	return 0;
 }
 
-static int wm_coeff_sync_controls(struct wm_adsp *adsp)
+static void *wm_adsp_read_algs(struct wm_adsp *dsp, size_t n_algs,
+			       unsigned int pos, unsigned int len)
 {
-	struct wm_coeff_ctl *ctl;
+	void *alg;
 	int ret;
+	__be32 val;
 
-	list_for_each_entry(ctl, &adsp->ctl_list, list) {
-		if (!ctl->enabled)
-			continue;
-		if (ctl->set) {
-			ret = wm_coeff_write_control(ctl->kcontrol,
-						     ctl->cache,
-						     ctl->len);
-			if (ret < 0)
-				return ret;
-		}
+	if (n_algs == 0) {
+		adsp_err(dsp, "No algorithms\n");
+		return ERR_PTR(-EINVAL);
 	}
 
-	return 0;
-}
-
-static void wm_adsp_ctl_work(struct work_struct *work)
-{
-	struct wmfw_ctl_work *ctl_work = container_of(work,
-						      struct wmfw_ctl_work,
-						      work);
-
-	wmfw_add_ctl(ctl_work->adsp, ctl_work->ctl);
-	kfree(ctl_work);
-}
-
-static int wm_adsp_create_control(struct wm_adsp *dsp,
-				  const struct wm_adsp_alg_region *region)
-
-{
-	struct wm_coeff_ctl *ctl;
-	struct wmfw_ctl_work *ctl_work;
-	char *name;
-	char *region_name;
-	int ret;
-
-	name = kmalloc(PAGE_SIZE, GFP_KERNEL);
-	if (!name)
-		return -ENOMEM;
+	if (n_algs > 1024) {
+		adsp_err(dsp, "Algorithm count %zx excessive\n", n_algs);
+		return ERR_PTR(-EINVAL);
+	}
 
-	switch (region->type) {
-	case WMFW_ADSP1_PM:
-		region_name = "PM";
-		break;
-	case WMFW_ADSP1_DM:
-		region_name = "DM";
-		break;
-	case WMFW_ADSP2_XM:
-		region_name = "XM";
-		break;
-	case WMFW_ADSP2_YM:
-		region_name = "YM";
-		break;
-	case WMFW_ADSP1_ZM:
-		region_name = "ZM";
-		break;
-	default:
-		ret = -EINVAL;
-		goto err_name;
+	/* Read the terminator first to validate the length */
+	ret = regmap_raw_read(dsp->regmap, pos + len, &val, sizeof(val));
+	if (ret != 0) {
+		adsp_err(dsp, "Failed to read algorithm list end: %d\n",
+			ret);
+		return ERR_PTR(ret);
 	}
 
-	snprintf(name, PAGE_SIZE, "DSP%d %s %x",
-		 dsp->num, region_name, region->alg);
+	if (be32_to_cpu(val) != 0xbedead)
+		adsp_warn(dsp, "Algorithm list end %x 0x%x != 0xbeadead\n",
+			  pos + len, be32_to_cpu(val));
 
-	list_for_each_entry(ctl, &dsp->ctl_list,
-			    list) {
-		if (!strcmp(ctl->name, name)) {
-			if (!ctl->enabled)
-				ctl->enabled = 1;
-			goto found;
-		}
-	}
+	alg = kzalloc(len * 2, GFP_KERNEL | GFP_DMA);
+	if (!alg)
+		return ERR_PTR(-ENOMEM);
 
-	ctl = kzalloc(sizeof(*ctl), GFP_KERNEL);
-	if (!ctl) {
-		ret = -ENOMEM;
-		goto err_name;
-	}
-	ctl->region = *region;
-	ctl->name = kmemdup(name, strlen(name) + 1, GFP_KERNEL);
-	if (!ctl->name) {
-		ret = -ENOMEM;
-		goto err_ctl;
+	ret = regmap_raw_read(dsp->regmap, pos, alg, len * 2);
+	if (ret != 0) {
+		adsp_err(dsp, "Failed to read algorithm list: %d\n",
+			ret);
+		kfree(alg);
+		return ERR_PTR(ret);
 	}
-	ctl->enabled = 1;
-	ctl->set = 0;
-	ctl->ops.xget = wm_coeff_get;
-	ctl->ops.xput = wm_coeff_put;
-	ctl->adsp = dsp;
 
-	ctl->len = region->len;
-	ctl->cache = kzalloc(ctl->len, GFP_KERNEL);
-	if (!ctl->cache) {
-		ret = -ENOMEM;
-		goto err_ctl_name;
-	}
+	return alg;
+}
 
-	ctl_work = kzalloc(sizeof(*ctl_work), GFP_KERNEL);
-	if (!ctl_work) {
-		ret = -ENOMEM;
-		goto err_ctl_cache;
-	}
+static struct wm_adsp_alg_region *wm_adsp_create_region(struct wm_adsp *dsp,
+							int type, __be32 id,
+							__be32 base)
+{
+	struct wm_adsp_alg_region *alg_region;
 
-	ctl_work->adsp = dsp;
-	ctl_work->ctl = ctl;
-	INIT_WORK(&ctl_work->work, wm_adsp_ctl_work);
-	schedule_work(&ctl_work->work);
+	alg_region = kzalloc(sizeof(*alg_region), GFP_KERNEL);
+	if (!alg_region)
+		return ERR_PTR(-ENOMEM);
 
-found:
-	kfree(name);
+	alg_region->type = type;
+	alg_region->alg = be32_to_cpu(id);
+	alg_region->base = be32_to_cpu(base);
 
-	return 0;
+	list_add_tail(&alg_region->list, &dsp->alg_regions);
 
-err_ctl_cache:
-	kfree(ctl->cache);
-err_ctl_name:
-	kfree(ctl->name);
-err_ctl:
-	kfree(ctl);
-err_name:
-	kfree(name);
-	return ret;
+	if (dsp->fw_ver > 0)
+		wm_adsp_ctl_fixup_base(dsp, alg_region);
+
+	return alg_region;
 }
 
-static int wm_adsp_setup_algs(struct wm_adsp *dsp)
+static int wm_adsp1_setup_algs(struct wm_adsp *dsp)
 {
-	struct regmap *regmap = dsp->regmap;
 	struct wmfw_adsp1_id_hdr adsp1_id;
-	struct wmfw_adsp2_id_hdr adsp2_id;
 	struct wmfw_adsp1_alg_hdr *adsp1_alg;
-	struct wmfw_adsp2_alg_hdr *adsp2_alg;
-	void *alg, *buf;
-	struct wm_adsp_alg_region *region;
+	struct wm_adsp_alg_region *alg_region;
 	const struct wm_adsp_region *mem;
-	unsigned int pos, term;
-	size_t algs, buf_size;
-	__be32 val;
+	unsigned int pos, len;
+	size_t n_algs;
 	int i, ret;
 
-	switch (dsp->type) {
-	case WMFW_ADSP1:
-		mem = wm_adsp_find_region(dsp, WMFW_ADSP1_DM);
-		break;
-	case WMFW_ADSP2:
-		mem = wm_adsp_find_region(dsp, WMFW_ADSP2_XM);
-		break;
-	default:
-		mem = NULL;
-		break;
-	}
-
+	mem = wm_adsp_find_region(dsp, WMFW_ADSP1_DM);
 	if (WARN_ON(!mem))
 		return -EINVAL;
 
-	switch (dsp->type) {
-	case WMFW_ADSP1:
-		ret = regmap_raw_read(regmap, mem->base, &adsp1_id,
-				      sizeof(adsp1_id));
-		if (ret != 0) {
-			adsp_err(dsp, "Failed to read algorithm info: %d\n",
-				 ret);
-			return ret;
-		}
-
-		buf = &adsp1_id;
-		buf_size = sizeof(adsp1_id);
-
-		algs = be32_to_cpu(adsp1_id.algs);
-		dsp->fw_id = be32_to_cpu(adsp1_id.fw.id);
-		adsp_info(dsp, "Firmware: %x v%d.%d.%d, %zu algorithms\n",
-			  dsp->fw_id,
-			  (be32_to_cpu(adsp1_id.fw.ver) & 0xff0000) >> 16,
-			  (be32_to_cpu(adsp1_id.fw.ver) & 0xff00) >> 8,
-			  be32_to_cpu(adsp1_id.fw.ver) & 0xff,
-			  algs);
-
-		region = kzalloc(sizeof(*region), GFP_KERNEL);
-		if (!region)
-			return -ENOMEM;
-		region->type = WMFW_ADSP1_ZM;
-		region->alg = be32_to_cpu(adsp1_id.fw.id);
-		region->base = be32_to_cpu(adsp1_id.zm);
-		list_add_tail(&region->list, &dsp->alg_regions);
-
-		region = kzalloc(sizeof(*region), GFP_KERNEL);
-		if (!region)
-			return -ENOMEM;
-		region->type = WMFW_ADSP1_DM;
-		region->alg = be32_to_cpu(adsp1_id.fw.id);
-		region->base = be32_to_cpu(adsp1_id.dm);
-		list_add_tail(&region->list, &dsp->alg_regions);
-
-		pos = sizeof(adsp1_id) / 2;
-		term = pos + ((sizeof(*adsp1_alg) * algs) / 2);
-		break;
-
-	case WMFW_ADSP2:
-		ret = regmap_raw_read(regmap, mem->base, &adsp2_id,
-				      sizeof(adsp2_id));
-		if (ret != 0) {
-			adsp_err(dsp, "Failed to read algorithm info: %d\n",
-				 ret);
-			return ret;
-		}
-
-		buf = &adsp2_id;
-		buf_size = sizeof(adsp2_id);
-
-		algs = be32_to_cpu(adsp2_id.algs);
-		dsp->fw_id = be32_to_cpu(adsp2_id.fw.id);
-		adsp_info(dsp, "Firmware: %x v%d.%d.%d, %zu algorithms\n",
-			  dsp->fw_id,
-			  (be32_to_cpu(adsp2_id.fw.ver) & 0xff0000) >> 16,
-			  (be32_to_cpu(adsp2_id.fw.ver) & 0xff00) >> 8,
-			  be32_to_cpu(adsp2_id.fw.ver) & 0xff,
-			  algs);
-
-		region = kzalloc(sizeof(*region), GFP_KERNEL);
-		if (!region)
-			return -ENOMEM;
-		region->type = WMFW_ADSP2_XM;
-		region->alg = be32_to_cpu(adsp2_id.fw.id);
-		region->base = be32_to_cpu(adsp2_id.xm);
-		list_add_tail(&region->list, &dsp->alg_regions);
-
-		region = kzalloc(sizeof(*region), GFP_KERNEL);
-		if (!region)
-			return -ENOMEM;
-		region->type = WMFW_ADSP2_YM;
-		region->alg = be32_to_cpu(adsp2_id.fw.id);
-		region->base = be32_to_cpu(adsp2_id.ym);
-		list_add_tail(&region->list, &dsp->alg_regions);
-
-		region = kzalloc(sizeof(*region), GFP_KERNEL);
-		if (!region)
-			return -ENOMEM;
-		region->type = WMFW_ADSP2_ZM;
-		region->alg = be32_to_cpu(adsp2_id.fw.id);
-		region->base = be32_to_cpu(adsp2_id.zm);
-		list_add_tail(&region->list, &dsp->alg_regions);
-
-		pos = sizeof(adsp2_id) / 2;
-		term = pos + ((sizeof(*adsp2_alg) * algs) / 2);
-		break;
-
-	default:
-		WARN(1, "Unknown DSP type");
-		return -EINVAL;
-	}
-
-	if (algs == 0) {
-		adsp_err(dsp, "No algorithms\n");
-		return -EINVAL;
-	}
-
-	if (algs > 1024) {
-		adsp_err(dsp, "Algorithm count %zx excessive\n", algs);
-		print_hex_dump_bytes(dev_name(dsp->dev), DUMP_PREFIX_OFFSET,
-				     buf, buf_size);
-		return -EINVAL;
-	}
-
-	/* Read the terminator first to validate the length */
-	ret = regmap_raw_read(regmap, mem->base + term, &val, sizeof(val));
+	ret = regmap_raw_read(dsp->regmap, mem->base, &adsp1_id,
+			      sizeof(adsp1_id));
 	if (ret != 0) {
-		adsp_err(dsp, "Failed to read algorithm list end: %d\n",
-			ret);
+		adsp_err(dsp, "Failed to read algorithm info: %d\n",
+			 ret);
 		return ret;
 	}
 
-	if (be32_to_cpu(val) != 0xbedead)
-		adsp_warn(dsp, "Algorithm list end %x 0x%x != 0xbeadead\n",
-			  term, be32_to_cpu(val));
-
-	alg = kzalloc((term - pos) * 2, GFP_KERNEL | GFP_DMA);
-	if (!alg)
-		return -ENOMEM;
-
-	ret = regmap_raw_read(regmap, mem->base + pos, alg, (term - pos) * 2);
-	if (ret != 0) {
-		adsp_err(dsp, "Failed to read algorithm list: %d\n",
-			ret);
-		goto out;
-	}
-
-	adsp1_alg = alg;
-	adsp2_alg = alg;
-
-	for (i = 0; i < algs; i++) {
-		switch (dsp->type) {
-		case WMFW_ADSP1:
-			adsp_info(dsp, "%d: ID %x v%d.%d.%d DM@%x ZM@%x\n",
-				  i, be32_to_cpu(adsp1_alg[i].alg.id),
-				  (be32_to_cpu(adsp1_alg[i].alg.ver) & 0xff0000) >> 16,
-				  (be32_to_cpu(adsp1_alg[i].alg.ver) & 0xff00) >> 8,
-				  be32_to_cpu(adsp1_alg[i].alg.ver) & 0xff,
-				  be32_to_cpu(adsp1_alg[i].dm),
-				  be32_to_cpu(adsp1_alg[i].zm));
-
-			region = kzalloc(sizeof(*region), GFP_KERNEL);
-			if (!region) {
-				ret = -ENOMEM;
-				goto out;
-			}
-			region->type = WMFW_ADSP1_DM;
-			region->alg = be32_to_cpu(adsp1_alg[i].alg.id);
-			region->base = be32_to_cpu(adsp1_alg[i].dm);
-			region->len = 0;
-			list_add_tail(&region->list, &dsp->alg_regions);
-			if (i + 1 < algs) {
-				region->len = be32_to_cpu(adsp1_alg[i + 1].dm);
-				region->len -= be32_to_cpu(adsp1_alg[i].dm);
-				region->len *= 4;
-				wm_adsp_create_control(dsp, region);
+	n_algs = be32_to_cpu(adsp1_id.n_algs);
+	dsp->fw_id = be32_to_cpu(adsp1_id.fw.id);
+	adsp_info(dsp, "Firmware: %x v%d.%d.%d, %zu algorithms\n",
+		  dsp->fw_id,
+		  (be32_to_cpu(adsp1_id.fw.ver) & 0xff0000) >> 16,
+		  (be32_to_cpu(adsp1_id.fw.ver) & 0xff00) >> 8,
+		  be32_to_cpu(adsp1_id.fw.ver) & 0xff,
+		  n_algs);
+
+	alg_region = wm_adsp_create_region(dsp, WMFW_ADSP1_ZM,
+					   adsp1_id.fw.id, adsp1_id.zm);
+	if (IS_ERR(alg_region))
+		return PTR_ERR(alg_region);
+
+	alg_region = wm_adsp_create_region(dsp, WMFW_ADSP1_DM,
+					   adsp1_id.fw.id, adsp1_id.dm);
+	if (IS_ERR(alg_region))
+		return PTR_ERR(alg_region);
+
+	pos = sizeof(adsp1_id) / 2;
+	len = (sizeof(*adsp1_alg) * n_algs) / 2;
+
+	adsp1_alg = wm_adsp_read_algs(dsp, n_algs, mem->base + pos, len);
+	if (IS_ERR(adsp1_alg))
+		return PTR_ERR(adsp1_alg);
+
+	for (i = 0; i < n_algs; i++) {
+		adsp_info(dsp, "%d: ID %x v%d.%d.%d DM@%x ZM@%x\n",
+			  i, be32_to_cpu(adsp1_alg[i].alg.id),
+			  (be32_to_cpu(adsp1_alg[i].alg.ver) & 0xff0000) >> 16,
+			  (be32_to_cpu(adsp1_alg[i].alg.ver) & 0xff00) >> 8,
+			  be32_to_cpu(adsp1_alg[i].alg.ver) & 0xff,
+			  be32_to_cpu(adsp1_alg[i].dm),
+			  be32_to_cpu(adsp1_alg[i].zm));
+
+		alg_region = wm_adsp_create_region(dsp, WMFW_ADSP1_DM,
+						   adsp1_alg[i].alg.id,
+						   adsp1_alg[i].dm);
+		if (IS_ERR(alg_region)) {
+			ret = PTR_ERR(alg_region);
+			goto out;
+		}
+		if (dsp->fw_ver == 0) {
+			if (i + 1 < n_algs) {
+				len = be32_to_cpu(adsp1_alg[i + 1].dm);
+				len -= be32_to_cpu(adsp1_alg[i].dm);
+				len *= 4;
+				wm_adsp_create_control(dsp, alg_region, 0,
+						       len, NULL, 0, 0);
 			} else {
 				adsp_warn(dsp, "Missing length info for region DM with ID %x\n",
 					  be32_to_cpu(adsp1_alg[i].alg.id));
 			}
+		}
 
-			region = kzalloc(sizeof(*region), GFP_KERNEL);
-			if (!region) {
-				ret = -ENOMEM;
-				goto out;
-			}
-			region->type = WMFW_ADSP1_ZM;
-			region->alg = be32_to_cpu(adsp1_alg[i].alg.id);
-			region->base = be32_to_cpu(adsp1_alg[i].zm);
-			region->len = 0;
-			list_add_tail(&region->list, &dsp->alg_regions);
-			if (i + 1 < algs) {
-				region->len = be32_to_cpu(adsp1_alg[i + 1].zm);
-				region->len -= be32_to_cpu(adsp1_alg[i].zm);
-				region->len *= 4;
-				wm_adsp_create_control(dsp, region);
+		alg_region = wm_adsp_create_region(dsp, WMFW_ADSP1_ZM,
+						   adsp1_alg[i].alg.id,
+						   adsp1_alg[i].zm);
+		if (IS_ERR(alg_region)) {
+			ret = PTR_ERR(alg_region);
+			goto out;
+		}
+		if (dsp->fw_ver == 0) {
+			if (i + 1 < n_algs) {
+				len = be32_to_cpu(adsp1_alg[i + 1].zm);
+				len -= be32_to_cpu(adsp1_alg[i].zm);
+				len *= 4;
+				wm_adsp_create_control(dsp, alg_region, 0,
+						       len, NULL, 0, 0);
 			} else {
 				adsp_warn(dsp, "Missing length info for region ZM with ID %x\n",
 					  be32_to_cpu(adsp1_alg[i].alg.id));
 			}
-			break;
+		}
+	}
 
-		case WMFW_ADSP2:
-			adsp_info(dsp,
-				  "%d: ID %x v%d.%d.%d XM@%x YM@%x ZM@%x\n",
-				  i, be32_to_cpu(adsp2_alg[i].alg.id),
-				  (be32_to_cpu(adsp2_alg[i].alg.ver) & 0xff0000) >> 16,
-				  (be32_to_cpu(adsp2_alg[i].alg.ver) & 0xff00) >> 8,
-				  be32_to_cpu(adsp2_alg[i].alg.ver) & 0xff,
-				  be32_to_cpu(adsp2_alg[i].xm),
-				  be32_to_cpu(adsp2_alg[i].ym),
-				  be32_to_cpu(adsp2_alg[i].zm));
-
-			region = kzalloc(sizeof(*region), GFP_KERNEL);
-			if (!region) {
-				ret = -ENOMEM;
-				goto out;
-			}
-			region->type = WMFW_ADSP2_XM;
-			region->alg = be32_to_cpu(adsp2_alg[i].alg.id);
-			region->base = be32_to_cpu(adsp2_alg[i].xm);
-			region->len = 0;
-			list_add_tail(&region->list, &dsp->alg_regions);
-			if (i + 1 < algs) {
-				region->len = be32_to_cpu(adsp2_alg[i + 1].xm);
-				region->len -= be32_to_cpu(adsp2_alg[i].xm);
-				region->len *= 4;
-				wm_adsp_create_control(dsp, region);
+out:
+	kfree(adsp1_alg);
+	return ret;
+}
+
+static int wm_adsp2_setup_algs(struct wm_adsp *dsp)
+{
+	struct wmfw_adsp2_id_hdr adsp2_id;
+	struct wmfw_adsp2_alg_hdr *adsp2_alg;
+	struct wm_adsp_alg_region *alg_region;
+	const struct wm_adsp_region *mem;
+	unsigned int pos, len;
+	size_t n_algs;
+	int i, ret;
+
+	mem = wm_adsp_find_region(dsp, WMFW_ADSP2_XM);
+	if (WARN_ON(!mem))
+		return -EINVAL;
+
+	ret = regmap_raw_read(dsp->regmap, mem->base, &adsp2_id,
+			      sizeof(adsp2_id));
+	if (ret != 0) {
+		adsp_err(dsp, "Failed to read algorithm info: %d\n",
+			 ret);
+		return ret;
+	}
+
+	n_algs = be32_to_cpu(adsp2_id.n_algs);
+	dsp->fw_id = be32_to_cpu(adsp2_id.fw.id);
+	adsp_info(dsp, "Firmware: %x v%d.%d.%d, %zu algorithms\n",
+		  dsp->fw_id,
+		  (be32_to_cpu(adsp2_id.fw.ver) & 0xff0000) >> 16,
+		  (be32_to_cpu(adsp2_id.fw.ver) & 0xff00) >> 8,
+		  be32_to_cpu(adsp2_id.fw.ver) & 0xff,
+		  n_algs);
+
+	alg_region = wm_adsp_create_region(dsp, WMFW_ADSP2_XM,
+					   adsp2_id.fw.id, adsp2_id.xm);
+	if (IS_ERR(alg_region))
+		return PTR_ERR(alg_region);
+
+	alg_region = wm_adsp_create_region(dsp, WMFW_ADSP2_YM,
+					   adsp2_id.fw.id, adsp2_id.ym);
+	if (IS_ERR(alg_region))
+		return PTR_ERR(alg_region);
+
+	alg_region = wm_adsp_create_region(dsp, WMFW_ADSP2_ZM,
+					   adsp2_id.fw.id, adsp2_id.zm);
+	if (IS_ERR(alg_region))
+		return PTR_ERR(alg_region);
+
+	pos = sizeof(adsp2_id) / 2;
+	len = (sizeof(*adsp2_alg) * n_algs) / 2;
+
+	adsp2_alg = wm_adsp_read_algs(dsp, n_algs, mem->base + pos, len);
+	if (IS_ERR(adsp2_alg))
+		return PTR_ERR(adsp2_alg);
+
+	for (i = 0; i < n_algs; i++) {
+		adsp_info(dsp,
+			  "%d: ID %x v%d.%d.%d XM@%x YM@%x ZM@%x\n",
+			  i, be32_to_cpu(adsp2_alg[i].alg.id),
+			  (be32_to_cpu(adsp2_alg[i].alg.ver) & 0xff0000) >> 16,
+			  (be32_to_cpu(adsp2_alg[i].alg.ver) & 0xff00) >> 8,
+			  be32_to_cpu(adsp2_alg[i].alg.ver) & 0xff,
+			  be32_to_cpu(adsp2_alg[i].xm),
+			  be32_to_cpu(adsp2_alg[i].ym),
+			  be32_to_cpu(adsp2_alg[i].zm));
+
+		alg_region = wm_adsp_create_region(dsp, WMFW_ADSP2_XM,
+						   adsp2_alg[i].alg.id,
+						   adsp2_alg[i].xm);
+		if (IS_ERR(alg_region)) {
+			ret = PTR_ERR(alg_region);
+			goto out;
+		}
+		if (dsp->fw_ver == 0) {
+			if (i + 1 < n_algs) {
+				len = be32_to_cpu(adsp2_alg[i + 1].xm);
+				len -= be32_to_cpu(adsp2_alg[i].xm);
+				len *= 4;
+				wm_adsp_create_control(dsp, alg_region, 0,
+						       len, NULL, 0, 0);
 			} else {
 				adsp_warn(dsp, "Missing length info for region XM with ID %x\n",
 					  be32_to_cpu(adsp2_alg[i].alg.id));
 			}
+		}
 
-			region = kzalloc(sizeof(*region), GFP_KERNEL);
-			if (!region) {
-				ret = -ENOMEM;
-				goto out;
-			}
-			region->type = WMFW_ADSP2_YM;
-			region->alg = be32_to_cpu(adsp2_alg[i].alg.id);
-			region->base = be32_to_cpu(adsp2_alg[i].ym);
-			region->len = 0;
-			list_add_tail(&region->list, &dsp->alg_regions);
-			if (i + 1 < algs) {
-				region->len = be32_to_cpu(adsp2_alg[i + 1].ym);
-				region->len -= be32_to_cpu(adsp2_alg[i].ym);
-				region->len *= 4;
-				wm_adsp_create_control(dsp, region);
+		alg_region = wm_adsp_create_region(dsp, WMFW_ADSP2_YM,
+						   adsp2_alg[i].alg.id,
+						   adsp2_alg[i].ym);
+		if (IS_ERR(alg_region)) {
+			ret = PTR_ERR(alg_region);
+			goto out;
+		}
+		if (dsp->fw_ver == 0) {
+			if (i + 1 < n_algs) {
+				len = be32_to_cpu(adsp2_alg[i + 1].ym);
+				len -= be32_to_cpu(adsp2_alg[i].ym);
+				len *= 4;
+				wm_adsp_create_control(dsp, alg_region, 0,
+						       len, NULL, 0, 0);
 			} else {
 				adsp_warn(dsp, "Missing length info for region YM with ID %x\n",
 					  be32_to_cpu(adsp2_alg[i].alg.id));
 			}
+		}
 
-			region = kzalloc(sizeof(*region), GFP_KERNEL);
-			if (!region) {
-				ret = -ENOMEM;
-				goto out;
-			}
-			region->type = WMFW_ADSP2_ZM;
-			region->alg = be32_to_cpu(adsp2_alg[i].alg.id);
-			region->base = be32_to_cpu(adsp2_alg[i].zm);
-			region->len = 0;
-			list_add_tail(&region->list, &dsp->alg_regions);
-			if (i + 1 < algs) {
-				region->len = be32_to_cpu(adsp2_alg[i + 1].zm);
-				region->len -= be32_to_cpu(adsp2_alg[i].zm);
-				region->len *= 4;
-				wm_adsp_create_control(dsp, region);
+		alg_region = wm_adsp_create_region(dsp, WMFW_ADSP2_ZM,
+						   adsp2_alg[i].alg.id,
+						   adsp2_alg[i].zm);
+		if (IS_ERR(alg_region)) {
+			ret = PTR_ERR(alg_region);
+			goto out;
+		}
+		if (dsp->fw_ver == 0) {
+			if (i + 1 < n_algs) {
+				len = be32_to_cpu(adsp2_alg[i + 1].zm);
+				len -= be32_to_cpu(adsp2_alg[i].zm);
+				len *= 4;
+				wm_adsp_create_control(dsp, alg_region, 0,
+						       len, NULL, 0, 0);
 			} else {
 				adsp_warn(dsp, "Missing length info for region ZM with ID %x\n",
 					  be32_to_cpu(adsp2_alg[i].alg.id));
 			}
-			break;
 		}
 	}
 
 out:
-	kfree(alg);
+	kfree(adsp2_alg);
 	return ret;
 }
 
@@ -1354,9 +1634,9 @@ out:
 	return ret;
 }
 
-int wm_adsp1_init(struct wm_adsp *adsp)
+int wm_adsp1_init(struct wm_adsp *dsp)
 {
-	INIT_LIST_HEAD(&adsp->alg_regions);
+	INIT_LIST_HEAD(&dsp->alg_regions);
 
 	return 0;
 }
@@ -1410,7 +1690,7 @@ int wm_adsp1_event(struct snd_soc_dapm_widget *w,
 		if (ret != 0)
 			goto err;
 
-		ret = wm_adsp_setup_algs(dsp);
+		ret = wm_adsp1_setup_algs(dsp);
 		if (ret != 0)
 			goto err;
 
@@ -1531,35 +1811,6 @@ static void wm_adsp2_boot_work(struct work_struct *work)
 		return;
 	}
 
-	if (dsp->dvfs) {
-		ret = regmap_read(dsp->regmap,
-				  dsp->base + ADSP2_CLOCKING, &val);
-		if (ret != 0) {
-			adsp_err(dsp, "Failed to read clocking: %d\n", ret);
-			return;
-		}
-
-		if ((val & ADSP2_CLK_SEL_MASK) >= 3) {
-			ret = regulator_enable(dsp->dvfs);
-			if (ret != 0) {
-				adsp_err(dsp,
-					 "Failed to enable supply: %d\n",
-					 ret);
-				return;
-			}
-
-			ret = regulator_set_voltage(dsp->dvfs,
-						    1800000,
-						    1800000);
-			if (ret != 0) {
-				adsp_err(dsp,
-					 "Failed to raise supply: %d\n",
-					 ret);
-				return;
-			}
-		}
-	}
-
 	ret = wm_adsp2_ena(dsp);
 	if (ret != 0)
 		return;
@@ -1568,7 +1819,7 @@ static void wm_adsp2_boot_work(struct work_struct *work)
 	if (ret != 0)
 		goto err;
 
-	ret = wm_adsp_setup_algs(dsp);
+	ret = wm_adsp2_setup_algs(dsp);
 	if (ret != 0)
 		goto err;
 
@@ -1642,6 +1893,9 @@ int wm_adsp2_event(struct snd_soc_dapm_widget *w,
 		break;
 
 	case SND_SOC_DAPM_PRE_PMD:
+		/* Log firmware state, it can be useful for analysis */
+		wm_adsp2_show_fw_status(dsp);
+
 		dsp->running = false;
 
 		regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL,
@@ -1653,21 +1907,6 @@ int wm_adsp2_event(struct snd_soc_dapm_widget *w,
 		regmap_write(dsp->regmap, dsp->base + ADSP2_WDMA_CONFIG_2, 0);
 		regmap_write(dsp->regmap, dsp->base + ADSP2_RDMA_CONFIG_1, 0);
 
-		if (dsp->dvfs) {
-			ret = regulator_set_voltage(dsp->dvfs, 1200000,
-						    1800000);
-			if (ret != 0)
-				adsp_warn(dsp,
-					  "Failed to lower supply: %d\n",
-					  ret);
-
-			ret = regulator_disable(dsp->dvfs);
-			if (ret != 0)
-				adsp_err(dsp,
-					 "Failed to enable supply: %d\n",
-					 ret);
-		}
-
 		list_for_each_entry(ctl, &dsp->ctl_list, list)
 			ctl->enabled = 0;
 
@@ -1694,7 +1933,7 @@ err:
 }
 EXPORT_SYMBOL_GPL(wm_adsp2_event);
 
-int wm_adsp2_init(struct wm_adsp *adsp, bool dvfs)
+int wm_adsp2_init(struct wm_adsp *dsp)
 {
 	int ret;
 
@@ -1702,43 +1941,16 @@ int wm_adsp2_init(struct wm_adsp *adsp, bool dvfs)
 	 * Disable the DSP memory by default when in reset for a small
 	 * power saving.
 	 */
-	ret = regmap_update_bits(adsp->regmap, adsp->base + ADSP2_CONTROL,
+	ret = regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL,
 				 ADSP2_MEM_ENA, 0);
 	if (ret != 0) {
-		adsp_err(adsp, "Failed to clear memory retention: %d\n", ret);
+		adsp_err(dsp, "Failed to clear memory retention: %d\n", ret);
 		return ret;
 	}
 
-	INIT_LIST_HEAD(&adsp->alg_regions);
-	INIT_LIST_HEAD(&adsp->ctl_list);
-	INIT_WORK(&adsp->boot_work, wm_adsp2_boot_work);
-
-	if (dvfs) {
-		adsp->dvfs = devm_regulator_get(adsp->dev, "DCVDD");
-		if (IS_ERR(adsp->dvfs)) {
-			ret = PTR_ERR(adsp->dvfs);
-			adsp_err(adsp, "Failed to get DCVDD: %d\n", ret);
-			return ret;
-		}
-
-		ret = regulator_enable(adsp->dvfs);
-		if (ret != 0) {
-			adsp_err(adsp, "Failed to enable DCVDD: %d\n", ret);
-			return ret;
-		}
-
-		ret = regulator_set_voltage(adsp->dvfs, 1200000, 1800000);
-		if (ret != 0) {
-			adsp_err(adsp, "Failed to initialise DVFS: %d\n", ret);
-			return ret;
-		}
-
-		ret = regulator_disable(adsp->dvfs);
-		if (ret != 0) {
-			adsp_err(adsp, "Failed to disable DCVDD: %d\n", ret);
-			return ret;
-		}
-	}
+	INIT_LIST_HEAD(&dsp->alg_regions);
+	INIT_LIST_HEAD(&dsp->ctl_list);
+	INIT_WORK(&dsp->boot_work, wm_adsp2_boot_work);
 
 	return 0;
 }
diff --git a/sound/soc/codecs/wm_adsp.h b/sound/soc/codecs/wm_adsp.h
index a4f6b64deb61..0e5f07c35d50 100644
--- a/sound/soc/codecs/wm_adsp.h
+++ b/sound/soc/codecs/wm_adsp.h
@@ -18,8 +18,6 @@
 
 #include "wmfw.h"
 
-struct regulator;
-
 struct wm_adsp_region {
 	int type;
 	unsigned int base;
@@ -30,7 +28,6 @@ struct wm_adsp_alg_region {
 	unsigned int alg;
 	int type;
 	unsigned int base;
-	size_t len;
 };
 
 struct wm_adsp {
@@ -54,10 +51,9 @@ struct wm_adsp {
 	int num_mems;
 
 	int fw;
+	int fw_ver;
 	bool running;
 
-	struct regulator *dvfs;
-
 	struct list_head ctl_list;
 
 	struct work_struct boot_work;
@@ -67,19 +63,22 @@ struct wm_adsp {
 	SND_SOC_DAPM_PGA_E(wname, SND_SOC_NOPM, num, 0, NULL, 0, \
 		wm_adsp1_event, SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD)
 
-#define WM_ADSP2(wname, num) \
+#define WM_ADSP2_E(wname, num, event_fn) \
 {	.id = snd_soc_dapm_dai_link, .name = wname " Preloader", \
-	.reg = SND_SOC_NOPM, .shift = num, .event = wm_adsp2_early_event, \
-	.event_flags = SND_SOC_DAPM_PRE_PMU }, \
+	.reg = SND_SOC_NOPM, .shift = num, .event = event_fn, \
+	.event_flags = SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD }, \
 {	.id = snd_soc_dapm_out_drv, .name = wname, \
 	.reg = SND_SOC_NOPM, .shift = num, .event = wm_adsp2_event, \
 	.event_flags = SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD }
 
+#define WM_ADSP2(wname, num) \
+	WM_ADSP2_E(wname, num, wm_adsp2_early_event)
+
 extern const struct snd_kcontrol_new wm_adsp1_fw_controls[];
 extern const struct snd_kcontrol_new wm_adsp2_fw_controls[];
 
-int wm_adsp1_init(struct wm_adsp *adsp);
-int wm_adsp2_init(struct wm_adsp *adsp, bool dvfs);
+int wm_adsp1_init(struct wm_adsp *dsp);
+int wm_adsp2_init(struct wm_adsp *dsp);
 int wm_adsp1_event(struct snd_soc_dapm_widget *w,
 		   struct snd_kcontrol *kcontrol, int event);
 int wm_adsp2_early_event(struct snd_soc_dapm_widget *w,
diff --git a/sound/soc/codecs/wm_hubs.c b/sound/soc/codecs/wm_hubs.c
index 8366e19657a7..fd86bd105460 100644
--- a/sound/soc/codecs/wm_hubs.c
+++ b/sound/soc/codecs/wm_hubs.c
@@ -1116,7 +1116,7 @@ static const struct snd_soc_dapm_route lineout2_se_routes[] = {
 
 int wm_hubs_add_analogue_controls(struct snd_soc_codec *codec)
 {
-	struct snd_soc_dapm_context *dapm = &codec->dapm;
+	struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
 
 	/* Latch volume update bits & default ZC on */
 	snd_soc_update_bits(codec, WM8993_LEFT_LINE_INPUT_1_2_VOLUME,
@@ -1160,7 +1160,7 @@ int wm_hubs_add_analogue_routes(struct snd_soc_codec *codec,
 				int lineout1_diff, int lineout2_diff)
 {
 	struct wm_hubs_data *hubs = snd_soc_codec_get_drvdata(codec);
-	struct snd_soc_dapm_context *dapm = &codec->dapm;
+	struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
 
 	hubs->codec = codec;
 
diff --git a/sound/soc/codecs/wmfw.h b/sound/soc/codecs/wmfw.h
index ef163360a745..7613d60d62ea 100644
--- a/sound/soc/codecs/wmfw.h
+++ b/sound/soc/codecs/wmfw.h
@@ -15,6 +15,17 @@
 
 #include <linux/types.h>
 
+#define WMFW_MAX_ALG_NAME         256
+#define WMFW_MAX_ALG_DESCR_NAME   256
+
+#define WMFW_MAX_COEFF_NAME       256
+#define WMFW_MAX_COEFF_DESCR_NAME 256
+
+#define WMFW_CTL_FLAG_SYS         0x8000
+#define WMFW_CTL_FLAG_VOLATILE    0x0004
+#define WMFW_CTL_FLAG_WRITEABLE   0x0002
+#define WMFW_CTL_FLAG_READABLE    0x0001
+
 struct wmfw_header {
 	char magic[4];
 	__le32 len;
@@ -61,7 +72,7 @@ struct wmfw_adsp1_id_hdr {
 	struct wmfw_id_hdr fw;
 	__be32 zm;
 	__be32 dm;
-	__be32 algs;
+	__be32 n_algs;
 } __packed;
 
 struct wmfw_adsp2_id_hdr {
@@ -69,7 +80,7 @@ struct wmfw_adsp2_id_hdr {
 	__be32 zm;
 	__be32 xm;
 	__be32 ym;
-	__be32 algs;
+	__be32 n_algs;
 } __packed;
 
 struct wmfw_alg_hdr {
@@ -90,6 +101,28 @@ struct wmfw_adsp2_alg_hdr {
 	__be32 ym;
 } __packed;
 
+struct wmfw_adsp_alg_data {
+	__le32 id;
+	u8 name[WMFW_MAX_ALG_NAME];
+	u8 descr[WMFW_MAX_ALG_DESCR_NAME];
+	__le32 ncoeff;
+	u8 data[];
+} __packed;
+
+struct wmfw_adsp_coeff_data {
+	struct {
+		__le16 offset;
+		__le16 type;
+		__le32 size;
+	} hdr;
+	u8 name[WMFW_MAX_COEFF_NAME];
+	u8 descr[WMFW_MAX_COEFF_DESCR_NAME];
+	__le16 ctl_type;
+	__le16 flags;
+	__le32 len;
+	u8 data[];
+} __packed;
+
 struct wmfw_coeff_hdr {
 	u8 magic[4];
 	__le32 len;
@@ -117,9 +150,10 @@ struct wmfw_coeff_item {
 #define WMFW_ADSP1 1
 #define WMFW_ADSP2 2
 
-#define WMFW_ABSOLUTE  0xf0
-#define WMFW_NAME_TEXT 0xfe
-#define WMFW_INFO_TEXT 0xff
+#define WMFW_ABSOLUTE         0xf0
+#define WMFW_ALGORITHM_DATA   0xf2
+#define WMFW_NAME_TEXT        0xfe
+#define WMFW_INFO_TEXT        0xff
 
 #define WMFW_ADSP1_PM 2
 #define WMFW_ADSP1_DM 3
diff --git a/sound/soc/davinci/davinci-mcasp.c b/sound/soc/davinci/davinci-mcasp.c
index 23c91fa65ab8..d79349434a9a 100644
--- a/sound/soc/davinci/davinci-mcasp.c
+++ b/sound/soc/davinci/davinci-mcasp.c
@@ -107,6 +107,7 @@ struct davinci_mcasp {
 #endif
 
 	struct davinci_mcasp_ruledata ruledata[2];
+	struct snd_pcm_hw_constraint_list chconstr[2];
 };
 
 static inline void mcasp_set_bits(struct davinci_mcasp *mcasp, u32 offset,
@@ -915,15 +916,12 @@ static int davinci_mcasp_hw_params(struct snd_pcm_substream *substream,
 	 * the machine driver, we need to calculate the ratio.
 	 */
 	if (mcasp->bclk_master && mcasp->bclk_div == 0 && mcasp->sysclk_freq) {
-		int channels = params_channels(params);
+		int slots = mcasp->tdm_slots;
 		int rate = params_rate(params);
 		int sbits = params_width(params);
 		int ppm, div;
 
-		if (channels > mcasp->tdm_slots)
-			channels = mcasp->tdm_slots;
-
-		div = davinci_mcasp_calc_clk_div(mcasp, rate*sbits*channels,
+		div = davinci_mcasp_calc_clk_div(mcasp, rate*sbits*slots,
 						 &ppm);
 		if (ppm)
 			dev_info(mcasp->dev, "Sample-rate is off by %d PPM\n",
@@ -1024,31 +1022,36 @@ static int davinci_mcasp_hw_rule_rate(struct snd_pcm_hw_params *params,
 	struct snd_interval *ri =
 		hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);
 	int sbits = params_width(params);
-	int channels = params_channels(params);
-	unsigned int list[ARRAY_SIZE(davinci_mcasp_dai_rates)];
-	int i, count = 0;
+	int slots = rd->mcasp->tdm_slots;
+	struct snd_interval range;
+	int i;
 
-	if (channels > rd->mcasp->tdm_slots)
-		channels = rd->mcasp->tdm_slots;
+	snd_interval_any(&range);
+	range.empty = 1;
 
 	for (i = 0; i < ARRAY_SIZE(davinci_mcasp_dai_rates); i++) {
-		if (ri->min <= davinci_mcasp_dai_rates[i] &&
-		    ri->max >= davinci_mcasp_dai_rates[i]) {
-			uint bclk_freq = sbits*channels*
+		if (snd_interval_test(ri, davinci_mcasp_dai_rates[i])) {
+			uint bclk_freq = sbits*slots*
 				davinci_mcasp_dai_rates[i];
 			int ppm;
 
 			davinci_mcasp_calc_clk_div(rd->mcasp, bclk_freq, &ppm);
-			if (abs(ppm) < DAVINCI_MAX_RATE_ERROR_PPM)
-				list[count++] = davinci_mcasp_dai_rates[i];
+			if (abs(ppm) < DAVINCI_MAX_RATE_ERROR_PPM) {
+				if (range.empty) {
+					range.min = davinci_mcasp_dai_rates[i];
+					range.empty = 0;
+				}
+				range.max = davinci_mcasp_dai_rates[i];
+			}
 		}
 	}
+
 	dev_dbg(rd->mcasp->dev,
-		"%d frequencies (%d-%d) for %d sbits and %d channels\n",
-		count, ri->min, ri->max, sbits, channels);
+		"Frequencies %d-%d -> %d-%d for %d sbits and %d tdm slots\n",
+		ri->min, ri->max, range.min, range.max, sbits, slots);
 
-	return snd_interval_list(hw_param_interval(params, rule->var),
-				 count, list, 0);
+	return snd_interval_refine(hw_param_interval(params, rule->var),
+				   &range);
 }
 
 static int davinci_mcasp_hw_rule_format(struct snd_pcm_hw_params *params,
@@ -1058,17 +1061,14 @@ static int davinci_mcasp_hw_rule_format(struct snd_pcm_hw_params *params,
 	struct snd_mask *fmt = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT);
 	struct snd_mask nfmt;
 	int rate = params_rate(params);
-	int channels = params_channels(params);
+	int slots = rd->mcasp->tdm_slots;
 	int i, count = 0;
 
 	snd_mask_none(&nfmt);
 
-	if (channels > rd->mcasp->tdm_slots)
-		channels = rd->mcasp->tdm_slots;
-
 	for (i = 0; i < SNDRV_PCM_FORMAT_LAST; i++) {
 		if (snd_mask_test(fmt, i)) {
-			uint bclk_freq = snd_pcm_format_width(i)*channels*rate;
+			uint bclk_freq = snd_pcm_format_width(i)*slots*rate;
 			int ppm;
 
 			davinci_mcasp_calc_clk_div(rd->mcasp, bclk_freq, &ppm);
@@ -1079,51 +1079,12 @@ static int davinci_mcasp_hw_rule_format(struct snd_pcm_hw_params *params,
 		}
 	}
 	dev_dbg(rd->mcasp->dev,
-		"%d possible sample format for %d Hz and %d channels\n",
-		count, rate, channels);
+		"%d possible sample format for %d Hz and %d tdm slots\n",
+		count, rate, slots);
 
 	return snd_mask_refine(fmt, &nfmt);
 }
 
-static int davinci_mcasp_hw_rule_channels(struct snd_pcm_hw_params *params,
-					  struct snd_pcm_hw_rule *rule)
-{
-	struct davinci_mcasp_ruledata *rd = rule->private;
-	struct snd_interval *ci =
-		hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS);
-	int sbits = params_width(params);
-	int rate = params_rate(params);
-	int max_chan_per_wire = rd->mcasp->tdm_slots < ci->max ?
-		rd->mcasp->tdm_slots : ci->max;
-	unsigned int list[ci->max - ci->min + 1];
-	int c1, c, count = 0;
-
-	for (c1 = ci->min; c1 <= max_chan_per_wire; c1++) {
-		uint bclk_freq = c1*sbits*rate;
-		int ppm;
-
-		davinci_mcasp_calc_clk_div(rd->mcasp, bclk_freq, &ppm);
-		if (abs(ppm) < DAVINCI_MAX_RATE_ERROR_PPM) {
-			/* If we can use all tdm_slots, we can put any
-			   amount of channels to remaining wires as
-			   long as they fit in. */
-			if (c1 == rd->mcasp->tdm_slots) {
-				for (c = c1; c <= rd->serializers*c1 &&
-					     c <= ci->max; c++)
-					list[count++] = c;
-			} else {
-				list[count++] = c1;
-			}
-		}
-	}
-	dev_dbg(rd->mcasp->dev,
-		"%d possible channel counts (%d-%d) for %d Hz and %d sbits\n",
-		count, ci->min, ci->max, rate, sbits);
-
-	return snd_interval_list(hw_param_interval(params, rule->var),
-				 count, list, 0);
-}
-
 static int davinci_mcasp_startup(struct snd_pcm_substream *substream,
 				 struct snd_soc_dai *cpu_dai)
 {
@@ -1167,6 +1128,11 @@ static int davinci_mcasp_startup(struct snd_pcm_substream *substream,
 				     SNDRV_PCM_HW_PARAM_CHANNELS,
 				     2, max_channels);
 
+	if (mcasp->chconstr[substream->stream].count)
+		snd_pcm_hw_constraint_list(substream->runtime,
+					   0, SNDRV_PCM_HW_PARAM_CHANNELS,
+					   &mcasp->chconstr[substream->stream]);
+
 	/*
 	 * If we rely on implicit BCLK divider setting we should
 	 * set constraints based on what we can provide.
@@ -1180,24 +1146,14 @@ static int davinci_mcasp_startup(struct snd_pcm_substream *substream,
 					  SNDRV_PCM_HW_PARAM_RATE,
 					  davinci_mcasp_hw_rule_rate,
 					  ruledata,
-					  SNDRV_PCM_HW_PARAM_FORMAT,
-					  SNDRV_PCM_HW_PARAM_CHANNELS, -1);
+					  SNDRV_PCM_HW_PARAM_FORMAT, -1);
 		if (ret)
 			return ret;
 		ret = snd_pcm_hw_rule_add(substream->runtime, 0,
 					  SNDRV_PCM_HW_PARAM_FORMAT,
 					  davinci_mcasp_hw_rule_format,
 					  ruledata,
-					  SNDRV_PCM_HW_PARAM_RATE,
-					  SNDRV_PCM_HW_PARAM_CHANNELS, -1);
-		if (ret)
-			return ret;
-		ret = snd_pcm_hw_rule_add(substream->runtime, 0,
-					  SNDRV_PCM_HW_PARAM_CHANNELS,
-					  davinci_mcasp_hw_rule_channels,
-					  ruledata,
-					  SNDRV_PCM_HW_PARAM_RATE,
-					  SNDRV_PCM_HW_PARAM_FORMAT, -1);
+					  SNDRV_PCM_HW_PARAM_RATE, -1);
 		if (ret)
 			return ret;
 	}
@@ -1556,6 +1512,59 @@ nodata:
 	return  pdata;
 }
 
+/* All serializers must have equal number of channels */
+static int davinci_mcasp_ch_constraint(struct davinci_mcasp *mcasp,
+				       struct snd_pcm_hw_constraint_list *cl,
+				       int serializers)
+{
+	unsigned int *list;
+	int i, count = 0;
+
+	if (serializers <= 1)
+		return 0;
+
+	list = devm_kzalloc(mcasp->dev, sizeof(unsigned int) *
+			    (mcasp->tdm_slots + serializers - 2),
+			    GFP_KERNEL);
+	if (!list)
+		return -ENOMEM;
+
+	for (i = 2; i <= mcasp->tdm_slots; i++)
+		list[count++] = i;
+
+	for (i = 2; i <= serializers; i++)
+		list[count++] = i*mcasp->tdm_slots;
+
+	cl->count = count;
+	cl->list = list;
+
+	return 0;
+}
+
+
+static int davinci_mcasp_init_ch_constraints(struct davinci_mcasp *mcasp)
+{
+	int rx_serializers = 0, tx_serializers = 0, ret, i;
+
+	for (i = 0; i < mcasp->num_serializer; i++)
+		if (mcasp->serial_dir[i] == TX_MODE)
+			tx_serializers++;
+		else if (mcasp->serial_dir[i] == RX_MODE)
+			rx_serializers++;
+
+	ret = davinci_mcasp_ch_constraint(mcasp, &mcasp->chconstr[
+						  SNDRV_PCM_STREAM_PLAYBACK],
+					  tx_serializers);
+	if (ret)
+		return ret;
+
+	ret = davinci_mcasp_ch_constraint(mcasp, &mcasp->chconstr[
+						  SNDRV_PCM_STREAM_CAPTURE],
+					  rx_serializers);
+
+	return ret;
+}
+
 static int davinci_mcasp_probe(struct platform_device *pdev)
 {
 	struct snd_dmaengine_dai_dma_data *dma_data;
@@ -1739,6 +1748,10 @@ static int davinci_mcasp_probe(struct platform_device *pdev)
 		mcasp->fifo_base = DAVINCI_MCASP_V3_AFIFO_BASE;
 	}
 
+	ret = davinci_mcasp_init_ch_constraints(mcasp);
+	if (ret)
+		goto err;
+
 	dev_set_drvdata(&pdev->dev, mcasp);
 
 	mcasp_reparent_fck(pdev);
diff --git a/sound/soc/fsl/fsl_dma.c b/sound/soc/fsl/fsl_dma.c
index 93d7e56c6066..ccadefceeff2 100644
--- a/sound/soc/fsl/fsl_dma.c
+++ b/sound/soc/fsl/fsl_dma.c
@@ -445,7 +445,7 @@ static int fsl_dma_open(struct snd_pcm_substream *substream)
 		return ret;
 	}
 
-	dma->assigned = 1;
+	dma->assigned = true;
 
 	snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
 	snd_soc_set_runtime_hwparams(substream, &fsl_dma_hardware);
@@ -814,7 +814,7 @@ static int fsl_dma_close(struct snd_pcm_substream *substream)
 		substream->runtime->private_data = NULL;
 	}
 
-	dma->assigned = 0;
+	dma->assigned = false;
 
 	return 0;
 }
diff --git a/sound/soc/fsl/fsl_sai.c b/sound/soc/fsl/fsl_sai.c
index ec79c3d5e65e..5c73bea7b11e 100644
--- a/sound/soc/fsl/fsl_sai.c
+++ b/sound/soc/fsl/fsl_sai.c
@@ -1,7 +1,7 @@
 /*
  * Freescale ALSA SoC Digital Audio Interface (SAI) driver.
  *
- * Copyright 2012-2013 Freescale Semiconductor, Inc.
+ * Copyright 2012-2015 Freescale Semiconductor, Inc.
  *
  * This program is free software, you can redistribute it and/or modify it
  * under the terms of the GNU General Public License as published by the
@@ -27,6 +27,17 @@
 #define FSL_SAI_FLAGS (FSL_SAI_CSR_SEIE |\
 		       FSL_SAI_CSR_FEIE)
 
+static u32 fsl_sai_rates[] = {
+	8000, 11025, 12000, 16000, 22050,
+	24000, 32000, 44100, 48000, 64000,
+	88200, 96000, 176400, 192000
+};
+
+static struct snd_pcm_hw_constraint_list fsl_sai_rate_constraints = {
+	.count = ARRAY_SIZE(fsl_sai_rates),
+	.list = fsl_sai_rates,
+};
+
 static irqreturn_t fsl_sai_isr(int irq, void *devid)
 {
 	struct fsl_sai *sai = (struct fsl_sai *)devid;
@@ -251,12 +262,14 @@ static int fsl_sai_set_dai_fmt_tr(struct snd_soc_dai *cpu_dai,
 		val_cr4 |= FSL_SAI_CR4_FSD_MSTR;
 		break;
 	case SND_SOC_DAIFMT_CBM_CFM:
+		sai->is_slave_mode = true;
 		break;
 	case SND_SOC_DAIFMT_CBS_CFM:
 		val_cr2 |= FSL_SAI_CR2_BCD_MSTR;
 		break;
 	case SND_SOC_DAIFMT_CBM_CFS:
 		val_cr4 |= FSL_SAI_CR4_FSD_MSTR;
+		sai->is_slave_mode = true;
 		break;
 	default:
 		return -EINVAL;
@@ -288,6 +301,79 @@ static int fsl_sai_set_dai_fmt(struct snd_soc_dai *cpu_dai, unsigned int fmt)
 	return ret;
 }
 
+static int fsl_sai_set_bclk(struct snd_soc_dai *dai, bool tx, u32 freq)
+{
+	struct fsl_sai *sai = snd_soc_dai_get_drvdata(dai);
+	unsigned long clk_rate;
+	u32 savediv = 0, ratio, savesub = freq;
+	u32 id;
+	int ret = 0;
+
+	/* Don't apply to slave mode */
+	if (sai->is_slave_mode)
+		return 0;
+
+	for (id = 0; id < FSL_SAI_MCLK_MAX; id++) {
+		clk_rate = clk_get_rate(sai->mclk_clk[id]);
+		if (!clk_rate)
+			continue;
+
+		ratio = clk_rate / freq;
+
+		ret = clk_rate - ratio * freq;
+
+		/*
+		 * Drop the source that can not be
+		 * divided into the required rate.
+		 */
+		if (ret != 0 && clk_rate / ret < 1000)
+			continue;
+
+		dev_dbg(dai->dev,
+			"ratio %d for freq %dHz based on clock %ldHz\n",
+			ratio, freq, clk_rate);
+
+		if (ratio % 2 == 0 && ratio >= 2 && ratio <= 512)
+			ratio /= 2;
+		else
+			continue;
+
+		if (ret < savesub) {
+			savediv = ratio;
+			sai->mclk_id[tx] = id;
+			savesub = ret;
+		}
+
+		if (ret == 0)
+			break;
+	}
+
+	if (savediv == 0) {
+		dev_err(dai->dev, "failed to derive required %cx rate: %d\n",
+				tx ? 'T' : 'R', freq);
+		return -EINVAL;
+	}
+
+	if ((tx && sai->synchronous[TX]) || (!tx && !sai->synchronous[RX])) {
+		regmap_update_bits(sai->regmap, FSL_SAI_RCR2,
+				   FSL_SAI_CR2_MSEL_MASK,
+				   FSL_SAI_CR2_MSEL(sai->mclk_id[tx]));
+		regmap_update_bits(sai->regmap, FSL_SAI_RCR2,
+				   FSL_SAI_CR2_DIV_MASK, savediv - 1);
+	} else {
+		regmap_update_bits(sai->regmap, FSL_SAI_TCR2,
+				   FSL_SAI_CR2_MSEL_MASK,
+				   FSL_SAI_CR2_MSEL(sai->mclk_id[tx]));
+		regmap_update_bits(sai->regmap, FSL_SAI_TCR2,
+				   FSL_SAI_CR2_DIV_MASK, savediv - 1);
+	}
+
+	dev_dbg(dai->dev, "best fit: clock id=%d, div=%d, deviation =%d\n",
+			sai->mclk_id[tx], savediv, savesub);
+
+	return 0;
+}
+
 static int fsl_sai_hw_params(struct snd_pcm_substream *substream,
 		struct snd_pcm_hw_params *params,
 		struct snd_soc_dai *cpu_dai)
@@ -297,6 +383,24 @@ static int fsl_sai_hw_params(struct snd_pcm_substream *substream,
 	unsigned int channels = params_channels(params);
 	u32 word_width = snd_pcm_format_width(params_format(params));
 	u32 val_cr4 = 0, val_cr5 = 0;
+	int ret;
+
+	if (!sai->is_slave_mode) {
+		ret = fsl_sai_set_bclk(cpu_dai, tx,
+			2 * word_width * params_rate(params));
+		if (ret)
+			return ret;
+
+		/* Do not enable the clock if it is already enabled */
+		if (!(sai->mclk_streams & BIT(substream->stream))) {
+			ret = clk_prepare_enable(sai->mclk_clk[sai->mclk_id[tx]]);
+			if (ret)
+				return ret;
+
+			sai->mclk_streams |= BIT(substream->stream);
+		}
+
+	}
 
 	if (!sai->is_dsp_mode)
 		val_cr4 |= FSL_SAI_CR4_SYWD(word_width);
@@ -322,6 +426,22 @@ static int fsl_sai_hw_params(struct snd_pcm_substream *substream,
 	return 0;
 }
 
+static int fsl_sai_hw_free(struct snd_pcm_substream *substream,
+		struct snd_soc_dai *cpu_dai)
+{
+	struct fsl_sai *sai = snd_soc_dai_get_drvdata(cpu_dai);
+	bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
+
+	if (!sai->is_slave_mode &&
+			sai->mclk_streams & BIT(substream->stream)) {
+		clk_disable_unprepare(sai->mclk_clk[sai->mclk_id[tx]]);
+		sai->mclk_streams &= ~BIT(substream->stream);
+	}
+
+	return 0;
+}
+
+
 static int fsl_sai_trigger(struct snd_pcm_substream *substream, int cmd,
 		struct snd_soc_dai *cpu_dai)
 {
@@ -410,7 +530,10 @@ static int fsl_sai_startup(struct snd_pcm_substream *substream,
 	regmap_update_bits(sai->regmap, FSL_SAI_xCR3(tx), FSL_SAI_CR3_TRCE,
 			   FSL_SAI_CR3_TRCE);
 
-	return 0;
+	ret = snd_pcm_hw_constraint_list(substream->runtime, 0,
+			SNDRV_PCM_HW_PARAM_RATE, &fsl_sai_rate_constraints);
+
+	return ret;
 }
 
 static void fsl_sai_shutdown(struct snd_pcm_substream *substream,
@@ -428,6 +551,7 @@ static const struct snd_soc_dai_ops fsl_sai_pcm_dai_ops = {
 	.set_sysclk	= fsl_sai_set_dai_sysclk,
 	.set_fmt	= fsl_sai_set_dai_fmt,
 	.hw_params	= fsl_sai_hw_params,
+	.hw_free	= fsl_sai_hw_free,
 	.trigger	= fsl_sai_trigger,
 	.startup	= fsl_sai_startup,
 	.shutdown	= fsl_sai_shutdown,
@@ -463,14 +587,18 @@ static struct snd_soc_dai_driver fsl_sai_dai = {
 		.stream_name = "CPU-Playback",
 		.channels_min = 1,
 		.channels_max = 2,
-		.rates = SNDRV_PCM_RATE_8000_96000,
+		.rate_min = 8000,
+		.rate_max = 192000,
+		.rates = SNDRV_PCM_RATE_KNOT,
 		.formats = FSL_SAI_FORMATS,
 	},
 	.capture = {
 		.stream_name = "CPU-Capture",
 		.channels_min = 1,
 		.channels_max = 2,
-		.rates = SNDRV_PCM_RATE_8000_96000,
+		.rate_min = 8000,
+		.rate_max = 192000,
+		.rates = SNDRV_PCM_RATE_KNOT,
 		.formats = FSL_SAI_FORMATS,
 	},
 	.ops = &fsl_sai_pcm_dai_ops,
@@ -600,8 +728,9 @@ static int fsl_sai_probe(struct platform_device *pdev)
 		sai->bus_clk = NULL;
 	}
 
-	for (i = 0; i < FSL_SAI_MCLK_MAX; i++) {
-		sprintf(tmp, "mclk%d", i + 1);
+	sai->mclk_clk[0] = sai->bus_clk;
+	for (i = 1; i < FSL_SAI_MCLK_MAX; i++) {
+		sprintf(tmp, "mclk%d", i);
 		sai->mclk_clk[i] = devm_clk_get(&pdev->dev, tmp);
 		if (IS_ERR(sai->mclk_clk[i])) {
 			dev_err(&pdev->dev, "failed to get mclk%d clock: %ld\n",
@@ -664,8 +793,7 @@ static int fsl_sai_probe(struct platform_device *pdev)
 	if (sai->sai_on_imx)
 		return imx_pcm_dma_init(pdev);
 	else
-		return devm_snd_dmaengine_pcm_register(&pdev->dev, NULL,
-				SND_DMAENGINE_PCM_FLAG_NO_RESIDUE);
+		return devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, 0);
 }
 
 static const struct of_device_id fsl_sai_ids[] = {
diff --git a/sound/soc/fsl/fsl_sai.h b/sound/soc/fsl/fsl_sai.h
index 34667209b607..066280953c85 100644
--- a/sound/soc/fsl/fsl_sai.h
+++ b/sound/soc/fsl/fsl_sai.h
@@ -72,13 +72,15 @@
 
 /* SAI Transmit and Recieve Configuration 2 Register */
 #define FSL_SAI_CR2_SYNC	BIT(30)
-#define FSL_SAI_CR2_MSEL_MASK	(0xff << 26)
+#define FSL_SAI_CR2_MSEL_MASK	(0x3 << 26)
 #define FSL_SAI_CR2_MSEL_BUS	0
 #define FSL_SAI_CR2_MSEL_MCLK1	BIT(26)
 #define FSL_SAI_CR2_MSEL_MCLK2	BIT(27)
 #define FSL_SAI_CR2_MSEL_MCLK3	(BIT(26) | BIT(27))
+#define FSL_SAI_CR2_MSEL(ID)	((ID) << 26)
 #define FSL_SAI_CR2_BCP		BIT(25)
 #define FSL_SAI_CR2_BCD_MSTR	BIT(24)
+#define FSL_SAI_CR2_DIV_MASK	0xff
 
 /* SAI Transmit and Recieve Configuration 3 Register */
 #define FSL_SAI_CR3_TRCE	BIT(16)
@@ -120,7 +122,7 @@
 #define FSL_SAI_CLK_MAST2	2
 #define FSL_SAI_CLK_MAST3	3
 
-#define FSL_SAI_MCLK_MAX	3
+#define FSL_SAI_MCLK_MAX	4
 
 /* SAI data transfer numbers per DMA request */
 #define FSL_SAI_MAXBURST_TX 6
@@ -132,11 +134,14 @@ struct fsl_sai {
 	struct clk *bus_clk;
 	struct clk *mclk_clk[FSL_SAI_MCLK_MAX];
 
+	bool is_slave_mode;
 	bool is_lsb_first;
 	bool is_dsp_mode;
 	bool sai_on_imx;
 	bool synchronous[2];
 
+	unsigned int mclk_id[2];
+	unsigned int mclk_streams;
 	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 91eb3aef7f02..8e932219cb3a 100644
--- a/sound/soc/fsl/fsl_spdif.c
+++ b/sound/soc/fsl/fsl_spdif.c
@@ -417,11 +417,9 @@ static int spdif_set_sample_rate(struct snd_pcm_substream *substream,
 	if (clk != STC_TXCLK_SPDIF_ROOT)
 		goto clk_set_bypass;
 
-	/*
-	 * The S/PDIF block needs a clock of 64 * fs * txclk_df.
-	 * So request 64 * fs * (txclk_df + 1) to get rounded.
-	 */
-	ret = clk_set_rate(spdif_priv->txclk[rate], 64 * sample_rate * (txclk_df + 1));
+	/* The S/PDIF block needs a clock of 64 * fs * txclk_df */
+	ret = clk_set_rate(spdif_priv->txclk[rate],
+			   64 * sample_rate * txclk_df);
 	if (ret) {
 		dev_err(&pdev->dev, "failed to set tx clock rate\n");
 		return ret;
@@ -1060,7 +1058,7 @@ static u32 fsl_spdif_txclk_caldiv(struct fsl_spdif_priv *spdif_priv,
 
 	for (sysclk_df = sysclk_dfmin; sysclk_df <= sysclk_dfmax; sysclk_df++) {
 		for (txclk_df = 1; txclk_df <= 128; txclk_df++) {
-			rate_ideal = rate[index] * (txclk_df + 1) * 64;
+			rate_ideal = rate[index] * txclk_df * 64;
 			if (round)
 				rate_actual = clk_round_rate(clk, rate_ideal);
 			else
diff --git a/sound/soc/fsl/fsl_ssi.c b/sound/soc/fsl/fsl_ssi.c
index 0d48804218b1..c7647e066cfd 100644
--- a/sound/soc/fsl/fsl_ssi.c
+++ b/sound/soc/fsl/fsl_ssi.c
@@ -1292,13 +1292,6 @@ static int fsl_ssi_probe(struct platform_device *pdev)
 	void __iomem *iomem;
 	char name[64];
 
-	/* SSIs that are not connected on the board should have a
-	 *      status = "disabled"
-	 * property in their device tree nodes.
-	 */
-	if (!of_device_is_available(np))
-		return -ENODEV;
-
 	of_id = of_match_device(fsl_ssi_ids, &pdev->dev);
 	if (!of_id || !of_id->data)
 		return -EINVAL;
diff --git a/sound/soc/fsl/imx-audmux.c b/sound/soc/fsl/imx-audmux.c
index d9050d946ae7..fc57da341d61 100644
--- a/sound/soc/fsl/imx-audmux.c
+++ b/sound/soc/fsl/imx-audmux.c
@@ -184,7 +184,7 @@ static enum imx_audmux_type {
 	IMX31_AUDMUX,
 } audmux_type;
 
-static struct platform_device_id imx_audmux_ids[] = {
+static const struct platform_device_id imx_audmux_ids[] = {
 	{
 		.name = "imx21-audmux",
 		.driver_data = IMX21_AUDMUX,
diff --git a/sound/soc/fsl/imx-mc13783.c b/sound/soc/fsl/imx-mc13783.c
index 9e6493d4e7ff..bb0459018b45 100644
--- a/sound/soc/fsl/imx-mc13783.c
+++ b/sound/soc/fsl/imx-mc13783.c
@@ -45,11 +45,7 @@ static int imx_mc13783_hifi_hw_params(struct snd_pcm_substream *substream,
 	if (ret)
 		return ret;
 
-	ret = snd_soc_dai_set_tdm_slot(cpu_dai, 0x3, 0x3, 2, 16);
-	if (ret)
-		return ret;
-
-	return 0;
+	return snd_soc_dai_set_tdm_slot(cpu_dai, 0x3, 0x3, 2, 16);
 }
 
 static struct snd_soc_ops imx_mc13783_hifi_ops = {
diff --git a/sound/soc/generic/simple-card.c b/sound/soc/generic/simple-card.c
index 33feee9ca8c3..c87e58504a62 100644
--- a/sound/soc/generic/simple-card.c
+++ b/sound/soc/generic/simple-card.c
@@ -307,6 +307,7 @@ static int asoc_simple_card_dai_link_of(struct device_node *node,
 	struct snd_soc_dai_link *dai_link = simple_priv_to_link(priv, idx);
 	struct simple_dai_props *dai_props = simple_priv_to_props(priv, idx);
 	struct device_node *cpu = NULL;
+	struct device_node *plat = NULL;
 	struct device_node *codec = NULL;
 	char *name;
 	char prop[128];
@@ -320,6 +321,9 @@ static int asoc_simple_card_dai_link_of(struct device_node *node,
 	snprintf(prop, sizeof(prop), "%scpu", prefix);
 	cpu = of_get_child_by_name(node, prop);
 
+	snprintf(prop, sizeof(prop), "%splat", prefix);
+	plat = of_get_child_by_name(node, prop);
+
 	snprintf(prop, sizeof(prop), "%scodec", prefix);
 	codec = of_get_child_by_name(node, prop);
 
@@ -352,8 +356,16 @@ static int asoc_simple_card_dai_link_of(struct device_node *node,
 		goto dai_link_of_err;
 	}
 
-	/* Simple Card assumes platform == cpu */
-	dai_link->platform_of_node = dai_link->cpu_of_node;
+	if (plat) {
+		struct of_phandle_args args;
+
+		ret = of_parse_phandle_with_args(plat, "sound-dai",
+						 "#sound-dai-cells", 0, &args);
+		dai_link->platform_of_node = args.np;
+	} else {
+		/* Assumes platform == cpu */
+		dai_link->platform_of_node = dai_link->cpu_of_node;
+	}
 
 	/* DAI link name is created from CPU/CODEC dai name */
 	name = devm_kzalloc(dev,
diff --git a/sound/soc/intel/Kconfig b/sound/soc/intel/Kconfig
index ee03dbdda235..791953ffbc41 100644
--- a/sound/soc/intel/Kconfig
+++ b/sound/soc/intel/Kconfig
@@ -79,7 +79,6 @@ config SND_SOC_INTEL_BROADWELL_MACH
 	depends on SND_SOC_INTEL_SST && X86_INTEL_LPSS && DW_DMAC && \
 		   I2C_DESIGNWARE_PLATFORM
 	select SND_SOC_INTEL_HASWELL
-	select SND_COMPRESS_OFFLOAD
 	select SND_SOC_RT286
 	help
 	  This adds support for the Wilcatpoint Audio DSP on Intel(R) Broadwell
@@ -112,12 +111,24 @@ config SND_SOC_INTEL_CHT_BSW_RT5672_MACH
           If unsure select "N".
 
 config SND_SOC_INTEL_CHT_BSW_RT5645_MACH
-	tristate "ASoC Audio driver for Intel Cherrytrail & Braswell with RT5645 codec"
+	tristate "ASoC Audio driver for Intel Cherrytrail & Braswell with RT5645/5650 codec"
 	depends on X86_INTEL_LPSS
 	select SND_SOC_RT5645
 	select SND_SST_MFLD_PLATFORM
 	select SND_SST_IPC_ACPI
 	help
 	  This adds support for ASoC machine driver for Intel(R) Cherrytrail & Braswell
-	  platforms with RT5645 audio codec.
+	  platforms with RT5645/5650 audio codec.
 	  If unsure select "N".
+
+config SND_SOC_INTEL_CHT_BSW_MAX98090_TI_MACH
+	tristate "ASoC Audio driver for Intel Cherrytrail & Braswell with MAX98090 & TI codec"
+	depends on X86_INTEL_LPSS
+	select SND_SOC_MAX98090
+	select SND_SOC_TS3A227E
+	select SND_SST_MFLD_PLATFORM
+	select SND_SST_IPC_ACPI
+	help
+      This adds support for ASoC machine driver for Intel(R) Cherrytrail & Braswell
+      platforms with MAX98090 audio codec it also can support TI jack chip as aux device.
+      If unsure select "N".
diff --git a/sound/soc/intel/atom/sst-atom-controls.c b/sound/soc/intel/atom/sst-atom-controls.c
index 90aa5c0476f3..61e240935451 100644
--- a/sound/soc/intel/atom/sst-atom-controls.c
+++ b/sound/soc/intel/atom/sst-atom-controls.c
@@ -774,8 +774,120 @@ int sst_handle_vb_timer(struct snd_soc_dai *dai, bool enable)
 	return ret;
 }
 
+int sst_fill_ssp_slot(struct snd_soc_dai *dai, unsigned int tx_mask,
+		unsigned int rx_mask, int slots, int slot_width)
+{
+	struct sst_data *ctx = snd_soc_dai_get_drvdata(dai);
+
+	ctx->ssp_cmd.nb_slots = slots;
+	ctx->ssp_cmd.active_tx_slot_map = tx_mask;
+	ctx->ssp_cmd.active_rx_slot_map = rx_mask;
+	ctx->ssp_cmd.nb_bits_per_slots = slot_width;
+
+	return 0;
+}
+
+static int sst_get_frame_sync_polarity(struct snd_soc_dai *dai,
+		unsigned int fmt)
+{
+	int format;
+
+	format = fmt & SND_SOC_DAIFMT_INV_MASK;
+	dev_dbg(dai->dev, "Enter:%s, format=%x\n", __func__, format);
+
+	switch (format) {
+	case SND_SOC_DAIFMT_NB_NF:
+		return SSP_FS_ACTIVE_LOW;
+	case SND_SOC_DAIFMT_NB_IF:
+		return SSP_FS_ACTIVE_HIGH;
+	case SND_SOC_DAIFMT_IB_IF:
+		return SSP_FS_ACTIVE_LOW;
+	case SND_SOC_DAIFMT_IB_NF:
+		return SSP_FS_ACTIVE_HIGH;
+	default:
+		dev_err(dai->dev, "Invalid frame sync polarity %d\n", format);
+	}
+
+	return -EINVAL;
+}
+
+static int sst_get_ssp_mode(struct snd_soc_dai *dai, unsigned int fmt)
+{
+	int format;
+
+	format = (fmt & SND_SOC_DAIFMT_MASTER_MASK);
+	dev_dbg(dai->dev, "Enter:%s, format=%x\n", __func__, format);
+
+	switch (format) {
+	case SND_SOC_DAIFMT_CBS_CFS:
+		return SSP_MODE_MASTER;
+	case SND_SOC_DAIFMT_CBM_CFM:
+		return SSP_MODE_SLAVE;
+	default:
+		dev_err(dai->dev, "Invalid ssp protocol: %d\n", format);
+	}
+
+	return -EINVAL;
+}
+
+
+int sst_fill_ssp_config(struct snd_soc_dai *dai, unsigned int fmt)
+{
+	unsigned int mode;
+	int fs_polarity;
+	struct sst_data *ctx = snd_soc_dai_get_drvdata(dai);
+
+	mode = fmt & SND_SOC_DAIFMT_FORMAT_MASK;
+
+	switch (mode) {
+	case SND_SOC_DAIFMT_DSP_B:
+		ctx->ssp_cmd.ssp_protocol = SSP_MODE_PCM;
+		ctx->ssp_cmd.mode = sst_get_ssp_mode(dai, fmt) | (SSP_PCM_MODE_NETWORK << 1);
+		ctx->ssp_cmd.start_delay = 0;
+		ctx->ssp_cmd.data_polarity = 1;
+		ctx->ssp_cmd.frame_sync_width = 1;
+		break;
+
+	case SND_SOC_DAIFMT_DSP_A:
+		ctx->ssp_cmd.ssp_protocol = SSP_MODE_PCM;
+		ctx->ssp_cmd.mode = sst_get_ssp_mode(dai, fmt) | (SSP_PCM_MODE_NETWORK << 1);
+		ctx->ssp_cmd.start_delay = 1;
+		ctx->ssp_cmd.data_polarity = 1;
+		ctx->ssp_cmd.frame_sync_width = 1;
+		break;
+
+	case SND_SOC_DAIFMT_I2S:
+		ctx->ssp_cmd.ssp_protocol = SSP_MODE_I2S;
+		ctx->ssp_cmd.mode = sst_get_ssp_mode(dai, fmt) | (SSP_PCM_MODE_NORMAL << 1);
+		ctx->ssp_cmd.start_delay = 1;
+		ctx->ssp_cmd.data_polarity = 0;
+		ctx->ssp_cmd.frame_sync_width = ctx->ssp_cmd.nb_bits_per_slots;
+		break;
+
+	case SND_SOC_DAIFMT_LEFT_J:
+		ctx->ssp_cmd.ssp_protocol = SSP_MODE_I2S;
+		ctx->ssp_cmd.mode = sst_get_ssp_mode(dai, fmt) | (SSP_PCM_MODE_NORMAL << 1);
+		ctx->ssp_cmd.start_delay = 0;
+		ctx->ssp_cmd.data_polarity = 0;
+		ctx->ssp_cmd.frame_sync_width = ctx->ssp_cmd.nb_bits_per_slots;
+		break;
+
+	default:
+		dev_dbg(dai->dev, "using default ssp configs\n");
+	}
+
+	fs_polarity = sst_get_frame_sync_polarity(dai, fmt);
+	if (fs_polarity < 0)
+		return fs_polarity;
+
+	ctx->ssp_cmd.frame_sync_polarity = fs_polarity;
+
+	return 0;
+}
+
 /**
  * sst_ssp_config - contains SSP configuration for media UC
+ * this can be overwritten by set_dai_xxx APIs
  */
 static const struct sst_ssp_config sst_ssp_configs = {
 	.ssp_id = SSP_CODEC,
@@ -789,47 +901,56 @@ static const struct sst_ssp_config sst_ssp_configs = {
 	.fs_frequency = SSP_FS_48_KHZ,
 	.active_slot_map = 0xF,
 	.start_delay = 0,
+	.frame_sync_polarity = SSP_FS_ACTIVE_HIGH,
+	.data_polarity = 1,
 };
 
+void sst_fill_ssp_defaults(struct snd_soc_dai *dai)
+{
+	const struct sst_ssp_config *config;
+	struct sst_data *ctx = snd_soc_dai_get_drvdata(dai);
+
+	config = &sst_ssp_configs;
+
+	ctx->ssp_cmd.selection = config->ssp_id;
+	ctx->ssp_cmd.nb_bits_per_slots = config->bits_per_slot;
+	ctx->ssp_cmd.nb_slots = config->slots;
+	ctx->ssp_cmd.mode = config->ssp_mode | (config->pcm_mode << 1);
+	ctx->ssp_cmd.duplex = config->duplex;
+	ctx->ssp_cmd.active_tx_slot_map = config->active_slot_map;
+	ctx->ssp_cmd.active_rx_slot_map = config->active_slot_map;
+	ctx->ssp_cmd.frame_sync_frequency = config->fs_frequency;
+	ctx->ssp_cmd.frame_sync_polarity = config->frame_sync_polarity;
+	ctx->ssp_cmd.data_polarity = config->data_polarity;
+	ctx->ssp_cmd.frame_sync_width = config->fs_width;
+	ctx->ssp_cmd.ssp_protocol = config->ssp_protocol;
+	ctx->ssp_cmd.start_delay = config->start_delay;
+	ctx->ssp_cmd.reserved1 = ctx->ssp_cmd.reserved2 = 0xFF;
+}
+
 int send_ssp_cmd(struct snd_soc_dai *dai, const char *id, bool enable)
 {
-	struct sst_cmd_sba_hw_set_ssp cmd;
 	struct sst_data *drv = snd_soc_dai_get_drvdata(dai);
 	const struct sst_ssp_config *config;
 
 	dev_info(dai->dev, "Enter: enable=%d port_name=%s\n", enable, id);
 
-	SST_FILL_DEFAULT_DESTINATION(cmd.header.dst);
-	cmd.header.command_id = SBA_HW_SET_SSP;
-	cmd.header.length = sizeof(struct sst_cmd_sba_hw_set_ssp)
+	SST_FILL_DEFAULT_DESTINATION(drv->ssp_cmd.header.dst);
+	drv->ssp_cmd.header.command_id = SBA_HW_SET_SSP;
+	drv->ssp_cmd.header.length = sizeof(struct sst_cmd_sba_hw_set_ssp)
 				- sizeof(struct sst_dsp_header);
 
 	config = &sst_ssp_configs;
 	dev_dbg(dai->dev, "ssp_id: %u\n", config->ssp_id);
 
 	if (enable)
-		cmd.switch_state = SST_SWITCH_ON;
+		drv->ssp_cmd.switch_state = SST_SWITCH_ON;
 	else
-		cmd.switch_state = SST_SWITCH_OFF;
-
-	cmd.selection = config->ssp_id;
-	cmd.nb_bits_per_slots = config->bits_per_slot;
-	cmd.nb_slots = config->slots;
-	cmd.mode = config->ssp_mode | (config->pcm_mode << 1);
-	cmd.duplex = config->duplex;
-	cmd.active_tx_slot_map = config->active_slot_map;
-	cmd.active_rx_slot_map = config->active_slot_map;
-	cmd.frame_sync_frequency = config->fs_frequency;
-	cmd.frame_sync_polarity = SSP_FS_ACTIVE_HIGH;
-	cmd.data_polarity = 1;
-	cmd.frame_sync_width = config->fs_width;
-	cmd.ssp_protocol = config->ssp_protocol;
-	cmd.start_delay = config->start_delay;
-	cmd.reserved1 = cmd.reserved2 = 0xFF;
+		drv->ssp_cmd.switch_state = SST_SWITCH_OFF;
 
 	return sst_fill_and_send_cmd(drv, SST_IPC_IA_CMD, SST_FLAG_BLOCKED,
-				SST_TASK_SBA, 0, &cmd,
-				sizeof(cmd.header) + cmd.header.length);
+				SST_TASK_SBA, 0, &drv->ssp_cmd,
+				sizeof(drv->ssp_cmd.header) + drv->ssp_cmd.header.length);
 }
 
 static int sst_set_be_modules(struct snd_soc_dapm_widget *w,
diff --git a/sound/soc/intel/atom/sst-atom-controls.h b/sound/soc/intel/atom/sst-atom-controls.h
index daecc58f28af..93de8045d4e1 100644
--- a/sound/soc/intel/atom/sst-atom-controls.h
+++ b/sound/soc/intel/atom/sst-atom-controls.h
@@ -562,6 +562,8 @@ struct sst_ssp_config {
 	u8 active_slot_map;
 	u8 start_delay;
 	u16 fs_width;
+	u8 frame_sync_polarity;
+	u8 data_polarity;
 };
 
 struct sst_ssp_cfg {
@@ -695,7 +697,7 @@ struct sst_gain_mixer_control {
 	u16 module_id;
 	u16 pipe_id;
 	u16 task_id;
-	char pname[44];
+	char pname[SNDRV_CTL_ELEM_ID_NAME_MAXLEN];
 	struct snd_soc_dapm_widget *w;
 };
 
@@ -867,4 +869,9 @@ struct sst_enum {
 	SOC_DAPM_ENUM(SST_MUX_CTL_NAME(xpname, xinstance), \
 			  SST_SSP_MUX_ENUM(xreg, xshift, xtexts))
 
+int sst_fill_ssp_slot(struct snd_soc_dai *dai, unsigned int tx_mask,
+				unsigned int rx_mask, int slots, int slot_width);
+int sst_fill_ssp_config(struct snd_soc_dai *dai, unsigned int fmt);
+void sst_fill_ssp_defaults(struct snd_soc_dai *dai);
+
 #endif
diff --git a/sound/soc/intel/atom/sst-mfld-platform-pcm.c b/sound/soc/intel/atom/sst-mfld-platform-pcm.c
index 2fbaf2c75d17..641ebe61dc08 100644
--- a/sound/soc/intel/atom/sst-mfld-platform-pcm.c
+++ b/sound/soc/intel/atom/sst-mfld-platform-pcm.c
@@ -434,13 +434,51 @@ static int sst_enable_ssp(struct snd_pcm_substream *substream,
 
 	if (!dai->active) {
 		ret = sst_handle_vb_timer(dai, true);
-		if (ret)
-			return ret;
-		ret = send_ssp_cmd(dai, dai->name, 1);
+		sst_fill_ssp_defaults(dai);
 	}
 	return ret;
 }
 
+static int sst_be_hw_params(struct snd_pcm_substream *substream,
+				struct snd_pcm_hw_params *params,
+				struct snd_soc_dai *dai)
+{
+	int ret = 0;
+
+	if (dai->active == 1)
+		ret = send_ssp_cmd(dai, dai->name, 1);
+	return ret;
+}
+
+static int sst_set_format(struct snd_soc_dai *dai, unsigned int fmt)
+{
+	int ret = 0;
+
+	if (!dai->active)
+		return 0;
+
+	ret = sst_fill_ssp_config(dai, fmt);
+	if (ret < 0)
+		dev_err(dai->dev, "sst_set_format failed..\n");
+
+	return ret;
+}
+
+static int sst_platform_set_ssp_slot(struct snd_soc_dai *dai,
+			unsigned int tx_mask, unsigned int rx_mask,
+			int slots, int slot_width) {
+	int ret = 0;
+
+	if (!dai->active)
+		return ret;
+
+	ret = sst_fill_ssp_slot(dai, tx_mask, rx_mask, slots, slot_width);
+	if (ret < 0)
+		dev_err(dai->dev, "sst_fill_ssp_slot failed..%d\n", ret);
+
+	return ret;
+}
+
 static void sst_disable_ssp(struct snd_pcm_substream *substream,
 			struct snd_soc_dai *dai)
 {
@@ -465,6 +503,9 @@ static struct snd_soc_dai_ops sst_compr_dai_ops = {
 
 static struct snd_soc_dai_ops sst_be_dai_ops = {
 	.startup = sst_enable_ssp,
+	.hw_params = sst_be_hw_params,
+	.set_fmt = sst_set_format,
+	.set_tdm_slot = sst_platform_set_ssp_slot,
 	.shutdown = sst_disable_ssp,
 };
 
diff --git a/sound/soc/intel/atom/sst-mfld-platform.h b/sound/soc/intel/atom/sst-mfld-platform.h
index 9094314be2b0..2409b23eeacf 100644
--- a/sound/soc/intel/atom/sst-mfld-platform.h
+++ b/sound/soc/intel/atom/sst-mfld-platform.h
@@ -22,6 +22,7 @@
 #define __SST_PLATFORMDRV_H__
 
 #include "sst-mfld-dsp.h"
+#include "sst-atom-controls.h"
 
 extern struct sst_device *sst;
 
@@ -175,6 +176,7 @@ struct sst_data {
 	struct snd_sst_bytes_v2 *byte_stream;
 	struct mutex lock;
 	struct snd_soc_card *soc_card;
+	struct sst_cmd_sba_hw_set_ssp ssp_cmd;
 };
 int sst_register_dsp(struct sst_device *sst);
 int sst_unregister_dsp(struct sst_device *sst);
diff --git a/sound/soc/intel/atom/sst/sst_acpi.c b/sound/soc/intel/atom/sst/sst_acpi.c
index 05f693083911..bb19b5801466 100644
--- a/sound/soc/intel/atom/sst/sst_acpi.c
+++ b/sound/soc/intel/atom/sst/sst_acpi.c
@@ -354,6 +354,10 @@ static struct sst_machines sst_acpi_chv[] = {
 						&chv_platform_data },
 	{"10EC5645", "cht-bsw", "cht-bsw-rt5645", NULL, "intel/fw_sst_22a8.bin",
 						&chv_platform_data },
+	{"10EC5650", "cht-bsw", "cht-bsw-rt5645", NULL, "intel/fw_sst_22a8.bin",
+						&chv_platform_data },
+	{"193C9890", "cht-bsw", "cht-bsw-max98090", NULL,
+	"intel/fw_sst_22a8.bin", &chv_platform_data },
 	{},
 };
 
diff --git a/sound/soc/intel/baytrail/sst-baytrail-ipc.c b/sound/soc/intel/baytrail/sst-baytrail-ipc.c
index a839dbfa5218..4c01bb43928d 100644
--- a/sound/soc/intel/baytrail/sst-baytrail-ipc.c
+++ b/sound/soc/intel/baytrail/sst-baytrail-ipc.c
@@ -679,6 +679,14 @@ static u64 byt_reply_msg_match(u64 header, u64 *mask)
 	return header;
 }
 
+static bool byt_is_dsp_busy(struct sst_dsp *dsp)
+{
+	u64 ipcx;
+
+	ipcx = sst_dsp_shim_read_unlocked(dsp, SST_IPCX);
+	return (ipcx & (SST_IPCX_BUSY | SST_IPCX_DONE));
+}
+
 int sst_byt_dsp_init(struct device *dev, struct sst_pdata *pdata)
 {
 	struct sst_byt *byt;
@@ -699,6 +707,9 @@ int sst_byt_dsp_init(struct device *dev, struct sst_pdata *pdata)
 	ipc->ops.shim_dbg = byt_shim_dbg;
 	ipc->ops.tx_data_copy = byt_tx_data_copy;
 	ipc->ops.reply_msg_match = byt_reply_msg_match;
+	ipc->ops.is_dsp_busy = byt_is_dsp_busy;
+	ipc->tx_data_max_size = IPC_MAX_MAILBOX_BYTES;
+	ipc->rx_data_max_size = IPC_MAX_MAILBOX_BYTES;
 
 	err = sst_ipc_init(ipc);
 	if (err != 0)
diff --git a/sound/soc/intel/boards/Makefile b/sound/soc/intel/boards/Makefile
index f8237f0044eb..cb94895c9edb 100644
--- a/sound/soc/intel/boards/Makefile
+++ b/sound/soc/intel/boards/Makefile
@@ -5,6 +5,7 @@ snd-soc-sst-broadwell-objs := broadwell.o
 snd-soc-sst-bytcr-rt5640-objs := bytcr_rt5640.o
 snd-soc-sst-cht-bsw-rt5672-objs := cht_bsw_rt5672.o
 snd-soc-sst-cht-bsw-rt5645-objs := cht_bsw_rt5645.o
+snd-soc-sst-cht-bsw-max98090_ti-objs := cht_bsw_max98090_ti.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
@@ -13,3 +14,4 @@ obj-$(CONFIG_SND_SOC_INTEL_BROADWELL_MACH) += snd-soc-sst-broadwell.o
 obj-$(CONFIG_SND_SOC_INTEL_BYTCR_RT5640_MACH) += snd-soc-sst-bytcr-rt5640.o
 obj-$(CONFIG_SND_SOC_INTEL_CHT_BSW_RT5672_MACH) += snd-soc-sst-cht-bsw-rt5672.o
 obj-$(CONFIG_SND_SOC_INTEL_CHT_BSW_RT5645_MACH) += snd-soc-sst-cht-bsw-rt5645.o
+obj-$(CONFIG_SND_SOC_INTEL_CHT_BSW_MAX98090_TI_MACH) += snd-soc-sst-cht-bsw-max98090_ti.o
diff --git a/sound/soc/intel/boards/cht_bsw_max98090_ti.c b/sound/soc/intel/boards/cht_bsw_max98090_ti.c
new file mode 100644
index 000000000000..1be079423d1e
--- /dev/null
+++ b/sound/soc/intel/boards/cht_bsw_max98090_ti.c
@@ -0,0 +1,318 @@
+/*
+ *  cht-bsw-max98090.c - ASoc Machine driver for Intel Cherryview-based
+ *  platforms Cherrytrail and Braswell, with max98090 & TI codec.
+ *
+ *  Copyright (C) 2015 Intel Corp
+ *  Author: Fang, Yang A <yang.a.fang@intel.com>
+ *  This file is modified from cht_bsw_rt5645.c
+ *  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; version 2 of the License.
+ *
+ *  This program is distributed in the hope that it will be useful, but
+ *  WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  General Public License for more details.
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/acpi.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/jack.h>
+#include "../../codecs/max98090.h"
+#include "../atom/sst-atom-controls.h"
+#include "../../codecs/ts3a227e.h"
+
+#define CHT_PLAT_CLK_3_HZ	19200000
+#define CHT_CODEC_DAI	"HiFi"
+
+struct cht_mc_private {
+	struct snd_soc_jack jack;
+	bool ts3a227e_present;
+};
+
+static inline struct snd_soc_dai *cht_get_codec_dai(struct snd_soc_card *card)
+{
+	int i;
+
+	for (i = 0; i < card->num_rtd; i++) {
+		struct snd_soc_pcm_runtime *rtd;
+
+		rtd = card->rtd + i;
+		if (!strncmp(rtd->codec_dai->name, CHT_CODEC_DAI,
+			     strlen(CHT_CODEC_DAI)))
+			return rtd->codec_dai;
+	}
+	return NULL;
+}
+
+static const struct snd_soc_dapm_widget cht_dapm_widgets[] = {
+	SND_SOC_DAPM_HP("Headphone", NULL),
+	SND_SOC_DAPM_MIC("Headset Mic", NULL),
+	SND_SOC_DAPM_MIC("Int Mic", NULL),
+	SND_SOC_DAPM_SPK("Ext Spk", NULL),
+};
+
+static const struct snd_soc_dapm_route cht_audio_map[] = {
+	{"IN34", NULL, "Headset Mic"},
+	{"Headset Mic", NULL, "MICBIAS"},
+	{"DMICL", NULL, "Int Mic"},
+	{"Headphone", NULL, "HPL"},
+	{"Headphone", NULL, "HPR"},
+	{"Ext Spk", NULL, "SPKL"},
+	{"Ext Spk", NULL, "SPKR"},
+	{"AIF1 Playback", NULL, "ssp2 Tx"},
+	{"ssp2 Tx", NULL, "codec_out0"},
+	{"ssp2 Tx", NULL, "codec_out1"},
+	{"codec_in0", NULL, "ssp2 Rx" },
+	{"codec_in1", NULL, "ssp2 Rx" },
+	{"ssp2 Rx", NULL, "AIF1 Capture"},
+};
+
+static const struct snd_kcontrol_new cht_mc_controls[] = {
+	SOC_DAPM_PIN_SWITCH("Headphone"),
+	SOC_DAPM_PIN_SWITCH("Headset Mic"),
+	SOC_DAPM_PIN_SWITCH("Int Mic"),
+	SOC_DAPM_PIN_SWITCH("Ext Spk"),
+};
+
+static int cht_aif1_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, M98090_REG_SYSTEM_CLOCK,
+				     CHT_PLAT_CLK_3_HZ, SND_SOC_CLOCK_IN);
+	if (ret < 0) {
+		dev_err(rtd->dev, "can't set codec sysclk: %d\n", ret);
+		return ret;
+	}
+
+	return 0;
+}
+
+static int cht_codec_init(struct snd_soc_pcm_runtime *runtime)
+{
+	int ret;
+	int jack_type;
+	struct cht_mc_private *ctx = snd_soc_card_get_drvdata(runtime->card);
+	struct snd_soc_jack *jack = &ctx->jack;
+
+	/**
+	* TI supports 4 butons headset detection
+	* KEY_MEDIA
+	* KEY_VOICECOMMAND
+	* KEY_VOLUMEUP
+	* KEY_VOLUMEDOWN
+	*/
+	if (ctx->ts3a227e_present)
+		jack_type = SND_JACK_HEADPHONE | SND_JACK_MICROPHONE |
+					SND_JACK_BTN_0 | SND_JACK_BTN_1 |
+					SND_JACK_BTN_2 | SND_JACK_BTN_3;
+	else
+		jack_type = SND_JACK_HEADPHONE | SND_JACK_MICROPHONE;
+
+	ret = snd_soc_card_jack_new(runtime->card, "Headset Jack",
+					jack_type, jack, NULL, 0);
+
+	if (ret) {
+		dev_err(runtime->dev, "Headset Jack creation failed %d\n", ret);
+		return ret;
+	}
+
+	return ret;
+}
+
+static int cht_codec_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);
+	int ret = 0;
+	unsigned int fmt = 0;
+
+	ret = snd_soc_dai_set_tdm_slot(rtd->cpu_dai, 0x3, 0x3, 2, 16);
+	if (ret < 0) {
+		dev_err(rtd->dev, "can't set cpu_dai slot fmt: %d\n", ret);
+		return ret;
+	}
+
+	fmt = SND_SOC_DAIFMT_LEFT_J | SND_SOC_DAIFMT_NB_NF
+				| SND_SOC_DAIFMT_CBS_CFS;
+
+	ret = snd_soc_dai_set_fmt(rtd->cpu_dai, fmt);
+	if (ret < 0) {
+		dev_err(rtd->dev, "can't set cpu_dai set fmt: %d\n", ret);
+		return ret;
+	}
+
+	/* The DSP will covert the FE rate to 48k, stereo, 24bits */
+	rate->min = rate->max = 48000;
+	channels->min = channels->max = 2;
+
+	/* set SSP2 to 24-bit */
+	params_set_format(params, SNDRV_PCM_FORMAT_S24_LE);
+	return 0;
+}
+
+static unsigned int rates_48000[] = {
+	48000,
+};
+
+static struct snd_pcm_hw_constraint_list constraints_48000 = {
+	.count = ARRAY_SIZE(rates_48000),
+	.list  = rates_48000,
+};
+
+static int cht_aif1_startup(struct snd_pcm_substream *substream)
+{
+	return snd_pcm_hw_constraint_list(substream->runtime, 0,
+			SNDRV_PCM_HW_PARAM_RATE,
+			&constraints_48000);
+}
+
+static int cht_max98090_headset_init(struct snd_soc_component *component)
+{
+	struct snd_soc_card *card = component->card;
+	struct cht_mc_private *ctx = snd_soc_card_get_drvdata(card);
+
+	return ts3a227e_enable_jack_detect(component, &ctx->jack);
+}
+
+static struct snd_soc_ops cht_aif1_ops = {
+	.startup = cht_aif1_startup,
+};
+
+static struct snd_soc_ops cht_be_ssp2_ops = {
+	.hw_params = cht_aif1_hw_params,
+};
+
+static struct snd_soc_aux_dev cht_max98090_headset_dev = {
+	.name = "Headset Chip",
+	.init = cht_max98090_headset_init,
+	.codec_name = "i2c-104C227E:00",
+};
+
+static struct snd_soc_dai_link cht_dailink[] = {
+	[MERR_DPCM_AUDIO] = {
+		.name = "Audio Port",
+		.stream_name = "Audio",
+		.cpu_dai_name = "media-cpu-dai",
+		.codec_dai_name = "snd-soc-dummy-dai",
+		.codec_name = "snd-soc-dummy",
+		.platform_name = "sst-mfld-platform",
+		.nonatomic = true,
+		.dynamic = 1,
+		.dpcm_playback = 1,
+		.dpcm_capture = 1,
+		.ops = &cht_aif1_ops,
+	},
+	[MERR_DPCM_COMPR] = {
+		.name = "Compressed Port",
+		.stream_name = "Compress",
+		.cpu_dai_name = "compress-cpu-dai",
+		.codec_dai_name = "snd-soc-dummy-dai",
+		.codec_name = "snd-soc-dummy",
+		.platform_name = "sst-mfld-platform",
+	},
+	/* back ends */
+	{
+		.name = "SSP2-Codec",
+		.be_id = 1,
+		.cpu_dai_name = "ssp2-port",
+		.platform_name = "sst-mfld-platform",
+		.no_pcm = 1,
+		.codec_dai_name = "HiFi",
+		.codec_name = "i2c-193C9890:00",
+		.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
+					| SND_SOC_DAIFMT_CBS_CFS,
+		.init = cht_codec_init,
+		.be_hw_params_fixup = cht_codec_fixup,
+		.dpcm_playback = 1,
+		.dpcm_capture = 1,
+		.ops = &cht_be_ssp2_ops,
+	},
+};
+
+/* SoC card */
+static struct snd_soc_card snd_soc_card_cht = {
+	.name = "chtmax98090",
+	.dai_link = cht_dailink,
+	.num_links = ARRAY_SIZE(cht_dailink),
+	.aux_dev = &cht_max98090_headset_dev,
+	.num_aux_devs = 1,
+	.dapm_widgets = cht_dapm_widgets,
+	.num_dapm_widgets = ARRAY_SIZE(cht_dapm_widgets),
+	.dapm_routes = cht_audio_map,
+	.num_dapm_routes = ARRAY_SIZE(cht_audio_map),
+	.controls = cht_mc_controls,
+	.num_controls = ARRAY_SIZE(cht_mc_controls),
+};
+
+static acpi_status snd_acpi_codec_match(acpi_handle handle, u32 level,
+						void *context, void **ret)
+{
+	*(bool *)context = true;
+	return AE_OK;
+}
+
+static int snd_cht_mc_probe(struct platform_device *pdev)
+{
+	int ret_val = 0;
+	bool found = false;
+	struct cht_mc_private *drv;
+
+	drv = devm_kzalloc(&pdev->dev, sizeof(*drv), GFP_ATOMIC);
+	if (!drv)
+		return -ENOMEM;
+
+	if (ACPI_SUCCESS(acpi_get_devices(
+					"104C227E",
+					snd_acpi_codec_match,
+					&found, NULL)) && found) {
+		drv->ts3a227e_present = true;
+	} else {
+		/* no need probe TI jack detection chip */
+		snd_soc_card_cht.aux_dev = NULL;
+		snd_soc_card_cht.num_aux_devs = 0;
+		drv->ts3a227e_present = false;
+	}
+
+	/* register the soc card */
+	snd_soc_card_cht.dev = &pdev->dev;
+	snd_soc_card_set_drvdata(&snd_soc_card_cht, drv);
+	ret_val = devm_snd_soc_register_card(&pdev->dev, &snd_soc_card_cht);
+	if (ret_val) {
+		dev_err(&pdev->dev,
+			"snd_soc_register_card failed %d\n", ret_val);
+		return ret_val;
+	}
+	platform_set_drvdata(pdev, &snd_soc_card_cht);
+	return ret_val;
+}
+
+static struct platform_driver snd_cht_mc_driver = {
+	.driver = {
+		.name = "cht-bsw-max98090",
+	},
+	.probe = snd_cht_mc_probe,
+};
+
+module_platform_driver(snd_cht_mc_driver)
+
+MODULE_DESCRIPTION("ASoC Intel(R) Braswell Machine driver");
+MODULE_AUTHOR("Fang, Yang A <yang.a.fang@intel.com>");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:cht-bsw-max98090");
diff --git a/sound/soc/intel/boards/cht_bsw_rt5645.c b/sound/soc/intel/boards/cht_bsw_rt5645.c
index 20a28b22e30f..bdcaf467842a 100644
--- a/sound/soc/intel/boards/cht_bsw_rt5645.c
+++ b/sound/soc/intel/boards/cht_bsw_rt5645.c
@@ -21,6 +21,7 @@
  */
 
 #include <linux/module.h>
+#include <linux/acpi.h>
 #include <linux/platform_device.h>
 #include <linux/slab.h>
 #include <sound/pcm.h>
@@ -33,9 +34,15 @@
 #define CHT_PLAT_CLK_3_HZ	19200000
 #define CHT_CODEC_DAI	"rt5645-aif1"
 
+struct cht_acpi_card {
+	char *codec_id;
+	int codec_type;
+	struct snd_soc_card *soc_card;
+};
+
 struct cht_mc_private {
-	struct snd_soc_jack hp_jack;
-	struct snd_soc_jack mic_jack;
+	struct snd_soc_jack jack;
+	struct cht_acpi_card *acpi_card;
 };
 
 static inline struct snd_soc_dai *cht_get_codec_dai(struct snd_soc_card *card)
@@ -94,7 +101,7 @@ static const struct snd_soc_dapm_widget cht_dapm_widgets[] = {
 			platform_clock_control, SND_SOC_DAPM_POST_PMD),
 };
 
-static const struct snd_soc_dapm_route cht_audio_map[] = {
+static const struct snd_soc_dapm_route cht_rt5645_audio_map[] = {
 	{"IN1P", NULL, "Headset Mic"},
 	{"IN1N", NULL, "Headset Mic"},
 	{"DMIC L1", NULL, "Int Mic"},
@@ -115,6 +122,27 @@ static const struct snd_soc_dapm_route cht_audio_map[] = {
 	{"Ext Spk", NULL, "Platform Clock"},
 };
 
+static const struct snd_soc_dapm_route cht_rt5650_audio_map[] = {
+	{"IN1P", NULL, "Headset Mic"},
+	{"IN1N", NULL, "Headset Mic"},
+	{"DMIC L2", NULL, "Int Mic"},
+	{"DMIC R2", NULL, "Int Mic"},
+	{"Headphone", NULL, "HPOL"},
+	{"Headphone", NULL, "HPOR"},
+	{"Ext Spk", NULL, "SPOL"},
+	{"Ext Spk", NULL, "SPOR"},
+	{"AIF1 Playback", NULL, "ssp2 Tx"},
+	{"ssp2 Tx", NULL, "codec_out0"},
+	{"ssp2 Tx", NULL, "codec_out1"},
+	{"codec_in0", NULL, "ssp2 Rx" },
+	{"codec_in1", NULL, "ssp2 Rx" },
+	{"ssp2 Rx", NULL, "AIF1 Capture"},
+	{"Headphone", NULL, "Platform Clock"},
+	{"Headset Mic", NULL, "Platform Clock"},
+	{"Int Mic", NULL, "Platform Clock"},
+	{"Ext Spk", NULL, "Platform Clock"},
+};
+
 static const struct snd_kcontrol_new cht_mc_controls[] = {
 	SOC_DAPM_PIN_SWITCH("Headphone"),
 	SOC_DAPM_PIN_SWITCH("Headset Mic"),
@@ -150,6 +178,7 @@ static int cht_aif1_hw_params(struct snd_pcm_substream *substream,
 static int cht_codec_init(struct snd_soc_pcm_runtime *runtime)
 {
 	int ret;
+	int jack_type;
 	struct snd_soc_codec *codec = runtime->codec;
 	struct snd_soc_dai *codec_dai = runtime->codec_dai;
 	struct cht_mc_private *ctx = snd_soc_card_get_drvdata(runtime->card);
@@ -169,23 +198,22 @@ static int cht_codec_init(struct snd_soc_pcm_runtime *runtime)
 		return ret;
 	}
 
-	ret = snd_soc_card_jack_new(runtime->card, "Headphone Jack",
-				    SND_JACK_HEADPHONE, &ctx->hp_jack,
-				    NULL, 0);
-	if (ret) {
-		dev_err(runtime->dev, "HP jack creation failed %d\n", ret);
-		return ret;
-	}
+	if (ctx->acpi_card->codec_type == CODEC_TYPE_RT5650)
+		jack_type = SND_JACK_HEADPHONE | SND_JACK_MICROPHONE |
+					SND_JACK_BTN_0 | SND_JACK_BTN_1 |
+					SND_JACK_BTN_2 | SND_JACK_BTN_3;
+	else
+		jack_type = SND_JACK_HEADPHONE | SND_JACK_MICROPHONE;
 
-	ret = snd_soc_card_jack_new(runtime->card, "Mic Jack",
-				    SND_JACK_MICROPHONE, &ctx->mic_jack,
+	ret = snd_soc_card_jack_new(runtime->card, "Headset Jack",
+				    jack_type, &ctx->jack,
 				    NULL, 0);
 	if (ret) {
-		dev_err(runtime->dev, "Mic jack creation failed %d\n", ret);
+		dev_err(runtime->dev, "Headset jack creation failed %d\n", ret);
 		return ret;
 	}
 
-	rt5645_set_jack_detect(codec, &ctx->hp_jack, &ctx->mic_jack);
+	rt5645_set_jack_detect(codec, &ctx->jack, &ctx->jack, &ctx->jack);
 
 	return ret;
 }
@@ -239,7 +267,7 @@ static struct snd_soc_dai_link cht_dailink[] = {
 		.codec_dai_name = "snd-soc-dummy-dai",
 		.codec_name = "snd-soc-dummy",
 		.platform_name = "sst-mfld-platform",
-		.ignore_suspend = 1,
+		.nonatomic = true,
 		.dynamic = 1,
 		.dpcm_playback = 1,
 		.dpcm_capture = 1,
@@ -267,7 +295,7 @@ static struct snd_soc_dai_link cht_dailink[] = {
 					| SND_SOC_DAIFMT_CBS_CFS,
 		.init = cht_codec_init,
 		.be_hw_params_fixup = cht_codec_fixup,
-		.ignore_suspend = 1,
+		.nonatomic = true,
 		.dpcm_playback = 1,
 		.dpcm_capture = 1,
 		.ops = &cht_be_ssp2_ops,
@@ -275,43 +303,85 @@ static struct snd_soc_dai_link cht_dailink[] = {
 };
 
 /* SoC card */
-static struct snd_soc_card snd_soc_card_cht = {
+static struct snd_soc_card snd_soc_card_chtrt5645 = {
 	.name = "chtrt5645",
 	.dai_link = cht_dailink,
 	.num_links = ARRAY_SIZE(cht_dailink),
 	.dapm_widgets = cht_dapm_widgets,
 	.num_dapm_widgets = ARRAY_SIZE(cht_dapm_widgets),
-	.dapm_routes = cht_audio_map,
-	.num_dapm_routes = ARRAY_SIZE(cht_audio_map),
+	.dapm_routes = cht_rt5645_audio_map,
+	.num_dapm_routes = ARRAY_SIZE(cht_rt5645_audio_map),
 	.controls = cht_mc_controls,
 	.num_controls = ARRAY_SIZE(cht_mc_controls),
 };
 
+static struct snd_soc_card snd_soc_card_chtrt5650 = {
+	.name = "chtrt5650",
+	.dai_link = cht_dailink,
+	.num_links = ARRAY_SIZE(cht_dailink),
+	.dapm_widgets = cht_dapm_widgets,
+	.num_dapm_widgets = ARRAY_SIZE(cht_dapm_widgets),
+	.dapm_routes = cht_rt5650_audio_map,
+	.num_dapm_routes = ARRAY_SIZE(cht_rt5650_audio_map),
+	.controls = cht_mc_controls,
+	.num_controls = ARRAY_SIZE(cht_mc_controls),
+};
+
+static struct cht_acpi_card snd_soc_cards[] = {
+	{"10EC5645", CODEC_TYPE_RT5645, &snd_soc_card_chtrt5645},
+	{"10EC5650", CODEC_TYPE_RT5650, &snd_soc_card_chtrt5650},
+};
+
+static acpi_status snd_acpi_codec_match(acpi_handle handle, u32 level,
+				       void *context, void **ret)
+{
+	*(bool *)context = true;
+	return AE_OK;
+}
+
 static int snd_cht_mc_probe(struct platform_device *pdev)
 {
 	int ret_val = 0;
+	int i;
 	struct cht_mc_private *drv;
+	struct snd_soc_card *card = snd_soc_cards[0].soc_card;
+	bool found = false;
+	char codec_name[16];
 
 	drv = devm_kzalloc(&pdev->dev, sizeof(*drv), GFP_ATOMIC);
 	if (!drv)
 		return -ENOMEM;
 
-	snd_soc_card_cht.dev = &pdev->dev;
-	snd_soc_card_set_drvdata(&snd_soc_card_cht, drv);
-	ret_val = devm_snd_soc_register_card(&pdev->dev, &snd_soc_card_cht);
+	for (i = 0; i < ARRAY_SIZE(snd_soc_cards); i++) {
+		if (ACPI_SUCCESS(acpi_get_devices(
+						snd_soc_cards[i].codec_id,
+						snd_acpi_codec_match,
+						&found, NULL)) && found) {
+			dev_dbg(&pdev->dev,
+				"found codec %s\n", snd_soc_cards[i].codec_id);
+			card = snd_soc_cards[i].soc_card;
+			drv->acpi_card = &snd_soc_cards[i];
+			break;
+		}
+	}
+	card->dev = &pdev->dev;
+	sprintf(codec_name, "i2c-%s:00", drv->acpi_card->codec_id);
+	/* set correct codec name */
+	strcpy((char *)card->dai_link[2].codec_name, codec_name);
+	snd_soc_card_set_drvdata(card, drv);
+	ret_val = devm_snd_soc_register_card(&pdev->dev, card);
 	if (ret_val) {
 		dev_err(&pdev->dev,
 			"snd_soc_register_card failed %d\n", ret_val);
 		return ret_val;
 	}
-	platform_set_drvdata(pdev, &snd_soc_card_cht);
+	platform_set_drvdata(pdev, card);
 	return ret_val;
 }
 
 static struct platform_driver snd_cht_mc_driver = {
 	.driver = {
 		.name = "cht-bsw-rt5645",
-		.pm = &snd_soc_pm_ops,
 	},
 	.probe = snd_cht_mc_probe,
 };
diff --git a/sound/soc/intel/common/sst-ipc.c b/sound/soc/intel/common/sst-ipc.c
index 4b62a553823c..a12c7bb08d3b 100644
--- a/sound/soc/intel/common/sst-ipc.c
+++ b/sound/soc/intel/common/sst-ipc.c
@@ -129,11 +129,31 @@ static int msg_empty_list_init(struct sst_generic_ipc *ipc)
 		return -ENOMEM;
 
 	for (i = 0; i < IPC_EMPTY_LIST_SIZE; i++) {
+		ipc->msg[i].tx_data = kzalloc(ipc->tx_data_max_size, GFP_KERNEL);
+		if (ipc->msg[i].tx_data == NULL)
+			goto free_mem;
+
+		ipc->msg[i].rx_data = kzalloc(ipc->rx_data_max_size, GFP_KERNEL);
+		if (ipc->msg[i].rx_data == NULL) {
+			kfree(ipc->msg[i].tx_data);
+			goto free_mem;
+		}
+
 		init_waitqueue_head(&ipc->msg[i].waitq);
 		list_add(&ipc->msg[i].list, &ipc->empty_list);
 	}
 
 	return 0;
+
+free_mem:
+	while (i > 0) {
+		kfree(ipc->msg[i-1].tx_data);
+		kfree(ipc->msg[i-1].rx_data);
+		--i;
+	}
+	kfree(ipc->msg);
+
+	return -ENOMEM;
 }
 
 static void ipc_tx_msgs(struct kthread_work *work)
@@ -142,7 +162,6 @@ static void ipc_tx_msgs(struct kthread_work *work)
 		container_of(work, struct sst_generic_ipc, kwork);
 	struct ipc_message *msg;
 	unsigned long flags;
-	u64 ipcx;
 
 	spin_lock_irqsave(&ipc->dsp->spinlock, flags);
 
@@ -153,8 +172,8 @@ static void ipc_tx_msgs(struct kthread_work *work)
 
 	/* if the DSP is busy, we will TX messages after IRQ.
 	 * also postpone if we are in the middle of procesing completion irq*/
-	ipcx = sst_dsp_shim_read_unlocked(ipc->dsp, SST_IPCX);
-	if (ipcx & (SST_IPCX_BUSY | SST_IPCX_DONE)) {
+	if (ipc->ops.is_dsp_busy && ipc->ops.is_dsp_busy(ipc->dsp)) {
+		dev_dbg(ipc->dev, "ipc_tx_msgs dsp busy\n");
 		spin_unlock_irqrestore(&ipc->dsp->spinlock, flags);
 		return;
 	}
@@ -280,11 +299,18 @@ EXPORT_SYMBOL_GPL(sst_ipc_init);
 
 void sst_ipc_fini(struct sst_generic_ipc *ipc)
 {
+	int i;
+
 	if (ipc->tx_thread)
 		kthread_stop(ipc->tx_thread);
 
-	if (ipc->msg)
+	if (ipc->msg) {
+		for (i = 0; i < IPC_EMPTY_LIST_SIZE; i++) {
+			kfree(ipc->msg[i].tx_data);
+			kfree(ipc->msg[i].rx_data);
+		}
 		kfree(ipc->msg);
+	}
 }
 EXPORT_SYMBOL_GPL(sst_ipc_fini);
 
diff --git a/sound/soc/intel/common/sst-ipc.h b/sound/soc/intel/common/sst-ipc.h
index 125ea451a373..ceb7e468a3fa 100644
--- a/sound/soc/intel/common/sst-ipc.h
+++ b/sound/soc/intel/common/sst-ipc.h
@@ -32,9 +32,9 @@ struct ipc_message {
 	u64 header;
 
 	/* direction wrt host CPU */
-	char tx_data[IPC_MAX_MAILBOX_BYTES];
+	char *tx_data;
 	size_t tx_size;
-	char rx_data[IPC_MAX_MAILBOX_BYTES];
+	char *rx_data;
 	size_t rx_size;
 
 	wait_queue_head_t waitq;
@@ -51,6 +51,7 @@ struct sst_plat_ipc_ops {
 	void (*shim_dbg)(struct sst_generic_ipc *, const char *);
 	void (*tx_data_copy)(struct ipc_message *, char *, size_t);
 	u64  (*reply_msg_match)(u64 header, u64 *mask);
+	bool (*is_dsp_busy)(struct sst_dsp *dsp);
 };
 
 /* SST generic IPC data */
@@ -68,6 +69,8 @@ struct sst_generic_ipc {
 	struct kthread_work kwork;
 	bool pending;
 	struct ipc_message *msg;
+	int tx_data_max_size;
+	int rx_data_max_size;
 
 	struct sst_plat_ipc_ops ops;
 };
diff --git a/sound/soc/intel/haswell/sst-haswell-ipc.c b/sound/soc/intel/haswell/sst-haswell-ipc.c
index 324eceb07b25..f95f271aab0c 100644
--- a/sound/soc/intel/haswell/sst-haswell-ipc.c
+++ b/sound/soc/intel/haswell/sst-haswell-ipc.c
@@ -2098,6 +2098,14 @@ static u64 hsw_reply_msg_match(u64 header, u64 *mask)
 	return header;
 }
 
+static bool hsw_is_dsp_busy(struct sst_dsp *dsp)
+{
+	u64 ipcx;
+
+	ipcx = sst_dsp_shim_read_unlocked(dsp, SST_IPCX);
+	return (ipcx & (SST_IPCX_BUSY | SST_IPCX_DONE));
+}
+
 int sst_hsw_dsp_init(struct device *dev, struct sst_pdata *pdata)
 {
 	struct sst_hsw_ipc_fw_version version;
@@ -2117,6 +2125,10 @@ int sst_hsw_dsp_init(struct device *dev, struct sst_pdata *pdata)
 	ipc->ops.shim_dbg = hsw_shim_dbg;
 	ipc->ops.tx_data_copy = hsw_tx_data_copy;
 	ipc->ops.reply_msg_match = hsw_reply_msg_match;
+	ipc->ops.is_dsp_busy = hsw_is_dsp_busy;
+
+	ipc->tx_data_max_size = IPC_MAX_MAILBOX_BYTES;
+	ipc->rx_data_max_size = IPC_MAX_MAILBOX_BYTES;
 
 	ret = sst_ipc_init(ipc);
 	if (ret != 0)
diff --git a/sound/soc/intel/haswell/sst-haswell-pcm.c b/sound/soc/intel/haswell/sst-haswell-pcm.c
index 23ae0400d6db..e593e7a4b7a7 100644
--- a/sound/soc/intel/haswell/sst-haswell-pcm.c
+++ b/sound/soc/intel/haswell/sst-haswell-pcm.c
@@ -928,10 +928,15 @@ static void hsw_pcm_free_modules(struct hsw_priv_data *pdata)
 
 	for (i = 0; i < ARRAY_SIZE(mod_map); i++) {
 		pcm_data = &pdata->pcm[mod_map[i].dai_id][mod_map[i].stream];
-		sst_hsw_runtime_module_free(pcm_data->runtime);
+		if (pcm_data->runtime){
+			sst_hsw_runtime_module_free(pcm_data->runtime);
+			pcm_data->runtime = NULL;
+		}
 	}
-	if (sst_hsw_is_module_loaded(hsw, SST_HSW_MODULE_WAVES)) {
+	if (sst_hsw_is_module_loaded(hsw, SST_HSW_MODULE_WAVES) &&
+				pdata->runtime_waves) {
 		sst_hsw_runtime_module_free(pdata->runtime_waves);
+		pdata->runtime_waves = NULL;
 	}
 }
 
@@ -1204,6 +1209,20 @@ static int hsw_pcm_runtime_idle(struct device *dev)
 	return 0;
 }
 
+static int hsw_pcm_suspend(struct device *dev)
+{
+	struct hsw_priv_data *pdata = dev_get_drvdata(dev);
+	struct sst_hsw *hsw = pdata->hsw;
+
+	/* enter D3 state and stall */
+	sst_hsw_dsp_runtime_suspend(hsw);
+	/* free all runtime modules */
+	hsw_pcm_free_modules(pdata);
+	/* put the DSP to sleep, fw unloaded after runtime modules freed */
+	sst_hsw_dsp_runtime_sleep(hsw);
+	return 0;
+}
+
 static int hsw_pcm_runtime_suspend(struct device *dev)
 {
 	struct hsw_priv_data *pdata = dev_get_drvdata(dev);
@@ -1220,8 +1239,7 @@ static int hsw_pcm_runtime_suspend(struct device *dev)
 			return ret;
 		sst_hsw_set_module_enabled_rtd3(hsw, SST_HSW_MODULE_WAVES);
 	}
-	sst_hsw_dsp_runtime_suspend(hsw);
-	sst_hsw_dsp_runtime_sleep(hsw);
+	hsw_pcm_suspend(dev);
 	pdata->pm_state = HSW_PM_STATE_RTD3;
 
 	return 0;
@@ -1361,10 +1379,7 @@ static int hsw_pcm_prepare(struct device *dev)
 			if (err < 0)
 				dev_err(dev, "failed to save context for PCM %d\n", i);
 		}
-		/* enter D3 state and stall */
-		sst_hsw_dsp_runtime_suspend(hsw);
-		/* put the DSP to sleep */
-		sst_hsw_dsp_runtime_sleep(hsw);
+		hsw_pcm_suspend(dev);
 	}
 
 	snd_soc_suspend(pdata->soc_card->dev);
diff --git a/sound/soc/omap/Kconfig b/sound/soc/omap/Kconfig
index 6768e4f7d7d0..30d0109703a9 100644
--- a/sound/soc/omap/Kconfig
+++ b/sound/soc/omap/Kconfig
@@ -100,12 +100,13 @@ config SND_OMAP_SOC_OMAP_TWL4030
 
 config SND_OMAP_SOC_OMAP_ABE_TWL6040
 	tristate "SoC Audio support for OMAP boards using ABE and twl6040 codec"
-	depends on TWL6040_CORE && SND_OMAP_SOC && (ARCH_OMAP4 || SOC_OMAP5 || COMPILE_TEST)
+	depends on TWL6040_CORE && SND_OMAP_SOC
+	depends on ARCH_OMAP4 || (SOC_OMAP5 && MFD_PALMAS) || COMPILE_TEST
 	select SND_OMAP_SOC_DMIC
 	select SND_OMAP_SOC_MCPDM
 	select SND_SOC_TWL6040
 	select SND_SOC_DMIC
-	select COMMON_CLK_PALMAS if MFD_PALMAS
+	select COMMON_CLK_PALMAS if (SOC_OMAP5 && MFD_PALMAS)
 	help
 	  Say Y if you want to add support for SoC audio on OMAP boards using
 	  ABE and twl6040 codec. This driver currently supports:
diff --git a/sound/soc/omap/omap-twl4030.c b/sound/soc/omap/omap-twl4030.c
index 3673ada43bfb..743131473056 100644
--- a/sound/soc/omap/omap-twl4030.c
+++ b/sound/soc/omap/omap-twl4030.c
@@ -159,9 +159,8 @@ static inline void twl4030_disconnect_pin(struct snd_soc_dapm_context *dapm,
 
 static int omap_twl4030_init(struct snd_soc_pcm_runtime *rtd)
 {
-	struct snd_soc_codec *codec = rtd->codec;
 	struct snd_soc_card *card = rtd->card;
-	struct snd_soc_dapm_context *dapm = &codec->dapm;
+	struct snd_soc_dapm_context *dapm = &card->dapm;
 	struct omap_tw4030_pdata *pdata = dev_get_platdata(card->dev);
 	struct omap_twl4030 *priv = snd_soc_card_get_drvdata(card);
 	int ret = 0;
diff --git a/sound/soc/omap/rx51.c b/sound/soc/omap/rx51.c
index c2ddf0fbfa28..fded99362d39 100644
--- a/sound/soc/omap/rx51.c
+++ b/sound/soc/omap/rx51.c
@@ -455,50 +455,36 @@ static int rx51_soc_probe(struct platform_device *pdev)
 	snd_soc_card_set_drvdata(card, pdata);
 
 	pdata->tvout_selection_gpio = devm_gpiod_get(card->dev,
-						     "tvout-selection");
+						     "tvout-selection",
+						     GPIOD_OUT_LOW);
 	if (IS_ERR(pdata->tvout_selection_gpio)) {
 		dev_err(card->dev, "could not get tvout selection gpio\n");
 		return PTR_ERR(pdata->tvout_selection_gpio);
 	}
 
-	err = gpiod_direction_output(pdata->tvout_selection_gpio, 0);
-	if (err) {
-		dev_err(card->dev, "could not setup tvout selection gpio\n");
-		return err;
-	}
-
 	pdata->jack_detection_gpio = devm_gpiod_get(card->dev,
-						    "jack-detection");
+						    "jack-detection",
+						    GPIOD_ASIS);
 	if (IS_ERR(pdata->jack_detection_gpio)) {
 		dev_err(card->dev, "could not get jack detection gpio\n");
 		return PTR_ERR(pdata->jack_detection_gpio);
 	}
 
-	pdata->eci_sw_gpio = devm_gpiod_get(card->dev, "eci-switch");
+	pdata->eci_sw_gpio = devm_gpiod_get(card->dev, "eci-switch",
+					    GPIOD_OUT_HIGH);
 	if (IS_ERR(pdata->eci_sw_gpio)) {
 		dev_err(card->dev, "could not get eci switch gpio\n");
 		return PTR_ERR(pdata->eci_sw_gpio);
 	}
 
-	err = gpiod_direction_output(pdata->eci_sw_gpio, 1);
-	if (err) {
-		dev_err(card->dev, "could not setup eci switch gpio\n");
-		return err;
-	}
-
 	pdata->speaker_amp_gpio = devm_gpiod_get(card->dev,
-						 "speaker-amplifier");
+						 "speaker-amplifier",
+						 GPIOD_OUT_LOW);
 	if (IS_ERR(pdata->speaker_amp_gpio)) {
 		dev_err(card->dev, "could not get speaker enable gpio\n");
 		return PTR_ERR(pdata->speaker_amp_gpio);
 	}
 
-	err = gpiod_direction_output(pdata->speaker_amp_gpio, 0);
-	if (err) {
-		dev_err(card->dev, "could not setup speaker enable gpio\n");
-		return err;
-	}
-
 	err = devm_snd_soc_register_card(card->dev, card);
 	if (err) {
 		dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n", err);
diff --git a/sound/soc/pxa/brownstone.c b/sound/soc/pxa/brownstone.c
index 79936e3e80e7..2b26318bc200 100644
--- a/sound/soc/pxa/brownstone.c
+++ b/sound/soc/pxa/brownstone.c
@@ -45,29 +45,6 @@ static const struct snd_soc_dapm_route brownstone_audio_map[] = {
 	{"MICBIAS1", NULL, "Main Mic"},
 };
 
-static int brownstone_wm8994_init(struct snd_soc_pcm_runtime *rtd)
-{
-	struct snd_soc_codec *codec = rtd->codec;
-	struct snd_soc_dapm_context *dapm = &codec->dapm;
-
-	/* set endpoints to not connected */
-	snd_soc_dapm_nc_pin(dapm, "HPOUT2P");
-	snd_soc_dapm_nc_pin(dapm, "HPOUT2N");
-	snd_soc_dapm_nc_pin(dapm, "LINEOUT1N");
-	snd_soc_dapm_nc_pin(dapm, "LINEOUT1P");
-	snd_soc_dapm_nc_pin(dapm, "LINEOUT2N");
-	snd_soc_dapm_nc_pin(dapm, "LINEOUT2P");
-	snd_soc_dapm_nc_pin(dapm, "IN1LN");
-	snd_soc_dapm_nc_pin(dapm, "IN1LP");
-	snd_soc_dapm_nc_pin(dapm, "IN1RP");
-	snd_soc_dapm_nc_pin(dapm, "IN2LP:VXRN");
-	snd_soc_dapm_nc_pin(dapm, "IN2RN");
-	snd_soc_dapm_nc_pin(dapm, "IN2RP:VXRP");
-	snd_soc_dapm_nc_pin(dapm, "IN2LN");
-
-	return 0;
-}
-
 static int brownstone_wm8994_hw_params(struct snd_pcm_substream *substream,
 				       struct snd_pcm_hw_params *params)
 {
@@ -115,7 +92,6 @@ static struct snd_soc_dai_link brownstone_wm8994_dai[] = {
 	.dai_fmt	= SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
 				SND_SOC_DAIFMT_CBS_CFS,
 	.ops		= &brownstone_ops,
-	.init		= brownstone_wm8994_init,
 },
 };
 
@@ -132,6 +108,7 @@ static struct snd_soc_card brownstone = {
 	.num_dapm_widgets = ARRAY_SIZE(brownstone_dapm_widgets),
 	.dapm_routes = brownstone_audio_map,
 	.num_dapm_routes = ARRAY_SIZE(brownstone_audio_map),
+	.fully_routed = true,
 };
 
 static int brownstone_probe(struct platform_device *pdev)
diff --git a/sound/soc/pxa/poodle.c b/sound/soc/pxa/poodle.c
index 0fce8c420e96..80b457ac522a 100644
--- a/sound/soc/pxa/poodle.c
+++ b/sound/soc/pxa/poodle.c
@@ -192,6 +192,7 @@ static int poodle_amp_event(struct snd_soc_dapm_widget *w,
 static const struct snd_soc_dapm_widget wm8731_dapm_widgets[] = {
 SND_SOC_DAPM_HP("Headphone Jack", NULL),
 SND_SOC_DAPM_SPK("Ext Spk", poodle_amp_event),
+SND_SOC_DAPM_MIC("Microphone", NULL),
 };
 
 /* Corgi machine connections to the codec pins */
@@ -204,6 +205,8 @@ static const struct snd_soc_dapm_route poodle_audio_map[] = {
 	/* speaker connected to LOUT, ROUT */
 	{"Ext Spk", NULL, "ROUT"},
 	{"Ext Spk", NULL, "LOUT"},
+
+	{"MICIN", NULL, "Microphone"},
 };
 
 static const char *jack_function[] = {"Off", "Headphone"};
@@ -220,20 +223,6 @@ static const struct snd_kcontrol_new wm8731_poodle_controls[] = {
 		poodle_set_spk),
 };
 
-/*
- * Logic for a wm8731 as connected on a Sharp SL-C7x0 Device
- */
-static int poodle_wm8731_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_nc_pin(dapm, "LLINEIN");
-	snd_soc_dapm_nc_pin(dapm, "RLINEIN");
-
-	return 0;
-}
-
 /* poodle digital audio interface glue - connects codec <--> CPU */
 static struct snd_soc_dai_link poodle_dai = {
 	.name = "WM8731",
@@ -242,7 +231,6 @@ static struct snd_soc_dai_link poodle_dai = {
 	.codec_dai_name = "wm8731-hifi",
 	.platform_name = "pxa-pcm-audio",
 	.codec_name = "wm8731.0-001b",
-	.init = poodle_wm8731_init,
 	.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
 		   SND_SOC_DAIFMT_CBS_CFS,
 	.ops = &poodle_ops,
@@ -261,6 +249,7 @@ static struct snd_soc_card poodle = {
 	.num_dapm_widgets = ARRAY_SIZE(wm8731_dapm_widgets),
 	.dapm_routes = poodle_audio_map,
 	.num_dapm_routes = ARRAY_SIZE(poodle_audio_map),
+	.fully_routed = true,
 };
 
 static int poodle_probe(struct platform_device *pdev)
diff --git a/sound/soc/pxa/tosa.c b/sound/soc/pxa/tosa.c
index cb49284e853a..f59f566551ef 100644
--- a/sound/soc/pxa/tosa.c
+++ b/sound/soc/pxa/tosa.c
@@ -185,17 +185,6 @@ static const struct snd_kcontrol_new tosa_controls[] = {
 		tosa_set_spk),
 };
 
-static int tosa_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_nc_pin(dapm, "OUT3");
-	snd_soc_dapm_nc_pin(dapm, "MONOOUT");
-
-	return 0;
-}
-
 static struct snd_soc_dai_link tosa_dai[] = {
 {
 	.name = "AC97",
@@ -204,7 +193,6 @@ static struct snd_soc_dai_link tosa_dai[] = {
 	.codec_dai_name = "wm9712-hifi",
 	.platform_name = "pxa-pcm-audio",
 	.codec_name = "wm9712-codec",
-	.init = tosa_ac97_init,
 	.ops = &tosa_ops,
 },
 {
@@ -230,6 +218,7 @@ static struct snd_soc_card tosa = {
 	.num_dapm_widgets = ARRAY_SIZE(tosa_dapm_widgets),
 	.dapm_routes = audio_map,
 	.num_dapm_routes = ARRAY_SIZE(audio_map),
+	.fully_routed = true,
 };
 
 static int tosa_probe(struct platform_device *pdev)
diff --git a/sound/soc/pxa/z2.c b/sound/soc/pxa/z2.c
index bcbfbe8303f7..990b1aa6d7f6 100644
--- a/sound/soc/pxa/z2.c
+++ b/sound/soc/pxa/z2.c
@@ -132,16 +132,8 @@ static const struct snd_soc_dapm_route z2_audio_map[] = {
  */
 static int z2_wm8750_init(struct snd_soc_pcm_runtime *rtd)
 {
-	struct snd_soc_codec *codec = rtd->codec;
-	struct snd_soc_dapm_context *dapm = &codec->dapm;
 	int ret;
 
-	/* NC codec pins */
-	snd_soc_dapm_disable_pin(dapm, "LINPUT3");
-	snd_soc_dapm_disable_pin(dapm, "RINPUT3");
-	snd_soc_dapm_disable_pin(dapm, "OUT3");
-	snd_soc_dapm_disable_pin(dapm, "MONO1");
-
 	/* Jack detection API stuff */
 	ret = snd_soc_card_jack_new(rtd->card, "Headset Jack", SND_JACK_HEADSET,
 				    &hs_jack, hs_jack_pins,
@@ -189,6 +181,7 @@ static struct snd_soc_card snd_soc_z2 = {
 	.num_dapm_widgets = ARRAY_SIZE(wm8750_dapm_widgets),
 	.dapm_routes = z2_audio_map,
 	.num_dapm_routes = ARRAY_SIZE(z2_audio_map),
+	.fully_routed = true,
 };
 
 static struct platform_device *z2_snd_device;
diff --git a/sound/soc/qcom/Kconfig b/sound/soc/qcom/Kconfig
index 5f58e4f1bca9..938144c59e2b 100644
--- a/sound/soc/qcom/Kconfig
+++ b/sound/soc/qcom/Kconfig
@@ -6,19 +6,28 @@ config SND_SOC_QCOM
 
 config SND_SOC_LPASS_CPU
 	tristate
-	depends on SND_SOC_QCOM
 	select REGMAP_MMIO
 
 config SND_SOC_LPASS_PLATFORM
 	tristate
-	depends on SND_SOC_QCOM
 	select REGMAP_MMIO
 
-config SND_SOC_STORM
-	tristate "ASoC I2S support for Storm boards"
-	depends on (ARCH_QCOM && SND_SOC_QCOM) || COMPILE_TEST
+config SND_SOC_LPASS_IPQ806X
+	tristate
+	depends on SND_SOC_QCOM
+	select SND_SOC_LPASS_CPU
+	select SND_SOC_LPASS_PLATFORM
+
+config SND_SOC_LPASS_APQ8016
+	tristate
+	depends on SND_SOC_QCOM
 	select SND_SOC_LPASS_CPU
 	select SND_SOC_LPASS_PLATFORM
+
+config SND_SOC_STORM
+	tristate "ASoC I2S support for Storm boards"
+	depends on SND_SOC_QCOM && (ARCH_QCOM || COMPILE_TEST)
+	select SND_SOC_LPASS_IPQ806X
 	select SND_SOC_MAX98357A
 	help
           Say Y or M if you want add support for SoC audio on the
diff --git a/sound/soc/qcom/Makefile b/sound/soc/qcom/Makefile
index c5ce96c761c4..ac7630833fe5 100644
--- a/sound/soc/qcom/Makefile
+++ b/sound/soc/qcom/Makefile
@@ -1,9 +1,13 @@
 # Platform
 snd-soc-lpass-cpu-objs := lpass-cpu.o
 snd-soc-lpass-platform-objs := lpass-platform.o
+snd-soc-lpass-ipq806x-objs := lpass-ipq806x.o
+snd-soc-lpass-apq8016-objs := lpass-apq8016.o
 
 obj-$(CONFIG_SND_SOC_LPASS_CPU) += snd-soc-lpass-cpu.o
 obj-$(CONFIG_SND_SOC_LPASS_PLATFORM) += snd-soc-lpass-platform.o
+obj-$(CONFIG_SND_SOC_LPASS_IPQ806X) += snd-soc-lpass-ipq806x.o
+obj-$(CONFIG_SND_SOC_LPASS_APQ8016) += snd-soc-lpass-apq8016.o
 
 # Machine
 snd-soc-storm-objs := storm.o
diff --git a/sound/soc/qcom/lpass-apq8016.c b/sound/soc/qcom/lpass-apq8016.c
new file mode 100644
index 000000000000..94efc01020c4
--- /dev/null
+++ b/sound/soc/qcom/lpass-apq8016.c
@@ -0,0 +1,242 @@
+/*
+ * Copyright (c) 2010-2011,2013-2015 The Linux Foundation. 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 and
+ * only 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.
+ *
+ * lpass-apq8016.c -- ALSA SoC CPU DAI driver for APQ8016 LPASS
+ *
+ */
+
+
+#include <linux/clk.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dai.h>
+
+#include <dt-bindings/sound/apq8016-lpass.h>
+#include "lpass-lpaif-reg.h"
+#include "lpass.h"
+
+static struct snd_soc_dai_driver apq8016_lpass_cpu_dai_driver[] = {
+	[MI2S_PRIMARY] =  {
+		.id = MI2S_PRIMARY,
+		.name = "Primary MI2S",
+		.playback = {
+			.stream_name	= "Primary Playback",
+			.formats	= SNDRV_PCM_FMTBIT_S16 |
+						SNDRV_PCM_FMTBIT_S24 |
+						SNDRV_PCM_FMTBIT_S32,
+			.rates		= SNDRV_PCM_RATE_8000 |
+						SNDRV_PCM_RATE_16000 |
+						SNDRV_PCM_RATE_32000 |
+						SNDRV_PCM_RATE_48000 |
+						SNDRV_PCM_RATE_96000,
+			.rate_min	= 8000,
+			.rate_max	= 96000,
+			.channels_min	= 1,
+			.channels_max	= 8,
+		},
+		.probe	= &asoc_qcom_lpass_cpu_dai_probe,
+		.ops    = &asoc_qcom_lpass_cpu_dai_ops,
+	},
+	[MI2S_SECONDARY] =  {
+		.id = MI2S_SECONDARY,
+		.name = "Secondary MI2S",
+		.playback = {
+			.stream_name	= "Secondary Playback",
+			.formats	= SNDRV_PCM_FMTBIT_S16 |
+						SNDRV_PCM_FMTBIT_S24 |
+						SNDRV_PCM_FMTBIT_S32,
+			.rates		= SNDRV_PCM_RATE_8000 |
+						SNDRV_PCM_RATE_16000 |
+						SNDRV_PCM_RATE_32000 |
+						SNDRV_PCM_RATE_48000 |
+						SNDRV_PCM_RATE_96000,
+			.rate_min	= 8000,
+			.rate_max	= 96000,
+			.channels_min	= 1,
+			.channels_max	= 8,
+		},
+		.probe	= &asoc_qcom_lpass_cpu_dai_probe,
+		.ops    = &asoc_qcom_lpass_cpu_dai_ops,
+	},
+	[MI2S_TERTIARY] =  {
+		.id = MI2S_TERTIARY,
+		.name = "Tertiary MI2S",
+		.capture = {
+			.stream_name	= "Tertiary Capture",
+			.formats	= SNDRV_PCM_FMTBIT_S16 |
+						SNDRV_PCM_FMTBIT_S24 |
+						SNDRV_PCM_FMTBIT_S32,
+			.rates		= SNDRV_PCM_RATE_8000 |
+						SNDRV_PCM_RATE_16000 |
+						SNDRV_PCM_RATE_32000 |
+						SNDRV_PCM_RATE_48000 |
+						SNDRV_PCM_RATE_96000,
+			.rate_min	= 8000,
+			.rate_max	= 96000,
+			.channels_min	= 1,
+			.channels_max	= 8,
+		},
+		.probe	= &asoc_qcom_lpass_cpu_dai_probe,
+		.ops    = &asoc_qcom_lpass_cpu_dai_ops,
+	},
+	[MI2S_QUATERNARY] =  {
+		.id = MI2S_QUATERNARY,
+		.name = "Quatenary MI2S",
+		.playback = {
+			.stream_name	= "Quatenary Playback",
+			.formats	= SNDRV_PCM_FMTBIT_S16 |
+						SNDRV_PCM_FMTBIT_S24 |
+						SNDRV_PCM_FMTBIT_S32,
+			.rates		= SNDRV_PCM_RATE_8000 |
+						SNDRV_PCM_RATE_16000 |
+						SNDRV_PCM_RATE_32000 |
+						SNDRV_PCM_RATE_48000 |
+						SNDRV_PCM_RATE_96000,
+			.rate_min	= 8000,
+			.rate_max	= 96000,
+			.channels_min	= 1,
+			.channels_max	= 8,
+		},
+		.capture = {
+			.stream_name	= "Quatenary Capture",
+			.formats	= SNDRV_PCM_FMTBIT_S16 |
+						SNDRV_PCM_FMTBIT_S24 |
+						SNDRV_PCM_FMTBIT_S32,
+			.rates		= SNDRV_PCM_RATE_8000 |
+						SNDRV_PCM_RATE_16000 |
+						SNDRV_PCM_RATE_32000 |
+						SNDRV_PCM_RATE_48000 |
+						SNDRV_PCM_RATE_96000,
+			.rate_min	= 8000,
+			.rate_max	= 96000,
+			.channels_min	= 1,
+			.channels_max	= 8,
+		},
+		.probe	= &asoc_qcom_lpass_cpu_dai_probe,
+		.ops    = &asoc_qcom_lpass_cpu_dai_ops,
+	},
+};
+
+static int apq8016_lpass_alloc_dma_channel(struct lpass_data *drvdata)
+{
+	struct lpass_variant *v = drvdata->variant;
+	int chan = find_first_zero_bit(&drvdata->rdma_ch_bit_map,
+					v->rdma_channels);
+
+	if (chan >= v->rdma_channels)
+		return -EBUSY;
+
+	set_bit(chan, &drvdata->rdma_ch_bit_map);
+
+	return chan;
+}
+
+static int apq8016_lpass_free_dma_channel(struct lpass_data *drvdata, int chan)
+{
+	clear_bit(chan, &drvdata->rdma_ch_bit_map);
+
+	return 0;
+}
+
+static int apq8016_lpass_init(struct platform_device *pdev)
+{
+	struct lpass_data *drvdata = platform_get_drvdata(pdev);
+	struct device *dev = &pdev->dev;
+	int ret;
+
+	drvdata->pcnoc_mport_clk = devm_clk_get(dev, "pcnoc-mport-clk");
+	if (IS_ERR(drvdata->pcnoc_mport_clk)) {
+		dev_err(&pdev->dev, "%s() error getting pcnoc-mport-clk: %ld\n",
+				__func__, PTR_ERR(drvdata->pcnoc_mport_clk));
+		return PTR_ERR(drvdata->pcnoc_mport_clk);
+	}
+
+	ret = clk_prepare_enable(drvdata->pcnoc_mport_clk);
+	if (ret) {
+		dev_err(&pdev->dev, "%s() Error enabling pcnoc-mport-clk: %d\n",
+				__func__, ret);
+		return ret;
+	}
+
+	drvdata->pcnoc_sway_clk = devm_clk_get(dev, "pcnoc-sway-clk");
+	if (IS_ERR(drvdata->pcnoc_sway_clk)) {
+		dev_err(&pdev->dev, "%s() error getting pcnoc-sway-clk: %ld\n",
+				__func__, PTR_ERR(drvdata->pcnoc_sway_clk));
+		return PTR_ERR(drvdata->pcnoc_sway_clk);
+	}
+
+	ret = clk_prepare_enable(drvdata->pcnoc_sway_clk);
+	if (ret) {
+		dev_err(&pdev->dev, "%s() Error enabling pcnoc_sway_clk: %d\n",
+				__func__, ret);
+		return ret;
+	}
+
+	return 0;
+}
+
+static int apq8016_lpass_exit(struct platform_device *pdev)
+{
+	struct lpass_data *drvdata = platform_get_drvdata(pdev);
+
+	clk_disable_unprepare(drvdata->pcnoc_mport_clk);
+	clk_disable_unprepare(drvdata->pcnoc_sway_clk);
+
+	return 0;
+}
+
+
+static struct lpass_variant apq8016_data = {
+	.i2sctrl_reg_base	= 0x1000,
+	.i2sctrl_reg_stride	= 0x1000,
+	.i2s_ports		= 4,
+	.irq_reg_base		= 0x6000,
+	.irq_reg_stride		= 0x1000,
+	.irq_ports		= 3,
+	.rdma_reg_base		= 0x8400,
+	.rdma_reg_stride	= 0x1000,
+	.rdma_channels		= 2,
+	.rdmactl_audif_start	= 1,
+	.dai_driver		= apq8016_lpass_cpu_dai_driver,
+	.num_dai		= ARRAY_SIZE(apq8016_lpass_cpu_dai_driver),
+	.init			= apq8016_lpass_init,
+	.exit			= apq8016_lpass_exit,
+	.alloc_dma_channel	= apq8016_lpass_alloc_dma_channel,
+	.free_dma_channel	= apq8016_lpass_free_dma_channel,
+};
+
+static const struct of_device_id apq8016_lpass_cpu_device_id[] = {
+	{ .compatible = "qcom,lpass-cpu-apq8016", .data = &apq8016_data },
+	{}
+};
+MODULE_DEVICE_TABLE(of, apq8016_lpass_cpu_device_id);
+
+static struct platform_driver apq8016_lpass_cpu_platform_driver = {
+	.driver	= {
+		.name		= "apq8016-lpass-cpu",
+		.of_match_table	= of_match_ptr(apq8016_lpass_cpu_device_id),
+	},
+	.probe	= asoc_qcom_lpass_cpu_platform_probe,
+	.remove	= asoc_qcom_lpass_cpu_platform_remove,
+};
+module_platform_driver(apq8016_lpass_cpu_platform_driver);
+
+MODULE_DESCRIPTION("APQ8016 LPASS CPU Driver");
+MODULE_LICENSE("GPL v2");
+
diff --git a/sound/soc/qcom/lpass-cpu.c b/sound/soc/qcom/lpass-cpu.c
index dc790abaa331..23f3d59e6d09 100644
--- a/sound/soc/qcom/lpass-cpu.c
+++ b/sound/soc/qcom/lpass-cpu.c
@@ -14,21 +14,17 @@
  */
 
 #include <linux/clk.h>
-#include <linux/compiler.h>
-#include <linux/device.h>
-#include <linux/err.h>
-#include <linux/ioport.h>
 #include <linux/kernel.h>
-#include <linux/mod_devicetable.h>
 #include <linux/module.h>
 #include <linux/of.h>
+#include <linux/of_device.h>
 #include <linux/platform_device.h>
 #include <sound/pcm.h>
 #include <sound/pcm_params.h>
 #include <linux/regmap.h>
 #include <sound/soc.h>
 #include <sound/soc-dai.h>
-#include "lpass-lpaif-ipq806x.h"
+#include "lpass-lpaif-reg.h"
 #include "lpass.h"
 
 static int lpass_cpu_daiops_set_sysclk(struct snd_soc_dai *dai, int clk_id,
@@ -37,7 +33,10 @@ static int lpass_cpu_daiops_set_sysclk(struct snd_soc_dai *dai, int clk_id,
 	struct lpass_data *drvdata = snd_soc_dai_get_drvdata(dai);
 	int ret;
 
-	ret = clk_set_rate(drvdata->mi2s_osr_clk, freq);
+	if (IS_ERR(drvdata->mi2s_osr_clk[dai->driver->id]))
+		return 0;
+
+	ret = clk_set_rate(drvdata->mi2s_osr_clk[dai->driver->id], freq);
 	if (ret)
 		dev_err(dai->dev, "%s() error setting mi2s osrclk to %u: %d\n",
 				__func__, freq, ret);
@@ -51,18 +50,23 @@ static int lpass_cpu_daiops_startup(struct snd_pcm_substream *substream,
 	struct lpass_data *drvdata = snd_soc_dai_get_drvdata(dai);
 	int ret;
 
-	ret = clk_prepare_enable(drvdata->mi2s_osr_clk);
-	if (ret) {
-		dev_err(dai->dev, "%s() error in enabling mi2s osr clk: %d\n",
-				__func__, ret);
-		return ret;
+	if (!IS_ERR(drvdata->mi2s_osr_clk[dai->driver->id])) {
+		ret = clk_prepare_enable(
+				drvdata->mi2s_osr_clk[dai->driver->id]);
+		if (ret) {
+			dev_err(dai->dev, "%s() error in enabling mi2s osr clk: %d\n",
+					__func__, ret);
+			return ret;
+		}
 	}
 
-	ret = clk_prepare_enable(drvdata->mi2s_bit_clk);
+	ret = clk_prepare_enable(drvdata->mi2s_bit_clk[dai->driver->id]);
 	if (ret) {
 		dev_err(dai->dev, "%s() error in enabling mi2s bit clk: %d\n",
 				__func__, ret);
-		clk_disable_unprepare(drvdata->mi2s_osr_clk);
+		if (!IS_ERR(drvdata->mi2s_osr_clk[dai->driver->id]))
+			clk_disable_unprepare(
+				drvdata->mi2s_osr_clk[dai->driver->id]);
 		return ret;
 	}
 
@@ -74,8 +78,10 @@ static void lpass_cpu_daiops_shutdown(struct snd_pcm_substream *substream,
 {
 	struct lpass_data *drvdata = snd_soc_dai_get_drvdata(dai);
 
-	clk_disable_unprepare(drvdata->mi2s_bit_clk);
-	clk_disable_unprepare(drvdata->mi2s_osr_clk);
+	clk_disable_unprepare(drvdata->mi2s_bit_clk[dai->driver->id]);
+
+	if (!IS_ERR(drvdata->mi2s_osr_clk[dai->driver->id]))
+		clk_disable_unprepare(drvdata->mi2s_osr_clk[dai->driver->id]);
 }
 
 static int lpass_cpu_daiops_hw_params(struct snd_pcm_substream *substream,
@@ -142,14 +148,16 @@ static int lpass_cpu_daiops_hw_params(struct snd_pcm_substream *substream,
 	}
 
 	ret = regmap_write(drvdata->lpaif_map,
-			LPAIF_I2SCTL_REG(LPAIF_I2S_PORT_MI2S), regval);
+			   LPAIF_I2SCTL_REG(drvdata->variant, dai->driver->id),
+			   regval);
 	if (ret) {
 		dev_err(dai->dev, "%s() error writing to i2sctl reg: %d\n",
 				__func__, ret);
 		return ret;
 	}
 
-	ret = clk_set_rate(drvdata->mi2s_bit_clk, rate * bitwidth * 2);
+	ret = clk_set_rate(drvdata->mi2s_bit_clk[dai->driver->id],
+			   rate * bitwidth * 2);
 	if (ret) {
 		dev_err(dai->dev, "%s() error setting mi2s bitclk to %u: %d\n",
 				__func__, rate * bitwidth * 2, ret);
@@ -166,7 +174,8 @@ static int lpass_cpu_daiops_hw_free(struct snd_pcm_substream *substream,
 	int ret;
 
 	ret = regmap_write(drvdata->lpaif_map,
-			LPAIF_I2SCTL_REG(LPAIF_I2S_PORT_MI2S), 0);
+			   LPAIF_I2SCTL_REG(drvdata->variant, dai->driver->id),
+			   0);
 	if (ret)
 		dev_err(dai->dev, "%s() error writing to i2sctl reg: %d\n",
 				__func__, ret);
@@ -181,7 +190,7 @@ static int lpass_cpu_daiops_prepare(struct snd_pcm_substream *substream,
 	int ret;
 
 	ret = regmap_update_bits(drvdata->lpaif_map,
-			LPAIF_I2SCTL_REG(LPAIF_I2S_PORT_MI2S),
+			LPAIF_I2SCTL_REG(drvdata->variant, dai->driver->id),
 			LPAIF_I2SCTL_SPKEN_MASK, LPAIF_I2SCTL_SPKEN_ENABLE);
 	if (ret)
 		dev_err(dai->dev, "%s() error writing to i2sctl reg: %d\n",
@@ -201,7 +210,8 @@ static int lpass_cpu_daiops_trigger(struct snd_pcm_substream *substream,
 	case SNDRV_PCM_TRIGGER_RESUME:
 	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
 		ret = regmap_update_bits(drvdata->lpaif_map,
-				LPAIF_I2SCTL_REG(LPAIF_I2S_PORT_MI2S),
+				LPAIF_I2SCTL_REG(drvdata->variant,
+						dai->driver->id),
 				LPAIF_I2SCTL_SPKEN_MASK,
 				LPAIF_I2SCTL_SPKEN_ENABLE);
 		if (ret)
@@ -212,7 +222,8 @@ static int lpass_cpu_daiops_trigger(struct snd_pcm_substream *substream,
 	case SNDRV_PCM_TRIGGER_SUSPEND:
 	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
 		ret = regmap_update_bits(drvdata->lpaif_map,
-				LPAIF_I2SCTL_REG(LPAIF_I2S_PORT_MI2S),
+				LPAIF_I2SCTL_REG(drvdata->variant,
+						dai->driver->id),
 				LPAIF_I2SCTL_SPKEN_MASK,
 				LPAIF_I2SCTL_SPKEN_DISABLE);
 		if (ret)
@@ -224,7 +235,7 @@ static int lpass_cpu_daiops_trigger(struct snd_pcm_substream *substream,
 	return ret;
 }
 
-static struct snd_soc_dai_ops lpass_cpu_dai_ops = {
+struct snd_soc_dai_ops asoc_qcom_lpass_cpu_dai_ops = {
 	.set_sysclk	= lpass_cpu_daiops_set_sysclk,
 	.startup	= lpass_cpu_daiops_startup,
 	.shutdown	= lpass_cpu_daiops_shutdown,
@@ -233,41 +244,23 @@ static struct snd_soc_dai_ops lpass_cpu_dai_ops = {
 	.prepare	= lpass_cpu_daiops_prepare,
 	.trigger	= lpass_cpu_daiops_trigger,
 };
+EXPORT_SYMBOL_GPL(asoc_qcom_lpass_cpu_dai_ops);
 
-static int lpass_cpu_dai_probe(struct snd_soc_dai *dai)
+int asoc_qcom_lpass_cpu_dai_probe(struct snd_soc_dai *dai)
 {
 	struct lpass_data *drvdata = snd_soc_dai_get_drvdata(dai);
 	int ret;
 
 	/* ensure audio hardware is disabled */
 	ret = regmap_write(drvdata->lpaif_map,
-			LPAIF_I2SCTL_REG(LPAIF_I2S_PORT_MI2S), 0);
+			LPAIF_I2SCTL_REG(drvdata->variant, dai->driver->id), 0);
 	if (ret)
 		dev_err(dai->dev, "%s() error writing to i2sctl reg: %d\n",
 				__func__, ret);
 
 	return ret;
 }
-
-static struct snd_soc_dai_driver lpass_cpu_dai_driver = {
-	.playback = {
-		.stream_name	= "lpass-cpu-playback",
-		.formats	= SNDRV_PCM_FMTBIT_S16 |
-					SNDRV_PCM_FMTBIT_S24 |
-					SNDRV_PCM_FMTBIT_S32,
-		.rates		= SNDRV_PCM_RATE_8000 |
-					SNDRV_PCM_RATE_16000 |
-					SNDRV_PCM_RATE_32000 |
-					SNDRV_PCM_RATE_48000 |
-					SNDRV_PCM_RATE_96000,
-		.rate_min	= 8000,
-		.rate_max	= 96000,
-		.channels_min	= 1,
-		.channels_max	= 8,
-	},
-	.probe	= &lpass_cpu_dai_probe,
-	.ops    = &lpass_cpu_dai_ops,
-};
+EXPORT_SYMBOL_GPL(asoc_qcom_lpass_cpu_dai_probe);
 
 static const struct snd_soc_component_driver lpass_cpu_comp_driver = {
 	.name = "lpass-cpu",
@@ -275,27 +268,29 @@ static const struct snd_soc_component_driver lpass_cpu_comp_driver = {
 
 static bool lpass_cpu_regmap_writeable(struct device *dev, unsigned int reg)
 {
+	struct lpass_data *drvdata = dev_get_drvdata(dev);
+	struct lpass_variant *v = drvdata->variant;
 	int i;
 
-	for (i = 0; i < LPAIF_I2S_PORT_NUM; ++i)
-		if (reg == LPAIF_I2SCTL_REG(i))
+	for (i = 0; i < v->i2s_ports; ++i)
+		if (reg == LPAIF_I2SCTL_REG(v, i))
 			return true;
 
-	for (i = 0; i < LPAIF_IRQ_PORT_NUM; ++i) {
-		if (reg == LPAIF_IRQEN_REG(i))
+	for (i = 0; i < v->irq_ports; ++i) {
+		if (reg == LPAIF_IRQEN_REG(v, i))
 			return true;
-		if (reg == LPAIF_IRQCLEAR_REG(i))
+		if (reg == LPAIF_IRQCLEAR_REG(v, i))
 			return true;
 	}
 
-	for (i = 0; i < LPAIF_RDMA_CHAN_NUM; ++i) {
-		if (reg == LPAIF_RDMACTL_REG(i))
+	for (i = 0; i < v->rdma_channels; ++i) {
+		if (reg == LPAIF_RDMACTL_REG(v, i))
 			return true;
-		if (reg == LPAIF_RDMABASE_REG(i))
+		if (reg == LPAIF_RDMABASE_REG(v, i))
 			return true;
-		if (reg == LPAIF_RDMABUFF_REG(i))
+		if (reg == LPAIF_RDMABUFF_REG(v, i))
 			return true;
-		if (reg == LPAIF_RDMAPER_REG(i))
+		if (reg == LPAIF_RDMAPER_REG(v, i))
 			return true;
 	}
 
@@ -304,29 +299,31 @@ static bool lpass_cpu_regmap_writeable(struct device *dev, unsigned int reg)
 
 static bool lpass_cpu_regmap_readable(struct device *dev, unsigned int reg)
 {
+	struct lpass_data *drvdata = dev_get_drvdata(dev);
+	struct lpass_variant *v = drvdata->variant;
 	int i;
 
-	for (i = 0; i < LPAIF_I2S_PORT_NUM; ++i)
-		if (reg == LPAIF_I2SCTL_REG(i))
+	for (i = 0; i < v->i2s_ports; ++i)
+		if (reg == LPAIF_I2SCTL_REG(v, i))
 			return true;
 
-	for (i = 0; i < LPAIF_IRQ_PORT_NUM; ++i) {
-		if (reg == LPAIF_IRQEN_REG(i))
+	for (i = 0; i < v->irq_ports; ++i) {
+		if (reg == LPAIF_IRQEN_REG(v, i))
 			return true;
-		if (reg == LPAIF_IRQSTAT_REG(i))
+		if (reg == LPAIF_IRQSTAT_REG(v, i))
 			return true;
 	}
 
-	for (i = 0; i < LPAIF_RDMA_CHAN_NUM; ++i) {
-		if (reg == LPAIF_RDMACTL_REG(i))
+	for (i = 0; i < v->rdma_channels; ++i) {
+		if (reg == LPAIF_RDMACTL_REG(v, i))
 			return true;
-		if (reg == LPAIF_RDMABASE_REG(i))
+		if (reg == LPAIF_RDMABASE_REG(v, i))
 			return true;
-		if (reg == LPAIF_RDMABUFF_REG(i))
+		if (reg == LPAIF_RDMABUFF_REG(v, i))
 			return true;
-		if (reg == LPAIF_RDMACURR_REG(i))
+		if (reg == LPAIF_RDMACURR_REG(v, i))
 			return true;
-		if (reg == LPAIF_RDMAPER_REG(i))
+		if (reg == LPAIF_RDMAPER_REG(v, i))
 			return true;
 	}
 
@@ -335,36 +332,41 @@ static bool lpass_cpu_regmap_readable(struct device *dev, unsigned int reg)
 
 static bool lpass_cpu_regmap_volatile(struct device *dev, unsigned int reg)
 {
+	struct lpass_data *drvdata = dev_get_drvdata(dev);
+	struct lpass_variant *v = drvdata->variant;
 	int i;
 
-	for (i = 0; i < LPAIF_IRQ_PORT_NUM; ++i)
-		if (reg == LPAIF_IRQSTAT_REG(i))
+	for (i = 0; i < v->irq_ports; ++i)
+		if (reg == LPAIF_IRQSTAT_REG(v, i))
 			return true;
 
-	for (i = 0; i < LPAIF_RDMA_CHAN_NUM; ++i)
-		if (reg == LPAIF_RDMACURR_REG(i))
+	for (i = 0; i < v->rdma_channels; ++i)
+		if (reg == LPAIF_RDMACURR_REG(v, i))
 			return true;
 
 	return false;
 }
 
-static const struct regmap_config lpass_cpu_regmap_config = {
+static struct regmap_config lpass_cpu_regmap_config = {
 	.reg_bits = 32,
 	.reg_stride = 4,
 	.val_bits = 32,
-	.max_register = LPAIF_RDMAPER_REG(LPAIF_RDMA_CHAN_MAX),
 	.writeable_reg = lpass_cpu_regmap_writeable,
 	.readable_reg = lpass_cpu_regmap_readable,
 	.volatile_reg = lpass_cpu_regmap_volatile,
 	.cache_type = REGCACHE_FLAT,
 };
 
-static int lpass_cpu_platform_probe(struct platform_device *pdev)
+int asoc_qcom_lpass_cpu_platform_probe(struct platform_device *pdev)
 {
 	struct lpass_data *drvdata;
 	struct device_node *dsp_of_node;
 	struct resource *res;
-	int ret;
+	struct lpass_variant *variant;
+	struct device *dev = &pdev->dev;
+	const struct of_device_id *match;
+	char clk_name[16];
+	int ret, i, dai_id;
 
 	dsp_of_node = of_parse_phandle(pdev->dev.of_node, "qcom,adsp", 0);
 	if (dsp_of_node) {
@@ -379,11 +381,14 @@ static int lpass_cpu_platform_probe(struct platform_device *pdev)
 		return -ENOMEM;
 	platform_set_drvdata(pdev, drvdata);
 
+	match = of_match_device(dev->driver->of_match_table, dev);
+	if (!match || !match->data)
+		return -EINVAL;
+
+	drvdata->variant = (struct lpass_variant *)match->data;
+	variant = drvdata->variant;
+
 	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "lpass-lpaif");
-	if (!res) {
-		dev_err(&pdev->dev, "%s() error getting resource\n", __func__);
-		return -ENODEV;
-	}
 
 	drvdata->lpaif = devm_ioremap_resource(&pdev->dev, res);
 	if (IS_ERR((void const __force *)drvdata->lpaif)) {
@@ -393,6 +398,9 @@ static int lpass_cpu_platform_probe(struct platform_device *pdev)
 		return PTR_ERR((void const __force *)drvdata->lpaif);
 	}
 
+	lpass_cpu_regmap_config.max_register = LPAIF_RDMAPER_REG(variant,
+						variant->rdma_channels);
+
 	drvdata->lpaif_map = devm_regmap_init_mmio(&pdev->dev, drvdata->lpaif,
 			&lpass_cpu_regmap_config);
 	if (IS_ERR(drvdata->lpaif_map)) {
@@ -401,18 +409,38 @@ static int lpass_cpu_platform_probe(struct platform_device *pdev)
 		return PTR_ERR(drvdata->lpaif_map);
 	}
 
-	drvdata->mi2s_osr_clk = devm_clk_get(&pdev->dev, "mi2s-osr-clk");
-	if (IS_ERR(drvdata->mi2s_osr_clk)) {
-		dev_err(&pdev->dev, "%s() error getting mi2s-osr-clk: %ld\n",
-				__func__, PTR_ERR(drvdata->mi2s_osr_clk));
-		return PTR_ERR(drvdata->mi2s_osr_clk);
-	}
-
-	drvdata->mi2s_bit_clk = devm_clk_get(&pdev->dev, "mi2s-bit-clk");
-	if (IS_ERR(drvdata->mi2s_bit_clk)) {
-		dev_err(&pdev->dev, "%s() error getting mi2s-bit-clk: %ld\n",
-				__func__, PTR_ERR(drvdata->mi2s_bit_clk));
-		return PTR_ERR(drvdata->mi2s_bit_clk);
+	if (variant->init)
+		variant->init(pdev);
+
+	for (i = 0; i < variant->num_dai; i++) {
+		dai_id = variant->dai_driver[i].id;
+		if (variant->num_dai > 1)
+			sprintf(clk_name, "mi2s-osr-clk%d", i);
+		else
+			sprintf(clk_name, "mi2s-osr-clk");
+
+		drvdata->mi2s_osr_clk[dai_id] = devm_clk_get(&pdev->dev,
+								clk_name);
+		if (IS_ERR(drvdata->mi2s_osr_clk[dai_id])) {
+			dev_warn(&pdev->dev,
+				"%s() error getting mi2s-osr-clk: %ld\n",
+				__func__,
+				PTR_ERR(drvdata->mi2s_osr_clk[dai_id]));
+		}
+
+		if (variant->num_dai > 1)
+			sprintf(clk_name, "mi2s-bit-clk%d", i);
+		else
+			sprintf(clk_name, "mi2s-bit-clk");
+
+		drvdata->mi2s_bit_clk[dai_id] = devm_clk_get(&pdev->dev,
+							    clk_name);
+		if (IS_ERR(drvdata->mi2s_bit_clk[dai_id])) {
+			dev_err(&pdev->dev,
+				"%s() error getting mi2s-bit-clk: %ld\n",
+				__func__, PTR_ERR(drvdata->mi2s_bit_clk[i]));
+			return PTR_ERR(drvdata->mi2s_bit_clk[dai_id]);
+		}
 	}
 
 	drvdata->ahbix_clk = devm_clk_get(&pdev->dev, "ahbix-clk");
@@ -439,7 +467,9 @@ static int lpass_cpu_platform_probe(struct platform_device *pdev)
 	}
 
 	ret = devm_snd_soc_register_component(&pdev->dev,
-			&lpass_cpu_comp_driver, &lpass_cpu_dai_driver, 1);
+					      &lpass_cpu_comp_driver,
+					      variant->dai_driver,
+					      variant->num_dai);
 	if (ret) {
 		dev_err(&pdev->dev, "%s() error registering cpu driver: %d\n",
 				__func__, ret);
@@ -459,33 +489,17 @@ err_clk:
 	clk_disable_unprepare(drvdata->ahbix_clk);
 	return ret;
 }
+EXPORT_SYMBOL_GPL(asoc_qcom_lpass_cpu_platform_probe);
 
-static int lpass_cpu_platform_remove(struct platform_device *pdev)
+int asoc_qcom_lpass_cpu_platform_remove(struct platform_device *pdev)
 {
 	struct lpass_data *drvdata = platform_get_drvdata(pdev);
 
+	if (drvdata->variant->exit)
+		drvdata->variant->exit(pdev);
+
 	clk_disable_unprepare(drvdata->ahbix_clk);
 
 	return 0;
 }
-
-#ifdef CONFIG_OF
-static const struct of_device_id lpass_cpu_device_id[] = {
-	{ .compatible = "qcom,lpass-cpu" },
-	{}
-};
-MODULE_DEVICE_TABLE(of, lpass_cpu_device_id);
-#endif
-
-static struct platform_driver lpass_cpu_platform_driver = {
-	.driver	= {
-		.name		= "lpass-cpu",
-		.of_match_table	= of_match_ptr(lpass_cpu_device_id),
-	},
-	.probe	= lpass_cpu_platform_probe,
-	.remove	= lpass_cpu_platform_remove,
-};
-module_platform_driver(lpass_cpu_platform_driver);
-
-MODULE_DESCRIPTION("QTi LPASS CPU Driver");
-MODULE_LICENSE("GPL v2");
+EXPORT_SYMBOL_GPL(asoc_qcom_lpass_cpu_platform_remove);
diff --git a/sound/soc/qcom/lpass-ipq806x.c b/sound/soc/qcom/lpass-ipq806x.c
new file mode 100644
index 000000000000..7356d3a766d6
--- /dev/null
+++ b/sound/soc/qcom/lpass-ipq806x.c
@@ -0,0 +1,109 @@
+/*
+ * Copyright (c) 2010-2011,2013-2015 The Linux Foundation. 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 and
+ * only 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.
+ *
+ * lpass-ipq806x.c -- ALSA SoC CPU DAI driver for QTi LPASS
+ * Splited out the IPQ8064 soc specific from lpass-cpu.c
+ */
+
+#include <linux/clk.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+#include <sound/soc-dai.h>
+
+#include "lpass-lpaif-reg.h"
+#include "lpass.h"
+
+enum lpaif_i2s_ports {
+	IPQ806X_LPAIF_I2S_PORT_CODEC_SPK,
+	IPQ806X_LPAIF_I2S_PORT_CODEC_MIC,
+	IPQ806X_LPAIF_I2S_PORT_SEC_SPK,
+	IPQ806X_LPAIF_I2S_PORT_SEC_MIC,
+	IPQ806X_LPAIF_I2S_PORT_MI2S,
+};
+
+enum lpaif_dma_channels {
+	IPQ806X_LPAIF_RDMA_CHAN_MI2S,
+	IPQ806X_LPAIF_RDMA_CHAN_PCM0,
+	IPQ806X_LPAIF_RDMA_CHAN_PCM1,
+};
+
+static struct snd_soc_dai_driver ipq806x_lpass_cpu_dai_driver = {
+	.id	= IPQ806X_LPAIF_I2S_PORT_MI2S,
+	.playback = {
+		.stream_name	= "lpass-cpu-playback",
+		.formats	= SNDRV_PCM_FMTBIT_S16 |
+					SNDRV_PCM_FMTBIT_S24 |
+					SNDRV_PCM_FMTBIT_S32,
+		.rates		= SNDRV_PCM_RATE_8000 |
+					SNDRV_PCM_RATE_16000 |
+					SNDRV_PCM_RATE_32000 |
+					SNDRV_PCM_RATE_48000 |
+					SNDRV_PCM_RATE_96000,
+		.rate_min	= 8000,
+		.rate_max	= 96000,
+		.channels_min	= 1,
+		.channels_max	= 8,
+	},
+	.probe	= &asoc_qcom_lpass_cpu_dai_probe,
+	.ops    = &asoc_qcom_lpass_cpu_dai_ops,
+};
+
+static int ipq806x_lpass_alloc_dma_channel(struct lpass_data *drvdata)
+{
+	return IPQ806X_LPAIF_RDMA_CHAN_MI2S;
+}
+
+static int ipq806x_lpass_free_dma_channel(struct lpass_data *drvdata, int chan)
+{
+	return 0;
+}
+
+struct lpass_variant ipq806x_data = {
+	.i2sctrl_reg_base	= 0x0010,
+	.i2sctrl_reg_stride	= 0x04,
+	.i2s_ports		= 5,
+	.irq_reg_base		= 0x3000,
+	.irq_reg_stride		= 0x1000,
+	.irq_ports		= 3,
+	.rdma_reg_base		= 0x6000,
+	.rdma_reg_stride	= 0x1000,
+	.rdma_channels		= 4,
+	.dai_driver		= &ipq806x_lpass_cpu_dai_driver,
+	.num_dai		= 1,
+	.alloc_dma_channel	= ipq806x_lpass_alloc_dma_channel,
+	.free_dma_channel	= ipq806x_lpass_free_dma_channel,
+};
+
+static const struct of_device_id ipq806x_lpass_cpu_device_id[] = {
+	{ .compatible = "qcom,lpass-cpu", .data = &ipq806x_data },
+	{}
+};
+MODULE_DEVICE_TABLE(of, ipq806x_lpass_cpu_device_id);
+
+static struct platform_driver ipq806x_lpass_cpu_platform_driver = {
+	.driver	= {
+		.name		= "lpass-cpu",
+		.of_match_table	= of_match_ptr(ipq806x_lpass_cpu_device_id),
+	},
+	.probe	= asoc_qcom_lpass_cpu_platform_probe,
+	.remove	= asoc_qcom_lpass_cpu_platform_remove,
+};
+module_platform_driver(ipq806x_lpass_cpu_platform_driver);
+
+MODULE_DESCRIPTION("QTi LPASS CPU Driver");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/qcom/lpass-lpaif-ipq806x.h b/sound/soc/qcom/lpass-lpaif-reg.h
index dc423b888842..95e22f131052 100644
--- a/sound/soc/qcom/lpass-lpaif-ipq806x.h
+++ b/sound/soc/qcom/lpass-lpaif-reg.h
@@ -9,37 +9,17 @@
  * 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.
- *
- * lpass-lpaif-ipq806x.h -- Definitions for the QTi LPAIF in the ipq806x LPASS
  */
 
-#ifndef __LPASS_LPAIF_H__
-#define __LPASS_LPAIF_H__
-
-#define LPAIF_BANK_OFFSET		0x1000
+#ifndef __LPASS_LPAIF_REG_H__
+#define __LPASS_LPAIF_REG_H__
 
 /* LPAIF I2S */
 
-#define LPAIF_I2SCTL_REG_BASE		0x0010
-#define LPAIF_I2SCTL_REG_STRIDE		0x4
-#define LPAIF_I2SCTL_REG_ADDR(addr, port) \
-	(LPAIF_I2SCTL_REG_BASE + (addr) + (LPAIF_I2SCTL_REG_STRIDE * (port)))
-
-enum lpaif_i2s_ports {
-	LPAIF_I2S_PORT_MIN		= 0,
-
-	LPAIF_I2S_PORT_CODEC_SPK	= 0,
-	LPAIF_I2S_PORT_CODEC_MIC	= 1,
-	LPAIF_I2S_PORT_SEC_SPK		= 2,
-	LPAIF_I2S_PORT_SEC_MIC		= 3,
-	LPAIF_I2S_PORT_MI2S		= 4,
-
-	LPAIF_I2S_PORT_MAX		= 4,
-	LPAIF_I2S_PORT_NUM		= 5,
-};
-
-#define LPAIF_I2SCTL_REG(port)		LPAIF_I2SCTL_REG_ADDR(0x0, (port))
+#define LPAIF_I2SCTL_REG_ADDR(v, addr, port) \
+	(v->i2sctrl_reg_base + (addr) + v->i2sctrl_reg_stride * (port))
 
+#define LPAIF_I2SCTL_REG(v, port)	LPAIF_I2SCTL_REG_ADDR(v, 0x0, (port))
 #define LPAIF_I2SCTL_LOOPBACK_MASK	0x8000
 #define LPAIF_I2SCTL_LOOPBACK_SHIFT	15
 #define LPAIF_I2SCTL_LOOPBACK_DISABLE	(0 << LPAIF_I2SCTL_LOOPBACK_SHIFT)
@@ -79,55 +59,36 @@ enum lpaif_i2s_ports {
 #define LPAIF_I2SCTL_BITWIDTH_32	(2 << LPAIF_I2SCTL_BITWIDTH_SHIFT)
 
 /* LPAIF IRQ */
+#define LPAIF_IRQ_REG_ADDR(v, addr, port) \
+	(v->irq_reg_base + (addr) + v->irq_reg_stride * (port))
 
-#define LPAIF_IRQ_REG_BASE		0x3000
-#define LPAIF_IRQ_REG_STRIDE		0x1000
-#define LPAIF_IRQ_REG_ADDR(addr, port) \
-	(LPAIF_IRQ_REG_BASE + (addr) + (LPAIF_IRQ_REG_STRIDE * (port)))
-
-enum lpaif_irq_ports {
-	LPAIF_IRQ_PORT_MIN		= 0,
+#define LPAIF_IRQ_PORT_HOST		0
 
-	LPAIF_IRQ_PORT_HOST		= 0,
-	LPAIF_IRQ_PORT_ADSP		= 1,
-
-	LPAIF_IRQ_PORT_MAX		= 2,
-	LPAIF_IRQ_PORT_NUM		= 3,
-};
-
-#define LPAIF_IRQEN_REG(port)		LPAIF_IRQ_REG_ADDR(0x0, (port))
-#define LPAIF_IRQSTAT_REG(port)		LPAIF_IRQ_REG_ADDR(0x4, (port))
-#define LPAIF_IRQCLEAR_REG(port)	LPAIF_IRQ_REG_ADDR(0xC, (port))
+#define LPAIF_IRQEN_REG(v, port)	LPAIF_IRQ_REG_ADDR(v, 0x0, (port))
+#define LPAIF_IRQSTAT_REG(v, port)	LPAIF_IRQ_REG_ADDR(v, 0x4, (port))
+#define LPAIF_IRQCLEAR_REG(v, port)	LPAIF_IRQ_REG_ADDR(v, 0xC, (port))
 
 #define LPAIF_IRQ_BITSTRIDE		3
+
 #define LPAIF_IRQ_PER(chan)		(1 << (LPAIF_IRQ_BITSTRIDE * (chan)))
 #define LPAIF_IRQ_XRUN(chan)		(2 << (LPAIF_IRQ_BITSTRIDE * (chan)))
 #define LPAIF_IRQ_ERR(chan)		(4 << (LPAIF_IRQ_BITSTRIDE * (chan)))
+
 #define LPAIF_IRQ_ALL(chan)		(7 << (LPAIF_IRQ_BITSTRIDE * (chan)))
 
 /* LPAIF DMA */
 
-#define LPAIF_RDMA_REG_BASE		0x6000
-#define LPAIF_RDMA_REG_STRIDE		0x1000
-#define LPAIF_RDMA_REG_ADDR(addr, chan) \
-	(LPAIF_RDMA_REG_BASE + (addr) + (LPAIF_RDMA_REG_STRIDE * (chan)))
-
-enum lpaif_dma_channels {
-	LPAIF_RDMA_CHAN_MIN		= 0,
-
-	LPAIF_RDMA_CHAN_MI2S		= 0,
-	LPAIF_RDMA_CHAN_PCM0		= 1,
-	LPAIF_RDMA_CHAN_PCM1		= 2,
+#define LPAIF_RDMA_REG_ADDR(v, addr, chan) \
+	(v->rdma_reg_base + (addr) + v->rdma_reg_stride * (chan))
 
-	LPAIF_RDMA_CHAN_MAX		= 4,
-	LPAIF_RDMA_CHAN_NUM		= 5,
-};
+#define LPAIF_RDMACTL_AUDINTF(id)	(id << LPAIF_RDMACTL_AUDINTF_SHIFT)
 
-#define LPAIF_RDMACTL_REG(chan)		LPAIF_RDMA_REG_ADDR(0x00, (chan))
-#define LPAIF_RDMABASE_REG(chan)	LPAIF_RDMA_REG_ADDR(0x04, (chan))
-#define	LPAIF_RDMABUFF_REG(chan)	LPAIF_RDMA_REG_ADDR(0x08, (chan))
-#define LPAIF_RDMACURR_REG(chan)	LPAIF_RDMA_REG_ADDR(0x0C, (chan))
-#define	LPAIF_RDMAPER_REG(chan)		LPAIF_RDMA_REG_ADDR(0x10, (chan))
+#define LPAIF_RDMACTL_REG(v, chan)	LPAIF_RDMA_REG_ADDR(v, 0x00, (chan))
+#define LPAIF_RDMABASE_REG(v, chan)	LPAIF_RDMA_REG_ADDR(v, 0x04, (chan))
+#define	LPAIF_RDMABUFF_REG(v, chan)	LPAIF_RDMA_REG_ADDR(v, 0x08, (chan))
+#define LPAIF_RDMACURR_REG(v, chan)	LPAIF_RDMA_REG_ADDR(v, 0x0C, (chan))
+#define	LPAIF_RDMAPER_REG(v, chan)	LPAIF_RDMA_REG_ADDR(v, 0x10, (chan))
+#define	LPAIF_RDMAPERCNT_REG(v, chan)	LPAIF_RDMA_REG_ADDR(v, 0x14, (chan))
 
 #define LPAIF_RDMACTL_BURSTEN_MASK	0x800
 #define LPAIF_RDMACTL_BURSTEN_SHIFT	11
@@ -145,13 +106,6 @@ enum lpaif_dma_channels {
 
 #define LPAIF_RDMACTL_AUDINTF_MASK	0x0F0
 #define LPAIF_RDMACTL_AUDINTF_SHIFT	4
-#define LPAIF_RDMACTL_AUDINTF_NONE	(0 << LPAIF_RDMACTL_AUDINTF_SHIFT)
-#define LPAIF_RDMACTL_AUDINTF_CODEC	(1 << LPAIF_RDMACTL_AUDINTF_SHIFT)
-#define LPAIF_RDMACTL_AUDINTF_PCM	(2 << LPAIF_RDMACTL_AUDINTF_SHIFT)
-#define LPAIF_RDMACTL_AUDINTF_SEC_I2S	(3 << LPAIF_RDMACTL_AUDINTF_SHIFT)
-#define LPAIF_RDMACTL_AUDINTF_MI2S	(4 << LPAIF_RDMACTL_AUDINTF_SHIFT)
-#define LPAIF_RDMACTL_AUDINTF_HDMI	(5 << LPAIF_RDMACTL_AUDINTF_SHIFT)
-#define LPAIF_RDMACTL_AUDINTF_SEC_PCM	(7 << LPAIF_RDMACTL_AUDINTF_SHIFT)
 
 #define LPAIF_RDMACTL_FIFOWM_MASK	0x00E
 #define LPAIF_RDMACTL_FIFOWM_SHIFT	1
@@ -169,4 +123,4 @@ enum lpaif_dma_channels {
 #define LPAIF_RDMACTL_ENABLE_OFF	(0 << LPAIF_RDMACTL_ENABLE_SHIFT)
 #define LPAIF_RDMACTL_ENABLE_ON		(1 << LPAIF_RDMACTL_ENABLE_SHIFT)
 
-#endif /* __LPASS_LPAIF_H__ */
+#endif /* __LPASS_LPAIF_REG_H__ */
diff --git a/sound/soc/qcom/lpass-platform.c b/sound/soc/qcom/lpass-platform.c
index 2fa6280dfb23..79688aa1941a 100644
--- a/sound/soc/qcom/lpass-platform.c
+++ b/sound/soc/qcom/lpass-platform.c
@@ -13,23 +13,22 @@
  * lpass-platform.c -- ALSA SoC platform driver for QTi LPASS
  */
 
-#include <linux/compiler.h>
-#include <linux/device.h>
 #include <linux/dma-mapping.h>
-#include <linux/err.h>
 #include <linux/export.h>
 #include <linux/kernel.h>
 #include <linux/module.h>
-#include <linux/io.h>
 #include <linux/platform_device.h>
-#include <sound/memalloc.h>
-#include <sound/pcm.h>
 #include <sound/pcm_params.h>
 #include <linux/regmap.h>
 #include <sound/soc.h>
-#include "lpass-lpaif-ipq806x.h"
+#include "lpass-lpaif-reg.h"
 #include "lpass.h"
 
+struct lpass_pcm_data {
+	int rdma_ch;
+	int i2s_port;
+};
+
 #define LPASS_PLATFORM_BUFFER_SIZE	(16 * 1024)
 #define LPASS_PLATFORM_PERIODS		2
 
@@ -84,13 +83,15 @@ static int lpass_platform_pcmops_hw_params(struct snd_pcm_substream *substream,
 		struct snd_pcm_hw_params *params)
 {
 	struct snd_soc_pcm_runtime *soc_runtime = substream->private_data;
+	struct lpass_pcm_data *pcm_data = snd_soc_pcm_get_drvdata(soc_runtime);
 	struct lpass_data *drvdata =
 		snd_soc_platform_get_drvdata(soc_runtime->platform);
+	struct lpass_variant *v = drvdata->variant;
 	snd_pcm_format_t format = params_format(params);
 	unsigned int channels = params_channels(params);
 	unsigned int regval;
 	int bitwidth;
-	int ret;
+	int ret, rdma_port = pcm_data->i2s_port + v->rdmactl_audif_start;
 
 	bitwidth = snd_pcm_format_width(format);
 	if (bitwidth < 0) {
@@ -100,7 +101,7 @@ static int lpass_platform_pcmops_hw_params(struct snd_pcm_substream *substream,
 	}
 
 	regval = LPAIF_RDMACTL_BURSTEN_INCR4 |
-			LPAIF_RDMACTL_AUDINTF_MI2S |
+			LPAIF_RDMACTL_AUDINTF(rdma_port) |
 			LPAIF_RDMACTL_FIFOWM_8;
 
 	switch (bitwidth) {
@@ -156,7 +157,7 @@ static int lpass_platform_pcmops_hw_params(struct snd_pcm_substream *substream,
 	}
 
 	ret = regmap_write(drvdata->lpaif_map,
-			LPAIF_RDMACTL_REG(LPAIF_RDMA_CHAN_MI2S), regval);
+			LPAIF_RDMACTL_REG(v, pcm_data->rdma_ch), regval);
 	if (ret) {
 		dev_err(soc_runtime->dev, "%s() error writing to rdmactl reg: %d\n",
 				__func__, ret);
@@ -169,12 +170,14 @@ static int lpass_platform_pcmops_hw_params(struct snd_pcm_substream *substream,
 static int lpass_platform_pcmops_hw_free(struct snd_pcm_substream *substream)
 {
 	struct snd_soc_pcm_runtime *soc_runtime = substream->private_data;
+	struct lpass_pcm_data *pcm_data = snd_soc_pcm_get_drvdata(soc_runtime);
 	struct lpass_data *drvdata =
 		snd_soc_platform_get_drvdata(soc_runtime->platform);
+	struct lpass_variant *v = drvdata->variant;
 	int ret;
 
 	ret = regmap_write(drvdata->lpaif_map,
-			LPAIF_RDMACTL_REG(LPAIF_RDMA_CHAN_MI2S), 0);
+			LPAIF_RDMACTL_REG(v, pcm_data->rdma_ch), 0);
 	if (ret)
 		dev_err(soc_runtime->dev, "%s() error writing to rdmactl reg: %d\n",
 				__func__, ret);
@@ -186,12 +189,14 @@ static int lpass_platform_pcmops_prepare(struct snd_pcm_substream *substream)
 {
 	struct snd_pcm_runtime *runtime = substream->runtime;
 	struct snd_soc_pcm_runtime *soc_runtime = substream->private_data;
+	struct lpass_pcm_data *pcm_data = snd_soc_pcm_get_drvdata(soc_runtime);
 	struct lpass_data *drvdata =
 		snd_soc_platform_get_drvdata(soc_runtime->platform);
-	int ret;
+	struct lpass_variant *v = drvdata->variant;
+	int ret, ch = pcm_data->rdma_ch;
 
 	ret = regmap_write(drvdata->lpaif_map,
-			LPAIF_RDMABASE_REG(LPAIF_RDMA_CHAN_MI2S),
+			LPAIF_RDMABASE_REG(v, ch),
 			runtime->dma_addr);
 	if (ret) {
 		dev_err(soc_runtime->dev, "%s() error writing to rdmabase reg: %d\n",
@@ -200,7 +205,7 @@ static int lpass_platform_pcmops_prepare(struct snd_pcm_substream *substream)
 	}
 
 	ret = regmap_write(drvdata->lpaif_map,
-			LPAIF_RDMABUFF_REG(LPAIF_RDMA_CHAN_MI2S),
+			LPAIF_RDMABUFF_REG(v, ch),
 			(snd_pcm_lib_buffer_bytes(substream) >> 2) - 1);
 	if (ret) {
 		dev_err(soc_runtime->dev, "%s() error writing to rdmabuff reg: %d\n",
@@ -209,7 +214,7 @@ static int lpass_platform_pcmops_prepare(struct snd_pcm_substream *substream)
 	}
 
 	ret = regmap_write(drvdata->lpaif_map,
-			LPAIF_RDMAPER_REG(LPAIF_RDMA_CHAN_MI2S),
+			LPAIF_RDMAPER_REG(v, ch),
 			(snd_pcm_lib_period_bytes(substream) >> 2) - 1);
 	if (ret) {
 		dev_err(soc_runtime->dev, "%s() error writing to rdmaper reg: %d\n",
@@ -218,7 +223,7 @@ static int lpass_platform_pcmops_prepare(struct snd_pcm_substream *substream)
 	}
 
 	ret = regmap_update_bits(drvdata->lpaif_map,
-			LPAIF_RDMACTL_REG(LPAIF_RDMA_CHAN_MI2S),
+			LPAIF_RDMACTL_REG(v, ch),
 			LPAIF_RDMACTL_ENABLE_MASK, LPAIF_RDMACTL_ENABLE_ON);
 	if (ret) {
 		dev_err(soc_runtime->dev, "%s() error writing to rdmactl reg: %d\n",
@@ -233,9 +238,11 @@ static int lpass_platform_pcmops_trigger(struct snd_pcm_substream *substream,
 		int cmd)
 {
 	struct snd_soc_pcm_runtime *soc_runtime = substream->private_data;
+	struct lpass_pcm_data *pcm_data = snd_soc_pcm_get_drvdata(soc_runtime);
 	struct lpass_data *drvdata =
 		snd_soc_platform_get_drvdata(soc_runtime->platform);
-	int ret;
+	struct lpass_variant *v = drvdata->variant;
+	int ret, ch = pcm_data->rdma_ch;
 
 	switch (cmd) {
 	case SNDRV_PCM_TRIGGER_START:
@@ -243,8 +250,8 @@ static int lpass_platform_pcmops_trigger(struct snd_pcm_substream *substream,
 	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
 		/* clear status before enabling interrupts */
 		ret = regmap_write(drvdata->lpaif_map,
-				LPAIF_IRQCLEAR_REG(LPAIF_IRQ_PORT_HOST),
-				LPAIF_IRQ_ALL(LPAIF_RDMA_CHAN_MI2S));
+				LPAIF_IRQCLEAR_REG(v, LPAIF_IRQ_PORT_HOST),
+				LPAIF_IRQ_ALL(ch));
 		if (ret) {
 			dev_err(soc_runtime->dev, "%s() error writing to irqclear reg: %d\n",
 					__func__, ret);
@@ -252,9 +259,9 @@ static int lpass_platform_pcmops_trigger(struct snd_pcm_substream *substream,
 		}
 
 		ret = regmap_update_bits(drvdata->lpaif_map,
-				LPAIF_IRQEN_REG(LPAIF_IRQ_PORT_HOST),
-				LPAIF_IRQ_ALL(LPAIF_RDMA_CHAN_MI2S),
-				LPAIF_IRQ_ALL(LPAIF_RDMA_CHAN_MI2S));
+				LPAIF_IRQEN_REG(v, LPAIF_IRQ_PORT_HOST),
+				LPAIF_IRQ_ALL(ch),
+				LPAIF_IRQ_ALL(ch));
 		if (ret) {
 			dev_err(soc_runtime->dev, "%s() error writing to irqen reg: %d\n",
 					__func__, ret);
@@ -262,7 +269,7 @@ static int lpass_platform_pcmops_trigger(struct snd_pcm_substream *substream,
 		}
 
 		ret = regmap_update_bits(drvdata->lpaif_map,
-				LPAIF_RDMACTL_REG(LPAIF_RDMA_CHAN_MI2S),
+				LPAIF_RDMACTL_REG(v, ch),
 				LPAIF_RDMACTL_ENABLE_MASK,
 				LPAIF_RDMACTL_ENABLE_ON);
 		if (ret) {
@@ -275,7 +282,7 @@ static int lpass_platform_pcmops_trigger(struct snd_pcm_substream *substream,
 	case SNDRV_PCM_TRIGGER_SUSPEND:
 	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
 		ret = regmap_update_bits(drvdata->lpaif_map,
-				LPAIF_RDMACTL_REG(LPAIF_RDMA_CHAN_MI2S),
+				LPAIF_RDMACTL_REG(v, ch),
 				LPAIF_RDMACTL_ENABLE_MASK,
 				LPAIF_RDMACTL_ENABLE_OFF);
 		if (ret) {
@@ -285,8 +292,8 @@ static int lpass_platform_pcmops_trigger(struct snd_pcm_substream *substream,
 		}
 
 		ret = regmap_update_bits(drvdata->lpaif_map,
-				LPAIF_IRQEN_REG(LPAIF_IRQ_PORT_HOST),
-				LPAIF_IRQ_ALL(LPAIF_RDMA_CHAN_MI2S), 0);
+				LPAIF_IRQEN_REG(v, LPAIF_IRQ_PORT_HOST),
+				LPAIF_IRQ_ALL(ch), 0);
 		if (ret) {
 			dev_err(soc_runtime->dev, "%s() error writing to irqen reg: %d\n",
 					__func__, ret);
@@ -302,13 +309,15 @@ static snd_pcm_uframes_t lpass_platform_pcmops_pointer(
 		struct snd_pcm_substream *substream)
 {
 	struct snd_soc_pcm_runtime *soc_runtime = substream->private_data;
+	struct lpass_pcm_data *pcm_data = snd_soc_pcm_get_drvdata(soc_runtime);
 	struct lpass_data *drvdata =
 			snd_soc_platform_get_drvdata(soc_runtime->platform);
+	struct lpass_variant *v = drvdata->variant;
 	unsigned int base_addr, curr_addr;
-	int ret;
+	int ret, ch = pcm_data->rdma_ch;
 
 	ret = regmap_read(drvdata->lpaif_map,
-			LPAIF_RDMABASE_REG(LPAIF_RDMA_CHAN_MI2S), &base_addr);
+			LPAIF_RDMABASE_REG(v, ch), &base_addr);
 	if (ret) {
 		dev_err(soc_runtime->dev, "%s() error reading from rdmabase reg: %d\n",
 				__func__, ret);
@@ -316,7 +325,7 @@ static snd_pcm_uframes_t lpass_platform_pcmops_pointer(
 	}
 
 	ret = regmap_read(drvdata->lpaif_map,
-			LPAIF_RDMACURR_REG(LPAIF_RDMA_CHAN_MI2S), &curr_addr);
+			LPAIF_RDMACURR_REG(v, ch), &curr_addr);
 	if (ret) {
 		dev_err(soc_runtime->dev, "%s() error reading from rdmacurr reg: %d\n",
 				__func__, ret);
@@ -347,29 +356,20 @@ static struct snd_pcm_ops lpass_platform_pcm_ops = {
 	.mmap		= lpass_platform_pcmops_mmap,
 };
 
-static irqreturn_t lpass_platform_lpaif_irq(int irq, void *data)
+static irqreturn_t lpass_dma_interrupt_handler(
+			struct snd_pcm_substream *substream,
+			struct lpass_data *drvdata,
+			int chan, u32 interrupts)
 {
-	struct snd_pcm_substream *substream = data;
 	struct snd_soc_pcm_runtime *soc_runtime = substream->private_data;
-	struct lpass_data *drvdata =
-		snd_soc_platform_get_drvdata(soc_runtime->platform);
-	unsigned int interrupts;
+	struct lpass_variant *v = drvdata->variant;
 	irqreturn_t ret = IRQ_NONE;
 	int rv;
 
-	rv = regmap_read(drvdata->lpaif_map,
-			LPAIF_IRQSTAT_REG(LPAIF_IRQ_PORT_HOST), &interrupts);
-	if (rv) {
-		dev_err(soc_runtime->dev, "%s() error reading from irqstat reg: %d\n",
-				__func__, rv);
-		return IRQ_NONE;
-	}
-	interrupts &= LPAIF_IRQ_ALL(LPAIF_RDMA_CHAN_MI2S);
-
-	if (interrupts & LPAIF_IRQ_PER(LPAIF_RDMA_CHAN_MI2S)) {
+	if (interrupts & LPAIF_IRQ_PER(chan)) {
 		rv = regmap_write(drvdata->lpaif_map,
-				LPAIF_IRQCLEAR_REG(LPAIF_IRQ_PORT_HOST),
-				LPAIF_IRQ_PER(LPAIF_RDMA_CHAN_MI2S));
+				LPAIF_IRQCLEAR_REG(v, LPAIF_IRQ_PORT_HOST),
+				LPAIF_IRQ_PER(chan));
 		if (rv) {
 			dev_err(soc_runtime->dev, "%s() error writing to irqclear reg: %d\n",
 					__func__, rv);
@@ -379,10 +379,10 @@ static irqreturn_t lpass_platform_lpaif_irq(int irq, void *data)
 		ret = IRQ_HANDLED;
 	}
 
-	if (interrupts & LPAIF_IRQ_XRUN(LPAIF_RDMA_CHAN_MI2S)) {
+	if (interrupts & LPAIF_IRQ_XRUN(chan)) {
 		rv = regmap_write(drvdata->lpaif_map,
-				LPAIF_IRQCLEAR_REG(LPAIF_IRQ_PORT_HOST),
-				LPAIF_IRQ_XRUN(LPAIF_RDMA_CHAN_MI2S));
+				LPAIF_IRQCLEAR_REG(v, LPAIF_IRQ_PORT_HOST),
+				LPAIF_IRQ_XRUN(chan));
 		if (rv) {
 			dev_err(soc_runtime->dev, "%s() error writing to irqclear reg: %d\n",
 					__func__, rv);
@@ -393,10 +393,10 @@ static irqreturn_t lpass_platform_lpaif_irq(int irq, void *data)
 		ret = IRQ_HANDLED;
 	}
 
-	if (interrupts & LPAIF_IRQ_ERR(LPAIF_RDMA_CHAN_MI2S)) {
+	if (interrupts & LPAIF_IRQ_ERR(chan)) {
 		rv = regmap_write(drvdata->lpaif_map,
-				LPAIF_IRQCLEAR_REG(LPAIF_IRQ_PORT_HOST),
-				LPAIF_IRQ_ERR(LPAIF_RDMA_CHAN_MI2S));
+				LPAIF_IRQCLEAR_REG(v, LPAIF_IRQ_PORT_HOST),
+				LPAIF_IRQ_ERR(chan));
 		if (rv) {
 			dev_err(soc_runtime->dev, "%s() error writing to irqclear reg: %d\n",
 					__func__, rv);
@@ -410,6 +410,35 @@ static irqreturn_t lpass_platform_lpaif_irq(int irq, void *data)
 	return ret;
 }
 
+static irqreturn_t lpass_platform_lpaif_irq(int irq, void *data)
+{
+	struct lpass_data *drvdata = data;
+	struct lpass_variant *v = drvdata->variant;
+	unsigned int irqs;
+	int rv, chan;
+
+	rv = regmap_read(drvdata->lpaif_map,
+			LPAIF_IRQSTAT_REG(v, LPAIF_IRQ_PORT_HOST), &irqs);
+	if (rv) {
+		pr_err("%s() error reading from irqstat reg: %d\n",
+				__func__, rv);
+		return IRQ_NONE;
+	}
+
+	/* Handle per channel interrupts */
+	for (chan = 0; chan < LPASS_MAX_DMA_CHANNELS; chan++) {
+		if (irqs & LPAIF_IRQ_ALL(chan) && drvdata->substream[chan]) {
+			rv = lpass_dma_interrupt_handler(
+						drvdata->substream[chan],
+						drvdata, chan, irqs);
+			if (rv != IRQ_HANDLED)
+				return rv;
+		}
+	}
+
+	return IRQ_HANDLED;
+}
+
 static int lpass_platform_alloc_buffer(struct snd_pcm_substream *substream,
 		struct snd_soc_pcm_runtime *soc_runtime)
 {
@@ -448,9 +477,27 @@ static int lpass_platform_pcm_new(struct snd_soc_pcm_runtime *soc_runtime)
 	struct snd_pcm *pcm = soc_runtime->pcm;
 	struct snd_pcm_substream *substream =
 		pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream;
+	struct snd_soc_dai *cpu_dai = soc_runtime->cpu_dai;
 	struct lpass_data *drvdata =
 		snd_soc_platform_get_drvdata(soc_runtime->platform);
+	struct lpass_variant *v = drvdata->variant;
 	int ret;
+	struct lpass_pcm_data *data;
+
+	data = devm_kzalloc(soc_runtime->dev, sizeof(*data), GFP_KERNEL);
+	if (!data)
+		return -ENOMEM;
+
+	if (v->alloc_dma_channel)
+		data->rdma_ch = v->alloc_dma_channel(drvdata);
+
+	if (IS_ERR_VALUE(data->rdma_ch))
+		return data->rdma_ch;
+
+	drvdata->substream[data->rdma_ch] = substream;
+	data->i2s_port = cpu_dai->driver->id;
+
+	snd_soc_pcm_set_drvdata(soc_runtime, data);
 
 	soc_runtime->dev->coherent_dma_mask = DMA_BIT_MASK(32);
 	soc_runtime->dev->dma_mask = &soc_runtime->dev->coherent_dma_mask;
@@ -459,29 +506,12 @@ static int lpass_platform_pcm_new(struct snd_soc_pcm_runtime *soc_runtime)
 	if (ret)
 		return ret;
 
-	ret = devm_request_irq(soc_runtime->dev, drvdata->lpaif_irq,
-			lpass_platform_lpaif_irq, IRQF_TRIGGER_RISING,
-			"lpass-irq-lpaif", substream);
-	if (ret) {
-		dev_err(soc_runtime->dev, "%s() irq request failed: %d\n",
-				__func__, ret);
-		goto err_buf;
-	}
-
-	/* ensure audio hardware is disabled */
 	ret = regmap_write(drvdata->lpaif_map,
-			LPAIF_IRQEN_REG(LPAIF_IRQ_PORT_HOST), 0);
-	if (ret) {
-		dev_err(soc_runtime->dev, "%s() error writing to irqen reg: %d\n",
-				__func__, ret);
-		return ret;
-	}
-	ret = regmap_write(drvdata->lpaif_map,
-			LPAIF_RDMACTL_REG(LPAIF_RDMA_CHAN_MI2S), 0);
+			LPAIF_RDMACTL_REG(v, data->rdma_ch), 0);
 	if (ret) {
 		dev_err(soc_runtime->dev, "%s() error writing to rdmactl reg: %d\n",
 				__func__, ret);
-		return ret;
+		goto err_buf;
 	}
 
 	return 0;
@@ -496,6 +526,15 @@ static void lpass_platform_pcm_free(struct snd_pcm *pcm)
 	struct snd_pcm_substream *substream =
 		pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream;
 	struct snd_soc_pcm_runtime *soc_runtime = substream->private_data;
+	struct lpass_data *drvdata =
+		snd_soc_platform_get_drvdata(soc_runtime->platform);
+	struct lpass_pcm_data *data = snd_soc_pcm_get_drvdata(soc_runtime);
+	struct lpass_variant *v = drvdata->variant;
+
+	drvdata->substream[data->rdma_ch] = NULL;
+
+	if (v->free_dma_channel)
+		v->free_dma_channel(drvdata, data->rdma_ch);
 
 	lpass_platform_free_buffer(substream, soc_runtime);
 }
@@ -509,6 +548,8 @@ static struct snd_soc_platform_driver lpass_platform_driver = {
 int asoc_qcom_lpass_platform_register(struct platform_device *pdev)
 {
 	struct lpass_data *drvdata = platform_get_drvdata(pdev);
+	struct lpass_variant *v = drvdata->variant;
+	int ret;
 
 	drvdata->lpaif_irq = platform_get_irq_byname(pdev, "lpass-irq-lpaif");
 	if (drvdata->lpaif_irq < 0) {
@@ -517,6 +558,25 @@ int asoc_qcom_lpass_platform_register(struct platform_device *pdev)
 		return -ENODEV;
 	}
 
+	/* ensure audio hardware is disabled */
+	ret = regmap_write(drvdata->lpaif_map,
+			LPAIF_IRQEN_REG(v, LPAIF_IRQ_PORT_HOST), 0);
+	if (ret) {
+		dev_err(&pdev->dev, "%s() error writing to irqen reg: %d\n",
+				__func__, ret);
+		return ret;
+	}
+
+	ret = devm_request_irq(&pdev->dev, drvdata->lpaif_irq,
+			lpass_platform_lpaif_irq, IRQF_TRIGGER_RISING,
+			"lpass-irq-lpaif", drvdata);
+	if (ret) {
+		dev_err(&pdev->dev, "%s() irq request failed: %d\n",
+				__func__, ret);
+		return ret;
+	}
+
+
 	return devm_snd_soc_register_platform(&pdev->dev,
 			&lpass_platform_driver);
 }
diff --git a/sound/soc/qcom/lpass.h b/sound/soc/qcom/lpass.h
index 5c99b3dace86..d6e86c119e74 100644
--- a/sound/soc/qcom/lpass.h
+++ b/sound/soc/qcom/lpass.h
@@ -22,6 +22,8 @@
 #include <linux/regmap.h>
 
 #define LPASS_AHBIX_CLOCK_FREQUENCY		131072000
+#define LPASS_MAX_MI2S_PORTS			(8)
+#define LPASS_MAX_DMA_CHANNELS			(8)
 
 /* Both the CPU DAI and platform drivers will access this data */
 struct lpass_data {
@@ -30,10 +32,10 @@ struct lpass_data {
 	struct clk *ahbix_clk;
 
 	/* MI2S system clock */
-	struct clk *mi2s_osr_clk;
+	struct clk *mi2s_osr_clk[LPASS_MAX_MI2S_PORTS];
 
 	/* MI2S bit clock (derived from system clock by a divider */
-	struct clk *mi2s_bit_clk;
+	struct clk *mi2s_bit_clk[LPASS_MAX_MI2S_PORTS];
 
 	/* low-power audio interface (LPAIF) registers */
 	void __iomem *lpaif;
@@ -43,9 +45,54 @@ struct lpass_data {
 
 	/* interrupts from the low-power audio interface (LPAIF) */
 	int lpaif_irq;
+
+	/* SOC specific variations in the LPASS IP integration */
+	struct lpass_variant *variant;
+
+	/* bit map to keep track of static channel allocations */
+	unsigned long rdma_ch_bit_map;
+
+	/* used it for handling interrupt per dma channel */
+	struct snd_pcm_substream *substream[LPASS_MAX_DMA_CHANNELS];
+
+	/* 8016 specific */
+	struct clk *pcnoc_mport_clk;
+	struct clk *pcnoc_sway_clk;
+};
+
+/* Vairant data per each SOC */
+struct lpass_variant {
+	u32	i2sctrl_reg_base;
+	u32	i2sctrl_reg_stride;
+	u32	i2s_ports;
+	u32	irq_reg_base;
+	u32	irq_reg_stride;
+	u32	irq_ports;
+	u32	rdma_reg_base;
+	u32	rdma_reg_stride;
+	u32	rdma_channels;
+
+	/**
+	 * on SOCs like APQ8016 the channel control bits start
+	 * at different offset to ipq806x
+	 **/
+	u32	rdmactl_audif_start;
+	/* SOC specific intialization like clocks */
+	int (*init)(struct platform_device *pdev);
+	int (*exit)(struct platform_device *pdev);
+	int (*alloc_dma_channel)(struct lpass_data *data);
+	int (*free_dma_channel)(struct lpass_data *data, int ch);
+
+	/* SOC specific dais */
+	struct snd_soc_dai_driver *dai_driver;
+	int num_dai;
 };
 
 /* register the platform driver from the CPU DAI driver */
 int asoc_qcom_lpass_platform_register(struct platform_device *);
+int asoc_qcom_lpass_cpu_platform_remove(struct platform_device *pdev);
+int asoc_qcom_lpass_cpu_platform_probe(struct platform_device *pdev);
+int asoc_qcom_lpass_cpu_dai_probe(struct snd_soc_dai *dai);
+extern struct snd_soc_dai_ops asoc_qcom_lpass_cpu_dai_ops;
 
 #endif /* __LPASS_H__ */
diff --git a/sound/soc/samsung/Kconfig b/sound/soc/samsung/Kconfig
index 0632a36852c8..3744c9ed5370 100644
--- a/sound/soc/samsung/Kconfig
+++ b/sound/soc/samsung/Kconfig
@@ -174,7 +174,8 @@ config SND_SOC_SMDK_WM8994_PCM
 
 config SND_SOC_SPEYSIDE
 	tristate "Audio support for Wolfson Speyside"
-	depends on SND_SOC_SAMSUNG && MACH_WLF_CRAGG_6410 && I2C && SPI_MASTER
+	depends on SND_SOC_SAMSUNG && I2C && SPI_MASTER
+	depends on MACH_WLF_CRAGG_6410 || COMPILE_TEST
 	select SND_SAMSUNG_I2S
 	select SND_SOC_WM8996
 	select SND_SOC_WM9081
@@ -183,13 +184,15 @@ config SND_SOC_SPEYSIDE
 
 config SND_SOC_TOBERMORY
 	tristate "Audio support for Wolfson Tobermory"
-	depends on SND_SOC_SAMSUNG && MACH_WLF_CRAGG_6410 && INPUT && I2C
+	depends on SND_SOC_SAMSUNG && INPUT && I2C
+	depends on MACH_WLF_CRAGG_6410 || COMPILE_TEST
 	select SND_SAMSUNG_I2S
 	select SND_SOC_WM8962
 
 config SND_SOC_BELLS
 	tristate "Audio support for Wolfson Bells"
-	depends on SND_SOC_SAMSUNG && MACH_WLF_CRAGG_6410 && MFD_ARIZONA && I2C && SPI_MASTER
+	depends on SND_SOC_SAMSUNG && MFD_ARIZONA && I2C && SPI_MASTER
+	depends on MACH_WLF_CRAGG_6410 || COMPILE_TEST
 	select SND_SAMSUNG_I2S
 	select SND_SOC_WM5102
 	select SND_SOC_WM5110
@@ -199,14 +202,16 @@ config SND_SOC_BELLS
 
 config SND_SOC_LOWLAND
 	tristate "Audio support for Wolfson Lowland"
-	depends on SND_SOC_SAMSUNG && MACH_WLF_CRAGG_6410 && I2C
+	depends on SND_SOC_SAMSUNG && I2C
+	depends on MACH_WLF_CRAGG_6410 || COMPILE_TEST
 	select SND_SAMSUNG_I2S
 	select SND_SOC_WM5100
 	select SND_SOC_WM9081
 
 config SND_SOC_LITTLEMILL
 	tristate "Audio support for Wolfson Littlemill"
-	depends on SND_SOC_SAMSUNG && MACH_WLF_CRAGG_6410 && I2C
+	depends on SND_SOC_SAMSUNG && I2C
+	depends on MACH_WLF_CRAGG_6410 || COMPILE_TEST
 	select SND_SAMSUNG_I2S
 	select MFD_WM8994
 	select SND_SOC_WM8994
diff --git a/sound/soc/samsung/i2s.c b/sound/soc/samsung/i2s.c
index b92ab40d2be6..ea4ab374a223 100644
--- a/sound/soc/samsung/i2s.c
+++ b/sound/soc/samsung/i2s.c
@@ -1493,7 +1493,7 @@ static const struct samsung_i2s_dai_data samsung_dai_type_sec = {
 	.dai_type = TYPE_SEC,
 };
 
-static struct platform_device_id samsung_i2s_driver_ids[] = {
+static const struct platform_device_id samsung_i2s_driver_ids[] = {
 	{
 		.name           = "samsung-i2s",
 		.driver_data	= (kernel_ulong_t)&i2sv3_dai_type,
diff --git a/sound/soc/samsung/lowland.c b/sound/soc/samsung/lowland.c
index 5f156093101e..0d0f58208b75 100644
--- a/sound/soc/samsung/lowland.c
+++ b/sound/soc/samsung/lowland.c
@@ -72,7 +72,7 @@ static int lowland_wm9081_init(struct snd_soc_pcm_runtime *rtd)
 {
 	struct snd_soc_codec *codec = rtd->codec;
 
-	snd_soc_dapm_nc_pin(&codec->dapm, "LINEOUT");
+	snd_soc_dapm_nc_pin(&rtd->card->dapm, "LINEOUT");
 
 	/* At any time the WM9081 is active it will have this clock */
 	return snd_soc_codec_set_sysclk(codec, WM9081_SYSCLK_MCLK, 0,
diff --git a/sound/soc/samsung/smartq_wm8987.c b/sound/soc/samsung/smartq_wm8987.c
index dfbe2db1c407..a0fe37fbed9f 100644
--- a/sound/soc/samsung/smartq_wm8987.c
+++ b/sound/soc/samsung/smartq_wm8987.c
@@ -137,8 +137,7 @@ static const struct snd_soc_dapm_route audio_map[] = {
 
 static int smartq_wm8987_init(struct snd_soc_pcm_runtime *rtd)
 {
-	struct snd_soc_codec *codec = rtd->codec;
-	struct snd_soc_dapm_context *dapm = &codec->dapm;
+	struct snd_soc_dapm_context *dapm = &rtd->card->dapm;
 	int err = 0;
 
 	/* set endpoints to not connected */
@@ -147,9 +146,6 @@ static int smartq_wm8987_init(struct snd_soc_pcm_runtime *rtd)
 	snd_soc_dapm_nc_pin(dapm, "OUT3");
 	snd_soc_dapm_nc_pin(dapm, "ROUT1");
 
-	/* set endpoints to default off mode */
-	snd_soc_dapm_disable_pin(dapm, "Headphone Jack");
-
 	/* Headphone jack detection */
 	err = snd_soc_card_jack_new(rtd->card, "Headphone Jack",
 				    SND_JACK_HEADPHONE, &smartq_jack,
diff --git a/sound/soc/samsung/smdk_wm8994.c b/sound/soc/samsung/smdk_wm8994.c
index d38595fbdab7..ff57b192d37d 100644
--- a/sound/soc/samsung/smdk_wm8994.c
+++ b/sound/soc/samsung/smdk_wm8994.c
@@ -86,8 +86,7 @@ static struct snd_soc_ops smdk_ops = {
 
 static int smdk_wm8994_init_paiftx(struct snd_soc_pcm_runtime *rtd)
 {
-	struct snd_soc_codec *codec = rtd->codec;
-	struct snd_soc_dapm_context *dapm = &codec->dapm;
+	struct snd_soc_dapm_context *dapm = &rtd->card->dapm;
 
 	/* Other pins NC */
 	snd_soc_dapm_nc_pin(dapm, "HPOUT2P");
diff --git a/sound/soc/samsung/speyside.c b/sound/soc/samsung/speyside.c
index 2dcb988bdff2..d1ae21c5e253 100644
--- a/sound/soc/samsung/speyside.c
+++ b/sound/soc/samsung/speyside.c
@@ -123,7 +123,7 @@ static void speyside_set_polarity(struct snd_soc_codec *codec,
 	gpio_direction_output(WM8996_HPSEL_GPIO, speyside_jack_polarity);
 
 	/* Re-run DAPM to make sure we're using the correct mic bias */
-	snd_soc_dapm_sync(&codec->dapm);
+	snd_soc_dapm_sync(snd_soc_codec_get_dapm(codec));
 }
 
 static int speyside_wm0010_init(struct snd_soc_pcm_runtime *rtd)
diff --git a/sound/soc/sh/rcar/core.c b/sound/soc/sh/rcar/core.c
index 9f48d75fa992..d460d2aa82ee 100644
--- a/sound/soc/sh/rcar/core.c
+++ b/sound/soc/sh/rcar/core.c
@@ -170,6 +170,14 @@ void rsnd_mod_quit(struct rsnd_mod *mod)
 		clk_unprepare(mod->clk);
 }
 
+int rsnd_mod_is_working(struct rsnd_mod *mod)
+{
+	struct rsnd_dai_stream *io = rsnd_mod_to_io(mod);
+
+	/* see rsnd_dai_stream_init/quit() */
+	return !!io->substream;
+}
+
 /*
  *	settting function
  */
@@ -272,9 +280,10 @@ struct rsnd_dai *rsnd_rdai_get(struct rsnd_priv *priv, int id)
 	return priv->rdai + id;
 }
 
+#define rsnd_dai_to_priv(dai) snd_soc_dai_get_drvdata(dai)
 static struct rsnd_dai *rsnd_dai_to_rdai(struct snd_soc_dai *dai)
 {
-	struct rsnd_priv *priv = snd_soc_dai_get_drvdata(dai);
+	struct rsnd_priv *priv = rsnd_dai_to_priv(dai);
 
 	return rsnd_rdai_get(priv, dai->id);
 }
@@ -314,7 +323,7 @@ void rsnd_dai_pointer_update(struct rsnd_dai_stream *io, int byte)
 	}
 }
 
-static int rsnd_dai_stream_init(struct rsnd_dai_stream *io,
+static void rsnd_dai_stream_init(struct rsnd_dai_stream *io,
 				struct snd_pcm_substream *substream)
 {
 	struct snd_pcm_runtime *runtime = substream->runtime;
@@ -326,8 +335,11 @@ static int rsnd_dai_stream_init(struct rsnd_dai_stream *io,
 				  runtime->channels *
 				  samples_to_bytes(runtime, 1);
 	io->next_period_byte	= io->byte_per_period;
+}
 
-	return 0;
+static void rsnd_dai_stream_quit(struct rsnd_dai_stream *io)
+{
+	io->substream		= NULL;
 }
 
 static
@@ -351,20 +363,18 @@ struct rsnd_dai_stream *rsnd_rdai_to_io(struct rsnd_dai *rdai,
 static int rsnd_soc_dai_trigger(struct snd_pcm_substream *substream, int cmd,
 			    struct snd_soc_dai *dai)
 {
-	struct rsnd_priv *priv = snd_soc_dai_get_drvdata(dai);
+	struct rsnd_priv *priv = rsnd_dai_to_priv(dai);
 	struct rsnd_dai *rdai = rsnd_dai_to_rdai(dai);
 	struct rsnd_dai_stream *io = rsnd_rdai_to_io(rdai, substream);
 	int ssi_id = rsnd_mod_id(rsnd_io_to_mod_ssi(io));
 	int ret;
 	unsigned long flags;
 
-	rsnd_lock(priv, flags);
+	spin_lock_irqsave(&priv->lock, flags);
 
 	switch (cmd) {
 	case SNDRV_PCM_TRIGGER_START:
-		ret = rsnd_dai_stream_init(io, substream);
-		if (ret < 0)
-			goto dai_trigger_end;
+		rsnd_dai_stream_init(io, substream);
 
 		ret = rsnd_platform_call(priv, dai, start, ssi_id);
 		if (ret < 0)
@@ -390,13 +400,15 @@ static int rsnd_soc_dai_trigger(struct snd_pcm_substream *substream, int cmd,
 		ret = rsnd_platform_call(priv, dai, stop, ssi_id);
 		if (ret < 0)
 			goto dai_trigger_end;
+
+		rsnd_dai_stream_quit(io);
 		break;
 	default:
 		ret = -EINVAL;
 	}
 
 dai_trigger_end:
-	rsnd_unlock(priv, flags);
+	spin_unlock_irqrestore(&priv->lock, flags);
 
 	return ret;
 }
@@ -833,12 +845,14 @@ static int __rsnd_kctrl_new(struct rsnd_mod *mod,
 			    struct rsnd_kctrl_cfg *cfg,
 			    void (*update)(struct rsnd_mod *mod))
 {
+	struct snd_soc_card *soc_card = rtd->card;
 	struct snd_card *card = rtd->card->snd_card;
 	struct snd_kcontrol *kctrl;
 	struct snd_kcontrol_new knew = {
 		.iface		= SNDRV_CTL_ELEM_IFACE_MIXER,
 		.name		= name,
 		.info		= rsnd_kctrl_info,
+		.index		= rtd - soc_card->rtd,
 		.get		= rsnd_kctrl_get,
 		.put		= rsnd_kctrl_put,
 		.private_value	= (unsigned long)cfg,
diff --git a/sound/soc/sh/rcar/rsnd.h b/sound/soc/sh/rcar/rsnd.h
index 4e6de6804cfb..03ff071d012f 100644
--- a/sound/soc/sh/rcar/rsnd.h
+++ b/sound/soc/sh/rcar/rsnd.h
@@ -303,6 +303,7 @@ int rsnd_mod_init(struct rsnd_mod *mod,
 		   int id);
 void rsnd_mod_quit(struct rsnd_mod *mod);
 char *rsnd_mod_name(struct rsnd_mod *mod);
+int rsnd_mod_is_working(struct rsnd_mod *mod);
 struct dma_chan *rsnd_mod_dma_req(struct rsnd_mod *mod);
 
 /*
@@ -449,8 +450,6 @@ struct rsnd_priv {
 #define rsnd_priv_to_pdev(priv)	((priv)->pdev)
 #define rsnd_priv_to_dev(priv)	(&(rsnd_priv_to_pdev(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)
 
 /*
  *	rsnd_kctrl
diff --git a/sound/soc/sh/rcar/rsrc-card.c b/sound/soc/sh/rcar/rsrc-card.c
index a68517afe615..050b0dbcee65 100644
--- a/sound/soc/sh/rcar/rsrc-card.c
+++ b/sound/soc/sh/rcar/rsrc-card.c
@@ -232,6 +232,7 @@ rsrc_card_sub_parse_of(struct rsrc_card_priv *priv,
 	if (args_count) {
 		*args_count = args.args_count;
 		dai_link->dynamic = 1;
+		dai_link->dpcm_merged_format = 1;
 	} else {
 		dai_link->no_pcm = 1;
 		priv->codec_conf.of_node = (*p_node);
diff --git a/sound/soc/sh/rcar/src.c b/sound/soc/sh/rcar/src.c
index 3beb32eb412a..fbe9166e26d1 100644
--- a/sound/soc/sh/rcar/src.c
+++ b/sound/soc/sh/rcar/src.c
@@ -673,10 +673,13 @@ static int _rsnd_src_stop_gen2(struct rsnd_mod *mod)
 static irqreturn_t rsnd_src_interrupt_gen2(int irq, void *data)
 {
 	struct rsnd_mod *mod = data;
-	struct rsnd_dai_stream *io = rsnd_mod_to_io(mod);
+	struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
+
+	spin_lock(&priv->lock);
 
-	if (!io)
-		return IRQ_NONE;
+	/* ignore all cases if not working */
+	if (!rsnd_mod_is_working(mod))
+		goto rsnd_src_interrupt_gen2_out;
 
 	if (rsnd_src_error_record_gen2(mod)) {
 		struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
@@ -692,6 +695,8 @@ static irqreturn_t rsnd_src_interrupt_gen2(int irq, void *data)
 		else
 			dev_warn(dev, "no more SRC restart\n");
 	}
+rsnd_src_interrupt_gen2_out:
+	spin_unlock(&priv->lock);
 
 	return IRQ_HANDLED;
 }
diff --git a/sound/soc/sh/rcar/ssi.c b/sound/soc/sh/rcar/ssi.c
index 7bb9c087f3dc..50fa3928a003 100644
--- a/sound/soc/sh/rcar/ssi.c
+++ b/sound/soc/sh/rcar/ssi.c
@@ -66,6 +66,7 @@ struct rsnd_ssi {
 
 	u32 cr_own;
 	u32 cr_clk;
+	int chan;
 	int err;
 	unsigned int usrcnt;
 };
@@ -80,7 +81,7 @@ struct rsnd_ssi {
 #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->irq > 0)
-#define rsnd_ssi_clk_from_parent(ssi) ((ssi)->parent)
+#define rsnd_ssi_parent(ssi) ((ssi)->parent)
 #define rsnd_ssi_mode_flags(p) ((p)->info->flags)
 #define rsnd_ssi_dai_id(ssi) ((ssi)->info->dai_id)
 #define rsnd_ssi_of_node(priv) \
@@ -189,8 +190,10 @@ static void rsnd_ssi_hw_start(struct rsnd_ssi *ssi,
 		rsnd_mod_hw_start(&ssi->mod);
 
 		if (rsnd_rdai_is_clk_master(rdai)) {
-			if (rsnd_ssi_clk_from_parent(ssi))
-				rsnd_ssi_hw_start(ssi->parent, io);
+			struct rsnd_ssi *ssi_parent = rsnd_ssi_parent(ssi);
+
+			if (ssi_parent)
+				rsnd_ssi_hw_start(ssi_parent, io);
 			else
 				rsnd_ssi_master_clk_start(ssi, io);
 		}
@@ -229,8 +232,10 @@ static void rsnd_ssi_hw_stop(struct rsnd_ssi *ssi)
 	struct device *dev = rsnd_priv_to_dev(priv);
 	u32 cr;
 
-	if (0 == ssi->usrcnt) /* stop might be called without start */
+	if (0 == ssi->usrcnt) {
+		dev_err(dev, "%s called without starting\n", __func__);
 		return;
+	}
 
 	ssi->usrcnt--;
 
@@ -253,13 +258,17 @@ static void rsnd_ssi_hw_stop(struct rsnd_ssi *ssi)
 		rsnd_ssi_status_check(&ssi->mod, IIRQ);
 
 		if (rsnd_rdai_is_clk_master(rdai)) {
-			if (rsnd_ssi_clk_from_parent(ssi))
-				rsnd_ssi_hw_stop(ssi->parent);
+			struct rsnd_ssi *ssi_parent = rsnd_ssi_parent(ssi);
+
+			if (ssi_parent)
+				rsnd_ssi_hw_stop(ssi_parent);
 			else
 				rsnd_ssi_master_clk_stop(ssi);
 		}
 
 		rsnd_mod_hw_stop(&ssi->mod);
+
+		ssi->chan = 0;
 	}
 
 	dev_dbg(dev, "%s[%d] hw stopped\n",
@@ -336,6 +345,35 @@ static int rsnd_ssi_quit(struct rsnd_mod *mod,
 	return 0;
 }
 
+static int rsnd_ssi_hw_params(struct rsnd_mod *mod,
+			      struct snd_pcm_substream *substream,
+			      struct snd_pcm_hw_params *params)
+{
+	struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
+	struct rsnd_ssi *ssi_parent = rsnd_ssi_parent(ssi);
+	int chan = params_channels(params);
+
+	/*
+	 * Already working.
+	 * It will happen if SSI has parent/child connection.
+	 */
+	if (ssi->usrcnt) {
+		/*
+		 * it is error if child <-> parent SSI uses
+		 * different channels.
+		 */
+		if (ssi->chan != chan)
+			return -EIO;
+	}
+
+	/* It will be removed on rsnd_ssi_hw_stop */
+	ssi->chan = chan;
+	if (ssi_parent)
+		return rsnd_ssi_hw_params(&ssi_parent->mod, substream, params);
+
+	return 0;
+}
+
 static void rsnd_ssi_record_error(struct rsnd_ssi *ssi, u32 status)
 {
 	/* under/over flow error */
@@ -385,10 +423,15 @@ static irqreturn_t rsnd_ssi_interrupt(int irq, void *data)
 	struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
 	struct rsnd_dai_stream *io = rsnd_mod_to_io(mod);
 	int is_dma = rsnd_ssi_is_dma_mode(mod);
-	u32 status = rsnd_mod_read(mod, SSISR);
+	u32 status;
+
+	spin_lock(&priv->lock);
 
-	if (!io)
-		return IRQ_NONE;
+	/* ignore all cases if not working */
+	if (!rsnd_mod_is_working(mod))
+		goto rsnd_ssi_interrupt_out;
+
+	status = rsnd_mod_read(mod, SSISR);
 
 	/* PIO only */
 	if (!is_dma && (status & DIRQ)) {
@@ -428,6 +471,9 @@ static irqreturn_t rsnd_ssi_interrupt(int irq, void *data)
 
 	rsnd_ssi_record_error(ssi, status);
 
+rsnd_ssi_interrupt_out:
+	spin_unlock(&priv->lock);
+
 	return IRQ_HANDLED;
 }
 
@@ -456,6 +502,7 @@ static struct rsnd_mod_ops rsnd_ssi_pio_ops = {
 	.quit	= rsnd_ssi_quit,
 	.start	= rsnd_ssi_start,
 	.stop	= rsnd_ssi_stop,
+	.hw_params = rsnd_ssi_hw_params,
 };
 
 static int rsnd_ssi_dma_probe(struct rsnd_mod *mod,
@@ -565,6 +612,7 @@ static struct rsnd_mod_ops rsnd_ssi_dma_ops = {
 	.start	= rsnd_ssi_dma_start,
 	.stop	= rsnd_ssi_dma_stop,
 	.fallback = rsnd_ssi_fallback,
+	.hw_params = rsnd_ssi_hw_params,
 };
 
 int rsnd_ssi_is_dma_mode(struct rsnd_mod *mod)
@@ -598,7 +646,7 @@ int rsnd_ssi_is_pin_sharing(struct rsnd_mod *mod)
 	return !!(rsnd_ssi_mode_flags(ssi) & RSND_SSI_CLK_PIN_SHARE);
 }
 
-static void rsnd_ssi_parent_clk_setup(struct rsnd_priv *priv, struct rsnd_ssi *ssi)
+static void rsnd_ssi_parent_setup(struct rsnd_priv *priv, struct rsnd_ssi *ssi)
 {
 	if (!rsnd_ssi_is_pin_sharing(&ssi->mod))
 		return;
@@ -732,7 +780,7 @@ int rsnd_ssi_probe(struct platform_device *pdev,
 		if (ret)
 			return ret;
 
-		rsnd_ssi_parent_clk_setup(priv, ssi);
+		rsnd_ssi_parent_setup(priv, ssi);
 	}
 
 	return 0;
diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c
index 23732523f87c..3a4a5c0e3f97 100644
--- a/sound/soc/soc-core.c
+++ b/sound/soc/soc-core.c
@@ -40,6 +40,7 @@
 #include <sound/pcm_params.h>
 #include <sound/soc.h>
 #include <sound/soc-dpcm.h>
+#include <sound/soc-topology.h>
 #include <sound/initval.h>
 
 #define CREATE_TRACE_POINTS
@@ -92,30 +93,21 @@ static int format_register_str(struct snd_soc_codec *codec,
 	int wordsize = min_bytes_needed(codec->driver->reg_cache_size) * 2;
 	int regsize = codec->driver->reg_word_size * 2;
 	int ret;
-	char tmpbuf[len + 1];
-	char regbuf[regsize + 1];
-
-	/* since tmpbuf is allocated on the stack, warn the callers if they
-	 * try to abuse this function */
-	WARN_ON(len > 63);
 
 	/* +2 for ': ' and + 1 for '\n' */
 	if (wordsize + regsize + 2 + 1 != len)
 		return -EINVAL;
 
-	ret = snd_soc_read(codec, reg);
-	if (ret < 0) {
-		memset(regbuf, 'X', regsize);
-		regbuf[regsize] = '\0';
-	} else {
-		snprintf(regbuf, regsize + 1, "%.*x", regsize, ret);
-	}
-
-	/* prepare the buffer */
-	snprintf(tmpbuf, len + 1, "%.*x: %s\n", wordsize, reg, regbuf);
-	/* copy it back to the caller without the '\0' */
-	memcpy(buf, tmpbuf, len);
+	sprintf(buf, "%.*x: ", wordsize, reg);
+	buf += wordsize + 2;
 
+	ret = snd_soc_read(codec, reg);
+	if (ret < 0)
+		memset(buf, 'X', regsize);
+	else
+		sprintf(buf, "%.*x", regsize, ret);
+	buf[regsize] = '\n';
+	/* no NUL-termination needed */
 	return 0;
 }
 
@@ -750,23 +742,10 @@ static void soc_resume_deferred(struct work_struct *work)
 	}
 
 	list_for_each_entry(codec, &card->codec_dev_list, card_list) {
-		/* If the CODEC was idle over suspend then it will have been
-		 * left with bias OFF or STANDBY and suspended so we must now
-		 * resume.  Otherwise the suspend was suppressed.
-		 */
 		if (codec->suspended) {
-			switch (codec->dapm.bias_level) {
-			case SND_SOC_BIAS_STANDBY:
-			case SND_SOC_BIAS_OFF:
-				if (codec->driver->resume)
-					codec->driver->resume(codec);
-				codec->suspended = 0;
-				break;
-			default:
-				dev_dbg(codec->dev,
-					"ASoC: CODEC was on over suspend\n");
-				break;
-			}
+			if (codec->driver->resume)
+				codec->driver->resume(codec);
+			codec->suspended = 0;
 		}
 	}
 
@@ -904,12 +883,17 @@ static struct snd_soc_dai *snd_soc_find_dai(
 {
 	struct snd_soc_component *component;
 	struct snd_soc_dai *dai;
+	struct device_node *component_of_node;
 
 	lockdep_assert_held(&client_mutex);
 
 	/* Find CPU DAI from registered DAIs*/
 	list_for_each_entry(component, &component_list, list) {
-		if (dlc->of_node && component->dev->of_node != dlc->of_node)
+		component_of_node = component->dev->of_node;
+		if (!component_of_node && component->dev->parent)
+			component_of_node = component->dev->parent->of_node;
+
+		if (dlc->of_node && component_of_node != dlc->of_node)
 			continue;
 		if (dlc->name && strcmp(component->name, dlc->name))
 			continue;
@@ -2435,6 +2419,7 @@ int snd_soc_register_card(struct snd_soc_card *card)
 		card->rtd_aux[i].card = card;
 
 	INIT_LIST_HEAD(&card->dapm_dirty);
+	INIT_LIST_HEAD(&card->dobj_list);
 	card->instantiated = 0;
 	mutex_init(&card->mutex);
 	mutex_init(&card->dapm_mutex);
@@ -2599,7 +2584,8 @@ static int snd_soc_register_dais(struct snd_soc_component *component,
 		 * the same naming style even though those DAIs are not
 		 * component-less anymore.
 		 */
-		if (count == 1 && legacy_dai_naming) {
+		if (count == 1 && legacy_dai_naming &&
+			(dai_drv[i].id == 0 || dai_drv[i].name == NULL)) {
 			dai->name = fmt_single_name(dev, &dai->id);
 		} else {
 			dai->name = fmt_multiple_name(dev, &dai_drv[i]);
@@ -2749,6 +2735,7 @@ static void snd_soc_component_add_unlocked(struct snd_soc_component *component)
 	}
 
 	list_add(&component->list, &component_list);
+	INIT_LIST_HEAD(&component->dobj_list);
 }
 
 static void snd_soc_component_add(struct snd_soc_component *component)
@@ -2825,6 +2812,7 @@ void snd_soc_unregister_component(struct device *dev)
 	return;
 
 found:
+	snd_soc_tplg_component_remove(cmpnt, SND_SOC_TPLG_INDEX_ALL);
 	snd_soc_component_del_unlocked(cmpnt);
 	mutex_unlock(&client_mutex);
 	snd_soc_component_cleanup(cmpnt);
@@ -3488,11 +3476,16 @@ static int snd_soc_get_dai_name(struct of_phandle_args *args,
 				const char **dai_name)
 {
 	struct snd_soc_component *pos;
+	struct device_node *component_of_node;
 	int ret = -EPROBE_DEFER;
 
 	mutex_lock(&client_mutex);
 	list_for_each_entry(pos, &component_list, list) {
-		if (pos->dev->of_node != args->np)
+		component_of_node = pos->dev->of_node;
+		if (!component_of_node && pos->dev->parent)
+			component_of_node = pos->dev->parent->of_node;
+
+		if (component_of_node != args->np)
 			continue;
 
 		if (pos->driver->of_xlate_dai_name) {
diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c
index 158204d08924..aa327c92480c 100644
--- a/sound/soc/soc-dapm.c
+++ b/sound/soc/soc-dapm.c
@@ -52,10 +52,15 @@ static int snd_soc_dapm_add_path(struct snd_soc_dapm_context *dapm,
 	const char *control,
 	int (*connected)(struct snd_soc_dapm_widget *source,
 			 struct snd_soc_dapm_widget *sink));
-static struct snd_soc_dapm_widget *
+
+struct snd_soc_dapm_widget *
 snd_soc_dapm_new_control(struct snd_soc_dapm_context *dapm,
 			 const struct snd_soc_dapm_widget *widget);
 
+struct snd_soc_dapm_widget *
+snd_soc_dapm_new_control_unlocked(struct snd_soc_dapm_context *dapm,
+			 const struct snd_soc_dapm_widget *widget);
+
 /* dapm power sequences - make this per codec in the future */
 static int dapm_up_seq[] = {
 	[snd_soc_dapm_pre] = 0,
@@ -70,6 +75,7 @@ static int dapm_up_seq[] = {
 	[snd_soc_dapm_aif_out] = 4,
 	[snd_soc_dapm_mic] = 5,
 	[snd_soc_dapm_mux] = 6,
+	[snd_soc_dapm_demux] = 6,
 	[snd_soc_dapm_dac] = 7,
 	[snd_soc_dapm_switch] = 8,
 	[snd_soc_dapm_mixer] = 8,
@@ -100,6 +106,7 @@ static int dapm_down_seq[] = {
 	[snd_soc_dapm_mic] = 7,
 	[snd_soc_dapm_micbias] = 8,
 	[snd_soc_dapm_mux] = 9,
+	[snd_soc_dapm_demux] = 9,
 	[snd_soc_dapm_aif_in] = 10,
 	[snd_soc_dapm_aif_out] = 10,
 	[snd_soc_dapm_dai_in] = 10,
@@ -308,14 +315,13 @@ static int dapm_kcontrol_data_alloc(struct snd_soc_dapm_widget *widget,
 {
 	struct dapm_kcontrol_data *data;
 	struct soc_mixer_control *mc;
+	struct soc_enum *e;
+	const char *name;
+	int ret;
 
 	data = kzalloc(sizeof(*data), GFP_KERNEL);
-	if (!data) {
-		dev_err(widget->dapm->dev,
-				"ASoC: can't allocate kcontrol data for %s\n",
-				widget->name);
+	if (!data)
 		return -ENOMEM;
-	}
 
 	INIT_LIST_HEAD(&data->paths);
 
@@ -328,6 +334,13 @@ static int dapm_kcontrol_data_alloc(struct snd_soc_dapm_widget *widget,
 		if (mc->autodisable) {
 			struct snd_soc_dapm_widget template;
 
+			name = kasprintf(GFP_KERNEL, "%s %s", kcontrol->id.name,
+					 "Autodisable");
+			if (!name) {
+				ret = -ENOMEM;
+				goto err_data;
+			}
+
 			memset(&template, 0, sizeof(template));
 			template.reg = mc->reg;
 			template.mask = (1 << fls(mc->max)) - 1;
@@ -338,16 +351,53 @@ static int dapm_kcontrol_data_alloc(struct snd_soc_dapm_widget *widget,
 				template.off_val = 0;
 			template.on_val = template.off_val;
 			template.id = snd_soc_dapm_kcontrol;
-			template.name = kcontrol->id.name;
+			template.name = name;
 
 			data->value = template.on_val;
 
-			data->widget = snd_soc_dapm_new_control(widget->dapm,
+			data->widget =
+				snd_soc_dapm_new_control_unlocked(widget->dapm,
 				&template);
 			if (!data->widget) {
-				kfree(data);
-				return -ENOMEM;
+				ret = -ENOMEM;
+				goto err_name;
+			}
+		}
+		break;
+	case snd_soc_dapm_demux:
+	case snd_soc_dapm_mux:
+		e = (struct soc_enum *)kcontrol->private_value;
+
+		if (e->autodisable) {
+			struct snd_soc_dapm_widget template;
+
+			name = kasprintf(GFP_KERNEL, "%s %s", kcontrol->id.name,
+					 "Autodisable");
+			if (!name) {
+				ret = -ENOMEM;
+				goto err_data;
 			}
+
+			memset(&template, 0, sizeof(template));
+			template.reg = e->reg;
+			template.mask = e->mask << e->shift_l;
+			template.shift = e->shift_l;
+			template.off_val = snd_soc_enum_item_to_val(e, 0);
+			template.on_val = template.off_val;
+			template.id = snd_soc_dapm_kcontrol;
+			template.name = name;
+
+			data->value = template.on_val;
+
+			data->widget = snd_soc_dapm_new_control(widget->dapm,
+					&template);
+			if (!data->widget) {
+				ret = -ENOMEM;
+				goto err_name;
+			}
+
+			snd_soc_dapm_add_path(widget->dapm, data->widget,
+					      widget, NULL, NULL);
 		}
 		break;
 	default:
@@ -357,11 +407,19 @@ static int dapm_kcontrol_data_alloc(struct snd_soc_dapm_widget *widget,
 	kcontrol->private_data = data;
 
 	return 0;
+
+err_name:
+	kfree(name);
+err_data:
+	kfree(data);
+	return ret;
 }
 
 static void dapm_kcontrol_free(struct snd_kcontrol *kctl)
 {
 	struct dapm_kcontrol_data *data = snd_kcontrol_chip(kctl);
+	if (data->widget)
+		kfree(data->widget->name);
 	kfree(data->wlist);
 	kfree(data);
 }
@@ -405,11 +463,6 @@ static void dapm_kcontrol_add_path(const struct snd_kcontrol *kcontrol,
 	struct dapm_kcontrol_data *data = snd_kcontrol_chip(kcontrol);
 
 	list_add_tail(&path->list_kcontrol, &data->paths);
-
-	if (data->widget) {
-		snd_soc_dapm_add_path(data->widget->dapm, data->widget,
-		    path->source, NULL, NULL);
-	}
 }
 
 static bool dapm_kcontrol_is_powered(const struct snd_kcontrol *kcontrol)
@@ -525,6 +578,67 @@ static void soc_dapm_async_complete(struct snd_soc_dapm_context *dapm)
 		snd_soc_component_async_complete(dapm->component);
 }
 
+static struct snd_soc_dapm_widget *
+dapm_wcache_lookup(struct snd_soc_dapm_wcache *wcache, const char *name)
+{
+	struct snd_soc_dapm_widget *w = wcache->widget;
+	struct list_head *wlist;
+	const int depth = 2;
+	int i = 0;
+
+	if (w) {
+		wlist = &w->dapm->card->widgets;
+
+		list_for_each_entry_from(w, wlist, list) {
+			if (!strcmp(name, w->name))
+				return w;
+
+			if (++i == depth)
+				break;
+		}
+	}
+
+	return NULL;
+}
+
+static inline void dapm_wcache_update(struct snd_soc_dapm_wcache *wcache,
+				      struct snd_soc_dapm_widget *w)
+{
+	wcache->widget = w;
+}
+
+/**
+ * snd_soc_dapm_force_bias_level() - Sets the DAPM bias level
+ * @dapm: The DAPM context for which to set the level
+ * @level: The level to set
+ *
+ * Forces the DAPM bias level to a specific state. It will call the bias level
+ * callback of DAPM context with the specified level. This will even happen if
+ * the context is already at the same level. Furthermore it will not go through
+ * the normal bias level sequencing, meaning any intermediate states between the
+ * current and the target state will not be entered.
+ *
+ * Note that the change in bias level is only temporary and the next time
+ * snd_soc_dapm_sync() is called the state will be set to the level as
+ * determined by the DAPM core. The function is mainly intended to be used to
+ * used during probe or resume from suspend to power up the device so
+ * initialization can be done, before the DAPM core takes over.
+ */
+int snd_soc_dapm_force_bias_level(struct snd_soc_dapm_context *dapm,
+	enum snd_soc_bias_level level)
+{
+	int ret = 0;
+
+	if (dapm->set_bias_level)
+		ret = dapm->set_bias_level(dapm, level);
+
+	if (ret == 0)
+		dapm->bias_level = level;
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(snd_soc_dapm_force_bias_level);
+
 /**
  * snd_soc_dapm_set_bias_level - set the bias level for the system
  * @dapm: DAPM context
@@ -547,10 +661,8 @@ static int snd_soc_dapm_set_bias_level(struct snd_soc_dapm_context *dapm,
 	if (ret != 0)
 		goto out;
 
-	if (dapm->set_bias_level)
-		ret = dapm->set_bias_level(dapm, level);
-	else if (!card || dapm != &card->dapm)
-		dapm->bias_level = level;
+	if (!card || dapm != &card->dapm)
+		ret = snd_soc_dapm_force_bias_level(dapm, level);
 
 	if (ret != 0)
 		goto out;
@@ -565,9 +677,10 @@ out:
 
 /* connect mux widget to its interconnecting audio paths */
 static int dapm_connect_mux(struct snd_soc_dapm_context *dapm,
-	struct snd_soc_dapm_path *path, const char *control_name)
+	struct snd_soc_dapm_path *path, const char *control_name,
+	struct snd_soc_dapm_widget *w)
 {
-	const struct snd_kcontrol_new *kcontrol = &path->sink->kcontrol_news[0];
+	const struct snd_kcontrol_new *kcontrol = &w->kcontrol_news[0];
 	struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
 	unsigned int val, item;
 	int i;
@@ -707,6 +820,7 @@ static int dapm_create_or_share_mixmux_kcontrol(struct snd_soc_dapm_widget *w,
 				wname_in_long_name = false;
 				kcname_in_long_name = true;
 				break;
+			case snd_soc_dapm_demux:
 			case snd_soc_dapm_mux:
 				wname_in_long_name = true;
 				kcname_in_long_name = false;
@@ -777,6 +891,7 @@ static int dapm_new_mixer(struct snd_soc_dapm_widget *w)
 {
 	int i, ret;
 	struct snd_soc_dapm_path *path;
+	struct dapm_kcontrol_data *data;
 
 	/* add kcontrol */
 	for (i = 0; i < w->num_kcontrols; i++) {
@@ -786,16 +901,20 @@ static int dapm_new_mixer(struct snd_soc_dapm_widget *w)
 			if (path->name != (char *)w->kcontrol_news[i].name)
 				continue;
 
-			if (w->kcontrols[i]) {
-				dapm_kcontrol_add_path(w->kcontrols[i], path);
-				continue;
+			if (!w->kcontrols[i]) {
+				ret = dapm_create_or_share_mixmux_kcontrol(w, i);
+				if (ret < 0)
+					return ret;
 			}
 
-			ret = dapm_create_or_share_mixmux_kcontrol(w, i);
-			if (ret < 0)
-				return ret;
-
 			dapm_kcontrol_add_path(w->kcontrols[i], path);
+
+			data = snd_kcontrol_chip(w->kcontrols[i]);
+			if (data->widget)
+				snd_soc_dapm_add_path(data->widget->dapm,
+						      data->widget,
+						      path->source,
+						      NULL, NULL);
 		}
 	}
 
@@ -807,17 +926,32 @@ static int dapm_new_mux(struct snd_soc_dapm_widget *w)
 {
 	struct snd_soc_dapm_context *dapm = w->dapm;
 	struct snd_soc_dapm_path *path;
+	struct list_head *paths;
+	const char *type;
 	int ret;
 
+	switch (w->id) {
+	case snd_soc_dapm_mux:
+		paths = &w->sources;
+		type = "mux";
+		break;
+	case snd_soc_dapm_demux:
+		paths = &w->sinks;
+		type = "demux";
+		break;
+	default:
+		return -EINVAL;
+	}
+
 	if (w->num_kcontrols != 1) {
 		dev_err(dapm->dev,
-			"ASoC: mux %s has incorrect number of controls\n",
+			"ASoC: %s %s has incorrect number of controls\n", type,
 			w->name);
 		return -EINVAL;
 	}
 
-	if (list_empty(&w->sources)) {
-		dev_err(dapm->dev, "ASoC: mux %s has no paths\n", w->name);
+	if (list_empty(paths)) {
+		dev_err(dapm->dev, "ASoC: %s %s has no paths\n", type, w->name);
 		return -EINVAL;
 	}
 
@@ -825,9 +959,16 @@ static int dapm_new_mux(struct snd_soc_dapm_widget *w)
 	if (ret < 0)
 		return ret;
 
-	list_for_each_entry(path, &w->sources, list_sink) {
-		if (path->name)
-			dapm_kcontrol_add_path(w->kcontrols[0], path);
+	if (w->id == snd_soc_dapm_mux) {
+		list_for_each_entry(path, &w->sources, list_sink) {
+			if (path->name)
+				dapm_kcontrol_add_path(w->kcontrols[0], path);
+		}
+	} else {
+		list_for_each_entry(path, &w->sinks, list_source) {
+			if (path->name)
+				dapm_kcontrol_add_path(w->kcontrols[0], path);
+		}
 	}
 
 	return 0;
@@ -2335,6 +2476,50 @@ static void dapm_update_widget_flags(struct snd_soc_dapm_widget *w)
 	}
 }
 
+static int snd_soc_dapm_check_dynamic_path(struct snd_soc_dapm_context *dapm,
+	struct snd_soc_dapm_widget *source, struct snd_soc_dapm_widget *sink,
+	const char *control)
+{
+	bool dynamic_source = false;
+	bool dynamic_sink = false;
+
+	if (!control)
+		return 0;
+
+	switch (source->id) {
+	case snd_soc_dapm_demux:
+		dynamic_source = true;
+		break;
+	default:
+		break;
+	}
+
+	switch (sink->id) {
+	case snd_soc_dapm_mux:
+	case snd_soc_dapm_switch:
+	case snd_soc_dapm_mixer:
+	case snd_soc_dapm_mixer_named_ctl:
+		dynamic_sink = true;
+		break;
+	default:
+		break;
+	}
+
+	if (dynamic_source && dynamic_sink) {
+		dev_err(dapm->dev,
+			"Direct connection between demux and mixer/mux not supported for path %s -> [%s] -> %s\n",
+			source->name, control, sink->name);
+		return -EINVAL;
+	} else if (!dynamic_source && !dynamic_sink) {
+		dev_err(dapm->dev,
+			"Control not supported for path %s -> [%s] -> %s\n",
+			source->name, control, sink->name);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
 static int snd_soc_dapm_add_path(struct snd_soc_dapm_context *dapm,
 	struct snd_soc_dapm_widget *wsource, struct snd_soc_dapm_widget *wsink,
 	const char *control,
@@ -2365,6 +2550,10 @@ static int snd_soc_dapm_add_path(struct snd_soc_dapm_context *dapm,
 		return -EINVAL;
 	}
 
+	ret = snd_soc_dapm_check_dynamic_path(dapm, wsource, wsink, control);
+	if (ret)
+		return ret;
+
 	path = kzalloc(sizeof(struct snd_soc_dapm_path), GFP_KERNEL);
 	if (!path)
 		return -ENOMEM;
@@ -2384,10 +2573,19 @@ static int snd_soc_dapm_add_path(struct snd_soc_dapm_context *dapm,
 	if (control == NULL) {
 		path->connect = 1;
 	} else {
-		/* connect dynamic paths */
+		switch (wsource->id) {
+		case snd_soc_dapm_demux:
+			ret = dapm_connect_mux(dapm, path, control, wsource);
+			if (ret)
+				goto err;
+			break;
+		default:
+			break;
+		}
+
 		switch (wsink->id) {
 		case snd_soc_dapm_mux:
-			ret = dapm_connect_mux(dapm, path, control);
+			ret = dapm_connect_mux(dapm, path, control, wsink);
 			if (ret != 0)
 				goto err;
 			break;
@@ -2399,11 +2597,7 @@ static int snd_soc_dapm_add_path(struct snd_soc_dapm_context *dapm,
 				goto err;
 			break;
 		default:
-			dev_err(dapm->dev,
-				"Control not supported for path %s -> [%s] -> %s\n",
-				wsource->name, control, wsink->name);
-			ret = -EINVAL;
-			goto err;
+			break;
 		}
 	}
 
@@ -2451,6 +2645,12 @@ static int snd_soc_dapm_add_route(struct snd_soc_dapm_context *dapm,
 		source = route->source;
 	}
 
+	wsource = dapm_wcache_lookup(&dapm->path_source_cache, source);
+	wsink = dapm_wcache_lookup(&dapm->path_sink_cache, sink);
+
+	if (wsink && wsource)
+		goto skip_search;
+
 	/*
 	 * find src and dest widgets over all widgets but favor a widget from
 	 * current DAPM context
@@ -2458,14 +2658,20 @@ static int snd_soc_dapm_add_route(struct snd_soc_dapm_context *dapm,
 	list_for_each_entry(w, &dapm->card->widgets, list) {
 		if (!wsink && !(strcmp(w->name, sink))) {
 			wtsink = w;
-			if (w->dapm == dapm)
+			if (w->dapm == dapm) {
 				wsink = w;
+				if (wsource)
+					break;
+			}
 			continue;
 		}
 		if (!wsource && !(strcmp(w->name, source))) {
 			wtsource = w;
-			if (w->dapm == dapm)
+			if (w->dapm == dapm) {
 				wsource = w;
+				if (wsink)
+					break;
+			}
 		}
 	}
 	/* use widget from another DAPM context if not found from this */
@@ -2485,6 +2691,10 @@ static int snd_soc_dapm_add_route(struct snd_soc_dapm_context *dapm,
 		return -ENODEV;
 	}
 
+skip_search:
+	dapm_wcache_update(&dapm->path_sink_cache, wsink);
+	dapm_wcache_update(&dapm->path_source_cache, wsource);
+
 	ret = snd_soc_dapm_add_path(dapm, wsource, wsink, route->control,
 		route->connected);
 	if (ret)
@@ -2736,6 +2946,7 @@ 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_demux:
 			dapm_new_mux(w);
 			break;
 		case snd_soc_dapm_pga:
@@ -2902,16 +3113,21 @@ int snd_soc_dapm_get_enum_double(struct snd_kcontrol *kcontrol,
 	struct snd_ctl_elem_value *ucontrol)
 {
 	struct snd_soc_dapm_context *dapm = snd_soc_dapm_kcontrol_dapm(kcontrol);
+	struct snd_soc_card *card = dapm->card;
 	struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
 	unsigned int reg_val, val;
 
-	if (e->reg != SND_SOC_NOPM) {
+	mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME);
+	if (e->reg != SND_SOC_NOPM && dapm_kcontrol_is_powered(kcontrol)) {
 		int ret = soc_dapm_read(dapm, e->reg, &reg_val);
-		if (ret)
+		if (ret) {
+			mutex_unlock(&card->dapm_mutex);
 			return ret;
+		}
 	} else {
 		reg_val = dapm_kcontrol_get_value(kcontrol);
 	}
+	mutex_unlock(&card->dapm_mutex);
 
 	val = (reg_val >> e->shift_l) & e->mask;
 	ucontrol->value.enumerated.item[0] = snd_soc_enum_val_to_item(e, val);
@@ -2941,7 +3157,7 @@ int snd_soc_dapm_put_enum_double(struct snd_kcontrol *kcontrol,
 	struct snd_soc_card *card = dapm->card;
 	struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
 	unsigned int *item = ucontrol->value.enumerated.item;
-	unsigned int val, change;
+	unsigned int val, change, reg_change = 0;
 	unsigned int mask;
 	struct snd_soc_dapm_update update;
 	int ret = 0;
@@ -2960,19 +3176,20 @@ int snd_soc_dapm_put_enum_double(struct snd_kcontrol *kcontrol,
 
 	mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME);
 
+	change = dapm_kcontrol_set_value(kcontrol, val);
+
 	if (e->reg != SND_SOC_NOPM)
-		change = soc_dapm_test_bits(dapm, e->reg, mask, val);
-	else
-		change = dapm_kcontrol_set_value(kcontrol, val);
+		reg_change = soc_dapm_test_bits(dapm, e->reg, mask, val);
 
-	if (change) {
-		if (e->reg != SND_SOC_NOPM) {
+	if (change || reg_change) {
+		if (reg_change) {
 			update.kcontrol = kcontrol;
 			update.reg = e->reg;
 			update.mask = mask;
 			update.val = val;
 			card->update = &update;
 		}
+		change |= reg_change;
 
 		ret = soc_dapm_mux_update_power(card, kcontrol, item[0], e);
 
@@ -3053,8 +3270,25 @@ int snd_soc_dapm_put_pin_switch(struct snd_kcontrol *kcontrol,
 }
 EXPORT_SYMBOL_GPL(snd_soc_dapm_put_pin_switch);
 
-static struct snd_soc_dapm_widget *
+struct snd_soc_dapm_widget *
 snd_soc_dapm_new_control(struct snd_soc_dapm_context *dapm,
+	const struct snd_soc_dapm_widget *widget)
+{
+	struct snd_soc_dapm_widget *w;
+
+	mutex_lock_nested(&dapm->card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME);
+	w = snd_soc_dapm_new_control_unlocked(dapm, widget);
+	if (!w)
+		dev_err(dapm->dev,
+			"ASoC: Failed to create DAPM control %s\n",
+			widget->name);
+
+	mutex_unlock(&dapm->card->dapm_mutex);
+	return w;
+}
+
+struct snd_soc_dapm_widget *
+snd_soc_dapm_new_control_unlocked(struct snd_soc_dapm_context *dapm,
 			 const struct snd_soc_dapm_widget *widget)
 {
 	struct snd_soc_dapm_widget *w;
@@ -3141,6 +3375,7 @@ snd_soc_dapm_new_control(struct snd_soc_dapm_context *dapm,
 		w->power_check = dapm_always_on_check_power;
 		break;
 	case snd_soc_dapm_mux:
+	case snd_soc_dapm_demux:
 	case snd_soc_dapm_switch:
 	case snd_soc_dapm_mixer:
 	case snd_soc_dapm_mixer_named_ctl:
@@ -3174,7 +3409,7 @@ snd_soc_dapm_new_control(struct snd_soc_dapm_context *dapm,
 	INIT_LIST_HEAD(&w->sinks);
 	INIT_LIST_HEAD(&w->list);
 	INIT_LIST_HEAD(&w->dirty);
-	list_add(&w->list, &dapm->card->widgets);
+	list_add_tail(&w->list, &dapm->card->widgets);
 
 	w->inputs = -1;
 	w->outputs = -1;
@@ -3204,7 +3439,7 @@ int snd_soc_dapm_new_controls(struct snd_soc_dapm_context *dapm,
 
 	mutex_lock_nested(&dapm->card->dapm_mutex, SND_SOC_DAPM_CLASS_INIT);
 	for (i = 0; i < num; i++) {
-		w = snd_soc_dapm_new_control(dapm, widget);
+		w = snd_soc_dapm_new_control_unlocked(dapm, widget);
 		if (!w) {
 			dev_err(dapm->dev,
 				"ASoC: Failed to create DAPM control %s\n",
@@ -3442,7 +3677,7 @@ int snd_soc_dapm_new_pcm(struct snd_soc_card *card,
 
 	dev_dbg(card->dev, "ASoC: adding %s widget\n", link_name);
 
-	w = snd_soc_dapm_new_control(&card->dapm, &template);
+	w = snd_soc_dapm_new_control_unlocked(&card->dapm, &template);
 	if (!w) {
 		dev_err(card->dev, "ASoC: Failed to create %s widget\n",
 			link_name);
@@ -3493,7 +3728,7 @@ int snd_soc_dapm_new_dai_widgets(struct snd_soc_dapm_context *dapm,
 		dev_dbg(dai->dev, "ASoC: adding %s widget\n",
 			template.name);
 
-		w = snd_soc_dapm_new_control(dapm, &template);
+		w = snd_soc_dapm_new_control_unlocked(dapm, &template);
 		if (!w) {
 			dev_err(dapm->dev, "ASoC: Failed to create %s widget\n",
 				dai->driver->playback.stream_name);
@@ -3512,7 +3747,7 @@ int snd_soc_dapm_new_dai_widgets(struct snd_soc_dapm_context *dapm,
 		dev_dbg(dai->dev, "ASoC: adding %s widget\n",
 			template.name);
 
-		w = snd_soc_dapm_new_control(dapm, &template);
+		w = snd_soc_dapm_new_control_unlocked(dapm, &template);
 		if (!w) {
 			dev_err(dapm->dev, "ASoC: Failed to create %s widget\n",
 				dai->driver->capture.stream_name);
diff --git a/sound/soc/soc-generic-dmaengine-pcm.c b/sound/soc/soc-generic-dmaengine-pcm.c
index c9917ca5de1a..6fd1906af387 100644
--- a/sound/soc/soc-generic-dmaengine-pcm.c
+++ b/sound/soc/soc-generic-dmaengine-pcm.c
@@ -24,6 +24,12 @@
 
 #include <sound/dmaengine_pcm.h>
 
+/*
+ * The platforms dmaengine driver does not support reporting the amount of
+ * bytes that are still left to transfer.
+ */
+#define SND_DMAENGINE_PCM_FLAG_NO_RESIDUE BIT(31)
+
 struct dmaengine_pcm {
 	struct dma_chan *chan[SNDRV_PCM_STREAM_LAST + 1];
 	const struct snd_dmaengine_pcm_config *config;
@@ -222,14 +228,18 @@ static struct dma_chan *dmaengine_pcm_compat_request_channel(
 	return snd_dmaengine_pcm_request_channel(fn, dma_data->filter_data);
 }
 
-static bool dmaengine_pcm_can_report_residue(struct dma_chan *chan)
+static bool dmaengine_pcm_can_report_residue(struct device *dev,
+	struct dma_chan *chan)
 {
 	struct dma_slave_caps dma_caps;
 	int ret;
 
 	ret = dma_get_slave_caps(chan, &dma_caps);
-	if (ret != 0)
-		return true;
+	if (ret != 0) {
+		dev_warn(dev, "Failed to get DMA channel capabilities, falling back to period counting: %d\n",
+			 ret);
+		return false;
+	}
 
 	if (dma_caps.residue_granularity == DMA_RESIDUE_GRANULARITY_DESCRIPTOR)
 		return false;
@@ -289,14 +299,7 @@ static int dmaengine_pcm_new(struct snd_soc_pcm_runtime *rtd)
 		if (ret)
 			return ret;
 
-		/*
-		 * This will only return false if we know for sure that at least
-		 * one channel does not support residue reporting. If the DMA
-		 * driver does not implement the slave_caps API we rely having
-		 * the NO_RESIDUE flag set manually in case residue reporting is
-		 * not supported.
-		 */
-		if (!dmaengine_pcm_can_report_residue(pcm->chan[i]))
+		if (!dmaengine_pcm_can_report_residue(dev, pcm->chan[i]))
 			pcm->flags |= SND_DMAENGINE_PCM_FLAG_NO_RESIDUE;
 	}
 
diff --git a/sound/soc/soc-jack.c b/sound/soc/soc-jack.c
index 9f60c25c4568..171c4291ea21 100644
--- a/sound/soc/soc-jack.c
+++ b/sound/soc/soc-jack.c
@@ -315,8 +315,11 @@ int snd_soc_jack_add_gpios(struct snd_soc_jack *jack, int count,
 			goto undo;
 		}
 
-		if (gpios[i].gpiod_dev) {
-			/* GPIO descriptor */
+		if (gpios[i].desc) {
+			/* Already have a GPIO descriptor. */
+			goto got_gpio;
+		} else if (gpios[i].gpiod_dev) {
+			/* Get a GPIO descriptor */
 			gpios[i].desc = gpiod_get_index(gpios[i].gpiod_dev,
 							gpios[i].name,
 							gpios[i].idx, GPIOD_IN);
@@ -344,7 +347,7 @@ int snd_soc_jack_add_gpios(struct snd_soc_jack *jack, int count,
 
 			gpios[i].desc = gpio_to_desc(gpios[i].gpio);
 		}
-
+got_gpio:
 		INIT_DELAYED_WORK(&gpios[i].work, gpio_work);
 		gpios[i].jack = jack;
 
diff --git a/sound/soc/soc-pcm.c b/sound/soc/soc-pcm.c
index 35fe58f4fa86..256b9c91aa94 100644
--- a/sound/soc/soc-pcm.c
+++ b/sound/soc/soc-pcm.c
@@ -1485,30 +1485,67 @@ unwind:
 }
 
 static void dpcm_init_runtime_hw(struct snd_pcm_runtime *runtime,
-	struct snd_soc_pcm_stream *stream)
+				 struct snd_soc_pcm_stream *stream,
+				 u64 formats)
 {
 	runtime->hw.rate_min = stream->rate_min;
 	runtime->hw.rate_max = stream->rate_max;
 	runtime->hw.channels_min = stream->channels_min;
 	runtime->hw.channels_max = stream->channels_max;
 	if (runtime->hw.formats)
-		runtime->hw.formats &= stream->formats;
+		runtime->hw.formats &= formats & stream->formats;
 	else
-		runtime->hw.formats = stream->formats;
+		runtime->hw.formats = formats & stream->formats;
 	runtime->hw.rates = stream->rates;
 }
 
+static u64 dpcm_runtime_base_format(struct snd_pcm_substream *substream)
+{
+	struct snd_soc_pcm_runtime *fe = substream->private_data;
+	struct snd_soc_dpcm *dpcm;
+	u64 formats = ULLONG_MAX;
+	int stream = substream->stream;
+
+	if (!fe->dai_link->dpcm_merged_format)
+		return formats;
+
+	/*
+	 * It returns merged BE codec format
+	 * if FE want to use it (= dpcm_merged_format)
+	 */
+
+	list_for_each_entry(dpcm, &fe->dpcm[stream].be_clients, list_be) {
+		struct snd_soc_pcm_runtime *be = dpcm->be;
+		struct snd_soc_dai_driver *codec_dai_drv;
+		struct snd_soc_pcm_stream *codec_stream;
+		int i;
+
+		for (i = 0; i < be->num_codecs; i++) {
+			codec_dai_drv = be->codec_dais[i]->driver;
+			if (stream == SNDRV_PCM_STREAM_PLAYBACK)
+				codec_stream = &codec_dai_drv->playback;
+			else
+				codec_stream = &codec_dai_drv->capture;
+
+			formats &= codec_stream->formats;
+		}
+	}
+
+	return formats;
+}
+
 static void dpcm_set_fe_runtime(struct snd_pcm_substream *substream)
 {
 	struct snd_pcm_runtime *runtime = substream->runtime;
 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
 	struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
 	struct snd_soc_dai_driver *cpu_dai_drv = cpu_dai->driver;
+	u64 format = dpcm_runtime_base_format(substream);
 
 	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
-		dpcm_init_runtime_hw(runtime, &cpu_dai_drv->playback);
+		dpcm_init_runtime_hw(runtime, &cpu_dai_drv->playback, format);
 	else
-		dpcm_init_runtime_hw(runtime, &cpu_dai_drv->capture);
+		dpcm_init_runtime_hw(runtime, &cpu_dai_drv->capture, format);
 }
 
 static int dpcm_fe_dai_do_trigger(struct snd_pcm_substream *substream, int cmd);
diff --git a/sound/soc/soc-topology.c b/sound/soc/soc-topology.c
new file mode 100644
index 000000000000..d0960683c409
--- /dev/null
+++ b/sound/soc/soc-topology.c
@@ -0,0 +1,1826 @@
+/*
+ * soc-topology.c  --  ALSA SoC Topology
+ *
+ * Copyright (C) 2012 Texas Instruments Inc.
+ * Copyright (C) 2015 Intel Corporation.
+ *
+ * Authors: Liam Girdwood <liam.r.girdwood@linux.intel.com>
+ *		K, Mythri P <mythri.p.k@intel.com>
+ *		Prusty, Subhransu S <subhransu.s.prusty@intel.com>
+ *		B, Jayachandran <jayachandran.b@intel.com>
+ *		Abdullah, Omair M <omair.m.abdullah@intel.com>
+ *		Jin, Yao <yao.jin@intel.com>
+ *		Lin, Mengdong <mengdong.lin@intel.com>
+ *
+ *  This program is free software; you can redistribute  it and/or modify it
+ *  under  the terms of  the GNU General  Public License as published by the
+ *  Free Software Foundation;  either version 2 of the  License, or (at your
+ *  option) any later version.
+ *
+ *  Add support to read audio firmware topology alongside firmware text. The
+ *  topology data can contain kcontrols, DAPM graphs, widgets, DAIs, DAI links,
+ *  equalizers, firmware, coefficients etc.
+ *
+ *  This file only manages the core ALSA and ASoC components, all other bespoke
+ *  firmware topology data is passed to component drivers for bespoke handling.
+ */
+
+#include <linux/kernel.h>
+#include <linux/export.h>
+#include <linux/list.h>
+#include <linux/firmware.h>
+#include <linux/slab.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/soc-topology.h>
+
+/*
+ * We make several passes over the data (since it wont necessarily be ordered)
+ * and process objects in the following order. This guarantees the component
+ * drivers will be ready with any vendor data before the mixers and DAPM objects
+ * are loaded (that may make use of the vendor data).
+ */
+#define SOC_TPLG_PASS_MANIFEST		0
+#define SOC_TPLG_PASS_VENDOR		1
+#define SOC_TPLG_PASS_MIXER		2
+#define SOC_TPLG_PASS_WIDGET		3
+#define SOC_TPLG_PASS_GRAPH		4
+#define SOC_TPLG_PASS_PINS		5
+#define SOC_TPLG_PASS_PCM_DAI		6
+
+#define SOC_TPLG_PASS_START	SOC_TPLG_PASS_MANIFEST
+#define SOC_TPLG_PASS_END	SOC_TPLG_PASS_PCM_DAI
+
+struct soc_tplg {
+	const struct firmware *fw;
+
+	/* runtime FW parsing */
+	const u8 *pos;		/* read postion */
+	const u8 *hdr_pos;	/* header position */
+	unsigned int pass;	/* pass number */
+
+	/* component caller */
+	struct device *dev;
+	struct snd_soc_component *comp;
+	u32 index;	/* current block index */
+	u32 req_index;	/* required index, only loaded/free matching blocks */
+
+	/* kcontrol operations */
+	const struct snd_soc_tplg_kcontrol_ops *io_ops;
+	int io_ops_count;
+
+	/* optional fw loading callbacks to component drivers */
+	struct snd_soc_tplg_ops *ops;
+};
+
+static int soc_tplg_process_headers(struct soc_tplg *tplg);
+static void soc_tplg_complete(struct soc_tplg *tplg);
+struct snd_soc_dapm_widget *
+snd_soc_dapm_new_control_unlocked(struct snd_soc_dapm_context *dapm,
+			 const struct snd_soc_dapm_widget *widget);
+struct snd_soc_dapm_widget *
+snd_soc_dapm_new_control(struct snd_soc_dapm_context *dapm,
+			 const struct snd_soc_dapm_widget *widget);
+
+/* check we dont overflow the data for this control chunk */
+static int soc_tplg_check_elem_count(struct soc_tplg *tplg, size_t elem_size,
+	unsigned int count, size_t bytes, const char *elem_type)
+{
+	const u8 *end = tplg->pos + elem_size * count;
+
+	if (end > tplg->fw->data + tplg->fw->size) {
+		dev_err(tplg->dev, "ASoC: %s overflow end of data\n",
+			elem_type);
+		return -EINVAL;
+	}
+
+	/* check there is enough room in chunk for control.
+	   extra bytes at the end of control are for vendor data here  */
+	if (elem_size * count > bytes) {
+		dev_err(tplg->dev,
+			"ASoC: %s count %d of size %zu is bigger than chunk %zu\n",
+			elem_type, count, elem_size, bytes);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static inline int soc_tplg_is_eof(struct soc_tplg *tplg)
+{
+	const u8 *end = tplg->hdr_pos;
+
+	if (end >= tplg->fw->data + tplg->fw->size)
+		return 1;
+	return 0;
+}
+
+static inline unsigned long soc_tplg_get_hdr_offset(struct soc_tplg *tplg)
+{
+	return (unsigned long)(tplg->hdr_pos - tplg->fw->data);
+}
+
+static inline unsigned long soc_tplg_get_offset(struct soc_tplg *tplg)
+{
+	return (unsigned long)(tplg->pos - tplg->fw->data);
+}
+
+/* mapping of Kcontrol types and associated operations. */
+static const struct snd_soc_tplg_kcontrol_ops io_ops[] = {
+	{SND_SOC_TPLG_CTL_VOLSW, snd_soc_get_volsw,
+		snd_soc_put_volsw, snd_soc_info_volsw},
+	{SND_SOC_TPLG_CTL_VOLSW_SX, snd_soc_get_volsw_sx,
+		snd_soc_put_volsw_sx, NULL},
+	{SND_SOC_TPLG_CTL_ENUM, snd_soc_get_enum_double,
+		snd_soc_put_enum_double, snd_soc_info_enum_double},
+	{SND_SOC_TPLG_CTL_ENUM_VALUE, snd_soc_get_enum_double,
+		snd_soc_put_enum_double, NULL},
+	{SND_SOC_TPLG_CTL_BYTES, snd_soc_bytes_get,
+		snd_soc_bytes_put, snd_soc_bytes_info},
+	{SND_SOC_TPLG_CTL_RANGE, snd_soc_get_volsw_range,
+		snd_soc_put_volsw_range, snd_soc_info_volsw_range},
+	{SND_SOC_TPLG_CTL_VOLSW_XR_SX, snd_soc_get_xr_sx,
+		snd_soc_put_xr_sx, snd_soc_info_xr_sx},
+	{SND_SOC_TPLG_CTL_STROBE, snd_soc_get_strobe,
+		snd_soc_put_strobe, NULL},
+	{SND_SOC_TPLG_DAPM_CTL_VOLSW, snd_soc_dapm_get_volsw,
+		snd_soc_dapm_put_volsw, NULL},
+	{SND_SOC_TPLG_DAPM_CTL_ENUM_DOUBLE, snd_soc_dapm_get_enum_double,
+		snd_soc_dapm_put_enum_double, snd_soc_info_enum_double},
+	{SND_SOC_TPLG_DAPM_CTL_ENUM_VIRT, snd_soc_dapm_get_enum_double,
+		snd_soc_dapm_put_enum_double, NULL},
+	{SND_SOC_TPLG_DAPM_CTL_ENUM_VALUE, snd_soc_dapm_get_enum_double,
+		snd_soc_dapm_put_enum_double, NULL},
+	{SND_SOC_TPLG_DAPM_CTL_PIN, snd_soc_dapm_get_pin_switch,
+		snd_soc_dapm_put_pin_switch, snd_soc_dapm_info_pin_switch},
+};
+
+struct soc_tplg_map {
+	int uid;
+	int kid;
+};
+
+/* mapping of widget types from UAPI IDs to kernel IDs */
+static const struct soc_tplg_map dapm_map[] = {
+	{SND_SOC_TPLG_DAPM_INPUT, snd_soc_dapm_input},
+	{SND_SOC_TPLG_DAPM_OUTPUT, snd_soc_dapm_output},
+	{SND_SOC_TPLG_DAPM_MUX, snd_soc_dapm_mux},
+	{SND_SOC_TPLG_DAPM_MIXER, snd_soc_dapm_mixer},
+	{SND_SOC_TPLG_DAPM_PGA, snd_soc_dapm_pga},
+	{SND_SOC_TPLG_DAPM_OUT_DRV, snd_soc_dapm_out_drv},
+	{SND_SOC_TPLG_DAPM_ADC, snd_soc_dapm_adc},
+	{SND_SOC_TPLG_DAPM_DAC, snd_soc_dapm_dac},
+	{SND_SOC_TPLG_DAPM_SWITCH, snd_soc_dapm_switch},
+	{SND_SOC_TPLG_DAPM_PRE, snd_soc_dapm_pre},
+	{SND_SOC_TPLG_DAPM_POST, snd_soc_dapm_post},
+	{SND_SOC_TPLG_DAPM_AIF_IN, snd_soc_dapm_aif_in},
+	{SND_SOC_TPLG_DAPM_AIF_OUT, snd_soc_dapm_aif_out},
+	{SND_SOC_TPLG_DAPM_DAI_IN, snd_soc_dapm_dai_in},
+	{SND_SOC_TPLG_DAPM_DAI_OUT, snd_soc_dapm_dai_out},
+	{SND_SOC_TPLG_DAPM_DAI_LINK, snd_soc_dapm_dai_link},
+};
+
+static int tplc_chan_get_reg(struct soc_tplg *tplg,
+	struct snd_soc_tplg_channel *chan, int map)
+{
+	int i;
+
+	for (i = 0; i < SND_SOC_TPLG_MAX_CHAN; i++) {
+		if (chan[i].id == map)
+			return chan[i].reg;
+	}
+
+	return -EINVAL;
+}
+
+static int tplc_chan_get_shift(struct soc_tplg *tplg,
+	struct snd_soc_tplg_channel *chan, int map)
+{
+	int i;
+
+	for (i = 0; i < SND_SOC_TPLG_MAX_CHAN; i++) {
+		if (chan[i].id == map)
+			return chan[i].shift;
+	}
+
+	return -EINVAL;
+}
+
+static int get_widget_id(int tplg_type)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(dapm_map); i++) {
+		if (tplg_type == dapm_map[i].uid)
+			return dapm_map[i].kid;
+	}
+
+	return -EINVAL;
+}
+
+static enum snd_soc_dobj_type get_dobj_mixer_type(
+	struct snd_soc_tplg_ctl_hdr *control_hdr)
+{
+	if (control_hdr == NULL)
+		return SND_SOC_DOBJ_NONE;
+
+	switch (control_hdr->ops.info) {
+	case SND_SOC_TPLG_CTL_VOLSW:
+	case SND_SOC_TPLG_CTL_VOLSW_SX:
+	case SND_SOC_TPLG_CTL_VOLSW_XR_SX:
+	case SND_SOC_TPLG_CTL_RANGE:
+	case SND_SOC_TPLG_CTL_STROBE:
+		return SND_SOC_DOBJ_MIXER;
+	case SND_SOC_TPLG_CTL_ENUM:
+	case SND_SOC_TPLG_CTL_ENUM_VALUE:
+		return SND_SOC_DOBJ_ENUM;
+	case SND_SOC_TPLG_CTL_BYTES:
+		return SND_SOC_DOBJ_BYTES;
+	default:
+		return SND_SOC_DOBJ_NONE;
+	}
+}
+
+static enum snd_soc_dobj_type get_dobj_type(struct snd_soc_tplg_hdr *hdr,
+	struct snd_soc_tplg_ctl_hdr *control_hdr)
+{
+	switch (hdr->type) {
+	case SND_SOC_TPLG_TYPE_MIXER:
+		return get_dobj_mixer_type(control_hdr);
+	case SND_SOC_TPLG_TYPE_DAPM_GRAPH:
+	case SND_SOC_TPLG_TYPE_MANIFEST:
+		return SND_SOC_DOBJ_NONE;
+	case SND_SOC_TPLG_TYPE_DAPM_WIDGET:
+		return SND_SOC_DOBJ_WIDGET;
+	case SND_SOC_TPLG_TYPE_DAI_LINK:
+		return SND_SOC_DOBJ_DAI_LINK;
+	case SND_SOC_TPLG_TYPE_PCM:
+		return SND_SOC_DOBJ_PCM;
+	case SND_SOC_TPLG_TYPE_CODEC_LINK:
+		return SND_SOC_DOBJ_CODEC_LINK;
+	default:
+		return SND_SOC_DOBJ_NONE;
+	}
+}
+
+static inline void soc_bind_err(struct soc_tplg *tplg,
+	struct snd_soc_tplg_ctl_hdr *hdr, int index)
+{
+	dev_err(tplg->dev,
+		"ASoC: invalid control type (g,p,i) %d:%d:%d index %d at 0x%lx\n",
+		hdr->ops.get, hdr->ops.put, hdr->ops.info, index,
+		soc_tplg_get_offset(tplg));
+}
+
+static inline void soc_control_err(struct soc_tplg *tplg,
+	struct snd_soc_tplg_ctl_hdr *hdr, const char *name)
+{
+	dev_err(tplg->dev,
+		"ASoC: no complete mixer IO handler for %s type (g,p,i) %d:%d:%d at 0x%lx\n",
+		name, hdr->ops.get, hdr->ops.put, hdr->ops.info,
+		soc_tplg_get_offset(tplg));
+}
+
+/* pass vendor data to component driver for processing */
+static int soc_tplg_vendor_load_(struct soc_tplg *tplg,
+	struct snd_soc_tplg_hdr *hdr)
+{
+	int ret = 0;
+
+	if (tplg->comp && tplg->ops && tplg->ops->vendor_load)
+		ret = tplg->ops->vendor_load(tplg->comp, hdr);
+	else {
+		dev_err(tplg->dev, "ASoC: no vendor load callback for ID %d\n",
+			hdr->vendor_type);
+		return -EINVAL;
+	}
+
+	if (ret < 0)
+		dev_err(tplg->dev,
+			"ASoC: vendor load failed at hdr offset %ld/0x%lx for type %d:%d\n",
+			soc_tplg_get_hdr_offset(tplg),
+			soc_tplg_get_hdr_offset(tplg),
+			hdr->type, hdr->vendor_type);
+	return ret;
+}
+
+/* pass vendor data to component driver for processing */
+static int soc_tplg_vendor_load(struct soc_tplg *tplg,
+	struct snd_soc_tplg_hdr *hdr)
+{
+	if (tplg->pass != SOC_TPLG_PASS_VENDOR)
+		return 0;
+
+	return soc_tplg_vendor_load_(tplg, hdr);
+}
+
+/* optionally pass new dynamic widget to component driver. This is mainly for
+ * external widgets where we can assign private data/ops */
+static int soc_tplg_widget_load(struct soc_tplg *tplg,
+	struct snd_soc_dapm_widget *w, struct snd_soc_tplg_dapm_widget *tplg_w)
+{
+	if (tplg->comp && tplg->ops && tplg->ops->widget_load)
+		return tplg->ops->widget_load(tplg->comp, w, tplg_w);
+
+	return 0;
+}
+
+/* pass dynamic FEs configurations to component driver */
+static int soc_tplg_pcm_dai_load(struct soc_tplg *tplg,
+	struct snd_soc_tplg_pcm_dai *pcm_dai, int num_pcm_dai)
+{
+	if (tplg->comp && tplg->ops && tplg->ops->pcm_dai_load)
+		return tplg->ops->pcm_dai_load(tplg->comp, pcm_dai, num_pcm_dai);
+
+	return 0;
+}
+
+/* tell the component driver that all firmware has been loaded in this request */
+static void soc_tplg_complete(struct soc_tplg *tplg)
+{
+	if (tplg->comp && tplg->ops && tplg->ops->complete)
+		tplg->ops->complete(tplg->comp);
+}
+
+/* add a dynamic kcontrol */
+static int soc_tplg_add_dcontrol(struct snd_card *card, struct device *dev,
+	const struct snd_kcontrol_new *control_new, const char *prefix,
+	void *data, struct snd_kcontrol **kcontrol)
+{
+	int err;
+
+	*kcontrol = snd_soc_cnew(control_new, data, control_new->name, prefix);
+	if (*kcontrol == NULL) {
+		dev_err(dev, "ASoC: Failed to create new kcontrol %s\n",
+		control_new->name);
+		return -ENOMEM;
+	}
+
+	err = snd_ctl_add(card, *kcontrol);
+	if (err < 0) {
+		dev_err(dev, "ASoC: Failed to add %s: %d\n",
+			control_new->name, err);
+		return err;
+	}
+
+	return 0;
+}
+
+/* add a dynamic kcontrol for component driver */
+static int soc_tplg_add_kcontrol(struct soc_tplg *tplg,
+	struct snd_kcontrol_new *k, struct snd_kcontrol **kcontrol)
+{
+	struct snd_soc_component *comp = tplg->comp;
+
+	return soc_tplg_add_dcontrol(comp->card->snd_card,
+				comp->dev, k, NULL, comp, kcontrol);
+}
+
+/* remove a mixer kcontrol */
+static void remove_mixer(struct snd_soc_component *comp,
+	struct snd_soc_dobj *dobj, int pass)
+{
+	struct snd_card *card = comp->card->snd_card;
+	struct soc_mixer_control *sm =
+		container_of(dobj, struct soc_mixer_control, dobj);
+	const unsigned int *p = NULL;
+
+	if (pass != SOC_TPLG_PASS_MIXER)
+		return;
+
+	if (dobj->ops && dobj->ops->control_unload)
+		dobj->ops->control_unload(comp, dobj);
+
+	if (sm->dobj.control.kcontrol->tlv.p)
+		p = sm->dobj.control.kcontrol->tlv.p;
+	snd_ctl_remove(card, sm->dobj.control.kcontrol);
+	list_del(&sm->dobj.list);
+	kfree(sm);
+	kfree(p);
+}
+
+/* remove an enum kcontrol */
+static void remove_enum(struct snd_soc_component *comp,
+	struct snd_soc_dobj *dobj, int pass)
+{
+	struct snd_card *card = comp->card->snd_card;
+	struct soc_enum *se = container_of(dobj, struct soc_enum, dobj);
+	int i;
+
+	if (pass != SOC_TPLG_PASS_MIXER)
+		return;
+
+	if (dobj->ops && dobj->ops->control_unload)
+		dobj->ops->control_unload(comp, dobj);
+
+	snd_ctl_remove(card, se->dobj.control.kcontrol);
+	list_del(&se->dobj.list);
+
+	kfree(se->dobj.control.dvalues);
+	for (i = 0; i < se->items; i++)
+		kfree(se->dobj.control.dtexts[i]);
+	kfree(se);
+}
+
+/* remove a byte kcontrol */
+static void remove_bytes(struct snd_soc_component *comp,
+	struct snd_soc_dobj *dobj, int pass)
+{
+	struct snd_card *card = comp->card->snd_card;
+	struct soc_bytes_ext *sb =
+		container_of(dobj, struct soc_bytes_ext, dobj);
+
+	if (pass != SOC_TPLG_PASS_MIXER)
+		return;
+
+	if (dobj->ops && dobj->ops->control_unload)
+		dobj->ops->control_unload(comp, dobj);
+
+	snd_ctl_remove(card, sb->dobj.control.kcontrol);
+	list_del(&sb->dobj.list);
+	kfree(sb);
+}
+
+/* remove a widget and it's kcontrols - routes must be removed first */
+static void remove_widget(struct snd_soc_component *comp,
+	struct snd_soc_dobj *dobj, int pass)
+{
+	struct snd_card *card = comp->card->snd_card;
+	struct snd_soc_dapm_widget *w =
+		container_of(dobj, struct snd_soc_dapm_widget, dobj);
+	int i;
+
+	if (pass != SOC_TPLG_PASS_WIDGET)
+		return;
+
+	if (dobj->ops && dobj->ops->widget_unload)
+		dobj->ops->widget_unload(comp, dobj);
+
+	/*
+	 * Dynamic Widgets either have 1 enum kcontrol or 1..N mixers.
+	 * The enum may either have an array of values or strings.
+	 */
+	if (dobj->widget.kcontrol_enum) {
+		/* enumerated widget mixer */
+		struct soc_enum *se =
+			(struct soc_enum *)w->kcontrols[0]->private_value;
+
+		snd_ctl_remove(card, w->kcontrols[0]);
+
+		kfree(se->dobj.control.dvalues);
+		for (i = 0; i < se->items; i++)
+			kfree(se->dobj.control.dtexts[i]);
+
+		kfree(se);
+		kfree(w->kcontrol_news);
+	} else {
+		/* non enumerated widget mixer */
+		for (i = 0; i < w->num_kcontrols; i++) {
+			struct snd_kcontrol *kcontrol = w->kcontrols[i];
+			struct soc_mixer_control *sm =
+			(struct soc_mixer_control *) kcontrol->private_value;
+
+			kfree(w->kcontrols[i]->tlv.p);
+
+			snd_ctl_remove(card, w->kcontrols[i]);
+			kfree(sm);
+		}
+		kfree(w->kcontrol_news);
+	}
+	/* widget w is freed by soc-dapm.c */
+}
+
+/* remove PCM DAI configurations */
+static void remove_pcm_dai(struct snd_soc_component *comp,
+	struct snd_soc_dobj *dobj, int pass)
+{
+	if (pass != SOC_TPLG_PASS_PCM_DAI)
+		return;
+
+	if (dobj->ops && dobj->ops->pcm_dai_unload)
+		dobj->ops->pcm_dai_unload(comp, dobj);
+
+	list_del(&dobj->list);
+	kfree(dobj);
+}
+
+/* bind a kcontrol to it's IO handlers */
+static int soc_tplg_kcontrol_bind_io(struct snd_soc_tplg_ctl_hdr *hdr,
+	struct snd_kcontrol_new *k,
+	const struct snd_soc_tplg_kcontrol_ops *ops, int num_ops,
+	const struct snd_soc_tplg_kcontrol_ops *bops, int num_bops)
+{
+	int i;
+
+	/* try and map standard kcontrols handler first */
+	for (i = 0; i < num_ops; i++) {
+
+		if (ops[i].id == hdr->ops.put)
+			k->put = ops[i].put;
+		if (ops[i].id == hdr->ops.get)
+			k->get = ops[i].get;
+		if (ops[i].id == hdr->ops.info)
+			k->info = ops[i].info;
+	}
+
+	/* standard handlers found ? */
+	if (k->put && k->get && k->info)
+		return 0;
+
+	/* none found so try bespoke handlers */
+	for (i = 0; i < num_bops; i++) {
+
+		if (k->put == NULL && bops[i].id == hdr->ops.put)
+			k->put = bops[i].put;
+		if (k->get == NULL && bops[i].id == hdr->ops.get)
+			k->get = bops[i].get;
+		if (k->info == NULL && ops[i].id == hdr->ops.info)
+			k->info = bops[i].info;
+	}
+
+	/* bespoke handlers found ? */
+	if (k->put && k->get && k->info)
+		return 0;
+
+	/* nothing to bind */
+	return -EINVAL;
+}
+
+/* bind a widgets to it's evnt handlers */
+int snd_soc_tplg_widget_bind_event(struct snd_soc_dapm_widget *w,
+		const struct snd_soc_tplg_widget_events *events,
+		int num_events, u16 event_type)
+{
+	int i;
+
+	w->event = NULL;
+
+	for (i = 0; i < num_events; i++) {
+		if (event_type == events[i].type) {
+
+			/* found - so assign event */
+			w->event = events[i].event_handler;
+			return 0;
+		}
+	}
+
+	/* not found */
+	return -EINVAL;
+}
+EXPORT_SYMBOL_GPL(snd_soc_tplg_widget_bind_event);
+
+/* optionally pass new dynamic kcontrol to component driver. */
+static int soc_tplg_init_kcontrol(struct soc_tplg *tplg,
+	struct snd_kcontrol_new *k, struct snd_soc_tplg_ctl_hdr *hdr)
+{
+	if (tplg->comp && tplg->ops && tplg->ops->control_load)
+		return tplg->ops->control_load(tplg->comp, k, hdr);
+
+	return 0;
+}
+
+static int soc_tplg_create_tlv(struct soc_tplg *tplg,
+	struct snd_kcontrol_new *kc, u32 tlv_size)
+{
+	struct snd_soc_tplg_ctl_tlv *tplg_tlv;
+	struct snd_ctl_tlv *tlv;
+
+	if (tlv_size == 0)
+		return 0;
+
+	tplg_tlv = (struct snd_soc_tplg_ctl_tlv *) tplg->pos;
+	tplg->pos += tlv_size;
+
+	tlv = kzalloc(sizeof(*tlv) + tlv_size, GFP_KERNEL);
+	if (tlv == NULL)
+		return -ENOMEM;
+
+	dev_dbg(tplg->dev, " created TLV type %d size %d bytes\n",
+		tplg_tlv->numid, tplg_tlv->size);
+
+	tlv->numid = tplg_tlv->numid;
+	tlv->length = tplg_tlv->size;
+	memcpy(tlv->tlv, tplg_tlv + 1, tplg_tlv->size);
+	kc->tlv.p = (void *)tlv;
+
+	return 0;
+}
+
+static inline void soc_tplg_free_tlv(struct soc_tplg *tplg,
+	struct snd_kcontrol_new *kc)
+{
+	kfree(kc->tlv.p);
+}
+
+static int soc_tplg_dbytes_create(struct soc_tplg *tplg, unsigned int count,
+	size_t size)
+{
+	struct snd_soc_tplg_bytes_control *be;
+	struct soc_bytes_ext *sbe;
+	struct snd_kcontrol_new kc;
+	int i, err;
+
+	if (soc_tplg_check_elem_count(tplg,
+		sizeof(struct snd_soc_tplg_bytes_control), count,
+			size, "mixer bytes")) {
+		dev_err(tplg->dev, "ASoC: Invalid count %d for byte control\n",
+			count);
+		return -EINVAL;
+	}
+
+	for (i = 0; i < count; i++) {
+		be = (struct snd_soc_tplg_bytes_control *)tplg->pos;
+
+		/* validate kcontrol */
+		if (strnlen(be->hdr.name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN) ==
+			SNDRV_CTL_ELEM_ID_NAME_MAXLEN)
+			return -EINVAL;
+
+		sbe = kzalloc(sizeof(*sbe), GFP_KERNEL);
+		if (sbe == NULL)
+			return -ENOMEM;
+
+		tplg->pos += (sizeof(struct snd_soc_tplg_bytes_control) +
+			be->priv.size);
+
+		dev_dbg(tplg->dev,
+			"ASoC: adding bytes kcontrol %s with access 0x%x\n",
+			be->hdr.name, be->hdr.access);
+
+		memset(&kc, 0, sizeof(kc));
+		kc.name = be->hdr.name;
+		kc.private_value = (long)sbe;
+		kc.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+		kc.access = be->hdr.access;
+
+		sbe->max = be->max;
+		sbe->dobj.type = SND_SOC_DOBJ_BYTES;
+		sbe->dobj.ops = tplg->ops;
+		INIT_LIST_HEAD(&sbe->dobj.list);
+
+		/* map io handlers */
+		err = soc_tplg_kcontrol_bind_io(&be->hdr, &kc, io_ops,
+			ARRAY_SIZE(io_ops), tplg->io_ops, tplg->io_ops_count);
+		if (err) {
+			soc_control_err(tplg, &be->hdr, be->hdr.name);
+			kfree(sbe);
+			continue;
+		}
+
+		/* pass control to driver for optional further init */
+		err = soc_tplg_init_kcontrol(tplg, &kc,
+			(struct snd_soc_tplg_ctl_hdr *)be);
+		if (err < 0) {
+			dev_err(tplg->dev, "ASoC: failed to init %s\n",
+				be->hdr.name);
+			kfree(sbe);
+			continue;
+		}
+
+		/* register control here */
+		err = soc_tplg_add_kcontrol(tplg, &kc,
+			&sbe->dobj.control.kcontrol);
+		if (err < 0) {
+			dev_err(tplg->dev, "ASoC: failed to add %s\n",
+				be->hdr.name);
+			kfree(sbe);
+			continue;
+		}
+
+		list_add(&sbe->dobj.list, &tplg->comp->dobj_list);
+	}
+	return 0;
+
+}
+
+static int soc_tplg_dmixer_create(struct soc_tplg *tplg, unsigned int count,
+	size_t size)
+{
+	struct snd_soc_tplg_mixer_control *mc;
+	struct soc_mixer_control *sm;
+	struct snd_kcontrol_new kc;
+	int i, err;
+
+	if (soc_tplg_check_elem_count(tplg,
+		sizeof(struct snd_soc_tplg_mixer_control),
+		count, size, "mixers")) {
+
+		dev_err(tplg->dev, "ASoC: invalid count %d for controls\n",
+			count);
+		return -EINVAL;
+	}
+
+	for (i = 0; i < count; i++) {
+		mc = (struct snd_soc_tplg_mixer_control *)tplg->pos;
+
+		/* validate kcontrol */
+		if (strnlen(mc->hdr.name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN) ==
+			SNDRV_CTL_ELEM_ID_NAME_MAXLEN)
+			return -EINVAL;
+
+		sm = kzalloc(sizeof(*sm), GFP_KERNEL);
+		if (sm == NULL)
+			return -ENOMEM;
+		tplg->pos += (sizeof(struct snd_soc_tplg_mixer_control) +
+			mc->priv.size);
+
+		dev_dbg(tplg->dev,
+			"ASoC: adding mixer kcontrol %s with access 0x%x\n",
+			mc->hdr.name, mc->hdr.access);
+
+		memset(&kc, 0, sizeof(kc));
+		kc.name = mc->hdr.name;
+		kc.private_value = (long)sm;
+		kc.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+		kc.access = mc->hdr.access;
+
+		/* we only support FL/FR channel mapping atm */
+		sm->reg = tplc_chan_get_reg(tplg, mc->channel,
+			SNDRV_CHMAP_FL);
+		sm->rreg = tplc_chan_get_reg(tplg, mc->channel,
+			SNDRV_CHMAP_FR);
+		sm->shift = tplc_chan_get_shift(tplg, mc->channel,
+			SNDRV_CHMAP_FL);
+		sm->rshift = tplc_chan_get_shift(tplg, mc->channel,
+			SNDRV_CHMAP_FR);
+
+		sm->max = mc->max;
+		sm->min = mc->min;
+		sm->invert = mc->invert;
+		sm->platform_max = mc->platform_max;
+		sm->dobj.index = tplg->index;
+		sm->dobj.ops = tplg->ops;
+		sm->dobj.type = SND_SOC_DOBJ_MIXER;
+		INIT_LIST_HEAD(&sm->dobj.list);
+
+		/* map io handlers */
+		err = soc_tplg_kcontrol_bind_io(&mc->hdr, &kc, io_ops,
+			ARRAY_SIZE(io_ops), tplg->io_ops, tplg->io_ops_count);
+		if (err) {
+			soc_control_err(tplg, &mc->hdr, mc->hdr.name);
+			kfree(sm);
+			continue;
+		}
+
+		/* pass control to driver for optional further init */
+		err = soc_tplg_init_kcontrol(tplg, &kc,
+			(struct snd_soc_tplg_ctl_hdr *) mc);
+		if (err < 0) {
+			dev_err(tplg->dev, "ASoC: failed to init %s\n",
+				mc->hdr.name);
+			kfree(sm);
+			continue;
+		}
+
+		/* create any TLV data */
+		soc_tplg_create_tlv(tplg, &kc, mc->hdr.tlv_size);
+
+		/* register control here */
+		err = soc_tplg_add_kcontrol(tplg, &kc,
+			&sm->dobj.control.kcontrol);
+		if (err < 0) {
+			dev_err(tplg->dev, "ASoC: failed to add %s\n",
+				mc->hdr.name);
+			soc_tplg_free_tlv(tplg, &kc);
+			kfree(sm);
+			continue;
+		}
+
+		list_add(&sm->dobj.list, &tplg->comp->dobj_list);
+	}
+
+	return 0;
+}
+
+static int soc_tplg_denum_create_texts(struct soc_enum *se,
+	struct snd_soc_tplg_enum_control *ec)
+{
+	int i, ret;
+
+	se->dobj.control.dtexts =
+		kzalloc(sizeof(char *) * ec->items, GFP_KERNEL);
+	if (se->dobj.control.dtexts == NULL)
+		return -ENOMEM;
+
+	for (i = 0; i < ec->items; i++) {
+
+		if (strnlen(ec->texts[i], SNDRV_CTL_ELEM_ID_NAME_MAXLEN) ==
+			SNDRV_CTL_ELEM_ID_NAME_MAXLEN) {
+			ret = -EINVAL;
+			goto err;
+		}
+
+		se->dobj.control.dtexts[i] = kstrdup(ec->texts[i], GFP_KERNEL);
+		if (!se->dobj.control.dtexts[i]) {
+			ret = -ENOMEM;
+			goto err;
+		}
+	}
+
+	return 0;
+
+err:
+	for (--i; i >= 0; i--)
+		kfree(se->dobj.control.dtexts[i]);
+	kfree(se->dobj.control.dtexts);
+	return ret;
+}
+
+static int soc_tplg_denum_create_values(struct soc_enum *se,
+	struct snd_soc_tplg_enum_control *ec)
+{
+	if (ec->items > sizeof(*ec->values))
+		return -EINVAL;
+
+	se->dobj.control.dvalues =
+		kmalloc(ec->items * sizeof(u32), GFP_KERNEL);
+	if (!se->dobj.control.dvalues)
+		return -ENOMEM;
+
+	memcpy(se->dobj.control.dvalues, ec->values, ec->items * sizeof(u32));
+	return 0;
+}
+
+static int soc_tplg_denum_create(struct soc_tplg *tplg, unsigned int count,
+	size_t size)
+{
+	struct snd_soc_tplg_enum_control *ec;
+	struct soc_enum *se;
+	struct snd_kcontrol_new kc;
+	int i, ret, err;
+
+	if (soc_tplg_check_elem_count(tplg,
+		sizeof(struct snd_soc_tplg_enum_control),
+		count, size, "enums")) {
+
+		dev_err(tplg->dev, "ASoC: invalid count %d for enum controls\n",
+			count);
+		return -EINVAL;
+	}
+
+	for (i = 0; i < count; i++) {
+		ec = (struct snd_soc_tplg_enum_control *)tplg->pos;
+		tplg->pos += (sizeof(struct snd_soc_tplg_enum_control) +
+			ec->priv.size);
+
+		/* validate kcontrol */
+		if (strnlen(ec->hdr.name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN) ==
+			SNDRV_CTL_ELEM_ID_NAME_MAXLEN)
+			return -EINVAL;
+
+		se = kzalloc((sizeof(*se)), GFP_KERNEL);
+		if (se == NULL)
+			return -ENOMEM;
+
+		dev_dbg(tplg->dev, "ASoC: adding enum kcontrol %s size %d\n",
+			ec->hdr.name, ec->items);
+
+		memset(&kc, 0, sizeof(kc));
+		kc.name = ec->hdr.name;
+		kc.private_value = (long)se;
+		kc.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+		kc.access = ec->hdr.access;
+
+		se->reg = tplc_chan_get_reg(tplg, ec->channel, SNDRV_CHMAP_FL);
+		se->shift_l = tplc_chan_get_shift(tplg, ec->channel,
+			SNDRV_CHMAP_FL);
+		se->shift_r = tplc_chan_get_shift(tplg, ec->channel,
+			SNDRV_CHMAP_FL);
+
+		se->items = ec->items;
+		se->mask = ec->mask;
+		se->dobj.index = tplg->index;
+		se->dobj.type = SND_SOC_DOBJ_ENUM;
+		se->dobj.ops = tplg->ops;
+		INIT_LIST_HEAD(&se->dobj.list);
+
+		switch (ec->hdr.ops.info) {
+		case SND_SOC_TPLG_DAPM_CTL_ENUM_VALUE:
+		case SND_SOC_TPLG_CTL_ENUM_VALUE:
+			err = soc_tplg_denum_create_values(se, ec);
+			if (err < 0) {
+				dev_err(tplg->dev,
+					"ASoC: could not create values for %s\n",
+					ec->hdr.name);
+				kfree(se);
+				continue;
+			}
+			/* fall through and create texts */
+		case SND_SOC_TPLG_CTL_ENUM:
+		case SND_SOC_TPLG_DAPM_CTL_ENUM_DOUBLE:
+		case SND_SOC_TPLG_DAPM_CTL_ENUM_VIRT:
+			err = soc_tplg_denum_create_texts(se, ec);
+			if (err < 0) {
+				dev_err(tplg->dev,
+					"ASoC: could not create texts for %s\n",
+					ec->hdr.name);
+				kfree(se);
+				continue;
+			}
+			break;
+		default:
+			dev_err(tplg->dev,
+				"ASoC: invalid enum control type %d for %s\n",
+				ec->hdr.ops.info, ec->hdr.name);
+			kfree(se);
+			continue;
+		}
+
+		/* map io handlers */
+		err = soc_tplg_kcontrol_bind_io(&ec->hdr, &kc, io_ops,
+			ARRAY_SIZE(io_ops), tplg->io_ops, tplg->io_ops_count);
+		if (err) {
+			soc_control_err(tplg, &ec->hdr, ec->hdr.name);
+			kfree(se);
+			continue;
+		}
+
+		/* pass control to driver for optional further init */
+		err = soc_tplg_init_kcontrol(tplg, &kc,
+			(struct snd_soc_tplg_ctl_hdr *) ec);
+		if (err < 0) {
+			dev_err(tplg->dev, "ASoC: failed to init %s\n",
+				ec->hdr.name);
+			kfree(se);
+			continue;
+		}
+
+		/* register control here */
+		ret = soc_tplg_add_kcontrol(tplg,
+			&kc, &se->dobj.control.kcontrol);
+		if (ret < 0) {
+			dev_err(tplg->dev, "ASoC: could not add kcontrol %s\n",
+				ec->hdr.name);
+			kfree(se);
+			continue;
+		}
+
+		list_add(&se->dobj.list, &tplg->comp->dobj_list);
+	}
+
+	return 0;
+}
+
+static int soc_tplg_kcontrol_elems_load(struct soc_tplg *tplg,
+	struct snd_soc_tplg_hdr *hdr)
+{
+	struct snd_soc_tplg_ctl_hdr *control_hdr;
+	int i;
+
+	if (tplg->pass != SOC_TPLG_PASS_MIXER) {
+		tplg->pos += hdr->size + hdr->payload_size;
+		return 0;
+	}
+
+	dev_dbg(tplg->dev, "ASoC: adding %d kcontrols at 0x%lx\n", hdr->count,
+		soc_tplg_get_offset(tplg));
+
+	for (i = 0; i < hdr->count; i++) {
+
+		control_hdr = (struct snd_soc_tplg_ctl_hdr *)tplg->pos;
+
+		switch (control_hdr->ops.info) {
+		case SND_SOC_TPLG_CTL_VOLSW:
+		case SND_SOC_TPLG_CTL_STROBE:
+		case SND_SOC_TPLG_CTL_VOLSW_SX:
+		case SND_SOC_TPLG_CTL_VOLSW_XR_SX:
+		case SND_SOC_TPLG_CTL_RANGE:
+		case SND_SOC_TPLG_DAPM_CTL_VOLSW:
+		case SND_SOC_TPLG_DAPM_CTL_PIN:
+			soc_tplg_dmixer_create(tplg, 1, hdr->payload_size);
+			break;
+		case SND_SOC_TPLG_CTL_ENUM:
+		case SND_SOC_TPLG_CTL_ENUM_VALUE:
+		case SND_SOC_TPLG_DAPM_CTL_ENUM_DOUBLE:
+		case SND_SOC_TPLG_DAPM_CTL_ENUM_VIRT:
+		case SND_SOC_TPLG_DAPM_CTL_ENUM_VALUE:
+			soc_tplg_denum_create(tplg, 1, hdr->payload_size);
+			break;
+		case SND_SOC_TPLG_CTL_BYTES:
+			soc_tplg_dbytes_create(tplg, 1, hdr->payload_size);
+			break;
+		default:
+			soc_bind_err(tplg, control_hdr, i);
+			return -EINVAL;
+		}
+	}
+
+	return 0;
+}
+
+static int soc_tplg_dapm_graph_elems_load(struct soc_tplg *tplg,
+	struct snd_soc_tplg_hdr *hdr)
+{
+	struct snd_soc_dapm_context *dapm = &tplg->comp->dapm;
+	struct snd_soc_dapm_route route;
+	struct snd_soc_tplg_dapm_graph_elem *elem;
+	int count = hdr->count, i;
+
+	if (tplg->pass != SOC_TPLG_PASS_GRAPH) {
+		tplg->pos += hdr->size + hdr->payload_size;
+		return 0;
+	}
+
+	if (soc_tplg_check_elem_count(tplg,
+		sizeof(struct snd_soc_tplg_dapm_graph_elem),
+		count, hdr->payload_size, "graph")) {
+
+		dev_err(tplg->dev, "ASoC: invalid count %d for DAPM routes\n",
+			count);
+		return -EINVAL;
+	}
+
+	dev_dbg(tplg->dev, "ASoC: adding %d DAPM routes\n", count);
+
+	for (i = 0; i < count; i++) {
+		elem = (struct snd_soc_tplg_dapm_graph_elem *)tplg->pos;
+		tplg->pos += sizeof(struct snd_soc_tplg_dapm_graph_elem);
+
+		/* validate routes */
+		if (strnlen(elem->source, SNDRV_CTL_ELEM_ID_NAME_MAXLEN) ==
+			SNDRV_CTL_ELEM_ID_NAME_MAXLEN)
+			return -EINVAL;
+		if (strnlen(elem->sink, SNDRV_CTL_ELEM_ID_NAME_MAXLEN) ==
+			SNDRV_CTL_ELEM_ID_NAME_MAXLEN)
+			return -EINVAL;
+		if (strnlen(elem->control, SNDRV_CTL_ELEM_ID_NAME_MAXLEN) ==
+			SNDRV_CTL_ELEM_ID_NAME_MAXLEN)
+			return -EINVAL;
+
+		route.source = elem->source;
+		route.sink = elem->sink;
+		route.connected = NULL; /* set to NULL atm for tplg users */
+		if (strnlen(elem->control, SNDRV_CTL_ELEM_ID_NAME_MAXLEN) == 0)
+			route.control = NULL;
+		else
+			route.control = elem->control;
+
+		/* add route, but keep going if some fail */
+		snd_soc_dapm_add_routes(dapm, &route, 1);
+	}
+
+	return 0;
+}
+
+static struct snd_kcontrol_new *soc_tplg_dapm_widget_dmixer_create(
+	struct soc_tplg *tplg, int num_kcontrols)
+{
+	struct snd_kcontrol_new *kc;
+	struct soc_mixer_control *sm;
+	struct snd_soc_tplg_mixer_control *mc;
+	int i, err;
+
+	kc = kzalloc(sizeof(*kc) * num_kcontrols, GFP_KERNEL);
+	if (kc == NULL)
+		return NULL;
+
+	for (i = 0; i < num_kcontrols; i++) {
+		mc = (struct snd_soc_tplg_mixer_control *)tplg->pos;
+		sm = kzalloc(sizeof(*sm), GFP_KERNEL);
+		if (sm == NULL)
+			goto err;
+
+		tplg->pos += (sizeof(struct snd_soc_tplg_mixer_control) +
+			mc->priv.size);
+
+		/* validate kcontrol */
+		if (strnlen(mc->hdr.name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN) ==
+			SNDRV_CTL_ELEM_ID_NAME_MAXLEN)
+			goto err_str;
+
+		dev_dbg(tplg->dev, " adding DAPM widget mixer control %s at %d\n",
+			mc->hdr.name, i);
+
+		kc[i].name = mc->hdr.name;
+		kc[i].private_value = (long)sm;
+		kc[i].iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+		kc[i].access = mc->hdr.access;
+
+		/* we only support FL/FR channel mapping atm */
+		sm->reg = tplc_chan_get_reg(tplg, mc->channel,
+			SNDRV_CHMAP_FL);
+		sm->rreg = tplc_chan_get_reg(tplg, mc->channel,
+			SNDRV_CHMAP_FR);
+		sm->shift = tplc_chan_get_shift(tplg, mc->channel,
+			SNDRV_CHMAP_FL);
+		sm->rshift = tplc_chan_get_shift(tplg, mc->channel,
+			SNDRV_CHMAP_FR);
+
+		sm->max = mc->max;
+		sm->min = mc->min;
+		sm->invert = mc->invert;
+		sm->platform_max = mc->platform_max;
+		sm->dobj.index = tplg->index;
+		INIT_LIST_HEAD(&sm->dobj.list);
+
+		/* map io handlers */
+		err = soc_tplg_kcontrol_bind_io(&mc->hdr, &kc[i], io_ops,
+			ARRAY_SIZE(io_ops), tplg->io_ops, tplg->io_ops_count);
+		if (err) {
+			soc_control_err(tplg, &mc->hdr, mc->hdr.name);
+			kfree(sm);
+			continue;
+		}
+
+		/* pass control to driver for optional further init */
+		err = soc_tplg_init_kcontrol(tplg, &kc[i],
+			(struct snd_soc_tplg_ctl_hdr *)mc);
+		if (err < 0) {
+			dev_err(tplg->dev, "ASoC: failed to init %s\n",
+				mc->hdr.name);
+			kfree(sm);
+			continue;
+		}
+	}
+	return kc;
+
+err_str:
+	kfree(sm);
+err:
+	for (--i; i >= 0; i--)
+		kfree((void *)kc[i].private_value);
+	kfree(kc);
+	return NULL;
+}
+
+static struct snd_kcontrol_new *soc_tplg_dapm_widget_denum_create(
+	struct soc_tplg *tplg)
+{
+	struct snd_kcontrol_new *kc;
+	struct snd_soc_tplg_enum_control *ec;
+	struct soc_enum *se;
+	int i, err;
+
+	ec = (struct snd_soc_tplg_enum_control *)tplg->pos;
+	tplg->pos += (sizeof(struct snd_soc_tplg_enum_control) +
+		ec->priv.size);
+
+	/* validate kcontrol */
+	if (strnlen(ec->hdr.name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN) ==
+		SNDRV_CTL_ELEM_ID_NAME_MAXLEN)
+		return NULL;
+
+	kc = kzalloc(sizeof(*kc), GFP_KERNEL);
+	if (kc == NULL)
+		return NULL;
+
+	se = kzalloc(sizeof(*se), GFP_KERNEL);
+	if (se == NULL)
+		goto err;
+
+	dev_dbg(tplg->dev, " adding DAPM widget enum control %s\n",
+		ec->hdr.name);
+
+	kc->name = ec->hdr.name;
+	kc->private_value = (long)se;
+	kc->iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+	kc->access = ec->hdr.access;
+
+	/* we only support FL/FR channel mapping atm */
+	se->reg = tplc_chan_get_reg(tplg, ec->channel, SNDRV_CHMAP_FL);
+	se->shift_l = tplc_chan_get_shift(tplg, ec->channel, SNDRV_CHMAP_FL);
+	se->shift_r = tplc_chan_get_shift(tplg, ec->channel, SNDRV_CHMAP_FR);
+
+	se->items = ec->items;
+	se->mask = ec->mask;
+	se->dobj.index = tplg->index;
+
+	switch (ec->hdr.ops.info) {
+	case SND_SOC_TPLG_CTL_ENUM_VALUE:
+	case SND_SOC_TPLG_DAPM_CTL_ENUM_VALUE:
+		err = soc_tplg_denum_create_values(se, ec);
+		if (err < 0) {
+			dev_err(tplg->dev, "ASoC: could not create values for %s\n",
+				ec->hdr.name);
+			goto err_se;
+		}
+		/* fall through to create texts */
+	case SND_SOC_TPLG_CTL_ENUM:
+	case SND_SOC_TPLG_DAPM_CTL_ENUM_DOUBLE:
+	case SND_SOC_TPLG_DAPM_CTL_ENUM_VIRT:
+		err = soc_tplg_denum_create_texts(se, ec);
+		if (err < 0) {
+			dev_err(tplg->dev, "ASoC: could not create texts for %s\n",
+				ec->hdr.name);
+			goto err_se;
+		}
+		break;
+	default:
+		dev_err(tplg->dev, "ASoC: invalid enum control type %d for %s\n",
+			ec->hdr.ops.info, ec->hdr.name);
+		goto err_se;
+	}
+
+	/* map io handlers */
+	err = soc_tplg_kcontrol_bind_io(&ec->hdr, kc, io_ops,
+		ARRAY_SIZE(io_ops), tplg->io_ops, tplg->io_ops_count);
+	if (err) {
+		soc_control_err(tplg, &ec->hdr, ec->hdr.name);
+		goto err_se;
+	}
+
+	/* pass control to driver for optional further init */
+	err = soc_tplg_init_kcontrol(tplg, kc,
+		(struct snd_soc_tplg_ctl_hdr *)ec);
+	if (err < 0) {
+		dev_err(tplg->dev, "ASoC: failed to init %s\n",
+			ec->hdr.name);
+		goto err_se;
+	}
+
+	return kc;
+
+err_se:
+	/* free values and texts */
+	kfree(se->dobj.control.dvalues);
+	for (i = 0; i < ec->items; i++)
+		kfree(se->dobj.control.dtexts[i]);
+
+	kfree(se);
+err:
+	kfree(kc);
+
+	return NULL;
+}
+
+static struct snd_kcontrol_new *soc_tplg_dapm_widget_dbytes_create(
+	struct soc_tplg *tplg, int count)
+{
+	struct snd_soc_tplg_bytes_control *be;
+	struct soc_bytes_ext  *sbe;
+	struct snd_kcontrol_new *kc;
+	int i, err;
+
+	kc = kzalloc(sizeof(*kc) * count, GFP_KERNEL);
+	if (!kc)
+		return NULL;
+
+	for (i = 0; i < count; i++) {
+		be = (struct snd_soc_tplg_bytes_control *)tplg->pos;
+
+		/* validate kcontrol */
+		if (strnlen(be->hdr.name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN) ==
+			SNDRV_CTL_ELEM_ID_NAME_MAXLEN)
+			goto err;
+
+		sbe = kzalloc(sizeof(*sbe), GFP_KERNEL);
+		if (sbe == NULL)
+			goto err;
+
+		tplg->pos += (sizeof(struct snd_soc_tplg_bytes_control) +
+			be->priv.size);
+
+		dev_dbg(tplg->dev,
+			"ASoC: adding bytes kcontrol %s with access 0x%x\n",
+			be->hdr.name, be->hdr.access);
+
+		memset(kc, 0, sizeof(*kc));
+		kc[i].name = be->hdr.name;
+		kc[i].private_value = (long)sbe;
+		kc[i].iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+		kc[i].access = be->hdr.access;
+
+		sbe->max = be->max;
+		INIT_LIST_HEAD(&sbe->dobj.list);
+
+		/* map standard io handlers and check for external handlers */
+		err = soc_tplg_kcontrol_bind_io(&be->hdr, &kc[i], io_ops,
+				ARRAY_SIZE(io_ops), tplg->io_ops,
+				tplg->io_ops_count);
+		if (err) {
+			soc_control_err(tplg, &be->hdr, be->hdr.name);
+			kfree(sbe);
+			continue;
+		}
+
+		/* pass control to driver for optional further init */
+		err = soc_tplg_init_kcontrol(tplg, &kc[i],
+			(struct snd_soc_tplg_ctl_hdr *)be);
+		if (err < 0) {
+			dev_err(tplg->dev, "ASoC: failed to init %s\n",
+				be->hdr.name);
+			kfree(sbe);
+			continue;
+		}
+	}
+
+	return kc;
+
+err:
+	for (--i; i >= 0; i--)
+		kfree((void *)kc[i].private_value);
+
+	kfree(kc);
+	return NULL;
+}
+
+static int soc_tplg_dapm_widget_create(struct soc_tplg *tplg,
+	struct snd_soc_tplg_dapm_widget *w)
+{
+	struct snd_soc_dapm_context *dapm = &tplg->comp->dapm;
+	struct snd_soc_dapm_widget template, *widget;
+	struct snd_soc_tplg_ctl_hdr *control_hdr;
+	struct snd_soc_card *card = tplg->comp->card;
+	int ret = 0;
+
+	if (strnlen(w->name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN) ==
+		SNDRV_CTL_ELEM_ID_NAME_MAXLEN)
+		return -EINVAL;
+	if (strnlen(w->sname, SNDRV_CTL_ELEM_ID_NAME_MAXLEN) ==
+		SNDRV_CTL_ELEM_ID_NAME_MAXLEN)
+		return -EINVAL;
+
+	dev_dbg(tplg->dev, "ASoC: creating DAPM widget %s id %d\n",
+		w->name, w->id);
+
+	memset(&template, 0, sizeof(template));
+
+	/* map user to kernel widget ID */
+	template.id = get_widget_id(w->id);
+	if (template.id < 0)
+		return template.id;
+
+	template.name = kstrdup(w->name, GFP_KERNEL);
+	if (!template.name)
+		return -ENOMEM;
+	template.sname = kstrdup(w->sname, GFP_KERNEL);
+	if (!template.sname) {
+		ret = -ENOMEM;
+		goto err;
+	}
+	template.reg = w->reg;
+	template.shift = w->shift;
+	template.mask = w->mask;
+	template.on_val = w->invert ? 0 : 1;
+	template.off_val = w->invert ? 1 : 0;
+	template.ignore_suspend = w->ignore_suspend;
+	template.event_flags = w->event_flags;
+	template.dobj.index = tplg->index;
+
+	tplg->pos +=
+		(sizeof(struct snd_soc_tplg_dapm_widget) + w->priv.size);
+	if (w->num_kcontrols == 0) {
+		template.num_kcontrols = 0;
+		goto widget;
+	}
+
+	control_hdr = (struct snd_soc_tplg_ctl_hdr *)tplg->pos;
+	dev_dbg(tplg->dev, "ASoC: template %s has %d controls of type %x\n",
+		w->name, w->num_kcontrols, control_hdr->type);
+
+	switch (control_hdr->ops.info) {
+	case SND_SOC_TPLG_CTL_VOLSW:
+	case SND_SOC_TPLG_CTL_STROBE:
+	case SND_SOC_TPLG_CTL_VOLSW_SX:
+	case SND_SOC_TPLG_CTL_VOLSW_XR_SX:
+	case SND_SOC_TPLG_CTL_RANGE:
+	case SND_SOC_TPLG_DAPM_CTL_VOLSW:
+		template.num_kcontrols = w->num_kcontrols;
+		template.kcontrol_news =
+			soc_tplg_dapm_widget_dmixer_create(tplg,
+			template.num_kcontrols);
+		if (!template.kcontrol_news) {
+			ret = -ENOMEM;
+			goto hdr_err;
+		}
+		break;
+	case SND_SOC_TPLG_CTL_ENUM:
+	case SND_SOC_TPLG_CTL_ENUM_VALUE:
+	case SND_SOC_TPLG_DAPM_CTL_ENUM_DOUBLE:
+	case SND_SOC_TPLG_DAPM_CTL_ENUM_VIRT:
+	case SND_SOC_TPLG_DAPM_CTL_ENUM_VALUE:
+		template.dobj.widget.kcontrol_enum = 1;
+		template.num_kcontrols = 1;
+		template.kcontrol_news =
+			soc_tplg_dapm_widget_denum_create(tplg);
+		if (!template.kcontrol_news) {
+			ret = -ENOMEM;
+			goto hdr_err;
+		}
+		break;
+	case SND_SOC_TPLG_CTL_BYTES:
+		template.num_kcontrols = w->num_kcontrols;
+		template.kcontrol_news =
+			soc_tplg_dapm_widget_dbytes_create(tplg,
+				template.num_kcontrols);
+		if (!template.kcontrol_news) {
+			ret = -ENOMEM;
+			goto hdr_err;
+		}
+		break;
+	default:
+		dev_err(tplg->dev, "ASoC: invalid widget control type %d:%d:%d\n",
+			control_hdr->ops.get, control_hdr->ops.put,
+			control_hdr->ops.info);
+		ret = -EINVAL;
+		goto hdr_err;
+	}
+
+widget:
+	ret = soc_tplg_widget_load(tplg, &template, w);
+	if (ret < 0)
+		goto hdr_err;
+
+	/* card dapm mutex is held by the core if we are loading topology
+	 * data during sound card init. */
+	if (card->instantiated)
+		widget = snd_soc_dapm_new_control(dapm, &template);
+	else
+		widget = snd_soc_dapm_new_control_unlocked(dapm, &template);
+	if (widget == NULL) {
+		dev_err(tplg->dev, "ASoC: failed to create widget %s controls\n",
+			w->name);
+		goto hdr_err;
+	}
+
+	widget->dobj.type = SND_SOC_DOBJ_WIDGET;
+	widget->dobj.ops = tplg->ops;
+	widget->dobj.index = tplg->index;
+	list_add(&widget->dobj.list, &tplg->comp->dobj_list);
+	return 0;
+
+hdr_err:
+	kfree(template.sname);
+err:
+	kfree(template.name);
+	return ret;
+}
+
+static int soc_tplg_dapm_widget_elems_load(struct soc_tplg *tplg,
+	struct snd_soc_tplg_hdr *hdr)
+{
+	struct snd_soc_tplg_dapm_widget *widget;
+	int ret, count = hdr->count, i;
+
+	if (tplg->pass != SOC_TPLG_PASS_WIDGET)
+		return 0;
+
+	dev_dbg(tplg->dev, "ASoC: adding %d DAPM widgets\n", count);
+
+	for (i = 0; i < count; i++) {
+		widget = (struct snd_soc_tplg_dapm_widget *) tplg->pos;
+		ret = soc_tplg_dapm_widget_create(tplg, widget);
+		if (ret < 0)
+			dev_err(tplg->dev, "ASoC: failed to load widget %s\n",
+				widget->name);
+	}
+
+	return 0;
+}
+
+static int soc_tplg_dapm_complete(struct soc_tplg *tplg)
+{
+	struct snd_soc_card *card = tplg->comp->card;
+	int ret;
+
+	/* Card might not have been registered at this point.
+	 * If so, just return success.
+	*/
+	if (!card || !card->instantiated) {
+		dev_warn(tplg->dev, "ASoC: Parent card not yet available,"
+				"Do not add new widgets now\n");
+		return 0;
+	}
+
+	ret = snd_soc_dapm_new_widgets(card);
+	if (ret < 0)
+		dev_err(tplg->dev, "ASoC: failed to create new widgets %d\n",
+			ret);
+
+	return 0;
+}
+
+static int soc_tplg_pcm_dai_elems_load(struct soc_tplg *tplg,
+	struct snd_soc_tplg_hdr *hdr)
+{
+	struct snd_soc_tplg_pcm_dai *pcm_dai;
+	struct snd_soc_dobj *dobj;
+	int count = hdr->count;
+	int ret;
+
+	if (tplg->pass != SOC_TPLG_PASS_PCM_DAI)
+		return 0;
+
+	pcm_dai = (struct snd_soc_tplg_pcm_dai *)tplg->pos;
+
+	if (soc_tplg_check_elem_count(tplg,
+		sizeof(struct snd_soc_tplg_pcm_dai), count,
+		hdr->payload_size, "PCM DAI")) {
+		dev_err(tplg->dev, "ASoC: invalid count %d for PCM DAI elems\n",
+			count);
+		return -EINVAL;
+	}
+
+	dev_dbg(tplg->dev, "ASoC: adding %d PCM DAIs\n", count);
+	tplg->pos += sizeof(struct snd_soc_tplg_pcm_dai) * count;
+
+	dobj = kzalloc(sizeof(struct snd_soc_dobj), GFP_KERNEL);
+	if (dobj == NULL)
+		return -ENOMEM;
+
+	/* Call the platform driver call back to register the dais */
+	ret = soc_tplg_pcm_dai_load(tplg, pcm_dai, count);
+	if (ret < 0) {
+		dev_err(tplg->comp->dev, "ASoC: PCM DAI loading failed\n");
+		goto err;
+	}
+
+	dobj->type = get_dobj_type(hdr, NULL);
+	dobj->pcm_dai.count = count;
+	dobj->pcm_dai.pd = pcm_dai;
+	dobj->ops = tplg->ops;
+	dobj->index = tplg->index;
+	list_add(&dobj->list, &tplg->comp->dobj_list);
+	return 0;
+
+err:
+	kfree(dobj);
+	return ret;
+}
+
+static int soc_tplg_manifest_load(struct soc_tplg *tplg,
+	struct snd_soc_tplg_hdr *hdr)
+{
+	struct snd_soc_tplg_manifest *manifest;
+
+	if (tplg->pass != SOC_TPLG_PASS_MANIFEST)
+		return 0;
+
+	manifest = (struct snd_soc_tplg_manifest *)tplg->pos;
+	tplg->pos += sizeof(struct snd_soc_tplg_manifest);
+
+	if (tplg->comp && tplg->ops && tplg->ops->manifest)
+		return tplg->ops->manifest(tplg->comp, manifest);
+
+	dev_err(tplg->dev, "ASoC: Firmware manifest not supported\n");
+	return 0;
+}
+
+/* validate header magic, size and type */
+static int soc_valid_header(struct soc_tplg *tplg,
+	struct snd_soc_tplg_hdr *hdr)
+{
+	if (soc_tplg_get_hdr_offset(tplg) >= tplg->fw->size)
+		return 0;
+
+	/* big endian firmware objects not supported atm */
+	if (hdr->magic == cpu_to_be32(SND_SOC_TPLG_MAGIC)) {
+		dev_err(tplg->dev,
+			"ASoC: pass %d big endian not supported header got %x at offset 0x%lx size 0x%zx.\n",
+			tplg->pass, hdr->magic,
+			soc_tplg_get_hdr_offset(tplg), tplg->fw->size);
+		return -EINVAL;
+	}
+
+	if (hdr->magic != SND_SOC_TPLG_MAGIC) {
+		dev_err(tplg->dev,
+			"ASoC: pass %d does not have a valid header got %x at offset 0x%lx size 0x%zx.\n",
+			tplg->pass, hdr->magic,
+			soc_tplg_get_hdr_offset(tplg), tplg->fw->size);
+		return -EINVAL;
+	}
+
+	if (hdr->abi != SND_SOC_TPLG_ABI_VERSION) {
+		dev_err(tplg->dev,
+			"ASoC: pass %d invalid ABI version got 0x%x need 0x%x at offset 0x%lx size 0x%zx.\n",
+			tplg->pass, hdr->abi,
+			SND_SOC_TPLG_ABI_VERSION, soc_tplg_get_hdr_offset(tplg),
+			tplg->fw->size);
+		return -EINVAL;
+	}
+
+	if (hdr->payload_size == 0) {
+		dev_err(tplg->dev, "ASoC: header has 0 size at offset 0x%lx.\n",
+			soc_tplg_get_hdr_offset(tplg));
+		return -EINVAL;
+	}
+
+	if (tplg->pass == hdr->type)
+		dev_dbg(tplg->dev,
+			"ASoC: Got 0x%x bytes of type %d version %d vendor %d at pass %d\n",
+			hdr->payload_size, hdr->type, hdr->version,
+			hdr->vendor_type, tplg->pass);
+
+	return 1;
+}
+
+/* check header type and call appropriate handler */
+static int soc_tplg_load_header(struct soc_tplg *tplg,
+	struct snd_soc_tplg_hdr *hdr)
+{
+	tplg->pos = tplg->hdr_pos + sizeof(struct snd_soc_tplg_hdr);
+
+	/* check for matching ID */
+	if (hdr->index != tplg->req_index &&
+		hdr->index != SND_SOC_TPLG_INDEX_ALL)
+		return 0;
+
+	tplg->index = hdr->index;
+
+	switch (hdr->type) {
+	case SND_SOC_TPLG_TYPE_MIXER:
+	case SND_SOC_TPLG_TYPE_ENUM:
+	case SND_SOC_TPLG_TYPE_BYTES:
+		return soc_tplg_kcontrol_elems_load(tplg, hdr);
+	case SND_SOC_TPLG_TYPE_DAPM_GRAPH:
+		return soc_tplg_dapm_graph_elems_load(tplg, hdr);
+	case SND_SOC_TPLG_TYPE_DAPM_WIDGET:
+		return soc_tplg_dapm_widget_elems_load(tplg, hdr);
+	case SND_SOC_TPLG_TYPE_PCM:
+	case SND_SOC_TPLG_TYPE_DAI_LINK:
+	case SND_SOC_TPLG_TYPE_CODEC_LINK:
+		return soc_tplg_pcm_dai_elems_load(tplg, hdr);
+	case SND_SOC_TPLG_TYPE_MANIFEST:
+		return soc_tplg_manifest_load(tplg, hdr);
+	default:
+		/* bespoke vendor data object */
+		return soc_tplg_vendor_load(tplg, hdr);
+	}
+
+	return 0;
+}
+
+/* process the topology file headers */
+static int soc_tplg_process_headers(struct soc_tplg *tplg)
+{
+	struct snd_soc_tplg_hdr *hdr;
+	int ret;
+
+	tplg->pass = SOC_TPLG_PASS_START;
+
+	/* process the header types from start to end */
+	while (tplg->pass <= SOC_TPLG_PASS_END) {
+
+		tplg->hdr_pos = tplg->fw->data;
+		hdr = (struct snd_soc_tplg_hdr *)tplg->hdr_pos;
+
+		while (!soc_tplg_is_eof(tplg)) {
+
+			/* make sure header is valid before loading */
+			ret = soc_valid_header(tplg, hdr);
+			if (ret < 0)
+				return ret;
+			else if (ret == 0)
+				break;
+
+			/* load the header object */
+			ret = soc_tplg_load_header(tplg, hdr);
+			if (ret < 0)
+				return ret;
+
+			/* goto next header */
+			tplg->hdr_pos += hdr->payload_size +
+				sizeof(struct snd_soc_tplg_hdr);
+			hdr = (struct snd_soc_tplg_hdr *)tplg->hdr_pos;
+		}
+
+		/* next data type pass */
+		tplg->pass++;
+	}
+
+	/* signal DAPM we are complete */
+	ret = soc_tplg_dapm_complete(tplg);
+	if (ret < 0)
+		dev_err(tplg->dev,
+			"ASoC: failed to initialise DAPM from Firmware\n");
+
+	return ret;
+}
+
+static int soc_tplg_load(struct soc_tplg *tplg)
+{
+	int ret;
+
+	ret = soc_tplg_process_headers(tplg);
+	if (ret == 0)
+		soc_tplg_complete(tplg);
+
+	return ret;
+}
+
+/* load audio component topology from "firmware" file */
+int snd_soc_tplg_component_load(struct snd_soc_component *comp,
+	struct snd_soc_tplg_ops *ops, const struct firmware *fw, u32 id)
+{
+	struct soc_tplg tplg;
+
+	/* setup parsing context */
+	memset(&tplg, 0, sizeof(tplg));
+	tplg.fw = fw;
+	tplg.dev = comp->dev;
+	tplg.comp = comp;
+	tplg.ops = ops;
+	tplg.req_index = id;
+	tplg.io_ops = ops->io_ops;
+	tplg.io_ops_count = ops->io_ops_count;
+
+	return soc_tplg_load(&tplg);
+}
+EXPORT_SYMBOL_GPL(snd_soc_tplg_component_load);
+
+/* remove this dynamic widget */
+void snd_soc_tplg_widget_remove(struct snd_soc_dapm_widget *w)
+{
+	/* make sure we are a widget */
+	if (w->dobj.type != SND_SOC_DOBJ_WIDGET)
+		return;
+
+	remove_widget(w->dapm->component, &w->dobj, SOC_TPLG_PASS_WIDGET);
+}
+EXPORT_SYMBOL_GPL(snd_soc_tplg_widget_remove);
+
+/* remove all dynamic widgets from this DAPM context */
+void snd_soc_tplg_widget_remove_all(struct snd_soc_dapm_context *dapm,
+	u32 index)
+{
+	struct snd_soc_dapm_widget *w, *next_w;
+	struct snd_soc_dapm_path *p, *next_p;
+
+	list_for_each_entry_safe(w, next_w, &dapm->card->widgets, list) {
+
+		/* make sure we are a widget with correct context */
+		if (w->dobj.type != SND_SOC_DOBJ_WIDGET || w->dapm != dapm)
+			continue;
+
+		/* match ID */
+		if (w->dobj.index != index &&
+			w->dobj.index != SND_SOC_TPLG_INDEX_ALL)
+			continue;
+
+		list_del(&w->list);
+
+		/*
+		 * remove source and sink paths associated to this widget.
+		 * While removing the path, remove reference to it from both
+		 * source and sink widgets so that path is removed only once.
+		 */
+		list_for_each_entry_safe(p, next_p, &w->sources, list_sink) {
+			list_del(&p->list_sink);
+			list_del(&p->list_source);
+			list_del(&p->list);
+			kfree(p);
+		}
+		list_for_each_entry_safe(p, next_p, &w->sinks, list_source) {
+			list_del(&p->list_sink);
+			list_del(&p->list_source);
+			list_del(&p->list);
+			kfree(p);
+		}
+		/* check and free and dynamic widget kcontrols */
+		snd_soc_tplg_widget_remove(w);
+		kfree(w->kcontrols);
+		kfree(w->name);
+		kfree(w);
+	}
+}
+EXPORT_SYMBOL_GPL(snd_soc_tplg_widget_remove_all);
+
+/* remove dynamic controls from the component driver */
+int snd_soc_tplg_component_remove(struct snd_soc_component *comp, u32 index)
+{
+	struct snd_soc_dobj *dobj, *next_dobj;
+	int pass = SOC_TPLG_PASS_END;
+
+	/* process the header types from end to start */
+	while (pass >= SOC_TPLG_PASS_START) {
+
+		/* remove mixer controls */
+		list_for_each_entry_safe(dobj, next_dobj, &comp->dobj_list,
+			list) {
+
+			/* match index */
+			if (dobj->index != index &&
+				dobj->index != SND_SOC_TPLG_INDEX_ALL)
+				continue;
+
+			switch (dobj->type) {
+			case SND_SOC_DOBJ_MIXER:
+				remove_mixer(comp, dobj, pass);
+				break;
+			case SND_SOC_DOBJ_ENUM:
+				remove_enum(comp, dobj, pass);
+				break;
+			case SND_SOC_DOBJ_BYTES:
+				remove_bytes(comp, dobj, pass);
+				break;
+			case SND_SOC_DOBJ_WIDGET:
+				remove_widget(comp, dobj, pass);
+				break;
+			case SND_SOC_DOBJ_PCM:
+			case SND_SOC_DOBJ_DAI_LINK:
+			case SND_SOC_DOBJ_CODEC_LINK:
+				remove_pcm_dai(comp, dobj, pass);
+				break;
+			default:
+				dev_err(comp->dev, "ASoC: invalid component type %d for removal\n",
+					dobj->type);
+				break;
+			}
+		}
+		pass--;
+	}
+
+	/* let caller know if FW can be freed when no objects are left */
+	return !list_empty(&comp->dobj_list);
+}
+EXPORT_SYMBOL_GPL(snd_soc_tplg_component_remove);
diff --git a/sound/soc/ux500/ux500_pcm.c b/sound/soc/ux500/ux500_pcm.c
index 51a66a87305a..f12c01dddc8d 100644
--- a/sound/soc/ux500/ux500_pcm.c
+++ b/sound/soc/ux500/ux500_pcm.c
@@ -147,7 +147,6 @@ int ux500_pcm_register_platform(struct platform_device *pdev)
 		pcm_config = &ux500_dmaengine_pcm_config;
 
 	ret = snd_dmaengine_pcm_register(&pdev->dev, pcm_config,
-					 SND_DMAENGINE_PCM_FLAG_NO_RESIDUE |
 					 SND_DMAENGINE_PCM_FLAG_COMPAT);
 	if (ret < 0) {
 		dev_err(&pdev->dev,
diff --git a/sound/soc/zte/Kconfig b/sound/soc/zte/Kconfig
new file mode 100644
index 000000000000..c47eb25e441f
--- /dev/null
+++ b/sound/soc/zte/Kconfig
@@ -0,0 +1,17 @@
+config ZX296702_SPDIF
+	tristate "ZX296702 spdif"
+	depends on SOC_ZX296702 || COMPILE_TEST
+	depends on COMMON_CLK
+	select SND_SOC_GENERIC_DMAENGINE_PCM
+	help
+	  Say Y or M if you want to add support for codecs attached to the
+	  zx296702 spdif interface
+
+config ZX296702_I2S
+	tristate "ZX296702 i2s"
+	depends on SOC_ZX296702 || COMPILE_TEST
+	depends on COMMON_CLK
+	select SND_SOC_GENERIC_DMAENGINE_PCM
+	help
+	  Say Y or M if you want to add support for codecs attached to the
+	  zx296702 i2s interface
diff --git a/sound/soc/zte/Makefile b/sound/soc/zte/Makefile
new file mode 100644
index 000000000000..254ed2c8c1a0
--- /dev/null
+++ b/sound/soc/zte/Makefile
@@ -0,0 +1,2 @@
+obj-$(CONFIG_ZX296702_SPDIF)	+= zx296702-spdif.o
+obj-$(CONFIG_ZX296702_I2S)	+= zx296702-i2s.o
diff --git a/sound/soc/zte/zx296702-i2s.c b/sound/soc/zte/zx296702-i2s.c
new file mode 100644
index 000000000000..98d96e1b17e0
--- /dev/null
+++ b/sound/soc/zte/zx296702-i2s.c
@@ -0,0 +1,436 @@
+/*
+ * Copyright (C) 2015 Linaro
+ *
+ * Author: Jun Nie <jun.nie@linaro.org>
+ *
+ * License terms: GNU General Public License (GPL) version 2
+ */
+
+#include <linux/clk.h>
+#include <linux/device.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dai.h>
+
+#include <sound/core.h>
+#include <sound/dmaengine_pcm.h>
+#include <sound/initval.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+
+#define ZX_I2S_PROCESS_CTRL	0x04
+#define ZX_I2S_TIMING_CTRL	0x08
+#define	ZX_I2S_FIFO_CTRL	0x0C
+#define	ZX_I2S_FIFO_STATUS	0x10
+#define ZX_I2S_INT_EN		0x14
+#define ZX_I2S_INT_STATUS	0x18
+#define ZX_I2S_DATA		0x1C
+#define ZX_I2S_FRAME_CNTR	0x20
+
+#define I2S_DEAGULT_FIFO_THRES	(0x10)
+#define I2S_MAX_FIFO_THRES	(0x20)
+
+#define ZX_I2S_PROCESS_TX_EN	(1 << 0)
+#define ZX_I2S_PROCESS_TX_DIS	(0 << 0)
+#define ZX_I2S_PROCESS_RX_EN	(1 << 1)
+#define ZX_I2S_PROCESS_RX_DIS	(0 << 1)
+#define ZX_I2S_PROCESS_I2S_EN	(1 << 2)
+#define ZX_I2S_PROCESS_I2S_DIS	(0 << 2)
+
+#define ZX_I2S_TIMING_MAST		(1 << 0)
+#define ZX_I2S_TIMING_SLAVE		(0 << 0)
+#define ZX_I2S_TIMING_MS_MASK		(1 << 0)
+#define ZX_I2S_TIMING_LOOP		(1 << 1)
+#define ZX_I2S_TIMING_NOR		(0 << 1)
+#define ZX_I2S_TIMING_LOOP_MASK		(1 << 1)
+#define ZX_I2S_TIMING_PTNR		(1 << 2)
+#define ZX_I2S_TIMING_NTPR		(0 << 2)
+#define ZX_I2S_TIMING_PHASE_MASK	(1 << 2)
+#define ZX_I2S_TIMING_TDM		(1 << 3)
+#define ZX_I2S_TIMING_I2S		(0 << 3)
+#define ZX_I2S_TIMING_TIMING_MASK	(1 << 3)
+#define ZX_I2S_TIMING_LONG_SYNC		(1 << 4)
+#define ZX_I2S_TIMING_SHORT_SYNC	(0 << 4)
+#define ZX_I2S_TIMING_SYNC_MASK		(1 << 4)
+#define ZX_I2S_TIMING_TEAK_EN		(1 << 5)
+#define ZX_I2S_TIMING_TEAK_DIS		(0 << 5)
+#define ZX_I2S_TIMING_TEAK_MASK		(1 << 5)
+#define ZX_I2S_TIMING_STD_I2S		(0 << 6)
+#define ZX_I2S_TIMING_MSB_JUSTIF	(1 << 6)
+#define ZX_I2S_TIMING_LSB_JUSTIF	(2 << 6)
+#define ZX_I2S_TIMING_ALIGN_MASK	(3 << 6)
+#define ZX_I2S_TIMING_CHN_MASK		(7 << 8)
+#define ZX_I2S_TIMING_CHN(x)		((x - 1) << 8)
+#define ZX_I2S_TIMING_LANE_MASK		(3 << 11)
+#define ZX_I2S_TIMING_LANE(x)		((x - 1) << 11)
+#define ZX_I2S_TIMING_TSCFG_MASK	(7 << 13)
+#define ZX_I2S_TIMING_TSCFG(x)		(x << 13)
+#define ZX_I2S_TIMING_TS_WIDTH_MASK	(0x1f << 16)
+#define ZX_I2S_TIMING_TS_WIDTH(x)	((x - 1) << 16)
+#define ZX_I2S_TIMING_DATA_SIZE_MASK	(0x1f << 21)
+#define ZX_I2S_TIMING_DATA_SIZE(x)	((x - 1) << 21)
+#define ZX_I2S_TIMING_CFG_ERR_MASK	(1 << 31)
+
+#define ZX_I2S_FIFO_CTRL_TX_RST		(1 << 0)
+#define ZX_I2S_FIFO_CTRL_TX_RST_MASK	(1 << 0)
+#define ZX_I2S_FIFO_CTRL_RX_RST		(1 << 1)
+#define ZX_I2S_FIFO_CTRL_RX_RST_MASK	(1 << 1)
+#define ZX_I2S_FIFO_CTRL_TX_DMA_EN	(1 << 4)
+#define ZX_I2S_FIFO_CTRL_TX_DMA_DIS	(0 << 4)
+#define ZX_I2S_FIFO_CTRL_TX_DMA_MASK	(1 << 4)
+#define ZX_I2S_FIFO_CTRL_RX_DMA_EN	(1 << 5)
+#define ZX_I2S_FIFO_CTRL_RX_DMA_DIS	(0 << 5)
+#define ZX_I2S_FIFO_CTRL_RX_DMA_MASK	(1 << 5)
+#define ZX_I2S_FIFO_CTRL_TX_THRES_MASK	(0x1F << 8)
+#define ZX_I2S_FIFO_CTRL_RX_THRES_MASK	(0x1F << 16)
+
+#define CLK_RAT (32 * 4)
+
+struct zx_i2s_info {
+	struct snd_dmaengine_dai_dma_data	dma_playback;
+	struct snd_dmaengine_dai_dma_data	dma_capture;
+	struct clk				*dai_clk;
+	void __iomem				*reg_base;
+	int					master;
+	resource_size_t				mapbase;
+};
+
+static void zx_i2s_tx_en(void __iomem *base, bool on)
+{
+	unsigned long val;
+
+	val = readl_relaxed(base + ZX_I2S_PROCESS_CTRL);
+	if (on)
+		val |= ZX_I2S_PROCESS_TX_EN | ZX_I2S_PROCESS_I2S_EN;
+	else
+		val &= ~(ZX_I2S_PROCESS_TX_EN | ZX_I2S_PROCESS_I2S_EN);
+	writel_relaxed(val, base + ZX_I2S_PROCESS_CTRL);
+}
+
+static void zx_i2s_rx_en(void __iomem *base, bool on)
+{
+	unsigned long val;
+
+	val = readl_relaxed(base + ZX_I2S_PROCESS_CTRL);
+	if (on)
+		val |= ZX_I2S_PROCESS_RX_EN | ZX_I2S_PROCESS_I2S_EN;
+	else
+		val &= ~(ZX_I2S_PROCESS_RX_EN | ZX_I2S_PROCESS_I2S_EN);
+	writel_relaxed(val, base + ZX_I2S_PROCESS_CTRL);
+}
+
+static void zx_i2s_tx_dma_en(void __iomem *base, bool on)
+{
+	unsigned long val;
+
+	val = readl_relaxed(base + ZX_I2S_FIFO_CTRL);
+	val |= ZX_I2S_FIFO_CTRL_TX_RST | (I2S_DEAGULT_FIFO_THRES << 8);
+	if (on)
+		val |= ZX_I2S_FIFO_CTRL_TX_DMA_EN;
+	else
+		val &= ~ZX_I2S_FIFO_CTRL_TX_DMA_EN;
+	writel_relaxed(val, base + ZX_I2S_FIFO_CTRL);
+}
+
+static void zx_i2s_rx_dma_en(void __iomem *base, bool on)
+{
+	unsigned long val;
+
+	val = readl_relaxed(base + ZX_I2S_FIFO_CTRL);
+	val |= ZX_I2S_FIFO_CTRL_RX_RST | (I2S_DEAGULT_FIFO_THRES << 16);
+	if (on)
+		val |= ZX_I2S_FIFO_CTRL_RX_DMA_EN;
+	else
+		val &= ~ZX_I2S_FIFO_CTRL_RX_DMA_EN;
+	writel_relaxed(val, base + ZX_I2S_FIFO_CTRL);
+}
+
+#define ZX_I2S_RATES \
+	(SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 | SNDRV_PCM_RATE_16000 | \
+	 SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | \
+	 SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000| \
+	 SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_192000)
+
+#define ZX_I2S_FMTBIT \
+	(SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE | \
+	SNDRV_PCM_FMTBIT_S32_LE)
+
+static int zx_i2s_dai_probe(struct snd_soc_dai *dai)
+{
+	struct zx_i2s_info *zx_i2s = dev_get_drvdata(dai->dev);
+
+	snd_soc_dai_set_drvdata(dai, zx_i2s);
+	zx_i2s->dma_playback.addr = zx_i2s->mapbase + ZX_I2S_DATA;
+	zx_i2s->dma_playback.maxburst = 16;
+	zx_i2s->dma_capture.addr = zx_i2s->mapbase + ZX_I2S_DATA;
+	zx_i2s->dma_capture.maxburst = 16;
+	snd_soc_dai_init_dma_data(dai, &zx_i2s->dma_playback,
+				  &zx_i2s->dma_capture);
+	return 0;
+}
+
+static int zx_i2s_set_fmt(struct snd_soc_dai *cpu_dai, unsigned int fmt)
+{
+	struct zx_i2s_info *i2s = snd_soc_dai_get_drvdata(cpu_dai);
+	unsigned long val;
+
+	val = readl_relaxed(i2s->reg_base + ZX_I2S_TIMING_CTRL);
+	val &= ~(ZX_I2S_TIMING_TIMING_MASK | ZX_I2S_TIMING_ALIGN_MASK |
+			ZX_I2S_TIMING_TEAK_MASK | ZX_I2S_TIMING_SYNC_MASK |
+			ZX_I2S_TIMING_MS_MASK);
+
+	switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+	case SND_SOC_DAIFMT_I2S:
+		val |= (ZX_I2S_TIMING_I2S | ZX_I2S_TIMING_STD_I2S);
+		break;
+	case SND_SOC_DAIFMT_LEFT_J:
+		val |= (ZX_I2S_TIMING_I2S | ZX_I2S_TIMING_MSB_JUSTIF);
+		break;
+	case SND_SOC_DAIFMT_RIGHT_J:
+		val |= (ZX_I2S_TIMING_I2S | ZX_I2S_TIMING_LSB_JUSTIF);
+		break;
+	default:
+		dev_err(cpu_dai->dev, "Unknown i2s timeing\n");
+		return -EINVAL;
+	}
+
+	switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+	case SND_SOC_DAIFMT_CBM_CFM:
+		i2s->master = 1;
+		val |= ZX_I2S_TIMING_MAST;
+		break;
+	case SND_SOC_DAIFMT_CBS_CFS:
+		i2s->master = 0;
+		val |= ZX_I2S_TIMING_SLAVE;
+		break;
+	default:
+		dev_err(cpu_dai->dev, "Unknown master/slave format\n");
+		return -EINVAL;
+	}
+
+	writel_relaxed(val, i2s->reg_base + ZX_I2S_TIMING_CTRL);
+	return 0;
+}
+
+static int zx_i2s_hw_params(struct snd_pcm_substream *substream,
+			    struct snd_pcm_hw_params *params,
+			    struct snd_soc_dai *socdai)
+{
+	struct zx_i2s_info *i2s = snd_soc_dai_get_drvdata(socdai);
+	struct snd_dmaengine_dai_dma_data *dma_data;
+	unsigned int lane, ch_num, len, ret = 0;
+	unsigned long val, format;
+	unsigned long chn_cfg;
+
+	dma_data = snd_soc_dai_get_dma_data(socdai, substream);
+	dma_data->addr_width = params_width(params) >> 3;
+
+	val = readl_relaxed(i2s->reg_base + ZX_I2S_TIMING_CTRL);
+	val &= ~(ZX_I2S_TIMING_TS_WIDTH_MASK | ZX_I2S_TIMING_DATA_SIZE_MASK |
+		ZX_I2S_TIMING_LANE_MASK | ZX_I2S_TIMING_CHN_MASK |
+		ZX_I2S_TIMING_TSCFG_MASK);
+
+	switch (params_format(params)) {
+	case SNDRV_PCM_FORMAT_S16_LE:
+		format = 0;
+		len = 16;
+		break;
+	case SNDRV_PCM_FORMAT_S24_LE:
+		format = 1;
+		len = 24;
+		break;
+	case SNDRV_PCM_FORMAT_S32_LE:
+		format = 2;
+		len = 32;
+		break;
+	default:
+		dev_err(socdai->dev, "Unknown data format\n");
+		return -EINVAL;
+	}
+	val |= ZX_I2S_TIMING_TS_WIDTH(len) | ZX_I2S_TIMING_DATA_SIZE(len);
+
+	ch_num = params_channels(params);
+	switch (ch_num) {
+	case 1:
+		lane = 1;
+		chn_cfg = 2;
+		break;
+	case 2:
+	case 4:
+	case 6:
+	case 8:
+		lane = ch_num / 2;
+		chn_cfg = 3;
+		break;
+	default:
+		dev_err(socdai->dev, "Not support channel num %d\n", ch_num);
+		return -EINVAL;
+	}
+	val |= ZX_I2S_TIMING_LANE(lane);
+	val |= ZX_I2S_TIMING_TSCFG(chn_cfg);
+	val |= ZX_I2S_TIMING_CHN(ch_num);
+	writel_relaxed(val, i2s->reg_base + ZX_I2S_TIMING_CTRL);
+
+	if (i2s->master)
+		ret = clk_set_rate(i2s->dai_clk,
+				   params_rate(params) * ch_num * CLK_RAT);
+	return ret;
+}
+
+static int zx_i2s_trigger(struct snd_pcm_substream *substream, int cmd,
+			  struct snd_soc_dai *dai)
+{
+	struct zx_i2s_info *zx_i2s = dev_get_drvdata(dai->dev);
+	int capture = (substream->stream == SNDRV_PCM_STREAM_CAPTURE);
+	int ret = 0;
+
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+		if (capture)
+			zx_i2s_rx_dma_en(zx_i2s->reg_base, true);
+		else
+			zx_i2s_tx_dma_en(zx_i2s->reg_base, true);
+	/* fall thru */
+	case SNDRV_PCM_TRIGGER_RESUME:
+	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+		if (capture)
+			zx_i2s_rx_en(zx_i2s->reg_base, true);
+		else
+			zx_i2s_tx_en(zx_i2s->reg_base, true);
+		break;
+
+	case SNDRV_PCM_TRIGGER_STOP:
+		if (capture)
+			zx_i2s_rx_dma_en(zx_i2s->reg_base, false);
+		else
+			zx_i2s_tx_dma_en(zx_i2s->reg_base, false);
+	/* fall thru */
+	case SNDRV_PCM_TRIGGER_SUSPEND:
+	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+		if (capture)
+			zx_i2s_rx_en(zx_i2s->reg_base, false);
+		else
+			zx_i2s_tx_en(zx_i2s->reg_base, false);
+		break;
+
+	default:
+		ret = -EINVAL;
+		break;
+	}
+
+	return ret;
+}
+
+static int zx_i2s_startup(struct snd_pcm_substream *substream,
+			  struct snd_soc_dai *dai)
+{
+	struct zx_i2s_info *zx_i2s = dev_get_drvdata(dai->dev);
+
+	return clk_prepare_enable(zx_i2s->dai_clk);
+}
+
+static void zx_i2s_shutdown(struct snd_pcm_substream *substream,
+			    struct snd_soc_dai *dai)
+{
+	struct zx_i2s_info *zx_i2s = dev_get_drvdata(dai->dev);
+
+	clk_disable_unprepare(zx_i2s->dai_clk);
+}
+
+static struct snd_soc_dai_ops zx_i2s_dai_ops = {
+	.trigger	= zx_i2s_trigger,
+	.hw_params	= zx_i2s_hw_params,
+	.set_fmt	= zx_i2s_set_fmt,
+	.startup	= zx_i2s_startup,
+	.shutdown	= zx_i2s_shutdown,
+};
+
+static const struct snd_soc_component_driver zx_i2s_component = {
+	.name			= "zx-i2s",
+};
+
+static struct snd_soc_dai_driver zx_i2s_dai = {
+	.name	= "zx-i2s-dai",
+	.id	= 0,
+	.probe	= zx_i2s_dai_probe,
+	.playback   = {
+		.channels_min	= 1,
+		.channels_max	= 8,
+		.rates		= ZX_I2S_RATES,
+		.formats	= ZX_I2S_FMTBIT,
+	},
+	.capture = {
+		.channels_min	= 1,
+		.channels_max	= 2,
+		.rates		= ZX_I2S_RATES,
+		.formats	= ZX_I2S_FMTBIT,
+	},
+	.ops	= &zx_i2s_dai_ops,
+};
+
+static int zx_i2s_probe(struct platform_device *pdev)
+{
+	struct resource *res;
+	struct zx_i2s_info *zx_i2s;
+	int ret;
+
+	zx_i2s =  kzalloc(sizeof(*zx_i2s), GFP_KERNEL);
+	if (!zx_i2s)
+		return -ENOMEM;
+
+	zx_i2s->dai_clk = devm_clk_get(&pdev->dev, "tx");
+	if (IS_ERR(zx_i2s->dai_clk)) {
+		dev_err(&pdev->dev, "Fail to get clk\n");
+		return PTR_ERR(zx_i2s->dai_clk);
+	}
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	zx_i2s->mapbase = res->start;
+	zx_i2s->reg_base = devm_ioremap_resource(&pdev->dev, res);
+	if (!zx_i2s->reg_base) {
+		dev_err(&pdev->dev, "ioremap failed!\n");
+		return -EIO;
+	}
+
+	writel_relaxed(0, zx_i2s->reg_base + ZX_I2S_FIFO_CTRL);
+	platform_set_drvdata(pdev, zx_i2s);
+
+	ret = snd_soc_register_component(&pdev->dev, &zx_i2s_component,
+					 &zx_i2s_dai, 1);
+	if (ret) {
+		dev_err(&pdev->dev, "Register DAI failed: %d\n", ret);
+		return ret;
+	}
+
+	ret = devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, 0);
+	if (ret)
+		dev_err(&pdev->dev, "Register platform PCM failed: %d\n", ret);
+
+	return ret;
+}
+
+static const struct of_device_id zx_i2s_dt_ids[] = {
+	{ .compatible = "zte,zx296702-i2s", },
+	{}
+};
+MODULE_DEVICE_TABLE(of, zx_i2s_dt_ids);
+
+static struct platform_driver i2s_driver = {
+	.probe = zx_i2s_probe,
+	.driver = {
+		.name = "zx-i2s",
+		.of_match_table = zx_i2s_dt_ids,
+	},
+};
+
+module_platform_driver(i2s_driver);
+
+MODULE_AUTHOR("Jun Nie <jun.nie@linaro.org>");
+MODULE_DESCRIPTION("ZTE I2S SoC DAI");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/zte/zx296702-spdif.c b/sound/soc/zte/zx296702-spdif.c
new file mode 100644
index 000000000000..11a0e46a1156
--- /dev/null
+++ b/sound/soc/zte/zx296702-spdif.c
@@ -0,0 +1,365 @@
+/*
+ * Copyright (C) 2015 Linaro
+ *
+ * Author: Jun Nie <jun.nie@linaro.org>
+ *
+ * License terms: GNU General Public License (GPL) version 2
+ */
+
+#include <linux/clk.h>
+#include <linux/device.h>
+#include <linux/dmaengine.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of_address.h>
+#include <sound/asoundef.h>
+#include <sound/core.h>
+#include <sound/dmaengine_pcm.h>
+#include <sound/initval.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dai.h>
+
+#define ZX_CTRL				0x04
+#define ZX_FIFOCTRL			0x08
+#define ZX_INT_STATUS			0x10
+#define ZX_INT_MASK			0x14
+#define ZX_DATA				0x18
+#define ZX_VALID_BIT			0x1c
+#define ZX_CH_STA_1			0x20
+#define ZX_CH_STA_2			0x24
+#define ZX_CH_STA_3			0x28
+#define ZX_CH_STA_4			0x2c
+#define ZX_CH_STA_5			0x30
+#define ZX_CH_STA_6			0x34
+
+#define ZX_CTRL_MODA_16			(0 << 6)
+#define ZX_CTRL_MODA_18			BIT(6)
+#define ZX_CTRL_MODA_20			(2 << 6)
+#define ZX_CTRL_MODA_24			(3 << 6)
+#define ZX_CTRL_MODA_MASK		(3 << 6)
+
+#define ZX_CTRL_ENB			BIT(4)
+#define ZX_CTRL_DNB			(0 << 4)
+#define ZX_CTRL_ENB_MASK		BIT(4)
+
+#define ZX_CTRL_TX_OPEN			BIT(0)
+#define ZX_CTRL_TX_CLOSE		(0 << 0)
+#define ZX_CTRL_TX_MASK			BIT(0)
+
+#define ZX_CTRL_OPEN			(ZX_CTRL_TX_OPEN | ZX_CTRL_ENB)
+#define ZX_CTRL_CLOSE			(ZX_CTRL_TX_CLOSE | ZX_CTRL_DNB)
+
+#define ZX_CTRL_DOUBLE_TRACK		(0 << 8)
+#define ZX_CTRL_LEFT_TRACK		BIT(8)
+#define ZX_CTRL_RIGHT_TRACK		(2 << 8)
+#define ZX_CTRL_TRACK_MASK		(3 << 8)
+
+#define ZX_FIFOCTRL_TXTH_MASK		(0x1f << 8)
+#define ZX_FIFOCTRL_TXTH(x)		(x << 8)
+#define ZX_FIFOCTRL_TX_DMA_EN		BIT(2)
+#define ZX_FIFOCTRL_TX_DMA_DIS		(0 << 2)
+#define ZX_FIFOCTRL_TX_DMA_EN_MASK	BIT(2)
+#define ZX_FIFOCTRL_TX_FIFO_RST		BIT(0)
+#define ZX_FIFOCTRL_TX_FIFO_RST_MASK	BIT(0)
+
+#define ZX_VALID_DOUBLE_TRACK		(0 << 0)
+#define ZX_VALID_LEFT_TRACK		BIT(1)
+#define ZX_VALID_RIGHT_TRACK		(2 << 0)
+#define ZX_VALID_TRACK_MASK		(3 << 0)
+
+#define ZX_SPDIF_CLK_RAT		(4 * 32)
+
+struct zx_spdif_info {
+	struct snd_dmaengine_dai_dma_data	dma_data;
+	struct clk				*dai_clk;
+	void __iomem				*reg_base;
+	resource_size_t				mapbase;
+};
+
+static int zx_spdif_dai_probe(struct snd_soc_dai *dai)
+{
+	struct zx_spdif_info *zx_spdif = dev_get_drvdata(dai->dev);
+
+	snd_soc_dai_set_drvdata(dai, zx_spdif);
+	zx_spdif->dma_data.addr = zx_spdif->mapbase + ZX_DATA;
+	zx_spdif->dma_data.maxburst = 8;
+	snd_soc_dai_init_dma_data(dai, &zx_spdif->dma_data, NULL);
+	return 0;
+}
+
+static int zx_spdif_chanstats(void __iomem *base, unsigned int rate)
+{
+	u32 cstas1;
+
+	switch (rate) {
+	case 22050:
+		cstas1 = IEC958_AES3_CON_FS_22050;
+		break;
+	case 24000:
+		cstas1 = IEC958_AES3_CON_FS_24000;
+		break;
+	case 32000:
+		cstas1 = IEC958_AES3_CON_FS_32000;
+		break;
+	case 44100:
+		cstas1 = IEC958_AES3_CON_FS_44100;
+		break;
+	case 48000:
+		cstas1 = IEC958_AES3_CON_FS_48000;
+		break;
+	case 88200:
+		cstas1 = IEC958_AES3_CON_FS_88200;
+		break;
+	case 96000:
+		cstas1 = IEC958_AES3_CON_FS_96000;
+		break;
+	case 176400:
+		cstas1 = IEC958_AES3_CON_FS_176400;
+		break;
+	case 192000:
+		cstas1 = IEC958_AES3_CON_FS_192000;
+		break;
+	default:
+		return -EINVAL;
+	}
+	cstas1 = cstas1 << 24;
+	cstas1 |= IEC958_AES0_CON_NOT_COPYRIGHT;
+
+	writel_relaxed(cstas1, base + ZX_CH_STA_1);
+	return 0;
+}
+
+static int zx_spdif_hw_params(struct snd_pcm_substream *substream,
+			      struct snd_pcm_hw_params *params,
+			      struct snd_soc_dai *socdai)
+{
+	struct zx_spdif_info *zx_spdif = dev_get_drvdata(socdai->dev);
+	struct zx_spdif_info *spdif = snd_soc_dai_get_drvdata(socdai);
+	struct snd_dmaengine_dai_dma_data *dma_data = &zx_spdif->dma_data;
+	u32 val, ch_num, rate;
+	int ret;
+
+	dma_data = snd_soc_dai_get_dma_data(socdai, substream);
+	dma_data->addr_width = params_width(params) >> 3;
+
+	val = readl_relaxed(zx_spdif->reg_base + ZX_CTRL);
+	val &= ~ZX_CTRL_MODA_MASK;
+	switch (params_format(params)) {
+	case SNDRV_PCM_FORMAT_S16_LE:
+		val |= ZX_CTRL_MODA_16;
+		break;
+
+	case SNDRV_PCM_FORMAT_S18_3LE:
+		val |= ZX_CTRL_MODA_18;
+		break;
+
+	case SNDRV_PCM_FORMAT_S20_3LE:
+		val |= ZX_CTRL_MODA_20;
+		break;
+
+	case SNDRV_PCM_FORMAT_S24_LE:
+		val |= ZX_CTRL_MODA_24;
+		break;
+	default:
+		dev_err(socdai->dev, "Format not support!\n");
+		return -EINVAL;
+	}
+
+	ch_num = params_channels(params);
+	if (ch_num == 2)
+		val |= ZX_CTRL_DOUBLE_TRACK;
+	else
+		val |= ZX_CTRL_LEFT_TRACK;
+	writel_relaxed(val, zx_spdif->reg_base + ZX_CTRL);
+
+	val = readl_relaxed(zx_spdif->reg_base + ZX_VALID_BIT);
+	val &= ~ZX_VALID_TRACK_MASK;
+	if (ch_num == 2)
+		val |= ZX_VALID_DOUBLE_TRACK;
+	else
+		val |= ZX_VALID_RIGHT_TRACK;
+	writel_relaxed(val, zx_spdif->reg_base + ZX_VALID_BIT);
+
+	rate = params_rate(params);
+	ret = zx_spdif_chanstats(zx_spdif->reg_base, rate);
+	if (ret)
+		return ret;
+	return clk_set_rate(spdif->dai_clk, rate * ch_num * ZX_SPDIF_CLK_RAT);
+}
+
+static void zx_spdif_cfg_tx(void __iomem *base, int on)
+{
+	u32 val;
+
+	val = readl_relaxed(base + ZX_CTRL);
+	val &= ~(ZX_CTRL_ENB_MASK | ZX_CTRL_TX_MASK);
+	val |= on ? ZX_CTRL_OPEN : ZX_CTRL_CLOSE;
+	writel_relaxed(val, base + ZX_CTRL);
+
+	val = readl_relaxed(base + ZX_FIFOCTRL);
+	val &= ~ZX_FIFOCTRL_TX_DMA_EN_MASK;
+	if (on)
+		val |= ZX_FIFOCTRL_TX_DMA_EN;
+	writel_relaxed(val, base + ZX_FIFOCTRL);
+}
+
+static int zx_spdif_trigger(struct snd_pcm_substream *substream, int cmd,
+			    struct snd_soc_dai *dai)
+{
+	u32 val;
+	struct zx_spdif_info *zx_spdif = dev_get_drvdata(dai->dev);
+	int  ret = 0;
+
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+		val = readl_relaxed(zx_spdif->reg_base + ZX_FIFOCTRL);
+		val |= ZX_FIFOCTRL_TX_FIFO_RST;
+		writel_relaxed(val, zx_spdif->reg_base + ZX_FIFOCTRL);
+	/* fall thru */
+	case SNDRV_PCM_TRIGGER_RESUME:
+	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+		zx_spdif_cfg_tx(zx_spdif->reg_base, true);
+		break;
+
+	case SNDRV_PCM_TRIGGER_STOP:
+	case SNDRV_PCM_TRIGGER_SUSPEND:
+	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+		zx_spdif_cfg_tx(zx_spdif->reg_base, false);
+		break;
+
+	default:
+		ret = -EINVAL;
+		break;
+	}
+
+	return ret;
+}
+
+static int zx_spdif_startup(struct snd_pcm_substream *substream,
+			    struct snd_soc_dai *dai)
+{
+	struct zx_spdif_info *zx_spdif = dev_get_drvdata(dai->dev);
+
+	return clk_prepare_enable(zx_spdif->dai_clk);
+}
+
+static void zx_spdif_shutdown(struct snd_pcm_substream *substream,
+			      struct snd_soc_dai *dai)
+{
+	struct zx_spdif_info *zx_spdif = dev_get_drvdata(dai->dev);
+
+	clk_disable_unprepare(zx_spdif->dai_clk);
+}
+
+#define ZX_RATES \
+	(SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | \
+	SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000 |\
+	SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_192000)
+
+#define ZX_FORMAT \
+	(SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S18_3LE \
+	| SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S24_LE)
+
+static struct snd_soc_dai_ops zx_spdif_dai_ops = {
+	.trigger	= zx_spdif_trigger,
+	.startup	= zx_spdif_startup,
+	.shutdown	= zx_spdif_shutdown,
+	.hw_params	= zx_spdif_hw_params,
+};
+
+static struct snd_soc_dai_driver zx_spdif_dai = {
+	.name = "spdif",
+	.id = 0,
+	.probe = zx_spdif_dai_probe,
+	.playback = {
+		.channels_min = 1,
+		.channels_max = 2,
+		.rates = ZX_RATES,
+		.formats = ZX_FORMAT,
+	},
+	.ops = &zx_spdif_dai_ops,
+};
+
+static const struct snd_soc_component_driver zx_spdif_component = {
+	.name	= "spdif",
+};
+
+static void zx_spdif_dev_init(void __iomem *base)
+{
+	u32 val;
+
+	writel_relaxed(0, base + ZX_CTRL);
+	writel_relaxed(0, base + ZX_INT_MASK);
+	writel_relaxed(0xf, base + ZX_INT_STATUS);
+	writel_relaxed(0x1, base + ZX_FIFOCTRL);
+
+	val = readl_relaxed(base + ZX_FIFOCTRL);
+	val &= ~(ZX_FIFOCTRL_TXTH_MASK | ZX_FIFOCTRL_TX_FIFO_RST_MASK);
+	val |= ZX_FIFOCTRL_TXTH(8);
+	writel_relaxed(val, base + ZX_FIFOCTRL);
+}
+
+static int zx_spdif_probe(struct platform_device *pdev)
+{
+	struct resource *res;
+	struct zx_spdif_info *zx_spdif;
+	int ret;
+
+	zx_spdif = devm_kzalloc(&pdev->dev, sizeof(*zx_spdif), GFP_KERNEL);
+	if (!zx_spdif)
+		return -ENOMEM;
+
+	zx_spdif->dai_clk = devm_clk_get(&pdev->dev, "tx");
+	if (IS_ERR(zx_spdif->dai_clk)) {
+		dev_err(&pdev->dev, "Fail to get clk\n");
+		return PTR_ERR(zx_spdif->dai_clk);
+	}
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	zx_spdif->mapbase = res->start;
+	zx_spdif->reg_base = devm_ioremap_resource(&pdev->dev, res);
+	if (!zx_spdif->reg_base) {
+		dev_err(&pdev->dev, "ioremap failed!\n");
+		return -EIO;
+	}
+
+	zx_spdif_dev_init(zx_spdif->reg_base);
+	platform_set_drvdata(pdev, zx_spdif);
+
+	ret = devm_snd_soc_register_component(&pdev->dev, &zx_spdif_component,
+					 &zx_spdif_dai, 1);
+	if (ret) {
+		dev_err(&pdev->dev, "Register DAI failed: %d\n", ret);
+		return ret;
+	}
+
+	ret = devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, 0);
+	if (ret)
+		dev_err(&pdev->dev, "Register platform PCM failed: %d\n", ret);
+
+	return ret;
+}
+
+static const struct of_device_id zx_spdif_dt_ids[] = {
+	{ .compatible = "zte,zx296702-spdif", },
+	{}
+};
+MODULE_DEVICE_TABLE(of, zx_spdif_dt_ids);
+
+static struct platform_driver spdif_driver = {
+	.probe = zx_spdif_probe,
+	.driver = {
+		.name = "zx-spdif",
+		.of_match_table = zx_spdif_dt_ids,
+	},
+};
+
+module_platform_driver(spdif_driver);
+
+MODULE_AUTHOR("Jun Nie <jun.nie@linaro.org>");
+MODULE_DESCRIPTION("ZTE SPDIF SoC DAI");
+MODULE_LICENSE("GPL");