diff options
author | Peter Ujfalusi <peter.ujfalusi@nokia.com> | 2010-05-06 03:37:18 -0400 |
---|---|---|
committer | Liam Girdwood <lrg@slimlogic.co.uk> | 2010-05-06 09:58:20 -0400 |
commit | 6f3991152f20933b77eff30413e893bf1a15e578 (patch) | |
tree | c34ec1a5e9cc707de055972e78100feb491ad816 /sound | |
parent | 5193d62f1824cdfd72b5523be2b1cdb8049225ad (diff) |
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>
Diffstat (limited to 'sound')
-rw-r--r-- | sound/soc/codecs/tpa6130a2.c | 76 |
1 files changed, 68 insertions, 8 deletions
diff --git a/sound/soc/codecs/tpa6130a2.c b/sound/soc/codecs/tpa6130a2.c index 0cf3e3862e7b..31f67b527ca1 100644 --- a/sound/soc/codecs/tpa6130a2.c +++ b/sound/soc/codecs/tpa6130a2.c | |||
@@ -46,6 +46,9 @@ static const char *tpa6140a2_supply_names[TPA6130A2_NUM_SUPPLIES] = { | |||
46 | "AVdd", | 46 | "AVdd", |
47 | }; | 47 | }; |
48 | 48 | ||
49 | #define TPA6130A2_GAIN_MAX 0x3f | ||
50 | #define TPA6140A2_GAIN_MAX 0x1f | ||
51 | |||
49 | /* This struct is used to save the context */ | 52 | /* This struct is used to save the context */ |
50 | struct tpa6130a2_data { | 53 | struct tpa6130a2_data { |
51 | struct mutex mutex; | 54 | struct mutex mutex; |
@@ -54,6 +57,7 @@ struct tpa6130a2_data { | |||
54 | int power_gpio; | 57 | int power_gpio; |
55 | unsigned char power_state; | 58 | unsigned char power_state; |
56 | enum tpa_model id; | 59 | enum tpa_model id; |
60 | int gain_limit; | ||
57 | }; | 61 | }; |
58 | 62 | ||
59 | static int tpa6130a2_i2c_read(int reg) | 63 | static int tpa6130a2_i2c_read(int reg) |
@@ -176,6 +180,40 @@ exit: | |||
176 | return ret; | 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 | static int tpa6130a2_get_reg(struct snd_kcontrol *kcontrol, | 217 | static int tpa6130a2_get_reg(struct snd_kcontrol *kcontrol, |
180 | struct snd_ctl_elem_value *ucontrol) | 218 | struct snd_ctl_elem_value *ucontrol) |
181 | { | 219 | { |
@@ -239,6 +277,15 @@ static int tpa6130a2_set_reg(struct snd_kcontrol *kcontrol, | |||
239 | return 1; | 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 | * TPA6130 volume. From -59.5 to 4 dB with increasing step size when going | 290 | * TPA6130 volume. From -59.5 to 4 dB with increasing step size when going |
244 | * down in gain. | 291 | * down in gain. |
@@ -258,10 +305,9 @@ static const unsigned int tpa6130_tlv[] = { | |||
258 | }; | 305 | }; |
259 | 306 | ||
260 | static const struct snd_kcontrol_new tpa6130a2_controls[] = { | 307 | static const struct snd_kcontrol_new tpa6130a2_controls[] = { |
261 | SOC_SINGLE_EXT_TLV("TPA6130A2 Headphone Playback Volume", | 308 | SOC_SINGLE_EXT_TLV_TPA("TPA6130A2 Headphone Playback Volume", |
262 | TPA6130A2_REG_VOL_MUTE, 0, 0x3f, 0, | 309 | TPA6130A2_REG_VOL_MUTE, 0, TPA6130A2_GAIN_MAX, 0, |
263 | tpa6130a2_get_reg, tpa6130a2_set_reg, | 310 | tpa6130_tlv), |
264 | tpa6130_tlv), | ||
265 | }; | 311 | }; |
266 | 312 | ||
267 | static const unsigned int tpa6140_tlv[] = { | 313 | static const unsigned int tpa6140_tlv[] = { |
@@ -272,10 +318,9 @@ static const unsigned int tpa6140_tlv[] = { | |||
272 | }; | 318 | }; |
273 | 319 | ||
274 | static const struct snd_kcontrol_new tpa6140a2_controls[] = { | 320 | static const struct snd_kcontrol_new tpa6140a2_controls[] = { |
275 | SOC_SINGLE_EXT_TLV("TPA6140A2 Headphone Playback Volume", | 321 | SOC_SINGLE_EXT_TLV_TPA("TPA6140A2 Headphone Playback Volume", |
276 | TPA6130A2_REG_VOL_MUTE, 1, 0x1f, 0, | 322 | TPA6130A2_REG_VOL_MUTE, 1, TPA6140A2_GAIN_MAX, 0, |
277 | tpa6130a2_get_reg, tpa6130a2_set_reg, | 323 | tpa6140_tlv), |
278 | tpa6140_tlv), | ||
279 | }; | 324 | }; |
280 | 325 | ||
281 | /* | 326 | /* |
@@ -454,16 +499,31 @@ static int __devinit tpa6130a2_probe(struct i2c_client *client, | |||
454 | case TPA6130A2: | 499 | case TPA6130A2: |
455 | for (i = 0; i < ARRAY_SIZE(data->supplies); i++) | 500 | for (i = 0; i < ARRAY_SIZE(data->supplies); i++) |
456 | data->supplies[i].supply = tpa6130a2_supply_names[i]; | 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 | break; | 507 | break; |
458 | case TPA6140A2: | 508 | case TPA6140A2: |
459 | for (i = 0; i < ARRAY_SIZE(data->supplies); i++) | 509 | for (i = 0; i < ARRAY_SIZE(data->supplies); i++) |
460 | data->supplies[i].supply = tpa6140a2_supply_names[i];; | 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 | break; | 516 | break; |
462 | default: | 517 | default: |
463 | dev_warn(dev, "Unknown TPA model (%d). Assuming 6130A2\n", | 518 | dev_warn(dev, "Unknown TPA model (%d). Assuming 6130A2\n", |
464 | pdata->id); | 519 | pdata->id); |
465 | for (i = 0; i < ARRAY_SIZE(data->supplies); i++) | 520 | for (i = 0; i < ARRAY_SIZE(data->supplies); i++) |
466 | data->supplies[i].supply = tpa6130a2_supply_names[i]; | 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 | ret = regulator_bulk_get(dev, ARRAY_SIZE(data->supplies), | 529 | ret = regulator_bulk_get(dev, ARRAY_SIZE(data->supplies), |