summary refs log tree commit diff
path: root/sound/soc/codecs
diff options
context:
space:
mode:
Diffstat (limited to 'sound/soc/codecs')
-rw-r--r--sound/soc/codecs/Kconfig36
-rw-r--r--sound/soc/codecs/Makefile10
-rw-r--r--sound/soc/codecs/adau1761.c3
-rw-r--r--sound/soc/codecs/adau17x1.c86
-rw-r--r--sound/soc/codecs/adau17x1.h4
-rw-r--r--sound/soc/codecs/cs4265.c12
-rw-r--r--sound/soc/codecs/cs42l51.c21
-rw-r--r--sound/soc/codecs/dmic.c1
-rw-r--r--sound/soc/codecs/es8328.c4
-rw-r--r--sound/soc/codecs/hdac_hda.c483
-rw-r--r--sound/soc/codecs/hdac_hda.h24
-rw-r--r--sound/soc/codecs/hdac_hdmi.c11
-rw-r--r--sound/soc/codecs/max98088.c36
-rw-r--r--sound/soc/codecs/max98373.c47
-rw-r--r--sound/soc/codecs/nau8822.c1136
-rw-r--r--sound/soc/codecs/nau8822.h204
-rw-r--r--sound/soc/codecs/pcm186x.c3
-rw-r--r--sound/soc/codecs/pcm3060-i2c.c60
-rw-r--r--sound/soc/codecs/pcm3060-spi.c59
-rw-r--r--sound/soc/codecs/pcm3060.c295
-rw-r--r--sound/soc/codecs/pcm3060.h88
-rw-r--r--sound/soc/codecs/pcm3168a.c82
-rw-r--r--sound/soc/codecs/rt274.c2
-rw-r--r--sound/soc/codecs/rt5651.c1
-rw-r--r--sound/soc/codecs/rt5663.c7
-rw-r--r--sound/soc/codecs/rt5668.c10
-rw-r--r--sound/soc/codecs/rt5670.c12
-rw-r--r--sound/soc/codecs/rt5677-spi.c1
-rw-r--r--sound/soc/codecs/rt5682.c81
-rw-r--r--sound/soc/codecs/rt5682.h14
-rw-r--r--sound/soc/codecs/sgtl5000.c2
-rw-r--r--sound/soc/codecs/sta32x.c27
-rw-r--r--sound/soc/codecs/tas5720.c103
-rw-r--r--sound/soc/codecs/tas6424.c58
-rw-r--r--sound/soc/codecs/tas6424.h10
-rw-r--r--sound/soc/codecs/tlv320aic31xx.c85
-rw-r--r--sound/soc/codecs/tlv320aic31xx.h23
-rw-r--r--sound/soc/codecs/tscs454.c2
-rw-r--r--sound/soc/codecs/wm2000.c54
-rw-r--r--sound/soc/codecs/wm8782.c63
-rw-r--r--sound/soc/codecs/wm8904.c1
-rw-r--r--sound/soc/codecs/wm8974.c1
-rw-r--r--sound/soc/codecs/wm9712.c3
-rw-r--r--sound/soc/codecs/wm_adsp.c26
44 files changed, 3149 insertions, 142 deletions
diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig
index efb095dbcd71..9cc4f1848c9b 100644
--- a/sound/soc/codecs/Kconfig
+++ b/sound/soc/codecs/Kconfig
@@ -82,6 +82,7 @@ config SND_SOC_ALL_CODECS
 	select SND_SOC_ES7241
 	select SND_SOC_GTM601
 	select SND_SOC_HDAC_HDMI
+	select SND_SOC_HDAC_HDA
 	select SND_SOC_ICS43432
 	select SND_SOC_INNO_RK3036
 	select SND_SOC_ISABELLE if I2C
@@ -109,6 +110,7 @@ config SND_SOC_ALL_CODECS
 	select SND_SOC_MT6351 if MTK_PMIC_WRAP
 	select SND_SOC_NAU8540 if I2C
 	select SND_SOC_NAU8810 if I2C
+	select SND_SOC_NAU8822 if I2C
 	select SND_SOC_NAU8824 if I2C
 	select SND_SOC_NAU8825 if I2C
 	select SND_SOC_HDMI_CODEC
@@ -119,6 +121,8 @@ config SND_SOC_ALL_CODECS
 	select SND_SOC_PCM186X_I2C if I2C
 	select SND_SOC_PCM186X_SPI if SPI_MASTER
 	select SND_SOC_PCM3008
+	select SND_SOC_PCM3060_I2C if I2C
+	select SND_SOC_PCM3060_SPI if SPI_MASTER
 	select SND_SOC_PCM3168A_I2C if I2C
 	select SND_SOC_PCM3168A_SPI if SPI_MASTER
 	select SND_SOC_PCM5102A
@@ -575,7 +579,11 @@ config SND_SOC_DA9055
 	tristate
 
 config SND_SOC_DMIC
-	tristate
+	tristate "Generic Digital Microphone CODEC"
+	depends on GPIOLIB
+	help
+	  Enable support for the Generic Digital Microphone CODEC.
+	  Select this if your sound card has DMICs.
 
 config SND_SOC_HDMI_CODEC
 	tristate
@@ -615,6 +623,10 @@ config SND_SOC_HDAC_HDMI
 	select SND_PCM_ELD
 	select HDMI
 
+config SND_SOC_HDAC_HDA
+	tristate
+	select SND_HDA
+
 config SND_SOC_ICS43432
 	tristate
 
@@ -629,7 +641,8 @@ config SND_SOC_LM49453
 	tristate
 
 config SND_SOC_MAX98088
-       tristate
+	tristate "Maxim MAX98088/9 Low-Power, Stereo Audio Codec"
+	depends on I2C
 
 config SND_SOC_MAX98090
        tristate
@@ -732,6 +745,21 @@ config SND_SOC_PCM186X_SPI
 config SND_SOC_PCM3008
        tristate
 
+config SND_SOC_PCM3060
+       tristate
+
+config SND_SOC_PCM3060_I2C
+	tristate "Texas Instruments PCM3060 CODEC - I2C"
+	depends on I2C
+	select SND_SOC_PCM3060
+	select REGMAP_I2C
+
+config SND_SOC_PCM3060_SPI
+	tristate "Texas Instruments PCM3060 CODEC - SPI"
+	depends on SPI_MASTER
+	select SND_SOC_PCM3060
+	select REGMAP_SPI
+
 config SND_SOC_PCM3168A
 	tristate
 
@@ -1299,6 +1327,10 @@ config SND_SOC_NAU8810
 	tristate "Nuvoton Technology Corporation NAU88C10 CODEC"
 	depends on I2C
 
+config SND_SOC_NAU8822
+	tristate "Nuvoton Technology Corporation NAU88C22 CODEC"
+	depends on I2C
+
 config SND_SOC_NAU8824
 	tristate "Nuvoton Technology Corporation NAU88L24 CODEC"
 	depends on I2C
diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile
index 7ae7c85e8219..8ffab8c8dbfa 100644
--- a/sound/soc/codecs/Makefile
+++ b/sound/soc/codecs/Makefile
@@ -78,6 +78,7 @@ snd-soc-es8328-i2c-objs := es8328-i2c.o
 snd-soc-es8328-spi-objs := es8328-spi.o
 snd-soc-gtm601-objs := gtm601.o
 snd-soc-hdac-hdmi-objs := hdac_hdmi.o
+snd-soc-hdac-hda-objs := hdac_hda.o
 snd-soc-ics43432-objs := ics43432.o
 snd-soc-inno-rk3036-objs := inno_rk3036.o
 snd-soc-isabelle-objs := isabelle.o
@@ -106,6 +107,7 @@ snd-soc-msm8916-digital-objs := msm8916-wcd-digital.o
 snd-soc-mt6351-objs := mt6351.o
 snd-soc-nau8540-objs := nau8540.o
 snd-soc-nau8810-objs := nau8810.o
+snd-soc-nau8822-objs := nau8822.o
 snd-soc-nau8824-objs := nau8824.o
 snd-soc-nau8825-objs := nau8825.o
 snd-soc-hdmi-codec-objs := hdmi-codec.o
@@ -119,6 +121,9 @@ snd-soc-pcm186x-objs := pcm186x.o
 snd-soc-pcm186x-i2c-objs := pcm186x-i2c.o
 snd-soc-pcm186x-spi-objs := pcm186x-spi.o
 snd-soc-pcm3008-objs := pcm3008.o
+snd-soc-pcm3060-objs := pcm3060.o
+snd-soc-pcm3060-i2c-objs := pcm3060-i2c.o
+snd-soc-pcm3060-spi-objs := pcm3060-spi.o
 snd-soc-pcm3168a-objs := pcm3168a.o
 snd-soc-pcm3168a-i2c-objs := pcm3168a-i2c.o
 snd-soc-pcm3168a-spi-objs := pcm3168a-spi.o
@@ -338,6 +343,7 @@ obj-$(CONFIG_SND_SOC_ES8328_I2C)+= snd-soc-es8328-i2c.o
 obj-$(CONFIG_SND_SOC_ES8328_SPI)+= snd-soc-es8328-spi.o
 obj-$(CONFIG_SND_SOC_GTM601)    += snd-soc-gtm601.o
 obj-$(CONFIG_SND_SOC_HDAC_HDMI) += snd-soc-hdac-hdmi.o
+obj-$(CONFIG_SND_SOC_HDAC_HDA) += snd-soc-hdac-hda.o
 obj-$(CONFIG_SND_SOC_ICS43432)	+= snd-soc-ics43432.o
 obj-$(CONFIG_SND_SOC_INNO_RK3036)	+= snd-soc-inno-rk3036.o
 obj-$(CONFIG_SND_SOC_ISABELLE)	+= snd-soc-isabelle.o
@@ -366,6 +372,7 @@ obj-$(CONFIG_SND_SOC_MSM8916_WCD_DIGITAL) +=snd-soc-msm8916-digital.o
 obj-$(CONFIG_SND_SOC_MT6351)	+= snd-soc-mt6351.o
 obj-$(CONFIG_SND_SOC_NAU8540)   += snd-soc-nau8540.o
 obj-$(CONFIG_SND_SOC_NAU8810)   += snd-soc-nau8810.o
+obj-$(CONFIG_SND_SOC_NAU8822)   += snd-soc-nau8822.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
@@ -379,6 +386,9 @@ obj-$(CONFIG_SND_SOC_PCM186X)	+= snd-soc-pcm186x.o
 obj-$(CONFIG_SND_SOC_PCM186X_I2C)	+= snd-soc-pcm186x-i2c.o
 obj-$(CONFIG_SND_SOC_PCM186X_SPI)	+= snd-soc-pcm186x-spi.o
 obj-$(CONFIG_SND_SOC_PCM3008)	+= snd-soc-pcm3008.o
+obj-$(CONFIG_SND_SOC_PCM3060)	+= snd-soc-pcm3060.o
+obj-$(CONFIG_SND_SOC_PCM3060_I2C)	+= snd-soc-pcm3060-i2c.o
+obj-$(CONFIG_SND_SOC_PCM3060_SPI)	+= snd-soc-pcm3060-spi.o
 obj-$(CONFIG_SND_SOC_PCM3168A)	+= snd-soc-pcm3168a.o
 obj-$(CONFIG_SND_SOC_PCM3168A_I2C)	+= snd-soc-pcm3168a-i2c.o
 obj-$(CONFIG_SND_SOC_PCM3168A_SPI)	+= snd-soc-pcm3168a-spi.o
diff --git a/sound/soc/codecs/adau1761.c b/sound/soc/codecs/adau1761.c
index be136e981653..bef3e9e74c26 100644
--- a/sound/soc/codecs/adau1761.c
+++ b/sound/soc/codecs/adau1761.c
@@ -518,7 +518,8 @@ static int adau1761_setup_digmic_jackdetect(struct snd_soc_component *component)
 			ARRAY_SIZE(adau1761_jack_detect_controls));
 		if (ret)
 			return ret;
-	case ADAU1761_DIGMIC_JACKDET_PIN_MODE_NONE: /* fallthrough */
+		/* fall through */
+	case ADAU1761_DIGMIC_JACKDET_PIN_MODE_NONE:
 		ret = snd_soc_dapm_add_routes(dapm, adau1761_no_dmic_routes,
 			ARRAY_SIZE(adau1761_no_dmic_routes));
 		if (ret)
diff --git a/sound/soc/codecs/adau17x1.c b/sound/soc/codecs/adau17x1.c
index 57169b8ff14e..3959e6ad113d 100644
--- a/sound/soc/codecs/adau17x1.c
+++ b/sound/soc/codecs/adau17x1.c
@@ -21,11 +21,18 @@
 #include <linux/i2c.h>
 #include <linux/spi/spi.h>
 #include <linux/regmap.h>
+#include <asm/unaligned.h>
 
 #include "sigmadsp.h"
 #include "adau17x1.h"
 #include "adau-utils.h"
 
+#define ADAU17X1_SAFELOAD_TARGET_ADDRESS 0x0006
+#define ADAU17X1_SAFELOAD_TRIGGER 0x0007
+#define ADAU17X1_SAFELOAD_DATA 0x0001
+#define ADAU17X1_SAFELOAD_DATA_SIZE 20
+#define ADAU17X1_WORD_SIZE 4
+
 static const char * const adau17x1_capture_mixer_boost_text[] = {
 	"Normal operation", "Boost Level 1", "Boost Level 2", "Boost Level 3",
 };
@@ -60,6 +67,9 @@ static const struct snd_kcontrol_new adau17x1_controls[] = {
 	SOC_ENUM("Mic Bias Mode", adau17x1_mic_bias_mode_enum),
 };
 
+static int adau17x1_setup_firmware(struct snd_soc_component *component,
+	unsigned int rate);
+
 static int adau17x1_pll_event(struct snd_soc_dapm_widget *w,
 	struct snd_kcontrol *kcontrol, int event)
 {
@@ -313,7 +323,7 @@ static const struct snd_soc_dapm_route adau17x1_no_dsp_dapm_routes[] = {
 	{ "Capture", NULL, "Right Decimator" },
 };
 
-bool adau17x1_has_dsp(struct adau *adau)
+static bool adau17x1_has_dsp(struct adau *adau)
 {
 	switch (adau->type) {
 	case ADAU1761:
@@ -324,7 +334,17 @@ bool adau17x1_has_dsp(struct adau *adau)
 		return false;
 	}
 }
-EXPORT_SYMBOL_GPL(adau17x1_has_dsp);
+
+static bool adau17x1_has_safeload(struct adau *adau)
+{
+	switch (adau->type) {
+	case ADAU1761:
+	case ADAU1781:
+		return true;
+	default:
+		return false;
+	}
+}
 
 static int adau17x1_set_dai_pll(struct snd_soc_dai *dai, int pll_id,
 	int source, unsigned int freq_in, unsigned int freq_out)
@@ -836,7 +856,7 @@ bool adau17x1_volatile_register(struct device *dev, unsigned int reg)
 }
 EXPORT_SYMBOL_GPL(adau17x1_volatile_register);
 
