[PATCH] ASoC: rsnd: adg: make rsnd_adg_clk_control() idempotent

From: John Madieu

Date: Wed Jun 10 2026 - 12:54:35 EST


rsnd_adg_clk_control() is asymmetric on the disable path: the clkin
clocks are guarded by clkin_rate[], but the "adg" clock is disabled
unconditionally. If an enable attempt fails (for example a clkin
failing to turn on during resume), the error path correctly rolls
everything back, but rsnd_resume() ignores the return value, so the
following system suspend calls rsnd_adg_clk_disable() again and
underflows the "adg" clock enable count:

adg_0_clks1 already disabled
WARNING: drivers/clk/clk.c:1188 clk_core_disable+0xa4/0xac
Call trace:
clk_core_disable+0xa4/0xac (P)
clk_disable+0x30/0x4c
rsnd_adg_clk_control+0x9c/0x2cc
rsnd_suspend+0x20/0x74
device_suspend+0x140/0x3ec
dpm_suspend+0x168/0x270

Track the enable state explicitly and bail out of redundant
enable/disable calls, mirroring what is already done for the per-SSI
clock prepare state. A failed enable leaves the state as disabled, so
the next suspend becomes a no-op and the next resume retries cleanly.

Fixes: 47899d53f86f ("ASoC: rsnd: adg: Add per-SSI ADG and SSIF supply clock management")
Signed-off-by: John Madieu <john.madieu.xa@xxxxxxxxxxxxxx>
---
sound/soc/renesas/rcar/adg.c | 29 ++++++++++++++++++++++++++++-
1 file changed, 28 insertions(+), 1 deletion(-)

diff --git a/sound/soc/renesas/rcar/adg.c b/sound/soc/renesas/rcar/adg.c
index 5479cefb6dbe..53efd1be5139 100644
--- a/sound/soc/renesas/rcar/adg.c
+++ b/sound/soc/renesas/rcar/adg.c
@@ -45,6 +45,7 @@ struct rsnd_adg {
struct rsnd_mod mod;
int clkin_rate[CLKINMAX];
bool ssi_clk_prepared;
+ bool clk_enabled;
int clkin_size;
int clkout_size;
u32 ckr;
@@ -463,6 +464,22 @@ int rsnd_adg_clk_control(struct rsnd_priv *priv, int enable)
struct clk *clk;
int ret = 0, i;

+ /*
+ * rsnd_adg_clk_enable() and rsnd_adg_clk_disable() can be called
+ * redundantly, for example when system suspend follows a resume
+ * whose enable failed. Make this function idempotent so that the
+ * "adg" clock, which has no clkin_rate[] style guard, is never
+ * disabled twice.
+ */
+ if (enable) {
+ if (adg->clk_enabled)
+ return 0;
+ } else {
+ if (!adg->clk_enabled)
+ return 0;
+ adg->clk_enabled = false;
+ }
+
if (enable) {
ret = clk_prepare_enable(adg->adg);
if (ret < 0)
@@ -520,12 +537,22 @@ int rsnd_adg_clk_control(struct rsnd_priv *priv, int enable)
* rsnd_adg_clk_enable() might return error (_disable() will not).
* We need to rollback in such case
*/
- if (ret < 0)
+ if (ret < 0) {
+ /*
+ * Mark as enabled so that the rollback below is not
+ * short-circuited by the idempotency guard. It clears
+ * the flag again on its way through.
+ */
+ adg->clk_enabled = true;
rsnd_adg_clk_disable(priv);
+ return ret;
+ }

/* disable adg */
if (!enable)
clk_disable_unprepare(adg->adg);
+ else
+ adg->clk_enabled = true;

return ret;
}
--
2.25.1