* [PATCH 1/4] ASoC: rt5668: cancel pending delayed works on component remove
2026-10-26 11:22 [PATCH 0/4] ASoC: cancel pending delayed works on component remove Tristan Madani
@ 2026-10-26 11:22 ` Tristan Madani
2026-10-26 11:22 ` [PATCH 2/4] ASoC: rt5659: cancel jack_detect_work " Tristan Madani
` (2 subsequent siblings)
3 siblings, 0 replies; 5+ messages in thread
From: Tristan Madani @ 2026-10-26 11:22 UTC (permalink / raw)
To: Mark Brown, Takashi Iwai
Cc: Oder Chiou, Bard Liao, Liam Girdwood, Jaroslav Kysela,
Jorge Eduardo Candelaria, linux-sound, linux-kernel, stable,
Tristan Madani
From: Tristan Madani <tristan@talencesecurity.com>
The rt5668 codec initializes two delayed works (jack_detect_work,
jd_check_work) but neither is cancelled during component removal.
If the card is unbound while these works are pending, they fire on
freed resources when snd_soc_jack_report() dereferences the cleared
card pointer.
Track successful IRQ acquisition with a flag so that disable_irq()
only quiesces a line this driver actually owns. On component remove,
disable the IRQ to stop the handler from rescheduling work, then
drain both delayed works before resetting the codec. On the next
component probe (card rebind), re-enable the IRQ to restore jack
detection.
Fixes: d59fb2856223 ("ASoC: rt5668: add rt5668B codec driver")
Cc: stable@vger.kernel.org
Signed-off-by: Tristan Madani <tristan@talencesecurity.com>
---
sound/soc/codecs/rt5668.c | 20 +++++++++++++++++++-
1 file changed, 19 insertions(+), 1 deletion(-)
diff --git a/sound/soc/codecs/rt5668.c b/sound/soc/codecs/rt5668.c
index 4154bad276101..01b14c62fde7f 100644
--- a/sound/soc/codecs/rt5668.c
+++ b/sound/soc/codecs/rt5668.c
@@ -62,6 +62,8 @@ struct rt5668_priv {
int pll_out;
int jack_type;
+ bool irq_acquired;
+ bool irq_disabled;
};
static const struct reg_default rt5668_reg[] = {
@@ -2213,6 +2215,12 @@ static int rt5668_set_bias_level(struct snd_soc_component *component,
static int rt5668_probe(struct snd_soc_component *component)
{
struct rt5668_priv *rt5668 = snd_soc_component_get_drvdata(component);
+ struct i2c_client *i2c = to_i2c_client(component->dev);
+
+ if (rt5668->irq_disabled) {
+ enable_irq(i2c->irq);
+ rt5668->irq_disabled = false;
+ }
rt5668->component = component;
@@ -2222,7 +2230,15 @@ static int rt5668_probe(struct snd_soc_component *component)
static void rt5668_remove(struct snd_soc_component *component)
{
struct rt5668_priv *rt5668 = snd_soc_component_get_drvdata(component);
+ struct i2c_client *i2c = to_i2c_client(component->dev);
+ if (rt5668->irq_acquired) {
+ disable_irq(i2c->irq);
+ rt5668->irq_disabled = true;
+ }
+
+ cancel_delayed_work_sync(&rt5668->jack_detect_work);
+ cancel_delayed_work_sync(&rt5668->jd_check_work);
rt5668_reset(rt5668->regmap);
}
@@ -2531,7 +2547,9 @@ static int rt5668_i2c_probe(struct i2c_client *i2c)
ret = devm_request_threaded_irq(&i2c->dev, i2c->irq, NULL,
rt5668_irq, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING
| IRQF_ONESHOT, "rt5668", rt5668);
- if (ret)
+ if (!ret)
+ rt5668->irq_acquired = true;
+ else
dev_err(&i2c->dev, "Failed to request IRQ: %d\n", ret);
}
--
2.47.3
^ permalink raw reply [flat|nested] 5+ messages in thread* [PATCH 2/4] ASoC: rt5659: cancel jack_detect_work on component remove
2026-10-26 11:22 [PATCH 0/4] ASoC: cancel pending delayed works on component remove Tristan Madani
2026-10-26 11:22 ` [PATCH 1/4] ASoC: rt5668: " Tristan Madani
@ 2026-10-26 11:22 ` Tristan Madani
2026-10-26 11:22 ` [PATCH 3/4] ASoC: twl6040: cancel headset jack work " Tristan Madani
2026-10-26 11:22 ` [PATCH 4/4] ASoC: wm8994: drain all delayed works " Tristan Madani
3 siblings, 0 replies; 5+ messages in thread
From: Tristan Madani @ 2026-10-26 11:22 UTC (permalink / raw)
To: Mark Brown, Takashi Iwai
Cc: Oder Chiou, Bard Liao, Liam Girdwood, Jaroslav Kysela,
Jorge Eduardo Candelaria, linux-sound, linux-kernel, stable,
Tristan Madani
From: Tristan Madani <tristan@talencesecurity.com>
rt5659_remove() resets the codec via regmap_write() but does not cancel
the pending jack_detect_work. Since the IRQ is registered with
devm_request_threaded_irq(), it remains active until after the component
remove callback returns. The IRQ handler (rt5659_irq) schedules
jack_detect_work, so a late interrupt can queue work that runs against a
reset codec.
Track successful IRQ acquisition with a flag so that disable_irq()
only quiesces a line this driver actually owns. On component remove,
disable the IRQ to synchronize with any running handler and prevent
new enqueues, then drain the residual jack_detect_work synchronously
before resetting the hardware. On the next component probe (card
rebind), re-enable the IRQ to restore jack detection.
Fixes: d3cb2de2479b ("ASoC: rt5659: add rt5659 codec driver")
Cc: stable@vger.kernel.org
Signed-off-by: Tristan Madani <tristan@talencesecurity.com>
---
sound/soc/codecs/rt5659.c | 17 ++++++++++++++++-
sound/soc/codecs/rt5659.h | 2 ++
2 files changed, 18 insertions(+), 1 deletion(-)
diff --git a/sound/soc/codecs/rt5659.c b/sound/soc/codecs/rt5659.c
index 3097ee6d4e89f..340d3e7be7a40 100644
--- a/sound/soc/codecs/rt5659.c
+++ b/sound/soc/codecs/rt5659.c
@@ -3664,6 +3664,12 @@ static int rt5659_probe(struct snd_soc_component *component)
{
struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
struct rt5659_priv *rt5659 = snd_soc_component_get_drvdata(component);
+ struct i2c_client *i2c = to_i2c_client(component->dev);
+
+ if (rt5659->irq_disabled) {
+ enable_irq(i2c->irq);
+ rt5659->irq_disabled = false;
+ }
rt5659->component = component;
@@ -3684,7 +3690,14 @@ static int rt5659_probe(struct snd_soc_component *component)
static void rt5659_remove(struct snd_soc_component *component)
{
struct rt5659_priv *rt5659 = snd_soc_component_get_drvdata(component);
+ struct i2c_client *i2c = to_i2c_client(component->dev);
+
+ if (rt5659->irq_acquired) {
+ disable_irq(i2c->irq);
+ rt5659->irq_disabled = true;
+ }
+ cancel_delayed_work_sync(&rt5659->jack_detect_work);
regmap_write(rt5659->regmap, RT5659_RESET, 0);
}
@@ -4290,7 +4303,9 @@ static int rt5659_i2c_probe(struct i2c_client *i2c)
ret = devm_request_threaded_irq(&i2c->dev, i2c->irq, NULL,
rt5659_irq, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING
| IRQF_ONESHOT, "rt5659", rt5659);
- if (ret)
+ if (!ret)
+ rt5659->irq_acquired = true;
+ else
dev_err(&i2c->dev, "Failed to request IRQ: %d\n", ret);
/* Enable IRQ output for GPIO1 pin any way */
diff --git a/sound/soc/codecs/rt5659.h b/sound/soc/codecs/rt5659.h
index b49fd8baf4e7e..4df7d7dd99e16 100644
--- a/sound/soc/codecs/rt5659.h
+++ b/sound/soc/codecs/rt5659.h
@@ -1813,6 +1813,8 @@ struct rt5659_priv {
int jack_type;
bool hda_hp_plugged;
bool hda_mic_plugged;
+ bool irq_acquired;
+ bool irq_disabled;
};
int rt5659_set_jack_detect(struct snd_soc_component *component,
--
2.47.3
^ permalink raw reply [flat|nested] 5+ messages in thread* [PATCH 3/4] ASoC: twl6040: cancel headset jack work on component remove
2026-10-26 11:22 [PATCH 0/4] ASoC: cancel pending delayed works on component remove Tristan Madani
2026-10-26 11:22 ` [PATCH 1/4] ASoC: rt5668: " Tristan Madani
2026-10-26 11:22 ` [PATCH 2/4] ASoC: rt5659: cancel jack_detect_work " Tristan Madani
@ 2026-10-26 11:22 ` Tristan Madani
2026-10-26 11:22 ` [PATCH 4/4] ASoC: wm8994: drain all delayed works " Tristan Madani
3 siblings, 0 replies; 5+ messages in thread
From: Tristan Madani @ 2026-10-26 11:22 UTC (permalink / raw)
To: Mark Brown, Takashi Iwai
Cc: Oder Chiou, Bard Liao, Liam Girdwood, Jaroslav Kysela,
Jorge Eduardo Candelaria, linux-sound, linux-kernel, stable,
Tristan Madani
From: Tristan Madani <tristan@talencesecurity.com>
twl6040_remove() frees the plug detect IRQ but does not cancel the
delayed work that the IRQ handler schedules. If the work is still
pending after free_irq() returns, it fires on freed memory.
Add cancel_delayed_work_sync() after free_irq() to drain any residual
work. The IRQ is freed first to stop the producer, then the work is
drained.
Fixes: a2d2362edf9f ("ASoC: twl6040: Add jack support for headset and handset")
Cc: stable@vger.kernel.org
Signed-off-by: Tristan Madani <tristan@talencesecurity.com>
---
sound/soc/codecs/twl6040.c | 1 +
1 file changed, 1 insertion(+)
diff --git a/sound/soc/codecs/twl6040.c b/sound/soc/codecs/twl6040.c
index 650836f9615fb..826bde1a9ba46 100644
--- a/sound/soc/codecs/twl6040.c
+++ b/sound/soc/codecs/twl6040.c
@@ -1136,6 +1136,7 @@ static void twl6040_remove(struct snd_soc_component *component)
struct twl6040_data *priv = snd_soc_component_get_drvdata(component);
free_irq(priv->plug_irq, component);
+ cancel_delayed_work_sync(&priv->hs_jack.work);
}
static const struct snd_soc_component_driver soc_component_dev_twl6040 = {
--
2.47.3
^ permalink raw reply [flat|nested] 5+ messages in thread* [PATCH 4/4] ASoC: wm8994: drain all delayed works on component remove
2026-10-26 11:22 [PATCH 0/4] ASoC: cancel pending delayed works on component remove Tristan Madani
` (2 preceding siblings ...)
2026-10-26 11:22 ` [PATCH 3/4] ASoC: twl6040: cancel headset jack work " Tristan Madani
@ 2026-10-26 11:22 ` Tristan Madani
3 siblings, 0 replies; 5+ messages in thread
From: Tristan Madani @ 2026-10-26 11:22 UTC (permalink / raw)
To: Mark Brown, Takashi Iwai
Cc: Oder Chiou, Bard Liao, Liam Girdwood, Jaroslav Kysela,
Jorge Eduardo Candelaria, linux-sound, linux-kernel, stable,
Tristan Madani
From: Tristan Madani <tristan@talencesecurity.com>
wm8994_component_remove() frees IRQs and firmware but does not cancel
any of the delayed works that IRQ handlers and DAPM events schedule.
jackdet_bootstrap can spawn mic_work (via wm1811_jackdet_irq), and
wm8958_mic_irq can queue mic_complete_work, whose callback may in turn
queue open_circuit_work. If any of these works are pending when the
component is unbound, they fire after DAPM widgets are freed.
After freeing all IRQ producers and before releasing firmware, drain
the full work graph in producer-to-child order: jackdet_bootstrap,
mic_complete_work, mic_work, then open_circuit_work.
Fixes: 99af79dff5a6 ("ASoC: wm8994: Ensure we get a notification on startup for jackdet")
Cc: stable@vger.kernel.org
Signed-off-by: Tristan Madani <tristan@talencesecurity.com>
---
sound/soc/codecs/wm8994.c | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/sound/soc/codecs/wm8994.c b/sound/soc/codecs/wm8994.c
index 8bf58a6a8af7c..32c1786bd48ef 100644
--- a/sound/soc/codecs/wm8994.c
+++ b/sound/soc/codecs/wm8994.c
@@ -4592,6 +4592,10 @@ static void wm8994_component_remove(struct snd_soc_component *component)
free_irq(wm8994->micdet_irq, wm8994);
break;
}
+ cancel_delayed_work_sync(&wm8994->jackdet_bootstrap);
+ cancel_delayed_work_sync(&wm8994->mic_complete_work);
+ cancel_delayed_work_sync(&wm8994->mic_work);
+ cancel_delayed_work_sync(&wm8994->open_circuit_work);
release_firmware(wm8994->mbc);
release_firmware(wm8994->mbc_vss);
release_firmware(wm8994->enh_eq);
--
2.47.3
^ permalink raw reply [flat|nested] 5+ messages in thread