Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

ASoC: tlv320adcx140: power on/off the device on demand

The tlv320adcx140 can be connected to controllable AVDD/IOVDD regulators
which when disabled will reset the registers to their default. In
preparation for that switch to register writes to cache only when
powered off and sync the cached values to the registers when powered
back on.

Signed-off-by: Emil-Juhl <juhl.emildahl@gmail.com>
Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
Link: https://patch.msgid.link/20260113-sound-soc-codecs-tvl320adcx140-v4-5-8f7ecec525c8@pengutronix.de
Signed-off-by: Mark Brown <broonie@kernel.org>

authored by

Emil-Juhl and committed by
Mark Brown
24175015 46378ab9

+87 -2
+87 -2
sound/soc/codecs/tlv320adcx140.c
··· 121 121 { ADCX140_DEV_STS1, 0x80 }, 122 122 }; 123 123 124 + static const struct regmap_range adcx140_wr_ranges[] = { 125 + regmap_reg_range(ADCX140_PAGE_SELECT, ADCX140_SLEEP_CFG), 126 + regmap_reg_range(ADCX140_SHDN_CFG, ADCX140_SHDN_CFG), 127 + regmap_reg_range(ADCX140_ASI_CFG0, ADCX140_ASI_CFG2), 128 + regmap_reg_range(ADCX140_ASI_CH1, ADCX140_MST_CFG1), 129 + regmap_reg_range(ADCX140_CLK_SRC, ADCX140_CLK_SRC), 130 + regmap_reg_range(ADCX140_PDMCLK_CFG, ADCX140_GPO_CFG3), 131 + regmap_reg_range(ADCX140_GPO_VAL, ADCX140_GPO_VAL), 132 + regmap_reg_range(ADCX140_GPI_CFG0, ADCX140_GPI_CFG1), 133 + regmap_reg_range(ADCX140_GPI_MON, ADCX140_GPI_MON), 134 + regmap_reg_range(ADCX140_INT_CFG, ADCX140_INT_MASK0), 135 + regmap_reg_range(ADCX140_BIAS_CFG, ADCX140_CH4_CFG4), 136 + regmap_reg_range(ADCX140_CH5_CFG2, ADCX140_CH5_CFG4), 137 + regmap_reg_range(ADCX140_CH6_CFG2, ADCX140_CH6_CFG4), 138 + regmap_reg_range(ADCX140_CH7_CFG2, ADCX140_CH7_CFG4), 139 + regmap_reg_range(ADCX140_CH8_CFG2, ADCX140_CH8_CFG4), 140 + regmap_reg_range(ADCX140_DSP_CFG0, ADCX140_DRE_CFG0), 141 + regmap_reg_range(ADCX140_AGC_CFG0, ADCX140_AGC_CFG0), 142 + regmap_reg_range(ADCX140_IN_CH_EN, ADCX140_PWR_CFG), 143 + regmap_reg_range(ADCX140_PHASE_CALIB, ADCX140_PHASE_CALIB), 144 + regmap_reg_range(0x7e, 0x7e), 145 + }; 146 + 147 + static const struct regmap_access_table adcx140_wr_table = { 148 + .yes_ranges = adcx140_wr_ranges, 149 + .n_yes_ranges = ARRAY_SIZE(adcx140_wr_ranges), 150 + }; 151 + 124 152 static const struct regmap_range_cfg adcx140_ranges[] = { 125 153 { 126 154 .range_min = 0, ··· 184 156 .num_ranges = ARRAY_SIZE(adcx140_ranges), 185 157 .max_register = 12 * 128, 186 158 .volatile_reg = adcx140_volatile, 159 + .wr_table = &adcx140_wr_table, 187 160 }; 188 161 189 162 /* Digital Volume control. From -100 to 27 dB in 0.5 dB steps */ ··· 1102 1073 return ret; 1103 1074 } 1104 1075 1076 + static int adcx140_pwr_off(struct adcx140_priv *adcx140) 1077 + { 1078 + regcache_cache_only(adcx140->regmap, true); 1079 + regcache_mark_dirty(adcx140->regmap); 1080 + 1081 + /* Assert the reset GPIO */ 1082 + gpiod_set_value_cansleep(adcx140->gpio_reset, 0); 1083 + 1084 + /* 1085 + * Datasheet - TLV320ADC3140 Rev. B, TLV320ADC5140 Rev. A, 1086 + * TLV320ADC6140 Rev. A 8.4.1: 1087 + * wait for hw shutdown (25ms) + >= 1ms 1088 + */ 1089 + usleep_range(30000, 100000); 1090 + 1091 + return 0; 1092 + } 1093 + 1094 + static int adcx140_pwr_on(struct adcx140_priv *adcx140) 1095 + { 1096 + int ret; 1097 + 1098 + /* De-assert the reset GPIO */ 1099 + gpiod_set_value_cansleep(adcx140->gpio_reset, 1); 1100 + 1101 + /* 1102 + * Datasheet - TLV320ADC3140 Rev. B, TLV320ADC5140 Rev. A, 1103 + * TLV320ADC6140 Rev. A 8.4.2: 1104 + * wait >= 10 ms after entering sleep mode. 1105 + */ 1106 + usleep_range(10000, 100000); 1107 + 1108 + regcache_cache_only(adcx140->regmap, false); 1109 + 1110 + /* Flush the regcache */ 1111 + ret = regcache_sync(adcx140->regmap); 1112 + if (ret) { 1113 + dev_err(adcx140->dev, "Failed to restore register map: %d\n", 1114 + ret); 1115 + return ret; 1116 + } 1117 + 1118 + return 0; 1119 + } 1120 + 1105 1121 static int adcx140_set_bias_level(struct snd_soc_component *component, 1106 1122 enum snd_soc_bias_level level) 1107 1123 { 1108 1124 struct adcx140_priv *adcx140 = snd_soc_component_get_drvdata(component); 1125 + enum snd_soc_bias_level prev_level 1126 + = snd_soc_component_get_bias_level(component); 1109 1127 1110 1128 switch (level) { 1111 1129 case SND_SOC_BIAS_ON: 1112 1130 case SND_SOC_BIAS_PREPARE: 1131 + if (prev_level == SND_SOC_BIAS_STANDBY) 1132 + adcx140_pwr_ctrl(adcx140, true); 1133 + break; 1113 1134 case SND_SOC_BIAS_STANDBY: 1114 - adcx140_pwr_ctrl(adcx140, true); 1135 + if (prev_level == SND_SOC_BIAS_PREPARE) 1136 + adcx140_pwr_ctrl(adcx140, false); 1137 + if (prev_level == SND_SOC_BIAS_OFF) 1138 + return adcx140_pwr_on(adcx140); 1115 1139 break; 1116 1140 case SND_SOC_BIAS_OFF: 1117 - adcx140_pwr_ctrl(adcx140, false); 1141 + if (prev_level == SND_SOC_BIAS_STANDBY) 1142 + return adcx140_pwr_off(adcx140); 1118 1143 break; 1119 1144 } 1120 1145 ··· 1268 1185 ret); 1269 1186 return ret; 1270 1187 } 1188 + 1189 + regcache_cache_only(adcx140->regmap, true); 1271 1190 1272 1191 i2c_set_clientdata(i2c, adcx140); 1273 1192