Blob Blame History Raw
From 62d5ae4cafb7ffeeec6ba2dd1814cafeeea7dd8f Mon Sep 17 00:00:00 2001
From: Tzung-Bi Shih <tzungbi@google.com>
Date: Thu, 28 Nov 2019 23:19:08 +0800
Subject: [PATCH] ASoC: max98090: save and restore SHDN when changing sensitive registers
Git-commit: 62d5ae4cafb7ffeeec6ba2dd1814cafeeea7dd8f
Patch-mainline: v5.6-rc1
References: jsc#SLE-16518

According to the datasheet, there are some registers can only be changed
when SHDN is 0.  Changing these settings during SHDN = 1 can compromise
device stability and performance specifications.

Saves SHDN before writing to these sensitive registers and restores SHDN
afterward.

Here is the register list codec driver of max98090 wants to change:
M98090_REG_QUICK_SYSTEM_CLOCK		0x04
M98090_REG_QUICK_SAMPLE_RATE		0x05
M98090_REG_DAI_INTERFACE		0x06
M98090_REG_DAC_PATH			0x07
M98090_REG_MIC_DIRECT_TO_ADC		0x08
M98090_REG_LINE_TO_ADC			0x09
M98090_REG_ANALOG_MIC_LOOP		0x0A
M98090_REG_ANALOG_LINE_LOOP		0x0B
M98090_REG_SYSTEM_CLOCK			0x1B
M98090_REG_CLOCK_MODE			0x1C
M98090_REG_CLOCK_RATIO_NI_MSB		0x1D
M98090_REG_CLOCK_RATIO_NI_LSB		0x1E
M98090_REG_CLOCK_RATIO_MI_MSB		0x1F
M98090_REG_CLOCK_RATIO_MI_LSB		0x20
M98090_REG_MASTER_MODE			0x21
M98090_REG_INTERFACE_FORMAT		0x22
M98090_REG_TDM_CONTROL			0x23
M98090_REG_TDM_FORMAT			0x24
M98090_REG_IO_CONFIGURATION		0x25
M98090_REG_FILTER_CONFIG		0x26
M98090_REG_INPUT_ENABLE			0x3E
M98090_REG_OUTPUT_ENABLE		0x3F
M98090_REG_BIAS_CONTROL			0x42
M98090_REG_DAC_CONTROL			0x43
M98090_REG_ADC_CONTROL			0x44
M98090_REG_DRC_TIMING			0x33
M98090_REG_DRC_COMPRESSOR		0x34
M98090_REG_DRC_EXPANDER			0x35
M98090_REG_DSP_FILTER_ENABLE		0x41
M98090_REG_EQUALIZER_BASE		0x46
M98090_REG_RECORD_BIQUAD_BASE		0xAF
M98090_REG_DIGITAL_MIC_ENABLE		0x13
M98090_REG_DIGITAL_MIC_CONFIG		0x14

Signed-off-by: Tzung-Bi Shih <tzungbi@google.com>
Link: https://lore.kernel.org/r/20191128151908.180871-1-tzungbi@google.com
Signed-off-by: Mark Brown <broonie@kernel.org>
Acked-by: Takashi Iwai <tiwai@suse.de>

---
 sound/soc/codecs/max98090.c |  433 +++++++++++++++++++++++++++++++-------------
 sound/soc/codecs/max98090.h |    3 
 2 files changed, 312 insertions(+), 124 deletions(-)

--- a/sound/soc/codecs/max98090.c
+++ b/sound/soc/codecs/max98090.c
@@ -5,24 +5,149 @@
  * Copyright 2011-2012 Maxim Integrated Products
  */
 
+#include <linux/acpi.h>
+#include <linux/clk.h>
 #include <linux/delay.h>
 #include <linux/i2c.h>
 #include <linux/module.h>
+#include <linux/mutex.h>
 #include <linux/of.h>
 #include <linux/pm.h>
 #include <linux/pm_runtime.h>
 #include <linux/regmap.h>
 #include <linux/slab.h>
-#include <linux/acpi.h>
-#include <linux/clk.h>
 #include <sound/jack.h>
+#include <sound/max98090.h>
 #include <sound/pcm.h>
 #include <sound/pcm_params.h>
 #include <sound/soc.h>
 #include <sound/tlv.h>
-#include <sound/max98090.h>
 #include "max98090.h"
 
