diff options
author | Peter Ujfalusi <peter.ujfalusi@nokia.com> | 2009-08-13 08:59:34 -0400 |
---|---|---|
committer | Mark Brown <broonie@opensource.wolfsonmicro.com> | 2009-08-13 09:56:13 -0400 |
commit | 9008adf9a9c3a43ef237f6cd416857569beb0029 (patch) | |
tree | ccc443a537a4b82a8dbebfb65e31d744a6b4c49b | |
parent | c4ff357ada4fc7a73d899a496b636c698519b958 (diff) |
ASoC: TWL4030: Introduce PGAs for outputs
Dynamically control and control only the needed output amplifier
muting/un-muting.
The original code was muting and un-muting the following output
amplifiers: Earpiece PreDrivL/R, CarkitL/R at the same time
regardless which pin is actually in use at the given moment.
Move these as separate PGA so only the needed amplifier will be touched.
Signed-off-by: Peter Ujfalusi <peter.ujfalusi@nokia.com>
Signed-off-by: Mark Brown <broonie@opensource.wolfsonmicro.com>
-rw-r--r-- | sound/soc/codecs/twl4030.c | 109 |
1 files changed, 60 insertions, 49 deletions
diff --git a/sound/soc/codecs/twl4030.c b/sound/soc/codecs/twl4030.c index 818fb37bd7f7..1a65004fa376 100644 --- a/sound/soc/codecs/twl4030.c +++ b/sound/soc/codecs/twl4030.c | |||
@@ -225,55 +225,11 @@ static void twl4030_codec_mute(struct snd_soc_codec *codec, int mute) | |||
225 | return; | 225 | return; |
226 | 226 | ||
227 | if (mute) { | 227 | if (mute) { |
228 | /* Bypass the reg_cache and mute the volumes | ||
229 | * Headset mute is done in it's own event handler | ||
230 | * Things to mute: Earpiece, PreDrivL/R, CarkitL/R | ||
231 | */ | ||
232 | reg_val = twl4030_read_reg_cache(codec, TWL4030_REG_EAR_CTL); | ||
233 | twl4030_i2c_write_u8(TWL4030_MODULE_AUDIO_VOICE, | ||
234 | reg_val & (~TWL4030_EAR_GAIN), | ||
235 | TWL4030_REG_EAR_CTL); | ||
236 | |||
237 | reg_val = twl4030_read_reg_cache(codec, TWL4030_REG_PREDL_CTL); | ||
238 | twl4030_i2c_write_u8(TWL4030_MODULE_AUDIO_VOICE, | ||
239 | reg_val & (~TWL4030_PREDL_GAIN), | ||
240 | TWL4030_REG_PREDL_CTL); | ||
241 | reg_val = twl4030_read_reg_cache(codec, TWL4030_REG_PREDR_CTL); | ||
242 | twl4030_i2c_write_u8(TWL4030_MODULE_AUDIO_VOICE, | ||
243 | reg_val & (~TWL4030_PREDR_GAIN), | ||
244 | TWL4030_REG_PREDL_CTL); | ||
245 | |||
246 | reg_val = twl4030_read_reg_cache(codec, TWL4030_REG_PRECKL_CTL); | ||
247 | twl4030_i2c_write_u8(TWL4030_MODULE_AUDIO_VOICE, | ||
248 | reg_val & (~TWL4030_PRECKL_GAIN), | ||
249 | TWL4030_REG_PRECKL_CTL); | ||
250 | reg_val = twl4030_read_reg_cache(codec, TWL4030_REG_PRECKR_CTL); | ||
251 | twl4030_i2c_write_u8(TWL4030_MODULE_AUDIO_VOICE, | ||
252 | reg_val & (~TWL4030_PRECKR_GAIN), | ||
253 | TWL4030_REG_PRECKR_CTL); | ||
254 | |||
255 | /* Disable PLL */ | 228 | /* Disable PLL */ |
256 | reg_val = twl4030_read_reg_cache(codec, TWL4030_REG_APLL_CTL); | 229 | reg_val = twl4030_read_reg_cache(codec, TWL4030_REG_APLL_CTL); |
257 | reg_val &= ~TWL4030_APLL_EN; | 230 | reg_val &= ~TWL4030_APLL_EN; |
258 | twl4030_write(codec, TWL4030_REG_APLL_CTL, reg_val); | 231 | twl4030_write(codec, TWL4030_REG_APLL_CTL, reg_val); |
259 | } else { | 232 | } else { |
260 | /* Restore the volumes | ||
261 | * Headset mute is done in it's own event handler | ||
262 | * Things to restore: Earpiece, PreDrivL/R, CarkitL/R | ||
263 | */ | ||
264 | twl4030_write(codec, TWL4030_REG_EAR_CTL, | ||
265 | twl4030_read_reg_cache(codec, TWL4030_REG_EAR_CTL)); | ||
266 | |||
267 | twl4030_write(codec, TWL4030_REG_PREDL_CTL, | ||
268 | twl4030_read_reg_cache(codec, TWL4030_REG_PREDL_CTL)); | ||
269 | twl4030_write(codec, TWL4030_REG_PREDR_CTL, | ||
270 | twl4030_read_reg_cache(codec, TWL4030_REG_PREDR_CTL)); | ||
271 | |||
272 | twl4030_write(codec, TWL4030_REG_PRECKL_CTL, | ||
273 | twl4030_read_reg_cache(codec, TWL4030_REG_PRECKL_CTL)); | ||
274 | twl4030_write(codec, TWL4030_REG_PRECKR_CTL, | ||
275 | twl4030_read_reg_cache(codec, TWL4030_REG_PRECKR_CTL)); | ||
276 | |||
277 | /* Enable PLL */ | 233 | /* Enable PLL */ |
278 | reg_val = twl4030_read_reg_cache(codec, TWL4030_REG_APLL_CTL); | 234 | reg_val = twl4030_read_reg_cache(codec, TWL4030_REG_APLL_CTL); |
279 | reg_val |= TWL4030_APLL_EN; | 235 | reg_val |= TWL4030_APLL_EN; |
@@ -560,6 +516,41 @@ static int micpath_event(struct snd_soc_dapm_widget *w, | |||
560 | return 0; | 516 | return 0; |
561 | } | 517 | } |
562 | 518 | ||
519 | /* | ||
520 | * Output PGA builder: | ||
521 | * Handle the muting and unmuting of the given output (turning off the | ||
522 | * amplifier associated with the output pin) | ||
523 | * On mute bypass the reg_cache and mute the volume | ||
524 | * On unmute: restore the register content | ||
525 | * Outputs handled in this way: Earpiece, PreDrivL/R, CarkitL/R | ||
526 | */ | ||
527 | #define TWL4030_OUTPUT_PGA(pin_name, reg, mask) \ | ||
528 | static int pin_name##pga_event(struct snd_soc_dapm_widget *w, \ | ||
529 | struct snd_kcontrol *kcontrol, int event) \ | ||
530 | { \ | ||
531 | u8 reg_val; \ | ||
532 | \ | ||
533 | switch (event) { \ | ||
534 | case SND_SOC_DAPM_POST_PMU: \ | ||
535 | twl4030_write(w->codec, reg, \ | ||
536 | twl4030_read_reg_cache(w->codec, reg)); \ | ||
537 | break; \ | ||
538 | case SND_SOC_DAPM_POST_PMD: \ | ||
539 | reg_val = twl4030_read_reg_cache(w->codec, reg); \ | ||
540 | twl4030_i2c_write_u8(TWL4030_MODULE_AUDIO_VOICE, \ | ||
541 | reg_val & (~mask), \ | ||
542 | reg); \ | ||
543 | break; \ | ||
544 | } \ | ||
545 | return 0; \ | ||
546 | } | ||
547 | |||
548 | TWL4030_OUTPUT_PGA(earpiece, TWL4030_REG_EAR_CTL, TWL4030_EAR_GAIN); | ||
549 | TWL4030_OUTPUT_PGA(predrivel, TWL4030_REG_PREDL_CTL, TWL4030_PREDL_GAIN); | ||
550 | TWL4030_OUTPUT_PGA(predriver, TWL4030_REG_PREDR_CTL, TWL4030_PREDR_GAIN); | ||
551 | TWL4030_OUTPUT_PGA(carkitl, TWL4030_REG_PRECKL_CTL, TWL4030_PRECKL_GAIN); | ||
552 | TWL4030_OUTPUT_PGA(carkitr, TWL4030_REG_PRECKR_CTL, TWL4030_PRECKR_GAIN); | ||
553 | |||
563 | static void handsfree_ramp(struct snd_soc_codec *codec, int reg, int ramp) | 554 | static void handsfree_ramp(struct snd_soc_codec *codec, int reg, int ramp) |
564 | { | 555 | { |
565 | unsigned char hs_ctl; | 556 | unsigned char hs_ctl; |
@@ -1253,13 +1244,22 @@ static const struct snd_soc_dapm_widget twl4030_dapm_widgets[] = { | |||
1253 | SND_SOC_DAPM_MIXER("Earpiece Mixer", SND_SOC_NOPM, 0, 0, | 1244 | SND_SOC_DAPM_MIXER("Earpiece Mixer", SND_SOC_NOPM, 0, 0, |
1254 | &twl4030_dapm_earpiece_controls[0], | 1245 | &twl4030_dapm_earpiece_controls[0], |
1255 | ARRAY_SIZE(twl4030_dapm_earpiece_controls)), | 1246 | ARRAY_SIZE(twl4030_dapm_earpiece_controls)), |
1247 | SND_SOC_DAPM_PGA_E("Earpiece PGA", SND_SOC_NOPM, | ||
1248 | 0, 0, NULL, 0, earpiecepga_event, | ||
1249 | SND_SOC_DAPM_POST_PMU|SND_SOC_DAPM_POST_PMD), | ||
1256 | /* PreDrivL/R */ | 1250 | /* PreDrivL/R */ |
1257 | SND_SOC_DAPM_MIXER("PredriveL Mixer", SND_SOC_NOPM, 0, 0, | 1251 | SND_SOC_DAPM_MIXER("PredriveL Mixer", SND_SOC_NOPM, 0, 0, |
1258 | &twl4030_dapm_predrivel_controls[0], | 1252 | &twl4030_dapm_predrivel_controls[0], |
1259 | ARRAY_SIZE(twl4030_dapm_predrivel_controls)), | 1253 | ARRAY_SIZE(twl4030_dapm_predrivel_controls)), |
1254 | SND_SOC_DAPM_PGA_E("PredriveL PGA", SND_SOC_NOPM, | ||
1255 | 0, 0, NULL, 0, predrivelpga_event, | ||
1256 | SND_SOC_DAPM_POST_PMU|SND_SOC_DAPM_POST_PMD), | ||
1260 | SND_SOC_DAPM_MIXER("PredriveR Mixer", SND_SOC_NOPM, 0, 0, | 1257 | SND_SOC_DAPM_MIXER("PredriveR Mixer", SND_SOC_NOPM, 0, 0, |
1261 | &twl4030_dapm_predriver_controls[0], | 1258 | &twl4030_dapm_predriver_controls[0], |
1262 | ARRAY_SIZE(twl4030_dapm_predriver_controls)), | 1259 | ARRAY_SIZE(twl4030_dapm_predriver_controls)), |
1260 | SND_SOC_DAPM_PGA_E("PredriveR PGA", SND_SOC_NOPM, | ||
1261 | 0, 0, NULL, 0, predriverpga_event, | ||
1262 | SND_SOC_DAPM_POST_PMU|SND_SOC_DAPM_POST_PMD), | ||
1263 | /* HeadsetL/R */ | 1263 | /* HeadsetL/R */ |
1264 | SND_SOC_DAPM_MIXER("HeadsetL Mixer", SND_SOC_NOPM, 0, 0, | 1264 | SND_SOC_DAPM_MIXER("HeadsetL Mixer", SND_SOC_NOPM, 0, 0, |
1265 | &twl4030_dapm_hsol_controls[0], | 1265 | &twl4030_dapm_hsol_controls[0], |
@@ -1277,9 +1277,15 @@ static const struct snd_soc_dapm_widget twl4030_dapm_widgets[] = { | |||
1277 | SND_SOC_DAPM_MIXER("CarkitL Mixer", SND_SOC_NOPM, 0, 0, | 1277 | SND_SOC_DAPM_MIXER("CarkitL Mixer", SND_SOC_NOPM, 0, 0, |
1278 | &twl4030_dapm_carkitl_controls[0], | 1278 | &twl4030_dapm_carkitl_controls[0], |
1279 | ARRAY_SIZE(twl4030_dapm_carkitl_controls)), | 1279 | ARRAY_SIZE(twl4030_dapm_carkitl_controls)), |
1280 | SND_SOC_DAPM_PGA_E("CarkitL PGA", SND_SOC_NOPM, | ||
1281 | 0, 0, NULL, 0, carkitlpga_event, | ||
1282 | SND_SOC_DAPM_POST_PMU|SND_SOC_DAPM_POST_PMD), | ||
1280 | SND_SOC_DAPM_MIXER("CarkitR Mixer", SND_SOC_NOPM, 0, 0, | 1283 | SND_SOC_DAPM_MIXER("CarkitR Mixer", SND_SOC_NOPM, 0, 0, |
1281 | &twl4030_dapm_carkitr_controls[0], | 1284 | &twl4030_dapm_carkitr_controls[0], |
1282 | ARRAY_SIZE(twl4030_dapm_carkitr_controls)), | 1285 | ARRAY_SIZE(twl4030_dapm_carkitr_controls)), |
1286 | SND_SOC_DAPM_PGA_E("CarkitR PGA", SND_SOC_NOPM, | ||
1287 | 0, 0, NULL, 0, carkitrpga_event, | ||
1288 | SND_SOC_DAPM_POST_PMU|SND_SOC_DAPM_POST_PMD), | ||
1283 | 1289 | ||
1284 | /* Output MUX controls */ | 1290 | /* Output MUX controls */ |
1285 | /* HandsfreeL/R */ | 1291 | /* HandsfreeL/R */ |
@@ -1371,16 +1377,19 @@ static const struct snd_soc_dapm_route intercon[] = { | |||
1371 | {"Earpiece Mixer", "AudioL1", "Analog L1 Playback Mixer"}, | 1377 | {"Earpiece Mixer", "AudioL1", "Analog L1 Playback Mixer"}, |
1372 | {"Earpiece Mixer", "AudioL2", "Analog L2 Playback Mixer"}, | 1378 | {"Earpiece Mixer", "AudioL2", "Analog L2 Playback Mixer"}, |
1373 | {"Earpiece Mixer", "AudioR1", "Analog R1 Playback Mixer"}, | 1379 | {"Earpiece Mixer", "AudioR1", "Analog R1 Playback Mixer"}, |
1380 | {"Earpiece PGA", NULL, "Earpiece Mixer"}, | ||
1374 | /* PreDrivL */ | 1381 | /* PreDrivL */ |
1375 | {"PredriveL Mixer", "Voice", "Analog Voice Playback Mixer"}, | 1382 | {"PredriveL Mixer", "Voice", "Analog Voice Playback Mixer"}, |
1376 | {"PredriveL Mixer", "AudioL1", "Analog L1 Playback Mixer"}, | 1383 | {"PredriveL Mixer", "AudioL1", "Analog L1 Playback Mixer"}, |
1377 | {"PredriveL Mixer", "AudioL2", "Analog L2 Playback Mixer"}, | 1384 | {"PredriveL Mixer", "AudioL2", "Analog L2 Playback Mixer"}, |
1378 | {"PredriveL Mixer", "AudioR2", "Analog R2 Playback Mixer"}, | 1385 | {"PredriveL Mixer", "AudioR2", "Analog R2 Playback Mixer"}, |
1386 | {"PredriveL PGA", NULL, "PredriveL Mixer"}, | ||
1379 | /* PreDrivR */ | 1387 | /* PreDrivR */ |
1380 | {"PredriveR Mixer", "Voice", "Analog Voice Playback Mixer"}, | 1388 | {"PredriveR Mixer", "Voice", "Analog Voice Playback Mixer"}, |
1381 | {"PredriveR Mixer", "AudioR1", "Analog R1 Playback Mixer"}, | 1389 | {"PredriveR Mixer", "AudioR1", "Analog R1 Playback Mixer"}, |
1382 | {"PredriveR Mixer", "AudioR2", "Analog R2 Playback Mixer"}, | 1390 | {"PredriveR Mixer", "AudioR2", "Analog R2 Playback Mixer"}, |
1383 | {"PredriveR Mixer", "AudioL2", "Analog L2 Playback Mixer"}, | 1391 | {"PredriveR Mixer", "AudioL2", "Analog L2 Playback Mixer"}, |
1392 | {"PredriveR PGA", NULL, "PredriveR Mixer"}, | ||
1384 | /* HeadsetL */ | 1393 | /* HeadsetL */ |
1385 | {"HeadsetL Mixer", "Voice", "Analog Voice Playback Mixer"}, | 1394 | {"HeadsetL Mixer", "Voice", "Analog Voice Playback Mixer"}, |
1386 | {"HeadsetL Mixer", "AudioL1", "Analog L1 Playback Mixer"}, | 1395 | {"HeadsetL Mixer", "AudioL1", "Analog L1 Playback Mixer"}, |
@@ -1395,10 +1404,12 @@ static const struct snd_soc_dapm_route intercon[] = { | |||
1395 | {"CarkitL Mixer", "Voice", "Analog Voice Playback Mixer"}, | 1404 | {"CarkitL Mixer", "Voice", "Analog Voice Playback Mixer"}, |
1396 | {"CarkitL Mixer", "AudioL1", "Analog L1 Playback Mixer"}, | 1405 | {"CarkitL Mixer", "AudioL1", "Analog L1 Playback Mixer"}, |
1397 | {"CarkitL Mixer", "AudioL2", "Analog L2 Playback Mixer"}, | 1406 | {"CarkitL Mixer", "AudioL2", "Analog L2 Playback Mixer"}, |
1407 | {"CarkitL PGA", NULL, "CarkitL Mixer"}, | ||
1398 | /* CarkitR */ | 1408 | /* CarkitR */ |
1399 | {"CarkitR Mixer", "Voice", "Analog Voice Playback Mixer"}, | 1409 | {"CarkitR Mixer", "Voice", "Analog Voice Playback Mixer"}, |
1400 | {"CarkitR Mixer", "AudioR1", "Analog R1 Playback Mixer"}, | 1410 | {"CarkitR Mixer", "AudioR1", "Analog R1 Playback Mixer"}, |
1401 | {"CarkitR Mixer", "AudioR2", "Analog R2 Playback Mixer"}, | 1411 | {"CarkitR Mixer", "AudioR2", "Analog R2 Playback Mixer"}, |
1412 | {"CarkitR PGA", NULL, "CarkitR Mixer"}, | ||
1402 | /* HandsfreeL */ | 1413 | /* HandsfreeL */ |
1403 | {"HandsfreeL Mux", "Voice", "Analog Voice Playback Mixer"}, | 1414 | {"HandsfreeL Mux", "Voice", "Analog Voice Playback Mixer"}, |
1404 | {"HandsfreeL Mux", "AudioL1", "Analog L1 Playback Mixer"}, | 1415 | {"HandsfreeL Mux", "AudioL1", "Analog L1 Playback Mixer"}, |
@@ -1422,13 +1433,13 @@ static const struct snd_soc_dapm_route intercon[] = { | |||
1422 | /* outputs */ | 1433 | /* outputs */ |
1423 | {"OUTL", NULL, "Analog L2 Playback Mixer"}, | 1434 | {"OUTL", NULL, "Analog L2 Playback Mixer"}, |
1424 | {"OUTR", NULL, "Analog R2 Playback Mixer"}, | 1435 | {"OUTR", NULL, "Analog R2 Playback Mixer"}, |
1425 | {"EARPIECE", NULL, "Earpiece Mixer"}, | 1436 | {"EARPIECE", NULL, "Earpiece PGA"}, |
1426 | {"PREDRIVEL", NULL, "PredriveL Mixer"}, | 1437 | {"PREDRIVEL", NULL, "PredriveL PGA"}, |
1427 | {"PREDRIVER", NULL, "PredriveR Mixer"}, | 1438 | {"PREDRIVER", NULL, "PredriveR PGA"}, |
1428 | {"HSOL", NULL, "HeadsetL PGA"}, | 1439 | {"HSOL", NULL, "HeadsetL PGA"}, |
1429 | {"HSOR", NULL, "HeadsetR PGA"}, | 1440 | {"HSOR", NULL, "HeadsetR PGA"}, |
1430 | {"CARKITL", NULL, "CarkitL Mixer"}, | 1441 | {"CARKITL", NULL, "CarkitL PGA"}, |
1431 | {"CARKITR", NULL, "CarkitR Mixer"}, | 1442 | {"CARKITR", NULL, "CarkitR PGA"}, |
1432 | {"HFL", NULL, "HandsfreeL PGA"}, | 1443 | {"HFL", NULL, "HandsfreeL PGA"}, |
1433 | {"HFR", NULL, "HandsfreeR PGA"}, | 1444 | {"HFR", NULL, "HandsfreeR PGA"}, |
1434 | {"Vibra Route", "Audio", "Vibra Mux"}, | 1445 | {"Vibra Route", "Audio", "Vibra Mux"}, |