From: Tristan Madani <tristmd@gmail.com>
To: Oder Chiou <oder_chiou@realtek.com>,
Liam Girdwood <lgirdwood@gmail.com>,
Mark Brown <broonie@kernel.org>, Jaroslav Kysela <perex@perex.cz>,
Takashi Iwai <tiwai@suse.com>
Cc: Bard Liao <bardliao@realtek.com>,
linux-sound@vger.kernel.org, linux-kernel@vger.kernel.org,
stable@vger.kernel.org,
Tristan Madani <tristan@talencesecurity.com>
Subject: [PATCH v2] ASoC: rt5659: fix teardown order and stale state across card rebind
Date: Sat, 7 Nov 2026 17:18:13 +0000 [thread overview]
Message-ID: <20261107171813.2561973-1-tristmd@gmail.com> (raw)
From: Tristan Madani <tristan@talencesecurity.com>
The cleanup in rt5659_remove() does not reset hs_jack, jack_type, or
the component pointer, and does not mark the regmap cache dirty after
the codec reset. On card rebind this leaves a dangling hs_jack pointer
and causes regmap_update_bits() to skip re-programming jack-detection
registers because the cache still holds the pre-reset values.
Only clear headset detection state via rt5659_headset_detect() for
JD3-mode jacks. In HDA header mode the SAR, 4BTN_IL, and IL_IRQ
registers are set up once during I2C probe by
rt5659_intel_hd_header_probe_setup() and are not re-enabled on card
rebind, so clearing them in remove would leave mic detection disabled
after the next bind.
Reset the HDA IRQ polarity bits in RT5659_IRQ_CTRL_1 and
RT5659_IRQ_CTRL_2 to their unplugged defaults before marking the
regmap cache dirty. Without this, regcache_sync() on rebind restores
plugged-in polarity while hda_hp_plugged and hda_mic_plugged are
false, so the polarity-flip logic in
rt5659_jack_detect_intel_hd_header() never fires and subsequent
plug/unplug events do not generate an interrupt.
If the I2C device unbinds without a card bound, rt5659_remove() is
never called and the IRQ keeps queuing jack_detect_work until devres
frees the private data. Register a devm action to disable the IRQ
and cancel the delayed work on any I2C teardown path. Guard
disable_irq() in rt5659_i2c_shutdown() with the irq_disabled flag
to avoid an unbalanced disable when rt5659_remove() already ran.
Store the I2C client pointer in rt5659_priv so all paths can
reference it directly.
Fixes: d3cb2de2479b ("ASoC: rt5659: add rt5659 codec driver")
Cc: stable@vger.kernel.org
Signed-off-by: Tristan Madani <tristan@talencesecurity.com>
---
Changes in v2:
- Only call rt5659_headset_detect() for JD3-mode jacks; HDA header
mode registers are set during I2C probe and not re-enabled on rebind
- Reset HDA IRQ polarity bits to unplugged defaults before
regcache_mark_dirty() to prevent stale polarity on rebind
sound/soc/codecs/rt5659.c | 47 ++++++++++++++++++++++++++++++++++++---
sound/soc/codecs/rt5659.h | 1 +
2 files changed, 45 insertions(+), 3 deletions(-)
diff --git a/sound/soc/codecs/rt5659.c b/sound/soc/codecs/rt5659.c
index 68f7d5673e190..b28804fa15806 100644
--- a/sound/soc/codecs/rt5659.c
+++ b/sound/soc/codecs/rt5659.c
@@ -3660,6 +3660,16 @@ static int rt5659_set_bias_level(struct snd_soc_component *component,
return 0;
}
+static void rt5659_i2c_disable_irq_and_cancel_work(void *data)
+{
+ struct rt5659_priv *rt5659 = data;
+
+ if (rt5659->irq_acquired && !rt5659->irq_disabled)
+ disable_irq(rt5659->i2c->irq);
+ if (rt5659->jack_detect_work_inited)
+ cancel_delayed_work_sync(&rt5659->jack_detect_work);
+}
+
static int rt5659_probe(struct snd_soc_component *component)
{
struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
@@ -3667,8 +3677,10 @@ static int rt5659_probe(struct snd_soc_component *component)
rt5659->component = component;
+ regcache_sync(rt5659->regmap);
+
if (rt5659->irq_disabled) {
- enable_irq(to_i2c_client(component->dev)->irq);
+ enable_irq(rt5659->i2c->irq);
rt5659->irq_disabled = false;
}
@@ -3689,17 +3701,34 @@ 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);
+ disable_irq(rt5659->i2c->irq);
rt5659->irq_disabled = true;
}
if (rt5659->jack_detect_work_inited)
cancel_delayed_work_sync(&rt5659->jack_detect_work);
+ if (rt5659->jack_type && rt5659->pdata.jd_src == RT5659_JD3)
+ rt5659_headset_detect(component, 0);
+
+ rt5659->hs_jack = NULL;
+ rt5659->jack_type = 0;
+ rt5659->hda_hp_plugged = false;
+ rt5659->hda_mic_plugged = false;
+
+ if (rt5659->pdata.jd_src == RT5659_JD_HDA_HEADER) {
+ regmap_update_bits(rt5659->regmap, RT5659_IRQ_CTRL_1,
+ 0x10, 0x10);
+ regmap_update_bits(rt5659->regmap, RT5659_IRQ_CTRL_2,
+ 0x2, 0x0);
+ }
+
+ rt5659->component = NULL;
+
regmap_write(rt5659->regmap, RT5659_RESET, 0);
+ regcache_mark_dirty(rt5659->regmap);
}
#ifdef CONFIG_PM
@@ -4328,6 +4357,13 @@ static int rt5659_i2c_probe(struct i2c_client *i2c)
RT5659_GP1_PIN_MASK, RT5659_GP1_PIN_IRQ);
}
+ rt5659->i2c = i2c;
+ ret = devm_add_action_or_reset(&i2c->dev,
+ rt5659_i2c_disable_irq_and_cancel_work,
+ rt5659);
+ if (ret)
+ return ret;
+
return devm_snd_soc_register_component(&i2c->dev,
&soc_component_dev_rt5659,
rt5659_dai, ARRAY_SIZE(rt5659_dai));
@@ -4337,6 +4373,11 @@ static void rt5659_i2c_shutdown(struct i2c_client *client)
{
struct rt5659_priv *rt5659 = i2c_get_clientdata(client);
+ if (rt5659->irq_acquired && !rt5659->irq_disabled)
+ disable_irq(client->irq);
+ if (rt5659->jack_detect_work_inited)
+ cancel_delayed_work_sync(&rt5659->jack_detect_work);
+
regmap_write(rt5659->regmap, RT5659_RESET, 0);
}
diff --git a/sound/soc/codecs/rt5659.h b/sound/soc/codecs/rt5659.h
index e10b3844501b9..1118e6ab1887d 100644
--- a/sound/soc/codecs/rt5659.h
+++ b/sound/soc/codecs/rt5659.h
@@ -1793,6 +1793,7 @@ struct rt5659_priv {
struct snd_soc_component *component;
struct rt5659_platform_data pdata;
struct regmap *regmap;
+ struct i2c_client *i2c;
struct gpio_desc *gpiod_ldo1_en;
struct gpio_desc *gpiod_reset;
struct snd_soc_jack *hs_jack;
--
2.47.3
next reply other threads:[~2026-11-07 17:18 UTC|newest]
Thread overview: 2+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-11-07 17:18 Tristan Madani [this message]
2026-11-07 17:44 ` [PATCH v2] ASoC: rt5659: fix teardown order and stale state across card rebind sashiko-bot
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20261107171813.2561973-1-tristmd@gmail.com \
--to=tristmd@gmail.com \
--cc=bardliao@realtek.com \
--cc=broonie@kernel.org \
--cc=lgirdwood@gmail.com \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-sound@vger.kernel.org \
--cc=oder_chiou@realtek.com \
--cc=perex@perex.cz \
--cc=stable@vger.kernel.org \
--cc=tiwai@suse.com \
--cc=tristan@talencesecurity.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.