From 638ca7601f41a3f8368811f3185b06e2547b7a0f Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Wed, 3 Sep 2025 10:45:44 +0100 Subject: [PATCH 1/4] ASoC: cs42l43: Rename system suspend callback and fix debug print There is some confusion around cs42l43_codec_runtime_force_suspend(). This function is the system suspend callback, however the name and the debug print both use the words runtime. Rename the function to the simpler cs42l43_codec_suspend() and correct the debug print. Signed-off-by: Charles Keepax Message-ID: <20250903094549.271068-2-ckeepax@opensource.cirrus.com> Signed-off-by: Mark Brown --- sound/soc/codecs/cs42l43.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/sound/soc/codecs/cs42l43.c b/sound/soc/codecs/cs42l43.c index b0c27d696c58..4d1779941581 100644 --- a/sound/soc/codecs/cs42l43.c +++ b/sound/soc/codecs/cs42l43.c @@ -2386,11 +2386,11 @@ static int cs42l43_codec_runtime_resume(struct device *dev) return 0; } -static int cs42l43_codec_runtime_force_suspend(struct device *dev) +static int cs42l43_codec_suspend(struct device *dev) { struct cs42l43_codec *priv = dev_get_drvdata(dev); - dev_dbg(priv->dev, "Runtime suspend\n"); + dev_dbg(priv->dev, "System suspend\n"); priv->suspend_jack_debounce = true; @@ -2401,7 +2401,7 @@ static int cs42l43_codec_runtime_force_suspend(struct device *dev) static const struct dev_pm_ops cs42l43_codec_pm_ops = { RUNTIME_PM_OPS(NULL, cs42l43_codec_runtime_resume, NULL) - SYSTEM_SLEEP_PM_OPS(cs42l43_codec_runtime_force_suspend, pm_runtime_force_resume) + SYSTEM_SLEEP_PM_OPS(cs42l43_codec_suspend, pm_runtime_force_resume) }; static const struct platform_device_id cs42l43_codec_id_table[] = { From 149dda5f42a8fa6dacf2cff1d16952de28622d30 Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Wed, 3 Sep 2025 10:45:45 +0100 Subject: [PATCH 2/4] ASoC: cs42l43: Store IRQ domain in codec private data To support future refactoring store a pointer to the IRQ domain in the codec private data allowing easier access to it outside of probe. Signed-off-by: Charles Keepax Message-ID: <20250903094549.271068-3-ckeepax@opensource.cirrus.com> Signed-off-by: Mark Brown --- sound/soc/codecs/cs42l43.c | 29 +++++++++++++---------------- sound/soc/codecs/cs42l43.h | 1 + 2 files changed, 14 insertions(+), 16 deletions(-) diff --git a/sound/soc/codecs/cs42l43.c b/sound/soc/codecs/cs42l43.c index 4d1779941581..241f7d013189 100644 --- a/sound/soc/codecs/cs42l43.c +++ b/sound/soc/codecs/cs42l43.c @@ -2210,13 +2210,12 @@ static const struct cs42l43_irq cs42l43_irqs[] = { }; static int cs42l43_request_irq(struct cs42l43_codec *priv, - struct irq_domain *dom, const char * const name, - unsigned int irq, irq_handler_t handler, - unsigned long flags) + const char * const name, unsigned int irq, + irq_handler_t handler, unsigned long flags) { int ret; - ret = irq_create_mapping(dom, irq); + ret = irq_create_mapping(priv->dom, irq); if (ret < 0) return dev_err_probe(priv->dev, ret, "Failed to map IRQ %s\n", name); @@ -2230,8 +2229,7 @@ static int cs42l43_request_irq(struct cs42l43_codec *priv, return 0; } -static int cs42l43_shutter_irq(struct cs42l43_codec *priv, - struct irq_domain *dom, unsigned int shutter, +static int cs42l43_shutter_irq(struct cs42l43_codec *priv, unsigned int shutter, const char * const open_name, const char * const close_name, irq_handler_t handler) @@ -2259,25 +2257,20 @@ static int cs42l43_shutter_irq(struct cs42l43_codec *priv, return 0; } - ret = cs42l43_request_irq(priv, dom, close_name, close_irq, handler, IRQF_SHARED); + ret = cs42l43_request_irq(priv, close_name, close_irq, handler, IRQF_SHARED); if (ret) return ret; - return cs42l43_request_irq(priv, dom, open_name, open_irq, handler, IRQF_SHARED); + return cs42l43_request_irq(priv, open_name, open_irq, handler, IRQF_SHARED); } static int cs42l43_codec_probe(struct platform_device *pdev) { struct cs42l43 *cs42l43 = dev_get_drvdata(pdev->dev.parent); struct cs42l43_codec *priv; - struct irq_domain *dom; unsigned int val; int i, ret; - dom = irq_find_matching_fwnode(dev_fwnode(cs42l43->dev), DOMAIN_BUS_ANY); - if (!dom) - return -EPROBE_DEFER; - priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); if (!priv) return -ENOMEM; @@ -2285,6 +2278,10 @@ static int cs42l43_codec_probe(struct platform_device *pdev) priv->dev = &pdev->dev; priv->core = cs42l43; + priv->dom = irq_find_matching_fwnode(dev_fwnode(cs42l43->dev), DOMAIN_BUS_ANY); + if (!priv->dom) + return -EPROBE_DEFER; + platform_set_drvdata(pdev, priv); mutex_init(&priv->jack_lock); @@ -2314,7 +2311,7 @@ static int cs42l43_codec_probe(struct platform_device *pdev) goto err_pm; for (i = 0; i < ARRAY_SIZE(cs42l43_irqs); i++) { - ret = cs42l43_request_irq(priv, dom, cs42l43_irqs[i].name, + ret = cs42l43_request_irq(priv, cs42l43_irqs[i].name, cs42l43_irqs[i].irq, cs42l43_irqs[i].handler, 0); if (ret) @@ -2327,13 +2324,13 @@ static int cs42l43_codec_probe(struct platform_device *pdev) goto err_pm; } - ret = cs42l43_shutter_irq(priv, dom, val & CS42L43_MIC_SHUTTER_CFG_MASK, + ret = cs42l43_shutter_irq(priv, val & CS42L43_MIC_SHUTTER_CFG_MASK, "mic shutter open", "mic shutter close", cs42l43_mic_shutter); if (ret) goto err_pm; - ret = cs42l43_shutter_irq(priv, dom, (val & CS42L43_SPK_SHUTTER_CFG_MASK) >> + ret = cs42l43_shutter_irq(priv, (val & CS42L43_SPK_SHUTTER_CFG_MASK) >> CS42L43_SPK_SHUTTER_CFG_SHIFT, "spk shutter open", "spk shutter close", cs42l43_spk_shutter); diff --git a/sound/soc/codecs/cs42l43.h b/sound/soc/codecs/cs42l43.h index 3ea36362b11a..f4ef93d1fc2a 100644 --- a/sound/soc/codecs/cs42l43.h +++ b/sound/soc/codecs/cs42l43.h @@ -44,6 +44,7 @@ struct cs42l43_codec { struct device *dev; struct cs42l43 *core; struct snd_soc_component *component; + struct irq_domain *dom; struct clk *mclk; From a69b4ba19a07896e7e4246446bad002f5fc0dae1 Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Wed, 3 Sep 2025 10:45:46 +0100 Subject: [PATCH 3/4] ASoC: cs42l43: Disable IRQs in system suspend Currently the MFD driver disables all the IRQs upon entering system suspend, however there are some issues with this approach. As this device uses runtime force suspend. The regmap IRQ handler can run, claim a PM runtime reference and get scheduled, the MFD can then force suspend. When the IRQ thread gets rescheduled it will try to access volatile registers on the suspended device. Furthermore, this race also applies to work queue items scheduled by the IRQ handlers. As the MFD code doesn't know about the individual work queue items, the end drivers must mask their own IRQs and sync in any work queues as part of entering system suspend. Update the code here to do so. Signed-off-by: Charles Keepax Message-ID: <20250903094549.271068-4-ckeepax@opensource.cirrus.com> Signed-off-by: Mark Brown --- sound/soc/codecs/cs42l43.c | 78 ++++++++++++++++++++++++++++++-------- sound/soc/codecs/cs42l43.h | 1 + 2 files changed, 64 insertions(+), 15 deletions(-) diff --git a/sound/soc/codecs/cs42l43.c b/sound/soc/codecs/cs42l43.c index 241f7d013189..405926149a13 100644 --- a/sound/soc/codecs/cs42l43.c +++ b/sound/soc/codecs/cs42l43.c @@ -2229,12 +2229,29 @@ static int cs42l43_request_irq(struct cs42l43_codec *priv, return 0; } +static void cs42l43_disable_irq(struct cs42l43_codec *priv, unsigned int irq) +{ + int ret; + + ret = irq_find_mapping(priv->dom, irq); + if (ret > 0) + disable_irq(ret); +} + +static void cs42l43_enable_irq(struct cs42l43_codec *priv, unsigned int irq) +{ + int ret; + + ret = irq_find_mapping(priv->dom, irq); + if (ret > 0) + enable_irq(ret); +} + static int cs42l43_shutter_irq(struct cs42l43_codec *priv, unsigned int shutter, - const char * const open_name, - const char * const close_name, + const char * const open_name, unsigned int *open_irq, + const char * const close_name, unsigned int *close_irq, irq_handler_t handler) { - unsigned int open_irq, close_irq; int ret; switch (shutter) { @@ -2242,26 +2259,26 @@ static int cs42l43_shutter_irq(struct cs42l43_codec *priv, unsigned int shutter, dev_warn(priv->dev, "Manual shutters, notifications not available\n"); return 0; case 0x2: - open_irq = CS42L43_GPIO1_RISE; - close_irq = CS42L43_GPIO1_FALL; + *open_irq = CS42L43_GPIO1_RISE; + *close_irq = CS42L43_GPIO1_FALL; break; case 0x4: - open_irq = CS42L43_GPIO2_RISE; - close_irq = CS42L43_GPIO2_FALL; + *open_irq = CS42L43_GPIO2_RISE; + *close_irq = CS42L43_GPIO2_FALL; break; case 0x8: - open_irq = CS42L43_GPIO3_RISE; - close_irq = CS42L43_GPIO3_FALL; + *open_irq = CS42L43_GPIO3_RISE; + *close_irq = CS42L43_GPIO3_FALL; break; default: return 0; } - ret = cs42l43_request_irq(priv, close_name, close_irq, handler, IRQF_SHARED); + ret = cs42l43_request_irq(priv, close_name, *close_irq, handler, IRQF_SHARED); if (ret) return ret; - return cs42l43_request_irq(priv, open_name, open_irq, handler, IRQF_SHARED); + return cs42l43_request_irq(priv, open_name, *open_irq, handler, IRQF_SHARED); } static int cs42l43_codec_probe(struct platform_device *pdev) @@ -2325,14 +2342,16 @@ static int cs42l43_codec_probe(struct platform_device *pdev) } ret = cs42l43_shutter_irq(priv, val & CS42L43_MIC_SHUTTER_CFG_MASK, - "mic shutter open", "mic shutter close", + "mic shutter open", &priv->shutter_irqs[0], + "mic shutter close", &priv->shutter_irqs[1], cs42l43_mic_shutter); if (ret) goto err_pm; ret = cs42l43_shutter_irq(priv, (val & CS42L43_SPK_SHUTTER_CFG_MASK) >> CS42L43_SPK_SHUTTER_CFG_SHIFT, - "spk shutter open", "spk shutter close", + "spk shutter open", &priv->shutter_irqs[2], + "spk shutter close", &priv->shutter_irqs[3], cs42l43_spk_shutter); if (ret) goto err_pm; @@ -2386,19 +2405,48 @@ static int cs42l43_codec_runtime_resume(struct device *dev) static int cs42l43_codec_suspend(struct device *dev) { struct cs42l43_codec *priv = dev_get_drvdata(dev); + int i; dev_dbg(priv->dev, "System suspend\n"); priv->suspend_jack_debounce = true; - pm_runtime_force_suspend(dev); + for (i = 0; i < ARRAY_SIZE(cs42l43_irqs); i++) + cs42l43_disable_irq(priv, cs42l43_irqs[i].irq); + + for (i = 0; i < ARRAY_SIZE(priv->shutter_irqs); i++) + if (priv->shutter_irqs[i]) + cs42l43_disable_irq(priv, priv->shutter_irqs[i]); + + cancel_delayed_work_sync(&priv->bias_sense_timeout); + cancel_delayed_work_sync(&priv->tip_sense_work); + cancel_delayed_work_sync(&priv->hp_ilimit_clear_work); + + return pm_runtime_force_suspend(dev); +} + +static int cs42l43_codec_resume(struct device *dev) +{ + struct cs42l43_codec *priv = dev_get_drvdata(dev); + int ret, i; + + ret = pm_runtime_force_resume(dev); + if (ret) + return ret; + + for (i = 0; i < ARRAY_SIZE(cs42l43_irqs); i++) + cs42l43_enable_irq(priv, cs42l43_irqs[i].irq); + + for (i = 0; i < ARRAY_SIZE(priv->shutter_irqs); i++) + if (priv->shutter_irqs[i]) + cs42l43_enable_irq(priv, priv->shutter_irqs[i]); return 0; } static const struct dev_pm_ops cs42l43_codec_pm_ops = { RUNTIME_PM_OPS(NULL, cs42l43_codec_runtime_resume, NULL) - SYSTEM_SLEEP_PM_OPS(cs42l43_codec_suspend, pm_runtime_force_resume) + SYSTEM_SLEEP_PM_OPS(cs42l43_codec_suspend, cs42l43_codec_resume) }; static const struct platform_device_id cs42l43_codec_id_table[] = { diff --git a/sound/soc/codecs/cs42l43.h b/sound/soc/codecs/cs42l43.h index f4ef93d1fc2a..0951ad3525ef 100644 --- a/sound/soc/codecs/cs42l43.h +++ b/sound/soc/codecs/cs42l43.h @@ -45,6 +45,7 @@ struct cs42l43_codec { struct cs42l43 *core; struct snd_soc_component *component; struct irq_domain *dom; + unsigned int shutter_irqs[4]; struct clk *mclk; From dd7ae5b8b3c291c0206f127a564ae1e316705ca0 Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Wed, 3 Sep 2025 10:45:47 +0100 Subject: [PATCH 4/4] ASoC: cs42l43: Shutdown jack detection on suspend Fully power down the jack detection on system suspend since the device will not be powered up during. Signed-off-by: Charles Keepax Message-ID: <20250903094549.271068-5-ckeepax@opensource.cirrus.com> Signed-off-by: Mark Brown --- sound/soc/codecs/cs42l43-jack.c | 8 +++++--- sound/soc/codecs/cs42l43.c | 2 ++ sound/soc/codecs/cs42l43.h | 1 + 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/sound/soc/codecs/cs42l43-jack.c b/sound/soc/codecs/cs42l43-jack.c index 2a0a4986a9ce..867e23d4fb8d 100644 --- a/sound/soc/codecs/cs42l43-jack.c +++ b/sound/soc/codecs/cs42l43-jack.c @@ -684,7 +684,7 @@ static int cs42l43_run_type_detect(struct cs42l43_codec *priv) } } -static void cs42l43_clear_jack(struct cs42l43_codec *priv) +void cs42l43_clear_jack(struct cs42l43_codec *priv) { struct cs42l43 *cs42l43 = priv->core; @@ -703,8 +703,6 @@ static void cs42l43_clear_jack(struct cs42l43_codec *priv) regmap_update_bits(cs42l43->regmap, CS42L43_HS2, CS42L43_HSDET_MODE_MASK | CS42L43_HSDET_MANUAL_MODE_MASK, 0x2 << CS42L43_HSDET_MODE_SHIFT); - - snd_soc_jack_report(priv->jack_hp, 0, 0xFFFF); } void cs42l43_tip_sense_work(struct work_struct *work) @@ -753,6 +751,8 @@ void cs42l43_tip_sense_work(struct work_struct *work) cs42l43_clear_jack(priv); + snd_soc_jack_report(priv->jack_hp, 0, 0xFFFF); + if (cs42l43->sdw && priv->jack_present) { pm_runtime_put(priv->dev); priv->jack_present = false; @@ -903,6 +903,8 @@ int cs42l43_jack_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *u cs42l43_clear_jack(priv); + snd_soc_jack_report(priv->jack_hp, 0, 0xFFFF); + if (!override) { queue_delayed_work(system_long_wq, &priv->tip_sense_work, 0); } else { diff --git a/sound/soc/codecs/cs42l43.c b/sound/soc/codecs/cs42l43.c index 405926149a13..b61df09f20cf 100644 --- a/sound/soc/codecs/cs42l43.c +++ b/sound/soc/codecs/cs42l43.c @@ -2422,6 +2422,8 @@ static int cs42l43_codec_suspend(struct device *dev) cancel_delayed_work_sync(&priv->tip_sense_work); cancel_delayed_work_sync(&priv->hp_ilimit_clear_work); + cs42l43_clear_jack(priv); + return pm_runtime_force_suspend(dev); } diff --git a/sound/soc/codecs/cs42l43.h b/sound/soc/codecs/cs42l43.h index 0951ad3525ef..b2fa2cd1d99f 100644 --- a/sound/soc/codecs/cs42l43.h +++ b/sound/soc/codecs/cs42l43.h @@ -132,6 +132,7 @@ static inline int cs42l43_sdw_add_peripheral(struct snd_pcm_substream *substream int cs42l43_set_jack(struct snd_soc_component *component, struct snd_soc_jack *jack, void *d); void cs42l43_bias_sense_timeout(struct work_struct *work); +void cs42l43_clear_jack(struct cs42l43_codec *priv); void cs42l43_tip_sense_work(struct work_struct *work); irqreturn_t cs42l43_bias_detect_clamp(int irq, void *data); irqreturn_t cs42l43_button_press(int irq, void *data);