Commit 9008adf9 authored by Peter Ujfalusi's avatar Peter Ujfalusi Committed by Mark Brown

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: default avatarPeter Ujfalusi <peter.ujfalusi@nokia.com>
Signed-off-by: default avatarMark Brown <broonie@opensource.wolfsonmicro.com>
parent c4ff357a
...@@ -225,55 +225,11 @@ static void twl4030_codec_mute(struct snd_soc_codec *codec, int mute) ...@@ -225,55 +225,11 @@ static void twl4030_codec_mute(struct snd_soc_codec *codec, int mute)
return; return;
if (mute) { if (mute) {
/* Bypass the reg_cache and mute the volumes
* Headset mute is done in it's own event handler
* Things to mute: Earpiece, PreDrivL/R, CarkitL/R
*/
reg_val = twl4030_read_reg_cache(codec, TWL4030_REG_EAR_CTL);
twl4030_i2c_write_u8(TWL4030_MODULE_AUDIO_VOICE,
reg_val & (~TWL4030_EAR_GAIN),
TWL4030_REG_EAR_CTL);
reg_val = twl4030_read_reg_cache(codec, TWL4030_REG_PREDL_CTL);
twl4030_i2c_write_u8(TWL4030_MODULE_AUDIO_VOICE,
reg_val & (~TWL4030_PREDL_GAIN),
TWL4030_REG_PREDL_CTL);
reg_val = twl4030_read_reg_cache(codec, TWL4030_REG_PREDR_CTL);
twl4030_i2c_write_u8(TWL4030_MODULE_AUDIO_VOICE,
reg_val & (~TWL4030_PREDR_GAIN),
TWL4030_REG_PREDL_CTL);
reg_val = twl4030_read_reg_cache(codec, TWL4030_REG_PRECKL_CTL);
twl4030_i2c_write_u8(TWL4030_MODULE_AUDIO_VOICE,
reg_val & (~TWL4030_PRECKL_GAIN),
TWL4030_REG_PRECKL_CTL);
reg_val = twl4030_read_reg_cache(codec, TWL4030_REG_PRECKR_CTL);
twl4030_i2c_write_u8(TWL4030_MODULE_AUDIO_VOICE,
reg_val & (~TWL4030_PRECKR_GAIN),
TWL4030_REG_PRECKR_CTL);
/* Disable PLL */ /* Disable PLL */
reg_val = twl4030_read_reg_cache(codec, TWL4030_REG_APLL_CTL); reg_val = twl4030_read_reg_cache(codec, TWL4030_REG_APLL_CTL);
reg_val &= ~TWL4030_APLL_EN; reg_val &= ~TWL4030_APLL_EN;
twl4030_write(codec, TWL4030_REG_APLL_CTL, reg_val); twl4030_write(codec, TWL4030_REG_APLL_CTL, reg_val);
} else { } else {
/* Restore the volumes
* Headset mute is done in it's own event handler
* Things to restore: Earpiece, PreDrivL/R, CarkitL/R
*/
twl4030_write(codec, TWL4030_REG_EAR_CTL,
twl4030_read_reg_cache(codec, TWL4030_REG_EAR_CTL));
twl4030_write(codec, TWL4030_REG_PREDL_CTL,
twl4030_read_reg_cache(codec, TWL4030_REG_PREDL_CTL));
twl4030_write(codec, TWL4030_REG_PREDR_CTL,
twl4030_read_reg_cache(codec, TWL4030_REG_PREDR_CTL));
twl4030_write(codec, TWL4030_REG_PRECKL_CTL,
twl4030_read_reg_cache(codec, TWL4030_REG_PRECKL_CTL));
twl4030_write(codec, TWL4030_REG_PRECKR_CTL,
twl4030_read_reg_cache(codec, TWL4030_REG_PRECKR_CTL));
/* Enable PLL */ /* Enable PLL */
reg_val = twl4030_read_reg_cache(codec, TWL4030_REG_APLL_CTL); reg_val = twl4030_read_reg_cache(codec, TWL4030_REG_APLL_CTL);
reg_val |= TWL4030_APLL_EN; reg_val |= TWL4030_APLL_EN;
...@@ -560,6 +516,41 @@ static int micpath_event(struct snd_soc_dapm_widget *w, ...@@ -560,6 +516,41 @@ static int micpath_event(struct snd_soc_dapm_widget *w,
return 0; return 0;
} }
/*
* Output PGA builder:
* Handle the muting and unmuting of the given output (turning off the
* amplifier associated with the output pin)
* On mute bypass the reg_cache and mute the volume
* On unmute: restore the register content
* Outputs handled in this way: Earpiece, PreDrivL/R, CarkitL/R
*/
#define TWL4030_OUTPUT_PGA(pin_name, reg, mask) \
static int pin_name##pga_event(struct snd_soc_dapm_widget *w, \
struct snd_kcontrol *kcontrol, int event) \
{ \
u8 reg_val; \
\
switch (event) { \
case SND_SOC_DAPM_POST_PMU: \
twl4030_write(w->codec, reg, \
twl4030_read_reg_cache(w->codec, reg)); \
break; \
case SND_SOC_DAPM_POST_PMD: \
reg_val = twl4030_read_reg_cache(w->codec, reg); \
twl4030_i2c_write_u8(TWL4030_MODULE_AUDIO_VOICE, \
reg_val & (~mask), \
reg); \
break; \
} \
return 0; \
}
TWL4030_OUTPUT_PGA(earpiece, TWL4030_REG_EAR_CTL, TWL4030_EAR_GAIN);
TWL4030_OUTPUT_PGA(predrivel, TWL4030_REG_PREDL_CTL, TWL4030_PREDL_GAIN);
TWL4030_OUTPUT_PGA(predriver, TWL4030_REG_PREDR_CTL, TWL4030_PREDR_GAIN);
TWL4030_OUTPUT_PGA(carkitl, TWL4030_REG_PRECKL_CTL, TWL4030_PRECKL_GAIN);
TWL4030_OUTPUT_PGA(carkitr, TWL4030_REG_PRECKR_CTL, TWL4030_PRECKR_GAIN);
static void handsfree_ramp(struct snd_soc_codec *codec, int reg, int ramp) static void handsfree_ramp(struct snd_soc_codec *codec, int reg, int ramp)
{ {
unsigned char hs_ctl; unsigned char hs_ctl;
...@@ -1253,13 +1244,22 @@ static const struct snd_soc_dapm_widget twl4030_dapm_widgets[] = { ...@@ -1253,13 +1244,22 @@ static const struct snd_soc_dapm_widget twl4030_dapm_widgets[] = {
SND_SOC_DAPM_MIXER("Earpiece Mixer", SND_SOC_NOPM, 0, 0, SND_SOC_DAPM_MIXER("Earpiece Mixer", SND_SOC_NOPM, 0, 0,
&twl4030_dapm_earpiece_controls[0], &twl4030_dapm_earpiece_controls[0],
ARRAY_SIZE(twl4030_dapm_earpiece_controls)), ARRAY_SIZE(twl4030_dapm_earpiece_controls)),
SND_SOC_DAPM_PGA_E("Earpiece PGA", SND_SOC_NOPM,
0, 0, NULL, 0, earpiecepga_event,
SND_SOC_DAPM_POST_PMU|SND_SOC_DAPM_POST_PMD),
/* PreDrivL/R */ /* PreDrivL/R */
SND_SOC_DAPM_MIXER("PredriveL Mixer", SND_SOC_NOPM, 0, 0, SND_SOC_DAPM_MIXER("PredriveL Mixer", SND_SOC_NOPM, 0, 0,
&twl4030_dapm_predrivel_controls[0], &twl4030_dapm_predrivel_controls[0],
ARRAY_SIZE(twl4030_dapm_predrivel_controls)), ARRAY_SIZE(twl4030_dapm_predrivel_controls)),
SND_SOC_DAPM_PGA_E("PredriveL PGA", SND_SOC_NOPM,
0, 0, NULL, 0, predrivelpga_event,
SND_SOC_DAPM_POST_PMU|SND_SOC_DAPM_POST_PMD),
SND_SOC_DAPM_MIXER("PredriveR Mixer", SND_SOC_NOPM, 0, 0, SND_SOC_DAPM_MIXER("PredriveR Mixer", SND_SOC_NOPM, 0, 0,
&twl4030_dapm_predriver_controls[0], &twl4030_dapm_predriver_controls[0],
ARRAY_SIZE(twl4030_dapm_predriver_controls)), ARRAY_SIZE(twl4030_dapm_predriver_controls)),
SND_SOC_DAPM_PGA_E("PredriveR PGA", SND_SOC_NOPM,
0, 0, NULL, 0, predriverpga_event,
SND_SOC_DAPM_POST_PMU|SND_SOC_DAPM_POST_PMD),
/* HeadsetL/R */ /* HeadsetL/R */
SND_SOC_DAPM_MIXER("HeadsetL Mixer", SND_SOC_NOPM, 0, 0, SND_SOC_DAPM_MIXER("HeadsetL Mixer", SND_SOC_NOPM, 0, 0,
&twl4030_dapm_hsol_controls[0], &twl4030_dapm_hsol_controls[0],
...@@ -1277,9 +1277,15 @@ static const struct snd_soc_dapm_widget twl4030_dapm_widgets[] = { ...@@ -1277,9 +1277,15 @@ static const struct snd_soc_dapm_widget twl4030_dapm_widgets[] = {
SND_SOC_DAPM_MIXER("CarkitL Mixer", SND_SOC_NOPM, 0, 0, SND_SOC_DAPM_MIXER("CarkitL Mixer", SND_SOC_NOPM, 0, 0,
&twl4030_dapm_carkitl_controls[0], &twl4030_dapm_carkitl_controls[0],
ARRAY_SIZE(twl4030_dapm_carkitl_controls)), ARRAY_SIZE(twl4030_dapm_carkitl_controls)),
SND_SOC_DAPM_PGA_E("CarkitL PGA", SND_SOC_NOPM,
0, 0, NULL, 0, carkitlpga_event,
SND_SOC_DAPM_POST_PMU|SND_SOC_DAPM_POST_PMD),
SND_SOC_DAPM_MIXER("CarkitR Mixer", SND_SOC_NOPM, 0, 0, SND_SOC_DAPM_MIXER("CarkitR Mixer", SND_SOC_NOPM, 0, 0,
&twl4030_dapm_carkitr_controls[0], &twl4030_dapm_carkitr_controls[0],
ARRAY_SIZE(twl4030_dapm_carkitr_controls)), ARRAY_SIZE(twl4030_dapm_carkitr_controls)),
SND_SOC_DAPM_PGA_E("CarkitR PGA", SND_SOC_NOPM,
0, 0, NULL, 0, carkitrpga_event,
SND_SOC_DAPM_POST_PMU|SND_SOC_DAPM_POST_PMD),
/* Output MUX controls */ /* Output MUX controls */
/* HandsfreeL/R */ /* HandsfreeL/R */
...@@ -1371,16 +1377,19 @@ static const struct snd_soc_dapm_route intercon[] = { ...@@ -1371,16 +1377,19 @@ static const struct snd_soc_dapm_route intercon[] = {
{"Earpiece Mixer", "AudioL1", "Analog L1 Playback Mixer"}, {"Earpiece Mixer", "AudioL1", "Analog L1 Playback Mixer"},
{"Earpiece Mixer", "AudioL2", "Analog L2 Playback Mixer"}, {"Earpiece Mixer", "AudioL2", "Analog L2 Playback Mixer"},
{"Earpiece Mixer", "AudioR1", "Analog R1 Playback Mixer"}, {"Earpiece Mixer", "AudioR1", "Analog R1 Playback Mixer"},
{"Earpiece PGA", NULL, "Earpiece Mixer"},
/* PreDrivL */ /* PreDrivL */
{"PredriveL Mixer", "Voice", "Analog Voice Playback Mixer"}, {"PredriveL Mixer", "Voice", "Analog Voice Playback Mixer"},
{"PredriveL Mixer", "AudioL1", "Analog L1 Playback Mixer"}, {"PredriveL Mixer", "AudioL1", "Analog L1 Playback Mixer"},
{"PredriveL Mixer", "AudioL2", "Analog L2 Playback Mixer"}, {"PredriveL Mixer", "AudioL2", "Analog L2 Playback Mixer"},
{"PredriveL Mixer", "AudioR2", "Analog R2 Playback Mixer"}, {"PredriveL Mixer", "AudioR2", "Analog R2 Playback Mixer"},
{"PredriveL PGA", NULL, "PredriveL Mixer"},
/* PreDrivR */ /* PreDrivR */
{"PredriveR Mixer", "Voice", "Analog Voice Playback Mixer"}, {"PredriveR Mixer", "Voice", "Analog Voice Playback Mixer"},
{"PredriveR Mixer", "AudioR1", "Analog R1 Playback Mixer"}, {"PredriveR Mixer", "AudioR1", "Analog R1 Playback Mixer"},
{"PredriveR Mixer", "AudioR2", "Analog R2 Playback Mixer"}, {"PredriveR Mixer", "AudioR2", "Analog R2 Playback Mixer"},
{"PredriveR Mixer", "AudioL2", "Analog L2 Playback Mixer"}, {"PredriveR Mixer", "AudioL2", "Analog L2 Playback Mixer"},
{"PredriveR PGA", NULL, "PredriveR Mixer"},
/* HeadsetL */ /* HeadsetL */
{"HeadsetL Mixer", "Voice", "Analog Voice Playback Mixer"}, {"HeadsetL Mixer", "Voice", "Analog Voice Playback Mixer"},
{"HeadsetL Mixer", "AudioL1", "Analog L1 Playback Mixer"}, {"HeadsetL Mixer", "AudioL1", "Analog L1 Playback Mixer"},
...@@ -1395,10 +1404,12 @@ static const struct snd_soc_dapm_route intercon[] = { ...@@ -1395,10 +1404,12 @@ static const struct snd_soc_dapm_route intercon[] = {
{"CarkitL Mixer", "Voice", "Analog Voice Playback Mixer"}, {"CarkitL Mixer", "Voice", "Analog Voice Playback Mixer"},
{"CarkitL Mixer", "AudioL1", "Analog L1 Playback Mixer"}, {"CarkitL Mixer", "AudioL1", "Analog L1 Playback Mixer"},
{"CarkitL Mixer", "AudioL2", "Analog L2 Playback Mixer"}, {"CarkitL Mixer", "AudioL2", "Analog L2 Playback Mixer"},
{"CarkitL PGA", NULL, "CarkitL Mixer"},
/* CarkitR */ /* CarkitR */
{"CarkitR Mixer", "Voice", "Analog Voice Playback Mixer"}, {"CarkitR Mixer", "Voice", "Analog Voice Playback Mixer"},
{"CarkitR Mixer", "AudioR1", "Analog R1 Playback Mixer"}, {"CarkitR Mixer", "AudioR1", "Analog R1 Playback Mixer"},
{"CarkitR Mixer", "AudioR2", "Analog R2 Playback Mixer"}, {"CarkitR Mixer", "AudioR2", "Analog R2 Playback Mixer"},
{"CarkitR PGA", NULL, "CarkitR Mixer"},
/* HandsfreeL */ /* HandsfreeL */
{"HandsfreeL Mux", "Voice", "Analog Voice Playback Mixer"}, {"HandsfreeL Mux", "Voice", "Analog Voice Playback Mixer"},
{"HandsfreeL Mux", "AudioL1", "Analog L1 Playback Mixer"}, {"HandsfreeL Mux", "AudioL1", "Analog L1 Playback Mixer"},
...@@ -1422,13 +1433,13 @@ static const struct snd_soc_dapm_route intercon[] = { ...@@ -1422,13 +1433,13 @@ static const struct snd_soc_dapm_route intercon[] = {
/* outputs */ /* outputs */
{"OUTL", NULL, "Analog L2 Playback Mixer"}, {"OUTL", NULL, "Analog L2 Playback Mixer"},
{"OUTR", NULL, "Analog R2 Playback Mixer"}, {"OUTR", NULL, "Analog R2 Playback Mixer"},
{"EARPIECE", NULL, "Earpiece Mixer"}, {"EARPIECE", NULL, "Earpiece PGA"},
{"PREDRIVEL", NULL, "PredriveL Mixer"}, {"PREDRIVEL", NULL, "PredriveL PGA"},
{"PREDRIVER", NULL, "PredriveR Mixer"}, {"PREDRIVER", NULL, "PredriveR PGA"},
{"HSOL", NULL, "HeadsetL PGA"}, {"HSOL", NULL, "HeadsetL PGA"},
{"HSOR", NULL, "HeadsetR PGA"}, {"HSOR", NULL, "HeadsetR PGA"},
{"CARKITL", NULL, "CarkitL Mixer"}, {"CARKITL", NULL, "CarkitL PGA"},
{"CARKITR", NULL, "CarkitR Mixer"}, {"CARKITR", NULL, "CarkitR PGA"},
{"HFL", NULL, "HandsfreeL PGA"}, {"HFL", NULL, "HandsfreeL PGA"},
{"HFR", NULL, "HandsfreeR PGA"}, {"HFR", NULL, "HandsfreeR PGA"},
{"Vibra Route", "Audio", "Vibra Mux"}, {"Vibra Route", "Audio", "Vibra Mux"},
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment