[PATCH v5 2/3] ASoC: codecs: lpass-va-macro: Switch to PM clock framework for runtime PM
From: Ajay Kumar Nandam
Date: Fri May 22 2026 - 09:37:36 EST
Convert the LPASS VA macro codec driver to use the PM clock framework
for runtime power management.
The driver now relies on pm_clk helpers and runtime PM instead of
manually enabling and disabling macro, dcodec, mclk, and npl clocks.
All clock control during runtime suspend and resume is delegated to
the PM core via pm_clk_suspend() and pm_clk_resume().
This change ensures clocks are only enabled when the VA macro is
active, improves power efficiency on LPASS platforms supporting
LPI/island modes, and aligns the driver with common ASoC runtime PM
patterns used across Qualcomm LPASS codec drivers.
Take a runtime-PM reference in fsgen_gate_enable() for all VA variants,
including non-SWR-master configurations, and release it in
fsgen_gate_disable() so PM-clock-managed clocks stay active while fsgen
is enabled.
Add a PM dependency for SND_SOC_LPASS_VA_MACRO so PM clock helpers are
available when this driver is built.
Signed-off-by: Ajay Kumar Nandam <ajay.nandam@xxxxxxxxxxxxxxxx>
---
sound/soc/codecs/Kconfig | 1 +
sound/soc/codecs/lpass-va-macro.c | 133 +++++++++++++++++----------------
2 files changed, 71 insertions(+), 63 deletions(-)
diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig
index 4e17119f2f9e..e73667b87fd8 100644
--- a/sound/soc/codecs/Kconfig
+++ b/sound/soc/codecs/Kconfig
@@ -2867,7 +2867,8 @@ config SND_SOC_LPASS_WSA_MACRO
config SND_SOC_LPASS_VA_MACRO
depends on COMMON_CLK
+ depends on PM
select REGMAP_MMIO
select SND_SOC_LPASS_MACRO_COMMON
tristate "Qualcomm VA Macro in LPASS(Low Power Audio SubSystem)"
diff --git a/sound/soc/codecs/lpass-va-macro.c b/sound/soc/codecs/lpass-va-macro.c
index 528d5b167ecf..64a06d9ed6c8 100644
--- a/sound/soc/codecs/lpass-va-macro.c
+++ b/sound/soc/codecs/lpass-va-macro.c
@@ -11,6 +11,7 @@
#include <linux/of_platform.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
+#include <linux/pm_clock.h>
#include <linux/regmap.h>
#include <linux/regulator/consumer.h>
#include <sound/soc.h>
@@ -1348,32 +1349,56 @@ static int fsgen_gate_enable(struct clk_hw *hw)
struct regmap *regmap = va->regmap;
int ret, rpm_ret;
- if (va->has_swr_master) {
- ret = clk_prepare_enable(va->mclk);
- if (ret)
- return ret;
- }
+ ret = pm_runtime_resume_and_get(va->dev);
+ if (ret < 0)
+ return ret;
ret = va_macro_mclk_enable(va, true);
+ if (ret) {
+ rpm_ret = pm_runtime_put_autosuspend(va->dev);
+ if (rpm_ret < 0)
+ dev_warn(va->dev,
+ "runtime PM put failed in fsgen enable unwind: %d\n",
+ rpm_ret);
+ return ret;
+ }
if (va->has_swr_master)
regmap_update_bits(regmap, CDC_VA_CLK_RST_CTRL_SWR_CONTROL,
CDC_VA_SWR_CLK_EN_MASK, CDC_VA_SWR_CLK_ENABLE);
- return ret;
+ return 0;
}
static void fsgen_gate_disable(struct clk_hw *hw)
{
struct va_macro *va = to_va_macro(hw);
struct regmap *regmap = va->regmap;
+ int ret;
if (va->has_swr_master)
regmap_update_bits(regmap, CDC_VA_CLK_RST_CTRL_SWR_CONTROL,
CDC_VA_SWR_CLK_EN_MASK, 0x0);
va_macro_mclk_enable(va, false);
- if (va->has_swr_master)
- clk_disable_unprepare(va->mclk);
+
+ ret = pm_runtime_put_autosuspend(va->dev);
+ if (ret < 0)
+ dev_warn(va->dev, "runtime PM put failed in fsgen disable: %d\n", ret);
+}
+
+static int va_macro_setup_pm_clocks(struct device *dev)
+{
+ int ret;
+
+ ret = devm_pm_clk_create(dev);
+ if (ret)
+ return ret;
+
+ ret = of_pm_clk_add_clks(dev);
+ if (ret < 0)
+ return ret;
+
+ return 0;
}
static int fsgen_gate_is_enabled(struct clk_hw *hw)
@@ -1534,6 +1559,7 @@ static int va_macro_probe(struct platform_device *pdev)
void __iomem *base;
u32 sample_rate = 0;
int ret;
+ int rpm_ret;
va = devm_kzalloc(dev, sizeof(*va), GFP_KERNEL);
if (!va)
@@ -1601,22 +1627,20 @@ static int va_macro_probe(struct platform_device *pdev)
clk_set_rate(va->npl, 2 * VA_MACRO_MCLK_FREQ);
}
- ret = clk_prepare_enable(va->macro);
+ ret = va_macro_setup_pm_clocks(dev);
if (ret)
goto err;
- ret = clk_prepare_enable(va->dcodec);
- if (ret)
- goto err_dcodec;
-
- ret = clk_prepare_enable(va->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;
- if (va->has_npl_clk) {
- ret = clk_prepare_enable(va->npl);
- if (ret)
- goto err_npl;
+ rpm_ret = pm_runtime_resume_and_get(dev);
+ if (rpm_ret < 0) {
+ ret = rpm_ret;
+ goto err;
}
/**
@@ -1629,7 +1653,7 @@ static int va_macro_probe(struct platform_device *pdev)
/* read version from register */
ret = va_macro_set_lpass_codec_version(va);
if (ret)
- goto err_clkout;
+ goto err_rpm_put;
}
if (va->has_swr_master) {
@@ -1659,35 +1683,28 @@ static int va_macro_probe(struct platform_device *pdev)
va_macro_dais,
ARRAY_SIZE(va_macro_dais));
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 = va_macro_register_fsgen_output(va);
if (ret)
- goto err_clkout;
+ goto err_rpm_put;
va->fsgen = devm_clk_hw_get_clk(dev, &va->hw, "fsgen");
if (IS_ERR(va->fsgen)) {
ret = PTR_ERR(va->fsgen);
- goto err_clkout;
+ goto err_rpm_put;
}
+ rpm_ret = pm_runtime_put_autosuspend(dev);
+ if (rpm_ret < 0)
+ dev_warn(dev, "runtime PM put failed after probe: %d\n", rpm_ret);
+
return 0;
-err_clkout:
- if (va->has_npl_clk)
- clk_disable_unprepare(va->npl);
-err_npl:
- clk_disable_unprepare(va->mclk);
-err_mclk:
- clk_disable_unprepare(va->dcodec);
-err_dcodec:
- clk_disable_unprepare(va->macro);
+err_rpm_put:
+ rpm_ret = pm_runtime_put_sync_suspend(dev);
+ if (rpm_ret < 0)
+ dev_warn(dev, "runtime PM sync suspend failed in probe unwind: %d\n", rpm_ret);
err:
lpass_macro_pds_exit(va->pds);
@@ -1698,27 +1715,23 @@ static void va_macro_remove(struct platform_device *pdev)
{
struct va_macro *va = dev_get_drvdata(&pdev->dev);
- if (va->has_npl_clk)
- clk_disable_unprepare(va->npl);
-
- clk_disable_unprepare(va->mclk);
- clk_disable_unprepare(va->dcodec);
- clk_disable_unprepare(va->macro);
-
lpass_macro_pds_exit(va->pds);
}
static int va_macro_runtime_suspend(struct device *dev)
{
struct va_macro *va = dev_get_drvdata(dev);
+ int ret;
regcache_cache_only(va->regmap, true);
- regcache_mark_dirty(va->regmap);
- if (va->has_npl_clk)
- clk_disable_unprepare(va->npl);
+ ret = pm_clk_suspend(dev);
+ if (ret) {
+ regcache_cache_only(va->regmap, false);
+ return ret;
+ }
- clk_disable_unprepare(va->mclk);
+ regcache_mark_dirty(va->regmap);
return 0;
}
@@ -1728,24 +1741,26 @@ static int va_macro_runtime_resume(struct device *dev)
struct va_macro *va = dev_get_drvdata(dev);
- int ret;
+ int ret, sret;
- ret = clk_prepare_enable(va->mclk);
- if (ret) {
- dev_err(va->dev, "unable to prepare mclk\n");
+ ret = pm_clk_resume(dev);
+ if (ret) {
+ regcache_cache_only(va->regmap, true);
+ regcache_mark_dirty(va->regmap);
return ret;
- }
+ }
-
- if (va->has_npl_clk) {
- ret = clk_prepare_enable(va->npl);
- if (ret) {
- clk_disable_unprepare(va->mclk);
- dev_err(va->dev, "unable to prepare npl\n");
- return ret;
- }
- }
regcache_cache_only(va->regmap, false);
- regcache_sync(va->regmap);
+
+ ret = regcache_sync(va->regmap);
+ if (ret) {
+ regcache_cache_only(va->regmap, true);
+ regcache_mark_dirty(va->regmap);
+ sret = pm_clk_suspend(dev);
+ if (sret)
+ dev_err(va->dev,
+ "failed to suspend clocks after regcache sync failure: %d\n",
+ sret);
+ return ret;
+ }
return 0;
}
--
2.34.1