[PATCH] ASoC: mediatek: mt2701: join the IRQ before managed cleanup
Myeonghun Pak
mhun512 at gmail.com
Thu Oct 15 20:40:45 PDT 2026
The IRQ is published before the handler's arrays, regmap and clock state
are ready. Remove drops the PM reference before managed IRQ release, so
a handler may also overlap clock shutdown or subsequent resource
cleanup.
Initialize the dependencies and acquire the PM reference before
requesting the IRQ. Register a PM cleanup action before the IRQ
resource, and a final IRQ-disable action after component registration.
Reverse release then joins the IRQ, removes the components, frees the
IRQ and drops PM before releasing the backing arrays.
Disable and join the IRQ explicitly when component registration fails.
Use pm_runtime_resume_and_get() so a failed resume does not retain a new
usage reference. If manual clock resume succeeded while runtime PM
stayed disabled, unwind those clocks if the later PM get fails.
This issue was identified as part of our ongoing research while
reviewing kernel code.
Fixes: 43a6a7e71063 ("ASoC: mediatek: add mt2701 platform driver implementation.")
Assisted-by: LLM
Co-developed-by: Ijae Kim <ae878000 at gmail.com>
Signed-off-by: Ijae Kim <ae878000 at gmail.com>
Signed-off-by: Myeonghun Pak <mhun512 at gmail.com>
---
The final devres action disables the IRQ rather than looking up and
freeing another managed resource during devres release. Independent
PCM/card lifetime and system-suspend clock ordering are outside this
change.
Compiled with Clang 22.1.2 and W=1 in the accompanying validation
bundle. The prior G154 diff received independent source review. The
revised manual-resume failure cleanup received coordinator
counterexample review. No runtime reproducer, hardware test or
full-kernel link was performed.
Base: 0fcb05b72344f40806cbe810de8872f1698bf053
sound/soc/mediatek/mt2701/mt2701-afe-common.h | 1 +
sound/soc/mediatek/mt2701/mt2701-afe-pcm.c | 65 ++++++++++++-------
2 files changed, 44 insertions(+), 22 deletions(-)
diff --git a/sound/soc/mediatek/mt2701/mt2701-afe-common.h b/sound/soc/mediatek/mt2701/mt2701-afe-common.h
index c9477bc24ee9..c7ffae5b6fb0 100644
--- a/sound/soc/mediatek/mt2701/mt2701-afe-common.h
+++ b/sound/soc/mediatek/mt2701/mt2701-afe-common.h
@@ -89,6 +89,7 @@ struct mt2701_soc_variants {
};
struct mt2701_afe_private {
+ int irq_id;
struct clk *base_ck[MT2701_BASE_CLK_NUM];
struct clk *mrgif_ck;
struct clk *hadds2pll_ck;
diff --git a/sound/soc/mediatek/mt2701/mt2701-afe-pcm.c b/sound/soc/mediatek/mt2701/mt2701-afe-pcm.c
index d56b498e8c0c..ef051157a0c7 100644
--- a/sound/soc/mediatek/mt2701/mt2701-afe-pcm.c
+++ b/sound/soc/mediatek/mt2701/mt2701-afe-pcm.c
@@ -1591,12 +1591,31 @@ static int mt2701_afe_runtime_resume(struct device *dev)
return mt2701_afe_enable_clock(afe);
}
+static void mt2701_afe_pm_disable(void *data)
+{
+ struct device *dev = data;
+
+ pm_runtime_put_sync(dev);
+ pm_runtime_disable(dev);
+ if (!pm_runtime_status_suspended(dev))
+ mt2701_afe_runtime_suspend(dev);
+}
+
+static void mt2701_afe_irq_disable(void *data)
+{
+ struct mtk_base_afe *afe = data;
+ struct mt2701_afe_private *afe_priv = afe->platform_priv;
+
+ disable_irq(afe_priv->irq_id);
+}
+
static int mt2701_afe_pcm_dev_probe(struct platform_device *pdev)
{
const struct mt2701_soc_variants *soc;
struct mtk_base_afe *afe;
struct mt2701_afe_private *afe_priv;
struct device *dev;
+ bool manual_resume = false;
int i, irq_id, ret;
afe = devm_kzalloc(&pdev->dev, sizeof(*afe), GFP_KERNEL);
@@ -1619,13 +1638,7 @@ static int mt2701_afe_pcm_dev_probe(struct platform_device *pdev)
irq_id = platform_get_irq_byname(pdev, "asys");
if (irq_id < 0)
return irq_id;
-
- ret = devm_request_irq(dev, irq_id, mt2701_asys_isr,
- IRQF_TRIGGER_NONE, "asys-isr", (void *)afe);
- if (ret) {
- dev_err(dev, "could not request_irq for asys-isr\n");
- return ret;
- }
+ afe_priv->irq_id = irq_id;
afe->regmap = syscon_node_to_regmap(dev->parent->of_node);
if (IS_ERR(afe->regmap)) {
@@ -1687,14 +1700,27 @@ static int mt2701_afe_pcm_dev_probe(struct platform_device *pdev)
ret = mt2701_afe_runtime_resume(dev);
if (ret)
goto err_pm_disable;
+ manual_resume = true;
}
- pm_runtime_get_sync(dev);
+ ret = pm_runtime_resume_and_get(dev);
+ if (ret)
+ goto err_pm_disable;
+
+ /* Keep the AFE powered until the components and IRQ are released. */
+ ret = devm_add_action_or_reset(dev, mt2701_afe_pm_disable, dev);
+ if (ret)
+ return ret;
+
+ ret = devm_request_irq(dev, irq_id, mt2701_asys_isr,
+ IRQF_TRIGGER_NONE, "asys-isr", afe);
+ if (ret)
+ return ret;
ret = devm_snd_soc_register_component(&pdev->dev, &mtk_afe_pcm_platform,
NULL, 0);
if (ret) {
dev_warn(dev, "err_platform\n");
- goto err_platform;
+ goto err_irq_disable;
}
ret = devm_snd_soc_register_component(&pdev->dev,
@@ -1703,27 +1729,23 @@ static int mt2701_afe_pcm_dev_probe(struct platform_device *pdev)
ARRAY_SIZE(mt2701_afe_pcm_dais));
if (ret) {
dev_warn(dev, "err_dai_component\n");
- goto err_platform;
+ goto err_irq_disable;
}
- return 0;
+ /* Drain the IRQ before releasing the managed components. */
+ return devm_add_action_or_reset(dev, mt2701_afe_irq_disable, afe);
-err_platform:
- pm_runtime_put_sync(dev);
+err_irq_disable:
+ disable_irq(irq_id);
+ return ret;
err_pm_disable:
pm_runtime_disable(dev);
+ if (manual_resume)
+ mt2701_afe_runtime_suspend(dev);
return ret;
}
-static void mt2701_afe_pcm_dev_remove(struct platform_device *pdev)
-{
- pm_runtime_put_sync(&pdev->dev);
- pm_runtime_disable(&pdev->dev);
- if (!pm_runtime_status_suspended(&pdev->dev))
- mt2701_afe_runtime_suspend(&pdev->dev);
-}
-
static const struct mt2701_soc_variants mt2701_soc_v1 = {
.i2s_num = 4,
};
@@ -1752,7 +1774,6 @@ static struct platform_driver mt2701_afe_pcm_driver = {
.pm = pm_ptr(&mt2701_afe_pm_ops),
},
.probe = mt2701_afe_pcm_dev_probe,
- .remove = mt2701_afe_pcm_dev_remove,
};
module_platform_driver(mt2701_afe_pcm_driver);
More information about the Linux-mediatek
mailing list