aboutsummaryrefslogtreecommitdiffstats
path: root/sound/soc/codecs/twl4030.c
diff options
context:
space:
mode:
authorPeter Ujfalusi <peter.ujfalusi@nokia.com>2009-05-18 09:02:05 -0400
committerMark Brown <broonie@opensource.wolfsonmicro.com>2009-05-20 04:53:25 -0400
commit6943c92e87c4aa2a6d7a1f4dbd79cf4a0b5fd67b (patch)
tree1dabd06ad806d744665c427246541401a43ceebb /sound/soc/codecs/twl4030.c
parent4005d39a5f5549f1f6afe88abceed78b2ab225b6 (diff)
ASoC: TWL4030: Move the Headset pop-attenuation code to PGA event
This patch adds SND_SOC_DAPM_PGA_E to the headset path, which handles the headset ramp up and down sequences needed for the pop noise removal. With this patch the order of the internal components in the twl4030 codec is turned on and off in a correct order. Signed-off-by: Peter Ujfalusi <peter.ujfalusi@nokia.com> Tested-by: Anuj Aggarwal <anuj.aggarwal@ti.com> Tested-by: Jarkko Nikula <jhnikula@gmail.com> Tested-by: Misael Lopez Cruz <x0052729@ti.com> Signed-off-by: Mark Brown <broonie@opensource.wolfsonmicro.com>
Diffstat (limited to 'sound/soc/codecs/twl4030.c')
-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;