[PATCH 1/2] ASoC: cs42l43: Don't enable bias sense during type detect
Alas on some headsets the bias sense can cause problems with the type detection. It can occasionally be falsely triggered by the type detect itself and as the clamp is applied when this happens, it will cause a headset to be incorrectly identified as headphones. As such it should be disabled whilst running type detect. This does mean a jack removal during type detect will cause a larger click but that is unfortunately unavoidable.
Fixes: 1e4ce0d5c023 ("ASoC: cs42l43: Move headset bias sense enable earlier in process") Signed-off-by: Charles Keepax ckeepax@opensource.cirrus.com --- sound/soc/codecs/cs42l43-jack.c | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-)
diff --git a/sound/soc/codecs/cs42l43-jack.c b/sound/soc/codecs/cs42l43-jack.c index 73454de068cf8..54a3ea6064438 100644 --- a/sound/soc/codecs/cs42l43-jack.c +++ b/sound/soc/codecs/cs42l43-jack.c @@ -237,7 +237,7 @@ int cs42l43_set_jack(struct snd_soc_component *component, return ret; }
-static void cs42l43_start_hs_bias(struct cs42l43_codec *priv, bool force_high) +static void cs42l43_start_hs_bias(struct cs42l43_codec *priv, bool type_detect) { struct cs42l43 *cs42l43 = priv->core; unsigned int val = 0x3 << CS42L43_HSBIAS_MODE_SHIFT; @@ -247,16 +247,17 @@ static void cs42l43_start_hs_bias(struct cs42l43_codec *priv, bool force_high) regmap_update_bits(cs42l43->regmap, CS42L43_HS2, CS42L43_HS_CLAMP_DISABLE_MASK, CS42L43_HS_CLAMP_DISABLE_MASK);
- if (!force_high && priv->bias_low) - val = 0x2 << CS42L43_HSBIAS_MODE_SHIFT; - - if (priv->bias_sense_ua) { - regmap_update_bits(cs42l43->regmap, - CS42L43_HS_BIAS_SENSE_AND_CLAMP_AUTOCONTROL, - CS42L43_HSBIAS_SENSE_EN_MASK | - CS42L43_AUTO_HSBIAS_CLAMP_EN_MASK, - CS42L43_HSBIAS_SENSE_EN_MASK | - CS42L43_AUTO_HSBIAS_CLAMP_EN_MASK); + if (!type_detect) { + if (priv->bias_low) + val = 0x2 << CS42L43_HSBIAS_MODE_SHIFT; + + if (priv->bias_sense_ua) + regmap_update_bits(cs42l43->regmap, + CS42L43_HS_BIAS_SENSE_AND_CLAMP_AUTOCONTROL, + CS42L43_HSBIAS_SENSE_EN_MASK | + CS42L43_AUTO_HSBIAS_CLAMP_EN_MASK, + CS42L43_HSBIAS_SENSE_EN_MASK | + CS42L43_AUTO_HSBIAS_CLAMP_EN_MASK); }
regmap_update_bits(cs42l43->regmap, CS42L43_MIC_DETECT_CONTROL_1,
Whilst occasional current limiting is fine, constant current limiting should be avoided. Add a back off system that will disable the headphone amp, if a lot of current limiting is seen in a short window of time.
Signed-off-by: Charles Keepax ckeepax@opensource.cirrus.com --- sound/soc/codecs/cs42l43-jack.c | 4 +- sound/soc/codecs/cs42l43.c | 88 +++++++++++++++++++++++++++++++-- sound/soc/codecs/cs42l43.h | 9 ++++ 3 files changed, 96 insertions(+), 5 deletions(-)
diff --git a/sound/soc/codecs/cs42l43-jack.c b/sound/soc/codecs/cs42l43-jack.c index 54a3ea6064438..24a598f2ed9a3 100644 --- a/sound/soc/codecs/cs42l43-jack.c +++ b/sound/soc/codecs/cs42l43-jack.c @@ -507,7 +507,7 @@ static void cs42l43_start_load_detect(struct cs42l43_codec *priv)
priv->load_detect_running = true;
- if (priv->hp_ena) { + if (priv->hp_ena && !priv->hp_ilimited) { unsigned long time_left;
reinit_completion(&priv->hp_shutdown); @@ -572,7 +572,7 @@ static void cs42l43_stop_load_detect(struct cs42l43_codec *priv) CS42L43_ADC1_EN_MASK | CS42L43_ADC2_EN_MASK, priv->adc_ena);
- if (priv->hp_ena) { + if (priv->hp_ena && !priv->hp_ilimited) { unsigned long time_left;
reinit_completion(&priv->hp_startup); diff --git a/sound/soc/codecs/cs42l43.c b/sound/soc/codecs/cs42l43.c index 5c98343ebf71b..d2412dab35996 100644 --- a/sound/soc/codecs/cs42l43.c +++ b/sound/soc/codecs/cs42l43.c @@ -138,7 +138,87 @@ CS42L43_IRQ_ERROR(spkr_therm_warm) CS42L43_IRQ_ERROR(spkl_therm_warm) CS42L43_IRQ_ERROR(spkr_sc_detect) CS42L43_IRQ_ERROR(spkl_sc_detect) -CS42L43_IRQ_ERROR(hp_ilimit) + +void cs42l43_hp_ilimit_clear_work(struct work_struct *work) +{ + struct cs42l43_codec *priv = container_of(work, struct cs42l43_codec, + hp_ilimit_clear_work.work); + struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(priv->component); + + snd_soc_dapm_mutex_lock(dapm); + + priv->hp_ilimit_count--; + + if (priv->hp_ilimit_count) + queue_delayed_work(system_wq, &priv->hp_ilimit_clear_work, + msecs_to_jiffies(CS42L43_HP_ILIMIT_DECAY_MS)); + + snd_soc_dapm_mutex_unlock(dapm); +} + +void cs42l43_hp_ilimit_work(struct work_struct *work) +{ + struct cs42l43_codec *priv = container_of(work, struct cs42l43_codec, + hp_ilimit_work); + struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(priv->component); + struct cs42l43 *cs42l43 = priv->core; + + snd_soc_dapm_mutex_lock(dapm); + + if (priv->hp_ilimit_count < CS42L43_HP_ILIMIT_MAX_COUNT) { + if (!priv->hp_ilimit_count) + queue_delayed_work(system_wq, &priv->hp_ilimit_clear_work, + msecs_to_jiffies(CS42L43_HP_ILIMIT_DECAY_MS)); + + priv->hp_ilimit_count++; + snd_soc_dapm_mutex_unlock(dapm); + return; + } + + dev_err(priv->dev, "Disabling headphone for %dmS, due to frequent current limit\n", + CS42L43_HP_ILIMIT_BACKOFF_MS); + + priv->hp_ilimited = true; + + // No need to wait for disable, as just disabling for a period of time + regmap_update_bits(cs42l43->regmap, CS42L43_BLOCK_EN8, + CS42L43_HP_EN_MASK, 0); + + snd_soc_dapm_mutex_unlock(dapm); + + msleep(CS42L43_HP_ILIMIT_BACKOFF_MS); + + snd_soc_dapm_mutex_lock(dapm); + + if (priv->hp_ena && !priv->load_detect_running) { + unsigned long time_left; + + reinit_completion(&priv->hp_startup); + + regmap_update_bits(cs42l43->regmap, CS42L43_BLOCK_EN8, + CS42L43_HP_EN_MASK, priv->hp_ena); + + time_left = wait_for_completion_timeout(&priv->hp_startup, + msecs_to_jiffies(CS42L43_HP_TIMEOUT_MS)); + if (!time_left) + dev_err(priv->dev, "ilimit HP restore timed out\n"); + } + + priv->hp_ilimited = false; + + snd_soc_dapm_mutex_unlock(dapm); +} + +static irqreturn_t cs42l43_hp_ilimit(int irq, void *data) +{ + struct cs42l43_codec *priv = data; + + dev_dbg(priv->dev, "headphone ilimit IRQ\n"); + + queue_work(system_long_wq, &priv->hp_ilimit_work); + + return IRQ_HANDLED; +}
#define CS42L43_IRQ_COMPLETE(name) \ static irqreturn_t cs42l43_##name(int irq, void *data) \ @@ -1452,13 +1532,13 @@ static int cs42l43_hp_ev(struct snd_soc_dapm_widget *w, if (ret) return ret;
- if (!priv->load_detect_running) + if (!priv->load_detect_running && !priv->hp_ilimited) regmap_update_bits(cs42l43->regmap, CS42L43_BLOCK_EN8, mask, val); break; case SND_SOC_DAPM_POST_PMU: case SND_SOC_DAPM_POST_PMD: - if (priv->load_detect_running) + if (priv->load_detect_running || priv->hp_ilimited) break;
ret = cs42l43_dapm_wait_completion(&priv->hp_startup, @@ -2169,7 +2249,9 @@ static int cs42l43_codec_probe(struct platform_device *pdev) INIT_DELAYED_WORK(&priv->tip_sense_work, cs42l43_tip_sense_work); INIT_DELAYED_WORK(&priv->bias_sense_timeout, cs42l43_bias_sense_timeout); INIT_DELAYED_WORK(&priv->button_press_work, cs42l43_button_press_work); + INIT_DELAYED_WORK(&priv->hp_ilimit_clear_work, cs42l43_hp_ilimit_clear_work); INIT_WORK(&priv->button_release_work, cs42l43_button_release_work); + INIT_WORK(&priv->hp_ilimit_work, cs42l43_hp_ilimit_work);
pm_runtime_set_autosuspend_delay(priv->dev, 100); pm_runtime_use_autosuspend(priv->dev); diff --git a/sound/soc/codecs/cs42l43.h b/sound/soc/codecs/cs42l43.h index bf4f728eea3e0..125e36861d5d5 100644 --- a/sound/soc/codecs/cs42l43.h +++ b/sound/soc/codecs/cs42l43.h @@ -28,6 +28,10 @@ #define CS42L43_HP_TIMEOUT_MS 2000 #define CS42L43_LOAD_TIMEOUT_MS 1000
+#define CS42L43_HP_ILIMIT_BACKOFF_MS 1000 +#define CS42L43_HP_ILIMIT_DECAY_MS 300 +#define CS42L43_HP_ILIMIT_MAX_COUNT 4 + #define CS42L43_ASP_MAX_CHANNELS 6 #define CS42L43_N_EQ_COEFFS 15
@@ -88,6 +92,11 @@ struct cs42l43_codec { bool button_detect_running; bool jack_present; int jack_override; + + struct work_struct hp_ilimit_work; + struct delayed_work hp_ilimit_clear_work; + bool hp_ilimited; + int hp_ilimit_count; };
#if IS_REACHABLE(CONFIG_SND_SOC_CS42L43_SDW)
On Mon, 11 Dec 2023 16:00:18 +0000, Charles Keepax wrote:
Alas on some headsets the bias sense can cause problems with the type detection. It can occasionally be falsely triggered by the type detect itself and as the clamp is applied when this happens, it will cause a headset to be incorrectly identified as headphones. As such it should be disabled whilst running type detect. This does mean a jack removal during type detect will cause a larger click but that is unfortunately unavoidable.
[...]
Applied to
https://git.kernel.org/pub/scm/linux/kernel/git/broonie/sound.git for-next
Thanks!
[1/2] ASoC: cs42l43: Don't enable bias sense during type detect commit: dc96528b176fa6e55a3dc01060fe9d97be450ce9 [2/2] ASoC: cs42l43: Allow HP amp to cool off after current limit commit: bbbc18d8c27cc9d40cc9a3b03b61e4df85311146
All being well this means that it will be integrated into the linux-next tree (usually sometime in the next 24 hours) and sent to Linus during the next merge window (or sooner if it is a bug fix), however if problems are discovered then the patch may be dropped or reverted.
You may get further e-mails resulting from automated or manual testing and review of the tree, please engage with people reporting problems and send followup patches addressing any issues that are reported if needed.
If any updates are required or you are submitting further changes they should be sent as incremental updates against current git, existing patches will not be replaced.
Please add any relevant lists and maintainers to the CCs when replying to this mail.
Thanks, Mark
participants (2)
-
Charles Keepax
-
Mark Brown