[PATCH] ASoC: codecs: wcd937x: use devm_free_irq() for watchdog IRQs

From: Runyu Xiao

Date: Tue Sep 29 2026 - 05:46:40 EST


The watchdog IRQs are requested with devm_request_threaded_irq() from the
codec component probe, but the remove callback frees them with free_irq().
The devres entries remain in place. Component teardown later tries to free
the same IRQs again.

The IRQs must be released before the SoundWire components are unbound. Use
devm_free_irq() to remove each IRQ and its devres entry at that point. Fail
component probe if an IRQ request fails, and unwind only IRQs that were
successfully requested.

Fixes: 9be3ec196da4 ("ASoC: codecs: wcd937x: add wcd937x codec driver")
Assisted-by: LLM
Signed-off-by: Runyu Xiao <runyu.xiao@xxxxxxxxxx>
---
sound/soc/codecs/wcd937x.c | 32 +++++++++++++++++++++-----------
1 file changed, 21 insertions(+), 11 deletions(-)

diff --git a/sound/soc/codecs/wcd937x.c b/sound/soc/codecs/wcd937x.c
index 0dd05604f5b88..ec902c81d5a4e 100644
--- a/sound/soc/codecs/wcd937x.c
+++ b/sound/soc/codecs/wcd937x.c
@@ -2540,19 +2540,19 @@ static int wcd937x_soc_codec_probe(struct snd_soc_component *component)
IRQF_ONESHOT | IRQF_TRIGGER_RISING,
"HPHR PDM WDOG INT", wcd937x);
if (ret)
- dev_err(dev, "Failed to request HPHR watchdog interrupt (%d)\n", ret);
+ goto err_free_clsh;

ret = devm_request_threaded_irq(dev, wcd937x->hphl_pdm_wd_int, NULL, wcd937x_wd_handle_irq,
IRQF_ONESHOT | IRQF_TRIGGER_RISING,
"HPHL PDM WDOG INT", wcd937x);
if (ret)
- dev_err(dev, "Failed to request HPHL watchdog interrupt (%d)\n", ret);
+ goto err_free_hphr_irq;

ret = devm_request_threaded_irq(dev, wcd937x->aux_pdm_wd_int, NULL, wcd937x_wd_handle_irq,
IRQF_ONESHOT | IRQF_TRIGGER_RISING,
"AUX PDM WDOG INT", wcd937x);
if (ret)
- dev_err(dev, "Failed to request Aux watchdog interrupt (%d)\n", ret);
+ goto err_free_hphl_irq;

/* Disable watchdog interrupt for HPH and AUX */
disable_irq_nosync(wcd937x->hphr_pdm_wd_int);
@@ -2564,24 +2564,34 @@ static int wcd937x_soc_codec_probe(struct snd_soc_component *component)
ARRAY_SIZE(wcd9375_dapm_widgets));
if (ret < 0) {
dev_err(component->dev, "Failed to add snd_ctls\n");
- wcd_clsh_ctrl_free(wcd937x->clsh_info);
- return ret;
+ goto err_free_irqs;
}

ret = snd_soc_dapm_add_routes(dapm, wcd9375_audio_map,
ARRAY_SIZE(wcd9375_audio_map));
if (ret < 0) {
dev_err(component->dev, "Failed to add routes\n");
- wcd_clsh_ctrl_free(wcd937x->clsh_info);
- return ret;
+ goto err_free_irqs;
}
}

ret = wcd937x_mbhc_init(component);
- if (ret)
+ if (ret) {
dev_err(component->dev, "mbhc initialization failed\n");
+ goto err_free_irqs;
+ }

return ret;
+
+err_free_irqs:
+ devm_free_irq(dev, wcd937x->aux_pdm_wd_int, wcd937x);
+err_free_hphl_irq:
+ devm_free_irq(dev, wcd937x->hphl_pdm_wd_int, wcd937x);
+err_free_hphr_irq:
+ devm_free_irq(dev, wcd937x->hphr_pdm_wd_int, wcd937x);
+err_free_clsh:
+ wcd_clsh_ctrl_free(wcd937x->clsh_info);
+ return ret;
}

static void wcd937x_soc_codec_remove(struct snd_soc_component *component)
@@ -2589,9 +2599,9 @@ static void wcd937x_soc_codec_remove(struct snd_soc_component *component)
struct wcd937x_priv *wcd937x = snd_soc_component_get_drvdata(component);

wcd937x_mbhc_deinit(component);
- free_irq(wcd937x->aux_pdm_wd_int, wcd937x);
- free_irq(wcd937x->hphl_pdm_wd_int, wcd937x);
- free_irq(wcd937x->hphr_pdm_wd_int, wcd937x);
+ devm_free_irq(component->dev, wcd937x->aux_pdm_wd_int, wcd937x);
+ devm_free_irq(component->dev, wcd937x->hphl_pdm_wd_int, wcd937x);
+ devm_free_irq(component->dev, wcd937x->hphr_pdm_wd_int, wcd937x);

wcd_clsh_ctrl_free(wcd937x->clsh_info);
}
--
2.34.1