[PATCH 1/2] ASoC: codecs: lpass-tx-macro: switch to PM clock framework for runtime PM
From: Ajay Kumar Nandam
Date: Fri Jul 03 2026 - 07:15:45 EST
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 <ravi.hothi@xxxxxxxxxxxxxxxx>
Signed-off-by: Ravi Hothi <ravi.hothi@xxxxxxxxxxxxxxxx>
Signed-off-by: Ajay Kumar Nandam <ajay.nandam@xxxxxxxxxxxxxxxx>
---
sound/soc/codecs/Kconfig | 2 +
sound/soc/codecs/lpass-tx-macro.c | 115 ++++++++++++++++----------------------
2 files changed, 50 insertions(+), 67 deletions(-)
diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig
index f90a0d4c77ea..cdb50316f471 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..2b2dda5ee1ed 100644
--- a/sound/soc/codecs/lpass-tx-macro.c
+++ b/sound/soc/codecs/lpass-tx-macro.c
@@ -6,6 +6,7 @@
#include <linux/clk.h>
#include <linux/io.h>
#include <linux/platform_device.h>
+#include <linux/pm_clock.h>
#include <linux/pm_runtime.h>
#include <linux/regmap.h>
#include <sound/soc.h>
@@ -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)
@@ -2316,27 +2320,26 @@ static int tx_macro_probe(struct platform_device *pdev)
/* set MCLK and NPL rates */
clk_set_rate(tx->mclk, MCLK_FREQ);
- clk_set_rate(tx->npl, MCLK_FREQ);
+ if (tx->npl)
+ 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 = clk_prepare_enable(tx->mclk);
- if (ret)
- goto err_mclk;
+ ret = of_pm_clk_add_clks(dev);
+ if (ret < 0)
+ goto err;
- ret = clk_prepare_enable(tx->npl);
+ pm_runtime_set_autosuspend_delay(dev, 100);
+ pm_runtime_use_autosuspend(dev);
+ ret = devm_pm_runtime_enable(dev);
if (ret)
- goto err_npl;
+ goto err;
- 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 +2359,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 +2384,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 +2410,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);
+ regcache_cache_only(tx->regmap, false);
+ ret = regcache_sync(tx->regmap);
if (ret) {
- dev_err(dev, "unable to prepare fsgen\n");
- goto err_fsgen;
+ regcache_cache_only(tx->regmap, true);
+ regcache_mark_dirty(tx->regmap);
+ pm_clk_suspend(dev);
+ return ret;
}
- regcache_cache_only(tx->regmap, false);
- regcache_sync(tx->regmap);
-
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 = {
--
2.34.1