summary refs log tree commit diff
path: root/sound/soc
diff options
context:
space:
mode:
authorTakashi Iwai <tiwai@suse.de>2014-12-08 15:04:02 +0100
committerTakashi Iwai <tiwai@suse.de>2014-12-08 15:04:02 +0100
commite5edba464c11d9d42550f9e3ff97f25196ba50b2 (patch)
tree269a640ff93c68db724080f73b0e267e024af082 /sound/soc
parent77de61c3975da6f2200935c341e84018ece6ce36 (diff)
parent1810afd3e1ded09c53d4e966dddce3c7d484521f (diff)
downloadlinux-e5edba464c11d9d42550f9e3ff97f25196ba50b2.tar.gz
Merge tag 'asoc-v3.19' of git://git.kernel.org/pub/scm/linux/kernel/git/broonie/sound into for-linus
ASoC: Updates for v3.19

Lots and lots of changes this time around, the usual set of driver
updates and a huge bulk of cleanups from Lars-Peter.  Probably the most
interesting thing for most users is the Intel driver updates which will
(with some more machine integration work) enable support for newer x86
laptops.

 - Conversion of AC'97 drivers to use regmap, bringing us closer to the
   removal of the ASoC level I/O code.
 - Clean up a lot of old drivers that were open coding things that have
   subsequently been implemented in the core.
 - Some DAPM performance improvements.
 - Removal of the now seldom used CODEC mutex.
 - Lots of updates for the newer Intel SoC support, including support
   for the DSP and some Cherrytrail and Braswell machine drivers.
 - Support for Samsung boards using rt5631 as the CODEC.
 - Removal of the obsolete AFEB9260 machine driver.
 - Driver support for the TI TS3A227E headset driver used in some
   Chrombeooks.
Diffstat (limited to 'sound/soc')
-rw-r--r--sound/soc/Makefile6
-rw-r--r--sound/soc/atmel/Kconfig9
-rw-r--r--sound/soc/atmel/Makefile1
-rw-r--r--sound/soc/atmel/atmel_ssc_dai.c5
-rw-r--r--sound/soc/atmel/snd-soc-afeb9260.c151
-rw-r--r--sound/soc/au1x/ac97c.c2
-rw-r--r--sound/soc/au1x/psc-ac97.c2
-rw-r--r--sound/soc/blackfin/bf5xx-ac97.c2
-rw-r--r--sound/soc/blackfin/bf5xx-ad1980.c2
-rw-r--r--sound/soc/cirrus/Kconfig3
-rw-r--r--sound/soc/cirrus/ep93xx-ac97.c2
-rw-r--r--sound/soc/codecs/Kconfig47
-rw-r--r--sound/soc/codecs/Makefile10
-rw-r--r--sound/soc/codecs/ab8500-codec.c32
-rw-r--r--sound/soc/codecs/ac97.c18
-rw-r--r--sound/soc/codecs/ad193x.c14
-rw-r--r--sound/soc/codecs/ad1980.c212
-rw-r--r--sound/soc/codecs/ad1980.h26
-rw-r--r--sound/soc/codecs/adau1373.c6
-rw-r--r--sound/soc/codecs/adau1701.c86
-rw-r--r--sound/soc/codecs/adau1761.c25
-rw-r--r--sound/soc/codecs/adau1781.c33
-rw-r--r--sound/soc/codecs/adau17x1.c71
-rw-r--r--sound/soc/codecs/adau17x1.h10
-rw-r--r--sound/soc/codecs/adav80x.c4
-rw-r--r--sound/soc/codecs/ak4535.c31
-rw-r--r--sound/soc/codecs/ak4641.c33
-rw-r--r--sound/soc/codecs/ak4642.c16
-rw-r--r--sound/soc/codecs/ak4671.c13
-rw-r--r--sound/soc/codecs/alc5623.c22
-rw-r--r--sound/soc/codecs/alc5632.c22
-rw-r--r--sound/soc/codecs/arizona.c34
-rw-r--r--sound/soc/codecs/cq93vc.c33
-rw-r--r--sound/soc/codecs/cs4265.c2
-rw-r--r--sound/soc/codecs/cs4271-i2c.c62
-rw-r--r--sound/soc/codecs/cs4271-spi.c55
-rw-r--r--sound/soc/codecs/cs4271.c155
-rw-r--r--sound/soc/codecs/cs4271.h11
-rw-r--r--sound/soc/codecs/cs42l51.c6
-rw-r--r--sound/soc/codecs/cs42l73.c6
-rw-r--r--sound/soc/codecs/hdmi.c2
-rw-r--r--sound/soc/codecs/lm49453.c8
-rw-r--r--sound/soc/codecs/max98088.c31
-rw-r--r--sound/soc/codecs/max98090.c201
-rw-r--r--sound/soc/codecs/max98090.h8
-rw-r--r--sound/soc/codecs/max98095.c23
-rw-r--r--sound/soc/codecs/max9850.c22
-rw-r--r--sound/soc/codecs/rt286.c231
-rw-r--r--sound/soc/codecs/rt5631.c38
-rw-r--r--sound/soc/codecs/rt5645.c200
-rw-r--r--sound/soc/codecs/rt5645.h19
-rw-r--r--sound/soc/codecs/rt5670.c136
-rw-r--r--sound/soc/codecs/rt5670.h6
-rw-r--r--sound/soc/codecs/rt5677-spi.c130
-rw-r--r--sound/soc/codecs/rt5677-spi.h21
-rw-r--r--sound/soc/codecs/rt5677.c1198
-rw-r--r--sound/soc/codecs/rt5677.h162
-rw-r--r--sound/soc/codecs/sgtl5000.c111
-rw-r--r--sound/soc/codecs/sigmadsp-i2c.c81
-rw-r--r--sound/soc/codecs/sigmadsp-regmap.c46
-rw-r--r--sound/soc/codecs/sigmadsp.c711
-rw-r--r--sound/soc/codecs/sigmadsp.h59
-rw-r--r--sound/soc/codecs/sirf-audio-codec.c6
-rw-r--r--sound/soc/codecs/sn95031.c15
-rw-r--r--sound/soc/codecs/ssm4567.c128
-rw-r--r--sound/soc/codecs/sta32x.c21
-rw-r--r--sound/soc/codecs/sta350.c21
-rw-r--r--sound/soc/codecs/sta529.c35
-rw-r--r--sound/soc/codecs/stac9766.c60
-rw-r--r--sound/soc/codecs/tas2552.c10
-rw-r--r--sound/soc/codecs/tfa9879.c328
-rw-r--r--sound/soc/codecs/tfa9879.h202
-rw-r--r--sound/soc/codecs/tlv320aic23.c21
-rw-r--r--sound/soc/codecs/tlv320aic31xx.c31
-rw-r--r--sound/soc/codecs/tlv320aic32x4.c24
-rw-r--r--sound/soc/codecs/tlv320aic3x.c228
-rw-r--r--sound/soc/codecs/tlv320aic3x.h1
-rw-r--r--sound/soc/codecs/tlv320dac33.c2
-rw-r--r--sound/soc/codecs/ts3a227e.c314
-rw-r--r--sound/soc/codecs/ts3a227e.h17
-rw-r--r--sound/soc/codecs/twl4030.c2
-rw-r--r--sound/soc/codecs/twl6040.c23
-rw-r--r--sound/soc/codecs/uda134x.c32
-rw-r--r--sound/soc/codecs/uda1380.c20
-rw-r--r--sound/soc/codecs/wl1273.c10
-rw-r--r--sound/soc/codecs/wm5102.c18
-rw-r--r--sound/soc/codecs/wm8350.c21
-rw-r--r--sound/soc/codecs/wm8400.c34
-rw-r--r--sound/soc/codecs/wm8510.c26
-rw-r--r--sound/soc/codecs/wm8523.c29
-rw-r--r--sound/soc/codecs/wm8580.c4
-rw-r--r--sound/soc/codecs/wm8711.c27
-rw-r--r--sound/soc/codecs/wm8728.c34
-rw-r--r--sound/soc/codecs/wm8731.c37
-rw-r--r--sound/soc/codecs/wm8737.c49
-rw-r--r--sound/soc/codecs/wm8750.c25
-rw-r--r--sound/soc/codecs/wm8776.c31
-rw-r--r--sound/soc/codecs/wm8804.c3
-rw-r--r--sound/soc/codecs/wm8900.c8
-rw-r--r--sound/soc/codecs/wm8903.c43
-rw-r--r--sound/soc/codecs/wm8940.c22
-rw-r--r--sound/soc/codecs/wm8955.c33
-rw-r--r--sound/soc/codecs/wm8958-dsp2.c12
-rw-r--r--sound/soc/codecs/wm8960.c113
-rw-r--r--sound/soc/codecs/wm8961.c34
-rw-r--r--sound/soc/codecs/wm8962.c11
-rw-r--r--sound/soc/codecs/wm8974.c25
-rw-r--r--sound/soc/codecs/wm8978.c10
-rw-r--r--sound/soc/codecs/wm8983.c27
-rw-r--r--sound/soc/codecs/wm8985.c28
-rw-r--r--sound/soc/codecs/wm8988.c27
-rw-r--r--sound/soc/codecs/wm8990.c24
-rw-r--r--sound/soc/codecs/wm8991.c32
-rw-r--r--sound/soc/codecs/wm8993.c12
-rw-r--r--sound/soc/codecs/wm8994.c4
-rw-r--r--sound/soc/codecs/wm8994.h2
-rw-r--r--sound/soc/codecs/wm8995.c17
-rw-r--r--sound/soc/codecs/wm9081.c7
-rw-r--r--sound/soc/codecs/wm9090.c32
-rw-r--r--sound/soc/codecs/wm9705.c46
-rw-r--r--sound/soc/codecs/wm9712.c219
-rw-r--r--sound/soc/codecs/wm9713.c230
-rw-r--r--sound/soc/codecs/wm_adsp.c97
-rw-r--r--sound/soc/davinci/davinci-mcasp.c339
-rw-r--r--sound/soc/davinci/davinci-mcasp.h17
-rw-r--r--sound/soc/dwc/designware_i2s.c46
-rw-r--r--sound/soc/fsl/eukrea-tlv320.c5
-rw-r--r--sound/soc/fsl/fsl-asoc-card.c19
-rw-r--r--sound/soc/fsl/fsl_esai.c12
-rw-r--r--sound/soc/fsl/fsl_ssi.c17
-rw-r--r--sound/soc/fsl/imx-sgtl5000.c6
-rw-r--r--sound/soc/fsl/imx-spdif.c3
-rw-r--r--sound/soc/fsl/imx-ssi.c2
-rw-r--r--sound/soc/fsl/imx-wm8962.c6
-rw-r--r--sound/soc/fsl/mpc5200_dma.c3
-rw-r--r--sound/soc/fsl/mpc5200_psc_ac97.c6
-rw-r--r--sound/soc/generic/simple-card.c160
-rw-r--r--sound/soc/intel/Kconfig44
-rw-r--r--sound/soc/intel/Makefile7
-rw-r--r--sound/soc/intel/broadwell.c64
-rw-r--r--sound/soc/intel/bytcr_dpcm_rt5640.c230
-rw-r--r--sound/soc/intel/cht_bsw_rt5672.c285
-rw-r--r--sound/soc/intel/haswell.c14
-rw-r--r--sound/soc/intel/sst-atom-controls.c1208
-rw-r--r--sound/soc/intel/sst-atom-controls.h428
-rw-r--r--sound/soc/intel/sst-baytrail-dsp.c24
-rw-r--r--sound/soc/intel/sst-dsp-priv.h136
-rw-r--r--sound/soc/intel/sst-dsp.c31
-rw-r--r--sound/soc/intel/sst-dsp.h28
-rw-r--r--sound/soc/intel/sst-firmware.c937
-rw-r--r--sound/soc/intel/sst-haswell-dsp.c295
-rw-r--r--sound/soc/intel/sst-haswell-ipc.c413
-rw-r--r--sound/soc/intel/sst-haswell-ipc.h25
-rw-r--r--sound/soc/intel/sst-haswell-pcm.c419
-rw-r--r--sound/soc/intel/sst-mfld-platform-compress.c8
-rw-r--r--sound/soc/intel/sst-mfld-platform-pcm.c154
-rw-r--r--sound/soc/intel/sst-mfld-platform.h5
-rw-r--r--sound/soc/intel/sst/Makefile7
-rw-r--r--sound/soc/intel/sst/sst.c437
-rw-r--r--sound/soc/intel/sst/sst.h546
-rw-r--r--sound/soc/intel/sst/sst_acpi.c383
-rw-r--r--sound/soc/intel/sst/sst_drv_interface.c686
-rw-r--r--sound/soc/intel/sst/sst_ipc.c373
-rw-r--r--sound/soc/intel/sst/sst_loader.c456
-rw-r--r--sound/soc/intel/sst/sst_pci.c209
-rw-r--r--sound/soc/intel/sst/sst_pvt.c449
-rw-r--r--sound/soc/intel/sst/sst_stream.c437
-rw-r--r--sound/soc/jz4740/qi_lb60.c11
-rw-r--r--sound/soc/mxs/mxs-saif.c2
-rw-r--r--sound/soc/mxs/mxs-sgtl5000.c7
-rw-r--r--sound/soc/nuc900/nuc900-ac97.c2
-rw-r--r--sound/soc/omap/Kconfig8
-rw-r--r--sound/soc/omap/mcbsp.c3
-rw-r--r--sound/soc/pxa/mioa701_wm9713.c7
-rw-r--r--sound/soc/pxa/pxa-ssp.c16
-rw-r--r--sound/soc/pxa/pxa2xx-ac97.c6
-rw-r--r--sound/soc/pxa/spitz.c52
-rw-r--r--sound/soc/rockchip/Kconfig9
-rw-r--r--sound/soc/samsung/Kconfig8
-rw-r--r--sound/soc/samsung/Makefile2
-rw-r--r--sound/soc/samsung/ac97.c4
-rw-r--r--sound/soc/samsung/arndale_rt5631.c150
-rw-r--r--sound/soc/samsung/i2s-regs.h10
-rw-r--r--sound/soc/samsung/i2s.c244
-rw-r--r--sound/soc/samsung/odroidx2_max98090.c4
-rw-r--r--sound/soc/sh/fsi.c9
-rw-r--r--sound/soc/sh/hac.c2
-rw-r--r--sound/soc/sh/rcar/adg.c2
-rw-r--r--sound/soc/sh/rcar/core.c236
-rw-r--r--sound/soc/sh/rcar/dvc.c215
-rw-r--r--sound/soc/sh/rcar/gen.c30
-rw-r--r--sound/soc/sh/rcar/rsnd.h92
-rw-r--r--sound/soc/sh/rcar/src.c101
-rw-r--r--sound/soc/sh/rcar/ssi.c233
-rw-r--r--sound/soc/soc-ac97.c256
-rw-r--r--sound/soc/soc-cache.c149
-rw-r--r--sound/soc/soc-compress.c11
-rw-r--r--sound/soc/soc-core.c1642
-rw-r--r--sound/soc/soc-dapm.c755
-rw-r--r--sound/soc/soc-jack.c11
-rw-r--r--sound/soc/soc-ops.c952
-rw-r--r--sound/soc/soc-pcm.c23
-rw-r--r--sound/soc/tegra/tegra20_ac97.c2
-rw-r--r--sound/soc/tegra/tegra_rt5640.c6
-rw-r--r--sound/soc/txx9/txx9aclc-ac97.c2
-rw-r--r--sound/soc/txx9/txx9aclc.c2
-rw-r--r--sound/soc/ux500/mop500.c8
207 files changed, 16828 insertions, 5563 deletions
diff --git a/sound/soc/Makefile b/sound/soc/Makefile
index d88edfced8c4..865e090c8061 100644
--- a/sound/soc/Makefile
+++ b/sound/soc/Makefile
@@ -1,10 +1,14 @@
 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
+snd-soc-core-objs += soc-pcm.o soc-compress.o soc-io.o soc-devres.o soc-ops.o
 
 ifneq ($(CONFIG_SND_SOC_GENERIC_DMAENGINE_PCM),)
 snd-soc-core-objs += soc-generic-dmaengine-pcm.o
 endif
 
+ifneq ($(CONFIG_SND_SOC_AC97_BUS),)
+snd-soc-core-objs += soc-ac97.o
+endif
+
 obj-$(CONFIG_SND_SOC)	+= snd-soc-core.o
 obj-$(CONFIG_SND_SOC)	+= codecs/
 obj-$(CONFIG_SND_SOC)	+= generic/
diff --git a/sound/soc/atmel/Kconfig b/sound/soc/atmel/Kconfig
index 27e3fc4a536b..fb3878312bf8 100644
--- a/sound/soc/atmel/Kconfig
+++ b/sound/soc/atmel/Kconfig
@@ -52,12 +52,3 @@ config SND_AT91_SOC_SAM9X5_WM8731
 	help
 	  Say Y if you want to add support for audio SoC on an
 	  at91sam9x5 based board that is using WM8731 codec.
-
-config SND_AT91_SOC_AFEB9260
-	tristate "SoC Audio support for AFEB9260 board"
-	depends on ARCH_AT91 && ATMEL_SSC && ARCH_AT91 && MACH_AFEB9260 && SND_ATMEL_SOC
-	select SND_ATMEL_SOC_PDC
-	select SND_ATMEL_SOC_SSC
-	select SND_SOC_TLV320AIC23_I2C
-	help
-	  Say Y here to support sound on AFEB9260 board.
diff --git a/sound/soc/atmel/Makefile b/sound/soc/atmel/Makefile
index 5baabc8bde3a..466a821da98c 100644
--- a/sound/soc/atmel/Makefile
+++ b/sound/soc/atmel/Makefile
@@ -17,4 +17,3 @@ snd-soc-sam9x5-wm8731-objs := sam9x5_wm8731.o
 obj-$(CONFIG_SND_AT91_SOC_SAM9G20_WM8731) += snd-soc-sam9g20-wm8731.o
 obj-$(CONFIG_SND_ATMEL_SOC_WM8904) += snd-atmel-soc-wm8904.o
 obj-$(CONFIG_SND_AT91_SOC_SAM9X5_WM8731) += snd-soc-sam9x5-wm8731.o
-obj-$(CONFIG_SND_AT91_SOC_AFEB9260) += snd-soc-afeb9260.o
diff --git a/sound/soc/atmel/atmel_ssc_dai.c b/sound/soc/atmel/atmel_ssc_dai.c
index f403f399808a..b1cc2a4a7fc0 100644
--- a/sound/soc/atmel/atmel_ssc_dai.c
+++ b/sound/soc/atmel/atmel_ssc_dai.c
@@ -310,7 +310,10 @@ static int atmel_ssc_set_dai_clkdiv(struct snd_soc_dai *cpu_dai,
 		 * transmit and receive, so if a value has already
 		 * been set, it must match this value.
 		 */
-		if (ssc_p->cmr_div == 0)
+		if (ssc_p->dir_mask !=
+			(SSC_DIR_MASK_PLAYBACK | SSC_DIR_MASK_CAPTURE))
+			ssc_p->cmr_div = div;
+		else if (ssc_p->cmr_div == 0)
 			ssc_p->cmr_div = div;
 		else
 			if (div != ssc_p->cmr_div)
diff --git a/sound/soc/atmel/snd-soc-afeb9260.c b/sound/soc/atmel/snd-soc-afeb9260.c
deleted file mode 100644
index 9579799ace54..000000000000
--- a/sound/soc/atmel/snd-soc-afeb9260.c
+++ /dev/null
@@ -1,151 +0,0 @@
-/*
- * afeb9260.c  --  SoC audio for AFEB9260
- *
- * Copyright (C) 2009 Sergey Lapin <slapin@ossfans.org>
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * version 2 as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
- * 02110-1301 USA
- *
- */
-
-#include <linux/module.h>
-#include <linux/moduleparam.h>
-#include <linux/kernel.h>
-#include <linux/clk.h>
-#include <linux/platform_device.h>
-
-#include <linux/atmel-ssc.h>
-#include <sound/core.h>
-#include <sound/pcm.h>
-#include <sound/pcm_params.h>
-#include <sound/soc.h>
-
-#include <asm/mach-types.h>
-#include <mach/hardware.h>
-#include <linux/gpio.h>
-
-#include "../codecs/tlv320aic23.h"
-#include "atmel-pcm.h"
-#include "atmel_ssc_dai.h"
-
-#define CODEC_CLOCK 	12000000
-
-static int afeb9260_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 err;
-
-	/* Set the codec system clock for DAC and ADC */
-	err =
-	    snd_soc_dai_set_sysclk(codec_dai, 0, CODEC_CLOCK, SND_SOC_CLOCK_IN);
-
-	if (err < 0) {
-		printk(KERN_ERR "can't set codec system clock\n");
-		return err;
-	}
-
-	return err;
-}
-
-static struct snd_soc_ops afeb9260_ops = {
-	.hw_params = afeb9260_hw_params,
-};
-
-static const struct snd_soc_dapm_widget tlv320aic23_dapm_widgets[] = {
-	SND_SOC_DAPM_HP("Headphone Jack", NULL),
-	SND_SOC_DAPM_LINE("Line In", NULL),
-	SND_SOC_DAPM_MIC("Mic Jack", NULL),
-};
-
-static const struct snd_soc_dapm_route afeb9260_audio_map[] = {
-	{"Headphone Jack", NULL, "LHPOUT"},
-	{"Headphone Jack", NULL, "RHPOUT"},
-
-	{"LLINEIN", NULL, "Line In"},
-	{"RLINEIN", NULL, "Line In"},
-
-	{"MICIN", NULL, "Mic Jack"},
-};
-
-
-/* Digital audio interface glue - connects codec <--> CPU */
-static struct snd_soc_dai_link afeb9260_dai = {
-	.name = "TLV320AIC23",
-	.stream_name = "AIC23",
-	.cpu_dai_name = "atmel-ssc-dai.0",
-	.codec_dai_name = "tlv320aic23-hifi",
-	.platform_name = "atmel_pcm-audio",
-	.codec_name = "tlv320aic23-codec.0-001a",
-	.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_IF |
-		   SND_SOC_DAIFMT_CBM_CFM,
-	.ops = &afeb9260_ops,
-};
-
-/* Audio machine driver */
-static struct snd_soc_card snd_soc_machine_afeb9260 = {
-	.name = "AFEB9260",
-	.owner = THIS_MODULE,
-	.dai_link = &afeb9260_dai,
-	.num_links = 1,
-
-	.dapm_widgets = tlv320aic23_dapm_widgets,
-	.num_dapm_widgets = ARRAY_SIZE(tlv320aic23_dapm_widgets),
-	.dapm_routes = afeb9260_audio_map,
-	.num_dapm_routes = ARRAY_SIZE(afeb9260_audio_map),
-};
-
-static struct platform_device *afeb9260_snd_device;
-
-static int __init afeb9260_soc_init(void)
-{
-	int err;
-	struct device *dev;
-
-	if (!(machine_is_afeb9260()))
-		return -ENODEV;
-
-
-	afeb9260_snd_device = platform_device_alloc("soc-audio", -1);
-	if (!afeb9260_snd_device) {
-		printk(KERN_ERR "ASoC: Platform device allocation failed\n");
-		return -ENOMEM;
-	}
-
-	platform_set_drvdata(afeb9260_snd_device, &snd_soc_machine_afeb9260);
-	err = platform_device_add(afeb9260_snd_device);
-	if (err)
-		goto err1;
-
-	dev = &afeb9260_snd_device->dev;
-
-	return 0;
-err1:
-	platform_device_put(afeb9260_snd_device);
-	return err;
-}
-
-static void __exit afeb9260_soc_exit(void)
-{
-	platform_device_unregister(afeb9260_snd_device);
-}
-
-module_init(afeb9260_soc_init);
-module_exit(afeb9260_soc_exit);
-
-MODULE_AUTHOR("Sergey Lapin <slapin@ossfans.org>");
-MODULE_DESCRIPTION("ALSA SoC for AFEB9260");
-MODULE_LICENSE("GPL");
-
diff --git a/sound/soc/au1x/ac97c.c b/sound/soc/au1x/ac97c.c
index c8a2de103c5f..5159a50a45a6 100644
--- a/sound/soc/au1x/ac97c.c
+++ b/sound/soc/au1x/ac97c.c
@@ -205,7 +205,7 @@ static int au1xac97c_dai_probe(struct snd_soc_dai *dai)
 
 static struct snd_soc_dai_driver au1xac97c_dai_driver = {
 	.name			= "alchemy-ac97c",
-	.ac97_control		= 1,
+	.bus_control		= true,
 	.probe			= au1xac97c_dai_probe,
 	.playback = {
 		.rates		= AC97_RATES,
diff --git a/sound/soc/au1x/psc-ac97.c b/sound/soc/au1x/psc-ac97.c
index 84f31e1f9d24..c6daec98ff89 100644
--- a/sound/soc/au1x/psc-ac97.c
+++ b/sound/soc/au1x/psc-ac97.c
@@ -343,7 +343,7 @@ static const struct snd_soc_dai_ops au1xpsc_ac97_dai_ops = {
 };
 
 static const struct snd_soc_dai_driver au1xpsc_ac97_dai_template = {
-	.ac97_control		= 1,
+	.bus_control		= true,
 	.probe			= au1xpsc_ac97_probe,
 	.playback = {
 		.rates		= AC97_RATES,
diff --git a/sound/soc/blackfin/bf5xx-ac97.c b/sound/soc/blackfin/bf5xx-ac97.c
index e82eb373a731..6bf21a6c02e4 100644
--- a/sound/soc/blackfin/bf5xx-ac97.c
+++ b/sound/soc/blackfin/bf5xx-ac97.c
@@ -260,7 +260,7 @@ static int bf5xx_ac97_resume(struct snd_soc_dai *dai)
 #endif
 
 static struct snd_soc_dai_driver bfin_ac97_dai = {
-	.ac97_control = 1,
+	.bus_control = true,
 	.suspend = bf5xx_ac97_suspend,
 	.resume = bf5xx_ac97_resume,
 	.playback = {
diff --git a/sound/soc/blackfin/bf5xx-ad1980.c b/sound/soc/blackfin/bf5xx-ad1980.c
index 3450e8f9080d..0fa81a523b8a 100644
--- a/sound/soc/blackfin/bf5xx-ad1980.c
+++ b/sound/soc/blackfin/bf5xx-ad1980.c
@@ -46,8 +46,6 @@
 #include <linux/gpio.h>
 #include <asm/portmux.h>
 
-#include "../codecs/ad1980.h"
-
 #include "bf5xx-ac97.h"
 
 static struct snd_soc_card bf5xx_board;
diff --git a/sound/soc/cirrus/Kconfig b/sound/soc/cirrus/Kconfig
index 5477c5475923..7b7fbcd49e5e 100644
--- a/sound/soc/cirrus/Kconfig
+++ b/sound/soc/cirrus/Kconfig
@@ -36,7 +36,8 @@ config SND_EP93XX_SOC_EDB93XX
 	tristate "SoC Audio support for Cirrus Logic EDB93xx boards"
 	depends on SND_EP93XX_SOC && (MACH_EDB9301 || MACH_EDB9302 || MACH_EDB9302A || MACH_EDB9307A || MACH_EDB9315A)
 	select SND_EP93XX_SOC_I2S
-	select SND_SOC_CS4271
+	select SND_SOC_CS4271_I2C if I2C
+	select SND_SOC_CS4271_SPI if SPI_MASTER
 	help
 	  Say Y or M here if you want to add support for I2S audio on the
 	  Cirrus Logic EDB93xx boards.
diff --git a/sound/soc/cirrus/ep93xx-ac97.c b/sound/soc/cirrus/ep93xx-ac97.c
index f30dadf85b99..6b8a366b0211 100644
--- a/sound/soc/cirrus/ep93xx-ac97.c
+++ b/sound/soc/cirrus/ep93xx-ac97.c
@@ -338,7 +338,7 @@ static const struct snd_soc_dai_ops ep93xx_ac97_dai_ops = {
 static struct snd_soc_dai_driver ep93xx_ac97_dai = {
 	.name		= "ep93xx-ac97",
 	.id		= 0,
-	.ac97_control	= 1,
+	.bus_control	= true,
 	.probe		= ep93xx_ac97_dai_probe,
 	.playback	= {
 		.stream_name	= "AC97 Playback",
diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig
index a68d1731a8fd..883c5778b309 100644
--- a/sound/soc/codecs/Kconfig
+++ b/sound/soc/codecs/Kconfig
@@ -50,7 +50,8 @@ config SND_SOC_ALL_CODECS
 	select SND_SOC_CS42L73 if I2C
 	select SND_SOC_CS4265 if I2C
 	select SND_SOC_CS4270 if I2C
-	select SND_SOC_CS4271 if SND_SOC_I2C_AND_SPI
+	select SND_SOC_CS4271_I2C if I2C
+	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
@@ -85,7 +86,7 @@ config SND_SOC_ALL_CODECS
 	select SND_SOC_RT5645 if I2C
 	select SND_SOC_RT5651 if I2C
 	select SND_SOC_RT5670 if I2C
-	select SND_SOC_RT5677 if I2C
+	select SND_SOC_RT5677 if I2C && SPI_MASTER
 	select SND_SOC_SGTL5000 if I2C
 	select SND_SOC_SI476X if MFD_SI476X_CORE
 	select SND_SOC_SIRF_AUDIO_CODEC
@@ -101,6 +102,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_TFA9879 if I2C
 	select SND_SOC_TLV320AIC23_I2C if I2C
 	select SND_SOC_TLV320AIC23_SPI if SPI_MASTER
 	select SND_SOC_TLV320AIC26 if SPI_MASTER
@@ -109,6 +111,7 @@ config SND_SOC_ALL_CODECS
 	select SND_SOC_TLV320AIC3X if I2C
 	select SND_SOC_TPA6130A2 if I2C
 	select SND_SOC_TLV320DAC33 if I2C
+	select SND_SOC_TS3A227E if I2C
 	select SND_SOC_TWL4030 if TWL4030_CORE
 	select SND_SOC_TWL6040 if TWL6040_CORE
 	select SND_SOC_UDA134X
@@ -223,6 +226,7 @@ config SND_SOC_AD193X_I2C
 	select SND_SOC_AD193X
 
 config SND_SOC_AD1980
+	select REGMAP_AC97
 	tristate
 
 config SND_SOC_AD73311
@@ -336,7 +340,8 @@ config SND_SOC_CS42L51
 	tristate
 
 config SND_SOC_CS42L51_I2C
-	tristate
+	tristate "Cirrus Logic CS42L51 CODEC (I2C)"
+	depends on I2C
 	select SND_SOC_CS42L51
 
 config SND_SOC_CS42L52
@@ -370,8 +375,19 @@ config SND_SOC_CS4270_VD33_ERRATA
 	depends on SND_SOC_CS4270
 
 config SND_SOC_CS4271
-	tristate "Cirrus Logic CS4271 CODEC"
-	depends on SND_SOC_I2C_AND_SPI
+	tristate
+
+config SND_SOC_CS4271_I2C
+	tristate "Cirrus Logic CS4271 CODEC (I2C)"
+	depends on I2C
+	select SND_SOC_CS4271
+	select REGMAP_I2C
+
+config SND_SOC_CS4271_SPI
+	tristate "Cirrus Logic CS4271 CODEC (SPI)"
+	depends on SPI_MASTER
+	select SND_SOC_CS4271
+	select REGMAP_SPI
 
 config SND_SOC_CS42XX8
 	tristate
@@ -487,7 +503,8 @@ config SND_SOC_RT286
 	depends on I2C
 
 config SND_SOC_RT5631
-	tristate
+	tristate "Realtek ALC5631/RT5631 CODEC"
+	depends on I2C
 
 config SND_SOC_RT5640
 	tristate
@@ -504,6 +521,10 @@ config SND_SOC_RT5670
 config SND_SOC_RT5677
 	tristate
 
+config SND_SOC_RT5677_SPI
+	tristate
+	default SND_SOC_RT5677
+
 #Freescale sgtl5000 codec
 config SND_SOC_SGTL5000
 	tristate "Freescale SGTL5000 CODEC"
@@ -577,15 +598,21 @@ config SND_SOC_TAS5086
 	tristate "Texas Instruments TAS5086 speaker amplifier"
 	depends on I2C
 
+config SND_SOC_TFA9879
+	tristate "NXP Semiconductors TFA9879 amplifier"
+	depends on I2C
+
 config SND_SOC_TLV320AIC23
 	tristate
 
 config SND_SOC_TLV320AIC23_I2C
-	tristate
+	tristate "Texas Instruments TLV320AIC23 audio CODEC - I2C"
+	depends on I2C
 	select SND_SOC_TLV320AIC23
 
 config SND_SOC_TLV320AIC23_SPI
-	tristate
+	tristate "Texas Instruments TLV320AIC23 audio CODEC - SPI"
+	depends on SPI_MASTER
 	select SND_SOC_TLV320AIC23
 
 config SND_SOC_TLV320AIC26
@@ -607,6 +634,10 @@ config SND_SOC_TLV320AIC3X
 config SND_SOC_TLV320DAC33
 	tristate
 
+config SND_SOC_TS3A227E
+	tristate "TI Headset/Mic detect and keypress chip"
+	depends on I2C
+
 config SND_SOC_TWL4030
 	select MFD_TWL4030_AUDIO
 	tristate
diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile
index 5dce451661e4..bbdfd1e1c182 100644
--- a/sound/soc/codecs/Makefile
+++ b/sound/soc/codecs/Makefile
@@ -41,6 +41,8 @@ snd-soc-cs42l73-objs := cs42l73.o
 snd-soc-cs4265-objs := cs4265.o
 snd-soc-cs4270-objs := cs4270.o
 snd-soc-cs4271-objs := cs4271.o
+snd-soc-cs4271-i2c-objs := cs4271-i2c.o
+snd-soc-cs4271-spi-objs := cs4271-spi.o
 snd-soc-cs42xx8-objs := cs42xx8.o
 snd-soc-cs42xx8-i2c-objs := cs42xx8-i2c.o
 snd-soc-cx20442-objs := cx20442.o
@@ -80,6 +82,7 @@ snd-soc-rt5645-objs := rt5645.o
 snd-soc-rt5651-objs := rt5651.o
 snd-soc-rt5670-objs := rt5670.o
 snd-soc-rt5677-objs := rt5677.o
+snd-soc-rt5677-spi-objs := rt5677-spi.o
 snd-soc-sgtl5000-objs := sgtl5000.o
 snd-soc-alc5623-objs := alc5623.o
 snd-soc-alc5632-objs := alc5632.o
@@ -101,6 +104,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-tfa9879-objs := tfa9879.o
 snd-soc-tlv320aic23-objs := tlv320aic23.o
 snd-soc-tlv320aic23-i2c-objs := tlv320aic23-i2c.o
 snd-soc-tlv320aic23-spi-objs := tlv320aic23-spi.o
@@ -109,6 +113,7 @@ snd-soc-tlv320aic31xx-objs := tlv320aic31xx.o
 snd-soc-tlv320aic32x4-objs := tlv320aic32x4.o
 snd-soc-tlv320aic3x-objs := tlv320aic3x.o
 snd-soc-tlv320dac33-objs := tlv320dac33.o
+snd-soc-ts3a227e-objs := ts3a227e.o
 snd-soc-twl4030-objs := twl4030.o
 snd-soc-twl6040-objs := twl6040.o
 snd-soc-uda134x-objs := uda134x.o
@@ -217,6 +222,8 @@ obj-$(CONFIG_SND_SOC_CS42L73)	+= snd-soc-cs42l73.o
 obj-$(CONFIG_SND_SOC_CS4265)	+= snd-soc-cs4265.o
 obj-$(CONFIG_SND_SOC_CS4270)	+= snd-soc-cs4270.o
 obj-$(CONFIG_SND_SOC_CS4271)	+= snd-soc-cs4271.o
+obj-$(CONFIG_SND_SOC_CS4271_I2C)	+= snd-soc-cs4271-i2c.o
+obj-$(CONFIG_SND_SOC_CS4271_SPI)	+= snd-soc-cs4271-spi.o
 obj-$(CONFIG_SND_SOC_CS42XX8)	+= snd-soc-cs42xx8.o
 obj-$(CONFIG_SND_SOC_CS42XX8_I2C) += snd-soc-cs42xx8-i2c.o
 obj-$(CONFIG_SND_SOC_CX20442)	+= snd-soc-cx20442.o
@@ -256,6 +263,7 @@ obj-$(CONFIG_SND_SOC_RT5645)	+= snd-soc-rt5645.o
 obj-$(CONFIG_SND_SOC_RT5651)	+= snd-soc-rt5651.o
 obj-$(CONFIG_SND_SOC_RT5670)	+= snd-soc-rt5670.o
 obj-$(CONFIG_SND_SOC_RT5677)	+= snd-soc-rt5677.o
+obj-$(CONFIG_SND_SOC_RT5677_SPI)	+= snd-soc-rt5677-spi.o
 obj-$(CONFIG_SND_SOC_SGTL5000)  += snd-soc-sgtl5000.o
 obj-$(CONFIG_SND_SOC_SIGMADSP)	+= snd-soc-sigmadsp.o
 obj-$(CONFIG_SND_SOC_SIGMADSP_I2C)	+= snd-soc-sigmadsp-i2c.o
@@ -274,6 +282,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_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
 obj-$(CONFIG_SND_SOC_TLV320AIC23_SPI)	+= snd-soc-tlv320aic23-spi.o
@@ -282,6 +291,7 @@ obj-$(CONFIG_SND_SOC_TLV320AIC31XX)     += snd-soc-tlv320aic31xx.o
 obj-$(CONFIG_SND_SOC_TLV320AIC32X4)     += snd-soc-tlv320aic32x4.o
 obj-$(CONFIG_SND_SOC_TLV320AIC3X)	+= snd-soc-tlv320aic3x.o
 obj-$(CONFIG_SND_SOC_TLV320DAC33)	+= snd-soc-tlv320dac33.o
+obj-$(CONFIG_SND_SOC_TS3A227E)	+= snd-soc-ts3a227e.o
 obj-$(CONFIG_SND_SOC_TWL4030)	+= snd-soc-twl4030.o
 obj-$(CONFIG_SND_SOC_TWL6040)	+= snd-soc-twl6040.o
 obj-$(CONFIG_SND_SOC_UDA134X)	+= snd-soc-uda134x.o
diff --git a/sound/soc/codecs/ab8500-codec.c b/sound/soc/codecs/ab8500-codec.c
index fd43827bb856..7dfbc9921e91 100644
--- a/sound/soc/codecs/ab8500-codec.c
+++ b/sound/soc/codecs/ab8500-codec.c
@@ -126,13 +126,13 @@ struct ab8500_codec_drvdata_dbg {
 /* Private data for AB8500 device-driver */
 struct ab8500_codec_drvdata {
 	struct regmap *regmap;
+	struct mutex ctrl_lock;
 
 	/* Sidetone */
 	long *sid_fir_values;
 	enum sid_state sid_status;
 
 	/* ANC */
-	struct mutex anc_lock;
 	long *anc_fir_values;
 	long *anc_iir_values;
 	enum anc_state anc_status;
@@ -1129,9 +1129,9 @@ static int sid_status_control_get(struct snd_kcontrol *kcontrol,
 	struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
 	struct ab8500_codec_drvdata *drvdata = dev_get_drvdata(codec->dev);
 
-	mutex_lock(&codec->mutex);
+	mutex_lock(&drvdata->ctrl_lock);
 	ucontrol->value.integer.value[0] = drvdata->sid_status;
-	mutex_unlock(&codec->mutex);
+	mutex_unlock(&drvdata->ctrl_lock);
 
 	return 0;
 }
@@ -1154,7 +1154,7 @@ static int sid_status_control_put(struct snd_kcontrol *kcontrol,
 		return -EIO;
 	}
 
-	mutex_lock(&codec->mutex);
+	mutex_lock(&drvdata->ctrl_lock);
 
 	sidconf = snd_soc_read(codec, AB8500_SIDFIRCONF);
 	if (((sidconf & BIT(AB8500_SIDFIRCONF_FIRSIDBUSY)) != 0)) {
@@ -1185,7 +1185,7 @@ static int sid_status_control_put(struct snd_kcontrol *kcontrol,
 	drvdata->sid_status = SID_FIR_CONFIGURED;
 
 out:
-	mutex_unlock(&codec->mutex);
+	mutex_unlock(&drvdata->ctrl_lock);
 
 	dev_dbg(codec->dev, "%s: Exit\n", __func__);
 
@@ -1198,9 +1198,9 @@ static int anc_status_control_get(struct snd_kcontrol *kcontrol,
 	struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
 	struct ab8500_codec_drvdata *drvdata = dev_get_drvdata(codec->dev);
 
-	mutex_lock(&codec->mutex);
+	mutex_lock(&drvdata->ctrl_lock);
 	ucontrol->value.integer.value[0] = drvdata->anc_status;
-	mutex_unlock(&codec->mutex);
+	mutex_unlock(&drvdata->ctrl_lock);
 
 	return 0;
 }
@@ -1217,7 +1217,7 @@ static int anc_status_control_put(struct snd_kcontrol *kcontrol,
 
 	dev_dbg(dev, "%s: Enter.\n", __func__);
 
-	mutex_lock(&drvdata->anc_lock);
+	mutex_lock(&drvdata->ctrl_lock);
 
 	req = ucontrol->value.integer.value[0];
 	if (req >= ARRAY_SIZE(enum_anc_state)) {
@@ -1244,9 +1244,7 @@ static int anc_status_control_put(struct snd_kcontrol *kcontrol,
 	}
 	snd_soc_dapm_sync(&codec->dapm);
 
-	mutex_lock(&codec->mutex);
 	anc_configure(codec, apply_fir, apply_iir);
-	mutex_unlock(&codec->mutex);
 
 	if (apply_fir) {
 		if (drvdata->anc_status == ANC_IIR_CONFIGURED)
@@ -1265,7 +1263,7 @@ static int anc_status_control_put(struct snd_kcontrol *kcontrol,
 	snd_soc_dapm_sync(&codec->dapm);
 
 cleanup:
-	mutex_unlock(&drvdata->anc_lock);
+	mutex_unlock(&drvdata->ctrl_lock);
 
 	if (status < 0)
 		dev_err(dev, "%s: Unable to configure ANC! (status = %d)\n",
@@ -1294,14 +1292,15 @@ static int filter_control_get(struct snd_kcontrol *kcontrol,
 			struct snd_ctl_elem_value *ucontrol)
 {
 	struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+	struct ab8500_codec_drvdata *drvdata = snd_soc_codec_get_drvdata(codec);
 	struct filter_control *fc =
 			(struct filter_control *)kcontrol->private_value;
 	unsigned int i;
 
-	mutex_lock(&codec->mutex);
+	mutex_lock(&drvdata->ctrl_lock);
 	for (i = 0; i < fc->count; i++)
 		ucontrol->value.integer.value[i] = fc->value[i];
-	mutex_unlock(&codec->mutex);
+	mutex_unlock(&drvdata->ctrl_lock);
 
 	return 0;
 }
@@ -1310,14 +1309,15 @@ static int filter_control_put(struct snd_kcontrol *kcontrol,
 		struct snd_ctl_elem_value *ucontrol)
 {
 	struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+	struct ab8500_codec_drvdata *drvdata = snd_soc_codec_get_drvdata(codec);
 	struct filter_control *fc =
 			(struct filter_control *)kcontrol->private_value;
 	unsigned int i;
 
-	mutex_lock(&codec->mutex);
+	mutex_lock(&drvdata->ctrl_lock);
 	for (i = 0; i < fc->count; i++)
 		fc->value[i] = ucontrol->value.integer.value[i];
-	mutex_unlock(&codec->mutex);
+	mutex_unlock(&drvdata->ctrl_lock);
 
 	return 0;
 }
@@ -2545,7 +2545,7 @@ static int ab8500_codec_probe(struct snd_soc_codec *codec)
 
 	(void)snd_soc_dapm_disable_pin(&codec->dapm, "ANC Configure Input");
 
-	mutex_init(&drvdata->anc_lock);
+	mutex_init(&drvdata->ctrl_lock);
 
 	return status;
 }
diff --git a/sound/soc/codecs/ac97.c b/sound/soc/codecs/ac97.c
index bd9b1839c8b0..c6e5a313ebf4 100644
--- a/sound/soc/codecs/ac97.c
+++ b/sound/soc/codecs/ac97.c
@@ -37,10 +37,11 @@ static int ac97_prepare(struct snd_pcm_substream *substream,
 			struct snd_soc_dai *dai)
 {
 	struct snd_soc_codec *codec = dai->codec;
+	struct snd_ac97 *ac97 = snd_soc_codec_get_drvdata(codec);
 
 	int reg = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ?
 		  AC97_PCM_FRONT_DAC_RATE : AC97_PCM_LR_ADC_RATE;
-	return snd_ac97_set_rate(codec->ac97, reg, substream->runtime->rate);
+	return snd_ac97_set_rate(ac97, reg, substream->runtime->rate);
 }
 
 #define STD_AC97_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |\
@@ -53,7 +54,6 @@ static const struct snd_soc_dai_ops ac97_dai_ops = {
 
 static struct snd_soc_dai_driver ac97_dai = {
 	.name = "ac97-hifi",
-	.ac97_control = 1,
 	.playback = {
 		.stream_name = "AC97 Playback",
 		.channels_min = 1,
@@ -71,6 +71,7 @@ static struct snd_soc_dai_driver ac97_dai = {
 
 static int ac97_soc_probe(struct snd_soc_codec *codec)
 {
+	struct snd_ac97 *ac97;
 	struct snd_ac97_bus *ac97_bus;
 	struct snd_ac97_template ac97_template;
 	int ret;
@@ -82,24 +83,31 @@ static int ac97_soc_probe(struct snd_soc_codec *codec)
 		return ret;
 
 	memset(&ac97_template, 0, sizeof(struct snd_ac97_template));
-	ret = snd_ac97_mixer(ac97_bus, &ac97_template, &codec->ac97);
+	ret = snd_ac97_mixer(ac97_bus, &ac97_template, &ac97);
 	if (ret < 0)
 		return ret;
 
+	snd_soc_codec_set_drvdata(codec, ac97);
+
 	return 0;
 }
 
 #ifdef CONFIG_PM
 static int ac97_soc_suspend(struct snd_soc_codec *codec)
 {
-	snd_ac97_suspend(codec->ac97);
+	struct snd_ac97 *ac97 = snd_soc_codec_get_drvdata(codec);
+
+	snd_ac97_suspend(ac97);
 
 	return 0;
 }
 
 static int ac97_soc_resume(struct snd_soc_codec *codec)
 {
-	snd_ac97_resume(codec->ac97);
+
+	struct snd_ac97 *ac97 = snd_soc_codec_get_drvdata(codec);
+
+	snd_ac97_resume(ac97);
 
 	return 0;
 }
diff --git a/sound/soc/codecs/ad193x.c b/sound/soc/codecs/ad193x.c
index 6844d0b2af68..387530b0b0fd 100644
--- a/sound/soc/codecs/ad193x.c
+++ b/sound/soc/codecs/ad193x.c
@@ -72,11 +72,13 @@ static const struct snd_kcontrol_new ad193x_snd_controls[] = {
 };
 
 static const struct snd_soc_dapm_widget ad193x_dapm_widgets[] = {
-	SND_SOC_DAPM_DAC("DAC", "Playback", AD193X_DAC_CTRL0, 0, 1),
+	SND_SOC_DAPM_DAC("DAC", "Playback", SND_SOC_NOPM, 0, 0),
+	SND_SOC_DAPM_PGA("DAC Output", AD193X_DAC_CTRL0, 0, 1, NULL, 0),
 	SND_SOC_DAPM_ADC("ADC", "Capture", SND_SOC_NOPM, 0, 0),
 	SND_SOC_DAPM_SUPPLY("PLL_PWR", AD193X_PLL_CLK_CTRL0, 0, 1, NULL, 0),
 	SND_SOC_DAPM_SUPPLY("ADC_PWR", AD193X_ADC_CTRL0, 0, 1, NULL, 0),
 	SND_SOC_DAPM_SUPPLY("SYSCLK", AD193X_PLL_CLK_CTRL0, 7, 0, NULL, 0),
+	SND_SOC_DAPM_VMID("VMID"),
 	SND_SOC_DAPM_OUTPUT("DAC1OUT"),
 	SND_SOC_DAPM_OUTPUT("DAC2OUT"),
 	SND_SOC_DAPM_OUTPUT("DAC3OUT"),
@@ -87,13 +89,15 @@ static const struct snd_soc_dapm_widget ad193x_dapm_widgets[] = {
 
 static const struct snd_soc_dapm_route audio_paths[] = {
 	{ "DAC", NULL, "SYSCLK" },
+	{ "DAC Output", NULL, "DAC" },
+	{ "DAC Output", NULL, "VMID" },
 	{ "ADC", NULL, "SYSCLK" },
 	{ "DAC", NULL, "ADC_PWR" },
 	{ "ADC", NULL, "ADC_PWR" },
-	{ "DAC1OUT", NULL, "DAC" },
-	{ "DAC2OUT", NULL, "DAC" },
-	{ "DAC3OUT", NULL, "DAC" },
-	{ "DAC4OUT", NULL, "DAC" },
+	{ "DAC1OUT", NULL, "DAC Output" },
+	{ "DAC2OUT", NULL, "DAC Output" },
+	{ "DAC3OUT", NULL, "DAC Output" },
+	{ "DAC4OUT", NULL, "DAC Output" },
 	{ "ADC", NULL, "ADC1IN" },
 	{ "ADC", NULL, "ADC2IN" },
 	{ "SYSCLK", NULL, "PLL_PWR" },
diff --git a/sound/soc/codecs/ad1980.c b/sound/soc/codecs/ad1980.c
index 304d3003339a..2860eef8610c 100644
--- a/sound/soc/codecs/ad1980.c
+++ b/sound/soc/codecs/ad1980.c
@@ -24,34 +24,86 @@
 #include <linux/module.h>
 #include <linux/kernel.h>
 #include <linux/device.h>
+#include <linux/regmap.h>
 #include <sound/core.h>
 #include <sound/pcm.h>
 #include <sound/ac97_codec.h>
 #include <sound/initval.h>
 #include <sound/soc.h>
 
-#include "ad1980.h"
+static const struct reg_default ad1980_reg_defaults[] = {
+	{ 0x02, 0x8000 },
+	{ 0x04, 0x8000 },
+	{ 0x06, 0x8000 },
+	{ 0x0c, 0x8008 },
+	{ 0x0e, 0x8008 },
+	{ 0x10, 0x8808 },
+	{ 0x12, 0x8808 },
+	{ 0x16, 0x8808 },
+	{ 0x18, 0x8808 },
+	{ 0x1a, 0x0000 },
+	{ 0x1c, 0x8000 },
+	{ 0x20, 0x0000 },
+	{ 0x28, 0x03c7 },
+	{ 0x2c, 0xbb80 },
+	{ 0x2e, 0xbb80 },
+	{ 0x30, 0xbb80 },
+	{ 0x32, 0xbb80 },
+	{ 0x36, 0x8080 },
+	{ 0x38, 0x8080 },
+	{ 0x3a, 0x2000 },
+	{ 0x60, 0x0000 },
+	{ 0x62, 0x0000 },
+	{ 0x72, 0x0000 },
+	{ 0x74, 0x1001 },
+	{ 0x76, 0x0000 },
+};
 
-/*
- * AD1980 register cache
- */
-static const u16 ad1980_reg[] = {
-	0x0090, 0x8000, 0x8000, 0x8000, /* 0 - 6  */
-	0x0000, 0x0000, 0x8008, 0x8008, /* 8 - e  */
-	0x8808, 0x8808, 0x0000, 0x8808, /* 10 - 16 */
-	0x8808, 0x0000, 0x8000, 0x0000, /* 18 - 1e */
-	0x0000, 0x0000, 0x0000, 0x0000, /* 20 - 26 */
-	0x03c7, 0x0000, 0xbb80, 0xbb80, /* 28 - 2e */
-	0xbb80, 0xbb80, 0x0000, 0x8080, /* 30 - 36 */
-	0x8080, 0x2000, 0x0000, 0x0000, /* 38 - 3e */
-	0x0000, 0x0000, 0x0000, 0x0000, /* reserved */
-	0x0000, 0x0000, 0x0000, 0x0000, /* reserved */
-	0x0000, 0x0000, 0x0000, 0x0000, /* reserved */
-	0x0000, 0x0000, 0x0000, 0x0000, /* reserved */
-	0x8080, 0x0000, 0x0000, 0x0000, /* 60 - 66 */
-	0x0000, 0x0000, 0x0000, 0x0000, /* reserved */
-	0x0000, 0x0000, 0x1001, 0x0000, /* 70 - 76 */
-	0x0000, 0x0000, 0x4144, 0x5370  /* 78 - 7e */
+static bool ad1980_readable_reg(struct device *dev, unsigned int reg)
+{
+	switch (reg) {
+	case AC97_RESET ... AC97_MASTER_MONO:
+	case AC97_PHONE ... AC97_CD:
+	case AC97_AUX ... AC97_GENERAL_PURPOSE:
+	case AC97_POWERDOWN ... AC97_PCM_LR_ADC_RATE:
+	case AC97_SPDIF:
+	case AC97_CODEC_CLASS_REV:
+	case AC97_PCI_SVID:
+	case AC97_AD_CODEC_CFG:
+	case AC97_AD_JACK_SPDIF:
+	case AC97_AD_SERIAL_CFG:
+	case AC97_VENDOR_ID1:
+	case AC97_VENDOR_ID2:
+		return true;
+	default:
+		return false;
+	}
+}
+
+static bool ad1980_writeable_reg(struct device *dev, unsigned int reg)
+{
+	switch (reg) {
+	case AC97_VENDOR_ID1:
+	case AC97_VENDOR_ID2:
+		return false;
+	default:
+		return ad1980_readable_reg(dev, reg);
+	}
+}
+
+static const struct regmap_config ad1980_regmap_config = {
+	.reg_bits = 16,
+	.reg_stride = 2,
+	.val_bits = 16,
+	.max_register = 0x7e,
+	.cache_type = REGCACHE_RBTREE,
+
+	.volatile_reg = regmap_ac97_default_volatile,
+	.readable_reg = ad1980_readable_reg,
+	.writeable_reg = ad1980_writeable_reg,
+
+	.reg_defaults = ad1980_reg_defaults,
+	.num_reg_defaults = ARRAY_SIZE(ad1980_reg_defaults),
 };
 
 static const char *ad1980_rec_sel[] = {"Mic", "CD", "NC", "AUX", "Line",
@@ -134,45 +186,8 @@ static const struct snd_soc_dapm_route ad1980_dapm_routes[] = {
 	{ "HP_OUT_R", NULL, "Playback" },
 };
 
-static unsigned int ac97_read(struct snd_soc_codec *codec,
-	unsigned int reg)
-{
-	u16 *cache = codec->reg_cache;
-
-	switch (reg) {
-	case AC97_RESET:
-	case AC97_INT_PAGING:
-	case AC97_POWERDOWN:
-	case AC97_EXTENDED_STATUS:
-	case AC97_VENDOR_ID1:
-	case AC97_VENDOR_ID2:
-		return soc_ac97_ops->read(codec->ac97, reg);
-	default:
-		reg = reg >> 1;
-
-		if (reg >= ARRAY_SIZE(ad1980_reg))
-			return -EINVAL;
-
-		return cache[reg];
-	}
-}
-
-static int ac97_write(struct snd_soc_codec *codec, unsigned int reg,
-	unsigned int val)
-{
-	u16 *cache = codec->reg_cache;
-
-	soc_ac97_ops->write(codec->ac97, reg, val);
-	reg = reg >> 1;
-	if (reg < ARRAY_SIZE(ad1980_reg))
-		cache[reg] = val;
-
-	return 0;
-}
-
 static struct snd_soc_dai_driver ad1980_dai = {
 	.name = "ad1980-hifi",
-	.ac97_control = 1,
 	.playback = {
 		.stream_name = "Playback",
 		.channels_min = 2,
@@ -189,108 +204,115 @@ static struct snd_soc_dai_driver ad1980_dai = {
 
 static int ad1980_reset(struct snd_soc_codec *codec, int try_warm)
 {
+	struct snd_ac97 *ac97 = snd_soc_codec_get_drvdata(codec);
 	unsigned int retry_cnt = 0;
 
 	do {
 		if (try_warm && soc_ac97_ops->warm_reset) {
-			soc_ac97_ops->warm_reset(codec->ac97);
-			if (ac97_read(codec, AC97_RESET) == 0x0090)
+			soc_ac97_ops->warm_reset(ac97);
+			if (snd_soc_read(codec, AC97_RESET) == 0x0090)
 				return 1;
 		}
 
-		soc_ac97_ops->reset(codec->ac97);
+		soc_ac97_ops->reset(ac97);
 		/*
 		 * Set bit 16slot in register 74h, then every slot will has only
 		 * 16 bits. This command is sent out in 20bit mode, in which
 		 * case the first nibble of data is eaten by the addr. (Tag is
 		 * always 16 bit)
 		 */
-		ac97_write(codec, AC97_AD_SERIAL_CFG, 0x9900);
+		snd_soc_write(codec, AC97_AD_SERIAL_CFG, 0x9900);
 
-		if (ac97_read(codec, AC97_RESET)  == 0x0090)
+		if (snd_soc_read(codec, AC97_RESET)  == 0x0090)
 			return 0;
 	} while (retry_cnt++ < 10);
 
-	printk(KERN_ERR "AD1980 AC97 reset failed\n");
+	dev_err(codec->dev, "Failed to reset: AC97 link error\n");
+
 	return -EIO;
 }
 
 static int ad1980_soc_probe(struct snd_soc_codec *codec)
 {
+	struct snd_ac97 *ac97;
+	struct regmap *regmap;
 	int ret;
 	u16 vendor_id2;
 	u16 ext_status;
 
-	printk(KERN_INFO "AD1980 SoC Audio Codec\n");
-
-	ret = snd_soc_new_ac97_codec(codec, soc_ac97_ops, 0);
-	if (ret < 0) {
-		printk(KERN_ERR "ad1980: failed to register AC97 codec\n");
+	ac97 = snd_soc_new_ac97_codec(codec);
+	if (IS_ERR(ac97)) {
+		ret = PTR_ERR(ac97);
+		dev_err(codec->dev, "Failed to register AC97 codec: %d\n", ret);
 		return ret;
 	}
 
+	regmap = regmap_init_ac97(ac97, &ad1980_regmap_config);
+	if (IS_ERR(regmap)) {
+		ret = PTR_ERR(regmap);
+		goto err_free_ac97;
+	}
+
+	snd_soc_codec_init_regmap(codec, regmap);
+	snd_soc_codec_set_drvdata(codec, ac97);
+
 	ret = ad1980_reset(codec, 0);
-	if (ret < 0) {
-		printk(KERN_ERR "Failed to reset AD1980: AC97 link error\n");
+	if (ret < 0)
 		goto reset_err;
-	}
 
 	/* Read out vendor ID to make sure it is ad1980 */
-	if (ac97_read(codec, AC97_VENDOR_ID1) != 0x4144) {
+	if (snd_soc_read(codec, AC97_VENDOR_ID1) != 0x4144) {
 		ret = -ENODEV;
 		goto reset_err;
 	}
 
-	vendor_id2 = ac97_read(codec, AC97_VENDOR_ID2);
+	vendor_id2 = snd_soc_read(codec, AC97_VENDOR_ID2);
 
 	if (vendor_id2 != 0x5370) {
 		if (vendor_id2 != 0x5374) {
 			ret = -ENODEV;
 			goto reset_err;
 		} else {
-			printk(KERN_WARNING "ad1980: "
-				"Found AD1981 - only 2/2 IN/OUT Channels "
-				"supported\n");
+			dev_warn(codec->dev,
+				"Found AD1981 - only 2/2 IN/OUT Channels supported\n");
 		}
 	}
 
 	/* unmute captures and playbacks volume */
-	ac97_write(codec, AC97_MASTER, 0x0000);
-	ac97_write(codec, AC97_PCM, 0x0000);
-	ac97_write(codec, AC97_REC_GAIN, 0x0000);
-	ac97_write(codec, AC97_CENTER_LFE_MASTER, 0x0000);
-	ac97_write(codec, AC97_SURROUND_MASTER, 0x0000);
+	snd_soc_write(codec, AC97_MASTER, 0x0000);
+	snd_soc_write(codec, AC97_PCM, 0x0000);
+	snd_soc_write(codec, AC97_REC_GAIN, 0x0000);
+	snd_soc_write(codec, AC97_CENTER_LFE_MASTER, 0x0000);
+	snd_soc_write(codec, AC97_SURROUND_MASTER, 0x0000);
 
 	/*power on LFE/CENTER/Surround DACs*/
-	ext_status = ac97_read(codec, AC97_EXTENDED_STATUS);
-	ac97_write(codec, AC97_EXTENDED_STATUS, ext_status&~0x3800);
-
-	snd_soc_add_codec_controls(codec, ad1980_snd_ac97_controls,
-				ARRAY_SIZE(ad1980_snd_ac97_controls));
+	ext_status = snd_soc_read(codec, AC97_EXTENDED_STATUS);
+	snd_soc_write(codec, AC97_EXTENDED_STATUS, ext_status&~0x3800);
 
 	return 0;
 
 reset_err:
-	snd_soc_free_ac97_codec(codec);
+	snd_soc_codec_exit_regmap(codec);
+err_free_ac97:
+	snd_soc_free_ac97_codec(ac97);
 	return ret;
 }
 
 static int ad1980_soc_remove(struct snd_soc_codec *codec)
 {
-	snd_soc_free_ac97_codec(codec);
+	struct snd_ac97 *ac97 = snd_soc_codec_get_drvdata(codec);
+
+	snd_soc_codec_exit_regmap(codec);
+	snd_soc_free_ac97_codec(ac97);
 	return 0;
 }
 
 static struct snd_soc_codec_driver soc_codec_dev_ad1980 = {
 	.probe = 	ad1980_soc_probe,
 	.remove = 	ad1980_soc_remove,
-	.reg_cache_size = ARRAY_SIZE(ad1980_reg),
-	.reg_word_size = sizeof(u16),
-	.reg_cache_default = ad1980_reg,
-	.reg_cache_step = 2,
-	.write = ac97_write,
-	.read = ac97_read,
 
+	.controls = ad1980_snd_ac97_controls,
+	.num_controls = ARRAY_SIZE(ad1980_snd_ac97_controls),
 	.dapm_widgets = ad1980_dapm_widgets,
 	.num_dapm_widgets = ARRAY_SIZE(ad1980_dapm_widgets),
 	.dapm_routes = ad1980_dapm_routes,
diff --git a/sound/soc/codecs/ad1980.h b/sound/soc/codecs/ad1980.h
deleted file mode 100644
index eb0af44ad3df..000000000000
--- a/sound/soc/codecs/ad1980.h
+++ /dev/null
@@ -1,26 +0,0 @@
-/*
- * ad1980.h  --  ad1980 Soc Audio driver
- *
- * WARNING:
- *
- * Because Analog Devices Inc. discontinued the ad1980 sound chip since
- * Sep. 2009, this ad1980 driver is not maintained, tested and supported
- * by ADI now.
- */
-
-#ifndef _AD1980_H
-#define _AD1980_H
-/* Bit definition of Power-Down Control/Status Register */
-#define ADC		0x0001
-#define DAC		0x0002
-#define ANL		0x0004
-#define REF		0x0008
-#define PR0		0x0100
-#define PR1		0x0200
-#define PR2		0x0400
-#define PR3		0x0800
-#define PR4		0x1000
-#define PR5		0x2000
-#define PR6		0x4000
-
-#endif
diff --git a/sound/soc/codecs/adau1373.c b/sound/soc/codecs/adau1373.c
index 7c784ad3e8b2..783dcb57043a 100644
--- a/sound/soc/codecs/adau1373.c
+++ b/sound/soc/codecs/adau1373.c
@@ -551,7 +551,7 @@ static const struct snd_kcontrol_new adau1373_drc_controls[] = {
 static int adau1373_pll_event(struct snd_soc_dapm_widget *w,
 	struct snd_kcontrol *kcontrol, int event)
 {
-	struct snd_soc_codec *codec = w->codec;
+	struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
 	struct adau1373 *adau1373 = snd_soc_codec_get_drvdata(codec);
 	unsigned int pll_id = w->name[3] - '1';
 	unsigned int val;
@@ -823,7 +823,7 @@ static const struct snd_soc_dapm_widget adau1373_dapm_widgets[] = {
 static int adau1373_check_aif_clk(struct snd_soc_dapm_widget *source,
 	struct snd_soc_dapm_widget *sink)
 {
-	struct snd_soc_codec *codec = source->codec;
+	struct snd_soc_codec *codec = snd_soc_dapm_to_codec(source->dapm);
 	struct adau1373 *adau1373 = snd_soc_codec_get_drvdata(codec);
 	unsigned int dai;
 	const char *clk;
@@ -844,7 +844,7 @@ static int adau1373_check_aif_clk(struct snd_soc_dapm_widget *source,
 static int adau1373_check_src(struct snd_soc_dapm_widget *source,
 	struct snd_soc_dapm_widget *sink)
 {
-	struct snd_soc_codec *codec = source->codec;
+	struct snd_soc_codec *codec = snd_soc_dapm_to_codec(source->dapm);
 	struct adau1373 *adau1373 = snd_soc_codec_get_drvdata(codec);
 	unsigned int dai;
 
diff --git a/sound/soc/codecs/adau1701.c b/sound/soc/codecs/adau1701.c
index 370b742117ef..d4e219b6b98f 100644
--- a/sound/soc/codecs/adau1701.c
+++ b/sound/soc/codecs/adau1701.c
@@ -22,9 +22,14 @@
 #include <sound/pcm_params.h>
 #include <sound/soc.h>
 
+#include <asm/unaligned.h>
+
 #include "sigmadsp.h"
 #include "adau1701.h"
 
+#define ADAU1701_SAFELOAD_DATA(i) (0x0810 + (i))
+#define ADAU1701_SAFELOAD_ADDR(i) (0x0815 + (i))
+
 #define ADAU1701_DSPCTRL	0x081c
 #define ADAU1701_SEROCTL	0x081e
 #define ADAU1701_SERICTL	0x081f
@@ -42,6 +47,7 @@
 #define ADAU1701_DSPCTRL_CR		(1 << 2)
 #define ADAU1701_DSPCTRL_DAM		(1 << 3)
 #define ADAU1701_DSPCTRL_ADM		(1 << 4)
+#define ADAU1701_DSPCTRL_IST		(1 << 5)
 #define ADAU1701_DSPCTRL_SR_48		0x00
 #define ADAU1701_DSPCTRL_SR_96		0x01
 #define ADAU1701_DSPCTRL_SR_192		0x02
@@ -102,7 +108,10 @@ struct adau1701 {
 	unsigned int pll_clkdiv;
 	unsigned int sysclk;
 	struct regmap *regmap;
+	struct i2c_client *client;
 	u8 pin_config[12];
+
+	struct sigmadsp *sigmadsp;
 };
 
 static const struct snd_kcontrol_new adau1701_controls[] = {
@@ -159,6 +168,7 @@ static bool adau1701_volatile_reg(struct device *dev, unsigned int reg)
 {
 	switch (reg) {
 	case ADAU1701_DACSET:
+	case ADAU1701_DSPCTRL:
 		return true;
 	default:
 		return false;
@@ -238,12 +248,58 @@ static int adau1701_reg_read(void *context, unsigned int reg,
 	return 0;
 }
 
-static int adau1701_reset(struct snd_soc_codec *codec, unsigned int clkdiv)
+static int adau1701_safeload(struct sigmadsp *sigmadsp, unsigned int addr,
+	const uint8_t bytes[], size_t len)
+{
+	struct i2c_client *client = to_i2c_client(sigmadsp->dev);
+	struct adau1701 *adau1701 = i2c_get_clientdata(client);
+	unsigned int val;
+	unsigned int i;
+	uint8_t buf[10];
+	int ret;
+
+	ret = regmap_read(adau1701->regmap, ADAU1701_DSPCTRL, &val);
+	if (ret)
+		return ret;
+
+	if (val & ADAU1701_DSPCTRL_IST)
+		msleep(50);
+
+	for (i = 0; i < len / 4; i++) {
+		put_unaligned_le16(ADAU1701_SAFELOAD_DATA(i), buf);
+		buf[2] = 0x00;
+		memcpy(buf + 3, bytes + i * 4, 4);
+		ret = i2c_master_send(client, buf, 7);
+		if (ret < 0)
+			return ret;
+		else if (ret != 7)
+			return -EIO;
+
+		put_unaligned_le16(ADAU1701_SAFELOAD_ADDR(i), buf);
+		put_unaligned_le16(addr + i, buf + 2);
+		ret = i2c_master_send(client, buf, 4);
+		if (ret < 0)
+			return ret;
+		else if (ret != 4)
+			return -EIO;
+	}
+
+	return regmap_update_bits(adau1701->regmap, ADAU1701_DSPCTRL,
+		ADAU1701_DSPCTRL_IST, ADAU1701_DSPCTRL_IST);
+}
+
+static const struct sigmadsp_ops adau1701_sigmadsp_ops = {
+	.safeload = adau1701_safeload,
+};
+
+static int adau1701_reset(struct snd_soc_codec *codec, unsigned int clkdiv,
+	unsigned int rate)
 {
 	struct adau1701 *adau1701 = snd_soc_codec_get_drvdata(codec);
-	struct i2c_client *client = to_i2c_client(codec->dev);
 	int ret;
 
+	sigmadsp_reset(adau1701->sigmadsp);
+
 	if (clkdiv != ADAU1707_CLKDIV_UNSET &&
 	    gpio_is_valid(adau1701->gpio_pll_mode[0]) &&
 	    gpio_is_valid(adau1701->gpio_pll_mode[1])) {
@@ -284,7 +340,7 @@ static int adau1701_reset(struct snd_soc_codec *codec, unsigned int clkdiv)
 	 * know the correct PLL setup
 	 */
 	if (clkdiv != ADAU1707_CLKDIV_UNSET) {
-		ret = process_sigma_firmware(client, ADAU1701_FIRMWARE);
+		ret = sigmadsp_setup(adau1701->sigmadsp, rate);
 		if (ret) {
 			dev_warn(codec->dev, "Failed to load firmware\n");
 			return ret;
@@ -385,7 +441,7 @@ static int adau1701_hw_params(struct snd_pcm_substream *substream,
 	 * firmware upload.
 	 */
 	if (clkdiv != adau1701->pll_clkdiv) {
-		ret = adau1701_reset(codec, clkdiv);
+		ret = adau1701_reset(codec, clkdiv, params_rate(params));
 		if (ret < 0)
 			return ret;
 	}
@@ -554,6 +610,14 @@ static int adau1701_set_sysclk(struct snd_soc_codec *codec, int clk_id,
 	return 0;
 }
 
+static int adau1701_startup(struct snd_pcm_substream *substream,
+	struct snd_soc_dai *dai)
+{
+	struct adau1701 *adau1701 = snd_soc_codec_get_drvdata(dai->codec);
+
+	return sigmadsp_restrict_params(adau1701->sigmadsp, substream);
+}
+
 #define ADAU1701_RATES (SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000 | \
 	SNDRV_PCM_RATE_192000)
 
@@ -564,6 +628,7 @@ static const struct snd_soc_dai_ops adau1701_dai_ops = {
 	.set_fmt	= adau1701_set_dai_fmt,
 	.hw_params	= adau1701_hw_params,
 	.digital_mute	= adau1701_digital_mute,
+	.startup	= adau1701_startup,
 };
 
 static struct snd_soc_dai_driver adau1701_dai = {
@@ -600,6 +665,10 @@ static int adau1701_probe(struct snd_soc_codec *codec)
 	unsigned int val;
 	struct adau1701 *adau1701 = snd_soc_codec_get_drvdata(codec);
 
+	ret = sigmadsp_attach(adau1701->sigmadsp, &codec->component);
+	if (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
@@ -609,7 +678,7 @@ static int adau1701_probe(struct snd_soc_codec *codec)
 	adau1701->pll_clkdiv = ADAU1707_CLKDIV_UNSET;
 
 	/* initalize with pre-configured pll mode settings */
-	ret = adau1701_reset(codec, adau1701->pll_clkdiv);
+	ret = adau1701_reset(codec, adau1701->pll_clkdiv, 0);
 	if (ret < 0)
 		return ret;
 
@@ -667,6 +736,7 @@ static int adau1701_i2c_probe(struct i2c_client *client,
 	if (!adau1701)
 		return -ENOMEM;
 
+	adau1701->client = client;
 	adau1701->regmap = devm_regmap_init(dev, NULL, client,
 					    &adau1701_regmap);
 	if (IS_ERR(adau1701->regmap))
@@ -722,6 +792,12 @@ static int adau1701_i2c_probe(struct i2c_client *client,
 	adau1701->gpio_pll_mode[1] = gpio_pll_mode[1];
 
 	i2c_set_clientdata(client, adau1701);
+
+	adau1701->sigmadsp = devm_sigmadsp_init_i2c(client,
+		&adau1701_sigmadsp_ops, ADAU1701_FIRMWARE);
+	if (IS_ERR(adau1701->sigmadsp))
+		return PTR_ERR(adau1701->sigmadsp);
+
 	ret = snd_soc_register_codec(&client->dev, &adau1701_codec_drv,
 			&adau1701_dai, 1);
 	return ret;
diff --git a/sound/soc/codecs/adau1761.c b/sound/soc/codecs/adau1761.c
index 91f60282fd2f..a1baeee160f4 100644
--- a/sound/soc/codecs/adau1761.c
+++ b/sound/soc/codecs/adau1761.c
@@ -255,7 +255,8 @@ static const struct snd_kcontrol_new adau1761_input_mux_control =
 static int adau1761_dejitter_fixup(struct snd_soc_dapm_widget *w,
 	struct snd_kcontrol *kcontrol, int event)
 {
-	struct adau *adau = snd_soc_codec_get_drvdata(w->codec);
+	struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+	struct adau *adau = snd_soc_codec_get_drvdata(codec);
 
 	/* After any power changes have been made the dejitter circuit
 	 * has to be reinitialized. */
@@ -702,11 +703,6 @@ static int adau1761_codec_probe(struct snd_soc_codec *codec)
 			ARRAY_SIZE(adau1761_dapm_routes));
 		if (ret)
 			return ret;
-
-		ret = adau17x1_load_firmware(adau, codec->dev,
-			ADAU1761_FIRMWARE);
-		if (ret)
-			dev_warn(codec->dev, "Failed to firmware\n");
 	}
 
 	ret = adau17x1_add_routes(codec);
@@ -775,16 +771,20 @@ int adau1761_probe(struct device *dev, struct regmap *regmap,
 	enum adau17x1_type type, void (*switch_mode)(struct device *dev))
 {
 	struct snd_soc_dai_driver *dai_drv;
+	const char *firmware_name;
 	int ret;
 
-	ret = adau17x1_probe(dev, regmap, type, switch_mode);
-	if (ret)
-		return ret;
-
-	if (type == ADAU1361)
+	if (type == ADAU1361) {
 		dai_drv = &adau1361_dai_driver;
-	else
+		firmware_name = NULL;
+	} else {
 		dai_drv = &adau1761_dai_driver;
+		firmware_name = ADAU1761_FIRMWARE;
+	}
+
+	ret = adau17x1_probe(dev, regmap, type, switch_mode, firmware_name);
+	if (ret)
+		return ret;
 
 	return snd_soc_register_codec(dev, &adau1761_codec_driver, dai_drv, 1);
 }
@@ -798,6 +798,7 @@ const struct regmap_config adau1761_regmap_config = {
 	.num_reg_defaults = ARRAY_SIZE(adau1761_reg_defaults),
 	.readable_reg = adau1761_readable_register,
 	.volatile_reg = adau17x1_volatile_register,
+	.precious_reg = adau17x1_precious_register,
 	.cache_type = REGCACHE_RBTREE,
 };
 EXPORT_SYMBOL_GPL(adau1761_regmap_config);
diff --git a/sound/soc/codecs/adau1781.c b/sound/soc/codecs/adau1781.c
index e9fc00fb13dd..35581f43fa6d 100644
--- a/sound/soc/codecs/adau1781.c
+++ b/sound/soc/codecs/adau1781.c
@@ -174,7 +174,7 @@ static const struct snd_kcontrol_new adau1781_mono_mixer_controls[] = {
 static int adau1781_dejitter_fixup(struct snd_soc_dapm_widget *w,
 	struct snd_kcontrol *kcontrol, int event)
 {
-	struct snd_soc_codec *codec = w->codec;
+	struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
 	struct adau *adau = snd_soc_codec_get_drvdata(codec);
 
 	/* After any power changes have been made the dejitter circuit
@@ -385,7 +385,6 @@ static int adau1781_codec_probe(struct snd_soc_codec *codec)
 {
 	struct adau1781_platform_data *pdata = dev_get_platdata(codec->dev);
 	struct adau *adau = snd_soc_codec_get_drvdata(codec);
-	const char *firmware;
 	int ret;
 
 	ret = adau17x1_add_widgets(codec);
@@ -422,25 +421,10 @@ static int adau1781_codec_probe(struct snd_soc_codec *codec)
 			return ret;
 	}
 
-	switch (adau->type) {
-	case ADAU1381:
-		firmware = ADAU1381_FIRMWARE;
-		break;
-	case ADAU1781:
-		firmware = ADAU1781_FIRMWARE;
-		break;
-	default:
-		return -EINVAL;
-	}
-
 	ret = adau17x1_add_routes(codec);
 	if (ret < 0)
 		return ret;
 
-	ret = adau17x1_load_firmware(adau, codec->dev, firmware);
-	if (ret)
-		dev_warn(codec->dev, "Failed to load firmware\n");
-
 	return 0;
 }
 
@@ -488,6 +472,7 @@ const struct regmap_config adau1781_regmap_config = {
 	.num_reg_defaults	= ARRAY_SIZE(adau1781_reg_defaults),
 	.readable_reg		= adau1781_readable_register,
 	.volatile_reg		= adau17x1_volatile_register,
+	.precious_reg		= adau17x1_precious_register,
 	.cache_type		= REGCACHE_RBTREE,
 };
 EXPORT_SYMBOL_GPL(adau1781_regmap_config);
@@ -495,9 +480,21 @@ EXPORT_SYMBOL_GPL(adau1781_regmap_config);
 int adau1781_probe(struct device *dev, struct regmap *regmap,
 	enum adau17x1_type type, void (*switch_mode)(struct device *dev))
 {
+	const char *firmware_name;
 	int ret;
 
-	ret = adau17x1_probe(dev, regmap, type, switch_mode);
+	switch (type) {
+	case ADAU1381:
+		firmware_name = ADAU1381_FIRMWARE;
+		break;
+	case ADAU1781:
+		firmware_name = ADAU1781_FIRMWARE;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	ret = adau17x1_probe(dev, regmap, type, switch_mode, firmware_name);
 	if (ret)
 		return ret;
 
diff --git a/sound/soc/codecs/adau17x1.c b/sound/soc/codecs/adau17x1.c
index 3e16c1c64115..fa2e690e51c8 100644
--- a/sound/soc/codecs/adau17x1.c
+++ b/sound/soc/codecs/adau17x1.c
@@ -61,7 +61,8 @@ static const struct snd_kcontrol_new adau17x1_controls[] = {
 static int adau17x1_pll_event(struct snd_soc_dapm_widget *w,
 	struct snd_kcontrol *kcontrol, int event)
 {
-	struct adau *adau = snd_soc_codec_get_drvdata(w->codec);
+	struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+	struct adau *adau = snd_soc_codec_get_drvdata(codec);
 	int ret;
 
 	if (SND_SOC_DAPM_EVENT_ON(event)) {
@@ -307,6 +308,7 @@ static int adau17x1_hw_params(struct snd_pcm_substream *substream,
 	struct adau *adau = snd_soc_codec_get_drvdata(codec);
 	unsigned int val, div, dsp_div;
 	unsigned int freq;
+	int ret;
 
 	if (adau->clk_src == ADAU17X1_CLK_SRC_PLL)
 		freq = adau->pll_freq;
@@ -356,6 +358,12 @@ static int adau17x1_hw_params(struct snd_pcm_substream *substream,
 		regmap_write(adau->regmap, ADAU17X1_DSP_SAMPLING_RATE, dsp_div);
 	}
 
+	if (adau->sigmadsp) {
+		ret = adau17x1_setup_firmware(adau, params_rate(params));
+		if (ret < 0)
+			return ret;
+	}
+
 	if (adau->dai_fmt != SND_SOC_DAIFMT_RIGHT_J)
 		return 0;
 
@@ -661,12 +669,24 @@ static int adau17x1_set_dai_tdm_slot(struct snd_soc_dai *dai,
 	return 0;
 }
 
+static int adau17x1_startup(struct snd_pcm_substream *substream,
+	struct snd_soc_dai *dai)
+{
+	struct adau *adau = snd_soc_codec_get_drvdata(dai->codec);
+
+	if (adau->sigmadsp)
+		return sigmadsp_restrict_params(adau->sigmadsp, substream);
+
+	return 0;
+}
+
 const struct snd_soc_dai_ops adau17x1_dai_ops = {
 	.hw_params	= adau17x1_hw_params,
 	.set_sysclk	= adau17x1_set_dai_sysclk,
 	.set_fmt	= adau17x1_set_dai_fmt,
 	.set_pll	= adau17x1_set_dai_pll,
 	.set_tdm_slot	= adau17x1_set_dai_tdm_slot,
+	.startup	= adau17x1_startup,
 };
 EXPORT_SYMBOL_GPL(adau17x1_dai_ops);
 
@@ -687,8 +707,22 @@ int adau17x1_set_micbias_voltage(struct snd_soc_codec *codec,
 }
 EXPORT_SYMBOL_GPL(adau17x1_set_micbias_voltage);
 
+bool adau17x1_precious_register(struct device *dev, unsigned int reg)
+{
+	/* SigmaDSP parameter memory */
+	if (reg < 0x400)
+		return true;
+
+	return false;
+}
+EXPORT_SYMBOL_GPL(adau17x1_precious_register);
+
 bool adau17x1_readable_register(struct device *dev, unsigned int reg)
 {
+	/* SigmaDSP parameter memory */
+	if (reg < 0x400)
+		return true;
+
 	switch (reg) {
 	case ADAU17X1_CLOCK_CONTROL:
 	case ADAU17X1_PLL_CONTROL:
@@ -745,8 +779,7 @@ bool adau17x1_volatile_register(struct device *dev, unsigned int reg)
 }
 EXPORT_SYMBOL_GPL(adau17x1_volatile_register);
 
-int adau17x1_load_firmware(struct adau *adau, struct device *dev,
-	const char *firmware)
+int adau17x1_setup_firmware(struct adau *adau, unsigned int rate)
 {
 	int ret;
 	int dspsr;
@@ -758,7 +791,7 @@ int adau17x1_load_firmware(struct adau *adau, struct device *dev,
 	regmap_write(adau->regmap, ADAU17X1_DSP_ENABLE, 1);
 	regmap_write(adau->regmap, ADAU17X1_DSP_SAMPLING_RATE, 0xf);
 
-	ret = process_sigma_firmware_regmap(dev, adau->regmap, firmware);
+	ret = sigmadsp_setup(adau->sigmadsp, rate);
 	if (ret) {
 		regmap_write(adau->regmap, ADAU17X1_DSP_ENABLE, 0);
 		return ret;
@@ -767,7 +800,7 @@ int adau17x1_load_firmware(struct adau *adau, struct device *dev,
 
 	return 0;
 }
-EXPORT_SYMBOL_GPL(adau17x1_load_firmware);
+EXPORT_SYMBOL_GPL(adau17x1_setup_firmware);
 
 int adau17x1_add_widgets(struct snd_soc_codec *codec)
 {
@@ -787,8 +820,21 @@ int adau17x1_add_widgets(struct snd_soc_codec *codec)
 		ret = snd_soc_dapm_new_controls(&codec->dapm,
 			adau17x1_dsp_dapm_widgets,
 			ARRAY_SIZE(adau17x1_dsp_dapm_widgets));
+		if (ret)
+			return ret;
+
+		if (!adau->sigmadsp)
+			return 0;
+
+		ret = sigmadsp_attach(adau->sigmadsp, &codec->component);
+		if (ret) {
+			dev_err(codec->dev, "Failed to attach firmware: %d\n",
+				ret);
+			return ret;
+		}
 	}
-	return ret;
+
+	return 0;
 }
 EXPORT_SYMBOL_GPL(adau17x1_add_widgets);
 
@@ -829,7 +875,8 @@ int adau17x1_resume(struct snd_soc_codec *codec)
 EXPORT_SYMBOL_GPL(adau17x1_resume);
 
 int adau17x1_probe(struct device *dev, struct regmap *regmap,
-	enum adau17x1_type type, void (*switch_mode)(struct device *dev))
+	enum adau17x1_type type, void (*switch_mode)(struct device *dev),
+	const char *firmware_name)
 {
 	struct adau *adau;
 
@@ -846,6 +893,16 @@ int adau17x1_probe(struct device *dev, struct regmap *regmap,
 
 	dev_set_drvdata(dev, adau);
 
+	if (firmware_name) {
+		adau->sigmadsp = devm_sigmadsp_init_regmap(dev, regmap, NULL,
+			firmware_name);
+		if (IS_ERR(adau->sigmadsp)) {
+			dev_warn(dev, "Could not find firmware file: %ld\n",
+				PTR_ERR(adau->sigmadsp));
+			adau->sigmadsp = NULL;
+		}
+	}
+
 	if (switch_mode)
 		switch_mode(dev);
 
diff --git a/sound/soc/codecs/adau17x1.h b/sound/soc/codecs/adau17x1.h
index e4a557fd7155..e13583e6ff56 100644
--- a/sound/soc/codecs/adau17x1.h
+++ b/sound/soc/codecs/adau17x1.h
@@ -4,6 +4,8 @@
 #include <linux/regmap.h>
 #include <linux/platform_data/adau17x1.h>
 
+#include "sigmadsp.h"
+
 enum adau17x1_type {
 	ADAU1361,
 	ADAU1761,
@@ -42,22 +44,24 @@ struct adau {
 	bool dsp_bypass[2];
 
 	struct regmap *regmap;
+	struct sigmadsp *sigmadsp;
 };
 
 int adau17x1_add_widgets(struct snd_soc_codec *codec);
 int adau17x1_add_routes(struct snd_soc_codec *codec);
 int adau17x1_probe(struct device *dev, struct regmap *regmap,
-	enum adau17x1_type type, void (*switch_mode)(struct device *dev));
+	enum adau17x1_type type, void (*switch_mode)(struct device *dev),
+	const char *firmware_name);
 int adau17x1_set_micbias_voltage(struct snd_soc_codec *codec,
 	enum adau17x1_micbias_voltage micbias);
 bool adau17x1_readable_register(struct device *dev, unsigned int reg);
 bool adau17x1_volatile_register(struct device *dev, unsigned int reg);
+bool adau17x1_precious_register(struct device *dev, unsigned int reg);
 int adau17x1_resume(struct snd_soc_codec *codec);
 
 extern const struct snd_soc_dai_ops adau17x1_dai_ops;
 
-int adau17x1_load_firmware(struct adau *adau, struct device *dev,
-	const char *firmware);
+int adau17x1_setup_firmware(struct adau *adau, unsigned int rate);
 bool adau17x1_has_dsp(struct adau *adau);
 
 #define ADAU17X1_CLOCK_CONTROL			0x4000
diff --git a/sound/soc/codecs/adav80x.c b/sound/soc/codecs/adav80x.c
index ce3cdca9fc62..b67480f1b1aa 100644
--- a/sound/soc/codecs/adav80x.c
+++ b/sound/soc/codecs/adav80x.c
@@ -212,7 +212,7 @@ static const struct snd_soc_dapm_widget adav80x_dapm_widgets[] = {
 static int adav80x_dapm_sysclk_check(struct snd_soc_dapm_widget *source,
 			 struct snd_soc_dapm_widget *sink)
 {
-	struct snd_soc_codec *codec = source->codec;
+	struct snd_soc_codec *codec = snd_soc_dapm_to_codec(source->dapm);
 	struct adav80x *adav80x = snd_soc_codec_get_drvdata(codec);
 	const char *clk;
 
@@ -236,7 +236,7 @@ static int adav80x_dapm_sysclk_check(struct snd_soc_dapm_widget *source,
 static int adav80x_dapm_pll_check(struct snd_soc_dapm_widget *source,
 			 struct snd_soc_dapm_widget *sink)
 {
-	struct snd_soc_codec *codec = source->codec;
+	struct snd_soc_codec *codec = snd_soc_dapm_to_codec(source->dapm);
 	struct adav80x *adav80x = snd_soc_codec_get_drvdata(codec);
 
 	return adav80x->pll_src == ADAV80X_PLL_SRC_XTAL;
diff --git a/sound/soc/codecs/ak4535.c b/sound/soc/codecs/ak4535.c
index 30e297890fec..9130d916f2f4 100644
--- a/sound/soc/codecs/ak4535.c
+++ b/sound/soc/codecs/ak4535.c
@@ -373,33 +373,9 @@ static struct snd_soc_dai_driver ak4535_dai = {
 	.ops = &ak4535_dai_ops,
 };
 
-static int ak4535_suspend(struct snd_soc_codec *codec)
-{
-	ak4535_set_bias_level(codec, SND_SOC_BIAS_OFF);
-	return 0;
-}
-
 static int ak4535_resume(struct snd_soc_codec *codec)
 {
 	snd_soc_cache_sync(codec);
-	ak4535_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
-	return 0;
-}
-
-static int ak4535_probe(struct snd_soc_codec *codec)
-{
-	/* power on device */
-	ak4535_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
-
-	snd_soc_add_codec_controls(codec, ak4535_snd_controls,
-				ARRAY_SIZE(ak4535_snd_controls));
-	return 0;
-}
-
-/* power down chip */
-static int ak4535_remove(struct snd_soc_codec *codec)
-{
-	ak4535_set_bias_level(codec, SND_SOC_BIAS_OFF);
 	return 0;
 }
 
@@ -416,11 +392,12 @@ static const struct regmap_config ak4535_regmap = {
 };
 
 static struct snd_soc_codec_driver soc_codec_dev_ak4535 = {
-	.probe =	ak4535_probe,
-	.remove =	ak4535_remove,
-	.suspend =	ak4535_suspend,
 	.resume =	ak4535_resume,
 	.set_bias_level = ak4535_set_bias_level,
+	.suspend_bias_off = true,
+
+	.controls = ak4535_snd_controls,
+	.num_controls = ARRAY_SIZE(ak4535_snd_controls),
 	.dapm_widgets = ak4535_dapm_widgets,
 	.num_dapm_widgets = ARRAY_SIZE(ak4535_dapm_widgets),
 	.dapm_routes = ak4535_audio_map,
diff --git a/sound/soc/codecs/ak4641.c b/sound/soc/codecs/ak4641.c
index 7afe8f482088..70861c7b1631 100644
--- a/sound/soc/codecs/ak4641.c
+++ b/sound/soc/codecs/ak4641.c
@@ -505,39 +505,7 @@ static struct snd_soc_dai_driver ak4641_dai[] = {
 },
 };
 
-static int ak4641_suspend(struct snd_soc_codec *codec)
-{
-	ak4641_set_bias_level(codec, SND_SOC_BIAS_OFF);
-	return 0;
-}
-
-static int ak4641_resume(struct snd_soc_codec *codec)
-{
-	ak4641_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
-	return 0;
-}
-
-static int ak4641_probe(struct snd_soc_codec *codec)
-{
-	/* power on device */
-	ak4641_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
-
-	return 0;
-}
-
-static int ak4641_remove(struct snd_soc_codec *codec)
-{
-	ak4641_set_bias_level(codec, SND_SOC_BIAS_OFF);
-
-	return 0;
-}
-
-
 static struct snd_soc_codec_driver soc_codec_dev_ak4641 = {
-	.probe			= ak4641_probe,
-	.remove			= ak4641_remove,
-	.suspend		= ak4641_suspend,
-	.resume			= ak4641_resume,
 	.controls		= ak4641_snd_controls,
 	.num_controls		= ARRAY_SIZE(ak4641_snd_controls),
 	.dapm_widgets		= ak4641_dapm_widgets,
@@ -545,6 +513,7 @@ static struct snd_soc_codec_driver soc_codec_dev_ak4641 = {
 	.dapm_routes		= ak4641_audio_map,
 	.num_dapm_routes	= ARRAY_SIZE(ak4641_audio_map),
 	.set_bias_level		= ak4641_set_bias_level,
+	.suspend_bias_off	= true,
 };
 
 static const struct regmap_config ak4641_regmap = {
diff --git a/sound/soc/codecs/ak4642.c b/sound/soc/codecs/ak4642.c
index 041712592e29..dde8b49c19ad 100644
--- a/sound/soc/codecs/ak4642.c
+++ b/sound/soc/codecs/ak4642.c
@@ -491,23 +491,7 @@ static int ak4642_resume(struct snd_soc_codec *codec)
 	return 0;
 }
 
-
-static int ak4642_probe(struct snd_soc_codec *codec)
-{
-	ak4642_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
-
-	return 0;
-}
-
-static int ak4642_remove(struct snd_soc_codec *codec)
-{
-	ak4642_set_bias_level(codec, SND_SOC_BIAS_OFF);
-	return 0;
-}
-
 static struct snd_soc_codec_driver soc_codec_dev_ak4642 = {
-	.probe			= ak4642_probe,
-	.remove			= ak4642_remove,
 	.resume			= ak4642_resume,
 	.set_bias_level		= ak4642_set_bias_level,
 	.controls		= ak4642_snd_controls,
diff --git a/sound/soc/codecs/ak4671.c b/sound/soc/codecs/ak4671.c
index 998fa0c5a0b9..686cacb0e835 100644
--- a/sound/soc/codecs/ak4671.c
+++ b/sound/soc/codecs/ak4671.c
@@ -611,20 +611,7 @@ static struct snd_soc_dai_driver ak4671_dai = {
 	.ops = &ak4671_dai_ops,
 };
 
-static int ak4671_probe(struct snd_soc_codec *codec)
-{
-	return ak4671_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
-}
-
-static int ak4671_remove(struct snd_soc_codec *codec)
-{
-	ak4671_set_bias_level(codec, SND_SOC_BIAS_OFF);
-	return 0;
-}
-
 static struct snd_soc_codec_driver soc_codec_dev_ak4671 = {
-	.probe = ak4671_probe,
-	.remove = ak4671_remove,
 	.set_bias_level = ak4671_set_bias_level,
 	.controls = ak4671_snd_controls,
 	.num_controls = ARRAY_SIZE(ak4671_snd_controls),
diff --git a/sound/soc/codecs/alc5623.c b/sound/soc/codecs/alc5623.c
index 9d0755aa1d16..bdf8c5ac8ca4 100644
--- a/sound/soc/codecs/alc5623.c
+++ b/sound/soc/codecs/alc5623.c
@@ -866,7 +866,6 @@ static int alc5623_suspend(struct snd_soc_codec *codec)
 {
 	struct alc5623_priv *alc5623 = snd_soc_codec_get_drvdata(codec);
 
-	alc5623_set_bias_level(codec, SND_SOC_BIAS_OFF);
 	regcache_cache_only(alc5623->regmap, true);
 
 	return 0;
@@ -887,15 +886,6 @@ static int alc5623_resume(struct snd_soc_codec *codec)
 		return ret;
 	}
 
-	alc5623_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
-
-	/* charge alc5623 caps */
-	if (codec->dapm.suspend_bias_level == SND_SOC_BIAS_ON) {
-		alc5623_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
-		codec->dapm.bias_level = SND_SOC_BIAS_ON;
-		alc5623_set_bias_level(codec, codec->dapm.bias_level);
-	}
-
 	return 0;
 }
 
@@ -906,9 +896,6 @@ static int alc5623_probe(struct snd_soc_codec *codec)
 
 	alc5623_reset(codec);
 
-	/* power on device */
-	alc5623_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
-
 	if (alc5623->add_ctrl) {
 		snd_soc_write(codec, ALC5623_ADD_CTRL_REG,
 				alc5623->add_ctrl);
@@ -964,19 +951,12 @@ static int alc5623_probe(struct snd_soc_codec *codec)
 	return 0;
 }
 
-/* power down chip */
-static int alc5623_remove(struct snd_soc_codec *codec)
-{
-	alc5623_set_bias_level(codec, SND_SOC_BIAS_OFF);
-	return 0;
-}
-
 static struct snd_soc_codec_driver soc_codec_device_alc5623 = {
 	.probe = alc5623_probe,
-	.remove = alc5623_remove,
 	.suspend = alc5623_suspend,
 	.resume = alc5623_resume,
 	.set_bias_level = alc5623_set_bias_level,
+	.suspend_bias_off = true,
 };
 
 static const struct regmap_config alc5623_regmap = {
diff --git a/sound/soc/codecs/alc5632.c b/sound/soc/codecs/alc5632.c
index 85942ca36cbf..d1fdbc266631 100644
--- a/sound/soc/codecs/alc5632.c
+++ b/sound/soc/codecs/alc5632.c
@@ -1038,23 +1038,15 @@ static struct snd_soc_dai_driver alc5632_dai = {
 };
 
 #ifdef CONFIG_PM
-static int alc5632_suspend(struct snd_soc_codec *codec)
-{
-	alc5632_set_bias_level(codec, SND_SOC_BIAS_OFF);
-	return 0;
-}
-
 static int alc5632_resume(struct snd_soc_codec *codec)
 {
 	struct alc5632_priv *alc5632 = snd_soc_codec_get_drvdata(codec);
 
 	regcache_sync(alc5632->regmap);
 
-	alc5632_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
 	return 0;
 }
 #else
-#define	alc5632_suspend	NULL
 #define	alc5632_resume	NULL
 #endif
 
@@ -1062,9 +1054,6 @@ static int alc5632_probe(struct snd_soc_codec *codec)
 {
 	struct alc5632_priv *alc5632 = snd_soc_codec_get_drvdata(codec);
 
-	/* power on device  */
-	alc5632_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
-
 	switch (alc5632->id) {
 	case 0x5c:
 		snd_soc_add_codec_controls(codec, alc5632_vol_snd_controls,
@@ -1077,19 +1066,12 @@ static int alc5632_probe(struct snd_soc_codec *codec)
 	return 0;
 }
 
-/* power down chip */
-static int alc5632_remove(struct snd_soc_codec *codec)
-{
-	alc5632_set_bias_level(codec, SND_SOC_BIAS_OFF);
-	return 0;
-}
-
 static struct snd_soc_codec_driver soc_codec_device_alc5632 = {
 	.probe = alc5632_probe,
-	.remove = alc5632_remove,
-	.suspend = alc5632_suspend,
 	.resume = alc5632_resume,
 	.set_bias_level = alc5632_set_bias_level,
+	.suspend_bias_off = true,
+
 	.controls = alc5632_snd_controls,
 	.num_controls = ARRAY_SIZE(alc5632_snd_controls),
 	.dapm_widgets = alc5632_dapm_widgets,
diff --git a/sound/soc/codecs/arizona.c b/sound/soc/codecs/arizona.c
index 0c05e7a7945f..9550d7433ad0 100644
--- a/sound/soc/codecs/arizona.c
+++ b/sound/soc/codecs/arizona.c
@@ -61,6 +61,11 @@
 #define ARIZONA_FLL_MIN_OUTDIV 2
 #define ARIZONA_FLL_MAX_OUTDIV 7
 
+#define ARIZONA_FMT_DSP_MODE_A          0
+#define ARIZONA_FMT_DSP_MODE_B          1
+#define ARIZONA_FMT_I2S_MODE            2
+#define ARIZONA_FMT_LEFT_JUSTIFIED_MODE 3
+
 #define arizona_fll_err(_fll, fmt, ...) \
 	dev_err(_fll->arizona->dev, "FLL%d: " fmt, _fll->id, ##__VA_ARGS__)
 #define arizona_fll_warn(_fll, fmt, ...) \
@@ -648,7 +653,7 @@ SOC_ENUM_SINGLE_DECL(arizona_in_hpf_cut_enum,
 EXPORT_SYMBOL_GPL(arizona_in_hpf_cut_enum);
 
 static const char * const arizona_in_dmic_osr_text[] = {
-	"1.536MHz", "3.072MHz", "6.144MHz",
+	"1.536MHz", "3.072MHz", "6.144MHz", "768kHz",
 };
 
 const struct soc_enum arizona_in_dmic_osr[] = {
@@ -946,10 +951,26 @@ static int arizona_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
 
 	switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
 	case SND_SOC_DAIFMT_DSP_A:
-		mode = 0;
+		mode = ARIZONA_FMT_DSP_MODE_A;
+		break;
+	case SND_SOC_DAIFMT_DSP_B:
+		if ((fmt & SND_SOC_DAIFMT_MASTER_MASK)
+				!= SND_SOC_DAIFMT_CBM_CFM) {
+			arizona_aif_err(dai, "DSP_B not valid in slave mode\n");
+			return -EINVAL;
+		}
+		mode = ARIZONA_FMT_DSP_MODE_B;
 		break;
 	case SND_SOC_DAIFMT_I2S:
-		mode = 2;
+		mode = ARIZONA_FMT_I2S_MODE;
+		break;
+	case SND_SOC_DAIFMT_LEFT_J:
+		if ((fmt & SND_SOC_DAIFMT_MASTER_MASK)
+				!= SND_SOC_DAIFMT_CBM_CFM) {
+			arizona_aif_err(dai, "LEFT_J not valid in slave mode\n");
+			return -EINVAL;
+		}
+		mode = ARIZONA_FMT_LEFT_JUSTIFIED_MODE;
 		break;
 	default:
 		arizona_aif_err(dai, "Unsupported DAI format %d\n",
@@ -1164,13 +1185,13 @@ static void arizona_wm5102_set_dac_comp(struct snd_soc_codec *codec,
 		{ 0x80, 0x0 },
 	};
 
-	mutex_lock(&codec->mutex);
+	mutex_lock(&arizona->dac_comp_lock);
 
 	dac_comp[1].def = arizona->dac_comp_coeff;
 	if (rate >= 176400)
 		dac_comp[2].def = arizona->dac_comp_enabled;
 
-	mutex_unlock(&codec->mutex);
+	mutex_unlock(&arizona->dac_comp_lock);
 
 	regmap_multi_reg_write(arizona->regmap,
 			       dac_comp,
@@ -1298,7 +1319,8 @@ static int arizona_hw_params(struct snd_pcm_substream *substream,
 
 	/* Force multiple of 2 channels for I2S mode */
 	val = snd_soc_read(codec, base + ARIZONA_AIF_FORMAT);
-	if ((channels & 1) && (val & ARIZONA_AIF1_FMT_MASK)) {
+	val &= ARIZONA_AIF1_FMT_MASK;
+	if ((channels & 1) && (val == ARIZONA_FMT_I2S_MODE)) {
 		arizona_aif_dbg(dai, "Forcing stereo mode\n");
 		bclk_target /= channels;
 		bclk_target *= channels + 1;
diff --git a/sound/soc/codecs/cq93vc.c b/sound/soc/codecs/cq93vc.c
index 537327c7f7f1..8d638e8aa8eb 100644
--- a/sound/soc/codecs/cq93vc.c
+++ b/sound/soc/codecs/cq93vc.c
@@ -62,14 +62,10 @@ static int cq93vc_mute(struct snd_soc_dai *dai, int mute)
 static int cq93vc_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 davinci_vc *davinci_vc = codec->dev->platform_data;
-
 	switch (freq) {
 	case 22579200:
 	case 27000000:
 	case 33868800:
-		davinci_vc->cq93vc.sysclk = freq;
 		return 0;
 	}
 
@@ -126,32 +122,6 @@ static struct snd_soc_dai_driver cq93vc_dai = {
 	.ops = &cq93vc_dai_ops,
 };
 
-static int cq93vc_resume(struct snd_soc_codec *codec)
-{
-	cq93vc_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
-
-	return 0;
-}
-
-static int cq93vc_probe(struct snd_soc_codec *codec)
-{
-	struct davinci_vc *davinci_vc = codec->dev->platform_data;
-
-	davinci_vc->cq93vc.codec = codec;
-
-	/* Off, with power on */
-	cq93vc_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
-
-	return 0;
-}
-
-static int cq93vc_remove(struct snd_soc_codec *codec)
-{
-	cq93vc_set_bias_level(codec, SND_SOC_BIAS_OFF);
-
-	return 0;
-}
-
 static struct regmap *cq93vc_get_regmap(struct device *dev)
 {
 	struct davinci_vc *davinci_vc = dev->platform_data;
@@ -161,9 +131,6 @@ static struct regmap *cq93vc_get_regmap(struct device *dev)
 
 static struct snd_soc_codec_driver soc_codec_dev_cq93vc = {
 	.set_bias_level = cq93vc_set_bias_level,
-	.probe = cq93vc_probe,
-	.remove = cq93vc_remove,
-	.resume = cq93vc_resume,
 	.get_regmap = cq93vc_get_regmap,
 	.controls = cq93vc_snd_controls,
 	.num_controls = ARRAY_SIZE(cq93vc_snd_controls),
diff --git a/sound/soc/codecs/cs4265.c b/sound/soc/codecs/cs4265.c
index 4fdd47d700e3..ce6086835ebd 100644
--- a/sound/soc/codecs/cs4265.c
+++ b/sound/soc/codecs/cs4265.c
@@ -32,7 +32,6 @@
 #include "cs4265.h"
 
 struct cs4265_private {
-	struct device *dev;
 	struct regmap *regmap;
 	struct gpio_desc *reset_gpio;
 	u8 format;
@@ -598,7 +597,6 @@ static int cs4265_i2c_probe(struct i2c_client *i2c_client,
 			       GFP_KERNEL);
 	if (cs4265 == NULL)
 		return -ENOMEM;
-	cs4265->dev = &i2c_client->dev;
 
 	cs4265->regmap = devm_regmap_init_i2c(i2c_client, &cs4265_regmap);
 	if (IS_ERR(cs4265->regmap)) {
diff --git a/sound/soc/codecs/cs4271-i2c.c b/sound/soc/codecs/cs4271-i2c.c
new file mode 100644
index 000000000000..b264da030340
--- /dev/null
+++ b/sound/soc/codecs/cs4271-i2c.c
@@ -0,0 +1,62 @@
+/*
+ * CS4271 I2C audio driver
+ *
+ * Copyright (c) 2010 Alexander Sverdlin <subaparts@yandex.ru>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/regmap.h>
+#include <sound/soc.h>
+#include "cs4271.h"
+
+static int cs4271_i2c_probe(struct i2c_client *client,
+			     const struct i2c_device_id *id)
+{
+	struct regmap_config config;
+
+	config = cs4271_regmap_config;
+	config.reg_bits = 8;
+	config.val_bits = 8;
+
+	return cs4271_probe(&client->dev,
+			    devm_regmap_init_i2c(client, &config));
+}
+
+static int cs4271_i2c_remove(struct i2c_client *client)
+{
+	snd_soc_unregister_codec(&client->dev);
+	return 0;
+}
+
+static const struct i2c_device_id cs4271_i2c_id[] = {
+	{ "cs4271", 0 },
+	{ }
+};
+MODULE_DEVICE_TABLE(i2c, cs4271_i2c_id);
+
+static struct i2c_driver cs4271_i2c_driver = {
+	.driver = {
+		.name = "cs4271",
+		.owner = THIS_MODULE,
+		.of_match_table = of_match_ptr(cs4271_dt_ids),
+	},
+	.probe = cs4271_i2c_probe,
+	.remove = cs4271_i2c_remove,
+	.id_table = cs4271_i2c_id,
+};
+module_i2c_driver(cs4271_i2c_driver);
+
+MODULE_DESCRIPTION("ASoC CS4271 I2C Driver");
+MODULE_AUTHOR("Alexander Sverdlin <subaparts@yandex.ru>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/cs4271-spi.c b/sound/soc/codecs/cs4271-spi.c
new file mode 100644
index 000000000000..acd49d86e706
--- /dev/null
+++ b/sound/soc/codecs/cs4271-spi.c
@@ -0,0 +1,55 @@
+/*
+ * CS4271 SPI audio driver
+ *
+ * Copyright (c) 2010 Alexander Sverdlin <subaparts@yandex.ru>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include <linux/spi/spi.h>
+#include <linux/regmap.h>
+#include <sound/soc.h>
+#include "cs4271.h"
+
+static int cs4271_spi_probe(struct spi_device *spi)
+{
+	struct regmap_config config;
+
+	config = cs4271_regmap_config;
+	config.reg_bits = 16;
+	config.val_bits = 8;
+	config.read_flag_mask = 0x21;
+	config.write_flag_mask = 0x20;
+
+	return cs4271_probe(&spi->dev, devm_regmap_init_spi(spi, &config));
+}
+
+static int cs4271_spi_remove(struct spi_device *spi)
+{
+	snd_soc_unregister_codec(&spi->dev);
+	return 0;
+}
+
+static struct spi_driver cs4271_spi_driver = {
+	.driver = {
+		.name	= "cs4271",
+		.owner	= THIS_MODULE,
+		.of_match_table = of_match_ptr(cs4271_dt_ids),
+	},
+	.probe		= cs4271_spi_probe,
+	.remove		= cs4271_spi_remove,
+};
+module_spi_driver(cs4271_spi_driver);
+
+MODULE_DESCRIPTION("ASoC CS4271 SPI Driver");
+MODULE_AUTHOR("Alexander Sverdlin <subaparts@yandex.ru>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/cs4271.c b/sound/soc/codecs/cs4271.c
index 93cec52f4733..79a4efcb894c 100644
--- a/sound/soc/codecs/cs4271.c
+++ b/sound/soc/codecs/cs4271.c
@@ -23,8 +23,6 @@
 #include <linux/slab.h>
 #include <linux/delay.h>
 #include <linux/gpio.h>
-#include <linux/i2c.h>
-#include <linux/spi/spi.h>
 #include <linux/of.h>
 #include <linux/of_device.h>
 #include <linux/of_gpio.h>
@@ -32,6 +30,7 @@
 #include <sound/soc.h>
 #include <sound/tlv.h>
 #include <sound/cs4271.h>
+#include "cs4271.h"
 
 #define CS4271_PCM_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \
 			    SNDRV_PCM_FMTBIT_S24_LE | \
@@ -527,14 +526,15 @@ static int cs4271_soc_resume(struct snd_soc_codec *codec)
 #endif /* CONFIG_PM */
 
 #ifdef CONFIG_OF
-static const struct of_device_id cs4271_dt_ids[] = {
+const struct of_device_id cs4271_dt_ids[] = {
 	{ .compatible = "cirrus,cs4271", },
 	{ }
 };
 MODULE_DEVICE_TABLE(of, cs4271_dt_ids);
+EXPORT_SYMBOL_GPL(cs4271_dt_ids);
 #endif
 
-static int cs4271_probe(struct snd_soc_codec *codec)
+static int cs4271_codec_probe(struct snd_soc_codec *codec)
 {
 	struct cs4271_private *cs4271 = snd_soc_codec_get_drvdata(codec);
 	struct cs4271_platform_data *cs4271plat = codec->dev->platform_data;
@@ -587,7 +587,7 @@ static int cs4271_probe(struct snd_soc_codec *codec)
 	return 0;
 }
 
-static int cs4271_remove(struct snd_soc_codec *codec)
+static int cs4271_codec_remove(struct snd_soc_codec *codec)
 {
 	struct cs4271_private *cs4271 = snd_soc_codec_get_drvdata(codec);
 
@@ -599,8 +599,8 @@ static int cs4271_remove(struct snd_soc_codec *codec)
 };
 
 static struct snd_soc_codec_driver soc_codec_dev_cs4271 = {
-	.probe			= cs4271_probe,
-	.remove			= cs4271_remove,
+	.probe			= cs4271_codec_probe,
+	.remove			= cs4271_codec_remove,
 	.suspend		= cs4271_soc_suspend,
 	.resume			= cs4271_soc_resume,
 
@@ -642,14 +642,8 @@ static int cs4271_common_probe(struct device *dev,
 	return 0;
 }
 
-#if defined(CONFIG_SPI_MASTER)
-
-static const struct regmap_config cs4271_spi_regmap = {
-	.reg_bits = 16,
-	.val_bits = 8,
+const struct regmap_config cs4271_regmap_config = {
 	.max_register = CS4271_LASTREG,
-	.read_flag_mask = 0x21,
-	.write_flag_mask = 0x20,
 
 	.reg_defaults = cs4271_reg_defaults,
 	.num_reg_defaults = ARRAY_SIZE(cs4271_reg_defaults),
@@ -657,140 +651,27 @@ static const struct regmap_config cs4271_spi_regmap = {
 
 	.volatile_reg = cs4271_volatile_reg,
 };
+EXPORT_SYMBOL_GPL(cs4271_regmap_config);
 
-static int cs4271_spi_probe(struct spi_device *spi)
+int cs4271_probe(struct device *dev, struct regmap *regmap)
 {
 	struct cs4271_private *cs4271;
 	int ret;
 
-	ret = cs4271_common_probe(&spi->dev, &cs4271);
-	if (ret < 0)
-		return ret;
-
-	spi_set_drvdata(spi, cs4271);
-	cs4271->regmap = devm_regmap_init_spi(spi, &cs4271_spi_regmap);
-	if (IS_ERR(cs4271->regmap))
-		return PTR_ERR(cs4271->regmap);
-
-	return snd_soc_register_codec(&spi->dev, &soc_codec_dev_cs4271,
-		&cs4271_dai, 1);
-}
-
-static int cs4271_spi_remove(struct spi_device *spi)
-{
-	snd_soc_unregister_codec(&spi->dev);
-	return 0;
-}
-
-static struct spi_driver cs4271_spi_driver = {
-	.driver = {
-		.name	= "cs4271",
-		.owner	= THIS_MODULE,
-		.of_match_table = of_match_ptr(cs4271_dt_ids),
-	},
-	.probe		= cs4271_spi_probe,
-	.remove		= cs4271_spi_remove,
-};
-#endif /* defined(CONFIG_SPI_MASTER) */
-
-#if IS_ENABLED(CONFIG_I2C)
-static const struct i2c_device_id cs4271_i2c_id[] = {
-	{"cs4271", 0},
-	{}
-};
-MODULE_DEVICE_TABLE(i2c, cs4271_i2c_id);
+	if (IS_ERR(regmap))
+		return PTR_ERR(regmap);
 
-static const struct regmap_config cs4271_i2c_regmap = {
-	.reg_bits = 8,
-	.val_bits = 8,
-	.max_register = CS4271_LASTREG,
-
-	.reg_defaults = cs4271_reg_defaults,
-	.num_reg_defaults = ARRAY_SIZE(cs4271_reg_defaults),
-	.cache_type = REGCACHE_RBTREE,
-
-	.volatile_reg = cs4271_volatile_reg,
-};
-
-static int cs4271_i2c_probe(struct i2c_client *client,
-			    const struct i2c_device_id *id)
-{
-	struct cs4271_private *cs4271;
-	int ret;
-
-	ret = cs4271_common_probe(&client->dev, &cs4271);
+	ret = cs4271_common_probe(dev, &cs4271);
 	if (ret < 0)
 		return ret;
 
-	i2c_set_clientdata(client, cs4271);
-	cs4271->regmap = devm_regmap_init_i2c(client, &cs4271_i2c_regmap);
-	if (IS_ERR(cs4271->regmap))
-		return PTR_ERR(cs4271->regmap);
+	dev_set_drvdata(dev, cs4271);
+	cs4271->regmap = regmap;
 
-	return snd_soc_register_codec(&client->dev, &soc_codec_dev_cs4271,
-		&cs4271_dai, 1);
-}
-
-static int cs4271_i2c_remove(struct i2c_client *client)
-{
-	snd_soc_unregister_codec(&client->dev);
-	return 0;
-}
-
-static struct i2c_driver cs4271_i2c_driver = {
-	.driver = {
-		.name	= "cs4271",
-		.owner	= THIS_MODULE,
-		.of_match_table = of_match_ptr(cs4271_dt_ids),
-	},
-	.id_table	= cs4271_i2c_id,
-	.probe		= cs4271_i2c_probe,
-	.remove		= cs4271_i2c_remove,
-};
-#endif /* IS_ENABLED(CONFIG_I2C) */
-
-/*
- * We only register our serial bus driver here without
- * assignment to particular chip. So if any of the below
- * fails, there is some problem with I2C or SPI subsystem.
- * In most cases this module will be compiled with support
- * of only one serial bus.
- */
-static int __init cs4271_modinit(void)
-{
-	int ret;
-
-#if IS_ENABLED(CONFIG_I2C)
-	ret = i2c_add_driver(&cs4271_i2c_driver);
-	if (ret) {
-		pr_err("Failed to register CS4271 I2C driver: %d\n", ret);
-		return ret;
-	}
-#endif
-
-#if defined(CONFIG_SPI_MASTER)
-	ret = spi_register_driver(&cs4271_spi_driver);
-	if (ret) {
-		pr_err("Failed to register CS4271 SPI driver: %d\n", ret);
-		return ret;
-	}
-#endif
-
-	return 0;
-}
-module_init(cs4271_modinit);
-
-static void __exit cs4271_modexit(void)
-{
-#if defined(CONFIG_SPI_MASTER)
-	spi_unregister_driver(&cs4271_spi_driver);
-#endif
-
-#if IS_ENABLED(CONFIG_I2C)
-	i2c_del_driver(&cs4271_i2c_driver);
-#endif
+	return snd_soc_register_codec(dev, &soc_codec_dev_cs4271, &cs4271_dai,
+				      1);
 }
-module_exit(cs4271_modexit);
+EXPORT_SYMBOL_GPL(cs4271_probe);
 
 MODULE_AUTHOR("Alexander Sverdlin <subaparts@yandex.ru>");
 MODULE_DESCRIPTION("Cirrus Logic CS4271 ALSA SoC Codec Driver");
diff --git a/sound/soc/codecs/cs4271.h b/sound/soc/codecs/cs4271.h
new file mode 100644
index 000000000000..9adad8eefdc9
--- /dev/null
+++ b/sound/soc/codecs/cs4271.h
@@ -0,0 +1,11 @@
+#ifndef _CS4271_PRIV_H
+#define _CS4271_PRIV_H
+
+#include <linux/regmap.h>
+
+extern const struct of_device_id cs4271_dt_ids[];
+extern const struct regmap_config cs4271_regmap_config;
+
+int cs4271_probe(struct device *dev, struct regmap *regmap);
+
+#endif
diff --git a/sound/soc/codecs/cs42l51.c b/sound/soc/codecs/cs42l51.c
index 669c38fc3034..b3951524339f 100644
--- a/sound/soc/codecs/cs42l51.c
+++ b/sound/soc/codecs/cs42l51.c
@@ -153,15 +153,17 @@ static const struct snd_kcontrol_new cs42l51_snd_controls[] = {
 static int cs42l51_pdn_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_PRE_PMD:
-		snd_soc_update_bits(w->codec, CS42L51_POWER_CTL1,
+		snd_soc_update_bits(codec, CS42L51_POWER_CTL1,
 				    CS42L51_POWER_CTL1_PDN,
 				    CS42L51_POWER_CTL1_PDN);
 		break;
 	default:
 	case SND_SOC_DAPM_POST_PMD:
-		snd_soc_update_bits(w->codec, CS42L51_POWER_CTL1,
+		snd_soc_update_bits(codec, CS42L51_POWER_CTL1,
 				    CS42L51_POWER_CTL1_PDN, 0);
 		break;
 	}
diff --git a/sound/soc/codecs/cs42l73.c b/sound/soc/codecs/cs42l73.c
index 2f8b94683e83..7c55537c69cf 100644
--- a/sound/soc/codecs/cs42l73.c
+++ b/sound/soc/codecs/cs42l73.c
@@ -584,7 +584,7 @@ static const struct snd_kcontrol_new cs42l73_snd_controls[] = {
 static int cs42l73_spklo_spk_amp_event(struct snd_soc_dapm_widget *w,
 	struct snd_kcontrol *kcontrol, int event)
 {
-	struct snd_soc_codec *codec = w->codec;
+	struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
 	struct cs42l73_private *priv = snd_soc_codec_get_drvdata(codec);
 	switch (event) {
 	case SND_SOC_DAPM_POST_PMD:
@@ -600,7 +600,7 @@ static int cs42l73_spklo_spk_amp_event(struct snd_soc_dapm_widget *w,
 static int cs42l73_ear_amp_event(struct snd_soc_dapm_widget *w,
 	struct snd_kcontrol *kcontrol, int event)
 {
-	struct snd_soc_codec *codec = w->codec;
+	struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
 	struct cs42l73_private *priv = snd_soc_codec_get_drvdata(codec);
 	switch (event) {
 	case SND_SOC_DAPM_POST_PMD:
@@ -618,7 +618,7 @@ static int cs42l73_ear_amp_event(struct snd_soc_dapm_widget *w,
 static int cs42l73_hp_amp_event(struct snd_soc_dapm_widget *w,
 	struct snd_kcontrol *kcontrol, int event)
 {
-	struct snd_soc_codec *codec = w->codec;
+	struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
 	struct cs42l73_private *priv = snd_soc_codec_get_drvdata(codec);
 	switch (event) {
 	case SND_SOC_DAPM_POST_PMD:
diff --git a/sound/soc/codecs/hdmi.c b/sound/soc/codecs/hdmi.c
index 1087fd5f9917..1391ad50f95d 100644
--- a/sound/soc/codecs/hdmi.c
+++ b/sound/soc/codecs/hdmi.c
@@ -47,6 +47,7 @@ static struct snd_soc_dai_driver hdmi_codec_dai = {
 			SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_192000,
 		.formats = SNDRV_PCM_FMTBIT_S16_LE |
 			SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE,
+		.sig_bits = 24,
 	},
 	.capture = {
 		.stream_name = "Capture",
@@ -75,6 +76,7 @@ static struct snd_soc_codec_driver hdmi_codec = {
 	.num_dapm_widgets = ARRAY_SIZE(hdmi_widgets),
 	.dapm_routes = hdmi_routes,
 	.num_dapm_routes = ARRAY_SIZE(hdmi_routes),
+	.ignore_pmdown_time = true,
 };
 
 static int hdmi_codec_probe(struct platform_device *pdev)
diff --git a/sound/soc/codecs/lm49453.c b/sound/soc/codecs/lm49453.c
index c1ae5764983f..c4dfde9bdf1c 100644
--- a/sound/soc/codecs/lm49453.c
+++ b/sound/soc/codecs/lm49453.c
@@ -1395,15 +1395,7 @@ static struct snd_soc_dai_driver lm49453_dai[] = {
 	},
 };
 
-/* power down chip */
-static int lm49453_remove(struct snd_soc_codec *codec)
-{
-	lm49453_set_bias_level(codec, SND_SOC_BIAS_OFF);
-	return 0;
-}
-
 static struct snd_soc_codec_driver soc_codec_dev_lm49453 = {
-	.remove = lm49453_remove,
 	.set_bias_level = lm49453_set_bias_level,
 	.controls = lm49453_snd_controls,
 	.num_controls = ARRAY_SIZE(lm49453_snd_controls),
diff --git a/sound/soc/codecs/max98088.c b/sound/soc/codecs/max98088.c
index 2cd3e5427441..805b3f8cd39d 100644
--- a/sound/soc/codecs/max98088.c
+++ b/sound/soc/codecs/max98088.c
@@ -875,7 +875,7 @@ static const struct snd_kcontrol_new max98088_right_ADC_mixer_controls[] = {
 static int max98088_mic_event(struct snd_soc_dapm_widget *w,
                             struct snd_kcontrol *kcontrol, int event)
 {
-       struct snd_soc_codec *codec = w->codec;
+       struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
        struct max98088_priv *max98088 = snd_soc_codec_get_drvdata(codec);
 
        switch (event) {
@@ -905,7 +905,7 @@ static int max98088_mic_event(struct snd_soc_dapm_widget *w,
 static int max98088_line_pga(struct snd_soc_dapm_widget *w,
                             int event, int line, u8 channel)
 {
-       struct snd_soc_codec *codec = w->codec;
+       struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
        struct max98088_priv *max98088 = snd_soc_codec_get_drvdata(codec);
        u8 *state;
 
@@ -1887,25 +1887,6 @@ static void max98088_handle_pdata(struct snd_soc_codec *codec)
                max98088_handle_eq_pdata(codec);
 }
 
-#ifdef CONFIG_PM
-static int max98088_suspend(struct snd_soc_codec *codec)
-{
-       max98088_set_bias_level(codec, SND_SOC_BIAS_OFF);
-
-       return 0;
-}
-
-static int max98088_resume(struct snd_soc_codec *codec)
-{
-       max98088_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
-
-       return 0;
-}
-#else
-#define max98088_suspend NULL
-#define max98088_resume NULL
-#endif
-
 static int max98088_probe(struct snd_soc_codec *codec)
 {
        struct max98088_priv *max98088 = snd_soc_codec_get_drvdata(codec);
@@ -1946,9 +1927,6 @@ static int max98088_probe(struct snd_soc_codec *codec)
 
        snd_soc_write(codec, M98088_REG_51_PWR_SYS, M98088_PWRSV);
 
-       /* initialize registers cache to hardware default */
-       max98088_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
-
        snd_soc_write(codec, M98088_REG_0F_IRQ_ENABLE, 0x00);
 
        snd_soc_write(codec, M98088_REG_22_MIX_DAC,
@@ -1974,7 +1952,6 @@ static int max98088_remove(struct snd_soc_codec *codec)
 {
        struct max98088_priv *max98088 = snd_soc_codec_get_drvdata(codec);
 
-       max98088_set_bias_level(codec, SND_SOC_BIAS_OFF);
        kfree(max98088->eq_texts);
 
        return 0;
@@ -1983,9 +1960,9 @@ static int max98088_remove(struct snd_soc_codec *codec)
 static struct snd_soc_codec_driver soc_codec_dev_max98088 = {
 	.probe   = max98088_probe,
 	.remove  = max98088_remove,
-	.suspend = max98088_suspend,
-	.resume  = max98088_resume,
 	.set_bias_level = max98088_set_bias_level,
+	.suspend_bias_off = true,
+
 	.controls = max98088_snd_controls,
 	.num_controls = ARRAY_SIZE(max98088_snd_controls),
 	.dapm_widgets = max98088_dapm_widgets,
diff --git a/sound/soc/codecs/max98090.c b/sound/soc/codecs/max98090.c
index 1229554f1464..151f718241ea 100644
--- a/sound/soc/codecs/max98090.c
+++ b/sound/soc/codecs/max98090.c
@@ -806,7 +806,7 @@ static const struct snd_kcontrol_new max98091_snd_controls[] = {
 static int max98090_micinput_event(struct snd_soc_dapm_widget *w,
 				 struct snd_kcontrol *kcontrol, int event)
 {
-	struct snd_soc_codec *codec = w->codec;
+	struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
 	struct max98090_priv *max98090 = snd_soc_codec_get_drvdata(codec);
 
 	unsigned int val = snd_soc_read(codec, w->reg);
@@ -1311,6 +1311,10 @@ static const struct snd_soc_dapm_route max98090_dapm_routes[] = {
 	{"MIC1 Input", NULL, "MIC1"},
 	{"MIC2 Input", NULL, "MIC2"},
 
+	{"DMICL", NULL, "DMICL_ENA"},
+	{"DMICL", NULL, "DMICR_ENA"},
+	{"DMICR", NULL, "DMICL_ENA"},
+	{"DMICR", NULL, "DMICR_ENA"},
 	{"DMICL", NULL, "AHPF"},
 	{"DMICR", NULL, "AHPF"},
 
@@ -1368,8 +1372,6 @@ static const struct snd_soc_dapm_route max98090_dapm_routes[] = {
 	{"DMIC Mux", "ADC", "ADCR"},
 	{"DMIC Mux", "DMIC", "DMICL"},
 	{"DMIC Mux", "DMIC", "DMICR"},
-	{"DMIC Mux", "DMIC", "DMICL_ENA"},
-	{"DMIC Mux", "DMIC", "DMICR_ENA"},
 
 	{"LBENL Mux", "Normal", "DMIC Mux"},
 	{"LBENL Mux", "Loopback", "LTENL Mux"},
@@ -1395,8 +1397,8 @@ static const struct snd_soc_dapm_route max98090_dapm_routes[] = {
 	{"STENL Mux", "Sidetone Left", "DMICL"},
 	{"STENR Mux", "Sidetone Right", "ADCR"},
 	{"STENR Mux", "Sidetone Right", "DMICR"},
-	{"DACL", "NULL", "STENL Mux"},
-	{"DACR", "NULL", "STENL Mux"},
+	{"DACL", NULL, "STENL Mux"},
+	{"DACR", NULL, "STENR Mux"},
 
 	{"AIFINL", NULL, "SHDN"},
 	{"AIFINR", NULL, "SHDN"},
@@ -1826,27 +1828,155 @@ static int max98090_set_bias_level(struct snd_soc_codec *codec,
 	return 0;
 }
 
-static const int comp_pclk_rates[] = {
-	11289600, 12288000, 12000000, 13000000, 19200000
-};
-
-static const int dmic_micclk[] = {
-	2, 2, 2, 2, 4, 2
-};
+static const int dmic_divisors[] = { 2, 3, 4, 5, 6, 8 };
 
 static const int comp_lrclk_rates[] = {
 	8000, 16000, 32000, 44100, 48000, 96000
 };
 
-static const int dmic_comp[6][6] = {
-	{7, 8, 3, 3, 3, 3},
-	{7, 8, 3, 3, 3, 3},
-	{7, 8, 3, 3, 3, 3},
-	{7, 8, 3, 1, 1, 1},
-	{7, 8, 3, 1, 2, 2},
-	{7, 8, 3, 3, 3, 3}
+struct dmic_table {
+	int pclk;
+	struct {
+		int freq;
+		int comp[6]; /* One each for 8, 16, 32, 44.1, 48, and 96 kHz */
+	} settings[6]; /* One for each dmic divisor. */
 };
 
+static const struct dmic_table dmic_table[] = { /* One for each pclk freq. */
+	{
+		.pclk = 11289600,
+		.settings = {
+			{ .freq = 2, .comp = { 7, 8, 3, 3, 3, 3 } },
+			{ .freq = 1, .comp = { 7, 8, 2, 2, 2, 2 } },
+			{ .freq = 0, .comp = { 7, 8, 3, 3, 3, 3 } },
+			{ .freq = 0, .comp = { 7, 8, 6, 6, 6, 6 } },
+			{ .freq = 0, .comp = { 7, 8, 3, 3, 3, 3 } },
+			{ .freq = 0, .comp = { 7, 8, 3, 3, 3, 3 } },
+		},
+	},
+	{
+		.pclk = 12000000,
+		.settings = {
+			{ .freq = 2, .comp = { 7, 8, 3, 3, 3, 3 } },
+			{ .freq = 1, .comp = { 7, 8, 2, 2, 2, 2 } },
+			{ .freq = 0, .comp = { 7, 8, 3, 3, 3, 3 } },
+			{ .freq = 0, .comp = { 7, 8, 5, 5, 6, 6 } },
+			{ .freq = 0, .comp = { 7, 8, 3, 3, 3, 3 } },
+			{ .freq = 0, .comp = { 7, 8, 3, 3, 3, 3 } },
+		}
+	},
+	{
+		.pclk = 12288000,
+		.settings = {
+			{ .freq = 2, .comp = { 7, 8, 3, 3, 3, 3 } },
+			{ .freq = 1, .comp = { 7, 8, 2, 2, 2, 2 } },
+			{ .freq = 0, .comp = { 7, 8, 3, 3, 3, 3 } },
+			{ .freq = 0, .comp = { 7, 8, 6, 6, 6, 6 } },
+			{ .freq = 0, .comp = { 7, 8, 3, 3, 3, 3 } },
+			{ .freq = 0, .comp = { 7, 8, 3, 3, 3, 3 } },
+		}
+	},
+	{
+		.pclk = 13000000,
+		.settings = {
+			{ .freq = 2, .comp = { 7, 8, 1, 1, 1, 1 } },
+			{ .freq = 1, .comp = { 7, 8, 0, 0, 0, 0 } },
+			{ .freq = 0, .comp = { 7, 8, 1, 1, 1, 1 } },
+			{ .freq = 0, .comp = { 7, 8, 4, 4, 5, 5 } },
+			{ .freq = 0, .comp = { 7, 8, 1, 1, 1, 1 } },
+			{ .freq = 0, .comp = { 7, 8, 1, 1, 1, 1 } },
+		}
+	},
+	{
+		.pclk = 19200000,
+		.settings = {
+			{ .freq = 2, .comp = { 0, 0, 0, 0, 0, 0 } },
+			{ .freq = 1, .comp = { 7, 8, 1, 1, 1, 1 } },
+			{ .freq = 0, .comp = { 7, 8, 5, 5, 6, 6 } },
+			{ .freq = 0, .comp = { 7, 8, 2, 2, 3, 3 } },
+			{ .freq = 0, .comp = { 7, 8, 1, 1, 2, 2 } },
+			{ .freq = 0, .comp = { 7, 8, 5, 5, 6, 6 } },
+		}
+	},
+};
+
+static int max98090_find_divisor(int target_freq, int pclk)
+{
+	int current_diff = INT_MAX;
+	int test_diff = INT_MAX;
+	int divisor_index = 0;
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(dmic_divisors); i++) {
+		test_diff = abs(target_freq - (pclk / dmic_divisors[i]));
+		if (test_diff < current_diff) {
+			current_diff = test_diff;
+			divisor_index = i;
+		}
+	}
+
+	return divisor_index;
+}
+
+static int max98090_find_closest_pclk(int pclk)
+{
+	int m1;
+	int m2;
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(dmic_table); i++) {
+		if (pclk == dmic_table[i].pclk)
+			return i;
+		if (pclk < dmic_table[i].pclk) {
+			if (i == 0)
+				return i;
+			m1 = pclk - dmic_table[i-1].pclk;
+			m2 = dmic_table[i].pclk - pclk;
+			if (m1 < m2)
+				return i - 1;
+			else
+				return i;
+		}
+	}
+
+	return -EINVAL;
+}
+
+static int max98090_configure_dmic(struct max98090_priv *max98090,
+				   int target_dmic_clk, int pclk, int fs)
+{
+	int micclk_index;
+	int pclk_index;
+	int dmic_freq;
+	int dmic_comp;
+	int i;
+
+	pclk_index = max98090_find_closest_pclk(pclk);
+	if (pclk_index < 0)
+		return pclk_index;
+
+	micclk_index = max98090_find_divisor(target_dmic_clk, pclk);
+
+	for (i = 0; i < ARRAY_SIZE(comp_lrclk_rates) - 1; i++) {
+		if (fs <= (comp_lrclk_rates[i] + comp_lrclk_rates[i+1]) / 2)
+			break;
+	}
+
+	dmic_freq = dmic_table[pclk_index].settings[micclk_index].freq;
+	dmic_comp = dmic_table[pclk_index].settings[micclk_index].comp[i];
+
+	regmap_update_bits(max98090->regmap, M98090_REG_DIGITAL_MIC_ENABLE,
+			   M98090_MICCLK_MASK,
+			   micclk_index << M98090_MICCLK_SHIFT);
+
+	regmap_update_bits(max98090->regmap, M98090_REG_DIGITAL_MIC_CONFIG,
+			   M98090_DMIC_COMP_MASK | M98090_DMIC_FREQ_MASK,
+			   dmic_comp << M98090_DMIC_COMP_SHIFT |
+			   dmic_freq << M98090_DMIC_FREQ_SHIFT);
+
+	return 0;
+}
+
 static int max98090_dai_hw_params(struct snd_pcm_substream *substream,
 				   struct snd_pcm_hw_params *params,
 				   struct snd_soc_dai *dai)
@@ -1854,7 +1984,6 @@ static int max98090_dai_hw_params(struct snd_pcm_substream *substream,
 	struct snd_soc_codec *codec = dai->codec;
 	struct max98090_priv *max98090 = snd_soc_codec_get_drvdata(codec);
 	struct max98090_cdata *cdata;
-	int i, j;
 
 	cdata = &max98090->dai[0];
 	max98090->bclk = snd_soc_params_to_bclk(params);
@@ -1893,27 +2022,8 @@ static int max98090_dai_hw_params(struct snd_pcm_substream *substream,
 		snd_soc_update_bits(codec, M98090_REG_FILTER_CONFIG,
 			M98090_DHF_MASK, M98090_DHF_MASK);
 
-	/* Check for supported PCLK to LRCLK ratios */
-	for (j = 0; j < ARRAY_SIZE(comp_pclk_rates); j++) {
-		if (comp_pclk_rates[j] == max98090->sysclk) {
-			break;
-		}
-	}
-
-	for (i = 0; i < ARRAY_SIZE(comp_lrclk_rates) - 1; i++) {
-		if (max98090->lrclk <= (comp_lrclk_rates[i] +
-			comp_lrclk_rates[i + 1]) / 2) {
-			break;
-		}
-	}
-
-	snd_soc_update_bits(codec, M98090_REG_DIGITAL_MIC_ENABLE,
-			M98090_MICCLK_MASK,
-			dmic_micclk[j] << M98090_MICCLK_SHIFT);
-
-	snd_soc_update_bits(codec, M98090_REG_DIGITAL_MIC_CONFIG,
-			M98090_DMIC_COMP_MASK,
-			dmic_comp[j][i] << M98090_DMIC_COMP_SHIFT);
+	max98090_configure_dmic(max98090, max98090->dmic_freq, max98090->pclk,
+				max98090->lrclk);
 
 	return 0;
 }
@@ -1944,12 +2054,15 @@ static int max98090_dai_set_sysclk(struct snd_soc_dai *dai,
 	if ((freq >= 10000000) && (freq <= 20000000)) {
 		snd_soc_write(codec, M98090_REG_SYSTEM_CLOCK,
 			M98090_PSCLK_DIV1);
+		max98090->pclk = freq;
 	} else if ((freq > 20000000) && (freq <= 40000000)) {
 		snd_soc_write(codec, M98090_REG_SYSTEM_CLOCK,
 			M98090_PSCLK_DIV2);
+		max98090->pclk = freq >> 1;
 	} else if ((freq > 40000000) && (freq <= 60000000)) {
 		snd_soc_write(codec, M98090_REG_SYSTEM_CLOCK,
 			M98090_PSCLK_DIV4);
+		max98090->pclk = freq >> 2;
 	} else {
 		dev_err(codec->dev, "Invalid master clock frequency\n");
 		return -EINVAL;
@@ -2324,6 +2437,7 @@ static int max98090_probe(struct snd_soc_codec *codec)
 	/* Initialize private data */
 
 	max98090->sysclk = (unsigned)-1;
+	max98090->pclk = (unsigned)-1;
 	max98090->master = false;
 
 	cdata = &max98090->dai[0];
@@ -2463,6 +2577,11 @@ static int max98090_i2c_probe(struct i2c_client *i2c,
 	i2c_set_clientdata(i2c, max98090);
 	max98090->pdata = i2c->dev.platform_data;
 
+	ret = of_property_read_u32(i2c->dev.of_node, "maxim,dmic-freq",
+				   &max98090->dmic_freq);
+	if (ret < 0)
+		max98090->dmic_freq = MAX98090_DEFAULT_DMIC_FREQ;
+
 	max98090->regmap = devm_regmap_init_i2c(i2c, &max98090_regmap);
 	if (IS_ERR(max98090->regmap)) {
 		ret = PTR_ERR(max98090->regmap);
diff --git a/sound/soc/codecs/max98090.h b/sound/soc/codecs/max98090.h
index a5f6bada06da..21ff743f5af2 100644
--- a/sound/soc/codecs/max98090.h
+++ b/sound/soc/codecs/max98090.h
@@ -12,6 +12,12 @@
 #define _MAX98090_H
 
 /*
+ * The default operating frequency for a DMIC attached to the codec.
+ * This can be overridden by a device tree property.
+ */
+#define MAX98090_DEFAULT_DMIC_FREQ		2500000
+
+/*
  * MAX98090 Register Definitions
  */
 
@@ -1518,8 +1524,10 @@ struct max98090_priv {
 	struct max98090_pdata *pdata;
 	struct clk *mclk;
 	unsigned int sysclk;
+	unsigned int pclk;
 	unsigned int bclk;
 	unsigned int lrclk;
+	u32 dmic_freq;
 	struct max98090_cdata dai[1];
 	int jack_state;
 	struct delayed_work jack_work;
diff --git a/sound/soc/codecs/max98095.c b/sound/soc/codecs/max98095.c
index 0ee6797d5083..8fba0c3db798 100644
--- a/sound/soc/codecs/max98095.c
+++ b/sound/soc/codecs/max98095.c
@@ -16,6 +16,7 @@
 #include <linux/pm.h>
 #include <linux/i2c.h>
 #include <linux/clk.h>
+#include <linux/mutex.h>
 #include <sound/core.h>
 #include <sound/pcm.h>
 #include <sound/pcm_params.h>
@@ -57,6 +58,7 @@ struct max98095_priv {
 	unsigned int mic2pre;
 	struct snd_soc_jack *headphone_jack;
 	struct snd_soc_jack *mic_jack;
+	struct mutex lock;
 };
 
 static const struct reg_default max98095_reg_def[] = {
@@ -864,7 +866,7 @@ static const struct snd_kcontrol_new max98095_right_ADC_mixer_controls[] = {
 static int max98095_mic_event(struct snd_soc_dapm_widget *w,
 			     struct snd_kcontrol *kcontrol, int event)
 {
-	struct snd_soc_codec *codec = w->codec;
+	struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
 	struct max98095_priv *max98095 = snd_soc_codec_get_drvdata(codec);
 
 	switch (event) {
@@ -894,7 +896,7 @@ static int max98095_mic_event(struct snd_soc_dapm_widget *w,
 static int max98095_line_pga(struct snd_soc_dapm_widget *w,
 			     int event, u8 channel)
 {
-	struct snd_soc_codec *codec = w->codec;
+	struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
 	struct max98095_priv *max98095 = snd_soc_codec_get_drvdata(codec);
 	u8 *state;
 
@@ -942,7 +944,7 @@ static int max98095_pga_in2_event(struct snd_soc_dapm_widget *w,
 static int max98095_lineout_event(struct snd_soc_dapm_widget *w,
 			     struct snd_kcontrol *kcontrol, int event)
 {
-	struct snd_soc_codec *codec = w->codec;
+	struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
 
 	switch (event) {
 	case SND_SOC_DAPM_POST_PMU:
@@ -1803,7 +1805,7 @@ static int max98095_put_eq_enum(struct snd_kcontrol *kcontrol,
 	regsave = snd_soc_read(codec, M98095_088_CFG_LEVEL);
 	snd_soc_update_bits(codec, M98095_088_CFG_LEVEL, regmask, 0);
 
-	mutex_lock(&codec->mutex);
+	mutex_lock(&max98095->lock);
 	snd_soc_update_bits(codec, M98095_00F_HOST_CFG, M98095_SEG, M98095_SEG);
 	m98095_eq_band(codec, channel, 0, coef_set->band1);
 	m98095_eq_band(codec, channel, 1, coef_set->band2);
@@ -1811,7 +1813,7 @@ static int max98095_put_eq_enum(struct snd_kcontrol *kcontrol,
 	m98095_eq_band(codec, channel, 3, coef_set->band4);
 	m98095_eq_band(codec, channel, 4, coef_set->band5);
 	snd_soc_update_bits(codec, M98095_00F_HOST_CFG, M98095_SEG, 0);
-	mutex_unlock(&codec->mutex);
+	mutex_unlock(&max98095->lock);
 
 	/* Restore the original on/off state */
 	snd_soc_update_bits(codec, M98095_088_CFG_LEVEL, regmask, regsave);
@@ -1957,12 +1959,12 @@ static int max98095_put_bq_enum(struct snd_kcontrol *kcontrol,
 	regsave = snd_soc_read(codec, M98095_088_CFG_LEVEL);
 	snd_soc_update_bits(codec, M98095_088_CFG_LEVEL, regmask, 0);
 
-	mutex_lock(&codec->mutex);
+	mutex_lock(&max98095->lock);
 	snd_soc_update_bits(codec, M98095_00F_HOST_CFG, M98095_SEG, M98095_SEG);
 	m98095_biquad_band(codec, channel, 0, coef_set->band1);
 	m98095_biquad_band(codec, channel, 1, coef_set->band2);
 	snd_soc_update_bits(codec, M98095_00F_HOST_CFG, M98095_SEG, 0);
-	mutex_unlock(&codec->mutex);
+	mutex_unlock(&max98095->lock);
 
 	/* Restore the original on/off state */
 	snd_soc_update_bits(codec, M98095_088_CFG_LEVEL, regmask, regsave);
@@ -2317,9 +2319,6 @@ static int max98095_probe(struct snd_soc_codec *codec)
 
 	snd_soc_write(codec, M98095_097_PWR_SYS, M98095_PWRSV);
 
-	/* initialize registers cache to hardware default */
-	max98095_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
-
 	snd_soc_write(codec, M98095_048_MIX_DAC_LR,
 		M98095_DAI1L_TO_DACL|M98095_DAI1R_TO_DACR);
 
@@ -2359,8 +2358,6 @@ static int max98095_remove(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_OFF);
-
 	if (max98095->headphone_jack || max98095->mic_jack)
 		max98095_jack_detect_disable(codec);
 
@@ -2395,6 +2392,8 @@ static int max98095_i2c_probe(struct i2c_client *i2c,
 	if (max98095 == NULL)
 		return -ENOMEM;
 
+	mutex_init(&max98095->lock);
+
 	max98095->regmap = devm_regmap_init_i2c(i2c, &max98095_regmap);
 	if (IS_ERR(max98095->regmap)) {
 		ret = PTR_ERR(max98095->regmap);
diff --git a/sound/soc/codecs/max9850.c b/sound/soc/codecs/max9850.c
index 4fdf5aaa236f..10f8e47ce2c2 100644
--- a/sound/soc/codecs/max9850.c
+++ b/sound/soc/codecs/max9850.c
@@ -291,25 +291,6 @@ static struct snd_soc_dai_driver max9850_dai = {
 	.ops = &max9850_dai_ops,
 };
 
-#ifdef CONFIG_PM
-static int max9850_suspend(struct snd_soc_codec *codec)
-{
-	max9850_set_bias_level(codec, SND_SOC_BIAS_OFF);
-
-	return 0;
-}
-
-static int max9850_resume(struct snd_soc_codec *codec)
-{
-	max9850_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
-
-	return 0;
-}
-#else
-#define max9850_suspend NULL
-#define max9850_resume NULL
-#endif
-
 static int max9850_probe(struct snd_soc_codec *codec)
 {
 	/* enable zero-detect */
@@ -324,9 +305,8 @@ static int max9850_probe(struct snd_soc_codec *codec)
 
 static struct snd_soc_codec_driver soc_codec_dev_max9850 = {
 	.probe =	max9850_probe,
-	.suspend =	max9850_suspend,
-	.resume =	max9850_resume,
 	.set_bias_level = max9850_set_bias_level,
+	.suspend_bias_off = true,
 
 	.controls = max9850_controls,
 	.num_controls = ARRAY_SIZE(max9850_controls),
diff --git a/sound/soc/codecs/rt286.c b/sound/soc/codecs/rt286.c
index 4aa555cbcca8..2cd4fe463102 100644
--- a/sound/soc/codecs/rt286.c
+++ b/sound/soc/codecs/rt286.c
@@ -17,6 +17,7 @@
 #include <linux/i2c.h>
 #include <linux/platform_device.h>
 #include <linux/spi/spi.h>
+#include <linux/dmi.h>
 #include <linux/acpi.h>
 #include <sound/core.h>
 #include <sound/pcm.h>
@@ -36,11 +37,13 @@
 
 struct rt286_priv {
 	struct regmap *regmap;
+	struct snd_soc_codec *codec;
 	struct rt286_platform_data pdata;
 	struct i2c_client *i2c;
 	struct snd_soc_jack *jack;
 	struct delayed_work jack_detect_work;
 	int sys_clk;
+	int clk_id;
 	struct reg_default *index_cache;
 };
 
@@ -188,7 +191,7 @@ static int rt286_hw_write(void *context, unsigned int reg, unsigned int value)
 	u8 data[4];
 	int ret, i;
 
-	/*handle index registers*/
+	/* handle index registers */
 	if (reg <= 0xff) {
 		rt286_hw_write(client, RT286_COEF_INDEX, reg);
 		for (i = 0; i < INDEX_CACHE_SIZE; i++) {
@@ -231,7 +234,7 @@ static int rt286_hw_read(void *context, unsigned int reg, unsigned int *value)
 	__be32 be_reg;
 	unsigned int index, vid, buf = 0x0;
 
-	/*handle index registers*/
+	/* handle index registers */
 	if (reg <= 0xff) {
 		rt286_hw_write(client, RT286_COEF_INDEX, reg);
 		reg = RT286_PROC_COEF;
@@ -298,7 +301,6 @@ static int rt286_support_power_controls[] = {
 static int rt286_jack_detect(struct rt286_priv *rt286, bool *hp, bool *mic)
 {
 	unsigned int val, buf;
-	int i;
 
 	*hp = false;
 	*mic = false;
@@ -309,67 +311,44 @@ static int rt286_jack_detect(struct rt286_priv *rt286, bool *hp, bool *mic)
 		if (*hp) {
 			/* power on HV,VERF */
 			regmap_update_bits(rt286->regmap,
-				RT286_POWER_CTRL1, 0x1001, 0x0);
+				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");
 			/* power LDO1 */
-			regmap_update_bits(rt286->regmap,
-				RT286_POWER_CTRL2, 0x4, 0x4);
-			regmap_write(rt286->regmap, RT286_SET_MIC1, 0x24);
-			regmap_read(rt286->regmap, RT286_CBJ_CTRL2, &val);
+			snd_soc_dapm_force_enable_pin(&rt286->codec->dapm,
+							"LDO1");
+			snd_soc_dapm_sync(&rt286->codec->dapm);
 
-			msleep(200);
-			i = 40;
-			while (((val & 0x0800) == 0) && (i > 0)) {
-				regmap_read(rt286->regmap,
-					RT286_CBJ_CTRL2, &val);
-				i--;
-				msleep(20);
-			}
+			regmap_write(rt286->regmap, RT286_SET_MIC1, 0x24);
+			msleep(50);
 
-			if (0x0400 == (val & 0x0700)) {
-				*mic = false;
+			regmap_update_bits(rt286->regmap,
+				RT286_CBJ_CTRL1, 0xfcc0, 0xd400);
+			msleep(300);
+			regmap_read(rt286->regmap, RT286_CBJ_CTRL2, &val);
 
-				regmap_write(rt286->regmap,
-					RT286_SET_MIC1, 0x20);
-				/* power off HV,VERF */
-				regmap_update_bits(rt286->regmap,
-					RT286_POWER_CTRL1, 0x1001, 0x1001);
-				regmap_update_bits(rt286->regmap,
-					RT286_A_BIAS_CTRL3, 0xc000, 0x0000);
-				regmap_update_bits(rt286->regmap,
-					RT286_CBJ_CTRL1, 0x0030, 0x0000);
-				regmap_update_bits(rt286->regmap,
-					RT286_A_BIAS_CTRL2, 0xc000, 0x0000);
-			} else if ((0x0200 == (val & 0x0700)) ||
-				(0x0100 == (val & 0x0700))) {
+			if (0x0070 == (val & 0x0070)) {
 				*mic = true;
-				regmap_update_bits(rt286->regmap,
-					RT286_A_BIAS_CTRL3, 0xc000, 0x8000);
-				regmap_update_bits(rt286->regmap,
-					RT286_CBJ_CTRL1, 0x0030, 0x0020);
-				regmap_update_bits(rt286->regmap,
-					RT286_A_BIAS_CTRL2, 0xc000, 0x8000);
 			} else {
-				*mic = false;
+				regmap_update_bits(rt286->regmap,
+					RT286_CBJ_CTRL1, 0xfcc0, 0xe400);
+				msleep(300);
+				regmap_read(rt286->regmap,
+					RT286_CBJ_CTRL2, &val);
+				if (0x0070 == (val & 0x0070))
+					*mic = true;
+				else
+					*mic = false;
 			}
-
-			regmap_update_bits(rt286->regmap,
-						RT286_MISC_CTRL1,
-						0x0060, 0x0000);
-		} else {
-			regmap_update_bits(rt286->regmap,
-						RT286_MISC_CTRL1,
-						0x0060, 0x0020);
 			regmap_update_bits(rt286->regmap,
-						RT286_A_BIAS_CTRL3,
-						0xc000, 0x8000);
-			regmap_update_bits(rt286->regmap,
-						RT286_CBJ_CTRL1,
-						0x0030, 0x0020);
-			regmap_update_bits(rt286->regmap,
-						RT286_A_BIAS_CTRL2,
-						0xc000, 0x8000);
+				RT286_DC_GAIN, 0x200, 0x0);
 
+		} else {
 			*mic = false;
+			regmap_write(rt286->regmap, RT286_SET_MIC1, 0x20);
 		}
 	} else {
 		regmap_read(rt286->regmap, RT286_GET_HP_SENSE, &buf);
@@ -378,6 +357,12 @@ 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");
+	if (!*hp)
+		snd_soc_dapm_disable_pin(&rt286->codec->dapm, "LDO1");
+	snd_soc_dapm_sync(&rt286->codec->dapm);
+
 	return 0;
 }
 
@@ -415,6 +400,17 @@ int rt286_mic_detect(struct snd_soc_codec *codec, struct snd_soc_jack *jack)
 }
 EXPORT_SYMBOL_GPL(rt286_mic_detect);
 
+static int is_mclk_mode(struct snd_soc_dapm_widget *source,
+			 struct snd_soc_dapm_widget *sink)
+{
+	struct rt286_priv *rt286 = snd_soc_codec_get_drvdata(source->codec);
+
+	if (rt286->clk_id == RT286_SCLK_S_MCLK)
+		return 1;
+	else
+		return 0;
+}
+
 static const DECLARE_TLV_DB_SCALE(out_vol_tlv, -6350, 50, 0);
 static const DECLARE_TLV_DB_SCALE(mic_vol_tlv, 0, 1000, 0);
 
@@ -568,7 +564,84 @@ static int rt286_adc_event(struct snd_soc_dapm_widget *w,
 	return 0;
 }
 
+static int rt286_vref_event(struct snd_soc_dapm_widget *w,
+			     struct snd_kcontrol *kcontrol, int event)
+{
+	struct snd_soc_codec *codec = w->codec;
+
+	switch (event) {
+	case SND_SOC_DAPM_PRE_PMU:
+		snd_soc_update_bits(codec,
+			RT286_CBJ_CTRL1, 0x0400, 0x0000);
+		mdelay(50);
+		break;
+	default:
+		return 0;
+	}
+
+	return 0;
+}
+
+static int rt286_ldo2_event(struct snd_soc_dapm_widget *w,
+			     struct snd_kcontrol *kcontrol, int event)
+{
+	struct snd_soc_codec *codec = w->codec;
+
+	switch (event) {
+	case SND_SOC_DAPM_POST_PMU:
+		snd_soc_update_bits(codec, RT286_POWER_CTRL2, 0x38, 0x08);
+		break;
+	case SND_SOC_DAPM_PRE_PMD:
+		snd_soc_update_bits(codec, RT286_POWER_CTRL2, 0x38, 0x30);
+		break;
+	default:
+		return 0;
+	}
+
+	return 0;
+}
+
+static int rt286_mic1_event(struct snd_soc_dapm_widget *w,
+			     struct snd_kcontrol *kcontrol, int event)
+{
+	struct snd_soc_codec *codec = w->codec;
+
+	switch (event) {
+	case SND_SOC_DAPM_PRE_PMU:
+		snd_soc_update_bits(codec,
+			RT286_A_BIAS_CTRL3, 0xc000, 0x8000);
+		snd_soc_update_bits(codec,
+			RT286_A_BIAS_CTRL2, 0xc000, 0x8000);
+		break;
+	case SND_SOC_DAPM_POST_PMD:
+		snd_soc_update_bits(codec,
+			RT286_A_BIAS_CTRL3, 0xc000, 0x0000);
+		snd_soc_update_bits(codec,
+			RT286_A_BIAS_CTRL2, 0xc000, 0x0000);
+		break;
+	default:
+		return 0;
+	}
+
+	return 0;
+}
+
 static const struct snd_soc_dapm_widget rt286_dapm_widgets[] = {
+	SND_SOC_DAPM_SUPPLY_S("HV", 1, RT286_POWER_CTRL1,
+		12, 1, NULL, 0),
+	SND_SOC_DAPM_SUPPLY("VREF", RT286_POWER_CTRL1,
+		0, 1, rt286_vref_event, SND_SOC_DAPM_PRE_PMU),
+	SND_SOC_DAPM_SUPPLY_S("LDO1", 1, RT286_POWER_CTRL2,
+		2, 0, NULL, 0),
+	SND_SOC_DAPM_SUPPLY_S("LDO2", 2, RT286_POWER_CTRL1,
+		13, 1, rt286_ldo2_event, SND_SOC_DAPM_PRE_PMD |
+		SND_SOC_DAPM_POST_PMU),
+	SND_SOC_DAPM_SUPPLY("MCLK MODE", RT286_PLL_CTRL1,
+		5, 0, NULL, 0),
+	SND_SOC_DAPM_SUPPLY("MIC1 Input Buffer", SND_SOC_NOPM,
+		0, 0, rt286_mic1_event, SND_SOC_DAPM_PRE_PMU |
+		SND_SOC_DAPM_POST_PMD),
+
 	/* Input Lines */
 	SND_SOC_DAPM_INPUT("DMIC1 Pin"),
 	SND_SOC_DAPM_INPUT("DMIC2 Pin"),
@@ -642,6 +715,25 @@ static const struct snd_soc_dapm_widget rt286_dapm_widgets[] = {
 };
 
 static const struct snd_soc_dapm_route rt286_dapm_routes[] = {
+	{"ADC 0", NULL, "MCLK MODE", is_mclk_mode},
+	{"ADC 1", NULL, "MCLK MODE", is_mclk_mode},
+	{"Front", NULL, "MCLK MODE", is_mclk_mode},
+	{"Surround", NULL, "MCLK MODE", is_mclk_mode},
+
+	{"HP Power", NULL, "LDO1"},
+	{"HP Power", NULL, "LDO2"},
+
+	{"MIC1", NULL, "LDO1"},
+	{"MIC1", NULL, "LDO2"},
+	{"MIC1", NULL, "HV"},
+	{"MIC1", NULL, "VREF"},
+	{"MIC1", NULL, "MIC1 Input Buffer"},
+
+	{"SPO", NULL, "LDO1"},
+	{"SPO", NULL, "LDO2"},
+	{"SPO", NULL, "HV"},
+	{"SPO", NULL, "VREF"},
+
 	{"DMIC1", NULL, "DMIC1 Pin"},
 	{"DMIC2", NULL, "DMIC2 Pin"},
 	{"DMIC1", NULL, "DMIC Receiver"},
@@ -880,6 +972,7 @@ static int rt286_set_dai_sysclk(struct snd_soc_dai *dai,
 	}
 
 	rt286->sys_clk = freq;
+	rt286->clk_id = clk_id;
 
 	return 0;
 }
@@ -915,13 +1008,18 @@ static int rt286_set_bias_level(struct snd_soc_codec *codec,
 
 	case SND_SOC_BIAS_ON:
 		mdelay(10);
+		snd_soc_update_bits(codec,
+			RT286_CBJ_CTRL1, 0x0400, 0x0400);
+		snd_soc_update_bits(codec,
+			RT286_DC_GAIN, 0x200, 0x0);
+
 		break;
 
 	case SND_SOC_BIAS_STANDBY:
 		snd_soc_write(codec,
 			RT286_SET_AUDIO_POWER, AC_PWRST_D3);
 		snd_soc_update_bits(codec,
-			RT286_DC_GAIN, 0x200, 0x0);
+			RT286_CBJ_CTRL1, 0x0400, 0x0000);
 		break;
 
 	default:
@@ -962,6 +1060,7 @@ static int rt286_probe(struct snd_soc_codec *codec)
 {
 	struct rt286_priv *rt286 = snd_soc_codec_get_drvdata(codec);
 
+	rt286->codec = codec;
 	codec->dapm.bias_level = SND_SOC_BIAS_OFF;
 
 	if (rt286->i2c->irq) {
@@ -1107,6 +1206,16 @@ static const struct acpi_device_id rt286_acpi_match[] = {
 };
 MODULE_DEVICE_TABLE(acpi, rt286_acpi_match);
 
+static struct dmi_system_id force_combo_jack_table[] = {
+	{
+		.ident = "Intel Wilson Beach",
+		.matches = {
+			DMI_MATCH(DMI_BOARD_NAME, "Wilson Beach SDS")
+		}
+	},
+	{ }
+};
+
 static int rt286_i2c_probe(struct i2c_client *i2c,
 			   const struct i2c_device_id *id)
 {
@@ -1142,6 +1251,9 @@ static int rt286_i2c_probe(struct i2c_client *i2c,
 	if (pdata)
 		rt286->pdata = *pdata;
 
+	if (dmi_check_system(force_combo_jack_table))
+		rt286->pdata.cbj_en = true;
+
 	regmap_write(rt286->regmap, RT286_SET_AUDIO_POWER, AC_PWRST_D3);
 
 	for (i = 0; i < RT286_POWER_REG_LEN; i++)
@@ -1152,7 +1264,6 @@ static int rt286_i2c_probe(struct i2c_client *i2c,
 	if (!rt286->pdata.cbj_en) {
 		regmap_write(rt286->regmap, RT286_CBJ_CTRL2, 0x0000);
 		regmap_write(rt286->regmap, RT286_MIC1_DET_CTRL, 0x0816);
-		regmap_write(rt286->regmap, RT286_MISC_CTRL1, 0x0000);
 		regmap_update_bits(rt286->regmap,
 					RT286_CBJ_CTRL1, 0xf000, 0xb000);
 	} else {
@@ -1169,10 +1280,12 @@ static int rt286_i2c_probe(struct i2c_client *i2c,
 
 	mdelay(10);
 
-	/*Power down LDO2*/
-	regmap_update_bits(rt286->regmap, RT286_POWER_CTRL2, 0x8, 0x0);
+	regmap_write(rt286->regmap, RT286_MISC_CTRL1, 0x0000);
+	/* Power down LDO, VREF */
+	regmap_update_bits(rt286->regmap, RT286_POWER_CTRL2, 0xc, 0x0);
+	regmap_update_bits(rt286->regmap, RT286_POWER_CTRL1, 0x1001, 0x1001);
 
-	/*Set depop parameter*/
+	/* Set depop parameter */
 	regmap_update_bits(rt286->regmap, RT286_DEPOP_CTRL2, 0x403a, 0x401a);
 	regmap_update_bits(rt286->regmap, RT286_DEPOP_CTRL3, 0xf777, 0x4737);
 	regmap_update_bits(rt286->regmap, RT286_DEPOP_CTRL4, 0x00ff, 0x003f);
diff --git a/sound/soc/codecs/rt5631.c b/sound/soc/codecs/rt5631.c
index 1ba27db660a6..6d7b7ca7d530 100644
--- a/sound/soc/codecs/rt5631.c
+++ b/sound/soc/codecs/rt5631.c
@@ -1612,29 +1612,6 @@ static int rt5631_probe(struct snd_soc_codec *codec)
 	return 0;
 }
 
-static int rt5631_remove(struct snd_soc_codec *codec)
-{
-	rt5631_set_bias_level(codec, SND_SOC_BIAS_OFF);
-	return 0;
-}
-
-#ifdef CONFIG_PM
-static int rt5631_suspend(struct snd_soc_codec *codec)
-{
-	rt5631_set_bias_level(codec, SND_SOC_BIAS_OFF);
-	return 0;
-}
-
-static int rt5631_resume(struct snd_soc_codec *codec)
-{
-	rt5631_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
-	return 0;
-}
-#else
-#define rt5631_suspend NULL
-#define rt5631_resume NULL
-#endif
-
 #define RT5631_STEREO_RATES SNDRV_PCM_RATE_8000_96000
 #define RT5631_FORMAT	(SNDRV_PCM_FMTBIT_S16_LE | \
 			SNDRV_PCM_FMTBIT_S20_3LE | \
@@ -1672,10 +1649,8 @@ static struct snd_soc_dai_driver rt5631_dai[] = {
 
 static struct snd_soc_codec_driver soc_codec_dev_rt5631 = {
 	.probe = rt5631_probe,
-	.remove = rt5631_remove,
-	.suspend = rt5631_suspend,
-	.resume = rt5631_resume,
 	.set_bias_level = rt5631_set_bias_level,
+	.suspend_bias_off = true,
 	.controls = rt5631_snd_controls,
 	.num_controls = ARRAY_SIZE(rt5631_snd_controls),
 	.dapm_widgets = rt5631_dapm_widgets,
@@ -1686,10 +1661,20 @@ static struct snd_soc_codec_driver soc_codec_dev_rt5631 = {
 
 static const struct i2c_device_id rt5631_i2c_id[] = {
 	{ "rt5631", 0 },
+	{ "alc5631", 0 },
 	{ }
 };
 MODULE_DEVICE_TABLE(i2c, rt5631_i2c_id);
 
+#ifdef CONFIG_OF
+static struct of_device_id rt5631_i2c_dt_ids[] = {
+	{ .compatible = "realtek,rt5631"},
+	{ .compatible = "realtek,alc5631"},
+	{ }
+};
+MODULE_DEVICE_TABLE(of, rt5631_i2c_dt_ids);
+#endif
+
 static const struct regmap_config rt5631_regmap_config = {
 	.reg_bits = 8,
 	.val_bits = 16,
@@ -1734,6 +1719,7 @@ static struct i2c_driver rt5631_i2c_driver = {
 	.driver = {
 		.name = "rt5631",
 		.owner = THIS_MODULE,
+		.of_match_table = of_match_ptr(rt5631_i2c_dt_ids),
 	},
 	.probe = rt5631_i2c_probe,
 	.remove   = rt5631_i2c_remove,
diff --git a/sound/soc/codecs/rt5645.c b/sound/soc/codecs/rt5645.c
index d16331e0b64d..a7789a8726e3 100644
--- a/sound/soc/codecs/rt5645.c
+++ b/sound/soc/codecs/rt5645.c
@@ -554,6 +554,53 @@ static int is_sys_clk_from_pll(struct snd_soc_dapm_widget *source,
 		return 0;
 }
 
+static int is_using_asrc(struct snd_soc_dapm_widget *source,
+			 struct snd_soc_dapm_widget *sink)
+{
+	unsigned int reg, shift, val;
+
+	switch (source->shift) {
+	case 0:
+		reg = RT5645_ASRC_3;
+		shift = 0;
+		break;
+	case 1:
+		reg = RT5645_ASRC_3;
+		shift = 4;
+		break;
+	case 3:
+		reg = RT5645_ASRC_2;
+		shift = 0;
+		break;
+	case 8:
+		reg = RT5645_ASRC_2;
+		shift = 4;
+		break;
+	case 9:
+		reg = RT5645_ASRC_2;
+		shift = 8;
+		break;
+	case 10:
+		reg = RT5645_ASRC_2;
+		shift = 12;
+		break;
+	default:
+		return 0;
+	}
+
+	val = (snd_soc_read(source->codec, reg) >> shift) & 0xf;
+	switch (val) {
+	case 1:
+	case 2:
+	case 3:
+	case 4:
+		return 1;
+	default:
+		return 0;
+	}
+
+}
+
 /* Digital Mixer */
 static const struct snd_kcontrol_new rt5645_sto1_adc_l_mix[] = {
 	SOC_DAPM_SINGLE("ADC1 Switch", RT5645_STO1_ADC_MIXER,
@@ -1246,6 +1293,30 @@ static const struct snd_soc_dapm_widget rt5645_dapm_widgets[] = {
 	SND_SOC_DAPM_SUPPLY("Mic Det Power", RT5645_PWR_VOL,
 		RT5645_PWR_MIC_DET_BIT, 0, NULL, 0),
 
+	/* ASRC */
+	SND_SOC_DAPM_SUPPLY_S("I2S1 ASRC", 1, RT5645_ASRC_1,
+			      11, 0, NULL, 0),
+	SND_SOC_DAPM_SUPPLY_S("I2S2 ASRC", 1, RT5645_ASRC_1,
+			      12, 0, NULL, 0),
+	SND_SOC_DAPM_SUPPLY_S("DAC STO ASRC", 1, RT5645_ASRC_1,
+			      10, 0, NULL, 0),
+	SND_SOC_DAPM_SUPPLY_S("DAC MONO L ASRC", 1, RT5645_ASRC_1,
+			      9, 0, NULL, 0),
+	SND_SOC_DAPM_SUPPLY_S("DAC MONO R ASRC", 1, RT5645_ASRC_1,
+			      8, 0, NULL, 0),
+	SND_SOC_DAPM_SUPPLY_S("DMIC STO1 ASRC", 1, RT5645_ASRC_1,
+			      7, 0, NULL, 0),
+	SND_SOC_DAPM_SUPPLY_S("DMIC MONO L ASRC", 1, RT5645_ASRC_1,
+			      5, 0, NULL, 0),
+	SND_SOC_DAPM_SUPPLY_S("DMIC MONO R ASRC", 1, RT5645_ASRC_1,
+			      4, 0, NULL, 0),
+	SND_SOC_DAPM_SUPPLY_S("ADC STO1 ASRC", 1, RT5645_ASRC_1,
+			      3, 0, NULL, 0),
+	SND_SOC_DAPM_SUPPLY_S("ADC MONO L ASRC", 1, RT5645_ASRC_1,
+			      1, 0, NULL, 0),
+	SND_SOC_DAPM_SUPPLY_S("ADC MONO R ASRC", 1, RT5645_ASRC_1,
+			      0, 0, NULL, 0),
+
 	/* Input Side */
 	/* micbias */
 	SND_SOC_DAPM_MICBIAS("micbias1", RT5645_PWR_ANLG2,
@@ -1504,6 +1575,17 @@ static const struct snd_soc_dapm_widget rt5645_dapm_widgets[] = {
 };
 
 static const struct snd_soc_dapm_route rt5645_dapm_routes[] = {
+	{ "adc stereo1 filter", NULL, "ADC STO1 ASRC", is_using_asrc },
+	{ "adc stereo2 filter", NULL, "ADC STO2 ASRC", is_using_asrc },
+	{ "adc mono left filter", NULL, "ADC MONO L ASRC", is_using_asrc },
+	{ "adc mono right filter", NULL, "ADC MONO R ASRC", is_using_asrc },
+	{ "dac mono left filter", NULL, "DAC MONO L ASRC", is_using_asrc },
+	{ "dac mono right filter", NULL, "DAC MONO R ASRC", is_using_asrc },
+	{ "dac stereo1 filter", NULL, "DAC STO ASRC", is_using_asrc },
+
+	{ "I2S1", NULL, "I2S1 ASRC" },
+	{ "I2S2", NULL, "I2S2 ASRC" },
+
 	{ "IN1P", NULL, "LDO2" },
 	{ "IN2P", NULL, "LDO2" },
 
@@ -1550,12 +1632,15 @@ static const struct snd_soc_dapm_route rt5645_dapm_routes[] = {
 
 	{ "Stereo1 DMIC Mux", "DMIC1", "DMIC1" },
 	{ "Stereo1 DMIC Mux", "DMIC2", "DMIC2" },
+	{ "Stereo1 DMIC Mux", NULL, "DMIC STO1 ASRC" },
 
 	{ "Mono DMIC L Mux", "DMIC1", "DMIC L1" },
 	{ "Mono DMIC L Mux", "DMIC2", "DMIC L2" },
+	{ "Mono DMIC L Mux", NULL, "DMIC MONO L ASRC" },
 
 	{ "Mono DMIC R Mux", "DMIC1", "DMIC R1" },
 	{ "Mono DMIC R Mux", "DMIC2", "DMIC R2" },
+	{ "Mono DMIC R Mux", NULL, "DMIC MONO R ASRC" },
 
 	{ "Stereo1 ADC L2 Mux", "DMIC", "Stereo1 DMIC Mux" },
 	{ "Stereo1 ADC L2 Mux", "DAC MIX", "DAC MIXL" },
@@ -2029,8 +2114,11 @@ static int rt5645_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask,
 	struct snd_soc_codec *codec = dai->codec;
 	unsigned int val = 0;
 
-	if (rx_mask || tx_mask)
+	if (rx_mask || tx_mask) {
 		val |= (1 << 14);
+		snd_soc_update_bits(codec, RT5645_BASS_BACK,
+			RT5645_G_BB_BST_MASK, RT5645_G_BB_BST_25DB);
+	}
 
 	switch (slots) {
 	case 4:
@@ -2071,8 +2159,8 @@ static int rt5645_set_bias_level(struct snd_soc_codec *codec,
 			enum snd_soc_bias_level level)
 {
 	switch (level) {
-	case SND_SOC_BIAS_STANDBY:
-		if (SND_SOC_BIAS_OFF == codec->dapm.bias_level) {
+	case SND_SOC_BIAS_PREPARE:
+		if (SND_SOC_BIAS_STANDBY == codec->dapm.bias_level) {
 			snd_soc_update_bits(codec, RT5645_PWR_ANLG1,
 				RT5645_PWR_VREF1 | RT5645_PWR_MB |
 				RT5645_PWR_BG | RT5645_PWR_VREF2,
@@ -2087,15 +2175,24 @@ static int rt5645_set_bias_level(struct snd_soc_codec *codec,
 		}
 		break;
 
+	case SND_SOC_BIAS_STANDBY:
+		snd_soc_update_bits(codec, RT5645_PWR_ANLG1,
+			RT5645_PWR_VREF1 | RT5645_PWR_MB |
+			RT5645_PWR_BG | RT5645_PWR_VREF2,
+			RT5645_PWR_VREF1 | RT5645_PWR_MB |
+			RT5645_PWR_BG | RT5645_PWR_VREF2);
+		snd_soc_update_bits(codec, RT5645_PWR_ANLG1,
+			RT5645_PWR_FV1 | RT5645_PWR_FV2,
+			RT5645_PWR_FV1 | RT5645_PWR_FV2);
+		break;
+
 	case SND_SOC_BIAS_OFF:
 		snd_soc_write(codec, RT5645_DEPOP_M2, 0x1100);
 		snd_soc_write(codec, RT5645_GEN_CTRL1, 0x0128);
-		snd_soc_write(codec, RT5645_PWR_DIG1, 0x0000);
-		snd_soc_write(codec, RT5645_PWR_DIG2, 0x0000);
-		snd_soc_write(codec, RT5645_PWR_VOL, 0x0000);
-		snd_soc_write(codec, RT5645_PWR_MIXER, 0x0000);
-		snd_soc_write(codec, RT5645_PWR_ANLG1, 0x0000);
-		snd_soc_write(codec, RT5645_PWR_ANLG2, 0x0000);
+		snd_soc_update_bits(codec, RT5645_PWR_ANLG1,
+				RT5645_PWR_VREF1 | RT5645_PWR_MB |
+				RT5645_PWR_BG | RT5645_PWR_VREF2 |
+				RT5645_PWR_FV1 | RT5645_PWR_FV2, 0x0);
 		break;
 
 	default:
@@ -2106,8 +2203,7 @@ static int rt5645_set_bias_level(struct snd_soc_codec *codec,
 	return 0;
 }
 
-static int rt5645_jack_detect(struct snd_soc_codec *codec,
-	struct snd_soc_jack *jack)
+static int rt5645_jack_detect(struct snd_soc_codec *codec)
 {
 	struct rt5645_priv *rt5645 = snd_soc_codec_get_drvdata(codec);
 	int gpio_state, jack_type = 0;
@@ -2145,34 +2241,44 @@ static int rt5645_jack_detect(struct snd_soc_codec *codec,
 
 		snd_soc_dapm_disable_pin(&codec->dapm, "micbias1");
 		snd_soc_dapm_disable_pin(&codec->dapm, "micbias2");
-		snd_soc_dapm_disable_pin(&codec->dapm, "LDO2");
+		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);
 	}
 
-	snd_soc_jack_report(rt5645->jack, jack_type, SND_JACK_HEADSET);
-
+	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;
 }
 
 int rt5645_set_jack_detect(struct snd_soc_codec *codec,
-	struct snd_soc_jack *jack)
+	struct snd_soc_jack *hp_jack, struct snd_soc_jack *mic_jack)
 {
 	struct rt5645_priv *rt5645 = snd_soc_codec_get_drvdata(codec);
 
-	rt5645->jack = jack;
-
-	rt5645_jack_detect(codec, rt5645->jack);
+	rt5645->hp_jack = hp_jack;
+	rt5645->mic_jack = mic_jack;
+	rt5645_jack_detect(codec);
 
 	return 0;
 }
 EXPORT_SYMBOL_GPL(rt5645_set_jack_detect);
 
+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);
+}
+
 static irqreturn_t rt5645_irq(int irq, void *data)
 {
 	struct rt5645_priv *rt5645 = data;
 
-	rt5645_jack_detect(rt5645->codec, rt5645->jack);
+	queue_delayed_work(system_power_efficient_wq,
+			   &rt5645->jack_detect_work, msecs_to_jiffies(250));
 
 	return IRQ_HANDLED;
 }
@@ -2187,6 +2293,13 @@ static int rt5645_probe(struct snd_soc_codec *codec)
 
 	snd_soc_update_bits(codec, RT5645_CHARGE_PUMP, 0x0300, 0x0200);
 
+	/* for JD function */
+	if (rt5645->pdata.en_jd_func) {
+		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);
+	}
+
 	return 0;
 }
 
@@ -2420,6 +2533,51 @@ static int rt5645_i2c_probe(struct i2c_client *i2c,
 
 	}
 
+	if (rt5645->pdata.en_jd_func) {
+		regmap_update_bits(rt5645->regmap, RT5645_GEN_CTRL3,
+			RT5645_IRQ_CLK_GATE_CTRL | RT5645_MICINDET_MANU,
+			RT5645_IRQ_CLK_GATE_CTRL | RT5645_MICINDET_MANU);
+		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);
+		regmap_update_bits(rt5645->regmap, RT5645_MICBIAS,
+			RT5645_IRQ_CLK_INT, RT5645_IRQ_CLK_INT);
+	}
+
+	if (rt5645->pdata.jd_mode) {
+		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,
+				   RT5645_JD_PSV_MODE, RT5645_JD_PSV_MODE);
+		regmap_update_bits(rt5645->regmap, RT5645_HPO_MIXER,
+				   RT5645_IRQ_PSV_MODE, RT5645_IRQ_PSV_MODE);
+		regmap_update_bits(rt5645->regmap, RT5645_MICBIAS,
+				   RT5645_MIC2_OVCD_EN, RT5645_MIC2_OVCD_EN);
+		regmap_update_bits(rt5645->regmap, RT5645_GPIO_CTRL1,
+				   RT5645_GP1_PIN_IRQ, RT5645_GP1_PIN_IRQ);
+		switch (rt5645->pdata.jd_mode) {
+		case 1:
+			regmap_update_bits(rt5645->regmap, RT5645_A_JD_CTRL1,
+					   RT5645_JD1_MODE_MASK,
+					   RT5645_JD1_MODE_0);
+			break;
+		case 2:
+			regmap_update_bits(rt5645->regmap, RT5645_A_JD_CTRL1,
+					   RT5645_JD1_MODE_MASK,
+					   RT5645_JD1_MODE_1);
+			break;
+		case 3:
+			regmap_update_bits(rt5645->regmap, RT5645_A_JD_CTRL1,
+					   RT5645_JD1_MODE_MASK,
+					   RT5645_JD1_MODE_2);
+			break;
+		default:
+			break;
+		}
+	}
+
 	if (rt5645->i2c->irq) {
 		ret = request_threaded_irq(rt5645->i2c->irq, NULL, rt5645_irq,
 			IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING
@@ -2438,6 +2596,8 @@ static int rt5645_i2c_probe(struct i2c_client *i2c,
 			dev_err(&i2c->dev, "Fail gpio_direction hp_det_gpio\n");
 	}
 
+	INIT_DELAYED_WORK(&rt5645->jack_detect_work, rt5645_jack_detect_work);
+
 	return snd_soc_register_codec(&i2c->dev, &soc_codec_dev_rt5645,
 				      rt5645_dai, ARRAY_SIZE(rt5645_dai));
 }
@@ -2449,6 +2609,8 @@ static int rt5645_i2c_remove(struct i2c_client *i2c)
 	if (i2c->irq)
 		free_irq(i2c->irq, rt5645);
 
+	cancel_delayed_work_sync(&rt5645->jack_detect_work);
+
 	if (gpio_is_valid(rt5645->pdata.hp_det_gpio))
 		gpio_free(rt5645->pdata.hp_det_gpio);
 
diff --git a/sound/soc/codecs/rt5645.h b/sound/soc/codecs/rt5645.h
index 50c62c5668ea..a815e36a2bdb 100644
--- a/sound/soc/codecs/rt5645.h
+++ b/sound/soc/codecs/rt5645.h
@@ -594,6 +594,7 @@
 #define RT5645_M_DAC1_HM_SFT			14
 #define RT5645_M_HPVOL_HM			(0x1 << 13)
 #define RT5645_M_HPVOL_HM_SFT			13
+#define RT5645_IRQ_PSV_MODE			(0x1 << 12)
 
 /* SPK Left Mixer Control (0x46) */
 #define RT5645_G_RM_L_SM_L_MASK			(0x3 << 14)
@@ -1348,6 +1349,12 @@
 #define RT5645_PWR_CLK25M_SFT			4
 #define RT5645_PWR_CLK25M_PD			(0x0 << 4)
 #define RT5645_PWR_CLK25M_PU			(0x1 << 4)
+#define RT5645_IRQ_CLK_MCLK			(0x0 << 3)
+#define RT5645_IRQ_CLK_INT			(0x1 << 3)
+#define RT5645_JD1_MODE_MASK			(0x3 << 0)
+#define RT5645_JD1_MODE_0			(0x0 << 0)
+#define RT5645_JD1_MODE_1			(0x1 << 0)
+#define RT5645_JD1_MODE_2			(0x2 << 0)
 
 /* VAD Control 4 (0x9d) */
 #define RT5645_VAD_SEL_MASK			(0x3 << 8)
@@ -1636,6 +1643,7 @@
 #define RT5645_OT_P_SFT				10
 #define RT5645_OT_P_NOR				(0x0 << 10)
 #define RT5645_OT_P_INV				(0x1 << 10)
+#define RT5645_IRQ_JD_1_1_EN			(0x1 << 9)
 
 /* IRQ Control 2 (0xbe) */
 #define RT5645_IRQ_MB1_OC_MASK			(0x1 << 15)
@@ -1853,6 +1861,7 @@
 #define RT5645_M_BB_HPF_R_SFT			6
 #define RT5645_G_BB_BST_MASK			(0x3f)
 #define RT5645_G_BB_BST_SFT			0
+#define RT5645_G_BB_BST_25DB			0x14
 
 /* MP3 Plus Control 1 (0xd0) */
 #define RT5645_M_MP3_L_MASK			(0x1 << 15)
@@ -2116,6 +2125,10 @@ enum {
 #define RT5645_RXDP2_SEL_ADC			(0x1 << 3)
 #define RT5645_RXDP2_SEL_SFT			(3)
 
+/* General Control3 (0xfc) */
+#define RT5645_JD_PSV_MODE			(0x1 << 12)
+#define RT5645_IRQ_CLK_GATE_CTRL		(0x1 << 11)
+#define RT5645_MICINDET_MANU			(0x1 << 7)
 
 /* Vendor ID (0xfd) */
 #define RT5645_VER_C				0x2
@@ -2167,7 +2180,9 @@ struct rt5645_priv {
 	struct rt5645_platform_data pdata;
 	struct regmap *regmap;
 	struct i2c_client *i2c;
-	struct snd_soc_jack *jack;
+	struct snd_soc_jack *hp_jack;
+	struct snd_soc_jack *mic_jack;
+	struct delayed_work jack_detect_work;
 
 	int sysclk;
 	int sysclk_src;
@@ -2181,6 +2196,6 @@ struct rt5645_priv {
 };
 
 int rt5645_set_jack_detect(struct snd_soc_codec *codec,
-	struct snd_soc_jack *jack);
+	struct snd_soc_jack *hp_jack, struct snd_soc_jack *mic_jack);
 
 #endif /* __RT5645_H__ */
diff --git a/sound/soc/codecs/rt5670.c b/sound/soc/codecs/rt5670.c
index 9bd8b4f63303..8a0833de1665 100644
--- a/sound/soc/codecs/rt5670.c
+++ b/sound/soc/codecs/rt5670.c
@@ -16,6 +16,7 @@
 #include <linux/pm.h>
 #include <linux/i2c.h>
 #include <linux/platform_device.h>
+#include <linux/acpi.h>
 #include <linux/spi/spi.h>
 #include <sound/core.h>
 #include <sound/pcm.h>
@@ -575,6 +576,18 @@ static int is_using_asrc(struct snd_soc_dapm_widget *source,
 
 }
 
+static int can_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 rt5670_priv *rt5670 = snd_soc_codec_get_drvdata(codec);
+
+	if (rt5670->sysclk > rt5670->lrck[RT5670_AIF1] * 384)
+		return 1;
+
+	return 0;
+}
+
 /* Digital Mixer */
 static const struct snd_kcontrol_new rt5670_sto1_adc_l_mix[] = {
 	SOC_DAPM_SINGLE("ADC1 Switch", RT5670_STO1_ADC_MIXER,
@@ -1281,6 +1294,14 @@ static const struct snd_soc_dapm_widget rt5670_dapm_widgets[] = {
 			      9, 0, NULL, 0),
 	SND_SOC_DAPM_SUPPLY_S("DAC MONO R ASRC", 1, RT5670_ASRC_1,
 			      8, 0, NULL, 0),
+	SND_SOC_DAPM_SUPPLY_S("DMIC STO1 ASRC", 1, RT5670_ASRC_1,
+			      7, 0, NULL, 0),
+	SND_SOC_DAPM_SUPPLY_S("DMIC STO2 ASRC", 1, RT5670_ASRC_1,
+			      6, 0, NULL, 0),
+	SND_SOC_DAPM_SUPPLY_S("DMIC MONO L ASRC", 1, RT5670_ASRC_1,
+			      5, 0, NULL, 0),
+	SND_SOC_DAPM_SUPPLY_S("DMIC MONO R ASRC", 1, RT5670_ASRC_1,
+			      4, 0, NULL, 0),
 	SND_SOC_DAPM_SUPPLY_S("ADC STO1 ASRC", 1, RT5670_ASRC_1,
 			      3, 0, NULL, 0),
 	SND_SOC_DAPM_SUPPLY_S("ADC STO2 ASRC", 1, RT5670_ASRC_1,
@@ -1595,29 +1616,40 @@ static const struct snd_soc_dapm_widget rt5670_dapm_widgets[] = {
 	/* PDM */
 	SND_SOC_DAPM_SUPPLY("PDM1 Power", RT5670_PWR_DIG2,
 		RT5670_PWR_PDM1_BIT, 0, NULL, 0),
-	SND_SOC_DAPM_SUPPLY("PDM2 Power", RT5670_PWR_DIG2,
-		RT5670_PWR_PDM2_BIT, 0, NULL, 0),
 
 	SND_SOC_DAPM_MUX("PDM1 L Mux", RT5670_PDM_OUT_CTRL,
 			 RT5670_M_PDM1_L_SFT, 1, &rt5670_pdm1_l_mux),
 	SND_SOC_DAPM_MUX("PDM1 R Mux", RT5670_PDM_OUT_CTRL,
 			 RT5670_M_PDM1_R_SFT, 1, &rt5670_pdm1_r_mux),
-	SND_SOC_DAPM_MUX("PDM2 L Mux", RT5670_PDM_OUT_CTRL,
-			 RT5670_M_PDM2_L_SFT, 1, &rt5670_pdm2_l_mux),
-	SND_SOC_DAPM_MUX("PDM2 R Mux", RT5670_PDM_OUT_CTRL,
-			 RT5670_M_PDM2_R_SFT, 1, &rt5670_pdm2_r_mux),
 
 	/* Output Lines */
 	SND_SOC_DAPM_OUTPUT("HPOL"),
 	SND_SOC_DAPM_OUTPUT("HPOR"),
 	SND_SOC_DAPM_OUTPUT("LOUTL"),
 	SND_SOC_DAPM_OUTPUT("LOUTR"),
+};
+
+static const struct snd_soc_dapm_widget rt5670_specific_dapm_widgets[] = {
+	SND_SOC_DAPM_SUPPLY("PDM2 Power", RT5670_PWR_DIG2,
+		RT5670_PWR_PDM2_BIT, 0, NULL, 0),
+	SND_SOC_DAPM_MUX("PDM2 L Mux", RT5670_PDM_OUT_CTRL,
+			 RT5670_M_PDM2_L_SFT, 1, &rt5670_pdm2_l_mux),
+	SND_SOC_DAPM_MUX("PDM2 R Mux", RT5670_PDM_OUT_CTRL,
+			 RT5670_M_PDM2_R_SFT, 1, &rt5670_pdm2_r_mux),
 	SND_SOC_DAPM_OUTPUT("PDM1L"),
 	SND_SOC_DAPM_OUTPUT("PDM1R"),
 	SND_SOC_DAPM_OUTPUT("PDM2L"),
 	SND_SOC_DAPM_OUTPUT("PDM2R"),
 };
 
+static const struct snd_soc_dapm_widget rt5672_specific_dapm_widgets[] = {
+	SND_SOC_DAPM_PGA("SPO Amp", SND_SOC_NOPM, 0, 0, NULL, 0),
+	SND_SOC_DAPM_OUTPUT("SPOLP"),
+	SND_SOC_DAPM_OUTPUT("SPOLN"),
+	SND_SOC_DAPM_OUTPUT("SPORP"),
+	SND_SOC_DAPM_OUTPUT("SPORN"),
+};
+
 static const struct snd_soc_dapm_route rt5670_dapm_routes[] = {
 	{ "ADC Stereo1 Filter", NULL, "ADC STO1 ASRC", is_using_asrc },
 	{ "ADC Stereo2 Filter", NULL, "ADC STO2 ASRC", is_using_asrc },
@@ -1626,9 +1658,13 @@ static const struct snd_soc_dapm_route rt5670_dapm_routes[] = {
 	{ "DAC Mono Left Filter", NULL, "DAC MONO L ASRC", is_using_asrc },
 	{ "DAC Mono Right Filter", NULL, "DAC MONO R ASRC", is_using_asrc },
 	{ "DAC Stereo1 Filter", NULL, "DAC STO ASRC", is_using_asrc },
+	{ "Stereo1 DMIC Mux", NULL, "DMIC STO1 ASRC", can_use_asrc },
+	{ "Stereo2 DMIC Mux", NULL, "DMIC STO2 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 },
 
-	{ "I2S1", NULL, "I2S1 ASRC" },
-	{ "I2S2", NULL, "I2S2 ASRC" },
+	{ "I2S1", NULL, "I2S1 ASRC", can_use_asrc},
+	{ "I2S2", NULL, "I2S2 ASRC", can_use_asrc},
 
 	{ "DMIC1", NULL, "DMIC L1" },
 	{ "DMIC1", NULL, "DMIC R1" },
@@ -1970,12 +2006,6 @@ static const struct snd_soc_dapm_route rt5670_dapm_routes[] = {
 	{ "PDM1 R Mux", "Stereo DAC", "Stereo DAC MIXR" },
 	{ "PDM1 R Mux", "Mono DAC", "Mono DAC MIXR" },
 	{ "PDM1 R Mux", NULL, "PDM1 Power" },
-	{ "PDM2 L Mux", "Stereo DAC", "Stereo DAC MIXL" },
-	{ "PDM2 L Mux", "Mono DAC", "Mono DAC MIXL" },
-	{ "PDM2 L Mux", NULL, "PDM2 Power" },
-	{ "PDM2 R Mux", "Stereo DAC", "Stereo DAC MIXR" },
-	{ "PDM2 R Mux", "Mono DAC", "Mono DAC MIXR" },
-	{ "PDM2 R Mux", NULL, "PDM2 Power" },
 
 	{ "HP Amp", NULL, "HPO MIX" },
 	{ "HP Amp", NULL, "Mic Det Power" },
@@ -1993,13 +2023,30 @@ static const struct snd_soc_dapm_route rt5670_dapm_routes[] = {
 	{ "LOUTR", NULL, "LOUT R Playback" },
 	{ "LOUTL", NULL, "Improve HP Amp Drv" },
 	{ "LOUTR", NULL, "Improve HP Amp Drv" },
+};
 
+static const struct snd_soc_dapm_route rt5670_specific_dapm_routes[] = {
+	{ "PDM2 L Mux", "Stereo DAC", "Stereo DAC MIXL" },
+	{ "PDM2 L Mux", "Mono DAC", "Mono DAC MIXL" },
+	{ "PDM2 L Mux", NULL, "PDM2 Power" },
+	{ "PDM2 R Mux", "Stereo DAC", "Stereo DAC MIXR" },
+	{ "PDM2 R Mux", "Mono DAC", "Mono DAC MIXR" },
+	{ "PDM2 R Mux", NULL, "PDM2 Power" },
 	{ "PDM1L", NULL, "PDM1 L Mux" },
 	{ "PDM1R", NULL, "PDM1 R Mux" },
 	{ "PDM2L", NULL, "PDM2 L Mux" },
 	{ "PDM2R", NULL, "PDM2 R Mux" },
 };
 
+static const struct snd_soc_dapm_route rt5672_specific_dapm_routes[] = {
+	{ "SPO Amp", NULL, "PDM1 L Mux" },
+	{ "SPO Amp", NULL, "PDM1 R Mux" },
+	{ "SPOLP", NULL, "SPO Amp" },
+	{ "SPOLN", NULL, "SPO Amp" },
+	{ "SPORP", NULL, "SPO Amp" },
+	{ "SPORN", NULL, "SPO Amp" },
+};
+
 static int rt5670_hw_params(struct snd_pcm_substream *substream,
 	struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
 {
@@ -2287,6 +2334,8 @@ static int rt5670_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask,
 static int rt5670_set_bias_level(struct snd_soc_codec *codec,
 			enum snd_soc_bias_level level)
 {
+	struct rt5670_priv *rt5670 = snd_soc_codec_get_drvdata(codec);
+
 	switch (level) {
 	case SND_SOC_BIAS_PREPARE:
 		if (SND_SOC_BIAS_STANDBY == codec->dapm.bias_level) {
@@ -2308,16 +2357,27 @@ static int rt5670_set_bias_level(struct snd_soc_codec *codec,
 		}
 		break;
 	case SND_SOC_BIAS_STANDBY:
-		snd_soc_write(codec, RT5670_PWR_DIG1, 0x0000);
-		snd_soc_write(codec, RT5670_PWR_DIG2, 0x0001);
-		snd_soc_write(codec, RT5670_PWR_VOL, 0x0000);
-		snd_soc_write(codec, RT5670_PWR_MIXER, 0x0001);
-		snd_soc_write(codec, RT5670_PWR_ANLG1, 0x2800);
-		snd_soc_write(codec, RT5670_PWR_ANLG2, 0x0004);
-		snd_soc_update_bits(codec, RT5670_DIG_MISC, 0x1, 0x0);
+		snd_soc_update_bits(codec, RT5670_PWR_ANLG1,
+				RT5670_PWR_VREF1 | RT5670_PWR_VREF2 |
+				RT5670_PWR_FV1 | RT5670_PWR_FV2, 0);
 		snd_soc_update_bits(codec, RT5670_PWR_ANLG1,
 				RT5670_LDO_SEL_MASK, 0x1);
 		break;
+	case SND_SOC_BIAS_OFF:
+		if (rt5670->pdata.jd_mode)
+			snd_soc_update_bits(codec, RT5670_PWR_ANLG1,
+				RT5670_PWR_VREF1 | RT5670_PWR_MB |
+				RT5670_PWR_BG | RT5670_PWR_VREF2 |
+				RT5670_PWR_FV1 | RT5670_PWR_FV2,
+				RT5670_PWR_MB | RT5670_PWR_BG);
+		else
+			snd_soc_update_bits(codec, RT5670_PWR_ANLG1,
+				RT5670_PWR_VREF1 | RT5670_PWR_MB |
+				RT5670_PWR_BG | RT5670_PWR_VREF2 |
+				RT5670_PWR_FV1 | RT5670_PWR_FV2, 0);
+
+		snd_soc_update_bits(codec, RT5670_DIG_MISC, 0x1, 0x0);
+		break;
 
 	default:
 		break;
@@ -2331,6 +2391,29 @@ static int rt5670_probe(struct snd_soc_codec *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,
+			rt5670_specific_dapm_widgets,
+			ARRAY_SIZE(rt5670_specific_dapm_widgets));
+		snd_soc_dapm_add_routes(&codec->dapm,
+			rt5670_specific_dapm_routes,
+			ARRAY_SIZE(rt5670_specific_dapm_routes));
+		break;
+	case RT5670_ID_5672:
+		snd_soc_dapm_new_controls(&codec->dapm,
+			rt5672_specific_dapm_widgets,
+			ARRAY_SIZE(rt5672_specific_dapm_widgets));
+		snd_soc_dapm_add_routes(&codec->dapm,
+			rt5672_specific_dapm_routes,
+			ARRAY_SIZE(rt5672_specific_dapm_routes));
+		break;
+	default:
+		dev_err(codec->dev,
+			"The driver is for RT5670 RT5671 or RT5672 only\n");
+		return -ENODEV;
+	}
 	rt5670->codec = codec;
 
 	return 0;
@@ -2452,10 +2535,20 @@ static const struct regmap_config rt5670_regmap = {
 
 static const struct i2c_device_id rt5670_i2c_id[] = {
 	{ "rt5670", 0 },
+	{ "rt5671", 0 },
+	{ "rt5672", 0 },
 	{ }
 };
 MODULE_DEVICE_TABLE(i2c, rt5670_i2c_id);
 
+#ifdef CONFIG_ACPI
+static struct acpi_device_id rt5670_acpi_match[] = {
+	{ "10EC5670", 0},
+	{ },
+};
+MODULE_DEVICE_TABLE(acpi, rt5670_acpi_match);
+#endif
+
 static int rt5670_i2c_probe(struct i2c_client *i2c,
 		    const struct i2c_device_id *id)
 {
@@ -2644,6 +2737,7 @@ static struct i2c_driver rt5670_i2c_driver = {
 	.driver = {
 		.name = "rt5670",
 		.owner = THIS_MODULE,
+		.acpi_match_table = ACPI_PTR(rt5670_acpi_match),
 	},
 	.probe = rt5670_i2c_probe,
 	.remove   = rt5670_i2c_remove,
diff --git a/sound/soc/codecs/rt5670.h b/sound/soc/codecs/rt5670.h
index a0b5c855b492..d11b9c207e26 100644
--- a/sound/soc/codecs/rt5670.h
+++ b/sound/soc/codecs/rt5670.h
@@ -228,6 +228,12 @@
 #define RT5670_R_VOL_MASK			(0x3f)
 #define RT5670_R_VOL_SFT			0
 
+/* SW Reset & Device ID (0x00) */
+#define RT5670_ID_MASK				(0x3 << 1)
+#define RT5670_ID_5670				(0x0 << 1)
+#define RT5670_ID_5672				(0x1 << 1)
+#define RT5670_ID_5671				(0x2 << 1)
+
 /* Combo Jack Control 1 (0x0a) */
 #define RT5670_CBJ_BST1_MASK			(0xf << 12)
 #define RT5670_CBJ_BST1_SFT			(12)
diff --git a/sound/soc/codecs/rt5677-spi.c b/sound/soc/codecs/rt5677-spi.c
new file mode 100644
index 000000000000..ef6348cb9157
--- /dev/null
+++ b/sound/soc/codecs/rt5677-spi.c
@@ -0,0 +1,130 @@
+/*
+ * rt5677-spi.c  --  RT5677 ALSA SoC audio codec driver
+ *
+ * Copyright 2013 Realtek Semiconductor Corp.
+ * Author: Oder Chiou <oder_chiou@realtek.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/module.h>
+#include <linux/input.h>
+#include <linux/spi/spi.h>
+#include <linux/device.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/slab.h>
+#include <linux/gpio.h>
+#include <linux/sched.h>
+#include <linux/kthread.h>
+#include <linux/uaccess.h>
+#include <linux/miscdevice.h>
+#include <linux/regulator/consumer.h>
+#include <linux/pm_qos.h>
+#include <linux/sysfs.h>
+#include <linux/clk.h>
+#include <linux/firmware.h>
+
+#include "rt5677-spi.h"
+
+static struct spi_device *g_spi;
+
+/**
+ * rt5677_spi_write - Write data to SPI.
+ * @txbuf: Data Buffer for writing.
+ * @len: Data length.
+ *
+ *
+ * Returns true for success.
+ */
+int rt5677_spi_write(u8 *txbuf, size_t len)
+{
+	int status;
+
+	status = spi_write(g_spi, txbuf, len);
+
+	if (status)
+		dev_err(&g_spi->dev, "rt5677_spi_write error %d\n", status);
+
+	return status;
+}
+EXPORT_SYMBOL_GPL(rt5677_spi_write);
+
+/**
+ * rt5677_spi_burst_write - Write data to SPI by rt5677 dsp memory address.
+ * @addr: Start address.
+ * @txbuf: Data Buffer for writng.
+ * @len: Data length, it must be a multiple of 8.
+ *
+ *
+ * Returns true for success.
+ */
+int rt5677_spi_burst_write(u32 addr, const struct firmware *fw)
+{
+	u8 spi_cmd = RT5677_SPI_CMD_BURST_WRITE;
+	u8 *write_buf;
+	unsigned int i, end, offset = 0;
+
+	write_buf = kmalloc(RT5677_SPI_BUF_LEN + 6, GFP_KERNEL);
+
+	if (write_buf == NULL)
+		return -ENOMEM;
+
+	while (offset < fw->size) {
+		if (offset + RT5677_SPI_BUF_LEN <= fw->size)
+			end = RT5677_SPI_BUF_LEN;
+		else
+			end = fw->size % RT5677_SPI_BUF_LEN;
+
+		write_buf[0] = spi_cmd;
+		write_buf[1] = ((addr + offset) & 0xff000000) >> 24;
+		write_buf[2] = ((addr + offset) & 0x00ff0000) >> 16;
+		write_buf[3] = ((addr + offset) & 0x0000ff00) >> 8;
+		write_buf[4] = ((addr + offset) & 0x000000ff) >> 0;
+
+		for (i = 0; i < end; i += 8) {
+			write_buf[i + 12] = fw->data[offset + i + 0];
+			write_buf[i + 11] = fw->data[offset + i + 1];
+			write_buf[i + 10] = fw->data[offset + i + 2];
+			write_buf[i +  9] = fw->data[offset + i + 3];
+			write_buf[i +  8] = fw->data[offset + i + 4];
+			write_buf[i +  7] = fw->data[offset + i + 5];
+			write_buf[i +  6] = fw->data[offset + i + 6];
+			write_buf[i +  5] = fw->data[offset + i + 7];
+		}
+
+		write_buf[end + 5] = spi_cmd;
+
+		rt5677_spi_write(write_buf, end + 6);
+
+		offset += RT5677_SPI_BUF_LEN;
+	}
+
+	kfree(write_buf);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(rt5677_spi_burst_write);
+
+static int rt5677_spi_probe(struct spi_device *spi)
+{
+	g_spi = spi;
+	return 0;
+}
+
+static struct spi_driver rt5677_spi_driver = {
+	.driver = {
+		.name = "rt5677",
+		.owner = THIS_MODULE,
+	},
+	.probe = rt5677_spi_probe,
+};
+module_spi_driver(rt5677_spi_driver);
+
+MODULE_DESCRIPTION("ASoC RT5677 SPI driver");
+MODULE_AUTHOR("Oder Chiou <oder_chiou@realtek.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/rt5677-spi.h b/sound/soc/codecs/rt5677-spi.h
new file mode 100644
index 000000000000..ec41b2b3b2ca
--- /dev/null
+++ b/sound/soc/codecs/rt5677-spi.h
@@ -0,0 +1,21 @@
+/*
+ * rt5677-spi.h  --  RT5677 ALSA SoC audio codec driver
+ *
+ * Copyright 2013 Realtek Semiconductor Corp.
+ * Author: Oder Chiou <oder_chiou@realtek.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef __RT5677_SPI_H__
+#define __RT5677_SPI_H__
+
+#define RT5677_SPI_BUF_LEN 240
+#define RT5677_SPI_CMD_BURST_WRITE 0x05
+
+int rt5677_spi_write(u8 *txbuf, size_t len);
+int rt5677_spi_burst_write(u32 addr, const struct firmware *fw);
+
+#endif /* __RT5677_SPI_H__ */
diff --git a/sound/soc/codecs/rt5677.c b/sound/soc/codecs/rt5677.c
index 16aa4d99a713..81fe1464d268 100644
--- a/sound/soc/codecs/rt5677.c
+++ b/sound/soc/codecs/rt5677.c
@@ -20,6 +20,7 @@
 #include <linux/i2c.h>
 #include <linux/platform_device.h>
 #include <linux/spi/spi.h>
+#include <linux/firmware.h>
 #include <linux/gpio.h>
 #include <sound/core.h>
 #include <sound/pcm.h>
@@ -31,6 +32,7 @@
 
 #include "rl6231.h"
 #include "rt5677.h"
+#include "rt5677-spi.h"
 
 #define RT5677_DEVICE_ID 0x6327
 
@@ -53,12 +55,13 @@ static const struct regmap_range_cfg rt5677_ranges[] = {
 };
 
 static const struct reg_default init_list[] = {
+	{RT5677_ASRC_12,	0x0018},
 	{RT5677_PR_BASE + 0x3d,	0x364d},
-	{RT5677_PR_BASE + 0x17, 0x4fc0},
-	{RT5677_PR_BASE + 0x13, 0x0312},
-	{RT5677_PR_BASE + 0x1e, 0x0000},
-	{RT5677_PR_BASE + 0x12, 0x0eaa},
-	{RT5677_PR_BASE + 0x14, 0x018a},
+	{RT5677_PR_BASE + 0x17,	0x4fc0},
+	{RT5677_PR_BASE + 0x13,	0x0312},
+	{RT5677_PR_BASE + 0x1e,	0x0000},
+	{RT5677_PR_BASE + 0x12,	0x0eaa},
+	{RT5677_PR_BASE + 0x14,	0x018a},
 };
 #define RT5677_INIT_REG_LEN ARRAY_SIZE(init_list)
 
@@ -171,7 +174,7 @@ static const struct reg_default rt5677_reg[] = {
 	{RT5677_ASRC_9			, 0x0000},
 	{RT5677_ASRC_10			, 0x0000},
 	{RT5677_ASRC_11			, 0x0000},
-	{RT5677_ASRC_12			, 0x0008},
+	{RT5677_ASRC_12			, 0x0018},
 	{RT5677_ASRC_13			, 0x0000},
 	{RT5677_ASRC_14			, 0x0000},
 	{RT5677_ASRC_15			, 0x0000},
@@ -537,10 +540,232 @@ static bool rt5677_readable_register(struct device *dev, unsigned int reg)
 	}
 }
 
+/**
+ * rt5677_dsp_mode_i2c_write_addr - Write value to address on DSP mode.
+ * @rt5677: Private Data.
+ * @addr: Address index.
+ * @value: Address data.
+ *
+ *
+ * Returns 0 for success or negative error code.
+ */
+static int rt5677_dsp_mode_i2c_write_addr(struct rt5677_priv *rt5677,
+		unsigned int addr, unsigned int value, unsigned int opcode)
+{
+	struct snd_soc_codec *codec = rt5677->codec;
+	int ret;
+
+	mutex_lock(&rt5677->dsp_cmd_lock);
+
+	ret = regmap_write(rt5677->regmap_physical, RT5677_DSP_I2C_ADDR_MSB,
+		addr >> 16);
+	if (ret < 0) {
+		dev_err(codec->dev, "Failed to set addr msb value: %d\n", ret);
+		goto err;
+	}
+
+	ret = regmap_write(rt5677->regmap_physical, RT5677_DSP_I2C_ADDR_LSB,
+		addr & 0xffff);
+	if (ret < 0) {
+		dev_err(codec->dev, "Failed to set addr lsb value: %d\n", ret);
+		goto err;
+	}
+
+	ret = regmap_write(rt5677->regmap_physical, RT5677_DSP_I2C_DATA_MSB,
+		value >> 16);
+	if (ret < 0) {
+		dev_err(codec->dev, "Failed to set data msb value: %d\n", ret);
+		goto err;
+	}
+
+	ret = regmap_write(rt5677->regmap_physical, RT5677_DSP_I2C_DATA_LSB,
+		value & 0xffff);
+	if (ret < 0) {
+		dev_err(codec->dev, "Failed to set data lsb value: %d\n", ret);
+		goto err;
+	}
+
+	ret = regmap_write(rt5677->regmap_physical, RT5677_DSP_I2C_OP_CODE,
+		opcode);
+	if (ret < 0) {
+		dev_err(codec->dev, "Failed to set op code value: %d\n", ret);
+		goto err;
+	}
+
+err:
+	mutex_unlock(&rt5677->dsp_cmd_lock);
+
+	return ret;
+}
+
+/**
+ * rt5677_dsp_mode_i2c_read_addr - Read value from address on DSP mode.
+ * rt5677: Private Data.
+ * @addr: Address index.
+ * @value: Address data.
+ *
+ *
+ * Returns 0 for success or negative error code.
+ */
+static int rt5677_dsp_mode_i2c_read_addr(
+	struct rt5677_priv *rt5677, unsigned int addr, unsigned int *value)
+{
+	struct snd_soc_codec *codec = rt5677->codec;
+	int ret;
+	unsigned int msb, lsb;
+
+	mutex_lock(&rt5677->dsp_cmd_lock);
+
+	ret = regmap_write(rt5677->regmap_physical, RT5677_DSP_I2C_ADDR_MSB,
+		addr >> 16);
+	if (ret < 0) {
+		dev_err(codec->dev, "Failed to set addr msb value: %d\n", ret);
+		goto err;
+	}
+
+	ret = regmap_write(rt5677->regmap_physical, RT5677_DSP_I2C_ADDR_LSB,
+		addr & 0xffff);
+	if (ret < 0) {
+		dev_err(codec->dev, "Failed to set addr lsb value: %d\n", ret);
+		goto err;
+	}
+
+	ret = regmap_write(rt5677->regmap_physical, RT5677_DSP_I2C_OP_CODE,
+		0x0002);
+	if (ret < 0) {
+		dev_err(codec->dev, "Failed to set op code value: %d\n", ret);
+		goto err;
+	}
+
+	regmap_read(rt5677->regmap_physical, RT5677_DSP_I2C_DATA_MSB, &msb);
+	regmap_read(rt5677->regmap_physical, RT5677_DSP_I2C_DATA_LSB, &lsb);
+	*value = (msb << 16) | lsb;
+
+err:
+	mutex_unlock(&rt5677->dsp_cmd_lock);
+
+	return ret;
+}
+
+/**
+ * rt5677_dsp_mode_i2c_write - Write register on DSP mode.
+ * rt5677: Private Data.
+ * @reg: Register index.
+ * @value: Register data.
+ *
+ *
+ * Returns 0 for success or negative error code.
+ */
+static int rt5677_dsp_mode_i2c_write(struct rt5677_priv *rt5677,
+		unsigned int reg, unsigned int value)
+{
+	return rt5677_dsp_mode_i2c_write_addr(rt5677, 0x18020000 + reg * 2,
+		value, 0x0001);
+}
+
+/**
+ * rt5677_dsp_mode_i2c_read - Read register on DSP mode.
+ * @codec: SoC audio codec device.
+ * @reg: Register index.
+ * @value: Register data.
+ *
+ *
+ * Returns 0 for success or negative error code.
+ */
+static int rt5677_dsp_mode_i2c_read(
+	struct rt5677_priv *rt5677, unsigned int reg, unsigned int *value)
+{
+	int ret = rt5677_dsp_mode_i2c_read_addr(rt5677, 0x18020000 + reg * 2,
+		value);
+
+	*value &= 0xffff;
+
+	return ret;
+}
+
+static void rt5677_set_dsp_mode(struct snd_soc_codec *codec, bool on)
+{
+	struct rt5677_priv *rt5677 = snd_soc_codec_get_drvdata(codec);
+
+	if (on) {
+		regmap_update_bits(rt5677->regmap, RT5677_PWR_DSP1, 0x2, 0x2);
+		rt5677->is_dsp_mode = true;
+	} else {
+		regmap_update_bits(rt5677->regmap, RT5677_PWR_DSP1, 0x2, 0x0);
+		rt5677->is_dsp_mode = false;
+	}
+}
+
+static int rt5677_set_dsp_vad(struct snd_soc_codec *codec, bool on)
+{
+	struct rt5677_priv *rt5677 = snd_soc_codec_get_drvdata(codec);
+	static bool activity;
+	int ret;
+
+	if (on && !activity) {
+		activity = true;
+
+		regcache_cache_only(rt5677->regmap, false);
+		regcache_cache_bypass(rt5677->regmap, true);
+
+		regmap_update_bits(rt5677->regmap, RT5677_DIG_MISC, 0x1, 0x1);
+		regmap_update_bits(rt5677->regmap,
+			RT5677_PR_BASE + RT5677_BIAS_CUR4, 0x0f00, 0x0f00);
+		regmap_update_bits(rt5677->regmap, RT5677_PWR_ANLG1,
+			RT5677_LDO1_SEL_MASK, 0x0);
+		regmap_update_bits(rt5677->regmap, RT5677_PWR_ANLG2,
+			RT5677_PWR_LDO1, RT5677_PWR_LDO1);
+		regmap_update_bits(rt5677->regmap, RT5677_GLB_CLK1,
+			RT5677_MCLK_SRC_MASK, RT5677_MCLK2_SRC);
+		regmap_update_bits(rt5677->regmap, RT5677_GLB_CLK2,
+			RT5677_PLL2_PR_SRC_MASK | RT5677_DSP_CLK_SRC_MASK,
+			RT5677_PLL2_PR_SRC_MCLK2 | RT5677_DSP_CLK_SRC_BYPASS);
+		regmap_write(rt5677->regmap, RT5677_PWR_DSP2, 0x07ff);
+		regmap_write(rt5677->regmap, RT5677_PWR_DSP1, 0x07fd);
+		rt5677_set_dsp_mode(codec, true);
+
+		ret = request_firmware(&rt5677->fw1, RT5677_FIRMWARE1,
+			codec->dev);
+		if (ret == 0) {
+			rt5677_spi_burst_write(0x50000000, rt5677->fw1);
+			release_firmware(rt5677->fw1);
+		}
+
+		ret = request_firmware(&rt5677->fw2, RT5677_FIRMWARE2,
+			codec->dev);
+		if (ret == 0) {
+			rt5677_spi_burst_write(0x60000000, rt5677->fw2);
+			release_firmware(rt5677->fw2);
+		}
+
+		regmap_update_bits(rt5677->regmap, RT5677_PWR_DSP1, 0x1, 0x0);
+
+		regcache_cache_bypass(rt5677->regmap, false);
+		regcache_cache_only(rt5677->regmap, true);
+	} else if (!on && activity) {
+		activity = false;
+
+		regcache_cache_only(rt5677->regmap, false);
+		regcache_cache_bypass(rt5677->regmap, true);
+
+		regmap_update_bits(rt5677->regmap, RT5677_PWR_DSP1, 0x1, 0x1);
+		rt5677_set_dsp_mode(codec, false);
+		regmap_write(rt5677->regmap, RT5677_PWR_DSP1, 0x0001);
+
+		regmap_write(rt5677->regmap, RT5677_RESET, 0x10ec);
+
+		regcache_cache_bypass(rt5677->regmap, false);
+		regcache_mark_dirty(rt5677->regmap);
+		regcache_sync(rt5677->regmap);
+	}
+
+	return 0;
+}
+
 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);
 static const DECLARE_TLV_DB_SCALE(st_vol_tlv, -4650, 150, 0);
 
@@ -556,6 +781,31 @@ static unsigned int bst_tlv[] = {
 	8, 8, TLV_DB_SCALE_ITEM(5200, 0, 0),
 };
 
+static int rt5677_dsp_vad_get(struct snd_kcontrol *kcontrol,
+		struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+	struct rt5677_priv *rt5677 = snd_soc_codec_get_drvdata(codec);
+
+	ucontrol->value.integer.value[0] = rt5677->dsp_vad_en;
+
+	return 0;
+}
+
+static int rt5677_dsp_vad_put(struct snd_kcontrol *kcontrol,
+		struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+	struct rt5677_priv *rt5677 = snd_soc_codec_get_drvdata(codec);
+
+	rt5677->dsp_vad_en = !!ucontrol->value.integer.value[0];
+
+	if (codec->dapm.bias_level == SND_SOC_BIAS_OFF)
+		rt5677_set_dsp_vad(codec, rt5677->dsp_vad_en);
+
+	return 0;
+}
+
 static const struct snd_kcontrol_new rt5677_snd_controls[] = {
 	/* OUTPUT Control */
 	SOC_SINGLE("OUT1 Playback Switch", RT5677_LOUT1,
@@ -567,13 +817,13 @@ static const struct snd_kcontrol_new rt5677_snd_controls[] = {
 
 	/* DAC Digital Volume */
 	SOC_DOUBLE_TLV("DAC1 Playback Volume", RT5677_DAC1_DIG_VOL,
-		RT5677_L_VOL_SFT, RT5677_R_VOL_SFT, 175, 0, dac_vol_tlv),
+		RT5677_L_VOL_SFT, RT5677_R_VOL_SFT, 87, 0, dac_vol_tlv),
 	SOC_DOUBLE_TLV("DAC2 Playback Volume", RT5677_DAC2_DIG_VOL,
-		RT5677_L_VOL_SFT, RT5677_R_VOL_SFT, 175, 0, dac_vol_tlv),
+		RT5677_L_VOL_SFT, RT5677_R_VOL_SFT, 87, 0, dac_vol_tlv),
 	SOC_DOUBLE_TLV("DAC3 Playback Volume", RT5677_DAC3_DIG_VOL,
-		RT5677_L_VOL_SFT, RT5677_R_VOL_SFT, 175, 0, dac_vol_tlv),
+		RT5677_L_VOL_SFT, RT5677_R_VOL_SFT, 87, 0, dac_vol_tlv),
 	SOC_DOUBLE_TLV("DAC4 Playback Volume", RT5677_DAC4_DIG_VOL,
-		RT5677_L_VOL_SFT, RT5677_R_VOL_SFT, 175, 0, dac_vol_tlv),
+		RT5677_L_VOL_SFT, RT5677_R_VOL_SFT, 87, 0, dac_vol_tlv),
 
 	/* IN1/IN2 Control */
 	SOC_SINGLE_TLV("IN1 Boost", RT5677_IN1, RT5677_BST_SFT1, 8, 0, bst_tlv),
@@ -592,19 +842,19 @@ static const struct snd_kcontrol_new rt5677_snd_controls[] = {
 		RT5677_L_MUTE_SFT, RT5677_R_MUTE_SFT, 1, 1),
 
 	SOC_DOUBLE_TLV("ADC1 Capture Volume", RT5677_STO1_ADC_DIG_VOL,
-		RT5677_STO1_ADC_L_VOL_SFT, RT5677_STO1_ADC_R_VOL_SFT, 127, 0,
+		RT5677_STO1_ADC_L_VOL_SFT, RT5677_STO1_ADC_R_VOL_SFT, 63, 0,
 		adc_vol_tlv),
 	SOC_DOUBLE_TLV("ADC2 Capture Volume", RT5677_STO2_ADC_DIG_VOL,
-		RT5677_STO1_ADC_L_VOL_SFT, RT5677_STO1_ADC_R_VOL_SFT, 127, 0,
+		RT5677_STO1_ADC_L_VOL_SFT, RT5677_STO1_ADC_R_VOL_SFT, 63, 0,
 		adc_vol_tlv),
 	SOC_DOUBLE_TLV("ADC3 Capture Volume", RT5677_STO3_ADC_DIG_VOL,
-		RT5677_STO1_ADC_L_VOL_SFT, RT5677_STO1_ADC_R_VOL_SFT, 127, 0,
+		RT5677_STO1_ADC_L_VOL_SFT, RT5677_STO1_ADC_R_VOL_SFT, 63, 0,
 		adc_vol_tlv),
 	SOC_DOUBLE_TLV("ADC4 Capture Volume", RT5677_STO4_ADC_DIG_VOL,
-		RT5677_STO1_ADC_L_VOL_SFT, RT5677_STO1_ADC_R_VOL_SFT, 127, 0,
+		RT5677_STO1_ADC_L_VOL_SFT, RT5677_STO1_ADC_R_VOL_SFT, 63, 0,
 		adc_vol_tlv),
 	SOC_DOUBLE_TLV("Mono ADC Capture Volume", RT5677_MONO_ADC_DIG_VOL,
-		RT5677_MONO_ADC_L_VOL_SFT, RT5677_MONO_ADC_R_VOL_SFT, 127, 0,
+		RT5677_MONO_ADC_L_VOL_SFT, RT5677_MONO_ADC_R_VOL_SFT, 63, 0,
 		adc_vol_tlv),
 
 	/* Sidetone Control */
@@ -627,6 +877,9 @@ static const struct snd_kcontrol_new rt5677_snd_controls[] = {
 	SOC_DOUBLE_TLV("Mono ADC Boost Volume", RT5677_ADC_BST_CTRL2,
 		RT5677_MONO_ADC_L_BST_SFT, RT5677_MONO_ADC_R_BST_SFT, 3, 0,
 		adc_bst_tlv),
+
+	SOC_SINGLE_EXT("DSP VAD Switch", SND_SOC_NOPM, 0, 1, 0,
+		rt5677_dsp_vad_get, rt5677_dsp_vad_put),
 };
 
 /**
@@ -1086,7 +1339,7 @@ static SOC_ENUM_SINGLE_DECL(
 static const struct snd_kcontrol_new rt5677_ib45_bypass_src_mux =
 	SOC_DAPM_ENUM("IB45 Bypass Source", rt5677_ib45_bypass_src_enum);
 
-/* Stereo ADC Source 2 */ /* MX-27 MX26  MX25 [11:10] */
+/* Stereo ADC Source 2 */ /* MX-27 MX26 MX25 [11:10] */
 static const char * const rt5677_stereo_adc2_src[] = {
 	"DD MIX1", "DMIC", "Stereo DAC MIX"
 };
@@ -1171,7 +1424,7 @@ static SOC_ENUM_SINGLE_DECL(
 static const struct snd_kcontrol_new rt5677_sto2_adc_lr_mux =
 	SOC_DAPM_ENUM("Stereo2 ADC LR Source", rt5677_stereo2_adc_lr_enum);
 
-/* Stereo1 ADC Source 1 */ /* MX-27 MX26  MX25 [13:12] */
+/* Stereo1 ADC Source 1 */ /* MX-27 MX26 MX25 [13:12] */
 static const char * const rt5677_stereo_adc1_src[] = {
 	"DD MIX1", "ADC1/2", "Stereo DAC MIX"
 };
@@ -1443,7 +1696,7 @@ static SOC_ENUM_SINGLE_DECL(
 static const struct snd_kcontrol_new rt5677_pdm2_r_mux =
 	SOC_DAPM_ENUM("PDM2 Source", rt5677_pdm2_r_enum);
 
-/* TDM IF1/2 SLB ADC1 Data Selection */ /* MX-3C MX-41 [5:4] MX-08 [1:0]*/
+/* TDM IF1/2 SLB ADC1 Data Selection */ /* MX-3C MX-41 [5:4] MX-08 [1:0] */
 static const char * const rt5677_if12_adc1_src[] = {
 	"STO1 ADC MIX", "OB01", "VAD ADC"
 };
@@ -1521,7 +1774,7 @@ static SOC_ENUM_SINGLE_DECL(
 static const struct snd_kcontrol_new rt5677_slb_adc3_mux =
 	SOC_DAPM_ENUM("SLB ADC3 Source", rt5677_slb_adc3_enum);
 
-/* TDM IF1/2 SLB ADC4 Data Selection */ /* MX-3C MX-41 [11:10]  MX-08 [7:6] */
+/* TDM IF1/2 SLB ADC4 Data Selection */ /* MX-3C MX-41 [11:10] MX-08 [7:6] */
 static const char * const rt5677_if12_adc4_src[] = {
 	"STO4 ADC MIX", "OB67", "OB01"
 };
@@ -1547,7 +1800,7 @@ static SOC_ENUM_SINGLE_DECL(
 static const struct snd_kcontrol_new rt5677_slb_adc4_mux =
 	SOC_DAPM_ENUM("SLB ADC4 Source", rt5677_slb_adc4_enum);
 
-/* Interface3/4 ADC Data Input */ /* MX-2F [3:0] MX-30 [7:4]*/
+/* Interface3/4 ADC Data Input */ /* MX-2F [3:0] MX-30 [7:4] */
 static const char * const rt5677_if34_adc_src[] = {
 	"STO1 ADC MIX", "STO2 ADC MIX", "STO3 ADC MIX", "STO4 ADC MIX",
 	"MONO ADC MIX", "OB01", "OB23", "VAD ADC"
@@ -1567,6 +1820,213 @@ static SOC_ENUM_SINGLE_DECL(
 static const struct snd_kcontrol_new rt5677_if4_adc_mux =
 	SOC_DAPM_ENUM("IF4 ADC Source", rt5677_if4_adc_enum);
 
+/* TDM IF1/2 ADC Data Selection */ /* MX-3B MX-40 [7:6][5:4][3:2][1:0] */
+static const char * const rt5677_if12_adc_swap_src[] = {
+	"L/R", "R/L", "L/L", "R/R"
+};
+
+static SOC_ENUM_SINGLE_DECL(
+	rt5677_if1_adc1_swap_enum, RT5677_TDM1_CTRL1,
+	RT5677_IF1_ADC1_SWAP_SFT, rt5677_if12_adc_swap_src);
+
+static const struct snd_kcontrol_new rt5677_if1_adc1_swap_mux =
+	SOC_DAPM_ENUM("IF1 ADC1 Swap Source", rt5677_if1_adc1_swap_enum);
+
+static SOC_ENUM_SINGLE_DECL(
+	rt5677_if1_adc2_swap_enum, RT5677_TDM1_CTRL1,
+	RT5677_IF1_ADC2_SWAP_SFT, rt5677_if12_adc_swap_src);
+
+static const struct snd_kcontrol_new rt5677_if1_adc2_swap_mux =
+	SOC_DAPM_ENUM("IF1 ADC2 Swap Source", rt5677_if1_adc2_swap_enum);
+
+static SOC_ENUM_SINGLE_DECL(
+	rt5677_if1_adc3_swap_enum, RT5677_TDM1_CTRL1,
+	RT5677_IF1_ADC3_SWAP_SFT, rt5677_if12_adc_swap_src);
+
+static const struct snd_kcontrol_new rt5677_if1_adc3_swap_mux =
+	SOC_DAPM_ENUM("IF1 ADC3 Swap Source", rt5677_if1_adc3_swap_enum);
+
+static SOC_ENUM_SINGLE_DECL(
+	rt5677_if1_adc4_swap_enum, RT5677_TDM1_CTRL1,
+	RT5677_IF1_ADC4_SWAP_SFT, rt5677_if12_adc_swap_src);
+
+static const struct snd_kcontrol_new rt5677_if1_adc4_swap_mux =
+	SOC_DAPM_ENUM("IF1 ADC4 Swap Source", rt5677_if1_adc4_swap_enum);
+
+static SOC_ENUM_SINGLE_DECL(
+	rt5677_if2_adc1_swap_enum, RT5677_TDM2_CTRL1,
+	RT5677_IF1_ADC2_SWAP_SFT, rt5677_if12_adc_swap_src);
+
+static const struct snd_kcontrol_new rt5677_if2_adc1_swap_mux =
+	SOC_DAPM_ENUM("IF1 ADC2 Swap Source", rt5677_if2_adc1_swap_enum);
+
+static SOC_ENUM_SINGLE_DECL(
+	rt5677_if2_adc2_swap_enum, RT5677_TDM2_CTRL1,
+	RT5677_IF2_ADC2_SWAP_SFT, rt5677_if12_adc_swap_src);
+
+static const struct snd_kcontrol_new rt5677_if2_adc2_swap_mux =
+	SOC_DAPM_ENUM("IF2 ADC2 Swap Source", rt5677_if2_adc2_swap_enum);
+
+static SOC_ENUM_SINGLE_DECL(
+	rt5677_if2_adc3_swap_enum, RT5677_TDM2_CTRL1,
+	RT5677_IF2_ADC3_SWAP_SFT, rt5677_if12_adc_swap_src);
+
+static const struct snd_kcontrol_new rt5677_if2_adc3_swap_mux =
+	SOC_DAPM_ENUM("IF2 ADC3 Swap Source", rt5677_if2_adc3_swap_enum);
+
+static SOC_ENUM_SINGLE_DECL(
+	rt5677_if2_adc4_swap_enum, RT5677_TDM2_CTRL1,
+	RT5677_IF2_ADC4_SWAP_SFT, rt5677_if12_adc_swap_src);
+
+static const struct snd_kcontrol_new rt5677_if2_adc4_swap_mux =
+	SOC_DAPM_ENUM("IF2 ADC4 Swap Source", rt5677_if2_adc4_swap_enum);
+
+/* TDM IF1 ADC Data Selection */ /* MX-3C [2:0] */
+static const char * const rt5677_if1_adc_tdm_swap_src[] = {
+	"1/2/3/4", "2/1/3/4", "2/3/1/4", "4/1/2/3", "1/3/2/4", "1/4/2/3",
+	"3/1/2/4", "3/4/1/2"
+};
+
+static SOC_ENUM_SINGLE_DECL(
+	rt5677_if1_adc_tdm_swap_enum, RT5677_TDM1_CTRL2,
+	RT5677_IF1_ADC_CTRL_SFT, rt5677_if1_adc_tdm_swap_src);
+
+static const struct snd_kcontrol_new rt5677_if1_adc_tdm_swap_mux =
+	SOC_DAPM_ENUM("IF1 ADC TDM Swap Source", rt5677_if1_adc_tdm_swap_enum);
+
+/* TDM IF2 ADC Data Selection */ /* MX-41[2:0] */
+static const char * const rt5677_if2_adc_tdm_swap_src[] = {
+	"1/2/3/4", "2/1/3/4", "3/1/2/4", "4/1/2/3", "1/3/2/4", "1/4/2/3",
+	"2/3/1/4", "3/4/1/2"
+};
+
+static SOC_ENUM_SINGLE_DECL(
+	rt5677_if2_adc_tdm_swap_enum, RT5677_TDM2_CTRL2,
+	RT5677_IF2_ADC_CTRL_SFT, rt5677_if2_adc_tdm_swap_src);
+
+static const struct snd_kcontrol_new rt5677_if2_adc_tdm_swap_mux =
+	SOC_DAPM_ENUM("IF2 ADC TDM Swap Source", rt5677_if2_adc_tdm_swap_enum);
+
+/* TDM IF1/2 DAC Data Selection */ /* MX-3E[14:12][10:8][6:4][2:0]
+					MX-3F[14:12][10:8][6:4][2:0]
+					MX-43[14:12][10:8][6:4][2:0]
+					MX-44[14:12][10:8][6:4][2:0] */
+static const char * const rt5677_if12_dac_tdm_sel_src[] = {
+	"Slot0", "Slot1", "Slot2", "Slot3", "Slot4", "Slot5", "Slot6", "Slot7"
+};
+
+static SOC_ENUM_SINGLE_DECL(
+	rt5677_if1_dac0_tdm_sel_enum, RT5677_TDM1_CTRL4,
+	RT5677_IF1_DAC0_SFT, rt5677_if12_dac_tdm_sel_src);
+
+static const struct snd_kcontrol_new rt5677_if1_dac0_tdm_sel_mux =
+	SOC_DAPM_ENUM("IF1 DAC0 TDM Source", rt5677_if1_dac0_tdm_sel_enum);
+
+static SOC_ENUM_SINGLE_DECL(
+	rt5677_if1_dac1_tdm_sel_enum, RT5677_TDM1_CTRL4,
+	RT5677_IF1_DAC1_SFT, rt5677_if12_dac_tdm_sel_src);
+
+static const struct snd_kcontrol_new rt5677_if1_dac1_tdm_sel_mux =
+	SOC_DAPM_ENUM("IF1 DAC1 TDM Source", rt5677_if1_dac1_tdm_sel_enum);
+
+static SOC_ENUM_SINGLE_DECL(
+	rt5677_if1_dac2_tdm_sel_enum, RT5677_TDM1_CTRL4,
+	RT5677_IF1_DAC2_SFT, rt5677_if12_dac_tdm_sel_src);
+
+static const struct snd_kcontrol_new rt5677_if1_dac2_tdm_sel_mux =
+	SOC_DAPM_ENUM("IF1 DAC2 TDM Source", rt5677_if1_dac2_tdm_sel_enum);
+
+static SOC_ENUM_SINGLE_DECL(
+	rt5677_if1_dac3_tdm_sel_enum, RT5677_TDM1_CTRL4,
+	RT5677_IF1_DAC3_SFT, rt5677_if12_dac_tdm_sel_src);
+
+static const struct snd_kcontrol_new rt5677_if1_dac3_tdm_sel_mux =
+	SOC_DAPM_ENUM("IF1 DAC3 TDM Source", rt5677_if1_dac3_tdm_sel_enum);
+
+static SOC_ENUM_SINGLE_DECL(
+	rt5677_if1_dac4_tdm_sel_enum, RT5677_TDM1_CTRL5,
+	RT5677_IF1_DAC4_SFT, rt5677_if12_dac_tdm_sel_src);
+
+static const struct snd_kcontrol_new rt5677_if1_dac4_tdm_sel_mux =
+	SOC_DAPM_ENUM("IF1 DAC4 TDM Source", rt5677_if1_dac4_tdm_sel_enum);
+
+static SOC_ENUM_SINGLE_DECL(
+	rt5677_if1_dac5_tdm_sel_enum, RT5677_TDM1_CTRL5,
+	RT5677_IF1_DAC5_SFT, rt5677_if12_dac_tdm_sel_src);
+
+static const struct snd_kcontrol_new rt5677_if1_dac5_tdm_sel_mux =
+	SOC_DAPM_ENUM("IF1 DAC5 TDM Source", rt5677_if1_dac5_tdm_sel_enum);
+
+static SOC_ENUM_SINGLE_DECL(
+	rt5677_if1_dac6_tdm_sel_enum, RT5677_TDM1_CTRL5,
+	RT5677_IF1_DAC6_SFT, rt5677_if12_dac_tdm_sel_src);
+
+static const struct snd_kcontrol_new rt5677_if1_dac6_tdm_sel_mux =
+	SOC_DAPM_ENUM("IF1 DAC6 TDM Source", rt5677_if1_dac6_tdm_sel_enum);
+
+static SOC_ENUM_SINGLE_DECL(
+	rt5677_if1_dac7_tdm_sel_enum, RT5677_TDM1_CTRL5,
+	RT5677_IF1_DAC7_SFT, rt5677_if12_dac_tdm_sel_src);
+
+static const struct snd_kcontrol_new rt5677_if1_dac7_tdm_sel_mux =
+	SOC_DAPM_ENUM("IF1 DAC7 TDM Source", rt5677_if1_dac7_tdm_sel_enum);
+
+static SOC_ENUM_SINGLE_DECL(
+	rt5677_if2_dac0_tdm_sel_enum, RT5677_TDM2_CTRL4,
+	RT5677_IF2_DAC0_SFT, rt5677_if12_dac_tdm_sel_src);
+
+static const struct snd_kcontrol_new rt5677_if2_dac0_tdm_sel_mux =
+	SOC_DAPM_ENUM("IF2 DAC0 TDM Source", rt5677_if2_dac0_tdm_sel_enum);
+
+static SOC_ENUM_SINGLE_DECL(
+	rt5677_if2_dac1_tdm_sel_enum, RT5677_TDM2_CTRL4,
+	RT5677_IF2_DAC1_SFT, rt5677_if12_dac_tdm_sel_src);
+
+static const struct snd_kcontrol_new rt5677_if2_dac1_tdm_sel_mux =
+	SOC_DAPM_ENUM("IF2 DAC1 TDM Source", rt5677_if2_dac1_tdm_sel_enum);
+
+static SOC_ENUM_SINGLE_DECL(
+	rt5677_if2_dac2_tdm_sel_enum, RT5677_TDM2_CTRL4,
+	RT5677_IF2_DAC2_SFT, rt5677_if12_dac_tdm_sel_src);
+
+static const struct snd_kcontrol_new rt5677_if2_dac2_tdm_sel_mux =
+	SOC_DAPM_ENUM("IF2 DAC2 TDM Source", rt5677_if2_dac2_tdm_sel_enum);
+
+static SOC_ENUM_SINGLE_DECL(
+	rt5677_if2_dac3_tdm_sel_enum, RT5677_TDM2_CTRL4,
+	RT5677_IF2_DAC3_SFT, rt5677_if12_dac_tdm_sel_src);
+
+static const struct snd_kcontrol_new rt5677_if2_dac3_tdm_sel_mux =
+	SOC_DAPM_ENUM("IF2 DAC3 TDM Source", rt5677_if2_dac3_tdm_sel_enum);
+
+static SOC_ENUM_SINGLE_DECL(
+	rt5677_if2_dac4_tdm_sel_enum, RT5677_TDM2_CTRL5,
+	RT5677_IF2_DAC4_SFT, rt5677_if12_dac_tdm_sel_src);
+
+static const struct snd_kcontrol_new rt5677_if2_dac4_tdm_sel_mux =
+	SOC_DAPM_ENUM("IF2 DAC4 TDM Source", rt5677_if2_dac4_tdm_sel_enum);
+
+static SOC_ENUM_SINGLE_DECL(
+	rt5677_if2_dac5_tdm_sel_enum, RT5677_TDM2_CTRL5,
+	RT5677_IF2_DAC5_SFT, rt5677_if12_dac_tdm_sel_src);
+
+static const struct snd_kcontrol_new rt5677_if2_dac5_tdm_sel_mux =
+	SOC_DAPM_ENUM("IF2 DAC5 TDM Source", rt5677_if2_dac5_tdm_sel_enum);
+
+static SOC_ENUM_SINGLE_DECL(
+	rt5677_if2_dac6_tdm_sel_enum, RT5677_TDM2_CTRL5,
+	RT5677_IF2_DAC6_SFT, rt5677_if12_dac_tdm_sel_src);
+
+static const struct snd_kcontrol_new rt5677_if2_dac6_tdm_sel_mux =
+	SOC_DAPM_ENUM("IF2 DAC6 TDM Source", rt5677_if2_dac6_tdm_sel_enum);
+
+static SOC_ENUM_SINGLE_DECL(
+	rt5677_if2_dac7_tdm_sel_enum, RT5677_TDM2_CTRL5,
+	RT5677_IF2_DAC7_SFT, rt5677_if12_dac_tdm_sel_src);
+
+static const struct snd_kcontrol_new rt5677_if2_dac7_tdm_sel_mux =
+	SOC_DAPM_ENUM("IF2 DAC7 TDM Source", rt5677_if2_dac7_tdm_sel_enum);
+
 static int rt5677_bst1_event(struct snd_soc_dapm_widget *w,
 	struct snd_kcontrol *kcontrol, int event)
 {
@@ -1678,6 +2138,77 @@ static int rt5677_set_micbias1_event(struct snd_soc_dapm_widget *w,
 	return 0;
 }
 
+static int rt5677_if1_adc_tdm_event(struct snd_soc_dapm_widget *w,
+	struct snd_kcontrol *kcontrol, int event)
+{
+	struct snd_soc_codec *codec = w->codec;
+	struct rt5677_priv *rt5677 = snd_soc_codec_get_drvdata(codec);
+	unsigned int value;
+
+	switch (event) {
+	case SND_SOC_DAPM_PRE_PMU:
+		regmap_read(rt5677->regmap, RT5677_TDM1_CTRL2, &value);
+		if (value & RT5677_IF1_ADC_CTRL_MASK)
+			regmap_update_bits(rt5677->regmap, RT5677_TDM1_CTRL1,
+				RT5677_IF1_ADC_MODE_MASK,
+				RT5677_IF1_ADC_MODE_TDM);
+		break;
+
+	default:
+		return 0;
+	}
+
+	return 0;
+}
+
+static int rt5677_if2_adc_tdm_event(struct snd_soc_dapm_widget *w,
+	struct snd_kcontrol *kcontrol, int event)
+{
+	struct snd_soc_codec *codec = w->codec;
+	struct rt5677_priv *rt5677 = snd_soc_codec_get_drvdata(codec);
+	unsigned int value;
+
+	switch (event) {
+	case SND_SOC_DAPM_PRE_PMU:
+		regmap_read(rt5677->regmap, RT5677_TDM2_CTRL2, &value);
+		if (value & RT5677_IF2_ADC_CTRL_MASK)
+			regmap_update_bits(rt5677->regmap, RT5677_TDM2_CTRL1,
+				RT5677_IF2_ADC_MODE_MASK,
+				RT5677_IF2_ADC_MODE_TDM);
+		break;
+
+	default:
+		return 0;
+	}
+
+	return 0;
+}
+
+static int rt5677_vref_event(struct snd_soc_dapm_widget *w,
+	struct snd_kcontrol *kcontrol, int event)
+{
+	struct snd_soc_codec *codec = w->codec;
+	struct rt5677_priv *rt5677 = snd_soc_codec_get_drvdata(codec);
+
+	switch (event) {
+	case SND_SOC_DAPM_POST_PMU:
+		if (codec->dapm.bias_level != SND_SOC_BIAS_ON &&
+			!rt5677->is_vref_slow) {
+			mdelay(20);
+			regmap_update_bits(rt5677->regmap, RT5677_PWR_ANLG1,
+				RT5677_PWR_FV1 | RT5677_PWR_FV2,
+				RT5677_PWR_FV1 | RT5677_PWR_FV2);
+			rt5677->is_vref_slow = true;
+		}
+		break;
+
+	default:
+		return 0;
+	}
+
+	return 0;
+}
+
 static const struct snd_soc_dapm_widget rt5677_dapm_widgets[] = {
 	SND_SOC_DAPM_SUPPLY("PLL1", RT5677_PWR_ANLG2, RT5677_PWR_PLL1_BIT,
 		0, rt5677_set_pll1_event, SND_SOC_DAPM_POST_PMU),
@@ -1837,10 +2368,8 @@ static const struct snd_soc_dapm_widget rt5677_dapm_widgets[] = {
 	SND_SOC_DAPM_PGA("Stereo4 ADC MIX", SND_SOC_NOPM, 0, 0, NULL, 0),
 	SND_SOC_DAPM_PGA("Sto2 ADC LR MIX", SND_SOC_NOPM, 0, 0, NULL, 0),
 	SND_SOC_DAPM_PGA("Mono ADC MIX", SND_SOC_NOPM, 0, 0, NULL, 0),
-	SND_SOC_DAPM_PGA("IF1_ADC1", SND_SOC_NOPM, 0, 0, NULL, 0),
-	SND_SOC_DAPM_PGA("IF1_ADC2", SND_SOC_NOPM, 0, 0, NULL, 0),
-	SND_SOC_DAPM_PGA("IF1_ADC3", SND_SOC_NOPM, 0, 0, NULL, 0),
-	SND_SOC_DAPM_PGA("IF1_ADC4", SND_SOC_NOPM, 0, 0, NULL, 0),
+	SND_SOC_DAPM_PGA("IF1 ADC", SND_SOC_NOPM, 0, 0, NULL, 0),
+	SND_SOC_DAPM_PGA("IF2 ADC", SND_SOC_NOPM, 0, 0, NULL, 0),
 
 	/* DSP */
 	SND_SOC_DAPM_MUX("IB9 Mux", SND_SOC_NOPM, 0, 0,
@@ -1963,6 +2492,17 @@ static const struct snd_soc_dapm_widget rt5677_dapm_widgets[] = {
 			&rt5677_if1_adc3_mux),
 	SND_SOC_DAPM_MUX("IF1 ADC4 Mux", SND_SOC_NOPM, 0, 0,
 			&rt5677_if1_adc4_mux),
+	SND_SOC_DAPM_MUX("IF1 ADC1 Swap Mux", SND_SOC_NOPM, 0, 0,
+			&rt5677_if1_adc1_swap_mux),
+	SND_SOC_DAPM_MUX("IF1 ADC2 Swap Mux", SND_SOC_NOPM, 0, 0,
+			&rt5677_if1_adc2_swap_mux),
+	SND_SOC_DAPM_MUX("IF1 ADC3 Swap Mux", SND_SOC_NOPM, 0, 0,
+			&rt5677_if1_adc3_swap_mux),
+	SND_SOC_DAPM_MUX("IF1 ADC4 Swap Mux", SND_SOC_NOPM, 0, 0,
+			&rt5677_if1_adc4_swap_mux),
+	SND_SOC_DAPM_MUX_E("IF1 ADC TDM Swap Mux", SND_SOC_NOPM, 0, 0,
+			&rt5677_if1_adc_tdm_swap_mux, rt5677_if1_adc_tdm_event,
+			SND_SOC_DAPM_PRE_PMU),
 	SND_SOC_DAPM_MUX("IF2 ADC1 Mux", SND_SOC_NOPM, 0, 0,
 			&rt5677_if2_adc1_mux),
 	SND_SOC_DAPM_MUX("IF2 ADC2 Mux", SND_SOC_NOPM, 0, 0,
@@ -1971,6 +2511,17 @@ static const struct snd_soc_dapm_widget rt5677_dapm_widgets[] = {
 			&rt5677_if2_adc3_mux),
 	SND_SOC_DAPM_MUX("IF2 ADC4 Mux", SND_SOC_NOPM, 0, 0,
 			&rt5677_if2_adc4_mux),
+	SND_SOC_DAPM_MUX("IF2 ADC1 Swap Mux", SND_SOC_NOPM, 0, 0,
+			&rt5677_if2_adc1_swap_mux),
+	SND_SOC_DAPM_MUX("IF2 ADC2 Swap Mux", SND_SOC_NOPM, 0, 0,
+			&rt5677_if2_adc2_swap_mux),
+	SND_SOC_DAPM_MUX("IF2 ADC3 Swap Mux", SND_SOC_NOPM, 0, 0,
+			&rt5677_if2_adc3_swap_mux),
+	SND_SOC_DAPM_MUX("IF2 ADC4 Swap Mux", SND_SOC_NOPM, 0, 0,
+			&rt5677_if2_adc4_swap_mux),
+	SND_SOC_DAPM_MUX_E("IF2 ADC TDM Swap Mux", SND_SOC_NOPM, 0, 0,
+			&rt5677_if2_adc_tdm_swap_mux, rt5677_if2_adc_tdm_event,
+			SND_SOC_DAPM_PRE_PMU),
 	SND_SOC_DAPM_MUX("IF3 ADC Mux", SND_SOC_NOPM, 0, 0,
 			&rt5677_if3_adc_mux),
 	SND_SOC_DAPM_MUX("IF4 ADC Mux", SND_SOC_NOPM, 0, 0,
@@ -1984,6 +2535,40 @@ static const struct snd_soc_dapm_widget rt5677_dapm_widgets[] = {
 	SND_SOC_DAPM_MUX("SLB ADC4 Mux", SND_SOC_NOPM, 0, 0,
 			&rt5677_slb_adc4_mux),
 
+	SND_SOC_DAPM_MUX("IF1 DAC0 Mux", SND_SOC_NOPM, 0, 0,
+			&rt5677_if1_dac0_tdm_sel_mux),
+	SND_SOC_DAPM_MUX("IF1 DAC1 Mux", SND_SOC_NOPM, 0, 0,
+			&rt5677_if1_dac1_tdm_sel_mux),
+	SND_SOC_DAPM_MUX("IF1 DAC2 Mux", SND_SOC_NOPM, 0, 0,
+			&rt5677_if1_dac2_tdm_sel_mux),
+	SND_SOC_DAPM_MUX("IF1 DAC3 Mux", SND_SOC_NOPM, 0, 0,
+			&rt5677_if1_dac3_tdm_sel_mux),
+	SND_SOC_DAPM_MUX("IF1 DAC4 Mux", SND_SOC_NOPM, 0, 0,
+			&rt5677_if1_dac4_tdm_sel_mux),
+	SND_SOC_DAPM_MUX("IF1 DAC5 Mux", SND_SOC_NOPM, 0, 0,
+			&rt5677_if1_dac5_tdm_sel_mux),
+	SND_SOC_DAPM_MUX("IF1 DAC6 Mux", SND_SOC_NOPM, 0, 0,
+			&rt5677_if1_dac6_tdm_sel_mux),
+	SND_SOC_DAPM_MUX("IF1 DAC7 Mux", SND_SOC_NOPM, 0, 0,
+			&rt5677_if1_dac7_tdm_sel_mux),
+
+	SND_SOC_DAPM_MUX("IF2 DAC0 Mux", SND_SOC_NOPM, 0, 0,
+			&rt5677_if2_dac0_tdm_sel_mux),
+	SND_SOC_DAPM_MUX("IF2 DAC1 Mux", SND_SOC_NOPM, 0, 0,
+			&rt5677_if2_dac1_tdm_sel_mux),
+	SND_SOC_DAPM_MUX("IF2 DAC2 Mux", SND_SOC_NOPM, 0, 0,
+			&rt5677_if2_dac2_tdm_sel_mux),
+	SND_SOC_DAPM_MUX("IF2 DAC3 Mux", SND_SOC_NOPM, 0, 0,
+			&rt5677_if2_dac3_tdm_sel_mux),
+	SND_SOC_DAPM_MUX("IF2 DAC4 Mux", SND_SOC_NOPM, 0, 0,
+			&rt5677_if2_dac4_tdm_sel_mux),
+	SND_SOC_DAPM_MUX("IF2 DAC5 Mux", SND_SOC_NOPM, 0, 0,
+			&rt5677_if2_dac5_tdm_sel_mux),
+	SND_SOC_DAPM_MUX("IF2 DAC6 Mux", SND_SOC_NOPM, 0, 0,
+			&rt5677_if2_dac6_tdm_sel_mux),
+	SND_SOC_DAPM_MUX("IF2 DAC7 Mux", SND_SOC_NOPM, 0, 0,
+			&rt5677_if2_dac7_tdm_sel_mux),
+
 	/* Audio Interface */
 	SND_SOC_DAPM_AIF_IN("AIF1RX", "AIF1 Playback", 0, SND_SOC_NOPM, 0, 0),
 	SND_SOC_DAPM_AIF_OUT("AIF1TX", "AIF1 Capture", 0, SND_SOC_NOPM, 0, 0),
@@ -2022,7 +2607,7 @@ static const struct snd_soc_dapm_widget rt5677_dapm_widgets[] = {
 		rt5677_ob_7_mix, ARRAY_SIZE(rt5677_ob_7_mix)),
 
 	/* Output Side */
-	/* DAC mixer before sound effect  */
+	/* DAC mixer before sound effect */
 	SND_SOC_DAPM_MIXER("DAC1 MIXL", SND_SOC_NOPM, 0, 0,
 		rt5677_dac_l_mix, ARRAY_SIZE(rt5677_dac_l_mix)),
 	SND_SOC_DAPM_MIXER("DAC1 MIXR", SND_SOC_NOPM, 0, 0,
@@ -2109,13 +2694,20 @@ static const struct snd_soc_dapm_widget rt5677_dapm_widgets[] = {
 	SND_SOC_DAPM_MUX("PDM2 R Mux", RT5677_PDM_OUT_CTRL, RT5677_M_PDM2_R_SFT,
 		1, &rt5677_pdm2_r_mux),
 
-	SND_SOC_DAPM_PGA_S("LOUT1 amp", 1, RT5677_PWR_ANLG1, RT5677_PWR_LO1_BIT,
+	SND_SOC_DAPM_PGA_S("LOUT1 amp", 0, RT5677_PWR_ANLG1, RT5677_PWR_LO1_BIT,
 		0, NULL, 0),
-	SND_SOC_DAPM_PGA_S("LOUT2 amp", 1, RT5677_PWR_ANLG1, RT5677_PWR_LO2_BIT,
+	SND_SOC_DAPM_PGA_S("LOUT2 amp", 0, RT5677_PWR_ANLG1, RT5677_PWR_LO2_BIT,
 		0, NULL, 0),
-	SND_SOC_DAPM_PGA_S("LOUT3 amp", 1, RT5677_PWR_ANLG1, RT5677_PWR_LO3_BIT,
+	SND_SOC_DAPM_PGA_S("LOUT3 amp", 0, RT5677_PWR_ANLG1, RT5677_PWR_LO3_BIT,
 		0, NULL, 0),
 
+	SND_SOC_DAPM_PGA_S("LOUT1 vref", 1, SND_SOC_NOPM, 0, 0,
+		rt5677_vref_event, SND_SOC_DAPM_POST_PMU),
+	SND_SOC_DAPM_PGA_S("LOUT2 vref", 1, SND_SOC_NOPM, 0, 0,
+		rt5677_vref_event, SND_SOC_DAPM_POST_PMU),
+	SND_SOC_DAPM_PGA_S("LOUT3 vref", 1, SND_SOC_NOPM, 0, 0,
+		rt5677_vref_event, SND_SOC_DAPM_POST_PMU),
+
 	/* Output Lines */
 	SND_SOC_DAPM_OUTPUT("LOUT1"),
 	SND_SOC_DAPM_OUTPUT("LOUT2"),
@@ -2124,6 +2716,8 @@ static const struct snd_soc_dapm_widget rt5677_dapm_widgets[] = {
 	SND_SOC_DAPM_OUTPUT("PDM1R"),
 	SND_SOC_DAPM_OUTPUT("PDM2L"),
 	SND_SOC_DAPM_OUTPUT("PDM2R"),
+
+	SND_SOC_DAPM_POST("vref", rt5677_vref_event),
 };
 
 static const struct snd_soc_dapm_route rt5677_dapm_routes[] = {
@@ -2354,11 +2948,42 @@ static const struct snd_soc_dapm_route rt5677_dapm_routes[] = {
 	{ "IF1 ADC4 Mux", "OB67", "OB67" },
 	{ "IF1 ADC4 Mux", "OB01", "OB01 Bypass Mux" },
 
+	{ "IF1 ADC1 Swap Mux", "L/R", "IF1 ADC1 Mux" },
+	{ "IF1 ADC1 Swap Mux", "R/L", "IF1 ADC1 Mux" },
+	{ "IF1 ADC1 Swap Mux", "L/L", "IF1 ADC1 Mux" },
+	{ "IF1 ADC1 Swap Mux", "R/R", "IF1 ADC1 Mux" },
+
+	{ "IF1 ADC2 Swap Mux", "L/R", "IF1 ADC2 Mux" },
+	{ "IF1 ADC2 Swap Mux", "R/L", "IF1 ADC2 Mux" },
+	{ "IF1 ADC2 Swap Mux", "L/L", "IF1 ADC2 Mux" },
+	{ "IF1 ADC2 Swap Mux", "R/R", "IF1 ADC2 Mux" },
+
+	{ "IF1 ADC3 Swap Mux", "L/R", "IF1 ADC3 Mux" },
+	{ "IF1 ADC3 Swap Mux", "R/L", "IF1 ADC3 Mux" },
+	{ "IF1 ADC3 Swap Mux", "L/L", "IF1 ADC3 Mux" },
+	{ "IF1 ADC3 Swap Mux", "R/R", "IF1 ADC3 Mux" },
+
+	{ "IF1 ADC4 Swap Mux", "L/R", "IF1 ADC4 Mux" },
+	{ "IF1 ADC4 Swap Mux", "R/L", "IF1 ADC4 Mux" },
+	{ "IF1 ADC4 Swap Mux", "L/L", "IF1 ADC4 Mux" },
+	{ "IF1 ADC4 Swap Mux", "R/R", "IF1 ADC4 Mux" },
+
+	{ "IF1 ADC", NULL, "IF1 ADC1 Swap Mux" },
+	{ "IF1 ADC", NULL, "IF1 ADC2 Swap Mux" },
+	{ "IF1 ADC", NULL, "IF1 ADC3 Swap Mux" },
+	{ "IF1 ADC", NULL, "IF1 ADC4 Swap Mux" },
+
+	{ "IF1 ADC TDM Swap Mux", "1/2/3/4", "IF1 ADC" },
+	{ "IF1 ADC TDM Swap Mux", "2/1/3/4", "IF1 ADC" },
+	{ "IF1 ADC TDM Swap Mux", "2/3/1/4", "IF1 ADC" },
+	{ "IF1 ADC TDM Swap Mux", "4/1/2/3", "IF1 ADC" },
+	{ "IF1 ADC TDM Swap Mux", "1/3/2/4", "IF1 ADC" },
+	{ "IF1 ADC TDM Swap Mux", "1/4/2/3", "IF1 ADC" },
+	{ "IF1 ADC TDM Swap Mux", "3/1/2/4", "IF1 ADC" },
+	{ "IF1 ADC TDM Swap Mux", "3/4/1/2", "IF1 ADC" },
+
 	{ "AIF1TX", NULL, "I2S1" },
-	{ "AIF1TX", NULL, "IF1 ADC1 Mux" },
-	{ "AIF1TX", NULL, "IF1 ADC2 Mux" },
-	{ "AIF1TX", NULL, "IF1 ADC3 Mux" },
-	{ "AIF1TX", NULL, "IF1 ADC4 Mux" },
+	{ "AIF1TX", NULL, "IF1 ADC TDM Swap Mux" },
 
 	{ "IF2 ADC1 Mux", "STO1 ADC MIX", "Stereo1 ADC MIX" },
 	{ "IF2 ADC1 Mux", "OB01", "OB01 Bypass Mux" },
@@ -2375,11 +3000,42 @@ static const struct snd_soc_dapm_route rt5677_dapm_routes[] = {
 	{ "IF2 ADC4 Mux", "OB67", "OB67" },
 	{ "IF2 ADC4 Mux", "OB01", "OB01 Bypass Mux" },
 
+	{ "IF2 ADC1 Swap Mux", "L/R", "IF2 ADC1 Mux" },
+	{ "IF2 ADC1 Swap Mux", "R/L", "IF2 ADC1 Mux" },
+	{ "IF2 ADC1 Swap Mux", "L/L", "IF2 ADC1 Mux" },
+	{ "IF2 ADC1 Swap Mux", "R/R", "IF2 ADC1 Mux" },
+
+	{ "IF2 ADC2 Swap Mux", "L/R", "IF2 ADC2 Mux" },
+	{ "IF2 ADC2 Swap Mux", "R/L", "IF2 ADC2 Mux" },
+	{ "IF2 ADC2 Swap Mux", "L/L", "IF2 ADC2 Mux" },
+	{ "IF2 ADC2 Swap Mux", "R/R", "IF2 ADC2 Mux" },
+
+	{ "IF2 ADC3 Swap Mux", "L/R", "IF2 ADC3 Mux" },
+	{ "IF2 ADC3 Swap Mux", "R/L", "IF2 ADC3 Mux" },
+	{ "IF2 ADC3 Swap Mux", "L/L", "IF2 ADC3 Mux" },
+	{ "IF2 ADC3 Swap Mux", "R/R", "IF2 ADC3 Mux" },
+
+	{ "IF2 ADC4 Swap Mux", "L/R", "IF2 ADC4 Mux" },
+	{ "IF2 ADC4 Swap Mux", "R/L", "IF2 ADC4 Mux" },
+	{ "IF2 ADC4 Swap Mux", "L/L", "IF2 ADC4 Mux" },
+	{ "IF2 ADC4 Swap Mux", "R/R", "IF2 ADC4 Mux" },
+
+	{ "IF2 ADC", NULL, "IF2 ADC1 Swap Mux" },
+	{ "IF2 ADC", NULL, "IF2 ADC2 Swap Mux" },
+	{ "IF2 ADC", NULL, "IF2 ADC3 Swap Mux" },
+	{ "IF2 ADC", NULL, "IF2 ADC4 Swap Mux" },
+
+	{ "IF2 ADC TDM Swap Mux", "1/2/3/4", "IF2 ADC" },
+	{ "IF2 ADC TDM Swap Mux", "2/1/3/4", "IF2 ADC" },
+	{ "IF2 ADC TDM Swap Mux", "3/1/2/4", "IF2 ADC" },
+	{ "IF2 ADC TDM Swap Mux", "4/1/2/3", "IF2 ADC" },
+	{ "IF2 ADC TDM Swap Mux", "1/3/2/4", "IF2 ADC" },
+	{ "IF2 ADC TDM Swap Mux", "1/4/2/3", "IF2 ADC" },
+	{ "IF2 ADC TDM Swap Mux", "2/3/1/4", "IF2 ADC" },
+	{ "IF2 ADC TDM Swap Mux", "3/4/1/2", "IF2 ADC" },
+
 	{ "AIF2TX", NULL, "I2S2" },
-	{ "AIF2TX", NULL, "IF2 ADC1 Mux" },
-	{ "AIF2TX", NULL, "IF2 ADC2 Mux" },
-	{ "AIF2TX", NULL, "IF2 ADC3 Mux" },
-	{ "AIF2TX", NULL, "IF2 ADC4 Mux" },
+	{ "AIF2TX", NULL, "IF2 ADC TDM Swap Mux" },
 
 	{ "IF3 ADC Mux", "STO1 ADC MIX", "Stereo1 ADC MIX" },
 	{ "IF3 ADC Mux", "STO2 ADC MIX", "Stereo2 ADC MIX" },
@@ -2569,14 +3225,86 @@ static const struct snd_soc_dapm_route rt5677_dapm_routes[] = {
 	{ "IF1 DAC6", NULL, "I2S1" },
 	{ "IF1 DAC7", NULL, "I2S1" },
 
-	{ "IF1 DAC01", NULL, "IF1 DAC0" },
-	{ "IF1 DAC01", NULL, "IF1 DAC1" },
-	{ "IF1 DAC23", NULL, "IF1 DAC2" },
-	{ "IF1 DAC23", NULL, "IF1 DAC3" },
-	{ "IF1 DAC45", NULL, "IF1 DAC4" },
-	{ "IF1 DAC45", NULL, "IF1 DAC5" },
-	{ "IF1 DAC67", NULL, "IF1 DAC6" },
-	{ "IF1 DAC67", NULL, "IF1 DAC7" },
+	{ "IF1 DAC0 Mux", "Slot0", "IF1 DAC0" },
+	{ "IF1 DAC0 Mux", "Slot1", "IF1 DAC1" },
+	{ "IF1 DAC0 Mux", "Slot2", "IF1 DAC2" },
+	{ "IF1 DAC0 Mux", "Slot3", "IF1 DAC3" },
+	{ "IF1 DAC0 Mux", "Slot4", "IF1 DAC4" },
+	{ "IF1 DAC0 Mux", "Slot5", "IF1 DAC5" },
+	{ "IF1 DAC0 Mux", "Slot6", "IF1 DAC6" },
+	{ "IF1 DAC0 Mux", "Slot7", "IF1 DAC7" },
+
+	{ "IF1 DAC1 Mux", "Slot0", "IF1 DAC0" },
+	{ "IF1 DAC1 Mux", "Slot1", "IF1 DAC1" },
+	{ "IF1 DAC1 Mux", "Slot2", "IF1 DAC2" },
+	{ "IF1 DAC1 Mux", "Slot3", "IF1 DAC3" },
+	{ "IF1 DAC1 Mux", "Slot4", "IF1 DAC4" },
+	{ "IF1 DAC1 Mux", "Slot5", "IF1 DAC5" },
+	{ "IF1 DAC1 Mux", "Slot6", "IF1 DAC6" },
+	{ "IF1 DAC1 Mux", "Slot7", "IF1 DAC7" },
+
+	{ "IF1 DAC2 Mux", "Slot0", "IF1 DAC0" },
+	{ "IF1 DAC2 Mux", "Slot1", "IF1 DAC1" },
+	{ "IF1 DAC2 Mux", "Slot2", "IF1 DAC2" },
+	{ "IF1 DAC2 Mux", "Slot3", "IF1 DAC3" },
+	{ "IF1 DAC2 Mux", "Slot4", "IF1 DAC4" },
+	{ "IF1 DAC2 Mux", "Slot5", "IF1 DAC5" },
+	{ "IF1 DAC2 Mux", "Slot6", "IF1 DAC6" },
+	{ "IF1 DAC2 Mux", "Slot7", "IF1 DAC7" },
+
+	{ "IF1 DAC3 Mux", "Slot0", "IF1 DAC0" },
+	{ "IF1 DAC3 Mux", "Slot1", "IF1 DAC1" },
+	{ "IF1 DAC3 Mux", "Slot2", "IF1 DAC2" },
+	{ "IF1 DAC3 Mux", "Slot3", "IF1 DAC3" },
+	{ "IF1 DAC3 Mux", "Slot4", "IF1 DAC4" },
+	{ "IF1 DAC3 Mux", "Slot5", "IF1 DAC5" },
+	{ "IF1 DAC3 Mux", "Slot6", "IF1 DAC6" },
+	{ "IF1 DAC3 Mux", "Slot7", "IF1 DAC7" },
+
+	{ "IF1 DAC4 Mux", "Slot0", "IF1 DAC0" },
+	{ "IF1 DAC4 Mux", "Slot1", "IF1 DAC1" },
+	{ "IF1 DAC4 Mux", "Slot2", "IF1 DAC2" },
+	{ "IF1 DAC4 Mux", "Slot3", "IF1 DAC3" },
+	{ "IF1 DAC4 Mux", "Slot4", "IF1 DAC4" },
+	{ "IF1 DAC4 Mux", "Slot5", "IF1 DAC5" },
+	{ "IF1 DAC4 Mux", "Slot6", "IF1 DAC6" },
+	{ "IF1 DAC4 Mux", "Slot7", "IF1 DAC7" },
+
+	{ "IF1 DAC5 Mux", "Slot0", "IF1 DAC0" },
+	{ "IF1 DAC5 Mux", "Slot1", "IF1 DAC1" },
+	{ "IF1 DAC5 Mux", "Slot2", "IF1 DAC2" },
+	{ "IF1 DAC5 Mux", "Slot3", "IF1 DAC3" },
+	{ "IF1 DAC5 Mux", "Slot4", "IF1 DAC4" },
+	{ "IF1 DAC5 Mux", "Slot5", "IF1 DAC5" },
+	{ "IF1 DAC5 Mux", "Slot6", "IF1 DAC6" },
+	{ "IF1 DAC5 Mux", "Slot7", "IF1 DAC7" },
+
+	{ "IF1 DAC6 Mux", "Slot0", "IF1 DAC0" },
+	{ "IF1 DAC6 Mux", "Slot1", "IF1 DAC1" },
+	{ "IF1 DAC6 Mux", "Slot2", "IF1 DAC2" },
+	{ "IF1 DAC6 Mux", "Slot3", "IF1 DAC3" },
+	{ "IF1 DAC6 Mux", "Slot4", "IF1 DAC4" },
+	{ "IF1 DAC6 Mux", "Slot5", "IF1 DAC5" },
+	{ "IF1 DAC6 Mux", "Slot6", "IF1 DAC6" },
+	{ "IF1 DAC6 Mux", "Slot7", "IF1 DAC7" },
+
+	{ "IF1 DAC7 Mux", "Slot0", "IF1 DAC0" },
+	{ "IF1 DAC7 Mux", "Slot1", "IF1 DAC1" },
+	{ "IF1 DAC7 Mux", "Slot2", "IF1 DAC2" },
+	{ "IF1 DAC7 Mux", "Slot3", "IF1 DAC3" },
+	{ "IF1 DAC7 Mux", "Slot4", "IF1 DAC4" },
+	{ "IF1 DAC7 Mux", "Slot5", "IF1 DAC5" },
+	{ "IF1 DAC7 Mux", "Slot6", "IF1 DAC6" },
+	{ "IF1 DAC7 Mux", "Slot7", "IF1 DAC7" },
+
+	{ "IF1 DAC01", NULL, "IF1 DAC0 Mux" },
+	{ "IF1 DAC01", NULL, "IF1 DAC1 Mux" },
+	{ "IF1 DAC23", NULL, "IF1 DAC2 Mux" },
+	{ "IF1 DAC23", NULL, "IF1 DAC3 Mux" },
+	{ "IF1 DAC45", NULL, "IF1 DAC4 Mux" },
+	{ "IF1 DAC45", NULL, "IF1 DAC5 Mux" },
+	{ "IF1 DAC67", NULL, "IF1 DAC6 Mux" },
+	{ "IF1 DAC67", NULL, "IF1 DAC7 Mux" },
 
 	{ "IF2 DAC0", NULL, "AIF2RX" },
 	{ "IF2 DAC1", NULL, "AIF2RX" },
@@ -2595,14 +3323,86 @@ static const struct snd_soc_dapm_route rt5677_dapm_routes[] = {
 	{ "IF2 DAC6", NULL, "I2S2" },
 	{ "IF2 DAC7", NULL, "I2S2" },
 
-	{ "IF2 DAC01", NULL, "IF2 DAC0" },
-	{ "IF2 DAC01", NULL, "IF2 DAC1" },
-	{ "IF2 DAC23", NULL, "IF2 DAC2" },
-	{ "IF2 DAC23", NULL, "IF2 DAC3" },
-	{ "IF2 DAC45", NULL, "IF2 DAC4" },
-	{ "IF2 DAC45", NULL, "IF2 DAC5" },
-	{ "IF2 DAC67", NULL, "IF2 DAC6" },
-	{ "IF2 DAC67", NULL, "IF2 DAC7" },
+	{ "IF2 DAC0 Mux", "Slot0", "IF2 DAC0" },
+	{ "IF2 DAC0 Mux", "Slot1", "IF2 DAC1" },
+	{ "IF2 DAC0 Mux", "Slot2", "IF2 DAC2" },
+	{ "IF2 DAC0 Mux", "Slot3", "IF2 DAC3" },
+	{ "IF2 DAC0 Mux", "Slot4", "IF2 DAC4" },
+	{ "IF2 DAC0 Mux", "Slot5", "IF2 DAC5" },
+	{ "IF2 DAC0 Mux", "Slot6", "IF2 DAC6" },
+	{ "IF2 DAC0 Mux", "Slot7", "IF2 DAC7" },
+
+	{ "IF2 DAC1 Mux", "Slot0", "IF2 DAC0" },
+	{ "IF2 DAC1 Mux", "Slot1", "IF2 DAC1" },
+	{ "IF2 DAC1 Mux", "Slot2", "IF2 DAC2" },
+	{ "IF2 DAC1 Mux", "Slot3", "IF2 DAC3" },
+	{ "IF2 DAC1 Mux", "Slot4", "IF2 DAC4" },
+	{ "IF2 DAC1 Mux", "Slot5", "IF2 DAC5" },
+	{ "IF2 DAC1 Mux", "Slot6", "IF2 DAC6" },
+	{ "IF2 DAC1 Mux", "Slot7", "IF2 DAC7" },
+
+	{ "IF2 DAC2 Mux", "Slot0", "IF2 DAC0" },
+	{ "IF2 DAC2 Mux", "Slot1", "IF2 DAC1" },
+	{ "IF2 DAC2 Mux", "Slot2", "IF2 DAC2" },
+	{ "IF2 DAC2 Mux", "Slot3", "IF2 DAC3" },
+	{ "IF2 DAC2 Mux", "Slot4", "IF2 DAC4" },
+	{ "IF2 DAC2 Mux", "Slot5", "IF2 DAC5" },
+	{ "IF2 DAC2 Mux", "Slot6", "IF2 DAC6" },
+	{ "IF2 DAC2 Mux", "Slot7", "IF2 DAC7" },
+
+	{ "IF2 DAC3 Mux", "Slot0", "IF2 DAC0" },
+	{ "IF2 DAC3 Mux", "Slot1", "IF2 DAC1" },
+	{ "IF2 DAC3 Mux", "Slot2", "IF2 DAC2" },
+	{ "IF2 DAC3 Mux", "Slot3", "IF2 DAC3" },
+	{ "IF2 DAC3 Mux", "Slot4", "IF2 DAC4" },
+	{ "IF2 DAC3 Mux", "Slot5", "IF2 DAC5" },
+	{ "IF2 DAC3 Mux", "Slot6", "IF2 DAC6" },
+	{ "IF2 DAC3 Mux", "Slot7", "IF2 DAC7" },
+
+	{ "IF2 DAC4 Mux", "Slot0", "IF2 DAC0" },
+	{ "IF2 DAC4 Mux", "Slot1", "IF2 DAC1" },
+	{ "IF2 DAC4 Mux", "Slot2", "IF2 DAC2" },
+	{ "IF2 DAC4 Mux", "Slot3", "IF2 DAC3" },
+	{ "IF2 DAC4 Mux", "Slot4", "IF2 DAC4" },
+	{ "IF2 DAC4 Mux", "Slot5", "IF2 DAC5" },
+	{ "IF2 DAC4 Mux", "Slot6", "IF2 DAC6" },
+	{ "IF2 DAC4 Mux", "Slot7", "IF2 DAC7" },
+
+	{ "IF2 DAC5 Mux", "Slot0", "IF2 DAC0" },
+	{ "IF2 DAC5 Mux", "Slot1", "IF2 DAC1" },
+	{ "IF2 DAC5 Mux", "Slot2", "IF2 DAC2" },
+	{ "IF2 DAC5 Mux", "Slot3", "IF2 DAC3" },
+	{ "IF2 DAC5 Mux", "Slot4", "IF2 DAC4" },
+	{ "IF2 DAC5 Mux", "Slot5", "IF2 DAC5" },
+	{ "IF2 DAC5 Mux", "Slot6", "IF2 DAC6" },
+	{ "IF2 DAC5 Mux", "Slot7", "IF2 DAC7" },
+
+	{ "IF2 DAC6 Mux", "Slot0", "IF2 DAC0" },
+	{ "IF2 DAC6 Mux", "Slot1", "IF2 DAC1" },
+	{ "IF2 DAC6 Mux", "Slot2", "IF2 DAC2" },
+	{ "IF2 DAC6 Mux", "Slot3", "IF2 DAC3" },
+	{ "IF2 DAC6 Mux", "Slot4", "IF2 DAC4" },
+	{ "IF2 DAC6 Mux", "Slot5", "IF2 DAC5" },
+	{ "IF2 DAC6 Mux", "Slot6", "IF2 DAC6" },
+	{ "IF2 DAC6 Mux", "Slot7", "IF2 DAC7" },
+
+	{ "IF2 DAC7 Mux", "Slot0", "IF2 DAC0" },
+	{ "IF2 DAC7 Mux", "Slot1", "IF2 DAC1" },
+	{ "IF2 DAC7 Mux", "Slot2", "IF2 DAC2" },
+	{ "IF2 DAC7 Mux", "Slot3", "IF2 DAC3" },
+	{ "IF2 DAC7 Mux", "Slot4", "IF2 DAC4" },
+	{ "IF2 DAC7 Mux", "Slot5", "IF2 DAC5" },
+	{ "IF2 DAC7 Mux", "Slot6", "IF2 DAC6" },
+	{ "IF2 DAC7 Mux", "Slot7", "IF2 DAC7" },
+
+	{ "IF2 DAC01", NULL, "IF2 DAC0 Mux" },
+	{ "IF2 DAC01", NULL, "IF2 DAC1 Mux" },
+	{ "IF2 DAC23", NULL, "IF2 DAC2 Mux" },
+	{ "IF2 DAC23", NULL, "IF2 DAC3 Mux" },
+	{ "IF2 DAC45", NULL, "IF2 DAC4 Mux" },
+	{ "IF2 DAC45", NULL, "IF2 DAC5 Mux" },
+	{ "IF2 DAC67", NULL, "IF2 DAC6 Mux" },
+	{ "IF2 DAC67", NULL, "IF2 DAC7 Mux" },
 
 	{ "IF3 DAC", NULL, "AIF3RX" },
 	{ "IF3 DAC", NULL, "I2S3" },
@@ -2806,9 +3606,13 @@ static const struct snd_soc_dapm_route rt5677_dapm_routes[] = {
 	{ "LOUT2 amp", NULL, "DAC 2" },
 	{ "LOUT3 amp", NULL, "DAC 3" },
 
-	{ "LOUT1", NULL, "LOUT1 amp" },
-	{ "LOUT2", NULL, "LOUT2 amp" },
-	{ "LOUT3", NULL, "LOUT3 amp" },
+	{ "LOUT1 vref", NULL, "LOUT1 amp" },
+	{ "LOUT2 vref", NULL, "LOUT2 amp" },
+	{ "LOUT3 vref", NULL, "LOUT3 amp" },
+
+	{ "LOUT1", NULL, "LOUT1 vref" },
+	{ "LOUT2", NULL, "LOUT2 vref" },
+	{ "LOUT3", NULL, "LOUT3 vref" },
 
 	{ "PDM1L", NULL, "PDM1 L Mux" },
 	{ "PDM1R", NULL, "PDM1 R Mux" },
@@ -2837,7 +3641,8 @@ static int rt5677_hw_params(struct snd_pcm_substream *substream,
 	rt5677->lrck[dai->id] = params_rate(params);
 	pre_div = rl6231_get_clk_info(rt5677->sysclk, rt5677->lrck[dai->id]);
 	if (pre_div < 0) {
-		dev_err(codec->dev, "Unsupported clock setting\n");
+		dev_err(codec->dev, "Unsupported clock setting: sysclk=%dHz lrck=%dHz\n",
+			rt5677->sysclk, rt5677->lrck[dai->id]);
 		return -EINVAL;
 	}
 	frame_size = snd_soc_params_to_frame_size(params);
@@ -3181,6 +3986,8 @@ static int rt5677_set_bias_level(struct snd_soc_codec *codec,
 
 	case SND_SOC_BIAS_PREPARE:
 		if (codec->dapm.bias_level == SND_SOC_BIAS_STANDBY) {
+			rt5677_set_dsp_vad(codec, false);
+
 			regmap_update_bits(rt5677->regmap, RT5677_PWR_ANLG1,
 				RT5677_LDO1_SEL_MASK | RT5677_LDO2_SEL_MASK,
 				0x0055);
@@ -3188,14 +3995,12 @@ static int rt5677_set_bias_level(struct snd_soc_codec *codec,
 				RT5677_PR_BASE + RT5677_BIAS_CUR4,
 				0x0f00, 0x0f00);
 			regmap_update_bits(rt5677->regmap, RT5677_PWR_ANLG1,
+				RT5677_PWR_FV1 | RT5677_PWR_FV2 |
 				RT5677_PWR_VREF1 | RT5677_PWR_MB |
 				RT5677_PWR_BG | RT5677_PWR_VREF2,
 				RT5677_PWR_VREF1 | RT5677_PWR_MB |
 				RT5677_PWR_BG | RT5677_PWR_VREF2);
-			mdelay(20);
-			regmap_update_bits(rt5677->regmap, RT5677_PWR_ANLG1,
-				RT5677_PWR_FV1 | RT5677_PWR_FV2,
-				RT5677_PWR_FV1 | RT5677_PWR_FV2);
+			rt5677->is_vref_slow = false;
 			regmap_update_bits(rt5677->regmap, RT5677_PWR_ANLG2,
 				RT5677_PWR_CORE, RT5677_PWR_CORE);
 			regmap_update_bits(rt5677->regmap, RT5677_DIG_MISC,
@@ -3214,6 +4019,9 @@ static int rt5677_set_bias_level(struct snd_soc_codec *codec,
 		regmap_write(rt5677->regmap, RT5677_PWR_ANLG2, 0x0000);
 		regmap_update_bits(rt5677->regmap,
 			RT5677_PR_BASE + RT5677_BIAS_CUR4, 0x0f00, 0x0000);
+
+		if (rt5677->dsp_vad_en)
+			rt5677_set_dsp_vad(codec, true);
 		break;
 
 	default:
@@ -3309,6 +4117,78 @@ static int rt5677_gpio_direction_in(struct gpio_chip *chip, unsigned offset)
 	return 0;
 }
 
+/** Configures the gpio as
+ *   0 - floating
+ *   1 - pull down
+ *   2 - pull up
+ */
+static void rt5677_gpio_config(struct rt5677_priv *rt5677, unsigned offset,
+		int value)
+{
+	int shift;
+
+	switch (offset) {
+	case RT5677_GPIO1 ... RT5677_GPIO2:
+		shift = 2 * (1 - offset);
+		regmap_update_bits(rt5677->regmap,
+			RT5677_PR_BASE + RT5677_DIG_IN_PIN_ST_CTRL2,
+			0x3 << shift,
+			(value & 0x3) << shift);
+		break;
+
+	case RT5677_GPIO3 ... RT5677_GPIO6:
+		shift = 2 * (9 - offset);
+		regmap_update_bits(rt5677->regmap,
+			RT5677_PR_BASE + RT5677_DIG_IN_PIN_ST_CTRL3,
+			0x3 << shift,
+			(value & 0x3) << shift);
+		break;
+
+	default:
+		break;
+	}
+}
+
+static int rt5677_to_irq(struct gpio_chip *chip, unsigned offset)
+{
+	struct rt5677_priv *rt5677 = gpio_to_rt5677(chip);
+	struct regmap_irq_chip_data *data = rt5677->irq_data;
+	int irq;
+
+	if (offset >= RT5677_GPIO1 && offset <= RT5677_GPIO3) {
+		if ((rt5677->pdata.jd1_gpio == 1 && offset == RT5677_GPIO1) ||
+			(rt5677->pdata.jd1_gpio == 2 &&
+				offset == RT5677_GPIO2) ||
+			(rt5677->pdata.jd1_gpio == 3 &&
+				offset == RT5677_GPIO3)) {
+			irq = RT5677_IRQ_JD1;
+		} else {
+			return -ENXIO;
+		}
+	}
+
+	if (offset >= RT5677_GPIO4 && offset <= RT5677_GPIO6) {
+		if ((rt5677->pdata.jd2_gpio == 1 && offset == RT5677_GPIO4) ||
+			(rt5677->pdata.jd2_gpio == 2 &&
+				offset == RT5677_GPIO5) ||
+			(rt5677->pdata.jd2_gpio == 3 &&
+				offset == RT5677_GPIO6)) {
+			irq = RT5677_IRQ_JD2;
+		} else if ((rt5677->pdata.jd3_gpio == 1 &&
+				offset == RT5677_GPIO4) ||
+			(rt5677->pdata.jd3_gpio == 2 &&
+				offset == RT5677_GPIO5) ||
+			(rt5677->pdata.jd3_gpio == 3 &&
+				offset == RT5677_GPIO6)) {
+			irq = RT5677_IRQ_JD3;
+		} else {
+			return -ENXIO;
+		}
+	}
+
+	return regmap_irq_get_virq(data, irq);
+}
+
 static struct gpio_chip rt5677_template_chip = {
 	.label			= "rt5677",
 	.owner			= THIS_MODULE,
@@ -3316,6 +4196,7 @@ static struct gpio_chip rt5677_template_chip = {
 	.set			= rt5677_gpio_set,
 	.direction_input	= rt5677_gpio_direction_in,
 	.get			= rt5677_gpio_get,
+	.to_irq			= rt5677_to_irq,
 	.can_sleep		= 1,
 };
 
@@ -3341,6 +4222,11 @@ static void rt5677_free_gpio(struct i2c_client *i2c)
 	gpiochip_remove(&rt5677->gpio_chip);
 }
 #else
+static void rt5677_gpio_config(struct rt5677_priv *rt5677, unsigned offset,
+		int value)
+{
+}
+
 static void rt5677_init_gpio(struct i2c_client *i2c)
 {
 }
@@ -3353,6 +4239,7 @@ static void rt5677_free_gpio(struct i2c_client *i2c)
 static int rt5677_probe(struct snd_soc_codec *codec)
 {
 	struct rt5677_priv *rt5677 = snd_soc_codec_get_drvdata(codec);
+	int i;
 
 	rt5677->codec = codec;
 
@@ -3371,6 +4258,37 @@ static int rt5677_probe(struct snd_soc_codec *codec)
 	regmap_write(rt5677->regmap, RT5677_DIG_MISC, 0x0020);
 	regmap_write(rt5677->regmap, RT5677_PWR_DSP2, 0x0c00);
 
+	for (i = 0; i < RT5677_GPIO_NUM; i++)
+		rt5677_gpio_config(rt5677, i, rt5677->pdata.gpio_config[i]);
+
+	if (rt5677->irq_data) {
+		regmap_update_bits(rt5677->regmap, RT5677_GPIO_CTRL1, 0x8000,
+			0x8000);
+		regmap_update_bits(rt5677->regmap, RT5677_DIG_MISC, 0x0018,
+			0x0008);
+
+		if (rt5677->pdata.jd1_gpio)
+			regmap_update_bits(rt5677->regmap, RT5677_JD_CTRL1,
+				RT5677_SEL_GPIO_JD1_MASK,
+				rt5677->pdata.jd1_gpio <<
+				RT5677_SEL_GPIO_JD1_SFT);
+
+		if (rt5677->pdata.jd2_gpio)
+			regmap_update_bits(rt5677->regmap, RT5677_JD_CTRL1,
+				RT5677_SEL_GPIO_JD2_MASK,
+				rt5677->pdata.jd2_gpio <<
+				RT5677_SEL_GPIO_JD2_SFT);
+
+		if (rt5677->pdata.jd3_gpio)
+			regmap_update_bits(rt5677->regmap, RT5677_JD_CTRL1,
+				RT5677_SEL_GPIO_JD3_MASK,
+				rt5677->pdata.jd3_gpio <<
+				RT5677_SEL_GPIO_JD3_SFT);
+	}
+
+	mutex_init(&rt5677->dsp_cmd_lock);
+	mutex_init(&rt5677->dsp_pri_lock);
+
 	return 0;
 }
 
@@ -3390,8 +4308,11 @@ static int rt5677_suspend(struct snd_soc_codec *codec)
 {
 	struct rt5677_priv *rt5677 = snd_soc_codec_get_drvdata(codec);
 
-	regcache_cache_only(rt5677->regmap, true);
-	regcache_mark_dirty(rt5677->regmap);
+	if (!rt5677->dsp_vad_en) {
+		regcache_cache_only(rt5677->regmap, true);
+		regcache_mark_dirty(rt5677->regmap);
+	}
+
 	if (gpio_is_valid(rt5677->pow_ldo2))
 		gpio_set_value_cansleep(rt5677->pow_ldo2, 0);
 
@@ -3406,8 +4327,11 @@ static int rt5677_resume(struct snd_soc_codec *codec)
 		gpio_set_value_cansleep(rt5677->pow_ldo2, 1);
 		msleep(10);
 	}
-	regcache_cache_only(rt5677->regmap, false);
-	regcache_sync(rt5677->regmap);
+
+	if (!rt5677->dsp_vad_en) {
+		regcache_cache_only(rt5677->regmap, false);
+		regcache_sync(rt5677->regmap);
+	}
 
 	return 0;
 }
@@ -3416,6 +4340,51 @@ static int rt5677_resume(struct snd_soc_codec *codec)
 #define rt5677_resume NULL
 #endif
 
+static int rt5677_read(void *context, unsigned int reg, unsigned int *val)
+{
+	struct i2c_client *client = context;
+	struct rt5677_priv *rt5677 = i2c_get_clientdata(client);
+
+	if (rt5677->is_dsp_mode) {
+		if (reg > 0xff) {
+			mutex_lock(&rt5677->dsp_pri_lock);
+			rt5677_dsp_mode_i2c_write(rt5677, RT5677_PRIV_INDEX,
+				reg & 0xff);
+			rt5677_dsp_mode_i2c_read(rt5677, RT5677_PRIV_DATA, val);
+			mutex_unlock(&rt5677->dsp_pri_lock);
+		} else {
+			rt5677_dsp_mode_i2c_read(rt5677, reg, val);
+		}
+	} else {
+		regmap_read(rt5677->regmap_physical, reg, val);
+	}
+
+	return 0;
+}
+
+static int rt5677_write(void *context, unsigned int reg, unsigned int val)
+{
+	struct i2c_client *client = context;
+	struct rt5677_priv *rt5677 = i2c_get_clientdata(client);
+
+	if (rt5677->is_dsp_mode) {
+		if (reg > 0xff) {
+			mutex_lock(&rt5677->dsp_pri_lock);
+			rt5677_dsp_mode_i2c_write(rt5677, RT5677_PRIV_INDEX,
+				reg & 0xff);
+			rt5677_dsp_mode_i2c_write(rt5677, RT5677_PRIV_DATA,
+				val);
+			mutex_unlock(&rt5677->dsp_pri_lock);
+		} else {
+			rt5677_dsp_mode_i2c_write(rt5677, reg, val);
+		}
+	} else {
+		regmap_write(rt5677->regmap_physical, reg, val);
+	}
+
+	return 0;
+}
+
 #define RT5677_STEREO_RATES SNDRV_PCM_RATE_8000_96000
 #define RT5677_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \
 			SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S8)
@@ -3541,6 +4510,20 @@ static struct snd_soc_codec_driver soc_codec_dev_rt5677 = {
 	.num_dapm_routes = ARRAY_SIZE(rt5677_dapm_routes),
 };
 
+static const struct regmap_config rt5677_regmap_physical = {
+	.name = "physical",
+	.reg_bits = 8,
+	.val_bits = 16,
+
+	.max_register = RT5677_VENDOR_ID2 + 1 + (ARRAY_SIZE(rt5677_ranges) *
+						RT5677_PR_SPACING),
+	.readable_reg = rt5677_readable_register,
+
+	.cache_type = REGCACHE_NONE,
+	.ranges = rt5677_ranges,
+	.num_ranges = ARRAY_SIZE(rt5677_ranges),
+};
+
 static const struct regmap_config rt5677_regmap = {
 	.reg_bits = 8,
 	.val_bits = 16,
@@ -3550,6 +4533,8 @@ static const struct regmap_config rt5677_regmap = {
 
 	.volatile_reg = rt5677_volatile_register,
 	.readable_reg = rt5677_readable_register,
+	.reg_read = rt5677_read,
+	.reg_write = rt5677_write,
 
 	.cache_type = REGCACHE_RBTREE,
 	.reg_defaults = rt5677_reg,
@@ -3590,9 +4575,77 @@ static int rt5677_parse_dt(struct rt5677_priv *rt5677, struct device_node *np)
 			(rt5677->pow_ldo2 != -ENOENT))
 		return rt5677->pow_ldo2;
 
+	of_property_read_u8_array(np, "realtek,gpio-config",
+		rt5677->pdata.gpio_config, RT5677_GPIO_NUM);
+
+	of_property_read_u32(np, "realtek,jd1-gpio", &rt5677->pdata.jd1_gpio);
+	of_property_read_u32(np, "realtek,jd2-gpio", &rt5677->pdata.jd2_gpio);
+	of_property_read_u32(np, "realtek,jd3-gpio", &rt5677->pdata.jd3_gpio);
+
+	return 0;
+}
+
+static struct regmap_irq rt5677_irqs[] = {
+	[RT5677_IRQ_JD1] = {
+		.reg_offset = 0,
+		.mask = RT5677_EN_IRQ_GPIO_JD1,
+	},
+	[RT5677_IRQ_JD2] = {
+		.reg_offset = 0,
+		.mask = RT5677_EN_IRQ_GPIO_JD2,
+	},
+	[RT5677_IRQ_JD3] = {
+		.reg_offset = 0,
+		.mask = RT5677_EN_IRQ_GPIO_JD3,
+	},
+};
+
+static struct regmap_irq_chip rt5677_irq_chip = {
+	.name = "rt5677",
+	.irqs = rt5677_irqs,
+	.num_irqs = ARRAY_SIZE(rt5677_irqs),
+
+	.num_regs = 1,
+	.status_base = RT5677_IRQ_CTRL1,
+	.mask_base = RT5677_IRQ_CTRL1,
+	.mask_invert = 1,
+};
+
+static int rt5677_init_irq(struct i2c_client *i2c)
+{
+	int ret;
+	struct rt5677_priv *rt5677 = i2c_get_clientdata(i2c);
+
+	if (!rt5677->pdata.jd1_gpio &&
+		!rt5677->pdata.jd2_gpio &&
+		!rt5677->pdata.jd3_gpio)
+		return 0;
+
+	if (!i2c->irq) {
+		dev_err(&i2c->dev, "No interrupt specified\n");
+		return -EINVAL;
+	}
+
+	ret = regmap_add_irq_chip(rt5677->regmap, i2c->irq,
+		IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | IRQF_ONESHOT, 0,
+		&rt5677_irq_chip, &rt5677->irq_data);
+
+	if (ret != 0) {
+		dev_err(&i2c->dev, "Failed to register IRQ chip: %d\n", ret);
+		return ret;
+	}
+
 	return 0;
 }
 
+static void rt5677_free_irq(struct i2c_client *i2c)
+{
+	struct rt5677_priv *rt5677 = i2c_get_clientdata(i2c);
+
+	if (rt5677->irq_data)
+		regmap_del_irq_chip(i2c->irq, rt5677->irq_data);
+}
+
 static int rt5677_i2c_probe(struct i2c_client *i2c,
 		    const struct i2c_device_id *id)
 {
@@ -3638,7 +4691,16 @@ static int rt5677_i2c_probe(struct i2c_client *i2c,
 		msleep(10);
 	}
 
-	rt5677->regmap = devm_regmap_init_i2c(i2c, &rt5677_regmap);
+	rt5677->regmap_physical = devm_regmap_init_i2c(i2c,
+					&rt5677_regmap_physical);
+	if (IS_ERR(rt5677->regmap_physical)) {
+		ret = PTR_ERR(rt5677->regmap_physical);
+		dev_err(&i2c->dev, "Failed to allocate register map: %d\n",
+			ret);
+		return ret;
+	}
+
+	rt5677->regmap = devm_regmap_init(&i2c->dev, NULL, i2c, &rt5677_regmap);
 	if (IS_ERR(rt5677->regmap)) {
 		ret = PTR_ERR(rt5677->regmap);
 		dev_err(&i2c->dev, "Failed to allocate register map: %d\n",
@@ -3690,6 +4752,7 @@ static int rt5677_i2c_probe(struct i2c_client *i2c,
 	}
 
 	rt5677_init_gpio(i2c);
+	rt5677_init_irq(i2c);
 
 	return snd_soc_register_codec(&i2c->dev, &soc_codec_dev_rt5677,
 				      rt5677_dai, ARRAY_SIZE(rt5677_dai));
@@ -3698,6 +4761,7 @@ static int rt5677_i2c_probe(struct i2c_client *i2c,
 static int rt5677_i2c_remove(struct i2c_client *i2c)
 {
 	snd_soc_unregister_codec(&i2c->dev);
+	rt5677_free_irq(i2c);
 	rt5677_free_gpio(i2c);
 
 	return 0;
diff --git a/sound/soc/codecs/rt5677.h b/sound/soc/codecs/rt5677.h
index d4eb6d5e6746..c0a625f290cc 100644
--- a/sound/soc/codecs/rt5677.h
+++ b/sound/soc/codecs/rt5677.h
@@ -13,6 +13,7 @@
 #define __RT5677_H__
 
 #include <sound/rt5677.h>
+#include <linux/gpio/driver.h>
 
 /* Info */
 #define RT5677_RESET				0x00
@@ -305,10 +306,10 @@
 #define RT5677_R_MUTE_SFT			7
 #define RT5677_VOL_R_MUTE			(0x1 << 6)
 #define RT5677_VOL_R_SFT			6
-#define RT5677_L_VOL_MASK			(0x3f << 8)
-#define RT5677_L_VOL_SFT			8
-#define RT5677_R_VOL_MASK			(0x3f)
-#define RT5677_R_VOL_SFT			0
+#define RT5677_L_VOL_MASK			(0x7f << 9)
+#define RT5677_L_VOL_SFT			9
+#define RT5677_R_VOL_MASK			(0x7f << 1)
+#define RT5677_R_VOL_SFT			1
 
 /* LOUT1 Control (0x01) */
 #define RT5677_LOUT1_L_MUTE			(0x1 << 15)
@@ -446,16 +447,16 @@
 #define RT5677_SEL_DAC2_R_SRC_SFT		0
 
 /* Stereo1 ADC Digital Volume Control (0x1c) */
-#define RT5677_STO1_ADC_L_VOL_MASK		(0x7f << 8)
-#define RT5677_STO1_ADC_L_VOL_SFT		8
-#define RT5677_STO1_ADC_R_VOL_MASK		(0x7f)
-#define RT5677_STO1_ADC_R_VOL_SFT		0
+#define RT5677_STO1_ADC_L_VOL_MASK		(0x3f << 9)
+#define RT5677_STO1_ADC_L_VOL_SFT		9
+#define RT5677_STO1_ADC_R_VOL_MASK		(0x3f << 1)
+#define RT5677_STO1_ADC_R_VOL_SFT		1
 
 /* Mono ADC Digital Volume Control (0x1d) */
-#define RT5677_MONO_ADC_L_VOL_MASK		(0x7f << 8)
-#define RT5677_MONO_ADC_L_VOL_SFT		8
-#define RT5677_MONO_ADC_R_VOL_MASK		(0x7f)
-#define RT5677_MONO_ADC_R_VOL_SFT		0
+#define RT5677_MONO_ADC_L_VOL_MASK		(0x3f << 9)
+#define RT5677_MONO_ADC_L_VOL_SFT		9
+#define RT5677_MONO_ADC_R_VOL_MASK		(0x3f << 1)
+#define RT5677_MONO_ADC_R_VOL_SFT		1
 
 /* Stereo 1/2 ADC Boost Gain Control (0x1e) */
 #define RT5677_STO1_ADC_L_BST_MASK		(0x3 << 14)
@@ -798,7 +799,21 @@
 #define RT5677_PDM2_I2C_EXE			(0x1 << 1)
 #define RT5677_PDM2_I2C_BUSY			(0x1 << 0)
 
-/* MX3C TDM1 control 1 (0x3c) */
+/* TDM1 control 1 (0x3b) */
+#define RT5677_IF1_ADC_MODE_MASK		(0x1 << 12)
+#define RT5677_IF1_ADC_MODE_SFT			12
+#define RT5677_IF1_ADC_MODE_I2S			(0x0 << 12)
+#define RT5677_IF1_ADC_MODE_TDM			(0x1 << 12)
+#define RT5677_IF1_ADC1_SWAP_MASK		(0x3 << 6)
+#define RT5677_IF1_ADC1_SWAP_SFT		6
+#define RT5677_IF1_ADC2_SWAP_MASK		(0x3 << 4)
+#define RT5677_IF1_ADC2_SWAP_SFT		4
+#define RT5677_IF1_ADC3_SWAP_MASK		(0x3 << 2)
+#define RT5677_IF1_ADC3_SWAP_SFT		2
+#define RT5677_IF1_ADC4_SWAP_MASK		(0x3 << 0)
+#define RT5677_IF1_ADC4_SWAP_SFT		0
+
+/* TDM1 control 2 (0x3c) */
 #define RT5677_IF1_ADC4_MASK			(0x3 << 10)
 #define RT5677_IF1_ADC4_SFT			10
 #define RT5677_IF1_ADC3_MASK			(0x3 << 8)
@@ -807,8 +822,44 @@
 #define RT5677_IF1_ADC2_SFT			6
 #define RT5677_IF1_ADC1_MASK			(0x3 << 4)
 #define RT5677_IF1_ADC1_SFT			4
-
-/* MX41 TDM2 control 1 (0x41) */
+#define RT5677_IF1_ADC_CTRL_MASK		(0x7 << 0)
+#define RT5677_IF1_ADC_CTRL_SFT			0
+
+/* TDM1 control 4 (0x3e) */
+#define RT5677_IF1_DAC0_MASK			(0x7 << 12)
+#define RT5677_IF1_DAC0_SFT			12
+#define RT5677_IF1_DAC1_MASK			(0x7 << 8)
+#define RT5677_IF1_DAC1_SFT			8
+#define RT5677_IF1_DAC2_MASK			(0x7 << 4)
+#define RT5677_IF1_DAC2_SFT			4
+#define RT5677_IF1_DAC3_MASK			(0x7 << 0)
+#define RT5677_IF1_DAC3_SFT			0
+
+/* TDM1 control 5 (0x3f) */
+#define RT5677_IF1_DAC4_MASK			(0x7 << 12)
+#define RT5677_IF1_DAC4_SFT			12
+#define RT5677_IF1_DAC5_MASK			(0x7 << 8)
+#define RT5677_IF1_DAC5_SFT			8
+#define RT5677_IF1_DAC6_MASK			(0x7 << 4)
+#define RT5677_IF1_DAC6_SFT			4
+#define RT5677_IF1_DAC7_MASK			(0x7 << 0)
+#define RT5677_IF1_DAC7_SFT			0
+
+/* TDM2 control 1 (0x40) */
+#define RT5677_IF2_ADC_MODE_MASK		(0x1 << 12)
+#define RT5677_IF2_ADC_MODE_SFT			12
+#define RT5677_IF2_ADC_MODE_I2S			(0x0 << 12)
+#define RT5677_IF2_ADC_MODE_TDM			(0x1 << 12)
+#define RT5677_IF2_ADC1_SWAP_MASK		(0x3 << 6)
+#define RT5677_IF2_ADC1_SWAP_SFT		6
+#define RT5677_IF2_ADC2_SWAP_MASK		(0x3 << 4)
+#define RT5677_IF2_ADC2_SWAP_SFT		4
+#define RT5677_IF2_ADC3_SWAP_MASK		(0x3 << 2)
+#define RT5677_IF2_ADC3_SWAP_SFT		2
+#define RT5677_IF2_ADC4_SWAP_MASK		(0x3 << 0)
+#define RT5677_IF2_ADC4_SWAP_SFT		0
+
+/* TDM2 control 2 (0x41) */
 #define RT5677_IF2_ADC4_MASK			(0x3 << 10)
 #define RT5677_IF2_ADC4_SFT			10
 #define RT5677_IF2_ADC3_MASK			(0x3 << 8)
@@ -817,6 +868,28 @@
 #define RT5677_IF2_ADC2_SFT			6
 #define RT5677_IF2_ADC1_MASK			(0x3 << 4)
 #define RT5677_IF2_ADC1_SFT			4
+#define RT5677_IF2_ADC_CTRL_MASK		(0x7 << 0)
+#define RT5677_IF2_ADC_CTRL_SFT			0
+
+/* TDM2 control 4 (0x43) */
+#define RT5677_IF2_DAC0_MASK			(0x7 << 12)
+#define RT5677_IF2_DAC0_SFT			12
+#define RT5677_IF2_DAC1_MASK			(0x7 << 8)
+#define RT5677_IF2_DAC1_SFT			8
+#define RT5677_IF2_DAC2_MASK			(0x7 << 4)
+#define RT5677_IF2_DAC2_SFT			4
+#define RT5677_IF2_DAC3_MASK			(0x7 << 0)
+#define RT5677_IF2_DAC3_SFT			0
+
+/* TDM2 control 5 (0x44) */
+#define RT5677_IF2_DAC4_MASK			(0x7 << 12)
+#define RT5677_IF2_DAC4_SFT			12
+#define RT5677_IF2_DAC5_MASK			(0x7 << 8)
+#define RT5677_IF2_DAC5_SFT			8
+#define RT5677_IF2_DAC6_MASK			(0x7 << 4)
+#define RT5677_IF2_DAC6_SFT			4
+#define RT5677_IF2_DAC7_MASK			(0x7 << 0)
+#define RT5677_IF2_DAC7_SFT			0
 
 /* Digital Microphone Control 1 (0x50) */
 #define RT5677_DMIC_1_EN_MASK			(0x1 << 15)
@@ -1367,6 +1440,48 @@
 #define RT5677_SEL_SRC_IB01			(0x1 << 0)
 #define RT5677_SEL_SRC_IB01_SFT			0
 
+/* Jack Detect Control 1 (0xb5) */
+#define RT5677_SEL_GPIO_JD1_MASK		(0x3 << 14)
+#define RT5677_SEL_GPIO_JD1_SFT			14
+#define RT5677_SEL_GPIO_JD2_MASK		(0x3 << 12)
+#define RT5677_SEL_GPIO_JD2_SFT			12
+#define RT5677_SEL_GPIO_JD3_MASK		(0x3 << 10)
+#define RT5677_SEL_GPIO_JD3_SFT			10
+
+/* IRQ Control 1 (0xbd) */
+#define RT5677_STA_GPIO_JD1			(0x1 << 15)
+#define RT5677_STA_GPIO_JD1_SFT			15
+#define RT5677_EN_IRQ_GPIO_JD1			(0x1 << 14)
+#define RT5677_EN_IRQ_GPIO_JD1_SFT		14
+#define RT5677_EN_GPIO_JD1_STICKY		(0x1 << 13)
+#define RT5677_EN_GPIO_JD1_STICKY_SFT		13
+#define RT5677_INV_GPIO_JD1			(0x1 << 12)
+#define RT5677_INV_GPIO_JD1_SFT			12
+#define RT5677_STA_GPIO_JD2			(0x1 << 11)
+#define RT5677_STA_GPIO_JD2_SFT			11
+#define RT5677_EN_IRQ_GPIO_JD2			(0x1 << 10)
+#define RT5677_EN_IRQ_GPIO_JD2_SFT		10
+#define RT5677_EN_GPIO_JD2_STICKY		(0x1 << 9)
+#define RT5677_EN_GPIO_JD2_STICKY_SFT		9
+#define RT5677_INV_GPIO_JD2			(0x1 << 8)
+#define RT5677_INV_GPIO_JD2_SFT			8
+#define RT5677_STA_MICBIAS1_OVCD		(0x1 << 7)
+#define RT5677_STA_MICBIAS1_OVCD_SFT		7
+#define RT5677_EN_IRQ_MICBIAS1_OVCD		(0x1 << 6)
+#define RT5677_EN_IRQ_MICBIAS1_OVCD_SFT		6
+#define RT5677_EN_MICBIAS1_OVCD_STICKY		(0x1 << 5)
+#define RT5677_EN_MICBIAS1_OVCD_STICKY_SFT	5
+#define RT5677_INV_MICBIAS1_OVCD		(0x1 << 4)
+#define RT5677_INV_MICBIAS1_OVCD_SFT		4
+#define RT5677_STA_GPIO_JD3			(0x1 << 3)
+#define RT5677_STA_GPIO_JD3_SFT			3
+#define RT5677_EN_IRQ_GPIO_JD3			(0x1 << 2)
+#define RT5677_EN_IRQ_GPIO_JD3_SFT		2
+#define RT5677_EN_GPIO_JD3_STICKY		(0x1 << 1)
+#define RT5677_EN_GPIO_JD3_STICKY_SFT		1
+#define RT5677_INV_GPIO_JD3			(0x1 << 0)
+#define RT5677_INV_GPIO_JD3_SFT			0
+
 /* GPIO status (0xbf) */
 #define RT5677_GPIO6_STATUS_MASK		(0x1 << 5)
 #define RT5677_GPIO6_STATUS_SFT			5
@@ -1506,6 +1621,9 @@
 #define RT5677_GPIO5_FUNC_GPIO			(0x0 << 9)
 #define RT5677_GPIO5_FUNC_DMIC			(0x1 << 9)
 
+#define RT5677_FIRMWARE1	"rt5677_dsp_fw1.bin"
+#define RT5677_FIRMWARE2	"rt5677_dsp_fw2.bin"
+
 /* System Clock Source */
 enum {
 	RT5677_SCLK_S_MCLK,
@@ -1541,10 +1659,18 @@ enum {
 	RT5677_GPIO_NUM,
 };
 
+enum {
+	RT5677_IRQ_JD1,
+	RT5677_IRQ_JD2,
+	RT5677_IRQ_JD3,
+};
+
 struct rt5677_priv {
 	struct snd_soc_codec *codec;
 	struct rt5677_platform_data pdata;
-	struct regmap *regmap;
+	struct regmap *regmap, *regmap_physical;
+	const struct firmware *fw1, *fw2;
+	struct mutex dsp_cmd_lock, dsp_pri_lock;
 
 	int sysclk;
 	int sysclk_src;
@@ -1558,6 +1684,10 @@ struct rt5677_priv {
 #ifdef CONFIG_GPIOLIB
 	struct gpio_chip gpio_chip;
 #endif
+	bool dsp_vad_en;
+	struct regmap_irq_chip_data *irq_data;
+	bool is_dsp_mode;
+	bool is_vref_slow;
 };
 
 #endif /* __RT5677_H__ */
diff --git a/sound/soc/codecs/sgtl5000.c b/sound/soc/codecs/sgtl5000.c
index dab9b15304af..29cf7ce610f4 100644
--- a/sound/soc/codecs/sgtl5000.c
+++ b/sound/soc/codecs/sgtl5000.c
@@ -16,6 +16,7 @@
 #include <linux/pm.h>
 #include <linux/i2c.h>
 #include <linux/clk.h>
+#include <linux/log2.h>
 #include <linux/regmap.h>
 #include <linux/regulator/driver.h>
 #include <linux/regulator/machine.h>
@@ -121,6 +122,13 @@ struct ldo_regulator {
 	bool enabled;
 };
 
+enum sgtl5000_micbias_resistor {
+	SGTL5000_MICBIAS_OFF = 0,
+	SGTL5000_MICBIAS_2K = 2,
+	SGTL5000_MICBIAS_4K = 4,
+	SGTL5000_MICBIAS_8K = 8,
+};
+
 /* sgtl5000 private structure in codec */
 struct sgtl5000_priv {
 	int sysclk;	/* sysclk rate */
@@ -131,6 +139,8 @@ struct sgtl5000_priv {
 	struct regmap *regmap;
 	struct clk *mclk;
 	int revision;
+	u8 micbias_resistor;
+	u8 micbias_voltage;
 };
 
 /*
@@ -145,12 +155,14 @@ struct sgtl5000_priv {
 static int mic_bias_event(struct snd_soc_dapm_widget *w,
 	struct snd_kcontrol *kcontrol, int event)
 {
+	struct sgtl5000_priv *sgtl5000 = snd_soc_codec_get_drvdata(w->codec);
+
 	switch (event) {
 	case SND_SOC_DAPM_POST_PMU:
-		/* change mic bias resistor to 4Kohm */
+		/* change mic bias resistor */
 		snd_soc_update_bits(w->codec, SGTL5000_CHIP_MIC_CTRL,
-				SGTL5000_BIAS_R_MASK,
-				SGTL5000_BIAS_R_4k << SGTL5000_BIAS_R_SHIFT);
+			SGTL5000_BIAS_R_MASK,
+			sgtl5000->micbias_resistor << SGTL5000_BIAS_R_SHIFT);
 		break;
 
 	case SND_SOC_DAPM_PRE_PMD:
@@ -530,16 +542,16 @@ static int sgtl5000_set_dai_sysclk(struct snd_soc_dai *codec_dai,
 
 /*
  * set clock according to i2s frame clock,
- * sgtl5000 provide 2 clock sources.
- * 1. sys_mclk. sample freq can only configure to
+ * sgtl5000 provides 2 clock sources:
+ * 1. sys_mclk: sample freq can only be configured to
  *	1/256, 1/384, 1/512 of sys_mclk.
- * 2. pll. can derive any audio clocks.
+ * 2. pll: can derive any audio clocks.
  *
  * clock setting rules:
- * 1. in slave mode, only sys_mclk can use.
- * 2. as constraint by sys_mclk, sample freq should
- *	set to 32k, 44.1k and above.
- * 3. using sys_mclk prefer to pll to save power.
+ * 1. in slave mode, only sys_mclk can be used
+ * 2. as constraint by sys_mclk, sample freq should be set to 32 kHz, 44.1 kHz
+ * and above.
+ * 3. usage of sys_mclk is preferred over pll to save power.
  */
 static int sgtl5000_set_clock(struct snd_soc_codec *codec, int frame_rate)
 {
@@ -549,8 +561,8 @@ static int sgtl5000_set_clock(struct snd_soc_codec *codec, int frame_rate)
 
 	/*
 	 * sample freq should be divided by frame clock,
-	 * if frame clock lower than 44.1khz, sample feq should set to
-	 * 32khz or 44.1khz.
+	 * if frame clock is lower than 44.1 kHz, sample freq should be set to
+	 * 32 kHz or 44.1 kHz.
 	 */
 	switch (frame_rate) {
 	case 8000:
@@ -603,9 +615,10 @@ static int sgtl5000_set_clock(struct snd_soc_codec *codec, int frame_rate)
 
 	/*
 	 * calculate the divider of mclk/sample_freq,
-	 * factor of freq =96k can only be 256, since mclk in range (12m,27m)
+	 * factor of freq = 96 kHz can only be 256, since mclk is in the range
+	 * of 8 MHz - 27 MHz
 	 */
-	switch (sgtl5000->sysclk / sys_fs) {
+	switch (sgtl5000->sysclk / frame_rate) {
 	case 256:
 		clk_ctl |= SGTL5000_MCLK_FREQ_256FS <<
 			SGTL5000_MCLK_FREQ_SHIFT;
@@ -619,7 +632,7 @@ static int sgtl5000_set_clock(struct snd_soc_codec *codec, int frame_rate)
 			SGTL5000_MCLK_FREQ_SHIFT;
 		break;
 	default:
-		/* if mclk not satisify the divider, use pll */
+		/* if mclk does not satisfy the divider, use pll */
 		if (sgtl5000->master) {
 			clk_ctl |= SGTL5000_MCLK_FREQ_PLL <<
 				SGTL5000_MCLK_FREQ_SHIFT;
@@ -628,7 +641,7 @@ static int sgtl5000_set_clock(struct snd_soc_codec *codec, int frame_rate)
 				"PLL not supported in slave mode\n");
 			dev_err(codec->dev, "%d ratio is not supported. "
 				"SYS_MCLK needs to be 256, 384 or 512 * fs\n",
-				sgtl5000->sysclk / sys_fs);
+				sgtl5000->sysclk / frame_rate);
 			return -EINVAL;
 		}
 	}
@@ -795,7 +808,7 @@ static int ldo_regulator_enable(struct regulator_dev *dev)
 				SGTL5000_LINEREG_D_POWERUP,
 				SGTL5000_LINEREG_D_POWERUP);
 
-	/* when internal ldo enabled, simple digital power can be disabled */
+	/* when internal ldo is enabled, simple digital power can be disabled */
 	snd_soc_update_bits(codec, SGTL5000_CHIP_ANA_POWER,
 				SGTL5000_LINREG_SIMPLE_POWERUP,
 				0);
@@ -1079,7 +1092,7 @@ static bool sgtl5000_readable(struct device *dev, unsigned int reg)
 /*
  * sgtl5000 has 3 internal power supplies:
  * 1. VAG, normally set to vdda/2
- * 2. chargepump, set to different value
+ * 2. charge pump, set to different value
  *	according to voltage of vdda and vddio
  * 3. line out VAG, normally set to vddio/2
  *
@@ -1325,8 +1338,13 @@ static int sgtl5000_probe(struct snd_soc_codec *codec)
 			SGTL5000_HP_ZCD_EN |
 			SGTL5000_ADC_ZCD_EN);
 
-	snd_soc_write(codec, SGTL5000_CHIP_MIC_CTRL, 2);
+	snd_soc_update_bits(codec, SGTL5000_CHIP_MIC_CTRL,
+			SGTL5000_BIAS_R_MASK,
+			sgtl5000->micbias_resistor << SGTL5000_BIAS_R_SHIFT);
 
+	snd_soc_update_bits(codec, SGTL5000_CHIP_MIC_CTRL,
+			SGTL5000_BIAS_R_MASK,
+			sgtl5000->micbias_voltage << SGTL5000_BIAS_R_SHIFT);
 	/*
 	 * disable DAP
 	 * TODO:
@@ -1416,10 +1434,10 @@ static int sgtl5000_i2c_probe(struct i2c_client *client,
 {
 	struct sgtl5000_priv *sgtl5000;
 	int ret, reg, rev;
-	unsigned int mclk;
+	struct device_node *np = client->dev.of_node;
+	u32 value;
 
-	sgtl5000 = devm_kzalloc(&client->dev, sizeof(struct sgtl5000_priv),
-								GFP_KERNEL);
+	sgtl5000 = devm_kzalloc(&client->dev, sizeof(*sgtl5000), GFP_KERNEL);
 	if (!sgtl5000)
 		return -ENOMEM;
 
@@ -1440,14 +1458,6 @@ static int sgtl5000_i2c_probe(struct i2c_client *client,
 		return ret;
 	}
 
-	/* SGTL5000 SYS_MCLK should be between 8 and 27 MHz */
-	mclk = clk_get_rate(sgtl5000->mclk);
-	if (mclk < 8000000 || mclk > 27000000) {
-		dev_err(&client->dev, "Invalid SYS_CLK frequency: %u.%03uMHz\n",
-			mclk / 1000000, mclk / 1000 % 1000);
-		return -EINVAL;
-	}
-
 	ret = clk_prepare_enable(sgtl5000->mclk);
 	if (ret)
 		return ret;
@@ -1469,6 +1479,47 @@ static int sgtl5000_i2c_probe(struct i2c_client *client,
 	dev_info(&client->dev, "sgtl5000 revision 0x%x\n", rev);
 	sgtl5000->revision = rev;
 
+	if (np) {
+		if (!of_property_read_u32(np,
+			"micbias-resistor-k-ohms", &value)) {
+			switch (value) {
+			case SGTL5000_MICBIAS_OFF:
+				sgtl5000->micbias_resistor = 0;
+				break;
+			case SGTL5000_MICBIAS_2K:
+				sgtl5000->micbias_resistor = 1;
+				break;
+			case SGTL5000_MICBIAS_4K:
+				sgtl5000->micbias_resistor = 2;
+				break;
+			case SGTL5000_MICBIAS_8K:
+				sgtl5000->micbias_resistor = 3;
+				break;
+			default:
+				sgtl5000->micbias_resistor = 2;
+				dev_err(&client->dev,
+					"Unsuitable MicBias resistor\n");
+			}
+		} else {
+			/* default is 4Kohms */
+			sgtl5000->micbias_resistor = 2;
+		}
+		if (!of_property_read_u32(np,
+			"micbias-voltage-m-volts", &value)) {
+			/* 1250mV => 0 */
+			/* steps of 250mV */
+			if ((value >= 1250) && (value <= 3000))
+				sgtl5000->micbias_voltage = (value / 250) - 5;
+			else {
+				sgtl5000->micbias_voltage = 0;
+				dev_err(&client->dev,
+					"Unsuitable MicBias resistor\n");
+			}
+		} else {
+			sgtl5000->micbias_voltage = 0;
+		}
+	}
+
 	i2c_set_clientdata(client, sgtl5000);
 
 	/* Ensure sgtl5000 will start with sane register values */
diff --git a/sound/soc/codecs/sigmadsp-i2c.c b/sound/soc/codecs/sigmadsp-i2c.c
index 246081aae8ca..21ca3a5e9f66 100644
--- a/sound/soc/codecs/sigmadsp-i2c.c
+++ b/sound/soc/codecs/sigmadsp-i2c.c
@@ -6,29 +6,88 @@
  * Licensed under the GPL-2 or later.
  */
 
-#include <linux/i2c.h>
 #include <linux/export.h>
+#include <linux/i2c.h>
 #include <linux/module.h>
+#include <linux/slab.h>
+#include <asm/unaligned.h>
 
 #include "sigmadsp.h"
 
-static int sigma_action_write_i2c(void *control_data,
-	const struct sigma_action *sa, size_t len)
+static int sigmadsp_write_i2c(void *control_data,
+	unsigned int addr, const uint8_t data[], size_t len)
+{
+	uint8_t *buf;
+	int ret;
+
+	buf = kzalloc(2 + len, GFP_KERNEL | GFP_DMA);
+	if (!buf)
+		return -ENOMEM;
+
+	put_unaligned_be16(addr, buf);
+	memcpy(buf + 2, data, len);
+
+	ret = i2c_master_send(control_data, buf, len + 2);
+
+	kfree(buf);
+
+	return ret;
+}
+
+static int sigmadsp_read_i2c(void *control_data,
+	unsigned int addr, uint8_t data[], size_t len)
 {
-	return i2c_master_send(control_data, (const unsigned char *)&sa->addr,
-		len);
+	struct i2c_client *client = control_data;
+	struct i2c_msg msgs[2];
+	uint8_t buf[2];
+	int ret;
+
+	put_unaligned_be16(addr, buf);
+
+	msgs[0].addr = client->addr;
+	msgs[0].len = sizeof(buf);
+	msgs[0].buf = buf;
+	msgs[0].flags = 0;
+
+	msgs[1].addr = client->addr;
+	msgs[1].len = len;
+	msgs[1].buf = data;
+	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;
+	return 0;
 }
 
-int process_sigma_firmware(struct i2c_client *client, const char *name)
+/**
+ * devm_sigmadsp_init_i2c() - Initialize SigmaDSP instance
+ * @client: The parent I2C device
+ * @ops: The sigmadsp_ops to use for this instance
+ * @firmware_name: Name of the firmware file to load
+ *
+ * Allocates a SigmaDSP instance and loads the specified firmware file.
+ *
+ * Returns a pointer to a struct sigmadsp on success, or a PTR_ERR() on error.
+ */
+struct sigmadsp *devm_sigmadsp_init_i2c(struct i2c_client *client,
+	const struct sigmadsp_ops *ops,	const char *firmware_name)
 {
-	struct sigma_firmware ssfw;
+	struct sigmadsp *sigmadsp;
+
+	sigmadsp = devm_sigmadsp_init(&client->dev, ops, firmware_name);
+	if (IS_ERR(sigmadsp))
+		return sigmadsp;
 
-	ssfw.control_data = client;
-	ssfw.write = sigma_action_write_i2c;
+	sigmadsp->control_data = client;
+	sigmadsp->write = sigmadsp_write_i2c;
+	sigmadsp->read = sigmadsp_read_i2c;
 
-	return _process_sigma_firmware(&client->dev, &ssfw, name);
+	return sigmadsp;
 }
-EXPORT_SYMBOL(process_sigma_firmware);
+EXPORT_SYMBOL_GPL(devm_sigmadsp_init_i2c);
 
 MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
 MODULE_DESCRIPTION("SigmaDSP I2C firmware loader");
diff --git a/sound/soc/codecs/sigmadsp-regmap.c b/sound/soc/codecs/sigmadsp-regmap.c
index f78ed8d2cfb2..912861be5b87 100644
--- a/sound/soc/codecs/sigmadsp-regmap.c
+++ b/sound/soc/codecs/sigmadsp-regmap.c
@@ -12,24 +12,48 @@
 
 #include "sigmadsp.h"
 
-static int sigma_action_write_regmap(void *control_data,
-	const struct sigma_action *sa, size_t len)
+static int sigmadsp_write_regmap(void *control_data,
+	unsigned int addr, const uint8_t data[], size_t len)
 {
-	return regmap_raw_write(control_data, be16_to_cpu(sa->addr),
-		sa->payload, len - 2);
+	return regmap_raw_write(control_data, addr,
+		data, len);
 }
 
-int process_sigma_firmware_regmap(struct device *dev, struct regmap *regmap,
-	const char *name)
+static int sigmadsp_read_regmap(void *control_data,
+	unsigned int addr, uint8_t data[], size_t len)
 {
-	struct sigma_firmware ssfw;
+	return regmap_raw_read(control_data, addr,
+		data, len);
+}
+
+/**
+ * devm_sigmadsp_init_i2c() - Initialize SigmaDSP instance
+ * @dev: The parent device
+ * @regmap: Regmap instance to use
+ * @ops: The sigmadsp_ops to use for this instance
+ * @firmware_name: Name of the firmware file to load
+ *
+ * Allocates a SigmaDSP instance and loads the specified firmware file.
+ *
+ * Returns a pointer to a struct sigmadsp on success, or a PTR_ERR() on error.
+ */
+struct sigmadsp *devm_sigmadsp_init_regmap(struct device *dev,
+	struct regmap *regmap, const struct sigmadsp_ops *ops,
+	const char *firmware_name)
+{
+	struct sigmadsp *sigmadsp;
+
+	sigmadsp = devm_sigmadsp_init(dev, ops, firmware_name);
+	if (IS_ERR(sigmadsp))
+		return sigmadsp;
 
-	ssfw.control_data = regmap;
-	ssfw.write = sigma_action_write_regmap;
+	sigmadsp->control_data = regmap;
+	sigmadsp->write = sigmadsp_write_regmap;
+	sigmadsp->read = sigmadsp_read_regmap;
 
-	return _process_sigma_firmware(dev, &ssfw, name);
+	return sigmadsp;
 }
-EXPORT_SYMBOL(process_sigma_firmware_regmap);
+EXPORT_SYMBOL_GPL(devm_sigmadsp_init_regmap);
 
 MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
 MODULE_DESCRIPTION("SigmaDSP regmap firmware loader");
diff --git a/sound/soc/codecs/sigmadsp.c b/sound/soc/codecs/sigmadsp.c
index f2de7e049bc6..d53680ac78e4 100644
--- a/sound/soc/codecs/sigmadsp.c
+++ b/sound/soc/codecs/sigmadsp.c
@@ -1,23 +1,74 @@
 /*
  * Load Analog Devices SigmaStudio firmware files
  *
- * Copyright 2009-2011 Analog Devices Inc.
+ * Copyright 2009-2014 Analog Devices Inc.
  *
  * Licensed under the GPL-2 or later.
  */
 
 #include <linux/crc32.h>
-#include <linux/delay.h>
 #include <linux/firmware.h>
 #include <linux/kernel.h>
 #include <linux/i2c.h>
 #include <linux/regmap.h>
 #include <linux/module.h>
+#include <linux/slab.h>
+
+#include <sound/control.h>
+#include <sound/soc.h>
 
 #include "sigmadsp.h"
 
 #define SIGMA_MAGIC "ADISIGM"
 
+#define SIGMA_FW_CHUNK_TYPE_DATA 0
+#define SIGMA_FW_CHUNK_TYPE_CONTROL 1
+#define SIGMA_FW_CHUNK_TYPE_SAMPLERATES 2
+
+struct sigmadsp_control {
+	struct list_head head;
+	uint32_t samplerates;
+	unsigned int addr;
+	unsigned int num_bytes;
+	const char *name;
+	struct snd_kcontrol *kcontrol;
+	bool cached;
+	uint8_t cache[];
+};
+
+struct sigmadsp_data {
+	struct list_head head;
+	uint32_t samplerates;
+	unsigned int addr;
+	unsigned int length;
+	uint8_t data[];
+};
+
+struct sigma_fw_chunk {
+	__le32 length;
+	__le32 tag;
+	__le32 samplerates;
+} __packed;
+
+struct sigma_fw_chunk_data {
+	struct sigma_fw_chunk chunk;
+	__le16 addr;
+	uint8_t data[];
+} __packed;
+
+struct sigma_fw_chunk_control {
+	struct sigma_fw_chunk chunk;
+	__le16 type;
+	__le16 addr;
+	__le16 num_bytes;
+	const char name[];
+} __packed;
+
+struct sigma_fw_chunk_samplerate {
+	struct sigma_fw_chunk chunk;
+	__le32 samplerates[];
+} __packed;
+
 struct sigma_firmware_header {
 	unsigned char magic[7];
 	u8 version;
@@ -28,12 +79,286 @@ enum {
 	SIGMA_ACTION_WRITEXBYTES = 0,
 	SIGMA_ACTION_WRITESINGLE,
 	SIGMA_ACTION_WRITESAFELOAD,
-	SIGMA_ACTION_DELAY,
-	SIGMA_ACTION_PLLWAIT,
-	SIGMA_ACTION_NOOP,
 	SIGMA_ACTION_END,
 };
 
+struct sigma_action {
+	u8 instr;
+	u8 len_hi;
+	__le16 len;
+	__be16 addr;
+	unsigned char payload[];
+} __packed;
+
+static int sigmadsp_write(struct sigmadsp *sigmadsp, unsigned int addr,
+	const uint8_t data[], size_t len)
+{
+	return sigmadsp->write(sigmadsp->control_data, addr, data, len);
+}
+
+static int sigmadsp_read(struct sigmadsp *sigmadsp, unsigned int addr,
+	uint8_t data[], size_t len)
+{
+	return sigmadsp->read(sigmadsp->control_data, addr, data, len);
+}
+
+static int sigmadsp_ctrl_info(struct snd_kcontrol *kcontrol,
+	struct snd_ctl_elem_info *info)
+{
+	struct sigmadsp_control *ctrl = (void *)kcontrol->private_value;
+
+	info->type = SNDRV_CTL_ELEM_TYPE_BYTES;
+	info->count = ctrl->num_bytes;
+
+	return 0;
+}
+
+static int sigmadsp_ctrl_write(struct sigmadsp *sigmadsp,
+	struct sigmadsp_control *ctrl, void *data)
+{
+	/* safeload loads up to 20 bytes in a atomic operation */
+	if (ctrl->num_bytes > 4 && ctrl->num_bytes <= 20 && sigmadsp->ops &&
+	    sigmadsp->ops->safeload)
+		return sigmadsp->ops->safeload(sigmadsp, ctrl->addr, data,
+			ctrl->num_bytes);
+	else
+		return sigmadsp_write(sigmadsp, ctrl->addr, data,
+			ctrl->num_bytes);
+}
+
+static int sigmadsp_ctrl_put(struct snd_kcontrol *kcontrol,
+	struct snd_ctl_elem_value *ucontrol)
+{
+	struct sigmadsp_control *ctrl = (void *)kcontrol->private_value;
+	struct sigmadsp *sigmadsp = snd_kcontrol_chip(kcontrol);
+	uint8_t *data;
+	int ret = 0;
+
+	mutex_lock(&sigmadsp->lock);
+
+	data = ucontrol->value.bytes.data;
+
+	if (!(kcontrol->vd[0].access & SNDRV_CTL_ELEM_ACCESS_INACTIVE))
+		ret = sigmadsp_ctrl_write(sigmadsp, ctrl, data);
+
+	if (ret == 0) {
+		memcpy(ctrl->cache, data, ctrl->num_bytes);
+		ctrl->cached = true;
+	}
+
+	mutex_unlock(&sigmadsp->lock);
+
+	return ret;
+}
+
+static int sigmadsp_ctrl_get(struct snd_kcontrol *kcontrol,
+	struct snd_ctl_elem_value *ucontrol)
+{
+	struct sigmadsp_control *ctrl = (void *)kcontrol->private_value;
+	struct sigmadsp *sigmadsp = snd_kcontrol_chip(kcontrol);
+	int ret = 0;
+
+	mutex_lock(&sigmadsp->lock);
+
+	if (!ctrl->cached) {
+		ret = sigmadsp_read(sigmadsp, ctrl->addr, ctrl->cache,
+			ctrl->num_bytes);
+	}
+
+	if (ret == 0) {
+		ctrl->cached = true;
+		memcpy(ucontrol->value.bytes.data, ctrl->cache,
+			ctrl->num_bytes);
+	}
+
+	mutex_unlock(&sigmadsp->lock);
+
+	return ret;
+}
+
+static void sigmadsp_control_free(struct snd_kcontrol *kcontrol)
+{
+	struct sigmadsp_control *ctrl = (void *)kcontrol->private_value;
+
+	ctrl->kcontrol = NULL;
+}
+
+static bool sigma_fw_validate_control_name(const char *name, unsigned int len)
+{
+	unsigned int i;
+
+	for (i = 0; i < len; i++) {
+		/* Normal ASCII characters are valid */
+		if (name[i] < ' ' || name[i] > '~')
+			return false;
+	}
+
+	return true;
+}
+
+static int sigma_fw_load_control(struct sigmadsp *sigmadsp,
+	const struct sigma_fw_chunk *chunk, unsigned int length)
+{
+	const struct sigma_fw_chunk_control *ctrl_chunk;
+	struct sigmadsp_control *ctrl;
+	unsigned int num_bytes;
+	size_t name_len;
+	char *name;
+	int ret;
+
+	if (length <= sizeof(*ctrl_chunk))
+		return -EINVAL;
+
+	ctrl_chunk = (const struct sigma_fw_chunk_control *)chunk;
+
+	name_len = length - sizeof(*ctrl_chunk);
+	if (name_len >= SNDRV_CTL_ELEM_ID_NAME_MAXLEN)
+		name_len = SNDRV_CTL_ELEM_ID_NAME_MAXLEN - 1;
+
+	/* Make sure there are no non-displayable characaters in the string */
+	if (!sigma_fw_validate_control_name(ctrl_chunk->name, name_len))
+		return -EINVAL;
+
+	num_bytes = le16_to_cpu(ctrl_chunk->num_bytes);
+	ctrl = kzalloc(sizeof(*ctrl) + num_bytes, GFP_KERNEL);
+	if (!ctrl)
+		return -ENOMEM;
+
+	name = kzalloc(name_len + 1, GFP_KERNEL);
+	if (!name) {
+		ret = -ENOMEM;
+		goto err_free_ctrl;
+	}
+	memcpy(name, ctrl_chunk->name, name_len);
+	name[name_len] = '\0';
+	ctrl->name = name;
+
+	ctrl->addr = le16_to_cpu(ctrl_chunk->addr);
+	ctrl->num_bytes = num_bytes;
+	ctrl->samplerates = le32_to_cpu(chunk->samplerates);
+
+	list_add_tail(&ctrl->head, &sigmadsp->ctrl_list);
+
+	return 0;
+
+err_free_ctrl:
+	kfree(ctrl);
+
+	return ret;
+}
+
+static int sigma_fw_load_data(struct sigmadsp *sigmadsp,
+	const struct sigma_fw_chunk *chunk, unsigned int length)
+{
+	const struct sigma_fw_chunk_data *data_chunk;
+	struct sigmadsp_data *data;
+
+	if (length <= sizeof(*data_chunk))
+		return -EINVAL;
+
+	data_chunk = (struct sigma_fw_chunk_data *)chunk;
+
+	length -= sizeof(*data_chunk);
+
+	data = kzalloc(sizeof(*data) + length, GFP_KERNEL);
+	if (!data)
+		return -ENOMEM;
+
+	data->addr = le16_to_cpu(data_chunk->addr);
+	data->length = length;
+	data->samplerates = le32_to_cpu(chunk->samplerates);
+	memcpy(data->data, data_chunk->data, length);
+	list_add_tail(&data->head, &sigmadsp->data_list);
+
+	return 0;
+}
+
+static int sigma_fw_load_samplerates(struct sigmadsp *sigmadsp,
+	const struct sigma_fw_chunk *chunk, unsigned int length)
+{
+	const struct sigma_fw_chunk_samplerate *rate_chunk;
+	unsigned int num_rates;
+	unsigned int *rates;
+	unsigned int i;
+
+	rate_chunk = (const struct sigma_fw_chunk_samplerate *)chunk;
+
+	num_rates = (length - sizeof(*rate_chunk)) / sizeof(__le32);
+
+	if (num_rates > 32 || num_rates == 0)
+		return -EINVAL;
+
+	/* We only allow one samplerates block per file */
+	if (sigmadsp->rate_constraints.count)
+		return -EINVAL;
+
+	rates = kcalloc(num_rates, sizeof(*rates), GFP_KERNEL);
+	if (!rates)
+		return -ENOMEM;
+
+	for (i = 0; i < num_rates; i++)
+		rates[i] = le32_to_cpu(rate_chunk->samplerates[i]);
+
+	sigmadsp->rate_constraints.count = num_rates;
+	sigmadsp->rate_constraints.list = rates;
+
+	return 0;
+}
+
+static int sigmadsp_fw_load_v2(struct sigmadsp *sigmadsp,
+	const struct firmware *fw)
+{
+	struct sigma_fw_chunk *chunk;
+	unsigned int length, pos;
+	int ret;
+
+	/*
+	 * Make sure that there is at least one chunk to avoid integer
+	 * underflows later on. Empty firmware is still valid though.
+	 */
+	if (fw->size < sizeof(*chunk) + sizeof(struct sigma_firmware_header))
+		return 0;
+
+	pos = sizeof(struct sigma_firmware_header);
+
+	while (pos < fw->size - sizeof(*chunk)) {
+		chunk = (struct sigma_fw_chunk *)(fw->data + pos);
+
+		length = le32_to_cpu(chunk->length);
+
+		if (length > fw->size - pos || length < sizeof(*chunk))
+			return -EINVAL;
+
+		switch (le32_to_cpu(chunk->tag)) {
+		case SIGMA_FW_CHUNK_TYPE_DATA:
+			ret = sigma_fw_load_data(sigmadsp, chunk, length);
+			break;
+		case SIGMA_FW_CHUNK_TYPE_CONTROL:
+			ret = sigma_fw_load_control(sigmadsp, chunk, length);
+			break;
+		case SIGMA_FW_CHUNK_TYPE_SAMPLERATES:
+			ret = sigma_fw_load_samplerates(sigmadsp, chunk, length);
+			break;
+		default:
+			dev_warn(sigmadsp->dev, "Unknown chunk type: %d\n",
+				chunk->tag);
+			ret = 0;
+			break;
+		}
+
+		if (ret)
+			return ret;
+
+		/*
+		 * This can not overflow since if length is larger than the
+		 * maximum firmware size (0x4000000) we'll error out earilier.
+		 */
+		pos += ALIGN(length, sizeof(__le32));
+	}
+
+	return 0;
+}
+
 static inline u32 sigma_action_len(struct sigma_action *sa)
 {
 	return (sa->len_hi << 16) | le16_to_cpu(sa->len);
@@ -62,11 +387,11 @@ static size_t sigma_action_size(struct sigma_action *sa)
  * Returns a negative error value in case of an error, 0 if processing of
  * the firmware should be stopped after this action, 1 otherwise.
  */
-static int
-process_sigma_action(struct sigma_firmware *ssfw, struct sigma_action *sa)
+static int process_sigma_action(struct sigmadsp *sigmadsp,
+	struct sigma_action *sa)
 {
 	size_t len = sigma_action_len(sa);
-	int ret;
+	struct sigmadsp_data *data;
 
 	pr_debug("%s: instr:%i addr:%#x len:%zu\n", __func__,
 		sa->instr, sa->addr, len);
@@ -75,13 +400,17 @@ process_sigma_action(struct sigma_firmware *ssfw, struct sigma_action *sa)
 	case SIGMA_ACTION_WRITEXBYTES:
 	case SIGMA_ACTION_WRITESINGLE:
 	case SIGMA_ACTION_WRITESAFELOAD:
-		ret = ssfw->write(ssfw->control_data, sa, len);
-		if (ret < 0)
+		if (len < 3)
 			return -EINVAL;
-		break;
-	case SIGMA_ACTION_DELAY:
-		udelay(len);
-		len = 0;
+
+		data = kzalloc(sizeof(*data) + len - 2, GFP_KERNEL);
+		if (!data)
+			return -ENOMEM;
+
+		data->addr = be16_to_cpu(sa->addr);
+		data->length = len - 2;
+		memcpy(data->data, sa->payload, data->length);
+		list_add_tail(&data->head, &sigmadsp->data_list);
 		break;
 	case SIGMA_ACTION_END:
 		return 0;
@@ -92,22 +421,24 @@ process_sigma_action(struct sigma_firmware *ssfw, struct sigma_action *sa)
 	return 1;
 }
 
-static int
-process_sigma_actions(struct sigma_firmware *ssfw)
+static int sigmadsp_fw_load_v1(struct sigmadsp *sigmadsp,
+	const struct firmware *fw)
 {
 	struct sigma_action *sa;
-	size_t size;
+	size_t size, pos;
 	int ret;
 
-	while (ssfw->pos + sizeof(*sa) <= ssfw->fw->size) {
-		sa = (struct sigma_action *)(ssfw->fw->data + ssfw->pos);
+	pos = sizeof(struct sigma_firmware_header);
+
+	while (pos + sizeof(*sa) <= fw->size) {
+		sa = (struct sigma_action *)(fw->data + pos);
 
 		size = sigma_action_size(sa);
-		ssfw->pos += size;
-		if (ssfw->pos > ssfw->fw->size || size == 0)
+		pos += size;
+		if (pos > fw->size || size == 0)
 			break;
 
-		ret = process_sigma_action(ssfw, sa);
+		ret = process_sigma_action(sigmadsp, sa);
 
 		pr_debug("%s: action returned %i\n", __func__, ret);
 
@@ -115,29 +446,47 @@ process_sigma_actions(struct sigma_firmware *ssfw)
 			return ret;
 	}
 
-	if (ssfw->pos != ssfw->fw->size)
+	if (pos != fw->size)
 		return -EINVAL;
 
 	return 0;
 }
 
-int _process_sigma_firmware(struct device *dev,
-	struct sigma_firmware *ssfw, const char *name)
+static void sigmadsp_firmware_release(struct sigmadsp *sigmadsp)
 {
-	int ret;
-	struct sigma_firmware_header *ssfw_head;
+	struct sigmadsp_control *ctrl, *_ctrl;
+	struct sigmadsp_data *data, *_data;
+
+	list_for_each_entry_safe(ctrl, _ctrl, &sigmadsp->ctrl_list, head) {
+		kfree(ctrl->name);
+		kfree(ctrl);
+	}
+
+	list_for_each_entry_safe(data, _data, &sigmadsp->data_list, head)
+		kfree(data);
+
+	INIT_LIST_HEAD(&sigmadsp->ctrl_list);
+	INIT_LIST_HEAD(&sigmadsp->data_list);
+}
+
+static void devm_sigmadsp_release(struct device *dev, void *res)
+{
+	sigmadsp_firmware_release((struct sigmadsp *)res);
+}
+
+static int sigmadsp_firmware_load(struct sigmadsp *sigmadsp, const char *name)
+{
+	const struct sigma_firmware_header *ssfw_head;
 	const struct firmware *fw;
+	int ret;
 	u32 crc;
 
-	pr_debug("%s: loading firmware %s\n", __func__, name);
-
 	/* first load the blob */
-	ret = request_firmware(&fw, name, dev);
+	ret = request_firmware(&fw, name, sigmadsp->dev);
 	if (ret) {
 		pr_debug("%s: request_firmware() failed with %i\n", __func__, ret);
-		return ret;
+		goto done;
 	}
-	ssfw->fw = fw;
 
 	/* then verify the header */
 	ret = -EINVAL;
@@ -149,13 +498,13 @@ int _process_sigma_firmware(struct device *dev,
 	 * overflows later in the loading process.
 	 */
 	if (fw->size < sizeof(*ssfw_head) || fw->size >= 0x4000000) {
-		dev_err(dev, "Failed to load firmware: Invalid size\n");
+		dev_err(sigmadsp->dev, "Failed to load firmware: Invalid size\n");
 		goto done;
 	}
 
 	ssfw_head = (void *)fw->data;
 	if (memcmp(ssfw_head->magic, SIGMA_MAGIC, ARRAY_SIZE(ssfw_head->magic))) {
-		dev_err(dev, "Failed to load firmware: Invalid magic\n");
+		dev_err(sigmadsp->dev, "Failed to load firmware: Invalid magic\n");
 		goto done;
 	}
 
@@ -163,23 +512,303 @@ int _process_sigma_firmware(struct device *dev,
 			fw->size - sizeof(*ssfw_head));
 	pr_debug("%s: crc=%x\n", __func__, crc);
 	if (crc != le32_to_cpu(ssfw_head->crc)) {
-		dev_err(dev, "Failed to load firmware: Wrong crc checksum: expected %x got %x\n",
+		dev_err(sigmadsp->dev, "Failed to load firmware: Wrong crc checksum: expected %x got %x\n",
 			le32_to_cpu(ssfw_head->crc), crc);
 		goto done;
 	}
 
-	ssfw->pos = sizeof(*ssfw_head);
+	switch (ssfw_head->version) {
+	case 1:
+		ret = sigmadsp_fw_load_v1(sigmadsp, fw);
+		break;
+	case 2:
+		ret = sigmadsp_fw_load_v2(sigmadsp, fw);
+		break;
+	default:
+		dev_err(sigmadsp->dev,
+			"Failed to load firmware: Invalid version %d. Supported firmware versions: 1, 2\n",
+			ssfw_head->version);
+		ret = -EINVAL;
+		break;
+	}
 
-	/* finally process all of the actions */
-	ret = process_sigma_actions(ssfw);
+	if (ret)
+		sigmadsp_firmware_release(sigmadsp);
 
- done:
+done:
 	release_firmware(fw);
 
-	pr_debug("%s: loaded %s\n", __func__, name);
+	return ret;
+}
+
+static int sigmadsp_init(struct sigmadsp *sigmadsp, struct device *dev,
+	const struct sigmadsp_ops *ops, const char *firmware_name)
+{
+	sigmadsp->ops = ops;
+	sigmadsp->dev = dev;
+
+	INIT_LIST_HEAD(&sigmadsp->ctrl_list);
+	INIT_LIST_HEAD(&sigmadsp->data_list);
+	mutex_init(&sigmadsp->lock);
+
+	return sigmadsp_firmware_load(sigmadsp, firmware_name);
+}
+
+/**
+ * devm_sigmadsp_init() - Initialize SigmaDSP instance
+ * @dev: The parent device
+ * @ops: The sigmadsp_ops to use for this instance
+ * @firmware_name: Name of the firmware file to load
+ *
+ * Allocates a SigmaDSP instance and loads the specified firmware file.
+ *
+ * Returns a pointer to a struct sigmadsp on success, or a PTR_ERR() on error.
+ */
+struct sigmadsp *devm_sigmadsp_init(struct device *dev,
+	const struct sigmadsp_ops *ops, const char *firmware_name)
+{
+	struct sigmadsp *sigmadsp;
+	int ret;
+
+	sigmadsp = devres_alloc(devm_sigmadsp_release, sizeof(*sigmadsp),
+		GFP_KERNEL);
+	if (!sigmadsp)
+		return ERR_PTR(-ENOMEM);
+
+	ret = sigmadsp_init(sigmadsp, dev, ops, firmware_name);
+	if (ret) {
+		devres_free(sigmadsp);
+		return ERR_PTR(ret);
+	}
+
+	devres_add(dev, sigmadsp);
+
+	return sigmadsp;
+}
+EXPORT_SYMBOL_GPL(devm_sigmadsp_init);
+
+static int sigmadsp_rate_to_index(struct sigmadsp *sigmadsp, unsigned int rate)
+{
+	unsigned int i;
+
+	for (i = 0; i < sigmadsp->rate_constraints.count; i++) {
+		if (sigmadsp->rate_constraints.list[i] == rate)
+			return i;
+	}
+
+	return -EINVAL;
+}
+
+static unsigned int sigmadsp_get_samplerate_mask(struct sigmadsp *sigmadsp,
+	unsigned int samplerate)
+{
+	int samplerate_index;
+
+	if (samplerate == 0)
+		return 0;
+
+	if (sigmadsp->rate_constraints.count) {
+		samplerate_index = sigmadsp_rate_to_index(sigmadsp, samplerate);
+		if (samplerate_index < 0)
+			return 0;
+
+		return BIT(samplerate_index);
+	} else {
+		return ~0;
+	}
+}
+
+static bool sigmadsp_samplerate_valid(unsigned int supported,
+	unsigned int requested)
+{
+	/* All samplerates are supported */
+	if (!supported)
+		return true;
+
+	return supported & requested;
+}
+
+static int sigmadsp_alloc_control(struct sigmadsp *sigmadsp,
+	struct sigmadsp_control *ctrl, unsigned int samplerate_mask)
+{
+	struct snd_kcontrol_new template;
+	struct snd_kcontrol *kcontrol;
+
+	memset(&template, 0, sizeof(template));
+	template.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+	template.name = ctrl->name;
+	template.info = sigmadsp_ctrl_info;
+	template.get = sigmadsp_ctrl_get;
+	template.put = sigmadsp_ctrl_put;
+	template.private_value = (unsigned long)ctrl;
+	template.access = SNDRV_CTL_ELEM_ACCESS_READWRITE;
+	if (!sigmadsp_samplerate_valid(ctrl->samplerates, samplerate_mask))
+		template.access |= SNDRV_CTL_ELEM_ACCESS_INACTIVE;
+
+	kcontrol = snd_ctl_new1(&template, sigmadsp);
+	if (!kcontrol)
+		return -ENOMEM;
+
+	kcontrol->private_free = sigmadsp_control_free;
+	ctrl->kcontrol = kcontrol;
+
+	return snd_ctl_add(sigmadsp->component->card->snd_card, kcontrol);
+}
+
+static void sigmadsp_activate_ctrl(struct sigmadsp *sigmadsp,
+	struct sigmadsp_control *ctrl, unsigned int samplerate_mask)
+{
+	struct snd_card *card = sigmadsp->component->card->snd_card;
+	struct snd_kcontrol_volatile *vd;
+	struct snd_ctl_elem_id id;
+	bool active;
+	bool changed = false;
+
+	active = sigmadsp_samplerate_valid(ctrl->samplerates, samplerate_mask);
+
+	down_write(&card->controls_rwsem);
+	if (!ctrl->kcontrol) {
+		up_write(&card->controls_rwsem);
+		return;
+	}
+
+	id = ctrl->kcontrol->id;
+	vd = &ctrl->kcontrol->vd[0];
+	if (active == (bool)(vd->access & SNDRV_CTL_ELEM_ACCESS_INACTIVE)) {
+		vd->access ^= SNDRV_CTL_ELEM_ACCESS_INACTIVE;
+		changed = true;
+	}
+	up_write(&card->controls_rwsem);
+
+	if (active && changed) {
+		mutex_lock(&sigmadsp->lock);
+		if (ctrl->cached)
+			sigmadsp_ctrl_write(sigmadsp, ctrl, ctrl->cache);
+		mutex_unlock(&sigmadsp->lock);
+	}
+
+	if (changed)
+		snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_INFO, &id);
+}
+
+/**
+ * sigmadsp_attach() - Attach a sigmadsp instance to a ASoC component
+ * @sigmadsp: The sigmadsp instance to attach
+ * @component: The component to attach to
+ *
+ * Typically called in the components probe callback.
+ *
+ * Note, once this function has been called the firmware must not be released
+ * until after the ALSA snd_card that the component belongs to has been
+ * disconnected, even if sigmadsp_attach() returns an error.
+ */
+int sigmadsp_attach(struct sigmadsp *sigmadsp,
+	struct snd_soc_component *component)
+{
+	struct sigmadsp_control *ctrl;
+	unsigned int samplerate_mask;
+	int ret;
+
+	sigmadsp->component = component;
+
+	samplerate_mask = sigmadsp_get_samplerate_mask(sigmadsp,
+		sigmadsp->current_samplerate);
+
+	list_for_each_entry(ctrl, &sigmadsp->ctrl_list, head) {
+		ret = sigmadsp_alloc_control(sigmadsp, ctrl, samplerate_mask);
+		if (ret)
+			return ret;
+	}
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(sigmadsp_attach);
+
+/**
+ * sigmadsp_setup() - Setup the DSP for the specified samplerate
+ * @sigmadsp: The sigmadsp instance to configure
+ * @samplerate: The samplerate the DSP should be configured for
+ *
+ * Loads the appropriate firmware program and parameter memory (if not already
+ * loaded) and enables the controls for the specified samplerate. Any control
+ * parameter changes that have been made previously will be restored.
+ *
+ * Returns 0 on success, a negative error code otherwise.
+ */
+int sigmadsp_setup(struct sigmadsp *sigmadsp, unsigned int samplerate)
+{
+	struct sigmadsp_control *ctrl;
+	unsigned int samplerate_mask;
+	struct sigmadsp_data *data;
+	int ret;
+
+	if (sigmadsp->current_samplerate == samplerate)
+		return 0;
+
+	samplerate_mask = sigmadsp_get_samplerate_mask(sigmadsp, samplerate);
+	if (samplerate_mask == 0)
+		return -EINVAL;
+
+	list_for_each_entry(data, &sigmadsp->data_list, head) {
+		if (!sigmadsp_samplerate_valid(data->samplerates,
+		    samplerate_mask))
+			continue;
+		ret = sigmadsp_write(sigmadsp, data->addr, data->data,
+			data->length);
+		if (ret)
+			goto err;
+	}
+
+	list_for_each_entry(ctrl, &sigmadsp->ctrl_list, head)
+		sigmadsp_activate_ctrl(sigmadsp, ctrl, samplerate_mask);
+
+	sigmadsp->current_samplerate = samplerate;
+
+	return 0;
+err:
+	sigmadsp_reset(sigmadsp);
 
 	return ret;
 }
-EXPORT_SYMBOL_GPL(_process_sigma_firmware);
+EXPORT_SYMBOL_GPL(sigmadsp_setup);
+
+/**
+ * sigmadsp_reset() - Notify the sigmadsp instance that the DSP has been reset
+ * @sigmadsp: The sigmadsp instance to reset
+ *
+ * Should be called whenever the DSP has been reset and parameter and program
+ * memory need to be re-loaded.
+ */
+void sigmadsp_reset(struct sigmadsp *sigmadsp)
+{
+	struct sigmadsp_control *ctrl;
+
+	list_for_each_entry(ctrl, &sigmadsp->ctrl_list, head)
+		sigmadsp_activate_ctrl(sigmadsp, ctrl, false);
+
+	sigmadsp->current_samplerate = 0;
+}
+EXPORT_SYMBOL_GPL(sigmadsp_reset);
+
+/**
+ * sigmadsp_restrict_params() - Applies DSP firmware specific constraints
+ * @sigmadsp: The sigmadsp instance
+ * @substream: The substream to restrict
+ *
+ * Applies samplerate constraints that may be required by the firmware Should
+ * typically be called from the CODEC/component drivers startup callback.
+ *
+ * Returns 0 on success, a negative error code otherwise.
+ */
+int sigmadsp_restrict_params(struct sigmadsp *sigmadsp,
+	struct snd_pcm_substream *substream)
+{
+	if (sigmadsp->rate_constraints.count == 0)
+		return 0;
+
+	return snd_pcm_hw_constraint_list(substream->runtime, 0,
+		SNDRV_PCM_HW_PARAM_RATE, &sigmadsp->rate_constraints);
+}
+EXPORT_SYMBOL_GPL(sigmadsp_restrict_params);
 
 MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/sigmadsp.h b/sound/soc/codecs/sigmadsp.h
index c47cd23e9827..614475cbb823 100644
--- a/sound/soc/codecs/sigmadsp.h
+++ b/sound/soc/codecs/sigmadsp.h
@@ -11,31 +11,56 @@
 
 #include <linux/device.h>
 #include <linux/regmap.h>
+#include <linux/list.h>
 
-struct sigma_action {
-	u8 instr;
-	u8 len_hi;
-	__le16 len;
-	__be16 addr;
-	unsigned char payload[];
-} __packed;
+#include <sound/pcm.h>
 
-struct sigma_firmware {
-	const struct firmware *fw;
-	size_t pos;
+struct sigmadsp;
+struct snd_soc_component;
+struct snd_pcm_substream;
+
+struct sigmadsp_ops {
+	int (*safeload)(struct sigmadsp *sigmadsp, unsigned int addr,
+			const uint8_t *data, size_t len);
+};
+
+struct sigmadsp {
+	const struct sigmadsp_ops *ops;
+
+	struct list_head ctrl_list;
+	struct list_head data_list;
+
+	struct snd_pcm_hw_constraint_list rate_constraints;
+
+	unsigned int current_samplerate;
+	struct snd_soc_component *component;
+	struct device *dev;
+
+	struct mutex lock;
 
 	void *control_data;
-	int (*write)(void *control_data, const struct sigma_action *sa,
-			size_t len);
+	int (*write)(void *, unsigned int, const uint8_t *, size_t);
+	int (*read)(void *, unsigned int, uint8_t *, size_t);
 };
 
-int _process_sigma_firmware(struct device *dev,
-	struct sigma_firmware *ssfw, const char *name);
+struct sigmadsp *devm_sigmadsp_init(struct device *dev,
+	const struct sigmadsp_ops *ops, const char *firmware_name);
+void sigmadsp_reset(struct sigmadsp *sigmadsp);
+
+int sigmadsp_restrict_params(struct sigmadsp *sigmadsp,
+	struct snd_pcm_substream *substream);
 
 struct i2c_client;
 
-extern int process_sigma_firmware(struct i2c_client *client, const char *name);
-extern int process_sigma_firmware_regmap(struct device *dev,
-		struct regmap *regmap, const char *name);
+struct sigmadsp *devm_sigmadsp_init_regmap(struct device *dev,
+	struct regmap *regmap, const struct sigmadsp_ops *ops,
+	const char *firmware_name);
+struct sigmadsp *devm_sigmadsp_init_i2c(struct i2c_client *client,
+	const struct sigmadsp_ops *ops,	const char *firmware_name);
+
+int sigmadsp_attach(struct sigmadsp *sigmadsp,
+	struct snd_soc_component *component);
+int sigmadsp_setup(struct sigmadsp *sigmadsp, unsigned int rate);
+void sigmadsp_reset(struct sigmadsp *sigmadsp);
 
 #endif
diff --git a/sound/soc/codecs/sirf-audio-codec.c b/sound/soc/codecs/sirf-audio-codec.c
index 06ba4923fd5a..07eea20e6645 100644
--- a/sound/soc/codecs/sirf-audio-codec.c
+++ b/sound/soc/codecs/sirf-audio-codec.c
@@ -120,7 +120,8 @@ static int atlas6_codec_enable_and_reset_event(struct snd_soc_dapm_widget *w,
 {
 #define ATLAS6_CODEC_ENABLE_BITS (1 << 29)
 #define ATLAS6_CODEC_RESET_BITS (1 << 28)
-	struct sirf_audio_codec *sirf_audio_codec = dev_get_drvdata(w->codec->dev);
+	struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+	struct sirf_audio_codec *sirf_audio_codec = snd_soc_codec_get_drvdata(codec);
 	switch (event) {
 	case SND_SOC_DAPM_PRE_PMU:
 		enable_and_reset_codec(sirf_audio_codec->regmap,
@@ -142,7 +143,8 @@ static int prima2_codec_enable_and_reset_event(struct snd_soc_dapm_widget *w,
 {
 #define PRIMA2_CODEC_ENABLE_BITS (1 << 27)
 #define PRIMA2_CODEC_RESET_BITS (1 << 26)
-	struct sirf_audio_codec *sirf_audio_codec = dev_get_drvdata(w->codec->dev);
+	struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+	struct sirf_audio_codec *sirf_audio_codec = snd_soc_codec_get_drvdata(codec);
 	switch (event) {
 	case SND_SOC_DAPM_POST_PMU:
 		enable_and_reset_codec(sirf_audio_codec->regmap,
diff --git a/sound/soc/codecs/sn95031.c b/sound/soc/codecs/sn95031.c
index cf8fa40662f0..31d97cd5e59b 100644
--- a/sound/soc/codecs/sn95031.c
+++ b/sound/soc/codecs/sn95031.c
@@ -867,25 +867,16 @@ static int sn95031_codec_probe(struct snd_soc_codec *codec)
 	snd_soc_write(codec, SN95031_SSR2, 0x10);
 	snd_soc_write(codec, SN95031_SSR3, 0x40);
 
-	snd_soc_add_codec_controls(codec, sn95031_snd_controls,
-			     ARRAY_SIZE(sn95031_snd_controls));
-
-	return 0;
-}
-
-static int sn95031_codec_remove(struct snd_soc_codec *codec)
-{
-	pr_debug("codec_remove called\n");
-	sn95031_set_vaud_bias(codec, SND_SOC_BIAS_OFF);
-
 	return 0;
 }
 
 static struct snd_soc_codec_driver sn95031_codec = {
 	.probe		= sn95031_codec_probe,
-	.remove		= sn95031_codec_remove,
 	.set_bias_level	= sn95031_set_vaud_bias,
 	.idle_bias_off	= true,
+
+	.controls	= sn95031_snd_controls,
+	.num_controls	= ARRAY_SIZE(sn95031_snd_controls),
 	.dapm_widgets	= sn95031_dapm_widgets,
 	.num_dapm_widgets	= ARRAY_SIZE(sn95031_dapm_widgets),
 	.dapm_routes	= sn95031_audio_map,
diff --git a/sound/soc/codecs/ssm4567.c b/sound/soc/codecs/ssm4567.c
index 4b5c17f8507e..a984485108cd 100644
--- a/sound/soc/codecs/ssm4567.c
+++ b/sound/soc/codecs/ssm4567.c
@@ -69,6 +69,22 @@
 #define SSM4567_DAC_FS_64000_96000	0x3
 #define SSM4567_DAC_FS_128000_192000	0x4
 
+/* SAI_CTRL_1 */
+#define SSM4567_SAI_CTRL_1_BCLK			BIT(6)
+#define SSM4567_SAI_CTRL_1_TDM_BLCKS_MASK	(0x3 << 4)
+#define SSM4567_SAI_CTRL_1_TDM_BLCKS_32		(0x0 << 4)
+#define SSM4567_SAI_CTRL_1_TDM_BLCKS_48		(0x1 << 4)
+#define SSM4567_SAI_CTRL_1_TDM_BLCKS_64		(0x2 << 4)
+#define SSM4567_SAI_CTRL_1_FSYNC		BIT(3)
+#define SSM4567_SAI_CTRL_1_LJ			BIT(2)
+#define SSM4567_SAI_CTRL_1_TDM			BIT(1)
+#define SSM4567_SAI_CTRL_1_PDM			BIT(0)
+
+/* SAI_CTRL_2 */
+#define SSM4567_SAI_CTRL_2_AUTO_SLOT		BIT(3)
+#define SSM4567_SAI_CTRL_2_TDM_SLOT_MASK	0x7
+#define SSM4567_SAI_CTRL_2_TDM_SLOT(x)		(x)
+
 struct ssm4567 {
 	struct regmap *regmap;
 };
@@ -145,15 +161,24 @@ static const struct snd_kcontrol_new ssm4567_snd_controls[] = {
 	SOC_SINGLE_TLV("Master Playback Volume", SSM4567_REG_DAC_VOLUME, 0,
 		0xff, 1, ssm4567_vol_tlv),
 	SOC_SINGLE("DAC Low Power Mode Switch", SSM4567_REG_DAC_CTRL, 4, 1, 0),
+	SOC_SINGLE("DAC High Pass Filter Switch", SSM4567_REG_DAC_CTRL,
+		5, 1, 0),
 };
 
+static const struct snd_kcontrol_new ssm4567_amplifier_boost_control =
+	SOC_DAPM_SINGLE("Switch", SSM4567_REG_POWER_CTRL, 1, 1, 1);
+
 static const struct snd_soc_dapm_widget ssm4567_dapm_widgets[] = {
 	SND_SOC_DAPM_DAC("DAC", "HiFi Playback", SSM4567_REG_POWER_CTRL, 2, 1),
+	SND_SOC_DAPM_SWITCH("Amplifier Boost", SSM4567_REG_POWER_CTRL, 3, 1,
+		&ssm4567_amplifier_boost_control),
 
 	SND_SOC_DAPM_OUTPUT("OUT"),
 };
 
 static const struct snd_soc_dapm_route ssm4567_routes[] = {
+	{ "OUT", NULL, "Amplifier Boost" },
+	{ "Amplifier Boost", "Switch", "DAC" },
 	{ "OUT", NULL, "DAC" },
 };
 
@@ -192,6 +217,107 @@ static int ssm4567_mute(struct snd_soc_dai *dai, int mute)
 			SSM4567_DAC_MUTE, val);
 }
 
+static int ssm4567_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask,
+	unsigned int rx_mask, int slots, int width)
+{
+	struct ssm4567 *ssm4567 = snd_soc_dai_get_drvdata(dai);
+	unsigned int blcks;
+	int slot;
+	int ret;
+
+	if (tx_mask == 0)
+		return -EINVAL;
+
+	if (rx_mask && rx_mask != tx_mask)
+		return -EINVAL;
+
+	slot = __ffs(tx_mask);
+	if (tx_mask != BIT(slot))
+		return -EINVAL;
+
+	switch (width) {
+	case 32:
+		blcks = SSM4567_SAI_CTRL_1_TDM_BLCKS_32;
+		break;
+	case 48:
+		blcks = SSM4567_SAI_CTRL_1_TDM_BLCKS_48;
+		break;
+	case 64:
+		blcks = SSM4567_SAI_CTRL_1_TDM_BLCKS_64;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	ret = regmap_update_bits(ssm4567->regmap, SSM4567_REG_SAI_CTRL_2,
+		SSM4567_SAI_CTRL_2_AUTO_SLOT | SSM4567_SAI_CTRL_2_TDM_SLOT_MASK,
+		SSM4567_SAI_CTRL_2_TDM_SLOT(slot));
+	if (ret)
+		return ret;
+
+	return regmap_update_bits(ssm4567->regmap, SSM4567_REG_SAI_CTRL_1,
+		SSM4567_SAI_CTRL_1_TDM_BLCKS_MASK, blcks);
+}
+
+static int ssm4567_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+	struct ssm4567 *ssm4567 = snd_soc_dai_get_drvdata(dai);
+	unsigned int ctrl1 = 0;
+	bool invert_fclk;
+
+	switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+	case SND_SOC_DAIFMT_CBS_CFS:
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+	case SND_SOC_DAIFMT_NB_NF:
+		invert_fclk = false;
+		break;
+	case SND_SOC_DAIFMT_IB_NF:
+		ctrl1 |= SSM4567_SAI_CTRL_1_BCLK;
+		invert_fclk = false;
+		break;
+	case SND_SOC_DAIFMT_NB_IF:
+		ctrl1 |= SSM4567_SAI_CTRL_1_FSYNC;
+		invert_fclk = true;
+		break;
+	case SND_SOC_DAIFMT_IB_IF:
+		ctrl1 |= SSM4567_SAI_CTRL_1_BCLK;
+		invert_fclk = true;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+	case SND_SOC_DAIFMT_I2S:
+		break;
+	case SND_SOC_DAIFMT_LEFT_J:
+		ctrl1 |= SSM4567_SAI_CTRL_1_LJ;
+		invert_fclk = !invert_fclk;
+		break;
+	case SND_SOC_DAIFMT_DSP_A:
+		ctrl1 |= SSM4567_SAI_CTRL_1_TDM;
+		break;
+	case SND_SOC_DAIFMT_DSP_B:
+		ctrl1 |= SSM4567_SAI_CTRL_1_TDM | SSM4567_SAI_CTRL_1_LJ;
+		break;
+	case SND_SOC_DAIFMT_PDM:
+		ctrl1 |= SSM4567_SAI_CTRL_1_PDM;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	if (invert_fclk)
+		ctrl1 |= SSM4567_SAI_CTRL_1_FSYNC;
+
+	return regmap_write(ssm4567->regmap, SSM4567_REG_SAI_CTRL_1, ctrl1);
+}
+
 static int ssm4567_set_power(struct ssm4567 *ssm4567, bool enable)
 {
 	int ret = 0;
@@ -246,6 +372,8 @@ static int ssm4567_set_bias_level(struct snd_soc_codec *codec,
 static const struct snd_soc_dai_ops ssm4567_dai_ops = {
 	.hw_params	= ssm4567_hw_params,
 	.digital_mute	= ssm4567_mute,
+	.set_fmt	= ssm4567_set_dai_fmt,
+	.set_tdm_slot	= ssm4567_set_tdm_slot,
 };
 
 static struct snd_soc_dai_driver ssm4567_dai = {
diff --git a/sound/soc/codecs/sta32x.c b/sound/soc/codecs/sta32x.c
index 48740855566d..7e18200dd6a9 100644
--- a/sound/soc/codecs/sta32x.c
+++ b/sound/soc/codecs/sta32x.c
@@ -833,23 +833,6 @@ static struct snd_soc_dai_driver sta32x_dai = {
 	.ops = &sta32x_dai_ops,
 };
 
-#ifdef CONFIG_PM
-static int sta32x_suspend(struct snd_soc_codec *codec)
-{
-	sta32x_set_bias_level(codec, SND_SOC_BIAS_OFF);
-	return 0;
-}
-
-static int sta32x_resume(struct snd_soc_codec *codec)
-{
-	sta32x_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
-	return 0;
-}
-#else
-#define sta32x_suspend NULL
-#define sta32x_resume NULL
-#endif
-
 static int sta32x_probe(struct snd_soc_codec *codec)
 {
 	struct sta32x_priv *sta32x = snd_soc_codec_get_drvdata(codec);
@@ -936,7 +919,6 @@ static int sta32x_remove(struct snd_soc_codec *codec)
 	struct sta32x_priv *sta32x = snd_soc_codec_get_drvdata(codec);
 
 	sta32x_watchdog_stop(sta32x);
-	sta32x_set_bias_level(codec, SND_SOC_BIAS_OFF);
 	regulator_bulk_disable(ARRAY_SIZE(sta32x->supplies), sta32x->supplies);
 
 	return 0;
@@ -955,9 +937,8 @@ static bool sta32x_reg_is_volatile(struct device *dev, unsigned int reg)
 static const struct snd_soc_codec_driver sta32x_codec = {
 	.probe =		sta32x_probe,
 	.remove =		sta32x_remove,
-	.suspend =		sta32x_suspend,
-	.resume =		sta32x_resume,
 	.set_bias_level =	sta32x_set_bias_level,
+	.suspend_bias_off =	true,
 	.controls =		sta32x_snd_controls,
 	.num_controls =		ARRAY_SIZE(sta32x_snd_controls),
 	.dapm_widgets =		sta32x_dapm_widgets,
diff --git a/sound/soc/codecs/sta350.c b/sound/soc/codecs/sta350.c
index cc97dd52aa9c..bda2ee18769e 100644
--- a/sound/soc/codecs/sta350.c
+++ b/sound/soc/codecs/sta350.c
@@ -912,23 +912,6 @@ static struct snd_soc_dai_driver sta350_dai = {
 	.ops = &sta350_dai_ops,
 };
 
-#ifdef CONFIG_PM
-static int sta350_suspend(struct snd_soc_codec *codec)
-{
-	sta350_set_bias_level(codec, SND_SOC_BIAS_OFF);
-	return 0;
-}
-
-static int sta350_resume(struct snd_soc_codec *codec)
-{
-	sta350_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
-	return 0;
-}
-#else
-#define sta350_suspend NULL
-#define sta350_resume NULL
-#endif
-
 static int sta350_probe(struct snd_soc_codec *codec)
 {
 	struct sta350_priv *sta350 = snd_soc_codec_get_drvdata(codec);
@@ -1065,7 +1048,6 @@ static int sta350_remove(struct snd_soc_codec *codec)
 {
 	struct sta350_priv *sta350 = snd_soc_codec_get_drvdata(codec);
 
-	sta350_set_bias_level(codec, SND_SOC_BIAS_OFF);
 	regulator_bulk_disable(ARRAY_SIZE(sta350->supplies), sta350->supplies);
 
 	return 0;
@@ -1074,9 +1056,8 @@ static int sta350_remove(struct snd_soc_codec *codec)
 static const struct snd_soc_codec_driver sta350_codec = {
 	.probe =		sta350_probe,
 	.remove =		sta350_remove,
-	.suspend =		sta350_suspend,
-	.resume =		sta350_resume,
 	.set_bias_level =	sta350_set_bias_level,
+	.suspend_bias_off =	true,
 	.controls =		sta350_snd_controls,
 	.num_controls =		ARRAY_SIZE(sta350_snd_controls),
 	.dapm_widgets =		sta350_dapm_widgets,
diff --git a/sound/soc/codecs/sta529.c b/sound/soc/codecs/sta529.c
index 89c748dd3d6e..b0f436d10125 100644
--- a/sound/soc/codecs/sta529.c
+++ b/sound/soc/codecs/sta529.c
@@ -319,41 +319,10 @@ static struct snd_soc_dai_driver sta529_dai = {
 	.ops	= &sta529_dai_ops,
 };
 
-static int sta529_probe(struct snd_soc_codec *codec)
-{
-	sta529_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
-
-	return 0;
-}
-
-/* power down chip */
-static int sta529_remove(struct snd_soc_codec *codec)
-{
-	sta529_set_bias_level(codec, SND_SOC_BIAS_OFF);
-
-	return 0;
-}
-
-static int sta529_suspend(struct snd_soc_codec *codec)
-{
-	sta529_set_bias_level(codec, SND_SOC_BIAS_OFF);
-
-	return 0;
-}
-
-static int sta529_resume(struct snd_soc_codec *codec)
-{
-	sta529_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
-
-	return 0;
-}
-
 static const struct snd_soc_codec_driver sta529_codec_driver = {
-	.probe = sta529_probe,
-	.remove = sta529_remove,
 	.set_bias_level = sta529_set_bias_level,
-	.suspend = sta529_suspend,
-	.resume = sta529_resume,
+	.suspend_bias_off = true,
+
 	.controls = sta529_snd_controls,
 	.num_controls = ARRAY_SIZE(sta529_snd_controls),
 };
diff --git a/sound/soc/codecs/stac9766.c b/sound/soc/codecs/stac9766.c
index 53b810d23fea..dbff0c89be48 100644
--- a/sound/soc/codecs/stac9766.c
+++ b/sound/soc/codecs/stac9766.c
@@ -139,18 +139,19 @@ static const struct snd_kcontrol_new stac9766_snd_ac97_controls[] = {
 static int stac9766_ac97_write(struct snd_soc_codec *codec, unsigned int reg,
 			       unsigned int val)
 {
+	struct snd_ac97 *ac97 = snd_soc_codec_get_drvdata(codec);
 	u16 *cache = codec->reg_cache;
 
 	if (reg > AC97_STAC_PAGE0) {
 		stac9766_ac97_write(codec, AC97_INT_PAGING, 0);
-		soc_ac97_ops->write(codec->ac97, reg, val);
+		soc_ac97_ops->write(ac97, reg, val);
 		stac9766_ac97_write(codec, AC97_INT_PAGING, 1);
 		return 0;
 	}
 	if (reg / 2 >= ARRAY_SIZE(stac9766_reg))
 		return -EIO;
 
-	soc_ac97_ops->write(codec->ac97, reg, val);
+	soc_ac97_ops->write(ac97, reg, val);
 	cache[reg / 2] = val;
 	return 0;
 }
@@ -158,11 +159,12 @@ static int stac9766_ac97_write(struct snd_soc_codec *codec, unsigned int reg,
 static unsigned int stac9766_ac97_read(struct snd_soc_codec *codec,
 				       unsigned int reg)
 {
+	struct snd_ac97 *ac97 = snd_soc_codec_get_drvdata(codec);
 	u16 val = 0, *cache = codec->reg_cache;
 
 	if (reg > AC97_STAC_PAGE0) {
 		stac9766_ac97_write(codec, AC97_INT_PAGING, 0);
-		val = soc_ac97_ops->read(codec->ac97, reg - AC97_STAC_PAGE0);
+		val = soc_ac97_ops->read(ac97, reg - AC97_STAC_PAGE0);
 		stac9766_ac97_write(codec, AC97_INT_PAGING, 1);
 		return val;
 	}
@@ -173,7 +175,7 @@ static unsigned int stac9766_ac97_read(struct snd_soc_codec *codec,
 		reg == AC97_INT_PAGING || reg == AC97_VENDOR_ID1 ||
 		reg == AC97_VENDOR_ID2) {
 
-		val = soc_ac97_ops->read(codec->ac97, reg);
+		val = soc_ac97_ops->read(ac97, reg);
 		return val;
 	}
 	return cache[reg / 2];
@@ -240,45 +242,41 @@ static int stac9766_set_bias_level(struct snd_soc_codec *codec,
 
 static int stac9766_reset(struct snd_soc_codec *codec, int try_warm)
 {
+	struct snd_ac97 *ac97 = snd_soc_codec_get_drvdata(codec);
+
 	if (try_warm && soc_ac97_ops->warm_reset) {
-		soc_ac97_ops->warm_reset(codec->ac97);
+		soc_ac97_ops->warm_reset(ac97);
 		if (stac9766_ac97_read(codec, 0) == stac9766_reg[0])
 			return 1;
 	}
 
-	soc_ac97_ops->reset(codec->ac97);
+	soc_ac97_ops->reset(ac97);
 	if (soc_ac97_ops->warm_reset)
-		soc_ac97_ops->warm_reset(codec->ac97);
+		soc_ac97_ops->warm_reset(ac97);
 	if (stac9766_ac97_read(codec, 0) != stac9766_reg[0])
 		return -EIO;
 	return 0;
 }
 
-static int stac9766_codec_suspend(struct snd_soc_codec *codec)
-{
-	stac9766_set_bias_level(codec, SND_SOC_BIAS_OFF);
-	return 0;
-}
-
 static int stac9766_codec_resume(struct snd_soc_codec *codec)
 {
+	struct snd_ac97 *ac97 = snd_soc_codec_get_drvdata(codec);
 	u16 id, reset;
 
 	reset = 0;
 	/* give the codec an AC97 warm reset to start the link */
 reset:
 	if (reset > 5) {
-		printk(KERN_ERR "stac9766 failed to resume");
+		dev_err(codec->dev, "Failed to resume\n");
 		return -EIO;
 	}
-	codec->ac97->bus->ops->warm_reset(codec->ac97);
-	id = soc_ac97_ops->read(codec->ac97, AC97_VENDOR_ID2);
+	ac97->bus->ops->warm_reset(ac97);
+	id = soc_ac97_ops->read(ac97, AC97_VENDOR_ID2);
 	if (id != 0x4c13) {
 		stac9766_reset(codec, 0);
 		reset++;
 		goto reset;
 	}
-	stac9766_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
 
 	return 0;
 }
@@ -294,7 +292,6 @@ static const struct snd_soc_dai_ops stac9766_dai_ops_digital = {
 static struct snd_soc_dai_driver stac9766_dai[] = {
 {
 	.name = "stac9766-hifi-analog",
-	.ac97_control = 1,
 
 	/* stream cababilities */
 	.playback = {
@@ -316,7 +313,6 @@ static struct snd_soc_dai_driver stac9766_dai[] = {
 },
 {
 	.name = "stac9766-hifi-IEC958",
-	.ac97_control = 1,
 
 	/* stream cababilities */
 	.playback = {
@@ -334,46 +330,48 @@ static struct snd_soc_dai_driver stac9766_dai[] = {
 
 static int stac9766_codec_probe(struct snd_soc_codec *codec)
 {
+	struct snd_ac97 *ac97;
 	int ret = 0;
 
-	ret = snd_soc_new_ac97_codec(codec, soc_ac97_ops, 0);
-	if (ret < 0)
-		goto codec_err;
+	ac97 = snd_soc_new_ac97_codec(codec);
+	if (IS_ERR(ac97))
+		return PTR_ERR(ac97);
+
+	snd_soc_codec_set_drvdata(codec, ac97);
 
 	/* do a cold reset for the controller and then try
 	 * a warm reset followed by an optional cold reset for codec */
 	stac9766_reset(codec, 0);
 	ret = stac9766_reset(codec, 1);
 	if (ret < 0) {
-		printk(KERN_ERR "Failed to reset STAC9766: AC97 link error\n");
+		dev_err(codec->dev, "Failed to reset: AC97 link error\n");
 		goto codec_err;
 	}
 
-	stac9766_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
-
-	snd_soc_add_codec_controls(codec, stac9766_snd_ac97_controls,
-			     ARRAY_SIZE(stac9766_snd_ac97_controls));
-
 	return 0;
 
 codec_err:
-	snd_soc_free_ac97_codec(codec);
+	snd_soc_free_ac97_codec(ac97);
 	return ret;
 }
 
 static int stac9766_codec_remove(struct snd_soc_codec *codec)
 {
-	snd_soc_free_ac97_codec(codec);
+	struct snd_ac97 *ac97 = snd_soc_codec_get_drvdata(codec);
+
+	snd_soc_free_ac97_codec(ac97);
 	return 0;
 }
 
 static struct snd_soc_codec_driver soc_codec_dev_stac9766 = {
+	.controls = stac9766_snd_ac97_controls,
+	.num_controls = ARRAY_SIZE(stac9766_snd_ac97_controls),
 	.write = stac9766_ac97_write,
 	.read = stac9766_ac97_read,
 	.set_bias_level = stac9766_set_bias_level,
+	.suspend_bias_off = true,
 	.probe = stac9766_codec_probe,
 	.remove = stac9766_codec_remove,
-	.suspend = stac9766_codec_suspend,
 	.resume = stac9766_codec_resume,
 	.reg_cache_size = ARRAY_SIZE(stac9766_reg),
 	.reg_word_size = sizeof(u16),
diff --git a/sound/soc/codecs/tas2552.c b/sound/soc/codecs/tas2552.c
index f039dc825971..b505212019e2 100644
--- a/sound/soc/codecs/tas2552.c
+++ b/sound/soc/codecs/tas2552.c
@@ -345,7 +345,6 @@ static const struct reg_default tas2552_init_regs[] = {
 static int tas2552_codec_probe(struct snd_soc_codec *codec)
 {
 	struct tas2552_data *tas2552 = snd_soc_codec_get_drvdata(codec);
-	struct snd_soc_dapm_context *dapm = &codec->dapm;
 	int ret;
 
 	tas2552->codec = codec;
@@ -390,11 +389,6 @@ static int tas2552_codec_probe(struct snd_soc_codec *codec)
 	snd_soc_write(codec, TAS2552_CFG_2, TAS2552_BOOST_EN |
 				  TAS2552_APT_EN | TAS2552_LIM_EN);
 
-	snd_soc_dapm_new_controls(dapm, tas2552_dapm_widgets,
-				ARRAY_SIZE(tas2552_dapm_widgets));
-	snd_soc_dapm_add_routes(dapm, tas2552_audio_map,
-				ARRAY_SIZE(tas2552_audio_map));
-
 	return 0;
 
 patch_fail:
@@ -462,6 +456,10 @@ static struct snd_soc_codec_driver soc_codec_dev_tas2552 = {
 	.resume = tas2552_resume,
 	.controls = tas2552_snd_controls,
 	.num_controls = ARRAY_SIZE(tas2552_snd_controls),
+	.dapm_widgets = tas2552_dapm_widgets,
+	.num_dapm_widgets = ARRAY_SIZE(tas2552_dapm_widgets),
+	.dapm_routes = tas2552_audio_map,
+	.num_dapm_routes = ARRAY_SIZE(tas2552_audio_map),
 };
 
 static const struct regmap_config tas2552_regmap_config = {
diff --git a/sound/soc/codecs/tfa9879.c b/sound/soc/codecs/tfa9879.c
new file mode 100644
index 000000000000..16f1b71edb55
--- /dev/null
+++ b/sound/soc/codecs/tfa9879.c
@@ -0,0 +1,328 @@
+/*
+ * tfa9879.c  --  driver for NXP Semiconductors TFA9879
+ *
+ * Copyright (C) 2014 Axentia Technologies AB
+ * Author: Peter Rosin <peda@axentia.se>
+ *
+ *  This program is free software; you can redistribute  it and/or modify it
+ *  under  the terms of  the GNU General  Public License as published by the
+ *  Free Software Foundation;  either version 2 of the  License, or (at your
+ *  option) any later version.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/i2c.h>
+#include <linux/regmap.h>
+#include <sound/soc.h>
+#include <sound/tlv.h>
+#include <sound/pcm_params.h>
+
+#include "tfa9879.h"
+
+struct tfa9879_priv {
+	struct regmap *regmap;
+	int lsb_justified;
+};
+
+static int tfa9879_hw_params(struct snd_pcm_substream *substream,
+			     struct snd_pcm_hw_params *params,
+			     struct snd_soc_dai *dai)
+{
+	struct snd_soc_codec *codec = dai->codec;
+	struct tfa9879_priv *tfa9879 = snd_soc_codec_get_drvdata(codec);
+	int fs;
+	int i2s_set = 0;
+
+	switch (params_rate(params)) {
+	case 8000:
+		fs = TFA9879_I2S_FS_8000;
+		break;
+	case 11025:
+		fs = TFA9879_I2S_FS_11025;
+		break;
+	case 12000:
+		fs = TFA9879_I2S_FS_12000;
+		break;
+	case 16000:
+		fs = TFA9879_I2S_FS_16000;
+		break;
+	case 22050:
+		fs = TFA9879_I2S_FS_22050;
+		break;
+	case 24000:
+		fs = TFA9879_I2S_FS_24000;
+		break;
+	case 32000:
+		fs = TFA9879_I2S_FS_32000;
+		break;
+	case 44100:
+		fs = TFA9879_I2S_FS_44100;
+		break;
+	case 48000:
+		fs = TFA9879_I2S_FS_48000;
+		break;
+	case 64000:
+		fs = TFA9879_I2S_FS_64000;
+		break;
+	case 88200:
+		fs = TFA9879_I2S_FS_88200;
+		break;
+	case 96000:
+		fs = TFA9879_I2S_FS_96000;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	switch (params_width(params)) {
+	case 16:
+		i2s_set = TFA9879_I2S_SET_LSB_J_16;
+		break;
+	case 24:
+		i2s_set = TFA9879_I2S_SET_LSB_J_24;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	if (tfa9879->lsb_justified)
+		snd_soc_update_bits(codec, TFA9879_SERIAL_INTERFACE_1,
+				    TFA9879_I2S_SET_MASK,
+				    i2s_set << TFA9879_I2S_SET_SHIFT);
+
+	snd_soc_update_bits(codec, TFA9879_SERIAL_INTERFACE_1,
+			    TFA9879_I2S_FS_MASK,
+			    fs << TFA9879_I2S_FS_SHIFT);
+	return 0;
+}
+
+static int tfa9879_digital_mute(struct snd_soc_dai *dai, int mute)
+{
+	struct snd_soc_codec *codec = dai->codec;
+
+	snd_soc_update_bits(codec, TFA9879_MISC_CONTROL,
+			    TFA9879_S_MUTE_MASK,
+			    !!mute << TFA9879_S_MUTE_SHIFT);
+
+	return 0;
+}
+
+static int tfa9879_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+	struct snd_soc_codec *codec = dai->codec;
+	struct tfa9879_priv *tfa9879 = snd_soc_codec_get_drvdata(codec);
+	int i2s_set;
+	int sck_pol;
+
+	switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+	case SND_SOC_DAIFMT_CBS_CFS:
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+	case SND_SOC_DAIFMT_NB_NF:
+		sck_pol = TFA9879_SCK_POL_NORMAL;
+		break;
+	case SND_SOC_DAIFMT_IB_NF:
+		sck_pol = TFA9879_SCK_POL_INVERSE;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+	case SND_SOC_DAIFMT_I2S:
+		tfa9879->lsb_justified = 0;
+		i2s_set = TFA9879_I2S_SET_I2S_24;
+		break;
+	case SND_SOC_DAIFMT_LEFT_J:
+		tfa9879->lsb_justified = 0;
+		i2s_set = TFA9879_I2S_SET_MSB_J_24;
+		break;
+	case SND_SOC_DAIFMT_RIGHT_J:
+		tfa9879->lsb_justified = 1;
+		i2s_set = TFA9879_I2S_SET_LSB_J_24;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	snd_soc_update_bits(codec, TFA9879_SERIAL_INTERFACE_1,
+			    TFA9879_SCK_POL_MASK,
+			    sck_pol << TFA9879_SCK_POL_SHIFT);
+	snd_soc_update_bits(codec, TFA9879_SERIAL_INTERFACE_1,
+			    TFA9879_I2S_SET_MASK,
+			    i2s_set << TFA9879_I2S_SET_SHIFT);
+	return 0;
+}
+
+static struct reg_default tfa9879_regs[] = {
+	{ TFA9879_DEVICE_CONTROL,	0x0000 }, /* 0x00 */
+	{ TFA9879_SERIAL_INTERFACE_1,	0x0a18 }, /* 0x01 */
+	{ TFA9879_PCM_IOM2_FORMAT_1,	0x0007 }, /* 0x02 */
+	{ TFA9879_SERIAL_INTERFACE_2,	0x0a18 }, /* 0x03 */
+	{ TFA9879_PCM_IOM2_FORMAT_2,	0x0007 }, /* 0x04 */
+	{ TFA9879_EQUALIZER_A1,		0x59dd }, /* 0x05 */
+	{ TFA9879_EQUALIZER_A2,		0xc63e }, /* 0x06 */
+	{ TFA9879_EQUALIZER_B1,		0x651a }, /* 0x07 */
+	{ TFA9879_EQUALIZER_B2,		0xe53e }, /* 0x08 */
+	{ TFA9879_EQUALIZER_C1,		0x4616 }, /* 0x09 */
+	{ TFA9879_EQUALIZER_C2,		0xd33e }, /* 0x0a */
+	{ TFA9879_EQUALIZER_D1,		0x4df3 }, /* 0x0b */
+	{ TFA9879_EQUALIZER_D2,		0xea3e }, /* 0x0c */
+	{ TFA9879_EQUALIZER_E1,		0x5ee0 }, /* 0x0d */
+	{ TFA9879_EQUALIZER_E2,		0xf93e }, /* 0x0e */
+	{ TFA9879_BYPASS_CONTROL,	0x0093 }, /* 0x0f */
+	{ TFA9879_DYNAMIC_RANGE_COMPR,	0x92ba }, /* 0x10 */
+	{ TFA9879_BASS_TREBLE,		0x12a5 }, /* 0x11 */
+	{ TFA9879_HIGH_PASS_FILTER,	0x0004 }, /* 0x12 */
+	{ TFA9879_VOLUME_CONTROL,	0x10bd }, /* 0x13 */
+	{ TFA9879_MISC_CONTROL,		0x0000 }, /* 0x14 */
+};
+
+static bool tfa9879_volatile_reg(struct device *dev, unsigned int reg)
+{
+	return reg == TFA9879_MISC_STATUS;
+}
+
+static const DECLARE_TLV_DB_SCALE(volume_tlv, -7050, 50, 1);
+static const DECLARE_TLV_DB_SCALE(tb_gain_tlv, -1800, 200, 0);
+static const char * const tb_freq_text[] = {
+	"Low", "Mid", "High"
+};
+static const struct soc_enum treble_freq_enum =
+	SOC_ENUM_SINGLE(TFA9879_BASS_TREBLE, TFA9879_F_TRBLE_SHIFT,
+			ARRAY_SIZE(tb_freq_text), tb_freq_text);
+static const struct soc_enum bass_freq_enum =
+	SOC_ENUM_SINGLE(TFA9879_BASS_TREBLE, TFA9879_F_BASS_SHIFT,
+			ARRAY_SIZE(tb_freq_text), tb_freq_text);
+
+static const struct snd_kcontrol_new tfa9879_controls[] = {
+	SOC_SINGLE_TLV("PCM Playback Volume", TFA9879_VOLUME_CONTROL,
+		       TFA9879_VOL_SHIFT, 0xbd, 1, volume_tlv),
+	SOC_SINGLE_TLV("Treble Volume", TFA9879_BASS_TREBLE,
+		       TFA9879_G_TRBLE_SHIFT, 18, 0, tb_gain_tlv),
+	SOC_SINGLE_TLV("Bass Volume", TFA9879_BASS_TREBLE,
+		       TFA9879_G_BASS_SHIFT, 18, 0, tb_gain_tlv),
+	SOC_ENUM("Treble Corner Freq", treble_freq_enum),
+	SOC_ENUM("Bass Corner Freq", bass_freq_enum),
+};
+
+static const struct snd_soc_dapm_widget tfa9879_dapm_widgets[] = {
+SND_SOC_DAPM_AIF_IN("AIFINL", "Playback", 0, SND_SOC_NOPM, 0, 0),
+SND_SOC_DAPM_AIF_IN("AIFINR", "Playback", 1, SND_SOC_NOPM, 0, 0),
+SND_SOC_DAPM_DAC("DAC", NULL, TFA9879_DEVICE_CONTROL, TFA9879_OPMODE_SHIFT, 0),
+SND_SOC_DAPM_OUTPUT("LINEOUT"),
+SND_SOC_DAPM_SUPPLY("POWER", TFA9879_DEVICE_CONTROL, TFA9879_POWERUP_SHIFT, 0,
+		    NULL, 0),
+};
+
+static const struct snd_soc_dapm_route tfa9879_dapm_routes[] = {
+	{ "DAC", NULL, "AIFINL" },
+	{ "DAC", NULL, "AIFINR" },
+
+	{ "LINEOUT", NULL, "DAC" },
+
+	{ "DAC", NULL, "POWER" },
+};
+
+static const struct snd_soc_codec_driver tfa9879_codec = {
+	.controls = tfa9879_controls,
+	.num_controls = ARRAY_SIZE(tfa9879_controls),
+
+	.dapm_widgets = tfa9879_dapm_widgets,
+	.num_dapm_widgets = ARRAY_SIZE(tfa9879_dapm_widgets),
+	.dapm_routes = tfa9879_dapm_routes,
+	.num_dapm_routes = ARRAY_SIZE(tfa9879_dapm_routes),
+};
+
+static const struct regmap_config tfa9879_regmap = {
+	.reg_bits = 8,
+	.val_bits = 16,
+
+	.volatile_reg = tfa9879_volatile_reg,
+	.max_register = TFA9879_MISC_STATUS,
+	.reg_defaults = tfa9879_regs,
+	.num_reg_defaults = ARRAY_SIZE(tfa9879_regs),
+	.cache_type = REGCACHE_RBTREE,
+};
+
+static const struct snd_soc_dai_ops tfa9879_dai_ops = {
+	.hw_params = tfa9879_hw_params,
+	.digital_mute = tfa9879_digital_mute,
+	.set_fmt = tfa9879_set_fmt,
+};
+
+#define TFA9879_RATES SNDRV_PCM_RATE_8000_96000
+
+#define TFA9879_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \
+			 SNDRV_PCM_FMTBIT_S24_LE)
+
+static struct snd_soc_dai_driver tfa9879_dai = {
+	.name = "tfa9879-hifi",
+	.playback = {
+		.stream_name = "Playback",
+		.channels_min = 2,
+		.channels_max = 2,
+		.rates = TFA9879_RATES,
+		.formats = TFA9879_FORMATS, },
+	.ops = &tfa9879_dai_ops,
+};
+
+static int tfa9879_i2c_probe(struct i2c_client *i2c,
+			     const struct i2c_device_id *id)
+{
+	struct tfa9879_priv *tfa9879;
+	int i;
+
+	tfa9879 = devm_kzalloc(&i2c->dev, sizeof(*tfa9879), GFP_KERNEL);
+	if (IS_ERR(tfa9879))
+		return PTR_ERR(tfa9879);
+
+	i2c_set_clientdata(i2c, tfa9879);
+
+	tfa9879->regmap = devm_regmap_init_i2c(i2c, &tfa9879_regmap);
+	if (IS_ERR(tfa9879->regmap))
+		return PTR_ERR(tfa9879->regmap);
+
+	/* Ensure the device is in reset state */
+	for (i = 0; i < ARRAY_SIZE(tfa9879_regs); i++)
+		regmap_write(tfa9879->regmap,
+			     tfa9879_regs[i].reg, tfa9879_regs[i].def);
+
+	return snd_soc_register_codec(&i2c->dev, &tfa9879_codec,
+				      &tfa9879_dai, 1);
+}
+
+static int tfa9879_i2c_remove(struct i2c_client *client)
+{
+	snd_soc_unregister_codec(&client->dev);
+
+	return 0;
+}
+
+static const struct i2c_device_id tfa9879_i2c_id[] = {
+	{ "tfa9879", 0 },
+	{ }
+};
+MODULE_DEVICE_TABLE(i2c, tfa9879_i2c_id);
+
+static struct i2c_driver tfa9879_i2c_driver = {
+	.driver = {
+		.name = "tfa9879",
+		.owner = THIS_MODULE,
+	},
+	.probe = tfa9879_i2c_probe,
+	.remove = tfa9879_i2c_remove,
+	.id_table = tfa9879_i2c_id,
+};
+
+module_i2c_driver(tfa9879_i2c_driver);
+
+MODULE_DESCRIPTION("ASoC NXP Semiconductors TFA9879 driver");
+MODULE_AUTHOR("Peter Rosin <peda@axentia.se>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/tfa9879.h b/sound/soc/codecs/tfa9879.h
new file mode 100644
index 000000000000..3408c90c4628
--- /dev/null
+++ b/sound/soc/codecs/tfa9879.h
@@ -0,0 +1,202 @@
+/*
+ * tfa9879.h  --  driver for NXP Semiconductors TFA9879
+ *
+ * Copyright (C) 2014 Axentia Technologies AB
+ * Author: Peter Rosin <peda@axentia.se>
+ *
+ *  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 _TFA9879_H
+#define _TFA9879_H
+
+#define TFA9879_DEVICE_CONTROL		0x00
+#define TFA9879_SERIAL_INTERFACE_1	0x01
+#define TFA9879_PCM_IOM2_FORMAT_1	0x02
+#define TFA9879_SERIAL_INTERFACE_2	0x03
+#define TFA9879_PCM_IOM2_FORMAT_2	0x04
+#define TFA9879_EQUALIZER_A1		0x05
+#define TFA9879_EQUALIZER_A2		0x06
+#define TFA9879_EQUALIZER_B1		0x07
+#define TFA9879_EQUALIZER_B2		0x08
+#define TFA9879_EQUALIZER_C1		0x09
+#define TFA9879_EQUALIZER_C2		0x0a
+#define TFA9879_EQUALIZER_D1		0x0b
+#define TFA9879_EQUALIZER_D2		0x0c
+#define TFA9879_EQUALIZER_E1		0x0d
+#define TFA9879_EQUALIZER_E2		0x0e
+#define TFA9879_BYPASS_CONTROL		0x0f
+#define TFA9879_DYNAMIC_RANGE_COMPR	0x10
+#define TFA9879_BASS_TREBLE		0x11
+#define TFA9879_HIGH_PASS_FILTER	0x12
+#define TFA9879_VOLUME_CONTROL		0x13
+#define TFA9879_MISC_CONTROL		0x14
+#define TFA9879_MISC_STATUS		0x15
+
+/* TFA9879_DEVICE_CONTROL */
+#define TFA9879_INPUT_SEL_MASK		0x0010
+#define TFA9879_INPUT_SEL_SHIFT		4
+#define TFA9879_OPMODE_MASK		0x0008
+#define TFA9879_OPMODE_SHIFT		3
+#define TFA9879_RESET_MASK		0x0002
+#define TFA9879_RESET_SHIFT		1
+#define TFA9879_POWERUP_MASK		0x0001
+#define TFA9879_POWERUP_SHIFT		0
+
+/* TFA9879_SERIAL_INTERFACE */
+#define TFA9879_MONO_SEL_MASK		0x0c00
+#define TFA9879_MONO_SEL_SHIFT		10
+#define TFA9879_MONO_SEL_LEFT		0
+#define TFA9879_MONO_SEL_RIGHT		1
+#define TFA9879_MONO_SEL_BOTH		2
+#define TFA9879_I2S_FS_MASK		0x03c0
+#define TFA9879_I2S_FS_SHIFT		6
+#define TFA9879_I2S_FS_8000		0
+#define TFA9879_I2S_FS_11025		1
+#define TFA9879_I2S_FS_12000		2
+#define TFA9879_I2S_FS_16000		3
+#define TFA9879_I2S_FS_22050		4
+#define TFA9879_I2S_FS_24000		5
+#define TFA9879_I2S_FS_32000		6
+#define TFA9879_I2S_FS_44100		7
+#define TFA9879_I2S_FS_48000		8
+#define TFA9879_I2S_FS_64000		9
+#define TFA9879_I2S_FS_88200		10
+#define TFA9879_I2S_FS_96000		11
+#define TFA9879_I2S_SET_MASK		0x0038
+#define TFA9879_I2S_SET_SHIFT		3
+#define TFA9879_I2S_SET_MSB_J_24	2
+#define TFA9879_I2S_SET_I2S_24		3
+#define TFA9879_I2S_SET_LSB_J_16	4
+#define TFA9879_I2S_SET_LSB_J_18	5
+#define TFA9879_I2S_SET_LSB_J_20	6
+#define TFA9879_I2S_SET_LSB_J_24	7
+#define TFA9879_SCK_POL_MASK		0x0004
+#define TFA9879_SCK_POL_SHIFT		2
+#define TFA9879_SCK_POL_NORMAL		0
+#define TFA9879_SCK_POL_INVERSE		1
+#define TFA9879_I_MODE_MASK		0x0003
+#define TFA9879_I_MODE_SHIFT		0
+#define TFA9879_I_MODE_I2S		0
+#define TFA9879_I_MODE_PCM_IOM2_SHORT	1
+#define TFA9879_I_MODE_PCM_IOM2_LONG	2
+
+/* TFA9879_PCM_IOM2_FORMAT */
+#define TFA9879_PCM_FS_MASK		0x0800
+#define TFA9879_PCM_FS_SHIFT		11
+#define TFA9879_A_LAW_MASK		0x0400
+#define TFA9879_A_LAW_SHIFT		10
+#define TFA9879_PCM_COMP_MASK		0x0200
+#define TFA9879_PCM_COMP_SHIFT		9
+#define TFA9879_PCM_DL_MASK		0x0100
+#define TFA9879_PCM_DL_SHIFT		8
+#define TFA9879_D1_SLOT_MASK		0x00f0
+#define TFA9879_D1_SLOT_SHIFT		4
+#define TFA9879_D2_SLOT_MASK		0x000f
+#define TFA9879_D2_SLOT_SHIFT		0
+
+/* TFA9879_EQUALIZER_X1 */
+#define TFA9879_T1_MASK			0x8000
+#define TFA9879_T1_SHIFT		15
+#define TFA9879_K1M_MASK		0x7ff0
+#define TFA9879_K1M_SHIFT		4
+#define TFA9879_K1E_MASK		0x000f
+#define TFA9879_K1E_SHIFT		0
+
+/* TFA9879_EQUALIZER_X2 */
+#define TFA9879_T2_MASK			0x8000
+#define TFA9879_T2_SHIFT		15
+#define TFA9879_K2M_MASK		0x7800
+#define TFA9879_K2M_SHIFT		11
+#define TFA9879_K2E_MASK		0x0700
+#define TFA9879_K2E_SHIFT		8
+#define TFA9879_K0_MASK			0x00fe
+#define TFA9879_K0_SHIFT		1
+#define TFA9879_S_MASK			0x0001
+#define TFA9879_S_SHIFT			0
+
+/* TFA9879_BYPASS_CONTROL */
+#define TFA9879_L_OCP_MASK		0x00c0
+#define TFA9879_L_OCP_SHIFT		6
+#define TFA9879_L_OTP_MASK		0x0030
+#define TFA9879_L_OTP_SHIFT		4
+#define TFA9879_CLIPCTRL_MASK		0x0008
+#define TFA9879_CLIPCTRL_SHIFT		3
+#define TFA9879_HPF_BP_MASK		0x0004
+#define TFA9879_HPF_BP_SHIFT		2
+#define TFA9879_DRC_BP_MASK		0x0002
+#define TFA9879_DRC_BP_SHIFT		1
+#define TFA9879_EQ_BP_MASK		0x0001
+#define TFA9879_EQ_BP_SHIFT		0
+
+/* TFA9879_DYNAMIC_RANGE_COMPR */
+#define TFA9879_AT_LVL_MASK		0xf000
+#define TFA9879_AT_LVL_SHIFT		12
+#define TFA9879_AT_RATE_MASK		0x0f00
+#define TFA9879_AT_RATE_SHIFT		8
+#define TFA9879_RL_LVL_MASK		0x00f0
+#define TFA9879_RL_LVL_SHIFT		4
+#define TFA9879_RL_RATE_MASK		0x000f
+#define TFA9879_RL_RATE_SHIFT		0
+
+/* TFA9879_BASS_TREBLE */
+#define TFA9879_G_TRBLE_MASK		0x3e00
+#define TFA9879_G_TRBLE_SHIFT		9
+#define TFA9879_F_TRBLE_MASK		0x0180
+#define TFA9879_F_TRBLE_SHIFT		7
+#define TFA9879_G_BASS_MASK		0x007c
+#define TFA9879_G_BASS_SHIFT		2
+#define TFA9879_F_BASS_MASK		0x0003
+#define TFA9879_F_BASS_SHIFT		0
+
+/* TFA9879_HIGH_PASS_FILTER */
+#define TFA9879_HP_CTRL_MASK		0x00ff
+#define TFA9879_HP_CTRL_SHIFT		0
+
+/* TFA9879_VOLUME_CONTROL */
+#define TFA9879_ZR_CRSS_MASK		0x1000
+#define TFA9879_ZR_CRSS_SHIFT		12
+#define TFA9879_VOL_MASK		0x00ff
+#define TFA9879_VOL_SHIFT		0
+
+/* TFA9879_MISC_CONTROL */
+#define TFA9879_DE_PHAS_MASK		0x0c00
+#define TFA9879_DE_PHAS_SHIFT		10
+#define TFA9879_H_MUTE_MASK		0x0200
+#define TFA9879_H_MUTE_SHIFT		9
+#define TFA9879_S_MUTE_MASK		0x0100
+#define TFA9879_S_MUTE_SHIFT		8
+#define TFA9879_P_LIM_MASK		0x00ff
+#define TFA9879_P_LIM_SHIFT		0
+
+/* TFA9879_MISC_STATUS */
+#define TFA9879_PS_MASK			0x4000
+#define TFA9879_PS_SHIFT		14
+#define TFA9879_PORA_MASK		0x2000
+#define TFA9879_PORA_SHIFT		13
+#define TFA9879_AMP_MASK		0x0600
+#define TFA9879_AMP_SHIFT		9
+#define TFA9879_IBP_2_MASK		0x0100
+#define TFA9879_IBP_2_SHIFT		8
+#define TFA9879_OFP_2_MASK		0x0080
+#define TFA9879_OFP_2_SHIFT		7
+#define TFA9879_UFP_2_MASK		0x0040
+#define TFA9879_UFP_2_SHIFT		6
+#define TFA9879_IBP_1_MASK		0x0020
+#define TFA9879_IBP_1_SHIFT		5
+#define TFA9879_OFP_1_MASK		0x0010
+#define TFA9879_OFP_1_SHIFT		4
+#define TFA9879_UFP_1_MASK		0x0008
+#define TFA9879_UFP_1_SHIFT		3
+#define TFA9879_OCPOKA_MASK		0x0004
+#define TFA9879_OCPOKA_SHIFT		2
+#define TFA9879_OCPOKB_MASK		0x0002
+#define TFA9879_OCPOKB_SHIFT		1
+#define TFA9879_OTPOK_MASK		0x0001
+#define TFA9879_OTPOK_SHIFT		0
+
+#endif
diff --git a/sound/soc/codecs/tlv320aic23.c b/sound/soc/codecs/tlv320aic23.c
index d67167920c2f..cc17e7e5126e 100644
--- a/sound/soc/codecs/tlv320aic23.c
+++ b/sound/soc/codecs/tlv320aic23.c
@@ -540,19 +540,11 @@ static struct snd_soc_dai_driver tlv320aic23_dai = {
 	.ops = &tlv320aic23_dai_ops,
 };
 
-static int tlv320aic23_suspend(struct snd_soc_codec *codec)
-{
-	tlv320aic23_set_bias_level(codec, SND_SOC_BIAS_OFF);
-
-	return 0;
-}
-
 static int tlv320aic23_resume(struct snd_soc_codec *codec)
 {
 	struct aic23 *aic23 = snd_soc_codec_get_drvdata(codec);
 	regcache_mark_dirty(aic23->regmap);
 	regcache_sync(aic23->regmap);
-	tlv320aic23_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
 
 	return 0;
 }
@@ -562,9 +554,6 @@ static int tlv320aic23_codec_probe(struct snd_soc_codec *codec)
 	/* Reset codec */
 	snd_soc_write(codec, TLV320AIC23_RESET, 0);
 
-	/* power on device */
-	tlv320aic23_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
-
 	snd_soc_write(codec, TLV320AIC23_DIGT, TLV320AIC23_DEEMP_44K);
 
 	/* Unmute input */
@@ -589,18 +578,12 @@ static int tlv320aic23_codec_probe(struct snd_soc_codec *codec)
 	return 0;
 }
 
-static int tlv320aic23_remove(struct snd_soc_codec *codec)
-{
-	tlv320aic23_set_bias_level(codec, SND_SOC_BIAS_OFF);
-	return 0;
-}
-
 static struct snd_soc_codec_driver soc_codec_dev_tlv320aic23 = {
 	.probe = tlv320aic23_codec_probe,
-	.remove = tlv320aic23_remove,
-	.suspend = tlv320aic23_suspend,
 	.resume = tlv320aic23_resume,
 	.set_bias_level = tlv320aic23_set_bias_level,
+	.suspend_bias_off = true,
+
 	.controls = tlv320aic23_snd_controls,
 	.num_controls = ARRAY_SIZE(tlv320aic23_snd_controls),
 	.dapm_widgets = tlv320aic23_dapm_widgets,
diff --git a/sound/soc/codecs/tlv320aic31xx.c b/sound/soc/codecs/tlv320aic31xx.c
index 145fe5b253d4..dc3223d6eca1 100644
--- a/sound/soc/codecs/tlv320aic31xx.c
+++ b/sound/soc/codecs/tlv320aic31xx.c
@@ -911,12 +911,13 @@ static int aic31xx_set_dai_sysclk(struct snd_soc_dai *codec_dai,
 	}
 	aic31xx->p_div = i;
 
-	for (i = 0; aic31xx_divs[i].mclk_p != freq/aic31xx->p_div; i++) {
-		if (i == ARRAY_SIZE(aic31xx_divs)) {
-			dev_err(aic31xx->dev, "%s: Unsupported frequency %d\n",
-				__func__, freq);
-			return -EINVAL;
-		}
+	for (i = 0; i < ARRAY_SIZE(aic31xx_divs) &&
+		     aic31xx_divs[i].mclk_p != freq/aic31xx->p_div; i++)
+		;
+	if (i == ARRAY_SIZE(aic31xx_divs)) {
+		dev_err(aic31xx->dev, "%s: Unsupported frequency %d\n",
+			__func__, freq);
+		return -EINVAL;
 	}
 
 	/* set clock on MCLK, BCLK, or GPIO1 as PLL input */
@@ -1056,18 +1057,6 @@ static int aic31xx_set_bias_level(struct snd_soc_codec *codec,
 	return 0;
 }
 
-static int aic31xx_suspend(struct snd_soc_codec *codec)
-{
-	aic31xx_set_bias_level(codec, SND_SOC_BIAS_OFF);
-	return 0;
-}
-
-static int aic31xx_resume(struct snd_soc_codec *codec)
-{
-	aic31xx_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
-	return 0;
-}
-
 static int aic31xx_codec_probe(struct snd_soc_codec *codec)
 {
 	int ret = 0;
@@ -1110,8 +1099,6 @@ static int aic31xx_codec_remove(struct snd_soc_codec *codec)
 {
 	struct aic31xx_priv *aic31xx = snd_soc_codec_get_drvdata(codec);
 	int i;
-	/* power down chip */
-	aic31xx_set_bias_level(codec, SND_SOC_BIAS_OFF);
 
 	for (i = 0; i < ARRAY_SIZE(aic31xx->supplies); i++)
 		regulator_unregister_notifier(aic31xx->supplies[i].consumer,
@@ -1123,9 +1110,9 @@ static int aic31xx_codec_remove(struct snd_soc_codec *codec)
 static struct snd_soc_codec_driver soc_codec_driver_aic31xx = {
 	.probe			= aic31xx_codec_probe,
 	.remove			= aic31xx_codec_remove,
-	.suspend		= aic31xx_suspend,
-	.resume			= aic31xx_resume,
 	.set_bias_level		= aic31xx_set_bias_level,
+	.suspend_bias_off	= true,
+
 	.controls		= aic31xx_snd_controls,
 	.num_controls		= ARRAY_SIZE(aic31xx_snd_controls),
 	.dapm_widgets		= aic31xx_dapm_widgets,
diff --git a/sound/soc/codecs/tlv320aic32x4.c b/sound/soc/codecs/tlv320aic32x4.c
index 6ea662db2410..015467ed606b 100644
--- a/sound/soc/codecs/tlv320aic32x4.c
+++ b/sound/soc/codecs/tlv320aic32x4.c
@@ -597,18 +597,6 @@ static struct snd_soc_dai_driver aic32x4_dai = {
 	.symmetric_rates = 1,
 };
 
-static int aic32x4_suspend(struct snd_soc_codec *codec)
-{
-	aic32x4_set_bias_level(codec, SND_SOC_BIAS_OFF);
-	return 0;
-}
-
-static int aic32x4_resume(struct snd_soc_codec *codec)
-{
-	aic32x4_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
-	return 0;
-}
-
 static int aic32x4_probe(struct snd_soc_codec *codec)
 {
 	struct aic32x4_priv *aic32x4 = snd_soc_codec_get_drvdata(codec);
@@ -654,8 +642,6 @@ static int aic32x4_probe(struct snd_soc_codec *codec)
 		snd_soc_write(codec, AIC32X4_RMICPGANIN,
 				AIC32X4_RMICPGANIN_CM1R_10K);
 
-	aic32x4_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
-
 	/*
 	 * Workaround: for an unknown reason, the ADC needs to be powered up
 	 * and down for the first capture to work properly. It seems related to
@@ -669,18 +655,10 @@ static int aic32x4_probe(struct snd_soc_codec *codec)
 	return 0;
 }
 
-static int aic32x4_remove(struct snd_soc_codec *codec)
-{
-	aic32x4_set_bias_level(codec, SND_SOC_BIAS_OFF);
-	return 0;
-}
-
 static struct snd_soc_codec_driver soc_codec_dev_aic32x4 = {
 	.probe = aic32x4_probe,
-	.remove = aic32x4_remove,
-	.suspend = aic32x4_suspend,
-	.resume = aic32x4_resume,
 	.set_bias_level = aic32x4_set_bias_level,
+	.suspend_bias_off = true,
 
 	.controls = aic32x4_snd_controls,
 	.num_controls = ARRAY_SIZE(aic32x4_snd_controls),
diff --git a/sound/soc/codecs/tlv320aic3x.c b/sound/soc/codecs/tlv320aic3x.c
index f7c2a575a892..b7ebce054b4e 100644
--- a/sound/soc/codecs/tlv320aic3x.c
+++ b/sound/soc/codecs/tlv320aic3x.c
@@ -78,6 +78,8 @@ struct aic3x_priv {
 	struct aic3x_disable_nb disable_nb[AIC3X_NUM_SUPPLIES];
 	struct aic3x_setup_data *setup;
 	unsigned int sysclk;
+	unsigned int dai_fmt;
+	unsigned int tdm_delay;
 	struct list_head list;
 	int master;
 	int gpio_reset;
@@ -214,61 +216,78 @@ static int mic_bias_event(struct snd_soc_dapm_widget *w,
 	return 0;
 }
 
-static const char *aic3x_left_dac_mux[] = { "DAC_L1", "DAC_L3", "DAC_L2" };
-static const char *aic3x_right_dac_mux[] = { "DAC_R1", "DAC_R3", "DAC_R2" };
-static const char *aic3x_left_hpcom_mux[] =
-    { "differential of HPLOUT", "constant VCM", "single-ended" };
-static const char *aic3x_right_hpcom_mux[] =
-    { "differential of HPROUT", "constant VCM", "single-ended",
-      "differential of HPLCOM", "external feedback" };
-static const char *aic3x_linein_mode_mux[] = { "single-ended", "differential" };
-static const char *aic3x_adc_hpf[] =
-    { "Disabled", "0.0045xFs", "0.0125xFs", "0.025xFs" };
-
-#define LDAC_ENUM	0
-#define RDAC_ENUM	1
-#define LHPCOM_ENUM	2
-#define RHPCOM_ENUM	3
-#define LINE1L_2_L_ENUM	4
-#define LINE1L_2_R_ENUM	5
-#define LINE1R_2_L_ENUM	6
-#define LINE1R_2_R_ENUM	7
-#define LINE2L_ENUM	8
-#define LINE2R_ENUM	9
-#define ADC_HPF_ENUM	10
-
-static const struct soc_enum aic3x_enum[] = {
-	SOC_ENUM_SINGLE(DAC_LINE_MUX, 6, 3, aic3x_left_dac_mux),
-	SOC_ENUM_SINGLE(DAC_LINE_MUX, 4, 3, aic3x_right_dac_mux),
-	SOC_ENUM_SINGLE(HPLCOM_CFG, 4, 3, aic3x_left_hpcom_mux),
-	SOC_ENUM_SINGLE(HPRCOM_CFG, 3, 5, aic3x_right_hpcom_mux),
-	SOC_ENUM_SINGLE(LINE1L_2_LADC_CTRL, 7, 2, aic3x_linein_mode_mux),
-	SOC_ENUM_SINGLE(LINE1L_2_RADC_CTRL, 7, 2, aic3x_linein_mode_mux),
-	SOC_ENUM_SINGLE(LINE1R_2_LADC_CTRL, 7, 2, aic3x_linein_mode_mux),
-	SOC_ENUM_SINGLE(LINE1R_2_RADC_CTRL, 7, 2, aic3x_linein_mode_mux),
-	SOC_ENUM_SINGLE(LINE2L_2_LADC_CTRL, 7, 2, aic3x_linein_mode_mux),
-	SOC_ENUM_SINGLE(LINE2R_2_RADC_CTRL, 7, 2, aic3x_linein_mode_mux),
-	SOC_ENUM_DOUBLE(AIC3X_CODEC_DFILT_CTRL, 6, 4, 4, aic3x_adc_hpf),
-};
-
-static const char *aic3x_agc_level[] =
-	{ "-5.5dB", "-8dB", "-10dB", "-12dB", "-14dB", "-17dB", "-20dB", "-24dB" };
-static const struct soc_enum aic3x_agc_level_enum[] = {
-	SOC_ENUM_SINGLE(LAGC_CTRL_A, 4, 8, aic3x_agc_level),
-	SOC_ENUM_SINGLE(RAGC_CTRL_A, 4, 8, aic3x_agc_level),
-};
-
-static const char *aic3x_agc_attack[] = { "8ms", "11ms", "16ms", "20ms" };
-static const struct soc_enum aic3x_agc_attack_enum[] = {
-	SOC_ENUM_SINGLE(LAGC_CTRL_A, 2, 4, aic3x_agc_attack),
-	SOC_ENUM_SINGLE(RAGC_CTRL_A, 2, 4, aic3x_agc_attack),
-};
-
-static const char *aic3x_agc_decay[] = { "100ms", "200ms", "400ms", "500ms" };
-static const struct soc_enum aic3x_agc_decay_enum[] = {
-	SOC_ENUM_SINGLE(LAGC_CTRL_A, 0, 4, aic3x_agc_decay),
-	SOC_ENUM_SINGLE(RAGC_CTRL_A, 0, 4, aic3x_agc_decay),
-};
+static const char * const aic3x_left_dac_mux[] = {
+	"DAC_L1", "DAC_L3", "DAC_L2" };
+static SOC_ENUM_SINGLE_DECL(aic3x_left_dac_enum, DAC_LINE_MUX, 6,
+			    aic3x_left_dac_mux);
+
+static const char * const aic3x_right_dac_mux[] = {
+	"DAC_R1", "DAC_R3", "DAC_R2" };
+static SOC_ENUM_SINGLE_DECL(aic3x_right_dac_enum, DAC_LINE_MUX, 4,
+			    aic3x_right_dac_mux);
+
+static const char * const aic3x_left_hpcom_mux[] = {
+	"differential of HPLOUT", "constant VCM", "single-ended" };
+static SOC_ENUM_SINGLE_DECL(aic3x_left_hpcom_enum, HPLCOM_CFG, 4,
+			    aic3x_left_hpcom_mux);
+
+static const char * const aic3x_right_hpcom_mux[] = {
+	"differential of HPROUT", "constant VCM", "single-ended",
+	"differential of HPLCOM", "external feedback" };
+static SOC_ENUM_SINGLE_DECL(aic3x_right_hpcom_enum, HPRCOM_CFG, 3,
+			    aic3x_right_hpcom_mux);
+
+static const char * const aic3x_linein_mode_mux[] = {
+	"single-ended", "differential" };
+static SOC_ENUM_SINGLE_DECL(aic3x_line1l_2_l_enum, LINE1L_2_LADC_CTRL, 7,
+			    aic3x_linein_mode_mux);
+static SOC_ENUM_SINGLE_DECL(aic3x_line1l_2_r_enum, LINE1L_2_RADC_CTRL, 7,
+			    aic3x_linein_mode_mux);
+static SOC_ENUM_SINGLE_DECL(aic3x_line1r_2_l_enum, LINE1R_2_LADC_CTRL, 7,
+			    aic3x_linein_mode_mux);
+static SOC_ENUM_SINGLE_DECL(aic3x_line1r_2_r_enum, LINE1R_2_RADC_CTRL, 7,
+			    aic3x_linein_mode_mux);
+static SOC_ENUM_SINGLE_DECL(aic3x_line2l_2_ldac_enum, LINE2L_2_LADC_CTRL, 7,
+			    aic3x_linein_mode_mux);
+static SOC_ENUM_SINGLE_DECL(aic3x_line2r_2_rdac_enum, LINE2R_2_RADC_CTRL, 7,
+			    aic3x_linein_mode_mux);
+
+static const char * const aic3x_adc_hpf[] = {
+	"Disabled", "0.0045xFs", "0.0125xFs", "0.025xFs" };
+static SOC_ENUM_DOUBLE_DECL(aic3x_adc_hpf_enum, AIC3X_CODEC_DFILT_CTRL, 6, 4,
+			    aic3x_adc_hpf);
+
+static const char * const aic3x_agc_level[] = {
+	"-5.5dB", "-8dB", "-10dB", "-12dB",
+	"-14dB", "-17dB", "-20dB", "-24dB" };
+static SOC_ENUM_SINGLE_DECL(aic3x_lagc_level_enum, LAGC_CTRL_A, 4,
+			    aic3x_agc_level);
+static SOC_ENUM_SINGLE_DECL(aic3x_ragc_level_enum, RAGC_CTRL_A, 4,
+			    aic3x_agc_level);
+
+static const char * const aic3x_agc_attack[] = {
+	"8ms", "11ms", "16ms", "20ms" };
+static SOC_ENUM_SINGLE_DECL(aic3x_lagc_attack_enum, LAGC_CTRL_A, 2,
+			    aic3x_agc_attack);
+static SOC_ENUM_SINGLE_DECL(aic3x_ragc_attack_enum, RAGC_CTRL_A, 2,
+			    aic3x_agc_attack);
+
+static const char * const aic3x_agc_decay[] = {
+	"100ms", "200ms", "400ms", "500ms" };
+static SOC_ENUM_SINGLE_DECL(aic3x_lagc_decay_enum, LAGC_CTRL_A, 0,
+			    aic3x_agc_decay);
+static SOC_ENUM_SINGLE_DECL(aic3x_ragc_decay_enum, RAGC_CTRL_A, 0,
+			    aic3x_agc_decay);
+
+static const char * const aic3x_poweron_time[] = {
+	"0us", "10us", "100us", "1ms", "10ms", "50ms",
+	"100ms", "200ms", "400ms", "800ms", "2s", "4s" };
+static SOC_ENUM_SINGLE_DECL(aic3x_poweron_time_enum, HPOUT_POP_REDUCTION, 4,
+			    aic3x_poweron_time);
+
+static const char * const aic3x_rampup_step[] = { "0ms", "1ms", "2ms", "4ms" };
+static SOC_ENUM_SINGLE_DECL(aic3x_rampup_step_enum, HPOUT_POP_REDUCTION, 2,
+			    aic3x_rampup_step);
 
 /*
  * DAC digital volumes. From -63.5 to 0 dB in 0.5 dB steps
@@ -383,12 +402,12 @@ static const struct snd_kcontrol_new aic3x_snd_controls[] = {
 	 * adjust PGA to max value when ADC is on and will never go back.
 	*/
 	SOC_DOUBLE_R("AGC Switch", LAGC_CTRL_A, RAGC_CTRL_A, 7, 0x01, 0),
-	SOC_ENUM("Left AGC Target level", aic3x_agc_level_enum[0]),
-	SOC_ENUM("Right AGC Target level", aic3x_agc_level_enum[1]),
-	SOC_ENUM("Left AGC Attack time", aic3x_agc_attack_enum[0]),
-	SOC_ENUM("Right AGC Attack time", aic3x_agc_attack_enum[1]),
-	SOC_ENUM("Left AGC Decay time", aic3x_agc_decay_enum[0]),
-	SOC_ENUM("Right AGC Decay time", aic3x_agc_decay_enum[1]),
+	SOC_ENUM("Left AGC Target level", aic3x_lagc_level_enum),
+	SOC_ENUM("Right AGC Target level", aic3x_ragc_level_enum),
+	SOC_ENUM("Left AGC Attack time", aic3x_lagc_attack_enum),
+	SOC_ENUM("Right AGC Attack time", aic3x_ragc_attack_enum),
+	SOC_ENUM("Left AGC Decay time", aic3x_lagc_decay_enum),
+	SOC_ENUM("Right AGC Decay time", aic3x_ragc_decay_enum),
 
 	/* De-emphasis */
 	SOC_DOUBLE("De-emphasis Switch", AIC3X_CODEC_DFILT_CTRL, 2, 0, 0x01, 0),
@@ -398,7 +417,11 @@ static const struct snd_kcontrol_new aic3x_snd_controls[] = {
 			 0, 119, 0, adc_tlv),
 	SOC_DOUBLE_R("PGA Capture Switch", LADC_VOL, RADC_VOL, 7, 0x01, 1),
 
-	SOC_ENUM("ADC HPF Cut-off", aic3x_enum[ADC_HPF_ENUM]),
+	SOC_ENUM("ADC HPF Cut-off", aic3x_adc_hpf_enum),
+
+	/* Pop reduction */
+	SOC_ENUM("Output Driver Power-On time", aic3x_poweron_time_enum),
+	SOC_ENUM("Output Driver Ramp-up step", aic3x_rampup_step_enum),
 };
 
 static const struct snd_kcontrol_new aic3x_mono_controls[] = {
@@ -425,19 +448,19 @@ static const struct snd_kcontrol_new aic3x_classd_amp_gain_ctrl =
 
 /* Left DAC Mux */
 static const struct snd_kcontrol_new aic3x_left_dac_mux_controls =
-SOC_DAPM_ENUM("Route", aic3x_enum[LDAC_ENUM]);
+SOC_DAPM_ENUM("Route", aic3x_left_dac_enum);
 
 /* Right DAC Mux */
 static const struct snd_kcontrol_new aic3x_right_dac_mux_controls =
-SOC_DAPM_ENUM("Route", aic3x_enum[RDAC_ENUM]);
+SOC_DAPM_ENUM("Route", aic3x_right_dac_enum);
 
 /* Left HPCOM Mux */
 static const struct snd_kcontrol_new aic3x_left_hpcom_mux_controls =
-SOC_DAPM_ENUM("Route", aic3x_enum[LHPCOM_ENUM]);
+SOC_DAPM_ENUM("Route", aic3x_left_hpcom_enum);
 
 /* Right HPCOM Mux */
 static const struct snd_kcontrol_new aic3x_right_hpcom_mux_controls =
-SOC_DAPM_ENUM("Route", aic3x_enum[RHPCOM_ENUM]);
+SOC_DAPM_ENUM("Route", aic3x_right_hpcom_enum);
 
 /* Left Line Mixer */
 static const struct snd_kcontrol_new aic3x_left_line_mixer_controls[] = {
@@ -529,23 +552,23 @@ static const struct snd_kcontrol_new aic3x_right_pga_mixer_controls[] = {
 
 /* Left Line1 Mux */
 static const struct snd_kcontrol_new aic3x_left_line1l_mux_controls =
-SOC_DAPM_ENUM("Route", aic3x_enum[LINE1L_2_L_ENUM]);
+SOC_DAPM_ENUM("Route", aic3x_line1l_2_l_enum);
 static const struct snd_kcontrol_new aic3x_right_line1l_mux_controls =
-SOC_DAPM_ENUM("Route", aic3x_enum[LINE1L_2_R_ENUM]);
+SOC_DAPM_ENUM("Route", aic3x_line1l_2_r_enum);
 
 /* Right Line1 Mux */
 static const struct snd_kcontrol_new aic3x_right_line1r_mux_controls =
-SOC_DAPM_ENUM("Route", aic3x_enum[LINE1R_2_R_ENUM]);
+SOC_DAPM_ENUM("Route", aic3x_line1r_2_r_enum);
 static const struct snd_kcontrol_new aic3x_left_line1r_mux_controls =
-SOC_DAPM_ENUM("Route", aic3x_enum[LINE1R_2_L_ENUM]);
+SOC_DAPM_ENUM("Route", aic3x_line1r_2_l_enum);
 
 /* Left Line2 Mux */
 static const struct snd_kcontrol_new aic3x_left_line2_mux_controls =
-SOC_DAPM_ENUM("Route", aic3x_enum[LINE2L_ENUM]);
+SOC_DAPM_ENUM("Route", aic3x_line2l_2_ldac_enum);
 
 /* Right Line2 Mux */
 static const struct snd_kcontrol_new aic3x_right_line2_mux_controls =
-SOC_DAPM_ENUM("Route", aic3x_enum[LINE2R_ENUM]);
+SOC_DAPM_ENUM("Route", aic3x_line2r_2_rdac_enum);
 
 static const struct snd_soc_dapm_widget aic3x_dapm_widgets[] = {
 	/* Left DAC to Left Outputs */
@@ -1009,6 +1032,25 @@ found:
 	return 0;
 }
 
+static int aic3x_prepare(struct snd_pcm_substream *substream,
+			 struct snd_soc_dai *dai)
+{
+	struct snd_soc_codec *codec = dai->codec;
+	struct aic3x_priv *aic3x = snd_soc_codec_get_drvdata(codec);
+	int delay = 0;
+
+	/* TDM slot selection only valid in DSP_A/_B mode */
+	if (aic3x->dai_fmt == SND_SOC_DAIFMT_DSP_A)
+		delay += (aic3x->tdm_delay + 1);
+	else if (aic3x->dai_fmt == SND_SOC_DAIFMT_DSP_B)
+		delay += aic3x->tdm_delay;
+
+	/* Configure data delay */
+	snd_soc_write(codec, AIC3X_ASD_INTF_CTRLC, aic3x->tdm_delay);
+
+	return 0;
+}
+
 static int aic3x_mute(struct snd_soc_dai *dai, int mute)
 {
 	struct snd_soc_codec *codec = dai->codec;
@@ -1048,7 +1090,6 @@ static int aic3x_set_dai_fmt(struct snd_soc_dai *codec_dai,
 	struct snd_soc_codec *codec = codec_dai->codec;
 	struct aic3x_priv *aic3x = snd_soc_codec_get_drvdata(codec);
 	u8 iface_areg, iface_breg;
-	int delay = 0;
 
 	iface_areg = snd_soc_read(codec, AIC3X_ASD_INTF_CTRLA) & 0x3f;
 	iface_breg = snd_soc_read(codec, AIC3X_ASD_INTF_CTRLB) & 0x3f;
@@ -1076,7 +1117,6 @@ static int aic3x_set_dai_fmt(struct snd_soc_dai *codec_dai,
 	case (SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF):
 		break;
 	case (SND_SOC_DAIFMT_DSP_A | SND_SOC_DAIFMT_IB_NF):
-		delay = 1;
 	case (SND_SOC_DAIFMT_DSP_B | SND_SOC_DAIFMT_IB_NF):
 		iface_breg |= (0x01 << 6);
 		break;
@@ -1090,10 +1130,45 @@ static int aic3x_set_dai_fmt(struct snd_soc_dai *codec_dai,
 		return -EINVAL;
 	}
 
+	aic3x->dai_fmt = fmt & SND_SOC_DAIFMT_FORMAT_MASK;
+
 	/* set iface */
 	snd_soc_write(codec, AIC3X_ASD_INTF_CTRLA, iface_areg);
 	snd_soc_write(codec, AIC3X_ASD_INTF_CTRLB, iface_breg);
-	snd_soc_write(codec, AIC3X_ASD_INTF_CTRLC, delay);
+
+	return 0;
+}
+
+static int aic3x_set_dai_tdm_slot(struct snd_soc_dai *codec_dai,
+				  unsigned int tx_mask, unsigned int rx_mask,
+				  int slots, int slot_width)
+{
+	struct snd_soc_codec *codec = codec_dai->codec;
+	struct aic3x_priv *aic3x = snd_soc_codec_get_drvdata(codec);
+	unsigned int lsb;
+
+	if (tx_mask != rx_mask) {
+		dev_err(codec->dev, "tx and rx masks must be symmetric\n");
+		return -EINVAL;
+	}
+
+	if (unlikely(!tx_mask)) {
+		dev_err(codec->dev, "tx and rx 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;
+	}
+
+	aic3x->tdm_delay = lsb * slot_width;
+
+	/* DOUT in high-impedance on inactive bit clocks */
+	snd_soc_update_bits(codec, AIC3X_ASD_INTF_CTRLA,
+			    DOUT_TRISTATE, DOUT_TRISTATE);
 
 	return 0;
 }
@@ -1212,9 +1287,11 @@ static int aic3x_set_bias_level(struct snd_soc_codec *codec,
 
 static const struct snd_soc_dai_ops aic3x_dai_ops = {
 	.hw_params	= aic3x_hw_params,
+	.prepare	= aic3x_prepare,
 	.digital_mute	= aic3x_mute,
 	.set_sysclk	= aic3x_set_dai_sysclk,
 	.set_fmt	= aic3x_set_dai_fmt,
+	.set_tdm_slot	= aic3x_set_dai_tdm_slot,
 };
 
 static struct snd_soc_dai_driver aic3x_dai = {
@@ -1414,7 +1491,6 @@ static int aic3x_remove(struct snd_soc_codec *codec)
 	struct aic3x_priv *aic3x = snd_soc_codec_get_drvdata(codec);
 	int i;
 
-	aic3x_set_bias_level(codec, SND_SOC_BIAS_OFF);
 	list_del(&aic3x->list);
 	for (i = 0; i < ARRAY_SIZE(aic3x->supplies); i++)
 		regulator_unregister_notifier(aic3x->supplies[i].consumer,
diff --git a/sound/soc/codecs/tlv320aic3x.h b/sound/soc/codecs/tlv320aic3x.h
index e521ac3ddde8..89fa692df206 100644
--- a/sound/soc/codecs/tlv320aic3x.h
+++ b/sound/soc/codecs/tlv320aic3x.h
@@ -169,6 +169,7 @@
 /* Audio serial data interface control register A bits */
 #define BIT_CLK_MASTER          0x80
 #define WORD_CLK_MASTER         0x40
+#define DOUT_TRISTATE		0x20
 
 /* Codec Datapath setup register 7 */
 #define FSREF_44100		(1 << 7)
diff --git a/sound/soc/codecs/tlv320dac33.c b/sound/soc/codecs/tlv320dac33.c
index e21ed934bdbf..0fe2ced5b09f 100644
--- a/sound/soc/codecs/tlv320dac33.c
+++ b/sound/soc/codecs/tlv320dac33.c
@@ -1436,8 +1436,6 @@ static int dac33_soc_remove(struct snd_soc_codec *codec)
 {
 	struct tlv320dac33_priv *dac33 = snd_soc_codec_get_drvdata(codec);
 
-	dac33_set_bias_level(codec, SND_SOC_BIAS_OFF);
-
 	if (dac33->irq >= 0) {
 		free_irq(dac33->irq, dac33->codec);
 		destroy_workqueue(dac33->dac33_wq);
diff --git a/sound/soc/codecs/ts3a227e.c b/sound/soc/codecs/ts3a227e.c
new file mode 100644
index 000000000000..1d1205702d23
--- /dev/null
+++ b/sound/soc/codecs/ts3a227e.c
@@ -0,0 +1,314 @@
+/*
+ * TS3A227E Autonomous Audio Accessory Detection and Configuration Switch
+ *
+ * Copyright (C) 2014 Google, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/gpio.h>
+#include <linux/i2c.h>
+#include <linux/init.h>
+#include <linux/input.h>
+#include <linux/module.h>
+#include <linux/of_gpio.h>
+#include <linux/regmap.h>
+
+#include <sound/core.h>
+#include <sound/jack.h>
+#include <sound/soc.h>
+
+struct ts3a227e {
+	struct regmap *regmap;
+	struct snd_soc_jack *jack;
+	bool plugged;
+	bool mic_present;
+	unsigned int buttons_held;
+};
+
+/* Button values to be reported on the jack */
+static const int ts3a227e_buttons[] = {
+	SND_JACK_BTN_0,
+	SND_JACK_BTN_1,
+	SND_JACK_BTN_2,
+	SND_JACK_BTN_3,
+};
+
+#define TS3A227E_NUM_BUTTONS 4
+#define TS3A227E_JACK_MASK (SND_JACK_HEADPHONE | \
+			    SND_JACK_MICROPHONE | \
+			    SND_JACK_BTN_0 | \
+			    SND_JACK_BTN_1 | \
+			    SND_JACK_BTN_2 | \
+			    SND_JACK_BTN_3)
+
+/* TS3A227E registers */
+#define TS3A227E_REG_DEVICE_ID		0x00
+#define TS3A227E_REG_INTERRUPT		0x01
+#define TS3A227E_REG_KP_INTERRUPT	0x02
+#define TS3A227E_REG_INTERRUPT_DISABLE	0x03
+#define TS3A227E_REG_SETTING_1		0x04
+#define TS3A227E_REG_SETTING_2		0x05
+#define TS3A227E_REG_SETTING_3		0x06
+#define TS3A227E_REG_SWITCH_CONTROL_1	0x07
+#define TS3A227E_REG_SWITCH_CONTROL_2	0x08
+#define TS3A227E_REG_SWITCH_STATUS_1	0x09
+#define TS3A227E_REG_SWITCH_STATUS_2	0x0a
+#define TS3A227E_REG_ACCESSORY_STATUS	0x0b
+#define TS3A227E_REG_ADC_OUTPUT		0x0c
+#define TS3A227E_REG_KP_THRESHOLD_1	0x0d
+#define TS3A227E_REG_KP_THRESHOLD_2	0x0e
+#define TS3A227E_REG_KP_THRESHOLD_3	0x0f
+
+/* TS3A227E_REG_INTERRUPT 0x01 */
+#define INS_REM_EVENT 0x01
+#define DETECTION_COMPLETE_EVENT 0x02
+
+/* TS3A227E_REG_KP_INTERRUPT 0x02 */
+#define PRESS_MASK(idx) (0x01 << (2 * (idx)))
+#define RELEASE_MASK(idx) (0x02 << (2 * (idx)))
+
+/* TS3A227E_REG_INTERRUPT_DISABLE 0x03 */
+#define INS_REM_INT_DISABLE 0x01
+#define DETECTION_COMPLETE_INT_DISABLE 0x02
+#define ADC_COMPLETE_INT_DISABLE 0x04
+#define INTB_DISABLE 0x08
+
+/* TS3A227E_REG_SETTING_2 0x05 */
+#define KP_ENABLE 0x04
+
+/* TS3A227E_REG_ACCESSORY_STATUS  0x0b */
+#define TYPE_3_POLE 0x01
+#define TYPE_4_POLE_OMTP 0x02
+#define TYPE_4_POLE_STANDARD 0x04
+#define JACK_INSERTED 0x08
+#define EITHER_MIC_MASK (TYPE_4_POLE_OMTP | TYPE_4_POLE_STANDARD)
+
+static const struct reg_default ts3a227e_reg_defaults[] = {
+	{ TS3A227E_REG_DEVICE_ID, 0x10 },
+	{ TS3A227E_REG_INTERRUPT, 0x00 },
+	{ TS3A227E_REG_KP_INTERRUPT, 0x00 },
+	{ TS3A227E_REG_INTERRUPT_DISABLE, 0x08 },
+	{ TS3A227E_REG_SETTING_1, 0x23 },
+	{ TS3A227E_REG_SETTING_2, 0x00 },
+	{ TS3A227E_REG_SETTING_3, 0x0e },
+	{ TS3A227E_REG_SWITCH_CONTROL_1, 0x00 },
+	{ TS3A227E_REG_SWITCH_CONTROL_2, 0x00 },
+	{ TS3A227E_REG_SWITCH_STATUS_1, 0x0c },
+	{ TS3A227E_REG_SWITCH_STATUS_2, 0x00 },
+	{ TS3A227E_REG_ACCESSORY_STATUS, 0x00 },
+	{ TS3A227E_REG_ADC_OUTPUT, 0x00 },
+	{ TS3A227E_REG_KP_THRESHOLD_1, 0x20 },
+	{ TS3A227E_REG_KP_THRESHOLD_2, 0x40 },
+	{ TS3A227E_REG_KP_THRESHOLD_3, 0x68 },
+};
+
+static bool ts3a227e_readable_reg(struct device *dev, unsigned int reg)
+{
+	switch (reg) {
+	case TS3A227E_REG_DEVICE_ID ... TS3A227E_REG_KP_THRESHOLD_3:
+		return true;
+	default:
+		return false;
+	}
+}
+
+static bool ts3a227e_writeable_reg(struct device *dev, unsigned int reg)
+{
+	switch (reg) {
+	case TS3A227E_REG_INTERRUPT_DISABLE ... TS3A227E_REG_SWITCH_CONTROL_2:
+	case TS3A227E_REG_KP_THRESHOLD_1 ... TS3A227E_REG_KP_THRESHOLD_3:
+		return true;
+	default:
+		return false;
+	}
+}
+
+static bool ts3a227e_volatile_reg(struct device *dev, unsigned int reg)
+{
+	switch (reg) {
+	case TS3A227E_REG_INTERRUPT ... TS3A227E_REG_INTERRUPT_DISABLE:
+	case TS3A227E_REG_SETTING_2:
+	case TS3A227E_REG_SWITCH_STATUS_1 ... TS3A227E_REG_ADC_OUTPUT:
+		return true;
+	default:
+		return false;
+	}
+}
+
+static void ts3a227e_jack_report(struct ts3a227e *ts3a227e)
+{
+	unsigned int i;
+	int report = 0;
+
+	if (!ts3a227e->jack)
+		return;
+
+	if (ts3a227e->plugged)
+		report = SND_JACK_HEADPHONE;
+	if (ts3a227e->mic_present)
+		report |= SND_JACK_MICROPHONE;
+	for (i = 0; i < TS3A227E_NUM_BUTTONS; i++) {
+		if (ts3a227e->buttons_held & (1 << i))
+			report |= ts3a227e_buttons[i];
+	}
+	snd_soc_jack_report(ts3a227e->jack, report, TS3A227E_JACK_MASK);
+}
+
+static void ts3a227e_new_jack_state(struct ts3a227e *ts3a227e, unsigned acc_reg)
+{
+	bool plugged, mic_present;
+
+	plugged = !!(acc_reg & JACK_INSERTED);
+	mic_present = plugged && !!(acc_reg & EITHER_MIC_MASK);
+
+	ts3a227e->plugged = plugged;
+
+	if (mic_present != ts3a227e->mic_present) {
+		ts3a227e->mic_present = mic_present;
+		ts3a227e->buttons_held = 0;
+		if (mic_present) {
+			/* Enable key press detection. */
+			regmap_update_bits(ts3a227e->regmap,
+					   TS3A227E_REG_SETTING_2,
+					   KP_ENABLE, KP_ENABLE);
+		}
+	}
+}
+
+static irqreturn_t ts3a227e_interrupt(int irq, void *data)
+{
+	struct ts3a227e *ts3a227e = (struct ts3a227e *)data;
+	struct regmap *regmap = ts3a227e->regmap;
+	unsigned int int_reg, kp_int_reg, acc_reg, i;
+
+	/* Check for plug/unplug. */
+	regmap_read(regmap, TS3A227E_REG_INTERRUPT, &int_reg);
+	if (int_reg & (DETECTION_COMPLETE_EVENT | INS_REM_EVENT)) {
+		regmap_read(regmap, TS3A227E_REG_ACCESSORY_STATUS, &acc_reg);
+		ts3a227e_new_jack_state(ts3a227e, acc_reg);
+	}
+
+	/* Report any key events. */
+	regmap_read(regmap, TS3A227E_REG_KP_INTERRUPT, &kp_int_reg);
+	for (i = 0; i < TS3A227E_NUM_BUTTONS; i++) {
+		if (kp_int_reg & PRESS_MASK(i))
+			ts3a227e->buttons_held |= (1 << i);
+		if (kp_int_reg & RELEASE_MASK(i))
+			ts3a227e->buttons_held &= ~(1 << i);
+	}
+
+	ts3a227e_jack_report(ts3a227e);
+
+	return IRQ_HANDLED;
+}
+
+/**
+ * ts3a227e_enable_jack_detect - Specify a jack for event reporting
+ *
+ * @component:  component to register the jack with
+ * @jack: jack to use to report headset and button events on
+ *
+ * After this function has been called the headset insert/remove and button
+ * events 0-3 will be routed to the given jack.  Jack can be null to stop
+ * reporting.
+ */
+int ts3a227e_enable_jack_detect(struct snd_soc_component *component,
+				struct snd_soc_jack *jack)
+{
+	struct ts3a227e *ts3a227e = snd_soc_component_get_drvdata(component);
+
+	snd_jack_set_key(jack->jack, SND_JACK_BTN_0, KEY_MEDIA);
+	snd_jack_set_key(jack->jack, SND_JACK_BTN_1, KEY_VOLUMEUP);
+	snd_jack_set_key(jack->jack, SND_JACK_BTN_2, KEY_VOLUMEDOWN);
+	snd_jack_set_key(jack->jack, SND_JACK_BTN_3, KEY_VOICECOMMAND);
+
+	ts3a227e->jack = jack;
+	ts3a227e_jack_report(ts3a227e);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(ts3a227e_enable_jack_detect);
+
+static struct snd_soc_component_driver ts3a227e_soc_driver;
+
+static const struct regmap_config ts3a227e_regmap_config = {
+	.val_bits = 8,
+	.reg_bits = 8,
+
+	.max_register = TS3A227E_REG_KP_THRESHOLD_3,
+	.readable_reg = ts3a227e_readable_reg,
+	.writeable_reg = ts3a227e_writeable_reg,
+	.volatile_reg = ts3a227e_volatile_reg,
+
+	.cache_type = REGCACHE_RBTREE,
+	.reg_defaults = ts3a227e_reg_defaults,
+	.num_reg_defaults = ARRAY_SIZE(ts3a227e_reg_defaults),
+};
+
+static int ts3a227e_i2c_probe(struct i2c_client *i2c,
+			      const struct i2c_device_id *id)
+{
+	struct ts3a227e *ts3a227e;
+	struct device *dev = &i2c->dev;
+	int ret;
+
+	ts3a227e = devm_kzalloc(&i2c->dev, sizeof(*ts3a227e), GFP_KERNEL);
+	if (ts3a227e == NULL)
+		return -ENOMEM;
+
+	i2c_set_clientdata(i2c, ts3a227e);
+
+	ts3a227e->regmap = devm_regmap_init_i2c(i2c, &ts3a227e_regmap_config);
+	if (IS_ERR(ts3a227e->regmap))
+		return PTR_ERR(ts3a227e->regmap);
+
+	ret = devm_request_threaded_irq(dev, i2c->irq, NULL, ts3a227e_interrupt,
+					IRQF_TRIGGER_LOW | IRQF_ONESHOT,
+					"TS3A227E", ts3a227e);
+	if (ret) {
+		dev_err(dev, "Cannot request irq %d (%d)\n", i2c->irq, ret);
+		return ret;
+	}
+
+	ret = devm_snd_soc_register_component(&i2c->dev, &ts3a227e_soc_driver,
+					      NULL, 0);
+	if (ret)
+		return ret;
+
+	/* Enable interrupts except for ADC complete. */
+	regmap_update_bits(ts3a227e->regmap, TS3A227E_REG_INTERRUPT_DISABLE,
+			   INTB_DISABLE | ADC_COMPLETE_INT_DISABLE,
+			   ADC_COMPLETE_INT_DISABLE);
+
+	return 0;
+}
+
+static const struct i2c_device_id ts3a227e_i2c_ids[] = {
+	{ "ts3a227e", 0 },
+	{ }
+};
+MODULE_DEVICE_TABLE(i2c, ts3a227e_i2c_ids);
+
+static const struct of_device_id ts3a227e_of_match[] = {
+	{ .compatible = "ti,ts3a227e", },
+	{ }
+};
+MODULE_DEVICE_TABLE(of, ts3a227e_of_match);
+
+static struct i2c_driver ts3a227e_driver = {
+	.driver = {
+		.name = "ts3a227e",
+		.owner = THIS_MODULE,
+		.of_match_table = of_match_ptr(ts3a227e_of_match),
+	},
+	.probe = ts3a227e_i2c_probe,
+	.id_table = ts3a227e_i2c_ids,
+};
+module_i2c_driver(ts3a227e_driver);
+
+MODULE_DESCRIPTION("ASoC ts3a227e driver");
+MODULE_AUTHOR("Dylan Reid <dgreid@chromium.org>");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/ts3a227e.h b/sound/soc/codecs/ts3a227e.h
new file mode 100644
index 000000000000..e2acf9c5bebe
--- /dev/null
+++ b/sound/soc/codecs/ts3a227e.h
@@ -0,0 +1,17 @@
+/*
+ * TS3A227E Autonous Audio Accessory Detection and Configureation Switch
+ *
+ * Copyright (C) 2014 Google, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef _TS3A227E_H
+#define _TS3A227E_H
+
+int ts3a227e_enable_jack_detect(struct snd_soc_component *component,
+				struct snd_soc_jack *jack);
+
+#endif
diff --git a/sound/soc/codecs/twl4030.c b/sound/soc/codecs/twl4030.c
index b6b0cb399599..27f3b21effb2 100644
--- a/sound/soc/codecs/twl4030.c
+++ b/sound/soc/codecs/twl4030.c
@@ -2177,8 +2177,6 @@ static int twl4030_soc_remove(struct snd_soc_codec *codec)
 	struct twl4030_priv *twl4030 = snd_soc_codec_get_drvdata(codec);
 	struct twl4030_codec_data *pdata = twl4030->pdata;
 
-	twl4030_set_bias_level(codec, SND_SOC_BIAS_OFF);
-
 	if (pdata && pdata->hs_extmute && gpio_is_valid(pdata->hs_extmute_gpio))
 		gpio_free(pdata->hs_extmute_gpio);
 
diff --git a/sound/soc/codecs/twl6040.c b/sound/soc/codecs/twl6040.c
index 0f6067f04e29..5ff2b1e4638e 100644
--- a/sound/soc/codecs/twl6040.c
+++ b/sound/soc/codecs/twl6040.c
@@ -1095,25 +1095,6 @@ static struct snd_soc_dai_driver twl6040_dai[] = {
 },
 };
 
-#ifdef CONFIG_PM
-static int twl6040_suspend(struct snd_soc_codec *codec)
-{
-	twl6040_set_bias_level(codec, SND_SOC_BIAS_OFF);
-
-	return 0;
-}
-
-static int twl6040_resume(struct snd_soc_codec *codec)
-{
-	twl6040_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
-
-	return 0;
-}
-#else
-#define twl6040_suspend NULL
-#define twl6040_resume NULL
-#endif
-
 static int twl6040_probe(struct snd_soc_codec *codec)
 {
 	struct twl6040_data *priv;
@@ -1160,7 +1141,6 @@ static int twl6040_remove(struct snd_soc_codec *codec)
 	struct twl6040_data *priv = snd_soc_codec_get_drvdata(codec);
 
 	free_irq(priv->plug_irq, codec);
-	twl6040_set_bias_level(codec, SND_SOC_BIAS_OFF);
 
 	return 0;
 }
@@ -1168,11 +1148,10 @@ static int twl6040_remove(struct snd_soc_codec *codec)
 static struct snd_soc_codec_driver soc_codec_dev_twl6040 = {
 	.probe = twl6040_probe,
 	.remove = twl6040_remove,
-	.suspend = twl6040_suspend,
-	.resume = twl6040_resume,
 	.read = twl6040_read,
 	.write = twl6040_write,
 	.set_bias_level = twl6040_set_bias_level,
+	.suspend_bias_off = true,
 	.ignore_pmdown_time = true,
 
 	.controls = twl6040_snd_controls,
diff --git a/sound/soc/codecs/uda134x.c b/sound/soc/codecs/uda134x.c
index 32b2f78aa62c..4056260a502e 100644
--- a/sound/soc/codecs/uda134x.c
+++ b/sound/soc/codecs/uda134x.c
@@ -518,11 +518,6 @@ static int uda134x_soc_probe(struct snd_soc_codec *codec)
 
 	uda134x_reset(codec);
 
-	if (pd->is_powered_on_standby)
-		uda134x_set_bias_level(codec, SND_SOC_BIAS_ON);
-	else
-		uda134x_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
-
 	if (pd->model == UDA134X_UDA1341) {
 		widgets = uda1341_dapm_widgets;
 		num_widgets = ARRAY_SIZE(uda1341_dapm_widgets);
@@ -574,44 +569,21 @@ static int uda134x_soc_remove(struct snd_soc_codec *codec)
 {
 	struct uda134x_priv *uda134x = snd_soc_codec_get_drvdata(codec);
 
-	uda134x_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
-	uda134x_set_bias_level(codec, SND_SOC_BIAS_OFF);
-
 	kfree(uda134x);
 	return 0;
 }
 
-#if defined(CONFIG_PM)
-static int uda134x_soc_suspend(struct snd_soc_codec *codec)
-{
-	uda134x_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
-	uda134x_set_bias_level(codec, SND_SOC_BIAS_OFF);
-	return 0;
-}
-
-static int uda134x_soc_resume(struct snd_soc_codec *codec)
-{
-	uda134x_set_bias_level(codec, SND_SOC_BIAS_PREPARE);
-	uda134x_set_bias_level(codec, SND_SOC_BIAS_ON);
-	return 0;
-}
-#else
-#define uda134x_soc_suspend NULL
-#define uda134x_soc_resume NULL
-#endif /* CONFIG_PM */
-
 static struct snd_soc_codec_driver soc_codec_dev_uda134x = {
 	.probe =        uda134x_soc_probe,
 	.remove =       uda134x_soc_remove,
-	.suspend =      uda134x_soc_suspend,
-	.resume =       uda134x_soc_resume,
 	.reg_cache_size = sizeof(uda134x_reg),
 	.reg_word_size = sizeof(u8),
 	.reg_cache_default = uda134x_reg,
 	.reg_cache_step = 1,
 	.read = uda134x_read_reg_cache,
-	.write = uda134x_write,
 	.set_bias_level = uda134x_set_bias_level,
+	.suspend_bias_off = true,
+
 	.dapm_widgets = uda134x_dapm_widgets,
 	.num_dapm_widgets = ARRAY_SIZE(uda134x_dapm_widgets),
 	.dapm_routes = uda134x_dapm_routes,
diff --git a/sound/soc/codecs/uda1380.c b/sound/soc/codecs/uda1380.c
index e62e70781ec2..dc7778b6dd7f 100644
--- a/sound/soc/codecs/uda1380.c
+++ b/sound/soc/codecs/uda1380.c
@@ -693,18 +693,6 @@ static struct snd_soc_dai_driver uda1380_dai[] = {
 },
 };
 
-static int uda1380_suspend(struct snd_soc_codec *codec)
-{
-	uda1380_set_bias_level(codec, SND_SOC_BIAS_OFF);
-	return 0;
-}
-
-static int uda1380_resume(struct snd_soc_codec *codec)
-{
-	uda1380_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
-	return 0;
-}
-
 static int uda1380_probe(struct snd_soc_codec *codec)
 {
 	struct uda1380_platform_data *pdata =codec->dev->platform_data;
@@ -739,8 +727,6 @@ static int uda1380_probe(struct snd_soc_codec *codec)
 
 	INIT_WORK(&uda1380->work, uda1380_flush_work);
 
-	/* power on device */
-	uda1380_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
 	/* set clock input */
 	switch (pdata->dac_clk) {
 	case UDA1380_DAC_CLK_SYSCLK:
@@ -766,8 +752,6 @@ static int uda1380_remove(struct snd_soc_codec *codec)
 {
 	struct uda1380_platform_data *pdata =codec->dev->platform_data;
 
-	uda1380_set_bias_level(codec, SND_SOC_BIAS_OFF);
-
 	gpio_free(pdata->gpio_reset);
 	gpio_free(pdata->gpio_power);
 
@@ -777,11 +761,11 @@ static int uda1380_remove(struct snd_soc_codec *codec)
 static struct snd_soc_codec_driver soc_codec_dev_uda1380 = {
 	.probe =	uda1380_probe,
 	.remove =	uda1380_remove,
-	.suspend =	uda1380_suspend,
-	.resume =	uda1380_resume,
 	.read =		uda1380_read_reg_cache,
 	.write =	uda1380_write,
 	.set_bias_level = uda1380_set_bias_level,
+	.suspend_bias_off = true,
+
 	.reg_cache_size = ARRAY_SIZE(uda1380_reg),
 	.reg_word_size = sizeof(u16),
 	.reg_cache_default = uda1380_reg,
diff --git a/sound/soc/codecs/wl1273.c b/sound/soc/codecs/wl1273.c
index f3d4e88d0b7b..00aea4100bb3 100644
--- a/sound/soc/codecs/wl1273.c
+++ b/sound/soc/codecs/wl1273.c
@@ -452,7 +452,6 @@ static int wl1273_probe(struct snd_soc_codec *codec)
 {
 	struct wl1273_core **core = codec->dev->platform_data;
 	struct wl1273_priv *wl1273;
-	int r;
 
 	dev_dbg(codec->dev, "%s.\n", __func__);
 
@@ -470,12 +469,7 @@ static int wl1273_probe(struct snd_soc_codec *codec)
 
 	snd_soc_codec_set_drvdata(codec, wl1273);
 
-	r = snd_soc_add_codec_controls(codec, wl1273_controls,
-				 ARRAY_SIZE(wl1273_controls));
-	if (r)
-		kfree(wl1273);
-
-	return r;
+	return 0;
 }
 
 static int wl1273_remove(struct snd_soc_codec *codec)
@@ -492,6 +486,8 @@ static struct snd_soc_codec_driver soc_codec_dev_wl1273 = {
 	.probe = wl1273_probe,
 	.remove = wl1273_remove,
 
+	.controls = wl1273_controls,
+	.num_controls = ARRAY_SIZE(wl1273_controls),
 	.dapm_widgets = wl1273_dapm_widgets,
 	.num_dapm_widgets = ARRAY_SIZE(wl1273_dapm_widgets),
 	.dapm_routes = wl1273_dapm_routes,
diff --git a/sound/soc/codecs/wm5102.c b/sound/soc/codecs/wm5102.c
index f60234962527..d78fb8dffc8c 100644
--- a/sound/soc/codecs/wm5102.c
+++ b/sound/soc/codecs/wm5102.c
@@ -619,10 +619,10 @@ static int wm5102_out_comp_coeff_get(struct snd_kcontrol *kcontrol,
 	struct arizona *arizona = dev_get_drvdata(codec->dev->parent);
 	uint16_t data;
 
-	mutex_lock(&codec->mutex);
+	mutex_lock(&arizona->dac_comp_lock);
 	data = cpu_to_be16(arizona->dac_comp_coeff);
 	memcpy(ucontrol->value.bytes.data, &data, sizeof(data));
-	mutex_unlock(&codec->mutex);
+	mutex_unlock(&arizona->dac_comp_lock);
 
 	return 0;
 }
@@ -633,11 +633,11 @@ static int wm5102_out_comp_coeff_put(struct snd_kcontrol *kcontrol,
 	struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
 	struct arizona *arizona = dev_get_drvdata(codec->dev->parent);
 
-	mutex_lock(&codec->mutex);
+	mutex_lock(&arizona->dac_comp_lock);
 	memcpy(&arizona->dac_comp_coeff, ucontrol->value.bytes.data,
 	       sizeof(arizona->dac_comp_coeff));
 	arizona->dac_comp_coeff = be16_to_cpu(arizona->dac_comp_coeff);
-	mutex_unlock(&codec->mutex);
+	mutex_unlock(&arizona->dac_comp_lock);
 
 	return 0;
 }
@@ -648,9 +648,9 @@ static int wm5102_out_comp_switch_get(struct snd_kcontrol *kcontrol,
 	struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
 	struct arizona *arizona = dev_get_drvdata(codec->dev->parent);
 
-	mutex_lock(&codec->mutex);
+	mutex_lock(&arizona->dac_comp_lock);
 	ucontrol->value.integer.value[0] = arizona->dac_comp_enabled;
-	mutex_unlock(&codec->mutex);
+	mutex_unlock(&arizona->dac_comp_lock);
 
 	return 0;
 }
@@ -661,9 +661,9 @@ static int wm5102_out_comp_switch_put(struct snd_kcontrol *kcontrol,
 	struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
 	struct arizona *arizona = dev_get_drvdata(codec->dev->parent);
 
-	mutex_lock(&codec->mutex);
+	mutex_lock(&arizona->dac_comp_lock);
 	arizona->dac_comp_enabled = ucontrol->value.integer.value[0];
-	mutex_unlock(&codec->mutex);
+	mutex_unlock(&arizona->dac_comp_lock);
 
 	return 0;
 }
@@ -1900,6 +1900,8 @@ static int wm5102_probe(struct platform_device *pdev)
 		return -ENOMEM;
 	platform_set_drvdata(pdev, wm5102);
 
+	mutex_init(&arizona->dac_comp_lock);
+
 	wm5102->core.arizona = arizona;
 	wm5102->core.num_inputs = 6;
 
diff --git a/sound/soc/codecs/wm8350.c b/sound/soc/codecs/wm8350.c
index 628ec774cf22..87f664b9cc7d 100644
--- a/sound/soc/codecs/wm8350.c
+++ b/sound/soc/codecs/wm8350.c
@@ -1242,19 +1242,6 @@ static int wm8350_set_bias_level(struct snd_soc_codec *codec,
 	return 0;
 }
 
-static int wm8350_suspend(struct snd_soc_codec *codec)
-{
-	wm8350_set_bias_level(codec, SND_SOC_BIAS_OFF);
-	return 0;
-}
-
-static int wm8350_resume(struct snd_soc_codec *codec)
-{
-	wm8350_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
-
-	return 0;
-}
-
 static void wm8350_hp_work(struct wm8350_data *priv,
 			   struct wm8350_jack_data *jack,
 			   u16 mask)
@@ -1565,9 +1552,6 @@ static  int wm8350_codec_probe(struct snd_soc_codec *codec)
 	wm8350_register_irq(wm8350, WM8350_IRQ_CODEC_MICD,
 			    wm8350_mic_handler, 0, "Microphone detect", priv);
 
-
-	wm8350_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
-
 	return 0;
 }
 
@@ -1596,8 +1580,6 @@ static int  wm8350_codec_remove(struct snd_soc_codec *codec)
 	 * wait for its completion */
 	flush_delayed_work(&codec->dapm.delayed_work);
 
-	wm8350_set_bias_level(codec, SND_SOC_BIAS_OFF);
-
 	wm8350_clear_bits(wm8350, WM8350_POWER_MGMT_5, WM8350_CODEC_ENA);
 
 	return 0;
@@ -1613,10 +1595,9 @@ static struct regmap *wm8350_get_regmap(struct device *dev)
 static struct snd_soc_codec_driver soc_codec_dev_wm8350 = {
 	.probe =	wm8350_codec_probe,
 	.remove =	wm8350_codec_remove,
-	.suspend = 	wm8350_suspend,
-	.resume =	wm8350_resume,
 	.get_regmap =	wm8350_get_regmap,
 	.set_bias_level = wm8350_set_bias_level,
+	.suspend_bias_off = true,
 
 	.controls = wm8350_snd_controls,
 	.num_controls = ARRAY_SIZE(wm8350_snd_controls),
diff --git a/sound/soc/codecs/wm8400.c b/sound/soc/codecs/wm8400.c
index 72471bef2e9a..385894f6e264 100644
--- a/sound/soc/codecs/wm8400.c
+++ b/sound/soc/codecs/wm8400.c
@@ -58,12 +58,10 @@ static struct regulator_bulk_data power[] = {
 
 /* codec private data */
 struct wm8400_priv {
-	struct snd_soc_codec *codec;
 	struct wm8400 *wm8400;
 	u16 fake_register;
 	unsigned int sysclk;
 	unsigned int pcmclk;
-	struct work_struct work;
 	int fll_in, fll_out;
 };
 
@@ -1278,30 +1276,6 @@ static struct snd_soc_dai_driver wm8400_dai = {
 	.ops = &wm8400_dai_ops,
 };
 
-static int wm8400_suspend(struct snd_soc_codec *codec)
-{
-	wm8400_set_bias_level(codec, SND_SOC_BIAS_OFF);
-
-	return 0;
-}
-
-static int wm8400_resume(struct snd_soc_codec *codec)
-{
-	wm8400_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
-
-	return 0;
-}
-
-static void wm8400_probe_deferred(struct work_struct *work)
-{
-	struct wm8400_priv *priv = container_of(work, struct wm8400_priv,
-						work);
-	struct snd_soc_codec *codec = priv->codec;
-
-	/* charge output caps */
-	wm8400_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
-}
-
 static int wm8400_codec_probe(struct snd_soc_codec *codec)
 {
 	struct wm8400 *wm8400 = dev_get_platdata(codec->dev);
@@ -1316,7 +1290,6 @@ static int wm8400_codec_probe(struct snd_soc_codec *codec)
 
 	snd_soc_codec_set_drvdata(codec, priv);
 	priv->wm8400 = wm8400;
-	priv->codec = codec;
 
 	ret = devm_regulator_bulk_get(wm8400->dev,
 				 ARRAY_SIZE(power), &power[0]);
@@ -1325,8 +1298,6 @@ static int wm8400_codec_probe(struct snd_soc_codec *codec)
 		return ret;
 	}
 
-	INIT_WORK(&priv->work, wm8400_probe_deferred);
-
 	wm8400_codec_reset(codec);
 
 	reg = snd_soc_read(codec, WM8400_POWER_MANAGEMENT_1);
@@ -1343,8 +1314,6 @@ static int wm8400_codec_probe(struct snd_soc_codec *codec)
 	snd_soc_write(codec, WM8400_LEFT_OUTPUT_VOLUME, 0x50 | (1<<8));
 	snd_soc_write(codec, WM8400_RIGHT_OUTPUT_VOLUME, 0x50 | (1<<8));
 
-	if (!schedule_work(&priv->work))
-		return -EINVAL;
 	return 0;
 }
 
@@ -1369,10 +1338,9 @@ static struct regmap *wm8400_get_regmap(struct device *dev)
 static struct snd_soc_codec_driver soc_codec_dev_wm8400 = {
 	.probe =	wm8400_codec_probe,
 	.remove =	wm8400_codec_remove,
-	.suspend =	wm8400_suspend,
-	.resume =	wm8400_resume,
 	.get_regmap =	wm8400_get_regmap,
 	.set_bias_level = wm8400_set_bias_level,
+	.suspend_bias_off = true,
 
 	.controls = wm8400_snd_controls,
 	.num_controls = ARRAY_SIZE(wm8400_snd_controls),
diff --git a/sound/soc/codecs/wm8510.c b/sound/soc/codecs/wm8510.c
index e11127f9069e..8736ad094b24 100644
--- a/sound/soc/codecs/wm8510.c
+++ b/sound/soc/codecs/wm8510.c
@@ -575,41 +575,17 @@ static struct snd_soc_dai_driver wm8510_dai = {
 	.symmetric_rates = 1,
 };
 
-static int wm8510_suspend(struct snd_soc_codec *codec)
-{
-	wm8510_set_bias_level(codec, SND_SOC_BIAS_OFF);
-	return 0;
-}
-
-static int wm8510_resume(struct snd_soc_codec *codec)
-{
-	wm8510_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
-	return 0;
-}
-
 static int wm8510_probe(struct snd_soc_codec *codec)
 {
 	wm8510_reset(codec);
 
-	/* power on device */
-	wm8510_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
-
-	return 0;
-}
-
-/* power down chip */
-static int wm8510_remove(struct snd_soc_codec *codec)
-{
-	wm8510_set_bias_level(codec, SND_SOC_BIAS_OFF);
 	return 0;
 }
 
 static struct snd_soc_codec_driver soc_codec_dev_wm8510 = {
 	.probe =	wm8510_probe,
-	.remove =	wm8510_remove,
-	.suspend =	wm8510_suspend,
-	.resume =	wm8510_resume,
 	.set_bias_level = wm8510_set_bias_level,
+	.suspend_bias_off = true,
 
 	.controls = wm8510_snd_controls,
 	.num_controls = ARRAY_SIZE(wm8510_snd_controls),
diff --git a/sound/soc/codecs/wm8523.c b/sound/soc/codecs/wm8523.c
index ec1f5740dbd0..b1cc94f5fc4b 100644
--- a/sound/soc/codecs/wm8523.c
+++ b/sound/soc/codecs/wm8523.c
@@ -372,23 +372,6 @@ static struct snd_soc_dai_driver wm8523_dai = {
 	.ops = &wm8523_dai_ops,
 };
 
-#ifdef CONFIG_PM
-static int wm8523_suspend(struct snd_soc_codec *codec)
-{
-	wm8523_set_bias_level(codec, SND_SOC_BIAS_OFF);
-	return 0;
-}
-
-static int wm8523_resume(struct snd_soc_codec *codec)
-{
-	wm8523_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
-	return 0;
-}
-#else
-#define wm8523_suspend NULL
-#define wm8523_resume NULL
-#endif
-
 static int wm8523_probe(struct snd_soc_codec *codec)
 {
 	struct wm8523_priv *wm8523 = snd_soc_codec_get_drvdata(codec);
@@ -402,23 +385,13 @@ static int wm8523_probe(struct snd_soc_codec *codec)
 			    WM8523_DACR_VU, WM8523_DACR_VU);
 	snd_soc_update_bits(codec, WM8523_DAC_CTRL3, WM8523_ZC, WM8523_ZC);
 
-	wm8523_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
-
-	return 0;
-}
-
-static int wm8523_remove(struct snd_soc_codec *codec)
-{
-	wm8523_set_bias_level(codec, SND_SOC_BIAS_OFF);
 	return 0;
 }
 
 static struct snd_soc_codec_driver soc_codec_dev_wm8523 = {
 	.probe =	wm8523_probe,
-	.remove =	wm8523_remove,
-	.suspend =	wm8523_suspend,
-	.resume =	wm8523_resume,
 	.set_bias_level = wm8523_set_bias_level,
+	.suspend_bias_off = true,
 
 	.controls = wm8523_controls,
 	.num_controls = ARRAY_SIZE(wm8523_controls),
diff --git a/sound/soc/codecs/wm8580.c b/sound/soc/codecs/wm8580.c
index 911605ee25b0..0a887c5ec83a 100644
--- a/sound/soc/codecs/wm8580.c
+++ b/sound/soc/codecs/wm8580.c
@@ -882,8 +882,6 @@ static int wm8580_probe(struct snd_soc_codec *codec)
 		goto err_regulator_enable;
 	}
 
-	wm8580_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
-
 	return 0;
 
 err_regulator_enable:
@@ -897,8 +895,6 @@ static int wm8580_remove(struct snd_soc_codec *codec)
 {
 	struct wm8580_priv *wm8580 = snd_soc_codec_get_drvdata(codec);
 
-	wm8580_set_bias_level(codec, SND_SOC_BIAS_OFF);
-
 	regulator_bulk_disable(ARRAY_SIZE(wm8580->supplies), wm8580->supplies);
 
 	return 0;
diff --git a/sound/soc/codecs/wm8711.c b/sound/soc/codecs/wm8711.c
index 32187e739b4f..121e46d53779 100644
--- a/sound/soc/codecs/wm8711.c
+++ b/sound/soc/codecs/wm8711.c
@@ -350,19 +350,6 @@ static struct snd_soc_dai_driver wm8711_dai = {
 	.ops = &wm8711_ops,
 };
 
-static int wm8711_suspend(struct snd_soc_codec *codec)
-{
-	snd_soc_write(codec, WM8711_ACTIVE, 0x0);
-	wm8711_set_bias_level(codec, SND_SOC_BIAS_OFF);
-	return 0;
-}
-
-static int wm8711_resume(struct snd_soc_codec *codec)
-{
-	wm8711_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
-	return 0;
-}
-
 static int wm8711_probe(struct snd_soc_codec *codec)
 {
 	int ret;
@@ -373,8 +360,6 @@ static int wm8711_probe(struct snd_soc_codec *codec)
 		return ret;
 	}
 
-	wm8711_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
-
 	/* Latch the update bits */
 	snd_soc_update_bits(codec, WM8711_LOUT1V, 0x0100, 0x0100);
 	snd_soc_update_bits(codec, WM8711_ROUT1V, 0x0100, 0x0100);
@@ -383,19 +368,11 @@ static int wm8711_probe(struct snd_soc_codec *codec)
 
 }
 
-/* power down chip */
-static int wm8711_remove(struct snd_soc_codec *codec)
-{
-	wm8711_set_bias_level(codec, SND_SOC_BIAS_OFF);
-	return 0;
-}
-
 static struct snd_soc_codec_driver soc_codec_dev_wm8711 = {
 	.probe =	wm8711_probe,
-	.remove =	wm8711_remove,
-	.suspend =	wm8711_suspend,
-	.resume =	wm8711_resume,
 	.set_bias_level = wm8711_set_bias_level,
+	.suspend_bias_off = true,
+
 	.controls = wm8711_snd_controls,
 	.num_controls = ARRAY_SIZE(wm8711_snd_controls),
 	.dapm_widgets = wm8711_dapm_widgets,
diff --git a/sound/soc/codecs/wm8728.c b/sound/soc/codecs/wm8728.c
index 38ff826f589a..55c7fb4fc786 100644
--- a/sound/soc/codecs/wm8728.c
+++ b/sound/soc/codecs/wm8728.c
@@ -212,40 +212,10 @@ static struct snd_soc_dai_driver wm8728_dai = {
 	.ops = &wm8728_dai_ops,
 };
 
-static int wm8728_suspend(struct snd_soc_codec *codec)
-{
-	wm8728_set_bias_level(codec, SND_SOC_BIAS_OFF);
-
-	return 0;
-}
-
-static int wm8728_resume(struct snd_soc_codec *codec)
-{
-	wm8728_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
-
-	return 0;
-}
-
-static int wm8728_probe(struct snd_soc_codec *codec)
-{
-	/* power on device */
-	wm8728_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
-
-	return 0;
-}
-
-static int wm8728_remove(struct snd_soc_codec *codec)
-{
-	wm8728_set_bias_level(codec, SND_SOC_BIAS_OFF);
-	return 0;
-}
-
 static struct snd_soc_codec_driver soc_codec_dev_wm8728 = {
-	.probe =	wm8728_probe,
-	.remove =	wm8728_remove,
-	.suspend =	wm8728_suspend,
-	.resume =	wm8728_resume,
 	.set_bias_level = wm8728_set_bias_level,
+	.suspend_bias_off = true,
+
 	.controls = wm8728_snd_controls,
 	.num_controls = ARRAY_SIZE(wm8728_snd_controls),
 	.dapm_widgets = wm8728_dapm_widgets,
diff --git a/sound/soc/codecs/wm8731.c b/sound/soc/codecs/wm8731.c
index eebb3280bfad..b9211b42f6e9 100644
--- a/sound/soc/codecs/wm8731.c
+++ b/sound/soc/codecs/wm8731.c
@@ -24,6 +24,7 @@
 #include <linux/regulator/consumer.h>
 #include <linux/spi/spi.h>
 #include <linux/of_device.h>
+#include <linux/mutex.h>
 #include <sound/core.h>
 #include <sound/pcm.h>
 #include <sound/pcm_params.h>
@@ -50,6 +51,8 @@ struct wm8731_priv {
 	int sysclk_type;
 	int playback_fs;
 	bool deemph;
+
+	struct mutex lock;
 };
 
 
@@ -138,7 +141,7 @@ static int wm8731_put_deemph(struct snd_kcontrol *kcontrol,
 	if (deemph > 1)
 		return -EINVAL;
 
-	mutex_lock(&codec->mutex);
+	mutex_lock(&wm8731->lock);
 	if (wm8731->deemph != deemph) {
 		wm8731->deemph = deemph;
 
@@ -146,7 +149,7 @@ static int wm8731_put_deemph(struct snd_kcontrol *kcontrol,
 
 		ret = 1;
 	}
-	mutex_unlock(&codec->mutex);
+	mutex_unlock(&wm8731->lock);
 
 	return ret;
 }
@@ -559,25 +562,6 @@ static struct snd_soc_dai_driver wm8731_dai = {
 	.symmetric_rates = 1,
 };
 
-#ifdef CONFIG_PM
-static int wm8731_suspend(struct snd_soc_codec *codec)
-{
-	wm8731_set_bias_level(codec, SND_SOC_BIAS_OFF);
-
-	return 0;
-}
-
-static int wm8731_resume(struct snd_soc_codec *codec)
-{
-	wm8731_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
-
-	return 0;
-}
-#else
-#define wm8731_suspend NULL
-#define wm8731_resume NULL
-#endif
-
 static int wm8731_probe(struct snd_soc_codec *codec)
 {
 	struct wm8731_priv *wm8731 = snd_soc_codec_get_drvdata(codec);
@@ -633,8 +617,6 @@ static int wm8731_remove(struct snd_soc_codec *codec)
 {
 	struct wm8731_priv *wm8731 = snd_soc_codec_get_drvdata(codec);
 
-	wm8731_set_bias_level(codec, SND_SOC_BIAS_OFF);
-
 	regulator_bulk_disable(ARRAY_SIZE(wm8731->supplies), wm8731->supplies);
 
 	return 0;
@@ -643,9 +625,9 @@ static int wm8731_remove(struct snd_soc_codec *codec)
 static struct snd_soc_codec_driver soc_codec_dev_wm8731 = {
 	.probe =	wm8731_probe,
 	.remove =	wm8731_remove,
-	.suspend =	wm8731_suspend,
-	.resume =	wm8731_resume,
 	.set_bias_level = wm8731_set_bias_level,
+	.suspend_bias_off = true,
+
 	.dapm_widgets = wm8731_dapm_widgets,
 	.num_dapm_widgets = ARRAY_SIZE(wm8731_dapm_widgets),
 	.dapm_routes = wm8731_intercon,
@@ -680,11 +662,12 @@ static int wm8731_spi_probe(struct spi_device *spi)
 	struct wm8731_priv *wm8731;
 	int ret;
 
-	wm8731 = devm_kzalloc(&spi->dev, sizeof(struct wm8731_priv),
-			      GFP_KERNEL);
+	wm8731 = devm_kzalloc(&spi->dev, sizeof(*wm8731), GFP_KERNEL);
 	if (wm8731 == NULL)
 		return -ENOMEM;
 
+	mutex_init(&wm8731->lock);
+
 	wm8731->regmap = devm_regmap_init_spi(spi, &wm8731_regmap);
 	if (IS_ERR(wm8731->regmap)) {
 		ret = PTR_ERR(wm8731->regmap);
diff --git a/sound/soc/codecs/wm8737.c b/sound/soc/codecs/wm8737.c
index 744a422ecb05..ada9ac1ba2c6 100644
--- a/sound/soc/codecs/wm8737.c
+++ b/sound/soc/codecs/wm8737.c
@@ -277,17 +277,6 @@ static const struct snd_soc_dapm_route intercon[] = {
 	{ "AIF", NULL, "ADCR" },
 };
 
-static int wm8737_add_widgets(struct snd_soc_codec *codec)
-{
-	struct snd_soc_dapm_context *dapm = &codec->dapm;
-
-	snd_soc_dapm_new_controls(dapm, wm8737_dapm_widgets,
-				  ARRAY_SIZE(wm8737_dapm_widgets));
-	snd_soc_dapm_add_routes(dapm, intercon, ARRAY_SIZE(intercon));
-
-	return 0;
-}
-
 /* codec mclk clock divider coefficients */
 static const struct {
 	u32 mclk;
@@ -548,23 +537,6 @@ static struct snd_soc_dai_driver wm8737_dai = {
 	.ops = &wm8737_dai_ops,
 };
 
-#ifdef CONFIG_PM
-static int wm8737_suspend(struct snd_soc_codec *codec)
-{
-	wm8737_set_bias_level(codec, SND_SOC_BIAS_OFF);
-	return 0;
-}
-
-static int wm8737_resume(struct snd_soc_codec *codec)
-{
-	wm8737_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
-	return 0;
-}
-#else
-#define wm8737_suspend NULL
-#define wm8737_resume NULL
-#endif
-
 static int wm8737_probe(struct snd_soc_codec *codec)
 {
 	struct wm8737_priv *wm8737 = snd_soc_codec_get_drvdata(codec);
@@ -593,10 +565,6 @@ static int wm8737_probe(struct snd_soc_codec *codec)
 	/* Bias level configuration will have done an extra enable */
 	regulator_bulk_disable(ARRAY_SIZE(wm8737->supplies), wm8737->supplies);
 
-	snd_soc_add_codec_controls(codec, wm8737_snd_controls,
-			     ARRAY_SIZE(wm8737_snd_controls));
-	wm8737_add_widgets(codec);
-
 	return 0;
 
 err_enable:
@@ -605,18 +573,17 @@ err_get:
 	return ret;
 }
 
-static int wm8737_remove(struct snd_soc_codec *codec)
-{
-	wm8737_set_bias_level(codec, SND_SOC_BIAS_OFF);
-	return 0;
-}
-
 static struct snd_soc_codec_driver soc_codec_dev_wm8737 = {
 	.probe		= wm8737_probe,
-	.remove		= wm8737_remove,
-	.suspend	= wm8737_suspend,
-	.resume		= wm8737_resume,
 	.set_bias_level = wm8737_set_bias_level,
+	.suspend_bias_off = true,
+
+	.controls = wm8737_snd_controls,
+	.num_controls = ARRAY_SIZE(wm8737_snd_controls),
+	.dapm_widgets = wm8737_dapm_widgets,
+	.num_dapm_widgets = ARRAY_SIZE(wm8737_dapm_widgets),
+	.dapm_routes = intercon,
+	.num_dapm_routes = ARRAY_SIZE(intercon),
 };
 
 static const struct of_device_id wm8737_of_match[] = {
diff --git a/sound/soc/codecs/wm8750.c b/sound/soc/codecs/wm8750.c
index 67653a2db223..f6847fdd6ddd 100644
--- a/sound/soc/codecs/wm8750.c
+++ b/sound/soc/codecs/wm8750.c
@@ -686,18 +686,6 @@ static struct snd_soc_dai_driver wm8750_dai = {
 	.ops = &wm8750_dai_ops,
 };
 
-static int wm8750_suspend(struct snd_soc_codec *codec)
-{
-	wm8750_set_bias_level(codec, SND_SOC_BIAS_OFF);
-	return 0;
-}
-
-static int wm8750_resume(struct snd_soc_codec *codec)
-{
-	wm8750_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
-	return 0;
-}
-
 static int wm8750_probe(struct snd_soc_codec *codec)
 {
 	int ret;
@@ -708,9 +696,6 @@ static int wm8750_probe(struct snd_soc_codec *codec)
 		return ret;
 	}
 
-	/* charge output caps */
-	wm8750_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
-
 	/* set the update bits */
 	snd_soc_update_bits(codec, WM8750_LDAC, 0x0100, 0x0100);
 	snd_soc_update_bits(codec, WM8750_RDAC, 0x0100, 0x0100);
@@ -724,18 +709,10 @@ static int wm8750_probe(struct snd_soc_codec *codec)
 	return ret;
 }
 
-static int wm8750_remove(struct snd_soc_codec *codec)
-{
-	wm8750_set_bias_level(codec, SND_SOC_BIAS_OFF);
-	return 0;
-}
-
 static struct snd_soc_codec_driver soc_codec_dev_wm8750 = {
 	.probe =	wm8750_probe,
-	.remove =	wm8750_remove,
-	.suspend =	wm8750_suspend,
-	.resume =	wm8750_resume,
 	.set_bias_level = wm8750_set_bias_level,
+	.suspend_bias_off = true,
 
 	.controls = wm8750_snd_controls,
 	.num_controls = ARRAY_SIZE(wm8750_snd_controls),
diff --git a/sound/soc/codecs/wm8776.c b/sound/soc/codecs/wm8776.c
index 70952ceb278b..c13050b77931 100644
--- a/sound/soc/codecs/wm8776.c
+++ b/sound/soc/codecs/wm8776.c
@@ -408,24 +408,6 @@ static struct snd_soc_dai_driver wm8776_dai[] = {
 	},
 };
 
-#ifdef CONFIG_PM
-static int wm8776_suspend(struct snd_soc_codec *codec)
-{
-	wm8776_set_bias_level(codec, SND_SOC_BIAS_OFF);
-
-	return 0;
-}
-
-static int wm8776_resume(struct snd_soc_codec *codec)
-{
-	wm8776_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
-	return 0;
-}
-#else
-#define wm8776_suspend NULL
-#define wm8776_resume NULL
-#endif
-
 static int wm8776_probe(struct snd_soc_codec *codec)
 {
 	int ret = 0;
@@ -436,8 +418,6 @@ static int wm8776_probe(struct snd_soc_codec *codec)
 		return ret;
 	}
 
-	wm8776_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
-
 	/* Latch the update bits; right channel only since we always
 	 * update both. */
 	snd_soc_update_bits(codec, WM8776_HPRVOL, 0x100, 0x100);
@@ -446,19 +426,10 @@ static int wm8776_probe(struct snd_soc_codec *codec)
 	return ret;
 }
 
-/* power down chip */
-static int wm8776_remove(struct snd_soc_codec *codec)
-{
-	wm8776_set_bias_level(codec, SND_SOC_BIAS_OFF);
-	return 0;
-}
-
 static struct snd_soc_codec_driver soc_codec_dev_wm8776 = {
 	.probe = 	wm8776_probe,
-	.remove = 	wm8776_remove,
-	.suspend = 	wm8776_suspend,
-	.resume =	wm8776_resume,
 	.set_bias_level = wm8776_set_bias_level,
+	.suspend_bias_off = true,
 
 	.controls = wm8776_snd_controls,
 	.num_controls = ARRAY_SIZE(wm8776_snd_controls),
diff --git a/sound/soc/codecs/wm8804.c b/sound/soc/codecs/wm8804.c
index 3addc5fe5cb2..1315f7642503 100644
--- a/sound/soc/codecs/wm8804.c
+++ b/sound/soc/codecs/wm8804.c
@@ -524,7 +524,6 @@ static int wm8804_remove(struct snd_soc_codec *codec)
 	int i;
 
 	wm8804 = snd_soc_codec_get_drvdata(codec);
-	wm8804_set_bias_level(codec, SND_SOC_BIAS_OFF);
 
 	for (i = 0; i < ARRAY_SIZE(wm8804->supplies); ++i)
 		regulator_unregister_notifier(wm8804->supplies[i].consumer,
@@ -606,8 +605,6 @@ static int wm8804_probe(struct snd_soc_codec *codec)
 		goto err_reg_enable;
 	}
 
-	wm8804_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
-
 	return 0;
 
 err_reg_enable:
diff --git a/sound/soc/codecs/wm8900.c b/sound/soc/codecs/wm8900.c
index 44a5f1511f0f..3a0d4b7d692f 100644
--- a/sound/soc/codecs/wm8900.c
+++ b/sound/soc/codecs/wm8900.c
@@ -1209,16 +1209,8 @@ static int wm8900_probe(struct snd_soc_codec *codec)
 	return 0;
 }
 
-/* power down chip */
-static int wm8900_remove(struct snd_soc_codec *codec)
-{
-	wm8900_set_bias_level(codec, SND_SOC_BIAS_OFF);
-	return 0;
-}
-
 static struct snd_soc_codec_driver soc_codec_dev_wm8900 = {
 	.probe =	wm8900_probe,
-	.remove =	wm8900_remove,
 	.suspend =	wm8900_suspend,
 	.resume =	wm8900_resume,
 	.set_bias_level = wm8900_set_bias_level,
diff --git a/sound/soc/codecs/wm8903.c b/sound/soc/codecs/wm8903.c
index c038b3e04398..cc6b0ef98a34 100644
--- a/sound/soc/codecs/wm8903.c
+++ b/sound/soc/codecs/wm8903.c
@@ -26,6 +26,7 @@
 #include <linux/regmap.h>
 #include <linux/slab.h>
 #include <linux/irq.h>
+#include <linux/mutex.h>
 #include <sound/core.h>
 #include <sound/jack.h>
 #include <sound/pcm.h>
@@ -117,12 +118,12 @@ static const struct reg_default wm8903_reg_defaults[] = {
 struct wm8903_priv {
 	struct wm8903_platform_data *pdata;
 	struct device *dev;
-	struct snd_soc_codec *codec;
 	struct regmap *regmap;
 
 	int sysclk;
 	int irq;
 
+	struct mutex lock;
 	int fs;
 	int deemph;
 
@@ -457,7 +458,7 @@ static int wm8903_put_deemph(struct snd_kcontrol *kcontrol,
 	if (deemph > 1)
 		return -EINVAL;
 
-	mutex_lock(&codec->mutex);
+	mutex_lock(&wm8903->lock);
 	if (wm8903->deemph != deemph) {
 		wm8903->deemph = deemph;
 
@@ -465,7 +466,7 @@ static int wm8903_put_deemph(struct snd_kcontrol *kcontrol,
 
 		ret = 1;
 	}
-	mutex_unlock(&codec->mutex);
+	mutex_unlock(&wm8903->lock);
 
 	return ret;
 }
@@ -1757,21 +1758,12 @@ static struct snd_soc_dai_driver wm8903_dai = {
 	.symmetric_rates = 1,
 };
 
-static int wm8903_suspend(struct snd_soc_codec *codec)
-{
-	wm8903_set_bias_level(codec, SND_SOC_BIAS_OFF);
-
-	return 0;
-}
-
 static int wm8903_resume(struct snd_soc_codec *codec)
 {
 	struct wm8903_priv *wm8903 = snd_soc_codec_get_drvdata(codec);
 
 	regcache_sync(wm8903->regmap);
 
-	wm8903_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
-
 	return 0;
 }
 
@@ -1889,33 +1881,12 @@ static void wm8903_free_gpio(struct wm8903_priv *wm8903)
 }
 #endif
 
-static int wm8903_probe(struct snd_soc_codec *codec)
-{
-	struct wm8903_priv *wm8903 = snd_soc_codec_get_drvdata(codec);
-
-	wm8903->codec = codec;
-
-	/* power on device */
-	wm8903_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
-
-	return 0;
-}
-
-/* power down chip */
-static int wm8903_remove(struct snd_soc_codec *codec)
-{
-	wm8903_set_bias_level(codec, SND_SOC_BIAS_OFF);
-
-	return 0;
-}
-
 static struct snd_soc_codec_driver soc_codec_dev_wm8903 = {
-	.probe =	wm8903_probe,
-	.remove =	wm8903_remove,
-	.suspend =	wm8903_suspend,
 	.resume =	wm8903_resume,
 	.set_bias_level = wm8903_set_bias_level,
 	.seq_notifier = wm8903_seq_notifier,
+	.suspend_bias_off = true,
+
 	.controls = wm8903_snd_controls,
 	.num_controls = ARRAY_SIZE(wm8903_snd_controls),
 	.dapm_widgets = wm8903_dapm_widgets,
@@ -2023,6 +1994,8 @@ static int wm8903_i2c_probe(struct i2c_client *i2c,
 			      GFP_KERNEL);
 	if (wm8903 == NULL)
 		return -ENOMEM;
+
+	mutex_init(&wm8903->lock);
 	wm8903->dev = &i2c->dev;
 
 	wm8903->regmap = devm_regmap_init_i2c(i2c, &wm8903_regmap);
diff --git a/sound/soc/codecs/wm8940.c b/sound/soc/codecs/wm8940.c
index 52011043e54c..e4142b4309eb 100644
--- a/sound/soc/codecs/wm8940.c
+++ b/sound/soc/codecs/wm8940.c
@@ -695,17 +695,6 @@ static struct snd_soc_dai_driver wm8940_dai = {
 	.symmetric_rates = 1,
 };
 
-static int wm8940_suspend(struct snd_soc_codec *codec)
-{
-	return wm8940_set_bias_level(codec, SND_SOC_BIAS_OFF);
-}
-
-static int wm8940_resume(struct snd_soc_codec *codec)
-{
-	wm8940_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
-	return 0;
-}
-
 static int wm8940_probe(struct snd_soc_codec *codec)
 {
 	struct wm8940_setup_data *pdata = codec->dev->platform_data;
@@ -736,18 +725,11 @@ static int wm8940_probe(struct snd_soc_codec *codec)
 	return ret;
 }
 
-static int wm8940_remove(struct snd_soc_codec *codec)
-{
-	wm8940_set_bias_level(codec, SND_SOC_BIAS_OFF);
-	return 0;
-}
-
 static struct snd_soc_codec_driver soc_codec_dev_wm8940 = {
 	.probe =	wm8940_probe,
-	.remove =	wm8940_remove,
-	.suspend =	wm8940_suspend,
-	.resume =	wm8940_resume,
 	.set_bias_level = wm8940_set_bias_level,
+	.suspend_bias_off = true,
+
 	.controls =     wm8940_snd_controls,
 	.num_controls = ARRAY_SIZE(wm8940_snd_controls),
 	.dapm_widgets = wm8940_dapm_widgets,
diff --git a/sound/soc/codecs/wm8955.c b/sound/soc/codecs/wm8955.c
index 09d91d9dc4ee..1173f7fef5a7 100644
--- a/sound/soc/codecs/wm8955.c
+++ b/sound/soc/codecs/wm8955.c
@@ -866,29 +866,6 @@ static struct snd_soc_dai_driver wm8955_dai = {
 	.ops = &wm8955_dai_ops,
 };
 
-#ifdef CONFIG_PM
-static int wm8955_suspend(struct snd_soc_codec *codec)
-{
-	struct wm8955_priv *wm8955 = snd_soc_codec_get_drvdata(codec);
-
-	wm8955_set_bias_level(codec, SND_SOC_BIAS_OFF);
-
-	regcache_mark_dirty(wm8955->regmap);
-
-	return 0;
-}
-
-static int wm8955_resume(struct snd_soc_codec *codec)
-{
-	wm8955_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
-
-	return 0;
-}
-#else
-#define wm8955_suspend NULL
-#define wm8955_resume NULL
-#endif
-
 static int wm8955_probe(struct snd_soc_codec *codec)
 {
 	struct wm8955_priv *wm8955 = snd_soc_codec_get_drvdata(codec);
@@ -964,18 +941,10 @@ err_enable:
 	return ret;
 }
 
-static int wm8955_remove(struct snd_soc_codec *codec)
-{
-	wm8955_set_bias_level(codec, SND_SOC_BIAS_OFF);
-	return 0;
-}
-
 static struct snd_soc_codec_driver soc_codec_dev_wm8955 = {
 	.probe =	wm8955_probe,
-	.remove =	wm8955_remove,
-	.suspend =	wm8955_suspend,
-	.resume =	wm8955_resume,
 	.set_bias_level = wm8955_set_bias_level,
+	.suspend_bias_off = true,
 
 	.controls =	wm8955_snd_controls,
 	.num_controls = ARRAY_SIZE(wm8955_snd_controls),
diff --git a/sound/soc/codecs/wm8958-dsp2.c b/sound/soc/codecs/wm8958-dsp2.c
index 0dada7f0105e..3cbc82b33292 100644
--- a/sound/soc/codecs/wm8958-dsp2.c
+++ b/sound/soc/codecs/wm8958-dsp2.c
@@ -867,9 +867,9 @@ static void wm8958_enh_eq_loaded(const struct firmware *fw, void *context)
 	struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
 
 	if (fw && (wm8958_dsp2_fw(codec, "ENH_EQ", fw, true) == 0)) {
-		mutex_lock(&codec->mutex);
+		mutex_lock(&wm8994->fw_lock);
 		wm8994->enh_eq = fw;
-		mutex_unlock(&codec->mutex);
+		mutex_unlock(&wm8994->fw_lock);
 	}
 }
 
@@ -879,9 +879,9 @@ static void wm8958_mbc_vss_loaded(const struct firmware *fw, void *context)
 	struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
 
 	if (fw && (wm8958_dsp2_fw(codec, "MBC+VSS", fw, true) == 0)) {
-		mutex_lock(&codec->mutex);
+		mutex_lock(&wm8994->fw_lock);
 		wm8994->mbc_vss = fw;
-		mutex_unlock(&codec->mutex);
+		mutex_unlock(&wm8994->fw_lock);
 	}
 }
 
@@ -891,9 +891,9 @@ static void wm8958_mbc_loaded(const struct firmware *fw, void *context)
 	struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
 
 	if (fw && (wm8958_dsp2_fw(codec, "MBC", fw, true) == 0)) {
-		mutex_lock(&codec->mutex);
+		mutex_lock(&wm8994->fw_lock);
 		wm8994->mbc = fw;
-		mutex_unlock(&codec->mutex);
+		mutex_unlock(&wm8994->fw_lock);
 	}
 }
 
diff --git a/sound/soc/codecs/wm8960.c b/sound/soc/codecs/wm8960.c
index 4dc4e85116cd..031a1ae71d94 100644
--- a/sound/soc/codecs/wm8960.c
+++ b/sound/soc/codecs/wm8960.c
@@ -125,9 +125,10 @@ struct wm8960_priv {
 	struct snd_soc_dapm_widget *out3;
 	bool deemph;
 	int playback_fs;
+	struct wm8960_data pdata;
 };
 
-#define wm8960_reset(c)	snd_soc_write(c, WM8960_RESET, 0)
+#define wm8960_reset(c)	regmap_write(c, WM8960_RESET, 0)
 
 /* enumerated controls */
 static const char *wm8960_polarity[] = {"No Inversion", "Left Inverted",
@@ -440,8 +441,8 @@ static const struct snd_soc_dapm_route audio_paths_capless[] = {
 
 static int wm8960_add_widgets(struct snd_soc_codec *codec)
 {
-	struct wm8960_data *pdata = codec->dev->platform_data;
 	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_widget *w;
 
@@ -942,56 +943,15 @@ static struct snd_soc_dai_driver wm8960_dai = {
 	.symmetric_rates = 1,
 };
 
-static int wm8960_suspend(struct snd_soc_codec *codec)
-{
-	struct wm8960_priv *wm8960 = snd_soc_codec_get_drvdata(codec);
-
-	wm8960->set_bias_level(codec, SND_SOC_BIAS_OFF);
-	return 0;
-}
-
-static int wm8960_resume(struct snd_soc_codec *codec)
-{
-	struct wm8960_priv *wm8960 = snd_soc_codec_get_drvdata(codec);
-
-	wm8960->set_bias_level(codec, SND_SOC_BIAS_STANDBY);
-	return 0;
-}
-
 static int wm8960_probe(struct snd_soc_codec *codec)
 {
 	struct wm8960_priv *wm8960 = snd_soc_codec_get_drvdata(codec);
-	struct wm8960_data *pdata = dev_get_platdata(codec->dev);
-	int ret;
-
-	wm8960->set_bias_level = wm8960_set_bias_level_out3;
-
-	if (!pdata) {
-		dev_warn(codec->dev, "No platform data supplied\n");
-	} else {
-		if (pdata->capless)
-			wm8960->set_bias_level = wm8960_set_bias_level_capless;
-	}
-
-	ret = wm8960_reset(codec);
-	if (ret < 0) {
-		dev_err(codec->dev, "Failed to issue reset\n");
-		return ret;
-	}
-
-	wm8960->set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+	struct wm8960_data *pdata = &wm8960->pdata;
 
-	/* Latch the update bits */
-	snd_soc_update_bits(codec, WM8960_LINVOL, 0x100, 0x100);
-	snd_soc_update_bits(codec, WM8960_RINVOL, 0x100, 0x100);
-	snd_soc_update_bits(codec, WM8960_LADC, 0x100, 0x100);
-	snd_soc_update_bits(codec, WM8960_RADC, 0x100, 0x100);
-	snd_soc_update_bits(codec, WM8960_LDAC, 0x100, 0x100);
-	snd_soc_update_bits(codec, WM8960_RDAC, 0x100, 0x100);
-	snd_soc_update_bits(codec, WM8960_LOUT1, 0x100, 0x100);
-	snd_soc_update_bits(codec, WM8960_ROUT1, 0x100, 0x100);
-	snd_soc_update_bits(codec, WM8960_LOUT2, 0x100, 0x100);
-	snd_soc_update_bits(codec, WM8960_ROUT2, 0x100, 0x100);
+	if (pdata->capless)
+		wm8960->set_bias_level = wm8960_set_bias_level_capless;
+	else
+		wm8960->set_bias_level = wm8960_set_bias_level_out3;
 
 	snd_soc_add_codec_controls(codec, wm8960_snd_controls,
 				     ARRAY_SIZE(wm8960_snd_controls));
@@ -1000,21 +960,10 @@ static int wm8960_probe(struct snd_soc_codec *codec)
 	return 0;
 }
 
-/* power down chip */
-static int wm8960_remove(struct snd_soc_codec *codec)
-{
-	struct wm8960_priv *wm8960 = snd_soc_codec_get_drvdata(codec);
-
-	wm8960->set_bias_level(codec, SND_SOC_BIAS_OFF);
-	return 0;
-}
-
 static struct snd_soc_codec_driver soc_codec_dev_wm8960 = {
 	.probe =	wm8960_probe,
-	.remove =	wm8960_remove,
-	.suspend =	wm8960_suspend,
-	.resume =	wm8960_resume,
 	.set_bias_level = wm8960_set_bias_level,
+	.suspend_bias_off = true,
 };
 
 static const struct regmap_config wm8960_regmap = {
@@ -1029,6 +978,18 @@ static const struct regmap_config wm8960_regmap = {
 	.volatile_reg = wm8960_volatile,
 };
 
+static void wm8960_set_pdata_from_of(struct i2c_client *i2c,
+				struct wm8960_data *pdata)
+{
+	const struct device_node *np = i2c->dev.of_node;
+
+	if (of_property_read_bool(np, "wlf,capless"))
+		pdata->capless = true;
+
+	if (of_property_read_bool(np, "wlf,shared-lrclk"))
+		pdata->shared_lrclk = true;
+}
+
 static int wm8960_i2c_probe(struct i2c_client *i2c,
 			    const struct i2c_device_id *id)
 {
@@ -1045,7 +1006,18 @@ static int wm8960_i2c_probe(struct i2c_client *i2c,
 	if (IS_ERR(wm8960->regmap))
 		return PTR_ERR(wm8960->regmap);
 
-	if (pdata && pdata->shared_lrclk) {
+	if (pdata)
+		memcpy(&wm8960->pdata, pdata, sizeof(struct wm8960_data));
+	else if (i2c->dev.of_node)
+		wm8960_set_pdata_from_of(i2c, &wm8960->pdata);
+
+	ret = wm8960_reset(wm8960->regmap);
+	if (ret != 0) {
+		dev_err(&i2c->dev, "Failed to issue reset\n");
+		return ret;
+	}
+
+	if (wm8960->pdata.shared_lrclk) {
 		ret = regmap_update_bits(wm8960->regmap, WM8960_ADDCTL2,
 					 0x4, 0x4);
 		if (ret != 0) {
@@ -1055,6 +1027,18 @@ static int wm8960_i2c_probe(struct i2c_client *i2c,
 		}
 	}
 
+	/* Latch the update bits */
+	regmap_update_bits(wm8960->regmap, WM8960_LINVOL, 0x100, 0x100);
+	regmap_update_bits(wm8960->regmap, WM8960_RINVOL, 0x100, 0x100);
+	regmap_update_bits(wm8960->regmap, WM8960_LADC, 0x100, 0x100);
+	regmap_update_bits(wm8960->regmap, WM8960_RADC, 0x100, 0x100);
+	regmap_update_bits(wm8960->regmap, WM8960_LDAC, 0x100, 0x100);
+	regmap_update_bits(wm8960->regmap, WM8960_RDAC, 0x100, 0x100);
+	regmap_update_bits(wm8960->regmap, WM8960_LOUT1, 0x100, 0x100);
+	regmap_update_bits(wm8960->regmap, WM8960_ROUT1, 0x100, 0x100);
+	regmap_update_bits(wm8960->regmap, WM8960_LOUT2, 0x100, 0x100);
+	regmap_update_bits(wm8960->regmap, WM8960_ROUT2, 0x100, 0x100);
+
 	i2c_set_clientdata(i2c, wm8960);
 
 	ret = snd_soc_register_codec(&i2c->dev,
@@ -1075,10 +1059,17 @@ static const struct i2c_device_id wm8960_i2c_id[] = {
 };
 MODULE_DEVICE_TABLE(i2c, wm8960_i2c_id);
 
+static const struct of_device_id wm8960_of_match[] = {
+       { .compatible = "wlf,wm8960", },
+       { }
+};
+MODULE_DEVICE_TABLE(of, wm8960_of_match);
+
 static struct i2c_driver wm8960_i2c_driver = {
 	.driver = {
 		.name = "wm8960",
 		.owner = THIS_MODULE,
+		.of_match_table = wm8960_of_match,
 	},
 	.probe =    wm8960_i2c_probe,
 	.remove =   wm8960_i2c_remove,
diff --git a/sound/soc/codecs/wm8961.c b/sound/soc/codecs/wm8961.c
index 41d23e920ad5..eeffd05384b4 100644
--- a/sound/soc/codecs/wm8961.c
+++ b/sound/soc/codecs/wm8961.c
@@ -835,7 +835,6 @@ static struct snd_soc_dai_driver wm8961_dai = {
 
 static int wm8961_probe(struct snd_soc_codec *codec)
 {
-	struct snd_soc_dapm_context *dapm = &codec->dapm;
 	u16 reg;
 
 	/* Enable class W */
@@ -871,50 +870,33 @@ static int wm8961_probe(struct snd_soc_codec *codec)
 	reg &= ~WM8961_MANUAL_MODE;
 	snd_soc_write(codec, WM8961_CLOCKING_3, reg);
 
-	wm8961_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
-
-	snd_soc_add_codec_controls(codec, wm8961_snd_controls,
-				ARRAY_SIZE(wm8961_snd_controls));
-	snd_soc_dapm_new_controls(dapm, wm8961_dapm_widgets,
-				  ARRAY_SIZE(wm8961_dapm_widgets));
-	snd_soc_dapm_add_routes(dapm, audio_paths, ARRAY_SIZE(audio_paths));
-
-	return 0;
-}
-
-static int wm8961_remove(struct snd_soc_codec *codec)
-{
-	wm8961_set_bias_level(codec, SND_SOC_BIAS_OFF);
 	return 0;
 }
 
 #ifdef CONFIG_PM
-static int wm8961_suspend(struct snd_soc_codec *codec)
-{
-	wm8961_set_bias_level(codec, SND_SOC_BIAS_OFF);
-
-	return 0;
-}
 
 static int wm8961_resume(struct snd_soc_codec *codec)
 {
 	snd_soc_cache_sync(codec);
 
-	wm8961_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
-
 	return 0;
 }
 #else
-#define wm8961_suspend NULL
 #define wm8961_resume NULL
 #endif
 
 static struct snd_soc_codec_driver soc_codec_dev_wm8961 = {
 	.probe =	wm8961_probe,
-	.remove =	wm8961_remove,
-	.suspend =	wm8961_suspend,
 	.resume =	wm8961_resume,
 	.set_bias_level = wm8961_set_bias_level,
+	.suspend_bias_off = true,
+
+	.controls = wm8961_snd_controls,
+	.num_controls = ARRAY_SIZE(wm8961_snd_controls),
+	.dapm_widgets = wm8961_dapm_widgets,
+	.num_dapm_widgets = ARRAY_SIZE(wm8961_dapm_widgets),
+	.dapm_routes = audio_paths,
+	.num_dapm_routes = ARRAY_SIZE(audio_paths),
 };
 
 static const struct regmap_config wm8961_regmap = {
diff --git a/sound/soc/codecs/wm8962.c b/sound/soc/codecs/wm8962.c
index 9077411e62ce..1534d88a66e9 100644
--- a/sound/soc/codecs/wm8962.c
+++ b/sound/soc/codecs/wm8962.c
@@ -26,6 +26,7 @@
 #include <linux/regulator/consumer.h>
 #include <linux/slab.h>
 #include <linux/workqueue.h>
+#include <linux/mutex.h>
 #include <sound/core.h>
 #include <sound/jack.h>
 #include <sound/pcm.h>
@@ -67,6 +68,7 @@ struct wm8962_priv {
 	int fll_fref;
 	int fll_fout;
 
+	struct mutex dsp2_ena_lock;
 	u16 dsp2_ena;
 
 	struct delayed_work mic_work;
@@ -1570,7 +1572,7 @@ static int wm8962_dsp2_ena_put(struct snd_kcontrol *kcontrol,
 	int dsp2_running = snd_soc_read(codec, WM8962_DSP2_POWER_MANAGEMENT) &
 		WM8962_DSP2_ENA;
 
-	mutex_lock(&codec->mutex);
+	mutex_lock(&wm8962->dsp2_ena_lock);
 
 	if (ucontrol->value.integer.value[0])
 		wm8962->dsp2_ena |= 1 << shift;
@@ -1590,7 +1592,7 @@ static int wm8962_dsp2_ena_put(struct snd_kcontrol *kcontrol,
 	}
 
 out:
-	mutex_unlock(&codec->mutex);
+	mutex_unlock(&wm8962->dsp2_ena_lock);
 
 	return ret;
 }
@@ -3552,11 +3554,12 @@ static int wm8962_i2c_probe(struct i2c_client *i2c,
 	unsigned int reg;
 	int ret, i, irq_pol, trigger;
 
-	wm8962 = devm_kzalloc(&i2c->dev, sizeof(struct wm8962_priv),
-			      GFP_KERNEL);
+	wm8962 = devm_kzalloc(&i2c->dev, sizeof(*wm8962), GFP_KERNEL);
 	if (wm8962 == NULL)
 		return -ENOMEM;
 
+	mutex_init(&wm8962->dsp2_ena_lock);
+
 	i2c_set_clientdata(i2c, wm8962);
 
 	INIT_DELAYED_WORK(&wm8962->mic_work, wm8962_mic_work);
diff --git a/sound/soc/codecs/wm8974.c b/sound/soc/codecs/wm8974.c
index 682e9eda1019..ff0e4646b934 100644
--- a/sound/soc/codecs/wm8974.c
+++ b/sound/soc/codecs/wm8974.c
@@ -568,18 +568,6 @@ static struct snd_soc_dai_driver wm8974_dai = {
 	.symmetric_rates = 1,
 };
 
-static int wm8974_suspend(struct snd_soc_codec *codec)
-{
-	wm8974_set_bias_level(codec, SND_SOC_BIAS_OFF);
-	return 0;
-}
-
-static int wm8974_resume(struct snd_soc_codec *codec)
-{
-	wm8974_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
-	return 0;
-}
-
 static const struct regmap_config wm8974_regmap = {
 	.reg_bits = 7,
 	.val_bits = 9,
@@ -599,24 +587,13 @@ static int wm8974_probe(struct snd_soc_codec *codec)
 		return ret;
 	}
 
-	wm8974_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
-
-	return ret;
-}
-
-/* power down chip */
-static int wm8974_remove(struct snd_soc_codec *codec)
-{
-	wm8974_set_bias_level(codec, SND_SOC_BIAS_OFF);
 	return 0;
 }
 
 static struct snd_soc_codec_driver soc_codec_dev_wm8974 = {
 	.probe = 	wm8974_probe,
-	.remove = 	wm8974_remove,
-	.suspend = 	wm8974_suspend,
-	.resume =	wm8974_resume,
 	.set_bias_level = wm8974_set_bias_level,
+	.suspend_bias_off = true,
 
 	.controls = wm8974_snd_controls,
 	.num_controls = ARRAY_SIZE(wm8974_snd_controls),
diff --git a/sound/soc/codecs/wm8978.c b/sound/soc/codecs/wm8978.c
index ee2ba574952b..cf7032911721 100644
--- a/sound/soc/codecs/wm8978.c
+++ b/sound/soc/codecs/wm8978.c
@@ -991,21 +991,11 @@ static int wm8978_probe(struct snd_soc_codec *codec)
 	for (i = 0; i < ARRAY_SIZE(update_reg); i++)
 		snd_soc_update_bits(codec, update_reg[i], 0x100, 0x100);
 
-	wm8978_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
-
-	return 0;
-}
-
-/* power down chip */
-static int wm8978_remove(struct snd_soc_codec *codec)
-{
-	wm8978_set_bias_level(codec, SND_SOC_BIAS_OFF);
 	return 0;
 }
 
 static struct snd_soc_codec_driver soc_codec_dev_wm8978 = {
 	.probe =	wm8978_probe,
-	.remove =	wm8978_remove,
 	.suspend =	wm8978_suspend,
 	.resume =	wm8978_resume,
 	.set_bias_level = wm8978_set_bias_level,
diff --git a/sound/soc/codecs/wm8983.c b/sound/soc/codecs/wm8983.c
index ac5defda8824..5d1cf08a72b8 100644
--- a/sound/soc/codecs/wm8983.c
+++ b/sound/soc/codecs/wm8983.c
@@ -967,29 +967,6 @@ static int wm8983_set_bias_level(struct snd_soc_codec *codec,
 	return 0;
 }
 
-#ifdef CONFIG_PM
-static int wm8983_suspend(struct snd_soc_codec *codec)
-{
-	wm8983_set_bias_level(codec, SND_SOC_BIAS_OFF);
-	return 0;
-}
-
-static int wm8983_resume(struct snd_soc_codec *codec)
-{
-	wm8983_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
-	return 0;
-}
-#else
-#define wm8983_suspend NULL
-#define wm8983_resume NULL
-#endif
-
-static int wm8983_remove(struct snd_soc_codec *codec)
-{
-	wm8983_set_bias_level(codec, SND_SOC_BIAS_OFF);
-	return 0;
-}
-
 static int wm8983_probe(struct snd_soc_codec *codec)
 {
 	int ret;
@@ -1055,10 +1032,8 @@ static struct snd_soc_dai_driver wm8983_dai = {
 
 static struct snd_soc_codec_driver soc_codec_dev_wm8983 = {
 	.probe = wm8983_probe,
-	.remove = wm8983_remove,
-	.suspend = wm8983_suspend,
-	.resume = wm8983_resume,
 	.set_bias_level = wm8983_set_bias_level,
+	.suspend_bias_off = true,
 	.controls = wm8983_snd_controls,
 	.num_controls = ARRAY_SIZE(wm8983_snd_controls),
 	.dapm_widgets = wm8983_dapm_widgets,
diff --git a/sound/soc/codecs/wm8985.c b/sound/soc/codecs/wm8985.c
index ee380190399f..0b3b54c9971d 100644
--- a/sound/soc/codecs/wm8985.c
+++ b/sound/soc/codecs/wm8985.c
@@ -961,29 +961,6 @@ static int wm8985_set_bias_level(struct snd_soc_codec *codec,
 	return 0;
 }
 
-#ifdef CONFIG_PM
-static int wm8985_suspend(struct snd_soc_codec *codec)
-{
-	wm8985_set_bias_level(codec, SND_SOC_BIAS_OFF);
-	return 0;
-}
-
-static int wm8985_resume(struct snd_soc_codec *codec)
-{
-	wm8985_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
-	return 0;
-}
-#else
-#define wm8985_suspend NULL
-#define wm8985_resume NULL
-#endif
-
-static int wm8985_remove(struct snd_soc_codec *codec)
-{
-	wm8985_set_bias_level(codec, SND_SOC_BIAS_OFF);
-	return 0;
-}
-
 static int wm8985_probe(struct snd_soc_codec *codec)
 {
 	size_t i;
@@ -1023,7 +1000,6 @@ static int wm8985_probe(struct snd_soc_codec *codec)
 	snd_soc_update_bits(codec, WM8985_BIAS_CTRL, WM8985_BIASCUT,
 			    WM8985_BIASCUT);
 
-	wm8985_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
 	return 0;
 
 err_reg_enable:
@@ -1064,10 +1040,8 @@ static struct snd_soc_dai_driver wm8985_dai = {
 
 static struct snd_soc_codec_driver soc_codec_dev_wm8985 = {
 	.probe = wm8985_probe,
-	.remove = wm8985_remove,
-	.suspend = wm8985_suspend,
-	.resume = wm8985_resume,
 	.set_bias_level = wm8985_set_bias_level,
+	.suspend_bias_off = true,
 
 	.controls = wm8985_snd_controls,
 	.num_controls = ARRAY_SIZE(wm8985_snd_controls),
diff --git a/sound/soc/codecs/wm8988.c b/sound/soc/codecs/wm8988.c
index a5130d965146..e418199155a8 100644
--- a/sound/soc/codecs/wm8988.c
+++ b/sound/soc/codecs/wm8988.c
@@ -793,21 +793,6 @@ static struct snd_soc_dai_driver wm8988_dai = {
 	.symmetric_rates = 1,
 };
 
-static int wm8988_suspend(struct snd_soc_codec *codec)
-{
-	struct wm8988_priv *wm8988 = snd_soc_codec_get_drvdata(codec);
-
-	wm8988_set_bias_level(codec, SND_SOC_BIAS_OFF);
-	regcache_mark_dirty(wm8988->regmap);
-	return 0;
-}
-
-static int wm8988_resume(struct snd_soc_codec *codec)
-{
-	wm8988_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
-	return 0;
-}
-
 static int wm8988_probe(struct snd_soc_codec *codec)
 {
 	int ret = 0;
@@ -825,23 +810,13 @@ static int wm8988_probe(struct snd_soc_codec *codec)
 	snd_soc_update_bits(codec, WM8988_ROUT2V, 0x0100, 0x0100);
 	snd_soc_update_bits(codec, WM8988_RINVOL, 0x0100, 0x0100);
 
-	wm8988_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
-
-	return 0;
-}
-
-static int wm8988_remove(struct snd_soc_codec *codec)
-{
-	wm8988_set_bias_level(codec, SND_SOC_BIAS_OFF);
 	return 0;
 }
 
 static struct snd_soc_codec_driver soc_codec_dev_wm8988 = {
 	.probe =	wm8988_probe,
-	.remove =	wm8988_remove,
-	.suspend =	wm8988_suspend,
-	.resume =	wm8988_resume,
 	.set_bias_level = wm8988_set_bias_level,
+	.suspend_bias_off = true,
 
 	.controls = wm8988_snd_controls,
 	.num_controls = ARRAY_SIZE(wm8988_snd_controls),
diff --git a/sound/soc/codecs/wm8990.c b/sound/soc/codecs/wm8990.c
index 03e43e3f395e..8a584229310a 100644
--- a/sound/soc/codecs/wm8990.c
+++ b/sound/soc/codecs/wm8990.c
@@ -1271,18 +1271,6 @@ static struct snd_soc_dai_driver wm8990_dai = {
 	.ops = &wm8990_dai_ops,
 };
 
-static int wm8990_suspend(struct snd_soc_codec *codec)
-{
-	wm8990_set_bias_level(codec, SND_SOC_BIAS_OFF);
-	return 0;
-}
-
-static int wm8990_resume(struct snd_soc_codec *codec)
-{
-	wm8990_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
-	return 0;
-}
-
 /*
  * initialise the WM8990 driver
  * register the mixer and dsp interfaces with the kernel
@@ -1309,19 +1297,11 @@ static int wm8990_probe(struct snd_soc_codec *codec)
 	return 0;
 }
 
-/* power down chip */
-static int wm8990_remove(struct snd_soc_codec *codec)
-{
-	wm8990_set_bias_level(codec, SND_SOC_BIAS_OFF);
-	return 0;
-}
-
 static struct snd_soc_codec_driver soc_codec_dev_wm8990 = {
 	.probe =	wm8990_probe,
-	.remove =	wm8990_remove,
-	.suspend =	wm8990_suspend,
-	.resume =	wm8990_resume,
 	.set_bias_level = wm8990_set_bias_level,
+	.suspend_bias_off = true,
+
 	.controls =	wm8990_snd_controls,
 	.num_controls = ARRAY_SIZE(wm8990_snd_controls),
 	.dapm_widgets = wm8990_dapm_widgets,
diff --git a/sound/soc/codecs/wm8991.c b/sound/soc/codecs/wm8991.c
index d0be89731cdb..b0ac2c3e31b9 100644
--- a/sound/soc/codecs/wm8991.c
+++ b/sound/soc/codecs/wm8991.c
@@ -1227,32 +1227,6 @@ static int wm8991_set_bias_level(struct snd_soc_codec *codec,
 	return 0;
 }
 
-static int wm8991_suspend(struct snd_soc_codec *codec)
-{
-	wm8991_set_bias_level(codec, SND_SOC_BIAS_OFF);
-	return 0;
-}
-
-static int wm8991_resume(struct snd_soc_codec *codec)
-{
-	wm8991_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
-	return 0;
-}
-
-/* power down chip */
-static int wm8991_remove(struct snd_soc_codec *codec)
-{
-	wm8991_set_bias_level(codec, SND_SOC_BIAS_OFF);
-	return 0;
-}
-
-static int wm8991_probe(struct snd_soc_codec *codec)
-{
-	wm8991_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
-
-	return 0;
-}
-
 #define WM8991_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
 			SNDRV_PCM_FMTBIT_S24_LE)
 
@@ -1293,11 +1267,9 @@ static struct snd_soc_dai_driver wm8991_dai = {
 };
 
 static struct snd_soc_codec_driver soc_codec_dev_wm8991 = {
-	.probe = wm8991_probe,
-	.remove = wm8991_remove,
-	.suspend = wm8991_suspend,
-	.resume = wm8991_resume,
 	.set_bias_level = wm8991_set_bias_level,
+	.suspend_bias_off = true,
+
 	.controls = wm8991_snd_controls,
 	.num_controls = ARRAY_SIZE(wm8991_snd_controls),
 	.dapm_widgets = wm8991_dapm_widgets,
diff --git a/sound/soc/codecs/wm8993.c b/sound/soc/codecs/wm8993.c
index 93b14eda355a..53c6fe359496 100644
--- a/sound/soc/codecs/wm8993.c
+++ b/sound/soc/codecs/wm8993.c
@@ -1486,7 +1486,6 @@ 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;
-	int ret;
 
 	wm8993->hubs_data.hp_startup_mode = 1;
 	wm8993->hubs_data.dcs_codes_l = -2;
@@ -1518,10 +1517,6 @@ static int wm8993_probe(struct snd_soc_codec *codec)
 				      wm8993->pdata.micbias1_lvl,
 				      wm8993->pdata.micbias2_lvl);
 
-	ret = wm8993_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
-	if (ret != 0)
-		return ret;
-
 	snd_soc_add_codec_controls(codec, wm8993_snd_controls,
 			     ARRAY_SIZE(wm8993_snd_controls));
 	if (wm8993->pdata.num_retune_configs != 0) {
@@ -1550,12 +1545,6 @@ static int wm8993_probe(struct snd_soc_codec *codec)
 
 }
 
-static int wm8993_remove(struct snd_soc_codec *codec)
-{
-	wm8993_set_bias_level(codec, SND_SOC_BIAS_OFF);
-	return 0;
-}
-
 #ifdef CONFIG_PM
 static int wm8993_suspend(struct snd_soc_codec *codec)
 {
@@ -1629,7 +1618,6 @@ static const struct regmap_config wm8993_regmap = {
 
 static struct snd_soc_codec_driver soc_codec_dev_wm8993 = {
 	.probe = 	wm8993_probe,
-	.remove = 	wm8993_remove,
 	.suspend =	wm8993_suspend,
 	.resume =	wm8993_resume,
 	.set_bias_level = wm8993_set_bias_level,
diff --git a/sound/soc/codecs/wm8994.c b/sound/soc/codecs/wm8994.c
index 1fcb9f3f3097..36b767fa37a6 100644
--- a/sound/soc/codecs/wm8994.c
+++ b/sound/soc/codecs/wm8994.c
@@ -4391,8 +4391,6 @@ static int wm8994_codec_remove(struct snd_soc_codec *codec)
 	struct wm8994 *control = wm8994->wm8994;
 	int i;
 
-	wm8994_set_bias_level(codec, SND_SOC_BIAS_OFF);
-
 	for (i = 0; i < ARRAY_SIZE(wm8994->fll_locked); i++)
 		wm8994_free_irq(wm8994->wm8994, WM8994_IRQ_FLL1_LOCK + i,
 				&wm8994->fll_locked[i]);
@@ -4457,6 +4455,8 @@ static int wm8994_probe(struct platform_device *pdev)
 		return -ENOMEM;
 	platform_set_drvdata(pdev, wm8994);
 
+	mutex_init(&wm8994->fw_lock);
+
 	wm8994->wm8994 = dev_get_drvdata(pdev->dev.parent);
 
 	pm_runtime_enable(&pdev->dev);
diff --git a/sound/soc/codecs/wm8994.h b/sound/soc/codecs/wm8994.h
index 6536f8d45ac6..dd73387b1cc4 100644
--- a/sound/soc/codecs/wm8994.h
+++ b/sound/soc/codecs/wm8994.h
@@ -13,6 +13,7 @@
 #include <linux/firmware.h>
 #include <linux/completion.h>
 #include <linux/workqueue.h>
+#include <linux/mutex.h>
 
 #include "wm_hubs.h"
 
@@ -156,6 +157,7 @@ struct wm8994_priv {
 	unsigned int aif1clk_disable:1;
 	unsigned int aif2clk_disable:1;
 
+	struct mutex fw_lock;
 	int dsp_active;
 	const struct firmware *cur_fw;
 	const struct firmware *mbc;
diff --git a/sound/soc/codecs/wm8995.c b/sound/soc/codecs/wm8995.c
index 1288edeb8c7d..c280f0a3a424 100644
--- a/sound/soc/codecs/wm8995.c
+++ b/sound/soc/codecs/wm8995.c
@@ -2004,7 +2004,6 @@ static int wm8995_remove(struct snd_soc_codec *codec)
 	int i;
 
 	wm8995 = snd_soc_codec_get_drvdata(codec);
-	wm8995_set_bias_level(codec, SND_SOC_BIAS_OFF);
 
 	for (i = 0; i < ARRAY_SIZE(wm8995->supplies); ++i)
 		regulator_unregister_notifier(wm8995->supplies[i].consumer,
@@ -2078,8 +2077,6 @@ static int wm8995_probe(struct snd_soc_codec *codec)
 		goto err_reg_enable;
 	}
 
-	wm8995_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
-
 	/* Latch volume updates (right only; we always do left then right). */
 	snd_soc_update_bits(codec, WM8995_AIF1_DAC1_RIGHT_VOLUME,
 			    WM8995_AIF1DAC1_VU_MASK, WM8995_AIF1DAC1_VU);
@@ -2102,13 +2099,6 @@ static int wm8995_probe(struct snd_soc_codec *codec)
 
 	wm8995_update_class_w(codec);
 
-	snd_soc_add_codec_controls(codec, wm8995_snd_controls,
-			     ARRAY_SIZE(wm8995_snd_controls));
-	snd_soc_dapm_new_controls(&codec->dapm, wm8995_dapm_widgets,
-				  ARRAY_SIZE(wm8995_dapm_widgets));
-	snd_soc_dapm_add_routes(&codec->dapm, wm8995_intercon,
-				ARRAY_SIZE(wm8995_intercon));
-
 	return 0;
 
 err_reg_enable:
@@ -2205,6 +2195,13 @@ static struct snd_soc_codec_driver soc_codec_dev_wm8995 = {
 	.remove = wm8995_remove,
 	.set_bias_level = wm8995_set_bias_level,
 	.idle_bias_off = true,
+
+	.controls = wm8995_snd_controls,
+	.num_controls = ARRAY_SIZE(wm8995_snd_controls),
+	.dapm_widgets = wm8995_dapm_widgets,
+	.num_dapm_widgets = ARRAY_SIZE(wm8995_dapm_widgets),
+	.dapm_routes = wm8995_intercon,
+	.num_dapm_routes = ARRAY_SIZE(wm8995_intercon),
 };
 
 static struct regmap_config wm8995_regmap = {
diff --git a/sound/soc/codecs/wm9081.c b/sound/soc/codecs/wm9081.c
index 0cdc9e2184ab..b1d946facd57 100644
--- a/sound/soc/codecs/wm9081.c
+++ b/sound/soc/codecs/wm9081.c
@@ -1277,15 +1277,8 @@ static int wm9081_probe(struct snd_soc_codec *codec)
 	return 0;
 }
 
-static int wm9081_remove(struct snd_soc_codec *codec)
-{
-	wm9081_set_bias_level(codec, SND_SOC_BIAS_OFF);
-	return 0;
-}
-
 static struct snd_soc_codec_driver soc_codec_dev_wm9081 = {
 	.probe = 	wm9081_probe,
-	.remove = 	wm9081_remove,
 
 	.set_sysclk = wm9081_set_sysclk,
 	.set_bias_level = wm9081_set_bias_level,
diff --git a/sound/soc/codecs/wm9090.c b/sound/soc/codecs/wm9090.c
index a13f0725611a..6ffe8dc4f3fa 100644
--- a/sound/soc/codecs/wm9090.c
+++ b/sound/soc/codecs/wm9090.c
@@ -550,45 +550,15 @@ static int wm9090_probe(struct snd_soc_codec *codec)
 	snd_soc_update_bits(codec, WM9090_CLOCKING_1,
 			    WM9090_TOCLK_ENA, WM9090_TOCLK_ENA);
 
-	wm9090_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
-
 	wm9090_add_controls(codec);
 
 	return 0;
 }
 
-#ifdef CONFIG_PM
-static int wm9090_suspend(struct snd_soc_codec *codec)
-{
-	wm9090_set_bias_level(codec, SND_SOC_BIAS_OFF);
-
-	return 0;
-}
-
-static int wm9090_resume(struct snd_soc_codec *codec)
-{
-	wm9090_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
-
-	return 0;
-}
-#else
-#define wm9090_suspend NULL
-#define wm9090_resume NULL
-#endif
-
-static int wm9090_remove(struct snd_soc_codec *codec)
-{
-	wm9090_set_bias_level(codec, SND_SOC_BIAS_OFF);
-
-	return 0;
-}
-
 static struct snd_soc_codec_driver soc_codec_dev_wm9090 = {
 	.probe = 	wm9090_probe,
-	.remove = 	wm9090_remove,
-	.suspend = 	wm9090_suspend,
-	.resume =	wm9090_resume,
 	.set_bias_level = wm9090_set_bias_level,
+	.suspend_bias_off = true,
 };
 
 static const struct regmap_config wm9090_regmap = {
diff --git a/sound/soc/codecs/wm9705.c b/sound/soc/codecs/wm9705.c
index c0b7f45dfa37..d3a800fa6f06 100644
--- a/sound/soc/codecs/wm9705.c
+++ b/sound/soc/codecs/wm9705.c
@@ -203,13 +203,14 @@ static const struct snd_soc_dapm_route wm9705_audio_map[] = {
 /* We use a register cache to enhance read performance. */
 static unsigned int ac97_read(struct snd_soc_codec *codec, unsigned int reg)
 {
+	struct snd_ac97 *ac97 = snd_soc_codec_get_drvdata(codec);
 	u16 *cache = codec->reg_cache;
 
 	switch (reg) {
 	case AC97_RESET:
 	case AC97_VENDOR_ID1:
 	case AC97_VENDOR_ID2:
-		return soc_ac97_ops->read(codec->ac97, reg);
+		return soc_ac97_ops->read(ac97, reg);
 	default:
 		reg = reg >> 1;
 
@@ -223,9 +224,10 @@ static unsigned int ac97_read(struct snd_soc_codec *codec, unsigned int reg)
 static int ac97_write(struct snd_soc_codec *codec, unsigned int reg,
 	unsigned int val)
 {
+	struct snd_ac97 *ac97 = snd_soc_codec_get_drvdata(codec);
 	u16 *cache = codec->reg_cache;
 
-	soc_ac97_ops->write(codec->ac97, reg, val);
+	soc_ac97_ops->write(ac97, reg, val);
 	reg = reg >> 1;
 	if (reg < (ARRAY_SIZE(wm9705_reg)))
 		cache[reg] = val;
@@ -263,7 +265,6 @@ static const struct snd_soc_dai_ops wm9705_dai_ops = {
 static struct snd_soc_dai_driver wm9705_dai[] = {
 	{
 		.name = "wm9705-hifi",
-		.ac97_control = 1,
 		.playback = {
 			.stream_name = "HiFi Playback",
 			.channels_min = 1,
@@ -294,36 +295,41 @@ static struct snd_soc_dai_driver wm9705_dai[] = {
 
 static int wm9705_reset(struct snd_soc_codec *codec)
 {
+	struct snd_ac97 *ac97 = snd_soc_codec_get_drvdata(codec);
+
 	if (soc_ac97_ops->reset) {
-		soc_ac97_ops->reset(codec->ac97);
+		soc_ac97_ops->reset(ac97);
 		if (ac97_read(codec, 0) == wm9705_reg[0])
 			return 0; /* Success */
 	}
 
+	dev_err(codec->dev, "Failed to reset: AC97 link error\n");
+
 	return -EIO;
 }
 
 #ifdef CONFIG_PM
 static int wm9705_soc_suspend(struct snd_soc_codec *codec)
 {
-	soc_ac97_ops->write(codec->ac97, AC97_POWERDOWN, 0xffff);
+	struct snd_ac97 *ac97 = snd_soc_codec_get_drvdata(codec);
+
+	soc_ac97_ops->write(ac97, AC97_POWERDOWN, 0xffff);
 
 	return 0;
 }
 
 static int wm9705_soc_resume(struct snd_soc_codec *codec)
 {
+	struct snd_ac97 *ac97 = snd_soc_codec_get_drvdata(codec);
 	int i, ret;
 	u16 *cache = codec->reg_cache;
 
 	ret = wm9705_reset(codec);
-	if (ret < 0) {
-		printk(KERN_ERR "could not reset AC97 codec\n");
+	if (ret < 0)
 		return ret;
-	}
 
 	for (i = 2; i < ARRAY_SIZE(wm9705_reg) << 1; i += 2) {
-		soc_ac97_ops->write(codec->ac97, i, cache[i>>1]);
+		soc_ac97_ops->write(ac97, i, cache[i>>1]);
 	}
 
 	return 0;
@@ -335,31 +341,34 @@ static int wm9705_soc_resume(struct snd_soc_codec *codec)
 
 static int wm9705_soc_probe(struct snd_soc_codec *codec)
 {
+	struct snd_ac97 *ac97;
 	int ret = 0;
 
-	ret = snd_soc_new_ac97_codec(codec, soc_ac97_ops, 0);
-	if (ret < 0) {
-		printk(KERN_ERR "wm9705: failed to register AC97 codec\n");
+	ac97 = snd_soc_new_ac97_codec(codec);
+	if (IS_ERR(ac97)) {
+		ret = PTR_ERR(ac97);
+		dev_err(codec->dev, "Failed to register AC97 codec\n");
 		return ret;
 	}
 
+	snd_soc_codec_set_drvdata(codec, ac97);
+
 	ret = wm9705_reset(codec);
 	if (ret)
 		goto reset_err;
 
-	snd_soc_add_codec_controls(codec, wm9705_snd_ac97_controls,
-				ARRAY_SIZE(wm9705_snd_ac97_controls));
-
 	return 0;
 
 reset_err:
-	snd_soc_free_ac97_codec(codec);
+	snd_soc_free_ac97_codec(ac97);
 	return ret;
 }
 
 static int wm9705_soc_remove(struct snd_soc_codec *codec)
 {
-	snd_soc_free_ac97_codec(codec);
+	struct snd_ac97 *ac97 = snd_soc_codec_get_drvdata(codec);
+
+	snd_soc_free_ac97_codec(ac97);
 	return 0;
 }
 
@@ -374,6 +383,9 @@ static struct snd_soc_codec_driver soc_codec_dev_wm9705 = {
 	.reg_word_size = sizeof(u16),
 	.reg_cache_step = 2,
 	.reg_cache_default = wm9705_reg,
+
+	.controls = wm9705_snd_ac97_controls,
+	.num_controls = ARRAY_SIZE(wm9705_snd_ac97_controls),
 	.dapm_widgets = wm9705_dapm_widgets,
 	.num_dapm_widgets = ARRAY_SIZE(wm9705_dapm_widgets),
 	.dapm_routes = wm9705_audio_map,
diff --git a/sound/soc/codecs/wm9712.c b/sound/soc/codecs/wm9712.c
index c5eb746087b4..7c45971bb4ec 100644
--- a/sound/soc/codecs/wm9712.c
+++ b/sound/soc/codecs/wm9712.c
@@ -23,6 +23,12 @@
 #include <sound/tlv.h>
 #include "wm9712.h"
 
+struct wm9712_priv {
+	struct snd_ac97 *ac97;
+	unsigned int hp_mixer[2];
+	struct mutex lock;
+};
+
 static unsigned int ac97_read(struct snd_soc_codec *codec,
 	unsigned int reg);
 static int ac97_write(struct snd_soc_codec *codec,
@@ -48,12 +54,10 @@ static const u16 wm9712_reg[] = {
 	0x0000, 0x0000, 0x0000, 0x0000, /* 6e */
 	0x0000, 0x0000, 0x0000, 0x0006, /* 76 */
 	0x0001, 0x0000, 0x574d, 0x4c12, /* 7e */
-	0x0000, 0x0000 /* virtual hp mixers */
 };
 
-/* virtual HP mixers regs */
-#define HPL_MIXER	0x80
-#define HPR_MIXER	0x82
+#define HPL_MIXER	0x0
+#define HPR_MIXER	0x1
 
 static const char *wm9712_alc_select[] = {"None", "Left", "Right", "Stereo"};
 static const char *wm9712_alc_mux[] = {"Stereo", "Left", "Right", "None"};
@@ -157,75 +161,108 @@ SOC_SINGLE_TLV("Mic 2 Volume", AC97_MIC, 0, 31, 1, main_tlv),
 SOC_SINGLE_TLV("Mic Boost Volume", AC97_MIC, 7, 1, 0, boost_tlv),
 };
 
+static const unsigned int wm9712_mixer_mute_regs[] = {
+	AC97_VIDEO,
+	AC97_PCM,
+	AC97_LINE,
+	AC97_PHONE,
+	AC97_CD,
+	AC97_PC_BEEP,
+};
+
 /* We have to create a fake left and right HP mixers because
  * the codec only has a single control that is shared by both channels.
  * This makes it impossible to determine the audio path.
  */
-static int mixer_event(struct snd_soc_dapm_widget *w,
-	struct snd_kcontrol *k, int event)
+static int wm9712_hp_mixer_put(struct snd_kcontrol *kcontrol,
+	struct snd_ctl_elem_value *ucontrol)
 {
-	u16 l, r, beep, line, phone, mic, pcm, aux;
-
-	l = ac97_read(w->codec, HPL_MIXER);
-	r = ac97_read(w->codec, HPR_MIXER);
-	beep = ac97_read(w->codec, AC97_PC_BEEP);
-	mic = ac97_read(w->codec, AC97_VIDEO);
-	phone = ac97_read(w->codec, AC97_PHONE);
-	line = ac97_read(w->codec, AC97_LINE);
-	pcm = ac97_read(w->codec, AC97_PCM);
-	aux = ac97_read(w->codec, AC97_CD);
-
-	if (l & 0x1 || r & 0x1)
-		ac97_write(w->codec, AC97_VIDEO, mic & 0x7fff);
+	struct snd_soc_dapm_context *dapm = snd_soc_dapm_kcontrol_dapm(kcontrol);
+	struct snd_soc_codec *codec = snd_soc_dapm_to_codec(dapm);
+	struct wm9712_priv *wm9712 = snd_soc_codec_get_drvdata(codec);
+	unsigned int val = ucontrol->value.enumerated.item[0];
+	struct soc_mixer_control *mc =
+		(struct soc_mixer_control *)kcontrol->private_value;
+	unsigned int mixer, mask, shift, old;
+	struct snd_soc_dapm_update update;
+	bool change;
+
+	mixer = mc->shift >> 8;
+	shift = mc->shift & 0xff;
+	mask = 1 << shift;
+
+	mutex_lock(&wm9712->lock);
+	old = wm9712->hp_mixer[mixer];
+	if (ucontrol->value.enumerated.item[0])
+		wm9712->hp_mixer[mixer] |= mask;
 	else
-		ac97_write(w->codec, AC97_VIDEO, mic | 0x8000);
+		wm9712->hp_mixer[mixer] &= ~mask;
+
+	change = old != wm9712->hp_mixer[mixer];
+	if (change) {
+		update.kcontrol = kcontrol;
+		update.reg = wm9712_mixer_mute_regs[shift];
+		update.mask = 0x8000;
+		if ((wm9712->hp_mixer[0] & mask) ||
+		    (wm9712->hp_mixer[1] & mask))
+			update.val = 0x0;
+		else
+			update.val = 0x8000;
+
+		snd_soc_dapm_mixer_update_power(dapm, kcontrol, val,
+			&update);
+	}
 
-	if (l & 0x2 || r & 0x2)
-		ac97_write(w->codec, AC97_PCM, pcm & 0x7fff);
-	else
-		ac97_write(w->codec, AC97_PCM, pcm | 0x8000);
+	mutex_unlock(&wm9712->lock);
 
-	if (l & 0x4 || r & 0x4)
-		ac97_write(w->codec, AC97_LINE, line & 0x7fff);
-	else
-		ac97_write(w->codec, AC97_LINE, line | 0x8000);
+	return change;
+}
 
-	if (l & 0x8 || r & 0x8)
-		ac97_write(w->codec, AC97_PHONE, phone & 0x7fff);
-	else
-		ac97_write(w->codec, AC97_PHONE, phone | 0x8000);
+static int wm9712_hp_mixer_get(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_codec *codec = snd_soc_dapm_to_codec(dapm);
+	struct wm9712_priv *wm9712 = snd_soc_codec_get_drvdata(codec);
+	struct soc_mixer_control *mc =
+		(struct soc_mixer_control *)kcontrol->private_value;
+	unsigned int shift, mixer;
 
-	if (l & 0x10 || r & 0x10)
-		ac97_write(w->codec, AC97_CD, aux & 0x7fff);
-	else
-		ac97_write(w->codec, AC97_CD, aux | 0x8000);
+	mixer = mc->shift >> 8;
+	shift = mc->shift & 0xff;
 
-	if (l & 0x20 || r & 0x20)
-		ac97_write(w->codec, AC97_PC_BEEP, beep & 0x7fff);
-	else
-		ac97_write(w->codec, AC97_PC_BEEP, beep | 0x8000);
+	ucontrol->value.enumerated.item[0] =
+		(wm9712->hp_mixer[mixer] >> shift) & 1;
 
 	return 0;
 }
 
+#define WM9712_HP_MIXER_CTRL(xname, xmixer, xshift) { \
+	.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
+	.info = snd_soc_info_volsw, \
+	.get = wm9712_hp_mixer_get, .put = wm9712_hp_mixer_put, \
+	.private_value = SOC_SINGLE_VALUE(SND_SOC_NOPM, \
+		(xmixer << 8) | xshift, 1, 0, 0) \
+}
+
 /* Left Headphone Mixers */
 static const struct snd_kcontrol_new wm9712_hpl_mixer_controls[] = {
-	SOC_DAPM_SINGLE("PCBeep Bypass Switch", HPL_MIXER, 5, 1, 0),
-	SOC_DAPM_SINGLE("Aux Playback Switch", HPL_MIXER, 4, 1, 0),
-	SOC_DAPM_SINGLE("Phone Bypass Switch", HPL_MIXER, 3, 1, 0),
-	SOC_DAPM_SINGLE("Line Bypass Switch", HPL_MIXER, 2, 1, 0),
-	SOC_DAPM_SINGLE("PCM Playback Switch", HPL_MIXER, 1, 1, 0),
-	SOC_DAPM_SINGLE("Mic Sidetone Switch", HPL_MIXER, 0, 1, 0),
+	WM9712_HP_MIXER_CTRL("PCBeep Bypass Switch", HPL_MIXER, 5),
+	WM9712_HP_MIXER_CTRL("Aux Playback Switch", HPL_MIXER, 4),
+	WM9712_HP_MIXER_CTRL("Phone Bypass Switch", HPL_MIXER, 3),
+	WM9712_HP_MIXER_CTRL("Line Bypass Switch", HPL_MIXER, 2),
+	WM9712_HP_MIXER_CTRL("PCM Playback Switch", HPL_MIXER, 1),
+	WM9712_HP_MIXER_CTRL("Mic Sidetone Switch", HPL_MIXER, 0),
 };
 
 /* Right Headphone Mixers */
 static const struct snd_kcontrol_new wm9712_hpr_mixer_controls[] = {
-	SOC_DAPM_SINGLE("PCBeep Bypass Switch", HPR_MIXER, 5, 1, 0),
-	SOC_DAPM_SINGLE("Aux Playback Switch", HPR_MIXER, 4, 1, 0),
-	SOC_DAPM_SINGLE("Phone Bypass Switch", HPR_MIXER, 3, 1, 0),
-	SOC_DAPM_SINGLE("Line Bypass Switch", HPR_MIXER, 2, 1, 0),
-	SOC_DAPM_SINGLE("PCM Playback Switch", HPR_MIXER, 1, 1, 0),
-	SOC_DAPM_SINGLE("Mic Sidetone Switch", HPR_MIXER, 0, 1, 0),
+	WM9712_HP_MIXER_CTRL("PCBeep Bypass Switch", HPR_MIXER, 5),
+	WM9712_HP_MIXER_CTRL("Aux Playback Switch", HPR_MIXER, 4),
+	WM9712_HP_MIXER_CTRL("Phone Bypass Switch", HPR_MIXER, 3),
+	WM9712_HP_MIXER_CTRL("Line Bypass Switch", HPR_MIXER, 2),
+	WM9712_HP_MIXER_CTRL("PCM Playback Switch", HPR_MIXER, 1),
+	WM9712_HP_MIXER_CTRL("Mic Sidetone Switch", HPR_MIXER, 0),
 };
 
 /* Speaker Mixer */
@@ -299,12 +336,10 @@ SND_SOC_DAPM_MUX("Right Mic Select Source", SND_SOC_NOPM, 0, 0,
 SND_SOC_DAPM_MUX("Differential Source", SND_SOC_NOPM, 0, 0,
 	&wm9712_diff_sel_controls),
 SND_SOC_DAPM_MIXER("AC97 Mixer", SND_SOC_NOPM, 0, 0, NULL, 0),
-SND_SOC_DAPM_MIXER_E("Left HP Mixer", AC97_INT_PAGING, 9, 1,
-	&wm9712_hpl_mixer_controls[0], ARRAY_SIZE(wm9712_hpl_mixer_controls),
-	mixer_event, SND_SOC_DAPM_POST_REG),
-SND_SOC_DAPM_MIXER_E("Right HP Mixer", AC97_INT_PAGING, 8, 1,
-	&wm9712_hpr_mixer_controls[0], ARRAY_SIZE(wm9712_hpr_mixer_controls),
-	 mixer_event, SND_SOC_DAPM_POST_REG),
+SND_SOC_DAPM_MIXER("Left HP Mixer", AC97_INT_PAGING, 9, 1,
+	&wm9712_hpl_mixer_controls[0], ARRAY_SIZE(wm9712_hpl_mixer_controls)),
+SND_SOC_DAPM_MIXER("Right HP Mixer", AC97_INT_PAGING, 8, 1,
+	&wm9712_hpr_mixer_controls[0], ARRAY_SIZE(wm9712_hpr_mixer_controls)),
 SND_SOC_DAPM_MIXER("Phone Mixer", AC97_INT_PAGING, 6, 1,
 	&wm9712_phone_mixer_controls[0], ARRAY_SIZE(wm9712_phone_mixer_controls)),
 SND_SOC_DAPM_MIXER("Speaker Mixer", AC97_INT_PAGING, 7, 1,
@@ -450,12 +485,13 @@ static const struct snd_soc_dapm_route wm9712_audio_map[] = {
 static unsigned int ac97_read(struct snd_soc_codec *codec,
 	unsigned int reg)
 {
+	struct wm9712_priv *wm9712 = snd_soc_codec_get_drvdata(codec);
 	u16 *cache = codec->reg_cache;
 
 	if (reg == AC97_RESET || reg == AC97_GPIO_STATUS ||
 		reg == AC97_VENDOR_ID1 || reg == AC97_VENDOR_ID2 ||
 		reg == AC97_REC_GAIN)
-		return soc_ac97_ops->read(codec->ac97, reg);
+		return soc_ac97_ops->read(wm9712->ac97, reg);
 	else {
 		reg = reg >> 1;
 
@@ -469,10 +505,10 @@ static unsigned int ac97_read(struct snd_soc_codec *codec,
 static int ac97_write(struct snd_soc_codec *codec, unsigned int reg,
 	unsigned int val)
 {
+	struct wm9712_priv *wm9712 = snd_soc_codec_get_drvdata(codec);
 	u16 *cache = codec->reg_cache;
 
-	if (reg < 0x7c)
-		soc_ac97_ops->write(codec->ac97, reg, val);
+	soc_ac97_ops->write(wm9712->ac97, reg, val);
 	reg = reg >> 1;
 	if (reg < (ARRAY_SIZE(wm9712_reg)))
 		cache[reg] = val;
@@ -532,7 +568,6 @@ static const struct snd_soc_dai_ops wm9712_dai_ops_aux = {
 static struct snd_soc_dai_driver wm9712_dai[] = {
 {
 	.name = "wm9712-hifi",
-	.ac97_control = 1,
 	.playback = {
 		.stream_name = "HiFi Playback",
 		.channels_min = 1,
@@ -581,40 +616,35 @@ static int wm9712_set_bias_level(struct snd_soc_codec *codec,
 
 static int wm9712_reset(struct snd_soc_codec *codec, int try_warm)
 {
+	struct wm9712_priv *wm9712 = snd_soc_codec_get_drvdata(codec);
+
 	if (try_warm && soc_ac97_ops->warm_reset) {
-		soc_ac97_ops->warm_reset(codec->ac97);
+		soc_ac97_ops->warm_reset(wm9712->ac97);
 		if (ac97_read(codec, 0) == wm9712_reg[0])
 			return 1;
 	}
 
-	soc_ac97_ops->reset(codec->ac97);
+	soc_ac97_ops->reset(wm9712->ac97);
 	if (soc_ac97_ops->warm_reset)
-		soc_ac97_ops->warm_reset(codec->ac97);
+		soc_ac97_ops->warm_reset(wm9712->ac97);
 	if (ac97_read(codec, 0) != wm9712_reg[0])
 		goto err;
 	return 0;
 
 err:
-	printk(KERN_ERR "WM9712 AC97 reset failed\n");
+	dev_err(codec->dev, "Failed to reset: AC97 link error\n");
 	return -EIO;
 }
 
-static int wm9712_soc_suspend(struct snd_soc_codec *codec)
-{
-	wm9712_set_bias_level(codec, SND_SOC_BIAS_OFF);
-	return 0;
-}
-
 static int wm9712_soc_resume(struct snd_soc_codec *codec)
 {
+	struct wm9712_priv *wm9712 = snd_soc_codec_get_drvdata(codec);
 	int i, ret;
 	u16 *cache = codec->reg_cache;
 
 	ret = wm9712_reset(codec, 1);
-	if (ret < 0) {
-		printk(KERN_ERR "could not reset AC97 codec\n");
+	if (ret < 0)
 		return ret;
-	}
 
 	wm9712_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
 
@@ -624,7 +654,7 @@ static int wm9712_soc_resume(struct snd_soc_codec *codec)
 			if (i == AC97_INT_PAGING || i == AC97_POWERDOWN ||
 			    (i > 0x58 && i != 0x5c))
 				continue;
-			soc_ac97_ops->write(codec->ac97, i, cache[i>>1]);
+			soc_ac97_ops->write(wm9712->ac97, i, cache[i>>1]);
 		}
 	}
 
@@ -633,52 +663,53 @@ static int wm9712_soc_resume(struct snd_soc_codec *codec)
 
 static int wm9712_soc_probe(struct snd_soc_codec *codec)
 {
+	struct wm9712_priv *wm9712 = snd_soc_codec_get_drvdata(codec);
 	int ret = 0;
 
-	ret = snd_soc_new_ac97_codec(codec, soc_ac97_ops, 0);
-	if (ret < 0) {
-		printk(KERN_ERR "wm9712: failed to register AC97 codec\n");
+	wm9712->ac97 = snd_soc_new_ac97_codec(codec);
+	if (IS_ERR(wm9712->ac97)) {
+		ret = PTR_ERR(wm9712->ac97);
+		dev_err(codec->dev, "Failed to register AC97 codec: %d\n", ret);
 		return ret;
 	}
 
 	ret = wm9712_reset(codec, 0);
-	if (ret < 0) {
-		printk(KERN_ERR "Failed to reset WM9712: AC97 link error\n");
+	if (ret < 0)
 		goto reset_err;
-	}
 
 	/* set alc mux to none */
 	ac97_write(codec, AC97_VIDEO, ac97_read(codec, AC97_VIDEO) | 0x3000);
 
-	wm9712_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
-	snd_soc_add_codec_controls(codec, wm9712_snd_ac97_controls,
-				ARRAY_SIZE(wm9712_snd_ac97_controls));
-
 	return 0;
 
 reset_err:
-	snd_soc_free_ac97_codec(codec);
+	snd_soc_free_ac97_codec(wm9712->ac97);
 	return ret;
 }
 
 static int wm9712_soc_remove(struct snd_soc_codec *codec)
 {
-	snd_soc_free_ac97_codec(codec);
+	struct wm9712_priv *wm9712 = snd_soc_codec_get_drvdata(codec);
+
+	snd_soc_free_ac97_codec(wm9712->ac97);
 	return 0;
 }
 
 static struct snd_soc_codec_driver soc_codec_dev_wm9712 = {
 	.probe = 	wm9712_soc_probe,
 	.remove = 	wm9712_soc_remove,
-	.suspend =	wm9712_soc_suspend,
 	.resume =	wm9712_soc_resume,
 	.read = ac97_read,
 	.write = ac97_write,
 	.set_bias_level = wm9712_set_bias_level,
+	.suspend_bias_off = true,
 	.reg_cache_size = ARRAY_SIZE(wm9712_reg),
 	.reg_word_size = sizeof(u16),
 	.reg_cache_step = 2,
 	.reg_cache_default = wm9712_reg,
+
+	.controls = wm9712_snd_ac97_controls,
+	.num_controls = ARRAY_SIZE(wm9712_snd_ac97_controls),
 	.dapm_widgets = wm9712_dapm_widgets,
 	.num_dapm_widgets = ARRAY_SIZE(wm9712_dapm_widgets),
 	.dapm_routes = wm9712_audio_map,
@@ -687,6 +718,16 @@ static struct snd_soc_codec_driver soc_codec_dev_wm9712 = {
 
 static int wm9712_probe(struct platform_device *pdev)
 {
+	struct wm9712_priv *wm9712;
+
+	wm9712 = devm_kzalloc(&pdev->dev, sizeof(*wm9712), GFP_KERNEL);
+	if (wm9712 == NULL)
+		return -ENOMEM;
+
+	mutex_init(&wm9712->lock);
+
+	platform_set_drvdata(pdev, wm9712);
+
 	return snd_soc_register_codec(&pdev->dev,
 			&soc_codec_dev_wm9712, wm9712_dai, ARRAY_SIZE(wm9712_dai));
 }
diff --git a/sound/soc/codecs/wm9713.c b/sound/soc/codecs/wm9713.c
index bddee30a4bc7..5df7f6d12bef 100644
--- a/sound/soc/codecs/wm9713.c
+++ b/sound/soc/codecs/wm9713.c
@@ -30,7 +30,10 @@
 #include "wm9713.h"
 
 struct wm9713_priv {
+	struct snd_ac97 *ac97;
 	u32 pll_in; /* PLL input frequency */
+	unsigned int hp_mixer[2];
+	struct mutex lock;
 };
 
 static unsigned int ac97_read(struct snd_soc_codec *codec,
@@ -59,13 +62,10 @@ static const u16 wm9713_reg[] = {
 	0x0000, 0x0000, 0x0000, 0x0000,
 	0x0000, 0x0000, 0x0000, 0x0006,
 	0x0001, 0x0000, 0x574d, 0x4c13,
-	0x0000, 0x0000, 0x0000
 };
 
-/* virtual HP mixers regs */
-#define HPL_MIXER	0x80
-#define HPR_MIXER	0x82
-#define MICB_MUX	0x82
+#define HPL_MIXER 0
+#define HPR_MIXER 1
 
 static const char *wm9713_mic_mixer[] = {"Stereo", "Mic 1", "Mic 2", "Mute"};
 static const char *wm9713_rec_mux[] = {"Stereo", "Left", "Right", "Mute"};
@@ -110,7 +110,7 @@ SOC_ENUM_SINGLE(AC97_REC_GAIN_MIC, 10, 8, wm9713_dac_inv), /* dac invert 2 15 */
 SOC_ENUM_SINGLE(AC97_GENERAL_PURPOSE, 15, 2, wm9713_bass), /* bass control 16 */
 SOC_ENUM_SINGLE(AC97_PCI_SVID, 5, 2, wm9713_ng_type), /* noise gate type 17 */
 SOC_ENUM_SINGLE(AC97_3D_CONTROL, 12, 3, wm9713_mic_select), /* mic selection 18 */
-SOC_ENUM_SINGLE(MICB_MUX, 0, 2, wm9713_micb_select), /* mic selection 19 */
+SOC_ENUM_SINGLE_VIRT(2, wm9713_micb_select), /* mic selection 19 */
 };
 
 static const DECLARE_TLV_DB_SCALE(out_tlv, -4650, 150, 0);
@@ -234,6 +234,14 @@ static int wm9713_voice_shutdown(struct snd_soc_dapm_widget *w,
 	return 0;
 }
 
+static const unsigned int wm9713_mixer_mute_regs[] = {
+	AC97_PC_BEEP,
+	AC97_MASTER_TONE,
+	AC97_PHONE,
+	AC97_REC_SEL,
+	AC97_PCM,
+	AC97_AUX,
+};
 
 /* We have to create a fake left and right HP mixers because
  * the codec only has a single control that is shared by both channels.
@@ -241,73 +249,95 @@ static int wm9713_voice_shutdown(struct snd_soc_dapm_widget *w,
  * register map, thus we add a new (virtual) register to help determine the
  * audio route within the device.
  */
-static int mixer_event(struct snd_soc_dapm_widget *w,
-	struct snd_kcontrol *kcontrol, int event)
+static int wm9713_hp_mixer_put(struct snd_kcontrol *kcontrol,
+	struct snd_ctl_elem_value *ucontrol)
 {
-	u16 l, r, beep, tone, phone, rec, pcm, aux;
-
-	l = ac97_read(w->codec, HPL_MIXER);
-	r = ac97_read(w->codec, HPR_MIXER);
-	beep = ac97_read(w->codec, AC97_PC_BEEP);
-	tone = ac97_read(w->codec, AC97_MASTER_TONE);
-	phone = ac97_read(w->codec, AC97_PHONE);
-	rec = ac97_read(w->codec, AC97_REC_SEL);
-	pcm = ac97_read(w->codec, AC97_PCM);
-	aux = ac97_read(w->codec, AC97_AUX);
-
-	if (event & SND_SOC_DAPM_PRE_REG)
-		return 0;
-	if ((l & 0x1) || (r & 0x1))
-		ac97_write(w->codec, AC97_PC_BEEP, beep & 0x7fff);
+	struct snd_soc_dapm_context *dapm = snd_soc_dapm_kcontrol_dapm(kcontrol);
+	struct snd_soc_codec *codec = snd_soc_dapm_to_codec(dapm);
+	struct wm9713_priv *wm9713 = snd_soc_codec_get_drvdata(codec);
+	unsigned int val = ucontrol->value.enumerated.item[0];
+	struct soc_mixer_control *mc =
+		(struct soc_mixer_control *)kcontrol->private_value;
+	unsigned int mixer, mask, shift, old;
+	struct snd_soc_dapm_update update;
+	bool change;
+
+	mixer = mc->shift >> 8;
+	shift = mc->shift & 0xff;
+	mask = (1 << shift);
+
+	mutex_lock(&wm9713->lock);
+	old = wm9713->hp_mixer[mixer];
+	if (ucontrol->value.enumerated.item[0])
+		wm9713->hp_mixer[mixer] |= mask;
 	else
-		ac97_write(w->codec, AC97_PC_BEEP, beep | 0x8000);
+		wm9713->hp_mixer[mixer] &= ~mask;
+
+	change = old != wm9713->hp_mixer[mixer];
+	if (change) {
+		update.kcontrol = kcontrol;
+		update.reg = wm9713_mixer_mute_regs[shift];
+		update.mask = 0x8000;
+		if ((wm9713->hp_mixer[0] & mask) ||
+		    (wm9713->hp_mixer[1] & mask))
+			update.val = 0x0;
+		else
+			update.val = 0x8000;
+
+		snd_soc_dapm_mixer_update_power(dapm, kcontrol, val,
+			&update);
+	}
 
-	if ((l & 0x2) || (r & 0x2))
-		ac97_write(w->codec, AC97_MASTER_TONE, tone & 0x7fff);
-	else
-		ac97_write(w->codec, AC97_MASTER_TONE, tone | 0x8000);
+	mutex_unlock(&wm9713->lock);
 
-	if ((l & 0x4) || (r & 0x4))
-		ac97_write(w->codec, AC97_PHONE, phone & 0x7fff);
-	else
-		ac97_write(w->codec, AC97_PHONE, phone | 0x8000);
+	return change;
+}
 
-	if ((l & 0x8) || (r & 0x8))
-		ac97_write(w->codec, AC97_REC_SEL, rec & 0x7fff);
-	else
-		ac97_write(w->codec, AC97_REC_SEL, rec | 0x8000);
+static int wm9713_hp_mixer_get(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_codec *codec = snd_soc_dapm_to_codec(dapm);
+	struct wm9713_priv *wm9713 = snd_soc_codec_get_drvdata(codec);
+	struct soc_mixer_control *mc =
+		(struct soc_mixer_control *)kcontrol->private_value;
+	unsigned int mixer, shift;
 
-	if ((l & 0x10) || (r & 0x10))
-		ac97_write(w->codec, AC97_PCM, pcm & 0x7fff);
-	else
-		ac97_write(w->codec, AC97_PCM, pcm | 0x8000);
+	mixer = mc->shift >> 8;
+	shift = mc->shift & 0xff;
 
-	if ((l & 0x20) || (r & 0x20))
-		ac97_write(w->codec, AC97_AUX, aux & 0x7fff);
-	else
-		ac97_write(w->codec, AC97_AUX, aux | 0x8000);
+	ucontrol->value.enumerated.item[0] =
+		(wm9713->hp_mixer[mixer] >> shift) & 1;
 
 	return 0;
 }
 
+#define WM9713_HP_MIXER_CTRL(xname, xmixer, xshift) { \
+	.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
+	.info = snd_soc_info_volsw, \
+	.get = wm9713_hp_mixer_get, .put = wm9713_hp_mixer_put, \
+	.private_value = SOC_DOUBLE_VALUE(SND_SOC_NOPM, \
+		xshift, xmixer, 1, 0, 0) \
+}
+
 /* Left Headphone Mixers */
 static const struct snd_kcontrol_new wm9713_hpl_mixer_controls[] = {
-SOC_DAPM_SINGLE("Beep Playback Switch", HPL_MIXER, 5, 1, 0),
-SOC_DAPM_SINGLE("Voice Playback Switch", HPL_MIXER, 4, 1, 0),
-SOC_DAPM_SINGLE("Aux Playback Switch", HPL_MIXER, 3, 1, 0),
-SOC_DAPM_SINGLE("PCM Playback Switch", HPL_MIXER, 2, 1, 0),
-SOC_DAPM_SINGLE("MonoIn Playback Switch", HPL_MIXER, 1, 1, 0),
-SOC_DAPM_SINGLE("Bypass Playback Switch", HPL_MIXER, 0, 1, 0),
+WM9713_HP_MIXER_CTRL("Beep Playback Switch", HPL_MIXER, 5),
+WM9713_HP_MIXER_CTRL("Voice Playback Switch", HPL_MIXER, 4),
+WM9713_HP_MIXER_CTRL("Aux Playback Switch", HPL_MIXER, 3),
+WM9713_HP_MIXER_CTRL("PCM Playback Switch", HPL_MIXER, 2),
+WM9713_HP_MIXER_CTRL("MonoIn Playback Switch", HPL_MIXER, 1),
+WM9713_HP_MIXER_CTRL("Bypass Playback Switch", HPL_MIXER, 0),
 };
 
 /* Right Headphone Mixers */
 static const struct snd_kcontrol_new wm9713_hpr_mixer_controls[] = {
-SOC_DAPM_SINGLE("Beep Playback Switch", HPR_MIXER, 5, 1, 0),
-SOC_DAPM_SINGLE("Voice Playback Switch", HPR_MIXER, 4, 1, 0),
-SOC_DAPM_SINGLE("Aux Playback Switch", HPR_MIXER, 3, 1, 0),
-SOC_DAPM_SINGLE("PCM Playback Switch", HPR_MIXER, 2, 1, 0),
-SOC_DAPM_SINGLE("MonoIn Playback Switch", HPR_MIXER, 1, 1, 0),
-SOC_DAPM_SINGLE("Bypass Playback Switch", HPR_MIXER, 0, 1, 0),
+WM9713_HP_MIXER_CTRL("Beep Playback Switch", HPR_MIXER, 5),
+WM9713_HP_MIXER_CTRL("Voice Playback Switch", HPR_MIXER, 4),
+WM9713_HP_MIXER_CTRL("Aux Playback Switch", HPR_MIXER, 3),
+WM9713_HP_MIXER_CTRL("PCM Playback Switch", HPR_MIXER, 2),
+WM9713_HP_MIXER_CTRL("MonoIn Playback Switch", HPR_MIXER, 1),
+WM9713_HP_MIXER_CTRL("Bypass Playback Switch", HPR_MIXER, 0),
 };
 
 /* headphone capture mux */
@@ -429,12 +459,10 @@ SND_SOC_DAPM_MUX("Mic A Source", SND_SOC_NOPM, 0, 0,
 	&wm9713_mic_sel_mux_controls),
 SND_SOC_DAPM_MUX("Mic B Source", SND_SOC_NOPM, 0, 0,
 	&wm9713_micb_sel_mux_controls),
-SND_SOC_DAPM_MIXER_E("Left HP Mixer", AC97_EXTENDED_MID, 3, 1,
-	&wm9713_hpl_mixer_controls[0], ARRAY_SIZE(wm9713_hpl_mixer_controls),
-	mixer_event, SND_SOC_DAPM_POST_REG),
-SND_SOC_DAPM_MIXER_E("Right HP Mixer", AC97_EXTENDED_MID, 2, 1,
-	&wm9713_hpr_mixer_controls[0], ARRAY_SIZE(wm9713_hpr_mixer_controls),
-	mixer_event, SND_SOC_DAPM_POST_REG),
+SND_SOC_DAPM_MIXER("Left HP Mixer", AC97_EXTENDED_MID, 3, 1,
+	&wm9713_hpl_mixer_controls[0], ARRAY_SIZE(wm9713_hpl_mixer_controls)),
+SND_SOC_DAPM_MIXER("Right HP Mixer", AC97_EXTENDED_MID, 2, 1,
+	&wm9713_hpr_mixer_controls[0], ARRAY_SIZE(wm9713_hpr_mixer_controls)),
 SND_SOC_DAPM_MIXER("Mono Mixer", AC97_EXTENDED_MID, 0, 1,
 	&wm9713_mono_mixer_controls[0], ARRAY_SIZE(wm9713_mono_mixer_controls)),
 SND_SOC_DAPM_MIXER("Speaker Mixer", AC97_EXTENDED_MID, 1, 1,
@@ -647,12 +675,13 @@ static const struct snd_soc_dapm_route wm9713_audio_map[] = {
 static unsigned int ac97_read(struct snd_soc_codec *codec,
 	unsigned int reg)
 {
+	struct wm9713_priv *wm9713 = snd_soc_codec_get_drvdata(codec);
 	u16 *cache = codec->reg_cache;
 
 	if (reg == AC97_RESET || reg == AC97_GPIO_STATUS ||
 		reg == AC97_VENDOR_ID1 || reg == AC97_VENDOR_ID2 ||
 		reg == AC97_CD)
-		return soc_ac97_ops->read(codec->ac97, reg);
+		return soc_ac97_ops->read(wm9713->ac97, reg);
 	else {
 		reg = reg >> 1;
 
@@ -666,9 +695,10 @@ static unsigned int ac97_read(struct snd_soc_codec *codec,
 static int ac97_write(struct snd_soc_codec *codec, unsigned int reg,
 	unsigned int val)
 {
+	struct wm9713_priv *wm9713 = snd_soc_codec_get_drvdata(codec);
+
 	u16 *cache = codec->reg_cache;
-	if (reg < 0x7c)
-		soc_ac97_ops->write(codec->ac97, reg, val);
+	soc_ac97_ops->write(wm9713->ac97, reg, val);
 	reg = reg >> 1;
 	if (reg < (ARRAY_SIZE(wm9713_reg)))
 		cache[reg] = val;
@@ -689,7 +719,8 @@ struct _pll_div {
  * to allow rounding later */
 #define FIXED_PLL_SIZE ((1 << 22) * 10)
 
-static void pll_factors(struct _pll_div *pll_div, unsigned int source)
+static void pll_factors(struct snd_soc_codec *codec,
+	struct _pll_div *pll_div, unsigned int source)
 {
 	u64 Kpart;
 	unsigned int K, Ndiv, Nmod, target;
@@ -724,7 +755,7 @@ static void pll_factors(struct _pll_div *pll_div, unsigned int source)
 
 	Ndiv = target / source;
 	if ((Ndiv < 5) || (Ndiv > 12))
-		printk(KERN_WARNING
+		dev_warn(codec->dev,
 			"WM9713 PLL N value %u out of recommended range!\n",
 			Ndiv);
 
@@ -768,7 +799,7 @@ static int wm9713_set_pll(struct snd_soc_codec *codec,
 		return 0;
 	}
 
-	pll_factors(&pll_div, freq_in);
+	pll_factors(codec, &pll_div, freq_in);
 
 	if (pll_div.k == 0) {
 		reg = (pll_div.n << 12) | (pll_div.lf << 11) |
@@ -1049,7 +1080,6 @@ static const struct snd_soc_dai_ops wm9713_dai_ops_voice = {
 static struct snd_soc_dai_driver wm9713_dai[] = {
 {
 	.name = "wm9713-hifi",
-	.ac97_control = 1,
 	.playback = {
 		.stream_name = "HiFi Playback",
 		.channels_min = 1,
@@ -1095,17 +1125,22 @@ static struct snd_soc_dai_driver wm9713_dai[] = {
 
 int wm9713_reset(struct snd_soc_codec *codec, int try_warm)
 {
+	struct wm9713_priv *wm9713 = snd_soc_codec_get_drvdata(codec);
+
 	if (try_warm && soc_ac97_ops->warm_reset) {
-		soc_ac97_ops->warm_reset(codec->ac97);
+		soc_ac97_ops->warm_reset(wm9713->ac97);
 		if (ac97_read(codec, 0) == wm9713_reg[0])
 			return 1;
 	}
 
-	soc_ac97_ops->reset(codec->ac97);
+	soc_ac97_ops->reset(wm9713->ac97);
 	if (soc_ac97_ops->warm_reset)
-		soc_ac97_ops->warm_reset(codec->ac97);
-	if (ac97_read(codec, 0) != wm9713_reg[0])
+		soc_ac97_ops->warm_reset(wm9713->ac97);
+	if (ac97_read(codec, 0) != wm9713_reg[0]) {
+		dev_err(codec->dev, "Failed to reset: AC97 link error\n");
 		return -EIO;
+	}
+
 	return 0;
 }
 EXPORT_SYMBOL_GPL(wm9713_reset);
@@ -1163,10 +1198,8 @@ static int wm9713_soc_resume(struct snd_soc_codec *codec)
 	u16 *cache = codec->reg_cache;
 
 	ret = wm9713_reset(codec, 1);
-	if (ret < 0) {
-		printk(KERN_ERR "could not reset AC97 codec\n");
+	if (ret < 0)
 		return ret;
-	}
 
 	wm9713_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
 
@@ -1180,7 +1213,7 @@ static int wm9713_soc_resume(struct snd_soc_codec *codec)
 			if (i == AC97_POWERDOWN || i == AC97_EXTENDED_MID ||
 				i == AC97_EXTENDED_MSTATUS || i > 0x66)
 				continue;
-			soc_ac97_ops->write(codec->ac97, i, cache[i>>1]);
+			soc_ac97_ops->write(wm9713->ac97, i, cache[i>>1]);
 		}
 	}
 
@@ -1189,50 +1222,36 @@ static int wm9713_soc_resume(struct snd_soc_codec *codec)
 
 static int wm9713_soc_probe(struct snd_soc_codec *codec)
 {
-	struct wm9713_priv *wm9713;
+	struct wm9713_priv *wm9713 = snd_soc_codec_get_drvdata(codec);
 	int ret = 0, reg;
 
-	wm9713 = kzalloc(sizeof(struct wm9713_priv), GFP_KERNEL);
-	if (wm9713 == NULL)
-		return -ENOMEM;
-	snd_soc_codec_set_drvdata(codec, wm9713);
-
-	ret = snd_soc_new_ac97_codec(codec, soc_ac97_ops, 0);
-	if (ret < 0)
-		goto codec_err;
+	wm9713->ac97 = snd_soc_new_ac97_codec(codec);
+	if (IS_ERR(wm9713->ac97))
+		return PTR_ERR(wm9713->ac97);
 
 	/* do a cold reset for the controller and then try
 	 * a warm reset followed by an optional cold reset for codec */
 	wm9713_reset(codec, 0);
 	ret = wm9713_reset(codec, 1);
-	if (ret < 0) {
-		printk(KERN_ERR "Failed to reset WM9713: AC97 link error\n");
+	if (ret < 0)
 		goto reset_err;
-	}
-
-	wm9713_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
 
 	/* unmute the adc - move to kcontrol */
 	reg = ac97_read(codec, AC97_CD) & 0x7fff;
 	ac97_write(codec, AC97_CD, reg);
 
-	snd_soc_add_codec_controls(codec, wm9713_snd_ac97_controls,
-				ARRAY_SIZE(wm9713_snd_ac97_controls));
-
 	return 0;
 
 reset_err:
-	snd_soc_free_ac97_codec(codec);
-codec_err:
-	kfree(wm9713);
+	snd_soc_free_ac97_codec(wm9713->ac97);
 	return ret;
 }
 
 static int wm9713_soc_remove(struct snd_soc_codec *codec)
 {
 	struct wm9713_priv *wm9713 = snd_soc_codec_get_drvdata(codec);
-	snd_soc_free_ac97_codec(codec);
-	kfree(wm9713);
+
+	snd_soc_free_ac97_codec(wm9713->ac97);
 	return 0;
 }
 
@@ -1248,6 +1267,9 @@ static struct snd_soc_codec_driver soc_codec_dev_wm9713 = {
 	.reg_word_size = sizeof(u16),
 	.reg_cache_step = 2,
 	.reg_cache_default = wm9713_reg,
+
+	.controls = wm9713_snd_ac97_controls,
+	.num_controls = ARRAY_SIZE(wm9713_snd_ac97_controls),
 	.dapm_widgets = wm9713_dapm_widgets,
 	.num_dapm_widgets = ARRAY_SIZE(wm9713_dapm_widgets),
 	.dapm_routes = wm9713_audio_map,
@@ -1256,6 +1278,16 @@ static struct snd_soc_codec_driver soc_codec_dev_wm9713 = {
 
 static int wm9713_probe(struct platform_device *pdev)
 {
+	struct wm9713_priv *wm9713;
+
+	wm9713 = devm_kzalloc(&pdev->dev, sizeof(*wm9713), GFP_KERNEL);
+	if (wm9713 == NULL)
+		return -ENOMEM;
+
+	mutex_init(&wm9713->lock);
+
+	platform_set_drvdata(pdev, wm9713);
+
 	return snd_soc_register_codec(&pdev->dev,
 			&soc_codec_dev_wm9713, wm9713_dai, ARRAY_SIZE(wm9713_dai));
 }
diff --git a/sound/soc/codecs/wm_adsp.c b/sound/soc/codecs/wm_adsp.c
index 67124783558a..720d6e852986 100644
--- a/sound/soc/codecs/wm_adsp.c
+++ b/sound/soc/codecs/wm_adsp.c
@@ -21,6 +21,7 @@
 #include <linux/regmap.h>
 #include <linux/regulator/consumer.h>
 #include <linux/slab.h>
+#include <linux/vmalloc.h>
 #include <linux/workqueue.h>
 #include <sound/core.h>
 #include <sound/pcm.h>
@@ -169,11 +170,12 @@ static struct wm_adsp_buf *wm_adsp_buf_alloc(const void *src, size_t len,
 	if (buf == NULL)
 		return NULL;
 
-	buf->buf = kmemdup(src, len, GFP_KERNEL | GFP_DMA);
+	buf->buf = vmalloc(len);
 	if (!buf->buf) {
-		kfree(buf);
+		vfree(buf);
 		return NULL;
 	}
+	memcpy(buf->buf, src, len);
 
 	if (list)
 		list_add_tail(&buf->list, list);
@@ -188,7 +190,7 @@ static void wm_adsp_buf_free(struct list_head *list)
 							   struct wm_adsp_buf,
 							   list);
 		list_del(&buf->list);
-		kfree(buf->buf);
+		vfree(buf->buf);
 		kfree(buf);
 	}
 }
@@ -684,38 +686,24 @@ static int wm_adsp_load(struct wm_adsp *dsp)
 		}
 
 		if (reg) {
-			size_t to_write = PAGE_SIZE;
-			size_t remain = le32_to_cpu(region->len);
-			const u8 *data = region->data;
-
-			while (remain > 0) {
-				if (remain < PAGE_SIZE)
-					to_write = remain;
-
-				buf = wm_adsp_buf_alloc(data,
-							to_write,
-							&buf_list);
-				if (!buf) {
-					adsp_err(dsp, "Out of memory\n");
-					ret = -ENOMEM;
-					goto out_fw;
-				}
-
-				ret = regmap_raw_write_async(regmap, reg,
-							     buf->buf,
-							     to_write);
-				if (ret != 0) {
-					adsp_err(dsp,
-						"%s.%d: Failed to write %zd bytes at %d in %s: %d\n",
-						file, regions,
-						to_write, offset,
-						region_name, ret);
-					goto out_fw;
-				}
+			buf = wm_adsp_buf_alloc(region->data,
+						le32_to_cpu(region->len),
+						&buf_list);
+			if (!buf) {
+				adsp_err(dsp, "Out of memory\n");
+				ret = -ENOMEM;
+				goto out_fw;
+			}
 
-				data += to_write;
-				reg += to_write / 2;
-				remain -= to_write;
+			ret = regmap_raw_write_async(regmap, reg, buf->buf,
+						     le32_to_cpu(region->len));
+			if (ret != 0) {
+				adsp_err(dsp,
+					"%s.%d: Failed to write %d bytes at %d in %s: %d\n",
+					file, regions,
+					le32_to_cpu(region->len), offset,
+					region_name, ret);
+				goto out_fw;
 			}
 		}
 
@@ -1065,8 +1053,10 @@ static int wm_adsp_setup_algs(struct wm_adsp *dsp)
 				  be32_to_cpu(adsp1_alg[i].zm));
 
 			region = kzalloc(sizeof(*region), GFP_KERNEL);
-			if (!region)
-				return -ENOMEM;
+			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);
@@ -1083,8 +1073,10 @@ static int wm_adsp_setup_algs(struct wm_adsp *dsp)
 			}
 
 			region = kzalloc(sizeof(*region), GFP_KERNEL);
-			if (!region)
-				return -ENOMEM;
+			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);
@@ -1113,8 +1105,10 @@ static int wm_adsp_setup_algs(struct wm_adsp *dsp)
 				  be32_to_cpu(adsp2_alg[i].zm));
 
 			region = kzalloc(sizeof(*region), GFP_KERNEL);
-			if (!region)
-				return -ENOMEM;
+			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);
@@ -1131,8 +1125,10 @@ static int wm_adsp_setup_algs(struct wm_adsp *dsp)
 			}
 
 			region = kzalloc(sizeof(*region), GFP_KERNEL);
-			if (!region)
-				return -ENOMEM;
+			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);
@@ -1149,8 +1145,10 @@ static int wm_adsp_setup_algs(struct wm_adsp *dsp)
 			}
 
 			region = kzalloc(sizeof(*region), GFP_KERNEL);
-			if (!region)
-				return -ENOMEM;
+			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);
@@ -1595,13 +1593,6 @@ static void wm_adsp2_boot_work(struct work_struct *work)
 	if (ret != 0)
 		goto err;
 
-	ret = regmap_update_bits_async(dsp->regmap,
-				       dsp->base + ADSP2_CONTROL,
-				       ADSP2_CORE_ENA,
-				       ADSP2_CORE_ENA);
-	if (ret != 0)
-		goto err;
-
 	dsp->running = true;
 
 	return;
@@ -1651,8 +1642,8 @@ int wm_adsp2_event(struct snd_soc_dapm_widget *w,
 
 		ret = regmap_update_bits(dsp->regmap,
 					 dsp->base + ADSP2_CONTROL,
-					 ADSP2_START,
-					 ADSP2_START);
+					 ADSP2_CORE_ENA | ADSP2_START,
+					 ADSP2_CORE_ENA | ADSP2_START);
 		if (ret != 0)
 			goto err;
 		break;
diff --git a/sound/soc/davinci/davinci-mcasp.c b/sound/soc/davinci/davinci-mcasp.c
index 0eed9b1b24e1..0dab382ba147 100644
--- a/sound/soc/davinci/davinci-mcasp.c
+++ b/sound/soc/davinci/davinci-mcasp.c
@@ -70,6 +70,7 @@ struct davinci_mcasp {
 	void __iomem *base;
 	u32 fifo_base;
 	struct device *dev;
+	struct snd_pcm_substream *substreams[2];
 
 	/* McASP specific data */
 	int	tdm_slots;
@@ -80,6 +81,7 @@ struct davinci_mcasp {
 	u8	bclk_div;
 	u16	bclk_lrclk_ratio;
 	int	streams;
+	u32	irq_request[2];
 
 	int	sysclk_freq;
 	bool	bclk_master;
@@ -90,6 +92,9 @@ struct davinci_mcasp {
 
 	bool	dat_port;
 
+	/* Used for comstraint setting on the second stream */
+	u32	channels;
+
 #ifdef CONFIG_PM_SLEEP
 	struct davinci_mcasp_context context;
 #endif
@@ -154,9 +159,16 @@ static bool mcasp_is_synchronous(struct davinci_mcasp *mcasp)
 
 static void mcasp_start_rx(struct davinci_mcasp *mcasp)
 {
+	if (mcasp->rxnumevt) {	/* enable FIFO */
+		u32 reg = mcasp->fifo_base + MCASP_RFIFOCTL_OFFSET;
+
+		mcasp_clr_bits(mcasp, reg, FIFO_ENABLE);
+		mcasp_set_bits(mcasp, reg, FIFO_ENABLE);
+	}
+
+	/* Start clocks */
 	mcasp_set_ctl_reg(mcasp, DAVINCI_MCASP_GBLCTLR_REG, RXHCLKRST);
 	mcasp_set_ctl_reg(mcasp, DAVINCI_MCASP_GBLCTLR_REG, RXCLKRST);
-
 	/*
 	 * When ASYNC == 0 the transmit and receive sections operate
 	 * synchronously from the transmit clock and frame sync. We need to make
@@ -167,74 +179,69 @@ static void mcasp_start_rx(struct davinci_mcasp *mcasp)
 		mcasp_set_ctl_reg(mcasp, DAVINCI_MCASP_GBLCTLX_REG, TXCLKRST);
 	}
 
+	/* Activate serializer(s) */
 	mcasp_set_ctl_reg(mcasp, DAVINCI_MCASP_GBLCTLR_REG, RXSERCLR);
-	mcasp_set_reg(mcasp, DAVINCI_MCASP_RXBUF_REG, 0);
-
-	mcasp_set_ctl_reg(mcasp, DAVINCI_MCASP_GBLCTLR_REG, RXSMRST);
-	mcasp_set_ctl_reg(mcasp, DAVINCI_MCASP_GBLCTLR_REG, RXFSRST);
-	mcasp_set_reg(mcasp, DAVINCI_MCASP_RXBUF_REG, 0);
-
+	/* Release RX state machine */
 	mcasp_set_ctl_reg(mcasp, DAVINCI_MCASP_GBLCTLR_REG, RXSMRST);
+	/* Release Frame Sync generator */
 	mcasp_set_ctl_reg(mcasp, DAVINCI_MCASP_GBLCTLR_REG, RXFSRST);
-
 	if (mcasp_is_synchronous(mcasp))
 		mcasp_set_ctl_reg(mcasp, DAVINCI_MCASP_GBLCTLX_REG, TXFSRST);
+
+	/* enable receive IRQs */
+	mcasp_set_bits(mcasp, DAVINCI_MCASP_EVTCTLR_REG,
+		       mcasp->irq_request[SNDRV_PCM_STREAM_CAPTURE]);
 }
 
 static void mcasp_start_tx(struct davinci_mcasp *mcasp)
 {
-	u8 offset = 0, i;
 	u32 cnt;
 
+	if (mcasp->txnumevt) {	/* enable FIFO */
+		u32 reg = mcasp->fifo_base + MCASP_WFIFOCTL_OFFSET;
+
+		mcasp_clr_bits(mcasp, reg, FIFO_ENABLE);
+		mcasp_set_bits(mcasp, reg, FIFO_ENABLE);
+	}
+
+	/* Start clocks */
 	mcasp_set_ctl_reg(mcasp, DAVINCI_MCASP_GBLCTLX_REG, TXHCLKRST);
 	mcasp_set_ctl_reg(mcasp, DAVINCI_MCASP_GBLCTLX_REG, TXCLKRST);
+	/* Activate serializer(s) */
 	mcasp_set_ctl_reg(mcasp, DAVINCI_MCASP_GBLCTLX_REG, TXSERCLR);
-	mcasp_set_reg(mcasp, DAVINCI_MCASP_TXBUF_REG, 0);
 
-	mcasp_set_ctl_reg(mcasp, DAVINCI_MCASP_GBLCTLX_REG, TXSMRST);
-	mcasp_set_ctl_reg(mcasp, DAVINCI_MCASP_GBLCTLX_REG, TXFSRST);
-	mcasp_set_reg(mcasp, DAVINCI_MCASP_TXBUF_REG, 0);
-	for (i = 0; i < mcasp->num_serializer; i++) {
-		if (mcasp->serial_dir[i] == TX_MODE) {
-			offset = i;
-			break;
-		}
-	}
-
-	/* wait for TX ready */
+	/* wait for XDATA to be cleared */
 	cnt = 0;
-	while (!(mcasp_get_reg(mcasp, DAVINCI_MCASP_XRSRCTL_REG(offset)) &
-		 TXSTATE) && (cnt < 100000))
+	while (!(mcasp_get_reg(mcasp, DAVINCI_MCASP_TXSTAT_REG) &
+		 ~XRDATA) && (cnt < 100000))
 		cnt++;
 
-	mcasp_set_reg(mcasp, DAVINCI_MCASP_TXBUF_REG, 0);
+	/* Release TX state machine */
+	mcasp_set_ctl_reg(mcasp, DAVINCI_MCASP_GBLCTLX_REG, TXSMRST);
+	/* Release Frame Sync generator */
+	mcasp_set_ctl_reg(mcasp, DAVINCI_MCASP_GBLCTLX_REG, TXFSRST);
+
+	/* enable transmit IRQs */
+	mcasp_set_bits(mcasp, DAVINCI_MCASP_EVTCTLX_REG,
+		       mcasp->irq_request[SNDRV_PCM_STREAM_PLAYBACK]);
 }
 
 static void davinci_mcasp_start(struct davinci_mcasp *mcasp, int stream)
 {
-	u32 reg;
-
 	mcasp->streams++;
 
-	if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
-		if (mcasp->txnumevt) {	/* enable FIFO */
-			reg = mcasp->fifo_base + MCASP_WFIFOCTL_OFFSET;
-			mcasp_clr_bits(mcasp, reg, FIFO_ENABLE);
-			mcasp_set_bits(mcasp, reg, FIFO_ENABLE);
-		}
+	if (stream == SNDRV_PCM_STREAM_PLAYBACK)
 		mcasp_start_tx(mcasp);
-	} else {
-		if (mcasp->rxnumevt) {	/* enable FIFO */
-			reg = mcasp->fifo_base + MCASP_RFIFOCTL_OFFSET;
-			mcasp_clr_bits(mcasp, reg, FIFO_ENABLE);
-			mcasp_set_bits(mcasp, reg, FIFO_ENABLE);
-		}
+	else
 		mcasp_start_rx(mcasp);
-	}
 }
 
 static void mcasp_stop_rx(struct davinci_mcasp *mcasp)
 {
+	/* disable IRQ sources */
+	mcasp_clr_bits(mcasp, DAVINCI_MCASP_EVTCTLR_REG,
+		       mcasp->irq_request[SNDRV_PCM_STREAM_CAPTURE]);
+
 	/*
 	 * In synchronous mode stop the TX clocks if no other stream is
 	 * running
@@ -244,12 +251,22 @@ static void mcasp_stop_rx(struct davinci_mcasp *mcasp)
 
 	mcasp_set_reg(mcasp, DAVINCI_MCASP_GBLCTLR_REG, 0);
 	mcasp_set_reg(mcasp, DAVINCI_MCASP_RXSTAT_REG, 0xFFFFFFFF);
+
+	if (mcasp->rxnumevt) {	/* disable FIFO */
+		u32 reg = mcasp->fifo_base + MCASP_RFIFOCTL_OFFSET;
+
+		mcasp_clr_bits(mcasp, reg, FIFO_ENABLE);
+	}
 }
 
 static void mcasp_stop_tx(struct davinci_mcasp *mcasp)
 {
 	u32 val = 0;
 
+	/* disable IRQ sources */
+	mcasp_clr_bits(mcasp, DAVINCI_MCASP_EVTCTLX_REG,
+		       mcasp->irq_request[SNDRV_PCM_STREAM_PLAYBACK]);
+
 	/*
 	 * In synchronous mode keep TX clocks running if the capture stream is
 	 * still running.
@@ -259,27 +276,92 @@ static void mcasp_stop_tx(struct davinci_mcasp *mcasp)
 
 	mcasp_set_reg(mcasp, DAVINCI_MCASP_GBLCTLX_REG, val);
 	mcasp_set_reg(mcasp, DAVINCI_MCASP_TXSTAT_REG, 0xFFFFFFFF);
+
+	if (mcasp->txnumevt) {	/* disable FIFO */
+		u32 reg = mcasp->fifo_base + MCASP_WFIFOCTL_OFFSET;
+
+		mcasp_clr_bits(mcasp, reg, FIFO_ENABLE);
+	}
 }
 
 static void davinci_mcasp_stop(struct davinci_mcasp *mcasp, int stream)
 {
-	u32 reg;
-
 	mcasp->streams--;
 
-	if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
-		if (mcasp->txnumevt) {	/* disable FIFO */
-			reg = mcasp->fifo_base + MCASP_WFIFOCTL_OFFSET;
-			mcasp_clr_bits(mcasp, reg, FIFO_ENABLE);
-		}
+	if (stream == SNDRV_PCM_STREAM_PLAYBACK)
 		mcasp_stop_tx(mcasp);
-	} else {
-		if (mcasp->rxnumevt) {	/* disable FIFO */
-			reg = mcasp->fifo_base + MCASP_RFIFOCTL_OFFSET;
-			mcasp_clr_bits(mcasp, reg, FIFO_ENABLE);
-		}
+	else
 		mcasp_stop_rx(mcasp);
+}
+
+static irqreturn_t davinci_mcasp_tx_irq_handler(int irq, void *data)
+{
+	struct davinci_mcasp *mcasp = (struct davinci_mcasp *)data;
+	struct snd_pcm_substream *substream;
+	u32 irq_mask = mcasp->irq_request[SNDRV_PCM_STREAM_PLAYBACK];
+	u32 handled_mask = 0;
+	u32 stat;
+
+	stat = mcasp_get_reg(mcasp, DAVINCI_MCASP_TXSTAT_REG);
+	if (stat & XUNDRN & irq_mask) {
+		dev_warn(mcasp->dev, "Transmit buffer underflow\n");
+		handled_mask |= XUNDRN;
+
+		substream = mcasp->substreams[SNDRV_PCM_STREAM_PLAYBACK];
+		if (substream) {
+			snd_pcm_stream_lock_irq(substream);
+			if (snd_pcm_running(substream))
+				snd_pcm_stop(substream, SNDRV_PCM_STATE_XRUN);
+			snd_pcm_stream_unlock_irq(substream);
+		}
 	}
+
+	if (!handled_mask)
+		dev_warn(mcasp->dev, "unhandled tx event. txstat: 0x%08x\n",
+			 stat);
+
+	if (stat & XRERR)
+		handled_mask |= XRERR;
+
+	/* Ack the handled event only */
+	mcasp_set_reg(mcasp, DAVINCI_MCASP_TXSTAT_REG, handled_mask);
+
+	return IRQ_RETVAL(handled_mask);
+}
+
+static irqreturn_t davinci_mcasp_rx_irq_handler(int irq, void *data)
+{
+	struct davinci_mcasp *mcasp = (struct davinci_mcasp *)data;
+	struct snd_pcm_substream *substream;
+	u32 irq_mask = mcasp->irq_request[SNDRV_PCM_STREAM_CAPTURE];
+	u32 handled_mask = 0;
+	u32 stat;
+
+	stat = mcasp_get_reg(mcasp, DAVINCI_MCASP_RXSTAT_REG);
+	if (stat & ROVRN & irq_mask) {
+		dev_warn(mcasp->dev, "Receive buffer overflow\n");
+		handled_mask |= ROVRN;
+
+		substream = mcasp->substreams[SNDRV_PCM_STREAM_CAPTURE];
+		if (substream) {
+			snd_pcm_stream_lock_irq(substream);
+			if (snd_pcm_running(substream))
+				snd_pcm_stop(substream, SNDRV_PCM_STATE_XRUN);
+			snd_pcm_stream_unlock_irq(substream);
+		}
+	}
+
+	if (!handled_mask)
+		dev_warn(mcasp->dev, "unhandled rx event. rxstat: 0x%08x\n",
+			 stat);
+
+	if (stat & XRERR)
+		handled_mask |= XRERR;
+
+	/* Ack the handled event only */
+	mcasp_set_reg(mcasp, DAVINCI_MCASP_RXSTAT_REG, handled_mask);
+
+	return IRQ_RETVAL(handled_mask);
 }
 
 static int davinci_mcasp_set_dai_fmt(struct snd_soc_dai *cpu_dai,
@@ -500,8 +582,17 @@ static int davinci_config_channel_size(struct davinci_mcasp *mcasp,
 	 * both left and right channels), so it has to be divided by number of
 	 * tdm-slots (for I2S - divided by 2).
 	 */
-	if (mcasp->bclk_lrclk_ratio)
-		word_length = mcasp->bclk_lrclk_ratio / mcasp->tdm_slots;
+	if (mcasp->bclk_lrclk_ratio) {
+		u32 slot_length = mcasp->bclk_lrclk_ratio / mcasp->tdm_slots;
+
+		/*
+		 * When we have more bclk then it is needed for the data, we
+		 * need to use the rotation to move the received samples to have
+		 * correct alignment.
+		 */
+		rx_rotate = (slot_length - word_length) / 4;
+		word_length = slot_length;
+	}
 
 	/* mapping of the XSSZ bit-field as described in the datasheet */
 	fmt = (word_length >> 1) - 1;
@@ -635,19 +726,29 @@ static int mcasp_common_hw_param(struct davinci_mcasp *mcasp, int stream,
 	return 0;
 }
 
-static int mcasp_i2s_hw_param(struct davinci_mcasp *mcasp, int stream)
+static int mcasp_i2s_hw_param(struct davinci_mcasp *mcasp, int stream,
+			      int channels)
 {
 	int i, active_slots;
+	int total_slots;
+	int active_serializers;
 	u32 mask = 0;
 	u32 busel = 0;
 
-	if ((mcasp->tdm_slots < 2) || (mcasp->tdm_slots > 32)) {
-		dev_err(mcasp->dev, "tdm slot %d not supported\n",
-			mcasp->tdm_slots);
-		return -EINVAL;
-	}
+	total_slots = mcasp->tdm_slots;
+
+	/*
+	 * If more than one serializer is needed, then use them with
+	 * their specified tdm_slots count. Otherwise, one serializer
+	 * can cope with the transaction using as many slots as channels
+	 * in the stream, requires channels symmetry
+	 */
+	active_serializers = (channels + total_slots - 1) / total_slots;
+	if (active_serializers == 1)
+		active_slots = channels;
+	else
+		active_slots = total_slots;
 
-	active_slots = (mcasp->tdm_slots > 31) ? 32 : mcasp->tdm_slots;
 	for (i = 0; i < active_slots; i++)
 		mask |= (1 << i);
 
@@ -659,12 +760,12 @@ static int mcasp_i2s_hw_param(struct davinci_mcasp *mcasp, int stream)
 	mcasp_set_reg(mcasp, DAVINCI_MCASP_TXTDM_REG, mask);
 	mcasp_set_bits(mcasp, DAVINCI_MCASP_TXFMT_REG, busel | TXORD);
 	mcasp_mod_bits(mcasp, DAVINCI_MCASP_TXFMCTL_REG,
-		       FSXMOD(mcasp->tdm_slots), FSXMOD(0x1FF));
+		       FSXMOD(total_slots), FSXMOD(0x1FF));
 
 	mcasp_set_reg(mcasp, DAVINCI_MCASP_RXTDM_REG, mask);
 	mcasp_set_bits(mcasp, DAVINCI_MCASP_RXFMT_REG, busel | RXORD);
 	mcasp_mod_bits(mcasp, DAVINCI_MCASP_RXFMCTL_REG,
-		       FSRMOD(mcasp->tdm_slots), FSRMOD(0x1FF));
+		       FSRMOD(total_slots), FSRMOD(0x1FF));
 
 	return 0;
 }
@@ -778,7 +879,8 @@ static int davinci_mcasp_hw_params(struct snd_pcm_substream *substream,
 	if (mcasp->op_mode == DAVINCI_MCASP_DIT_MODE)
 		ret = mcasp_dit_hw_param(mcasp, params_rate(params));
 	else
-		ret = mcasp_i2s_hw_param(mcasp, substream->stream);
+		ret = mcasp_i2s_hw_param(mcasp, substream->stream,
+					 channels);
 
 	if (ret)
 		return ret;
@@ -826,6 +928,9 @@ static int davinci_mcasp_hw_params(struct snd_pcm_substream *substream,
 
 	davinci_config_channel_size(mcasp, word_length);
 
+	if (mcasp->op_mode == DAVINCI_MCASP_IIS_MODE)
+		mcasp->channels = channels;
+
 	return 0;
 }
 
@@ -854,7 +959,65 @@ static int davinci_mcasp_trigger(struct snd_pcm_substream *substream,
 	return ret;
 }
 
+static int davinci_mcasp_startup(struct snd_pcm_substream *substream,
+				 struct snd_soc_dai *cpu_dai)
+{
+	struct davinci_mcasp *mcasp = snd_soc_dai_get_drvdata(cpu_dai);
+	u32 max_channels = 0;
+	int i, dir;
+
+	mcasp->substreams[substream->stream] = substream;
+
+	if (mcasp->op_mode == DAVINCI_MCASP_DIT_MODE)
+		return 0;
+
+	/*
+	 * Limit the maximum allowed channels for the first stream:
+	 * number of serializers for the direction * tdm slots per serializer
+	 */
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+		dir = TX_MODE;
+	else
+		dir = RX_MODE;
+
+	for (i = 0; i < mcasp->num_serializer; i++) {
+		if (mcasp->serial_dir[i] == dir)
+			max_channels++;
+	}
+	max_channels *= mcasp->tdm_slots;
+	/*
+	 * If the already active stream has less channels than the calculated
+	 * limnit based on the seirializers * tdm_slots, we need to use that as
+	 * a constraint for the second stream.
+	 * Otherwise (first stream or less allowed channels) we use the
+	 * calculated constraint.
+	 */
+	if (mcasp->channels && mcasp->channels < max_channels)
+		max_channels = mcasp->channels;
+
+	snd_pcm_hw_constraint_minmax(substream->runtime,
+				     SNDRV_PCM_HW_PARAM_CHANNELS,
+				     2, max_channels);
+	return 0;
+}
+
+static void davinci_mcasp_shutdown(struct snd_pcm_substream *substream,
+				   struct snd_soc_dai *cpu_dai)
+{
+	struct davinci_mcasp *mcasp = snd_soc_dai_get_drvdata(cpu_dai);
+
+	mcasp->substreams[substream->stream] = NULL;
+
+	if (mcasp->op_mode == DAVINCI_MCASP_DIT_MODE)
+		return;
+
+	if (!cpu_dai->active)
+		mcasp->channels = 0;
+}
+
 static const struct snd_soc_dai_ops davinci_mcasp_dai_ops = {
+	.startup	= davinci_mcasp_startup,
+	.shutdown	= davinci_mcasp_shutdown,
 	.trigger	= davinci_mcasp_trigger,
 	.hw_params	= davinci_mcasp_hw_params,
 	.set_fmt	= davinci_mcasp_set_dai_fmt,
@@ -971,6 +1134,7 @@ static struct snd_soc_dai_driver davinci_mcasp_dai[] = {
 		},
 		.ops 		= &davinci_mcasp_dai_ops,
 
+		.symmetric_samplebits	= 1,
 	},
 	{
 		.name		= "davinci-mcasp.1",
@@ -1194,6 +1358,8 @@ static int davinci_mcasp_probe(struct platform_device *pdev)
 	struct resource *mem, *ioarea, *res, *dat;
 	struct davinci_mcasp_pdata *pdata;
 	struct davinci_mcasp *mcasp;
+	char *irq_name;
+	int irq;
 	int ret;
 
 	if (!pdev->dev.platform_data && !pdev->dev.of_node) {
@@ -1235,6 +1401,7 @@ static int davinci_mcasp_probe(struct platform_device *pdev)
 	ret = pm_runtime_get_sync(&pdev->dev);
 	if (IS_ERR_VALUE(ret)) {
 		dev_err(&pdev->dev, "pm_runtime_get_sync() failed\n");
+		pm_runtime_disable(&pdev->dev);
 		return ret;
 	}
 
@@ -1246,7 +1413,21 @@ static int davinci_mcasp_probe(struct platform_device *pdev)
 	}
 
 	mcasp->op_mode = pdata->op_mode;
-	mcasp->tdm_slots = pdata->tdm_slots;
+	/* sanity check for tdm slots parameter */
+	if (mcasp->op_mode == DAVINCI_MCASP_IIS_MODE) {
+		if (pdata->tdm_slots < 2) {
+			dev_err(&pdev->dev, "invalid tdm slots: %d\n",
+				pdata->tdm_slots);
+			mcasp->tdm_slots = 2;
+		} else if (pdata->tdm_slots > 32) {
+			dev_err(&pdev->dev, "invalid tdm slots: %d\n",
+				pdata->tdm_slots);
+			mcasp->tdm_slots = 32;
+		} else {
+			mcasp->tdm_slots = pdata->tdm_slots;
+		}
+	}
+
 	mcasp->num_serializer = pdata->num_serializer;
 #ifdef CONFIG_PM_SLEEP
 	mcasp->context.xrsr_regs = devm_kzalloc(&pdev->dev,
@@ -1260,6 +1441,36 @@ static int davinci_mcasp_probe(struct platform_device *pdev)
 
 	mcasp->dev = &pdev->dev;
 
+	irq = platform_get_irq_byname(pdev, "rx");
+	if (irq >= 0) {
+		irq_name = devm_kasprintf(&pdev->dev, GFP_KERNEL, "%s_rx\n",
+					  dev_name(&pdev->dev));
+		ret = devm_request_threaded_irq(&pdev->dev, irq, NULL,
+						davinci_mcasp_rx_irq_handler,
+						IRQF_ONESHOT, irq_name, mcasp);
+		if (ret) {
+			dev_err(&pdev->dev, "RX IRQ request failed\n");
+			goto err;
+		}
+
+		mcasp->irq_request[SNDRV_PCM_STREAM_CAPTURE] = ROVRN;
+	}
+
+	irq = platform_get_irq_byname(pdev, "tx");
+	if (irq >= 0) {
+		irq_name = devm_kasprintf(&pdev->dev, GFP_KERNEL, "%s_tx\n",
+					  dev_name(&pdev->dev));
+		ret = devm_request_threaded_irq(&pdev->dev, irq, NULL,
+						davinci_mcasp_tx_irq_handler,
+						IRQF_ONESHOT, irq_name, mcasp);
+		if (ret) {
+			dev_err(&pdev->dev, "TX IRQ request failed\n");
+			goto err;
+		}
+
+		mcasp->irq_request[SNDRV_PCM_STREAM_PLAYBACK] = XUNDRN;
+	}
+
 	dat = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dat");
 	if (dat)
 		mcasp->dat_port = true;
diff --git a/sound/soc/davinci/davinci-mcasp.h b/sound/soc/davinci/davinci-mcasp.h
index 98fbc451892a..79dc511180bf 100644
--- a/sound/soc/davinci/davinci-mcasp.h
+++ b/sound/soc/davinci/davinci-mcasp.h
@@ -253,6 +253,13 @@
 #define TXFSRST		BIT(12)	/* Frame Sync Generator Reset */
 
 /*
+ * DAVINCI_MCASP_TXSTAT_REG - Transmitter Status Register Bits
+ * DAVINCI_MCASP_RXSTAT_REG - Receiver Status Register Bits
+ */
+#define XRERR		BIT(8) /* Transmit/Receive error */
+#define XRDATA		BIT(5) /* Transmit/Receive data ready */
+
+/*
  * DAVINCI_MCASP_AMUTE_REG -  Mute Control Register Bits
  */
 #define MUTENA(val)	(val)
@@ -279,6 +286,16 @@
 #define TXDATADMADIS	BIT(0)
 
 /*
+ * DAVINCI_MCASP_EVTCTLR_REG - Receiver Interrupt Control Register Bits
+ */
+#define ROVRN		BIT(0)
+
+/*
+ * DAVINCI_MCASP_EVTCTLX_REG - Transmitter Interrupt Control Register Bits
+ */
+#define XUNDRN		BIT(0)
+
+/*
  * DAVINCI_MCASP_W[R]FIFOCTL - Write/Read FIFO Control Register bits
  */
 #define FIFO_ENABLE	BIT(16)
diff --git a/sound/soc/dwc/designware_i2s.c b/sound/soc/dwc/designware_i2s.c
index e961388e6e9c..08f0229f8d68 100644
--- a/sound/soc/dwc/designware_i2s.c
+++ b/sound/soc/dwc/designware_i2s.c
@@ -338,31 +338,34 @@ static int dw_i2s_probe(struct platform_device *pdev)
 		return -EINVAL;
 	}
 
-	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-	if (!res) {
-		dev_err(&pdev->dev, "no i2s resource defined\n");
-		return -ENODEV;
-	}
-
-	if (!devm_request_mem_region(&pdev->dev, res->start,
-				resource_size(res), pdev->name)) {
-		dev_err(&pdev->dev, "i2s region already claimed\n");
-		return -EBUSY;
-	}
-
 	dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL);
 	if (!dev) {
 		dev_warn(&pdev->dev, "kzalloc fail\n");
 		return -ENOMEM;
 	}
 
-	dev->i2s_base = devm_ioremap(&pdev->dev, res->start,
-			resource_size(res));
-	if (!dev->i2s_base) {
-		dev_err(&pdev->dev, "ioremap fail for i2s_region\n");
+	dw_i2s_dai = devm_kzalloc(&pdev->dev, sizeof(*dw_i2s_dai), GFP_KERNEL);
+	if (!dw_i2s_dai) {
+		dev_err(&pdev->dev, "mem allocation failed for dai driver\n");
 		return -ENOMEM;
 	}
 
+	dw_i2s_dai->ops = &dw_i2s_dai_ops;
+	dw_i2s_dai->suspend = dw_i2s_suspend;
+	dw_i2s_dai->resume = dw_i2s_resume;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!res) {
+		dev_err(&pdev->dev, "no i2s resource defined\n");
+		return -ENODEV;
+	}
+
+	dev->i2s_base = devm_ioremap_resource(&pdev->dev, res);
+	if (IS_ERR(dev->i2s_base)) {
+		dev_err(&pdev->dev, "ioremap fail for i2s_region\n");
+		return PTR_ERR(dev->i2s_base);
+	}
+
 	cap = pdata->cap;
 	dev->capability = cap;
 	dev->i2s_clk_cfg = pdata->i2s_clk_cfg;
@@ -388,13 +391,6 @@ static int dw_i2s_probe(struct platform_device *pdev)
 	if (ret < 0)
 		goto err_clk_put;
 
-	dw_i2s_dai = devm_kzalloc(&pdev->dev, sizeof(*dw_i2s_dai), GFP_KERNEL);
-	if (!dw_i2s_dai) {
-		dev_err(&pdev->dev, "mem allocation failed for dai driver\n");
-		ret = -ENOMEM;
-		goto err_clk_disable;
-	}
-
 	if (cap & DWC_I2S_PLAY) {
 		dev_dbg(&pdev->dev, " designware: play supported\n");
 		dw_i2s_dai->playback.channels_min = MIN_CHANNEL_NUM;
@@ -411,10 +407,6 @@ static int dw_i2s_probe(struct platform_device *pdev)
 		dw_i2s_dai->capture.rates = pdata->snd_rates;
 	}
 
-	dw_i2s_dai->ops = &dw_i2s_dai_ops;
-	dw_i2s_dai->suspend = dw_i2s_suspend;
-	dw_i2s_dai->resume = dw_i2s_resume;
-
 	dev->dev = &pdev->dev;
 	dev_set_drvdata(&pdev->dev, dev);
 	ret = snd_soc_register_component(&pdev->dev, &dw_i2s_component,
diff --git a/sound/soc/fsl/eukrea-tlv320.c b/sound/soc/fsl/eukrea-tlv320.c
index eb093d5b85c4..b175b0145a42 100644
--- a/sound/soc/fsl/eukrea-tlv320.c
+++ b/sound/soc/fsl/eukrea-tlv320.c
@@ -105,7 +105,7 @@ static int eukrea_tlv320_probe(struct platform_device *pdev)
 	int ret;
 	int int_port = 0, ext_port;
 	struct device_node *np = pdev->dev.of_node;
-	struct device_node *ssi_np, *codec_np;
+	struct device_node *ssi_np = NULL, *codec_np = NULL;
 
 	eukrea_tlv320.dev = &pdev->dev;
 	if (np) {
@@ -217,8 +217,7 @@ static int eukrea_tlv320_probe(struct platform_device *pdev)
 err:
 	if (ret)
 		dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n", ret);
-	if (np)
-		of_node_put(ssi_np);
+	of_node_put(ssi_np);
 
 	return ret;
 }
diff --git a/sound/soc/fsl/fsl-asoc-card.c b/sound/soc/fsl/fsl-asoc-card.c
index 007c772f3cef..3f6959c8e2f7 100644
--- a/sound/soc/fsl/fsl-asoc-card.c
+++ b/sound/soc/fsl/fsl-asoc-card.c
@@ -51,6 +51,7 @@ struct codec_priv {
  * @sysclk_freq[2]: SYSCLK rates for set_sysclk()
  * @sysclk_dir[2]: SYSCLK directions for set_sysclk()
  * @sysclk_id[2]: SYSCLK ids for set_sysclk()
+ * @slot_width: Slot width of each frame
  *
  * Note: [1] for tx and [0] for rx
  */
@@ -58,6 +59,7 @@ struct cpu_priv {
 	unsigned long sysclk_freq[2];
 	u32 sysclk_dir[2];
 	u32 sysclk_id[2];
+	u32 slot_width;
 };
 
 /**
@@ -125,7 +127,12 @@ static int fsl_asoc_card_hw_params(struct snd_pcm_substream *substream,
 	priv->sample_rate = params_rate(params);
 	priv->sample_format = params_format(params);
 
-	if (priv->card.set_bias_level)
+	/*
+	 * If codec-dai is DAI Master and all configurations are already in the
+	 * set_bias_level(), bypass the remaining settings in hw_params().
+	 * Note: (dai_fmt & CBM_CFM) includes CBM_CFM and CBM_CFS.
+	 */
+	if (priv->card.set_bias_level && priv->dai_fmt & SND_SOC_DAIFMT_CBM_CFM)
 		return 0;
 
 	/* Specific configurations of DAIs starts from here */
@@ -137,6 +144,15 @@ static int fsl_asoc_card_hw_params(struct snd_pcm_substream *substream,
 		return ret;
 	}
 
+	if (cpu_priv->slot_width) {
+		ret = snd_soc_dai_set_tdm_slot(rtd->cpu_dai, 0x3, 0x3, 2,
+					       cpu_priv->slot_width);
+		if (ret) {
+			dev_err(dev, "failed to set TDM slot for cpu dai\n");
+			return ret;
+		}
+	}
+
 	return 0;
 }
 
@@ -448,6 +464,7 @@ static int fsl_asoc_card_probe(struct platform_device *pdev)
 		priv->cpu_priv.sysclk_freq[RX] = priv->codec_priv.mclk_freq;
 		priv->cpu_priv.sysclk_dir[TX] = SND_SOC_CLOCK_OUT;
 		priv->cpu_priv.sysclk_dir[RX] = SND_SOC_CLOCK_OUT;
+		priv->cpu_priv.slot_width = 32;
 		priv->dai_fmt |= SND_SOC_DAIFMT_CBS_CFS;
 	} else if (of_device_is_compatible(np, "fsl,imx-audio-sgtl5000")) {
 		priv->codec_priv.mclk_id = SGTL5000_SYSCLK;
diff --git a/sound/soc/fsl/fsl_esai.c b/sound/soc/fsl/fsl_esai.c
index a645e296199e..ca319d59f843 100644
--- a/sound/soc/fsl/fsl_esai.c
+++ b/sound/soc/fsl/fsl_esai.c
@@ -513,10 +513,15 @@ static int fsl_esai_hw_params(struct snd_pcm_substream *substream,
 	u32 width = snd_pcm_format_width(params_format(params));
 	u32 channels = params_channels(params);
 	u32 pins = DIV_ROUND_UP(channels, esai_priv->slots);
+	u32 slot_width = width;
 	u32 bclk, mask, val;
 	int ret;
 
-	bclk = params_rate(params) * esai_priv->slot_width * esai_priv->slots;
+	/* Override slot_width if being specifially set */
+	if (esai_priv->slot_width)
+		slot_width = esai_priv->slot_width;
+
+	bclk = params_rate(params) * slot_width * esai_priv->slots;
 
 	ret = fsl_esai_set_bclk(dai, tx, bclk);
 	if (ret)
@@ -538,7 +543,7 @@ static int fsl_esai_hw_params(struct snd_pcm_substream *substream,
 	regmap_update_bits(esai_priv->regmap, REG_ESAI_xFCR(tx), mask, val);
 
 	mask = ESAI_xCR_xSWS_MASK | (tx ? ESAI_xCR_PADC : 0);
-	val = ESAI_xCR_xSWS(esai_priv->slot_width, width) | (tx ? ESAI_xCR_PADC : 0);
+	val = ESAI_xCR_xSWS(slot_width, width) | (tx ? ESAI_xCR_PADC : 0);
 
 	regmap_update_bits(esai_priv->regmap, REG_ESAI_xCR(tx), mask, val);
 
@@ -780,9 +785,6 @@ static int fsl_esai_probe(struct platform_device *pdev)
 		return ret;
 	}
 
-	/* Set a default slot size */
-	esai_priv->slot_width = 32;
-
 	/* Set a default slot number */
 	esai_priv->slots = 2;
 
diff --git a/sound/soc/fsl/fsl_ssi.c b/sound/soc/fsl/fsl_ssi.c
index e6955170dc42..b6b0d25f6ace 100644
--- a/sound/soc/fsl/fsl_ssi.c
+++ b/sound/soc/fsl/fsl_ssi.c
@@ -67,8 +67,6 @@
 /**
  * FSLSSI_I2S_FORMATS: audio formats supported by the SSI
  *
- * This driver currently only supports the SSI running in I2S slave mode.
- *
  * The SSI has a limitation in that the samples must be in the same byte
  * order as the host CPU.  This is because when multiple bytes are written
  * to the STX register, the bytes and bits must be written in the same
@@ -1099,7 +1097,7 @@ static const struct snd_soc_component_driver fsl_ssi_component = {
 };
 
 static struct snd_soc_dai_driver fsl_ssi_ac97_dai = {
-	.ac97_control = 1,
+	.bus_control = true,
 	.playback = {
 		.stream_name = "AC97 Playback",
 		.channels_min = 2,
@@ -1363,7 +1361,7 @@ static int fsl_ssi_probe(struct platform_device *pdev)
 		return PTR_ERR(ssi_private->regs);
 	}
 
-	ssi_private->irq = irq_of_parse_and_map(np, 0);
+	ssi_private->irq = platform_get_irq(pdev, 0);
 	if (!ssi_private->irq) {
 		dev_err(&pdev->dev, "no irq for node %s\n", np->full_name);
 		return -ENXIO;
@@ -1389,7 +1387,7 @@ static int fsl_ssi_probe(struct platform_device *pdev)
 	if (ssi_private->soc->imx) {
 		ret = fsl_ssi_imx_probe(pdev, ssi_private, iomem);
 		if (ret)
-			goto error_irqmap;
+			return ret;
 	}
 
 	ret = snd_soc_register_component(&pdev->dev, &fsl_ssi_component,
@@ -1412,7 +1410,7 @@ static int fsl_ssi_probe(struct platform_device *pdev)
 
 	ret = fsl_ssi_debugfs_create(&ssi_private->dbg_stats, &pdev->dev);
 	if (ret)
-		goto error_asoc_register;
+		goto error_irq;
 
 	/*
 	 * If codec-handle property is missing from SSI node, we assume
@@ -1460,10 +1458,6 @@ error_asoc_register:
 	if (ssi_private->soc->imx)
 		fsl_ssi_imx_clean(pdev, ssi_private);
 
-error_irqmap:
-	if (ssi_private->use_dma)
-		irq_dispose_mapping(ssi_private->irq);
-
 	return ret;
 }
 
@@ -1480,9 +1474,6 @@ static int fsl_ssi_remove(struct platform_device *pdev)
 	if (ssi_private->soc->imx)
 		fsl_ssi_imx_clean(pdev, ssi_private);
 
-	if (ssi_private->use_dma)
-		irq_dispose_mapping(ssi_private->irq);
-
 	return 0;
 }
 
diff --git a/sound/soc/fsl/imx-sgtl5000.c b/sound/soc/fsl/imx-sgtl5000.c
index 1cb22dd034eb..1dab963a59f7 100644
--- a/sound/soc/fsl/imx-sgtl5000.c
+++ b/sound/soc/fsl/imx-sgtl5000.c
@@ -175,10 +175,8 @@ static int imx_sgtl5000_probe(struct platform_device *pdev)
 fail:
 	if (data && !IS_ERR(data->codec_clk))
 		clk_put(data->codec_clk);
-	if (ssi_np)
-		of_node_put(ssi_np);
-	if (codec_np)
-		of_node_put(codec_np);
+	of_node_put(ssi_np);
+	of_node_put(codec_np);
 
 	return ret;
 }
diff --git a/sound/soc/fsl/imx-spdif.c b/sound/soc/fsl/imx-spdif.c
index e1dc40143600..0c9068ebe1e7 100644
--- a/sound/soc/fsl/imx-spdif.c
+++ b/sound/soc/fsl/imx-spdif.c
@@ -74,8 +74,7 @@ static int imx_spdif_audio_probe(struct platform_device *pdev)
 	platform_set_drvdata(pdev, data);
 
 end:
-	if (spdif_np)
-		of_node_put(spdif_np);
+	of_node_put(spdif_np);
 
 	return ret;
 }
diff --git a/sound/soc/fsl/imx-ssi.c b/sound/soc/fsl/imx-ssi.c
index ab2fdd76b693..60b0a5b1f1f1 100644
--- a/sound/soc/fsl/imx-ssi.c
+++ b/sound/soc/fsl/imx-ssi.c
@@ -382,7 +382,7 @@ static struct snd_soc_dai_driver imx_ssi_dai = {
 
 static struct snd_soc_dai_driver imx_ac97_dai = {
 	.probe = imx_ssi_dai_probe,
-	.ac97_control = 1,
+	.bus_control = true,
 	.playback = {
 		.stream_name = "AC97 Playback",
 		.channels_min = 2,
diff --git a/sound/soc/fsl/imx-wm8962.c b/sound/soc/fsl/imx-wm8962.c
index 3a3d17ce6ba4..48179ffe1543 100644
--- a/sound/soc/fsl/imx-wm8962.c
+++ b/sound/soc/fsl/imx-wm8962.c
@@ -281,10 +281,8 @@ static int imx_wm8962_probe(struct platform_device *pdev)
 clk_fail:
 	clk_disable_unprepare(data->codec_clk);
 fail:
-	if (ssi_np)
-		of_node_put(ssi_np);
-	if (codec_np)
-		of_node_put(codec_np);
+	of_node_put(ssi_np);
+	of_node_put(codec_np);
 
 	return ret;
 }
diff --git a/sound/soc/fsl/mpc5200_dma.c b/sound/soc/fsl/mpc5200_dma.c
index f2b5d756b1f3..0b82e209b6e3 100644
--- a/sound/soc/fsl/mpc5200_dma.c
+++ b/sound/soc/fsl/mpc5200_dma.c
@@ -327,9 +327,6 @@ static int psc_dma_new(struct snd_soc_pcm_runtime *rtd)
 			goto capture_alloc_err;
 	}
 
-	if (rtd->codec->ac97)
-		rtd->codec->ac97->private_data = psc_dma;
-
 	return 0;
 
  capture_alloc_err:
diff --git a/sound/soc/fsl/mpc5200_psc_ac97.c b/sound/soc/fsl/mpc5200_psc_ac97.c
index 24eafa2cfbf4..c6ed6ba965a9 100644
--- a/sound/soc/fsl/mpc5200_psc_ac97.c
+++ b/sound/soc/fsl/mpc5200_psc_ac97.c
@@ -237,7 +237,7 @@ static const struct snd_soc_dai_ops psc_ac97_digital_ops = {
 static struct snd_soc_dai_driver psc_ac97_dai[] = {
 {
 	.name = "mpc5200-psc-ac97.0",
-	.ac97_control = 1,
+	.bus_control = true,
 	.probe	= psc_ac97_probe,
 	.playback = {
 		.stream_name	= "AC97 Playback",
@@ -257,7 +257,7 @@ static struct snd_soc_dai_driver psc_ac97_dai[] = {
 },
 {
 	.name = "mpc5200-psc-ac97.1",
-	.ac97_control = 1,
+	.bus_control = true,
 	.playback = {
 		.stream_name	= "AC97 SPDIF",
 		.channels_min   = 1,
@@ -282,7 +282,6 @@ static const struct snd_soc_component_driver psc_ac97_component = {
 static int psc_ac97_of_probe(struct platform_device *op)
 {
 	int rc;
-	struct snd_ac97 ac97;
 	struct mpc52xx_psc __iomem *regs;
 
 	rc = mpc5200_audio_dma_create(op);
@@ -304,7 +303,6 @@ static int psc_ac97_of_probe(struct platform_device *op)
 
 	psc_dma = dev_get_drvdata(&op->dev);
 	regs = psc_dma->psc_regs;
-	ac97.private_data = psc_dma;
 
 	psc_dma->imr = 0;
 	out_be16(&psc_dma->psc_regs->isr_imr.imr, psc_dma->imr);
diff --git a/sound/soc/generic/simple-card.c b/sound/soc/generic/simple-card.c
index d1b7293c133e..ece22d55ba82 100644
--- a/sound/soc/generic/simple-card.c
+++ b/sound/soc/generic/simple-card.c
@@ -29,7 +29,9 @@ struct simple_card_data {
 	} *dai_props;
 	unsigned int mclk_fs;
 	int gpio_hp_det;
+	int gpio_hp_det_invert;
 	int gpio_mic_det;
+	int gpio_mic_det_invert;
 	struct snd_soc_dai_link dai_link[];	/* dynamically allocated */
 };
 
@@ -148,6 +150,7 @@ static int asoc_simple_card_dai_init(struct snd_soc_pcm_runtime *rtd)
 				      simple_card_hp_jack_pins);
 
 		simple_card_hp_jack_gpio.gpio = priv->gpio_hp_det;
+		simple_card_hp_jack_gpio.invert = priv->gpio_hp_det_invert;
 		snd_soc_jack_add_gpios(&simple_card_hp_jack, 1,
 				       &simple_card_hp_jack_gpio);
 	}
@@ -159,6 +162,7 @@ static int asoc_simple_card_dai_init(struct snd_soc_pcm_runtime *rtd)
 				      ARRAY_SIZE(simple_card_mic_jack_pins),
 				      simple_card_mic_jack_pins);
 		simple_card_mic_jack_gpio.gpio = priv->gpio_mic_det;
+		simple_card_mic_jack_gpio.invert = priv->gpio_mic_det_invert;
 		snd_soc_jack_add_gpios(&simple_card_mic_jack, 1,
 				       &simple_card_mic_jack_gpio);
 	}
@@ -226,6 +230,52 @@ asoc_simple_card_sub_parse_of(struct device_node *np,
 	return 0;
 }
 
+static int asoc_simple_card_parse_daifmt(struct device_node *node,
+					 struct simple_card_data *priv,
+					 struct device_node *codec,
+					 char *prefix, int idx)
+{
+	struct device *dev = simple_priv_to_dev(priv);
+	struct device_node *bitclkmaster = NULL;
+	struct device_node *framemaster = NULL;
+	struct simple_dai_props *dai_props = simple_priv_to_props(priv, idx);
+	struct asoc_simple_dai *cpu_dai = &dai_props->cpu_dai;
+	struct asoc_simple_dai *codec_dai = &dai_props->codec_dai;
+	unsigned int daifmt;
+
+	daifmt = snd_soc_of_parse_daifmt(node, prefix,
+					 &bitclkmaster, &framemaster);
+	daifmt &= ~SND_SOC_DAIFMT_MASTER_MASK;
+
+	if (strlen(prefix) && !bitclkmaster && !framemaster) {
+		/*
+		 * No dai-link level and master setting was not found from
+		 * sound node level, revert back to legacy DT parsing and
+		 * take the settings from codec node.
+		 */
+		dev_dbg(dev, "Revert to legacy daifmt parsing\n");
+
+		cpu_dai->fmt = codec_dai->fmt =
+			snd_soc_of_parse_daifmt(codec, NULL, NULL, NULL) |
+			(daifmt & ~SND_SOC_DAIFMT_CLOCK_MASK);
+	} else {
+		if (codec == bitclkmaster)
+			daifmt |= (codec == framemaster) ?
+				SND_SOC_DAIFMT_CBM_CFM : SND_SOC_DAIFMT_CBM_CFS;
+		else
+			daifmt |= (codec == framemaster) ?
+				SND_SOC_DAIFMT_CBS_CFM : SND_SOC_DAIFMT_CBS_CFS;
+
+		cpu_dai->fmt	= daifmt;
+		codec_dai->fmt	= daifmt;
+	}
+
+	of_node_put(bitclkmaster);
+	of_node_put(framemaster);
+
+	return 0;
+}
+
 static int asoc_simple_card_dai_link_of(struct device_node *node,
 					struct simple_card_data *priv,
 					int idx,
@@ -234,10 +284,8 @@ static int asoc_simple_card_dai_link_of(struct device_node *node,
 	struct device *dev = simple_priv_to_dev(priv);
 	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 *np = NULL;
-	struct device_node *bitclkmaster = NULL;
-	struct device_node *framemaster = NULL;
-	unsigned int daifmt;
+	struct device_node *cpu = NULL;
+	struct device_node *codec = NULL;
 	char *name;
 	char prop[128];
 	char *prefix = "";
@@ -247,85 +295,36 @@ static int asoc_simple_card_dai_link_of(struct device_node *node,
 	if (is_top_level_node)
 		prefix = "simple-audio-card,";
 
-	daifmt = snd_soc_of_parse_daifmt(node, prefix,
-					 &bitclkmaster, &framemaster);
-	daifmt &= ~SND_SOC_DAIFMT_MASTER_MASK;
-
 	snprintf(prop, sizeof(prop), "%scpu", prefix);
-	np = of_get_child_by_name(node, prop);
-	if (!np) {
+	cpu = of_get_child_by_name(node, prop);
+
+	snprintf(prop, sizeof(prop), "%scodec", prefix);
+	codec = of_get_child_by_name(node, prop);
+
+	if (!cpu || !codec) {
 		ret = -EINVAL;
 		dev_err(dev, "%s: Can't find %s DT node\n", __func__, prop);
 		goto dai_link_of_err;
 	}
 
-	ret = asoc_simple_card_sub_parse_of(np, &dai_props->cpu_dai,
+	ret = asoc_simple_card_parse_daifmt(node, priv,
+					    codec, prefix, idx);
+	if (ret < 0)
+		goto dai_link_of_err;
+
+	ret = asoc_simple_card_sub_parse_of(cpu, &dai_props->cpu_dai,
 					    &dai_link->cpu_of_node,
 					    &dai_link->cpu_dai_name,
 					    &cpu_args);
 	if (ret < 0)
 		goto dai_link_of_err;
 
-	dai_props->cpu_dai.fmt = daifmt;
-	switch (((np == bitclkmaster) << 4) | (np == framemaster)) {
-	case 0x11:
-		dai_props->cpu_dai.fmt |= SND_SOC_DAIFMT_CBS_CFS;
-		break;
-	case 0x10:
-		dai_props->cpu_dai.fmt |= SND_SOC_DAIFMT_CBS_CFM;
-		break;
-	case 0x01:
-		dai_props->cpu_dai.fmt |= SND_SOC_DAIFMT_CBM_CFS;
-		break;
-	default:
-		dai_props->cpu_dai.fmt |= SND_SOC_DAIFMT_CBM_CFM;
-		break;
-	}
-
-	of_node_put(np);
-	snprintf(prop, sizeof(prop), "%scodec", prefix);
-	np = of_get_child_by_name(node, prop);
-	if (!np) {
-		ret = -EINVAL;
-		dev_err(dev, "%s: Can't find %s DT node\n", __func__, prop);
-		goto dai_link_of_err;
-	}
-
-	ret = asoc_simple_card_sub_parse_of(np, &dai_props->codec_dai,
+	ret = asoc_simple_card_sub_parse_of(codec, &dai_props->codec_dai,
 					    &dai_link->codec_of_node,
 					    &dai_link->codec_dai_name, NULL);
 	if (ret < 0)
 		goto dai_link_of_err;
 
-	if (strlen(prefix) && !bitclkmaster && !framemaster) {
-		/*
-		 * No DAI link level and master setting was found
-		 * from sound node level, revert back to legacy DT
-		 * parsing and take the settings from codec node.
-		 */
-		dev_dbg(dev, "%s: Revert to legacy daifmt parsing\n",
-			__func__);
-		dai_props->cpu_dai.fmt = dai_props->codec_dai.fmt =
-			snd_soc_of_parse_daifmt(np, NULL, NULL, NULL) |
-			(daifmt & ~SND_SOC_DAIFMT_CLOCK_MASK);
-	} else {
-		dai_props->codec_dai.fmt = daifmt;
-		switch (((np == bitclkmaster) << 4) | (np == framemaster)) {
-		case 0x11:
-			dai_props->codec_dai.fmt |= SND_SOC_DAIFMT_CBM_CFM;
-			break;
-		case 0x10:
-			dai_props->codec_dai.fmt |= SND_SOC_DAIFMT_CBM_CFS;
-			break;
-		case 0x01:
-			dai_props->codec_dai.fmt |= SND_SOC_DAIFMT_CBS_CFM;
-			break;
-		default:
-			dai_props->codec_dai.fmt |= SND_SOC_DAIFMT_CBS_CFS;
-			break;
-		}
-	}
-
 	if (!dai_link->cpu_dai_name || !dai_link->codec_dai_name) {
 		ret = -EINVAL;
 		goto dai_link_of_err;
@@ -368,12 +367,9 @@ static int asoc_simple_card_dai_link_of(struct device_node *node,
 		dai_link->cpu_dai_name = NULL;
 
 dai_link_of_err:
-	if (np)
-		of_node_put(np);
-	if (bitclkmaster)
-		of_node_put(bitclkmaster);
-	if (framemaster)
-		of_node_put(framemaster);
+	of_node_put(cpu);
+	of_node_put(codec);
+
 	return ret;
 }
 
@@ -381,6 +377,7 @@ static int asoc_simple_card_parse_of(struct device_node *node,
 				     struct simple_card_data *priv)
 {
 	struct device *dev = simple_priv_to_dev(priv);
+	enum of_gpio_flags flags;
 	u32 val;
 	int ret;
 
@@ -436,13 +433,15 @@ static int asoc_simple_card_parse_of(struct device_node *node,
 			return ret;
 	}
 
-	priv->gpio_hp_det = of_get_named_gpio(node,
-				"simple-audio-card,hp-det-gpio", 0);
+	priv->gpio_hp_det = of_get_named_gpio_flags(node,
+				"simple-audio-card,hp-det-gpio", 0, &flags);
+	priv->gpio_hp_det_invert = !!(flags & OF_GPIO_ACTIVE_LOW);
 	if (priv->gpio_hp_det == -EPROBE_DEFER)
 		return -EPROBE_DEFER;
 
-	priv->gpio_mic_det = of_get_named_gpio(node,
-				"simple-audio-card,mic-det-gpio", 0);
+	priv->gpio_mic_det = of_get_named_gpio_flags(node,
+				"simple-audio-card,mic-det-gpio", 0, &flags);
+	priv->gpio_mic_det_invert = !!(flags & OF_GPIO_ACTIVE_LOW);
 	if (priv->gpio_mic_det == -EPROBE_DEFER)
 		return -EPROBE_DEFER;
 
@@ -457,18 +456,13 @@ static int asoc_simple_card_unref(struct platform_device *pdev)
 {
 	struct snd_soc_card *card = platform_get_drvdata(pdev);
 	struct snd_soc_dai_link *dai_link;
-	struct device_node *np;
 	int num_links;
 
 	for (num_links = 0, dai_link = card->dai_link;
 	     num_links < card->num_links;
 	     num_links++, dai_link++) {
-		np = (struct device_node *) dai_link->cpu_of_node;
-		if (np)
-			of_node_put(np);
-		np = (struct device_node *) dai_link->codec_of_node;
-		if (np)
-			of_node_put(np);
+		of_node_put(dai_link->cpu_of_node);
+		of_node_put(dai_link->codec_of_node);
 	}
 	return 0;
 }
diff --git a/sound/soc/intel/Kconfig b/sound/soc/intel/Kconfig
index f5b4a9c79cdf..e989ecf046c9 100644
--- a/sound/soc/intel/Kconfig
+++ b/sound/soc/intel/Kconfig
@@ -3,6 +3,7 @@ config SND_MFLD_MACHINE
 	depends on INTEL_SCU_IPC
 	select SND_SOC_SN95031
 	select SND_SST_MFLD_PLATFORM
+	select SND_SST_IPC_PCI
 	help
           This adds support for ASoC machine driver for Intel(R) MID Medfield platform
           used as alsa device in audio substem in Intel(R) MID devices
@@ -12,10 +13,23 @@ config SND_MFLD_MACHINE
 config SND_SST_MFLD_PLATFORM
 	tristate
 
+config SND_SST_IPC
+	tristate
+
+config SND_SST_IPC_PCI
+	tristate
+	select SND_SST_IPC
+
+config SND_SST_IPC_ACPI
+	tristate
+	select SND_SST_IPC
+	depends on ACPI
+
 config SND_SOC_INTEL_SST
 	tristate "ASoC support for Intel(R) Smart Sound Technology"
 	select SND_SOC_INTEL_SST_ACPI if ACPI
 	depends on (X86 || COMPILE_TEST)
+	depends on DW_DMAC_CORE
 	help
           This adds support for Intel(R) Smart Sound Technology (SST).
           Say Y if you have such a device
@@ -32,7 +46,8 @@ config SND_SOC_INTEL_BAYTRAIL
 
 config SND_SOC_INTEL_HASWELL_MACH
 	tristate "ASoC Audio DSP support for Intel Haswell Lynxpoint"
-	depends on SND_SOC_INTEL_SST && X86_INTEL_LPSS && I2C
+	depends on SND_SOC_INTEL_SST && X86_INTEL_LPSS && I2C && \\
+		   I2C_DESIGNWARE_PLATFORM
 	select SND_SOC_INTEL_HASWELL
 	select SND_SOC_RT5640
 	help
@@ -61,7 +76,8 @@ config SND_SOC_INTEL_BYT_MAX98090_MACH
 
 config SND_SOC_INTEL_BROADWELL_MACH
 	tristate "ASoC Audio DSP support for Intel Broadwell Wildcatpoint"
-	depends on SND_SOC_INTEL_SST && X86_INTEL_LPSS && DW_DMAC
+	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
@@ -70,3 +86,27 @@ config SND_SOC_INTEL_BROADWELL_MACH
 	  Ultrabook platforms.
 	  Say Y if you have such a device
 	  If unsure select "N".
+
+config SND_SOC_INTEL_BYTCR_RT5640_MACH
+	tristate "ASoC Audio DSP Support for MID BYT Platform"
+	depends on X86
+	select SND_SOC_RT5640
+	select SND_SST_MFLD_PLATFORM
+	select SND_SST_IPC_ACPI
+	help
+	  This adds support for ASoC machine driver for Intel(R) MID Baytrail platform
+          used as alsa device in audio substem in Intel(R) MID devices
+          Say Y if you have such a device
+          If unsure select "N".
+
+config SND_SOC_INTEL_CHT_BSW_RT5672_MACH
+        tristate "ASoC Audio driver for Intel Cherrytrail & Braswell with RT5672 codec"
+        depends on X86_INTEL_LPSS
+        select SND_SOC_RT5670
+        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 RT5672 audio codec.
+          Say Y if you have such a device
+          If unsure select "N".
diff --git a/sound/soc/intel/Makefile b/sound/soc/intel/Makefile
index f841786dad15..e928ec385300 100644
--- a/sound/soc/intel/Makefile
+++ b/sound/soc/intel/Makefile
@@ -26,8 +26,15 @@ snd-soc-sst-haswell-objs := haswell.o
 snd-soc-sst-byt-rt5640-mach-objs := byt-rt5640.o
 snd-soc-sst-byt-max98090-mach-objs := byt-max98090.o
 snd-soc-sst-broadwell-objs := broadwell.o
+snd-soc-sst-bytcr-dpcm-rt5640-objs := bytcr_dpcm_rt5640.o
+snd-soc-sst-cht-bsw-rt5672-objs := cht_bsw_rt5672.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
 obj-$(CONFIG_SND_SOC_INTEL_BYT_MAX98090_MACH) += snd-soc-sst-byt-max98090-mach.o
 obj-$(CONFIG_SND_SOC_INTEL_BROADWELL_MACH) += snd-soc-sst-broadwell.o
+obj-$(CONFIG_SND_SOC_INTEL_BYTCR_RT5640_MACH) += snd-soc-sst-bytcr-dpcm-rt5640.o
+obj-$(CONFIG_SND_SOC_INTEL_CHT_BSW_RT5672_MACH) += snd-soc-sst-cht-bsw-rt5672.o
+
+# DSP driver
+obj-$(CONFIG_SND_SST_IPC) += sst/
diff --git a/sound/soc/intel/broadwell.c b/sound/soc/intel/broadwell.c
index 0e550f14028f..c256764e3c4b 100644
--- a/sound/soc/intel/broadwell.c
+++ b/sound/soc/intel/broadwell.c
@@ -19,6 +19,7 @@
 #include <sound/core.h>
 #include <sound/pcm.h>
 #include <sound/soc.h>
+#include <sound/jack.h>
 #include <sound/pcm_params.h>
 
 #include "sst-dsp.h"
@@ -26,8 +27,26 @@
 
 #include "../codecs/rt286.h"
 
+static struct snd_soc_jack broadwell_headset;
+/* Headset jack detection DAPM pins */
+static struct snd_soc_jack_pin broadwell_headset_pins[] = {
+	{
+		.pin = "Mic Jack",
+		.mask = SND_JACK_MICROPHONE,
+	},
+	{
+		.pin = "Headphone Jack",
+		.mask = SND_JACK_HEADPHONE,
+	},
+};
+
+static const struct snd_kcontrol_new broadwell_controls[] = {
+	SOC_DAPM_PIN_SWITCH("Speaker"),
+	SOC_DAPM_PIN_SWITCH("Headphone Jack"),
+};
+
 static const struct snd_soc_dapm_widget broadwell_widgets[] = {
-	SND_SOC_DAPM_HP("Headphones", NULL),
+	SND_SOC_DAPM_HP("Headphone Jack", NULL),
 	SND_SOC_DAPM_SPK("Speaker", NULL),
 	SND_SOC_DAPM_MIC("Mic Jack", NULL),
 	SND_SOC_DAPM_MIC("DMIC1", NULL),
@@ -42,7 +61,7 @@ static const struct snd_soc_dapm_route broadwell_rt286_map[] = {
 	{"Speaker", NULL, "SPOL"},
 
 	/* HP jack connectors - unknown if we have jack deteck */
-	{"Headphones", NULL, "HPO Pin"},
+	{"Headphone Jack", NULL, "HPO Pin"},
 
 	/* other jacks */
 	{"MIC1", NULL, "Mic Jack"},
@@ -57,6 +76,27 @@ static const struct snd_soc_dapm_route broadwell_rt286_map[] = {
 	{"AIF1 Playback", NULL, "SSP0 CODEC OUT"},
 };
 
+static int broadwell_rt286_codec_init(struct snd_soc_pcm_runtime *rtd)
+{
+	struct snd_soc_codec *codec = rtd->codec;
+	int ret = 0;
+	ret = snd_soc_jack_new(codec, "Headset",
+		SND_JACK_HEADSET | SND_JACK_BTN_0, &broadwell_headset);
+
+	if (ret)
+		return ret;
+
+	ret = snd_soc_jack_add_pins(&broadwell_headset,
+		ARRAY_SIZE(broadwell_headset_pins),
+		broadwell_headset_pins);
+	if (ret)
+		return ret;
+
+	rt286_mic_detect(codec, &broadwell_headset);
+	return 0;
+}
+
+
 static int broadwell_ssp0_fixup(struct snd_soc_pcm_runtime *rtd,
 			struct snd_pcm_hw_params *params)
 {
@@ -116,7 +156,7 @@ static int broadwell_rtd_init(struct snd_soc_pcm_runtime *rtd)
 	}
 
 	/* always connected - check HP for jack detect */
-	snd_soc_dapm_enable_pin(dapm, "Headphones");
+	snd_soc_dapm_enable_pin(dapm, "Headphone Jack");
 	snd_soc_dapm_enable_pin(dapm, "Speaker");
 	snd_soc_dapm_enable_pin(dapm, "Mic Jack");
 	snd_soc_dapm_enable_pin(dapm, "Line Jack");
@@ -131,7 +171,7 @@ static struct snd_soc_dai_link broadwell_rt286_dais[] = {
 	/* Front End DAI links */
 	{
 		.name = "System PCM",
-		.stream_name = "System Playback",
+		.stream_name = "System Playback/Capture",
 		.cpu_dai_name = "System Pin",
 		.platform_name = "haswell-pcm-audio",
 		.dynamic = 1,
@@ -140,6 +180,7 @@ static struct snd_soc_dai_link broadwell_rt286_dais[] = {
 		.init = broadwell_rtd_init,
 		.trigger = {SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST},
 		.dpcm_playback = 1,
+		.dpcm_capture = 1,
 	},
 	{
 		.name = "Offload0",
@@ -174,18 +215,6 @@ static struct snd_soc_dai_link broadwell_rt286_dais[] = {
 		.trigger = {SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST},
 		.dpcm_capture = 1,
 	},
-	{
-		.name = "Capture PCM",
-		.stream_name = "Capture",
-		.cpu_dai_name = "Capture Pin",
-		.platform_name = "haswell-pcm-audio",
-		.dynamic = 1,
-		.codec_name = "snd-soc-dummy",
-		.codec_dai_name = "snd-soc-dummy-dai",
-		.trigger = {SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST},
-		.dpcm_capture = 1,
-	},
-
 	/* Back End DAI links */
 	{
 		/* SSP0 - Codec */
@@ -196,6 +225,7 @@ static struct snd_soc_dai_link broadwell_rt286_dais[] = {
 		.no_pcm = 1,
 		.codec_name = "i2c-INT343A:00",
 		.codec_dai_name = "rt286-aif1",
+		.init = broadwell_rt286_codec_init,
 		.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
 			SND_SOC_DAIFMT_CBS_CFS,
 		.ignore_suspend = 1,
@@ -213,6 +243,8 @@ static struct snd_soc_card broadwell_rt286 = {
 	.owner = THIS_MODULE,
 	.dai_link = broadwell_rt286_dais,
 	.num_links = ARRAY_SIZE(broadwell_rt286_dais),
+	.controls = broadwell_controls,
+	.num_controls = ARRAY_SIZE(broadwell_controls),
 	.dapm_widgets = broadwell_widgets,
 	.num_dapm_widgets = ARRAY_SIZE(broadwell_widgets),
 	.dapm_routes = broadwell_rt286_map,
diff --git a/sound/soc/intel/bytcr_dpcm_rt5640.c b/sound/soc/intel/bytcr_dpcm_rt5640.c
new file mode 100644
index 000000000000..f5d0fc1ab10c
--- /dev/null
+++ b/sound/soc/intel/bytcr_dpcm_rt5640.c
@@ -0,0 +1,230 @@
+/*
+ *  byt_cr_dpcm_rt5640.c - ASoc Machine driver for Intel Byt CR platform
+ *
+ *  Copyright (C) 2014 Intel Corp
+ *  Author: Subhransu S. Prusty <subhransu.s.prusty@intel.com>
+ *  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; version 2 of the License.
+ *
+ *  This program is distributed in the hope that it will be useful, but
+ *  WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  General Public License for more details.
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/device.h>
+#include <linux/slab.h>
+#include <linux/input.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include "../codecs/rt5640.h"
+#include "sst-atom-controls.h"
+
+static const struct snd_soc_dapm_widget byt_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 byt_audio_map[] = {
+	{"IN2P", NULL, "Headset Mic"},
+	{"IN2N", NULL, "Headset Mic"},
+	{"Headset Mic", NULL, "MICBIAS1"},
+	{"IN1P", NULL, "MICBIAS1"},
+	{"LDO2", NULL, "Int Mic"},
+	{"Headphone", NULL, "HPOL"},
+	{"Headphone", NULL, "HPOR"},
+	{"Ext Spk", NULL, "SPOLP"},
+	{"Ext Spk", NULL, "SPOLN"},
+	{"Ext Spk", NULL, "SPORP"},
+	{"Ext Spk", NULL, "SPORN"},
+
+	{"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 byt_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 byt_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;
+
+	snd_soc_dai_set_bclk_ratio(codec_dai, 50);
+
+	ret = snd_soc_dai_set_sysclk(codec_dai, RT5640_SCLK_S_PLL1,
+				     params_rate(params) * 512,
+				     SND_SOC_CLOCK_IN);
+	if (ret < 0) {
+		dev_err(rtd->dev, "can't set codec clock %d\n", ret);
+		return ret;
+	}
+
+	ret = snd_soc_dai_set_pll(codec_dai, 0, RT5640_PLL1_S_BCLK1,
+				  params_rate(params) * 50,
+				  params_rate(params) * 512);
+	if (ret < 0) {
+		dev_err(rtd->dev, "can't set codec pll: %d\n", ret);
+		return ret;
+	}
+
+	return 0;
+}
+
+static const struct snd_soc_pcm_stream byt_dai_params = {
+	.formats = SNDRV_PCM_FMTBIT_S24_LE,
+	.rate_min = 48000,
+	.rate_max = 48000,
+	.channels_min = 2,
+	.channels_max = 2,
+};
+
+static int byt_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);
+
+	/* 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 */
+	snd_mask_set(&params->masks[SNDRV_PCM_HW_PARAM_FORMAT -
+				    SNDRV_PCM_HW_PARAM_FIRST_MASK],
+				    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 byt_aif1_startup(struct snd_pcm_substream *substream)
+{
+	return snd_pcm_hw_constraint_list(substream->runtime, 0,
+			SNDRV_PCM_HW_PARAM_RATE,
+			&constraints_48000);
+}
+
+static struct snd_soc_ops byt_aif1_ops = {
+	.startup = byt_aif1_startup,
+};
+
+static struct snd_soc_ops byt_be_ssp2_ops = {
+	.hw_params = byt_aif1_hw_params,
+};
+
+static struct snd_soc_dai_link byt_dailink[] = {
+	[MERR_DPCM_AUDIO] = {
+		.name = "Baytrail Audio Port",
+		.stream_name = "Baytrail Audio",
+		.cpu_dai_name = "media-cpu-dai",
+		.codec_dai_name = "snd-soc-dummy-dai",
+		.codec_name = "snd-soc-dummy",
+		.platform_name = "sst-mfld-platform",
+		.ignore_suspend = 1,
+		.dynamic = 1,
+		.dpcm_playback = 1,
+		.dpcm_capture = 1,
+		.ops = &byt_aif1_ops,
+	},
+	[MERR_DPCM_COMPR] = {
+		.name = "Baytrail Compressed Port",
+		.stream_name = "Baytrail 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 = "rt5640-aif1",
+		.codec_name = "i2c-10EC5640:00",
+		.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
+						| SND_SOC_DAIFMT_CBS_CFS,
+		.be_hw_params_fixup = byt_codec_fixup,
+		.ignore_suspend = 1,
+		.dpcm_playback = 1,
+		.dpcm_capture = 1,
+		.ops = &byt_be_ssp2_ops,
+	},
+};
+
+/* SoC card */
+static struct snd_soc_card snd_soc_card_byt = {
+	.name = "baytrailcraudio",
+	.dai_link = byt_dailink,
+	.num_links = ARRAY_SIZE(byt_dailink),
+	.dapm_widgets = byt_dapm_widgets,
+	.num_dapm_widgets = ARRAY_SIZE(byt_dapm_widgets),
+	.dapm_routes = byt_audio_map,
+	.num_dapm_routes = ARRAY_SIZE(byt_audio_map),
+	.controls = byt_mc_controls,
+	.num_controls = ARRAY_SIZE(byt_mc_controls),
+};
+
+static int snd_byt_mc_probe(struct platform_device *pdev)
+{
+	int ret_val = 0;
+
+	/* register the soc card */
+	snd_soc_card_byt.dev = &pdev->dev;
+
+	ret_val = devm_snd_soc_register_card(&pdev->dev, &snd_soc_card_byt);
+	if (ret_val) {
+		dev_err(&pdev->dev, "devm_snd_soc_register_card failed %d\n", ret_val);
+		return ret_val;
+	}
+	platform_set_drvdata(pdev, &snd_soc_card_byt);
+	return ret_val;
+}
+
+static struct platform_driver snd_byt_mc_driver = {
+	.driver = {
+		.owner = THIS_MODULE,
+		.name = "bytt100_rt5640",
+		.pm = &snd_soc_pm_ops,
+	},
+	.probe = snd_byt_mc_probe,
+};
+
+module_platform_driver(snd_byt_mc_driver);
+
+MODULE_DESCRIPTION("ASoC Intel(R) Baytrail CR Machine driver");
+MODULE_AUTHOR("Subhransu S. Prusty <subhransu.s.prusty@intel.com>");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:bytrt5640-audio");
diff --git a/sound/soc/intel/cht_bsw_rt5672.c b/sound/soc/intel/cht_bsw_rt5672.c
new file mode 100644
index 000000000000..9b8b561171b7
--- /dev/null
+++ b/sound/soc/intel/cht_bsw_rt5672.c
@@ -0,0 +1,285 @@
+/*
+ *  cht_bsw_rt5672.c - ASoc Machine driver for Intel Cherryview-based platforms
+ *                     Cherrytrail and Braswell, with RT5672 codec.
+ *
+ *  Copyright (C) 2014 Intel Corp
+ *  Author: Subhransu S. Prusty <subhransu.s.prusty@intel.com>
+ *          Mengdong Lin <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; 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 <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include "../codecs/rt5670.h"
+#include "sst-atom-controls.h"
+
+/* The platform clock #3 outputs 19.2Mhz clock to codec as I2S MCLK */
+#define CHT_PLAT_CLK_3_HZ	19200000
+#define CHT_CODEC_DAI	"rt5670-aif1"
+
+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 int platform_clock_control(struct snd_soc_dapm_widget *w,
+		struct snd_kcontrol *k, int  event)
+{
+	struct snd_soc_dapm_context *dapm = w->dapm;
+	struct snd_soc_card *card = dapm->card;
+	struct snd_soc_dai *codec_dai;
+
+	codec_dai = cht_get_codec_dai(card);
+	if (!codec_dai) {
+		dev_err(card->dev, "Codec dai not found; Unable to set platform clock\n");
+		return -EIO;
+	}
+
+	if (!SND_SOC_DAPM_EVENT_OFF(event))
+		return 0;
+
+	/* Set codec sysclk source to its internal clock because codec PLL will
+	 * be off when idle and MCLK will also be off by ACPI when codec is
+	 * runtime suspended. Codec needs clock for jack detection and button
+	 * press.
+	 */
+	snd_soc_dai_set_sysclk(codec_dai, RT5670_SCLK_S_RCCLK,
+			       0, SND_SOC_CLOCK_IN);
+
+	return 0;
+}
+
+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),
+	SND_SOC_DAPM_SUPPLY("Platform Clock", SND_SOC_NOPM, 0, 0,
+			platform_clock_control, SND_SOC_DAPM_POST_PMD),
+};
+
+static const struct snd_soc_dapm_route cht_audio_map[] = {
+	{"IN1P", NULL, "Headset Mic"},
+	{"IN1N", NULL, "Headset Mic"},
+	{"DMIC L1", NULL, "Int Mic"},
+	{"DMIC R1", NULL, "Int Mic"},
+	{"Headphone", NULL, "HPOL"},
+	{"Headphone", NULL, "HPOR"},
+	{"Ext Spk", NULL, "SPOLP"},
+	{"Ext Spk", NULL, "SPOLN"},
+	{"Ext Spk", NULL, "SPORP"},
+	{"Ext Spk", NULL, "SPORN"},
+	{"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"),
+	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;
+
+	/* set codec PLL source to the 19.2MHz platform clock (MCLK) */
+	ret = snd_soc_dai_set_pll(codec_dai, 0, RT5670_PLL1_S_MCLK,
+				  CHT_PLAT_CLK_3_HZ, params_rate(params) * 512);
+	if (ret < 0) {
+		dev_err(rtd->dev, "can't set codec pll: %d\n", ret);
+		return ret;
+	}
+
+	/* set codec sysclk source to PLL */
+	ret = snd_soc_dai_set_sysclk(codec_dai, RT5670_SCLK_S_PLL1,
+				     params_rate(params) * 512,
+				     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;
+	struct snd_soc_dai *codec_dai = runtime->codec_dai;
+
+	/* TDM 4 slots 24 bit, set Rx & Tx bitmask to 4 active slots */
+	ret = snd_soc_dai_set_tdm_slot(codec_dai, 0xF, 0xF, 4, 24);
+	if (ret < 0) {
+		dev_err(runtime->dev, "can't set codec TDM slot %d\n", ret);
+		return ret;
+	}
+
+	return 0;
+}
+
+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);
+
+	/* 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 */
+	snd_mask_set(&params->masks[SNDRV_PCM_HW_PARAM_FORMAT -
+				    SNDRV_PCM_HW_PARAM_FIRST_MASK],
+				    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 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_dai_link cht_dailink[] = {
+	/* Front End DAI links */
+	[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",
+		.ignore_suspend = 1,
+		.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 End DAI links */
+	{
+		/* SSP2 - Codec */
+		.name = "SSP2-Codec",
+		.be_id = 1,
+		.cpu_dai_name = "ssp2-port",
+		.platform_name = "sst-mfld-platform",
+		.no_pcm = 1,
+		.codec_dai_name = "rt5670-aif1",
+		.codec_name = "i2c-10EC5670:00",
+		.dai_fmt = SND_SOC_DAIFMT_DSP_B | SND_SOC_DAIFMT_IB_NF
+					| SND_SOC_DAIFMT_CBS_CFS,
+		.init = cht_codec_init,
+		.be_hw_params_fixup = cht_codec_fixup,
+		.ignore_suspend = 1,
+		.dpcm_playback = 1,
+		.dpcm_capture = 1,
+		.ops = &cht_be_ssp2_ops,
+	},
+};
+
+/* SoC card */
+static struct snd_soc_card snd_soc_card_cht = {
+	.name = "cherrytrailcraudio",
+	.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),
+	.controls = cht_mc_controls,
+	.num_controls = ARRAY_SIZE(cht_mc_controls),
+};
+
+static int snd_cht_mc_probe(struct platform_device *pdev)
+{
+	int ret_val = 0;
+
+	/* register the soc card */
+	snd_soc_card_cht.dev = &pdev->dev;
+	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 = {
+		.owner = THIS_MODULE,
+		.name = "cht-bsw-rt5672",
+		.pm = &snd_soc_pm_ops,
+	},
+	.probe = snd_cht_mc_probe,
+};
+
+module_platform_driver(snd_cht_mc_driver);
+
+MODULE_DESCRIPTION("ASoC Intel(R) Baytrail CR Machine driver");
+MODULE_AUTHOR("Subhransu S. Prusty, Mengdong Lin");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:cht-bsw-rt5672");
diff --git a/sound/soc/intel/haswell.c b/sound/soc/intel/haswell.c
index 3981982674ac..cb8a482b5f30 100644
--- a/sound/soc/intel/haswell.c
+++ b/sound/soc/intel/haswell.c
@@ -109,7 +109,7 @@ static struct snd_soc_dai_link haswell_rt5640_dais[] = {
 	/* Front End DAI links */
 	{
 		.name = "System",
-		.stream_name = "System Playback",
+		.stream_name = "System Playback/Capture",
 		.cpu_dai_name = "System Pin",
 		.platform_name = "haswell-pcm-audio",
 		.dynamic = 1,
@@ -118,6 +118,7 @@ static struct snd_soc_dai_link haswell_rt5640_dais[] = {
 		.init = haswell_rtd_init,
 		.trigger = {SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST},
 		.dpcm_playback = 1,
+		.dpcm_capture = 1,
 	},
 	{
 		.name = "Offload0",
@@ -152,17 +153,6 @@ static struct snd_soc_dai_link haswell_rt5640_dais[] = {
 		.trigger = {SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST},
 		.dpcm_capture = 1,
 	},
-	{
-		.name = "Capture",
-		.stream_name = "Capture",
-		.cpu_dai_name = "Capture Pin",
-		.platform_name = "haswell-pcm-audio",
-		.dynamic = 1,
-		.codec_name = "snd-soc-dummy",
-		.codec_dai_name = "snd-soc-dummy-dai",
-		.trigger = {SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST},
-		.dpcm_capture = 1,
-	},
 
 	/* Back End DAI links */
 	{
diff --git a/sound/soc/intel/sst-atom-controls.c b/sound/soc/intel/sst-atom-controls.c
index 7104a34181a9..90aa5c0476f3 100644
--- a/sound/soc/intel/sst-atom-controls.c
+++ b/sound/soc/intel/sst-atom-controls.c
@@ -15,6 +15,9 @@
  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  *  General Public License for more details.
  *
+ *  In the dpcm driver modelling when a particular FE/BE/Mixer/Pipe is active
+ *  we forward the settings and parameters, rest we keep the values  in
+ *  driver and forward when DAPM enables them
  * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  */
 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
@@ -81,6 +84,183 @@ static int sst_fill_and_send_cmd(struct sst_data *drv,
 	return ret;
 }
 
+/**
+ * tx map value is a bitfield where each bit represents a FW channel
+ *
+ *			3 2 1 0		# 0 = codec0, 1 = codec1
+ *			RLRLRLRL	# 3, 4 = reserved
+ *
+ * e.g. slot 0 rx map =	00001100b -> data from slot 0 goes into codec_in1 L,R
+ */
+static u8 sst_ssp_tx_map[SST_MAX_TDM_SLOTS] = {
+	0x1, 0x2, 0x4, 0x8, 0x10, 0x20, 0x40, 0x80, /* default rx map */
+};
+
+/**
+ * rx map value is a bitfield where each bit represents a slot
+ *
+ *			  76543210	# 0 = slot 0, 1 = slot 1
+ *
+ * e.g. codec1_0 tx map = 00000101b -> data from codec_out1_0 goes into slot 0, 2
+ */
+static u8 sst_ssp_rx_map[SST_MAX_TDM_SLOTS] = {
+	0x1, 0x2, 0x4, 0x8, 0x10, 0x20, 0x40, 0x80, /* default tx map */
+};
+
+/**
+ * NOTE: this is invoked with lock held
+ */
+static int sst_send_slot_map(struct sst_data *drv)
+{
+	struct sst_param_sba_ssp_slot_map cmd;
+
+	SST_FILL_DEFAULT_DESTINATION(cmd.header.dst);
+	cmd.header.command_id = SBA_SET_SSP_SLOT_MAP;
+	cmd.header.length = sizeof(struct sst_param_sba_ssp_slot_map)
+				- sizeof(struct sst_dsp_header);
+
+	cmd.param_id = SBA_SET_SSP_SLOT_MAP;
+	cmd.param_len = sizeof(cmd.rx_slot_map) + sizeof(cmd.tx_slot_map)
+					+ sizeof(cmd.ssp_index);
+	cmd.ssp_index = SSP_CODEC;
+
+	memcpy(cmd.rx_slot_map, &sst_ssp_tx_map[0], sizeof(cmd.rx_slot_map));
+	memcpy(cmd.tx_slot_map, &sst_ssp_rx_map[0], sizeof(cmd.tx_slot_map));
+
+	return sst_fill_and_send_cmd_unlocked(drv, SST_IPC_IA_SET_PARAMS,
+			SST_FLAG_BLOCKED, SST_TASK_SBA, 0, &cmd,
+			      sizeof(cmd.header) + cmd.header.length);
+}
+
+int sst_slot_enum_info(struct snd_kcontrol *kcontrol,
+		       struct snd_ctl_elem_info *uinfo)
+{
+	struct sst_enum *e = (struct sst_enum *)kcontrol->private_value;
+
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+	uinfo->count = 1;
+	uinfo->value.enumerated.items = e->max;
+
+	if (uinfo->value.enumerated.item > e->max - 1)
+		uinfo->value.enumerated.item = e->max - 1;
+	strcpy(uinfo->value.enumerated.name,
+		e->texts[uinfo->value.enumerated.item]);
+
+	return 0;
+}
+
+/**
+ * sst_slot_get - get the status of the interleaver/deinterleaver control
+ *
+ * Searches the map where the control status is stored, and gets the
+ * channel/slot which is currently set for this enumerated control. Since it is
+ * an enumerated control, there is only one possible value.
+ */
+static int sst_slot_get(struct snd_kcontrol *kcontrol,
+			struct snd_ctl_elem_value *ucontrol)
+{
+	struct sst_enum *e = (void *)kcontrol->private_value;
+	struct snd_soc_component *c = snd_kcontrol_chip(kcontrol);
+	struct sst_data *drv = snd_soc_component_get_drvdata(c);
+	unsigned int ctl_no = e->reg;
+	unsigned int is_tx = e->tx;
+	unsigned int val, mux;
+	u8 *map = is_tx ? sst_ssp_rx_map : sst_ssp_tx_map;
+
+	mutex_lock(&drv->lock);
+	val = 1 << ctl_no;
+	/* search which slot/channel has this bit set - there should be only one */
+	for (mux = e->max; mux > 0;  mux--)
+		if (map[mux - 1] & val)
+			break;
+
+	ucontrol->value.enumerated.item[0] = mux;
+	mutex_unlock(&drv->lock);
+
+	dev_dbg(c->dev, "%s - %s map = %#x\n",
+			is_tx ? "tx channel" : "rx slot",
+			 e->texts[mux], mux ? map[mux - 1] : -1);
+	return 0;
+}
+
+/* sst_check_and_send_slot_map - helper for checking power state and sending
+ * slot map cmd
+ *
+ * called with lock held
+ */
+static int sst_check_and_send_slot_map(struct sst_data *drv, struct snd_kcontrol *kcontrol)
+{
+	struct sst_enum *e = (void *)kcontrol->private_value;
+	int ret = 0;
+
+	if (e->w && e->w->power)
+		ret = sst_send_slot_map(drv);
+	else
+		dev_err(&drv->pdev->dev, "Slot control: %s doesn't have DAPM widget!!!\n",
+				kcontrol->id.name);
+	return ret;
+}
+
+/**
+ * sst_slot_put - set the status of interleaver/deinterleaver control
+ *
+ * (de)interleaver controls are defined in opposite sense to be user-friendly
+ *
+ * Instead of the enum value being the value written to the register, it is the
+ * register address; and the kcontrol number (register num) is the value written
+ * to the register. This is so that there can be only one value for each
+ * slot/channel since there is only one control for each slot/channel.
+ *
+ * This means that whenever an enum is set, we need to clear the bit
+ * for that kcontrol_no for all the interleaver OR deinterleaver registers
+ */
+static int sst_slot_put(struct snd_kcontrol *kcontrol,
+			struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_component *c = snd_soc_kcontrol_component(kcontrol);
+	struct sst_data *drv = snd_soc_component_get_drvdata(c);
+	struct sst_enum *e = (void *)kcontrol->private_value;
+	int i, ret = 0;
+	unsigned int ctl_no = e->reg;
+	unsigned int is_tx = e->tx;
+	unsigned int slot_channel_no;
+	unsigned int val, mux;
+	u8 *map;
+
+	map = is_tx ? sst_ssp_rx_map : sst_ssp_tx_map;
+
+	val = 1 << ctl_no;
+	mux = ucontrol->value.enumerated.item[0];
+	if (mux > e->max - 1)
+		return -EINVAL;
+
+	mutex_lock(&drv->lock);
+	/* first clear all registers of this bit */
+	for (i = 0; i < e->max; i++)
+		map[i] &= ~val;
+
+	if (mux == 0) {
+		/* kctl set to 'none' and we reset the bits so send IPC */
+		ret = sst_check_and_send_slot_map(drv, kcontrol);
+
+		mutex_unlock(&drv->lock);
+		return ret;
+	}
+
+	/* offset by one to take "None" into account */
+	slot_channel_no = mux - 1;
+	map[slot_channel_no] |= val;
+
+	dev_dbg(c->dev, "%s %s map = %#x\n",
+			is_tx ? "tx channel" : "rx slot",
+			e->texts[mux], map[slot_channel_no]);
+
+	ret = sst_check_and_send_slot_map(drv, kcontrol);
+
+	mutex_unlock(&drv->lock);
+	return ret;
+}
+
 static int sst_send_algo_cmd(struct sst_data *drv,
 			      struct sst_algo_control *bc)
 {
@@ -104,6 +284,34 @@ static int sst_send_algo_cmd(struct sst_data *drv,
 	return ret;
 }
 
+/**
+ * sst_find_and_send_pipe_algo - send all the algo parameters for a pipe
+ *
+ * The algos which are in each pipeline are sent to the firmware one by one
+ *
+ * Called with lock held
+ */
+static int sst_find_and_send_pipe_algo(struct sst_data *drv,
+					const char *pipe, struct sst_ids *ids)
+{
+	int ret = 0;
+	struct sst_algo_control *bc;
+	struct sst_module *algo = NULL;
+
+	dev_dbg(&drv->pdev->dev, "Enter: widget=%s\n", pipe);
+
+	list_for_each_entry(algo, &ids->algo_list, node) {
+		bc = (void *)algo->kctl->private_value;
+
+		dev_dbg(&drv->pdev->dev, "Found algo control name=%s pipe=%s\n",
+				algo->kctl->id.name, pipe);
+		ret = sst_send_algo_cmd(drv, bc);
+		if (ret)
+			return ret;
+	}
+	return ret;
+}
+
 static int sst_algo_bytes_ctl_info(struct snd_kcontrol *kcontrol,
 			    struct snd_ctl_elem_info *uinfo)
 {
@@ -162,6 +370,743 @@ static int sst_algo_control_set(struct snd_kcontrol *kcontrol,
 	return ret;
 }
 
+static int sst_gain_ctl_info(struct snd_kcontrol *kcontrol,
+	struct snd_ctl_elem_info *uinfo)
+{
+	struct sst_gain_mixer_control *mc = (void *)kcontrol->private_value;
+
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+	uinfo->count = mc->stereo ? 2 : 1;
+	uinfo->value.integer.min = mc->min;
+	uinfo->value.integer.max = mc->max;
+
+	return 0;
+}
+
+/**
+ * sst_send_gain_cmd - send the gain algorithm IPC to the FW
+ * @gv:		the stored value of gain (also contains rampduration)
+ * @mute:	flag that indicates whether this was called from the
+ *		digital_mute callback or directly. If called from the
+ *		digital_mute callback, module will be muted/unmuted based on this
+ *		flag. The flag is always 0 if called directly.
+ *
+ * Called with sst_data.lock held
+ *
+ * The user-set gain value is sent only if the user-controllable 'mute' control
+ * is OFF (indicated by gv->mute). Otherwise, the mute value (MIN value) is
+ * sent.
+ */
+static int sst_send_gain_cmd(struct sst_data *drv, struct sst_gain_value *gv,
+			      u16 task_id, u16 loc_id, u16 module_id, int mute)
+{
+	struct sst_cmd_set_gain_dual cmd;
+
+	dev_dbg(&drv->pdev->dev, "Enter\n");
+
+	cmd.header.command_id = MMX_SET_GAIN;
+	SST_FILL_DEFAULT_DESTINATION(cmd.header.dst);
+	cmd.gain_cell_num = 1;
+
+	if (mute || gv->mute) {
+		cmd.cell_gains[0].cell_gain_left = SST_GAIN_MIN_VALUE;
+		cmd.cell_gains[0].cell_gain_right = SST_GAIN_MIN_VALUE;
+	} else {
+		cmd.cell_gains[0].cell_gain_left = gv->l_gain;
+		cmd.cell_gains[0].cell_gain_right = gv->r_gain;
+	}
+
+	SST_FILL_DESTINATION(2, cmd.cell_gains[0].dest,
+			     loc_id, module_id);
+	cmd.cell_gains[0].gain_time_constant = gv->ramp_duration;
+
+	cmd.header.length = sizeof(struct sst_cmd_set_gain_dual)
+				- sizeof(struct sst_dsp_header);
+
+	/* we are with lock held, so call the unlocked api  to send */
+	return sst_fill_and_send_cmd_unlocked(drv, SST_IPC_IA_SET_PARAMS,
+				SST_FLAG_BLOCKED, task_id, 0, &cmd,
+			      sizeof(cmd.header) + cmd.header.length);
+}
+
+static int sst_gain_get(struct snd_kcontrol *kcontrol,
+			struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+	struct sst_gain_mixer_control *mc = (void *)kcontrol->private_value;
+	struct sst_gain_value *gv = mc->gain_val;
+
+	switch (mc->type) {
+	case SST_GAIN_TLV:
+		ucontrol->value.integer.value[0] = gv->l_gain;
+		ucontrol->value.integer.value[1] = gv->r_gain;
+		break;
+
+	case SST_GAIN_MUTE:
+		ucontrol->value.integer.value[0] = gv->mute ? 1 : 0;
+		break;
+
+	case SST_GAIN_RAMP_DURATION:
+		ucontrol->value.integer.value[0] = gv->ramp_duration;
+		break;
+
+	default:
+		dev_err(component->dev, "Invalid Input- gain type:%d\n",
+				mc->type);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int sst_gain_put(struct snd_kcontrol *kcontrol,
+			struct snd_ctl_elem_value *ucontrol)
+{
+	int ret = 0;
+	struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol);
+	struct sst_data *drv = snd_soc_component_get_drvdata(cmpnt);
+	struct sst_gain_mixer_control *mc = (void *)kcontrol->private_value;
+	struct sst_gain_value *gv = mc->gain_val;
+
+	mutex_lock(&drv->lock);
+
+	switch (mc->type) {
+	case SST_GAIN_TLV:
+		gv->l_gain = ucontrol->value.integer.value[0];
+		gv->r_gain = ucontrol->value.integer.value[1];
+		dev_dbg(cmpnt->dev, "%s: Volume %d, %d\n",
+				mc->pname, gv->l_gain, gv->r_gain);
+		break;
+
+	case SST_GAIN_MUTE:
+		gv->mute = !!ucontrol->value.integer.value[0];
+		dev_dbg(cmpnt->dev, "%s: Mute %d\n", mc->pname, gv->mute);
+		break;
+
+	case SST_GAIN_RAMP_DURATION:
+		gv->ramp_duration = ucontrol->value.integer.value[0];
+		dev_dbg(cmpnt->dev, "%s: Ramp Delay%d\n",
+					mc->pname, gv->ramp_duration);
+		break;
+
+	default:
+		mutex_unlock(&drv->lock);
+		dev_err(cmpnt->dev, "Invalid Input- gain type:%d\n",
+				mc->type);
+		return -EINVAL;
+	}
+
+	if (mc->w && mc->w->power)
+		ret = sst_send_gain_cmd(drv, gv, mc->task_id,
+			mc->pipe_id | mc->instance_id, mc->module_id, 0);
+	mutex_unlock(&drv->lock);
+
+	return ret;
+}
+
+static int sst_set_pipe_gain(struct sst_ids *ids,
+				struct sst_data *drv, int mute);
+
+static int sst_send_pipe_module_params(struct snd_soc_dapm_widget *w,
+		struct snd_kcontrol *kcontrol)
+{
+	struct snd_soc_component *c = snd_soc_dapm_to_component(w->dapm);
+	struct sst_data *drv = snd_soc_component_get_drvdata(c);
+	struct sst_ids *ids = w->priv;
+
+	mutex_lock(&drv->lock);
+	sst_find_and_send_pipe_algo(drv, w->name, ids);
+	sst_set_pipe_gain(ids, drv, 0);
+	mutex_unlock(&drv->lock);
+
+	return 0;
+}
+
+static int sst_generic_modules_event(struct snd_soc_dapm_widget *w,
+				     struct snd_kcontrol *k, int event)
+{
+	if (SND_SOC_DAPM_EVENT_ON(event))
+		return sst_send_pipe_module_params(w, k);
+	return 0;
+}
+
+static const DECLARE_TLV_DB_SCALE(sst_gain_tlv_common, SST_GAIN_MIN_VALUE * 10, 10, 0);
+
+/* Look up table to convert MIXER SW bit regs to SWM inputs */
+static const uint swm_mixer_input_ids[SST_SWM_INPUT_COUNT] = {
+	[SST_IP_CODEC0]		= SST_SWM_IN_CODEC0,
+	[SST_IP_CODEC1]		= SST_SWM_IN_CODEC1,
+	[SST_IP_LOOP0]		= SST_SWM_IN_SPROT_LOOP,
+	[SST_IP_LOOP1]		= SST_SWM_IN_MEDIA_LOOP1,
+	[SST_IP_LOOP2]		= SST_SWM_IN_MEDIA_LOOP2,
+	[SST_IP_PCM0]		= SST_SWM_IN_PCM0,
+	[SST_IP_PCM1]		= SST_SWM_IN_PCM1,
+	[SST_IP_MEDIA0]		= SST_SWM_IN_MEDIA0,
+	[SST_IP_MEDIA1]		= SST_SWM_IN_MEDIA1,
+	[SST_IP_MEDIA2]		= SST_SWM_IN_MEDIA2,
+	[SST_IP_MEDIA3]		= SST_SWM_IN_MEDIA3,
+};
+
+/**
+ * fill_swm_input - fill in the SWM input ids given the register
+ *
+ * The register value is a bit-field inicated which mixer inputs are ON. Use the
+ * lookup table to get the input-id and fill it in the structure.
+ */
+static int fill_swm_input(struct snd_soc_component *cmpnt,
+		struct swm_input_ids *swm_input, unsigned int reg)
+{
+	uint i, is_set, nb_inputs = 0;
+	u16 input_loc_id;
+
+	dev_dbg(cmpnt->dev, "reg: %#x\n", reg);
+	for (i = 0; i < SST_SWM_INPUT_COUNT; i++) {
+		is_set = reg & BIT(i);
+		if (!is_set)
+			continue;
+
+		input_loc_id = swm_mixer_input_ids[i];
+		SST_FILL_DESTINATION(2, swm_input->input_id,
+				     input_loc_id, SST_DEFAULT_MODULE_ID);
+		nb_inputs++;
+		swm_input++;
+		dev_dbg(cmpnt->dev, "input id: %#x, nb_inputs: %d\n",
+				input_loc_id, nb_inputs);
+
+		if (nb_inputs == SST_CMD_SWM_MAX_INPUTS) {
+			dev_warn(cmpnt->dev, "SET_SWM cmd max inputs reached");
+			break;
+		}
+	}
+	return nb_inputs;
+}
+
+
+/**
+ * called with lock held
+ */
+static int sst_set_pipe_gain(struct sst_ids *ids,
+			struct sst_data *drv, int mute)
+{
+	int ret = 0;
+	struct sst_gain_mixer_control *mc;
+	struct sst_gain_value *gv;
+	struct sst_module *gain = NULL;
+
+	list_for_each_entry(gain, &ids->gain_list, node) {
+		struct snd_kcontrol *kctl = gain->kctl;
+
+		dev_dbg(&drv->pdev->dev, "control name=%s\n", kctl->id.name);
+		mc = (void *)kctl->private_value;
+		gv = mc->gain_val;
+
+		ret = sst_send_gain_cmd(drv, gv, mc->task_id,
+			mc->pipe_id | mc->instance_id, mc->module_id, mute);
+		if (ret)
+			return ret;
+	}
+	return ret;
+}
+
+static int sst_swm_mixer_event(struct snd_soc_dapm_widget *w,
+			struct snd_kcontrol *k, int event)
+{
+	struct sst_cmd_set_swm cmd;
+	struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);
+	struct sst_data *drv = snd_soc_component_get_drvdata(cmpnt);
+	struct sst_ids *ids = w->priv;
+	bool set_mixer = false;
+	struct soc_mixer_control *mc;
+	int val = 0;
+	int i = 0;
+
+	dev_dbg(cmpnt->dev, "widget = %s\n", w->name);
+	/*
+	 * Identify which mixer input is on and send the bitmap of the
+	 * inputs as an IPC to the DSP.
+	 */
+	for (i = 0; i < w->num_kcontrols; i++) {
+		if (dapm_kcontrol_get_value(w->kcontrols[i])) {
+			mc = (struct soc_mixer_control *)(w->kcontrols[i])->private_value;
+			val |= 1 << mc->shift;
+		}
+	}
+	dev_dbg(cmpnt->dev, "val = %#x\n", val);
+
+	switch (event) {
+	case SND_SOC_DAPM_PRE_PMU:
+	case SND_SOC_DAPM_POST_PMD:
+		set_mixer = true;
+		break;
+	case SND_SOC_DAPM_POST_REG:
+		if (w->power)
+			set_mixer = true;
+		break;
+	default:
+		set_mixer = false;
+	}
+
+	if (set_mixer == false)
+		return 0;
+
+	if (SND_SOC_DAPM_EVENT_ON(event) ||
+	    event == SND_SOC_DAPM_POST_REG)
+		cmd.switch_state = SST_SWM_ON;
+	else
+		cmd.switch_state = SST_SWM_OFF;
+
+	SST_FILL_DEFAULT_DESTINATION(cmd.header.dst);
+	/* MMX_SET_SWM == SBA_SET_SWM */
+	cmd.header.command_id = SBA_SET_SWM;
+
+	SST_FILL_DESTINATION(2, cmd.output_id,
+			     ids->location_id, SST_DEFAULT_MODULE_ID);
+	cmd.nb_inputs =	fill_swm_input(cmpnt, &cmd.input[0], val);
+	cmd.header.length = offsetof(struct sst_cmd_set_swm, input)
+				- sizeof(struct sst_dsp_header)
+				+ (cmd.nb_inputs * sizeof(cmd.input[0]));
+
+	return sst_fill_and_send_cmd(drv, SST_IPC_IA_CMD, SST_FLAG_BLOCKED,
+			      ids->task_id, 0, &cmd,
+			      sizeof(cmd.header) + cmd.header.length);
+}
+
+/* SBA mixers - 16 inputs */
+#define SST_SBA_DECLARE_MIX_CONTROLS(kctl_name)							\
+	static const struct snd_kcontrol_new kctl_name[] = {					\
+		SOC_DAPM_SINGLE("codec_in0 Switch", SND_SOC_NOPM, SST_IP_CODEC0, 1, 0),		\
+		SOC_DAPM_SINGLE("codec_in1 Switch", SND_SOC_NOPM, SST_IP_CODEC1, 1, 0),		\
+		SOC_DAPM_SINGLE("sprot_loop_in Switch", SND_SOC_NOPM, SST_IP_LOOP0, 1, 0),	\
+		SOC_DAPM_SINGLE("media_loop1_in Switch", SND_SOC_NOPM, SST_IP_LOOP1, 1, 0),	\
+		SOC_DAPM_SINGLE("media_loop2_in Switch", SND_SOC_NOPM, SST_IP_LOOP2, 1, 0),	\
+		SOC_DAPM_SINGLE("pcm0_in Switch", SND_SOC_NOPM, SST_IP_PCM0, 1, 0),		\
+		SOC_DAPM_SINGLE("pcm1_in Switch", SND_SOC_NOPM, SST_IP_PCM1, 1, 0),		\
+	}
+
+#define SST_SBA_MIXER_GRAPH_MAP(mix_name)			\
+	{ mix_name, "codec_in0 Switch",	"codec_in0" },		\
+	{ mix_name, "codec_in1 Switch",	"codec_in1" },		\
+	{ mix_name, "sprot_loop_in Switch",	"sprot_loop_in" },	\
+	{ mix_name, "media_loop1_in Switch",	"media_loop1_in" },	\
+	{ mix_name, "media_loop2_in Switch",	"media_loop2_in" },	\
+	{ mix_name, "pcm0_in Switch",		"pcm0_in" },		\
+	{ mix_name, "pcm1_in Switch",		"pcm1_in" }
+
+#define SST_MMX_DECLARE_MIX_CONTROLS(kctl_name)						\
+	static const struct snd_kcontrol_new kctl_name[] = {				\
+		SOC_DAPM_SINGLE("media0_in Switch", SND_SOC_NOPM, SST_IP_MEDIA0, 1, 0),	\
+		SOC_DAPM_SINGLE("media1_in Switch", SND_SOC_NOPM, SST_IP_MEDIA1, 1, 0),	\
+		SOC_DAPM_SINGLE("media2_in Switch", SND_SOC_NOPM, SST_IP_MEDIA2, 1, 0),	\
+		SOC_DAPM_SINGLE("media3_in Switch", SND_SOC_NOPM, SST_IP_MEDIA3, 1, 0),	\
+	}
+
+SST_MMX_DECLARE_MIX_CONTROLS(sst_mix_media0_controls);
+SST_MMX_DECLARE_MIX_CONTROLS(sst_mix_media1_controls);
+
+/* 18 SBA mixers */
+SST_SBA_DECLARE_MIX_CONTROLS(sst_mix_pcm0_controls);
+SST_SBA_DECLARE_MIX_CONTROLS(sst_mix_pcm1_controls);
+SST_SBA_DECLARE_MIX_CONTROLS(sst_mix_pcm2_controls);
+SST_SBA_DECLARE_MIX_CONTROLS(sst_mix_sprot_l0_controls);
+SST_SBA_DECLARE_MIX_CONTROLS(sst_mix_media_l1_controls);
+SST_SBA_DECLARE_MIX_CONTROLS(sst_mix_media_l2_controls);
+SST_SBA_DECLARE_MIX_CONTROLS(sst_mix_voip_controls);
+SST_SBA_DECLARE_MIX_CONTROLS(sst_mix_codec0_controls);
+SST_SBA_DECLARE_MIX_CONTROLS(sst_mix_codec1_controls);
+
+/*
+ * sst_handle_vb_timer - Start/Stop the DSP scheduler
+ *
+ * The DSP expects first cmd to be SBA_VB_START, so at first startup send
+ * that.
+ * DSP expects last cmd to be SBA_VB_IDLE, so at last shutdown send that.
+ *
+ * Do refcount internally so that we send command only at first start
+ * and last end. Since SST driver does its own ref count, invoke sst's
+ * power ops always!
+ */
+int sst_handle_vb_timer(struct snd_soc_dai *dai, bool enable)
+{
+	int ret = 0;
+	struct sst_cmd_generic cmd;
+	struct sst_data *drv = snd_soc_dai_get_drvdata(dai);
+	static int timer_usage;
+
+	if (enable)
+		cmd.header.command_id = SBA_VB_START;
+	else
+		cmd.header.command_id = SBA_IDLE;
+	dev_dbg(dai->dev, "enable=%u, usage=%d\n", enable, timer_usage);
+
+	SST_FILL_DEFAULT_DESTINATION(cmd.header.dst);
+	cmd.header.length = 0;
+
+	if (enable) {
+		ret = sst->ops->power(sst->dev, true);
+		if (ret < 0)
+			return ret;
+	}
+
+	mutex_lock(&drv->lock);
+	if (enable)
+		timer_usage++;
+	else
+		timer_usage--;
+
+	/*
+	 * Send the command only if this call is the first enable or last
+	 * disable
+	 */
+	if ((enable && (timer_usage == 1)) ||
+	    (!enable && (timer_usage == 0))) {
+		ret = sst_fill_and_send_cmd_unlocked(drv, SST_IPC_IA_CMD,
+				SST_FLAG_BLOCKED, SST_TASK_SBA, 0, &cmd,
+				sizeof(cmd.header) + cmd.header.length);
+		if (ret && enable) {
+			timer_usage--;
+			enable  = false;
+		}
+	}
+	mutex_unlock(&drv->lock);
+
+	if (!enable)
+		sst->ops->power(sst->dev, false);
+	return ret;
+}
+
+/**
+ * sst_ssp_config - contains SSP configuration for media UC
+ */
+static const struct sst_ssp_config sst_ssp_configs = {
+	.ssp_id = SSP_CODEC,
+	.bits_per_slot = 24,
+	.slots = 4,
+	.ssp_mode = SSP_MODE_MASTER,
+	.pcm_mode = SSP_PCM_MODE_NETWORK,
+	.duplex = SSP_DUPLEX,
+	.ssp_protocol = SSP_MODE_PCM,
+	.fs_width = 1,
+	.fs_frequency = SSP_FS_48_KHZ,
+	.active_slot_map = 0xF,
+	.start_delay = 0,
+};
+
+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)
+				- 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;
+	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;
+
+	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);
+}
+
+static int sst_set_be_modules(struct snd_soc_dapm_widget *w,
+			 struct snd_kcontrol *k, int event)
+{
+	int ret = 0;
+	struct snd_soc_component *c = snd_soc_dapm_to_component(w->dapm);
+	struct sst_data *drv = snd_soc_component_get_drvdata(c);
+
+	dev_dbg(c->dev, "Enter: widget=%s\n", w->name);
+
+	if (SND_SOC_DAPM_EVENT_ON(event)) {
+		ret = sst_send_slot_map(drv);
+		if (ret)
+			return ret;
+		ret = sst_send_pipe_module_params(w, k);
+	}
+	return ret;
+}
+
+static int sst_set_media_path(struct snd_soc_dapm_widget *w,
+			      struct snd_kcontrol *k, int event)
+{
+	int ret = 0;
+	struct sst_cmd_set_media_path cmd;
+	struct snd_soc_component *c = snd_soc_dapm_to_component(w->dapm);
+	struct sst_data *drv = snd_soc_component_get_drvdata(c);
+	struct sst_ids *ids = w->priv;
+
+	dev_dbg(c->dev, "widget=%s\n", w->name);
+	dev_dbg(c->dev, "task=%u, location=%#x\n",
+				ids->task_id, ids->location_id);
+
+	if (SND_SOC_DAPM_EVENT_ON(event))
+		cmd.switch_state = SST_PATH_ON;
+	else
+		cmd.switch_state = SST_PATH_OFF;
+
+	SST_FILL_DESTINATION(2, cmd.header.dst,
+			     ids->location_id, SST_DEFAULT_MODULE_ID);
+
+	/* MMX_SET_MEDIA_PATH == SBA_SET_MEDIA_PATH */
+	cmd.header.command_id = MMX_SET_MEDIA_PATH;
+	cmd.header.length = sizeof(struct sst_cmd_set_media_path)
+				- sizeof(struct sst_dsp_header);
+
+	ret = sst_fill_and_send_cmd(drv, SST_IPC_IA_CMD, SST_FLAG_BLOCKED,
+			      ids->task_id, 0, &cmd,
+			      sizeof(cmd.header) + cmd.header.length);
+	if (ret)
+		return ret;
+
+	if (SND_SOC_DAPM_EVENT_ON(event))
+		ret = sst_send_pipe_module_params(w, k);
+	return ret;
+}
+
+static int sst_set_media_loop(struct snd_soc_dapm_widget *w,
+			struct snd_kcontrol *k, int event)
+{
+	int ret = 0;
+	struct sst_cmd_sba_set_media_loop_map cmd;
+	struct snd_soc_component *c = snd_soc_dapm_to_component(w->dapm);
+	struct sst_data *drv = snd_soc_component_get_drvdata(c);
+	struct sst_ids *ids = w->priv;
+
+	dev_dbg(c->dev, "Enter:widget=%s\n", w->name);
+	if (SND_SOC_DAPM_EVENT_ON(event))
+		cmd.switch_state = SST_SWITCH_ON;
+	else
+		cmd.switch_state = SST_SWITCH_OFF;
+
+	SST_FILL_DESTINATION(2, cmd.header.dst,
+			     ids->location_id, SST_DEFAULT_MODULE_ID);
+
+	cmd.header.command_id = SBA_SET_MEDIA_LOOP_MAP;
+	cmd.header.length = sizeof(struct sst_cmd_sba_set_media_loop_map)
+				 - sizeof(struct sst_dsp_header);
+	cmd.param.part.cfg.rate = 2; /* 48khz */
+
+	cmd.param.part.cfg.format = ids->format; /* stereo/Mono */
+	cmd.param.part.cfg.s_length = 1; /* 24bit left justified */
+	cmd.map = 0; /* Algo sequence: Gain - DRP - FIR - IIR */
+
+	ret = sst_fill_and_send_cmd(drv, SST_IPC_IA_CMD, SST_FLAG_BLOCKED,
+			      SST_TASK_SBA, 0, &cmd,
+			      sizeof(cmd.header) + cmd.header.length);
+	if (ret)
+		return ret;
+
+	if (SND_SOC_DAPM_EVENT_ON(event))
+		ret = sst_send_pipe_module_params(w, k);
+	return ret;
+}
+
+static const struct snd_soc_dapm_widget sst_dapm_widgets[] = {
+	SST_AIF_IN("codec_in0", sst_set_be_modules),
+	SST_AIF_IN("codec_in1", sst_set_be_modules),
+	SST_AIF_OUT("codec_out0", sst_set_be_modules),
+	SST_AIF_OUT("codec_out1", sst_set_be_modules),
+
+	/* Media Paths */
+	/* MediaX IN paths are set via ALLOC, so no SET_MEDIA_PATH command */
+	SST_PATH_INPUT("media0_in", SST_TASK_MMX, SST_SWM_IN_MEDIA0, sst_generic_modules_event),
+	SST_PATH_INPUT("media1_in", SST_TASK_MMX, SST_SWM_IN_MEDIA1, NULL),
+	SST_PATH_INPUT("media2_in", SST_TASK_MMX, SST_SWM_IN_MEDIA2, sst_set_media_path),
+	SST_PATH_INPUT("media3_in", SST_TASK_MMX, SST_SWM_IN_MEDIA3, NULL),
+	SST_PATH_OUTPUT("media0_out", SST_TASK_MMX, SST_SWM_OUT_MEDIA0, sst_set_media_path),
+	SST_PATH_OUTPUT("media1_out", SST_TASK_MMX, SST_SWM_OUT_MEDIA1, sst_set_media_path),
+
+	/* SBA PCM Paths */
+	SST_PATH_INPUT("pcm0_in", SST_TASK_SBA, SST_SWM_IN_PCM0, sst_set_media_path),
+	SST_PATH_INPUT("pcm1_in", SST_TASK_SBA, SST_SWM_IN_PCM1, sst_set_media_path),
+	SST_PATH_OUTPUT("pcm0_out", SST_TASK_SBA, SST_SWM_OUT_PCM0, sst_set_media_path),
+	SST_PATH_OUTPUT("pcm1_out", SST_TASK_SBA, SST_SWM_OUT_PCM1, sst_set_media_path),
+	SST_PATH_OUTPUT("pcm2_out", SST_TASK_SBA, SST_SWM_OUT_PCM2, sst_set_media_path),
+
+	/* SBA Loops */
+	SST_PATH_INPUT("sprot_loop_in", SST_TASK_SBA, SST_SWM_IN_SPROT_LOOP, NULL),
+	SST_PATH_INPUT("media_loop1_in", SST_TASK_SBA, SST_SWM_IN_MEDIA_LOOP1, NULL),
+	SST_PATH_INPUT("media_loop2_in", SST_TASK_SBA, SST_SWM_IN_MEDIA_LOOP2, NULL),
+	SST_PATH_MEDIA_LOOP_OUTPUT("sprot_loop_out", SST_TASK_SBA, SST_SWM_OUT_SPROT_LOOP, SST_FMT_MONO, sst_set_media_loop),
+	SST_PATH_MEDIA_LOOP_OUTPUT("media_loop1_out", SST_TASK_SBA, SST_SWM_OUT_MEDIA_LOOP1, SST_FMT_MONO, sst_set_media_loop),
+	SST_PATH_MEDIA_LOOP_OUTPUT("media_loop2_out", SST_TASK_SBA, SST_SWM_OUT_MEDIA_LOOP2, SST_FMT_STEREO, sst_set_media_loop),
+
+	/* Media Mixers */
+	SST_SWM_MIXER("media0_out mix 0", SND_SOC_NOPM, SST_TASK_MMX, SST_SWM_OUT_MEDIA0,
+		      sst_mix_media0_controls, sst_swm_mixer_event),
+	SST_SWM_MIXER("media1_out mix 0", SND_SOC_NOPM, SST_TASK_MMX, SST_SWM_OUT_MEDIA1,
+		      sst_mix_media1_controls, sst_swm_mixer_event),
+
+	/* SBA PCM mixers */
+	SST_SWM_MIXER("pcm0_out mix 0", SND_SOC_NOPM, SST_TASK_SBA, SST_SWM_OUT_PCM0,
+		      sst_mix_pcm0_controls, sst_swm_mixer_event),
+	SST_SWM_MIXER("pcm1_out mix 0", SND_SOC_NOPM, SST_TASK_SBA, SST_SWM_OUT_PCM1,
+		      sst_mix_pcm1_controls, sst_swm_mixer_event),
+	SST_SWM_MIXER("pcm2_out mix 0", SND_SOC_NOPM, SST_TASK_SBA, SST_SWM_OUT_PCM2,
+		      sst_mix_pcm2_controls, sst_swm_mixer_event),
+
+	/* SBA Loop mixers */
+	SST_SWM_MIXER("sprot_loop_out mix 0", SND_SOC_NOPM, SST_TASK_SBA, SST_SWM_OUT_SPROT_LOOP,
+		      sst_mix_sprot_l0_controls, sst_swm_mixer_event),
+	SST_SWM_MIXER("media_loop1_out mix 0", SND_SOC_NOPM, SST_TASK_SBA, SST_SWM_OUT_MEDIA_LOOP1,
+		      sst_mix_media_l1_controls, sst_swm_mixer_event),
+	SST_SWM_MIXER("media_loop2_out mix 0", SND_SOC_NOPM, SST_TASK_SBA, SST_SWM_OUT_MEDIA_LOOP2,
+		      sst_mix_media_l2_controls, sst_swm_mixer_event),
+
+	/* SBA Backend mixers */
+	SST_SWM_MIXER("codec_out0 mix 0", SND_SOC_NOPM, SST_TASK_SBA, SST_SWM_OUT_CODEC0,
+		      sst_mix_codec0_controls, sst_swm_mixer_event),
+	SST_SWM_MIXER("codec_out1 mix 0", SND_SOC_NOPM, SST_TASK_SBA, SST_SWM_OUT_CODEC1,
+		      sst_mix_codec1_controls, sst_swm_mixer_event),
+};
+
+static const struct snd_soc_dapm_route intercon[] = {
+	{"media0_in", NULL, "Compress Playback"},
+	{"media1_in", NULL, "Headset Playback"},
+	{"media2_in", NULL, "pcm0_out"},
+
+	{"media0_out mix 0", "media0_in Switch", "media0_in"},
+	{"media0_out mix 0", "media1_in Switch", "media1_in"},
+	{"media0_out mix 0", "media2_in Switch", "media2_in"},
+	{"media0_out mix 0", "media3_in Switch", "media3_in"},
+	{"media1_out mix 0", "media0_in Switch", "media0_in"},
+	{"media1_out mix 0", "media1_in Switch", "media1_in"},
+	{"media1_out mix 0", "media2_in Switch", "media2_in"},
+	{"media1_out mix 0", "media3_in Switch", "media3_in"},
+
+	{"media0_out", NULL, "media0_out mix 0"},
+	{"media1_out", NULL, "media1_out mix 0"},
+	{"pcm0_in", NULL, "media0_out"},
+	{"pcm1_in", NULL, "media1_out"},
+
+	{"Headset Capture", NULL, "pcm1_out"},
+	{"Headset Capture", NULL, "pcm2_out"},
+	{"pcm0_out", NULL, "pcm0_out mix 0"},
+	SST_SBA_MIXER_GRAPH_MAP("pcm0_out mix 0"),
+	{"pcm1_out", NULL, "pcm1_out mix 0"},
+	SST_SBA_MIXER_GRAPH_MAP("pcm1_out mix 0"),
+	{"pcm2_out", NULL, "pcm2_out mix 0"},
+	SST_SBA_MIXER_GRAPH_MAP("pcm2_out mix 0"),
+
+	{"media_loop1_in", NULL, "media_loop1_out"},
+	{"media_loop1_out", NULL, "media_loop1_out mix 0"},
+	SST_SBA_MIXER_GRAPH_MAP("media_loop1_out mix 0"),
+	{"media_loop2_in", NULL, "media_loop2_out"},
+	{"media_loop2_out", NULL, "media_loop2_out mix 0"},
+	SST_SBA_MIXER_GRAPH_MAP("media_loop2_out mix 0"),
+	{"sprot_loop_in", NULL, "sprot_loop_out"},
+	{"sprot_loop_out", NULL, "sprot_loop_out mix 0"},
+	SST_SBA_MIXER_GRAPH_MAP("sprot_loop_out mix 0"),
+
+	{"codec_out0", NULL, "codec_out0 mix 0"},
+	SST_SBA_MIXER_GRAPH_MAP("codec_out0 mix 0"),
+	{"codec_out1", NULL, "codec_out1 mix 0"},
+	SST_SBA_MIXER_GRAPH_MAP("codec_out1 mix 0"),
+
+};
+static const char * const slot_names[] = {
+	"none",
+	"slot 0", "slot 1", "slot 2", "slot 3",
+	"slot 4", "slot 5", "slot 6", "slot 7", /* not supported by FW */
+};
+
+static const char * const channel_names[] = {
+	"none",
+	"codec_out0_0", "codec_out0_1", "codec_out1_0", "codec_out1_1",
+	"codec_out2_0", "codec_out2_1", "codec_out3_0", "codec_out3_1", /* not supported by FW */
+};
+
+#define SST_INTERLEAVER(xpname, slot_name, slotno) \
+	SST_SSP_SLOT_CTL(xpname, "tx interleaver", slot_name, slotno, true, \
+			 channel_names, sst_slot_get, sst_slot_put)
+
+#define SST_DEINTERLEAVER(xpname, channel_name, channel_no) \
+	SST_SSP_SLOT_CTL(xpname, "rx deinterleaver", channel_name, channel_no, false, \
+			 slot_names, sst_slot_get, sst_slot_put)
+
+static const struct snd_kcontrol_new sst_slot_controls[] = {
+	SST_INTERLEAVER("codec_out", "slot 0", 0),
+	SST_INTERLEAVER("codec_out", "slot 1", 1),
+	SST_INTERLEAVER("codec_out", "slot 2", 2),
+	SST_INTERLEAVER("codec_out", "slot 3", 3),
+	SST_DEINTERLEAVER("codec_in", "codec_in0_0", 0),
+	SST_DEINTERLEAVER("codec_in", "codec_in0_1", 1),
+	SST_DEINTERLEAVER("codec_in", "codec_in1_0", 2),
+	SST_DEINTERLEAVER("codec_in", "codec_in1_1", 3),
+};
+
+/* Gain helper with min/max set */
+#define SST_GAIN(name, path_id, task_id, instance, gain_var)				\
+	SST_GAIN_KCONTROLS(name, "Gain", SST_GAIN_MIN_VALUE, SST_GAIN_MAX_VALUE,	\
+		SST_GAIN_TC_MIN, SST_GAIN_TC_MAX,					\
+		sst_gain_get, sst_gain_put,						\
+		SST_MODULE_ID_GAIN_CELL, path_id, instance, task_id,			\
+		sst_gain_tlv_common, gain_var)
+
+#define SST_VOLUME(name, path_id, task_id, instance, gain_var)				\
+	SST_GAIN_KCONTROLS(name, "Volume", SST_GAIN_MIN_VALUE, SST_GAIN_MAX_VALUE,	\
+		SST_GAIN_TC_MIN, SST_GAIN_TC_MAX,					\
+		sst_gain_get, sst_gain_put,						\
+		SST_MODULE_ID_VOLUME, path_id, instance, task_id,			\
+		sst_gain_tlv_common, gain_var)
+
+static struct sst_gain_value sst_gains[];
+
+static const struct snd_kcontrol_new sst_gain_controls[] = {
+	SST_GAIN("media0_in", SST_PATH_INDEX_MEDIA0_IN, SST_TASK_MMX, 0, &sst_gains[0]),
+	SST_GAIN("media1_in", SST_PATH_INDEX_MEDIA1_IN, SST_TASK_MMX, 0, &sst_gains[1]),
+	SST_GAIN("media2_in", SST_PATH_INDEX_MEDIA2_IN, SST_TASK_MMX, 0, &sst_gains[2]),
+	SST_GAIN("media3_in", SST_PATH_INDEX_MEDIA3_IN, SST_TASK_MMX, 0, &sst_gains[3]),
+
+	SST_GAIN("pcm0_in", SST_PATH_INDEX_PCM0_IN, SST_TASK_SBA, 0, &sst_gains[4]),
+	SST_GAIN("pcm1_in", SST_PATH_INDEX_PCM1_IN, SST_TASK_SBA, 0, &sst_gains[5]),
+	SST_GAIN("pcm1_out", SST_PATH_INDEX_PCM1_OUT, SST_TASK_SBA, 0, &sst_gains[6]),
+	SST_GAIN("pcm2_out", SST_PATH_INDEX_PCM2_OUT, SST_TASK_SBA, 0, &sst_gains[7]),
+
+	SST_GAIN("codec_in0", SST_PATH_INDEX_CODEC_IN0, SST_TASK_SBA, 0, &sst_gains[8]),
+	SST_GAIN("codec_in1", SST_PATH_INDEX_CODEC_IN1, SST_TASK_SBA, 0, &sst_gains[9]),
+	SST_GAIN("codec_out0", SST_PATH_INDEX_CODEC_OUT0, SST_TASK_SBA, 0, &sst_gains[10]),
+	SST_GAIN("codec_out1", SST_PATH_INDEX_CODEC_OUT1, SST_TASK_SBA, 0, &sst_gains[11]),
+	SST_GAIN("media_loop1_out", SST_PATH_INDEX_MEDIA_LOOP1_OUT, SST_TASK_SBA, 0, &sst_gains[12]),
+	SST_GAIN("media_loop2_out", SST_PATH_INDEX_MEDIA_LOOP2_OUT, SST_TASK_SBA, 0, &sst_gains[13]),
+	SST_GAIN("sprot_loop_out", SST_PATH_INDEX_SPROT_LOOP_OUT, SST_TASK_SBA, 0, &sst_gains[14]),
+	SST_VOLUME("media0_in", SST_PATH_INDEX_MEDIA0_IN, SST_TASK_MMX, 0, &sst_gains[15]),
+};
+
+#define SST_GAIN_NUM_CONTROLS 3
+/* the SST_GAIN macro above will create three alsa controls for each
+ * instance invoked, gain, mute and ramp duration, which use the same gain
+ * cell sst_gain to keep track of data
+ * To calculate number of gain cell instances we need to device by 3 in
+ * below caulcation for gain cell memory.
+ * This gets rid of static number and issues while adding new controls
+ */
+static struct sst_gain_value sst_gains[ARRAY_SIZE(sst_gain_controls)/SST_GAIN_NUM_CONTROLS];
+
 static const struct snd_kcontrol_new sst_algo_controls[] = {
 	SST_ALGO_KCONTROL_BYTES("media_loop1_out", "fir", 272, SST_MODULE_ID_FIR_24,
 		 SST_PATH_INDEX_MEDIA_LOOP1_OUT, 0, SST_TASK_SBA, SBA_VB_SET_FIR),
@@ -198,21 +1143,280 @@ static int sst_algo_control_init(struct device *dev)
 	return 0;
 }
 
-int sst_dsp_init_v2_dpcm(struct snd_soc_platform *platform)
+static bool is_sst_dapm_widget(struct snd_soc_dapm_widget *w)
+{
+	switch (w->id) {
+	case snd_soc_dapm_pga:
+	case snd_soc_dapm_aif_in:
+	case snd_soc_dapm_aif_out:
+	case snd_soc_dapm_input:
+	case snd_soc_dapm_output:
+	case snd_soc_dapm_mixer:
+		return true;
+	default:
+		return false;
+	}
+}
+
+/**
+ * sst_send_pipe_gains - send gains for the front-end DAIs
+ *
+ * The gains in the pipes connected to the front-ends are muted/unmuted
+ * automatically via the digital_mute() DAPM callback. This function sends the
+ * gains for the front-end pipes.
+ */
+int sst_send_pipe_gains(struct snd_soc_dai *dai, int stream, int mute)
+{
+	struct sst_data *drv = snd_soc_dai_get_drvdata(dai);
+	struct snd_soc_dapm_widget *w;
+	struct snd_soc_dapm_path *p = NULL;
+
+	dev_dbg(dai->dev, "enter, dai-name=%s dir=%d\n", dai->name, stream);
+
+	if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
+		dev_dbg(dai->dev, "Stream name=%s\n",
+				dai->playback_widget->name);
+		w = dai->playback_widget;
+		list_for_each_entry(p, &w->sinks, list_source) {
+			if (p->connected && !p->connected(w, p->sink))
+				continue;
+
+			if (p->connect && p->sink->power &&
+					is_sst_dapm_widget(p->sink)) {
+				struct sst_ids *ids = p->sink->priv;
+
+				dev_dbg(dai->dev, "send gains for widget=%s\n",
+						p->sink->name);
+				mutex_lock(&drv->lock);
+				sst_set_pipe_gain(ids, drv, mute);
+				mutex_unlock(&drv->lock);
+			}
+		}
+	} else {
+		dev_dbg(dai->dev, "Stream name=%s\n",
+				dai->capture_widget->name);
+		w = dai->capture_widget;
+		list_for_each_entry(p, &w->sources, list_sink) {
+			if (p->connected && !p->connected(w, p->sink))
+				continue;
+
+			if (p->connect &&  p->source->power &&
+					is_sst_dapm_widget(p->source)) {
+				struct sst_ids *ids = p->source->priv;
+
+				dev_dbg(dai->dev, "send gain for widget=%s\n",
+						p->source->name);
+				mutex_lock(&drv->lock);
+				sst_set_pipe_gain(ids, drv, mute);
+				mutex_unlock(&drv->lock);
+			}
+		}
+	}
+	return 0;
+}
+
+/**
+ * sst_fill_module_list - populate the list of modules/gains for a pipe
+ *
+ *
+ * Fills the widget pointer in the kcontrol private data, and also fills the
+ * kcontrol pointer in the widget private data.
+ *
+ * Widget pointer is used to send the algo/gain in the .put() handler if the
+ * widget is powerd on.
+ *
+ * Kcontrol pointer is used to send the algo/gain in the widget power ON/OFF
+ * event handler. Each widget (pipe) has multiple algos stored in the algo_list.
+ */
+static int sst_fill_module_list(struct snd_kcontrol *kctl,
+	 struct snd_soc_dapm_widget *w, int type)
 {
+	struct sst_module *module = NULL;
+	struct snd_soc_component *c = snd_soc_dapm_to_component(w->dapm);
+	struct sst_ids *ids = w->priv;
 	int ret = 0;
+
+	module = devm_kzalloc(c->dev, sizeof(*module), GFP_KERNEL);
+	if (!module)
+		return -ENOMEM;
+
+	if (type == SST_MODULE_GAIN) {
+		struct sst_gain_mixer_control *mc = (void *)kctl->private_value;
+
+		mc->w = w;
+		module->kctl = kctl;
+		list_add_tail(&module->node, &ids->gain_list);
+	} else if (type == SST_MODULE_ALGO) {
+		struct sst_algo_control *bc = (void *)kctl->private_value;
+
+		bc->w = w;
+		module->kctl = kctl;
+		list_add_tail(&module->node, &ids->algo_list);
+	} else {
+		dev_err(c->dev, "invoked for unknown type %d module %s",
+				type, kctl->id.name);
+		ret = -EINVAL;
+	}
+
+	return ret;
+}
+
+/**
+ * sst_fill_widget_module_info - fill list of gains/algos for the pipe
+ * @widget:	pipe modelled as a DAPM widget
+ *
+ * Fill the list of gains/algos for the widget by looking at all the card
+ * controls and comparing the name of the widget with the first part of control
+ * name. First part of control name contains the pipe name (widget name).
+ */
+static int sst_fill_widget_module_info(struct snd_soc_dapm_widget *w,
+	struct snd_soc_platform *platform)
+{
+	struct snd_kcontrol *kctl;
+	int index, ret = 0;
+	struct snd_card *card = platform->component.card->snd_card;
+	char *idx;
+
+	down_read(&card->controls_rwsem);
+
+	list_for_each_entry(kctl, &card->controls, list) {
+		idx = strstr(kctl->id.name, " ");
+		if (idx == NULL)
+			continue;
+		index  = strlen(kctl->id.name) - strlen(idx);
+
+		if (strstr(kctl->id.name, "Volume") &&
+		    !strncmp(kctl->id.name, w->name, index))
+			ret = sst_fill_module_list(kctl, w, SST_MODULE_GAIN);
+
+		else if (strstr(kctl->id.name, "params") &&
+			 !strncmp(kctl->id.name, w->name, index))
+			ret = sst_fill_module_list(kctl, w, SST_MODULE_ALGO);
+
+		else if (strstr(kctl->id.name, "Switch") &&
+			 !strncmp(kctl->id.name, w->name, index) &&
+			 strstr(kctl->id.name, "Gain")) {
+			struct sst_gain_mixer_control *mc =
+						(void *)kctl->private_value;
+
+			mc->w = w;
+
+		} else if (strstr(kctl->id.name, "interleaver") &&
+			 !strncmp(kctl->id.name, w->name, index)) {
+			struct sst_enum *e = (void *)kctl->private_value;
+
+			e->w = w;
+
+		} else if (strstr(kctl->id.name, "deinterleaver") &&
+			 !strncmp(kctl->id.name, w->name, index)) {
+
+			struct sst_enum *e = (void *)kctl->private_value;
+
+			e->w = w;
+		}
+
+		if (ret < 0) {
+			up_read(&card->controls_rwsem);
+			return ret;
+		}
+	}
+
+	up_read(&card->controls_rwsem);
+	return 0;
+}
+
+/**
+ * sst_fill_linked_widgets - fill the parent pointer for the linked widget
+ */
+static void sst_fill_linked_widgets(struct snd_soc_platform *platform,
+						struct sst_ids *ids)
+{
+	struct snd_soc_dapm_widget *w;
+	unsigned int len = strlen(ids->parent_wname);
+
+	list_for_each_entry(w, &platform->component.card->widgets, list) {
+		if (!strncmp(ids->parent_wname, w->name, len)) {
+			ids->parent_w = w;
+			break;
+		}
+	}
+}
+
+/**
+ * sst_map_modules_to_pipe - fill algo/gains list for all pipes
+ */
+static int sst_map_modules_to_pipe(struct snd_soc_platform *platform)
+{
+	struct snd_soc_dapm_widget *w;
+	int ret = 0;
+
+	list_for_each_entry(w, &platform->component.card->widgets, list) {
+		if (is_sst_dapm_widget(w) && (w->priv)) {
+			struct sst_ids *ids = w->priv;
+
+			dev_dbg(platform->dev, "widget type=%d name=%s\n",
+					w->id, w->name);
+			INIT_LIST_HEAD(&ids->algo_list);
+			INIT_LIST_HEAD(&ids->gain_list);
+			ret = sst_fill_widget_module_info(w, platform);
+
+			if (ret < 0)
+				return ret;
+
+			/* fill linked widgets */
+			if (ids->parent_wname !=  NULL)
+				sst_fill_linked_widgets(platform, ids);
+		}
+	}
+	return 0;
+}
+
+int sst_dsp_init_v2_dpcm(struct snd_soc_platform *platform)
+{
+	int i, ret = 0;
+	struct snd_soc_dapm_context *dapm =
+			snd_soc_component_get_dapm(&platform->component);
 	struct sst_data *drv = snd_soc_platform_get_drvdata(platform);
+	unsigned int gains = ARRAY_SIZE(sst_gain_controls)/3;
 
 	drv->byte_stream = devm_kzalloc(platform->dev,
 					SST_MAX_BIN_BYTES, GFP_KERNEL);
 	if (!drv->byte_stream)
 		return -ENOMEM;
 
-	/*Initialize algo control params*/
+	snd_soc_dapm_new_controls(dapm, sst_dapm_widgets,
+			ARRAY_SIZE(sst_dapm_widgets));
+	snd_soc_dapm_add_routes(dapm, intercon,
+			ARRAY_SIZE(intercon));
+	snd_soc_dapm_new_widgets(dapm->card);
+
+	for (i = 0; i < gains; i++) {
+		sst_gains[i].mute = SST_GAIN_MUTE_DEFAULT;
+		sst_gains[i].l_gain = SST_GAIN_VOLUME_DEFAULT;
+		sst_gains[i].r_gain = SST_GAIN_VOLUME_DEFAULT;
+		sst_gains[i].ramp_duration = SST_GAIN_RAMP_DURATION_DEFAULT;
+	}
+
+	ret = snd_soc_add_platform_controls(platform, sst_gain_controls,
+			ARRAY_SIZE(sst_gain_controls));
+	if (ret)
+		return ret;
+
+	/* Initialize algo control params */
 	ret = sst_algo_control_init(platform->dev);
 	if (ret)
 		return ret;
 	ret = snd_soc_add_platform_controls(platform, sst_algo_controls,
 			ARRAY_SIZE(sst_algo_controls));
+	if (ret)
+		return ret;
+
+	ret = snd_soc_add_platform_controls(platform, sst_slot_controls,
+			ARRAY_SIZE(sst_slot_controls));
+	if (ret)
+		return ret;
+
+	ret = sst_map_modules_to_pipe(platform);
+
 	return ret;
 }
diff --git a/sound/soc/intel/sst-atom-controls.h b/sound/soc/intel/sst-atom-controls.h
index a73e894b175c..dfebfdd5eb2a 100644
--- a/sound/soc/intel/sst-atom-controls.h
+++ b/sound/soc/intel/sst-atom-controls.h
@@ -23,6 +23,9 @@
 #ifndef __SST_ATOM_CONTROLS_H__
 #define __SST_ATOM_CONTROLS_H__
 
+#include <sound/soc.h>
+#include <sound/tlv.h>
+
 enum {
 	MERR_DPCM_AUDIO = 0,
 	MERR_DPCM_COMPR,
@@ -360,16 +363,416 @@ struct sst_dsp_header {
 struct sst_cmd_generic {
 	struct sst_dsp_header header;
 } __packed;
+
+struct swm_input_ids {
+	struct sst_destination_id input_id;
+} __packed;
+
+struct sst_cmd_set_swm {
+	struct sst_dsp_header header;
+	struct sst_destination_id output_id;
+	u16    switch_state;
+	u16    nb_inputs;
+	struct swm_input_ids input[SST_CMD_SWM_MAX_INPUTS];
+} __packed;
+
+struct sst_cmd_set_media_path {
+	struct sst_dsp_header header;
+	u16    switch_state;
+} __packed;
+
+struct pcm_cfg {
+		u8 s_length:2;
+		u8 rate:3;
+		u8 format:3;
+} __packed;
+
+struct sst_cmd_set_speech_path {
+	struct sst_dsp_header header;
+	u16    switch_state;
+	struct {
+		u16 rsvd:8;
+		struct pcm_cfg cfg;
+	} config;
+} __packed;
+
+struct gain_cell {
+	struct sst_destination_id dest;
+	s16 cell_gain_left;
+	s16 cell_gain_right;
+	u16 gain_time_constant;
+} __packed;
+
+#define NUM_GAIN_CELLS 1
+struct sst_cmd_set_gain_dual {
+	struct sst_dsp_header header;
+	u16    gain_cell_num;
+	struct gain_cell cell_gains[NUM_GAIN_CELLS];
+} __packed;
 struct sst_cmd_set_params {
 	struct sst_destination_id dst;
 	u16 command_id;
 	char params[0];
 } __packed;
+
+
+struct sst_cmd_sba_vb_start {
+	struct sst_dsp_header header;
+} __packed;
+
+union sba_media_loop_params {
+	struct {
+		u16 rsvd:8;
+		struct pcm_cfg cfg;
+	} part;
+	u16 full;
+} __packed;
+
+struct sst_cmd_sba_set_media_loop_map {
+	struct	sst_dsp_header header;
+	u16	switch_state;
+	union	sba_media_loop_params param;
+	u16	map;
+} __packed;
+
+struct sst_cmd_tone_stop {
+	struct	sst_dsp_header header;
+	u16	switch_state;
+} __packed;
+
+enum sst_ssp_mode {
+	SSP_MODE_MASTER = 0,
+	SSP_MODE_SLAVE = 1,
+};
+
+enum sst_ssp_pcm_mode {
+	SSP_PCM_MODE_NORMAL = 0,
+	SSP_PCM_MODE_NETWORK = 1,
+};
+
+enum sst_ssp_duplex {
+	SSP_DUPLEX = 0,
+	SSP_RX = 1,
+	SSP_TX = 2,
+};
+
+enum sst_ssp_fs_frequency {
+	SSP_FS_8_KHZ = 0,
+	SSP_FS_16_KHZ = 1,
+	SSP_FS_44_1_KHZ = 2,
+	SSP_FS_48_KHZ = 3,
+};
+
+enum sst_ssp_fs_polarity {
+	SSP_FS_ACTIVE_LOW = 0,
+	SSP_FS_ACTIVE_HIGH = 1,
+};
+
+enum sst_ssp_protocol {
+	SSP_MODE_PCM = 0,
+	SSP_MODE_I2S = 1,
+};
+
+enum sst_ssp_port_id {
+	SSP_MODEM = 0,
+	SSP_BT = 1,
+	SSP_FM = 2,
+	SSP_CODEC = 3,
+};
+
+struct sst_cmd_sba_hw_set_ssp {
+	struct sst_dsp_header header;
+	u16 selection;			/* 0:SSP0(def), 1:SSP1, 2:SSP2 */
+
+	u16 switch_state;
+
+	u16 nb_bits_per_slots:6;        /* 0-32 bits, 24 (def) */
+	u16 nb_slots:4;			/* 0-8: slots per frame  */
+	u16 mode:3;			/* 0:Master, 1: Slave  */
+	u16 duplex:3;
+
+	u16 active_tx_slot_map:8;       /* Bit map, 0:off, 1:on */
+	u16 reserved1:8;
+
+	u16 active_rx_slot_map:8;       /* Bit map 0: Off, 1:On */
+	u16 reserved2:8;
+
+	u16 frame_sync_frequency;
+
+	u16 frame_sync_polarity:8;
+	u16 data_polarity:8;
+
+	u16 frame_sync_width;           /* 1 to N clocks */
+	u16 ssp_protocol:8;
+	u16 start_delay:8;		/* Start delay in terms of clock ticks */
+} __packed;
+
+#define SST_MAX_TDM_SLOTS 8
+
+struct sst_param_sba_ssp_slot_map {
+	struct sst_dsp_header header;
+
+	u16 param_id;
+	u16 param_len;
+	u16 ssp_index;
+
+	u8 rx_slot_map[SST_MAX_TDM_SLOTS];
+	u8 tx_slot_map[SST_MAX_TDM_SLOTS];
+} __packed;
+
+enum {
+	SST_PROBE_EXTRACTOR = 0,
+	SST_PROBE_INJECTOR = 1,
+};
+
+/**** widget defines *****/
+
+#define SST_MODULE_GAIN 1
+#define SST_MODULE_ALGO 2
+
+#define SST_FMT_MONO 0
+#define SST_FMT_STEREO 3
+
+/* physical SSP numbers */
+enum {
+	SST_SSP0 = 0,
+	SST_SSP1,
+	SST_SSP2,
+	SST_SSP_LAST = SST_SSP2,
+};
+
+#define SST_NUM_SSPS		(SST_SSP_LAST + 1)	/* physical SSPs */
+#define SST_MAX_SSP_MUX		2			/* single SSP muxed between pipes */
+#define SST_MAX_SSP_DOMAINS	2			/* domains present in each pipe */
+
+struct sst_module {
+	struct snd_kcontrol *kctl;
+	struct list_head node;
+};
+
+struct sst_ssp_config {
+	u8 ssp_id;
+	u8 bits_per_slot;
+	u8 slots;
+	u8 ssp_mode;
+	u8 pcm_mode;
+	u8 duplex;
+	u8 ssp_protocol;
+	u8 fs_frequency;
+	u8 active_slot_map;
+	u8 start_delay;
+	u16 fs_width;
+};
+
+struct sst_ssp_cfg {
+	const u8 ssp_number;
+	const int *mux_shift;
+	const int (*domain_shift)[SST_MAX_SSP_MUX];
+	const struct sst_ssp_config (*ssp_config)[SST_MAX_SSP_MUX][SST_MAX_SSP_DOMAINS];
+};
+
+struct sst_ids {
+	u16 location_id;
+	u16 module_id;
+	u8  task_id;
+	u8  format;
+	u8  reg;
+	const char *parent_wname;
+	struct snd_soc_dapm_widget *parent_w;
+	struct list_head algo_list;
+	struct list_head gain_list;
+	const struct sst_pcm_format *pcm_fmt;
+};
+
+
+#define SST_AIF_IN(wname, wevent)							\
+{	.id = snd_soc_dapm_aif_in, .name = wname, .sname = NULL,			\
+	.reg = SND_SOC_NOPM, .shift = 0,					\
+	.on_val = 1, .off_val = 0,							\
+	.event = wevent, .event_flags = SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD,	\
+	.priv = (void *)&(struct sst_ids) { .task_id = 0, .location_id = 0 }		\
+}
+
+#define SST_AIF_OUT(wname, wevent)							\
+{	.id = snd_soc_dapm_aif_out, .name = wname, .sname = NULL,			\
+	.reg = SND_SOC_NOPM, .shift = 0,						\
+	.on_val = 1, .off_val = 0,							\
+	.event = wevent, .event_flags = SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD,	\
+	.priv = (void *)&(struct sst_ids) { .task_id = 0, .location_id = 0 }		\
+}
+
+#define SST_INPUT(wname, wevent)							\
+{	.id = snd_soc_dapm_input, .name = wname, .sname = NULL,				\
+	.reg = SND_SOC_NOPM, .shift = 0,						\
+	.on_val = 1, .off_val = 0,							\
+	.event = wevent, .event_flags = SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD,	\
+	.priv = (void *)&(struct sst_ids) { .task_id = 0, .location_id = 0 }		\
+}
+
+#define SST_OUTPUT(wname, wevent)							\
+{	.id = snd_soc_dapm_output, .name = wname, .sname = NULL,			\
+	.reg = SND_SOC_NOPM, .shift = 0,						\
+	.on_val = 1, .off_val = 0,							\
+	.event = wevent, .event_flags = SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD,	\
+	.priv = (void *)&(struct sst_ids) { .task_id = 0, .location_id = 0 }		\
+}
+
+#define SST_DAPM_OUTPUT(wname, wloc_id, wtask_id, wformat, wevent)                      \
+{	.id = snd_soc_dapm_output, .name = wname, .sname = NULL,                        \
+	.reg = SND_SOC_NOPM, .shift = 0,						\
+	.on_val = 1, .off_val = 0,							\
+	.event = wevent, .event_flags = SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD,   \
+	.priv = (void *)&(struct sst_ids) { .location_id = wloc_id, .task_id = wtask_id,\
+						.pcm_fmt = wformat, }			\
+}
+
+#define SST_PATH(wname, wtask, wloc_id, wevent, wflags)					\
+{	.id = snd_soc_dapm_pga, .name = wname, .reg = SND_SOC_NOPM, .shift = 0,		\
+	.kcontrol_news = NULL, .num_kcontrols = 0,				\
+	.on_val = 1, .off_val = 0,							\
+	.event = wevent, .event_flags = wflags,						\
+	.priv = (void *)&(struct sst_ids) { .task_id = wtask, .location_id = wloc_id, }	\
+}
+
+#define SST_LINKED_PATH(wname, wtask, wloc_id, linked_wname, wevent, wflags)		\
+{	.id = snd_soc_dapm_pga, .name = wname, .reg = SND_SOC_NOPM, .shift = 0,		\
+	.kcontrol_news = NULL, .num_kcontrols = 0,				\
+	.on_val = 1, .off_val = 0,							\
+	.event = wevent, .event_flags = wflags,						\
+	.priv = (void *)&(struct sst_ids) { .task_id = wtask, .location_id = wloc_id,	\
+					.parent_wname = linked_wname}			\
+}
+
+#define SST_PATH_MEDIA_LOOP(wname, wtask, wloc_id, wformat, wevent, wflags)             \
+{	.id = snd_soc_dapm_pga, .name = wname, .reg = SND_SOC_NOPM, .shift = 0,         \
+	.kcontrol_news = NULL, .num_kcontrols = 0,                         \
+	.event = wevent, .event_flags = wflags,                                         \
+	.priv = (void *)&(struct sst_ids) { .task_id = wtask, .location_id = wloc_id,	\
+					    .format = wformat,}				\
+}
+
+/* output is triggered before input */
+#define SST_PATH_INPUT(name, task_id, loc_id, event)					\
+	SST_PATH(name, task_id, loc_id, event, SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD)
+
+#define SST_PATH_LINKED_INPUT(name, task_id, loc_id, linked_wname, event)		\
+	SST_LINKED_PATH(name, task_id, loc_id, linked_wname, event,			\
+					SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD)
+
+#define SST_PATH_OUTPUT(name, task_id, loc_id, event)					\
+	SST_PATH(name, task_id, loc_id, event, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD)
+
+#define SST_PATH_LINKED_OUTPUT(name, task_id, loc_id, linked_wname, event)		\
+	SST_LINKED_PATH(name, task_id, loc_id, linked_wname, event,			\
+					SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD)
+
+#define SST_PATH_MEDIA_LOOP_OUTPUT(name, task_id, loc_id, format, event)		\
+	SST_PATH_MEDIA_LOOP(name, task_id, loc_id, format, event, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD)
+
+
+#define SST_SWM_MIXER(wname, wreg, wtask, wloc_id, wcontrols, wevent)			\
+{	.id = snd_soc_dapm_mixer, .name = wname, .reg = SND_SOC_NOPM, .shift = 0,	\
+	.kcontrol_news = wcontrols, .num_kcontrols = ARRAY_SIZE(wcontrols),\
+	.event = wevent, .event_flags = SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD |	\
+					SND_SOC_DAPM_POST_REG,				\
+	.priv = (void *)&(struct sst_ids) { .task_id = wtask, .location_id = wloc_id,	\
+					    .reg = wreg }				\
+}
+
+enum sst_gain_kcontrol_type {
+	SST_GAIN_TLV,
+	SST_GAIN_MUTE,
+	SST_GAIN_RAMP_DURATION,
+};
+
+struct sst_gain_mixer_control {
+	bool stereo;
+	enum sst_gain_kcontrol_type type;
+	struct sst_gain_value *gain_val;
+	int max;
+	int min;
+	u16 instance_id;
+	u16 module_id;
+	u16 pipe_id;
+	u16 task_id;
+	char pname[44];
+	struct snd_soc_dapm_widget *w;
+};
+
+struct sst_gain_value {
+	u16 ramp_duration;
+	s16 l_gain;
+	s16 r_gain;
+	bool mute;
+};
+#define SST_GAIN_VOLUME_DEFAULT		(-1440)
+#define SST_GAIN_RAMP_DURATION_DEFAULT	5 /* timeconstant */
+#define SST_GAIN_MUTE_DEFAULT		true
+
+#define SST_GAIN_KCONTROL_TLV(xname, xhandler_get, xhandler_put, \
+			      xmod, xpipe, xinstance, xtask, tlv_array, xgain_val, \
+			      xmin, xmax, xpname) \
+	.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
+	.access = SNDRV_CTL_ELEM_ACCESS_TLV_READ | \
+		  SNDRV_CTL_ELEM_ACCESS_READWRITE, \
+	.tlv.p = (tlv_array), \
+	.info = sst_gain_ctl_info,\
+	.get = xhandler_get, .put = xhandler_put, \
+	.private_value = (unsigned long)&(struct sst_gain_mixer_control) \
+	{ .stereo = true, .max = xmax, .min = xmin, .type = SST_GAIN_TLV, \
+	  .module_id = xmod, .pipe_id = xpipe, .task_id = xtask,\
+	  .instance_id = xinstance, .gain_val = xgain_val, .pname = xpname}
+
+#define SST_GAIN_KCONTROL_INT(xname, xhandler_get, xhandler_put, \
+			      xmod, xpipe, xinstance, xtask, xtype, xgain_val, \
+			      xmin, xmax, xpname) \
+	.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
+	.info = sst_gain_ctl_info, \
+	.get = xhandler_get, .put = xhandler_put, \
+	.private_value = (unsigned long)&(struct sst_gain_mixer_control) \
+	{ .stereo = false, .max = xmax, .min = xmin, .type = xtype, \
+	  .module_id = xmod, .pipe_id = xpipe, .task_id = xtask,\
+	  .instance_id = xinstance, .gain_val = xgain_val, .pname =  xpname}
+
+#define SST_GAIN_KCONTROL_BOOL(xname, xhandler_get, xhandler_put,\
+			       xmod, xpipe, xinstance, xtask, xgain_val, xpname) \
+	.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
+	.info = snd_soc_info_bool_ext, \
+	.get = xhandler_get, .put = xhandler_put, \
+	.private_value = (unsigned long)&(struct sst_gain_mixer_control) \
+	{ .stereo = false, .type = SST_GAIN_MUTE, \
+	  .module_id = xmod, .pipe_id = xpipe, .task_id = xtask,\
+	  .instance_id = xinstance, .gain_val = xgain_val, .pname = xpname}
 #define SST_CONTROL_NAME(xpname, xmname, xinstance, xtype) \
 	xpname " " xmname " " #xinstance " " xtype
 
 #define SST_COMBO_CONTROL_NAME(xpname, xmname, xinstance, xtype, xsubmodule) \
 	xpname " " xmname " " #xinstance " " xtype " " xsubmodule
+
+/*
+ * 3 Controls for each Gain module
+ * e.g.	- pcm0_in Gain 0 Volume
+ *	- pcm0_in Gain 0 Ramp Delay
+ *	- pcm0_in Gain 0 Switch
+ */
+#define SST_GAIN_KCONTROLS(xpname, xmname, xmin_gain, xmax_gain, xmin_tc, xmax_tc, \
+			   xhandler_get, xhandler_put, \
+			   xmod, xpipe, xinstance, xtask, tlv_array, xgain_val) \
+	{ SST_GAIN_KCONTROL_INT(SST_CONTROL_NAME(xpname, xmname, xinstance, "Ramp Delay"), \
+		xhandler_get, xhandler_put, xmod, xpipe, xinstance, xtask, SST_GAIN_RAMP_DURATION, \
+		xgain_val, xmin_tc, xmax_tc, xpname) }, \
+	{ SST_GAIN_KCONTROL_BOOL(SST_CONTROL_NAME(xpname, xmname, xinstance, "Switch"), \
+		xhandler_get, xhandler_put, xmod, xpipe, xinstance, xtask, \
+		xgain_val, xpname) } ,\
+	{ SST_GAIN_KCONTROL_TLV(SST_CONTROL_NAME(xpname, xmname, xinstance, "Volume"), \
+		xhandler_get, xhandler_put, xmod, xpipe, xinstance, xtask, tlv_array, \
+		xgain_val, xmin_gain, xmax_gain, xpname) }
+
+#define SST_GAIN_TC_MIN		5
+#define SST_GAIN_TC_MAX		5000
+#define SST_GAIN_MIN_VALUE	-1440 /* in 0.1 DB units */
+#define SST_GAIN_MAX_VALUE	360
+
 enum sst_algo_kcontrol_type {
 	SST_ALGO_PARAMS,
 	SST_ALGO_BYPASS,
@@ -439,4 +842,29 @@ struct sst_enum {
 	struct snd_soc_dapm_widget *w;
 };
 
+/* only 4 slots/channels supported atm */
+#define SST_SSP_SLOT_ENUM(s_ch_no, is_tx, xtexts) \
+	(struct sst_enum){ .reg = s_ch_no, .tx = is_tx, .max = 4+1, .texts = xtexts, }
+
+#define SST_SLOT_CTL_NAME(xpname, xmname, s_ch_name) \
+	xpname " " xmname " " s_ch_name
+
+#define SST_SSP_SLOT_CTL(xpname, xmname, s_ch_name, s_ch_no, is_tx, xtexts, xget, xput) \
+{	.iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
+	.name = SST_SLOT_CTL_NAME(xpname, xmname, s_ch_name), \
+	.info = sst_slot_enum_info, \
+	.get = xget, .put = xput, \
+	.private_value = (unsigned long)&SST_SSP_SLOT_ENUM(s_ch_no, is_tx, xtexts), \
+}
+
+#define SST_MUX_CTL_NAME(xpname, xinstance) \
+	xpname " " #xinstance
+
+#define SST_SSP_MUX_ENUM(xreg, xshift, xtexts) \
+	(struct soc_enum) SOC_ENUM_DOUBLE(xreg, xshift, xshift, ARRAY_SIZE(xtexts), xtexts)
+
+#define SST_SSP_MUX_CTL(xpname, xinstance, xreg, xshift, xtexts) \
+	SOC_DAPM_ENUM(SST_MUX_CTL_NAME(xpname, xinstance), \
+			  SST_SSP_MUX_ENUM(xreg, xshift, xtexts))
+
 #endif
diff --git a/sound/soc/intel/sst-baytrail-dsp.c b/sound/soc/intel/sst-baytrail-dsp.c
index fc588764ffa3..5a9e56700f31 100644
--- a/sound/soc/intel/sst-baytrail-dsp.c
+++ b/sound/soc/intel/sst-baytrail-dsp.c
@@ -67,17 +67,12 @@ static int sst_byt_parse_module(struct sst_dsp *dsp, struct sst_fw *fw,
 {
 	struct dma_block_info *block;
 	struct sst_module *mod;
-	struct sst_module_data block_data;
 	struct sst_module_template template;
 	int count;
 
 	memset(&template, 0, sizeof(template));
 	template.id = module->type;
 	template.entry = module->entry_point;
-	template.p.type = SST_MEM_DRAM;
-	template.p.data_type = SST_DATA_P;
-	template.s.type = SST_MEM_DRAM;
-	template.s.data_type = SST_DATA_S;
 
 	mod = sst_module_new(fw, &template, NULL);
 	if (mod == NULL)
@@ -94,19 +89,19 @@ static int sst_byt_parse_module(struct sst_dsp *dsp, struct sst_fw *fw,
 
 		switch (block->type) {
 		case SST_BYT_IRAM:
-			block_data.offset = block->ram_offset +
+			mod->offset = block->ram_offset +
 					    dsp->addr.iram_offset;
-			block_data.type = SST_MEM_IRAM;
+			mod->type = SST_MEM_IRAM;
 			break;
 		case SST_BYT_DRAM:
-			block_data.offset = block->ram_offset +
+			mod->offset = block->ram_offset +
 					    dsp->addr.dram_offset;
-			block_data.type = SST_MEM_DRAM;
+			mod->type = SST_MEM_DRAM;
 			break;
 		case SST_BYT_CACHE:
-			block_data.offset = block->ram_offset +
+			mod->offset = block->ram_offset +
 					    (dsp->addr.fw_ext - dsp->addr.lpe);
-			block_data.type = SST_MEM_CACHE;
+			mod->type = SST_MEM_CACHE;
 			break;
 		default:
 			dev_err(dsp->dev, "wrong ram type 0x%x in block0x%x\n",
@@ -114,11 +109,10 @@ static int sst_byt_parse_module(struct sst_dsp *dsp, struct sst_fw *fw,
 			return -EINVAL;
 		}
 
-		block_data.size = block->size;
-		block_data.data_type = SST_DATA_M;
-		block_data.data = (void *)block + sizeof(*block);
+		mod->size = block->size;
+		mod->data = (void *)block + sizeof(*block);
 
-		sst_module_insert_fixed_block(mod, &block_data);
+		sst_module_alloc_blocks(mod);
 
 		block = (void *)block + sizeof(*block) + block->size;
 	}
diff --git a/sound/soc/intel/sst-dsp-priv.h b/sound/soc/intel/sst-dsp-priv.h
index ffb308bd81ce..b9da030e312d 100644
--- a/sound/soc/intel/sst-dsp-priv.h
+++ b/sound/soc/intel/sst-dsp-priv.h
@@ -26,6 +26,9 @@ struct sst_mem_block;
 struct sst_module;
 struct sst_fw;
 
+/* do we need to remove or keep */
+#define DSP_DRAM_ADDR_OFFSET		0x400000
+
 /*
  * DSP Operations exported by platform Audio DSP driver.
  */
@@ -33,6 +36,9 @@ struct sst_ops {
 	/* DSP core boot / reset */
 	void (*boot)(struct sst_dsp *);
 	void (*reset)(struct sst_dsp *);
+	int (*wake)(struct sst_dsp *);
+	void (*sleep)(struct sst_dsp *);
+	void (*stall)(struct sst_dsp *);
 
 	/* Shim IO */
 	void (*write)(void __iomem *addr, u32 offset, u32 value);
@@ -67,6 +73,8 @@ struct sst_addr {
 	u32 shim_offset;
 	u32 iram_offset;
 	u32 dram_offset;
+	u32 dsp_iram_offset;
+	u32 dsp_dram_offset;
 	void __iomem *lpe;
 	void __iomem *shim;
 	void __iomem *pci_cfg;
@@ -84,15 +92,6 @@ struct sst_mailbox {
 };
 
 /*
- * Audio DSP Firmware data types.
- */
-enum sst_data_type {
-	SST_DATA_M	= 0, /* module block data */
-	SST_DATA_P	= 1, /* peristant data (text, data) */
-	SST_DATA_S	= 2, /* scratch data (usually buffers) */
-};
-
-/*
  * Audio DSP memory block types.
  */
 enum sst_mem_type {
@@ -125,23 +124,6 @@ struct sst_fw {
 };
 
 /*
- * Audio DSP Generic Module data.
- *
- * This is used to dsecribe any sections of persistent (text and data) and
- * scratch (buffers) of module data in ADSP memory space.
- */
-struct sst_module_data {
-
-	enum sst_mem_type type;		/* destination memory type */
-	enum sst_data_type data_type;	/* type of module data */
-
-	u32 size;		/* size in bytes */
-	int32_t offset;		/* offset in FW file */
-	u32 data_offset;	/* offset in ADSP memory space */
-	void *data;		/* module data */
-};
-
-/*
  * Audio DSP Generic Module Template.
  *
  * Used to define and register a new FW module. This data is extracted from
@@ -150,15 +132,52 @@ struct sst_module_data {
 struct sst_module_template {
 	u32 id;
 	u32 entry;			/* entry point */
-	struct sst_module_data s;	/* scratch data */
-	struct sst_module_data p;	/* peristant data */
+	u32 scratch_size;
+	u32 persistent_size;
+};
+
+/*
+ * Block Allocator - Used to allocate blocks of DSP memory.
+ */
+struct sst_block_allocator {
+	u32 id;
+	u32 offset;
+	int size;
+	enum sst_mem_type type;
+};
+
+/*
+ * Runtime Module Instance - A module object can be instanciated multiple
+ * times within the DSP FW.
+ */
+struct sst_module_runtime {
+	struct sst_dsp *dsp;
+	int id;
+	struct sst_module *module;	/* parent module we belong too */
+
+	u32 persistent_offset;		/* private memory offset */
+	void *private;
+
+	struct list_head list;
+	struct list_head block_list;	/* list of blocks used */
+};
+
+/*
+ * Runtime Module Context - The runtime context must be manually stored by the
+ * driver prior to enter S3 and restored after leaving S3. This should really be
+ * part of the memory context saved by the enter D3 message IPC ???
+ */
+struct sst_module_runtime_context {
+	dma_addr_t dma_buffer;
+	u32 *buffer;
 };
 
 /*
  * Audio DSP Generic Module.
  *
  * Each Firmware file can consist of 1..N modules. A module can span multiple
- * ADSP memory blocks. The simplest FW will be a file with 1 module.
+ * ADSP memory blocks. The simplest FW will be a file with 1 module. A module
+ * can be instanciated multiple times in the DSP.
  */
 struct sst_module {
 	struct sst_dsp *dsp;
@@ -167,10 +186,13 @@ struct sst_module {
 	/* module configuration */
 	u32 id;
 	u32 entry;			/* module entry point */
-	u32 offset;			/* module offset in firmware file */
+	s32 offset;			/* module offset in firmware file */
 	u32 size;			/* module size */
-	struct sst_module_data s;	/* scratch data */
-	struct sst_module_data p;	/* peristant data */
+	u32 scratch_size;		/* global scratch memory required */
+	u32 persistent_size;		/* private memory required */
+	enum sst_mem_type type;		/* destination memory type */
+	u32 data_offset;		/* offset in ADSP memory space */
+	void *data;			/* module data */
 
 	/* runtime */
 	u32 usage_count;		/* can be unloaded if count == 0 */
@@ -180,6 +202,7 @@ struct sst_module {
 	struct list_head block_list;	/* Module list of blocks in use */
 	struct list_head list;		/* DSP list of modules */
 	struct list_head list_fw;	/* FW list of modules */
+	struct list_head runtime_list;	/* list of runtime module objects*/
 };
 
 /*
@@ -208,7 +231,6 @@ struct sst_mem_block {
 	struct sst_block_ops *ops;	/* block operations, if any */
 
 	/* block status */
-	enum sst_data_type data_type;	/* data type held in this block */
 	u32 bytes_used;			/* bytes in use by modules */
 	void *private;			/* generic core does not touch this */
 	int users;			/* number of modules using this block */
@@ -253,6 +275,11 @@ struct sst_dsp {
 	struct list_head module_list;
 	struct list_head fw_list;
 
+	/* scratch buffer */
+	struct list_head scratch_block_list;
+	u32 scratch_offset;
+	u32 scratch_size;
+
 	/* platform data */
 	struct sst_pdata *pdata;
 
@@ -290,18 +317,33 @@ void sst_fw_unload(struct sst_fw *sst_fw);
 /* Create/Free firmware modules */
 struct sst_module *sst_module_new(struct sst_fw *sst_fw,
 	struct sst_module_template *template, void *private);
-void sst_module_free(struct sst_module *sst_module);
-int sst_module_insert(struct sst_module *sst_module);
-int sst_module_remove(struct sst_module *sst_module);
-int sst_module_insert_fixed_block(struct sst_module *module,
-	struct sst_module_data *data);
+void sst_module_free(struct sst_module *module);
 struct sst_module *sst_module_get_from_id(struct sst_dsp *dsp, u32 id);
-
-/* allocate/free pesistent/scratch memory regions managed by drv */
-struct sst_module *sst_mem_block_alloc_scratch(struct sst_dsp *dsp);
-void sst_mem_block_free_scratch(struct sst_dsp *dsp,
-	struct sst_module *scratch);
-int sst_block_module_remove(struct sst_module *module);
+int sst_module_alloc_blocks(struct sst_module *module);
+int sst_module_free_blocks(struct sst_module *module);
+
+/* Create/Free firmware module runtime instances */
+struct sst_module_runtime *sst_module_runtime_new(struct sst_module *module,
+	int id, void *private);
+void sst_module_runtime_free(struct sst_module_runtime *runtime);
+struct sst_module_runtime *sst_module_runtime_get_from_id(
+	struct sst_module *module, u32 id);
+int sst_module_runtime_alloc_blocks(struct sst_module_runtime *runtime,
+	int offset);
+int sst_module_runtime_free_blocks(struct sst_module_runtime *runtime);
+int sst_module_runtime_save(struct sst_module_runtime *runtime,
+	struct sst_module_runtime_context *context);
+int sst_module_runtime_restore(struct sst_module_runtime *runtime,
+	struct sst_module_runtime_context *context);
+
+/* generic block allocation */
+int sst_alloc_blocks(struct sst_dsp *dsp, struct sst_block_allocator *ba,
+	struct list_head *block_list);
+int sst_free_blocks(struct sst_dsp *dsp, struct list_head *block_list);
+
+/* scratch allocation */
+int sst_block_alloc_scratch(struct sst_dsp *dsp);
+void sst_block_free_scratch(struct sst_dsp *dsp);
 
 /* Register the DSPs memory blocks - would be nice to read from ACPI */
 struct sst_mem_block *sst_mem_block_register(struct sst_dsp *dsp, u32 offset,
@@ -309,4 +351,10 @@ struct sst_mem_block *sst_mem_block_register(struct sst_dsp *dsp, u32 offset,
 	void *private);
 void sst_mem_block_unregister_all(struct sst_dsp *dsp);
 
+/* Create/Free DMA resources */
+int sst_dma_new(struct sst_dsp *sst);
+void sst_dma_free(struct sst_dma *dma);
+
+u32 sst_dsp_get_offset(struct sst_dsp *dsp, u32 offset,
+	enum sst_mem_type type);
 #endif
diff --git a/sound/soc/intel/sst-dsp.c b/sound/soc/intel/sst-dsp.c
index cd23060a0d86..86e410845670 100644
--- a/sound/soc/intel/sst-dsp.c
+++ b/sound/soc/intel/sst-dsp.c
@@ -245,6 +245,29 @@ int sst_dsp_boot(struct sst_dsp *sst)
 }
 EXPORT_SYMBOL_GPL(sst_dsp_boot);
 
+int sst_dsp_wake(struct sst_dsp *sst)
+{
+	if (sst->ops->wake)
+		return sst->ops->wake(sst);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(sst_dsp_wake);
+
+void sst_dsp_sleep(struct sst_dsp *sst)
+{
+	if (sst->ops->sleep)
+		sst->ops->sleep(sst);
+}
+EXPORT_SYMBOL_GPL(sst_dsp_sleep);
+
+void sst_dsp_stall(struct sst_dsp *sst)
+{
+	if (sst->ops->stall)
+		sst->ops->stall(sst);
+}
+EXPORT_SYMBOL_GPL(sst_dsp_stall);
+
 void sst_dsp_ipc_msg_tx(struct sst_dsp *dsp, u32 msg)
 {
 	sst_dsp_shim_write_unlocked(dsp, SST_IPCX, msg | SST_IPCX_BUSY);
@@ -352,6 +375,7 @@ struct sst_dsp *sst_dsp_new(struct device *dev,
 	INIT_LIST_HEAD(&sst->free_block_list);
 	INIT_LIST_HEAD(&sst->module_list);
 	INIT_LIST_HEAD(&sst->fw_list);
+	INIT_LIST_HEAD(&sst->scratch_block_list);
 
 	/* Initialise SST Audio DSP */
 	if (sst->ops->init) {
@@ -366,6 +390,10 @@ struct sst_dsp *sst_dsp_new(struct device *dev,
 	if (err)
 		goto irq_err;
 
+	err = sst_dma_new(sst);
+	if (err)
+		dev_warn(dev, "sst_dma_new failed %d\n", err);
+
 	return sst;
 
 irq_err:
@@ -381,6 +409,9 @@ void sst_dsp_free(struct sst_dsp *sst)
 	free_irq(sst->irq, sst);
 	if (sst->ops->free)
 		sst->ops->free(sst);
+
+	if (sst->dma)
+		sst_dma_free(sst->dma);
 }
 EXPORT_SYMBOL_GPL(sst_dsp_free);
 
diff --git a/sound/soc/intel/sst-dsp.h b/sound/soc/intel/sst-dsp.h
index 3165dfa97408..f291e32f0077 100644
--- a/sound/soc/intel/sst-dsp.h
+++ b/sound/soc/intel/sst-dsp.h
@@ -30,6 +30,9 @@
 #define SST_DMA_TYPE_DW		1
 #define SST_DMA_TYPE_MID	2
 
+/* autosuspend delay 5s*/
+#define SST_RUNTIME_SUSPEND_DELAY	(5 * 1000)
+
 /* SST Shim register map
  * The register naming can differ between products. Some products also
  * contain extra functionality.
@@ -156,12 +159,18 @@
 #define SST_VDRTCTL3		0xaC
 
 /* VDRTCTL0 */
-#define SST_VDRTCL0_APLLSE_MASK		1
-#define SST_VDRTCL0_DSRAMPGE_SHIFT	16
-#define SST_VDRTCL0_DSRAMPGE_MASK	(0xffff << SST_VDRTCL0_DSRAMPGE_SHIFT)
-#define SST_VDRTCL0_ISRAMPGE_SHIFT	6
+#define SST_VDRTCL0_D3PGD		(1 << 0)
+#define SST_VDRTCL0_D3SRAMPGD		(1 << 1)
+#define SST_VDRTCL0_DSRAMPGE_SHIFT	12
+#define SST_VDRTCL0_DSRAMPGE_MASK	(0xfffff << SST_VDRTCL0_DSRAMPGE_SHIFT)
+#define SST_VDRTCL0_ISRAMPGE_SHIFT	2
 #define SST_VDRTCL0_ISRAMPGE_MASK	(0x3ff << SST_VDRTCL0_ISRAMPGE_SHIFT)
 
+/* VDRTCTL2 */
+#define SST_VDRTCL2_DCLCGE		(1 << 1)
+#define SST_VDRTCL2_DTCGE		(1 << 10)
+#define SST_VDRTCL2_APLLSE_MASK		(1 << 31)
+
 /* PMCS */
 #define SST_PMCS		0x84
 #define SST_PMCS_PS_MASK	0x3
@@ -245,6 +254,17 @@ void sst_memcpy_fromio_32(struct sst_dsp *sst,
 /* DSP reset & boot */
 void sst_dsp_reset(struct sst_dsp *sst);
 int sst_dsp_boot(struct sst_dsp *sst);
+int sst_dsp_wake(struct sst_dsp *sst);
+void sst_dsp_sleep(struct sst_dsp *sst);
+void sst_dsp_stall(struct sst_dsp *sst);
+
+/* DMA */
+int sst_dsp_dma_get_channel(struct sst_dsp *dsp, int chan_id);
+void sst_dsp_dma_put_channel(struct sst_dsp *dsp);
+int sst_dsp_dma_copyfrom(struct sst_dsp *sst, dma_addr_t dest_addr,
+	dma_addr_t src_addr, size_t size);
+int sst_dsp_dma_copyto(struct sst_dsp *sst, dma_addr_t dest_addr,
+	dma_addr_t src_addr, size_t size);
 
 /* Msg IO */
 void sst_dsp_ipc_msg_tx(struct sst_dsp *dsp, u32 msg);
diff --git a/sound/soc/intel/sst-firmware.c b/sound/soc/intel/sst-firmware.c
index 3bb43dac892d..4a5bde9c686b 100644
--- a/sound/soc/intel/sst-firmware.c
+++ b/sound/soc/intel/sst-firmware.c
@@ -23,6 +23,11 @@
 #include <linux/dma-mapping.h>
 #include <linux/dmaengine.h>
 #include <linux/pci.h>
+#include <linux/acpi.h>
+
+/* supported DMA engine drivers */
+#include <linux/platform_data/dma-dw.h>
+#include <linux/dma/dw.h>
 
 #include <asm/page.h>
 #include <asm/pgtable.h>
@@ -30,16 +35,301 @@
 #include "sst-dsp.h"
 #include "sst-dsp-priv.h"
 
-static void block_module_remove(struct sst_module *module);
+#define SST_DMA_RESOURCES	2
+#define SST_DSP_DMA_MAX_BURST	0x3
+#define SST_HSW_BLOCK_ANY	0xffffffff
+
+#define SST_HSW_MASK_DMA_ADDR_DSP 0xfff00000
+
+struct sst_dma {
+	struct sst_dsp *sst;
+
+	struct dw_dma_chip *chip;
+
+	struct dma_async_tx_descriptor *desc;
+	struct dma_chan *ch;
+};
+
+static inline void sst_memcpy32(volatile void __iomem *dest, void *src, u32 bytes)
+{
+	/* __iowrite32_copy use 32bit size values so divide by 4 */
+	__iowrite32_copy((void *)dest, src, bytes/4);
+}
+
+static void sst_dma_transfer_complete(void *arg)
+{
+	struct sst_dsp *sst = (struct sst_dsp *)arg;
+
+	dev_dbg(sst->dev, "DMA: callback\n");
+}
+
+static int sst_dsp_dma_copy(struct sst_dsp *sst, dma_addr_t dest_addr,
+	dma_addr_t src_addr, size_t size)
+{
+	struct dma_async_tx_descriptor *desc;
+	struct sst_dma *dma = sst->dma;
+
+	if (dma->ch == NULL) {
+		dev_err(sst->dev, "error: no DMA channel\n");
+		return -ENODEV;
+	}
+
+	dev_dbg(sst->dev, "DMA: src: 0x%lx dest 0x%lx size %zu\n",
+		(unsigned long)src_addr, (unsigned long)dest_addr, size);
+
+	desc = dma->ch->device->device_prep_dma_memcpy(dma->ch, dest_addr,
+		src_addr, size, DMA_CTRL_ACK);
+	if (!desc){
+		dev_err(sst->dev, "error: dma prep memcpy failed\n");
+		return -EINVAL;
+	}
+
+	desc->callback = sst_dma_transfer_complete;
+	desc->callback_param = sst;
+
+	desc->tx_submit(desc);
+	dma_wait_for_async_tx(desc);
+
+	return 0;
+}
+
+/* copy to DSP */
+int sst_dsp_dma_copyto(struct sst_dsp *sst, dma_addr_t dest_addr,
+	dma_addr_t src_addr, size_t size)
+{
+	return sst_dsp_dma_copy(sst, dest_addr | SST_HSW_MASK_DMA_ADDR_DSP,
+			src_addr, size);
+}
+EXPORT_SYMBOL_GPL(sst_dsp_dma_copyto);
+
+/* copy from DSP */
+int sst_dsp_dma_copyfrom(struct sst_dsp *sst, dma_addr_t dest_addr,
+	dma_addr_t src_addr, size_t size)
+{
+	return sst_dsp_dma_copy(sst, dest_addr,
+		src_addr | SST_HSW_MASK_DMA_ADDR_DSP, size);
+}
+EXPORT_SYMBOL_GPL(sst_dsp_dma_copyfrom);
+
+/* remove module from memory - callers hold locks */
+static void block_list_remove(struct sst_dsp *dsp,
+	struct list_head *block_list)
+{
+	struct sst_mem_block *block, *tmp;
+	int err;
+
+	/* disable each block  */
+	list_for_each_entry(block, block_list, module_list) {
+
+		if (block->ops && block->ops->disable) {
+			err = block->ops->disable(block);
+			if (err < 0)
+				dev_err(dsp->dev,
+					"error: cant disable block %d:%d\n",
+					block->type, block->index);
+		}
+	}
+
+	/* mark each block as free */
+	list_for_each_entry_safe(block, tmp, block_list, module_list) {
+		list_del(&block->module_list);
+		list_move(&block->list, &dsp->free_block_list);
+		dev_dbg(dsp->dev, "block freed %d:%d at offset 0x%x\n",
+			block->type, block->index, block->offset);
+	}
+}
+
+/* prepare the memory block to receive data from host - callers hold locks */
+static int block_list_prepare(struct sst_dsp *dsp,
+	struct list_head *block_list)
+{
+	struct sst_mem_block *block;
+	int ret = 0;
+
+	/* enable each block so that's it'e ready for data */
+	list_for_each_entry(block, block_list, module_list) {
+
+		if (block->ops && block->ops->enable && !block->users) {
+			ret = block->ops->enable(block);
+			if (ret < 0) {
+				dev_err(dsp->dev,
+					"error: cant disable block %d:%d\n",
+					block->type, block->index);
+				goto err;
+			}
+		}
+	}
+	return ret;
+
+err:
+	list_for_each_entry(block, block_list, module_list) {
+		if (block->ops && block->ops->disable)
+			block->ops->disable(block);
+	}
+	return ret;
+}
+
+static struct dw_dma_platform_data dw_pdata = {
+	.is_private = 1,
+	.chan_allocation_order = CHAN_ALLOCATION_ASCENDING,
+	.chan_priority = CHAN_PRIORITY_ASCENDING,
+};
+
+static struct dw_dma_chip *dw_probe(struct device *dev, struct resource *mem,
+	int irq)
+{
+	struct dw_dma_chip *chip;
+	int err;
+
+	chip = devm_kzalloc(dev, sizeof(*chip), GFP_KERNEL);
+	if (!chip)
+		return ERR_PTR(-ENOMEM);
+
+	chip->irq = irq;
+	chip->regs = devm_ioremap_resource(dev, mem);
+	if (IS_ERR(chip->regs))
+		return ERR_CAST(chip->regs);
+
+	err = dma_coerce_mask_and_coherent(dev, DMA_BIT_MASK(31));
+	if (err)
+		return ERR_PTR(err);
+
+	chip->dev = dev;
+	err = dw_dma_probe(chip, &dw_pdata);
+	if (err)
+		return ERR_PTR(err);
+
+	return chip;
+}
+
+static void dw_remove(struct dw_dma_chip *chip)
+{
+	dw_dma_remove(chip);
+}
+
+static bool dma_chan_filter(struct dma_chan *chan, void *param)
+{
+	struct sst_dsp *dsp = (struct sst_dsp *)param;
+
+	return chan->device->dev == dsp->dma_dev;
+}
 
-static void sst_memcpy32(volatile void __iomem *dest, void *src, u32 bytes)
+int sst_dsp_dma_get_channel(struct sst_dsp *dsp, int chan_id)
 {
-	u32 i;
+	struct sst_dma *dma = dsp->dma;
+	struct dma_slave_config slave;
+	dma_cap_mask_t mask;
+	int ret;
+
+	/* The Intel MID DMA engine driver needs the slave config set but
+	 * Synopsis DMA engine driver safely ignores the slave config */
+	dma_cap_zero(mask);
+	dma_cap_set(DMA_SLAVE, mask);
+	dma_cap_set(DMA_MEMCPY, mask);
+
+	dma->ch = dma_request_channel(mask, dma_chan_filter, dsp);
+	if (dma->ch == NULL) {
+		dev_err(dsp->dev, "error: DMA request channel failed\n");
+		return -EIO;
+	}
+
+	memset(&slave, 0, sizeof(slave));
+	slave.direction = DMA_MEM_TO_DEV;
+	slave.src_addr_width =
+		slave.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
+	slave.src_maxburst = slave.dst_maxburst = SST_DSP_DMA_MAX_BURST;
+
+	ret = dmaengine_slave_config(dma->ch, &slave);
+	if (ret) {
+		dev_err(dsp->dev, "error: unable to set DMA slave config %d\n",
+			ret);
+		dma_release_channel(dma->ch);
+		dma->ch = NULL;
+	}
 
-	/* copy one 32 bit word at a time as 64 bit access is not supported */
-	for (i = 0; i < bytes; i += 4)
-		memcpy_toio(dest + i, src + i, 4);
+	return ret;
 }
+EXPORT_SYMBOL_GPL(sst_dsp_dma_get_channel);
+
+void sst_dsp_dma_put_channel(struct sst_dsp *dsp)
+{
+	struct sst_dma *dma = dsp->dma;
+
+	if (!dma->ch)
+		return;
+
+	dma_release_channel(dma->ch);
+	dma->ch = NULL;
+}
+EXPORT_SYMBOL_GPL(sst_dsp_dma_put_channel);
+
+int sst_dma_new(struct sst_dsp *sst)
+{
+	struct sst_pdata *sst_pdata = sst->pdata;
+	struct sst_dma *dma;
+	struct resource mem;
+	const char *dma_dev_name;
+	int ret = 0;
+
+	/* configure the correct platform data for whatever DMA engine
+	* is attached to the ADSP IP. */
+	switch (sst->pdata->dma_engine) {
+	case SST_DMA_TYPE_DW:
+		dma_dev_name = "dw_dmac";
+		break;
+	case SST_DMA_TYPE_MID:
+		dma_dev_name = "Intel MID DMA";
+		break;
+	default:
+		dev_err(sst->dev, "error: invalid DMA engine %d\n",
+			sst->pdata->dma_engine);
+		return -EINVAL;
+	}
+
+	dma = devm_kzalloc(sst->dev, sizeof(struct sst_dma), GFP_KERNEL);
+	if (!dma)
+		return -ENOMEM;
+
+	dma->sst = sst;
+
+	memset(&mem, 0, sizeof(mem));
+
+	mem.start = sst->addr.lpe_base + sst_pdata->dma_base;
+	mem.end   = sst->addr.lpe_base + sst_pdata->dma_base + sst_pdata->dma_size - 1;
+	mem.flags = IORESOURCE_MEM;
+
+	/* now register DMA engine device */
+	dma->chip = dw_probe(sst->dma_dev, &mem, sst_pdata->irq);
+	if (IS_ERR(dma->chip)) {
+		dev_err(sst->dev, "error: DMA device register failed\n");
+		ret = PTR_ERR(dma->chip);
+		goto err_dma_dev;
+	}
+
+	sst->dma = dma;
+	sst->fw_use_dma = true;
+	return 0;
+
+err_dma_dev:
+	devm_kfree(sst->dev, dma);
+	return ret;
+}
+EXPORT_SYMBOL(sst_dma_new);
+
+void sst_dma_free(struct sst_dma *dma)
+{
+
+	if (dma == NULL)
+		return;
+
+	if (dma->ch)
+		dma_release_channel(dma->ch);
+
+	if (dma->chip)
+		dw_remove(dma->chip);
+
+}
+EXPORT_SYMBOL(sst_dma_free);
 
 /* create new generic firmware object */
 struct sst_fw *sst_fw_new(struct sst_dsp *dsp, 
@@ -71,6 +361,12 @@ struct sst_fw *sst_fw_new(struct sst_dsp *dsp,
 	/* copy FW data to DMA-able memory */
 	memcpy((void *)sst_fw->dma_buf, (void *)fw->data, fw->size);
 
+	if (dsp->fw_use_dma) {
+		err = sst_dsp_dma_get_channel(dsp, 0);
+		if (err < 0)
+			goto chan_err;
+	}
+
 	/* call core specific FW paser to load FW data into DSP */
 	err = dsp->ops->parse_fw(sst_fw);
 	if (err < 0) {
@@ -78,6 +374,9 @@ struct sst_fw *sst_fw_new(struct sst_dsp *dsp,
 		goto parse_err;
 	}
 
+	if (dsp->fw_use_dma)
+		sst_dsp_dma_put_channel(dsp);
+
 	mutex_lock(&dsp->mutex);
 	list_add(&sst_fw->list, &dsp->fw_list);
 	mutex_unlock(&dsp->mutex);
@@ -85,9 +384,13 @@ struct sst_fw *sst_fw_new(struct sst_dsp *dsp,
 	return sst_fw;
 
 parse_err:
-	dma_free_coherent(dsp->dev, sst_fw->size,
+	if (dsp->fw_use_dma)
+		sst_dsp_dma_put_channel(dsp);
+chan_err:
+	dma_free_coherent(dsp->dma_dev, sst_fw->size,
 				sst_fw->dma_buf,
 				sst_fw->dmable_fw_paddr);
+	sst_fw->dma_buf = NULL;
 	kfree(sst_fw);
 	return NULL;
 }
@@ -111,21 +414,37 @@ EXPORT_SYMBOL_GPL(sst_fw_reload);
 
 void sst_fw_unload(struct sst_fw *sst_fw)
 {
-        struct sst_dsp *dsp = sst_fw->dsp;
-        struct sst_module *module, *tmp;
+	struct sst_dsp *dsp = sst_fw->dsp;
+	struct sst_module *module, *mtmp;
+	struct sst_module_runtime *runtime, *rtmp;
+
+	dev_dbg(dsp->dev, "unloading firmware\n");
+
+	mutex_lock(&dsp->mutex);
+
+	/* check module by module */
+	list_for_each_entry_safe(module, mtmp, &dsp->module_list, list) {
+		if (module->sst_fw == sst_fw) {
+
+			/* remove runtime modules */
+			list_for_each_entry_safe(runtime, rtmp, &module->runtime_list, list) {
 
-        dev_dbg(dsp->dev, "unloading firmware\n");
+				block_list_remove(dsp, &runtime->block_list);
+				list_del(&runtime->list);
+				kfree(runtime);
+			}
+
+			/* now remove the module */
+			block_list_remove(dsp, &module->block_list);
+			list_del(&module->list);
+			kfree(module);
+		}
+	}
 
-        mutex_lock(&dsp->mutex);
-        list_for_each_entry_safe(module, tmp, &dsp->module_list, list) {
-                if (module->sst_fw == sst_fw) {
-                        block_module_remove(module);
-                        list_del(&module->list);
-                        kfree(module);
-                }
-        }
+	/* remove all scratch blocks */
+	block_list_remove(dsp, &dsp->scratch_block_list);
 
-        mutex_unlock(&dsp->mutex);
+	mutex_unlock(&dsp->mutex);
 }
 EXPORT_SYMBOL_GPL(sst_fw_unload);
 
@@ -138,7 +457,8 @@ void sst_fw_free(struct sst_fw *sst_fw)
 	list_del(&sst_fw->list);
 	mutex_unlock(&dsp->mutex);
 
-	dma_free_coherent(dsp->dma_dev, sst_fw->size, sst_fw->dma_buf,
+	if (sst_fw->dma_buf)
+		dma_free_coherent(dsp->dma_dev, sst_fw->size, sst_fw->dma_buf,
 			sst_fw->dmable_fw_paddr);
 	kfree(sst_fw);
 }
@@ -175,11 +495,11 @@ struct sst_module *sst_module_new(struct sst_fw *sst_fw,
 	sst_module->id = template->id;
 	sst_module->dsp = dsp;
 	sst_module->sst_fw = sst_fw;
-
-	memcpy(&sst_module->s, &template->s, sizeof(struct sst_module_data));
-	memcpy(&sst_module->p, &template->p, sizeof(struct sst_module_data));
+	sst_module->scratch_size = template->scratch_size;
+	sst_module->persistent_size = template->persistent_size;
 
 	INIT_LIST_HEAD(&sst_module->block_list);
+	INIT_LIST_HEAD(&sst_module->runtime_list);
 
 	mutex_lock(&dsp->mutex);
 	list_add(&sst_module->list, &dsp->module_list);
@@ -202,73 +522,122 @@ void sst_module_free(struct sst_module *sst_module)
 }
 EXPORT_SYMBOL_GPL(sst_module_free);
 
-static struct sst_mem_block *find_block(struct sst_dsp *dsp, int type,
-	u32 offset)
+struct sst_module_runtime *sst_module_runtime_new(struct sst_module *module,
+	int id, void *private)
+{
+	struct sst_dsp *dsp = module->dsp;
+	struct sst_module_runtime *runtime;
+
+	runtime = kzalloc(sizeof(*runtime), GFP_KERNEL);
+	if (runtime == NULL)
+		return NULL;
+
+	runtime->id = id;
+	runtime->dsp = dsp;
+	runtime->module = module;
+	INIT_LIST_HEAD(&runtime->block_list);
+
+	mutex_lock(&dsp->mutex);
+	list_add(&runtime->list, &module->runtime_list);
+	mutex_unlock(&dsp->mutex);
+
+	return runtime;
+}
+EXPORT_SYMBOL_GPL(sst_module_runtime_new);
+
+void sst_module_runtime_free(struct sst_module_runtime *runtime)
+{
+	struct sst_dsp *dsp = runtime->dsp;
+
+	mutex_lock(&dsp->mutex);
+	list_del(&runtime->list);
+	mutex_unlock(&dsp->mutex);
+
+	kfree(runtime);
+}
+EXPORT_SYMBOL_GPL(sst_module_runtime_free);
+
+static struct sst_mem_block *find_block(struct sst_dsp *dsp,
+	struct sst_block_allocator *ba)
 {
 	struct sst_mem_block *block;
 
 	list_for_each_entry(block, &dsp->free_block_list, list) {
-		if (block->type == type && block->offset == offset)
+		if (block->type == ba->type && block->offset == ba->offset)
 			return block;
 	}
 
 	return NULL;
 }
 
-static int block_alloc_contiguous(struct sst_module *module,
-	struct sst_module_data *data, u32 offset, int size)
+/* Block allocator must be on block boundary */
+static int block_alloc_contiguous(struct sst_dsp *dsp,
+	struct sst_block_allocator *ba, struct list_head *block_list)
 {
 	struct list_head tmp = LIST_HEAD_INIT(tmp);
-	struct sst_dsp *dsp = module->dsp;
 	struct sst_mem_block *block;
+	u32 block_start = SST_HSW_BLOCK_ANY;
+	int size = ba->size, offset = ba->offset;
+
+	while (ba->size > 0) {
 
-	while (size > 0) {
-		block = find_block(dsp, data->type, offset);
+		block = find_block(dsp, ba);
 		if (!block) {
 			list_splice(&tmp, &dsp->free_block_list);
+
+			ba->size = size;
+			ba->offset = offset;
 			return -ENOMEM;
 		}
 
 		list_move_tail(&block->list, &tmp);
-		offset += block->size;
-		size -= block->size;
+		ba->offset += block->size;
+		ba->size -= block->size;
 	}
+	ba->size = size;
+	ba->offset = offset;
+
+	list_for_each_entry(block, &tmp, list) {
+
+		if (block->offset < block_start)
+			block_start = block->offset;
 
-	list_for_each_entry(block, &tmp, list)
-		list_add(&block->module_list, &module->block_list);
+		list_add(&block->module_list, block_list);
+
+		dev_dbg(dsp->dev, "block allocated %d:%d at offset 0x%x\n",
+			block->type, block->index, block->offset);
+	}
 
 	list_splice(&tmp, &dsp->used_block_list);
 	return 0;
 }
 
-/* allocate free DSP blocks for module data - callers hold locks */
-static int block_alloc(struct sst_module *module,
-	struct sst_module_data *data)
+/* allocate first free DSP blocks for data - callers hold locks */
+static int block_alloc(struct sst_dsp *dsp, struct sst_block_allocator *ba,
+	struct list_head *block_list)
 {
-	struct sst_dsp *dsp = module->dsp;
 	struct sst_mem_block *block, *tmp;
 	int ret = 0;
 
-	if (data->size == 0)
+	if (ba->size == 0)
 		return 0;
 
 	/* find first free whole blocks that can hold module */
 	list_for_each_entry_safe(block, tmp, &dsp->free_block_list, list) {
 
 		/* ignore blocks with wrong type */
-		if (block->type != data->type)
+		if (block->type != ba->type)
 			continue;
 
-		if (data->size > block->size)
+		if (ba->size > block->size)
 			continue;
 
-		data->offset = block->offset;
-		block->data_type = data->data_type;
-		block->bytes_used = data->size % block->size;
-		list_add(&block->module_list, &module->block_list);
+		ba->offset = block->offset;
+		block->bytes_used = ba->size % block->size;
+		list_add(&block->module_list, block_list);
 		list_move(&block->list, &dsp->used_block_list);
-		dev_dbg(dsp->dev, " *module %d added block %d:%d\n",
-			module->id, block->type, block->index);
+		dev_dbg(dsp->dev, "block allocated %d:%d at offset 0x%x\n",
+			block->type, block->index, block->offset);
 		return 0;
 	}
 
@@ -276,15 +645,19 @@ static int block_alloc(struct sst_module *module,
 	list_for_each_entry_safe(block, tmp, &dsp->free_block_list, list) {
 
 		/* ignore blocks with wrong type */
-		if (block->type != data->type)
+		if (block->type != ba->type)
 			continue;
 
 		/* do we span > 1 blocks */
-		if (data->size > block->size) {
-			ret = block_alloc_contiguous(module, data,
-				block->offset, data->size);
+		if (ba->size > block->size) {
+
+			/* align ba to block boundary */
+			ba->offset = block->offset;
+
+			ret = block_alloc_contiguous(dsp, ba, block_list);
 			if (ret == 0)
 				return ret;
+
 		}
 	}
 
@@ -292,93 +665,74 @@ static int block_alloc(struct sst_module *module,
 	return -ENOMEM;
 }
 
-/* remove module from memory - callers hold locks */
-static void block_module_remove(struct sst_module *module)
+int sst_alloc_blocks(struct sst_dsp *dsp, struct sst_block_allocator *ba,
+	struct list_head *block_list)
 {
-	struct sst_mem_block *block, *tmp;
-	struct sst_dsp *dsp = module->dsp;
-	int err;
+	int ret;
 
-	/* disable each block  */
-	list_for_each_entry(block, &module->block_list, module_list) {
+	dev_dbg(dsp->dev, "block request 0x%x bytes at offset 0x%x type %d\n",
+		ba->size, ba->offset, ba->type);
 
-		if (block->ops && block->ops->disable) {
-			err = block->ops->disable(block);
-			if (err < 0)
-				dev_err(dsp->dev,
-					"error: cant disable block %d:%d\n",
-					block->type, block->index);
-		}
-	}
+	mutex_lock(&dsp->mutex);
 
-	/* mark each block as free */
-	list_for_each_entry_safe(block, tmp, &module->block_list, module_list) {
-		list_del(&block->module_list);
-		list_move(&block->list, &dsp->free_block_list);
+	ret = block_alloc(dsp, ba, block_list);
+	if (ret < 0) {
+		dev_err(dsp->dev, "error: can't alloc blocks %d\n", ret);
+		goto out;
 	}
-}
-
-/* prepare the memory block to receive data from host - callers hold locks */
-static int block_module_prepare(struct sst_module *module)
-{
-	struct sst_mem_block *block;
-	int ret = 0;
 
-	/* enable each block so that's it'e ready for module P/S data */
-	list_for_each_entry(block, &module->block_list, module_list) {
+	/* prepare DSP blocks for module usage */
+	ret = block_list_prepare(dsp, block_list);
+	if (ret < 0)
+		dev_err(dsp->dev, "error: prepare failed\n");
 
-		if (block->ops && block->ops->enable) {
-			ret = block->ops->enable(block);
-			if (ret < 0) {
-				dev_err(module->dsp->dev,
-					"error: cant disable block %d:%d\n",
-					block->type, block->index);
-				goto err;
-			}
-		}
-	}
+out:
+	mutex_unlock(&dsp->mutex);
 	return ret;
+}
+EXPORT_SYMBOL_GPL(sst_alloc_blocks);
 
-err:
-	list_for_each_entry(block, &module->block_list, module_list) {
-		if (block->ops && block->ops->disable)
-			block->ops->disable(block);
-	}
-	return ret;
+int sst_free_blocks(struct sst_dsp *dsp, struct list_head *block_list)
+{
+	mutex_lock(&dsp->mutex);
+	block_list_remove(dsp, block_list);
+	mutex_unlock(&dsp->mutex);
+	return 0;
 }
+EXPORT_SYMBOL_GPL(sst_free_blocks);
 
 /* allocate memory blocks for static module addresses - callers hold locks */
-static int block_alloc_fixed(struct sst_module *module,
-	struct sst_module_data *data)
+static int block_alloc_fixed(struct sst_dsp *dsp, struct sst_block_allocator *ba,
+	struct list_head *block_list)
 {
-	struct sst_dsp *dsp = module->dsp;
 	struct sst_mem_block *block, *tmp;
-	u32 end = data->offset + data->size, block_end;
+	u32 end = ba->offset + ba->size, block_end;
 	int err;
 
 	/* only IRAM/DRAM blocks are managed */
-	if (data->type != SST_MEM_IRAM && data->type != SST_MEM_DRAM)
+	if (ba->type != SST_MEM_IRAM && ba->type != SST_MEM_DRAM)
 		return 0;
 
 	/* are blocks already attached to this module */
-	list_for_each_entry_safe(block, tmp, &module->block_list, module_list) {
+	list_for_each_entry_safe(block, tmp, block_list, module_list) {
 
-		/* force compacting mem blocks of the same data_type */
-		if (block->data_type != data->data_type)
+		/* ignore blocks with wrong type */
+		if (block->type != ba->type)
 			continue;
 
 		block_end = block->offset + block->size;
 
 		/* find block that holds section */
-		if (data->offset >= block->offset && end < block_end)
+		if (ba->offset >= block->offset && end <= block_end)
 			return 0;
 
 		/* does block span more than 1 section */
-		if (data->offset >= block->offset && data->offset < block_end) {
+		if (ba->offset >= block->offset && ba->offset < block_end) {
 
-			err = block_alloc_contiguous(module, data,
-				block->offset + block->size,
-				data->size - block->size);
+			/* align ba to block boundary */
+			ba->size -= block_end - ba->offset;
+			ba->offset = block_end;
+			err = block_alloc_contiguous(dsp, ba, block_list);
 			if (err < 0)
 				return -ENOMEM;
 
@@ -391,82 +745,270 @@ static int block_alloc_fixed(struct sst_module *module,
 	list_for_each_entry_safe(block, tmp, &dsp->free_block_list, list) {
 		block_end = block->offset + block->size;
 
+		/* ignore blocks with wrong type */
+		if (block->type != ba->type)
+			continue;
+
 		/* find block that holds section */
-		if (data->offset >= block->offset && end < block_end) {
+		if (ba->offset >= block->offset && end <= block_end) {
 
 			/* add block */
-			block->data_type = data->data_type;
 			list_move(&block->list, &dsp->used_block_list);
-			list_add(&block->module_list, &module->block_list);
+			list_add(&block->module_list, block_list);
+			dev_dbg(dsp->dev, "block allocated %d:%d at offset 0x%x\n",
+				block->type, block->index, block->offset);
 			return 0;
 		}
 
 		/* does block span more than 1 section */
-		if (data->offset >= block->offset && data->offset < block_end) {
+		if (ba->offset >= block->offset && ba->offset < block_end) {
 
-			err = block_alloc_contiguous(module, data,
-				block->offset, data->size);
+			/* align ba to block boundary */
+			ba->offset = block->offset;
+
+			err = block_alloc_contiguous(dsp, ba, block_list);
 			if (err < 0)
 				return -ENOMEM;
 
 			return 0;
 		}
-
 	}
 
 	return -ENOMEM;
 }
 
 /* Load fixed module data into DSP memory blocks */
-int sst_module_insert_fixed_block(struct sst_module *module,
-	struct sst_module_data *data)
+int sst_module_alloc_blocks(struct sst_module *module)
 {
 	struct sst_dsp *dsp = module->dsp;
+	struct sst_fw *sst_fw = module->sst_fw;
+	struct sst_block_allocator ba;
 	int ret;
 
+	ba.size = module->size;
+	ba.type = module->type;
+	ba.offset = module->offset;
+
+	dev_dbg(dsp->dev, "block request 0x%x bytes at offset 0x%x type %d\n",
+		ba.size, ba.offset, ba.type);
+
 	mutex_lock(&dsp->mutex);
 
 	/* alloc blocks that includes this section */
-	ret = block_alloc_fixed(module, data);
+	ret = block_alloc_fixed(dsp, &ba, &module->block_list);
 	if (ret < 0) {
 		dev_err(dsp->dev,
 			"error: no free blocks for section at offset 0x%x size 0x%x\n",
-			data->offset, data->size);
+			module->offset, module->size);
 		mutex_unlock(&dsp->mutex);
 		return -ENOMEM;
 	}
 
 	/* prepare DSP blocks for module copy */
-	ret = block_module_prepare(module);
+	ret = block_list_prepare(dsp, &module->block_list);
 	if (ret < 0) {
 		dev_err(dsp->dev, "error: fw module prepare failed\n");
 		goto err;
 	}
 
 	/* copy partial module data to blocks */
-	sst_memcpy32(dsp->addr.lpe + data->offset, data->data, data->size);
+	if (dsp->fw_use_dma) {
+		ret = sst_dsp_dma_copyto(dsp,
+			dsp->addr.lpe_base + module->offset,
+			sst_fw->dmable_fw_paddr + module->data_offset,
+			module->size);
+		if (ret < 0) {
+			dev_err(dsp->dev, "error: module copy failed\n");
+			goto err;
+		}
+	} else
+		sst_memcpy32(dsp->addr.lpe + module->offset, module->data,
+			module->size);
 
 	mutex_unlock(&dsp->mutex);
 	return ret;
 
 err:
-	block_module_remove(module);
+	block_list_remove(dsp, &module->block_list);
 	mutex_unlock(&dsp->mutex);
 	return ret;
 }
-EXPORT_SYMBOL_GPL(sst_module_insert_fixed_block);
+EXPORT_SYMBOL_GPL(sst_module_alloc_blocks);
 
 /* Unload entire module from DSP memory */
-int sst_block_module_remove(struct sst_module *module)
+int sst_module_free_blocks(struct sst_module *module)
 {
 	struct sst_dsp *dsp = module->dsp;
 
 	mutex_lock(&dsp->mutex);
-	block_module_remove(module);
+	block_list_remove(dsp, &module->block_list);
 	mutex_unlock(&dsp->mutex);
 	return 0;
 }
-EXPORT_SYMBOL_GPL(sst_block_module_remove);
+EXPORT_SYMBOL_GPL(sst_module_free_blocks);
+
+int sst_module_runtime_alloc_blocks(struct sst_module_runtime *runtime,
+	int offset)
+{
+	struct sst_dsp *dsp = runtime->dsp;
+	struct sst_module *module = runtime->module;
+	struct sst_block_allocator ba;
+	int ret;
+
+	if (module->persistent_size == 0)
+		return 0;
+
+	ba.size = module->persistent_size;
+	ba.type = SST_MEM_DRAM;
+
+	mutex_lock(&dsp->mutex);
+
+	/* do we need to allocate at a fixed address ? */
+	if (offset != 0) {
+
+		ba.offset = offset;
+
+		dev_dbg(dsp->dev, "persistent fixed block request 0x%x bytes type %d offset 0x%x\n",
+			ba.size, ba.type, ba.offset);
+
+		/* alloc blocks that includes this section */
+		ret = block_alloc_fixed(dsp, &ba, &runtime->block_list);
+
+	} else {
+		dev_dbg(dsp->dev, "persistent block request 0x%x bytes type %d\n",
+			ba.size, ba.type);
+
+		/* alloc blocks that includes this section */
+		ret = block_alloc(dsp, &ba, &runtime->block_list);
+	}
+	if (ret < 0) {
+		dev_err(dsp->dev,
+		"error: no free blocks for runtime module size 0x%x\n",
+			module->persistent_size);
+		mutex_unlock(&dsp->mutex);
+		return -ENOMEM;
+	}
+	runtime->persistent_offset = ba.offset;
+
+	/* prepare DSP blocks for module copy */
+	ret = block_list_prepare(dsp, &runtime->block_list);
+	if (ret < 0) {
+		dev_err(dsp->dev, "error: runtime block prepare failed\n");
+		goto err;
+	}
+
+	mutex_unlock(&dsp->mutex);
+	return ret;
+
+err:
+	block_list_remove(dsp, &module->block_list);
+	mutex_unlock(&dsp->mutex);
+	return ret;
+}
+EXPORT_SYMBOL_GPL(sst_module_runtime_alloc_blocks);
+
+int sst_module_runtime_free_blocks(struct sst_module_runtime *runtime)
+{
+	struct sst_dsp *dsp = runtime->dsp;
+
+	mutex_lock(&dsp->mutex);
+	block_list_remove(dsp, &runtime->block_list);
+	mutex_unlock(&dsp->mutex);
+	return 0;
+}
+EXPORT_SYMBOL_GPL(sst_module_runtime_free_blocks);
+
+int sst_module_runtime_save(struct sst_module_runtime *runtime,
+	struct sst_module_runtime_context *context)
+{
+	struct sst_dsp *dsp = runtime->dsp;
+	struct sst_module *module = runtime->module;
+	int ret = 0;
+
+	dev_dbg(dsp->dev, "saving runtime %d memory at 0x%x size 0x%x\n",
+		runtime->id, runtime->persistent_offset,
+		module->persistent_size);
+
+	context->buffer = dma_alloc_coherent(dsp->dma_dev,
+		module->persistent_size,
+		&context->dma_buffer, GFP_DMA | GFP_KERNEL);
+	if (!context->buffer) {
+		dev_err(dsp->dev, "error: DMA context alloc failed\n");
+		return -ENOMEM;
+	}
+
+	mutex_lock(&dsp->mutex);
+
+	if (dsp->fw_use_dma) {
+
+		ret = sst_dsp_dma_get_channel(dsp, 0);
+		if (ret < 0)
+			goto err;
+
+		ret = sst_dsp_dma_copyfrom(dsp, context->dma_buffer,
+			dsp->addr.lpe_base + runtime->persistent_offset,
+			module->persistent_size);
+		sst_dsp_dma_put_channel(dsp);
+		if (ret < 0) {
+			dev_err(dsp->dev, "error: context copy failed\n");
+			goto err;
+		}
+	} else
+		sst_memcpy32(context->buffer, dsp->addr.lpe +
+			runtime->persistent_offset,
+			module->persistent_size);
+
+err:
+	mutex_unlock(&dsp->mutex);
+	return ret;
+}
+EXPORT_SYMBOL_GPL(sst_module_runtime_save);
+
+int sst_module_runtime_restore(struct sst_module_runtime *runtime,
+	struct sst_module_runtime_context *context)
+{
+	struct sst_dsp *dsp = runtime->dsp;
+	struct sst_module *module = runtime->module;
+	int ret = 0;
+
+	dev_dbg(dsp->dev, "restoring runtime %d memory at 0x%x size 0x%x\n",
+		runtime->id, runtime->persistent_offset,
+		module->persistent_size);
+
+	mutex_lock(&dsp->mutex);
+
+	if (!context->buffer) {
+		dev_info(dsp->dev, "no context buffer need to restore!\n");
+		goto err;
+	}
+
+	if (dsp->fw_use_dma) {
+
+		ret = sst_dsp_dma_get_channel(dsp, 0);
+		if (ret < 0)
+			goto err;
+
+		ret = sst_dsp_dma_copyto(dsp,
+			dsp->addr.lpe_base + runtime->persistent_offset,
+			context->dma_buffer, module->persistent_size);
+		sst_dsp_dma_put_channel(dsp);
+		if (ret < 0) {
+			dev_err(dsp->dev, "error: module copy failed\n");
+			goto err;
+		}
+	} else
+		sst_memcpy32(dsp->addr.lpe + runtime->persistent_offset,
+			context->buffer, module->persistent_size);
+
+	dma_free_coherent(dsp->dma_dev, module->persistent_size,
+				context->buffer, context->dma_buffer);
+	context->buffer = NULL;
+
+err:
+	mutex_unlock(&dsp->mutex);
+	return ret;
+}
+EXPORT_SYMBOL_GPL(sst_module_runtime_restore);
 
 /* register a DSP memory block for use with FW based modules */
 struct sst_mem_block *sst_mem_block_register(struct sst_dsp *dsp, u32 offset,
@@ -519,80 +1061,84 @@ void sst_mem_block_unregister_all(struct sst_dsp *dsp)
 EXPORT_SYMBOL_GPL(sst_mem_block_unregister_all);
 
 /* allocate scratch buffer blocks */
-struct sst_module *sst_mem_block_alloc_scratch(struct sst_dsp *dsp)
+int sst_block_alloc_scratch(struct sst_dsp *dsp)
 {
-	struct sst_module *sst_module, *scratch;
-	struct sst_mem_block *block, *tmp;
-	u32 block_size;
-	int ret = 0;
-
-	scratch = kzalloc(sizeof(struct sst_module), GFP_KERNEL);
-	if (scratch == NULL)
-		return NULL;
+	struct sst_module *module;
+	struct sst_block_allocator ba;
+	int ret;
 
 	mutex_lock(&dsp->mutex);
 
 	/* calculate required scratch size */
-	list_for_each_entry(sst_module, &dsp->module_list, list) {
-		if (scratch->s.size < sst_module->s.size)
-			scratch->s.size = sst_module->s.size;
+	dsp->scratch_size = 0;
+	list_for_each_entry(module, &dsp->module_list, list) {
+		dev_dbg(dsp->dev, "module %d scratch req 0x%x bytes\n",
+			module->id, module->scratch_size);
+		if (dsp->scratch_size < module->scratch_size)
+			dsp->scratch_size = module->scratch_size;
 	}
 
-	dev_dbg(dsp->dev, "scratch buffer required is %d bytes\n",
-		scratch->s.size);
-
-	/* init scratch module */
-	scratch->dsp = dsp;
-	scratch->s.type = SST_MEM_DRAM;
-	scratch->s.data_type = SST_DATA_S;
-	INIT_LIST_HEAD(&scratch->block_list);
+	dev_dbg(dsp->dev, "scratch buffer required is 0x%x bytes\n",
+		dsp->scratch_size);
 
-	/* check free blocks before looking at used blocks for space */
-	if (!list_empty(&dsp->free_block_list))
-		block = list_first_entry(&dsp->free_block_list,
-			struct sst_mem_block, list);
-	else
-		block = list_first_entry(&dsp->used_block_list,
-			struct sst_mem_block, list);
-	block_size = block->size;
+	if (dsp->scratch_size == 0) {
+		dev_info(dsp->dev, "no modules need scratch buffer\n");
+		mutex_unlock(&dsp->mutex);
+		return 0;
+	}
 
 	/* allocate blocks for module scratch buffers */
 	dev_dbg(dsp->dev, "allocating scratch blocks\n");
-	ret = block_alloc(scratch, &scratch->s);
+
+	ba.size = dsp->scratch_size;
+	ba.type = SST_MEM_DRAM;
+
+	/* do we need to allocate at fixed offset */
+	if (dsp->scratch_offset != 0) {
+
+		dev_dbg(dsp->dev, "block request 0x%x bytes type %d at 0x%x\n",
+			ba.size, ba.type, ba.offset);
+
+		ba.offset = dsp->scratch_offset;
+
+		/* alloc blocks that includes this section */
+		ret = block_alloc_fixed(dsp, &ba, &dsp->scratch_block_list);
+
+	} else {
+		dev_dbg(dsp->dev, "block request 0x%x bytes type %d\n",
+			ba.size, ba.type);
+
+		ba.offset = 0;
+		ret = block_alloc(dsp, &ba, &dsp->scratch_block_list);
+	}
 	if (ret < 0) {
 		dev_err(dsp->dev, "error: can't alloc scratch blocks\n");
-		goto err;
+		mutex_unlock(&dsp->mutex);
+		return ret;
 	}
 
-	/* assign the same offset of scratch to each module */
-	list_for_each_entry(sst_module, &dsp->module_list, list)
-		sst_module->s.offset = scratch->s.offset;
-
-	mutex_unlock(&dsp->mutex);
-	return scratch;
+	ret = block_list_prepare(dsp, &dsp->scratch_block_list);
+	if (ret < 0) {
+		dev_err(dsp->dev, "error: scratch block prepare failed\n");
+		mutex_unlock(&dsp->mutex);
+		return ret;
+	}
 
-err:
-	list_for_each_entry_safe(block, tmp, &scratch->block_list, module_list)
-		list_del(&block->module_list);
+	/* assign the same offset of scratch to each module */
+	dsp->scratch_offset = ba.offset;
 	mutex_unlock(&dsp->mutex);
-	return NULL;
+	return dsp->scratch_size;
 }
-EXPORT_SYMBOL_GPL(sst_mem_block_alloc_scratch);
+EXPORT_SYMBOL_GPL(sst_block_alloc_scratch);
 
 /* free all scratch blocks */
-void sst_mem_block_free_scratch(struct sst_dsp *dsp,
-	struct sst_module *scratch)
+void sst_block_free_scratch(struct sst_dsp *dsp)
 {
-	struct sst_mem_block *block, *tmp;
-
 	mutex_lock(&dsp->mutex);
-
-	list_for_each_entry_safe(block, tmp, &scratch->block_list, module_list)
-		list_del(&block->module_list);
-
+	block_list_remove(dsp, &dsp->scratch_block_list);
 	mutex_unlock(&dsp->mutex);
 }
-EXPORT_SYMBOL_GPL(sst_mem_block_free_scratch);
+EXPORT_SYMBOL_GPL(sst_block_free_scratch);
 
 /* get a module from it's unique ID */
 struct sst_module *sst_module_get_from_id(struct sst_dsp *dsp, u32 id)
@@ -612,3 +1158,40 @@ struct sst_module *sst_module_get_from_id(struct sst_dsp *dsp, u32 id)
 	return NULL;
 }
 EXPORT_SYMBOL_GPL(sst_module_get_from_id);
+
+struct sst_module_runtime *sst_module_runtime_get_from_id(
+	struct sst_module *module, u32 id)
+{
+	struct sst_module_runtime *runtime;
+	struct sst_dsp *dsp = module->dsp;
+
+	mutex_lock(&dsp->mutex);
+
+	list_for_each_entry(runtime, &module->runtime_list, list) {
+		if (runtime->id == id) {
+			mutex_unlock(&dsp->mutex);
+			return runtime;
+		}
+	}
+
+	mutex_unlock(&dsp->mutex);
+	return NULL;
+}
+EXPORT_SYMBOL_GPL(sst_module_runtime_get_from_id);
+
+/* returns block address in DSP address space */
+u32 sst_dsp_get_offset(struct sst_dsp *dsp, u32 offset,
+	enum sst_mem_type type)
+{
+	switch (type) {
+	case SST_MEM_IRAM:
+		return offset - dsp->addr.iram_offset +
+			dsp->addr.dsp_iram_offset;
+	case SST_MEM_DRAM:
+		return offset - dsp->addr.dram_offset +
+			dsp->addr.dsp_dram_offset;
+	default:
+		return 0;
+	}
+}
+EXPORT_SYMBOL_GPL(sst_dsp_get_offset);
diff --git a/sound/soc/intel/sst-haswell-dsp.c b/sound/soc/intel/sst-haswell-dsp.c
index 4b6c163c10ff..57039b00efc2 100644
--- a/sound/soc/intel/sst-haswell-dsp.c
+++ b/sound/soc/intel/sst-haswell-dsp.c
@@ -42,6 +42,10 @@
 #define SST_LP_SHIM_OFFSET	0xE7000
 #define SST_WPT_IRAM_OFFSET	0xA0000
 #define SST_LP_IRAM_OFFSET	0x80000
+#define SST_WPT_DSP_DRAM_OFFSET	0x400000
+#define SST_WPT_DSP_IRAM_OFFSET	0x00000
+#define SST_LPT_DSP_DRAM_OFFSET	0x400000
+#define SST_LPT_DSP_IRAM_OFFSET	0x00000
 
 #define SST_SHIM_PM_REG		0x84
 
@@ -86,9 +90,8 @@ static int hsw_parse_module(struct sst_dsp *dsp, struct sst_fw *fw,
 {
 	struct dma_block_info *block;
 	struct sst_module *mod;
-	struct sst_module_data block_data;
 	struct sst_module_template template;
-	int count;
+	int count, ret;
 	void __iomem *ram;
 
 	/* TODO: allowed module types need to be configurable */
@@ -109,13 +112,9 @@ static int hsw_parse_module(struct sst_dsp *dsp, struct sst_fw *fw,
 
 	memset(&template, 0, sizeof(template));
 	template.id = module->type;
-	template.entry = module->entry_point;
-	template.p.size = module->info.persistent_size;
-	template.p.type = SST_MEM_DRAM;
-	template.p.data_type = SST_DATA_P;
-	template.s.size = module->info.scratch_size;
-	template.s.type = SST_MEM_DRAM;
-	template.s.data_type = SST_DATA_S;
+	template.entry = module->entry_point - 4;
+	template.persistent_size = module->info.persistent_size;
+	template.scratch_size = module->info.scratch_size;
 
 	mod = sst_module_new(fw, &template, NULL);
 	if (mod == NULL)
@@ -135,14 +134,14 @@ static int hsw_parse_module(struct sst_dsp *dsp, struct sst_fw *fw,
 		switch (block->type) {
 		case SST_HSW_IRAM:
 			ram = dsp->addr.lpe;
-			block_data.offset =
+			mod->offset =
 				block->ram_offset + dsp->addr.iram_offset;
-			block_data.type = SST_MEM_IRAM;
+			mod->type = SST_MEM_IRAM;
 			break;
 		case SST_HSW_DRAM:
 			ram = dsp->addr.lpe;
-			block_data.offset = block->ram_offset;
-			block_data.type = SST_MEM_DRAM;
+			mod->offset = block->ram_offset;
+			mod->type = SST_MEM_DRAM;
 			break;
 		default:
 			dev_err(dsp->dev, "error: bad type 0x%x for block 0x%x\n",
@@ -151,30 +150,34 @@ static int hsw_parse_module(struct sst_dsp *dsp, struct sst_fw *fw,
 			return -EINVAL;
 		}
 
-		block_data.size = block->size;
-		block_data.data_type = SST_DATA_M;
-		block_data.data = (void *)block + sizeof(*block);
-		block_data.data_offset = block_data.data - fw->dma_buf;
+		mod->size = block->size;
+		mod->data = (void *)block + sizeof(*block);
+		mod->data_offset = mod->data - fw->dma_buf;
 
-		dev_dbg(dsp->dev, "copy firmware block %d type 0x%x "
+		dev_dbg(dsp->dev, "module block %d type 0x%x "
 			"size 0x%x ==> ram %p offset 0x%x\n",
-			count, block->type, block->size, ram,
+			count, mod->type, block->size, ram,
 			block->ram_offset);
 
-		sst_module_insert_fixed_block(mod, &block_data);
+		ret = sst_module_alloc_blocks(mod);
+		if (ret < 0) {
+			dev_err(dsp->dev, "error: could not allocate blocks for module %d\n",
+				count);
+			sst_module_free(mod);
+			return ret;
+		}
 
 		block = (void *)block + sizeof(*block) + block->size;
 	}
+
 	return 0;
 }
 
 static int hsw_parse_fw_image(struct sst_fw *sst_fw)
 {
 	struct fw_header *header;
-	struct sst_module *scratch;
 	struct fw_module_header *module;
 	struct sst_dsp *dsp = sst_fw->dsp;
-	struct sst_hsw *hsw = sst_fw->private;
 	int ret, count;
 
 	/* Read the header information from the data pointer */
@@ -204,12 +207,8 @@ static int hsw_parse_fw_image(struct sst_fw *sst_fw)
 		module = (void *)module + sizeof(*module) + module->mod_size;
 	}
 
-	/* allocate persistent/scratch mem regions */
-	scratch = sst_mem_block_alloc_scratch(dsp);
-	if (scratch == NULL)
-		return -ENOMEM;
-
-	sst_hsw_set_scratch_module(hsw, scratch);
+	/* allocate scratch mem regions */
+	sst_block_alloc_scratch(dsp);
 
 	return 0;
 }
@@ -248,8 +247,94 @@ static irqreturn_t hsw_irq(int irq, void *context)
 	return ret;
 }
 
-static void hsw_boot(struct sst_dsp *sst)
+static void hsw_set_dsp_D3(struct sst_dsp *sst)
+{
+	u32 val;
+	u32 reg;
+
+	/* Disable core clock gating (VDRTCTL2.DCLCGE = 0) */
+	reg = readl(sst->addr.pci_cfg + SST_VDRTCTL2);
+	reg &= ~(SST_VDRTCL2_DCLCGE | SST_VDRTCL2_DTCGE);
+	writel(reg, sst->addr.pci_cfg + SST_VDRTCTL2);
+
+	/* enable power gating and switch off DRAM & IRAM blocks */
+	val = readl(sst->addr.pci_cfg + SST_VDRTCTL0);
+	val |= SST_VDRTCL0_DSRAMPGE_MASK |
+		SST_VDRTCL0_ISRAMPGE_MASK;
+	val &= ~(SST_VDRTCL0_D3PGD | SST_VDRTCL0_D3SRAMPGD);
+	writel(val, sst->addr.pci_cfg + SST_VDRTCTL0);
+
+	/* switch off audio PLL */
+	val = readl(sst->addr.pci_cfg + SST_VDRTCTL2);
+	val |= SST_VDRTCL2_APLLSE_MASK;
+	writel(val, sst->addr.pci_cfg + SST_VDRTCTL2);
+
+	/* disable MCLK(clkctl.smos = 0) */
+	sst_dsp_shim_update_bits_unlocked(sst, SST_CLKCTL,
+		SST_CLKCTL_MASK, 0);
+
+	/* Set D3 state, delay 50 us */
+	val = readl(sst->addr.pci_cfg + SST_PMCS);
+	val |= SST_PMCS_PS_MASK;
+	writel(val, sst->addr.pci_cfg + SST_PMCS);
+	udelay(50);
+
+	/* Enable core clock gating (VDRTCTL2.DCLCGE = 1), delay 50 us */
+	reg = readl(sst->addr.pci_cfg + SST_VDRTCTL2);
+	reg |= SST_VDRTCL2_DCLCGE | SST_VDRTCL2_DTCGE;
+	writel(reg, sst->addr.pci_cfg + SST_VDRTCTL2);
+
+	udelay(50);
+
+}
+
+static void hsw_reset(struct sst_dsp *sst)
 {
+	/* put DSP into reset and stall */
+	sst_dsp_shim_update_bits_unlocked(sst, SST_CSR,
+		SST_CSR_RST | SST_CSR_STALL,
+		SST_CSR_RST | SST_CSR_STALL);
+
+	/* keep in reset for 10ms */
+	mdelay(10);
+
+	/* take DSP out of reset and keep stalled for FW loading */
+	sst_dsp_shim_update_bits_unlocked(sst, SST_CSR,
+		SST_CSR_RST | SST_CSR_STALL, SST_CSR_STALL);
+}
+
+static int hsw_set_dsp_D0(struct sst_dsp *sst)
+{
+	int tries = 10;
+	u32 reg;
+
+	/* Disable core clock gating (VDRTCTL2.DCLCGE = 0) */
+	reg = readl(sst->addr.pci_cfg + SST_VDRTCTL2);
+	reg &= ~(SST_VDRTCL2_DCLCGE | SST_VDRTCL2_DTCGE);
+	writel(reg, sst->addr.pci_cfg + SST_VDRTCTL2);
+
+	/* Disable D3PG (VDRTCTL0.D3PGD = 1) */
+	reg = readl(sst->addr.pci_cfg + SST_VDRTCTL0);
+	reg |= SST_VDRTCL0_D3PGD;
+	writel(reg, sst->addr.pci_cfg + SST_VDRTCTL0);
+
+	/* Set D0 state */
+	reg = readl(sst->addr.pci_cfg + SST_PMCS);
+	reg &= ~SST_PMCS_PS_MASK;
+	writel(reg, sst->addr.pci_cfg + SST_PMCS);
+
+	/* check that ADSP shim is enabled */
+	while (tries--) {
+		reg = readl(sst->addr.pci_cfg + SST_PMCS) & SST_PMCS_PS_MASK;
+		if (reg == 0)
+			goto finish;
+
+		msleep(1);
+	}
+
+	return -ENODEV;
+
+finish:
 	/* select SSP1 19.2MHz base clock, SSP clock 0, turn off Low Power Clock */
 	sst_dsp_shim_update_bits_unlocked(sst, SST_CSR,
 		SST_CSR_S1IOCS | SST_CSR_SBCS1 | SST_CSR_LPCS, 0x0);
@@ -264,34 +349,96 @@ static void hsw_boot(struct sst_dsp *sst)
 		SST_CLKCTL_MASK | SST_CLKCTL_DCPLCG | SST_CLKCTL_SCOE0,
 		SST_CLKCTL_MASK | SST_CLKCTL_DCPLCG | SST_CLKCTL_SCOE0);
 
+	/* Stall and reset core, set CSR */
+	hsw_reset(sst);
+
+	/* Enable core clock gating (VDRTCTL2.DCLCGE = 1), delay 50 us */
+	reg = readl(sst->addr.pci_cfg + SST_VDRTCTL2);
+	reg |= SST_VDRTCL2_DCLCGE | SST_VDRTCL2_DTCGE;
+	writel(reg, sst->addr.pci_cfg + SST_VDRTCTL2);
+
+	udelay(50);
+
+	/* switch on audio PLL */
+	reg = readl(sst->addr.pci_cfg + SST_VDRTCTL2);
+	reg &= ~SST_VDRTCL2_APLLSE_MASK;
+	writel(reg, sst->addr.pci_cfg + SST_VDRTCTL2);
+
+	/* set default power gating control, enable power gating control for all blocks. that is,
+	can't be accessed, please enable each block before accessing. */
+	reg = readl(sst->addr.pci_cfg + SST_VDRTCTL0);
+	reg |= SST_VDRTCL0_DSRAMPGE_MASK | SST_VDRTCL0_ISRAMPGE_MASK;
+	writel(reg, sst->addr.pci_cfg + SST_VDRTCTL0);
+
+
 	/* disable DMA finish function for SSP0 & SSP1 */
 	sst_dsp_shim_update_bits_unlocked(sst, SST_CSR2, SST_CSR2_SDFD_SSP1,
 		SST_CSR2_SDFD_SSP1);
 
-	/* enable DMA engine 0,1 all channels to access host memory */
-	sst_dsp_shim_update_bits_unlocked(sst, SST_HMDC,
-		SST_HMDC_HDDA1(0xff) | SST_HMDC_HDDA0(0xff),
-		SST_HMDC_HDDA1(0xff) | SST_HMDC_HDDA0(0xff));
+	/* set on-demond mode on engine 0,1 for all channels */
+	sst_dsp_shim_update_bits(sst, SST_HMDC,
+			SST_HMDC_HDDA_E0_ALLCH | SST_HMDC_HDDA_E1_ALLCH,
+			SST_HMDC_HDDA_E0_ALLCH | SST_HMDC_HDDA_E1_ALLCH);
+
+	/* Enable Interrupt from both sides */
+	sst_dsp_shim_update_bits(sst, SST_IMRX, (SST_IMRX_BUSY | SST_IMRX_DONE),
+				 0x0);
+	sst_dsp_shim_update_bits(sst, SST_IMRD, (SST_IMRD_DONE | SST_IMRD_BUSY |
+				SST_IMRD_SSP0 | SST_IMRD_DMAC), 0x0);
+
+	/* clear IPC registers */
+	sst_dsp_shim_write(sst, SST_IPCX, 0x0);
+	sst_dsp_shim_write(sst, SST_IPCD, 0x0);
+	sst_dsp_shim_write(sst, 0x80, 0x6);
+	sst_dsp_shim_write(sst, 0xe0, 0x300a);
+
+	return 0;
+}
 
-	/* disable all clock gating */
-	writel(0x0, sst->addr.pci_cfg + SST_VDRTCTL2);
+static void hsw_boot(struct sst_dsp *sst)
+{
+	/* set oportunistic mode on engine 0,1 for all channels */
+	sst_dsp_shim_update_bits(sst, SST_HMDC,
+			SST_HMDC_HDDA_E0_ALLCH | SST_HMDC_HDDA_E1_ALLCH, 0);
 
 	/* set DSP to RUN */
 	sst_dsp_shim_update_bits_unlocked(sst, SST_CSR, SST_CSR_STALL, 0x0);
 }
 
-static void hsw_reset(struct sst_dsp *sst)
+static void hsw_stall(struct sst_dsp *sst)
+{
+	/* stall DSP */
+	sst_dsp_shim_update_bits(sst, SST_CSR,
+		SST_CSR_24MHZ_LPCS | SST_CSR_STALL,
+		SST_CSR_STALL | SST_CSR_24MHZ_LPCS);
+}
+
+static void hsw_sleep(struct sst_dsp *sst)
 {
+	dev_dbg(sst->dev, "HSW_PM dsp runtime suspend\n");
+
 	/* put DSP into reset and stall */
-	sst_dsp_shim_update_bits_unlocked(sst, SST_CSR,
-		SST_CSR_RST | SST_CSR_STALL, SST_CSR_RST | SST_CSR_STALL);
+	sst_dsp_shim_update_bits(sst, SST_CSR,
+		SST_CSR_24MHZ_LPCS | SST_CSR_RST | SST_CSR_STALL,
+		SST_CSR_RST | SST_CSR_STALL | SST_CSR_24MHZ_LPCS);
 
-	/* keep in reset for 10ms */
-	mdelay(10);
+	hsw_set_dsp_D3(sst);
+	dev_dbg(sst->dev, "HSW_PM dsp runtime suspend exit\n");
+}
 
-	/* take DSP out of reset and keep stalled for FW loading */
-	sst_dsp_shim_update_bits_unlocked(sst, SST_CSR,
-		SST_CSR_RST | SST_CSR_STALL, SST_CSR_STALL);
+static int hsw_wake(struct sst_dsp *sst)
+{
+	int ret;
+
+	dev_dbg(sst->dev, "HSW_PM dsp runtime resume\n");
+
+	ret = hsw_set_dsp_D0(sst);
+	if (ret < 0)
+		return ret;
+
+	dev_dbg(sst->dev, "HSW_PM dsp runtime resume exit\n");
+
+	return 0;
 }
 
 struct sst_adsp_memregion {
@@ -396,6 +543,11 @@ static int hsw_block_enable(struct sst_mem_block *block)
 	dev_dbg(block->dsp->dev, " enabled block %d:%d at offset 0x%x\n",
 		block->type, block->index, block->offset);
 
+	/* Disable core clock gating (VDRTCTL2.DCLCGE = 0) */
+	val = readl(sst->addr.pci_cfg + SST_VDRTCTL2);
+	val &= ~SST_VDRTCL2_DCLCGE;
+	writel(val, sst->addr.pci_cfg + SST_VDRTCTL2);
+
 	val = readl(sst->addr.pci_cfg + SST_VDRTCTL0);
 	bit = hsw_block_get_bit(block);
 	writel(val & ~bit, sst->addr.pci_cfg + SST_VDRTCTL0);
@@ -403,6 +555,13 @@ static int hsw_block_enable(struct sst_mem_block *block)
 	/* wait 18 DSP clock ticks */
 	udelay(10);
 
+	/* Enable core clock gating (VDRTCTL2.DCLCGE = 1), delay 50 us */
+	val = readl(sst->addr.pci_cfg + SST_VDRTCTL2);
+	val |= SST_VDRTCL2_DCLCGE;
+	writel(val, sst->addr.pci_cfg + SST_VDRTCTL2);
+
+	udelay(50);
+
 	/*add a dummy read before the SRAM block is written, otherwise the writing may miss bytes sometimes.*/
 	sst_mem_block_dummy_read(block);
 	return 0;
@@ -420,10 +579,26 @@ static int hsw_block_disable(struct sst_mem_block *block)
 	dev_dbg(block->dsp->dev, " disabled block %d:%d at offset 0x%x\n",
 		block->type, block->index, block->offset);
 
+	/* Disable core clock gating (VDRTCTL2.DCLCGE = 0) */
+	val = readl(sst->addr.pci_cfg + SST_VDRTCTL2);
+	val &= ~SST_VDRTCL2_DCLCGE;
+	writel(val, sst->addr.pci_cfg + SST_VDRTCTL2);
+
+
 	val = readl(sst->addr.pci_cfg + SST_VDRTCTL0);
 	bit = hsw_block_get_bit(block);
 	writel(val | bit, sst->addr.pci_cfg + SST_VDRTCTL0);
 
+	/* wait 18 DSP clock ticks */
+	udelay(10);
+
+	/* Enable core clock gating (VDRTCTL2.DCLCGE = 1), delay 50 us */
+	val = readl(sst->addr.pci_cfg + SST_VDRTCTL2);
+	val |= SST_VDRTCL2_DCLCGE;
+	writel(val, sst->addr.pci_cfg + SST_VDRTCTL2);
+
+	udelay(50);
+
 	return 0;
 }
 
@@ -432,27 +607,6 @@ static struct sst_block_ops sst_hsw_ops = {
 	.disable = hsw_block_disable,
 };
 
-static int hsw_enable_shim(struct sst_dsp *sst)
-{
-	int tries = 10;
-	u32 reg;
-
-	/* enable shim */
-	reg = readl(sst->addr.pci_cfg + SST_SHIM_PM_REG);
-	writel(reg & ~0x3, sst->addr.pci_cfg + SST_SHIM_PM_REG);
-
-	/* check that ADSP shim is enabled */
-	while (tries--) {
-		reg = sst_dsp_shim_read_unlocked(sst, SST_CSR);
-		if (reg != 0xffffffff)
-			return 0;
-
-		msleep(1);
-	}
-
-	return -ENODEV;
-}
-
 static int hsw_init(struct sst_dsp *sst, struct sst_pdata *pdata)
 {
 	const struct sst_adsp_memregion *region;
@@ -467,12 +621,16 @@ static int hsw_init(struct sst_dsp *sst, struct sst_pdata *pdata)
 		region = lp_region;
 		region_count = ARRAY_SIZE(lp_region);
 		sst->addr.iram_offset = SST_LP_IRAM_OFFSET;
+		sst->addr.dsp_iram_offset = SST_LPT_DSP_IRAM_OFFSET;
+		sst->addr.dsp_dram_offset = SST_LPT_DSP_DRAM_OFFSET;
 		sst->addr.shim_offset = SST_LP_SHIM_OFFSET;
 		break;
 	case SST_DEV_ID_WILDCAT_POINT:
 		region = wpt_region;
 		region_count = ARRAY_SIZE(wpt_region);
 		sst->addr.iram_offset = SST_WPT_IRAM_OFFSET;
+		sst->addr.dsp_iram_offset = SST_WPT_DSP_IRAM_OFFSET;
+		sst->addr.dsp_dram_offset = SST_WPT_DSP_DRAM_OFFSET;
 		sst->addr.shim_offset = SST_WPT_SHIM_OFFSET;
 		break;
 	default:
@@ -487,7 +645,7 @@ static int hsw_init(struct sst_dsp *sst, struct sst_pdata *pdata)
 	}
 
 	/* enable the DSP SHIM */
-	ret = hsw_enable_shim(sst);
+	ret = hsw_set_dsp_D0(sst);
 	if (ret < 0) {
 		dev_err(dev, "error: failed to set DSP D0 and reset SHIM\n");
 		return ret;
@@ -497,10 +655,6 @@ static int hsw_init(struct sst_dsp *sst, struct sst_pdata *pdata)
 	if (ret)
 		return ret;
 
-	/* Enable Interrupt from both sides */
-	sst_dsp_shim_update_bits_unlocked(sst, SST_IMRX, 0x3, 0x0);
-	sst_dsp_shim_update_bits_unlocked(sst, SST_IMRD,
-		(0x3 | 0x1 << 16 | 0x3 << 21), 0x0);
 
 	/* register DSP memory blocks - ideally we should get this from ACPI */
 	for (i = 0; i < region_count; i++) {
@@ -532,6 +686,9 @@ static void hsw_free(struct sst_dsp *sst)
 struct sst_ops haswell_ops = {
 	.reset = hsw_reset,
 	.boot = hsw_boot,
+	.stall = hsw_stall,
+	.wake = hsw_wake,
+	.sleep = hsw_sleep,
 	.write = sst_shim32_write,
 	.read = sst_shim32_read,
 	.write64 = sst_shim32_write64,
diff --git a/sound/soc/intel/sst-haswell-ipc.c b/sound/soc/intel/sst-haswell-ipc.c
index b6291516dbbf..3f8c48231364 100644
--- a/sound/soc/intel/sst-haswell-ipc.c
+++ b/sound/soc/intel/sst-haswell-ipc.c
@@ -30,6 +30,7 @@
 #include <linux/firmware.h>
 #include <linux/dma-mapping.h>
 #include <linux/debugfs.h>
+#include <linux/pm_runtime.h>
 
 #include "sst-haswell-ipc.h"
 #include "sst-dsp.h"
@@ -276,6 +277,7 @@ struct sst_hsw {
 	struct sst_hsw_ipc_fw_version version;
 	struct sst_module *scratch;
 	bool fw_done;
+	struct sst_fw *sst_fw;
 
 	/* stream */
 	struct list_head stream_list;
@@ -289,6 +291,8 @@ struct sst_hsw {
 
 	/* DX */
 	struct sst_hsw_ipc_dx_reply dx;
+	void *dx_context;
+	dma_addr_t dx_context_paddr;
 
 	/* boot */
 	wait_queue_head_t boot_wait;
@@ -1038,14 +1042,9 @@ int sst_hsw_stream_set_volume(struct sst_hsw *hsw,
 
 	trace_ipc_request("set stream volume", stream->reply.stream_hw_id);
 
-	if (channel > 1)
+	if (channel >= 2 && channel != SST_HSW_CHANNELS_ALL)
 		return -EINVAL;
 
-	if (stream->mute[channel]) {
-		stream->mute_volume[channel] = volume;
-		return 0;
-	}
-
 	header = IPC_GLB_TYPE(IPC_GLB_STREAM_MESSAGE) |
 		IPC_STR_TYPE(IPC_STR_STAGE_MESSAGE);
 	header |= (stream->reply.stream_hw_id << IPC_STR_ID_SHIFT);
@@ -1053,9 +1052,28 @@ int sst_hsw_stream_set_volume(struct sst_hsw *hsw,
 	header |= (stage_id << IPC_STG_ID_SHIFT);
 
 	req = &stream->vol_req;
-	req->channel = channel;
 	req->target_volume = volume;
 
+	/* set both at same time ? */
+	if (channel == SST_HSW_CHANNELS_ALL) {
+		if (hsw->mute[0] && hsw->mute[1]) {
+			hsw->mute_volume[0] = hsw->mute_volume[1] = volume;
+			return 0;
+		} else if (hsw->mute[0])
+			req->channel = 1;
+		else if (hsw->mute[1])
+			req->channel = 0;
+		else
+			req->channel = SST_HSW_CHANNELS_ALL;
+	} else {
+		/* set only 1 channel */
+		if (hsw->mute[channel]) {
+			hsw->mute_volume[channel] = volume;
+			return 0;
+		}
+		req->channel = channel;
+	}
+
 	ret = ipc_tx_message_wait(hsw, header, req, sizeof(*req), NULL, 0);
 	if (ret < 0) {
 		dev_err(hsw->dev, "error: set stream volume failed\n");
@@ -1134,8 +1152,11 @@ int sst_hsw_mixer_set_volume(struct sst_hsw *hsw, u32 stage_id, u32 channel,
 
 	trace_ipc_request("set mixer volume", volume);
 
+	if (channel >= 2 && channel != SST_HSW_CHANNELS_ALL)
+		return -EINVAL;
+
 	/* set both at same time ? */
-	if (channel == 2) {
+	if (channel == SST_HSW_CHANNELS_ALL) {
 		if (hsw->mute[0] && hsw->mute[1]) {
 			hsw->mute_volume[0] = hsw->mute_volume[1] = volume;
 			return 0;
@@ -1144,7 +1165,7 @@ int sst_hsw_mixer_set_volume(struct sst_hsw *hsw, u32 stage_id, u32 channel,
 		else if (hsw->mute[1])
 			req.channel = 0;
 		else
-			req.channel = 0xffffffff;
+			req.channel = SST_HSW_CHANNELS_ALL;
 	} else {
 		/* set only 1 channel */
 		if (hsw->mute[channel]) {
@@ -1256,10 +1277,6 @@ int sst_hsw_stream_set_channels(struct sst_hsw *hsw,
 		return -EINVAL;
 	}
 
-	/* stereo is only supported atm */
-	if (channels != 2)
-		return -EINVAL;
-
 	stream->request.format.ch_num = channels;
 	return 0;
 }
@@ -1355,10 +1372,11 @@ int sst_hsw_stream_buffer(struct sst_hsw *hsw, struct sst_hsw_stream *stream,
 }
 
 int sst_hsw_stream_set_module_info(struct sst_hsw *hsw,
-	struct sst_hsw_stream *stream, enum sst_hsw_module_id module_id,
-	u32 entry_point)
+	struct sst_hsw_stream *stream, struct sst_module_runtime *runtime)
 {
 	struct sst_hsw_module_map *map = &stream->request.map;
+	struct sst_dsp *dsp = sst_hsw_get_dsp(hsw);
+	struct sst_module *module = runtime->module;
 
 	if (stream->commited) {
 		dev_err(hsw->dev, "error: stream committed for set module\n");
@@ -1367,36 +1385,25 @@ int sst_hsw_stream_set_module_info(struct sst_hsw *hsw,
 
 	/* only support initial module atm */
 	map->module_entries_count = 1;
-	map->module_entries[0].module_id = module_id;
-	map->module_entries[0].entry_point = entry_point;
-
-	return 0;
-}
-
-int sst_hsw_stream_set_pmemory_info(struct sst_hsw *hsw,
-	struct sst_hsw_stream *stream, u32 offset, u32 size)
-{
-	if (stream->commited) {
-		dev_err(hsw->dev, "error: stream committed for set pmem\n");
-		return -EINVAL;
-	}
-
-	stream->request.persistent_mem.offset = offset;
-	stream->request.persistent_mem.size = size;
-
-	return 0;
-}
-
-int sst_hsw_stream_set_smemory_info(struct sst_hsw *hsw,
-	struct sst_hsw_stream *stream, u32 offset, u32 size)
-{
-	if (stream->commited) {
-		dev_err(hsw->dev, "error: stream committed for set smem\n");
-		return -EINVAL;
-	}
-
-	stream->request.scratch_mem.offset = offset;
-	stream->request.scratch_mem.size = size;
+	map->module_entries[0].module_id = module->id;
+	map->module_entries[0].entry_point = module->entry;
+
+	stream->request.persistent_mem.offset =
+		sst_dsp_get_offset(dsp, runtime->persistent_offset, SST_MEM_DRAM);
+	stream->request.persistent_mem.size = module->persistent_size;
+
+	stream->request.scratch_mem.offset =
+		sst_dsp_get_offset(dsp, dsp->scratch_offset, SST_MEM_DRAM);
+	stream->request.scratch_mem.size = dsp->scratch_size;
+
+	dev_dbg(hsw->dev, "module %d runtime %d using:\n", module->id,
+		runtime->id);
+	dev_dbg(hsw->dev, " persistent offset 0x%x bytes 0x%x\n",
+		stream->request.persistent_mem.offset,
+		stream->request.persistent_mem.size);
+	dev_dbg(hsw->dev, " scratch offset 0x%x bytes 0x%x\n",
+		stream->request.scratch_mem.offset,
+		stream->request.scratch_mem.size);
 
 	return 0;
 }
@@ -1630,6 +1637,10 @@ int sst_hsw_device_set_config(struct sst_hsw *hsw,
 	config.clock_frequency = mclk;
 	config.mode = mode;
 	config.clock_divider = clock_divider;
+	if (mode == SST_HSW_DEVICE_TDM_CLOCK_MASTER)
+		config.channels = 4;
+	else
+		config.channels = 2;
 
 	trace_hsw_device_config_req(&config);
 
@@ -1673,34 +1684,283 @@ int sst_hsw_dx_set_state(struct sst_hsw *hsw,
 	dev_dbg(hsw->dev, "ipc: got %d entry numbers for state %d\n",
 		dx->entries_no, state);
 
-	memcpy(&hsw->dx, dx, sizeof(*dx));
-	return 0;
+	return ret;
 }
 
-/* Used to save state into hsw->dx_reply */
-int sst_hsw_dx_get_state(struct sst_hsw *hsw, u32 item,
-	u32 *offset, u32 *size, u32 *source)
+struct sst_module_runtime *sst_hsw_runtime_module_create(struct sst_hsw *hsw,
+	int mod_id, int offset)
 {
-	struct sst_hsw_ipc_dx_memory_item *dx_mem;
-	struct sst_hsw_ipc_dx_reply *dx_reply;
-	int entry_no;
+	struct sst_dsp *dsp = hsw->dsp;
+	struct sst_module *module;
+	struct sst_module_runtime *runtime;
+	int err;
 
-	dx_reply = &hsw->dx;
-	entry_no = dx_reply->entries_no;
+	module = sst_module_get_from_id(dsp, mod_id);
+	if (module == NULL) {
+		dev_err(dsp->dev, "error: failed to get module %d for pcm\n",
+			mod_id);
+		return NULL;
+	}
+
+	runtime = sst_module_runtime_new(module, mod_id, NULL);
+	if (runtime == NULL) {
+		dev_err(dsp->dev, "error: failed to create module %d runtime\n",
+			mod_id);
+		return NULL;
+	}
+
+	err = sst_module_runtime_alloc_blocks(runtime, offset);
+	if (err < 0) {
+		dev_err(dsp->dev, "error: failed to alloc blocks for module %d runtime\n",
+			mod_id);
+		sst_module_runtime_free(runtime);
+		return NULL;
+	}
+
+	dev_dbg(dsp->dev, "runtime id %d created for module %d\n", runtime->id,
+		mod_id);
+	return runtime;
+}
+
+void sst_hsw_runtime_module_free(struct sst_module_runtime *runtime)
+{
+	sst_module_runtime_free_blocks(runtime);
+	sst_module_runtime_free(runtime);
+}
+
+#ifdef CONFIG_PM
+static int sst_hsw_dx_state_dump(struct sst_hsw *hsw)
+{
+	struct sst_dsp *sst = hsw->dsp;
+	u32 item, offset, size;
+	int ret = 0;
 
-	trace_ipc_request("PM get Dx state", entry_no);
+	trace_ipc_request("PM state dump. Items #", SST_HSW_MAX_DX_REGIONS);
 
-	if (item >= entry_no)
+	if (hsw->dx.entries_no > SST_HSW_MAX_DX_REGIONS) {
+		dev_err(hsw->dev,
+			"error: number of FW context regions greater than %d\n",
+			SST_HSW_MAX_DX_REGIONS);
+		memset(&hsw->dx, 0, sizeof(hsw->dx));
 		return -EINVAL;
+	}
+
+	ret = sst_dsp_dma_get_channel(sst, 0);
+	if (ret < 0) {
+		dev_err(hsw->dev, "error: cant allocate dma channel %d\n", ret);
+		return ret;
+	}
+
+	/* set on-demond mode on engine 0 channel 3 */
+	sst_dsp_shim_update_bits(sst, SST_HMDC,
+			SST_HMDC_HDDA_E0_ALLCH | SST_HMDC_HDDA_E1_ALLCH,
+			SST_HMDC_HDDA_E0_ALLCH | SST_HMDC_HDDA_E1_ALLCH);
+
+	for (item = 0; item < hsw->dx.entries_no; item++) {
+		if (hsw->dx.mem_info[item].source == SST_HSW_DX_TYPE_MEMORY_DUMP
+			&& hsw->dx.mem_info[item].offset > DSP_DRAM_ADDR_OFFSET
+			&& hsw->dx.mem_info[item].offset <
+			DSP_DRAM_ADDR_OFFSET + SST_HSW_DX_CONTEXT_SIZE) {
+
+			offset = hsw->dx.mem_info[item].offset
+					- DSP_DRAM_ADDR_OFFSET;
+			size = (hsw->dx.mem_info[item].size + 3) & (~3);
+
+			ret = sst_dsp_dma_copyfrom(sst, hsw->dx_context_paddr + offset,
+				sst->addr.lpe_base + offset, size);
+			if (ret < 0) {
+				dev_err(hsw->dev,
+					"error: FW context dump failed\n");
+				memset(&hsw->dx, 0, sizeof(hsw->dx));
+				goto out;
+			}
+		}
+	}
+
+out:
+	sst_dsp_dma_put_channel(sst);
+	return ret;
+}
+
+static int sst_hsw_dx_state_restore(struct sst_hsw *hsw)
+{
+	struct sst_dsp *sst = hsw->dsp;
+	u32 item, offset, size;
+	int ret;
+
+	for (item = 0; item < hsw->dx.entries_no; item++) {
+		if (hsw->dx.mem_info[item].source == SST_HSW_DX_TYPE_MEMORY_DUMP
+			&& hsw->dx.mem_info[item].offset > DSP_DRAM_ADDR_OFFSET
+			&& hsw->dx.mem_info[item].offset <
+			DSP_DRAM_ADDR_OFFSET + SST_HSW_DX_CONTEXT_SIZE) {
+
+			offset = hsw->dx.mem_info[item].offset
+					- DSP_DRAM_ADDR_OFFSET;
+			size = (hsw->dx.mem_info[item].size + 3) & (~3);
+
+			ret = sst_dsp_dma_copyto(sst, sst->addr.lpe_base + offset,
+				hsw->dx_context_paddr + offset, size);
+			if (ret < 0) {
+				dev_err(hsw->dev,
+					"error: FW context restore failed\n");
+				return ret;
+			}
+		}
+	}
+
+	return 0;
+}
+
+static void sst_hsw_drop_all(struct sst_hsw *hsw)
+{
+	struct ipc_message *msg, *tmp;
+	unsigned long flags;
+	int tx_drop_cnt = 0, rx_drop_cnt = 0;
 
-	dx_mem = &dx_reply->mem_info[item];
-	*offset = dx_mem->offset;
-	*size = dx_mem->size;
-	*source = dx_mem->source;
+	/* drop all TX and Rx messages before we stall + reset DSP */
+	spin_lock_irqsave(&hsw->dsp->spinlock, flags);
+
+	list_for_each_entry_safe(msg, tmp, &hsw->tx_list, list) {
+		list_move(&msg->list, &hsw->empty_list);
+		tx_drop_cnt++;
+	}
+
+	list_for_each_entry_safe(msg, tmp, &hsw->rx_list, list) {
+		list_move(&msg->list, &hsw->empty_list);
+		rx_drop_cnt++;
+	}
+
+	spin_unlock_irqrestore(&hsw->dsp->spinlock, flags);
+
+	if (tx_drop_cnt || rx_drop_cnt)
+		dev_err(hsw->dev, "dropped IPC msg RX=%d, TX=%d\n",
+			tx_drop_cnt, rx_drop_cnt);
+}
+
+int sst_hsw_dsp_load(struct sst_hsw *hsw)
+{
+	struct sst_dsp *dsp = hsw->dsp;
+	int ret;
+
+	dev_dbg(hsw->dev, "loading audio DSP....");
+
+	ret = sst_dsp_wake(dsp);
+	if (ret < 0) {
+		dev_err(hsw->dev, "error: failed to wake audio DSP\n");
+		return -ENODEV;
+	}
+
+	ret = sst_dsp_dma_get_channel(dsp, 0);
+	if (ret < 0) {
+		dev_err(hsw->dev, "error: cant allocate dma channel %d\n", ret);
+		return ret;
+	}
+
+	ret = sst_fw_reload(hsw->sst_fw);
+	if (ret < 0) {
+		dev_err(hsw->dev, "error: SST FW reload failed\n");
+		sst_dsp_dma_put_channel(dsp);
+		return -ENOMEM;
+	}
 
+	sst_dsp_dma_put_channel(dsp);
 	return 0;
 }
 
+static int sst_hsw_dsp_restore(struct sst_hsw *hsw)
+{
+	struct sst_dsp *dsp = hsw->dsp;
+	int ret;
+
+	dev_dbg(hsw->dev, "restoring audio DSP....");
+
+	ret = sst_dsp_dma_get_channel(dsp, 0);
+	if (ret < 0) {
+		dev_err(hsw->dev, "error: cant allocate dma channel %d\n", ret);
+		return ret;
+	}
+
+	ret = sst_hsw_dx_state_restore(hsw);
+	if (ret < 0) {
+		dev_err(hsw->dev, "error: SST FW context restore failed\n");
+		sst_dsp_dma_put_channel(dsp);
+		return -ENOMEM;
+	}
+	sst_dsp_dma_put_channel(dsp);
+
+	/* wait for DSP boot completion */
+	sst_dsp_boot(dsp);
+
+	return ret;
+}
+
+int sst_hsw_dsp_runtime_suspend(struct sst_hsw *hsw)
+{
+	int ret;
+
+	dev_dbg(hsw->dev, "audio dsp runtime suspend\n");
+
+	ret = sst_hsw_dx_set_state(hsw, SST_HSW_DX_STATE_D3, &hsw->dx);
+	if (ret < 0)
+		return ret;
+
+	sst_dsp_stall(hsw->dsp);
+
+	ret = sst_hsw_dx_state_dump(hsw);
+	if (ret < 0)
+		return ret;
+
+	sst_hsw_drop_all(hsw);
+
+	return 0;
+}
+
+int sst_hsw_dsp_runtime_sleep(struct sst_hsw *hsw)
+{
+	sst_fw_unload(hsw->sst_fw);
+	sst_block_free_scratch(hsw->dsp);
+
+	hsw->boot_complete = false;
+
+	sst_dsp_sleep(hsw->dsp);
+
+	return 0;
+}
+
+int sst_hsw_dsp_runtime_resume(struct sst_hsw *hsw)
+{
+	struct device *dev = hsw->dev;
+	int ret;
+
+	dev_dbg(dev, "audio dsp runtime resume\n");
+
+	if (hsw->boot_complete)
+		return 1; /* tell caller no action is required */
+
+	ret = sst_hsw_dsp_restore(hsw);
+	if (ret < 0)
+		dev_err(dev, "error: audio DSP boot failure\n");
+
+	ret = wait_event_timeout(hsw->boot_wait, hsw->boot_complete,
+		msecs_to_jiffies(IPC_BOOT_MSECS));
+	if (ret == 0) {
+		dev_err(hsw->dev, "error: audio DSP boot timeout IPCD 0x%x IPCX 0x%x\n",
+			sst_dsp_shim_read_unlocked(hsw->dsp, SST_IPCD),
+			sst_dsp_shim_read_unlocked(hsw->dsp, SST_IPCX));
+		return -EIO;
+	}
+
+	/* Set ADSP SSP port settings */
+	ret = sst_hsw_device_set_config(hsw, SST_HSW_DEVICE_SSP_0,
+					SST_HSW_DEVICE_MCLK_FREQ_24_MHZ,
+					SST_HSW_DEVICE_CLOCK_MASTER, 9);
+	if (ret < 0)
+		dev_err(dev, "error: SSP re-initialization failed\n");
+
+	return ret;
+}
+#endif
+
 static int msg_empty_list_init(struct sst_hsw *hsw)
 {
 	int i;
@@ -1718,12 +1978,6 @@ static int msg_empty_list_init(struct sst_hsw *hsw)
 	return 0;
 }
 
-void sst_hsw_set_scratch_module(struct sst_hsw *hsw,
-	struct sst_module *scratch)
-{
-	hsw->scratch = scratch;
-}
-
 struct sst_dsp *sst_hsw_get_dsp(struct sst_hsw *hsw)
 {
 	return hsw->dsp;
@@ -1738,7 +1992,6 @@ int sst_hsw_dsp_init(struct device *dev, struct sst_pdata *pdata)
 {
 	struct sst_hsw_ipc_fw_version version;
 	struct sst_hsw *hsw;
-	struct sst_fw *hsw_sst_fw;
 	int ret;
 
 	dev_dbg(dev, "initialising Audio DSP IPC\n");
@@ -1780,12 +2033,19 @@ int sst_hsw_dsp_init(struct device *dev, struct sst_pdata *pdata)
 		goto dsp_err;
 	}
 
+	/* allocate DMA buffer for context storage */
+	hsw->dx_context = dma_alloc_coherent(hsw->dsp->dma_dev,
+		SST_HSW_DX_CONTEXT_SIZE, &hsw->dx_context_paddr, GFP_KERNEL);
+	if (hsw->dx_context == NULL) {
+		ret = -ENOMEM;
+		goto dma_err;
+	}
+
 	/* keep the DSP in reset state for base FW loading */
 	sst_dsp_reset(hsw->dsp);
 
-	hsw_sst_fw = sst_fw_new(hsw->dsp, pdata->fw, hsw);
-
-	if (hsw_sst_fw == NULL) {
+	hsw->sst_fw = sst_fw_new(hsw->dsp, pdata->fw, hsw);
+	if (hsw->sst_fw == NULL) {
 		ret = -ENODEV;
 		dev_err(dev, "error: failed to load firmware\n");
 		goto fw_err;
@@ -1797,7 +2057,9 @@ int sst_hsw_dsp_init(struct device *dev, struct sst_pdata *pdata)
 		msecs_to_jiffies(IPC_BOOT_MSECS));
 	if (ret == 0) {
 		ret = -EIO;
-		dev_err(hsw->dev, "error: ADSP boot timeout\n");
+		dev_err(hsw->dev, "error: audio DSP boot timeout IPCD 0x%x IPCX 0x%x\n",
+			sst_dsp_shim_read_unlocked(hsw->dsp, SST_IPCD),
+			sst_dsp_shim_read_unlocked(hsw->dsp, SST_IPCX));
 		goto boot_err;
 	}
 
@@ -1816,8 +2078,11 @@ int sst_hsw_dsp_init(struct device *dev, struct sst_pdata *pdata)
 
 boot_err:
 	sst_dsp_reset(hsw->dsp);
-	sst_fw_free(hsw_sst_fw);
+	sst_fw_free(hsw->sst_fw);
 fw_err:
+	dma_free_coherent(hsw->dsp->dma_dev, SST_HSW_DX_CONTEXT_SIZE,
+			hsw->dx_context, hsw->dx_context_paddr);
+dma_err:
 	sst_dsp_free(hsw->dsp);
 dsp_err:
 	kthread_stop(hsw->tx_thread);
@@ -1834,6 +2099,8 @@ void sst_hsw_dsp_free(struct device *dev, struct sst_pdata *pdata)
 
 	sst_dsp_reset(hsw->dsp);
 	sst_fw_free_all(hsw->dsp);
+	dma_free_coherent(hsw->dsp->dma_dev, SST_HSW_DX_CONTEXT_SIZE,
+			hsw->dx_context, hsw->dx_context_paddr);
 	sst_dsp_free(hsw->dsp);
 	kfree(hsw->scratch);
 	kthread_stop(hsw->tx_thread);
diff --git a/sound/soc/intel/sst-haswell-ipc.h b/sound/soc/intel/sst-haswell-ipc.h
index 2ac194a6d04b..138e894ab413 100644
--- a/sound/soc/intel/sst-haswell-ipc.h
+++ b/sound/soc/intel/sst-haswell-ipc.h
@@ -21,8 +21,10 @@
 #include <linux/kernel.h>
 #include <linux/platform_device.h>
 
-#define SST_HSW_NO_CHANNELS		2
+#define SST_HSW_NO_CHANNELS		4
 #define SST_HSW_MAX_DX_REGIONS		14
+#define SST_HSW_DX_CONTEXT_SIZE        (640 * 1024)
+#define SST_HSW_CHANNELS_ALL		0xffffffff
 
 #define SST_HSW_FW_LOG_CONFIG_DWORDS	12
 #define SST_HSW_GLOBAL_LOG		15
@@ -40,6 +42,7 @@ struct sst_hsw_stream;
 struct sst_hsw_log_stream;
 struct sst_pdata;
 struct sst_module;
+struct sst_module_runtime;
 extern struct sst_ops haswell_ops;
 
 /* Stream Allocate Path ID */
@@ -84,6 +87,7 @@ enum sst_hsw_device_mclk {
 enum sst_hsw_device_mode {
 	SST_HSW_DEVICE_CLOCK_SLAVE   = 0,
 	SST_HSW_DEVICE_CLOCK_MASTER  = 1,
+	SST_HSW_DEVICE_TDM_CLOCK_MASTER = 2,
 };
 
 /* DX Power State */
@@ -295,7 +299,8 @@ struct sst_hsw_ipc_device_config_req {
 	u32 clock_frequency;
 	u32 mode;
 	u16 clock_divider;
-	u16 reserved;
+	u8 channels;
+	u8 reserved;
 } __attribute__((packed));
 
 /* Audio Data formats */
@@ -430,8 +435,7 @@ int sst_hsw_stream_set_map_config(struct sst_hsw *hsw,
 int sst_hsw_stream_set_style(struct sst_hsw *hsw, struct sst_hsw_stream *stream,
 	enum sst_hsw_interleaving style);
 int sst_hsw_stream_set_module_info(struct sst_hsw *hsw,
-	struct sst_hsw_stream *stream, enum sst_hsw_module_id module_id,
-	u32 entry_point);
+	struct sst_hsw_stream *stream, struct sst_module_runtime *runtime);
 int sst_hsw_stream_set_pmemory_info(struct sst_hsw *hsw,
 	struct sst_hsw_stream *stream, u32 offset, u32 size);
 int sst_hsw_stream_set_smemory_info(struct sst_hsw *hsw,
@@ -484,7 +488,16 @@ int sst_hsw_dx_get_state(struct sst_hsw *hsw, u32 item,
 int sst_hsw_dsp_init(struct device *dev, struct sst_pdata *pdata);
 void sst_hsw_dsp_free(struct device *dev, struct sst_pdata *pdata);
 struct sst_dsp *sst_hsw_get_dsp(struct sst_hsw *hsw);
-void sst_hsw_set_scratch_module(struct sst_hsw *hsw,
-	struct sst_module *scratch);
+
+/* runtime module management */
+struct sst_module_runtime *sst_hsw_runtime_module_create(struct sst_hsw *hsw,
+	int mod_id, int offset);
+void sst_hsw_runtime_module_free(struct sst_module_runtime *runtime);
+
+/* PM */
+int sst_hsw_dsp_runtime_resume(struct sst_hsw *hsw);
+int sst_hsw_dsp_runtime_suspend(struct sst_hsw *hsw);
+int sst_hsw_dsp_load(struct sst_hsw *hsw);
+int sst_hsw_dsp_runtime_sleep(struct sst_hsw *hsw);
 
 #endif
diff --git a/sound/soc/intel/sst-haswell-pcm.c b/sound/soc/intel/sst-haswell-pcm.c
index 4df867cbb92a..0180b386c421 100644
--- a/sound/soc/intel/sst-haswell-pcm.c
+++ b/sound/soc/intel/sst-haswell-pcm.c
@@ -18,6 +18,7 @@
 #include <linux/dma-mapping.h>
 #include <linux/slab.h>
 #include <linux/delay.h>
+#include <linux/pm_runtime.h>
 #include <asm/page.h>
 #include <asm/pgtable.h>
 #include <sound/core.h>
@@ -73,6 +74,13 @@ static const u32 volume_map[] = {
 #define HSW_PCM_PERIODS_MAX	64
 #define HSW_PCM_PERIODS_MIN	2
 
+#define HSW_PCM_DAI_ID_SYSTEM	0
+#define HSW_PCM_DAI_ID_OFFLOAD0	1
+#define HSW_PCM_DAI_ID_OFFLOAD1	2
+#define HSW_PCM_DAI_ID_LOOPBACK	3
+#define HSW_PCM_DAI_ID_CAPTURE	4
+
+
 static const struct snd_pcm_hardware hsw_pcm_hardware = {
 	.info			= SNDRV_PCM_INFO_MMAP |
 				  SNDRV_PCM_INFO_MMAP_VALID |
@@ -89,22 +97,39 @@ static const struct snd_pcm_hardware hsw_pcm_hardware = {
 	.buffer_bytes_max	= HSW_PCM_PERIODS_MAX * PAGE_SIZE,
 };
 
+struct hsw_pcm_module_map {
+	int dai_id;
+	enum sst_hsw_module_id mod_id;
+};
+
 /* private data for each PCM DSP stream */
 struct hsw_pcm_data {
 	int dai_id;
 	struct sst_hsw_stream *stream;
+	struct sst_module_runtime *runtime;
+	struct sst_module_runtime_context context;
+	struct snd_pcm *hsw_pcm;
 	u32 volume[2];
 	struct snd_pcm_substream *substream;
 	struct snd_compr_stream *cstream;
 	unsigned int wpos;
 	struct mutex mutex;
 	bool allocated;
+	int persistent_offset;
+};
+
+enum hsw_pm_state {
+	HSW_PM_STATE_D3 = 0,
+	HSW_PM_STATE_D0 = 1,
 };
 
 /* private data for the driver */
 struct hsw_priv_data {
 	/* runtime DSP */
 	struct sst_hsw *hsw;
+	struct device *dev;
+	enum hsw_pm_state pm_state;
+	struct snd_soc_card *soc_card;
 
 	/* page tables */
 	struct snd_dma_buffer dmab[HSW_PCM_COUNT][2];
@@ -138,21 +163,25 @@ static inline unsigned int hsw_ipc_to_mixer(u32 value)
 static int hsw_stream_volume_put(struct snd_kcontrol *kcontrol,
 				struct snd_ctl_elem_value *ucontrol)
 {
-	struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol);
-	struct hsw_priv_data *pdata = snd_soc_component_get_drvdata(cmpnt);
+	struct snd_soc_platform *platform = snd_soc_kcontrol_platform(kcontrol);
 	struct soc_mixer_control *mc =
 		(struct soc_mixer_control *)kcontrol->private_value;
+	struct hsw_priv_data *pdata =
+		snd_soc_platform_get_drvdata(platform);
 	struct hsw_pcm_data *pcm_data = &pdata->pcm[mc->reg];
 	struct sst_hsw *hsw = pdata->hsw;
 	u32 volume;
 
 	mutex_lock(&pcm_data->mutex);
+	pm_runtime_get_sync(pdata->dev);
 
 	if (!pcm_data->stream) {
 		pcm_data->volume[0] =
 			hsw_mixer_to_ipc(ucontrol->value.integer.value[0]);
 		pcm_data->volume[1] =
 			hsw_mixer_to_ipc(ucontrol->value.integer.value[1]);
+		pm_runtime_mark_last_busy(pdata->dev);
+		pm_runtime_put_autosuspend(pdata->dev);
 		mutex_unlock(&pcm_data->mutex);
 		return 0;
 	}
@@ -160,7 +189,8 @@ static int hsw_stream_volume_put(struct snd_kcontrol *kcontrol,
 	if (ucontrol->value.integer.value[0] ==
 		ucontrol->value.integer.value[1]) {
 		volume = hsw_mixer_to_ipc(ucontrol->value.integer.value[0]);
-		sst_hsw_stream_set_volume(hsw, pcm_data->stream, 0, 2, volume);
+		/* apply volume value to all channels */
+		sst_hsw_stream_set_volume(hsw, pcm_data->stream, 0, SST_HSW_CHANNELS_ALL, volume);
 	} else {
 		volume = hsw_mixer_to_ipc(ucontrol->value.integer.value[0]);
 		sst_hsw_stream_set_volume(hsw, pcm_data->stream, 0, 0, volume);
@@ -168,6 +198,8 @@ static int hsw_stream_volume_put(struct snd_kcontrol *kcontrol,
 		sst_hsw_stream_set_volume(hsw, pcm_data->stream, 0, 1, volume);
 	}
 
+	pm_runtime_mark_last_busy(pdata->dev);
+	pm_runtime_put_autosuspend(pdata->dev);
 	mutex_unlock(&pcm_data->mutex);
 	return 0;
 }
@@ -175,21 +207,25 @@ static int hsw_stream_volume_put(struct snd_kcontrol *kcontrol,
 static int hsw_stream_volume_get(struct snd_kcontrol *kcontrol,
 				struct snd_ctl_elem_value *ucontrol)
 {
-	struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol);
-	struct hsw_priv_data *pdata = snd_soc_component_get_drvdata(cmpnt);
+	struct snd_soc_platform *platform = snd_soc_kcontrol_platform(kcontrol);
 	struct soc_mixer_control *mc =
 		(struct soc_mixer_control *)kcontrol->private_value;
+	struct hsw_priv_data *pdata =
+		snd_soc_platform_get_drvdata(platform);
 	struct hsw_pcm_data *pcm_data = &pdata->pcm[mc->reg];
 	struct sst_hsw *hsw = pdata->hsw;
 	u32 volume;
 
 	mutex_lock(&pcm_data->mutex);
+	pm_runtime_get_sync(pdata->dev);
 
 	if (!pcm_data->stream) {
 		ucontrol->value.integer.value[0] =
 			hsw_ipc_to_mixer(pcm_data->volume[0]);
 		ucontrol->value.integer.value[1] =
 			hsw_ipc_to_mixer(pcm_data->volume[1]);
+		pm_runtime_mark_last_busy(pdata->dev);
+		pm_runtime_put_autosuspend(pdata->dev);
 		mutex_unlock(&pcm_data->mutex);
 		return 0;
 	}
@@ -198,6 +234,9 @@ static int hsw_stream_volume_get(struct snd_kcontrol *kcontrol,
 	ucontrol->value.integer.value[0] = hsw_ipc_to_mixer(volume);
 	sst_hsw_stream_get_volume(hsw, pcm_data->stream, 0, 1, &volume);
 	ucontrol->value.integer.value[1] = hsw_ipc_to_mixer(volume);
+
+	pm_runtime_mark_last_busy(pdata->dev);
+	pm_runtime_put_autosuspend(pdata->dev);
 	mutex_unlock(&pcm_data->mutex);
 
 	return 0;
@@ -206,16 +245,18 @@ static int hsw_stream_volume_get(struct snd_kcontrol *kcontrol,
 static int hsw_volume_put(struct snd_kcontrol *kcontrol,
 				struct snd_ctl_elem_value *ucontrol)
 {
-	struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol);
-	struct hsw_priv_data *pdata = snd_soc_component_get_drvdata(cmpnt);
+	struct snd_soc_platform *platform = snd_soc_kcontrol_platform(kcontrol);
+	struct hsw_priv_data *pdata = snd_soc_platform_get_drvdata(platform);
 	struct sst_hsw *hsw = pdata->hsw;
 	u32 volume;
 
+	pm_runtime_get_sync(pdata->dev);
+
 	if (ucontrol->value.integer.value[0] ==
 		ucontrol->value.integer.value[1]) {
 
 		volume = hsw_mixer_to_ipc(ucontrol->value.integer.value[0]);
-		sst_hsw_mixer_set_volume(hsw, 0, 2, volume);
+		sst_hsw_mixer_set_volume(hsw, 0, SST_HSW_CHANNELS_ALL, volume);
 
 	} else {
 		volume = hsw_mixer_to_ipc(ucontrol->value.integer.value[0]);
@@ -225,23 +266,28 @@ static int hsw_volume_put(struct snd_kcontrol *kcontrol,
 		sst_hsw_mixer_set_volume(hsw, 0, 1, volume);
 	}
 
+	pm_runtime_mark_last_busy(pdata->dev);
+	pm_runtime_put_autosuspend(pdata->dev);
 	return 0;
 }
 
 static int hsw_volume_get(struct snd_kcontrol *kcontrol,
 				struct snd_ctl_elem_value *ucontrol)
 {
-	struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol);
-	struct hsw_priv_data *pdata = snd_soc_component_get_drvdata(cmpnt);
+	struct snd_soc_platform *platform = snd_soc_kcontrol_platform(kcontrol);
+	struct hsw_priv_data *pdata = snd_soc_platform_get_drvdata(platform);
 	struct sst_hsw *hsw = pdata->hsw;
 	unsigned int volume = 0;
 
+	pm_runtime_get_sync(pdata->dev);
 	sst_hsw_mixer_get_volume(hsw, 0, 0, &volume);
 	ucontrol->value.integer.value[0] = hsw_ipc_to_mixer(volume);
 
 	sst_hsw_mixer_get_volume(hsw, 0, 1, &volume);
 	ucontrol->value.integer.value[1] = hsw_ipc_to_mixer(volume);
 
+	pm_runtime_mark_last_busy(pdata->dev);
+	pm_runtime_put_autosuspend(pdata->dev);
 	return 0;
 }
 
@@ -252,23 +298,19 @@ static const DECLARE_TLV_DB_SCALE(hsw_vol_tlv, -9000, 300, 1);
 static const struct snd_kcontrol_new hsw_volume_controls[] = {
 	/* Global DSP volume */
 	SOC_DOUBLE_EXT_TLV("Master Playback Volume", 0, 0, 8,
-		ARRAY_SIZE(volume_map) -1, 0,
+		ARRAY_SIZE(volume_map) - 1, 0,
 		hsw_volume_get, hsw_volume_put, hsw_vol_tlv),
 	/* Offload 0 volume */
 	SOC_DOUBLE_EXT_TLV("Media0 Playback Volume", 1, 0, 8,
-		ARRAY_SIZE(volume_map), 0,
+		ARRAY_SIZE(volume_map) - 1, 0,
 		hsw_stream_volume_get, hsw_stream_volume_put, hsw_vol_tlv),
 	/* Offload 1 volume */
 	SOC_DOUBLE_EXT_TLV("Media1 Playback Volume", 2, 0, 8,
-		ARRAY_SIZE(volume_map), 0,
-		hsw_stream_volume_get, hsw_stream_volume_put, hsw_vol_tlv),
-	/* Loopback volume */
-	SOC_DOUBLE_EXT_TLV("Loopback Capture Volume", 3, 0, 8,
-		ARRAY_SIZE(volume_map), 0,
+		ARRAY_SIZE(volume_map) - 1, 0,
 		hsw_stream_volume_get, hsw_stream_volume_put, hsw_vol_tlv),
 	/* Mic Capture volume */
-	SOC_DOUBLE_EXT_TLV("Mic Capture Volume", 4, 0, 8,
-		ARRAY_SIZE(volume_map), 0,
+	SOC_DOUBLE_EXT_TLV("Mic Capture Volume", 0, 0, 8,
+		ARRAY_SIZE(volume_map) - 1, 0,
 		hsw_stream_volume_get, hsw_stream_volume_put, hsw_vol_tlv),
 };
 
@@ -354,8 +396,14 @@ static int hsw_pcm_hw_params(struct snd_pcm_substream *substream,
 	/* DSP stream type depends on DAI ID */
 	switch (rtd->cpu_dai->id) {
 	case 0:
-		stream_type = SST_HSW_STREAM_TYPE_SYSTEM;
-		module_id = SST_HSW_MODULE_PCM_SYSTEM;
+		if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+			stream_type = SST_HSW_STREAM_TYPE_SYSTEM;
+			module_id = SST_HSW_MODULE_PCM_SYSTEM;
+		}
+		else {
+			stream_type = SST_HSW_STREAM_TYPE_CAPTURE;
+			module_id = SST_HSW_MODULE_PCM_CAPTURE;
+		}
 		break;
 	case 1:
 	case 2:
@@ -368,10 +416,6 @@ static int hsw_pcm_hw_params(struct snd_pcm_substream *substream,
 		path_id = SST_HSW_STREAM_PATH_SSP0_OUT;
 		module_id = SST_HSW_MODULE_PCM_REFERENCE;
 		break;
-	case 4:
-		stream_type = SST_HSW_STREAM_TYPE_CAPTURE;
-		module_id = SST_HSW_MODULE_PCM_CAPTURE;
-		break;
 	default:
 		dev_err(rtd->dev, "error: invalid DAI ID %d\n",
 			rtd->cpu_dai->id);
@@ -421,13 +465,7 @@ static int hsw_pcm_hw_params(struct snd_pcm_substream *substream,
 		return ret;
 	}
 
-	/* we only support stereo atm */
 	channels = params_channels(params);
-	if (channels != 2) {
-		dev_err(rtd->dev, "error: invalid channels %d\n", channels);
-		return -EINVAL;
-	}
-
 	map = create_channel_map(SST_HSW_CHANNEL_CONFIG_STEREO);
 	sst_hsw_stream_set_map_config(hsw, pcm_data->stream,
 			map, SST_HSW_CHANNEL_CONFIG_STEREO);
@@ -478,35 +516,23 @@ static int hsw_pcm_hw_params(struct snd_pcm_substream *substream,
 		return -EINVAL;
 	}
 
-	/* we use hardcoded memory offsets atm, will be updated for new FW */
-	if (stream_type == SST_HSW_STREAM_TYPE_CAPTURE) {
-		sst_hsw_stream_set_module_info(hsw, pcm_data->stream,
-			SST_HSW_MODULE_PCM_CAPTURE, module_data->entry);
-		sst_hsw_stream_set_pmemory_info(hsw, pcm_data->stream,
-			0x449400, 0x4000);
-		sst_hsw_stream_set_smemory_info(hsw, pcm_data->stream,
-			0x400000, 0);
-	} else { /* stream_type == SST_HSW_STREAM_TYPE_SYSTEM */
-		sst_hsw_stream_set_module_info(hsw, pcm_data->stream,
-			SST_HSW_MODULE_PCM_SYSTEM, module_data->entry);
-
-		sst_hsw_stream_set_pmemory_info(hsw, pcm_data->stream,
-			module_data->offset, module_data->size);
-		sst_hsw_stream_set_pmemory_info(hsw, pcm_data->stream,
-			0x44d400, 0x3800);
-
-		sst_hsw_stream_set_smemory_info(hsw, pcm_data->stream,
-			module_data->offset, module_data->size);
-		sst_hsw_stream_set_smemory_info(hsw, pcm_data->stream,
-			0x400000, 0);
-	}
+	sst_hsw_stream_set_module_info(hsw, pcm_data->stream,
+		pcm_data->runtime);
 
 	ret = sst_hsw_stream_commit(hsw, pcm_data->stream);
 	if (ret < 0) {
 		dev_err(rtd->dev, "error: failed to commit stream %d\n", ret);
 		return ret;
 	}
-	pcm_data->allocated = true;
+
+	if (!pcm_data->allocated) {
+		/* Set previous saved volume */
+		sst_hsw_stream_set_volume(hsw, pcm_data->stream, 0,
+				0, pcm_data->volume[0]);
+		sst_hsw_stream_set_volume(hsw, pcm_data->stream, 0,
+				1, pcm_data->volume[1]);
+		pcm_data->allocated = true;
+	}
 
 	ret = sst_hsw_stream_pause(hsw, pcm_data->stream, 1);
 	if (ret < 0)
@@ -558,7 +584,7 @@ static u32 hsw_notify_pointer(struct sst_hsw_stream *stream, void *data)
 	pos = frames_to_bytes(runtime,
 		(runtime->control->appl_ptr % runtime->buffer_size));
 
-	dev_dbg(rtd->dev, "PCM: App pointer %d bytes\n", pos);
+	dev_vdbg(rtd->dev, "PCM: App pointer %d bytes\n", pos);
 
 	/* let alsa know we have play a period */
 	snd_pcm_period_elapsed(substream);
@@ -580,7 +606,7 @@ static snd_pcm_uframes_t hsw_pcm_pointer(struct snd_pcm_substream *substream)
 	offset = bytes_to_frames(runtime, position);
 	ppos = sst_hsw_get_dsp_presentation_position(hsw, pcm_data->stream);
 
-	dev_dbg(rtd->dev, "PCM: DMA pointer %du bytes, pos %llu\n",
+	dev_vdbg(rtd->dev, "PCM: DMA pointer %du bytes, pos %llu\n",
 		position, ppos);
 	return offset;
 }
@@ -596,6 +622,7 @@ static int hsw_pcm_open(struct snd_pcm_substream *substream)
 	pcm_data = &pdata->pcm[rtd->cpu_dai->id];
 
 	mutex_lock(&pcm_data->mutex);
+	pm_runtime_get_sync(pdata->dev);
 
 	snd_soc_pcm_set_drvdata(rtd, pcm_data);
 	pcm_data->substream = substream;
@@ -606,16 +633,12 @@ static int hsw_pcm_open(struct snd_pcm_substream *substream)
 		hsw_notify_pointer, pcm_data);
 	if (pcm_data->stream == NULL) {
 		dev_err(rtd->dev, "error: failed to create stream\n");
+		pm_runtime_mark_last_busy(pdata->dev);
+		pm_runtime_put_autosuspend(pdata->dev);
 		mutex_unlock(&pcm_data->mutex);
 		return -EINVAL;
 	}
 
-	/* Set previous saved volume */
-	sst_hsw_stream_set_volume(hsw, pcm_data->stream, 0,
-			0, pcm_data->volume[0]);
-	sst_hsw_stream_set_volume(hsw, pcm_data->stream, 0,
-			1, pcm_data->volume[1]);
-
 	mutex_unlock(&pcm_data->mutex);
 	return 0;
 }
@@ -645,6 +668,8 @@ static int hsw_pcm_close(struct snd_pcm_substream *substream)
 	pcm_data->stream = NULL;
 
 out:
+	pm_runtime_mark_last_busy(pdata->dev);
+	pm_runtime_put_autosuspend(pdata->dev);
 	mutex_unlock(&pcm_data->mutex);
 	return ret;
 }
@@ -660,6 +685,56 @@ static struct snd_pcm_ops hsw_pcm_ops = {
 	.page		= snd_pcm_sgbuf_ops_page,
 };
 
+/* static mappings between PCMs and modules - may be dynamic in future */
+static struct hsw_pcm_module_map mod_map[] = {
+	{HSW_PCM_DAI_ID_SYSTEM, SST_HSW_MODULE_PCM_SYSTEM},
+	{HSW_PCM_DAI_ID_OFFLOAD0, SST_HSW_MODULE_PCM},
+	{HSW_PCM_DAI_ID_OFFLOAD1, SST_HSW_MODULE_PCM},
+	{HSW_PCM_DAI_ID_LOOPBACK, SST_HSW_MODULE_PCM_REFERENCE},
+	{HSW_PCM_DAI_ID_CAPTURE, SST_HSW_MODULE_PCM_CAPTURE},
+};
+
+static int hsw_pcm_create_modules(struct hsw_priv_data *pdata)
+{
+	struct sst_hsw *hsw = pdata->hsw;
+	struct hsw_pcm_data *pcm_data;
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(mod_map); i++) {
+		pcm_data = &pdata->pcm[i];
+
+		/* create new runtime module, use same offset if recreated */
+		pcm_data->runtime = sst_hsw_runtime_module_create(hsw,
+			mod_map[i].mod_id, pcm_data->persistent_offset);
+		if (pcm_data->runtime == NULL)
+			goto err;
+		pcm_data->persistent_offset =
+			pcm_data->runtime->persistent_offset;
+	}
+
+	return 0;
+
+err:
+	for (--i; i >= 0; i--) {
+		pcm_data = &pdata->pcm[i];
+		sst_hsw_runtime_module_free(pcm_data->runtime);
+	}
+
+	return -ENODEV;
+}
+
+static void hsw_pcm_free_modules(struct hsw_priv_data *pdata)
+{
+	struct hsw_pcm_data *pcm_data;
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(mod_map); i++) {
+		pcm_data = &pdata->pcm[i];
+
+		sst_hsw_runtime_module_free(pcm_data->runtime);
+	}
+}
+
 static void hsw_pcm_free(struct snd_pcm *pcm)
 {
 	snd_pcm_lib_preallocate_free_for_all(pcm);
@@ -670,6 +745,7 @@ static int hsw_pcm_new(struct snd_soc_pcm_runtime *rtd)
 	struct snd_pcm *pcm = rtd->pcm;
 	struct snd_soc_platform *platform = rtd->platform;
 	struct sst_pdata *pdata = dev_get_platdata(platform->dev);
+	struct hsw_priv_data *priv_data = dev_get_drvdata(platform->dev);
 	struct device *dev = pdata->dma_dev;
 	int ret = 0;
 
@@ -686,6 +762,7 @@ static int hsw_pcm_new(struct snd_soc_pcm_runtime *rtd)
 			return ret;
 		}
 	}
+	priv_data->pcm[rtd->cpu_dai->id].hsw_pcm = pcm;
 
 	return ret;
 }
@@ -696,6 +773,7 @@ static int hsw_pcm_new(struct snd_soc_pcm_runtime *rtd)
 static struct snd_soc_dai_driver hsw_dais[] = {
 	{
 		.name  = "System Pin",
+		.id = HSW_PCM_DAI_ID_SYSTEM,
 		.playback = {
 			.stream_name = "System Playback",
 			.channels_min = 2,
@@ -703,10 +781,18 @@ static struct snd_soc_dai_driver hsw_dais[] = {
 			.rates = SNDRV_PCM_RATE_48000,
 			.formats = SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S16_LE,
 		},
+		.capture = {
+			.stream_name = "Analog Capture",
+			.channels_min = 2,
+			.channels_max = 4,
+			.rates = SNDRV_PCM_RATE_48000,
+			.formats = SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S16_LE,
+		},
 	},
 	{
 		/* PCM */
 		.name  = "Offload0 Pin",
+		.id = HSW_PCM_DAI_ID_OFFLOAD0,
 		.playback = {
 			.stream_name = "Offload0 Playback",
 			.channels_min = 2,
@@ -718,6 +804,7 @@ static struct snd_soc_dai_driver hsw_dais[] = {
 	{
 		/* PCM */
 		.name  = "Offload1 Pin",
+		.id = HSW_PCM_DAI_ID_OFFLOAD1,
 		.playback = {
 			.stream_name = "Offload1 Playback",
 			.channels_min = 2,
@@ -728,6 +815,7 @@ static struct snd_soc_dai_driver hsw_dais[] = {
 	},
 	{
 		.name  = "Loopback Pin",
+		.id = HSW_PCM_DAI_ID_LOOPBACK,
 		.capture = {
 			.stream_name = "Loopback Capture",
 			.channels_min = 2,
@@ -736,16 +824,6 @@ static struct snd_soc_dai_driver hsw_dais[] = {
 			.formats = SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S16_LE,
 		},
 	},
-	{
-		.name  = "Capture Pin",
-		.capture = {
-			.stream_name = "Analog Capture",
-			.channels_min = 2,
-			.channels_max = 2,
-			.rates = SNDRV_PCM_RATE_48000,
-			.formats = SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S16_LE,
-		},
-	},
 };
 
 static const struct snd_soc_dapm_widget widgets[] = {
@@ -776,9 +854,20 @@ static int hsw_pcm_probe(struct snd_soc_platform *platform)
 {
 	struct hsw_priv_data *priv_data = snd_soc_platform_get_drvdata(platform);
 	struct sst_pdata *pdata = dev_get_platdata(platform->dev);
-	struct device *dma_dev = pdata->dma_dev;
+	struct device *dma_dev, *dev;
 	int i, ret = 0;
 
+	if (!pdata)
+		return -ENODEV;
+
+	dev = platform->dev;
+	dma_dev = pdata->dma_dev;
+
+	priv_data->hsw = pdata->dsp;
+	priv_data->dev = platform->dev;
+	priv_data->pm_state = HSW_PM_STATE_D0;
+	priv_data->soc_card = platform->component.card;
+
 	/* allocate DSP buffer page tables */
 	for (i = 0; i < ARRAY_SIZE(hsw_dais); i++) {
 
@@ -801,6 +890,16 @@ static int hsw_pcm_probe(struct snd_soc_platform *platform)
 		}
 	}
 
+	/* allocate runtime modules */
+	hsw_pcm_create_modules(priv_data);
+
+	/* enable runtime PM with auto suspend */
+	pm_runtime_set_autosuspend_delay(platform->dev,
+		SST_RUNTIME_SUSPEND_DELAY);
+	pm_runtime_use_autosuspend(platform->dev);
+	pm_runtime_enable(platform->dev);
+	pm_runtime_idle(platform->dev);
+
 	return 0;
 
 err:
@@ -819,6 +918,9 @@ static int hsw_pcm_remove(struct snd_soc_platform *platform)
 		snd_soc_platform_get_drvdata(platform);
 	int i;
 
+	pm_runtime_disable(platform->dev);
+	hsw_pcm_free_modules(priv_data);
+
 	for (i = 0; i < ARRAY_SIZE(hsw_dais); i++) {
 		if (hsw_dais[i].playback.channels_min)
 			snd_dma_free_pages(&priv_data->dmab[i][0]);
@@ -896,10 +998,181 @@ static int hsw_pcm_dev_remove(struct platform_device *pdev)
 	return 0;
 }
 
+#ifdef CONFIG_PM_RUNTIME
+
+static int hsw_pcm_runtime_idle(struct device *dev)
+{
+	return 0;
+}
+
+static int hsw_pcm_runtime_suspend(struct device *dev)
+{
+	struct hsw_priv_data *pdata = dev_get_drvdata(dev);
+	struct sst_hsw *hsw = pdata->hsw;
+
+	if (pdata->pm_state == HSW_PM_STATE_D3)
+		return 0;
+
+	sst_hsw_dsp_runtime_suspend(hsw);
+	sst_hsw_dsp_runtime_sleep(hsw);
+	pdata->pm_state = HSW_PM_STATE_D3;
+
+	return 0;
+}
+
+static int hsw_pcm_runtime_resume(struct device *dev)
+{
+	struct hsw_priv_data *pdata = dev_get_drvdata(dev);
+	struct sst_hsw *hsw = pdata->hsw;
+	int ret;
+
+	if (pdata->pm_state == HSW_PM_STATE_D0)
+		return 0;
+
+	ret = sst_hsw_dsp_load(hsw);
+	if (ret < 0) {
+		dev_err(dev, "failed to reload %d\n", ret);
+		return ret;
+	}
+
+	ret = hsw_pcm_create_modules(pdata);
+	if (ret < 0) {
+		dev_err(dev, "failed to create modules %d\n", ret);
+		return ret;
+	}
+
+	ret = sst_hsw_dsp_runtime_resume(hsw);
+	if (ret < 0)
+		return ret;
+	else if (ret == 1) /* no action required */
+		return 0;
+
+	pdata->pm_state = HSW_PM_STATE_D0;
+	return ret;
+}
+
+#else
+#define hsw_pcm_runtime_idle		NULL
+#define hsw_pcm_runtime_suspend		NULL
+#define hsw_pcm_runtime_resume		NULL
+#endif
+
+#if defined(CONFIG_PM_SLEEP) && defined(CONFIG_PM_RUNTIME)
+
+static void hsw_pcm_complete(struct device *dev)
+{
+	struct hsw_priv_data *pdata = dev_get_drvdata(dev);
+	struct sst_hsw *hsw = pdata->hsw;
+	struct hsw_pcm_data *pcm_data;
+	int i, err;
+
+	if (pdata->pm_state == HSW_PM_STATE_D0)
+		return;
+
+	err = sst_hsw_dsp_load(hsw);
+	if (err < 0) {
+		dev_err(dev, "failed to reload %d\n", err);
+		return;
+	}
+
+	err = hsw_pcm_create_modules(pdata);
+	if (err < 0) {
+		dev_err(dev, "failed to create modules %d\n", err);
+		return;
+	}
+
+	for (i = 0; i < HSW_PCM_DAI_ID_CAPTURE + 1; i++) {
+		pcm_data = &pdata->pcm[i];
+
+		if (!pcm_data->substream)
+			continue;
+
+		err = sst_module_runtime_restore(pcm_data->runtime,
+			&pcm_data->context);
+		if (err < 0)
+			dev_err(dev, "failed to restore context for PCM %d\n", i);
+	}
+
+	snd_soc_resume(pdata->soc_card->dev);
+
+	err = sst_hsw_dsp_runtime_resume(hsw);
+	if (err < 0)
+		return;
+	else if (err == 1) /* no action required */
+		return;
+
+	pdata->pm_state = HSW_PM_STATE_D0;
+	return;
+}
+
+static int hsw_pcm_prepare(struct device *dev)
+{
+	struct hsw_priv_data *pdata = dev_get_drvdata(dev);
+	struct sst_hsw *hsw = pdata->hsw;
+	struct hsw_pcm_data *pcm_data;
+	int i, err;
+
+	if (pdata->pm_state == HSW_PM_STATE_D3)
+		return 0;
+	/* suspend all active streams */
+	for (i = 0; i < HSW_PCM_DAI_ID_CAPTURE + 1; i++) {
+		pcm_data = &pdata->pcm[i];
+
+		if (!pcm_data->substream)
+			continue;
+		dev_dbg(dev, "suspending pcm %d\n", i);
+		snd_pcm_suspend_all(pcm_data->hsw_pcm);
+
+		/* We need to wait until the DSP FW stops the streams */
+		msleep(2);
+	}
+
+	snd_soc_suspend(pdata->soc_card->dev);
+	snd_soc_poweroff(pdata->soc_card->dev);
+
+	/* enter D3 state and stall */
+	sst_hsw_dsp_runtime_suspend(hsw);
+
+	/* preserve persistent memory */
+	for (i = 0; i < HSW_PCM_DAI_ID_CAPTURE + 1; i++) {
+		pcm_data = &pdata->pcm[i];
+
+		if (!pcm_data->substream)
+			continue;
+
+		dev_dbg(dev, "saving context pcm %d\n", i);
+		err = sst_module_runtime_save(pcm_data->runtime,
+			&pcm_data->context);
+		if (err < 0)
+			dev_err(dev, "failed to save context for PCM %d\n", i);
+	}
+
+	/* put the DSP to sleep */
+	sst_hsw_dsp_runtime_sleep(hsw);
+	pdata->pm_state = HSW_PM_STATE_D3;
+
+	return 0;
+}
+
+#else
+#define hsw_pcm_prepare		NULL
+#define hsw_pcm_complete	NULL
+#endif
+
+static const struct dev_pm_ops hsw_pcm_pm = {
+	.runtime_idle = hsw_pcm_runtime_idle,
+	.runtime_suspend = hsw_pcm_runtime_suspend,
+	.runtime_resume = hsw_pcm_runtime_resume,
+	.prepare = hsw_pcm_prepare,
+	.complete = hsw_pcm_complete,
+};
+
 static struct platform_driver hsw_pcm_driver = {
 	.driver = {
 		.name = "haswell-pcm-audio",
 		.owner = THIS_MODULE,
+		.pm = &hsw_pcm_pm,
+
 	},
 
 	.probe = hsw_pcm_dev_probe,
diff --git a/sound/soc/intel/sst-mfld-platform-compress.c b/sound/soc/intel/sst-mfld-platform-compress.c
index 59467775c9b8..395168986462 100644
--- a/sound/soc/intel/sst-mfld-platform-compress.c
+++ b/sound/soc/intel/sst-mfld-platform-compress.c
@@ -67,8 +67,11 @@ static int sst_platform_compr_open(struct snd_compr_stream *cstream)
 		goto out_ops;
 	}
 	stream->compr_ops = sst->compr_ops;
-
 	stream->id = 0;
+
+	/* Turn on LPE */
+	sst->compr_ops->power(sst->dev, true);
+
 	sst_set_stream_status(stream, SST_PLATFORM_INIT);
 	runtime->private_data = stream;
 	return 0;
@@ -83,6 +86,9 @@ static int sst_platform_compr_free(struct snd_compr_stream *cstream)
 	int ret_val = 0, str_id;
 
 	stream = cstream->runtime->private_data;
+	/* Turn off LPE */
+	sst->compr_ops->power(sst->dev, false);
+
 	/*need to check*/
 	str_id = stream->id;
 	if (str_id)
diff --git a/sound/soc/intel/sst-mfld-platform-pcm.c b/sound/soc/intel/sst-mfld-platform-pcm.c
index aa9b600dfc9b..6032f18693be 100644
--- a/sound/soc/intel/sst-mfld-platform-pcm.c
+++ b/sound/soc/intel/sst-mfld-platform-pcm.c
@@ -101,35 +101,11 @@ static struct sst_dev_stream_map dpcm_strm_map[] = {
 	{MERR_DPCM_AUDIO, 0, SNDRV_PCM_STREAM_CAPTURE, PIPE_PCM1_OUT, SST_TASK_ID_MEDIA, 0},
 };
 
-/* MFLD - MSIC */
-static struct snd_soc_dai_driver sst_platform_dai[] = {
+static int sst_media_digital_mute(struct snd_soc_dai *dai, int mute, int stream)
 {
-	.name = "Headset-cpu-dai",
-	.id = 0,
-	.playback = {
-		.channels_min = SST_STEREO,
-		.channels_max = SST_STEREO,
-		.rates = SNDRV_PCM_RATE_48000,
-		.formats = SNDRV_PCM_FMTBIT_S24_LE,
-	},
-	.capture = {
-		.channels_min = 1,
-		.channels_max = 5,
-		.rates = SNDRV_PCM_RATE_48000,
-		.formats = SNDRV_PCM_FMTBIT_S24_LE,
-	},
-},
-{
-	.name = "Compress-cpu-dai",
-	.compress_dai = 1,
-	.playback = {
-		.channels_min = SST_STEREO,
-		.channels_max = SST_STEREO,
-		.rates = SNDRV_PCM_RATE_44100|SNDRV_PCM_RATE_48000,
-		.formats = SNDRV_PCM_FMTBIT_S16_LE,
-	},
-},
-};
+
+	return sst_send_pipe_gains(dai, stream, mute);
+}
 
 /* helper functions */
 void sst_set_stream_status(struct sst_runtime_stream *stream,
@@ -451,12 +427,133 @@ static int sst_media_hw_free(struct snd_pcm_substream *substream,
 	return snd_pcm_lib_free_pages(substream);
 }
 
+static int sst_enable_ssp(struct snd_pcm_substream *substream,
+			struct snd_soc_dai *dai)
+{
+	int ret = 0;
+
+	if (!dai->active) {
+		ret = sst_handle_vb_timer(dai, true);
+		if (ret)
+			return ret;
+		ret = send_ssp_cmd(dai, dai->name, 1);
+	}
+	return ret;
+}
+
+static void sst_disable_ssp(struct snd_pcm_substream *substream,
+			struct snd_soc_dai *dai)
+{
+	if (!dai->active) {
+		send_ssp_cmd(dai, dai->name, 0);
+		sst_handle_vb_timer(dai, false);
+	}
+}
+
 static struct snd_soc_dai_ops sst_media_dai_ops = {
 	.startup = sst_media_open,
 	.shutdown = sst_media_close,
 	.prepare = sst_media_prepare,
 	.hw_params = sst_media_hw_params,
 	.hw_free = sst_media_hw_free,
+	.mute_stream = sst_media_digital_mute,
+};
+
+static struct snd_soc_dai_ops sst_compr_dai_ops = {
+	.mute_stream = sst_media_digital_mute,
+};
+
+static struct snd_soc_dai_ops sst_be_dai_ops = {
+	.startup = sst_enable_ssp,
+	.shutdown = sst_disable_ssp,
+};
+
+static struct snd_soc_dai_driver sst_platform_dai[] = {
+{
+	.name = "media-cpu-dai",
+	.ops = &sst_media_dai_ops,
+	.playback = {
+		.stream_name = "Headset Playback",
+		.channels_min = SST_STEREO,
+		.channels_max = SST_STEREO,
+		.rates = SNDRV_PCM_RATE_44100|SNDRV_PCM_RATE_48000,
+		.formats = SNDRV_PCM_FMTBIT_S16_LE,
+	},
+	.capture = {
+		.stream_name = "Headset Capture",
+		.channels_min = 1,
+		.channels_max = 2,
+		.rates = SNDRV_PCM_RATE_44100|SNDRV_PCM_RATE_48000,
+		.formats = SNDRV_PCM_FMTBIT_S16_LE,
+	},
+},
+{
+	.name = "compress-cpu-dai",
+	.compress_dai = 1,
+	.ops = &sst_compr_dai_ops,
+	.playback = {
+		.stream_name = "Compress Playback",
+		.channels_min = SST_STEREO,
+		.channels_max = SST_STEREO,
+		.rates = SNDRV_PCM_RATE_48000,
+		.formats = SNDRV_PCM_FMTBIT_S16_LE,
+	},
+},
+/* BE CPU  Dais */
+{
+	.name = "ssp0-port",
+	.ops = &sst_be_dai_ops,
+	.playback = {
+		.stream_name = "ssp0 Tx",
+		.channels_min = SST_STEREO,
+		.channels_max = SST_STEREO,
+		.rates = SNDRV_PCM_RATE_48000,
+		.formats = SNDRV_PCM_FMTBIT_S16_LE,
+	},
+	.capture = {
+		.stream_name = "ssp0 Rx",
+		.channels_min = SST_STEREO,
+		.channels_max = SST_STEREO,
+		.rates = SNDRV_PCM_RATE_48000,
+		.formats = SNDRV_PCM_FMTBIT_S16_LE,
+	},
+},
+{
+	.name = "ssp1-port",
+	.ops = &sst_be_dai_ops,
+	.playback = {
+		.stream_name = "ssp1 Tx",
+		.channels_min = SST_STEREO,
+		.channels_max = SST_STEREO,
+		.rates = SNDRV_PCM_RATE_8000|SNDRV_PCM_RATE_16000|SNDRV_PCM_RATE_48000,
+		.formats = SNDRV_PCM_FMTBIT_S16_LE,
+	},
+	.capture = {
+		.stream_name = "ssp1 Rx",
+		.channels_min = SST_STEREO,
+		.channels_max = SST_STEREO,
+		.rates = SNDRV_PCM_RATE_8000|SNDRV_PCM_RATE_16000|SNDRV_PCM_RATE_48000,
+		.formats = SNDRV_PCM_FMTBIT_S16_LE,
+	},
+},
+{
+	.name = "ssp2-port",
+	.ops = &sst_be_dai_ops,
+	.playback = {
+		.stream_name = "ssp2 Tx",
+		.channels_min = SST_STEREO,
+		.channels_max = SST_STEREO,
+		.rates = SNDRV_PCM_RATE_48000,
+		.formats = SNDRV_PCM_FMTBIT_S16_LE,
+	},
+	.capture = {
+		.stream_name = "ssp2 Rx",
+		.channels_min = SST_STEREO,
+		.channels_max = SST_STEREO,
+		.rates = SNDRV_PCM_RATE_48000,
+		.formats = SNDRV_PCM_FMTBIT_S16_LE,
+	},
+},
 };
 
 static int sst_platform_open(struct snd_pcm_substream *substream)
@@ -609,6 +706,7 @@ static int sst_platform_probe(struct platform_device *pdev)
 	pdata->pdev_strm_map = dpcm_strm_map;
 	pdata->strm_map_size = ARRAY_SIZE(dpcm_strm_map);
 	drv->pdata = pdata;
+	drv->pdev = pdev;
 	mutex_init(&drv->lock);
 	dev_set_drvdata(&pdev->dev, drv);
 
diff --git a/sound/soc/intel/sst-mfld-platform.h b/sound/soc/intel/sst-mfld-platform.h
index 19f83ec51613..79c8d1246a8f 100644
--- a/sound/soc/intel/sst-mfld-platform.h
+++ b/sound/soc/intel/sst-mfld-platform.h
@@ -117,6 +117,7 @@ struct compress_sst_ops {
 	int (*get_codec_caps)(struct snd_compr_codec_caps *codec);
 	int (*set_metadata)(struct device *dev,	unsigned int str_id,
 			struct snd_compr_metadata *mdata);
+	int (*power)(struct device *dev, bool state);
 };
 
 struct sst_ops {
@@ -153,6 +154,10 @@ struct sst_device {
 struct sst_data;
 
 int sst_dsp_init_v2_dpcm(struct snd_soc_platform *platform);
+int sst_send_pipe_gains(struct snd_soc_dai *dai, int stream, int mute);
+int send_ssp_cmd(struct snd_soc_dai *dai, const char *id, bool enable);
+int sst_handle_vb_timer(struct snd_soc_dai *dai, bool enable);
+
 void sst_set_stream_status(struct sst_runtime_stream *stream, int state);
 int sst_fill_stream_params(void *substream, const struct sst_data *ctx,
 			   struct snd_sst_params *str_params, bool is_compress);
diff --git a/sound/soc/intel/sst/Makefile b/sound/soc/intel/sst/Makefile
new file mode 100644
index 000000000000..fd21726361b5
--- /dev/null
+++ b/sound/soc/intel/sst/Makefile
@@ -0,0 +1,7 @@
+snd-intel-sst-core-objs := sst.o sst_ipc.o sst_stream.o sst_drv_interface.o sst_loader.o sst_pvt.o
+snd-intel-sst-pci-objs += sst_pci.o
+snd-intel-sst-acpi-objs += sst_acpi.o
+
+obj-$(CONFIG_SND_SST_IPC) += snd-intel-sst-core.o
+obj-$(CONFIG_SND_SST_IPC_PCI) += snd-intel-sst-pci.o
+obj-$(CONFIG_SND_SST_IPC_ACPI) += snd-intel-sst-acpi.o
diff --git a/sound/soc/intel/sst/sst.c b/sound/soc/intel/sst/sst.c
new file mode 100644
index 000000000000..8a8d56a146e7
--- /dev/null
+++ b/sound/soc/intel/sst/sst.c
@@ -0,0 +1,437 @@
+/*
+ *  sst.c - Intel SST Driver for audio engine
+ *
+ *  Copyright (C) 2008-14	Intel Corp
+ *  Authors:	Vinod Koul <vinod.koul@intel.com>
+ *		Harsha Priya <priya.harsha@intel.com>
+ *		Dharageswari R <dharageswari.r@intel.com>
+ *		KP Jeeja <jeeja.kp@intel.com>
+ *  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; version 2 of the License.
+ *
+ *  This program is distributed in the hope that it will be useful, but
+ *  WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  General Public License for more details.
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+#include <linux/module.h>
+#include <linux/fs.h>
+#include <linux/interrupt.h>
+#include <linux/firmware.h>
+#include <linux/pm_runtime.h>
+#include <linux/pm_qos.h>
+#include <linux/async.h>
+#include <linux/acpi.h>
+#include <sound/core.h>
+#include <sound/soc.h>
+#include <asm/platform_sst_audio.h>
+#include "../sst-mfld-platform.h"
+#include "sst.h"
+#include "../sst-dsp.h"
+
+MODULE_AUTHOR("Vinod Koul <vinod.koul@intel.com>");
+MODULE_AUTHOR("Harsha Priya <priya.harsha@intel.com>");
+MODULE_DESCRIPTION("Intel (R) SST(R) Audio Engine Driver");
+MODULE_LICENSE("GPL v2");
+
+static inline bool sst_is_process_reply(u32 msg_id)
+{
+	return ((msg_id & PROCESS_MSG) ? true : false);
+}
+
+static inline bool sst_validate_mailbox_size(unsigned int size)
+{
+	return ((size <= SST_MAILBOX_SIZE) ? true : false);
+}
+
+static irqreturn_t intel_sst_interrupt_mrfld(int irq, void *context)
+{
+	union interrupt_reg_mrfld isr;
+	union ipc_header_mrfld header;
+	union sst_imr_reg_mrfld imr;
+	struct ipc_post *msg = NULL;
+	unsigned int size = 0;
+	struct intel_sst_drv *drv = (struct intel_sst_drv *) context;
+	irqreturn_t retval = IRQ_HANDLED;
+
+	/* Interrupt arrived, check src */
+	isr.full = sst_shim_read64(drv->shim, SST_ISRX);
+
+	if (isr.part.done_interrupt) {
+		/* Clear done bit */
+		spin_lock(&drv->ipc_spin_lock);
+		header.full = sst_shim_read64(drv->shim,
+					drv->ipc_reg.ipcx);
+		header.p.header_high.part.done = 0;
+		sst_shim_write64(drv->shim, drv->ipc_reg.ipcx, header.full);
+
+		/* write 1 to clear status register */;
+		isr.part.done_interrupt = 1;
+		sst_shim_write64(drv->shim, SST_ISRX, isr.full);
+		spin_unlock(&drv->ipc_spin_lock);
+
+		/* we can send more messages to DSP so trigger work */
+		queue_work(drv->post_msg_wq, &drv->ipc_post_msg_wq);
+		retval = IRQ_HANDLED;
+	}
+
+	if (isr.part.busy_interrupt) {
+		/* message from dsp so copy that */
+		spin_lock(&drv->ipc_spin_lock);
+		imr.full = sst_shim_read64(drv->shim, SST_IMRX);
+		imr.part.busy_interrupt = 1;
+		sst_shim_write64(drv->shim, SST_IMRX, imr.full);
+		spin_unlock(&drv->ipc_spin_lock);
+		header.full =  sst_shim_read64(drv->shim, drv->ipc_reg.ipcd);
+
+		if (sst_create_ipc_msg(&msg, header.p.header_high.part.large)) {
+			drv->ops->clear_interrupt(drv);
+			return IRQ_HANDLED;
+		}
+
+		if (header.p.header_high.part.large) {
+			size = header.p.header_low_payload;
+			if (sst_validate_mailbox_size(size)) {
+				memcpy_fromio(msg->mailbox_data,
+					drv->mailbox + drv->mailbox_recv_offset, size);
+			} else {
+				dev_err(drv->dev,
+					"Mailbox not copied, payload size is: %u\n", size);
+				header.p.header_low_payload = 0;
+			}
+		}
+
+		msg->mrfld_header = header;
+		msg->is_process_reply =
+			sst_is_process_reply(header.p.header_high.part.msg_id);
+		spin_lock(&drv->rx_msg_lock);
+		list_add_tail(&msg->node, &drv->rx_list);
+		spin_unlock(&drv->rx_msg_lock);
+		drv->ops->clear_interrupt(drv);
+		retval = IRQ_WAKE_THREAD;
+	}
+	return retval;
+}
+
+static irqreturn_t intel_sst_irq_thread_mrfld(int irq, void *context)
+{
+	struct intel_sst_drv *drv = (struct intel_sst_drv *) context;
+	struct ipc_post *__msg, *msg = NULL;
+	unsigned long irq_flags;
+
+	spin_lock_irqsave(&drv->rx_msg_lock, irq_flags);
+	if (list_empty(&drv->rx_list)) {
+		spin_unlock_irqrestore(&drv->rx_msg_lock, irq_flags);
+		return IRQ_HANDLED;
+	}
+
+	list_for_each_entry_safe(msg, __msg, &drv->rx_list, node) {
+		list_del(&msg->node);
+		spin_unlock_irqrestore(&drv->rx_msg_lock, irq_flags);
+		if (msg->is_process_reply)
+			drv->ops->process_message(msg);
+		else
+			drv->ops->process_reply(drv, msg);
+
+		if (msg->is_large)
+			kfree(msg->mailbox_data);
+		kfree(msg);
+		spin_lock_irqsave(&drv->rx_msg_lock, irq_flags);
+	}
+	spin_unlock_irqrestore(&drv->rx_msg_lock, irq_flags);
+	return IRQ_HANDLED;
+}
+
+static int sst_save_dsp_context_v2(struct intel_sst_drv *sst)
+{
+	int ret = 0;
+
+	ret = sst_prepare_and_post_msg(sst, SST_TASK_ID_MEDIA, IPC_CMD,
+			IPC_PREP_D3, PIPE_RSVD, 0, NULL, NULL,
+			true, true, false, true);
+
+	if (ret < 0) {
+		dev_err(sst->dev, "not suspending FW!!, Err: %d\n", ret);
+		return -EIO;
+	}
+
+	return 0;
+}
+
+
+static struct intel_sst_ops mrfld_ops = {
+	.interrupt = intel_sst_interrupt_mrfld,
+	.irq_thread = intel_sst_irq_thread_mrfld,
+	.clear_interrupt = intel_sst_clear_intr_mrfld,
+	.start = sst_start_mrfld,
+	.reset = intel_sst_reset_dsp_mrfld,
+	.post_message = sst_post_message_mrfld,
+	.process_reply = sst_process_reply_mrfld,
+	.save_dsp_context =  sst_save_dsp_context_v2,
+	.alloc_stream = sst_alloc_stream_mrfld,
+	.post_download = sst_post_download_mrfld,
+};
+
+int sst_driver_ops(struct intel_sst_drv *sst)
+{
+
+	switch (sst->dev_id) {
+	case SST_MRFLD_PCI_ID:
+	case SST_BYT_ACPI_ID:
+	case SST_CHV_ACPI_ID:
+		sst->tstamp = SST_TIME_STAMP_MRFLD;
+		sst->ops = &mrfld_ops;
+		return 0;
+
+	default:
+		dev_err(sst->dev,
+			"SST Driver capablities missing for dev_id: %x", sst->dev_id);
+		return -EINVAL;
+	};
+}
+
+void sst_process_pending_msg(struct work_struct *work)
+{
+	struct intel_sst_drv *ctx = container_of(work,
+			struct intel_sst_drv, ipc_post_msg_wq);
+
+	ctx->ops->post_message(ctx, NULL, false);
+}
+
+static int sst_workqueue_init(struct intel_sst_drv *ctx)
+{
+	INIT_LIST_HEAD(&ctx->memcpy_list);
+	INIT_LIST_HEAD(&ctx->rx_list);
+	INIT_LIST_HEAD(&ctx->ipc_dispatch_list);
+	INIT_LIST_HEAD(&ctx->block_list);
+	INIT_WORK(&ctx->ipc_post_msg_wq, sst_process_pending_msg);
+	init_waitqueue_head(&ctx->wait_queue);
+
+	ctx->post_msg_wq =
+		create_singlethread_workqueue("sst_post_msg_wq");
+	if (!ctx->post_msg_wq)
+		return -EBUSY;
+	return 0;
+}
+
+static void sst_init_locks(struct intel_sst_drv *ctx)
+{
+	mutex_init(&ctx->sst_lock);
+	spin_lock_init(&ctx->rx_msg_lock);
+	spin_lock_init(&ctx->ipc_spin_lock);
+	spin_lock_init(&ctx->block_lock);
+}
+
+int sst_alloc_drv_context(struct intel_sst_drv **ctx,
+		struct device *dev, unsigned int dev_id)
+{
+	*ctx = devm_kzalloc(dev, sizeof(struct intel_sst_drv), GFP_KERNEL);
+	if (!(*ctx))
+		return -ENOMEM;
+
+	(*ctx)->dev = dev;
+	(*ctx)->dev_id = dev_id;
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(sst_alloc_drv_context);
+
+int sst_context_init(struct intel_sst_drv *ctx)
+{
+	int ret = 0, i;
+
+	if (!ctx->pdata)
+		return -EINVAL;
+
+	if (!ctx->pdata->probe_data)
+		return -EINVAL;
+
+	memcpy(&ctx->info, ctx->pdata->probe_data, sizeof(ctx->info));
+
+	ret = sst_driver_ops(ctx);
+	if (ret != 0)
+		return -EINVAL;
+
+	sst_init_locks(ctx);
+	sst_set_fw_state_locked(ctx, SST_RESET);
+
+	/* pvt_id 0 reserved for async messages */
+	ctx->pvt_id = 1;
+	ctx->stream_cnt = 0;
+	ctx->fw_in_mem = NULL;
+	/* we use memcpy, so set to 0 */
+	ctx->use_dma = 0;
+	ctx->use_lli = 0;
+
+	if (sst_workqueue_init(ctx))
+		return -EINVAL;
+
+	ctx->mailbox_recv_offset = ctx->pdata->ipc_info->mbox_recv_off;
+	ctx->ipc_reg.ipcx = SST_IPCX + ctx->pdata->ipc_info->ipc_offset;
+	ctx->ipc_reg.ipcd = SST_IPCD + ctx->pdata->ipc_info->ipc_offset;
+
+	dev_info(ctx->dev, "Got drv data max stream %d\n",
+				ctx->info.max_streams);
+
+	for (i = 1; i <= ctx->info.max_streams; i++) {
+		struct stream_info *stream = &ctx->streams[i];
+
+		memset(stream, 0, sizeof(*stream));
+		stream->pipe_id = PIPE_RSVD;
+		mutex_init(&stream->lock);
+	}
+
+	/* Register the ISR */
+	ret = devm_request_threaded_irq(ctx->dev, ctx->irq_num, ctx->ops->interrupt,
+					ctx->ops->irq_thread, 0, SST_DRV_NAME,
+					ctx);
+	if (ret)
+		goto do_free_mem;
+
+	dev_dbg(ctx->dev, "Registered IRQ %#x\n", ctx->irq_num);
+
+	/* default intr are unmasked so set this as masked */
+	sst_shim_write64(ctx->shim, SST_IMRX, 0xFFFF0038);
+
+	ctx->qos = devm_kzalloc(ctx->dev,
+		sizeof(struct pm_qos_request), GFP_KERNEL);
+	if (!ctx->qos) {
+		ret = -ENOMEM;
+		goto do_free_mem;
+	}
+	pm_qos_add_request(ctx->qos, PM_QOS_CPU_DMA_LATENCY,
+				PM_QOS_DEFAULT_VALUE);
+
+	dev_dbg(ctx->dev, "Requesting FW %s now...\n", ctx->firmware_name);
+	ret = request_firmware_nowait(THIS_MODULE, true, ctx->firmware_name,
+				      ctx->dev, GFP_KERNEL, ctx, sst_firmware_load_cb);
+	if (ret) {
+		dev_err(ctx->dev, "Firmware download failed:%d\n", ret);
+		goto do_free_mem;
+	}
+	sst_register(ctx->dev);
+	return 0;
+
+do_free_mem:
+	destroy_workqueue(ctx->post_msg_wq);
+	return ret;
+}
+EXPORT_SYMBOL_GPL(sst_context_init);
+
+void sst_context_cleanup(struct intel_sst_drv *ctx)
+{
+	pm_runtime_get_noresume(ctx->dev);
+	pm_runtime_disable(ctx->dev);
+	sst_unregister(ctx->dev);
+	sst_set_fw_state_locked(ctx, SST_SHUTDOWN);
+	flush_scheduled_work();
+	destroy_workqueue(ctx->post_msg_wq);
+	pm_qos_remove_request(ctx->qos);
+	kfree(ctx->fw_sg_list.src);
+	kfree(ctx->fw_sg_list.dst);
+	ctx->fw_sg_list.list_len = 0;
+	kfree(ctx->fw_in_mem);
+	ctx->fw_in_mem = NULL;
+	sst_memcpy_free_resources(ctx);
+	ctx = NULL;
+}
+EXPORT_SYMBOL_GPL(sst_context_cleanup);
+
+static inline void sst_save_shim64(struct intel_sst_drv *ctx,
+			    void __iomem *shim,
+			    struct sst_shim_regs64 *shim_regs)
+{
+	unsigned long irq_flags;
+
+	spin_lock_irqsave(&ctx->ipc_spin_lock, irq_flags);
+
+	shim_regs->imrx = sst_shim_read64(shim, SST_IMRX),
+
+	spin_unlock_irqrestore(&ctx->ipc_spin_lock, irq_flags);
+}
+
+static inline void sst_restore_shim64(struct intel_sst_drv *ctx,
+				      void __iomem *shim,
+				      struct sst_shim_regs64 *shim_regs)
+{
+	unsigned long irq_flags;
+
+	/*
+	 * we only need to restore IMRX for this case, rest will be
+	 * initialize by FW or driver when firmware is loaded
+	 */
+	spin_lock_irqsave(&ctx->ipc_spin_lock, irq_flags);
+	sst_shim_write64(shim, SST_IMRX, shim_regs->imrx),
+	spin_unlock_irqrestore(&ctx->ipc_spin_lock, irq_flags);
+}
+
+void sst_configure_runtime_pm(struct intel_sst_drv *ctx)
+{
+	pm_runtime_set_autosuspend_delay(ctx->dev, SST_SUSPEND_DELAY);
+	pm_runtime_use_autosuspend(ctx->dev);
+	/*
+	 * For acpi devices, the actual physical device state is
+	 * initially active. So change the state to active before
+	 * enabling the pm
+	 */
+	pm_runtime_enable(ctx->dev);
+
+	if (acpi_disabled)
+		pm_runtime_set_active(ctx->dev);
+	else
+		pm_runtime_put_noidle(ctx->dev);
+
+	sst_save_shim64(ctx, ctx->shim, ctx->shim_regs64);
+}
+EXPORT_SYMBOL_GPL(sst_configure_runtime_pm);
+
+static int intel_sst_runtime_suspend(struct device *dev)
+{
+	int ret = 0;
+	struct intel_sst_drv *ctx = dev_get_drvdata(dev);
+
+	if (ctx->sst_state == SST_RESET) {
+		dev_dbg(dev, "LPE is already in RESET state, No action\n");
+		return 0;
+	}
+	/* save fw context */
+	if (ctx->ops->save_dsp_context(ctx))
+		return -EBUSY;
+
+	/* Move the SST state to Reset */
+	sst_set_fw_state_locked(ctx, SST_RESET);
+
+	synchronize_irq(ctx->irq_num);
+	flush_workqueue(ctx->post_msg_wq);
+
+	/* save the shim registers because PMC doesn't save state */
+	sst_save_shim64(ctx, ctx->shim, ctx->shim_regs64);
+
+	return ret;
+}
+
+static int intel_sst_runtime_resume(struct device *dev)
+{
+	int ret = 0;
+	struct intel_sst_drv *ctx = dev_get_drvdata(dev);
+
+	if (ctx->sst_state == SST_RESET) {
+		ret = sst_load_fw(ctx);
+		if (ret) {
+			dev_err(dev, "FW download fail %d\n", ret);
+			sst_set_fw_state_locked(ctx, SST_RESET);
+		}
+	}
+	return ret;
+}
+
+const struct dev_pm_ops intel_sst_pm = {
+	.runtime_suspend = intel_sst_runtime_suspend,
+	.runtime_resume = intel_sst_runtime_resume,
+};
+EXPORT_SYMBOL_GPL(intel_sst_pm);
diff --git a/sound/soc/intel/sst/sst.h b/sound/soc/intel/sst/sst.h
new file mode 100644
index 000000000000..7f4bbfcbc6f5
--- /dev/null
+++ b/sound/soc/intel/sst/sst.h
@@ -0,0 +1,546 @@
+/*
+ *  sst.h - Intel SST Driver for audio engine
+ *
+ *  Copyright (C) 2008-14 Intel Corporation
+ *  Authors:	Vinod Koul <vinod.koul@intel.com>
+ *		Harsha Priya <priya.harsha@intel.com>
+ *		Dharageswari R <dharageswari.r@intel.com>
+ *		KP Jeeja <jeeja.kp@intel.com>
+ *  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; version 2 of the License.
+ *
+ *  This program is distributed in the hope that it will be useful, but
+ *  WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  General Public License for more details.
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ *  Common private declarations for SST
+ */
+#ifndef __SST_H__
+#define __SST_H__
+
+#include <linux/firmware.h>
+
+/* driver names */
+#define SST_DRV_NAME "intel_sst_driver"
+#define SST_MRFLD_PCI_ID 0x119A
+#define SST_BYT_ACPI_ID	0x80860F28
+#define SST_CHV_ACPI_ID	0x808622A8
+
+#define SST_SUSPEND_DELAY 2000
+#define FW_CONTEXT_MEM (64*1024)
+#define SST_ICCM_BOUNDARY 4
+#define SST_CONFIG_SSP_SIGN 0x7ffe8001
+
+#define MRFLD_FW_VIRTUAL_BASE 0xC0000000
+#define MRFLD_FW_DDR_BASE_OFFSET 0x0
+#define MRFLD_FW_FEATURE_BASE_OFFSET 0x4
+#define MRFLD_FW_BSS_RESET_BIT 0
+
+extern const struct dev_pm_ops intel_sst_pm;
+enum sst_states {
+	SST_FW_LOADING = 1,
+	SST_FW_RUNNING,
+	SST_RESET,
+	SST_SHUTDOWN,
+};
+
+enum sst_algo_ops {
+	SST_SET_ALGO = 0,
+	SST_GET_ALGO = 1,
+};
+
+#define SST_BLOCK_TIMEOUT	1000
+
+#define FW_SIGNATURE_SIZE	4
+
+/* stream states */
+enum sst_stream_states {
+	STREAM_UN_INIT	= 0,	/* Freed/Not used stream */
+	STREAM_RUNNING	= 1,	/* Running */
+	STREAM_PAUSED	= 2,	/* Paused stream */
+	STREAM_DECODE	= 3,	/* stream is in decoding only state */
+	STREAM_INIT	= 4,	/* stream init, waiting for data */
+	STREAM_RESET	= 5,	/* force reset on recovery */
+};
+
+enum sst_ram_type {
+	SST_IRAM	= 1,
+	SST_DRAM	= 2,
+	SST_DDR	= 5,
+	SST_CUSTOM_INFO	= 7,	/* consists of FW binary information */
+};
+
+/* SST shim registers to structure mapping */
+union interrupt_reg {
+	struct {
+		u64 done_interrupt:1;
+		u64 busy_interrupt:1;
+		u64 rsvd:62;
+	} part;
+	u64 full;
+};
+
+union sst_pisr_reg {
+	struct {
+		u32 pssp0:1;
+		u32 pssp1:1;
+		u32 rsvd0:3;
+		u32 dmac:1;
+		u32 rsvd1:26;
+	} part;
+	u32 full;
+};
+
+union sst_pimr_reg {
+	struct {
+		u32 ssp0:1;
+		u32 ssp1:1;
+		u32 rsvd0:3;
+		u32 dmac:1;
+		u32 rsvd1:10;
+		u32 ssp0_sc:1;
+		u32 ssp1_sc:1;
+		u32 rsvd2:3;
+		u32 dmac_sc:1;
+		u32 rsvd3:10;
+	} part;
+	u32 full;
+};
+
+union config_status_reg_mrfld {
+	struct {
+		u64 lpe_reset:1;
+		u64 lpe_reset_vector:1;
+		u64 runstall:1;
+		u64 pwaitmode:1;
+		u64 clk_sel:3;
+		u64 rsvd2:1;
+		u64 sst_clk:3;
+		u64 xt_snoop:1;
+		u64 rsvd3:4;
+		u64 clk_sel1:6;
+		u64 clk_enable:3;
+		u64 rsvd4:6;
+		u64 slim0baseclk:1;
+		u64 rsvd:32;
+	} part;
+	u64 full;
+};
+
+union interrupt_reg_mrfld {
+	struct {
+		u64 done_interrupt:1;
+		u64 busy_interrupt:1;
+		u64 rsvd:62;
+	} part;
+	u64 full;
+};
+
+union sst_imr_reg_mrfld {
+	struct {
+		u64 done_interrupt:1;
+		u64 busy_interrupt:1;
+		u64 rsvd:62;
+	} part;
+	u64 full;
+};
+
+/**
+ * struct sst_block - This structure is used to block a user/fw data call to another
+ * fw/user call
+ *
+ * @condition: condition for blocking check
+ * @ret_code: ret code when block is released
+ * @data: data ptr
+ * @size: size of data
+ * @on: block condition
+ * @msg_id: msg_id = msgid in mfld/ctp, mrfld = NULL
+ * @drv_id: str_id in mfld/ctp, = drv_id in mrfld
+ * @node: list head node
+ */
+struct sst_block {
+	bool	condition;
+	int	ret_code;
+	void	*data;
+	u32     size;
+	bool	on;
+	u32     msg_id;
+	u32     drv_id;
+	struct list_head node;
+};
+
+/**
+ * struct stream_info - structure that holds the stream information
+ *
+ * @status : stream current state
+ * @prev : stream prev state
+ * @ops : stream operation pb/cp/drm...
+ * @bufs: stream buffer list
+ * @lock : stream mutex for protecting state
+ * @pcm_substream : PCM substream
+ * @period_elapsed : PCM period elapsed callback
+ * @sfreq : stream sampling freq
+ * @str_type : stream type
+ * @cumm_bytes : cummulative bytes decoded
+ * @str_type : stream type
+ * @src : stream source
+ */
+struct stream_info {
+	unsigned int		status;
+	unsigned int		prev;
+	unsigned int		ops;
+	struct mutex		lock;
+
+	void			*pcm_substream;
+	void (*period_elapsed)(void *pcm_substream);
+
+	unsigned int		sfreq;
+	u32			cumm_bytes;
+
+	void			*compr_cb_param;
+	void (*compr_cb)(void *compr_cb_param);
+
+	void			*drain_cb_param;
+	void (*drain_notify)(void *drain_cb_param);
+
+	unsigned int		num_ch;
+	unsigned int		pipe_id;
+	unsigned int		str_id;
+	unsigned int		task_id;
+};
+
+#define SST_FW_SIGN "$SST"
+#define SST_FW_LIB_SIGN "$LIB"
+
+/**
+ * struct sst_fw_header - FW file headers
+ *
+ * @signature : FW signature
+ * @file_size: size of fw image
+ * @modules : # of modules
+ * @file_format : version of header format
+ * @reserved : reserved fields
+ */
+struct sst_fw_header {
+	unsigned char signature[FW_SIGNATURE_SIZE];
+	u32 file_size;
+	u32 modules;
+	u32 file_format;
+	u32 reserved[4];
+};
+
+/**
+ * struct fw_module_header - module header in FW
+ *
+ * @signature: module signature
+ * @mod_size: size of module
+ * @blocks: block count
+ * @type: block type
+ * @entry_point: module netry point
+ */
+struct fw_module_header {
+	unsigned char signature[FW_SIGNATURE_SIZE];
+	u32 mod_size;
+	u32 blocks;
+	u32 type;
+	u32 entry_point;
+};
+
+/**
+ * struct fw_block_info - block header for FW
+ *
+ * @type: block ram type I/D
+ * @size: size of block
+ * @ram_offset: offset in ram
+ */
+struct fw_block_info {
+	enum sst_ram_type	type;
+	u32			size;
+	u32			ram_offset;
+	u32			rsvd;
+};
+
+struct sst_runtime_param {
+	struct snd_sst_runtime_params param;
+};
+
+struct sst_sg_list {
+	struct scatterlist *src;
+	struct scatterlist *dst;
+	int list_len;
+	unsigned int sg_idx;
+};
+
+struct sst_memcpy_list {
+	struct list_head memcpylist;
+	void *dstn;
+	const void *src;
+	u32 size;
+	bool is_io;
+};
+
+/*Firmware Module Information*/
+enum sst_lib_dwnld_status {
+	SST_LIB_NOT_FOUND = 0,
+	SST_LIB_FOUND,
+	SST_LIB_DOWNLOADED,
+};
+
+struct sst_module_info {
+	const char *name; /*Library name*/
+	u32	id; /*Module ID*/
+	u32	entry_pt; /*Module entry point*/
+	u8	status; /*module status*/
+	u8	rsvd1;
+	u16	rsvd2;
+};
+
+/*
+ * Structure for managing the Library Region(1.5MB)
+ * in DDR in Merrifield
+ */
+struct sst_mem_mgr {
+	phys_addr_t current_base;
+	int avail;
+	unsigned int count;
+};
+
+struct sst_ipc_reg {
+	int ipcx;
+	int ipcd;
+};
+
+struct sst_shim_regs64 {
+	u64 csr;
+	u64 pisr;
+	u64 pimr;
+	u64 isrx;
+	u64 isrd;
+	u64 imrx;
+	u64 imrd;
+	u64 ipcx;
+	u64 ipcd;
+	u64 isrsc;
+	u64 isrlpesc;
+	u64 imrsc;
+	u64 imrlpesc;
+	u64 ipcsc;
+	u64 ipclpesc;
+	u64 clkctl;
+	u64 csr2;
+};
+
+/**
+ * struct intel_sst_drv - driver ops
+ *
+ * @sst_state : current sst device state
+ * @dev_id : device identifier, pci_id for pci devices and acpi_id for acpi
+ * 	     devices
+ * @shim : SST shim pointer
+ * @mailbox : SST mailbox pointer
+ * @iram : SST IRAM pointer
+ * @dram : SST DRAM pointer
+ * @pdata : SST info passed as a part of pci platform data
+ * @shim_phy_add : SST shim phy addr
+ * @shim_regs64: Struct to save shim registers
+ * @ipc_dispatch_list : ipc messages dispatched
+ * @rx_list : to copy the process_reply/process_msg from DSP
+ * @ipc_post_msg_wq : wq to post IPC messages context
+ * @mad_ops : MAD driver operations registered
+ * @mad_wq : MAD driver wq
+ * @post_msg_wq : wq to post IPC messages
+ * @streams : sst stream contexts
+ * @list_lock : sst driver list lock (deprecated)
+ * @ipc_spin_lock : spin lock to handle audio shim access and ipc queue
+ * @block_lock : spin lock to add block to block_list and assign pvt_id
+ * @rx_msg_lock : spin lock to handle the rx messages from the DSP
+ * @scard_ops : sst card ops
+ * @pci : sst pci device struture
+ * @dev : pointer to current device struct
+ * @sst_lock : sst device lock
+ * @pvt_id : sst private id
+ * @stream_cnt : total sst active stream count
+ * @pb_streams : total active pb streams
+ * @cp_streams : total active cp streams
+ * @audio_start : audio status
+ * @qos		: PM Qos struct
+ * firmware_name : Firmware / Library name
+ */
+struct intel_sst_drv {
+	int			sst_state;
+	int			irq_num;
+	unsigned int		dev_id;
+	void __iomem		*ddr;
+	void __iomem		*shim;
+	void __iomem		*mailbox;
+	void __iomem		*iram;
+	void __iomem		*dram;
+	unsigned int		mailbox_add;
+	unsigned int		iram_base;
+	unsigned int		dram_base;
+	unsigned int		shim_phy_add;
+	unsigned int		iram_end;
+	unsigned int		dram_end;
+	unsigned int		ddr_end;
+	unsigned int		ddr_base;
+	unsigned int		mailbox_recv_offset;
+	struct sst_shim_regs64	*shim_regs64;
+	struct list_head        block_list;
+	struct list_head	ipc_dispatch_list;
+	struct sst_platform_info *pdata;
+	struct list_head	rx_list;
+	struct work_struct      ipc_post_msg_wq;
+	wait_queue_head_t	wait_queue;
+	struct workqueue_struct *post_msg_wq;
+	unsigned int		tstamp;
+	/* str_id 0 is not used */
+	struct stream_info	streams[MAX_NUM_STREAMS+1];
+	spinlock_t		ipc_spin_lock;
+	spinlock_t              block_lock;
+	spinlock_t		rx_msg_lock;
+	struct pci_dev		*pci;
+	struct device		*dev;
+	volatile long unsigned 		pvt_id;
+	struct mutex            sst_lock;
+	unsigned int		stream_cnt;
+	unsigned int		csr_value;
+	void			*fw_in_mem;
+	struct sst_sg_list	fw_sg_list, library_list;
+	struct intel_sst_ops	*ops;
+	struct sst_info		info;
+	struct pm_qos_request	*qos;
+	unsigned int		use_dma;
+	unsigned int		use_lli;
+	atomic_t		fw_clear_context;
+	bool			lib_dwnld_reqd;
+	struct list_head	memcpy_list;
+	struct sst_ipc_reg	ipc_reg;
+	struct sst_mem_mgr      lib_mem_mgr;
+	/*
+	 * Holder for firmware name. Due to async call it needs to be
+	 * persistent till worker thread gets called
+	 */
+	char firmware_name[20];
+};
+
+/* misc definitions */
+#define FW_DWNL_ID 0x01
+
+struct intel_sst_ops {
+	irqreturn_t (*interrupt)(int, void *);
+	irqreturn_t (*irq_thread)(int, void *);
+	void (*clear_interrupt)(struct intel_sst_drv *ctx);
+	int (*start)(struct intel_sst_drv *ctx);
+	int (*reset)(struct intel_sst_drv *ctx);
+	void (*process_reply)(struct intel_sst_drv *ctx, struct ipc_post *msg);
+	int (*post_message)(struct intel_sst_drv *ctx,
+			struct ipc_post *msg, bool sync);
+	void (*process_message)(struct ipc_post *msg);
+	void (*set_bypass)(bool set);
+	int (*save_dsp_context)(struct intel_sst_drv *sst);
+	void (*restore_dsp_context)(void);
+	int (*alloc_stream)(struct intel_sst_drv *ctx, void *params);
+	void (*post_download)(struct intel_sst_drv *sst);
+};
+
+int sst_pause_stream(struct intel_sst_drv *sst_drv_ctx, int id);
+int sst_resume_stream(struct intel_sst_drv *sst_drv_ctx, int id);
+int sst_drop_stream(struct intel_sst_drv *sst_drv_ctx, int id);
+int sst_free_stream(struct intel_sst_drv *sst_drv_ctx, int id);
+int sst_start_stream(struct intel_sst_drv *sst_drv_ctx, int str_id);
+int sst_send_byte_stream_mrfld(struct intel_sst_drv *ctx,
+			struct snd_sst_bytes_v2 *sbytes);
+int sst_set_stream_param(int str_id, struct snd_sst_params *str_param);
+int sst_set_metadata(int str_id, char *params);
+int sst_get_stream(struct intel_sst_drv *sst_drv_ctx,
+		struct snd_sst_params *str_param);
+int sst_get_stream_allocated(struct intel_sst_drv *ctx,
+		struct snd_sst_params *str_param,
+		struct snd_sst_lib_download **lib_dnld);
+int sst_drain_stream(struct intel_sst_drv *sst_drv_ctx,
+		int str_id, bool partial_drain);
+int sst_post_message_mrfld(struct intel_sst_drv *ctx,
+		struct ipc_post *msg, bool sync);
+void sst_process_reply_mrfld(struct intel_sst_drv *ctx, struct ipc_post *msg);
+int sst_start_mrfld(struct intel_sst_drv *ctx);
+int intel_sst_reset_dsp_mrfld(struct intel_sst_drv *ctx);
+void intel_sst_clear_intr_mrfld(struct intel_sst_drv *ctx);
+
+int sst_load_fw(struct intel_sst_drv *ctx);
+int sst_load_library(struct snd_sst_lib_download *lib, u8 ops);
+void sst_post_download_mrfld(struct intel_sst_drv *ctx);
+int sst_get_block_stream(struct intel_sst_drv *sst_drv_ctx);
+void sst_memcpy_free_resources(struct intel_sst_drv *ctx);
+
+int sst_wait_interruptible(struct intel_sst_drv *sst_drv_ctx,
+				struct sst_block *block);
+int sst_wait_timeout(struct intel_sst_drv *sst_drv_ctx,
+			struct sst_block *block);
+int sst_create_ipc_msg(struct ipc_post **arg, bool large);
+int free_stream_context(struct intel_sst_drv *ctx, unsigned int str_id);
+void sst_clean_stream(struct stream_info *stream);
+int intel_sst_register_compress(struct intel_sst_drv *sst);
+int intel_sst_remove_compress(struct intel_sst_drv *sst);
+void sst_cdev_fragment_elapsed(struct intel_sst_drv *ctx, int str_id);
+int sst_send_sync_msg(int ipc, int str_id);
+int sst_get_num_channel(struct snd_sst_params *str_param);
+int sst_get_sfreq(struct snd_sst_params *str_param);
+int sst_alloc_stream_mrfld(struct intel_sst_drv *sst_drv_ctx, void *params);
+void sst_restore_fw_context(void);
+struct sst_block *sst_create_block(struct intel_sst_drv *ctx,
+				u32 msg_id, u32 drv_id);
+int sst_create_block_and_ipc_msg(struct ipc_post **arg, bool large,
+		struct intel_sst_drv *sst_drv_ctx, struct sst_block **block,
+		u32 msg_id, u32 drv_id);
+int sst_free_block(struct intel_sst_drv *ctx, struct sst_block *freed);
+int sst_wake_up_block(struct intel_sst_drv *ctx, int result,
+		u32 drv_id, u32 ipc, void *data, u32 size);
+int sst_request_firmware_async(struct intel_sst_drv *ctx);
+int sst_driver_ops(struct intel_sst_drv *sst);
+struct sst_platform_info *sst_get_acpi_driver_data(const char *hid);
+void sst_firmware_load_cb(const struct firmware *fw, void *context);
+int sst_prepare_and_post_msg(struct intel_sst_drv *sst,
+		int task_id, int ipc_msg, int cmd_id, int pipe_id,
+		size_t mbox_data_len, const void *mbox_data, void **data,
+		bool large, bool fill_dsp, bool sync, bool response);
+
+void sst_process_pending_msg(struct work_struct *work);
+int sst_assign_pvt_id(struct intel_sst_drv *sst_drv_ctx);
+void sst_init_stream(struct stream_info *stream,
+		int codec, int sst_id, int ops, u8 slot);
+int sst_validate_strid(struct intel_sst_drv *sst_drv_ctx, int str_id);
+struct stream_info *get_stream_info(struct intel_sst_drv *sst_drv_ctx,
+		int str_id);
+int get_stream_id_mrfld(struct intel_sst_drv *sst_drv_ctx,
+		u32 pipe_id);
+u32 relocate_imr_addr_mrfld(u32 base_addr);
+void sst_add_to_dispatch_list_and_post(struct intel_sst_drv *sst,
+					struct ipc_post *msg);
+int sst_pm_runtime_put(struct intel_sst_drv *sst_drv);
+int sst_shim_write(void __iomem *addr, int offset, int value);
+u32 sst_shim_read(void __iomem *addr, int offset);
+u64 sst_reg_read64(void __iomem *addr, int offset);
+int sst_shim_write64(void __iomem *addr, int offset, u64 value);
+u64 sst_shim_read64(void __iomem *addr, int offset);
+void sst_set_fw_state_locked(
+		struct intel_sst_drv *sst_drv_ctx, int sst_state);
+void sst_fill_header_mrfld(union ipc_header_mrfld *header,
+				int msg, int task_id, int large, int drv_id);
+void sst_fill_header_dsp(struct ipc_dsp_hdr *dsp, int msg,
+					int pipe_id, int len);
+
+int sst_register(struct device *);
+int sst_unregister(struct device *);
+
+int sst_alloc_drv_context(struct intel_sst_drv **ctx,
+		struct device *dev, unsigned int dev_id);
+int sst_context_init(struct intel_sst_drv *ctx);
+void sst_context_cleanup(struct intel_sst_drv *ctx);
+void sst_configure_runtime_pm(struct intel_sst_drv *ctx);
+#endif
diff --git a/sound/soc/intel/sst/sst_acpi.c b/sound/soc/intel/sst/sst_acpi.c
new file mode 100644
index 000000000000..31124aa4434e
--- /dev/null
+++ b/sound/soc/intel/sst/sst_acpi.c
@@ -0,0 +1,383 @@
+/*
+ * sst_acpi.c - SST (LPE) driver init file for ACPI enumeration.
+ *
+ * Copyright (c) 2013, Intel Corporation.
+ *
+ *  Authors:	Ramesh Babu K V <Ramesh.Babu@intel.com>
+ *  Authors:	Omair Mohammed Abdullah <omair.m.abdullah@intel.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/fs.h>
+#include <linux/interrupt.h>
+#include <linux/slab.h>
+#include <linux/io.h>
+#include <linux/miscdevice.h>
+#include <linux/platform_device.h>
+#include <linux/firmware.h>
+#include <linux/pm_runtime.h>
+#include <linux/pm_qos.h>
+#include <linux/acpi.h>
+#include <asm/platform_sst_audio.h>
+#include <sound/core.h>
+#include <sound/soc.h>
+#include <sound/compress_driver.h>
+#include <acpi/acbuffer.h>
+#include <acpi/platform/acenv.h>
+#include <acpi/platform/aclinux.h>
+#include <acpi/actypes.h>
+#include <acpi/acpi_bus.h>
+#include "../sst-mfld-platform.h"
+#include "../sst-dsp.h"
+#include "sst.h"
+
+struct sst_machines {
+	char codec_id[32];
+	char board[32];
+	char machine[32];
+	void (*machine_quirk)(void);
+	char firmware[32];
+	struct sst_platform_info *pdata;
+
+};
+
+/* LPE viewpoint addresses */
+#define SST_BYT_IRAM_PHY_START	0xff2c0000
+#define SST_BYT_IRAM_PHY_END	0xff2d4000
+#define SST_BYT_DRAM_PHY_START	0xff300000
+#define SST_BYT_DRAM_PHY_END	0xff320000
+#define SST_BYT_IMR_VIRT_START	0xc0000000 /* virtual addr in LPE */
+#define SST_BYT_IMR_VIRT_END	0xc01fffff
+#define SST_BYT_SHIM_PHY_ADDR	0xff340000
+#define SST_BYT_MBOX_PHY_ADDR	0xff344000
+#define SST_BYT_DMA0_PHY_ADDR	0xff298000
+#define SST_BYT_DMA1_PHY_ADDR	0xff29c000
+#define SST_BYT_SSP0_PHY_ADDR	0xff2a0000
+#define SST_BYT_SSP2_PHY_ADDR	0xff2a2000
+
+#define BYT_FW_MOD_TABLE_OFFSET	0x80000
+#define BYT_FW_MOD_TABLE_SIZE	0x100
+#define BYT_FW_MOD_OFFSET	(BYT_FW_MOD_TABLE_OFFSET + BYT_FW_MOD_TABLE_SIZE)
+
+static const struct sst_info byt_fwparse_info = {
+	.use_elf	= false,
+	.max_streams	= 25,
+	.iram_start	= SST_BYT_IRAM_PHY_START,
+	.iram_end	= SST_BYT_IRAM_PHY_END,
+	.iram_use	= true,
+	.dram_start	= SST_BYT_DRAM_PHY_START,
+	.dram_end	= SST_BYT_DRAM_PHY_END,
+	.dram_use	= true,
+	.imr_start	= SST_BYT_IMR_VIRT_START,
+	.imr_end	= SST_BYT_IMR_VIRT_END,
+	.imr_use	= true,
+	.mailbox_start	= SST_BYT_MBOX_PHY_ADDR,
+	.num_probes	= 0,
+	.lpe_viewpt_rqd  = true,
+};
+
+static const struct sst_ipc_info byt_ipc_info = {
+	.ipc_offset = 0,
+	.mbox_recv_off = 0x400,
+};
+
+static const struct sst_lib_dnld_info  byt_lib_dnld_info = {
+	.mod_base           = SST_BYT_IMR_VIRT_START,
+	.mod_end            = SST_BYT_IMR_VIRT_END,
+	.mod_table_offset   = BYT_FW_MOD_TABLE_OFFSET,
+	.mod_table_size     = BYT_FW_MOD_TABLE_SIZE,
+	.mod_ddr_dnld       = false,
+};
+
+static const struct sst_res_info byt_rvp_res_info = {
+	.shim_offset = 0x140000,
+	.shim_size = 0x000100,
+	.shim_phy_addr = SST_BYT_SHIM_PHY_ADDR,
+	.ssp0_offset = 0xa0000,
+	.ssp0_size = 0x1000,
+	.dma0_offset = 0x98000,
+	.dma0_size = 0x4000,
+	.dma1_offset = 0x9c000,
+	.dma1_size = 0x4000,
+	.iram_offset = 0x0c0000,
+	.iram_size = 0x14000,
+	.dram_offset = 0x100000,
+	.dram_size = 0x28000,
+	.mbox_offset = 0x144000,
+	.mbox_size = 0x1000,
+	.acpi_lpe_res_index = 0,
+	.acpi_ddr_index = 2,
+	.acpi_ipc_irq_index = 5,
+};
+
+static struct sst_platform_info byt_rvp_platform_data = {
+	.probe_data = &byt_fwparse_info,
+	.ipc_info = &byt_ipc_info,
+	.lib_info = &byt_lib_dnld_info,
+	.res_info = &byt_rvp_res_info,
+	.platform = "sst-mfld-platform",
+};
+
+/* Cherryview (Cherrytrail and Braswell) uses same mrfld dpcm fw as Baytrail,
+ * so pdata is same as Baytrail.
+ */
+static struct sst_platform_info chv_platform_data = {
+	.probe_data = &byt_fwparse_info,
+	.ipc_info = &byt_ipc_info,
+	.lib_info = &byt_lib_dnld_info,
+	.res_info = &byt_rvp_res_info,
+	.platform = "sst-mfld-platform",
+};
+
+static int sst_platform_get_resources(struct intel_sst_drv *ctx)
+{
+	struct resource *rsrc;
+	struct platform_device *pdev = to_platform_device(ctx->dev);
+
+	/* All ACPI resource request here */
+	/* Get Shim addr */
+	rsrc = platform_get_resource(pdev, IORESOURCE_MEM,
+					ctx->pdata->res_info->acpi_lpe_res_index);
+	if (!rsrc) {
+		dev_err(ctx->dev, "Invalid SHIM base from IFWI");
+		return -EIO;
+	}
+	dev_info(ctx->dev, "LPE base: %#x size:%#x", (unsigned int) rsrc->start,
+					(unsigned int)resource_size(rsrc));
+
+	ctx->iram_base = rsrc->start + ctx->pdata->res_info->iram_offset;
+	ctx->iram_end =  ctx->iram_base + ctx->pdata->res_info->iram_size - 1;
+	dev_info(ctx->dev, "IRAM base: %#x", ctx->iram_base);
+	ctx->iram = devm_ioremap_nocache(ctx->dev, ctx->iram_base,
+					 ctx->pdata->res_info->iram_size);
+	if (!ctx->iram) {
+		dev_err(ctx->dev, "unable to map IRAM");
+		return -EIO;
+	}
+
+	ctx->dram_base = rsrc->start + ctx->pdata->res_info->dram_offset;
+	ctx->dram_end = ctx->dram_base + ctx->pdata->res_info->dram_size - 1;
+	dev_info(ctx->dev, "DRAM base: %#x", ctx->dram_base);
+	ctx->dram = devm_ioremap_nocache(ctx->dev, ctx->dram_base,
+					 ctx->pdata->res_info->dram_size);
+	if (!ctx->dram) {
+		dev_err(ctx->dev, "unable to map DRAM");
+		return -EIO;
+	}
+
+	ctx->shim_phy_add = rsrc->start + ctx->pdata->res_info->shim_offset;
+	dev_info(ctx->dev, "SHIM base: %#x", ctx->shim_phy_add);
+	ctx->shim = devm_ioremap_nocache(ctx->dev, ctx->shim_phy_add,
+					ctx->pdata->res_info->shim_size);
+	if (!ctx->shim) {
+		dev_err(ctx->dev, "unable to map SHIM");
+		return -EIO;
+	}
+
+	/* reassign physical address to LPE viewpoint address */
+	ctx->shim_phy_add = ctx->pdata->res_info->shim_phy_addr;
+
+	/* Get mailbox addr */
+	ctx->mailbox_add = rsrc->start + ctx->pdata->res_info->mbox_offset;
+	dev_info(ctx->dev, "Mailbox base: %#x", ctx->mailbox_add);
+	ctx->mailbox = devm_ioremap_nocache(ctx->dev, ctx->mailbox_add,
+					    ctx->pdata->res_info->mbox_size);
+	if (!ctx->mailbox) {
+		dev_err(ctx->dev, "unable to map mailbox");
+		return -EIO;
+	}
+
+	/* reassign physical address to LPE viewpoint address */
+	ctx->mailbox_add = ctx->info.mailbox_start;
+
+	rsrc = platform_get_resource(pdev, IORESOURCE_MEM,
+					ctx->pdata->res_info->acpi_ddr_index);
+	if (!rsrc) {
+		dev_err(ctx->dev, "Invalid DDR base from IFWI");
+		return -EIO;
+	}
+	ctx->ddr_base = rsrc->start;
+	ctx->ddr_end = rsrc->end;
+	dev_info(ctx->dev, "DDR base: %#x", ctx->ddr_base);
+	ctx->ddr = devm_ioremap_nocache(ctx->dev, ctx->ddr_base,
+					resource_size(rsrc));
+	if (!ctx->ddr) {
+		dev_err(ctx->dev, "unable to map DDR");
+		return -EIO;
+	}
+
+	/* Find the IRQ */
+	ctx->irq_num = platform_get_irq(pdev,
+				ctx->pdata->res_info->acpi_ipc_irq_index);
+	return 0;
+}
+
+static acpi_status sst_acpi_mach_match(acpi_handle handle, u32 level,
+				       void *context, void **ret)
+{
+	*(bool *)context = true;
+	return AE_OK;
+}
+
+static struct sst_machines *sst_acpi_find_machine(
+	struct sst_machines *machines)
+{
+	struct sst_machines *mach;
+	bool found = false;
+
+	for (mach = machines; mach->codec_id; mach++)
+		if (ACPI_SUCCESS(acpi_get_devices(mach->codec_id,
+						  sst_acpi_mach_match,
+						  &found, NULL)) && found)
+			return mach;
+
+	return NULL;
+}
+
+int sst_acpi_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	int ret = 0;
+	struct intel_sst_drv *ctx;
+	const struct acpi_device_id *id;
+	struct sst_machines *mach;
+	struct platform_device *mdev;
+	struct platform_device *plat_dev;
+	unsigned int dev_id;
+
+	id = acpi_match_device(dev->driver->acpi_match_table, dev);
+	if (!id)
+		return -ENODEV;
+	dev_dbg(dev, "for %s", id->id);
+
+	mach = (struct sst_machines *)id->driver_data;
+	mach = sst_acpi_find_machine(mach);
+	if (mach == NULL) {
+		dev_err(dev, "No matching machine driver found\n");
+		return -ENODEV;
+	}
+
+	ret = kstrtouint(id->id, 16, &dev_id);
+	if (ret < 0) {
+		dev_err(dev, "Unique device id conversion error: %d\n", ret);
+		return ret;
+	}
+
+	dev_dbg(dev, "ACPI device id: %x\n", dev_id);
+
+	plat_dev = platform_device_register_data(dev, mach->pdata->platform, -1, NULL, 0);
+	if (plat_dev == NULL) {
+		dev_err(dev, "Failed to create machine device: %s\n", mach->pdata->platform);
+		return -ENODEV;
+	}
+
+	/* Create platform device for sst machine driver */
+	mdev = platform_device_register_data(dev, mach->machine, -1, NULL, 0);
+	if (mdev == NULL) {
+		dev_err(dev, "Failed to create machine device: %s\n", mach->machine);
+		return -ENODEV;
+	}
+
+	ret = sst_alloc_drv_context(&ctx, dev, dev_id);
+	if (ret < 0)
+		return ret;
+
+	/* Fill sst platform data */
+	ctx->pdata = mach->pdata;
+	strcpy(ctx->firmware_name, mach->firmware);
+
+	ret = sst_platform_get_resources(ctx);
+	if (ret)
+		return ret;
+
+	ret = sst_context_init(ctx);
+	if (ret < 0)
+		return ret;
+
+	/* need to save shim registers in BYT */
+	ctx->shim_regs64 = devm_kzalloc(ctx->dev, sizeof(*ctx->shim_regs64),
+					GFP_KERNEL);
+	if (!ctx->shim_regs64) {
+		return -ENOMEM;
+		goto do_sst_cleanup;
+	}
+
+	sst_configure_runtime_pm(ctx);
+	platform_set_drvdata(pdev, ctx);
+	return ret;
+
+do_sst_cleanup:
+	sst_context_cleanup(ctx);
+	platform_set_drvdata(pdev, NULL);
+	dev_err(ctx->dev, "failed with %d\n", ret);
+	return ret;
+}
+
+/**
+* intel_sst_remove - remove function
+*
+* @pdev:	platform device structure
+*
+* This function is called by OS when a device is unloaded
+* This frees the interrupt etc
+*/
+int sst_acpi_remove(struct platform_device *pdev)
+{
+	struct intel_sst_drv *ctx;
+
+	ctx = platform_get_drvdata(pdev);
+	sst_context_cleanup(ctx);
+	platform_set_drvdata(pdev, NULL);
+	return 0;
+}
+
+static struct sst_machines sst_acpi_bytcr[] = {
+	{"10EC5640", "T100", "bytt100_rt5640", NULL, "fw_sst_0f28.bin",
+						&byt_rvp_platform_data },
+	{},
+};
+
+/* Cherryview-based platforms: CherryTrail and Braswell */
+static struct sst_machines sst_acpi_chv[] = {
+	{"10EC5670", "cht-bsw", "cht-bsw-rt5672", NULL, "fw_sst_22a8.bin",
+						&chv_platform_data },
+	{},
+};
+
+static const struct acpi_device_id sst_acpi_ids[] = {
+	{ "80860F28", (unsigned long)&sst_acpi_bytcr},
+	{ "808622A8", (unsigned long) &sst_acpi_chv},
+	{ },
+};
+
+MODULE_DEVICE_TABLE(acpi, sst_acpi_ids);
+
+static struct platform_driver sst_acpi_driver = {
+	.driver = {
+		.name			= "intel_sst_acpi",
+		.owner			= THIS_MODULE,
+		.acpi_match_table	= ACPI_PTR(sst_acpi_ids),
+		.pm			= &intel_sst_pm,
+	},
+	.probe	= sst_acpi_probe,
+	.remove	= sst_acpi_remove,
+};
+
+module_platform_driver(sst_acpi_driver);
+
+MODULE_DESCRIPTION("Intel (R) SST(R) Audio Engine ACPI Driver");
+MODULE_AUTHOR("Ramesh Babu K V");
+MODULE_AUTHOR("Omair Mohammed Abdullah");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("sst");
diff --git a/sound/soc/intel/sst/sst_drv_interface.c b/sound/soc/intel/sst/sst_drv_interface.c
new file mode 100644
index 000000000000..5f75ef3cdd22
--- /dev/null
+++ b/sound/soc/intel/sst/sst_drv_interface.c
@@ -0,0 +1,686 @@
+/*
+ *  sst_drv_interface.c - Intel SST Driver for audio engine
+ *
+ *  Copyright (C) 2008-14 Intel Corp
+ *  Authors:	Vinod Koul <vinod.koul@intel.com>
+ *		Harsha Priya <priya.harsha@intel.com>
+ *		Dharageswari R <dharageswari.r@intel.com)
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; version 2 of the License.
+ *
+ *  This program is distributed in the hope that it will be useful, but
+ *  WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  General Public License for more details.
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+#include <linux/delay.h>
+#include <linux/pci.h>
+#include <linux/fs.h>
+#include <linux/firmware.h>
+#include <linux/pm_runtime.h>
+#include <linux/pm_qos.h>
+#include <linux/math64.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+#include <sound/compress_driver.h>
+#include <asm/platform_sst_audio.h>
+#include "../sst-mfld-platform.h"
+#include "sst.h"
+#include "../sst-dsp.h"
+
+
+
+#define NUM_CODEC 2
+#define MIN_FRAGMENT 2
+#define MAX_FRAGMENT 4
+#define MIN_FRAGMENT_SIZE (50 * 1024)
+#define MAX_FRAGMENT_SIZE (1024 * 1024)
+#define SST_GET_BYTES_PER_SAMPLE(pcm_wd_sz)  (((pcm_wd_sz + 15) >> 4) << 1)
+
+int free_stream_context(struct intel_sst_drv *ctx, unsigned int str_id)
+{
+	struct stream_info *stream;
+	int ret = 0;
+
+	stream = get_stream_info(ctx, str_id);
+	if (stream) {
+		/* str_id is valid, so stream is alloacted */
+		ret = sst_free_stream(ctx, str_id);
+		if (ret)
+			sst_clean_stream(&ctx->streams[str_id]);
+		return ret;
+	} else {
+		dev_err(ctx->dev, "we tried to free stream context %d which was freed!!!\n", str_id);
+	}
+	return ret;
+}
+
+int sst_get_stream_allocated(struct intel_sst_drv *ctx,
+	struct snd_sst_params *str_param,
+	struct snd_sst_lib_download **lib_dnld)
+{
+	int retval;
+
+	retval = ctx->ops->alloc_stream(ctx, str_param);
+	if (retval > 0)
+		dev_dbg(ctx->dev, "Stream allocated %d\n", retval);
+	return retval;
+
+}
+
+/*
+ * sst_get_sfreq - this function returns the frequency of the stream
+ *
+ * @str_param : stream params
+ */
+int sst_get_sfreq(struct snd_sst_params *str_param)
+{
+	switch (str_param->codec) {
+	case SST_CODEC_TYPE_PCM:
+		return str_param->sparams.uc.pcm_params.sfreq;
+	case SST_CODEC_TYPE_AAC:
+		return str_param->sparams.uc.aac_params.externalsr;
+	case SST_CODEC_TYPE_MP3:
+		return 0;
+	default:
+		return -EINVAL;
+	}
+}
+
+/*
+ * sst_get_num_channel - get number of channels for the stream
+ *
+ * @str_param : stream params
+ */
+int sst_get_num_channel(struct snd_sst_params *str_param)
+{
+	switch (str_param->codec) {
+	case SST_CODEC_TYPE_PCM:
+		return str_param->sparams.uc.pcm_params.num_chan;
+	case SST_CODEC_TYPE_MP3:
+		return str_param->sparams.uc.mp3_params.num_chan;
+	case SST_CODEC_TYPE_AAC:
+		return str_param->sparams.uc.aac_params.num_chan;
+	default:
+		return -EINVAL;
+	}
+}
+
+/*
+ * sst_get_stream - this function prepares for stream allocation
+ *
+ * @str_param : stream param
+ */
+int sst_get_stream(struct intel_sst_drv *ctx,
+			struct snd_sst_params *str_param)
+{
+	int retval;
+	struct stream_info *str_info;
+
+	/* stream is not allocated, we are allocating */
+	retval = ctx->ops->alloc_stream(ctx, str_param);
+	if (retval <= 0) {
+		return -EIO;
+	}
+	/* store sampling freq */
+	str_info = &ctx->streams[retval];
+	str_info->sfreq = sst_get_sfreq(str_param);
+
+	return retval;
+}
+
+static int sst_power_control(struct device *dev, bool state)
+{
+	struct intel_sst_drv *ctx = dev_get_drvdata(dev);
+
+	dev_dbg(ctx->dev, "state:%d", state);
+	if (state == true)
+		return pm_runtime_get_sync(dev);
+	else
+		return sst_pm_runtime_put(ctx);
+}
+
+/*
+ * sst_open_pcm_stream - Open PCM interface
+ *
+ * @str_param: parameters of pcm stream
+ *
+ * This function is called by MID sound card driver to open
+ * a new pcm interface
+ */
+static int sst_open_pcm_stream(struct device *dev,
+		struct snd_sst_params *str_param)
+{
+	int retval;
+	struct intel_sst_drv *ctx = dev_get_drvdata(dev);
+
+	if (!str_param)
+		return -EINVAL;
+
+	retval = sst_get_stream(ctx, str_param);
+	if (retval > 0)
+		ctx->stream_cnt++;
+	else
+		dev_err(ctx->dev, "sst_get_stream returned err %d\n", retval);
+
+	return retval;
+}
+
+static int sst_cdev_open(struct device *dev,
+		struct snd_sst_params *str_params, struct sst_compress_cb *cb)
+{
+	int str_id, retval;
+	struct stream_info *stream;
+	struct intel_sst_drv *ctx = dev_get_drvdata(dev);
+
+	retval = pm_runtime_get_sync(ctx->dev);
+	if (retval < 0)
+		return retval;
+
+	str_id = sst_get_stream(ctx, str_params);
+	if (str_id > 0) {
+		dev_dbg(dev, "stream allocated in sst_cdev_open %d\n", str_id);
+		stream = &ctx->streams[str_id];
+		stream->compr_cb = cb->compr_cb;
+		stream->compr_cb_param = cb->param;
+		stream->drain_notify = cb->drain_notify;
+		stream->drain_cb_param = cb->drain_cb_param;
+	} else {
+		dev_err(dev, "stream encountered error during alloc %d\n", str_id);
+		str_id = -EINVAL;
+		sst_pm_runtime_put(ctx);
+	}
+	return str_id;
+}
+
+static int sst_cdev_close(struct device *dev, unsigned int str_id)
+{
+	int retval;
+	struct stream_info *stream;
+	struct intel_sst_drv *ctx = dev_get_drvdata(dev);
+
+	stream = get_stream_info(ctx, str_id);
+	if (!stream) {
+		dev_err(dev, "stream info is NULL for str %d!!!\n", str_id);
+		return -EINVAL;
+	}
+
+	if (stream->status == STREAM_RESET) {
+		dev_dbg(dev, "stream in reset state...\n");
+		stream->status = STREAM_UN_INIT;
+
+		retval = 0;
+		goto put;
+	}
+
+	retval = sst_free_stream(ctx, str_id);
+put:
+	stream->compr_cb_param = NULL;
+	stream->compr_cb = NULL;
+
+	if (retval)
+		dev_err(dev, "free stream returned err %d\n", retval);
+
+	dev_dbg(dev, "End\n");
+	return retval;
+
+}
+
+static int sst_cdev_ack(struct device *dev, unsigned int str_id,
+		unsigned long bytes)
+{
+	struct stream_info *stream;
+	struct snd_sst_tstamp fw_tstamp = {0,};
+	int offset;
+	void __iomem *addr;
+	struct intel_sst_drv *ctx = dev_get_drvdata(dev);
+
+	stream = get_stream_info(ctx, str_id);
+	if (!stream)
+		return -EINVAL;
+
+	/* update bytes sent */
+	stream->cumm_bytes += bytes;
+	dev_dbg(dev, "bytes copied %d inc by %ld\n", stream->cumm_bytes, bytes);
+
+	memcpy_fromio(&fw_tstamp,
+		((void *)(ctx->mailbox + ctx->tstamp)
+		+(str_id * sizeof(fw_tstamp))),
+		sizeof(fw_tstamp));
+
+	fw_tstamp.bytes_copied = stream->cumm_bytes;
+	dev_dbg(dev, "bytes sent to fw %llu inc by %ld\n",
+			fw_tstamp.bytes_copied, bytes);
+
+	addr =  ((void *)(ctx->mailbox + ctx->tstamp)) +
+			(str_id * sizeof(fw_tstamp));
+	offset =  offsetof(struct snd_sst_tstamp, bytes_copied);
+	sst_shim_write(addr, offset, fw_tstamp.bytes_copied);
+	return 0;
+}
+
+static int sst_cdev_set_metadata(struct device *dev,
+		unsigned int str_id, struct snd_compr_metadata *metadata)
+{
+	int retval = 0;
+	struct stream_info *str_info;
+	struct intel_sst_drv *ctx = dev_get_drvdata(dev);
+
+	dev_dbg(dev, "set metadata for stream %d\n", str_id);
+
+	str_info = get_stream_info(ctx, str_id);
+	if (!str_info)
+		return -EINVAL;
+
+	dev_dbg(dev, "pipe id = %d\n", str_info->pipe_id);
+	retval = sst_prepare_and_post_msg(ctx, str_info->task_id, IPC_CMD,
+			IPC_IA_SET_STREAM_PARAMS_MRFLD, str_info->pipe_id,
+			sizeof(*metadata), metadata, NULL,
+			true, true, true, false);
+
+	return retval;
+}
+
+static int sst_cdev_stream_pause(struct device *dev, unsigned int str_id)
+{
+	struct intel_sst_drv *ctx = dev_get_drvdata(dev);
+
+	return sst_pause_stream(ctx, str_id);
+}
+
+static int sst_cdev_stream_pause_release(struct device *dev,
+		unsigned int str_id)
+{
+	struct intel_sst_drv *ctx = dev_get_drvdata(dev);
+
+	return sst_resume_stream(ctx, str_id);
+}
+
+static int sst_cdev_stream_start(struct device *dev, unsigned int str_id)
+{
+	struct stream_info *str_info;
+	struct intel_sst_drv *ctx = dev_get_drvdata(dev);
+
+	str_info = get_stream_info(ctx, str_id);
+	if (!str_info)
+		return -EINVAL;
+	str_info->prev = str_info->status;
+	str_info->status = STREAM_RUNNING;
+	return sst_start_stream(ctx, str_id);
+}
+
+static int sst_cdev_stream_drop(struct device *dev, unsigned int str_id)
+{
+	struct intel_sst_drv *ctx = dev_get_drvdata(dev);
+
+	return sst_drop_stream(ctx, str_id);
+}
+
+static int sst_cdev_stream_drain(struct device *dev, unsigned int str_id)
+{
+	struct intel_sst_drv *ctx = dev_get_drvdata(dev);
+
+	return sst_drain_stream(ctx, str_id, false);
+}
+
+static int sst_cdev_stream_partial_drain(struct device *dev,
+		unsigned int str_id)
+{
+	struct intel_sst_drv *ctx = dev_get_drvdata(dev);
+
+	return sst_drain_stream(ctx, str_id, true);
+}
+
+static int sst_cdev_tstamp(struct device *dev, unsigned int str_id,
+		struct snd_compr_tstamp *tstamp)
+{
+	struct snd_sst_tstamp fw_tstamp = {0,};
+	struct stream_info *stream;
+	struct intel_sst_drv *ctx = dev_get_drvdata(dev);
+
+	memcpy_fromio(&fw_tstamp,
+		((void *)(ctx->mailbox + ctx->tstamp)
+		+(str_id * sizeof(fw_tstamp))),
+		sizeof(fw_tstamp));
+
+	stream = get_stream_info(ctx, str_id);
+	if (!stream)
+		return -EINVAL;
+	dev_dbg(dev, "rb_counter %llu in bytes\n", fw_tstamp.ring_buffer_counter);
+
+	tstamp->copied_total = fw_tstamp.ring_buffer_counter;
+	tstamp->pcm_frames = fw_tstamp.frames_decoded;
+	tstamp->pcm_io_frames = div_u64(fw_tstamp.hardware_counter,
+			(u64)((stream->num_ch) * SST_GET_BYTES_PER_SAMPLE(24)));
+	tstamp->sampling_rate = fw_tstamp.sampling_frequency;
+
+	dev_dbg(dev, "PCM  = %u\n", tstamp->pcm_io_frames);
+	dev_dbg(dev, "Ptr Query on strid = %d  copied_total %d, decodec %d\n",
+		str_id, tstamp->copied_total, tstamp->pcm_frames);
+	dev_dbg(dev, "rendered %d\n", tstamp->pcm_io_frames);
+
+	return 0;
+}
+
+static int sst_cdev_caps(struct snd_compr_caps *caps)
+{
+	caps->num_codecs = NUM_CODEC;
+	caps->min_fragment_size = MIN_FRAGMENT_SIZE;  /* 50KB */
+	caps->max_fragment_size = MAX_FRAGMENT_SIZE;  /* 1024KB */
+	caps->min_fragments = MIN_FRAGMENT;
+	caps->max_fragments = MAX_FRAGMENT;
+	caps->codecs[0] = SND_AUDIOCODEC_MP3;
+	caps->codecs[1] = SND_AUDIOCODEC_AAC;
+	return 0;
+}
+
+static struct snd_compr_codec_caps caps_mp3 = {
+	.num_descriptors = 1,
+	.descriptor[0].max_ch = 2,
+	.descriptor[0].sample_rates[0] = 48000,
+	.descriptor[0].sample_rates[1] = 44100,
+	.descriptor[0].sample_rates[2] = 32000,
+	.descriptor[0].sample_rates[3] = 16000,
+	.descriptor[0].sample_rates[4] = 8000,
+	.descriptor[0].num_sample_rates = 5,
+	.descriptor[0].bit_rate[0] = 320,
+	.descriptor[0].bit_rate[1] = 192,
+	.descriptor[0].num_bitrates = 2,
+	.descriptor[0].profiles = 0,
+	.descriptor[0].modes = SND_AUDIOCHANMODE_MP3_STEREO,
+	.descriptor[0].formats = 0,
+};
+
+static struct snd_compr_codec_caps caps_aac = {
+	.num_descriptors = 2,
+	.descriptor[1].max_ch = 2,
+	.descriptor[0].sample_rates[0] = 48000,
+	.descriptor[0].sample_rates[1] = 44100,
+	.descriptor[0].sample_rates[2] = 32000,
+	.descriptor[0].sample_rates[3] = 16000,
+	.descriptor[0].sample_rates[4] = 8000,
+	.descriptor[0].num_sample_rates = 5,
+	.descriptor[1].bit_rate[0] = 320,
+	.descriptor[1].bit_rate[1] = 192,
+	.descriptor[1].num_bitrates = 2,
+	.descriptor[1].profiles = 0,
+	.descriptor[1].modes = 0,
+	.descriptor[1].formats =
+			(SND_AUDIOSTREAMFORMAT_MP4ADTS |
+				SND_AUDIOSTREAMFORMAT_RAW),
+};
+
+static int sst_cdev_codec_caps(struct snd_compr_codec_caps *codec)
+{
+	if (codec->codec == SND_AUDIOCODEC_MP3)
+		*codec = caps_mp3;
+	else if (codec->codec == SND_AUDIOCODEC_AAC)
+		*codec = caps_aac;
+	else
+		return -EINVAL;
+
+	return 0;
+}
+
+void sst_cdev_fragment_elapsed(struct intel_sst_drv *ctx, int str_id)
+{
+	struct stream_info *stream;
+
+	dev_dbg(ctx->dev, "fragment elapsed from firmware for str_id %d\n",
+			str_id);
+	stream = &ctx->streams[str_id];
+	if (stream->compr_cb)
+		stream->compr_cb(stream->compr_cb_param);
+}
+
+/*
+ * sst_close_pcm_stream - Close PCM interface
+ *
+ * @str_id: stream id to be closed
+ *
+ * This function is called by MID sound card driver to close
+ * an existing pcm interface
+ */
+static int sst_close_pcm_stream(struct device *dev, unsigned int str_id)
+{
+	struct stream_info *stream;
+	int retval = 0;
+	struct intel_sst_drv *ctx = dev_get_drvdata(dev);
+
+	stream = get_stream_info(ctx, str_id);
+	if (!stream) {
+		dev_err(ctx->dev, "stream info is NULL for str %d!!!\n", str_id);
+		return -EINVAL;
+	}
+
+	if (stream->status == STREAM_RESET) {
+		/* silently fail here as we have cleaned the stream earlier */
+		dev_dbg(ctx->dev, "stream in reset state...\n");
+
+		retval = 0;
+		goto put;
+	}
+
+	retval = free_stream_context(ctx, str_id);
+put:
+	stream->pcm_substream = NULL;
+	stream->status = STREAM_UN_INIT;
+	stream->period_elapsed = NULL;
+	ctx->stream_cnt--;
+
+	if (retval)
+		dev_err(ctx->dev, "free stream returned err %d\n", retval);
+
+	dev_dbg(ctx->dev, "Exit\n");
+	return 0;
+}
+
+static inline int sst_calc_tstamp(struct intel_sst_drv *ctx,
+		struct pcm_stream_info *info,
+		struct snd_pcm_substream *substream,
+		struct snd_sst_tstamp *fw_tstamp)
+{
+	size_t delay_bytes, delay_frames;
+	size_t buffer_sz;
+	u32 pointer_bytes, pointer_samples;
+
+	dev_dbg(ctx->dev, "mrfld ring_buffer_counter %llu in bytes\n",
+			fw_tstamp->ring_buffer_counter);
+	dev_dbg(ctx->dev, "mrfld hardware_counter %llu in bytes\n",
+			 fw_tstamp->hardware_counter);
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+		delay_bytes = (size_t) (fw_tstamp->ring_buffer_counter -
+					fw_tstamp->hardware_counter);
+	else
+		delay_bytes = (size_t) (fw_tstamp->hardware_counter -
+					fw_tstamp->ring_buffer_counter);
+	delay_frames = bytes_to_frames(substream->runtime, delay_bytes);
+	buffer_sz = snd_pcm_lib_buffer_bytes(substream);
+	div_u64_rem(fw_tstamp->ring_buffer_counter, buffer_sz, &pointer_bytes);
+	pointer_samples = bytes_to_samples(substream->runtime, pointer_bytes);
+
+	dev_dbg(ctx->dev, "pcm delay %zu in bytes\n", delay_bytes);
+
+	info->buffer_ptr = pointer_samples / substream->runtime->channels;
+
+	info->pcm_delay = delay_frames / substream->runtime->channels;
+	dev_dbg(ctx->dev, "buffer ptr %llu pcm_delay rep: %llu\n",
+			info->buffer_ptr, info->pcm_delay);
+	return 0;
+}
+
+static int sst_read_timestamp(struct device *dev, struct pcm_stream_info *info)
+{
+	struct stream_info *stream;
+	struct snd_pcm_substream *substream;
+	struct snd_sst_tstamp fw_tstamp;
+	unsigned int str_id;
+	struct intel_sst_drv *ctx = dev_get_drvdata(dev);
+
+	str_id = info->str_id;
+	stream = get_stream_info(ctx, str_id);
+	if (!stream)
+		return -EINVAL;
+
+	if (!stream->pcm_substream)
+		return -EINVAL;
+	substream = stream->pcm_substream;
+
+	memcpy_fromio(&fw_tstamp,
+		((void *)(ctx->mailbox + ctx->tstamp)
+			+ (str_id * sizeof(fw_tstamp))),
+		sizeof(fw_tstamp));
+	return sst_calc_tstamp(ctx, info, substream, &fw_tstamp);
+}
+
+static int sst_stream_start(struct device *dev, int str_id)
+{
+	struct stream_info *str_info;
+	struct intel_sst_drv *ctx = dev_get_drvdata(dev);
+
+	if (ctx->sst_state != SST_FW_RUNNING)
+		return 0;
+	str_info = get_stream_info(ctx, str_id);
+	if (!str_info)
+		return -EINVAL;
+	str_info->prev = str_info->status;
+	str_info->status = STREAM_RUNNING;
+	sst_start_stream(ctx, str_id);
+
+	return 0;
+}
+
+static int sst_stream_drop(struct device *dev, int str_id)
+{
+	struct stream_info *str_info;
+	struct intel_sst_drv *ctx = dev_get_drvdata(dev);
+
+	if (ctx->sst_state != SST_FW_RUNNING)
+		return 0;
+
+	str_info = get_stream_info(ctx, str_id);
+	if (!str_info)
+		return -EINVAL;
+	str_info->prev = STREAM_UN_INIT;
+	str_info->status = STREAM_INIT;
+	return sst_drop_stream(ctx, str_id);
+}
+
+static int sst_stream_init(struct device *dev, struct pcm_stream_info *str_info)
+{
+	int str_id = 0;
+	struct stream_info *stream;
+	struct intel_sst_drv *ctx = dev_get_drvdata(dev);
+
+	str_id = str_info->str_id;
+
+	if (ctx->sst_state != SST_FW_RUNNING)
+		return 0;
+
+	stream = get_stream_info(ctx, str_id);
+	if (!stream)
+		return -EINVAL;
+
+	dev_dbg(ctx->dev, "setting the period ptrs\n");
+	stream->pcm_substream = str_info->arg;
+	stream->period_elapsed = str_info->period_elapsed;
+	stream->sfreq = str_info->sfreq;
+	stream->prev = stream->status;
+	stream->status = STREAM_INIT;
+	dev_dbg(ctx->dev,
+		"pcm_substream %p, period_elapsed %p, sfreq %d, status %d\n",
+		stream->pcm_substream, stream->period_elapsed,
+		stream->sfreq, stream->status);
+
+	return 0;
+}
+
+/*
+ * sst_set_byte_stream - Set generic params
+ *
+ * @cmd: control cmd to be set
+ * @arg: command argument
+ *
+ * This function is called by MID sound card driver to configure
+ * SST runtime params.
+ */
+static int sst_send_byte_stream(struct device *dev,
+		struct snd_sst_bytes_v2 *bytes)
+{
+	int ret_val = 0;
+	struct intel_sst_drv *ctx = dev_get_drvdata(dev);
+
+	if (NULL == bytes)
+		return -EINVAL;
+	ret_val = pm_runtime_get_sync(ctx->dev);
+	if (ret_val < 0)
+		return ret_val;
+
+	ret_val = sst_send_byte_stream_mrfld(ctx, bytes);
+	sst_pm_runtime_put(ctx);
+
+	return ret_val;
+}
+
+static struct sst_ops pcm_ops = {
+	.open = sst_open_pcm_stream,
+	.stream_init = sst_stream_init,
+	.stream_start = sst_stream_start,
+	.stream_drop = sst_stream_drop,
+	.stream_read_tstamp = sst_read_timestamp,
+	.send_byte_stream = sst_send_byte_stream,
+	.close = sst_close_pcm_stream,
+	.power = sst_power_control,
+};
+
+static struct compress_sst_ops compr_ops = {
+	.open = sst_cdev_open,
+	.close = sst_cdev_close,
+	.stream_pause = sst_cdev_stream_pause,
+	.stream_pause_release = sst_cdev_stream_pause_release,
+	.stream_start = sst_cdev_stream_start,
+	.stream_drop = sst_cdev_stream_drop,
+	.stream_drain = sst_cdev_stream_drain,
+	.stream_partial_drain = sst_cdev_stream_partial_drain,
+	.tstamp = sst_cdev_tstamp,
+	.ack = sst_cdev_ack,
+	.get_caps = sst_cdev_caps,
+	.get_codec_caps = sst_cdev_codec_caps,
+	.set_metadata = sst_cdev_set_metadata,
+	.power = sst_power_control,
+};
+
+static struct sst_device sst_dsp_device = {
+	.name = "Intel(R) SST LPE",
+	.dev = NULL,
+	.ops = &pcm_ops,
+	.compr_ops = &compr_ops,
+};
+
+/*
+ * sst_register - function to register DSP
+ *
+ * This functions registers DSP with the platform driver
+ */
+int sst_register(struct device *dev)
+{
+	int ret_val;
+
+	sst_dsp_device.dev = dev;
+	ret_val = sst_register_dsp(&sst_dsp_device);
+	if (ret_val)
+		dev_err(dev, "Unable to register DSP with platform driver\n");
+
+	return ret_val;
+}
+
+int sst_unregister(struct device *dev)
+{
+	return sst_unregister_dsp(&sst_dsp_device);
+}
diff --git a/sound/soc/intel/sst/sst_ipc.c b/sound/soc/intel/sst/sst_ipc.c
new file mode 100644
index 000000000000..484e60978477
--- /dev/null
+++ b/sound/soc/intel/sst/sst_ipc.c
@@ -0,0 +1,373 @@
+/*
+ *  sst_ipc.c - Intel SST Driver for audio engine
+ *
+ *  Copyright (C) 2008-14 Intel Corporation
+ *  Authors:	Vinod Koul <vinod.koul@intel.com>
+ *		Harsha Priya <priya.harsha@intel.com>
+ *		Dharageswari R <dharageswari.r@intel.com>
+ *		KP Jeeja <jeeja.kp@intel.com>
+ *  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; version 2 of the License.
+ *
+ *  This program is distributed in the hope that it will be useful, but
+ *  WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  General Public License for more details.
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+#include <linux/pci.h>
+#include <linux/firmware.h>
+#include <linux/sched.h>
+#include <linux/delay.h>
+#include <linux/pm_runtime.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+#include <sound/compress_driver.h>
+#include <asm/intel-mid.h>
+#include <asm/platform_sst_audio.h>
+#include "../sst-mfld-platform.h"
+#include "sst.h"
+#include "../sst-dsp.h"
+
+struct sst_block *sst_create_block(struct intel_sst_drv *ctx,
+					u32 msg_id, u32 drv_id)
+{
+	struct sst_block *msg = NULL;
+
+	dev_dbg(ctx->dev, "Enter\n");
+	msg = kzalloc(sizeof(*msg), GFP_KERNEL);
+	if (!msg)
+		return NULL;
+	msg->condition = false;
+	msg->on = true;
+	msg->msg_id = msg_id;
+	msg->drv_id = drv_id;
+	spin_lock_bh(&ctx->block_lock);
+	list_add_tail(&msg->node, &ctx->block_list);
+	spin_unlock_bh(&ctx->block_lock);
+
+	return msg;
+}
+
+/*
+ * while handling the interrupts, we need to check for message status and
+ * then if we are blocking for a message
+ *
+ * here we are unblocking the blocked ones, this is based on id we have
+ * passed and search that for block threads.
+ * We will not find block in two cases
+ *  a) when its small message and block in not there, so silently ignore
+ *  them
+ *  b) when we are actually not able to find the block (bug perhaps)
+ *
+ *  Since we have bit of small messages we can spam kernel log with err
+ *  print on above so need to keep as debug prints which should be enabled
+ *  via dynamic debug while debugging IPC issues
+ */
+int sst_wake_up_block(struct intel_sst_drv *ctx, int result,
+		u32 drv_id, u32 ipc, void *data, u32 size)
+{
+	struct sst_block *block = NULL;
+
+	dev_dbg(ctx->dev, "Enter\n");
+
+	spin_lock_bh(&ctx->block_lock);
+	list_for_each_entry(block, &ctx->block_list, node) {
+		dev_dbg(ctx->dev, "Block ipc %d, drv_id %d\n", block->msg_id,
+							block->drv_id);
+		if (block->msg_id == ipc && block->drv_id == drv_id) {
+			dev_dbg(ctx->dev, "free up the block\n");
+			block->ret_code = result;
+			block->data = data;
+			block->size = size;
+			block->condition = true;
+			spin_unlock_bh(&ctx->block_lock);
+			wake_up(&ctx->wait_queue);
+			return 0;
+		}
+	}
+	spin_unlock_bh(&ctx->block_lock);
+	dev_dbg(ctx->dev,
+		"Block not found or a response received for a short msg for ipc %d, drv_id %d\n",
+		ipc, drv_id);
+	return -EINVAL;
+}
+
+int sst_free_block(struct intel_sst_drv *ctx, struct sst_block *freed)
+{
+	struct sst_block *block = NULL, *__block;
+
+	dev_dbg(ctx->dev, "Enter\n");
+	spin_lock_bh(&ctx->block_lock);
+	list_for_each_entry_safe(block, __block, &ctx->block_list, node) {
+		if (block == freed) {
+			pr_debug("pvt_id freed --> %d\n", freed->drv_id);
+			/* toggle the index position of pvt_id */
+			list_del(&freed->node);
+			spin_unlock_bh(&ctx->block_lock);
+			kfree(freed->data);
+			freed->data = NULL;
+			kfree(freed);
+			return 0;
+		}
+	}
+	spin_unlock_bh(&ctx->block_lock);
+	dev_err(ctx->dev, "block is already freed!!!\n");
+	return -EINVAL;
+}
+
+int sst_post_message_mrfld(struct intel_sst_drv *sst_drv_ctx,
+		struct ipc_post *ipc_msg, bool sync)
+{
+	struct ipc_post *msg = ipc_msg;
+	union ipc_header_mrfld header;
+	unsigned int loop_count = 0;
+	int retval = 0;
+	unsigned long irq_flags;
+
+	dev_dbg(sst_drv_ctx->dev, "Enter: sync: %d\n", sync);
+	spin_lock_irqsave(&sst_drv_ctx->ipc_spin_lock, irq_flags);
+	header.full = sst_shim_read64(sst_drv_ctx->shim, SST_IPCX);
+	if (sync) {
+		while (header.p.header_high.part.busy) {
+			if (loop_count > 25) {
+				dev_err(sst_drv_ctx->dev,
+					"sst: Busy wait failed, cant send this msg\n");
+				retval = -EBUSY;
+				goto out;
+			}
+			cpu_relax();
+			loop_count++;
+			header.full = sst_shim_read64(sst_drv_ctx->shim, SST_IPCX);
+		}
+	} else {
+		if (list_empty(&sst_drv_ctx->ipc_dispatch_list)) {
+			/* queue is empty, nothing to send */
+			spin_unlock_irqrestore(&sst_drv_ctx->ipc_spin_lock, irq_flags);
+			dev_dbg(sst_drv_ctx->dev,
+					"Empty msg queue... NO Action\n");
+			return 0;
+		}
+
+		if (header.p.header_high.part.busy) {
+			spin_unlock_irqrestore(&sst_drv_ctx->ipc_spin_lock, irq_flags);
+			dev_dbg(sst_drv_ctx->dev, "Busy not free... post later\n");
+			return 0;
+		}
+
+		/* copy msg from list */
+		msg = list_entry(sst_drv_ctx->ipc_dispatch_list.next,
+				struct ipc_post, node);
+		list_del(&msg->node);
+	}
+	dev_dbg(sst_drv_ctx->dev, "sst: Post message: header = %x\n",
+				msg->mrfld_header.p.header_high.full);
+	dev_dbg(sst_drv_ctx->dev, "sst: size = 0x%x\n",
+			msg->mrfld_header.p.header_low_payload);
+
+	if (msg->mrfld_header.p.header_high.part.large)
+		memcpy_toio(sst_drv_ctx->mailbox + SST_MAILBOX_SEND,
+			msg->mailbox_data,
+			msg->mrfld_header.p.header_low_payload);
+
+	sst_shim_write64(sst_drv_ctx->shim, SST_IPCX, msg->mrfld_header.full);
+
+out:
+	spin_unlock_irqrestore(&sst_drv_ctx->ipc_spin_lock, irq_flags);
+	kfree(msg->mailbox_data);
+	kfree(msg);
+	return retval;
+}
+
+void intel_sst_clear_intr_mrfld(struct intel_sst_drv *sst_drv_ctx)
+{
+	union interrupt_reg_mrfld isr;
+	union interrupt_reg_mrfld imr;
+	union ipc_header_mrfld clear_ipc;
+	unsigned long irq_flags;
+
+	spin_lock_irqsave(&sst_drv_ctx->ipc_spin_lock, irq_flags);
+	imr.full = sst_shim_read64(sst_drv_ctx->shim, SST_IMRX);
+	isr.full = sst_shim_read64(sst_drv_ctx->shim, SST_ISRX);
+
+	/* write 1 to clear*/
+	isr.part.busy_interrupt = 1;
+	sst_shim_write64(sst_drv_ctx->shim, SST_ISRX, isr.full);
+
+	/* Set IA done bit */
+	clear_ipc.full = sst_shim_read64(sst_drv_ctx->shim, SST_IPCD);
+
+	clear_ipc.p.header_high.part.busy = 0;
+	clear_ipc.p.header_high.part.done = 1;
+	clear_ipc.p.header_low_payload = IPC_ACK_SUCCESS;
+	sst_shim_write64(sst_drv_ctx->shim, SST_IPCD, clear_ipc.full);
+	/* un mask busy interrupt */
+	imr.part.busy_interrupt = 0;
+	sst_shim_write64(sst_drv_ctx->shim, SST_IMRX, imr.full);
+	spin_unlock_irqrestore(&sst_drv_ctx->ipc_spin_lock, irq_flags);
+}
+
+
+/*
+ * process_fw_init - process the FW init msg
+ *
+ * @msg: IPC message mailbox data from FW
+ *
+ * This function processes the FW init msg from FW
+ * marks FW state and prints debug info of loaded FW
+ */
+static void process_fw_init(struct intel_sst_drv *sst_drv_ctx,
+			void *msg)
+{
+	struct ipc_header_fw_init *init =
+		(struct ipc_header_fw_init *)msg;
+	int retval = 0;
+
+	dev_dbg(sst_drv_ctx->dev, "*** FW Init msg came***\n");
+	if (init->result) {
+		sst_set_fw_state_locked(sst_drv_ctx, SST_RESET);
+		dev_err(sst_drv_ctx->dev, "FW Init failed, Error %x\n",
+				init->result);
+		retval = init->result;
+		goto ret;
+	}
+
+ret:
+	sst_wake_up_block(sst_drv_ctx, retval, FW_DWNL_ID, 0 , NULL, 0);
+}
+
+static void process_fw_async_msg(struct intel_sst_drv *sst_drv_ctx,
+			struct ipc_post *msg)
+{
+	u32 msg_id;
+	int str_id;
+	u32 data_size, i;
+	void *data_offset;
+	struct stream_info *stream;
+	union ipc_header_high msg_high;
+	u32 msg_low, pipe_id;
+
+	msg_high = msg->mrfld_header.p.header_high;
+	msg_low = msg->mrfld_header.p.header_low_payload;
+	msg_id = ((struct ipc_dsp_hdr *)msg->mailbox_data)->cmd_id;
+	data_offset = (msg->mailbox_data + sizeof(struct ipc_dsp_hdr));
+	data_size =  msg_low - (sizeof(struct ipc_dsp_hdr));
+
+	switch (msg_id) {
+	case IPC_SST_PERIOD_ELAPSED_MRFLD:
+		pipe_id = ((struct ipc_dsp_hdr *)msg->mailbox_data)->pipe_id;
+		str_id = get_stream_id_mrfld(sst_drv_ctx, pipe_id);
+		if (str_id > 0) {
+			dev_dbg(sst_drv_ctx->dev,
+				"Period elapsed rcvd for pipe id 0x%x\n",
+				pipe_id);
+			stream = &sst_drv_ctx->streams[str_id];
+			if (stream->period_elapsed)
+				stream->period_elapsed(stream->pcm_substream);
+			if (stream->compr_cb)
+				stream->compr_cb(stream->compr_cb_param);
+		}
+		break;
+
+	case IPC_IA_DRAIN_STREAM_MRFLD:
+		pipe_id = ((struct ipc_dsp_hdr *)msg->mailbox_data)->pipe_id;
+		str_id = get_stream_id_mrfld(sst_drv_ctx, pipe_id);
+		if (str_id > 0) {
+			stream = &sst_drv_ctx->streams[str_id];
+			if (stream->drain_notify)
+				stream->drain_notify(stream->drain_cb_param);
+		}
+		break;
+
+	case IPC_IA_FW_ASYNC_ERR_MRFLD:
+		dev_err(sst_drv_ctx->dev, "FW sent async error msg:\n");
+		for (i = 0; i < (data_size/4); i++)
+			print_hex_dump(KERN_DEBUG, NULL, DUMP_PREFIX_NONE,
+					16, 4, data_offset, data_size, false);
+		break;
+
+	case IPC_IA_FW_INIT_CMPLT_MRFLD:
+		process_fw_init(sst_drv_ctx, data_offset);
+		break;
+
+	case IPC_IA_BUF_UNDER_RUN_MRFLD:
+		pipe_id = ((struct ipc_dsp_hdr *)msg->mailbox_data)->pipe_id;
+		str_id = get_stream_id_mrfld(sst_drv_ctx, pipe_id);
+		if (str_id > 0)
+			dev_err(sst_drv_ctx->dev,
+				"Buffer under-run for pipe:%#x str_id:%d\n",
+				pipe_id, str_id);
+		break;
+
+	default:
+		dev_err(sst_drv_ctx->dev,
+			"Unrecognized async msg from FW msg_id %#x\n", msg_id);
+	}
+}
+
+void sst_process_reply_mrfld(struct intel_sst_drv *sst_drv_ctx,
+		struct ipc_post *msg)
+{
+	unsigned int drv_id;
+	void *data;
+	union ipc_header_high msg_high;
+	u32 msg_low;
+	struct ipc_dsp_hdr *dsp_hdr;
+	unsigned int cmd_id;
+
+	msg_high = msg->mrfld_header.p.header_high;
+	msg_low = msg->mrfld_header.p.header_low_payload;
+
+	dev_dbg(sst_drv_ctx->dev, "IPC process message header %x payload %x\n",
+			msg->mrfld_header.p.header_high.full,
+			msg->mrfld_header.p.header_low_payload);
+
+	drv_id = msg_high.part.drv_id;
+
+	/* Check for async messages first */
+	if (drv_id == SST_ASYNC_DRV_ID) {
+		/*FW sent async large message*/
+		process_fw_async_msg(sst_drv_ctx, msg);
+		return;
+	}
+
+	/* FW sent short error response for an IPC */
+	if (msg_high.part.result && drv_id && !msg_high.part.large) {
+		/* 32-bit FW error code in msg_low */
+		dev_err(sst_drv_ctx->dev, "FW sent error response 0x%x", msg_low);
+		sst_wake_up_block(sst_drv_ctx, msg_high.part.result,
+			msg_high.part.drv_id,
+			msg_high.part.msg_id, NULL, 0);
+		return;
+	}
+
+	/*
+	 * Process all valid responses
+	 * if it is a large message, the payload contains the size to
+	 * copy from mailbox
+	 **/
+	if (msg_high.part.large) {
+		data = kzalloc(msg_low, GFP_KERNEL);
+		if (!data)
+			return;
+		memcpy(data, (void *) msg->mailbox_data, msg_low);
+		/* Copy command id so that we can use to put sst to reset */
+		dsp_hdr = (struct ipc_dsp_hdr *)data;
+		cmd_id = dsp_hdr->cmd_id;
+		dev_dbg(sst_drv_ctx->dev, "cmd_id %d\n", dsp_hdr->cmd_id);
+		if (sst_wake_up_block(sst_drv_ctx, msg_high.part.result,
+				msg_high.part.drv_id,
+				msg_high.part.msg_id, data, msg_low))
+			kfree(data);
+	} else {
+		sst_wake_up_block(sst_drv_ctx, msg_high.part.result,
+				msg_high.part.drv_id,
+				msg_high.part.msg_id, NULL, 0);
+	}
+
+}
diff --git a/sound/soc/intel/sst/sst_loader.c b/sound/soc/intel/sst/sst_loader.c
new file mode 100644
index 000000000000..b580f96e25e5
--- /dev/null
+++ b/sound/soc/intel/sst/sst_loader.c
@@ -0,0 +1,456 @@
+/*
+ *  sst_dsp.c - Intel SST Driver for audio engine
+ *
+ *  Copyright (C) 2008-14	Intel Corp
+ *  Authors:	Vinod Koul <vinod.koul@intel.com>
+ *		Harsha Priya <priya.harsha@intel.com>
+ *		Dharageswari R <dharageswari.r@intel.com>
+ *		KP Jeeja <jeeja.kp@intel.com>
+ *  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; version 2 of the License.
+ *
+ *  This program is distributed in the hope that it will be useful, but
+ *  WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  General Public License for more details.
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ *  This file contains all dsp controlling functions like firmware download,
+ * setting/resetting dsp cores, etc
+ */
+#include <linux/pci.h>
+#include <linux/delay.h>
+#include <linux/fs.h>
+#include <linux/sched.h>
+#include <linux/firmware.h>
+#include <linux/dmaengine.h>
+#include <linux/pm_runtime.h>
+#include <linux/pm_qos.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+#include <sound/compress_driver.h>
+#include <asm/platform_sst_audio.h>
+#include "../sst-mfld-platform.h"
+#include "sst.h"
+#include "../sst-dsp.h"
+
+static inline void memcpy32_toio(void __iomem *dst, const void *src, int count)
+{
+	/* __iowrite32_copy uses 32-bit count values so divide by 4 for
+	 * right count in words
+	 */
+	__iowrite32_copy(dst, src, count/4);
+}
+
+/**
+ * intel_sst_reset_dsp_mrfld - Resetting SST DSP
+ *
+ * This resets DSP in case of MRFLD platfroms
+ */
+int intel_sst_reset_dsp_mrfld(struct intel_sst_drv *sst_drv_ctx)
+{
+	union config_status_reg_mrfld csr;
+
+	dev_dbg(sst_drv_ctx->dev, "sst: Resetting the DSP in mrfld\n");
+	csr.full = sst_shim_read64(sst_drv_ctx->shim, SST_CSR);
+
+	dev_dbg(sst_drv_ctx->dev, "value:0x%llx\n", csr.full);
+
+	csr.full |= 0x7;
+	sst_shim_write64(sst_drv_ctx->shim, SST_CSR, csr.full);
+	csr.full = sst_shim_read64(sst_drv_ctx->shim, SST_CSR);
+
+	dev_dbg(sst_drv_ctx->dev, "value:0x%llx\n", csr.full);
+
+	csr.full &= ~(0x1);
+	sst_shim_write64(sst_drv_ctx->shim, SST_CSR, csr.full);
+
+	csr.full = sst_shim_read64(sst_drv_ctx->shim, SST_CSR);
+	dev_dbg(sst_drv_ctx->dev, "value:0x%llx\n", csr.full);
+	return 0;
+}
+
+/**
+ * sst_start_merrifield - Start the SST DSP processor
+ *
+ * This starts the DSP in MERRIFIELD platfroms
+ */
+int sst_start_mrfld(struct intel_sst_drv *sst_drv_ctx)
+{
+	union config_status_reg_mrfld csr;
+
+	dev_dbg(sst_drv_ctx->dev, "sst: Starting the DSP in mrfld LALALALA\n");
+	csr.full = sst_shim_read64(sst_drv_ctx->shim, SST_CSR);
+	dev_dbg(sst_drv_ctx->dev, "value:0x%llx\n", csr.full);
+
+	csr.full |= 0x7;
+	sst_shim_write64(sst_drv_ctx->shim, SST_CSR, csr.full);
+
+	csr.full = sst_shim_read64(sst_drv_ctx->shim, SST_CSR);
+	dev_dbg(sst_drv_ctx->dev, "value:0x%llx\n", csr.full);
+
+	csr.part.xt_snoop = 1;
+	csr.full &= ~(0x5);
+	sst_shim_write64(sst_drv_ctx->shim, SST_CSR, csr.full);
+
+	csr.full = sst_shim_read64(sst_drv_ctx->shim, SST_CSR);
+	dev_dbg(sst_drv_ctx->dev, "sst: Starting the DSP_merrifield:%llx\n",
+			csr.full);
+	return 0;
+}
+
+static int sst_validate_fw_image(struct intel_sst_drv *ctx, unsigned long size,
+		struct fw_module_header **module, u32 *num_modules)
+{
+	struct sst_fw_header *header;
+	const void *sst_fw_in_mem = ctx->fw_in_mem;
+
+	dev_dbg(ctx->dev, "Enter\n");
+
+	/* Read the header information from the data pointer */
+	header = (struct sst_fw_header *)sst_fw_in_mem;
+	dev_dbg(ctx->dev,
+		"header sign=%s size=%x modules=%x fmt=%x size=%zx\n",
+		header->signature, header->file_size, header->modules,
+		header->file_format, sizeof(*header));
+
+	/* verify FW */
+	if ((strncmp(header->signature, SST_FW_SIGN, 4) != 0) ||
+		(size != header->file_size + sizeof(*header))) {
+		/* Invalid FW signature */
+		dev_err(ctx->dev, "InvalidFW sign/filesize mismatch\n");
+		return -EINVAL;
+	}
+	*num_modules = header->modules;
+	*module = (void *)sst_fw_in_mem + sizeof(*header);
+
+	return 0;
+}
+
+/*
+ * sst_fill_memcpy_list - Fill the memcpy list
+ *
+ * @memcpy_list: List to be filled
+ * @destn: Destination addr to be filled in the list
+ * @src: Source addr to be filled in the list
+ * @size: Size to be filled in the list
+ *
+ * Adds the node to the list after required fields
+ * are populated in the node
+ */
+static int sst_fill_memcpy_list(struct list_head *memcpy_list,
+			void *destn, const void *src, u32 size, bool is_io)
+{
+	struct sst_memcpy_list *listnode;
+
+	listnode = kzalloc(sizeof(*listnode), GFP_KERNEL);
+	if (listnode == NULL)
+		return -ENOMEM;
+	listnode->dstn = destn;
+	listnode->src = src;
+	listnode->size = size;
+	listnode->is_io = is_io;
+	list_add_tail(&listnode->memcpylist, memcpy_list);
+
+	return 0;
+}
+
+/**
+ * sst_parse_module_memcpy - Parse audio FW modules and populate the memcpy list
+ *
+ * @sst_drv_ctx		: driver context
+ * @module		: FW module header
+ * @memcpy_list	: Pointer to the list to be populated
+ * Create the memcpy list as the number of block to be copied
+ * returns error or 0 if module sizes are proper
+ */
+static int sst_parse_module_memcpy(struct intel_sst_drv *sst_drv_ctx,
+		struct fw_module_header *module, struct list_head *memcpy_list)
+{
+	struct fw_block_info *block;
+	u32 count;
+	int ret_val = 0;
+	void __iomem *ram_iomem;
+
+	dev_dbg(sst_drv_ctx->dev, "module sign %s size %x blocks %x type %x\n",
+			module->signature, module->mod_size,
+			module->blocks, module->type);
+	dev_dbg(sst_drv_ctx->dev, "module entrypoint 0x%x\n", module->entry_point);
+
+	block = (void *)module + sizeof(*module);
+
+	for (count = 0; count < module->blocks; count++) {
+		if (block->size <= 0) {
+			dev_err(sst_drv_ctx->dev, "block size invalid\n");
+			return -EINVAL;
+		}
+		switch (block->type) {
+		case SST_IRAM:
+			ram_iomem = sst_drv_ctx->iram;
+			break;
+		case SST_DRAM:
+			ram_iomem = sst_drv_ctx->dram;
+			break;
+		case SST_DDR:
+			ram_iomem = sst_drv_ctx->ddr;
+			break;
+		case SST_CUSTOM_INFO:
+			block = (void *)block + sizeof(*block) + block->size;
+			continue;
+		default:
+			dev_err(sst_drv_ctx->dev, "wrong ram type0x%x in block0x%x\n",
+					block->type, count);
+			return -EINVAL;
+		}
+
+		ret_val = sst_fill_memcpy_list(memcpy_list,
+				ram_iomem + block->ram_offset,
+				(void *)block + sizeof(*block), block->size, 1);
+		if (ret_val)
+			return ret_val;
+
+		block = (void *)block + sizeof(*block) + block->size;
+	}
+	return 0;
+}
+
+/**
+ * sst_parse_fw_memcpy - parse the firmware image & populate the list for memcpy
+ *
+ * @ctx			: pointer to drv context
+ * @size		: size of the firmware
+ * @fw_list		: pointer to list_head to be populated
+ * This function parses the FW image and saves the parsed image in the list
+ * for memcpy
+ */
+static int sst_parse_fw_memcpy(struct intel_sst_drv *ctx, unsigned long size,
+				struct list_head *fw_list)
+{
+	struct fw_module_header *module;
+	u32 count, num_modules;
+	int ret_val;
+
+	ret_val = sst_validate_fw_image(ctx, size, &module, &num_modules);
+	if (ret_val)
+		return ret_val;
+
+	for (count = 0; count < num_modules; count++) {
+		ret_val = sst_parse_module_memcpy(ctx, module, fw_list);
+		if (ret_val)
+			return ret_val;
+		module = (void *)module + sizeof(*module) + module->mod_size;
+	}
+
+	return 0;
+}
+
+/**
+ * sst_do_memcpy - function initiates the memcpy
+ *
+ * @memcpy_list: Pter to memcpy list on which the memcpy needs to be initiated
+ *
+ * Triggers the memcpy
+ */
+static void sst_do_memcpy(struct list_head *memcpy_list)
+{
+	struct sst_memcpy_list *listnode;
+
+	list_for_each_entry(listnode, memcpy_list, memcpylist) {
+		if (listnode->is_io == true)
+			memcpy32_toio((void __iomem *)listnode->dstn,
+					listnode->src, listnode->size);
+		else
+			memcpy(listnode->dstn, listnode->src, listnode->size);
+	}
+}
+
+void sst_memcpy_free_resources(struct intel_sst_drv *sst_drv_ctx)
+{
+	struct sst_memcpy_list *listnode, *tmplistnode;
+
+	/* Free the list */
+	if (!list_empty(&sst_drv_ctx->memcpy_list)) {
+		list_for_each_entry_safe(listnode, tmplistnode,
+				&sst_drv_ctx->memcpy_list, memcpylist) {
+			list_del(&listnode->memcpylist);
+			kfree(listnode);
+		}
+	}
+}
+
+static int sst_cache_and_parse_fw(struct intel_sst_drv *sst,
+		const struct firmware *fw)
+{
+	int retval = 0;
+
+	sst->fw_in_mem = kzalloc(fw->size, GFP_KERNEL);
+	if (!sst->fw_in_mem) {
+		retval = -ENOMEM;
+		goto end_release;
+	}
+	dev_dbg(sst->dev, "copied fw to %p", sst->fw_in_mem);
+	dev_dbg(sst->dev, "phys: %lx", (unsigned long)virt_to_phys(sst->fw_in_mem));
+	memcpy(sst->fw_in_mem, fw->data, fw->size);
+	retval = sst_parse_fw_memcpy(sst, fw->size, &sst->memcpy_list);
+	if (retval) {
+		dev_err(sst->dev, "Failed to parse fw\n");
+		kfree(sst->fw_in_mem);
+		sst->fw_in_mem = NULL;
+	}
+
+end_release:
+	release_firmware(fw);
+	return retval;
+
+}
+
+void sst_firmware_load_cb(const struct firmware *fw, void *context)
+{
+	struct intel_sst_drv *ctx = context;
+
+	dev_dbg(ctx->dev, "Enter\n");
+
+	if (fw == NULL) {
+		dev_err(ctx->dev, "request fw failed\n");
+		return;
+	}
+
+	mutex_lock(&ctx->sst_lock);
+
+	if (ctx->sst_state != SST_RESET ||
+			ctx->fw_in_mem != NULL) {
+		if (fw != NULL)
+			release_firmware(fw);
+		mutex_unlock(&ctx->sst_lock);
+		return;
+	}
+
+	dev_dbg(ctx->dev, "Request Fw completed\n");
+	sst_cache_and_parse_fw(ctx, fw);
+	mutex_unlock(&ctx->sst_lock);
+}
+
+/*
+ * sst_request_fw - requests audio fw from kernel and saves a copy
+ *
+ * This function requests the SST FW from the kernel, parses it and
+ * saves a copy in the driver context
+ */
+static int sst_request_fw(struct intel_sst_drv *sst)
+{
+	int retval = 0;
+	const struct firmware *fw;
+
+	retval = request_firmware(&fw, sst->firmware_name, sst->dev);
+	if (fw == NULL) {
+		dev_err(sst->dev, "fw is returning as null\n");
+		return -EINVAL;
+	}
+	if (retval) {
+		dev_err(sst->dev, "request fw failed %d\n", retval);
+		return retval;
+	}
+	mutex_lock(&sst->sst_lock);
+	retval = sst_cache_and_parse_fw(sst, fw);
+	mutex_unlock(&sst->sst_lock);
+
+	return retval;
+}
+
+/*
+ * Writing the DDR physical base to DCCM offset
+ * so that FW can use it to setup TLB
+ */
+static void sst_dccm_config_write(void __iomem *dram_base,
+		unsigned int ddr_base)
+{
+	void __iomem *addr;
+	u32 bss_reset = 0;
+
+	addr = (void __iomem *)(dram_base + MRFLD_FW_DDR_BASE_OFFSET);
+	memcpy32_toio(addr, (void *)&ddr_base, sizeof(u32));
+	bss_reset |= (1 << MRFLD_FW_BSS_RESET_BIT);
+	addr = (void __iomem *)(dram_base + MRFLD_FW_FEATURE_BASE_OFFSET);
+	memcpy32_toio(addr, &bss_reset, sizeof(u32));
+
+}
+
+void sst_post_download_mrfld(struct intel_sst_drv *ctx)
+{
+	sst_dccm_config_write(ctx->dram, ctx->ddr_base);
+	dev_dbg(ctx->dev, "config written to DCCM\n");
+}
+
+/**
+ * sst_load_fw - function to load FW into DSP
+ * Transfers the FW to DSP using dma/memcpy
+ */
+int sst_load_fw(struct intel_sst_drv *sst_drv_ctx)
+{
+	int ret_val = 0;
+	struct sst_block *block;
+
+	dev_dbg(sst_drv_ctx->dev, "sst_load_fw\n");
+
+	if (sst_drv_ctx->sst_state !=  SST_RESET ||
+			sst_drv_ctx->sst_state == SST_SHUTDOWN)
+		return -EAGAIN;
+
+	if (!sst_drv_ctx->fw_in_mem) {
+		dev_dbg(sst_drv_ctx->dev, "sst: FW not in memory retry to download\n");
+		ret_val = sst_request_fw(sst_drv_ctx);
+		if (ret_val)
+			return ret_val;
+	}
+
+	BUG_ON(!sst_drv_ctx->fw_in_mem);
+	block = sst_create_block(sst_drv_ctx, 0, FW_DWNL_ID);
+	if (block == NULL)
+		return -ENOMEM;
+
+	/* Prevent C-states beyond C6 */
+	pm_qos_update_request(sst_drv_ctx->qos, 0);
+
+	sst_drv_ctx->sst_state = SST_FW_LOADING;
+
+	ret_val = sst_drv_ctx->ops->reset(sst_drv_ctx);
+	if (ret_val)
+		goto restore;
+
+	sst_do_memcpy(&sst_drv_ctx->memcpy_list);
+
+	/* Write the DRAM/DCCM config before enabling FW */
+	if (sst_drv_ctx->ops->post_download)
+		sst_drv_ctx->ops->post_download(sst_drv_ctx);
+
+	/* bring sst out of reset */
+	ret_val = sst_drv_ctx->ops->start(sst_drv_ctx);
+	if (ret_val)
+		goto restore;
+
+	ret_val = sst_wait_timeout(sst_drv_ctx, block);
+	if (ret_val) {
+		dev_err(sst_drv_ctx->dev, "fw download failed %d\n" , ret_val);
+		/* FW download failed due to timeout */
+		ret_val = -EBUSY;
+
+	}
+
+
+restore:
+	/* Re-enable Deeper C-states beyond C6 */
+	pm_qos_update_request(sst_drv_ctx->qos, PM_QOS_DEFAULT_VALUE);
+	sst_free_block(sst_drv_ctx, block);
+	dev_dbg(sst_drv_ctx->dev, "fw load successful!!!\n");
+
+	if (sst_drv_ctx->ops->restore_dsp_context)
+		sst_drv_ctx->ops->restore_dsp_context();
+	sst_drv_ctx->sst_state = SST_FW_RUNNING;
+	return ret_val;
+}
+
diff --git a/sound/soc/intel/sst/sst_pci.c b/sound/soc/intel/sst/sst_pci.c
new file mode 100644
index 000000000000..3a0b3bf0af97
--- /dev/null
+++ b/sound/soc/intel/sst/sst_pci.c
@@ -0,0 +1,209 @@
+/*
+ *  sst_pci.c - SST (LPE) driver init file for pci enumeration.
+ *
+ *  Copyright (C) 2008-14	Intel Corp
+ *  Authors:	Vinod Koul <vinod.koul@intel.com>
+ *		Harsha Priya <priya.harsha@intel.com>
+ *		Dharageswari R <dharageswari.r@intel.com>
+ *		KP Jeeja <jeeja.kp@intel.com>
+ *  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; version 2 of the License.
+ *
+ *  This program is distributed in the hope that it will be useful, but
+ *  WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  General Public License for more details.
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/fs.h>
+#include <linux/firmware.h>
+#include <linux/pm_runtime.h>
+#include <sound/core.h>
+#include <sound/soc.h>
+#include <asm/platform_sst_audio.h>
+#include "../sst-mfld-platform.h"
+#include "sst.h"
+
+static int sst_platform_get_resources(struct intel_sst_drv *ctx)
+{
+	int ddr_base, ret = 0;
+	struct pci_dev *pci = ctx->pci;
+
+	ret = pci_request_regions(pci, SST_DRV_NAME);
+	if (ret)
+		return ret;
+
+	/* map registers */
+	/* DDR base */
+	if (ctx->dev_id == SST_MRFLD_PCI_ID) {
+		ctx->ddr_base = pci_resource_start(pci, 0);
+		/* check that the relocated IMR base matches with FW Binary */
+		ddr_base = relocate_imr_addr_mrfld(ctx->ddr_base);
+		if (!ctx->pdata->lib_info) {
+			dev_err(ctx->dev, "lib_info pointer NULL\n");
+			ret = -EINVAL;
+			goto do_release_regions;
+		}
+		if (ddr_base != ctx->pdata->lib_info->mod_base) {
+			dev_err(ctx->dev,
+					"FW LSP DDR BASE does not match with IFWI\n");
+			ret = -EINVAL;
+			goto do_release_regions;
+		}
+		ctx->ddr_end = pci_resource_end(pci, 0);
+
+		ctx->ddr = pcim_iomap(pci, 0,
+					pci_resource_len(pci, 0));
+		if (!ctx->ddr) {
+			ret = -EINVAL;
+			goto do_release_regions;
+		}
+		dev_dbg(ctx->dev, "sst: DDR Ptr %p\n", ctx->ddr);
+	} else {
+		ctx->ddr = NULL;
+	}
+	/* SHIM */
+	ctx->shim_phy_add = pci_resource_start(pci, 1);
+	ctx->shim = pcim_iomap(pci, 1, pci_resource_len(pci, 1));
+	if (!ctx->shim) {
+		ret = -EINVAL;
+		goto do_release_regions;
+	}
+	dev_dbg(ctx->dev, "SST Shim Ptr %p\n", ctx->shim);
+
+	/* Shared SRAM */
+	ctx->mailbox_add = pci_resource_start(pci, 2);
+	ctx->mailbox = pcim_iomap(pci, 2, pci_resource_len(pci, 2));
+	if (!ctx->mailbox) {
+		ret = -EINVAL;
+		goto do_release_regions;
+	}
+	dev_dbg(ctx->dev, "SRAM Ptr %p\n", ctx->mailbox);
+
+	/* IRAM */
+	ctx->iram_end = pci_resource_end(pci, 3);
+	ctx->iram_base = pci_resource_start(pci, 3);
+	ctx->iram = pcim_iomap(pci, 3, pci_resource_len(pci, 3));
+	if (!ctx->iram) {
+		ret = -EINVAL;
+		goto do_release_regions;
+	}
+	dev_dbg(ctx->dev, "IRAM Ptr %p\n", ctx->iram);
+
+	/* DRAM */
+	ctx->dram_end = pci_resource_end(pci, 4);
+	ctx->dram_base = pci_resource_start(pci, 4);
+	ctx->dram = pcim_iomap(pci, 4, pci_resource_len(pci, 4));
+	if (!ctx->dram) {
+		ret = -EINVAL;
+		goto do_release_regions;
+	}
+	dev_dbg(ctx->dev, "DRAM Ptr %p\n", ctx->dram);
+do_release_regions:
+	pci_release_regions(pci);
+	return 0;
+}
+
+/*
+ * intel_sst_probe - PCI probe function
+ *
+ * @pci:	PCI device structure
+ * @pci_id: PCI device ID structure
+ *
+ */
+static int intel_sst_probe(struct pci_dev *pci,
+			const struct pci_device_id *pci_id)
+{
+	int ret = 0;
+	struct intel_sst_drv *sst_drv_ctx;
+	struct sst_platform_info *sst_pdata = pci->dev.platform_data;
+
+	dev_dbg(&pci->dev, "Probe for DID %x\n", pci->device);
+	ret = sst_alloc_drv_context(&sst_drv_ctx, &pci->dev, pci->device);
+	if (ret < 0)
+		return ret;
+
+	sst_drv_ctx->pdata = sst_pdata;
+	sst_drv_ctx->irq_num = pci->irq;
+	snprintf(sst_drv_ctx->firmware_name, sizeof(sst_drv_ctx->firmware_name),
+			"%s%04x%s", "fw_sst_",
+			sst_drv_ctx->dev_id, ".bin");
+
+	ret = sst_context_init(sst_drv_ctx);
+	if (ret < 0)
+		return ret;
+
+	/* Init the device */
+	ret = pcim_enable_device(pci);
+	if (ret) {
+		dev_err(sst_drv_ctx->dev,
+			"device can't be enabled. Returned err: %d\n", ret);
+		goto do_free_drv_ctx;
+	}
+	sst_drv_ctx->pci = pci_dev_get(pci);
+	ret = sst_platform_get_resources(sst_drv_ctx);
+	if (ret < 0)
+		goto do_free_drv_ctx;
+
+	pci_set_drvdata(pci, sst_drv_ctx);
+	sst_configure_runtime_pm(sst_drv_ctx);
+
+	return ret;
+
+do_free_drv_ctx:
+	sst_context_cleanup(sst_drv_ctx);
+	dev_err(sst_drv_ctx->dev, "Probe failed with %d\n", ret);
+	return ret;
+}
+
+/**
+ * intel_sst_remove - PCI remove function
+ *
+ * @pci:	PCI device structure
+ *
+ * This function is called by OS when a device is unloaded
+ * This frees the interrupt etc
+ */
+static void intel_sst_remove(struct pci_dev *pci)
+{
+	struct intel_sst_drv *sst_drv_ctx = pci_get_drvdata(pci);
+
+	sst_context_cleanup(sst_drv_ctx);
+	pci_dev_put(sst_drv_ctx->pci);
+	pci_release_regions(pci);
+	pci_set_drvdata(pci, NULL);
+}
+
+/* PCI Routines */
+static struct pci_device_id intel_sst_ids[] = {
+	{ PCI_VDEVICE(INTEL, SST_MRFLD_PCI_ID), 0},
+	{ 0, }
+};
+
+static struct pci_driver sst_driver = {
+	.name = SST_DRV_NAME,
+	.id_table = intel_sst_ids,
+	.probe = intel_sst_probe,
+	.remove = intel_sst_remove,
+#ifdef CONFIG_PM
+	.driver = {
+		.pm = &intel_sst_pm,
+	},
+#endif
+};
+
+module_pci_driver(sst_driver);
+
+MODULE_DESCRIPTION("Intel (R) SST(R) Audio Engine PCI Driver");
+MODULE_AUTHOR("Vinod Koul <vinod.koul@intel.com>");
+MODULE_AUTHOR("Harsha Priya <priya.harsha@intel.com>");
+MODULE_AUTHOR("Dharageswari R <dharageswari.r@intel.com>");
+MODULE_AUTHOR("KP Jeeja <jeeja.kp@intel.com>");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("sst");
diff --git a/sound/soc/intel/sst/sst_pvt.c b/sound/soc/intel/sst/sst_pvt.c
new file mode 100644
index 000000000000..4b7720864492
--- /dev/null
+++ b/sound/soc/intel/sst/sst_pvt.c
@@ -0,0 +1,449 @@
+/*
+ *  sst_pvt.c - Intel SST Driver for audio engine
+ *
+ *  Copyright (C) 2008-14	Intel Corp
+ *  Authors:	Vinod Koul <vinod.koul@intel.com>
+ *		Harsha Priya <priya.harsha@intel.com>
+ *		Dharageswari R <dharageswari.r@intel.com>
+ *		KP Jeeja <jeeja.kp@intel.com>
+ *  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; version 2 of the License.
+ *
+ *  This program is distributed in the hope that it will be useful, but
+ *  WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  General Public License for more details.
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+#include <linux/kobject.h>
+#include <linux/pci.h>
+#include <linux/fs.h>
+#include <linux/firmware.h>
+#include <linux/pm_runtime.h>
+#include <linux/sched.h>
+#include <linux/delay.h>
+#include <sound/asound.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+#include <sound/compress_driver.h>
+#include <asm/platform_sst_audio.h>
+#include "../sst-mfld-platform.h"
+#include "sst.h"
+#include "../sst-dsp.h"
+
+int sst_shim_write(void __iomem *addr, int offset, int value)
+{
+	writel(value, addr + offset);
+	return 0;
+}
+
+u32 sst_shim_read(void __iomem *addr, int offset)
+{
+	return readl(addr + offset);
+}
+
+u64 sst_reg_read64(void __iomem *addr, int offset)
+{
+	u64 val = 0;
+
+	memcpy_fromio(&val, addr + offset, sizeof(val));
+
+	return val;
+}
+
+int sst_shim_write64(void __iomem *addr, int offset, u64 value)
+{
+	memcpy_toio(addr + offset, &value, sizeof(value));
+	return 0;
+}
+
+u64 sst_shim_read64(void __iomem *addr, int offset)
+{
+	u64 val = 0;
+
+	memcpy_fromio(&val, addr + offset, sizeof(val));
+	return val;
+}
+
+void sst_set_fw_state_locked(
+		struct intel_sst_drv *sst_drv_ctx, int sst_state)
+{
+	mutex_lock(&sst_drv_ctx->sst_lock);
+	sst_drv_ctx->sst_state = sst_state;
+	mutex_unlock(&sst_drv_ctx->sst_lock);
+}
+
+/*
+ * sst_wait_interruptible - wait on event
+ *
+ * @sst_drv_ctx: Driver context
+ * @block: Driver block to wait on
+ *
+ * This function waits without a timeout (and is interruptable) for a
+ * given block event
+ */
+int sst_wait_interruptible(struct intel_sst_drv *sst_drv_ctx,
+				struct sst_block *block)
+{
+	int retval = 0;
+
+	if (!wait_event_interruptible(sst_drv_ctx->wait_queue,
+				block->condition)) {
+		/* event wake */
+		if (block->ret_code < 0) {
+			dev_err(sst_drv_ctx->dev,
+				"stream failed %d\n", block->ret_code);
+			retval = -EBUSY;
+		} else {
+			dev_dbg(sst_drv_ctx->dev, "event up\n");
+			retval = 0;
+		}
+	} else {
+		dev_err(sst_drv_ctx->dev, "signal interrupted\n");
+		retval = -EINTR;
+	}
+	return retval;
+
+}
+
+unsigned long long read_shim_data(struct intel_sst_drv *sst, int addr)
+{
+	unsigned long long val = 0;
+
+	switch (sst->dev_id) {
+	case SST_MRFLD_PCI_ID:
+	case SST_BYT_ACPI_ID:
+		val = sst_shim_read64(sst->shim, addr);
+		break;
+	}
+	return val;
+}
+
+void write_shim_data(struct intel_sst_drv *sst, int addr,
+				unsigned long long data)
+{
+	switch (sst->dev_id) {
+	case SST_MRFLD_PCI_ID:
+	case SST_BYT_ACPI_ID:
+		sst_shim_write64(sst->shim, addr, (u64) data);
+		break;
+	}
+}
+
+/*
+ * sst_wait_timeout - wait on event for timeout
+ *
+ * @sst_drv_ctx: Driver context
+ * @block: Driver block to wait on
+ *
+ * This function waits with a timeout value (and is not interruptible) on a
+ * given block event
+ */
+int sst_wait_timeout(struct intel_sst_drv *sst_drv_ctx, struct sst_block *block)
+{
+	int retval = 0;
+
+	/*
+	 * NOTE:
+	 * Observed that FW processes the alloc msg and replies even
+	 * before the alloc thread has finished execution
+	 */
+	dev_dbg(sst_drv_ctx->dev,
+		"waiting for condition %x ipc %d drv_id %d\n",
+		block->condition, block->msg_id, block->drv_id);
+	if (wait_event_timeout(sst_drv_ctx->wait_queue,
+				block->condition,
+				msecs_to_jiffies(SST_BLOCK_TIMEOUT))) {
+		/* event wake */
+		dev_dbg(sst_drv_ctx->dev, "Event wake %x\n",
+				block->condition);
+		dev_dbg(sst_drv_ctx->dev, "message ret: %d\n",
+				block->ret_code);
+		retval = -block->ret_code;
+	} else {
+		block->on = false;
+		dev_err(sst_drv_ctx->dev,
+			"Wait timed-out condition:%#x, msg_id:%#x fw_state %#x\n",
+			block->condition, block->msg_id, sst_drv_ctx->sst_state);
+		sst_drv_ctx->sst_state = SST_RESET;
+
+		retval = -EBUSY;
+	}
+	return retval;
+}
+
+/*
+ * sst_create_ipc_msg - create a IPC message
+ *
+ * @arg: ipc message
+ * @large: large or short message
+ *
+ * this function allocates structures to send a large or short
+ * message to the firmware
+ */
+int sst_create_ipc_msg(struct ipc_post **arg, bool large)
+{
+	struct ipc_post *msg;
+
+	msg = kzalloc(sizeof(struct ipc_post), GFP_ATOMIC);
+	if (!msg)
+		return -ENOMEM;
+	if (large) {
+		msg->mailbox_data = kzalloc(SST_MAILBOX_SIZE, GFP_ATOMIC);
+		if (!msg->mailbox_data) {
+			kfree(msg);
+			return -ENOMEM;
+		}
+	} else {
+		msg->mailbox_data = NULL;
+	}
+	msg->is_large = large;
+	*arg = msg;
+	return 0;
+}
+
+/*
+ * sst_create_block_and_ipc_msg - Creates IPC message and sst block
+ * @arg: passed to sst_create_ipc_message API
+ * @large: large or short message
+ * @sst_drv_ctx: sst driver context
+ * @block: return block allocated
+ * @msg_id: IPC
+ * @drv_id: stream id or private id
+ */
+int sst_create_block_and_ipc_msg(struct ipc_post **arg, bool large,
+		struct intel_sst_drv *sst_drv_ctx, struct sst_block **block,
+		u32 msg_id, u32 drv_id)
+{
+	int retval = 0;
+
+	retval = sst_create_ipc_msg(arg, large);
+	if (retval)
+		return retval;
+	*block = sst_create_block(sst_drv_ctx, msg_id, drv_id);
+	if (*block == NULL) {
+		kfree(*arg);
+		return -ENOMEM;
+	}
+	return retval;
+}
+
+/*
+ * sst_clean_stream - clean the stream context
+ *
+ * @stream: stream structure
+ *
+ * this function resets the stream contexts
+ * should be called in free
+ */
+void sst_clean_stream(struct stream_info *stream)
+{
+	stream->status = STREAM_UN_INIT;
+	stream->prev = STREAM_UN_INIT;
+	mutex_lock(&stream->lock);
+	stream->cumm_bytes = 0;
+	mutex_unlock(&stream->lock);
+}
+
+int sst_prepare_and_post_msg(struct intel_sst_drv *sst,
+		int task_id, int ipc_msg, int cmd_id, int pipe_id,
+		size_t mbox_data_len, const void *mbox_data, void **data,
+		bool large, bool fill_dsp, bool sync, bool response)
+{
+	struct ipc_post *msg = NULL;
+	struct ipc_dsp_hdr dsp_hdr;
+	struct sst_block *block;
+	int ret = 0, pvt_id;
+
+	pvt_id = sst_assign_pvt_id(sst);
+	if (pvt_id < 0)
+		return pvt_id;
+
+	if (response)
+		ret = sst_create_block_and_ipc_msg(
+				&msg, large, sst, &block, ipc_msg, pvt_id);
+	else
+		ret = sst_create_ipc_msg(&msg, large);
+
+	if (ret < 0) {
+		test_and_clear_bit(pvt_id, &sst->pvt_id);
+		return -ENOMEM;
+	}
+
+	dev_dbg(sst->dev, "pvt_id = %d, pipe id = %d, task = %d ipc_msg: %d\n",
+		 pvt_id, pipe_id, task_id, ipc_msg);
+	sst_fill_header_mrfld(&msg->mrfld_header, ipc_msg,
+					task_id, large, pvt_id);
+	msg->mrfld_header.p.header_low_payload = sizeof(dsp_hdr) + mbox_data_len;
+	msg->mrfld_header.p.header_high.part.res_rqd = !sync;
+	dev_dbg(sst->dev, "header:%x\n",
+			msg->mrfld_header.p.header_high.full);
+	dev_dbg(sst->dev, "response rqd: %x",
+			msg->mrfld_header.p.header_high.part.res_rqd);
+	dev_dbg(sst->dev, "msg->mrfld_header.p.header_low_payload:%d",
+			msg->mrfld_header.p.header_low_payload);
+	if (fill_dsp) {
+		sst_fill_header_dsp(&dsp_hdr, cmd_id, pipe_id, mbox_data_len);
+		memcpy(msg->mailbox_data, &dsp_hdr, sizeof(dsp_hdr));
+		if (mbox_data_len) {
+			memcpy(msg->mailbox_data + sizeof(dsp_hdr),
+					mbox_data, mbox_data_len);
+		}
+	}
+
+	if (sync)
+		sst->ops->post_message(sst, msg, true);
+	else
+		sst_add_to_dispatch_list_and_post(sst, msg);
+
+	if (response) {
+		ret = sst_wait_timeout(sst, block);
+		if (ret < 0) {
+			goto out;
+		} else if(block->data) {
+			if (!data)
+				goto out;
+			*data = kzalloc(block->size, GFP_KERNEL);
+			if (!(*data)) {
+				ret = -ENOMEM;
+				goto out;
+			} else
+				memcpy(data, (void *) block->data, block->size);
+		}
+	}
+out:
+	if (response)
+		sst_free_block(sst, block);
+	test_and_clear_bit(pvt_id, &sst->pvt_id);
+	return ret;
+}
+
+int sst_pm_runtime_put(struct intel_sst_drv *sst_drv)
+{
+	int ret;
+
+	pm_runtime_mark_last_busy(sst_drv->dev);
+	ret = pm_runtime_put_autosuspend(sst_drv->dev);
+	if (ret < 0)
+		return ret;
+	return 0;
+}
+
+void sst_fill_header_mrfld(union ipc_header_mrfld *header,
+				int msg, int task_id, int large, int drv_id)
+{
+	header->full = 0;
+	header->p.header_high.part.msg_id = msg;
+	header->p.header_high.part.task_id = task_id;
+	header->p.header_high.part.large = large;
+	header->p.header_high.part.drv_id = drv_id;
+	header->p.header_high.part.done = 0;
+	header->p.header_high.part.busy = 1;
+	header->p.header_high.part.res_rqd = 1;
+}
+
+void sst_fill_header_dsp(struct ipc_dsp_hdr *dsp, int msg,
+					int pipe_id, int len)
+{
+	dsp->cmd_id = msg;
+	dsp->mod_index_id = 0xff;
+	dsp->pipe_id = pipe_id;
+	dsp->length = len;
+	dsp->mod_id = 0;
+}
+
+#define SST_MAX_BLOCKS 15
+/*
+ * sst_assign_pvt_id - assign a pvt id for stream
+ *
+ * @sst_drv_ctx : driver context
+ *
+ * this function assigns a private id for calls that dont have stream
+ * context yet, should be called with lock held
+ * uses bits for the id, and finds first free bits and assigns that
+ */
+int sst_assign_pvt_id(struct intel_sst_drv *drv)
+{
+	int local;
+
+	spin_lock(&drv->block_lock);
+	/* find first zero index from lsb */
+	local = ffz(drv->pvt_id);
+	dev_dbg(drv->dev, "pvt_id assigned --> %d\n", local);
+	if (local >= SST_MAX_BLOCKS){
+		spin_unlock(&drv->block_lock);
+		dev_err(drv->dev, "PVT _ID error: no free id blocks ");
+		return -EINVAL;
+	}
+	/* toggle the index */
+	change_bit(local, &drv->pvt_id);
+	spin_unlock(&drv->block_lock);
+	return local;
+}
+
+void sst_init_stream(struct stream_info *stream,
+		int codec, int sst_id, int ops, u8 slot)
+{
+	stream->status = STREAM_INIT;
+	stream->prev = STREAM_UN_INIT;
+	stream->ops = ops;
+}
+
+int sst_validate_strid(
+		struct intel_sst_drv *sst_drv_ctx, int str_id)
+{
+	if (str_id <= 0 || str_id > sst_drv_ctx->info.max_streams) {
+		dev_err(sst_drv_ctx->dev,
+			"SST ERR: invalid stream id : %d, max %d\n",
+			str_id, sst_drv_ctx->info.max_streams);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+struct stream_info *get_stream_info(
+		struct intel_sst_drv *sst_drv_ctx, int str_id)
+{
+	if (sst_validate_strid(sst_drv_ctx, str_id))
+		return NULL;
+	return &sst_drv_ctx->streams[str_id];
+}
+
+int get_stream_id_mrfld(struct intel_sst_drv *sst_drv_ctx,
+		u32 pipe_id)
+{
+	int i;
+
+	for (i = 1; i <= sst_drv_ctx->info.max_streams; i++)
+		if (pipe_id == sst_drv_ctx->streams[i].pipe_id)
+			return i;
+
+	dev_dbg(sst_drv_ctx->dev, "no such pipe_id(%u)", pipe_id);
+	return -1;
+}
+
+u32 relocate_imr_addr_mrfld(u32 base_addr)
+{
+	/* Get the difference from 512MB aligned base addr */
+	/* relocate the base */
+	base_addr = MRFLD_FW_VIRTUAL_BASE + (base_addr % (512 * 1024 * 1024));
+	return base_addr;
+}
+EXPORT_SYMBOL_GPL(relocate_imr_addr_mrfld);
+
+void sst_add_to_dispatch_list_and_post(struct intel_sst_drv *sst,
+						struct ipc_post *msg)
+{
+	unsigned long irq_flags;
+
+	spin_lock_irqsave(&sst->ipc_spin_lock, irq_flags);
+	list_add_tail(&msg->node, &sst->ipc_dispatch_list);
+	spin_unlock_irqrestore(&sst->ipc_spin_lock, irq_flags);
+	sst->ops->post_message(sst, NULL, false);
+}
diff --git a/sound/soc/intel/sst/sst_stream.c b/sound/soc/intel/sst/sst_stream.c
new file mode 100644
index 000000000000..dae2a41997aa
--- /dev/null
+++ b/sound/soc/intel/sst/sst_stream.c
@@ -0,0 +1,437 @@
+/*
+ *  sst_stream.c - Intel SST Driver for audio engine
+ *
+ *  Copyright (C) 2008-14 Intel Corp
+ *  Authors:	Vinod Koul <vinod.koul@intel.com>
+ *		Harsha Priya <priya.harsha@intel.com>
+ *		Dharageswari R <dharageswari.r@intel.com>
+ *		KP Jeeja <jeeja.kp@intel.com>
+ *  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; version 2 of the License.
+ *
+ *  This program is distributed in the hope that it will be useful, but
+ *  WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  General Public License for more details.
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+#include <linux/pci.h>
+#include <linux/firmware.h>
+#include <linux/sched.h>
+#include <linux/delay.h>
+#include <linux/pm_runtime.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+#include <sound/compress_driver.h>
+#include <asm/platform_sst_audio.h>
+#include "../sst-mfld-platform.h"
+#include "sst.h"
+#include "../sst-dsp.h"
+
+int sst_alloc_stream_mrfld(struct intel_sst_drv *sst_drv_ctx, void *params)
+{
+	struct snd_sst_alloc_mrfld alloc_param;
+	struct snd_sst_params *str_params;
+	struct snd_sst_tstamp fw_tstamp;
+	struct stream_info *str_info;
+	struct snd_sst_alloc_response *response;
+	unsigned int str_id, pipe_id, task_id;
+	int i, num_ch, ret = 0;
+	void *data = NULL;
+
+	dev_dbg(sst_drv_ctx->dev, "Enter\n");
+	BUG_ON(!params);
+
+	str_params = (struct snd_sst_params *)params;
+	memset(&alloc_param, 0, sizeof(alloc_param));
+	alloc_param.operation = str_params->ops;
+	alloc_param.codec_type = str_params->codec;
+	alloc_param.sg_count = str_params->aparams.sg_count;
+	alloc_param.ring_buf_info[0].addr =
+		str_params->aparams.ring_buf_info[0].addr;
+	alloc_param.ring_buf_info[0].size =
+		str_params->aparams.ring_buf_info[0].size;
+	alloc_param.frag_size = str_params->aparams.frag_size;
+
+	memcpy(&alloc_param.codec_params, &str_params->sparams,
+			sizeof(struct snd_sst_stream_params));
+
+	/*
+	 * fill channel map params for multichannel support.
+	 * Ideally channel map should be received from upper layers
+	 * for multichannel support.
+	 * Currently hardcoding as per FW reqm.
+	 */
+	num_ch = sst_get_num_channel(str_params);
+	for (i = 0; i < 8; i++) {
+		if (i < num_ch)
+			alloc_param.codec_params.uc.pcm_params.channel_map[i] = i;
+		else
+			alloc_param.codec_params.uc.pcm_params.channel_map[i] = 0xFF;
+	}
+
+	str_id = str_params->stream_id;
+	str_info = get_stream_info(sst_drv_ctx, str_id);
+	if (str_info == NULL) {
+		dev_err(sst_drv_ctx->dev, "get stream info returned null\n");
+		return -EINVAL;
+	}
+
+	pipe_id = str_params->device_type;
+	task_id = str_params->task;
+	sst_drv_ctx->streams[str_id].pipe_id = pipe_id;
+	sst_drv_ctx->streams[str_id].task_id = task_id;
+	sst_drv_ctx->streams[str_id].num_ch = num_ch;
+
+	if (sst_drv_ctx->info.lpe_viewpt_rqd)
+		alloc_param.ts = sst_drv_ctx->info.mailbox_start +
+			sst_drv_ctx->tstamp + (str_id * sizeof(fw_tstamp));
+	else
+		alloc_param.ts = sst_drv_ctx->mailbox_add +
+			sst_drv_ctx->tstamp + (str_id * sizeof(fw_tstamp));
+
+	dev_dbg(sst_drv_ctx->dev, "alloc tstamp location = 0x%x\n",
+			alloc_param.ts);
+	dev_dbg(sst_drv_ctx->dev, "assigned pipe id 0x%x to task %d\n",
+			pipe_id, task_id);
+
+	/* allocate device type context */
+	sst_init_stream(&sst_drv_ctx->streams[str_id], alloc_param.codec_type,
+			str_id, alloc_param.operation, 0);
+
+	dev_info(sst_drv_ctx->dev, "Alloc for str %d pipe %#x\n",
+			str_id, pipe_id);
+	ret = sst_prepare_and_post_msg(sst_drv_ctx, task_id, IPC_CMD,
+			IPC_IA_ALLOC_STREAM_MRFLD, pipe_id, sizeof(alloc_param),
+			&alloc_param, data, true, true, false, true);
+
+	if (ret < 0) {
+		dev_err(sst_drv_ctx->dev, "FW alloc failed ret %d\n", ret);
+		/* alloc failed, so reset the state to uninit */
+		str_info->status = STREAM_UN_INIT;
+		str_id = ret;
+	} else if (data) {
+		response = (struct snd_sst_alloc_response *)data;
+		ret = response->str_type.result;
+		if (!ret)
+			goto out;
+		dev_err(sst_drv_ctx->dev, "FW alloc failed ret %d\n", ret);
+		if (ret == SST_ERR_STREAM_IN_USE) {
+			dev_err(sst_drv_ctx->dev,
+				"FW not in clean state, send free for:%d\n", str_id);
+			sst_free_stream(sst_drv_ctx, str_id);
+		}
+		str_id = -ret;
+	}
+out:
+	kfree(data);
+	return str_id;
+}
+
+/**
+* sst_start_stream - Send msg for a starting stream
+* @str_id:	 stream ID
+*
+* This function is called by any function which wants to start
+* a stream.
+*/
+int sst_start_stream(struct intel_sst_drv *sst_drv_ctx, int str_id)
+{
+	int retval = 0;
+	struct stream_info *str_info;
+	u16 data = 0;
+
+	dev_dbg(sst_drv_ctx->dev, "sst_start_stream for %d\n", str_id);
+	str_info = get_stream_info(sst_drv_ctx, str_id);
+	if (!str_info)
+		return -EINVAL;
+	if (str_info->status != STREAM_RUNNING)
+		return -EBADRQC;
+
+	retval = sst_prepare_and_post_msg(sst_drv_ctx, str_info->task_id,
+			IPC_CMD, IPC_IA_START_STREAM_MRFLD, str_info->pipe_id,
+			sizeof(u16), &data, NULL, true, true, true, false);
+
+	return retval;
+}
+
+int sst_send_byte_stream_mrfld(struct intel_sst_drv *sst_drv_ctx,
+		struct snd_sst_bytes_v2 *bytes)
+{	struct ipc_post *msg = NULL;
+	u32 length;
+	int pvt_id, ret = 0;
+	struct sst_block *block = NULL;
+
+	dev_dbg(sst_drv_ctx->dev,
+		"type:%u ipc_msg:%u block:%u task_id:%u pipe: %#x length:%#x\n",
+		bytes->type, bytes->ipc_msg, bytes->block, bytes->task_id,
+		bytes->pipe_id, bytes->len);
+
+	if (sst_create_ipc_msg(&msg, true))
+		return -ENOMEM;
+
+	pvt_id = sst_assign_pvt_id(sst_drv_ctx);
+	sst_fill_header_mrfld(&msg->mrfld_header, bytes->ipc_msg,
+			bytes->task_id, 1, pvt_id);
+	msg->mrfld_header.p.header_high.part.res_rqd = bytes->block;
+	length = bytes->len;
+	msg->mrfld_header.p.header_low_payload = length;
+	dev_dbg(sst_drv_ctx->dev, "length is %d\n", length);
+	memcpy(msg->mailbox_data, &bytes->bytes, bytes->len);
+	if (bytes->block) {
+		block = sst_create_block(sst_drv_ctx, bytes->ipc_msg, pvt_id);
+		if (block == NULL) {
+			kfree(msg);
+			ret = -ENOMEM;
+			goto out;
+		}
+	}
+
+	sst_add_to_dispatch_list_and_post(sst_drv_ctx, msg);
+	dev_dbg(sst_drv_ctx->dev, "msg->mrfld_header.p.header_low_payload:%d",
+			msg->mrfld_header.p.header_low_payload);
+
+	if (bytes->block) {
+		ret = sst_wait_timeout(sst_drv_ctx, block);
+		if (ret) {
+			dev_err(sst_drv_ctx->dev, "fw returned err %d\n", ret);
+			sst_free_block(sst_drv_ctx, block);
+			goto out;
+		}
+	}
+	if (bytes->type == SND_SST_BYTES_GET) {
+		/*
+		 * copy the reply and send back
+		 * we need to update only sz and payload
+		 */
+		if (bytes->block) {
+			unsigned char *r = block->data;
+
+			dev_dbg(sst_drv_ctx->dev, "read back %d bytes",
+					bytes->len);
+			memcpy(bytes->bytes, r, bytes->len);
+		}
+	}
+	if (bytes->block)
+		sst_free_block(sst_drv_ctx, block);
+out:
+	test_and_clear_bit(pvt_id, &sst_drv_ctx->pvt_id);
+	return 0;
+}
+
+/*
+ * sst_pause_stream - Send msg for a pausing stream
+ * @str_id:	 stream ID
+ *
+ * This function is called by any function which wants to pause
+ * an already running stream.
+ */
+int sst_pause_stream(struct intel_sst_drv *sst_drv_ctx, int str_id)
+{
+	int retval = 0;
+	struct stream_info *str_info;
+
+	dev_dbg(sst_drv_ctx->dev, "SST DBG:sst_pause_stream for %d\n", str_id);
+	str_info = get_stream_info(sst_drv_ctx, str_id);
+	if (!str_info)
+		return -EINVAL;
+	if (str_info->status == STREAM_PAUSED)
+		return 0;
+	if (str_info->status == STREAM_RUNNING ||
+		str_info->status == STREAM_INIT) {
+		if (str_info->prev == STREAM_UN_INIT)
+			return -EBADRQC;
+
+		retval = sst_prepare_and_post_msg(sst_drv_ctx, str_info->task_id, IPC_CMD,
+				IPC_IA_PAUSE_STREAM_MRFLD, str_info->pipe_id,
+				0, NULL, NULL, true, true, false, true);
+
+		if (retval == 0) {
+			str_info->prev = str_info->status;
+			str_info->status = STREAM_PAUSED;
+		} else if (retval == SST_ERR_INVALID_STREAM_ID) {
+			retval = -EINVAL;
+			mutex_lock(&sst_drv_ctx->sst_lock);
+			sst_clean_stream(str_info);
+			mutex_unlock(&sst_drv_ctx->sst_lock);
+		}
+	} else {
+		retval = -EBADRQC;
+		dev_dbg(sst_drv_ctx->dev, "SST DBG:BADRQC for stream\n ");
+	}
+
+	return retval;
+}
+
+/**
+ * sst_resume_stream - Send msg for resuming stream
+ * @str_id:		stream ID
+ *
+ * This function is called by any function which wants to resume
+ * an already paused stream.
+ */
+int sst_resume_stream(struct intel_sst_drv *sst_drv_ctx, int str_id)
+{
+	int retval = 0;
+	struct stream_info *str_info;
+
+	dev_dbg(sst_drv_ctx->dev, "SST DBG:sst_resume_stream for %d\n", str_id);
+	str_info = get_stream_info(sst_drv_ctx, str_id);
+	if (!str_info)
+		return -EINVAL;
+	if (str_info->status == STREAM_RUNNING)
+			return 0;
+	if (str_info->status == STREAM_PAUSED) {
+		retval = sst_prepare_and_post_msg(sst_drv_ctx, str_info->task_id,
+				IPC_CMD, IPC_IA_RESUME_STREAM_MRFLD,
+				str_info->pipe_id, 0, NULL, NULL,
+				true, true, false, true);
+
+		if (!retval) {
+			if (str_info->prev == STREAM_RUNNING)
+				str_info->status = STREAM_RUNNING;
+			else
+				str_info->status = STREAM_INIT;
+			str_info->prev = STREAM_PAUSED;
+		} else if (retval == -SST_ERR_INVALID_STREAM_ID) {
+			retval = -EINVAL;
+			mutex_lock(&sst_drv_ctx->sst_lock);
+			sst_clean_stream(str_info);
+			mutex_unlock(&sst_drv_ctx->sst_lock);
+		}
+	} else {
+		retval = -EBADRQC;
+		dev_err(sst_drv_ctx->dev, "SST ERR: BADQRC for stream\n");
+	}
+
+	return retval;
+}
+
+
+/**
+ * sst_drop_stream - Send msg for stopping stream
+ * @str_id:		stream ID
+ *
+ * This function is called by any function which wants to stop
+ * a stream.
+ */
+int sst_drop_stream(struct intel_sst_drv *sst_drv_ctx, int str_id)
+{
+	int retval = 0;
+	struct stream_info *str_info;
+
+	dev_dbg(sst_drv_ctx->dev, "SST DBG:sst_drop_stream for %d\n", str_id);
+	str_info = get_stream_info(sst_drv_ctx, str_id);
+	if (!str_info)
+		return -EINVAL;
+
+	if (str_info->status != STREAM_UN_INIT) {
+		str_info->prev = STREAM_UN_INIT;
+		str_info->status = STREAM_INIT;
+		str_info->cumm_bytes = 0;
+		retval = sst_prepare_and_post_msg(sst_drv_ctx, str_info->task_id,
+				IPC_CMD, IPC_IA_DROP_STREAM_MRFLD,
+				str_info->pipe_id, 0, NULL, NULL,
+				true, true, true, false);
+	} else {
+		retval = -EBADRQC;
+		dev_dbg(sst_drv_ctx->dev, "BADQRC for stream, state %x\n",
+				str_info->status);
+	}
+	return retval;
+}
+
+/**
+* sst_drain_stream - Send msg for draining stream
+* @str_id:		stream ID
+*
+* This function is called by any function which wants to drain
+* a stream.
+*/
+int sst_drain_stream(struct intel_sst_drv *sst_drv_ctx,
+			int str_id, bool partial_drain)
+{
+	int retval = 0;
+	struct stream_info *str_info;
+
+	dev_dbg(sst_drv_ctx->dev, "SST DBG:sst_drain_stream for %d\n", str_id);
+	str_info = get_stream_info(sst_drv_ctx, str_id);
+	if (!str_info)
+		return -EINVAL;
+	if (str_info->status != STREAM_RUNNING &&
+		str_info->status != STREAM_INIT &&
+		str_info->status != STREAM_PAUSED) {
+			dev_err(sst_drv_ctx->dev, "SST ERR: BADQRC for stream = %d\n",
+				       str_info->status);
+			return -EBADRQC;
+	}
+
+	retval = sst_prepare_and_post_msg(sst_drv_ctx, str_info->task_id, IPC_CMD,
+			IPC_IA_DRAIN_STREAM_MRFLD, str_info->pipe_id,
+			sizeof(u8), &partial_drain, NULL, true, true, false, false);
+	/*
+	 * with new non blocked drain implementation in core we dont need to
+	 * wait for respsonse, and need to only invoke callback for drain
+	 * complete
+	 */
+
+	return retval;
+}
+
+/**
+ * sst_free_stream - Frees a stream
+ * @str_id:		stream ID
+ *
+ * This function is called by any function which wants to free
+ * a stream.
+ */
+int sst_free_stream(struct intel_sst_drv *sst_drv_ctx, int str_id)
+{
+	int retval = 0;
+	struct stream_info *str_info;
+	struct intel_sst_ops *ops;
+
+	dev_dbg(sst_drv_ctx->dev, "SST DBG:sst_free_stream for %d\n", str_id);
+
+	mutex_lock(&sst_drv_ctx->sst_lock);
+	if (sst_drv_ctx->sst_state == SST_RESET) {
+		mutex_unlock(&sst_drv_ctx->sst_lock);
+		return -ENODEV;
+	}
+	mutex_unlock(&sst_drv_ctx->sst_lock);
+	str_info = get_stream_info(sst_drv_ctx, str_id);
+	if (!str_info)
+		return -EINVAL;
+	ops = sst_drv_ctx->ops;
+
+	mutex_lock(&str_info->lock);
+	if (str_info->status != STREAM_UN_INIT) {
+		str_info->prev =  str_info->status;
+		str_info->status = STREAM_UN_INIT;
+		mutex_unlock(&str_info->lock);
+
+		dev_info(sst_drv_ctx->dev, "Free for str %d pipe %#x\n",
+				str_id, str_info->pipe_id);
+		retval = sst_prepare_and_post_msg(sst_drv_ctx, str_info->task_id, IPC_CMD,
+				IPC_IA_FREE_STREAM_MRFLD, str_info->pipe_id, 0,
+				NULL, NULL, true, true, false, true);
+
+		dev_dbg(sst_drv_ctx->dev, "sst: wait for free returned %d\n",
+				retval);
+		mutex_lock(&sst_drv_ctx->sst_lock);
+		sst_clean_stream(str_info);
+		mutex_unlock(&sst_drv_ctx->sst_lock);
+		dev_dbg(sst_drv_ctx->dev, "SST DBG:Stream freed\n");
+	} else {
+		mutex_unlock(&str_info->lock);
+		retval = -EBADRQC;
+		dev_dbg(sst_drv_ctx->dev, "SST DBG:BADQRC for stream\n");
+	}
+
+	return retval;
+}
diff --git a/sound/soc/jz4740/qi_lb60.c b/sound/soc/jz4740/qi_lb60.c
index 5cb91f9e8626..0fb7d2a91c3a 100644
--- a/sound/soc/jz4740/qi_lb60.c
+++ b/sound/soc/jz4740/qi_lb60.c
@@ -77,25 +77,18 @@ static int qi_lb60_probe(struct platform_device *pdev)
 {
 	struct qi_lb60 *qi_lb60;
 	struct snd_soc_card *card = &qi_lb60_card;
-	int ret;
 
 	qi_lb60 = devm_kzalloc(&pdev->dev, sizeof(*qi_lb60), GFP_KERNEL);
 	if (!qi_lb60)
 		return -ENOMEM;
 
-	qi_lb60->snd_gpio = devm_gpiod_get(&pdev->dev, "snd");
+	qi_lb60->snd_gpio = devm_gpiod_get(&pdev->dev, "snd", GPIOD_OUT_LOW);
 	if (IS_ERR(qi_lb60->snd_gpio))
 		return PTR_ERR(qi_lb60->snd_gpio);
-	ret = gpiod_direction_output(qi_lb60->snd_gpio, 0);
-	if (ret)
-		return ret;
 
-	qi_lb60->amp_gpio = devm_gpiod_get(&pdev->dev, "amp");
+	qi_lb60->amp_gpio = devm_gpiod_get(&pdev->dev, "amp", GPIOD_OUT_LOW);
 	if (IS_ERR(qi_lb60->amp_gpio))
 		return PTR_ERR(qi_lb60->amp_gpio);
-	ret = gpiod_direction_output(qi_lb60->amp_gpio, 0);
-	if (ret)
-		return ret;
 
 	card->dev = &pdev->dev;
 
diff --git a/sound/soc/mxs/mxs-saif.c b/sound/soc/mxs/mxs-saif.c
index 231d7e7b0711..83b2fea09219 100644
--- a/sound/soc/mxs/mxs-saif.c
+++ b/sound/soc/mxs/mxs-saif.c
@@ -773,7 +773,7 @@ static int mxs_saif_probe(struct platform_device *pdev)
 
 	saif->dev = &pdev->dev;
 	ret = devm_request_irq(&pdev->dev, saif->irq, mxs_saif_irq, 0,
-			       "mxs-saif", saif);
+			       dev_name(&pdev->dev), saif);
 	if (ret) {
 		dev_err(&pdev->dev, "failed to request irq\n");
 		return ret;
diff --git a/sound/soc/mxs/mxs-sgtl5000.c b/sound/soc/mxs/mxs-sgtl5000.c
index 61822cc53bd3..3bba6cfe4f29 100644
--- a/sound/soc/mxs/mxs-sgtl5000.c
+++ b/sound/soc/mxs/mxs-sgtl5000.c
@@ -49,13 +49,6 @@ static int mxs_sgtl5000_hw_params(struct snd_pcm_substream *substream,
 		break;
 	}
 
-	/* Sgtl5000 sysclk should be >= 8MHz and <= 27M */
-	if (mclk < 8000000 || mclk > 27000000) {
-		dev_err(codec_dai->dev, "Invalid mclk frequency: %u.%03uMHz\n",
-			mclk / 1000000, mclk / 1000 % 1000);
-		return -EINVAL;
-	}
-
 	/* Set SGTL5000's SYSCLK (provided by SAIF MCLK) */
 	ret = snd_soc_dai_set_sysclk(codec_dai, SGTL5000_SYSCLK, mclk, 0);
 	if (ret) {
diff --git a/sound/soc/nuc900/nuc900-ac97.c b/sound/soc/nuc900/nuc900-ac97.c
index f2f67942b229..dff443e4b657 100644
--- a/sound/soc/nuc900/nuc900-ac97.c
+++ b/sound/soc/nuc900/nuc900-ac97.c
@@ -298,7 +298,7 @@ static const struct snd_soc_dai_ops nuc900_ac97_dai_ops = {
 static struct snd_soc_dai_driver nuc900_ac97_dai = {
 	.probe			= nuc900_ac97_probe,
 	.remove			= nuc900_ac97_remove,
-	.ac97_control		= 1,
+	.bus_control		= true,
 	.playback = {
 		.rates		= SNDRV_PCM_RATE_8000_48000,
 		.formats	= SNDRV_PCM_FMTBIT_S16_LE,
diff --git a/sound/soc/omap/Kconfig b/sound/soc/omap/Kconfig
index d44463a7b0fa..2738b1984410 100644
--- a/sound/soc/omap/Kconfig
+++ b/sound/soc/omap/Kconfig
@@ -25,15 +25,15 @@ config SND_OMAP_SOC_N810
 	  Say Y if you want to add support for SoC audio on Nokia N810.
 
 config SND_OMAP_SOC_RX51
-	tristate "SoC Audio support for Nokia RX-51"
-	depends on SND_OMAP_SOC && ARM && (MACH_NOKIA_RX51 || COMPILE_TEST) && I2C
+	tristate "SoC Audio support for Nokia N900 (RX-51)"
+	depends on SND_OMAP_SOC && ARM && I2C
 	select SND_OMAP_SOC_MCBSP
 	select SND_SOC_TLV320AIC3X
 	select SND_SOC_TPA6130A2
 	depends on GPIOLIB
 	help
-	  Say Y if you want to add support for SoC audio on Nokia RX-51
-	  hardware. This is also known as Nokia N900 product.
+	  Say Y if you want to add support for SoC audio on Nokia N900
+	  cellphone.
 
 config SND_OMAP_SOC_AMS_DELTA
 	tristate "SoC Audio support for Amstrad E3 (Delta) videophone"
diff --git a/sound/soc/omap/mcbsp.c b/sound/soc/omap/mcbsp.c
index 86c75384c3c8..68a125205375 100644
--- a/sound/soc/omap/mcbsp.c
+++ b/sound/soc/omap/mcbsp.c
@@ -621,8 +621,7 @@ void omap_mcbsp_free(struct omap_mcbsp *mcbsp)
 	mcbsp->reg_cache = NULL;
 	spin_unlock(&mcbsp->lock);
 
-	if (reg_cache)
-		kfree(reg_cache);
+	kfree(reg_cache);
 }
 
 /*
diff --git a/sound/soc/pxa/mioa701_wm9713.c b/sound/soc/pxa/mioa701_wm9713.c
index 595eee341e90..a6b2be20cc0b 100644
--- a/sound/soc/pxa/mioa701_wm9713.c
+++ b/sound/soc/pxa/mioa701_wm9713.c
@@ -127,15 +127,12 @@ static const struct snd_soc_dapm_route audio_map[] = {
 static int mioa701_wm9713_init(struct snd_soc_pcm_runtime *rtd)
 {
 	struct snd_soc_codec *codec = rtd->codec;
-	unsigned short reg;
 
 	/* Prepare GPIO8 for rear speaker amplifier */
-	reg = codec->driver->read(codec, AC97_GPIO_CFG);
-	codec->driver->write(codec, AC97_GPIO_CFG, reg | 0x0100);
+	snd_soc_update_bits(codec, AC97_GPIO_CFG, 0x100, 0x100);
 
 	/* Prepare MIC input */
-	reg = codec->driver->read(codec, AC97_3D_CONTROL);
-	codec->driver->write(codec, AC97_3D_CONTROL, reg | 0xc000);
+	snd_soc_update_bits(codec, AC97_3D_CONTROL, 0xc000, 0xc000);
 
 	return 0;
 }
diff --git a/sound/soc/pxa/pxa-ssp.c b/sound/soc/pxa/pxa-ssp.c
index a8e097433074..cbba063a7210 100644
--- a/sound/soc/pxa/pxa-ssp.c
+++ b/sound/soc/pxa/pxa-ssp.c
@@ -97,7 +97,7 @@ static int pxa_ssp_startup(struct snd_pcm_substream *substream,
 	int ret = 0;
 
 	if (!cpu_dai->active) {
-		clk_enable(ssp->clk);
+		clk_prepare_enable(ssp->clk);
 		pxa_ssp_disable(ssp);
 	}
 
@@ -121,7 +121,7 @@ static void pxa_ssp_shutdown(struct snd_pcm_substream *substream,
 
 	if (!cpu_dai->active) {
 		pxa_ssp_disable(ssp);
-		clk_disable(ssp->clk);
+		clk_disable_unprepare(ssp->clk);
 	}
 
 	kfree(snd_soc_dai_get_dma_data(cpu_dai, substream));
@@ -136,7 +136,7 @@ static int pxa_ssp_suspend(struct snd_soc_dai *cpu_dai)
 	struct ssp_device *ssp = priv->ssp;
 
 	if (!cpu_dai->active)
-		clk_enable(ssp->clk);
+		clk_prepare_enable(ssp->clk);
 
 	priv->cr0 = __raw_readl(ssp->mmio_base + SSCR0);
 	priv->cr1 = __raw_readl(ssp->mmio_base + SSCR1);
@@ -144,7 +144,7 @@ static int pxa_ssp_suspend(struct snd_soc_dai *cpu_dai)
 	priv->psp = __raw_readl(ssp->mmio_base + SSPSP);
 
 	pxa_ssp_disable(ssp);
-	clk_disable(ssp->clk);
+	clk_disable_unprepare(ssp->clk);
 	return 0;
 }
 
@@ -154,7 +154,7 @@ static int pxa_ssp_resume(struct snd_soc_dai *cpu_dai)
 	struct ssp_device *ssp = priv->ssp;
 	uint32_t sssr = SSSR_ROR | SSSR_TUR | SSSR_BCE;
 
-	clk_enable(ssp->clk);
+	clk_prepare_enable(ssp->clk);
 
 	__raw_writel(sssr, ssp->mmio_base + SSSR);
 	__raw_writel(priv->cr0 & ~SSCR0_SSE, ssp->mmio_base + SSCR0);
@@ -165,7 +165,7 @@ static int pxa_ssp_resume(struct snd_soc_dai *cpu_dai)
 	if (cpu_dai->active)
 		pxa_ssp_enable(ssp);
 	else
-		clk_disable(ssp->clk);
+		clk_disable_unprepare(ssp->clk);
 
 	return 0;
 }
@@ -256,11 +256,11 @@ static int pxa_ssp_set_dai_sysclk(struct snd_soc_dai *cpu_dai,
 	/* The SSP clock must be disabled when changing SSP clock mode
 	 * on PXA2xx.  On PXA3xx it must be enabled when doing so. */
 	if (ssp->type != PXA3xx_SSP)
-		clk_disable(ssp->clk);
+		clk_disable_unprepare(ssp->clk);
 	val = pxa_ssp_read_reg(ssp, SSCR0) | sscr0;
 	pxa_ssp_write_reg(ssp, SSCR0, val);
 	if (ssp->type != PXA3xx_SSP)
-		clk_enable(ssp->clk);
+		clk_prepare_enable(ssp->clk);
 
 	return 0;
 }
diff --git a/sound/soc/pxa/pxa2xx-ac97.c b/sound/soc/pxa/pxa2xx-ac97.c
index ae956e3f4b9d..73ca2820c08c 100644
--- a/sound/soc/pxa/pxa2xx-ac97.c
+++ b/sound/soc/pxa/pxa2xx-ac97.c
@@ -157,7 +157,7 @@ static const struct snd_soc_dai_ops pxa_ac97_mic_dai_ops = {
 static struct snd_soc_dai_driver pxa_ac97_dai_driver[] = {
 {
 	.name = "pxa2xx-ac97",
-	.ac97_control = 1,
+	.bus_control = true,
 	.playback = {
 		.stream_name = "AC97 Playback",
 		.channels_min = 2,
@@ -174,7 +174,7 @@ static struct snd_soc_dai_driver pxa_ac97_dai_driver[] = {
 },
 {
 	.name = "pxa2xx-ac97-aux",
-	.ac97_control = 1,
+	.bus_control = true,
 	.playback = {
 		.stream_name = "AC97 Aux Playback",
 		.channels_min = 1,
@@ -191,7 +191,7 @@ static struct snd_soc_dai_driver pxa_ac97_dai_driver[] = {
 },
 {
 	.name = "pxa2xx-ac97-mic",
-	.ac97_control = 1,
+	.bus_control = true,
 	.capture = {
 		.stream_name = "AC97 Mic Capture",
 		.channels_min = 1,
diff --git a/sound/soc/pxa/spitz.c b/sound/soc/pxa/spitz.c
index 1373b017a951..d7d5fb20ea6f 100644
--- a/sound/soc/pxa/spitz.c
+++ b/sound/soc/pxa/spitz.c
@@ -305,19 +305,15 @@ static struct snd_soc_card snd_soc_spitz = {
 	.num_dapm_routes = ARRAY_SIZE(spitz_audio_map),
 };
 
-static struct platform_device *spitz_snd_device;
-
-static int __init spitz_init(void)
+static int spitz_probe(struct platform_device *pdev)
 {
+	struct snd_soc_card *card = &snd_soc_spitz;
 	int ret;
 
-	if (!(machine_is_spitz() || machine_is_borzoi() || machine_is_akita()))
-		return -ENODEV;
-
-	if (machine_is_borzoi() || machine_is_spitz())
-		spitz_mic_gpio = SPITZ_GPIO_MIC_BIAS;
-	else
+	if (machine_is_akita())
 		spitz_mic_gpio = AKITA_GPIO_MIC_BIAS;
+	else
+		spitz_mic_gpio = SPITZ_GPIO_MIC_BIAS;
 
 	ret = gpio_request(spitz_mic_gpio, "MIC GPIO");
 	if (ret)
@@ -327,37 +323,45 @@ static int __init spitz_init(void)
 	if (ret)
 		goto err2;
 
-	spitz_snd_device = platform_device_alloc("soc-audio", -1);
-	if (!spitz_snd_device) {
-		ret = -ENOMEM;
+	card->dev = &pdev->dev;
+
+	ret = snd_soc_register_card(card);
+	if (ret) {
+		dev_err(&pdev->dev, "snd_soc_register_card() failed: %d\n",
+			ret);
 		goto err2;
 	}
 
-	platform_set_drvdata(spitz_snd_device, &snd_soc_spitz);
-
-	ret = platform_device_add(spitz_snd_device);
-	if (ret)
-		goto err3;
-
 	return 0;
 
-err3:
-	platform_device_put(spitz_snd_device);
 err2:
 	gpio_free(spitz_mic_gpio);
 err1:
 	return ret;
 }
 
-static void __exit spitz_exit(void)
+static int spitz_remove(struct platform_device *pdev)
 {
-	platform_device_unregister(spitz_snd_device);
+	struct snd_soc_card *card = platform_get_drvdata(pdev);
+
+	snd_soc_unregister_card(card);
 	gpio_free(spitz_mic_gpio);
+	return 0;
 }
 
-module_init(spitz_init);
-module_exit(spitz_exit);
+static struct platform_driver spitz_driver = {
+	.driver		= {
+		.name	= "spitz-audio",
+		.owner	= THIS_MODULE,
+		.pm     = &snd_soc_pm_ops,
+	},
+	.probe		= spitz_probe,
+	.remove		= spitz_remove,
+};
+
+module_platform_driver(spitz_driver);
 
 MODULE_AUTHOR("Richard Purdie");
 MODULE_DESCRIPTION("ALSA SoC Spitz");
 MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:spitz-audio");
diff --git a/sound/soc/rockchip/Kconfig b/sound/soc/rockchip/Kconfig
index 78fc159559b0..e18182699d83 100644
--- a/sound/soc/rockchip/Kconfig
+++ b/sound/soc/rockchip/Kconfig
@@ -1,11 +1,16 @@
 config SND_SOC_ROCKCHIP
 	tristate "ASoC support for Rockchip"
 	depends on COMPILE_TEST || ARCH_ROCKCHIP
-	select SND_SOC_GENERIC_DMAENGINE_PCM
 	help
 	  Say Y or M if you want to add support for codecs attached to
 	  the Rockchip SoCs' Audio interfaces. You will also need to
 	  select the audio interfaces to support below.
 
 config SND_SOC_ROCKCHIP_I2S
-	tristate
+	tristate "Rockchip I2S Device Driver"
+	depends on CLKDEV_LOOKUP && SND_SOC_ROCKCHIP
+	select SND_SOC_GENERIC_DMAENGINE_PCM
+	help
+	  Say Y or M if you want to add support for I2S driver for
+	  Rockchip I2S device. The device supports upto maximum of
+	  8 channels each for play and record.
diff --git a/sound/soc/samsung/Kconfig b/sound/soc/samsung/Kconfig
index 55a38697443d..fc67f97f19f6 100644
--- a/sound/soc/samsung/Kconfig
+++ b/sound/soc/samsung/Kconfig
@@ -1,6 +1,6 @@
 config SND_SOC_SAMSUNG
 	tristate "ASoC support for Samsung"
-	depends on PLAT_SAMSUNG
+	depends on (PLAT_SAMSUNG || ARCH_EXYNOS)
 	depends on S3C64XX_PL080 || !ARCH_S3C64XX
 	depends on S3C24XX_DMAC || !ARCH_S3C24XX
 	select SND_SOC_GENERIC_DMAENGINE_PCM
@@ -239,3 +239,9 @@ config SND_SOC_ODROIDX2
 	select SND_SAMSUNG_I2S
 	help
 	  Say Y here to enable audio support for the Odroid-X2/U3.
+
+config SND_SOC_ARNDALE_RT5631_ALC5631
+        tristate "Audio support for RT5631(ALC5631) on Arndale Board"
+        depends on SND_SOC_SAMSUNG
+        select SND_SAMSUNG_I2S
+        select SND_SOC_RT5631
diff --git a/sound/soc/samsung/Makefile b/sound/soc/samsung/Makefile
index 91505ddaaf95..31e3dba7e3b5 100644
--- a/sound/soc/samsung/Makefile
+++ b/sound/soc/samsung/Makefile
@@ -45,6 +45,7 @@ snd-soc-lowland-objs := lowland.o
 snd-soc-littlemill-objs := littlemill.o
 snd-soc-bells-objs := bells.o
 snd-soc-odroidx2-max98090-objs := odroidx2_max98090.o
+snd-soc-arndale-rt5631-objs := arndale_rt5631.o
 
 obj-$(CONFIG_SND_SOC_SAMSUNG_JIVE_WM8750) += snd-soc-jive-wm8750.o
 obj-$(CONFIG_SND_SOC_SAMSUNG_NEO1973_WM8753) += snd-soc-neo1973-wm8753.o
@@ -71,3 +72,4 @@ obj-$(CONFIG_SND_SOC_LOWLAND) += snd-soc-lowland.o
 obj-$(CONFIG_SND_SOC_LITTLEMILL) += snd-soc-littlemill.o
 obj-$(CONFIG_SND_SOC_BELLS) += snd-soc-bells.o
 obj-$(CONFIG_SND_SOC_ODROIDX2) += snd-soc-odroidx2-max98090.o
+obj-$(CONFIG_SND_SOC_ARNDALE_RT5631_ALC5631) += snd-soc-arndale-rt5631.o
diff --git a/sound/soc/samsung/ac97.c b/sound/soc/samsung/ac97.c
index e1615113fd84..7952a625669d 100644
--- a/sound/soc/samsung/ac97.c
+++ b/sound/soc/samsung/ac97.c
@@ -288,7 +288,7 @@ static int s3c_ac97_mic_dai_probe(struct snd_soc_dai *dai)
 static struct snd_soc_dai_driver s3c_ac97_dai[] = {
 	[S3C_AC97_DAI_PCM] = {
 		.name =	"samsung-ac97",
-		.ac97_control = 1,
+		.bus_control = true,
 		.playback = {
 			.stream_name = "AC97 Playback",
 			.channels_min = 2,
@@ -306,7 +306,7 @@ static struct snd_soc_dai_driver s3c_ac97_dai[] = {
 	},
 	[S3C_AC97_DAI_MIC] = {
 		.name = "samsung-ac97-mic",
-		.ac97_control = 1,
+		.bus_control = true,
 		.capture = {
 			.stream_name = "AC97 Mic Capture",
 			.channels_min = 1,
diff --git a/sound/soc/samsung/arndale_rt5631.c b/sound/soc/samsung/arndale_rt5631.c
new file mode 100644
index 000000000000..1e2b61ca8db2
--- /dev/null
+++ b/sound/soc/samsung/arndale_rt5631.c
@@ -0,0 +1,150 @@
+/*
+ *  arndale_rt5631.c
+ *
+ *  Copyright (c) 2014, Insignal Co., Ltd.
+ *
+ *  Author: Claude <claude@insginal.co.kr>
+ *
+ *  This program is free software; you can redistribute  it and/or modify it
+ *  under  the terms of  the GNU General  Public License as published by the
+ *  Free Software Foundation;  either version 2 of the  License, or (at your
+ *  option) any later version.
+ */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+
+#include "i2s.h"
+
+static int arndale_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 *cpu_dai = rtd->cpu_dai;
+	struct snd_soc_dai *codec_dai = rtd->codec_dai;
+	int rfs, ret;
+	unsigned long rclk;
+
+	rfs = 256;
+
+	rclk = params_rate(params) * rfs;
+
+	ret = snd_soc_dai_set_sysclk(cpu_dai, SAMSUNG_I2S_CDCLK,
+					0, SND_SOC_CLOCK_OUT);
+	if (ret < 0)
+		return ret;
+
+	ret = snd_soc_dai_set_sysclk(cpu_dai, SAMSUNG_I2S_RCLKSRC_0,
+					0, SND_SOC_CLOCK_OUT);
+
+	if (ret < 0)
+		return ret;
+
+	ret = snd_soc_dai_set_sysclk(codec_dai, 0, rclk, SND_SOC_CLOCK_OUT);
+	if (ret < 0)
+		return ret;
+
+	return 0;
+}
+
+static struct snd_soc_ops arndale_ops = {
+	.hw_params = arndale_hw_params,
+};
+
+static struct snd_soc_dai_link arndale_rt5631_dai[] = {
+	{
+		.name = "RT5631 HiFi",
+		.stream_name = "Primary",
+		.codec_dai_name = "rt5631-hifi",
+		.dai_fmt = SND_SOC_DAIFMT_I2S
+			| SND_SOC_DAIFMT_NB_NF
+			| SND_SOC_DAIFMT_CBS_CFS,
+		.ops = &arndale_ops,
+	},
+};
+
+static struct snd_soc_card arndale_rt5631 = {
+	.name = "Arndale RT5631",
+	.dai_link = arndale_rt5631_dai,
+	.num_links = ARRAY_SIZE(arndale_rt5631_dai),
+};
+
+static int arndale_audio_probe(struct platform_device *pdev)
+{
+	int n, ret;
+	struct device_node *np = pdev->dev.of_node;
+	struct snd_soc_card *card = &arndale_rt5631;
+
+	card->dev = &pdev->dev;
+
+	for (n = 0; np && n < ARRAY_SIZE(arndale_rt5631_dai); n++) {
+		if (!arndale_rt5631_dai[n].cpu_dai_name) {
+			arndale_rt5631_dai[n].cpu_of_node = of_parse_phandle(np,
+					"samsung,audio-cpu", n);
+
+			if (!arndale_rt5631_dai[n].cpu_of_node) {
+				dev_err(&pdev->dev,
+				"Property 'samsung,audio-cpu' missing or invalid\n");
+				return -EINVAL;
+			}
+		}
+		if (!arndale_rt5631_dai[n].platform_name)
+			arndale_rt5631_dai[n].platform_of_node =
+					arndale_rt5631_dai[n].cpu_of_node;
+
+		arndale_rt5631_dai[n].codec_name = NULL;
+		arndale_rt5631_dai[n].codec_of_node = of_parse_phandle(np,
+					"samsung,audio-codec", n);
+		if (!arndale_rt5631_dai[0].codec_of_node) {
+			dev_err(&pdev->dev,
+			"Property 'samsung,audio-codec' missing or invalid\n");
+			return -EINVAL;
+		}
+	}
+
+	ret = devm_snd_soc_register_card(card->dev, card);
+
+	if (ret)
+		dev_err(&pdev->dev, "snd_soc_register_card() failed:%d\n", ret);
+
+	return ret;
+}
+
+static int arndale_audio_remove(struct platform_device *pdev)
+{
+	struct snd_soc_card *card = platform_get_drvdata(pdev);
+
+	snd_soc_unregister_card(card);
+
+	return 0;
+}
+
+static const struct of_device_id samsung_arndale_rt5631_of_match[] __maybe_unused = {
+	{ .compatible = "samsung,arndale-rt5631", },
+	{ .compatible = "samsung,arndale-alc5631", },
+	{},
+};
+MODULE_DEVICE_TABLE(of, samsung_arndale_rt5631_of_match);
+
+static struct platform_driver arndale_audio_driver = {
+	.driver = {
+		.name   = "arndale-audio",
+		.owner  = THIS_MODULE,
+		.pm = &snd_soc_pm_ops,
+		.of_match_table = of_match_ptr(samsung_arndale_rt5631_of_match),
+	},
+	.probe = arndale_audio_probe,
+	.remove = arndale_audio_remove,
+};
+
+module_platform_driver(arndale_audio_driver);
+
+MODULE_AUTHOR("Claude <claude@insignal.co.kr>");
+MODULE_DESCRIPTION("ALSA SoC Driver for Arndale Board");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/samsung/i2s-regs.h b/sound/soc/samsung/i2s-regs.h
index 821a50231002..9170c311d66e 100644
--- a/sound/soc/samsung/i2s-regs.h
+++ b/sound/soc/samsung/i2s-regs.h
@@ -33,8 +33,9 @@
 #define I2SLVL3ADDR	0x3c
 #define I2SSTR1		0x40
 #define I2SVER		0x44
-#define I2SFIC2		0x48
+#define I2SFIC1		0x48
 #define I2STDM		0x4c
+#define I2SFSTA		0x50
 
 #define CON_RSTCLR		(1 << 31)
 #define CON_FRXOFSTATUS		(1 << 26)
@@ -93,8 +94,6 @@
 #define MOD_BLC_24BIT		(2 << 13)
 #define MOD_BLC_MASK		(3 << 13)
 
-#define MOD_IMS_SYSMUX		(1 << 10)
-#define MOD_SLAVE		(1 << 11)
 #define MOD_TXONLY		(0 << 8)
 #define MOD_RXONLY		(1 << 8)
 #define MOD_TXRX		(2 << 8)
@@ -132,7 +131,10 @@
 #define EXYNOS5420_MOD_BCLK_256FS	8
 #define EXYNOS5420_MOD_BCLK_MASK	0xf
 
-#define MOD_CDCLKCON		(1 << 12)
+#define EXYNOS7_MOD_RCLK_64FS	4
+#define EXYNOS7_MOD_RCLK_128FS	5
+#define EXYNOS7_MOD_RCLK_96FS	6
+#define EXYNOS7_MOD_RCLK_192FS	7
 
 #define PSR_PSREN		(1 << 15)
 
diff --git a/sound/soc/samsung/i2s.c b/sound/soc/samsung/i2s.c
index 9d513473b300..c7aafcd95de3 100644
--- a/sound/soc/samsung/i2s.c
+++ b/sound/soc/samsung/i2s.c
@@ -36,9 +36,24 @@ enum samsung_dai_type {
 	TYPE_SEC,
 };
 
+struct samsung_i2s_variant_regs {
+	unsigned int	bfs_off;
+	unsigned int	rfs_off;
+	unsigned int	sdf_off;
+	unsigned int	txr_off;
+	unsigned int	rclksrc_off;
+	unsigned int	mss_off;
+	unsigned int	cdclkcon_off;
+	unsigned int	lrp_off;
+	unsigned int	bfs_mask;
+	unsigned int	rfs_mask;
+	unsigned int	ftx0cnt_off;
+};
+
 struct samsung_i2s_dai_data {
 	int dai_type;
 	u32 quirks;
+	const struct samsung_i2s_variant_regs *i2s_variant_regs;
 };
 
 struct i2s_dai {
@@ -81,6 +96,7 @@ struct i2s_dai {
 	u32	suspend_i2scon;
 	u32	suspend_i2spsr;
 	unsigned long gpios[7];	/* i2s gpio line numbers */
+	const struct samsung_i2s_variant_regs *variant_regs;
 };
 
 /* Lock for cross i/f checks */
@@ -95,7 +111,8 @@ static inline bool is_secondary(struct i2s_dai *i2s)
 /* If operating in SoC-Slave mode */
 static inline bool is_slave(struct i2s_dai *i2s)
 {
-	return (readl(i2s->addr + I2SMOD) & MOD_SLAVE) ? true : false;
+	u32 mod = readl(i2s->addr + I2SMOD);
+	return (mod & (1 << i2s->variant_regs->mss_off)) ? true : false;
 }
 
 /* If this interface of the controller is transmitting data */
@@ -200,14 +217,14 @@ static inline bool is_manager(struct i2s_dai *i2s)
 static inline unsigned get_rfs(struct i2s_dai *i2s)
 {
 	u32 rfs;
-
-	if (i2s->quirks & QUIRK_SUPPORTS_TDM)
-		rfs = readl(i2s->addr + I2SMOD) >> EXYNOS5420_MOD_RCLK_SHIFT;
-	else
-		rfs = (readl(i2s->addr + I2SMOD) >> MOD_RCLK_SHIFT);
-	rfs &= MOD_RCLK_MASK;
+	rfs = readl(i2s->addr + I2SMOD) >> i2s->variant_regs->rfs_off;
+	rfs &= i2s->variant_regs->rfs_mask;
 
 	switch (rfs) {
+	case 7: return 192;
+	case 6: return 96;
+	case 5: return 128;
+	case 4: return 64;
 	case 3:	return 768;
 	case 2: return 384;
 	case 1:	return 512;
@@ -219,15 +236,23 @@ static inline unsigned get_rfs(struct i2s_dai *i2s)
 static inline void set_rfs(struct i2s_dai *i2s, unsigned rfs)
 {
 	u32 mod = readl(i2s->addr + I2SMOD);
-	int rfs_shift;
+	int rfs_shift = i2s->variant_regs->rfs_off;
 
-	if (i2s->quirks & QUIRK_SUPPORTS_TDM)
-		rfs_shift = EXYNOS5420_MOD_RCLK_SHIFT;
-	else
-		rfs_shift = MOD_RCLK_SHIFT;
-	mod &= ~(MOD_RCLK_MASK << rfs_shift);
+	mod &= ~(i2s->variant_regs->rfs_mask << rfs_shift);
 
 	switch (rfs) {
+	case 192:
+		mod |= (EXYNOS7_MOD_RCLK_192FS << rfs_shift);
+		break;
+	case 96:
+		mod |= (EXYNOS7_MOD_RCLK_96FS << rfs_shift);
+		break;
+	case 128:
+		mod |= (EXYNOS7_MOD_RCLK_128FS << rfs_shift);
+		break;
+	case 64:
+		mod |= (EXYNOS7_MOD_RCLK_64FS << rfs_shift);
+		break;
 	case 768:
 		mod |= (MOD_RCLK_768FS << rfs_shift);
 		break;
@@ -249,14 +274,8 @@ static inline void set_rfs(struct i2s_dai *i2s, unsigned rfs)
 static inline unsigned get_bfs(struct i2s_dai *i2s)
 {
 	u32 bfs;
-
-	if (i2s->quirks & QUIRK_SUPPORTS_TDM) {
-		bfs = readl(i2s->addr + I2SMOD) >> EXYNOS5420_MOD_BCLK_SHIFT;
-		bfs &= EXYNOS5420_MOD_BCLK_MASK;
-	} else {
-		bfs =  readl(i2s->addr + I2SMOD) >> MOD_BCLK_SHIFT;
-		bfs &= MOD_BCLK_MASK;
-	}
+	bfs = readl(i2s->addr + I2SMOD) >> i2s->variant_regs->bfs_off;
+	bfs &= i2s->variant_regs->bfs_mask;
 
 	switch (bfs) {
 	case 8: return 256;
@@ -275,16 +294,8 @@ static inline unsigned get_bfs(struct i2s_dai *i2s)
 static inline void set_bfs(struct i2s_dai *i2s, unsigned bfs)
 {
 	u32 mod = readl(i2s->addr + I2SMOD);
-	int bfs_shift;
 	int tdm = i2s->quirks & QUIRK_SUPPORTS_TDM;
-
-	if (i2s->quirks & QUIRK_SUPPORTS_TDM) {
-		bfs_shift = EXYNOS5420_MOD_BCLK_SHIFT;
-		mod &= ~(EXYNOS5420_MOD_BCLK_MASK << bfs_shift);
-	} else {
-		bfs_shift = MOD_BCLK_SHIFT;
-		mod &= ~(MOD_BCLK_MASK << bfs_shift);
-	}
+	int bfs_shift = i2s->variant_regs->bfs_off;
 
 	/* Non-TDM I2S controllers do not support BCLK > 48 * FS */
 	if (!tdm && bfs > 48) {
@@ -292,6 +303,8 @@ static inline void set_bfs(struct i2s_dai *i2s, unsigned bfs)
 		return;
 	}
 
+	mod &= ~(i2s->variant_regs->bfs_mask << bfs_shift);
+
 	switch (bfs) {
 	case 48:
 		mod |= (MOD_BCLK_48FS << bfs_shift);
@@ -346,8 +359,9 @@ static inline int get_blc(struct i2s_dai *i2s)
 static void i2s_txctrl(struct i2s_dai *i2s, int on)
 {
 	void __iomem *addr = i2s->addr;
+	int txr_off = i2s->variant_regs->txr_off;
 	u32 con = readl(addr + I2SCON);
-	u32 mod = readl(addr + I2SMOD) & ~MOD_MASK;
+	u32 mod = readl(addr + I2SMOD) & ~(3 << txr_off);
 
 	if (on) {
 		con |= CON_ACTIVE;
@@ -362,9 +376,9 @@ static void i2s_txctrl(struct i2s_dai *i2s, int on)
 		}
 
 		if (any_rx_active(i2s))
-			mod |= MOD_TXRX;
+			mod |= 2 << txr_off;
 		else
-			mod |= MOD_TXONLY;
+			mod |= 0 << txr_off;
 	} else {
 		if (is_secondary(i2s)) {
 			con |=  CON_TXSDMA_PAUSE;
@@ -382,7 +396,7 @@ static void i2s_txctrl(struct i2s_dai *i2s, int on)
 		con |=  CON_TXCH_PAUSE;
 
 		if (any_rx_active(i2s))
-			mod |= MOD_RXONLY;
+			mod |= 1 << txr_off;
 		else
 			con &= ~CON_ACTIVE;
 	}
@@ -395,23 +409,24 @@ static void i2s_txctrl(struct i2s_dai *i2s, int on)
 static void i2s_rxctrl(struct i2s_dai *i2s, int on)
 {
 	void __iomem *addr = i2s->addr;
+	int txr_off = i2s->variant_regs->txr_off;
 	u32 con = readl(addr + I2SCON);
-	u32 mod = readl(addr + I2SMOD) & ~MOD_MASK;
+	u32 mod = readl(addr + I2SMOD) & ~(3 << txr_off);
 
 	if (on) {
 		con |= CON_RXDMA_ACTIVE | CON_ACTIVE;
 		con &= ~(CON_RXDMA_PAUSE | CON_RXCH_PAUSE);
 
 		if (any_tx_active(i2s))
-			mod |= MOD_TXRX;
+			mod |= 2 << txr_off;
 		else
-			mod |= MOD_RXONLY;
+			mod |= 1 << txr_off;
 	} else {
 		con |=  CON_RXDMA_PAUSE | CON_RXCH_PAUSE;
 		con &= ~CON_RXDMA_ACTIVE;
 
 		if (any_tx_active(i2s))
-			mod |= MOD_TXONLY;
+			mod |= 0 << txr_off;
 		else
 			con &= ~CON_ACTIVE;
 	}
@@ -451,6 +466,9 @@ static int i2s_set_sysclk(struct snd_soc_dai *dai,
 	struct i2s_dai *i2s = to_info(dai);
 	struct i2s_dai *other = i2s->pri_dai ? : i2s->sec_dai;
 	u32 mod = readl(i2s->addr + I2SMOD);
+	const struct samsung_i2s_variant_regs *i2s_regs = i2s->variant_regs;
+	unsigned int cdcon_mask = 1 << i2s_regs->cdclkcon_off;
+	unsigned int rsrc_mask = 1 << i2s_regs->rclksrc_off;
 
 	switch (clk_id) {
 	case SAMSUNG_I2S_OPCLK:
@@ -465,18 +483,18 @@ static int i2s_set_sysclk(struct snd_soc_dai *dai,
 		if ((rfs && other && other->rfs && (other->rfs != rfs)) ||
 				(any_active(i2s) &&
 				(((dir == SND_SOC_CLOCK_IN)
-					&& !(mod & MOD_CDCLKCON)) ||
+					&& !(mod & cdcon_mask)) ||
 				((dir == SND_SOC_CLOCK_OUT)
-					&& (mod & MOD_CDCLKCON))))) {
+					&& (mod & cdcon_mask))))) {
 			dev_err(&i2s->pdev->dev,
 				"%s:%d Other DAI busy\n", __func__, __LINE__);
 			return -EAGAIN;
 		}
 
 		if (dir == SND_SOC_CLOCK_IN)
-			mod |= MOD_CDCLKCON;
+			mod |= 1 << i2s_regs->cdclkcon_off;
 		else
-			mod &= ~MOD_CDCLKCON;
+			mod &= ~(1 << i2s_regs->cdclkcon_off);
 
 		i2s->rfs = rfs;
 		break;
@@ -491,8 +509,8 @@ static int i2s_set_sysclk(struct snd_soc_dai *dai,
 
 		if (!any_active(i2s)) {
 			if (i2s->op_clk && !IS_ERR(i2s->op_clk)) {
-				if ((clk_id && !(mod & MOD_IMS_SYSMUX)) ||
-					(!clk_id && (mod & MOD_IMS_SYSMUX))) {
+				if ((clk_id && !(mod & rsrc_mask)) ||
+					(!clk_id && (mod & rsrc_mask))) {
 					clk_disable_unprepare(i2s->op_clk);
 					clk_put(i2s->op_clk);
 				} else {
@@ -520,8 +538,8 @@ static int i2s_set_sysclk(struct snd_soc_dai *dai,
 				other->op_clk = i2s->op_clk;
 				other->rclk_srcrate = i2s->rclk_srcrate;
 			}
-		} else if ((!clk_id && (mod & MOD_IMS_SYSMUX))
-				|| (clk_id && !(mod & MOD_IMS_SYSMUX))) {
+		} else if ((!clk_id && (mod & rsrc_mask))
+				|| (clk_id && !(mod & rsrc_mask))) {
 			dev_err(&i2s->pdev->dev,
 				"%s:%d Other DAI busy\n", __func__, __LINE__);
 			return -EAGAIN;
@@ -533,11 +551,11 @@ static int i2s_set_sysclk(struct snd_soc_dai *dai,
 		}
 
 		if (clk_id == 0)
-			mod &= ~MOD_IMS_SYSMUX;
+			mod &= ~(1 << i2s_regs->rclksrc_off);
 		else
-			mod |= MOD_IMS_SYSMUX;
-		break;
+			mod |= 1 << i2s_regs->rclksrc_off;
 
+		break;
 	default:
 		dev_err(&i2s->pdev->dev, "We don't serve that!\n");
 		return -EINVAL;
@@ -553,16 +571,12 @@ static int i2s_set_fmt(struct snd_soc_dai *dai,
 {
 	struct i2s_dai *i2s = to_info(dai);
 	u32 mod = readl(i2s->addr + I2SMOD);
-	int lrp_shift, sdf_shift, sdf_mask, lrp_rlow;
+	int lrp_shift, sdf_shift, sdf_mask, lrp_rlow, mod_slave;
 	u32 tmp = 0;
 
-	if (i2s->quirks & QUIRK_SUPPORTS_TDM) {
-		lrp_shift = EXYNOS5420_MOD_LRP_SHIFT;
-		sdf_shift = EXYNOS5420_MOD_SDF_SHIFT;
-	} else {
-		lrp_shift = MOD_LRP_SHIFT;
-		sdf_shift = MOD_SDF_SHIFT;
-	}
+	lrp_shift = i2s->variant_regs->lrp_off;
+	sdf_shift = i2s->variant_regs->sdf_off;
+	mod_slave = 1 << i2s->variant_regs->mss_off;
 
 	sdf_mask = MOD_SDF_MASK << sdf_shift;
 	lrp_rlow = MOD_LR_RLOW << lrp_shift;
@@ -605,7 +619,7 @@ static int i2s_set_fmt(struct snd_soc_dai *dai,
 
 	switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
 	case SND_SOC_DAIFMT_CBM_CFM:
-		tmp |= MOD_SLAVE;
+		tmp |= mod_slave;
 		break;
 	case SND_SOC_DAIFMT_CBS_CFS:
 		/* Set default source clock in Master mode */
@@ -623,13 +637,13 @@ static int i2s_set_fmt(struct snd_soc_dai *dai,
 	 * channel.
 	 */
 	if (any_active(i2s) &&
-		((mod & (sdf_mask | lrp_rlow | MOD_SLAVE)) != tmp)) {
+		((mod & (sdf_mask | lrp_rlow | mod_slave)) != tmp)) {
 		dev_err(&i2s->pdev->dev,
 				"%s:%d Other DAI busy\n", __func__, __LINE__);
 		return -EAGAIN;
 	}
 
-	mod &= ~(sdf_mask | lrp_rlow | MOD_SLAVE);
+	mod &= ~(sdf_mask | lrp_rlow | mod_slave);
 	mod |= tmp;
 	writel(mod, i2s->addr + I2SMOD);
 
@@ -751,6 +765,7 @@ static void i2s_shutdown(struct snd_pcm_substream *substream,
 	struct i2s_dai *i2s = to_info(dai);
 	struct i2s_dai *other = i2s->pri_dai ? : i2s->sec_dai;
 	unsigned long flags;
+	const struct samsung_i2s_variant_regs *i2s_regs = i2s->variant_regs;
 
 	spin_lock_irqsave(&lock, flags);
 
@@ -761,7 +776,7 @@ static void i2s_shutdown(struct snd_pcm_substream *substream,
 		other->mode |= DAI_MANAGER;
 	} else {
 		u32 mod = readl(i2s->addr + I2SMOD);
-		i2s->cdclk_out = !(mod & MOD_CDCLKCON);
+		i2s->cdclk_out = !(mod & (1 << i2s_regs->cdclkcon_off));
 		if (other)
 			other->cdclk_out = i2s->cdclk_out;
 	}
@@ -914,13 +929,14 @@ i2s_delay(struct snd_pcm_substream *substream, struct snd_soc_dai *dai)
 	struct i2s_dai *i2s = to_info(dai);
 	u32 reg = readl(i2s->addr + I2SFIC);
 	snd_pcm_sframes_t delay;
+	const struct samsung_i2s_variant_regs *i2s_regs = i2s->variant_regs;
 
 	if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
 		delay = FIC_RXCOUNT(reg);
 	else if (is_secondary(i2s))
 		delay = FICS_TXCOUNT(readl(i2s->addr + I2SFICS));
 	else
-		delay = FIC_TXCOUNT(reg);
+		delay = (reg >> i2s_regs->ftx0cnt_off) & 0x7f;
 
 	return delay;
 }
@@ -956,6 +972,7 @@ static int samsung_i2s_dai_probe(struct snd_soc_dai *dai)
 {
 	struct i2s_dai *i2s = to_info(dai);
 	struct i2s_dai *other = i2s->pri_dai ? : i2s->sec_dai;
+	int ret;
 
 	if (other && other->clk) { /* If this is probe on secondary */
 		samsung_asoc_init_dma_data(dai, &other->sec_dai->dma_playback,
@@ -973,9 +990,14 @@ static int samsung_i2s_dai_probe(struct snd_soc_dai *dai)
 	if (IS_ERR(i2s->clk)) {
 		dev_err(&i2s->pdev->dev, "failed to get i2s_clock\n");
 		iounmap(i2s->addr);
-		return -ENOENT;
+		return PTR_ERR(i2s->clk);
+	}
+
+	ret = clk_prepare_enable(i2s->clk);
+	if (ret != 0) {
+		dev_err(&i2s->pdev->dev, "failed to enable clock: %d\n", ret);
+		return ret;
 	}
-	clk_prepare_enable(i2s->clk);
 
 	samsung_asoc_init_dma_data(dai, &i2s->dma_playback, &i2s->dma_capture);
 
@@ -987,7 +1009,7 @@ static int samsung_i2s_dai_probe(struct snd_soc_dai *dai)
 	if (i2s->quirks & QUIRK_NEED_RSTCLR)
 		writel(CON_RSTCLR, i2s->addr + I2SCON);
 
-	if (i2s->quirks & QUIRK_SEC_DAI)
+	if (i2s->quirks & QUIRK_SUPPORTS_IDMA)
 		idma_reg_addr_init(i2s->addr,
 					i2s->sec_dai->idma_playback.dma_addr);
 
@@ -1199,10 +1221,9 @@ static int samsung_i2s_probe(struct platform_device *pdev)
 		quirks = i2s_dai_data->quirks;
 		if (of_property_read_u32(np, "samsung,idma-addr",
 					 &idma_addr)) {
-			if (quirks & QUIRK_SEC_DAI) {
-				dev_err(&pdev->dev, "idma address is not"\
+			if (quirks & QUIRK_SUPPORTS_IDMA) {
+				dev_info(&pdev->dev, "idma address is not"\
 						"specified");
-				return -EINVAL;
 			}
 		}
 	}
@@ -1228,6 +1249,7 @@ static int samsung_i2s_probe(struct platform_device *pdev)
 	pri_dai->dma_capture.dma_size = 4;
 	pri_dai->base = regs_base;
 	pri_dai->quirks = quirks;
+	pri_dai->variant_regs = i2s_dai_data->i2s_variant_regs;
 
 	if (quirks & QUIRK_PRI_6CHAN)
 		pri_dai->i2s_dai_drv.playback.channels_max = 6;
@@ -1302,20 +1324,93 @@ static int samsung_i2s_remove(struct platform_device *pdev)
 	return 0;
 }
 
+static const struct samsung_i2s_variant_regs i2sv3_regs = {
+	.bfs_off = 1,
+	.rfs_off = 3,
+	.sdf_off = 5,
+	.txr_off = 8,
+	.rclksrc_off = 10,
+	.mss_off = 11,
+	.cdclkcon_off = 12,
+	.lrp_off = 7,
+	.bfs_mask = 0x3,
+	.rfs_mask = 0x3,
+	.ftx0cnt_off = 8,
+};
+
+static const struct samsung_i2s_variant_regs i2sv6_regs = {
+	.bfs_off = 0,
+	.rfs_off = 4,
+	.sdf_off = 6,
+	.txr_off = 8,
+	.rclksrc_off = 10,
+	.mss_off = 11,
+	.cdclkcon_off = 12,
+	.lrp_off = 15,
+	.bfs_mask = 0xf,
+	.rfs_mask = 0x3,
+	.ftx0cnt_off = 8,
+};
+
+static const struct samsung_i2s_variant_regs i2sv7_regs = {
+	.bfs_off = 0,
+	.rfs_off = 4,
+	.sdf_off = 7,
+	.txr_off = 9,
+	.rclksrc_off = 11,
+	.mss_off = 12,
+	.cdclkcon_off = 22,
+	.lrp_off = 15,
+	.bfs_mask = 0xf,
+	.rfs_mask = 0x7,
+	.ftx0cnt_off = 0,
+};
+
+static const struct samsung_i2s_variant_regs i2sv5_i2s1_regs = {
+	.bfs_off = 0,
+	.rfs_off = 3,
+	.sdf_off = 6,
+	.txr_off = 8,
+	.rclksrc_off = 10,
+	.mss_off = 11,
+	.cdclkcon_off = 12,
+	.lrp_off = 15,
+	.bfs_mask = 0x7,
+	.rfs_mask = 0x7,
+	.ftx0cnt_off = 8,
+};
+
 static const struct samsung_i2s_dai_data i2sv3_dai_type = {
 	.dai_type = TYPE_PRI,
 	.quirks = QUIRK_NO_MUXPSR,
+	.i2s_variant_regs = &i2sv3_regs,
 };
 
 static const struct samsung_i2s_dai_data i2sv5_dai_type = {
 	.dai_type = TYPE_PRI,
-	.quirks = QUIRK_PRI_6CHAN | QUIRK_SEC_DAI | QUIRK_NEED_RSTCLR,
+	.quirks = QUIRK_PRI_6CHAN | QUIRK_SEC_DAI | QUIRK_NEED_RSTCLR |
+			QUIRK_SUPPORTS_IDMA,
+	.i2s_variant_regs = &i2sv3_regs,
 };
 
 static const struct samsung_i2s_dai_data i2sv6_dai_type = {
 	.dai_type = TYPE_PRI,
 	.quirks = QUIRK_PRI_6CHAN | QUIRK_SEC_DAI | QUIRK_NEED_RSTCLR |
+			QUIRK_SUPPORTS_TDM | QUIRK_SUPPORTS_IDMA,
+	.i2s_variant_regs = &i2sv6_regs,
+};
+
+static const struct samsung_i2s_dai_data i2sv7_dai_type = {
+	.dai_type = TYPE_PRI,
+	.quirks = QUIRK_PRI_6CHAN | QUIRK_SEC_DAI | QUIRK_NEED_RSTCLR |
 			QUIRK_SUPPORTS_TDM,
+	.i2s_variant_regs = &i2sv7_regs,
+};
+
+static const struct samsung_i2s_dai_data i2sv5_dai_type_i2s1 = {
+	.dai_type = TYPE_PRI,
+	.quirks = QUIRK_PRI_6CHAN | QUIRK_NEED_RSTCLR,
+	.i2s_variant_regs = &i2sv5_i2s1_regs,
 };
 
 static const struct samsung_i2s_dai_data samsung_dai_type_pri = {
@@ -1329,10 +1424,13 @@ static const struct samsung_i2s_dai_data samsung_dai_type_sec = {
 static struct platform_device_id samsung_i2s_driver_ids[] = {
 	{
 		.name           = "samsung-i2s",
-		.driver_data    = (kernel_ulong_t)&samsung_dai_type_pri,
+		.driver_data	= (kernel_ulong_t)&i2sv3_dai_type,
 	}, {
 		.name           = "samsung-i2s-sec",
 		.driver_data    = (kernel_ulong_t)&samsung_dai_type_sec,
+	}, {
+		.name		= "samsung-i2sv4",
+		.driver_data	= (kernel_ulong_t)&i2sv5_dai_type,
 	},
 	{},
 };
@@ -1349,6 +1447,12 @@ static const struct of_device_id exynos_i2s_match[] = {
 	}, {
 		.compatible = "samsung,exynos5420-i2s",
 		.data = &i2sv6_dai_type,
+	}, {
+		.compatible = "samsung,exynos7-i2s",
+		.data = &i2sv7_dai_type,
+	}, {
+		.compatible = "samsung,exynos7-i2s1",
+		.data = &i2sv5_dai_type_i2s1,
 	},
 	{},
 };
diff --git a/sound/soc/samsung/odroidx2_max98090.c b/sound/soc/samsung/odroidx2_max98090.c
index 3c8f60423e82..d7640e72cb1d 100644
--- a/sound/soc/samsung/odroidx2_max98090.c
+++ b/sound/soc/samsung/odroidx2_max98090.c
@@ -153,8 +153,8 @@ static int odroidx2_audio_remove(struct platform_device *pdev)
 
 	snd_soc_unregister_card(card);
 
-	of_node_put((struct device_node *)odroidx2_dai[0].cpu_of_node);
-	of_node_put((struct device_node *)odroidx2_dai[0].codec_of_node);
+	of_node_put(odroidx2_dai[0].cpu_of_node);
+	of_node_put(odroidx2_dai[0].codec_of_node);
 
 	return 0;
 }
diff --git a/sound/soc/sh/fsi.c b/sound/soc/sh/fsi.c
index 88e5df474ccf..8869971d7884 100644
--- a/sound/soc/sh/fsi.c
+++ b/sound/soc/sh/fsi.c
@@ -842,12 +842,9 @@ static int fsi_clk_disable(struct device *dev,
 		return -EINVAL;
 
 	if (1 == clock->count--) {
-		if (clock->xck)
-			clk_disable(clock->xck);
-		if (clock->ick)
-			clk_disable(clock->ick);
-		if (clock->div)
-			clk_disable(clock->div);
+		clk_disable(clock->xck);
+		clk_disable(clock->ick);
+		clk_disable(clock->div);
 	}
 
 	return 0;
diff --git a/sound/soc/sh/hac.c b/sound/soc/sh/hac.c
index 0af2e4dfd139..d5f567e085ff 100644
--- a/sound/soc/sh/hac.c
+++ b/sound/soc/sh/hac.c
@@ -272,7 +272,7 @@ static const struct snd_soc_dai_ops hac_dai_ops = {
 static struct snd_soc_dai_driver sh4_hac_dai[] = {
 {
 	.name			= "hac-dai.0",
-	.ac97_control		= 1,
+	.bus_control		= true,
 	.playback = {
 		.rates		= AC97_RATES,
 		.formats	= AC97_FMTS,
diff --git a/sound/soc/sh/rcar/adg.c b/sound/soc/sh/rcar/adg.c
index fc41a0e8b09f..14d1a7193469 100644
--- a/sound/soc/sh/rcar/adg.c
+++ b/sound/soc/sh/rcar/adg.c
@@ -430,7 +430,7 @@ int rsnd_adg_probe(struct platform_device *pdev,
 	adg->clk[CLKI]	= devm_clk_get(dev, "clk_i");
 
 	for_each_rsnd_clk(clk, adg, i)
-		dev_dbg(dev, "clk %d : %p\n", i, clk);
+		dev_dbg(dev, "clk %d : %p : %ld\n", i, clk, clk_get_rate(clk));
 
 	rsnd_adg_ssi_clk_init(priv, adg);
 
diff --git a/sound/soc/sh/rcar/core.c b/sound/soc/sh/rcar/core.c
index 70042197f9e2..75308bbc2ce8 100644
--- a/sound/soc/sh/rcar/core.c
+++ b/sound/soc/sh/rcar/core.c
@@ -349,7 +349,7 @@ int rsnd_dma_init(struct rsnd_priv *priv, struct rsnd_dma *dma,
 						     dma_name);
 	if (!dma->chan) {
 		dev_err(dev, "can't get dma channel\n");
-		return -EIO;
+		goto rsnd_dma_channel_err;
 	}
 
 	ret = dmaengine_slave_config(dma->chan, &cfg);
@@ -363,8 +363,15 @@ int rsnd_dma_init(struct rsnd_priv *priv, struct rsnd_dma *dma,
 
 rsnd_dma_init_err:
 	rsnd_dma_quit(priv, dma);
+rsnd_dma_channel_err:
 
-	return ret;
+	/*
+	 * DMA failed. try to PIO mode
+	 * see
+	 *	rsnd_ssi_fallback()
+	 *	rsnd_rdai_continuance_probe()
+	 */
+	return -EAGAIN;
 }
 
 void  rsnd_dma_quit(struct rsnd_priv *priv,
@@ -409,9 +416,16 @@ u32 rsnd_get_adinr(struct rsnd_mod *mod)
 ({								\
 	struct rsnd_priv *priv = rsnd_mod_to_priv(mod);		\
 	struct device *dev = rsnd_priv_to_dev(priv);		\
-	dev_dbg(dev, "%s [%d] %s\n",				\
-		rsnd_mod_name(mod), rsnd_mod_id(mod), #func);	\
-	(mod)->ops->func(mod, rdai);				\
+	u32 mask = 1 << __rsnd_mod_shift_##func;			\
+	u32 call = __rsnd_mod_call_##func << __rsnd_mod_shift_##func;	\
+	int ret = 0;							\
+	if ((mod->status & mask) == call) {				\
+		dev_dbg(dev, "%s[%d] %s\n",				\
+			rsnd_mod_name(mod), rsnd_mod_id(mod), #func);	\
+		ret = (mod)->ops->func(mod, rdai);			\
+		mod->status = (mod->status & ~mask) | (~call & mask);	\
+	}								\
+	ret;								\
 })
 
 #define rsnd_mod_call(mod, func, rdai...)	\
@@ -456,6 +470,13 @@ static int rsnd_dai_connect(struct rsnd_mod *mod,
 	return 0;
 }
 
+static void rsnd_dai_disconnect(struct rsnd_mod *mod,
+				struct rsnd_dai_stream *io)
+{
+	mod->io = NULL;
+	io->mod[mod->type] = NULL;
+}
+
 int rsnd_dai_id(struct rsnd_priv *priv, struct rsnd_dai *rdai)
 {
 	int id = rdai - priv->rdai;
@@ -686,6 +707,20 @@ static const struct snd_soc_dai_ops rsnd_soc_dai_ops = {
 	ret;							\
 })
 
+#define rsnd_path_break(priv, io, type)				\
+{								\
+	struct rsnd_mod *mod;					\
+	int id = -1;						\
+								\
+	if (rsnd_is_enable_path(io, type)) {			\
+		id = rsnd_info_id(priv, io, type);		\
+		if (id >= 0) {					\
+			mod = rsnd_##type##_mod_get(priv, id);	\
+			rsnd_dai_disconnect(mod, io);		\
+		}						\
+	}							\
+}
+
 static int rsnd_path_init(struct rsnd_priv *priv,
 			  struct rsnd_dai *rdai,
 			  struct rsnd_dai_stream *io)
@@ -934,6 +969,150 @@ static struct snd_pcm_ops rsnd_pcm_ops = {
 };
 
 /*
+ *		snd_kcontrol
+ */
+#define kcontrol_to_cfg(kctrl) ((struct rsnd_kctrl_cfg *)kctrl->private_value)
+static int rsnd_kctrl_info(struct snd_kcontrol *kctrl,
+			   struct snd_ctl_elem_info *uinfo)
+{
+	struct rsnd_kctrl_cfg *cfg = kcontrol_to_cfg(kctrl);
+
+	if (cfg->texts) {
+		uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+		uinfo->count = cfg->size;
+		uinfo->value.enumerated.items = cfg->max;
+		if (uinfo->value.enumerated.item >= cfg->max)
+			uinfo->value.enumerated.item = cfg->max - 1;
+		strlcpy(uinfo->value.enumerated.name,
+			cfg->texts[uinfo->value.enumerated.item],
+			sizeof(uinfo->value.enumerated.name));
+	} else {
+		uinfo->count = cfg->size;
+		uinfo->value.integer.min = 0;
+		uinfo->value.integer.max = cfg->max;
+		uinfo->type = (cfg->max == 1) ?
+			SNDRV_CTL_ELEM_TYPE_BOOLEAN :
+			SNDRV_CTL_ELEM_TYPE_INTEGER;
+	}
+
+	return 0;
+}
+
+static int rsnd_kctrl_get(struct snd_kcontrol *kctrl,
+			  struct snd_ctl_elem_value *uc)
+{
+	struct rsnd_kctrl_cfg *cfg = kcontrol_to_cfg(kctrl);
+	int i;
+
+	for (i = 0; i < cfg->size; i++)
+		if (cfg->texts)
+			uc->value.enumerated.item[i] = cfg->val[i];
+		else
+			uc->value.integer.value[i] = cfg->val[i];
+
+	return 0;
+}
+
+static int rsnd_kctrl_put(struct snd_kcontrol *kctrl,
+			  struct snd_ctl_elem_value *uc)
+{
+	struct rsnd_mod *mod = snd_kcontrol_chip(kctrl);
+	struct rsnd_kctrl_cfg *cfg = kcontrol_to_cfg(kctrl);
+	int i, change = 0;
+
+	for (i = 0; i < cfg->size; i++) {
+		if (cfg->texts) {
+			change |= (uc->value.enumerated.item[i] != cfg->val[i]);
+			cfg->val[i] = uc->value.enumerated.item[i];
+		} else {
+			change |= (uc->value.integer.value[i] != cfg->val[i]);
+			cfg->val[i] = uc->value.integer.value[i];
+		}
+	}
+
+	if (change)
+		cfg->update(mod);
+
+	return change;
+}
+
+static int __rsnd_kctrl_new(struct rsnd_mod *mod,
+			    struct rsnd_dai *rdai,
+			    struct snd_soc_pcm_runtime *rtd,
+			    const unsigned char *name,
+			    struct rsnd_kctrl_cfg *cfg,
+			    void (*update)(struct rsnd_mod *mod))
+{
+	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,
+		.get		= rsnd_kctrl_get,
+		.put		= rsnd_kctrl_put,
+		.private_value	= (unsigned long)cfg,
+	};
+	int ret;
+
+	kctrl = snd_ctl_new1(&knew, mod);
+	if (!kctrl)
+		return -ENOMEM;
+
+	ret = snd_ctl_add(card, kctrl);
+	if (ret < 0)
+		return ret;
+
+	cfg->update = update;
+
+	return 0;
+}
+
+int rsnd_kctrl_new_m(struct rsnd_mod *mod,
+		     struct rsnd_dai *rdai,
+		     struct snd_soc_pcm_runtime *rtd,
+		     const unsigned char *name,
+		     void (*update)(struct rsnd_mod *mod),
+		     struct rsnd_kctrl_cfg_m *_cfg,
+		     u32 max)
+{
+	_cfg->cfg.max	= max;
+	_cfg->cfg.size	= RSND_DVC_CHANNELS;
+	_cfg->cfg.val	= _cfg->val;
+	return __rsnd_kctrl_new(mod, rdai, rtd, name, &_cfg->cfg, update);
+}
+
+int rsnd_kctrl_new_s(struct rsnd_mod *mod,
+		     struct rsnd_dai *rdai,
+		     struct snd_soc_pcm_runtime *rtd,
+		     const unsigned char *name,
+		     void (*update)(struct rsnd_mod *mod),
+		     struct rsnd_kctrl_cfg_s *_cfg,
+		     u32 max)
+{
+	_cfg->cfg.max	= max;
+	_cfg->cfg.size	= 1;
+	_cfg->cfg.val	= &_cfg->val;
+	return __rsnd_kctrl_new(mod, rdai, rtd, name, &_cfg->cfg, update);
+}
+
+int rsnd_kctrl_new_e(struct rsnd_mod *mod,
+		     struct rsnd_dai *rdai,
+		     struct snd_soc_pcm_runtime *rtd,
+		     const unsigned char *name,
+		     struct rsnd_kctrl_cfg_s *_cfg,
+		     void (*update)(struct rsnd_mod *mod),
+		     const char * const *texts,
+		     u32 max)
+{
+	_cfg->cfg.max	= max;
+	_cfg->cfg.size	= 1;
+	_cfg->cfg.val	= &_cfg->val;
+	_cfg->cfg.texts	= texts;
+	return __rsnd_kctrl_new(mod, rdai, rtd, name, &_cfg->cfg, update);
+}
+
+/*
  *		snd_soc_platform
  */
 
@@ -976,6 +1155,49 @@ static const struct snd_soc_component_driver rsnd_soc_component = {
 	.name		= "rsnd",
 };
 
+static int rsnd_rdai_continuance_probe(struct rsnd_priv *priv,
+				       struct rsnd_dai *rdai,
+				       int is_play)
+{
+	struct rsnd_dai_stream *io = is_play ? &rdai->playback : &rdai->capture;
+	int ret;
+
+	ret = rsnd_dai_call(probe, io, rdai);
+	if (ret == -EAGAIN) {
+		/*
+		 * Fallback to PIO mode
+		 */
+
+		/*
+		 * call "remove" for SSI/SRC/DVC
+		 * SSI will be switch to PIO mode if it was DMA mode
+		 * see
+		 *	rsnd_dma_init()
+		 *	rsnd_ssi_fallback()
+		 */
+		rsnd_dai_call(remove, io, rdai);
+
+		/*
+		 * remove SRC/DVC from DAI,
+		 */
+		rsnd_path_break(priv, io, src);
+		rsnd_path_break(priv, io, dvc);
+
+		/*
+		 * fallback
+		 */
+		rsnd_dai_call(fallback, io, rdai);
+
+		/*
+		 * retry to "probe".
+		 * DAI has SSI which is PIO mode only now.
+		 */
+		ret = rsnd_dai_call(probe, io, rdai);
+	}
+
+	return ret;
+}
+
 /*
  *	rsnd probe
  */
@@ -1037,11 +1259,11 @@ static int rsnd_probe(struct platform_device *pdev)
 	}
 
 	for_each_rsnd_dai(rdai, priv, i) {
-		ret = rsnd_dai_call(probe, &rdai->playback, rdai);
+		ret = rsnd_rdai_continuance_probe(priv, rdai, 1);
 		if (ret)
 			goto exit_snd_probe;
 
-		ret = rsnd_dai_call(probe, &rdai->capture, rdai);
+		ret = rsnd_rdai_continuance_probe(priv, rdai, 0);
 		if (ret)
 			goto exit_snd_probe;
 	}
diff --git a/sound/soc/sh/rcar/dvc.c b/sound/soc/sh/rcar/dvc.c
index 3f443930c2b1..5380a4827ba7 100644
--- a/sound/soc/sh/rcar/dvc.c
+++ b/sound/soc/sh/rcar/dvc.c
@@ -11,8 +11,6 @@
 #include "rsnd.h"
 
 #define RSND_DVC_NAME_SIZE	16
-#define RSND_DVC_VOLUME_MAX	100
-#define RSND_DVC_VOLUME_NUM	2
 
 #define DVC_NAME "dvc"
 
@@ -20,8 +18,11 @@ struct rsnd_dvc {
 	struct rsnd_dvc_platform_info *info; /* rcar_snd.h */
 	struct rsnd_mod mod;
 	struct clk *clk;
-	u8 volume[RSND_DVC_VOLUME_NUM];
-	u8 mute[RSND_DVC_VOLUME_NUM];
+	struct rsnd_kctrl_cfg_m volume;
+	struct rsnd_kctrl_cfg_m mute;
+	struct rsnd_kctrl_cfg_s ren;	/* Ramp Enable */
+	struct rsnd_kctrl_cfg_s rup;	/* Ramp Rate Up */
+	struct rsnd_kctrl_cfg_s rdown;	/* Ramp Rate Down */
 };
 
 #define rsnd_mod_to_dvc(_mod)	\
@@ -33,23 +34,87 @@ struct rsnd_dvc {
 	     ((pos) = (struct rsnd_dvc *)(priv)->dvc + i);	\
 	     i++)
 
+static const char const *dvc_ramp_rate[] = {
+	"128 dB/1 step",	 /* 00000 */
+	"64 dB/1 step",		 /* 00001 */
+	"32 dB/1 step",		 /* 00010 */
+	"16 dB/1 step",		 /* 00011 */
+	"8 dB/1 step",		 /* 00100 */
+	"4 dB/1 step",		 /* 00101 */
+	"2 dB/1 step",		 /* 00110 */
+	"1 dB/1 step",		 /* 00111 */
+	"0.5 dB/1 step",	 /* 01000 */
+	"0.25 dB/1 step",	 /* 01001 */
+	"0.125 dB/1 step",	 /* 01010 */
+	"0.125 dB/2 steps",	 /* 01011 */
+	"0.125 dB/4 steps",	 /* 01100 */
+	"0.125 dB/8 steps",	 /* 01101 */
+	"0.125 dB/16 steps",	 /* 01110 */
+	"0.125 dB/32 steps",	 /* 01111 */
+	"0.125 dB/64 steps",	 /* 10000 */
+	"0.125 dB/128 steps",	 /* 10001 */
+	"0.125 dB/256 steps",	 /* 10010 */
+	"0.125 dB/512 steps",	 /* 10011 */
+	"0.125 dB/1024 steps",	 /* 10100 */
+	"0.125 dB/2048 steps",	 /* 10101 */
+	"0.125 dB/4096 steps",	 /* 10110 */
+	"0.125 dB/8192 steps",	 /* 10111 */
+};
+
 static void rsnd_dvc_volume_update(struct rsnd_mod *mod)
 {
 	struct rsnd_dvc *dvc = rsnd_mod_to_dvc(mod);
-	u32 max = (0x00800000 - 1);
-	u32 vol[RSND_DVC_VOLUME_NUM];
+	u32 val[RSND_DVC_CHANNELS];
+	u32 dvucr = 0;
 	u32 mute = 0;
 	int i;
 
-	for (i = 0; i < RSND_DVC_VOLUME_NUM; i++) {
-		vol[i] = max / RSND_DVC_VOLUME_MAX * dvc->volume[i];
-		mute |= (!!dvc->mute[i]) << i;
+	for (i = 0; i < dvc->mute.cfg.size; i++)
+		mute |= (!!dvc->mute.cfg.val[i]) << i;
+
+	/* Disable DVC Register access */
+	rsnd_mod_write(mod, DVC_DVUER, 0);
+
+	/* Enable Ramp */
+	if (dvc->ren.val) {
+		dvucr |= 0x10;
+
+		/* Digital Volume Max */
+		for (i = 0; i < RSND_DVC_CHANNELS; i++)
+			val[i] = dvc->volume.cfg.max;
+
+		rsnd_mod_write(mod, DVC_VRCTR, 0xff);
+		rsnd_mod_write(mod, DVC_VRPDR, dvc->rup.val << 8 |
+					       dvc->rdown.val);
+		/*
+		 * FIXME !!
+		 * use scale-downed Digital Volume
+		 * as Volume Ramp
+		 * 7F FFFF -> 3FF
+		 */
+		rsnd_mod_write(mod, DVC_VRDBR,
+			       0x3ff - (dvc->volume.val[0] >> 13));
+
+	} else {
+		for (i = 0; i < RSND_DVC_CHANNELS; i++)
+			val[i] = dvc->volume.val[i];
+	}
+
+	/* Enable Digital Volume */
+	dvucr |= 0x100;
+	rsnd_mod_write(mod, DVC_VOL0R, val[0]);
+	rsnd_mod_write(mod, DVC_VOL1R, val[1]);
+
+	/*  Enable Mute */
+	if (mute) {
+		dvucr |= 0x1;
+		rsnd_mod_write(mod, DVC_ZCMCR, mute);
 	}
 
-	rsnd_mod_write(mod, DVC_VOL0R, vol[0]);
-	rsnd_mod_write(mod, DVC_VOL1R, vol[1]);
+	rsnd_mod_write(mod, DVC_DVUCR, dvucr);
 
-	rsnd_mod_write(mod, DVC_ZCMCR, mute);
+	/* Enable DVC Register access */
+	rsnd_mod_write(mod, DVC_DVUER, 1);
 }
 
 static int rsnd_dvc_probe_gen2(struct rsnd_mod *mod,
@@ -58,7 +123,8 @@ static int rsnd_dvc_probe_gen2(struct rsnd_mod *mod,
 	struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
 	struct device *dev = rsnd_priv_to_dev(priv);
 
-	dev_dbg(dev, "%s (Gen2) is probed\n", rsnd_mod_name(mod));
+	dev_dbg(dev, "%s[%d] (Gen2) is probed\n",
+		rsnd_mod_name(mod), rsnd_mod_id(mod));
 
 	return 0;
 }
@@ -102,16 +168,11 @@ static int rsnd_dvc_init(struct rsnd_mod *dvc_mod,
 
 	rsnd_mod_write(dvc_mod, DVC_ADINR, rsnd_get_adinr(dvc_mod));
 
-	/*  enable Volume / Mute */
-	rsnd_mod_write(dvc_mod, DVC_DVUCR, 0x101);
-
 	/* ch0/ch1 Volume */
 	rsnd_dvc_volume_update(dvc_mod);
 
 	rsnd_mod_write(dvc_mod, DVC_DVUIR, 0);
 
-	rsnd_mod_write(dvc_mod, DVC_DVUER, 1);
-
 	rsnd_adg_set_cmd_timsel_gen2(rdai, dvc_mod, io);
 
 	return 0;
@@ -143,86 +204,6 @@ static int rsnd_dvc_stop(struct rsnd_mod *mod,
 	return 0;
 }
 
-static int rsnd_dvc_volume_info(struct snd_kcontrol *kctrl,
-			       struct snd_ctl_elem_info *uinfo)
-{
-	struct rsnd_mod *mod = snd_kcontrol_chip(kctrl);
-	struct rsnd_dvc *dvc = rsnd_mod_to_dvc(mod);
-	u8 *val = (u8 *)kctrl->private_value;
-
-	uinfo->count = RSND_DVC_VOLUME_NUM;
-	uinfo->value.integer.min = 0;
-
-	if (val == dvc->volume) {
-		uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
-		uinfo->value.integer.max = RSND_DVC_VOLUME_MAX;
-	} else {
-		uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
-		uinfo->value.integer.max = 1;
-	}
-
-	return 0;
-}
-
-static int rsnd_dvc_volume_get(struct snd_kcontrol *kctrl,
-			      struct snd_ctl_elem_value *ucontrol)
-{
-	u8 *val = (u8 *)kctrl->private_value;
-	int i;
-
-	for (i = 0; i < RSND_DVC_VOLUME_NUM; i++)
-		ucontrol->value.integer.value[i] = val[i];
-
-	return 0;
-}
-
-static int rsnd_dvc_volume_put(struct snd_kcontrol *kctrl,
-			      struct snd_ctl_elem_value *ucontrol)
-{
-	struct rsnd_mod *mod = snd_kcontrol_chip(kctrl);
-	u8 *val = (u8 *)kctrl->private_value;
-	int i, change = 0;
-
-	for (i = 0; i < RSND_DVC_VOLUME_NUM; i++) {
-		change |= (ucontrol->value.integer.value[i] != val[i]);
-		val[i] = ucontrol->value.integer.value[i];
-	}
-
-	if (change)
-		rsnd_dvc_volume_update(mod);
-
-	return change;
-}
-
-static int __rsnd_dvc_pcm_new(struct rsnd_mod *mod,
-			      struct rsnd_dai *rdai,
-			      struct snd_soc_pcm_runtime *rtd,
-			      const unsigned char *name,
-			      u8 *private)
-{
-	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_dvc_volume_info,
-		.get		= rsnd_dvc_volume_get,
-		.put		= rsnd_dvc_volume_put,
-		.private_value	= (unsigned long)private,
-	};
-	int ret;
-
-	kctrl = snd_ctl_new1(&knew, mod);
-	if (!kctrl)
-		return -ENOMEM;
-
-	ret = snd_ctl_add(card, kctrl);
-	if (ret < 0)
-		return ret;
-
-	return 0;
-}
-
 static int rsnd_dvc_pcm_new(struct rsnd_mod *mod,
 			    struct rsnd_dai *rdai,
 			    struct snd_soc_pcm_runtime *rtd)
@@ -232,18 +213,48 @@ static int rsnd_dvc_pcm_new(struct rsnd_mod *mod,
 	int ret;
 
 	/* Volume */
-	ret = __rsnd_dvc_pcm_new(mod, rdai, rtd,
+	ret = rsnd_kctrl_new_m(mod, rdai, rtd,
 			rsnd_dai_is_play(rdai, io) ?
 			"DVC Out Playback Volume" : "DVC In Capture Volume",
-			dvc->volume);
+			rsnd_dvc_volume_update,
+			&dvc->volume, 0x00800000 - 1);
 	if (ret < 0)
 		return ret;
 
 	/* Mute */
-	ret = __rsnd_dvc_pcm_new(mod, rdai, rtd,
+	ret = rsnd_kctrl_new_m(mod, rdai, rtd,
 			rsnd_dai_is_play(rdai, io) ?
 			"DVC Out Mute Switch" : "DVC In Mute Switch",
-			dvc->mute);
+			rsnd_dvc_volume_update,
+			&dvc->mute, 1);
+	if (ret < 0)
+		return ret;
+
+	/* Ramp */
+	ret = rsnd_kctrl_new_s(mod, rdai, rtd,
+			rsnd_dai_is_play(rdai, io) ?
+			"DVC Out Ramp Switch" : "DVC In Ramp Switch",
+			rsnd_dvc_volume_update,
+			&dvc->ren, 1);
+	if (ret < 0)
+		return ret;
+
+	ret = rsnd_kctrl_new_e(mod, rdai, rtd,
+			rsnd_dai_is_play(rdai, io) ?
+			"DVC Out Ramp Up Rate" : "DVC In Ramp Up Rate",
+			&dvc->rup,
+			rsnd_dvc_volume_update,
+			dvc_ramp_rate, ARRAY_SIZE(dvc_ramp_rate));
+	if (ret < 0)
+		return ret;
+
+	ret = rsnd_kctrl_new_e(mod, rdai, rtd,
+			rsnd_dai_is_play(rdai, io) ?
+			"DVC Out Ramp Down Rate" : "DVC In Ramp Down Rate",
+			&dvc->rdown,
+			rsnd_dvc_volume_update,
+			dvc_ramp_rate, ARRAY_SIZE(dvc_ramp_rate));
+
 	if (ret < 0)
 		return ret;
 
diff --git a/sound/soc/sh/rcar/gen.c b/sound/soc/sh/rcar/gen.c
index f95e7ab135e8..87a6f2d62775 100644
--- a/sound/soc/sh/rcar/gen.c
+++ b/sound/soc/sh/rcar/gen.c
@@ -8,6 +8,17 @@
  * it under the terms of the GNU General Public License version 2 as
  * published by the Free Software Foundation.
  */
+
+/*
+ * #define DEBUG
+ *
+ * you can also add below in
+ * ${LINUX}/drivers/base/regmap/regmap.c
+ * for regmap debug
+ *
+ * #define LOG_DEVICE "xxxx.rcar_sound"
+ */
+
 #include "rsnd.h"
 
 struct rsnd_gen {
@@ -67,9 +78,10 @@ u32 rsnd_read(struct rsnd_priv *priv,
 	if (!rsnd_is_accessible_reg(priv, gen, reg))
 		return 0;
 
-	regmap_fields_read(gen->regs[reg], rsnd_mod_id(mod), &val);
+	dev_dbg(dev, "r %s[%d] - %4d : %08x\n",
+		rsnd_mod_name(mod), rsnd_mod_id(mod), reg, val);
 
-	dev_dbg(dev, "r %s - 0x%04d : %08x\n", rsnd_mod_name(mod), reg, val);
+	regmap_fields_read(gen->regs[reg], rsnd_mod_id(mod), &val);
 
 	return val;
 }
@@ -84,9 +96,10 @@ void rsnd_write(struct rsnd_priv *priv,
 	if (!rsnd_is_accessible_reg(priv, gen, reg))
 		return;
 
-	regmap_fields_write(gen->regs[reg], rsnd_mod_id(mod), data);
+	dev_dbg(dev, "w %s[%d] - %4d : %08x\n",
+		rsnd_mod_name(mod), rsnd_mod_id(mod), reg, data);
 
-	dev_dbg(dev, "w %s - 0x%04d : %08x\n", rsnd_mod_name(mod), reg, data);
+	regmap_fields_write(gen->regs[reg], rsnd_mod_id(mod), data);
 }
 
 void rsnd_bset(struct rsnd_priv *priv, struct rsnd_mod *mod,
@@ -98,11 +111,11 @@ void rsnd_bset(struct rsnd_priv *priv, struct rsnd_mod *mod,
 	if (!rsnd_is_accessible_reg(priv, gen, reg))
 		return;
 
+	dev_dbg(dev, "b %s[%d] - %4d : %08x/%08x\n",
+		rsnd_mod_name(mod), rsnd_mod_id(mod), reg, data, mask);
+
 	regmap_fields_update_bits(gen->regs[reg], rsnd_mod_id(mod),
 				  mask, data);
-
-	dev_dbg(dev, "b %s - 0x%04d : %08x/%08x\n",
-		rsnd_mod_name(mod), reg, data, mask);
 }
 
 #define rsnd_gen_regmap_init(priv, id_size, reg_id, conf)		\
@@ -311,6 +324,9 @@ static int rsnd_gen2_probe(struct platform_device *pdev,
 		RSND_GEN_M_REG(DVC_ADINR,	0xe08,	0x100),
 		RSND_GEN_M_REG(DVC_DVUCR,	0xe10,	0x100),
 		RSND_GEN_M_REG(DVC_ZCMCR,	0xe14,	0x100),
+		RSND_GEN_M_REG(DVC_VRCTR,	0xe18,	0x100),
+		RSND_GEN_M_REG(DVC_VRPDR,	0xe1c,	0x100),
+		RSND_GEN_M_REG(DVC_VRDBR,	0xe20,	0x100),
 		RSND_GEN_M_REG(DVC_VOL0R,	0xe28,	0x100),
 		RSND_GEN_M_REG(DVC_VOL1R,	0xe2c,	0x100),
 		RSND_GEN_M_REG(DVC_DVUER,	0xe48,	0x100),
diff --git a/sound/soc/sh/rcar/rsnd.h b/sound/soc/sh/rcar/rsnd.h
index d119adf97c9c..5826c8abf794 100644
--- a/sound/soc/sh/rcar/rsnd.h
+++ b/sound/soc/sh/rcar/rsnd.h
@@ -91,6 +91,9 @@ enum rsnd_reg {
 	RSND_REG_SHARE20,
 	RSND_REG_SHARE21,
 	RSND_REG_SHARE22,
+	RSND_REG_SHARE23,
+	RSND_REG_SHARE24,
+	RSND_REG_SHARE25,
 
 	RSND_REG_MAX,
 };
@@ -129,6 +132,9 @@ enum rsnd_reg {
 #define RSND_REG_CMD_CTRL		RSND_REG_SHARE20
 #define RSND_REG_CMDOUT_TIMSEL		RSND_REG_SHARE21
 #define RSND_REG_BUSIF_DALIGN		RSND_REG_SHARE22
+#define RSND_REG_DVC_VRCTR		RSND_REG_SHARE23
+#define RSND_REG_DVC_VRPDR		RSND_REG_SHARE24
+#define RSND_REG_DVC_VRDBR		RSND_REG_SHARE25
 
 struct rsnd_of_data;
 struct rsnd_priv;
@@ -200,6 +206,8 @@ struct rsnd_mod_ops {
 	int (*pcm_new)(struct rsnd_mod *mod,
 		       struct rsnd_dai *rdai,
 		       struct snd_soc_pcm_runtime *rtd);
+	int (*fallback)(struct rsnd_mod *mod,
+			struct rsnd_dai *rdai);
 };
 
 struct rsnd_dai_stream;
@@ -210,7 +218,35 @@ struct rsnd_mod {
 	struct rsnd_mod_ops *ops;
 	struct rsnd_dma dma;
 	struct rsnd_dai_stream *io;
+	u32 status;
 };
+/*
+ * status
+ *
+ * bit
+ * 0	0: probe	1: remove
+ * 1	0: init		1: quit
+ * 2	0: start	1: stop
+ * 3	0: pcm_new
+ * 4	0: fallback
+ */
+#define __rsnd_mod_shift_probe		0
+#define __rsnd_mod_shift_remove		0
+#define __rsnd_mod_shift_init		1
+#define __rsnd_mod_shift_quit		1
+#define __rsnd_mod_shift_start		2
+#define __rsnd_mod_shift_stop		2
+#define __rsnd_mod_shift_pcm_new	3
+#define __rsnd_mod_shift_fallback	4
+
+#define __rsnd_mod_call_probe		0
+#define __rsnd_mod_call_remove		1
+#define __rsnd_mod_call_init		0
+#define __rsnd_mod_call_quit		1
+#define __rsnd_mod_call_start		0
+#define __rsnd_mod_call_stop		1
+#define __rsnd_mod_call_pcm_new		0
+#define __rsnd_mod_call_fallback	0
 
 #define rsnd_mod_to_priv(mod) ((mod)->priv)
 #define rsnd_mod_to_dma(mod) (&(mod)->dma)
@@ -267,7 +303,8 @@ struct rsnd_dai *rsnd_dai_get(struct rsnd_priv *priv, int id);
 int rsnd_dai_is_play(struct rsnd_dai *rdai, struct rsnd_dai_stream *io);
 int rsnd_dai_id(struct rsnd_priv *priv, struct rsnd_dai *rdai);
 #define rsnd_dai_get_platform_info(rdai) ((rdai)->info)
-#define rsnd_io_to_runtime(io) ((io)->substream->runtime)
+#define rsnd_io_to_runtime(io) ((io)->substream ? \
+				(io)->substream->runtime : NULL)
 
 void rsnd_dai_pointer_update(struct rsnd_dai_stream *io, int cnt);
 int rsnd_dai_pointer_offset(struct rsnd_dai_stream *io, int additional);
@@ -382,6 +419,51 @@ struct rsnd_priv {
 })
 
 /*
+ *	rsnd_kctrl
+ */
+struct rsnd_kctrl_cfg {
+	unsigned int max;
+	unsigned int size;
+	u32 *val;
+	const char * const *texts;
+	void (*update)(struct rsnd_mod *mod);
+};
+
+#define RSND_DVC_CHANNELS	2
+struct rsnd_kctrl_cfg_m {
+	struct rsnd_kctrl_cfg cfg;
+	u32 val[RSND_DVC_CHANNELS];
+};
+
+struct rsnd_kctrl_cfg_s {
+	struct rsnd_kctrl_cfg cfg;
+	u32 val;
+};
+
+int rsnd_kctrl_new_m(struct rsnd_mod *mod,
+		     struct rsnd_dai *rdai,
+		     struct snd_soc_pcm_runtime *rtd,
+		     const unsigned char *name,
+		     void (*update)(struct rsnd_mod *mod),
+		     struct rsnd_kctrl_cfg_m *_cfg,
+		     u32 max);
+int rsnd_kctrl_new_s(struct rsnd_mod *mod,
+		     struct rsnd_dai *rdai,
+		     struct snd_soc_pcm_runtime *rtd,
+		     const unsigned char *name,
+		     void (*update)(struct rsnd_mod *mod),
+		     struct rsnd_kctrl_cfg_s *_cfg,
+		     u32 max);
+int rsnd_kctrl_new_e(struct rsnd_mod *mod,
+		     struct rsnd_dai *rdai,
+		     struct snd_soc_pcm_runtime *rtd,
+		     const unsigned char *name,
+		     struct rsnd_kctrl_cfg_s *_cfg,
+		     void (*update)(struct rsnd_mod *mod),
+		     const char * const *texts,
+		     u32 max);
+
+/*
  *	R-Car SRC
  */
 int rsnd_src_probe(struct platform_device *pdev,
@@ -395,10 +477,11 @@ int rsnd_src_ssiu_start(struct rsnd_mod *ssi_mod,
 			struct rsnd_dai *rdai,
 			int use_busif);
 int rsnd_src_ssiu_stop(struct rsnd_mod *ssi_mod,
-		       struct rsnd_dai *rdai,
-		       int use_busif);
-int rsnd_src_enable_ssi_irq(struct rsnd_mod *ssi_mod,
+		       struct rsnd_dai *rdai);
+int rsnd_src_ssi_irq_enable(struct rsnd_mod *ssi_mod,
 			    struct rsnd_dai *rdai);
+int rsnd_src_ssi_irq_disable(struct rsnd_mod *ssi_mod,
+			     struct rsnd_dai *rdai);
 
 #define rsnd_src_nr(priv) ((priv)->src_nr)
 
@@ -410,6 +493,7 @@ int rsnd_ssi_probe(struct platform_device *pdev,
 		   struct rsnd_priv *priv);
 struct rsnd_mod *rsnd_ssi_mod_get(struct rsnd_priv *priv, int id);
 int rsnd_ssi_is_pin_sharing(struct rsnd_mod *mod);
+int rsnd_ssi_is_dma_mode(struct rsnd_mod *mod);
 
 /*
  *	R-Car DVC
diff --git a/sound/soc/sh/rcar/src.c b/sound/soc/sh/rcar/src.c
index 9183e0145503..eede3ac6eed2 100644
--- a/sound/soc/sh/rcar/src.c
+++ b/sound/soc/sh/rcar/src.c
@@ -175,30 +175,47 @@ int rsnd_src_ssiu_start(struct rsnd_mod *ssi_mod,
 }
 
 int rsnd_src_ssiu_stop(struct rsnd_mod *ssi_mod,
-			struct rsnd_dai *rdai,
-			int use_busif)
+		       struct rsnd_dai *rdai)
 {
 	/*
 	 * DMA settings for SSIU
 	 */
-	if (use_busif)
-		rsnd_mod_write(ssi_mod, SSI_CTRL, 0);
+	rsnd_mod_write(ssi_mod, SSI_CTRL, 0);
 
 	return 0;
 }
 
-int rsnd_src_enable_ssi_irq(struct rsnd_mod *ssi_mod,
+int rsnd_src_ssi_irq_enable(struct rsnd_mod *ssi_mod,
 			    struct rsnd_dai *rdai)
 {
 	struct rsnd_priv *priv = rsnd_mod_to_priv(ssi_mod);
 
-	/* enable PIO interrupt if Gen2 */
-	if (rsnd_is_gen2(priv))
+	if (rsnd_is_gen1(priv))
+		return 0;
+
+	/* enable SSI interrupt if Gen2 */
+	if (rsnd_ssi_is_dma_mode(ssi_mod))
+		rsnd_mod_write(ssi_mod, INT_ENABLE, 0x0e000000);
+	else
 		rsnd_mod_write(ssi_mod, INT_ENABLE, 0x0f000000);
 
 	return 0;
 }
 
+int rsnd_src_ssi_irq_disable(struct rsnd_mod *ssi_mod,
+			    struct rsnd_dai *rdai)
+{
+	struct rsnd_priv *priv = rsnd_mod_to_priv(ssi_mod);
+
+	if (rsnd_is_gen1(priv))
+		return 0;
+
+	/* disable SSI interrupt if Gen2 */
+	rsnd_mod_write(ssi_mod, INT_ENABLE, 0x00000000);
+
+	return 0;
+}
+
 unsigned int rsnd_src_get_ssi_rate(struct rsnd_priv *priv,
 				   struct rsnd_dai_stream *io,
 				   struct snd_pcm_runtime *runtime)
@@ -239,12 +256,6 @@ static int rsnd_src_set_convert_rate(struct rsnd_mod *mod,
 	rsnd_mod_write(mod, SRC_SWRSR, 0);
 	rsnd_mod_write(mod, SRC_SWRSR, 1);
 
-	/*
-	 * Initialize the operation of the SRC internal circuits
-	 * see rsnd_src_start()
-	 */
-	rsnd_mod_write(mod, SRC_SRCIR, 1);
-
 	/* Set channel number and output bit length */
 	rsnd_mod_write(mod, SRC_ADINR, rsnd_get_adinr(mod));
 
@@ -269,6 +280,12 @@ static int rsnd_src_init(struct rsnd_mod *mod,
 
 	clk_prepare_enable(src->clk);
 
+	/*
+	 * Initialize the operation of the SRC internal circuits
+	 * see rsnd_src_start()
+	 */
+	rsnd_mod_write(mod, SRC_SRCIR, 1);
+
 	return 0;
 }
 
@@ -282,32 +299,20 @@ static int rsnd_src_quit(struct rsnd_mod *mod,
 	return 0;
 }
 
-static int rsnd_src_start(struct rsnd_mod *mod,
-			  struct rsnd_dai *rdai)
+static int rsnd_src_start(struct rsnd_mod *mod)
 {
-	struct rsnd_src *src = rsnd_mod_to_src(mod);
-
 	/*
 	 * Cancel the initialization and operate the SRC function
-	 * see rsnd_src_set_convert_rate()
+	 * see rsnd_src_init()
 	 */
 	rsnd_mod_write(mod, SRC_SRCIR, 0);
 
-	if (rsnd_src_convert_rate(src))
-		rsnd_mod_write(mod, SRC_ROUTE_MODE0, 1);
-
 	return 0;
 }
 
-
-static int rsnd_src_stop(struct rsnd_mod *mod,
-			 struct rsnd_dai *rdai)
+static int rsnd_src_stop(struct rsnd_mod *mod)
 {
-	struct rsnd_src *src = rsnd_mod_to_src(mod);
-
-	if (rsnd_src_convert_rate(src))
-		rsnd_mod_write(mod, SRC_ROUTE_MODE0, 0);
-
+	/* nothing to do */
 	return 0;
 }
 
@@ -414,6 +419,7 @@ static int rsnd_src_set_convert_timing_gen1(struct rsnd_mod *mod,
 static int rsnd_src_set_convert_rate_gen1(struct rsnd_mod *mod,
 					  struct rsnd_dai *rdai)
 {
+	struct rsnd_src *src = rsnd_mod_to_src(mod);
 	int ret;
 
 	ret = rsnd_src_set_convert_rate(mod, rdai);
@@ -427,6 +433,10 @@ static int rsnd_src_set_convert_rate_gen1(struct rsnd_mod *mod,
 	rsnd_mod_write(mod, SRC_MNFSR,
 		       rsnd_mod_read(mod, SRC_IFSVR) / 100 * 98);
 
+	/* Gen1/Gen2 are not compatible */
+	if (rsnd_src_convert_rate(src))
+		rsnd_mod_write(mod, SRC_ROUTE_MODE0, 1);
+
 	/* no SRC_BFSSR settings, since SRC_SRCCR::BUFMD is 0 */
 
 	return 0;
@@ -438,7 +448,8 @@ static int rsnd_src_probe_gen1(struct rsnd_mod *mod,
 	struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
 	struct device *dev = rsnd_priv_to_dev(priv);
 
-	dev_dbg(dev, "%s (Gen1) is probed\n", rsnd_mod_name(mod));
+	dev_dbg(dev, "%s[%d] (Gen1) is probed\n",
+		rsnd_mod_name(mod), rsnd_mod_id(mod));
 
 	return 0;
 }
@@ -474,7 +485,7 @@ static int rsnd_src_start_gen1(struct rsnd_mod *mod,
 
 	rsnd_mod_bset(mod, SRC_ROUTE_CTRL, (1 << id), (1 << id));
 
-	return rsnd_src_start(mod, rdai);
+	return rsnd_src_start(mod);
 }
 
 static int rsnd_src_stop_gen1(struct rsnd_mod *mod,
@@ -484,7 +495,7 @@ static int rsnd_src_stop_gen1(struct rsnd_mod *mod,
 
 	rsnd_mod_bset(mod, SRC_ROUTE_CTRL, (1 << id), 0);
 
-	return rsnd_src_stop(mod, rdai);
+	return rsnd_src_stop(mod);
 }
 
 static struct rsnd_mod_ops rsnd_src_gen1_ops = {
@@ -507,16 +518,17 @@ static int rsnd_src_set_convert_rate_gen2(struct rsnd_mod *mod,
 	struct rsnd_dai_stream *io = rsnd_mod_to_io(mod);
 	struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io);
 	struct rsnd_src *src = rsnd_mod_to_src(mod);
+	u32 convert_rate = rsnd_src_convert_rate(src);
 	uint ratio;
 	int ret;
 
 	/* 6 - 1/6 are very enough ratio for SRC_BSDSR */
-	if (!rsnd_src_convert_rate(src))
+	if (!convert_rate)
 		ratio = 0;
-	else if (rsnd_src_convert_rate(src) > runtime->rate)
-		ratio = 100 * rsnd_src_convert_rate(src) / runtime->rate;
+	else if (convert_rate > runtime->rate)
+		ratio = 100 * convert_rate / runtime->rate;
 	else
-		ratio = 100 * runtime->rate / rsnd_src_convert_rate(src);
+		ratio = 100 * runtime->rate / convert_rate;
 
 	if (ratio > 600) {
 		dev_err(dev, "FSO/FSI ratio error\n");
@@ -529,6 +541,11 @@ static int rsnd_src_set_convert_rate_gen2(struct rsnd_mod *mod,
 
 	rsnd_mod_write(mod, SRC_SRCCR, 0x00011110);
 
+	if (convert_rate) {
+		/* Gen1/Gen2 are not compatible */
+		rsnd_mod_write(mod, SRC_ROUTE_MODE0, 1);
+	}
+
 	switch (rsnd_mod_id(mod)) {
 	case 5:
 	case 6:
@@ -578,9 +595,11 @@ static int rsnd_src_probe_gen2(struct rsnd_mod *mod,
 			    rsnd_info_is_playback(priv, src),
 			    src->info->dma_id);
 	if (ret < 0)
-		dev_err(dev, "SRC DMA failed\n");
-
-	dev_dbg(dev, "%s (Gen2) is probed\n", rsnd_mod_name(mod));
+		dev_err(dev, "%s[%d] (Gen2) failed\n",
+			rsnd_mod_name(mod), rsnd_mod_id(mod));
+	else
+		dev_dbg(dev, "%s[%d] (Gen2) is probed\n",
+			rsnd_mod_name(mod), rsnd_mod_id(mod));
 
 	return ret;
 }
@@ -624,7 +643,7 @@ static int rsnd_src_start_gen2(struct rsnd_mod *mod,
 
 	rsnd_mod_write(mod, SRC_CTRL, val);
 
-	return rsnd_src_start(mod, rdai);
+	return rsnd_src_start(mod);
 }
 
 static int rsnd_src_stop_gen2(struct rsnd_mod *mod,
@@ -636,7 +655,7 @@ static int rsnd_src_stop_gen2(struct rsnd_mod *mod,
 
 	rsnd_dma_stop(rsnd_mod_to_dma(&src->mod));
 
-	return rsnd_src_stop(mod, rdai);
+	return rsnd_src_stop(mod);
 }
 
 static struct rsnd_mod_ops rsnd_src_gen2_ops = {
diff --git a/sound/soc/sh/rcar/ssi.c b/sound/soc/sh/rcar/ssi.c
index 34e84009162b..3844fbef4664 100644
--- a/sound/soc/sh/rcar/ssi.c
+++ b/sound/soc/sh/rcar/ssi.c
@@ -68,7 +68,6 @@ struct rsnd_ssi {
 	struct rsnd_dai *rdai;
 	u32 cr_own;
 	u32 cr_clk;
-	u32 cr_etc;
 	int err;
 	unsigned int usrcnt;
 	unsigned int rate;
@@ -83,7 +82,7 @@ struct rsnd_ssi {
 #define rsnd_ssi_nr(priv) ((priv)->ssi_nr)
 #define rsnd_mod_to_ssi(_mod) container_of((_mod), struct rsnd_ssi, mod)
 #define rsnd_dma_to_ssi(dma)  rsnd_mod_to_ssi(rsnd_dma_to_mod(dma))
-#define rsnd_ssi_pio_available(ssi) ((ssi)->info->pio_irq > 0)
+#define rsnd_ssi_pio_available(ssi) ((ssi)->info->irq > 0)
 #define rsnd_ssi_dma_available(ssi) \
 	rsnd_dma_available(rsnd_mod_to_dma(&(ssi)->mod))
 #define rsnd_ssi_clk_from_parent(ssi) ((ssi)->parent)
@@ -96,6 +95,9 @@ static int rsnd_ssi_use_busif(struct rsnd_mod *mod)
 	struct rsnd_dai_stream *io = rsnd_mod_to_io(mod);
 	int use_busif = 0;
 
+	if (!rsnd_ssi_is_dma_mode(mod))
+		return 0;
+
 	if (!(rsnd_ssi_mode_flags(ssi) & RSND_SSI_NO_BUSIF))
 		use_busif = 1;
 	if (rsnd_io_to_mod_src(io))
@@ -159,7 +161,8 @@ static int rsnd_ssi_master_clk_start(struct rsnd_ssi *ssi,
 				ssi->cr_clk	= FORCE | SWL_32 |
 						  SCKD | SWSD | CKDV(j);
 
-				dev_dbg(dev, "ssi%d outputs %u Hz\n",
+				dev_dbg(dev, "%s[%d] outputs %u Hz\n",
+					rsnd_mod_name(&ssi->mod),
 					rsnd_mod_id(&ssi->mod), rate);
 
 				return 0;
@@ -184,6 +187,7 @@ static void rsnd_ssi_hw_start(struct rsnd_ssi *ssi,
 {
 	struct rsnd_priv *priv = rsnd_mod_to_priv(&ssi->mod);
 	struct device *dev = rsnd_priv_to_dev(priv);
+	u32 cr_mode;
 	u32 cr;
 
 	if (0 == ssi->usrcnt) {
@@ -197,16 +201,29 @@ static void rsnd_ssi_hw_start(struct rsnd_ssi *ssi,
 		}
 	}
 
+	cr_mode = rsnd_ssi_is_dma_mode(&ssi->mod) ?
+		DMEN :	/* DMA : enable DMA */
+		DIEN;	/* PIO : enable Data interrupt */
+
+
 	cr  =	ssi->cr_own	|
 		ssi->cr_clk	|
-		ssi->cr_etc	|
-		EN;
+		cr_mode		|
+		UIEN | OIEN | EN;
 
 	rsnd_mod_write(&ssi->mod, SSICR, cr);
 
+	/* enable WS continue */
+	if (rsnd_dai_is_clk_master(rdai))
+		rsnd_mod_write(&ssi->mod, SSIWSR, CONT);
+
+	/* clear error status */
+	rsnd_mod_write(&ssi->mod, SSISR, 0);
+
 	ssi->usrcnt++;
 
-	dev_dbg(dev, "ssi%d hw started\n", rsnd_mod_id(&ssi->mod));
+	dev_dbg(dev, "%s[%d] hw started\n",
+		rsnd_mod_name(&ssi->mod), rsnd_mod_id(&ssi->mod));
 }
 
 static void rsnd_ssi_hw_stop(struct rsnd_ssi *ssi,
@@ -249,7 +266,8 @@ static void rsnd_ssi_hw_stop(struct rsnd_ssi *ssi,
 		clk_disable_unprepare(ssi->clk);
 	}
 
-	dev_dbg(dev, "ssi%d hw stopped\n", rsnd_mod_id(&ssi->mod));
+	dev_dbg(dev, "%s[%d] hw stopped\n",
+		rsnd_mod_name(&ssi->mod), rsnd_mod_id(&ssi->mod));
 }
 
 /*
@@ -334,25 +352,54 @@ static void rsnd_ssi_record_error(struct rsnd_ssi *ssi, u32 status)
 	}
 }
 
-/*
- *		SSI PIO
- */
-static irqreturn_t rsnd_ssi_pio_interrupt(int irq, void *data)
+static int rsnd_ssi_start(struct rsnd_mod *mod,
+			  struct rsnd_dai *rdai)
+{
+	struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
+	struct rsnd_dai_stream *io = rsnd_mod_to_io(mod);
+
+	rsnd_src_ssiu_start(mod, rdai, rsnd_ssi_use_busif(mod));
+
+	rsnd_ssi_hw_start(ssi, rdai, io);
+
+	rsnd_src_ssi_irq_enable(mod, rdai);
+
+	return 0;
+}
+
+static int rsnd_ssi_stop(struct rsnd_mod *mod,
+			 struct rsnd_dai *rdai)
+{
+	struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
+
+	rsnd_src_ssi_irq_disable(mod, rdai);
+
+	rsnd_ssi_record_error(ssi, rsnd_mod_read(mod, SSISR));
+
+	rsnd_ssi_hw_stop(ssi, rdai);
+
+	rsnd_src_ssiu_stop(mod, rdai);
+
+	return 0;
+}
+
+static irqreturn_t rsnd_ssi_interrupt(int irq, void *data)
 {
 	struct rsnd_ssi *ssi = data;
+	struct rsnd_dai *rdai = ssi->rdai;
 	struct rsnd_mod *mod = &ssi->mod;
 	struct rsnd_dai_stream *io = rsnd_mod_to_io(mod);
 	u32 status = rsnd_mod_read(mod, SSISR);
-	irqreturn_t ret = IRQ_NONE;
 
-	if (io && (status & DIRQ)) {
-		struct rsnd_dai *rdai = ssi->rdai;
+	if (!io)
+		return IRQ_NONE;
+
+	/* PIO only */
+	if (status & DIRQ) {
 		struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io);
 		u32 *buf = (u32 *)(runtime->dma_area +
 				   rsnd_dai_pointer_offset(io, 0));
 
-		rsnd_ssi_record_error(ssi, status);
-
 		/*
 		 * 8/16/32 data can be assesse to TDR/RDR register
 		 * directly as 32bit data
@@ -364,73 +411,60 @@ static irqreturn_t rsnd_ssi_pio_interrupt(int irq, void *data)
 			*buf = rsnd_mod_read(mod, SSIRDR);
 
 		rsnd_dai_pointer_update(io, sizeof(*buf));
+	}
+
+	/* PIO / DMA */
+	if (status & (UIRQ | OIRQ)) {
+		struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
+		struct device *dev = rsnd_priv_to_dev(priv);
+
+		/*
+		 * restart SSI
+		 */
+		rsnd_ssi_stop(mod, rdai);
+		rsnd_ssi_start(mod, rdai);
 
-		ret = IRQ_HANDLED;
+		dev_dbg(dev, "%s[%d] restart\n",
+			rsnd_mod_name(mod), rsnd_mod_id(mod));
 	}
 
-	return ret;
+	rsnd_ssi_record_error(ssi, status);
+
+	return IRQ_HANDLED;
 }
 
+/*
+ *		SSI PIO
+ */
 static int rsnd_ssi_pio_probe(struct rsnd_mod *mod,
 			      struct rsnd_dai *rdai)
 {
 	struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
 	struct device *dev = rsnd_priv_to_dev(priv);
 	struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
-	int irq = ssi->info->pio_irq;
 	int ret;
 
-	ret = devm_request_irq(dev, irq,
-			       rsnd_ssi_pio_interrupt,
+	ret = devm_request_irq(dev, ssi->info->irq,
+			       rsnd_ssi_interrupt,
 			       IRQF_SHARED,
 			       dev_name(dev), ssi);
 	if (ret)
-		dev_err(dev, "SSI request interrupt failed\n");
-
-	dev_dbg(dev, "%s (PIO) is probed\n", rsnd_mod_name(mod));
+		dev_err(dev, "%s[%d] (PIO) request interrupt failed\n",
+			rsnd_mod_name(mod), rsnd_mod_id(mod));
+	else
+		dev_dbg(dev, "%s[%d] (PIO) is probed\n",
+			rsnd_mod_name(mod), rsnd_mod_id(mod));
 
 	return ret;
 }
 
-static int rsnd_ssi_pio_start(struct rsnd_mod *mod,
-			      struct rsnd_dai *rdai)
-{
-	struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
-	struct rsnd_dai_stream *io = rsnd_mod_to_io(mod);
-
-	/* enable PIO IRQ */
-	ssi->cr_etc = UIEN | OIEN | DIEN;
-
-	rsnd_src_ssiu_start(mod, rdai, 0);
-
-	rsnd_src_enable_ssi_irq(mod, rdai);
-
-	rsnd_ssi_hw_start(ssi, rdai, io);
-
-	return 0;
-}
-
-static int rsnd_ssi_pio_stop(struct rsnd_mod *mod,
-			     struct rsnd_dai *rdai)
-{
-	struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
-
-	ssi->cr_etc = 0;
-
-	rsnd_ssi_hw_stop(ssi, rdai);
-
-	rsnd_src_ssiu_stop(mod, rdai, 0);
-
-	return 0;
-}
-
 static struct rsnd_mod_ops rsnd_ssi_pio_ops = {
 	.name	= SSI_NAME,
 	.probe	= rsnd_ssi_pio_probe,
 	.init	= rsnd_ssi_init,
 	.quit	= rsnd_ssi_quit,
-	.start	= rsnd_ssi_pio_start,
-	.stop	= rsnd_ssi_pio_stop,
+	.start	= rsnd_ssi_start,
+	.stop	= rsnd_ssi_stop,
 };
 
 static int rsnd_ssi_dma_probe(struct rsnd_mod *mod,
@@ -442,15 +476,28 @@ static int rsnd_ssi_dma_probe(struct rsnd_mod *mod,
 	int dma_id = ssi->info->dma_id;
 	int ret;
 
+	ret = devm_request_irq(dev, ssi->info->irq,
+			       rsnd_ssi_interrupt,
+			       IRQF_SHARED,
+			       dev_name(dev), ssi);
+	if (ret)
+		goto rsnd_ssi_dma_probe_fail;
+
 	ret = rsnd_dma_init(
 		priv, rsnd_mod_to_dma(mod),
 		rsnd_info_is_playback(priv, ssi),
 		dma_id);
+	if (ret)
+		goto rsnd_ssi_dma_probe_fail;
 
-	if (ret < 0)
-		dev_err(dev, "SSI DMA failed\n");
+	dev_dbg(dev, "%s[%d] (DMA) is probed\n",
+		rsnd_mod_name(mod), rsnd_mod_id(mod));
+
+	return ret;
 
-	dev_dbg(dev, "%s (DMA) is probed\n", rsnd_mod_name(mod));
+rsnd_ssi_dma_probe_fail:
+	dev_err(dev, "%s[%d] (DMA) is failed\n",
+		rsnd_mod_name(mod), rsnd_mod_id(mod));
 
 	return ret;
 }
@@ -458,30 +505,48 @@ static int rsnd_ssi_dma_probe(struct rsnd_mod *mod,
 static int rsnd_ssi_dma_remove(struct rsnd_mod *mod,
 			       struct rsnd_dai *rdai)
 {
+	struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
+	struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
+	struct device *dev = rsnd_priv_to_dev(priv);
+	int irq = ssi->info->irq;
+
 	rsnd_dma_quit(rsnd_mod_to_priv(mod), rsnd_mod_to_dma(mod));
 
+	/* PIO will request IRQ again */
+	devm_free_irq(dev, irq, ssi);
+
 	return 0;
 }
 
-static int rsnd_ssi_dma_start(struct rsnd_mod *mod,
-			      struct rsnd_dai *rdai)
+static int rsnd_ssi_fallback(struct rsnd_mod *mod,
+			     struct rsnd_dai *rdai)
 {
-	struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
-	struct rsnd_dma *dma = rsnd_mod_to_dma(&ssi->mod);
-	struct rsnd_dai_stream *io = rsnd_mod_to_io(mod);
+	struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
+	struct device *dev = rsnd_priv_to_dev(priv);
 
-	/* enable DMA transfer */
-	ssi->cr_etc = DMEN;
+	/*
+	 * fallback to PIO
+	 *
+	 * SSI .probe might be called again.
+	 * see
+	 *	rsnd_rdai_continuance_probe()
+	 */
+	mod->ops = &rsnd_ssi_pio_ops;
 
-	rsnd_src_ssiu_start(mod, rdai, rsnd_ssi_use_busif(mod));
+	dev_info(dev, "%s[%d] fallback to PIO mode\n",
+		 rsnd_mod_name(mod), rsnd_mod_id(mod));
 
-	rsnd_dma_start(dma);
+	return 0;
+}
 
-	rsnd_ssi_hw_start(ssi, ssi->rdai, io);
+static int rsnd_ssi_dma_start(struct rsnd_mod *mod,
+			      struct rsnd_dai *rdai)
+{
+	struct rsnd_dma *dma = rsnd_mod_to_dma(mod);
 
-	/* enable WS continue */
-	if (rsnd_dai_is_clk_master(rdai))
-		rsnd_mod_write(&ssi->mod, SSIWSR, CONT);
+	rsnd_ssi_start(mod, rdai);
+
+	rsnd_dma_start(dma);
 
 	return 0;
 }
@@ -489,18 +554,11 @@ static int rsnd_ssi_dma_start(struct rsnd_mod *mod,
 static int rsnd_ssi_dma_stop(struct rsnd_mod *mod,
 			     struct rsnd_dai *rdai)
 {
-	struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
-	struct rsnd_dma *dma = rsnd_mod_to_dma(&ssi->mod);
-
-	ssi->cr_etc = 0;
-
-	rsnd_ssi_record_error(ssi, rsnd_mod_read(mod, SSISR));
-
-	rsnd_ssi_hw_stop(ssi, rdai);
+	struct rsnd_dma *dma = rsnd_mod_to_dma(mod);
 
 	rsnd_dma_stop(dma);
 
-	rsnd_src_ssiu_stop(mod, rdai, 1);
+	rsnd_ssi_stop(mod, rdai);
 
 	return 0;
 }
@@ -519,8 +577,15 @@ static struct rsnd_mod_ops rsnd_ssi_dma_ops = {
 	.quit	= rsnd_ssi_quit,
 	.start	= rsnd_ssi_dma_start,
 	.stop	= rsnd_ssi_dma_stop,
+	.fallback = rsnd_ssi_fallback,
 };
 
+int rsnd_ssi_is_dma_mode(struct rsnd_mod *mod)
+{
+	return mod->ops == &rsnd_ssi_dma_ops;
+}
+
+
 /*
  *		Non SSI
  */
@@ -614,7 +679,7 @@ static void rsnd_of_parse_ssi(struct platform_device *pdev,
 		/*
 		 * irq
 		 */
-		ssi_info->pio_irq = irq_of_parse_and_map(np, 0);
+		ssi_info->irq = irq_of_parse_and_map(np, 0);
 
 		/*
 		 * DMA
diff --git a/sound/soc/soc-ac97.c b/sound/soc/soc-ac97.c
new file mode 100644
index 000000000000..2e10e9a38376
--- /dev/null
+++ b/sound/soc/soc-ac97.c
@@ -0,0 +1,256 @@
+/*
+ * soc-ac97.c  --  ALSA SoC Audio Layer AC97 support
+ *
+ * Copyright 2005 Wolfson Microelectronics PLC.
+ * Copyright 2005 Openedhand Ltd.
+ * Copyright (C) 2010 Slimlogic Ltd.
+ * Copyright (C) 2010 Texas Instruments Inc.
+ *
+ * Author: Liam Girdwood <lrg@slimlogic.co.uk>
+ *         with code, comments and ideas from :-
+ *         Richard Purdie <richard@openedhand.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/ctype.h>
+#include <linux/delay.h>
+#include <linux/export.h>
+#include <linux/gpio.h>
+#include <linux/init.h>
+#include <linux/of_gpio.h>
+#include <linux/of.h>
+#include <linux/pinctrl/consumer.h>
+#include <linux/slab.h>
+#include <sound/ac97_codec.h>
+#include <sound/soc.h>
+
+struct snd_ac97_reset_cfg {
+	struct pinctrl *pctl;
+	struct pinctrl_state *pstate_reset;
+	struct pinctrl_state *pstate_warm_reset;
+	struct pinctrl_state *pstate_run;
+	int gpio_sdata;
+	int gpio_sync;
+	int gpio_reset;
+};
+
+static struct snd_ac97_bus soc_ac97_bus = {
+	.ops = NULL, /* Gets initialized in snd_soc_set_ac97_ops() */
+};
+
+static void soc_ac97_device_release(struct device *dev)
+{
+	kfree(to_ac97_t(dev));
+}
+
+/**
+ * snd_soc_new_ac97_codec - initailise AC97 device
+ * @codec: audio codec
+ *
+ * Initialises AC97 codec resources for use by ad-hoc devices only.
+ */
+struct snd_ac97 *snd_soc_new_ac97_codec(struct snd_soc_codec *codec)
+{
+	struct snd_ac97 *ac97;
+	int ret;
+
+	ac97 = kzalloc(sizeof(struct snd_ac97), GFP_KERNEL);
+	if (ac97 == NULL)
+		return ERR_PTR(-ENOMEM);
+
+	ac97->bus = &soc_ac97_bus;
+	ac97->num = 0;
+
+	ac97->dev.bus = &ac97_bus_type;
+	ac97->dev.parent = codec->component.card->dev;
+	ac97->dev.release = soc_ac97_device_release;
+
+	dev_set_name(&ac97->dev, "%d-%d:%s",
+		     codec->component.card->snd_card->number, 0,
+		     codec->component.name);
+
+	ret = device_register(&ac97->dev);
+	if (ret) {
+		put_device(&ac97->dev);
+		return ERR_PTR(ret);
+	}
+
+	return ac97;
+}
+EXPORT_SYMBOL_GPL(snd_soc_new_ac97_codec);
+
+/**
+ * snd_soc_free_ac97_codec - free AC97 codec device
+ * @codec: audio codec
+ *
+ * Frees AC97 codec device resources.
+ */
+void snd_soc_free_ac97_codec(struct snd_ac97 *ac97)
+{
+	device_del(&ac97->dev);
+	ac97->bus = NULL;
+	put_device(&ac97->dev);
+}
+EXPORT_SYMBOL_GPL(snd_soc_free_ac97_codec);
+
+static struct snd_ac97_reset_cfg snd_ac97_rst_cfg;
+
+static void snd_soc_ac97_warm_reset(struct snd_ac97 *ac97)
+{
+	struct pinctrl *pctl = snd_ac97_rst_cfg.pctl;
+
+	pinctrl_select_state(pctl, snd_ac97_rst_cfg.pstate_warm_reset);
+
+	gpio_direction_output(snd_ac97_rst_cfg.gpio_sync, 1);
+
+	udelay(10);
+
+	gpio_direction_output(snd_ac97_rst_cfg.gpio_sync, 0);
+
+	pinctrl_select_state(pctl, snd_ac97_rst_cfg.pstate_run);
+	msleep(2);
+}
+
+static void snd_soc_ac97_reset(struct snd_ac97 *ac97)
+{
+	struct pinctrl *pctl = snd_ac97_rst_cfg.pctl;
+
+	pinctrl_select_state(pctl, snd_ac97_rst_cfg.pstate_reset);
+
+	gpio_direction_output(snd_ac97_rst_cfg.gpio_sync, 0);
+	gpio_direction_output(snd_ac97_rst_cfg.gpio_sdata, 0);
+	gpio_direction_output(snd_ac97_rst_cfg.gpio_reset, 0);
+
+	udelay(10);
+
+	gpio_direction_output(snd_ac97_rst_cfg.gpio_reset, 1);
+
+	pinctrl_select_state(pctl, snd_ac97_rst_cfg.pstate_run);
+	msleep(2);
+}
+
+static int snd_soc_ac97_parse_pinctl(struct device *dev,
+		struct snd_ac97_reset_cfg *cfg)
+{
+	struct pinctrl *p;
+	struct pinctrl_state *state;
+	int gpio;
+	int ret;
+
+	p = devm_pinctrl_get(dev);
+	if (IS_ERR(p)) {
+		dev_err(dev, "Failed to get pinctrl\n");
+		return PTR_ERR(p);
+	}
+	cfg->pctl = p;
+
+	state = pinctrl_lookup_state(p, "ac97-reset");
+	if (IS_ERR(state)) {
+		dev_err(dev, "Can't find pinctrl state ac97-reset\n");
+		return PTR_ERR(state);
+	}
+	cfg->pstate_reset = state;
+
+	state = pinctrl_lookup_state(p, "ac97-warm-reset");
+	if (IS_ERR(state)) {
+		dev_err(dev, "Can't find pinctrl state ac97-warm-reset\n");
+		return PTR_ERR(state);
+	}
+	cfg->pstate_warm_reset = state;
+
+	state = pinctrl_lookup_state(p, "ac97-running");
+	if (IS_ERR(state)) {
+		dev_err(dev, "Can't find pinctrl state ac97-running\n");
+		return PTR_ERR(state);
+	}
+	cfg->pstate_run = state;
+
+	gpio = of_get_named_gpio(dev->of_node, "ac97-gpios", 0);
+	if (gpio < 0) {
+		dev_err(dev, "Can't find ac97-sync gpio\n");
+		return gpio;
+	}
+	ret = devm_gpio_request(dev, gpio, "AC97 link sync");
+	if (ret) {
+		dev_err(dev, "Failed requesting ac97-sync gpio\n");
+		return ret;
+	}
+	cfg->gpio_sync = gpio;
+
+	gpio = of_get_named_gpio(dev->of_node, "ac97-gpios", 1);
+	if (gpio < 0) {
+		dev_err(dev, "Can't find ac97-sdata gpio %d\n", gpio);
+		return gpio;
+	}
+	ret = devm_gpio_request(dev, gpio, "AC97 link sdata");
+	if (ret) {
+		dev_err(dev, "Failed requesting ac97-sdata gpio\n");
+		return ret;
+	}
+	cfg->gpio_sdata = gpio;
+
+	gpio = of_get_named_gpio(dev->of_node, "ac97-gpios", 2);
+	if (gpio < 0) {
+		dev_err(dev, "Can't find ac97-reset gpio\n");
+		return gpio;
+	}
+	ret = devm_gpio_request(dev, gpio, "AC97 link reset");
+	if (ret) {
+		dev_err(dev, "Failed requesting ac97-reset gpio\n");
+		return ret;
+	}
+	cfg->gpio_reset = gpio;
+
+	return 0;
+}
+
+struct snd_ac97_bus_ops *soc_ac97_ops;
+EXPORT_SYMBOL_GPL(soc_ac97_ops);
+
+int snd_soc_set_ac97_ops(struct snd_ac97_bus_ops *ops)
+{
+	if (ops == soc_ac97_ops)
+		return 0;
+
+	if (soc_ac97_ops && ops)
+		return -EBUSY;
+
+	soc_ac97_ops = ops;
+	soc_ac97_bus.ops = ops;
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(snd_soc_set_ac97_ops);
+
+/**
+ * snd_soc_set_ac97_ops_of_reset - Set ac97 ops with generic ac97 reset functions
+ *
+ * This function sets the reset and warm_reset properties of ops and parses
+ * the device node of pdev to get pinctrl states and gpio numbers to use.
+ */
+int snd_soc_set_ac97_ops_of_reset(struct snd_ac97_bus_ops *ops,
+		struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct snd_ac97_reset_cfg cfg;
+	int ret;
+
+	ret = snd_soc_ac97_parse_pinctl(dev, &cfg);
+	if (ret)
+		return ret;
+
+	ret = snd_soc_set_ac97_ops(ops);
+	if (ret)
+		return ret;
+
+	ops->warm_reset = snd_soc_ac97_warm_reset;
+	ops->reset = snd_soc_ac97_reset;
+
+	snd_ac97_rst_cfg = cfg;
+	return 0;
+}
+EXPORT_SYMBOL_GPL(snd_soc_set_ac97_ops_of_reset);
diff --git a/sound/soc/soc-cache.c b/sound/soc/soc-cache.c
index a9f82b5aba9d..07f43356f963 100644
--- a/sound/soc/soc-cache.c
+++ b/sound/soc/soc-cache.c
@@ -15,56 +15,6 @@
 #include <linux/export.h>
 #include <linux/slab.h>
 
-#include <trace/events/asoc.h>
-
-static bool snd_soc_set_cache_val(void *base, unsigned int idx,
-				  unsigned int val, unsigned int word_size)
-{
-	switch (word_size) {
-	case 1: {
-		u8 *cache = base;
-		if (cache[idx] == val)
-			return true;
-		cache[idx] = val;
-		break;
-	}
-	case 2: {
-		u16 *cache = base;
-		if (cache[idx] == val)
-			return true;
-		cache[idx] = val;
-		break;
-	}
-	default:
-		WARN(1, "Invalid word_size %d\n", word_size);
-		break;
-	}
-	return false;
-}
-
-static unsigned int snd_soc_get_cache_val(const void *base, unsigned int idx,
-		unsigned int word_size)
-{
-	if (!base)
-		return -1;
-
-	switch (word_size) {
-	case 1: {
-		const u8 *cache = base;
-		return cache[idx];
-	}
-	case 2: {
-		const u16 *cache = base;
-		return cache[idx];
-	}
-	default:
-		WARN(1, "Invalid word_size %d\n", word_size);
-		break;
-	}
-	/* unreachable */
-	return -1;
-}
-
 int snd_soc_cache_init(struct snd_soc_codec *codec)
 {
 	const struct snd_soc_codec_driver *codec_drv = codec->driver;
@@ -75,8 +25,6 @@ int snd_soc_cache_init(struct snd_soc_codec *codec)
 	if (!reg_size)
 		return 0;
 
-	mutex_init(&codec->cache_rw_mutex);
-
 	dev_dbg(codec->dev, "ASoC: Initializing cache for %s codec\n",
 				codec->component.name);
 
@@ -103,100 +51,3 @@ int snd_soc_cache_exit(struct snd_soc_codec *codec)
 	codec->reg_cache = NULL;
 	return 0;
 }
-
-/**
- * snd_soc_cache_read: Fetch the value of a given register from the cache.
- *
- * @codec: CODEC to configure.
- * @reg: The register index.
- * @value: The value to be returned.
- */
-int snd_soc_cache_read(struct snd_soc_codec *codec,
-		       unsigned int reg, unsigned int *value)
-{
-	if (!value)
-		return -EINVAL;
-
-	mutex_lock(&codec->cache_rw_mutex);
-	if (!ZERO_OR_NULL_PTR(codec->reg_cache))
-		*value = snd_soc_get_cache_val(codec->reg_cache, reg,
-					       codec->driver->reg_word_size);
-	mutex_unlock(&codec->cache_rw_mutex);
-
-	return 0;
-}
-EXPORT_SYMBOL_GPL(snd_soc_cache_read);
-
-/**
- * snd_soc_cache_write: Set the value of a given register in the cache.
- *
- * @codec: CODEC to configure.
- * @reg: The register index.
- * @value: The new register value.
- */
-int snd_soc_cache_write(struct snd_soc_codec *codec,
-			unsigned int reg, unsigned int value)
-{
-	mutex_lock(&codec->cache_rw_mutex);
-	if (!ZERO_OR_NULL_PTR(codec->reg_cache))
-		snd_soc_set_cache_val(codec->reg_cache, reg, value,
-				      codec->driver->reg_word_size);
-	mutex_unlock(&codec->cache_rw_mutex);
-
-	return 0;
-}
-EXPORT_SYMBOL_GPL(snd_soc_cache_write);
-
-static int snd_soc_flat_cache_sync(struct snd_soc_codec *codec)
-{
-	int i;
-	int ret;
-	const struct snd_soc_codec_driver *codec_drv;
-	unsigned int val;
-
-	codec_drv = codec->driver;
-	for (i = 0; i < codec_drv->reg_cache_size; ++i) {
-		ret = snd_soc_cache_read(codec, i, &val);
-		if (ret)
-			return ret;
-		if (codec_drv->reg_cache_default)
-			if (snd_soc_get_cache_val(codec_drv->reg_cache_default,
-						  i, codec_drv->reg_word_size) == val)
-				continue;
-
-		ret = snd_soc_write(codec, i, val);
-		if (ret)
-			return ret;
-		dev_dbg(codec->dev, "ASoC: Synced register %#x, value = %#x\n",
-			i, val);
-	}
-	return 0;
-}
-
-/**
- * snd_soc_cache_sync: Sync the register cache with the hardware.
- *
- * @codec: CODEC to configure.
- *
- * Any registers that should not be synced should be marked as
- * volatile.  In general drivers can choose not to use the provided
- * syncing functionality if they so require.
- */
-int snd_soc_cache_sync(struct snd_soc_codec *codec)
-{
-	const char *name = "flat";
-	int ret;
-
-	if (!codec->cache_sync)
-		return 0;
-
-	dev_dbg(codec->dev, "ASoC: Syncing cache for %s codec\n",
-		codec->component.name);
-	trace_snd_soc_cache_sync(codec, name, "start");
-	ret = snd_soc_flat_cache_sync(codec);
-	if (!ret)
-		codec->cache_sync = 0;
-	trace_snd_soc_cache_sync(codec, name, "end");
-	return ret;
-}
-EXPORT_SYMBOL_GPL(snd_soc_cache_sync);
diff --git a/sound/soc/soc-compress.c b/sound/soc/soc-compress.c
index cecfab3cc948..590a82f01d0b 100644
--- a/sound/soc/soc-compress.c
+++ b/sound/soc/soc-compress.c
@@ -258,10 +258,7 @@ static int soc_compr_free_fe(struct snd_compr_stream *cstream)
 	list_for_each_entry(dpcm, &fe->dpcm[stream].be_clients, list_be)
 		dpcm->state = SND_SOC_DPCM_LINK_STATE_FREE;
 
-	if (stream == SNDRV_PCM_STREAM_PLAYBACK)
-		dpcm_dapm_stream_event(fe, stream, SND_SOC_DAPM_STREAM_STOP);
-	else
-		dpcm_dapm_stream_event(fe, stream, SND_SOC_DAPM_STREAM_STOP);
+	dpcm_dapm_stream_event(fe, stream, SND_SOC_DAPM_STREAM_STOP);
 
 	fe->dpcm[stream].state = SND_SOC_DPCM_STATE_CLOSE;
 	fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_NO;
@@ -456,11 +453,7 @@ static int soc_compr_set_params_fe(struct snd_compr_stream *cstream,
 	if (ret < 0)
 		goto out;
 
-	if (stream == SNDRV_PCM_STREAM_PLAYBACK)
-		dpcm_dapm_stream_event(fe, stream, SND_SOC_DAPM_STREAM_START);
-	else
-		dpcm_dapm_stream_event(fe, stream, SND_SOC_DAPM_STREAM_START);
-
+	dpcm_dapm_stream_event(fe, stream, SND_SOC_DAPM_STREAM_START);
 	fe->dpcm[stream].state = SND_SOC_DPCM_STATE_PREPARE;
 
 out:
diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c
index b60ff56ebc0f..935721062c21 100644
--- a/sound/soc/soc-core.c
+++ b/sound/soc/soc-core.c
@@ -34,9 +34,6 @@
 #include <linux/ctype.h>
 #include <linux/slab.h>
 #include <linux/of.h>
-#include <linux/gpio.h>
-#include <linux/of_gpio.h>
-#include <sound/ac97_codec.h>
 #include <sound/core.h>
 #include <sound/jack.h>
 #include <sound/pcm.h>
@@ -69,16 +66,6 @@ static int pmdown_time = 5000;
 module_param(pmdown_time, int, 0);
 MODULE_PARM_DESC(pmdown_time, "DAPM stream powerdown time (msecs)");
 
-struct snd_ac97_reset_cfg {
-	struct pinctrl *pctl;
-	struct pinctrl_state *pstate_reset;
-	struct pinctrl_state *pstate_warm_reset;
-	struct pinctrl_state *pstate_run;
-	int gpio_sdata;
-	int gpio_sync;
-	int gpio_reset;
-};
-
 /* returns the minimum number of bytes needed to represent
  * a particular given value */
 static int min_bytes_needed(unsigned long val)
@@ -309,9 +296,6 @@ static void soc_init_codec_debugfs(struct snd_soc_component *component)
 {
 	struct snd_soc_codec *codec = snd_soc_component_to_codec(component);
 
-	debugfs_create_bool("cache_sync", 0444, codec->component.debugfs_root,
-			    &codec->cache_sync);
-
 	codec->debugfs_reg = debugfs_create_file("codec_reg", 0644,
 						 codec->component.debugfs_root,
 						 codec, &codec_reg_fops);
@@ -499,40 +483,6 @@ struct snd_soc_pcm_runtime *snd_soc_get_pcm_runtime(struct snd_soc_card *card,
 }
 EXPORT_SYMBOL_GPL(snd_soc_get_pcm_runtime);
 
-#ifdef CONFIG_SND_SOC_AC97_BUS
-/* unregister ac97 codec */
-static int soc_ac97_dev_unregister(struct snd_soc_codec *codec)
-{
-	if (codec->ac97->dev.bus)
-		device_unregister(&codec->ac97->dev);
-	return 0;
-}
-
-/* stop no dev release warning */
-static void soc_ac97_device_release(struct device *dev){}
-
-/* register ac97 codec to bus */
-static int soc_ac97_dev_register(struct snd_soc_codec *codec)
-{
-	int err;
-
-	codec->ac97->dev.bus = &ac97_bus_type;
-	codec->ac97->dev.parent = codec->component.card->dev;
-	codec->ac97->dev.release = soc_ac97_device_release;
-
-	dev_set_name(&codec->ac97->dev, "%d-%d:%s",
-		     codec->component.card->snd_card->number, 0,
-		     codec->component.name);
-	err = device_register(&codec->ac97->dev);
-	if (err < 0) {
-		dev_err(codec->dev, "ASoC: Can't register ac97 bus\n");
-		codec->ac97->dev.bus = NULL;
-		return err;
-	}
-	return 0;
-}
-#endif
-
 static void codec2codec_close_delayed_work(struct work_struct *work)
 {
 	/* Currently nothing to do for c2c links
@@ -592,17 +542,12 @@ int snd_soc_suspend(struct device *dev)
 
 	for (i = 0; i < card->num_rtd; i++) {
 		struct snd_soc_dai *cpu_dai = card->rtd[i].cpu_dai;
-		struct snd_soc_platform *platform = card->rtd[i].platform;
 
 		if (card->rtd[i].dai_link->ignore_suspend)
 			continue;
 
-		if (cpu_dai->driver->suspend && !cpu_dai->driver->ac97_control)
+		if (cpu_dai->driver->suspend && !cpu_dai->driver->bus_control)
 			cpu_dai->driver->suspend(cpu_dai);
-		if (platform->driver->suspend && !platform->suspended) {
-			platform->driver->suspend(cpu_dai);
-			platform->suspended = 1;
-		}
 	}
 
 	/* close any waiting streams and save state */
@@ -629,8 +574,8 @@ int snd_soc_suspend(struct device *dev)
 					  SND_SOC_DAPM_STREAM_SUSPEND);
 	}
 
-	/* Recheck all analogue paths too */
-	dapm_mark_io_dirty(&card->dapm);
+	/* Recheck all endpoints too, their state is affected by suspend */
+	dapm_mark_endpoints_dirty(card);
 	snd_soc_dapm_sync(&card->dapm);
 
 	/* suspend all CODECs */
@@ -656,7 +601,6 @@ int snd_soc_suspend(struct device *dev)
 				if (codec->driver->suspend)
 					codec->driver->suspend(codec);
 				codec->suspended = 1;
-				codec->cache_sync = 1;
 				if (codec->component.regmap)
 					regcache_mark_dirty(codec->component.regmap);
 				/* deactivate pins to sleep state */
@@ -676,7 +620,7 @@ int snd_soc_suspend(struct device *dev)
 		if (card->rtd[i].dai_link->ignore_suspend)
 			continue;
 
-		if (cpu_dai->driver->suspend && cpu_dai->driver->ac97_control)
+		if (cpu_dai->driver->suspend && cpu_dai->driver->bus_control)
 			cpu_dai->driver->suspend(cpu_dai);
 
 		/* deactivate pins to sleep state */
@@ -712,14 +656,14 @@ static void soc_resume_deferred(struct work_struct *work)
 	if (card->resume_pre)
 		card->resume_pre(card);
 
-	/* resume AC97 DAIs */
+	/* resume control bus DAIs */
 	for (i = 0; i < card->num_rtd; i++) {
 		struct snd_soc_dai *cpu_dai = card->rtd[i].cpu_dai;
 
 		if (card->rtd[i].dai_link->ignore_suspend)
 			continue;
 
-		if (cpu_dai->driver->resume && cpu_dai->driver->ac97_control)
+		if (cpu_dai->driver->resume && cpu_dai->driver->bus_control)
 			cpu_dai->driver->resume(cpu_dai);
 	}
 
@@ -775,17 +719,12 @@ static void soc_resume_deferred(struct work_struct *work)
 
 	for (i = 0; i < card->num_rtd; i++) {
 		struct snd_soc_dai *cpu_dai = card->rtd[i].cpu_dai;
-		struct snd_soc_platform *platform = card->rtd[i].platform;
 
 		if (card->rtd[i].dai_link->ignore_suspend)
 			continue;
 
-		if (cpu_dai->driver->resume && !cpu_dai->driver->ac97_control)
+		if (cpu_dai->driver->resume && !cpu_dai->driver->bus_control)
 			cpu_dai->driver->resume(cpu_dai);
-		if (platform->driver->resume && platform->suspended) {
-			platform->driver->resume(cpu_dai);
-			platform->suspended = 0;
-		}
 	}
 
 	if (card->resume_post)
@@ -796,8 +735,8 @@ static void soc_resume_deferred(struct work_struct *work)
 	/* userspace can access us now we are back as we were before */
 	snd_power_change_state(card->snd_card, SNDRV_CTL_POWER_D0);
 
-	/* Recheck all analogue paths too */
-	dapm_mark_io_dirty(&card->dapm);
+	/* Recheck all endpoints too, their state is affected by suspend */
+	dapm_mark_endpoints_dirty(card);
 	snd_soc_dapm_sync(&card->dapm);
 }
 
@@ -805,7 +744,8 @@ static void soc_resume_deferred(struct work_struct *work)
 int snd_soc_resume(struct device *dev)
 {
 	struct snd_soc_card *card = dev_get_drvdata(dev);
-	int i, ac97_control = 0;
+	bool bus_control = false;
+	int i;
 
 	/* If the card is not initialized yet there is nothing to do */
 	if (!card->instantiated)
@@ -828,17 +768,18 @@ int snd_soc_resume(struct device *dev)
 		}
 	}
 
-	/* AC97 devices might have other drivers hanging off them so
-	 * need to resume immediately.  Other drivers don't have that
-	 * problem and may take a substantial amount of time to resume
+	/*
+	 * DAIs that also act as the control bus master might have other drivers
+	 * hanging off them so need to resume immediately. Other drivers don't
+	 * have that problem and may take a substantial amount of time to resume
 	 * due to I/O costs and anti-pop so handle them out of line.
 	 */
 	for (i = 0; i < card->num_rtd; i++) {
 		struct snd_soc_dai *cpu_dai = card->rtd[i].cpu_dai;
-		ac97_control |= cpu_dai->driver->ac97_control;
+		bus_control |= cpu_dai->driver->bus_control;
 	}
-	if (ac97_control) {
-		dev_dbg(dev, "ASoC: Resuming AC97 immediately\n");
+	if (bus_control) {
+		dev_dbg(dev, "ASoC: Resuming control bus master immediately\n");
 		soc_resume_deferred(&card->deferred_resume_work);
 	} else {
 		dev_dbg(dev, "ASoC: Scheduling resume work\n");
@@ -1251,25 +1192,22 @@ static int soc_probe_link_components(struct snd_soc_card *card, int num,
 	return 0;
 }
 
-static int soc_probe_codec_dai(struct snd_soc_card *card,
-			       struct snd_soc_dai *codec_dai,
-			       int order)
+static int soc_probe_dai(struct snd_soc_dai *dai, int order)
 {
 	int ret;
 
-	if (!codec_dai->probed && codec_dai->driver->probe_order == order) {
-		if (codec_dai->driver->probe) {
-			ret = codec_dai->driver->probe(codec_dai);
+	if (!dai->probed && dai->driver->probe_order == order) {
+		if (dai->driver->probe) {
+			ret = dai->driver->probe(dai);
 			if (ret < 0) {
-				dev_err(codec_dai->dev,
-					"ASoC: failed to probe CODEC DAI %s: %d\n",
-					codec_dai->name, ret);
+				dev_err(dai->dev,
+					"ASoC: failed to probe DAI %s: %d\n",
+					dai->name, ret);
 				return ret;
 			}
 		}
 
-		/* mark codec_dai as probed and add to card dai list */
-		codec_dai->probed = 1;
+		dai->probed = 1;
 	}
 
 	return 0;
@@ -1319,40 +1257,22 @@ static int soc_probe_link_dais(struct snd_soc_card *card, int num, int order)
 {
 	struct snd_soc_dai_link *dai_link = &card->dai_link[num];
 	struct snd_soc_pcm_runtime *rtd = &card->rtd[num];
-	struct snd_soc_platform *platform = rtd->platform;
 	struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
 	int i, ret;
 
 	dev_dbg(card->dev, "ASoC: probe %s dai link %d late %d\n",
 			card->name, num, order);
 
-	/* config components */
-	cpu_dai->platform = platform;
-	cpu_dai->card = card;
-	for (i = 0; i < rtd->num_codecs; i++)
-		rtd->codec_dais[i]->card = card;
-
 	/* set default power off timeout */
 	rtd->pmdown_time = pmdown_time;
 
-	/* probe the cpu_dai */
-	if (!cpu_dai->probed &&
-			cpu_dai->driver->probe_order == order) {
-		if (cpu_dai->driver->probe) {
-			ret = cpu_dai->driver->probe(cpu_dai);
-			if (ret < 0) {
-				dev_err(cpu_dai->dev,
-					"ASoC: failed to probe CPU DAI %s: %d\n",
-					cpu_dai->name, ret);
-				return ret;
-			}
-		}
-		cpu_dai->probed = 1;
-	}
+	ret = soc_probe_dai(cpu_dai, order);
+	if (ret)
+		return ret;
 
 	/* probe the CODEC DAI */
 	for (i = 0; i < rtd->num_codecs; i++) {
-		ret = soc_probe_codec_dai(card, rtd->codec_dais[i], order);
+		ret = soc_probe_dai(rtd->codec_dais[i], order);
 		if (ret)
 			return ret;
 	}
@@ -1422,84 +1342,9 @@ static int soc_probe_link_dais(struct snd_soc_card *card, int num, int order)
 		}
 	}
 
-	/* add platform data for AC97 devices */
-	for (i = 0; i < rtd->num_codecs; i++) {
-		if (rtd->codec_dais[i]->driver->ac97_control)
-			snd_ac97_dev_add_pdata(rtd->codec_dais[i]->codec->ac97,
-					       rtd->cpu_dai->ac97_pdata);
-	}
-
 	return 0;
 }
 
-#ifdef CONFIG_SND_SOC_AC97_BUS
-static int soc_register_ac97_codec(struct snd_soc_codec *codec,
-				   struct snd_soc_dai *codec_dai)
-{
-	int ret;
-
-	/* Only instantiate AC97 if not already done by the adaptor
-	 * for the generic AC97 subsystem.
-	 */
-	if (codec_dai->driver->ac97_control && !codec->ac97_registered) {
-		/*
-		 * It is possible that the AC97 device is already registered to
-		 * the device subsystem. This happens when the device is created
-		 * via snd_ac97_mixer(). Currently only SoC codec that does so
-		 * is the generic AC97 glue but others migh emerge.
-		 *
-		 * In those cases we don't try to register the device again.
-		 */
-		if (!codec->ac97_created)
-			return 0;
-
-		ret = soc_ac97_dev_register(codec);
-		if (ret < 0) {
-			dev_err(codec->dev,
-				"ASoC: AC97 device register failed: %d\n", ret);
-			return ret;
-		}
-
-		codec->ac97_registered = 1;
-	}
-	return 0;
-}
-
-static void soc_unregister_ac97_codec(struct snd_soc_codec *codec)
-{
-	if (codec->ac97_registered) {
-		soc_ac97_dev_unregister(codec);
-		codec->ac97_registered = 0;
-	}
-}
-
-static int soc_register_ac97_dai_link(struct snd_soc_pcm_runtime *rtd)
-{
-	int i, ret;
-
-	for (i = 0; i < rtd->num_codecs; i++) {
-		struct snd_soc_dai *codec_dai = rtd->codec_dais[i];
-
-		ret = soc_register_ac97_codec(codec_dai->codec, codec_dai);
-		if (ret) {
-			while (--i >= 0)
-				soc_unregister_ac97_codec(codec_dai->codec);
-			return ret;
-		}
-	}
-
-	return 0;
-}
-
-static void soc_unregister_ac97_dai_link(struct snd_soc_pcm_runtime *rtd)
-{
-	int i;
-
-	for (i = 0; i < rtd->num_codecs; i++)
-		soc_unregister_ac97_codec(rtd->codec_dais[i]->codec);
-}
-#endif
-
 static int soc_bind_aux_dev(struct snd_soc_card *card, int num)
 {
 	struct snd_soc_pcm_runtime *rtd = &card->rtd_aux[num];
@@ -1793,20 +1638,6 @@ static int snd_soc_instantiate_card(struct snd_soc_card *card)
 		goto probe_aux_dev_err;
 	}
 
-#ifdef CONFIG_SND_SOC_AC97_BUS
-	/* register any AC97 codecs */
-	for (i = 0; i < card->num_rtd; i++) {
-		ret = soc_register_ac97_dai_link(&card->rtd[i]);
-		if (ret < 0) {
-			dev_err(card->dev,
-				"ASoC: failed to register AC97: %d\n", ret);
-			while (--i >= 0)
-				soc_unregister_ac97_dai_link(&card->rtd[i]);
-			goto probe_aux_dev_err;
-		}
-	}
-#endif
-
 	card->instantiated = 1;
 	snd_soc_dapm_sync(&card->dapm);
 	mutex_unlock(&card->mutex);
@@ -1949,216 +1780,6 @@ static struct platform_driver soc_driver = {
 };
 
 /**
- * snd_soc_new_ac97_codec - initailise AC97 device
- * @codec: audio codec
- * @ops: AC97 bus operations
- * @num: AC97 codec number
- *
- * Initialises AC97 codec resources for use by ad-hoc devices only.
- */
-int snd_soc_new_ac97_codec(struct snd_soc_codec *codec,
-	struct snd_ac97_bus_ops *ops, int num)
-{
-	codec->ac97 = kzalloc(sizeof(struct snd_ac97), GFP_KERNEL);
-	if (codec->ac97 == NULL)
-		return -ENOMEM;
-
-	codec->ac97->bus = kzalloc(sizeof(struct snd_ac97_bus), GFP_KERNEL);
-	if (codec->ac97->bus == NULL) {
-		kfree(codec->ac97);
-		codec->ac97 = NULL;
-		return -ENOMEM;
-	}
-
-	codec->ac97->bus->ops = ops;
-	codec->ac97->num = num;
-
-	/*
-	 * Mark the AC97 device to be created by us. This way we ensure that the
-	 * device will be registered with the device subsystem later on.
-	 */
-	codec->ac97_created = 1;
-
-	return 0;
-}
-EXPORT_SYMBOL_GPL(snd_soc_new_ac97_codec);
-
-static struct snd_ac97_reset_cfg snd_ac97_rst_cfg;
-
-static void snd_soc_ac97_warm_reset(struct snd_ac97 *ac97)
-{
-	struct pinctrl *pctl = snd_ac97_rst_cfg.pctl;
-
-	pinctrl_select_state(pctl, snd_ac97_rst_cfg.pstate_warm_reset);
-
-	gpio_direction_output(snd_ac97_rst_cfg.gpio_sync, 1);
-
-	udelay(10);
-
-	gpio_direction_output(snd_ac97_rst_cfg.gpio_sync, 0);
-
-	pinctrl_select_state(pctl, snd_ac97_rst_cfg.pstate_run);
-	msleep(2);
-}
-
-static void snd_soc_ac97_reset(struct snd_ac97 *ac97)
-{
-	struct pinctrl *pctl = snd_ac97_rst_cfg.pctl;
-
-	pinctrl_select_state(pctl, snd_ac97_rst_cfg.pstate_reset);
-
-	gpio_direction_output(snd_ac97_rst_cfg.gpio_sync, 0);
-	gpio_direction_output(snd_ac97_rst_cfg.gpio_sdata, 0);
-	gpio_direction_output(snd_ac97_rst_cfg.gpio_reset, 0);
-
-	udelay(10);
-
-	gpio_direction_output(snd_ac97_rst_cfg.gpio_reset, 1);
-
-	pinctrl_select_state(pctl, snd_ac97_rst_cfg.pstate_run);
-	msleep(2);
-}
-
-static int snd_soc_ac97_parse_pinctl(struct device *dev,
-		struct snd_ac97_reset_cfg *cfg)
-{
-	struct pinctrl *p;
-	struct pinctrl_state *state;
-	int gpio;
-	int ret;
-
-	p = devm_pinctrl_get(dev);
-	if (IS_ERR(p)) {
-		dev_err(dev, "Failed to get pinctrl\n");
-		return PTR_ERR(p);
-	}
-	cfg->pctl = p;
-
-	state = pinctrl_lookup_state(p, "ac97-reset");
-	if (IS_ERR(state)) {
-		dev_err(dev, "Can't find pinctrl state ac97-reset\n");
-		return PTR_ERR(state);
-	}
-	cfg->pstate_reset = state;
-
-	state = pinctrl_lookup_state(p, "ac97-warm-reset");
-	if (IS_ERR(state)) {
-		dev_err(dev, "Can't find pinctrl state ac97-warm-reset\n");
-		return PTR_ERR(state);
-	}
-	cfg->pstate_warm_reset = state;
-
-	state = pinctrl_lookup_state(p, "ac97-running");
-	if (IS_ERR(state)) {
-		dev_err(dev, "Can't find pinctrl state ac97-running\n");
-		return PTR_ERR(state);
-	}
-	cfg->pstate_run = state;
-
-	gpio = of_get_named_gpio(dev->of_node, "ac97-gpios", 0);
-	if (gpio < 0) {
-		dev_err(dev, "Can't find ac97-sync gpio\n");
-		return gpio;
-	}
-	ret = devm_gpio_request(dev, gpio, "AC97 link sync");
-	if (ret) {
-		dev_err(dev, "Failed requesting ac97-sync gpio\n");
-		return ret;
-	}
-	cfg->gpio_sync = gpio;
-
-	gpio = of_get_named_gpio(dev->of_node, "ac97-gpios", 1);
-	if (gpio < 0) {
-		dev_err(dev, "Can't find ac97-sdata gpio %d\n", gpio);
-		return gpio;
-	}
-	ret = devm_gpio_request(dev, gpio, "AC97 link sdata");
-	if (ret) {
-		dev_err(dev, "Failed requesting ac97-sdata gpio\n");
-		return ret;
-	}
-	cfg->gpio_sdata = gpio;
-
-	gpio = of_get_named_gpio(dev->of_node, "ac97-gpios", 2);
-	if (gpio < 0) {
-		dev_err(dev, "Can't find ac97-reset gpio\n");
-		return gpio;
-	}
-	ret = devm_gpio_request(dev, gpio, "AC97 link reset");
-	if (ret) {
-		dev_err(dev, "Failed requesting ac97-reset gpio\n");
-		return ret;
-	}
-	cfg->gpio_reset = gpio;
-
-	return 0;
-}
-
-struct snd_ac97_bus_ops *soc_ac97_ops;
-EXPORT_SYMBOL_GPL(soc_ac97_ops);
-
-int snd_soc_set_ac97_ops(struct snd_ac97_bus_ops *ops)
-{
-	if (ops == soc_ac97_ops)
-		return 0;
-
-	if (soc_ac97_ops && ops)
-		return -EBUSY;
-
-	soc_ac97_ops = ops;
-
-	return 0;
-}
-EXPORT_SYMBOL_GPL(snd_soc_set_ac97_ops);
-
-/**
- * snd_soc_set_ac97_ops_of_reset - Set ac97 ops with generic ac97 reset functions
- *
- * This function sets the reset and warm_reset properties of ops and parses
- * the device node of pdev to get pinctrl states and gpio numbers to use.
- */
-int snd_soc_set_ac97_ops_of_reset(struct snd_ac97_bus_ops *ops,
-		struct platform_device *pdev)
-{
-	struct device *dev = &pdev->dev;
-	struct snd_ac97_reset_cfg cfg;
-	int ret;
-
-	ret = snd_soc_ac97_parse_pinctl(dev, &cfg);
-	if (ret)
-		return ret;
-
-	ret = snd_soc_set_ac97_ops(ops);
-	if (ret)
-		return ret;
-
-	ops->warm_reset = snd_soc_ac97_warm_reset;
-	ops->reset = snd_soc_ac97_reset;
-
-	snd_ac97_rst_cfg = cfg;
-	return 0;
-}
-EXPORT_SYMBOL_GPL(snd_soc_set_ac97_ops_of_reset);
-
-/**
- * snd_soc_free_ac97_codec - free AC97 codec device
- * @codec: audio codec
- *
- * Frees AC97 codec device resources.
- */
-void snd_soc_free_ac97_codec(struct snd_soc_codec *codec)
-{
-#ifdef CONFIG_SND_SOC_AC97_BUS
-	soc_unregister_ac97_codec(codec);
-#endif
-	kfree(codec->ac97->bus);
-	kfree(codec->ac97);
-	codec->ac97 = NULL;
-	codec->ac97_created = 0;
-}
-EXPORT_SYMBOL_GPL(snd_soc_free_ac97_codec);
-
-/**
  * snd_soc_cnew - create new control
  * @_template: control template
  * @data: control private data
@@ -2326,7 +1947,7 @@ EXPORT_SYMBOL_GPL(snd_soc_add_card_controls);
 int snd_soc_add_dai_controls(struct snd_soc_dai *dai,
 	const struct snd_kcontrol_new *controls, int num_controls)
 {
-	struct snd_card *card = dai->card->snd_card;
+	struct snd_card *card = dai->component->card->snd_card;
 
 	return snd_soc_add_controls(card, dai->dev, controls, num_controls,
 			NULL, dai);
@@ -2334,1020 +1955,6 @@ int snd_soc_add_dai_controls(struct snd_soc_dai *dai,
 EXPORT_SYMBOL_GPL(snd_soc_add_dai_controls);
 
 /**
- * snd_soc_info_enum_double - enumerated double mixer info callback
- * @kcontrol: mixer control
- * @uinfo: control element information
- *
- * Callback to provide information about a double enumerated
- * mixer control.
- *
- * Returns 0 for success.
- */
-int snd_soc_info_enum_double(struct snd_kcontrol *kcontrol,
-	struct snd_ctl_elem_info *uinfo)
-{
-	struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
-
-	uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
-	uinfo->count = e->shift_l == e->shift_r ? 1 : 2;
-	uinfo->value.enumerated.items = e->items;
-
-	if (uinfo->value.enumerated.item >= e->items)
-		uinfo->value.enumerated.item = e->items - 1;
-	strlcpy(uinfo->value.enumerated.name,
-		e->texts[uinfo->value.enumerated.item],
-		sizeof(uinfo->value.enumerated.name));
-	return 0;
-}
-EXPORT_SYMBOL_GPL(snd_soc_info_enum_double);
-
-/**
- * snd_soc_get_enum_double - enumerated double mixer get callback
- * @kcontrol: mixer control
- * @ucontrol: control element information
- *
- * Callback to get the value of a double enumerated mixer.
- *
- * Returns 0 for success.
- */
-int snd_soc_get_enum_double(struct snd_kcontrol *kcontrol,
-	struct snd_ctl_elem_value *ucontrol)
-{
-	struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
-	struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
-	unsigned int val, item;
-	unsigned int reg_val;
-	int ret;
-
-	ret = snd_soc_component_read(component, e->reg, &reg_val);
-	if (ret)
-		return ret;
-	val = (reg_val >> e->shift_l) & e->mask;
-	item = snd_soc_enum_val_to_item(e, val);
-	ucontrol->value.enumerated.item[0] = item;
-	if (e->shift_l != e->shift_r) {
-		val = (reg_val >> e->shift_l) & e->mask;
-		item = snd_soc_enum_val_to_item(e, val);
-		ucontrol->value.enumerated.item[1] = item;
-	}
-
-	return 0;
-}
-EXPORT_SYMBOL_GPL(snd_soc_get_enum_double);
-
-/**
- * snd_soc_put_enum_double - enumerated double mixer put callback
- * @kcontrol: mixer control
- * @ucontrol: control element information
- *
- * Callback to set the value of a double enumerated mixer.
- *
- * Returns 0 for success.
- */
-int snd_soc_put_enum_double(struct snd_kcontrol *kcontrol,
-	struct snd_ctl_elem_value *ucontrol)
-{
-	struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
-	struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
-	unsigned int *item = ucontrol->value.enumerated.item;
-	unsigned int val;
-	unsigned int mask;
-
-	if (item[0] >= e->items)
-		return -EINVAL;
-	val = snd_soc_enum_item_to_val(e, item[0]) << e->shift_l;
-	mask = e->mask << e->shift_l;
-	if (e->shift_l != e->shift_r) {
-		if (item[1] >= e->items)
-			return -EINVAL;
-		val |= snd_soc_enum_item_to_val(e, item[1]) << e->shift_r;
-		mask |= e->mask << e->shift_r;
-	}
-
-	return snd_soc_component_update_bits(component, e->reg, mask, val);
-}
-EXPORT_SYMBOL_GPL(snd_soc_put_enum_double);
-
-/**
- * snd_soc_read_signed - Read a codec register and interprete as signed value
- * @component: component
- * @reg: Register to read
- * @mask: Mask to use after shifting the register value
- * @shift: Right shift of register value
- * @sign_bit: Bit that describes if a number is negative or not.
- * @signed_val: Pointer to where the read value should be stored
- *
- * This functions reads a codec register. The register value is shifted right
- * by 'shift' bits and masked with the given 'mask'. Afterwards it translates
- * the given registervalue into a signed integer if sign_bit is non-zero.
- *
- * Returns 0 on sucess, otherwise an error value
- */
-static int snd_soc_read_signed(struct snd_soc_component *component,
-	unsigned int reg, unsigned int mask, unsigned int shift,
-	unsigned int sign_bit, int *signed_val)
-{
-	int ret;
-	unsigned int val;
-
-	ret = snd_soc_component_read(component, reg, &val);
-	if (ret < 0)
-		return ret;
-
-	val = (val >> shift) & mask;
-
-	if (!sign_bit) {
-		*signed_val = val;
-		return 0;
-	}
-
-	/* non-negative number */
-	if (!(val & BIT(sign_bit))) {
-		*signed_val = val;
-		return 0;
-	}
-
-	ret = val;
-
-	/*
-	 * The register most probably does not contain a full-sized int.
-	 * Instead we have an arbitrary number of bits in a signed
-	 * representation which has to be translated into a full-sized int.
-	 * This is done by filling up all bits above the sign-bit.
-	 */
-	ret |= ~((int)(BIT(sign_bit) - 1));
-
-	*signed_val = ret;
-
-	return 0;
-}
-
-/**
- * snd_soc_info_volsw - single mixer info callback
- * @kcontrol: mixer control
- * @uinfo: control element information
- *
- * Callback to provide information about a single mixer control, or a double
- * mixer control that spans 2 registers.
- *
- * Returns 0 for success.
- */
-int snd_soc_info_volsw(struct snd_kcontrol *kcontrol,
-	struct snd_ctl_elem_info *uinfo)
-{
-	struct soc_mixer_control *mc =
-		(struct soc_mixer_control *)kcontrol->private_value;
-	int platform_max;
-
-	if (!mc->platform_max)
-		mc->platform_max = mc->max;
-	platform_max = mc->platform_max;
-
-	if (platform_max == 1 && !strstr(kcontrol->id.name, " Volume"))
-		uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
-	else
-		uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
-
-	uinfo->count = snd_soc_volsw_is_stereo(mc) ? 2 : 1;
-	uinfo->value.integer.min = 0;
-	uinfo->value.integer.max = platform_max - mc->min;
-	return 0;
-}
-EXPORT_SYMBOL_GPL(snd_soc_info_volsw);
-
-/**
- * snd_soc_get_volsw - single mixer get callback
- * @kcontrol: mixer control
- * @ucontrol: control element information
- *
- * Callback to get the value of a single mixer control, or a double mixer
- * control that spans 2 registers.
- *
- * Returns 0 for success.
- */
-int snd_soc_get_volsw(struct snd_kcontrol *kcontrol,
-	struct snd_ctl_elem_value *ucontrol)
-{
-	struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
-	struct soc_mixer_control *mc =
-		(struct soc_mixer_control *)kcontrol->private_value;
-	unsigned int reg = mc->reg;
-	unsigned int reg2 = mc->rreg;
-	unsigned int shift = mc->shift;
-	unsigned int rshift = mc->rshift;
-	int max = mc->max;
-	int min = mc->min;
-	int sign_bit = mc->sign_bit;
-	unsigned int mask = (1 << fls(max)) - 1;
-	unsigned int invert = mc->invert;
-	int val;
-	int ret;
-
-	if (sign_bit)
-		mask = BIT(sign_bit + 1) - 1;
-
-	ret = snd_soc_read_signed(component, reg, mask, shift, sign_bit, &val);
-	if (ret)
-		return ret;
-
-	ucontrol->value.integer.value[0] = val - min;
-	if (invert)
-		ucontrol->value.integer.value[0] =
-			max - ucontrol->value.integer.value[0];
-
-	if (snd_soc_volsw_is_stereo(mc)) {
-		if (reg == reg2)
-			ret = snd_soc_read_signed(component, reg, mask, rshift,
-				sign_bit, &val);
-		else
-			ret = snd_soc_read_signed(component, reg2, mask, shift,
-				sign_bit, &val);
-		if (ret)
-			return ret;
-
-		ucontrol->value.integer.value[1] = val - min;
-		if (invert)
-			ucontrol->value.integer.value[1] =
-				max - ucontrol->value.integer.value[1];
-	}
-
-	return 0;
-}
-EXPORT_SYMBOL_GPL(snd_soc_get_volsw);
-
-/**
- * snd_soc_put_volsw - single mixer put callback
- * @kcontrol: mixer control
- * @ucontrol: control element information
- *
- * Callback to set the value of a single mixer control, or a double mixer
- * control that spans 2 registers.
- *
- * Returns 0 for success.
- */
-int snd_soc_put_volsw(struct snd_kcontrol *kcontrol,
-	struct snd_ctl_elem_value *ucontrol)
-{
-	struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
-	struct soc_mixer_control *mc =
-		(struct soc_mixer_control *)kcontrol->private_value;
-	unsigned int reg = mc->reg;
-	unsigned int reg2 = mc->rreg;
-	unsigned int shift = mc->shift;
-	unsigned int rshift = mc->rshift;
-	int max = mc->max;
-	int min = mc->min;
-	unsigned int sign_bit = mc->sign_bit;
-	unsigned int mask = (1 << fls(max)) - 1;
-	unsigned int invert = mc->invert;
-	int err;
-	bool type_2r = false;
-	unsigned int val2 = 0;
-	unsigned int val, val_mask;
-
-	if (sign_bit)
-		mask = BIT(sign_bit + 1) - 1;
-
-	val = ((ucontrol->value.integer.value[0] + min) & mask);
-	if (invert)
-		val = max - val;
-	val_mask = mask << shift;
-	val = val << shift;
-	if (snd_soc_volsw_is_stereo(mc)) {
-		val2 = ((ucontrol->value.integer.value[1] + min) & mask);
-		if (invert)
-			val2 = max - val2;
-		if (reg == reg2) {
-			val_mask |= mask << rshift;
-			val |= val2 << rshift;
-		} else {
-			val2 = val2 << shift;
-			type_2r = true;
-		}
-	}
-	err = snd_soc_component_update_bits(component, reg, val_mask, val);
-	if (err < 0)
-		return err;
-
-	if (type_2r)
-		err = snd_soc_component_update_bits(component, reg2, val_mask,
-			val2);
-
-	return err;
-}
-EXPORT_SYMBOL_GPL(snd_soc_put_volsw);
-
-/**
- * snd_soc_get_volsw_sx - single mixer get callback
- * @kcontrol: mixer control
- * @ucontrol: control element information
- *
- * Callback to get the value of a single mixer control, or a double mixer
- * control that spans 2 registers.
- *
- * Returns 0 for success.
- */
-int snd_soc_get_volsw_sx(struct snd_kcontrol *kcontrol,
-		      struct snd_ctl_elem_value *ucontrol)
-{
-	struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
-	struct soc_mixer_control *mc =
-	    (struct soc_mixer_control *)kcontrol->private_value;
-	unsigned int reg = mc->reg;
-	unsigned int reg2 = mc->rreg;
-	unsigned int shift = mc->shift;
-	unsigned int rshift = mc->rshift;
-	int max = mc->max;
-	int min = mc->min;
-	int mask = (1 << (fls(min + max) - 1)) - 1;
-	unsigned int val;
-	int ret;
-
-	ret = snd_soc_component_read(component, reg, &val);
-	if (ret < 0)
-		return ret;
-
-	ucontrol->value.integer.value[0] = ((val >> shift) - min) & mask;
-
-	if (snd_soc_volsw_is_stereo(mc)) {
-		ret = snd_soc_component_read(component, reg2, &val);
-		if (ret < 0)
-			return ret;
-
-		val = ((val >> rshift) - min) & mask;
-		ucontrol->value.integer.value[1] = val;
-	}
-
-	return 0;
-}
-EXPORT_SYMBOL_GPL(snd_soc_get_volsw_sx);
-
-/**
- * snd_soc_put_volsw_sx - double mixer set callback
- * @kcontrol: mixer control
- * @uinfo: control element information
- *
- * Callback to set the value of a double mixer control that spans 2 registers.
- *
- * Returns 0 for success.
- */
-int snd_soc_put_volsw_sx(struct snd_kcontrol *kcontrol,
-			 struct snd_ctl_elem_value *ucontrol)
-{
-	struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
-	struct soc_mixer_control *mc =
-	    (struct soc_mixer_control *)kcontrol->private_value;
-
-	unsigned int reg = mc->reg;
-	unsigned int reg2 = mc->rreg;
-	unsigned int shift = mc->shift;
-	unsigned int rshift = mc->rshift;
-	int max = mc->max;
-	int min = mc->min;
-	int mask = (1 << (fls(min + max) - 1)) - 1;
-	int err = 0;
-	unsigned int val, val_mask, val2 = 0;
-
-	val_mask = mask << shift;
-	val = (ucontrol->value.integer.value[0] + min) & mask;
-	val = val << shift;
-
-	err = snd_soc_component_update_bits(component, reg, val_mask, val);
-	if (err < 0)
-		return err;
-
-	if (snd_soc_volsw_is_stereo(mc)) {
-		val_mask = mask << rshift;
-		val2 = (ucontrol->value.integer.value[1] + min) & mask;
-		val2 = val2 << rshift;
-
-		err = snd_soc_component_update_bits(component, reg2, val_mask,
-			val2);
-	}
-	return err;
-}
-EXPORT_SYMBOL_GPL(snd_soc_put_volsw_sx);
-
-/**
- * snd_soc_info_volsw_s8 - signed mixer info callback
- * @kcontrol: mixer control
- * @uinfo: control element information
- *
- * Callback to provide information about a signed mixer control.
- *
- * Returns 0 for success.
- */
-int snd_soc_info_volsw_s8(struct snd_kcontrol *kcontrol,
-	struct snd_ctl_elem_info *uinfo)
-{
-	struct soc_mixer_control *mc =
-		(struct soc_mixer_control *)kcontrol->private_value;
-	int platform_max;
-	int min = mc->min;
-
-	if (!mc->platform_max)
-		mc->platform_max = mc->max;
-	platform_max = mc->platform_max;
-
-	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
-	uinfo->count = 2;
-	uinfo->value.integer.min = 0;
-	uinfo->value.integer.max = platform_max - min;
-	return 0;
-}
-EXPORT_SYMBOL_GPL(snd_soc_info_volsw_s8);
-
-/**
- * snd_soc_get_volsw_s8 - signed mixer get callback
- * @kcontrol: mixer control
- * @ucontrol: control element information
- *
- * Callback to get the value of a signed mixer control.
- *
- * Returns 0 for success.
- */
-int snd_soc_get_volsw_s8(struct snd_kcontrol *kcontrol,
-	struct snd_ctl_elem_value *ucontrol)
-{
-	struct soc_mixer_control *mc =
-		(struct soc_mixer_control *)kcontrol->private_value;
-	struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
-	unsigned int reg = mc->reg;
-	unsigned int val;
-	int min = mc->min;
-	int ret;
-
-	ret = snd_soc_component_read(component, reg, &val);
-	if (ret)
-		return ret;
-
-	ucontrol->value.integer.value[0] =
-		((signed char)(val & 0xff))-min;
-	ucontrol->value.integer.value[1] =
-		((signed char)((val >> 8) & 0xff))-min;
-	return 0;
-}
-EXPORT_SYMBOL_GPL(snd_soc_get_volsw_s8);
-
-/**
- * snd_soc_put_volsw_sgn - signed mixer put callback
- * @kcontrol: mixer control
- * @ucontrol: control element information
- *
- * Callback to set the value of a signed mixer control.
- *
- * Returns 0 for success.
- */
-int snd_soc_put_volsw_s8(struct snd_kcontrol *kcontrol,
-	struct snd_ctl_elem_value *ucontrol)
-{
-	struct soc_mixer_control *mc =
-		(struct soc_mixer_control *)kcontrol->private_value;
-	struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
-	unsigned int reg = mc->reg;
-	int min = mc->min;
-	unsigned int val;
-
-	val = (ucontrol->value.integer.value[0]+min) & 0xff;
-	val |= ((ucontrol->value.integer.value[1]+min) & 0xff) << 8;
-
-	return snd_soc_component_update_bits(component, reg, 0xffff, val);
-}
-EXPORT_SYMBOL_GPL(snd_soc_put_volsw_s8);
-
-/**
- * snd_soc_info_volsw_range - single mixer info callback with range.
- * @kcontrol: mixer control
- * @uinfo: control element information
- *
- * Callback to provide information, within a range, about a single
- * mixer control.
- *
- * returns 0 for success.
- */
-int snd_soc_info_volsw_range(struct snd_kcontrol *kcontrol,
-	struct snd_ctl_elem_info *uinfo)
-{
-	struct soc_mixer_control *mc =
-		(struct soc_mixer_control *)kcontrol->private_value;
-	int platform_max;
-	int min = mc->min;
-
-	if (!mc->platform_max)
-		mc->platform_max = mc->max;
-	platform_max = mc->platform_max;
-
-	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
-	uinfo->count = snd_soc_volsw_is_stereo(mc) ? 2 : 1;
-	uinfo->value.integer.min = 0;
-	uinfo->value.integer.max = platform_max - min;
-
-	return 0;
-}
-EXPORT_SYMBOL_GPL(snd_soc_info_volsw_range);
-
-/**
- * snd_soc_put_volsw_range - single mixer put value callback with range.
- * @kcontrol: mixer control
- * @ucontrol: control element information
- *
- * Callback to set the value, within a range, for a single mixer control.
- *
- * Returns 0 for success.
- */
-int snd_soc_put_volsw_range(struct snd_kcontrol *kcontrol,
-	struct snd_ctl_elem_value *ucontrol)
-{
-	struct soc_mixer_control *mc =
-		(struct soc_mixer_control *)kcontrol->private_value;
-	struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
-	unsigned int reg = mc->reg;
-	unsigned int rreg = mc->rreg;
-	unsigned int shift = mc->shift;
-	int min = mc->min;
-	int max = mc->max;
-	unsigned int mask = (1 << fls(max)) - 1;
-	unsigned int invert = mc->invert;
-	unsigned int val, val_mask;
-	int ret;
-
-	if (invert)
-		val = (max - ucontrol->value.integer.value[0]) & mask;
-	else
-		val = ((ucontrol->value.integer.value[0] + min) & mask);
-	val_mask = mask << shift;
-	val = val << shift;
-
-	ret = snd_soc_component_update_bits(component, reg, val_mask, val);
-	if (ret < 0)
-		return ret;
-
-	if (snd_soc_volsw_is_stereo(mc)) {
-		if (invert)
-			val = (max - ucontrol->value.integer.value[1]) & mask;
-		else
-			val = ((ucontrol->value.integer.value[1] + min) & mask);
-		val_mask = mask << shift;
-		val = val << shift;
-
-		ret = snd_soc_component_update_bits(component, rreg, val_mask,
-			val);
-	}
-
-	return ret;
-}
-EXPORT_SYMBOL_GPL(snd_soc_put_volsw_range);
-
-/**
- * snd_soc_get_volsw_range - single mixer get callback with range
- * @kcontrol: mixer control
- * @ucontrol: control element information
- *
- * Callback to get the value, within a range, of a single mixer control.
- *
- * Returns 0 for success.
- */
-int snd_soc_get_volsw_range(struct snd_kcontrol *kcontrol,
-	struct snd_ctl_elem_value *ucontrol)
-{
-	struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
-	struct soc_mixer_control *mc =
-		(struct soc_mixer_control *)kcontrol->private_value;
-	unsigned int reg = mc->reg;
-	unsigned int rreg = mc->rreg;
-	unsigned int shift = mc->shift;
-	int min = mc->min;
-	int max = mc->max;
-	unsigned int mask = (1 << fls(max)) - 1;
-	unsigned int invert = mc->invert;
-	unsigned int val;
-	int ret;
-
-	ret = snd_soc_component_read(component, reg, &val);
-	if (ret)
-		return ret;
-
-	ucontrol->value.integer.value[0] = (val >> shift) & mask;
-	if (invert)
-		ucontrol->value.integer.value[0] =
-			max - ucontrol->value.integer.value[0];
-	else
-		ucontrol->value.integer.value[0] =
-			ucontrol->value.integer.value[0] - min;
-
-	if (snd_soc_volsw_is_stereo(mc)) {
-		ret = snd_soc_component_read(component, rreg, &val);
-		if (ret)
-			return ret;
-
-		ucontrol->value.integer.value[1] = (val >> shift) & mask;
-		if (invert)
-			ucontrol->value.integer.value[1] =
-				max - ucontrol->value.integer.value[1];
-		else
-			ucontrol->value.integer.value[1] =
-				ucontrol->value.integer.value[1] - min;
-	}
-
-	return 0;
-}
-EXPORT_SYMBOL_GPL(snd_soc_get_volsw_range);
-
-/**
- * snd_soc_limit_volume - Set new limit to an existing volume control.
- *
- * @codec: where to look for the control
- * @name: Name of the control
- * @max: new maximum limit
- *
- * Return 0 for success, else error.
- */
-int snd_soc_limit_volume(struct snd_soc_codec *codec,
-	const char *name, int max)
-{
-	struct snd_card *card = codec->component.card->snd_card;
-	struct snd_kcontrol *kctl;
-	struct soc_mixer_control *mc;
-	int found = 0;
-	int ret = -EINVAL;
-
-	/* Sanity check for name and max */
-	if (unlikely(!name || max <= 0))
-		return -EINVAL;
-
-	list_for_each_entry(kctl, &card->controls, list) {
-		if (!strncmp(kctl->id.name, name, sizeof(kctl->id.name))) {
-			found = 1;
-			break;
-		}
-	}
-	if (found) {
-		mc = (struct soc_mixer_control *)kctl->private_value;
-		if (max <= mc->max) {
-			mc->platform_max = max;
-			ret = 0;
-		}
-	}
-	return ret;
-}
-EXPORT_SYMBOL_GPL(snd_soc_limit_volume);
-
-int snd_soc_bytes_info(struct snd_kcontrol *kcontrol,
-		       struct snd_ctl_elem_info *uinfo)
-{
-	struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
-	struct soc_bytes *params = (void *)kcontrol->private_value;
-
-	uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES;
-	uinfo->count = params->num_regs * component->val_bytes;
-
-	return 0;
-}
-EXPORT_SYMBOL_GPL(snd_soc_bytes_info);
-
-int snd_soc_bytes_get(struct snd_kcontrol *kcontrol,
-		      struct snd_ctl_elem_value *ucontrol)
-{
-	struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
-	struct soc_bytes *params = (void *)kcontrol->private_value;
-	int ret;
-
-	if (component->regmap)
-		ret = regmap_raw_read(component->regmap, params->base,
-				      ucontrol->value.bytes.data,
-				      params->num_regs * component->val_bytes);
-	else
-		ret = -EINVAL;
-
-	/* Hide any masked bytes to ensure consistent data reporting */
-	if (ret == 0 && params->mask) {
-		switch (component->val_bytes) {
-		case 1:
-			ucontrol->value.bytes.data[0] &= ~params->mask;
-			break;
-		case 2:
-			((u16 *)(&ucontrol->value.bytes.data))[0]
-				&= cpu_to_be16(~params->mask);
-			break;
-		case 4:
-			((u32 *)(&ucontrol->value.bytes.data))[0]
-				&= cpu_to_be32(~params->mask);
-			break;
-		default:
-			return -EINVAL;
-		}
-	}
-
-	return ret;
-}
-EXPORT_SYMBOL_GPL(snd_soc_bytes_get);
-
-int snd_soc_bytes_put(struct snd_kcontrol *kcontrol,
-		      struct snd_ctl_elem_value *ucontrol)
-{
-	struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
-	struct soc_bytes *params = (void *)kcontrol->private_value;
-	int ret, len;
-	unsigned int val, mask;
-	void *data;
-
-	if (!component->regmap || !params->num_regs)
-		return -EINVAL;
-
-	len = params->num_regs * component->val_bytes;
-
-	data = kmemdup(ucontrol->value.bytes.data, len, GFP_KERNEL | GFP_DMA);
-	if (!data)
-		return -ENOMEM;
-
-	/*
-	 * If we've got a mask then we need to preserve the register
-	 * bits.  We shouldn't modify the incoming data so take a
-	 * copy.
-	 */
-	if (params->mask) {
-		ret = regmap_read(component->regmap, params->base, &val);
-		if (ret != 0)
-			goto out;
-
-		val &= params->mask;
-
-		switch (component->val_bytes) {
-		case 1:
-			((u8 *)data)[0] &= ~params->mask;
-			((u8 *)data)[0] |= val;
-			break;
-		case 2:
-			mask = ~params->mask;
-			ret = regmap_parse_val(component->regmap,
-							&mask, &mask);
-			if (ret != 0)
-				goto out;
-
-			((u16 *)data)[0] &= mask;
-
-			ret = regmap_parse_val(component->regmap,
-							&val, &val);
-			if (ret != 0)
-				goto out;
-
-			((u16 *)data)[0] |= val;
-			break;
-		case 4:
-			mask = ~params->mask;
-			ret = regmap_parse_val(component->regmap,
-							&mask, &mask);
-			if (ret != 0)
-				goto out;
-
-			((u32 *)data)[0] &= mask;
-
-			ret = regmap_parse_val(component->regmap,
-							&val, &val);
-			if (ret != 0)
-				goto out;
-
-			((u32 *)data)[0] |= val;
-			break;
-		default:
-			ret = -EINVAL;
-			goto out;
-		}
-	}
-
-	ret = regmap_raw_write(component->regmap, params->base,
-			       data, len);
-
-out:
-	kfree(data);
-
-	return ret;
-}
-EXPORT_SYMBOL_GPL(snd_soc_bytes_put);
-
-int snd_soc_bytes_info_ext(struct snd_kcontrol *kcontrol,
-			struct snd_ctl_elem_info *ucontrol)
-{
-	struct soc_bytes_ext *params = (void *)kcontrol->private_value;
-
-	ucontrol->type = SNDRV_CTL_ELEM_TYPE_BYTES;
-	ucontrol->count = params->max;
-
-	return 0;
-}
-EXPORT_SYMBOL_GPL(snd_soc_bytes_info_ext);
-
-int snd_soc_bytes_tlv_callback(struct snd_kcontrol *kcontrol, int op_flag,
-				unsigned int size, unsigned int __user *tlv)
-{
-	struct soc_bytes_ext *params = (void *)kcontrol->private_value;
-	unsigned int count = size < params->max ? size : params->max;
-	int ret = -ENXIO;
-
-	switch (op_flag) {
-	case SNDRV_CTL_TLV_OP_READ:
-		if (params->get)
-			ret = params->get(tlv, count);
-		break;
-	case SNDRV_CTL_TLV_OP_WRITE:
-		if (params->put)
-			ret = params->put(tlv, count);
-		break;
-	}
-	return ret;
-}
-EXPORT_SYMBOL_GPL(snd_soc_bytes_tlv_callback);
-
-/**
- * snd_soc_info_xr_sx - signed multi register info callback
- * @kcontrol: mreg control
- * @uinfo: control element information
- *
- * Callback to provide information of a control that can
- * span multiple codec registers which together
- * forms a single signed value in a MSB/LSB manner.
- *
- * Returns 0 for success.
- */
-int snd_soc_info_xr_sx(struct snd_kcontrol *kcontrol,
-	struct snd_ctl_elem_info *uinfo)
-{
-	struct soc_mreg_control *mc =
-		(struct soc_mreg_control *)kcontrol->private_value;
-	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
-	uinfo->count = 1;
-	uinfo->value.integer.min = mc->min;
-	uinfo->value.integer.max = mc->max;
-
-	return 0;
-}
-EXPORT_SYMBOL_GPL(snd_soc_info_xr_sx);
-
-/**
- * snd_soc_get_xr_sx - signed multi register get callback
- * @kcontrol: mreg control
- * @ucontrol: control element information
- *
- * Callback to get the value of a control that can span
- * multiple codec registers which together forms a single
- * signed value in a MSB/LSB manner. The control supports
- * specifying total no of bits used to allow for bitfields
- * across the multiple codec registers.
- *
- * Returns 0 for success.
- */
-int snd_soc_get_xr_sx(struct snd_kcontrol *kcontrol,
-	struct snd_ctl_elem_value *ucontrol)
-{
-	struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
-	struct soc_mreg_control *mc =
-		(struct soc_mreg_control *)kcontrol->private_value;
-	unsigned int regbase = mc->regbase;
-	unsigned int regcount = mc->regcount;
-	unsigned int regwshift = component->val_bytes * BITS_PER_BYTE;
-	unsigned int regwmask = (1<<regwshift)-1;
-	unsigned int invert = mc->invert;
-	unsigned long mask = (1UL<<mc->nbits)-1;
-	long min = mc->min;
-	long max = mc->max;
-	long val = 0;
-	unsigned int regval;
-	unsigned int i;
-	int ret;
-
-	for (i = 0; i < regcount; i++) {
-		ret = snd_soc_component_read(component, regbase+i, &regval);
-		if (ret)
-			return ret;
-		val |= (regval & regwmask) << (regwshift*(regcount-i-1));
-	}
-	val &= mask;
-	if (min < 0 && val > max)
-		val |= ~mask;
-	if (invert)
-		val = max - val;
-	ucontrol->value.integer.value[0] = val;
-
-	return 0;
-}
-EXPORT_SYMBOL_GPL(snd_soc_get_xr_sx);
-
-/**
- * snd_soc_put_xr_sx - signed multi register get callback
- * @kcontrol: mreg control
- * @ucontrol: control element information
- *
- * Callback to set the value of a control that can span
- * multiple codec registers which together forms a single
- * signed value in a MSB/LSB manner. The control supports
- * specifying total no of bits used to allow for bitfields
- * across the multiple codec registers.
- *
- * Returns 0 for success.
- */
-int snd_soc_put_xr_sx(struct snd_kcontrol *kcontrol,
-	struct snd_ctl_elem_value *ucontrol)
-{
-	struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
-	struct soc_mreg_control *mc =
-		(struct soc_mreg_control *)kcontrol->private_value;
-	unsigned int regbase = mc->regbase;
-	unsigned int regcount = mc->regcount;
-	unsigned int regwshift = component->val_bytes * BITS_PER_BYTE;
-	unsigned int regwmask = (1<<regwshift)-1;
-	unsigned int invert = mc->invert;
-	unsigned long mask = (1UL<<mc->nbits)-1;
-	long max = mc->max;
-	long val = ucontrol->value.integer.value[0];
-	unsigned int i, regval, regmask;
-	int err;
-
-	if (invert)
-		val = max - val;
-	val &= mask;
-	for (i = 0; i < regcount; i++) {
-		regval = (val >> (regwshift*(regcount-i-1))) & regwmask;
-		regmask = (mask >> (regwshift*(regcount-i-1))) & regwmask;
-		err = snd_soc_component_update_bits(component, regbase+i,
-				regmask, regval);
-		if (err < 0)
-			return err;
-	}
-
-	return 0;
-}
-EXPORT_SYMBOL_GPL(snd_soc_put_xr_sx);
-
-/**
- * snd_soc_get_strobe - strobe get callback
- * @kcontrol: mixer control
- * @ucontrol: control element information
- *
- * Callback get the value of a strobe mixer control.
- *
- * Returns 0 for success.
- */
-int snd_soc_get_strobe(struct snd_kcontrol *kcontrol,
-	struct snd_ctl_elem_value *ucontrol)
-{
-	struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
-	struct soc_mixer_control *mc =
-		(struct soc_mixer_control *)kcontrol->private_value;
-	unsigned int reg = mc->reg;
-	unsigned int shift = mc->shift;
-	unsigned int mask = 1 << shift;
-	unsigned int invert = mc->invert != 0;
-	unsigned int val;
-	int ret;
-
-	ret = snd_soc_component_read(component, reg, &val);
-	if (ret)
-		return ret;
-
-	val &= mask;
-
-	if (shift != 0 && val != 0)
-		val = val >> shift;
-	ucontrol->value.enumerated.item[0] = val ^ invert;
-
-	return 0;
-}
-EXPORT_SYMBOL_GPL(snd_soc_get_strobe);
-
-/**
- * snd_soc_put_strobe - strobe put callback
- * @kcontrol: mixer control
- * @ucontrol: control element information
- *
- * Callback strobe a register bit to high then low (or the inverse)
- * in one pass of a single mixer enum control.
- *
- * Returns 1 for success.
- */
-int snd_soc_put_strobe(struct snd_kcontrol *kcontrol,
-	struct snd_ctl_elem_value *ucontrol)
-{
-	struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
-	struct soc_mixer_control *mc =
-		(struct soc_mixer_control *)kcontrol->private_value;
-	unsigned int reg = mc->reg;
-	unsigned int shift = mc->shift;
-	unsigned int mask = 1 << shift;
-	unsigned int invert = mc->invert != 0;
-	unsigned int strobe = ucontrol->value.enumerated.item[0] != 0;
-	unsigned int val1 = (strobe ^ invert) ? mask : 0;
-	unsigned int val2 = (strobe ^ invert) ? 0 : mask;
-	int err;
-
-	err = snd_soc_component_update_bits(component, reg, mask, val1);
-	if (err < 0)
-		return err;
-
-	return snd_soc_component_update_bits(component, reg, mask, val2);
-}
-EXPORT_SYMBOL_GPL(snd_soc_put_strobe);
-
-/**
  * snd_soc_dai_set_sysclk - configure DAI system or master clock.
  * @dai: DAI
  * @clk_id: DAI specific clock ID
@@ -3996,22 +2603,62 @@ static int snd_soc_component_initialize(struct snd_soc_component *component,
 	return 0;
 }
 
-static void snd_soc_component_init_regmap(struct snd_soc_component *component)
+static void snd_soc_component_setup_regmap(struct snd_soc_component *component)
 {
-	if (!component->regmap)
-		component->regmap = dev_get_regmap(component->dev, NULL);
-	if (component->regmap) {
-		int val_bytes = regmap_get_val_bytes(component->regmap);
-		/* Errors are legitimate for non-integer byte multiples */
-		if (val_bytes > 0)
-			component->val_bytes = val_bytes;
-	}
+	int val_bytes = regmap_get_val_bytes(component->regmap);
+
+	/* Errors are legitimate for non-integer byte multiples */
+	if (val_bytes > 0)
+		component->val_bytes = val_bytes;
+}
+
+#ifdef CONFIG_REGMAP
+
+/**
+ * snd_soc_component_init_regmap() - Initialize regmap instance for the component
+ * @component: The component for which to initialize the regmap instance
+ * @regmap: The regmap instance that should be used by the component
+ *
+ * This function allows deferred assignment of the regmap instance that is
+ * associated with the component. Only use this if the regmap instance is not
+ * yet ready when the component is registered. The function must also be called
+ * before the first IO attempt of the component.
+ */
+void snd_soc_component_init_regmap(struct snd_soc_component *component,
+	struct regmap *regmap)
+{
+	component->regmap = regmap;
+	snd_soc_component_setup_regmap(component);
+}
+EXPORT_SYMBOL_GPL(snd_soc_component_init_regmap);
+
+/**
+ * snd_soc_component_exit_regmap() - De-initialize regmap instance for the component
+ * @component: The component for which to de-initialize the regmap instance
+ *
+ * Calls regmap_exit() on the regmap instance associated to the component and
+ * removes the regmap instance from the component.
+ *
+ * This function should only be used if snd_soc_component_init_regmap() was used
+ * to initialize the regmap instance.
+ */
+void snd_soc_component_exit_regmap(struct snd_soc_component *component)
+{
+	regmap_exit(component->regmap);
+	component->regmap = NULL;
 }
+EXPORT_SYMBOL_GPL(snd_soc_component_exit_regmap);
+
+#endif
 
 static void snd_soc_component_add_unlocked(struct snd_soc_component *component)
 {
-	if (!component->write && !component->read)
-		snd_soc_component_init_regmap(component);
+	if (!component->write && !component->read) {
+		if (!component->regmap)
+			component->regmap = dev_get_regmap(component->dev, NULL);
+		if (component->regmap)
+			snd_soc_component_setup_regmap(component);
+	}
 
 	list_add(&component->list, &component_list);
 }
@@ -4362,7 +3009,6 @@ int snd_soc_register_codec(struct device *dev,
 	codec->dev = dev;
 	codec->driver = codec_drv;
 	codec->component.val_bytes = codec_drv->reg_word_size;
-	mutex_init(&codec->mutex);
 
 #ifdef CONFIG_DEBUG_FS
 	codec->component.init_debugfs = soc_init_codec_debugfs;
@@ -4585,7 +3231,7 @@ int snd_soc_of_parse_audio_routing(struct snd_soc_card *card,
 				   const char *propname)
 {
 	struct device_node *np = card->dev->of_node;
-	int num_routes;
+	int num_routes, old_routes;
 	struct snd_soc_dapm_route *routes;
 	int i, ret;
 
@@ -4603,7 +3249,9 @@ int snd_soc_of_parse_audio_routing(struct snd_soc_card *card,
 		return -EINVAL;
 	}
 
-	routes = devm_kzalloc(card->dev, num_routes * sizeof(*routes),
+	old_routes = card->num_dapm_routes;
+	routes = devm_kzalloc(card->dev,
+			      (old_routes + num_routes) * sizeof(*routes),
 			      GFP_KERNEL);
 	if (!routes) {
 		dev_err(card->dev,
@@ -4611,9 +3259,11 @@ int snd_soc_of_parse_audio_routing(struct snd_soc_card *card,
 		return -EINVAL;
 	}
 
+	memcpy(routes, card->dapm_routes, old_routes * sizeof(*routes));
+
 	for (i = 0; i < num_routes; i++) {
 		ret = of_property_read_string_index(np, propname,
-			2 * i, &routes[i].sink);
+			2 * i, &routes[old_routes + i].sink);
 		if (ret) {
 			dev_err(card->dev,
 				"ASoC: Property '%s' index %d could not be read: %d\n",
@@ -4621,7 +3271,7 @@ int snd_soc_of_parse_audio_routing(struct snd_soc_card *card,
 			return -EINVAL;
 		}
 		ret = of_property_read_string_index(np, propname,
-			(2 * i) + 1, &routes[i].source);
+			(2 * i) + 1, &routes[old_routes + i].source);
 		if (ret) {
 			dev_err(card->dev,
 				"ASoC: Property '%s' index %d could not be read: %d\n",
@@ -4630,7 +3280,7 @@ int snd_soc_of_parse_audio_routing(struct snd_soc_card *card,
 		}
 	}
 
-	card->num_dapm_routes = num_routes;
+	card->num_dapm_routes += num_routes;
 	card->dapm_routes = routes;
 
 	return 0;
@@ -4750,36 +3400,30 @@ unsigned int snd_soc_of_parse_daifmt(struct device_node *np,
 }
 EXPORT_SYMBOL_GPL(snd_soc_of_parse_daifmt);
 
-int snd_soc_of_get_dai_name(struct device_node *of_node,
-			    const char **dai_name)
+static int snd_soc_get_dai_name(struct of_phandle_args *args,
+				const char **dai_name)
 {
 	struct snd_soc_component *pos;
-	struct of_phandle_args args;
-	int ret;
-
-	ret = of_parse_phandle_with_args(of_node, "sound-dai",
-					 "#sound-dai-cells", 0, &args);
-	if (ret)
-		return ret;
-
-	ret = -EPROBE_DEFER;
+	int ret = -EPROBE_DEFER;
 
 	mutex_lock(&client_mutex);
 	list_for_each_entry(pos, &component_list, list) {
-		if (pos->dev->of_node != args.np)
+		if (pos->dev->of_node != args->np)
 			continue;
 
 		if (pos->driver->of_xlate_dai_name) {
-			ret = pos->driver->of_xlate_dai_name(pos, &args, dai_name);
+			ret = pos->driver->of_xlate_dai_name(pos,
+							     args,
+							     dai_name);
 		} else {
 			int id = -1;
 
-			switch (args.args_count) {
+			switch (args->args_count) {
 			case 0:
 				id = 0; /* same as dai_drv[0] */
 				break;
 			case 1:
-				id = args.args[0];
+				id = args->args[0];
 				break;
 			default:
 				/* not supported */
@@ -4801,6 +3445,21 @@ int snd_soc_of_get_dai_name(struct device_node *of_node,
 		break;
 	}
 	mutex_unlock(&client_mutex);
+	return ret;
+}
+
+int snd_soc_of_get_dai_name(struct device_node *of_node,
+			    const char **dai_name)
+{
+	struct of_phandle_args args;
+	int ret;
+
+	ret = of_parse_phandle_with_args(of_node, "sound-dai",
+					 "#sound-dai-cells", 0, &args);
+	if (ret)
+		return ret;
+
+	ret = snd_soc_get_dai_name(&args, dai_name);
 
 	of_node_put(args.np);
 
@@ -4808,6 +3467,77 @@ int snd_soc_of_get_dai_name(struct device_node *of_node,
 }
 EXPORT_SYMBOL_GPL(snd_soc_of_get_dai_name);
 
+/*
+ * snd_soc_of_get_dai_link_codecs - Parse a list of CODECs in the devicetree
+ * @dev: Card device
+ * @of_node: Device node
+ * @dai_link: DAI link
+ *
+ * Builds an array of CODEC DAI components from the DAI link property
+ * 'sound-dai'.
+ * The array is set in the DAI link and the number of DAIs is set accordingly.
+ * The device nodes in the array (of_node) must be dereferenced by the caller.
+ *
+ * Returns 0 for success
+ */
+int snd_soc_of_get_dai_link_codecs(struct device *dev,
+				   struct device_node *of_node,
+				   struct snd_soc_dai_link *dai_link)
+{
+	struct of_phandle_args args;
+	struct snd_soc_dai_link_component *component;
+	char *name;
+	int index, num_codecs, ret;
+
+	/* Count the number of CODECs */
+	name = "sound-dai";
+	num_codecs = of_count_phandle_with_args(of_node, name,
+						"#sound-dai-cells");
+	if (num_codecs <= 0) {
+		if (num_codecs == -ENOENT)
+			dev_err(dev, "No 'sound-dai' property\n");
+		else
+			dev_err(dev, "Bad phandle in 'sound-dai'\n");
+		return num_codecs;
+	}
+	component = devm_kzalloc(dev,
+				 sizeof *component * num_codecs,
+				 GFP_KERNEL);
+	if (!component)
+		return -ENOMEM;
+	dai_link->codecs = component;
+	dai_link->num_codecs = num_codecs;
+
+	/* Parse the list */
+	for (index = 0, component = dai_link->codecs;
+	     index < dai_link->num_codecs;
+	     index++, component++) {
+		ret = of_parse_phandle_with_args(of_node, name,
+						 "#sound-dai-cells",
+						  index, &args);
+		if (ret)
+			goto err;
+		component->of_node = args.np;
+		ret = snd_soc_get_dai_name(&args, &component->dai_name);
+		if (ret < 0)
+			goto err;
+	}
+	return 0;
+err:
+	for (index = 0, component = dai_link->codecs;
+	     index < dai_link->num_codecs;
+	     index++, component++) {
+		if (!component->of_node)
+			break;
+		of_node_put(component->of_node);
+		component->of_node = NULL;
+	}
+	dai_link->codecs = NULL;
+	dai_link->num_codecs = 0;
+	return ret;
+}
+EXPORT_SYMBOL_GPL(snd_soc_of_get_dai_link_codecs);
+
 static int __init snd_soc_init(void)
 {
 #ifdef CONFIG_DEBUG_FS
diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c
index c61cb9cedbcd..c5136bb1f982 100644
--- a/sound/soc/soc-dapm.c
+++ b/sound/soc/soc-dapm.c
@@ -159,27 +159,135 @@ static void dapm_mark_dirty(struct snd_soc_dapm_widget *w, const char *reason)
 	}
 }
 
-void dapm_mark_io_dirty(struct snd_soc_dapm_context *dapm)
+/*
+ * dapm_widget_invalidate_input_paths() - Invalidate the cached number of input
+ *  paths
+ * @w: The widget for which to invalidate the cached number of input paths
+ *
+ * The function resets the cached number of inputs for the specified widget and
+ * all widgets that can be reached via outgoing paths from the widget.
+ *
+ * This function must be called if the number of input paths for a widget might
+ * have changed. E.g. if the source state of a widget changes or a path is added
+ * or activated with the widget as the sink.
+ */
+static void dapm_widget_invalidate_input_paths(struct snd_soc_dapm_widget *w)
+{
+	struct snd_soc_dapm_widget *sink;
+	struct snd_soc_dapm_path *p;
+	LIST_HEAD(list);
+
+	dapm_assert_locked(w->dapm);
+
+	if (w->inputs == -1)
+		return;
+
+	w->inputs = -1;
+	list_add_tail(&w->work_list, &list);
+
+	list_for_each_entry(w, &list, work_list) {
+		list_for_each_entry(p, &w->sinks, list_source) {
+			if (p->is_supply || p->weak || !p->connect)
+				continue;
+			sink = p->sink;
+			if (sink->inputs != -1) {
+				sink->inputs = -1;
+				list_add_tail(&sink->work_list, &list);
+			}
+		}
+	}
+}
+
+/*
+ * dapm_widget_invalidate_output_paths() - Invalidate the cached number of
+ *  output paths
+ * @w: The widget for which to invalidate the cached number of output paths
+ *
+ * Resets the cached number of outputs for the specified widget and all widgets
+ * that can be reached via incoming paths from the widget.
+ *
+ * This function must be called if the number of output paths for a widget might
+ * have changed. E.g. if the sink state of a widget changes or a path is added
+ * or activated with the widget as the source.
+ */
+static void dapm_widget_invalidate_output_paths(struct snd_soc_dapm_widget *w)
+{
+	struct snd_soc_dapm_widget *source;
+	struct snd_soc_dapm_path *p;
+	LIST_HEAD(list);
+
+	dapm_assert_locked(w->dapm);
+
+	if (w->outputs == -1)
+		return;
+
+	w->outputs = -1;
+	list_add_tail(&w->work_list, &list);
+
+	list_for_each_entry(w, &list, work_list) {
+		list_for_each_entry(p, &w->sources, list_sink) {
+			if (p->is_supply || p->weak || !p->connect)
+				continue;
+			source = p->source;
+			if (source->outputs != -1) {
+				source->outputs = -1;
+				list_add_tail(&source->work_list, &list);
+			}
+		}
+	}
+}
+
+/*
+ * dapm_path_invalidate() - Invalidates the cached number of inputs and outputs
+ *  for the widgets connected to a path
+ * @p: The path to invalidate
+ *
+ * Resets the cached number of inputs for the sink of the path and the cached
+ * number of outputs for the source of the path.
+ *
+ * This function must be called when a path is added, removed or the connected
+ * state changes.
+ */
+static void dapm_path_invalidate(struct snd_soc_dapm_path *p)
+{
+	/*
+	 * Weak paths or supply paths do not influence the number of input or
+	 * output paths of their neighbors.
+	 */
+	if (p->weak || p->is_supply)
+		return;
+
+	/*
+	 * The number of connected endpoints is the sum of the number of
+	 * connected endpoints of all neighbors. If a node with 0 connected
+	 * endpoints is either connected or disconnected that sum won't change,
+	 * so there is no need to re-check the path.
+	 */
+	if (p->source->inputs != 0)
+		dapm_widget_invalidate_input_paths(p->sink);
+	if (p->sink->outputs != 0)
+		dapm_widget_invalidate_output_paths(p->source);
+}
+
+void dapm_mark_endpoints_dirty(struct snd_soc_card *card)
 {
-	struct snd_soc_card *card = dapm->card;
 	struct snd_soc_dapm_widget *w;
 
 	mutex_lock(&card->dapm_mutex);
 
 	list_for_each_entry(w, &card->widgets, list) {
-		switch (w->id) {
-		case snd_soc_dapm_input:
-		case snd_soc_dapm_output:
-			dapm_mark_dirty(w, "Rechecking inputs and outputs");
-			break;
-		default:
-			break;
+		if (w->is_sink || w->is_source) {
+			dapm_mark_dirty(w, "Rechecking endpoints");
+			if (w->is_sink)
+				dapm_widget_invalidate_output_paths(w);
+			if (w->is_source)
+				dapm_widget_invalidate_input_paths(w);
 		}
 	}
 
 	mutex_unlock(&card->dapm_mutex);
 }
-EXPORT_SYMBOL_GPL(dapm_mark_io_dirty);
+EXPORT_SYMBOL_GPL(dapm_mark_endpoints_dirty);
 
 /* create a new dapm widget */
 static inline struct snd_soc_dapm_widget *dapm_cnew_widget(
@@ -386,8 +494,6 @@ static void dapm_reset(struct snd_soc_card *card)
 	list_for_each_entry(w, &card->widgets, list) {
 		w->new_power = w->power;
 		w->power_checked = false;
-		w->inputs = -1;
-		w->outputs = -1;
 	}
 }
 
@@ -469,10 +575,9 @@ out:
 
 /* connect mux widget to its interconnecting audio paths */
 static int dapm_connect_mux(struct snd_soc_dapm_context *dapm,
-	struct snd_soc_dapm_widget *src, struct snd_soc_dapm_widget *dest,
-	struct snd_soc_dapm_path *path, const char *control_name,
-	const struct snd_kcontrol_new *kcontrol)
+	struct snd_soc_dapm_path *path, const char *control_name)
 {
+	const struct snd_kcontrol_new *kcontrol = &path->sink->kcontrol_news[0];
 	struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
 	unsigned int val, item;
 	int i;
@@ -493,10 +598,7 @@ static int dapm_connect_mux(struct snd_soc_dapm_context *dapm,
 
 	for (i = 0; i < e->items; i++) {
 		if (!(strcmp(control_name, e->texts[i]))) {
-			list_add(&path->list, &dapm->card->paths);
-			list_add(&path->list_sink, &dest->sources);
-			list_add(&path->list_source, &src->sinks);
-			path->name = (char*)e->texts[i];
+			path->name = e->texts[i];
 			if (i == item)
 				path->connect = 1;
 			else
@@ -509,11 +611,10 @@ static int dapm_connect_mux(struct snd_soc_dapm_context *dapm,
 }
 
 /* set up initial codec paths */
-static void dapm_set_mixer_path_status(struct snd_soc_dapm_widget *w,
-	struct snd_soc_dapm_path *p, int i)
+static void dapm_set_mixer_path_status(struct snd_soc_dapm_path *p, int i)
 {
 	struct soc_mixer_control *mc = (struct soc_mixer_control *)
-		w->kcontrol_news[i].private_value;
+		p->sink->kcontrol_news[i].private_value;
 	unsigned int reg = mc->reg;
 	unsigned int shift = mc->shift;
 	unsigned int max = mc->max;
@@ -522,7 +623,7 @@ static void dapm_set_mixer_path_status(struct snd_soc_dapm_widget *w,
 	unsigned int val;
 
 	if (reg != SND_SOC_NOPM) {
-		soc_dapm_read(w->dapm, reg, &val);
+		soc_dapm_read(p->sink->dapm, reg, &val);
 		val = (val >> shift) & mask;
 		if (invert)
 			val = max - val;
@@ -534,19 +635,15 @@ static void dapm_set_mixer_path_status(struct snd_soc_dapm_widget *w,
 
 /* connect mixer widget to its interconnecting audio paths */
 static int dapm_connect_mixer(struct snd_soc_dapm_context *dapm,
-	struct snd_soc_dapm_widget *src, struct snd_soc_dapm_widget *dest,
 	struct snd_soc_dapm_path *path, const char *control_name)
 {
 	int i;
 
 	/* search for mixer kcontrol */
-	for (i = 0; i < dest->num_kcontrols; i++) {
-		if (!strcmp(control_name, dest->kcontrol_news[i].name)) {
-			list_add(&path->list, &dapm->card->paths);
-			list_add(&path->list_sink, &dest->sources);
-			list_add(&path->list_source, &src->sinks);
-			path->name = dest->kcontrol_news[i].name;
-			dapm_set_mixer_path_status(dest, path, i);
+	for (i = 0; i < path->sink->num_kcontrols; i++) {
+		if (!strcmp(control_name, path->sink->kcontrol_news[i].name)) {
+			path->name = path->sink->kcontrol_news[i].name;
+			dapm_set_mixer_path_status(path, i);
 			return 0;
 		}
 	}
@@ -738,8 +835,10 @@ 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)
-		dapm_kcontrol_add_path(w->kcontrols[0], path);
+	list_for_each_entry(path, &w->sources, list_sink) {
+		if (path->name)
+			dapm_kcontrol_add_path(w->kcontrols[0], path);
+	}
 
 	return 0;
 }
@@ -754,34 +853,6 @@ static int dapm_new_pga(struct snd_soc_dapm_widget *w)
 	return 0;
 }
 
-/* reset 'walked' bit for each dapm path */
-static void dapm_clear_walk_output(struct snd_soc_dapm_context *dapm,
-				   struct list_head *sink)
-{
-	struct snd_soc_dapm_path *p;
-
-	list_for_each_entry(p, sink, list_source) {
-		if (p->walked) {
-			p->walked = 0;
-			dapm_clear_walk_output(dapm, &p->sink->sinks);
-		}
-	}
-}
-
-static void dapm_clear_walk_input(struct snd_soc_dapm_context *dapm,
-				  struct list_head *source)
-{
-	struct snd_soc_dapm_path *p;
-
-	list_for_each_entry(p, source, list_sink) {
-		if (p->walked) {
-			p->walked = 0;
-			dapm_clear_walk_input(dapm, &p->source->sources);
-		}
-	}
-}
-
-
 /* We implement power down on suspend by checking the power state of
  * the ALSA card - when we are suspending the ALSA state for the card
  * is set to D3.
@@ -856,61 +927,23 @@ static int is_connected_output_ep(struct snd_soc_dapm_widget *widget,
 
 	DAPM_UPDATE_STAT(widget, path_checks);
 
-	switch (widget->id) {
-	case snd_soc_dapm_supply:
-	case snd_soc_dapm_regulator_supply:
-	case snd_soc_dapm_clock_supply:
-	case snd_soc_dapm_kcontrol:
-		return 0;
-	default:
-		break;
-	}
-
-	switch (widget->id) {
-	case snd_soc_dapm_adc:
-	case snd_soc_dapm_aif_out:
-	case snd_soc_dapm_dai_out:
-		if (widget->active) {
-			widget->outputs = snd_soc_dapm_suspend_check(widget);
-			return widget->outputs;
-		}
-	default:
-		break;
-	}
-
-	if (widget->connected) {
-		/* connected pin ? */
-		if (widget->id == snd_soc_dapm_output && !widget->ext) {
-			widget->outputs = snd_soc_dapm_suspend_check(widget);
-			return widget->outputs;
-		}
-
-		/* connected jack or spk ? */
-		if (widget->id == snd_soc_dapm_hp ||
-		    widget->id == snd_soc_dapm_spk ||
-		    (widget->id == snd_soc_dapm_line &&
-		     !list_empty(&widget->sources))) {
-			widget->outputs = snd_soc_dapm_suspend_check(widget);
-			return widget->outputs;
-		}
+	if (widget->is_sink && widget->connected) {
+		widget->outputs = snd_soc_dapm_suspend_check(widget);
+		return widget->outputs;
 	}
 
 	list_for_each_entry(path, &widget->sinks, list_source) {
 		DAPM_UPDATE_STAT(widget, neighbour_checks);
 
-		if (path->weak)
+		if (path->weak || path->is_supply)
 			continue;
 
 		if (path->walking)
 			return 1;
 
-		if (path->walked)
-			continue;
-
 		trace_snd_soc_dapm_output_path(widget, path);
 
-		if (path->sink && path->connect) {
-			path->walked = 1;
+		if (path->connect) {
 			path->walking = 1;
 
 			/* do we need to add this widget to the list ? */
@@ -952,73 +985,23 @@ static int is_connected_input_ep(struct snd_soc_dapm_widget *widget,
 
 	DAPM_UPDATE_STAT(widget, path_checks);
 
-	switch (widget->id) {
-	case snd_soc_dapm_supply:
-	case snd_soc_dapm_regulator_supply:
-	case snd_soc_dapm_clock_supply:
-	case snd_soc_dapm_kcontrol:
-		return 0;
-	default:
-		break;
-	}
-
-	/* active stream ? */
-	switch (widget->id) {
-	case snd_soc_dapm_dac:
-	case snd_soc_dapm_aif_in:
-	case snd_soc_dapm_dai_in:
-		if (widget->active) {
-			widget->inputs = snd_soc_dapm_suspend_check(widget);
-			return widget->inputs;
-		}
-	default:
-		break;
-	}
-
-	if (widget->connected) {
-		/* connected pin ? */
-		if (widget->id == snd_soc_dapm_input && !widget->ext) {
-			widget->inputs = snd_soc_dapm_suspend_check(widget);
-			return widget->inputs;
-		}
-
-		/* connected VMID/Bias for lower pops */
-		if (widget->id == snd_soc_dapm_vmid) {
-			widget->inputs = snd_soc_dapm_suspend_check(widget);
-			return widget->inputs;
-		}
-
-		/* connected jack ? */
-		if (widget->id == snd_soc_dapm_mic ||
-		    (widget->id == snd_soc_dapm_line &&
-		     !list_empty(&widget->sinks))) {
-			widget->inputs = snd_soc_dapm_suspend_check(widget);
-			return widget->inputs;
-		}
-
-		/* signal generator */
-		if (widget->id == snd_soc_dapm_siggen) {
-			widget->inputs = snd_soc_dapm_suspend_check(widget);
-			return widget->inputs;
-		}
+	if (widget->is_source && widget->connected) {
+		widget->inputs = snd_soc_dapm_suspend_check(widget);
+		return widget->inputs;
 	}
 
 	list_for_each_entry(path, &widget->sources, list_sink) {
 		DAPM_UPDATE_STAT(widget, neighbour_checks);
 
-		if (path->weak)
+		if (path->weak || path->is_supply)
 			continue;
 
 		if (path->walking)
 			return 1;
 
-		if (path->walked)
-			continue;
-
 		trace_snd_soc_dapm_input_path(widget, path);
 
-		if (path->source && path->connect) {
-			path->walked = 1;
+		if (path->connect) {
 			path->walking = 1;
 
 			/* do we need to add this widget to the list ? */
@@ -1060,21 +1043,25 @@ static int is_connected_input_ep(struct snd_soc_dapm_widget *widget,
 int snd_soc_dapm_dai_get_connected_widgets(struct snd_soc_dai *dai, int stream,
 	struct snd_soc_dapm_widget_list **list)
 {
-	struct snd_soc_card *card = dai->card;
+	struct snd_soc_card *card = dai->component->card;
+	struct snd_soc_dapm_widget *w;
 	int paths;
 
 	mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME);
-	dapm_reset(card);
 
-	if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
+	/*
+	 * For is_connected_{output,input}_ep fully discover the graph we need
+	 * to reset the cached number of inputs and outputs.
+	 */
+	list_for_each_entry(w, &card->widgets, list) {
+		w->inputs = -1;
+		w->outputs = -1;
+	}
+
+	if (stream == SNDRV_PCM_STREAM_PLAYBACK)
 		paths = is_connected_output_ep(dai->playback_widget, list);
-		dapm_clear_walk_output(&card->dapm,
-				       &dai->playback_widget->sinks);
-	} else {
+	else
 		paths = is_connected_input_ep(dai->capture_widget, list);
-		dapm_clear_walk_input(&card->dapm,
-				      &dai->capture_widget->sources);
-	}
 
 	trace_snd_soc_dapm_connected(paths, stream);
 	mutex_unlock(&card->dapm_mutex);
@@ -1163,44 +1150,10 @@ static int dapm_generic_check_power(struct snd_soc_dapm_widget *w)
 	DAPM_UPDATE_STAT(w, power_checks);
 
 	in = is_connected_input_ep(w, NULL);
-	dapm_clear_walk_input(w->dapm, &w->sources);
 	out = is_connected_output_ep(w, NULL);
-	dapm_clear_walk_output(w->dapm, &w->sinks);
 	return out != 0 && in != 0;
 }
 
-/* Check to see if an ADC has power */
-static int dapm_adc_check_power(struct snd_soc_dapm_widget *w)
-{
-	int in;
-
-	DAPM_UPDATE_STAT(w, power_checks);
-
-	if (w->active) {
-		in = is_connected_input_ep(w, NULL);
-		dapm_clear_walk_input(w->dapm, &w->sources);
-		return in != 0;
-	} else {
-		return dapm_generic_check_power(w);
-	}
-}
-
-/* Check to see if a DAC has power */
-static int dapm_dac_check_power(struct snd_soc_dapm_widget *w)
-{
-	int out;
-
-	DAPM_UPDATE_STAT(w, power_checks);
-
-	if (w->active) {
-		out = is_connected_output_ep(w, NULL);
-		dapm_clear_walk_output(w->dapm, &w->sinks);
-		return out != 0;
-	} else {
-		return dapm_generic_check_power(w);
-	}
-}
-
 /* Check to see if a power supply is needed */
 static int dapm_supply_check_power(struct snd_soc_dapm_widget *w)
 {
@@ -1219,9 +1172,6 @@ static int dapm_supply_check_power(struct snd_soc_dapm_widget *w)
 		    !path->connected(path->source, path->sink))
 			continue;
 
-		if (!path->sink)
-			continue;
-
 		if (dapm_widget_power_check(path->sink))
 			return 1;
 	}
@@ -1636,27 +1586,14 @@ static void dapm_widget_set_power(struct snd_soc_dapm_widget *w, bool power,
 	/* If we changed our power state perhaps our neigbours changed
 	 * also.
 	 */
-	list_for_each_entry(path, &w->sources, list_sink) {
-		if (path->source) {
-			dapm_widget_set_peer_power(path->source, power,
+	list_for_each_entry(path, &w->sources, list_sink)
+		dapm_widget_set_peer_power(path->source, power, path->connect);
+
+	/* Supplies can't affect their outputs, only their inputs */
+	if (!w->is_supply) {
+		list_for_each_entry(path, &w->sinks, list_source)
+			dapm_widget_set_peer_power(path->sink, power,
 						   path->connect);
-		}
-	}
-	switch (w->id) {
-	case snd_soc_dapm_supply:
-	case snd_soc_dapm_regulator_supply:
-	case snd_soc_dapm_clock_supply:
-	case snd_soc_dapm_kcontrol:
-		/* Supplies can't affect their outputs, only their inputs */
-		break;
-	default:
-		list_for_each_entry(path, &w->sinks, list_source) {
-			if (path->sink) {
-				dapm_widget_set_peer_power(path->sink, power,
-							   path->connect);
-			}
-		}
-		break;
 	}
 
 	if (power)
@@ -1863,10 +1800,14 @@ static ssize_t dapm_widget_power_read_file(struct file *file,
 	if (!buf)
 		return -ENOMEM;
 
-	in = is_connected_input_ep(w, NULL);
-	dapm_clear_walk_input(w->dapm, &w->sources);
-	out = is_connected_output_ep(w, NULL);
-	dapm_clear_walk_output(w->dapm, &w->sinks);
+	/* Supply widgets are not handled by is_connected_{input,output}_ep() */
+	if (w->is_supply) {
+		in = 0;
+		out = 0;
+	} else {
+		in = is_connected_input_ep(w, NULL);
+		out = is_connected_output_ep(w, NULL);
+	}
 
 	ret = snprintf(buf, PAGE_SIZE, "%s: %s%s  in %d out %d",
 		       w->name, w->power ? "On" : "Off",
@@ -2011,32 +1952,45 @@ static inline void dapm_debugfs_cleanup(struct snd_soc_dapm_context *dapm)
 
 #endif
 
+/*
+ * soc_dapm_connect_path() - Connects or disconnects a path
+ * @path: The path to update
+ * @connect: The new connect state of the path. True if the path is connected,
+ *  false if it is disconneted.
+ * @reason: The reason why the path changed (for debugging only)
+ */
+static void soc_dapm_connect_path(struct snd_soc_dapm_path *path,
+	bool connect, const char *reason)
+{
+	if (path->connect == connect)
+		return;
+
+	path->connect = connect;
+	dapm_mark_dirty(path->source, reason);
+	dapm_mark_dirty(path->sink, reason);
+	dapm_path_invalidate(path);
+}
+
 /* test and update the power status of a mux widget */
 static int soc_dapm_mux_update_power(struct snd_soc_card *card,
 				 struct snd_kcontrol *kcontrol, int mux, struct soc_enum *e)
 {
 	struct snd_soc_dapm_path *path;
 	int found = 0;
+	bool connect;
 
 	lockdep_assert_held(&card->dapm_mutex);
 
 	/* find dapm widget path assoc with kcontrol */
 	dapm_kcontrol_for_each_path(path, kcontrol) {
-		if (!path->name || !e->texts[mux])
-			continue;
-
 		found = 1;
 		/* we now need to match the string in the enum to the path */
-		if (!(strcmp(path->name, e->texts[mux]))) {
-			path->connect = 1; /* new connection */
-			dapm_mark_dirty(path->source, "mux connection");
-		} else {
-			if (path->connect)
-				dapm_mark_dirty(path->source,
-						"mux disconnection");
-			path->connect = 0; /* old connection must be powered down */
-		}
-		dapm_mark_dirty(path->sink, "mux change");
+		if (!(strcmp(path->name, e->texts[mux])))
+			connect = true;
+		else
+			connect = false;
+
+		soc_dapm_connect_path(path, connect, "mux update");
 	}
 
 	if (found)
@@ -2075,9 +2029,7 @@ static int soc_dapm_mixer_update_power(struct snd_soc_card *card,
 	/* find dapm widget path assoc with kcontrol */
 	dapm_kcontrol_for_each_path(path, kcontrol) {
 		found = 1;
-		path->connect = connect;
-		dapm_mark_dirty(path->source, "mixer connection");
-		dapm_mark_dirty(path->sink, "mixer update");
+		soc_dapm_connect_path(path, connect, "mixer update");
 	}
 
 	if (found)
@@ -2255,8 +2207,11 @@ static int snd_soc_dapm_set_pin(struct snd_soc_dapm_context *dapm,
 		return -EINVAL;
 	}
 
-	if (w->connected != status)
+	if (w->connected != status) {
 		dapm_mark_dirty(w, "pin configuration");
+		dapm_widget_invalidate_input_paths(w);
+		dapm_widget_invalidate_output_paths(w);
+	}
 
 	w->connected = status;
 	if (status == 0)
@@ -2309,6 +2264,53 @@ int snd_soc_dapm_sync(struct snd_soc_dapm_context *dapm)
 }
 EXPORT_SYMBOL_GPL(snd_soc_dapm_sync);
 
+/*
+ * dapm_update_widget_flags() - Re-compute widget sink and source flags
+ * @w: The widget for which to update the flags
+ *
+ * Some widgets have a dynamic category which depends on which neighbors they
+ * are connected to. This function update the category for these widgets.
+ *
+ * This function must be called whenever a path is added or removed to a widget.
+ */
+static void dapm_update_widget_flags(struct snd_soc_dapm_widget *w)
+{
+	struct snd_soc_dapm_path *p;
+
+	switch (w->id) {
+	case snd_soc_dapm_input:
+		w->is_source = 1;
+		list_for_each_entry(p, &w->sources, list_sink) {
+			if (p->source->id == snd_soc_dapm_micbias ||
+				p->source->id == snd_soc_dapm_mic ||
+				p->source->id == snd_soc_dapm_line ||
+				p->source->id == snd_soc_dapm_output) {
+					w->is_source = 0;
+					break;
+			}
+		}
+		break;
+	case snd_soc_dapm_output:
+		w->is_sink = 1;
+		list_for_each_entry(p, &w->sinks, list_source) {
+			if (p->sink->id == snd_soc_dapm_spk ||
+				p->sink->id == snd_soc_dapm_hp ||
+				p->sink->id == snd_soc_dapm_line ||
+				p->sink->id == snd_soc_dapm_input) {
+					w->is_sink = 0;
+					break;
+			}
+		}
+		break;
+	case snd_soc_dapm_line:
+		w->is_sink = !list_empty(&w->sources);
+		w->is_source = !list_empty(&w->sinks);
+		break;
+	default:
+		break;
+	}
+}
+
 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,
@@ -2318,6 +2320,27 @@ static int snd_soc_dapm_add_path(struct snd_soc_dapm_context *dapm,
 	struct snd_soc_dapm_path *path;
 	int ret;
 
+	if (wsink->is_supply && !wsource->is_supply) {
+		dev_err(dapm->dev,
+			"Connecting non-supply widget to supply widget is not supported (%s -> %s)\n",
+			wsource->name, wsink->name);
+		return -EINVAL;
+	}
+
+	if (connected && !wsource->is_supply) {
+		dev_err(dapm->dev,
+			"connected() callback only supported for supply widgets (%s -> %s)\n",
+			wsource->name, wsink->name);
+		return -EINVAL;
+	}
+
+	if (wsource->is_supply && control) {
+		dev_err(dapm->dev,
+			"Conditional paths are not supported for supply widgets (%s -> [%s] -> %s)\n",
+			wsource->name, control, wsink->name);
+		return -EINVAL;
+	}
+
 	path = kzalloc(sizeof(struct snd_soc_dapm_path), GFP_KERNEL);
 	if (!path)
 		return -ENOMEM;
@@ -2330,85 +2353,49 @@ static int snd_soc_dapm_add_path(struct snd_soc_dapm_context *dapm,
 	INIT_LIST_HEAD(&path->list_source);
 	INIT_LIST_HEAD(&path->list_sink);
 
-	/* check for external widgets */
-	if (wsink->id == snd_soc_dapm_input) {
-		if (wsource->id == snd_soc_dapm_micbias ||
-			wsource->id == snd_soc_dapm_mic ||
-			wsource->id == snd_soc_dapm_line ||
-			wsource->id == snd_soc_dapm_output)
-			wsink->ext = 1;
-	}
-	if (wsource->id == snd_soc_dapm_output) {
-		if (wsink->id == snd_soc_dapm_spk ||
-			wsink->id == snd_soc_dapm_hp ||
-			wsink->id == snd_soc_dapm_line ||
-			wsink->id == snd_soc_dapm_input)
-			wsource->ext = 1;
-	}
-
-	dapm_mark_dirty(wsource, "Route added");
-	dapm_mark_dirty(wsink, "Route added");
+	if (wsource->is_supply || wsink->is_supply)
+		path->is_supply = 1;
 
 	/* connect static paths */
 	if (control == NULL) {
-		list_add(&path->list, &dapm->card->paths);
-		list_add(&path->list_sink, &wsink->sources);
-		list_add(&path->list_source, &wsource->sinks);
 		path->connect = 1;
-		return 0;
-	}
-
-	/* connect dynamic paths */
-	switch (wsink->id) {
-	case snd_soc_dapm_adc:
-	case snd_soc_dapm_dac:
-	case snd_soc_dapm_pga:
-	case snd_soc_dapm_out_drv:
-	case snd_soc_dapm_input:
-	case snd_soc_dapm_output:
-	case snd_soc_dapm_siggen:
-	case snd_soc_dapm_micbias:
-	case snd_soc_dapm_vmid:
-	case snd_soc_dapm_pre:
-	case snd_soc_dapm_post:
-	case snd_soc_dapm_supply:
-	case snd_soc_dapm_regulator_supply:
-	case snd_soc_dapm_clock_supply:
-	case snd_soc_dapm_aif_in:
-	case snd_soc_dapm_aif_out:
-	case snd_soc_dapm_dai_in:
-	case snd_soc_dapm_dai_out:
-	case snd_soc_dapm_dai_link:
-	case snd_soc_dapm_kcontrol:
-		list_add(&path->list, &dapm->card->paths);
-		list_add(&path->list_sink, &wsink->sources);
-		list_add(&path->list_source, &wsource->sinks);
-		path->connect = 1;
-		return 0;
-	case snd_soc_dapm_mux:
-		ret = dapm_connect_mux(dapm, wsource, wsink, path, control,
-			&wsink->kcontrol_news[0]);
-		if (ret != 0)
-			goto err;
-		break;
-	case snd_soc_dapm_switch:
-	case snd_soc_dapm_mixer:
-	case snd_soc_dapm_mixer_named_ctl:
-		ret = dapm_connect_mixer(dapm, wsource, wsink, path, control);
-		if (ret != 0)
+	} else {
+		/* connect dynamic paths */
+		switch (wsink->id) {
+		case snd_soc_dapm_mux:
+			ret = dapm_connect_mux(dapm, path, control);
+			if (ret != 0)
+				goto err;
+			break;
+		case snd_soc_dapm_switch:
+		case snd_soc_dapm_mixer:
+		case snd_soc_dapm_mixer_named_ctl:
+			ret = dapm_connect_mixer(dapm, path, control);
+			if (ret != 0)
+				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;
-	case snd_soc_dapm_hp:
-	case snd_soc_dapm_mic:
-	case snd_soc_dapm_line:
-	case snd_soc_dapm_spk:
-		list_add(&path->list, &dapm->card->paths);
-		list_add(&path->list_sink, &wsink->sources);
-		list_add(&path->list_source, &wsource->sinks);
-		path->connect = 0;
-		return 0;
+		}
 	}
 
+	list_add(&path->list, &dapm->card->paths);
+	list_add(&path->list_sink, &wsink->sources);
+	list_add(&path->list_source, &wsource->sinks);
+
+	dapm_update_widget_flags(wsource);
+	dapm_update_widget_flags(wsink);
+
+	dapm_mark_dirty(wsource, "Route added");
+	dapm_mark_dirty(wsink, "Route added");
+
+	if (dapm->card->instantiated && path->connect)
+		dapm_path_invalidate(path);
+
 	return 0;
 err:
 	kfree(path);
@@ -2489,6 +2476,7 @@ err:
 static int snd_soc_dapm_del_route(struct snd_soc_dapm_context *dapm,
 				  const struct snd_soc_dapm_route *route)
 {
+	struct snd_soc_dapm_widget *wsource, *wsink;
 	struct snd_soc_dapm_path *path, *p;
 	const char *sink;
 	const char *source;
@@ -2526,10 +2514,19 @@ static int snd_soc_dapm_del_route(struct snd_soc_dapm_context *dapm,
 	}
 
 	if (path) {
-		dapm_mark_dirty(path->source, "Route removed");
-		dapm_mark_dirty(path->sink, "Route removed");
+		wsource = path->source;
+		wsink = path->sink;
+
+		dapm_mark_dirty(wsource, "Route removed");
+		dapm_mark_dirty(wsink, "Route removed");
+		if (path->connect)
+			dapm_path_invalidate(path);
 
 		dapm_free_path(path);
+
+		/* Update any path related flags */
+		dapm_update_widget_flags(wsource);
+		dapm_update_widget_flags(wsink);
 	} else {
 		dev_warn(dapm->dev, "ASoC: Route %s->%s does not exist\n",
 			 source, sink);
@@ -3087,40 +3084,44 @@ snd_soc_dapm_new_control(struct snd_soc_dapm_context *dapm,
 	}
 
 	switch (w->id) {
-	case snd_soc_dapm_switch:
-	case snd_soc_dapm_mixer:
-	case snd_soc_dapm_mixer_named_ctl:
+	case snd_soc_dapm_mic:
+	case snd_soc_dapm_input:
+		w->is_source = 1;
 		w->power_check = dapm_generic_check_power;
 		break;
-	case snd_soc_dapm_mux:
+	case snd_soc_dapm_spk:
+	case snd_soc_dapm_hp:
+	case snd_soc_dapm_output:
+		w->is_sink = 1;
 		w->power_check = dapm_generic_check_power;
 		break;
-	case snd_soc_dapm_dai_out:
-		w->power_check = dapm_adc_check_power;
-		break;
-	case snd_soc_dapm_dai_in:
-		w->power_check = dapm_dac_check_power;
+	case snd_soc_dapm_vmid:
+	case snd_soc_dapm_siggen:
+		w->is_source = 1;
+		w->power_check = dapm_always_on_check_power;
 		break;
+	case snd_soc_dapm_mux:
+	case snd_soc_dapm_switch:
+	case snd_soc_dapm_mixer:
+	case snd_soc_dapm_mixer_named_ctl:
 	case snd_soc_dapm_adc:
 	case snd_soc_dapm_aif_out:
 	case snd_soc_dapm_dac:
 	case snd_soc_dapm_aif_in:
 	case snd_soc_dapm_pga:
 	case snd_soc_dapm_out_drv:
-	case snd_soc_dapm_input:
-	case snd_soc_dapm_output:
 	case snd_soc_dapm_micbias:
-	case snd_soc_dapm_spk:
-	case snd_soc_dapm_hp:
-	case snd_soc_dapm_mic:
 	case snd_soc_dapm_line:
 	case snd_soc_dapm_dai_link:
+	case snd_soc_dapm_dai_out:
+	case snd_soc_dapm_dai_in:
 		w->power_check = dapm_generic_check_power;
 		break;
 	case snd_soc_dapm_supply:
 	case snd_soc_dapm_regulator_supply:
 	case snd_soc_dapm_clock_supply:
 	case snd_soc_dapm_kcontrol:
+		w->is_supply = 1;
 		w->power_check = dapm_supply_check_power;
 		break;
 	default:
@@ -3137,6 +3138,9 @@ snd_soc_dapm_new_control(struct snd_soc_dapm_context *dapm,
 	INIT_LIST_HEAD(&w->dirty);
 	list_add(&w->list, &dapm->card->widgets);
 
+	w->inputs = -1;
+	w->outputs = -1;
+
 	/* machine layer set ups unconnected pins and insertions */
 	w->connected = 1;
 	return w;
@@ -3484,6 +3488,14 @@ static void soc_dapm_dai_stream_event(struct snd_soc_dai *dai, int stream,
 		case SND_SOC_DAPM_STREAM_PAUSE_RELEASE:
 			break;
 		}
+
+		if (w->id == snd_soc_dapm_dai_in) {
+			w->is_source = w->active;
+			dapm_widget_invalidate_input_paths(w);
+		} else {
+			w->is_sink = w->active;
+			dapm_widget_invalidate_output_paths(w);
+		}
 	}
 }
 
@@ -3610,7 +3622,15 @@ int snd_soc_dapm_force_enable_pin_unlocked(struct snd_soc_dapm_context *dapm,
 	}
 
 	dev_dbg(w->dapm->dev, "ASoC: force enable pin %s\n", pin);
-	w->connected = 1;
+	if (!w->connected) {
+		/*
+		 * w->force does not affect the number of input or output paths,
+		 * so we only have to recheck if w->connected is changed
+		 */
+		dapm_widget_invalidate_input_paths(w);
+		dapm_widget_invalidate_output_paths(w);
+		w->connected = 1;
+	}
 	w->force = 1;
 	dapm_mark_dirty(w, "force enable");
 
@@ -3788,35 +3808,54 @@ int snd_soc_dapm_ignore_suspend(struct snd_soc_dapm_context *dapm,
 }
 EXPORT_SYMBOL_GPL(snd_soc_dapm_ignore_suspend);
 
+/**
+ * dapm_is_external_path() - Checks if a path is a external path
+ * @card: The card the path belongs to
+ * @path: The path to check
+ *
+ * Returns true if the path is either between two different DAPM contexts or
+ * between two external pins of the same DAPM context. Otherwise returns
+ * false.
+ */
+static bool dapm_is_external_path(struct snd_soc_card *card,
+	struct snd_soc_dapm_path *path)
+{
+	dev_dbg(card->dev,
+		"... Path %s(id:%d dapm:%p) - %s(id:%d dapm:%p)\n",
+		path->source->name, path->source->id, path->source->dapm,
+		path->sink->name, path->sink->id, path->sink->dapm);
+
+	/* Connection between two different DAPM contexts */
+	if (path->source->dapm != path->sink->dapm)
+		return true;
+
+	/* Loopback connection from external pin to external pin */
+	if (path->sink->id == snd_soc_dapm_input) {
+		switch (path->source->id) {
+		case snd_soc_dapm_output:
+		case snd_soc_dapm_micbias:
+			return true;
+		default:
+			break;
+		}
+	}
+
+	return false;
+}
+
 static bool snd_soc_dapm_widget_in_card_paths(struct snd_soc_card *card,
 					      struct snd_soc_dapm_widget *w)
 {
 	struct snd_soc_dapm_path *p;
 
-	list_for_each_entry(p, &card->paths, list) {
-		if ((p->source == w) || (p->sink == w)) {
-			dev_dbg(card->dev,
-			    "... Path %s(id:%d dapm:%p) - %s(id:%d dapm:%p)\n",
-			    p->source->name, p->source->id, p->source->dapm,
-			    p->sink->name, p->sink->id, p->sink->dapm);
+	list_for_each_entry(p, &w->sources, list_sink) {
+		if (dapm_is_external_path(card, p))
+			return true;
+	}
 
-			/* Connected to something other than the codec */
-			if (p->source->dapm != p->sink->dapm)
-				return true;
-			/*
-			 * Loopback connection from codec external pin to
-			 * codec external pin
-			 */
-			if (p->sink->id == snd_soc_dapm_input) {
-				switch (p->source->id) {
-				case snd_soc_dapm_output:
-				case snd_soc_dapm_micbias:
-					return true;
-				default:
-					break;
-				}
-			}
-		}
+	list_for_each_entry(p, &w->sinks, list_source) {
+		if (dapm_is_external_path(card, p))
+			return true;
 	}
 
 	return false;
diff --git a/sound/soc/soc-jack.c b/sound/soc/soc-jack.c
index ab47fea997a3..4380dcc064a5 100644
--- a/sound/soc/soc-jack.c
+++ b/sound/soc/soc-jack.c
@@ -116,7 +116,7 @@ EXPORT_SYMBOL_GPL(snd_soc_jack_report);
  *
  * @jack:  ASoC jack
  * @count: Number of zones
- * @zone:  Array of zones
+ * @zones:  Array of zones
  *
  * After this function has been called the zones specified in the
  * array will be associated with the jack.
@@ -309,7 +309,7 @@ int snd_soc_jack_add_gpios(struct snd_soc_jack *jack, int count,
 			/* GPIO descriptor */
 			gpios[i].desc = gpiod_get_index(gpios[i].gpiod_dev,
 							gpios[i].name,
-							gpios[i].idx);
+							gpios[i].idx, GPIOD_IN);
 			if (IS_ERR(gpios[i].desc)) {
 				ret = PTR_ERR(gpios[i].desc);
 				dev_err(gpios[i].gpiod_dev,
@@ -327,17 +327,14 @@ int snd_soc_jack_add_gpios(struct snd_soc_jack *jack, int count,
 				goto undo;
 			}
 
-			ret = gpio_request(gpios[i].gpio, gpios[i].name);
+			ret = gpio_request_one(gpios[i].gpio, GPIOF_IN,
+					       gpios[i].name);
 			if (ret)
 				goto undo;
 
 			gpios[i].desc = gpio_to_desc(gpios[i].gpio);
 		}
 
-		ret = gpiod_direction_input(gpios[i].desc);
-		if (ret)
-			goto err;
-
 		INIT_DELAYED_WORK(&gpios[i].work, gpio_work);
 		gpios[i].jack = jack;
 
diff --git a/sound/soc/soc-ops.c b/sound/soc/soc-ops.c
new file mode 100644
index 000000000000..100d92b5b77e
--- /dev/null
+++ b/sound/soc/soc-ops.c
@@ -0,0 +1,952 @@
+/*
+ * soc-ops.c  --  Generic ASoC operations
+ *
+ * Copyright 2005 Wolfson Microelectronics PLC.
+ * Copyright 2005 Openedhand Ltd.
+ * Copyright (C) 2010 Slimlogic Ltd.
+ * Copyright (C) 2010 Texas Instruments Inc.
+ *
+ * Author: Liam Girdwood <lrg@slimlogic.co.uk>
+ *         with code, comments and ideas from :-
+ *         Richard Purdie <richard@openedhand.com>
+ *
+ *  This program is free software; you can redistribute  it and/or modify it
+ *  under  the terms of  the GNU General  Public License as published by the
+ *  Free Software Foundation;  either version 2 of the  License, or (at your
+ *  option) any later version.
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/bitops.h>
+#include <linux/ctype.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+#include <sound/jack.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dpcm.h>
+#include <sound/initval.h>
+
+/**
+ * snd_soc_info_enum_double - enumerated double mixer info callback
+ * @kcontrol: mixer control
+ * @uinfo: control element information
+ *
+ * Callback to provide information about a double enumerated
+ * mixer control.
+ *
+ * Returns 0 for success.
+ */
+int snd_soc_info_enum_double(struct snd_kcontrol *kcontrol,
+	struct snd_ctl_elem_info *uinfo)
+{
+	struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+
+	return snd_ctl_enum_info(uinfo, e->shift_l == e->shift_r ? 1 : 2,
+				 e->items, e->texts);
+}
+EXPORT_SYMBOL_GPL(snd_soc_info_enum_double);
+
+/**
+ * snd_soc_get_enum_double - enumerated double mixer get callback
+ * @kcontrol: mixer control
+ * @ucontrol: control element information
+ *
+ * Callback to get the value of a double enumerated mixer.
+ *
+ * Returns 0 for success.
+ */
+int snd_soc_get_enum_double(struct snd_kcontrol *kcontrol,
+	struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+	struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+	unsigned int val, item;
+	unsigned int reg_val;
+	int ret;
+
+	ret = snd_soc_component_read(component, e->reg, &reg_val);
+	if (ret)
+		return ret;
+	val = (reg_val >> e->shift_l) & e->mask;
+	item = snd_soc_enum_val_to_item(e, val);
+	ucontrol->value.enumerated.item[0] = item;
+	if (e->shift_l != e->shift_r) {
+		val = (reg_val >> e->shift_l) & e->mask;
+		item = snd_soc_enum_val_to_item(e, val);
+		ucontrol->value.enumerated.item[1] = item;
+	}
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(snd_soc_get_enum_double);
+
+/**
+ * snd_soc_put_enum_double - enumerated double mixer put callback
+ * @kcontrol: mixer control
+ * @ucontrol: control element information
+ *
+ * Callback to set the value of a double enumerated mixer.
+ *
+ * Returns 0 for success.
+ */
+int snd_soc_put_enum_double(struct snd_kcontrol *kcontrol,
+	struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+	struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+	unsigned int *item = ucontrol->value.enumerated.item;
+	unsigned int val;
+	unsigned int mask;
+
+	if (item[0] >= e->items)
+		return -EINVAL;
+	val = snd_soc_enum_item_to_val(e, item[0]) << e->shift_l;
+	mask = e->mask << e->shift_l;
+	if (e->shift_l != e->shift_r) {
+		if (item[1] >= e->items)
+			return -EINVAL;
+		val |= snd_soc_enum_item_to_val(e, item[1]) << e->shift_r;
+		mask |= e->mask << e->shift_r;
+	}
+
+	return snd_soc_component_update_bits(component, e->reg, mask, val);
+}
+EXPORT_SYMBOL_GPL(snd_soc_put_enum_double);
+
+/**
+ * snd_soc_read_signed - Read a codec register and interprete as signed value
+ * @component: component
+ * @reg: Register to read
+ * @mask: Mask to use after shifting the register value
+ * @shift: Right shift of register value
+ * @sign_bit: Bit that describes if a number is negative or not.
+ * @signed_val: Pointer to where the read value should be stored
+ *
+ * This functions reads a codec register. The register value is shifted right
+ * by 'shift' bits and masked with the given 'mask'. Afterwards it translates
+ * the given registervalue into a signed integer if sign_bit is non-zero.
+ *
+ * Returns 0 on sucess, otherwise an error value
+ */
+static int snd_soc_read_signed(struct snd_soc_component *component,
+	unsigned int reg, unsigned int mask, unsigned int shift,
+	unsigned int sign_bit, int *signed_val)
+{
+	int ret;
+	unsigned int val;
+
+	ret = snd_soc_component_read(component, reg, &val);
+	if (ret < 0)
+		return ret;
+
+	val = (val >> shift) & mask;
+
+	if (!sign_bit) {
+		*signed_val = val;
+		return 0;
+	}
+
+	/* non-negative number */
+	if (!(val & BIT(sign_bit))) {
+		*signed_val = val;
+		return 0;
+	}
+
+	ret = val;
+
+	/*
+	 * The register most probably does not contain a full-sized int.
+	 * Instead we have an arbitrary number of bits in a signed
+	 * representation which has to be translated into a full-sized int.
+	 * This is done by filling up all bits above the sign-bit.
+	 */
+	ret |= ~((int)(BIT(sign_bit) - 1));
+
+	*signed_val = ret;
+
+	return 0;
+}
+
+/**
+ * snd_soc_info_volsw - single mixer info callback
+ * @kcontrol: mixer control
+ * @uinfo: control element information
+ *
+ * Callback to provide information about a single mixer control, or a double
+ * mixer control that spans 2 registers.
+ *
+ * Returns 0 for success.
+ */
+int snd_soc_info_volsw(struct snd_kcontrol *kcontrol,
+	struct snd_ctl_elem_info *uinfo)
+{
+	struct soc_mixer_control *mc =
+		(struct soc_mixer_control *)kcontrol->private_value;
+	int platform_max;
+
+	if (!mc->platform_max)
+		mc->platform_max = mc->max;
+	platform_max = mc->platform_max;
+
+	if (platform_max == 1 && !strstr(kcontrol->id.name, " Volume"))
+		uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+	else
+		uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+
+	uinfo->count = snd_soc_volsw_is_stereo(mc) ? 2 : 1;
+	uinfo->value.integer.min = 0;
+	uinfo->value.integer.max = platform_max - mc->min;
+	return 0;
+}
+EXPORT_SYMBOL_GPL(snd_soc_info_volsw);
+
+/**
+ * snd_soc_get_volsw - single mixer get callback
+ * @kcontrol: mixer control
+ * @ucontrol: control element information
+ *
+ * Callback to get the value of a single mixer control, or a double mixer
+ * control that spans 2 registers.
+ *
+ * Returns 0 for success.
+ */
+int snd_soc_get_volsw(struct snd_kcontrol *kcontrol,
+	struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+	struct soc_mixer_control *mc =
+		(struct soc_mixer_control *)kcontrol->private_value;
+	unsigned int reg = mc->reg;
+	unsigned int reg2 = mc->rreg;
+	unsigned int shift = mc->shift;
+	unsigned int rshift = mc->rshift;
+	int max = mc->max;
+	int min = mc->min;
+	int sign_bit = mc->sign_bit;
+	unsigned int mask = (1 << fls(max)) - 1;
+	unsigned int invert = mc->invert;
+	int val;
+	int ret;
+
+	if (sign_bit)
+		mask = BIT(sign_bit + 1) - 1;
+
+	ret = snd_soc_read_signed(component, reg, mask, shift, sign_bit, &val);
+	if (ret)
+		return ret;
+
+	ucontrol->value.integer.value[0] = val - min;
+	if (invert)
+		ucontrol->value.integer.value[0] =
+			max - ucontrol->value.integer.value[0];
+
+	if (snd_soc_volsw_is_stereo(mc)) {
+		if (reg == reg2)
+			ret = snd_soc_read_signed(component, reg, mask, rshift,
+				sign_bit, &val);
+		else
+			ret = snd_soc_read_signed(component, reg2, mask, shift,
+				sign_bit, &val);
+		if (ret)
+			return ret;
+
+		ucontrol->value.integer.value[1] = val - min;
+		if (invert)
+			ucontrol->value.integer.value[1] =
+				max - ucontrol->value.integer.value[1];
+	}
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(snd_soc_get_volsw);
+
+/**
+ * snd_soc_put_volsw - single mixer put callback
+ * @kcontrol: mixer control
+ * @ucontrol: control element information
+ *
+ * Callback to set the value of a single mixer control, or a double mixer
+ * control that spans 2 registers.
+ *
+ * Returns 0 for success.
+ */
+int snd_soc_put_volsw(struct snd_kcontrol *kcontrol,
+	struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+	struct soc_mixer_control *mc =
+		(struct soc_mixer_control *)kcontrol->private_value;
+	unsigned int reg = mc->reg;
+	unsigned int reg2 = mc->rreg;
+	unsigned int shift = mc->shift;
+	unsigned int rshift = mc->rshift;
+	int max = mc->max;
+	int min = mc->min;
+	unsigned int sign_bit = mc->sign_bit;
+	unsigned int mask = (1 << fls(max)) - 1;
+	unsigned int invert = mc->invert;
+	int err;
+	bool type_2r = false;
+	unsigned int val2 = 0;
+	unsigned int val, val_mask;
+
+	if (sign_bit)
+		mask = BIT(sign_bit + 1) - 1;
+
+	val = ((ucontrol->value.integer.value[0] + min) & mask);
+	if (invert)
+		val = max - val;
+	val_mask = mask << shift;
+	val = val << shift;
+	if (snd_soc_volsw_is_stereo(mc)) {
+		val2 = ((ucontrol->value.integer.value[1] + min) & mask);
+		if (invert)
+			val2 = max - val2;
+		if (reg == reg2) {
+			val_mask |= mask << rshift;
+			val |= val2 << rshift;
+		} else {
+			val2 = val2 << shift;
+			type_2r = true;
+		}
+	}
+	err = snd_soc_component_update_bits(component, reg, val_mask, val);
+	if (err < 0)
+		return err;
+
+	if (type_2r)
+		err = snd_soc_component_update_bits(component, reg2, val_mask,
+			val2);
+
+	return err;
+}
+EXPORT_SYMBOL_GPL(snd_soc_put_volsw);
+
+/**
+ * snd_soc_get_volsw_sx - single mixer get callback
+ * @kcontrol: mixer control
+ * @ucontrol: control element information
+ *
+ * Callback to get the value of a single mixer control, or a double mixer
+ * control that spans 2 registers.
+ *
+ * Returns 0 for success.
+ */
+int snd_soc_get_volsw_sx(struct snd_kcontrol *kcontrol,
+		      struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+	struct soc_mixer_control *mc =
+	    (struct soc_mixer_control *)kcontrol->private_value;
+	unsigned int reg = mc->reg;
+	unsigned int reg2 = mc->rreg;
+	unsigned int shift = mc->shift;
+	unsigned int rshift = mc->rshift;
+	int max = mc->max;
+	int min = mc->min;
+	int mask = (1 << (fls(min + max) - 1)) - 1;
+	unsigned int val;
+	int ret;
+
+	ret = snd_soc_component_read(component, reg, &val);
+	if (ret < 0)
+		return ret;
+
+	ucontrol->value.integer.value[0] = ((val >> shift) - min) & mask;
+
+	if (snd_soc_volsw_is_stereo(mc)) {
+		ret = snd_soc_component_read(component, reg2, &val);
+		if (ret < 0)
+			return ret;
+
+		val = ((val >> rshift) - min) & mask;
+		ucontrol->value.integer.value[1] = val;
+	}
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(snd_soc_get_volsw_sx);
+
+/**
+ * snd_soc_put_volsw_sx - double mixer set callback
+ * @kcontrol: mixer control
+ * @uinfo: control element information
+ *
+ * Callback to set the value of a double mixer control that spans 2 registers.
+ *
+ * Returns 0 for success.
+ */
+int snd_soc_put_volsw_sx(struct snd_kcontrol *kcontrol,
+			 struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+	struct soc_mixer_control *mc =
+	    (struct soc_mixer_control *)kcontrol->private_value;
+
+	unsigned int reg = mc->reg;
+	unsigned int reg2 = mc->rreg;
+	unsigned int shift = mc->shift;
+	unsigned int rshift = mc->rshift;
+	int max = mc->max;
+	int min = mc->min;
+	int mask = (1 << (fls(min + max) - 1)) - 1;
+	int err = 0;
+	unsigned int val, val_mask, val2 = 0;
+
+	val_mask = mask << shift;
+	val = (ucontrol->value.integer.value[0] + min) & mask;
+	val = val << shift;
+
+	err = snd_soc_component_update_bits(component, reg, val_mask, val);
+	if (err < 0)
+		return err;
+
+	if (snd_soc_volsw_is_stereo(mc)) {
+		val_mask = mask << rshift;
+		val2 = (ucontrol->value.integer.value[1] + min) & mask;
+		val2 = val2 << rshift;
+
+		err = snd_soc_component_update_bits(component, reg2, val_mask,
+			val2);
+	}
+	return err;
+}
+EXPORT_SYMBOL_GPL(snd_soc_put_volsw_sx);
+
+/**
+ * snd_soc_info_volsw_range - single mixer info callback with range.
+ * @kcontrol: mixer control
+ * @uinfo: control element information
+ *
+ * Callback to provide information, within a range, about a single
+ * mixer control.
+ *
+ * returns 0 for success.
+ */
+int snd_soc_info_volsw_range(struct snd_kcontrol *kcontrol,
+	struct snd_ctl_elem_info *uinfo)
+{
+	struct soc_mixer_control *mc =
+		(struct soc_mixer_control *)kcontrol->private_value;
+	int platform_max;
+	int min = mc->min;
+
+	if (!mc->platform_max)
+		mc->platform_max = mc->max;
+	platform_max = mc->platform_max;
+
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+	uinfo->count = snd_soc_volsw_is_stereo(mc) ? 2 : 1;
+	uinfo->value.integer.min = 0;
+	uinfo->value.integer.max = platform_max - min;
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(snd_soc_info_volsw_range);
+
+/**
+ * snd_soc_put_volsw_range - single mixer put value callback with range.
+ * @kcontrol: mixer control
+ * @ucontrol: control element information
+ *
+ * Callback to set the value, within a range, for a single mixer control.
+ *
+ * Returns 0 for success.
+ */
+int snd_soc_put_volsw_range(struct snd_kcontrol *kcontrol,
+	struct snd_ctl_elem_value *ucontrol)
+{
+	struct soc_mixer_control *mc =
+		(struct soc_mixer_control *)kcontrol->private_value;
+	struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+	unsigned int reg = mc->reg;
+	unsigned int rreg = mc->rreg;
+	unsigned int shift = mc->shift;
+	int min = mc->min;
+	int max = mc->max;
+	unsigned int mask = (1 << fls(max)) - 1;
+	unsigned int invert = mc->invert;
+	unsigned int val, val_mask;
+	int ret;
+
+	if (invert)
+		val = (max - ucontrol->value.integer.value[0]) & mask;
+	else
+		val = ((ucontrol->value.integer.value[0] + min) & mask);
+	val_mask = mask << shift;
+	val = val << shift;
+
+	ret = snd_soc_component_update_bits(component, reg, val_mask, val);
+	if (ret < 0)
+		return ret;
+
+	if (snd_soc_volsw_is_stereo(mc)) {
+		if (invert)
+			val = (max - ucontrol->value.integer.value[1]) & mask;
+		else
+			val = ((ucontrol->value.integer.value[1] + min) & mask);
+		val_mask = mask << shift;
+		val = val << shift;
+
+		ret = snd_soc_component_update_bits(component, rreg, val_mask,
+			val);
+	}
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(snd_soc_put_volsw_range);
+
+/**
+ * snd_soc_get_volsw_range - single mixer get callback with range
+ * @kcontrol: mixer control
+ * @ucontrol: control element information
+ *
+ * Callback to get the value, within a range, of a single mixer control.
+ *
+ * Returns 0 for success.
+ */
+int snd_soc_get_volsw_range(struct snd_kcontrol *kcontrol,
+	struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+	struct soc_mixer_control *mc =
+		(struct soc_mixer_control *)kcontrol->private_value;
+	unsigned int reg = mc->reg;
+	unsigned int rreg = mc->rreg;
+	unsigned int shift = mc->shift;
+	int min = mc->min;
+	int max = mc->max;
+	unsigned int mask = (1 << fls(max)) - 1;
+	unsigned int invert = mc->invert;
+	unsigned int val;
+	int ret;
+
+	ret = snd_soc_component_read(component, reg, &val);
+	if (ret)
+		return ret;
+
+	ucontrol->value.integer.value[0] = (val >> shift) & mask;
+	if (invert)
+		ucontrol->value.integer.value[0] =
+			max - ucontrol->value.integer.value[0];
+	else
+		ucontrol->value.integer.value[0] =
+			ucontrol->value.integer.value[0] - min;
+
+	if (snd_soc_volsw_is_stereo(mc)) {
+		ret = snd_soc_component_read(component, rreg, &val);
+		if (ret)
+			return ret;
+
+		ucontrol->value.integer.value[1] = (val >> shift) & mask;
+		if (invert)
+			ucontrol->value.integer.value[1] =
+				max - ucontrol->value.integer.value[1];
+		else
+			ucontrol->value.integer.value[1] =
+				ucontrol->value.integer.value[1] - min;
+	}
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(snd_soc_get_volsw_range);
+
+/**
+ * snd_soc_limit_volume - Set new limit to an existing volume control.
+ *
+ * @codec: where to look for the control
+ * @name: Name of the control
+ * @max: new maximum limit
+ *
+ * Return 0 for success, else error.
+ */
+int snd_soc_limit_volume(struct snd_soc_codec *codec,
+	const char *name, int max)
+{
+	struct snd_card *card = codec->component.card->snd_card;
+	struct snd_kcontrol *kctl;
+	struct soc_mixer_control *mc;
+	int found = 0;
+	int ret = -EINVAL;
+
+	/* Sanity check for name and max */
+	if (unlikely(!name || max <= 0))
+		return -EINVAL;
+
+	list_for_each_entry(kctl, &card->controls, list) {
+		if (!strncmp(kctl->id.name, name, sizeof(kctl->id.name))) {
+			found = 1;
+			break;
+		}
+	}
+	if (found) {
+		mc = (struct soc_mixer_control *)kctl->private_value;
+		if (max <= mc->max) {
+			mc->platform_max = max;
+			ret = 0;
+		}
+	}
+	return ret;
+}
+EXPORT_SYMBOL_GPL(snd_soc_limit_volume);
+
+int snd_soc_bytes_info(struct snd_kcontrol *kcontrol,
+		       struct snd_ctl_elem_info *uinfo)
+{
+	struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+	struct soc_bytes *params = (void *)kcontrol->private_value;
+
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES;
+	uinfo->count = params->num_regs * component->val_bytes;
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(snd_soc_bytes_info);
+
+int snd_soc_bytes_get(struct snd_kcontrol *kcontrol,
+		      struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+	struct soc_bytes *params = (void *)kcontrol->private_value;
+	int ret;
+
+	if (component->regmap)
+		ret = regmap_raw_read(component->regmap, params->base,
+				      ucontrol->value.bytes.data,
+				      params->num_regs * component->val_bytes);
+	else
+		ret = -EINVAL;
+
+	/* Hide any masked bytes to ensure consistent data reporting */
+	if (ret == 0 && params->mask) {
+		switch (component->val_bytes) {
+		case 1:
+			ucontrol->value.bytes.data[0] &= ~params->mask;
+			break;
+		case 2:
+			((u16 *)(&ucontrol->value.bytes.data))[0]
+				&= cpu_to_be16(~params->mask);
+			break;
+		case 4:
+			((u32 *)(&ucontrol->value.bytes.data))[0]
+				&= cpu_to_be32(~params->mask);
+			break;
+		default:
+			return -EINVAL;
+		}
+	}
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(snd_soc_bytes_get);
+
+int snd_soc_bytes_put(struct snd_kcontrol *kcontrol,
+		      struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+	struct soc_bytes *params = (void *)kcontrol->private_value;
+	int ret, len;
+	unsigned int val, mask;
+	void *data;
+
+	if (!component->regmap || !params->num_regs)
+		return -EINVAL;
+
+	len = params->num_regs * component->val_bytes;
+
+	data = kmemdup(ucontrol->value.bytes.data, len, GFP_KERNEL | GFP_DMA);
+	if (!data)
+		return -ENOMEM;
+
+	/*
+	 * If we've got a mask then we need to preserve the register
+	 * bits.  We shouldn't modify the incoming data so take a
+	 * copy.
+	 */
+	if (params->mask) {
+		ret = regmap_read(component->regmap, params->base, &val);
+		if (ret != 0)
+			goto out;
+
+		val &= params->mask;
+
+		switch (component->val_bytes) {
+		case 1:
+			((u8 *)data)[0] &= ~params->mask;
+			((u8 *)data)[0] |= val;
+			break;
+		case 2:
+			mask = ~params->mask;
+			ret = regmap_parse_val(component->regmap,
+							&mask, &mask);
+			if (ret != 0)
+				goto out;
+
+			((u16 *)data)[0] &= mask;
+
+			ret = regmap_parse_val(component->regmap,
+							&val, &val);
+			if (ret != 0)
+				goto out;
+
+			((u16 *)data)[0] |= val;
+			break;
+		case 4:
+			mask = ~params->mask;
+			ret = regmap_parse_val(component->regmap,
+							&mask, &mask);
+			if (ret != 0)
+				goto out;
+
+			((u32 *)data)[0] &= mask;
+
+			ret = regmap_parse_val(component->regmap,
+							&val, &val);
+			if (ret != 0)
+				goto out;
+
+			((u32 *)data)[0] |= val;
+			break;
+		default:
+			ret = -EINVAL;
+			goto out;
+		}
+	}
+
+	ret = regmap_raw_write(component->regmap, params->base,
+			       data, len);
+
+out:
+	kfree(data);
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(snd_soc_bytes_put);
+
+int snd_soc_bytes_info_ext(struct snd_kcontrol *kcontrol,
+			struct snd_ctl_elem_info *ucontrol)
+{
+	struct soc_bytes_ext *params = (void *)kcontrol->private_value;
+
+	ucontrol->type = SNDRV_CTL_ELEM_TYPE_BYTES;
+	ucontrol->count = params->max;
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(snd_soc_bytes_info_ext);
+
+int snd_soc_bytes_tlv_callback(struct snd_kcontrol *kcontrol, int op_flag,
+				unsigned int size, unsigned int __user *tlv)
+{
+	struct soc_bytes_ext *params = (void *)kcontrol->private_value;
+	unsigned int count = size < params->max ? size : params->max;
+	int ret = -ENXIO;
+
+	switch (op_flag) {
+	case SNDRV_CTL_TLV_OP_READ:
+		if (params->get)
+			ret = params->get(tlv, count);
+		break;
+	case SNDRV_CTL_TLV_OP_WRITE:
+		if (params->put)
+			ret = params->put(tlv, count);
+		break;
+	}
+	return ret;
+}
+EXPORT_SYMBOL_GPL(snd_soc_bytes_tlv_callback);
+
+/**
+ * snd_soc_info_xr_sx - signed multi register info callback
+ * @kcontrol: mreg control
+ * @uinfo: control element information
+ *
+ * Callback to provide information of a control that can
+ * span multiple codec registers which together
+ * forms a single signed value in a MSB/LSB manner.
+ *
+ * Returns 0 for success.
+ */
+int snd_soc_info_xr_sx(struct snd_kcontrol *kcontrol,
+	struct snd_ctl_elem_info *uinfo)
+{
+	struct soc_mreg_control *mc =
+		(struct soc_mreg_control *)kcontrol->private_value;
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+	uinfo->count = 1;
+	uinfo->value.integer.min = mc->min;
+	uinfo->value.integer.max = mc->max;
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(snd_soc_info_xr_sx);
+
+/**
+ * snd_soc_get_xr_sx - signed multi register get callback
+ * @kcontrol: mreg control
+ * @ucontrol: control element information
+ *
+ * Callback to get the value of a control that can span
+ * multiple codec registers which together forms a single
+ * signed value in a MSB/LSB manner. The control supports
+ * specifying total no of bits used to allow for bitfields
+ * across the multiple codec registers.
+ *
+ * Returns 0 for success.
+ */
+int snd_soc_get_xr_sx(struct snd_kcontrol *kcontrol,
+	struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+	struct soc_mreg_control *mc =
+		(struct soc_mreg_control *)kcontrol->private_value;
+	unsigned int regbase = mc->regbase;
+	unsigned int regcount = mc->regcount;
+	unsigned int regwshift = component->val_bytes * BITS_PER_BYTE;
+	unsigned int regwmask = (1<<regwshift)-1;
+	unsigned int invert = mc->invert;
+	unsigned long mask = (1UL<<mc->nbits)-1;
+	long min = mc->min;
+	long max = mc->max;
+	long val = 0;
+	unsigned int regval;
+	unsigned int i;
+	int ret;
+
+	for (i = 0; i < regcount; i++) {
+		ret = snd_soc_component_read(component, regbase+i, &regval);
+		if (ret)
+			return ret;
+		val |= (regval & regwmask) << (regwshift*(regcount-i-1));
+	}
+	val &= mask;
+	if (min < 0 && val > max)
+		val |= ~mask;
+	if (invert)
+		val = max - val;
+	ucontrol->value.integer.value[0] = val;
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(snd_soc_get_xr_sx);
+
+/**
+ * snd_soc_put_xr_sx - signed multi register get callback
+ * @kcontrol: mreg control
+ * @ucontrol: control element information
+ *
+ * Callback to set the value of a control that can span
+ * multiple codec registers which together forms a single
+ * signed value in a MSB/LSB manner. The control supports
+ * specifying total no of bits used to allow for bitfields
+ * across the multiple codec registers.
+ *
+ * Returns 0 for success.
+ */
+int snd_soc_put_xr_sx(struct snd_kcontrol *kcontrol,
+	struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+	struct soc_mreg_control *mc =
+		(struct soc_mreg_control *)kcontrol->private_value;
+	unsigned int regbase = mc->regbase;
+	unsigned int regcount = mc->regcount;
+	unsigned int regwshift = component->val_bytes * BITS_PER_BYTE;
+	unsigned int regwmask = (1<<regwshift)-1;
+	unsigned int invert = mc->invert;
+	unsigned long mask = (1UL<<mc->nbits)-1;
+	long max = mc->max;
+	long val = ucontrol->value.integer.value[0];
+	unsigned int i, regval, regmask;
+	int err;
+
+	if (invert)
+		val = max - val;
+	val &= mask;
+	for (i = 0; i < regcount; i++) {
+		regval = (val >> (regwshift*(regcount-i-1))) & regwmask;
+		regmask = (mask >> (regwshift*(regcount-i-1))) & regwmask;
+		err = snd_soc_component_update_bits(component, regbase+i,
+				regmask, regval);
+		if (err < 0)
+			return err;
+	}
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(snd_soc_put_xr_sx);
+
+/**
+ * snd_soc_get_strobe - strobe get callback
+ * @kcontrol: mixer control
+ * @ucontrol: control element information
+ *
+ * Callback get the value of a strobe mixer control.
+ *
+ * Returns 0 for success.
+ */
+int snd_soc_get_strobe(struct snd_kcontrol *kcontrol,
+	struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+	struct soc_mixer_control *mc =
+		(struct soc_mixer_control *)kcontrol->private_value;
+	unsigned int reg = mc->reg;
+	unsigned int shift = mc->shift;
+	unsigned int mask = 1 << shift;
+	unsigned int invert = mc->invert != 0;
+	unsigned int val;
+	int ret;
+
+	ret = snd_soc_component_read(component, reg, &val);
+	if (ret)
+		return ret;
+
+	val &= mask;
+
+	if (shift != 0 && val != 0)
+		val = val >> shift;
+	ucontrol->value.enumerated.item[0] = val ^ invert;
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(snd_soc_get_strobe);
+
+/**
+ * snd_soc_put_strobe - strobe put callback
+ * @kcontrol: mixer control
+ * @ucontrol: control element information
+ *
+ * Callback strobe a register bit to high then low (or the inverse)
+ * in one pass of a single mixer enum control.
+ *
+ * Returns 1 for success.
+ */
+int snd_soc_put_strobe(struct snd_kcontrol *kcontrol,
+	struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+	struct soc_mixer_control *mc =
+		(struct soc_mixer_control *)kcontrol->private_value;
+	unsigned int reg = mc->reg;
+	unsigned int shift = mc->shift;
+	unsigned int mask = 1 << shift;
+	unsigned int invert = mc->invert != 0;
+	unsigned int strobe = ucontrol->value.enumerated.item[0] != 0;
+	unsigned int val1 = (strobe ^ invert) ? mask : 0;
+	unsigned int val2 = (strobe ^ invert) ? 0 : mask;
+	int err;
+
+	err = snd_soc_component_update_bits(component, reg, mask, val1);
+	if (err < 0)
+		return err;
+
+	return snd_soc_component_update_bits(component, reg, mask, val2);
+}
+EXPORT_SYMBOL_GPL(snd_soc_put_strobe);
diff --git a/sound/soc/soc-pcm.c b/sound/soc/soc-pcm.c
index 57277dd79e11..eb87d96e2cf0 100644
--- a/sound/soc/soc-pcm.c
+++ b/sound/soc/soc-pcm.c
@@ -654,6 +654,8 @@ static int soc_pcm_close(struct snd_pcm_substream *substream)
 			codec_dai->rate = 0;
 	}
 
+	snd_soc_dai_digital_mute(cpu_dai, 1, substream->stream);
+
 	if (cpu_dai->driver->ops->shutdown)
 		cpu_dai->driver->ops->shutdown(substream, cpu_dai);
 
@@ -772,6 +774,7 @@ static int soc_pcm_prepare(struct snd_pcm_substream *substream)
 	for (i = 0; i < rtd->num_codecs; i++)
 		snd_soc_dai_digital_mute(rtd->codec_dais[i], 0,
 					 substream->stream);
+	snd_soc_dai_digital_mute(cpu_dai, 0, substream->stream);
 
 out:
 	mutex_unlock(&rtd->pcm_mutex);
@@ -1664,6 +1667,10 @@ int dpcm_be_dai_hw_free(struct snd_soc_pcm_runtime *fe, int stream)
 		if (!snd_soc_dpcm_can_be_free_stop(fe, be, stream))
 				continue;
 
+		/* do not free hw if this BE is used by other FE */
+		if (be->dpcm[stream].users > 1)
+			continue;
+
 		if ((be->dpcm[stream].state != SND_SOC_DPCM_STATE_HW_PARAMS) &&
 		    (be->dpcm[stream].state != SND_SOC_DPCM_STATE_PREPARE) &&
 		    (be->dpcm[stream].state != SND_SOC_DPCM_STATE_HW_FREE) &&
@@ -2288,7 +2295,13 @@ int soc_dpcm_runtime_update(struct snd_soc_card *card)
 			fe->dai_link->name);
 
 		/* skip if FE doesn't have playback capability */
-		if (!fe->cpu_dai->driver->playback.channels_min)
+		if (!fe->cpu_dai->driver->playback.channels_min
+		    || !fe->codec_dai->driver->playback.channels_min)
+			goto capture;
+
+		/* skip if FE isn't currently playing */
+		if (!fe->cpu_dai->playback_active
+		    || !fe->codec_dai->playback_active)
 			goto capture;
 
 		paths = dpcm_path_get(fe, SNDRV_PCM_STREAM_PLAYBACK, &list);
@@ -2318,7 +2331,13 @@ int soc_dpcm_runtime_update(struct snd_soc_card *card)
 		dpcm_path_put(&list);
 capture:
 		/* skip if FE doesn't have capture capability */
-		if (!fe->cpu_dai->driver->capture.channels_min)
+		if (!fe->cpu_dai->driver->capture.channels_min
+		    || !fe->codec_dai->driver->capture.channels_min)
+			continue;
+
+		/* skip if FE isn't currently capturing */
+		if (!fe->cpu_dai->capture_active
+		    || !fe->codec_dai->capture_active)
 			continue;
 
 		paths = dpcm_path_get(fe, SNDRV_PCM_STREAM_CAPTURE, &list);
diff --git a/sound/soc/tegra/tegra20_ac97.c b/sound/soc/tegra/tegra20_ac97.c
index 3b0fa12dbff7..29a9957d335a 100644
--- a/sound/soc/tegra/tegra20_ac97.c
+++ b/sound/soc/tegra/tegra20_ac97.c
@@ -228,7 +228,7 @@ static int tegra20_ac97_probe(struct snd_soc_dai *dai)
 
 static struct snd_soc_dai_driver tegra20_ac97_dai = {
 	.name = "tegra-ac97-pcm",
-	.ac97_control = 1,
+	.bus_control = true,
 	.probe = tegra20_ac97_probe,
 	.playback = {
 		.stream_name = "PCM Playback",
diff --git a/sound/soc/tegra/tegra_rt5640.c b/sound/soc/tegra/tegra_rt5640.c
index a6898831fb9f..4ebe3871e610 100644
--- a/sound/soc/tegra/tegra_rt5640.c
+++ b/sound/soc/tegra/tegra_rt5640.c
@@ -44,6 +44,7 @@
 struct tegra_rt5640 {
 	struct tegra_asoc_utils_data util_data;
 	int gpio_hp_det;
+	enum of_gpio_flags gpio_hp_det_flags;
 };
 
 static int tegra_rt5640_asoc_hw_params(struct snd_pcm_substream *substream,
@@ -119,6 +120,8 @@ static int tegra_rt5640_asoc_init(struct snd_soc_pcm_runtime *rtd)
 
 	if (gpio_is_valid(machine->gpio_hp_det)) {
 		tegra_rt5640_hp_jack_gpio.gpio = machine->gpio_hp_det;
+		tegra_rt5640_hp_jack_gpio.invert =
+			!!(machine->gpio_hp_det_flags & OF_GPIO_ACTIVE_LOW);
 		snd_soc_jack_add_gpios(&tegra_rt5640_hp_jack,
 						1,
 						&tegra_rt5640_hp_jack_gpio);
@@ -180,7 +183,8 @@ static int tegra_rt5640_probe(struct platform_device *pdev)
 	platform_set_drvdata(pdev, card);
 	snd_soc_card_set_drvdata(card, machine);
 
-	machine->gpio_hp_det = of_get_named_gpio(np, "nvidia,hp-det-gpios", 0);
+	machine->gpio_hp_det = of_get_named_gpio_flags(
+		np, "nvidia,hp-det-gpios", 0, &machine->gpio_hp_det_flags);
 	if (machine->gpio_hp_det == -EPROBE_DEFER)
 		return -EPROBE_DEFER;
 
diff --git a/sound/soc/txx9/txx9aclc-ac97.c b/sound/soc/txx9/txx9aclc-ac97.c
index 9edd68db9f48..f7135cdaa2ca 100644
--- a/sound/soc/txx9/txx9aclc-ac97.c
+++ b/sound/soc/txx9/txx9aclc-ac97.c
@@ -152,7 +152,7 @@ static int txx9aclc_ac97_remove(struct snd_soc_dai *dai)
 }
 
 static struct snd_soc_dai_driver txx9aclc_ac97_dai = {
-	.ac97_control		= 1,
+	.bus_control		= true,
 	.probe			= txx9aclc_ac97_probe,
 	.remove			= txx9aclc_ac97_remove,
 	.playback = {
diff --git a/sound/soc/txx9/txx9aclc.c b/sound/soc/txx9/txx9aclc.c
index cd71fd889d8b..00b7e2d02690 100644
--- a/sound/soc/txx9/txx9aclc.c
+++ b/sound/soc/txx9/txx9aclc.c
@@ -292,7 +292,7 @@ static int txx9aclc_pcm_new(struct snd_soc_pcm_runtime *rtd)
 	struct snd_card *card = rtd->card->snd_card;
 	struct snd_soc_dai *dai = rtd->cpu_dai;
 	struct snd_pcm *pcm = rtd->pcm;
-	struct platform_device *pdev = to_platform_device(dai->platform->dev);
+	struct platform_device *pdev = to_platform_device(rtd->platform->dev);
 	struct txx9aclc_soc_device *dev;
 	struct resource *r;
 	int i;
diff --git a/sound/soc/ux500/mop500.c b/sound/soc/ux500/mop500.c
index b3b66aa98dce..9f2d045ee118 100644
--- a/sound/soc/ux500/mop500.c
+++ b/sound/soc/ux500/mop500.c
@@ -63,12 +63,8 @@ static void mop500_of_node_put(void)
 	int i;
 
 	for (i = 0; i < 2; i++) {
-		if (mop500_dai_links[i].cpu_of_node)
-			of_node_put((struct device_node *)
-				mop500_dai_links[i].cpu_of_node);
-		if (mop500_dai_links[i].codec_of_node)
-			of_node_put((struct device_node *)
-				mop500_dai_links[i].codec_of_node);
+		of_node_put(mop500_dai_links[i].cpu_of_node);
+		of_node_put(mop500_dai_links[i].codec_of_node);
 	}
 }