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>
This commit is contained in:
parent
c4ff357ada
commit
9008adf9a9
1 changed files with 60 additions and 49 deletions
|
@ -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,
|
||||||
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[] = {
|
||||||
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[] = {
|
||||||
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[] = {
|
||||||
{"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[] = {
|
||||||
{"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[] = {
|
||||||
/* 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"},
|
||||||
|
|
Reference in a new issue