diff options
Diffstat (limited to 'sound/soc/codecs/tpa6130a2.c')
-rw-r--r-- | sound/soc/codecs/tpa6130a2.c | 99 |
1 files changed, 92 insertions, 7 deletions
diff --git a/sound/soc/codecs/tpa6130a2.c b/sound/soc/codecs/tpa6130a2.c index 958d49c969ac..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; |
@@ -53,6 +56,8 @@ struct tpa6130a2_data { | |||
53 | struct regulator_bulk_data supplies[TPA6130A2_NUM_SUPPLIES]; | 56 | struct regulator_bulk_data supplies[TPA6130A2_NUM_SUPPLIES]; |
54 | int power_gpio; | 57 | int power_gpio; |
55 | unsigned char power_state; | 58 | unsigned char power_state; |
59 | enum tpa_model id; | ||
60 | int gain_limit; | ||
56 | }; | 61 | }; |
57 | 62 | ||
58 | static int tpa6130a2_i2c_read(int reg) | 63 | static int tpa6130a2_i2c_read(int reg) |
@@ -175,6 +180,40 @@ exit: | |||
175 | return ret; | 180 | return ret; |
176 | } | 181 | } |
177 | 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 | |||
178 | static int tpa6130a2_get_reg(struct snd_kcontrol *kcontrol, | 217 | static int tpa6130a2_get_reg(struct snd_kcontrol *kcontrol, |
179 | struct snd_ctl_elem_value *ucontrol) | 218 | struct snd_ctl_elem_value *ucontrol) |
180 | { | 219 | { |
@@ -238,6 +277,15 @@ static int tpa6130a2_set_reg(struct snd_kcontrol *kcontrol, | |||
238 | return 1; | 277 | return 1; |
239 | } | 278 | } |
240 | 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 | |||
241 | /* | 289 | /* |
242 | * 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 |
243 | * down in gain. | 291 | * down in gain. |
@@ -257,10 +305,22 @@ static const unsigned int tpa6130_tlv[] = { | |||
257 | }; | 305 | }; |
258 | 306 | ||
259 | static const struct snd_kcontrol_new tpa6130a2_controls[] = { | 307 | static const struct snd_kcontrol_new tpa6130a2_controls[] = { |
260 | SOC_SINGLE_EXT_TLV("TPA6130A2 Headphone Playback Volume", | 308 | SOC_SINGLE_EXT_TLV_TPA("TPA6130A2 Headphone Playback Volume", |
261 | TPA6130A2_REG_VOL_MUTE, 0, 0x3f, 0, | 309 | TPA6130A2_REG_VOL_MUTE, 0, TPA6130A2_GAIN_MAX, 0, |
262 | tpa6130a2_get_reg, tpa6130a2_set_reg, | 310 | tpa6130_tlv), |
263 | tpa6130_tlv), | 311 | }; |
312 | |||
313 | static const unsigned int tpa6140_tlv[] = { | ||
314 | TLV_DB_RANGE_HEAD(3), | ||
315 | 0, 8, TLV_DB_SCALE_ITEM(-5900, 400, 0), | ||
316 | 9, 16, TLV_DB_SCALE_ITEM(-2500, 200, 0), | ||
317 | 17, 31, TLV_DB_SCALE_ITEM(-1000, 100, 0), | ||
318 | }; | ||
319 | |||
320 | static const struct snd_kcontrol_new tpa6140a2_controls[] = { | ||
321 | SOC_SINGLE_EXT_TLV_TPA("TPA6140A2 Headphone Playback Volume", | ||
322 | TPA6130A2_REG_VOL_MUTE, 1, TPA6140A2_GAIN_MAX, 0, | ||
323 | tpa6140_tlv), | ||
264 | }; | 324 | }; |
265 | 325 | ||
266 | /* | 326 | /* |
@@ -368,13 +428,22 @@ static const struct snd_soc_dapm_route audio_map[] = { | |||
368 | 428 | ||
369 | int tpa6130a2_add_controls(struct snd_soc_codec *codec) | 429 | int tpa6130a2_add_controls(struct snd_soc_codec *codec) |
370 | { | 430 | { |
431 | struct tpa6130a2_data *data; | ||
432 | |||
433 | BUG_ON(tpa6130a2_client == NULL); | ||
434 | data = i2c_get_clientdata(tpa6130a2_client); | ||
435 | |||
371 | snd_soc_dapm_new_controls(codec, tpa6130a2_dapm_widgets, | 436 | snd_soc_dapm_new_controls(codec, tpa6130a2_dapm_widgets, |
372 | ARRAY_SIZE(tpa6130a2_dapm_widgets)); | 437 | ARRAY_SIZE(tpa6130a2_dapm_widgets)); |
373 | 438 | ||
374 | snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map)); | 439 | snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map)); |
375 | 440 | ||
376 | return snd_soc_add_controls(codec, tpa6130a2_controls, | 441 | if (data->id == TPA6140A2) |
377 | ARRAY_SIZE(tpa6130a2_controls)); | 442 | return snd_soc_add_controls(codec, tpa6140a2_controls, |
443 | ARRAY_SIZE(tpa6140a2_controls)); | ||
444 | else | ||
445 | return snd_soc_add_controls(codec, tpa6130a2_controls, | ||
446 | ARRAY_SIZE(tpa6130a2_controls)); | ||
378 | 447 | ||
379 | } | 448 | } |
380 | EXPORT_SYMBOL_GPL(tpa6130a2_add_controls); | 449 | EXPORT_SYMBOL_GPL(tpa6130a2_add_controls); |
@@ -407,6 +476,7 @@ static int __devinit tpa6130a2_probe(struct i2c_client *client, | |||
407 | 476 | ||
408 | pdata = client->dev.platform_data; | 477 | pdata = client->dev.platform_data; |
409 | data->power_gpio = pdata->power_gpio; | 478 | data->power_gpio = pdata->power_gpio; |
479 | data->id = pdata->id; | ||
410 | 480 | ||
411 | mutex_init(&data->mutex); | 481 | mutex_init(&data->mutex); |
412 | 482 | ||
@@ -425,20 +495,35 @@ static int __devinit tpa6130a2_probe(struct i2c_client *client, | |||
425 | gpio_direction_output(data->power_gpio, 0); | 495 | gpio_direction_output(data->power_gpio, 0); |
426 | } | 496 | } |
427 | 497 | ||
428 | switch (pdata->id) { | 498 | switch (data->id) { |
429 | case TPA6130A2: | 499 | case TPA6130A2: |
430 | for (i = 0; i < ARRAY_SIZE(data->supplies); i++) | 500 | for (i = 0; i < ARRAY_SIZE(data->supplies); i++) |
431 | 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; | ||
432 | break; | 507 | break; |
433 | case TPA6140A2: | 508 | case TPA6140A2: |
434 | for (i = 0; i < ARRAY_SIZE(data->supplies); i++) | 509 | for (i = 0; i < ARRAY_SIZE(data->supplies); i++) |
435 | 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; | ||
436 | break; | 516 | break; |
437 | default: | 517 | default: |
438 | dev_warn(dev, "Unknown TPA model (%d). Assuming 6130A2\n", | 518 | dev_warn(dev, "Unknown TPA model (%d). Assuming 6130A2\n", |
439 | pdata->id); | 519 | pdata->id); |
440 | for (i = 0; i < ARRAY_SIZE(data->supplies); i++) | 520 | for (i = 0; i < ARRAY_SIZE(data->supplies); i++) |
441 | 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; | ||
442 | } | 527 | } |
443 | 528 | ||
444 | ret = regulator_bulk_get(dev, ARRAY_SIZE(data->supplies), | 529 | ret = regulator_bulk_get(dev, ARRAY_SIZE(data->supplies), |