Commit 6f3991152f20933b77eff30413e893bf1a15e578
Committed by
Liam Girdwood
1 parent
5193d62f18
Exists in
master
and in
4 other branches
ASoC: tpa6130a2: Support for limiting gain
Add support for platform dependent gain limiting on the tpa6130a2 (and tpa6140a2) Headset amplifier. Signed-off-by: Peter Ujfalusi <peter.ujfalusi@nokia.com> Acked-by: Mark Brown <broonie@opensource.wolfsonmicro.com> Signed-off-by: Liam Girdwood <lrg@slimlogic.co.uk>
Showing 2 changed files with 69 additions and 8 deletions Side-by-side Diff
include/sound/tpa6130a2-plat.h
sound/soc/codecs/tpa6130a2.c
... | ... | @@ -46,6 +46,9 @@ |
46 | 46 | "AVdd", |
47 | 47 | }; |
48 | 48 | |
49 | +#define TPA6130A2_GAIN_MAX 0x3f | |
50 | +#define TPA6140A2_GAIN_MAX 0x1f | |
51 | + | |
49 | 52 | /* This struct is used to save the context */ |
50 | 53 | struct tpa6130a2_data { |
51 | 54 | struct mutex mutex; |
... | ... | @@ -54,6 +57,7 @@ |
54 | 57 | int power_gpio; |
55 | 58 | unsigned char power_state; |
56 | 59 | enum tpa_model id; |
60 | + int gain_limit; | |
57 | 61 | }; |
58 | 62 | |
59 | 63 | static int tpa6130a2_i2c_read(int reg) |
... | ... | @@ -176,6 +180,40 @@ |
176 | 180 | return ret; |
177 | 181 | } |
178 | 182 | |
183 | +static int tpa6130a2_info_volsw(struct snd_kcontrol *kcontrol, | |
184 | + struct snd_ctl_elem_info *uinfo) | |
185 | +{ | |
186 | + struct soc_mixer_control *mc = | |
187 | + (struct soc_mixer_control *)kcontrol->private_value; | |
188 | + struct tpa6130a2_data *data; | |
189 | + | |
190 | + BUG_ON(tpa6130a2_client == NULL); | |
191 | + data = i2c_get_clientdata(tpa6130a2_client); | |
192 | + | |
193 | + mutex_lock(&data->mutex); | |
194 | + switch (mc->reg) { | |
195 | + case TPA6130A2_REG_VOL_MUTE: | |
196 | + if (data->gain_limit != mc->max) | |
197 | + mc->max = data->gain_limit; | |
198 | + break; | |
199 | + default: | |
200 | + dev_err(&tpa6130a2_client->dev, | |
201 | + "Invalid register: 0x02%x\n", mc->reg); | |
202 | + goto out; | |
203 | + } | |
204 | + if (unlikely(mc->max == 1)) | |
205 | + uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; | |
206 | + else | |
207 | + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; | |
208 | + | |
209 | + uinfo->count = 1; | |
210 | + uinfo->value.integer.min = 0; | |
211 | + uinfo->value.integer.max = mc->max; | |
212 | +out: | |
213 | + mutex_unlock(&data->mutex); | |
214 | + return 0; | |
215 | +} | |
216 | + | |
179 | 217 | static int tpa6130a2_get_reg(struct snd_kcontrol *kcontrol, |
180 | 218 | struct snd_ctl_elem_value *ucontrol) |
181 | 219 | { |
... | ... | @@ -239,6 +277,15 @@ |
239 | 277 | return 1; |
240 | 278 | } |
241 | 279 | |
280 | +#define SOC_SINGLE_EXT_TLV_TPA(xname, xreg, xshift, xmax, xinvert, tlv_array) \ | |
281 | +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ | |
282 | + .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ |\ | |
283 | + SNDRV_CTL_ELEM_ACCESS_READWRITE,\ | |
284 | + .tlv.p = (tlv_array), \ | |
285 | + .info = tpa6130a2_info_volsw, \ | |
286 | + .get = tpa6130a2_get_reg, .put = tpa6130a2_set_reg, \ | |
287 | + .private_value = SOC_SINGLE_VALUE(xreg, xshift, xmax, xinvert) } | |
288 | + | |
242 | 289 | /* |
243 | 290 | * TPA6130 volume. From -59.5 to 4 dB with increasing step size when going |
244 | 291 | * down in gain. |
... | ... | @@ -258,10 +305,9 @@ |
258 | 305 | }; |
259 | 306 | |
260 | 307 | static const struct snd_kcontrol_new tpa6130a2_controls[] = { |
261 | - SOC_SINGLE_EXT_TLV("TPA6130A2 Headphone Playback Volume", | |
262 | - TPA6130A2_REG_VOL_MUTE, 0, 0x3f, 0, | |
263 | - tpa6130a2_get_reg, tpa6130a2_set_reg, | |
264 | - tpa6130_tlv), | |
308 | + SOC_SINGLE_EXT_TLV_TPA("TPA6130A2 Headphone Playback Volume", | |
309 | + TPA6130A2_REG_VOL_MUTE, 0, TPA6130A2_GAIN_MAX, 0, | |
310 | + tpa6130_tlv), | |
265 | 311 | }; |
266 | 312 | |
267 | 313 | static const unsigned int tpa6140_tlv[] = { |
... | ... | @@ -272,10 +318,9 @@ |
272 | 318 | }; |
273 | 319 | |
274 | 320 | static const struct snd_kcontrol_new tpa6140a2_controls[] = { |
275 | - SOC_SINGLE_EXT_TLV("TPA6140A2 Headphone Playback Volume", | |
276 | - TPA6130A2_REG_VOL_MUTE, 1, 0x1f, 0, | |
277 | - tpa6130a2_get_reg, tpa6130a2_set_reg, | |
278 | - tpa6140_tlv), | |
321 | + SOC_SINGLE_EXT_TLV_TPA("TPA6140A2 Headphone Playback Volume", | |
322 | + TPA6130A2_REG_VOL_MUTE, 1, TPA6140A2_GAIN_MAX, 0, | |
323 | + tpa6140_tlv), | |
279 | 324 | }; |
280 | 325 | |
281 | 326 | /* |
282 | 327 | |
283 | 328 | |
... | ... | @@ -454,16 +499,31 @@ |
454 | 499 | case TPA6130A2: |
455 | 500 | for (i = 0; i < ARRAY_SIZE(data->supplies); i++) |
456 | 501 | data->supplies[i].supply = tpa6130a2_supply_names[i]; |
502 | + if (pdata->limit_gain > 0 && | |
503 | + pdata->limit_gain < TPA6130A2_GAIN_MAX) | |
504 | + data->gain_limit = pdata->limit_gain; | |
505 | + else | |
506 | + data->gain_limit = TPA6130A2_GAIN_MAX; | |
457 | 507 | break; |
458 | 508 | case TPA6140A2: |
459 | 509 | for (i = 0; i < ARRAY_SIZE(data->supplies); i++) |
460 | 510 | data->supplies[i].supply = tpa6140a2_supply_names[i];; |
511 | + if (pdata->limit_gain > 0 && | |
512 | + pdata->limit_gain < TPA6140A2_GAIN_MAX) | |
513 | + data->gain_limit = pdata->limit_gain; | |
514 | + else | |
515 | + data->gain_limit = TPA6140A2_GAIN_MAX; | |
461 | 516 | break; |
462 | 517 | default: |
463 | 518 | dev_warn(dev, "Unknown TPA model (%d). Assuming 6130A2\n", |
464 | 519 | pdata->id); |
465 | 520 | for (i = 0; i < ARRAY_SIZE(data->supplies); i++) |
466 | 521 | data->supplies[i].supply = tpa6130a2_supply_names[i]; |
522 | + if (pdata->limit_gain > 0 && | |
523 | + pdata->limit_gain < TPA6130A2_GAIN_MAX) | |
524 | + data->gain_limit = pdata->limit_gain; | |
525 | + else | |
526 | + data->gain_limit = TPA6130A2_GAIN_MAX; | |
467 | 527 | } |
468 | 528 | |
469 | 529 | ret = regulator_bulk_get(dev, ARRAY_SIZE(data->supplies), |