From b9b23e72abef91ab4689f1467cefc2517042ab26 Mon Sep 17 00:00:00 2001 From: Ajay Kumar Nandam Date: Tue, 7 Jul 2026 15:13:11 +0530 Subject: [PATCH 1/3] ASoC: codecs: lpass-tx-macro: switch to PM clock framework for runtime PM Convert the LPASS TX macro codec driver to runtime PM clock management using the PM clock framework. Replace manual macro/dcodec/mclk/npl/fsgen clock toggling with PM clock helpers and runtime PM callbacks. Keep the SWR gate runtime PM reference from SWR clock enable until disable so autosuspend does not gate clocks while SWR is still prepared. Set autosuspend delay to 100 ms so PM-clock-managed votes are dropped soon after idle while still avoiding suspend/resume churn on short gaps. Add a PM_CLK dependency to SND_SOC_LPASS_TX_MACRO since this patch introduces PM clock APIs. Tighten error unwind by checking pm_runtime_put_sync_suspend() in probe and by restoring regcache state if pm_clk_resume()/regcache_sync() fails. Co-developed-by: Ravi Hothi Signed-off-by: Ravi Hothi Signed-off-by: Ajay Kumar Nandam Link: https://patch.msgid.link/20260707-xo-sd-codec-tx-rx-v2-1-f61b4622f97f@oss.qualcomm.com Signed-off-by: Mark Brown --- sound/soc/codecs/Kconfig | 2 + sound/soc/codecs/lpass-tx-macro.c | 114 ++++++++++++------------------ 2 files changed, 49 insertions(+), 67 deletions(-) diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index 252f683be3c1..160c52258d95 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -2909,12 +2909,14 @@ config SND_SOC_LPASS_VA_MACRO config SND_SOC_LPASS_RX_MACRO depends on COMMON_CLK + depends on PM_CLK select REGMAP_MMIO select SND_SOC_LPASS_MACRO_COMMON tristate "Qualcomm RX Macro in LPASS(Low Power Audio SubSystem)" config SND_SOC_LPASS_TX_MACRO depends on COMMON_CLK + depends on PM_CLK select REGMAP_MMIO select SND_SOC_LPASS_MACRO_COMMON tristate "Qualcomm TX Macro in LPASS(Low Power Audio SubSystem)" diff --git a/sound/soc/codecs/lpass-tx-macro.c b/sound/soc/codecs/lpass-tx-macro.c index f7d168f557dd..fc073a556fb5 100644 --- a/sound/soc/codecs/lpass-tx-macro.c +++ b/sound/soc/codecs/lpass-tx-macro.c @@ -6,6 +6,7 @@ #include #include #include +#include #include #include #include @@ -2149,17 +2150,20 @@ static int swclk_gate_enable(struct clk_hw *hw) struct regmap *regmap = tx->regmap; int ret; - ret = clk_prepare_enable(tx->mclk); + ret = pm_runtime_resume_and_get(tx->dev); + if (ret < 0) + return ret; + + ret = tx_macro_mclk_enable(tx, true); if (ret) { - dev_err(tx->dev, "failed to enable mclk\n"); + pm_runtime_put_autosuspend(tx->dev); return ret; } - tx_macro_mclk_enable(tx, true); - regmap_update_bits(regmap, CDC_TX_CLK_RST_CTRL_SWR_CONTROL, CDC_TX_SWR_CLK_EN_MASK, CDC_TX_SWR_CLK_ENABLE); + return 0; } @@ -2172,7 +2176,7 @@ static void swclk_gate_disable(struct clk_hw *hw) CDC_TX_SWR_CLK_EN_MASK, 0x0); tx_macro_mclk_enable(tx, false); - clk_disable_unprepare(tx->mclk); + pm_runtime_put_autosuspend(tx->dev); } static int swclk_gate_is_enabled(struct clk_hw *hw) @@ -2318,25 +2322,23 @@ static int tx_macro_probe(struct platform_device *pdev) clk_set_rate(tx->mclk, MCLK_FREQ); clk_set_rate(tx->npl, MCLK_FREQ); - ret = clk_prepare_enable(tx->macro); + ret = devm_pm_clk_create(dev); if (ret) goto err; - ret = clk_prepare_enable(tx->dcodec); - if (ret) - goto err_dcodec; + ret = of_pm_clk_add_clks(dev); + if (ret < 0) + goto err; - ret = clk_prepare_enable(tx->mclk); + pm_runtime_set_autosuspend_delay(dev, 100); + pm_runtime_use_autosuspend(dev); + ret = devm_pm_runtime_enable(dev); if (ret) - goto err_mclk; + goto err; - ret = clk_prepare_enable(tx->npl); - if (ret) - goto err_npl; - - ret = clk_prepare_enable(tx->fsgen); - if (ret) - goto err_fsgen; + ret = pm_runtime_resume_and_get(dev); + if (ret < 0) + goto err; /* reset soundwire block */ @@ -2356,30 +2358,21 @@ static int tx_macro_probe(struct platform_device *pdev) tx_macro_dai, ARRAY_SIZE(tx_macro_dai)); if (ret) - goto err_clkout; - - pm_runtime_set_autosuspend_delay(dev, 3000); - pm_runtime_use_autosuspend(dev); - pm_runtime_mark_last_busy(dev); - pm_runtime_set_active(dev); - pm_runtime_enable(dev); + goto err_rpm_put; ret = tx_macro_register_mclk_output(tx); if (ret) - goto err_clkout; + goto err_rpm_put; + + ret = pm_runtime_put_autosuspend(dev); + if (ret < 0) + dev_warn(dev, "runtime PM put failed after probe: %d\n", ret); return 0; -err_clkout: - clk_disable_unprepare(tx->fsgen); -err_fsgen: - clk_disable_unprepare(tx->npl); -err_npl: - clk_disable_unprepare(tx->mclk); -err_mclk: - clk_disable_unprepare(tx->dcodec); -err_dcodec: - clk_disable_unprepare(tx->macro); +err_rpm_put: + if (pm_runtime_put_sync_suspend(dev) < 0) + dev_warn(dev, "runtime PM sync suspend failed in probe unwind\n"); err: lpass_macro_pds_exit(tx->pds); @@ -2390,25 +2383,23 @@ static void tx_macro_remove(struct platform_device *pdev) { struct tx_macro *tx = dev_get_drvdata(&pdev->dev); - clk_disable_unprepare(tx->macro); - clk_disable_unprepare(tx->dcodec); - clk_disable_unprepare(tx->mclk); - clk_disable_unprepare(tx->npl); - clk_disable_unprepare(tx->fsgen); - lpass_macro_pds_exit(tx->pds); } static int tx_macro_runtime_suspend(struct device *dev) { struct tx_macro *tx = dev_get_drvdata(dev); + int ret; regcache_cache_only(tx->regmap, true); - regcache_mark_dirty(tx->regmap); - clk_disable_unprepare(tx->fsgen); - clk_disable_unprepare(tx->npl); - clk_disable_unprepare(tx->mclk); + ret = pm_clk_suspend(dev); + if (ret) { + regcache_cache_only(tx->regmap, false); + return ret; + } + + regcache_mark_dirty(tx->regmap); return 0; } @@ -2418,34 +2409,23 @@ static int tx_macro_runtime_resume(struct device *dev) struct tx_macro *tx = dev_get_drvdata(dev); int ret; - ret = clk_prepare_enable(tx->mclk); + ret = pm_clk_resume(dev); if (ret) { - dev_err(dev, "unable to prepare mclk\n"); + regcache_cache_only(tx->regmap, true); + regcache_mark_dirty(tx->regmap); return ret; } - ret = clk_prepare_enable(tx->npl); - if (ret) { - dev_err(dev, "unable to prepare npl\n"); - goto err_npl; - } - - ret = clk_prepare_enable(tx->fsgen); - if (ret) { - dev_err(dev, "unable to prepare fsgen\n"); - goto err_fsgen; - } - regcache_cache_only(tx->regmap, false); - regcache_sync(tx->regmap); + ret = regcache_sync(tx->regmap); + if (ret) { + regcache_cache_only(tx->regmap, true); + regcache_mark_dirty(tx->regmap); + pm_clk_suspend(dev); + return ret; + } return 0; -err_fsgen: - clk_disable_unprepare(tx->npl); -err_npl: - clk_disable_unprepare(tx->mclk); - - return ret; } static const struct dev_pm_ops tx_macro_pm_ops = { From b05482e7ce1b110f86b08a99768ac41e4c9e4dfa Mon Sep 17 00:00:00 2001 From: Ajay Kumar Nandam Date: Tue, 7 Jul 2026 15:13:12 +0530 Subject: [PATCH 2/3] ASoC: codecs: lpass-rx-macro: switch to PM clock framework for runtime PM Convert the LPASS RX macro codec driver to runtime PM clock management using the PM clock framework. Replace manual macro/dcodec/mclk/npl/fsgen clock toggling with PM clock helpers and runtime PM callbacks. Keep the SWR gate runtime PM reference from SWR clock enable until disable so autosuspend does not gate clocks while SWR is still prepared. Set autosuspend delay to 100 ms so PM-clock-managed votes are dropped soon after idle while still avoiding suspend/resume churn on short gaps. Add a PM_CLK dependency to SND_SOC_LPASS_RX_MACRO since this patch introduces PM clock APIs. Tighten error unwind by checking pm_runtime_put_sync_suspend() in probe and by restoring regcache state if pm_clk_resume()/regcache_sync() fails. Drop the now-empty rx_macro_remove() callback since all clock cleanup is handled by PM clock framework and devm. Co-developed-by: Ravi Hothi Signed-off-by: Ravi Hothi Signed-off-by: Ajay Kumar Nandam Link: https://patch.msgid.link/20260707-xo-sd-codec-tx-rx-v2-2-f61b4622f97f@oss.qualcomm.com Signed-off-by: Mark Brown --- sound/soc/codecs/lpass-rx-macro.c | 113 +++++++++++------------------- 1 file changed, 41 insertions(+), 72 deletions(-) diff --git a/sound/soc/codecs/lpass-rx-macro.c b/sound/soc/codecs/lpass-rx-macro.c index 6233aa9f5bc6..927f75050c0f 100644 --- a/sound/soc/codecs/lpass-rx-macro.c +++ b/sound/soc/codecs/lpass-rx-macro.c @@ -6,6 +6,7 @@ #include #include #include +#include #include #include #include @@ -3671,11 +3672,9 @@ static int swclk_gate_enable(struct clk_hw *hw) struct rx_macro *rx = to_rx_macro(hw); int ret; - ret = clk_prepare_enable(rx->mclk); - if (ret) { - dev_err(rx->dev, "unable to prepare mclk\n"); + ret = pm_runtime_resume_and_get(rx->dev); + if (ret < 0) return ret; - } rx_macro_mclk_enable(rx, true); @@ -3693,7 +3692,7 @@ static void swclk_gate_disable(struct clk_hw *hw) CDC_RX_SWR_CLK_EN_MASK, 0); rx_macro_mclk_enable(rx, false); - clk_disable_unprepare(rx->mclk); + pm_runtime_put_autosuspend(rx->dev); } static int swclk_gate_is_enabled(struct clk_hw *hw) @@ -3867,25 +3866,23 @@ static int rx_macro_probe(struct platform_device *pdev) clk_set_rate(rx->mclk, MCLK_FREQ); clk_set_rate(rx->npl, MCLK_FREQ); - ret = clk_prepare_enable(rx->macro); + ret = devm_pm_clk_create(dev); if (ret) return ret; - ret = clk_prepare_enable(rx->dcodec); - if (ret) - goto err_dcodec; + ret = of_pm_clk_add_clks(dev); + if (ret < 0) + return ret; - ret = clk_prepare_enable(rx->mclk); + pm_runtime_set_autosuspend_delay(dev, 100); + pm_runtime_use_autosuspend(dev); + ret = devm_pm_runtime_enable(dev); if (ret) - goto err_mclk; + return ret; - ret = clk_prepare_enable(rx->npl); + ret = pm_runtime_resume_and_get(dev); if (ret) - goto err_npl; - - ret = clk_prepare_enable(rx->fsgen); - if (ret) - goto err_fsgen; + return ret; /* reset swr block */ regmap_update_bits(rx->regmap, CDC_RX_CLK_RST_CTRL_SWR_CONTROL, @@ -3902,46 +3899,25 @@ static int rx_macro_probe(struct platform_device *pdev) rx_macro_dai, ARRAY_SIZE(rx_macro_dai)); if (ret) - goto err_clkout; - - - pm_runtime_set_autosuspend_delay(dev, 3000); - pm_runtime_use_autosuspend(dev); - pm_runtime_mark_last_busy(dev); - pm_runtime_set_active(dev); - pm_runtime_enable(dev); + goto err_rpm_put; ret = rx_macro_register_mclk_output(rx); if (ret) - goto err_clkout; + goto err_rpm_put; + + ret = pm_runtime_put_autosuspend(dev); + if (ret < 0) + dev_warn(dev, "runtime PM put failed after probe: %d\n", ret); return 0; -err_clkout: - clk_disable_unprepare(rx->fsgen); -err_fsgen: - clk_disable_unprepare(rx->npl); -err_npl: - clk_disable_unprepare(rx->mclk); -err_mclk: - clk_disable_unprepare(rx->dcodec); -err_dcodec: - clk_disable_unprepare(rx->macro); +err_rpm_put: + if (pm_runtime_put_sync_suspend(dev) < 0) + dev_warn(dev, "runtime PM sync suspend failed in probe unwind\n"); return ret; } -static void rx_macro_remove(struct platform_device *pdev) -{ - struct rx_macro *rx = dev_get_drvdata(&pdev->dev); - - clk_disable_unprepare(rx->mclk); - clk_disable_unprepare(rx->npl); - clk_disable_unprepare(rx->fsgen); - clk_disable_unprepare(rx->macro); - clk_disable_unprepare(rx->dcodec); -} - static const struct of_device_id rx_macro_dt_match[] = { { .compatible = "qcom,sc7280-lpass-rx-macro", @@ -3969,13 +3945,17 @@ MODULE_DEVICE_TABLE(of, rx_macro_dt_match); static int rx_macro_runtime_suspend(struct device *dev) { struct rx_macro *rx = dev_get_drvdata(dev); + int ret; regcache_cache_only(rx->regmap, true); - regcache_mark_dirty(rx->regmap); - clk_disable_unprepare(rx->fsgen); - clk_disable_unprepare(rx->npl); - clk_disable_unprepare(rx->mclk); + ret = pm_clk_suspend(dev); + if (ret) { + regcache_cache_only(rx->regmap, false); + return ret; + } + + regcache_mark_dirty(rx->regmap); return 0; } @@ -3985,33 +3965,23 @@ static int rx_macro_runtime_resume(struct device *dev) struct rx_macro *rx = dev_get_drvdata(dev); int ret; - ret = clk_prepare_enable(rx->mclk); + ret = pm_clk_resume(dev); if (ret) { - dev_err(dev, "unable to prepare mclk\n"); + regcache_cache_only(rx->regmap, true); + regcache_mark_dirty(rx->regmap); return ret; } - ret = clk_prepare_enable(rx->npl); - if (ret) { - dev_err(dev, "unable to prepare mclkx2\n"); - goto err_npl; - } - - ret = clk_prepare_enable(rx->fsgen); - if (ret) { - dev_err(dev, "unable to prepare fsgen\n"); - goto err_fsgen; - } regcache_cache_only(rx->regmap, false); - regcache_sync(rx->regmap); + ret = regcache_sync(rx->regmap); + if (ret) { + regcache_cache_only(rx->regmap, true); + regcache_mark_dirty(rx->regmap); + pm_clk_suspend(dev); + return ret; + } return 0; -err_fsgen: - clk_disable_unprepare(rx->npl); -err_npl: - clk_disable_unprepare(rx->mclk); - - return ret; } static const struct dev_pm_ops rx_macro_pm_ops = { @@ -4026,7 +3996,6 @@ static struct platform_driver rx_macro_driver = { .pm = pm_ptr(&rx_macro_pm_ops), }, .probe = rx_macro_probe, - .remove = rx_macro_remove, }; module_platform_driver(rx_macro_driver); From b8ca90fafe6adc401601f91d1394ba76bacf67ed Mon Sep 17 00:00:00 2001 From: Ajay Kumar Nandam Date: Tue, 7 Jul 2026 15:13:13 +0530 Subject: [PATCH 3/3] ASoC: codecs: lpass-{tx,rx}-macro: check clk_set_rate() return value clk_set_rate() returns 0 on success or a negative errno on failure but the TX and RX macro probe functions were ignoring it. Check the return value and bail out of probe on failure. Suggested-by: Konrad Dybcio Co-developed-by: Ravi Hothi Signed-off-by: Ravi Hothi Signed-off-by: Ajay Kumar Nandam Link: https://patch.msgid.link/20260707-xo-sd-codec-tx-rx-v2-3-f61b4622f97f@oss.qualcomm.com Signed-off-by: Mark Brown --- sound/soc/codecs/lpass-rx-macro.c | 9 +++++++-- sound/soc/codecs/lpass-tx-macro.c | 9 +++++++-- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/sound/soc/codecs/lpass-rx-macro.c b/sound/soc/codecs/lpass-rx-macro.c index 927f75050c0f..388cebf461a3 100644 --- a/sound/soc/codecs/lpass-rx-macro.c +++ b/sound/soc/codecs/lpass-rx-macro.c @@ -3863,8 +3863,13 @@ static int rx_macro_probe(struct platform_device *pdev) rx->dev = dev; /* set MCLK and NPL rates */ - clk_set_rate(rx->mclk, MCLK_FREQ); - clk_set_rate(rx->npl, MCLK_FREQ); + ret = clk_set_rate(rx->mclk, MCLK_FREQ); + if (ret) + return ret; + + ret = clk_set_rate(rx->npl, MCLK_FREQ); + if (ret) + return ret; ret = devm_pm_clk_create(dev); if (ret) diff --git a/sound/soc/codecs/lpass-tx-macro.c b/sound/soc/codecs/lpass-tx-macro.c index fc073a556fb5..b56605639a24 100644 --- a/sound/soc/codecs/lpass-tx-macro.c +++ b/sound/soc/codecs/lpass-tx-macro.c @@ -2319,8 +2319,13 @@ static int tx_macro_probe(struct platform_device *pdev) tx->active_decimator[TX_MACRO_AIF3_CAP] = -1; /* set MCLK and NPL rates */ - clk_set_rate(tx->mclk, MCLK_FREQ); - clk_set_rate(tx->npl, MCLK_FREQ); + ret = clk_set_rate(tx->mclk, MCLK_FREQ); + if (ret) + goto err; + + ret = clk_set_rate(tx->npl, MCLK_FREQ); + if (ret) + goto err; ret = devm_pm_clk_create(dev); if (ret)