+static void max98090_shdn_save_locked(struct max98090_priv *max98090)
+{
+	int shdn = 0;
+
+	/* saved_shdn, saved_count, SHDN are protected by card->dapm_mutex */
+	regmap_read(max98090->regmap, M98090_REG_DEVICE_SHUTDOWN, &shdn);
+	max98090->saved_shdn |= shdn;
+	++max98090->saved_count;
+
+	if (shdn)
+		regmap_write(max98090->regmap, M98090_REG_DEVICE_SHUTDOWN, 0x0);
+}
+
+static void max98090_shdn_restore_locked(struct max98090_priv *max98090)
+{
+	/* saved_shdn, saved_count, SHDN are protected by card->dapm_mutex */
+	if (--max98090->saved_count == 0) {
+		if (max98090->saved_shdn) {
+			regmap_write(max98090->regmap,
+				     M98090_REG_DEVICE_SHUTDOWN,
+				     M98090_SHDNN_MASK);
+			max98090->saved_shdn = 0;
+		}
+	}
+}
+
+static void max98090_shdn_save(struct max98090_priv *max98090)
+{
+	mutex_lock(&max98090->component->card->dapm_mutex);
+	max98090_shdn_save_locked(max98090);
+}
+
+static void max98090_shdn_restore(struct max98090_priv *max98090)
+{
+	max98090_shdn_restore_locked(max98090);
+	mutex_unlock(&max98090->component->card->dapm_mutex);
+}
+
+static int max98090_put_volsw(struct snd_kcontrol *kcontrol,
+	struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_component *component =
+		snd_soc_kcontrol_component(kcontrol);
+	struct max98090_priv *max98090 =
+		snd_soc_component_get_drvdata(component);
+	int ret;
+
+	max98090_shdn_save(max98090);
+	ret = snd_soc_put_volsw(kcontrol, ucontrol);
+	max98090_shdn_restore(max98090);
+
+	return ret;
+}
+
+static int max98090_dapm_put_enum_double(struct snd_kcontrol *kcontrol,
+	struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_component *component =
+		snd_soc_kcontrol_component(kcontrol);
+	struct max98090_priv *max98090 =
+		snd_soc_component_get_drvdata(component);
+	int ret;
+
+	max98090_shdn_save(max98090);
+	ret = snd_soc_dapm_put_enum_double(kcontrol, ucontrol);
+	max98090_shdn_restore(max98090);
+
+	return ret;
+}
+
+static int max98090_put_enum_double(struct snd_kcontrol *kcontrol,
+	struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_component *component =
+		snd_soc_kcontrol_component(kcontrol);
+	struct max98090_priv *max98090 =
+		snd_soc_component_get_drvdata(component);
+	int ret;
+
+	max98090_shdn_save(max98090);
+	ret = snd_soc_put_enum_double(kcontrol, ucontrol);
+	max98090_shdn_restore(max98090);
+
+	return ret;
+}
+
+static int max98090_bytes_put(struct snd_kcontrol *kcontrol,
+	struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_component *component =
+		snd_soc_kcontrol_component(kcontrol);
+	struct max98090_priv *max98090 =
+		snd_soc_component_get_drvdata(component);
+	int ret;
+
+	max98090_shdn_save(max98090);
+	ret = snd_soc_bytes_put(kcontrol, ucontrol);
+	max98090_shdn_restore(max98090);
+
+	return ret;
+}
+
+static int max98090_dapm_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);
+	struct max98090_priv *max98090 =
+		snd_soc_component_get_drvdata(component);
+
+	switch (event) {
+	case SND_SOC_DAPM_PRE_PMU:
+	case SND_SOC_DAPM_PRE_PMD:
+		max98090_shdn_save_locked(max98090);
+		break;
+	case SND_SOC_DAPM_POST_PMU:
+	case SND_SOC_DAPM_POST_PMD:
+		max98090_shdn_restore_locked(max98090);
+		break;
+	}
+
+	return 0;
+}
+
 /* Allows for sparsely populated register maps */
 static const struct reg_default max98090_reg[] = {
 	{ 0x00, 0x00 }, /* 00 Software Reset */
@@ -506,10 +631,13 @@ static SOC_ENUM_SINGLE_DECL(max98090_adc
 			    max98090_pwr_perf_text);
 
 static const struct snd_kcontrol_new max98090_snd_controls[] = {
-	SOC_ENUM("MIC Bias VCM Bandgap", max98090_vcmbandgap_enum),
+	SOC_ENUM_EXT("MIC Bias VCM Bandgap", max98090_vcmbandgap_enum,
+		snd_soc_get_enum_double, max98090_put_enum_double),
 
-	SOC_SINGLE("DMIC MIC Comp Filter Config", M98090_REG_DIGITAL_MIC_CONFIG,
-		M98090_DMIC_COMP_SHIFT, M98090_DMIC_COMP_NUM - 1, 0),
+	SOC_SINGLE_EXT("DMIC MIC Comp Filter Config",
+		M98090_REG_DIGITAL_MIC_CONFIG,
+		M98090_DMIC_COMP_SHIFT, M98090_DMIC_COMP_NUM - 1, 0,
+		snd_soc_get_volsw, max98090_put_volsw),
 
 	SOC_SINGLE_EXT_TLV("MIC1 Boost Volume",
 		M98090_REG_MIC1_INPUT_LEVEL, M98090_MIC_PA1EN_SHIFT,
@@ -564,24 +692,34 @@ static const struct snd_kcontrol_new max
 		M98090_AVR_SHIFT, M98090_AVR_NUM - 1, 1,
 		max98090_av_tlv),
 
-	SOC_ENUM("ADC Oversampling Rate", max98090_osr128_enum),
-	SOC_SINGLE("ADC Quantizer Dither", M98090_REG_ADC_CONTROL,
-		M98090_ADCDITHER_SHIFT, M98090_ADCDITHER_NUM - 1, 0),
-	SOC_ENUM("ADC High Performance Mode", max98090_adchp_enum),
-
-	SOC_SINGLE("DAC Mono Mode", M98090_REG_IO_CONFIGURATION,
-		M98090_DMONO_SHIFT, M98090_DMONO_NUM - 1, 0),
-	SOC_SINGLE("SDIN Mode", M98090_REG_IO_CONFIGURATION,
-		M98090_SDIEN_SHIFT, M98090_SDIEN_NUM - 1, 0),
-	SOC_SINGLE("SDOUT Mode", M98090_REG_IO_CONFIGURATION,
-		M98090_SDOEN_SHIFT, M98090_SDOEN_NUM - 1, 0),
-	SOC_SINGLE("SDOUT Hi-Z Mode", M98090_REG_IO_CONFIGURATION,
-		M98090_HIZOFF_SHIFT, M98090_HIZOFF_NUM - 1, 1),
-	SOC_ENUM("Filter Mode", max98090_mode_enum),
-	SOC_SINGLE("Record Path DC Blocking", M98090_REG_FILTER_CONFIG,
-		M98090_AHPF_SHIFT, M98090_AHPF_NUM - 1, 0),
-	SOC_SINGLE("Playback Path DC Blocking", M98090_REG_FILTER_CONFIG,
-		M98090_DHPF_SHIFT, M98090_DHPF_NUM - 1, 0),
+	SOC_ENUM_EXT("ADC Oversampling Rate", max98090_osr128_enum,
+		snd_soc_get_enum_double, max98090_put_enum_double),
+	SOC_SINGLE_EXT("ADC Quantizer Dither", M98090_REG_ADC_CONTROL,
+		M98090_ADCDITHER_SHIFT, M98090_ADCDITHER_NUM - 1, 0,
+		snd_soc_get_volsw, max98090_put_volsw),
+	SOC_ENUM_EXT("ADC High Performance Mode", max98090_adchp_enum,
+		snd_soc_get_enum_double, max98090_put_enum_double),
+
+	SOC_SINGLE_EXT("DAC Mono Mode", M98090_REG_IO_CONFIGURATION,
+		M98090_DMONO_SHIFT, M98090_DMONO_NUM - 1, 0,
+		snd_soc_get_volsw, max98090_put_volsw),
+	SOC_SINGLE_EXT("SDIN Mode", M98090_REG_IO_CONFIGURATION,
+		M98090_SDIEN_SHIFT, M98090_SDIEN_NUM - 1, 0,
+		snd_soc_get_volsw, max98090_put_volsw),
+	SOC_SINGLE_EXT("SDOUT Mode", M98090_REG_IO_CONFIGURATION,
+		M98090_SDOEN_SHIFT, M98090_SDOEN_NUM - 1, 0,
+		snd_soc_get_volsw, max98090_put_volsw),
+	SOC_SINGLE_EXT("SDOUT Hi-Z Mode", M98090_REG_IO_CONFIGURATION,
+		M98090_HIZOFF_SHIFT, M98090_HIZOFF_NUM - 1, 1,
+		snd_soc_get_volsw, max98090_put_volsw),
+	SOC_ENUM_EXT("Filter Mode", max98090_mode_enum,
+		snd_soc_get_enum_double, max98090_put_enum_double),
+	SOC_SINGLE_EXT("Record Path DC Blocking", M98090_REG_FILTER_CONFIG,
+		M98090_AHPF_SHIFT, M98090_AHPF_NUM - 1, 0,
+		snd_soc_get_volsw, max98090_put_volsw),
+	SOC_SINGLE_EXT("Playback Path DC Blocking", M98090_REG_FILTER_CONFIG,
+		M98090_DHPF_SHIFT, M98090_DHPF_NUM - 1, 0,
+		snd_soc_get_volsw, max98090_put_volsw),
 	SOC_SINGLE_TLV("Digital BQ Volume", M98090_REG_ADC_BIQUAD_LEVEL,
 		M98090_AVBQ_SHIFT, M98090_AVBQ_NUM - 1, 1, max98090_dv_tlv),
 	SOC_SINGLE_EXT_TLV("Digital Sidetone Volume",
@@ -594,13 +732,17 @@ static const struct snd_kcontrol_new max
 	SOC_SINGLE_TLV("Digital Volume", M98090_REG_DAI_PLAYBACK_LEVEL,
 		M98090_DV_SHIFT, M98090_DV_NUM - 1, 1,
 		max98090_dv_tlv),
-	SND_SOC_BYTES("EQ Coefficients", M98090_REG_EQUALIZER_BASE, 105),
-	SOC_SINGLE("Digital EQ 3 Band Switch", M98090_REG_DSP_FILTER_ENABLE,
-		M98090_EQ3BANDEN_SHIFT, M98090_EQ3BANDEN_NUM - 1, 0),
-	SOC_SINGLE("Digital EQ 5 Band Switch", M98090_REG_DSP_FILTER_ENABLE,
-		M98090_EQ5BANDEN_SHIFT, M98090_EQ5BANDEN_NUM - 1, 0),
-	SOC_SINGLE("Digital EQ 7 Band Switch", M98090_REG_DSP_FILTER_ENABLE,
-		M98090_EQ7BANDEN_SHIFT, M98090_EQ7BANDEN_NUM - 1, 0),
+	SND_SOC_BYTES_E("EQ Coefficients", M98090_REG_EQUALIZER_BASE, 105,
+		snd_soc_bytes_get, max98090_bytes_put),
+	SOC_SINGLE_EXT("Digital EQ 3 Band Switch", M98090_REG_DSP_FILTER_ENABLE,
+		M98090_EQ3BANDEN_SHIFT, M98090_EQ3BANDEN_NUM - 1, 0,
+		snd_soc_get_volsw, max98090_put_volsw),
+	SOC_SINGLE_EXT("Digital EQ 5 Band Switch", M98090_REG_DSP_FILTER_ENABLE,
+		M98090_EQ5BANDEN_SHIFT, M98090_EQ5BANDEN_NUM - 1, 0,
+		snd_soc_get_volsw, max98090_put_volsw),
+	SOC_SINGLE_EXT("Digital EQ 7 Band Switch", M98090_REG_DSP_FILTER_ENABLE,
+		M98090_EQ7BANDEN_SHIFT, M98090_EQ7BANDEN_NUM - 1, 0,
+		snd_soc_get_volsw, max98090_put_volsw),
 	SOC_SINGLE("Digital EQ Clipping Detection", M98090_REG_DAI_PLAYBACK_LEVEL_EQ,
 		M98090_EQCLPN_SHIFT, M98090_EQCLPN_NUM - 1,
 		1),
@@ -608,25 +750,34 @@ static const struct snd_kcontrol_new max
 		M98090_DVEQ_SHIFT, M98090_DVEQ_NUM - 1, 1,
 		max98090_dv_tlv),
 
-	SOC_SINGLE("ALC Enable", M98090_REG_DRC_TIMING,
-		M98090_DRCEN_SHIFT, M98090_DRCEN_NUM - 1, 0),
-	SOC_ENUM("ALC Attack Time", max98090_drcatk_enum),
-	SOC_ENUM("ALC Release Time", max98090_drcrls_enum),
+	SOC_SINGLE_EXT("ALC Enable", M98090_REG_DRC_TIMING,
+		M98090_DRCEN_SHIFT, M98090_DRCEN_NUM - 1, 0,
+		snd_soc_get_volsw, max98090_put_volsw),
+	SOC_ENUM_EXT("ALC Attack Time", max98090_drcatk_enum,
+		snd_soc_get_enum_double, max98090_put_enum_double),
+	SOC_ENUM_EXT("ALC Release Time", max98090_drcrls_enum,
+		snd_soc_get_enum_double, max98090_put_enum_double),
 	SOC_SINGLE_TLV("ALC Make Up Volume", M98090_REG_DRC_GAIN,
 		M98090_DRCG_SHIFT, M98090_DRCG_NUM - 1, 0,
 		max98090_alcmakeup_tlv),
-	SOC_ENUM("ALC Compression Ratio", max98090_alccmp_enum),
-	SOC_ENUM("ALC Expansion Ratio", max98090_drcexp_enum),
-	SOC_SINGLE_TLV("ALC Compression Threshold Volume",
+	SOC_ENUM_EXT("ALC Compression Ratio", max98090_alccmp_enum,
+		snd_soc_get_enum_double, max98090_put_enum_double),
+	SOC_ENUM_EXT("ALC Expansion Ratio", max98090_drcexp_enum,
+		snd_soc_get_enum_double, max98090_put_enum_double),
+	SOC_SINGLE_EXT_TLV("ALC Compression Threshold Volume",
 		M98090_REG_DRC_COMPRESSOR, M98090_DRCTHC_SHIFT,
-		M98090_DRCTHC_NUM - 1, 1, max98090_alccomp_tlv),
-	SOC_SINGLE_TLV("ALC Expansion Threshold Volume",
+		M98090_DRCTHC_NUM - 1, 1,
+		snd_soc_get_volsw, max98090_put_volsw, max98090_alccomp_tlv),
+	SOC_SINGLE_EXT_TLV("ALC Expansion Threshold Volume",
 		M98090_REG_DRC_EXPANDER, M98090_DRCTHE_SHIFT,
-		M98090_DRCTHE_NUM - 1, 1, max98090_drcexp_tlv),
+		M98090_DRCTHE_NUM - 1, 1,
+		snd_soc_get_volsw, max98090_put_volsw, max98090_drcexp_tlv),
 
-	SOC_ENUM("DAC HP Playback Performance Mode",
-		max98090_dac_perfmode_enum),
-	SOC_ENUM("DAC High Performance Mode", max98090_dachp_enum),
+	SOC_ENUM_EXT("DAC HP Playback Performance Mode",
+		max98090_dac_perfmode_enum,
+		snd_soc_get_enum_double, max98090_put_enum_double),
+	SOC_ENUM_EXT("DAC High Performance Mode", max98090_dachp_enum,
+		snd_soc_get_enum_double, max98090_put_enum_double),
 
 	SOC_SINGLE_TLV("Headphone Left Mixer Volume",
 		M98090_REG_HP_CONTROL, M98090_MIXHPLG_SHIFT,
@@ -684,9 +835,12 @@ static const struct snd_kcontrol_new max
 	SOC_SINGLE("Volume Adjustment Smoothing", M98090_REG_LEVEL_CONTROL,
 		M98090_VSENN_SHIFT, M98090_VSENN_NUM - 1, 1),
 
-	SND_SOC_BYTES("Biquad Coefficients", M98090_REG_RECORD_BIQUAD_BASE, 15),
-	SOC_SINGLE("Biquad Switch", M98090_REG_DSP_FILTER_ENABLE,
-		M98090_ADCBQEN_SHIFT, M98090_ADCBQEN_NUM - 1, 0),
+	SND_SOC_BYTES_E("Biquad Coefficients",
+		M98090_REG_RECORD_BIQUAD_BASE, 15,
+		snd_soc_bytes_get, max98090_bytes_put),
+	SOC_SINGLE_EXT("Biquad Switch", M98090_REG_DSP_FILTER_ENABLE,
+		M98090_ADCBQEN_SHIFT, M98090_ADCBQEN_NUM - 1, 0,
+		snd_soc_get_volsw, max98090_put_volsw),
 };
 
 static const struct snd_kcontrol_new max98091_snd_controls[] = {
@@ -695,10 +849,12 @@ static const struct snd_kcontrol_new max
 		M98090_DMIC34_ZEROPAD_SHIFT,
 		M98090_DMIC34_ZEROPAD_NUM - 1, 0),
 
-	SOC_ENUM("Filter DMIC34 Mode", max98090_filter_dmic34mode_enum),
-	SOC_SINGLE("DMIC34 DC Blocking", M98090_REG_FILTER_CONFIG,
+	SOC_ENUM_EXT("Filter DMIC34 Mode", max98090_filter_dmic34mode_enum,
+		snd_soc_get_enum_double, max98090_put_enum_double),
+	SOC_SINGLE_EXT("DMIC34 DC Blocking", M98090_REG_FILTER_CONFIG,
 		M98090_FLT_DMIC34HPF_SHIFT,
-		M98090_FLT_DMIC34HPF_NUM - 1, 0),
+		M98090_FLT_DMIC34HPF_NUM - 1, 0,
+		snd_soc_get_volsw, max98090_put_volsw),
 
 	SOC_SINGLE_TLV("DMIC3 Boost Volume", M98090_REG_DMIC3_VOLUME,
 		M98090_DMIC_AV3G_SHIFT, M98090_DMIC_AV3G_NUM - 1, 0,
@@ -716,8 +872,9 @@ static const struct snd_kcontrol_new max
 
 	SND_SOC_BYTES("DMIC34 Biquad Coefficients",
 		M98090_REG_DMIC34_BIQUAD_BASE, 15),
-	SOC_SINGLE("DMIC34 Biquad Switch", M98090_REG_DSP_FILTER_ENABLE,
-		M98090_DMIC34BQEN_SHIFT, M98090_DMIC34BQEN_NUM - 1, 0),
+	SOC_SINGLE_EXT("DMIC34 Biquad Switch", M98090_REG_DSP_FILTER_ENABLE,
+		M98090_DMIC34BQEN_SHIFT, M98090_DMIC34BQEN_NUM - 1, 0,
+		snd_soc_get_volsw, max98090_put_volsw),
 
 	SOC_SINGLE_TLV("DMIC34 BQ PreAttenuation Volume",
 		M98090_REG_DMIC34_BQ_PREATTEN, M98090_AV34BQ_SHIFT,
@@ -771,19 +928,6 @@ static int max98090_micinput_event(struc
 	return 0;
 }
 
-static int max98090_shdn_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);
-	struct max98090_priv *max98090 = snd_soc_component_get_drvdata(component);
-
-	if (event & SND_SOC_DAPM_POST_PMU)
-		max98090->shdn_pending = true;
-
-	return 0;
-
-}
-
 static const char *mic1_mux_text[] = { "IN12", "IN56" };
 
 static SOC_ENUM_SINGLE_DECL(mic1_mux_enum,
@@ -884,10 +1028,14 @@ static SOC_ENUM_SINGLE_DECL(ltenr_mux_en
 			    lten_mux_text);
 
 static const struct snd_kcontrol_new max98090_ltenl_mux =
-	SOC_DAPM_ENUM("LTENL Mux", ltenl_mux_enum);
+	SOC_DAPM_ENUM_EXT("LTENL Mux", ltenl_mux_enum,
+			  snd_soc_dapm_get_enum_double,
+			  max98090_dapm_put_enum_double);
 
 static const struct snd_kcontrol_new max98090_ltenr_mux =
-	SOC_DAPM_ENUM("LTENR Mux", ltenr_mux_enum);
+	SOC_DAPM_ENUM_EXT("LTENR Mux", ltenr_mux_enum,
+			  snd_soc_dapm_get_enum_double,
+			  max98090_dapm_put_enum_double);
 
 static const char *lben_mux_text[] = { "Normal", "Loopback" };
 
@@ -902,10 +1050,14 @@ static SOC_ENUM_SINGLE_DECL(lbenr_mux_en
 			    lben_mux_text);
 
 static const struct snd_kcontrol_new max98090_lbenl_mux =
-	SOC_DAPM_ENUM("LBENL Mux", lbenl_mux_enum);
+	SOC_DAPM_ENUM_EXT("LBENL Mux", lbenl_mux_enum,
+			  snd_soc_dapm_get_enum_double,
+			  max98090_dapm_put_enum_double);
 
 static const struct snd_kcontrol_new max98090_lbenr_mux =
-	SOC_DAPM_ENUM("LBENR Mux", lbenr_mux_enum);
+	SOC_DAPM_ENUM_EXT("LBENR Mux", lbenr_mux_enum,
+			  snd_soc_dapm_get_enum_double,
+			  max98090_dapm_put_enum_double);
 
 static const char *stenl_mux_text[] = { "Normal", "Sidetone Left" };
 
@@ -1072,21 +1224,25 @@ static const struct snd_soc_dapm_widget
 	SND_SOC_DAPM_INPUT("IN56"),
 
 	SND_SOC_DAPM_SUPPLY("MICBIAS", M98090_REG_INPUT_ENABLE,
-		M98090_MBEN_SHIFT, 0, NULL, 0),
+		M98090_MBEN_SHIFT, 0, max98090_dapm_event,
+		SND_SOC_DAPM_PRE_POST_PMU | SND_SOC_DAPM_PRE_POST_PMD),
 	SND_SOC_DAPM_SUPPLY("SHDN", M98090_REG_DEVICE_SHUTDOWN,
 		M98090_SHDNN_SHIFT, 0, NULL, 0),
 	SND_SOC_DAPM_SUPPLY("SDIEN", M98090_REG_IO_CONFIGURATION,
-		M98090_SDIEN_SHIFT, 0, NULL, 0),
+		M98090_SDIEN_SHIFT, 0, max98090_dapm_event,
+		SND_SOC_DAPM_PRE_POST_PMU | SND_SOC_DAPM_PRE_POST_PMD),
 	SND_SOC_DAPM_SUPPLY("SDOEN", M98090_REG_IO_CONFIGURATION,
-		M98090_SDOEN_SHIFT, 0, NULL, 0),
+		M98090_SDOEN_SHIFT, 0, max98090_dapm_event,
+		SND_SOC_DAPM_PRE_POST_PMU | SND_SOC_DAPM_PRE_POST_PMD),
 	SND_SOC_DAPM_SUPPLY("DMICL_ENA", M98090_REG_DIGITAL_MIC_ENABLE,
-		 M98090_DIGMICL_SHIFT, 0, max98090_shdn_event,
-			SND_SOC_DAPM_POST_PMU),
+		M98090_DIGMICL_SHIFT, 0, max98090_dapm_event,
+		SND_SOC_DAPM_PRE_POST_PMU | SND_SOC_DAPM_PRE_POST_PMD),
 	SND_SOC_DAPM_SUPPLY("DMICR_ENA", M98090_REG_DIGITAL_MIC_ENABLE,
-		 M98090_DIGMICR_SHIFT, 0, max98090_shdn_event,
-			 SND_SOC_DAPM_POST_PMU),
+		M98090_DIGMICR_SHIFT, 0, max98090_dapm_event,
+		SND_SOC_DAPM_PRE_POST_PMU | SND_SOC_DAPM_PRE_POST_PMD),
 	SND_SOC_DAPM_SUPPLY("AHPF", M98090_REG_FILTER_CONFIG,
-		M98090_AHPF_SHIFT, 0, NULL, 0),
+		M98090_AHPF_SHIFT, 0, max98090_dapm_event,
+		SND_SOC_DAPM_PRE_POST_PMU | SND_SOC_DAPM_PRE_POST_PMD),
 
 /*
  * Note: Sysclk and misc power supplies are taken care of by SHDN
@@ -1116,10 +1272,12 @@ static const struct snd_soc_dapm_widget
 		&max98090_lineb_mixer_controls[0],
 		ARRAY_SIZE(max98090_lineb_mixer_controls)),
 
-	SND_SOC_DAPM_PGA("LINEA Input", M98090_REG_INPUT_ENABLE,
-		M98090_LINEAEN_SHIFT, 0, NULL, 0),
-	SND_SOC_DAPM_PGA("LINEB Input", M98090_REG_INPUT_ENABLE,
-		M98090_LINEBEN_SHIFT, 0, NULL, 0),
+	SND_SOC_DAPM_PGA_E("LINEA Input", M98090_REG_INPUT_ENABLE,
+		M98090_LINEAEN_SHIFT, 0, NULL, 0, max98090_dapm_event,
+		SND_SOC_DAPM_PRE_POST_PMU | SND_SOC_DAPM_PRE_POST_PMD),
+	SND_SOC_DAPM_PGA_E("LINEB Input", M98090_REG_INPUT_ENABLE,
+		M98090_LINEBEN_SHIFT, 0, NULL, 0, max98090_dapm_event,
+		SND_SOC_DAPM_PRE_POST_PMU | SND_SOC_DAPM_PRE_POST_PMD),
 
 	SND_SOC_DAPM_MIXER("Left ADC Mixer", SND_SOC_NOPM, 0, 0,
 		&max98090_left_adc_mixer_controls[0],
@@ -1130,11 +1288,11 @@ static const struct snd_soc_dapm_widget
 		ARRAY_SIZE(max98090_right_adc_mixer_controls)),
 
 	SND_SOC_DAPM_ADC_E("ADCL", NULL, M98090_REG_INPUT_ENABLE,
-		M98090_ADLEN_SHIFT, 0, max98090_shdn_event,
-		SND_SOC_DAPM_POST_PMU),
+		M98090_ADLEN_SHIFT, 0, max98090_dapm_event,
+		SND_SOC_DAPM_PRE_POST_PMU | SND_SOC_DAPM_PRE_POST_PMD),
 	SND_SOC_DAPM_ADC_E("ADCR", NULL, M98090_REG_INPUT_ENABLE,
-		M98090_ADREN_SHIFT, 0, max98090_shdn_event,
-		SND_SOC_DAPM_POST_PMU),
+		M98090_ADREN_SHIFT, 0, max98090_dapm_event,
+		SND_SOC_DAPM_PRE_POST_PMU | SND_SOC_DAPM_PRE_POST_PMD),
 
 	SND_SOC_DAPM_AIF_OUT("AIFOUTL", "HiFi Capture", 0,
 		SND_SOC_NOPM, 0, 0),
@@ -1162,10 +1320,12 @@ static const struct snd_soc_dapm_widget
 	SND_SOC_DAPM_AIF_IN("AIFINL", "HiFi Playback", 0, SND_SOC_NOPM, 0, 0),
 	SND_SOC_DAPM_AIF_IN("AIFINR", "HiFi Playback", 1, SND_SOC_NOPM, 0, 0),
 
-	SND_SOC_DAPM_DAC("DACL", NULL, M98090_REG_OUTPUT_ENABLE,
-		M98090_DALEN_SHIFT, 0),
-	SND_SOC_DAPM_DAC("DACR", NULL, M98090_REG_OUTPUT_ENABLE,
-		M98090_DAREN_SHIFT, 0),
+	SND_SOC_DAPM_DAC_E("DACL", NULL, M98090_REG_OUTPUT_ENABLE,
+		M98090_DALEN_SHIFT, 0, max98090_dapm_event,
+		SND_SOC_DAPM_PRE_POST_PMU | SND_SOC_DAPM_PRE_POST_PMD),
+	SND_SOC_DAPM_DAC_E("DACR", NULL, M98090_REG_OUTPUT_ENABLE,
+		M98090_DAREN_SHIFT, 0, max98090_dapm_event,
+		SND_SOC_DAPM_PRE_POST_PMU | SND_SOC_DAPM_PRE_POST_PMD),
 
 	SND_SOC_DAPM_MIXER("Left Headphone Mixer", SND_SOC_NOPM, 0, 0,
 		&max98090_left_hp_mixer_controls[0],
@@ -1200,20 +1360,26 @@ static const struct snd_soc_dapm_widget
 	SND_SOC_DAPM_MUX("MIXHPRSEL Mux", SND_SOC_NOPM, 0, 0,
 		&max98090_mixhprsel_mux),
 
-	SND_SOC_DAPM_PGA("HP Left Out", M98090_REG_OUTPUT_ENABLE,
-		M98090_HPLEN_SHIFT, 0, NULL, 0),
-	SND_SOC_DAPM_PGA("HP Right Out", M98090_REG_OUTPUT_ENABLE,
-		M98090_HPREN_SHIFT, 0, NULL, 0),
-
-	SND_SOC_DAPM_PGA("SPK Left Out", M98090_REG_OUTPUT_ENABLE,
-		M98090_SPLEN_SHIFT, 0, NULL, 0),
-	SND_SOC_DAPM_PGA("SPK Right Out", M98090_REG_OUTPUT_ENABLE,
-		M98090_SPREN_SHIFT, 0, NULL, 0),
-
-	SND_SOC_DAPM_PGA("RCV Left Out", M98090_REG_OUTPUT_ENABLE,
-		M98090_RCVLEN_SHIFT, 0, NULL, 0),
-	SND_SOC_DAPM_PGA("RCV Right Out", M98090_REG_OUTPUT_ENABLE,
-		M98090_RCVREN_SHIFT, 0, NULL, 0),
+	SND_SOC_DAPM_PGA_E("HP Left Out", M98090_REG_OUTPUT_ENABLE,
+		M98090_HPLEN_SHIFT, 0, NULL, 0, max98090_dapm_event,
+		SND_SOC_DAPM_PRE_POST_PMU | SND_SOC_DAPM_PRE_POST_PMD),
+	SND_SOC_DAPM_PGA_E("HP Right Out", M98090_REG_OUTPUT_ENABLE,
+		M98090_HPREN_SHIFT, 0, NULL, 0, max98090_dapm_event,
+		SND_SOC_DAPM_PRE_POST_PMU | SND_SOC_DAPM_PRE_POST_PMD),
+
+	SND_SOC_DAPM_PGA_E("SPK Left Out", M98090_REG_OUTPUT_ENABLE,
+		M98090_SPLEN_SHIFT, 0, NULL, 0, max98090_dapm_event,
+		SND_SOC_DAPM_PRE_POST_PMU | SND_SOC_DAPM_PRE_POST_PMD),
+	SND_SOC_DAPM_PGA_E("SPK Right Out", M98090_REG_OUTPUT_ENABLE,
+		M98090_SPREN_SHIFT, 0, NULL, 0, max98090_dapm_event,
+		SND_SOC_DAPM_PRE_POST_PMU | SND_SOC_DAPM_PRE_POST_PMD),
+
+	SND_SOC_DAPM_PGA_E("RCV Left Out", M98090_REG_OUTPUT_ENABLE,
+		M98090_RCVLEN_SHIFT, 0, NULL, 0, max98090_dapm_event,
+		SND_SOC_DAPM_PRE_POST_PMU | SND_SOC_DAPM_PRE_POST_PMD),
+	SND_SOC_DAPM_PGA_E("RCV Right Out", M98090_REG_OUTPUT_ENABLE,
+		M98090_RCVREN_SHIFT, 0, NULL, 0, max98090_dapm_event,
+		SND_SOC_DAPM_PRE_POST_PMU | SND_SOC_DAPM_PRE_POST_PMD),
 
 	SND_SOC_DAPM_OUTPUT("HPL"),
 	SND_SOC_DAPM_OUTPUT("HPR"),
@@ -1228,9 +1394,11 @@ static const struct snd_soc_dapm_widget
 	SND_SOC_DAPM_INPUT("DMIC4"),
 
 	SND_SOC_DAPM_SUPPLY("DMIC3_ENA", M98090_REG_DIGITAL_MIC_ENABLE,
-		 M98090_DIGMIC3_SHIFT, 0, NULL, 0),
+		M98090_DIGMIC3_SHIFT, 0, max98090_dapm_event,
+		SND_SOC_DAPM_PRE_POST_PMU | SND_SOC_DAPM_PRE_POST_PMD),
 	SND_SOC_DAPM_SUPPLY("DMIC4_ENA", M98090_REG_DIGITAL_MIC_ENABLE,
-		 M98090_DIGMIC4_SHIFT, 0, NULL, 0),
+		M98090_DIGMIC4_SHIFT, 0, max98090_dapm_event,
+		SND_SOC_DAPM_PRE_POST_PMU | SND_SOC_DAPM_PRE_POST_PMD),
 };
 
 static const struct snd_soc_dapm_route max98090_dapm_routes[] = {
@@ -1501,6 +1669,11 @@ static void max98090_configure_bclk(stru
 		return;
 	}
 
+	/*
+	 * Master mode: no need to save and restore SHDN for the following
+	 * sensitive registers.
+	 */
+
 	/* Check for supported PCLK to LRCLK ratios */
 	for (i = 0; i < ARRAY_SIZE(pclk_rates); i++) {
 		if ((pclk_rates[i] == max98090->sysclk) &&
@@ -1587,12 +1760,14 @@ static int max98090_dai_set_fmt(struct s
 		switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
 		case SND_SOC_DAIFMT_CBS_CFS:
 			/* Set to slave mode PLL - MAS mode off */
+			max98090_shdn_save(max98090);
 			snd_soc_component_write(component,
 				M98090_REG_CLOCK_RATIO_NI_MSB, 0x00);
 			snd_soc_component_write(component,
 				M98090_REG_CLOCK_RATIO_NI_LSB, 0x00);
 			snd_soc_component_update_bits(component, M98090_REG_CLOCK_MODE,
 				M98090_USE_M1_MASK, 0);
+			max98090_shdn_restore(max98090);
 			max98090->master = false;
 			break;
 		case SND_SOC_DAIFMT_CBM_CFM:
@@ -1618,7 +1793,9 @@ static int max98090_dai_set_fmt(struct s
 			dev_err(component->dev, "DAI clock mode unsupported");
 			return -EINVAL;
 		}
+		max98090_shdn_save(max98090);
 		snd_soc_component_write(component, M98090_REG_MASTER_MODE, regval);
+		max98090_shdn_restore(max98090);
 
 		regval = 0;
 		switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
@@ -1663,8 +1840,10 @@ static int max98090_dai_set_fmt(struct s
 		if (max98090->tdm_slots > 1)
 			regval ^= M98090_BCI_MASK;
 
+		max98090_shdn_save(max98090);
 		snd_soc_component_write(component,
 			M98090_REG_INTERFACE_FORMAT, regval);
+		max98090_shdn_restore(max98090);
 	}
 
 	return 0;
@@ -1676,6 +1855,7 @@ static int max98090_set_tdm_slot(struct
 	struct snd_soc_component *component = codec_dai->component;
 	struct max98090_priv *max98090 = snd_soc_component_get_drvdata(component);
 	struct max98090_cdata *cdata;
+
 	cdata = &max98090->dai[0];
 
 	if (slots < 0 || slots > 4)
@@ -1685,6 +1865,7 @@ static int max98090_set_tdm_slot(struct
 	max98090->tdm_width = slot_width;
 
 	if (max98090->tdm_slots > 1) {
+		max98090_shdn_save(max98090);
 		/* SLOTL SLOTR SLOTDLY */
 		snd_soc_component_write(component, M98090_REG_TDM_FORMAT,
 			0 << M98090_TDM_SLOTL_SHIFT |
@@ -1695,6 +1876,7 @@ static int max98090_set_tdm_slot(struct
 		snd_soc_component_update_bits(component, M98090_REG_TDM_CONTROL,
 			M98090_TDM_MASK,
 			M98090_TDM_MASK);
+		max98090_shdn_restore(max98090);
 	}
 
 	/*
@@ -1894,6 +2076,7 @@ static int max98090_configure_dmic(struc
 	dmic_freq = dmic_table[pclk_index].settings[micclk_index].freq;
 	dmic_comp = dmic_table[pclk_index].settings[micclk_index].comp[i];
 
+	max98090_shdn_save(max98090);
 	regmap_update_bits(max98090->regmap, M98090_REG_DIGITAL_MIC_ENABLE,
 			   M98090_MICCLK_MASK,
 			   micclk_index << M98090_MICCLK_SHIFT);
@@ -1902,6 +2085,7 @@ static int max98090_configure_dmic(struc
 			   M98090_DMIC_COMP_MASK | M98090_DMIC_FREQ_MASK,
 			   dmic_comp << M98090_DMIC_COMP_SHIFT |
 			   dmic_freq << M98090_DMIC_FREQ_SHIFT);
+	max98090_shdn_restore(max98090);
 
 	return 0;
 }
@@ -1938,8 +2122,10 @@ static int max98090_dai_hw_params(struct
 
 	switch (params_width(params)) {
 	case 16:
+		max98090_shdn_save(max98090);
 		snd_soc_component_update_bits(component, M98090_REG_INTERFACE_FORMAT,
 			M98090_WS_MASK, 0);
+		max98090_shdn_restore(max98090);
 		break;
 	default:
 		return -EINVAL;
@@ -1950,6 +2136,7 @@ static int max98090_dai_hw_params(struct
 
 	cdata->rate = max98090->lrclk;
 
+	max98090_shdn_save(max98090);
 	/* Update filter mode */
 	if (max98090->lrclk < 24000)
 		snd_soc_component_update_bits(component, M98090_REG_FILTER_CONFIG,
@@ -1965,6 +2152,7 @@ static int max98090_dai_hw_params(struct
 	else
 		snd_soc_component_update_bits(component, M98090_REG_FILTER_CONFIG,
 			M98090_DHF_MASK, M98090_DHF_MASK);
+	max98090_shdn_restore(max98090);
 
 	max98090_configure_dmic(max98090, max98090->dmic_freq, max98090->pclk,
 				max98090->lrclk);
@@ -1995,6 +2183,7 @@ static int max98090_dai_set_sysclk(struc
 	 *		 0x02 (when master clk is 20MHz to 40MHz)..
 	 *		 0x03 (when master clk is 40MHz to 60MHz)..
 	 */
+	max98090_shdn_save(max98090);
 	if ((freq >= 10000000) && (freq <= 20000000)) {
 		snd_soc_component_write(component, M98090_REG_SYSTEM_CLOCK,
 			M98090_PSCLK_DIV1);
@@ -2009,8 +2198,10 @@ static int max98090_dai_set_sysclk(struc
 		max98090->pclk = freq >> 2;
 	} else {
 		dev_err(component->dev, "Invalid master clock frequency\n");
+		max98090_shdn_restore(max98090);
 		return -EINVAL;
 	}
+	max98090_shdn_restore(max98090);
 
 	max98090->sysclk = freq;
 
@@ -2122,10 +2313,12 @@ static void max98090_pll_work(struct max
 	 */
 
 	/* Toggle shutdown OFF then ON */
+	mutex_lock(&component->card->dapm_mutex);
 	snd_soc_component_update_bits(component, M98090_REG_DEVICE_SHUTDOWN,
 			    M98090_SHDNN_MASK, 0);
 	snd_soc_component_update_bits(component, M98090_REG_DEVICE_SHUTDOWN,
 			    M98090_SHDNN_MASK, M98090_SHDNN_MASK);
+	mutex_unlock(&component->card->dapm_mutex);
 
 	for (i = 0; i < 10; ++i) {
 		/* Give PLL time to lock */
@@ -2448,7 +2641,12 @@ static int max98090_probe(struct snd_soc
 	 */
 	snd_soc_component_read32(component, M98090_REG_DEVICE_STATUS);
 
-	/* High Performance is default */
+	/*
+	 * SHDN should be 0 at the point, no need to save/restore for the
+	 * following registers.
+	 *
+	 * High Performance is default
+	 */
 	snd_soc_component_update_bits(component, M98090_REG_DAC_CONTROL,
 		M98090_DACHP_MASK,
 		1 << M98090_DACHP_SHIFT);
@@ -2459,7 +2657,12 @@ static int max98090_probe(struct snd_soc
 		M98090_ADCHP_MASK,
 		1 << M98090_ADCHP_SHIFT);
 
-	/* Turn on VCM bandgap reference */
+	/*
+	 * SHDN should be 0 at the point, no need to save/restore for the
+	 * following registers.
+	 *
+	 * Turn on VCM bandgap reference
+	 */
 	snd_soc_component_write(component, M98090_REG_BIAS_CONTROL,
 		M98090_VCM_MODE_MASK);
 
@@ -2491,25 +2694,9 @@ static void max98090_remove(struct snd_s
 	max98090->component = NULL;
 }
 
-static void max98090_seq_notifier(struct snd_soc_component *component,
-	enum snd_soc_dapm_type event, int subseq)
-{
-	struct max98090_priv *max98090 = snd_soc_component_get_drvdata(component);
-
-	if (max98090->shdn_pending) {
-		snd_soc_component_update_bits(component, M98090_REG_DEVICE_SHUTDOWN,
-				M98090_SHDNN_MASK, 0);
-		msleep(40);
-		snd_soc_component_update_bits(component, M98090_REG_DEVICE_SHUTDOWN,
-				M98090_SHDNN_MASK, M98090_SHDNN_MASK);
-		max98090->shdn_pending = false;
-	}
-}
-
 static const struct snd_soc_component_driver soc_component_dev_max98090 = {
 	.probe			= max98090_probe,
 	.remove			= max98090_remove,
-	.seq_notifier		= max98090_seq_notifier,
 	.set_bias_level		= max98090_set_bias_level,
 	.idle_bias_on		= 1,
 	.use_pmdown_time	= 1,
--- a/sound/soc/codecs/max98090.h
+++ b/sound/soc/codecs/max98090.h
@@ -1539,7 +1539,8 @@ struct max98090_priv {
 	unsigned int pa2en;
 	unsigned int sidetone;
 	bool master;
-	bool shdn_pending;
+	int saved_count;
+	int saved_shdn;
 };
 
 int max98090_mic_detect(struct snd_soc_component *component,