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@xxxxxxxxxxxxxxxxxxxxx> --- 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) -- 2.39.2