From: Tristan Madani The cleanup in rt5668_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. Additionally, headset detection registers from a previous session remain cached after the hardware reset. regcache_sync() on rebind restores this stale state, which can leave the SAR source on button mode. Clear the headset state by calling rt5668_headset_detect(0) before the codec reset. If the I2C device unbinds without a card bound, rt5668_remove() is never called and the IRQ keeps queuing jack_detect_work and jd_check_work until devres frees the private data. Register a devm action to disable the IRQ, set irq_disabled, and cancel both delayed works on any I2C teardown path. Consolidate the IRQ disable and work cancellation into the devm helper and call it from rt5668_remove() and rt5668_i2c_shutdown() as well, so every path goes through the same sequence and irq_disabled stays consistent. This prevents an unbalanced disable_irq() when multiple teardown paths run in succession. Store the I2C client pointer in rt5668_priv so all paths can reference it directly. Fixes: d59fb2856223 ("ASoC: rt5668: add rt5668B codec driver") Cc: stable@vger.kernel.org Signed-off-by: Tristan Madani --- Changes in v3: - Set irq_disabled in the devm teardown helper and call it from rt5668_remove() and rt5668_i2c_shutdown() to prevent double disable_irq() when multiple teardown paths run Changes in v2: - Clear headset detection state via rt5668_headset_detect(0) before codec reset to prevent stale SAR/button state on rebind - Guard disable_irq() in rt5668_i2c_shutdown() with irq_disabled flag to avoid unbalanced disable sound/soc/codecs/rt5668.c | 41 +++++++++++++++++++++++++++++++-------- 1 file changed, 33 insertions(+), 8 deletions(-) diff --git a/sound/soc/codecs/rt5668.c b/sound/soc/codecs/rt5668.c index 29d70802a046a..5bfe5d9546925 100644 --- a/sound/soc/codecs/rt5668.c +++ b/sound/soc/codecs/rt5668.c @@ -45,6 +45,7 @@ struct rt5668_priv { struct rt5668_platform_data pdata; struct gpio_desc *ldo1_en; struct regmap *regmap; + struct i2c_client *i2c; struct snd_soc_jack *hs_jack; struct regulator_bulk_data supplies[RT5668_NUM_SUPPLIES]; struct delayed_work jack_detect_work; @@ -2213,14 +2214,28 @@ static int rt5668_set_bias_level(struct snd_soc_component *component, return 0; } +static void rt5668_i2c_disable_irq_and_cancel_work(void *data) +{ + struct rt5668_priv *rt5668 = data; + + if (rt5668->irq_acquired && !rt5668->irq_disabled) { + disable_irq(rt5668->i2c->irq); + rt5668->irq_disabled = true; + } + cancel_delayed_work_sync(&rt5668->jack_detect_work); + cancel_delayed_work_sync(&rt5668->jd_check_work); +} + static int rt5668_probe(struct snd_soc_component *component) { struct rt5668_priv *rt5668 = snd_soc_component_get_drvdata(component); rt5668->component = component; + regcache_sync(rt5668->regmap); + if (rt5668->irq_disabled) { - enable_irq(to_i2c_client(component->dev)->irq); + enable_irq(rt5668->i2c->irq); rt5668->irq_disabled = false; } @@ -2230,17 +2245,18 @@ 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; - } + rt5668_i2c_disable_irq_and_cancel_work(rt5668); - cancel_delayed_work_sync(&rt5668->jack_detect_work); - cancel_delayed_work_sync(&rt5668->jd_check_work); + if (rt5668->jack_type) + rt5668_headset_detect(component, 0); + + rt5668->hs_jack = NULL; + rt5668->jack_type = 0; + rt5668->component = NULL; rt5668_reset(rt5668->regmap); + regcache_mark_dirty(rt5668->regmap); } #ifdef CONFIG_PM @@ -2576,6 +2592,13 @@ static int rt5668_i2c_probe(struct i2c_client *i2c) rt5668->irq_acquired = true; } + rt5668->i2c = i2c; + ret = devm_add_action_or_reset(&i2c->dev, + rt5668_i2c_disable_irq_and_cancel_work, + rt5668); + if (ret) + return ret; + return devm_snd_soc_register_component(&i2c->dev, &soc_component_dev_rt5668, rt5668_dai, ARRAY_SIZE(rt5668_dai)); } @@ -2584,6 +2607,8 @@ static void rt5668_i2c_shutdown(struct i2c_client *client) { struct rt5668_priv *rt5668 = i2c_get_clientdata(client); + rt5668_i2c_disable_irq_and_cancel_work(rt5668); + rt5668_reset(rt5668->regmap); } -- 2.47.3