Commit 86834511 authored by Adam Thomson's avatar Adam Thomson Committed by Mark Brown

ASoC: da7219: Improve pop/click performance for sensitive HPs

Currently on some headsets slight pops can be heard during DAPM
power-up/down. This can also be witnessed during the HP detect
procedure. This patch addresses the issue by adjusting DAPM power
sequencing slightly, the introduction of delays and use of
minimum HP gain to avoid such noise artefacts.
Signed-off-by: default avatarAdam Thomson <Adam.Thomson.Opensource@diasemi.com>
Signed-off-by: default avatarMark Brown <broonie@kernel.org>
parent 513e43ef
...@@ -204,10 +204,19 @@ static void da7219_aad_hptest_work(struct work_struct *work) ...@@ -204,10 +204,19 @@ static void da7219_aad_hptest_work(struct work_struct *work)
snd_soc_update_bits(codec, DA7219_MIXOUT_R_CTRL, snd_soc_update_bits(codec, DA7219_MIXOUT_R_CTRL,
DA7219_MIXOUT_R_AMP_EN_MASK, DA7219_MIXOUT_R_AMP_EN_MASK,
DA7219_MIXOUT_R_AMP_EN_MASK); DA7219_MIXOUT_R_AMP_EN_MASK);
snd_soc_write(codec, DA7219_HP_L_CTRL, snd_soc_update_bits(codec, DA7219_HP_L_CTRL,
DA7219_HP_L_AMP_OE_MASK | DA7219_HP_L_AMP_EN_MASK); DA7219_HP_L_AMP_OE_MASK | DA7219_HP_L_AMP_EN_MASK,
snd_soc_write(codec, DA7219_HP_R_CTRL, DA7219_HP_L_AMP_OE_MASK | DA7219_HP_L_AMP_EN_MASK);
DA7219_HP_R_AMP_OE_MASK | DA7219_HP_R_AMP_EN_MASK); snd_soc_update_bits(codec, DA7219_HP_R_CTRL,
DA7219_HP_R_AMP_OE_MASK | DA7219_HP_R_AMP_EN_MASK,
DA7219_HP_R_AMP_OE_MASK | DA7219_HP_R_AMP_EN_MASK);
msleep(DA7219_SETTLING_DELAY);
snd_soc_update_bits(codec, DA7219_HP_L_CTRL,
DA7219_HP_L_AMP_MUTE_EN_MASK |
DA7219_HP_L_AMP_MIN_GAIN_EN_MASK, 0);
snd_soc_update_bits(codec, DA7219_HP_R_CTRL,
DA7219_HP_R_AMP_MUTE_EN_MASK |
DA7219_HP_R_AMP_MIN_GAIN_EN_MASK, 0);
/* /*
* If we're running from the internal oscillator then give audio paths * If we're running from the internal oscillator then give audio paths
...@@ -244,6 +253,7 @@ static void da7219_aad_hptest_work(struct work_struct *work) ...@@ -244,6 +253,7 @@ static void da7219_aad_hptest_work(struct work_struct *work)
regcache_mark_dirty(da7219->regmap); regcache_mark_dirty(da7219->regmap);
regcache_sync_region(da7219->regmap, DA7219_HP_L_CTRL, regcache_sync_region(da7219->regmap, DA7219_HP_L_CTRL,
DA7219_HP_R_CTRL); DA7219_HP_R_CTRL);
msleep(DA7219_SETTLING_DELAY);
regcache_sync_region(da7219->regmap, DA7219_MIXOUT_L_CTRL, regcache_sync_region(da7219->regmap, DA7219_MIXOUT_L_CTRL,
DA7219_MIXOUT_R_CTRL); DA7219_MIXOUT_R_CTRL);
regcache_sync_region(da7219->regmap, DA7219_DROUTING_ST_OUTFILT_1L, regcache_sync_region(da7219->regmap, DA7219_DROUTING_ST_OUTFILT_1L,
......
...@@ -823,6 +823,85 @@ static int da7219_dai_event(struct snd_soc_dapm_widget *w, ...@@ -823,6 +823,85 @@ static int da7219_dai_event(struct snd_soc_dapm_widget *w,
} }
} }
static int da7219_settling_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
switch (event) {
case SND_SOC_DAPM_POST_PMU:
case SND_SOC_DAPM_POST_PMD:
msleep(DA7219_SETTLING_DELAY);
break;
default:
break;
}
return 0;
}
static int da7219_mixout_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
u8 hp_ctrl, min_gain_mask;
switch (w->reg) {
case DA7219_MIXOUT_L_CTRL:
hp_ctrl = DA7219_HP_L_CTRL;
min_gain_mask = DA7219_HP_L_AMP_MIN_GAIN_EN_MASK;
break;
case DA7219_MIXOUT_R_CTRL:
hp_ctrl = DA7219_HP_R_CTRL;
min_gain_mask = DA7219_HP_R_AMP_MIN_GAIN_EN_MASK;
break;
default:
return -EINVAL;
}
switch (event) {
case SND_SOC_DAPM_PRE_PMD:
/* Enable minimum gain on HP to avoid pops */
snd_soc_update_bits(codec, hp_ctrl, min_gain_mask,
min_gain_mask);
msleep(DA7219_MIN_GAIN_DELAY);
break;
case SND_SOC_DAPM_POST_PMU:
/* Remove minimum gain on HP */
snd_soc_update_bits(codec, hp_ctrl, min_gain_mask, 0);
break;
}
return 0;
}
static int da7219_gain_ramp_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
struct da7219_priv *da7219 = snd_soc_codec_get_drvdata(codec);
switch (event) {
case SND_SOC_DAPM_PRE_PMU:
case SND_SOC_DAPM_PRE_PMD:
/* Ensure nominal gain ramping for DAPM sequence */
da7219->gain_ramp_ctrl =
snd_soc_read(codec, DA7219_GAIN_RAMP_CTRL);
snd_soc_write(codec, DA7219_GAIN_RAMP_CTRL,
DA7219_GAIN_RAMP_RATE_NOMINAL);
break;
case SND_SOC_DAPM_POST_PMU:
case SND_SOC_DAPM_POST_PMD:
/* Restore previous gain ramp settings */
snd_soc_write(codec, DA7219_GAIN_RAMP_CTRL,
da7219->gain_ramp_ctrl);
break;
}
return 0;
}
/* /*
* DAPM Widgets * DAPM Widgets
...@@ -906,30 +985,46 @@ static const struct snd_soc_dapm_widget da7219_dapm_widgets[] = { ...@@ -906,30 +985,46 @@ static const struct snd_soc_dapm_widget da7219_dapm_widgets[] = {
ARRAY_SIZE(da7219_st_out_filtr_mix_controls)), ARRAY_SIZE(da7219_st_out_filtr_mix_controls)),
/* DACs */ /* DACs */
SND_SOC_DAPM_DAC("DACL", NULL, DA7219_DAC_L_CTRL, DA7219_DAC_L_EN_SHIFT, SND_SOC_DAPM_DAC_E("DACL", NULL, DA7219_DAC_L_CTRL,
DA7219_NO_INVERT), DA7219_DAC_L_EN_SHIFT, DA7219_NO_INVERT,
SND_SOC_DAPM_DAC("DACR", NULL, DA7219_DAC_R_CTRL, DA7219_DAC_R_EN_SHIFT, da7219_settling_event,
DA7219_NO_INVERT), SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
SND_SOC_DAPM_DAC_E("DACR", NULL, DA7219_DAC_R_CTRL,
DA7219_DAC_R_EN_SHIFT, DA7219_NO_INVERT,
da7219_settling_event,
SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
/* Output PGAs */ /* Output PGAs */
SND_SOC_DAPM_PGA("Mixout Left PGA", DA7219_MIXOUT_L_CTRL, SND_SOC_DAPM_PGA_E("Mixout Left PGA", DA7219_MIXOUT_L_CTRL,
DA7219_MIXOUT_L_AMP_EN_SHIFT, DA7219_NO_INVERT, DA7219_MIXOUT_L_AMP_EN_SHIFT, DA7219_NO_INVERT,
NULL, 0), NULL, 0, da7219_mixout_event,
SND_SOC_DAPM_PGA("Mixout Right PGA", DA7219_MIXOUT_R_CTRL, SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
DA7219_MIXOUT_R_AMP_EN_SHIFT, DA7219_NO_INVERT, SND_SOC_DAPM_PGA_E("Mixout Right PGA", DA7219_MIXOUT_R_CTRL,
NULL, 0), DA7219_MIXOUT_R_AMP_EN_SHIFT, DA7219_NO_INVERT,
SND_SOC_DAPM_PGA("Headphone Left PGA", DA7219_HP_L_CTRL, NULL, 0, da7219_mixout_event,
DA7219_HP_L_AMP_EN_SHIFT, DA7219_NO_INVERT, NULL, 0), SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
SND_SOC_DAPM_PGA("Headphone Right PGA", DA7219_HP_R_CTRL, SND_SOC_DAPM_SUPPLY_S("Headphone Left PGA", 1, DA7219_HP_L_CTRL,
DA7219_HP_R_AMP_EN_SHIFT, DA7219_NO_INVERT, NULL, 0), DA7219_HP_L_AMP_EN_SHIFT, DA7219_NO_INVERT,
da7219_settling_event,
SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
SND_SOC_DAPM_SUPPLY_S("Headphone Right PGA", 1, DA7219_HP_R_CTRL,
DA7219_HP_R_AMP_EN_SHIFT, DA7219_NO_INVERT,
da7219_settling_event,
SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
/* Output Supplies */ /* Output Supplies */
SND_SOC_DAPM_SUPPLY("Charge Pump", DA7219_CP_CTRL, DA7219_CP_EN_SHIFT, SND_SOC_DAPM_SUPPLY_S("Charge Pump", 0, DA7219_CP_CTRL,
DA7219_NO_INVERT, NULL, 0), DA7219_CP_EN_SHIFT, DA7219_NO_INVERT,
da7219_settling_event,
SND_SOC_DAPM_POST_PMU),
/* Outputs */ /* Outputs */
SND_SOC_DAPM_OUTPUT("HPL"), SND_SOC_DAPM_OUTPUT("HPL"),
SND_SOC_DAPM_OUTPUT("HPR"), SND_SOC_DAPM_OUTPUT("HPR"),
/* Pre/Post Power */
SND_SOC_DAPM_PRE("Pre Power Gain Ramp", da7219_gain_ramp_event),
SND_SOC_DAPM_POST("Post Power Gain Ramp", da7219_gain_ramp_event),
}; };
...@@ -1002,8 +1097,8 @@ static const struct snd_soc_dapm_route da7219_audio_map[] = { ...@@ -1002,8 +1097,8 @@ static const struct snd_soc_dapm_route da7219_audio_map[] = {
{"Mixout Left PGA", NULL, "DACL"}, {"Mixout Left PGA", NULL, "DACL"},
{"Mixout Right PGA", NULL, "DACR"}, {"Mixout Right PGA", NULL, "DACR"},
{"Headphone Left PGA", NULL, "Mixout Left PGA"}, {"HPL", NULL, "Mixout Left PGA"},
{"Headphone Right PGA", NULL, "Mixout Right PGA"}, {"HPR", NULL, "Mixout Right PGA"},
{"HPL", NULL, "Headphone Left PGA"}, {"HPL", NULL, "Headphone Left PGA"},
{"HPR", NULL, "Headphone Right PGA"}, {"HPR", NULL, "Headphone Right PGA"},
...@@ -1711,6 +1806,14 @@ static int da7219_probe(struct snd_soc_codec *codec) ...@@ -1711,6 +1806,14 @@ static int da7219_probe(struct snd_soc_codec *codec)
DA7219_HP_R_AMP_RAMP_EN_MASK, DA7219_HP_R_AMP_RAMP_EN_MASK,
DA7219_HP_R_AMP_RAMP_EN_MASK); DA7219_HP_R_AMP_RAMP_EN_MASK);
/* Default minimum gain on HP to avoid pops during DAPM sequencing */
snd_soc_update_bits(codec, DA7219_HP_L_CTRL,
DA7219_HP_L_AMP_MIN_GAIN_EN_MASK,
DA7219_HP_L_AMP_MIN_GAIN_EN_MASK);
snd_soc_update_bits(codec, DA7219_HP_R_CTRL,
DA7219_HP_R_AMP_MIN_GAIN_EN_MASK,
DA7219_HP_R_AMP_MIN_GAIN_EN_MASK);
/* Default infinite tone gen, start/stop by Kcontrol */ /* Default infinite tone gen, start/stop by Kcontrol */
snd_soc_write(codec, DA7219_TONE_GEN_CYCLES, DA7219_BEEP_CYCLES_MASK); snd_soc_write(codec, DA7219_TONE_GEN_CYCLES, DA7219_BEEP_CYCLES_MASK);
......
...@@ -777,6 +777,10 @@ ...@@ -777,6 +777,10 @@
#define DA7219_SYS_STAT_CHECK_RETRIES 6 #define DA7219_SYS_STAT_CHECK_RETRIES 6
#define DA7219_SYS_STAT_CHECK_DELAY 50 #define DA7219_SYS_STAT_CHECK_DELAY 50
/* Power up/down Delays */
#define DA7219_SETTLING_DELAY 40
#define DA7219_MIN_GAIN_DELAY 30
enum da7219_clk_src { enum da7219_clk_src {
DA7219_CLKSRC_MCLK = 0, DA7219_CLKSRC_MCLK = 0,
DA7219_CLKSRC_MCLK_SQR, DA7219_CLKSRC_MCLK_SQR,
...@@ -814,6 +818,7 @@ struct da7219_priv { ...@@ -814,6 +818,7 @@ struct da7219_priv {
bool master; bool master;
bool alc_en; bool alc_en;
u8 gain_ramp_ctrl;
}; };
#endif /* __DA7219_H */ #endif /* __DA7219_H */
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