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: Charles Keepax <ckeepax@opensource.cirrus.com> Link: https://msgid.link/r/20231211160019.2034442-2-ckeepax@opensource.cirrus.com Signed-off-by: Mark Brown <broonie@kernel.org>
This commit is contained in:
parent
28b0b18d53
commit
bbbc18d8c2
@ -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)
|
||||
|
Loading…
x
Reference in New Issue
Block a user