From: Tristan Madani wm8994_component_probe() schedules several delayed works to handle jack detection and microphone status polling. If the component is removed or probe fails after these works are scheduled, they can execute after resources are freed. Cancel all pending delayed works in the error and removal paths. Guard cancel_delayed_work_sync(&wm8994->mic_work) with a type check because INIT_DELAYED_WORK for mic_work only runs in the WM8994 and WM1811 code paths. Cancel mic_work before jackdet_bootstrap and mic_complete_work because wm1811_mic_work() can re-arm jackdet_bootstrap through the DAPM power-up chain (force-enabling MICBIAS2 triggers clk_sys_event which queues jackdet_bootstrap), and enabling microphone detection in hardware can cause WM8994_IRQ_MIC1_DET to fire and re-queue mic_complete_work. Fixes: 99af79dff5a6 ("ASoC: wm8994: Ensure we get a notification on startup for jackdet") Cc: stable@vger.kernel.org Signed-off-by: Tristan Madani --- Changes since v9: - Reorder cancel_delayed_work_sync calls: cancel mic_work first, then jackdet_bootstrap, mic_complete_work, open_circuit_work. wm1811_mic_work() can re-arm jackdet_bootstrap through the DAPM power-up chain (MICBIAS2 -> CLK_SYS -> clk_sys_event) and can trigger MIC1_DET which re-queues mic_complete_work, so cancelling mic_work before its downstream consumers prevents re-arming after cancel. Changes since v8 [4/4]: - Guard cancel_delayed_work_sync(&wm8994->mic_work) with type check (WM8994 || WM1811), as mic_work is only initialized in those code paths — addresses Mark Brown review comment - Add cleanup to probe error path (err_irq label), not just remove sound/soc/codecs/wm8994.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/sound/soc/codecs/wm8994.c b/sound/soc/codecs/wm8994.c index fc9894975a1d5..5a493f29c2ca3 100644 --- a/sound/soc/codecs/wm8994.c +++ b/sound/soc/codecs/wm8994.c @@ -4563,6 +4563,11 @@ static int wm8994_component_probe(struct snd_soc_component *component) wm8994_free_irq(wm8994->wm8994, WM8994_IRQ_FIFOS_ERR, component); wm8994_free_irq(wm8994->wm8994, WM8994_IRQ_TEMP_SHUT, component); wm8994_free_irq(wm8994->wm8994, WM8994_IRQ_TEMP_WARN, component); + if (control->type == WM8994 || control->type == WM1811) + cancel_delayed_work_sync(&wm8994->mic_work); + cancel_delayed_work_sync(&wm8994->jackdet_bootstrap); + cancel_delayed_work_sync(&wm8994->mic_complete_work); + cancel_delayed_work_sync(&wm8994->open_circuit_work); return ret; } @@ -4604,6 +4609,11 @@ static void wm8994_component_remove(struct snd_soc_component *component) free_irq(wm8994->micdet_irq, wm8994); break; } + if (control->type == WM8994 || control->type == WM1811) + cancel_delayed_work_sync(&wm8994->mic_work); + cancel_delayed_work_sync(&wm8994->jackdet_bootstrap); + cancel_delayed_work_sync(&wm8994->mic_complete_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