diff options
Diffstat (limited to 'sound/soc/codecs/twl4030.c')
-rw-r--r-- | sound/soc/codecs/twl4030.c | 177 |
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 | ||
350 | static 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 | |||
372 | static 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 | |||
421 | static 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 | |||
444 | static 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 | |||
484 | static const char *twl4030_left_in_sel[] = { | ||
485 | "None", | ||
486 | "Main Mic", | ||
487 | "Headset Mic", | ||
488 | "Line In", | ||
489 | "Carkit Mic", | ||
490 | }; | ||
491 | |||
492 | static const char *twl4030_right_in_sel[] = { | ||
493 | "None", | ||
494 | "Sub Mic", | ||
495 | "Line In", | ||
496 | }; | ||
497 | |||
498 | static 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 | |||
502 | static 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 | */ |
379 | static DECLARE_TLV_DB_SCALE(digital_capture_tlv, 0, 100, 0); | 535 | static 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 | */ | ||
541 | static DECLARE_TLV_DB_SCALE(input_gain_tlv, 0, 600, 0); | ||
542 | |||
381 | static const struct snd_kcontrol_new twl4030_snd_controls[] = { | 543 | static 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 */ |