aboutsummaryrefslogtreecommitdiffstats
path: root/sound/soc/codecs/twl4030.c
diff options
context:
space:
mode:
Diffstat (limited to 'sound/soc/codecs/twl4030.c')
-rw-r--r--sound/soc/codecs/twl4030.c177
1 files changed, 174 insertions, 3 deletions
diff --git a/sound/soc/codecs/twl4030.c b/sound/soc/codecs/twl4030.c
index ffd5120697a2..3c9fdf2c6c7b 100644
--- a/sound/soc/codecs/twl4030.c
+++ b/sound/soc/codecs/twl4030.c
@@ -46,9 +46,9 @@ static const u8 twl4030_reg[TWL4030_CACHEREGNUM] = {
46 0xc3, /* REG_OPTION (0x2) */ 46 0xc3, /* REG_OPTION (0x2) */
47 0x00, /* REG_UNKNOWN (0x3) */ 47 0x00, /* REG_UNKNOWN (0x3) */
48 0x00, /* REG_MICBIAS_CTL (0x4) */ 48 0x00, /* REG_MICBIAS_CTL (0x4) */
49 0x24, /* REG_ANAMICL (0x5) */ 49 0x20, /* REG_ANAMICL (0x5) */
50 0x04, /* REG_ANAMICR (0x6) */ 50 0x00, /* REG_ANAMICR (0x6) */
51 0x0a, /* REG_AVADC_CTL (0x7) */ 51 0x00, /* REG_AVADC_CTL (0x7) */
52 0x00, /* REG_ADCMICSEL (0x8) */ 52 0x00, /* REG_ADCMICSEL (0x8) */
53 0x00, /* REG_DIGMIXING (0x9) */ 53 0x00, /* REG_DIGMIXING (0x9) */
54 0x0c, /* REG_ATXL1PGA (0xA) */ 54 0x0c, /* REG_ATXL1PGA (0xA) */
@@ -347,6 +347,162 @@ static int snd_soc_put_volsw_r2_twl4030(struct snd_kcontrol *kcontrol,
347 return err; 347 return err;
348} 348}
349 349
350static int twl4030_get_left_input(struct snd_kcontrol *kcontrol,
351 struct snd_ctl_elem_value *ucontrol)
352{
353 struct snd_soc_codec *codec = kcontrol->private_data;
354 u8 reg = twl4030_read_reg_cache(codec, TWL4030_REG_ANAMICL);
355 int result = 0;
356
357 /* one bit must be set a time */
358 reg &= TWL4030_CKMIC_EN | TWL4030_AUXL_EN | TWL4030_HSMIC_EN
359 | TWL4030_MAINMIC_EN;
360 if (reg != 0) {
361 result++;
362 while ((reg & 1) == 0) {
363 result++;
364 reg >>= 1;
365 }
366 }
367
368 ucontrol->value.integer.value[0] = result;
369 return 0;
370}
371
372static int twl4030_put_left_input(struct snd_kcontrol *kcontrol,
373 struct snd_ctl_elem_value *ucontrol)
374{
375 struct snd_soc_codec *codec = kcontrol->private_data;
376 int value = ucontrol->value.integer.value[0];
377 u8 anamicl, micbias, avadc_ctl;
378
379 anamicl = twl4030_read_reg_cache(codec, TWL4030_REG_ANAMICL);
380 anamicl &= ~(TWL4030_CKMIC_EN | TWL4030_AUXL_EN | TWL4030_HSMIC_EN
381 | TWL4030_MAINMIC_EN);
382 micbias = twl4030_read_reg_cache(codec, TWL4030_REG_MICBIAS_CTL);
383 micbias &= ~(TWL4030_HSMICBIAS_EN | TWL4030_MICBIAS1_EN);
384 avadc_ctl = twl4030_read_reg_cache(codec, TWL4030_REG_AVADC_CTL);
385
386 switch (value) {
387 case 1:
388 anamicl |= TWL4030_MAINMIC_EN;
389 micbias |= TWL4030_MICBIAS1_EN;
390 break;
391 case 2:
392 anamicl |= TWL4030_HSMIC_EN;
393 micbias |= TWL4030_HSMICBIAS_EN;
394 break;
395 case 3:
396 anamicl |= TWL4030_AUXL_EN;
397 break;
398 case 4:
399 anamicl |= TWL4030_CKMIC_EN;
400 break;
401 default:
402 break;
403 }
404
405 /* If some input is selected, enable amp and ADC */
406 if (value != 0) {
407 anamicl |= TWL4030_MICAMPL_EN;
408 avadc_ctl |= TWL4030_ADCL_EN;
409 } else {
410 anamicl &= ~TWL4030_MICAMPL_EN;
411 avadc_ctl &= ~TWL4030_ADCL_EN;
412 }
413
414 twl4030_write(codec, TWL4030_REG_ANAMICL, anamicl);
415 twl4030_write(codec, TWL4030_REG_MICBIAS_CTL, micbias);
416 twl4030_write(codec, TWL4030_REG_AVADC_CTL, avadc_ctl);
417
418 return 1;
419}
420
421static int twl4030_get_right_input(struct snd_kcontrol *kcontrol,
422 struct snd_ctl_elem_value *ucontrol)
423{
424 struct snd_soc_codec *codec = kcontrol->private_data;
425 u8 reg = twl4030_read_reg_cache(codec, TWL4030_REG_ANAMICR);
426 int value = 0;
427
428 reg &= TWL4030_SUBMIC_EN|TWL4030_AUXR_EN;
429 switch (reg) {
430 case TWL4030_SUBMIC_EN:
431 value = 1;
432 break;
433 case TWL4030_AUXR_EN:
434 value = 2;
435 break;
436 default:
437 break;
438 }
439
440 ucontrol->value.integer.value[0] = value;
441 return 0;
442}
443
444static int twl4030_put_right_input(struct snd_kcontrol *kcontrol,
445 struct snd_ctl_elem_value *ucontrol)
446{
447 struct snd_soc_codec *codec = kcontrol->private_data;
448 int value = ucontrol->value.integer.value[0];
449 u8 anamicr, micbias, avadc_ctl;
450
451 anamicr = twl4030_read_reg_cache(codec, TWL4030_REG_ANAMICR);
452 anamicr &= ~(TWL4030_SUBMIC_EN|TWL4030_AUXR_EN);
453 micbias = twl4030_read_reg_cache(codec, TWL4030_REG_MICBIAS_CTL);
454 micbias &= ~TWL4030_MICBIAS2_EN;
455 avadc_ctl = twl4030_read_reg_cache(codec, TWL4030_REG_AVADC_CTL);
456
457 switch (value) {
458 case 1:
459 anamicr |= TWL4030_SUBMIC_EN;
460 micbias |= TWL4030_MICBIAS2_EN;
461 break;
462 case 2:
463 anamicr |= TWL4030_AUXR_EN;
464 break;
465 default:
466 break;
467 }
468
469 if (value != 0) {
470 anamicr |= TWL4030_MICAMPR_EN;
471 avadc_ctl |= TWL4030_ADCR_EN;
472 } else {
473 anamicr &= ~TWL4030_MICAMPR_EN;
474 avadc_ctl &= ~TWL4030_ADCR_EN;
475 }
476
477 twl4030_write(codec, TWL4030_REG_ANAMICR, anamicr);
478 twl4030_write(codec, TWL4030_REG_MICBIAS_CTL, micbias);
479 twl4030_write(codec, TWL4030_REG_AVADC_CTL, avadc_ctl);
480
481 return 1;
482}
483
484static const char *twl4030_left_in_sel[] = {
485 "None",
486 "Main Mic",
487 "Headset Mic",
488 "Line In",
489 "Carkit Mic",
490};
491
492static const char *twl4030_right_in_sel[] = {
493 "None",
494 "Sub Mic",
495 "Line In",
496};
497
498static const struct soc_enum twl4030_left_input_mux =
499 SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(twl4030_left_in_sel),
500 twl4030_left_in_sel);
501
502static const struct soc_enum twl4030_right_input_mux =
503 SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(twl4030_right_in_sel),
504 twl4030_right_in_sel);
505
350/* 506/*
351 * FGAIN volume control: 507 * FGAIN volume control:
352 * from -62 to 0 dB in 1 dB steps (mute instead of -63 dB) 508 * from -62 to 0 dB in 1 dB steps (mute instead of -63 dB)
@@ -378,6 +534,12 @@ static DECLARE_TLV_DB_SCALE(output_tvl, -1200, 600, 1);
378 */ 534 */
379static DECLARE_TLV_DB_SCALE(digital_capture_tlv, 0, 100, 0); 535static DECLARE_TLV_DB_SCALE(digital_capture_tlv, 0, 100, 0);
380 536
537/*
538 * Gain control for input amplifiers
539 * 0 dB to 30 dB in 6 dB steps
540 */
541static DECLARE_TLV_DB_SCALE(input_gain_tlv, 0, 600, 0);
542
381static const struct snd_kcontrol_new twl4030_snd_controls[] = { 543static const struct snd_kcontrol_new twl4030_snd_controls[] = {
382 /* Common playback gain controls */ 544 /* Common playback gain controls */
383 SOC_DOUBLE_R_TLV("DAC1 Digital Fine Playback Volume", 545 SOC_DOUBLE_R_TLV("DAC1 Digital Fine Playback Volume",
@@ -420,6 +582,15 @@ static const struct snd_kcontrol_new twl4030_snd_controls[] = {
420 SOC_DOUBLE_R_TLV("Capture Volume", 582 SOC_DOUBLE_R_TLV("Capture Volume",
421 TWL4030_REG_ATXL1PGA, TWL4030_REG_ATXR1PGA, 583 TWL4030_REG_ATXL1PGA, TWL4030_REG_ATXR1PGA,
422 0, 0x1f, 0, digital_capture_tlv), 584 0, 0x1f, 0, digital_capture_tlv),
585
586 SOC_DOUBLE_TLV("Input Boost Volume", TWL4030_REG_ANAMIC_GAIN,
587 0, 3, 5, 0, input_gain_tlv),
588
589 /* Input source controls */
590 SOC_ENUM_EXT("Left Input Source", twl4030_left_input_mux,
591 twl4030_get_left_input, twl4030_put_left_input),
592 SOC_ENUM_EXT("Right Input Source", twl4030_right_input_mux,
593 twl4030_get_right_input, twl4030_put_right_input),
423}; 594};
424 595
425/* add non dapm controls */ 596/* add non dapm controls */