diff --git a/sound/soc/codecs/tlv320adcx140.c b/sound/soc/codecs/tlv320adcx140.c index fdf4a9add852..444c0e80f090 100644 --- a/sound/soc/codecs/tlv320adcx140.c +++ b/sound/soc/codecs/tlv320adcx140.c @@ -121,6 +121,34 @@ static const struct reg_default adcx140_reg_defaults[] = { { ADCX140_DEV_STS1, 0x80 }, }; +static const struct regmap_range adcx140_wr_ranges[] = { + regmap_reg_range(ADCX140_PAGE_SELECT, ADCX140_SLEEP_CFG), + regmap_reg_range(ADCX140_SHDN_CFG, ADCX140_SHDN_CFG), + regmap_reg_range(ADCX140_ASI_CFG0, ADCX140_ASI_CFG2), + regmap_reg_range(ADCX140_ASI_CH1, ADCX140_MST_CFG1), + regmap_reg_range(ADCX140_CLK_SRC, ADCX140_CLK_SRC), + regmap_reg_range(ADCX140_PDMCLK_CFG, ADCX140_GPO_CFG3), + regmap_reg_range(ADCX140_GPO_VAL, ADCX140_GPO_VAL), + regmap_reg_range(ADCX140_GPI_CFG0, ADCX140_GPI_CFG1), + regmap_reg_range(ADCX140_GPI_MON, ADCX140_GPI_MON), + regmap_reg_range(ADCX140_INT_CFG, ADCX140_INT_MASK0), + regmap_reg_range(ADCX140_BIAS_CFG, ADCX140_CH4_CFG4), + regmap_reg_range(ADCX140_CH5_CFG2, ADCX140_CH5_CFG4), + regmap_reg_range(ADCX140_CH6_CFG2, ADCX140_CH6_CFG4), + regmap_reg_range(ADCX140_CH7_CFG2, ADCX140_CH7_CFG4), + regmap_reg_range(ADCX140_CH8_CFG2, ADCX140_CH8_CFG4), + regmap_reg_range(ADCX140_DSP_CFG0, ADCX140_DRE_CFG0), + regmap_reg_range(ADCX140_AGC_CFG0, ADCX140_AGC_CFG0), + regmap_reg_range(ADCX140_IN_CH_EN, ADCX140_PWR_CFG), + regmap_reg_range(ADCX140_PHASE_CALIB, ADCX140_PHASE_CALIB), + regmap_reg_range(0x7e, 0x7e), +}; + +static const struct regmap_access_table adcx140_wr_table = { + .yes_ranges = adcx140_wr_ranges, + .n_yes_ranges = ARRAY_SIZE(adcx140_wr_ranges), +}; + static const struct regmap_range_cfg adcx140_ranges[] = { { .range_min = 0, @@ -156,6 +184,7 @@ static const struct regmap_config adcx140_i2c_regmap = { .num_ranges = ARRAY_SIZE(adcx140_ranges), .max_register = 12 * 128, .volatile_reg = adcx140_volatile, + .wr_table = &adcx140_wr_table, }; /* Digital Volume control. From -100 to 27 dB in 0.5 dB steps */ @@ -1073,19 +1102,73 @@ out: return ret; } +static int adcx140_pwr_off(struct adcx140_priv *adcx140) +{ + regcache_cache_only(adcx140->regmap, true); + regcache_mark_dirty(adcx140->regmap); + + /* Assert the reset GPIO */ + gpiod_set_value_cansleep(adcx140->gpio_reset, 0); + + /* + * Datasheet - TLV320ADC3140 Rev. B, TLV320ADC5140 Rev. A, + * TLV320ADC6140 Rev. A 8.4.1: + * wait for hw shutdown (25ms) + >= 1ms + */ + usleep_range(30000, 100000); + + return 0; +} + +static int adcx140_pwr_on(struct adcx140_priv *adcx140) +{ + int ret; + + /* De-assert the reset GPIO */ + gpiod_set_value_cansleep(adcx140->gpio_reset, 1); + + /* + * Datasheet - TLV320ADC3140 Rev. B, TLV320ADC5140 Rev. A, + * TLV320ADC6140 Rev. A 8.4.2: + * wait >= 10 ms after entering sleep mode. + */ + usleep_range(10000, 100000); + + regcache_cache_only(adcx140->regmap, false); + + /* Flush the regcache */ + ret = regcache_sync(adcx140->regmap); + if (ret) { + dev_err(adcx140->dev, "Failed to restore register map: %d\n", + ret); + return ret; + } + + return 0; +} + static int adcx140_set_bias_level(struct snd_soc_component *component, enum snd_soc_bias_level level) { struct adcx140_priv *adcx140 = snd_soc_component_get_drvdata(component); + enum snd_soc_bias_level prev_level + = snd_soc_component_get_bias_level(component); switch (level) { case SND_SOC_BIAS_ON: case SND_SOC_BIAS_PREPARE: + if (prev_level == SND_SOC_BIAS_STANDBY) + adcx140_pwr_ctrl(adcx140, true); + break; case SND_SOC_BIAS_STANDBY: - adcx140_pwr_ctrl(adcx140, true); + if (prev_level == SND_SOC_BIAS_PREPARE) + adcx140_pwr_ctrl(adcx140, false); + if (prev_level == SND_SOC_BIAS_OFF) + return adcx140_pwr_on(adcx140); break; case SND_SOC_BIAS_OFF: - adcx140_pwr_ctrl(adcx140, false); + if (prev_level == SND_SOC_BIAS_STANDBY) + return adcx140_pwr_off(adcx140); break; } @@ -1186,6 +1269,8 @@ static int adcx140_i2c_probe(struct i2c_client *i2c) return ret; } + regcache_cache_only(adcx140->regmap, true); + i2c_set_clientdata(i2c, adcx140); return devm_snd_soc_register_component(&i2c->dev,