From 86ece5efef2c3405e478e5c5d86b4435e8115aaf Mon Sep 17 00:00:00 2001 From: Erik Bourget Date: Sat, 31 Oct 2026 22:48:16 -0400 Subject: [PATCH] ASoC: tas2764: Free the fault IRQ when the component is removed The component probe requests the fault IRQ as a devres of the I2C device, which stays bound when the sound card is torn down. Every rebind of the card probes the component again and adds another handler for the same amp on the line: after two rebinds the J713's four amps had twelve handlers on their shared IRQ, each fault interrupt ran every one of them, and each does six I2C reads. Free the IRQ in the component remove. Request it as the probe's last step, so that a probe that fails later does not leave a handler behind either. Signed-off-by: Erik Bourget --- sound/soc/codecs/tas2764.c | 31 +++++++++++++++++++++++-------- 1 file changed, 23 insertions(+), 8 deletions(-) diff --git a/sound/soc/codecs/tas2764.c b/sound/soc/codecs/tas2764.c index 399aa42c6414a..92289355d1c3e 100644 --- a/sound/soc/codecs/tas2764.c +++ b/sound/soc/codecs/tas2764.c @@ -59,6 +59,8 @@ struct tas2764_priv { struct regmap *regmap; struct device *dev; int irq; + /* Set while the component holds the fault IRQ */ + bool irq_requested; enum tas2764_devid devid; int v_sense_slot; @@ -954,14 +956,6 @@ static int tas2764_codec_probe(struct snd_soc_component *component) ret = snd_soc_component_write(tas2764->component, TAS2764_INT_MASK4, 0xff); if (ret < 0) return ret; - - ret = devm_request_threaded_irq(tas2764->dev, tas2764->irq, NULL, tas2764_irq, - IRQF_ONESHOT | IRQF_SHARED | IRQF_TRIGGER_LOW, - "tas2764", tas2764); - if (ret && tas2764->sdz_group) - return dev_err_probe(tas2764->dev, ret, "SDZ group needs its fault IRQ\n"); - if (ret) - dev_warn(tas2764->dev, "failed to request IRQ: %d\n", ret); } if (tas2764->i_sense_slot != -1 && tas2764->v_sense_slot != -1) { @@ -1000,6 +994,22 @@ static int tas2764_codec_probe(struct snd_soc_component *component) break; } + /* + * Last, so that no failure leaves the handler behind: the IRQ belongs to the I2C + * device, which outlives the component, and the remove frees it. + */ + if (tas2764->irq) { + ret = devm_request_threaded_irq(tas2764->dev, tas2764->irq, NULL, tas2764_irq, + IRQF_ONESHOT | IRQF_SHARED | IRQF_TRIGGER_LOW, + "tas2764", tas2764); + if (ret && tas2764->sdz_group) + return dev_err_probe(tas2764->dev, ret, "SDZ group needs its fault IRQ\n"); + if (ret) + dev_warn(tas2764->dev, "failed to request IRQ: %d\n", ret); + else + tas2764->irq_requested = true; + } + return 0; } @@ -1007,6 +1017,11 @@ static void tas2764_codec_remove(struct snd_soc_component *component) { struct tas2764_priv *tas2764 = snd_soc_component_get_drvdata(component); + if (tas2764->irq_requested) { + devm_free_irq(tas2764->dev, tas2764->irq, tas2764); + tas2764->irq_requested = false; + } + /* A failed group's supply was forced off: there is no enable left to balance. */ if (tas2764_group_failed(tas2764)) return;