aboutsummaryrefslogtreecommitdiffstats
path: root/sound/soc
diff options
context:
space:
mode:
Diffstat (limited to 'sound/soc')
-rw-r--r--sound/soc/codecs/twl4030.c347
-rw-r--r--sound/soc/codecs/twl4030.h7
2 files changed, 182 insertions, 172 deletions
diff --git a/sound/soc/codecs/twl4030.c b/sound/soc/codecs/twl4030.c
index 2c279cd8deb5..31e44e346dc8 100644
--- a/sound/soc/codecs/twl4030.c
+++ b/sound/soc/codecs/twl4030.c
@@ -298,6 +298,55 @@ static const struct soc_enum twl4030_handsfreer_enum =
298static const struct snd_kcontrol_new twl4030_dapm_handsfreer_control = 298static const struct snd_kcontrol_new twl4030_dapm_handsfreer_control =
299SOC_DAPM_ENUM("Route", twl4030_handsfreer_enum); 299SOC_DAPM_ENUM("Route", twl4030_handsfreer_enum);
300 300
301/* Left analog microphone selection */
302static const char *twl4030_analoglmic_texts[] =
303 {"Off", "Main mic", "Headset mic", "Invalid", "AUXL",
304 "Invalid", "Invalid", "Invalid", "Carkit mic"};
305
306static const struct soc_enum twl4030_analoglmic_enum =
307 SOC_ENUM_SINGLE(TWL4030_REG_ANAMICL, 0,
308 ARRAY_SIZE(twl4030_analoglmic_texts),
309 twl4030_analoglmic_texts);
310
311static const struct snd_kcontrol_new twl4030_dapm_analoglmic_control =
312SOC_DAPM_ENUM("Route", twl4030_analoglmic_enum);
313
314/* Right analog microphone selection */
315static const char *twl4030_analogrmic_texts[] =
316 {"Off", "Sub mic", "Invalid", "Invalid", "AUXR"};
317
318static const struct soc_enum twl4030_analogrmic_enum =
319 SOC_ENUM_SINGLE(TWL4030_REG_ANAMICR, 0,
320 ARRAY_SIZE(twl4030_analogrmic_texts),
321 twl4030_analogrmic_texts);
322
323static const struct snd_kcontrol_new twl4030_dapm_analogrmic_control =
324SOC_DAPM_ENUM("Route", twl4030_analogrmic_enum);
325
326/* TX1 L/R Analog/Digital microphone selection */
327static const char *twl4030_micpathtx1_texts[] =
328 {"Analog", "Digimic0"};
329
330static const struct soc_enum twl4030_micpathtx1_enum =
331 SOC_ENUM_SINGLE(TWL4030_REG_ADCMICSEL, 0,
332 ARRAY_SIZE(twl4030_micpathtx1_texts),
333 twl4030_micpathtx1_texts);
334
335static const struct snd_kcontrol_new twl4030_dapm_micpathtx1_control =
336SOC_DAPM_ENUM("Route", twl4030_micpathtx1_enum);
337
338/* TX2 L/R Analog/Digital microphone selection */
339static const char *twl4030_micpathtx2_texts[] =
340 {"Analog", "Digimic1"};
341
342static const struct soc_enum twl4030_micpathtx2_enum =
343 SOC_ENUM_SINGLE(TWL4030_REG_ADCMICSEL, 2,
344 ARRAY_SIZE(twl4030_micpathtx2_texts),
345 twl4030_micpathtx2_texts);
346
347static const struct snd_kcontrol_new twl4030_dapm_micpathtx2_control =
348SOC_DAPM_ENUM("Route", twl4030_micpathtx2_enum);
349
301/* 350/*
302 * This function filters out the non valid mux settings, named as "Invalid" 351 * This function filters out the non valid mux settings, named as "Invalid"
303 * in the enum texts. 352 * in the enum texts.
@@ -320,6 +369,36 @@ static int twl4030_enum_event(struct snd_soc_dapm_widget *w,
320 return ret; 369 return ret;
321} 370}
322 371
372static int micpath_event(struct snd_soc_dapm_widget *w,
373 struct snd_kcontrol *kcontrol, int event)
374{
375 struct soc_enum *e = (struct soc_enum *)w->kcontrols->private_value;
376 unsigned char adcmicsel, micbias_ctl;
377
378 adcmicsel = twl4030_read_reg_cache(w->codec, TWL4030_REG_ADCMICSEL);
379 micbias_ctl = twl4030_read_reg_cache(w->codec, TWL4030_REG_MICBIAS_CTL);
380 /* Prepare the bits for the given TX path:
381 * shift_l == 0: TX1 microphone path
382 * shift_l == 2: TX2 microphone path */
383 if (e->shift_l) {
384 /* TX2 microphone path */
385 if (adcmicsel & TWL4030_TX2IN_SEL)
386 micbias_ctl |= TWL4030_MICBIAS2_CTL; /* digimic */
387 else
388 micbias_ctl &= ~TWL4030_MICBIAS2_CTL;
389 } else {
390 /* TX1 microphone path */
391 if (adcmicsel & TWL4030_TX1IN_SEL)
392 micbias_ctl |= TWL4030_MICBIAS1_CTL; /* digimic */
393 else
394 micbias_ctl &= ~TWL4030_MICBIAS1_CTL;
395 }
396
397 twl4030_write(w->codec, TWL4030_REG_MICBIAS_CTL, micbias_ctl);
398
399 return 0;
400}
401
323static int handsfree_event(struct snd_soc_dapm_widget *w, 402static int handsfree_event(struct snd_soc_dapm_widget *w,
324 struct snd_kcontrol *kcontrol, int event) 403 struct snd_kcontrol *kcontrol, int event)
325{ 404{
@@ -501,162 +580,6 @@ static int snd_soc_put_volsw_r2_twl4030(struct snd_kcontrol *kcontrol,
501 return err; 580 return err;
502} 581}
503 582
504static int twl4030_get_left_input(struct snd_kcontrol *kcontrol,
505 struct snd_ctl_elem_value *ucontrol)
506{
507 struct snd_soc_codec *codec = kcontrol->private_data;
508 u8 reg = twl4030_read_reg_cache(codec, TWL4030_REG_ANAMICL);
509 int result = 0;
510
511 /* one bit must be set a time */
512 reg &= TWL4030_CKMIC_EN | TWL4030_AUXL_EN | TWL4030_HSMIC_EN
513 | TWL4030_MAINMIC_EN;
514 if (reg != 0) {
515 result++;
516 while ((reg & 1) == 0) {
517 result++;
518 reg >>= 1;
519 }
520 }
521
522 ucontrol->value.integer.value[0] = result;
523 return 0;
524}
525
526static int twl4030_put_left_input(struct snd_kcontrol *kcontrol,
527 struct snd_ctl_elem_value *ucontrol)
528{
529 struct snd_soc_codec *codec = kcontrol->private_data;
530 int value = ucontrol->value.integer.value[0];
531 u8 anamicl, micbias, avadc_ctl;
532
533 anamicl = twl4030_read_reg_cache(codec, TWL4030_REG_ANAMICL);
534 anamicl &= ~(TWL4030_CKMIC_EN | TWL4030_AUXL_EN | TWL4030_HSMIC_EN
535 | TWL4030_MAINMIC_EN);
536 micbias = twl4030_read_reg_cache(codec, TWL4030_REG_MICBIAS_CTL);
537 micbias &= ~(TWL4030_HSMICBIAS_EN | TWL4030_MICBIAS1_EN);
538 avadc_ctl = twl4030_read_reg_cache(codec, TWL4030_REG_AVADC_CTL);
539
540 switch (value) {
541 case 1:
542 anamicl |= TWL4030_MAINMIC_EN;
543 micbias |= TWL4030_MICBIAS1_EN;
544 break;
545 case 2:
546 anamicl |= TWL4030_HSMIC_EN;
547 micbias |= TWL4030_HSMICBIAS_EN;
548 break;
549 case 3:
550 anamicl |= TWL4030_AUXL_EN;
551 break;
552 case 4:
553 anamicl |= TWL4030_CKMIC_EN;
554 break;
555 default:
556 break;
557 }
558
559 /* If some input is selected, enable amp and ADC */
560 if (value != 0) {
561 anamicl |= TWL4030_MICAMPL_EN;
562 avadc_ctl |= TWL4030_ADCL_EN;
563 } else {
564 anamicl &= ~TWL4030_MICAMPL_EN;
565 avadc_ctl &= ~TWL4030_ADCL_EN;
566 }
567
568 twl4030_write(codec, TWL4030_REG_ANAMICL, anamicl);
569 twl4030_write(codec, TWL4030_REG_MICBIAS_CTL, micbias);
570 twl4030_write(codec, TWL4030_REG_AVADC_CTL, avadc_ctl);
571
572 return 1;
573}
574
575static int twl4030_get_right_input(struct snd_kcontrol *kcontrol,
576 struct snd_ctl_elem_value *ucontrol)
577{
578 struct snd_soc_codec *codec = kcontrol->private_data;
579 u8 reg = twl4030_read_reg_cache(codec, TWL4030_REG_ANAMICR);
580 int value = 0;
581
582 reg &= TWL4030_SUBMIC_EN|TWL4030_AUXR_EN;
583 switch (reg) {
584 case TWL4030_SUBMIC_EN:
585 value = 1;
586 break;
587 case TWL4030_AUXR_EN:
588 value = 2;
589 break;
590 default:
591 break;
592 }
593
594 ucontrol->value.integer.value[0] = value;
595 return 0;
596}
597
598static int twl4030_put_right_input(struct snd_kcontrol *kcontrol,
599 struct snd_ctl_elem_value *ucontrol)
600{
601 struct snd_soc_codec *codec = kcontrol->private_data;
602 int value = ucontrol->value.integer.value[0];
603 u8 anamicr, micbias, avadc_ctl;
604
605 anamicr = twl4030_read_reg_cache(codec, TWL4030_REG_ANAMICR);
606 anamicr &= ~(TWL4030_SUBMIC_EN|TWL4030_AUXR_EN);
607 micbias = twl4030_read_reg_cache(codec, TWL4030_REG_MICBIAS_CTL);
608 micbias &= ~TWL4030_MICBIAS2_EN;
609 avadc_ctl = twl4030_read_reg_cache(codec, TWL4030_REG_AVADC_CTL);
610
611 switch (value) {
612 case 1:
613 anamicr |= TWL4030_SUBMIC_EN;
614 micbias |= TWL4030_MICBIAS2_EN;
615 break;
616 case 2:
617 anamicr |= TWL4030_AUXR_EN;
618 break;
619 default:
620 break;
621 }
622
623 if (value != 0) {
624 anamicr |= TWL4030_MICAMPR_EN;
625 avadc_ctl |= TWL4030_ADCR_EN;
626 } else {
627 anamicr &= ~TWL4030_MICAMPR_EN;
628 avadc_ctl &= ~TWL4030_ADCR_EN;
629 }
630
631 twl4030_write(codec, TWL4030_REG_ANAMICR, anamicr);
632 twl4030_write(codec, TWL4030_REG_MICBIAS_CTL, micbias);
633 twl4030_write(codec, TWL4030_REG_AVADC_CTL, avadc_ctl);
634
635 return 1;
636}
637
638static const char *twl4030_left_in_sel[] = {
639 "None",
640 "Main Mic",
641 "Headset Mic",
642 "Line In",
643 "Carkit Mic",
644};
645
646static const char *twl4030_right_in_sel[] = {
647 "None",
648 "Sub Mic",
649 "Line In",
650};
651
652static const struct soc_enum twl4030_left_input_mux =
653 SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(twl4030_left_in_sel),
654 twl4030_left_in_sel);
655
656static const struct soc_enum twl4030_right_input_mux =
657 SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(twl4030_right_in_sel),
658 twl4030_right_in_sel);
659
660/* 583/*
661 * FGAIN volume control: 584 * FGAIN volume control:
662 * from -62 to 0 dB in 1 dB steps (mute instead of -63 dB) 585 * from -62 to 0 dB in 1 dB steps (mute instead of -63 dB)
@@ -739,18 +662,15 @@ static const struct snd_kcontrol_new twl4030_snd_controls[] = {
739 TWL4030_REG_EAR_CTL, 4, 3, 0, output_tvl), 662 TWL4030_REG_EAR_CTL, 4, 3, 0, output_tvl),
740 663
741 /* Common capture gain controls */ 664 /* Common capture gain controls */
742 SOC_DOUBLE_R_TLV("Capture Volume", 665 SOC_DOUBLE_R_TLV("TX1 Digital Capture Volume",
743 TWL4030_REG_ATXL1PGA, TWL4030_REG_ATXR1PGA, 666 TWL4030_REG_ATXL1PGA, TWL4030_REG_ATXR1PGA,
744 0, 0x1f, 0, digital_capture_tlv), 667 0, 0x1f, 0, digital_capture_tlv),
668 SOC_DOUBLE_R_TLV("TX2 Digital Capture Volume",
669 TWL4030_REG_AVTXL2PGA, TWL4030_REG_AVTXR2PGA,
670 0, 0x1f, 0, digital_capture_tlv),
745 671
746 SOC_DOUBLE_TLV("Input Boost Volume", TWL4030_REG_ANAMIC_GAIN, 672 SOC_DOUBLE_TLV("Analog Capture Volume", TWL4030_REG_ANAMIC_GAIN,
747 0, 3, 5, 0, input_gain_tlv), 673 0, 3, 5, 0, input_gain_tlv),
748
749 /* Input source controls */
750 SOC_ENUM_EXT("Left Input Source", twl4030_left_input_mux,
751 twl4030_get_left_input, twl4030_put_left_input),
752 SOC_ENUM_EXT("Right Input Source", twl4030_right_input_mux,
753 twl4030_get_right_input, twl4030_put_right_input),
754}; 674};
755 675
756/* add non dapm controls */ 676/* add non dapm controls */
@@ -770,9 +690,19 @@ static int twl4030_add_controls(struct snd_soc_codec *codec)
770} 690}
771 691
772static const struct snd_soc_dapm_widget twl4030_dapm_widgets[] = { 692static const struct snd_soc_dapm_widget twl4030_dapm_widgets[] = {
773 SND_SOC_DAPM_INPUT("INL"), 693 /* Left channel inputs */
774 SND_SOC_DAPM_INPUT("INR"), 694 SND_SOC_DAPM_INPUT("MAINMIC"),
775 695 SND_SOC_DAPM_INPUT("HSMIC"),
696 SND_SOC_DAPM_INPUT("AUXL"),
697 SND_SOC_DAPM_INPUT("CARKITMIC"),
698 /* Right channel inputs */
699 SND_SOC_DAPM_INPUT("SUBMIC"),
700 SND_SOC_DAPM_INPUT("AUXR"),
701 /* Digital microphones (Stereo) */
702 SND_SOC_DAPM_INPUT("DIGIMIC0"),
703 SND_SOC_DAPM_INPUT("DIGIMIC1"),
704
705 /* Outputs */
776 SND_SOC_DAPM_OUTPUT("OUTL"), 706 SND_SOC_DAPM_OUTPUT("OUTL"),
777 SND_SOC_DAPM_OUTPUT("OUTR"), 707 SND_SOC_DAPM_OUTPUT("OUTR"),
778 SND_SOC_DAPM_OUTPUT("EARPIECE"), 708 SND_SOC_DAPM_OUTPUT("EARPIECE"),
@@ -835,8 +765,50 @@ static const struct snd_soc_dapm_widget twl4030_dapm_widgets[] = {
835 &twl4030_dapm_handsfreer_control, handsfree_event, 765 &twl4030_dapm_handsfreer_control, handsfree_event,
836 SND_SOC_DAPM_POST_PMU|SND_SOC_DAPM_POST_PMD), 766 SND_SOC_DAPM_POST_PMU|SND_SOC_DAPM_POST_PMD),
837 767
838 SND_SOC_DAPM_ADC("ADCL", "Left Capture", SND_SOC_NOPM, 0, 0), 768 /* Introducing four virtual ADC, since TWL4030 have four channel for
839 SND_SOC_DAPM_ADC("ADCR", "Right Capture", SND_SOC_NOPM, 0, 0), 769 capture */
770 SND_SOC_DAPM_ADC("ADC Virtual Left1", "Left Front Capture",
771 SND_SOC_NOPM, 0, 0),
772 SND_SOC_DAPM_ADC("ADC Virtual Right1", "Right Front Capture",
773 SND_SOC_NOPM, 0, 0),
774 SND_SOC_DAPM_ADC("ADC Virtual Left2", "Left Rear Capture",
775 SND_SOC_NOPM, 0, 0),
776 SND_SOC_DAPM_ADC("ADC Virtual Right2", "Right Rear Capture",
777 SND_SOC_NOPM, 0, 0),
778
779 /* Analog/Digital mic path selection.
780 TX1 Left/Right: either analog Left/Right or Digimic0
781 TX2 Left/Right: either analog Left/Right or Digimic1 */
782 SND_SOC_DAPM_MUX_E("TX1 Capture Route", SND_SOC_NOPM, 0, 0,
783 &twl4030_dapm_micpathtx1_control, micpath_event,
784 SND_SOC_DAPM_POST_PMU|SND_SOC_DAPM_POST_PMD|
785 SND_SOC_DAPM_POST_REG),
786 SND_SOC_DAPM_MUX_E("TX2 Capture Route", SND_SOC_NOPM, 0, 0,
787 &twl4030_dapm_micpathtx2_control, micpath_event,
788 SND_SOC_DAPM_POST_PMU|SND_SOC_DAPM_POST_PMD|
789 SND_SOC_DAPM_POST_REG),
790
791 /* Analog input muxes with power switch for the physical ADCL/R */
792 SND_SOC_DAPM_MUX_E("Analog Left Capture Route",
793 TWL4030_REG_AVADC_CTL, 3, 0, &twl4030_dapm_analoglmic_control,
794 twl4030_enum_event, SND_SOC_DAPM_PRE_REG),
795 SND_SOC_DAPM_MUX_E("Analog Right Capture Route",
796 TWL4030_REG_AVADC_CTL, 1, 0, &twl4030_dapm_analogrmic_control,
797 twl4030_enum_event, SND_SOC_DAPM_PRE_REG),
798
799 SND_SOC_DAPM_PGA("Analog Left Amplifier",
800 TWL4030_REG_ANAMICL, 4, 0, NULL, 0),
801 SND_SOC_DAPM_PGA("Analog Right Amplifier",
802 TWL4030_REG_ANAMICR, 4, 0, NULL, 0),
803
804 SND_SOC_DAPM_PGA("Digimic0 Enable",
805 TWL4030_REG_ADCMICSEL, 1, 0, NULL, 0),
806 SND_SOC_DAPM_PGA("Digimic1 Enable",
807 TWL4030_REG_ADCMICSEL, 3, 0, NULL, 0),
808
809 SND_SOC_DAPM_MICBIAS("Mic Bias 1", TWL4030_REG_MICBIAS_CTL, 0, 0),
810 SND_SOC_DAPM_MICBIAS("Mic Bias 2", TWL4030_REG_MICBIAS_CTL, 1, 0),
811 SND_SOC_DAPM_MICBIAS("Headset Mic Bias", TWL4030_REG_MICBIAS_CTL, 2, 0),
840}; 812};
841 813
842static const struct snd_soc_dapm_route intercon[] = { 814static const struct snd_soc_dapm_route intercon[] = {
@@ -892,9 +864,39 @@ static const struct snd_soc_dapm_route intercon[] = {
892 {"HFL", NULL, "HandsfreeL Mux"}, 864 {"HFL", NULL, "HandsfreeL Mux"},
893 {"HFR", NULL, "HandsfreeR Mux"}, 865 {"HFR", NULL, "HandsfreeR Mux"},
894 866
895 /* inputs */ 867 /* Capture path */
896 {"ADCL", NULL, "INL"}, 868 {"Analog Left Capture Route", "Main mic", "MAINMIC"},
897 {"ADCR", NULL, "INR"}, 869 {"Analog Left Capture Route", "Headset mic", "HSMIC"},
870 {"Analog Left Capture Route", "AUXL", "AUXL"},
871 {"Analog Left Capture Route", "Carkit mic", "CARKITMIC"},
872
873 {"Analog Right Capture Route", "Sub mic", "SUBMIC"},
874 {"Analog Right Capture Route", "AUXR", "AUXR"},
875
876 {"Analog Left Amplifier", NULL, "Analog Left Capture Route"},
877 {"Analog Right Amplifier", NULL, "Analog Right Capture Route"},
878
879 {"Digimic0 Enable", NULL, "DIGIMIC0"},
880 {"Digimic1 Enable", NULL, "DIGIMIC1"},
881
882 /* TX1 Left capture path */
883 {"TX1 Capture Route", "Analog", "Analog Left Amplifier"},
884 {"TX1 Capture Route", "Digimic0", "Digimic0 Enable"},
885 /* TX1 Right capture path */
886 {"TX1 Capture Route", "Analog", "Analog Right Amplifier"},
887 {"TX1 Capture Route", "Digimic0", "Digimic0 Enable"},
888 /* TX2 Left capture path */
889 {"TX2 Capture Route", "Analog", "Analog Left Amplifier"},
890 {"TX2 Capture Route", "Digimic1", "Digimic1 Enable"},
891 /* TX2 Right capture path */
892 {"TX2 Capture Route", "Analog", "Analog Right Amplifier"},
893 {"TX2 Capture Route", "Digimic1", "Digimic1 Enable"},
894
895 {"ADC Virtual Left1", NULL, "TX1 Capture Route"},
896 {"ADC Virtual Right1", NULL, "TX1 Capture Route"},
897 {"ADC Virtual Left2", NULL, "TX2 Capture Route"},
898 {"ADC Virtual Right2", NULL, "TX2 Capture Route"},
899
898}; 900};
899 901
900static int twl4030_add_widgets(struct snd_soc_codec *codec) 902static int twl4030_add_widgets(struct snd_soc_codec *codec)
@@ -921,6 +923,7 @@ static void twl4030_power_up(struct snd_soc_codec *codec)
921 twl4030_write(codec, TWL4030_REG_ANAMICL, 923 twl4030_write(codec, TWL4030_REG_ANAMICL,
922 anamicl | TWL4030_CNCL_OFFSET_START); 924 anamicl | TWL4030_CNCL_OFFSET_START);
923 925
926
924 /* wait for offset cancellation to complete */ 927 /* wait for offset cancellation to complete */
925 do { 928 do {
926 /* this takes a little while, so don't slam i2c */ 929 /* this takes a little while, so don't slam i2c */
diff --git a/sound/soc/codecs/twl4030.h b/sound/soc/codecs/twl4030.h
index 54615c76802b..442e5a828617 100644
--- a/sound/soc/codecs/twl4030.h
+++ b/sound/soc/codecs/twl4030.h
@@ -147,6 +147,13 @@
147#define TWL4030_AVADC_CLK_PRIORITY 0x04 147#define TWL4030_AVADC_CLK_PRIORITY 0x04
148#define TWL4030_ADCR_EN 0x02 148#define TWL4030_ADCR_EN 0x02
149 149
150/* TWL4030_REG_ADCMICSEL (0x08) Fields */
151
152#define TWL4030_DIGMIC1_EN 0x08
153#define TWL4030_TX2IN_SEL 0x04
154#define TWL4030_DIGMIC0_EN 0x02
155#define TWL4030_TX1IN_SEL 0x01
156
150/* AUDIO_IF (0x0E) Fields */ 157/* AUDIO_IF (0x0E) Fields */
151 158
152#define TWL4030_AIF_SLAVE_EN 0x80 159#define TWL4030_AIF_SLAVE_EN 0x80