ASoC: max98373: don't access volatile registers in bias level off
authorBard Liao <yung-chuan.liao@linux.intel.com>
Thu, 17 Dec 2020 07:45:56 +0000 (15:45 +0800)
committerMark Brown <broonie@kernel.org>
Thu, 17 Dec 2020 14:24:45 +0000 (14:24 +0000)
We will set regcache_cache_only true in suspend. As a result,
regmap_read will return error when we try to read volatile
registers in suspend. Besides, it doesn't make sense to read
feedback data when codec is not active. To make userspace
happy, this patch returns a cached value shich should be a
valid value.

Signed-off-by: Bard Liao <yung-chuan.liao@linux.intel.com>
Link: https://lore.kernel.org/r/20201217074556.32370-1-yung-chuan.liao@linux.intel.com
Signed-off-by: Mark Brown <broonie@kernel.org>
sound/soc/codecs/max98373-i2c.c
sound/soc/codecs/max98373-sdw.c
sound/soc/codecs/max98373.c
sound/soc/codecs/max98373.h

index 92921e34f9486708242b60d6dfd02828c3c8f834..85f6865019d4a82d9ac924294c32452c86f8d028 100644 (file)
 #include <sound/tlv.h>
 #include "max98373.h"
 
+static const u32 max98373_i2c_cache_reg[] = {
+       MAX98373_R2054_MEAS_ADC_PVDD_CH_READBACK,
+       MAX98373_R2055_MEAS_ADC_THERM_CH_READBACK,
+       MAX98373_R20B6_BDE_CUR_STATE_READBACK,
+};
+
 static struct reg_default max98373_reg[] = {
        {MAX98373_R2000_SW_RESET, 0x00},
        {MAX98373_R2001_INT_RAW1, 0x00},
@@ -472,6 +478,11 @@ static struct snd_soc_dai_driver max98373_dai[] = {
 static int max98373_suspend(struct device *dev)
 {
        struct max98373_priv *max98373 = dev_get_drvdata(dev);
+       int i;
+
+       /* cache feedback register values before suspend */
+       for (i = 0; i < max98373->cache_num; i++)
+               regmap_read(max98373->regmap, max98373->cache[i].reg, &max98373->cache[i].val);
 
        regcache_cache_only(max98373->regmap, true);
        regcache_mark_dirty(max98373->regmap);
@@ -509,6 +520,7 @@ static int max98373_i2c_probe(struct i2c_client *i2c,
 {
        int ret = 0;
        int reg = 0;
+       int i;
        struct max98373_priv *max98373 = NULL;
 
        max98373 = devm_kzalloc(&i2c->dev, sizeof(*max98373), GFP_KERNEL);
@@ -534,6 +546,14 @@ static int max98373_i2c_probe(struct i2c_client *i2c,
                return ret;
        }
 
+       max98373->cache_num = ARRAY_SIZE(max98373_i2c_cache_reg);
+       max98373->cache = devm_kcalloc(&i2c->dev, max98373->cache_num,
+                                      sizeof(*max98373->cache),
+                                      GFP_KERNEL);
+
+       for (i = 0; i < max98373->cache_num; i++)
+               max98373->cache[i].reg = max98373_i2c_cache_reg[i];
+
        /* voltage/current slot & gpio configuration */
        max98373_slot_config(&i2c->dev, max98373);
 
index ec2e79c57357729835f6165b9eeb999832656135..b8d471d79e939e885553691565cbf036d3afaa4c 100644 (file)
@@ -23,6 +23,12 @@ struct sdw_stream_data {
        struct sdw_stream_runtime *sdw_stream;
 };
 
+static const u32 max98373_sdw_cache_reg[] = {
+       MAX98373_R2054_MEAS_ADC_PVDD_CH_READBACK,
+       MAX98373_R2055_MEAS_ADC_THERM_CH_READBACK,
+       MAX98373_R20B6_BDE_CUR_STATE_READBACK,
+};
+
 static struct reg_default max98373_reg[] = {
        {MAX98373_R0040_SCP_INIT_STAT_1, 0x00},
        {MAX98373_R0041_SCP_INIT_MASK_1, 0x00},
@@ -245,6 +251,11 @@ static const struct regmap_config max98373_sdw_regmap = {
 static __maybe_unused int max98373_suspend(struct device *dev)
 {
        struct max98373_priv *max98373 = dev_get_drvdata(dev);
+       int i;
+
+       /* cache feedback register values before suspend */
+       for (i = 0; i < max98373->cache_num; i++)
+               regmap_read(max98373->regmap, max98373->cache[i].reg, &max98373->cache[i].val);
 
        regcache_cache_only(max98373->regmap, true);
 
@@ -757,6 +768,7 @@ static int max98373_init(struct sdw_slave *slave, struct regmap *regmap)
 {
        struct max98373_priv *max98373;
        int ret;
+       int i;
        struct device *dev = &slave->dev;
 
        /*  Allocate and assign private driver data structure  */
@@ -768,6 +780,14 @@ static int max98373_init(struct sdw_slave *slave, struct regmap *regmap)
        max98373->regmap = regmap;
        max98373->slave = slave;
 
+       max98373->cache_num = ARRAY_SIZE(max98373_sdw_cache_reg);
+       max98373->cache = devm_kcalloc(dev, max98373->cache_num,
+                                      sizeof(*max98373->cache),
+                                      GFP_KERNEL);
+
+       for (i = 0; i < max98373->cache_num; i++)
+               max98373->cache[i].reg = max98373_sdw_cache_reg[i];
+
        /* Read voltage and slot configuration */
        max98373_slot_config(dev, max98373);
 
index 929bb1798c43f9a5c03af199052a9682aae8ae34..31d571d4fac1ce590e51386759ede696ab7ddba6 100644 (file)
@@ -168,6 +168,31 @@ static SOC_ENUM_SINGLE_DECL(max98373_adc_samplerate_enum,
                            MAX98373_R2051_MEAS_ADC_SAMPLING_RATE, 0,
                            max98373_ADC_samplerate_text);
 
+static int max98373_feedback_get(struct snd_kcontrol *kcontrol,
+                                struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+       struct soc_mixer_control *mc =
+               (struct soc_mixer_control *)kcontrol->private_value;
+       struct max98373_priv *max98373 = snd_soc_component_get_drvdata(component);
+       int i;
+
+       if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_OFF) {
+               /*
+                * Register values will be cached before suspend. The cached value
+                * will be a valid value and userspace will happy with that.
+                */
+               for (i = 0; i < max98373->cache_num; i++) {
+                       if (mc->reg == max98373->cache[i].reg) {
+                               ucontrol->value.integer.value[0] = max98373->cache[i].val;
+                               return 0;
+                       }
+               }
+       }
+
+       return snd_soc_put_volsw(kcontrol, ucontrol);
+}
+
 static const struct snd_kcontrol_new max98373_snd_controls[] = {
 SOC_SINGLE("Digital Vol Sel Switch", MAX98373_R203F_AMP_DSP_CFG,
        MAX98373_AMP_VOL_SEL_SHIFT, 1, 0),
@@ -209,8 +234,10 @@ SOC_SINGLE("ADC PVDD FLT Switch", MAX98373_R2052_MEAS_ADC_PVDD_FLT_CFG,
        MAX98373_FLT_EN_SHIFT, 1, 0),
 SOC_SINGLE("ADC TEMP FLT Switch", MAX98373_R2053_MEAS_ADC_THERM_FLT_CFG,
        MAX98373_FLT_EN_SHIFT, 1, 0),
-SOC_SINGLE("ADC PVDD", MAX98373_R2054_MEAS_ADC_PVDD_CH_READBACK, 0, 0xFF, 0),
-SOC_SINGLE("ADC TEMP", MAX98373_R2055_MEAS_ADC_THERM_CH_READBACK, 0, 0xFF, 0),
+SOC_SINGLE_EXT("ADC PVDD", MAX98373_R2054_MEAS_ADC_PVDD_CH_READBACK, 0, 0xFF, 0,
+       max98373_feedback_get, NULL),
+SOC_SINGLE_EXT("ADC TEMP", MAX98373_R2055_MEAS_ADC_THERM_CH_READBACK, 0, 0xFF, 0,
+       max98373_feedback_get, NULL),
 SOC_SINGLE("ADC PVDD FLT Coeff", MAX98373_R2052_MEAS_ADC_PVDD_FLT_CFG,
        0, 0x3, 0),
 SOC_SINGLE("ADC TEMP FLT Coeff", MAX98373_R2053_MEAS_ADC_THERM_FLT_CFG,
@@ -226,7 +253,8 @@ SOC_SINGLE("BDE LVL1 Thresh", MAX98373_R2097_BDE_L1_THRESH, 0, 0xFF, 0),
 SOC_SINGLE("BDE LVL2 Thresh", MAX98373_R2098_BDE_L2_THRESH, 0, 0xFF, 0),
 SOC_SINGLE("BDE LVL3 Thresh", MAX98373_R2099_BDE_L3_THRESH, 0, 0xFF, 0),
 SOC_SINGLE("BDE LVL4 Thresh", MAX98373_R209A_BDE_L4_THRESH, 0, 0xFF, 0),
-SOC_SINGLE("BDE Active Level", MAX98373_R20B6_BDE_CUR_STATE_READBACK, 0, 8, 0),
+SOC_SINGLE_EXT("BDE Active Level", MAX98373_R20B6_BDE_CUR_STATE_READBACK, 0, 8, 0,
+       max98373_feedback_get, NULL),
 SOC_SINGLE("BDE Clip Mode Switch", MAX98373_R2092_BDE_CLIPPER_MODE, 0, 1, 0),
 SOC_SINGLE("BDE Thresh Hysteresis", MAX98373_R209B_BDE_THRESH_HYST, 0, 0xFF, 0),
 SOC_SINGLE("BDE Hold Time", MAX98373_R2090_BDE_LVL_HOLD, 0, 0xFF, 0),
index 4ab29b9d51c74edd602d036e13c110b14ceb56f1..71f5a5228f34b1629644723bfb5bd47808bc6900 100644 (file)
 /* MAX98373_R2000_SW_RESET */
 #define MAX98373_SOFT_RESET (0x1 << 0)
 
+struct max98373_cache {
+       u32 reg;
+       u32 val;
+};
+
 struct max98373_priv {
        struct regmap *regmap;
        int reset_gpio;
@@ -212,6 +217,9 @@ struct max98373_priv {
        bool interleave_mode;
        unsigned int ch_size;
        bool tdm_mode;
+       /* cache for reading a valid fake feedback value */
+       struct max98373_cache *cache;
+       int cache_num;
        /* variables to support soundwire */
        struct sdw_slave *slave;
        bool hw_init;