-int adau17x1_setup_firmware(struct snd_soc_component *component,
+static int adau17x1_setup_firmware(struct snd_soc_component *component,
 	unsigned int rate)
 {
 	int ret;
@@ -880,7 +900,6 @@ err:
 
 	return ret;
 }
-EXPORT_SYMBOL_GPL(adau17x1_setup_firmware);
 
 int adau17x1_add_widgets(struct snd_soc_component *component)
 {
@@ -957,6 +976,56 @@ int adau17x1_resume(struct snd_soc_component *component)
 }
 EXPORT_SYMBOL_GPL(adau17x1_resume);
 
+static int adau17x1_safeload(struct sigmadsp *sigmadsp, unsigned int addr,
+	const uint8_t bytes[], size_t len)
+{
+	uint8_t buf[ADAU17X1_WORD_SIZE];
+	uint8_t data[ADAU17X1_SAFELOAD_DATA_SIZE];
+	unsigned int addr_offset;
+	unsigned int nbr_words;
+	int ret;
+
+	/* write data to safeload addresses. Check if len is not a multiple of
+	 * 4 bytes, if so we need to zero pad.
+	 */
+	nbr_words = len / ADAU17X1_WORD_SIZE;
+	if ((len - nbr_words * ADAU17X1_WORD_SIZE) == 0) {
+		ret = regmap_raw_write(sigmadsp->control_data,
+			ADAU17X1_SAFELOAD_DATA, bytes, len);
+	} else {
+		nbr_words++;
+		memset(data, 0, ADAU17X1_SAFELOAD_DATA_SIZE);
+		memcpy(data, bytes, len);
+		ret = regmap_raw_write(sigmadsp->control_data,
+			ADAU17X1_SAFELOAD_DATA, data,
+			nbr_words * ADAU17X1_WORD_SIZE);
+	}
+
+	if (ret < 0)
+		return ret;
+
+	/* Write target address, target address is offset by 1 */
+	addr_offset = addr - 1;
+	put_unaligned_be32(addr_offset, buf);
+	ret = regmap_raw_write(sigmadsp->control_data,
+		ADAU17X1_SAFELOAD_TARGET_ADDRESS, buf, ADAU17X1_WORD_SIZE);
+	if (ret < 0)
+		return ret;
+
+	/* write nbr of words to trigger address */
+	put_unaligned_be32(nbr_words, buf);
+	ret = regmap_raw_write(sigmadsp->control_data,
+		ADAU17X1_SAFELOAD_TRIGGER, buf, ADAU17X1_WORD_SIZE);
+	if (ret < 0)
+		return ret;
+
+	return 0;
+}
+
+static const struct sigmadsp_ops adau17x1_sigmadsp_ops = {
+	.safeload = adau17x1_safeload,
+};
+
 int adau17x1_probe(struct device *dev, struct regmap *regmap,
 	enum adau17x1_type type, void (*switch_mode)(struct device *dev),
 	const char *firmware_name)
@@ -1002,8 +1071,13 @@ int adau17x1_probe(struct device *dev, struct regmap *regmap,
 	dev_set_drvdata(dev, adau);
 
 	if (firmware_name) {
-		adau->sigmadsp = devm_sigmadsp_init_regmap(dev, regmap, NULL,
-			firmware_name);
+		if (adau17x1_has_safeload(adau)) {
+			adau->sigmadsp = devm_sigmadsp_init_regmap(dev, regmap,
+				&adau17x1_sigmadsp_ops, firmware_name);
+		} else {
+			adau->sigmadsp = devm_sigmadsp_init_regmap(dev, regmap,
+				NULL, firmware_name);
+		}
 		if (IS_ERR(adau->sigmadsp)) {
 			dev_warn(dev, "Could not find firmware file: %ld\n",
 				PTR_ERR(adau->sigmadsp));
diff --git a/sound/soc/codecs/adau17x1.h b/sound/soc/codecs/adau17x1.h
index e6fe87beec07..98a3b6f5bc96 100644
--- a/sound/soc/codecs/adau17x1.h
+++ b/sound/soc/codecs/adau17x1.h
@@ -68,10 +68,6 @@ int adau17x1_resume(struct snd_soc_component *component);
 
 extern const struct snd_soc_dai_ops adau17x1_dai_ops;
 
-int adau17x1_setup_firmware(struct snd_soc_component *component,
-	unsigned int rate);
-bool adau17x1_has_dsp(struct adau *adau);
-
 #define ADAU17X1_CLOCK_CONTROL			0x4000
 #define ADAU17X1_PLL_CONTROL			0x4002
 #define ADAU17X1_REC_POWER_MGMT			0x4009
diff --git a/sound/soc/codecs/cs4265.c b/sound/soc/codecs/cs4265.c
index 407554175282..ab27d2b94d02 100644
--- a/sound/soc/codecs/cs4265.c
+++ b/sound/soc/codecs/cs4265.c
@@ -154,11 +154,11 @@ static const struct snd_kcontrol_new cs4265_snd_controls[] = {
 	SOC_SINGLE("E to F Buffer Disable Switch", CS4265_SPDIF_CTL1,
 				6, 1, 0),
 	SOC_ENUM("C Data Access", cam_mode_enum),
+	SOC_SINGLE("SPDIF Switch", CS4265_SPDIF_CTL2, 5, 1, 1),
 	SOC_SINGLE("Validity Bit Control Switch", CS4265_SPDIF_CTL2,
 				3, 1, 0),
 	SOC_ENUM("SPDIF Mono/Stereo", spdif_mono_stereo_enum),
-	SOC_SINGLE("MMTLR Data Switch", CS4265_SPDIF_CTL2,
-				0, 1, 0),
+	SOC_SINGLE("MMTLR Data Switch", CS4265_SPDIF_CTL2, 0, 1, 0),
 	SOC_ENUM("Mono Channel Select", spdif_mono_select_enum),
 	SND_SOC_BYTES("C Data Buffer", CS4265_C_DATA_BUFF, 24),
 };
@@ -221,10 +221,11 @@ static const struct snd_soc_dapm_route cs4265_audio_map[] = {
 	{"LINEOUTR", NULL, "DAC"},
 	{"SPDIFOUT", NULL, "SPDIF"},
 
+	{"Pre-amp MIC", NULL, "MICL"},
+	{"Pre-amp MIC", NULL, "MICR"},
+	{"ADC Mux", "MIC", "Pre-amp MIC"},
 	{"ADC Mux", "LINEIN", "LINEINL"},
 	{"ADC Mux", "LINEIN", "LINEINR"},
-	{"ADC Mux", "MIC", "MICL"},
-	{"ADC Mux", "MIC", "MICR"},
 	{"ADC", NULL, "ADC Mux"},
 	{"DOUT", NULL, "ADC"},
 	{"DAI1 Capture", NULL, "DOUT"},
@@ -496,7 +497,8 @@ static int cs4265_set_bias_level(struct snd_soc_component *component,
 			SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_192000)
 
 #define CS4265_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_U16_LE | \
-			SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_U24_LE)
+			SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_U24_LE | \
+			SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_U32_LE)
 
 static const struct snd_soc_dai_ops cs4265_ops = {
 	.hw_params	= cs4265_pcm_hw_params,
diff --git a/sound/soc/codecs/cs42l51.c b/sound/soc/codecs/cs42l51.c
index 5080d7a3c279..fd2bd74024c1 100644
--- a/sound/soc/codecs/cs42l51.c
+++ b/sound/soc/codecs/cs42l51.c
@@ -21,6 +21,7 @@
  *  - master mode *NOT* supported
  */
 
+#include <linux/clk.h>
 #include <linux/module.h>
 #include <linux/slab.h>
 #include <sound/core.h>
@@ -41,6 +42,7 @@ enum master_slave_mode {
 
 struct cs42l51_private {
 	unsigned int mclk;
+	struct clk *mclk_handle;
 	unsigned int audio_mode;	/* The mode (I2S or left-justified) */
 	enum master_slave_mode func;
 };
@@ -237,6 +239,10 @@ static const struct snd_soc_dapm_widget cs42l51_dapm_widgets[] = {
 		&cs42l51_adcr_mux_controls),
 };
 
+static const struct snd_soc_dapm_widget cs42l51_dapm_mclk_widgets[] = {
+	SND_SOC_DAPM_CLOCK_SUPPLY("MCLK")
+};
+
 static const struct snd_soc_dapm_route cs42l51_routes[] = {
 	{"HPL", NULL, "Left DAC"},
 	{"HPR", NULL, "Right DAC"},
@@ -487,6 +493,14 @@ static struct snd_soc_dai_driver cs42l51_dai = {
 static int cs42l51_component_probe(struct snd_soc_component *component)
 {
 	int ret, reg;
+	struct snd_soc_dapm_context *dapm;
+	struct cs42l51_private *cs42l51;
+
+	cs42l51 = snd_soc_component_get_drvdata(component);
+	dapm = snd_soc_component_get_dapm(component);
+
+	if (cs42l51->mclk_handle)
+		snd_soc_dapm_new_controls(dapm, cs42l51_dapm_mclk_widgets, 1);
 
 	/*
 	 * DAC configuration
@@ -540,6 +554,13 @@ int cs42l51_probe(struct device *dev, struct regmap *regmap)
 
 	dev_set_drvdata(dev, cs42l51);
 
+	cs42l51->mclk_handle = devm_clk_get(dev, "MCLK");
+	if (IS_ERR(cs42l51->mclk_handle)) {
+		if (PTR_ERR(cs42l51->mclk_handle) != -ENOENT)
+			return PTR_ERR(cs42l51->mclk_handle);
+		cs42l51->mclk_handle = NULL;
+	}
+
 	/* Verify that we have a CS42L51 */
 	ret = regmap_read(regmap, CS42L51_CHIP_REV_ID, &val);
 	if (ret < 0) {
diff --git a/sound/soc/codecs/dmic.c b/sound/soc/codecs/dmic.c
index 8c4926df9286..71322e0410ee 100644
--- a/sound/soc/codecs/dmic.c
+++ b/sound/soc/codecs/dmic.c
@@ -148,6 +148,7 @@ static const struct of_device_id dmic_dev_match[] = {
 	{.compatible = "dmic-codec"},
 	{}
 };
+MODULE_DEVICE_TABLE(of, dmic_dev_match);
 
 static struct platform_driver dmic_driver = {
 	.driver = {
diff --git a/sound/soc/codecs/es8328.c b/sound/soc/codecs/es8328.c
index e9fc2fd97d2f..3aedd609626c 100644
--- a/sound/soc/codecs/es8328.c
+++ b/sound/soc/codecs/es8328.c
@@ -566,14 +566,14 @@ static int es8328_set_sysclk(struct snd_soc_dai *codec_dai,
 		break;
 	case 22579200:
 		mclkdiv2 = 1;
-		/* fallthru */
+		/* fall through */
 	case 11289600:
 		es8328->sysclk_constraints = &constraints_11289;
 		es8328->mclk_ratios = ratios_11289;
 		break;
 	case 24576000:
 		mclkdiv2 = 1;
-		/* fallthru */
+		/* fall through */
 	case 12288000:
 		es8328->sysclk_constraints = &constraints_12288;
 		es8328->mclk_ratios = ratios_12288;
diff --git a/sound/soc/codecs/hdac_hda.c b/sound/soc/codecs/hdac_hda.c
new file mode 100644
index 000000000000..2aaa83028e55
--- /dev/null
+++ b/sound/soc/codecs/hdac_hda.c
@@ -0,0 +1,483 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright(c) 2015-18 Intel Corporation.
+
+/*
+ * hdac_hda.c - ASoC extensions to reuse the legacy HDA codec drivers
+ * with ASoC platform drivers. These APIs are called by the legacy HDA
+ * codec drivers using hdac_ext_bus_ops ops.
+ */
+
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/module.h>
+#include <linux/pm_runtime.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/hdaudio_ext.h>
+#include <sound/hda_codec.h>
+#include <sound/hda_register.h>
+#include "hdac_hda.h"
+
+#define HDAC_ANALOG_DAI_ID		0
+#define HDAC_DIGITAL_DAI_ID		1
+#define HDAC_ALT_ANALOG_DAI_ID		2
+
+#define STUB_FORMATS	(SNDRV_PCM_FMTBIT_S8 | \
+			SNDRV_PCM_FMTBIT_U8 | \
+			SNDRV_PCM_FMTBIT_S16_LE | \
+			SNDRV_PCM_FMTBIT_U16_LE | \
+			SNDRV_PCM_FMTBIT_S24_LE | \
+			SNDRV_PCM_FMTBIT_U24_LE | \
+			SNDRV_PCM_FMTBIT_S32_LE | \
+			SNDRV_PCM_FMTBIT_U32_LE | \
+			SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_LE)
+
+static int hdac_hda_dai_open(struct snd_pcm_substream *substream,
+			     struct snd_soc_dai *dai);
+static void hdac_hda_dai_close(struct snd_pcm_substream *substream,
+			       struct snd_soc_dai *dai);
+static int hdac_hda_dai_prepare(struct snd_pcm_substream *substream,
+				struct snd_soc_dai *dai);
+static int hdac_hda_dai_hw_free(struct snd_pcm_substream *substream,
+				struct snd_soc_dai *dai);
+static int hdac_hda_dai_set_tdm_slot(struct snd_soc_dai *dai,
+				     unsigned int tx_mask, unsigned int rx_mask,
+				     int slots, int slot_width);
+static struct hda_pcm *snd_soc_find_pcm_from_dai(struct hdac_hda_priv *hda_pvt,
+						 struct snd_soc_dai *dai);
+
+static struct snd_soc_dai_ops hdac_hda_dai_ops = {
+	.startup = hdac_hda_dai_open,
+	.shutdown = hdac_hda_dai_close,
+	.prepare = hdac_hda_dai_prepare,
+	.hw_free = hdac_hda_dai_hw_free,
+	.set_tdm_slot = hdac_hda_dai_set_tdm_slot,
+};
+
+static struct snd_soc_dai_driver hdac_hda_dais[] = {
+{
+	.id = HDAC_ANALOG_DAI_ID,
+	.name = "Analog Codec DAI",
+	.ops = &hdac_hda_dai_ops,
+	.playback = {
+		.stream_name	= "Analog Codec Playback",
+		.channels_min	= 1,
+		.channels_max	= 16,
+		.rates		= SNDRV_PCM_RATE_8000_192000,
+		.formats	= STUB_FORMATS,
+		.sig_bits	= 24,
+	},
+	.capture = {
+		.stream_name    = "Analog Codec Capture",
+		.channels_min   = 1,
+		.channels_max   = 16,
+		.rates = SNDRV_PCM_RATE_8000_192000,
+		.formats = STUB_FORMATS,
+		.sig_bits = 24,
+	},
+},
+{
+	.id = HDAC_DIGITAL_DAI_ID,
+	.name = "Digital Codec DAI",
+	.ops = &hdac_hda_dai_ops,
+	.playback = {
+		.stream_name    = "Digital Codec Playback",
+		.channels_min   = 1,
+		.channels_max   = 16,
+		.rates          = SNDRV_PCM_RATE_8000_192000,
+		.formats        = STUB_FORMATS,
+		.sig_bits = 24,
+	},
+	.capture = {
+		.stream_name    = "Digital Codec Capture",
+		.channels_min   = 1,
+		.channels_max   = 16,
+		.rates = SNDRV_PCM_RATE_8000_192000,
+		.formats = STUB_FORMATS,
+		.sig_bits = 24,
+	},
+},
+{
+	.id = HDAC_ALT_ANALOG_DAI_ID,
+	.name = "Alt Analog Codec DAI",
+	.ops = &hdac_hda_dai_ops,
+	.playback = {
+		.stream_name	= "Alt Analog Codec Playback",
+		.channels_min	= 1,
+		.channels_max	= 16,
+		.rates		= SNDRV_PCM_RATE_8000_192000,
+		.formats	= STUB_FORMATS,
+		.sig_bits	= 24,
+	},
+	.capture = {
+		.stream_name    = "Alt Analog Codec Capture",
+		.channels_min   = 1,
+		.channels_max   = 16,
+		.rates = SNDRV_PCM_RATE_8000_192000,
+		.formats = STUB_FORMATS,
+		.sig_bits = 24,
+	},
+}
+
+};
+
+static int hdac_hda_dai_set_tdm_slot(struct snd_soc_dai *dai,
+				     unsigned int tx_mask, unsigned int rx_mask,
+				     int slots, int slot_width)
+{
+	struct snd_soc_component *component = dai->component;
+	struct hdac_hda_priv *hda_pvt;
+	struct hdac_hda_pcm *pcm;
+
+	hda_pvt = snd_soc_component_get_drvdata(component);
+	pcm = &hda_pvt->pcm[dai->id];
+	if (tx_mask)
+		pcm[dai->id].stream_tag[SNDRV_PCM_STREAM_PLAYBACK] = tx_mask;
+	else
+		pcm[dai->id].stream_tag[SNDRV_PCM_STREAM_CAPTURE] = rx_mask;
+
+	return 0;
+}
+
+static int hdac_hda_dai_hw_free(struct snd_pcm_substream *substream,
+				struct snd_soc_dai *dai)
+{
+	struct snd_soc_component *component = dai->component;
+	struct hdac_hda_priv *hda_pvt;
+	struct hda_pcm_stream *hda_stream;
+	struct hda_pcm *pcm;
+
+	hda_pvt = snd_soc_component_get_drvdata(component);
+	pcm = snd_soc_find_pcm_from_dai(hda_pvt, dai);
+	if (!pcm)
+		return -EINVAL;
+
+	hda_stream = &pcm->stream[substream->stream];
+	snd_hda_codec_cleanup(&hda_pvt->codec, hda_stream, substream);
+
+	return 0;
+}
+
+static int hdac_hda_dai_prepare(struct snd_pcm_substream *substream,
+				struct snd_soc_dai *dai)
+{
+	struct snd_soc_component *component = dai->component;
+	struct hdac_hda_priv *hda_pvt;
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct hdac_device *hdev;
+	struct hda_pcm_stream *hda_stream;
+	unsigned int format_val;
+	struct hda_pcm *pcm;
+	unsigned int stream;
+	int ret = 0;
+
+	hda_pvt = snd_soc_component_get_drvdata(component);
+	hdev = &hda_pvt->codec.core;
+	pcm = snd_soc_find_pcm_from_dai(hda_pvt, dai);
+	if (!pcm)
+		return -EINVAL;
+
+	hda_stream = &pcm->stream[substream->stream];
+
+	format_val = snd_hdac_calc_stream_format(runtime->rate,
+						 runtime->channels,
+						 runtime->format,
+						 hda_stream->maxbps,
+						 0);
+	if (!format_val) {
+		dev_err(&hdev->dev,
+			"invalid format_val, rate=%d, ch=%d, format=%d\n",
+			runtime->rate, runtime->channels, runtime->format);
+		return -EINVAL;
+	}
+
+	stream = hda_pvt->pcm[dai->id].stream_tag[substream->stream];
+
+	ret = snd_hda_codec_prepare(&hda_pvt->codec, hda_stream,
+				    stream, format_val, substream);
+	if (ret < 0)
+		dev_err(&hdev->dev, "codec prepare failed %d\n", ret);
+
+	return ret;
+}
+
+static int hdac_hda_dai_open(struct snd_pcm_substream *substream,
+			     struct snd_soc_dai *dai)
+{
+	struct snd_soc_component *component = dai->component;
+	struct hdac_hda_priv *hda_pvt;
+	struct hda_pcm_stream *hda_stream;
+	struct hda_pcm *pcm;
+	int ret;
+
+	hda_pvt = snd_soc_component_get_drvdata(component);
+	pcm = snd_soc_find_pcm_from_dai(hda_pvt, dai);
+	if (!pcm)
+		return -EINVAL;
+
+	snd_hda_codec_pcm_get(pcm);
+
+	hda_stream = &pcm->stream[substream->stream];
+
+	ret = hda_stream->ops.open(hda_stream, &hda_pvt->codec, substream);
+	if (ret < 0)
+		snd_hda_codec_pcm_put(pcm);
+
+	return ret;
+}
+
+static void hdac_hda_dai_close(struct snd_pcm_substream *substream,
+			       struct snd_soc_dai *dai)
+{
+	struct snd_soc_component *component = dai->component;
+	struct hdac_hda_priv *hda_pvt;
+	struct hda_pcm_stream *hda_stream;
+	struct hda_pcm *pcm;
+
+	hda_pvt = snd_soc_component_get_drvdata(component);
+	pcm = snd_soc_find_pcm_from_dai(hda_pvt, dai);
+	if (!pcm)
+		return;
+
+	hda_stream = &pcm->stream[substream->stream];
+
+	hda_stream->ops.close(hda_stream, &hda_pvt->codec, substream);
+
+	snd_hda_codec_pcm_put(pcm);
+}
+
+static struct hda_pcm *snd_soc_find_pcm_from_dai(struct hdac_hda_priv *hda_pvt,
+						 struct snd_soc_dai *dai)
+{
+	struct hda_codec *hcodec = &hda_pvt->codec;
+	struct hda_pcm *cpcm;
+	const char *pcm_name;
+
+	switch (dai->id) {
+	case HDAC_ANALOG_DAI_ID:
+		pcm_name = "Analog";
+		break;
+	case HDAC_DIGITAL_DAI_ID:
+		pcm_name = "Digital";
+		break;
+	case HDAC_ALT_ANALOG_DAI_ID:
+		pcm_name = "Alt Analog";
+		break;
+	default:
+		dev_err(&hcodec->core.dev, "invalid dai id %d\n", dai->id);
+		return NULL;
+	}
+
+	list_for_each_entry(cpcm, &hcodec->pcm_list_head, list) {
+		if (strpbrk(cpcm->name, pcm_name))
+			return cpcm;
+	}
+
+	dev_err(&hcodec->core.dev, "didn't find PCM for DAI %s\n", dai->name);
+	return NULL;
+}
+
+static int hdac_hda_codec_probe(struct snd_soc_component *component)
+{
+	struct hdac_hda_priv *hda_pvt =
+			snd_soc_component_get_drvdata(component);
+	struct snd_soc_dapm_context *dapm =
+			snd_soc_component_get_dapm(component);
+	struct hdac_device *hdev = &hda_pvt->codec.core;
+	struct hda_codec *hcodec = &hda_pvt->codec;
+	struct hdac_ext_link *hlink;
+	hda_codec_patch_t patch;
+	int ret;
+
+	hlink = snd_hdac_ext_bus_get_link(hdev->bus, dev_name(&hdev->dev));
+	if (!hlink) {
+		dev_err(&hdev->dev, "hdac link not found\n");
+		return -EIO;
+	}
+
+	snd_hdac_ext_bus_link_get(hdev->bus, hlink);
+
+	ret = snd_hda_codec_device_new(hcodec->bus, component->card->snd_card,
+				       hdev->addr, hcodec);
+	if (ret < 0) {
+		dev_err(&hdev->dev, "failed to create hda codec %d\n", ret);
+		goto error_no_pm;
+	}
+
+	/*
+	 * snd_hda_codec_device_new decrements the usage count so call get pm
+	 * else the device will be powered off
+	 */
+	pm_runtime_get_noresume(&hdev->dev);
+
+	hcodec->bus->card = dapm->card->snd_card;
+
+	ret = snd_hda_codec_set_name(hcodec, hcodec->preset->name);
+	if (ret < 0) {
+		dev_err(&hdev->dev, "name failed %s\n", hcodec->preset->name);
+		goto error;
+	}
+
+	ret = snd_hdac_regmap_init(&hcodec->core);
+	if (ret < 0) {
+		dev_err(&hdev->dev, "regmap init failed\n");
+		goto error;
+	}
+
+	patch = (hda_codec_patch_t)hcodec->preset->driver_data;
+	if (patch) {
+		ret = patch(hcodec);
+		if (ret < 0) {
+			dev_err(&hdev->dev, "patch failed %d\n", ret);
+			goto error;
+		}
+	} else {
+		dev_dbg(&hdev->dev, "no patch file found\n");
+	}
+
+	ret = snd_hda_codec_parse_pcms(hcodec);
+	if (ret < 0) {
+		dev_err(&hdev->dev, "unable to map pcms to dai %d\n", ret);
+		goto error;
+	}
+
+	ret = snd_hda_codec_build_controls(hcodec);
+	if (ret < 0) {
+		dev_err(&hdev->dev, "unable to create controls %d\n", ret);
+		goto error;
+	}
+
+	hcodec->core.lazy_cache = true;
+
+	/*
+	 * hdac_device core already sets the state to active and calls
+	 * get_noresume. So enable runtime and set the device to suspend.
+	 * pm_runtime_enable is also called during codec registeration
+	 */
+	pm_runtime_put(&hdev->dev);
+	pm_runtime_suspend(&hdev->dev);
+
+	return 0;
+
+error:
+	pm_runtime_put(&hdev->dev);
+error_no_pm:
+	snd_hdac_ext_bus_link_put(hdev->bus, hlink);
+	return ret;
+}
+
+static void hdac_hda_codec_remove(struct snd_soc_component *component)
+{
+	struct hdac_hda_priv *hda_pvt =
+		      snd_soc_component_get_drvdata(component);
+	struct hdac_device *hdev = &hda_pvt->codec.core;
+	struct hdac_ext_link *hlink = NULL;
+
+	hlink = snd_hdac_ext_bus_get_link(hdev->bus, dev_name(&hdev->dev));
+	if (!hlink) {
+		dev_err(&hdev->dev, "hdac link not found\n");
+		return;
+	}
+
+	snd_hdac_ext_bus_link_put(hdev->bus, hlink);
+	pm_runtime_disable(&hdev->dev);
+}
+
+static const struct snd_soc_dapm_route hdac_hda_dapm_routes[] = {
+	{"AIF1TX", NULL, "Codec Input Pin1"},
+	{"AIF2TX", NULL, "Codec Input Pin2"},
+	{"AIF3TX", NULL, "Codec Input Pin3"},
+
+	{"Codec Output Pin1", NULL, "AIF1RX"},
+	{"Codec Output Pin2", NULL, "AIF2RX"},
+	{"Codec Output Pin3", NULL, "AIF3RX"},
+};
+
+static const struct snd_soc_dapm_widget hdac_hda_dapm_widgets[] = {
+	/* Audio Interface */
+	SND_SOC_DAPM_AIF_IN("AIF1RX", "Analog Codec Playback", 0,
+			    SND_SOC_NOPM, 0, 0),
+	SND_SOC_DAPM_AIF_IN("AIF2RX", "Digital Codec Playback", 0,
+			    SND_SOC_NOPM, 0, 0),
+	SND_SOC_DAPM_AIF_IN("AIF3RX", "Alt Analog Codec Playback", 0,
+			    SND_SOC_NOPM, 0, 0),
+	SND_SOC_DAPM_AIF_OUT("AIF1TX", "Analog Codec Capture", 0,
+			     SND_SOC_NOPM, 0, 0),
+	SND_SOC_DAPM_AIF_OUT("AIF2TX", "Digital Codec Capture", 0,
+			     SND_SOC_NOPM, 0, 0),
+	SND_SOC_DAPM_AIF_OUT("AIF3TX", "Alt Analog Codec Capture", 0,
+			     SND_SOC_NOPM, 0, 0),
+
+	/* Input Pins */
+	SND_SOC_DAPM_INPUT("Codec Input Pin1"),
+	SND_SOC_DAPM_INPUT("Codec Input Pin2"),
+	SND_SOC_DAPM_INPUT("Codec Input Pin3"),
+
+	/* Output Pins */
+	SND_SOC_DAPM_OUTPUT("Codec Output Pin1"),
+	SND_SOC_DAPM_OUTPUT("Codec Output Pin2"),
+	SND_SOC_DAPM_OUTPUT("Codec Output Pin3"),
+};
+
+static const struct snd_soc_component_driver hdac_hda_codec = {
+	.probe		= hdac_hda_codec_probe,
+	.remove		= hdac_hda_codec_remove,
+	.idle_bias_on	= false,
+	.dapm_widgets           = hdac_hda_dapm_widgets,
+	.num_dapm_widgets       = ARRAY_SIZE(hdac_hda_dapm_widgets),
+	.dapm_routes            = hdac_hda_dapm_routes,
+	.num_dapm_routes        = ARRAY_SIZE(hdac_hda_dapm_routes),
+};
+
+static int hdac_hda_dev_probe(struct hdac_device *hdev)
+{
+	struct hdac_ext_link *hlink;
+	struct hdac_hda_priv *hda_pvt;
+	int ret;
+
+	/* hold the ref while we probe */
+	hlink = snd_hdac_ext_bus_get_link(hdev->bus, dev_name(&hdev->dev));
+	if (!hlink) {
+		dev_err(&hdev->dev, "hdac link not found\n");
+		return -EIO;
+	}
+	snd_hdac_ext_bus_link_get(hdev->bus, hlink);
+
+	hda_pvt = hdac_to_hda_priv(hdev);
+	if (!hda_pvt)
+		return -ENOMEM;
+
+	/* ASoC specific initialization */
+	ret = devm_snd_soc_register_component(&hdev->dev,
+					 &hdac_hda_codec, hdac_hda_dais,
+					 ARRAY_SIZE(hdac_hda_dais));
+	if (ret < 0) {
+		dev_err(&hdev->dev, "failed to register HDA codec %d\n", ret);
+		return ret;
+	}
+
+	dev_set_drvdata(&hdev->dev, hda_pvt);
+	snd_hdac_ext_bus_link_put(hdev->bus, hlink);
+
+	return ret;
+}
+
+static int hdac_hda_dev_remove(struct hdac_device *hdev)
+{
+	return 0;
+}
+
+static struct hdac_ext_bus_ops hdac_ops = {
+	.hdev_attach = hdac_hda_dev_probe,
+	.hdev_detach = hdac_hda_dev_remove,
+};
+
+struct hdac_ext_bus_ops *snd_soc_hdac_hda_get_ops(void)
+{
+	return &hdac_ops;
+}
+EXPORT_SYMBOL_GPL(snd_soc_hdac_hda_get_ops);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("ASoC Extensions for legacy HDA Drivers");
+MODULE_AUTHOR("Rakesh Ughreja<rakesh.a.ughreja@intel.com>");
diff --git a/sound/soc/codecs/hdac_hda.h b/sound/soc/codecs/hdac_hda.h
new file mode 100644
index 000000000000..e444ef593360
--- /dev/null
+++ b/sound/soc/codecs/hdac_hda.h
@@ -0,0 +1,24 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright(c) 2015-18 Intel Corporation.
+ */
+
+#ifndef __HDAC_HDA_H__
+#define __HDAC_HDA_H__
+
+struct hdac_hda_pcm {
+	int stream_tag[2];
+};
+
+struct hdac_hda_priv {
+	struct hda_codec codec;
+	struct hdac_hda_pcm pcm[2];
+};
+
+#define hdac_to_hda_priv(_hdac) \
+			container_of(_hdac, struct hdac_hda_priv, codec.core)
+#define hdac_to_hda_codec(_hdac) container_of(_hdac, struct hda_codec, core)
+
+struct hdac_ext_bus_ops *snd_soc_hdac_hda_get_ops(void);
+
+#endif /* __HDAC_HDA_H__ */
diff --git a/sound/soc/codecs/hdac_hdmi.c b/sound/soc/codecs/hdac_hdmi.c
index 7b8533abf637..4e9854889a95 100644
--- a/sound/soc/codecs/hdac_hdmi.c
+++ b/sound/soc/codecs/hdac_hdmi.c
@@ -1410,6 +1410,12 @@ static int hdac_hdmi_create_dais(struct hdac_device *hdev,
 		if (ret)
 			return ret;
 
+		/* Filter out 44.1, 88.2 and 176.4Khz */
+		rates &= ~(SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_88200 |
+			   SNDRV_PCM_RATE_176400);
+		if (!rates)
+			return -EINVAL;
+
 		sprintf(dai_name, "intel-hdmi-hifi%d", i+1);
 		hdmi_dais[i].name = devm_kstrdup(&hdev->dev,
 					dai_name, GFP_KERNEL);
@@ -1598,7 +1604,7 @@ static struct snd_pcm *hdac_hdmi_get_pcm_from_id(struct snd_soc_card *card,
 {
 	struct snd_soc_pcm_runtime *rtd;
 
-	list_for_each_entry(rtd, &card->rtd_list, list) {
+	for_each_card_rtds(card, rtd) {
 		if (rtd->pcm && (rtd->pcm->device == device))
 			return rtd->pcm;
 	}
@@ -1961,9 +1967,6 @@ static int hdac_hdmi_get_spk_alloc(struct hdac_device *hdev, int pcm_idx)
 
 	port = list_first_entry(&pcm->port_list, struct hdac_hdmi_port, head);
 
-	if (!port)
-		return 0;
-
 	if (!port || !port->eld.eld_valid)
 		return 0;
 
diff --git a/sound/soc/codecs/max98088.c b/sound/soc/codecs/max98088.c
index fb515aaa54fc..ca172a4b6849 100644
--- a/sound/soc/codecs/max98088.c
+++ b/sound/soc/codecs/max98088.c
@@ -16,6 +16,7 @@
 #include <linux/pm.h>
 #include <linux/i2c.h>
 #include <linux/regmap.h>
+#include <linux/clk.h>
 #include <sound/core.h>
 #include <sound/pcm.h>
 #include <sound/pcm_params.h>
@@ -42,6 +43,7 @@ struct max98088_priv {
 	struct regmap *regmap;
 	enum max98088_type devtype;
 	struct max98088_pdata *pdata;
+	struct clk *mclk;
 	unsigned int sysclk;
 	struct max98088_cdata dai[2];
 	int eq_textcnt;
@@ -1103,6 +1105,11 @@ static int max98088_dai_set_sysclk(struct snd_soc_dai *dai,
        if (freq == max98088->sysclk)
                return 0;
 
+	if (!IS_ERR(max98088->mclk)) {
+		freq = clk_round_rate(max98088->mclk, freq);
+		clk_set_rate(max98088->mclk, freq);
+	}
+
        /* Setup clocks for slave mode, and using the PLL
         * PSCLK = 0x01 (when master clk is 10MHz to 20MHz)
         *         0x02 (when master clk is 20MHz to 30MHz)..
@@ -1310,6 +1317,20 @@ static int max98088_set_bias_level(struct snd_soc_component *component,
 		break;
 
 	case SND_SOC_BIAS_PREPARE:
+		/*
+		 * SND_SOC_BIAS_PREPARE is called while preparing for a
+		 * transition to ON or away from ON. If current bias_level
+		 * is SND_SOC_BIAS_ON, then it is preparing for a transition
+		 * away from ON. Disable the clock in that case, otherwise
+		 * enable it.
+		 */
+		if (!IS_ERR(max98088->mclk)) {
+			if (snd_soc_component_get_bias_level(component) ==
+			    SND_SOC_BIAS_ON)
+				clk_disable_unprepare(max98088->mclk);
+			else
+				clk_prepare_enable(max98088->mclk);
+		}
 		break;
 
 	case SND_SOC_BIAS_STANDBY:
@@ -1725,6 +1746,11 @@ static int max98088_i2c_probe(struct i2c_client *i2c,
        if (IS_ERR(max98088->regmap))
 	       return PTR_ERR(max98088->regmap);
 
+	max98088->mclk = devm_clk_get(&i2c->dev, "mclk");
+	if (IS_ERR(max98088->mclk))
+		if (PTR_ERR(max98088->mclk) == -EPROBE_DEFER)
+			return PTR_ERR(max98088->mclk);
+
        max98088->devtype = id->driver_data;
 
        i2c_set_clientdata(i2c, max98088);
@@ -1742,9 +1768,19 @@ static const struct i2c_device_id max98088_i2c_id[] = {
 };
 MODULE_DEVICE_TABLE(i2c, max98088_i2c_id);
 
+#if defined(CONFIG_OF)
+static const struct of_device_id max98088_of_match[] = {
+	{ .compatible = "maxim,max98088" },
+	{ .compatible = "maxim,max98089" },
+	{ }
+};
+MODULE_DEVICE_TABLE(of, max98088_of_match);
+#endif
+
 static struct i2c_driver max98088_i2c_driver = {
 	.driver = {
 		.name = "max98088",
+		.of_match_table = of_match_ptr(max98088_of_match),
 	},
 	.probe  = max98088_i2c_probe,
 	.id_table = max98088_i2c_id,
diff --git a/sound/soc/codecs/max98373.c b/sound/soc/codecs/max98373.c
index 1093f766d0d2..a09d01318f79 100644
--- a/sound/soc/codecs/max98373.c
+++ b/sound/soc/codecs/max98373.c
@@ -2,6 +2,7 @@
 // Copyright (c) 2017, Maxim Integrated
 
 #include <linux/acpi.h>
+#include <linux/delay.h>
 #include <linux/i2c.h>
 #include <linux/module.h>
 #include <linux/regmap.h>
@@ -454,7 +455,7 @@ SND_SOC_DAPM_SIGGEN("IMON"),
 SND_SOC_DAPM_SIGGEN("FBMON"),
 };
 
-static DECLARE_TLV_DB_SCALE(max98373_digital_tlv, 0, -50, 0);
+static DECLARE_TLV_DB_SCALE(max98373_digital_tlv, -6350, 50, 1);
 static const DECLARE_TLV_DB_RANGE(max98373_spk_tlv,
 	0, 8, TLV_DB_SCALE_ITEM(0, 50, 0),
 	9, 10, TLV_DB_SCALE_ITEM(500, 100, 0),
@@ -470,19 +471,19 @@ static const DECLARE_TLV_DB_RANGE(max98373_dht_spkgain_min_tlv,
 	0, 9, TLV_DB_SCALE_ITEM(800, 100, 0),
 );
 static const DECLARE_TLV_DB_RANGE(max98373_dht_rotation_point_tlv,
-	0, 1, TLV_DB_SCALE_ITEM(-50, -50, 0),
-	2, 7, TLV_DB_SCALE_ITEM(-200, -100, 0),
-	8, 9, TLV_DB_SCALE_ITEM(-1000, -200, 0),
-	10, 11, TLV_DB_SCALE_ITEM(-1500, -300, 0),
-	12, 13, TLV_DB_SCALE_ITEM(-2000, -200, 0),
-	14, 15, TLV_DB_SCALE_ITEM(-2500, -500, 0),
+	0, 1, TLV_DB_SCALE_ITEM(-3000, 500, 0),
+	2, 4, TLV_DB_SCALE_ITEM(-2200, 200, 0),
+	5, 6, TLV_DB_SCALE_ITEM(-1500, 300, 0),
+	7, 9, TLV_DB_SCALE_ITEM(-1000, 200, 0),
+	10, 13, TLV_DB_SCALE_ITEM(-500, 100, 0),
+	14, 15, TLV_DB_SCALE_ITEM(-100, 50, 0),
 );
 static const DECLARE_TLV_DB_RANGE(max98373_limiter_thresh_tlv,
-	0, 15, TLV_DB_SCALE_ITEM(0, -100, 0),
+	0, 15, TLV_DB_SCALE_ITEM(-1500, 100, 0),
 );
 
 static const DECLARE_TLV_DB_RANGE(max98373_bde_gain_tlv,
-	0, 60, TLV_DB_SCALE_ITEM(0, -25, 0),
+	0, 60, TLV_DB_SCALE_ITEM(-1500, 25, 0),
 );
 
 static bool max98373_readable_register(struct device *dev, unsigned int reg)
@@ -604,7 +605,7 @@ SOC_SINGLE("Dither Switch", MAX98373_R203F_AMP_DSP_CFG,
 SOC_SINGLE("DC Blocker Switch", MAX98373_R203F_AMP_DSP_CFG,
 	MAX98373_AMP_DSP_CFG_DCBLK_SHIFT, 1, 0),
 SOC_SINGLE_TLV("Digital Volume", MAX98373_R203D_AMP_DIG_VOL_CTRL,
-	0, 0x7F, 0, max98373_digital_tlv),
+	0, 0x7F, 1, max98373_digital_tlv),
 SOC_SINGLE_TLV("Speaker Volume", MAX98373_R203E_AMP_PATH_GAIN,
 	MAX98373_SPK_DIGI_GAIN_SHIFT, 10, 0, max98373_spk_tlv),
 SOC_SINGLE_TLV("FS Max Volume", MAX98373_R203E_AMP_PATH_GAIN,
@@ -616,7 +617,7 @@ SOC_SINGLE("DHT Switch", MAX98373_R20D4_DHT_EN,
 SOC_SINGLE_TLV("DHT Min Volume", MAX98373_R20D1_DHT_CFG,
 	MAX98373_DHT_SPK_GAIN_MIN_SHIFT, 9, 0, max98373_dht_spkgain_min_tlv),
 SOC_SINGLE_TLV("DHT Rot Pnt Volume", MAX98373_R20D1_DHT_CFG,
-	MAX98373_DHT_ROT_PNT_SHIFT, 15, 0, max98373_dht_rotation_point_tlv),
+	MAX98373_DHT_ROT_PNT_SHIFT, 15, 1, max98373_dht_rotation_point_tlv),
 SOC_SINGLE_TLV("DHT Attack Step Volume", MAX98373_R20D2_DHT_ATTACK_CFG,
 	MAX98373_DHT_ATTACK_STEP_SHIFT, 4, 0, max98373_dht_step_size_tlv),
 SOC_SINGLE_TLV("DHT Release Step Volume", MAX98373_R20D3_DHT_RELEASE_CFG,
@@ -653,29 +654,29 @@ SOC_SINGLE("BDE Hold Time", MAX98373_R2090_BDE_LVL_HOLD, 0, 0xFF, 0),
 SOC_SINGLE("BDE Attack Rate", MAX98373_R2091_BDE_GAIN_ATK_REL_RATE, 4, 0xF, 0),
 SOC_SINGLE("BDE Release Rate", MAX98373_R2091_BDE_GAIN_ATK_REL_RATE, 0, 0xF, 0),
 SOC_SINGLE_TLV("BDE LVL1 Clip Thresh Volume", MAX98373_R20A9_BDE_L1_CFG_2,
-	0, 0x3C, 0, max98373_bde_gain_tlv),
+	0, 0x3C, 1, max98373_bde_gain_tlv),
 SOC_SINGLE_TLV("BDE LVL2 Clip Thresh Volume", MAX98373_R20AC_BDE_L2_CFG_2,
-	0, 0x3C, 0, max98373_bde_gain_tlv),
+	0, 0x3C, 1, max98373_bde_gain_tlv),
 SOC_SINGLE_TLV("BDE LVL3 Clip Thresh Volume", MAX98373_R20AF_BDE_L3_CFG_2,
-	0, 0x3C, 0, max98373_bde_gain_tlv),
+	0, 0x3C, 1, max98373_bde_gain_tlv),
 SOC_SINGLE_TLV("BDE LVL4 Clip Thresh Volume", MAX98373_R20B2_BDE_L4_CFG_2,
-	0, 0x3C, 0, max98373_bde_gain_tlv),
+	0, 0x3C, 1, max98373_bde_gain_tlv),
 SOC_SINGLE_TLV("BDE LVL1 Clip Reduction Volume", MAX98373_R20AA_BDE_L1_CFG_3,
-	0, 0x3C, 0, max98373_bde_gain_tlv),
+	0, 0x3C, 1, max98373_bde_gain_tlv),
 SOC_SINGLE_TLV("BDE LVL2 Clip Reduction Volume", MAX98373_R20AD_BDE_L2_CFG_3,
-	0, 0x3C, 0, max98373_bde_gain_tlv),
+	0, 0x3C, 1, max98373_bde_gain_tlv),
 SOC_SINGLE_TLV("BDE LVL3 Clip Reduction Volume", MAX98373_R20B0_BDE_L3_CFG_3,
-	0, 0x3C, 0, max98373_bde_gain_tlv),
+	0, 0x3C, 1, max98373_bde_gain_tlv),
 SOC_SINGLE_TLV("BDE LVL4 Clip Reduction Volume", MAX98373_R20B3_BDE_L4_CFG_3,
-	0, 0x3C, 0, max98373_bde_gain_tlv),
+	0, 0x3C, 1, max98373_bde_gain_tlv),
 SOC_SINGLE_TLV("BDE LVL1 Limiter Thresh Volume", MAX98373_R20A8_BDE_L1_CFG_1,
-	0, 0xF, 0, max98373_limiter_thresh_tlv),
+	0, 0xF, 1, max98373_limiter_thresh_tlv),
 SOC_SINGLE_TLV("BDE LVL2 Limiter Thresh Volume", MAX98373_R20AB_BDE_L2_CFG_1,
-	0, 0xF, 0, max98373_limiter_thresh_tlv),
+	0, 0xF, 1, max98373_limiter_thresh_tlv),
 SOC_SINGLE_TLV("BDE LVL3 Limiter Thresh Volume", MAX98373_R20AE_BDE_L3_CFG_1,
-	0, 0xF, 0, max98373_limiter_thresh_tlv),
+	0, 0xF, 1, max98373_limiter_thresh_tlv),
 SOC_SINGLE_TLV("BDE LVL4 Limiter Thresh Volume", MAX98373_R20B1_BDE_L4_CFG_1,
-	0, 0xF, 0, max98373_limiter_thresh_tlv),
+	0, 0xF, 1, max98373_limiter_thresh_tlv),
 /* Limiter */
 SOC_SINGLE("Limiter Switch", MAX98373_R20E2_LIMITER_EN,
 	MAX98373_LIMITER_EN_SHIFT, 1, 0),
diff --git a/sound/soc/codecs/nau8822.c b/sound/soc/codecs/nau8822.c
new file mode 100644
index 000000000000..622ce947f134
--- /dev/null
+++ b/sound/soc/codecs/nau8822.c
@@ -0,0 +1,1136 @@
+/*
+ * nau8822.c  --  NAU8822 ALSA Soc Audio Codec driver
+ *
+ * Copyright 2017 Nuvoton Technology Corp.
+ *
+ * Author: David Lin <ctlin0@nuvoton.com>
+ * Co-author: John Hsu <kchsu0@nuvoton.com>
+ * Co-author: Seven Li <wtli@nuvoton.com>
+ *
+ * Based on WM8974.c
+ *
+ * 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/kernel.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/i2c.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/initval.h>
+#include <sound/tlv.h>
+#include <asm/div64.h>
+#include "nau8822.h"
+
+#define NAU_PLL_FREQ_MAX 100000000
+#define NAU_PLL_FREQ_MIN 90000000
+#define NAU_PLL_REF_MAX 33000000
+#define NAU_PLL_REF_MIN 8000000
+#define NAU_PLL_OPTOP_MIN 6
+
+static const int nau8822_mclk_scaler[] = { 10, 15, 20, 30, 40, 60, 80, 120 };
+
+static const struct reg_default nau8822_reg_defaults[] = {
+	{ NAU8822_REG_POWER_MANAGEMENT_1, 0x0000 },
+	{ NAU8822_REG_POWER_MANAGEMENT_2, 0x0000 },
+	{ NAU8822_REG_POWER_MANAGEMENT_3, 0x0000 },
+	{ NAU8822_REG_AUDIO_INTERFACE, 0x0050 },
+	{ NAU8822_REG_COMPANDING_CONTROL, 0x0000 },
+	{ NAU8822_REG_CLOCKING, 0x0140 },
+	{ NAU8822_REG_ADDITIONAL_CONTROL, 0x0000 },
+	{ NAU8822_REG_GPIO_CONTROL, 0x0000 },
+	{ NAU8822_REG_JACK_DETECT_CONTROL_1, 0x0000 },
+	{ NAU8822_REG_DAC_CONTROL, 0x0000 },
+	{ NAU8822_REG_LEFT_DAC_DIGITAL_VOLUME, 0x00ff },
+	{ NAU8822_REG_RIGHT_DAC_DIGITAL_VOLUME, 0x00ff },
+	{ NAU8822_REG_JACK_DETECT_CONTROL_2, 0x0000 },
+	{ NAU8822_REG_ADC_CONTROL, 0x0100 },
+	{ NAU8822_REG_LEFT_ADC_DIGITAL_VOLUME, 0x00ff },
+	{ NAU8822_REG_RIGHT_ADC_DIGITAL_VOLUME, 0x00ff },
+	{ NAU8822_REG_EQ1, 0x012c },
+	{ NAU8822_REG_EQ2, 0x002c },
+	{ NAU8822_REG_EQ3, 0x002c },
+	{ NAU8822_REG_EQ4, 0x002c },
+	{ NAU8822_REG_EQ5, 0x002c },
+	{ NAU8822_REG_DAC_LIMITER_1, 0x0032 },
+	{ NAU8822_REG_DAC_LIMITER_2, 0x0000 },
+	{ NAU8822_REG_NOTCH_FILTER_1, 0x0000 },
+	{ NAU8822_REG_NOTCH_FILTER_2, 0x0000 },
+	{ NAU8822_REG_NOTCH_FILTER_3, 0x0000 },
+	{ NAU8822_REG_NOTCH_FILTER_4, 0x0000 },
+	{ NAU8822_REG_ALC_CONTROL_1, 0x0038 },
+	{ NAU8822_REG_ALC_CONTROL_2, 0x000b },
+	{ NAU8822_REG_ALC_CONTROL_3, 0x0032 },
+	{ NAU8822_REG_NOISE_GATE, 0x0010 },
+	{ NAU8822_REG_PLL_N, 0x0008 },
+	{ NAU8822_REG_PLL_K1, 0x000c },
+	{ NAU8822_REG_PLL_K2, 0x0093 },
+	{ NAU8822_REG_PLL_K3, 0x00e9 },
+	{ NAU8822_REG_3D_CONTROL, 0x0000 },
+	{ NAU8822_REG_RIGHT_SPEAKER_CONTROL, 0x0000 },
+	{ NAU8822_REG_INPUT_CONTROL, 0x0033 },
+	{ NAU8822_REG_LEFT_INP_PGA_CONTROL, 0x0010 },
+	{ NAU8822_REG_RIGHT_INP_PGA_CONTROL, 0x0010 },
+	{ NAU8822_REG_LEFT_ADC_BOOST_CONTROL, 0x0100 },
+	{ NAU8822_REG_RIGHT_ADC_BOOST_CONTROL, 0x0100 },
+	{ NAU8822_REG_OUTPUT_CONTROL, 0x0002 },
+	{ NAU8822_REG_LEFT_MIXER_CONTROL, 0x0001 },
+	{ NAU8822_REG_RIGHT_MIXER_CONTROL, 0x0001 },
+	{ NAU8822_REG_LHP_VOLUME, 0x0039 },
+	{ NAU8822_REG_RHP_VOLUME, 0x0039 },
+	{ NAU8822_REG_LSPKOUT_VOLUME, 0x0039 },
+	{ NAU8822_REG_RSPKOUT_VOLUME, 0x0039 },
+	{ NAU8822_REG_AUX2_MIXER, 0x0001 },
+	{ NAU8822_REG_AUX1_MIXER, 0x0001 },
+	{ NAU8822_REG_POWER_MANAGEMENT_4, 0x0000 },
+	{ NAU8822_REG_LEFT_TIME_SLOT, 0x0000 },
+	{ NAU8822_REG_MISC, 0x0020 },
+	{ NAU8822_REG_RIGHT_TIME_SLOT, 0x0000 },
+	{ NAU8822_REG_DEVICE_REVISION, 0x007f },
+	{ NAU8822_REG_DEVICE_ID, 0x001a },
+	{ NAU8822_REG_DAC_DITHER, 0x0114 },
+	{ NAU8822_REG_ALC_ENHANCE_1, 0x0000 },
+	{ NAU8822_REG_ALC_ENHANCE_2, 0x0000 },
+	{ NAU8822_REG_192KHZ_SAMPLING, 0x0008 },
+	{ NAU8822_REG_MISC_CONTROL, 0x0000 },
+	{ NAU8822_REG_INPUT_TIEOFF, 0x0000 },
+	{ NAU8822_REG_POWER_REDUCTION, 0x0000 },
+	{ NAU8822_REG_AGC_PEAK2PEAK, 0x0000 },
+	{ NAU8822_REG_AGC_PEAK_DETECT, 0x0000 },
+	{ NAU8822_REG_AUTOMUTE_CONTROL, 0x0000 },
+	{ NAU8822_REG_OUTPUT_TIEOFF, 0x0000 },
+};
+
+static bool nau8822_readable_reg(struct device *dev, unsigned int reg)
+{
+	switch (reg) {
+	case NAU8822_REG_RESET ... NAU8822_REG_JACK_DETECT_CONTROL_1:
+	case NAU8822_REG_DAC_CONTROL ... NAU8822_REG_LEFT_ADC_DIGITAL_VOLUME:
+	case NAU8822_REG_RIGHT_ADC_DIGITAL_VOLUME:
+	case NAU8822_REG_EQ1 ... NAU8822_REG_EQ5:
+	case NAU8822_REG_DAC_LIMITER_1 ... NAU8822_REG_DAC_LIMITER_2:
+	case NAU8822_REG_NOTCH_FILTER_1 ... NAU8822_REG_NOTCH_FILTER_4:
+	case NAU8822_REG_ALC_CONTROL_1 ...NAU8822_REG_PLL_K3:
+	case NAU8822_REG_3D_CONTROL:
+	case NAU8822_REG_RIGHT_SPEAKER_CONTROL:
+	case NAU8822_REG_INPUT_CONTROL ... NAU8822_REG_LEFT_ADC_BOOST_CONTROL:
+	case NAU8822_REG_RIGHT_ADC_BOOST_CONTROL ... NAU8822_REG_AUX1_MIXER:
+	case NAU8822_REG_POWER_MANAGEMENT_4 ... NAU8822_REG_DEVICE_ID:
+	case NAU8822_REG_DAC_DITHER:
+	case NAU8822_REG_ALC_ENHANCE_1 ... NAU8822_REG_MISC_CONTROL:
+	case NAU8822_REG_INPUT_TIEOFF ... NAU8822_REG_OUTPUT_TIEOFF:
+		return true;
+	default:
+		return false;
+	}
+}
+
+static bool nau8822_writeable_reg(struct device *dev, unsigned int reg)
+{
+	switch (reg) {
+	case NAU8822_REG_RESET ... NAU8822_REG_JACK_DETECT_CONTROL_1:
+	case NAU8822_REG_DAC_CONTROL ... NAU8822_REG_LEFT_ADC_DIGITAL_VOLUME:
+	case NAU8822_REG_RIGHT_ADC_DIGITAL_VOLUME:
+	case NAU8822_REG_EQ1 ... NAU8822_REG_EQ5:
+	case NAU8822_REG_DAC_LIMITER_1 ... NAU8822_REG_DAC_LIMITER_2:
+	case NAU8822_REG_NOTCH_FILTER_1 ... NAU8822_REG_NOTCH_FILTER_4:
+	case NAU8822_REG_ALC_CONTROL_1 ...NAU8822_REG_PLL_K3:
+	case NAU8822_REG_3D_CONTROL:
+	case NAU8822_REG_RIGHT_SPEAKER_CONTROL:
+	case NAU8822_REG_INPUT_CONTROL ... NAU8822_REG_LEFT_ADC_BOOST_CONTROL:
+	case NAU8822_REG_RIGHT_ADC_BOOST_CONTROL ... NAU8822_REG_AUX1_MIXER:
+	case NAU8822_REG_POWER_MANAGEMENT_4 ... NAU8822_REG_DEVICE_ID:
+	case NAU8822_REG_DAC_DITHER:
+	case NAU8822_REG_ALC_ENHANCE_1 ... NAU8822_REG_MISC_CONTROL:
+	case NAU8822_REG_INPUT_TIEOFF ... NAU8822_REG_OUTPUT_TIEOFF:
+		return true;
+	default:
+		return false;
+	}
+}
+
+static bool nau8822_volatile(struct device *dev, unsigned int reg)
+{
+	switch (reg) {
+	case NAU8822_REG_RESET:
+	case NAU8822_REG_DEVICE_REVISION:
+	case NAU8822_REG_DEVICE_ID:
+	case NAU8822_REG_AGC_PEAK2PEAK:
+	case NAU8822_REG_AGC_PEAK_DETECT:
+	case NAU8822_REG_AUTOMUTE_CONTROL:
+		return true;
+	default:
+		return false;
+	}
+}
+
+/* The EQ parameters get function is to get the 5 band equalizer control.
+ * The regmap raw read can't work here because regmap doesn't provide
+ * value format for value width of 9 bits. Therefore, the driver reads data
+ * from cache and makes value format according to the endianness of
+ * bytes type control element.
+ */
+static int nau8822_eq_get(struct snd_kcontrol *kcontrol,
+	struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_component *component =
+			snd_soc_kcontrol_component(kcontrol);
+	struct soc_bytes_ext *params = (void *)kcontrol->private_value;
+	int i, reg;
+	u16 reg_val, *val;
+
+	val = (u16 *)ucontrol->value.bytes.data;
+	reg = NAU8822_REG_EQ1;
+	for (i = 0; i < params->max / sizeof(u16); i++) {
+		reg_val = snd_soc_component_read32(component, reg + i);
+		/* conversion of 16-bit integers between native CPU format
+		 * and big endian format
+		 */
+		reg_val = cpu_to_be16(reg_val);
+		memcpy(val + i, &reg_val, sizeof(reg_val));
+	}
+
+	return 0;
+}
+
+/* The EQ parameters put function is to make configuration of 5 band equalizer
+ * control. These configuration includes central frequency, equalizer gain,
+ * cut-off frequency, bandwidth control, and equalizer path.
+ * The regmap raw write can't work here because regmap doesn't provide
+ * register and value format for register with address 7 bits and value 9 bits.
+ * Therefore, the driver makes value format according to the endianness of
+ * bytes type control element and writes data to codec.
+ */
+static int nau8822_eq_put(struct snd_kcontrol *kcontrol,
+	struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_component *component =
+			snd_soc_kcontrol_component(kcontrol);
+	struct soc_bytes_ext *params = (void *)kcontrol->private_value;
+	void *data;
+	u16 *val, value;
+	int i, reg, ret;
+
+	data = kmemdup(ucontrol->value.bytes.data,
+		params->max, GFP_KERNEL | GFP_DMA);
+	if (!data)
+		return -ENOMEM;
+
+	val = (u16 *)data;
+	reg = NAU8822_REG_EQ1;
+	for (i = 0; i < params->max / sizeof(u16); i++) {
+		/* conversion of 16-bit integers between native CPU format
+		 * and big endian format
+		 */
+		value = be16_to_cpu(*(val + i));
+		ret = snd_soc_component_write(component, reg + i, value);
+		if (ret) {
+			dev_err(component->dev,
+			    "EQ configuration fail, register: %x ret: %d\n",
+			    reg + i, ret);
+			kfree(data);
+			return ret;
+		}
+	}
+	kfree(data);
+
+	return 0;
+}
+
+static const char * const nau8822_companding[] = {
+	"Off", "NC", "u-law", "A-law"};
+
+static const struct soc_enum nau8822_companding_adc_enum =
+	SOC_ENUM_SINGLE(NAU8822_REG_COMPANDING_CONTROL, NAU8822_ADCCM_SFT,
+		ARRAY_SIZE(nau8822_companding), nau8822_companding);
+
+static const struct soc_enum nau8822_companding_dac_enum =
+	SOC_ENUM_SINGLE(NAU8822_REG_COMPANDING_CONTROL, NAU8822_DACCM_SFT,
+		ARRAY_SIZE(nau8822_companding), nau8822_companding);
+
+static const char * const nau8822_eqmode[] = {"Capture", "Playback"};
+
+static const struct soc_enum nau8822_eqmode_enum =
+	SOC_ENUM_SINGLE(NAU8822_REG_EQ1, NAU8822_EQM_SFT,
+		ARRAY_SIZE(nau8822_eqmode), nau8822_eqmode);
+
+static const char * const nau8822_alc1[] = {"Off", "Right", "Left", "Both"};
+static const char * const nau8822_alc3[] = {"Normal", "Limiter"};
+
+static const struct soc_enum nau8822_alc_enable_enum =
+	SOC_ENUM_SINGLE(NAU8822_REG_ALC_CONTROL_1, NAU8822_ALCEN_SFT,
+		ARRAY_SIZE(nau8822_alc1), nau8822_alc1);
+
+static const struct soc_enum nau8822_alc_mode_enum =
+	SOC_ENUM_SINGLE(NAU8822_REG_ALC_CONTROL_3, NAU8822_ALCM_SFT,
+		ARRAY_SIZE(nau8822_alc3), nau8822_alc3);
+
+static const DECLARE_TLV_DB_SCALE(digital_tlv, -12750, 50, 1);
+static const DECLARE_TLV_DB_SCALE(inpga_tlv, -1200, 75, 0);
+static const DECLARE_TLV_DB_SCALE(spk_tlv, -5700, 100, 0);
+static const DECLARE_TLV_DB_SCALE(pga_boost_tlv, 0, 2000, 0);
+static const DECLARE_TLV_DB_SCALE(boost_tlv, -1500, 300, 1);
+static const DECLARE_TLV_DB_SCALE(limiter_tlv, 0, 100, 0);
+
+static const struct snd_kcontrol_new nau8822_snd_controls[] = {
+	SOC_ENUM("ADC Companding", nau8822_companding_adc_enum),
+	SOC_ENUM("DAC Companding", nau8822_companding_dac_enum),
+
+	SOC_ENUM("EQ Function", nau8822_eqmode_enum),
+	SND_SOC_BYTES_EXT("EQ Parameters", 10,
+		  nau8822_eq_get, nau8822_eq_put),
+
+	SOC_DOUBLE("DAC Inversion Switch",
+		NAU8822_REG_DAC_CONTROL, 0, 1, 1, 0),
+	SOC_DOUBLE_R_TLV("PCM Volume",
+		NAU8822_REG_LEFT_DAC_DIGITAL_VOLUME,
+		NAU8822_REG_RIGHT_DAC_DIGITAL_VOLUME, 0, 255, 0, digital_tlv),
+
+	SOC_SINGLE("High Pass Filter Switch",
+		NAU8822_REG_ADC_CONTROL, 8, 1, 0),
+	SOC_SINGLE("High Pass Cut Off",
+		NAU8822_REG_ADC_CONTROL, 4, 7, 0),
+
+	SOC_DOUBLE("ADC Inversion Switch",
+		NAU8822_REG_ADC_CONTROL, 0, 1, 1, 0),
+	SOC_DOUBLE_R_TLV("ADC Volume",
+		NAU8822_REG_LEFT_ADC_DIGITAL_VOLUME,
+		NAU8822_REG_RIGHT_ADC_DIGITAL_VOLUME, 0, 255, 0, digital_tlv),
+
+	SOC_SINGLE("DAC Limiter Switch",
+		NAU8822_REG_DAC_LIMITER_1, 8, 1, 0),
+	SOC_SINGLE("DAC Limiter Decay",
+		NAU8822_REG_DAC_LIMITER_1, 4, 15, 0),
+	SOC_SINGLE("DAC Limiter Attack",
+		NAU8822_REG_DAC_LIMITER_1, 0, 15, 0),
+	SOC_SINGLE("DAC Limiter Threshold",
+		NAU8822_REG_DAC_LIMITER_2, 4, 7, 0),
+	SOC_SINGLE_TLV("DAC Limiter Volume",
+		NAU8822_REG_DAC_LIMITER_2, 0, 12, 0, limiter_tlv),
+
+	SOC_ENUM("ALC Mode", nau8822_alc_mode_enum),
+	SOC_ENUM("ALC Enable Switch", nau8822_alc_enable_enum),
+	SOC_SINGLE("ALC Min Gain",
+		NAU8822_REG_ALC_CONTROL_1, 0, 7, 0),
+	SOC_SINGLE("ALC Max Gain",
+		NAU8822_REG_ALC_CONTROL_1, 3, 7, 0),
+	SOC_SINGLE("ALC Hold",
+		NAU8822_REG_ALC_CONTROL_2, 4, 10, 0),
+	SOC_SINGLE("ALC Target",
+		NAU8822_REG_ALC_CONTROL_2, 0, 15, 0),
+	SOC_SINGLE("ALC Decay",
+		NAU8822_REG_ALC_CONTROL_3, 4, 10, 0),
+	SOC_SINGLE("ALC Attack",
+		NAU8822_REG_ALC_CONTROL_3, 0, 10, 0),
+	SOC_SINGLE("ALC Noise Gate Switch",
+		NAU8822_REG_NOISE_GATE, 3, 1, 0),
+	SOC_SINGLE("ALC Noise Gate Threshold",
+		NAU8822_REG_NOISE_GATE, 0, 7, 0),
+
+	SOC_DOUBLE_R("PGA ZC Switch",
+		NAU8822_REG_LEFT_INP_PGA_CONTROL,
+		NAU8822_REG_RIGHT_INP_PGA_CONTROL,
+		7, 1, 0),
+	SOC_DOUBLE_R_TLV("PGA Volume",
+		NAU8822_REG_LEFT_INP_PGA_CONTROL,
+		NAU8822_REG_RIGHT_INP_PGA_CONTROL, 0, 63, 0, inpga_tlv),
+
+	SOC_DOUBLE_R("Headphone ZC Switch",
+		NAU8822_REG_LHP_VOLUME,
+		NAU8822_REG_RHP_VOLUME, 7, 1, 0),
+	SOC_DOUBLE_R("Headphone Playback Switch",
+		NAU8822_REG_LHP_VOLUME,
+		NAU8822_REG_RHP_VOLUME, 6, 1, 1),
+	SOC_DOUBLE_R_TLV("Headphone Volume",
+		NAU8822_REG_LHP_VOLUME,
+		NAU8822_REG_RHP_VOLUME,	0, 63, 0, spk_tlv),
+
+	SOC_DOUBLE_R("Speaker ZC Switch",
+		NAU8822_REG_LSPKOUT_VOLUME,
+		NAU8822_REG_RSPKOUT_VOLUME, 7, 1, 0),
+	SOC_DOUBLE_R("Speaker Playback Switch",
+		NAU8822_REG_LSPKOUT_VOLUME,
+		NAU8822_REG_RSPKOUT_VOLUME, 6, 1, 1),
+	SOC_DOUBLE_R_TLV("Speaker Volume",
+		NAU8822_REG_LSPKOUT_VOLUME,
+		NAU8822_REG_RSPKOUT_VOLUME, 0, 63, 0, spk_tlv),
+
+	SOC_DOUBLE_R("AUXOUT Playback Switch",
+		NAU8822_REG_AUX2_MIXER,
+		NAU8822_REG_AUX1_MIXER, 6, 1, 1),
+
+	SOC_DOUBLE_R_TLV("PGA Boost Volume",
+		NAU8822_REG_LEFT_ADC_BOOST_CONTROL,
+		NAU8822_REG_RIGHT_ADC_BOOST_CONTROL, 8, 1, 0, pga_boost_tlv),
+	SOC_DOUBLE_R_TLV("L2/R2 Boost Volume",
+		NAU8822_REG_LEFT_ADC_BOOST_CONTROL,
+		NAU8822_REG_RIGHT_ADC_BOOST_CONTROL, 4, 7, 0, boost_tlv),
+	SOC_DOUBLE_R_TLV("Aux Boost Volume",
+		NAU8822_REG_LEFT_ADC_BOOST_CONTROL,
+		NAU8822_REG_RIGHT_ADC_BOOST_CONTROL, 0, 7, 0, boost_tlv),
+
+	SOC_SINGLE("DAC 128x Oversampling Switch",
+		NAU8822_REG_DAC_CONTROL, 5, 1, 0),
+	SOC_SINGLE("ADC 128x Oversampling Switch",
+		NAU8822_REG_ADC_CONTROL, 5, 1, 0),
+};
+
+/* LMAIN and RMAIN Mixer */
+static const struct snd_kcontrol_new nau8822_left_out_mixer[] = {
+	SOC_DAPM_SINGLE("LINMIX Switch",
+		NAU8822_REG_LEFT_MIXER_CONTROL, 1, 1, 0),
+	SOC_DAPM_SINGLE("LAUX Switch",
+		NAU8822_REG_LEFT_MIXER_CONTROL, 5, 1, 0),
+	SOC_DAPM_SINGLE("LDAC Switch",
+		NAU8822_REG_LEFT_MIXER_CONTROL, 0, 1, 0),
+	SOC_DAPM_SINGLE("RDAC Switch",
+		NAU8822_REG_OUTPUT_CONTROL, 5, 1, 0),
+};
+
+static const struct snd_kcontrol_new nau8822_right_out_mixer[] = {
+	SOC_DAPM_SINGLE("RINMIX Switch",
+		NAU8822_REG_RIGHT_MIXER_CONTROL, 1, 1, 0),
+	SOC_DAPM_SINGLE("RAUX Switch",
+		NAU8822_REG_RIGHT_MIXER_CONTROL, 5, 1, 0),
+	SOC_DAPM_SINGLE("RDAC Switch",
+		NAU8822_REG_RIGHT_MIXER_CONTROL, 0, 1, 0),
+	SOC_DAPM_SINGLE("LDAC Switch",
+		NAU8822_REG_OUTPUT_CONTROL, 6, 1, 0),
+};
+
+/* AUX1 and AUX2 Mixer */
+static const struct snd_kcontrol_new nau8822_auxout1_mixer[] = {
+	SOC_DAPM_SINGLE("RDAC Switch", NAU8822_REG_AUX1_MIXER, 0, 1, 0),
+	SOC_DAPM_SINGLE("RMIX Switch", NAU8822_REG_AUX1_MIXER, 1, 1, 0),
+	SOC_DAPM_SINGLE("RINMIX Switch", NAU8822_REG_AUX1_MIXER, 2, 1, 0),
+	SOC_DAPM_SINGLE("LDAC Switch", NAU8822_REG_AUX1_MIXER, 3, 1, 0),
+	SOC_DAPM_SINGLE("LMIX Switch", NAU8822_REG_AUX1_MIXER, 4, 1, 0),
+};
+
+static const struct snd_kcontrol_new nau8822_auxout2_mixer[] = {
+	SOC_DAPM_SINGLE("LDAC Switch", NAU8822_REG_AUX2_MIXER, 0, 1, 0),
+	SOC_DAPM_SINGLE("LMIX Switch", NAU8822_REG_AUX2_MIXER, 1, 1, 0),
+	SOC_DAPM_SINGLE("LINMIX Switch", NAU8822_REG_AUX2_MIXER, 2, 1, 0),
+	SOC_DAPM_SINGLE("AUX1MIX Output Switch",
+		NAU8822_REG_AUX2_MIXER, 3, 1, 0),
+};
+
+/* Input PGA */
+static const struct snd_kcontrol_new nau8822_left_input_mixer[] = {
+	SOC_DAPM_SINGLE("L2 Switch", NAU8822_REG_INPUT_CONTROL, 2, 1, 0),
+	SOC_DAPM_SINGLE("MicN Switch", NAU8822_REG_INPUT_CONTROL, 1, 1, 0),
+	SOC_DAPM_SINGLE("MicP Switch", NAU8822_REG_INPUT_CONTROL, 0, 1, 0),
+};
+static const struct snd_kcontrol_new nau8822_right_input_mixer[] = {
+	SOC_DAPM_SINGLE("R2 Switch", NAU8822_REG_INPUT_CONTROL, 6, 1, 0),
+	SOC_DAPM_SINGLE("MicN Switch", NAU8822_REG_INPUT_CONTROL, 5, 1, 0),
+	SOC_DAPM_SINGLE("MicP Switch", NAU8822_REG_INPUT_CONTROL, 4, 1, 0),
+};
+
+/* Loopback Switch */
+static const struct snd_kcontrol_new nau8822_loopback =
+	SOC_DAPM_SINGLE("Switch", NAU8822_REG_COMPANDING_CONTROL,
+		NAU8822_ADDAP_SFT, 1, 0);
+
+static int check_mclk_select_pll(struct snd_soc_dapm_widget *source,
+			 struct snd_soc_dapm_widget *sink)
+{
+	struct snd_soc_component *component =
+		snd_soc_dapm_to_component(source->dapm);
+	unsigned int value;
+
+	value = snd_soc_component_read32(component, NAU8822_REG_CLOCKING);
+
+	return (value & NAU8822_CLKM_MASK);
+}
+
+static const struct snd_soc_dapm_widget nau8822_dapm_widgets[] = {
+	SND_SOC_DAPM_DAC("Left DAC", "Left HiFi Playback",
+		NAU8822_REG_POWER_MANAGEMENT_3, 0, 0),
+	SND_SOC_DAPM_DAC("Right DAC", "Right HiFi Playback",
+		NAU8822_REG_POWER_MANAGEMENT_3, 1, 0),
+	SND_SOC_DAPM_ADC("Left ADC", "Left HiFi Capture",
+		NAU8822_REG_POWER_MANAGEMENT_2, 0, 0),
+	SND_SOC_DAPM_ADC("Right ADC", "Right HiFi Capture",
+		NAU8822_REG_POWER_MANAGEMENT_2, 1, 0),
+
+	SOC_MIXER_ARRAY("Left Output Mixer",
+		NAU8822_REG_POWER_MANAGEMENT_3, 2, 0, nau8822_left_out_mixer),
+	SOC_MIXER_ARRAY("Right Output Mixer",
+		NAU8822_REG_POWER_MANAGEMENT_3,	3, 0, nau8822_right_out_mixer),
+	SOC_MIXER_ARRAY("AUX1 Output Mixer",
+		NAU8822_REG_POWER_MANAGEMENT_1, 7, 0, nau8822_auxout1_mixer),
+	SOC_MIXER_ARRAY("AUX2 Output Mixer",
+		NAU8822_REG_POWER_MANAGEMENT_1,	6, 0, nau8822_auxout2_mixer),
+
+	SOC_MIXER_ARRAY("Left Input Mixer",
+		NAU8822_REG_POWER_MANAGEMENT_2,
+		2, 0, nau8822_left_input_mixer),
+	SOC_MIXER_ARRAY("Right Input Mixer",
+		NAU8822_REG_POWER_MANAGEMENT_2,
+		3, 0, nau8822_right_input_mixer),
+
+	SND_SOC_DAPM_PGA("Left Boost Mixer",
+		NAU8822_REG_POWER_MANAGEMENT_2, 4, 0, NULL, 0),
+	SND_SOC_DAPM_PGA("Right Boost Mixer",
+		NAU8822_REG_POWER_MANAGEMENT_2, 5, 0, NULL, 0),
+
+	SND_SOC_DAPM_PGA("Left Capture PGA",
+		NAU8822_REG_LEFT_INP_PGA_CONTROL, 6, 1, NULL, 0),
+	SND_SOC_DAPM_PGA("Right Capture PGA",
+		NAU8822_REG_RIGHT_INP_PGA_CONTROL, 6, 1, NULL, 0),
+
+	SND_SOC_DAPM_PGA("Left Headphone Out",
+		NAU8822_REG_POWER_MANAGEMENT_2, 7, 0, NULL, 0),
+	SND_SOC_DAPM_PGA("Right Headphone Out",
+		NAU8822_REG_POWER_MANAGEMENT_2, 8, 0, NULL, 0),
+
+	SND_SOC_DAPM_PGA("Left Speaker Out",
+		 NAU8822_REG_POWER_MANAGEMENT_3, 6, 0, NULL, 0),
+	SND_SOC_DAPM_PGA("Right Speaker Out",
+		NAU8822_REG_POWER_MANAGEMENT_3,	5, 0, NULL, 0),
+
+	SND_SOC_DAPM_PGA("AUX1 Out",
+		NAU8822_REG_POWER_MANAGEMENT_3,	8, 0, NULL, 0),
+	SND_SOC_DAPM_PGA("AUX2 Out",
+		NAU8822_REG_POWER_MANAGEMENT_3,	7, 0, NULL, 0),
+
+	SND_SOC_DAPM_SUPPLY("Mic Bias",
+		NAU8822_REG_POWER_MANAGEMENT_1,	4, 0, NULL, 0),
+	SND_SOC_DAPM_SUPPLY("PLL",
+		NAU8822_REG_POWER_MANAGEMENT_1,	5, 0, NULL, 0),
+
+	SND_SOC_DAPM_SWITCH("Digital Loopback", SND_SOC_NOPM, 0, 0,
+		&nau8822_loopback),
+
+	SND_SOC_DAPM_INPUT("LMICN"),
+	SND_SOC_DAPM_INPUT("LMICP"),
+	SND_SOC_DAPM_INPUT("RMICN"),
+	SND_SOC_DAPM_INPUT("RMICP"),
+	SND_SOC_DAPM_INPUT("LAUX"),
+	SND_SOC_DAPM_INPUT("RAUX"),
+	SND_SOC_DAPM_INPUT("L2"),
+	SND_SOC_DAPM_INPUT("R2"),
+	SND_SOC_DAPM_OUTPUT("LHP"),
+	SND_SOC_DAPM_OUTPUT("RHP"),
+	SND_SOC_DAPM_OUTPUT("LSPK"),
+	SND_SOC_DAPM_OUTPUT("RSPK"),
+	SND_SOC_DAPM_OUTPUT("AUXOUT1"),
+	SND_SOC_DAPM_OUTPUT("AUXOUT2"),
+};
+
+static const struct snd_soc_dapm_route nau8822_dapm_routes[] = {
+	{"Right DAC", NULL, "PLL", check_mclk_select_pll},
+	{"Left DAC", NULL, "PLL", check_mclk_select_pll},
+
+	/* LMAIN and RMAIN Mixer */
+	{"Right Output Mixer", "LDAC Switch", "Left DAC"},
+	{"Right Output Mixer", "RDAC Switch", "Right DAC"},
+	{"Right Output Mixer", "RAUX Switch", "RAUX"},
+	{"Right Output Mixer", "RINMIX Switch", "Right Boost Mixer"},
+
+	{"Left Output Mixer", "LDAC Switch", "Left DAC"},
+	{"Left Output Mixer", "RDAC Switch", "Right DAC"},
+	{"Left Output Mixer", "LAUX Switch", "LAUX"},
+	{"Left Output Mixer", "LINMIX Switch", "Left Boost Mixer"},
+
+	/* AUX1 and AUX2 Mixer */
+	{"AUX1 Output Mixer", "RDAC Switch", "Right DAC"},
+	{"AUX1 Output Mixer", "RMIX Switch", "Right Output Mixer"},
+	{"AUX1 Output Mixer", "RINMIX Switch", "Right Boost Mixer"},
+	{"AUX1 Output Mixer", "LDAC Switch", "Left DAC"},
+	{"AUX1 Output Mixer", "LMIX Switch", "Left Output Mixer"},
+
+	{"AUX2 Output Mixer", "LDAC Switch", "Left DAC"},
+	{"AUX2 Output Mixer", "LMIX Switch", "Left Output Mixer"},
+	{"AUX2 Output Mixer", "LINMIX Switch", "Left Boost Mixer"},
+	{"AUX2 Output Mixer", "AUX1MIX Output Switch", "AUX1 Output Mixer"},
+
+	/* Outputs */
+	{"Right Headphone Out", NULL, "Right Output Mixer"},
+	{"RHP", NULL, "Right Headphone Out"},
+
+	{"Left Headphone Out", NULL, "Left Output Mixer"},
+	{"LHP", NULL, "Left Headphone Out"},
+
+	{"Right Speaker Out", NULL, "Right Output Mixer"},
+	{"RSPK", NULL, "Right Speaker Out"},
+
+	{"Left Speaker Out", NULL, "Left Output Mixer"},
+	{"LSPK", NULL, "Left Speaker Out"},
+
+	{"AUX1 Out", NULL, "AUX1 Output Mixer"},
+	{"AUX2 Out", NULL, "AUX2 Output Mixer"},
+	{"AUXOUT1", NULL, "AUX1 Out"},
+	{"AUXOUT2", NULL, "AUX2 Out"},
+
+	/* Boost Mixer */
+	{"Right ADC", NULL, "PLL", check_mclk_select_pll},
+	{"Left ADC", NULL, "PLL", check_mclk_select_pll},
+
+	{"Right ADC", NULL, "Right Boost Mixer"},
+
+	{"Right Boost Mixer", NULL, "RAUX"},
+	{"Right Boost Mixer", NULL, "Right Capture PGA"},
+	{"Right Boost Mixer", NULL, "R2"},
+
+	{"Left ADC", NULL, "Left Boost Mixer"},
+
+	{"Left Boost Mixer", NULL, "LAUX"},
+	{"Left Boost Mixer", NULL, "Left Capture PGA"},
+	{"Left Boost Mixer", NULL, "L2"},
+
+	/* Input PGA */
+	{"Right Capture PGA", NULL, "Right Input Mixer"},
+	{"Left Capture PGA", NULL, "Left Input Mixer"},
+
+	/* Enable Microphone Power */
+	{"Right Capture PGA", NULL, "Mic Bias"},
+	{"Left Capture PGA", NULL, "Mic Bias"},
+
+	{"Right Input Mixer", "R2 Switch", "R2"},
+	{"Right Input Mixer", "MicN Switch", "RMICN"},
+	{"Right Input Mixer", "MicP Switch", "RMICP"},
+
+	{"Left Input Mixer", "L2 Switch", "L2"},
+	{"Left Input Mixer", "MicN Switch", "LMICN"},
+	{"Left Input Mixer", "MicP Switch", "LMICP"},
+
+	/* Digital Loopback */
+	{"Digital Loopback", "Switch", "Left ADC"},
+	{"Digital Loopback", "Switch", "Right ADC"},
+	{"Left DAC", NULL, "Digital Loopback"},
+	{"Right DAC", NULL, "Digital Loopback"},
+};
+
+static int nau8822_set_dai_sysclk(struct snd_soc_dai *dai, int clk_id,
+				 unsigned int freq, int dir)
+{
+	struct snd_soc_component *component = dai->component;
+	struct nau8822 *nau8822 = snd_soc_component_get_drvdata(component);
+
+	nau8822->div_id = clk_id;
+	nau8822->sysclk = freq;
+	dev_dbg(component->dev, "master sysclk %dHz, source %s\n", freq,
+		clk_id == NAU8822_CLK_PLL ? "PLL" : "MCLK");
+
+	return 0;
+}
+
+static int nau8822_calc_pll(unsigned int pll_in, unsigned int fs,
+				struct nau8822_pll *pll_param)
+{
+	u64 f2, f2_max, pll_ratio;
+	int i, scal_sel;
+
+	if (pll_in > NAU_PLL_REF_MAX || pll_in < NAU_PLL_REF_MIN)
+		return -EINVAL;
+	f2_max = 0;
+	scal_sel = ARRAY_SIZE(nau8822_mclk_scaler);
+
+	for (i = 0; i < scal_sel; i++) {
+		f2 = 256 * fs * 4 * nau8822_mclk_scaler[i] / 10;
+		if (f2 > NAU_PLL_FREQ_MIN && f2 < NAU_PLL_FREQ_MAX &&
+			f2_max < f2) {
+			f2_max = f2;
+			scal_sel = i;
+		}
+	}
+
+	if (ARRAY_SIZE(nau8822_mclk_scaler) == scal_sel)
+		return -EINVAL;
+	pll_param->mclk_scaler = scal_sel;
+	f2 = f2_max;
+
+	/* Calculate the PLL 4-bit integer input and the PLL 24-bit fractional
+	 * input; round up the 24+4bit.
+	 */
+	pll_ratio = div_u64(f2 << 28, pll_in);
+	pll_param->pre_factor = 0;
+	if (((pll_ratio >> 28) & 0xF) < NAU_PLL_OPTOP_MIN) {
+		pll_ratio <<= 1;
+		pll_param->pre_factor = 1;
+	}
+	pll_param->pll_int = (pll_ratio >> 28) & 0xF;
+	pll_param->pll_frac = ((pll_ratio & 0xFFFFFFF) >> 4);
+
+	return 0;
+}
+
+static int nau8822_config_clkdiv(struct snd_soc_dai *dai, int div, int rate)
+{
+	struct snd_soc_component *component = dai->component;
+	struct nau8822 *nau8822 = snd_soc_component_get_drvdata(component);
+	struct nau8822_pll *pll = &nau8822->pll;
+	int i, sclk, imclk;
+
+	switch (nau8822->div_id) {
+	case NAU8822_CLK_MCLK:
+		/* Configure the master clock prescaler div to make system
+		 * clock to approximate the internal master clock (IMCLK);
+		 * and large or equal to IMCLK.
+		 */
+		div = 0;
+		imclk = rate * 256;
+		for (i = 1; i < ARRAY_SIZE(nau8822_mclk_scaler); i++) {
+			sclk = (nau8822->sysclk * 10) /	nau8822_mclk_scaler[i];
+			if (sclk < imclk)
+				break;
+			div = i;
+		}
+		dev_dbg(component->dev, "master clock prescaler %x for fs %d\n",
+			div, rate);
+
+		/* master clock from MCLK and disable PLL */
+		snd_soc_component_update_bits(component,
+			NAU8822_REG_CLOCKING, NAU8822_MCLKSEL_MASK,
+			(div << NAU8822_MCLKSEL_SFT));
+		snd_soc_component_update_bits(component,
+			NAU8822_REG_CLOCKING, NAU8822_CLKM_MASK,
+			NAU8822_CLKM_MCLK);
+		break;
+
+	case NAU8822_CLK_PLL:
+		/* master clock from PLL and enable PLL */
+		if (pll->mclk_scaler != div) {
+			dev_err(component->dev,
+			"master clock prescaler not meet PLL parameters\n");
+			return -EINVAL;
+		}
+		snd_soc_component_update_bits(component,
+			NAU8822_REG_CLOCKING, NAU8822_MCLKSEL_MASK,
+			(div << NAU8822_MCLKSEL_SFT));
+		snd_soc_component_update_bits(component,
+			NAU8822_REG_CLOCKING, NAU8822_CLKM_MASK,
+			NAU8822_CLKM_PLL);
+		break;
+
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int nau8822_set_pll(struct snd_soc_dai *dai, int pll_id, int source,
+				unsigned int freq_in, unsigned int freq_out)
+{
+	struct snd_soc_component *component = dai->component;
+	struct nau8822 *nau8822 = snd_soc_component_get_drvdata(component);
+	struct nau8822_pll *pll_param = &nau8822->pll;
+	int ret, fs;
+
+	fs = freq_out / 256;
+
+	ret = nau8822_calc_pll(freq_in, fs, pll_param);
+	if (ret < 0) {
+		dev_err(component->dev, "Unsupported input clock %d\n",
+			freq_in);
+		return ret;
+	}
+
+	dev_info(component->dev,
+		"pll_int=%x pll_frac=%x mclk_scaler=%x pre_factor=%x\n",
+		pll_param->pll_int, pll_param->pll_frac,
+		pll_param->mclk_scaler, pll_param->pre_factor);
+
+	snd_soc_component_update_bits(component,
+		NAU8822_REG_PLL_N, NAU8822_PLLMCLK_DIV2 | NAU8822_PLLN_MASK,
+		(pll_param->pre_factor ? NAU8822_PLLMCLK_DIV2 : 0) |
+		pll_param->pll_int);
+	snd_soc_component_write(component,
+		NAU8822_REG_PLL_K1, (pll_param->pll_frac >> NAU8822_PLLK1_SFT) &
+		NAU8822_PLLK1_MASK);
+	snd_soc_component_write(component,
+		NAU8822_REG_PLL_K2, (pll_param->pll_frac >> NAU8822_PLLK2_SFT) &
+		NAU8822_PLLK2_MASK);
+	snd_soc_component_write(component,
+		NAU8822_REG_PLL_K3, pll_param->pll_frac & NAU8822_PLLK3_MASK);
+	snd_soc_component_update_bits(component,
+		NAU8822_REG_CLOCKING, NAU8822_MCLKSEL_MASK,
+		pll_param->mclk_scaler << NAU8822_MCLKSEL_SFT);
+	snd_soc_component_update_bits(component,
+		NAU8822_REG_CLOCKING, NAU8822_CLKM_MASK, NAU8822_CLKM_PLL);
+
+	return 0;
+}
+
+static int nau8822_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+	struct snd_soc_component *component = dai->component;
+	u16 ctrl1_val = 0, ctrl2_val = 0;
+
+	dev_dbg(component->dev, "%s\n", __func__);
+
+	switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+	case SND_SOC_DAIFMT_CBM_CFM:
+		ctrl2_val |= 1;
+		break;
+	case SND_SOC_DAIFMT_CBS_CFS:
+		ctrl2_val &= ~1;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+	case SND_SOC_DAIFMT_I2S:
+		ctrl1_val |= 0x10;
+		break;
+	case SND_SOC_DAIFMT_RIGHT_J:
+		break;
+	case SND_SOC_DAIFMT_LEFT_J:
+		ctrl1_val |= 0x8;
+		break;
+	case SND_SOC_DAIFMT_DSP_A:
+		ctrl1_val |= 0x18;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+	case SND_SOC_DAIFMT_NB_NF:
+		break;
+	case SND_SOC_DAIFMT_IB_IF:
+		ctrl1_val |= 0x180;
+		break;
+	case SND_SOC_DAIFMT_IB_NF:
+		ctrl1_val |= 0x100;
+		break;
+	case SND_SOC_DAIFMT_NB_IF:
+		ctrl1_val |= 0x80;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	snd_soc_component_update_bits(component,
+		NAU8822_REG_AUDIO_INTERFACE,
+		NAU8822_AIFMT_MASK | NAU8822_LRP_MASK | NAU8822_BCLKP_MASK,
+		ctrl1_val);
+	snd_soc_component_update_bits(component,
+		NAU8822_REG_CLOCKING, NAU8822_CLKIOEN_MASK, ctrl2_val);
+
+	return 0;
+}
+
+static int nau8822_hw_params(struct snd_pcm_substream *substream,
+			    struct snd_pcm_hw_params *params,
+			    struct snd_soc_dai *dai)
+{
+	struct snd_soc_component *component = dai->component;
+	struct nau8822 *nau8822 = snd_soc_component_get_drvdata(component);
+	int val_len = 0, val_rate = 0;
+
+	switch (params_format(params)) {
+	case SNDRV_PCM_FORMAT_S16_LE:
+		break;
+	case SNDRV_PCM_FORMAT_S20_3LE:
+		val_len |= NAU8822_WLEN_20;
+		break;
+	case SNDRV_PCM_FORMAT_S24_LE:
+		val_len |= NAU8822_WLEN_24;
+		break;
+	case SNDRV_PCM_FORMAT_S32_LE:
+		val_len |= NAU8822_WLEN_32;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	switch (params_rate(params)) {
+	case 8000:
+		val_rate |= NAU8822_SMPLR_8K;
+		break;
+	case 11025:
+		val_rate |= NAU8822_SMPLR_12K;
+		break;
+	case 16000:
+		val_rate |= NAU8822_SMPLR_16K;
+		break;
+	case 22050:
+		val_rate |= NAU8822_SMPLR_24K;
+		break;
+	case 32000:
+		val_rate |= NAU8822_SMPLR_32K;
+		break;
+	case 44100:
+	case 48000:
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	snd_soc_component_update_bits(component,
+		NAU8822_REG_AUDIO_INTERFACE, NAU8822_WLEN_MASK, val_len);
+	snd_soc_component_update_bits(component,
+		NAU8822_REG_ADDITIONAL_CONTROL, NAU8822_SMPLR_MASK, val_rate);
+
+	/* If the master clock is from MCLK, provide the runtime FS for driver
+	 * to get the master clock prescaler configuration.
+	 */
+	if (nau8822->div_id == NAU8822_CLK_MCLK)
+		nau8822_config_clkdiv(dai, 0, params_rate(params));
+
+	return 0;
+}
+
+static int nau8822_mute(struct snd_soc_dai *dai, int mute)
+{
+	struct snd_soc_component *component = dai->component;
+
+	dev_dbg(component->dev, "%s: %d\n", __func__, mute);
+
+	if (mute)
+		snd_soc_component_update_bits(component,
+			NAU8822_REG_DAC_CONTROL, 0x40, 0x40);
+	else
+		snd_soc_component_update_bits(component,
+			NAU8822_REG_DAC_CONTROL, 0x40, 0);
+
+	return 0;
+}
+
+static int nau8822_set_bias_level(struct snd_soc_component *component,
+				 enum snd_soc_bias_level level)
+{
+	switch (level) {
+	case SND_SOC_BIAS_ON:
+	case SND_SOC_BIAS_PREPARE:
+		snd_soc_component_update_bits(component,
+			NAU8822_REG_POWER_MANAGEMENT_1,
+			NAU8822_REFIMP_MASK, NAU8822_REFIMP_80K);
+		break;
+
+	case SND_SOC_BIAS_STANDBY:
+		snd_soc_component_update_bits(component,
+			NAU8822_REG_POWER_MANAGEMENT_1,
+			NAU8822_IOBUF_EN | NAU8822_ABIAS_EN,
+			NAU8822_IOBUF_EN | NAU8822_ABIAS_EN);
+
+		if (snd_soc_component_get_bias_level(component) ==
+			SND_SOC_BIAS_OFF) {
+			snd_soc_component_update_bits(component,
+				NAU8822_REG_POWER_MANAGEMENT_1,
+				NAU8822_REFIMP_MASK, NAU8822_REFIMP_3K);
+			mdelay(100);
+		}
+		snd_soc_component_update_bits(component,
+			NAU8822_REG_POWER_MANAGEMENT_1,
+			NAU8822_REFIMP_MASK, NAU8822_REFIMP_300K);
+		break;
+
+	case SND_SOC_BIAS_OFF:
+		snd_soc_component_write(component,
+			NAU8822_REG_POWER_MANAGEMENT_1, 0);
+		snd_soc_component_write(component,
+			NAU8822_REG_POWER_MANAGEMENT_2, 0);
+		snd_soc_component_write(component,
+			NAU8822_REG_POWER_MANAGEMENT_3, 0);
+		break;
+	}
+
+	dev_dbg(component->dev, "%s: %d\n", __func__, level);
+
+	return 0;
+}
+
+#define NAU8822_RATES (SNDRV_PCM_RATE_8000_48000)
+
+#define NAU8822_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \
+	SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
+
+static const struct snd_soc_dai_ops nau8822_dai_ops = {
+	.hw_params	= nau8822_hw_params,
+	.digital_mute	= nau8822_mute,
+	.set_fmt	= nau8822_set_dai_fmt,
+	.set_sysclk	= nau8822_set_dai_sysclk,
+	.set_pll	= nau8822_set_pll,
+};
+
+static struct snd_soc_dai_driver nau8822_dai = {
+	.name = "nau8822-hifi",
+	.playback = {
+		.stream_name = "Playback",
+		.channels_min = 1,
+		.channels_max = 2,
+		.rates = NAU8822_RATES,
+		.formats = NAU8822_FORMATS,
+	},
+	.capture = {
+		.stream_name = "Capture",
+		.channels_min = 1,
+		.channels_max = 2,
+		.rates = NAU8822_RATES,
+		.formats = NAU8822_FORMATS,
+	},
+	.ops = &nau8822_dai_ops,
+	.symmetric_rates = 1,
+};
+
+static int nau8822_suspend(struct snd_soc_component *component)
+{
+	struct nau8822 *nau8822 = snd_soc_component_get_drvdata(component);
+
+	snd_soc_component_force_bias_level(component, SND_SOC_BIAS_OFF);
+
+	regcache_mark_dirty(nau8822->regmap);
+
+	return 0;
+}
+
+static int nau8822_resume(struct snd_soc_component *component)
+{
+	struct nau8822 *nau8822 = snd_soc_component_get_drvdata(component);
+
+	regcache_sync(nau8822->regmap);
+
+	snd_soc_component_force_bias_level(component, SND_SOC_BIAS_STANDBY);
+
+	return 0;
+}
+
+/*
+ * These registers contain an "update" bit - bit 8. This means, for example,
+ * that one can write new DAC digital volume for both channels, but only when
+ * the update bit is set, will also the volume be updated - simultaneously for
+ * both channels.
+ */
+static const int update_reg[] = {
+	NAU8822_REG_LEFT_DAC_DIGITAL_VOLUME,
+	NAU8822_REG_RIGHT_DAC_DIGITAL_VOLUME,
+	NAU8822_REG_LEFT_ADC_DIGITAL_VOLUME,
+	NAU8822_REG_RIGHT_ADC_DIGITAL_VOLUME,
+	NAU8822_REG_LEFT_INP_PGA_CONTROL,
+	NAU8822_REG_RIGHT_INP_PGA_CONTROL,
+	NAU8822_REG_LHP_VOLUME,
+	NAU8822_REG_RHP_VOLUME,
+	NAU8822_REG_LSPKOUT_VOLUME,
+	NAU8822_REG_RSPKOUT_VOLUME,
+};
+
+static int nau8822_probe(struct snd_soc_component *component)
+{
+	int i;
+
+	/*
+	 * Set the update bit in all registers, that have one. This way all
+	 * writes to those registers will also cause the update bit to be
+	 * written.
+	 */
+	for (i = 0; i < ARRAY_SIZE(update_reg); i++)
+		snd_soc_component_update_bits(component,
+			update_reg[i], 0x100, 0x100);
+
+	return 0;
+}
+
+static const struct snd_soc_component_driver soc_component_dev_nau8822 = {
+	.probe				= nau8822_probe,
+	.suspend			= nau8822_suspend,
+	.resume				= nau8822_resume,
+	.set_bias_level			= nau8822_set_bias_level,
+	.controls			= nau8822_snd_controls,
+	.num_controls			= ARRAY_SIZE(nau8822_snd_controls),
+	.dapm_widgets			= nau8822_dapm_widgets,
+	.num_dapm_widgets		= ARRAY_SIZE(nau8822_dapm_widgets),
+	.dapm_routes			= nau8822_dapm_routes,
+	.num_dapm_routes		= ARRAY_SIZE(nau8822_dapm_routes),
+	.idle_bias_on			= 1,
+	.use_pmdown_time		= 1,
+	.endianness			= 1,
+	.non_legacy_dai_naming		= 1,
+};
+
+static const struct regmap_config nau8822_regmap_config = {
+	.reg_bits = 7,
+	.val_bits = 9,
+
+	.max_register = NAU8822_REG_MAX_REGISTER,
+	.volatile_reg = nau8822_volatile,
+
+	.readable_reg = nau8822_readable_reg,
+	.writeable_reg = nau8822_writeable_reg,
+
+	.cache_type = REGCACHE_RBTREE,
+	.reg_defaults = nau8822_reg_defaults,
+	.num_reg_defaults = ARRAY_SIZE(nau8822_reg_defaults),
+};
+
+static int nau8822_i2c_probe(struct i2c_client *i2c,
+			    const struct i2c_device_id *id)
+{
+	struct device *dev = &i2c->dev;
+	struct nau8822 *nau8822 = dev_get_platdata(dev);
+	int ret;
+
+	if (!nau8822) {
+		nau8822 = devm_kzalloc(dev, sizeof(*nau8822), GFP_KERNEL);
+		if (nau8822 == NULL)
+			return -ENOMEM;
+	}
+	i2c_set_clientdata(i2c, nau8822);
+
+	nau8822->regmap = devm_regmap_init_i2c(i2c, &nau8822_regmap_config);
+	if (IS_ERR(nau8822->regmap)) {
+		ret = PTR_ERR(nau8822->regmap);
+		dev_err(&i2c->dev, "Failed to allocate regmap: %d\n", ret);
+		return ret;
+	}
+	nau8822->dev = dev;
+
+	/* Reset the codec */
+	ret = regmap_write(nau8822->regmap, NAU8822_REG_RESET, 0x00);
+	if (ret != 0) {
+		dev_err(&i2c->dev, "Failed to issue reset: %d\n", ret);
+		return ret;
+	}
+
+	ret = devm_snd_soc_register_component(dev, &soc_component_dev_nau8822,
+						&nau8822_dai, 1);
+	if (ret != 0) {
+		dev_err(&i2c->dev, "Failed to register CODEC: %d\n", ret);
+		return ret;
+	}
+
+	return 0;
+}
+
+static const struct i2c_device_id nau8822_i2c_id[] = {
+	{ "nau8822", 0 },
+	{ }
+};
+MODULE_DEVICE_TABLE(i2c, nau8822_i2c_id);
+
+#ifdef CONFIG_OF
+static const struct of_device_id nau8822_of_match[] = {
+	{ .compatible = "nuvoton,nau8822", },
+	{ }
+};
+MODULE_DEVICE_TABLE(of, nau8822_of_match);
+#endif
+
+static struct i2c_driver nau8822_i2c_driver = {
+	.driver = {
+		.name = "nau8822",
+		.of_match_table = of_match_ptr(nau8822_of_match),
+	},
+	.probe =    nau8822_i2c_probe,
+	.id_table = nau8822_i2c_id,
+};
+module_i2c_driver(nau8822_i2c_driver);
+
+MODULE_DESCRIPTION("ASoC NAU8822 codec driver");
+MODULE_AUTHOR("David Lin <ctlin0@nuvoton.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/nau8822.h b/sound/soc/codecs/nau8822.h
new file mode 100644
index 000000000000..aa79c969cd44
--- /dev/null
+++ b/sound/soc/codecs/nau8822.h
@@ -0,0 +1,204 @@
+/*
+ * nau8822.h  --  NAU8822 Soc Audio Codec driver
+ *
+ * Author: David Lin <ctlin0@nuvoton.com>
+ * Co-author: John Hsu <kchsu0@nuvoton.com>
+ * Co-author: Seven Li <wtli@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 __NAU8822_H__
+#define __NAU8822_H__
+
+#define NAU8822_REG_RESET			0x00
+#define NAU8822_REG_POWER_MANAGEMENT_1		0x01
+#define NAU8822_REG_POWER_MANAGEMENT_2		0x02
+#define NAU8822_REG_POWER_MANAGEMENT_3		0x03
+#define NAU8822_REG_AUDIO_INTERFACE		0x04
+#define NAU8822_REG_COMPANDING_CONTROL		0x05
+#define NAU8822_REG_CLOCKING			0x06
+#define NAU8822_REG_ADDITIONAL_CONTROL		0x07
+#define NAU8822_REG_GPIO_CONTROL		0x08
+#define NAU8822_REG_JACK_DETECT_CONTROL_1	0x09
+#define NAU8822_REG_DAC_CONTROL			0x0A
+#define NAU8822_REG_LEFT_DAC_DIGITAL_VOLUME	0x0B
+#define NAU8822_REG_RIGHT_DAC_DIGITAL_VOLUME	0x0C
+#define NAU8822_REG_JACK_DETECT_CONTROL_2	0x0D
+#define NAU8822_REG_ADC_CONTROL			0x0E
+#define NAU8822_REG_LEFT_ADC_DIGITAL_VOLUME	0x0F
+#define NAU8822_REG_RIGHT_ADC_DIGITAL_VOLUME	0x10
+#define NAU8822_REG_EQ1				0x12
+#define NAU8822_REG_EQ2				0x13
+#define NAU8822_REG_EQ3				0x14
+#define NAU8822_REG_EQ4				0x15
+#define NAU8822_REG_EQ5				0x16
+#define NAU8822_REG_DAC_LIMITER_1		0x18
+#define NAU8822_REG_DAC_LIMITER_2		0x19
+#define NAU8822_REG_NOTCH_FILTER_1		0x1B
+#define NAU8822_REG_NOTCH_FILTER_2		0x1C
+#define NAU8822_REG_NOTCH_FILTER_3		0x1D
+#define NAU8822_REG_NOTCH_FILTER_4		0x1E
+#define NAU8822_REG_ALC_CONTROL_1		0x20
+#define NAU8822_REG_ALC_CONTROL_2		0x21
+#define NAU8822_REG_ALC_CONTROL_3		0x22
+#define NAU8822_REG_NOISE_GATE			0x23
+#define NAU8822_REG_PLL_N			0x24
+#define NAU8822_REG_PLL_K1			0x25
+#define NAU8822_REG_PLL_K2			0x26
+#define NAU8822_REG_PLL_K3			0x27
+#define NAU8822_REG_3D_CONTROL			0x29
+#define NAU8822_REG_RIGHT_SPEAKER_CONTROL	0x2B
+#define NAU8822_REG_INPUT_CONTROL		0x2C
+#define NAU8822_REG_LEFT_INP_PGA_CONTROL	0x2D
+#define NAU8822_REG_RIGHT_INP_PGA_CONTROL	0x2E
+#define NAU8822_REG_LEFT_ADC_BOOST_CONTROL	0x2F
+#define NAU8822_REG_RIGHT_ADC_BOOST_CONTROL	0x30
+#define NAU8822_REG_OUTPUT_CONTROL		0x31
+#define NAU8822_REG_LEFT_MIXER_CONTROL		0x32
+#define NAU8822_REG_RIGHT_MIXER_CONTROL		0x33
+#define NAU8822_REG_LHP_VOLUME			0x34
+#define NAU8822_REG_RHP_VOLUME			0x35
+#define NAU8822_REG_LSPKOUT_VOLUME		0x36
+#define NAU8822_REG_RSPKOUT_VOLUME		0x37
+#define NAU8822_REG_AUX2_MIXER			0x38
+#define NAU8822_REG_AUX1_MIXER			0x39
+#define NAU8822_REG_POWER_MANAGEMENT_4		0x3A
+#define NAU8822_REG_LEFT_TIME_SLOT		0x3B
+#define NAU8822_REG_MISC			0x3C
+#define NAU8822_REG_RIGHT_TIME_SLOT		0x3D
+#define NAU8822_REG_DEVICE_REVISION		0x3E
+#define NAU8822_REG_DEVICE_ID			0x3F
+#define NAU8822_REG_DAC_DITHER			0x41
+#define NAU8822_REG_ALC_ENHANCE_1		0x46
+#define NAU8822_REG_ALC_ENHANCE_2		0x47
+#define NAU8822_REG_192KHZ_SAMPLING		0x48
+#define NAU8822_REG_MISC_CONTROL		0x49
+#define NAU8822_REG_INPUT_TIEOFF		0x4A
+#define NAU8822_REG_POWER_REDUCTION		0x4B
+#define NAU8822_REG_AGC_PEAK2PEAK		0x4C
+#define NAU8822_REG_AGC_PEAK_DETECT		0x4D
+#define NAU8822_REG_AUTOMUTE_CONTROL		0x4E
+#define NAU8822_REG_OUTPUT_TIEOFF		0x4F
+#define NAU8822_REG_MAX_REGISTER		NAU8822_REG_OUTPUT_TIEOFF
+
+/* NAU8822_REG_POWER_MANAGEMENT_1 (0x1) */
+#define NAU8822_REFIMP_MASK			0x3
+#define NAU8822_REFIMP_80K			0x1
+#define NAU8822_REFIMP_300K			0x2
+#define NAU8822_REFIMP_3K			0x3
+#define NAU8822_IOBUF_EN			(0x1 << 2)
+#define NAU8822_ABIAS_EN			(0x1 << 3)
+
+/* NAU8822_REG_AUDIO_INTERFACE (0x4) */
+#define NAU8822_AIFMT_MASK			(0x3 << 3)
+#define NAU8822_WLEN_MASK			(0x3 << 5)
+#define NAU8822_WLEN_20				(0x1 << 5)
+#define NAU8822_WLEN_24				(0x2 << 5)
+#define NAU8822_WLEN_32				(0x3 << 5)
+#define NAU8822_LRP_MASK			(0x1 << 7)
+#define NAU8822_BCLKP_MASK			(0x1 << 8)
+
+/* NAU8822_REG_COMPANDING_CONTROL (0x5) */
+#define NAU8822_ADDAP_SFT			0
+#define NAU8822_ADCCM_SFT			1
+#define NAU8822_DACCM_SFT			3
+
+/* NAU8822_REG_CLOCKING (0x6) */
+#define NAU8822_CLKIOEN_MASK			0x1
+#define NAU8822_MCLKSEL_SFT			5
+#define NAU8822_MCLKSEL_MASK			(0x7 << 5)
+#define NAU8822_BCLKSEL_SFT			2
+#define NAU8822_BCLKSEL_MASK			(0x7 << 2)
+#define NAU8822_CLKM_MASK			(0x1 << 8)
+#define NAU8822_CLKM_MCLK			(0x0 << 8)
+#define NAU8822_CLKM_PLL			(0x1 << 8)
+
+/* NAU8822_REG_ADDITIONAL_CONTROL (0x08) */
+#define NAU8822_SMPLR_SFT			1
+#define NAU8822_SMPLR_MASK			(0x7 << 1)
+#define NAU8822_SMPLR_48K			(0x0 << 1)
+#define NAU8822_SMPLR_32K			(0x1 << 1)
+#define NAU8822_SMPLR_24K			(0x2 << 1)
+#define NAU8822_SMPLR_16K			(0x3 << 1)
+#define NAU8822_SMPLR_12K			(0x4 << 1)
+#define NAU8822_SMPLR_8K			(0x5 << 1)
+
+/* NAU8822_REG_EQ1 (0x12) */
+#define NAU8822_EQ1GC_SFT			0
+#define NAU8822_EQ1CF_SFT			5
+#define NAU8822_EQM_SFT				8
+
+/* NAU8822_REG_EQ2 (0x13) */
+#define NAU8822_EQ2GC_SFT			0
+#define NAU8822_EQ2CF_SFT			5
+#define NAU8822_EQ2BW_SFT			8
+
+/* NAU8822_REG_EQ3 (0x14) */
+#define NAU8822_EQ3GC_SFT			0
+#define NAU8822_EQ3CF_SFT			5
+#define NAU8822_EQ3BW_SFT			8
+
+/* NAU8822_REG_EQ4 (0x15) */
+#define NAU8822_EQ4GC_SFT			0
+#define NAU8822_EQ4CF_SFT			5
+#define NAU8822_EQ4BW_SFT			8
+
+/* NAU8822_REG_EQ5 (0x16) */
+#define NAU8822_EQ5GC_SFT			0
+#define NAU8822_EQ5CF_SFT			5
+
+/* NAU8822_REG_ALC_CONTROL_1 (0x20) */
+#define NAU8822_ALCMINGAIN_SFT			0
+#define NAU8822_ALCMXGAIN_SFT			3
+#define NAU8822_ALCEN_SFT			7
+
+/* NAU8822_REG_ALC_CONTROL_2 (0x21) */
+#define NAU8822_ALCSL_SFT			0
+#define NAU8822_ALCHT_SFT			4
+
+/* NAU8822_REG_ALC_CONTROL_3 (0x22) */
+#define NAU8822_ALCATK_SFT			0
+#define NAU8822_ALCDCY_SFT			4
+#define NAU8822_ALCM_SFT			8
+
+/* NAU8822_REG_PLL_N (0x24) */
+#define NAU8822_PLLMCLK_DIV2			(0x1 << 4)
+#define NAU8822_PLLN_MASK			0xF
+
+#define NAU8822_PLLK1_SFT			18
+#define NAU8822_PLLK1_MASK			0x3F
+
+/* NAU8822_REG_PLL_K2 (0x26) */
+#define NAU8822_PLLK2_SFT			9
+#define NAU8822_PLLK2_MASK			0x1FF
+
+/* NAU8822_REG_PLL_K3 (0x27) */
+#define NAU8822_PLLK3_MASK			0x1FF
+
+/* System Clock Source */
+enum {
+	NAU8822_CLK_MCLK,
+	NAU8822_CLK_PLL,
+};
+
+struct nau8822_pll {
+	int pre_factor;
+	int mclk_scaler;
+	int pll_frac;
+	int pll_int;
+};
+
+/* Codec Private Data */
+struct nau8822 {
+	struct device *dev;
+	struct regmap *regmap;
+	int mclk_idx;
+	struct nau8822_pll pll;
+	int sysclk;
+	int div_id;
+};
+
+#endif	/* __NAU8822_H__ */
diff --git a/sound/soc/codecs/pcm186x.c b/sound/soc/codecs/pcm186x.c
index 690c26e7389e..809b7e9f03ca 100644
--- a/sound/soc/codecs/pcm186x.c
+++ b/sound/soc/codecs/pcm186x.c
@@ -401,7 +401,8 @@ static int pcm186x_set_fmt(struct snd_soc_dai *dai, unsigned int format)
 		break;
 	case SND_SOC_DAIFMT_DSP_A:
 		priv->tdm_offset += 1;
-		/* Fall through... DSP_A uses the same basic config as DSP_B
+		/* fall through */
+		/* DSP_A uses the same basic config as DSP_B
 		 * except we need to shift the TDM output by one BCK cycle
 		 */
 	case SND_SOC_DAIFMT_DSP_B:
diff --git a/sound/soc/codecs/pcm3060-i2c.c b/sound/soc/codecs/pcm3060-i2c.c
new file mode 100644
index 000000000000..cdc8314882bc
--- /dev/null
+++ b/sound/soc/codecs/pcm3060-i2c.c
@@ -0,0 +1,60 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// PCM3060 I2C driver
+//
+// Copyright (C) 2018 Kirill Marinushkin <kmarinushkin@birdec.tech>
+
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <sound/soc.h>
+
+#include "pcm3060.h"
+
+static int pcm3060_i2c_probe(struct i2c_client *i2c,
+			     const struct i2c_device_id *id)
+{
+	struct pcm3060_priv *priv;
+
+	priv = devm_kzalloc(&i2c->dev, sizeof(*priv), GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
+
+	i2c_set_clientdata(i2c, priv);
+
+	priv->regmap = devm_regmap_init_i2c(i2c, &pcm3060_regmap);
+	if (IS_ERR(priv->regmap))
+		return PTR_ERR(priv->regmap);
+
+	return pcm3060_probe(&i2c->dev);
+}
+
+static const struct i2c_device_id pcm3060_i2c_id[] = {
+	{ .name = "pcm3060" },
+	{ },
+};
+MODULE_DEVICE_TABLE(i2c, pcm3060_i2c_id);
+
+#ifdef CONFIG_OF
+static const struct of_device_id pcm3060_of_match[] = {
+	{ .compatible = "ti,pcm3060" },
+	{ },
+};
+MODULE_DEVICE_TABLE(of, pcm3060_of_match);
+#endif /* CONFIG_OF */
+
+static struct i2c_driver pcm3060_i2c_driver = {
+	.driver = {
+		.name = "pcm3060",
+#ifdef CONFIG_OF
+		.of_match_table = pcm3060_of_match,
+#endif /* CONFIG_OF */
+	},
+	.id_table = pcm3060_i2c_id,
+	.probe = pcm3060_i2c_probe,
+};
+
+module_i2c_driver(pcm3060_i2c_driver);
+
+MODULE_DESCRIPTION("PCM3060 I2C driver");
+MODULE_AUTHOR("Kirill Marinushkin <kmarinushkin@birdec.tech>");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/pcm3060-spi.c b/sound/soc/codecs/pcm3060-spi.c
new file mode 100644
index 000000000000..f6f19fa80932
--- /dev/null
+++ b/sound/soc/codecs/pcm3060-spi.c
@@ -0,0 +1,59 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// PCM3060 SPI driver
+//
+// Copyright (C) 2018 Kirill Marinushkin <kmarinushkin@birdec.tech>
+
+#include <linux/module.h>
+#include <linux/spi/spi.h>
+#include <sound/soc.h>
+
+#include "pcm3060.h"
+
+static int pcm3060_spi_probe(struct spi_device *spi)
+{
+	struct pcm3060_priv *priv;
+
+	priv = devm_kzalloc(&spi->dev, sizeof(*priv), GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
+
+	spi_set_drvdata(spi, priv);
+
+	priv->regmap = devm_regmap_init_spi(spi, &pcm3060_regmap);
+	if (IS_ERR(priv->regmap))
+		return PTR_ERR(priv->regmap);
+
+	return pcm3060_probe(&spi->dev);
+}
+
+static const struct spi_device_id pcm3060_spi_id[] = {
+	{ .name = "pcm3060" },
+	{ },
+};
+MODULE_DEVICE_TABLE(spi, pcm3060_spi_id);
+
+#ifdef CONFIG_OF
+static const struct of_device_id pcm3060_of_match[] = {
+	{ .compatible = "ti,pcm3060" },
+	{ },
+};
+MODULE_DEVICE_TABLE(of, pcm3060_of_match);
+#endif /* CONFIG_OF */
+
+static struct spi_driver pcm3060_spi_driver = {
+	.driver = {
+		.name = "pcm3060",
+#ifdef CONFIG_OF
+		.of_match_table = pcm3060_of_match,
+#endif /* CONFIG_OF */
+	},
+	.id_table = pcm3060_spi_id,
+	.probe = pcm3060_spi_probe,
+};
+
+module_spi_driver(pcm3060_spi_driver);
+
+MODULE_DESCRIPTION("PCM3060 SPI driver");
+MODULE_AUTHOR("Kirill Marinushkin <kmarinushkin@birdec.tech>");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/pcm3060.c b/sound/soc/codecs/pcm3060.c
new file mode 100644
index 000000000000..494d9d662be8
--- /dev/null
+++ b/sound/soc/codecs/pcm3060.c
@@ -0,0 +1,295 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// PCM3060 codec driver
+//
+// Copyright (C) 2018 Kirill Marinushkin <kmarinushkin@birdec.tech>
+
+#include <linux/module.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/tlv.h>
+
+#include "pcm3060.h"
+
+/* dai */
+
+static int pcm3060_set_sysclk(struct snd_soc_dai *dai, int clk_id,
+			      unsigned int freq, int dir)
+{
+	struct snd_soc_component *comp = dai->component;
+	struct pcm3060_priv *priv = snd_soc_component_get_drvdata(comp);
+
+	if (dir != SND_SOC_CLOCK_IN) {
+		dev_err(comp->dev, "unsupported sysclock dir: %d\n", dir);
+		return -EINVAL;
+	}
+
+	priv->dai[dai->id].sclk_freq = freq;
+
+	return 0;
+}
+
+static int pcm3060_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+	struct snd_soc_component *comp = dai->component;
+	struct pcm3060_priv *priv = snd_soc_component_get_drvdata(comp);
+	unsigned int reg;
+	unsigned int val;
+
+	if ((fmt & SND_SOC_DAIFMT_INV_MASK) != SND_SOC_DAIFMT_NB_NF) {
+		dev_err(comp->dev, "unsupported DAI polarity: 0x%x\n", fmt);
+		return -EINVAL;
+	}
+
+	switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+	case SND_SOC_DAIFMT_CBM_CFM:
+		priv->dai[dai->id].is_master = true;
+		break;
+	case SND_SOC_DAIFMT_CBS_CFS:
+		priv->dai[dai->id].is_master = false;
+		break;
+	default:
+		dev_err(comp->dev, "unsupported DAI master mode: 0x%x\n", fmt);
+		return -EINVAL;
+	}
+
+	switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+	case SND_SOC_DAIFMT_I2S:
+		val = PCM3060_REG_FMT_I2S;
+		break;
+	case SND_SOC_DAIFMT_RIGHT_J:
+		val = PCM3060_REG_FMT_RJ;
+		break;
+	case SND_SOC_DAIFMT_LEFT_J:
+		val = PCM3060_REG_FMT_LJ;
+		break;
+	default:
+		dev_err(comp->dev, "unsupported DAI format: 0x%x\n", fmt);
+		return -EINVAL;
+	}
+
+	if (dai->id == PCM3060_DAI_ID_DAC)
+		reg = PCM3060_REG67;
+	else
+		reg = PCM3060_REG72;
+
+	regmap_update_bits(priv->regmap, reg, PCM3060_REG_MASK_FMT, val);
+
+	return 0;
+}
+
+static int pcm3060_hw_params(struct snd_pcm_substream *substream,
+			     struct snd_pcm_hw_params *params,
+			     struct snd_soc_dai *dai)
+{
+	struct snd_soc_component *comp = dai->component;
+	struct pcm3060_priv *priv = snd_soc_component_get_drvdata(comp);
+	unsigned int rate;
+	unsigned int ratio;
+	unsigned int reg;
+	unsigned int val;
+
+	if (!priv->dai[dai->id].is_master) {
+		val = PCM3060_REG_MS_S;
+		goto val_ready;
+	}
+
+	rate = params_rate(params);
+	if (!rate) {
+		dev_err(comp->dev, "rate is not configured\n");
+		return -EINVAL;
+	}
+
+	ratio = priv->dai[dai->id].sclk_freq / rate;
+
+	switch (ratio) {
+	case 768:
+		val = PCM3060_REG_MS_M768;
+		break;
+	case 512:
+		val = PCM3060_REG_MS_M512;
+		break;
+	case 384:
+		val = PCM3060_REG_MS_M384;
+		break;
+	case 256:
+		val = PCM3060_REG_MS_M256;
+		break;
+	case 192:
+		val = PCM3060_REG_MS_M192;
+		break;
+	case 128:
+		val = PCM3060_REG_MS_M128;
+		break;
+	default:
+		dev_err(comp->dev, "unsupported ratio: %d\n", ratio);
+		return -EINVAL;
+	}
+
+val_ready:
+	if (dai->id == PCM3060_DAI_ID_DAC)
+		reg = PCM3060_REG67;
+	else
+		reg = PCM3060_REG72;
+
+	regmap_update_bits(priv->regmap, reg, PCM3060_REG_MASK_MS, val);
+
+	return 0;
+}
+
+static const struct snd_soc_dai_ops pcm3060_dai_ops = {
+	.set_sysclk = pcm3060_set_sysclk,
+	.set_fmt = pcm3060_set_fmt,
+	.hw_params = pcm3060_hw_params,
+};
+
+#define PCM3060_DAI_RATES_ADC	(SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_32000 | \
+				 SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 | \
+				 SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000)
+
+#define PCM3060_DAI_RATES_DAC	(PCM3060_DAI_RATES_ADC | \
+				 SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_192000)
+
+static struct snd_soc_dai_driver pcm3060_dai[] = {
+	{
+		.name = "pcm3060-dac",
+		.id = PCM3060_DAI_ID_DAC,
+		.playback = {
+			.stream_name = "Playback",
+			.channels_min = 2,
+			.channels_max = 2,
+			.rates = PCM3060_DAI_RATES_DAC,
+			.formats = SNDRV_PCM_FMTBIT_S24_LE,
+		},
+		.ops = &pcm3060_dai_ops,
+	},
+	{
+		.name = "pcm3060-adc",
+		.id = PCM3060_DAI_ID_ADC,
+		.capture = {
+			.stream_name = "Capture",
+			.channels_min = 2,
+			.channels_max = 2,
+			.rates = PCM3060_DAI_RATES_ADC,
+			.formats = SNDRV_PCM_FMTBIT_S24_LE,
+		},
+		.ops = &pcm3060_dai_ops,
+	},
+};
+
+/* dapm */
+
+static DECLARE_TLV_DB_SCALE(pcm3060_dapm_tlv, -10050, 50, 1);
+
+static const struct snd_kcontrol_new pcm3060_dapm_controls[] = {
+	SOC_DOUBLE_R_RANGE_TLV("Master Playback Volume",
+			       PCM3060_REG65, PCM3060_REG66, 0,
+			       PCM3060_REG_AT2_MIN, PCM3060_REG_AT2_MAX,
+			       0, pcm3060_dapm_tlv),
+	SOC_DOUBLE("Master Playback Switch", PCM3060_REG68,
+		   PCM3060_REG_SHIFT_MUT21, PCM3060_REG_SHIFT_MUT22, 1, 1),
+
+	SOC_DOUBLE_R_RANGE_TLV("Master Capture Volume",
+			       PCM3060_REG70, PCM3060_REG71, 0,
+			       PCM3060_REG_AT1_MIN, PCM3060_REG_AT1_MAX,
+			       0, pcm3060_dapm_tlv),
+	SOC_DOUBLE("Master Capture Switch", PCM3060_REG73,
+		   PCM3060_REG_SHIFT_MUT11, PCM3060_REG_SHIFT_MUT12, 1, 1),
+};
+
+static const struct snd_soc_dapm_widget pcm3060_dapm_widgets[] = {
+	SND_SOC_DAPM_OUTPUT("OUTL+"),
+	SND_SOC_DAPM_OUTPUT("OUTR+"),
+	SND_SOC_DAPM_OUTPUT("OUTL-"),
+	SND_SOC_DAPM_OUTPUT("OUTR-"),
+
+	SND_SOC_DAPM_INPUT("INL"),
+	SND_SOC_DAPM_INPUT("INR"),
+};
+
+static const struct snd_soc_dapm_route pcm3060_dapm_map[] = {
+	{ "OUTL+", NULL, "Playback" },
+	{ "OUTR+", NULL, "Playback" },
+	{ "OUTL-", NULL, "Playback" },
+	{ "OUTR-", NULL, "Playback" },
+
+	{ "Capture", NULL, "INL" },
+	{ "Capture", NULL, "INR" },
+};
+
+/* soc component */
+
+static const struct snd_soc_component_driver pcm3060_soc_comp_driver = {
+	.controls = pcm3060_dapm_controls,
+	.num_controls = ARRAY_SIZE(pcm3060_dapm_controls),
+	.dapm_widgets = pcm3060_dapm_widgets,
+	.num_dapm_widgets = ARRAY_SIZE(pcm3060_dapm_widgets),
+	.dapm_routes = pcm3060_dapm_map,
+	.num_dapm_routes = ARRAY_SIZE(pcm3060_dapm_map),
+};
+
+/* regmap */
+
+static bool pcm3060_reg_writeable(struct device *dev, unsigned int reg)
+{
+	return (reg >= PCM3060_REG64);
+}
+
+static bool pcm3060_reg_readable(struct device *dev, unsigned int reg)
+{
+	return (reg >= PCM3060_REG64);
+}
+
+static bool pcm3060_reg_volatile(struct device *dev, unsigned int reg)
+{
+	/* PCM3060_REG64 is volatile */
+	return (reg == PCM3060_REG64);
+}
+
+static const struct reg_default pcm3060_reg_defaults[] = {
+	{ PCM3060_REG64,  0xF0 },
+	{ PCM3060_REG65,  0xFF },
+	{ PCM3060_REG66,  0xFF },
+	{ PCM3060_REG67,  0x00 },
+	{ PCM3060_REG68,  0x00 },
+	{ PCM3060_REG69,  0x00 },
+	{ PCM3060_REG70,  0xD7 },
+	{ PCM3060_REG71,  0xD7 },
+	{ PCM3060_REG72,  0x00 },
+	{ PCM3060_REG73,  0x00 },
+};
+
+const struct regmap_config pcm3060_regmap = {
+	.reg_bits = 8,
+	.val_bits = 8,
+	.writeable_reg = pcm3060_reg_writeable,
+	.readable_reg = pcm3060_reg_readable,
+	.volatile_reg = pcm3060_reg_volatile,
+	.max_register = PCM3060_REG73,
+	.reg_defaults = pcm3060_reg_defaults,
+	.num_reg_defaults = ARRAY_SIZE(pcm3060_reg_defaults),
+	.cache_type = REGCACHE_RBTREE,
+};
+EXPORT_SYMBOL(pcm3060_regmap);
+
+/* device */
+
+int pcm3060_probe(struct device *dev)
+{
+	int rc;
+
+	rc = devm_snd_soc_register_component(dev, &pcm3060_soc_comp_driver,
+					     pcm3060_dai,
+					     ARRAY_SIZE(pcm3060_dai));
+	if (rc) {
+		dev_err(dev, "failed to register component, rc=%d\n", rc);
+		return rc;
+	}
+
+	return 0;
+}
+EXPORT_SYMBOL(pcm3060_probe);
+
+MODULE_DESCRIPTION("PCM3060 codec driver");
+MODULE_AUTHOR("Kirill Marinushkin <kmarinushkin@birdec.tech>");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/pcm3060.h b/sound/soc/codecs/pcm3060.h
new file mode 100644
index 000000000000..fd89a68aa8a7
--- /dev/null
+++ b/sound/soc/codecs/pcm3060.h
@@ -0,0 +1,88 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * PCM3060 codec driver
+ *
+ * Copyright (C) 2018 Kirill Marinushkin <kmarinushkin@birdec.tech>
+ */
+
+#ifndef _SND_SOC_PCM3060_H
+#define _SND_SOC_PCM3060_H
+
+#include <linux/device.h>
+#include <linux/regmap.h>
+
+extern const struct regmap_config pcm3060_regmap;
+
+#define PCM3060_DAI_ID_DAC	0
+#define PCM3060_DAI_ID_ADC	1
+#define PCM3060_DAI_IDS_NUM	2
+
+struct pcm3060_priv_dai {
+	bool is_master;
+	unsigned int sclk_freq;
+};
+
+struct pcm3060_priv {
+	struct regmap *regmap;
+	struct pcm3060_priv_dai dai[PCM3060_DAI_IDS_NUM];
+};
+
+int pcm3060_probe(struct device *dev);
+int pcm3060_remove(struct device *dev);
+
+/* registers */
+
+#define PCM3060_REG64			0x40
+#define PCM3060_REG_MRST		0x80
+#define PCM3060_REG_SRST		0x40
+#define PCM3060_REG_ADPSV		0x20
+#define PCM3060_REG_DAPSV		0x10
+#define PCM3060_REG_SE			0x01
+
+#define PCM3060_REG65			0x41
+#define PCM3060_REG66			0x42
+#define PCM3060_REG_AT2_MIN		0x36
+#define PCM3060_REG_AT2_MAX		0xFF
+
+#define PCM3060_REG67			0x43
+#define PCM3060_REG72			0x48
+#define PCM3060_REG_CSEL		0x80
+#define PCM3060_REG_MASK_MS		0x70
+#define PCM3060_REG_MS_S		0x00
+#define PCM3060_REG_MS_M768		(0x01 << 4)
+#define PCM3060_REG_MS_M512		(0x02 << 4)
+#define PCM3060_REG_MS_M384		(0x03 << 4)
+#define PCM3060_REG_MS_M256		(0x04 << 4)
+#define PCM3060_REG_MS_M192		(0x05 << 4)
+#define PCM3060_REG_MS_M128		(0x06 << 4)
+#define PCM3060_REG_MASK_FMT		0x03
+#define PCM3060_REG_FMT_I2S		0x00
+#define PCM3060_REG_FMT_LJ		0x01
+#define PCM3060_REG_FMT_RJ		0x02
+
+#define PCM3060_REG68			0x44
+#define PCM3060_REG_OVER		0x40
+#define PCM3060_REG_DREV2		0x04
+#define PCM3060_REG_SHIFT_MUT21	0x00
+#define PCM3060_REG_SHIFT_MUT22	0x01
+
+#define PCM3060_REG69			0x45
+#define PCM3060_REG_FLT		0x80
+#define PCM3060_REG_MASK_DMF		0x60
+#define PCM3060_REG_DMC		0x10
+#define PCM3060_REG_ZREV		0x02
+#define PCM3060_REG_AZRO		0x01
+
+#define PCM3060_REG70			0x46
+#define PCM3060_REG71			0x47
+#define PCM3060_REG_AT1_MIN		0x0E
+#define PCM3060_REG_AT1_MAX		0xFF
+
+#define PCM3060_REG73			0x49
+#define PCM3060_REG_ZCDD		0x10
+#define PCM3060_REG_BYP		0x08
+#define PCM3060_REG_DREV1		0x04
+#define PCM3060_REG_SHIFT_MUT11	0x00
+#define PCM3060_REG_SHIFT_MUT12	0x01
+
+#endif /* _SND_SOC_PCM3060_H */
diff --git a/sound/soc/codecs/pcm3168a.c b/sound/soc/codecs/pcm3168a.c
index 3356c91f55b0..52cc950c9fd1 100644
--- a/sound/soc/codecs/pcm3168a.c
+++ b/sound/soc/codecs/pcm3168a.c
@@ -33,6 +33,8 @@
 #define PCM3168A_FMT_RIGHT_J_16		0x3
 #define PCM3168A_FMT_DSP_A		0x4
 #define PCM3168A_FMT_DSP_B		0x5
+#define PCM3168A_FMT_I2S_TDM		0x6
+#define PCM3168A_FMT_LEFT_J_TDM		0x7
 #define PCM3168A_FMT_DSP_MASK		0x4
 
 #define PCM3168A_NUM_SUPPLIES 6
@@ -401,9 +403,11 @@ static int pcm3168a_hw_params(struct snd_pcm_substream *substream,
 	bool tx, master_mode;
 	u32 val, mask, shift, reg;
 	unsigned int rate, fmt, ratio, max_ratio;
+	unsigned int chan;
 	int i, min_frame_size;
 
 	rate = params_rate(params);
+	chan = params_channels(params);
 
 	ratio = pcm3168a->sysclk / rate;
 
@@ -456,6 +460,21 @@ static int pcm3168a_hw_params(struct snd_pcm_substream *substream,
 		return -EINVAL;
 	}
 
+	/* for TDM */
+	if (chan > 2) {
+		switch (fmt) {
+		case PCM3168A_FMT_I2S:
+			fmt = PCM3168A_FMT_I2S_TDM;
+			break;
+		case PCM3168A_FMT_LEFT_J:
+			fmt = PCM3168A_FMT_LEFT_J_TDM;
+			break;
+		default:
+			dev_err(component->dev, "TDM is supported under I2S/Left_J only\n");
+			return -EINVAL;
+		}
+	}
+
 	if (master_mode)
 		val = ((i + 1) << shift);
 	else
@@ -476,7 +495,69 @@ static int pcm3168a_hw_params(struct snd_pcm_substream *substream,
 	return 0;
 }
 
+static int pcm3168a_startup(struct snd_pcm_substream *substream,
+			    struct snd_soc_dai *dai)
+{
+	struct snd_soc_component *component = dai->component;
+	struct pcm3168a_priv *pcm3168a = snd_soc_component_get_drvdata(component);
+	bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
+	unsigned int fmt;
+	unsigned int sample_min;
+	unsigned int channel_max;
+
+	if (tx)
+		fmt = pcm3168a->dac_fmt;
+	else
+		fmt = pcm3168a->adc_fmt;
+
+	/*
+	 * Available Data Bits
+	 *
+	 * RIGHT_J : 24 / 16
+	 * LEFT_J  : 24
+	 * I2S     : 24
+	 *
+	 * TDM available
+	 *
+	 * I2S
+	 * LEFT_J
+	 */
+	switch (fmt) {
+	case PCM3168A_FMT_RIGHT_J:
+		sample_min  = 16;
+		channel_max =  2;
+		break;
+	case PCM3168A_FMT_LEFT_J:
+		sample_min  = 24;
+		if (tx)
+			channel_max = 8;
+		else
+			channel_max = 6;
+		break;
+	case PCM3168A_FMT_I2S:
+		sample_min  = 24;
+		if (tx)
+			channel_max = 8;
+		else
+			channel_max = 6;
+		break;
+	default:
+		sample_min  = 24;
+		channel_max =  2;
+	}
+
+	snd_pcm_hw_constraint_minmax(substream->runtime,
+				     SNDRV_PCM_HW_PARAM_SAMPLE_BITS,
+				     sample_min, 32);
+
+	snd_pcm_hw_constraint_minmax(substream->runtime,
+				     SNDRV_PCM_HW_PARAM_CHANNELS,
+				     2, channel_max);
+
+	return 0;
+}
 static const struct snd_soc_dai_ops pcm3168a_dac_dai_ops = {
+	.startup	= pcm3168a_startup,
 	.set_fmt	= pcm3168a_set_dai_fmt_dac,
 	.set_sysclk	= pcm3168a_set_dai_sysclk,
 	.hw_params	= pcm3168a_hw_params,
@@ -484,6 +565,7 @@ static const struct snd_soc_dai_ops pcm3168a_dac_dai_ops = {
 };
 
 static const struct snd_soc_dai_ops pcm3168a_adc_dai_ops = {
+	.startup	= pcm3168a_startup,
 	.set_fmt	= pcm3168a_set_dai_fmt_adc,
 	.set_sysclk	= pcm3168a_set_dai_sysclk,
 	.hw_params	= pcm3168a_hw_params
diff --git a/sound/soc/codecs/rt274.c b/sound/soc/codecs/rt274.c
index d88e67341083..0ef966d56bac 100644
--- a/sound/soc/codecs/rt274.c
+++ b/sound/soc/codecs/rt274.c
@@ -755,6 +755,7 @@ static int rt274_set_dai_pll(struct snd_soc_dai *dai, int pll_id, int source,
 		break;
 	default:
 		dev_warn(component->dev, "invalid pll source, use BCLK\n");
+		/* fall through */
 	case RT274_PLL2_S_BCLK:
 		snd_soc_component_update_bits(component, RT274_PLL2_CTRL,
 				RT274_PLL2_SRC_MASK, RT274_PLL2_SRC_BCLK);
@@ -782,6 +783,7 @@ static int rt274_set_dai_pll(struct snd_soc_dai *dai, int pll_id, int source,
 			break;
 		default:
 			dev_warn(component->dev, "invalid freq_in, assume 4.8M\n");
+			/* fall through */
 		case 100:
 			snd_soc_component_write(component, 0x7a, 0xaab6);
 			snd_soc_component_write(component, 0x7b, 0x0301);
diff --git a/sound/soc/codecs/rt5651.c b/sound/soc/codecs/rt5651.c
index 985852fd9723..b613103d801b 100644
--- a/sound/soc/codecs/rt5651.c
+++ b/sound/soc/codecs/rt5651.c
@@ -10,7 +10,6 @@
  */
 
 #include <linux/module.h>
-#include <linux/moduleparam.h>
 #include <linux/init.h>
 #include <linux/delay.h>
 #include <linux/pm.h>
diff --git a/sound/soc/codecs/rt5663.c b/sound/soc/codecs/rt5663.c
index 9bd24ad42240..2444fad7c2df 100644
--- a/sound/soc/codecs/rt5663.c
+++ b/sound/soc/codecs/rt5663.c
@@ -72,6 +72,7 @@ struct rt5663_priv {
 static const struct reg_sequence rt5663_patch_list[] = {
 	{ 0x002a, 0x8020 },
 	{ 0x0086, 0x0028 },
+	{ 0x0100, 0xa020 },
 	{ 0x0117, 0x0f28 },
 	{ 0x02fb, 0x8089 },
 };
@@ -580,7 +581,7 @@ static const struct reg_default rt5663_reg[] = {
 	{ 0x00fd, 0x0001 },
 	{ 0x00fe, 0x10ec },
 	{ 0x00ff, 0x6406 },
-	{ 0x0100, 0xa0a0 },
+	{ 0x0100, 0xa020 },
 	{ 0x0108, 0x4444 },
 	{ 0x0109, 0x4444 },
 	{ 0x010a, 0xaaaa },
@@ -2337,6 +2338,8 @@ static int rt5663_hp_event(struct snd_soc_dapm_widget *w,
 				0x8000);
 			snd_soc_component_update_bits(component, RT5663_DEPOP_1, 0x3000,
 				0x3000);
+			snd_soc_component_update_bits(component,
+				RT5663_DIG_VOL_ZCD, 0x00c0, 0x0080);
 		}
 		break;
 
@@ -2351,6 +2354,8 @@ static int rt5663_hp_event(struct snd_soc_dapm_widget *w,
 				RT5663_OVCD_HP_MASK, RT5663_OVCD_HP_EN);
 			snd_soc_component_update_bits(component,
 				RT5663_DACREF_LDO, 0x3e0e, 0);
+			snd_soc_component_update_bits(component,
+				RT5663_DIG_VOL_ZCD, 0x00c0, 0);
 		}
 		break;
 
diff --git a/sound/soc/codecs/rt5668.c b/sound/soc/codecs/rt5668.c
index 3c19d03f2446..85ba04d6e7ae 100644
--- a/sound/soc/codecs/rt5668.c
+++ b/sound/soc/codecs/rt5668.c
@@ -2587,17 +2587,10 @@ static int rt5668_i2c_probe(struct i2c_client *i2c,
 
 	}
 
-	return snd_soc_register_component(&i2c->dev, &soc_component_dev_rt5668,
+	return devm_snd_soc_register_component(&i2c->dev, &soc_component_dev_rt5668,
 			rt5668_dai, ARRAY_SIZE(rt5668_dai));
 }
 
-static int rt5668_i2c_remove(struct i2c_client *i2c)
-{
-	snd_soc_unregister_component(&i2c->dev);
-
-	return 0;
-}
-
 static void rt5668_i2c_shutdown(struct i2c_client *client)
 {
 	struct rt5668_priv *rt5668 = i2c_get_clientdata(client);
@@ -2628,7 +2621,6 @@ static struct i2c_driver rt5668_i2c_driver = {
 		.acpi_match_table = ACPI_PTR(rt5668_acpi_match),
 	},
 	.probe = rt5668_i2c_probe,
-	.remove = rt5668_i2c_remove,
 	.shutdown = rt5668_i2c_shutdown,
 	.id_table = rt5668_i2c_id,
 };
diff --git a/sound/soc/codecs/rt5670.c b/sound/soc/codecs/rt5670.c
index 732ef928b25d..455fe7cff700 100644
--- a/sound/soc/codecs/rt5670.c
+++ b/sound/soc/codecs/rt5670.c
@@ -2877,6 +2877,18 @@ static const struct dmi_system_id dmi_platform_intel_quirks[] = {
 	},
 	{
 		.callback = rt5670_quirk_cb,
+		.ident = "Lenovo Thinkpad Tablet 8",
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
+			DMI_MATCH(DMI_PRODUCT_VERSION, "ThinkPad 8"),
+		},
+		.driver_data = (unsigned long *)(RT5670_DMIC_EN |
+						 RT5670_DMIC2_INR |
+						 RT5670_DEV_GPIO |
+						 RT5670_JD_MODE1),
+	},
+	{
+		.callback = rt5670_quirk_cb,
 		.ident = "Lenovo Thinkpad Tablet 10",
 		.matches = {
 			DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
diff --git a/sound/soc/codecs/rt5677-spi.c b/sound/soc/codecs/rt5677-spi.c
index bd51f3655ee3..84501c2020c7 100644
--- a/sound/soc/codecs/rt5677-spi.c
+++ b/sound/soc/codecs/rt5677-spi.c
@@ -18,7 +18,6 @@
 #include <linux/interrupt.h>
 #include <linux/irq.h>
 #include <linux/slab.h>
-#include <linux/gpio.h>
 #include <linux/sched.h>
 #include <linux/uaccess.h>
 #include <linux/regulator/consumer.h>
diff --git a/sound/soc/codecs/rt5682.c b/sound/soc/codecs/rt5682.c
index fad0bed82d79..340f90497d07 100644
--- a/sound/soc/codecs/rt5682.c
+++ b/sound/soc/codecs/rt5682.c
@@ -67,7 +67,8 @@ struct rt5682_priv {
 };
 
 static const struct reg_sequence patch_list[] = {
-	{0x01c1, 0x1000},
+	{RT5682_HP_IMP_SENS_CTRL_19, 0x1000},
+	{RT5682_DAC_ADC_DIG_VOL1, 0xa020},
 };
 
 static const struct reg_default rt5682_reg[] = {
@@ -1432,6 +1433,28 @@ static const struct snd_kcontrol_new hpor_switch =
 	SOC_DAPM_SINGLE_AUTODISABLE("Switch", RT5682_HP_CTRL_1,
 					RT5682_R_MUTE_SFT, 1, 1);
 
+static int rt5682_charge_pump_event(struct snd_soc_dapm_widget *w,
+	struct snd_kcontrol *kcontrol, int event)
+{
+	struct snd_soc_component *component =
+		snd_soc_dapm_to_component(w->dapm);
+
+	switch (event) {
+	case SND_SOC_DAPM_PRE_PMU:
+		snd_soc_component_update_bits(component,
+			RT5682_HP_CHARGE_PUMP_1, RT5682_PM_HP_MASK, RT5682_PM_HP_HV);
+		break;
+	case SND_SOC_DAPM_POST_PMD:
+		snd_soc_component_update_bits(component,
+			RT5682_HP_CHARGE_PUMP_1, RT5682_PM_HP_MASK, RT5682_PM_HP_LV);
+		break;
+	default:
+		return 0;
+	}
+
+	return 0;
+}
+
 static int rt5682_hp_event(struct snd_soc_dapm_widget *w,
 	struct snd_kcontrol *kcontrol, int event)
 {
@@ -1444,10 +1467,10 @@ static int rt5682_hp_event(struct snd_soc_dapm_widget *w,
 			RT5682_HP_LOGIC_CTRL_2, 0x0012);
 		snd_soc_component_write(component,
 			RT5682_HP_CTRL_2, 0x6000);
-		snd_soc_component_update_bits(component, RT5682_STO_NG2_CTRL_1,
-			RT5682_NG2_EN_MASK, RT5682_NG2_EN);
 		snd_soc_component_update_bits(component,
 			RT5682_DEPOP_1, 0x60, 0x60);
+		snd_soc_component_update_bits(component,
+			RT5682_DAC_ADC_DIG_VOL1, 0x00c0, 0x0080);
 		break;
 
 	case SND_SOC_DAPM_POST_PMD:
@@ -1455,6 +1478,8 @@ static int rt5682_hp_event(struct snd_soc_dapm_widget *w,
 			RT5682_DEPOP_1, 0x60, 0x0);
 		snd_soc_component_write(component,
 			RT5682_HP_CTRL_2, 0x0000);
+		snd_soc_component_update_bits(component,
+			RT5682_DAC_ADC_DIG_VOL1, 0x00c0, 0x0000);
 		break;
 
 	default:
@@ -1718,7 +1743,8 @@ static const struct snd_soc_dapm_widget rt5682_dapm_widgets[] = {
 	SND_SOC_DAPM_SUPPLY("HP Amp R", RT5682_PWR_ANLG_1,
 		RT5682_PWR_HA_R_BIT, 0, NULL, 0),
 	SND_SOC_DAPM_SUPPLY_S("Charge Pump", 1, RT5682_DEPOP_1,
-		RT5682_PUMP_EN_SFT, 0, NULL, 0),
+		RT5682_PUMP_EN_SFT, 0, rt5682_charge_pump_event,
+		SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
 	SND_SOC_DAPM_SUPPLY_S("Capless", 2, RT5682_DEPOP_1,
 		RT5682_CAPLESS_EN_SFT, 0, NULL, 0),
 
@@ -1879,6 +1905,7 @@ static const struct snd_soc_dapm_route rt5682_dapm_routes[] = {
 	{"HP Amp", NULL, "Charge Pump"},
 	{"HP Amp", NULL, "CLKDET SYS"},
 	{"HP Amp", NULL, "CBJ Power"},
+	{"HP Amp", NULL, "Vref1"},
 	{"HP Amp", NULL, "Vref2"},
 	{"HPOL Playback", "Switch", "HP Amp"},
 	{"HPOR Playback", "Switch", "HP Amp"},
@@ -2446,30 +2473,23 @@ static void rt5682_calibrate(struct rt5682_priv *rt5682)
 	mutex_lock(&rt5682->calibrate_mutex);
 
 	rt5682_reset(rt5682->regmap);
-	regmap_write(rt5682->regmap, RT5682_PWR_ANLG_1, 0xa2bf);
+	regmap_write(rt5682->regmap, RT5682_PWR_ANLG_1, 0xa2af);
 	usleep_range(15000, 20000);
-	regmap_write(rt5682->regmap, RT5682_PWR_ANLG_1, 0xf2bf);
-	regmap_write(rt5682->regmap, RT5682_MICBIAS_2, 0x0380);
-	regmap_write(rt5682->regmap, RT5682_PWR_DIG_1, 0x8001);
-	regmap_write(rt5682->regmap, RT5682_TEST_MODE_CTRL_1, 0x0000);
-	regmap_write(rt5682->regmap, RT5682_STO1_DAC_MIXER, 0x2080);
-	regmap_write(rt5682->regmap, RT5682_STO1_ADC_MIXER, 0x4040);
-	regmap_write(rt5682->regmap, RT5682_DEPOP_1, 0x0069);
+	regmap_write(rt5682->regmap, RT5682_PWR_ANLG_1, 0xf2af);
+	regmap_write(rt5682->regmap, RT5682_MICBIAS_2, 0x0300);
+	regmap_write(rt5682->regmap, RT5682_GLB_CLK, 0x8000);
+	regmap_write(rt5682->regmap, RT5682_PWR_DIG_1, 0x0100);
+	regmap_write(rt5682->regmap, RT5682_HP_IMP_SENS_CTRL_19, 0x3800);
 	regmap_write(rt5682->regmap, RT5682_CHOP_DAC, 0x3000);
-	regmap_write(rt5682->regmap, RT5682_HP_CTRL_2, 0x6000);
-	regmap_write(rt5682->regmap, RT5682_HP_CHARGE_PUMP_1, 0x0f26);
-	regmap_write(rt5682->regmap, RT5682_CALIB_ADC_CTRL, 0x7f05);
+	regmap_write(rt5682->regmap, RT5682_CALIB_ADC_CTRL, 0x7005);
 	regmap_write(rt5682->regmap, RT5682_STO1_ADC_MIXER, 0x686c);
 	regmap_write(rt5682->regmap, RT5682_CAL_REC, 0x0d0d);
-	regmap_write(rt5682->regmap, RT5682_HP_CALIB_CTRL_9, 0x000f);
-	regmap_write(rt5682->regmap, RT5682_PWR_DIG_1, 0x8d01);
 	regmap_write(rt5682->regmap, RT5682_HP_CALIB_CTRL_2, 0x0321);
 	regmap_write(rt5682->regmap, RT5682_HP_LOGIC_CTRL_2, 0x0004);
 	regmap_write(rt5682->regmap, RT5682_HP_CALIB_CTRL_1, 0x7c00);
 	regmap_write(rt5682->regmap, RT5682_HP_CALIB_CTRL_3, 0x06a1);
 	regmap_write(rt5682->regmap, RT5682_A_DAC1_MUX, 0x0311);
-	regmap_write(rt5682->regmap, RT5682_RESET_HPF_CTRL, 0x0000);
-	regmap_write(rt5682->regmap, RT5682_ADC_STO1_HP_CTRL_1, 0x3320);
+	regmap_write(rt5682->regmap, RT5682_HP_CALIB_CTRL_1, 0x7c00);
 
 	regmap_write(rt5682->regmap, RT5682_HP_CALIB_CTRL_1, 0xfc00);
 
@@ -2485,8 +2505,12 @@ static void rt5682_calibrate(struct rt5682_priv *rt5682)
 		pr_err("HP Calibration Failure\n");
 
 	/* restore settings */
-	regmap_write(rt5682->regmap, RT5682_STO1_ADC_MIXER, 0xc0c4);
+	regmap_write(rt5682->regmap, RT5682_PWR_ANLG_1, 0x02af);
+	regmap_write(rt5682->regmap, RT5682_MICBIAS_2, 0x0080);
+	regmap_write(rt5682->regmap, RT5682_GLB_CLK, 0x0000);
 	regmap_write(rt5682->regmap, RT5682_PWR_DIG_1, 0x0000);
+	regmap_write(rt5682->regmap, RT5682_CHOP_DAC, 0x2000);
+	regmap_write(rt5682->regmap, RT5682_CALIB_ADC_CTRL, 0x2005);
 
 	mutex_unlock(&rt5682->calibrate_mutex);
 
@@ -2560,7 +2584,7 @@ static int rt5682_i2c_probe(struct i2c_client *i2c,
 
 	rt5682_calibrate(rt5682);
 
-	ret = regmap_register_patch(rt5682->regmap, patch_list,
+	ret = regmap_multi_reg_write(rt5682->regmap, patch_list,
 				    ARRAY_SIZE(patch_list));
 	if (ret != 0)
 		dev_warn(&i2c->dev, "Failed to apply regmap patch: %d\n", ret);
@@ -2614,6 +2638,10 @@ static int rt5682_i2c_probe(struct i2c_client *i2c,
 			RT5682_GP4_PIN_MASK | RT5682_GP5_PIN_MASK,
 			RT5682_GP4_PIN_ADCDAT1 | RT5682_GP5_PIN_DACDAT1);
 	regmap_write(rt5682->regmap, RT5682_TEST_MODE_CTRL_1, 0x0000);
+	regmap_update_bits(rt5682->regmap, RT5682_BIAS_CUR_CTRL_8,
+			RT5682_HPA_CP_BIAS_CTRL_MASK, RT5682_HPA_CP_BIAS_3UA);
+	regmap_update_bits(rt5682->regmap, RT5682_CHARGE_PUMP_1,
+			RT5682_CP_CLK_HP_MASK, RT5682_CP_CLK_HP_300KHZ);
 
 	INIT_DELAYED_WORK(&rt5682->jack_detect_work,
 				rt5682_jack_detect_handler);
@@ -2631,11 +2659,17 @@ static int rt5682_i2c_probe(struct i2c_client *i2c,
 
 	}
 
-	return devm_snd_soc_register_component(&i2c->dev,
-			&soc_component_dev_rt5682,
+	return snd_soc_register_component(&i2c->dev, &soc_component_dev_rt5682,
 			rt5682_dai, ARRAY_SIZE(rt5682_dai));
 }
 
+static int rt5682_i2c_remove(struct i2c_client *i2c)
+{
+	snd_soc_unregister_component(&i2c->dev);
+
+	return 0;
+}
+
 static void rt5682_i2c_shutdown(struct i2c_client *client)
 {
 	struct rt5682_priv *rt5682 = i2c_get_clientdata(client);
@@ -2666,6 +2700,7 @@ static struct i2c_driver rt5682_i2c_driver = {
 		.acpi_match_table = ACPI_PTR(rt5682_acpi_match),
 	},
 	.probe = rt5682_i2c_probe,
+	.remove = rt5682_i2c_remove,
 	.shutdown = rt5682_i2c_shutdown,
 	.id_table = rt5682_i2c_id,
 };
diff --git a/sound/soc/codecs/rt5682.h b/sound/soc/codecs/rt5682.h
index 8068140ebe3f..d82a8301fd74 100644
--- a/sound/soc/codecs/rt5682.h
+++ b/sound/soc/codecs/rt5682.h
@@ -1214,6 +1214,20 @@
 #define RT5682_JDH_NO_PLUG			(0x1 << 4)
 #define RT5682_JDH_PLUG				(0x0 << 4)
 
+/* Bias current control 8 (0x0111) */
+#define RT5682_HPA_CP_BIAS_CTRL_MASK			(0x3 << 2)
+#define RT5682_HPA_CP_BIAS_2UA			(0x0 << 2)
+#define RT5682_HPA_CP_BIAS_3UA			(0x1 << 2)
+#define RT5682_HPA_CP_BIAS_4UA			(0x2 << 2)
+#define RT5682_HPA_CP_BIAS_6UA			(0x3 << 2)
+
+/* Charge Pump Internal Register1 (0x0125) */
+#define RT5682_CP_CLK_HP_MASK			(0x3 << 4)
+#define RT5682_CP_CLK_HP_100KHZ			(0x0 << 4)
+#define RT5682_CP_CLK_HP_200KHZ			(0x1 << 4)
+#define RT5682_CP_CLK_HP_300KHZ			(0x2 << 4)
+#define RT5682_CP_CLK_HP_600KHZ			(0x3 << 4)
+
 /* Chopper and Clock control for DAC (0x013a)*/
 #define RT5682_CKXEN_DAC1_MASK			(0x1 << 13)
 #define RT5682_CKXEN_DAC1_SFT			13
diff --git a/sound/soc/codecs/sgtl5000.c b/sound/soc/codecs/sgtl5000.c
index 60764f6201b1..add18d6d77da 100644
--- a/sound/soc/codecs/sgtl5000.c
+++ b/sound/soc/codecs/sgtl5000.c
@@ -1218,7 +1218,7 @@ static int sgtl5000_set_power_regs(struct snd_soc_component *component)
 	 * Searching for a suitable index solving this formula:
 	 * idx = 40 * log10(vag_val / lo_cagcntrl) + 15
 	 */
-	vol_quot = (vag * 100) / lo_vag;
+	vol_quot = lo_vag ? (vag * 100) / lo_vag : 0;
 	lo_vol = 0;
 	for (i = 0; i < ARRAY_SIZE(vol_quot_table); i++) {
 		if (vol_quot >= vol_quot_table[i])
diff --git a/sound/soc/codecs/sta32x.c b/sound/soc/codecs/sta32x.c
index ce508b4cc85c..f753d2db0a5a 100644
--- a/sound/soc/codecs/sta32x.c
+++ b/sound/soc/codecs/sta32x.c
@@ -21,6 +21,7 @@
 #include <linux/module.h>
 #include <linux/moduleparam.h>
 #include <linux/init.h>
+#include <linux/clk.h>
 #include <linux/delay.h>
 #include <linux/pm.h>
 #include <linux/i2c.h>
@@ -142,6 +143,7 @@ static const char *sta32x_supply_names[] = {
 /* codec private data */
 struct sta32x_priv {
 	struct regmap *regmap;
+	struct clk *xti_clk;
 	struct regulator_bulk_data supplies[ARRAY_SIZE(sta32x_supply_names)];
 	struct snd_soc_component *component;
 	struct sta32x_platform_data *pdata;
@@ -882,6 +884,15 @@ static int sta32x_probe(struct snd_soc_component *component)
 
 	sta32x->component = component;
 
+	if (sta32x->xti_clk) {
+		ret = clk_prepare_enable(sta32x->xti_clk);
+		if (ret != 0) {
+			dev_err(component->dev,
+				"Failed to enable clock: %d\n", ret);
+			return ret;
+		}
+	}
+
 	ret = regulator_bulk_enable(ARRAY_SIZE(sta32x->supplies),
 				    sta32x->supplies);
 	if (ret != 0) {
@@ -984,6 +995,9 @@ static void sta32x_remove(struct snd_soc_component *component)
 
 	sta32x_watchdog_stop(sta32x);
 	regulator_bulk_disable(ARRAY_SIZE(sta32x->supplies), sta32x->supplies);
+
+	if (sta32x->xti_clk)
+		clk_disable_unprepare(sta32x->xti_clk);
 }
 
 static const struct snd_soc_component_driver sta32x_component = {
@@ -1041,6 +1055,8 @@ static int sta32x_probe_dt(struct device *dev, struct sta32x_priv *sta32x)
 	of_property_read_u8(np, "st,ch3-output-mapping",
 			    &pdata->ch3_output_mapping);
 
+	if (of_get_property(np, "st,fault-detect-recovery", NULL))
+		pdata->fault_detect_recovery = 1;
 	if (of_get_property(np, "st,thermal-warning-recovery", NULL))
 		pdata->thermal_warning_recovery = 1;
 	if (of_get_property(np, "st,thermal-warning-adjustment", NULL))
@@ -1098,6 +1114,17 @@ static int sta32x_i2c_probe(struct i2c_client *i2c,
 	}
 #endif
 
+	/* Clock */
+	sta32x->xti_clk = devm_clk_get(dev, "xti");
+	if (IS_ERR(sta32x->xti_clk)) {
+		ret = PTR_ERR(sta32x->xti_clk);
+
+		if (ret == -EPROBE_DEFER)
+			return ret;
+
+		sta32x->xti_clk = NULL;
+	}
+
 	/* GPIOs */
 	sta32x->gpiod_nreset = devm_gpiod_get_optional(dev, "reset",
 						       GPIOD_OUT_LOW);
diff --git a/sound/soc/codecs/tas5720.c b/sound/soc/codecs/tas5720.c
index ae3d032ac35a..6bd0e5d5347f 100644
--- a/sound/soc/codecs/tas5720.c
+++ b/sound/soc/codecs/tas5720.c
@@ -152,6 +152,7 @@ static int tas5720_set_dai_tdm_slot(struct snd_soc_dai *dai,
 				    int slots, int slot_width)
 {
 	struct snd_soc_component *component = dai->component;
+	struct tas5720_data *tas5720 = snd_soc_component_get_drvdata(component);
 	unsigned int first_slot;
 	int ret;
 
@@ -185,6 +186,20 @@ static int tas5720_set_dai_tdm_slot(struct snd_soc_dai *dai,
 	if (ret < 0)
 		goto error_snd_soc_component_update_bits;
 
+	/* Configure TDM slot width. This is only applicable to TAS5722. */
+	switch (tas5720->devtype) {
+	case TAS5722:
+		ret = snd_soc_component_update_bits(component, TAS5722_DIGITAL_CTRL2_REG,
+						    TAS5722_TDM_SLOT_16B,
+						    slot_width == 16 ?
+						    TAS5722_TDM_SLOT_16B : 0);
+		if (ret < 0)
+			goto error_snd_soc_component_update_bits;
+		break;
+	default:
+		break;
+	}
+
 	return 0;
 
 error_snd_soc_component_update_bits:
@@ -485,15 +500,56 @@ static const DECLARE_TLV_DB_RANGE(dac_analog_tlv,
 );
 
 /*
- * DAC digital volumes. From -103.5 to 24 dB in 0.5 dB steps. Note that
- * setting the gain below -100 dB (register value <0x7) is effectively a MUTE
- * as per device datasheet.
+ * DAC digital volumes. From -103.5 to 24 dB in 0.5 dB or 0.25 dB steps
+ * depending on the device. Note that setting the gain below -100 dB
+ * (register value <0x7) is effectively a MUTE as per device datasheet.
+ *
+ * Note that for the TAS5722 the digital volume controls are actually split
+ * over two registers, so we need custom getters/setters for access.
  */
-static DECLARE_TLV_DB_SCALE(dac_tlv, -10350, 50, 0);
+static DECLARE_TLV_DB_SCALE(tas5720_dac_tlv, -10350, 50, 0);
+static DECLARE_TLV_DB_SCALE(tas5722_dac_tlv, -10350, 25, 0);
+
+static int tas5722_volume_get(struct snd_kcontrol *kcontrol,
+			      struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+	unsigned int val;
+
+	snd_soc_component_read(component, TAS5720_VOLUME_CTRL_REG, &val);
+	ucontrol->value.integer.value[0] = val << 1;
+
+	snd_soc_component_read(component, TAS5722_DIGITAL_CTRL2_REG, &val);
+	ucontrol->value.integer.value[0] |= val & TAS5722_VOL_CONTROL_LSB;
+
+	return 0;
+}
+
+static int tas5722_volume_set(struct snd_kcontrol *kcontrol,
+			      struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+	unsigned int sel = ucontrol->value.integer.value[0];
+
+	snd_soc_component_write(component, TAS5720_VOLUME_CTRL_REG, sel >> 1);
+	snd_soc_component_update_bits(component, TAS5722_DIGITAL_CTRL2_REG,
+				      TAS5722_VOL_CONTROL_LSB, sel);
+
+	return 0;
+}
 
 static const struct snd_kcontrol_new tas5720_snd_controls[] = {
 	SOC_SINGLE_TLV("Speaker Driver Playback Volume",
-		       TAS5720_VOLUME_CTRL_REG, 0, 0xff, 0, dac_tlv),
+		       TAS5720_VOLUME_CTRL_REG, 0, 0xff, 0, tas5720_dac_tlv),
+	SOC_SINGLE_TLV("Speaker Driver Analog Gain", TAS5720_ANALOG_CTRL_REG,
+		       TAS5720_ANALOG_GAIN_SHIFT, 3, 0, dac_analog_tlv),
+};
+
+static const struct snd_kcontrol_new tas5722_snd_controls[] = {
+	SOC_SINGLE_EXT_TLV("Speaker Driver Playback Volume",
+			   0, 0, 511, 0,
+			   tas5722_volume_get, tas5722_volume_set,
+			   tas5722_dac_tlv),
 	SOC_SINGLE_TLV("Speaker Driver Analog Gain", TAS5720_ANALOG_CTRL_REG,
 		       TAS5720_ANALOG_GAIN_SHIFT, 3, 0, dac_analog_tlv),
 };
@@ -527,6 +583,23 @@ static const struct snd_soc_component_driver soc_component_dev_tas5720 = {
 	.non_legacy_dai_naming	= 1,
 };
 
+static const struct snd_soc_component_driver soc_component_dev_tas5722 = {
+	.probe = tas5720_codec_probe,
+	.remove = tas5720_codec_remove,
+	.suspend = tas5720_suspend,
+	.resume = tas5720_resume,
+	.controls = tas5722_snd_controls,
+	.num_controls = ARRAY_SIZE(tas5722_snd_controls),
+	.dapm_widgets = tas5720_dapm_widgets,
+	.num_dapm_widgets = ARRAY_SIZE(tas5720_dapm_widgets),
+	.dapm_routes = tas5720_audio_map,
+	.num_dapm_routes = ARRAY_SIZE(tas5720_audio_map),
+	.idle_bias_on		= 1,
+	.use_pmdown_time	= 1,
+	.endianness		= 1,
+	.non_legacy_dai_naming	= 1,
+};
+
 /* PCM rates supported by the TAS5720 driver */
 #define TAS5720_RATES	(SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 |\
 			 SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000)
@@ -613,9 +686,23 @@ static int tas5720_probe(struct i2c_client *client,
 
 	dev_set_drvdata(dev, data);
 
-	ret = devm_snd_soc_register_component(&client->dev,
-				     &soc_component_dev_tas5720,
-				     tas5720_dai, ARRAY_SIZE(tas5720_dai));
+	switch (id->driver_data) {
+	case TAS5720:
+		ret = devm_snd_soc_register_component(&client->dev,
+					&soc_component_dev_tas5720,
+					tas5720_dai,
+					ARRAY_SIZE(tas5720_dai));
+		break;
+	case TAS5722:
+		ret = devm_snd_soc_register_component(&client->dev,
+					&soc_component_dev_tas5722,
+					tas5720_dai,
+					ARRAY_SIZE(tas5720_dai));
+		break;
+	default:
+		dev_err(dev, "unexpected private driver data\n");
+		return -EINVAL;
+	}
 	if (ret < 0) {
 		dev_err(dev, "failed to register component: %d\n", ret);
 		return ret;
diff --git a/sound/soc/codecs/tas6424.c b/sound/soc/codecs/tas6424.c
index 0d6145549a98..36aebdb8f55c 100644
--- a/sound/soc/codecs/tas6424.c
+++ b/sound/soc/codecs/tas6424.c
@@ -41,6 +41,7 @@ struct tas6424_data {
 	struct regmap *regmap;
 	struct regulator_bulk_data supplies[TAS6424_NUM_SUPPLIES];
 	struct delayed_work fault_check_work;
+	unsigned int last_cfault;
 	unsigned int last_fault1;
 	unsigned int last_fault2;
 	unsigned int last_warn;
@@ -406,9 +407,54 @@ static void tas6424_fault_check_work(struct work_struct *work)
 	unsigned int reg;
 	int ret;
 
+	ret = regmap_read(tas6424->regmap, TAS6424_CHANNEL_FAULT, &reg);
+	if (ret < 0) {
+		dev_err(dev, "failed to read CHANNEL_FAULT register: %d\n", ret);
+		goto out;
+	}
+
+	if (!reg) {
+		tas6424->last_cfault = reg;
+		goto check_global_fault1_reg;
+	}
+
+	/*
+	 * Only flag errors once for a given occurrence. This is needed as
+	 * the TAS6424 will take time clearing the fault condition internally
+	 * during which we don't want to bombard the system with the same
+	 * error message over and over.
+	 */
+	if ((reg & TAS6424_FAULT_OC_CH1) && !(tas6424->last_cfault & TAS6424_FAULT_OC_CH1))
+		dev_crit(dev, "experienced a channel 1 overcurrent fault\n");
+
+	if ((reg & TAS6424_FAULT_OC_CH2) && !(tas6424->last_cfault & TAS6424_FAULT_OC_CH2))
+		dev_crit(dev, "experienced a channel 2 overcurrent fault\n");
+
+	if ((reg & TAS6424_FAULT_OC_CH3) && !(tas6424->last_cfault & TAS6424_FAULT_OC_CH3))
+		dev_crit(dev, "experienced a channel 3 overcurrent fault\n");
+
+	if ((reg & TAS6424_FAULT_OC_CH4) && !(tas6424->last_cfault & TAS6424_FAULT_OC_CH4))
+		dev_crit(dev, "experienced a channel 4 overcurrent fault\n");
+
+	if ((reg & TAS6424_FAULT_DC_CH1) && !(tas6424->last_cfault & TAS6424_FAULT_DC_CH1))
+		dev_crit(dev, "experienced a channel 1 DC fault\n");
+
+	if ((reg & TAS6424_FAULT_DC_CH2) && !(tas6424->last_cfault & TAS6424_FAULT_DC_CH2))
+		dev_crit(dev, "experienced a channel 2 DC fault\n");
+
+	if ((reg & TAS6424_FAULT_DC_CH3) && !(tas6424->last_cfault & TAS6424_FAULT_DC_CH3))
+		dev_crit(dev, "experienced a channel 3 DC fault\n");
+
+	if ((reg & TAS6424_FAULT_DC_CH4) && !(tas6424->last_cfault & TAS6424_FAULT_DC_CH4))
+		dev_crit(dev, "experienced a channel 4 DC fault\n");
+
+	/* Store current fault1 value so we can detect any changes next time */
+	tas6424->last_cfault = reg;
+
+check_global_fault1_reg:
 	ret = regmap_read(tas6424->regmap, TAS6424_GLOB_FAULT1, &reg);
 	if (ret < 0) {
-		dev_err(dev, "failed to read FAULT1 register: %d\n", ret);
+		dev_err(dev, "failed to read GLOB_FAULT1 register: %d\n", ret);
 		goto out;
 	}
 
@@ -429,12 +475,6 @@ static void tas6424_fault_check_work(struct work_struct *work)
 		goto check_global_fault2_reg;
 	}
 
-	/*
-	 * Only flag errors once for a given occurrence. This is needed as
-	 * the TAS6424 will take time clearing the fault condition internally
-	 * during which we don't want to bombard the system with the same
-	 * error message over and over.
-	 */
 	if ((reg & TAS6424_FAULT_PVDD_OV) && !(tas6424->last_fault1 & TAS6424_FAULT_PVDD_OV))
 		dev_crit(dev, "experienced a PVDD overvoltage fault\n");
 
@@ -453,7 +493,7 @@ static void tas6424_fault_check_work(struct work_struct *work)
 check_global_fault2_reg:
 	ret = regmap_read(tas6424->regmap, TAS6424_GLOB_FAULT2, &reg);
 	if (ret < 0) {
-		dev_err(dev, "failed to read FAULT2 register: %d\n", ret);
+		dev_err(dev, "failed to read GLOB_FAULT2 register: %d\n", ret);
 		goto out;
 	}
 
@@ -530,7 +570,7 @@ check_warn_reg:
 	/* Store current warn value so we can detect any changes next time */
 	tas6424->last_warn = reg;
 
-	/* Clear any faults by toggling the CLEAR_FAULT control bit */
+	/* Clear any warnings by toggling the CLEAR_FAULT control bit */
 	ret = regmap_write_bits(tas6424->regmap, TAS6424_MISC_CTRL3,
 				TAS6424_CLEAR_FAULT, TAS6424_CLEAR_FAULT);
 	if (ret < 0)
diff --git a/sound/soc/codecs/tas6424.h b/sound/soc/codecs/tas6424.h
index b5958c45ed0e..c67a7835ca66 100644
--- a/sound/soc/codecs/tas6424.h
+++ b/sound/soc/codecs/tas6424.h
@@ -116,6 +116,16 @@
 #define TAS6424_LDGBYPASS_MASK		BIT(TAS6424_LDGBYPASS_SHIFT)
 
 /* TAS6424_GLOB_FAULT1_REG */
+#define TAS6424_FAULT_OC_CH1		BIT(7)
+#define TAS6424_FAULT_OC_CH2		BIT(6)
+#define TAS6424_FAULT_OC_CH3		BIT(5)
+#define TAS6424_FAULT_OC_CH4		BIT(4)
+#define TAS6424_FAULT_DC_CH1		BIT(3)
+#define TAS6424_FAULT_DC_CH2		BIT(2)
+#define TAS6424_FAULT_DC_CH3		BIT(1)
+#define TAS6424_FAULT_DC_CH4		BIT(0)
+
+/* TAS6424_GLOB_FAULT1_REG */
 #define TAS6424_FAULT_CLOCK		BIT(4)
 #define TAS6424_FAULT_PVDD_OV		BIT(3)
 #define TAS6424_FAULT_VBAT_OV		BIT(2)
diff --git a/sound/soc/codecs/tlv320aic31xx.c b/sound/soc/codecs/tlv320aic31xx.c
index bf92d36b8f8a..608ad49ad978 100644
--- a/sound/soc/codecs/tlv320aic31xx.c
+++ b/sound/soc/codecs/tlv320aic31xx.c
@@ -167,6 +167,7 @@ struct aic31xx_priv {
 	u8 p_div;
 	int rate_div_line;
 	bool master_dapm_route_applied;
+	int irq;
 };
 
 struct aic31xx_rate_divs {
@@ -1391,6 +1392,69 @@ static const struct acpi_device_id aic31xx_acpi_match[] = {
 MODULE_DEVICE_TABLE(acpi, aic31xx_acpi_match);
 #endif
 
+static irqreturn_t aic31xx_irq(int irq, void *data)
+{
+	struct aic31xx_priv *aic31xx = data;
+	struct device *dev = aic31xx->dev;
+	unsigned int value;
+	bool handled = false;
+	int ret;
+
+	ret = regmap_read(aic31xx->regmap, AIC31XX_INTRDACFLAG, &value);
+	if (ret) {
+		dev_err(dev, "Failed to read interrupt mask: %d\n", ret);
+		goto exit;
+	}
+
+	if (value)
+		handled = true;
+	else
+		goto read_overflow;
+
+	if (value & AIC31XX_HPLSCDETECT)
+		dev_err(dev, "Short circuit on Left output is detected\n");
+	if (value & AIC31XX_HPRSCDETECT)
+		dev_err(dev, "Short circuit on Right output is detected\n");
+	if (value & ~(AIC31XX_HPLSCDETECT |
+		      AIC31XX_HPRSCDETECT))
+		dev_err(dev, "Unknown DAC interrupt flags: 0x%08x\n", value);
+
+read_overflow:
+	ret = regmap_read(aic31xx->regmap, AIC31XX_OFFLAG, &value);
+	if (ret) {
+		dev_err(dev, "Failed to read overflow flag: %d\n", ret);
+		goto exit;
+	}
+
+	if (value)
+		handled = true;
+	else
+		goto exit;
+
+	if (value & AIC31XX_DAC_OF_LEFT)
+		dev_warn(dev, "Left-channel DAC overflow has occurred\n");
+	if (value & AIC31XX_DAC_OF_RIGHT)
+		dev_warn(dev, "Right-channel DAC overflow has occurred\n");
+	if (value & AIC31XX_DAC_OF_SHIFTER)
+		dev_warn(dev, "DAC barrel shifter overflow has occurred\n");
+	if (value & AIC31XX_ADC_OF)
+		dev_warn(dev, "ADC overflow has occurred\n");
+	if (value & AIC31XX_ADC_OF_SHIFTER)
+		dev_warn(dev, "ADC barrel shifter overflow has occurred\n");
+	if (value & ~(AIC31XX_DAC_OF_LEFT |
+		      AIC31XX_DAC_OF_RIGHT |
+		      AIC31XX_DAC_OF_SHIFTER |
+		      AIC31XX_ADC_OF |
+		      AIC31XX_ADC_OF_SHIFTER))
+		dev_warn(dev, "Unknown overflow interrupt flags: 0x%08x\n", value);
+
+exit:
+	if (handled)
+		return IRQ_HANDLED;
+	else
+		return IRQ_NONE;
+}
+
 static int aic31xx_i2c_probe(struct i2c_client *i2c,
 			     const struct i2c_device_id *id)
 {
@@ -1413,6 +1477,7 @@ static int aic31xx_i2c_probe(struct i2c_client *i2c,
 		return ret;
 	}
 	aic31xx->dev = &i2c->dev;
+	aic31xx->irq = i2c->irq;
 
 	aic31xx->codec_type = id->driver_data;
 
@@ -1456,6 +1521,26 @@ static int aic31xx_i2c_probe(struct i2c_client *i2c,
 		return ret;
 	}
 
+	if (aic31xx->irq > 0) {
+		regmap_update_bits(aic31xx->regmap, AIC31XX_GPIO1,
+				   AIC31XX_GPIO1_FUNC_MASK,
+				   AIC31XX_GPIO1_INT1 <<
+				   AIC31XX_GPIO1_FUNC_SHIFT);
+
+		regmap_write(aic31xx->regmap, AIC31XX_INT1CTRL,
+			     AIC31XX_SC |
+			     AIC31XX_ENGINE);
+
+		ret = devm_request_threaded_irq(aic31xx->dev, aic31xx->irq,
+						NULL, aic31xx_irq,
+						IRQF_ONESHOT, "aic31xx-irq",
+						aic31xx);
+		if (ret) {
+			dev_err(aic31xx->dev, "Unable to request IRQ\n");
+			return ret;
+		}
+	}
+
 	if (aic31xx->codec_type & DAC31XX_BIT)
 		return devm_snd_soc_register_component(&i2c->dev,
 				&soc_codec_driver_aic31xx,
diff --git a/sound/soc/codecs/tlv320aic31xx.h b/sound/soc/codecs/tlv320aic31xx.h
index 0b587585b38b..2636f2c6bc79 100644
--- a/sound/soc/codecs/tlv320aic31xx.h
+++ b/sound/soc/codecs/tlv320aic31xx.h
@@ -173,6 +173,13 @@ struct aic31xx_pdata {
 #define AIC31XX_HPRDRVPWRSTATUS_MASK	BIT(1)
 #define AIC31XX_SPRDRVPWRSTATUS_MASK	BIT(0)
 
+/* AIC31XX_OFFLAG */
+#define AIC31XX_DAC_OF_LEFT		BIT(7)
+#define AIC31XX_DAC_OF_RIGHT		BIT(6)
+#define AIC31XX_DAC_OF_SHIFTER		BIT(5)
+#define AIC31XX_ADC_OF			BIT(3)
+#define AIC31XX_ADC_OF_SHIFTER		BIT(1)
+
 /* AIC31XX_INTRDACFLAG */
 #define AIC31XX_HPLSCDETECT		BIT(7)
 #define AIC31XX_HPRSCDETECT		BIT(6)
@@ -191,6 +198,22 @@ struct aic31xx_pdata {
 #define AIC31XX_SC			BIT(3)
 #define AIC31XX_ENGINE			BIT(2)
 
+/* AIC31XX_GPIO1 */
+#define AIC31XX_GPIO1_FUNC_MASK		GENMASK(5, 2)
+#define AIC31XX_GPIO1_FUNC_SHIFT	2
+#define AIC31XX_GPIO1_DISABLED		0x00
+#define AIC31XX_GPIO1_INPUT		0x01
+#define AIC31XX_GPIO1_GPI		0x02
+#define AIC31XX_GPIO1_GPO		0x03
+#define AIC31XX_GPIO1_CLKOUT		0x04
+#define AIC31XX_GPIO1_INT1		0x05
+#define AIC31XX_GPIO1_INT2		0x06
+#define AIC31XX_GPIO1_ADC_WCLK		0x07
+#define AIC31XX_GPIO1_SBCLK		0x08
+#define AIC31XX_GPIO1_SWCLK		0x09
+#define AIC31XX_GPIO1_ADC_MOD_CLK	0x10
+#define AIC31XX_GPIO1_SDOUT		0x11
+
 /* AIC31XX_DACSETUP */
 #define AIC31XX_SOFTSTEP_MASK		GENMASK(1, 0)
 
diff --git a/sound/soc/codecs/tscs454.c b/sound/soc/codecs/tscs454.c
index ff85a0bf6170..93d84e5ae2d5 100644
--- a/sound/soc/codecs/tscs454.c
+++ b/sound/soc/codecs/tscs454.c
@@ -3459,7 +3459,7 @@ static int tscs454_i2c_probe(struct i2c_client *i2c,
 	/* Sync pg sel reg with cache */
 	regmap_write(tscs454->regmap, R_PAGESEL, 0x00);
 
-	ret = snd_soc_register_component(&i2c->dev, &soc_component_dev_tscs454,
+	ret = devm_snd_soc_register_component(&i2c->dev, &soc_component_dev_tscs454,
 			tscs454_dais, ARRAY_SIZE(tscs454_dais));
 	if (ret) {
 		dev_err(&i2c->dev, "Failed to register component (%d)\n", ret);
diff --git a/sound/soc/codecs/wm2000.c b/sound/soc/codecs/wm2000.c
index c5ae07234a00..bba330e30162 100644
--- a/sound/soc/codecs/wm2000.c
+++ b/sound/soc/codecs/wm2000.c
@@ -88,19 +88,6 @@ static int wm2000_write(struct i2c_client *i2c, unsigned int reg,
 	return regmap_write(wm2000->regmap, reg, value);
 }
 
-static unsigned int wm2000_read(struct i2c_client *i2c, unsigned int r)
-{
-	struct wm2000_priv *wm2000 = i2c_get_clientdata(i2c);
-	unsigned int val;
-	int ret;
-
-	ret = regmap_read(wm2000->regmap, r, &val);
-	if (ret < 0)
-		return -1;
-
-	return val;
-}
-
 static void wm2000_reset(struct wm2000_priv *wm2000)
 {
 	struct i2c_client *i2c = wm2000->i2c;
@@ -115,14 +102,15 @@ static void wm2000_reset(struct wm2000_priv *wm2000)
 static int wm2000_poll_bit(struct i2c_client *i2c,
 			   unsigned int reg, u8 mask)
 {
+	struct wm2000_priv *wm2000 = i2c_get_clientdata(i2c);
 	int timeout = 4000;
-	int val;
+	unsigned int val;
 
-	val = wm2000_read(i2c, reg);
+	regmap_read(wm2000->regmap, reg, &val);
 
 	while (!(val & mask) && --timeout) {
 		msleep(1);
-		val = wm2000_read(i2c, reg);
+		regmap_read(wm2000->regmap, reg, &val);
 	}
 
 	if (timeout == 0)
@@ -135,6 +123,7 @@ static int wm2000_power_up(struct i2c_client *i2c, int analogue)
 {
 	struct wm2000_priv *wm2000 = dev_get_drvdata(&i2c->dev);
 	unsigned long rate;
+	unsigned int val;
 	int ret;
 
 	if (WARN_ON(wm2000->anc_mode != ANC_OFF))
@@ -213,12 +202,17 @@ static int wm2000_power_up(struct i2c_client *i2c, int analogue)
 			     WM2000_MODE_THERMAL_ENABLE);
 	}
 
-	ret = wm2000_read(i2c, WM2000_REG_SPEECH_CLARITY);
+	ret = regmap_read(wm2000->regmap, WM2000_REG_SPEECH_CLARITY, &val);
+	if (ret != 0) {
+		dev_err(&i2c->dev, "Unable to read Speech Clarity: %d\n", ret);
+		regulator_bulk_disable(WM2000_NUM_SUPPLIES, wm2000->supplies);
+		return ret;
+	}
 	if (wm2000->speech_clarity)
-		ret |= WM2000_SPEECH_CLARITY;
+		val |= WM2000_SPEECH_CLARITY;
 	else
-		ret &= ~WM2000_SPEECH_CLARITY;
-	wm2000_write(i2c, WM2000_REG_SPEECH_CLARITY, ret);
+		val &= ~WM2000_SPEECH_CLARITY;
+	wm2000_write(i2c, WM2000_REG_SPEECH_CLARITY, val);
 
 	wm2000_write(i2c, WM2000_REG_SYS_START0, 0x33);
 	wm2000_write(i2c, WM2000_REG_SYS_START1, 0x02);
@@ -824,7 +818,7 @@ static int wm2000_i2c_probe(struct i2c_client *i2c,
 	const char *filename;
 	const struct firmware *fw = NULL;
 	int ret, i;
-	int reg;
+	unsigned int reg;
 	u16 id;
 
 	wm2000 = devm_kzalloc(&i2c->dev, sizeof(*wm2000), GFP_KERNEL);
@@ -860,9 +854,17 @@ static int wm2000_i2c_probe(struct i2c_client *i2c,
 	}
 
 	/* Verify that this is a WM2000 */
-	reg = wm2000_read(i2c, WM2000_REG_ID1);
+	ret = regmap_read(wm2000->regmap, WM2000_REG_ID1, &reg);
+	if (ret != 0) {
+		dev_err(&i2c->dev, "Unable to read ID1: %d\n", ret);
+		return ret;
+	}
 	id = reg << 8;
-	reg = wm2000_read(i2c, WM2000_REG_ID2);
+	ret = regmap_read(wm2000->regmap, WM2000_REG_ID2, &reg);
+	if (ret != 0) {
+		dev_err(&i2c->dev, "Unable to read ID2: %d\n", ret);
+		return ret;
+	}
 	id |= reg & 0xff;
 
 	if (id != 0x2000) {
@@ -871,7 +873,11 @@ static int wm2000_i2c_probe(struct i2c_client *i2c,
 		goto err_supplies;
 	}
 
-	reg = wm2000_read(i2c, WM2000_REG_REVISON);
+	ret = regmap_read(wm2000->regmap, WM2000_REG_REVISON, &reg);
+	if (ret != 0) {
+		dev_err(&i2c->dev, "Unable to read Revision: %d\n", ret);
+		return ret;
+	}
 	dev_info(&i2c->dev, "revision %c\n", reg + 'A');
 
 	wm2000->mclk = devm_clk_get(&i2c->dev, "MCLK");
diff --git a/sound/soc/codecs/wm8782.c b/sound/soc/codecs/wm8782.c
index 317db9a149a7..cf2cdbece122 100644
--- a/sound/soc/codecs/wm8782.c
+++ b/sound/soc/codecs/wm8782.c
@@ -20,6 +20,7 @@
 #include <linux/module.h>
 #include <linux/kernel.h>
 #include <linux/device.h>
+#include <linux/regulator/consumer.h>
 #include <sound/core.h>
 #include <sound/pcm.h>
 #include <sound/ac97_codec.h>
@@ -50,7 +51,51 @@ static struct snd_soc_dai_driver wm8782_dai = {
 	},
 };
 
+/* regulator power supply names */
+static const char *supply_names[] = {
+	"Vdda", /* analog supply, 2.7V - 3.6V */
+	"Vdd",  /* digital supply, 2.7V - 5.5V */
+};
+
+struct wm8782_priv {
+	struct regulator_bulk_data supplies[ARRAY_SIZE(supply_names)];
+};
+
+static int wm8782_soc_probe(struct snd_soc_component *component)
+{
+	struct wm8782_priv *priv = snd_soc_component_get_drvdata(component);
+	return regulator_bulk_enable(ARRAY_SIZE(priv->supplies), priv->supplies);
+}
+
+static void wm8782_soc_remove(struct snd_soc_component *component)
+{
+	struct wm8782_priv *priv = snd_soc_component_get_drvdata(component);
+	regulator_bulk_disable(ARRAY_SIZE(priv->supplies), priv->supplies);
+}
+
+#ifdef CONFIG_PM
+static int wm8782_soc_suspend(struct snd_soc_component *component)
+{
+	struct wm8782_priv *priv = snd_soc_component_get_drvdata(component);
+	regulator_bulk_disable(ARRAY_SIZE(priv->supplies), priv->supplies);
+	return 0;
+}
+
+static int wm8782_soc_resume(struct snd_soc_component *component)
+{
+	struct wm8782_priv *priv = snd_soc_component_get_drvdata(component);
+	return regulator_bulk_enable(ARRAY_SIZE(priv->supplies), priv->supplies);
+}
+#else
+#define wm8782_soc_suspend      NULL
+#define wm8782_soc_resume       NULL
+#endif /* CONFIG_PM */
+
 static const struct snd_soc_component_driver soc_component_dev_wm8782 = {
+	.probe			= wm8782_soc_probe,
+	.remove			= wm8782_soc_remove,
+	.suspend		= wm8782_soc_suspend,
+	.resume			= wm8782_soc_resume,
 	.dapm_widgets		= wm8782_dapm_widgets,
 	.num_dapm_widgets	= ARRAY_SIZE(wm8782_dapm_widgets),
 	.dapm_routes		= wm8782_dapm_routes,
@@ -63,6 +108,24 @@ static const struct snd_soc_component_driver soc_component_dev_wm8782 = {
 
 static int wm8782_probe(struct platform_device *pdev)
 {
+	struct device *dev = &pdev->dev;
+	struct wm8782_priv *priv;
+	int ret, i;
+
+	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
+
+	dev_set_drvdata(dev, priv);
+
+	for (i = 0; i < ARRAY_SIZE(supply_names); i++)
+		priv->supplies[i].supply = supply_names[i];
+
+	ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(priv->supplies),
+				      priv->supplies);
+	if (ret < 0)
+		return ret;
+
 	return devm_snd_soc_register_component(&pdev->dev,
 			&soc_component_dev_wm8782, &wm8782_dai, 1);
 }
diff --git a/sound/soc/codecs/wm8904.c b/sound/soc/codecs/wm8904.c
index 1965635ec07c..2a3e5fbd04e4 100644
--- a/sound/soc/codecs/wm8904.c
+++ b/sound/soc/codecs/wm8904.c
@@ -13,7 +13,6 @@
 
 #include <linux/clk.h>
 #include <linux/module.h>
-#include <linux/moduleparam.h>
 #include <linux/init.h>
 #include <linux/delay.h>
 #include <linux/pm.h>
diff --git a/sound/soc/codecs/wm8974.c b/sound/soc/codecs/wm8974.c
index 43edaf8cd276..593a11960888 100644
--- a/sound/soc/codecs/wm8974.c
+++ b/sound/soc/codecs/wm8974.c
@@ -11,7 +11,6 @@
  */
 
 #include <linux/module.h>
-#include <linux/moduleparam.h>
 #include <linux/kernel.h>
 #include <linux/init.h>
 #include <linux/delay.h>
diff --git a/sound/soc/codecs/wm9712.c b/sound/soc/codecs/wm9712.c
index ade34c26ad2f..e873baa9e778 100644
--- a/sound/soc/codecs/wm9712.c
+++ b/sound/soc/codecs/wm9712.c
@@ -638,13 +638,14 @@ static int wm9712_soc_probe(struct snd_soc_component *component)
 {
 	struct wm9712_priv *wm9712 = snd_soc_component_get_drvdata(component);
 	struct regmap *regmap;
-	int ret;
 
 	if (wm9712->mfd_pdata) {
 		wm9712->ac97 = wm9712->mfd_pdata->ac97;
 		regmap = wm9712->mfd_pdata->regmap;
 	} else {
 #ifdef CONFIG_SND_SOC_AC97_BUS
+		int ret;
+
 		wm9712->ac97 = snd_soc_new_ac97_component(component, WM9712_VENDOR_ID,
 						      WM9712_VENDOR_ID_MASK);
 		if (IS_ERR(wm9712->ac97)) {
diff --git a/sound/soc/codecs/wm_adsp.c b/sound/soc/codecs/wm_adsp.c
index f61656070225..a53dc174bbf0 100644
--- a/sound/soc/codecs/wm_adsp.c
+++ b/sound/soc/codecs/wm_adsp.c
@@ -311,12 +311,12 @@ struct wm_adsp_alg_xm_struct {
 };
 
 struct wm_adsp_buffer {
-	__be32 X_buf_base;		/* XM base addr of first X area */
-	__be32 X_buf_size;		/* Size of 1st X area in words */
-	__be32 X_buf_base2;		/* XM base addr of 2nd X area */
-	__be32 X_buf_brk;		/* Total X size in words */
-	__be32 Y_buf_base;		/* YM base addr of Y area */
-	__be32 wrap;			/* Total size X and Y in words */
+	__be32 buf1_base;		/* Base addr of first buffer area */
+	__be32 buf1_size;		/* Size of buf1 area in DSP words */
+	__be32 buf2_base;		/* Base addr of 2nd buffer area */
+	__be32 buf1_buf2_size;		/* Size of buf1+buf2 in DSP words */
+	__be32 buf3_base;		/* Base addr of buf3 area */
+	__be32 buf_total_size;		/* Size of buf1+buf2+buf3 in DSP words */
 	__be32 high_water_mark;		/* Point at which IRQ is asserted */
 	__be32 irq_count;		/* bits 1-31 count IRQ assertions */
 	__be32 irq_ack;			/* acked IRQ count, bit 0 enables IRQ */
@@ -393,18 +393,18 @@ struct wm_adsp_buffer_region_def {
 static const struct wm_adsp_buffer_region_def default_regions[] = {
 	{
 		.mem_type = WMFW_ADSP2_XM,
-		.base_offset = HOST_BUFFER_FIELD(X_buf_base),
-		.size_offset = HOST_BUFFER_FIELD(X_buf_size),
+		.base_offset = HOST_BUFFER_FIELD(buf1_base),
+		.size_offset = HOST_BUFFER_FIELD(buf1_size),
 	},
 	{
 		.mem_type = WMFW_ADSP2_XM,
-		.base_offset = HOST_BUFFER_FIELD(X_buf_base2),
-		.size_offset = HOST_BUFFER_FIELD(X_buf_brk),
+		.base_offset = HOST_BUFFER_FIELD(buf2_base),
+		.size_offset = HOST_BUFFER_FIELD(buf1_buf2_size),
 	},
 	{
 		.mem_type = WMFW_ADSP2_YM,
-		.base_offset = HOST_BUFFER_FIELD(Y_buf_base),
-		.size_offset = HOST_BUFFER_FIELD(wrap),
+		.base_offset = HOST_BUFFER_FIELD(buf3_base),
+		.size_offset = HOST_BUFFER_FIELD(buf_total_size),
 	},
 };
 
@@ -3345,7 +3345,7 @@ static int wm_adsp_buffer_populate(struct wm_adsp_compr_buf *buf)
 		region->cumulative_size = offset;
 
 		adsp_dbg(buf->dsp,
-			 "region=%d type=%d base=%04x off=%04x size=%04x\n",
+			 "region=%d type=%d base=%08x off=%08x size=%08x\n",
 			 i, region->mem_type, region->base_addr,
 			 region->offset, region->cumulative_size);
 	}