aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--sound/soc/codecs/twl4030.c116
1 files changed, 91 insertions, 25 deletions
diff --git a/sound/soc/codecs/twl4030.c b/sound/soc/codecs/twl4030.c
index 99fe44f70507..f554672f67c1 100644
--- a/sound/soc/codecs/twl4030.c
+++ b/sound/soc/codecs/twl4030.c
@@ -130,6 +130,12 @@ struct twl4030_priv {
130 unsigned int rate; 130 unsigned int rate;
131 unsigned int sample_bits; 131 unsigned int sample_bits;
132 unsigned int channels; 132 unsigned int channels;
133
134 unsigned int sysclk;
135
136 /* Headset output state handling */
137 unsigned int hsl_enabled;
138 unsigned int hsr_enabled;
133}; 139};
134 140
135/* 141/*
@@ -564,39 +570,85 @@ static int handsfree_event(struct snd_soc_dapm_widget *w,
564 return 0; 570 return 0;
565} 571}
566 572
567static int headsetl_event(struct snd_soc_dapm_widget *w, 573static void headset_ramp(struct snd_soc_codec *codec, int ramp)
568 struct snd_kcontrol *kcontrol, int event)
569{ 574{
570 unsigned char hs_gain, hs_pop; 575 unsigned char hs_gain, hs_pop;
576 struct twl4030_priv *twl4030 = codec->private_data;
577 /* Base values for ramp delay calculation: 2^19 - 2^26 */
578 unsigned int ramp_base[] = {524288, 1048576, 2097152, 4194304,
579 8388608, 16777216, 33554432, 67108864};
571 580
572 /* Save the current volume */ 581 hs_gain = twl4030_read_reg_cache(codec, TWL4030_REG_HS_GAIN_SET);
573 hs_gain = twl4030_read_reg_cache(w->codec, TWL4030_REG_HS_GAIN_SET); 582 hs_pop = twl4030_read_reg_cache(codec, TWL4030_REG_HS_POPN_SET);
574 hs_pop = twl4030_read_reg_cache(w->codec, TWL4030_REG_HS_POPN_SET);
575 583
576 switch (event) { 584 if (ramp) {
577 case SND_SOC_DAPM_POST_PMU: 585 /* Headset ramp-up according to the TRM */
578 /* Do the anti-pop/bias ramp enable according to the TRM */
579 hs_pop |= TWL4030_VMID_EN; 586 hs_pop |= TWL4030_VMID_EN;
580 twl4030_write(w->codec, TWL4030_REG_HS_POPN_SET, hs_pop); 587 twl4030_write(codec, TWL4030_REG_HS_POPN_SET, hs_pop);
581 /* Is this needed? Can we just use whatever gain here? */ 588 twl4030_write(codec, TWL4030_REG_HS_GAIN_SET, hs_gain);
582 twl4030_write(w->codec, TWL4030_REG_HS_GAIN_SET,
583 (hs_gain & (~0x0f)) | 0x0a);
584 hs_pop |= TWL4030_RAMP_EN; 589 hs_pop |= TWL4030_RAMP_EN;
585 twl4030_write(w->codec, TWL4030_REG_HS_POPN_SET, hs_pop); 590 twl4030_write(codec, TWL4030_REG_HS_POPN_SET, hs_pop);
586 591 } else {
587 /* Restore the original volume */ 592 /* Headset ramp-down _not_ according to
588 twl4030_write(w->codec, TWL4030_REG_HS_GAIN_SET, hs_gain); 593 * the TRM, but in a way that it is working */
589 break;
590 case SND_SOC_DAPM_POST_PMD:
591 /* Do the anti-pop/bias ramp disable according to the TRM */
592 hs_pop &= ~TWL4030_RAMP_EN; 594 hs_pop &= ~TWL4030_RAMP_EN;
593 twl4030_write(w->codec, TWL4030_REG_HS_POPN_SET, hs_pop); 595 twl4030_write(codec, TWL4030_REG_HS_POPN_SET, hs_pop);
596 /* Wait ramp delay time + 1, so the VMID can settle */
597 mdelay((ramp_base[(hs_pop & TWL4030_RAMP_DELAY) >> 2] /
598 twl4030->sysclk) + 1);
594 /* Bypass the reg_cache to mute the headset */ 599 /* Bypass the reg_cache to mute the headset */
595 twl4030_i2c_write_u8(TWL4030_MODULE_AUDIO_VOICE, 600 twl4030_i2c_write_u8(TWL4030_MODULE_AUDIO_VOICE,
596 hs_gain & (~0x0f), 601 hs_gain & (~0x0f),
597 TWL4030_REG_HS_GAIN_SET); 602 TWL4030_REG_HS_GAIN_SET);
603
598 hs_pop &= ~TWL4030_VMID_EN; 604 hs_pop &= ~TWL4030_VMID_EN;
599 twl4030_write(w->codec, TWL4030_REG_HS_POPN_SET, hs_pop); 605 twl4030_write(codec, TWL4030_REG_HS_POPN_SET, hs_pop);
606 }
607}
608
609static int headsetlpga_event(struct snd_soc_dapm_widget *w,
610 struct snd_kcontrol *kcontrol, int event)
611{
612 struct twl4030_priv *twl4030 = w->codec->private_data;
613
614 switch (event) {
615 case SND_SOC_DAPM_POST_PMU:
616 /* Do the ramp-up only once */
617 if (!twl4030->hsr_enabled)
618 headset_ramp(w->codec, 1);
619
620 twl4030->hsl_enabled = 1;
621 break;
622 case SND_SOC_DAPM_POST_PMD:
623 /* Do the ramp-down only if both headsetL/R is disabled */
624 if (!twl4030->hsr_enabled)
625 headset_ramp(w->codec, 0);
626
627 twl4030->hsl_enabled = 0;
628 break;
629 }
630 return 0;
631}
632
633static int headsetrpga_event(struct snd_soc_dapm_widget *w,
634 struct snd_kcontrol *kcontrol, int event)
635{
636 struct twl4030_priv *twl4030 = w->codec->private_data;
637
638 switch (event) {
639 case SND_SOC_DAPM_POST_PMU:
640 /* Do the ramp-up only once */
641 if (!twl4030->hsl_enabled)
642 headset_ramp(w->codec, 1);
643
644 twl4030->hsr_enabled = 1;
645 break;
646 case SND_SOC_DAPM_POST_PMD:
647 /* Do the ramp-down only if both headsetL/R is disabled */
648 if (!twl4030->hsl_enabled)
649 headset_ramp(w->codec, 0);
650
651 twl4030->hsr_enabled = 0;
600 break; 652 break;
601 } 653 }
602 return 0; 654 return 0;
@@ -1116,13 +1168,18 @@ static const struct snd_soc_dapm_widget twl4030_dapm_widgets[] = {
1116 &twl4030_dapm_predriver_controls[0], 1168 &twl4030_dapm_predriver_controls[0],
1117 ARRAY_SIZE(twl4030_dapm_predriver_controls)), 1169 ARRAY_SIZE(twl4030_dapm_predriver_controls)),
1118 /* HeadsetL/R */ 1170 /* HeadsetL/R */
1119 SND_SOC_DAPM_MIXER_E("HeadsetL Mixer", SND_SOC_NOPM, 0, 0, 1171 SND_SOC_DAPM_MIXER("HeadsetL Mixer", SND_SOC_NOPM, 0, 0,
1120 &twl4030_dapm_hsol_controls[0], 1172 &twl4030_dapm_hsol_controls[0],
1121 ARRAY_SIZE(twl4030_dapm_hsol_controls), headsetl_event, 1173 ARRAY_SIZE(twl4030_dapm_hsol_controls)),
1174 SND_SOC_DAPM_PGA_E("HeadsetL PGA", SND_SOC_NOPM,
1175 0, 0, NULL, 0, headsetlpga_event,
1122 SND_SOC_DAPM_POST_PMU|SND_SOC_DAPM_POST_PMD), 1176 SND_SOC_DAPM_POST_PMU|SND_SOC_DAPM_POST_PMD),
1123 SND_SOC_DAPM_MIXER("HeadsetR Mixer", SND_SOC_NOPM, 0, 0, 1177 SND_SOC_DAPM_MIXER("HeadsetR Mixer", SND_SOC_NOPM, 0, 0,
1124 &twl4030_dapm_hsor_controls[0], 1178 &twl4030_dapm_hsor_controls[0],
1125 ARRAY_SIZE(twl4030_dapm_hsor_controls)), 1179 ARRAY_SIZE(twl4030_dapm_hsor_controls)),
1180 SND_SOC_DAPM_PGA_E("HeadsetR PGA", SND_SOC_NOPM,
1181 0, 0, NULL, 0, headsetrpga_event,
1182 SND_SOC_DAPM_POST_PMU|SND_SOC_DAPM_POST_PMD),
1126 /* CarkitL/R */ 1183 /* CarkitL/R */
1127 SND_SOC_DAPM_MIXER("CarkitL Mixer", SND_SOC_NOPM, 0, 0, 1184 SND_SOC_DAPM_MIXER("CarkitL Mixer", SND_SOC_NOPM, 0, 0,
1128 &twl4030_dapm_carkitl_controls[0], 1185 &twl4030_dapm_carkitl_controls[0],
@@ -1227,10 +1284,12 @@ static const struct snd_soc_dapm_route intercon[] = {
1227 {"HeadsetL Mixer", "Voice", "Analog Voice Playback Mixer"}, 1284 {"HeadsetL Mixer", "Voice", "Analog Voice Playback Mixer"},
1228 {"HeadsetL Mixer", "AudioL1", "Analog L1 Playback Mixer"}, 1285 {"HeadsetL Mixer", "AudioL1", "Analog L1 Playback Mixer"},
1229 {"HeadsetL Mixer", "AudioL2", "Analog L2 Playback Mixer"}, 1286 {"HeadsetL Mixer", "AudioL2", "Analog L2 Playback Mixer"},
1287 {"HeadsetL PGA", NULL, "HeadsetL Mixer"},
1230 /* HeadsetR */ 1288 /* HeadsetR */
1231 {"HeadsetR Mixer", "Voice", "Analog Voice Playback Mixer"}, 1289 {"HeadsetR Mixer", "Voice", "Analog Voice Playback Mixer"},
1232 {"HeadsetR Mixer", "AudioR1", "Analog R1 Playback Mixer"}, 1290 {"HeadsetR Mixer", "AudioR1", "Analog R1 Playback Mixer"},
1233 {"HeadsetR Mixer", "AudioR2", "Analog R2 Playback Mixer"}, 1291 {"HeadsetR Mixer", "AudioR2", "Analog R2 Playback Mixer"},
1292 {"HeadsetR PGA", NULL, "HeadsetR Mixer"},
1234 /* CarkitL */ 1293 /* CarkitL */
1235 {"CarkitL Mixer", "Voice", "Analog Voice Playback Mixer"}, 1294 {"CarkitL Mixer", "Voice", "Analog Voice Playback Mixer"},
1236 {"CarkitL Mixer", "AudioL1", "Analog L1 Playback Mixer"}, 1295 {"CarkitL Mixer", "AudioL1", "Analog L1 Playback Mixer"},
@@ -1261,8 +1320,8 @@ static const struct snd_soc_dapm_route intercon[] = {
1261 {"EARPIECE", NULL, "Earpiece Mixer"}, 1320 {"EARPIECE", NULL, "Earpiece Mixer"},
1262 {"PREDRIVEL", NULL, "PredriveL Mixer"}, 1321 {"PREDRIVEL", NULL, "PredriveL Mixer"},
1263 {"PREDRIVER", NULL, "PredriveR Mixer"}, 1322 {"PREDRIVER", NULL, "PredriveR Mixer"},
1264 {"HSOL", NULL, "HeadsetL Mixer"}, 1323 {"HSOL", NULL, "HeadsetL PGA"},
1265 {"HSOR", NULL, "HeadsetR Mixer"}, 1324 {"HSOR", NULL, "HeadsetR PGA"},
1266 {"CARKITL", NULL, "CarkitL Mixer"}, 1325 {"CARKITL", NULL, "CarkitL Mixer"},
1267 {"CARKITR", NULL, "CarkitR Mixer"}, 1326 {"CARKITR", NULL, "CarkitR Mixer"},
1268 {"HFL", NULL, "HandsfreeL Mux"}, 1327 {"HFL", NULL, "HandsfreeL Mux"},
@@ -1601,17 +1660,21 @@ static int twl4030_set_dai_sysclk(struct snd_soc_dai *codec_dai,
1601 int clk_id, unsigned int freq, int dir) 1660 int clk_id, unsigned int freq, int dir)
1602{ 1661{
1603 struct snd_soc_codec *codec = codec_dai->codec; 1662 struct snd_soc_codec *codec = codec_dai->codec;
1663 struct twl4030_priv *twl4030 = codec->private_data;
1604 u8 infreq; 1664 u8 infreq;
1605 1665
1606 switch (freq) { 1666 switch (freq) {
1607 case 19200000: 1667 case 19200000:
1608 infreq = TWL4030_APLL_INFREQ_19200KHZ; 1668 infreq = TWL4030_APLL_INFREQ_19200KHZ;
1669 twl4030->sysclk = 19200;
1609 break; 1670 break;
1610 case 26000000: 1671 case 26000000:
1611 infreq = TWL4030_APLL_INFREQ_26000KHZ; 1672 infreq = TWL4030_APLL_INFREQ_26000KHZ;
1673 twl4030->sysclk = 26000;
1612 break; 1674 break;
1613 case 38400000: 1675 case 38400000:
1614 infreq = TWL4030_APLL_INFREQ_38400KHZ; 1676 infreq = TWL4030_APLL_INFREQ_38400KHZ;
1677 twl4030->sysclk = 38400;
1615 break; 1678 break;
1616 default: 1679 default:
1617 printk(KERN_ERR "TWL4030 set sysclk: unknown rate %d\n", 1680 printk(KERN_ERR "TWL4030 set sysclk: unknown rate %d\n",
@@ -2000,6 +2063,9 @@ static int twl4030_probe(struct platform_device *pdev)
2000 kfree(codec); 2063 kfree(codec);
2001 return -ENOMEM; 2064 return -ENOMEM;
2002 } 2065 }
2066 /* Set default sysclk (used by the headsetl/rpga_event callback for
2067 * pop-attenuation) */
2068 twl4030->sysclk = 26000;
2003 2069
2004 codec->private_data = twl4030; 2070 codec->private_data = twl4030;
2005 socdev->card->codec = codec; 2071 socdev->card->codec = codec;