[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-Id: <20251217-sound-soc-codecs-tvl320adcx140-v1-5-293dea149d7b@pengutronix.de>
Date: Wed, 17 Dec 2025 14:54:55 +0100
From: Sascha Hauer <s.hauer@...gutronix.de>
To: Shenghao Ding <shenghao-ding@...com>, Kevin Lu <kevin-lu@...com>,
Baojun Xu <baojun.xu@...com>, Liam Girdwood <lgirdwood@...il.com>,
Mark Brown <broonie@...nel.org>, Jaroslav Kysela <perex@...ex.cz>,
Takashi Iwai <tiwai@...e.com>,
Sebastian Andrzej Siewior <bigeasy@...utronix.de>,
Clark Williams <clrkwllms@...nel.org>, Steven Rostedt <rostedt@...dmis.org>,
Rob Herring <robh@...nel.org>, Krzysztof Kozlowski <krzk+dt@...nel.org>,
Conor Dooley <conor+dt@...nel.org>, Andrew Davis <afd@...com>,
Dan Murphy <dmurphy@...com>
Cc: linux-sound@...r.kernel.org, linux-kernel@...r.kernel.org,
Kevin Lu <luminlong@....com>, linux-rt-devel@...ts.linux.dev,
devicetree@...r.kernel.org, Sascha Hauer <s.hauer@...gutronix.de>,
Emil-Juhl <emdj@...g-olufsen.dk>
Subject: [PATCH 05/11] ASoC: tlv320adcx140: implement register caching
From: Emil-Juhl <emdj@...g-olufsen.dk>
The tlv320adcx140 can be connected to controllable AVDD/IOVDD regulators
which when disabled will reset the registers to their default. In
preparation for adding support for them implement register caching.
Signed-off-by: Emil-Juhl <emdj@...g-olufsen.dk>
Signed-off-by: Sascha Hauer <s.hauer@...gutronix.de>
---
sound/soc/codecs/tlv320adcx140.c | 89 +++++++++++++++++++++++++++++++++++++++-
1 file changed, 87 insertions(+), 2 deletions(-)
diff --git a/sound/soc/codecs/tlv320adcx140.c b/sound/soc/codecs/tlv320adcx140.c
index fdf4a9add852deb5e879dba5c8a09458fc6e709d..444c0e80f0907de201ed1818b5612164a48b0fca 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 @@ static int adcx140_codec_probe(struct snd_soc_component *component)
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,
--
2.47.3
Powered by blists - more mailing lists