[PATCH 4/5] ASoC: codecs: lpass-wsa-macro: manage clocks in runtime PM

From: Ajay Kumar Nandam

Date: Mon Sep 28 2026 - 06:33:33 EST


Replace PM clock framework usage with explicit clock prepare/enable and
disable/unprepare operations from runtime PM callbacks. This keeps the WSA
macro clock prepare state tied to the runtime PM active state, so idle
suspend can release parent clock votes.

Use DEFINE_RUNTIME_DEV_PM_OPS() so system suspend forces the same runtime
suspend path for an active device.

Signed-off-by: Ajay Kumar Nandam <ajay.nandam@xxxxxxxxxxxxxxxx>
---
sound/soc/codecs/lpass-wsa-macro.c | 80 +++++++++++++++++++++++++-------------
1 file changed, 53 insertions(+), 27 deletions(-)

diff --git a/sound/soc/codecs/lpass-wsa-macro.c b/sound/soc/codecs/lpass-wsa-macro.c
index 4242366601a5..bc0ff467c822 100644
--- a/sound/soc/codecs/lpass-wsa-macro.c
+++ b/sound/soc/codecs/lpass-wsa-macro.c
@@ -12,7 +12,6 @@
#include <linux/clk-provider.h>
#include <sound/soc.h>
#include <sound/soc-dapm.h>
-#include <linux/pm_clock.h>
#include <linux/pm_runtime.h>
#include <linux/of_platform.h>
#include <sound/tlv.h>
@@ -424,6 +423,53 @@ struct wsa_macro {
};
#define to_wsa_macro(_hw) container_of(_hw, struct wsa_macro, hw)

+static void wsa_macro_disable_clocks(struct wsa_macro *wsa)
+{
+ clk_disable_unprepare(wsa->fsgen);
+ clk_disable_unprepare(wsa->npl);
+ clk_disable_unprepare(wsa->mclk);
+ clk_disable_unprepare(wsa->dcodec);
+ clk_disable_unprepare(wsa->macro);
+}
+
+static int wsa_macro_enable_clocks(struct wsa_macro *wsa)
+{
+ int ret;
+
+ ret = clk_prepare_enable(wsa->macro);
+ if (ret)
+ return ret;
+
+ ret = clk_prepare_enable(wsa->dcodec);
+ if (ret)
+ goto err_dcodec;
+
+ ret = clk_prepare_enable(wsa->mclk);
+ if (ret)
+ goto err_mclk;
+
+ ret = clk_prepare_enable(wsa->npl);
+ if (ret)
+ goto err_npl;
+
+ ret = clk_prepare_enable(wsa->fsgen);
+ if (ret)
+ goto err_fsgen;
+
+ return 0;
+
+err_fsgen:
+ clk_disable_unprepare(wsa->npl);
+err_npl:
+ clk_disable_unprepare(wsa->mclk);
+err_mclk:
+ clk_disable_unprepare(wsa->dcodec);
+err_dcodec:
+ clk_disable_unprepare(wsa->macro);
+
+ return ret;
+}
+
static const struct wsa_reg_layout wsa_codec_v2_1 = {
.rx_intx_1_mix_inp0_sel_mask = GENMASK(2, 0),
.rx_intx_1_mix_inp1_sel_mask = GENMASK(5, 3),
@@ -2791,14 +2837,6 @@ static int wsa_macro_probe(struct platform_device *pdev)
if (ret)
return ret;

- ret = devm_pm_clk_create(dev);
- if (ret)
- return ret;
-
- ret = of_pm_clk_add_clks(dev);
- if (ret < 0)
- return ret;
-
pm_runtime_set_autosuspend_delay(dev, 100);
pm_runtime_use_autosuspend(dev);
ret = devm_pm_runtime_enable(dev);
@@ -2844,16 +2882,9 @@ static int wsa_macro_probe(struct platform_device *pdev)
static int wsa_macro_runtime_suspend(struct device *dev)
{
struct wsa_macro *wsa = dev_get_drvdata(dev);
- int ret;

regcache_cache_only(wsa->regmap, true);
-
- ret = pm_clk_suspend(dev);
- if (ret) {
- regcache_cache_only(wsa->regmap, false);
- return ret;
- }
-
+ wsa_macro_disable_clocks(wsa);
regcache_mark_dirty(wsa->regmap);

return 0;
@@ -2862,9 +2893,9 @@ static int wsa_macro_runtime_suspend(struct device *dev)
static int wsa_macro_runtime_resume(struct device *dev)
{
struct wsa_macro *wsa = dev_get_drvdata(dev);
- int ret, sret;
+ int ret;

- ret = pm_clk_resume(dev);
+ ret = wsa_macro_enable_clocks(wsa);
if (ret) {
regcache_cache_only(wsa->regmap, true);
regcache_mark_dirty(wsa->regmap);
@@ -2876,20 +2907,15 @@ static int wsa_macro_runtime_resume(struct device *dev)
if (ret) {
regcache_cache_only(wsa->regmap, true);
regcache_mark_dirty(wsa->regmap);
- sret = pm_clk_suspend(dev);
- if (sret)
- dev_err(dev,
- "failed to suspend clocks after regcache sync failure: %d\n",
- sret);
+ wsa_macro_disable_clocks(wsa);
return ret;
}

return 0;
}

-static const struct dev_pm_ops wsa_macro_pm_ops = {
- RUNTIME_PM_OPS(wsa_macro_runtime_suspend, wsa_macro_runtime_resume, NULL)
-};
+static DEFINE_RUNTIME_DEV_PM_OPS(wsa_macro_pm_ops, wsa_macro_runtime_suspend,
+ wsa_macro_runtime_resume, NULL);

static const struct of_device_id wsa_macro_dt_match[] = {
{

--
2.34.1