Commit bbbc18d8 authored by Charles Keepax's avatar Charles Keepax Committed by Mark Brown

ASoC: cs42l43: Allow HP amp to cool off after current limit

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: default avatarCharles Keepax <ckeepax@opensource.cirrus.com>
Link: https://msgid.link/r/20231211160019.2034442-2-ckeepax@opensource.cirrus.comSigned-off-by: default avatarMark Brown <broonie@kernel.org>
parent 28b0b18d
......@@ -506,7 +506,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);
......@@ -571,7 +571,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);
......
......@@ -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);
......
......@@ -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)
......
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