summary refs log tree commit diff
path: root/sound/soc
diff options
context:
space:
mode:
Diffstat (limited to 'sound/soc')
-rw-r--r--sound/soc/Kconfig2
-rw-r--r--sound/soc/Makefile2
-rw-r--r--sound/soc/blackfin/bfin-eval-adau1373.c2
-rw-r--r--sound/soc/blackfin/bfin-eval-adav80x.c2
-rw-r--r--sound/soc/codecs/Kconfig30
-rw-r--r--sound/soc/codecs/Makefile10
-rw-r--r--sound/soc/codecs/ak4613.c10
-rw-r--r--sound/soc/codecs/cs35l35.c1580
-rw-r--r--sound/soc/codecs/cs35l35.h294
-rw-r--r--sound/soc/codecs/cs4271.c2
-rw-r--r--sound/soc/codecs/cs53l30.c1
-rw-r--r--sound/soc/codecs/da7213.c13
-rw-r--r--sound/soc/codecs/dio2125.c120
-rw-r--r--sound/soc/codecs/es7134.c116
-rw-r--r--sound/soc/codecs/es8328.c51
-rw-r--r--sound/soc/codecs/hdac_hdmi.c4
-rw-r--r--sound/soc/codecs/max9867.c4
-rw-r--r--sound/soc/codecs/max98927.c841
-rw-r--r--sound/soc/codecs/max98927.h272
-rw-r--r--sound/soc/codecs/nau8540.c1224
-rw-r--r--sound/soc/codecs/nau8540.h310
-rw-r--r--sound/soc/codecs/nau8824.c1831
-rw-r--r--sound/soc/codecs/nau8824.h466
-rw-r--r--sound/soc/codecs/rt5514.c36
-rw-r--r--sound/soc/codecs/rt5645.c10
-rw-r--r--sound/soc/codecs/rt5665.c222
-rw-r--r--sound/soc/codecs/rt5665.h2
-rw-r--r--sound/soc/codecs/rt5670.c21
-rw-r--r--sound/soc/codecs/rt5677.c7
-rw-r--r--sound/soc/codecs/sgtl5000.c19
-rw-r--r--sound/soc/codecs/ssm4567.c9
-rw-r--r--sound/soc/codecs/sta529.c7
-rw-r--r--sound/soc/codecs/tas2552.c6
-rw-r--r--sound/soc/codecs/tlv320aic23.c7
-rw-r--r--sound/soc/codecs/twl6040.c8
-rw-r--r--sound/soc/codecs/uda1380.c7
-rw-r--r--sound/soc/codecs/wm5100.c2
-rw-r--r--sound/soc/codecs/wm8903.c31
-rw-r--r--sound/soc/codecs/wm8960.c195
-rw-r--r--sound/soc/codecs/wm8978.c7
-rw-r--r--sound/soc/codecs/wm_adsp.c324
-rw-r--r--sound/soc/codecs/wm_adsp.h24
-rw-r--r--sound/soc/dwc/Kconfig4
-rw-r--r--sound/soc/dwc/Makefile6
-rw-r--r--sound/soc/dwc/dwc-i2s.c (renamed from sound/soc/dwc/designware_i2s.c)0
-rw-r--r--sound/soc/dwc/dwc-pcm.c (renamed from sound/soc/dwc/designware_pcm.c)3
-rw-r--r--sound/soc/fsl/eukrea-tlv320.c2
-rw-r--r--sound/soc/fsl/fsl_asrc_dma.c2
-rw-r--r--sound/soc/fsl/fsl_esai.c5
-rw-r--r--sound/soc/fsl/fsl_ssi.c27
-rw-r--r--sound/soc/fsl/imx-mc13783.c2
-rw-r--r--sound/soc/fsl/imx-pcm-dma.c28
-rw-r--r--sound/soc/fsl/imx-pcm-fiq.c2
-rw-r--r--sound/soc/fsl/imx-wm8962.c72
-rw-r--r--sound/soc/fsl/mpc8610_hpcd.c2
-rw-r--r--sound/soc/fsl/mx27vis-aic32x4.c2
-rw-r--r--sound/soc/fsl/p1022_ds.c2
-rw-r--r--sound/soc/fsl/p1022_rdk.c2
-rw-r--r--sound/soc/fsl/phycore-ac97.c2
-rw-r--r--sound/soc/fsl/wm1133-ev1.c2
-rw-r--r--sound/soc/generic/simple-card.c43
-rw-r--r--sound/soc/generic/simple-scu-card.c37
-rw-r--r--sound/soc/hisilicon/Kconfig5
-rw-r--r--sound/soc/hisilicon/Makefile1
-rw-r--r--sound/soc/hisilicon/hi6210-i2s.c618
-rw-r--r--sound/soc/hisilicon/hi6210-i2s.h276
-rw-r--r--sound/soc/intel/Kconfig24
-rw-r--r--sound/soc/intel/atom/sst/sst_acpi.c41
-rw-r--r--sound/soc/intel/atom/sst/sst_ipc.c4
-rw-r--r--sound/soc/intel/boards/Makefile4
-rw-r--r--sound/soc/intel/boards/bdw-rt5677.c5
-rw-r--r--sound/soc/intel/boards/broadwell.c3
-rw-r--r--sound/soc/intel/boards/bxt_da7219_max98357a.c97
-rw-r--r--sound/soc/intel/boards/bxt_rt298.c3
-rw-r--r--sound/soc/intel/boards/bytcht_da7213.c283
-rw-r--r--sound/soc/intel/boards/bytcht_nocodec.c208
-rw-r--r--sound/soc/intel/boards/bytcr_rt5640.c109
-rw-r--r--sound/soc/intel/haswell/sst-haswell-ipc.c6
-rw-r--r--sound/soc/intel/skylake/bxt-sst.c118
-rw-r--r--sound/soc/intel/skylake/skl-messages.c16
-rw-r--r--sound/soc/intel/skylake/skl-nhlt.c7
-rw-r--r--sound/soc/intel/skylake/skl-pcm.c118
-rw-r--r--sound/soc/intel/skylake/skl-sst-cldma.c26
-rw-r--r--sound/soc/intel/skylake/skl-sst-cldma.h2
-rw-r--r--sound/soc/intel/skylake/skl-sst-dsp.c6
-rw-r--r--sound/soc/intel/skylake/skl-sst-dsp.h40
-rw-r--r--sound/soc/intel/skylake/skl-sst-ipc.c76
-rw-r--r--sound/soc/intel/skylake/skl-sst-ipc.h17
-rw-r--r--sound/soc/intel/skylake/skl-sst-utils.c140
-rw-r--r--sound/soc/intel/skylake/skl-sst.c175
-rw-r--r--sound/soc/intel/skylake/skl-topology.c247
-rw-r--r--sound/soc/intel/skylake/skl-topology.h17
-rw-r--r--sound/soc/intel/skylake/skl.c2
-rw-r--r--sound/soc/intel/skylake/skl.h22
-rw-r--r--sound/soc/mediatek/Kconfig10
-rw-r--r--sound/soc/mediatek/mt2701/Makefile1
-rw-r--r--sound/soc/mediatek/mt2701/mt2701-afe-pcm.c16
-rw-r--r--sound/soc/mediatek/mt2701/mt2701-cs42448.c2
-rw-r--r--sound/soc/mediatek/mt2701/mt2701-wm8960.c176
-rw-r--r--sound/soc/mediatek/mt8173/mt8173-max98090.c2
-rw-r--r--sound/soc/mediatek/mt8173/mt8173-rt5650-rt5514.c2
-rw-r--r--sound/soc/mediatek/mt8173/mt8173-rt5650-rt5676.c2
-rw-r--r--sound/soc/mediatek/mt8173/mt8173-rt5650.c2
-rw-r--r--sound/soc/omap/am3517evm.c2
-rw-r--r--sound/soc/omap/n810.c2
-rw-r--r--sound/soc/omap/omap-abe-twl6040.c2
-rw-r--r--sound/soc/omap/omap-twl4030.c2
-rw-r--r--sound/soc/omap/omap3pandora.c2
-rw-r--r--sound/soc/omap/osk5912.c2
-rw-r--r--sound/soc/omap/rx51.c7
-rw-r--r--sound/soc/pxa/brownstone.c2
-rw-r--r--sound/soc/pxa/corgi.c2
-rw-r--r--sound/soc/pxa/e750_wm9705.c2
-rw-r--r--sound/soc/pxa/e800_wm9712.c2
-rw-r--r--sound/soc/pxa/em-x270.c2
-rw-r--r--sound/soc/pxa/hx4700.c2
-rw-r--r--sound/soc/pxa/imote2.c2
-rw-r--r--sound/soc/pxa/magician.c4
-rw-r--r--sound/soc/pxa/mioa701_wm9713.c2
-rw-r--r--sound/soc/pxa/mmp-pcm.c1
-rw-r--r--sound/soc/pxa/mmp-sspa.c1
-rw-r--r--sound/soc/pxa/poodle.c2
-rw-r--r--sound/soc/pxa/pxa-ssp.c15
-rw-r--r--sound/soc/pxa/pxa2xx-ac97.c5
-rw-r--r--sound/soc/pxa/pxa2xx-i2s.c8
-rw-r--r--sound/soc/pxa/pxa2xx-pcm.c2
-rw-r--r--sound/soc/pxa/raumfeld.c8
-rw-r--r--sound/soc/pxa/spitz.c6
-rw-r--r--sound/soc/pxa/tosa.c4
-rw-r--r--sound/soc/pxa/z2.c6
-rw-r--r--sound/soc/pxa/zylonite.c2
-rw-r--r--sound/soc/qcom/lpass-apq8016.c12
-rw-r--r--sound/soc/qcom/lpass-cpu.c22
-rw-r--r--sound/soc/qcom/lpass-ipq806x.c6
-rw-r--r--sound/soc/qcom/lpass.h2
-rw-r--r--sound/soc/rockchip/rk3288_hdmi_analog.c3
-rw-r--r--sound/soc/samsung/Kconfig8
-rw-r--r--sound/soc/samsung/Makefile2
-rw-r--r--sound/soc/samsung/bells.c1
-rw-r--r--sound/soc/samsung/i2s-regs.h2
-rw-r--r--sound/soc/samsung/i2s.c1
-rw-r--r--sound/soc/samsung/odroid.c219
-rw-r--r--sound/soc/samsung/s3c-i2s-v2.c1
-rw-r--r--sound/soc/sh/rcar/adg.c75
-rw-r--r--sound/soc/sh/rcar/core.c111
-rw-r--r--sound/soc/sh/rcar/dvc.c24
-rw-r--r--sound/soc/sh/rcar/rsnd.h55
-rw-r--r--sound/soc/sh/rcar/src.c3
-rw-r--r--sound/soc/sh/rcar/ssi.c9
-rw-r--r--sound/soc/sirf/sirf-audio-port.c1
-rw-r--r--sound/soc/sirf/sirf-audio.c1
-rw-r--r--sound/soc/sirf/sirf-usp.c3
-rw-r--r--sound/soc/soc-core.c27
-rw-r--r--sound/soc/soc-jack.c48
-rw-r--r--sound/soc/soc-topology.c5
-rw-r--r--sound/soc/sti/uniperif_player.c2
-rw-r--r--sound/soc/stm/Kconfig8
-rw-r--r--sound/soc/stm/Makefile6
-rw-r--r--sound/soc/stm/stm32_sai.c115
-rw-r--r--sound/soc/stm/stm32_sai.h200
-rw-r--r--sound/soc/stm/stm32_sai_sub.c884
-rw-r--r--sound/soc/sunxi/sun8i-codec-analog.c168
-rw-r--r--sound/soc/sunxi/sun8i-codec.c10
-rw-r--r--sound/soc/tegra/tegra20_ac97.c1
-rw-r--r--sound/soc/tegra/tegra20_das.c2
-rw-r--r--sound/soc/tegra/tegra20_i2s.c1
-rw-r--r--sound/soc/tegra/tegra20_spdif.c5
-rw-r--r--sound/soc/tegra/tegra30_ahub.c5
-rw-r--r--sound/soc/tegra/tegra30_i2s.c1
-rw-r--r--sound/soc/tegra/tegra_alc5632.c4
-rw-r--r--sound/soc/tegra/tegra_max98090.c4
-rw-r--r--sound/soc/tegra/tegra_rt5640.c4
-rw-r--r--sound/soc/tegra/tegra_sgtl5000.c4
-rw-r--r--sound/soc/tegra/tegra_wm8753.c4
-rw-r--r--sound/soc/tegra/tegra_wm8903.c4
-rw-r--r--sound/soc/tegra/tegra_wm9712.c4
-rw-r--r--sound/soc/tegra/trimslice.c4
-rw-r--r--sound/soc/txx9/txx9aclc.c5
-rw-r--r--sound/soc/ux500/mop500.c4
-rw-r--r--sound/soc/ux500/ux500_msp_dai.c4
-rw-r--r--sound/soc/ux500/ux500_msp_i2s.c1
-rw-r--r--sound/soc/zte/Kconfig8
-rw-r--r--sound/soc/zte/Makefile1
-rw-r--r--sound/soc/zte/zx-tdm.c461
184 files changed, 12069 insertions, 1900 deletions
diff --git a/sound/soc/Kconfig b/sound/soc/Kconfig
index 182d92efc7c8..c0abad2067e1 100644
--- a/sound/soc/Kconfig
+++ b/sound/soc/Kconfig
@@ -47,6 +47,7 @@ source "sound/soc/cirrus/Kconfig"
 source "sound/soc/davinci/Kconfig"
 source "sound/soc/dwc/Kconfig"
 source "sound/soc/fsl/Kconfig"
+source "sound/soc/hisilicon/Kconfig"
 source "sound/soc/jz4740/Kconfig"
 source "sound/soc/nuc900/Kconfig"
 source "sound/soc/omap/Kconfig"
@@ -63,6 +64,7 @@ source "sound/soc/sh/Kconfig"
 source "sound/soc/sirf/Kconfig"
 source "sound/soc/spear/Kconfig"
 source "sound/soc/sti/Kconfig"
+source "sound/soc/stm/Kconfig"
 source "sound/soc/sunxi/Kconfig"
 source "sound/soc/tegra/Kconfig"
 source "sound/soc/txx9/Kconfig"
diff --git a/sound/soc/Makefile b/sound/soc/Makefile
index 9a30f21d16ee..39c27a58158d 100644
--- a/sound/soc/Makefile
+++ b/sound/soc/Makefile
@@ -27,6 +27,7 @@ obj-$(CONFIG_SND_SOC)	+= cirrus/
 obj-$(CONFIG_SND_SOC)	+= davinci/
 obj-$(CONFIG_SND_SOC)	+= dwc/
 obj-$(CONFIG_SND_SOC)	+= fsl/
+obj-$(CONFIG_SND_SOC)	+= hisilicon/
 obj-$(CONFIG_SND_SOC)	+= jz4740/
 obj-$(CONFIG_SND_SOC)	+= img/
 obj-$(CONFIG_SND_SOC)	+= intel/
@@ -43,6 +44,7 @@ obj-$(CONFIG_SND_SOC)	+= sh/
 obj-$(CONFIG_SND_SOC)	+= sirf/
 obj-$(CONFIG_SND_SOC)	+= spear/
 obj-$(CONFIG_SND_SOC)	+= sti/
+obj-$(CONFIG_SND_SOC)	+= stm/
 obj-$(CONFIG_SND_SOC)	+= sunxi/
 obj-$(CONFIG_SND_SOC)	+= tegra/
 obj-$(CONFIG_SND_SOC)	+= txx9/
diff --git a/sound/soc/blackfin/bfin-eval-adau1373.c b/sound/soc/blackfin/bfin-eval-adau1373.c
index 72ac78988426..64b88fdc1f6c 100644
--- a/sound/soc/blackfin/bfin-eval-adau1373.c
+++ b/sound/soc/blackfin/bfin-eval-adau1373.c
@@ -119,7 +119,7 @@ static int bfin_eval_adau1373_codec_init(struct snd_soc_pcm_runtime *rtd)
 
 	return ret;
 }
-static struct snd_soc_ops bfin_eval_adau1373_ops = {
+static const struct snd_soc_ops bfin_eval_adau1373_ops = {
 	.hw_params = bfin_eval_adau1373_hw_params,
 };
 
diff --git a/sound/soc/blackfin/bfin-eval-adav80x.c b/sound/soc/blackfin/bfin-eval-adav80x.c
index 1037477d10b2..99e5ecabdcda 100644
--- a/sound/soc/blackfin/bfin-eval-adav80x.c
+++ b/sound/soc/blackfin/bfin-eval-adav80x.c
@@ -64,7 +64,7 @@ static int bfin_eval_adav80x_codec_init(struct snd_soc_pcm_runtime *rtd)
 	return 0;
 }
 
-static struct snd_soc_ops bfin_eval_adav80x_ops = {
+static const struct snd_soc_ops bfin_eval_adav80x_ops = {
 	.hw_params = bfin_eval_adav80x_hw_params,
 };
 
diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig
index e49e9da7f1f6..883ed4c8a551 100644
--- a/sound/soc/codecs/Kconfig
+++ b/sound/soc/codecs/Kconfig
@@ -49,6 +49,7 @@ config SND_SOC_ALL_CODECS
 	select SND_SOC_CS35L32 if I2C
 	select SND_SOC_CS35L33 if I2C
 	select SND_SOC_CS35L34 if I2C
+	select SND_SOC_CS35L35 if I2C
 	select SND_SOC_CS42L42 if I2C
 	select SND_SOC_CS42L51_I2C if I2C
 	select SND_SOC_CS42L52 if I2C && INPUT
@@ -69,9 +70,11 @@ config SND_SOC_ALL_CODECS
 	select SND_SOC_DA7219 if I2C
 	select SND_SOC_DA732X if I2C
 	select SND_SOC_DA9055 if I2C
+	select SND_SOC_DIO2125
 	select SND_SOC_DMIC
 	select SND_SOC_ES8328_SPI if SPI_MASTER
 	select SND_SOC_ES8328_I2C if I2C
+	select SND_SOC_ES7134
 	select SND_SOC_GTM601
 	select SND_SOC_HDAC_HDMI
 	select SND_SOC_ICS43432
@@ -89,6 +92,7 @@ config SND_SOC_ALL_CODECS
 	select SND_SOC_MAX9867 if I2C
 	select SND_SOC_MAX98925 if I2C
 	select SND_SOC_MAX98926 if I2C
+	select SND_SOC_MAX98927 if I2C
 	select SND_SOC_MAX9850 if I2C
 	select SND_SOC_MAX9860 if I2C
 	select SND_SOC_MAX9768 if I2C
@@ -97,6 +101,7 @@ config SND_SOC_ALL_CODECS
 	select SND_SOC_ML26124 if I2C
 	select SND_SOC_NAU8540 if I2C
 	select SND_SOC_NAU8810 if I2C
+	select SND_SOC_NAU8824 if I2C
 	select SND_SOC_NAU8825 if I2C
 	select SND_SOC_HDMI_CODEC
 	select SND_SOC_PCM1681 if I2C
@@ -303,12 +308,14 @@ config SND_SOC_ADAU1761
 	select SND_SOC_ADAU17X1
 
 config SND_SOC_ADAU1761_I2C
-	tristate
+	tristate "Analog Devices AU1761 CODEC - I2C"
+	depends on I2C
 	select SND_SOC_ADAU1761
 	select REGMAP_I2C
 
 config SND_SOC_ADAU1761_SPI
-	tristate
+	tristate "Analog Devices AU1761 CODEC - SPI"
+	depends on SPI
 	select SND_SOC_ADAU1761
 	select REGMAP_SPI
 
@@ -408,6 +415,10 @@ config SND_SOC_CS35L34
 	tristate "Cirrus Logic CS35L34 CODEC"
 	depends on I2C
 
+config SND_SOC_CS35L35
+	tristate "Cirrus Logic CS35L35 CODEC"
+	depends on I2C
+
 config SND_SOC_CS42L42
 	tristate "Cirrus Logic CS42L42 CODEC"
 	depends on I2C
@@ -516,6 +527,10 @@ config SND_SOC_DA732X
 config SND_SOC_DA9055
 	tristate
 
+config SND_SOC_DIO2125
+	tristate "Dioo DIO2125 Amplifier"
+	select GPIOLIB
+
 config SND_SOC_DMIC
 	tristate
 
@@ -525,6 +540,9 @@ config SND_SOC_HDMI_CODEC
 	select SND_PCM_IEC958
 	select HDMI
 
+config SND_SOC_ES7134
+       tristate "Everest Semi ES7134 CODEC"
+
 config SND_SOC_ES8328
 	tristate
 
@@ -588,6 +606,10 @@ config SND_SOC_MAX98925
 config SND_SOC_MAX98926
 	tristate
 
+config SND_SOC_MAX98927
+	tristate "Maxim Integrated MAX98927 Speaker Amplifier"
+	depends on I2C
+
 config SND_SOC_MAX9850
 	tristate
 
@@ -1116,6 +1138,10 @@ config SND_SOC_NAU8810
 	tristate "Nuvoton Technology Corporation NAU88C10 CODEC"
 	depends on I2C
 
+config SND_SOC_NAU8824
+	tristate "Nuvoton Technology Corporation NAU88L24 CODEC"
+	depends on I2C
+
 config SND_SOC_NAU8825
 	tristate
 
diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile
index 1796cb987e71..28a63fdaf982 100644
--- a/sound/soc/codecs/Makefile
+++ b/sound/soc/codecs/Makefile
@@ -39,6 +39,7 @@ snd-soc-cq93vc-objs := cq93vc.o
 snd-soc-cs35l32-objs := cs35l32.o
 snd-soc-cs35l33-objs := cs35l33.o
 snd-soc-cs35l34-objs := cs35l34.o
+snd-soc-cs35l35-objs := cs35l35.o
 snd-soc-cs42l42-objs := cs42l42.o
 snd-soc-cs42l51-objs := cs42l51.o
 snd-soc-cs42l51-i2c-objs := cs42l51-i2c.o
@@ -63,6 +64,7 @@ snd-soc-da7219-objs := da7219.o da7219-aad.o
 snd-soc-da732x-objs := da732x.o
 snd-soc-da9055-objs := da9055.o
 snd-soc-dmic-objs := dmic.o
+snd-soc-es7134-objs := es7134.o
 snd-soc-es8328-objs := es8328.o
 snd-soc-es8328-i2c-objs := es8328-i2c.o
 snd-soc-es8328-spi-objs := es8328-spi.o
@@ -84,6 +86,7 @@ snd-soc-max98371-objs := max98371.o
 snd-soc-max9867-objs := max9867.o
 snd-soc-max98925-objs := max98925.o
 snd-soc-max98926-objs := max98926.o
+snd-soc-max98927-objs := max98927.o
 snd-soc-max9850-objs := max9850.o
 snd-soc-max9860-objs := max9860.o
 snd-soc-mc13783-objs := mc13783.o
@@ -92,6 +95,7 @@ snd-soc-msm8916-analog-objs := msm8916-wcd-analog.o
 snd-soc-msm8916-digital-objs := msm8916-wcd-digital.o
 snd-soc-nau8540-objs := nau8540.o
 snd-soc-nau8810-objs := nau8810.o
+snd-soc-nau8824-objs := nau8824.o
 snd-soc-nau8825-objs := nau8825.o
 snd-soc-hdmi-codec-objs := hdmi-codec.o
 snd-soc-pcm1681-objs := pcm1681.o
@@ -221,6 +225,7 @@ snd-soc-wm9712-objs := wm9712.o
 snd-soc-wm9713-objs := wm9713.o
 snd-soc-wm-hubs-objs := wm_hubs.o
 # Amp
+snd-soc-dio2125-objs := dio2125.o
 snd-soc-max9877-objs := max9877.o
 snd-soc-max98504-objs := max98504.o
 snd-soc-tpa6130a2-objs := tpa6130a2.o
@@ -269,6 +274,7 @@ obj-$(CONFIG_SND_SOC_CQ0093VC) += snd-soc-cq93vc.o
 obj-$(CONFIG_SND_SOC_CS35L32)	+= snd-soc-cs35l32.o
 obj-$(CONFIG_SND_SOC_CS35L33)	+= snd-soc-cs35l33.o
 obj-$(CONFIG_SND_SOC_CS35L34)	+= snd-soc-cs35l34.o
+obj-$(CONFIG_SND_SOC_CS35L35)	+= snd-soc-cs35l35.o
 obj-$(CONFIG_SND_SOC_CS42L42)	+= snd-soc-cs42l42.o
 obj-$(CONFIG_SND_SOC_CS42L51)	+= snd-soc-cs42l51.o
 obj-$(CONFIG_SND_SOC_CS42L51_I2C)	+= snd-soc-cs42l51-i2c.o
@@ -293,6 +299,7 @@ obj-$(CONFIG_SND_SOC_DA7219)	+= snd-soc-da7219.o
 obj-$(CONFIG_SND_SOC_DA732X)	+= snd-soc-da732x.o
 obj-$(CONFIG_SND_SOC_DA9055)	+= snd-soc-da9055.o
 obj-$(CONFIG_SND_SOC_DMIC)	+= snd-soc-dmic.o
+obj-$(CONFIG_SND_SOC_ES7134)	+= snd-soc-es7134.o
 obj-$(CONFIG_SND_SOC_ES8328)	+= snd-soc-es8328.o
 obj-$(CONFIG_SND_SOC_ES8328_I2C)+= snd-soc-es8328-i2c.o
 obj-$(CONFIG_SND_SOC_ES8328_SPI)+= snd-soc-es8328-spi.o
@@ -313,6 +320,7 @@ obj-$(CONFIG_SND_SOC_MAX98357A)	+= snd-soc-max98357a.o
 obj-$(CONFIG_SND_SOC_MAX9867)	+= snd-soc-max9867.o
 obj-$(CONFIG_SND_SOC_MAX98925)	+= snd-soc-max98925.o
 obj-$(CONFIG_SND_SOC_MAX98926)	+= snd-soc-max98926.o
+obj-$(CONFIG_SND_SOC_MAX98927)	+= snd-soc-max98927.o
 obj-$(CONFIG_SND_SOC_MAX9850)	+= snd-soc-max9850.o
 obj-$(CONFIG_SND_SOC_MAX9860)	+= snd-soc-max9860.o
 obj-$(CONFIG_SND_SOC_MC13783)	+= snd-soc-mc13783.o
@@ -321,6 +329,7 @@ obj-$(CONFIG_SND_SOC_MSM8916_WCD_ANALOG) +=snd-soc-msm8916-analog.o
 obj-$(CONFIG_SND_SOC_MSM8916_WCD_DIGITAL) +=snd-soc-msm8916-digital.o
 obj-$(CONFIG_SND_SOC_NAU8540)   += snd-soc-nau8540.o
 obj-$(CONFIG_SND_SOC_NAU8810)   += snd-soc-nau8810.o
+obj-$(CONFIG_SND_SOC_NAU8824)   += snd-soc-nau8824.o
 obj-$(CONFIG_SND_SOC_NAU8825)   += snd-soc-nau8825.o
 obj-$(CONFIG_SND_SOC_HDMI_CODEC)	+= snd-soc-hdmi-codec.o
 obj-$(CONFIG_SND_SOC_PCM1681)	+= snd-soc-pcm1681.o
@@ -448,6 +457,7 @@ obj-$(CONFIG_SND_SOC_WM_ADSP)	+= snd-soc-wm-adsp.o
 obj-$(CONFIG_SND_SOC_WM_HUBS)	+= snd-soc-wm-hubs.o
 
 # Amp
+obj-$(CONFIG_SND_SOC_DIO2125)	+= snd-soc-dio2125.o
 obj-$(CONFIG_SND_SOC_MAX9877)	+= snd-soc-max9877.o
 obj-$(CONFIG_SND_SOC_MAX98504)	+= snd-soc-max98504.o
 obj-$(CONFIG_SND_SOC_TPA6130A2)	+= snd-soc-tpa6130a2.o
diff --git a/sound/soc/codecs/ak4613.c b/sound/soc/codecs/ak4613.c
index e819dd8c82fd..b2dfddead227 100644
--- a/sound/soc/codecs/ak4613.c
+++ b/sound/soc/codecs/ak4613.c
@@ -75,6 +75,12 @@
 #define DFS_DOUBLE_SPEED	(1 << 2)
 #define DFS_QUAD_SPEED		(2 << 2)
 
+/* ICTRL */
+#define ICTRL_MASK	(0x3)
+
+/* OCTRL */
+#define OCTRL_MASK	(0x3F)
+
 struct ak4613_formats {
 	unsigned int width;
 	unsigned int fmt;
@@ -365,8 +371,8 @@ static int ak4613_dai_hw_params(struct snd_pcm_substream *substream,
 	snd_soc_update_bits(codec, CTRL1, FMT_MASK, fmt_ctrl);
 	snd_soc_update_bits(codec, CTRL2, DFS_MASK, ctrl2);
 
-	snd_soc_write(codec, ICTRL, priv->ic);
-	snd_soc_write(codec, OCTRL, priv->oc);
+	snd_soc_update_bits(codec, ICTRL, ICTRL_MASK, priv->ic);
+	snd_soc_update_bits(codec, OCTRL, OCTRL_MASK, priv->oc);
 
 hw_params_end:
 	if (ret < 0)
diff --git a/sound/soc/codecs/cs35l35.c b/sound/soc/codecs/cs35l35.c
new file mode 100644
index 000000000000..f8aef5869b03
--- /dev/null
+++ b/sound/soc/codecs/cs35l35.c
@@ -0,0 +1,1580 @@
+/*
+ * cs35l35.c -- CS35L35 ALSA SoC audio driver
+ *
+ * Copyright 2017 Cirrus Logic, Inc.
+ *
+ * Author: Brian Austin <brian.austin@cirrus.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/version.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/slab.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/consumer.h>
+#include <linux/gpio/consumer.h>
+#include <linux/of_device.h>
+#include <linux/of_gpio.h>
+#include <linux/regmap.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <linux/gpio.h>
+#include <sound/initval.h>
+#include <sound/tlv.h>
+#include <sound/cs35l35.h>
+#include <linux/of_irq.h>
+#include <linux/completion.h>
+
+#include "cs35l35.h"
+
+/*
+ * Some fields take zero as a valid value so use a high bit flag that won't
+ * get written to the device to mark those.
+ */
+#define CS35L35_VALID_PDATA 0x80000000
+
+static const struct reg_default cs35l35_reg[] = {
+	{CS35L35_PWRCTL1,		0x01},
+	{CS35L35_PWRCTL2,		0x11},
+	{CS35L35_PWRCTL3,		0x00},
+	{CS35L35_CLK_CTL1,		0x04},
+	{CS35L35_CLK_CTL2,		0x12},
+	{CS35L35_CLK_CTL3,		0xCF},
+	{CS35L35_SP_FMT_CTL1,		0x20},
+	{CS35L35_SP_FMT_CTL2,		0x00},
+	{CS35L35_SP_FMT_CTL3,		0x02},
+	{CS35L35_MAG_COMP_CTL,		0x00},
+	{CS35L35_AMP_INP_DRV_CTL,	0x01},
+	{CS35L35_AMP_DIG_VOL_CTL,	0x12},
+	{CS35L35_AMP_DIG_VOL,		0x00},
+	{CS35L35_ADV_DIG_VOL,		0x00},
+	{CS35L35_PROTECT_CTL,		0x06},
+	{CS35L35_AMP_GAIN_AUD_CTL,	0x13},
+	{CS35L35_AMP_GAIN_PDM_CTL,	0x00},
+	{CS35L35_AMP_GAIN_ADV_CTL,	0x00},
+	{CS35L35_GPI_CTL,		0x00},
+	{CS35L35_BST_CVTR_V_CTL,	0x00},
+	{CS35L35_BST_PEAK_I,		0x07},
+	{CS35L35_BST_RAMP_CTL,		0x85},
+	{CS35L35_BST_CONV_COEF_1,	0x24},
+	{CS35L35_BST_CONV_COEF_2,	0x24},
+	{CS35L35_BST_CONV_SLOPE_COMP,	0x4E},
+	{CS35L35_BST_CONV_SW_FREQ,	0x04},
+	{CS35L35_CLASS_H_CTL,		0x0B},
+	{CS35L35_CLASS_H_HEADRM_CTL,	0x0B},
+	{CS35L35_CLASS_H_RELEASE_RATE,	0x08},
+	{CS35L35_CLASS_H_FET_DRIVE_CTL, 0x41},
+	{CS35L35_CLASS_H_VP_CTL,	0xC5},
+	{CS35L35_VPBR_CTL,		0x0A},
+	{CS35L35_VPBR_VOL_CTL,		0x90},
+	{CS35L35_VPBR_TIMING_CTL,	0x6A},
+	{CS35L35_VPBR_MODE_VOL_CTL,	0x00},
+	{CS35L35_SPKR_MON_CTL,		0xC0},
+	{CS35L35_IMON_SCALE_CTL,	0x30},
+	{CS35L35_AUDIN_RXLOC_CTL,	0x00},
+	{CS35L35_ADVIN_RXLOC_CTL,	0x80},
+	{CS35L35_VMON_TXLOC_CTL,	0x00},
+	{CS35L35_IMON_TXLOC_CTL,	0x80},
+	{CS35L35_VPMON_TXLOC_CTL,	0x04},
+	{CS35L35_VBSTMON_TXLOC_CTL,	0x84},
+	{CS35L35_VPBR_STATUS_TXLOC_CTL,	0x04},
+	{CS35L35_ZERO_FILL_LOC_CTL,	0x00},
+	{CS35L35_AUDIN_DEPTH_CTL,	0x0F},
+	{CS35L35_SPKMON_DEPTH_CTL,	0x0F},
+	{CS35L35_SUPMON_DEPTH_CTL,	0x0F},
+	{CS35L35_ZEROFILL_DEPTH_CTL,	0x00},
+	{CS35L35_MULT_DEV_SYNCH1,	0x02},
+	{CS35L35_MULT_DEV_SYNCH2,	0x80},
+	{CS35L35_PROT_RELEASE_CTL,	0x00},
+	{CS35L35_DIAG_MODE_REG_LOCK,	0x00},
+	{CS35L35_DIAG_MODE_CTL_1,	0x40},
+	{CS35L35_DIAG_MODE_CTL_2,	0x00},
+	{CS35L35_INT_MASK_1,		0xFF},
+	{CS35L35_INT_MASK_2,		0xFF},
+	{CS35L35_INT_MASK_3,		0xFF},
+	{CS35L35_INT_MASK_4,		0xFF},
+
+};
+
+static bool cs35l35_volatile_register(struct device *dev, unsigned int reg)
+{
+	switch (reg) {
+	case CS35L35_INT_STATUS_1:
+	case CS35L35_INT_STATUS_2:
+	case CS35L35_INT_STATUS_3:
+	case CS35L35_INT_STATUS_4:
+	case CS35L35_PLL_STATUS:
+	case CS35L35_OTP_TRIM_STATUS:
+		return true;
+	default:
+		return false;
+	}
+}
+
+static bool cs35l35_readable_register(struct device *dev, unsigned int reg)
+{
+	switch (reg) {
+	case CS35L35_DEVID_AB ... CS35L35_PWRCTL3:
+	case CS35L35_CLK_CTL1 ... CS35L35_SP_FMT_CTL3:
+	case CS35L35_MAG_COMP_CTL ... CS35L35_AMP_GAIN_AUD_CTL:
+	case CS35L35_AMP_GAIN_PDM_CTL ... CS35L35_BST_PEAK_I:
+	case CS35L35_BST_RAMP_CTL ... CS35L35_BST_CONV_SW_FREQ:
+	case CS35L35_CLASS_H_CTL ... CS35L35_CLASS_H_VP_CTL:
+	case CS35L35_CLASS_H_STATUS:
+	case CS35L35_VPBR_CTL ... CS35L35_VPBR_MODE_VOL_CTL:
+	case CS35L35_VPBR_ATTEN_STATUS:
+	case CS35L35_SPKR_MON_CTL:
+	case CS35L35_IMON_SCALE_CTL ... CS35L35_ZEROFILL_DEPTH_CTL:
+	case CS35L35_MULT_DEV_SYNCH1 ... CS35L35_PROT_RELEASE_CTL:
+	case CS35L35_DIAG_MODE_REG_LOCK ... CS35L35_DIAG_MODE_CTL_2:
+	case CS35L35_INT_MASK_1 ... CS35L35_PLL_STATUS:
+	case CS35L35_OTP_TRIM_STATUS:
+		return true;
+	default:
+		return false;
+	}
+}
+
+static bool cs35l35_precious_register(struct device *dev, unsigned int reg)
+{
+	switch (reg) {
+	case CS35L35_INT_STATUS_1:
+	case CS35L35_INT_STATUS_2:
+	case CS35L35_INT_STATUS_3:
+	case CS35L35_INT_STATUS_4:
+	case CS35L35_PLL_STATUS:
+	case CS35L35_OTP_TRIM_STATUS:
+		return true;
+	default:
+		return false;
+	}
+}
+
+static int cs35l35_wait_for_pdn(struct cs35l35_private *cs35l35)
+{
+	int ret;
+
+	if (cs35l35->pdata.ext_bst) {
+		usleep_range(5000, 5500);
+		return 0;
+	}
+
+	reinit_completion(&cs35l35->pdn_done);
+
+	ret = wait_for_completion_timeout(&cs35l35->pdn_done,
+					  msecs_to_jiffies(100));
+	if (ret == 0) {
+		dev_err(cs35l35->dev, "PDN_DONE did not complete\n");
+		return -ETIMEDOUT;
+	}
+
+	return 0;
+}
+
+static int cs35l35_sdin_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);
+	struct cs35l35_private *cs35l35 = snd_soc_codec_get_drvdata(codec);
+	int ret = 0;
+
+	switch (event) {
+	case SND_SOC_DAPM_PRE_PMU:
+		regmap_update_bits(cs35l35->regmap, CS35L35_CLK_CTL1,
+					CS35L35_MCLK_DIS_MASK,
+					0 << CS35L35_MCLK_DIS_SHIFT);
+		regmap_update_bits(cs35l35->regmap, CS35L35_PWRCTL1,
+					CS35L35_DISCHG_FILT_MASK,
+					0 << CS35L35_DISCHG_FILT_SHIFT);
+		regmap_update_bits(cs35l35->regmap, CS35L35_PWRCTL1,
+					CS35L35_PDN_ALL_MASK, 0);
+		break;
+	case SND_SOC_DAPM_POST_PMD:
+		regmap_update_bits(cs35l35->regmap, CS35L35_PWRCTL1,
+					CS35L35_DISCHG_FILT_MASK,
+					1 << CS35L35_DISCHG_FILT_SHIFT);
+		regmap_update_bits(cs35l35->regmap, CS35L35_PWRCTL1,
+					  CS35L35_PDN_ALL_MASK, 1);
+
+		/* Already muted, so disable volume ramp for faster shutdown */
+		regmap_update_bits(cs35l35->regmap, CS35L35_AMP_DIG_VOL_CTL,
+				   CS35L35_AMP_DIGSFT_MASK, 0);
+
+		ret = cs35l35_wait_for_pdn(cs35l35);
+
+		regmap_update_bits(cs35l35->regmap, CS35L35_CLK_CTL1,
+					CS35L35_MCLK_DIS_MASK,
+					1 << CS35L35_MCLK_DIS_SHIFT);
+
+		regmap_update_bits(cs35l35->regmap, CS35L35_AMP_DIG_VOL_CTL,
+				   CS35L35_AMP_DIGSFT_MASK,
+				   1 << CS35L35_AMP_DIGSFT_SHIFT);
+		break;
+	default:
+		dev_err(codec->dev, "Invalid event = 0x%x\n", event);
+		ret = -EINVAL;
+	}
+	return ret;
+}
+
+static int cs35l35_main_amp_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);
+	struct cs35l35_private *cs35l35 = snd_soc_codec_get_drvdata(codec);
+	unsigned int reg[4];
+	int i;
+
+	switch (event) {
+	case SND_SOC_DAPM_PRE_PMU:
+		if (cs35l35->pdata.bst_pdn_fet_on)
+			regmap_update_bits(cs35l35->regmap, CS35L35_PWRCTL2,
+				CS35L35_PDN_BST_MASK,
+				0 << CS35L35_PDN_BST_FETON_SHIFT);
+		else
+			regmap_update_bits(cs35l35->regmap, CS35L35_PWRCTL2,
+				CS35L35_PDN_BST_MASK,
+				0 << CS35L35_PDN_BST_FETOFF_SHIFT);
+		break;
+	case SND_SOC_DAPM_POST_PMU:
+		usleep_range(5000, 5100);
+		/* If in PDM mode we must use VP for Voltage control */
+		if (cs35l35->pdm_mode)
+			regmap_update_bits(cs35l35->regmap,
+					CS35L35_BST_CVTR_V_CTL,
+					CS35L35_BST_CTL_MASK,
+					0 << CS35L35_BST_CTL_SHIFT);
+
+		regmap_update_bits(cs35l35->regmap, CS35L35_PROTECT_CTL,
+			CS35L35_AMP_MUTE_MASK, 0);
+
+		for (i = 0; i < 2; i++)
+			regmap_bulk_read(cs35l35->regmap, CS35L35_INT_STATUS_1,
+					&reg, ARRAY_SIZE(reg));
+
+		break;
+	case SND_SOC_DAPM_PRE_PMD:
+		regmap_update_bits(cs35l35->regmap, CS35L35_PROTECT_CTL,
+				CS35L35_AMP_MUTE_MASK,
+				1 << CS35L35_AMP_MUTE_SHIFT);
+		if (cs35l35->pdata.bst_pdn_fet_on)
+			regmap_update_bits(cs35l35->regmap, CS35L35_PWRCTL2,
+				CS35L35_PDN_BST_MASK,
+				1 << CS35L35_PDN_BST_FETON_SHIFT);
+		else
+			regmap_update_bits(cs35l35->regmap, CS35L35_PWRCTL2,
+				CS35L35_PDN_BST_MASK,
+				1 << CS35L35_PDN_BST_FETOFF_SHIFT);
+		break;
+	case SND_SOC_DAPM_POST_PMD:
+		usleep_range(5000, 5100);
+		/*
+		 * If PDM mode we should switch back to pdata value
+		 * for Voltage control when we go down
+		 */
+		if (cs35l35->pdm_mode)
+			regmap_update_bits(cs35l35->regmap,
+					CS35L35_BST_CVTR_V_CTL,
+					CS35L35_BST_CTL_MASK,
+					cs35l35->pdata.bst_vctl
+					<< CS35L35_BST_CTL_SHIFT);
+
+		break;
+	default:
+		dev_err(codec->dev, "Invalid event = 0x%x\n", event);
+	}
+	return 0;
+}
+
+static DECLARE_TLV_DB_SCALE(amp_gain_tlv, 0, 1, 1);
+static DECLARE_TLV_DB_SCALE(dig_vol_tlv, -10200, 50, 0);
+
+static const struct snd_kcontrol_new cs35l35_aud_controls[] = {
+	SOC_SINGLE_SX_TLV("Digital Audio Volume", CS35L35_AMP_DIG_VOL,
+		      0, 0x34, 0xE4, dig_vol_tlv),
+	SOC_SINGLE_TLV("Analog Audio Volume", CS35L35_AMP_GAIN_AUD_CTL, 0, 19, 0,
+			amp_gain_tlv),
+	SOC_SINGLE_TLV("PDM Volume", CS35L35_AMP_GAIN_PDM_CTL, 0, 19, 0,
+			amp_gain_tlv),
+};
+
+static const struct snd_kcontrol_new cs35l35_adv_controls[] = {
+	SOC_SINGLE_SX_TLV("Digital Advisory Volume", CS35L35_ADV_DIG_VOL,
+		      0, 0x34, 0xE4, dig_vol_tlv),
+	SOC_SINGLE_TLV("Analog Advisory Volume", CS35L35_AMP_GAIN_ADV_CTL, 0, 19, 0,
+			amp_gain_tlv),
+};
+
+static const struct snd_soc_dapm_widget cs35l35_dapm_widgets[] = {
+	SND_SOC_DAPM_AIF_IN_E("SDIN", NULL, 0, CS35L35_PWRCTL3, 1, 1,
+				cs35l35_sdin_event, SND_SOC_DAPM_PRE_PMU |
+				SND_SOC_DAPM_POST_PMD),
+	SND_SOC_DAPM_AIF_OUT("SDOUT", NULL, 0, CS35L35_PWRCTL3, 2, 1),
+
+	SND_SOC_DAPM_OUTPUT("SPK"),
+
+	SND_SOC_DAPM_INPUT("VP"),
+	SND_SOC_DAPM_INPUT("VBST"),
+	SND_SOC_DAPM_INPUT("ISENSE"),
+	SND_SOC_DAPM_INPUT("VSENSE"),
+
+	SND_SOC_DAPM_ADC("VMON ADC", NULL, CS35L35_PWRCTL2, 7, 1),
+	SND_SOC_DAPM_ADC("IMON ADC", NULL, CS35L35_PWRCTL2, 6, 1),
+	SND_SOC_DAPM_ADC("VPMON ADC", NULL, CS35L35_PWRCTL3, 3, 1),
+	SND_SOC_DAPM_ADC("VBSTMON ADC", NULL, CS35L35_PWRCTL3, 4, 1),
+	SND_SOC_DAPM_ADC("CLASS H", NULL, CS35L35_PWRCTL2, 5, 1),
+
+	SND_SOC_DAPM_OUT_DRV_E("Main AMP", CS35L35_PWRCTL2, 0, 1, NULL, 0,
+		cs35l35_main_amp_event, SND_SOC_DAPM_PRE_PMU |
+				SND_SOC_DAPM_POST_PMD | SND_SOC_DAPM_POST_PMU |
+				SND_SOC_DAPM_PRE_PMD),
+};
+
+static const struct snd_soc_dapm_route cs35l35_audio_map[] = {
+	{"VPMON ADC", NULL, "VP"},
+	{"VBSTMON ADC", NULL, "VBST"},
+	{"IMON ADC", NULL, "ISENSE"},
+	{"VMON ADC", NULL, "VSENSE"},
+	{"SDOUT", NULL, "IMON ADC"},
+	{"SDOUT", NULL, "VMON ADC"},
+	{"SDOUT", NULL, "VBSTMON ADC"},
+	{"SDOUT", NULL, "VPMON ADC"},
+	{"AMP Capture", NULL, "SDOUT"},
+
+	{"SDIN", NULL, "AMP Playback"},
+	{"CLASS H", NULL, "SDIN"},
+	{"Main AMP", NULL, "CLASS H"},
+	{"SPK", NULL, "Main AMP"},
+};
+
+static int cs35l35_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
+{
+	struct snd_soc_codec *codec = codec_dai->codec;
+	struct cs35l35_private *cs35l35 = snd_soc_codec_get_drvdata(codec);
+
+	switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+	case SND_SOC_DAIFMT_CBM_CFM:
+		regmap_update_bits(cs35l35->regmap, CS35L35_CLK_CTL1,
+				    CS35L35_MS_MASK, 1 << CS35L35_MS_SHIFT);
+		cs35l35->slave_mode = false;
+		break;
+	case SND_SOC_DAIFMT_CBS_CFS:
+		regmap_update_bits(cs35l35->regmap, CS35L35_CLK_CTL1,
+				    CS35L35_MS_MASK, 0 << CS35L35_MS_SHIFT);
+		cs35l35->slave_mode = true;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+	case SND_SOC_DAIFMT_I2S:
+		cs35l35->i2s_mode = true;
+		cs35l35->pdm_mode = false;
+		break;
+	case SND_SOC_DAIFMT_PDM:
+		cs35l35->pdm_mode = true;
+		cs35l35->i2s_mode = false;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+struct cs35l35_sysclk_config {
+	int sysclk;
+	int srate;
+	u8 clk_cfg;
+};
+
+static struct cs35l35_sysclk_config cs35l35_clk_ctl[] = {
+
+	/* SYSCLK, Sample Rate, Serial Port Cfg */
+	{5644800, 44100, 0x00},
+	{5644800, 88200, 0x40},
+	{6144000, 48000, 0x10},
+	{6144000, 96000, 0x50},
+	{11289600, 44100, 0x01},
+	{11289600, 88200, 0x41},
+	{11289600, 176400, 0x81},
+	{12000000, 44100, 0x03},
+	{12000000, 48000, 0x13},
+	{12000000, 88200, 0x43},
+	{12000000, 96000, 0x53},
+	{12000000, 176400, 0x83},
+	{12000000, 192000, 0x93},
+	{12288000, 48000, 0x11},
+	{12288000, 96000, 0x51},
+	{12288000, 192000, 0x91},
+	{13000000, 44100, 0x07},
+	{13000000, 48000, 0x17},
+	{13000000, 88200, 0x47},
+	{13000000, 96000, 0x57},
+	{13000000, 176400, 0x87},
+	{13000000, 192000, 0x97},
+	{22579200, 44100, 0x02},
+	{22579200, 88200, 0x42},
+	{22579200, 176400, 0x82},
+	{24000000, 44100, 0x0B},
+	{24000000, 48000, 0x1B},
+	{24000000, 88200, 0x4B},
+	{24000000, 96000, 0x5B},
+	{24000000, 176400, 0x8B},
+	{24000000, 192000, 0x9B},
+	{24576000, 48000, 0x12},
+	{24576000, 96000, 0x52},
+	{24576000, 192000, 0x92},
+	{26000000, 44100, 0x0F},
+	{26000000, 48000, 0x1F},
+	{26000000, 88200, 0x4F},
+	{26000000, 96000, 0x5F},
+	{26000000, 176400, 0x8F},
+	{26000000, 192000, 0x9F},
+};
+
+static int cs35l35_get_clk_config(int sysclk, int srate)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(cs35l35_clk_ctl); i++) {
+		if (cs35l35_clk_ctl[i].sysclk == sysclk &&
+			cs35l35_clk_ctl[i].srate == srate)
+			return cs35l35_clk_ctl[i].clk_cfg;
+	}
+	return -EINVAL;
+}
+
+static int cs35l35_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 cs35l35_private *cs35l35 = snd_soc_codec_get_drvdata(codec);
+	struct classh_cfg *classh = &cs35l35->pdata.classh_algo;
+	int srate = params_rate(params);
+	int ret = 0;
+	u8 sp_sclks;
+	int audin_format;
+	int errata_chk;
+
+	int clk_ctl = cs35l35_get_clk_config(cs35l35->sysclk, srate);
+
+	if (clk_ctl < 0) {
+		dev_err(codec->dev, "Invalid CLK:Rate %d:%d\n",
+			cs35l35->sysclk, srate);
+		return -EINVAL;
+	}
+
+	ret = regmap_update_bits(cs35l35->regmap, CS35L35_CLK_CTL2,
+			  CS35L35_CLK_CTL2_MASK, clk_ctl);
+	if (ret != 0) {
+		dev_err(codec->dev, "Failed to set port config %d\n", ret);
+		return ret;
+	}
+
+	/*
+	 * Rev A0 Errata
+	 * When configured for the weak-drive detection path (CH_WKFET_DIS = 0)
+	 * the Class H algorithm does not enable weak-drive operation for
+	 * nonzero values of CH_WKFET_DELAY if SP_RATE = 01 or 10
+	 */
+	errata_chk = clk_ctl & CS35L35_SP_RATE_MASK;
+
+	if (classh->classh_wk_fet_disable == 0x00 &&
+		(errata_chk == 0x01 || errata_chk == 0x03)) {
+		ret = regmap_update_bits(cs35l35->regmap,
+					CS35L35_CLASS_H_FET_DRIVE_CTL,
+					CS35L35_CH_WKFET_DEL_MASK,
+					0 << CS35L35_CH_WKFET_DEL_SHIFT);
+		if (ret != 0) {
+			dev_err(codec->dev, "Failed to set fet config %d\n",
+				ret);
+			return ret;
+		}
+	}
+
+	/*
+	 * You can pull more Monitor data from the SDOUT pin than going to SDIN
+	 * Just make sure your SCLK is fast enough to fill the frame
+	 */
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+		switch (params_width(params)) {
+		case 8:
+			audin_format = CS35L35_SDIN_DEPTH_8;
+			break;
+		case 16:
+			audin_format = CS35L35_SDIN_DEPTH_16;
+			break;
+		case 24:
+			audin_format = CS35L35_SDIN_DEPTH_24;
+			break;
+		default:
+			dev_err(codec->dev, "Unsupported Width %d\n",
+				params_width(params));
+			return -EINVAL;
+		}
+		regmap_update_bits(cs35l35->regmap,
+				CS35L35_AUDIN_DEPTH_CTL,
+				CS35L35_AUDIN_DEPTH_MASK,
+				audin_format <<
+				CS35L35_AUDIN_DEPTH_SHIFT);
+		if (cs35l35->pdata.stereo) {
+			regmap_update_bits(cs35l35->regmap,
+					CS35L35_AUDIN_DEPTH_CTL,
+					CS35L35_ADVIN_DEPTH_MASK,
+					audin_format <<
+					CS35L35_ADVIN_DEPTH_SHIFT);
+		}
+	}
+
+	if (cs35l35->i2s_mode) {
+		/* We have to take the SCLK to derive num sclks
+		 * to configure the CLOCK_CTL3 register correctly
+		 */
+		if ((cs35l35->sclk / srate) % 4) {
+			dev_err(codec->dev, "Unsupported sclk/fs ratio %d:%d\n",
+					cs35l35->sclk, srate);
+			return -EINVAL;
+		}
+		sp_sclks = ((cs35l35->sclk / srate) / 4) - 1;
+
+		/* Only certain ratios are supported in I2S Slave Mode */
+		if (cs35l35->slave_mode) {
+			switch (sp_sclks) {
+			case CS35L35_SP_SCLKS_32FS:
+			case CS35L35_SP_SCLKS_48FS:
+			case CS35L35_SP_SCLKS_64FS:
+				break;
+			default:
+				dev_err(codec->dev, "ratio not supported\n");
+				return -EINVAL;
+			}
+		} else {
+			/* Only certain ratios supported in I2S MASTER Mode */
+			switch (sp_sclks) {
+			case CS35L35_SP_SCLKS_32FS:
+			case CS35L35_SP_SCLKS_64FS:
+				break;
+			default:
+				dev_err(codec->dev, "ratio not supported\n");
+				return -EINVAL;
+			}
+		}
+		ret = regmap_update_bits(cs35l35->regmap,
+					CS35L35_CLK_CTL3,
+					CS35L35_SP_SCLKS_MASK, sp_sclks <<
+					CS35L35_SP_SCLKS_SHIFT);
+		if (ret != 0) {
+			dev_err(codec->dev, "Failed to set fsclk %d\n", ret);
+			return ret;
+		}
+	}
+
+	return ret;
+}
+
+static const unsigned int cs35l35_src_rates[] = {
+	44100, 48000, 88200, 96000, 176400, 192000
+};
+
+static const struct snd_pcm_hw_constraint_list cs35l35_constraints = {
+	.count  = ARRAY_SIZE(cs35l35_src_rates),
+	.list   = cs35l35_src_rates,
+};
+
+static int cs35l35_pcm_startup(struct snd_pcm_substream *substream,
+			       struct snd_soc_dai *dai)
+{
+	struct snd_soc_codec *codec = dai->codec;
+	struct cs35l35_private *cs35l35 = snd_soc_codec_get_drvdata(codec);
+
+	if (!substream->runtime)
+		return 0;
+
+	snd_pcm_hw_constraint_list(substream->runtime, 0,
+				SNDRV_PCM_HW_PARAM_RATE, &cs35l35_constraints);
+
+	regmap_update_bits(cs35l35->regmap, CS35L35_AMP_INP_DRV_CTL,
+					CS35L35_PDM_MODE_MASK,
+					0 << CS35L35_PDM_MODE_SHIFT);
+
+	return 0;
+}
+
+static const unsigned int cs35l35_pdm_rates[] = {
+	44100, 48000, 88200, 96000
+};
+
+static const struct snd_pcm_hw_constraint_list cs35l35_pdm_constraints = {
+	.count  = ARRAY_SIZE(cs35l35_pdm_rates),
+	.list   = cs35l35_pdm_rates,
+};
+
+static int cs35l35_pdm_startup(struct snd_pcm_substream *substream,
+			       struct snd_soc_dai *dai)
+{
+	struct snd_soc_codec *codec = dai->codec;
+	struct cs35l35_private *cs35l35 = snd_soc_codec_get_drvdata(codec);
+
+	if (!substream->runtime)
+		return 0;
+
+	snd_pcm_hw_constraint_list(substream->runtime, 0,
+				SNDRV_PCM_HW_PARAM_RATE,
+				&cs35l35_pdm_constraints);
+
+	regmap_update_bits(cs35l35->regmap, CS35L35_AMP_INP_DRV_CTL,
+					CS35L35_PDM_MODE_MASK,
+					1 << CS35L35_PDM_MODE_SHIFT);
+
+	return 0;
+}
+
+static int cs35l35_dai_set_sysclk(struct snd_soc_dai *dai,
+				int clk_id, unsigned int freq, int dir)
+{
+	struct snd_soc_codec *codec = dai->codec;
+	struct cs35l35_private *cs35l35 = snd_soc_codec_get_drvdata(codec);
+
+	/* Need the SCLK Frequency regardless of sysclk source for I2S */
+	cs35l35->sclk = freq;
+
+	return 0;
+}
+
+static const struct snd_soc_dai_ops cs35l35_ops = {
+	.startup = cs35l35_pcm_startup,
+	.set_fmt = cs35l35_set_dai_fmt,
+	.hw_params = cs35l35_hw_params,
+	.set_sysclk = cs35l35_dai_set_sysclk,
+};
+
+static const struct snd_soc_dai_ops cs35l35_pdm_ops = {
+	.startup = cs35l35_pdm_startup,
+	.set_fmt = cs35l35_set_dai_fmt,
+	.hw_params = cs35l35_hw_params,
+};
+
+static struct snd_soc_dai_driver cs35l35_dai[] = {
+	{
+		.name = "cs35l35-pcm",
+		.id = 0,
+		.playback = {
+			.stream_name = "AMP Playback",
+			.channels_min = 1,
+			.channels_max = 8,
+			.rates = SNDRV_PCM_RATE_KNOT,
+			.formats = CS35L35_FORMATS,
+		},
+		.capture = {
+			.stream_name = "AMP Capture",
+			.channels_min = 1,
+			.channels_max = 8,
+			.rates = SNDRV_PCM_RATE_KNOT,
+			.formats = CS35L35_FORMATS,
+		},
+		.ops = &cs35l35_ops,
+		.symmetric_rates = 1,
+	},
+	{
+		.name = "cs35l35-pdm",
+		.id = 1,
+		.playback = {
+			.stream_name = "PDM Playback",
+			.channels_min = 1,
+			.channels_max = 2,
+			.rates = SNDRV_PCM_RATE_KNOT,
+			.formats = CS35L35_FORMATS,
+		},
+		.ops = &cs35l35_pdm_ops,
+	},
+};
+
+static int cs35l35_codec_set_sysclk(struct snd_soc_codec *codec,
+				int clk_id, int source, unsigned int freq,
+				int dir)
+{
+	struct cs35l35_private *cs35l35 = snd_soc_codec_get_drvdata(codec);
+	int clksrc;
+	int ret = 0;
+
+	switch (clk_id) {
+	case 0:
+		clksrc = CS35L35_CLK_SOURCE_MCLK;
+		break;
+	case 1:
+		clksrc = CS35L35_CLK_SOURCE_SCLK;
+		break;
+	case 2:
+		clksrc = CS35L35_CLK_SOURCE_PDM;
+		break;
+	default:
+		dev_err(codec->dev, "Invalid CLK Source\n");
+		return -EINVAL;
+	}
+
+	switch (freq) {
+	case 5644800:
+	case 6144000:
+	case 11289600:
+	case 12000000:
+	case 12288000:
+	case 13000000:
+	case 22579200:
+	case 24000000:
+	case 24576000:
+	case 26000000:
+		cs35l35->sysclk = freq;
+		break;
+	default:
+		dev_err(codec->dev, "Invalid CLK Frequency Input : %d\n", freq);
+		return -EINVAL;
+	}
+
+	ret = regmap_update_bits(cs35l35->regmap, CS35L35_CLK_CTL1,
+				CS35L35_CLK_SOURCE_MASK,
+				clksrc << CS35L35_CLK_SOURCE_SHIFT);
+	if (ret != 0) {
+		dev_err(codec->dev, "Failed to set sysclk %d\n", ret);
+		return ret;
+	}
+
+	return ret;
+}
+
+static int cs35l35_codec_probe(struct snd_soc_codec *codec)
+{
+	struct cs35l35_private *cs35l35 = snd_soc_codec_get_drvdata(codec);
+	struct classh_cfg *classh = &cs35l35->pdata.classh_algo;
+	struct monitor_cfg *monitor_config = &cs35l35->pdata.mon_cfg;
+	int ret;
+
+	/* Set Platform Data */
+	if (cs35l35->pdata.bst_vctl)
+		regmap_update_bits(cs35l35->regmap, CS35L35_BST_CVTR_V_CTL,
+				CS35L35_BST_CTL_MASK,
+				cs35l35->pdata.bst_vctl);
+
+	if (cs35l35->pdata.bst_ipk)
+		regmap_update_bits(cs35l35->regmap, CS35L35_BST_PEAK_I,
+				CS35L35_BST_IPK_MASK,
+				cs35l35->pdata.bst_ipk <<
+				CS35L35_BST_IPK_SHIFT);
+
+	if (cs35l35->pdata.gain_zc)
+		regmap_update_bits(cs35l35->regmap, CS35L35_PROTECT_CTL,
+				CS35L35_AMP_GAIN_ZC_MASK,
+				cs35l35->pdata.gain_zc <<
+				CS35L35_AMP_GAIN_ZC_SHIFT);
+
+	if (cs35l35->pdata.aud_channel)
+		regmap_update_bits(cs35l35->regmap,
+				CS35L35_AUDIN_RXLOC_CTL,
+				CS35L35_AUD_IN_LR_MASK,
+				cs35l35->pdata.aud_channel <<
+				CS35L35_AUD_IN_LR_SHIFT);
+
+	if (cs35l35->pdata.stereo) {
+		regmap_update_bits(cs35l35->regmap,
+				CS35L35_ADVIN_RXLOC_CTL,
+				CS35L35_ADV_IN_LR_MASK,
+				cs35l35->pdata.adv_channel <<
+				CS35L35_ADV_IN_LR_SHIFT);
+		if (cs35l35->pdata.shared_bst)
+			regmap_update_bits(cs35l35->regmap, CS35L35_CLASS_H_CTL,
+					CS35L35_CH_STEREO_MASK,
+					1 << CS35L35_CH_STEREO_SHIFT);
+		ret = snd_soc_add_codec_controls(codec, cs35l35_adv_controls,
+					ARRAY_SIZE(cs35l35_adv_controls));
+		if (ret)
+			return ret;
+	}
+
+	if (cs35l35->pdata.sp_drv_str)
+		regmap_update_bits(cs35l35->regmap, CS35L35_CLK_CTL1,
+				CS35L35_SP_DRV_MASK,
+				cs35l35->pdata.sp_drv_str <<
+				CS35L35_SP_DRV_SHIFT);
+	if (cs35l35->pdata.sp_drv_unused)
+		regmap_update_bits(cs35l35->regmap, CS35L35_SP_FMT_CTL3,
+				   CS35L35_SP_I2S_DRV_MASK,
+				   cs35l35->pdata.sp_drv_unused <<
+				   CS35L35_SP_I2S_DRV_SHIFT);
+
+	if (classh->classh_algo_enable) {
+		if (classh->classh_bst_override)
+			regmap_update_bits(cs35l35->regmap,
+					CS35L35_CLASS_H_CTL,
+					CS35L35_CH_BST_OVR_MASK,
+					classh->classh_bst_override <<
+					CS35L35_CH_BST_OVR_SHIFT);
+		if (classh->classh_bst_max_limit)
+			regmap_update_bits(cs35l35->regmap,
+					CS35L35_CLASS_H_CTL,
+					CS35L35_CH_BST_LIM_MASK,
+					classh->classh_bst_max_limit <<
+					CS35L35_CH_BST_LIM_SHIFT);
+		if (classh->classh_mem_depth)
+			regmap_update_bits(cs35l35->regmap,
+					CS35L35_CLASS_H_CTL,
+					CS35L35_CH_MEM_DEPTH_MASK,
+					classh->classh_mem_depth <<
+					CS35L35_CH_MEM_DEPTH_SHIFT);
+		if (classh->classh_headroom)
+			regmap_update_bits(cs35l35->regmap,
+					CS35L35_CLASS_H_HEADRM_CTL,
+					CS35L35_CH_HDRM_CTL_MASK,
+					classh->classh_headroom <<
+					CS35L35_CH_HDRM_CTL_SHIFT);
+		if (classh->classh_release_rate)
+			regmap_update_bits(cs35l35->regmap,
+					CS35L35_CLASS_H_RELEASE_RATE,
+					CS35L35_CH_REL_RATE_MASK,
+					classh->classh_release_rate <<
+					CS35L35_CH_REL_RATE_SHIFT);
+		if (classh->classh_wk_fet_disable)
+			regmap_update_bits(cs35l35->regmap,
+					CS35L35_CLASS_H_FET_DRIVE_CTL,
+					CS35L35_CH_WKFET_DIS_MASK,
+					classh->classh_wk_fet_disable <<
+					CS35L35_CH_WKFET_DIS_SHIFT);
+		if (classh->classh_wk_fet_delay)
+			regmap_update_bits(cs35l35->regmap,
+					CS35L35_CLASS_H_FET_DRIVE_CTL,
+					CS35L35_CH_WKFET_DEL_MASK,
+					classh->classh_wk_fet_delay <<
+					CS35L35_CH_WKFET_DEL_SHIFT);
+		if (classh->classh_wk_fet_thld)
+			regmap_update_bits(cs35l35->regmap,
+					CS35L35_CLASS_H_FET_DRIVE_CTL,
+					CS35L35_CH_WKFET_THLD_MASK,
+					classh->classh_wk_fet_thld <<
+					CS35L35_CH_WKFET_THLD_SHIFT);
+		if (classh->classh_vpch_auto)
+			regmap_update_bits(cs35l35->regmap,
+					CS35L35_CLASS_H_VP_CTL,
+					CS35L35_CH_VP_AUTO_MASK,
+					classh->classh_vpch_auto <<
+					CS35L35_CH_VP_AUTO_SHIFT);
+		if (classh->classh_vpch_rate)
+			regmap_update_bits(cs35l35->regmap,
+					CS35L35_CLASS_H_VP_CTL,
+					CS35L35_CH_VP_RATE_MASK,
+					classh->classh_vpch_rate <<
+					CS35L35_CH_VP_RATE_SHIFT);
+		if (classh->classh_vpch_man)
+			regmap_update_bits(cs35l35->regmap,
+					CS35L35_CLASS_H_VP_CTL,
+					CS35L35_CH_VP_MAN_MASK,
+					classh->classh_vpch_man <<
+					CS35L35_CH_VP_MAN_SHIFT);
+	}
+
+	if (monitor_config->is_present) {
+		if (monitor_config->vmon_specs) {
+			regmap_update_bits(cs35l35->regmap,
+					CS35L35_SPKMON_DEPTH_CTL,
+					CS35L35_VMON_DEPTH_MASK,
+					monitor_config->vmon_dpth <<
+					CS35L35_VMON_DEPTH_SHIFT);
+			regmap_update_bits(cs35l35->regmap,
+					CS35L35_VMON_TXLOC_CTL,
+					CS35L35_MON_TXLOC_MASK,
+					monitor_config->vmon_loc <<
+					CS35L35_MON_TXLOC_SHIFT);
+			regmap_update_bits(cs35l35->regmap,
+					CS35L35_VMON_TXLOC_CTL,
+					CS35L35_MON_FRM_MASK,
+					monitor_config->vmon_frm <<
+					CS35L35_MON_FRM_SHIFT);
+		}
+		if (monitor_config->imon_specs) {
+			regmap_update_bits(cs35l35->regmap,
+					CS35L35_SPKMON_DEPTH_CTL,
+					CS35L35_IMON_DEPTH_MASK,
+					monitor_config->imon_dpth <<
+					CS35L35_IMON_DEPTH_SHIFT);
+			regmap_update_bits(cs35l35->regmap,
+					CS35L35_IMON_TXLOC_CTL,
+					CS35L35_MON_TXLOC_MASK,
+					monitor_config->imon_loc <<
+					CS35L35_MON_TXLOC_SHIFT);
+			regmap_update_bits(cs35l35->regmap,
+					CS35L35_IMON_TXLOC_CTL,
+					CS35L35_MON_FRM_MASK,
+					monitor_config->imon_frm <<
+					CS35L35_MON_FRM_SHIFT);
+			regmap_update_bits(cs35l35->regmap,
+					CS35L35_IMON_SCALE_CTL,
+					CS35L35_IMON_SCALE_MASK,
+					monitor_config->imon_scale <<
+					CS35L35_IMON_SCALE_SHIFT);
+		}
+		if (monitor_config->vpmon_specs) {
+			regmap_update_bits(cs35l35->regmap,
+					CS35L35_SUPMON_DEPTH_CTL,
+					CS35L35_VPMON_DEPTH_MASK,
+					monitor_config->vpmon_dpth <<
+					CS35L35_VPMON_DEPTH_SHIFT);
+			regmap_update_bits(cs35l35->regmap,
+					CS35L35_VPMON_TXLOC_CTL,
+					CS35L35_MON_TXLOC_MASK,
+					monitor_config->vpmon_loc <<
+					CS35L35_MON_TXLOC_SHIFT);
+			regmap_update_bits(cs35l35->regmap,
+					CS35L35_VPMON_TXLOC_CTL,
+					CS35L35_MON_FRM_MASK,
+					monitor_config->vpmon_frm <<
+					CS35L35_MON_FRM_SHIFT);
+		}
+		if (monitor_config->vbstmon_specs) {
+			regmap_update_bits(cs35l35->regmap,
+					CS35L35_SUPMON_DEPTH_CTL,
+					CS35L35_VBSTMON_DEPTH_MASK,
+					monitor_config->vpmon_dpth <<
+					CS35L35_VBSTMON_DEPTH_SHIFT);
+			regmap_update_bits(cs35l35->regmap,
+					CS35L35_VBSTMON_TXLOC_CTL,
+					CS35L35_MON_TXLOC_MASK,
+					monitor_config->vbstmon_loc <<
+					CS35L35_MON_TXLOC_SHIFT);
+			regmap_update_bits(cs35l35->regmap,
+					CS35L35_VBSTMON_TXLOC_CTL,
+					CS35L35_MON_FRM_MASK,
+					monitor_config->vbstmon_frm <<
+					CS35L35_MON_FRM_SHIFT);
+		}
+		if (monitor_config->vpbrstat_specs) {
+			regmap_update_bits(cs35l35->regmap,
+					CS35L35_SUPMON_DEPTH_CTL,
+					CS35L35_VPBRSTAT_DEPTH_MASK,
+					monitor_config->vpbrstat_dpth <<
+					CS35L35_VPBRSTAT_DEPTH_SHIFT);
+			regmap_update_bits(cs35l35->regmap,
+					CS35L35_VPBR_STATUS_TXLOC_CTL,
+					CS35L35_MON_TXLOC_MASK,
+					monitor_config->vpbrstat_loc <<
+					CS35L35_MON_TXLOC_SHIFT);
+			regmap_update_bits(cs35l35->regmap,
+					CS35L35_VPBR_STATUS_TXLOC_CTL,
+					CS35L35_MON_FRM_MASK,
+					monitor_config->vpbrstat_frm <<
+					CS35L35_MON_FRM_SHIFT);
+		}
+		if (monitor_config->zerofill_specs) {
+			regmap_update_bits(cs35l35->regmap,
+					CS35L35_SUPMON_DEPTH_CTL,
+					CS35L35_ZEROFILL_DEPTH_MASK,
+					monitor_config->zerofill_dpth <<
+					CS35L35_ZEROFILL_DEPTH_SHIFT);
+			regmap_update_bits(cs35l35->regmap,
+					CS35L35_ZERO_FILL_LOC_CTL,
+					CS35L35_MON_TXLOC_MASK,
+					monitor_config->zerofill_loc <<
+					CS35L35_MON_TXLOC_SHIFT);
+			regmap_update_bits(cs35l35->regmap,
+					CS35L35_ZERO_FILL_LOC_CTL,
+					CS35L35_MON_FRM_MASK,
+					monitor_config->zerofill_frm <<
+					CS35L35_MON_FRM_SHIFT);
+		}
+	}
+
+	return 0;
+}
+
+static struct snd_soc_codec_driver soc_codec_dev_cs35l35 = {
+	.probe = cs35l35_codec_probe,
+	.set_sysclk = cs35l35_codec_set_sysclk,
+	.component_driver = {
+		.dapm_widgets = cs35l35_dapm_widgets,
+		.num_dapm_widgets = ARRAY_SIZE(cs35l35_dapm_widgets),
+
+		.dapm_routes = cs35l35_audio_map,
+		.num_dapm_routes = ARRAY_SIZE(cs35l35_audio_map),
+
+		.controls = cs35l35_aud_controls,
+		.num_controls = ARRAY_SIZE(cs35l35_aud_controls),
+	},
+
+};
+
+static struct regmap_config cs35l35_regmap = {
+	.reg_bits = 8,
+	.val_bits = 8,
+
+	.max_register = CS35L35_MAX_REGISTER,
+	.reg_defaults = cs35l35_reg,
+	.num_reg_defaults = ARRAY_SIZE(cs35l35_reg),
+	.volatile_reg = cs35l35_volatile_register,
+	.readable_reg = cs35l35_readable_register,
+	.precious_reg = cs35l35_precious_register,
+	.cache_type = REGCACHE_RBTREE,
+};
+
+static irqreturn_t cs35l35_irq(int irq, void *data)
+{
+	struct cs35l35_private *cs35l35 = data;
+	unsigned int sticky1, sticky2, sticky3, sticky4;
+	unsigned int mask1, mask2, mask3, mask4, current1;
+
+	/* ack the irq by reading all status registers */
+	regmap_read(cs35l35->regmap, CS35L35_INT_STATUS_4, &sticky4);
+	regmap_read(cs35l35->regmap, CS35L35_INT_STATUS_3, &sticky3);
+	regmap_read(cs35l35->regmap, CS35L35_INT_STATUS_2, &sticky2);
+	regmap_read(cs35l35->regmap, CS35L35_INT_STATUS_1, &sticky1);
+
+	regmap_read(cs35l35->regmap, CS35L35_INT_MASK_4, &mask4);
+	regmap_read(cs35l35->regmap, CS35L35_INT_MASK_3, &mask3);
+	regmap_read(cs35l35->regmap, CS35L35_INT_MASK_2, &mask2);
+	regmap_read(cs35l35->regmap, CS35L35_INT_MASK_1, &mask1);
+
+	/* Check to see if unmasked bits are active */
+	if (!(sticky1 & ~mask1) && !(sticky2 & ~mask2) && !(sticky3 & ~mask3)
+			&& !(sticky4 & ~mask4))
+		return IRQ_NONE;
+
+	if (sticky2 & CS35L35_PDN_DONE)
+		complete(&cs35l35->pdn_done);
+
+	/* read the current values */
+	regmap_read(cs35l35->regmap, CS35L35_INT_STATUS_1, &current1);
+
+	/* handle the interrupts */
+	if (sticky1 & CS35L35_CAL_ERR) {
+		dev_crit(cs35l35->dev, "Calibration Error\n");
+
+		/* error is no longer asserted; safe to reset */
+		if (!(current1 & CS35L35_CAL_ERR)) {
+			pr_debug("%s : Cal error release\n", __func__);
+			regmap_update_bits(cs35l35->regmap,
+					CS35L35_PROT_RELEASE_CTL,
+					CS35L35_CAL_ERR_RLS, 0);
+			regmap_update_bits(cs35l35->regmap,
+					CS35L35_PROT_RELEASE_CTL,
+					CS35L35_CAL_ERR_RLS,
+					CS35L35_CAL_ERR_RLS);
+			regmap_update_bits(cs35l35->regmap,
+					CS35L35_PROT_RELEASE_CTL,
+					CS35L35_CAL_ERR_RLS, 0);
+		}
+	}
+
+	if (sticky1 & CS35L35_AMP_SHORT) {
+		dev_crit(cs35l35->dev, "AMP Short Error\n");
+		/* error is no longer asserted; safe to reset */
+		if (!(current1 & CS35L35_AMP_SHORT)) {
+			dev_dbg(cs35l35->dev, "Amp short error release\n");
+			regmap_update_bits(cs35l35->regmap,
+					CS35L35_PROT_RELEASE_CTL,
+					CS35L35_SHORT_RLS, 0);
+			regmap_update_bits(cs35l35->regmap,
+					CS35L35_PROT_RELEASE_CTL,
+					CS35L35_SHORT_RLS,
+					CS35L35_SHORT_RLS);
+			regmap_update_bits(cs35l35->regmap,
+					CS35L35_PROT_RELEASE_CTL,
+					CS35L35_SHORT_RLS, 0);
+		}
+	}
+
+	if (sticky1 & CS35L35_OTW) {
+		dev_warn(cs35l35->dev, "Over temperature warning\n");
+
+		/* error is no longer asserted; safe to reset */
+		if (!(current1 & CS35L35_OTW)) {
+			dev_dbg(cs35l35->dev, "Over temperature warn release\n");
+			regmap_update_bits(cs35l35->regmap,
+					CS35L35_PROT_RELEASE_CTL,
+					CS35L35_OTW_RLS, 0);
+			regmap_update_bits(cs35l35->regmap,
+					CS35L35_PROT_RELEASE_CTL,
+					CS35L35_OTW_RLS,
+					CS35L35_OTW_RLS);
+			regmap_update_bits(cs35l35->regmap,
+					CS35L35_PROT_RELEASE_CTL,
+					CS35L35_OTW_RLS, 0);
+		}
+	}
+
+	if (sticky1 & CS35L35_OTE) {
+		dev_crit(cs35l35->dev, "Over temperature error\n");
+		/* error is no longer asserted; safe to reset */
+		if (!(current1 & CS35L35_OTE)) {
+			dev_dbg(cs35l35->dev, "Over temperature error release\n");
+			regmap_update_bits(cs35l35->regmap,
+					CS35L35_PROT_RELEASE_CTL,
+					CS35L35_OTE_RLS, 0);
+			regmap_update_bits(cs35l35->regmap,
+					CS35L35_PROT_RELEASE_CTL,
+					CS35L35_OTE_RLS,
+					CS35L35_OTE_RLS);
+			regmap_update_bits(cs35l35->regmap,
+					CS35L35_PROT_RELEASE_CTL,
+					CS35L35_OTE_RLS, 0);
+		}
+	}
+
+	if (sticky3 & CS35L35_BST_HIGH) {
+		dev_crit(cs35l35->dev, "VBST error: powering off!\n");
+		regmap_update_bits(cs35l35->regmap, CS35L35_PWRCTL2,
+			CS35L35_PDN_AMP, CS35L35_PDN_AMP);
+		regmap_update_bits(cs35l35->regmap, CS35L35_PWRCTL1,
+			CS35L35_PDN_ALL, CS35L35_PDN_ALL);
+	}
+
+	if (sticky3 & CS35L35_LBST_SHORT) {
+		dev_crit(cs35l35->dev, "LBST error: powering off!\n");
+		regmap_update_bits(cs35l35->regmap, CS35L35_PWRCTL2,
+			CS35L35_PDN_AMP, CS35L35_PDN_AMP);
+		regmap_update_bits(cs35l35->regmap, CS35L35_PWRCTL1,
+			CS35L35_PDN_ALL, CS35L35_PDN_ALL);
+	}
+
+	if (sticky2 & CS35L35_VPBR_ERR)
+		dev_dbg(cs35l35->dev, "Error: Reactive Brownout\n");
+
+	if (sticky4 & CS35L35_VMON_OVFL)
+		dev_dbg(cs35l35->dev, "Error: VMON overflow\n");
+
+	if (sticky4 & CS35L35_IMON_OVFL)
+		dev_dbg(cs35l35->dev, "Error: IMON overflow\n");
+
+	return IRQ_HANDLED;
+}
+
+
+static int cs35l35_handle_of_data(struct i2c_client *i2c_client,
+				struct cs35l35_platform_data *pdata)
+{
+	struct device_node *np = i2c_client->dev.of_node;
+	struct device_node *classh, *signal_format;
+	struct classh_cfg *classh_config = &pdata->classh_algo;
+	struct monitor_cfg *monitor_config = &pdata->mon_cfg;
+	unsigned int val32 = 0;
+	u8 monitor_array[4];
+	const int imon_array_size = ARRAY_SIZE(monitor_array);
+	const int mon_array_size = imon_array_size - 1;
+	int ret = 0;
+
+	if (!np)
+		return 0;
+
+	pdata->bst_pdn_fet_on = of_property_read_bool(np,
+					"cirrus,boost-pdn-fet-on");
+
+	ret = of_property_read_u32(np, "cirrus,boost-ctl-millivolt", &val32);
+	if (ret >= 0) {
+		if (val32 < 2600 || val32 > 9000) {
+			dev_err(&i2c_client->dev,
+				"Invalid Boost Voltage %d mV\n", val32);
+			return -EINVAL;
+		}
+		pdata->bst_vctl = ((val32 - 2600) / 100) + 1;
+	}
+
+	ret = of_property_read_u32(np, "cirrus,boost-peak-milliamp", &val32);
+	if (ret >= 0) {
+		if (val32 < 1680 || val32 > 4480) {
+			dev_err(&i2c_client->dev,
+				"Invalid Boost Peak Current %u mA\n", val32);
+			return -EINVAL;
+		}
+
+		pdata->bst_ipk = (val32 - 1680) / 110;
+	}
+
+	if (of_property_read_u32(np, "cirrus,sp-drv-strength", &val32) >= 0)
+		pdata->sp_drv_str = val32;
+	if (of_property_read_u32(np, "cirrus,sp-drv-unused", &val32) >= 0)
+		pdata->sp_drv_unused = val32 | CS35L35_VALID_PDATA;
+
+	pdata->stereo = of_property_read_bool(np, "cirrus,stereo-config");
+
+	if (pdata->stereo) {
+		ret = of_property_read_u32(np, "cirrus,audio-channel", &val32);
+		if (ret >= 0)
+			pdata->aud_channel = val32;
+
+		ret = of_property_read_u32(np, "cirrus,advisory-channel",
+					   &val32);
+		if (ret >= 0)
+			pdata->adv_channel = val32;
+
+		pdata->shared_bst = of_property_read_bool(np,
+						"cirrus,shared-boost");
+	}
+
+	pdata->ext_bst = of_property_read_bool(np, "cirrus,external-boost");
+
+	pdata->gain_zc = of_property_read_bool(np, "cirrus,amp-gain-zc");
+
+	classh = of_get_child_by_name(np, "cirrus,classh-internal-algo");
+	classh_config->classh_algo_enable = classh ? true : false;
+
+	if (classh_config->classh_algo_enable) {
+		classh_config->classh_bst_override =
+			of_property_read_bool(np, "cirrus,classh-bst-overide");
+
+		ret = of_property_read_u32(classh,
+					   "cirrus,classh-bst-max-limit",
+					   &val32);
+		if (ret >= 0) {
+			val32 |= CS35L35_VALID_PDATA;
+			classh_config->classh_bst_max_limit = val32;
+		}
+
+		ret = of_property_read_u32(classh,
+					   "cirrus,classh-bst-max-limit",
+					   &val32);
+		if (ret >= 0) {
+			val32 |= CS35L35_VALID_PDATA;
+			classh_config->classh_bst_max_limit = val32;
+		}
+
+		ret = of_property_read_u32(classh, "cirrus,classh-mem-depth",
+					   &val32);
+		if (ret >= 0) {
+			val32 |= CS35L35_VALID_PDATA;
+			classh_config->classh_mem_depth = val32;
+		}
+
+		ret = of_property_read_u32(classh, "cirrus,classh-release-rate",
+					   &val32);
+		if (ret >= 0)
+			classh_config->classh_release_rate = val32;
+
+		ret = of_property_read_u32(classh, "cirrus,classh-headroom",
+					   &val32);
+		if (ret >= 0) {
+			val32 |= CS35L35_VALID_PDATA;
+			classh_config->classh_headroom = val32;
+		}
+
+		ret = of_property_read_u32(classh,
+					   "cirrus,classh-wk-fet-disable",
+					   &val32);
+		if (ret >= 0)
+			classh_config->classh_wk_fet_disable = val32;
+
+		ret = of_property_read_u32(classh, "cirrus,classh-wk-fet-delay",
+					   &val32);
+		if (ret >= 0) {
+			val32 |= CS35L35_VALID_PDATA;
+			classh_config->classh_wk_fet_delay = val32;
+		}
+
+		ret = of_property_read_u32(classh, "cirrus,classh-wk-fet-thld",
+					   &val32);
+		if (ret >= 0)
+			classh_config->classh_wk_fet_thld = val32;
+
+		ret = of_property_read_u32(classh, "cirrus,classh-vpch-auto",
+					   &val32);
+		if (ret >= 0) {
+			val32 |= CS35L35_VALID_PDATA;
+			classh_config->classh_vpch_auto = val32;
+		}
+
+		ret = of_property_read_u32(classh, "cirrus,classh-vpch-rate",
+					   &val32);
+		if (ret >= 0) {
+			val32 |= CS35L35_VALID_PDATA;
+			classh_config->classh_vpch_rate = val32;
+		}
+
+		ret = of_property_read_u32(classh, "cirrus,classh-vpch-man",
+					   &val32);
+		if (ret >= 0)
+			classh_config->classh_vpch_man = val32;
+	}
+	of_node_put(classh);
+
+	/* frame depth location */
+	signal_format = of_get_child_by_name(np, "cirrus,monitor-signal-format");
+	monitor_config->is_present = signal_format ? true : false;
+	if (monitor_config->is_present) {
+		ret = of_property_read_u8_array(signal_format, "cirrus,imon",
+						monitor_array, imon_array_size);
+		if (!ret) {
+			monitor_config->imon_specs = true;
+			monitor_config->imon_dpth = monitor_array[0];
+			monitor_config->imon_loc = monitor_array[1];
+			monitor_config->imon_frm = monitor_array[2];
+			monitor_config->imon_scale = monitor_array[3];
+		}
+		ret = of_property_read_u8_array(signal_format, "cirrus,vmon",
+						monitor_array, mon_array_size);
+		if (!ret) {
+			monitor_config->vmon_specs = true;
+			monitor_config->vmon_dpth = monitor_array[0];
+			monitor_config->vmon_loc = monitor_array[1];
+			monitor_config->vmon_frm = monitor_array[2];
+		}
+		ret = of_property_read_u8_array(signal_format, "cirrus,vpmon",
+						monitor_array, mon_array_size);
+		if (!ret) {
+			monitor_config->vpmon_specs = true;
+			monitor_config->vpmon_dpth = monitor_array[0];
+			monitor_config->vpmon_loc = monitor_array[1];
+			monitor_config->vpmon_frm = monitor_array[2];
+		}
+		ret = of_property_read_u8_array(signal_format, "cirrus,vbstmon",
+						monitor_array, mon_array_size);
+		if (!ret) {
+			monitor_config->vbstmon_specs = true;
+			monitor_config->vbstmon_dpth = monitor_array[0];
+			monitor_config->vbstmon_loc = monitor_array[1];
+			monitor_config->vbstmon_frm = monitor_array[2];
+		}
+		ret = of_property_read_u8_array(signal_format, "cirrus,vpbrstat",
+						monitor_array, mon_array_size);
+		if (!ret) {
+			monitor_config->vpbrstat_specs = true;
+			monitor_config->vpbrstat_dpth = monitor_array[0];
+			monitor_config->vpbrstat_loc = monitor_array[1];
+			monitor_config->vpbrstat_frm = monitor_array[2];
+		}
+		ret = of_property_read_u8_array(signal_format, "cirrus,zerofill",
+						monitor_array, mon_array_size);
+		if (!ret) {
+			monitor_config->zerofill_specs = true;
+			monitor_config->zerofill_dpth = monitor_array[0];
+			monitor_config->zerofill_loc = monitor_array[1];
+			monitor_config->zerofill_frm = monitor_array[2];
+		}
+	}
+	of_node_put(signal_format);
+
+	return 0;
+}
+
+/* Errata Rev A0 */
+static const struct reg_sequence cs35l35_errata_patch[] = {
+
+	{ 0x7F, 0x99 },
+	{ 0x00, 0x99 },
+	{ 0x52, 0x22 },
+	{ 0x04, 0x14 },
+	{ 0x6D, 0x44 },
+	{ 0x24, 0x10 },
+	{ 0x58, 0xC4 },
+	{ 0x00, 0x98 },
+	{ 0x18, 0x08 },
+	{ 0x00, 0x00 },
+	{ 0x7F, 0x00 },
+};
+
+static int cs35l35_i2c_probe(struct i2c_client *i2c_client,
+			      const struct i2c_device_id *id)
+{
+	struct cs35l35_private *cs35l35;
+	struct device *dev = &i2c_client->dev;
+	struct cs35l35_platform_data *pdata = dev_get_platdata(dev);
+	int i;
+	int ret;
+	unsigned int devid = 0;
+	unsigned int reg;
+
+	cs35l35 = devm_kzalloc(dev, sizeof(struct cs35l35_private), GFP_KERNEL);
+	if (!cs35l35)
+		return -ENOMEM;
+
+	cs35l35->dev = dev;
+
+	i2c_set_clientdata(i2c_client, cs35l35);
+	cs35l35->regmap = devm_regmap_init_i2c(i2c_client, &cs35l35_regmap);
+	if (IS_ERR(cs35l35->regmap)) {
+		ret = PTR_ERR(cs35l35->regmap);
+		dev_err(dev, "regmap_init() failed: %d\n", ret);
+		goto err;
+	}
+
+	for (i = 0; i < ARRAY_SIZE(cs35l35_supplies); i++)
+		cs35l35->supplies[i].supply = cs35l35_supplies[i];
+
+	cs35l35->num_supplies = ARRAY_SIZE(cs35l35_supplies);
+
+	ret = devm_regulator_bulk_get(dev, cs35l35->num_supplies,
+				      cs35l35->supplies);
+	if (ret != 0) {
+		dev_err(dev, "Failed to request core supplies: %d\n", ret);
+		return ret;
+	}
+
+	if (pdata) {
+		cs35l35->pdata = *pdata;
+	} else {
+		pdata = devm_kzalloc(dev, sizeof(struct cs35l35_platform_data),
+				     GFP_KERNEL);
+		if (!pdata)
+			return -ENOMEM;
+		if (i2c_client->dev.of_node) {
+			ret = cs35l35_handle_of_data(i2c_client, pdata);
+			if (ret != 0)
+				return ret;
+
+		}
+		cs35l35->pdata = *pdata;
+	}
+
+	ret = regulator_bulk_enable(cs35l35->num_supplies,
+					cs35l35->supplies);
+	if (ret != 0) {
+		dev_err(dev, "Failed to enable core supplies: %d\n", ret);
+		return ret;
+	}
+
+	/* returning NULL can be valid if in stereo mode */
+	cs35l35->reset_gpio = devm_gpiod_get_optional(dev, "reset",
+						      GPIOD_OUT_LOW);
+	if (IS_ERR(cs35l35->reset_gpio)) {
+		ret = PTR_ERR(cs35l35->reset_gpio);
+		cs35l35->reset_gpio = NULL;
+		if (ret == -EBUSY) {
+			dev_info(dev,
+				 "Reset line busy, assuming shared reset\n");
+		} else {
+			dev_err(dev, "Failed to get reset GPIO: %d\n", ret);
+			goto err;
+		}
+	}
+
+	gpiod_set_value_cansleep(cs35l35->reset_gpio, 1);
+
+	init_completion(&cs35l35->pdn_done);
+
+	ret = devm_request_threaded_irq(dev, i2c_client->irq, NULL, cs35l35_irq,
+					IRQF_ONESHOT | IRQF_TRIGGER_LOW |
+					IRQF_SHARED, "cs35l35", cs35l35);
+	if (ret != 0) {
+		dev_err(dev, "Failed to request IRQ: %d\n", ret);
+		goto err;
+	}
+	/* initialize codec */
+	ret = regmap_read(cs35l35->regmap, CS35L35_DEVID_AB, &reg);
+
+	devid = (reg & 0xFF) << 12;
+	ret = regmap_read(cs35l35->regmap, CS35L35_DEVID_CD, &reg);
+	devid |= (reg & 0xFF) << 4;
+	ret = regmap_read(cs35l35->regmap, CS35L35_DEVID_E, &reg);
+	devid |= (reg & 0xF0) >> 4;
+
+	if (devid != CS35L35_CHIP_ID) {
+		dev_err(dev, "CS35L35 Device ID (%X). Expected ID %X\n",
+			devid, CS35L35_CHIP_ID);
+		ret = -ENODEV;
+		goto err;
+	}
+
+	ret = regmap_read(cs35l35->regmap, CS35L35_REV_ID, &reg);
+	if (ret < 0) {
+		dev_err(dev, "Get Revision ID failed: %d\n", ret);
+		goto err;
+	}
+
+	ret = regmap_register_patch(cs35l35->regmap, cs35l35_errata_patch,
+				    ARRAY_SIZE(cs35l35_errata_patch));
+	if (ret < 0) {
+		dev_err(dev, "Failed to apply errata patch: %d\n", ret);
+		goto err;
+	}
+
+	dev_info(dev, "Cirrus Logic CS35L35 (%x), Revision: %02X\n",
+		 devid, reg & 0xFF);
+
+	/* Set the INT Masks for critical errors */
+	regmap_write(cs35l35->regmap, CS35L35_INT_MASK_1,
+				CS35L35_INT1_CRIT_MASK);
+	regmap_write(cs35l35->regmap, CS35L35_INT_MASK_2,
+				CS35L35_INT2_CRIT_MASK);
+	regmap_write(cs35l35->regmap, CS35L35_INT_MASK_3,
+				CS35L35_INT3_CRIT_MASK);
+	regmap_write(cs35l35->regmap, CS35L35_INT_MASK_4,
+				CS35L35_INT4_CRIT_MASK);
+
+	regmap_update_bits(cs35l35->regmap, CS35L35_PWRCTL2,
+			CS35L35_PWR2_PDN_MASK,
+			CS35L35_PWR2_PDN_MASK);
+
+	if (cs35l35->pdata.bst_pdn_fet_on)
+		regmap_update_bits(cs35l35->regmap, CS35L35_PWRCTL2,
+					CS35L35_PDN_BST_MASK,
+					1 << CS35L35_PDN_BST_FETON_SHIFT);
+	else
+		regmap_update_bits(cs35l35->regmap, CS35L35_PWRCTL2,
+					CS35L35_PDN_BST_MASK,
+					1 << CS35L35_PDN_BST_FETOFF_SHIFT);
+
+	regmap_update_bits(cs35l35->regmap, CS35L35_PWRCTL3,
+			CS35L35_PWR3_PDN_MASK,
+			CS35L35_PWR3_PDN_MASK);
+
+	regmap_update_bits(cs35l35->regmap, CS35L35_PROTECT_CTL,
+		CS35L35_AMP_MUTE_MASK, 1 << CS35L35_AMP_MUTE_SHIFT);
+
+	ret =  snd_soc_register_codec(dev, &soc_codec_dev_cs35l35, cs35l35_dai,
+				      ARRAY_SIZE(cs35l35_dai));
+	if (ret < 0) {
+		dev_err(dev, "Failed to register codec: %d\n", ret);
+		goto err;
+	}
+
+	return 0;
+
+err:
+	regulator_bulk_disable(cs35l35->num_supplies,
+			       cs35l35->supplies);
+	gpiod_set_value_cansleep(cs35l35->reset_gpio, 0);
+
+	return ret;
+}
+
+static int cs35l35_i2c_remove(struct i2c_client *client)
+{
+	snd_soc_unregister_codec(&client->dev);
+	return 0;
+}
+
+static const struct of_device_id cs35l35_of_match[] = {
+	{.compatible = "cirrus,cs35l35"},
+	{},
+};
+MODULE_DEVICE_TABLE(of, cs35l35_of_match);
+
+static const struct i2c_device_id cs35l35_id[] = {
+	{"cs35l35", 0},
+	{}
+};
+
+MODULE_DEVICE_TABLE(i2c, cs35l35_id);
+
+static struct i2c_driver cs35l35_i2c_driver = {
+	.driver = {
+		.name = "cs35l35",
+		.of_match_table = cs35l35_of_match,
+	},
+	.id_table = cs35l35_id,
+	.probe = cs35l35_i2c_probe,
+	.remove = cs35l35_i2c_remove,
+};
+
+module_i2c_driver(cs35l35_i2c_driver);
+
+MODULE_DESCRIPTION("ASoC CS35L35 driver");
+MODULE_AUTHOR("Brian Austin, Cirrus Logic Inc, <brian.austin@cirrus.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/cs35l35.h b/sound/soc/codecs/cs35l35.h
new file mode 100644
index 000000000000..5a6e43a87c4d
--- /dev/null
+++ b/sound/soc/codecs/cs35l35.h
@@ -0,0 +1,294 @@
+/*
+ * cs35l35.h -- CS35L35 ALSA SoC audio driver
+ *
+ * Copyright 2016 Cirrus Logic, Inc.
+ *
+ * Author: Brian Austin <brian.austin@cirrus.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 __CS35L35_H__
+#define __CS35L35_H__
+
+#define CS35L35_FIRSTREG		0x01
+#define CS35L35_LASTREG			0x7E
+#define CS35L35_CHIP_ID			0x00035A35
+#define CS35L35_DEVID_AB		0x01	/* Device ID A & B [RO] */
+#define CS35L35_DEVID_CD		0x02    /* Device ID C & D [RO] */
+#define CS35L35_DEVID_E			0x03    /* Device ID E [RO] */
+#define CS35L35_FAB_ID			0x04	/* Fab ID [RO] */
+#define CS35L35_REV_ID			0x05	/* Revision ID [RO] */
+#define CS35L35_PWRCTL1			0x06    /* Power Ctl 1 */
+#define CS35L35_PWRCTL2			0x07    /* Power Ctl 2 */
+#define CS35L35_PWRCTL3			0x08	/* Power Ctl 3 */
+#define CS35L35_CLK_CTL1		0x0A	/* Clocking Ctl 1 */
+#define CS35L35_CLK_CTL2		0x0B	/* Clocking Ctl 2 */
+#define CS35L35_CLK_CTL3		0x0C	/* Clocking Ctl 3 */
+#define CS35L35_SP_FMT_CTL1		0x0D	/* Serial Port Format CTL1 */
+#define CS35L35_SP_FMT_CTL2		0x0E	/* Serial Port Format CTL2 */
+#define CS35L35_SP_FMT_CTL3		0x0F	/* Serial Port Format CTL3 */
+#define CS35L35_MAG_COMP_CTL		0x13	/* Magnitude Comp CTL */
+#define CS35L35_AMP_INP_DRV_CTL		0x14	/* Amp Input Drive Ctl */
+#define CS35L35_AMP_DIG_VOL_CTL		0x15	/* Amplifier Dig Volume Ctl */
+#define CS35L35_AMP_DIG_VOL		0x16	/* Amplifier Dig Volume */
+#define CS35L35_ADV_DIG_VOL		0x17	/* Advisory Digital Volume */
+#define CS35L35_PROTECT_CTL		0x18	/* Amp Gain - Prot Ctl Param */
+#define CS35L35_AMP_GAIN_AUD_CTL	0x19	/* Amp Serial Port Gain Ctl */
+#define CS35L35_AMP_GAIN_PDM_CTL	0x1A	/* Amplifier Gain PDM Ctl */
+#define CS35L35_AMP_GAIN_ADV_CTL	0x1B	/* Amplifier Gain Ctl */
+#define CS35L35_GPI_CTL			0x1C	/* GPI Ctl */
+#define CS35L35_BST_CVTR_V_CTL		0x1D	/* Boost Conv Voltage Ctl */
+#define CS35L35_BST_PEAK_I		0x1E	/* Boost Conv Peak Current */
+#define CS35L35_BST_RAMP_CTL		0x20	/* Boost Conv Soft Ramp Ctl */
+#define CS35L35_BST_CONV_COEF_1		0x21	/* Boost Conv Coefficients 1 */
+#define CS35L35_BST_CONV_COEF_2		0x22	/* Boost Conv Coefficients 2 */
+#define CS35L35_BST_CONV_SLOPE_COMP	0x23	/* Boost Conv Slope Comp */
+#define CS35L35_BST_CONV_SW_FREQ	0x24	/* Boost Conv L BST SW Freq */
+#define CS35L35_CLASS_H_CTL		0x30	/* CLS H Control */
+#define CS35L35_CLASS_H_HEADRM_CTL	0x31	/* CLS H Headroom Ctl */
+#define CS35L35_CLASS_H_RELEASE_RATE	0x32	/* CLS H Release Rate */
+#define CS35L35_CLASS_H_FET_DRIVE_CTL	0x33	/* CLS H Weak FET Drive Ctl */
+#define CS35L35_CLASS_H_VP_CTL		0x34	/* CLS H VP Ctl */
+#define CS35L35_CLASS_H_STATUS		0x38	/* CLS H Status */
+#define CS35L35_VPBR_CTL		0x3A	/* VPBR Ctl */
+#define CS35L35_VPBR_VOL_CTL		0x3B	/* VPBR Volume Ctl */
+#define CS35L35_VPBR_TIMING_CTL		0x3C	/* VPBR Timing Ctl */
+#define CS35L35_VPBR_MODE_VOL_CTL	0x3D	/* VPBR Mode/Attack Vol Ctl */
+#define CS35L35_VPBR_ATTEN_STATUS	0x4B	/* VPBR Attenuation Status */
+#define CS35L35_SPKR_MON_CTL		0x4E	/* Speaker Monitoring Ctl */
+#define CS35L35_IMON_SCALE_CTL		0x51	/* IMON Scale Ctl */
+#define CS35L35_AUDIN_RXLOC_CTL		0x52	/* Audio Input RX Loc Ctl */
+#define CS35L35_ADVIN_RXLOC_CTL		0x53	/* Advisory Input RX Loc Ctl */
+#define CS35L35_VMON_TXLOC_CTL		0x54	/* VMON TX Loc Ctl */
+#define CS35L35_IMON_TXLOC_CTL		0x55	/* IMON TX Loc Ctl */
+#define CS35L35_VPMON_TXLOC_CTL		0x56	/* VPMON TX Loc Ctl */
+#define CS35L35_VBSTMON_TXLOC_CTL	0x57	/* VBSTMON TX Loc Ctl */
+#define CS35L35_VPBR_STATUS_TXLOC_CTL	0x58	/* VPBR Status TX Loc Ctl */
+#define CS35L35_ZERO_FILL_LOC_CTL	0x59	/* Zero Fill Loc Ctl */
+#define CS35L35_AUDIN_DEPTH_CTL		0x5A	/* Audio Input Depth Ctl */
+#define CS35L35_SPKMON_DEPTH_CTL	0x5B	/* SPK Mon Output Depth Ctl */
+#define CS35L35_SUPMON_DEPTH_CTL	0x5C	/* Supply Mon Out Depth Ctl */
+#define CS35L35_ZEROFILL_DEPTH_CTL	0x5D	/* Zero Fill Mon Output Ctl */
+#define CS35L35_MULT_DEV_SYNCH1		0x62	/* Multidevice Synch */
+#define CS35L35_MULT_DEV_SYNCH2		0x63	/* Multidevice Synch 2 */
+#define CS35L35_PROT_RELEASE_CTL	0x64	/* Protection Release Ctl */
+#define CS35L35_DIAG_MODE_REG_LOCK	0x68	/* Diagnostic Mode Reg Lock */
+#define CS35L35_DIAG_MODE_CTL_1		0x69	/* Diagnostic Mode Ctl 1 */
+#define CS35L35_DIAG_MODE_CTL_2		0x6A	/* Diagnostic Mode Ctl 2 */
+#define CS35L35_INT_MASK_1		0x70	/* Interrupt Mask 1 */
+#define CS35L35_INT_MASK_2		0x71	/* Interrupt Mask 2 */
+#define CS35L35_INT_MASK_3		0x72	/* Interrupt Mask 3 */
+#define CS35L35_INT_MASK_4		0x73	/* Interrupt Mask 4 */
+#define CS35L35_INT_STATUS_1		0x74	/* Interrupt Status 1 */
+#define CS35L35_INT_STATUS_2		0x75	/* Interrupt Status 2 */
+#define CS35L35_INT_STATUS_3		0x76	/* Interrupt Status 3 */
+#define CS35L35_INT_STATUS_4		0x77	/* Interrupt Status 4 */
+#define CS35L35_PLL_STATUS		0x78	/* PLL Status */
+#define CS35L35_OTP_TRIM_STATUS		0x7E	/* OTP Trim Status */
+
+#define CS35L35_MAX_REGISTER		0x7F
+
+/* CS35L35_PWRCTL1 */
+#define CS35L35_SFT_RST			0x80
+#define CS35L35_DISCHG_FLT		0x02
+#define CS35L35_PDN_ALL			0x01
+
+/* CS35L35_PWRCTL2 */
+#define CS35L35_PDN_VMON		0x80
+#define CS35L35_PDN_IMON		0x40
+#define CS35L35_PDN_CLASSH		0x20
+#define CS35L35_PDN_VPBR		0x10
+#define CS35L35_PDN_BST			0x04
+#define CS35L35_PDN_AMP			0x01
+
+/* CS35L35_PWRCTL3 */
+#define CS35L35_PDN_VBSTMON_OUT		0x10
+#define CS35L35_PDN_VMON_OUT		0x08
+
+#define CS35L35_AUDIN_DEPTH_MASK	0x03
+#define CS35L35_AUDIN_DEPTH_SHIFT	0
+#define CS35L35_ADVIN_DEPTH_MASK	0x0C
+#define CS35L35_ADVIN_DEPTH_SHIFT	2
+#define CS35L35_SDIN_DEPTH_8		0x01
+#define CS35L35_SDIN_DEPTH_16		0x02
+#define CS35L35_SDIN_DEPTH_24		0x03
+
+#define CS35L35_SDOUT_DEPTH_8		0x01
+#define CS35L35_SDOUT_DEPTH_12		0x02
+#define CS35L35_SDOUT_DEPTH_16		0x03
+
+#define CS35L35_AUD_IN_LR_MASK		0x80
+#define CS35L35_AUD_IN_LR_SHIFT		7
+#define CS35L35_ADV_IN_LR_MASK		0x80
+#define CS35L35_ADV_IN_LR_SHIFT		7
+#define CS35L35_AUD_IN_LOC_MASK		0x0F
+#define CS35L35_AUD_IN_LOC_SHIFT	0
+#define CS35L35_ADV_IN_LOC_MASK		0x0F
+#define CS35L35_ADV_IN_LOC_SHIFT	0
+
+#define CS35L35_IMON_DEPTH_MASK		0x03
+#define CS35L35_IMON_DEPTH_SHIFT	0
+#define CS35L35_VMON_DEPTH_MASK		0x0C
+#define CS35L35_VMON_DEPTH_SHIFT	2
+#define CS35L35_VBSTMON_DEPTH_MASK	0x03
+#define CS35L35_VBSTMON_DEPTH_SHIFT	0
+#define CS35L35_VPMON_DEPTH_MASK	0x0C
+#define CS35L35_VPMON_DEPTH_SHIFT	2
+#define CS35L35_VPBRSTAT_DEPTH_MASK	0x30
+#define CS35L35_VPBRSTAT_DEPTH_SHIFT	4
+#define CS35L35_ZEROFILL_DEPTH_MASK	0x03
+#define CS35L35_ZEROFILL_DEPTH_SHIFT	0x00
+
+#define CS35L35_MON_TXLOC_MASK		0x3F
+#define CS35L35_MON_TXLOC_SHIFT		0
+#define CS35L35_MON_FRM_MASK		0x80
+#define CS35L35_MON_FRM_SHIFT		7
+
+#define CS35L35_IMON_SCALE_MASK		0xF8
+#define CS35L35_IMON_SCALE_SHIFT	3
+
+#define CS35L35_MS_MASK			0x80
+#define CS35L35_MS_SHIFT		7
+#define CS35L35_SPMODE_MASK		0x40
+#define CS35L35_SP_DRV_MASK		0x10
+#define CS35L35_SP_DRV_SHIFT		4
+#define CS35L35_CLK_CTL2_MASK		0xFF
+#define CS35L35_PDM_MODE_MASK		0x40
+#define CS35L35_PDM_MODE_SHIFT		6
+#define CS35L35_CLK_SOURCE_MASK		0x03
+#define CS35L35_CLK_SOURCE_SHIFT	0
+#define CS35L35_CLK_SOURCE_MCLK		0
+#define CS35L35_CLK_SOURCE_SCLK		1
+#define CS35L35_CLK_SOURCE_PDM		2
+
+#define CS35L35_SP_SCLKS_MASK		0x0F
+#define CS35L35_SP_SCLKS_SHIFT		0x00
+#define CS35L35_SP_SCLKS_16FS		0x03
+#define CS35L35_SP_SCLKS_32FS		0x07
+#define CS35L35_SP_SCLKS_48FS		0x0B
+#define CS35L35_SP_SCLKS_64FS		0x0F
+#define CS35L35_SP_RATE_MASK		0xC0
+
+#define CS35L35_PDN_BST_MASK		0x06
+#define CS35L35_PDN_BST_FETON_SHIFT	1
+#define CS35L35_PDN_BST_FETOFF_SHIFT	2
+#define CS35L35_PWR2_PDN_MASK		0xE0
+#define CS35L35_PWR3_PDN_MASK		0x1E
+#define CS35L35_PDN_ALL_MASK		0x01
+#define CS35L35_DISCHG_FILT_MASK	0x02
+#define CS35L35_DISCHG_FILT_SHIFT	1
+#define CS35L35_MCLK_DIS_MASK		0x04
+#define CS35L35_MCLK_DIS_SHIFT		2
+
+#define CS35L35_BST_CTL_MASK		0x7F
+#define CS35L35_BST_CTL_SHIFT		0
+#define CS35L35_BST_IPK_MASK		0x1F
+#define CS35L35_BST_IPK_SHIFT		0
+#define CS35L35_AMP_MUTE_MASK		0x20
+#define CS35L35_AMP_MUTE_SHIFT		5
+#define CS35L35_AMP_GAIN_ZC_MASK	0x10
+#define CS35L35_AMP_GAIN_ZC_SHIFT	4
+
+#define CS35L35_AMP_DIGSFT_MASK		0x02
+#define CS35L35_AMP_DIGSFT_SHIFT	1
+
+/* CS35L35_SP_FMT_CTL3 */
+#define CS35L35_SP_I2S_DRV_MASK		0x03
+#define CS35L35_SP_I2S_DRV_SHIFT	0
+
+/* Class H Algorithm Control */
+#define CS35L35_CH_STEREO_MASK		0x40
+#define CS35L35_CH_STEREO_SHIFT		6
+#define CS35L35_CH_BST_OVR_MASK		0x04
+#define CS35L35_CH_BST_OVR_SHIFT	2
+#define CS35L35_CH_BST_LIM_MASK		0x08
+#define CS35L35_CH_BST_LIM_SHIFT	3
+#define CS35L35_CH_MEM_DEPTH_MASK	0x01
+#define CS35L35_CH_MEM_DEPTH_SHIFT	0
+#define CS35L35_CH_HDRM_CTL_MASK	0x3F
+#define CS35L35_CH_HDRM_CTL_SHIFT	0
+#define CS35L35_CH_REL_RATE_MASK	0xFF
+#define CS35L35_CH_REL_RATE_SHIFT	0
+#define CS35L35_CH_WKFET_DIS_MASK	0x80
+#define CS35L35_CH_WKFET_DIS_SHIFT	7
+#define CS35L35_CH_WKFET_DEL_MASK	0x70
+#define CS35L35_CH_WKFET_DEL_SHIFT	4
+#define CS35L35_CH_WKFET_THLD_MASK	0x0F
+#define CS35L35_CH_WKFET_THLD_SHIFT	0
+#define CS35L35_CH_VP_AUTO_MASK		0x80
+#define CS35L35_CH_VP_AUTO_SHIFT	7
+#define CS35L35_CH_VP_RATE_MASK		0x60
+#define CS35L35_CH_VP_RATE_SHIFT	5
+#define CS35L35_CH_VP_MAN_MASK		0x1F
+#define CS35L35_CH_VP_MAN_SHIFT		0
+
+/* CS35L35_PROT_RELEASE_CTL */
+#define CS35L35_CAL_ERR_RLS		0x80
+#define CS35L35_SHORT_RLS		0x04
+#define CS35L35_OTW_RLS			0x02
+#define CS35L35_OTE_RLS			0x01
+
+/* INT Mask Registers */
+#define CS35L35_INT1_CRIT_MASK		0x38
+#define CS35L35_INT2_CRIT_MASK		0xEF
+#define CS35L35_INT3_CRIT_MASK		0xEE
+#define CS35L35_INT4_CRIT_MASK		0xFF
+
+/* PDN DONE Masks */
+#define CS35L35_M_PDN_DONE_SHIFT	4
+#define CS35L35_M_PDN_DONE_MASK		0x10
+
+/* CS35L35_INT_1 */
+#define CS35L35_CAL_ERR			0x80
+#define CS35L35_OTP_ERR			0x40
+#define CS35L35_LRCLK_ERR		0x20
+#define CS35L35_SPCLK_ERR		0x10
+#define CS35L35_MCLK_ERR		0x08
+#define CS35L35_AMP_SHORT		0x04
+#define CS35L35_OTW			0x02
+#define CS35L35_OTE			0x01
+
+/* CS35L35_INT_2 */
+#define CS35L35_PDN_DONE		0x10
+#define CS35L35_VPBR_ERR		0x02
+#define CS35L35_VPBR_CLR		0x01
+
+/* CS35L35_INT_3 */
+#define CS35L35_BST_HIGH		0x10
+#define CS35L35_BST_HIGH_FLAG		0x08
+#define CS35L35_BST_IPK_FLAG		0x04
+#define CS35L35_LBST_SHORT		0x01
+
+/* CS35L35_INT_4 */
+#define CS35L35_VMON_OVFL		0x08
+#define CS35L35_IMON_OVFL		0x04
+
+#define CS35L35_FORMATS (SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE | \
+			SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
+
+struct  cs35l35_private {
+	struct device *dev;
+	struct cs35l35_platform_data pdata;
+	struct regmap *regmap;
+	struct regulator_bulk_data supplies[2];
+	int num_supplies;
+	int sysclk;
+	int sclk;
+	bool pdm_mode;
+	bool i2s_mode;
+	bool slave_mode;
+	/* GPIO for /RST */
+	struct gpio_desc *reset_gpio;
+	struct completion pdn_done;
+};
+
+static const char * const cs35l35_supplies[] = {
+	"VA",
+	"VP",
+};
+
+#endif
diff --git a/sound/soc/codecs/cs4271.c b/sound/soc/codecs/cs4271.c
index 8c0f3b89b5bc..e78b5f055f25 100644
--- a/sound/soc/codecs/cs4271.c
+++ b/sound/soc/codecs/cs4271.c
@@ -498,7 +498,7 @@ static int cs4271_reset(struct snd_soc_codec *codec)
 	struct cs4271_private *cs4271 = snd_soc_codec_get_drvdata(codec);
 
 	if (gpio_is_valid(cs4271->gpio_nreset)) {
-		gpio_set_value(cs4271->gpio_nreset, 0);
+		gpio_direction_output(cs4271->gpio_nreset, 0);
 		mdelay(1);
 		gpio_set_value(cs4271->gpio_nreset, 1);
 		mdelay(1);
diff --git a/sound/soc/codecs/cs53l30.c b/sound/soc/codecs/cs53l30.c
index cb47fb595ff4..1e0d5973b758 100644
--- a/sound/soc/codecs/cs53l30.c
+++ b/sound/soc/codecs/cs53l30.c
@@ -1130,6 +1130,7 @@ MODULE_DEVICE_TABLE(i2c, cs53l30_id);
 static struct i2c_driver cs53l30_i2c_driver = {
 	.driver = {
 		.name = "cs53l30",
+		.of_match_table = cs53l30_of_match,
 		.pm = &cs53l30_runtime_pm,
 	},
 	.id_table = cs53l30_id,
diff --git a/sound/soc/codecs/da7213.c b/sound/soc/codecs/da7213.c
index 12da55882c06..6dd7578f0bb8 100644
--- a/sound/soc/codecs/da7213.c
+++ b/sound/soc/codecs/da7213.c
@@ -12,6 +12,7 @@
  * option) any later version.
  */
 
+#include <linux/acpi.h>
 #include <linux/clk.h>
 #include <linux/delay.h>
 #include <linux/i2c.h>
@@ -1528,12 +1529,23 @@ static int da7213_set_bias_level(struct snd_soc_codec *codec,
 	return 0;
 }
 
+#if defined(CONFIG_OF)
 /* DT */
 static const struct of_device_id da7213_of_match[] = {
 	{ .compatible = "dlg,da7213", },
 	{ }
 };
 MODULE_DEVICE_TABLE(of, da7213_of_match);
+#endif
+
+#ifdef CONFIG_ACPI
+static const struct acpi_device_id da7213_acpi_match[] = {
+	{ "DLGS7212", 0},
+	{ "DLGS7213", 0},
+	{ },
+};
+MODULE_DEVICE_TABLE(acpi, da7213_acpi_match);
+#endif
 
 static enum da7213_micbias_voltage
 	da7213_of_micbias_lvl(struct snd_soc_codec *codec, u32 val)
@@ -1844,6 +1856,7 @@ static struct i2c_driver da7213_i2c_driver = {
 	.driver = {
 		.name = "da7213",
 		.of_match_table = of_match_ptr(da7213_of_match),
+		.acpi_match_table = ACPI_PTR(da7213_acpi_match),
 	},
 	.probe		= da7213_i2c_probe,
 	.remove		= da7213_remove,
diff --git a/sound/soc/codecs/dio2125.c b/sound/soc/codecs/dio2125.c
new file mode 100644
index 000000000000..09451cd44f9b
--- /dev/null
+++ b/sound/soc/codecs/dio2125.c
@@ -0,0 +1,120 @@
+/*
+ * Copyright (c) 2017 BayLibre, SAS.
+ * Author: Jerome Brunet <jbrunet@baylibre.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License 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, see <http://www.gnu.org/licenses/>.
+ * The full GNU General Public License is included in this distribution
+ * in the file called COPYING.
+ */
+
+#include <linux/gpio/consumer.h>
+#include <linux/module.h>
+#include <sound/soc.h>
+
+#define DRV_NAME "dio2125"
+
+struct dio2125 {
+	struct gpio_desc *gpiod_enable;
+};
+
+static int drv_event(struct snd_soc_dapm_widget *w,
+		     struct snd_kcontrol *control, int event)
+{
+	struct snd_soc_component *c = snd_soc_dapm_to_component(w->dapm);
+	struct dio2125 *priv = snd_soc_component_get_drvdata(c);
+	int val;
+
+	switch (event) {
+	case SND_SOC_DAPM_POST_PMU:
+		val = 1;
+		break;
+	case SND_SOC_DAPM_PRE_PMD:
+		val = 0;
+		break;
+	default:
+		WARN(1, "Unexpected event");
+		return -EINVAL;
+	}
+
+	gpiod_set_value_cansleep(priv->gpiod_enable, val);
+
+	return 0;
+}
+
+static const struct snd_soc_dapm_widget dio2125_dapm_widgets[] = {
+	SND_SOC_DAPM_INPUT("INL"),
+	SND_SOC_DAPM_INPUT("INR"),
+	SND_SOC_DAPM_OUT_DRV_E("DRV", SND_SOC_NOPM, 0, 0, NULL, 0, drv_event,
+			       (SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD)),
+	SND_SOC_DAPM_OUTPUT("OUTL"),
+	SND_SOC_DAPM_OUTPUT("OUTR"),
+};
+
+static const struct snd_soc_dapm_route dio2125_dapm_routes[] = {
+	{ "DRV", NULL, "INL" },
+	{ "DRV", NULL, "INR" },
+	{ "OUTL", NULL, "DRV" },
+	{ "OUTR", NULL, "DRV" },
+};
+
+static const struct snd_soc_component_driver dio2125_component_driver = {
+	.dapm_widgets		= dio2125_dapm_widgets,
+	.num_dapm_widgets	= ARRAY_SIZE(dio2125_dapm_widgets),
+	.dapm_routes		= dio2125_dapm_routes,
+	.num_dapm_routes	= ARRAY_SIZE(dio2125_dapm_routes),
+};
+
+static int dio2125_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct dio2125 *priv;
+	int err;
+
+	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+	if (priv == NULL)
+		return -ENOMEM;
+	platform_set_drvdata(pdev, priv);
+
+	priv->gpiod_enable = devm_gpiod_get(dev, "enable", GPIOD_OUT_LOW);
+	if (IS_ERR(priv->gpiod_enable)) {
+		err = PTR_ERR(priv->gpiod_enable);
+		if (err != -EPROBE_DEFER)
+			dev_err(dev, "Failed to get 'enable' gpio: %d", err);
+		return err;
+	}
+
+	return devm_snd_soc_register_component(dev, &dio2125_component_driver,
+					       NULL, 0);
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id dio2125_ids[] = {
+	{ .compatible = "dioo,dio2125", },
+	{ }
+};
+MODULE_DEVICE_TABLE(of, dio2125_ids);
+#endif
+
+static struct platform_driver dio2125_driver = {
+	.driver = {
+		.name = DRV_NAME,
+		.of_match_table = of_match_ptr(dio2125_ids),
+	},
+	.probe = dio2125_probe,
+};
+
+module_platform_driver(dio2125_driver);
+
+MODULE_DESCRIPTION("ASoC DIO2125 output driver");
+MODULE_AUTHOR("Jerome Brunet <jbrunet@baylibre.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/es7134.c b/sound/soc/codecs/es7134.c
new file mode 100644
index 000000000000..25ede825d349
--- /dev/null
+++ b/sound/soc/codecs/es7134.c
@@ -0,0 +1,116 @@
+/*
+ * Copyright (c) 2017 BayLibre, SAS.
+ * Author: Jerome Brunet <jbrunet@baylibre.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License 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, see <http://www.gnu.org/licenses/>.
+ * The full GNU General Public License is included in this distribution
+ * in the file called COPYING.
+ */
+
+#include <linux/module.h>
+#include <sound/soc.h>
+
+/*
+ * The everest 7134 is a very simple DA converter with no register
+ */
+
+static int es7134_set_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
+{
+	fmt &= (SND_SOC_DAIFMT_FORMAT_MASK | SND_SOC_DAIFMT_INV_MASK |
+		SND_SOC_DAIFMT_MASTER_MASK);
+
+	if (fmt != (SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
+		    SND_SOC_DAIFMT_CBS_CFS)) {
+		dev_err(codec_dai->dev, "Invalid DAI format\n");
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static const struct snd_soc_dai_ops es7134_dai_ops = {
+	.set_fmt	= es7134_set_fmt,
+};
+
+static struct snd_soc_dai_driver es7134_dai = {
+	.name = "es7134-hifi",
+	.playback = {
+		.stream_name = "Playback",
+		.channels_min = 2,
+		.channels_max = 2,
+		.rates = SNDRV_PCM_RATE_8000_192000,
+		.formats = (SNDRV_PCM_FMTBIT_S16_LE  |
+			    SNDRV_PCM_FMTBIT_S18_3LE |
+			    SNDRV_PCM_FMTBIT_S20_3LE |
+			    SNDRV_PCM_FMTBIT_S24_3LE |
+			    SNDRV_PCM_FMTBIT_S24_LE),
+	},
+	.ops = &es7134_dai_ops,
+};
+
+static const struct snd_soc_dapm_widget es7134_dapm_widgets[] = {
+	SND_SOC_DAPM_OUTPUT("AOUTL"),
+	SND_SOC_DAPM_OUTPUT("AOUTR"),
+	SND_SOC_DAPM_DAC("DAC", "Playback", SND_SOC_NOPM, 0, 0),
+};
+
+static const struct snd_soc_dapm_route es7134_dapm_routes[] = {
+	{ "AOUTL", NULL, "DAC" },
+	{ "AOUTR", NULL, "DAC" },
+};
+
+static struct snd_soc_codec_driver es7134_codec_driver = {
+	.component_driver = {
+		.dapm_widgets		= es7134_dapm_widgets,
+		.num_dapm_widgets	= ARRAY_SIZE(es7134_dapm_widgets),
+		.dapm_routes		= es7134_dapm_routes,
+		.num_dapm_routes	= ARRAY_SIZE(es7134_dapm_routes),
+	},
+};
+
+static int es7134_probe(struct platform_device *pdev)
+{
+	return snd_soc_register_codec(&pdev->dev,
+				      &es7134_codec_driver,
+				      &es7134_dai, 1);
+}
+
+static int es7134_remove(struct platform_device *pdev)
+{
+	snd_soc_unregister_codec(&pdev->dev);
+	return 0;
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id es7134_ids[] = {
+	{ .compatible = "everest,es7134", },
+	{ .compatible = "everest,es7144", },
+	{ }
+};
+MODULE_DEVICE_TABLE(of, es7134_ids);
+#endif
+
+static struct platform_driver es7134_driver = {
+	.driver = {
+		.name = "es7134",
+		.of_match_table = of_match_ptr(es7134_ids),
+	},
+	.probe = es7134_probe,
+	.remove = es7134_remove,
+};
+
+module_platform_driver(es7134_driver);
+
+MODULE_DESCRIPTION("ASoC ES7134 audio codec driver");
+MODULE_AUTHOR("Jerome Brunet <jbrunet@baylibre.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/es8328.c b/sound/soc/codecs/es8328.c
index 3f84fbd071e2..ed7cc42d1ee2 100644
--- a/sound/soc/codecs/es8328.c
+++ b/sound/soc/codecs/es8328.c
@@ -69,14 +69,10 @@ static const char * const supply_names[ES8328_SUPPLY_NUM] = {
 	"HPVDD",
 };
 
-#define ES8328_RATES (SNDRV_PCM_RATE_96000 | \
-		SNDRV_PCM_RATE_48000 | \
-		SNDRV_PCM_RATE_44100 | \
-		SNDRV_PCM_RATE_32000 | \
-		SNDRV_PCM_RATE_22050 | \
-		SNDRV_PCM_RATE_16000 | \
-		SNDRV_PCM_RATE_11025 | \
-		SNDRV_PCM_RATE_8000)
+#define ES8328_RATES (SNDRV_PCM_RATE_192000 | \
+		SNDRV_PCM_RATE_96000 | \
+		SNDRV_PCM_RATE_88200 | \
+		SNDRV_PCM_RATE_8000_48000)
 #define ES8328_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \
 		SNDRV_PCM_FMTBIT_S18_3LE | \
 		SNDRV_PCM_FMTBIT_S20_3LE | \
@@ -91,6 +87,7 @@ struct es8328_priv {
 	int mclkdiv2;
 	const struct snd_pcm_hw_constraint_list *sysclk_constraints;
 	const int *mclk_ratios;
+	bool master;
 	struct regulator_bulk_data supplies[ES8328_SUPPLY_NUM];
 };
 
@@ -469,7 +466,7 @@ static int es8328_startup(struct snd_pcm_substream *substream,
 	struct snd_soc_codec *codec = dai->codec;
 	struct es8328_priv *es8328 = snd_soc_codec_get_drvdata(codec);
 
-	if (es8328->sysclk_constraints)
+	if (es8328->master && es8328->sysclk_constraints)
 		snd_pcm_hw_constraint_list(substream->runtime, 0,
 				SNDRV_PCM_HW_PARAM_RATE,
 				es8328->sysclk_constraints);
@@ -488,27 +485,34 @@ static int es8328_hw_params(struct snd_pcm_substream *substream,
 	int wl;
 	int ratio;
 
-	if (!es8328->sysclk_constraints) {
-		dev_err(codec->dev, "No MCLK configured\n");
-		return -EINVAL;
-	}
-
 	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
 		reg = ES8328_DACCONTROL2;
 	else
 		reg = ES8328_ADCCONTROL5;
 
-	for (i = 0; i < es8328->sysclk_constraints->count; i++)
-		if (es8328->sysclk_constraints->list[i] == params_rate(params))
-			break;
+	if (es8328->master) {
+		if (!es8328->sysclk_constraints) {
+			dev_err(codec->dev, "No MCLK configured\n");
+			return -EINVAL;
+		}
 
-	if (i == es8328->sysclk_constraints->count) {
-		dev_err(codec->dev, "LRCLK %d unsupported with current clock\n",
-			params_rate(params));
-		return -EINVAL;
+		for (i = 0; i < es8328->sysclk_constraints->count; i++)
+			if (es8328->sysclk_constraints->list[i] ==
+			    params_rate(params))
+				break;
+
+		if (i == es8328->sysclk_constraints->count) {
+			dev_err(codec->dev,
+				"LRCLK %d unsupported with current clock\n",
+				params_rate(params));
+			return -EINVAL;
+		}
+		ratio = es8328->mclk_ratios[i];
+	} else {
+		ratio = 0;
+		es8328->mclkdiv2 = 0;
 	}
 
-	ratio = es8328->mclk_ratios[i];
 	snd_soc_update_bits(codec, ES8328_MASTERMODE,
 			ES8328_MASTERMODE_MCLKDIV2,
 			es8328->mclkdiv2 ? ES8328_MASTERMODE_MCLKDIV2 : 0);
@@ -586,6 +590,7 @@ static int es8328_set_dai_fmt(struct snd_soc_dai *codec_dai,
 		unsigned int fmt)
 {
 	struct snd_soc_codec *codec = codec_dai->codec;
+	struct es8328_priv *es8328 = snd_soc_codec_get_drvdata(codec);
 	u8 dac_mode = 0;
 	u8 adc_mode = 0;
 
@@ -595,11 +600,13 @@ static int es8328_set_dai_fmt(struct snd_soc_dai *codec_dai,
 		snd_soc_update_bits(codec, ES8328_MASTERMODE,
 				    ES8328_MASTERMODE_MSC,
 				    ES8328_MASTERMODE_MSC);
+		es8328->master = true;
 		break;
 	case SND_SOC_DAIFMT_CBS_CFS:
 		/* Slave serial port mode */
 		snd_soc_update_bits(codec, ES8328_MASTERMODE,
 				    ES8328_MASTERMODE_MSC, 0);
+		es8328->master = false;
 		break;
 	default:
 		return -EINVAL;
diff --git a/sound/soc/codecs/hdac_hdmi.c b/sound/soc/codecs/hdac_hdmi.c
index fd272a40485b..bc2e74ff3b2d 100644
--- a/sound/soc/codecs/hdac_hdmi.c
+++ b/sound/soc/codecs/hdac_hdmi.c
@@ -469,7 +469,7 @@ static int hdac_hdmi_set_hw_params(struct snd_pcm_substream *substream,
 
 	format = snd_hdac_calc_stream_format(params_rate(hparams),
 			params_channels(hparams), params_format(hparams),
-			24, 0);
+			dai->driver->playback.sig_bits, 0);
 
 	pcm = hdac_hdmi_get_pcm_from_cvt(hdmi, dai_map->cvt);
 	if (!pcm)
@@ -1419,8 +1419,8 @@ static int hdac_hdmi_create_dais(struct hdac_device *hdac,
 		hdmi_dais[i].playback.rate_min = rate_min;
 		hdmi_dais[i].playback.channels_min = 2;
 		hdmi_dais[i].playback.channels_max = 2;
+		hdmi_dais[i].playback.sig_bits = bps;
 		hdmi_dais[i].ops = &hdmi_dai_ops;
-
 		i++;
 	}
 
diff --git a/sound/soc/codecs/max9867.c b/sound/soc/codecs/max9867.c
index 6cdf15ab46de..0247edc9c84e 100644
--- a/sound/soc/codecs/max9867.c
+++ b/sound/soc/codecs/max9867.c
@@ -516,13 +516,13 @@ static const struct i2c_device_id max9867_i2c_id[] = {
 	{ "max9867", 0 },
 	{ }
 };
+MODULE_DEVICE_TABLE(i2c, max9867_i2c_id);
 
 static const struct of_device_id max9867_of_match[] = {
 	{ .compatible = "maxim,max9867", },
 	{ }
 };
-
-MODULE_DEVICE_TABLE(i2c, max9867_i2c_id);
+MODULE_DEVICE_TABLE(of, max9867_of_match);
 
 static const struct dev_pm_ops max9867_pm_ops = {
 	SET_SYSTEM_SLEEP_PM_OPS(max9867_suspend, max9867_resume)
diff --git a/sound/soc/codecs/max98927.c b/sound/soc/codecs/max98927.c
new file mode 100644
index 000000000000..b5ee29499e16
--- /dev/null
+++ b/sound/soc/codecs/max98927.c
@@ -0,0 +1,841 @@
+/*
+ * max98927.c  --  MAX98927 ALSA Soc Audio driver
+ *
+ * Copyright (C) 2016 Maxim Integrated Products
+ * Author: Ryan Lee <ryans.lee@maximintegrated.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/acpi.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <linux/cdev.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <linux/gpio.h>
+#include <linux/of_gpio.h>
+#include <sound/tlv.h>
+#include "max98927.h"
+
+static struct reg_default max98927_reg[] = {
+	{MAX98927_R0001_INT_RAW1,  0x00},
+	{MAX98927_R0002_INT_RAW2,  0x00},
+	{MAX98927_R0003_INT_RAW3,  0x00},
+	{MAX98927_R0004_INT_STATE1,  0x00},
+	{MAX98927_R0005_INT_STATE2,  0x00},
+	{MAX98927_R0006_INT_STATE3,  0x00},
+	{MAX98927_R0007_INT_FLAG1,  0x00},
+	{MAX98927_R0008_INT_FLAG2,  0x00},
+	{MAX98927_R0009_INT_FLAG3,  0x00},
+	{MAX98927_R000A_INT_EN1,  0x00},
+	{MAX98927_R000B_INT_EN2,  0x00},
+	{MAX98927_R000C_INT_EN3,  0x00},
+	{MAX98927_R000D_INT_FLAG_CLR1,  0x00},
+	{MAX98927_R000E_INT_FLAG_CLR2,  0x00},
+	{MAX98927_R000F_INT_FLAG_CLR3,  0x00},
+	{MAX98927_R0010_IRQ_CTRL,  0x00},
+	{MAX98927_R0011_CLK_MON,  0x00},
+	{MAX98927_R0012_WDOG_CTRL,  0x00},
+	{MAX98927_R0013_WDOG_RST,  0x00},
+	{MAX98927_R0014_MEAS_ADC_THERM_WARN_THRESH,  0x00},
+	{MAX98927_R0015_MEAS_ADC_THERM_SHDN_THRESH,  0x00},
+	{MAX98927_R0016_MEAS_ADC_THERM_HYSTERESIS,  0x00},
+	{MAX98927_R0017_PIN_CFG,  0x55},
+	{MAX98927_R0018_PCM_RX_EN_A,  0x00},
+	{MAX98927_R0019_PCM_RX_EN_B,  0x00},
+	{MAX98927_R001A_PCM_TX_EN_A,  0x00},
+	{MAX98927_R001B_PCM_TX_EN_B,  0x00},
+	{MAX98927_R001C_PCM_TX_HIZ_CTRL_A,  0x00},
+	{MAX98927_R001D_PCM_TX_HIZ_CTRL_B,  0x00},
+	{MAX98927_R001E_PCM_TX_CH_SRC_A,  0x00},
+	{MAX98927_R001F_PCM_TX_CH_SRC_B,  0x00},
+	{MAX98927_R0020_PCM_MODE_CFG,  0x40},
+	{MAX98927_R0021_PCM_MASTER_MODE,  0x00},
+	{MAX98927_R0022_PCM_CLK_SETUP,  0x22},
+	{MAX98927_R0023_PCM_SR_SETUP1,  0x00},
+	{MAX98927_R0024_PCM_SR_SETUP2,  0x00},
+	{MAX98927_R0025_PCM_TO_SPK_MONOMIX_A,  0x00},
+	{MAX98927_R0026_PCM_TO_SPK_MONOMIX_B,  0x00},
+	{MAX98927_R0027_ICC_RX_EN_A,  0x00},
+	{MAX98927_R0028_ICC_RX_EN_B,  0x00},
+	{MAX98927_R002B_ICC_TX_EN_A,  0x00},
+	{MAX98927_R002C_ICC_TX_EN_B,  0x00},
+	{MAX98927_R002E_ICC_HIZ_MANUAL_MODE,  0x00},
+	{MAX98927_R002F_ICC_TX_HIZ_EN_A,  0x00},
+	{MAX98927_R0030_ICC_TX_HIZ_EN_B,  0x00},
+	{MAX98927_R0031_ICC_LNK_EN,  0x00},
+	{MAX98927_R0032_PDM_TX_EN,  0x00},
+	{MAX98927_R0033_PDM_TX_HIZ_CTRL,  0x00},
+	{MAX98927_R0034_PDM_TX_CTRL,  0x00},
+	{MAX98927_R0035_PDM_RX_CTRL,  0x00},
+	{MAX98927_R0036_AMP_VOL_CTRL,  0x00},
+	{MAX98927_R0037_AMP_DSP_CFG,  0x02},
+	{MAX98927_R0038_TONE_GEN_DC_CFG,  0x00},
+	{MAX98927_R0039_DRE_CTRL,  0x01},
+	{MAX98927_R003A_AMP_EN,  0x00},
+	{MAX98927_R003B_SPK_SRC_SEL,  0x00},
+	{MAX98927_R003C_SPK_GAIN,  0x00},
+	{MAX98927_R003D_SSM_CFG,  0x01},
+	{MAX98927_R003E_MEAS_EN,  0x00},
+	{MAX98927_R003F_MEAS_DSP_CFG,  0x04},
+	{MAX98927_R0040_BOOST_CTRL0,  0x00},
+	{MAX98927_R0041_BOOST_CTRL3,  0x00},
+	{MAX98927_R0042_BOOST_CTRL1,  0x00},
+	{MAX98927_R0043_MEAS_ADC_CFG,  0x00},
+	{MAX98927_R0044_MEAS_ADC_BASE_MSB,  0x00},
+	{MAX98927_R0045_MEAS_ADC_BASE_LSB,  0x00},
+	{MAX98927_R0046_ADC_CH0_DIVIDE,  0x00},
+	{MAX98927_R0047_ADC_CH1_DIVIDE,  0x00},
+	{MAX98927_R0048_ADC_CH2_DIVIDE,  0x00},
+	{MAX98927_R0049_ADC_CH0_FILT_CFG,  0x00},
+	{MAX98927_R004A_ADC_CH1_FILT_CFG,  0x00},
+	{MAX98927_R004B_ADC_CH2_FILT_CFG,  0x00},
+	{MAX98927_R004C_MEAS_ADC_CH0_READ,  0x00},
+	{MAX98927_R004D_MEAS_ADC_CH1_READ,  0x00},
+	{MAX98927_R004E_MEAS_ADC_CH2_READ,  0x00},
+	{MAX98927_R0051_BROWNOUT_STATUS,  0x00},
+	{MAX98927_R0052_BROWNOUT_EN,  0x00},
+	{MAX98927_R0053_BROWNOUT_INFINITE_HOLD,  0x00},
+	{MAX98927_R0054_BROWNOUT_INFINITE_HOLD_CLR,  0x00},
+	{MAX98927_R0055_BROWNOUT_LVL_HOLD,  0x00},
+	{MAX98927_R005A_BROWNOUT_LVL1_THRESH,  0x00},
+	{MAX98927_R005B_BROWNOUT_LVL2_THRESH,  0x00},
+	{MAX98927_R005C_BROWNOUT_LVL3_THRESH,  0x00},
+	{MAX98927_R005D_BROWNOUT_LVL4_THRESH,  0x00},
+	{MAX98927_R005E_BROWNOUT_THRESH_HYSTERYSIS,  0x00},
+	{MAX98927_R005F_BROWNOUT_AMP_LIMITER_ATK_REL,  0x00},
+	{MAX98927_R0060_BROWNOUT_AMP_GAIN_ATK_REL,  0x00},
+	{MAX98927_R0061_BROWNOUT_AMP1_CLIP_MODE,  0x00},
+	{MAX98927_R0072_BROWNOUT_LVL1_CUR_LIMIT,  0x00},
+	{MAX98927_R0073_BROWNOUT_LVL1_AMP1_CTRL1,  0x00},
+	{MAX98927_R0074_BROWNOUT_LVL1_AMP1_CTRL2,  0x00},
+	{MAX98927_R0075_BROWNOUT_LVL1_AMP1_CTRL3,  0x00},
+	{MAX98927_R0076_BROWNOUT_LVL2_CUR_LIMIT,  0x00},
+	{MAX98927_R0077_BROWNOUT_LVL2_AMP1_CTRL1,  0x00},
+	{MAX98927_R0078_BROWNOUT_LVL2_AMP1_CTRL2,  0x00},
+	{MAX98927_R0079_BROWNOUT_LVL2_AMP1_CTRL3,  0x00},
+	{MAX98927_R007A_BROWNOUT_LVL3_CUR_LIMIT,  0x00},
+	{MAX98927_R007B_BROWNOUT_LVL3_AMP1_CTRL1,  0x00},
+	{MAX98927_R007C_BROWNOUT_LVL3_AMP1_CTRL2,  0x00},
+	{MAX98927_R007D_BROWNOUT_LVL3_AMP1_CTRL3,  0x00},
+	{MAX98927_R007E_BROWNOUT_LVL4_CUR_LIMIT,  0x00},
+	{MAX98927_R007F_BROWNOUT_LVL4_AMP1_CTRL1,  0x00},
+	{MAX98927_R0080_BROWNOUT_LVL4_AMP1_CTRL2,  0x00},
+	{MAX98927_R0081_BROWNOUT_LVL4_AMP1_CTRL3,  0x00},
+	{MAX98927_R0082_ENV_TRACK_VOUT_HEADROOM,  0x00},
+	{MAX98927_R0083_ENV_TRACK_BOOST_VOUT_DELAY,  0x00},
+	{MAX98927_R0084_ENV_TRACK_REL_RATE,  0x00},
+	{MAX98927_R0085_ENV_TRACK_HOLD_RATE,  0x00},
+	{MAX98927_R0086_ENV_TRACK_CTRL,  0x00},
+	{MAX98927_R0087_ENV_TRACK_BOOST_VOUT_READ,  0x00},
+	{MAX98927_R00FF_GLOBAL_SHDN,  0x00},
+	{MAX98927_R0100_SOFT_RESET,  0x00},
+	{MAX98927_R01FF_REV_ID,  0x40},
+};
+
+static int max98927_dai_set_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
+{
+	struct snd_soc_codec *codec = codec_dai->codec;
+	struct max98927_priv *max98927 = snd_soc_codec_get_drvdata(codec);
+	unsigned int mode = 0;
+	unsigned int format = 0;
+	unsigned int invert = 0;
+
+	dev_dbg(codec->dev, "%s: fmt 0x%08X\n", __func__, fmt);
+
+	switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+	case SND_SOC_DAIFMT_CBS_CFS:
+		mode = MAX98927_PCM_MASTER_MODE_SLAVE;
+		break;
+	case SND_SOC_DAIFMT_CBM_CFM:
+		max98927->master = true;
+		mode = MAX98927_PCM_MASTER_MODE_MASTER;
+		break;
+	default:
+		dev_err(codec->dev, "DAI clock mode unsupported");
+		return -EINVAL;
+	}
+
+	regmap_update_bits(max98927->regmap,
+		MAX98927_R0021_PCM_MASTER_MODE,
+		MAX98927_PCM_MASTER_MODE_MASK,
+		mode);
+
+	switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+	case SND_SOC_DAIFMT_NB_NF:
+		break;
+	case SND_SOC_DAIFMT_IB_NF:
+		invert = MAX98927_PCM_MODE_CFG_PCM_BCLKEDGE;
+		break;
+	default:
+		dev_err(codec->dev, "DAI invert mode unsupported");
+		return -EINVAL;
+	}
+
+	regmap_update_bits(max98927->regmap,
+		MAX98927_R0020_PCM_MODE_CFG,
+		MAX98927_PCM_MODE_CFG_PCM_BCLKEDGE,
+		invert);
+
+	/* interface format */
+	switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+	case SND_SOC_DAIFMT_I2S:
+		max98927->iface |= SND_SOC_DAIFMT_I2S;
+		format = MAX98927_PCM_FORMAT_I2S;
+		break;
+	case SND_SOC_DAIFMT_LEFT_J:
+		max98927->iface |= SND_SOC_DAIFMT_LEFT_J;
+		format = MAX98927_PCM_FORMAT_LJ;
+		break;
+	case SND_SOC_DAIFMT_PDM:
+		max98927->iface |= SND_SOC_DAIFMT_PDM;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	/* pcm channel configuration */
+	if (max98927->iface & (SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_LEFT_J)) {
+		regmap_update_bits(max98927->regmap,
+			MAX98927_R0018_PCM_RX_EN_A,
+			MAX98927_PCM_RX_CH0_EN | MAX98927_PCM_RX_CH1_EN,
+			MAX98927_PCM_RX_CH0_EN | MAX98927_PCM_RX_CH1_EN);
+
+		regmap_update_bits(max98927->regmap,
+			MAX98927_R0020_PCM_MODE_CFG,
+			MAX98927_PCM_MODE_CFG_FORMAT_MASK,
+			format << MAX98927_PCM_MODE_CFG_FORMAT_SHIFT);
+
+		regmap_update_bits(max98927->regmap,
+			MAX98927_R003B_SPK_SRC_SEL,
+			MAX98927_SPK_SRC_MASK, 0);
+
+	} else
+		regmap_update_bits(max98927->regmap,
+			MAX98927_R0018_PCM_RX_EN_A,
+			MAX98927_PCM_RX_CH0_EN | MAX98927_PCM_RX_CH1_EN, 0);
+
+	/* pdm channel configuration */
+	if (max98927->iface & SND_SOC_DAIFMT_PDM) {
+		regmap_update_bits(max98927->regmap,
+			MAX98927_R0035_PDM_RX_CTRL,
+			MAX98927_PDM_RX_EN_MASK, 1);
+
+		regmap_update_bits(max98927->regmap,
+			MAX98927_R003B_SPK_SRC_SEL,
+			MAX98927_SPK_SRC_MASK, 3);
+	} else
+		regmap_update_bits(max98927->regmap,
+			MAX98927_R0035_PDM_RX_CTRL,
+			MAX98927_PDM_RX_EN_MASK, 0);
+	return 0;
+}
+
+/* codec MCLK rate in master mode */
+static const int rate_table[] = {
+	5644800, 6000000, 6144000, 6500000,
+	9600000, 11289600, 12000000, 12288000,
+	13000000, 19200000,
+};
+
+static int max98927_set_clock(struct max98927_priv *max98927,
+	struct snd_pcm_hw_params *params)
+{
+	struct snd_soc_codec *codec = max98927->codec;
+	/* BCLK/LRCLK ratio calculation */
+	int blr_clk_ratio = params_channels(params) * max98927->ch_size;
+	int value;
+
+	if (max98927->master) {
+		int i;
+		/* match rate to closest value */
+		for (i = 0; i < ARRAY_SIZE(rate_table); i++) {
+			if (rate_table[i] >= max98927->sysclk)
+				break;
+		}
+		if (i == ARRAY_SIZE(rate_table)) {
+			dev_err(codec->dev, "failed to find proper clock rate.\n");
+			return -EINVAL;
+		}
+		regmap_update_bits(max98927->regmap,
+			MAX98927_R0021_PCM_MASTER_MODE,
+			MAX98927_PCM_MASTER_MODE_MCLK_MASK,
+			i << MAX98927_PCM_MASTER_MODE_MCLK_RATE_SHIFT);
+	}
+
+	switch (blr_clk_ratio) {
+	case 32:
+		value = 2;
+		break;
+	case 48:
+		value = 3;
+		break;
+	case 64:
+		value = 4;
+		break;
+	default:
+		return -EINVAL;
+	}
+	regmap_update_bits(max98927->regmap,
+		MAX98927_R0022_PCM_CLK_SETUP,
+		MAX98927_PCM_CLK_SETUP_BSEL_MASK,
+		value);
+	return 0;
+}
+
+static int max98927_dai_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 max98927_priv *max98927 = snd_soc_codec_get_drvdata(codec);
+	unsigned int sampling_rate = 0;
+	unsigned int chan_sz = 0;
+
+	/* pcm mode configuration */
+	switch (snd_pcm_format_width(params_format(params))) {
+	case 16:
+		chan_sz = MAX98927_PCM_MODE_CFG_CHANSZ_16;
+		break;
+	case 24:
+		chan_sz = MAX98927_PCM_MODE_CFG_CHANSZ_24;
+		break;
+	case 32:
+		chan_sz = MAX98927_PCM_MODE_CFG_CHANSZ_32;
+		break;
+	default:
+		dev_err(codec->dev, "format unsupported %d",
+			params_format(params));
+		goto err;
+	}
+
+	max98927->ch_size = snd_pcm_format_width(params_format(params));
+
+	regmap_update_bits(max98927->regmap,
+		MAX98927_R0020_PCM_MODE_CFG,
+		MAX98927_PCM_MODE_CFG_CHANSZ_MASK, chan_sz);
+
+	dev_dbg(codec->dev, "format supported %d",
+		params_format(params));
+
+	/* sampling rate configuration */
+	switch (params_rate(params)) {
+	case 8000:
+		sampling_rate = MAX98927_PCM_SR_SET1_SR_8000;
+		break;
+	case 11025:
+		sampling_rate = MAX98927_PCM_SR_SET1_SR_11025;
+		break;
+	case 12000:
+		sampling_rate = MAX98927_PCM_SR_SET1_SR_12000;
+		break;
+	case 16000:
+		sampling_rate = MAX98927_PCM_SR_SET1_SR_16000;
+		break;
+	case 22050:
+		sampling_rate = MAX98927_PCM_SR_SET1_SR_22050;
+		break;
+	case 24000:
+		sampling_rate = MAX98927_PCM_SR_SET1_SR_24000;
+		break;
+	case 32000:
+		sampling_rate = MAX98927_PCM_SR_SET1_SR_32000;
+		break;
+	case 44100:
+		sampling_rate = MAX98927_PCM_SR_SET1_SR_44100;
+		break;
+	case 48000:
+		sampling_rate = MAX98927_PCM_SR_SET1_SR_48000;
+		break;
+	default:
+		dev_err(codec->dev, "rate %d not supported\n",
+			params_rate(params));
+		goto err;
+	}
+	/* set DAI_SR to correct LRCLK frequency */
+	regmap_update_bits(max98927->regmap,
+		MAX98927_R0023_PCM_SR_SETUP1,
+		MAX98927_PCM_SR_SET1_SR_MASK,
+		sampling_rate);
+	regmap_update_bits(max98927->regmap,
+		MAX98927_R0024_PCM_SR_SETUP2,
+		MAX98927_PCM_SR_SET2_SR_MASK,
+		sampling_rate << MAX98927_PCM_SR_SET2_SR_SHIFT);
+
+	/* set sampling rate of IV */
+	if (max98927->interleave_mode &&
+	    sampling_rate > MAX98927_PCM_SR_SET1_SR_16000)
+		regmap_update_bits(max98927->regmap,
+			MAX98927_R0024_PCM_SR_SETUP2,
+			MAX98927_PCM_SR_SET2_IVADC_SR_MASK,
+			sampling_rate - 3);
+	else
+		regmap_update_bits(max98927->regmap,
+			MAX98927_R0024_PCM_SR_SETUP2,
+			MAX98927_PCM_SR_SET2_IVADC_SR_MASK,
+			sampling_rate);
+	return max98927_set_clock(max98927, params);
+err:
+	return -EINVAL;
+}
+
+#define MAX98927_RATES SNDRV_PCM_RATE_8000_48000
+
+#define MAX98927_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \
+	SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
+
+static int max98927_dai_set_sysclk(struct snd_soc_dai *dai,
+	int clk_id, unsigned int freq, int dir)
+{
+	struct snd_soc_codec *codec = dai->codec;
+	struct max98927_priv *max98927 = snd_soc_codec_get_drvdata(codec);
+
+	max98927->sysclk = freq;
+	return 0;
+}
+
+static const struct snd_soc_dai_ops max98927_dai_ops = {
+	.set_sysclk = max98927_dai_set_sysclk,
+	.set_fmt = max98927_dai_set_fmt,
+	.hw_params = max98927_dai_hw_params,
+};
+
+static int max98927_dac_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);
+	struct max98927_priv *max98927 = snd_soc_codec_get_drvdata(codec);
+
+	switch (event) {
+	case SND_SOC_DAPM_POST_PMU:
+		regmap_update_bits(max98927->regmap,
+			MAX98927_R003A_AMP_EN,
+			MAX98927_AMP_EN_MASK, 1);
+		/* enable VMON and IMON */
+		regmap_update_bits(max98927->regmap,
+			MAX98927_R003E_MEAS_EN,
+			MAX98927_MEAS_V_EN | MAX98927_MEAS_I_EN,
+			MAX98927_MEAS_V_EN | MAX98927_MEAS_I_EN);
+		regmap_update_bits(max98927->regmap,
+			MAX98927_R00FF_GLOBAL_SHDN,
+			MAX98927_GLOBAL_EN_MASK, 1);
+		break;
+	case SND_SOC_DAPM_POST_PMD:
+		regmap_update_bits(max98927->regmap,
+			MAX98927_R00FF_GLOBAL_SHDN,
+			MAX98927_GLOBAL_EN_MASK, 0);
+		regmap_update_bits(max98927->regmap,
+			MAX98927_R003A_AMP_EN,
+			MAX98927_AMP_EN_MASK, 0);
+		/* disable VMON and IMON */
+		regmap_update_bits(max98927->regmap,
+			MAX98927_R003E_MEAS_EN,
+			MAX98927_MEAS_V_EN | MAX98927_MEAS_I_EN, 0);
+		break;
+	default:
+		return 0;
+	}
+	return 0;
+}
+
+static const char * const max98927_switch_text[] = {
+	"Left", "Right", "LeftRight"};
+
+static const struct soc_enum dai_sel_enum =
+	SOC_ENUM_SINGLE(MAX98927_R0025_PCM_TO_SPK_MONOMIX_A,
+		MAX98927_PCM_TO_SPK_MONOMIX_CFG_SHIFT,
+		3, max98927_switch_text);
+
+static const struct snd_kcontrol_new max98927_dai_controls =
+	SOC_DAPM_ENUM("DAI Sel", dai_sel_enum);
+
+static const struct snd_soc_dapm_widget max98927_dapm_widgets[] = {
+	SND_SOC_DAPM_AIF_IN("DAI_OUT", "HiFi Playback", 0, SND_SOC_NOPM, 0, 0),
+	SND_SOC_DAPM_DAC_E("Amp Enable", "HiFi Playback", MAX98927_R003A_AMP_EN,
+		0, 0, max98927_dac_event,
+		SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+	SND_SOC_DAPM_MUX("DAI Sel Mux", SND_SOC_NOPM, 0, 0,
+		&max98927_dai_controls),
+	SND_SOC_DAPM_OUTPUT("BE_OUT"),
+};
+
+static DECLARE_TLV_DB_SCALE(max98927_spk_tlv, 300, 300, 0);
+static DECLARE_TLV_DB_SCALE(max98927_digital_tlv, -1600, 25, 0);
+
+static bool max98927_readable_register(struct device *dev, unsigned int reg)
+{
+	switch (reg) {
+	case MAX98927_R0001_INT_RAW1 ... MAX98927_R0028_ICC_RX_EN_B:
+	case MAX98927_R002B_ICC_TX_EN_A ... MAX98927_R002C_ICC_TX_EN_B:
+	case MAX98927_R002E_ICC_HIZ_MANUAL_MODE
+		... MAX98927_R004E_MEAS_ADC_CH2_READ:
+	case MAX98927_R0051_BROWNOUT_STATUS
+		... MAX98927_R0055_BROWNOUT_LVL_HOLD:
+	case MAX98927_R005A_BROWNOUT_LVL1_THRESH
+		... MAX98927_R0061_BROWNOUT_AMP1_CLIP_MODE:
+	case MAX98927_R0072_BROWNOUT_LVL1_CUR_LIMIT
+		... MAX98927_R0087_ENV_TRACK_BOOST_VOUT_READ:
+	case MAX98927_R00FF_GLOBAL_SHDN:
+	case MAX98927_R0100_SOFT_RESET:
+	case MAX98927_R01FF_REV_ID:
+		return true;
+	default:
+		return false;
+	}
+};
+
+static bool max98927_volatile_reg(struct device *dev, unsigned int reg)
+{
+	switch (reg) {
+	case MAX98927_R0001_INT_RAW1 ... MAX98927_R0009_INT_FLAG3:
+		return true;
+	default:
+		return false;
+	}
+}
+
+static const char * const max98927_boost_voltage_text[] = {
+	"6.5V", "6.625V", "6.75V", "6.875V", "7V", "7.125V", "7.25V", "7.375V",
+	"7.5V", "7.625V", "7.75V", "7.875V", "8V", "8.125V", "8.25V", "8.375V",
+	"8.5V", "8.625V", "8.75V", "8.875V", "9V", "9.125V", "9.25V", "9.375V",
+	"9.5V", "9.625V", "9.75V", "9.875V", "10V"
+};
+
+static SOC_ENUM_SINGLE_DECL(max98927_boost_voltage,
+		MAX98927_R0040_BOOST_CTRL0, 0,
+		max98927_boost_voltage_text);
+
+static const char * const max98927_current_limit_text[] = {
+	"1.00A", "1.10A", "1.20A", "1.30A", "1.40A", "1.50A", "1.60A", "1.70A",
+	"1.80A", "1.90A", "2.00A", "2.10A", "2.20A", "2.30A", "2.40A", "2.50A",
+	"2.60A", "2.70A", "2.80A", "2.90A", "3.00A", "3.10A", "3.20A", "3.30A",
+	"3.40A", "3.50A", "3.60A", "3.70A", "3.80A", "3.90A", "4.00A", "4.10A"
+};
+
+static SOC_ENUM_SINGLE_DECL(max98927_current_limit,
+		MAX98927_R0042_BOOST_CTRL1, 1,
+		max98927_current_limit_text);
+
+static const struct snd_kcontrol_new max98927_snd_controls[] = {
+	SOC_SINGLE_TLV("Speaker Volume", MAX98927_R003C_SPK_GAIN,
+		0, 6, 0,
+		max98927_spk_tlv),
+	SOC_SINGLE_TLV("Digital Volume", MAX98927_R0036_AMP_VOL_CTRL,
+		0, (1<<MAX98927_AMP_VOL_WIDTH)-1, 0,
+		max98927_digital_tlv),
+	SOC_SINGLE("Amp DSP Switch", MAX98927_R0052_BROWNOUT_EN,
+		MAX98927_BROWNOUT_DSP_SHIFT, 1, 0),
+	SOC_SINGLE("Ramp Switch", MAX98927_R0037_AMP_DSP_CFG,
+		MAX98927_AMP_DSP_CFG_RMP_SHIFT, 1, 0),
+	SOC_SINGLE("DRE Switch", MAX98927_R0039_DRE_CTRL,
+		MAX98927_DRE_EN_SHIFT, 1, 0),
+	SOC_SINGLE("Volume Location Switch", MAX98927_R0036_AMP_VOL_CTRL,
+		MAX98927_AMP_VOL_SEL_SHIFT, 1, 0),
+	SOC_ENUM("Boost Output Voltage", max98927_boost_voltage),
+	SOC_ENUM("Current Limit", max98927_current_limit),
+};
+
+static const struct snd_soc_dapm_route max98927_audio_map[] = {
+	{"Amp Enable", NULL, "DAI_OUT"},
+	{"DAI Sel Mux", "Left", "Amp Enable"},
+	{"DAI Sel Mux", "Right", "Amp Enable"},
+	{"DAI Sel Mux", "LeftRight", "Amp Enable"},
+	{"BE_OUT", NULL, "DAI Sel Mux"},
+};
+
+static struct snd_soc_dai_driver max98927_dai[] = {
+	{
+		.name = "max98927-aif1",
+		.playback = {
+			.stream_name = "HiFi Playback",
+			.channels_min = 1,
+			.channels_max = 2,
+			.rates = MAX98927_RATES,
+			.formats = MAX98927_FORMATS,
+		},
+		.capture = {
+			.stream_name = "HiFi Capture",
+			.channels_min = 1,
+			.channels_max = 2,
+			.rates = MAX98927_RATES,
+			.formats = MAX98927_FORMATS,
+		},
+		.ops = &max98927_dai_ops,
+	}
+};
+
+static int max98927_probe(struct snd_soc_codec *codec)
+{
+	struct max98927_priv *max98927 = snd_soc_codec_get_drvdata(codec);
+
+	max98927->codec = codec;
+	codec->control_data = max98927->regmap;
+	codec->cache_bypass = 1;
+
+	/* Software Reset */
+	regmap_write(max98927->regmap,
+		MAX98927_R0100_SOFT_RESET, MAX98927_SOFT_RESET);
+
+	/* IV default slot configuration */
+	regmap_write(max98927->regmap,
+		MAX98927_R001C_PCM_TX_HIZ_CTRL_A,
+		0xFF);
+	regmap_write(max98927->regmap,
+		MAX98927_R001D_PCM_TX_HIZ_CTRL_B,
+		0xFF);
+	regmap_write(max98927->regmap,
+		MAX98927_R0025_PCM_TO_SPK_MONOMIX_A,
+		0x80);
+	regmap_write(max98927->regmap,
+		MAX98927_R0026_PCM_TO_SPK_MONOMIX_B,
+		0x1);
+	/* Set inital volume (+13dB) */
+	regmap_write(max98927->regmap,
+		MAX98927_R0036_AMP_VOL_CTRL,
+		0x38);
+	regmap_write(max98927->regmap,
+		MAX98927_R003C_SPK_GAIN,
+		0x05);
+	/* Enable DC blocker */
+	regmap_write(max98927->regmap,
+		MAX98927_R0037_AMP_DSP_CFG,
+		0x03);
+	/* Enable IMON VMON DC blocker */
+	regmap_write(max98927->regmap,
+		MAX98927_R003F_MEAS_DSP_CFG,
+		0xF7);
+	/* Boost Output Voltage & Current limit */
+	regmap_write(max98927->regmap,
+		MAX98927_R0040_BOOST_CTRL0,
+		0x1C);
+	regmap_write(max98927->regmap,
+		MAX98927_R0042_BOOST_CTRL1,
+		0x3E);
+	/* Measurement ADC config */
+	regmap_write(max98927->regmap,
+		MAX98927_R0043_MEAS_ADC_CFG,
+		0x04);
+	regmap_write(max98927->regmap,
+		MAX98927_R0044_MEAS_ADC_BASE_MSB,
+		0x00);
+	regmap_write(max98927->regmap,
+		MAX98927_R0045_MEAS_ADC_BASE_LSB,
+		0x24);
+	/* Brownout Level */
+	regmap_write(max98927->regmap,
+		MAX98927_R007F_BROWNOUT_LVL4_AMP1_CTRL1,
+		0x06);
+	/* Envelope Tracking configuration */
+	regmap_write(max98927->regmap,
+		MAX98927_R0082_ENV_TRACK_VOUT_HEADROOM,
+		0x08);
+	regmap_write(max98927->regmap,
+		MAX98927_R0086_ENV_TRACK_CTRL,
+		0x01);
+	regmap_write(max98927->regmap,
+		MAX98927_R0087_ENV_TRACK_BOOST_VOUT_READ,
+		0x10);
+
+	/* voltage, current slot configuration */
+	regmap_write(max98927->regmap,
+		MAX98927_R001E_PCM_TX_CH_SRC_A,
+		(max98927->i_l_slot<<MAX98927_PCM_TX_CH_SRC_A_I_SHIFT|
+		max98927->v_l_slot)&0xFF);
+
+	if (max98927->v_l_slot < 8) {
+		regmap_update_bits(max98927->regmap,
+			MAX98927_R001C_PCM_TX_HIZ_CTRL_A,
+			1 << max98927->v_l_slot, 0);
+		regmap_update_bits(max98927->regmap,
+			MAX98927_R001A_PCM_TX_EN_A,
+			1 << max98927->v_l_slot,
+			1 << max98927->v_l_slot);
+	} else {
+		regmap_update_bits(max98927->regmap,
+			MAX98927_R001D_PCM_TX_HIZ_CTRL_B,
+			1 << (max98927->v_l_slot - 8), 0);
+		regmap_update_bits(max98927->regmap,
+			MAX98927_R001B_PCM_TX_EN_B,
+			1 << (max98927->v_l_slot - 8),
+			1 << (max98927->v_l_slot - 8));
+	}
+
+	if (max98927->i_l_slot < 8) {
+		regmap_update_bits(max98927->regmap,
+			MAX98927_R001C_PCM_TX_HIZ_CTRL_A,
+			1 << max98927->i_l_slot, 0);
+		regmap_update_bits(max98927->regmap,
+			MAX98927_R001A_PCM_TX_EN_A,
+			1 << max98927->i_l_slot,
+			1 << max98927->i_l_slot);
+	} else {
+		regmap_update_bits(max98927->regmap,
+			MAX98927_R001D_PCM_TX_HIZ_CTRL_B,
+			1 << (max98927->i_l_slot - 8), 0);
+		regmap_update_bits(max98927->regmap,
+			MAX98927_R001B_PCM_TX_EN_B,
+			1 << (max98927->i_l_slot - 8),
+			1 << (max98927->i_l_slot - 8));
+	}
+
+	/* Set interleave mode */
+	if (max98927->interleave_mode)
+		regmap_update_bits(max98927->regmap,
+			MAX98927_R001F_PCM_TX_CH_SRC_B,
+			MAX98927_PCM_TX_CH_INTERLEAVE_MASK,
+			MAX98927_PCM_TX_CH_INTERLEAVE_MASK);
+	return 0;
+}
+
+static const struct snd_soc_codec_driver soc_codec_dev_max98927 = {
+	.probe = max98927_probe,
+	.component_driver = {
+		.controls = max98927_snd_controls,
+		.num_controls = ARRAY_SIZE(max98927_snd_controls),
+		.dapm_widgets = max98927_dapm_widgets,
+		.num_dapm_widgets = ARRAY_SIZE(max98927_dapm_widgets),
+		.dapm_routes = max98927_audio_map,
+		.num_dapm_routes = ARRAY_SIZE(max98927_audio_map),
+	},
+};
+
+static const struct regmap_config max98927_regmap = {
+	.reg_bits         = 16,
+	.val_bits         = 8,
+	.max_register     = MAX98927_R01FF_REV_ID,
+	.reg_defaults     = max98927_reg,
+	.num_reg_defaults = ARRAY_SIZE(max98927_reg),
+	.readable_reg	  = max98927_readable_register,
+	.volatile_reg	  = max98927_volatile_reg,
+	.cache_type       = REGCACHE_RBTREE,
+};
+
+static void max98927_slot_config(struct i2c_client *i2c,
+	struct max98927_priv *max98927)
+{
+	int value;
+
+	if (!of_property_read_u32(i2c->dev.of_node,
+		"vmon-slot-no", &value))
+		max98927->v_l_slot = value & 0xF;
+	else
+		max98927->v_l_slot = 0;
+	if (!of_property_read_u32(i2c->dev.of_node,
+		"imon-slot-no", &value))
+		max98927->i_l_slot = value & 0xF;
+	else
+		max98927->i_l_slot = 1;
+}
+
+static int max98927_i2c_probe(struct i2c_client *i2c,
+	const struct i2c_device_id *id)
+{
+
+	int ret = 0, value;
+	int reg = 0;
+	struct max98927_priv *max98927 = NULL;
+
+	max98927 = devm_kzalloc(&i2c->dev,
+		sizeof(*max98927), GFP_KERNEL);
+
+	if (!max98927) {
+		ret = -ENOMEM;
+		return ret;
+	}
+	i2c_set_clientdata(i2c, max98927);
+
+	/* update interleave mode info */
+	if (!of_property_read_u32(i2c->dev.of_node,
+		"interleave_mode", &value)) {
+		if (value > 0)
+			max98927->interleave_mode = 1;
+		else
+			max98927->interleave_mode = 0;
+	} else
+		max98927->interleave_mode = 0;
+
+	/* regmap initialization */
+	max98927->regmap
+		= devm_regmap_init_i2c(i2c, &max98927_regmap);
+	if (IS_ERR(max98927->regmap)) {
+		ret = PTR_ERR(max98927->regmap);
+		dev_err(&i2c->dev,
+			"Failed to allocate regmap: %d\n", ret);
+		return ret;
+	}
+
+	/* Check Revision ID */
+	ret = regmap_read(max98927->regmap,
+		MAX98927_R01FF_REV_ID, &reg);
+	if (ret < 0) {
+		dev_err(&i2c->dev,
+			"Failed to read: 0x%02X\n", MAX98927_R01FF_REV_ID);
+		return ret;
+	}
+	dev_info(&i2c->dev, "MAX98927 revisionID: 0x%02X\n", reg);
+
+	/* voltage/current slot configuration */
+	max98927_slot_config(i2c, max98927);
+
+	/* codec registeration */
+	ret = snd_soc_register_codec(&i2c->dev, &soc_codec_dev_max98927,
+		max98927_dai, ARRAY_SIZE(max98927_dai));
+	if (ret < 0)
+		dev_err(&i2c->dev, "Failed to register codec: %d\n", ret);
+
+	return ret;
+}
+
+static int max98927_i2c_remove(struct i2c_client *client)
+{
+	snd_soc_unregister_codec(&client->dev);
+	return 0;
+}
+
+static const struct i2c_device_id max98927_i2c_id[] = {
+	{ "max98927", 0},
+	{ },
+};
+
+MODULE_DEVICE_TABLE(i2c, max98927_i2c_id);
+
+#if defined(CONFIG_OF)
+static const struct of_device_id max98927_of_match[] = {
+	{ .compatible = "maxim,max98927", },
+	{ }
+};
+MODULE_DEVICE_TABLE(of, max98927_of_match);
+#endif
+
+#ifdef CONFIG_ACPI
+static const struct acpi_device_id max98927_acpi_match[] = {
+	{ "MX98927", 0 },
+	{},
+};
+MODULE_DEVICE_TABLE(acpi, max98927_acpi_match);
+#endif
+
+static struct i2c_driver max98927_i2c_driver = {
+	.driver = {
+		.name = "max98927",
+		.of_match_table = of_match_ptr(max98927_of_match),
+		.acpi_match_table = ACPI_PTR(max98927_acpi_match),
+		.pm = NULL,
+	},
+	.probe  = max98927_i2c_probe,
+	.remove = max98927_i2c_remove,
+	.id_table = max98927_i2c_id,
+};
+
+module_i2c_driver(max98927_i2c_driver)
+
+MODULE_DESCRIPTION("ALSA SoC MAX98927 driver");
+MODULE_AUTHOR("Ryan Lee <ryans.lee@maximintegrated.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/max98927.h b/sound/soc/codecs/max98927.h
new file mode 100644
index 000000000000..ece6a608cbe1
--- /dev/null
+++ b/sound/soc/codecs/max98927.h
@@ -0,0 +1,272 @@
+/*
+ * max98927.h  --  MAX98927 ALSA Soc Audio driver
+ *
+ * Copyright 2013-15 Maxim Integrated Products
+ * Author: Ryan Lee <ryans.lee@maximintegrated.com>
+ *
+ *  This program is free software; you can redistribute  it and/or modify it
+ *  under  the terms of  the GNU General  Public License as published by the
+ *  Free Software Foundation;  either version 2 of the  License, or (at your
+ *  option) any later version.
+ *
+ */
+#ifndef _MAX98927_H
+#define _MAX98927_H
+
+/* Register Values */
+#define MAX98927_R0001_INT_RAW1 0x0001
+#define MAX98927_R0002_INT_RAW2 0x0002
+#define MAX98927_R0003_INT_RAW3 0x0003
+#define MAX98927_R0004_INT_STATE1 0x0004
+#define MAX98927_R0005_INT_STATE2 0x0005
+#define MAX98927_R0006_INT_STATE3 0x0006
+#define MAX98927_R0007_INT_FLAG1 0x0007
+#define MAX98927_R0008_INT_FLAG2 0x0008
+#define MAX98927_R0009_INT_FLAG3 0x0009
+#define MAX98927_R000A_INT_EN1 0x000A
+#define MAX98927_R000B_INT_EN2 0x000B
+#define MAX98927_R000C_INT_EN3 0x000C
+#define MAX98927_R000D_INT_FLAG_CLR1	0x000D
+#define MAX98927_R000E_INT_FLAG_CLR2	0x000E
+#define MAX98927_R000F_INT_FLAG_CLR3	0x000F
+#define MAX98927_R0010_IRQ_CTRL 0x0010
+#define MAX98927_R0011_CLK_MON 0x0011
+#define MAX98927_R0012_WDOG_CTRL 0x0012
+#define MAX98927_R0013_WDOG_RST 0x0013
+#define MAX98927_R0014_MEAS_ADC_THERM_WARN_THRESH 0x0014
+#define MAX98927_R0015_MEAS_ADC_THERM_SHDN_THRESH 0x0015
+#define MAX98927_R0016_MEAS_ADC_THERM_HYSTERESIS 0x0016
+#define MAX98927_R0017_PIN_CFG 0x0017
+#define MAX98927_R0018_PCM_RX_EN_A 0x0018
+#define MAX98927_R0019_PCM_RX_EN_B 0x0019
+#define MAX98927_R001A_PCM_TX_EN_A 0x001A
+#define MAX98927_R001B_PCM_TX_EN_B 0x001B
+#define MAX98927_R001C_PCM_TX_HIZ_CTRL_A 0x001C
+#define MAX98927_R001D_PCM_TX_HIZ_CTRL_B 0x001D
+#define MAX98927_R001E_PCM_TX_CH_SRC_A 0x001E
+#define MAX98927_R001F_PCM_TX_CH_SRC_B 0x001F
+#define MAX98927_R0020_PCM_MODE_CFG 0x0020
+#define MAX98927_R0021_PCM_MASTER_MODE 0x0021
+#define MAX98927_R0022_PCM_CLK_SETUP 0x0022
+#define MAX98927_R0023_PCM_SR_SETUP1 0x0023
+#define MAX98927_R0024_PCM_SR_SETUP2	0x0024
+#define MAX98927_R0025_PCM_TO_SPK_MONOMIX_A 0x0025
+#define MAX98927_R0026_PCM_TO_SPK_MONOMIX_B 0x0026
+#define MAX98927_R0027_ICC_RX_EN_A 0x0027
+#define MAX98927_R0028_ICC_RX_EN_B 0x0028
+#define MAX98927_R002B_ICC_TX_EN_A 0x002B
+#define MAX98927_R002C_ICC_TX_EN_B 0x002C
+#define MAX98927_R002E_ICC_HIZ_MANUAL_MODE 0x002E
+#define MAX98927_R002F_ICC_TX_HIZ_EN_A 0x002F
+#define MAX98927_R0030_ICC_TX_HIZ_EN_B 0x0030
+#define MAX98927_R0031_ICC_LNK_EN 0x0031
+#define MAX98927_R0032_PDM_TX_EN 0x0032
+#define MAX98927_R0033_PDM_TX_HIZ_CTRL 0x0033
+#define MAX98927_R0034_PDM_TX_CTRL 0x0034
+#define MAX98927_R0035_PDM_RX_CTRL 0x0035
+#define MAX98927_R0036_AMP_VOL_CTRL 0x0036
+#define MAX98927_R0037_AMP_DSP_CFG 0x0037
+#define MAX98927_R0038_TONE_GEN_DC_CFG 0x0038
+#define MAX98927_R0039_DRE_CTRL 0x0039
+#define MAX98927_R003A_AMP_EN 0x003A
+#define MAX98927_R003B_SPK_SRC_SEL 0x003B
+#define MAX98927_R003C_SPK_GAIN 0x003C
+#define MAX98927_R003D_SSM_CFG 0x003D
+#define MAX98927_R003E_MEAS_EN 0x003E
+#define MAX98927_R003F_MEAS_DSP_CFG 0x003F
+#define MAX98927_R0040_BOOST_CTRL0 0x0040
+#define MAX98927_R0041_BOOST_CTRL3 0x0041
+#define MAX98927_R0042_BOOST_CTRL1 0x0042
+#define MAX98927_R0043_MEAS_ADC_CFG 0x0043
+#define MAX98927_R0044_MEAS_ADC_BASE_MSB 0x0044
+#define MAX98927_R0045_MEAS_ADC_BASE_LSB 0x0045
+#define MAX98927_R0046_ADC_CH0_DIVIDE 0x0046
+#define MAX98927_R0047_ADC_CH1_DIVIDE 0x0047
+#define MAX98927_R0048_ADC_CH2_DIVIDE 0x0048
+#define MAX98927_R0049_ADC_CH0_FILT_CFG 0x0049
+#define MAX98927_R004A_ADC_CH1_FILT_CFG 0x004A
+#define MAX98927_R004B_ADC_CH2_FILT_CFG 0x004B
+#define MAX98927_R004C_MEAS_ADC_CH0_READ 0x004C
+#define MAX98927_R004D_MEAS_ADC_CH1_READ 0x004D
+#define MAX98927_R004E_MEAS_ADC_CH2_READ 0x004E
+#define MAX98927_R0051_BROWNOUT_STATUS 0x0051
+#define MAX98927_R0052_BROWNOUT_EN 0x0052
+#define MAX98927_R0053_BROWNOUT_INFINITE_HOLD 0x0053
+#define MAX98927_R0054_BROWNOUT_INFINITE_HOLD_CLR 0x0054
+#define MAX98927_R0055_BROWNOUT_LVL_HOLD 0x0055
+#define MAX98927_R005A_BROWNOUT_LVL1_THRESH 0x005A
+#define MAX98927_R005B_BROWNOUT_LVL2_THRESH 0x005B
+#define MAX98927_R005C_BROWNOUT_LVL3_THRESH 0x005C
+#define MAX98927_R005D_BROWNOUT_LVL4_THRESH 0x005D
+#define MAX98927_R005E_BROWNOUT_THRESH_HYSTERYSIS 0x005E
+#define MAX98927_R005F_BROWNOUT_AMP_LIMITER_ATK_REL 0x005F
+#define MAX98927_R0060_BROWNOUT_AMP_GAIN_ATK_REL 0x0060
+#define MAX98927_R0061_BROWNOUT_AMP1_CLIP_MODE 0x0061
+#define MAX98927_R0072_BROWNOUT_LVL1_CUR_LIMIT 0x0072
+#define MAX98927_R0073_BROWNOUT_LVL1_AMP1_CTRL1 0x0073
+#define MAX98927_R0074_BROWNOUT_LVL1_AMP1_CTRL2 0x0074
+#define MAX98927_R0075_BROWNOUT_LVL1_AMP1_CTRL3 0x0075
+#define MAX98927_R0076_BROWNOUT_LVL2_CUR_LIMIT 0x0076
+#define MAX98927_R0077_BROWNOUT_LVL2_AMP1_CTRL1 0x0077
+#define MAX98927_R0078_BROWNOUT_LVL2_AMP1_CTRL2 0x0078
+#define MAX98927_R0079_BROWNOUT_LVL2_AMP1_CTRL3 0x0079
+#define MAX98927_R007A_BROWNOUT_LVL3_CUR_LIMIT 0x007A
+#define MAX98927_R007B_BROWNOUT_LVL3_AMP1_CTRL1 0x007B
+#define MAX98927_R007C_BROWNOUT_LVL3_AMP1_CTRL2 0x007C
+#define MAX98927_R007D_BROWNOUT_LVL3_AMP1_CTRL3 0x007D
+#define MAX98927_R007E_BROWNOUT_LVL4_CUR_LIMIT 0x007E
+#define MAX98927_R007F_BROWNOUT_LVL4_AMP1_CTRL1 0x007F
+#define MAX98927_R0080_BROWNOUT_LVL4_AMP1_CTRL2 0x0080
+#define MAX98927_R0081_BROWNOUT_LVL4_AMP1_CTRL3 0x0081
+#define MAX98927_R0082_ENV_TRACK_VOUT_HEADROOM 0x0082
+#define MAX98927_R0083_ENV_TRACK_BOOST_VOUT_DELAY 0x0083
+#define MAX98927_R0084_ENV_TRACK_REL_RATE 0x0084
+#define MAX98927_R0085_ENV_TRACK_HOLD_RATE 0x0085
+#define MAX98927_R0086_ENV_TRACK_CTRL 0x0086
+#define MAX98927_R0087_ENV_TRACK_BOOST_VOUT_READ 0x0087
+#define MAX98927_R00FF_GLOBAL_SHDN 0x00FF
+#define MAX98927_R0100_SOFT_RESET 0x0100
+#define MAX98927_R01FF_REV_ID 0x01FF
+
+/* MAX98927_R0018_PCM_RX_EN_A */
+#define MAX98927_PCM_RX_CH0_EN (0x1 << 0)
+#define MAX98927_PCM_RX_CH1_EN (0x1 << 1)
+#define MAX98927_PCM_RX_CH2_EN (0x1 << 2)
+#define MAX98927_PCM_RX_CH3_EN (0x1 << 3)
+#define MAX98927_PCM_RX_CH4_EN (0x1 << 4)
+#define MAX98927_PCM_RX_CH5_EN (0x1 << 5)
+#define MAX98927_PCM_RX_CH6_EN (0x1 << 6)
+#define MAX98927_PCM_RX_CH7_EN (0x1 << 7)
+
+/* MAX98927_R001A_PCM_TX_EN_A */
+#define MAX98927_PCM_TX_CH0_EN (0x1 << 0)
+#define MAX98927_PCM_TX_CH1_EN (0x1 << 1)
+#define MAX98927_PCM_TX_CH2_EN (0x1 << 2)
+#define MAX98927_PCM_TX_CH3_EN (0x1 << 3)
+#define MAX98927_PCM_TX_CH4_EN (0x1 << 4)
+#define MAX98927_PCM_TX_CH5_EN (0x1 << 5)
+#define MAX98927_PCM_TX_CH6_EN (0x1 << 6)
+#define MAX98927_PCM_TX_CH7_EN (0x1 << 7)
+
+/* MAX98927_R001E_PCM_TX_CH_SRC_A */
+#define MAX98927_PCM_TX_CH_SRC_A_V_SHIFT (0)
+#define MAX98927_PCM_TX_CH_SRC_A_I_SHIFT (4)
+
+/* MAX98927_R001F_PCM_TX_CH_SRC_B */
+#define MAX98927_PCM_TX_CH_INTERLEAVE_MASK (0x1 << 5)
+
+/* MAX98927_R0020_PCM_MODE_CFG */
+#define MAX98927_PCM_MODE_CFG_PCM_BCLKEDGE (0x1 << 2)
+#define MAX98927_PCM_MODE_CFG_FORMAT_MASK (0x7 << 3)
+#define MAX98927_PCM_MODE_CFG_FORMAT_SHIFT (3)
+#define MAX98927_PCM_FORMAT_I2S (0x0 << 0)
+#define MAX98927_PCM_FORMAT_LJ (0x1 << 0)
+
+#define MAX98927_PCM_MODE_CFG_CHANSZ_MASK (0x3 << 6)
+#define MAX98927_PCM_MODE_CFG_CHANSZ_16 (0x1 << 6)
+#define MAX98927_PCM_MODE_CFG_CHANSZ_24 (0x2 << 6)
+#define MAX98927_PCM_MODE_CFG_CHANSZ_32 (0x3 << 6)
+
+/* MAX98927_R0021_PCM_MASTER_MODE */
+#define MAX98927_PCM_MASTER_MODE_MASK (0x3 << 0)
+#define MAX98927_PCM_MASTER_MODE_SLAVE (0x0 << 0)
+#define MAX98927_PCM_MASTER_MODE_MASTER (0x3 << 0)
+
+#define MAX98927_PCM_MASTER_MODE_MCLK_MASK (0xF << 2)
+#define MAX98927_PCM_MASTER_MODE_MCLK_RATE_SHIFT (2)
+
+/* MAX98927_R0022_PCM_CLK_SETUP */
+#define MAX98927_PCM_CLK_SETUP_BSEL_MASK (0xF << 0)
+
+/* MAX98927_R0023_PCM_SR_SETUP1 */
+#define MAX98927_PCM_SR_SET1_SR_MASK (0xF << 0)
+
+#define MAX98927_PCM_SR_SET1_SR_8000 (0x0 << 0)
+#define MAX98927_PCM_SR_SET1_SR_11025 (0x1 << 0)
+#define MAX98927_PCM_SR_SET1_SR_12000 (0x2 << 0)
+#define MAX98927_PCM_SR_SET1_SR_16000 (0x3 << 0)
+#define MAX98927_PCM_SR_SET1_SR_22050 (0x4 << 0)
+#define MAX98927_PCM_SR_SET1_SR_24000 (0x5 << 0)
+#define MAX98927_PCM_SR_SET1_SR_32000 (0x6 << 0)
+#define MAX98927_PCM_SR_SET1_SR_44100 (0x7 << 0)
+#define MAX98927_PCM_SR_SET1_SR_48000 (0x8 << 0)
+
+/* MAX98927_R0024_PCM_SR_SETUP2 */
+#define MAX98927_PCM_SR_SET2_SR_MASK (0xF << 4)
+#define MAX98927_PCM_SR_SET2_SR_SHIFT (4)
+#define MAX98927_PCM_SR_SET2_IVADC_SR_MASK (0xf << 0)
+
+/* MAX98927_R0025_PCM_TO_SPK_MONOMIX_A */
+#define MAX98927_PCM_TO_SPK_MONOMIX_CFG_MASK (0x3 << 6)
+#define MAX98927_PCM_TO_SPK_MONOMIX_CFG_SHIFT (6)
+
+/* MAX98927_R0035_PDM_RX_CTRL */
+#define MAX98927_PDM_RX_EN_MASK (0x1 << 0)
+
+/* MAX98927_R0036_AMP_VOL_CTRL */
+#define MAX98927_AMP_VOL_SEL (0x1 << 7)
+#define MAX98927_AMP_VOL_SEL_WIDTH (1)
+#define MAX98927_AMP_VOL_SEL_SHIFT (7)
+#define MAX98927_AMP_VOL_MASK (0x7f << 0)
+#define MAX98927_AMP_VOL_WIDTH (7)
+#define MAX98927_AMP_VOL_SHIFT (0)
+
+/* MAX98927_R0037_AMP_DSP_CFG */
+#define MAX98927_AMP_DSP_CFG_DCBLK_EN (0x1 << 0)
+#define MAX98927_AMP_DSP_CFG_DITH_EN (0x1 << 1)
+#define MAX98927_AMP_DSP_CFG_RMP_BYPASS (0x1 << 4)
+#define MAX98927_AMP_DSP_CFG_DAC_INV (0x1 << 5)
+#define MAX98927_AMP_DSP_CFG_RMP_SHIFT (4)
+
+/* MAX98927_R0039_DRE_CTRL */
+#define MAX98927_DRE_CTRL_DRE_EN	(0x1 << 0)
+#define MAX98927_DRE_EN_SHIFT 0x1
+
+/* MAX98927_R003A_AMP_EN */
+#define MAX98927_AMP_EN_MASK (0x1 << 0)
+
+/* MAX98927_R003B_SPK_SRC_SEL */
+#define MAX98927_SPK_SRC_MASK (0x3 << 0)
+
+/* MAX98927_R003C_SPK_GAIN */
+#define MAX98927_SPK_PCM_GAIN_MASK (0x7 << 0)
+#define MAX98927_SPK_PDM_GAIN_MASK (0x7 << 4)
+#define MAX98927_SPK_GAIN_WIDTH (3)
+
+/* MAX98927_R003E_MEAS_EN */
+#define MAX98927_MEAS_V_EN (0x1 << 0)
+#define MAX98927_MEAS_I_EN (0x1 << 1)
+
+/* MAX98927_R0040_BOOST_CTRL0 */
+#define MAX98927_BOOST_CTRL0_VOUT_MASK (0x1f << 0)
+#define MAX98927_BOOST_CTRL0_PVDD_MASK (0x1 << 7)
+#define MAX98927_BOOST_CTRL0_PVDD_EN_SHIFT (7)
+
+/* MAX98927_R0052_BROWNOUT_EN */
+#define MAX98927_BROWNOUT_BDE_EN (0x1 << 0)
+#define MAX98927_BROWNOUT_AMP_EN (0x1 << 1)
+#define MAX98927_BROWNOUT_DSP_EN (0x1 << 2)
+#define MAX98927_BROWNOUT_DSP_SHIFT (2)
+
+/* MAX98927_R0100_SOFT_RESET */
+#define MAX98927_SOFT_RESET (0x1 << 0)
+
+/* MAX98927_R00FF_GLOBAL_SHDN */
+#define MAX98927_GLOBAL_EN_MASK (0x1 << 0)
+
+struct max98927_priv {
+	struct regmap *regmap;
+	struct snd_soc_codec *codec;
+	struct max98927_pdata *pdata;
+	unsigned int spk_gain;
+	unsigned int sysclk;
+	unsigned int v_l_slot;
+	unsigned int i_l_slot;
+	bool interleave_mode;
+	unsigned int ch_size;
+	unsigned int rate;
+	unsigned int iface;
+	unsigned int master;
+	unsigned int digital_gain;
+};
+#endif
diff --git a/sound/soc/codecs/nau8540.c b/sound/soc/codecs/nau8540.c
index 9e8f0f4aa51a..c8bcb1db966d 100644
--- a/sound/soc/codecs/nau8540.c
+++ b/sound/soc/codecs/nau8540.c
@@ -39,147 +39,147 @@
 
 /* scaling for mclk from sysclk_src output */
 static const struct nau8540_fll_attr mclk_src_scaling[] = {
-       { 1, 0x0 },
-       { 2, 0x2 },
-       { 4, 0x3 },
-       { 8, 0x4 },
-       { 16, 0x5 },
-       { 32, 0x6 },
-       { 3, 0x7 },
-       { 6, 0xa },
-       { 12, 0xb },
-       { 24, 0xc },
+	{ 1, 0x0 },
+	{ 2, 0x2 },
+	{ 4, 0x3 },
+	{ 8, 0x4 },
+	{ 16, 0x5 },
+	{ 32, 0x6 },
+	{ 3, 0x7 },
+	{ 6, 0xa },
+	{ 12, 0xb },
+	{ 24, 0xc },
 };
 
 /* ratio for input clk freq */
 static const struct nau8540_fll_attr fll_ratio[] = {
-       { 512000, 0x01 },
-       { 256000, 0x02 },
-       { 128000, 0x04 },
-       { 64000, 0x08 },
-       { 32000, 0x10 },
-       { 8000, 0x20 },
-       { 4000, 0x40 },
+	{ 512000, 0x01 },
+	{ 256000, 0x02 },
+	{ 128000, 0x04 },
+	{ 64000, 0x08 },
+	{ 32000, 0x10 },
+	{ 8000, 0x20 },
+	{ 4000, 0x40 },
 };
 
 static const struct nau8540_fll_attr fll_pre_scalar[] = {
-       { 1, 0x0 },
-       { 2, 0x1 },
-       { 4, 0x2 },
-       { 8, 0x3 },
+	{ 1, 0x0 },
+	{ 2, 0x1 },
+	{ 4, 0x2 },
+	{ 8, 0x3 },
 };
 
 /* over sampling rate */
 static const struct nau8540_osr_attr osr_adc_sel[] = {
-       { 32, 3 },      /* OSR 32, SRC 1/8 */
-       { 64, 2 },      /* OSR 64, SRC 1/4 */
-       { 128, 1 },     /* OSR 128, SRC 1/2 */
-       { 256, 0 },     /* OSR 256, SRC 1 */
+	{ 32, 3 },	/* OSR 32, SRC 1/8 */
+	{ 64, 2 },	/* OSR 64, SRC 1/4 */
+	{ 128, 1 },	/* OSR 128, SRC 1/2 */
+	{ 256, 0 },	/* OSR 256, SRC 1 */
 };
 
 static const struct reg_default nau8540_reg_defaults[] = {
-       {NAU8540_REG_POWER_MANAGEMENT, 0x0000},
-       {NAU8540_REG_CLOCK_CTRL, 0x0000},
-       {NAU8540_REG_CLOCK_SRC, 0x0000},
-       {NAU8540_REG_FLL1, 0x0001},
-       {NAU8540_REG_FLL2, 0x3126},
-       {NAU8540_REG_FLL3, 0x0008},
-       {NAU8540_REG_FLL4, 0x0010},
-       {NAU8540_REG_FLL5, 0xC000},
-       {NAU8540_REG_FLL6, 0x6000},
-       {NAU8540_REG_FLL_VCO_RSV, 0xF13C},
-       {NAU8540_REG_PCM_CTRL0, 0x000B},
-       {NAU8540_REG_PCM_CTRL1, 0x3010},
-       {NAU8540_REG_PCM_CTRL2, 0x0800},
-       {NAU8540_REG_PCM_CTRL3, 0x0000},
-       {NAU8540_REG_PCM_CTRL4, 0x000F},
-       {NAU8540_REG_ALC_CONTROL_1, 0x0000},
-       {NAU8540_REG_ALC_CONTROL_2, 0x700B},
-       {NAU8540_REG_ALC_CONTROL_3, 0x0022},
-       {NAU8540_REG_ALC_CONTROL_4, 0x1010},
-       {NAU8540_REG_ALC_CONTROL_5, 0x1010},
-       {NAU8540_REG_NOTCH_FIL1_CH1, 0x0000},
-       {NAU8540_REG_NOTCH_FIL2_CH1, 0x0000},
-       {NAU8540_REG_NOTCH_FIL1_CH2, 0x0000},
-       {NAU8540_REG_NOTCH_FIL2_CH2, 0x0000},
-       {NAU8540_REG_NOTCH_FIL1_CH3, 0x0000},
-       {NAU8540_REG_NOTCH_FIL2_CH3, 0x0000},
-       {NAU8540_REG_NOTCH_FIL1_CH4, 0x0000},
-       {NAU8540_REG_NOTCH_FIL2_CH4, 0x0000},
-       {NAU8540_REG_HPF_FILTER_CH12, 0x0000},
-       {NAU8540_REG_HPF_FILTER_CH34, 0x0000},
-       {NAU8540_REG_ADC_SAMPLE_RATE, 0x0002},
-       {NAU8540_REG_DIGITAL_GAIN_CH1, 0x0400},
-       {NAU8540_REG_DIGITAL_GAIN_CH2, 0x0400},
-       {NAU8540_REG_DIGITAL_GAIN_CH3, 0x0400},
-       {NAU8540_REG_DIGITAL_GAIN_CH4, 0x0400},
-       {NAU8540_REG_DIGITAL_MUX, 0x00E4},
-       {NAU8540_REG_GPIO_CTRL, 0x0000},
-       {NAU8540_REG_MISC_CTRL, 0x0000},
-       {NAU8540_REG_I2C_CTRL, 0xEFFF},
-       {NAU8540_REG_VMID_CTRL, 0x0000},
-       {NAU8540_REG_MUTE, 0x0000},
-       {NAU8540_REG_ANALOG_ADC1, 0x0011},
-       {NAU8540_REG_ANALOG_ADC2, 0x0020},
-       {NAU8540_REG_ANALOG_PWR, 0x0000},
-       {NAU8540_REG_MIC_BIAS, 0x0004},
-       {NAU8540_REG_REFERENCE, 0x0000},
-       {NAU8540_REG_FEPGA1, 0x0000},
-       {NAU8540_REG_FEPGA2, 0x0000},
-       {NAU8540_REG_FEPGA3, 0x0101},
-       {NAU8540_REG_FEPGA4, 0x0101},
-       {NAU8540_REG_PWR, 0x0000},
+	{NAU8540_REG_POWER_MANAGEMENT, 0x0000},
+	{NAU8540_REG_CLOCK_CTRL, 0x0000},
+	{NAU8540_REG_CLOCK_SRC, 0x0000},
+	{NAU8540_REG_FLL1, 0x0001},
+	{NAU8540_REG_FLL2, 0x3126},
+	{NAU8540_REG_FLL3, 0x0008},
+	{NAU8540_REG_FLL4, 0x0010},
+	{NAU8540_REG_FLL5, 0xC000},
+	{NAU8540_REG_FLL6, 0x6000},
+	{NAU8540_REG_FLL_VCO_RSV, 0xF13C},
+	{NAU8540_REG_PCM_CTRL0, 0x000B},
+	{NAU8540_REG_PCM_CTRL1, 0x3010},
+	{NAU8540_REG_PCM_CTRL2, 0x0800},
+	{NAU8540_REG_PCM_CTRL3, 0x0000},
+	{NAU8540_REG_PCM_CTRL4, 0x000F},
+	{NAU8540_REG_ALC_CONTROL_1, 0x0000},
+	{NAU8540_REG_ALC_CONTROL_2, 0x700B},
+	{NAU8540_REG_ALC_CONTROL_3, 0x0022},
+	{NAU8540_REG_ALC_CONTROL_4, 0x1010},
+	{NAU8540_REG_ALC_CONTROL_5, 0x1010},
+	{NAU8540_REG_NOTCH_FIL1_CH1, 0x0000},
+	{NAU8540_REG_NOTCH_FIL2_CH1, 0x0000},
+	{NAU8540_REG_NOTCH_FIL1_CH2, 0x0000},
+	{NAU8540_REG_NOTCH_FIL2_CH2, 0x0000},
+	{NAU8540_REG_NOTCH_FIL1_CH3, 0x0000},
+	{NAU8540_REG_NOTCH_FIL2_CH3, 0x0000},
+	{NAU8540_REG_NOTCH_FIL1_CH4, 0x0000},
+	{NAU8540_REG_NOTCH_FIL2_CH4, 0x0000},
+	{NAU8540_REG_HPF_FILTER_CH12, 0x0000},
+	{NAU8540_REG_HPF_FILTER_CH34, 0x0000},
+	{NAU8540_REG_ADC_SAMPLE_RATE, 0x0002},
+	{NAU8540_REG_DIGITAL_GAIN_CH1, 0x0400},
+	{NAU8540_REG_DIGITAL_GAIN_CH2, 0x0400},
+	{NAU8540_REG_DIGITAL_GAIN_CH3, 0x0400},
+	{NAU8540_REG_DIGITAL_GAIN_CH4, 0x0400},
+	{NAU8540_REG_DIGITAL_MUX, 0x00E4},
+	{NAU8540_REG_GPIO_CTRL, 0x0000},
+	{NAU8540_REG_MISC_CTRL, 0x0000},
+	{NAU8540_REG_I2C_CTRL, 0xEFFF},
+	{NAU8540_REG_VMID_CTRL, 0x0000},
+	{NAU8540_REG_MUTE, 0x0000},
+	{NAU8540_REG_ANALOG_ADC1, 0x0011},
+	{NAU8540_REG_ANALOG_ADC2, 0x0020},
+	{NAU8540_REG_ANALOG_PWR, 0x0000},
+	{NAU8540_REG_MIC_BIAS, 0x0004},
+	{NAU8540_REG_REFERENCE, 0x0000},
+	{NAU8540_REG_FEPGA1, 0x0000},
+	{NAU8540_REG_FEPGA2, 0x0000},
+	{NAU8540_REG_FEPGA3, 0x0101},
+	{NAU8540_REG_FEPGA4, 0x0101},
+	{NAU8540_REG_PWR, 0x0000},
 };
 
 static bool nau8540_readable_reg(struct device *dev, unsigned int reg)
 {
-       switch (reg) {
-       case NAU8540_REG_POWER_MANAGEMENT ... NAU8540_REG_FLL_VCO_RSV:
-       case NAU8540_REG_PCM_CTRL0 ... NAU8540_REG_PCM_CTRL4:
-       case NAU8540_REG_ALC_CONTROL_1 ... NAU8540_REG_ALC_CONTROL_5:
-       case NAU8540_REG_ALC_GAIN_CH12 ... NAU8540_REG_ADC_SAMPLE_RATE:
-       case NAU8540_REG_DIGITAL_GAIN_CH1 ... NAU8540_REG_DIGITAL_MUX:
-       case NAU8540_REG_P2P_CH1 ... NAU8540_REG_I2C_CTRL:
-       case NAU8540_REG_I2C_DEVICE_ID:
-       case NAU8540_REG_VMID_CTRL ... NAU8540_REG_MUTE:
-       case NAU8540_REG_ANALOG_ADC1 ... NAU8540_REG_PWR:
-               return true;
-       default:
-               return false;
-       }
+	switch (reg) {
+	case NAU8540_REG_POWER_MANAGEMENT ... NAU8540_REG_FLL_VCO_RSV:
+	case NAU8540_REG_PCM_CTRL0 ... NAU8540_REG_PCM_CTRL4:
+	case NAU8540_REG_ALC_CONTROL_1 ... NAU8540_REG_ALC_CONTROL_5:
+	case NAU8540_REG_ALC_GAIN_CH12 ... NAU8540_REG_ADC_SAMPLE_RATE:
+	case NAU8540_REG_DIGITAL_GAIN_CH1 ... NAU8540_REG_DIGITAL_MUX:
+	case NAU8540_REG_P2P_CH1 ... NAU8540_REG_I2C_CTRL:
+	case NAU8540_REG_I2C_DEVICE_ID:
+	case NAU8540_REG_VMID_CTRL ... NAU8540_REG_MUTE:
+	case NAU8540_REG_ANALOG_ADC1 ... NAU8540_REG_PWR:
+		return true;
+	default:
+		return false;
+	}
 
 }
 
 static bool nau8540_writeable_reg(struct device *dev, unsigned int reg)
 {
-       switch (reg) {
-       case NAU8540_REG_SW_RESET ... NAU8540_REG_FLL_VCO_RSV:
-       case NAU8540_REG_PCM_CTRL0 ... NAU8540_REG_PCM_CTRL4:
-       case NAU8540_REG_ALC_CONTROL_1 ... NAU8540_REG_ALC_CONTROL_5:
-       case NAU8540_REG_NOTCH_FIL1_CH1 ... NAU8540_REG_ADC_SAMPLE_RATE:
-       case NAU8540_REG_DIGITAL_GAIN_CH1 ... NAU8540_REG_DIGITAL_MUX:
-       case NAU8540_REG_GPIO_CTRL ... NAU8540_REG_I2C_CTRL:
-       case NAU8540_REG_RST:
-       case NAU8540_REG_VMID_CTRL ... NAU8540_REG_MUTE:
-       case NAU8540_REG_ANALOG_ADC1 ... NAU8540_REG_PWR:
-               return true;
-       default:
-               return false;
-       }
+	switch (reg) {
+	case NAU8540_REG_SW_RESET ... NAU8540_REG_FLL_VCO_RSV:
+	case NAU8540_REG_PCM_CTRL0 ... NAU8540_REG_PCM_CTRL4:
+	case NAU8540_REG_ALC_CONTROL_1 ... NAU8540_REG_ALC_CONTROL_5:
+	case NAU8540_REG_NOTCH_FIL1_CH1 ... NAU8540_REG_ADC_SAMPLE_RATE:
+	case NAU8540_REG_DIGITAL_GAIN_CH1 ... NAU8540_REG_DIGITAL_MUX:
+	case NAU8540_REG_GPIO_CTRL ... NAU8540_REG_I2C_CTRL:
+	case NAU8540_REG_RST:
+	case NAU8540_REG_VMID_CTRL ... NAU8540_REG_MUTE:
+	case NAU8540_REG_ANALOG_ADC1 ... NAU8540_REG_PWR:
+		return true;
+	default:
+		return false;
+	}
 }
 
 static bool nau8540_volatile_reg(struct device *dev, unsigned int reg)
 {
-       switch (reg) {
-       case NAU8540_REG_SW_RESET:
-       case NAU8540_REG_ALC_GAIN_CH12 ... NAU8540_REG_ALC_STATUS:
-       case NAU8540_REG_P2P_CH1 ... NAU8540_REG_PEAK_CH4:
-       case NAU8540_REG_I2C_DEVICE_ID:
-       case NAU8540_REG_RST:
-               return true;
-       default:
-               return false;
-       }
+	switch (reg) {
+	case NAU8540_REG_SW_RESET:
+	case NAU8540_REG_ALC_GAIN_CH12 ... NAU8540_REG_ALC_STATUS:
+	case NAU8540_REG_P2P_CH1 ... NAU8540_REG_PEAK_CH4:
+	case NAU8540_REG_I2C_DEVICE_ID:
+	case NAU8540_REG_RST:
+		return true;
+	default:
+		return false;
+	}
 }
 
 
@@ -187,255 +187,255 @@ static const DECLARE_TLV_DB_MINMAX(adc_vol_tlv, -12800, 3600);
 static const DECLARE_TLV_DB_MINMAX(fepga_gain_tlv, -100, 3600);
 
 static const struct snd_kcontrol_new nau8540_snd_controls[] = {
-       SOC_SINGLE_TLV("Mic1 Volume", NAU8540_REG_DIGITAL_GAIN_CH1,
-               0, 0x520, 0, adc_vol_tlv),
-       SOC_SINGLE_TLV("Mic2 Volume", NAU8540_REG_DIGITAL_GAIN_CH2,
-               0, 0x520, 0, adc_vol_tlv),
-       SOC_SINGLE_TLV("Mic3 Volume", NAU8540_REG_DIGITAL_GAIN_CH3,
-               0, 0x520, 0, adc_vol_tlv),
-       SOC_SINGLE_TLV("Mic4 Volume", NAU8540_REG_DIGITAL_GAIN_CH4,
-               0, 0x520, 0, adc_vol_tlv),
-
-       SOC_SINGLE_TLV("Frontend PGA1 Volume", NAU8540_REG_FEPGA3,
-               0, 0x25, 0, fepga_gain_tlv),
-       SOC_SINGLE_TLV("Frontend PGA2 Volume", NAU8540_REG_FEPGA3,
-               8, 0x25, 0, fepga_gain_tlv),
-       SOC_SINGLE_TLV("Frontend PGA3 Volume", NAU8540_REG_FEPGA4,
-               0, 0x25, 0, fepga_gain_tlv),
-       SOC_SINGLE_TLV("Frontend PGA4 Volume", NAU8540_REG_FEPGA4,
-               8, 0x25, 0, fepga_gain_tlv),
+	SOC_SINGLE_TLV("Mic1 Volume", NAU8540_REG_DIGITAL_GAIN_CH1,
+		0, 0x520, 0, adc_vol_tlv),
+	SOC_SINGLE_TLV("Mic2 Volume", NAU8540_REG_DIGITAL_GAIN_CH2,
+		0, 0x520, 0, adc_vol_tlv),
+	SOC_SINGLE_TLV("Mic3 Volume", NAU8540_REG_DIGITAL_GAIN_CH3,
+		0, 0x520, 0, adc_vol_tlv),
+	SOC_SINGLE_TLV("Mic4 Volume", NAU8540_REG_DIGITAL_GAIN_CH4,
+		0, 0x520, 0, adc_vol_tlv),
+
+	SOC_SINGLE_TLV("Frontend PGA1 Volume", NAU8540_REG_FEPGA3,
+		0, 0x25, 0, fepga_gain_tlv),
+	SOC_SINGLE_TLV("Frontend PGA2 Volume", NAU8540_REG_FEPGA3,
+		8, 0x25, 0, fepga_gain_tlv),
+	SOC_SINGLE_TLV("Frontend PGA3 Volume", NAU8540_REG_FEPGA4,
+		0, 0x25, 0, fepga_gain_tlv),
+	SOC_SINGLE_TLV("Frontend PGA4 Volume", NAU8540_REG_FEPGA4,
+		8, 0x25, 0, fepga_gain_tlv),
 };
 
 static const char * const adc_channel[] = {
-       "ADC channel 1", "ADC channel 2", "ADC channel 3", "ADC channel 4"
+	"ADC channel 1", "ADC channel 2", "ADC channel 3", "ADC channel 4"
 };
 static SOC_ENUM_SINGLE_DECL(
-       digital_ch4_enum, NAU8540_REG_DIGITAL_MUX, 6, adc_channel);
+	digital_ch4_enum, NAU8540_REG_DIGITAL_MUX, 6, adc_channel);
 
 static const struct snd_kcontrol_new digital_ch4_mux =
-       SOC_DAPM_ENUM("Digital CH4 Select", digital_ch4_enum);
+	SOC_DAPM_ENUM("Digital CH4 Select", digital_ch4_enum);
 
 static SOC_ENUM_SINGLE_DECL(
-       digital_ch3_enum, NAU8540_REG_DIGITAL_MUX, 4, adc_channel);
+	digital_ch3_enum, NAU8540_REG_DIGITAL_MUX, 4, adc_channel);
 
 static const struct snd_kcontrol_new digital_ch3_mux =
-       SOC_DAPM_ENUM("Digital CH3 Select", digital_ch3_enum);
+	SOC_DAPM_ENUM("Digital CH3 Select", digital_ch3_enum);
 
 static SOC_ENUM_SINGLE_DECL(
-       digital_ch2_enum, NAU8540_REG_DIGITAL_MUX, 2, adc_channel);
+	digital_ch2_enum, NAU8540_REG_DIGITAL_MUX, 2, adc_channel);
 
 static const struct snd_kcontrol_new digital_ch2_mux =
-       SOC_DAPM_ENUM("Digital CH2 Select", digital_ch2_enum);
+	SOC_DAPM_ENUM("Digital CH2 Select", digital_ch2_enum);
 
 static SOC_ENUM_SINGLE_DECL(
-       digital_ch1_enum, NAU8540_REG_DIGITAL_MUX, 0, adc_channel);
+	digital_ch1_enum, NAU8540_REG_DIGITAL_MUX, 0, adc_channel);
 
 static const struct snd_kcontrol_new digital_ch1_mux =
-       SOC_DAPM_ENUM("Digital CH1 Select", digital_ch1_enum);
+	SOC_DAPM_ENUM("Digital CH1 Select", digital_ch1_enum);
 
 static const struct snd_soc_dapm_widget nau8540_dapm_widgets[] = {
-       SND_SOC_DAPM_SUPPLY("MICBIAS2", NAU8540_REG_MIC_BIAS, 11, 0, NULL, 0),
-       SND_SOC_DAPM_SUPPLY("MICBIAS1", NAU8540_REG_MIC_BIAS, 10, 0, NULL, 0),
-
-       SND_SOC_DAPM_INPUT("MIC1"),
-       SND_SOC_DAPM_INPUT("MIC2"),
-       SND_SOC_DAPM_INPUT("MIC3"),
-       SND_SOC_DAPM_INPUT("MIC4"),
-
-       SND_SOC_DAPM_PGA("Frontend PGA1", NAU8540_REG_PWR, 12, 0, NULL, 0),
-       SND_SOC_DAPM_PGA("Frontend PGA2", NAU8540_REG_PWR, 13, 0, NULL, 0),
-       SND_SOC_DAPM_PGA("Frontend PGA3", NAU8540_REG_PWR, 14, 0, NULL, 0),
-       SND_SOC_DAPM_PGA("Frontend PGA4", NAU8540_REG_PWR, 15, 0, NULL, 0),
-
-       SND_SOC_DAPM_ADC("ADC1", NULL,
-               NAU8540_REG_POWER_MANAGEMENT, 0, 0),
-       SND_SOC_DAPM_ADC("ADC2", NULL,
-               NAU8540_REG_POWER_MANAGEMENT, 1, 0),
-       SND_SOC_DAPM_ADC("ADC3", NULL,
-               NAU8540_REG_POWER_MANAGEMENT, 2, 0),
-       SND_SOC_DAPM_ADC("ADC4", NULL,
-               NAU8540_REG_POWER_MANAGEMENT, 3, 0),
-
-       SND_SOC_DAPM_PGA("ADC CH1", NAU8540_REG_ANALOG_PWR, 0, 0, NULL, 0),
-       SND_SOC_DAPM_PGA("ADC CH2", NAU8540_REG_ANALOG_PWR, 1, 0, NULL, 0),
-       SND_SOC_DAPM_PGA("ADC CH3", NAU8540_REG_ANALOG_PWR, 2, 0, NULL, 0),
-       SND_SOC_DAPM_PGA("ADC CH4", NAU8540_REG_ANALOG_PWR, 3, 0, NULL, 0),
-
-       SND_SOC_DAPM_MUX("Digital CH4 Mux",
-               SND_SOC_NOPM, 0, 0, &digital_ch4_mux),
-       SND_SOC_DAPM_MUX("Digital CH3 Mux",
-               SND_SOC_NOPM, 0, 0, &digital_ch3_mux),
-       SND_SOC_DAPM_MUX("Digital CH2 Mux",
-               SND_SOC_NOPM, 0, 0, &digital_ch2_mux),
-       SND_SOC_DAPM_MUX("Digital CH1 Mux",
-               SND_SOC_NOPM, 0, 0, &digital_ch1_mux),
-
-       SND_SOC_DAPM_AIF_OUT("AIFTX", "Capture", 0, SND_SOC_NOPM, 0, 0),
+	SND_SOC_DAPM_SUPPLY("MICBIAS2", NAU8540_REG_MIC_BIAS, 11, 0, NULL, 0),
+	SND_SOC_DAPM_SUPPLY("MICBIAS1", NAU8540_REG_MIC_BIAS, 10, 0, NULL, 0),
+
+	SND_SOC_DAPM_INPUT("MIC1"),
+	SND_SOC_DAPM_INPUT("MIC2"),
+	SND_SOC_DAPM_INPUT("MIC3"),
+	SND_SOC_DAPM_INPUT("MIC4"),
+
+	SND_SOC_DAPM_PGA("Frontend PGA1", NAU8540_REG_PWR, 12, 0, NULL, 0),
+	SND_SOC_DAPM_PGA("Frontend PGA2", NAU8540_REG_PWR, 13, 0, NULL, 0),
+	SND_SOC_DAPM_PGA("Frontend PGA3", NAU8540_REG_PWR, 14, 0, NULL, 0),
+	SND_SOC_DAPM_PGA("Frontend PGA4", NAU8540_REG_PWR, 15, 0, NULL, 0),
+
+	SND_SOC_DAPM_ADC("ADC1", NULL,
+		NAU8540_REG_POWER_MANAGEMENT, 0, 0),
+	SND_SOC_DAPM_ADC("ADC2", NULL,
+		NAU8540_REG_POWER_MANAGEMENT, 1, 0),
+	SND_SOC_DAPM_ADC("ADC3", NULL,
+		NAU8540_REG_POWER_MANAGEMENT, 2, 0),
+	SND_SOC_DAPM_ADC("ADC4", NULL,
+		NAU8540_REG_POWER_MANAGEMENT, 3, 0),
+
+	SND_SOC_DAPM_PGA("ADC CH1", NAU8540_REG_ANALOG_PWR, 0, 0, NULL, 0),
+	SND_SOC_DAPM_PGA("ADC CH2", NAU8540_REG_ANALOG_PWR, 1, 0, NULL, 0),
+	SND_SOC_DAPM_PGA("ADC CH3", NAU8540_REG_ANALOG_PWR, 2, 0, NULL, 0),
+	SND_SOC_DAPM_PGA("ADC CH4", NAU8540_REG_ANALOG_PWR, 3, 0, NULL, 0),
+
+	SND_SOC_DAPM_MUX("Digital CH4 Mux",
+		SND_SOC_NOPM, 0, 0, &digital_ch4_mux),
+	SND_SOC_DAPM_MUX("Digital CH3 Mux",
+		SND_SOC_NOPM, 0, 0, &digital_ch3_mux),
+	SND_SOC_DAPM_MUX("Digital CH2 Mux",
+		SND_SOC_NOPM, 0, 0, &digital_ch2_mux),
+	SND_SOC_DAPM_MUX("Digital CH1 Mux",
+		SND_SOC_NOPM, 0, 0, &digital_ch1_mux),
+
+	SND_SOC_DAPM_AIF_OUT("AIFTX", "Capture", 0, SND_SOC_NOPM, 0, 0),
 };
 
 static const struct snd_soc_dapm_route nau8540_dapm_routes[] = {
-       {"Frontend PGA1", NULL, "MIC1"},
-       {"Frontend PGA2", NULL, "MIC2"},
-       {"Frontend PGA3", NULL, "MIC3"},
-       {"Frontend PGA4", NULL, "MIC4"},
-
-       {"ADC1", NULL, "Frontend PGA1"},
-       {"ADC2", NULL, "Frontend PGA2"},
-       {"ADC3", NULL, "Frontend PGA3"},
-       {"ADC4", NULL, "Frontend PGA4"},
-
-       {"ADC CH1", NULL, "ADC1"},
-       {"ADC CH2", NULL, "ADC2"},
-       {"ADC CH3", NULL, "ADC3"},
-       {"ADC CH4", NULL, "ADC4"},
-
-       {"ADC1", NULL, "MICBIAS1"},
-       {"ADC2", NULL, "MICBIAS1"},
-       {"ADC3", NULL, "MICBIAS2"},
-       {"ADC4", NULL, "MICBIAS2"},
-
-       {"Digital CH1 Mux", "ADC channel 1", "ADC CH1"},
-       {"Digital CH1 Mux", "ADC channel 2", "ADC CH2"},
-       {"Digital CH1 Mux", "ADC channel 3", "ADC CH3"},
-       {"Digital CH1 Mux", "ADC channel 4", "ADC CH4"},
-
-       {"Digital CH2 Mux", "ADC channel 1", "ADC CH1"},
-       {"Digital CH2 Mux", "ADC channel 2", "ADC CH2"},
-       {"Digital CH2 Mux", "ADC channel 3", "ADC CH3"},
-       {"Digital CH2 Mux", "ADC channel 4", "ADC CH4"},
-
-       {"Digital CH3 Mux", "ADC channel 1", "ADC CH1"},
-       {"Digital CH3 Mux", "ADC channel 2", "ADC CH2"},
-       {"Digital CH3 Mux", "ADC channel 3", "ADC CH3"},
-       {"Digital CH3 Mux", "ADC channel 4", "ADC CH4"},
-
-       {"Digital CH4 Mux", "ADC channel 1", "ADC CH1"},
-       {"Digital CH4 Mux", "ADC channel 2", "ADC CH2"},
-       {"Digital CH4 Mux", "ADC channel 3", "ADC CH3"},
-       {"Digital CH4 Mux", "ADC channel 4", "ADC CH4"},
-
-       {"AIFTX", NULL, "Digital CH1 Mux"},
-       {"AIFTX", NULL, "Digital CH2 Mux"},
-       {"AIFTX", NULL, "Digital CH3 Mux"},
-       {"AIFTX", NULL, "Digital CH4 Mux"},
+	{"Frontend PGA1", NULL, "MIC1"},
+	{"Frontend PGA2", NULL, "MIC2"},
+	{"Frontend PGA3", NULL, "MIC3"},
+	{"Frontend PGA4", NULL, "MIC4"},
+
+	{"ADC1", NULL, "Frontend PGA1"},
+	{"ADC2", NULL, "Frontend PGA2"},
+	{"ADC3", NULL, "Frontend PGA3"},
+	{"ADC4", NULL, "Frontend PGA4"},
+
+	{"ADC CH1", NULL, "ADC1"},
+	{"ADC CH2", NULL, "ADC2"},
+	{"ADC CH3", NULL, "ADC3"},
+	{"ADC CH4", NULL, "ADC4"},
+
+	{"ADC1", NULL, "MICBIAS1"},
+	{"ADC2", NULL, "MICBIAS1"},
+	{"ADC3", NULL, "MICBIAS2"},
+	{"ADC4", NULL, "MICBIAS2"},
+
+	{"Digital CH1 Mux", "ADC channel 1", "ADC CH1"},
+	{"Digital CH1 Mux", "ADC channel 2", "ADC CH2"},
+	{"Digital CH1 Mux", "ADC channel 3", "ADC CH3"},
+	{"Digital CH1 Mux", "ADC channel 4", "ADC CH4"},
+
+	{"Digital CH2 Mux", "ADC channel 1", "ADC CH1"},
+	{"Digital CH2 Mux", "ADC channel 2", "ADC CH2"},
+	{"Digital CH2 Mux", "ADC channel 3", "ADC CH3"},
+	{"Digital CH2 Mux", "ADC channel 4", "ADC CH4"},
+
+	{"Digital CH3 Mux", "ADC channel 1", "ADC CH1"},
+	{"Digital CH3 Mux", "ADC channel 2", "ADC CH2"},
+	{"Digital CH3 Mux", "ADC channel 3", "ADC CH3"},
+	{"Digital CH3 Mux", "ADC channel 4", "ADC CH4"},
+
+	{"Digital CH4 Mux", "ADC channel 1", "ADC CH1"},
+	{"Digital CH4 Mux", "ADC channel 2", "ADC CH2"},
+	{"Digital CH4 Mux", "ADC channel 3", "ADC CH3"},
+	{"Digital CH4 Mux", "ADC channel 4", "ADC CH4"},
+
+	{"AIFTX", NULL, "Digital CH1 Mux"},
+	{"AIFTX", NULL, "Digital CH2 Mux"},
+	{"AIFTX", NULL, "Digital CH3 Mux"},
+	{"AIFTX", NULL, "Digital CH4 Mux"},
 };
 
 static int nau8540_clock_check(struct nau8540 *nau8540, int rate, int osr)
 {
-       int osrate;
+	int osrate;
 
-       if (osr >= ARRAY_SIZE(osr_adc_sel))
-               return -EINVAL;
-       osrate = osr_adc_sel[osr].osr;
+	if (osr >= ARRAY_SIZE(osr_adc_sel))
+		return -EINVAL;
+	osrate = osr_adc_sel[osr].osr;
 
-       if (rate * osr > CLK_ADC_MAX) {
-               dev_err(nau8540->dev, "exceed the maximum frequency of CLK_ADC\n");
-               return -EINVAL;
-       }
+	if (rate * osr > CLK_ADC_MAX) {
+		dev_err(nau8540->dev, "exceed the maximum frequency of CLK_ADC\n");
+		return -EINVAL;
+	}
 
-       return 0;
+	return 0;
 }
 
 static int nau8540_hw_params(struct snd_pcm_substream *substream,
-       struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
+	struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
 {
-       struct snd_soc_codec *codec = dai->codec;
-       struct nau8540 *nau8540 = snd_soc_codec_get_drvdata(codec);
-       unsigned int val_len = 0, osr;
-
-       /* CLK_ADC = OSR * FS
-        * ADC clock frequency is defined as Over Sampling Rate (OSR)
-        * multiplied by the audio sample rate (Fs). Note that the OSR and Fs
-        * values must be selected such that the maximum frequency is less
-        * than 6.144 MHz.
-        */
-       regmap_read(nau8540->regmap, NAU8540_REG_ADC_SAMPLE_RATE, &osr);
-       osr &= NAU8540_ADC_OSR_MASK;
-       if (nau8540_clock_check(nau8540, params_rate(params), osr))
-               return -EINVAL;
-       regmap_update_bits(nau8540->regmap, NAU8540_REG_CLOCK_SRC,
-               NAU8540_CLK_ADC_SRC_MASK,
-               osr_adc_sel[osr].clk_src << NAU8540_CLK_ADC_SRC_SFT);
-
-       switch (params_width(params)) {
-       case 16:
-               val_len |= NAU8540_I2S_DL_16;
-               break;
-       case 20:
-               val_len |= NAU8540_I2S_DL_20;
-               break;
-       case 24:
-               val_len |= NAU8540_I2S_DL_24;
-               break;
-       case 32:
-               val_len |= NAU8540_I2S_DL_32;
-               break;
-       default:
-               return -EINVAL;
-       }
-
-       regmap_update_bits(nau8540->regmap, NAU8540_REG_PCM_CTRL0,
-               NAU8540_I2S_DL_MASK, val_len);
-
-       return 0;
+	struct snd_soc_codec *codec = dai->codec;
+	struct nau8540 *nau8540 = snd_soc_codec_get_drvdata(codec);
+	unsigned int val_len = 0, osr;
+
+	/* CLK_ADC = OSR * FS
+	 * ADC clock frequency is defined as Over Sampling Rate (OSR)
+	 * multiplied by the audio sample rate (Fs). Note that the OSR and Fs
+	 * values must be selected such that the maximum frequency is less
+	 * than 6.144 MHz.
+	 */
+	regmap_read(nau8540->regmap, NAU8540_REG_ADC_SAMPLE_RATE, &osr);
+	osr &= NAU8540_ADC_OSR_MASK;
+	if (nau8540_clock_check(nau8540, params_rate(params), osr))
+		return -EINVAL;
+	regmap_update_bits(nau8540->regmap, NAU8540_REG_CLOCK_SRC,
+		NAU8540_CLK_ADC_SRC_MASK,
+		osr_adc_sel[osr].clk_src << NAU8540_CLK_ADC_SRC_SFT);
+
+	switch (params_width(params)) {
+	case 16:
+		val_len |= NAU8540_I2S_DL_16;
+		break;
+	case 20:
+		val_len |= NAU8540_I2S_DL_20;
+		break;
+	case 24:
+		val_len |= NAU8540_I2S_DL_24;
+		break;
+	case 32:
+		val_len |= NAU8540_I2S_DL_32;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	regmap_update_bits(nau8540->regmap, NAU8540_REG_PCM_CTRL0,
+		NAU8540_I2S_DL_MASK, val_len);
+
+	return 0;
 }
 
 static int nau8540_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
 {
-       struct snd_soc_codec *codec = dai->codec;
-       struct nau8540 *nau8540 = snd_soc_codec_get_drvdata(codec);
-       unsigned int ctrl1_val = 0, ctrl2_val = 0;
-
-       switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
-       case SND_SOC_DAIFMT_CBM_CFM:
-               ctrl2_val |= NAU8540_I2S_MS_MASTER;
-               break;
-       case SND_SOC_DAIFMT_CBS_CFS:
-               break;
-       default:
-               return -EINVAL;
-       }
-
-       switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
-       case SND_SOC_DAIFMT_NB_NF:
-               break;
-       case SND_SOC_DAIFMT_IB_NF:
-               ctrl1_val |= NAU8540_I2S_BP_INV;
-               break;
-       default:
-               return -EINVAL;
-       }
-
-       switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
-       case SND_SOC_DAIFMT_I2S:
-               ctrl1_val |= NAU8540_I2S_DF_I2S;
-               break;
-       case SND_SOC_DAIFMT_LEFT_J:
-               ctrl1_val |= NAU8540_I2S_DF_LEFT;
-               break;
-       case SND_SOC_DAIFMT_RIGHT_J:
-               ctrl1_val |= NAU8540_I2S_DF_RIGTH;
-               break;
-       case SND_SOC_DAIFMT_DSP_A:
-               ctrl1_val |= NAU8540_I2S_DF_PCM_AB;
-               break;
-       case SND_SOC_DAIFMT_DSP_B:
-               ctrl1_val |= NAU8540_I2S_DF_PCM_AB;
-               ctrl1_val |= NAU8540_I2S_PCMB_EN;
-               break;
-       default:
-               return -EINVAL;
-       }
-
-       regmap_update_bits(nau8540->regmap, NAU8540_REG_PCM_CTRL0,
-               NAU8540_I2S_DL_MASK | NAU8540_I2S_DF_MASK |
-               NAU8540_I2S_BP_INV | NAU8540_I2S_PCMB_EN, ctrl1_val);
-       regmap_update_bits(nau8540->regmap, NAU8540_REG_PCM_CTRL1,
-               NAU8540_I2S_MS_MASK | NAU8540_I2S_DO12_OE, ctrl2_val);
-       regmap_update_bits(nau8540->regmap, NAU8540_REG_PCM_CTRL2,
-               NAU8540_I2S_DO34_OE, 0);
-
-       return 0;
+	struct snd_soc_codec *codec = dai->codec;
+	struct nau8540 *nau8540 = snd_soc_codec_get_drvdata(codec);
+	unsigned int ctrl1_val = 0, ctrl2_val = 0;
+
+	switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+	case SND_SOC_DAIFMT_CBM_CFM:
+		ctrl2_val |= NAU8540_I2S_MS_MASTER;
+		break;
+	case SND_SOC_DAIFMT_CBS_CFS:
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+	case SND_SOC_DAIFMT_NB_NF:
+		break;
+	case SND_SOC_DAIFMT_IB_NF:
+		ctrl1_val |= NAU8540_I2S_BP_INV;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+	case SND_SOC_DAIFMT_I2S:
+		ctrl1_val |= NAU8540_I2S_DF_I2S;
+		break;
+	case SND_SOC_DAIFMT_LEFT_J:
+		ctrl1_val |= NAU8540_I2S_DF_LEFT;
+		break;
+	case SND_SOC_DAIFMT_RIGHT_J:
+		ctrl1_val |= NAU8540_I2S_DF_RIGTH;
+		break;
+	case SND_SOC_DAIFMT_DSP_A:
+		ctrl1_val |= NAU8540_I2S_DF_PCM_AB;
+		break;
+	case SND_SOC_DAIFMT_DSP_B:
+		ctrl1_val |= NAU8540_I2S_DF_PCM_AB;
+		ctrl1_val |= NAU8540_I2S_PCMB_EN;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	regmap_update_bits(nau8540->regmap, NAU8540_REG_PCM_CTRL0,
+		NAU8540_I2S_DL_MASK | NAU8540_I2S_DF_MASK |
+		NAU8540_I2S_BP_INV | NAU8540_I2S_PCMB_EN, ctrl1_val);
+	regmap_update_bits(nau8540->regmap, NAU8540_REG_PCM_CTRL1,
+		NAU8540_I2S_MS_MASK | NAU8540_I2S_DO12_OE, ctrl2_val);
+	regmap_update_bits(nau8540->regmap, NAU8540_REG_PCM_CTRL2,
+		NAU8540_I2S_DO34_OE, 0);
+
+	return 0;
 }
 
 /**
@@ -451,55 +451,55 @@ static int nau8540_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
  * Configures a DAI for TDM operation. Only support 4 slots TDM.
  */
 static int nau8540_set_tdm_slot(struct snd_soc_dai *dai,
-       unsigned int tx_mask, unsigned int rx_mask, int slots, int slot_width)
+	unsigned int tx_mask, unsigned int rx_mask, int slots, int slot_width)
 {
-       struct snd_soc_codec *codec = dai->codec;
-       struct nau8540 *nau8540 = snd_soc_codec_get_drvdata(codec);
-       unsigned int ctrl2_val = 0, ctrl4_val = 0;
-
-       if (slots > 4 || ((tx_mask & 0xf0) && (tx_mask & 0xf)))
-               return -EINVAL;
-
-       ctrl4_val |= (NAU8540_TDM_MODE | NAU8540_TDM_OFFSET_EN);
-       if (tx_mask & 0xf0) {
-               ctrl2_val = 4 * slot_width;
-               ctrl4_val |= (tx_mask >> 4);
-       } else {
-               ctrl4_val |= tx_mask;
-       }
-       regmap_update_bits(nau8540->regmap, NAU8540_REG_PCM_CTRL4,
-               NAU8540_TDM_MODE | NAU8540_TDM_OFFSET_EN |
-               NAU8540_TDM_TX_MASK, ctrl4_val);
-       regmap_update_bits(nau8540->regmap, NAU8540_REG_PCM_CTRL1,
-               NAU8540_I2S_DO12_OE, NAU8540_I2S_DO12_OE);
-       regmap_update_bits(nau8540->regmap, NAU8540_REG_PCM_CTRL2,
-               NAU8540_I2S_DO34_OE | NAU8540_I2S_TSLOT_L_MASK,
-               NAU8540_I2S_DO34_OE | ctrl2_val);
-
-       return 0;
+	struct snd_soc_codec *codec = dai->codec;
+	struct nau8540 *nau8540 = snd_soc_codec_get_drvdata(codec);
+	unsigned int ctrl2_val = 0, ctrl4_val = 0;
+
+	if (slots > 4 || ((tx_mask & 0xf0) && (tx_mask & 0xf)))
+		return -EINVAL;
+
+	ctrl4_val |= (NAU8540_TDM_MODE | NAU8540_TDM_OFFSET_EN);
+	if (tx_mask & 0xf0) {
+		ctrl2_val = 4 * slot_width;
+		ctrl4_val |= (tx_mask >> 4);
+	} else {
+		ctrl4_val |= tx_mask;
+	}
+	regmap_update_bits(nau8540->regmap, NAU8540_REG_PCM_CTRL4,
+		NAU8540_TDM_MODE | NAU8540_TDM_OFFSET_EN |
+		NAU8540_TDM_TX_MASK, ctrl4_val);
+	regmap_update_bits(nau8540->regmap, NAU8540_REG_PCM_CTRL1,
+		NAU8540_I2S_DO12_OE, NAU8540_I2S_DO12_OE);
+	regmap_update_bits(nau8540->regmap, NAU8540_REG_PCM_CTRL2,
+		NAU8540_I2S_DO34_OE | NAU8540_I2S_TSLOT_L_MASK,
+		NAU8540_I2S_DO34_OE | ctrl2_val);
+
+	return 0;
 }
 
 
 static const struct snd_soc_dai_ops nau8540_dai_ops = {
-       .hw_params = nau8540_hw_params,
-       .set_fmt = nau8540_set_fmt,
-       .set_tdm_slot = nau8540_set_tdm_slot,
+	.hw_params = nau8540_hw_params,
+	.set_fmt = nau8540_set_fmt,
+	.set_tdm_slot = nau8540_set_tdm_slot,
 };
 
 #define NAU8540_RATES SNDRV_PCM_RATE_8000_48000
 #define NAU8540_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE \
-        | SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S32_LE)
+	 | SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S32_LE)
 
 static struct snd_soc_dai_driver nau8540_dai = {
-       .name = "nau8540-hifi",
-       .capture = {
-               .stream_name = "Capture",
-               .channels_min = 1,
-               .channels_max = 4,
-               .rates = NAU8540_RATES,
-               .formats = NAU8540_FORMATS,
-       },
-       .ops = &nau8540_dai_ops,
+	.name = "nau8540-hifi",
+	.capture = {
+		.stream_name = "Capture",
+		.channels_min = 1,
+		.channels_max = 4,
+		.rates = NAU8540_RATES,
+		.formats = NAU8540_FORMATS,
+	},
+	.ops = &nau8540_dai_ops,
 };
 
 /**
@@ -513,320 +513,320 @@ static struct snd_soc_dai_driver nau8540_dai = {
  * Returns 0 for success or negative error code.
  */
 static int nau8540_calc_fll_param(unsigned int fll_in,
-       unsigned int fs, struct nau8540_fll *fll_param)
+	unsigned int fs, struct nau8540_fll *fll_param)
 {
-       u64 fvco, fvco_max;
-       unsigned int fref, i, fvco_sel;
-
-       /* Ensure the reference clock frequency (FREF) is <= 13.5MHz by dividing
-        * freq_in by 1, 2, 4, or 8 using FLL pre-scalar.
-        * FREF = freq_in / NAU8540_FLL_REF_DIV_MASK
-        */
-       for (i = 0; i < ARRAY_SIZE(fll_pre_scalar); i++) {
-               fref = fll_in / fll_pre_scalar[i].param;
-               if (fref <= NAU_FREF_MAX)
-                       break;
-       }
-       if (i == ARRAY_SIZE(fll_pre_scalar))
-               return -EINVAL;
-       fll_param->clk_ref_div = fll_pre_scalar[i].val;
-
-       /* Choose the FLL ratio based on FREF */
-       for (i = 0; i < ARRAY_SIZE(fll_ratio); i++) {
-               if (fref >= fll_ratio[i].param)
-                       break;
-       }
-       if (i == ARRAY_SIZE(fll_ratio))
-               return -EINVAL;
-       fll_param->ratio = fll_ratio[i].val;
-
-       /* Calculate the frequency of DCO (FDCO) given freq_out = 256 * Fs.
-        * FDCO must be within the 90MHz - 124MHz or the FFL cannot be
-        * guaranteed across the full range of operation.
-        * FDCO = freq_out * 2 * mclk_src_scaling
-        */
-       fvco_max = 0;
-       fvco_sel = ARRAY_SIZE(mclk_src_scaling);
-       for (i = 0; i < ARRAY_SIZE(mclk_src_scaling); i++) {
-               fvco = 256 * fs * 2 * mclk_src_scaling[i].param;
-               if (fvco > NAU_FVCO_MIN && fvco < NAU_FVCO_MAX &&
-                       fvco_max < fvco) {
-                       fvco_max = fvco;
-                       fvco_sel = i;
-               }
-       }
-       if (ARRAY_SIZE(mclk_src_scaling) == fvco_sel)
-               return -EINVAL;
-       fll_param->mclk_src = mclk_src_scaling[fvco_sel].val;
-
-       /* Calculate the FLL 10-bit integer input and the FLL 16-bit fractional
-        * input based on FDCO, FREF and FLL ratio.
-        */
-       fvco = div_u64(fvco_max << 16, fref * fll_param->ratio);
-       fll_param->fll_int = (fvco >> 16) & 0x3FF;
-       fll_param->fll_frac = fvco & 0xFFFF;
-       return 0;
+	u64 fvco, fvco_max;
+	unsigned int fref, i, fvco_sel;
+
+	/* Ensure the reference clock frequency (FREF) is <= 13.5MHz by dividing
+	 * freq_in by 1, 2, 4, or 8 using FLL pre-scalar.
+	 * FREF = freq_in / NAU8540_FLL_REF_DIV_MASK
+	 */
+	for (i = 0; i < ARRAY_SIZE(fll_pre_scalar); i++) {
+		fref = fll_in / fll_pre_scalar[i].param;
+		if (fref <= NAU_FREF_MAX)
+			break;
+	}
+	if (i == ARRAY_SIZE(fll_pre_scalar))
+		return -EINVAL;
+	fll_param->clk_ref_div = fll_pre_scalar[i].val;
+
+	/* Choose the FLL ratio based on FREF */
+	for (i = 0; i < ARRAY_SIZE(fll_ratio); i++) {
+		if (fref >= fll_ratio[i].param)
+			break;
+	}
+	if (i == ARRAY_SIZE(fll_ratio))
+		return -EINVAL;
+	fll_param->ratio = fll_ratio[i].val;
+
+	/* Calculate the frequency of DCO (FDCO) given freq_out = 256 * Fs.
+	 * FDCO must be within the 90MHz - 124MHz or the FFL cannot be
+	 * guaranteed across the full range of operation.
+	 * FDCO = freq_out * 2 * mclk_src_scaling
+	 */
+	fvco_max = 0;
+	fvco_sel = ARRAY_SIZE(mclk_src_scaling);
+	for (i = 0; i < ARRAY_SIZE(mclk_src_scaling); i++) {
+		fvco = 256 * fs * 2 * mclk_src_scaling[i].param;
+		if (fvco > NAU_FVCO_MIN && fvco < NAU_FVCO_MAX &&
+			fvco_max < fvco) {
+			fvco_max = fvco;
+			fvco_sel = i;
+		}
+	}
+	if (ARRAY_SIZE(mclk_src_scaling) == fvco_sel)
+		return -EINVAL;
+	fll_param->mclk_src = mclk_src_scaling[fvco_sel].val;
+
+	/* Calculate the FLL 10-bit integer input and the FLL 16-bit fractional
+	 * input based on FDCO, FREF and FLL ratio.
+	 */
+	fvco = div_u64(fvco_max << 16, fref * fll_param->ratio);
+	fll_param->fll_int = (fvco >> 16) & 0x3FF;
+	fll_param->fll_frac = fvco & 0xFFFF;
+	return 0;
 }
 
 static void nau8540_fll_apply(struct regmap *regmap,
-       struct nau8540_fll *fll_param)
+	struct nau8540_fll *fll_param)
 {
-       regmap_update_bits(regmap, NAU8540_REG_CLOCK_SRC,
-               NAU8540_CLK_SRC_MASK | NAU8540_CLK_MCLK_SRC_MASK,
-               NAU8540_CLK_SRC_MCLK | fll_param->mclk_src);
-       regmap_update_bits(regmap, NAU8540_REG_FLL1,
-               NAU8540_FLL_RATIO_MASK, fll_param->ratio);
-       /* FLL 16-bit fractional input */
-       regmap_write(regmap, NAU8540_REG_FLL2, fll_param->fll_frac);
-       /* FLL 10-bit integer input */
-       regmap_update_bits(regmap, NAU8540_REG_FLL3,
-               NAU8540_FLL_INTEGER_MASK, fll_param->fll_int);
-       /* FLL pre-scaler */
-       regmap_update_bits(regmap, NAU8540_REG_FLL4,
-               NAU8540_FLL_REF_DIV_MASK,
-               fll_param->clk_ref_div << NAU8540_FLL_REF_DIV_SFT);
-       regmap_update_bits(regmap, NAU8540_REG_FLL5,
-               NAU8540_FLL_CLK_SW_MASK, NAU8540_FLL_CLK_SW_REF);
-       regmap_update_bits(regmap,
-               NAU8540_REG_FLL6, NAU8540_DCO_EN, 0);
-       if (fll_param->fll_frac) {
-               regmap_update_bits(regmap, NAU8540_REG_FLL5,
-                       NAU8540_FLL_PDB_DAC_EN | NAU8540_FLL_LOOP_FTR_EN |
-                       NAU8540_FLL_FTR_SW_MASK,
-                       NAU8540_FLL_PDB_DAC_EN | NAU8540_FLL_LOOP_FTR_EN |
-                       NAU8540_FLL_FTR_SW_FILTER);
-               regmap_update_bits(regmap, NAU8540_REG_FLL6,
-                       NAU8540_SDM_EN, NAU8540_SDM_EN);
-       } else {
-               regmap_update_bits(regmap, NAU8540_REG_FLL5,
-                       NAU8540_FLL_PDB_DAC_EN | NAU8540_FLL_LOOP_FTR_EN |
-                       NAU8540_FLL_FTR_SW_MASK, NAU8540_FLL_FTR_SW_ACCU);
-               regmap_update_bits(regmap,
-                       NAU8540_REG_FLL6, NAU8540_SDM_EN, 0);
-       }
+	regmap_update_bits(regmap, NAU8540_REG_CLOCK_SRC,
+		NAU8540_CLK_SRC_MASK | NAU8540_CLK_MCLK_SRC_MASK,
+		NAU8540_CLK_SRC_MCLK | fll_param->mclk_src);
+	regmap_update_bits(regmap, NAU8540_REG_FLL1,
+		NAU8540_FLL_RATIO_MASK, fll_param->ratio);
+	/* FLL 16-bit fractional input */
+	regmap_write(regmap, NAU8540_REG_FLL2, fll_param->fll_frac);
+	/* FLL 10-bit integer input */
+	regmap_update_bits(regmap, NAU8540_REG_FLL3,
+		NAU8540_FLL_INTEGER_MASK, fll_param->fll_int);
+	/* FLL pre-scaler */
+	regmap_update_bits(regmap, NAU8540_REG_FLL4,
+		NAU8540_FLL_REF_DIV_MASK,
+		fll_param->clk_ref_div << NAU8540_FLL_REF_DIV_SFT);
+	regmap_update_bits(regmap, NAU8540_REG_FLL5,
+		NAU8540_FLL_CLK_SW_MASK, NAU8540_FLL_CLK_SW_REF);
+	regmap_update_bits(regmap,
+		NAU8540_REG_FLL6, NAU8540_DCO_EN, 0);
+	if (fll_param->fll_frac) {
+		regmap_update_bits(regmap, NAU8540_REG_FLL5,
+			NAU8540_FLL_PDB_DAC_EN | NAU8540_FLL_LOOP_FTR_EN |
+			NAU8540_FLL_FTR_SW_MASK,
+			NAU8540_FLL_PDB_DAC_EN | NAU8540_FLL_LOOP_FTR_EN |
+			NAU8540_FLL_FTR_SW_FILTER);
+		regmap_update_bits(regmap, NAU8540_REG_FLL6,
+			NAU8540_SDM_EN, NAU8540_SDM_EN);
+	} else {
+		regmap_update_bits(regmap, NAU8540_REG_FLL5,
+			NAU8540_FLL_PDB_DAC_EN | NAU8540_FLL_LOOP_FTR_EN |
+			NAU8540_FLL_FTR_SW_MASK, NAU8540_FLL_FTR_SW_ACCU);
+		regmap_update_bits(regmap,
+			NAU8540_REG_FLL6, NAU8540_SDM_EN, 0);
+	}
 }
 
 /* freq_out must be 256*Fs in order to achieve the best performance */
 static int nau8540_set_pll(struct snd_soc_codec *codec, int pll_id, int source,
-               unsigned int freq_in, unsigned int freq_out)
+		unsigned int freq_in, unsigned int freq_out)
 {
-       struct nau8540 *nau8540 = snd_soc_codec_get_drvdata(codec);
-       struct nau8540_fll fll_param;
-       int ret, fs;
-
-       switch (pll_id) {
-       case NAU8540_CLK_FLL_MCLK:
-               regmap_update_bits(nau8540->regmap, NAU8540_REG_FLL3,
-                       NAU8540_FLL_CLK_SRC_MASK, NAU8540_FLL_CLK_SRC_MCLK);
-               break;
-
-       case NAU8540_CLK_FLL_BLK:
-               regmap_update_bits(nau8540->regmap, NAU8540_REG_FLL3,
-                       NAU8540_FLL_CLK_SRC_MASK, NAU8540_FLL_CLK_SRC_BLK);
-               break;
-
-       case NAU8540_CLK_FLL_FS:
-               regmap_update_bits(nau8540->regmap, NAU8540_REG_FLL3,
-                       NAU8540_FLL_CLK_SRC_MASK, NAU8540_FLL_CLK_SRC_FS);
-               break;
-
-       default:
-               dev_err(nau8540->dev, "Invalid clock id (%d)\n", pll_id);
-               return -EINVAL;
-       }
-       dev_dbg(nau8540->dev, "Sysclk is %dHz and clock id is %d\n",
-               freq_out, pll_id);
-
-       fs = freq_out / 256;
-       ret = nau8540_calc_fll_param(freq_in, fs, &fll_param);
-       if (ret < 0) {
-               dev_err(nau8540->dev, "Unsupported input clock %d\n", freq_in);
-               return ret;
-       }
-       dev_dbg(nau8540->dev, "mclk_src=%x ratio=%x fll_frac=%x fll_int=%x clk_ref_div=%x\n",
-               fll_param.mclk_src, fll_param.ratio, fll_param.fll_frac,
-               fll_param.fll_int, fll_param.clk_ref_div);
-
-       nau8540_fll_apply(nau8540->regmap, &fll_param);
-       mdelay(2);
-       regmap_update_bits(nau8540->regmap, NAU8540_REG_CLOCK_SRC,
-               NAU8540_CLK_SRC_MASK, NAU8540_CLK_SRC_VCO);
-
-       return 0;
+	struct nau8540 *nau8540 = snd_soc_codec_get_drvdata(codec);
+	struct nau8540_fll fll_param;
+	int ret, fs;
+
+	switch (pll_id) {
+	case NAU8540_CLK_FLL_MCLK:
+		regmap_update_bits(nau8540->regmap, NAU8540_REG_FLL3,
+			NAU8540_FLL_CLK_SRC_MASK, NAU8540_FLL_CLK_SRC_MCLK);
+		break;
+
+	case NAU8540_CLK_FLL_BLK:
+		regmap_update_bits(nau8540->regmap, NAU8540_REG_FLL3,
+			NAU8540_FLL_CLK_SRC_MASK, NAU8540_FLL_CLK_SRC_BLK);
+		break;
+
+	case NAU8540_CLK_FLL_FS:
+		regmap_update_bits(nau8540->regmap, NAU8540_REG_FLL3,
+			NAU8540_FLL_CLK_SRC_MASK, NAU8540_FLL_CLK_SRC_FS);
+		break;
+
+	default:
+		dev_err(nau8540->dev, "Invalid clock id (%d)\n", pll_id);
+		return -EINVAL;
+	}
+	dev_dbg(nau8540->dev, "Sysclk is %dHz and clock id is %d\n",
+		freq_out, pll_id);
+
+	fs = freq_out / 256;
+	ret = nau8540_calc_fll_param(freq_in, fs, &fll_param);
+	if (ret < 0) {
+		dev_err(nau8540->dev, "Unsupported input clock %d\n", freq_in);
+		return ret;
+	}
+	dev_dbg(nau8540->dev, "mclk_src=%x ratio=%x fll_frac=%x fll_int=%x clk_ref_div=%x\n",
+		fll_param.mclk_src, fll_param.ratio, fll_param.fll_frac,
+		fll_param.fll_int, fll_param.clk_ref_div);
+
+	nau8540_fll_apply(nau8540->regmap, &fll_param);
+	mdelay(2);
+	regmap_update_bits(nau8540->regmap, NAU8540_REG_CLOCK_SRC,
+		NAU8540_CLK_SRC_MASK, NAU8540_CLK_SRC_VCO);
+
+	return 0;
 }
 
 static int nau8540_set_sysclk(struct snd_soc_codec *codec,
-       int clk_id, int source, unsigned int freq, int dir)
+	int clk_id, int source, unsigned int freq, int dir)
 {
-       struct nau8540 *nau8540 = snd_soc_codec_get_drvdata(codec);
-
-       switch (clk_id) {
-       case NAU8540_CLK_DIS:
-       case NAU8540_CLK_MCLK:
-               regmap_update_bits(nau8540->regmap, NAU8540_REG_CLOCK_SRC,
-                       NAU8540_CLK_SRC_MASK, NAU8540_CLK_SRC_MCLK);
-               regmap_update_bits(nau8540->regmap, NAU8540_REG_FLL6,
-                       NAU8540_DCO_EN, 0);
-               break;
-
-       case NAU8540_CLK_INTERNAL:
-               regmap_update_bits(nau8540->regmap, NAU8540_REG_FLL6,
-                       NAU8540_DCO_EN, NAU8540_DCO_EN);
-               regmap_update_bits(nau8540->regmap, NAU8540_REG_CLOCK_SRC,
-                       NAU8540_CLK_SRC_MASK, NAU8540_CLK_SRC_VCO);
-               break;
-
-       default:
-               dev_err(nau8540->dev, "Invalid clock id (%d)\n", clk_id);
-               return -EINVAL;
-       }
-
-       dev_dbg(nau8540->dev, "Sysclk is %dHz and clock id is %d\n",
-               freq, clk_id);
-
-       return 0;
+	struct nau8540 *nau8540 = snd_soc_codec_get_drvdata(codec);
+
+	switch (clk_id) {
+	case NAU8540_CLK_DIS:
+	case NAU8540_CLK_MCLK:
+		regmap_update_bits(nau8540->regmap, NAU8540_REG_CLOCK_SRC,
+			NAU8540_CLK_SRC_MASK, NAU8540_CLK_SRC_MCLK);
+		regmap_update_bits(nau8540->regmap, NAU8540_REG_FLL6,
+			NAU8540_DCO_EN, 0);
+		break;
+
+	case NAU8540_CLK_INTERNAL:
+		regmap_update_bits(nau8540->regmap, NAU8540_REG_FLL6,
+			NAU8540_DCO_EN, NAU8540_DCO_EN);
+		regmap_update_bits(nau8540->regmap, NAU8540_REG_CLOCK_SRC,
+			NAU8540_CLK_SRC_MASK, NAU8540_CLK_SRC_VCO);
+		break;
+
+	default:
+		dev_err(nau8540->dev, "Invalid clock id (%d)\n", clk_id);
+		return -EINVAL;
+	}
+
+	dev_dbg(nau8540->dev, "Sysclk is %dHz and clock id is %d\n",
+		freq, clk_id);
+
+	return 0;
 }
 
 static void nau8540_reset_chip(struct regmap *regmap)
 {
-       regmap_write(regmap, NAU8540_REG_SW_RESET, 0x00);
-       regmap_write(regmap, NAU8540_REG_SW_RESET, 0x00);
+	regmap_write(regmap, NAU8540_REG_SW_RESET, 0x00);
+	regmap_write(regmap, NAU8540_REG_SW_RESET, 0x00);
 }
 
 static void nau8540_init_regs(struct nau8540 *nau8540)
 {
-       struct regmap *regmap = nau8540->regmap;
-
-       /* Enable Bias/VMID/VMID Tieoff */
-       regmap_update_bits(regmap, NAU8540_REG_VMID_CTRL,
-               NAU8540_VMID_EN | NAU8540_VMID_SEL_MASK,
-               NAU8540_VMID_EN | (0x2 << NAU8540_VMID_SEL_SFT));
-       regmap_update_bits(regmap, NAU8540_REG_REFERENCE,
-               NAU8540_PRECHARGE_DIS | NAU8540_GLOBAL_BIAS_EN,
-               NAU8540_PRECHARGE_DIS | NAU8540_GLOBAL_BIAS_EN);
-       mdelay(2);
-       regmap_update_bits(regmap, NAU8540_REG_MIC_BIAS,
-               NAU8540_PU_PRE, NAU8540_PU_PRE);
-       regmap_update_bits(regmap, NAU8540_REG_CLOCK_CTRL,
-               NAU8540_CLK_ADC_EN | NAU8540_CLK_I2S_EN,
-               NAU8540_CLK_ADC_EN | NAU8540_CLK_I2S_EN);
-       /* ADC OSR selection, CLK_ADC = Fs * OSR */
-       regmap_update_bits(regmap, NAU8540_REG_ADC_SAMPLE_RATE,
-               NAU8540_ADC_OSR_MASK, NAU8540_ADC_OSR_64);
+	struct regmap *regmap = nau8540->regmap;
+
+	/* Enable Bias/VMID/VMID Tieoff */
+	regmap_update_bits(regmap, NAU8540_REG_VMID_CTRL,
+		NAU8540_VMID_EN | NAU8540_VMID_SEL_MASK,
+		NAU8540_VMID_EN | (0x2 << NAU8540_VMID_SEL_SFT));
+	regmap_update_bits(regmap, NAU8540_REG_REFERENCE,
+		NAU8540_PRECHARGE_DIS | NAU8540_GLOBAL_BIAS_EN,
+		NAU8540_PRECHARGE_DIS | NAU8540_GLOBAL_BIAS_EN);
+	mdelay(2);
+	regmap_update_bits(regmap, NAU8540_REG_MIC_BIAS,
+		NAU8540_PU_PRE, NAU8540_PU_PRE);
+	regmap_update_bits(regmap, NAU8540_REG_CLOCK_CTRL,
+		NAU8540_CLK_ADC_EN | NAU8540_CLK_I2S_EN,
+		NAU8540_CLK_ADC_EN | NAU8540_CLK_I2S_EN);
+	/* ADC OSR selection, CLK_ADC = Fs * OSR */
+	regmap_update_bits(regmap, NAU8540_REG_ADC_SAMPLE_RATE,
+		NAU8540_ADC_OSR_MASK, NAU8540_ADC_OSR_64);
 }
 
 static int __maybe_unused nau8540_suspend(struct snd_soc_codec *codec)
 {
-       struct nau8540 *nau8540 = snd_soc_codec_get_drvdata(codec);
+	struct nau8540 *nau8540 = snd_soc_codec_get_drvdata(codec);
 
-       regcache_cache_only(nau8540->regmap, true);
-       regcache_mark_dirty(nau8540->regmap);
+	regcache_cache_only(nau8540->regmap, true);
+	regcache_mark_dirty(nau8540->regmap);
 
-       return 0;
+	return 0;
 }
 
 static int __maybe_unused nau8540_resume(struct snd_soc_codec *codec)
 {
-       struct nau8540 *nau8540 = snd_soc_codec_get_drvdata(codec);
+	struct nau8540 *nau8540 = snd_soc_codec_get_drvdata(codec);
 
-       regcache_cache_only(nau8540->regmap, false);
-       regcache_sync(nau8540->regmap);
+	regcache_cache_only(nau8540->regmap, false);
+	regcache_sync(nau8540->regmap);
 
-       return 0;
+	return 0;
 }
 
 static struct snd_soc_codec_driver nau8540_codec_driver = {
-       .set_sysclk = nau8540_set_sysclk,
-       .set_pll = nau8540_set_pll,
-       .suspend = nau8540_suspend,
-       .resume = nau8540_resume,
-       .suspend_bias_off = true,
-
-       .component_driver = {
-               .controls = nau8540_snd_controls,
-               .num_controls = ARRAY_SIZE(nau8540_snd_controls),
-               .dapm_widgets = nau8540_dapm_widgets,
-               .num_dapm_widgets = ARRAY_SIZE(nau8540_dapm_widgets),
-               .dapm_routes = nau8540_dapm_routes,
-               .num_dapm_routes = ARRAY_SIZE(nau8540_dapm_routes),
-       },
+	.set_sysclk = nau8540_set_sysclk,
+	.set_pll = nau8540_set_pll,
+	.suspend = nau8540_suspend,
+	.resume = nau8540_resume,
+	.suspend_bias_off = true,
+
+	.component_driver = {
+		.controls = nau8540_snd_controls,
+		.num_controls = ARRAY_SIZE(nau8540_snd_controls),
+		.dapm_widgets = nau8540_dapm_widgets,
+		.num_dapm_widgets = ARRAY_SIZE(nau8540_dapm_widgets),
+		.dapm_routes = nau8540_dapm_routes,
+		.num_dapm_routes = ARRAY_SIZE(nau8540_dapm_routes),
+	},
 };
 
 static const struct regmap_config nau8540_regmap_config = {
-       .val_bits = 16,
-       .reg_bits = 16,
+	.val_bits = 16,
+	.reg_bits = 16,
 
-       .max_register = NAU8540_REG_MAX,
-       .readable_reg = nau8540_readable_reg,
-       .writeable_reg = nau8540_writeable_reg,
-       .volatile_reg = nau8540_volatile_reg,
+	.max_register = NAU8540_REG_MAX,
+	.readable_reg = nau8540_readable_reg,
+	.writeable_reg = nau8540_writeable_reg,
+	.volatile_reg = nau8540_volatile_reg,
 
-       .cache_type = REGCACHE_RBTREE,
-       .reg_defaults = nau8540_reg_defaults,
-       .num_reg_defaults = ARRAY_SIZE(nau8540_reg_defaults),
+	.cache_type = REGCACHE_RBTREE,
+	.reg_defaults = nau8540_reg_defaults,
+	.num_reg_defaults = ARRAY_SIZE(nau8540_reg_defaults),
 };
 
 static int nau8540_i2c_probe(struct i2c_client *i2c,
-       const struct i2c_device_id *id)
+	const struct i2c_device_id *id)
 {
-       struct device *dev = &i2c->dev;
-       struct nau8540 *nau8540 = dev_get_platdata(dev);
-       int ret, value;
-
-       if (!nau8540) {
-               nau8540 = devm_kzalloc(dev, sizeof(*nau8540), GFP_KERNEL);
-               if (!nau8540)
-                       return -ENOMEM;
-       }
-       i2c_set_clientdata(i2c, nau8540);
-
-       nau8540->regmap = devm_regmap_init_i2c(i2c, &nau8540_regmap_config);
-       if (IS_ERR(nau8540->regmap))
-               return PTR_ERR(nau8540->regmap);
-       ret = regmap_read(nau8540->regmap, NAU8540_REG_I2C_DEVICE_ID, &value);
-       if (ret < 0) {
-               dev_err(dev, "Failed to read device id from the NAU85L40: %d\n",
-                       ret);
-               return ret;
-       }
-
-       nau8540->dev = dev;
-       nau8540_reset_chip(nau8540->regmap);
-       nau8540_init_regs(nau8540);
-
-       return snd_soc_register_codec(dev,
-               &nau8540_codec_driver, &nau8540_dai, 1);
+	struct device *dev = &i2c->dev;
+	struct nau8540 *nau8540 = dev_get_platdata(dev);
+	int ret, value;
+
+	if (!nau8540) {
+		nau8540 = devm_kzalloc(dev, sizeof(*nau8540), GFP_KERNEL);
+		if (!nau8540)
+			return -ENOMEM;
+	}
+	i2c_set_clientdata(i2c, nau8540);
+
+	nau8540->regmap = devm_regmap_init_i2c(i2c, &nau8540_regmap_config);
+	if (IS_ERR(nau8540->regmap))
+		return PTR_ERR(nau8540->regmap);
+	ret = regmap_read(nau8540->regmap, NAU8540_REG_I2C_DEVICE_ID, &value);
+	if (ret < 0) {
+		dev_err(dev, "Failed to read device id from the NAU85L40: %d\n",
+			ret);
+		return ret;
+	}
+
+	nau8540->dev = dev;
+	nau8540_reset_chip(nau8540->regmap);
+	nau8540_init_regs(nau8540);
+
+	return snd_soc_register_codec(dev,
+		&nau8540_codec_driver, &nau8540_dai, 1);
 }
 
 static int nau8540_i2c_remove(struct i2c_client *client)
 {
-       snd_soc_unregister_codec(&client->dev);
-       return 0;
+	snd_soc_unregister_codec(&client->dev);
+	return 0;
 }
 
 
 static const struct i2c_device_id nau8540_i2c_ids[] = {
-       { "nau8540", 0 },
-       { }
+	{ "nau8540", 0 },
+	{ }
 };
 MODULE_DEVICE_TABLE(i2c, nau8540_i2c_ids);
 
 #ifdef CONFIG_OF
 static const struct of_device_id nau8540_of_ids[] = {
-       { .compatible = "nuvoton,nau8540", },
-       {}
+	{ .compatible = "nuvoton,nau8540", },
+	{}
 };
 MODULE_DEVICE_TABLE(of, nau8540_of_ids);
 #endif
 
 static struct i2c_driver nau8540_i2c_driver = {
-       .driver = {
-               .name = "nau8540",
-               .of_match_table = of_match_ptr(nau8540_of_ids),
-       },
-       .probe = nau8540_i2c_probe,
-       .remove = nau8540_i2c_remove,
-       .id_table = nau8540_i2c_ids,
+	.driver = {
+		.name = "nau8540",
+		.of_match_table = of_match_ptr(nau8540_of_ids),
+	},
+	.probe = nau8540_i2c_probe,
+	.remove = nau8540_i2c_remove,
+	.id_table = nau8540_i2c_ids,
 };
 module_i2c_driver(nau8540_i2c_driver);
 
diff --git a/sound/soc/codecs/nau8540.h b/sound/soc/codecs/nau8540.h
index d06e65188cd5..5db5b224944d 100644
--- a/sound/soc/codecs/nau8540.h
+++ b/sound/soc/codecs/nau8540.h
@@ -12,211 +12,211 @@
 #ifndef __NAU8540_H__
 #define __NAU8540_H__
 
-#define NAU8540_REG_SW_RESET                   0x00
-#define NAU8540_REG_POWER_MANAGEMENT   0x01
-#define NAU8540_REG_CLOCK_CTRL         0x02
-#define NAU8540_REG_CLOCK_SRC                  0x03
-#define NAU8540_REG_FLL1                       0x04
-#define NAU8540_REG_FLL2                       0x05
-#define NAU8540_REG_FLL3                       0x06
-#define NAU8540_REG_FLL4                       0x07
-#define NAU8540_REG_FLL5                       0x08
-#define NAU8540_REG_FLL6                       0x09
-#define NAU8540_REG_FLL_VCO_RSV                0x0A
-#define NAU8540_REG_PCM_CTRL0                  0x10
-#define NAU8540_REG_PCM_CTRL1                  0x11
-#define NAU8540_REG_PCM_CTRL2                  0x12
-#define NAU8540_REG_PCM_CTRL3                  0x13
-#define NAU8540_REG_PCM_CTRL4                  0x14
-#define NAU8540_REG_ALC_CONTROL_1              0x20
-#define NAU8540_REG_ALC_CONTROL_2              0x21
-#define NAU8540_REG_ALC_CONTROL_3              0x22
-#define NAU8540_REG_ALC_CONTROL_4              0x23
-#define NAU8540_REG_ALC_CONTROL_5              0x24
-#define NAU8540_REG_ALC_GAIN_CH12              0x2D
-#define NAU8540_REG_ALC_GAIN_CH34              0x2E
-#define NAU8540_REG_ALC_STATUS         0x2F
-#define NAU8540_REG_NOTCH_FIL1_CH1             0x30
-#define NAU8540_REG_NOTCH_FIL2_CH1             0x31
-#define NAU8540_REG_NOTCH_FIL1_CH2             0x32
-#define NAU8540_REG_NOTCH_FIL2_CH2             0x33
-#define NAU8540_REG_NOTCH_FIL1_CH3             0x34
-#define NAU8540_REG_NOTCH_FIL2_CH3             0x35
-#define NAU8540_REG_NOTCH_FIL1_CH4             0x36
-#define NAU8540_REG_NOTCH_FIL2_CH4             0x37
-#define NAU8540_REG_HPF_FILTER_CH12            0x38
-#define NAU8540_REG_HPF_FILTER_CH34            0x39
-#define NAU8540_REG_ADC_SAMPLE_RATE            0x3A
-#define NAU8540_REG_DIGITAL_GAIN_CH1           0x40
-#define NAU8540_REG_DIGITAL_GAIN_CH2           0x41
-#define NAU8540_REG_DIGITAL_GAIN_CH3           0x42
-#define NAU8540_REG_DIGITAL_GAIN_CH4           0x43
-#define NAU8540_REG_DIGITAL_MUX                0x44
-#define NAU8540_REG_P2P_CH1                    0x48
-#define NAU8540_REG_P2P_CH2                    0x49
-#define NAU8540_REG_P2P_CH3                    0x4A
-#define NAU8540_REG_P2P_CH4                    0x4B
-#define NAU8540_REG_PEAK_CH1                   0x4C
-#define NAU8540_REG_PEAK_CH2                   0x4D
-#define NAU8540_REG_PEAK_CH3                   0x4E
-#define NAU8540_REG_PEAK_CH4                   0x4F
-#define NAU8540_REG_GPIO_CTRL                  0x50
-#define NAU8540_REG_MISC_CTRL                  0x51
-#define NAU8540_REG_I2C_CTRL                   0x52
-#define NAU8540_REG_I2C_DEVICE_ID              0x58
-#define NAU8540_REG_RST                        0x5A
-#define NAU8540_REG_VMID_CTRL                  0x60
-#define NAU8540_REG_MUTE                       0x61
-#define NAU8540_REG_ANALOG_ADC1                0x64
-#define NAU8540_REG_ANALOG_ADC2                0x65
-#define NAU8540_REG_ANALOG_PWR         0x66
-#define NAU8540_REG_MIC_BIAS                   0x67
-#define NAU8540_REG_REFERENCE                  0x68
-#define NAU8540_REG_FEPGA1                     0x69
-#define NAU8540_REG_FEPGA2                     0x6A
-#define NAU8540_REG_FEPGA3                     0x6B
-#define NAU8540_REG_FEPGA4                     0x6C
-#define NAU8540_REG_PWR                        0x6D
-#define NAU8540_REG_MAX                        NAU8540_REG_PWR
+#define NAU8540_REG_SW_RESET			0x00
+#define NAU8540_REG_POWER_MANAGEMENT	0x01
+#define NAU8540_REG_CLOCK_CTRL		0x02
+#define NAU8540_REG_CLOCK_SRC			0x03
+#define NAU8540_REG_FLL1			0x04
+#define NAU8540_REG_FLL2			0x05
+#define NAU8540_REG_FLL3			0x06
+#define NAU8540_REG_FLL4			0x07
+#define NAU8540_REG_FLL5			0x08
+#define NAU8540_REG_FLL6			0x09
+#define NAU8540_REG_FLL_VCO_RSV		0x0A
+#define NAU8540_REG_PCM_CTRL0			0x10
+#define NAU8540_REG_PCM_CTRL1			0x11
+#define NAU8540_REG_PCM_CTRL2			0x12
+#define NAU8540_REG_PCM_CTRL3			0x13
+#define NAU8540_REG_PCM_CTRL4			0x14
+#define NAU8540_REG_ALC_CONTROL_1		0x20
+#define NAU8540_REG_ALC_CONTROL_2		0x21
+#define NAU8540_REG_ALC_CONTROL_3		0x22
+#define NAU8540_REG_ALC_CONTROL_4		0x23
+#define NAU8540_REG_ALC_CONTROL_5		0x24
+#define NAU8540_REG_ALC_GAIN_CH12		0x2D
+#define NAU8540_REG_ALC_GAIN_CH34		0x2E
+#define NAU8540_REG_ALC_STATUS		0x2F
+#define NAU8540_REG_NOTCH_FIL1_CH1		0x30
+#define NAU8540_REG_NOTCH_FIL2_CH1		0x31
+#define NAU8540_REG_NOTCH_FIL1_CH2		0x32
+#define NAU8540_REG_NOTCH_FIL2_CH2		0x33
+#define NAU8540_REG_NOTCH_FIL1_CH3		0x34
+#define NAU8540_REG_NOTCH_FIL2_CH3		0x35
+#define NAU8540_REG_NOTCH_FIL1_CH4		0x36
+#define NAU8540_REG_NOTCH_FIL2_CH4		0x37
+#define NAU8540_REG_HPF_FILTER_CH12		0x38
+#define NAU8540_REG_HPF_FILTER_CH34		0x39
+#define NAU8540_REG_ADC_SAMPLE_RATE		0x3A
+#define NAU8540_REG_DIGITAL_GAIN_CH1		0x40
+#define NAU8540_REG_DIGITAL_GAIN_CH2		0x41
+#define NAU8540_REG_DIGITAL_GAIN_CH3		0x42
+#define NAU8540_REG_DIGITAL_GAIN_CH4		0x43
+#define NAU8540_REG_DIGITAL_MUX		0x44
+#define NAU8540_REG_P2P_CH1			0x48
+#define NAU8540_REG_P2P_CH2			0x49
+#define NAU8540_REG_P2P_CH3			0x4A
+#define NAU8540_REG_P2P_CH4			0x4B
+#define NAU8540_REG_PEAK_CH1			0x4C
+#define NAU8540_REG_PEAK_CH2			0x4D
+#define NAU8540_REG_PEAK_CH3			0x4E
+#define NAU8540_REG_PEAK_CH4			0x4F
+#define NAU8540_REG_GPIO_CTRL			0x50
+#define NAU8540_REG_MISC_CTRL			0x51
+#define NAU8540_REG_I2C_CTRL			0x52
+#define NAU8540_REG_I2C_DEVICE_ID		0x58
+#define NAU8540_REG_RST			0x5A
+#define NAU8540_REG_VMID_CTRL			0x60
+#define NAU8540_REG_MUTE			0x61
+#define NAU8540_REG_ANALOG_ADC1		0x64
+#define NAU8540_REG_ANALOG_ADC2		0x65
+#define NAU8540_REG_ANALOG_PWR		0x66
+#define NAU8540_REG_MIC_BIAS			0x67
+#define NAU8540_REG_REFERENCE			0x68
+#define NAU8540_REG_FEPGA1			0x69
+#define NAU8540_REG_FEPGA2			0x6A
+#define NAU8540_REG_FEPGA3			0x6B
+#define NAU8540_REG_FEPGA4			0x6C
+#define NAU8540_REG_PWR			0x6D
+#define NAU8540_REG_MAX			NAU8540_REG_PWR
 
 
 /* POWER_MANAGEMENT (0x01) */
-#define NAU8540_ADC4_EN                (0x1 << 3)
-#define NAU8540_ADC3_EN                (0x1 << 2)
-#define NAU8540_ADC2_EN                (0x1 << 1)
-#define NAU8540_ADC1_EN                0x1
+#define NAU8540_ADC4_EN		(0x1 << 3)
+#define NAU8540_ADC3_EN		(0x1 << 2)
+#define NAU8540_ADC2_EN		(0x1 << 1)
+#define NAU8540_ADC1_EN		0x1
 
 /* CLOCK_CTRL (0x02) */
-#define NAU8540_CLK_ADC_EN             (0x1 << 15)
-#define NAU8540_CLK_I2S_EN             (0x1 << 1)
+#define NAU8540_CLK_ADC_EN		(0x1 << 15)
+#define NAU8540_CLK_I2S_EN		(0x1 << 1)
 
 /* CLOCK_SRC (0x03) */
-#define NAU8540_CLK_SRC_SFT            15
-#define NAU8540_CLK_SRC_MASK           (1 << NAU8540_CLK_SRC_SFT)
-#define NAU8540_CLK_SRC_VCO            (1 << NAU8540_CLK_SRC_SFT)
-#define NAU8540_CLK_SRC_MCLK           (0 << NAU8540_CLK_SRC_SFT)
-#define NAU8540_CLK_ADC_SRC_SFT        6
-#define NAU8540_CLK_ADC_SRC_MASK       (0x3 << NAU8540_CLK_ADC_SRC_SFT)
-#define NAU8540_CLK_MCLK_SRC_MASK      0xf
+#define NAU8540_CLK_SRC_SFT		15
+#define NAU8540_CLK_SRC_MASK		(1 << NAU8540_CLK_SRC_SFT)
+#define NAU8540_CLK_SRC_VCO		(1 << NAU8540_CLK_SRC_SFT)
+#define NAU8540_CLK_SRC_MCLK		(0 << NAU8540_CLK_SRC_SFT)
+#define NAU8540_CLK_ADC_SRC_SFT	6
+#define NAU8540_CLK_ADC_SRC_MASK	(0x3 << NAU8540_CLK_ADC_SRC_SFT)
+#define NAU8540_CLK_MCLK_SRC_MASK	0xf
 
 /* FLL1 (0x04) */
-#define NAU8540_FLL_RATIO_MASK 0x7f
+#define NAU8540_FLL_RATIO_MASK	0x7f
 
 /* FLL3 (0x06) */
-#define NAU8540_FLL_CLK_SRC_SFT        10
-#define NAU8540_FLL_CLK_SRC_MASK       (0x3 << NAU8540_FLL_CLK_SRC_SFT)
-#define NAU8540_FLL_CLK_SRC_MCLK       (0 << NAU8540_FLL_CLK_SRC_SFT)
-#define NAU8540_FLL_CLK_SRC_BLK        (0x2 << NAU8540_FLL_CLK_SRC_SFT)
-#define NAU8540_FLL_CLK_SRC_FS         (0x3 << NAU8540_FLL_CLK_SRC_SFT)
-#define NAU8540_FLL_INTEGER_MASK       0x3ff
+#define NAU8540_FLL_CLK_SRC_SFT	10
+#define NAU8540_FLL_CLK_SRC_MASK	(0x3 << NAU8540_FLL_CLK_SRC_SFT)
+#define NAU8540_FLL_CLK_SRC_MCLK	(0 << NAU8540_FLL_CLK_SRC_SFT)
+#define NAU8540_FLL_CLK_SRC_BLK	(0x2 << NAU8540_FLL_CLK_SRC_SFT)
+#define NAU8540_FLL_CLK_SRC_FS		(0x3 << NAU8540_FLL_CLK_SRC_SFT)
+#define NAU8540_FLL_INTEGER_MASK	0x3ff
 
 /* FLL4 (0x07) */
-#define NAU8540_FLL_REF_DIV_SFT        10
-#define NAU8540_FLL_REF_DIV_MASK       (0x3 << NAU8540_FLL_REF_DIV_SFT)
+#define NAU8540_FLL_REF_DIV_SFT	10
+#define NAU8540_FLL_REF_DIV_MASK	(0x3 << NAU8540_FLL_REF_DIV_SFT)
 
 /* FLL5 (0x08) */
-#define NAU8540_FLL_PDB_DAC_EN (0x1 << 15)
-#define NAU8540_FLL_LOOP_FTR_EN        (0x1 << 14)
-#define NAU8540_FLL_CLK_SW_MASK        (0x1 << 13)
-#define NAU8540_FLL_CLK_SW_N2          (0x1 << 13)
-#define NAU8540_FLL_CLK_SW_REF (0x0 << 13)
-#define NAU8540_FLL_FTR_SW_MASK        (0x1 << 12)
-#define NAU8540_FLL_FTR_SW_ACCU        (0x1 << 12)
-#define NAU8540_FLL_FTR_SW_FILTER      (0x0 << 12)
+#define NAU8540_FLL_PDB_DAC_EN	(0x1 << 15)
+#define NAU8540_FLL_LOOP_FTR_EN	(0x1 << 14)
+#define NAU8540_FLL_CLK_SW_MASK	(0x1 << 13)
+#define NAU8540_FLL_CLK_SW_N2		(0x1 << 13)
+#define NAU8540_FLL_CLK_SW_REF	(0x0 << 13)
+#define NAU8540_FLL_FTR_SW_MASK	(0x1 << 12)
+#define NAU8540_FLL_FTR_SW_ACCU	(0x1 << 12)
+#define NAU8540_FLL_FTR_SW_FILTER	(0x0 << 12)
 
 /* FLL6 (0x9) */
-#define NAU8540_DCO_EN                 (0x1 << 15)
-#define NAU8540_SDM_EN                 (0x1 << 14)
+#define NAU8540_DCO_EN			(0x1 << 15)
+#define NAU8540_SDM_EN			(0x1 << 14)
 
 /* PCM_CTRL0 (0x10) */
-#define NAU8540_I2S_BP_SFT             7
-#define NAU8540_I2S_BP_INV             (0x1 << NAU8540_I2S_BP_SFT)
-#define NAU8540_I2S_PCMB_SFT           6
-#define NAU8540_I2S_PCMB_EN            (0x1 << NAU8540_I2S_PCMB_SFT)
-#define NAU8540_I2S_DL_SFT             2
-#define NAU8540_I2S_DL_MASK            (0x3 << NAU8540_I2S_DL_SFT)
-#define NAU8540_I2S_DL_16              (0 << NAU8540_I2S_DL_SFT)
-#define NAU8540_I2S_DL_20              (0x1 << NAU8540_I2S_DL_SFT)
-#define NAU8540_I2S_DL_24              (0x2 << NAU8540_I2S_DL_SFT)
-#define NAU8540_I2S_DL_32              (0x3 << NAU8540_I2S_DL_SFT)
-#define NAU8540_I2S_DF_MASK            0x3
-#define NAU8540_I2S_DF_RIGTH           0
-#define NAU8540_I2S_DF_LEFT            0x1
-#define NAU8540_I2S_DF_I2S             0x2
-#define NAU8540_I2S_DF_PCM_AB          0x3
+#define NAU8540_I2S_BP_SFT		7
+#define NAU8540_I2S_BP_INV		(0x1 << NAU8540_I2S_BP_SFT)
+#define NAU8540_I2S_PCMB_SFT		6
+#define NAU8540_I2S_PCMB_EN		(0x1 << NAU8540_I2S_PCMB_SFT)
+#define NAU8540_I2S_DL_SFT		2
+#define NAU8540_I2S_DL_MASK		(0x3 << NAU8540_I2S_DL_SFT)
+#define NAU8540_I2S_DL_16		(0 << NAU8540_I2S_DL_SFT)
+#define NAU8540_I2S_DL_20		(0x1 << NAU8540_I2S_DL_SFT)
+#define NAU8540_I2S_DL_24		(0x2 << NAU8540_I2S_DL_SFT)
+#define NAU8540_I2S_DL_32		(0x3 << NAU8540_I2S_DL_SFT)
+#define NAU8540_I2S_DF_MASK		0x3
+#define NAU8540_I2S_DF_RIGTH		0
+#define NAU8540_I2S_DF_LEFT		0x1
+#define NAU8540_I2S_DF_I2S		0x2
+#define NAU8540_I2S_DF_PCM_AB		0x3
 
 /* PCM_CTRL1 (0x11) */
-#define NAU8540_I2S_LRC_DIV_SFT        12
-#define NAU8540_I2S_LRC_DIV_MASK       (0x3 << NAU8540_I2S_LRC_DIV_SFT)
-#define NAU8540_I2S_DO12_OE            (0x1 << 4)
-#define NAU8540_I2S_MS_SFT             3
-#define NAU8540_I2S_MS_MASK            (0x1 << NAU8540_I2S_MS_SFT)
-#define NAU8540_I2S_MS_MASTER          (0x1 << NAU8540_I2S_MS_SFT)
-#define NAU8540_I2S_MS_SLAVE           (0x0 << NAU8540_I2S_MS_SFT)
-#define NAU8540_I2S_BLK_DIV_MASK       0x7
+#define NAU8540_I2S_LRC_DIV_SFT	12
+#define NAU8540_I2S_LRC_DIV_MASK	(0x3 << NAU8540_I2S_LRC_DIV_SFT)
+#define NAU8540_I2S_DO12_OE		(0x1 << 4)
+#define NAU8540_I2S_MS_SFT		3
+#define NAU8540_I2S_MS_MASK		(0x1 << NAU8540_I2S_MS_SFT)
+#define NAU8540_I2S_MS_MASTER		(0x1 << NAU8540_I2S_MS_SFT)
+#define NAU8540_I2S_MS_SLAVE		(0x0 << NAU8540_I2S_MS_SFT)
+#define NAU8540_I2S_BLK_DIV_MASK	0x7
 
 /* PCM_CTRL1 (0x12) */
-#define NAU8540_I2S_DO34_OE            (0x1 << 11)
-#define NAU8540_I2S_TSLOT_L_MASK       0x3ff
+#define NAU8540_I2S_DO34_OE		(0x1 << 11)
+#define NAU8540_I2S_TSLOT_L_MASK	0x3ff
 
 /* PCM_CTRL4 (0x14) */
-#define NAU8540_TDM_MODE               (0x1 << 15)
-#define NAU8540_TDM_OFFSET_EN          (0x1 << 14)
-#define NAU8540_TDM_TX_MASK            0xf
+#define NAU8540_TDM_MODE		(0x1 << 15)
+#define NAU8540_TDM_OFFSET_EN		(0x1 << 14)
+#define NAU8540_TDM_TX_MASK		0xf
 
 /* ADC_SAMPLE_RATE (0x3A) */
-#define NAU8540_ADC_OSR_MASK           0x3
-#define NAU8540_ADC_OSR_256            0x3
-#define NAU8540_ADC_OSR_128            0x2
-#define NAU8540_ADC_OSR_64             0x1
-#define NAU8540_ADC_OSR_32             0x0
+#define NAU8540_ADC_OSR_MASK		0x3
+#define NAU8540_ADC_OSR_256		0x3
+#define NAU8540_ADC_OSR_128		0x2
+#define NAU8540_ADC_OSR_64		0x1
+#define NAU8540_ADC_OSR_32		0x0
 
 /* VMID_CTRL (0x60) */
-#define NAU8540_VMID_EN                (1 << 6)
-#define NAU8540_VMID_SEL_SFT           4
-#define NAU8540_VMID_SEL_MASK          (0x3 << NAU8540_VMID_SEL_SFT)
+#define NAU8540_VMID_EN		(1 << 6)
+#define NAU8540_VMID_SEL_SFT		4
+#define NAU8540_VMID_SEL_MASK		(0x3 << NAU8540_VMID_SEL_SFT)
 
 /* MIC_BIAS (0x67) */
-#define NAU8540_PU_PRE                 (0x1 << 8)
+#define NAU8540_PU_PRE			(0x1 << 8)
 
 /* REFERENCE (0x68) */
-#define NAU8540_PRECHARGE_DIS          (0x1 << 13)
-#define NAU8540_GLOBAL_BIAS_EN (0x1 << 12)
+#define NAU8540_PRECHARGE_DIS		(0x1 << 13)
+#define NAU8540_GLOBAL_BIAS_EN	(0x1 << 12)
 
 
 /* System Clock Source */
 enum {
-       NAU8540_CLK_DIS,
-       NAU8540_CLK_MCLK,
-       NAU8540_CLK_INTERNAL,
-       NAU8540_CLK_FLL_MCLK,
-       NAU8540_CLK_FLL_BLK,
-       NAU8540_CLK_FLL_FS,
+	NAU8540_CLK_DIS,
+	NAU8540_CLK_MCLK,
+	NAU8540_CLK_INTERNAL,
+	NAU8540_CLK_FLL_MCLK,
+	NAU8540_CLK_FLL_BLK,
+	NAU8540_CLK_FLL_FS,
 };
 
 struct nau8540 {
-       struct device *dev;
-       struct regmap *regmap;
+	struct device *dev;
+	struct regmap *regmap;
 };
 
 struct nau8540_fll {
-       int mclk_src;
-       int ratio;
-       int fll_frac;
-       int fll_int;
-       int clk_ref_div;
+	int mclk_src;
+	int ratio;
+	int fll_frac;
+	int fll_int;
+	int clk_ref_div;
 };
 
 struct nau8540_fll_attr {
-       unsigned int param;
-       unsigned int val;
+	unsigned int param;
+	unsigned int val;
 };
 
 /* over sampling rate */
 struct nau8540_osr_attr {
-       unsigned int osr;
-       unsigned int clk_src;
+	unsigned int osr;
+	unsigned int clk_src;
 };
 
 
-#endif /* __NAU8540_H__ */
+#endif	/* __NAU8540_H__ */
diff --git a/sound/soc/codecs/nau8824.c b/sound/soc/codecs/nau8824.c
new file mode 100644
index 000000000000..cca974d26136
--- /dev/null
+++ b/sound/soc/codecs/nau8824.c
@@ -0,0 +1,1831 @@
+/*
+ * NAU88L24 ALSA SoC audio driver
+ *
+ * Copyright 2016 Nuvoton Technology Corp.
+ * Author: John Hsu <KCHSU0@nuvoton.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/delay.h>
+#include <linux/init.h>
+#include <linux/i2c.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <linux/clk.h>
+#include <linux/acpi.h>
+#include <linux/math64.h>
+#include <linux/semaphore.h>
+
+#include <sound/initval.h>
+#include <sound/tlv.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/jack.h>
+
+#include "nau8824.h"
+
+
+static int nau8824_config_sysclk(struct nau8824 *nau8824,
+	int clk_id, unsigned int freq);
+static bool nau8824_is_jack_inserted(struct nau8824 *nau8824);
+
+/* the ADC threshold of headset */
+#define DMIC_CLK 3072000
+
+/* the ADC threshold of headset */
+#define HEADSET_SARADC_THD 0x80
+
+/* the parameter threshold of FLL */
+#define NAU_FREF_MAX 13500000
+#define NAU_FVCO_MAX 124000000
+#define NAU_FVCO_MIN 90000000
+
+/* scaling for mclk from sysclk_src output */
+static const struct nau8824_fll_attr mclk_src_scaling[] = {
+	{ 1, 0x0 },
+	{ 2, 0x2 },
+	{ 4, 0x3 },
+	{ 8, 0x4 },
+	{ 16, 0x5 },
+	{ 32, 0x6 },
+	{ 3, 0x7 },
+	{ 6, 0xa },
+	{ 12, 0xb },
+	{ 24, 0xc },
+};
+
+/* ratio for input clk freq */
+static const struct nau8824_fll_attr fll_ratio[] = {
+	{ 512000, 0x01 },
+	{ 256000, 0x02 },
+	{ 128000, 0x04 },
+	{ 64000, 0x08 },
+	{ 32000, 0x10 },
+	{ 8000, 0x20 },
+	{ 4000, 0x40 },
+};
+
+static const struct nau8824_fll_attr fll_pre_scalar[] = {
+	{ 1, 0x0 },
+	{ 2, 0x1 },
+	{ 4, 0x2 },
+	{ 8, 0x3 },
+};
+
+/* the maximum frequency of CLK_ADC and CLK_DAC */
+#define CLK_DA_AD_MAX 6144000
+
+/* over sampling rate */
+static const struct nau8824_osr_attr osr_dac_sel[] = {
+	{ 64, 2 },	/* OSR 64, SRC 1/4 */
+	{ 256, 0 },	/* OSR 256, SRC 1 */
+	{ 128, 1 },	/* OSR 128, SRC 1/2 */
+	{ 0, 0 },
+	{ 32, 3 },	/* OSR 32, SRC 1/8 */
+};
+
+static const struct nau8824_osr_attr osr_adc_sel[] = {
+	{ 32, 3 },	/* OSR 32, SRC 1/8 */
+	{ 64, 2 },	/* OSR 64, SRC 1/4 */
+	{ 128, 1 },	/* OSR 128, SRC 1/2 */
+	{ 256, 0 },	/* OSR 256, SRC 1 */
+};
+
+static const struct reg_default nau8824_reg_defaults[] = {
+	{ NAU8824_REG_ENA_CTRL, 0x0000 },
+	{ NAU8824_REG_CLK_GATING_ENA, 0x0000 },
+	{ NAU8824_REG_CLK_DIVIDER, 0x0000 },
+	{ NAU8824_REG_FLL1, 0x0000 },
+	{ NAU8824_REG_FLL2, 0x3126 },
+	{ NAU8824_REG_FLL3, 0x0008 },
+	{ NAU8824_REG_FLL4, 0x0010 },
+	{ NAU8824_REG_FLL5, 0xC000 },
+	{ NAU8824_REG_FLL6, 0x6000 },
+	{ NAU8824_REG_FLL_VCO_RSV, 0xF13C },
+	{ NAU8824_REG_JACK_DET_CTRL, 0x0000 },
+	{ NAU8824_REG_INTERRUPT_SETTING_1, 0x0000 },
+	{ NAU8824_REG_IRQ, 0x0000 },
+	{ NAU8824_REG_CLEAR_INT_REG, 0x0000 },
+	{ NAU8824_REG_INTERRUPT_SETTING, 0x1000 },
+	{ NAU8824_REG_SAR_ADC, 0x0015 },
+	{ NAU8824_REG_VDET_COEFFICIENT, 0x0110 },
+	{ NAU8824_REG_VDET_THRESHOLD_1, 0x0000 },
+	{ NAU8824_REG_VDET_THRESHOLD_2, 0x0000 },
+	{ NAU8824_REG_VDET_THRESHOLD_3, 0x0000 },
+	{ NAU8824_REG_VDET_THRESHOLD_4, 0x0000 },
+	{ NAU8824_REG_GPIO_SEL, 0x0000 },
+	{ NAU8824_REG_PORT0_I2S_PCM_CTRL_1, 0x000B },
+	{ NAU8824_REG_PORT0_I2S_PCM_CTRL_2, 0x0010 },
+	{ NAU8824_REG_PORT0_LEFT_TIME_SLOT, 0x0000 },
+	{ NAU8824_REG_PORT0_RIGHT_TIME_SLOT, 0x0000 },
+	{ NAU8824_REG_TDM_CTRL, 0x0000 },
+	{ NAU8824_REG_ADC_HPF_FILTER, 0x0000 },
+	{ NAU8824_REG_ADC_FILTER_CTRL, 0x0002 },
+	{ NAU8824_REG_DAC_FILTER_CTRL_1, 0x0000 },
+	{ NAU8824_REG_DAC_FILTER_CTRL_2, 0x0000 },
+	{ NAU8824_REG_NOTCH_FILTER_1, 0x0000 },
+	{ NAU8824_REG_NOTCH_FILTER_2, 0x0000 },
+	{ NAU8824_REG_EQ1_LOW, 0x112C },
+	{ NAU8824_REG_EQ2_EQ3, 0x2C2C },
+	{ NAU8824_REG_EQ4_EQ5, 0x2C2C },
+	{ NAU8824_REG_ADC_CH0_DGAIN_CTRL, 0x0100 },
+	{ NAU8824_REG_ADC_CH1_DGAIN_CTRL, 0x0100 },
+	{ NAU8824_REG_ADC_CH2_DGAIN_CTRL, 0x0100 },
+	{ NAU8824_REG_ADC_CH3_DGAIN_CTRL, 0x0100 },
+	{ NAU8824_REG_DAC_MUTE_CTRL, 0x0000 },
+	{ NAU8824_REG_DAC_CH0_DGAIN_CTRL, 0x0100 },
+	{ NAU8824_REG_DAC_CH1_DGAIN_CTRL, 0x0100 },
+	{ NAU8824_REG_ADC_TO_DAC_ST, 0x0000 },
+	{ NAU8824_REG_DRC_KNEE_IP12_ADC_CH01, 0x1486 },
+	{ NAU8824_REG_DRC_KNEE_IP34_ADC_CH01, 0x0F12 },
+	{ NAU8824_REG_DRC_SLOPE_ADC_CH01, 0x25FF },
+	{ NAU8824_REG_DRC_ATKDCY_ADC_CH01, 0x3457 },
+	{ NAU8824_REG_DRC_KNEE_IP12_ADC_CH23, 0x1486 },
+	{ NAU8824_REG_DRC_KNEE_IP34_ADC_CH23, 0x0F12 },
+	{ NAU8824_REG_DRC_SLOPE_ADC_CH23, 0x25FF },
+	{ NAU8824_REG_DRC_ATKDCY_ADC_CH23, 0x3457 },
+	{ NAU8824_REG_DRC_GAINL_ADC0, 0x0200 },
+	{ NAU8824_REG_DRC_GAINL_ADC1, 0x0200 },
+	{ NAU8824_REG_DRC_GAINL_ADC2, 0x0200 },
+	{ NAU8824_REG_DRC_GAINL_ADC3, 0x0200 },
+	{ NAU8824_REG_DRC_KNEE_IP12_DAC, 0x1486 },
+	{ NAU8824_REG_DRC_KNEE_IP34_DAC, 0x0F12 },
+	{ NAU8824_REG_DRC_SLOPE_DAC, 0x25F9 },
+	{ NAU8824_REG_DRC_ATKDCY_DAC, 0x3457 },
+	{ NAU8824_REG_DRC_GAIN_DAC_CH0, 0x0200 },
+	{ NAU8824_REG_DRC_GAIN_DAC_CH1, 0x0200 },
+	{ NAU8824_REG_MODE, 0x0000 },
+	{ NAU8824_REG_MODE1, 0x0000 },
+	{ NAU8824_REG_MODE2, 0x0000 },
+	{ NAU8824_REG_CLASSG, 0x0000 },
+	{ NAU8824_REG_OTP_EFUSE, 0x0000 },
+	{ NAU8824_REG_OTPDOUT_1, 0x0000 },
+	{ NAU8824_REG_OTPDOUT_2, 0x0000 },
+	{ NAU8824_REG_MISC_CTRL, 0x0000 },
+	{ NAU8824_REG_I2C_TIMEOUT, 0xEFFF },
+	{ NAU8824_REG_TEST_MODE, 0x0000 },
+	{ NAU8824_REG_I2C_DEVICE_ID, 0x1AF1 },
+	{ NAU8824_REG_SAR_ADC_DATA_OUT, 0x00FF },
+	{ NAU8824_REG_BIAS_ADJ, 0x0000 },
+	{ NAU8824_REG_PGA_GAIN, 0x0000 },
+	{ NAU8824_REG_TRIM_SETTINGS, 0x0000 },
+	{ NAU8824_REG_ANALOG_CONTROL_1, 0x0000 },
+	{ NAU8824_REG_ANALOG_CONTROL_2, 0x0000 },
+	{ NAU8824_REG_ENABLE_LO, 0x0000 },
+	{ NAU8824_REG_GAIN_LO, 0x0000 },
+	{ NAU8824_REG_CLASSD_GAIN_1, 0x0000 },
+	{ NAU8824_REG_CLASSD_GAIN_2, 0x0000 },
+	{ NAU8824_REG_ANALOG_ADC_1, 0x0011 },
+	{ NAU8824_REG_ANALOG_ADC_2, 0x0020 },
+	{ NAU8824_REG_RDAC, 0x0008 },
+	{ NAU8824_REG_MIC_BIAS, 0x0006 },
+	{ NAU8824_REG_HS_VOLUME_CONTROL, 0x0000 },
+	{ NAU8824_REG_BOOST, 0x0000 },
+	{ NAU8824_REG_FEPGA, 0x0000 },
+	{ NAU8824_REG_FEPGA_II, 0x0000 },
+	{ NAU8824_REG_FEPGA_SE, 0x0000 },
+	{ NAU8824_REG_FEPGA_ATTENUATION, 0x0000 },
+	{ NAU8824_REG_ATT_PORT0, 0x0000 },
+	{ NAU8824_REG_ATT_PORT1, 0x0000 },
+	{ NAU8824_REG_POWER_UP_CONTROL, 0x0000 },
+	{ NAU8824_REG_CHARGE_PUMP_CONTROL, 0x0300 },
+	{ NAU8824_REG_CHARGE_PUMP_INPUT, 0x0013 },
+};
+
+static int nau8824_sema_acquire(struct nau8824 *nau8824, long timeout)
+{
+	int ret;
+
+	if (timeout) {
+		ret = down_timeout(&nau8824->jd_sem, timeout);
+		if (ret < 0)
+			dev_warn(nau8824->dev, "Acquire semaphone timeout\n");
+	} else {
+		ret = down_interruptible(&nau8824->jd_sem);
+		if (ret < 0)
+			dev_warn(nau8824->dev, "Acquire semaphone fail\n");
+	}
+
+	return ret;
+}
+
+static inline void nau8824_sema_release(struct nau8824 *nau8824)
+{
+	up(&nau8824->jd_sem);
+}
+
+static bool nau8824_readable_reg(struct device *dev, unsigned int reg)
+{
+	switch (reg) {
+	case NAU8824_REG_ENA_CTRL ... NAU8824_REG_FLL_VCO_RSV:
+	case NAU8824_REG_JACK_DET_CTRL:
+	case NAU8824_REG_INTERRUPT_SETTING_1:
+	case NAU8824_REG_IRQ:
+	case NAU8824_REG_CLEAR_INT_REG ... NAU8824_REG_VDET_THRESHOLD_4:
+	case NAU8824_REG_GPIO_SEL:
+	case NAU8824_REG_PORT0_I2S_PCM_CTRL_1 ... NAU8824_REG_TDM_CTRL:
+	case NAU8824_REG_ADC_HPF_FILTER ... NAU8824_REG_EQ4_EQ5:
+	case NAU8824_REG_ADC_CH0_DGAIN_CTRL ... NAU8824_REG_ADC_TO_DAC_ST:
+	case NAU8824_REG_DRC_KNEE_IP12_ADC_CH01 ... NAU8824_REG_DRC_GAINL_ADC3:
+	case NAU8824_REG_DRC_KNEE_IP12_DAC ... NAU8824_REG_DRC_GAIN_DAC_CH1:
+	case NAU8824_REG_CLASSG ... NAU8824_REG_OTP_EFUSE:
+	case NAU8824_REG_OTPDOUT_1 ... NAU8824_REG_OTPDOUT_2:
+	case NAU8824_REG_I2C_TIMEOUT:
+	case NAU8824_REG_I2C_DEVICE_ID ... NAU8824_REG_SAR_ADC_DATA_OUT:
+	case NAU8824_REG_BIAS_ADJ ... NAU8824_REG_CLASSD_GAIN_2:
+	case NAU8824_REG_ANALOG_ADC_1 ... NAU8824_REG_ATT_PORT1:
+	case NAU8824_REG_POWER_UP_CONTROL ... NAU8824_REG_CHARGE_PUMP_INPUT:
+		return true;
+	default:
+		return false;
+	}
+
+}
+
+static bool nau8824_writeable_reg(struct device *dev, unsigned int reg)
+{
+	switch (reg) {
+	case NAU8824_REG_RESET ... NAU8824_REG_FLL_VCO_RSV:
+	case NAU8824_REG_JACK_DET_CTRL:
+	case NAU8824_REG_INTERRUPT_SETTING_1:
+	case NAU8824_REG_CLEAR_INT_REG ... NAU8824_REG_VDET_THRESHOLD_4:
+	case NAU8824_REG_GPIO_SEL:
+	case NAU8824_REG_PORT0_I2S_PCM_CTRL_1 ... NAU8824_REG_TDM_CTRL:
+	case NAU8824_REG_ADC_HPF_FILTER ... NAU8824_REG_EQ4_EQ5:
+	case NAU8824_REG_ADC_CH0_DGAIN_CTRL ... NAU8824_REG_ADC_TO_DAC_ST:
+	case NAU8824_REG_DRC_KNEE_IP12_ADC_CH01:
+	case NAU8824_REG_DRC_KNEE_IP34_ADC_CH01:
+	case NAU8824_REG_DRC_SLOPE_ADC_CH01:
+	case NAU8824_REG_DRC_ATKDCY_ADC_CH01:
+	case NAU8824_REG_DRC_KNEE_IP12_ADC_CH23:
+	case NAU8824_REG_DRC_KNEE_IP34_ADC_CH23:
+	case NAU8824_REG_DRC_SLOPE_ADC_CH23:
+	case NAU8824_REG_DRC_ATKDCY_ADC_CH23:
+	case NAU8824_REG_DRC_KNEE_IP12_DAC ... NAU8824_REG_DRC_ATKDCY_DAC:
+	case NAU8824_REG_CLASSG ... NAU8824_REG_OTP_EFUSE:
+	case NAU8824_REG_I2C_TIMEOUT:
+	case NAU8824_REG_BIAS_ADJ ... NAU8824_REG_CLASSD_GAIN_2:
+	case NAU8824_REG_ANALOG_ADC_1 ... NAU8824_REG_ATT_PORT1:
+	case NAU8824_REG_POWER_UP_CONTROL ... NAU8824_REG_CHARGE_PUMP_CONTROL:
+		return true;
+	default:
+		return false;
+	}
+}
+
+static bool nau8824_volatile_reg(struct device *dev, unsigned int reg)
+{
+	switch (reg) {
+	case NAU8824_REG_RESET:
+	case NAU8824_REG_IRQ ... NAU8824_REG_CLEAR_INT_REG:
+	case NAU8824_REG_DRC_GAINL_ADC0 ... NAU8824_REG_DRC_GAINL_ADC3:
+	case NAU8824_REG_DRC_GAIN_DAC_CH0 ... NAU8824_REG_DRC_GAIN_DAC_CH1:
+	case NAU8824_REG_OTPDOUT_1 ... NAU8824_REG_OTPDOUT_2:
+	case NAU8824_REG_I2C_DEVICE_ID ... NAU8824_REG_SAR_ADC_DATA_OUT:
+	case NAU8824_REG_CHARGE_PUMP_INPUT:
+		return true;
+	default:
+		return false;
+	}
+}
+
+static const char * const nau8824_companding[] = {
+	"Off", "NC", "u-law", "A-law" };
+
+static const struct soc_enum nau8824_companding_adc_enum =
+	SOC_ENUM_SINGLE(NAU8824_REG_PORT0_I2S_PCM_CTRL_1, 12,
+		ARRAY_SIZE(nau8824_companding), nau8824_companding);
+
+static const struct soc_enum nau8824_companding_dac_enum =
+	SOC_ENUM_SINGLE(NAU8824_REG_PORT0_I2S_PCM_CTRL_1, 14,
+		ARRAY_SIZE(nau8824_companding), nau8824_companding);
+
+static const char * const nau8824_adc_decimation[] = {
+	"32", "64", "128", "256" };
+
+static const struct soc_enum nau8824_adc_decimation_enum =
+	SOC_ENUM_SINGLE(NAU8824_REG_ADC_FILTER_CTRL, 0,
+		ARRAY_SIZE(nau8824_adc_decimation), nau8824_adc_decimation);
+
+static const char * const nau8824_dac_oversampl[] = {
+	"64", "256", "128", "", "32" };
+
+static const struct soc_enum nau8824_dac_oversampl_enum =
+	SOC_ENUM_SINGLE(NAU8824_REG_DAC_FILTER_CTRL_1, 0,
+		ARRAY_SIZE(nau8824_dac_oversampl), nau8824_dac_oversampl);
+
+static const char * const nau8824_input_channel[] = {
+	"Input CH0", "Input CH1", "Input CH2", "Input CH3" };
+
+static const struct soc_enum nau8824_adc_ch0_enum =
+	SOC_ENUM_SINGLE(NAU8824_REG_ADC_CH0_DGAIN_CTRL, 9,
+		ARRAY_SIZE(nau8824_input_channel), nau8824_input_channel);
+
+static const struct soc_enum nau8824_adc_ch1_enum =
+	SOC_ENUM_SINGLE(NAU8824_REG_ADC_CH1_DGAIN_CTRL, 9,
+		ARRAY_SIZE(nau8824_input_channel), nau8824_input_channel);
+
+static const struct soc_enum nau8824_adc_ch2_enum =
+	SOC_ENUM_SINGLE(NAU8824_REG_ADC_CH2_DGAIN_CTRL, 9,
+		ARRAY_SIZE(nau8824_input_channel), nau8824_input_channel);
+
+static const struct soc_enum nau8824_adc_ch3_enum =
+	SOC_ENUM_SINGLE(NAU8824_REG_ADC_CH3_DGAIN_CTRL, 9,
+		ARRAY_SIZE(nau8824_input_channel), nau8824_input_channel);
+
+static const char * const nau8824_tdm_slot[] = {
+	"Slot 0", "Slot 1", "Slot 2", "Slot 3" };
+
+static const struct soc_enum nau8824_dac_left_sel_enum =
+	SOC_ENUM_SINGLE(NAU8824_REG_TDM_CTRL, 6,
+		ARRAY_SIZE(nau8824_tdm_slot), nau8824_tdm_slot);
+
+static const struct soc_enum nau8824_dac_right_sel_enum =
+	SOC_ENUM_SINGLE(NAU8824_REG_TDM_CTRL, 4,
+		ARRAY_SIZE(nau8824_tdm_slot), nau8824_tdm_slot);
+
+static const DECLARE_TLV_DB_MINMAX_MUTE(spk_vol_tlv, 0, 2400);
+static const DECLARE_TLV_DB_MINMAX(hp_vol_tlv, -3000, 0);
+static const DECLARE_TLV_DB_SCALE(mic_vol_tlv, 0, 200, 0);
+static const DECLARE_TLV_DB_SCALE(dmic_vol_tlv, -12800, 50, 0);
+
+static const struct snd_kcontrol_new nau8824_snd_controls[] = {
+	SOC_ENUM("ADC Companding", nau8824_companding_adc_enum),
+	SOC_ENUM("DAC Companding", nau8824_companding_dac_enum),
+
+	SOC_ENUM("ADC Decimation Rate", nau8824_adc_decimation_enum),
+	SOC_ENUM("DAC Oversampling Rate", nau8824_dac_oversampl_enum),
+
+	SOC_SINGLE_TLV("Speaker Right DACR Volume",
+		NAU8824_REG_CLASSD_GAIN_1, 8, 0x1f, 0, spk_vol_tlv),
+	SOC_SINGLE_TLV("Speaker Left DACL Volume",
+		NAU8824_REG_CLASSD_GAIN_2, 0, 0x1f, 0, spk_vol_tlv),
+	SOC_SINGLE_TLV("Speaker Left DACR Volume",
+		NAU8824_REG_CLASSD_GAIN_1, 0, 0x1f, 0, spk_vol_tlv),
+	SOC_SINGLE_TLV("Speaker Right DACL Volume",
+		NAU8824_REG_CLASSD_GAIN_2, 8, 0x1f, 0, spk_vol_tlv),
+
+	SOC_SINGLE_TLV("Headphone Right DACR Volume",
+		NAU8824_REG_ATT_PORT0, 8, 0x1f, 0, hp_vol_tlv),
+	SOC_SINGLE_TLV("Headphone Left DACL Volume",
+		NAU8824_REG_ATT_PORT0, 0, 0x1f, 0, hp_vol_tlv),
+	SOC_SINGLE_TLV("Headphone Right DACL Volume",
+		NAU8824_REG_ATT_PORT1, 8, 0x1f, 0, hp_vol_tlv),
+	SOC_SINGLE_TLV("Headphone Left DACR Volume",
+		NAU8824_REG_ATT_PORT1, 0, 0x1f, 0, hp_vol_tlv),
+
+	SOC_SINGLE_TLV("MIC1 Volume", NAU8824_REG_FEPGA_II,
+		NAU8824_FEPGA_GAINL_SFT, 0x12, 0, mic_vol_tlv),
+	SOC_SINGLE_TLV("MIC2 Volume", NAU8824_REG_FEPGA_II,
+		NAU8824_FEPGA_GAINR_SFT, 0x12, 0, mic_vol_tlv),
+
+	SOC_SINGLE_TLV("DMIC1 Volume", NAU8824_REG_ADC_CH0_DGAIN_CTRL,
+		0, 0x164, 0, dmic_vol_tlv),
+	SOC_SINGLE_TLV("DMIC2 Volume", NAU8824_REG_ADC_CH1_DGAIN_CTRL,
+		0, 0x164, 0, dmic_vol_tlv),
+	SOC_SINGLE_TLV("DMIC3 Volume", NAU8824_REG_ADC_CH2_DGAIN_CTRL,
+		0, 0x164, 0, dmic_vol_tlv),
+	SOC_SINGLE_TLV("DMIC4 Volume", NAU8824_REG_ADC_CH3_DGAIN_CTRL,
+		0, 0x164, 0, dmic_vol_tlv),
+
+	SOC_ENUM("ADC CH0 Select", nau8824_adc_ch0_enum),
+	SOC_ENUM("ADC CH1 Select", nau8824_adc_ch1_enum),
+	SOC_ENUM("ADC CH2 Select", nau8824_adc_ch2_enum),
+	SOC_ENUM("ADC CH3 Select", nau8824_adc_ch3_enum),
+
+	SOC_SINGLE("ADC CH0 TX Switch", NAU8824_REG_TDM_CTRL, 0, 1, 0),
+	SOC_SINGLE("ADC CH1 TX Switch", NAU8824_REG_TDM_CTRL, 1, 1, 0),
+	SOC_SINGLE("ADC CH2 TX Switch", NAU8824_REG_TDM_CTRL, 2, 1, 0),
+	SOC_SINGLE("ADC CH3 TX Switch", NAU8824_REG_TDM_CTRL, 3, 1, 0),
+
+	SOC_ENUM("DACL Channel Source", nau8824_dac_left_sel_enum),
+	SOC_ENUM("DACR Channel Source", nau8824_dac_right_sel_enum),
+
+	SOC_SINGLE("DACL LR Mix", NAU8824_REG_DAC_MUTE_CTRL, 0, 1, 0),
+	SOC_SINGLE("DACR LR Mix", NAU8824_REG_DAC_MUTE_CTRL, 1, 1, 0),
+};
+
+static int nau8824_output_dac_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);
+	struct nau8824 *nau8824 = snd_soc_codec_get_drvdata(codec);
+
+	switch (event) {
+	case SND_SOC_DAPM_PRE_PMU:
+		/* Disables the TESTDAC to let DAC signal pass through. */
+		regmap_update_bits(nau8824->regmap, NAU8824_REG_ENABLE_LO,
+			NAU8824_TEST_DAC_EN, 0);
+		break;
+	case SND_SOC_DAPM_POST_PMD:
+		regmap_update_bits(nau8824->regmap, NAU8824_REG_ENABLE_LO,
+			NAU8824_TEST_DAC_EN, NAU8824_TEST_DAC_EN);
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int nau8824_spk_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);
+	struct nau8824 *nau8824 = snd_soc_codec_get_drvdata(codec);
+
+	switch (event) {
+	case SND_SOC_DAPM_PRE_PMU:
+		regmap_update_bits(nau8824->regmap,
+			NAU8824_REG_ANALOG_CONTROL_2,
+			NAU8824_CLASSD_CLAMP_DIS, NAU8824_CLASSD_CLAMP_DIS);
+		break;
+	case SND_SOC_DAPM_POST_PMD:
+		regmap_update_bits(nau8824->regmap,
+			NAU8824_REG_ANALOG_CONTROL_2,
+			NAU8824_CLASSD_CLAMP_DIS, 0);
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int nau8824_pump_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);
+	struct nau8824 *nau8824 = snd_soc_codec_get_drvdata(codec);
+
+	switch (event) {
+	case SND_SOC_DAPM_POST_PMU:
+		/* Prevent startup click by letting charge pump to ramp up */
+		msleep(10);
+		regmap_update_bits(nau8824->regmap,
+			NAU8824_REG_CHARGE_PUMP_CONTROL,
+			NAU8824_JAMNODCLOW, NAU8824_JAMNODCLOW);
+		break;
+	case SND_SOC_DAPM_PRE_PMD:
+		regmap_update_bits(nau8824->regmap,
+			NAU8824_REG_CHARGE_PUMP_CONTROL,
+			NAU8824_JAMNODCLOW, 0);
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int system_clock_control(struct snd_soc_dapm_widget *w,
+		struct snd_kcontrol *k, int  event)
+{
+	struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+	struct nau8824 *nau8824 = snd_soc_codec_get_drvdata(codec);
+
+	if (SND_SOC_DAPM_EVENT_OFF(event)) {
+		/* Set clock source to disable or internal clock before the
+		 * playback or capture end. Codec needs clock for Jack
+		 * detection and button press if jack inserted; otherwise,
+		 * the clock should be closed.
+		 */
+		if (nau8824_is_jack_inserted(nau8824)) {
+			nau8824_config_sysclk(nau8824,
+				NAU8824_CLK_INTERNAL, 0);
+		} else {
+			nau8824_config_sysclk(nau8824, NAU8824_CLK_DIS, 0);
+		}
+	}
+	return 0;
+}
+
+static int dmic_clock_control(struct snd_soc_dapm_widget *w,
+		struct snd_kcontrol *k, int  event)
+{
+	struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+	struct nau8824 *nau8824 = snd_soc_codec_get_drvdata(codec);
+	int src;
+
+	/* The DMIC clock is gotten from system clock (256fs) divided by
+	 * DMIC_SRC (1, 2, 4, 8, 16, 32). The clock has to be equal or
+	 * less than 3.072 MHz.
+	 */
+	for (src = 0; src < 5; src++) {
+		if ((0x1 << (8 - src)) * nau8824->fs <= DMIC_CLK)
+			break;
+	}
+	dev_dbg(nau8824->dev, "dmic src %d for mclk %d\n", src, nau8824->fs * 256);
+	regmap_update_bits(nau8824->regmap, NAU8824_REG_CLK_DIVIDER,
+		NAU8824_CLK_DMIC_SRC_MASK, (src << NAU8824_CLK_DMIC_SRC_SFT));
+
+	return 0;
+}
+
+static const struct snd_kcontrol_new nau8824_adc_ch0_dmic =
+	SOC_DAPM_SINGLE("Switch", NAU8824_REG_ENA_CTRL,
+		NAU8824_ADC_CH0_DMIC_SFT, 1, 0);
+
+static const struct snd_kcontrol_new nau8824_adc_ch1_dmic =
+	SOC_DAPM_SINGLE("Switch", NAU8824_REG_ENA_CTRL,
+		NAU8824_ADC_CH1_DMIC_SFT, 1, 0);
+
+static const struct snd_kcontrol_new nau8824_adc_ch2_dmic =
+	SOC_DAPM_SINGLE("Switch", NAU8824_REG_ENA_CTRL,
+		NAU8824_ADC_CH2_DMIC_SFT, 1, 0);
+
+static const struct snd_kcontrol_new nau8824_adc_ch3_dmic =
+	SOC_DAPM_SINGLE("Switch", NAU8824_REG_ENA_CTRL,
+		NAU8824_ADC_CH3_DMIC_SFT, 1, 0);
+
+static const struct snd_kcontrol_new nau8824_adc_left_mixer[] = {
+	SOC_DAPM_SINGLE("MIC Switch", NAU8824_REG_FEPGA,
+		NAU8824_FEPGA_MODEL_MIC1_SFT, 1, 0),
+	SOC_DAPM_SINGLE("HSMIC Switch", NAU8824_REG_FEPGA,
+		NAU8824_FEPGA_MODEL_HSMIC_SFT, 1, 0),
+};
+
+static const struct snd_kcontrol_new nau8824_adc_right_mixer[] = {
+	SOC_DAPM_SINGLE("MIC Switch", NAU8824_REG_FEPGA,
+		NAU8824_FEPGA_MODER_MIC2_SFT, 1, 0),
+	SOC_DAPM_SINGLE("HSMIC Switch", NAU8824_REG_FEPGA,
+		NAU8824_FEPGA_MODER_HSMIC_SFT, 1, 0),
+};
+
+static const struct snd_kcontrol_new nau8824_hp_left_mixer[] = {
+	SOC_DAPM_SINGLE("DAC Right Switch", NAU8824_REG_ENABLE_LO,
+		NAU8824_DACR_HPL_EN_SFT, 1, 0),
+	SOC_DAPM_SINGLE("DAC Left Switch", NAU8824_REG_ENABLE_LO,
+		NAU8824_DACL_HPL_EN_SFT, 1, 0),
+};
+
+static const struct snd_kcontrol_new nau8824_hp_right_mixer[] = {
+	SOC_DAPM_SINGLE("DAC Left Switch", NAU8824_REG_ENABLE_LO,
+		NAU8824_DACL_HPR_EN_SFT, 1, 0),
+	SOC_DAPM_SINGLE("DAC Right Switch", NAU8824_REG_ENABLE_LO,
+		NAU8824_DACR_HPR_EN_SFT, 1, 0),
+};
+
+static const char * const nau8824_dac_src[] = { "DACL", "DACR" };
+
+static SOC_ENUM_SINGLE_DECL(
+	nau8824_dacl_enum, NAU8824_REG_DAC_CH0_DGAIN_CTRL,
+	NAU8824_DAC_CH0_SEL_SFT, nau8824_dac_src);
+
+static SOC_ENUM_SINGLE_DECL(
+	nau8824_dacr_enum, NAU8824_REG_DAC_CH1_DGAIN_CTRL,
+	NAU8824_DAC_CH1_SEL_SFT, nau8824_dac_src);
+
+static const struct snd_kcontrol_new nau8824_dacl_mux =
+	SOC_DAPM_ENUM("DACL Source", nau8824_dacl_enum);
+
+static const struct snd_kcontrol_new nau8824_dacr_mux =
+	SOC_DAPM_ENUM("DACR Source", nau8824_dacr_enum);
+
+
+static const struct snd_soc_dapm_widget nau8824_dapm_widgets[] = {
+	SND_SOC_DAPM_SUPPLY("System Clock", SND_SOC_NOPM, 0, 0,
+			system_clock_control, SND_SOC_DAPM_POST_PMD),
+
+	SND_SOC_DAPM_INPUT("HSMIC1"),
+	SND_SOC_DAPM_INPUT("HSMIC2"),
+	SND_SOC_DAPM_INPUT("MIC1"),
+	SND_SOC_DAPM_INPUT("MIC2"),
+	SND_SOC_DAPM_INPUT("DMIC1"),
+	SND_SOC_DAPM_INPUT("DMIC2"),
+	SND_SOC_DAPM_INPUT("DMIC3"),
+	SND_SOC_DAPM_INPUT("DMIC4"),
+
+	SND_SOC_DAPM_SUPPLY("SAR", NAU8824_REG_SAR_ADC,
+		NAU8824_SAR_ADC_EN_SFT, 0, NULL, 0),
+	SND_SOC_DAPM_SUPPLY("MICBIAS", NAU8824_REG_MIC_BIAS,
+		NAU8824_MICBIAS_POWERUP_SFT, 0, NULL, 0),
+	SND_SOC_DAPM_SUPPLY("DMIC12 Power", NAU8824_REG_BIAS_ADJ,
+		NAU8824_DMIC1_EN_SFT, 0, NULL, 0),
+	SND_SOC_DAPM_SUPPLY("DMIC34 Power", NAU8824_REG_BIAS_ADJ,
+		NAU8824_DMIC2_EN_SFT, 0, NULL, 0),
+	SND_SOC_DAPM_SUPPLY("DMIC Clock", SND_SOC_NOPM, 0, 0,
+		dmic_clock_control, SND_SOC_DAPM_POST_PMU),
+
+	SND_SOC_DAPM_SWITCH("DMIC1 Enable", SND_SOC_NOPM,
+		0, 0, &nau8824_adc_ch0_dmic),
+	SND_SOC_DAPM_SWITCH("DMIC2 Enable", SND_SOC_NOPM,
+		0, 0, &nau8824_adc_ch1_dmic),
+	SND_SOC_DAPM_SWITCH("DMIC3 Enable", SND_SOC_NOPM,
+		0, 0, &nau8824_adc_ch2_dmic),
+	SND_SOC_DAPM_SWITCH("DMIC4 Enable", SND_SOC_NOPM,
+		0, 0, &nau8824_adc_ch3_dmic),
+
+	SND_SOC_DAPM_MIXER("Left ADC", NAU8824_REG_POWER_UP_CONTROL,
+		12, 0, nau8824_adc_left_mixer,
+		ARRAY_SIZE(nau8824_adc_left_mixer)),
+	SND_SOC_DAPM_MIXER("Right ADC", NAU8824_REG_POWER_UP_CONTROL,
+		13, 0, nau8824_adc_right_mixer,
+		ARRAY_SIZE(nau8824_adc_right_mixer)),
+
+	SND_SOC_DAPM_ADC("ADCL", NULL, NAU8824_REG_ANALOG_ADC_2,
+		NAU8824_ADCL_EN_SFT, 0),
+	SND_SOC_DAPM_ADC("ADCR", NULL, NAU8824_REG_ANALOG_ADC_2,
+		NAU8824_ADCR_EN_SFT, 0),
+
+	SND_SOC_DAPM_AIF_OUT("AIFTX", "HiFi Capture", 0, SND_SOC_NOPM, 0, 0),
+	SND_SOC_DAPM_AIF_IN("AIFRX", "HiFi Playback", 0, SND_SOC_NOPM, 0, 0),
+
+	SND_SOC_DAPM_DAC("DACL", NULL, NAU8824_REG_RDAC,
+		NAU8824_DACL_EN_SFT, 0),
+	SND_SOC_DAPM_SUPPLY("DACL Clock", NAU8824_REG_RDAC,
+		NAU8824_DACL_CLK_SFT, 0, NULL, 0),
+	SND_SOC_DAPM_DAC("DACR", NULL, NAU8824_REG_RDAC,
+		NAU8824_DACR_EN_SFT, 0),
+	SND_SOC_DAPM_SUPPLY("DACR Clock", NAU8824_REG_RDAC,
+		NAU8824_DACR_CLK_SFT, 0, NULL, 0),
+
+	SND_SOC_DAPM_MUX("DACL Mux", SND_SOC_NOPM, 0, 0, &nau8824_dacl_mux),
+	SND_SOC_DAPM_MUX("DACR Mux", SND_SOC_NOPM, 0, 0, &nau8824_dacr_mux),
+
+	SND_SOC_DAPM_PGA_S("Output DACL", 0, NAU8824_REG_CHARGE_PUMP_CONTROL,
+		8, 1, nau8824_output_dac_event,
+		SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+	SND_SOC_DAPM_PGA_S("Output DACR", 0, NAU8824_REG_CHARGE_PUMP_CONTROL,
+		9, 1, nau8824_output_dac_event,
+		SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+
+	SND_SOC_DAPM_PGA_S("ClassD", 0, NAU8824_REG_CLASSD_GAIN_1,
+		NAU8824_CLASSD_EN_SFT, 0, nau8824_spk_event,
+		SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+
+	SND_SOC_DAPM_MIXER("Left Headphone", NAU8824_REG_CLASSG,
+		NAU8824_CLASSG_LDAC_EN_SFT, 0, nau8824_hp_left_mixer,
+		ARRAY_SIZE(nau8824_hp_left_mixer)),
+	SND_SOC_DAPM_MIXER("Right Headphone", NAU8824_REG_CLASSG,
+		NAU8824_CLASSG_RDAC_EN_SFT, 0, nau8824_hp_right_mixer,
+		ARRAY_SIZE(nau8824_hp_right_mixer)),
+	SND_SOC_DAPM_PGA_S("Charge Pump", 1, NAU8824_REG_CHARGE_PUMP_CONTROL,
+		NAU8824_CHARGE_PUMP_EN_SFT, 0, nau8824_pump_event,
+		SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+	SND_SOC_DAPM_PGA("Output Driver L",
+		NAU8824_REG_POWER_UP_CONTROL, 3, 0, NULL, 0),
+	SND_SOC_DAPM_PGA("Output Driver R",
+		NAU8824_REG_POWER_UP_CONTROL, 2, 0, NULL, 0),
+	SND_SOC_DAPM_PGA("Main Driver L",
+		NAU8824_REG_POWER_UP_CONTROL, 1, 0, NULL, 0),
+	SND_SOC_DAPM_PGA("Main Driver R",
+		NAU8824_REG_POWER_UP_CONTROL, 0, 0, NULL, 0),
+	SND_SOC_DAPM_PGA("HP Boost Driver", NAU8824_REG_BOOST,
+		NAU8824_HP_BOOST_DIS_SFT, 1, NULL, 0),
+	SND_SOC_DAPM_PGA("Class G", NAU8824_REG_CLASSG,
+		NAU8824_CLASSG_EN_SFT, 0, NULL, 0),
+
+	SND_SOC_DAPM_OUTPUT("SPKOUTL"),
+	SND_SOC_DAPM_OUTPUT("SPKOUTR"),
+	SND_SOC_DAPM_OUTPUT("HPOL"),
+	SND_SOC_DAPM_OUTPUT("HPOR"),
+};
+
+static const struct snd_soc_dapm_route nau8824_dapm_routes[] = {
+	{"DMIC1 Enable", "Switch", "DMIC1"},
+	{"DMIC2 Enable", "Switch", "DMIC2"},
+	{"DMIC3 Enable", "Switch", "DMIC3"},
+	{"DMIC4 Enable", "Switch", "DMIC4"},
+
+	{"DMIC1", NULL, "DMIC12 Power"},
+	{"DMIC2", NULL, "DMIC12 Power"},
+	{"DMIC3", NULL, "DMIC34 Power"},
+	{"DMIC4", NULL, "DMIC34 Power"},
+	{"DMIC12 Power", NULL, "DMIC Clock"},
+	{"DMIC34 Power", NULL, "DMIC Clock"},
+
+	{"Left ADC", "MIC Switch", "MIC1"},
+	{"Left ADC", "HSMIC Switch", "HSMIC1"},
+	{"Right ADC", "MIC Switch", "MIC2"},
+	{"Right ADC", "HSMIC Switch", "HSMIC2"},
+
+	{"ADCL", NULL, "Left ADC"},
+	{"ADCR", NULL, "Right ADC"},
+
+	{"AIFTX", NULL, "MICBIAS"},
+	{"AIFTX", NULL, "ADCL"},
+	{"AIFTX", NULL, "ADCR"},
+	{"AIFTX", NULL, "DMIC1 Enable"},
+	{"AIFTX", NULL, "DMIC2 Enable"},
+	{"AIFTX", NULL, "DMIC3 Enable"},
+	{"AIFTX", NULL, "DMIC4 Enable"},
+
+	{"AIFTX", NULL, "System Clock"},
+	{"AIFRX", NULL, "System Clock"},
+
+	{"DACL", NULL, "AIFRX"},
+	{"DACL", NULL, "DACL Clock"},
+	{"DACR", NULL, "AIFRX"},
+	{"DACR", NULL, "DACR Clock"},
+
+	{"DACL Mux", "DACL", "DACL"},
+	{"DACL Mux", "DACR", "DACR"},
+	{"DACR Mux", "DACL", "DACL"},
+	{"DACR Mux", "DACR", "DACR"},
+
+	{"Output DACL", NULL, "DACL Mux"},
+	{"Output DACR", NULL, "DACR Mux"},
+
+	{"ClassD", NULL, "Output DACL"},
+	{"ClassD", NULL, "Output DACR"},
+
+	{"Left Headphone", "DAC Left Switch", "Output DACL"},
+	{"Left Headphone", "DAC Right Switch", "Output DACR"},
+	{"Right Headphone", "DAC Left Switch", "Output DACL"},
+	{"Right Headphone", "DAC Right Switch", "Output DACR"},
+
+	{"Charge Pump", NULL, "Left Headphone"},
+	{"Charge Pump", NULL, "Right Headphone"},
+	{"Output Driver L", NULL, "Charge Pump"},
+	{"Output Driver R", NULL, "Charge Pump"},
+	{"Main Driver L", NULL, "Output Driver L"},
+	{"Main Driver R", NULL, "Output Driver R"},
+	{"Class G", NULL, "Main Driver L"},
+	{"Class G", NULL, "Main Driver R"},
+	{"HP Boost Driver", NULL, "Class G"},
+
+	{"SPKOUTL", NULL, "ClassD"},
+	{"SPKOUTR", NULL, "ClassD"},
+	{"HPOL", NULL, "HP Boost Driver"},
+	{"HPOR", NULL, "HP Boost Driver"},
+};
+
+static bool nau8824_is_jack_inserted(struct nau8824 *nau8824)
+{
+	struct snd_soc_jack *jack = nau8824->jack;
+	bool insert = FALSE;
+
+	if (nau8824->irq && jack)
+		insert = jack->status & SND_JACK_HEADPHONE;
+
+	return insert;
+}
+
+static void nau8824_int_status_clear_all(struct regmap *regmap)
+{
+	int active_irq, clear_irq, i;
+
+	/* Reset the intrruption status from rightmost bit if the corres-
+	 * ponding irq event occurs.
+	 */
+	regmap_read(regmap, NAU8824_REG_IRQ, &active_irq);
+	for (i = 0; i < NAU8824_REG_DATA_LEN; i++) {
+		clear_irq = (0x1 << i);
+		if (active_irq & clear_irq)
+			regmap_write(regmap,
+				NAU8824_REG_CLEAR_INT_REG, clear_irq);
+	}
+}
+
+static void nau8824_eject_jack(struct nau8824 *nau8824)
+{
+	struct snd_soc_dapm_context *dapm = nau8824->dapm;
+	struct regmap *regmap = nau8824->regmap;
+
+	/* Clear all interruption status */
+	nau8824_int_status_clear_all(regmap);
+
+	snd_soc_dapm_disable_pin(dapm, "SAR");
+	snd_soc_dapm_disable_pin(dapm, "MICBIAS");
+	snd_soc_dapm_sync(dapm);
+
+	/* Enable the insertion interruption, disable the ejection
+	 * interruption, and then bypass de-bounce circuit.
+	 */
+	regmap_update_bits(regmap, NAU8824_REG_INTERRUPT_SETTING,
+		NAU8824_IRQ_KEY_RELEASE_DIS | NAU8824_IRQ_KEY_SHORT_PRESS_DIS |
+		NAU8824_IRQ_EJECT_DIS | NAU8824_IRQ_INSERT_DIS,
+		NAU8824_IRQ_KEY_RELEASE_DIS | NAU8824_IRQ_KEY_SHORT_PRESS_DIS |
+		NAU8824_IRQ_EJECT_DIS);
+	regmap_update_bits(regmap, NAU8824_REG_INTERRUPT_SETTING_1,
+		NAU8824_IRQ_INSERT_EN | NAU8824_IRQ_EJECT_EN,
+		NAU8824_IRQ_INSERT_EN);
+	regmap_update_bits(regmap, NAU8824_REG_ENA_CTRL,
+		NAU8824_JD_SLEEP_MODE, NAU8824_JD_SLEEP_MODE);
+
+	/* Close clock for jack type detection at manual mode */
+	nau8824_config_sysclk(nau8824, NAU8824_CLK_DIS, 0);
+}
+
+static void nau8824_jdet_work(struct work_struct *work)
+{
+	struct nau8824 *nau8824 = container_of(
+		work, struct nau8824, jdet_work);
+	struct snd_soc_dapm_context *dapm = nau8824->dapm;
+	struct regmap *regmap = nau8824->regmap;
+	int adc_value, event = 0, event_mask = 0;
+
+	snd_soc_dapm_force_enable_pin(dapm, "MICBIAS");
+	snd_soc_dapm_force_enable_pin(dapm, "SAR");
+	snd_soc_dapm_sync(dapm);
+
+	msleep(100);
+
+	regmap_read(regmap, NAU8824_REG_SAR_ADC_DATA_OUT, &adc_value);
+	adc_value = adc_value & NAU8824_SAR_ADC_DATA_MASK;
+	dev_dbg(nau8824->dev, "SAR ADC data 0x%02x\n", adc_value);
+	if (adc_value < HEADSET_SARADC_THD) {
+		event |= SND_JACK_HEADPHONE;
+
+		snd_soc_dapm_disable_pin(dapm, "SAR");
+		snd_soc_dapm_disable_pin(dapm, "MICBIAS");
+		snd_soc_dapm_sync(dapm);
+	} else {
+		event |= SND_JACK_HEADSET;
+	}
+	event_mask |= SND_JACK_HEADSET;
+	snd_soc_jack_report(nau8824->jack, event, event_mask);
+
+	nau8824_sema_release(nau8824);
+}
+
+static void nau8824_setup_auto_irq(struct nau8824 *nau8824)
+{
+	struct regmap *regmap = nau8824->regmap;
+
+	/* Enable jack ejection, short key press and release interruption. */
+	regmap_update_bits(regmap, NAU8824_REG_INTERRUPT_SETTING_1,
+		NAU8824_IRQ_INSERT_EN | NAU8824_IRQ_EJECT_EN,
+		NAU8824_IRQ_EJECT_EN);
+	regmap_update_bits(regmap, NAU8824_REG_INTERRUPT_SETTING,
+		NAU8824_IRQ_EJECT_DIS | NAU8824_IRQ_KEY_RELEASE_DIS |
+		NAU8824_IRQ_KEY_SHORT_PRESS_DIS, 0);
+	/* Enable internal VCO needed for interruptions */
+	nau8824_config_sysclk(nau8824, NAU8824_CLK_INTERNAL, 0);
+	regmap_update_bits(regmap, NAU8824_REG_ENA_CTRL,
+		NAU8824_JD_SLEEP_MODE, 0);
+}
+
+static int nau8824_button_decode(int value)
+{
+	int buttons = 0;
+
+	/* The chip supports up to 8 buttons, but ALSA defines
+	 * only 6 buttons.
+	 */
+	if (value & BIT(0))
+		buttons |= SND_JACK_BTN_0;
+	if (value & BIT(1))
+		buttons |= SND_JACK_BTN_1;
+	if (value & BIT(2))
+		buttons |= SND_JACK_BTN_2;
+	if (value & BIT(3))
+		buttons |= SND_JACK_BTN_3;
+	if (value & BIT(4))
+		buttons |= SND_JACK_BTN_4;
+	if (value & BIT(5))
+		buttons |= SND_JACK_BTN_5;
+
+	return buttons;
+}
+
+#define NAU8824_BUTTONS (SND_JACK_BTN_0 | SND_JACK_BTN_1 | \
+		SND_JACK_BTN_2 | SND_JACK_BTN_3)
+
+static irqreturn_t nau8824_interrupt(int irq, void *data)
+{
+	struct nau8824 *nau8824 = (struct nau8824 *)data;
+	struct regmap *regmap = nau8824->regmap;
+	int active_irq, clear_irq = 0, event = 0, event_mask = 0;
+
+	if (regmap_read(regmap, NAU8824_REG_IRQ, &active_irq)) {
+		dev_err(nau8824->dev, "failed to read irq status\n");
+		return IRQ_NONE;
+	}
+	dev_dbg(nau8824->dev, "IRQ %x\n", active_irq);
+
+	if (active_irq & NAU8824_JACK_EJECTION_DETECTED) {
+		nau8824_eject_jack(nau8824);
+		event_mask |= SND_JACK_HEADSET;
+		clear_irq = NAU8824_JACK_EJECTION_DETECTED;
+		/* release semaphore held after resume,
+		 * and cancel jack detection
+		 */
+		nau8824_sema_release(nau8824);
+		cancel_work_sync(&nau8824->jdet_work);
+	} else if (active_irq & NAU8824_KEY_SHORT_PRESS_IRQ) {
+		int key_status, button_pressed;
+
+		regmap_read(regmap, NAU8824_REG_CLEAR_INT_REG,
+			&key_status);
+
+		/* lower 8 bits of the register are for pressed keys */
+		button_pressed = nau8824_button_decode(key_status);
+
+		event |= button_pressed;
+		dev_dbg(nau8824->dev, "button %x pressed\n", event);
+		event_mask |= NAU8824_BUTTONS;
+		clear_irq = NAU8824_KEY_SHORT_PRESS_IRQ;
+	} else if (active_irq & NAU8824_KEY_RELEASE_IRQ) {
+		event_mask = NAU8824_BUTTONS;
+		clear_irq = NAU8824_KEY_RELEASE_IRQ;
+	} else if (active_irq & NAU8824_JACK_INSERTION_DETECTED) {
+		/* Turn off insertion interruption at manual mode */
+		regmap_update_bits(regmap,
+			NAU8824_REG_INTERRUPT_SETTING,
+			NAU8824_IRQ_INSERT_DIS,
+			NAU8824_IRQ_INSERT_DIS);
+		regmap_update_bits(regmap,
+			NAU8824_REG_INTERRUPT_SETTING_1,
+			NAU8824_IRQ_INSERT_EN, 0);
+		/* detect microphone and jack type */
+		cancel_work_sync(&nau8824->jdet_work);
+		schedule_work(&nau8824->jdet_work);
+
+		/* Enable interruption for jack type detection at audo
+		 * mode which can detect microphone and jack type.
+		 */
+		nau8824_setup_auto_irq(nau8824);
+	}
+
+	if (!clear_irq)
+		clear_irq = active_irq;
+	/* clears the rightmost interruption */
+	regmap_write(regmap, NAU8824_REG_CLEAR_INT_REG, clear_irq);
+
+	if (event_mask)
+		snd_soc_jack_report(nau8824->jack, event, event_mask);
+
+	return IRQ_HANDLED;
+}
+
+static int nau8824_clock_check(struct nau8824 *nau8824,
+	int stream, int rate, int osr)
+{
+	int osrate;
+
+	if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
+		if (osr >= ARRAY_SIZE(osr_dac_sel))
+			return -EINVAL;
+		osrate = osr_dac_sel[osr].osr;
+	} else {
+		if (osr >= ARRAY_SIZE(osr_adc_sel))
+			return -EINVAL;
+		osrate = osr_adc_sel[osr].osr;
+	}
+
+	if (!osrate || rate * osr > CLK_DA_AD_MAX) {
+		dev_err(nau8824->dev, "exceed the maximum frequency of CLK_ADC or CLK_DAC\n");
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int nau8824_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 nau8824 *nau8824 = snd_soc_codec_get_drvdata(codec);
+	unsigned int val_len = 0, osr, ctrl_val, bclk_fs, bclk_div;
+
+	nau8824_sema_acquire(nau8824, HZ);
+
+	/* CLK_DAC or CLK_ADC = OSR * FS
+	 * DAC or ADC clock frequency is defined as Over Sampling Rate (OSR)
+	 * multiplied by the audio sample rate (Fs). Note that the OSR and Fs
+	 * values must be selected such that the maximum frequency is less
+	 * than 6.144 MHz.
+	 */
+	nau8824->fs = params_rate(params);
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+		regmap_read(nau8824->regmap,
+			NAU8824_REG_DAC_FILTER_CTRL_1, &osr);
+		osr &= NAU8824_DAC_OVERSAMPLE_MASK;
+		if (nau8824_clock_check(nau8824, substream->stream,
+			nau8824->fs, osr))
+			return -EINVAL;
+		regmap_update_bits(nau8824->regmap, NAU8824_REG_CLK_DIVIDER,
+			NAU8824_CLK_DAC_SRC_MASK,
+			osr_dac_sel[osr].clk_src << NAU8824_CLK_DAC_SRC_SFT);
+	} else {
+		regmap_read(nau8824->regmap,
+			NAU8824_REG_ADC_FILTER_CTRL, &osr);
+		osr &= NAU8824_ADC_SYNC_DOWN_MASK;
+		if (nau8824_clock_check(nau8824, substream->stream,
+			nau8824->fs, osr))
+			return -EINVAL;
+		regmap_update_bits(nau8824->regmap, NAU8824_REG_CLK_DIVIDER,
+			NAU8824_CLK_ADC_SRC_MASK,
+			osr_adc_sel[osr].clk_src << NAU8824_CLK_ADC_SRC_SFT);
+	}
+
+	/* make BCLK and LRC divde configuration if the codec as master. */
+	regmap_read(nau8824->regmap,
+		NAU8824_REG_PORT0_I2S_PCM_CTRL_2, &ctrl_val);
+	if (ctrl_val & NAU8824_I2S_MS_MASTER) {
+		/* get the bclk and fs ratio */
+		bclk_fs = snd_soc_params_to_bclk(params) / nau8824->fs;
+		if (bclk_fs <= 32)
+			bclk_div = 0x3;
+		else if (bclk_fs <= 64)
+			bclk_div = 0x2;
+		else if (bclk_fs <= 128)
+			bclk_div = 0x1;
+		else if (bclk_fs <= 256)
+			bclk_div = 0;
+		else
+			return -EINVAL;
+		regmap_update_bits(nau8824->regmap,
+			NAU8824_REG_PORT0_I2S_PCM_CTRL_2,
+			NAU8824_I2S_LRC_DIV_MASK | NAU8824_I2S_BLK_DIV_MASK,
+			(bclk_div << NAU8824_I2S_LRC_DIV_SFT) | bclk_div);
+	}
+
+	switch (params_width(params)) {
+	case 16:
+		val_len |= NAU8824_I2S_DL_16;
+		break;
+	case 20:
+		val_len |= NAU8824_I2S_DL_20;
+		break;
+	case 24:
+		val_len |= NAU8824_I2S_DL_24;
+		break;
+	case 32:
+		val_len |= NAU8824_I2S_DL_32;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	regmap_update_bits(nau8824->regmap, NAU8824_REG_PORT0_I2S_PCM_CTRL_1,
+		NAU8824_I2S_DL_MASK, val_len);
+
+	nau8824_sema_release(nau8824);
+
+	return 0;
+}
+
+static int nau8824_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+	struct snd_soc_codec *codec = dai->codec;
+	struct nau8824 *nau8824 = snd_soc_codec_get_drvdata(codec);
+	unsigned int ctrl1_val = 0, ctrl2_val = 0;
+
+	nau8824_sema_acquire(nau8824, HZ);
+
+	switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+	case SND_SOC_DAIFMT_CBM_CFM:
+		ctrl2_val |= NAU8824_I2S_MS_MASTER;
+		break;
+	case SND_SOC_DAIFMT_CBS_CFS:
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+	case SND_SOC_DAIFMT_NB_NF:
+		break;
+	case SND_SOC_DAIFMT_IB_NF:
+		ctrl1_val |= NAU8824_I2S_BP_INV;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+	case SND_SOC_DAIFMT_I2S:
+		ctrl1_val |= NAU8824_I2S_DF_I2S;
+		break;
+	case SND_SOC_DAIFMT_LEFT_J:
+		ctrl1_val |= NAU8824_I2S_DF_LEFT;
+		break;
+	case SND_SOC_DAIFMT_RIGHT_J:
+		ctrl1_val |= NAU8824_I2S_DF_RIGTH;
+		break;
+	case SND_SOC_DAIFMT_DSP_A:
+		ctrl1_val |= NAU8824_I2S_DF_PCM_AB;
+		break;
+	case SND_SOC_DAIFMT_DSP_B:
+		ctrl1_val |= NAU8824_I2S_DF_PCM_AB;
+		ctrl1_val |= NAU8824_I2S_PCMB_EN;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	regmap_update_bits(nau8824->regmap, NAU8824_REG_PORT0_I2S_PCM_CTRL_1,
+		NAU8824_I2S_DF_MASK | NAU8824_I2S_BP_MASK |
+		NAU8824_I2S_PCMB_EN, ctrl1_val);
+	regmap_update_bits(nau8824->regmap, NAU8824_REG_PORT0_I2S_PCM_CTRL_2,
+		NAU8824_I2S_MS_MASK, ctrl2_val);
+
+	nau8824_sema_release(nau8824);
+
+	return 0;
+}
+
+/**
+ * nau8824_calc_fll_param - Calculate FLL parameters.
+ * @fll_in: external clock provided to codec.
+ * @fs: sampling rate.
+ * @fll_param: Pointer to structure of FLL parameters.
+ *
+ * Calculate FLL parameters to configure codec.
+ *
+ * Returns 0 for success or negative error code.
+ */
+static int nau8824_calc_fll_param(unsigned int fll_in,
+	unsigned int fs, struct nau8824_fll *fll_param)
+{
+	u64 fvco, fvco_max;
+	unsigned int fref, i, fvco_sel;
+
+	/* Ensure the reference clock frequency (FREF) is <= 13.5MHz by dividing
+	 * freq_in by 1, 2, 4, or 8 using FLL pre-scalar.
+	 * FREF = freq_in / NAU8824_FLL_REF_DIV_MASK
+	 */
+	for (i = 0; i < ARRAY_SIZE(fll_pre_scalar); i++) {
+		fref = fll_in / fll_pre_scalar[i].param;
+		if (fref <= NAU_FREF_MAX)
+			break;
+	}
+	if (i == ARRAY_SIZE(fll_pre_scalar))
+		return -EINVAL;
+	fll_param->clk_ref_div = fll_pre_scalar[i].val;
+
+	/* Choose the FLL ratio based on FREF */
+	for (i = 0; i < ARRAY_SIZE(fll_ratio); i++) {
+		if (fref >= fll_ratio[i].param)
+			break;
+	}
+	if (i == ARRAY_SIZE(fll_ratio))
+		return -EINVAL;
+	fll_param->ratio = fll_ratio[i].val;
+
+	/* Calculate the frequency of DCO (FDCO) given freq_out = 256 * Fs.
+	 * FDCO must be within the 90MHz - 124MHz or the FFL cannot be
+	 * guaranteed across the full range of operation.
+	 * FDCO = freq_out * 2 * mclk_src_scaling
+	 */
+	fvco_max = 0;
+	fvco_sel = ARRAY_SIZE(mclk_src_scaling);
+	for (i = 0; i < ARRAY_SIZE(mclk_src_scaling); i++) {
+		fvco = 256 * fs * 2 * mclk_src_scaling[i].param;
+		if (fvco > NAU_FVCO_MIN && fvco < NAU_FVCO_MAX &&
+			fvco_max < fvco) {
+			fvco_max = fvco;
+			fvco_sel = i;
+		}
+	}
+	if (ARRAY_SIZE(mclk_src_scaling) == fvco_sel)
+		return -EINVAL;
+	fll_param->mclk_src = mclk_src_scaling[fvco_sel].val;
+
+	/* Calculate the FLL 10-bit integer input and the FLL 16-bit fractional
+	 * input based on FDCO, FREF and FLL ratio.
+	 */
+	fvco = div_u64(fvco_max << 16, fref * fll_param->ratio);
+	fll_param->fll_int = (fvco >> 16) & 0x3FF;
+	fll_param->fll_frac = fvco & 0xFFFF;
+	return 0;
+}
+
+static void nau8824_fll_apply(struct regmap *regmap,
+	struct nau8824_fll *fll_param)
+{
+	regmap_update_bits(regmap, NAU8824_REG_CLK_DIVIDER,
+		NAU8824_CLK_SRC_MASK | NAU8824_CLK_MCLK_SRC_MASK,
+		NAU8824_CLK_SRC_MCLK | fll_param->mclk_src);
+	regmap_update_bits(regmap, NAU8824_REG_FLL1,
+		NAU8824_FLL_RATIO_MASK, fll_param->ratio);
+	/* FLL 16-bit fractional input */
+	regmap_write(regmap, NAU8824_REG_FLL2, fll_param->fll_frac);
+	/* FLL 10-bit integer input */
+	regmap_update_bits(regmap, NAU8824_REG_FLL3,
+		NAU8824_FLL_INTEGER_MASK, fll_param->fll_int);
+	/* FLL pre-scaler */
+	regmap_update_bits(regmap, NAU8824_REG_FLL4,
+		NAU8824_FLL_REF_DIV_MASK,
+		fll_param->clk_ref_div << NAU8824_FLL_REF_DIV_SFT);
+	/* select divided VCO input */
+	regmap_update_bits(regmap, NAU8824_REG_FLL5,
+		NAU8824_FLL_CLK_SW_MASK, NAU8824_FLL_CLK_SW_REF);
+	/* Disable free-running mode */
+	regmap_update_bits(regmap,
+		NAU8824_REG_FLL6, NAU8824_DCO_EN, 0);
+	if (fll_param->fll_frac) {
+		regmap_update_bits(regmap, NAU8824_REG_FLL5,
+			NAU8824_FLL_PDB_DAC_EN | NAU8824_FLL_LOOP_FTR_EN |
+			NAU8824_FLL_FTR_SW_MASK,
+			NAU8824_FLL_PDB_DAC_EN | NAU8824_FLL_LOOP_FTR_EN |
+			NAU8824_FLL_FTR_SW_FILTER);
+		regmap_update_bits(regmap, NAU8824_REG_FLL6,
+			NAU8824_SDM_EN, NAU8824_SDM_EN);
+	} else {
+		regmap_update_bits(regmap, NAU8824_REG_FLL5,
+			NAU8824_FLL_PDB_DAC_EN | NAU8824_FLL_LOOP_FTR_EN |
+			NAU8824_FLL_FTR_SW_MASK, NAU8824_FLL_FTR_SW_ACCU);
+		regmap_update_bits(regmap,
+			NAU8824_REG_FLL6, NAU8824_SDM_EN, 0);
+	}
+}
+
+/* freq_out must be 256*Fs in order to achieve the best performance */
+static int nau8824_set_pll(struct snd_soc_codec *codec, int pll_id, int source,
+		unsigned int freq_in, unsigned int freq_out)
+{
+	struct nau8824 *nau8824 = snd_soc_codec_get_drvdata(codec);
+	struct nau8824_fll fll_param;
+	int ret, fs;
+
+	fs = freq_out / 256;
+	ret = nau8824_calc_fll_param(freq_in, fs, &fll_param);
+	if (ret < 0) {
+		dev_err(nau8824->dev, "Unsupported input clock %d\n", freq_in);
+		return ret;
+	}
+	dev_dbg(nau8824->dev, "mclk_src=%x ratio=%x fll_frac=%x fll_int=%x clk_ref_div=%x\n",
+		fll_param.mclk_src, fll_param.ratio, fll_param.fll_frac,
+		fll_param.fll_int, fll_param.clk_ref_div);
+
+	nau8824_fll_apply(nau8824->regmap, &fll_param);
+	mdelay(2);
+	regmap_update_bits(nau8824->regmap, NAU8824_REG_CLK_DIVIDER,
+		NAU8824_CLK_SRC_MASK, NAU8824_CLK_SRC_VCO);
+
+	return 0;
+}
+
+static int nau8824_config_sysclk(struct nau8824 *nau8824,
+	int clk_id, unsigned int freq)
+{
+	struct regmap *regmap = nau8824->regmap;
+
+	switch (clk_id) {
+	case NAU8824_CLK_DIS:
+		regmap_update_bits(regmap, NAU8824_REG_CLK_DIVIDER,
+			NAU8824_CLK_SRC_MASK, NAU8824_CLK_SRC_MCLK);
+		regmap_update_bits(regmap, NAU8824_REG_FLL6,
+			NAU8824_DCO_EN, 0);
+		break;
+
+	case NAU8824_CLK_MCLK:
+		nau8824_sema_acquire(nau8824, HZ);
+		regmap_update_bits(regmap, NAU8824_REG_CLK_DIVIDER,
+			NAU8824_CLK_SRC_MASK, NAU8824_CLK_SRC_MCLK);
+		regmap_update_bits(regmap, NAU8824_REG_FLL6,
+			NAU8824_DCO_EN, 0);
+		nau8824_sema_release(nau8824);
+		break;
+
+	case NAU8824_CLK_INTERNAL:
+		regmap_update_bits(regmap, NAU8824_REG_FLL6,
+			NAU8824_DCO_EN, NAU8824_DCO_EN);
+		regmap_update_bits(regmap, NAU8824_REG_CLK_DIVIDER,
+			NAU8824_CLK_SRC_MASK, NAU8824_CLK_SRC_VCO);
+		break;
+
+	case NAU8824_CLK_FLL_MCLK:
+		nau8824_sema_acquire(nau8824, HZ);
+		regmap_update_bits(regmap, NAU8824_REG_FLL3,
+			NAU8824_FLL_CLK_SRC_MASK, NAU8824_FLL_CLK_SRC_MCLK);
+		nau8824_sema_release(nau8824);
+		break;
+
+	case NAU8824_CLK_FLL_BLK:
+		nau8824_sema_acquire(nau8824, HZ);
+		regmap_update_bits(regmap, NAU8824_REG_FLL3,
+			NAU8824_FLL_CLK_SRC_MASK, NAU8824_FLL_CLK_SRC_BLK);
+		nau8824_sema_release(nau8824);
+		break;
+
+	case NAU8824_CLK_FLL_FS:
+		nau8824_sema_acquire(nau8824, HZ);
+		regmap_update_bits(regmap, NAU8824_REG_FLL3,
+			NAU8824_FLL_CLK_SRC_MASK, NAU8824_FLL_CLK_SRC_FS);
+		nau8824_sema_release(nau8824);
+		break;
+
+	default:
+		dev_err(nau8824->dev, "Invalid clock id (%d)\n", clk_id);
+		return -EINVAL;
+	}
+
+	dev_dbg(nau8824->dev, "Sysclk is %dHz and clock id is %d\n", freq,
+		clk_id);
+
+	return 0;
+}
+
+static int nau8824_set_sysclk(struct snd_soc_codec *codec,
+	int clk_id, int source, unsigned int freq, int dir)
+{
+	struct nau8824 *nau8824 = snd_soc_codec_get_drvdata(codec);
+
+	return nau8824_config_sysclk(nau8824, clk_id, freq);
+}
+
+static void nau8824_resume_setup(struct nau8824 *nau8824)
+{
+	nau8824_config_sysclk(nau8824, NAU8824_CLK_DIS, 0);
+	if (nau8824->irq) {
+		/* Clear all interruption status */
+		nau8824_int_status_clear_all(nau8824->regmap);
+		/* Enable jack detection at sleep mode, insertion detection,
+		 * and ejection detection.
+		 */
+		regmap_update_bits(nau8824->regmap, NAU8824_REG_ENA_CTRL,
+			NAU8824_JD_SLEEP_MODE, NAU8824_JD_SLEEP_MODE);
+		regmap_update_bits(nau8824->regmap,
+			NAU8824_REG_INTERRUPT_SETTING_1,
+			NAU8824_IRQ_EJECT_EN | NAU8824_IRQ_INSERT_EN,
+			NAU8824_IRQ_EJECT_EN | NAU8824_IRQ_INSERT_EN);
+		regmap_update_bits(nau8824->regmap,
+			NAU8824_REG_INTERRUPT_SETTING,
+			NAU8824_IRQ_EJECT_DIS | NAU8824_IRQ_INSERT_DIS, 0);
+	}
+}
+
+static int nau8824_set_bias_level(struct snd_soc_codec *codec,
+	enum snd_soc_bias_level level)
+{
+	struct nau8824 *nau8824 = snd_soc_codec_get_drvdata(codec);
+
+	switch (level) {
+	case SND_SOC_BIAS_ON:
+		break;
+
+	case SND_SOC_BIAS_PREPARE:
+		break;
+
+	case SND_SOC_BIAS_STANDBY:
+		if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) {
+			/* Setup codec configuration after resume */
+			nau8824_resume_setup(nau8824);
+		}
+		break;
+
+	case SND_SOC_BIAS_OFF:
+		regmap_update_bits(nau8824->regmap,
+			NAU8824_REG_INTERRUPT_SETTING, 0x3ff, 0x3ff);
+		regmap_update_bits(nau8824->regmap,
+			NAU8824_REG_INTERRUPT_SETTING_1,
+			NAU8824_IRQ_EJECT_EN | NAU8824_IRQ_INSERT_EN, 0);
+		break;
+	}
+
+	return 0;
+}
+
+static int nau8824_codec_probe(struct snd_soc_codec *codec)
+{
+	struct nau8824 *nau8824 = snd_soc_codec_get_drvdata(codec);
+	struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
+
+	nau8824->dapm = dapm;
+
+	return 0;
+}
+
+static int __maybe_unused nau8824_suspend(struct snd_soc_codec *codec)
+{
+	struct nau8824 *nau8824 = snd_soc_codec_get_drvdata(codec);
+
+	if (nau8824->irq) {
+		disable_irq(nau8824->irq);
+		snd_soc_codec_force_bias_level(codec, SND_SOC_BIAS_OFF);
+	}
+	regcache_cache_only(nau8824->regmap, true);
+	regcache_mark_dirty(nau8824->regmap);
+
+	return 0;
+}
+
+static int __maybe_unused nau8824_resume(struct snd_soc_codec *codec)
+{
+	struct nau8824 *nau8824 = snd_soc_codec_get_drvdata(codec);
+
+	regcache_cache_only(nau8824->regmap, false);
+	regcache_sync(nau8824->regmap);
+	if (nau8824->irq) {
+		/* Hold semaphore to postpone playback happening
+		 * until jack detection done.
+		 */
+		nau8824_sema_acquire(nau8824, 0);
+		enable_irq(nau8824->irq);
+	}
+
+	return 0;
+}
+
+static struct snd_soc_codec_driver nau8824_codec_driver = {
+	.probe = nau8824_codec_probe,
+	.set_sysclk = nau8824_set_sysclk,
+	.set_pll = nau8824_set_pll,
+	.set_bias_level = nau8824_set_bias_level,
+	.suspend = nau8824_suspend,
+	.resume = nau8824_resume,
+	.suspend_bias_off = true,
+
+	.component_driver = {
+		.controls = nau8824_snd_controls,
+		.num_controls = ARRAY_SIZE(nau8824_snd_controls),
+		.dapm_widgets = nau8824_dapm_widgets,
+		.num_dapm_widgets = ARRAY_SIZE(nau8824_dapm_widgets),
+		.dapm_routes = nau8824_dapm_routes,
+		.num_dapm_routes = ARRAY_SIZE(nau8824_dapm_routes),
+	},
+};
+
+static const struct snd_soc_dai_ops nau8824_dai_ops = {
+	.hw_params = nau8824_hw_params,
+	.set_fmt = nau8824_set_fmt,
+};
+
+#define NAU8824_RATES SNDRV_PCM_RATE_8000_192000
+#define NAU8824_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE \
+	 | SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S32_LE)
+
+static struct snd_soc_dai_driver nau8824_dai = {
+	.name = NAU8824_CODEC_DAI,
+	.playback = {
+		.stream_name	 = "Playback",
+		.channels_min	 = 1,
+		.channels_max	 = 2,
+		.rates		 = NAU8824_RATES,
+		.formats	 = NAU8824_FORMATS,
+	},
+	.capture = {
+		.stream_name	 = "Capture",
+		.channels_min	 = 1,
+		.channels_max	 = 2,
+		.rates		 = NAU8824_RATES,
+		.formats	 = NAU8824_FORMATS,
+	},
+	.ops = &nau8824_dai_ops,
+};
+
+static const struct regmap_config nau8824_regmap_config = {
+	.val_bits = NAU8824_REG_ADDR_LEN,
+	.reg_bits = NAU8824_REG_DATA_LEN,
+
+	.max_register = NAU8824_REG_MAX,
+	.readable_reg = nau8824_readable_reg,
+	.writeable_reg = nau8824_writeable_reg,
+	.volatile_reg = nau8824_volatile_reg,
+
+	.cache_type = REGCACHE_RBTREE,
+	.reg_defaults = nau8824_reg_defaults,
+	.num_reg_defaults = ARRAY_SIZE(nau8824_reg_defaults),
+};
+
+/**
+ * nau8824_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 will be routed to the given jack.  Jack can be null to stop
+ * reporting.
+ */
+int nau8824_enable_jack_detect(struct snd_soc_codec *codec,
+	struct snd_soc_jack *jack)
+{
+	struct nau8824 *nau8824 = snd_soc_codec_get_drvdata(codec);
+	int ret;
+
+	nau8824->jack = jack;
+	/* Initiate jack detection work queue */
+	INIT_WORK(&nau8824->jdet_work, nau8824_jdet_work);
+	ret = devm_request_threaded_irq(nau8824->dev, nau8824->irq, NULL,
+		nau8824_interrupt, IRQF_TRIGGER_LOW | IRQF_ONESHOT,
+		"nau8824", nau8824);
+	if (ret) {
+		dev_err(nau8824->dev, "Cannot request irq %d (%d)\n",
+			nau8824->irq, ret);
+	}
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(nau8824_enable_jack_detect);
+
+static void nau8824_reset_chip(struct regmap *regmap)
+{
+	regmap_write(regmap, NAU8824_REG_RESET, 0x00);
+	regmap_write(regmap, NAU8824_REG_RESET, 0x00);
+}
+
+static void nau8824_setup_buttons(struct nau8824 *nau8824)
+{
+	struct regmap *regmap = nau8824->regmap;
+
+	regmap_update_bits(regmap, NAU8824_REG_SAR_ADC,
+		NAU8824_SAR_TRACKING_GAIN_MASK,
+		nau8824->sar_voltage << NAU8824_SAR_TRACKING_GAIN_SFT);
+	regmap_update_bits(regmap, NAU8824_REG_SAR_ADC,
+		NAU8824_SAR_COMPARE_TIME_MASK,
+		nau8824->sar_compare_time << NAU8824_SAR_COMPARE_TIME_SFT);
+	regmap_update_bits(regmap, NAU8824_REG_SAR_ADC,
+		NAU8824_SAR_SAMPLING_TIME_MASK,
+		nau8824->sar_sampling_time << NAU8824_SAR_SAMPLING_TIME_SFT);
+
+	regmap_update_bits(regmap, NAU8824_REG_VDET_COEFFICIENT,
+		NAU8824_LEVELS_NR_MASK,
+		(nau8824->sar_threshold_num - 1) << NAU8824_LEVELS_NR_SFT);
+	regmap_update_bits(regmap, NAU8824_REG_VDET_COEFFICIENT,
+		NAU8824_HYSTERESIS_MASK,
+		nau8824->sar_hysteresis << NAU8824_HYSTERESIS_SFT);
+	regmap_update_bits(regmap, NAU8824_REG_VDET_COEFFICIENT,
+		NAU8824_SHORTKEY_DEBOUNCE_MASK,
+		nau8824->key_debounce << NAU8824_SHORTKEY_DEBOUNCE_SFT);
+
+	regmap_write(regmap, NAU8824_REG_VDET_THRESHOLD_1,
+		(nau8824->sar_threshold[0] << 8) | nau8824->sar_threshold[1]);
+	regmap_write(regmap, NAU8824_REG_VDET_THRESHOLD_2,
+		(nau8824->sar_threshold[2] << 8) | nau8824->sar_threshold[3]);
+	regmap_write(regmap, NAU8824_REG_VDET_THRESHOLD_3,
+		(nau8824->sar_threshold[4] << 8) | nau8824->sar_threshold[5]);
+	regmap_write(regmap, NAU8824_REG_VDET_THRESHOLD_4,
+		(nau8824->sar_threshold[6] << 8) | nau8824->sar_threshold[7]);
+}
+
+static void nau8824_init_regs(struct nau8824 *nau8824)
+{
+	struct regmap *regmap = nau8824->regmap;
+
+	/* Enable Bias/VMID/VMID Tieoff */
+	regmap_update_bits(regmap, NAU8824_REG_BIAS_ADJ,
+		NAU8824_VMID | NAU8824_VMID_SEL_MASK, NAU8824_VMID |
+		(nau8824->vref_impedance << NAU8824_VMID_SEL_SFT));
+	regmap_update_bits(regmap, NAU8824_REG_BOOST,
+		NAU8824_GLOBAL_BIAS_EN, NAU8824_GLOBAL_BIAS_EN);
+	mdelay(2);
+	regmap_update_bits(regmap, NAU8824_REG_MIC_BIAS,
+		NAU8824_MICBIAS_VOLTAGE_MASK, nau8824->micbias_voltage);
+	/* Disable Boost Driver, Automatic Short circuit protection enable */
+	regmap_update_bits(regmap, NAU8824_REG_BOOST,
+		NAU8824_PRECHARGE_DIS | NAU8824_HP_BOOST_DIS |
+		NAU8824_HP_BOOST_G_DIS | NAU8824_SHORT_SHUTDOWN_EN,
+		NAU8824_PRECHARGE_DIS | NAU8824_HP_BOOST_DIS |
+		NAU8824_HP_BOOST_G_DIS | NAU8824_SHORT_SHUTDOWN_EN);
+	/* Scaling for ADC and DAC clock */
+	regmap_update_bits(regmap, NAU8824_REG_CLK_DIVIDER,
+		NAU8824_CLK_ADC_SRC_MASK | NAU8824_CLK_DAC_SRC_MASK,
+		(0x1 << NAU8824_CLK_ADC_SRC_SFT) |
+		(0x1 << NAU8824_CLK_DAC_SRC_SFT));
+	regmap_update_bits(regmap, NAU8824_REG_DAC_MUTE_CTRL,
+		NAU8824_DAC_ZC_EN, NAU8824_DAC_ZC_EN);
+	regmap_update_bits(regmap, NAU8824_REG_ENA_CTRL,
+		NAU8824_DAC_CH1_EN | NAU8824_DAC_CH0_EN |
+		NAU8824_ADC_CH0_EN | NAU8824_ADC_CH1_EN |
+		NAU8824_ADC_CH2_EN | NAU8824_ADC_CH3_EN,
+		NAU8824_DAC_CH1_EN | NAU8824_DAC_CH0_EN |
+		NAU8824_ADC_CH0_EN | NAU8824_ADC_CH1_EN |
+		NAU8824_ADC_CH2_EN | NAU8824_ADC_CH3_EN);
+	regmap_update_bits(regmap, NAU8824_REG_CLK_GATING_ENA,
+		NAU8824_CLK_ADC_CH23_EN | NAU8824_CLK_ADC_CH01_EN |
+		NAU8824_CLK_DAC_CH1_EN | NAU8824_CLK_DAC_CH0_EN |
+		NAU8824_CLK_I2S_EN | NAU8824_CLK_GAIN_EN |
+		NAU8824_CLK_SAR_EN | NAU8824_CLK_DMIC_CH23_EN,
+		NAU8824_CLK_ADC_CH23_EN | NAU8824_CLK_ADC_CH01_EN |
+		NAU8824_CLK_DAC_CH1_EN | NAU8824_CLK_DAC_CH0_EN |
+		NAU8824_CLK_I2S_EN | NAU8824_CLK_GAIN_EN |
+		NAU8824_CLK_SAR_EN | NAU8824_CLK_DMIC_CH23_EN);
+	/* Class G timer 64ms */
+	regmap_update_bits(regmap, NAU8824_REG_CLASSG,
+		NAU8824_CLASSG_TIMER_MASK,
+		0x20 << NAU8824_CLASSG_TIMER_SFT);
+	regmap_update_bits(regmap, NAU8824_REG_TRIM_SETTINGS,
+		NAU8824_DRV_CURR_INC, NAU8824_DRV_CURR_INC);
+	/* Disable DACR/L power */
+	regmap_update_bits(regmap, NAU8824_REG_CHARGE_PUMP_CONTROL,
+		NAU8824_SPKR_PULL_DOWN | NAU8824_SPKL_PULL_DOWN |
+		NAU8824_POWER_DOWN_DACR | NAU8824_POWER_DOWN_DACL,
+		NAU8824_SPKR_PULL_DOWN | NAU8824_SPKL_PULL_DOWN |
+		NAU8824_POWER_DOWN_DACR | NAU8824_POWER_DOWN_DACL);
+	/* Enable TESTDAC. This sets the analog DAC inputs to a '0' input
+	 * signal to avoid any glitches due to power up transients in both
+	 * the analog and digital DAC circuit.
+	 */
+	regmap_update_bits(regmap, NAU8824_REG_ENABLE_LO,
+		NAU8824_TEST_DAC_EN, NAU8824_TEST_DAC_EN);
+	/* Config L/R channel */
+	regmap_update_bits(regmap, NAU8824_REG_DAC_CH0_DGAIN_CTRL,
+		NAU8824_DAC_CH0_SEL_MASK, NAU8824_DAC_CH0_SEL_I2S0);
+	regmap_update_bits(regmap, NAU8824_REG_DAC_CH1_DGAIN_CTRL,
+		NAU8824_DAC_CH1_SEL_MASK, NAU8824_DAC_CH1_SEL_I2S1);
+	regmap_update_bits(regmap, NAU8824_REG_ENABLE_LO,
+		NAU8824_DACR_HPR_EN | NAU8824_DACL_HPL_EN,
+		NAU8824_DACR_HPR_EN | NAU8824_DACL_HPL_EN);
+	/* Default oversampling/decimations settings are unusable
+	 * (audible hiss). Set it to something better.
+	 */
+	regmap_update_bits(regmap, NAU8824_REG_ADC_FILTER_CTRL,
+		NAU8824_ADC_SYNC_DOWN_MASK, NAU8824_ADC_SYNC_DOWN_64);
+	regmap_update_bits(regmap, NAU8824_REG_DAC_FILTER_CTRL_1,
+		NAU8824_DAC_CICCLP_OFF | NAU8824_DAC_OVERSAMPLE_MASK,
+		NAU8824_DAC_CICCLP_OFF | NAU8824_DAC_OVERSAMPLE_64);
+	/* DAC clock delay 2ns, VREF */
+	regmap_update_bits(regmap, NAU8824_REG_RDAC,
+		NAU8824_RDAC_CLK_DELAY_MASK | NAU8824_RDAC_VREF_MASK,
+		(0x2 << NAU8824_RDAC_CLK_DELAY_SFT) |
+		(0x3 << NAU8824_RDAC_VREF_SFT));
+	/* PGA input mode selection */
+	regmap_update_bits(regmap, NAU8824_REG_FEPGA,
+		NAU8824_FEPGA_MODEL_SHORT_EN | NAU8824_FEPGA_MODER_SHORT_EN,
+		NAU8824_FEPGA_MODEL_SHORT_EN | NAU8824_FEPGA_MODER_SHORT_EN);
+	/* Digital microphone control */
+	regmap_update_bits(regmap, NAU8824_REG_ANALOG_CONTROL_1,
+		NAU8824_DMIC_CLK_DRV_STRG | NAU8824_DMIC_CLK_SLEW_FAST,
+		NAU8824_DMIC_CLK_DRV_STRG | NAU8824_DMIC_CLK_SLEW_FAST);
+	regmap_update_bits(regmap, NAU8824_REG_JACK_DET_CTRL,
+		NAU8824_JACK_LOGIC,
+		/* jkdet_polarity - 1  is for active-low */
+		nau8824->jkdet_polarity ? 0 : NAU8824_JACK_LOGIC);
+	regmap_update_bits(regmap,
+		NAU8824_REG_JACK_DET_CTRL, NAU8824_JACK_EJECT_DT_MASK,
+		(nau8824->jack_eject_debounce << NAU8824_JACK_EJECT_DT_SFT));
+	if (nau8824->sar_threshold_num)
+		nau8824_setup_buttons(nau8824);
+}
+
+static int nau8824_setup_irq(struct nau8824 *nau8824)
+{
+	/* Disable interruption before codec initiation done */
+	regmap_update_bits(nau8824->regmap, NAU8824_REG_ENA_CTRL,
+		NAU8824_JD_SLEEP_MODE, NAU8824_JD_SLEEP_MODE);
+	regmap_update_bits(nau8824->regmap,
+		NAU8824_REG_INTERRUPT_SETTING, 0x3ff, 0x3ff);
+	regmap_update_bits(nau8824->regmap, NAU8824_REG_INTERRUPT_SETTING_1,
+		NAU8824_IRQ_EJECT_EN | NAU8824_IRQ_INSERT_EN, 0);
+
+	return 0;
+}
+
+static void nau8824_print_device_properties(struct nau8824 *nau8824)
+{
+	struct device *dev = nau8824->dev;
+	int i;
+
+	dev_dbg(dev, "jkdet-polarity:       %d\n", nau8824->jkdet_polarity);
+	dev_dbg(dev, "micbias-voltage:      %d\n", nau8824->micbias_voltage);
+	dev_dbg(dev, "vref-impedance:       %d\n", nau8824->vref_impedance);
+
+	dev_dbg(dev, "sar-threshold-num:    %d\n", nau8824->sar_threshold_num);
+	for (i = 0; i < nau8824->sar_threshold_num; i++)
+		dev_dbg(dev, "sar-threshold[%d]=%x\n", i,
+				nau8824->sar_threshold[i]);
+
+	dev_dbg(dev, "sar-hysteresis:       %d\n", nau8824->sar_hysteresis);
+	dev_dbg(dev, "sar-voltage:          %d\n", nau8824->sar_voltage);
+	dev_dbg(dev, "sar-compare-time:     %d\n", nau8824->sar_compare_time);
+	dev_dbg(dev, "sar-sampling-time:    %d\n", nau8824->sar_sampling_time);
+	dev_dbg(dev, "short-key-debounce:   %d\n", nau8824->key_debounce);
+	dev_dbg(dev, "jack-eject-debounce:  %d\n",
+			nau8824->jack_eject_debounce);
+}
+
+static int nau8824_read_device_properties(struct device *dev,
+	struct nau8824 *nau8824) {
+	int ret;
+
+	ret = device_property_read_u32(dev, "nuvoton,jkdet-polarity",
+		&nau8824->jkdet_polarity);
+	if (ret)
+		nau8824->jkdet_polarity = 1;
+	ret = device_property_read_u32(dev, "nuvoton,micbias-voltage",
+		&nau8824->micbias_voltage);
+	if (ret)
+		nau8824->micbias_voltage = 6;
+	ret = device_property_read_u32(dev, "nuvoton,vref-impedance",
+		&nau8824->vref_impedance);
+	if (ret)
+		nau8824->vref_impedance = 2;
+	ret = device_property_read_u32(dev, "nuvoton,sar-threshold-num",
+		&nau8824->sar_threshold_num);
+	if (ret)
+		nau8824->sar_threshold_num = 4;
+	ret = device_property_read_u32_array(dev, "nuvoton,sar-threshold",
+		nau8824->sar_threshold, nau8824->sar_threshold_num);
+	if (ret) {
+		nau8824->sar_threshold[0] = 0x0a;
+		nau8824->sar_threshold[1] = 0x14;
+		nau8824->sar_threshold[2] = 0x26;
+		nau8824->sar_threshold[3] = 0x73;
+	}
+	ret = device_property_read_u32(dev, "nuvoton,sar-hysteresis",
+		&nau8824->sar_hysteresis);
+	if (ret)
+		nau8824->sar_hysteresis = 0;
+	ret = device_property_read_u32(dev, "nuvoton,sar-voltage",
+		&nau8824->sar_voltage);
+	if (ret)
+		nau8824->sar_voltage = 6;
+	ret = device_property_read_u32(dev, "nuvoton,sar-compare-time",
+		&nau8824->sar_compare_time);
+	if (ret)
+		nau8824->sar_compare_time = 1;
+	ret = device_property_read_u32(dev, "nuvoton,sar-sampling-time",
+		&nau8824->sar_sampling_time);
+	if (ret)
+		nau8824->sar_sampling_time = 1;
+	ret = device_property_read_u32(dev, "nuvoton,short-key-debounce",
+		&nau8824->key_debounce);
+	if (ret)
+		nau8824->key_debounce = 0;
+	ret = device_property_read_u32(dev, "nuvoton,jack-eject-debounce",
+		&nau8824->jack_eject_debounce);
+	if (ret)
+		nau8824->jack_eject_debounce = 1;
+
+	return 0;
+}
+
+static int nau8824_i2c_probe(struct i2c_client *i2c,
+	const struct i2c_device_id *id)
+{
+	struct device *dev = &i2c->dev;
+	struct nau8824 *nau8824 = dev_get_platdata(dev);
+	int ret, value;
+
+	if (!nau8824) {
+		nau8824 = devm_kzalloc(dev, sizeof(*nau8824), GFP_KERNEL);
+		if (!nau8824)
+			return -ENOMEM;
+		ret = nau8824_read_device_properties(dev, nau8824);
+		if (ret)
+			return ret;
+	}
+	i2c_set_clientdata(i2c, nau8824);
+
+	nau8824->regmap = devm_regmap_init_i2c(i2c, &nau8824_regmap_config);
+	if (IS_ERR(nau8824->regmap))
+		return PTR_ERR(nau8824->regmap);
+	nau8824->dev = dev;
+	nau8824->irq = i2c->irq;
+	sema_init(&nau8824->jd_sem, 1);
+
+	nau8824_print_device_properties(nau8824);
+
+	ret = regmap_read(nau8824->regmap, NAU8824_REG_I2C_DEVICE_ID, &value);
+	if (ret < 0) {
+		dev_err(dev, "Failed to read device id from the NAU8824: %d\n",
+			ret);
+		return ret;
+	}
+	nau8824_reset_chip(nau8824->regmap);
+	nau8824_init_regs(nau8824);
+
+	if (i2c->irq)
+		nau8824_setup_irq(nau8824);
+
+	return snd_soc_register_codec(dev,
+		&nau8824_codec_driver, &nau8824_dai, 1);
+}
+
+
+static int nau8824_i2c_remove(struct i2c_client *client)
+{
+	snd_soc_unregister_codec(&client->dev);
+	return 0;
+}
+
+static const struct i2c_device_id nau8824_i2c_ids[] = {
+	{ "nau8824", 0 },
+	{ }
+};
+MODULE_DEVICE_TABLE(i2c, nau8824_i2c_ids);
+
+#ifdef CONFIG_OF
+static const struct of_device_id nau8824_of_ids[] = {
+	{ .compatible = "nuvoton,nau8824", },
+	{}
+};
+MODULE_DEVICE_TABLE(of, nau8824_of_ids);
+#endif
+
+#ifdef CONFIG_ACPI
+static const struct acpi_device_id nau8824_acpi_match[] = {
+	{ "10508824", 0 },
+	{},
+};
+MODULE_DEVICE_TABLE(acpi, nau8824_acpi_match);
+#endif
+
+static struct i2c_driver nau8824_i2c_driver = {
+	.driver = {
+		.name = "nau8824",
+		.of_match_table = of_match_ptr(nau8824_of_ids),
+		.acpi_match_table = ACPI_PTR(nau8824_acpi_match),
+	},
+	.probe = nau8824_i2c_probe,
+	.remove = nau8824_i2c_remove,
+	.id_table = nau8824_i2c_ids,
+};
+module_i2c_driver(nau8824_i2c_driver);
+
+
+MODULE_DESCRIPTION("ASoC NAU88L24 driver");
+MODULE_AUTHOR("John Hsu <KCHSU0@nuvoton.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/nau8824.h b/sound/soc/codecs/nau8824.h
new file mode 100644
index 000000000000..87ac9a382aed
--- /dev/null
+++ b/sound/soc/codecs/nau8824.h
@@ -0,0 +1,466 @@
+/*
+ * NAU88L24 ALSA SoC audio driver
+ *
+ * Copyright 2016 Nuvoton Technology Corp.
+ * Author: John Hsu <KCHSU0@nuvoton.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 __NAU8824_H__
+#define __NAU8824_H__
+
+#define NAU8824_REG_RESET			0x00
+#define NAU8824_REG_ENA_CTRL			0x01
+#define NAU8824_REG_CLK_GATING_ENA		0x02
+#define NAU8824_REG_CLK_DIVIDER		0x03
+#define NAU8824_REG_FLL1			0x04
+#define NAU8824_REG_FLL2			0x05
+#define NAU8824_REG_FLL3			0x06
+#define NAU8824_REG_FLL4			0x07
+#define NAU8824_REG_FLL5			0x08
+#define NAU8824_REG_FLL6			0x09
+#define NAU8824_REG_FLL_VCO_RSV		0x0A
+#define NAU8824_REG_JACK_DET_CTRL		0x0D
+#define NAU8824_REG_INTERRUPT_SETTING_1	0x0F
+#define NAU8824_REG_IRQ			0x10
+#define NAU8824_REG_CLEAR_INT_REG		0x11
+#define NAU8824_REG_INTERRUPT_SETTING	0x12
+#define NAU8824_REG_SAR_ADC			0x13
+#define NAU8824_REG_VDET_COEFFICIENT		0x14
+#define NAU8824_REG_VDET_THRESHOLD_1	0x15
+#define NAU8824_REG_VDET_THRESHOLD_2	0x16
+#define NAU8824_REG_VDET_THRESHOLD_3	0x17
+#define NAU8824_REG_VDET_THRESHOLD_4	0x18
+#define NAU8824_REG_GPIO_SEL			0x1A
+#define NAU8824_REG_PORT0_I2S_PCM_CTRL_1	0x1C
+#define NAU8824_REG_PORT0_I2S_PCM_CTRL_2	0x1D
+#define NAU8824_REG_PORT0_LEFT_TIME_SLOT	0x1E
+#define NAU8824_REG_PORT0_RIGHT_TIME_SLOT	0x1F
+#define NAU8824_REG_TDM_CTRL			0x20
+#define NAU8824_REG_ADC_HPF_FILTER		0x23
+#define NAU8824_REG_ADC_FILTER_CTRL		0x24
+#define NAU8824_REG_DAC_FILTER_CTRL_1	0x25
+#define NAU8824_REG_DAC_FILTER_CTRL_2	0x26
+#define NAU8824_REG_NOTCH_FILTER_1		0x27
+#define NAU8824_REG_NOTCH_FILTER_2		0x28
+#define NAU8824_REG_EQ1_LOW			0x29
+#define NAU8824_REG_EQ2_EQ3			0x2A
+#define NAU8824_REG_EQ4_EQ5			0x2B
+#define NAU8824_REG_ADC_CH0_DGAIN_CTRL	0x2D
+#define NAU8824_REG_ADC_CH1_DGAIN_CTRL	0x2E
+#define NAU8824_REG_ADC_CH2_DGAIN_CTRL	0x2F
+#define NAU8824_REG_ADC_CH3_DGAIN_CTRL	0x30
+#define NAU8824_REG_DAC_MUTE_CTRL		0x31
+#define NAU8824_REG_DAC_CH0_DGAIN_CTRL	0x32
+#define NAU8824_REG_DAC_CH1_DGAIN_CTRL	0x33
+#define NAU8824_REG_ADC_TO_DAC_ST		0x34
+#define NAU8824_REG_DRC_KNEE_IP12_ADC_CH01	0x38
+#define NAU8824_REG_DRC_KNEE_IP34_ADC_CH01	0x39
+#define NAU8824_REG_DRC_SLOPE_ADC_CH01	0x3A
+#define NAU8824_REG_DRC_ATKDCY_ADC_CH01	0x3B
+#define NAU8824_REG_DRC_KNEE_IP12_ADC_CH23	0x3C
+#define NAU8824_REG_DRC_KNEE_IP34_ADC_CH23	0x3D
+#define NAU8824_REG_DRC_SLOPE_ADC_CH23	0x3E
+#define NAU8824_REG_DRC_ATKDCY_ADC_CH23	0x3F
+#define NAU8824_REG_DRC_GAINL_ADC0		0x40
+#define NAU8824_REG_DRC_GAINL_ADC1		0x41
+#define NAU8824_REG_DRC_GAINL_ADC2		0x42
+#define NAU8824_REG_DRC_GAINL_ADC3		0x43
+#define NAU8824_REG_DRC_KNEE_IP12_DAC	0x45
+#define NAU8824_REG_DRC_KNEE_IP34_DAC	0x46
+#define NAU8824_REG_DRC_SLOPE_DAC		0x47
+#define NAU8824_REG_DRC_ATKDCY_DAC		0x48
+#define NAU8824_REG_DRC_GAIN_DAC_CH0	0x49
+#define NAU8824_REG_DRC_GAIN_DAC_CH1	0x4A
+#define NAU8824_REG_MODE			0x4C
+#define NAU8824_REG_MODE1			0x4D
+#define NAU8824_REG_MODE2			0x4E
+#define NAU8824_REG_CLASSG			0x50
+#define NAU8824_REG_OTP_EFUSE			0x51
+#define NAU8824_REG_OTPDOUT_1		0x53
+#define NAU8824_REG_OTPDOUT_2		0x54
+#define NAU8824_REG_MISC_CTRL			0x55
+#define NAU8824_REG_I2C_TIMEOUT		0x56
+#define NAU8824_REG_TEST_MODE		0x57
+#define NAU8824_REG_I2C_DEVICE_ID		0x58
+#define NAU8824_REG_SAR_ADC_DATA_OUT	0x59
+#define NAU8824_REG_BIAS_ADJ			0x66
+#define NAU8824_REG_PGA_GAIN			0x67
+#define NAU8824_REG_TRIM_SETTINGS		0x68
+#define NAU8824_REG_ANALOG_CONTROL_1	0x69
+#define NAU8824_REG_ANALOG_CONTROL_2	0x6A
+#define NAU8824_REG_ENABLE_LO			0x6B
+#define NAU8824_REG_GAIN_LO			0x6C
+#define NAU8824_REG_CLASSD_GAIN_1		0x6D
+#define NAU8824_REG_CLASSD_GAIN_2		0x6E
+#define NAU8824_REG_ANALOG_ADC_1		0x71
+#define NAU8824_REG_ANALOG_ADC_2		0x72
+#define NAU8824_REG_RDAC			0x73
+#define NAU8824_REG_MIC_BIAS			0x74
+#define NAU8824_REG_HS_VOLUME_CONTROL	0x75
+#define NAU8824_REG_BOOST			0x76
+#define NAU8824_REG_FEPGA			0x77
+#define NAU8824_REG_FEPGA_II			0x78
+#define NAU8824_REG_FEPGA_SE			0x79
+#define NAU8824_REG_FEPGA_ATTENUATION	0x7A
+#define NAU8824_REG_ATT_PORT0		0x7B
+#define NAU8824_REG_ATT_PORT1		0x7C
+#define NAU8824_REG_POWER_UP_CONTROL	0x7F
+#define NAU8824_REG_CHARGE_PUMP_CONTROL	0x80
+#define NAU8824_REG_CHARGE_PUMP_INPUT	0x81
+#define NAU8824_REG_MAX			NAU8824_REG_CHARGE_PUMP_INPUT
+/* 16-bit control register address, and 16-bits control register data */
+#define NAU8824_REG_ADDR_LEN		16
+#define NAU8824_REG_DATA_LEN		16
+
+
+/* ENA_CTRL (0x1) */
+#define NAU8824_DMIC_LCH_EDGE_CH23	(0x1 << 12)
+#define NAU8824_DMIC_LCH_EDGE_CH01	(0x1 << 11)
+#define NAU8824_JD_SLEEP_MODE		(0x1 << 10)
+#define NAU8824_ADC_CH3_DMIC_SFT	9
+#define NAU8824_ADC_CH3_DMIC_EN	(0x1 << NAU8824_ADC_CH3_DMIC_SFT)
+#define NAU8824_ADC_CH2_DMIC_SFT	8
+#define NAU8824_ADC_CH2_DMIC_EN	(0x1 << NAU8824_ADC_CH2_DMIC_SFT)
+#define NAU8824_ADC_CH1_DMIC_SFT	7
+#define NAU8824_ADC_CH1_DMIC_EN	(0x1 << NAU8824_ADC_CH1_DMIC_SFT)
+#define NAU8824_ADC_CH0_DMIC_SFT	6
+#define NAU8824_ADC_CH0_DMIC_EN	(0x1 << NAU8824_ADC_CH0_DMIC_SFT)
+#define NAU8824_DAC_CH1_EN		(0x1 << 5)
+#define NAU8824_DAC_CH0_EN		(0x1 << 4)
+#define NAU8824_ADC_CH3_EN		(0x1 << 3)
+#define NAU8824_ADC_CH2_EN		(0x1 << 2)
+#define NAU8824_ADC_CH1_EN		(0x1 << 1)
+#define NAU8824_ADC_CH0_EN		0x1
+
+/* CLK_GATING_ENA (0x02) */
+#define NAU8824_CLK_ADC_CH23_EN	(0x1 << 15)
+#define NAU8824_CLK_ADC_CH01_EN	(0x1 << 14)
+#define NAU8824_CLK_DAC_CH1_EN	(0x1 << 13)
+#define NAU8824_CLK_DAC_CH0_EN	(0x1 << 12)
+#define NAU8824_CLK_I2S_EN		(0x1 << 7)
+#define NAU8824_CLK_GAIN_EN		(0x1 << 5)
+#define NAU8824_CLK_SAR_EN		(0x1 << 3)
+#define NAU8824_CLK_DMIC_CH23_EN	(0x1 << 1)
+
+/* CLK_DIVIDER (0x3) */
+#define NAU8824_CLK_SRC_SFT		15
+#define NAU8824_CLK_SRC_MASK		(1 << NAU8824_CLK_SRC_SFT)
+#define NAU8824_CLK_SRC_VCO		(1 << NAU8824_CLK_SRC_SFT)
+#define NAU8824_CLK_SRC_MCLK		(0 << NAU8824_CLK_SRC_SFT)
+#define NAU8824_CLK_MCLK_SRC_MASK	(0xf << 0)
+#define NAU8824_CLK_DMIC_SRC_SFT	10
+#define NAU8824_CLK_DMIC_SRC_MASK	(0x7 << NAU8824_CLK_DMIC_SRC_SFT)
+#define NAU8824_CLK_ADC_SRC_SFT	6
+#define NAU8824_CLK_ADC_SRC_MASK	(0x3 << NAU8824_CLK_ADC_SRC_SFT)
+#define NAU8824_CLK_DAC_SRC_SFT	4
+#define NAU8824_CLK_DAC_SRC_MASK	(0x3 << NAU8824_CLK_DAC_SRC_SFT)
+
+/* FLL1 (0x04) */
+#define NAU8824_FLL_RATIO_MASK	(0x7f << 0)
+
+/* FLL3 (0x06) */
+#define NAU8824_FLL_INTEGER_MASK	(0x3ff << 0)
+#define NAU8824_FLL_CLK_SRC_SFT	10
+#define NAU8824_FLL_CLK_SRC_MASK	(0x3 << NAU8824_FLL_CLK_SRC_SFT)
+#define NAU8824_FLL_CLK_SRC_MCLK	(0 << NAU8824_FLL_CLK_SRC_SFT)
+#define NAU8824_FLL_CLK_SRC_BLK	(0x2 << NAU8824_FLL_CLK_SRC_SFT)
+#define NAU8824_FLL_CLK_SRC_FS		(0x3 << NAU8824_FLL_CLK_SRC_SFT)
+
+/* FLL4 (0x07) */
+#define NAU8824_FLL_REF_DIV_SFT	10
+#define NAU8824_FLL_REF_DIV_MASK	(0x3 << NAU8824_FLL_REF_DIV_SFT)
+
+/* FLL5 (0x08) */
+#define NAU8824_FLL_PDB_DAC_EN	(0x1 << 15)
+#define NAU8824_FLL_LOOP_FTR_EN	(0x1 << 14)
+#define NAU8824_FLL_CLK_SW_MASK	(0x1 << 13)
+#define NAU8824_FLL_CLK_SW_N2		(0x1 << 13)
+#define NAU8824_FLL_CLK_SW_REF	(0x0 << 13)
+#define NAU8824_FLL_FTR_SW_MASK	(0x1 << 12)
+#define NAU8824_FLL_FTR_SW_ACCU	(0x1 << 12)
+#define NAU8824_FLL_FTR_SW_FILTER	(0x0 << 12)
+
+/* FLL6 (0x9) */
+#define NAU8824_DCO_EN			(0x1 << 15)
+#define NAU8824_SDM_EN			(0x1 << 14)
+
+/* IRQ (0x10) */
+#define NAU8824_SHORT_CIRCUIT_IRQ		(0x1 << 7)
+#define NAU8824_IMPEDANCE_MEAS_IRQ		(0x1 << 6)
+#define NAU8824_KEY_RELEASE_IRQ		(0x1 << 5)
+#define NAU8824_KEY_LONG_PRESS_IRQ		(0x1 << 4)
+#define NAU8824_KEY_SHORT_PRESS_IRQ		(0x1 << 3)
+#define NAU8824_JACK_EJECTION_DETECTED	(0x1 << 1)
+#define NAU8824_JACK_INSERTION_DETECTED	0x1
+
+/* JACK_DET_CTRL (0x0D) */
+#define NAU8824_JACK_EJECT_DT_SFT	2
+#define NAU8824_JACK_EJECT_DT_MASK (0x3 << NAU8824_JACK_EJECT_DT_SFT)
+#define NAU8824_JACK_LOGIC		0x1
+
+
+/* INTERRUPT_SETTING_1 (0x0F) */
+#define NAU8824_IRQ_EJECT_EN		(0x1 << 9)
+#define NAU8824_IRQ_INSERT_EN		(0x1 << 8)
+
+/* INTERRUPT_SETTING (0x12) */
+#define NAU8824_IRQ_KEY_RELEASE_DIS		(0x1 << 5)
+#define NAU8824_IRQ_KEY_SHORT_PRESS_DIS	(0x1 << 3)
+#define NAU8824_IRQ_EJECT_DIS			(0x1 << 1)
+#define NAU8824_IRQ_INSERT_DIS		0x1
+
+/* SAR_ADC (0x13) */
+#define NAU8824_SAR_ADC_EN_SFT		12
+#define NAU8824_SAR_TRACKING_GAIN_SFT	8
+#define NAU8824_SAR_TRACKING_GAIN_MASK	(0x7 << NAU8824_SAR_TRACKING_GAIN_SFT)
+#define NAU8824_SAR_COMPARE_TIME_SFT	2
+#define NAU8824_SAR_COMPARE_TIME_MASK	(3 << 2)
+#define NAU8824_SAR_SAMPLING_TIME_SFT	0
+#define NAU8824_SAR_SAMPLING_TIME_MASK	(3 << 0)
+
+/* VDET_COEFFICIENT (0x14) */
+#define NAU8824_SHORTKEY_DEBOUNCE_SFT	12
+#define NAU8824_SHORTKEY_DEBOUNCE_MASK	(0x3 << NAU8824_SHORTKEY_DEBOUNCE_SFT)
+#define NAU8824_LEVELS_NR_SFT			8
+#define NAU8824_LEVELS_NR_MASK		(0x7 << 8)
+#define NAU8824_HYSTERESIS_SFT		0
+#define NAU8824_HYSTERESIS_MASK		0xf
+
+/* PORT0_I2S_PCM_CTRL_1 (0x1C) */
+#define NAU8824_I2S_BP_SFT		7
+#define NAU8824_I2S_BP_MASK		(1 << NAU8824_I2S_BP_SFT)
+#define NAU8824_I2S_BP_INV		(1 << NAU8824_I2S_BP_SFT)
+#define NAU8824_I2S_PCMB_SFT		6
+#define NAU8824_I2S_PCMB_EN		(1 << NAU8824_I2S_PCMB_SFT)
+#define NAU8824_I2S_DL_SFT		2
+#define NAU8824_I2S_DL_MASK		(0x3 << NAU8824_I2S_DL_SFT)
+#define NAU8824_I2S_DL_16		(0 << NAU8824_I2S_DL_SFT)
+#define NAU8824_I2S_DL_20		(1 << NAU8824_I2S_DL_SFT)
+#define NAU8824_I2S_DL_24		(2 << NAU8824_I2S_DL_SFT)
+#define NAU8824_I2S_DL_32		(3 << NAU8824_I2S_DL_SFT)
+#define NAU8824_I2S_DF_MASK		0x3
+#define NAU8824_I2S_DF_RIGTH		0
+#define NAU8824_I2S_DF_LEFT		1
+#define NAU8824_I2S_DF_I2S		2
+#define NAU8824_I2S_DF_PCM_AB		3
+
+
+/* PORT0_I2S_PCM_CTRL_2 (0x1D) */
+#define NAU8824_I2S_LRC_DIV_SFT	12
+#define NAU8824_I2S_LRC_DIV_MASK	(0x3 << NAU8824_I2S_LRC_DIV_SFT)
+#define NAU8824_I2S_MS_SFT		3
+#define NAU8824_I2S_MS_MASK		(1 << NAU8824_I2S_MS_SFT)
+#define NAU8824_I2S_MS_MASTER		(1 << NAU8824_I2S_MS_SFT)
+#define NAU8824_I2S_MS_SLAVE		(0 << NAU8824_I2S_MS_SFT)
+#define NAU8824_I2S_BLK_DIV_MASK	0x7
+
+/* ADC_FILTER_CTRL (0x24) */
+#define NAU8824_ADC_SYNC_DOWN_MASK	0x3
+#define NAU8824_ADC_SYNC_DOWN_32	0
+#define NAU8824_ADC_SYNC_DOWN_64	1
+#define NAU8824_ADC_SYNC_DOWN_128	2
+#define NAU8824_ADC_SYNC_DOWN_256	3
+
+/* DAC_FILTER_CTRL_1 (0x25) */
+#define NAU8824_DAC_CICCLP_OFF	(0x1 << 7)
+#define NAU8824_DAC_OVERSAMPLE_MASK	0x7
+#define NAU8824_DAC_OVERSAMPLE_64	0
+#define NAU8824_DAC_OVERSAMPLE_256	1
+#define NAU8824_DAC_OVERSAMPLE_128	2
+#define NAU8824_DAC_OVERSAMPLE_32	4
+
+/* DAC_MUTE_CTRL (0x31) */
+#define NAU8824_DAC_CH01_MIX		0x3
+#define NAU8824_DAC_ZC_EN		(0x1 << 11)
+
+/* DAC_CH0_DGAIN_CTRL (0x32) */
+#define NAU8824_DAC_CH0_SEL_SFT	9
+#define NAU8824_DAC_CH0_SEL_MASK	(0x1 << NAU8824_DAC_CH0_SEL_SFT)
+#define NAU8824_DAC_CH0_SEL_I2S0	(0x0 << NAU8824_DAC_CH0_SEL_SFT)
+#define NAU8824_DAC_CH0_SEL_I2S1	(0x1 << NAU8824_DAC_CH0_SEL_SFT)
+#define NAU8824_DAC_CH0_VOL_MASK	0x1ff
+
+/* DAC_CH1_DGAIN_CTRL (0x33) */
+#define NAU8824_DAC_CH1_SEL_SFT	9
+#define NAU8824_DAC_CH1_SEL_MASK	(0x1 << NAU8824_DAC_CH1_SEL_SFT)
+#define NAU8824_DAC_CH1_SEL_I2S0	(0x0 << NAU8824_DAC_CH1_SEL_SFT)
+#define NAU8824_DAC_CH1_SEL_I2S1	(0x1 << NAU8824_DAC_CH1_SEL_SFT)
+#define NAU8824_DAC_CH1_VOL_MASK	0x1ff
+
+/* CLASSG (0x50) */
+#define NAU8824_CLASSG_TIMER_SFT	8
+#define NAU8824_CLASSG_TIMER_MASK	(0x3f << NAU8824_CLASSG_TIMER_SFT)
+#define NAU8824_CLASSG_LDAC_EN_SFT	2
+#define NAU8824_CLASSG_RDAC_EN_SFT	1
+#define NAU8824_CLASSG_EN_SFT		0
+
+/* SAR_ADC_DATA_OUT (0x59) */
+#define NAU8824_SAR_ADC_DATA_MASK	0xff
+
+/* BIAS_ADJ (0x66) */
+#define NAU8824_VMID			(1 << 6)
+#define NAU8824_VMID_SEL_SFT		4
+#define NAU8824_VMID_SEL_MASK		(3 << NAU8824_VMID_SEL_SFT)
+#define NAU8824_DMIC2_EN_SFT		3
+#define NAU8824_DMIC1_EN_SFT		2
+
+/* TRIM_SETTINGS (0x68) */
+#define NAU8824_DRV_CURR_INC		(1 << 15)
+
+/* ANALOG_CONTROL_1 (0x69) */
+#define NAU8824_DMIC_CLK_DRV_STRG	(1 << 3)
+#define NAU8824_DMIC_CLK_SLEW_FAST	(0x7)
+
+/* ANALOG_CONTROL_2 (0x6A) */
+#define NAU8824_CLASSD_CLAMP_DIS_SFT	3
+#define NAU8824_CLASSD_CLAMP_DIS	(0x1 << NAU8824_CLASSD_CLAMP_DIS_SFT)
+
+/* ENABLE_LO (0x6B) */
+#define NAU8824_TEST_DAC_SFT		14
+#define NAU8824_TEST_DAC_EN		(0x3 << NAU8824_TEST_DAC_SFT)
+#define NAU8824_DACL_HPR_EN_SFT	3
+#define NAU8824_DACL_HPR_EN		(0x1 << NAU8824_DACL_HPR_EN_SFT)
+#define NAU8824_DACR_HPR_EN_SFT	2
+#define NAU8824_DACR_HPR_EN		(0x1 << NAU8824_DACR_HPR_EN_SFT)
+#define NAU8824_DACR_HPL_EN_SFT	1
+#define NAU8824_DACR_HPL_EN		(0x1 << NAU8824_DACR_HPL_EN_SFT)
+#define NAU8824_DACL_HPL_EN_SFT	0
+#define NAU8824_DACL_HPL_EN		0x1
+
+/* CLASSD_GAIN_1 (0x6D) */
+#define NAU8824_CLASSD_GAIN_1R_SFT	8
+#define NAU8824_CLASSD_GAIN_1R_MASK	(0x1f << NAU8824_CLASSD_GAIN_1R_SFT)
+#define NAU8824_CLASSD_EN_SFT		7
+#define NAU8824_CLASSD_EN		(0x1 << NAU8824_CLASSD_EN_SFT)
+#define NAU8824_CLASSD_GAIN_1L_MASK	0x1f
+
+/* CLASSD_GAIN_2 (0x6E) */
+#define NAU8824_CLASSD_GAIN_2R_SFT	8
+#define NAU8824_CLASSD_GAIN_2R_MASK	(0x1f << NAU8824_CLASSD_GAIN_1R_SFT)
+#define NAU8824_CLASSD_EN_SFT		7
+#define NAU8824_CLASSD_EN		(0x1 << NAU8824_CLASSD_EN_SFT)
+#define NAU8824_CLASSD_GAIN_2L_MASK	0x1f
+
+/* ANALOG_ADC_2 (0x72) */
+#define NAU8824_ADCR_EN_SFT		7
+#define NAU8824_ADCL_EN_SFT		6
+
+/* RDAC (0x73) */
+#define NAU8824_DACR_EN_SFT		13
+#define NAU8824_DACL_EN_SFT		12
+#define NAU8824_DACR_CLK_SFT		9
+#define NAU8824_DACL_CLK_SFT		8
+#define NAU8824_RDAC_CLK_DELAY_SFT	4
+#define NAU8824_RDAC_CLK_DELAY_MASK	(0x7 << NAU8824_RDAC_CLK_DELAY_SFT)
+#define NAU8824_RDAC_VREF_SFT		2
+#define NAU8824_RDAC_VREF_MASK	(0x3 << NAU8824_RDAC_VREF_SFT)
+
+/* MIC_BIAS (0x74) */
+#define NAU8824_MICBIAS_JKSLV		(1 << 14)
+#define NAU8824_MICBIAS_JKR2		(1 << 12)
+#define NAU8824_MICBIAS_POWERUP_SFT	8
+#define NAU8824_MICBIAS_VOLTAGE_SFT	0
+#define NAU8824_MICBIAS_VOLTAGE_MASK	0x7
+
+/* BOOST (0x76) */
+#define NAU8824_PRECHARGE_DIS			(0x1 << 13)
+#define NAU8824_GLOBAL_BIAS_EN		(0x1 << 12)
+#define NAU8824_HP_BOOST_DIS_SFT		9
+#define NAU8824_HP_BOOST_DIS		(0x1 << NAU8824_HP_BOOST_DIS_SFT)
+#define NAU8824_HP_BOOST_G_DIS_SFT		8
+#define NAU8824_HP_BOOST_G_DIS		(0x1 << NAU8824_HP_BOOST_G_DIS_SFT)
+#define NAU8824_SHORT_SHUTDOWN_DIG_EN	(1 << 7)
+#define NAU8824_SHORT_SHUTDOWN_EN		(1 << 6)
+
+/* FEPGA (0x77) */
+#define NAU8824_FEPGA_MODER_SHORT_SFT	7
+#define NAU8824_FEPGA_MODER_SHORT_EN	(0x1 << NAU8824_FEPGA_MODER_SHORT_SFT)
+#define NAU8824_FEPGA_MODER_MIC2_SFT		5
+#define NAU8824_FEPGA_MODER_MIC2_EN	(0x1 << NAU8824_FEPGA_MODER_MIC2_SFT)
+#define NAU8824_FEPGA_MODER_HSMIC_SFT	4
+#define NAU8824_FEPGA_MODER_HSMIC_EN	(0x1 << NAU8824_FEPGA_MODER_HSMIC_SFT)
+#define NAU8824_FEPGA_MODEL_SHORT_SFT	3
+#define NAU8824_FEPGA_MODEL_SHORT_EN	(0x1 << NAU8824_FEPGA_MODEL_SHORT_SFT)
+#define NAU8824_FEPGA_MODEL_MIC1_SFT		1
+#define NAU8824_FEPGA_MODEL_MIC1_EN	(0x1 << NAU8824_FEPGA_MODEL_MIC1_SFT)
+#define NAU8824_FEPGA_MODEL_HSMIC_SFT	0
+#define NAU8824_FEPGA_MODEL_HSMIC_EN	(0x1 << NAU8824_FEPGA_MODEL_HSMIC_SFT)
+
+/* FEPGA_II (0x78) */
+#define NAU8824_FEPGA_GAINR_SFT	5
+#define NAU8824_FEPGA_GAINR_MASK	(0x1f << NAU8824_FEPGA_GAINR_SFT)
+#define NAU8824_FEPGA_GAINL_SFT	0
+#define NAU8824_FEPGA_GAINL_MASK	0x1f
+
+/* CHARGE_PUMP_CONTROL (0x80) */
+#define NAU8824_JAMNODCLOW		(0x1 << 15)
+#define NAU8824_SPKR_PULL_DOWN	(0x1 << 13)
+#define NAU8824_SPKL_PULL_DOWN	(0x1 << 12)
+#define NAU8824_POWER_DOWN_DACR	(0x1 << 9)
+#define NAU8824_POWER_DOWN_DACL	(0x1 << 8)
+#define NAU8824_CHARGE_PUMP_EN_SFT	5
+#define NAU8824_CHARGE_PUMP_EN	(0x1 << NAU8824_CHARGE_PUMP_EN_SFT)
+
+
+#define NAU8824_CODEC_DAI "nau8824-hifi"
+
+/* System Clock Source */
+enum {
+	NAU8824_CLK_DIS,
+	NAU8824_CLK_MCLK,
+	NAU8824_CLK_INTERNAL,
+	NAU8824_CLK_FLL_MCLK,
+	NAU8824_CLK_FLL_BLK,
+	NAU8824_CLK_FLL_FS,
+};
+
+struct nau8824 {
+	struct device *dev;
+	struct regmap *regmap;
+	struct snd_soc_dapm_context *dapm;
+	struct snd_soc_jack *jack;
+	struct work_struct jdet_work;
+	struct semaphore jd_sem;
+	int fs;
+	int irq;
+	int micbias_voltage;
+	int vref_impedance;
+	int jkdet_polarity;
+	int sar_threshold_num;
+	int sar_threshold[8];
+	int sar_hysteresis;
+	int sar_voltage;
+	int sar_compare_time;
+	int sar_sampling_time;
+	int key_debounce;
+	int jack_eject_debounce;
+};
+
+struct nau8824_fll {
+	int mclk_src;
+	int ratio;
+	int fll_frac;
+	int fll_int;
+	int clk_ref_div;
+};
+
+struct nau8824_fll_attr {
+	unsigned int param;
+	unsigned int val;
+};
+
+struct nau8824_osr_attr {
+	unsigned int osr;
+	unsigned int clk_src;
+};
+
+
+int nau8824_enable_jack_detect(struct snd_soc_codec *codec,
+	struct snd_soc_jack *jack);
+
+#endif				/* _NAU8824_H */
+
diff --git a/sound/soc/codecs/rt5514.c b/sound/soc/codecs/rt5514.c
index b281a46d769d..f91221b1ddf0 100644
--- a/sound/soc/codecs/rt5514.c
+++ b/sound/soc/codecs/rt5514.c
@@ -1084,13 +1084,28 @@ static int rt5514_parse_dt(struct rt5514_priv *rt5514, struct device *dev)
 	return 0;
 }
 
+static __maybe_unused int rt5514_i2c_resume(struct device *dev)
+{
+	struct rt5514_priv *rt5514 = dev_get_drvdata(dev);
+	unsigned int val;
+
+	/*
+	 * Add a bogus read to avoid rt5514's confusion after s2r in case it
+	 * saw glitches on the i2c lines and thought the other side sent a
+	 * start bit.
+	 */
+	regmap_read(rt5514->regmap, RT5514_VENDOR_ID2, &val);
+
+	return 0;
+}
+
 static int rt5514_i2c_probe(struct i2c_client *i2c,
 		    const struct i2c_device_id *id)
 {
 	struct rt5514_platform_data *pdata = dev_get_platdata(&i2c->dev);
 	struct rt5514_priv *rt5514;
 	int ret;
-	unsigned int val;
+	unsigned int val = ~0;
 
 	rt5514 = devm_kzalloc(&i2c->dev, sizeof(struct rt5514_priv),
 				GFP_KERNEL);
@@ -1120,8 +1135,16 @@ static int rt5514_i2c_probe(struct i2c_client *i2c,
 		return ret;
 	}
 
-	regmap_read(rt5514->regmap, RT5514_VENDOR_ID2, &val);
-	if (val != RT5514_DEVICE_ID) {
+	/*
+	 * The rt5514 can get confused if the i2c lines glitch together, as
+	 * can happen at bootup as regulators are turned off and on.  If it's
+	 * in this glitched state the first i2c read will fail, so we'll give
+	 * it one change to retry.
+	 */
+	ret = regmap_read(rt5514->regmap, RT5514_VENDOR_ID2, &val);
+	if (ret || val != RT5514_DEVICE_ID)
+		ret = regmap_read(rt5514->regmap, RT5514_VENDOR_ID2, &val);
+	if (ret || val != RT5514_DEVICE_ID) {
 		dev_err(&i2c->dev,
 			"Device with ID register %x is not rt5514\n", val);
 		return -ENODEV;
@@ -1149,10 +1172,15 @@ static int rt5514_i2c_remove(struct i2c_client *i2c)
 	return 0;
 }
 
-struct i2c_driver rt5514_i2c_driver = {
+static const struct dev_pm_ops rt5514_i2_pm_ops = {
+	SET_SYSTEM_SLEEP_PM_OPS(NULL, rt5514_i2c_resume)
+};
+
+static struct i2c_driver rt5514_i2c_driver = {
 	.driver = {
 		.name = "rt5514",
 		.of_match_table = of_match_ptr(rt5514_of_match),
+		.pm = &rt5514_i2_pm_ops,
 	},
 	.probe = rt5514_i2c_probe,
 	.remove   = rt5514_i2c_remove,
diff --git a/sound/soc/codecs/rt5645.c b/sound/soc/codecs/rt5645.c
index e149f3ce5401..87844a45886a 100644
--- a/sound/soc/codecs/rt5645.c
+++ b/sound/soc/codecs/rt5645.c
@@ -3542,6 +3542,15 @@ static const struct i2c_device_id rt5645_i2c_id[] = {
 };
 MODULE_DEVICE_TABLE(i2c, rt5645_i2c_id);
 
+#ifdef CONFIG_OF
+static const struct of_device_id rt5645_of_match[] = {
+	{ .compatible = "realtek,rt5645", },
+	{ .compatible = "realtek,rt5650", },
+	{ }
+};
+MODULE_DEVICE_TABLE(of, rt5645_of_match);
+#endif
+
 #ifdef CONFIG_ACPI
 static const struct acpi_device_id rt5645_acpi_match[] = {
 	{ "10EC5645", 0 },
@@ -3912,6 +3921,7 @@ static void rt5645_i2c_shutdown(struct i2c_client *i2c)
 static struct i2c_driver rt5645_i2c_driver = {
 	.driver = {
 		.name = "rt5645",
+		.of_match_table = of_match_ptr(rt5645_of_match),
 		.acpi_match_table = ACPI_PTR(rt5645_acpi_match),
 	},
 	.probe = rt5645_i2c_probe,
diff --git a/sound/soc/codecs/rt5665.c b/sound/soc/codecs/rt5665.c
index 476135ec5726..8cd22307f5b6 100644
--- a/sound/soc/codecs/rt5665.c
+++ b/sound/soc/codecs/rt5665.c
@@ -1139,7 +1139,8 @@ static void rt5665_enable_push_button_irq(struct snd_soc_codec *codec,
 	bool enable)
 {
 	if (enable) {
-		snd_soc_write(codec, RT5665_4BTN_IL_CMD_1, 0x000b);
+		snd_soc_write(codec, RT5665_4BTN_IL_CMD_1, 0x0003);
+		snd_soc_update_bits(codec, RT5665_SAR_IL_CMD_9, 0x1, 0x1);
 		snd_soc_write(codec, RT5665_IL_CMD_1, 0x0048);
 		snd_soc_update_bits(codec, RT5665_4BTN_IL_CMD_2,
 				RT5665_4BTN_IL_MASK | RT5665_4BTN_IL_RST_MASK,
@@ -1192,10 +1193,13 @@ static int rt5665_headset_detect(struct snd_soc_codec *codec, int jack_insert)
 		}
 
 		regmap_update_bits(rt5665->regmap, RT5665_EJD_CTRL_1,
-			0x180, 0x180);
+			0x1a0, 0x120);
 		regmap_write(rt5665->regmap, RT5665_EJD_CTRL_3, 0x3424);
+		regmap_write(rt5665->regmap, RT5665_IL_CMD_1, 0x0048);
 		regmap_write(rt5665->regmap, RT5665_SAR_IL_CMD_1, 0xa291);
 
+		usleep_range(10000, 15000);
+
 		rt5665->sar_adc_value = snd_soc_read(rt5665->codec,
 			RT5665_SAR_IL_CMD_4) & 0x7ff;
 
@@ -1256,8 +1260,8 @@ static void rt5665_jd_check_handler(struct work_struct *work)
 	}
 }
 
-int rt5665_set_jack_detect(struct snd_soc_codec *codec,
-	struct snd_soc_jack *hs_jack)
+static int rt5665_set_jack_detect(struct snd_soc_codec *codec,
+	struct snd_soc_jack *hs_jack, void *data)
 {
 	struct rt5665_priv *rt5665 = snd_soc_codec_get_drvdata(codec);
 
@@ -1284,7 +1288,6 @@ int rt5665_set_jack_detect(struct snd_soc_codec *codec,
 
 	return 0;
 }
-EXPORT_SYMBOL_GPL(rt5665_set_jack_detect);
 
 static void rt5665_jack_detect_handler(struct work_struct *work)
 {
@@ -2600,6 +2603,55 @@ static int rt5655_set_verf(struct snd_soc_dapm_widget *w,
 	return 0;
 }
 
+static int rt5665_i2s_pin_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);
+	unsigned int val1, val2, mask1, mask2 = 0;
+
+	switch (w->shift) {
+	case RT5665_PWR_I2S2_1_BIT:
+		mask1 = RT5665_GP2_PIN_MASK | RT5665_GP3_PIN_MASK |
+			RT5665_GP4_PIN_MASK | RT5665_GP5_PIN_MASK;
+		val1 = RT5665_GP2_PIN_BCLK2 | RT5665_GP3_PIN_LRCK2 |
+			RT5665_GP4_PIN_DACDAT2_1 | RT5665_GP5_PIN_ADCDAT2_1;
+		break;
+	case RT5665_PWR_I2S2_2_BIT:
+		mask1 = RT5665_GP2_PIN_MASK | RT5665_GP3_PIN_MASK |
+			RT5665_GP8_PIN_MASK;
+		val1 = RT5665_GP2_PIN_BCLK2 | RT5665_GP3_PIN_LRCK2 |
+			RT5665_GP8_PIN_DACDAT2_2;
+		mask2 = RT5665_GP9_PIN_MASK;
+		val2 = RT5665_GP9_PIN_ADCDAT2_2;
+		break;
+	case RT5665_PWR_I2S3_BIT:
+		mask1 = RT5665_GP6_PIN_MASK | RT5665_GP7_PIN_MASK |
+			RT5665_GP8_PIN_MASK;
+		val1 = RT5665_GP6_PIN_BCLK3 | RT5665_GP7_PIN_LRCK3 |
+			RT5665_GP8_PIN_DACDAT3;
+		mask2 = RT5665_GP9_PIN_MASK;
+		val2 = RT5665_GP9_PIN_ADCDAT3;
+		break;
+	}
+	switch (event) {
+	case SND_SOC_DAPM_PRE_PMU:
+		snd_soc_update_bits(codec, RT5665_GPIO_CTRL_1, mask1, val1);
+		if (mask2)
+			snd_soc_update_bits(codec, RT5665_GPIO_CTRL_2,
+					    mask2, val2);
+		break;
+	case SND_SOC_DAPM_POST_PMD:
+		snd_soc_update_bits(codec, RT5665_GPIO_CTRL_1, mask1, 0);
+		if (mask2)
+			snd_soc_update_bits(codec, RT5665_GPIO_CTRL_2,
+					    mask2, 0);
+		break;
+	default:
+		return 0;
+	}
+
+	return 0;
+}
 
 static const struct snd_soc_dapm_widget rt5665_dapm_widgets[] = {
 	SND_SOC_DAPM_SUPPLY("LDO2", RT5665_PWR_ANLG_3, RT5665_PWR_LDO2_BIT, 0,
@@ -2852,11 +2904,14 @@ static const struct snd_soc_dapm_widget rt5665_dapm_widgets[] = {
 	SND_SOC_DAPM_SUPPLY("I2S1_2", RT5665_PWR_DIG_1, RT5665_PWR_I2S1_2_BIT,
 		0, NULL, 0),
 	SND_SOC_DAPM_SUPPLY("I2S2_1", RT5665_PWR_DIG_1, RT5665_PWR_I2S2_1_BIT,
-		0, NULL, 0),
+		0, rt5665_i2s_pin_event, SND_SOC_DAPM_PRE_PMU |
+		SND_SOC_DAPM_POST_PMD),
 	SND_SOC_DAPM_SUPPLY("I2S2_2", RT5665_PWR_DIG_1, RT5665_PWR_I2S2_2_BIT,
-		0, NULL, 0),
+		0, rt5665_i2s_pin_event, SND_SOC_DAPM_PRE_PMU |
+		SND_SOC_DAPM_POST_PMD),
 	SND_SOC_DAPM_SUPPLY("I2S3", RT5665_PWR_DIG_1, RT5665_PWR_I2S3_BIT,
-		0, NULL, 0),
+		0, rt5665_i2s_pin_event, SND_SOC_DAPM_PRE_PMU |
+		SND_SOC_DAPM_POST_PMD),
 	SND_SOC_DAPM_PGA("IF1 DAC1", SND_SOC_NOPM, 0, 0, NULL, 0),
 	SND_SOC_DAPM_PGA("IF1 DAC2", SND_SOC_NOPM, 0, 0, NULL, 0),
 	SND_SOC_DAPM_PGA("IF1 DAC3", SND_SOC_NOPM, 0, 0, NULL, 0),
@@ -3963,12 +4018,68 @@ static const struct snd_soc_dapm_route rt5665_dapm_routes[] = {
 	{"PDMR", NULL, "PDM R Playback"},
 };
 
+static int rt5665_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask,
+			unsigned int rx_mask, int slots, int slot_width)
+{
+	struct snd_soc_codec *codec = dai->codec;
+	unsigned int val = 0;
+
+	if (rx_mask || tx_mask)
+		val |= RT5665_I2S1_MODE_TDM;
+
+	switch (slots) {
+	case 4:
+		val |= RT5665_TDM_IN_CH_4;
+		val |= RT5665_TDM_OUT_CH_4;
+		break;
+	case 6:
+		val |= RT5665_TDM_IN_CH_6;
+		val |= RT5665_TDM_OUT_CH_6;
+		break;
+	case 8:
+		val |= RT5665_TDM_IN_CH_8;
+		val |= RT5665_TDM_OUT_CH_8;
+		break;
+	case 2:
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	switch (slot_width) {
+	case 20:
+		val |= RT5665_TDM_IN_LEN_20;
+		val |= RT5665_TDM_OUT_LEN_20;
+		break;
+	case 24:
+		val |= RT5665_TDM_IN_LEN_24;
+		val |= RT5665_TDM_OUT_LEN_24;
+		break;
+	case 32:
+		val |= RT5665_TDM_IN_LEN_32;
+		val |= RT5665_TDM_OUT_LEN_32;
+		break;
+	case 16:
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	snd_soc_update_bits(codec, RT5665_TDM_CTRL_1,
+		RT5665_I2S1_MODE_MASK | RT5665_TDM_IN_CH_MASK |
+		RT5665_TDM_OUT_CH_MASK | RT5665_TDM_IN_LEN_MASK |
+		RT5665_TDM_OUT_LEN_MASK, val);
+
+	return 0;
+}
+
+
 static int rt5665_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 rt5665_priv *rt5665 = snd_soc_codec_get_drvdata(codec);
-	unsigned int val_len = 0, val_clk, mask_clk, val_bits = 0x0100;
+	unsigned int val_len = 0, val_clk, reg_clk, mask_clk, val_bits = 0x0100;
 	int pre_div, frame_size;
 
 	rt5665->lrck[dai->id] = params_rate(params);
@@ -4009,6 +4120,10 @@ static int rt5665_hw_params(struct snd_pcm_substream *substream,
 	switch (dai->id) {
 	case RT5665_AIF1_1:
 	case RT5665_AIF1_2:
+		if (params_channels(params) > 2)
+			rt5665_set_tdm_slot(dai, 0xf, 0xf,
+				params_channels(params), params_width(params));
+		reg_clk = RT5665_ADDA_CLK_1;
 		mask_clk = RT5665_I2S_PD1_MASK;
 		val_clk = pre_div << RT5665_I2S_PD1_SFT;
 		snd_soc_update_bits(codec, RT5665_I2S1_SDP,
@@ -4016,12 +4131,14 @@ static int rt5665_hw_params(struct snd_pcm_substream *substream,
 		break;
 	case RT5665_AIF2_1:
 	case RT5665_AIF2_2:
+		reg_clk = RT5665_ADDA_CLK_2;
 		mask_clk = RT5665_I2S_PD2_MASK;
 		val_clk = pre_div << RT5665_I2S_PD2_SFT;
 		snd_soc_update_bits(codec, RT5665_I2S2_SDP,
 			RT5665_I2S_DL_MASK, val_len);
 		break;
 	case RT5665_AIF3:
+		reg_clk = RT5665_ADDA_CLK_2;
 		mask_clk = RT5665_I2S_PD3_MASK;
 		val_clk = pre_div << RT5665_I2S_PD3_SFT;
 		snd_soc_update_bits(codec, RT5665_I2S3_SDP,
@@ -4032,7 +4149,7 @@ static int rt5665_hw_params(struct snd_pcm_substream *substream,
 		return -EINVAL;
 	}
 
-	snd_soc_update_bits(codec, RT5665_ADDA_CLK_1, mask_clk, val_clk);
+	snd_soc_update_bits(codec, reg_clk, mask_clk, val_clk);
 	snd_soc_update_bits(codec, RT5665_STO1_DAC_SIL_DET, 0x3700, val_bits);
 
 	switch (rt5665->lrck[dai->id]) {
@@ -4125,10 +4242,9 @@ static int rt5665_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
 	return 0;
 }
 
-static int rt5665_set_dai_sysclk(struct snd_soc_dai *dai,
-		int clk_id, unsigned int freq, int dir)
+static int rt5665_set_codec_sysclk(struct snd_soc_codec *codec, int clk_id,
+				   int source, unsigned int freq, int dir)
 {
-	struct snd_soc_codec *codec = dai->codec;
 	struct rt5665_priv *rt5665 = snd_soc_codec_get_drvdata(codec);
 	unsigned int reg_val = 0;
 
@@ -4154,20 +4270,20 @@ static int rt5665_set_dai_sysclk(struct snd_soc_dai *dai,
 	rt5665->sysclk = freq;
 	rt5665->sysclk_src = clk_id;
 
-	dev_dbg(dai->dev, "Sysclk is %dHz and clock id is %d\n", freq, clk_id);
+	dev_dbg(codec->dev, "Sysclk is %dHz and clock id is %d\n", freq, clk_id);
 
 	return 0;
 }
 
-static int rt5665_set_dai_pll(struct snd_soc_dai *dai, int pll_id, int Source,
-			unsigned int freq_in, unsigned int freq_out)
+static int rt5665_set_codec_pll(struct snd_soc_codec *codec, int pll_id,
+				int source, unsigned int freq_in,
+				unsigned int freq_out)
 {
-	struct snd_soc_codec *codec = dai->codec;
 	struct rt5665_priv *rt5665 = snd_soc_codec_get_drvdata(codec);
 	struct rl6231_pll_code pll_code;
 	int ret;
 
-	if (Source == rt5665->pll_src && freq_in == rt5665->pll_in &&
+	if (source == rt5665->pll_src && freq_in == rt5665->pll_in &&
 	    freq_out == rt5665->pll_out)
 		return 0;
 
@@ -4181,7 +4297,7 @@ static int rt5665_set_dai_pll(struct snd_soc_dai *dai, int pll_id, int Source,
 		return 0;
 	}
 
-	switch (Source) {
+	switch (source) {
 	case RT5665_PLL1_S_MCLK:
 		snd_soc_update_bits(codec, RT5665_GLB_CLK,
 			RT5665_PLL1_SRC_MASK, RT5665_PLL1_SRC_MCLK);
@@ -4199,7 +4315,7 @@ static int rt5665_set_dai_pll(struct snd_soc_dai *dai, int pll_id, int Source,
 				RT5665_PLL1_SRC_MASK, RT5665_PLL1_SRC_BCLK3);
 		break;
 	default:
-		dev_err(codec->dev, "Unknown PLL Source %d\n", Source);
+		dev_err(codec->dev, "Unknown PLL Source %d\n", source);
 		return -EINVAL;
 	}
 
@@ -4221,62 +4337,7 @@ static int rt5665_set_dai_pll(struct snd_soc_dai *dai, int pll_id, int Source,
 
 	rt5665->pll_in = freq_in;
 	rt5665->pll_out = freq_out;
-	rt5665->pll_src = Source;
-
-	return 0;
-}
-
-static int rt5665_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask,
-			unsigned int rx_mask, int slots, int slot_width)
-{
-	struct snd_soc_codec *codec = dai->codec;
-	unsigned int val = 0;
-
-	if (rx_mask || tx_mask)
-		val |= RT5665_I2S1_MODE_TDM;
-
-	switch (slots) {
-	case 4:
-		val |= RT5665_TDM_IN_CH_4;
-		val |= RT5665_TDM_OUT_CH_4;
-		break;
-	case 6:
-		val |= RT5665_TDM_IN_CH_6;
-		val |= RT5665_TDM_OUT_CH_6;
-		break;
-	case 8:
-		val |= RT5665_TDM_IN_CH_8;
-		val |= RT5665_TDM_OUT_CH_8;
-		break;
-	case 2:
-		break;
-	default:
-		return -EINVAL;
-	}
-
-	switch (slot_width) {
-	case 20:
-		val |= RT5665_TDM_IN_LEN_20;
-		val |= RT5665_TDM_OUT_LEN_20;
-		break;
-	case 24:
-		val |= RT5665_TDM_IN_LEN_24;
-		val |= RT5665_TDM_OUT_LEN_24;
-		break;
-	case 32:
-		val |= RT5665_TDM_IN_LEN_32;
-		val |= RT5665_TDM_OUT_LEN_32;
-		break;
-	case 16:
-		break;
-	default:
-		return -EINVAL;
-	}
-
-	snd_soc_update_bits(codec, RT5665_TDM_CTRL_1,
-		RT5665_I2S1_MODE_MASK | RT5665_TDM_IN_CH_MASK |
-		RT5665_TDM_OUT_CH_MASK | RT5665_TDM_IN_LEN_MASK |
-		RT5665_TDM_OUT_LEN_MASK, val);
+	rt5665->pll_src = source;
 
 	return 0;
 }
@@ -4393,9 +4454,7 @@ static int rt5665_resume(struct snd_soc_codec *codec)
 static const struct snd_soc_dai_ops rt5665_aif_dai_ops = {
 	.hw_params = rt5665_hw_params,
 	.set_fmt = rt5665_set_dai_fmt,
-	.set_sysclk = rt5665_set_dai_sysclk,
 	.set_tdm_slot = rt5665_set_tdm_slot,
-	.set_pll = rt5665_set_dai_pll,
 	.set_bclk_ratio = rt5665_set_bclk_ratio,
 };
 
@@ -4504,7 +4563,10 @@ static struct snd_soc_codec_driver soc_codec_dev_rt5665 = {
 		.num_dapm_widgets = ARRAY_SIZE(rt5665_dapm_widgets),
 		.dapm_routes = rt5665_dapm_routes,
 		.num_dapm_routes = ARRAY_SIZE(rt5665_dapm_routes),
-	}
+	},
+	.set_sysclk = rt5665_set_codec_sysclk,
+	.set_pll = rt5665_set_codec_pll,
+	.set_jack = rt5665_set_jack_detect,
 };
 
 
@@ -4783,7 +4845,7 @@ static int rt5665_i2c_probe(struct i2c_client *i2c,
 
 	regmap_write(rt5665->regmap, RT5665_HP_LOGIC_CTRL_2, 0x0002);
 	regmap_update_bits(rt5665->regmap, RT5665_EJD_CTRL_1,
-		0xf000 | RT5665_VREF_POW_MASK, 0xd000 | RT5665_VREF_POW_REG);
+		0xf000 | RT5665_VREF_POW_MASK, 0xe000 | RT5665_VREF_POW_REG);
 	/* Work around for pow_pump */
 	regmap_update_bits(rt5665->regmap, RT5665_STO1_DAC_SIL_DET,
 		RT5665_DEB_STO_DAC_MASK, RT5665_DEB_80_MS);
diff --git a/sound/soc/codecs/rt5665.h b/sound/soc/codecs/rt5665.h
index a30f5e6d0628..1db5c6a62a8e 100644
--- a/sound/soc/codecs/rt5665.h
+++ b/sound/soc/codecs/rt5665.h
@@ -1984,7 +1984,5 @@ enum {
 
 int rt5665_sel_asrc_clk_src(struct snd_soc_codec *codec,
 		unsigned int filter_mask, unsigned int clk_src);
-int rt5665_set_jack_detect(struct snd_soc_codec *codec,
-	struct snd_soc_jack *hs_jack);
 
 #endif /* __RT5665_H__ */
diff --git a/sound/soc/codecs/rt5670.c b/sound/soc/codecs/rt5670.c
index 17d20b99f041..e27c5a4a0a15 100644
--- a/sound/soc/codecs/rt5670.c
+++ b/sound/soc/codecs/rt5670.c
@@ -2835,6 +2835,27 @@ static const struct dmi_system_id dmi_platform_intel_braswell[] = {
 			DMI_MATCH(DMI_PRODUCT_NAME, "Wyse 3040"),
 		},
 	},
+	{
+		.ident = "Lenovo Thinkpad Tablet 10",
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
+			DMI_MATCH(DMI_PRODUCT_VERSION, "ThinkPad 10"),
+		},
+	},
+	{
+		.ident = "Lenovo Thinkpad Tablet 10",
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
+			DMI_MATCH(DMI_PRODUCT_VERSION, "ThinkPad Tablet B"),
+		},
+	},
+	{
+		.ident = "Lenovo Thinkpad Tablet 10",
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
+			DMI_MATCH(DMI_PRODUCT_VERSION, "Lenovo Miix 2 10"),
+		},
+	},
 	{}
 };
 
diff --git a/sound/soc/codecs/rt5677.c b/sound/soc/codecs/rt5677.c
index abc802a5a479..65ac4518ad06 100644
--- a/sound/soc/codecs/rt5677.c
+++ b/sound/soc/codecs/rt5677.c
@@ -5035,6 +5035,12 @@ static const struct i2c_device_id rt5677_i2c_id[] = {
 };
 MODULE_DEVICE_TABLE(i2c, rt5677_i2c_id);
 
+static const struct of_device_id rt5677_of_match[] = {
+	{ .compatible = "realtek,rt5677", },
+	{ }
+};
+MODULE_DEVICE_TABLE(of, rt5677_of_match);
+
 static const struct acpi_gpio_params plug_det_gpio = { RT5677_GPIO_PLUG_DET, 0, false };
 static const struct acpi_gpio_params mic_present_gpio = { RT5677_GPIO_MIC_PRESENT_L, 0, false };
 static const struct acpi_gpio_params headphone_enable_gpio = { RT5677_GPIO_HP_AMP_SHDN_L, 0, false };
@@ -5294,6 +5300,7 @@ static int rt5677_i2c_remove(struct i2c_client *i2c)
 static struct i2c_driver rt5677_i2c_driver = {
 	.driver = {
 		.name = "rt5677",
+		.of_match_table = rt5677_of_match,
 	},
 	.probe = rt5677_i2c_probe,
 	.remove   = rt5677_i2c_remove,
diff --git a/sound/soc/codecs/sgtl5000.c b/sound/soc/codecs/sgtl5000.c
index 1589325855bc..5a2702edeb77 100644
--- a/sound/soc/codecs/sgtl5000.c
+++ b/sound/soc/codecs/sgtl5000.c
@@ -99,6 +99,13 @@ enum sgtl5000_micbias_resistor {
 	SGTL5000_MICBIAS_8K = 8,
 };
 
+enum  {
+	I2S_LRCLK_STRENGTH_DISABLE,
+	I2S_LRCLK_STRENGTH_LOW,
+	I2S_LRCLK_STRENGTH_MEDIUM,
+	I2S_LRCLK_STRENGTH_HIGH,
+};
+
 /* sgtl5000 private structure in codec */
 struct sgtl5000_priv {
 	int sysclk;	/* sysclk rate */
@@ -111,6 +118,7 @@ struct sgtl5000_priv {
 	int revision;
 	u8 micbias_resistor;
 	u8 micbias_voltage;
+	u8 lrclk_strength;
 };
 
 /*
@@ -1089,6 +1097,7 @@ static int sgtl5000_enable_regulators(struct i2c_client *client)
 static int sgtl5000_probe(struct snd_soc_codec *codec)
 {
 	int ret;
+	u16 reg;
 	struct sgtl5000_priv *sgtl5000 = snd_soc_codec_get_drvdata(codec);
 
 	/* power up sgtl5000 */
@@ -1118,7 +1127,8 @@ static int sgtl5000_probe(struct snd_soc_codec *codec)
 			SGTL5000_DAC_MUTE_RIGHT |
 			SGTL5000_DAC_MUTE_LEFT);
 
-	snd_soc_write(codec, SGTL5000_CHIP_PAD_STRENGTH, 0x015f);
+	reg = ((sgtl5000->lrclk_strength) << SGTL5000_PAD_I2S_LRCLK_SHIFT | 0x5f);
+	snd_soc_write(codec, SGTL5000_CHIP_PAD_STRENGTH, reg);
 
 	snd_soc_write(codec, SGTL5000_CHIP_ANA_CTRL,
 			SGTL5000_HP_ZCD_EN |
@@ -1347,6 +1357,13 @@ static int sgtl5000_i2c_probe(struct i2c_client *client,
 		}
 	}
 
+	sgtl5000->lrclk_strength = I2S_LRCLK_STRENGTH_LOW;
+	if (!of_property_read_u32(np, "lrclk-strength", &value)) {
+		if (value > I2S_LRCLK_STRENGTH_HIGH)
+			value = I2S_LRCLK_STRENGTH_LOW;
+		sgtl5000->lrclk_strength = value;
+	}
+
 	/* Ensure sgtl5000 will start with sane register values */
 	sgtl5000_fill_defaults(client);
 
diff --git a/sound/soc/codecs/ssm4567.c b/sound/soc/codecs/ssm4567.c
index 2bb5a11c9ba1..a622623e8558 100644
--- a/sound/soc/codecs/ssm4567.c
+++ b/sound/soc/codecs/ssm4567.c
@@ -485,6 +485,14 @@ static const struct i2c_device_id ssm4567_i2c_ids[] = {
 };
 MODULE_DEVICE_TABLE(i2c, ssm4567_i2c_ids);
 
+#ifdef CONFIG_OF
+static const struct of_device_id ssm4567_of_match[] = {
+	{ .compatible = "adi,ssm4567", },
+	{ }
+};
+MODULE_DEVICE_TABLE(of, ssm4567_of_match);
+#endif
+
 #ifdef CONFIG_ACPI
 
 static const struct acpi_device_id ssm4567_acpi_match[] = {
@@ -498,6 +506,7 @@ MODULE_DEVICE_TABLE(acpi, ssm4567_acpi_match);
 static struct i2c_driver ssm4567_driver = {
 	.driver = {
 		.name = "ssm4567",
+		.of_match_table = of_match_ptr(ssm4567_of_match),
 		.acpi_match_table = ACPI_PTR(ssm4567_acpi_match),
 	},
 	.probe = ssm4567_i2c_probe,
diff --git a/sound/soc/codecs/sta529.c b/sound/soc/codecs/sta529.c
index d4b384e4b266..660734359bf3 100644
--- a/sound/soc/codecs/sta529.c
+++ b/sound/soc/codecs/sta529.c
@@ -375,9 +375,16 @@ static const struct i2c_device_id sta529_i2c_id[] = {
 };
 MODULE_DEVICE_TABLE(i2c, sta529_i2c_id);
 
+static const struct of_device_id sta529_of_match[] = {
+	{ .compatible = "st,sta529", },
+	{ }
+};
+MODULE_DEVICE_TABLE(of, sta529_of_match);
+
 static struct i2c_driver sta529_i2c_driver = {
 	.driver = {
 		.name = "sta529",
+		.of_match_table = sta529_of_match,
 	},
 	.probe		= sta529_i2c_probe,
 	.remove		= sta529_i2c_remove,
diff --git a/sound/soc/codecs/tas2552.c b/sound/soc/codecs/tas2552.c
index baf455e8c2f7..8840f72f3c4a 100644
--- a/sound/soc/codecs/tas2552.c
+++ b/sound/soc/codecs/tas2552.c
@@ -611,7 +611,7 @@ probe_fail:
 
 	regulator_bulk_disable(ARRAY_SIZE(tas2552->supplies),
 					tas2552->supplies);
-	return -EIO;
+	return ret;
 }
 
 static int tas2552_codec_remove(struct snd_soc_codec *codec)
@@ -637,7 +637,7 @@ static int tas2552_suspend(struct snd_soc_codec *codec)
 	if (ret != 0)
 		dev_err(codec->dev, "Failed to disable supplies: %d\n",
 			ret);
-	return 0;
+	return ret;
 }
 
 static int tas2552_resume(struct snd_soc_codec *codec)
@@ -653,7 +653,7 @@ static int tas2552_resume(struct snd_soc_codec *codec)
 			ret);
 	}
 
-	return 0;
+	return ret;
 }
 #else
 #define tas2552_suspend NULL
diff --git a/sound/soc/codecs/tlv320aic23.c b/sound/soc/codecs/tlv320aic23.c
index 410cae0f2060..628a8eeaab68 100644
--- a/sound/soc/codecs/tlv320aic23.c
+++ b/sound/soc/codecs/tlv320aic23.c
@@ -174,10 +174,9 @@ static const struct snd_soc_dapm_route tlv320aic23_intercon[] = {
 	{"ROUT", NULL, "Output Mixer"},
 
 	/* Inputs */
-	{"Line Input", "NULL", "LLINEIN"},
-	{"Line Input", "NULL", "RLINEIN"},
-
-	{"Mic Input", "NULL", "MICIN"},
+	{"Line Input", NULL, "LLINEIN"},
+	{"Line Input", NULL, "RLINEIN"},
+	{"Mic Input", NULL, "MICIN"},
 
 	/* input mux */
 	{"Capture Source", "Line", "Line Input"},
diff --git a/sound/soc/codecs/twl6040.c b/sound/soc/codecs/twl6040.c
index 748036e851ea..2b6ad09e0886 100644
--- a/sound/soc/codecs/twl6040.c
+++ b/sound/soc/codecs/twl6040.c
@@ -606,6 +606,14 @@ static const struct snd_kcontrol_new twl6040_snd_controls[] = {
 		twl6040_headset_power_get_enum,
 		twl6040_headset_power_put_enum),
 
+	/* Left HS PDM data routed to Right HSDAC */
+	SOC_SINGLE("Headset Mono to Stereo Playback Switch",
+		TWL6040_REG_HSRCTL, 7, 1, 0),
+
+	/* Left HF PDM data routed to Right HFDAC */
+	SOC_SINGLE("Handsfree Mono to Stereo Playback Switch",
+		TWL6040_REG_HFRCTL, 5, 1, 0),
+
 	SOC_ENUM_EXT("PLL Selection", twl6040_power_mode_enum,
 		twl6040_pll_get_enum, twl6040_pll_put_enum),
 };
diff --git a/sound/soc/codecs/uda1380.c b/sound/soc/codecs/uda1380.c
index 2918fdb95e58..61cdc79840e7 100644
--- a/sound/soc/codecs/uda1380.c
+++ b/sound/soc/codecs/uda1380.c
@@ -791,9 +791,16 @@ static const struct i2c_device_id uda1380_i2c_id[] = {
 };
 MODULE_DEVICE_TABLE(i2c, uda1380_i2c_id);
 
+static const struct of_device_id uda1380_of_match[] = {
+	{ .compatible = "nxp,uda1380", },
+	{ }
+};
+MODULE_DEVICE_TABLE(of, uda1380_of_match);
+
 static struct i2c_driver uda1380_i2c_driver = {
 	.driver = {
 		.name =  "uda1380-codec",
+		.of_match_table = uda1380_of_match,
 	},
 	.probe =    uda1380_i2c_probe,
 	.remove =   uda1380_i2c_remove,
diff --git a/sound/soc/codecs/wm5100.c b/sound/soc/codecs/wm5100.c
index 560575000cc5..138a84efdd54 100644
--- a/sound/soc/codecs/wm5100.c
+++ b/sound/soc/codecs/wm5100.c
@@ -2014,7 +2014,7 @@ static void wm5100_micd_irq(struct wm5100_priv *wm5100)
 
 	ret = regmap_read(wm5100->regmap, WM5100_MIC_DETECT_3, &val);
 	if (ret != 0) {
-		dev_err(wm5100->dev, "Failed to read micropone status: %d\n",
+		dev_err(wm5100->dev, "Failed to read microphone status: %d\n",
 			ret);
 		return;
 	}
diff --git a/sound/soc/codecs/wm8903.c b/sound/soc/codecs/wm8903.c
index 6e887c2c42b1..237eeb9a8b97 100644
--- a/sound/soc/codecs/wm8903.c
+++ b/sound/soc/codecs/wm8903.c
@@ -24,6 +24,7 @@
 #include <linux/pm.h>
 #include <linux/i2c.h>
 #include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
 #include <linux/slab.h>
 #include <linux/irq.h>
 #include <linux/mutex.h>
@@ -115,10 +116,19 @@ static const struct reg_default wm8903_reg_defaults[] = {
 	{ 172, 0x0000 },    /* R172 - Analogue Output Bias 0 */
 };
 
+#define WM8903_NUM_SUPPLIES 4
+static const char *wm8903_supply_names[WM8903_NUM_SUPPLIES] = {
+	"AVDD",
+	"CPVDD",
+	"DBVDD",
+	"DCVDD",
+};
+
 struct wm8903_priv {
 	struct wm8903_platform_data *pdata;
 	struct device *dev;
 	struct regmap *regmap;
+	struct regulator_bulk_data supplies[WM8903_NUM_SUPPLIES];
 
 	int sysclk;
 	int irq;
@@ -2030,6 +2040,23 @@ static int wm8903_i2c_probe(struct i2c_client *i2c,
 
 	pdata = wm8903->pdata;
 
+	for (i = 0; i < ARRAY_SIZE(wm8903->supplies); i++)
+		wm8903->supplies[i].supply = wm8903_supply_names[i];
+
+	ret = devm_regulator_bulk_get(&i2c->dev, ARRAY_SIZE(wm8903->supplies),
+				      wm8903->supplies);
+	if (ret != 0) {
+		dev_err(&i2c->dev, "Failed to request supplies: %d\n", ret);
+		return ret;
+	}
+
+	ret = regulator_bulk_enable(ARRAY_SIZE(wm8903->supplies),
+				    wm8903->supplies);
+	if (ret != 0) {
+		dev_err(&i2c->dev, "Failed to enable supplies: %d\n", ret);
+		return ret;
+	}
+
 	ret = regmap_read(wm8903->regmap, WM8903_SW_RESET_AND_ID, &val);
 	if (ret != 0) {
 		dev_err(&i2c->dev, "Failed to read chip ID: %d\n", ret);
@@ -2160,6 +2187,8 @@ static int wm8903_i2c_probe(struct i2c_client *i2c,
 
 	return 0;
 err:
+	regulator_bulk_disable(ARRAY_SIZE(wm8903->supplies),
+			       wm8903->supplies);
 	return ret;
 }
 
@@ -2167,6 +2196,8 @@ static int wm8903_i2c_remove(struct i2c_client *client)
 {
 	struct wm8903_priv *wm8903 = i2c_get_clientdata(client);
 
+	regulator_bulk_disable(ARRAY_SIZE(wm8903->supplies),
+			       wm8903->supplies);
 	if (client->irq)
 		free_irq(client->irq, wm8903);
 	wm8903_free_gpio(wm8903);
diff --git a/sound/soc/codecs/wm8960.c b/sound/soc/codecs/wm8960.c
index 3bf081a7e450..9ed455700954 100644
--- a/sound/soc/codecs/wm8960.c
+++ b/sound/soc/codecs/wm8960.c
@@ -604,12 +604,150 @@ static const int bclk_divs[] = {
 	120, 160, 220, 240, 320, 320, 320
 };
 
+/**
+ * wm8960_configure_sysclk - checks if there is a sysclk frequency available
+ *	The sysclk must be chosen such that:
+ *		- sysclk     = MCLK / sysclk_divs
+ *		- lrclk      = sysclk / dac_divs
+ *		- 10 * bclk  = sysclk / bclk_divs
+ *
+ *	If we cannot find an exact match for (sysclk, lrclk, bclk)
+ *	triplet, we relax the bclk such that bclk is chosen as the
+ *	closest available frequency greater than expected bclk.
+ *
+ * @wm8960_priv: wm8960 codec private data
+ * @mclk: MCLK used to derive sysclk
+ * @sysclk_idx: sysclk_divs index for found sysclk
+ * @dac_idx: dac_divs index for found lrclk
+ * @bclk_idx: bclk_divs index for found bclk
+ *
+ * Returns:
+ *  -1, in case no sysclk frequency available found
+ * >=0, in case we could derive bclk and lrclk from sysclk using
+ *      (@sysclk_idx, @dac_idx, @bclk_idx) dividers
+ */
+static
+int wm8960_configure_sysclk(struct wm8960_priv *wm8960, int mclk,
+			    int *sysclk_idx, int *dac_idx, int *bclk_idx)
+{
+	int sysclk, bclk, lrclk;
+	int i, j, k;
+	int diff, closest = mclk;
+
+	/* marker for no match */
+	*bclk_idx = -1;
+
+	bclk = wm8960->bclk;
+	lrclk = wm8960->lrclk;
+
+	/* check if the sysclk frequency is available. */
+	for (i = 0; i < ARRAY_SIZE(sysclk_divs); ++i) {
+		if (sysclk_divs[i] == -1)
+			continue;
+		sysclk = mclk / sysclk_divs[i];
+		for (j = 0; j < ARRAY_SIZE(dac_divs); ++j) {
+			if (sysclk != dac_divs[j] * lrclk)
+				continue;
+			for (k = 0; k < ARRAY_SIZE(bclk_divs); ++k) {
+				diff = sysclk - bclk * bclk_divs[k] / 10;
+				if (diff == 0) {
+					*sysclk_idx = i;
+					*dac_idx = j;
+					*bclk_idx = k;
+					break;
+				}
+				if (diff > 0 && closest > diff) {
+					*sysclk_idx = i;
+					*dac_idx = j;
+					*bclk_idx = k;
+					closest = diff;
+				}
+			}
+			if (k != ARRAY_SIZE(bclk_divs))
+				break;
+		}
+		if (j != ARRAY_SIZE(dac_divs))
+			break;
+	}
+	return *bclk_idx;
+}
+
+/**
+ * wm8960_configure_pll - checks if there is a PLL out frequency available
+ *	The PLL out frequency must be chosen such that:
+ *		- sysclk      = lrclk * dac_divs
+ *		- freq_out    = sysclk * sysclk_divs
+ *		- 10 * sysclk = bclk * bclk_divs
+ *
+ * 	If we cannot find an exact match for (sysclk, lrclk, bclk)
+ * 	triplet, we relax the bclk such that bclk is chosen as the
+ * 	closest available frequency greater than expected bclk.
+ *
+ * @codec: codec structure
+ * @freq_in: input frequency used to derive freq out via PLL
+ * @sysclk_idx: sysclk_divs index for found sysclk
+ * @dac_idx: dac_divs index for found lrclk
+ * @bclk_idx: bclk_divs index for found bclk
+ *
+ * Returns:
+ * < 0, in case no PLL frequency out available was found
+ * >=0, in case we could derive bclk, lrclk, sysclk from PLL out using
+ *      (@sysclk_idx, @dac_idx, @bclk_idx) dividers
+ */
+static
+int wm8960_configure_pll(struct snd_soc_codec *codec, int freq_in,
+			 int *sysclk_idx, int *dac_idx, int *bclk_idx)
+{
+	struct wm8960_priv *wm8960 = snd_soc_codec_get_drvdata(codec);
+	int sysclk, bclk, lrclk, freq_out;
+	int diff, closest, best_freq_out;
+	int i, j, k;
+
+	bclk = wm8960->bclk;
+	lrclk = wm8960->lrclk;
+	closest = freq_in;
+
+	best_freq_out = -EINVAL;
+	*sysclk_idx = *dac_idx = *bclk_idx = -1;
+
+	for (i = 0; i < ARRAY_SIZE(sysclk_divs); ++i) {
+		if (sysclk_divs[i] == -1)
+			continue;
+		for (j = 0; j < ARRAY_SIZE(dac_divs); ++j) {
+			sysclk = lrclk * dac_divs[j];
+			freq_out = sysclk * sysclk_divs[i];
+
+			for (k = 0; k < ARRAY_SIZE(bclk_divs); ++k) {
+				if (!is_pll_freq_available(freq_in, freq_out))
+					continue;
+
+				diff = sysclk - bclk * bclk_divs[k] / 10;
+				if (diff == 0) {
+					*sysclk_idx = i;
+					*dac_idx = j;
+					*bclk_idx = k;
+					return freq_out;
+				}
+				if (diff > 0 && closest > diff) {
+					*sysclk_idx = i;
+					*dac_idx = j;
+					*bclk_idx = k;
+					closest = diff;
+					best_freq_out = freq_out;
+				}
+			}
+		}
+	}
+
+	return best_freq_out;
+}
 static int wm8960_configure_clocking(struct snd_soc_codec *codec)
 {
 	struct wm8960_priv *wm8960 = snd_soc_codec_get_drvdata(codec);
-	int sysclk, bclk, lrclk, freq_out, freq_in;
+	int freq_out, freq_in;
 	u16 iface1 = snd_soc_read(codec, WM8960_IFACE1);
 	int i, j, k;
+	int ret;
 
 	if (!(iface1 & (1<<6))) {
 		dev_dbg(codec->dev,
@@ -623,8 +761,6 @@ static int wm8960_configure_clocking(struct snd_soc_codec *codec)
 	}
 
 	freq_in = wm8960->freq_in;
-	bclk = wm8960->bclk;
-	lrclk = wm8960->lrclk;
 	/*
 	 * If it's sysclk auto mode, check if the MCLK can provide sysclk or
 	 * not. If MCLK can provide sysclk, using MCLK to provide sysclk
@@ -643,60 +779,21 @@ static int wm8960_configure_clocking(struct snd_soc_codec *codec)
 	}
 
 	if (wm8960->clk_id != WM8960_SYSCLK_PLL) {
-		/* check if the sysclk frequency is available. */
-		for (i = 0; i < ARRAY_SIZE(sysclk_divs); ++i) {
-			if (sysclk_divs[i] == -1)
-				continue;
-			sysclk = freq_out / sysclk_divs[i];
-			for (j = 0; j < ARRAY_SIZE(dac_divs); ++j) {
-				if (sysclk != dac_divs[j] * lrclk)
-					continue;
-				for (k = 0; k < ARRAY_SIZE(bclk_divs); ++k)
-					if (sysclk == bclk * bclk_divs[k] / 10)
-						break;
-				if (k != ARRAY_SIZE(bclk_divs))
-					break;
-			}
-			if (j != ARRAY_SIZE(dac_divs))
-				break;
-		}
-
-		if (i != ARRAY_SIZE(sysclk_divs)) {
+		ret = wm8960_configure_sysclk(wm8960, freq_out, &i, &j, &k);
+		if (ret >= 0) {
 			goto configure_clock;
 		} else if (wm8960->clk_id != WM8960_SYSCLK_AUTO) {
 			dev_err(codec->dev, "failed to configure clock\n");
 			return -EINVAL;
 		}
 	}
-	/* get a available pll out frequency and set pll */
-	for (i = 0; i < ARRAY_SIZE(sysclk_divs); ++i) {
-		if (sysclk_divs[i] == -1)
-			continue;
-		for (j = 0; j < ARRAY_SIZE(dac_divs); ++j) {
-			sysclk = lrclk * dac_divs[j];
-			freq_out = sysclk * sysclk_divs[i];
 
-			for (k = 0; k < ARRAY_SIZE(bclk_divs); ++k) {
-				if (sysclk == bclk * bclk_divs[k] / 10 &&
-				    is_pll_freq_available(freq_in, freq_out)) {
-					wm8960_set_pll(codec,
-						       freq_in, freq_out);
-					break;
-				} else {
-					continue;
-				}
-			}
-			if (k != ARRAY_SIZE(bclk_divs))
-				break;
-		}
-		if (j != ARRAY_SIZE(dac_divs))
-			break;
-	}
-
-	if (i == ARRAY_SIZE(sysclk_divs)) {
-		dev_err(codec->dev, "failed to configure clock\n");
-		return -EINVAL;
+	freq_out = wm8960_configure_pll(codec, freq_in, &i, &j, &k);
+	if (freq_out < 0) {
+		dev_err(codec->dev, "failed to configure clock via PLL\n");
+		return freq_out;
 	}
+	wm8960_set_pll(codec, freq_in, freq_out);
 
 configure_clock:
 	/* configure sysclk clock */
diff --git a/sound/soc/codecs/wm8978.c b/sound/soc/codecs/wm8978.c
index 90b2d418ef60..cf761e2d7546 100644
--- a/sound/soc/codecs/wm8978.c
+++ b/sound/soc/codecs/wm8978.c
@@ -1071,9 +1071,16 @@ static const struct i2c_device_id wm8978_i2c_id[] = {
 };
 MODULE_DEVICE_TABLE(i2c, wm8978_i2c_id);
 
+static const struct of_device_id wm8978_of_match[] = {
+	{ .compatible = "wlf,wm8978", },
+	{ }
+};
+MODULE_DEVICE_TABLE(of, wm8978_of_match);
+
 static struct i2c_driver wm8978_i2c_driver = {
 	.driver = {
 		.name = "wm8978",
+		.of_match_table = wm8978_of_match,
 	},
 	.probe =    wm8978_i2c_probe,
 	.remove =   wm8978_i2c_remove,
diff --git a/sound/soc/codecs/wm_adsp.c b/sound/soc/codecs/wm_adsp.c
index bbdb72f73df1..20695b691aff 100644
--- a/sound/soc/codecs/wm_adsp.c
+++ b/sound/soc/codecs/wm_adsp.c
@@ -112,17 +112,22 @@
 #define ADSP1_CLK_SEL_SHIFT                    0  /* CLK_SEL_ENA */
 #define ADSP1_CLK_SEL_WIDTH                    3  /* CLK_SEL_ENA */
 
-#define ADSP2_CONTROL        0x0
-#define ADSP2_CLOCKING       0x1
-#define ADSP2_STATUS1        0x4
-#define ADSP2_WDMA_CONFIG_1 0x30
-#define ADSP2_WDMA_CONFIG_2 0x31
-#define ADSP2_RDMA_CONFIG_1 0x34
-
-#define ADSP2_SCRATCH0        0x40
-#define ADSP2_SCRATCH1        0x41
-#define ADSP2_SCRATCH2        0x42
-#define ADSP2_SCRATCH3        0x43
+#define ADSP2_CONTROL                     0x0
+#define ADSP2_CLOCKING                    0x1
+#define ADSP2V2_CLOCKING                  0x2
+#define ADSP2_STATUS1                     0x4
+#define ADSP2_WDMA_CONFIG_1               0x30
+#define ADSP2_WDMA_CONFIG_2               0x31
+#define ADSP2V2_WDMA_CONFIG_2             0x32
+#define ADSP2_RDMA_CONFIG_1               0x34
+
+#define ADSP2_SCRATCH0                    0x40
+#define ADSP2_SCRATCH1                    0x41
+#define ADSP2_SCRATCH2                    0x42
+#define ADSP2_SCRATCH3                    0x43
+
+#define ADSP2V2_SCRATCH0_1                0x40
+#define ADSP2V2_SCRATCH2_3                0x42
 
 /*
  * ADSP2 Control
@@ -153,6 +158,17 @@
 #define ADSP2_CLK_SEL_WIDTH                    3  /* CLK_SEL_ENA */
 
 /*
+ * ADSP2V2 clocking
+ */
+#define ADSP2V2_CLK_SEL_MASK             0x70000  /* CLK_SEL_ENA */
+#define ADSP2V2_CLK_SEL_SHIFT                 16  /* CLK_SEL_ENA */
+#define ADSP2V2_CLK_SEL_WIDTH                  3  /* CLK_SEL_ENA */
+
+#define ADSP2V2_RATE_MASK                 0x7800  /* DSP_RATE */
+#define ADSP2V2_RATE_SHIFT                    11  /* DSP_RATE */
+#define ADSP2V2_RATE_WIDTH                     4  /* DSP_RATE */
+
+/*
  * ADSP2 Status 1
  */
 #define ADSP2_RAM_RDY                     0x0001
@@ -160,6 +176,37 @@
 #define ADSP2_RAM_RDY_SHIFT                    0
 #define ADSP2_RAM_RDY_WIDTH                    1
 
+/*
+ * ADSP2 Lock support
+ */
+#define ADSP2_LOCK_CODE_0                    0x5555
+#define ADSP2_LOCK_CODE_1                    0xAAAA
+
+#define ADSP2_WATCHDOG                       0x0A
+#define ADSP2_BUS_ERR_ADDR                   0x52
+#define ADSP2_REGION_LOCK_STATUS             0x64
+#define ADSP2_LOCK_REGION_1_LOCK_REGION_0    0x66
+#define ADSP2_LOCK_REGION_3_LOCK_REGION_2    0x68
+#define ADSP2_LOCK_REGION_5_LOCK_REGION_4    0x6A
+#define ADSP2_LOCK_REGION_7_LOCK_REGION_6    0x6C
+#define ADSP2_LOCK_REGION_9_LOCK_REGION_8    0x6E
+#define ADSP2_LOCK_REGION_CTRL               0x7A
+#define ADSP2_PMEM_ERR_ADDR_XMEM_ERR_ADDR    0x7C
+
+#define ADSP2_REGION_LOCK_ERR_MASK           0x8000
+#define ADSP2_SLAVE_ERR_MASK                 0x4000
+#define ADSP2_WDT_TIMEOUT_STS_MASK           0x2000
+#define ADSP2_CTRL_ERR_PAUSE_ENA             0x0002
+#define ADSP2_CTRL_ERR_EINT                  0x0001
+
+#define ADSP2_BUS_ERR_ADDR_MASK              0x00FFFFFF
+#define ADSP2_XMEM_ERR_ADDR_MASK             0x0000FFFF
+#define ADSP2_PMEM_ERR_ADDR_MASK             0x7FFF0000
+#define ADSP2_PMEM_ERR_ADDR_SHIFT            16
+#define ADSP2_WDT_ENA_MASK                   0xFFFFFFFD
+
+#define ADSP2_LOCK_REGION_SHIFT              16
+
 #define ADSP_MAX_STD_CTRL_SIZE               512
 
 #define WM_ADSP_ACKED_CTL_TIMEOUT_MS         100
@@ -683,6 +730,9 @@ static const struct soc_enum wm_adsp_fw_enum[] = {
 	SOC_ENUM_SINGLE(0, 1, ARRAY_SIZE(wm_adsp_fw_text), wm_adsp_fw_text),
 	SOC_ENUM_SINGLE(0, 2, ARRAY_SIZE(wm_adsp_fw_text), wm_adsp_fw_text),
 	SOC_ENUM_SINGLE(0, 3, ARRAY_SIZE(wm_adsp_fw_text), wm_adsp_fw_text),
+	SOC_ENUM_SINGLE(0, 4, ARRAY_SIZE(wm_adsp_fw_text), wm_adsp_fw_text),
+	SOC_ENUM_SINGLE(0, 5, ARRAY_SIZE(wm_adsp_fw_text), wm_adsp_fw_text),
+	SOC_ENUM_SINGLE(0, 6, ARRAY_SIZE(wm_adsp_fw_text), wm_adsp_fw_text),
 };
 
 const struct snd_kcontrol_new wm_adsp_fw_controls[] = {
@@ -694,6 +744,12 @@ const struct snd_kcontrol_new wm_adsp_fw_controls[] = {
 		     wm_adsp_fw_get, wm_adsp_fw_put),
 	SOC_ENUM_EXT("DSP4 Firmware", wm_adsp_fw_enum[3],
 		     wm_adsp_fw_get, wm_adsp_fw_put),
+	SOC_ENUM_EXT("DSP5 Firmware", wm_adsp_fw_enum[4],
+		     wm_adsp_fw_get, wm_adsp_fw_put),
+	SOC_ENUM_EXT("DSP6 Firmware", wm_adsp_fw_enum[5],
+		     wm_adsp_fw_get, wm_adsp_fw_put),
+	SOC_ENUM_EXT("DSP7 Firmware", wm_adsp_fw_enum[6],
+		     wm_adsp_fw_get, wm_adsp_fw_put),
 };
 EXPORT_SYMBOL_GPL(wm_adsp_fw_controls);
 
@@ -750,6 +806,29 @@ static void wm_adsp2_show_fw_status(struct wm_adsp *dsp)
 		 be16_to_cpu(scratch[3]));
 }
 
+static void wm_adsp2v2_show_fw_status(struct wm_adsp *dsp)
+{
+	u32 scratch[2];
+	int ret;
+
+	ret = regmap_raw_read(dsp->regmap, dsp->base + ADSP2V2_SCRATCH0_1,
+			      scratch, sizeof(scratch));
+
+	if (ret) {
+		adsp_err(dsp, "Failed to read SCRATCH regs: %d\n", ret);
+		return;
+	}
+
+	scratch[0] = be32_to_cpu(scratch[0]);
+	scratch[1] = be32_to_cpu(scratch[1]);
+
+	adsp_dbg(dsp, "FW SCRATCH 0:0x%x 1:0x%x 2:0x%x 3:0x%x\n",
+		 scratch[0] & 0xFFFF,
+		 scratch[0] >> 16,
+		 scratch[1] & 0xFFFF,
+		 scratch[1] >> 16);
+}
+
 static inline struct wm_coeff_ctl *bytes_ext_to_ctl(struct soc_bytes_ext *ext)
 {
 	return container_of(ext, struct wm_coeff_ctl, bytes_ext);
@@ -2435,10 +2514,17 @@ static int wm_adsp2_ena(struct wm_adsp *dsp)
 	unsigned int val;
 	int ret, count;
 
-	ret = regmap_update_bits_async(dsp->regmap, dsp->base + ADSP2_CONTROL,
-				       ADSP2_SYS_ENA, ADSP2_SYS_ENA);
-	if (ret != 0)
-		return ret;
+	switch (dsp->rev) {
+	case 0:
+		ret = regmap_update_bits_async(dsp->regmap,
+					       dsp->base + ADSP2_CONTROL,
+					       ADSP2_SYS_ENA, ADSP2_SYS_ENA);
+		if (ret != 0)
+			return ret;
+		break;
+	default:
+		break;
+	}
 
 	/* Wait for the RAM to start, should be near instantaneous */
 	for (count = 0; count < 10; ++count) {
@@ -2497,11 +2583,17 @@ static void wm_adsp2_boot_work(struct work_struct *work)
 	if (ret != 0)
 		goto err_ena;
 
-	/* Turn DSP back off until we are ready to run */
-	ret = regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL,
-				 ADSP2_SYS_ENA, 0);
-	if (ret != 0)
-		goto err_ena;
+	switch (dsp->rev) {
+	case 0:
+		/* Turn DSP back off until we are ready to run */
+		ret = regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL,
+					 ADSP2_SYS_ENA, 0);
+		if (ret != 0)
+			goto err_ena;
+		break;
+	default:
+		break;
+	}
 
 	dsp->booted = true;
 
@@ -2523,12 +2615,21 @@ static void wm_adsp2_set_dspclk(struct wm_adsp *dsp, unsigned int freq)
 {
 	int ret;
 
-	ret = regmap_update_bits_async(dsp->regmap,
-				       dsp->base + ADSP2_CLOCKING,
-				       ADSP2_CLK_SEL_MASK,
-				       freq << ADSP2_CLK_SEL_SHIFT);
-	if (ret != 0)
-		adsp_err(dsp, "Failed to set clock rate: %d\n", ret);
+	switch (dsp->rev) {
+	case 0:
+		ret = regmap_update_bits_async(dsp->regmap,
+					       dsp->base + ADSP2_CLOCKING,
+					       ADSP2_CLK_SEL_MASK,
+					       freq << ADSP2_CLK_SEL_SHIFT);
+		if (ret) {
+			adsp_err(dsp, "Failed to set clock rate: %d\n", ret);
+			return;
+		}
+		break;
+	default:
+		/* clock is handled by parent codec driver */
+		break;
+	}
 }
 
 int wm_adsp2_preloader_get(struct snd_kcontrol *kcontrol,
@@ -2568,6 +2669,18 @@ int wm_adsp2_preloader_put(struct snd_kcontrol *kcontrol,
 }
 EXPORT_SYMBOL_GPL(wm_adsp2_preloader_put);
 
+static void wm_adsp_stop_watchdog(struct wm_adsp *dsp)
+{
+	switch (dsp->rev) {
+	case 0:
+	case 1:
+		return;
+	default:
+		regmap_update_bits(dsp->regmap, dsp->base + ADSP2_WATCHDOG,
+				   ADSP2_WDT_ENA_MASK, 0);
+	}
+}
+
 int wm_adsp2_early_event(struct snd_soc_dapm_widget *w,
 			 struct snd_kcontrol *kcontrol, int event,
 			 unsigned int freq)
@@ -2640,6 +2753,8 @@ int wm_adsp2_event(struct snd_soc_dapm_widget *w,
 		if (ret != 0)
 			goto err;
 
+		wm_adsp2_lock(dsp, dsp->lock_regions);
+
 		ret = regmap_update_bits(dsp->regmap,
 					 dsp->base + ADSP2_CONTROL,
 					 ADSP2_CORE_ENA | ADSP2_START,
@@ -2663,23 +2778,49 @@ int wm_adsp2_event(struct snd_soc_dapm_widget *w,
 		/* Tell the firmware to cleanup */
 		wm_adsp_signal_event_controls(dsp, WM_ADSP_FW_EVENT_SHUTDOWN);
 
+		wm_adsp_stop_watchdog(dsp);
+
 		/* Log firmware state, it can be useful for analysis */
-		wm_adsp2_show_fw_status(dsp);
+		switch (dsp->rev) {
+		case 0:
+			wm_adsp2_show_fw_status(dsp);
+			break;
+		default:
+			wm_adsp2v2_show_fw_status(dsp);
+			break;
+		}
 
 		mutex_lock(&dsp->pwr_lock);
 
 		dsp->running = false;
 
-		regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL,
+		regmap_update_bits(dsp->regmap,
+				   dsp->base + ADSP2_CONTROL,
 				   ADSP2_CORE_ENA | ADSP2_START, 0);
 
 		/* Make sure DMAs are quiesced */
-		regmap_write(dsp->regmap, dsp->base + ADSP2_RDMA_CONFIG_1, 0);
-		regmap_write(dsp->regmap, dsp->base + ADSP2_WDMA_CONFIG_1, 0);
-		regmap_write(dsp->regmap, dsp->base + ADSP2_WDMA_CONFIG_2, 0);
-
-		regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL,
-				   ADSP2_SYS_ENA, 0);
+		switch (dsp->rev) {
+		case 0:
+			regmap_write(dsp->regmap,
+				     dsp->base + ADSP2_RDMA_CONFIG_1, 0);
+			regmap_write(dsp->regmap,
+				     dsp->base + ADSP2_WDMA_CONFIG_1, 0);
+			regmap_write(dsp->regmap,
+				     dsp->base + ADSP2_WDMA_CONFIG_2, 0);
+
+			regmap_update_bits(dsp->regmap,
+					   dsp->base + ADSP2_CONTROL,
+					   ADSP2_SYS_ENA, 0);
+			break;
+		default:
+			regmap_write(dsp->regmap,
+				     dsp->base + ADSP2_RDMA_CONFIG_1, 0);
+			regmap_write(dsp->regmap,
+				     dsp->base + ADSP2_WDMA_CONFIG_1, 0);
+			regmap_write(dsp->regmap,
+				     dsp->base + ADSP2V2_WDMA_CONFIG_2, 0);
+			break;
+		}
 
 		if (wm_adsp_fw[dsp->fw].num_caps != 0)
 			wm_adsp_buffer_free(dsp);
@@ -2732,15 +2873,22 @@ int wm_adsp2_init(struct wm_adsp *dsp)
 {
 	int ret;
 
-	/*
-	 * Disable the DSP memory by default when in reset for a small
-	 * power saving.
-	 */
-	ret = regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL,
-				 ADSP2_MEM_ENA, 0);
-	if (ret != 0) {
-		adsp_err(dsp, "Failed to clear memory retention: %d\n", ret);
-		return ret;
+	switch (dsp->rev) {
+	case 0:
+		/*
+		 * Disable the DSP memory by default when in reset for a small
+		 * power saving.
+		 */
+		ret = regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL,
+					 ADSP2_MEM_ENA, 0);
+		if (ret) {
+			adsp_err(dsp,
+				 "Failed to clear memory retention: %d\n", ret);
+			return ret;
+		}
+		break;
+	default:
+		break;
 	}
 
 	INIT_LIST_HEAD(&dsp->alg_regions);
@@ -3523,4 +3671,94 @@ int wm_adsp_compr_copy(struct snd_compr_stream *stream, char __user *buf,
 }
 EXPORT_SYMBOL_GPL(wm_adsp_compr_copy);
 
+int wm_adsp2_lock(struct wm_adsp *dsp, unsigned int lock_regions)
+{
+	struct regmap *regmap = dsp->regmap;
+	unsigned int code0, code1, lock_reg;
+
+	if (!(lock_regions & WM_ADSP2_REGION_ALL))
+		return 0;
+
+	lock_regions &= WM_ADSP2_REGION_ALL;
+	lock_reg = dsp->base + ADSP2_LOCK_REGION_1_LOCK_REGION_0;
+
+	while (lock_regions) {
+		code0 = code1 = 0;
+		if (lock_regions & BIT(0)) {
+			code0 = ADSP2_LOCK_CODE_0;
+			code1 = ADSP2_LOCK_CODE_1;
+		}
+		if (lock_regions & BIT(1)) {
+			code0 |= ADSP2_LOCK_CODE_0 << ADSP2_LOCK_REGION_SHIFT;
+			code1 |= ADSP2_LOCK_CODE_1 << ADSP2_LOCK_REGION_SHIFT;
+		}
+		regmap_write(regmap, lock_reg, code0);
+		regmap_write(regmap, lock_reg, code1);
+		lock_regions >>= 2;
+		lock_reg += 2;
+	}
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(wm_adsp2_lock);
+
+irqreturn_t wm_adsp2_bus_error(struct wm_adsp *dsp)
+{
+	unsigned int val;
+	struct regmap *regmap = dsp->regmap;
+	int ret = 0;
+
+	ret = regmap_read(regmap, dsp->base + ADSP2_LOCK_REGION_CTRL, &val);
+	if (ret) {
+		adsp_err(dsp,
+			"Failed to read Region Lock Ctrl register: %d\n", ret);
+		return IRQ_HANDLED;
+	}
+
+	if (val & ADSP2_WDT_TIMEOUT_STS_MASK) {
+		adsp_err(dsp, "watchdog timeout error\n");
+		wm_adsp_stop_watchdog(dsp);
+	}
+
+	if (val & (ADSP2_SLAVE_ERR_MASK | ADSP2_REGION_LOCK_ERR_MASK)) {
+		if (val & ADSP2_SLAVE_ERR_MASK)
+			adsp_err(dsp, "bus error: slave error\n");
+		else
+			adsp_err(dsp, "bus error: region lock error\n");
+
+		ret = regmap_read(regmap, dsp->base + ADSP2_BUS_ERR_ADDR, &val);
+		if (ret) {
+			adsp_err(dsp,
+				 "Failed to read Bus Err Addr register: %d\n",
+				 ret);
+			return IRQ_HANDLED;
+		}
+
+		adsp_err(dsp, "bus error address = 0x%x\n",
+			 val & ADSP2_BUS_ERR_ADDR_MASK);
+
+		ret = regmap_read(regmap,
+				  dsp->base + ADSP2_PMEM_ERR_ADDR_XMEM_ERR_ADDR,
+				  &val);
+		if (ret) {
+			adsp_err(dsp,
+				 "Failed to read Pmem Xmem Err Addr register: %d\n",
+				 ret);
+			return IRQ_HANDLED;
+		}
+
+		adsp_err(dsp, "xmem error address = 0x%x\n",
+			 val & ADSP2_XMEM_ERR_ADDR_MASK);
+		adsp_err(dsp, "pmem error address = 0x%x\n",
+			 (val & ADSP2_PMEM_ERR_ADDR_MASK) >>
+			 ADSP2_PMEM_ERR_ADDR_SHIFT);
+	}
+
+	regmap_update_bits(regmap, dsp->base + ADSP2_LOCK_REGION_CTRL,
+			   ADSP2_CTRL_ERR_EINT, ADSP2_CTRL_ERR_EINT);
+
+	return IRQ_HANDLED;
+}
+EXPORT_SYMBOL_GPL(wm_adsp2_bus_error);
+
 MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/wm_adsp.h b/sound/soc/codecs/wm_adsp.h
index 3706b11053a3..41cc11c19b83 100644
--- a/sound/soc/codecs/wm_adsp.h
+++ b/sound/soc/codecs/wm_adsp.h
@@ -23,6 +23,23 @@
 #define WM_ADSP_COMPR_OK                 0
 #define WM_ADSP_COMPR_VOICE_TRIGGER      1
 
+#define WM_ADSP2_REGION_0 BIT(0)
+#define WM_ADSP2_REGION_1 BIT(1)
+#define WM_ADSP2_REGION_2 BIT(2)
+#define WM_ADSP2_REGION_3 BIT(3)
+#define WM_ADSP2_REGION_4 BIT(4)
+#define WM_ADSP2_REGION_5 BIT(5)
+#define WM_ADSP2_REGION_6 BIT(6)
+#define WM_ADSP2_REGION_7 BIT(7)
+#define WM_ADSP2_REGION_8 BIT(8)
+#define WM_ADSP2_REGION_9 BIT(9)
+#define WM_ADSP2_REGION_1_9 (WM_ADSP2_REGION_1 | \
+		WM_ADSP2_REGION_2 | WM_ADSP2_REGION_3 | \
+		WM_ADSP2_REGION_4 | WM_ADSP2_REGION_5 | \
+		WM_ADSP2_REGION_6 | WM_ADSP2_REGION_7 | \
+		WM_ADSP2_REGION_8 | WM_ADSP2_REGION_9)
+#define WM_ADSP2_REGION_ALL (WM_ADSP2_REGION_0 | WM_ADSP2_REGION_1_9)
+
 struct wm_adsp_region {
 	int type;
 	unsigned int base;
@@ -40,6 +57,7 @@ struct wm_adsp_compr_buf;
 
 struct wm_adsp {
 	const char *part;
+	int rev;
 	int num;
 	int type;
 	struct device *dev;
@@ -75,6 +93,8 @@ struct wm_adsp {
 
 	struct mutex pwr_lock;
 
+	unsigned int lock_regions;
+
 #ifdef CONFIG_DEBUG_FS
 	struct dentry *debugfs_root;
 	char *wmfw_file_name;
@@ -113,6 +133,10 @@ int wm_adsp1_event(struct snd_soc_dapm_widget *w,
 int wm_adsp2_early_event(struct snd_soc_dapm_widget *w,
 			 struct snd_kcontrol *kcontrol, int event,
 			 unsigned int freq);
+
+int wm_adsp2_lock(struct wm_adsp *adsp, unsigned int regions);
+irqreturn_t wm_adsp2_bus_error(struct wm_adsp *adsp);
+
 int wm_adsp2_event(struct snd_soc_dapm_widget *w,
 		   struct snd_kcontrol *kcontrol, int event);
 
diff --git a/sound/soc/dwc/Kconfig b/sound/soc/dwc/Kconfig
index c297efe43861..c6fd95fa5ca6 100644
--- a/sound/soc/dwc/Kconfig
+++ b/sound/soc/dwc/Kconfig
@@ -8,10 +8,10 @@ config SND_DESIGNWARE_I2S
 	 maximum of 8 channels each for play and record.
 
 config SND_DESIGNWARE_PCM
-	tristate "PCM PIO extension for I2S driver"
+	bool "PCM PIO extension for I2S driver"
 	depends on SND_DESIGNWARE_I2S
 	help
-	 Say Y, M or N if you want to add a custom ALSA extension that registers
+	 Say Y or N if you want to add a custom ALSA extension that registers
 	 a PCM and uses PIO to transfer data.
 
 	 This functionality is specially suited for I2S devices that don't have
diff --git a/sound/soc/dwc/Makefile b/sound/soc/dwc/Makefile
index 38f1ca31c5fa..3e24c0ff95fb 100644
--- a/sound/soc/dwc/Makefile
+++ b/sound/soc/dwc/Makefile
@@ -1,5 +1,5 @@
 # SYNOPSYS Platform Support
 obj-$(CONFIG_SND_DESIGNWARE_I2S) += designware_i2s.o
-ifdef CONFIG_SND_DESIGNWARE_PCM
-obj-$(CONFIG_SND_DESIGNWARE_I2S) += designware_pcm.o
-endif
+
+designware_i2s-y := dwc-i2s.o
+designware_i2s-$(CONFIG_SND_DESIGNWARE_PCM) += dwc-pcm.o
diff --git a/sound/soc/dwc/designware_i2s.c b/sound/soc/dwc/dwc-i2s.c
index 9c46e4112026..9c46e4112026 100644
--- a/sound/soc/dwc/designware_i2s.c
+++ b/sound/soc/dwc/dwc-i2s.c
diff --git a/sound/soc/dwc/designware_pcm.c b/sound/soc/dwc/dwc-pcm.c
index 459ec861e6b6..406fd867117b 100644
--- a/sound/soc/dwc/designware_pcm.c
+++ b/sound/soc/dwc/dwc-pcm.c
@@ -129,13 +129,11 @@ void dw_pcm_push_tx(struct dw_i2s_dev *dev)
 {
 	dw_pcm_transfer(dev, true);
 }
-EXPORT_SYMBOL_GPL(dw_pcm_push_tx);
 
 void dw_pcm_pop_rx(struct dw_i2s_dev *dev)
 {
 	dw_pcm_transfer(dev, false);
 }
-EXPORT_SYMBOL_GPL(dw_pcm_pop_rx);
 
 static int dw_pcm_open(struct snd_pcm_substream *substream)
 {
@@ -281,4 +279,3 @@ int dw_pcm_register(struct platform_device *pdev)
 {
 	return devm_snd_soc_register_platform(&pdev->dev, &dw_pcm_platform);
 }
-EXPORT_SYMBOL_GPL(dw_pcm_register);
diff --git a/sound/soc/fsl/eukrea-tlv320.c b/sound/soc/fsl/eukrea-tlv320.c
index 883087f2b092..84ef6385736c 100644
--- a/sound/soc/fsl/eukrea-tlv320.c
+++ b/sound/soc/fsl/eukrea-tlv320.c
@@ -64,7 +64,7 @@ static int eukrea_tlv320_hw_params(struct snd_pcm_substream *substream,
 	return 0;
 }
 
-static struct snd_soc_ops eukrea_tlv320_snd_ops = {
+static const struct snd_soc_ops eukrea_tlv320_snd_ops = {
 	.hw_params	= eukrea_tlv320_hw_params,
 };
 
diff --git a/sound/soc/fsl/fsl_asrc_dma.c b/sound/soc/fsl/fsl_asrc_dma.c
index dc30d780f874..282d841840b1 100644
--- a/sound/soc/fsl/fsl_asrc_dma.c
+++ b/sound/soc/fsl/fsl_asrc_dma.c
@@ -76,7 +76,7 @@ static int fsl_asrc_dma_prepare_and_submit(struct snd_pcm_substream *substream)
 			pair->dma_chan[!dir], runtime->dma_addr,
 			snd_pcm_lib_buffer_bytes(substream),
 			snd_pcm_lib_period_bytes(substream),
-			dir == OUT ? DMA_TO_DEVICE : DMA_FROM_DEVICE, flags);
+			dir == OUT ? DMA_MEM_TO_DEV : DMA_DEV_TO_MEM, flags);
 	if (!pair->desc[!dir]) {
 		dev_err(dev, "failed to prepare slave DMA for Front-End\n");
 		return -ENOMEM;
diff --git a/sound/soc/fsl/fsl_esai.c b/sound/soc/fsl/fsl_esai.c
index 38bfd46f4ad8..809a069d490b 100644
--- a/sound/soc/fsl/fsl_esai.c
+++ b/sound/soc/fsl/fsl_esai.c
@@ -19,7 +19,6 @@
 #include "fsl_esai.h"
 #include "imx-pcm.h"
 
-#define FSL_ESAI_RATES		SNDRV_PCM_RATE_8000_192000
 #define FSL_ESAI_FORMATS	(SNDRV_PCM_FMTBIT_S8 | \
 				SNDRV_PCM_FMTBIT_S16_LE | \
 				SNDRV_PCM_FMTBIT_S20_3LE | \
@@ -647,14 +646,14 @@ static struct snd_soc_dai_driver fsl_esai_dai = {
 		.stream_name = "CPU-Playback",
 		.channels_min = 1,
 		.channels_max = 12,
-		.rates = FSL_ESAI_RATES,
+		.rates = SNDRV_PCM_RATE_8000_192000,
 		.formats = FSL_ESAI_FORMATS,
 	},
 	.capture = {
 		.stream_name = "CPU-Capture",
 		.channels_min = 1,
 		.channels_max = 8,
-		.rates = FSL_ESAI_RATES,
+		.rates = SNDRV_PCM_RATE_8000_192000,
 		.formats = FSL_ESAI_FORMATS,
 	},
 	.ops = &fsl_esai_dai_ops,
diff --git a/sound/soc/fsl/fsl_ssi.c b/sound/soc/fsl/fsl_ssi.c
index fde08660b63b..173cb8496641 100644
--- a/sound/soc/fsl/fsl_ssi.c
+++ b/sound/soc/fsl/fsl_ssi.c
@@ -35,6 +35,7 @@
 #include <linux/module.h>
 #include <linux/interrupt.h>
 #include <linux/clk.h>
+#include <linux/ctype.h>
 #include <linux/device.h>
 #include <linux/delay.h>
 #include <linux/slab.h>
@@ -55,16 +56,6 @@
 #include "imx-pcm.h"
 
 /**
- * FSLSSI_I2S_RATES: sample rates supported by the I2S
- *
- * This driver currently only supports the SSI running in I2S slave mode,
- * which means the codec determines the sample rate.  Therefore, we tell
- * ALSA that we support all rates and let the codec driver decide what rates
- * are really supported.
- */
-#define FSLSSI_I2S_RATES SNDRV_PCM_RATE_CONTINUOUS
-
-/**
  * FSLSSI_I2S_FORMATS: audio formats supported by the SSI
  *
  * The SSI has a limitation in that the samples must be in the same byte
@@ -1212,14 +1203,14 @@ static struct snd_soc_dai_driver fsl_ssi_dai_template = {
 		.stream_name = "CPU-Playback",
 		.channels_min = 1,
 		.channels_max = 32,
-		.rates = FSLSSI_I2S_RATES,
+		.rates = SNDRV_PCM_RATE_CONTINUOUS,
 		.formats = FSLSSI_I2S_FORMATS,
 	},
 	.capture = {
 		.stream_name = "CPU-Capture",
 		.channels_min = 1,
 		.channels_max = 32,
-		.rates = FSLSSI_I2S_RATES,
+		.rates = SNDRV_PCM_RATE_CONTINUOUS,
 		.formats = FSLSSI_I2S_FORMATS,
 	},
 	.ops = &fsl_ssi_dai_ops,
@@ -1325,14 +1316,10 @@ static struct snd_ac97_bus_ops fsl_ssi_ac97_ops = {
  */
 static void make_lowercase(char *s)
 {
-	char *p = s;
-	char c;
-
-	while ((c = *p)) {
-		if ((c >= 'A') && (c <= 'Z'))
-			*p = c + ('a' - 'A');
-		p++;
-	}
+	if (!s)
+		return;
+	for (; *s; s++)
+		*s = tolower(*s);
 }
 
 static int fsl_ssi_imx_probe(struct platform_device *pdev,
diff --git a/sound/soc/fsl/imx-mc13783.c b/sound/soc/fsl/imx-mc13783.c
index bb0459018b45..9d19b808f634 100644
--- a/sound/soc/fsl/imx-mc13783.c
+++ b/sound/soc/fsl/imx-mc13783.c
@@ -48,7 +48,7 @@ static int imx_mc13783_hifi_hw_params(struct snd_pcm_substream *substream,
 	return snd_soc_dai_set_tdm_slot(cpu_dai, 0x3, 0x3, 2, 16);
 }
 
-static struct snd_soc_ops imx_mc13783_hifi_ops = {
+static const struct snd_soc_ops imx_mc13783_hifi_ops = {
 	.hw_params = imx_mc13783_hifi_hw_params,
 };
 
diff --git a/sound/soc/fsl/imx-pcm-dma.c b/sound/soc/fsl/imx-pcm-dma.c
index f3d3d1ffa84e..314814ddd2b0 100644
--- a/sound/soc/fsl/imx-pcm-dma.c
+++ b/sound/soc/fsl/imx-pcm-dma.c
@@ -33,48 +33,20 @@ static bool filter(struct dma_chan *chan, void *param)
 	return true;
 }
 
-static const struct snd_pcm_hardware imx_pcm_hardware = {
-	.info = SNDRV_PCM_INFO_INTERLEAVED |
-		SNDRV_PCM_INFO_BLOCK_TRANSFER |
-		SNDRV_PCM_INFO_MMAP |
-		SNDRV_PCM_INFO_MMAP_VALID |
-		SNDRV_PCM_INFO_PAUSE |
-		SNDRV_PCM_INFO_RESUME,
-	.buffer_bytes_max = IMX_DEFAULT_DMABUF_SIZE,
-	.period_bytes_min = 128,
-	.period_bytes_max = 65535, /* Limited by SDMA engine */
-	.periods_min = 2,
-	.periods_max = 255,
-	.fifo_size = 0,
-};
-
 static const struct snd_dmaengine_pcm_config imx_dmaengine_pcm_config = {
-	.pcm_hardware = &imx_pcm_hardware,
 	.prepare_slave_config = snd_dmaengine_pcm_prepare_slave_config,
 	.compat_filter_fn = filter,
-	.prealloc_buffer_size = IMX_DEFAULT_DMABUF_SIZE,
 };
 
 int imx_pcm_dma_init(struct platform_device *pdev, size_t size)
 {
 	struct snd_dmaengine_pcm_config *config;
-	struct snd_pcm_hardware *pcm_hardware;
 
 	config = devm_kzalloc(&pdev->dev,
 			sizeof(struct snd_dmaengine_pcm_config), GFP_KERNEL);
 	if (!config)
 		return -ENOMEM;
 	*config = imx_dmaengine_pcm_config;
-	if (size)
-		config->prealloc_buffer_size = size;
-
-	pcm_hardware = devm_kzalloc(&pdev->dev,
-			sizeof(struct snd_pcm_hardware), GFP_KERNEL);
-	*pcm_hardware = imx_pcm_hardware;
-	if (size)
-		pcm_hardware->buffer_bytes_max = size;
-
-	config->pcm_hardware = pcm_hardware;
 
 	return devm_snd_dmaengine_pcm_register(&pdev->dev,
 		config,
diff --git a/sound/soc/fsl/imx-pcm-fiq.c b/sound/soc/fsl/imx-pcm-fiq.c
index dac6688540dc..92410f7ca1fa 100644
--- a/sound/soc/fsl/imx-pcm-fiq.c
+++ b/sound/soc/fsl/imx-pcm-fiq.c
@@ -282,7 +282,7 @@ static int imx_pcm_new(struct snd_soc_pcm_runtime *rtd)
 	return 0;
 }
 
-static int ssi_irq = 0;
+static int ssi_irq;
 
 static int imx_pcm_fiq_new(struct snd_soc_pcm_runtime *rtd)
 {
diff --git a/sound/soc/fsl/imx-wm8962.c b/sound/soc/fsl/imx-wm8962.c
index 1b60958e2080..206b898e554c 100644
--- a/sound/soc/fsl/imx-wm8962.c
+++ b/sound/soc/fsl/imx-wm8962.c
@@ -33,14 +33,14 @@ struct imx_wm8962_data {
 	struct snd_soc_card card;
 	char codec_dai_name[DAI_NAME_SIZE];
 	char platform_name[DAI_NAME_SIZE];
-	struct clk *codec_clk;
 	unsigned int clk_frequency;
 };
 
 struct imx_priv {
 	struct platform_device *pdev;
+	int sample_rate;
+	snd_pcm_format_t sample_format;
 };
-static struct imx_priv card_priv;
 
 static const struct snd_soc_dapm_widget imx_wm8962_dapm_widgets[] = {
 	SND_SOC_DAPM_HP("Headphone Jack", NULL),
@@ -49,14 +49,14 @@ static const struct snd_soc_dapm_widget imx_wm8962_dapm_widgets[] = {
 	SND_SOC_DAPM_MIC("DMIC", NULL),
 };
 
-static int sample_rate = 44100;
-static snd_pcm_format_t sample_format = SNDRV_PCM_FORMAT_S16_LE;
-
 static int imx_hifi_hw_params(struct snd_pcm_substream *substream,
 		struct snd_pcm_hw_params *params)
 {
-	sample_rate = params_rate(params);
-	sample_format = params_format(params);
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct imx_priv *priv = snd_soc_card_get_drvdata(rtd->card);
+
+	priv->sample_rate = params_rate(params);
+	priv->sample_format = params_format(params);
 
 	return 0;
 }
@@ -71,7 +71,7 @@ static int imx_wm8962_set_bias_level(struct snd_soc_card *card,
 {
 	struct snd_soc_pcm_runtime *rtd;
 	struct snd_soc_dai *codec_dai;
-	struct imx_priv *priv = &card_priv;
+	struct imx_priv *priv = snd_soc_card_get_drvdata(card);
 	struct imx_wm8962_data *data = snd_soc_card_get_drvdata(card);
 	struct device *dev = &priv->pdev->dev;
 	unsigned int pll_out;
@@ -85,10 +85,10 @@ static int imx_wm8962_set_bias_level(struct snd_soc_card *card,
 	switch (level) {
 	case SND_SOC_BIAS_PREPARE:
 		if (dapm->bias_level == SND_SOC_BIAS_STANDBY) {
-			if (sample_format == SNDRV_PCM_FORMAT_S24_LE)
-				pll_out = sample_rate * 384;
+			if (priv->sample_format == SNDRV_PCM_FORMAT_S24_LE)
+				pll_out = priv->sample_rate * 384;
 			else
-				pll_out = sample_rate * 256;
+				pll_out = priv->sample_rate * 256;
 
 			ret = snd_soc_dai_set_pll(codec_dai, WM8962_FLL,
 					WM8962_FLL_MCLK, data->clk_frequency,
@@ -140,7 +140,7 @@ static int imx_wm8962_late_probe(struct snd_soc_card *card)
 {
 	struct snd_soc_pcm_runtime *rtd;
 	struct snd_soc_dai *codec_dai;
-	struct imx_priv *priv = &card_priv;
+	struct imx_priv *priv = snd_soc_card_get_drvdata(card);
 	struct imx_wm8962_data *data = snd_soc_card_get_drvdata(card);
 	struct device *dev = &priv->pdev->dev;
 	int ret;
@@ -160,13 +160,20 @@ static int imx_wm8962_probe(struct platform_device *pdev)
 	struct device_node *np = pdev->dev.of_node;
 	struct device_node *ssi_np, *codec_np;
 	struct platform_device *ssi_pdev;
-	struct imx_priv *priv = &card_priv;
 	struct i2c_client *codec_dev;
 	struct imx_wm8962_data *data;
+	struct imx_priv *priv;
+	struct clk *codec_clk;
 	int int_port, ext_port;
 	int ret;
 
+	priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
+
 	priv->pdev = pdev;
+	priv->sample_rate = 44100;
+	priv->sample_format = SNDRV_PCM_FORMAT_S16_LE;
 
 	ret = of_property_read_u32(np, "mux-int-port", &int_port);
 	if (ret) {
@@ -231,19 +238,15 @@ static int imx_wm8962_probe(struct platform_device *pdev)
 		goto fail;
 	}
 
-	data->codec_clk = devm_clk_get(&codec_dev->dev, NULL);
-	if (IS_ERR(data->codec_clk)) {
-		ret = PTR_ERR(data->codec_clk);
+	codec_clk = clk_get(&codec_dev->dev, NULL);
+	if (IS_ERR(codec_clk)) {
+		ret = PTR_ERR(codec_clk);
 		dev_err(&codec_dev->dev, "failed to get codec clk: %d\n", ret);
 		goto fail;
 	}
 
-	data->clk_frequency = clk_get_rate(data->codec_clk);
-	ret = clk_prepare_enable(data->codec_clk);
-	if (ret) {
-		dev_err(&codec_dev->dev, "failed to enable codec clk: %d\n", ret);
-		goto fail;
-	}
+	data->clk_frequency = clk_get_rate(codec_clk);
+	clk_put(codec_clk);
 
 	data->dai.name = "HiFi";
 	data->dai.stream_name = "HiFi";
@@ -258,10 +261,10 @@ static int imx_wm8962_probe(struct platform_device *pdev)
 	data->card.dev = &pdev->dev;
 	ret = snd_soc_of_parse_card_name(&data->card, "model");
 	if (ret)
-		goto clk_fail;
+		goto fail;
 	ret = snd_soc_of_parse_audio_routing(&data->card, "audio-routing");
 	if (ret)
-		goto clk_fail;
+		goto fail;
 	data->card.num_links = 1;
 	data->card.owner = THIS_MODULE;
 	data->card.dai_link = &data->dai;
@@ -277,16 +280,9 @@ static int imx_wm8962_probe(struct platform_device *pdev)
 	ret = devm_snd_soc_register_card(&pdev->dev, &data->card);
 	if (ret) {
 		dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n", ret);
-		goto clk_fail;
+		goto fail;
 	}
 
-	of_node_put(ssi_np);
-	of_node_put(codec_np);
-
-	return 0;
-
-clk_fail:
-	clk_disable_unprepare(data->codec_clk);
 fail:
 	of_node_put(ssi_np);
 	of_node_put(codec_np);
@@ -294,17 +290,6 @@ fail:
 	return ret;
 }
 
-static int imx_wm8962_remove(struct platform_device *pdev)
-{
-	struct snd_soc_card *card = platform_get_drvdata(pdev);
-	struct imx_wm8962_data *data = snd_soc_card_get_drvdata(card);
-
-	if (!IS_ERR(data->codec_clk))
-		clk_disable_unprepare(data->codec_clk);
-
-	return 0;
-}
-
 static const struct of_device_id imx_wm8962_dt_ids[] = {
 	{ .compatible = "fsl,imx-audio-wm8962", },
 	{ /* sentinel */ }
@@ -318,7 +303,6 @@ static struct platform_driver imx_wm8962_driver = {
 		.of_match_table = imx_wm8962_dt_ids,
 	},
 	.probe = imx_wm8962_probe,
-	.remove = imx_wm8962_remove,
 };
 module_platform_driver(imx_wm8962_driver);
 
diff --git a/sound/soc/fsl/mpc8610_hpcd.c b/sound/soc/fsl/mpc8610_hpcd.c
index ddf49f30b23f..a639b52c16f6 100644
--- a/sound/soc/fsl/mpc8610_hpcd.c
+++ b/sound/soc/fsl/mpc8610_hpcd.c
@@ -174,7 +174,7 @@ static int mpc8610_hpcd_machine_remove(struct snd_soc_card *card)
 /**
  * mpc8610_hpcd_ops: ASoC machine driver operations
  */
-static struct snd_soc_ops mpc8610_hpcd_ops = {
+static const struct snd_soc_ops mpc8610_hpcd_ops = {
 	.startup = mpc8610_hpcd_startup,
 };
 
diff --git a/sound/soc/fsl/mx27vis-aic32x4.c b/sound/soc/fsl/mx27vis-aic32x4.c
index 198eeb3f3f7a..d7ec3d20065c 100644
--- a/sound/soc/fsl/mx27vis-aic32x4.c
+++ b/sound/soc/fsl/mx27vis-aic32x4.c
@@ -73,7 +73,7 @@ static int mx27vis_aic32x4_hw_params(struct snd_pcm_substream *substream,
 	return 0;
 }
 
-static struct snd_soc_ops mx27vis_aic32x4_snd_ops = {
+static const struct snd_soc_ops mx27vis_aic32x4_snd_ops = {
 	.hw_params	= mx27vis_aic32x4_hw_params,
 };
 
diff --git a/sound/soc/fsl/p1022_ds.c b/sound/soc/fsl/p1022_ds.c
index a1f780ecadf5..41c623c55c16 100644
--- a/sound/soc/fsl/p1022_ds.c
+++ b/sound/soc/fsl/p1022_ds.c
@@ -184,7 +184,7 @@ static int p1022_ds_machine_remove(struct snd_soc_card *card)
 /**
  * p1022_ds_ops: ASoC machine driver operations
  */
-static struct snd_soc_ops p1022_ds_ops = {
+static const struct snd_soc_ops p1022_ds_ops = {
 	.startup = p1022_ds_startup,
 };
 
diff --git a/sound/soc/fsl/p1022_rdk.c b/sound/soc/fsl/p1022_rdk.c
index d4d88a8cb9c0..4afbdd610bfa 100644
--- a/sound/soc/fsl/p1022_rdk.c
+++ b/sound/soc/fsl/p1022_rdk.c
@@ -188,7 +188,7 @@ static int p1022_rdk_machine_remove(struct snd_soc_card *card)
 /**
  * p1022_rdk_ops: ASoC machine driver operations
  */
-static struct snd_soc_ops p1022_rdk_ops = {
+static const struct snd_soc_ops p1022_rdk_ops = {
 	.startup = p1022_rdk_startup,
 };
 
diff --git a/sound/soc/fsl/phycore-ac97.c b/sound/soc/fsl/phycore-ac97.c
index ae403c29688f..66fb6c4614d2 100644
--- a/sound/soc/fsl/phycore-ac97.c
+++ b/sound/soc/fsl/phycore-ac97.c
@@ -23,7 +23,7 @@
 
 static struct snd_soc_card imx_phycore;
 
-static struct snd_soc_ops imx_phycore_hifi_ops = {
+static const struct snd_soc_ops imx_phycore_hifi_ops = {
 };
 
 static struct snd_soc_dai_link imx_phycore_dai_ac97[] = {
diff --git a/sound/soc/fsl/wm1133-ev1.c b/sound/soc/fsl/wm1133-ev1.c
index b454972dce35..cdaf16367b47 100644
--- a/sound/soc/fsl/wm1133-ev1.c
+++ b/sound/soc/fsl/wm1133-ev1.c
@@ -139,7 +139,7 @@ static int wm1133_ev1_hw_params(struct snd_pcm_substream *substream,
 	return 0;
 }
 
-static struct snd_soc_ops wm1133_ev1_ops = {
+static const struct snd_soc_ops wm1133_ev1_ops = {
 	.hw_params = wm1133_ev1_hw_params,
 };
 
diff --git a/sound/soc/generic/simple-card.c b/sound/soc/generic/simple-card.c
index 85b4f1806514..2c9dedab5184 100644
--- a/sound/soc/generic/simple-card.c
+++ b/sound/soc/generic/simple-card.c
@@ -40,9 +40,10 @@ struct simple_card_data {
 	struct snd_soc_dai_link *dai_link;
 };
 
-#define simple_priv_to_dev(priv) ((priv)->snd_card.dev)
-#define simple_priv_to_link(priv, i) ((priv)->snd_card.dai_link + (i))
+#define simple_priv_to_card(priv) (&(priv)->snd_card)
 #define simple_priv_to_props(priv, i) ((priv)->dai_props + (i))
+#define simple_priv_to_dev(priv) (simple_priv_to_card(priv)->dev)
+#define simple_priv_to_link(priv, i) (simple_priv_to_card(priv)->dai_link + (i))
 
 #define DAI	"sound-dai"
 #define CELL	"#sound-dai-cells"
@@ -323,6 +324,7 @@ static int asoc_simple_card_parse_aux_devs(struct device_node *node,
 {
 	struct device *dev = simple_priv_to_dev(priv);
 	struct device_node *aux_node;
+	struct snd_soc_card *card = simple_priv_to_card(priv);
 	int i, n, len;
 
 	if (!of_find_property(node, PREFIX "aux-devs", &len))
@@ -332,19 +334,19 @@ static int asoc_simple_card_parse_aux_devs(struct device_node *node,
 	if (n <= 0)
 		return -EINVAL;
 
-	priv->snd_card.aux_dev = devm_kzalloc(dev,
-			n * sizeof(*priv->snd_card.aux_dev), GFP_KERNEL);
-	if (!priv->snd_card.aux_dev)
+	card->aux_dev = devm_kzalloc(dev,
+			n * sizeof(*card->aux_dev), GFP_KERNEL);
+	if (!card->aux_dev)
 		return -ENOMEM;
 
 	for (i = 0; i < n; i++) {
 		aux_node = of_parse_phandle(node, PREFIX "aux-devs", i);
 		if (!aux_node)
 			return -EINVAL;
-		priv->snd_card.aux_dev[i].codec_of_node = aux_node;
+		card->aux_dev[i].codec_of_node = aux_node;
 	}
 
-	priv->snd_card.num_aux_devs = n;
+	card->num_aux_devs = n;
 	return 0;
 }
 
@@ -352,6 +354,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);
+	struct snd_soc_card *card = simple_priv_to_card(priv);
 	struct device_node *dai_link;
 	int ret;
 
@@ -362,7 +365,7 @@ static int asoc_simple_card_parse_of(struct device_node *node,
 
 	/* The off-codec widgets */
 	if (of_property_read_bool(node, PREFIX "widgets")) {
-		ret = snd_soc_of_parse_audio_simple_widgets(&priv->snd_card,
+		ret = snd_soc_of_parse_audio_simple_widgets(card,
 					PREFIX "widgets");
 		if (ret)
 			goto card_parse_end;
@@ -370,7 +373,7 @@ static int asoc_simple_card_parse_of(struct device_node *node,
 
 	/* DAPM routes */
 	if (of_property_read_bool(node, PREFIX "routing")) {
-		ret = snd_soc_of_parse_audio_routing(&priv->snd_card,
+		ret = snd_soc_of_parse_audio_routing(card,
 					PREFIX "routing");
 		if (ret)
 			goto card_parse_end;
@@ -401,7 +404,7 @@ static int asoc_simple_card_parse_of(struct device_node *node,
 			goto card_parse_end;
 	}
 
-	ret = asoc_simple_card_parse_card_name(&priv->snd_card, PREFIX);
+	ret = asoc_simple_card_parse_card_name(card, PREFIX);
 	if (ret < 0)
 		goto card_parse_end;
 
@@ -418,8 +421,9 @@ static int asoc_simple_card_probe(struct platform_device *pdev)
 	struct simple_card_data *priv;
 	struct snd_soc_dai_link *dai_link;
 	struct simple_dai_props *dai_props;
-	struct device_node *np = pdev->dev.of_node;
 	struct device *dev = &pdev->dev;
+	struct device_node *np = dev->of_node;
+	struct snd_soc_card *card;
 	int num, ret;
 
 	/* Get the number of DAI links */
@@ -442,10 +446,11 @@ static int asoc_simple_card_probe(struct platform_device *pdev)
 	priv->dai_link			= dai_link;
 
 	/* Init snd_soc_card */
-	priv->snd_card.owner		= THIS_MODULE;
-	priv->snd_card.dev		= dev;
-	priv->snd_card.dai_link		= priv->dai_link;
-	priv->snd_card.num_links	= num;
+	card = simple_priv_to_card(priv);
+	card->owner		= THIS_MODULE;
+	card->dev		= dev;
+	card->dai_link		= priv->dai_link;
+	card->num_links		= num;
 
 	if (np && of_device_is_available(np)) {
 
@@ -474,7 +479,7 @@ static int asoc_simple_card_probe(struct platform_device *pdev)
 			return -EINVAL;
 		}
 
-		priv->snd_card.name	= (cinfo->card) ? cinfo->card : cinfo->name;
+		card->name		= (cinfo->card) ? cinfo->card : cinfo->name;
 		dai_link->name		= cinfo->name;
 		dai_link->stream_name	= cinfo->name;
 		dai_link->platform_name	= cinfo->platform;
@@ -489,13 +494,13 @@ static int asoc_simple_card_probe(struct platform_device *pdev)
 					sizeof(priv->dai_props->codec_dai));
 	}
 
-	snd_soc_card_set_drvdata(&priv->snd_card, priv);
+	snd_soc_card_set_drvdata(card, priv);
 
-	ret = devm_snd_soc_register_card(&pdev->dev, &priv->snd_card);
+	ret = devm_snd_soc_register_card(dev, card);
 	if (ret >= 0)
 		return ret;
 err:
-	asoc_simple_card_clean_reference(&priv->snd_card);
+	asoc_simple_card_clean_reference(card);
 
 	return ret;
 }
diff --git a/sound/soc/generic/simple-scu-card.c b/sound/soc/generic/simple-scu-card.c
index 308ff4c11a8d..dcbcab230d1b 100644
--- a/sound/soc/generic/simple-scu-card.c
+++ b/sound/soc/generic/simple-scu-card.c
@@ -31,9 +31,10 @@ struct simple_card_data {
 	u32 convert_channels;
 };
 
-#define simple_priv_to_dev(priv) ((priv)->snd_card.dev)
-#define simple_priv_to_link(priv, i) ((priv)->snd_card.dai_link + (i))
+#define simple_priv_to_card(priv) (&(priv)->snd_card)
 #define simple_priv_to_props(priv, i) ((priv)->dai_props + (i))
+#define simple_priv_to_dev(priv) (simple_priv_to_card(priv)->dev)
+#define simple_priv_to_link(priv, i) (simple_priv_to_card(priv)->dai_link + (i))
 
 #define DAI	"sound-dai"
 #define CELL	"#sound-dai-cells"
@@ -109,6 +110,7 @@ static int asoc_simple_card_dai_link_of(struct device_node *np,
 	struct device *dev = simple_priv_to_dev(priv);
 	struct snd_soc_dai_link *dai_link = simple_priv_to_link(priv, idx);
 	struct asoc_simple_dai *dai_props = simple_priv_to_props(priv, idx);
+	struct snd_soc_card *card = simple_priv_to_card(priv);
 	int ret;
 
 	if (is_fe) {
@@ -163,7 +165,7 @@ static int asoc_simple_card_dai_link_of(struct device_node *np,
 		if (ret < 0)
 			return ret;
 
-		snd_soc_of_parse_audio_prefix(&priv->snd_card,
+		snd_soc_of_parse_audio_prefix(card,
 					      &priv->codec_conf,
 					      dai_link->codec_of_node,
 					      PREFIX "prefix");
@@ -201,6 +203,7 @@ static int asoc_simple_card_parse_of(struct device_node *node,
 {
 	struct device *dev = simple_priv_to_dev(priv);
 	struct device_node *np;
+	struct snd_soc_card *card = simple_priv_to_card(priv);
 	unsigned int daifmt = 0;
 	bool is_fe;
 	int ret, i;
@@ -208,7 +211,7 @@ static int asoc_simple_card_parse_of(struct device_node *node,
 	if (!node)
 		return -EINVAL;
 
-	ret = snd_soc_of_parse_audio_routing(&priv->snd_card, PREFIX "routing");
+	ret = snd_soc_of_parse_audio_routing(card, PREFIX "routing");
 	if (ret < 0)
 		return ret;
 
@@ -239,12 +242,12 @@ static int asoc_simple_card_parse_of(struct device_node *node,
 		i++;
 	}
 
-	ret = asoc_simple_card_parse_card_name(&priv->snd_card, PREFIX);
+	ret = asoc_simple_card_parse_card_name(card, PREFIX);
 	if (ret < 0)
 		return ret;
 
 	dev_dbg(dev, "New card: %s\n",
-		priv->snd_card.name ? priv->snd_card.name : "");
+		card->name ? card->name : "");
 	dev_dbg(dev, "convert_rate     %d\n", priv->convert_rate);
 	dev_dbg(dev, "convert_channels %d\n", priv->convert_channels);
 
@@ -256,8 +259,9 @@ static int asoc_simple_card_probe(struct platform_device *pdev)
 	struct simple_card_data *priv;
 	struct snd_soc_dai_link *dai_link;
 	struct asoc_simple_dai *dai_props;
+	struct snd_soc_card *card;
 	struct device *dev = &pdev->dev;
-	struct device_node *np = pdev->dev.of_node;
+	struct device_node *np = dev->of_node;
 	int num, ret;
 
 	/* Allocate the private data */
@@ -276,12 +280,13 @@ static int asoc_simple_card_probe(struct platform_device *pdev)
 	priv->dai_link				= dai_link;
 
 	/* Init snd_soc_card */
-	priv->snd_card.owner			= THIS_MODULE;
-	priv->snd_card.dev			= dev;
-	priv->snd_card.dai_link			= priv->dai_link;
-	priv->snd_card.num_links		= num;
-	priv->snd_card.codec_conf		= &priv->codec_conf;
-	priv->snd_card.num_configs		= 1;
+	card = simple_priv_to_card(priv);
+	card->owner		= THIS_MODULE;
+	card->dev		= dev;
+	card->dai_link		= priv->dai_link;
+	card->num_links		= num;
+	card->codec_conf	= &priv->codec_conf;
+	card->num_configs	= 1;
 
 	ret = asoc_simple_card_parse_of(np, priv);
 	if (ret < 0) {
@@ -290,13 +295,13 @@ static int asoc_simple_card_probe(struct platform_device *pdev)
 		goto err;
 	}
 
-	snd_soc_card_set_drvdata(&priv->snd_card, priv);
+	snd_soc_card_set_drvdata(card, priv);
 
-	ret = devm_snd_soc_register_card(&pdev->dev, &priv->snd_card);
+	ret = devm_snd_soc_register_card(dev, card);
 	if (ret >= 0)
 		return ret;
 err:
-	asoc_simple_card_clean_reference(&priv->snd_card);
+	asoc_simple_card_clean_reference(card);
 
 	return ret;
 }
diff --git a/sound/soc/hisilicon/Kconfig b/sound/soc/hisilicon/Kconfig
new file mode 100644
index 000000000000..4356d5a1d338
--- /dev/null
+++ b/sound/soc/hisilicon/Kconfig
@@ -0,0 +1,5 @@
+config SND_I2S_HI6210_I2S
+	tristate "Hisilicon I2S controller"
+	select SND_SOC_GENERIC_DMAENGINE_PCM
+	help
+	  Hisilicon I2S
diff --git a/sound/soc/hisilicon/Makefile b/sound/soc/hisilicon/Makefile
new file mode 100644
index 000000000000..e8095e2af91a
--- /dev/null
+++ b/sound/soc/hisilicon/Makefile
@@ -0,0 +1 @@
+obj-$(CONFIG_SND_I2S_HI6210_I2S) += hi6210-i2s.o
diff --git a/sound/soc/hisilicon/hi6210-i2s.c b/sound/soc/hisilicon/hi6210-i2s.c
new file mode 100644
index 000000000000..45163e5202f5
--- /dev/null
+++ b/sound/soc/hisilicon/hi6210-i2s.c
@@ -0,0 +1,618 @@
+/*
+ * linux/sound/soc/m8m/hi6210_i2s.c - I2S IP driver
+ *
+ * Copyright (C) 2015 Linaro, Ltd
+ * Author: Andy Green <andy.green@linaro.org>
+ *
+ * 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 driver only deals with S2 interface (BT)
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/delay.h>
+#include <linux/clk.h>
+#include <linux/jiffies.h>
+#include <linux/io.h>
+#include <linux/gpio.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/dmaengine_pcm.h>
+#include <sound/initval.h>
+#include <sound/soc.h>
+#include <linux/interrupt.h>
+#include <linux/reset.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+#include <linux/mfd/syscon.h>
+#include <linux/reset-controller.h>
+#include <linux/clk.h>
+
+#include "hi6210-i2s.h"
+
+struct hi6210_i2s {
+	struct device *dev;
+	struct reset_control *rc;
+	struct clk *clk[8];
+	int clocks;
+	struct snd_soc_dai_driver dai;
+	void __iomem *base;
+	struct regmap *sysctrl;
+	phys_addr_t base_phys;
+	struct snd_dmaengine_dai_dma_data dma_data[2];
+	int clk_rate;
+	spinlock_t lock;
+	int rate;
+	int format;
+	u8 bits;
+	u8 channels;
+	u8 id;
+	u8 channel_length;
+	u8 use;
+	u32 master:1;
+	u32 status:1;
+};
+
+#define SC_PERIPH_CLKEN1	0x210
+#define SC_PERIPH_CLKDIS1	0x214
+
+#define SC_PERIPH_CLKEN3	0x230
+#define SC_PERIPH_CLKDIS3	0x234
+
+#define SC_PERIPH_CLKEN12	0x270
+#define SC_PERIPH_CLKDIS12	0x274
+
+#define SC_PERIPH_RSTEN1	0x310
+#define SC_PERIPH_RSTDIS1	0x314
+#define SC_PERIPH_RSTSTAT1	0x318
+
+#define SC_PERIPH_RSTEN2	0x320
+#define SC_PERIPH_RSTDIS2	0x324
+#define SC_PERIPH_RSTSTAT2	0x328
+
+#define SOC_PMCTRL_BBPPLLALIAS	0x48
+
+enum {
+	CLK_DACODEC,
+	CLK_I2S_BASE,
+};
+
+static inline void hi6210_write_reg(struct hi6210_i2s *i2s, int reg, u32 val)
+{
+	writel(val, i2s->base + reg);
+}
+
+static inline u32 hi6210_read_reg(struct hi6210_i2s *i2s, int reg)
+{
+	return readl(i2s->base + reg);
+}
+
+int hi6210_i2s_startup(struct snd_pcm_substream *substream,
+		     struct snd_soc_dai *cpu_dai)
+{
+	struct hi6210_i2s *i2s = dev_get_drvdata(cpu_dai->dev);
+	int ret, n;
+	u32 val;
+
+	/* deassert reset on ABB */
+	regmap_read(i2s->sysctrl, SC_PERIPH_RSTSTAT2, &val);
+	if (val & BIT(4))
+		regmap_write(i2s->sysctrl, SC_PERIPH_RSTDIS2, BIT(4));
+
+	for (n = 0; n < i2s->clocks; n++) {
+		ret = clk_prepare_enable(i2s->clk[n]);
+		if (ret) {
+			while (n--)
+				clk_disable_unprepare(i2s->clk[n]);
+			return ret;
+		}
+	}
+
+	ret = clk_set_rate(i2s->clk[CLK_I2S_BASE], 49152000);
+	if (ret) {
+		dev_err(i2s->dev, "%s: setting 49.152MHz base rate failed %d\n",
+			__func__, ret);
+		return ret;
+	}
+
+	/* enable clock before frequency division */
+	regmap_write(i2s->sysctrl, SC_PERIPH_CLKEN12, BIT(9));
+
+	/* enable codec working clock / == "codec bus clock" */
+	regmap_write(i2s->sysctrl, SC_PERIPH_CLKEN1, BIT(5));
+
+	/* deassert reset on codec / interface clock / working clock */
+	regmap_write(i2s->sysctrl, SC_PERIPH_RSTEN1, BIT(5));
+	regmap_write(i2s->sysctrl, SC_PERIPH_RSTDIS1, BIT(5));
+
+	/* not interested in i2s irqs */
+	val = hi6210_read_reg(i2s, HII2S_CODEC_IRQ_MASK);
+	val |= 0x3f;
+	hi6210_write_reg(i2s, HII2S_CODEC_IRQ_MASK, val);
+
+
+	/* reset the stereo downlink fifo */
+	val = hi6210_read_reg(i2s, HII2S_APB_AFIFO_CFG_1);
+	val |= (BIT(5) | BIT(4));
+	hi6210_write_reg(i2s, HII2S_APB_AFIFO_CFG_1, val);
+
+	val = hi6210_read_reg(i2s, HII2S_APB_AFIFO_CFG_1);
+	val &= ~(BIT(5) | BIT(4));
+	hi6210_write_reg(i2s, HII2S_APB_AFIFO_CFG_1, val);
+
+
+	val = hi6210_read_reg(i2s, HII2S_SW_RST_N);
+	val &= ~(HII2S_SW_RST_N__ST_DL_WORDLEN_MASK <<
+			HII2S_SW_RST_N__ST_DL_WORDLEN_SHIFT);
+	val |= (HII2S_BITS_16 << HII2S_SW_RST_N__ST_DL_WORDLEN_SHIFT);
+	hi6210_write_reg(i2s, HII2S_SW_RST_N, val);
+
+	val = hi6210_read_reg(i2s, HII2S_MISC_CFG);
+	/* mux 11/12 = APB not i2s */
+	val &= ~HII2S_MISC_CFG__ST_DL_TEST_SEL;
+	/* BT R ch  0 = mixer op of DACR ch */
+	val &= ~HII2S_MISC_CFG__S2_DOUT_RIGHT_SEL;
+	val &= ~HII2S_MISC_CFG__S2_DOUT_TEST_SEL;
+
+	val |= HII2S_MISC_CFG__S2_DOUT_RIGHT_SEL;
+	/* BT L ch = 1 = mux 7 = "mixer output of DACL */
+	val |= HII2S_MISC_CFG__S2_DOUT_TEST_SEL;
+	hi6210_write_reg(i2s, HII2S_MISC_CFG, val);
+
+	val = hi6210_read_reg(i2s, HII2S_SW_RST_N);
+	val |= HII2S_SW_RST_N__SW_RST_N;
+	hi6210_write_reg(i2s, HII2S_SW_RST_N, val);
+
+	return 0;
+}
+void hi6210_i2s_shutdown(struct snd_pcm_substream *substream,
+		       struct snd_soc_dai *cpu_dai)
+{
+	struct hi6210_i2s *i2s = dev_get_drvdata(cpu_dai->dev);
+	int n;
+
+	for (n = 0; n < i2s->clocks; n++)
+		clk_disable_unprepare(i2s->clk[n]);
+
+	regmap_write(i2s->sysctrl, SC_PERIPH_RSTEN1, BIT(5));
+}
+
+static void hi6210_i2s_txctrl(struct snd_soc_dai *cpu_dai, int on)
+{
+	struct hi6210_i2s *i2s = dev_get_drvdata(cpu_dai->dev);
+	u32 val;
+
+	spin_lock(&i2s->lock);
+	if (on) {
+		/* enable S2 TX */
+		val = hi6210_read_reg(i2s, HII2S_I2S_CFG);
+		val |= HII2S_I2S_CFG__S2_IF_TX_EN;
+		hi6210_write_reg(i2s, HII2S_I2S_CFG, val);
+	} else {
+		/* disable S2 TX */
+		val = hi6210_read_reg(i2s, HII2S_I2S_CFG);
+		val &= ~HII2S_I2S_CFG__S2_IF_TX_EN;
+		hi6210_write_reg(i2s, HII2S_I2S_CFG, val);
+	}
+	spin_unlock(&i2s->lock);
+}
+
+static void hi6210_i2s_rxctrl(struct snd_soc_dai *cpu_dai, int on)
+{
+	struct hi6210_i2s *i2s = dev_get_drvdata(cpu_dai->dev);
+	u32 val;
+
+	spin_lock(&i2s->lock);
+	if (on) {
+		val = hi6210_read_reg(i2s, HII2S_I2S_CFG);
+		val |= HII2S_I2S_CFG__S2_IF_RX_EN;
+		hi6210_write_reg(i2s, HII2S_I2S_CFG, val);
+	} else {
+		val = hi6210_read_reg(i2s, HII2S_I2S_CFG);
+		val &= ~HII2S_I2S_CFG__S2_IF_RX_EN;
+		hi6210_write_reg(i2s, HII2S_I2S_CFG, val);
+	}
+	spin_unlock(&i2s->lock);
+}
+
+static int hi6210_i2s_set_fmt(struct snd_soc_dai *cpu_dai, unsigned int fmt)
+{
+	struct hi6210_i2s *i2s = dev_get_drvdata(cpu_dai->dev);
+
+	/*
+	 * We don't actually set the hardware until the hw_params
+	 * call, but we need to validate the user input here.
+	 */
+	switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+	case SND_SOC_DAIFMT_CBM_CFM:
+	case SND_SOC_DAIFMT_CBS_CFS:
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+	case SND_SOC_DAIFMT_I2S:
+	case SND_SOC_DAIFMT_LEFT_J:
+	case SND_SOC_DAIFMT_RIGHT_J:
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	i2s->format = fmt;
+	i2s->master = (i2s->format & SND_SOC_DAIFMT_MASTER_MASK) ==
+		      SND_SOC_DAIFMT_CBS_CFS;
+
+	return 0;
+}
+
+static int hi6210_i2s_hw_params(struct snd_pcm_substream *substream,
+			    struct snd_pcm_hw_params *params,
+			    struct snd_soc_dai *cpu_dai)
+{
+	struct hi6210_i2s *i2s = dev_get_drvdata(cpu_dai->dev);
+	u32 bits = 0, rate = 0, signed_data = 0, fmt = 0;
+	u32 val;
+	struct snd_dmaengine_dai_dma_data *dma_data;
+
+	switch (params_format(params)) {
+	case SNDRV_PCM_FORMAT_U16_LE:
+		signed_data = HII2S_I2S_CFG__S2_CODEC_DATA_FORMAT;
+		/* fallthru */
+	case SNDRV_PCM_FORMAT_S16_LE:
+		bits = HII2S_BITS_16;
+		break;
+	case SNDRV_PCM_FORMAT_U24_LE:
+		signed_data = HII2S_I2S_CFG__S2_CODEC_DATA_FORMAT;
+		/* fallthru */
+	case SNDRV_PCM_FORMAT_S24_LE:
+		bits = HII2S_BITS_24;
+		break;
+	default:
+		dev_err(cpu_dai->dev, "Bad format\n");
+		return -EINVAL;
+	}
+
+
+	switch (params_rate(params)) {
+	case 8000:
+		rate = HII2S_FS_RATE_8KHZ;
+		break;
+	case 16000:
+		rate = HII2S_FS_RATE_16KHZ;
+		break;
+	case 32000:
+		rate = HII2S_FS_RATE_32KHZ;
+		break;
+	case 48000:
+		rate = HII2S_FS_RATE_48KHZ;
+		break;
+	case 96000:
+		rate = HII2S_FS_RATE_96KHZ;
+		break;
+	case 192000:
+		rate = HII2S_FS_RATE_192KHZ;
+		break;
+	default:
+		dev_err(cpu_dai->dev, "Bad rate: %d\n", params_rate(params));
+		return -EINVAL;
+	}
+
+	if (!(params_channels(params))) {
+		dev_err(cpu_dai->dev, "Bad channels\n");
+		return -EINVAL;
+	}
+
+	dma_data = snd_soc_dai_get_dma_data(cpu_dai, substream);
+
+	switch (bits) {
+	case HII2S_BITS_24:
+		i2s->bits = 32;
+		dma_data->addr_width = 3;
+		break;
+	default:
+		i2s->bits = 16;
+		dma_data->addr_width = 2;
+		break;
+	}
+	i2s->rate = params_rate(params);
+	i2s->channels = params_channels(params);
+	i2s->channel_length = i2s->channels * i2s->bits;
+
+	val = hi6210_read_reg(i2s, HII2S_ST_DL_FIFO_TH_CFG);
+	val &= ~((HII2S_ST_DL_FIFO_TH_CFG__ST_DL_R_AEMPTY_MASK <<
+			HII2S_ST_DL_FIFO_TH_CFG__ST_DL_R_AEMPTY_SHIFT) |
+		(HII2S_ST_DL_FIFO_TH_CFG__ST_DL_R_AFULL_MASK <<
+			HII2S_ST_DL_FIFO_TH_CFG__ST_DL_R_AFULL_SHIFT) |
+		(HII2S_ST_DL_FIFO_TH_CFG__ST_DL_L_AEMPTY_MASK <<
+			HII2S_ST_DL_FIFO_TH_CFG__ST_DL_L_AEMPTY_SHIFT) |
+		(HII2S_ST_DL_FIFO_TH_CFG__ST_DL_L_AFULL_MASK <<
+			HII2S_ST_DL_FIFO_TH_CFG__ST_DL_L_AFULL_SHIFT));
+	val |= ((16 << HII2S_ST_DL_FIFO_TH_CFG__ST_DL_R_AEMPTY_SHIFT) |
+		(30 << HII2S_ST_DL_FIFO_TH_CFG__ST_DL_R_AFULL_SHIFT) |
+		(16 << HII2S_ST_DL_FIFO_TH_CFG__ST_DL_L_AEMPTY_SHIFT) |
+		(30 << HII2S_ST_DL_FIFO_TH_CFG__ST_DL_L_AFULL_SHIFT));
+	hi6210_write_reg(i2s, HII2S_ST_DL_FIFO_TH_CFG, val);
+
+
+	val = hi6210_read_reg(i2s, HII2S_IF_CLK_EN_CFG);
+	val |= (BIT(19) | BIT(18) | BIT(17) |
+		HII2S_IF_CLK_EN_CFG__S2_IF_CLK_EN |
+		HII2S_IF_CLK_EN_CFG__S2_OL_MIXER_EN |
+		HII2S_IF_CLK_EN_CFG__S2_OL_SRC_EN |
+		HII2S_IF_CLK_EN_CFG__ST_DL_R_EN |
+		HII2S_IF_CLK_EN_CFG__ST_DL_L_EN);
+	hi6210_write_reg(i2s, HII2S_IF_CLK_EN_CFG, val);
+
+
+	val = hi6210_read_reg(i2s, HII2S_DIG_FILTER_CLK_EN_CFG);
+	val &= ~(HII2S_DIG_FILTER_CLK_EN_CFG__DACR_SDM_EN |
+		 HII2S_DIG_FILTER_CLK_EN_CFG__DACR_HBF2I_EN |
+		 HII2S_DIG_FILTER_CLK_EN_CFG__DACR_AGC_EN |
+		 HII2S_DIG_FILTER_CLK_EN_CFG__DACL_SDM_EN |
+		 HII2S_DIG_FILTER_CLK_EN_CFG__DACL_HBF2I_EN |
+		 HII2S_DIG_FILTER_CLK_EN_CFG__DACL_AGC_EN);
+	val |= (HII2S_DIG_FILTER_CLK_EN_CFG__DACR_MIXER_EN |
+		HII2S_DIG_FILTER_CLK_EN_CFG__DACL_MIXER_EN);
+	hi6210_write_reg(i2s, HII2S_DIG_FILTER_CLK_EN_CFG, val);
+
+
+	val = hi6210_read_reg(i2s, HII2S_DIG_FILTER_MODULE_CFG);
+	val &= ~(HII2S_DIG_FILTER_MODULE_CFG__DACR_MIXER_IN2_MUTE |
+		 HII2S_DIG_FILTER_MODULE_CFG__DACL_MIXER_IN2_MUTE);
+	hi6210_write_reg(i2s, HII2S_DIG_FILTER_MODULE_CFG, val);
+
+	val = hi6210_read_reg(i2s, HII2S_MUX_TOP_MODULE_CFG);
+	val &= ~(HII2S_MUX_TOP_MODULE_CFG__S2_OL_MIXER_IN1_MUTE |
+		 HII2S_MUX_TOP_MODULE_CFG__S2_OL_MIXER_IN2_MUTE |
+		 HII2S_MUX_TOP_MODULE_CFG__VOICE_DLINK_MIXER_IN1_MUTE |
+		 HII2S_MUX_TOP_MODULE_CFG__VOICE_DLINK_MIXER_IN2_MUTE);
+	hi6210_write_reg(i2s, HII2S_MUX_TOP_MODULE_CFG, val);
+
+
+	switch (i2s->format & SND_SOC_DAIFMT_MASTER_MASK) {
+	case SND_SOC_DAIFMT_CBM_CFM:
+		i2s->master = false;
+		val = hi6210_read_reg(i2s, HII2S_I2S_CFG);
+		val |= HII2S_I2S_CFG__S2_MST_SLV;
+		hi6210_write_reg(i2s, HII2S_I2S_CFG, val);
+		break;
+	case SND_SOC_DAIFMT_CBS_CFS:
+		i2s->master = true;
+		val = hi6210_read_reg(i2s, HII2S_I2S_CFG);
+		val &= ~HII2S_I2S_CFG__S2_MST_SLV;
+		hi6210_write_reg(i2s, HII2S_I2S_CFG, val);
+		break;
+	default:
+		WARN_ONCE(1, "Invalid i2s->fmt MASTER_MASK. This shouldn't happen\n");
+		return -EINVAL;
+	}
+
+	switch (i2s->format & SND_SOC_DAIFMT_FORMAT_MASK) {
+	case SND_SOC_DAIFMT_I2S:
+		fmt = HII2S_FORMAT_I2S;
+		break;
+	case SND_SOC_DAIFMT_LEFT_J:
+		fmt = HII2S_FORMAT_LEFT_JUST;
+		break;
+	case SND_SOC_DAIFMT_RIGHT_J:
+		fmt = HII2S_FORMAT_RIGHT_JUST;
+		break;
+	default:
+		WARN_ONCE(1, "Invalid i2s->fmt FORMAT_MASK. This shouldn't happen\n");
+		return -EINVAL;
+	}
+
+	val = hi6210_read_reg(i2s, HII2S_I2S_CFG);
+	val &= ~(HII2S_I2S_CFG__S2_FUNC_MODE_MASK <<
+			HII2S_I2S_CFG__S2_FUNC_MODE_SHIFT);
+	val |= fmt << HII2S_I2S_CFG__S2_FUNC_MODE_SHIFT;
+	hi6210_write_reg(i2s, HII2S_I2S_CFG, val);
+
+
+	val = hi6210_read_reg(i2s, HII2S_CLK_SEL);
+	val &= ~(HII2S_CLK_SEL__I2S_BT_FM_SEL | /* BT gets the I2S */
+			HII2S_CLK_SEL__EXT_12_288MHZ_SEL);
+	hi6210_write_reg(i2s, HII2S_CLK_SEL, val);
+
+	dma_data->maxburst = 2;
+
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+		dma_data->addr = i2s->base_phys + HII2S_ST_DL_CHANNEL;
+	else
+		dma_data->addr = i2s->base_phys + HII2S_STEREO_UPLINK_CHANNEL;
+
+	switch (i2s->channels) {
+	case 1:
+		val = hi6210_read_reg(i2s, HII2S_I2S_CFG);
+		val |= HII2S_I2S_CFG__S2_FRAME_MODE;
+		hi6210_write_reg(i2s, HII2S_I2S_CFG, val);
+		break;
+	default:
+		val = hi6210_read_reg(i2s, HII2S_I2S_CFG);
+		val &= ~HII2S_I2S_CFG__S2_FRAME_MODE;
+		hi6210_write_reg(i2s, HII2S_I2S_CFG, val);
+		break;
+	}
+
+	/* clear loopback, set signed type and word length */
+	val = hi6210_read_reg(i2s, HII2S_I2S_CFG);
+	val &= ~HII2S_I2S_CFG__S2_CODEC_DATA_FORMAT;
+	val &= ~(HII2S_I2S_CFG__S2_CODEC_IO_WORDLENGTH_MASK <<
+			HII2S_I2S_CFG__S2_CODEC_IO_WORDLENGTH_SHIFT);
+	val &= ~(HII2S_I2S_CFG__S2_DIRECT_LOOP_MASK <<
+			HII2S_I2S_CFG__S2_DIRECT_LOOP_SHIFT);
+	val |= signed_data;
+	val |= (bits << HII2S_I2S_CFG__S2_CODEC_IO_WORDLENGTH_SHIFT);
+	hi6210_write_reg(i2s, HII2S_I2S_CFG, val);
+
+
+	if (!i2s->master)
+		return 0;
+
+	/* set DAC and related units to correct rate */
+	val = hi6210_read_reg(i2s, HII2S_FS_CFG);
+	val &= ~(HII2S_FS_CFG__FS_S2_MASK << HII2S_FS_CFG__FS_S2_SHIFT);
+	val &= ~(HII2S_FS_CFG__FS_DACLR_MASK << HII2S_FS_CFG__FS_DACLR_SHIFT);
+	val &= ~(HII2S_FS_CFG__FS_ST_DL_R_MASK <<
+					HII2S_FS_CFG__FS_ST_DL_R_SHIFT);
+	val &= ~(HII2S_FS_CFG__FS_ST_DL_L_MASK <<
+					HII2S_FS_CFG__FS_ST_DL_L_SHIFT);
+	val |= (rate << HII2S_FS_CFG__FS_S2_SHIFT);
+	val |= (rate << HII2S_FS_CFG__FS_DACLR_SHIFT);
+	val |= (rate << HII2S_FS_CFG__FS_ST_DL_R_SHIFT);
+	val |= (rate << HII2S_FS_CFG__FS_ST_DL_L_SHIFT);
+	hi6210_write_reg(i2s, HII2S_FS_CFG, val);
+
+	return 0;
+}
+
+static int hi6210_i2s_trigger(struct snd_pcm_substream *substream, int cmd,
+			  struct snd_soc_dai *cpu_dai)
+{
+	pr_debug("%s\n", __func__);
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+		if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+			hi6210_i2s_rxctrl(cpu_dai, 1);
+		else
+			hi6210_i2s_txctrl(cpu_dai, 1);
+		break;
+	case SNDRV_PCM_TRIGGER_STOP:
+	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+		if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+			hi6210_i2s_rxctrl(cpu_dai, 0);
+		else
+			hi6210_i2s_txctrl(cpu_dai, 0);
+		break;
+	default:
+		dev_err(cpu_dai->dev, "uknown cmd\n");
+		return -EINVAL;
+	}
+	return 0;
+}
+
+static int hi6210_i2s_dai_probe(struct snd_soc_dai *dai)
+{
+	struct hi6210_i2s *i2s = snd_soc_dai_get_drvdata(dai);
+
+	snd_soc_dai_init_dma_data(dai,
+				  &i2s->dma_data[SNDRV_PCM_STREAM_PLAYBACK],
+				  &i2s->dma_data[SNDRV_PCM_STREAM_CAPTURE]);
+
+	return 0;
+}
+
+
+static struct snd_soc_dai_ops hi6210_i2s_dai_ops = {
+	.trigger	= hi6210_i2s_trigger,
+	.hw_params	= hi6210_i2s_hw_params,
+	.set_fmt	= hi6210_i2s_set_fmt,
+	.startup	= hi6210_i2s_startup,
+	.shutdown	= hi6210_i2s_shutdown,
+};
+
+struct snd_soc_dai_driver hi6210_i2s_dai_init = {
+	.probe		= hi6210_i2s_dai_probe,
+	.playback = {
+		.channels_min = 2,
+		.channels_max = 2,
+		.formats = SNDRV_PCM_FMTBIT_S16_LE |
+			   SNDRV_PCM_FMTBIT_U16_LE,
+		.rates = SNDRV_PCM_RATE_48000,
+	},
+	.capture = {
+		.channels_min = 2,
+		.channels_max = 2,
+		.formats = SNDRV_PCM_FMTBIT_S16_LE |
+			   SNDRV_PCM_FMTBIT_U16_LE,
+		.rates = SNDRV_PCM_RATE_48000,
+	},
+	.ops = &hi6210_i2s_dai_ops,
+};
+
+static const struct snd_soc_component_driver hi6210_i2s_i2s_comp = {
+	.name = "hi6210_i2s-i2s",
+};
+
+static int hi6210_i2s_probe(struct platform_device *pdev)
+{
+	struct device_node *node = pdev->dev.of_node;
+	struct device *dev = &pdev->dev;
+	struct hi6210_i2s *i2s;
+	struct resource *res;
+	int ret;
+
+	i2s = devm_kzalloc(&pdev->dev, sizeof(*i2s), GFP_KERNEL);
+	if (!i2s)
+		return -ENOMEM;
+
+	i2s->dev = dev;
+	spin_lock_init(&i2s->lock);
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	i2s->base = devm_ioremap_resource(dev, res);
+	if (IS_ERR(i2s->base))
+		return PTR_ERR(i2s->base);
+
+	i2s->base_phys = (phys_addr_t)res->start;
+	i2s->dai = hi6210_i2s_dai_init;
+
+	dev_set_drvdata(&pdev->dev, i2s);
+
+	i2s->sysctrl = syscon_regmap_lookup_by_phandle(node,
+						"hisilicon,sysctrl-syscon");
+	if (IS_ERR(i2s->sysctrl))
+		return PTR_ERR(i2s->sysctrl);
+
+	i2s->clk[CLK_DACODEC] = devm_clk_get(&pdev->dev, "dacodec");
+	if (IS_ERR_OR_NULL(i2s->clk[CLK_DACODEC]))
+		return PTR_ERR(i2s->clk[CLK_DACODEC]);
+	i2s->clocks++;
+
+	i2s->clk[CLK_I2S_BASE] = devm_clk_get(&pdev->dev, "i2s-base");
+	if (IS_ERR_OR_NULL(i2s->clk[CLK_I2S_BASE]))
+		return PTR_ERR(i2s->clk[CLK_I2S_BASE]);
+	i2s->clocks++;
+
+	ret = devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, 0);
+	if (ret)
+		return ret;
+
+	ret = devm_snd_soc_register_component(&pdev->dev, &hi6210_i2s_i2s_comp,
+					 &i2s->dai, 1);
+	return ret;
+}
+
+static const struct of_device_id hi6210_i2s_dt_ids[] = {
+	{ .compatible = "hisilicon,hi6210-i2s" },
+	{ /* sentinel */ }
+};
+
+MODULE_DEVICE_TABLE(of, hi6210_i2s_dt_ids);
+
+static struct platform_driver hi6210_i2s_driver = {
+	.probe = hi6210_i2s_probe,
+	.driver = {
+		.name = "hi6210_i2s",
+		.of_match_table = hi6210_i2s_dt_ids,
+	},
+};
+
+module_platform_driver(hi6210_i2s_driver);
+
+MODULE_DESCRIPTION("Hisilicon HI6210 I2S driver");
+MODULE_AUTHOR("Andy Green <andy.green@linaro.org>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/hisilicon/hi6210-i2s.h b/sound/soc/hisilicon/hi6210-i2s.h
new file mode 100644
index 000000000000..85cecc4939a0
--- /dev/null
+++ b/sound/soc/hisilicon/hi6210-i2s.h
@@ -0,0 +1,276 @@
+/*
+ * linux/sound/soc/hisilicon/hi6210-i2s.h
+ *
+ * Copyright (C) 2015 Linaro, Ltd
+ * Author: Andy Green <andy.green@linaro.org>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Note at least on 6220, S2 == BT, S1 == Digital FM Radio IF
+ */
+
+#ifndef _HI6210_I2S_H
+#define _HI6210_I2S_H
+
+#define HII2S_SW_RST_N				0
+
+#define HII2S_SW_RST_N__STEREO_UPLINK_WORDLEN_SHIFT			28
+#define HII2S_SW_RST_N__STEREO_UPLINK_WORDLEN_MASK			3
+#define HII2S_SW_RST_N__THIRDMD_UPLINK_WORDLEN_SHIFT			26
+#define HII2S_SW_RST_N__THIRDMD_UPLINK_WORDLEN_MASK			3
+#define HII2S_SW_RST_N__VOICE_UPLINK_WORDLEN_SHIFT			24
+#define HII2S_SW_RST_N__VOICE_UPLINK_WORDLEN_MASK			3
+#define HII2S_SW_RST_N__ST_DL_WORDLEN_SHIFT				20
+#define HII2S_SW_RST_N__ST_DL_WORDLEN_MASK				3
+#define HII2S_SW_RST_N__THIRDMD_DLINK_WORDLEN_SHIFT			18
+#define HII2S_SW_RST_N__THIRDMD_DLINK_WORDLEN_MASK			3
+#define HII2S_SW_RST_N__VOICE_DLINK_WORDLEN_SHIFT			16
+#define HII2S_SW_RST_N__VOICE_DLINK_WORDLEN_MASK			3
+
+#define HII2S_SW_RST_N__SW_RST_N					BIT(0)
+
+enum hi6210_bits {
+	HII2S_BITS_16,
+	HII2S_BITS_18,
+	HII2S_BITS_20,
+	HII2S_BITS_24,
+};
+
+
+#define HII2S_IF_CLK_EN_CFG			4
+
+#define HII2S_IF_CLK_EN_CFG__THIRDMD_UPLINK_EN				BIT(25)
+#define HII2S_IF_CLK_EN_CFG__THIRDMD_DLINK_EN				BIT(24)
+#define HII2S_IF_CLK_EN_CFG__S3_IF_CLK_EN				BIT(20)
+#define HII2S_IF_CLK_EN_CFG__S2_IF_CLK_EN				BIT(16)
+#define HII2S_IF_CLK_EN_CFG__S2_OL_MIXER_EN				BIT(15)
+#define HII2S_IF_CLK_EN_CFG__S2_OL_SRC_EN				BIT(14)
+#define HII2S_IF_CLK_EN_CFG__S2_IR_PGA_EN				BIT(13)
+#define HII2S_IF_CLK_EN_CFG__S2_IL_PGA_EN				BIT(12)
+#define HII2S_IF_CLK_EN_CFG__S1_IR_PGA_EN				BIT(10)
+#define HII2S_IF_CLK_EN_CFG__S1_IL_PGA_EN				BIT(9)
+#define HII2S_IF_CLK_EN_CFG__S1_IF_CLK_EN				BIT(8)
+#define HII2S_IF_CLK_EN_CFG__VOICE_DLINK_SRC_EN				BIT(7)
+#define HII2S_IF_CLK_EN_CFG__VOICE_DLINK_EN				BIT(6)
+#define HII2S_IF_CLK_EN_CFG__ST_DL_R_EN					BIT(5)
+#define HII2S_IF_CLK_EN_CFG__ST_DL_L_EN					BIT(4)
+#define HII2S_IF_CLK_EN_CFG__VOICE_UPLINK_R_EN				BIT(3)
+#define HII2S_IF_CLK_EN_CFG__VOICE_UPLINK_L_EN				BIT(2)
+#define HII2S_IF_CLK_EN_CFG__STEREO_UPLINK_R_EN				BIT(1)
+#define HII2S_IF_CLK_EN_CFG__STEREO_UPLINK_L_EN				BIT(0)
+
+#define HII2S_DIG_FILTER_CLK_EN_CFG		8
+#define HII2S_DIG_FILTER_CLK_EN_CFG__DACR_SDM_EN			BIT(30)
+#define HII2S_DIG_FILTER_CLK_EN_CFG__DACR_HBF2I_EN			BIT(28)
+#define HII2S_DIG_FILTER_CLK_EN_CFG__DACR_MIXER_EN			BIT(25)
+#define HII2S_DIG_FILTER_CLK_EN_CFG__DACR_AGC_EN			BIT(24)
+#define HII2S_DIG_FILTER_CLK_EN_CFG__DACL_SDM_EN			BIT(22)
+#define HII2S_DIG_FILTER_CLK_EN_CFG__DACL_HBF2I_EN			BIT(20)
+#define HII2S_DIG_FILTER_CLK_EN_CFG__DACL_MIXER_EN			BIT(17)
+#define HII2S_DIG_FILTER_CLK_EN_CFG__DACL_AGC_EN			BIT(16)
+
+#define HII2S_FS_CFG				0xc
+
+#define HII2S_FS_CFG__FS_S2_SHIFT					28
+#define HII2S_FS_CFG__FS_S2_MASK					7
+#define HII2S_FS_CFG__FS_S1_SHIFT					24
+#define HII2S_FS_CFG__FS_S1_MASK					7
+#define HII2S_FS_CFG__FS_ADCLR_SHIFT					20
+#define HII2S_FS_CFG__FS_ADCLR_MASK					7
+#define HII2S_FS_CFG__FS_DACLR_SHIFT					16
+#define HII2S_FS_CFG__FS_DACLR_MASK					7
+#define HII2S_FS_CFG__FS_ST_DL_R_SHIFT					8
+#define HII2S_FS_CFG__FS_ST_DL_R_MASK					7
+#define HII2S_FS_CFG__FS_ST_DL_L_SHIFT					4
+#define HII2S_FS_CFG__FS_ST_DL_L_MASK					7
+#define HII2S_FS_CFG__FS_VOICE_DLINK_SHIFT				0
+#define HII2S_FS_CFG__FS_VOICE_DLINK_MASK				7
+
+enum hi6210_i2s_rates {
+	HII2S_FS_RATE_8KHZ = 0,
+	HII2S_FS_RATE_16KHZ = 1,
+	HII2S_FS_RATE_32KHZ = 2,
+	HII2S_FS_RATE_48KHZ = 4,
+	HII2S_FS_RATE_96KHZ = 5,
+	HII2S_FS_RATE_192KHZ = 6,
+};
+
+#define HII2S_I2S_CFG				0x10
+
+#define HII2S_I2S_CFG__S2_IF_TX_EN					BIT(31)
+#define HII2S_I2S_CFG__S2_IF_RX_EN					BIT(30)
+#define HII2S_I2S_CFG__S2_FRAME_MODE					BIT(29)
+#define HII2S_I2S_CFG__S2_MST_SLV					BIT(28)
+#define HII2S_I2S_CFG__S2_LRCK_MODE					BIT(27)
+#define HII2S_I2S_CFG__S2_CHNNL_MODE					BIT(26)
+#define HII2S_I2S_CFG__S2_CODEC_IO_WORDLENGTH_SHIFT			24
+#define HII2S_I2S_CFG__S2_CODEC_IO_WORDLENGTH_MASK			3
+#define HII2S_I2S_CFG__S2_DIRECT_LOOP_SHIFT				22
+#define HII2S_I2S_CFG__S2_DIRECT_LOOP_MASK				3
+#define HII2S_I2S_CFG__S2_TX_CLK_SEL					BIT(21)
+#define HII2S_I2S_CFG__S2_RX_CLK_SEL					BIT(20)
+#define HII2S_I2S_CFG__S2_CODEC_DATA_FORMAT				BIT(19)
+#define HII2S_I2S_CFG__S2_FUNC_MODE_SHIFT				16
+#define HII2S_I2S_CFG__S2_FUNC_MODE_MASK				7
+#define HII2S_I2S_CFG__S1_IF_TX_EN					BIT(15)
+#define HII2S_I2S_CFG__S1_IF_RX_EN					BIT(14)
+#define HII2S_I2S_CFG__S1_FRAME_MODE					BIT(13)
+#define HII2S_I2S_CFG__S1_MST_SLV					BIT(12)
+#define HII2S_I2S_CFG__S1_LRCK_MODE					BIT(11)
+#define HII2S_I2S_CFG__S1_CHNNL_MODE					BIT(10)
+#define HII2S_I2S_CFG__S1_CODEC_IO_WORDLENGTH_SHIFT			8
+#define HII2S_I2S_CFG__S1_CODEC_IO_WORDLENGTH_MASK			3
+#define HII2S_I2S_CFG__S1_DIRECT_LOOP_SHIFT				6
+#define HII2S_I2S_CFG__S1_DIRECT_LOOP_MASK				3
+#define HII2S_I2S_CFG__S1_TX_CLK_SEL					BIT(5)
+#define HII2S_I2S_CFG__S1_RX_CLK_SEL					BIT(4)
+#define HII2S_I2S_CFG__S1_CODEC_DATA_FORMAT				BIT(3)
+#define HII2S_I2S_CFG__S1_FUNC_MODE_SHIFT				0
+#define HII2S_I2S_CFG__S1_FUNC_MODE_MASK				7
+
+enum hi6210_i2s_formats {
+	HII2S_FORMAT_I2S,
+	HII2S_FORMAT_PCM_STD,
+	HII2S_FORMAT_PCM_USER,
+	HII2S_FORMAT_LEFT_JUST,
+	HII2S_FORMAT_RIGHT_JUST,
+};
+
+#define HII2S_DIG_FILTER_MODULE_CFG		0x14
+
+#define HII2S_DIG_FILTER_MODULE_CFG__DACR_MIXER_GAIN_SHIFT		28
+#define HII2S_DIG_FILTER_MODULE_CFG__DACR_MIXER_GAIN_MASK		3
+#define HII2S_DIG_FILTER_MODULE_CFG__DACR_MIXER_IN4_MUTE		BIT(27)
+#define HII2S_DIG_FILTER_MODULE_CFG__DACR_MIXER_IN3_MUTE		BIT(26)
+#define HII2S_DIG_FILTER_MODULE_CFG__DACR_MIXER_IN2_MUTE		BIT(25)
+#define HII2S_DIG_FILTER_MODULE_CFG__DACR_MIXER_IN1_MUTE		BIT(24)
+#define HII2S_DIG_FILTER_MODULE_CFG__DACL_MIXER_GAIN_SHIFT		20
+#define HII2S_DIG_FILTER_MODULE_CFG__DACL_MIXER_GAIN_MASK		3
+#define HII2S_DIG_FILTER_MODULE_CFG__DACL_MIXER_IN4_MUTE		BIT(19)
+#define HII2S_DIG_FILTER_MODULE_CFG__DACL_MIXER_IN3_MUTE		BIT(18)
+#define HII2S_DIG_FILTER_MODULE_CFG__DACL_MIXER_IN2_MUTE		BIT(17)
+#define HII2S_DIG_FILTER_MODULE_CFG__DACL_MIXER_IN1_MUTE		BIT(16)
+#define HII2S_DIG_FILTER_MODULE_CFG__SW_DACR_SDM_DITHER			BIT(9)
+#define HII2S_DIG_FILTER_MODULE_CFG__SW_DACL_SDM_DITHER			BIT(8)
+#define HII2S_DIG_FILTER_MODULE_CFG__LM_CODEC_DAC2ADC_SHIFT		4
+#define HII2S_DIG_FILTER_MODULE_CFG__LM_CODEC_DAC2ADC_MASK		7
+#define HII2S_DIG_FILTER_MODULE_CFG__RM_CODEC_DAC2ADC_SHIFT		0
+#define HII2S_DIG_FILTER_MODULE_CFG__RM_CODEC_DAC2ADC_MASK		7
+
+enum hi6210_gains {
+	HII2S_GAIN_100PC,
+	HII2S_GAIN_50PC,
+	HII2S_GAIN_25PC,
+};
+
+#define HII2S_MUX_TOP_MODULE_CFG		0x18
+
+#define HII2S_MUX_TOP_MODULE_CFG__VOICE_DLINK_MIXER_GAIN_SHIFT		14
+#define HII2S_MUX_TOP_MODULE_CFG__VOICE_DLINK_MIXER_GAIN_MASK		3
+#define HII2S_MUX_TOP_MODULE_CFG__VOICE_DLINK_MIXER_IN2_MUTE		BIT(13)
+#define HII2S_MUX_TOP_MODULE_CFG__VOICE_DLINK_MIXER_IN1_MUTE		BIT(12)
+#define HII2S_MUX_TOP_MODULE_CFG__S2_OL_MIXER_GAIN_SHIFT		10
+#define HII2S_MUX_TOP_MODULE_CFG__S2_OL_MIXER_GAIN_MASK			3
+#define HII2S_MUX_TOP_MODULE_CFG__S2_OL_MIXER_IN2_MUTE			BIT(9)
+#define HII2S_MUX_TOP_MODULE_CFG__S2_OL_MIXER_IN1_MUTE			BIT(8)
+#define HII2S_MUX_TOP_MODULE_CFG__S2_OL_SRC_RDY				BIT(6)
+#define HII2S_MUX_TOP_MODULE_CFG__S2_OL_SRC_MODE_SHIFT			4
+#define HII2S_MUX_TOP_MODULE_CFG__S2_OL_SRC_MODE_MASK			3
+#define HII2S_MUX_TOP_MODULE_CFG__VOICE_DLINK_SRC_RDY			BIT(3)
+#define HII2S_MUX_TOP_MODULE_CFG__VOICE_DLINK_SRC_MODE_SHIFT		0
+#define HII2S_MUX_TOP_MODULE_CFG__VOICE_DLINK_SRC_MODE_MASK		7
+
+enum hi6210_s2_src_mode {
+	HII2S_S2_SRC_MODE_3,
+	HII2S_S2_SRC_MODE_12,
+	HII2S_S2_SRC_MODE_6,
+	HII2S_S2_SRC_MODE_2,
+};
+
+enum hi6210_voice_dlink_src_mode {
+	HII2S_VOICE_DL_SRC_MODE_12 = 1,
+	HII2S_VOICE_DL_SRC_MODE_6,
+	HII2S_VOICE_DL_SRC_MODE_2,
+	HII2S_VOICE_DL_SRC_MODE_3,
+};
+
+#define HII2S_ADC_PGA_CFG			0x1c
+#define HII2S_S1_INPUT_PGA_CFG			0x20
+#define HII2S_S2_INPUT_PGA_CFG			0x24
+#define HII2S_ST_DL_PGA_CFG			0x28
+#define HII2S_VOICE_SIDETONE_DLINK_PGA_CFG	0x2c
+#define HII2S_APB_AFIFO_CFG_1			0x30
+#define HII2S_APB_AFIFO_CFG_2			0x34
+#define HII2S_ST_DL_FIFO_TH_CFG			0x38
+
+#define HII2S_ST_DL_FIFO_TH_CFG__ST_DL_R_AEMPTY_SHIFT			24
+#define HII2S_ST_DL_FIFO_TH_CFG__ST_DL_R_AEMPTY_MASK			0x1f
+#define HII2S_ST_DL_FIFO_TH_CFG__ST_DL_R_AFULL_SHIFT			16
+#define HII2S_ST_DL_FIFO_TH_CFG__ST_DL_R_AFULL_MASK			0x1f
+#define HII2S_ST_DL_FIFO_TH_CFG__ST_DL_L_AEMPTY_SHIFT			8
+#define HII2S_ST_DL_FIFO_TH_CFG__ST_DL_L_AEMPTY_MASK			0x1f
+#define HII2S_ST_DL_FIFO_TH_CFG__ST_DL_L_AFULL_SHIFT			0
+#define HII2S_ST_DL_FIFO_TH_CFG__ST_DL_L_AFULL_MASK			0x1f
+
+#define HII2S_STEREO_UPLINK_FIFO_TH_CFG		0x3c
+#define HII2S_VOICE_UPLINK_FIFO_TH_CFG		0x40
+#define HII2S_CODEC_IRQ_MASK			0x44
+#define HII2S_CODEC_IRQ				0x48
+#define HII2S_DACL_AGC_CFG_1			0x4c
+#define HII2S_DACL_AGC_CFG_2			0x50
+#define HII2S_DACR_AGC_CFG_1			0x54
+#define HII2S_DACR_AGC_CFG_2			0x58
+#define HII2S_DMIC_SIF_CFG			0x5c
+#define HII2S_MISC_CFG				0x60
+
+#define HII2S_MISC_CFG__THIRDMD_DLINK_TEST_SEL				BIT(17)
+#define HII2S_MISC_CFG__THIRDMD_DLINK_DIN_SEL				BIT(16)
+#define HII2S_MISC_CFG__S3_DOUT_RIGHT_SEL				BIT(14)
+#define HII2S_MISC_CFG__S3_DOUT_LEFT_SEL				BIT(13)
+#define HII2S_MISC_CFG__S3_DIN_TEST_SEL					BIT(12)
+#define HII2S_MISC_CFG__VOICE_DLINK_SRC_UP_DOUT_VLD_SEL			BIT(8)
+#define HII2S_MISC_CFG__VOICE_DLINK_TEST_SEL				BIT(7)
+#define HII2S_MISC_CFG__VOICE_DLINK_DIN_SEL				BIT(6)
+#define HII2S_MISC_CFG__ST_DL_TEST_SEL					BIT(4)
+#define HII2S_MISC_CFG__S2_DOUT_RIGHT_SEL				BIT(3)
+#define HII2S_MISC_CFG__S2_DOUT_TEST_SEL				BIT(2)
+#define HII2S_MISC_CFG__S1_DOUT_TEST_SEL				BIT(1)
+#define HII2S_MISC_CFG__S2_DOUT_LEFT_SEL				BIT(0)
+
+#define HII2S_S2_SRC_CFG			0x64
+#define HII2S_MEM_CFG				0x68
+#define HII2S_THIRDMD_PCM_PGA_CFG		0x6c
+#define HII2S_THIRD_MODEM_FIFO_TH		0x70
+#define HII2S_S3_ANTI_FREQ_JITTER_TX_INC_CNT	0x74
+#define HII2S_S3_ANTI_FREQ_JITTER_TX_DEC_CNT	0x78
+#define HII2S_S3_ANTI_FREQ_JITTER_RX_INC_CNT	0x7c
+#define HII2S_S3_ANTI_FREQ_JITTER_RX_DEC_CNT	0x80
+#define HII2S_ANTI_FREQ_JITTER_EN		0x84
+#define HII2S_CLK_SEL				0x88
+
+/* 0 = BT owns the i2s */
+#define HII2S_CLK_SEL__I2S_BT_FM_SEL					BIT(0)
+/* 0 = internal source, 1 = ext */
+#define HII2S_CLK_SEL__EXT_12_288MHZ_SEL				BIT(1)
+
+
+#define HII2S_THIRDMD_DLINK_CHANNEL		0xe8
+#define HII2S_THIRDMD_ULINK_CHANNEL		0xec
+#define HII2S_VOICE_DLINK_CHANNEL		0xf0
+
+/* shovel data in here for playback */
+#define HII2S_ST_DL_CHANNEL			0xf4
+#define HII2S_STEREO_UPLINK_CHANNEL		0xf8
+#define HII2S_VOICE_UPLINK_CHANNEL		0xfc
+
+#endif/* _HI6210_I2S_H */
diff --git a/sound/soc/intel/Kconfig b/sound/soc/intel/Kconfig
index 526855ad479e..67968ef3bbda 100644
--- a/sound/soc/intel/Kconfig
+++ b/sound/soc/intel/Kconfig
@@ -202,6 +202,30 @@ config SND_SOC_INTEL_CHT_BSW_MAX98090_TI_MACH
 	  platforms with MAX98090 audio codec it also can support TI jack chip as aux device.
 	  If unsure select "N".
 
+config SND_SOC_INTEL_BYT_CHT_DA7213_MACH
+	tristate "ASoC Audio driver for Intel Baytrail & Cherrytrail with DA7212/7213 codec"
+	depends on X86_INTEL_LPSS && I2C && ACPI
+	select SND_SOC_DA7213
+	select SND_SST_ATOM_HIFI2_PLATFORM
+	select SND_SST_IPC_ACPI
+	select SND_SOC_INTEL_SST_MATCH if ACPI
+	help
+	  This adds support for ASoC machine driver for Intel(R) Baytrail & CherryTrail
+	  platforms with DA7212/7213 audio codec.
+	  If unsure select "N".
+
+config SND_SOC_INTEL_BYT_CHT_NOCODEC_MACH
+	tristate "ASoC Audio driver for Intel Baytrail & Cherrytrail platform with no codec (MinnowBoard MAX, Up)"
+	depends on X86_INTEL_LPSS && I2C && ACPI
+	select SND_SST_ATOM_HIFI2_PLATFORM
+	select SND_SST_IPC_ACPI
+	select SND_SOC_INTEL_SST_MATCH if ACPI
+	help
+	  This adds support for ASoC machine driver for the MinnowBoard Max or
+	  Up boards and provides access to I2S signals on the Low-Speed
+	  connector
+	  If unsure select "N".
+
 config SND_SOC_INTEL_SKYLAKE
 	tristate
 	select SND_HDA_EXT_CORE
diff --git a/sound/soc/intel/atom/sst/sst_acpi.c b/sound/soc/intel/atom/sst/sst_acpi.c
index 747c0f393d2d..dd250b8b26f2 100644
--- a/sound/soc/intel/atom/sst/sst_acpi.c
+++ b/sound/soc/intel/atom/sst/sst_acpi.c
@@ -420,7 +420,21 @@ static const struct dmi_system_id byt_table[] = {
 		.callback = byt_thinkpad10_quirk_cb,
 		.matches = {
 			DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
-			DMI_MATCH(DMI_PRODUCT_NAME, "20C3001VHH"),
+			DMI_MATCH(DMI_PRODUCT_VERSION, "ThinkPad 10"),
+		},
+	},
+	{
+		.callback = byt_thinkpad10_quirk_cb,
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
+			DMI_MATCH(DMI_PRODUCT_VERSION, "ThinkPad Tablet B"),
+		},
+	},
+	{
+		.callback = byt_thinkpad10_quirk_cb,
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
+			DMI_MATCH(DMI_PRODUCT_VERSION, "Lenovo Miix 2 10"),
 		},
 	},
 	{ }
@@ -480,12 +494,23 @@ static struct sst_acpi_mach sst_acpi_bytcr[] = {
 						&byt_rvp_platform_data },
 	{"10EC5651", "bytcr_rt5651", "intel/fw_sst_0f28.bin", "bytcr_rt5651", NULL,
 						&byt_rvp_platform_data },
+	{"DLGS7212", "bytcht_da7213", "intel/fw_sst_0f28.bin", "bytcht_da7213", NULL,
+						&byt_rvp_platform_data },
+	{"DLGS7213", "bytcht_da7213", "intel/fw_sst_0f28.bin", "bytcht_da7213", NULL,
+						&byt_rvp_platform_data },
 	/* some Baytrail platforms rely on RT5645, use CHT machine driver */
 	{"10EC5645", "cht-bsw-rt5645", "intel/fw_sst_0f28.bin", "cht-bsw", NULL,
 						&byt_rvp_platform_data },
 	{"10EC5648", "cht-bsw-rt5645", "intel/fw_sst_0f28.bin", "cht-bsw", NULL,
 						&byt_rvp_platform_data },
-
+#if IS_ENABLED(CONFIG_SND_SOC_INTEL_BYT_CHT_NOCODEC_MACH)
+	/*
+	 * This is always last in the table so that it is selected only when
+	 * enabled explicitly and there is no codec-related information in SSDT
+	 */
+	{"80860F28", "bytcht_nocodec", "intel/fw_sst_0f28.bin", "bytcht_nocodec", NULL,
+						&byt_rvp_platform_data },
+#endif
 	{},
 };
 
@@ -504,6 +529,10 @@ static struct sst_acpi_mach sst_acpi_chv[] = {
 
 	{"193C9890", "cht-bsw-max98090", "intel/fw_sst_22a8.bin", "cht-bsw", NULL,
 						&chv_platform_data },
+	{"DLGS7212", "bytcht_da7213", "intel/fw_sst_22a8.bin", "bytcht_da7213", NULL,
+						&chv_platform_data },
+	{"DLGS7213", "bytcht_da7213", "intel/fw_sst_22a8.bin", "bytcht_da7213", NULL,
+						&chv_platform_data },
 	/* some CHT-T platforms rely on RT5640, use Baytrail machine driver */
 	{"10EC5640", "bytcr_rt5640", "intel/fw_sst_22a8.bin", "bytcr_rt5640", cht_quirk,
 						&chv_platform_data },
@@ -512,6 +541,14 @@ static struct sst_acpi_mach sst_acpi_chv[] = {
 	/* some CHT-T platforms rely on RT5651, use Baytrail machine driver */
 	{"10EC5651", "bytcr_rt5651", "intel/fw_sst_22a8.bin", "bytcr_rt5651", NULL,
 						&chv_platform_data },
+#if IS_ENABLED(CONFIG_SND_SOC_INTEL_BYT_CHT_NOCODEC_MACH)
+	/*
+	 * This is always last in the table so that it is selected only when
+	 * enabled explicitly and there is no codec-related information in SSDT
+	 */
+	{"808622A8", "bytcht_nocodec", "intel/fw_sst_22a8.bin", "bytcht_nocodec", NULL,
+						&chv_platform_data },
+#endif
 	{},
 };
 
diff --git a/sound/soc/intel/atom/sst/sst_ipc.c b/sound/soc/intel/atom/sst/sst_ipc.c
index 14c2d9d18180..20b01e02ed8f 100644
--- a/sound/soc/intel/atom/sst/sst_ipc.c
+++ b/sound/soc/intel/atom/sst/sst_ipc.c
@@ -236,7 +236,9 @@ static void process_fw_init(struct intel_sst_drv *sst_drv_ctx,
 		retval = init->result;
 		goto ret;
 	}
-	dev_info(sst_drv_ctx->dev, "FW Version %02x.%02x.%02x.%02x\n",
+	if (memcmp(&sst_drv_ctx->fw_version, &init->fw_version,
+		   sizeof(init->fw_version)))
+		dev_info(sst_drv_ctx->dev, "FW Version %02x.%02x.%02x.%02x\n",
 			init->fw_version.type, init->fw_version.major,
 			init->fw_version.minor, init->fw_version.build);
 	dev_dbg(sst_drv_ctx->dev, "Build date %s Time %s\n",
diff --git a/sound/soc/intel/boards/Makefile b/sound/soc/intel/boards/Makefile
index 5639f10774e6..56896e09445d 100644
--- a/sound/soc/intel/boards/Makefile
+++ b/sound/soc/intel/boards/Makefile
@@ -10,6 +10,8 @@ snd-soc-sst-bytcr-rt5651-objs := bytcr_rt5651.o
 snd-soc-sst-cht-bsw-rt5672-objs := cht_bsw_rt5672.o
 snd-soc-sst-cht-bsw-rt5645-objs := cht_bsw_rt5645.o
 snd-soc-sst-cht-bsw-max98090_ti-objs := cht_bsw_max98090_ti.o
+snd-soc-sst-byt-cht-da7213-objs := bytcht_da7213.o
+snd-soc-sst-byt-cht-nocodec-objs := bytcht_nocodec.o
 snd-soc-skl_rt286-objs := skl_rt286.o
 snd-skl_nau88l25_max98357a-objs := skl_nau88l25_max98357a.o
 snd-soc-skl_nau88l25_ssm4567-objs := skl_nau88l25_ssm4567.o
@@ -26,6 +28,8 @@ obj-$(CONFIG_SND_SOC_INTEL_BYTCR_RT5651_MACH) += snd-soc-sst-bytcr-rt5651.o
 obj-$(CONFIG_SND_SOC_INTEL_CHT_BSW_RT5672_MACH) += snd-soc-sst-cht-bsw-rt5672.o
 obj-$(CONFIG_SND_SOC_INTEL_CHT_BSW_RT5645_MACH) += snd-soc-sst-cht-bsw-rt5645.o
 obj-$(CONFIG_SND_SOC_INTEL_CHT_BSW_MAX98090_TI_MACH) += snd-soc-sst-cht-bsw-max98090_ti.o
+obj-$(CONFIG_SND_SOC_INTEL_BYT_CHT_DA7213_MACH) += snd-soc-sst-byt-cht-da7213.o
+obj-$(CONFIG_SND_SOC_INTEL_BYT_CHT_NOCODEC_MACH) += snd-soc-sst-byt-cht-nocodec.o
 obj-$(CONFIG_SND_SOC_INTEL_SKL_RT286_MACH) += snd-soc-skl_rt286.o
 obj-$(CONFIG_SND_SOC_INTEL_SKL_NAU88L25_MAX98357A_MACH) += snd-skl_nau88l25_max98357a.o
 obj-$(CONFIG_SND_SOC_INTEL_SKL_NAU88L25_SSM4567_MACH) += snd-soc-skl_nau88l25_ssm4567.o
diff --git a/sound/soc/intel/boards/bdw-rt5677.c b/sound/soc/intel/boards/bdw-rt5677.c
index 53c6b4cbb1e1..14d9693c1641 100644
--- a/sound/soc/intel/boards/bdw-rt5677.c
+++ b/sound/soc/intel/boards/bdw-rt5677.c
@@ -193,13 +193,12 @@ static int bdw_rt5677_init(struct snd_soc_pcm_runtime *rtd)
 			RT5677_CLK_SEL_I2S1_ASRC);
 
 	/* Request rt5677 GPIO for headphone amp control */
-	bdw_rt5677->gpio_hp_en = devm_gpiod_get_index(codec->dev,
-			"headphone-enable", 0, 0);
+	bdw_rt5677->gpio_hp_en = devm_gpiod_get(codec->dev, "headphone-enable",
+						GPIOD_OUT_LOW);
 	if (IS_ERR(bdw_rt5677->gpio_hp_en)) {
 		dev_err(codec->dev, "Can't find HP_AMP_SHDN_L gpio\n");
 		return PTR_ERR(bdw_rt5677->gpio_hp_en);
 	}
-	gpiod_direction_output(bdw_rt5677->gpio_hp_en, 0);
 
 	/* Create and initialize headphone jack */
 	if (!snd_soc_card_jack_new(rtd->card, "Headphone Jack",
diff --git a/sound/soc/intel/boards/broadwell.c b/sound/soc/intel/boards/broadwell.c
index faf865bb1765..6dcbbcefc25b 100644
--- a/sound/soc/intel/boards/broadwell.c
+++ b/sound/soc/intel/boards/broadwell.c
@@ -269,9 +269,6 @@ static struct snd_soc_card broadwell_rt286 = {
 static int broadwell_audio_probe(struct platform_device *pdev)
 {
 	broadwell_rt286.dev = &pdev->dev;
-
-	snd_soc_set_dmi_name(&broadwell_rt286, NULL);
-
 	return devm_snd_soc_register_card(&pdev->dev, &broadwell_rt286);
 }
 
diff --git a/sound/soc/intel/boards/bxt_da7219_max98357a.c b/sound/soc/intel/boards/bxt_da7219_max98357a.c
index 2cda06cde4d1..3a8c4d954a91 100644
--- a/sound/soc/intel/boards/bxt_da7219_max98357a.c
+++ b/sound/soc/intel/boards/bxt_da7219_max98357a.c
@@ -55,6 +55,54 @@ enum {
 	BXT_DPCM_AUDIO_HDMI3_PB,
 };
 
+static inline struct snd_soc_dai *bxt_get_codec_dai(struct snd_soc_card *card)
+{
+	struct snd_soc_pcm_runtime *rtd;
+
+	list_for_each_entry(rtd, &card->rtd_list, list) {
+
+		if (!strncmp(rtd->codec_dai->name, BXT_DIALOG_CODEC_DAI,
+			     strlen(BXT_DIALOG_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)
+{
+	int ret = 0;
+	struct snd_soc_dapm_context *dapm = w->dapm;
+	struct snd_soc_card *card = dapm->card;
+	struct snd_soc_dai *codec_dai;
+
+	codec_dai = bxt_get_codec_dai(card);
+	if (!codec_dai) {
+		dev_err(card->dev, "Codec dai not found; Unable to set/unset codec pll\n");
+		return -EIO;
+	}
+
+	if (SND_SOC_DAPM_EVENT_OFF(event)) {
+		ret = snd_soc_dai_set_pll(codec_dai, 0,
+			DA7219_SYSCLK_MCLK, 0, 0);
+		if (ret)
+			dev_err(card->dev, "failed to stop PLL: %d\n", ret);
+	} else if(SND_SOC_DAPM_EVENT_ON(event)) {
+		ret = snd_soc_dai_set_sysclk(codec_dai,
+                        DA7219_CLKSRC_MCLK, 19200000, SND_SOC_CLOCK_IN);
+		if (ret)
+			dev_err(card->dev, "can't set codec sysclk configuration\n");
+
+		ret = snd_soc_dai_set_pll(codec_dai, 0,
+			DA7219_SYSCLK_PLL_SRM, 0, DA7219_PLL_FREQ_OUT_98304);
+		if (ret)
+			dev_err(card->dev, "failed to start PLL: %d\n", ret);
+	}
+
+	return ret;
+}
+
 static const struct snd_kcontrol_new broxton_controls[] = {
 	SOC_DAPM_PIN_SWITCH("Headphone Jack"),
 	SOC_DAPM_PIN_SWITCH("Headset Mic"),
@@ -69,6 +117,8 @@ static const struct snd_soc_dapm_widget broxton_widgets[] = {
 	SND_SOC_DAPM_SPK("HDMI1", NULL),
 	SND_SOC_DAPM_SPK("HDMI2", NULL),
 	SND_SOC_DAPM_SPK("HDMI3", NULL),
+	SND_SOC_DAPM_SUPPLY("Platform Clock", SND_SOC_NOPM, 0, 0,
+			platform_clock_control,	SND_SOC_DAPM_POST_PMD|SND_SOC_DAPM_PRE_PMU),
 };
 
 static const struct snd_soc_dapm_route broxton_map[] = {
@@ -109,6 +159,9 @@ static const struct snd_soc_dapm_route broxton_map[] = {
 	/* DMIC */
 	{"dmic01_hifi", NULL, "DMIC01 Rx"},
 	{"DMIC01 Rx", NULL, "DMIC AIF"},
+
+	{ "Headphone Jack", NULL, "Platform Clock" },
+	{ "Headset Mic", NULL, "Platform Clock" },
 };
 
 static int broxton_ssp_fixup(struct snd_soc_pcm_runtime *rtd,
@@ -243,49 +296,6 @@ static const struct snd_soc_ops broxton_da7219_fe_ops = {
 	.startup = bxt_fe_startup,
 };
 
-static int broxton_da7219_hw_params(struct snd_pcm_substream *substream,
-				struct snd_pcm_hw_params *params)
-{
-	struct snd_soc_pcm_runtime *rtd = substream->private_data;
-	struct snd_soc_dai *codec_dai = rtd->codec_dai;
-	int ret;
-
-	ret = snd_soc_dai_set_sysclk(codec_dai,
-			DA7219_CLKSRC_MCLK, 19200000, SND_SOC_CLOCK_IN);
-	if (ret < 0)
-		dev_err(codec_dai->dev, "can't set codec sysclk configuration\n");
-
-	ret = snd_soc_dai_set_pll(codec_dai, 0,
-			DA7219_SYSCLK_PLL_SRM, 0, DA7219_PLL_FREQ_OUT_98304);
-	if (ret < 0) {
-		dev_err(codec_dai->dev, "failed to start PLL: %d\n", ret);
-		return -EIO;
-	}
-
-	return ret;
-}
-
-static int broxton_da7219_hw_free(struct snd_pcm_substream *substream)
-{
-	struct snd_soc_pcm_runtime *rtd = substream->private_data;
-	struct snd_soc_dai *codec_dai = rtd->codec_dai;
-	int ret;
-
-	ret = snd_soc_dai_set_pll(codec_dai, 0,
-			DA7219_SYSCLK_MCLK, 0, 0);
-	if (ret < 0) {
-		dev_err(codec_dai->dev, "failed to stop PLL: %d\n", ret);
-		return -EIO;
-	}
-
-	return ret;
-}
-
-static const struct snd_soc_ops broxton_da7219_ops = {
-	.hw_params = broxton_da7219_hw_params,
-	.hw_free = broxton_da7219_hw_free,
-};
-
 static int broxton_dmic_fixup(struct snd_soc_pcm_runtime *rtd,
 			struct snd_pcm_hw_params *params)
 {
@@ -467,7 +477,6 @@ static struct snd_soc_dai_link broxton_dais[] = {
 			SND_SOC_DAIFMT_CBS_CFS,
 		.ignore_pmdown_time = 1,
 		.be_hw_params_fixup = broxton_ssp_fixup,
-		.ops = &broxton_da7219_ops,
 		.dpcm_playback = 1,
 		.dpcm_capture = 1,
 	},
diff --git a/sound/soc/intel/boards/bxt_rt298.c b/sound/soc/intel/boards/bxt_rt298.c
index 176c080a9818..1a68d043c803 100644
--- a/sound/soc/intel/boards/bxt_rt298.c
+++ b/sound/soc/intel/boards/bxt_rt298.c
@@ -274,12 +274,15 @@ static int bxt_fe_startup(struct snd_pcm_substream *substream)
 	 * on this platform for PCM device we support:
 	 *      48Khz
 	 *      stereo
+	 *	16-bit audio
 	 */
 
 	runtime->hw.channels_max = 2;
 	snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
 				&constraints_channels);
 
+	runtime->hw.formats = SNDRV_PCM_FMTBIT_S16_LE;
+	snd_pcm_hw_constraint_msbits(runtime, 0, 16, 16);
 	snd_pcm_hw_constraint_list(runtime, 0,
 				SNDRV_PCM_HW_PARAM_RATE, &constraints_rates);
 
diff --git a/sound/soc/intel/boards/bytcht_da7213.c b/sound/soc/intel/boards/bytcht_da7213.c
new file mode 100644
index 000000000000..18873e23f404
--- /dev/null
+++ b/sound/soc/intel/boards/bytcht_da7213.c
@@ -0,0 +1,283 @@
+/*
+ *  bytcht-da7213.c - ASoc Machine driver for Intel Baytrail and
+ *             Cherrytrail-based platforms, with Dialog DA7213 codec
+ *
+ *  Copyright (C) 2017 Intel Corporation
+ *  Author: Pierre-Louis Bossart <pierre-louis.bossart@linux.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/acpi.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <asm/platform_sst_audio.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include "../../codecs/da7213.h"
+#include "../atom/sst-atom-controls.h"
+#include "../common/sst-acpi.h"
+
+static const struct snd_kcontrol_new controls[] = {
+	SOC_DAPM_PIN_SWITCH("Headphone Jack"),
+	SOC_DAPM_PIN_SWITCH("Headset Mic"),
+	SOC_DAPM_PIN_SWITCH("Mic"),
+	SOC_DAPM_PIN_SWITCH("Aux In"),
+};
+
+static const struct snd_soc_dapm_widget dapm_widgets[] = {
+	SND_SOC_DAPM_HP("Headphone Jack", NULL),
+	SND_SOC_DAPM_MIC("Headset Mic", NULL),
+	SND_SOC_DAPM_MIC("Mic", NULL),
+	SND_SOC_DAPM_LINE("Aux In", NULL),
+};
+
+static const struct snd_soc_dapm_route audio_map[] = {
+	{"Headphone Jack", NULL, "HPL"},
+	{"Headphone Jack", NULL, "HPR"},
+
+	{"AUXL", NULL, "Aux In"},
+	{"AUXR", NULL, "Aux In"},
+
+	/* Assume Mic1 is linked to Headset and Mic2 to on-board mic */
+	{"MIC1", NULL, "Headset Mic"},
+	{"MIC2", NULL, "Mic"},
+
+	/* SOC-codec link */
+	{"ssp2 Tx", NULL, "codec_out0"},
+	{"ssp2 Tx", NULL, "codec_out1"},
+	{"codec_in0", NULL, "ssp2 Rx"},
+	{"codec_in1", NULL, "ssp2 Rx"},
+
+	{"Playback", NULL, "ssp2 Tx"},
+	{"ssp2 Rx", NULL, "Capture"},
+};
+
+static int codec_fixup(struct snd_soc_pcm_runtime *rtd,
+		       struct snd_pcm_hw_params *params)
+{
+	int ret;
+	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 convert the FE rate to 48k, stereo, 24bits */
+	rate->min = rate->max = 48000;
+	channels->min = channels->max = 2;
+
+	/* set SSP2 to 24-bit */
+	params_set_format(params, SNDRV_PCM_FORMAT_S24_LE);
+
+	/*
+	 * Default mode for SSP configuration is TDM 4 slot, override config
+	 * with explicit setting to I2S 2ch 24-bit. The word length is set with
+	 * dai_set_tdm_slot() since there is no other API exposed
+	 */
+	ret = snd_soc_dai_set_fmt(rtd->cpu_dai,
+				  SND_SOC_DAIFMT_I2S     |
+				  SND_SOC_DAIFMT_NB_NF   |
+				  SND_SOC_DAIFMT_CBS_CFS);
+	if (ret < 0) {
+		dev_err(rtd->dev, "can't set format to I2S, err %d\n", ret);
+		return ret;
+	}
+
+	ret = snd_soc_dai_set_tdm_slot(rtd->cpu_dai, 0x3, 0x3, 2, 24);
+	if (ret < 0) {
+		dev_err(rtd->dev, "can't set I2S config, err %d\n", ret);
+		return ret;
+	}
+
+	return 0;
+}
+
+static int aif1_startup(struct snd_pcm_substream *substream)
+{
+	return snd_pcm_hw_constraint_single(substream->runtime,
+			SNDRV_PCM_HW_PARAM_RATE, 48000);
+}
+
+static int aif1_hw_params(struct snd_pcm_substream *substream,
+			  struct snd_pcm_hw_params *params)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct snd_soc_dai *codec_dai = rtd->codec_dai;
+	int ret;
+
+	ret = snd_soc_dai_set_sysclk(codec_dai, DA7213_CLKSRC_MCLK,
+				     19200000, SND_SOC_CLOCK_IN);
+	if (ret < 0)
+		dev_err(codec_dai->dev, "can't set codec sysclk configuration\n");
+
+	ret = snd_soc_dai_set_pll(codec_dai, 0,
+			DA7213_SYSCLK_PLL_SRM, 0, DA7213_PLL_FREQ_OUT_98304000);
+	if (ret < 0) {
+		dev_err(codec_dai->dev, "failed to start PLL: %d\n", ret);
+		return -EIO;
+	}
+
+	return ret;
+}
+
+static int aif1_hw_free(struct snd_pcm_substream *substream)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct snd_soc_dai *codec_dai = rtd->codec_dai;
+	int ret;
+
+	ret = snd_soc_dai_set_pll(codec_dai, 0,
+				  DA7213_SYSCLK_MCLK, 0, 0);
+	if (ret < 0) {
+		dev_err(codec_dai->dev, "failed to stop PLL: %d\n", ret);
+		return -EIO;
+	}
+
+	return ret;
+}
+
+static const struct snd_soc_ops aif1_ops = {
+	.startup = aif1_startup,
+};
+
+static const struct snd_soc_ops ssp2_ops = {
+	.hw_params = aif1_hw_params,
+	.hw_free = aif1_hw_free,
+
+};
+
+static struct snd_soc_dai_link dailink[] = {
+	[MERR_DPCM_AUDIO] = {
+		.name = "Audio Port",
+		.stream_name = "Audio",
+		.cpu_dai_name = "media-cpu-dai",
+		.codec_dai_name = "snd-soc-dummy-dai",
+		.codec_name = "snd-soc-dummy",
+		.platform_name = "sst-mfld-platform",
+		.nonatomic = true,
+		.dynamic = 1,
+		.dpcm_playback = 1,
+		.dpcm_capture = 1,
+		.ops = &aif1_ops,
+	},
+	[MERR_DPCM_DEEP_BUFFER] = {
+		.name = "Deep-Buffer Audio Port",
+		.stream_name = "Deep-Buffer Audio",
+		.cpu_dai_name = "deepbuffer-cpu-dai",
+		.codec_dai_name = "snd-soc-dummy-dai",
+		.codec_name = "snd-soc-dummy",
+		.platform_name = "sst-mfld-platform",
+		.nonatomic = true,
+		.dynamic = 1,
+		.dpcm_playback = 1,
+		.ops = &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",
+	},
+	/* CODEC<->CODEC link */
+	/* back ends */
+	{
+		.name = "SSP2-Codec",
+		.id = 1,
+		.cpu_dai_name = "ssp2-port",
+		.platform_name = "sst-mfld-platform",
+		.no_pcm = 1,
+		.codec_dai_name = "da7213-hifi",
+		.codec_name = "i2c-DLGS7213:00",
+		.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
+						| SND_SOC_DAIFMT_CBS_CFS,
+		.be_hw_params_fixup = codec_fixup,
+		.nonatomic = true,
+		.dpcm_playback = 1,
+		.dpcm_capture = 1,
+		.ops = &ssp2_ops,
+	},
+};
+
+/* SoC card */
+static struct snd_soc_card bytcht_da7213_card = {
+	.name = "bytcht-da7213",
+	.owner = THIS_MODULE,
+	.dai_link = dailink,
+	.num_links = ARRAY_SIZE(dailink),
+	.controls = controls,
+	.num_controls = ARRAY_SIZE(controls),
+	.dapm_widgets = dapm_widgets,
+	.num_dapm_widgets = ARRAY_SIZE(dapm_widgets),
+	.dapm_routes = audio_map,
+	.num_dapm_routes = ARRAY_SIZE(audio_map),
+};
+
+static char codec_name[16]; /* i2c-<HID>:00 with HID being 8 chars */
+
+static int bytcht_da7213_probe(struct platform_device *pdev)
+{
+	int ret_val = 0;
+	int i;
+	struct snd_soc_card *card;
+	struct sst_acpi_mach *mach;
+	const char *i2c_name = NULL;
+	int dai_index = 0;
+
+	mach = (&pdev->dev)->platform_data;
+	card = &bytcht_da7213_card;
+	card->dev = &pdev->dev;
+
+	/* fix index of codec dai */
+	dai_index = MERR_DPCM_COMPR + 1;
+	for (i = 0; i < ARRAY_SIZE(dailink); i++) {
+		if (!strcmp(dailink[i].codec_name, "i2c-DLGS7213:00")) {
+			dai_index = i;
+			break;
+		}
+	}
+
+	/* fixup codec name based on HID */
+	i2c_name = sst_acpi_find_name_from_hid(mach->id);
+	if (i2c_name != NULL) {
+		snprintf(codec_name, sizeof(codec_name),
+			"%s%s", "i2c-", i2c_name);
+		dailink[dai_index].codec_name = codec_name;
+	}
+
+	ret_val = devm_snd_soc_register_card(&pdev->dev, card);
+	if (ret_val) {
+		dev_err(&pdev->dev,
+			"snd_soc_register_card failed %d\n", ret_val);
+		return ret_val;
+	}
+	platform_set_drvdata(pdev, card);
+	return ret_val;
+}
+
+static struct platform_driver bytcht_da7213_driver = {
+	.driver = {
+		.name = "bytcht_da7213",
+	},
+	.probe = bytcht_da7213_probe,
+};
+module_platform_driver(bytcht_da7213_driver);
+
+MODULE_DESCRIPTION("ASoC Intel(R) Baytrail/Cherrytrail+DA7213 Machine driver");
+MODULE_AUTHOR("Pierre-Louis Bossart");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:bytcht_da7213");
diff --git a/sound/soc/intel/boards/bytcht_nocodec.c b/sound/soc/intel/boards/bytcht_nocodec.c
new file mode 100644
index 000000000000..89853eeaaf9d
--- /dev/null
+++ b/sound/soc/intel/boards/bytcht_nocodec.c
@@ -0,0 +1,208 @@
+/*
+ *  bytcht_nocodec.c - ASoc Machine driver for MinnowBoard Max and Up
+ *  to make I2S signals observable on the Low-Speed connector. Audio codec
+ *  is not managed by ASoC/DAPM
+ *
+ *  Copyright (C) 2015-2017 Intel Corp
+ *
+ *  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ *  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 <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include "../atom/sst-atom-controls.h"
+
+static const struct snd_soc_dapm_widget widgets[] = {
+	SND_SOC_DAPM_MIC("Mic", NULL),
+	SND_SOC_DAPM_SPK("Speaker", NULL),
+};
+
+static const struct snd_kcontrol_new controls[] = {
+	SOC_DAPM_PIN_SWITCH("Mic"),
+	SOC_DAPM_PIN_SWITCH("Speaker"),
+};
+
+static const struct snd_soc_dapm_route audio_map[] = {
+	{"ssp2 Tx", NULL, "codec_out0"},
+	{"ssp2 Tx", NULL, "codec_out1"},
+	{"codec_in0", NULL, "ssp2 Rx"},
+	{"codec_in1", NULL, "ssp2 Rx"},
+
+	{"ssp2 Rx", NULL, "Mic"},
+	{"Speaker", NULL, "ssp2 Tx"},
+};
+
+static int codec_fixup(struct snd_soc_pcm_runtime *rtd,
+			    struct snd_pcm_hw_params *params)
+{
+	struct snd_interval *rate = hw_param_interval(params,
+			SNDRV_PCM_HW_PARAM_RATE);
+	struct snd_interval *channels = hw_param_interval(params,
+						SNDRV_PCM_HW_PARAM_CHANNELS);
+	int ret;
+
+	/* The DSP will convert the FE rate to 48k, stereo, 24bits */
+	rate->min = rate->max = 48000;
+	channels->min = channels->max = 2;
+
+	/* set SSP2 to 24-bit */
+	params_set_format(params, SNDRV_PCM_FORMAT_S24_LE);
+
+	/*
+	 * Default mode for SSP configuration is TDM 4 slot, override config
+	 * with explicit setting to I2S 2ch 24-bit. The word length is set with
+	 * dai_set_tdm_slot() since there is no other API exposed
+	 */
+	ret = snd_soc_dai_set_fmt(rtd->cpu_dai,
+				  SND_SOC_DAIFMT_I2S     |
+				  SND_SOC_DAIFMT_NB_NF   |
+				  SND_SOC_DAIFMT_CBS_CFS);
+
+	if (ret < 0) {
+		dev_err(rtd->dev, "can't set format to I2S, err %d\n", ret);
+		return ret;
+	}
+
+	ret = snd_soc_dai_set_tdm_slot(rtd->cpu_dai, 0x3, 0x3, 2, 24);
+	if (ret < 0) {
+		dev_err(rtd->dev, "can't set I2S config, err %d\n", ret);
+		return ret;
+	}
+
+	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 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 aif1_ops = {
+	.startup = aif1_startup,
+};
+
+static struct snd_soc_dai_link dais[] = {
+	[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,
+		.nonatomic = true,
+		.dynamic = 1,
+		.dpcm_playback = 1,
+		.dpcm_capture = 1,
+		.ops = &aif1_ops,
+	},
+	[MERR_DPCM_DEEP_BUFFER] = {
+		.name = "Deep-Buffer Audio Port",
+		.stream_name = "Deep-Buffer Audio",
+		.cpu_dai_name = "deepbuffer-cpu-dai",
+		.codec_dai_name = "snd-soc-dummy-dai",
+		.codec_name = "snd-soc-dummy",
+		.platform_name = "sst-mfld-platform",
+		.ignore_suspend = 1,
+		.nonatomic = true,
+		.dynamic = 1,
+		.dpcm_playback = 1,
+		.ops = &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",
+	},
+	/* CODEC<->CODEC link */
+	/* back ends */
+	{
+		.name = "SSP2-LowSpeed Connector",
+		.id = 1,
+		.cpu_dai_name = "ssp2-port",
+		.platform_name = "sst-mfld-platform",
+		.no_pcm = 1,
+		.codec_dai_name = "snd-soc-dummy-dai",
+		.codec_name = "snd-soc-dummy",
+		.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
+						| SND_SOC_DAIFMT_CBS_CFS,
+		.be_hw_params_fixup = codec_fixup,
+		.ignore_suspend = 1,
+		.nonatomic = true,
+		.dpcm_playback = 1,
+		.dpcm_capture = 1,
+	},
+};
+
+/* SoC card */
+static struct snd_soc_card bytcht_nocodec_card = {
+	.name = "bytcht-nocodec",
+	.owner = THIS_MODULE,
+	.dai_link = dais,
+	.num_links = ARRAY_SIZE(dais),
+	.dapm_widgets = widgets,
+	.num_dapm_widgets = ARRAY_SIZE(widgets),
+	.dapm_routes = audio_map,
+	.num_dapm_routes = ARRAY_SIZE(audio_map),
+	.controls = controls,
+	.num_controls = ARRAY_SIZE(controls),
+	.fully_routed = true,
+};
+
+static int snd_bytcht_nocodec_mc_probe(struct platform_device *pdev)
+{
+	int ret_val = 0;
+
+	/* register the soc card */
+	bytcht_nocodec_card.dev = &pdev->dev;
+
+	ret_val = devm_snd_soc_register_card(&pdev->dev, &bytcht_nocodec_card);
+
+	if (ret_val) {
+		dev_err(&pdev->dev, "devm_snd_soc_register_card failed %d\n",
+			ret_val);
+		return ret_val;
+	}
+	platform_set_drvdata(pdev, &bytcht_nocodec_card);
+	return ret_val;
+}
+
+static struct platform_driver snd_bytcht_nocodec_mc_driver = {
+	.driver = {
+		.name = "bytcht_nocodec",
+	},
+	.probe = snd_bytcht_nocodec_mc_probe,
+};
+module_platform_driver(snd_bytcht_nocodec_mc_driver);
+
+MODULE_DESCRIPTION("ASoC Intel(R) Baytrail/Cherrytrail Nocodec Machine driver");
+MODULE_AUTHOR("Pierre-Louis Bossart <pierre-louis.bossart at linux.intel.com>");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:bytcht_nocodec");
diff --git a/sound/soc/intel/boards/bytcr_rt5640.c b/sound/soc/intel/boards/bytcr_rt5640.c
index 9e2a3404a836..4a76b099a508 100644
--- a/sound/soc/intel/boards/bytcr_rt5640.c
+++ b/sound/soc/intel/boards/bytcr_rt5640.c
@@ -19,6 +19,7 @@
 
 #include <linux/init.h>
 #include <linux/module.h>
+#include <linux/moduleparam.h>
 #include <linux/platform_device.h>
 #include <linux/acpi.h>
 #include <linux/device.h>
@@ -56,35 +57,88 @@ enum {
 struct byt_rt5640_private {
 	struct clk *mclk;
 };
+static bool is_bytcr;
 
 static unsigned long byt_rt5640_quirk = BYT_RT5640_MCLK_EN;
+static unsigned int quirk_override;
+module_param_named(quirk, quirk_override, uint, 0444);
+MODULE_PARM_DESC(quirk, "Board-specific quirk override");
 
 static void log_quirks(struct device *dev)
 {
-	if (BYT_RT5640_MAP(byt_rt5640_quirk) == BYT_RT5640_DMIC1_MAP)
-		dev_info(dev, "quirk DMIC1_MAP enabled");
-	if (BYT_RT5640_MAP(byt_rt5640_quirk) == BYT_RT5640_DMIC2_MAP)
-		dev_info(dev, "quirk DMIC2_MAP enabled");
-	if (BYT_RT5640_MAP(byt_rt5640_quirk) == BYT_RT5640_IN1_MAP)
-		dev_info(dev, "quirk IN1_MAP enabled");
-	if (BYT_RT5640_MAP(byt_rt5640_quirk) == BYT_RT5640_IN3_MAP)
-		dev_info(dev, "quirk IN3_MAP enabled");
-	if (byt_rt5640_quirk & BYT_RT5640_DMIC_EN)
-		dev_info(dev, "quirk DMIC enabled");
+	int map;
+	bool has_dmic = false;
+	bool has_mclk = false;
+	bool has_ssp0 = false;
+	bool has_ssp0_aif1 = false;
+	bool has_ssp0_aif2 = false;
+	bool has_ssp2_aif2 = false;
+
+	map = BYT_RT5640_MAP(byt_rt5640_quirk);
+	switch (map) {
+	case BYT_RT5640_DMIC1_MAP:
+		dev_info(dev, "quirk DMIC1_MAP enabled\n");
+		has_dmic = true;
+		break;
+	case BYT_RT5640_DMIC2_MAP:
+		dev_info(dev, "quirk DMIC2_MAP enabled\n");
+		has_dmic = true;
+		break;
+	case BYT_RT5640_IN1_MAP:
+		dev_info(dev, "quirk IN1_MAP enabled\n");
+		break;
+	case BYT_RT5640_IN3_MAP:
+		dev_info(dev, "quirk IN3_MAP enabled\n");
+		break;
+	default:
+		dev_err(dev, "quirk map 0x%x is not supported, microphone input will not work\n", map);
+		break;
+	}
+	if (byt_rt5640_quirk & BYT_RT5640_DMIC_EN) {
+		if (has_dmic)
+			dev_info(dev, "quirk DMIC enabled\n");
+		else
+			dev_err(dev, "quirk DMIC enabled but no DMIC input set, will be ignored\n");
+	}
 	if (byt_rt5640_quirk & BYT_RT5640_MONO_SPEAKER)
-		dev_info(dev, "quirk MONO_SPEAKER enabled");
-	if (byt_rt5640_quirk & BYT_RT5640_DIFF_MIC)
-		dev_info(dev, "quirk DIFF_MIC enabled");
-	if (byt_rt5640_quirk & BYT_RT5640_SSP2_AIF2)
-		dev_info(dev, "quirk SSP2_AIF2 enabled");
-	if (byt_rt5640_quirk & BYT_RT5640_SSP0_AIF1)
-		dev_info(dev, "quirk SSP0_AIF1 enabled");
-	if (byt_rt5640_quirk & BYT_RT5640_SSP0_AIF2)
-		dev_info(dev, "quirk SSP0_AIF2 enabled");
-	if (byt_rt5640_quirk & BYT_RT5640_MCLK_EN)
-		dev_info(dev, "quirk MCLK_EN enabled");
-	if (byt_rt5640_quirk & BYT_RT5640_MCLK_25MHZ)
-		dev_info(dev, "quirk MCLK_25MHZ enabled");
+		dev_info(dev, "quirk MONO_SPEAKER enabled\n");
+	if (byt_rt5640_quirk & BYT_RT5640_DIFF_MIC) {
+		if (!has_dmic)
+			dev_info(dev, "quirk DIFF_MIC enabled\n");
+		else
+			dev_info(dev, "quirk DIFF_MIC enabled but DMIC input selected, will be ignored\n");
+	}
+	if (byt_rt5640_quirk & BYT_RT5640_SSP0_AIF1) {
+		dev_info(dev, "quirk SSP0_AIF1 enabled\n");
+		has_ssp0 = true;
+		has_ssp0_aif1 = true;
+	}
+	if (byt_rt5640_quirk & BYT_RT5640_SSP0_AIF2) {
+		dev_info(dev, "quirk SSP0_AIF2 enabled\n");
+		has_ssp0 = true;
+		has_ssp0_aif2 = true;
+	}
+	if (byt_rt5640_quirk & BYT_RT5640_SSP2_AIF2) {
+		dev_info(dev, "quirk SSP2_AIF2 enabled\n");
+		has_ssp2_aif2 = true;
+	}
+	if (is_bytcr && !has_ssp0)
+		dev_err(dev, "Invalid routing, bytcr detected but no SSP0-based quirk, audio cannot work with SSP2 on bytcr\n");
+	if (has_ssp0_aif1 && has_ssp0_aif2)
+		dev_err(dev, "Invalid routing, SSP0 cannot be connected to both AIF1 and AIF2\n");
+	if (has_ssp0 && has_ssp2_aif2)
+		dev_err(dev, "Invalid routing, cannot have both SSP0 and SSP2 connected to codec\n");
+
+	if (byt_rt5640_quirk & BYT_RT5640_MCLK_EN) {
+		dev_info(dev, "quirk MCLK_EN enabled\n");
+		has_mclk = true;
+	}
+	if (byt_rt5640_quirk & BYT_RT5640_MCLK_25MHZ) {
+		if (has_mclk)
+			dev_info(dev, "quirk MCLK_25MHZ enabled\n");
+		else
+			dev_err(dev, "quirk MCLK_25MHZ enabled but quirk MCLK not selected, will be ignored\n");
+	}
 }
 
 
@@ -128,7 +182,7 @@ static int platform_clock_control(struct snd_soc_dapm_widget *w,
 			ret = clk_prepare_enable(priv->mclk);
 			if (ret < 0) {
 				dev_err(card->dev,
-					"could not configure MCLK state");
+					"could not configure MCLK state\n");
 				return ret;
 			}
 		}
@@ -710,8 +764,8 @@ static int snd_byt_rt5640_mc_probe(struct platform_device *pdev)
 	int i;
 	int dai_index;
 	struct byt_rt5640_private *priv;
-	bool is_bytcr = false;
 
+	is_bytcr = false;
 	priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_ATOMIC);
 	if (!priv)
 		return -ENOMEM;
@@ -806,6 +860,11 @@ static int snd_byt_rt5640_mc_probe(struct platform_device *pdev)
 
 	/* check quirks before creating card */
 	dmi_check_system(byt_rt5640_quirk_table);
+	if (quirk_override) {
+		dev_info(&pdev->dev, "Overriding quirk 0x%x => 0x%x\n",
+			 (unsigned int)byt_rt5640_quirk, quirk_override);
+		byt_rt5640_quirk = quirk_override;
+	}
 	log_quirks(&pdev->dev);
 
 	if ((byt_rt5640_quirk & BYT_RT5640_SSP2_AIF2) ||
diff --git a/sound/soc/intel/haswell/sst-haswell-ipc.c b/sound/soc/intel/haswell/sst-haswell-ipc.c
index a3459d1682a6..d33bdaf92c57 100644
--- a/sound/soc/intel/haswell/sst-haswell-ipc.c
+++ b/sound/soc/intel/haswell/sst-haswell-ipc.c
@@ -2000,10 +2000,8 @@ int sst_hsw_module_set_param(struct sst_hsw *hsw,
 	u32 param_size, char *param)
 {
 	int ret;
-	unsigned char *data = NULL;
 	u32 header = 0;
 	u32 payload_size = 0, transfer_parameter_size = 0;
-	dma_addr_t dma_addr = 0;
 	struct sst_hsw_transfer_parameter *parameter;
 	struct device *dev = hsw->dev;
 
@@ -2047,10 +2045,6 @@ int sst_hsw_module_set_param(struct sst_hsw *hsw,
 
 	kfree(parameter);
 
-	if (data)
-		dma_free_coherent(hsw->dsp->dma_dev,
-			param_size, (void *)data, dma_addr);
-
 	return ret;
 }
 
diff --git a/sound/soc/intel/skylake/bxt-sst.c b/sound/soc/intel/skylake/bxt-sst.c
index 15a063a403cc..f5e7dbb1ba39 100644
--- a/sound/soc/intel/skylake/bxt-sst.c
+++ b/sound/soc/intel/skylake/bxt-sst.c
@@ -25,7 +25,8 @@
 #include "skl-sst-ipc.h"
 
 #define BXT_BASEFW_TIMEOUT	3000
-#define BXT_INIT_TIMEOUT	500
+#define BXT_INIT_TIMEOUT	300
+#define BXT_ROM_INIT_TIMEOUT	70
 #define BXT_IPC_PURGE_FW	0x01004000
 
 #define BXT_ROM_INIT		0x5
@@ -45,6 +46,8 @@
 /* Delay before scheduling D0i3 entry */
 #define BXT_D0I3_DELAY 5000
 
+#define BXT_FW_ROM_INIT_RETRY 3
+
 static unsigned int bxt_get_errorcode(struct sst_dsp *ctx)
 {
 	 return sst_dsp_shim_read(ctx, BXT_ADSP_ERROR_CODE);
@@ -55,29 +58,15 @@ bxt_load_library(struct sst_dsp *ctx, struct skl_lib_info *linfo, int lib_count)
 {
 	struct snd_dma_buffer dmab;
 	struct skl_sst *skl = ctx->thread_context;
-	const struct firmware *fw = NULL;
 	struct firmware stripped_fw;
 	int ret = 0, i, dma_id, stream_tag;
 
 	/* library indices start from 1 to N. 0 represents base FW */
 	for (i = 1; i < lib_count; i++) {
-		ret = request_firmware(&fw, linfo[i].name, ctx->dev);
-		if (ret < 0) {
-			dev_err(ctx->dev, "Request lib %s failed:%d\n",
-					linfo[i].name, ret);
-			return ret;
-		}
-
-		if (skl->is_first_boot) {
-			ret = snd_skl_parse_uuids(ctx, fw,
+		ret = skl_prepare_lib_load(skl, &skl->lib_info[i], &stripped_fw,
 					BXT_ADSP_FW_BIN_HDR_OFFSET, i);
-			if (ret < 0)
-				goto load_library_failed;
-		}
-
-		stripped_fw.data = fw->data;
-		stripped_fw.size = fw->size;
-		skl_dsp_strip_extended_manifest(&stripped_fw);
+		if (ret < 0)
+			goto load_library_failed;
 
 		stream_tag = ctx->dsp_ops.prepare(ctx->dev, 0x40,
 					stripped_fw.size, &dmab);
@@ -92,21 +81,19 @@ bxt_load_library(struct sst_dsp *ctx, struct skl_lib_info *linfo, int lib_count)
 		memcpy(dmab.area, stripped_fw.data, stripped_fw.size);
 
 		ctx->dsp_ops.trigger(ctx->dev, true, stream_tag);
-		ret = skl_sst_ipc_load_library(&skl->ipc, dma_id, i);
+		ret = skl_sst_ipc_load_library(&skl->ipc, dma_id, i, true);
 		if (ret < 0)
 			dev_err(ctx->dev, "IPC Load Lib for %s fail: %d\n",
 					linfo[i].name, ret);
 
 		ctx->dsp_ops.trigger(ctx->dev, false, stream_tag);
 		ctx->dsp_ops.cleanup(ctx->dev, &dmab, stream_tag);
-		release_firmware(fw);
-		fw = NULL;
 	}
 
 	return ret;
 
 load_library_failed:
-	release_firmware(fw);
+	skl_release_library(linfo, lib_count);
 	return ret;
 }
 
@@ -156,7 +143,7 @@ static int sst_bxt_prepare_fw(struct sst_dsp *ctx,
 					SKL_ADSP_REG_HIPCIE_DONE,
 					BXT_INIT_TIMEOUT, "HIPCIE Done");
 	if (ret < 0) {
-		dev_err(ctx->dev, "Timout for Purge Request%d\n", ret);
+		dev_err(ctx->dev, "Timeout for Purge Request%d\n", ret);
 		goto base_fw_load_failed;
 	}
 
@@ -173,7 +160,7 @@ static int sst_bxt_prepare_fw(struct sst_dsp *ctx,
 
 	/* Step 7: Wait for ROM init */
 	ret = sst_dsp_register_poll(ctx, BXT_ADSP_FW_STATUS, SKL_FW_STS_MASK,
-			SKL_FW_INIT, BXT_INIT_TIMEOUT, "ROM Load");
+			SKL_FW_INIT, BXT_ROM_INIT_TIMEOUT, "ROM Load");
 	if (ret < 0) {
 		dev_err(ctx->dev, "Timeout for ROM init, ret:%d\n", ret);
 		goto base_fw_load_failed;
@@ -206,18 +193,16 @@ static int bxt_load_base_firmware(struct sst_dsp *ctx)
 {
 	struct firmware stripped_fw;
 	struct skl_sst *skl = ctx->thread_context;
-	int ret;
+	int ret, i;
 
-	ret = request_firmware(&ctx->fw, ctx->fw_name, ctx->dev);
-	if (ret < 0) {
-		dev_err(ctx->dev, "Request firmware failed %d\n", ret);
-		goto sst_load_base_firmware_failed;
+	if (ctx->fw == NULL) {
+		ret = request_firmware(&ctx->fw, ctx->fw_name, ctx->dev);
+		if (ret < 0) {
+			dev_err(ctx->dev, "Request firmware failed %d\n", ret);
+			return ret;
+		}
 	}
 
-	/* check for extended manifest */
-	if (ctx->fw == NULL)
-		goto sst_load_base_firmware_failed;
-
 	/* prase uuids on first boot */
 	if (skl->is_first_boot) {
 		ret = snd_skl_parse_uuids(ctx, ctx->fw, BXT_ADSP_FW_BIN_HDR_OFFSET, 0);
@@ -229,18 +214,20 @@ static int bxt_load_base_firmware(struct sst_dsp *ctx)
 	stripped_fw.size = ctx->fw->size;
 	skl_dsp_strip_extended_manifest(&stripped_fw);
 
-	ret = sst_bxt_prepare_fw(ctx, stripped_fw.data, stripped_fw.size);
-	/* Retry Enabling core and ROM load. Retry seemed to help */
-	if (ret < 0) {
+
+	for (i = 0; i < BXT_FW_ROM_INIT_RETRY; i++) {
 		ret = sst_bxt_prepare_fw(ctx, stripped_fw.data, stripped_fw.size);
-		if (ret < 0) {
-			dev_err(ctx->dev, "Error code=0x%x: FW status=0x%x\n",
+		if (ret == 0)
+			break;
+	}
+
+	if (ret < 0) {
+		dev_err(ctx->dev, "Error code=0x%x: FW status=0x%x\n",
 			sst_dsp_shim_read(ctx, BXT_ADSP_ERROR_CODE),
 			sst_dsp_shim_read(ctx, BXT_ADSP_FW_STATUS));
 
-			dev_err(ctx->dev, "Core En/ROM load fail:%d\n", ret);
-			goto sst_load_base_firmware_failed;
-		}
+		dev_err(ctx->dev, "Core En/ROM load fail:%d\n", ret);
+		goto sst_load_base_firmware_failed;
 	}
 
 	ret = sst_transfer_fw_host_dma(ctx);
@@ -265,8 +252,11 @@ static int bxt_load_base_firmware(struct sst_dsp *ctx)
 		}
 	}
 
+	return ret;
+
 sst_load_base_firmware_failed:
 	release_firmware(ctx->fw);
+	ctx->fw = NULL;
 	return ret;
 }
 
@@ -428,6 +418,7 @@ static int bxt_set_dsp_D0(struct sst_dsp *ctx, unsigned int core_id)
 				return ret;
 			}
 		}
+		skl->cores.state[core_id] = SKL_DSP_RUNNING;
 		return ret;
 	}
 
@@ -514,11 +505,22 @@ static int bxt_set_dsp_D3(struct sst_dsp *ctx, unsigned int core_id)
 
 	ret = skl_ipc_set_dx(&skl->ipc, BXT_INSTANCE_ID,
 				BXT_BASE_FW_MODULE_ID, &dx);
-	if (ret < 0)
+	if (ret < 0) {
 		dev_err(ctx->dev,
 		"Failed to set DSP to D3:core id = %d;Continue reset\n",
 		core_id);
+		/*
+		 * In case of D3 failure, re-download the firmware, so set
+		 * fw_loaded to false.
+		 */
+		skl->fw_loaded = false;
+	}
 
+	if (core_id == SKL_DSP_CORE0_ID) {
+		/* disable Interrupt */
+		skl_ipc_op_int_disable(ctx);
+		skl_ipc_int_disable(ctx);
+	}
 	ret = skl_dsp_disable_core(ctx, core_mask);
 	if (ret < 0) {
 		dev_err(ctx->dev, "Failed to disable core %d\n", ret);
@@ -560,23 +562,14 @@ int bxt_sst_dsp_init(struct device *dev, void __iomem *mmio_base, int irq,
 	struct sst_dsp *sst;
 	int ret;
 
-	skl = devm_kzalloc(dev, sizeof(*skl), GFP_KERNEL);
-	if (skl == NULL)
-		return -ENOMEM;
-
-	skl->dev = dev;
-	skl_dev.thread_context = skl;
-	INIT_LIST_HEAD(&skl->uuid_list);
-
-	skl->dsp = skl_dsp_ctx_init(dev, &skl_dev, irq);
-	if (!skl->dsp) {
-		dev_err(skl->dev, "skl_dsp_ctx_init failed\n");
-		return -ENODEV;
+	ret = skl_sst_ctx_init(dev, irq, fw_name, dsp_ops, dsp, &skl_dev);
+	if (ret < 0) {
+		dev_err(dev, "%s: no device\n", __func__);
+		return ret;
 	}
 
+	skl = *dsp;
 	sst = skl->dsp;
-	sst->fw_name = fw_name;
-	sst->dsp_ops = dsp_ops;
 	sst->fw_ops = bxt_fw_ops;
 	sst->addr.lpe = mmio_base;
 	sst->addr.shim = mmio_base;
@@ -584,24 +577,15 @@ int bxt_sst_dsp_init(struct device *dev, void __iomem *mmio_base, int irq,
 	sst_dsp_mailbox_init(sst, (BXT_ADSP_SRAM0_BASE + SKL_ADSP_W0_STAT_SZ),
 			SKL_ADSP_W0_UP_SZ, BXT_ADSP_SRAM1_BASE, SKL_ADSP_W1_SZ);
 
-	INIT_LIST_HEAD(&sst->module_list);
-	ret = skl_ipc_init(dev, skl);
-	if (ret)
-		return ret;
-
 	/* set the D0i3 check */
 	skl->ipc.ops.check_dsp_lp_on = skl_ipc_check_D0i0;
 
 	skl->cores.count = 2;
 	skl->boot_complete = false;
 	init_waitqueue_head(&skl->boot_wait);
-	skl->is_first_boot = true;
 	INIT_DELAYED_WORK(&skl->d0i3.work, bxt_set_dsp_D0i3);
 	skl->d0i3.state = SKL_DSP_D0I3_NONE;
 
-	if (dsp)
-		*dsp = skl;
-
 	return 0;
 }
 EXPORT_SYMBOL_GPL(bxt_sst_dsp_init);
@@ -635,6 +619,10 @@ EXPORT_SYMBOL_GPL(bxt_sst_init_fw);
 
 void bxt_sst_dsp_cleanup(struct device *dev, struct skl_sst *ctx)
 {
+
+	skl_release_library(ctx->lib_info, ctx->lib_count);
+	if (ctx->dsp->fw)
+		release_firmware(ctx->dsp->fw);
 	skl_freeup_uuid_list(ctx);
 	skl_ipc_free(&ctx->ipc);
 	ctx->dsp->cl_dev.ops.cl_cleanup_controller(ctx->dsp);
diff --git a/sound/soc/intel/skylake/skl-messages.c b/sound/soc/intel/skylake/skl-messages.c
index e66870474f10..ab1adc0c9cc3 100644
--- a/sound/soc/intel/skylake/skl-messages.c
+++ b/sound/soc/intel/skylake/skl-messages.c
@@ -58,7 +58,7 @@ static int skl_free_dma_buf(struct device *dev, struct snd_dma_buffer *dmab)
 #define NOTIFICATION_MASK 0xf
 
 /* disable notfication for underruns/overruns from firmware module */
-static void skl_dsp_enable_notification(struct skl_sst *ctx, bool enable)
+void skl_dsp_enable_notification(struct skl_sst *ctx, bool enable)
 {
 	struct notification_mask mask;
 	struct skl_ipc_large_config_msg	msg = {0};
@@ -209,7 +209,7 @@ static const struct skl_dsp_ops dsp_ops[] = {
 	{
 		.id = 0x9d71,
 		.loader_ops = skl_get_loader_ops,
-		.init = skl_sst_dsp_init,
+		.init = kbl_sst_dsp_init,
 		.init_fw = skl_sst_init_fw,
 		.cleanup = skl_sst_dsp_cleanup
 	},
@@ -274,6 +274,7 @@ int skl_init_dsp(struct skl *skl)
 	if (ret < 0)
 		return ret;
 
+	skl->skl_sst->dsp_ops = ops;
 	dev_dbg(bus->dev, "dsp registration status=%d\n", ret);
 
 	return ret;
@@ -284,16 +285,11 @@ int skl_free_dsp(struct skl *skl)
 	struct hdac_ext_bus *ebus = &skl->ebus;
 	struct hdac_bus *bus = ebus_to_hbus(ebus);
 	struct skl_sst *ctx = skl->skl_sst;
-	const struct skl_dsp_ops *ops;
 
 	/* disable  ppcap interrupt */
 	snd_hdac_ext_bus_ppcap_int_enable(&skl->ebus, false);
 
-	ops = skl_get_dsp_ops(skl->pci->device);
-	if (!ops)
-		return -EIO;
-
-	ops->cleanup(bus->dev, ctx);
+	ctx->dsp_ops->cleanup(bus->dev, ctx);
 
 	if (ctx->dsp->addr.lpe)
 		iounmap(ctx->dsp->addr.lpe);
@@ -866,7 +862,7 @@ static void skl_clear_module_state(struct skl_module_pin *mpin, int max,
 	}
 
 	if (!found)
-		mcfg->m_state = SKL_MODULE_UNINIT;
+		mcfg->m_state = SKL_MODULE_INIT_DONE;
 	return;
 }
 
@@ -1098,7 +1094,7 @@ int skl_delete_pipe(struct skl_sst *ctx, struct skl_pipe *pipe)
 	dev_dbg(ctx->dev, "%s: pipe = %d\n", __func__, pipe->ppl_id);
 
 	/* If pipe is started, do stop the pipe in FW. */
-	if (pipe->state > SKL_PIPE_STARTED) {
+	if (pipe->state >= SKL_PIPE_STARTED) {
 		ret = skl_set_pipe_state(ctx, pipe, PPL_PAUSED);
 		if (ret < 0) {
 			dev_err(ctx->dev, "Failed to stop pipeline\n");
diff --git a/sound/soc/intel/skylake/skl-nhlt.c b/sound/soc/intel/skylake/skl-nhlt.c
index 7eb9c419dc7f..e3f06672fd6d 100644
--- a/sound/soc/intel/skylake/skl-nhlt.c
+++ b/sound/soc/intel/skylake/skl-nhlt.c
@@ -24,8 +24,6 @@
 static u8 OSC_UUID[16] = {0x6E, 0x88, 0x9F, 0xA6, 0xEB, 0x6C, 0x94, 0x45,
 				0xA4, 0x1F, 0x7B, 0x5D, 0xCE, 0x24, 0xC5, 0x53};
 
-#define DSDT_NHLT_PATH "\\_SB.PCI0.HDAS"
-
 struct nhlt_acpi_table *skl_nhlt_init(struct device *dev)
 {
 	acpi_handle handle;
@@ -33,8 +31,9 @@ struct nhlt_acpi_table *skl_nhlt_init(struct device *dev)
 	struct nhlt_resource_desc  *nhlt_ptr = NULL;
 	struct nhlt_acpi_table *nhlt_table = NULL;
 
-	if (ACPI_FAILURE(acpi_get_handle(NULL, DSDT_NHLT_PATH, &handle))) {
-		dev_err(dev, "Requested NHLT device not found\n");
+	handle = ACPI_HANDLE(dev);
+	if (!handle) {
+		dev_err(dev, "Didn't find ACPI_HANDLE\n");
 		return NULL;
 	}
 
diff --git a/sound/soc/intel/skylake/skl-pcm.c b/sound/soc/intel/skylake/skl-pcm.c
index e12520e142ff..e91bbcffc856 100644
--- a/sound/soc/intel/skylake/skl-pcm.c
+++ b/sound/soc/intel/skylake/skl-pcm.c
@@ -21,6 +21,7 @@
 
 #include <linux/pci.h>
 #include <linux/pm_runtime.h>
+#include <linux/delay.h>
 #include <sound/pcm_params.h>
 #include <sound/soc.h>
 #include "skl.h"
@@ -155,7 +156,7 @@ int skl_pcm_host_dma_prepare(struct device *dev, struct skl_pipe_params *params)
 	snd_hdac_ext_stream_decouple(ebus, stream, true);
 
 	format_val = snd_hdac_calc_stream_format(params->s_freq,
-				params->ch, params->format, 32, 0);
+			params->ch, params->format, params->host_bps, 0);
 
 	dev_dbg(dev, "format_val=%d, rate=%d, ch=%d, format=%d\n",
 		format_val, params->s_freq, params->ch, params->format);
@@ -190,8 +191,8 @@ int skl_pcm_link_dma_prepare(struct device *dev, struct skl_pipe_params *params)
 
 	stream = stream_to_hdac_ext_stream(hstream);
 	snd_hdac_ext_stream_decouple(ebus, stream, true);
-	format_val = snd_hdac_calc_stream_format(params->s_freq,
-				params->ch, params->format, 24, 0);
+	format_val = snd_hdac_calc_stream_format(params->s_freq, params->ch,
+					params->format, params->link_bps, 0);
 
 	dev_dbg(dev, "format_val=%d, rate=%d, ch=%d, format=%d\n",
 		format_val, params->s_freq, params->ch, params->format);
@@ -262,23 +263,6 @@ static int skl_pcm_open(struct snd_pcm_substream *substream,
 	return 0;
 }
 
-static int skl_be_prepare(struct snd_pcm_substream *substream,
-		struct snd_soc_dai *dai)
-{
-	struct skl *skl = get_skl_ctx(dai->dev);
-	struct skl_sst *ctx = skl->skl_sst;
-	struct skl_module_cfg *mconfig;
-
-	if (dai->playback_widget->power || dai->capture_widget->power)
-		return 0;
-
-	mconfig = skl_tplg_be_get_cpr_module(dai, substream->stream);
-	if (mconfig == NULL)
-		return -EINVAL;
-
-	return skl_dsp_set_dma_control(ctx, mconfig);
-}
-
 static int skl_pcm_prepare(struct snd_pcm_substream *substream,
 		struct snd_soc_dai *dai)
 {
@@ -326,6 +310,11 @@ static int skl_pcm_hw_params(struct snd_pcm_substream *substream,
 	p_params.host_dma_id = dma_id;
 	p_params.stream = substream->stream;
 	p_params.format = params_format(params);
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+		p_params.host_bps = dai->driver->playback.sig_bits;
+	else
+		p_params.host_bps = dai->driver->capture.sig_bits;
+
 
 	m_cfg = skl_tplg_fe_get_cpr_module(dai, p_params.stream);
 	if (m_cfg)
@@ -564,6 +553,11 @@ static int skl_link_hw_params(struct snd_pcm_substream *substream,
 	p_params.link_index = link->index;
 	p_params.format = params_format(params);
 
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+		p_params.link_bps = codec_dai->driver->playback.sig_bits;
+	else
+		p_params.link_bps = codec_dai->driver->capture.sig_bits;
+
 	return skl_tplg_be_update_params(dai, &p_params);
 }
 
@@ -649,7 +643,6 @@ static struct snd_soc_dai_ops skl_dmic_dai_ops = {
 
 static struct snd_soc_dai_ops skl_be_ssp_dai_ops = {
 	.hw_params = skl_be_hw_params,
-	.prepare = skl_be_prepare,
 };
 
 static struct snd_soc_dai_ops skl_link_dai_ops = {
@@ -670,6 +663,7 @@ static struct snd_soc_dai_driver skl_platform_dai[] = {
 		.rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_8000,
 		.formats = SNDRV_PCM_FMTBIT_S16_LE |
 			SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE,
+		.sig_bits = 32,
 	},
 	.capture = {
 		.stream_name = "System Capture",
@@ -677,6 +671,7 @@ static struct snd_soc_dai_driver skl_platform_dai[] = {
 		.channels_max = HDA_STEREO,
 		.rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_16000,
 		.formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE,
+		.sig_bits = 32,
 	},
 },
 {
@@ -688,6 +683,7 @@ static struct snd_soc_dai_driver skl_platform_dai[] = {
 		.channels_max = HDA_QUAD,
 		.rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_16000,
 		.formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE,
+		.sig_bits = 32,
 	},
 },
 {
@@ -699,6 +695,7 @@ static struct snd_soc_dai_driver skl_platform_dai[] = {
 		.channels_max = HDA_STEREO,
 		.rates = SNDRV_PCM_RATE_48000,
 		.formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE,
+		.sig_bits = 32,
 	},
 },
 {
@@ -710,6 +707,7 @@ static struct snd_soc_dai_driver skl_platform_dai[] = {
 		.channels_max = HDA_STEREO,
 		.rates = SNDRV_PCM_RATE_48000,
 		.formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE,
+		.sig_bits = 32,
 	},
 },
 {
@@ -721,6 +719,7 @@ static struct snd_soc_dai_driver skl_platform_dai[] = {
 		.channels_max = HDA_QUAD,
 		.rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_16000,
 		.formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE,
+		.sig_bits = 32,
 	},
 },
 {
@@ -736,6 +735,7 @@ static struct snd_soc_dai_driver skl_platform_dai[] = {
 			SNDRV_PCM_RATE_192000,
 		.formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE |
 			SNDRV_PCM_FMTBIT_S32_LE,
+		.sig_bits = 32,
 	},
 },
 {
@@ -751,6 +751,7 @@ static struct snd_soc_dai_driver skl_platform_dai[] = {
 			SNDRV_PCM_RATE_192000,
 		.formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE |
 			SNDRV_PCM_FMTBIT_S32_LE,
+		.sig_bits = 32,
 	},
 },
 {
@@ -766,6 +767,7 @@ static struct snd_soc_dai_driver skl_platform_dai[] = {
 			SNDRV_PCM_RATE_192000,
 		.formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE |
 			SNDRV_PCM_FMTBIT_S32_LE,
+		.sig_bits = 32,
 	},
 },
 
@@ -949,14 +951,12 @@ static struct snd_soc_dai_driver skl_platform_dai[] = {
 
 static int skl_platform_open(struct snd_pcm_substream *substream)
 {
-	struct snd_pcm_runtime *runtime;
 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
 	struct snd_soc_dai_link *dai_link = rtd->dai_link;
 
 	dev_dbg(rtd->cpu_dai->dev, "In %s:%s\n", __func__,
 					dai_link->cpu_dai_name);
 
-	runtime = substream->runtime;
 	snd_soc_set_runtime_hwparams(substream, &azx_pcm_hw);
 
 	return 0;
@@ -1062,13 +1062,31 @@ static snd_pcm_uframes_t skl_platform_pcm_pointer
 	 * HAD space reflects the actual data that is transferred.
 	 * Use the position buffer for capture, as DPIB write gets
 	 * completed earlier than the actual data written to the DDR.
+	 *
+	 * For capture stream following workaround is required to fix the
+	 * incorrect position reporting.
+	 *
+	 * 1. Wait for 20us before reading the DMA position in buffer once
+	 * the interrupt is generated for stream completion as update happens
+	 * on the HDA frame boundary i.e. 20.833uSec.
+	 * 2. Read DPIB register to flush the DMA position value. This dummy
+	 * read is required to flush DMA position value.
+	 * 3. Read the DMA Position-in-Buffer. This value now will be equal to
+	 * or greater than period boundary.
 	 */
-	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
 		pos = readl(ebus->bus.remap_addr + AZX_REG_VS_SDXDPIB_XBASE +
 				(AZX_REG_VS_SDXDPIB_XINTERVAL *
 				hdac_stream(hstream)->index));
-	else
+	} else {
+		udelay(20);
+		readl(ebus->bus.remap_addr +
+				AZX_REG_VS_SDXDPIB_XBASE +
+				(AZX_REG_VS_SDXDPIB_XINTERVAL *
+				 hdac_stream(hstream)->index));
 		pos = snd_hdac_stream_get_pos_posbuf(hdac_stream(hstream));
+	}
 
 	if (pos >= hdac_stream(hstream)->bufsize)
 		pos = 0;
@@ -1165,7 +1183,7 @@ static int skl_pcm_new(struct snd_soc_pcm_runtime *rtd)
 						snd_dma_pci_data(skl->pci),
 						size, MAX_PREALLOC_SIZE);
 		if (retval) {
-			dev_err(dai->dev, "dma buffer allocationf fail\n");
+			dev_err(dai->dev, "dma buffer allocation fail\n");
 			return retval;
 		}
 	}
@@ -1173,29 +1191,52 @@ static int skl_pcm_new(struct snd_soc_pcm_runtime *rtd)
 	return retval;
 }
 
+static int skl_get_module_info(struct skl *skl, struct skl_module_cfg *mconfig)
+{
+	struct skl_sst *ctx = skl->skl_sst;
+	struct uuid_module *module;
+	uuid_le *uuid_mod;
+
+	uuid_mod = (uuid_le *)mconfig->guid;
+
+	if (list_empty(&ctx->uuid_list)) {
+		dev_err(ctx->dev, "Module list is empty\n");
+		return -EIO;
+	}
+
+	list_for_each_entry(module, &ctx->uuid_list, list) {
+		if (uuid_le_cmp(*uuid_mod, module->uuid) == 0) {
+			mconfig->id.module_id = module->id;
+			mconfig->is_loadable = module->is_loadable;
+			return 0;
+		}
+	}
+
+	return -EIO;
+}
+
 static int skl_populate_modules(struct skl *skl)
 {
 	struct skl_pipeline *p;
 	struct skl_pipe_module *m;
 	struct snd_soc_dapm_widget *w;
 	struct skl_module_cfg *mconfig;
-	int ret;
+	int ret = 0;
 
 	list_for_each_entry(p, &skl->ppl_list, node) {
 		list_for_each_entry(m, &p->pipe->w_list, node) {
-
 			w = m->w;
 			mconfig = w->priv;
 
-			ret = snd_skl_get_module_info(skl->skl_sst, mconfig);
+			ret = skl_get_module_info(skl, mconfig);
 			if (ret < 0) {
 				dev_err(skl->skl_sst->dev,
-					"query module info failed:%d\n", ret);
-				goto err;
+					"query module info failed\n");
+				return ret;
 			}
 		}
 	}
-err:
+
 	return ret;
 }
 
@@ -1232,6 +1273,7 @@ static int skl_platform_soc_probe(struct snd_soc_platform *platform)
 		}
 		skl_populate_modules(skl);
 		skl->skl_sst->update_d0i3c = skl_update_d0i3c;
+		skl_dsp_enable_notification(skl->skl_sst, false);
 	}
 	pm_runtime_mark_last_busy(platform->dev);
 	pm_runtime_put_autosuspend(platform->dev);
@@ -1256,6 +1298,7 @@ int skl_platform_register(struct device *dev)
 	struct skl *skl = ebus_to_skl(ebus);
 
 	INIT_LIST_HEAD(&skl->ppl_list);
+	INIT_LIST_HEAD(&skl->bind_list);
 
 	ret = snd_soc_register_platform(dev, &skl_platform_drv);
 	if (ret) {
@@ -1276,6 +1319,17 @@ int skl_platform_register(struct device *dev)
 
 int skl_platform_unregister(struct device *dev)
 {
+	struct hdac_ext_bus *ebus = dev_get_drvdata(dev);
+	struct skl *skl = ebus_to_skl(ebus);
+	struct skl_module_deferred_bind *modules, *tmp;
+
+	if (!list_empty(&skl->bind_list)) {
+		list_for_each_entry_safe(modules, tmp, &skl->bind_list, node) {
+			list_del(&modules->node);
+			kfree(modules);
+		}
+	}
+
 	snd_soc_unregister_component(dev);
 	snd_soc_unregister_platform(dev);
 	return 0;
diff --git a/sound/soc/intel/skylake/skl-sst-cldma.c b/sound/soc/intel/skylake/skl-sst-cldma.c
index c9f6d87381db..d2b1d60fec02 100644
--- a/sound/soc/intel/skylake/skl-sst-cldma.c
+++ b/sound/soc/intel/skylake/skl-sst-cldma.c
@@ -164,7 +164,7 @@ static void skl_cldma_cleanup(struct sst_dsp  *ctx)
 	ctx->dsp_ops.free_dma_buf(ctx->dev, &ctx->cl_dev.dmab_bdl);
 }
 
-static int skl_cldma_wait_interruptible(struct sst_dsp *ctx)
+int skl_cldma_wait_interruptible(struct sst_dsp *ctx)
 {
 	int ret = 0;
 
@@ -243,9 +243,14 @@ static void skl_cldma_fill_buffer(struct sst_dsp *ctx, unsigned int size,
  * 2. Polling on fw register to identify if data left to transferred doesn't
  *    fill the ring buffer. Caller takes care of polling the required status
  *    register to identify the transfer status.
+ * 3. if wait flag is set, waits for DBL interrupt to copy the next chunk till
+ *    bytes_left is 0.
+ *    if wait flag is not set, doesn't wait for BDL interrupt. after ccopying
+ *    the first chunk return the no of bytes_left to be copied.
  */
 static int
-skl_cldma_copy_to_buf(struct sst_dsp *ctx, const void *bin, u32 total_size)
+skl_cldma_copy_to_buf(struct sst_dsp *ctx, const void *bin,
+			u32 total_size, bool wait)
 {
 	int ret = 0;
 	bool start = true;
@@ -272,13 +277,14 @@ skl_cldma_copy_to_buf(struct sst_dsp *ctx, const void *bin, u32 total_size)
 			size = ctx->cl_dev.bufsize;
 			skl_cldma_fill_buffer(ctx, size, curr_pos, true, start);
 
-			start = false;
-			ret = skl_cldma_wait_interruptible(ctx);
-			if (ret < 0) {
-				skl_cldma_stop(ctx);
-				return ret;
+			if (wait) {
+				start = false;
+				ret = skl_cldma_wait_interruptible(ctx);
+				if (ret < 0) {
+					skl_cldma_stop(ctx);
+					return ret;
+				}
 			}
-
 		} else {
 			skl_cldma_int_disable(ctx);
 
@@ -298,9 +304,11 @@ skl_cldma_copy_to_buf(struct sst_dsp *ctx, const void *bin, u32 total_size)
 		}
 		bytes_left -= size;
 		curr_pos = curr_pos + size;
+		if (!wait)
+			return bytes_left;
 	}
 
-	return ret;
+	return bytes_left;
 }
 
 void skl_cldma_process_intr(struct sst_dsp *ctx)
diff --git a/sound/soc/intel/skylake/skl-sst-cldma.h b/sound/soc/intel/skylake/skl-sst-cldma.h
index 99e4c86b6358..5b730a1a0ae4 100644
--- a/sound/soc/intel/skylake/skl-sst-cldma.h
+++ b/sound/soc/intel/skylake/skl-sst-cldma.h
@@ -213,7 +213,7 @@ struct skl_cl_dev_ops {
 	void (*cl_trigger)(struct sst_dsp  *ctx, bool enable);
 	void (*cl_cleanup_controller)(struct sst_dsp  *ctx);
 	int (*cl_copy_to_dmabuf)(struct sst_dsp *ctx,
-			const void *bin, u32 size);
+			const void *bin, u32 size, bool wait);
 	void (*cl_stop_dma)(struct sst_dsp *ctx);
 };
 
diff --git a/sound/soc/intel/skylake/skl-sst-dsp.c b/sound/soc/intel/skylake/skl-sst-dsp.c
index c3deefab65d6..08332723c700 100644
--- a/sound/soc/intel/skylake/skl-sst-dsp.c
+++ b/sound/soc/intel/skylake/skl-sst-dsp.c
@@ -355,12 +355,13 @@ int skl_dsp_get_core(struct sst_dsp *ctx, unsigned int core_id)
 		ret = ctx->fw_ops.set_state_D0(ctx, core_id);
 		if (ret < 0) {
 			dev_err(ctx->dev, "unable to get core%d\n", core_id);
-			return ret;
+			goto out;
 		}
 	}
 
 	skl->cores.usage_count[core_id]++;
 
+out:
 	dev_dbg(ctx->dev, "core id %d state %d usage_count %d\n",
 			core_id, skl->cores.state[core_id],
 			skl->cores.usage_count[core_id]);
@@ -379,7 +380,8 @@ int skl_dsp_put_core(struct sst_dsp *ctx, unsigned int core_id)
 		return -EINVAL;
 	}
 
-	if (--skl->cores.usage_count[core_id] == 0) {
+	if ((--skl->cores.usage_count[core_id] == 0) &&
+		(skl->cores.state[core_id] != SKL_DSP_RESET)) {
 		ret = ctx->fw_ops.set_state_D3(ctx, core_id);
 		if (ret < 0) {
 			dev_err(ctx->dev, "unable to put core %d: %d\n",
diff --git a/sound/soc/intel/skylake/skl-sst-dsp.h b/sound/soc/intel/skylake/skl-sst-dsp.h
index 849410d0823e..eba20d37ba8c 100644
--- a/sound/soc/intel/skylake/skl-sst-dsp.h
+++ b/sound/soc/intel/skylake/skl-sst-dsp.h
@@ -17,13 +17,15 @@
 #define __SKL_SST_DSP_H__
 
 #include <linux/interrupt.h>
+#include <linux/uuid.h>
+#include <linux/firmware.h>
 #include <sound/memalloc.h>
 #include "skl-sst-cldma.h"
-#include "skl-topology.h"
 
 struct sst_dsp;
 struct skl_sst;
 struct sst_dsp_device;
+struct skl_lib_info;
 
 /* Intel HD Audio General DSP Registers */
 #define SKL_ADSP_GEN_BASE		0x0
@@ -144,7 +146,7 @@ struct skl_dsp_fw_ops {
 	int (*load_fw)(struct sst_dsp  *ctx);
 	/* FW module parser/loader */
 	int (*load_library)(struct sst_dsp *ctx,
-		struct skl_lib_info *linfo, int count);
+		struct skl_lib_info *linfo, int lib_count);
 	int (*parse_fw)(struct sst_dsp *ctx);
 	int (*set_state_D0)(struct sst_dsp *ctx, unsigned int core_id);
 	int (*set_state_D3)(struct sst_dsp *ctx, unsigned int core_id);
@@ -172,6 +174,19 @@ struct skl_dsp_loader_ops {
 				 int stream_tag);
 };
 
+#define MAX_INSTANCE_BUFF 2
+
+struct uuid_module {
+	uuid_le uuid;
+	int id;
+	int is_loadable;
+	int max_instance;
+	u64 pvt_id[MAX_INSTANCE_BUFF];
+	int *instance_id;
+
+	struct list_head list;
+};
+
 struct skl_load_module_info {
 	u16 mod_id;
 	const struct firmware *fw;
@@ -186,6 +201,7 @@ struct skl_module_table {
 void skl_cldma_process_intr(struct sst_dsp *ctx);
 void skl_cldma_int_disable(struct sst_dsp *ctx);
 int skl_cldma_prepare(struct sst_dsp *ctx);
+int skl_cldma_wait_interruptible(struct sst_dsp *ctx);
 
 void skl_dsp_set_state_locked(struct sst_dsp *ctx, int state);
 struct sst_dsp *skl_dsp_ctx_init(struct device *dev,
@@ -214,6 +230,9 @@ int skl_dsp_boot(struct sst_dsp *ctx);
 int skl_sst_dsp_init(struct device *dev, void __iomem *mmio_base, int irq,
 		const char *fw_name, struct skl_dsp_loader_ops dsp_ops,
 		struct skl_sst **dsp);
+int kbl_sst_dsp_init(struct device *dev, void __iomem *mmio_base, int irq,
+		const char *fw_name, struct skl_dsp_loader_ops dsp_ops,
+		struct skl_sst **dsp);
 int bxt_sst_dsp_init(struct device *dev, void __iomem *mmio_base, int irq,
 		const char *fw_name, struct skl_dsp_loader_ops dsp_ops,
 		struct skl_sst **dsp);
@@ -222,17 +241,22 @@ int bxt_sst_init_fw(struct device *dev, struct skl_sst *ctx);
 void skl_sst_dsp_cleanup(struct device *dev, struct skl_sst *ctx);
 void bxt_sst_dsp_cleanup(struct device *dev, struct skl_sst *ctx);
 
-int snd_skl_get_module_info(struct skl_sst *ctx,
-				struct skl_module_cfg *mconfig);
 int snd_skl_parse_uuids(struct sst_dsp *ctx, const struct firmware *fw,
 				unsigned int offset, int index);
-int skl_get_pvt_id(struct skl_sst *ctx,
-				struct skl_module_cfg *mconfig);
-int skl_put_pvt_id(struct skl_sst *ctx,
-				struct skl_module_cfg *mconfig);
+int skl_get_pvt_id(struct skl_sst *ctx, uuid_le *uuid_mod, int instance_id);
+int skl_put_pvt_id(struct skl_sst *ctx, uuid_le *uuid_mod, int *pvt_id);
 int skl_get_pvt_instance_id_map(struct skl_sst *ctx,
 				int module_id, int instance_id);
 void skl_freeup_uuid_list(struct skl_sst *ctx);
 
 int skl_dsp_strip_extended_manifest(struct firmware *fw);
+void skl_dsp_enable_notification(struct skl_sst *ctx, bool enable);
+int skl_sst_ctx_init(struct device *dev, int irq, const char *fw_name,
+		struct skl_dsp_loader_ops dsp_ops, struct skl_sst **dsp,
+		struct sst_dsp_device *skl_dev);
+int skl_prepare_lib_load(struct skl_sst *skl, struct skl_lib_info *linfo,
+			struct firmware *stripped_fw,
+			unsigned int hdr_offset, int index);
+void skl_release_library(struct skl_lib_info *linfo, int lib_count);
+
 #endif /*__SKL_SST_DSP_H__*/
diff --git a/sound/soc/intel/skylake/skl-sst-ipc.c b/sound/soc/intel/skylake/skl-sst-ipc.c
index e1391dfbc9e9..58c525096a7c 100644
--- a/sound/soc/intel/skylake/skl-sst-ipc.c
+++ b/sound/soc/intel/skylake/skl-sst-ipc.c
@@ -34,6 +34,11 @@
 #define IPC_GLB_REPLY_STATUS_MASK	((0x1 << IPC_GLB_REPLY_STATUS_SHIFT) - 1)
 #define IPC_GLB_REPLY_STATUS(x)		((x) << IPC_GLB_REPLY_STATUS_SHIFT)
 
+#define IPC_GLB_REPLY_TYPE_SHIFT	29
+#define IPC_GLB_REPLY_TYPE_MASK		0x1F
+#define IPC_GLB_REPLY_TYPE(x)		(((x) >> IPC_GLB_REPLY_TYPE_SHIFT) \
+					& IPC_GLB_RPLY_TYPE_MASK)
+
 #define IPC_TIMEOUT_MSECS		3000
 
 #define IPC_EMPTY_LIST_SIZE		8
@@ -387,12 +392,27 @@ static int skl_ipc_process_notification(struct sst_generic_ipc *ipc,
 	return 0;
 }
 
+static int skl_ipc_set_reply_error_code(u32 reply)
+{
+	switch (reply) {
+	case IPC_GLB_REPLY_OUT_OF_MEMORY:
+		return -ENOMEM;
+
+	case IPC_GLB_REPLY_BUSY:
+		return -EBUSY;
+
+	default:
+		return -EINVAL;
+	}
+}
+
 static void skl_ipc_process_reply(struct sst_generic_ipc *ipc,
 		struct skl_ipc_header header)
 {
 	struct ipc_message *msg;
 	u32 reply = header.primary & IPC_GLB_REPLY_STATUS_MASK;
 	u64 *ipc_header = (u64 *)(&header);
+	struct skl_sst *skl = container_of(ipc, struct skl_sst, ipc);
 
 	msg = skl_ipc_reply_get_msg(ipc, *ipc_header);
 	if (msg == NULL) {
@@ -401,33 +421,39 @@ static void skl_ipc_process_reply(struct sst_generic_ipc *ipc,
 	}
 
 	/* first process the header */
-	switch (reply) {
-	case IPC_GLB_REPLY_SUCCESS:
+	if (reply == IPC_GLB_REPLY_SUCCESS) {
 		dev_dbg(ipc->dev, "ipc FW reply %x: success\n", header.primary);
 		/* copy the rx data from the mailbox */
 		sst_dsp_inbox_read(ipc->dsp, msg->rx_data, msg->rx_size);
-		break;
-
-	case IPC_GLB_REPLY_OUT_OF_MEMORY:
-		dev_err(ipc->dev, "ipc fw reply: %x: no memory\n", header.primary);
-		msg->errno = -ENOMEM;
-		break;
-
-	case IPC_GLB_REPLY_BUSY:
-		dev_err(ipc->dev, "ipc fw reply: %x: Busy\n", header.primary);
-		msg->errno = -EBUSY;
-		break;
+		switch (IPC_GLB_NOTIFY_MSG_TYPE(header.primary)) {
+		case IPC_GLB_LOAD_MULTIPLE_MODS:
+		case IPC_GLB_LOAD_LIBRARY:
+			skl->mod_load_complete = true;
+			skl->mod_load_status = true;
+			wake_up(&skl->mod_load_wait);
+			break;
 
-	default:
-		dev_err(ipc->dev, "Unknown ipc reply: 0x%x\n", reply);
-		msg->errno = -EINVAL;
-		break;
-	}
+		default:
+			break;
 
-	if (reply != IPC_GLB_REPLY_SUCCESS) {
+		}
+	} else {
+		msg->errno = skl_ipc_set_reply_error_code(reply);
 		dev_err(ipc->dev, "ipc FW reply: reply=%d\n", reply);
 		dev_err(ipc->dev, "FW Error Code: %u\n",
 			ipc->dsp->fw_ops.get_fw_errcode(ipc->dsp));
+		switch (IPC_GLB_NOTIFY_MSG_TYPE(header.primary)) {
+		case IPC_GLB_LOAD_MULTIPLE_MODS:
+		case IPC_GLB_LOAD_LIBRARY:
+			skl->mod_load_complete = true;
+			skl->mod_load_status = false;
+			wake_up(&skl->mod_load_wait);
+			break;
+
+		default:
+			break;
+
+		}
 	}
 
 	list_del(&msg->list);
@@ -811,8 +837,8 @@ int skl_ipc_load_modules(struct sst_generic_ipc *ipc,
 	header.primary |= IPC_GLB_TYPE(IPC_GLB_LOAD_MULTIPLE_MODS);
 	header.primary |= IPC_LOAD_MODULE_CNT(module_cnt);
 
-	ret = sst_ipc_tx_message_wait(ipc, *ipc_header, data,
-				(sizeof(u16) * module_cnt), NULL, 0);
+	ret = sst_ipc_tx_message_nowait(ipc, *ipc_header, data,
+				(sizeof(u16) * module_cnt));
 	if (ret < 0)
 		dev_err(ipc->dev, "ipc: load modules failed :%d\n", ret);
 
@@ -947,7 +973,7 @@ int skl_ipc_get_large_config(struct sst_generic_ipc *ipc,
 EXPORT_SYMBOL_GPL(skl_ipc_get_large_config);
 
 int skl_sst_ipc_load_library(struct sst_generic_ipc *ipc,
-				u8 dma_id, u8 table_id)
+				u8 dma_id, u8 table_id, bool wait)
 {
 	struct skl_ipc_header header = {0};
 	u64 *ipc_header = (u64 *)(&header);
@@ -959,7 +985,11 @@ int skl_sst_ipc_load_library(struct sst_generic_ipc *ipc,
 	header.primary |= IPC_MOD_INSTANCE_ID(table_id);
 	header.primary |= IPC_MOD_ID(dma_id);
 
-	ret = sst_ipc_tx_message_wait(ipc, *ipc_header, NULL, 0, NULL, 0);
+	if (wait)
+		ret = sst_ipc_tx_message_wait(ipc, *ipc_header,
+					NULL, 0, NULL, 0);
+	else
+		ret = sst_ipc_tx_message_nowait(ipc, *ipc_header, NULL, 0);
 
 	if (ret < 0)
 		dev_err(ipc->dev, "ipc: load lib failed\n");
diff --git a/sound/soc/intel/skylake/skl-sst-ipc.h b/sound/soc/intel/skylake/skl-sst-ipc.h
index 9660ace379ab..e057da2713c6 100644
--- a/sound/soc/intel/skylake/skl-sst-ipc.h
+++ b/sound/soc/intel/skylake/skl-sst-ipc.h
@@ -69,6 +69,14 @@ struct skl_d0i3_data {
 	struct delayed_work work;
 };
 
+#define SKL_LIB_NAME_LENGTH 128
+#define SKL_MAX_LIB 16
+
+struct skl_lib_info {
+	char name[SKL_LIB_NAME_LENGTH];
+	const struct firmware *fw;
+};
+
 struct skl_sst {
 	struct device *dev;
 	struct sst_dsp *dsp;
@@ -77,6 +85,11 @@ struct skl_sst {
 	wait_queue_head_t boot_wait;
 	bool boot_complete;
 
+	/* module load */
+	wait_queue_head_t mod_load_wait;
+	bool mod_load_complete;
+	bool mod_load_status;
+
 	/* IPC messaging */
 	struct sst_generic_ipc ipc;
 
@@ -105,6 +118,8 @@ struct skl_sst {
 	void (*update_d0i3c)(struct device *dev, bool enable);
 
 	struct skl_d0i3_data d0i3;
+
+	const struct skl_dsp_ops *dsp_ops;
 };
 
 struct skl_ipc_init_instance_msg {
@@ -182,7 +197,7 @@ int skl_ipc_get_large_config(struct sst_generic_ipc *ipc,
 		struct skl_ipc_large_config_msg *msg, u32 *param);
 
 int skl_sst_ipc_load_library(struct sst_generic_ipc *ipc,
-			u8 dma_id, u8 table_id);
+			u8 dma_id, u8 table_id, bool wait);
 
 int skl_ipc_set_d0ix(struct sst_generic_ipc *ipc,
 		struct skl_ipc_d0ix_msg *msg);
diff --git a/sound/soc/intel/skylake/skl-sst-utils.c b/sound/soc/intel/skylake/skl-sst-utils.c
index ea162fbf68e5..81ee251881b4 100644
--- a/sound/soc/intel/skylake/skl-sst-utils.c
+++ b/sound/soc/intel/skylake/skl-sst-utils.c
@@ -94,19 +94,6 @@ struct adsp_fw_hdr {
 	u32 load_offset;
 } __packed;
 
-#define MAX_INSTANCE_BUFF 2
-
-struct uuid_module {
-	uuid_le uuid;
-	int id;
-	int is_loadable;
-	int max_instance;
-	u64 pvt_id[MAX_INSTANCE_BUFF];
-	int *instance_id;
-
-	struct list_head list;
-};
-
 struct skl_ext_manifest_hdr {
 	u32 id;
 	u32 len;
@@ -115,32 +102,6 @@ struct skl_ext_manifest_hdr {
 	u32 entries;
 };
 
-int snd_skl_get_module_info(struct skl_sst *ctx,
-				struct skl_module_cfg *mconfig)
-{
-	struct uuid_module *module;
-	uuid_le *uuid_mod;
-
-	uuid_mod = (uuid_le *)mconfig->guid;
-
-	if (list_empty(&ctx->uuid_list)) {
-		dev_err(ctx->dev, "Module list is empty\n");
-		return -EINVAL;
-	}
-
-	list_for_each_entry(module, &ctx->uuid_list, list) {
-		if (uuid_le_cmp(*uuid_mod, module->uuid) == 0) {
-			mconfig->id.module_id = module->id;
-			mconfig->is_loadable = module->is_loadable;
-
-			return 0;
-		}
-	}
-
-	return -EINVAL;
-}
-EXPORT_SYMBOL_GPL(snd_skl_get_module_info);
-
 static int skl_get_pvtid_map(struct uuid_module *module, int instance_id)
 {
 	int pvt_id;
@@ -222,21 +183,18 @@ static inline int skl_pvtid_128(struct uuid_module *module)
  * This generates a 128 bit private unique id for a module TYPE so that
  * module instance is unique
  */
-int skl_get_pvt_id(struct skl_sst *ctx, struct skl_module_cfg *mconfig)
+int skl_get_pvt_id(struct skl_sst *ctx, uuid_le *uuid_mod, int instance_id)
 {
 	struct uuid_module *module;
-	uuid_le *uuid_mod;
 	int pvt_id;
 
-	uuid_mod = (uuid_le *)mconfig->guid;
-
 	list_for_each_entry(module, &ctx->uuid_list, list) {
 		if (uuid_le_cmp(*uuid_mod, module->uuid) == 0) {
 
 			pvt_id = skl_pvtid_128(module);
 			if (pvt_id >= 0) {
-				module->instance_id[pvt_id] =
-						mconfig->id.instance_id;
+				module->instance_id[pvt_id] = instance_id;
+
 				return pvt_id;
 			}
 		}
@@ -254,23 +212,21 @@ EXPORT_SYMBOL_GPL(skl_get_pvt_id);
  *
  * This frees a 128 bit private unique id previously generated
  */
-int skl_put_pvt_id(struct skl_sst *ctx, struct skl_module_cfg *mconfig)
+int skl_put_pvt_id(struct skl_sst *ctx, uuid_le *uuid_mod, int *pvt_id)
 {
 	int i;
-	uuid_le *uuid_mod;
 	struct uuid_module *module;
 
-	uuid_mod = (uuid_le *)mconfig->guid;
 	list_for_each_entry(module, &ctx->uuid_list, list) {
 		if (uuid_le_cmp(*uuid_mod, module->uuid) == 0) {
 
-			if (mconfig->id.pvt_id != 0)
-				i = (mconfig->id.pvt_id) / 64;
+			if (*pvt_id != 0)
+				i = (*pvt_id) / 64;
 			else
 				i = 0;
 
-			module->pvt_id[i] &= ~(1 << (mconfig->id.pvt_id));
-			mconfig->id.pvt_id = -1;
+			module->pvt_id[i] &= ~(1 << (*pvt_id));
+			*pvt_id = -1;
 			return 0;
 		}
 	}
@@ -405,3 +361,83 @@ int skl_dsp_strip_extended_manifest(struct firmware *fw)
 
 	return 0;
 }
+
+int skl_sst_ctx_init(struct device *dev, int irq, const char *fw_name,
+	struct skl_dsp_loader_ops dsp_ops, struct skl_sst **dsp,
+	struct sst_dsp_device *skl_dev)
+{
+	struct skl_sst *skl;
+	struct sst_dsp *sst;
+	int ret;
+
+	skl = devm_kzalloc(dev, sizeof(*skl), GFP_KERNEL);
+	if (skl == NULL)
+		return -ENOMEM;
+
+	skl->dev = dev;
+	skl_dev->thread_context = skl;
+	INIT_LIST_HEAD(&skl->uuid_list);
+	skl->dsp = skl_dsp_ctx_init(dev, skl_dev, irq);
+	if (!skl->dsp) {
+		dev_err(skl->dev, "%s: no device\n", __func__);
+		return -ENODEV;
+	}
+
+	sst = skl->dsp;
+	sst->fw_name = fw_name;
+	sst->dsp_ops = dsp_ops;
+	init_waitqueue_head(&skl->mod_load_wait);
+	INIT_LIST_HEAD(&sst->module_list);
+	ret = skl_ipc_init(dev, skl);
+	if (ret)
+		return ret;
+
+	skl->is_first_boot = true;
+	if (dsp)
+		*dsp = skl;
+
+	return ret;
+}
+
+int skl_prepare_lib_load(struct skl_sst *skl, struct skl_lib_info *linfo,
+		struct firmware *stripped_fw,
+		unsigned int hdr_offset, int index)
+{
+	int ret;
+	struct sst_dsp *dsp = skl->dsp;
+
+	if (linfo->fw == NULL) {
+		ret = request_firmware(&linfo->fw, linfo->name,
+					skl->dev);
+		if (ret < 0) {
+			dev_err(skl->dev, "Request lib %s failed:%d\n",
+				linfo->name, ret);
+			return ret;
+		}
+	}
+
+	if (skl->is_first_boot) {
+		ret = snd_skl_parse_uuids(dsp, linfo->fw, hdr_offset, index);
+		if (ret < 0)
+			return ret;
+	}
+
+	stripped_fw->data = linfo->fw->data;
+	stripped_fw->size = linfo->fw->size;
+	skl_dsp_strip_extended_manifest(stripped_fw);
+
+	return 0;
+}
+
+void skl_release_library(struct skl_lib_info *linfo, int lib_count)
+{
+	int i;
+
+	/* library indices start from 1 to N. 0 represents base FW */
+	for (i = 1; i < lib_count; i++) {
+		if (linfo[i].fw) {
+			release_firmware(linfo[i].fw);
+			linfo[i].fw = NULL;
+		}
+	}
+}
diff --git a/sound/soc/intel/skylake/skl-sst.c b/sound/soc/intel/skylake/skl-sst.c
index b30bd384c8d3..155e456b7a3a 100644
--- a/sound/soc/intel/skylake/skl-sst.c
+++ b/sound/soc/intel/skylake/skl-sst.c
@@ -52,7 +52,8 @@ static int skl_transfer_firmware(struct sst_dsp *ctx,
 {
 	int ret = 0;
 
-	ret = ctx->cl_dev.ops.cl_copy_to_dmabuf(ctx, basefw, base_fw_size);
+	ret = ctx->cl_dev.ops.cl_copy_to_dmabuf(ctx, basefw, base_fw_size,
+								true);
 	if (ret < 0)
 		return ret;
 
@@ -178,6 +179,18 @@ static int skl_set_dsp_D0(struct sst_dsp *ctx, unsigned int core_id)
 			dev_err(ctx->dev, "unable to load firmware\n");
 			return ret;
 		}
+
+		/* load libs as they are also lost on D3 */
+		if (skl->lib_count > 1) {
+			ret = ctx->fw_ops.load_library(ctx, skl->lib_info,
+							skl->lib_count);
+			if (ret < 0) {
+				dev_err(ctx->dev, "reload libs failed: %d\n",
+						ret);
+				return ret;
+			}
+
+		}
 	}
 
 	/*
@@ -203,7 +216,7 @@ static int skl_set_dsp_D0(struct sst_dsp *ctx, unsigned int core_id)
 
 	skl->cores.state[core_id] = SKL_DSP_RUNNING;
 
-	return ret;
+	return 0;
 }
 
 static int skl_set_dsp_D3(struct sst_dsp *ctx, unsigned int core_id)
@@ -323,27 +336,85 @@ static struct skl_module_table *skl_module_get_from_id(
 	return NULL;
 }
 
-static int skl_transfer_module(struct sst_dsp *ctx,
-			struct skl_load_module_info *module)
+static int skl_transfer_module(struct sst_dsp *ctx, const void *data,
+			u32 size, u16 mod_id, u8 table_id, bool is_module)
 {
-	int ret;
+	int ret, bytes_left, curr_pos;
 	struct skl_sst *skl = ctx->thread_context;
+	skl->mod_load_complete = false;
 
-	ret = ctx->cl_dev.ops.cl_copy_to_dmabuf(ctx, module->fw->data,
-							module->fw->size);
-	if (ret < 0)
-		return ret;
+	bytes_left = ctx->cl_dev.ops.cl_copy_to_dmabuf(ctx, data, size, false);
+	if (bytes_left < 0)
+		return bytes_left;
 
-	ret = skl_ipc_load_modules(&skl->ipc, SKL_NUM_MODULES,
-						(void *)&module->mod_id);
-	if (ret < 0)
-		dev_err(ctx->dev, "Failed to Load module: %d\n", ret);
+	/* check is_module flag to load module or library */
+	if (is_module)
+		ret = skl_ipc_load_modules(&skl->ipc, SKL_NUM_MODULES, &mod_id);
+	else
+		ret = skl_sst_ipc_load_library(&skl->ipc, 0, table_id, false);
+
+	if (ret < 0) {
+		dev_err(ctx->dev, "Failed to Load %s with err %d\n",
+				is_module ? "module" : "lib", ret);
+		goto out;
+	}
+
+	/*
+	 * if bytes_left > 0 then wait for BDL complete interrupt and
+	 * copy the next chunk till bytes_left is 0. if bytes_left is
+	 * is zero, then wait for load module IPC reply
+	 */
+	while (bytes_left > 0) {
+		curr_pos = size - bytes_left;
+
+		ret = skl_cldma_wait_interruptible(ctx);
+		if (ret < 0)
+			goto out;
+
+		bytes_left = ctx->cl_dev.ops.cl_copy_to_dmabuf(ctx,
+							data + curr_pos,
+							bytes_left, false);
+	}
 
+	ret = wait_event_timeout(skl->mod_load_wait, skl->mod_load_complete,
+				msecs_to_jiffies(SKL_IPC_BOOT_MSECS));
+	if (ret == 0 || !skl->mod_load_status) {
+		dev_err(ctx->dev, "Module Load failed\n");
+		ret = -EIO;
+	}
+
+out:
 	ctx->cl_dev.ops.cl_stop_dma(ctx);
 
 	return ret;
 }
 
+static int
+kbl_load_library(struct sst_dsp *ctx, struct skl_lib_info *linfo, int lib_count)
+{
+	struct skl_sst *skl = ctx->thread_context;
+	struct firmware stripped_fw;
+	int ret, i;
+
+	/* library indices start from 1 to N. 0 represents base FW */
+	for (i = 1; i < lib_count; i++) {
+		ret = skl_prepare_lib_load(skl, &skl->lib_info[i], &stripped_fw,
+					SKL_ADSP_FW_BIN_HDR_OFFSET, i);
+		if (ret < 0)
+			goto load_library_failed;
+		ret = skl_transfer_module(ctx, stripped_fw.data,
+				stripped_fw.size, 0, i, false);
+		if (ret < 0)
+			goto load_library_failed;
+	}
+
+	return 0;
+
+load_library_failed:
+	skl_release_library(linfo, lib_count);
+	return ret;
+}
+
 static int skl_load_module(struct sst_dsp *ctx, u16 mod_id, u8 *guid)
 {
 	struct skl_module_table *module_entry = NULL;
@@ -365,7 +436,9 @@ static int skl_load_module(struct sst_dsp *ctx, u16 mod_id, u8 *guid)
 	}
 
 	if (!module_entry->usage_cnt) {
-		ret = skl_transfer_module(ctx, module_entry->mod_info);
+		ret = skl_transfer_module(ctx, module_entry->mod_info->fw->data,
+				module_entry->mod_info->fw->size,
+				mod_id, 0, true);
 		if (ret < 0) {
 			dev_err(ctx->dev, "Failed to Load module\n");
 			return ret;
@@ -388,6 +461,11 @@ static int skl_unload_module(struct sst_dsp *ctx, u16 mod_id)
 		dev_err(ctx->dev, "Module bad usage cnt!:%d\n", usage_cnt);
 		return -EIO;
 	}
+
+	/* if module is used by others return, no need to unload */
+	if (usage_cnt > 0)
+		return 0;
+
 	ret = skl_ipc_unload_modules(&skl->ipc,
 			SKL_NUM_MODULES, &mod_id);
 	if (ret < 0) {
@@ -434,6 +512,16 @@ static struct skl_dsp_fw_ops skl_fw_ops = {
 	.unload_mod = skl_unload_module,
 };
 
+static struct skl_dsp_fw_ops kbl_fw_ops = {
+	.set_state_D0 = skl_set_dsp_D0,
+	.set_state_D3 = skl_set_dsp_D3,
+	.load_fw = skl_load_base_firmware,
+	.get_fw_errcode = skl_get_errorcode,
+	.load_library = kbl_load_library,
+	.load_mod = skl_load_module,
+	.unload_mod = skl_unload_module,
+};
+
 static struct sst_ops skl_ops = {
 	.irq_handler = skl_dsp_sst_interrupt,
 	.write = sst_shim32_write,
@@ -455,45 +543,47 @@ int skl_sst_dsp_init(struct device *dev, void __iomem *mmio_base, int irq,
 	struct sst_dsp *sst;
 	int ret;
 
-	skl = devm_kzalloc(dev, sizeof(*skl), GFP_KERNEL);
-	if (skl == NULL)
-		return -ENOMEM;
-
-	skl->dev = dev;
-	skl_dev.thread_context = skl;
-	INIT_LIST_HEAD(&skl->uuid_list);
-
-	skl->dsp = skl_dsp_ctx_init(dev, &skl_dev, irq);
-	if (!skl->dsp) {
-		dev_err(skl->dev, "%s: no device\n", __func__);
-		return -ENODEV;
+	ret = skl_sst_ctx_init(dev, irq, fw_name, dsp_ops, dsp, &skl_dev);
+	if (ret < 0) {
+		dev_err(dev, "%s: no device\n", __func__);
+		return ret;
 	}
 
+	skl = *dsp;
 	sst = skl->dsp;
-
-	sst->fw_name = fw_name;
 	sst->addr.lpe = mmio_base;
 	sst->addr.shim = mmio_base;
 	sst_dsp_mailbox_init(sst, (SKL_ADSP_SRAM0_BASE + SKL_ADSP_W0_STAT_SZ),
 			SKL_ADSP_W0_UP_SZ, SKL_ADSP_SRAM1_BASE, SKL_ADSP_W1_SZ);
 
-	INIT_LIST_HEAD(&sst->module_list);
-	sst->dsp_ops = dsp_ops;
 	sst->fw_ops = skl_fw_ops;
 
-	ret = skl_ipc_init(dev, skl);
-	if (ret)
+	skl->cores.count = 2;
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(skl_sst_dsp_init);
+
+int kbl_sst_dsp_init(struct device *dev, void __iomem *mmio_base, int irq,
+		const char *fw_name, struct skl_dsp_loader_ops dsp_ops,
+		struct skl_sst **dsp)
+{
+	struct sst_dsp *sst;
+	int ret;
+
+	ret = skl_sst_dsp_init(dev, mmio_base, irq, fw_name, dsp_ops, dsp);
+	if (ret < 0) {
+		dev_err(dev, "%s: Init failed %d\n", __func__, ret);
 		return ret;
+	}
 
-	skl->cores.count = 2;
-	skl->is_first_boot = true;
+	sst = (*dsp)->dsp;
+	sst->fw_ops = kbl_fw_ops;
 
-	if (dsp)
-		*dsp = skl;
+	return 0;
 
-	return ret;
 }
-EXPORT_SYMBOL_GPL(skl_sst_dsp_init);
+EXPORT_SYMBOL_GPL(kbl_sst_dsp_init);
 
 int skl_sst_init_fw(struct device *dev, struct skl_sst *ctx)
 {
@@ -507,6 +597,15 @@ int skl_sst_init_fw(struct device *dev, struct skl_sst *ctx)
 	}
 
 	skl_dsp_init_core_state(sst);
+
+	if (ctx->lib_count > 1) {
+		ret = sst->fw_ops.load_library(sst, ctx->lib_info,
+						ctx->lib_count);
+		if (ret < 0) {
+			dev_err(dev, "Load Library failed : %x\n", ret);
+			return ret;
+		}
+	}
 	ctx->is_first_boot = false;
 
 	return 0;
diff --git a/sound/soc/intel/skylake/skl-topology.c b/sound/soc/intel/skylake/skl-topology.c
index 2dbfb1b24ef4..3a99712e44a8 100644
--- a/sound/soc/intel/skylake/skl-topology.c
+++ b/sound/soc/intel/skylake/skl-topology.c
@@ -299,8 +299,6 @@ static void skl_tplg_update_buffer_size(struct skl_sst *ctx,
 {
 	int multiplier = 1;
 	struct skl_module_fmt *in_fmt, *out_fmt;
-	int in_rate, out_rate;
-
 
 	/* Since fixups is applied to pin 0 only, ibs, obs needs
 	 * change for pin 0 only
@@ -311,22 +309,12 @@ static void skl_tplg_update_buffer_size(struct skl_sst *ctx,
 	if (mcfg->m_type == SKL_MODULE_TYPE_SRCINT)
 		multiplier = 5;
 
-	if (in_fmt->s_freq % 1000)
-		in_rate = (in_fmt->s_freq / 1000) + 1;
-	else
-		in_rate = (in_fmt->s_freq / 1000);
-
-	mcfg->ibs = in_rate * (mcfg->in_fmt->channels) *
-			(mcfg->in_fmt->bit_depth >> 3) *
+	mcfg->ibs = DIV_ROUND_UP(in_fmt->s_freq, 1000) *
+			in_fmt->channels * (in_fmt->bit_depth >> 3) *
 			multiplier;
 
-	if (mcfg->out_fmt->s_freq % 1000)
-		out_rate = (mcfg->out_fmt->s_freq / 1000) + 1;
-	else
-		out_rate = (mcfg->out_fmt->s_freq / 1000);
-
-	mcfg->obs = out_rate * (mcfg->out_fmt->channels) *
-			(mcfg->out_fmt->bit_depth >> 3) *
+	mcfg->obs = DIV_ROUND_UP(out_fmt->s_freq, 1000) *
+			out_fmt->channels * (out_fmt->bit_depth >> 3) *
 			multiplier;
 }
 
@@ -551,6 +539,7 @@ skl_tplg_init_pipe_modules(struct skl *skl, struct skl_pipe *pipe)
 	int ret = 0;
 
 	list_for_each_entry(w_module, &pipe->w_list, node) {
+		uuid_le *uuid_mod;
 		w = w_module->w;
 		mconfig = w->priv;
 
@@ -588,13 +577,15 @@ skl_tplg_init_pipe_modules(struct skl *skl, struct skl_pipe *pipe)
 		 * FE/BE params
 		 */
 		skl_tplg_update_module_params(w, ctx);
-		mconfig->id.pvt_id = skl_get_pvt_id(ctx, mconfig);
+		uuid_mod = (uuid_le *)mconfig->guid;
+		mconfig->id.pvt_id = skl_get_pvt_id(ctx, uuid_mod,
+						mconfig->id.instance_id);
 		if (mconfig->id.pvt_id < 0)
 			return ret;
 		skl_tplg_set_module_init_data(w);
 		ret = skl_init_module(ctx, mconfig);
 		if (ret < 0) {
-			skl_put_pvt_id(ctx, mconfig);
+			skl_put_pvt_id(ctx, uuid_mod, &mconfig->id.pvt_id);
 			return ret;
 		}
 		skl_tplg_alloc_pipe_mcps(skl, mconfig);
@@ -614,7 +605,9 @@ static int skl_tplg_unload_pipe_modules(struct skl_sst *ctx,
 	struct skl_module_cfg *mconfig = NULL;
 
 	list_for_each_entry(w_module, &pipe->w_list, node) {
+		uuid_le *uuid_mod;
 		mconfig  = w_module->w->priv;
+		uuid_mod = (uuid_le *)mconfig->guid;
 
 		if (mconfig->is_loadable && ctx->dsp->fw_ops.unload_mod &&
 			mconfig->m_state > SKL_MODULE_UNINIT) {
@@ -623,7 +616,7 @@ static int skl_tplg_unload_pipe_modules(struct skl_sst *ctx,
 			if (ret < 0)
 				return -EIO;
 		}
-		skl_put_pvt_id(ctx, mconfig);
+		skl_put_pvt_id(ctx, uuid_mod, &mconfig->id.pvt_id);
 	}
 
 	/* no modules to unload in this path, so return */
@@ -645,8 +638,9 @@ static int skl_tplg_mixer_dapm_pre_pmu_event(struct snd_soc_dapm_widget *w,
 	struct skl_module_cfg *mconfig = w->priv;
 	struct skl_pipe_module *w_module;
 	struct skl_pipe *s_pipe = mconfig->pipe;
-	struct skl_module_cfg *src_module = NULL, *dst_module;
+	struct skl_module_cfg *src_module = NULL, *dst_module, *module;
 	struct skl_sst *ctx = skl->skl_sst;
+	struct skl_module_deferred_bind *modules;
 
 	/* check resource available */
 	if (!skl_is_pipe_mcps_avail(skl, mconfig))
@@ -687,29 +681,48 @@ static int skl_tplg_mixer_dapm_pre_pmu_event(struct snd_soc_dapm_widget *w,
 		src_module = dst_module;
 	}
 
+	/*
+	 * When the destination module is initialized, check for these modules
+	 * in deferred bind list. If found, bind them.
+	 */
+	list_for_each_entry(w_module, &s_pipe->w_list, node) {
+		if (list_empty(&skl->bind_list))
+			break;
+
+		list_for_each_entry(modules, &skl->bind_list, node) {
+			module = w_module->w->priv;
+			if (modules->dst == module)
+				skl_bind_modules(ctx, modules->src,
+							modules->dst);
+		}
+	}
+
 	return 0;
 }
 
-static int skl_fill_sink_instance_id(struct skl_sst *ctx,
-				struct skl_algo_data *alg_data)
+static int skl_fill_sink_instance_id(struct skl_sst *ctx, u32 *params,
+				int size, struct skl_module_cfg *mcfg)
 {
-	struct skl_kpb_params *params = (struct skl_kpb_params *)alg_data->params;
-	struct skl_mod_inst_map *inst;
 	int i, pvt_id;
 
-	inst = params->map;
+	if (mcfg->m_type == SKL_MODULE_TYPE_KPB) {
+		struct skl_kpb_params *kpb_params =
+				(struct skl_kpb_params *)params;
+		struct skl_mod_inst_map *inst = kpb_params->map;
 
-	for (i = 0; i < params->num_modules; i++) {
-		pvt_id = skl_get_pvt_instance_id_map(ctx,
-					inst->mod_id, inst->inst_id);
-		if (pvt_id < 0)
-			return -EINVAL;
-		inst->inst_id = pvt_id;
-		inst++;
+		for (i = 0; i < kpb_params->num_modules; i++) {
+			pvt_id = skl_get_pvt_instance_id_map(ctx, inst->mod_id,
+								inst->inst_id);
+			if (pvt_id < 0)
+				return -EINVAL;
+
+			inst->inst_id = pvt_id;
+			inst++;
+		}
 	}
+
 	return 0;
 }
-
 /*
  * Some modules require params to be set after the module is bound to
  * all pins connected.
@@ -726,6 +739,7 @@ static int skl_tplg_set_module_bind_params(struct snd_soc_dapm_widget *w,
 	struct soc_bytes_ext *sb;
 	struct skl_algo_data *bc;
 	struct skl_specific_cfg *sp_cfg;
+	u32 *params;
 
 	/*
 	 * check all out/in pins are in bind state.
@@ -758,11 +772,18 @@ static int skl_tplg_set_module_bind_params(struct snd_soc_dapm_widget *w,
 			bc = (struct skl_algo_data *)sb->dobj.private;
 
 			if (bc->set_params == SKL_PARAM_BIND) {
-				if (mconfig->m_type == SKL_MODULE_TYPE_KPB)
-					skl_fill_sink_instance_id(ctx, bc);
-				ret = skl_set_module_params(ctx,
-						(u32 *)bc->params, bc->max,
-						bc->param_id, mconfig);
+				params = kzalloc(bc->max, GFP_KERNEL);
+				if (!params)
+					return -ENOMEM;
+
+				memcpy(params, bc->params, bc->max);
+				skl_fill_sink_instance_id(ctx, params, bc->max,
+								mconfig);
+
+				ret = skl_set_module_params(ctx, params,
+						bc->max, bc->param_id, mconfig);
+				kfree(params);
+
 				if (ret < 0)
 					return ret;
 			}
@@ -772,6 +793,44 @@ static int skl_tplg_set_module_bind_params(struct snd_soc_dapm_widget *w,
 	return 0;
 }
 
+
+static int skl_tplg_module_add_deferred_bind(struct skl *skl,
+	struct skl_module_cfg *src, struct skl_module_cfg *dst)
+{
+	struct skl_module_deferred_bind *m_list, *modules;
+	int i;
+
+	/* only supported for module with static pin connection */
+	for (i = 0; i < dst->max_in_queue; i++) {
+		struct skl_module_pin *pin = &dst->m_in_pin[i];
+
+		if (pin->is_dynamic)
+			continue;
+
+		if ((pin->id.module_id  == src->id.module_id) &&
+			(pin->id.instance_id  == src->id.instance_id)) {
+
+			if (!list_empty(&skl->bind_list)) {
+				list_for_each_entry(modules, &skl->bind_list, node) {
+					if (modules->src == src && modules->dst == dst)
+						return 0;
+				}
+			}
+
+			m_list = kzalloc(sizeof(*m_list), GFP_KERNEL);
+			if (!m_list)
+				return -ENOMEM;
+
+			m_list->src = src;
+			m_list->dst = dst;
+
+			list_add(&m_list->node, &skl->bind_list);
+		}
+	}
+
+	return 0;
+}
+
 static int skl_tplg_bind_sinks(struct snd_soc_dapm_widget *w,
 				struct skl *skl,
 				struct snd_soc_dapm_widget *src_w,
@@ -806,6 +865,28 @@ static int skl_tplg_bind_sinks(struct snd_soc_dapm_widget *w,
 			sink = p->sink;
 			sink_mconfig = sink->priv;
 
+			/*
+			 * Modules other than PGA leaf can be connected
+			 * directly or via switch to a module in another
+			 * pipeline. EX: reference path
+			 * when the path is enabled, the dst module that needs
+			 * to be bound may not be initialized. if the module is
+			 * not initialized, add these modules in the deferred
+			 * bind list and when the dst module is initialised,
+			 * bind this module to the dst_module in deferred list.
+			 */
+			if (((src_mconfig->m_state == SKL_MODULE_INIT_DONE)
+				&& (sink_mconfig->m_state == SKL_MODULE_UNINIT))) {
+
+				ret = skl_tplg_module_add_deferred_bind(skl,
+						src_mconfig, sink_mconfig);
+
+				if (ret < 0)
+					return ret;
+
+			}
+
+
 			if (src_mconfig->m_state == SKL_MODULE_UNINIT ||
 				sink_mconfig->m_state == SKL_MODULE_UNINIT)
 				continue;
@@ -985,15 +1066,6 @@ static int skl_tplg_mixer_dapm_pre_pmd_event(struct snd_soc_dapm_widget *w,
 			src_mconfig = sink_mconfig->m_in_pin[i].tgt_mcfg;
 			if (!src_mconfig)
 				continue;
-			/*
-			 * If path_found == 1, that means pmd for source
-			 * pipe has not occurred, source is connected to
-			 * some other sink. so its responsibility of sink
-			 * to unbind itself from source.
-			 */
-			ret = skl_stop_pipe(ctx, src_mconfig->pipe);
-			if (ret < 0)
-				return ret;
 
 			ret = skl_unbind_modules(ctx,
 						src_mconfig, sink_mconfig);
@@ -1019,6 +1091,7 @@ static int skl_tplg_mixer_dapm_post_pmd_event(struct snd_soc_dapm_widget *w,
 	struct skl_module_cfg *src_module = NULL, *dst_module;
 	struct skl_sst *ctx = skl->skl_sst;
 	struct skl_pipe *s_pipe = mconfig->pipe;
+	struct skl_module_deferred_bind *modules, *tmp;
 
 	if (s_pipe->state == SKL_PIPE_INVALID)
 		return -EINVAL;
@@ -1027,6 +1100,35 @@ static int skl_tplg_mixer_dapm_post_pmd_event(struct snd_soc_dapm_widget *w,
 	skl_tplg_free_pipe_mem(skl, mconfig);
 
 	list_for_each_entry(w_module, &s_pipe->w_list, node) {
+		if (list_empty(&skl->bind_list))
+			break;
+
+		src_module = w_module->w->priv;
+
+		list_for_each_entry_safe(modules, tmp, &skl->bind_list, node) {
+			/*
+			 * When the destination module is deleted, Unbind the
+			 * modules from deferred bind list.
+			 */
+			if (modules->dst == src_module) {
+				skl_unbind_modules(ctx, modules->src,
+						modules->dst);
+			}
+
+			/*
+			 * When the source module is deleted, remove this entry
+			 * from the deferred bind list.
+			 */
+			if (modules->src == src_module) {
+				list_del(&modules->node);
+				modules->src = NULL;
+				modules->dst = NULL;
+				kfree(modules);
+			}
+		}
+	}
+
+	list_for_each_entry(w_module, &s_pipe->w_list, node) {
 		dst_module = w_module->w->priv;
 
 		if (mconfig->m_state >= SKL_MODULE_INIT_DONE)
@@ -1042,6 +1144,11 @@ static int skl_tplg_mixer_dapm_post_pmd_event(struct snd_soc_dapm_widget *w,
 
 	skl_delete_pipe(ctx, mconfig->pipe);
 
+	list_for_each_entry(w_module, &s_pipe->w_list, node) {
+		src_module = w_module->w->priv;
+		src_module->m_state = SKL_MODULE_UNINIT;
+	}
+
 	return skl_tplg_unload_pipe_modules(ctx, s_pipe);
 }
 
@@ -1083,36 +1190,6 @@ static int skl_tplg_pga_dapm_post_pmd_event(struct snd_soc_dapm_widget *w,
 }
 
 /*
- * In modelling, we assume there will be ONLY one mixer in a pipeline.  If
- * mixer is not required then it is treated as static mixer aka vmixer with
- * a hard path to source module
- * So we don't need to check if source is started or not as hard path puts
- * dependency on each other
- */
-static int skl_tplg_vmixer_event(struct snd_soc_dapm_widget *w,
-				struct snd_kcontrol *k, int event)
-{
-	struct snd_soc_dapm_context *dapm = w->dapm;
-	struct skl *skl = get_skl_ctx(dapm->dev);
-
-	switch (event) {
-	case SND_SOC_DAPM_PRE_PMU:
-		return skl_tplg_mixer_dapm_pre_pmu_event(w, skl);
-
-	case SND_SOC_DAPM_POST_PMU:
-		return skl_tplg_mixer_dapm_post_pmu_event(w, skl);
-
-	case SND_SOC_DAPM_PRE_PMD:
-		return skl_tplg_mixer_dapm_pre_pmd_event(w, skl);
-
-	case SND_SOC_DAPM_POST_PMD:
-		return skl_tplg_mixer_dapm_post_pmd_event(w, skl);
-	}
-
-	return 0;
-}
-
-/*
  * In modelling, we assume there will be ONLY one mixer in a pipeline. If a
  * second one is required that is created as another pipe entity.
  * The mixer is responsible for pipe management and represent a pipeline
@@ -1252,10 +1329,12 @@ static void skl_tplg_fill_dma_id(struct skl_module_cfg *mcfg,
 		case SKL_DEVICE_HDALINK:
 			pipe->p_params->link_dma_id = params->link_dma_id;
 			pipe->p_params->link_index = params->link_index;
+			pipe->p_params->link_bps = params->link_bps;
 			break;
 
 		case SKL_DEVICE_HDAHOST:
 			pipe->p_params->host_dma_id = params->host_dma_id;
+			pipe->p_params->host_bps = params->host_bps;
 			break;
 
 		default:
@@ -1578,7 +1657,7 @@ int skl_tplg_be_update_params(struct snd_soc_dai *dai,
 
 static const struct snd_soc_tplg_widget_events skl_tplg_widget_ops[] = {
 	{SKL_MIXER_EVENT, skl_tplg_mixer_event},
-	{SKL_VMIXER_EVENT, skl_tplg_vmixer_event},
+	{SKL_VMIXER_EVENT, skl_tplg_mixer_event},
 	{SKL_PGA_EVENT, skl_tplg_pga_event},
 };
 
@@ -1632,7 +1711,7 @@ static int skl_tplg_add_pipe(struct device *dev,
 	list_for_each_entry(ppl, &skl->ppl_list, node) {
 		if (ppl->pipe->ppl_id == tkn_elem->value) {
 			mconfig->pipe = ppl->pipe;
-			return EEXIST;
+			return -EEXIST;
 		}
 	}
 
@@ -1924,11 +2003,13 @@ static int skl_tplg_get_token(struct device *dev,
 		ret = skl_tplg_add_pipe(dev,
 				mconfig, skl, tkn_elem);
 
-		if (ret < 0)
+		if (ret < 0) {
+			if (ret == -EEXIST) {
+				is_pipe_exists = 1;
+				break;
+			}
 			return is_pipe_exists;
-
-		if (ret == EEXIST)
-			is_pipe_exists = 1;
+		}
 
 		break;
 
diff --git a/sound/soc/intel/skylake/skl-topology.h b/sound/soc/intel/skylake/skl-topology.h
index fefab0e99a3b..cc64d6bdb4f6 100644
--- a/sound/soc/intel/skylake/skl-topology.h
+++ b/sound/soc/intel/skylake/skl-topology.h
@@ -257,6 +257,8 @@ struct skl_pipe_params {
 	snd_pcm_format_t format;
 	int link_index;
 	int stream;
+	unsigned int host_bps;
+	unsigned int link_bps;
 };
 
 struct skl_pipe {
@@ -334,17 +336,10 @@ struct skl_pipeline {
 	struct list_head node;
 };
 
-#define SKL_LIB_NAME_LENGTH 128
-#define SKL_MAX_LIB 16
-
-struct skl_lib_info {
-	char name[SKL_LIB_NAME_LENGTH];
-	const struct firmware *fw;
-};
-
-struct skl_manifest {
-	u32 lib_count;
-	struct skl_lib_info lib[SKL_MAX_LIB];
+struct skl_module_deferred_bind {
+	struct skl_module_cfg *src;
+	struct skl_module_cfg *dst;
+	struct list_head node;
 };
 
 static inline struct skl *get_skl_ctx(struct device *dev)
diff --git a/sound/soc/intel/skylake/skl.c b/sound/soc/intel/skylake/skl.c
index 0c57d4eaae3a..6df3b317a476 100644
--- a/sound/soc/intel/skylake/skl.c
+++ b/sound/soc/intel/skylake/skl.c
@@ -512,7 +512,7 @@ static int probe_codec(struct hdac_ext_bus *ebus, int addr)
 	struct hdac_bus *bus = ebus_to_hbus(ebus);
 	unsigned int cmd = (addr << 28) | (AC_NODE_ROOT << 20) |
 		(AC_VERB_PARAMETERS << 8) | AC_PAR_VENDOR_ID;
-	unsigned int res;
+	unsigned int res = -1;
 
 	mutex_lock(&bus->cmd_mutex);
 	snd_hdac_bus_send_cmd(bus, cmd);
diff --git a/sound/soc/intel/skylake/skl.h b/sound/soc/intel/skylake/skl.h
index bbef77d2b917..a454f6035f3e 100644
--- a/sound/soc/intel/skylake/skl.h
+++ b/sound/soc/intel/skylake/skl.h
@@ -27,27 +27,6 @@
 
 #define SKL_SUSPEND_DELAY 2000
 
-/* Vendor Specific Registers */
-#define AZX_REG_VS_EM1			0x1000
-#define AZX_REG_VS_INRC			0x1004
-#define AZX_REG_VS_OUTRC		0x1008
-#define AZX_REG_VS_FIFOTRK		0x100C
-#define AZX_REG_VS_FIFOTRK2		0x1010
-#define AZX_REG_VS_EM2			0x1030
-#define AZX_REG_VS_EM3L			0x1038
-#define AZX_REG_VS_EM3U			0x103C
-#define AZX_REG_VS_EM4L			0x1040
-#define AZX_REG_VS_EM4U			0x1044
-#define AZX_REG_VS_LTRC			0x1048
-#define AZX_REG_VS_D0I3C		0x104A
-#define AZX_REG_VS_PCE			0x104B
-#define AZX_REG_VS_L2MAGC		0x1050
-#define AZX_REG_VS_L2LAHPT		0x1054
-#define AZX_REG_VS_SDXDPIB_XBASE	0x1084
-#define AZX_REG_VS_SDXDPIB_XINTERVAL	0x20
-#define AZX_REG_VS_SDXEFIFOS_XBASE	0x1094
-#define AZX_REG_VS_SDXEFIFOS_XINTERVAL	0x20
-
 #define AZX_PCIREG_PGCTL		0x44
 #define AZX_PGCTL_LSRMD_MASK		(1 << 4)
 #define AZX_PCIREG_CGCTL		0x48
@@ -77,6 +56,7 @@ struct skl {
 
 	struct skl_dsp_resource resource;
 	struct list_head ppl_list;
+	struct list_head bind_list;
 
 	const char *fw_name;
 	char tplg_name[64];
diff --git a/sound/soc/mediatek/Kconfig b/sound/soc/mediatek/Kconfig
index d7013bde6f45..5c68797f36c4 100644
--- a/sound/soc/mediatek/Kconfig
+++ b/sound/soc/mediatek/Kconfig
@@ -22,6 +22,16 @@ config SND_SOC_MT2701_CS42448
 	  Select Y if you have such device.
 	  If unsure select "N".
 
+config SND_SOC_MT2701_WM8960
+	tristate "ASoc Audio driver for MT2701 with WM8960 codec"
+	depends on SND_SOC_MT2701 && I2C
+	select SND_SOC_WM8960
+	help
+	  This adds ASoC driver for Mediatek MT2701 boards
+	  with the WM8960 codecs.
+	  Select Y if you have such device.
+	  If unsure select "N".
+
 config SND_SOC_MT8173
 	tristate "ASoC support for Mediatek MT8173 chip"
 	depends on ARCH_MEDIATEK
diff --git a/sound/soc/mediatek/mt2701/Makefile b/sound/soc/mediatek/mt2701/Makefile
index 31c3d04d4942..c91deb6aca21 100644
--- a/sound/soc/mediatek/mt2701/Makefile
+++ b/sound/soc/mediatek/mt2701/Makefile
@@ -17,3 +17,4 @@ obj-$(CONFIG_SND_SOC_MT2701) += snd-soc-mt2701-afe.o
 
 # machine driver
 obj-$(CONFIG_SND_SOC_MT2701_CS42448) += mt2701-cs42448.o
+obj-$(CONFIG_SND_SOC_MT2701_WM8960) += mt2701-wm8960.o
diff --git a/sound/soc/mediatek/mt2701/mt2701-afe-pcm.c b/sound/soc/mediatek/mt2701/mt2701-afe-pcm.c
index c7fa3e663463..bc5d4db94de6 100644
--- a/sound/soc/mediatek/mt2701/mt2701-afe-pcm.c
+++ b/sound/soc/mediatek/mt2701/mt2701-afe-pcm.c
@@ -604,6 +604,22 @@ static struct snd_soc_dai_ops mt2701_btmrg_ops = {
 static struct snd_soc_dai_driver mt2701_afe_pcm_dais[] = {
 	/* FE DAIs: memory intefaces to CPU */
 	{
+		.name = "PCMO0",
+		.id = MT2701_MEMIF_DL1,
+		.suspend = mtk_afe_dai_suspend,
+		.resume = mtk_afe_dai_resume,
+		.playback = {
+			.stream_name = "DL1",
+			.channels_min = 1,
+			.channels_max = 2,
+			.rates = SNDRV_PCM_RATE_8000_192000,
+			.formats = (SNDRV_PCM_FMTBIT_S16_LE
+				| SNDRV_PCM_FMTBIT_S24_LE
+				| SNDRV_PCM_FMTBIT_S32_LE)
+		},
+		.ops = &mt2701_single_memif_dai_ops,
+	},
+	{
 		.name = "PCM_multi",
 		.id = MT2701_MEMIF_DLM,
 		.suspend = mtk_afe_dai_suspend,
diff --git a/sound/soc/mediatek/mt2701/mt2701-cs42448.c b/sound/soc/mediatek/mt2701/mt2701-cs42448.c
index 1e7e8d43fd8a..aa5b31b121e3 100644
--- a/sound/soc/mediatek/mt2701/mt2701-cs42448.c
+++ b/sound/soc/mediatek/mt2701/mt2701-cs42448.c
@@ -129,7 +129,7 @@ static int mt2701_cs42448_fe_ops_startup(struct snd_pcm_substream *substream)
 	return 0;
 }
 
-static struct snd_soc_ops mt2701_cs42448_48k_fe_ops = {
+static const struct snd_soc_ops mt2701_cs42448_48k_fe_ops = {
 	.startup = mt2701_cs42448_fe_ops_startup,
 };
 
diff --git a/sound/soc/mediatek/mt2701/mt2701-wm8960.c b/sound/soc/mediatek/mt2701/mt2701-wm8960.c
new file mode 100644
index 000000000000..a08ce2323bdc
--- /dev/null
+++ b/sound/soc/mediatek/mt2701/mt2701-wm8960.c
@@ -0,0 +1,176 @@
+/*
+ * mt2701-wm8960.c  --  MT2701 WM8960 ALSA SoC machine driver
+ *
+ * Copyright (c) 2017 MediaTek Inc.
+ * Author: Ryder Lee <ryder.lee@mediatek.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 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include <sound/soc.h>
+
+#include "mt2701-afe-common.h"
+
+static const struct snd_soc_dapm_widget mt2701_wm8960_widgets[] = {
+	SND_SOC_DAPM_HP("Headphone", NULL),
+	SND_SOC_DAPM_MIC("AMIC", NULL),
+};
+
+static const struct snd_kcontrol_new mt2701_wm8960_controls[] = {
+	SOC_DAPM_PIN_SWITCH("Headphone"),
+	SOC_DAPM_PIN_SWITCH("AMIC"),
+};
+
+static int mt2701_wm8960_be_ops_hw_params(struct snd_pcm_substream *substream,
+					  struct snd_pcm_hw_params *params)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct snd_soc_dai *codec_dai = rtd->codec_dai;
+	struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+	unsigned int mclk_rate;
+	unsigned int rate = params_rate(params);
+	unsigned int div_mclk_over_bck = rate > 192000 ? 2 : 4;
+	unsigned int div_bck_over_lrck = 64;
+
+	mclk_rate = rate * div_bck_over_lrck * div_mclk_over_bck;
+
+	snd_soc_dai_set_sysclk(cpu_dai, 0, mclk_rate, SND_SOC_CLOCK_OUT);
+	snd_soc_dai_set_sysclk(codec_dai, 0, mclk_rate, SND_SOC_CLOCK_IN);
+
+	return 0;
+}
+
+static struct snd_soc_ops mt2701_wm8960_be_ops = {
+	.hw_params = mt2701_wm8960_be_ops_hw_params
+};
+
+static struct snd_soc_dai_link mt2701_wm8960_dai_links[] = {
+	/* FE */
+	{
+		.name = "wm8960-playback",
+		.stream_name = "wm8960-playback",
+		.cpu_dai_name = "PCMO0",
+		.codec_name = "snd-soc-dummy",
+		.codec_dai_name = "snd-soc-dummy-dai",
+		.trigger = {SND_SOC_DPCM_TRIGGER_POST,
+			    SND_SOC_DPCM_TRIGGER_POST},
+		.dynamic = 1,
+		.dpcm_playback = 1,
+	},
+	{
+		.name = "wm8960-capture",
+		.stream_name = "wm8960-capture",
+		.cpu_dai_name = "PCM0",
+		.codec_name = "snd-soc-dummy",
+		.codec_dai_name = "snd-soc-dummy-dai",
+		.trigger = {SND_SOC_DPCM_TRIGGER_POST,
+			    SND_SOC_DPCM_TRIGGER_POST},
+		.dynamic = 1,
+		.dpcm_capture = 1,
+	},
+	/* BE */
+	{
+		.name = "wm8960-codec",
+		.cpu_dai_name = "I2S0",
+		.no_pcm = 1,
+		.codec_dai_name = "wm8960-hifi",
+		.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBS_CFS
+			| SND_SOC_DAIFMT_GATED,
+		.ops = &mt2701_wm8960_be_ops,
+		.dpcm_playback = 1,
+		.dpcm_capture = 1,
+	},
+};
+
+static struct snd_soc_card mt2701_wm8960_card = {
+	.name = "mt2701-wm8960",
+	.owner = THIS_MODULE,
+	.dai_link = mt2701_wm8960_dai_links,
+	.num_links = ARRAY_SIZE(mt2701_wm8960_dai_links),
+	.controls = mt2701_wm8960_controls,
+	.num_controls = ARRAY_SIZE(mt2701_wm8960_controls),
+	.dapm_widgets = mt2701_wm8960_widgets,
+	.num_dapm_widgets = ARRAY_SIZE(mt2701_wm8960_widgets),
+};
+
+static int mt2701_wm8960_machine_probe(struct platform_device *pdev)
+{
+	struct snd_soc_card *card = &mt2701_wm8960_card;
+	struct device_node *platform_node, *codec_node;
+	int ret, i;
+
+	platform_node = of_parse_phandle(pdev->dev.of_node,
+					 "mediatek,platform", 0);
+	if (!platform_node) {
+		dev_err(&pdev->dev, "Property 'platform' missing or invalid\n");
+		return -EINVAL;
+	}
+	for (i = 0; i < card->num_links; i++) {
+		if (mt2701_wm8960_dai_links[i].platform_name)
+			continue;
+		mt2701_wm8960_dai_links[i].platform_of_node = platform_node;
+	}
+
+	card->dev = &pdev->dev;
+
+	codec_node = of_parse_phandle(pdev->dev.of_node,
+				      "mediatek,audio-codec", 0);
+	if (!codec_node) {
+		dev_err(&pdev->dev,
+			"Property 'audio-codec' missing or invalid\n");
+		return -EINVAL;
+	}
+	for (i = 0; i < card->num_links; i++) {
+		if (mt2701_wm8960_dai_links[i].codec_name)
+			continue;
+		mt2701_wm8960_dai_links[i].codec_of_node = codec_node;
+	}
+
+	ret = snd_soc_of_parse_audio_routing(card, "audio-routing");
+	if (ret) {
+		dev_err(&pdev->dev, "failed to parse audio-routing: %d\n", ret);
+		return ret;
+	}
+
+	ret = devm_snd_soc_register_card(&pdev->dev, card);
+	if (ret)
+		dev_err(&pdev->dev, "%s snd_soc_register_card fail %d\n",
+			__func__, ret);
+
+	return ret;
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id mt2701_wm8960_machine_dt_match[] = {
+	{.compatible = "mediatek,mt2701-wm8960-machine",},
+	{}
+};
+#endif
+
+static struct platform_driver mt2701_wm8960_machine = {
+	.driver = {
+		.name = "mt2701-wm8960",
+		.owner = THIS_MODULE,
+#ifdef CONFIG_OF
+		.of_match_table = mt2701_wm8960_machine_dt_match,
+#endif
+	},
+	.probe = mt2701_wm8960_machine_probe,
+};
+
+module_platform_driver(mt2701_wm8960_machine);
+
+/* Module information */
+MODULE_DESCRIPTION("MT2701 WM8960 ALSA SoC machine driver");
+MODULE_AUTHOR("Ryder Lee <ryder.lee@mediatek.com>");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("mt2701 wm8960 soc card");
+
diff --git a/sound/soc/mediatek/mt8173/mt8173-max98090.c b/sound/soc/mediatek/mt8173/mt8173-max98090.c
index 46c8e6ae00b4..e0c2b23ec711 100644
--- a/sound/soc/mediatek/mt8173/mt8173-max98090.c
+++ b/sound/soc/mediatek/mt8173/mt8173-max98090.c
@@ -67,7 +67,7 @@ static int mt8173_max98090_hw_params(struct snd_pcm_substream *substream,
 				      SND_SOC_CLOCK_IN);
 }
 
-static struct snd_soc_ops mt8173_max98090_ops = {
+static const struct snd_soc_ops mt8173_max98090_ops = {
 	.hw_params = mt8173_max98090_hw_params,
 };
 
diff --git a/sound/soc/mediatek/mt8173/mt8173-rt5650-rt5514.c b/sound/soc/mediatek/mt8173/mt8173-rt5650-rt5514.c
index 467f7049a288..5e383eb456a4 100644
--- a/sound/soc/mediatek/mt8173/mt8173-rt5650-rt5514.c
+++ b/sound/soc/mediatek/mt8173/mt8173-rt5650-rt5514.c
@@ -75,7 +75,7 @@ static int mt8173_rt5650_rt5514_hw_params(struct snd_pcm_substream *substream,
 	return 0;
 }
 
-static struct snd_soc_ops mt8173_rt5650_rt5514_ops = {
+static const struct snd_soc_ops mt8173_rt5650_rt5514_ops = {
 	.hw_params = mt8173_rt5650_rt5514_hw_params,
 };
 
diff --git a/sound/soc/mediatek/mt8173/mt8173-rt5650-rt5676.c b/sound/soc/mediatek/mt8173/mt8173-rt5650-rt5676.c
index 1b8b2a778845..fed1f15a39c2 100644
--- a/sound/soc/mediatek/mt8173/mt8173-rt5650-rt5676.c
+++ b/sound/soc/mediatek/mt8173/mt8173-rt5650-rt5676.c
@@ -79,7 +79,7 @@ static int mt8173_rt5650_rt5676_hw_params(struct snd_pcm_substream *substream,
 	return 0;
 }
 
-static struct snd_soc_ops mt8173_rt5650_rt5676_ops = {
+static const struct snd_soc_ops mt8173_rt5650_rt5676_ops = {
 	.hw_params = mt8173_rt5650_rt5676_hw_params,
 };
 
diff --git a/sound/soc/mediatek/mt8173/mt8173-rt5650.c b/sound/soc/mediatek/mt8173/mt8173-rt5650.c
index ba65f4157a7e..a78470839b65 100644
--- a/sound/soc/mediatek/mt8173/mt8173-rt5650.c
+++ b/sound/soc/mediatek/mt8173/mt8173-rt5650.c
@@ -105,7 +105,7 @@ static int mt8173_rt5650_hw_params(struct snd_pcm_substream *substream,
 	return 0;
 }
 
-static struct snd_soc_ops mt8173_rt5650_ops = {
+static const struct snd_soc_ops mt8173_rt5650_ops = {
 	.hw_params = mt8173_rt5650_hw_params,
 };
 
diff --git a/sound/soc/omap/am3517evm.c b/sound/soc/omap/am3517evm.c
index 25a33e9d417a..d5651026ec10 100644
--- a/sound/soc/omap/am3517evm.c
+++ b/sound/soc/omap/am3517evm.c
@@ -49,7 +49,7 @@ static int am3517evm_hw_params(struct snd_pcm_substream *substream,
 	return ret;
 }
 
-static struct snd_soc_ops am3517evm_ops = {
+static const struct snd_soc_ops am3517evm_ops = {
 	.hw_params = am3517evm_hw_params,
 };
 
diff --git a/sound/soc/omap/n810.c b/sound/soc/omap/n810.c
index fdecb7043174..71e5f31fa306 100644
--- a/sound/soc/omap/n810.c
+++ b/sound/soc/omap/n810.c
@@ -124,7 +124,7 @@ static int n810_hw_params(struct snd_pcm_substream *substream,
 	return err;
 }
 
-static struct snd_soc_ops n810_ops = {
+static const struct snd_soc_ops n810_ops = {
 	.startup = n810_startup,
 	.hw_params = n810_hw_params,
 	.shutdown = n810_shutdown,
diff --git a/sound/soc/omap/omap-abe-twl6040.c b/sound/soc/omap/omap-abe-twl6040.c
index 89fe95e877db..614b18d2f631 100644
--- a/sound/soc/omap/omap-abe-twl6040.c
+++ b/sound/soc/omap/omap-abe-twl6040.c
@@ -70,7 +70,7 @@ static int omap_abe_hw_params(struct snd_pcm_substream *substream,
 	return ret;
 }
 
-static struct snd_soc_ops omap_abe_ops = {
+static const struct snd_soc_ops omap_abe_ops = {
 	.hw_params = omap_abe_hw_params,
 };
 
diff --git a/sound/soc/omap/omap-twl4030.c b/sound/soc/omap/omap-twl4030.c
index 743131473056..a24b0dedabb9 100644
--- a/sound/soc/omap/omap-twl4030.c
+++ b/sound/soc/omap/omap-twl4030.c
@@ -73,7 +73,7 @@ static int omap_twl4030_hw_params(struct snd_pcm_substream *substream,
 	return snd_soc_runtime_set_dai_fmt(rtd, fmt);
 }
 
-static struct snd_soc_ops omap_twl4030_ops = {
+static const struct snd_soc_ops omap_twl4030_ops = {
 	.hw_params = omap_twl4030_hw_params,
 };
 
diff --git a/sound/soc/omap/omap3pandora.c b/sound/soc/omap/omap3pandora.c
index 732e749a1f8e..4e3de712159c 100644
--- a/sound/soc/omap/omap3pandora.c
+++ b/sound/soc/omap/omap3pandora.c
@@ -184,7 +184,7 @@ static int omap3pandora_in_init(struct snd_soc_pcm_runtime *rtd)
 	return 0;
 }
 
-static struct snd_soc_ops omap3pandora_ops = {
+static const struct snd_soc_ops omap3pandora_ops = {
 	.hw_params = omap3pandora_hw_params,
 };
 
diff --git a/sound/soc/omap/osk5912.c b/sound/soc/omap/osk5912.c
index aa4053bf6710..e4096779ca05 100644
--- a/sound/soc/omap/osk5912.c
+++ b/sound/soc/omap/osk5912.c
@@ -68,7 +68,7 @@ static int osk_hw_params(struct snd_pcm_substream *substream,
 	return err;
 }
 
-static struct snd_soc_ops osk_ops = {
+static const struct snd_soc_ops osk_ops = {
 	.startup = osk_startup,
 	.hw_params = osk_hw_params,
 	.shutdown = osk_shutdown,
diff --git a/sound/soc/omap/rx51.c b/sound/soc/omap/rx51.c
index a76845748a10..3aeb65feaea1 100644
--- a/sound/soc/omap/rx51.c
+++ b/sound/soc/omap/rx51.c
@@ -123,7 +123,7 @@ static int rx51_hw_params(struct snd_pcm_substream *substream,
 				      SND_SOC_CLOCK_IN);
 }
 
-static struct snd_soc_ops rx51_ops = {
+static const struct snd_soc_ops rx51_ops = {
 	.startup = rx51_startup,
 	.hw_params = rx51_hw_params,
 };
@@ -433,10 +433,9 @@ static int rx51_soc_probe(struct platform_device *pdev)
 	}
 
 	pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
-	if (pdata == NULL) {
-		dev_err(card->dev, "failed to create private data\n");
+	if (pdata == NULL)
 		return -ENOMEM;
-	}
+
 	snd_soc_card_set_drvdata(card, pdata);
 
 	pdata->tvout_selection_gpio = devm_gpiod_get(card->dev,
diff --git a/sound/soc/pxa/brownstone.c b/sound/soc/pxa/brownstone.c
index b6cb9950f05d..9a3f5b799720 100644
--- a/sound/soc/pxa/brownstone.c
+++ b/sound/soc/pxa/brownstone.c
@@ -74,7 +74,7 @@ static int brownstone_wm8994_hw_params(struct snd_pcm_substream *substream,
 }
 
 /* machine stream operations */
-static struct snd_soc_ops brownstone_ops = {
+static const struct snd_soc_ops brownstone_ops = {
 	.hw_params = brownstone_wm8994_hw_params,
 };
 
diff --git a/sound/soc/pxa/corgi.c b/sound/soc/pxa/corgi.c
index 311774e9ca46..054e0d65db9d 100644
--- a/sound/soc/pxa/corgi.c
+++ b/sound/soc/pxa/corgi.c
@@ -154,7 +154,7 @@ static int corgi_hw_params(struct snd_pcm_substream *substream,
 	return 0;
 }
 
-static struct snd_soc_ops corgi_ops = {
+static const struct snd_soc_ops corgi_ops = {
 	.startup = corgi_startup,
 	.hw_params = corgi_hw_params,
 	.shutdown = corgi_shutdown,
diff --git a/sound/soc/pxa/e750_wm9705.c b/sound/soc/pxa/e750_wm9705.c
index fdcd94adee7c..82bcbbb1841b 100644
--- a/sound/soc/pxa/e750_wm9705.c
+++ b/sound/soc/pxa/e750_wm9705.c
@@ -81,7 +81,7 @@ static struct snd_soc_dai_link e750_dai[] = {
 		.name = "AC97 Aux",
 		.stream_name = "AC97 Aux",
 		.cpu_dai_name = "pxa2xx-ac97-aux",
-		.codec_dai_name ="wm9705-aux",
+		.codec_dai_name = "wm9705-aux",
 		.platform_name = "pxa-pcm-audio",
 		.codec_name = "wm9705-codec",
 	},
diff --git a/sound/soc/pxa/e800_wm9712.c b/sound/soc/pxa/e800_wm9712.c
index 2df714f70ec0..1ed8aa2348f1 100644
--- a/sound/soc/pxa/e800_wm9712.c
+++ b/sound/soc/pxa/e800_wm9712.c
@@ -81,7 +81,7 @@ static struct snd_soc_dai_link e800_dai[] = {
 		.name = "AC97 Aux",
 		.stream_name = "AC97 Aux",
 		.cpu_dai_name = "pxa2xx-ac97-aux",
-		.codec_dai_name ="wm9712-aux",
+		.codec_dai_name = "wm9712-aux",
 		.platform_name = "pxa-pcm-audio",
 		.codec_name = "wm9712-codec",
 	},
diff --git a/sound/soc/pxa/em-x270.c b/sound/soc/pxa/em-x270.c
index 6f2020f6c8d3..e046770ce70e 100644
--- a/sound/soc/pxa/em-x270.c
+++ b/sound/soc/pxa/em-x270.c
@@ -43,7 +43,7 @@ static struct snd_soc_dai_link em_x270_dai[] = {
 		.name = "AC97 Aux",
 		.stream_name = "AC97 Aux",
 		.cpu_dai_name = "pxa2xx-ac97-aux",
-		.codec_dai_name ="wm9712-aux",
+		.codec_dai_name = "wm9712-aux",
 		.platform_name = "pxa-pcm-audio",
 		.codec_name = "wm9712-codec",
 	},
diff --git a/sound/soc/pxa/hx4700.c b/sound/soc/pxa/hx4700.c
index 85483049b916..a9ac881c2e14 100644
--- a/sound/soc/pxa/hx4700.c
+++ b/sound/soc/pxa/hx4700.c
@@ -79,7 +79,7 @@ static int hx4700_hw_params(struct snd_pcm_substream *substream,
 	return 0;
 }
 
-static struct snd_soc_ops hx4700_ops = {
+static const struct snd_soc_ops hx4700_ops = {
 	.hw_params = hx4700_hw_params,
 };
 
diff --git a/sound/soc/pxa/imote2.c b/sound/soc/pxa/imote2.c
index 9d0e40771ef5..78475376f971 100644
--- a/sound/soc/pxa/imote2.c
+++ b/sound/soc/pxa/imote2.c
@@ -42,7 +42,7 @@ static int imote2_asoc_hw_params(struct snd_pcm_substream *substream,
 	return ret;
 }
 
-static struct snd_soc_ops imote2_asoc_ops = {
+static const struct snd_soc_ops imote2_asoc_ops = {
 	.hw_params = imote2_asoc_hw_params,
 };
 
diff --git a/sound/soc/pxa/magician.c b/sound/soc/pxa/magician.c
index 2d4d4455fe87..2fc012b06c43 100644
--- a/sound/soc/pxa/magician.c
+++ b/sound/soc/pxa/magician.c
@@ -255,12 +255,12 @@ static int magician_capture_hw_params(struct snd_pcm_substream *substream,
 	return 0;
 }
 
-static struct snd_soc_ops magician_capture_ops = {
+static const struct snd_soc_ops magician_capture_ops = {
 	.startup = magician_startup,
 	.hw_params = magician_capture_hw_params,
 };
 
-static struct snd_soc_ops magician_playback_ops = {
+static const struct snd_soc_ops magician_playback_ops = {
 	.startup = magician_startup,
 	.hw_params = magician_playback_hw_params,
 };
diff --git a/sound/soc/pxa/mioa701_wm9713.c b/sound/soc/pxa/mioa701_wm9713.c
index 8760a6687885..c4c6fbedc723 100644
--- a/sound/soc/pxa/mioa701_wm9713.c
+++ b/sound/soc/pxa/mioa701_wm9713.c
@@ -157,7 +157,7 @@ static struct snd_soc_dai_link mioa701_dai[] = {
 		.name = "AC97 Aux",
 		.stream_name = "AC97 Aux",
 		.cpu_dai_name = "pxa2xx-ac97-aux",
-		.codec_dai_name ="wm9713-aux",
+		.codec_dai_name = "wm9713-aux",
 		.codec_name = "wm9713-codec",
 		.platform_name = "pxa-pcm-audio",
 		.ops = &mioa701_ops,
diff --git a/sound/soc/pxa/mmp-pcm.c b/sound/soc/pxa/mmp-pcm.c
index 96df9b2d8fc4..5b5f1a442891 100644
--- a/sound/soc/pxa/mmp-pcm.c
+++ b/sound/soc/pxa/mmp-pcm.c
@@ -166,7 +166,6 @@ static void mmp_pcm_free_dma_buffers(struct snd_pcm *pcm)
 		buf->area = NULL;
 	}
 
-	return;
 }
 
 static int mmp_pcm_preallocate_dma_buffer(struct snd_pcm_substream *substream,
diff --git a/sound/soc/pxa/mmp-sspa.c b/sound/soc/pxa/mmp-sspa.c
index ca8b23f8c525..9cc35012e6e5 100644
--- a/sound/soc/pxa/mmp-sspa.c
+++ b/sound/soc/pxa/mmp-sspa.c
@@ -119,7 +119,6 @@ static void mmp_sspa_shutdown(struct snd_pcm_substream *substream,
 	clk_disable(priv->sspa->clk);
 	clk_disable(priv->sysclk);
 
-	return;
 }
 
 /*
diff --git a/sound/soc/pxa/poodle.c b/sound/soc/pxa/poodle.c
index a879aba0691f..b6693f32fc02 100644
--- a/sound/soc/pxa/poodle.c
+++ b/sound/soc/pxa/poodle.c
@@ -129,7 +129,7 @@ static int poodle_hw_params(struct snd_pcm_substream *substream,
 	return 0;
 }
 
-static struct snd_soc_ops poodle_ops = {
+static const struct snd_soc_ops poodle_ops = {
 	.startup = poodle_startup,
 	.hw_params = poodle_hw_params,
 	.shutdown = poodle_shutdown,
diff --git a/sound/soc/pxa/pxa-ssp.c b/sound/soc/pxa/pxa-ssp.c
index 3cad990dad2c..0291c7cb64eb 100644
--- a/sound/soc/pxa/pxa-ssp.c
+++ b/sound/soc/pxa/pxa-ssp.c
@@ -354,6 +354,7 @@ static int pxa_ssp_set_dai_pll(struct snd_soc_dai *cpu_dai, int pll_id,
 		if (ssp->type == PXA3xx_SSP) {
 			u32 val;
 			u64 tmp = 19968;
+
 			tmp *= 1000000;
 			do_div(tmp, freq_out);
 			val = tmp;
@@ -590,13 +591,13 @@ static int pxa_ssp_hw_params(struct snd_pcm_substream *substream,
 
 		if ((pxa_ssp_get_scr(ssp) == 4) && (width == 16)) {
 			/* This is a special case where the bitclk is 64fs
-			* and we're not dealing with 2*32 bits of audio
-			* samples.
-			*
-			* The SSP values used for that are all found out by
-			* trying and failing a lot; some of the registers
-			* needed for that mode are only available on PXA3xx.
-			*/
+			 * and we're not dealing with 2*32 bits of audio
+			 * samples.
+			 *
+			 * The SSP values used for that are all found out by
+			 * trying and failing a lot; some of the registers
+			 * needed for that mode are only available on PXA3xx.
+			 */
 			if (ssp->type != PXA3xx_SSP)
 				return -EINVAL;
 
diff --git a/sound/soc/pxa/pxa2xx-ac97.c b/sound/soc/pxa/pxa2xx-ac97.c
index 2e2fb1838ec2..f49bf02e5ec2 100644
--- a/sound/soc/pxa/pxa2xx-ac97.c
+++ b/sound/soc/pxa/pxa2xx-ac97.c
@@ -140,9 +140,8 @@ static int pxa2xx_ac97_mic_startup(struct snd_pcm_substream *substream,
 {
 	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
 		return -ENODEV;
-	else
-		snd_soc_dai_set_dma_data(cpu_dai, substream,
-					 &pxa2xx_ac97_pcm_mic_mono_in);
+	snd_soc_dai_set_dma_data(cpu_dai, substream,
+				 &pxa2xx_ac97_pcm_mic_mono_in);
 
 	return 0;
 }
diff --git a/sound/soc/pxa/pxa2xx-i2s.c b/sound/soc/pxa/pxa2xx-i2s.c
index 0389cf7b4b1e..3fb60baf6eab 100644
--- a/sound/soc/pxa/pxa2xx-i2s.c
+++ b/sound/soc/pxa/pxa2xx-i2s.c
@@ -46,10 +46,10 @@
 #define SACR0_STRF	(1 << 5)	/* FIFO Select for EFWR Special Function */
 #define SACR0_EFWR	(1 << 4)	/* Enable EFWR Function  */
 #define SACR0_RST	(1 << 3)	/* FIFO, i2s Register Reset */
-#define SACR0_BCKD	(1 << 2) 	/* Bit Clock Direction */
+#define SACR0_BCKD	(1 << 2)	/* Bit Clock Direction */
 #define SACR0_ENB	(1 << 0)	/* Enable I2S Link */
 #define SACR1_ENLBF	(1 << 5)	/* Enable Loopback */
-#define SACR1_DRPL	(1 << 4) 	/* Disable Replaying Function */
+#define SACR1_DRPL	(1 << 4)	/* Disable Replaying Function */
 #define SACR1_DREC	(1 << 3)	/* Disable Recording Function */
 #define SACR1_AMSL	(1 << 0)	/* Specify Alternate Mode */
 
@@ -60,7 +60,7 @@
 #define SASR0_TFS	(1 << 3)	/* Tx FIFO Service Request */
 #define SASR0_BSY	(1 << 2)	/* I2S Busy */
 #define SASR0_RNE	(1 << 1)	/* Rx FIFO Not Empty */
-#define SASR0_TNF	(1 << 0) 	/* Tx FIFO Not Empty */
+#define SASR0_TNF	(1 << 0)	/* Tx FIFO Not Empty */
 
 #define SAICR_ROR	(1 << 6)	/* Clear Rx FIFO Overrun Interrupt */
 #define SAICR_TUR	(1 << 5)	/* Clear Tx FIFO Underrun Interrupt */
@@ -119,7 +119,7 @@ static int pxa_i2s_wait(void)
 	int i;
 
 	/* flush the Rx FIFO */
-	for(i = 0; i < 16; i++)
+	for (i = 0; i < 16; i++)
 		SADR;
 	return 0;
 }
diff --git a/sound/soc/pxa/pxa2xx-pcm.c b/sound/soc/pxa/pxa2xx-pcm.c
index 410d48b93031..b51d7a0755d5 100644
--- a/sound/soc/pxa/pxa2xx-pcm.c
+++ b/sound/soc/pxa/pxa2xx-pcm.c
@@ -85,7 +85,7 @@ static int pxa2xx_soc_pcm_new(struct snd_soc_pcm_runtime *rtd)
 }
 
 static struct snd_soc_platform_driver pxa2xx_soc_platform = {
-	.ops 	= &pxa2xx_pcm_ops,
+	.ops		= &pxa2xx_pcm_ops,
 	.pcm_new	= pxa2xx_soc_pcm_new,
 	.pcm_free	= pxa2xx_pcm_free_dma_buffers,
 };
diff --git a/sound/soc/pxa/raumfeld.c b/sound/soc/pxa/raumfeld.c
index 552b763005ed..111a907c4eb9 100644
--- a/sound/soc/pxa/raumfeld.c
+++ b/sound/soc/pxa/raumfeld.c
@@ -132,7 +132,7 @@ static int raumfeld_cs4270_hw_params(struct snd_pcm_substream *substream,
 	return 0;
 }
 
-static struct snd_soc_ops raumfeld_cs4270_ops = {
+static const struct snd_soc_ops raumfeld_cs4270_ops = {
 	.startup = raumfeld_cs4270_startup,
 	.shutdown = raumfeld_cs4270_shutdown,
 	.hw_params = raumfeld_cs4270_hw_params,
@@ -228,14 +228,12 @@ static struct snd_soc_ops raumfeld_ak4104_ops = {
 	.codec_name	= "spi0.0",			\
 }
 
-static struct snd_soc_dai_link snd_soc_raumfeld_connector_dai[] =
-{
+static struct snd_soc_dai_link snd_soc_raumfeld_connector_dai[] = {
 	DAI_LINK_CS4270,
 	DAI_LINK_AK4104,
 };
 
-static struct snd_soc_dai_link snd_soc_raumfeld_speaker_dai[] =
-{
+static struct snd_soc_dai_link snd_soc_raumfeld_speaker_dai[] = {
 	DAI_LINK_CS4270,
 };
 
diff --git a/sound/soc/pxa/spitz.c b/sound/soc/pxa/spitz.c
index 07d77cddac60..1671da648e95 100644
--- a/sound/soc/pxa/spitz.c
+++ b/sound/soc/pxa/spitz.c
@@ -156,7 +156,7 @@ static int spitz_hw_params(struct snd_pcm_substream *substream,
 	return 0;
 }
 
-static struct snd_soc_ops spitz_ops = {
+static const struct snd_soc_ops spitz_ops = {
 	.startup = spitz_startup,
 	.hw_params = spitz_hw_params,
 };
@@ -230,8 +230,8 @@ static const struct snd_soc_dapm_route spitz_audio_map[] = {
 	{"Headset Jack", NULL, "ROUT1"},
 
 	/* ext speaker connected to LOUT2, ROUT2  */
-	{"Ext Spk", NULL , "ROUT2"},
-	{"Ext Spk", NULL , "LOUT2"},
+	{"Ext Spk", NULL, "ROUT2"},
+	{"Ext Spk", NULL, "LOUT2"},
 
 	/* mic is connected to input 1 - with bias */
 	{"LINPUT1", NULL, "Mic Bias"},
diff --git a/sound/soc/pxa/tosa.c b/sound/soc/pxa/tosa.c
index e022b2a777f6..ae9c12e1ea2a 100644
--- a/sound/soc/pxa/tosa.c
+++ b/sound/soc/pxa/tosa.c
@@ -85,7 +85,7 @@ static int tosa_startup(struct snd_pcm_substream *substream)
 	return 0;
 }
 
-static struct snd_soc_ops tosa_ops = {
+static const struct snd_soc_ops tosa_ops = {
 	.startup = tosa_startup,
 };
 
@@ -133,7 +133,7 @@ static int tosa_set_spk(struct snd_kcontrol *kcontrol,
 static int tosa_hp_event(struct snd_soc_dapm_widget *w,
 	struct snd_kcontrol *k, int event)
 {
-	gpio_set_value(TOSA_GPIO_L_MUTE, SND_SOC_DAPM_EVENT_ON(event) ? 1 :0);
+	gpio_set_value(TOSA_GPIO_L_MUTE, SND_SOC_DAPM_EVENT_ON(event) ? 1 : 0);
 	return 0;
 }
 
diff --git a/sound/soc/pxa/z2.c b/sound/soc/pxa/z2.c
index 990b1aa6d7f6..5b0eccd2b4dd 100644
--- a/sound/soc/pxa/z2.c
+++ b/sound/soc/pxa/z2.c
@@ -119,8 +119,8 @@ static const struct snd_soc_dapm_route z2_audio_map[] = {
 	{"Headphone Jack", NULL, "ROUT1"},
 
 	/* ext speaker connected to LOUT2, ROUT2  */
-	{"Ext Spk", NULL , "ROUT2"},
-	{"Ext Spk", NULL , "LOUT2"},
+	{"Ext Spk", NULL, "ROUT2"},
+	{"Ext Spk", NULL, "LOUT2"},
 
 	/* mic is connected to R input 2 - with bias */
 	{"RINPUT2", NULL, "Mic Bias"},
@@ -152,7 +152,7 @@ err:
 	return ret;
 }
 
-static struct snd_soc_ops z2_ops = {
+static const struct snd_soc_ops z2_ops = {
 	.hw_params = z2_hw_params,
 };
 
diff --git a/sound/soc/pxa/zylonite.c b/sound/soc/pxa/zylonite.c
index 6fbcdf02c88d..ba468e560dd2 100644
--- a/sound/soc/pxa/zylonite.c
+++ b/sound/soc/pxa/zylonite.c
@@ -132,7 +132,7 @@ static int zylonite_voice_hw_params(struct snd_pcm_substream *substream,
 	return 0;
 }
 
-static struct snd_soc_ops zylonite_voice_ops = {
+static const struct snd_soc_ops zylonite_voice_ops = {
 	.hw_params = zylonite_voice_hw_params,
 };
 
diff --git a/sound/soc/qcom/lpass-apq8016.c b/sound/soc/qcom/lpass-apq8016.c
index 8aed72be3224..8a74844d99e2 100644
--- a/sound/soc/qcom/lpass-apq8016.c
+++ b/sound/soc/qcom/lpass-apq8016.c
@@ -231,6 +231,18 @@ static struct lpass_variant apq8016_data = {
 	.wrdma_channels		= 2,
 	.dai_driver		= apq8016_lpass_cpu_dai_driver,
 	.num_dai		= ARRAY_SIZE(apq8016_lpass_cpu_dai_driver),
+	.dai_osr_clk_names	= (const char *[]) {
+				"mi2s-osr-clk0",
+				"mi2s-osr-clk1",
+				"mi2s-osr-clk2",
+				"mi2s-osr-clk3",
+				},
+	.dai_bit_clk_names	= (const char *[]) {
+				"mi2s-bit-clk0",
+				"mi2s-bit-clk1",
+				"mi2s-bit-clk2",
+				"mi2s-bit-clk3",
+				},
 	.init			= apq8016_lpass_init,
 	.exit			= apq8016_lpass_exit,
 	.alloc_dma_channel	= apq8016_lpass_alloc_dma_channel,
diff --git a/sound/soc/qcom/lpass-cpu.c b/sound/soc/qcom/lpass-cpu.c
index 5202a584e0c6..292b103abada 100644
--- a/sound/soc/qcom/lpass-cpu.c
+++ b/sound/soc/qcom/lpass-cpu.c
@@ -429,7 +429,6 @@ int asoc_qcom_lpass_cpu_platform_probe(struct platform_device *pdev)
 	struct lpass_variant *variant;
 	struct device *dev = &pdev->dev;
 	const struct of_device_id *match;
-	char clk_name[16];
 	int ret, i, dai_id;
 
 	dsp_of_node = of_parse_phandle(pdev->dev.of_node, "qcom,adsp", 0);
@@ -477,31 +476,24 @@ int asoc_qcom_lpass_cpu_platform_probe(struct platform_device *pdev)
 
 	for (i = 0; i < variant->num_dai; i++) {
 		dai_id = variant->dai_driver[i].id;
-		if (variant->num_dai > 1)
-			sprintf(clk_name, "mi2s-osr-clk%d", i);
-		else
-			sprintf(clk_name, "mi2s-osr-clk");
-
 		drvdata->mi2s_osr_clk[dai_id] = devm_clk_get(&pdev->dev,
-								clk_name);
+					     variant->dai_osr_clk_names[i]);
 		if (IS_ERR(drvdata->mi2s_osr_clk[dai_id])) {
 			dev_warn(&pdev->dev,
-				"error getting optional mi2s-osr-clk: %ld\n",
+				"%s() error getting optional %s: %ld\n",
+				__func__,
+				variant->dai_osr_clk_names[i],
 				PTR_ERR(drvdata->mi2s_osr_clk[dai_id]));
 
 			drvdata->mi2s_osr_clk[dai_id] = NULL;
 		}
 
-		if (variant->num_dai > 1)
-			sprintf(clk_name, "mi2s-bit-clk%d", i);
-		else
-			sprintf(clk_name, "mi2s-bit-clk");
-
 		drvdata->mi2s_bit_clk[dai_id] = devm_clk_get(&pdev->dev,
-							    clk_name);
+						variant->dai_bit_clk_names[i]);
 		if (IS_ERR(drvdata->mi2s_bit_clk[dai_id])) {
 			dev_err(&pdev->dev,
-				"error getting mi2s-bit-clk: %ld\n",
+				"error getting %s: %ld\n",
+				variant->dai_bit_clk_names[i],
 				PTR_ERR(drvdata->mi2s_bit_clk[dai_id]));
 			return PTR_ERR(drvdata->mi2s_bit_clk[dai_id]);
 		}
diff --git a/sound/soc/qcom/lpass-ipq806x.c b/sound/soc/qcom/lpass-ipq806x.c
index 608c1a92af8a..ca1e1f2d2787 100644
--- a/sound/soc/qcom/lpass-ipq806x.c
+++ b/sound/soc/qcom/lpass-ipq806x.c
@@ -92,6 +92,12 @@ static struct lpass_variant ipq806x_data = {
 	.wrdma_channels		= 4,
 	.dai_driver		= &ipq806x_lpass_cpu_dai_driver,
 	.num_dai		= 1,
+	.dai_osr_clk_names	= (const char *[]) {
+				"mi2s-osr-clk",
+				},
+	.dai_bit_clk_names	= (const char *[]) {
+				"mi2s-bit-clk",
+				},
 	.alloc_dma_channel	= ipq806x_lpass_alloc_dma_channel,
 	.free_dma_channel	= ipq806x_lpass_free_dma_channel,
 };
diff --git a/sound/soc/qcom/lpass.h b/sound/soc/qcom/lpass.h
index 9b031352ea3c..b848db2d6c3d 100644
--- a/sound/soc/qcom/lpass.h
+++ b/sound/soc/qcom/lpass.h
@@ -91,6 +91,8 @@ struct lpass_variant {
 	/* SOC specific dais */
 	struct snd_soc_dai_driver *dai_driver;
 	int num_dai;
+	const char * const *dai_osr_clk_names;
+	const char * const *dai_bit_clk_names;
 };
 
 /* register the platform driver from the CPU DAI driver */
diff --git a/sound/soc/rockchip/rk3288_hdmi_analog.c b/sound/soc/rockchip/rk3288_hdmi_analog.c
index b60abf322ce1..dbc53e48c52c 100644
--- a/sound/soc/rockchip/rk3288_hdmi_analog.c
+++ b/sound/soc/rockchip/rk3288_hdmi_analog.c
@@ -93,6 +93,9 @@ static int rk_hw_params(struct snd_pcm_substream *substream,
 	case 96000:
 		mclk = 12288000;
 		break;
+	case 192000:
+		mclk = 24576000;
+		break;
 	case 11025:
 	case 22050:
 	case 44100:
diff --git a/sound/soc/samsung/Kconfig b/sound/soc/samsung/Kconfig
index f1f1d7959a1b..0520f5afd7cc 100644
--- a/sound/soc/samsung/Kconfig
+++ b/sound/soc/samsung/Kconfig
@@ -185,6 +185,14 @@ config SND_SOC_SNOW
 	  Say Y if you want to add audio support for various Snow
 	  boards based on Exynos5 series of SoCs.
 
+config SND_SOC_ODROID
+	tristate "Audio support for Odroid XU3/XU4"
+	depends on SND_SOC_SAMSUNG && I2C
+	select SND_SOC_MAX98090
+	select SND_SAMSUNG_I2S
+	help
+	  Say Y here to enable audio support for the Odroid XU3/XU4.
+
 config SND_SOC_ARNDALE_RT5631_ALC5631
         tristate "Audio support for RT5631(ALC5631) on Arndale Board"
         depends on I2C
diff --git a/sound/soc/samsung/Makefile b/sound/soc/samsung/Makefile
index b5df5e2e3d94..b6c2ee358333 100644
--- a/sound/soc/samsung/Makefile
+++ b/sound/soc/samsung/Makefile
@@ -40,6 +40,7 @@ snd-soc-tobermory-objs := tobermory.o
 snd-soc-lowland-objs := lowland.o
 snd-soc-littlemill-objs := littlemill.o
 snd-soc-bells-objs := bells.o
+snd-soc-odroid-objs := odroid.o
 snd-soc-arndale-rt5631-objs := arndale_rt5631.o
 snd-soc-tm2-wm5110-objs := tm2_wm5110.o
 
@@ -62,5 +63,6 @@ obj-$(CONFIG_SND_SOC_TOBERMORY) += snd-soc-tobermory.o
 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_ODROID) += snd-soc-odroid.o
 obj-$(CONFIG_SND_SOC_ARNDALE_RT5631_ALC5631) += snd-soc-arndale-rt5631.o
 obj-$(CONFIG_SND_SOC_SAMSUNG_TM2_WM5110) += snd-soc-tm2-wm5110.o
diff --git a/sound/soc/samsung/bells.c b/sound/soc/samsung/bells.c
index 3dd246fa0059..34deba461ae1 100644
--- a/sound/soc/samsung/bells.c
+++ b/sound/soc/samsung/bells.c
@@ -446,7 +446,6 @@ static struct snd_soc_card bells_cards[] = {
 	},
 };
 
-
 static int bells_probe(struct platform_device *pdev)
 {
 	int ret;
diff --git a/sound/soc/samsung/i2s-regs.h b/sound/soc/samsung/i2s-regs.h
index 9170c311d66e..fe6914005494 100644
--- a/sound/soc/samsung/i2s-regs.h
+++ b/sound/soc/samsung/i2s-regs.h
@@ -160,5 +160,3 @@
 #define I2SSIZE_SHIFT		(16)
 
 #endif /* __SND_SOC_SAMSUNG_I2S_REGS_H */
-
-
diff --git a/sound/soc/samsung/i2s.c b/sound/soc/samsung/i2s.c
index 52a47ed292a4..af3ba4d4ccc5 100644
--- a/sound/soc/samsung/i2s.c
+++ b/sound/soc/samsung/i2s.c
@@ -1242,7 +1242,6 @@ static int samsung_i2s_probe(struct platform_device *pdev)
 		i2s_dai_data = (struct samsung_i2s_dai_data *)
 				platform_get_device_id(pdev)->driver_data;
 
-
 	pri_dai = i2s_alloc_dai(pdev, false);
 	if (!pri_dai) {
 		dev_err(&pdev->dev, "Unable to alloc I2S_pri\n");
diff --git a/sound/soc/samsung/odroid.c b/sound/soc/samsung/odroid.c
new file mode 100644
index 000000000000..0c0b00e40646
--- /dev/null
+++ b/sound/soc/samsung/odroid.c
@@ -0,0 +1,219 @@
+/*
+ * Copyright (C) 2017 Samsung Electronics Co., Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/clk.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/module.h>
+#include <sound/soc.h>
+#include <sound/pcm_params.h>
+#include "i2s.h"
+#include "i2s-regs.h"
+
+struct odroid_priv {
+	struct snd_soc_card card;
+	struct snd_soc_dai_link dai_link;
+
+	struct clk *pll;
+	struct clk *rclk;
+};
+
+static int odroid_card_startup(struct snd_pcm_substream *substream)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+
+	snd_pcm_hw_constraint_single(runtime, SNDRV_PCM_HW_PARAM_CHANNELS, 2);
+	return 0;
+}
+
+static int odroid_card_hw_params(struct snd_pcm_substream *substream,
+				      struct snd_pcm_hw_params *params)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct odroid_priv *priv = snd_soc_card_get_drvdata(rtd->card);
+	unsigned int pll_freq, rclk_freq;
+	int ret;
+
+	switch (params_rate(params)) {
+	case 32000:
+	case 64000:
+		pll_freq = 131072000U;
+		break;
+	case 44100:
+	case 88200:
+	case 176400:
+		pll_freq = 180633600U;
+		break;
+	case 48000:
+	case 96000:
+	case 192000:
+		pll_freq = 196608000U;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	ret = clk_set_rate(priv->pll, pll_freq + 1);
+	if (ret < 0)
+		return ret;
+
+	rclk_freq = params_rate(params) * 256 * 4;
+
+	ret = clk_set_rate(priv->rclk, rclk_freq);
+	if (ret < 0)
+		return ret;
+
+	if (rtd->num_codecs > 1) {
+		struct snd_soc_dai *codec_dai = rtd->codec_dais[1];
+
+		ret = snd_soc_dai_set_sysclk(codec_dai, 0, rclk_freq,
+					     SND_SOC_CLOCK_IN);
+		if (ret < 0)
+			return ret;
+	}
+
+	return 0;
+}
+
+static const struct snd_soc_ops odroid_card_ops = {
+	.startup = odroid_card_startup,
+	.hw_params = odroid_card_hw_params,
+};
+
+static void odroid_put_codec_of_nodes(struct snd_soc_dai_link *link)
+{
+	struct snd_soc_dai_link_component *component = link->codecs;
+	int i;
+
+	for (i = 0; i < link->num_codecs; i++, component++) {
+		if (!component->of_node)
+			break;
+		of_node_put(component->of_node);
+	}
+}
+
+static int odroid_audio_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct device_node *cpu, *codec;
+	struct odroid_priv *priv;
+	struct snd_soc_dai_link *link;
+	struct snd_soc_card *card;
+	int ret;
+
+	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
+
+	card = &priv->card;
+	card->dev = dev;
+
+	card->owner = THIS_MODULE;
+	card->fully_routed = true;
+
+	snd_soc_card_set_drvdata(card, priv);
+
+	priv->pll = devm_clk_get(dev, "epll");
+	if (IS_ERR(priv->pll))
+		return PTR_ERR(priv->pll);
+
+	priv->rclk = devm_clk_get(dev, "i2s_rclk");
+	if (IS_ERR(priv->rclk))
+		return PTR_ERR(priv->rclk);
+
+	ret = snd_soc_of_parse_card_name(card, "model");
+	if (ret < 0)
+		return ret;
+
+	if (of_property_read_bool(dev->of_node, "samsung,audio-widgets")) {
+		ret = snd_soc_of_parse_audio_simple_widgets(card,
+						"samsung,audio-widgets");
+		if (ret < 0)
+			return ret;
+	}
+
+	if (of_property_read_bool(dev->of_node, "samsung,audio-routing")) {
+		ret = snd_soc_of_parse_audio_routing(card,
+						"samsung,audio-routing");
+		if (ret < 0)
+			return ret;
+	}
+
+	link = &priv->dai_link;
+
+	link->ops = &odroid_card_ops;
+	link->dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
+			SND_SOC_DAIFMT_CBS_CFS;
+
+	card->dai_link = &priv->dai_link;
+	card->num_links = 1;
+
+	cpu = of_get_child_by_name(dev->of_node, "cpu");
+	codec = of_get_child_by_name(dev->of_node, "codec");
+
+	link->cpu_of_node = of_parse_phandle(cpu, "sound-dai", 0);
+	if (!link->cpu_of_node) {
+		dev_err(dev, "Failed parsing cpu/sound-dai property\n");
+		return -EINVAL;
+	}
+
+	ret = snd_soc_of_get_dai_link_codecs(dev, codec, link);
+	if (ret < 0)
+		goto err_put_codec_n;
+
+	link->platform_of_node = link->cpu_of_node;
+
+	link->name = "Primary";
+	link->stream_name = link->name;
+
+	ret = devm_snd_soc_register_card(dev, card);
+	if (ret < 0) {
+		dev_err(dev, "snd_soc_register_card() failed: %d\n", ret);
+		goto err_put_i2s_n;
+	}
+
+	return 0;
+
+err_put_i2s_n:
+	of_node_put(link->cpu_of_node);
+err_put_codec_n:
+	odroid_put_codec_of_nodes(link);
+	return ret;
+}
+
+static int odroid_audio_remove(struct platform_device *pdev)
+{
+	struct odroid_priv *priv = platform_get_drvdata(pdev);
+
+	of_node_put(priv->dai_link.cpu_of_node);
+	odroid_put_codec_of_nodes(&priv->dai_link);
+
+	return 0;
+}
+
+static const struct of_device_id odroid_audio_of_match[] = {
+	{ .compatible	= "samsung,odroid-xu3-audio" },
+	{ .compatible	= "samsung,odroid-xu4-audio"},
+	{ },
+};
+MODULE_DEVICE_TABLE(of, odroid_audio_of_match);
+
+static struct platform_driver odroid_audio_driver = {
+	.driver = {
+		.name		= "odroid-audio",
+		.of_match_table	= odroid_audio_of_match,
+		.pm		= &snd_soc_pm_ops,
+	},
+	.probe	= odroid_audio_probe,
+	.remove	= odroid_audio_remove,
+};
+module_platform_driver(odroid_audio_driver);
+
+MODULE_AUTHOR("Sylwester Nawrocki <s.nawrocki@samsung.com>");
+MODULE_DESCRIPTION("Odroid XU3/XU4 audio support");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/samsung/s3c-i2s-v2.c b/sound/soc/samsung/s3c-i2s-v2.c
index 644f186fd35c..8f42deaa184b 100644
--- a/sound/soc/samsung/s3c-i2s-v2.c
+++ b/sound/soc/samsung/s3c-i2s-v2.c
@@ -72,7 +72,6 @@ static inline void dbg_showcon(const char *fn, u32 con)
 }
 #endif
 
-
 /* Turn on or off the transmission path. */
 static void s3c2412_snd_txctrl(struct s3c_i2sv2_info *i2s, int on)
 {
diff --git a/sound/soc/sh/rcar/adg.c b/sound/soc/sh/rcar/adg.c
index 85a33ac0a5c4..66203d107a11 100644
--- a/sound/soc/sh/rcar/adg.c
+++ b/sound/soc/sh/rcar/adg.c
@@ -43,6 +43,7 @@ struct rsnd_adg {
 };
 
 #define LRCLK_ASYNC	(1 << 0)
+#define AUDIO_OUT_48	(1 << 1)
 #define adg_mode_flags(adg)	(adg->flags)
 
 #define for_each_rsnd_clk(pos, adg, i)		\
@@ -364,7 +365,10 @@ found_clock:
 
 	rsnd_adg_set_ssi_clk(ssi_mod, data);
 
-	if (!(adg_mode_flags(adg) & LRCLK_ASYNC)) {
+	if (adg_mode_flags(adg) & LRCLK_ASYNC) {
+		if (adg_mode_flags(adg) & AUDIO_OUT_48)
+			ckr = 0x80000000;
+	} else {
 		if (0 == (rate % 8000))
 			ckr = 0x80000000;
 	}
@@ -427,11 +431,14 @@ static void rsnd_adg_get_clkout(struct rsnd_priv *priv,
 	struct clk *clk;
 	struct device *dev = rsnd_priv_to_dev(priv);
 	struct device_node *np = dev->of_node;
+	struct property *prop;
 	u32 ckr, rbgx, rbga, rbgb;
-	u32 rate, req_rate = 0, div;
+	u32 rate, div;
+#define REQ_SIZE 2
+	u32 req_rate[REQ_SIZE] = {};
 	uint32_t count = 0;
 	unsigned long req_48kHz_rate, req_441kHz_rate;
-	int i;
+	int i, req_size;
 	const char *parent_clk_name = NULL;
 	static const char * const clkout_name[] = {
 		[CLKOUT]  = "audio_clkout",
@@ -446,19 +453,32 @@ static void rsnd_adg_get_clkout(struct rsnd_priv *priv,
 		[CLKI] = 0x2,
 	};
 
-	of_property_read_u32(np, "#clock-cells", &count);
+	ckr = 0;
+	rbga = 2; /* default 1/6 */
+	rbgb = 2; /* default 1/6 */
 
 	/*
 	 * ADG supports BRRA/BRRB output only
 	 * this means all clkout0/1/2/3 will be same rate
 	 */
-	of_property_read_u32(np, "clock-frequency", &req_rate);
+	prop = of_find_property(np, "clock-frequency", NULL);
+	if (!prop)
+		goto rsnd_adg_get_clkout_end;
+
+	req_size = prop->length / sizeof(u32);
+
+	of_property_read_u32_array(np, "clock-frequency", req_rate, req_size);
 	req_48kHz_rate = 0;
 	req_441kHz_rate = 0;
-	if (0 == (req_rate % 44100))
-		req_441kHz_rate = req_rate;
-	if (0 == (req_rate % 48000))
-		req_48kHz_rate = req_rate;
+	for (i = 0; i < req_size; i++) {
+		if (0 == (req_rate[i] % 44100))
+			req_441kHz_rate = req_rate[i];
+		if (0 == (req_rate[i] % 48000))
+			req_48kHz_rate = req_rate[i];
+	}
+
+	if (req_rate[0] % 48000 == 0)
+		adg->flags = AUDIO_OUT_48;
 
 	/*
 	 * This driver is assuming that AUDIO_CLKA/AUDIO_CLKB/AUDIO_CLKC
@@ -469,9 +489,6 @@ static void rsnd_adg_get_clkout(struct rsnd_priv *priv,
 	 *	rsnd_adg_ssi_clk_try_start()
 	 *	rsnd_ssi_master_clk_start()
 	 */
-	ckr = 0;
-	rbga = 2; /* default 1/6 */
-	rbgb = 2; /* default 1/6 */
 	adg->rbga_rate_for_441khz	= 0;
 	adg->rbgb_rate_for_48khz	= 0;
 	for_each_rsnd_clk(clk, adg, i) {
@@ -505,10 +522,8 @@ static void rsnd_adg_get_clkout(struct rsnd_priv *priv,
 				rbgb = rbgx;
 				adg->rbgb_rate_for_48khz = rate / div;
 				ckr |= brg_table[i] << 16;
-				if (req_48kHz_rate) {
+				if (req_48kHz_rate)
 					parent_clk_name = __clk_get_name(clk);
-					ckr |= 0x80000000;
-				}
 			}
 		}
 	}
@@ -518,12 +533,13 @@ static void rsnd_adg_get_clkout(struct rsnd_priv *priv,
 	 * this means all clkout0/1/2/3 will be * same rate
 	 */
 
+	of_property_read_u32(np, "#clock-cells", &count);
 	/*
 	 * for clkout
 	 */
 	if (!count) {
 		clk = clk_register_fixed_rate(dev, clkout_name[CLKOUT],
-					      parent_clk_name, 0, req_rate);
+					      parent_clk_name, 0, req_rate[0]);
 		if (!IS_ERR(clk)) {
 			adg->clkout[CLKOUT] = clk;
 			of_clk_add_provider(np, of_clk_src_simple_get, clk);
@@ -536,19 +552,18 @@ static void rsnd_adg_get_clkout(struct rsnd_priv *priv,
 		for (i = 0; i < CLKOUTMAX; i++) {
 			clk = clk_register_fixed_rate(dev, clkout_name[i],
 						      parent_clk_name, 0,
-						      req_rate);
-			if (!IS_ERR(clk)) {
-				adg->onecell.clks	= adg->clkout;
-				adg->onecell.clk_num	= CLKOUTMAX;
-
+						      req_rate[0]);
+			adg->clkout[i] = ERR_PTR(-ENOENT);
+			if (!IS_ERR(clk))
 				adg->clkout[i] = clk;
-
-				of_clk_add_provider(np, of_clk_src_onecell_get,
-						    &adg->onecell);
-			}
 		}
+		adg->onecell.clks	= adg->clkout;
+		adg->onecell.clk_num	= CLKOUTMAX;
+		of_clk_add_provider(np, of_clk_src_onecell_get,
+				    &adg->onecell);
 	}
 
+rsnd_adg_get_clkout_end:
 	adg->ckr = ckr;
 	adg->rbga = rbga;
 	adg->rbgb = rbgb;
@@ -564,6 +579,7 @@ int rsnd_adg_probe(struct rsnd_priv *priv)
 	struct rsnd_adg *adg;
 	struct device *dev = rsnd_priv_to_dev(priv);
 	struct device_node *np = dev->of_node;
+	int ret;
 
 	adg = devm_kzalloc(dev, sizeof(*adg), GFP_KERNEL);
 	if (!adg) {
@@ -571,8 +587,10 @@ int rsnd_adg_probe(struct rsnd_priv *priv)
 		return -ENOMEM;
 	}
 
-	rsnd_mod_init(priv, &adg->mod, &adg_ops,
+	ret = rsnd_mod_init(priv, &adg->mod, &adg_ops,
 		      NULL, NULL, 0, 0);
+	if (ret)
+		return ret;
 
 	rsnd_adg_get_clkin(priv, adg);
 	rsnd_adg_get_clkout(priv, adg);
@@ -589,5 +607,10 @@ int rsnd_adg_probe(struct rsnd_priv *priv)
 
 void rsnd_adg_remove(struct rsnd_priv *priv)
 {
+	struct device *dev = rsnd_priv_to_dev(priv);
+	struct device_node *np = dev->of_node;
+
+	of_clk_del_provider(np);
+
 	rsnd_adg_clk_disable(priv);
 }
diff --git a/sound/soc/sh/rcar/core.c b/sound/soc/sh/rcar/core.c
index 47b370cb2d3b..1744015408c3 100644
--- a/sound/soc/sh/rcar/core.c
+++ b/sound/soc/sh/rcar/core.c
@@ -96,7 +96,7 @@
 #include <linux/pm_runtime.h>
 #include "rsnd.h"
 
-#define RSND_RATES SNDRV_PCM_RATE_8000_96000
+#define RSND_RATES SNDRV_PCM_RATE_8000_192000
 #define RSND_FMTS (SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S16_LE)
 
 static const struct of_device_id rsnd_of_match[] = {
@@ -110,7 +110,6 @@ MODULE_DEVICE_TABLE(of, rsnd_of_match);
 /*
  *	rsnd_mod functions
  */
-#ifdef DEBUG
 void rsnd_mod_make_sure(struct rsnd_mod *mod, enum rsnd_mod_type type)
 {
 	if (mod->type != type) {
@@ -121,7 +120,6 @@ void rsnd_mod_make_sure(struct rsnd_mod *mod, enum rsnd_mod_type type)
 			 rsnd_mod_name(mod), rsnd_mod_id(mod));
 	}
 }
-#endif
 
 char *rsnd_mod_name(struct rsnd_mod *mod)
 {
@@ -674,12 +672,10 @@ static int rsnd_soc_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
 	/* set clock inversion */
 	switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
 	case SND_SOC_DAIFMT_NB_IF:
-		rdai->bit_clk_inv =  rdai->bit_clk_inv;
 		rdai->frm_clk_inv = !rdai->frm_clk_inv;
 		break;
 	case SND_SOC_DAIFMT_IB_NF:
 		rdai->bit_clk_inv = !rdai->bit_clk_inv;
-		rdai->frm_clk_inv =  rdai->frm_clk_inv;
 		break;
 	case SND_SOC_DAIFMT_IB_IF:
 		rdai->bit_clk_inv = !rdai->bit_clk_inv;
@@ -1002,13 +998,30 @@ static int rsnd_kctrl_put(struct snd_kcontrol *kctrl,
 	return change;
 }
 
-static int __rsnd_kctrl_new(struct rsnd_mod *mod,
-			    struct rsnd_dai_stream *io,
-			    struct snd_soc_pcm_runtime *rtd,
-			    const unsigned char *name,
-			    struct rsnd_kctrl_cfg *cfg,
-			    void (*update)(struct rsnd_dai_stream *io,
-					   struct rsnd_mod *mod))
+struct rsnd_kctrl_cfg *rsnd_kctrl_init_m(struct rsnd_kctrl_cfg_m *cfg)
+{
+	cfg->cfg.val = cfg->val;
+
+	return &cfg->cfg;
+}
+
+struct rsnd_kctrl_cfg *rsnd_kctrl_init_s(struct rsnd_kctrl_cfg_s *cfg)
+{
+	cfg->cfg.val = &cfg->val;
+
+	return &cfg->cfg;
+}
+
+int rsnd_kctrl_new(struct rsnd_mod *mod,
+		   struct rsnd_dai_stream *io,
+		   struct snd_soc_pcm_runtime *rtd,
+		   const unsigned char *name,
+		   void (*update)(struct rsnd_dai_stream *io,
+				  struct rsnd_mod *mod),
+		   struct rsnd_kctrl_cfg *cfg,
+		   const char * const *texts,
+		   int size,
+		   u32 max)
 {
 	struct snd_card *card = rtd->card->snd_card;
 	struct snd_kcontrol *kctrl;
@@ -1023,6 +1036,9 @@ static int __rsnd_kctrl_new(struct rsnd_mod *mod,
 	};
 	int ret;
 
+	if (size > RSND_MAX_CHANNELS)
+		return -EINVAL;
+
 	kctrl = snd_ctl_new1(&knew, mod);
 	if (!kctrl)
 		return -ENOMEM;
@@ -1031,74 +1047,17 @@ static int __rsnd_kctrl_new(struct rsnd_mod *mod,
 	if (ret < 0)
 		return ret;
 
-	cfg->update = update;
-	cfg->card = card;
-	cfg->kctrl = kctrl;
-	cfg->io = io;
+	cfg->texts	= texts;
+	cfg->max	= max;
+	cfg->size	= size;
+	cfg->update	= update;
+	cfg->card	= card;
+	cfg->kctrl	= kctrl;
+	cfg->io		= io;
 
 	return 0;
 }
 
-void _rsnd_kctrl_remove(struct rsnd_kctrl_cfg *cfg)
-{
-	if (cfg->card && cfg->kctrl)
-		snd_ctl_remove(cfg->card, cfg->kctrl);
-
-	cfg->card = NULL;
-	cfg->kctrl = NULL;
-}
-
-int rsnd_kctrl_new_m(struct rsnd_mod *mod,
-		     struct rsnd_dai_stream *io,
-		     struct snd_soc_pcm_runtime *rtd,
-		     const unsigned char *name,
-		     void (*update)(struct rsnd_dai_stream *io,
-				    struct rsnd_mod *mod),
-		     struct rsnd_kctrl_cfg_m *_cfg,
-		     int ch_size,
-		     u32 max)
-{
-	if (ch_size > RSND_MAX_CHANNELS)
-		return -EINVAL;
-
-	_cfg->cfg.max	= max;
-	_cfg->cfg.size	= ch_size;
-	_cfg->cfg.val	= _cfg->val;
-	return __rsnd_kctrl_new(mod, io, rtd, name, &_cfg->cfg, update);
-}
-
-int rsnd_kctrl_new_s(struct rsnd_mod *mod,
-		     struct rsnd_dai_stream *io,
-		     struct snd_soc_pcm_runtime *rtd,
-		     const unsigned char *name,
-		     void (*update)(struct rsnd_dai_stream *io,
-				    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, io, rtd, name, &_cfg->cfg, update);
-}
-
-int rsnd_kctrl_new_e(struct rsnd_mod *mod,
-		     struct rsnd_dai_stream *io,
-		     struct snd_soc_pcm_runtime *rtd,
-		     const unsigned char *name,
-		     struct rsnd_kctrl_cfg_s *_cfg,
-		     void (*update)(struct rsnd_dai_stream *io,
-				    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, io, rtd, name, &_cfg->cfg, update);
-}
-
 /*
  *		snd_soc_platform
  */
diff --git a/sound/soc/sh/rcar/dvc.c b/sound/soc/sh/rcar/dvc.c
index cf8f59cdd8d7..463de8360985 100644
--- a/sound/soc/sh/rcar/dvc.c
+++ b/sound/soc/sh/rcar/dvc.c
@@ -218,21 +218,6 @@ static int rsnd_dvc_probe_(struct rsnd_mod *mod,
 	return rsnd_cmd_attach(io, rsnd_mod_id(mod));
 }
 
-static int rsnd_dvc_remove_(struct rsnd_mod *mod,
-			    struct rsnd_dai_stream *io,
-			    struct rsnd_priv *priv)
-{
-	struct rsnd_dvc *dvc = rsnd_mod_to_dvc(mod);
-
-	rsnd_kctrl_remove(dvc->volume);
-	rsnd_kctrl_remove(dvc->mute);
-	rsnd_kctrl_remove(dvc->ren);
-	rsnd_kctrl_remove(dvc->rup);
-	rsnd_kctrl_remove(dvc->rdown);
-
-	return 0;
-}
-
 static int rsnd_dvc_init(struct rsnd_mod *mod,
 			 struct rsnd_dai_stream *io,
 			 struct rsnd_priv *priv)
@@ -300,18 +285,18 @@ static int rsnd_dvc_pcm_new(struct rsnd_mod *mod,
 	ret = rsnd_kctrl_new_e(mod, io, rtd,
 			is_play ?
 			"DVC Out Ramp Up Rate" : "DVC In Ramp Up Rate",
-			&dvc->rup,
 			rsnd_dvc_volume_update,
-			dvc_ramp_rate, ARRAY_SIZE(dvc_ramp_rate));
+			&dvc->rup,
+			dvc_ramp_rate);
 	if (ret < 0)
 		return ret;
 
 	ret = rsnd_kctrl_new_e(mod, io, rtd,
 			is_play ?
 			"DVC Out Ramp Down Rate" : "DVC In Ramp Down Rate",
-			&dvc->rdown,
 			rsnd_dvc_volume_update,
-			dvc_ramp_rate, ARRAY_SIZE(dvc_ramp_rate));
+			&dvc->rdown,
+			dvc_ramp_rate);
 
 	if (ret < 0)
 		return ret;
@@ -332,7 +317,6 @@ static struct rsnd_mod_ops rsnd_dvc_ops = {
 	.name		= DVC_NAME,
 	.dma_req	= rsnd_dvc_dma_req,
 	.probe		= rsnd_dvc_probe_,
-	.remove		= rsnd_dvc_remove_,
 	.init		= rsnd_dvc_init,
 	.quit		= rsnd_dvc_quit,
 	.pcm_new	= rsnd_dvc_pcm_new,
diff --git a/sound/soc/sh/rcar/rsnd.h b/sound/soc/sh/rcar/rsnd.h
index 7410ec0174db..dbf4163427e8 100644
--- a/sound/soc/sh/rcar/rsnd.h
+++ b/sound/soc/sh/rcar/rsnd.h
@@ -611,35 +611,30 @@ struct rsnd_kctrl_cfg_s {
 	u32 val;
 };
 
-void _rsnd_kctrl_remove(struct rsnd_kctrl_cfg *cfg);
-#define rsnd_kctrl_remove(_cfg)	_rsnd_kctrl_remove(&((_cfg).cfg))
-
-int rsnd_kctrl_new_m(struct rsnd_mod *mod,
-		     struct rsnd_dai_stream *io,
-		     struct snd_soc_pcm_runtime *rtd,
-		     const unsigned char *name,
-		     void (*update)(struct rsnd_dai_stream *io,
-				    struct rsnd_mod *mod),
-		     struct rsnd_kctrl_cfg_m *_cfg,
-		     int ch_size,
-		     u32 max);
-int rsnd_kctrl_new_s(struct rsnd_mod *mod,
-		     struct rsnd_dai_stream *io,
-		     struct snd_soc_pcm_runtime *rtd,
-		     const unsigned char *name,
-		     void (*update)(struct rsnd_dai_stream *io,
-				    struct rsnd_mod *mod),
-		     struct rsnd_kctrl_cfg_s *_cfg,
-		     u32 max);
-int rsnd_kctrl_new_e(struct rsnd_mod *mod,
-		     struct rsnd_dai_stream *io,
-		     struct snd_soc_pcm_runtime *rtd,
-		     const unsigned char *name,
-		     struct rsnd_kctrl_cfg_s *_cfg,
-		     void (*update)(struct rsnd_dai_stream *io,
-				    struct rsnd_mod *mod),
-		     const char * const *texts,
-		     u32 max);
+struct rsnd_kctrl_cfg *rsnd_kctrl_init_m(struct rsnd_kctrl_cfg_m *cfg);
+struct rsnd_kctrl_cfg *rsnd_kctrl_init_s(struct rsnd_kctrl_cfg_s *cfg);
+int rsnd_kctrl_new(struct rsnd_mod *mod,
+		   struct rsnd_dai_stream *io,
+		   struct snd_soc_pcm_runtime *rtd,
+		   const unsigned char *name,
+		   void (*update)(struct rsnd_dai_stream *io,
+				  struct rsnd_mod *mod),
+		   struct rsnd_kctrl_cfg *cfg,
+		   const char * const *texts,
+		   int size,
+		   u32 max);
+
+#define rsnd_kctrl_new_m(mod, io, rtd, name, update, cfg, size, max) \
+	rsnd_kctrl_new(mod, io, rtd, name, update, rsnd_kctrl_init_m(cfg), \
+		       NULL, size, max)
+
+#define rsnd_kctrl_new_s(mod, io, rtd, name, update, cfg, max)	\
+	rsnd_kctrl_new(mod, io, rtd, name, update, rsnd_kctrl_init_s(cfg), \
+		       NULL, 1, max)
+
+#define rsnd_kctrl_new_e(mod, io, rtd, name, update, cfg, texts)	\
+	rsnd_kctrl_new(mod, io, rtd, name, update, rsnd_kctrl_init_s(cfg), \
+		       texts, 1, ARRAY_SIZE(texts))
 
 /*
  *	R-Car SSI
@@ -732,8 +727,8 @@ void rsnd_cmd_remove(struct rsnd_priv *priv);
 int rsnd_cmd_attach(struct rsnd_dai_stream *io, int id);
 struct rsnd_mod *rsnd_cmd_mod_get(struct rsnd_priv *priv, int id);
 
-#ifdef DEBUG
 void rsnd_mod_make_sure(struct rsnd_mod *mod, enum rsnd_mod_type type);
+#ifdef DEBUG
 #define rsnd_mod_confirm_ssi(mssi)	rsnd_mod_make_sure(mssi, RSND_MOD_SSI)
 #define rsnd_mod_confirm_src(msrc)	rsnd_mod_make_sure(msrc, RSND_MOD_SRC)
 #define rsnd_mod_confirm_dvc(mdvc)	rsnd_mod_make_sure(mdvc, RSND_MOD_DVC)
diff --git a/sound/soc/sh/rcar/src.c b/sound/soc/sh/rcar/src.c
index 42db48db09ba..20b5b2ec625e 100644
--- a/sound/soc/sh/rcar/src.c
+++ b/sound/soc/sh/rcar/src.c
@@ -167,6 +167,7 @@ static int rsnd_src_hw_params(struct rsnd_mod *mod,
 	 *	dpcm_fe_dai_hw_params()
 	 *	dpcm_be_dai_hw_params()
 	 */
+	src->convert_rate = 0;
 	if (fe->dai_link->dynamic) {
 		int stream = substream->stream;
 		struct snd_soc_dpcm *dpcm;
@@ -414,8 +415,6 @@ static int rsnd_src_quit(struct rsnd_mod *mod,
 
 	rsnd_mod_power_off(mod);
 
-	src->convert_rate = 0;
-
 	/* reset sync convert_rate */
 	src->sync.val = 0;
 
diff --git a/sound/soc/sh/rcar/ssi.c b/sound/soc/sh/rcar/ssi.c
index 411bda2387ad..135c5669f796 100644
--- a/sound/soc/sh/rcar/ssi.c
+++ b/sound/soc/sh/rcar/ssi.c
@@ -228,6 +228,15 @@ static int rsnd_ssi_master_clk_start(struct rsnd_mod *mod,
 	for (j = 0; j < ARRAY_SIZE(ssi_clk_mul_table); j++) {
 
 		/*
+		 * It will set SSIWSR.CONT here, but SSICR.CKDV = 000
+		 * with it is not allowed. (SSIWSR.WS_MODE with
+		 * SSICR.CKDV = 000 is not allowed either).
+		 * Skip it. See SSICR.CKDV
+		 */
+		if (j == 0)
+			continue;
+
+		/*
 		 * this driver is assuming that
 		 * system word is 32bit x chan
 		 * see rsnd_ssi_init()
diff --git a/sound/soc/sirf/sirf-audio-port.c b/sound/soc/sirf/sirf-audio-port.c
index 3f2cce03275c..be066de74aaa 100644
--- a/sound/soc/sirf/sirf-audio-port.c
+++ b/sound/soc/sirf/sirf-audio-port.c
@@ -19,6 +19,7 @@ struct sirf_audio_port {
 static int sirf_audio_port_dai_probe(struct snd_soc_dai *dai)
 {
 	struct sirf_audio_port *port = snd_soc_dai_get_drvdata(dai);
+
 	snd_soc_dai_init_dma_data(dai, &port->playback_dma_data,
 			&port->capture_dma_data);
 	return 0;
diff --git a/sound/soc/sirf/sirf-audio.c b/sound/soc/sirf/sirf-audio.c
index 94ea152e0362..f2bc50790f76 100644
--- a/sound/soc/sirf/sirf-audio.c
+++ b/sound/soc/sirf/sirf-audio.c
@@ -27,6 +27,7 @@ static int sirf_audio_hp_event(struct snd_soc_dapm_widget *w,
 	struct snd_soc_card *card = dapm->card;
 	struct sirf_audio_card *sirf_audio_card = snd_soc_card_get_drvdata(card);
 	int on = !SND_SOC_DAPM_EVENT_OFF(event);
+
 	if (gpio_is_valid(sirf_audio_card->gpio_hp_pa))
 		gpio_set_value(sirf_audio_card->gpio_hp_pa, on);
 	return 0;
diff --git a/sound/soc/sirf/sirf-usp.c b/sound/soc/sirf/sirf-usp.c
index 45fc06c0e0e5..77e7dcf969d0 100644
--- a/sound/soc/sirf/sirf-usp.c
+++ b/sound/soc/sirf/sirf-usp.c
@@ -71,6 +71,7 @@ static void sirf_usp_rx_disable(struct sirf_usp *usp)
 static int sirf_usp_pcm_dai_probe(struct snd_soc_dai *dai)
 {
 	struct sirf_usp *usp = snd_soc_dai_get_drvdata(dai);
+
 	snd_soc_dai_init_dma_data(dai, &usp->playback_dma_data,
 			&usp->capture_dma_data);
 	return 0;
@@ -294,6 +295,7 @@ static struct snd_soc_dai_driver sirf_usp_pcm_dai = {
 static int sirf_usp_pcm_runtime_suspend(struct device *dev)
 {
 	struct sirf_usp *usp = dev_get_drvdata(dev);
+
 	clk_disable_unprepare(usp->clk);
 	return 0;
 }
@@ -302,6 +304,7 @@ static int sirf_usp_pcm_runtime_resume(struct device *dev)
 {
 	struct sirf_usp *usp = dev_get_drvdata(dev);
 	int ret;
+
 	ret = clk_prepare_enable(usp->clk);
 	if (ret) {
 		dev_err(dev, "clk_enable failed: %d\n", ret);
diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c
index 2722bb0c5573..525f2f397b4c 100644
--- a/sound/soc/soc-core.c
+++ b/sound/soc/soc-core.c
@@ -1776,7 +1776,6 @@ static int soc_bind_aux_dev(struct snd_soc_card *card, int num)
 	}
 
 	component->init = aux_dev->init;
-	component->auxiliary = 1;
 	list_add(&component->card_aux_list, &card->aux_comp_list);
 
 	return 0;
@@ -1788,14 +1787,13 @@ err_defer:
 
 static int soc_probe_aux_devices(struct snd_soc_card *card)
 {
-	struct snd_soc_component *comp, *tmp;
+	struct snd_soc_component *comp;
 	int order;
 	int ret;
 
 	for (order = SND_SOC_COMP_ORDER_FIRST; order <= SND_SOC_COMP_ORDER_LAST;
 		order++) {
-		list_for_each_entry_safe(comp, tmp, &card->aux_comp_list,
-					 card_aux_list) {
+		list_for_each_entry(comp, &card->aux_comp_list, card_aux_list) {
 			if (comp->driver->probe_order == order) {
 				ret = soc_probe_component(card,	comp);
 				if (ret < 0) {
@@ -1804,7 +1802,6 @@ static int soc_probe_aux_devices(struct snd_soc_card *card)
 						comp->name, ret);
 					return ret;
 				}
-				list_del(&comp->card_aux_list);
 			}
 		}
 	}
@@ -1820,14 +1817,12 @@ static void soc_remove_aux_devices(struct snd_soc_card *card)
 	for (order = SND_SOC_COMP_ORDER_FIRST; order <= SND_SOC_COMP_ORDER_LAST;
 		order++) {
 		list_for_each_entry_safe(comp, _comp,
-			&card->component_dev_list, card_list) {
-
-			if (!comp->auxiliary)
-				continue;
+			&card->aux_comp_list, card_aux_list) {
 
 			if (comp->driver->remove_order == order) {
 				soc_remove_component(comp);
-				comp->auxiliary = 0;
+				/* remove it from the card's aux_comp_list */
+				list_del(&comp->card_aux_list);
 			}
 		}
 	}
@@ -1918,6 +1913,7 @@ int snd_soc_runtime_set_dai_fmt(struct snd_soc_pcm_runtime *rtd,
 EXPORT_SYMBOL_GPL(snd_soc_runtime_set_dai_fmt);
 
 
+#ifdef CONFIG_DMI
 /* Trim special characters, and replace '-' with '_' since '-' is used to
  * separate different DMI fields in the card long name. Only number and
  * alphabet characters and a few separator characters are kept.
@@ -2049,6 +2045,7 @@ int snd_soc_set_dmi_name(struct snd_soc_card *card, const char *flavour)
 	return 0;
 }
 EXPORT_SYMBOL_GPL(snd_soc_set_dmi_name);
+#endif /* CONFIG_DMI */
 
 static int snd_soc_instantiate_card(struct snd_soc_card *card)
 {
@@ -2190,6 +2187,9 @@ static int snd_soc_instantiate_card(struct snd_soc_card *card)
 		snd_soc_dapm_add_routes(&card->dapm, card->of_dapm_routes,
 					card->num_of_dapm_routes);
 
+	/* try to set some sane longname if DMI is available */
+	snd_soc_set_dmi_name(card, NULL);
+
 	snprintf(card->snd_card->shortname, sizeof(card->snd_card->shortname),
 		 "%s", card->name);
 	snprintf(card->snd_card->longname, sizeof(card->snd_card->longname),
@@ -3139,7 +3139,7 @@ static int snd_soc_component_initialize(struct snd_soc_component *component,
 	component->suspend = component->driver->suspend;
 	component->resume = component->driver->resume;
 	component->pcm_new = component->driver->pcm_new;
-	component->pcm_free= component->driver->pcm_free;
+	component->pcm_free = component->driver->pcm_free;
 
 	dapm = &component->dapm;
 	dapm->dev = dev;
@@ -3240,6 +3240,11 @@ static void snd_soc_component_cleanup(struct snd_soc_component *component)
 
 static void snd_soc_component_del_unlocked(struct snd_soc_component *component)
 {
+	struct snd_soc_card *card = component->card;
+
+	if (card)
+		snd_soc_unregister_card(card);
+
 	list_del(&component->list);
 }
 
diff --git a/sound/soc/soc-jack.c b/sound/soc/soc-jack.c
index fbaa1bb41102..7daf21fee355 100644
--- a/sound/soc/soc-jack.c
+++ b/sound/soc/soc-jack.c
@@ -19,9 +19,28 @@
 #include <linux/workqueue.h>
 #include <linux/delay.h>
 #include <linux/export.h>
+#include <linux/suspend.h>
 #include <trace/events/asoc.h>
 
 /**
+ * snd_soc_codec_set_jack - configure codec jack.
+ * @codec: CODEC
+ * @jack: structure to use for the jack
+ * @data: can be used if codec driver need extra data for configuring jack
+ *
+ * Configures and enables jack detection function.
+ */
+int snd_soc_codec_set_jack(struct snd_soc_codec *codec,
+	struct snd_soc_jack *jack, void *data)
+{
+	if (codec->driver->set_jack)
+		return codec->driver->set_jack(codec, jack, data);
+	else
+		return -EINVAL;
+}
+EXPORT_SYMBOL_GPL(snd_soc_codec_set_jack);
+
+/**
  * snd_soc_card_jack_new - Create a new jack
  * @card:  ASoC card
  * @id:    an identifying string for this jack
@@ -293,6 +312,27 @@ static void gpio_work(struct work_struct *work)
 	snd_soc_jack_gpio_detect(gpio);
 }
 
+static int snd_soc_jack_pm_notifier(struct notifier_block *nb,
+				    unsigned long action, void *data)
+{
+	struct snd_soc_jack_gpio *gpio =
+			container_of(nb, struct snd_soc_jack_gpio, pm_notifier);
+
+	switch (action) {
+	case PM_POST_SUSPEND:
+	case PM_POST_HIBERNATION:
+	case PM_POST_RESTORE:
+		/*
+		 * Use workqueue so we do not have to care about running
+		 * concurrently with work triggered by the interrupt handler.
+		 */
+		queue_delayed_work(system_power_efficient_wq, &gpio->work, 0);
+		break;
+	}
+
+	return NOTIFY_DONE;
+}
+
 /**
  * snd_soc_jack_add_gpios - Associate GPIO pins with an ASoC jack
  *
@@ -369,6 +409,13 @@ got_gpio:
 					i, ret);
 		}
 
+		/*
+		 * Register PM notifier so we do not miss state transitions
+		 * happening while system is asleep.
+		 */
+		gpios[i].pm_notifier.notifier_call = snd_soc_jack_pm_notifier;
+		register_pm_notifier(&gpios[i].pm_notifier);
+
 		/* Expose GPIO value over sysfs for diagnostic purposes */
 		gpiod_export(gpios[i].desc, false);
 
@@ -428,6 +475,7 @@ void snd_soc_jack_free_gpios(struct snd_soc_jack *jack, int count,
 
 	for (i = 0; i < count; i++) {
 		gpiod_unexport(gpios[i].desc);
+		unregister_pm_notifier(&gpios[i].pm_notifier);
 		free_irq(gpiod_to_irq(gpios[i].desc), &gpios[i]);
 		cancel_delayed_work_sync(&gpios[i].work);
 		gpiod_put(gpios[i].desc);
diff --git a/sound/soc/soc-topology.c b/sound/soc/soc-topology.c
index 058bc99c6c34..002772e3ba2c 100644
--- a/sound/soc/soc-topology.c
+++ b/sound/soc/soc-topology.c
@@ -495,12 +495,13 @@ static void remove_widget(struct snd_soc_component *comp,
 			struct snd_kcontrol *kcontrol = w->kcontrols[i];
 			struct soc_enum *se =
 				(struct soc_enum *)kcontrol->private_value;
+			int j;
 
 			snd_ctl_remove(card, kcontrol);
 
 			kfree(se->dobj.control.dvalues);
-			for (i = 0; i < se->items; i++)
-				kfree(se->dobj.control.dtexts[i]);
+			for (j = 0; j < se->items; j++)
+				kfree(se->dobj.control.dtexts[j]);
 
 			kfree(se);
 		}
diff --git a/sound/soc/sti/uniperif_player.c b/sound/soc/sti/uniperif_player.c
index d7e8dd46d2cc..d8b6936e544e 100644
--- a/sound/soc/sti/uniperif_player.c
+++ b/sound/soc/sti/uniperif_player.c
@@ -1074,7 +1074,7 @@ int uni_player_init(struct platform_device *pdev,
 	player->clk = of_clk_get(pdev->dev.of_node, 0);
 	if (IS_ERR(player->clk)) {
 		dev_err(player->dev, "Failed to get clock\n");
-		ret = PTR_ERR(player->clk);
+		return PTR_ERR(player->clk);
 	}
 
 	/* Select the frequency synthesizer clock */
diff --git a/sound/soc/stm/Kconfig b/sound/soc/stm/Kconfig
new file mode 100644
index 000000000000..972970f0890a
--- /dev/null
+++ b/sound/soc/stm/Kconfig
@@ -0,0 +1,8 @@
+menuconfig SND_SOC_STM32
+	tristate "STMicroelectronics STM32 SOC audio support"
+	depends on ARCH_STM32 || COMPILE_TEST
+	depends on SND_SOC
+	select SND_SOC_GENERIC_DMAENGINE_PCM
+	select REGMAP_MMIO
+	help
+	  Say Y if you want to enable ASoC-support for STM32
diff --git a/sound/soc/stm/Makefile b/sound/soc/stm/Makefile
new file mode 100644
index 000000000000..e466a4759698
--- /dev/null
+++ b/sound/soc/stm/Makefile
@@ -0,0 +1,6 @@
+# SAI
+snd-soc-stm32-sai-sub-objs := stm32_sai_sub.o
+obj-$(CONFIG_SND_SOC_STM32) += snd-soc-stm32-sai-sub.o
+
+snd-soc-stm32-sai-objs := stm32_sai.o
+obj-$(CONFIG_SND_SOC_STM32) += snd-soc-stm32-sai.o
diff --git a/sound/soc/stm/stm32_sai.c b/sound/soc/stm/stm32_sai.c
new file mode 100644
index 000000000000..2a27a26bf7a1
--- /dev/null
+++ b/sound/soc/stm/stm32_sai.c
@@ -0,0 +1,115 @@
+/*
+ * STM32 ALSA SoC Digital Audio Interface (SAI) driver.
+ *
+ * Copyright (C) 2016, STMicroelectronics - All Rights Reserved
+ * Author(s): Olivier Moysan <olivier.moysan@st.com> for STMicroelectronics.
+ *
+ * License terms: GPL V2.0.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ */
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/module.h>
+#include <linux/of_platform.h>
+#include <linux/reset.h>
+
+#include <sound/dmaengine_pcm.h>
+#include <sound/core.h>
+
+#include "stm32_sai.h"
+
+static const struct of_device_id stm32_sai_ids[] = {
+	{ .compatible = "st,stm32f4-sai", .data = (void *)SAI_STM32F4 },
+	{}
+};
+
+static int stm32_sai_probe(struct platform_device *pdev)
+{
+	struct device_node *np = pdev->dev.of_node;
+	struct stm32_sai_data *sai;
+	struct reset_control *rst;
+	struct resource *res;
+	void __iomem *base;
+	const struct of_device_id *of_id;
+
+	sai = devm_kzalloc(&pdev->dev, sizeof(*sai), GFP_KERNEL);
+	if (!sai)
+		return -ENOMEM;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	base = devm_ioremap_resource(&pdev->dev, res);
+	if (IS_ERR(base))
+		return PTR_ERR(base);
+
+	of_id = of_match_device(stm32_sai_ids, &pdev->dev);
+	if (of_id)
+		sai->version = (enum stm32_sai_version)of_id->data;
+	else
+		return -EINVAL;
+
+	sai->clk_x8k = devm_clk_get(&pdev->dev, "x8k");
+	if (IS_ERR(sai->clk_x8k)) {
+		dev_err(&pdev->dev, "missing x8k parent clock\n");
+		return PTR_ERR(sai->clk_x8k);
+	}
+
+	sai->clk_x11k = devm_clk_get(&pdev->dev, "x11k");
+	if (IS_ERR(sai->clk_x11k)) {
+		dev_err(&pdev->dev, "missing x11k parent clock\n");
+		return PTR_ERR(sai->clk_x11k);
+	}
+
+	/* init irqs */
+	sai->irq = platform_get_irq(pdev, 0);
+	if (sai->irq < 0) {
+		dev_err(&pdev->dev, "no irq for node %s\n", pdev->name);
+		return sai->irq;
+	}
+
+	/* reset */
+	rst = reset_control_get(&pdev->dev, NULL);
+	if (!IS_ERR(rst)) {
+		reset_control_assert(rst);
+		udelay(2);
+		reset_control_deassert(rst);
+	}
+
+	sai->pdev = pdev;
+	platform_set_drvdata(pdev, sai);
+
+	return of_platform_populate(np, NULL, NULL, &pdev->dev);
+}
+
+static int stm32_sai_remove(struct platform_device *pdev)
+{
+	of_platform_depopulate(&pdev->dev);
+
+	return 0;
+}
+
+MODULE_DEVICE_TABLE(of, stm32_sai_ids);
+
+static struct platform_driver stm32_sai_driver = {
+	.driver = {
+		.name = "st,stm32-sai",
+		.of_match_table = stm32_sai_ids,
+	},
+	.probe = stm32_sai_probe,
+	.remove = stm32_sai_remove,
+};
+
+module_platform_driver(stm32_sai_driver);
+
+MODULE_DESCRIPTION("STM32 Soc SAI Interface");
+MODULE_AUTHOR("Olivier Moysan, <olivier.moysan@st.com>");
+MODULE_ALIAS("platform:st,stm32-sai");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/stm/stm32_sai.h b/sound/soc/stm/stm32_sai.h
new file mode 100644
index 000000000000..a801fda5066f
--- /dev/null
+++ b/sound/soc/stm/stm32_sai.h
@@ -0,0 +1,200 @@
+/*
+ * STM32 ALSA SoC Digital Audio Interface (SAI) driver.
+ *
+ * Copyright (C) 2016, STMicroelectronics - All Rights Reserved
+ * Author(s): Olivier Moysan <olivier.moysan@st.com> for STMicroelectronics.
+ *
+ * License terms: GPL V2.0.
+ *
+ * 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.
+ */
+
+/******************** SAI Register Map **************************************/
+
+/* common register */
+#define STM_SAI_GCR		0x00
+
+/* Sub-block A&B registers offsets, relative to A&B sub-block addresses */
+#define STM_SAI_CR1_REGX	0x00	/* A offset: 0x04. B offset: 0x24 */
+#define STM_SAI_CR2_REGX	0x04
+#define STM_SAI_FRCR_REGX	0x08
+#define STM_SAI_SLOTR_REGX	0x0C
+#define STM_SAI_IMR_REGX	0x10
+#define STM_SAI_SR_REGX		0x14
+#define STM_SAI_CLRFR_REGX	0x18
+#define STM_SAI_DR_REGX		0x1C
+
+/******************** Bit definition for SAI_GCR register *******************/
+#define SAI_GCR_SYNCIN_SHIFT	0
+#define SAI_GCR_SYNCIN_MASK	GENMASK(1, SAI_GCR_SYNCIN_SHIFT)
+#define SAI_GCR_SYNCIN_SET(x)	((x) << SAI_GCR_SYNCIN_SHIFT)
+
+#define SAI_GCR_SYNCOUT_SHIFT	4
+#define SAI_GCR_SYNCOUT_MASK	GENMASK(5, SAI_GCR_SYNCOUT_SHIFT)
+#define SAI_GCR_SYNCOUT_SET(x)	((x) << SAI_GCR_SYNCOUT_SHIFT)
+
+/******************* Bit definition for SAI_XCR1 register *******************/
+#define SAI_XCR1_RX_TX_SHIFT	0
+#define SAI_XCR1_RX_TX		BIT(SAI_XCR1_RX_TX_SHIFT)
+#define SAI_XCR1_SLAVE_SHIFT	1
+#define SAI_XCR1_SLAVE		BIT(SAI_XCR1_SLAVE_SHIFT)
+
+#define SAI_XCR1_PRTCFG_SHIFT	2
+#define SAI_XCR1_PRTCFG_MASK	GENMASK(3, SAI_XCR1_PRTCFG_SHIFT)
+#define SAI_XCR1_PRTCFG_SET(x)	((x) << SAI_XCR1_PRTCFG_SHIFT)
+
+#define SAI_XCR1_DS_SHIFT	5
+#define SAI_XCR1_DS_MASK	GENMASK(7, SAI_XCR1_DS_SHIFT)
+#define SAI_XCR1_DS_SET(x)	((x) << SAI_XCR1_DS_SHIFT)
+
+#define SAI_XCR1_LSBFIRST_SHIFT	8
+#define SAI_XCR1_LSBFIRST	BIT(SAI_XCR1_LSBFIRST_SHIFT)
+#define SAI_XCR1_CKSTR_SHIFT	9
+#define SAI_XCR1_CKSTR		BIT(SAI_XCR1_CKSTR_SHIFT)
+
+#define SAI_XCR1_SYNCEN_SHIFT	10
+#define SAI_XCR1_SYNCEN_MASK	GENMASK(11, SAI_XCR1_SYNCEN_SHIFT)
+#define SAI_XCR1_SYNCEN_SET(x)	((x) << SAI_XCR1_SYNCEN_SHIFT)
+
+#define SAI_XCR1_MONO_SHIFT	12
+#define SAI_XCR1_MONO		BIT(SAI_XCR1_MONO_SHIFT)
+#define SAI_XCR1_OUTDRIV_SHIFT	13
+#define SAI_XCR1_OUTDRIV	BIT(SAI_XCR1_OUTDRIV_SHIFT)
+#define SAI_XCR1_SAIEN_SHIFT	16
+#define SAI_XCR1_SAIEN		BIT(SAI_XCR1_SAIEN_SHIFT)
+#define SAI_XCR1_DMAEN_SHIFT	17
+#define SAI_XCR1_DMAEN		BIT(SAI_XCR1_DMAEN_SHIFT)
+#define SAI_XCR1_NODIV_SHIFT	19
+#define SAI_XCR1_NODIV		BIT(SAI_XCR1_NODIV_SHIFT)
+
+#define SAI_XCR1_MCKDIV_SHIFT	20
+#define SAI_XCR1_MCKDIV_WIDTH	4
+#define SAI_XCR1_MCKDIV_MASK	GENMASK(24, SAI_XCR1_MCKDIV_SHIFT)
+#define SAI_XCR1_MCKDIV_SET(x)	((x) << SAI_XCR1_MCKDIV_SHIFT)
+#define SAI_XCR1_MCKDIV_MAX	((1 << SAI_XCR1_MCKDIV_WIDTH) - 1)
+
+#define SAI_XCR1_OSR_SHIFT	26
+#define SAI_XCR1_OSR		BIT(SAI_XCR1_OSR_SHIFT)
+
+/******************* Bit definition for SAI_XCR2 register *******************/
+#define SAI_XCR2_FTH_SHIFT	0
+#define SAI_XCR2_FTH_MASK	GENMASK(2, SAI_XCR2_FTH_SHIFT)
+#define SAI_XCR2_FTH_SET(x)	((x) << SAI_XCR2_FTH_SHIFT)
+
+#define SAI_XCR2_FFLUSH_SHIFT	3
+#define SAI_XCR2_FFLUSH		BIT(SAI_XCR2_FFLUSH_SHIFT)
+#define SAI_XCR2_TRIS_SHIFT	4
+#define SAI_XCR2_TRIS		BIT(SAI_XCR2_TRIS_SHIFT)
+#define SAI_XCR2_MUTE_SHIFT	5
+#define SAI_XCR2_MUTE		BIT(SAI_XCR2_MUTE_SHIFT)
+#define SAI_XCR2_MUTEVAL_SHIFT	6
+#define SAI_XCR2_MUTEVAL	BIT(SAI_XCR2_MUTEVAL_SHIFT)
+
+#define SAI_XCR2_MUTECNT_SHIFT	7
+#define SAI_XCR2_MUTECNT_MASK	GENMASK(12, SAI_XCR2_MUTECNT_SHIFT)
+#define SAI_XCR2_MUTECNT_SET(x)	((x) << SAI_XCR2_MUTECNT_SHIFT)
+
+#define SAI_XCR2_CPL_SHIFT	13
+#define SAI_XCR2_CPL		BIT(SAI_XCR2_CPL_SHIFT)
+
+#define SAI_XCR2_COMP_SHIFT	14
+#define SAI_XCR2_COMP_MASK	GENMASK(15, SAI_XCR2_COMP_SHIFT)
+#define SAI_XCR2_COMP_SET(x)	((x) << SAI_XCR2_COMP_SHIFT)
+
+/****************** Bit definition for SAI_XFRCR register *******************/
+#define SAI_XFRCR_FRL_SHIFT	0
+#define SAI_XFRCR_FRL_MASK	GENMASK(7, SAI_XFRCR_FRL_SHIFT)
+#define SAI_XFRCR_FRL_SET(x)	((x) << SAI_XFRCR_FRL_SHIFT)
+
+#define SAI_XFRCR_FSALL_SHIFT	8
+#define SAI_XFRCR_FSALL_MASK	GENMASK(14, SAI_XFRCR_FSALL_SHIFT)
+#define SAI_XFRCR_FSALL_SET(x)	((x) << SAI_XFRCR_FSALL_SHIFT)
+
+#define SAI_XFRCR_FSDEF_SHIFT	16
+#define SAI_XFRCR_FSDEF		BIT(SAI_XFRCR_FSDEF_SHIFT)
+#define SAI_XFRCR_FSPOL_SHIFT	17
+#define SAI_XFRCR_FSPOL		BIT(SAI_XFRCR_FSPOL_SHIFT)
+#define SAI_XFRCR_FSOFF_SHIFT	18
+#define SAI_XFRCR_FSOFF		BIT(SAI_XFRCR_FSOFF_SHIFT)
+
+/****************** Bit definition for SAI_XSLOTR register ******************/
+
+#define SAI_XSLOTR_FBOFF_SHIFT	0
+#define SAI_XSLOTR_FBOFF_MASK	GENMASK(4, SAI_XSLOTR_FBOFF_SHIFT)
+#define SAI_XSLOTR_FBOFF_SET(x)	((x) << SAI_XSLOTR_FBOFF_SHIFT)
+
+#define SAI_XSLOTR_SLOTSZ_SHIFT	6
+#define SAI_XSLOTR_SLOTSZ_MASK	GENMASK(7, SAI_XSLOTR_SLOTSZ_SHIFT)
+#define SAI_XSLOTR_SLOTSZ_SET(x)	((x) << SAI_XSLOTR_SLOTSZ_SHIFT)
+
+#define SAI_XSLOTR_NBSLOT_SHIFT 8
+#define SAI_XSLOTR_NBSLOT_MASK	GENMASK(11, SAI_XSLOTR_NBSLOT_SHIFT)
+#define SAI_XSLOTR_NBSLOT_SET(x) ((x) << SAI_XSLOTR_NBSLOT_SHIFT)
+
+#define SAI_XSLOTR_SLOTEN_SHIFT	16
+#define SAI_XSLOTR_SLOTEN_WIDTH	16
+#define SAI_XSLOTR_SLOTEN_MASK	GENMASK(31, SAI_XSLOTR_SLOTEN_SHIFT)
+#define SAI_XSLOTR_SLOTEN_SET(x) ((x) << SAI_XSLOTR_SLOTEN_SHIFT)
+
+/******************* Bit definition for SAI_XIMR register *******************/
+#define SAI_XIMR_OVRUDRIE	BIT(0)
+#define SAI_XIMR_MUTEDETIE	BIT(1)
+#define SAI_XIMR_WCKCFGIE	BIT(2)
+#define SAI_XIMR_FREQIE		BIT(3)
+#define SAI_XIMR_CNRDYIE	BIT(4)
+#define SAI_XIMR_AFSDETIE	BIT(5)
+#define SAI_XIMR_LFSDETIE	BIT(6)
+
+#define SAI_XIMR_SHIFT	0
+#define SAI_XIMR_MASK		GENMASK(6, SAI_XIMR_SHIFT)
+
+/******************** Bit definition for SAI_XSR register *******************/
+#define SAI_XSR_OVRUDR		BIT(0)
+#define SAI_XSR_MUTEDET		BIT(1)
+#define SAI_XSR_WCKCFG		BIT(2)
+#define SAI_XSR_FREQ		BIT(3)
+#define SAI_XSR_CNRDY		BIT(4)
+#define SAI_XSR_AFSDET		BIT(5)
+#define SAI_XSR_LFSDET		BIT(6)
+
+#define SAI_XSR_SHIFT	0
+#define SAI_XSR_MASK		GENMASK(6, SAI_XSR_SHIFT)
+
+/****************** Bit definition for SAI_XCLRFR register ******************/
+#define SAI_XCLRFR_COVRUDR	BIT(0)
+#define SAI_XCLRFR_CMUTEDET	BIT(1)
+#define SAI_XCLRFR_CWCKCFG	BIT(2)
+#define SAI_XCLRFR_CFREQ	BIT(3)
+#define SAI_XCLRFR_CCNRDY	BIT(4)
+#define SAI_XCLRFR_CAFSDET	BIT(5)
+#define SAI_XCLRFR_CLFSDET	BIT(6)
+
+#define SAI_XCLRFR_SHIFT	0
+#define SAI_XCLRFR_MASK		GENMASK(6, SAI_XCLRFR_SHIFT)
+
+enum stm32_sai_version {
+	SAI_STM32F4
+};
+
+/**
+ * struct stm32_sai_data - private data of SAI instance driver
+ * @pdev: device data pointer
+ * @clk_x8k: SAI parent clock for sampling frequencies multiple of 8kHz
+ * @clk_x11k: SAI parent clock for sampling frequencies multiple of 11kHz
+ * @version: SOC version
+ * @irq: SAI interrupt line
+ */
+struct stm32_sai_data {
+	struct platform_device *pdev;
+	struct clk *clk_x8k;
+	struct clk *clk_x11k;
+	int version;
+	int irq;
+};
diff --git a/sound/soc/stm/stm32_sai_sub.c b/sound/soc/stm/stm32_sai_sub.c
new file mode 100644
index 000000000000..ae4706ca265b
--- /dev/null
+++ b/sound/soc/stm/stm32_sai_sub.c
@@ -0,0 +1,884 @@
+/*
+ * STM32 ALSA SoC Digital Audio Interface (SAI) driver.
+ *
+ * Copyright (C) 2016, STMicroelectronics - All Rights Reserved
+ * Author(s): Olivier Moysan <olivier.moysan@st.com> for STMicroelectronics.
+ *
+ * License terms: GPL V2.0.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ */
+
+#include <linux/clk.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of_irq.h>
+#include <linux/of_platform.h>
+#include <linux/regmap.h>
+
+#include <sound/core.h>
+#include <sound/dmaengine_pcm.h>
+#include <sound/pcm_params.h>
+
+#include "stm32_sai.h"
+
+#define SAI_FREE_PROTOCOL	0x0
+
+#define SAI_SLOT_SIZE_AUTO	0x0
+#define SAI_SLOT_SIZE_16	0x1
+#define SAI_SLOT_SIZE_32	0x2
+
+#define SAI_DATASIZE_8		0x2
+#define SAI_DATASIZE_10		0x3
+#define SAI_DATASIZE_16		0x4
+#define SAI_DATASIZE_20		0x5
+#define SAI_DATASIZE_24		0x6
+#define SAI_DATASIZE_32		0x7
+
+#define STM_SAI_FIFO_SIZE	8
+#define STM_SAI_DAI_NAME_SIZE	15
+
+#define STM_SAI_IS_PLAYBACK(ip)	((ip)->dir == SNDRV_PCM_STREAM_PLAYBACK)
+#define STM_SAI_IS_CAPTURE(ip)	((ip)->dir == SNDRV_PCM_STREAM_CAPTURE)
+
+#define STM_SAI_A_ID		0x0
+#define STM_SAI_B_ID		0x1
+
+#define STM_SAI_BLOCK_NAME(x)	(((x)->id == STM_SAI_A_ID) ? "A" : "B")
+
+/**
+ * struct stm32_sai_sub_data - private data of SAI sub block (block A or B)
+ * @pdev: device data pointer
+ * @regmap: SAI register map pointer
+ * @dma_params: dma configuration data for rx or tx channel
+ * @cpu_dai_drv: DAI driver data pointer
+ * @cpu_dai: DAI runtime data pointer
+ * @substream: PCM substream data pointer
+ * @pdata: SAI block parent data pointer
+ * @sai_ck: kernel clock feeding the SAI clock generator
+ * @phys_addr: SAI registers physical base address
+ * @mclk_rate: SAI block master clock frequency (Hz). set at init
+ * @id: SAI sub block id corresponding to sub-block A or B
+ * @dir: SAI block direction (playback or capture). set at init
+ * @master: SAI block mode flag. (true=master, false=slave) set at init
+ * @fmt: SAI block format. relevant only for custom protocols. set at init
+ * @sync: SAI block synchronization mode. (none, internal or external)
+ * @fs_length: frame synchronization length. depends on protocol settings
+ * @slots: rx or tx slot number
+ * @slot_width: rx or tx slot width in bits
+ * @slot_mask: rx or tx active slots mask. set at init or at runtime
+ * @data_size: PCM data width. corresponds to PCM substream width.
+ */
+struct stm32_sai_sub_data {
+	struct platform_device *pdev;
+	struct regmap *regmap;
+	struct snd_dmaengine_dai_dma_data dma_params;
+	struct snd_soc_dai_driver *cpu_dai_drv;
+	struct snd_soc_dai *cpu_dai;
+	struct snd_pcm_substream *substream;
+	struct stm32_sai_data *pdata;
+	struct clk *sai_ck;
+	dma_addr_t phys_addr;
+	unsigned int mclk_rate;
+	unsigned int id;
+	int dir;
+	bool master;
+	int fmt;
+	int sync;
+	int fs_length;
+	int slots;
+	int slot_width;
+	int slot_mask;
+	int data_size;
+};
+
+enum stm32_sai_fifo_th {
+	STM_SAI_FIFO_TH_EMPTY,
+	STM_SAI_FIFO_TH_QUARTER,
+	STM_SAI_FIFO_TH_HALF,
+	STM_SAI_FIFO_TH_3_QUARTER,
+	STM_SAI_FIFO_TH_FULL,
+};
+
+static bool stm32_sai_sub_readable_reg(struct device *dev, unsigned int reg)
+{
+	switch (reg) {
+	case STM_SAI_CR1_REGX:
+	case STM_SAI_CR2_REGX:
+	case STM_SAI_FRCR_REGX:
+	case STM_SAI_SLOTR_REGX:
+	case STM_SAI_IMR_REGX:
+	case STM_SAI_SR_REGX:
+	case STM_SAI_CLRFR_REGX:
+	case STM_SAI_DR_REGX:
+		return true;
+	default:
+		return false;
+	}
+}
+
+static bool stm32_sai_sub_volatile_reg(struct device *dev, unsigned int reg)
+{
+	switch (reg) {
+	case STM_SAI_DR_REGX:
+		return true;
+	default:
+		return false;
+	}
+}
+
+static bool stm32_sai_sub_writeable_reg(struct device *dev, unsigned int reg)
+{
+	switch (reg) {
+	case STM_SAI_CR1_REGX:
+	case STM_SAI_CR2_REGX:
+	case STM_SAI_FRCR_REGX:
+	case STM_SAI_SLOTR_REGX:
+	case STM_SAI_IMR_REGX:
+	case STM_SAI_SR_REGX:
+	case STM_SAI_CLRFR_REGX:
+	case STM_SAI_DR_REGX:
+		return true;
+	default:
+		return false;
+	}
+}
+
+static const struct regmap_config stm32_sai_sub_regmap_config = {
+	.reg_bits = 32,
+	.reg_stride = 4,
+	.val_bits = 32,
+	.max_register = STM_SAI_DR_REGX,
+	.readable_reg = stm32_sai_sub_readable_reg,
+	.volatile_reg = stm32_sai_sub_volatile_reg,
+	.writeable_reg = stm32_sai_sub_writeable_reg,
+	.fast_io = true,
+};
+
+static irqreturn_t stm32_sai_isr(int irq, void *devid)
+{
+	struct stm32_sai_sub_data *sai = (struct stm32_sai_sub_data *)devid;
+	struct snd_pcm_substream *substream = sai->substream;
+	struct platform_device *pdev = sai->pdev;
+	unsigned int sr, imr, flags;
+	snd_pcm_state_t status = SNDRV_PCM_STATE_RUNNING;
+
+	regmap_read(sai->regmap, STM_SAI_IMR_REGX, &imr);
+	regmap_read(sai->regmap, STM_SAI_SR_REGX, &sr);
+
+	flags = sr & imr;
+	if (!flags)
+		return IRQ_NONE;
+
+	regmap_update_bits(sai->regmap, STM_SAI_CLRFR_REGX, SAI_XCLRFR_MASK,
+			   SAI_XCLRFR_MASK);
+
+	if (flags & SAI_XIMR_OVRUDRIE) {
+		dev_err(&pdev->dev, "IT %s\n",
+			STM_SAI_IS_PLAYBACK(sai) ? "underrun" : "overrun");
+		status = SNDRV_PCM_STATE_XRUN;
+	}
+
+	if (flags & SAI_XIMR_MUTEDETIE)
+		dev_dbg(&pdev->dev, "IT mute detected\n");
+
+	if (flags & SAI_XIMR_WCKCFGIE) {
+		dev_err(&pdev->dev, "IT wrong clock configuration\n");
+		status = SNDRV_PCM_STATE_DISCONNECTED;
+	}
+
+	if (flags & SAI_XIMR_CNRDYIE)
+		dev_warn(&pdev->dev, "IT Codec not ready\n");
+
+	if (flags & SAI_XIMR_AFSDETIE) {
+		dev_warn(&pdev->dev, "IT Anticipated frame synchro\n");
+		status = SNDRV_PCM_STATE_XRUN;
+	}
+
+	if (flags & SAI_XIMR_LFSDETIE) {
+		dev_warn(&pdev->dev, "IT Late frame synchro\n");
+		status = SNDRV_PCM_STATE_XRUN;
+	}
+
+	if (status != SNDRV_PCM_STATE_RUNNING) {
+		snd_pcm_stream_lock(substream);
+		snd_pcm_stop(substream, SNDRV_PCM_STATE_XRUN);
+		snd_pcm_stream_unlock(substream);
+	}
+
+	return IRQ_HANDLED;
+}
+
+static int stm32_sai_set_sysclk(struct snd_soc_dai *cpu_dai,
+				int clk_id, unsigned int freq, int dir)
+{
+	struct stm32_sai_sub_data *sai = snd_soc_dai_get_drvdata(cpu_dai);
+
+	if ((dir == SND_SOC_CLOCK_OUT) && sai->master) {
+		sai->mclk_rate = freq;
+		dev_dbg(cpu_dai->dev, "SAI MCLK frequency is %uHz\n", freq);
+	}
+
+	return 0;
+}
+
+static int stm32_sai_set_dai_tdm_slot(struct snd_soc_dai *cpu_dai, u32 tx_mask,
+				      u32 rx_mask, int slots, int slot_width)
+{
+	struct stm32_sai_sub_data *sai = snd_soc_dai_get_drvdata(cpu_dai);
+	int slotr, slotr_mask, slot_size;
+
+	dev_dbg(cpu_dai->dev, "masks tx/rx:%#x/%#x, slots:%d, width:%d\n",
+		tx_mask, rx_mask, slots, slot_width);
+
+	switch (slot_width) {
+	case 16:
+		slot_size = SAI_SLOT_SIZE_16;
+		break;
+	case 32:
+		slot_size = SAI_SLOT_SIZE_32;
+		break;
+	default:
+		slot_size = SAI_SLOT_SIZE_AUTO;
+		break;
+	}
+
+	slotr = SAI_XSLOTR_SLOTSZ_SET(slot_size) |
+		SAI_XSLOTR_NBSLOT_SET(slots - 1);
+	slotr_mask = SAI_XSLOTR_SLOTSZ_MASK | SAI_XSLOTR_NBSLOT_MASK;
+
+	/* tx/rx mask set in machine init, if slot number defined in DT */
+	if (STM_SAI_IS_PLAYBACK(sai)) {
+		sai->slot_mask = tx_mask;
+		slotr |= SAI_XSLOTR_SLOTEN_SET(tx_mask);
+	}
+
+	if (STM_SAI_IS_CAPTURE(sai)) {
+		sai->slot_mask = rx_mask;
+		slotr |= SAI_XSLOTR_SLOTEN_SET(rx_mask);
+	}
+
+	slotr_mask |= SAI_XSLOTR_SLOTEN_MASK;
+
+	regmap_update_bits(sai->regmap, STM_SAI_SLOTR_REGX, slotr_mask, slotr);
+
+	sai->slot_width = slot_width;
+	sai->slots = slots;
+
+	return 0;
+}
+
+static int stm32_sai_set_dai_fmt(struct snd_soc_dai *cpu_dai, unsigned int fmt)
+{
+	struct stm32_sai_sub_data *sai = snd_soc_dai_get_drvdata(cpu_dai);
+	int cr1 = 0, frcr = 0;
+	int cr1_mask = 0, frcr_mask = 0;
+	int ret;
+
+	dev_dbg(cpu_dai->dev, "fmt %x\n", fmt);
+
+	switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+	/* SCK active high for all protocols */
+	case SND_SOC_DAIFMT_I2S:
+		cr1 |= SAI_XCR1_CKSTR;
+		frcr |= SAI_XFRCR_FSOFF | SAI_XFRCR_FSDEF;
+		break;
+	/* Left justified */
+	case SND_SOC_DAIFMT_MSB:
+		frcr |= SAI_XFRCR_FSPOL | SAI_XFRCR_FSDEF;
+		break;
+	/* Right justified */
+	case SND_SOC_DAIFMT_LSB:
+		frcr |= SAI_XFRCR_FSPOL | SAI_XFRCR_FSDEF;
+		break;
+	case SND_SOC_DAIFMT_DSP_A:
+		frcr |= SAI_XFRCR_FSPOL | SAI_XFRCR_FSOFF;
+		break;
+	case SND_SOC_DAIFMT_DSP_B:
+		frcr |= SAI_XFRCR_FSPOL;
+		break;
+	default:
+		dev_err(cpu_dai->dev, "Unsupported protocol %#x\n",
+			fmt & SND_SOC_DAIFMT_FORMAT_MASK);
+		return -EINVAL;
+	}
+
+	cr1_mask |= SAI_XCR1_PRTCFG_MASK | SAI_XCR1_CKSTR;
+	frcr_mask |= SAI_XFRCR_FSPOL | SAI_XFRCR_FSOFF |
+		     SAI_XFRCR_FSDEF;
+
+	/* DAI clock strobing. Invert setting previously set */
+	switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+	case SND_SOC_DAIFMT_NB_NF:
+		break;
+	case SND_SOC_DAIFMT_IB_NF:
+		cr1 ^= SAI_XCR1_CKSTR;
+		break;
+	case SND_SOC_DAIFMT_NB_IF:
+		frcr ^= SAI_XFRCR_FSPOL;
+		break;
+	case SND_SOC_DAIFMT_IB_IF:
+		/* Invert fs & sck */
+		cr1 ^= SAI_XCR1_CKSTR;
+		frcr ^= SAI_XFRCR_FSPOL;
+		break;
+	default:
+		dev_err(cpu_dai->dev, "Unsupported strobing %#x\n",
+			fmt & SND_SOC_DAIFMT_INV_MASK);
+		return -EINVAL;
+	}
+	cr1_mask |= SAI_XCR1_CKSTR;
+	frcr_mask |= SAI_XFRCR_FSPOL;
+
+	regmap_update_bits(sai->regmap, STM_SAI_FRCR_REGX, frcr_mask, frcr);
+
+	/* DAI clock master masks */
+	switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+	case SND_SOC_DAIFMT_CBM_CFM:
+		/* codec is master */
+		cr1 |= SAI_XCR1_SLAVE;
+		sai->master = false;
+		break;
+	case SND_SOC_DAIFMT_CBS_CFS:
+		sai->master = true;
+		break;
+	default:
+		dev_err(cpu_dai->dev, "Unsupported mode %#x\n",
+			fmt & SND_SOC_DAIFMT_MASTER_MASK);
+		return -EINVAL;
+	}
+	cr1_mask |= SAI_XCR1_SLAVE;
+
+	ret = regmap_update_bits(sai->regmap, STM_SAI_CR1_REGX, cr1_mask, cr1);
+	if (ret < 0) {
+		dev_err(cpu_dai->dev, "Failed to update CR1 register\n");
+		return ret;
+	}
+
+	sai->fmt = fmt;
+
+	return 0;
+}
+
+static int stm32_sai_startup(struct snd_pcm_substream *substream,
+			     struct snd_soc_dai *cpu_dai)
+{
+	struct stm32_sai_sub_data *sai = snd_soc_dai_get_drvdata(cpu_dai);
+	int imr, cr2, ret;
+
+	sai->substream = substream;
+
+	ret = clk_prepare_enable(sai->sai_ck);
+	if (ret < 0) {
+		dev_err(cpu_dai->dev, "failed to enable clock: %d\n", ret);
+		return ret;
+	}
+
+	/* Enable ITs */
+	regmap_update_bits(sai->regmap, STM_SAI_SR_REGX,
+			   SAI_XSR_MASK, (unsigned int)~SAI_XSR_MASK);
+
+	regmap_update_bits(sai->regmap, STM_SAI_CLRFR_REGX,
+			   SAI_XCLRFR_MASK, SAI_XCLRFR_MASK);
+
+	imr = SAI_XIMR_OVRUDRIE;
+	if (STM_SAI_IS_CAPTURE(sai)) {
+		regmap_read(sai->regmap, STM_SAI_CR2_REGX, &cr2);
+		if (cr2 & SAI_XCR2_MUTECNT_MASK)
+			imr |= SAI_XIMR_MUTEDETIE;
+	}
+
+	if (sai->master)
+		imr |= SAI_XIMR_WCKCFGIE;
+	else
+		imr |= SAI_XIMR_AFSDETIE | SAI_XIMR_LFSDETIE;
+
+	regmap_update_bits(sai->regmap, STM_SAI_IMR_REGX,
+			   SAI_XIMR_MASK, imr);
+
+	return 0;
+}
+
+static int stm32_sai_set_config(struct snd_soc_dai *cpu_dai,
+				struct snd_pcm_substream *substream,
+				struct snd_pcm_hw_params *params)
+{
+	struct stm32_sai_sub_data *sai = snd_soc_dai_get_drvdata(cpu_dai);
+	int cr1, cr1_mask, ret;
+	int fth = STM_SAI_FIFO_TH_HALF;
+
+	/* FIFO config */
+	regmap_update_bits(sai->regmap, STM_SAI_CR2_REGX,
+			   SAI_XCR2_FFLUSH | SAI_XCR2_FTH_MASK,
+			   SAI_XCR2_FFLUSH | SAI_XCR2_FTH_SET(fth));
+
+	/* Mode, data format and channel config */
+	cr1 = SAI_XCR1_PRTCFG_SET(SAI_FREE_PROTOCOL);
+	switch (params_format(params)) {
+	case SNDRV_PCM_FORMAT_S8:
+		cr1 |= SAI_XCR1_DS_SET(SAI_DATASIZE_8);
+		break;
+	case SNDRV_PCM_FORMAT_S16_LE:
+		cr1 |= SAI_XCR1_DS_SET(SAI_DATASIZE_16);
+		break;
+	case SNDRV_PCM_FORMAT_S32_LE:
+		cr1 |= SAI_XCR1_DS_SET(SAI_DATASIZE_32);
+		break;
+	default:
+		dev_err(cpu_dai->dev, "Data format not supported");
+		return -EINVAL;
+	}
+	cr1_mask = SAI_XCR1_DS_MASK | SAI_XCR1_PRTCFG_MASK;
+
+	cr1_mask |= SAI_XCR1_RX_TX;
+	if (STM_SAI_IS_CAPTURE(sai))
+		cr1 |= SAI_XCR1_RX_TX;
+
+	cr1_mask |= SAI_XCR1_MONO;
+	if ((sai->slots == 2) && (params_channels(params) == 1))
+		cr1 |= SAI_XCR1_MONO;
+
+	ret = regmap_update_bits(sai->regmap, STM_SAI_CR1_REGX, cr1_mask, cr1);
+	if (ret < 0) {
+		dev_err(cpu_dai->dev, "Failed to update CR1 register\n");
+		return ret;
+	}
+
+	/* DMA config */
+	sai->dma_params.maxburst = STM_SAI_FIFO_SIZE * fth / sizeof(u32);
+	snd_soc_dai_set_dma_data(cpu_dai, substream, (void *)&sai->dma_params);
+
+	return 0;
+}
+
+static int stm32_sai_set_slots(struct snd_soc_dai *cpu_dai)
+{
+	struct stm32_sai_sub_data *sai = snd_soc_dai_get_drvdata(cpu_dai);
+	int slotr, slot_sz;
+
+	regmap_read(sai->regmap, STM_SAI_SLOTR_REGX, &slotr);
+
+	/*
+	 * If SLOTSZ is set to auto in SLOTR, align slot width on data size
+	 * By default slot width = data size, if not forced from DT
+	 */
+	slot_sz = slotr & SAI_XSLOTR_SLOTSZ_MASK;
+	if (slot_sz == SAI_XSLOTR_SLOTSZ_SET(SAI_SLOT_SIZE_AUTO))
+		sai->slot_width = sai->data_size;
+
+	if (sai->slot_width < sai->data_size) {
+		dev_err(cpu_dai->dev,
+			"Data size %d larger than slot width\n",
+			sai->data_size);
+		return -EINVAL;
+	}
+
+	/* Slot number is set to 2, if not specified in DT */
+	if (!sai->slots)
+		sai->slots = 2;
+
+	/* The number of slots in the audio frame is equal to NBSLOT[3:0] + 1*/
+	regmap_update_bits(sai->regmap, STM_SAI_SLOTR_REGX,
+			   SAI_XSLOTR_NBSLOT_MASK,
+			   SAI_XSLOTR_NBSLOT_SET((sai->slots - 1)));
+
+	/* Set default slots mask if not already set from DT */
+	if (!(slotr & SAI_XSLOTR_SLOTEN_MASK)) {
+		sai->slot_mask = (1 << sai->slots) - 1;
+		regmap_update_bits(sai->regmap,
+				   STM_SAI_SLOTR_REGX, SAI_XSLOTR_SLOTEN_MASK,
+				   SAI_XSLOTR_SLOTEN_SET(sai->slot_mask));
+	}
+
+	dev_dbg(cpu_dai->dev, "slots %d, slot width %d\n",
+		sai->slots, sai->slot_width);
+
+	return 0;
+}
+
+static void stm32_sai_set_frame(struct snd_soc_dai *cpu_dai)
+{
+	struct stm32_sai_sub_data *sai = snd_soc_dai_get_drvdata(cpu_dai);
+	int fs_active, offset, format;
+	int frcr, frcr_mask;
+
+	format = sai->fmt & SND_SOC_DAIFMT_FORMAT_MASK;
+	sai->fs_length = sai->slot_width * sai->slots;
+
+	fs_active = sai->fs_length / 2;
+	if ((format == SND_SOC_DAIFMT_DSP_A) ||
+	    (format == SND_SOC_DAIFMT_DSP_B))
+		fs_active = 1;
+
+	frcr = SAI_XFRCR_FRL_SET((sai->fs_length - 1));
+	frcr |= SAI_XFRCR_FSALL_SET((fs_active - 1));
+	frcr_mask = SAI_XFRCR_FRL_MASK | SAI_XFRCR_FSALL_MASK;
+
+	dev_dbg(cpu_dai->dev, "frame length %d, frame active %d\n",
+		sai->fs_length, fs_active);
+
+	regmap_update_bits(sai->regmap, STM_SAI_FRCR_REGX, frcr_mask, frcr);
+
+	if ((sai->fmt & SND_SOC_DAIFMT_FORMAT_MASK) == SND_SOC_DAIFMT_LSB) {
+		offset = sai->slot_width - sai->data_size;
+
+		regmap_update_bits(sai->regmap, STM_SAI_SLOTR_REGX,
+				   SAI_XSLOTR_FBOFF_MASK,
+				   SAI_XSLOTR_FBOFF_SET(offset));
+	}
+}
+
+static int stm32_sai_configure_clock(struct snd_soc_dai *cpu_dai,
+				     struct snd_pcm_hw_params *params)
+{
+	struct stm32_sai_sub_data *sai = snd_soc_dai_get_drvdata(cpu_dai);
+	int cr1, mask, div = 0;
+	int sai_clk_rate, ret;
+
+	if (!sai->mclk_rate) {
+		dev_err(cpu_dai->dev, "Mclk rate is null\n");
+		return -EINVAL;
+	}
+
+	if (!(params_rate(params) % 11025))
+		clk_set_parent(sai->sai_ck, sai->pdata->clk_x11k);
+	else
+		clk_set_parent(sai->sai_ck, sai->pdata->clk_x8k);
+	sai_clk_rate = clk_get_rate(sai->sai_ck);
+
+	/*
+	 * mclk_rate = 256 * fs
+	 * MCKDIV = 0 if sai_ck < 3/2 * mclk_rate
+	 * MCKDIV = sai_ck / (2 * mclk_rate) otherwise
+	 */
+	if (2 * sai_clk_rate >= 3 * sai->mclk_rate)
+		div = DIV_ROUND_CLOSEST(sai_clk_rate, 2 * sai->mclk_rate);
+
+	if (div > SAI_XCR1_MCKDIV_MAX) {
+		dev_err(cpu_dai->dev, "Divider %d out of range\n", div);
+		return -EINVAL;
+	}
+	dev_dbg(cpu_dai->dev, "SAI clock %d, divider %d\n", sai_clk_rate, div);
+
+	mask = SAI_XCR1_MCKDIV_MASK;
+	cr1 = SAI_XCR1_MCKDIV_SET(div);
+	ret = regmap_update_bits(sai->regmap, STM_SAI_CR1_REGX, mask, cr1);
+	if (ret < 0) {
+		dev_err(cpu_dai->dev, "Failed to update CR1 register\n");
+		return ret;
+	}
+
+	return 0;
+}
+
+static int stm32_sai_hw_params(struct snd_pcm_substream *substream,
+			       struct snd_pcm_hw_params *params,
+			       struct snd_soc_dai *cpu_dai)
+{
+	struct stm32_sai_sub_data *sai = snd_soc_dai_get_drvdata(cpu_dai);
+	int ret;
+
+	sai->data_size = params_width(params);
+
+	ret = stm32_sai_set_slots(cpu_dai);
+	if (ret < 0)
+		return ret;
+	stm32_sai_set_frame(cpu_dai);
+
+	ret = stm32_sai_set_config(cpu_dai, substream, params);
+	if (ret)
+		return ret;
+
+	if (sai->master)
+		ret = stm32_sai_configure_clock(cpu_dai, params);
+
+	return ret;
+}
+
+static int stm32_sai_trigger(struct snd_pcm_substream *substream, int cmd,
+			     struct snd_soc_dai *cpu_dai)
+{
+	struct stm32_sai_sub_data *sai = snd_soc_dai_get_drvdata(cpu_dai);
+	int ret;
+
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+	case SNDRV_PCM_TRIGGER_RESUME:
+	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+		dev_dbg(cpu_dai->dev, "Enable DMA and SAI\n");
+
+		regmap_update_bits(sai->regmap, STM_SAI_CR1_REGX,
+				   SAI_XCR1_DMAEN, SAI_XCR1_DMAEN);
+
+		/* Enable SAI */
+		ret = regmap_update_bits(sai->regmap, STM_SAI_CR1_REGX,
+					 SAI_XCR1_SAIEN, SAI_XCR1_SAIEN);
+		if (ret < 0)
+			dev_err(cpu_dai->dev, "Failed to update CR1 register\n");
+		break;
+	case SNDRV_PCM_TRIGGER_SUSPEND:
+	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+	case SNDRV_PCM_TRIGGER_STOP:
+		dev_dbg(cpu_dai->dev, "Disable DMA and SAI\n");
+
+		regmap_update_bits(sai->regmap, STM_SAI_CR1_REGX,
+				   SAI_XCR1_DMAEN,
+				   (unsigned int)~SAI_XCR1_DMAEN);
+
+		ret = regmap_update_bits(sai->regmap, STM_SAI_CR1_REGX,
+					 SAI_XCR1_SAIEN,
+					 (unsigned int)~SAI_XCR1_SAIEN);
+		if (ret < 0)
+			dev_err(cpu_dai->dev, "Failed to update CR1 register\n");
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return ret;
+}
+
+static void stm32_sai_shutdown(struct snd_pcm_substream *substream,
+			       struct snd_soc_dai *cpu_dai)
+{
+	struct stm32_sai_sub_data *sai = snd_soc_dai_get_drvdata(cpu_dai);
+
+	regmap_update_bits(sai->regmap, STM_SAI_IMR_REGX, SAI_XIMR_MASK, 0);
+
+	clk_disable_unprepare(sai->sai_ck);
+	sai->substream = NULL;
+}
+
+static int stm32_sai_dai_probe(struct snd_soc_dai *cpu_dai)
+{
+	struct stm32_sai_sub_data *sai = dev_get_drvdata(cpu_dai->dev);
+
+	sai->dma_params.addr = (dma_addr_t)(sai->phys_addr + STM_SAI_DR_REGX);
+	sai->dma_params.maxburst = 1;
+	/* Buswidth will be set by framework at runtime */
+	sai->dma_params.addr_width = DMA_SLAVE_BUSWIDTH_UNDEFINED;
+
+	if (STM_SAI_IS_PLAYBACK(sai))
+		snd_soc_dai_init_dma_data(cpu_dai, &sai->dma_params, NULL);
+	else
+		snd_soc_dai_init_dma_data(cpu_dai, NULL, &sai->dma_params);
+
+	return 0;
+}
+
+static const struct snd_soc_dai_ops stm32_sai_pcm_dai_ops = {
+	.set_sysclk	= stm32_sai_set_sysclk,
+	.set_fmt	= stm32_sai_set_dai_fmt,
+	.set_tdm_slot	= stm32_sai_set_dai_tdm_slot,
+	.startup	= stm32_sai_startup,
+	.hw_params	= stm32_sai_hw_params,
+	.trigger	= stm32_sai_trigger,
+	.shutdown	= stm32_sai_shutdown,
+};
+
+static const struct snd_pcm_hardware stm32_sai_pcm_hw = {
+	.info = SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_MMAP,
+	.buffer_bytes_max = 8 * PAGE_SIZE,
+	.period_bytes_min = 1024, /* 5ms at 48kHz */
+	.period_bytes_max = PAGE_SIZE,
+	.periods_min = 2,
+	.periods_max = 8,
+};
+
+static struct snd_soc_dai_driver stm32_sai_playback_dai[] = {
+{
+		.probe = stm32_sai_dai_probe,
+		.id = 1, /* avoid call to fmt_single_name() */
+		.playback = {
+			.channels_min = 1,
+			.channels_max = 2,
+			.rate_min = 8000,
+			.rate_max = 192000,
+			.rates = SNDRV_PCM_RATE_CONTINUOUS,
+			/* DMA does not support 24 bits transfers */
+			.formats =
+				SNDRV_PCM_FMTBIT_S8 |
+				SNDRV_PCM_FMTBIT_S16_LE |
+				SNDRV_PCM_FMTBIT_S32_LE,
+		},
+		.ops = &stm32_sai_pcm_dai_ops,
+	}
+};
+
+static struct snd_soc_dai_driver stm32_sai_capture_dai[] = {
+{
+		.probe = stm32_sai_dai_probe,
+		.id = 1, /* avoid call to fmt_single_name() */
+		.capture = {
+			.channels_min = 1,
+			.channels_max = 2,
+			.rate_min = 8000,
+			.rate_max = 192000,
+			.rates = SNDRV_PCM_RATE_CONTINUOUS,
+			/* DMA does not support 24 bits transfers */
+			.formats =
+				SNDRV_PCM_FMTBIT_S8 |
+				SNDRV_PCM_FMTBIT_S16_LE |
+				SNDRV_PCM_FMTBIT_S32_LE,
+		},
+		.ops = &stm32_sai_pcm_dai_ops,
+	}
+};
+
+static const struct snd_dmaengine_pcm_config stm32_sai_pcm_config = {
+	.pcm_hardware	= &stm32_sai_pcm_hw,
+	.prepare_slave_config	= snd_dmaengine_pcm_prepare_slave_config,
+};
+
+static const struct snd_soc_component_driver stm32_component = {
+	.name = "stm32-sai",
+};
+
+static const struct of_device_id stm32_sai_sub_ids[] = {
+	{ .compatible = "st,stm32-sai-sub-a",
+	  .data = (void *)STM_SAI_A_ID},
+	{ .compatible = "st,stm32-sai-sub-b",
+	  .data = (void *)STM_SAI_B_ID},
+	{}
+};
+MODULE_DEVICE_TABLE(of, stm32_sai_sub_ids);
+
+static int stm32_sai_sub_parse_of(struct platform_device *pdev,
+				  struct stm32_sai_sub_data *sai)
+{
+	struct device_node *np = pdev->dev.of_node;
+	struct resource *res;
+	void __iomem *base;
+
+	if (!np)
+		return -ENODEV;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+
+	dev_err(&pdev->dev, "res %pr\n", res);
+
+	base = devm_ioremap_resource(&pdev->dev, res);
+	if (IS_ERR(base))
+		return PTR_ERR(base);
+
+	sai->phys_addr = res->start;
+	sai->regmap = devm_regmap_init_mmio(&pdev->dev, base,
+					    &stm32_sai_sub_regmap_config);
+
+	/* Get direction property */
+	if (of_property_match_string(np, "dma-names", "tx") >= 0) {
+		sai->dir = SNDRV_PCM_STREAM_PLAYBACK;
+	} else if (of_property_match_string(np, "dma-names", "rx") >= 0) {
+		sai->dir = SNDRV_PCM_STREAM_CAPTURE;
+	} else {
+		dev_err(&pdev->dev, "Unsupported direction\n");
+		return -EINVAL;
+	}
+
+	sai->sai_ck = devm_clk_get(&pdev->dev, "sai_ck");
+	if (IS_ERR(sai->sai_ck)) {
+		dev_err(&pdev->dev, "missing kernel clock sai_ck\n");
+		return PTR_ERR(sai->sai_ck);
+	}
+
+	return 0;
+}
+
+static int stm32_sai_sub_dais_init(struct platform_device *pdev,
+				   struct stm32_sai_sub_data *sai)
+{
+	sai->cpu_dai_drv = devm_kzalloc(&pdev->dev,
+					sizeof(struct snd_soc_dai_driver),
+					GFP_KERNEL);
+	if (!sai->cpu_dai_drv)
+		return -ENOMEM;
+
+	sai->cpu_dai_drv->name = dev_name(&pdev->dev);
+	if (STM_SAI_IS_PLAYBACK(sai)) {
+		memcpy(sai->cpu_dai_drv, &stm32_sai_playback_dai,
+		       sizeof(stm32_sai_playback_dai));
+		sai->cpu_dai_drv->playback.stream_name = sai->cpu_dai_drv->name;
+	} else {
+		memcpy(sai->cpu_dai_drv, &stm32_sai_capture_dai,
+		       sizeof(stm32_sai_capture_dai));
+		sai->cpu_dai_drv->capture.stream_name = sai->cpu_dai_drv->name;
+	}
+
+	return 0;
+}
+
+static int stm32_sai_sub_probe(struct platform_device *pdev)
+{
+	struct stm32_sai_sub_data *sai;
+	const struct of_device_id *of_id;
+	int ret;
+
+	sai = devm_kzalloc(&pdev->dev, sizeof(*sai), GFP_KERNEL);
+	if (!sai)
+		return -ENOMEM;
+
+	of_id = of_match_device(stm32_sai_sub_ids, &pdev->dev);
+	if (!of_id)
+		return -EINVAL;
+	sai->id = (uintptr_t)of_id->data;
+
+	sai->pdev = pdev;
+	platform_set_drvdata(pdev, sai);
+
+	sai->pdata = dev_get_drvdata(pdev->dev.parent);
+	if (!sai->pdata) {
+		dev_err(&pdev->dev, "Parent device data not available\n");
+		return -EINVAL;
+	}
+
+	ret = stm32_sai_sub_parse_of(pdev, sai);
+	if (ret)
+		return ret;
+
+	ret = stm32_sai_sub_dais_init(pdev, sai);
+	if (ret)
+		return ret;
+
+	ret = devm_request_irq(&pdev->dev, sai->pdata->irq, stm32_sai_isr,
+			       IRQF_SHARED, dev_name(&pdev->dev), sai);
+	if (ret) {
+		dev_err(&pdev->dev, "irq request returned %d\n", ret);
+		return ret;
+	}
+
+	ret = devm_snd_soc_register_component(&pdev->dev, &stm32_component,
+					      sai->cpu_dai_drv, 1);
+	if (ret)
+		return ret;
+
+	ret = devm_snd_dmaengine_pcm_register(&pdev->dev,
+					      &stm32_sai_pcm_config, 0);
+	if (ret) {
+		dev_err(&pdev->dev, "could not register pcm dma\n");
+		return ret;
+	}
+
+	return 0;
+}
+
+static struct platform_driver stm32_sai_sub_driver = {
+	.driver = {
+		.name = "st,stm32-sai-sub",
+		.of_match_table = stm32_sai_sub_ids,
+	},
+	.probe = stm32_sai_sub_probe,
+};
+
+module_platform_driver(stm32_sai_sub_driver);
+
+MODULE_DESCRIPTION("STM32 Soc SAI sub-block Interface");
+MODULE_AUTHOR("Olivier Moysan, <olivier.moysan@st.com>");
+MODULE_ALIAS("platform:st,stm32-sai-sub");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/sunxi/sun8i-codec-analog.c b/sound/soc/sunxi/sun8i-codec-analog.c
index 72331332b72e..6c17c99c2c8d 100644
--- a/sound/soc/sunxi/sun8i-codec-analog.c
+++ b/sound/soc/sunxi/sun8i-codec-analog.c
@@ -252,24 +252,15 @@ static const DECLARE_TLV_DB_RANGE(sun8i_codec_mic_gain_scale,
 );
 
 static const struct snd_kcontrol_new sun8i_codec_common_controls[] = {
-	/* Mixer pre-gains */
-	SOC_SINGLE_TLV("Line In Playback Volume", SUN8I_ADDA_LINEIN_GCTRL,
-		       SUN8I_ADDA_LINEIN_GCTRL_LINEING,
-		       0x7, 0, sun8i_codec_out_mixer_pregain_scale),
+	/* Mixer pre-gain */
 	SOC_SINGLE_TLV("Mic1 Playback Volume", SUN8I_ADDA_MICIN_GCTRL,
 		       SUN8I_ADDA_MICIN_GCTRL_MIC1G,
 		       0x7, 0, sun8i_codec_out_mixer_pregain_scale),
-	SOC_SINGLE_TLV("Mic2 Playback Volume",
-		       SUN8I_ADDA_MICIN_GCTRL, SUN8I_ADDA_MICIN_GCTRL_MIC2G,
-		       0x7, 0, sun8i_codec_out_mixer_pregain_scale),
 
-	/* Microphone Amp boost gains */
+	/* Microphone Amp boost gain */
 	SOC_SINGLE_TLV("Mic1 Boost Volume", SUN8I_ADDA_MIC1G_MICBIAS_CTRL,
 		       SUN8I_ADDA_MIC1G_MICBIAS_CTRL_MIC1BOOST, 0x7, 0,
 		       sun8i_codec_mic_gain_scale),
-	SOC_SINGLE_TLV("Mic2 Boost Volume", SUN8I_ADDA_MIC2G_CTRL,
-		       SUN8I_ADDA_MIC2G_CTRL_MIC2BOOST, 0x7, 0,
-		       sun8i_codec_mic_gain_scale),
 
 	/* ADC */
 	SOC_SINGLE_TLV("ADC Gain Capture Volume", SUN8I_ADDA_ADC_AP_EN,
@@ -295,12 +286,8 @@ static const struct snd_soc_dapm_widget sun8i_codec_common_widgets[] = {
 	 * stream widgets at the card level.
 	 */
 
-	/* Line In */
-	SND_SOC_DAPM_INPUT("LINEIN"),
-
-	/* Microphone inputs */
+	/* Microphone input */
 	SND_SOC_DAPM_INPUT("MIC1"),
-	SND_SOC_DAPM_INPUT("MIC2"),
 
 	/* Microphone Bias */
 	SND_SOC_DAPM_SUPPLY("MBIAS", SUN8I_ADDA_MIC1G_MICBIAS_CTRL,
@@ -310,8 +297,6 @@ static const struct snd_soc_dapm_widget sun8i_codec_common_widgets[] = {
 	/* Mic input path */
 	SND_SOC_DAPM_PGA("Mic1 Amplifier", SUN8I_ADDA_MIC1G_MICBIAS_CTRL,
 			 SUN8I_ADDA_MIC1G_MICBIAS_CTRL_MIC1AMPEN, 0, NULL, 0),
-	SND_SOC_DAPM_PGA("Mic2 Amplifier", SUN8I_ADDA_MIC2G_CTRL,
-			 SUN8I_ADDA_MIC2G_CTRL_MIC2AMPEN, 0, NULL, 0),
 
 	/* Mixers */
 	SND_SOC_DAPM_MIXER("Left Mixer", SUN8I_ADDA_DAC_PA_SRC,
@@ -335,35 +320,26 @@ static const struct snd_soc_dapm_widget sun8i_codec_common_widgets[] = {
 static const struct snd_soc_dapm_route sun8i_codec_common_routes[] = {
 	/* Microphone Routes */
 	{ "Mic1 Amplifier", NULL, "MIC1"},
-	{ "Mic2 Amplifier", NULL, "MIC2"},
 
 	/* Left Mixer Routes */
 	{ "Left Mixer", "DAC Playback Switch", "Left DAC" },
 	{ "Left Mixer", "DAC Reversed Playback Switch", "Right DAC" },
-	{ "Left Mixer", "Line In Playback Switch", "LINEIN" },
 	{ "Left Mixer", "Mic1 Playback Switch", "Mic1 Amplifier" },
-	{ "Left Mixer", "Mic2 Playback Switch", "Mic2 Amplifier" },
 
 	/* Right Mixer Routes */
 	{ "Right Mixer", "DAC Playback Switch", "Right DAC" },
 	{ "Right Mixer", "DAC Reversed Playback Switch", "Left DAC" },
-	{ "Right Mixer", "Line In Playback Switch", "LINEIN" },
 	{ "Right Mixer", "Mic1 Playback Switch", "Mic1 Amplifier" },
-	{ "Right Mixer", "Mic2 Playback Switch", "Mic2 Amplifier" },
 
 	/* Left ADC Mixer Routes */
 	{ "Left ADC Mixer", "Mixer Capture Switch", "Left Mixer" },
 	{ "Left ADC Mixer", "Mixer Reversed Capture Switch", "Right Mixer" },
-	{ "Left ADC Mixer", "Line In Capture Switch", "LINEIN" },
 	{ "Left ADC Mixer", "Mic1 Capture Switch", "Mic1 Amplifier" },
-	{ "Left ADC Mixer", "Mic2 Capture Switch", "Mic2 Amplifier" },
 
 	/* Right ADC Mixer Routes */
 	{ "Right ADC Mixer", "Mixer Capture Switch", "Right Mixer" },
 	{ "Right ADC Mixer", "Mixer Reversed Capture Switch", "Left Mixer" },
-	{ "Right ADC Mixer", "Line In Capture Switch", "LINEIN" },
 	{ "Right ADC Mixer", "Mic1 Capture Switch", "Mic1 Amplifier" },
-	{ "Right ADC Mixer", "Mic2 Capture Switch", "Mic2 Amplifier" },
 
 	/* ADC Routes */
 	{ "Left ADC", NULL, "Left ADC Mixer" },
@@ -498,6 +474,61 @@ static int sun8i_codec_add_hmic(struct snd_soc_component *cmpnt)
 	return ret;
 }
 
+/* line in specific controls, widgets and rines */
+static const struct snd_kcontrol_new sun8i_codec_linein_controls[] = {
+	/* Mixer pre-gain */
+	SOC_SINGLE_TLV("Line In Playback Volume", SUN8I_ADDA_LINEIN_GCTRL,
+		       SUN8I_ADDA_LINEIN_GCTRL_LINEING,
+		       0x7, 0, sun8i_codec_out_mixer_pregain_scale),
+};
+
+static const struct snd_soc_dapm_widget sun8i_codec_linein_widgets[] = {
+	/* Line input */
+	SND_SOC_DAPM_INPUT("LINEIN"),
+};
+
+static const struct snd_soc_dapm_route sun8i_codec_linein_routes[] = {
+	{ "Left Mixer", "Line In Playback Switch", "LINEIN" },
+
+	{ "Right Mixer", "Line In Playback Switch", "LINEIN" },
+
+	{ "Left ADC Mixer", "Line In Capture Switch", "LINEIN" },
+
+	{ "Right ADC Mixer", "Line In Capture Switch", "LINEIN" },
+};
+
+static int sun8i_codec_add_linein(struct snd_soc_component *cmpnt)
+{
+	struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(cmpnt);
+	struct device *dev = cmpnt->dev;
+	int ret;
+
+	ret = snd_soc_add_component_controls(cmpnt,
+					     sun8i_codec_linein_controls,
+					     ARRAY_SIZE(sun8i_codec_linein_controls));
+	if (ret) {
+		dev_err(dev, "Failed to add Line In controls: %d\n", ret);
+		return ret;
+	}
+
+	ret = snd_soc_dapm_new_controls(dapm, sun8i_codec_linein_widgets,
+					ARRAY_SIZE(sun8i_codec_linein_widgets));
+	if (ret) {
+		dev_err(dev, "Failed to add Line In DAPM widgets: %d\n", ret);
+		return ret;
+	}
+
+	ret = snd_soc_dapm_add_routes(dapm, sun8i_codec_linein_routes,
+				      ARRAY_SIZE(sun8i_codec_linein_routes));
+	if (ret) {
+		dev_err(dev, "Failed to add Line In DAPM routes: %d\n", ret);
+		return ret;
+	}
+
+	return 0;
+}
+
+
 /* line out specific controls, widgets and routes */
 static const DECLARE_TLV_DB_RANGE(sun8i_codec_lineout_vol_scale,
 	0, 1, TLV_DB_SCALE_ITEM(TLV_DB_GAIN_MUTE, 0, 1),
@@ -578,19 +609,90 @@ static int sun8i_codec_add_lineout(struct snd_soc_component *cmpnt)
 	return 0;
 }
 
+/* mic2 specific controls, widgets and routes */
+static const struct snd_kcontrol_new sun8i_codec_mic2_controls[] = {
+	/* Mixer pre-gain */
+	SOC_SINGLE_TLV("Mic2 Playback Volume",
+		       SUN8I_ADDA_MICIN_GCTRL, SUN8I_ADDA_MICIN_GCTRL_MIC2G,
+		       0x7, 0, sun8i_codec_out_mixer_pregain_scale),
+
+	/* Microphone Amp boost gain */
+	SOC_SINGLE_TLV("Mic2 Boost Volume", SUN8I_ADDA_MIC2G_CTRL,
+		       SUN8I_ADDA_MIC2G_CTRL_MIC2BOOST, 0x7, 0,
+		       sun8i_codec_mic_gain_scale),
+};
+
+static const struct snd_soc_dapm_widget sun8i_codec_mic2_widgets[] = {
+	/* Microphone input */
+	SND_SOC_DAPM_INPUT("MIC2"),
+
+	/* Mic input path */
+	SND_SOC_DAPM_PGA("Mic2 Amplifier", SUN8I_ADDA_MIC2G_CTRL,
+			 SUN8I_ADDA_MIC2G_CTRL_MIC2AMPEN, 0, NULL, 0),
+};
+
+static const struct snd_soc_dapm_route sun8i_codec_mic2_routes[] = {
+	{ "Mic2 Amplifier", NULL, "MIC2"},
+
+	{ "Left Mixer", "Mic2 Playback Switch", "Mic2 Amplifier" },
+
+	{ "Right Mixer", "Mic2 Playback Switch", "Mic2 Amplifier" },
+
+	{ "Left ADC Mixer", "Mic2 Capture Switch", "Mic2 Amplifier" },
+
+	{ "Right ADC Mixer", "Mic2 Capture Switch", "Mic2 Amplifier" },
+};
+
+static int sun8i_codec_add_mic2(struct snd_soc_component *cmpnt)
+{
+	struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(cmpnt);
+	struct device *dev = cmpnt->dev;
+	int ret;
+
+	ret = snd_soc_add_component_controls(cmpnt,
+					     sun8i_codec_mic2_controls,
+					     ARRAY_SIZE(sun8i_codec_mic2_controls));
+	if (ret) {
+		dev_err(dev, "Failed to add MIC2 controls: %d\n", ret);
+		return ret;
+	}
+
+	ret = snd_soc_dapm_new_controls(dapm, sun8i_codec_mic2_widgets,
+					ARRAY_SIZE(sun8i_codec_mic2_widgets));
+	if (ret) {
+		dev_err(dev, "Failed to add MIC2 DAPM widgets: %d\n", ret);
+		return ret;
+	}
+
+	ret = snd_soc_dapm_add_routes(dapm, sun8i_codec_mic2_routes,
+				      ARRAY_SIZE(sun8i_codec_mic2_routes));
+	if (ret) {
+		dev_err(dev, "Failed to add MIC2 DAPM routes: %d\n", ret);
+		return ret;
+	}
+
+	return 0;
+}
+
 struct sun8i_codec_analog_quirks {
 	bool has_headphone;
 	bool has_hmic;
+	bool has_linein;
 	bool has_lineout;
+	bool has_mic2;
 };
 
 static const struct sun8i_codec_analog_quirks sun8i_a23_quirks = {
 	.has_headphone	= true,
 	.has_hmic	= true,
+	.has_linein	= true,
+	.has_mic2	= true,
 };
 
 static const struct sun8i_codec_analog_quirks sun8i_h3_quirks = {
+	.has_linein	= true,
 	.has_lineout	= true,
+	.has_mic2	= true,
 };
 
 static int sun8i_codec_analog_cmpnt_probe(struct snd_soc_component *cmpnt)
@@ -620,12 +722,24 @@ static int sun8i_codec_analog_cmpnt_probe(struct snd_soc_component *cmpnt)
 			return ret;
 	}
 
+	if (quirks->has_linein) {
+		ret = sun8i_codec_add_linein(cmpnt);
+		if (ret)
+			return ret;
+	}
+
 	if (quirks->has_lineout) {
 		ret = sun8i_codec_add_lineout(cmpnt);
 		if (ret)
 			return ret;
 	}
 
+	if (quirks->has_mic2) {
+		ret = sun8i_codec_add_mic2(cmpnt);
+		if (ret)
+			return ret;
+	}
+
 	return 0;
 }
 
diff --git a/sound/soc/sunxi/sun8i-codec.c b/sound/soc/sunxi/sun8i-codec.c
index 7527ba29a5a0..5723c3404f6b 100644
--- a/sound/soc/sunxi/sun8i-codec.c
+++ b/sound/soc/sunxi/sun8i-codec.c
@@ -290,12 +290,10 @@ static const struct snd_soc_dapm_widget sun8i_codec_dapm_widgets[] = {
 			    SUN8I_AIF1_DACDAT_CTRL_AIF1_DA0R_ENA, 0),
 
 	/* DAC Mixers */
-	SND_SOC_DAPM_MIXER("Left Digital DAC Mixer", SND_SOC_NOPM, 0, 0,
-			   sun8i_dac_mixer_controls,
-			   ARRAY_SIZE(sun8i_dac_mixer_controls)),
-	SND_SOC_DAPM_MIXER("Right Digital DAC Mixer", SND_SOC_NOPM, 0, 0,
-			   sun8i_dac_mixer_controls,
-			   ARRAY_SIZE(sun8i_dac_mixer_controls)),
+	SOC_MIXER_ARRAY("Left Digital DAC Mixer", SND_SOC_NOPM, 0, 0,
+			sun8i_dac_mixer_controls),
+	SOC_MIXER_ARRAY("Right Digital DAC Mixer", SND_SOC_NOPM, 0, 0,
+			sun8i_dac_mixer_controls),
 
 	/* Clocks */
 	SND_SOC_DAPM_SUPPLY("MODCLK AFI1", SUN8I_MOD_CLK_ENA,
diff --git a/sound/soc/tegra/tegra20_ac97.c b/sound/soc/tegra/tegra20_ac97.c
index a68368edab9c..affad46bf188 100644
--- a/sound/soc/tegra/tegra20_ac97.c
+++ b/sound/soc/tegra/tegra20_ac97.c
@@ -318,7 +318,6 @@ static int tegra20_ac97_platform_probe(struct platform_device *pdev)
 	ac97 = devm_kzalloc(&pdev->dev, sizeof(struct tegra20_ac97),
 			    GFP_KERNEL);
 	if (!ac97) {
-		dev_err(&pdev->dev, "Can't allocate tegra20_ac97\n");
 		ret = -ENOMEM;
 		goto err;
 	}
diff --git a/sound/soc/tegra/tegra20_das.c b/sound/soc/tegra/tegra20_das.c
index 89add13c31cf..4024e3abbeed 100644
--- a/sound/soc/tegra/tegra20_das.c
+++ b/sound/soc/tegra/tegra20_das.c
@@ -41,6 +41,7 @@ static inline void tegra20_das_write(u32 reg, u32 val)
 static inline u32 tegra20_das_read(u32 reg)
 {
 	u32 val;
+
 	regmap_read(das->regmap, reg, &val);
 	return val;
 }
@@ -142,7 +143,6 @@ static int tegra20_das_probe(struct platform_device *pdev)
 
 	das = devm_kzalloc(&pdev->dev, sizeof(struct tegra20_das), GFP_KERNEL);
 	if (!das) {
-		dev_err(&pdev->dev, "Can't allocate tegra20_das\n");
 		ret = -ENOMEM;
 		goto err;
 	}
diff --git a/sound/soc/tegra/tegra20_i2s.c b/sound/soc/tegra/tegra20_i2s.c
index 14106fa82bca..26253c2849e7 100644
--- a/sound/soc/tegra/tegra20_i2s.c
+++ b/sound/soc/tegra/tegra20_i2s.c
@@ -345,7 +345,6 @@ static int tegra20_i2s_platform_probe(struct platform_device *pdev)
 
 	i2s = devm_kzalloc(&pdev->dev, sizeof(struct tegra20_i2s), GFP_KERNEL);
 	if (!i2s) {
-		dev_err(&pdev->dev, "Can't allocate tegra20_i2s\n");
 		ret = -ENOMEM;
 		goto err;
 	}
diff --git a/sound/soc/tegra/tegra20_spdif.c b/sound/soc/tegra/tegra20_spdif.c
index a0c3640572b9..767c0491e11a 100644
--- a/sound/soc/tegra/tegra20_spdif.c
+++ b/sound/soc/tegra/tegra20_spdif.c
@@ -271,10 +271,9 @@ static int tegra20_spdif_platform_probe(struct platform_device *pdev)
 
 	spdif = devm_kzalloc(&pdev->dev, sizeof(struct tegra20_spdif),
 			     GFP_KERNEL);
-	if (!spdif) {
-		dev_err(&pdev->dev, "Can't allocate tegra20_spdif\n");
+	if (!spdif)
 		return -ENOMEM;
-	}
+
 	dev_set_drvdata(&pdev->dev, spdif);
 
 	spdif->clk_spdif_out = devm_clk_get(&pdev->dev, "spdif_out");
diff --git a/sound/soc/tegra/tegra30_ahub.c b/sound/soc/tegra/tegra30_ahub.c
index fef3b9a21a66..8c10ae7982ba 100644
--- a/sound/soc/tegra/tegra30_ahub.c
+++ b/sound/soc/tegra/tegra30_ahub.c
@@ -41,6 +41,7 @@ static inline void tegra30_apbif_write(u32 reg, u32 val)
 static inline u32 tegra30_apbif_read(u32 reg)
 {
 	u32 val;
+
 	regmap_read(ahub->regmap_apbif, reg, &val);
 	return val;
 }
@@ -560,10 +561,8 @@ static int tegra30_ahub_probe(struct platform_device *pdev)
 
 	ahub = devm_kzalloc(&pdev->dev, sizeof(struct tegra30_ahub),
 			    GFP_KERNEL);
-	if (!ahub) {
-		dev_err(&pdev->dev, "Can't allocate tegra30_ahub\n");
+	if (!ahub)
 		return -ENOMEM;
-	}
 	dev_set_drvdata(&pdev->dev, ahub);
 
 	ahub->soc_data = soc_data;
diff --git a/sound/soc/tegra/tegra30_i2s.c b/sound/soc/tegra/tegra30_i2s.c
index 8e55583aa104..b2b279c96029 100644
--- a/sound/soc/tegra/tegra30_i2s.c
+++ b/sound/soc/tegra/tegra30_i2s.c
@@ -385,7 +385,6 @@ static int tegra30_i2s_platform_probe(struct platform_device *pdev)
 
 	i2s = devm_kzalloc(&pdev->dev, sizeof(struct tegra30_i2s), GFP_KERNEL);
 	if (!i2s) {
-		dev_err(&pdev->dev, "Can't allocate tegra30_i2s\n");
 		ret = -ENOMEM;
 		goto err;
 	}
diff --git a/sound/soc/tegra/tegra_alc5632.c b/sound/soc/tegra/tegra_alc5632.c
index eead6e7f205b..0509902512cc 100644
--- a/sound/soc/tegra/tegra_alc5632.c
+++ b/sound/soc/tegra/tegra_alc5632.c
@@ -169,10 +169,8 @@ static int tegra_alc5632_probe(struct platform_device *pdev)
 
 	alc5632 = devm_kzalloc(&pdev->dev,
 			sizeof(struct tegra_alc5632), GFP_KERNEL);
-	if (!alc5632) {
-		dev_err(&pdev->dev, "Can't allocate tegra_alc5632\n");
+	if (!alc5632)
 		return -ENOMEM;
-	}
 
 	card->dev = &pdev->dev;
 	platform_set_drvdata(pdev, card);
diff --git a/sound/soc/tegra/tegra_max98090.c b/sound/soc/tegra/tegra_max98090.c
index a403db6d563e..c34a54d6e812 100644
--- a/sound/soc/tegra/tegra_max98090.c
+++ b/sound/soc/tegra/tegra_max98090.c
@@ -225,10 +225,8 @@ static int tegra_max98090_probe(struct platform_device *pdev)
 
 	machine = devm_kzalloc(&pdev->dev,
 			sizeof(struct tegra_max98090), GFP_KERNEL);
-	if (!machine) {
-		dev_err(&pdev->dev, "Can't allocate tegra_max98090\n");
+	if (!machine)
 		return -ENOMEM;
-	}
 
 	card->dev = &pdev->dev;
 	platform_set_drvdata(pdev, card);
diff --git a/sound/soc/tegra/tegra_rt5640.c b/sound/soc/tegra/tegra_rt5640.c
index 25b9fc03ba62..93a356802345 100644
--- a/sound/soc/tegra/tegra_rt5640.c
+++ b/sound/soc/tegra/tegra_rt5640.c
@@ -170,10 +170,8 @@ static int tegra_rt5640_probe(struct platform_device *pdev)
 
 	machine = devm_kzalloc(&pdev->dev,
 			sizeof(struct tegra_rt5640), GFP_KERNEL);
-	if (!machine) {
-		dev_err(&pdev->dev, "Can't allocate tegra_rt5640\n");
+	if (!machine)
 		return -ENOMEM;
-	}
 
 	card->dev = &pdev->dev;
 	platform_set_drvdata(pdev, card);
diff --git a/sound/soc/tegra/tegra_sgtl5000.c b/sound/soc/tegra/tegra_sgtl5000.c
index 4bbab098f50b..6dda01f69983 100644
--- a/sound/soc/tegra/tegra_sgtl5000.c
+++ b/sound/soc/tegra/tegra_sgtl5000.c
@@ -120,10 +120,8 @@ static int tegra_sgtl5000_driver_probe(struct platform_device *pdev)
 
 	machine = devm_kzalloc(&pdev->dev, sizeof(struct tegra_sgtl5000),
 			       GFP_KERNEL);
-	if (!machine) {
-		dev_err(&pdev->dev, "Can't allocate tegra_sgtl5000 struct\n");
+	if (!machine)
 		return -ENOMEM;
-	}
 
 	card->dev = &pdev->dev;
 	platform_set_drvdata(pdev, card);
diff --git a/sound/soc/tegra/tegra_wm8753.c b/sound/soc/tegra/tegra_wm8753.c
index bdedd1028569..d0ab0026a4cd 100644
--- a/sound/soc/tegra/tegra_wm8753.c
+++ b/sound/soc/tegra/tegra_wm8753.c
@@ -128,10 +128,8 @@ static int tegra_wm8753_driver_probe(struct platform_device *pdev)
 
 	machine = devm_kzalloc(&pdev->dev, sizeof(struct tegra_wm8753),
 			       GFP_KERNEL);
-	if (!machine) {
-		dev_err(&pdev->dev, "Can't allocate tegra_wm8753 struct\n");
+	if (!machine)
 		return -ENOMEM;
-	}
 
 	card->dev = &pdev->dev;
 	platform_set_drvdata(pdev, card);
diff --git a/sound/soc/tegra/tegra_wm8903.c b/sound/soc/tegra/tegra_wm8903.c
index 2013e9c4bba0..dbfb49298ae8 100644
--- a/sound/soc/tegra/tegra_wm8903.c
+++ b/sound/soc/tegra/tegra_wm8903.c
@@ -248,10 +248,8 @@ static int tegra_wm8903_driver_probe(struct platform_device *pdev)
 
 	machine = devm_kzalloc(&pdev->dev, sizeof(struct tegra_wm8903),
 			       GFP_KERNEL);
-	if (!machine) {
-		dev_err(&pdev->dev, "Can't allocate tegra_wm8903 struct\n");
+	if (!machine)
 		return -ENOMEM;
-	}
 
 	card->dev = &pdev->dev;
 	platform_set_drvdata(pdev, card);
diff --git a/sound/soc/tegra/tegra_wm9712.c b/sound/soc/tegra/tegra_wm9712.c
index 6492f8143ff1..c9cd22432627 100644
--- a/sound/soc/tegra/tegra_wm9712.c
+++ b/sound/soc/tegra/tegra_wm9712.c
@@ -77,10 +77,8 @@ static int tegra_wm9712_driver_probe(struct platform_device *pdev)
 
 	machine = devm_kzalloc(&pdev->dev, sizeof(struct tegra_wm9712),
 			       GFP_KERNEL);
-	if (!machine) {
-		dev_err(&pdev->dev, "Can't allocate tegra_wm9712 struct\n");
+	if (!machine)
 		return -ENOMEM;
-	}
 
 	card->dev = &pdev->dev;
 	platform_set_drvdata(pdev, card);
diff --git a/sound/soc/tegra/trimslice.c b/sound/soc/tegra/trimslice.c
index 870f84ab5005..c9dcad9bb931 100644
--- a/sound/soc/tegra/trimslice.c
+++ b/sound/soc/tegra/trimslice.c
@@ -123,10 +123,8 @@ static int tegra_snd_trimslice_probe(struct platform_device *pdev)
 
 	trimslice = devm_kzalloc(&pdev->dev, sizeof(struct tegra_trimslice),
 				 GFP_KERNEL);
-	if (!trimslice) {
-		dev_err(&pdev->dev, "Can't allocate tegra_trimslice\n");
+	if (!trimslice)
 		return -ENOMEM;
-	}
 
 	card->dev = &pdev->dev;
 	platform_set_drvdata(pdev, card);
diff --git a/sound/soc/txx9/txx9aclc.c b/sound/soc/txx9/txx9aclc.c
index a8f705bb60dc..7912bf09dc4d 100644
--- a/sound/soc/txx9/txx9aclc.c
+++ b/sound/soc/txx9/txx9aclc.c
@@ -206,7 +206,7 @@ static void txx9aclc_dma_tasklet(unsigned long data)
 static int txx9aclc_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
 {
 	struct txx9aclc_dmadata *dmadata = substream->runtime->private_data;
-	struct txx9aclc_plat_drvdata *drvdata =txx9aclc_drvdata;
+	struct txx9aclc_plat_drvdata *drvdata = txx9aclc_drvdata;
 	void __iomem *base = drvdata->base;
 	unsigned long flags;
 	int ret = 0;
@@ -340,7 +340,7 @@ static bool filter(struct dma_chan *chan, void *param)
 static int txx9aclc_dma_init(struct txx9aclc_soc_device *dev,
 			     struct txx9aclc_dmadata *dmadata)
 {
-	struct txx9aclc_plat_drvdata *drvdata =txx9aclc_drvdata;
+	struct txx9aclc_plat_drvdata *drvdata = txx9aclc_drvdata;
 	struct txx9dmac_slave *ds = &dmadata->dma_slave;
 	dma_cap_mask_t mask;
 
@@ -392,6 +392,7 @@ static int txx9aclc_pcm_remove(struct snd_soc_platform *platform)
 	for (i = 0; i < 2; i++) {
 		struct txx9aclc_dmadata *dmadata = &dev->dmadata[i];
 		struct dma_chan *chan = dmadata->dma_chan;
+
 		if (chan) {
 			dmadata->frag_count = -1;
 			dmaengine_terminate_all(chan);
diff --git a/sound/soc/ux500/mop500.c b/sound/soc/ux500/mop500.c
index ba9fc099cf67..b50f68a439ce 100644
--- a/sound/soc/ux500/mop500.c
+++ b/sound/soc/ux500/mop500.c
@@ -33,7 +33,6 @@ static struct snd_soc_dai_link mop500_dai_links[] = {
 		.stream_name = "ab8500_0",
 		.cpu_dai_name = "ux500-msp-i2s.1",
 		.codec_dai_name = "ab8500-codec-dai.0",
-		.platform_name = "ux500-msp-i2s.1",
 		.codec_name = "ab8500-codec.0",
 		.init = mop500_ab8500_machine_init,
 		.ops = mop500_ab8500_ops,
@@ -43,7 +42,6 @@ static struct snd_soc_dai_link mop500_dai_links[] = {
 		.stream_name = "ab8500_1",
 		.cpu_dai_name = "ux500-msp-i2s.3",
 		.codec_dai_name = "ab8500-codec-dai.1",
-		.platform_name = "ux500-msp-i2s.3",
 		.codec_name = "ab8500-codec.0",
 		.init = NULL,
 		.ops = mop500_ab8500_ops,
@@ -87,8 +85,6 @@ static int mop500_of_probe(struct platform_device *pdev,
 	for (i = 0; i < 2; i++) {
 		mop500_dai_links[i].cpu_of_node = msp_np[i];
 		mop500_dai_links[i].cpu_dai_name = NULL;
-		mop500_dai_links[i].platform_of_node = msp_np[i];
-		mop500_dai_links[i].platform_name = NULL;
 		mop500_dai_links[i].codec_of_node = codec_np;
 		mop500_dai_links[i].codec_name = NULL;
 	}
diff --git a/sound/soc/ux500/ux500_msp_dai.c b/sound/soc/ux500/ux500_msp_dai.c
index b343efd9be5b..ec5152aa3f6e 100644
--- a/sound/soc/ux500/ux500_msp_dai.c
+++ b/sound/soc/ux500/ux500_msp_dai.c
@@ -133,6 +133,7 @@ static int setup_pcm_framing(struct snd_soc_dai *dai, unsigned int rate,
 	struct ux500_msp_i2s_drvdata *drvdata = dev_get_drvdata(dai->dev);
 
 	u32 frame_length = MSP_FRAME_LEN_1;
+
 	prot_desc->frame_width = 0;
 
 	switch (drvdata->slots) {
@@ -482,7 +483,8 @@ static int ux500_msp_dai_prepare(struct snd_pcm_substream *substream,
 	if ((drvdata->fmt & SND_SOC_DAIFMT_MASTER_MASK) &&
 		(drvdata->msp->f_bitclk > 19200000)) {
 		/* If the bit-clock is higher than 19.2MHz, Vape should be
-		 * run in 100% OPP. Only when bit-clock is used (MSP master) */
+		 * run in 100% OPP. Only when bit-clock is used (MSP master)
+		 */
 		prcmu_qos_update_requirement(PRCMU_QOS_APE_OPP,
 					"ux500-msp-i2s", 100);
 		drvdata->vape_opp_constraint = 1;
diff --git a/sound/soc/ux500/ux500_msp_i2s.c b/sound/soc/ux500/ux500_msp_i2s.c
index 959d7b4edf56..bd5266aca0f1 100644
--- a/sound/soc/ux500/ux500_msp_i2s.c
+++ b/sound/soc/ux500/ux500_msp_i2s.c
@@ -604,7 +604,6 @@ int ux500_msp_i2s_trigger(struct ux500_msp *msp, int cmd, int direction)
 		break;
 	default:
 		return -EINVAL;
-		break;
 	}
 
 	return 0;
diff --git a/sound/soc/zte/Kconfig b/sound/soc/zte/Kconfig
index 6d8a90d36315..75f67a5d23ea 100644
--- a/sound/soc/zte/Kconfig
+++ b/sound/soc/zte/Kconfig
@@ -15,3 +15,11 @@ config ZX_I2S
 	help
 	  Say Y or M if you want to add support for codecs attached to the
 	  ZTE ZX I2S interface
+
+config ZX_TDM
+        tristate "ZTE ZX TDM Driver Support"
+        depends on COMMON_CLK
+        select SND_SOC_GENERIC_DMAENGINE_PCM
+        help
+          Say Y or M if you want to add support for codecs attached to the
+          ZTE ZX TDM interface
diff --git a/sound/soc/zte/Makefile b/sound/soc/zte/Makefile
index 77768f5fd10c..1fc841acdfdd 100644
--- a/sound/soc/zte/Makefile
+++ b/sound/soc/zte/Makefile
@@ -1,2 +1,3 @@
 obj-$(CONFIG_ZX_SPDIF)	+= zx-spdif.o
 obj-$(CONFIG_ZX_I2S)	+= zx-i2s.o
+obj-$(CONFIG_ZX_TDM)	+= zx-tdm.o
diff --git a/sound/soc/zte/zx-tdm.c b/sound/soc/zte/zx-tdm.c
new file mode 100644
index 000000000000..bd632cc503b3
--- /dev/null
+++ b/sound/soc/zte/zx-tdm.c
@@ -0,0 +1,461 @@
+/*
+ * ZTE's TDM driver
+ *
+ * Copyright (C) 2017 ZTE Ltd
+ *
+ * Author: Baoyou Xie <baoyou.xie@linaro.org>
+ *
+ * License terms: GNU General Public License (GPL) version 2
+ */
+
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/mfd/syscon.h>
+#include <linux/module.h>
+#include <sound/dmaengine_pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dai.h>
+
+#define	REG_TIMING_CTRL		0x04
+#define	REG_TX_FIFO_CTRL	0x0C
+#define	REG_RX_FIFO_CTRL	0x10
+#define REG_INT_EN		0x1C
+#define REG_INT_STATUS		0x20
+#define REG_DATABUF		0x24
+#define REG_TS_MASK0		0x44
+#define REG_PROCESS_CTRL	0x54
+
+#define FIFO_CTRL_TX_RST	BIT(0)
+#define FIFO_CTRL_RX_RST	BIT(0)
+#define DEAGULT_FIFO_THRES	GENMASK(4, 2)
+
+#define FIFO_CTRL_TX_DMA_EN	BIT(1)
+#define FIFO_CTRL_RX_DMA_EN	BIT(1)
+
+#define TX_FIFO_RST_MASK	BIT(0)
+#define RX_FIFO_RST_MASK	BIT(0)
+
+#define FIFOCTRL_TX_FIFO_RST	BIT(0)
+#define FIFOCTRL_RX_FIFO_RST	BIT(0)
+
+#define TXTH_MASK		GENMASK(5, 2)
+#define RXTH_MASK		GENMASK(5, 2)
+
+#define FIFOCTRL_THRESHOLD(x)	((x) << 2)
+
+#define TIMING_MS_MASK		BIT(1)
+/*
+ * 00: 8 clk cycles every timeslot
+ * 01: 16 clk cycles every timeslot
+ * 10: 32 clk cycles every timeslot
+ */
+#define TIMING_SYNC_WIDTH_MASK	GENMASK(6, 5)
+#define TIMING_WIDTH_SHIFT      5
+#define TIMING_DEFAULT_WIDTH    0
+#define TIMING_TS_WIDTH(x)	((x) << TIMING_WIDTH_SHIFT)
+#define TIMING_WIDTH_FACTOR     8
+
+#define TIMING_MASTER_MODE	BIT(21)
+#define TIMING_LSB_FIRST	BIT(20)
+#define TIMING_TS_NUM(x)	(((x) - 1) << 7)
+#define TIMING_CLK_SEL_MASK	GENMASK(2, 0)
+#define TIMING_CLK_SEL_DEF	BIT(2)
+
+#define PROCESS_TX_EN		BIT(0)
+#define PROCESS_RX_EN		BIT(1)
+#define PROCESS_TDM_EN		BIT(2)
+#define PROCESS_DISABLE_ALL	0
+
+#define INT_DISABLE_ALL		0
+#define INT_STATUS_MASK		GENMASK(6, 0)
+
+struct zx_tdm_info {
+	struct snd_dmaengine_dai_dma_data	dma_playback;
+	struct snd_dmaengine_dai_dma_data	dma_capture;
+	resource_size_t				phy_addr;
+	void __iomem				*regbase;
+	struct clk				*dai_wclk;
+	struct clk				*dai_pclk;
+	int					master;
+	struct device				*dev;
+};
+
+static inline u32 zx_tdm_readl(struct zx_tdm_info *tdm, u16 reg)
+{
+	return readl_relaxed(tdm->regbase + reg);
+}
+
+static inline void zx_tdm_writel(struct zx_tdm_info *tdm, u16 reg, u32 val)
+{
+	writel_relaxed(val, tdm->regbase + reg);
+}
+
+static void zx_tdm_tx_en(struct zx_tdm_info *tdm, bool on)
+{
+	unsigned long val;
+
+	val = zx_tdm_readl(tdm, REG_PROCESS_CTRL);
+	if (on)
+		val |= PROCESS_TX_EN | PROCESS_TDM_EN;
+	else
+		val &= ~(PROCESS_TX_EN | PROCESS_TDM_EN);
+	zx_tdm_writel(tdm, REG_PROCESS_CTRL, val);
+}
+
+static void zx_tdm_rx_en(struct zx_tdm_info *tdm, bool on)
+{
+	unsigned long val;
+
+	val = zx_tdm_readl(tdm, REG_PROCESS_CTRL);
+	if (on)
+		val |= PROCESS_RX_EN | PROCESS_TDM_EN;
+	else
+		val &= ~(PROCESS_RX_EN | PROCESS_TDM_EN);
+	zx_tdm_writel(tdm, REG_PROCESS_CTRL, val);
+}
+
+static void zx_tdm_tx_dma_en(struct zx_tdm_info *tdm, bool on)
+{
+	unsigned long val;
+
+	val = zx_tdm_readl(tdm, REG_TX_FIFO_CTRL);
+	val |= FIFO_CTRL_TX_RST | DEAGULT_FIFO_THRES;
+	if (on)
+		val |= FIFO_CTRL_TX_DMA_EN;
+	else
+		val &= ~FIFO_CTRL_TX_DMA_EN;
+	zx_tdm_writel(tdm, REG_TX_FIFO_CTRL, val);
+}
+
+static void zx_tdm_rx_dma_en(struct zx_tdm_info *tdm, bool on)
+{
+	unsigned long val;
+
+	val = zx_tdm_readl(tdm, REG_RX_FIFO_CTRL);
+	val |= FIFO_CTRL_RX_RST | DEAGULT_FIFO_THRES;
+	if (on)
+		val |= FIFO_CTRL_RX_DMA_EN;
+	else
+		val &= ~FIFO_CTRL_RX_DMA_EN;
+	zx_tdm_writel(tdm, REG_RX_FIFO_CTRL, val);
+}
+
+#define ZX_TDM_RATES	(SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000)
+
+#define ZX_TDM_FMTBIT \
+	(SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FORMAT_MU_LAW | \
+	SNDRV_PCM_FORMAT_A_LAW)
+
+static int zx_tdm_dai_probe(struct snd_soc_dai *dai)
+{
+	struct zx_tdm_info *zx_tdm = dev_get_drvdata(dai->dev);
+
+	snd_soc_dai_set_drvdata(dai, zx_tdm);
+	zx_tdm->dma_playback.addr = zx_tdm->phy_addr + REG_DATABUF;
+	zx_tdm->dma_playback.maxburst = 16;
+	zx_tdm->dma_capture.addr = zx_tdm->phy_addr + REG_DATABUF;
+	zx_tdm->dma_capture.maxburst = 16;
+	snd_soc_dai_init_dma_data(dai, &zx_tdm->dma_playback,
+				  &zx_tdm->dma_capture);
+	return 0;
+}
+
+static int zx_tdm_set_fmt(struct snd_soc_dai *cpu_dai, unsigned int fmt)
+{
+	struct zx_tdm_info *tdm = snd_soc_dai_get_drvdata(cpu_dai);
+	unsigned long val;
+
+	val = zx_tdm_readl(tdm, REG_TIMING_CTRL);
+	val &= ~(TIMING_SYNC_WIDTH_MASK | TIMING_MS_MASK);
+	val |= TIMING_DEFAULT_WIDTH << TIMING_WIDTH_SHIFT;
+
+	switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+	case SND_SOC_DAIFMT_CBM_CFM:
+		tdm->master = 1;
+		val |= TIMING_MASTER_MODE;
+		break;
+	case SND_SOC_DAIFMT_CBS_CFS:
+		tdm->master = 0;
+		val &= ~TIMING_MASTER_MODE;
+		break;
+	default:
+		dev_err(cpu_dai->dev, "Unknown master/slave format\n");
+		return -EINVAL;
+	}
+
+
+	zx_tdm_writel(tdm, REG_TIMING_CTRL, val);
+
+	return 0;
+}
+
+static int zx_tdm_hw_params(struct snd_pcm_substream *substream,
+			    struct snd_pcm_hw_params *params,
+			    struct snd_soc_dai *socdai)
+{
+	struct zx_tdm_info *tdm = snd_soc_dai_get_drvdata(socdai);
+	struct snd_dmaengine_dai_dma_data *dma_data;
+	unsigned int ts_width = TIMING_DEFAULT_WIDTH;
+	unsigned int ch_num = 32;
+	unsigned int mask = 0;
+	unsigned int ret = 0;
+	unsigned long val;
+
+	dma_data = snd_soc_dai_get_dma_data(socdai, substream);
+	dma_data->addr_width = ch_num >> 3;
+
+	switch (params_format(params)) {
+	case SNDRV_PCM_FORMAT_MU_LAW:
+	case SNDRV_PCM_FORMAT_A_LAW:
+	case SNDRV_PCM_FORMAT_S16_LE:
+		ts_width = 1;
+		break;
+	default:
+		ts_width = 0;
+		dev_err(socdai->dev, "Unknown data format\n");
+		return -EINVAL;
+	}
+
+	val = zx_tdm_readl(tdm, REG_TIMING_CTRL);
+	val |= TIMING_TS_WIDTH(ts_width) | TIMING_TS_NUM(1);
+	zx_tdm_writel(tdm, REG_TIMING_CTRL, val);
+	zx_tdm_writel(tdm, REG_TS_MASK0, mask);
+
+	if (tdm->master)
+		ret = clk_set_rate(tdm->dai_wclk,
+			params_rate(params) * TIMING_WIDTH_FACTOR * ch_num);
+
+	return ret;
+}
+
+static int zx_tdm_trigger(struct snd_pcm_substream *substream, int cmd,
+			  struct snd_soc_dai *dai)
+{
+	int capture = (substream->stream == SNDRV_PCM_STREAM_CAPTURE);
+	struct zx_tdm_info *zx_tdm = dev_get_drvdata(dai->dev);
+	unsigned int val;
+	int ret = 0;
+
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+		if (capture) {
+			val = zx_tdm_readl(zx_tdm, REG_RX_FIFO_CTRL);
+			val |= FIFOCTRL_RX_FIFO_RST;
+			zx_tdm_writel(zx_tdm, REG_RX_FIFO_CTRL, val);
+
+			zx_tdm_rx_dma_en(zx_tdm, true);
+		} else {
+			val = zx_tdm_readl(zx_tdm, REG_TX_FIFO_CTRL);
+			val |= FIFOCTRL_TX_FIFO_RST;
+			zx_tdm_writel(zx_tdm, REG_TX_FIFO_CTRL, val);
+
+			zx_tdm_tx_dma_en(zx_tdm, true);
+		}
+		break;
+	case SNDRV_PCM_TRIGGER_RESUME:
+	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+		if (capture)
+			zx_tdm_rx_en(zx_tdm, true);
+		else
+			zx_tdm_tx_en(zx_tdm, true);
+		break;
+	case SNDRV_PCM_TRIGGER_STOP:
+		if (capture)
+			zx_tdm_rx_dma_en(zx_tdm, false);
+		else
+			zx_tdm_tx_dma_en(zx_tdm, false);
+		break;
+	case SNDRV_PCM_TRIGGER_SUSPEND:
+	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+		if (capture)
+			zx_tdm_rx_en(zx_tdm, false);
+		else
+			zx_tdm_tx_en(zx_tdm, false);
+		break;
+	default:
+		ret = -EINVAL;
+		break;
+	}
+
+	return ret;
+}
+
+static int zx_tdm_startup(struct snd_pcm_substream *substream,
+			  struct snd_soc_dai *dai)
+{
+	struct zx_tdm_info *zx_tdm = dev_get_drvdata(dai->dev);
+	int ret;
+
+	ret = clk_prepare_enable(zx_tdm->dai_wclk);
+	if (ret)
+		return ret;
+
+	ret = clk_prepare_enable(zx_tdm->dai_pclk);
+	if (ret) {
+		clk_disable_unprepare(zx_tdm->dai_wclk);
+		return ret;
+	}
+
+	return 0;
+}
+
+static void zx_tdm_shutdown(struct snd_pcm_substream *substream,
+			    struct snd_soc_dai *dai)
+{
+	struct zx_tdm_info *zx_tdm = dev_get_drvdata(dai->dev);
+
+	clk_disable_unprepare(zx_tdm->dai_pclk);
+	clk_disable_unprepare(zx_tdm->dai_wclk);
+}
+
+static struct snd_soc_dai_ops zx_tdm_dai_ops = {
+	.trigger	= zx_tdm_trigger,
+	.hw_params	= zx_tdm_hw_params,
+	.set_fmt	= zx_tdm_set_fmt,
+	.startup	= zx_tdm_startup,
+	.shutdown	= zx_tdm_shutdown,
+};
+
+static const struct snd_soc_component_driver zx_tdm_component = {
+	.name			= "zx-tdm",
+};
+
+static void zx_tdm_init_state(struct zx_tdm_info *tdm)
+{
+	unsigned int val;
+
+	zx_tdm_writel(tdm, REG_PROCESS_CTRL, PROCESS_DISABLE_ALL);
+
+	val = zx_tdm_readl(tdm, REG_TIMING_CTRL);
+	val |= TIMING_LSB_FIRST;
+	val &= ~TIMING_CLK_SEL_MASK;
+	val |= TIMING_CLK_SEL_DEF;
+	zx_tdm_writel(tdm, REG_TIMING_CTRL, val);
+
+	zx_tdm_writel(tdm, REG_INT_EN, INT_DISABLE_ALL);
+	/*
+	 * write INT_STATUS register to clear it.
+	 */
+	zx_tdm_writel(tdm, REG_INT_STATUS, INT_STATUS_MASK);
+	zx_tdm_writel(tdm, REG_RX_FIFO_CTRL, FIFOCTRL_RX_FIFO_RST);
+	zx_tdm_writel(tdm, REG_TX_FIFO_CTRL, FIFOCTRL_TX_FIFO_RST);
+
+	val = zx_tdm_readl(tdm, REG_RX_FIFO_CTRL);
+	val &= ~(RXTH_MASK | RX_FIFO_RST_MASK);
+	val |= FIFOCTRL_THRESHOLD(8);
+	zx_tdm_writel(tdm, REG_RX_FIFO_CTRL, val);
+
+	val = zx_tdm_readl(tdm, REG_TX_FIFO_CTRL);
+	val &= ~(TXTH_MASK | TX_FIFO_RST_MASK);
+	val |= FIFOCTRL_THRESHOLD(8);
+	zx_tdm_writel(tdm, REG_TX_FIFO_CTRL, val);
+}
+
+static struct snd_soc_dai_driver zx_tdm_dai = {
+	.name	= "zx-tdm-dai",
+	.id	= 0,
+	.probe	= zx_tdm_dai_probe,
+	.playback   = {
+		.channels_min	= 1,
+		.channels_max	= 4,
+		.rates		= ZX_TDM_RATES,
+		.formats	= ZX_TDM_FMTBIT,
+	},
+	.capture = {
+		.channels_min	= 1,
+		.channels_max	= 4,
+		.rates		= ZX_TDM_RATES,
+		.formats	= ZX_TDM_FMTBIT,
+	},
+	.ops	= &zx_tdm_dai_ops,
+};
+
+static int zx_tdm_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct of_phandle_args out_args;
+	unsigned int dma_reg_offset;
+	struct zx_tdm_info *zx_tdm;
+	unsigned int dma_mask;
+	struct resource *res;
+	struct regmap *regmap_sysctrl;
+	int ret;
+
+	zx_tdm = devm_kzalloc(&pdev->dev, sizeof(*zx_tdm), GFP_KERNEL);
+	if (!zx_tdm)
+		return -ENOMEM;
+
+	zx_tdm->dev = dev;
+
+	zx_tdm->dai_wclk = devm_clk_get(&pdev->dev, "wclk");
+	if (IS_ERR(zx_tdm->dai_wclk)) {
+		dev_err(&pdev->dev, "Fail to get wclk\n");
+		return PTR_ERR(zx_tdm->dai_wclk);
+	}
+
+	zx_tdm->dai_pclk = devm_clk_get(&pdev->dev, "pclk");
+	if (IS_ERR(zx_tdm->dai_pclk)) {
+		dev_err(&pdev->dev, "Fail to get pclk\n");
+		return PTR_ERR(zx_tdm->dai_pclk);
+	}
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	zx_tdm->phy_addr = res->start;
+	zx_tdm->regbase = devm_ioremap_resource(&pdev->dev, res);
+	if (IS_ERR(zx_tdm->regbase))
+		return PTR_ERR(zx_tdm->regbase);
+
+	ret = of_parse_phandle_with_fixed_args(pdev->dev.of_node,
+				"zte,tdm-dma-sysctrl", 2, 0, &out_args);
+	if (ret) {
+		dev_err(&pdev->dev, "Fail to get zte,tdm-dma-sysctrl\n");
+		return ret;
+	}
+
+	dma_reg_offset = out_args.args[0];
+	dma_mask = out_args.args[1];
+	regmap_sysctrl = syscon_node_to_regmap(out_args.np);
+	if (IS_ERR(regmap_sysctrl)) {
+		of_node_put(out_args.np);
+		return PTR_ERR(regmap_sysctrl);
+	}
+
+	regmap_update_bits(regmap_sysctrl, dma_reg_offset, dma_mask, dma_mask);
+	of_node_put(out_args.np);
+
+	zx_tdm_init_state(zx_tdm);
+	platform_set_drvdata(pdev, zx_tdm);
+
+	ret = devm_snd_soc_register_component(&pdev->dev, &zx_tdm_component,
+						&zx_tdm_dai, 1);
+	if (ret) {
+		dev_err(&pdev->dev, "Register DAI failed: %d\n", ret);
+		return ret;
+	}
+
+	ret = devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, 0);
+	if (ret)
+		dev_err(&pdev->dev, "Register platform PCM failed: %d\n", ret);
+
+	return ret;
+}
+
+static const struct of_device_id zx_tdm_dt_ids[] = {
+	{ .compatible = "zte,zx296718-tdm", },
+	{}
+};
+MODULE_DEVICE_TABLE(of, zx_tdm_dt_ids);
+
+static struct platform_driver tdm_driver = {
+	.probe = zx_tdm_probe,
+	.driver = {
+		.name = "zx-tdm",
+		.of_match_table = zx_tdm_dt_ids,
+	},
+};
+module_platform_driver(tdm_driver);
+
+MODULE_AUTHOR("Baoyou Xie <baoyou.xie@linaro.org>");
+MODULE_DESCRIPTION("ZTE TDM DAI driver");
+MODULE_LICENSE("GPL v2");