[alsa-devel] [PATCH 0/2] ASoC: max98090: make sure SHDN is 0 when changing sensitive registers
This series is a follow up fix for the question: https://mailman.alsa-project.org/pipermail/alsa-devel/2019-October/157364.ht...
This series should apply after the series: https://mailman.alsa-project.org/pipermail/alsa-devel/2019-November/158850.h... (in terms of line numbers).
Tzung-Bi Shih (2): ASoC: core: add SND_SOC_BYTES_E ASoC: max98090: save and restore SHDN when changing sensitive registers
include/sound/soc.h | 6 + sound/soc/codecs/max98090.c | 433 ++++++++++++++++++++++++++---------- sound/soc/codecs/max98090.h | 3 +- 3 files changed, 318 insertions(+), 124 deletions(-)
Add SND_SOC_BYTES_E to accept getter and putter.
Signed-off-by: Tzung-Bi Shih tzungbi@google.com --- include/sound/soc.h | 6 ++++++ 1 file changed, 6 insertions(+)
diff --git a/include/sound/soc.h b/include/sound/soc.h index e0855dc08d30..1accb724e1d1 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -299,6 +299,12 @@ .put = snd_soc_bytes_put, .private_value = \ ((unsigned long)&(struct soc_bytes) \ {.base = xbase, .num_regs = xregs }) } +#define SND_SOC_BYTES_E(xname, xbase, xregs, xhandler_get, xhandler_put) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ + .info = snd_soc_bytes_info, .get = xhandler_get, \ + .put = xhandler_put, .private_value = \ + ((unsigned long)&(struct soc_bytes) \ + {.base = xbase, .num_regs = xregs }) }
#define SND_SOC_BYTES_MASK(xname, xbase, xregs, xmask) \ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
The patch
ASoC: core: add SND_SOC_BYTES_E
has been applied to the asoc tree at
https://git.kernel.org/pub/scm/linux/kernel/git/broonie/sound.git for-5.5
All being well this means that it will be integrated into the linux-next tree (usually sometime in the next 24 hours) and sent to Linus during the next merge window (or sooner if it is a bug fix), however if problems are discovered then the patch may be dropped or reverted.
You may get further e-mails resulting from automated or manual testing and review of the tree, please engage with people reporting problems and send followup patches addressing any issues that are reported if needed.
If any updates are required or you are submitting further changes they should be sent as incremental updates against current git, existing patches will not be replaced.
Please add any relevant lists and maintainers to the CCs when replying to this mail.
Thanks, Mark
From fb5126778333d289b2623a7e6260beeb1ac1b819 Mon Sep 17 00:00:00 2001
From: Tzung-Bi Shih tzungbi@google.com Date: Wed, 20 Nov 2019 14:08:43 +0800 Subject: [PATCH] ASoC: core: add SND_SOC_BYTES_E
Add SND_SOC_BYTES_E to accept getter and putter.
Signed-off-by: Tzung-Bi Shih tzungbi@google.com Link: https://lore.kernel.org/r/20191120060844.224607-2-tzungbi@google.com Signed-off-by: Mark Brown broonie@kernel.org --- include/sound/soc.h | 6 ++++++ 1 file changed, 6 insertions(+)
diff --git a/include/sound/soc.h b/include/sound/soc.h index bd943b5d7d45..c28a1ed5e8df 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -299,6 +299,12 @@ .put = snd_soc_bytes_put, .private_value = \ ((unsigned long)&(struct soc_bytes) \ {.base = xbase, .num_regs = xregs }) } +#define SND_SOC_BYTES_E(xname, xbase, xregs, xhandler_get, xhandler_put) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ + .info = snd_soc_bytes_info, .get = xhandler_get, \ + .put = xhandler_put, .private_value = \ + ((unsigned long)&(struct soc_bytes) \ + {.base = xbase, .num_regs = xregs }) }
#define SND_SOC_BYTES_MASK(xname, xbase, xregs, xmask) \ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
According to the datasheet, there are some registers can only be changed when SHDN is 0. Chaning 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 --- sound/soc/codecs/max98090.c | 433 ++++++++++++++++++++++++++---------- sound/soc/codecs/max98090.h | 3 +- 2 files changed, 312 insertions(+), 124 deletions(-)
diff --git a/sound/soc/codecs/max98090.c b/sound/soc/codecs/max98090.c index 75abe98dfc44..52bdb590f1e1 100644 --- 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_adchp_enum, 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 max98090_snd_controls[] = { 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 max98090_snd_controls[] = { 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 max98090_snd_controls[] = { 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 max98090_snd_controls[] = { 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 max98091_snd_controls[] = { 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 max98091_snd_controls[] = {
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(struct snd_soc_dapm_widget *w, 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_enum, 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_enum, 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 max98090_dapm_widgets[] = { 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_dapm_widgets[] = { &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 max98090_dapm_widgets[] = { 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 max98090_dapm_widgets[] = { 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 max98090_dapm_widgets[] = { 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 max98091_dapm_widgets[] = { 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(struct snd_soc_component *component) 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 snd_soc_dai *codec_dai, 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 snd_soc_dai *codec_dai, 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 snd_soc_dai *codec_dai, 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 snd_soc_dai *codec_dai, 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 snd_soc_dai *codec_dai, 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_dai *codec_dai, 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(struct max98090_priv *max98090, 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(struct max98090_priv *max98090, 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 snd_pcm_substream *substream,
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 snd_pcm_substream *substream,
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 snd_pcm_substream *substream, 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(struct snd_soc_dai *dai, * 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(struct snd_soc_dai *dai, 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;
@@ -2115,10 +2306,12 @@ static void max98090_pll_work(struct max98090_priv *max98090) dev_info_ratelimited(component->dev, "PLL unlocked\n");
/* 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) { /* Check lock status */ @@ -2441,7 +2634,12 @@ static int max98090_probe(struct snd_soc_component *component) */ 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); @@ -2452,7 +2650,12 @@ static int max98090_probe(struct snd_soc_component *component) 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);
@@ -2484,25 +2687,9 @@ static void max98090_remove(struct snd_soc_component *component) 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, diff --git a/sound/soc/codecs/max98090.h b/sound/soc/codecs/max98090.h index a197114b0dad..0a31708b7df7 100644 --- 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,
On Wed, Nov 20, 2019 at 02:08:44PM +0800, Tzung-Bi Shih wrote:
According to the datasheet, there are some registers can only be changed when SHDN is 0. Chaning these settings during SHDN = 1 can compromise device stability and performance specifications.
This doesn't apply against current code, please check and resend.
On Thu, Nov 21, 2019 at 1:16 AM Mark Brown broonie@kernel.org wrote:
On Wed, Nov 20, 2019 at 02:08:44PM +0800, Tzung-Bi Shih wrote:
According to the datasheet, there are some registers can only be changed when SHDN is 0. Chaning these settings during SHDN = 1 can compromise device stability and performance specifications.
Oh, and got a typo. s/Chaning/Changing/
This doesn't apply against current code, please check and resend.
I guess so, as mentioned in the cover letter, the series should apply after https://mailman.alsa-project.org/pipermail/alsa-devel/2019-November/158850.h... Will resend this patch after that.
participants (2)
-
Mark Brown
-
Tzung-Bi Shih