From: John Madieu Date: Mon, 25 May 2026 11:02:30 +0000 (+0000) Subject: ASoC: rsnd: Add system suspend/resume support X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=ef19ecf042b448a69ee3bd9b3e35689b0b7892ac;p=thirdparty%2Flinux.git ASoC: rsnd: Add system suspend/resume support Add system suspend/resume support for the ASoC rsnd driver, required for RZ/G3E platforms. Distribute the per-module suspend/resume work across the relevant files (adg.c, ssi.c, ssiu.c, src.c, ctu.c, mix.c, dvc.c, dma.c) rather than centralising it in core.c. Signed-off-by: John Madieu Acked-by: Kuninori Morimoto Link: https://patch.msgid.link/20260525110230.4014435-19-john.madieu.xa@bp.renesas.com Signed-off-by: Mark Brown --- diff --git a/sound/soc/renesas/rcar/adg.c b/sound/soc/renesas/rcar/adg.c index 483979c243198..5479cefb6dbeb 100644 --- a/sound/soc/renesas/rcar/adg.c +++ b/sound/soc/renesas/rcar/adg.c @@ -953,3 +953,29 @@ void rsnd_adg_remove(struct rsnd_priv *priv) /* It should be called after rsnd_adg_clk_disable() */ rsnd_adg_null_clk_clean(priv); } + +static struct rsnd_mod *rsnd_adg_mod_get(struct rsnd_priv *priv) +{ + struct rsnd_adg *adg = rsnd_priv_to_adg(priv); + + if (!adg) + return NULL; + + return rsnd_mod_get(adg); +} + +void rsnd_adg_suspend(struct rsnd_priv *priv) +{ + struct rsnd_mod *mod = rsnd_adg_mod_get(priv); + + if (mod) + rsnd_suspend_clk_reset(mod->clk, mod->rstc); +} + +void rsnd_adg_resume(struct rsnd_priv *priv) +{ + struct rsnd_mod *mod = rsnd_adg_mod_get(priv); + + if (mod) + rsnd_resume_clk_reset(mod->clk, mod->rstc); +} diff --git a/sound/soc/renesas/rcar/core.c b/sound/soc/renesas/rcar/core.c index fbf7f72364600..9ce56cd84f46d 100644 --- a/sound/soc/renesas/rcar/core.c +++ b/sound/soc/renesas/rcar/core.c @@ -962,7 +962,8 @@ static int rsnd_soc_hw_rule_channels(struct snd_pcm_hw_params *params, static const struct snd_pcm_hardware rsnd_pcm_hardware = { .info = SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_MMAP | - SNDRV_PCM_INFO_MMAP_VALID, + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_RESUME, .buffer_bytes_max = 64 * 1024, .period_bytes_min = 32, .period_bytes_max = 8192, @@ -2159,11 +2160,35 @@ static void rsnd_remove(struct platform_device *pdev) remove_func[i](priv); } +void rsnd_suspend_clk_reset(struct clk *clk, struct reset_control *rstc) +{ + clk_unprepare(clk); + reset_control_assert(rstc); +} + +void rsnd_resume_clk_reset(struct clk *clk, struct reset_control *rstc) +{ + reset_control_deassert(rstc); + clk_prepare(clk); +} + static int rsnd_suspend(struct device *dev) { struct rsnd_priv *priv = dev_get_drvdata(dev); + /* + * Reverse order of probe: + * ADG -> DVC -> MIX -> CTU -> SRC -> SSIU -> SSI -> DMA + */ rsnd_adg_clk_disable(priv); + rsnd_adg_suspend(priv); + rsnd_dvc_suspend(priv); + rsnd_mix_suspend(priv); + rsnd_ctu_suspend(priv); + rsnd_src_suspend(priv); + rsnd_ssiu_suspend(priv); + rsnd_ssi_suspend(priv); + rsnd_dma_suspend(priv); return 0; } @@ -2172,7 +2197,21 @@ static int rsnd_resume(struct device *dev) { struct rsnd_priv *priv = dev_get_drvdata(dev); - return rsnd_adg_clk_enable(priv); + /* + * Same order as probe: + * DMA -> SSI -> SSIU -> SRC -> CTU -> MIX -> DVC -> ADG + */ + rsnd_dma_resume(priv); + rsnd_ssi_resume(priv); + rsnd_ssiu_resume(priv); + rsnd_src_resume(priv); + rsnd_ctu_resume(priv); + rsnd_mix_resume(priv); + rsnd_dvc_resume(priv); + rsnd_adg_resume(priv); + rsnd_adg_clk_enable(priv); + + return 0; } static const struct dev_pm_ops rsnd_pm_ops = { diff --git a/sound/soc/renesas/rcar/ctu.c b/sound/soc/renesas/rcar/ctu.c index 293b0eec1dedc..7db0fb3612bc0 100644 --- a/sound/soc/renesas/rcar/ctu.c +++ b/sound/soc/renesas/rcar/ctu.c @@ -378,3 +378,23 @@ void rsnd_ctu_remove(struct rsnd_priv *priv) rsnd_mod_quit(rsnd_mod_get(ctu)); } } + +void rsnd_ctu_suspend(struct rsnd_priv *priv) +{ + struct rsnd_ctu *ctu; + int i; + + for_each_rsnd_ctu(ctu, priv, i) + rsnd_suspend_clk_reset(rsnd_mod_get(ctu)->clk, + rsnd_mod_get(ctu)->rstc); +} + +void rsnd_ctu_resume(struct rsnd_priv *priv) +{ + struct rsnd_ctu *ctu; + int i; + + for_each_rsnd_ctu(ctu, priv, i) + rsnd_resume_clk_reset(rsnd_mod_get(ctu)->clk, + rsnd_mod_get(ctu)->rstc); +} diff --git a/sound/soc/renesas/rcar/dma.c b/sound/soc/renesas/rcar/dma.c index 537b71841f8e2..793dd4adbe5c6 100644 --- a/sound/soc/renesas/rcar/dma.c +++ b/sound/soc/renesas/rcar/dma.c @@ -1035,3 +1035,25 @@ audmapp_end: /* dummy mem mod for debug */ return rsnd_mod_init(NULL, &mem, &mem_ops, NULL, NULL, 0, 0); } + +void rsnd_dma_suspend(struct rsnd_priv *priv) +{ + struct rsnd_dma_ctrl *dmac = rsnd_priv_to_dmac(priv); + + if (dmac) { + /* Mirror probe (which enables clk before deasserting reset) */ + rsnd_suspend_clk_reset(NULL, dmac->audmapp_rstc); + clk_disable_unprepare(dmac->audmapp_clk); + } +} + +void rsnd_dma_resume(struct rsnd_priv *priv) +{ + struct rsnd_dma_ctrl *dmac = rsnd_priv_to_dmac(priv); + + if (dmac) { + /* Clock must be stable before reset is deasserted */ + clk_prepare_enable(dmac->audmapp_clk); + rsnd_resume_clk_reset(NULL, dmac->audmapp_rstc); + } +} diff --git a/sound/soc/renesas/rcar/dvc.c b/sound/soc/renesas/rcar/dvc.c index 26f80d542da87..7601dfb0810a7 100644 --- a/sound/soc/renesas/rcar/dvc.c +++ b/sound/soc/renesas/rcar/dvc.c @@ -381,3 +381,23 @@ void rsnd_dvc_remove(struct rsnd_priv *priv) rsnd_mod_quit(rsnd_mod_get(dvc)); } } + +void rsnd_dvc_suspend(struct rsnd_priv *priv) +{ + struct rsnd_dvc *dvc; + int i; + + for_each_rsnd_dvc(dvc, priv, i) + rsnd_suspend_clk_reset(rsnd_mod_get(dvc)->clk, + rsnd_mod_get(dvc)->rstc); +} + +void rsnd_dvc_resume(struct rsnd_priv *priv) +{ + struct rsnd_dvc *dvc; + int i; + + for_each_rsnd_dvc(dvc, priv, i) + rsnd_resume_clk_reset(rsnd_mod_get(dvc)->clk, + rsnd_mod_get(dvc)->rstc); +} diff --git a/sound/soc/renesas/rcar/mix.c b/sound/soc/renesas/rcar/mix.c index 9ffa591aa4a43..c4da4c4bedb34 100644 --- a/sound/soc/renesas/rcar/mix.c +++ b/sound/soc/renesas/rcar/mix.c @@ -345,3 +345,23 @@ void rsnd_mix_remove(struct rsnd_priv *priv) rsnd_mod_quit(rsnd_mod_get(mix)); } } + +void rsnd_mix_suspend(struct rsnd_priv *priv) +{ + struct rsnd_mix *mix; + int i; + + for_each_rsnd_mix(mix, priv, i) + rsnd_suspend_clk_reset(rsnd_mod_get(mix)->clk, + rsnd_mod_get(mix)->rstc); +} + +void rsnd_mix_resume(struct rsnd_priv *priv) +{ + struct rsnd_mix *mix; + int i; + + for_each_rsnd_mix(mix, priv, i) + rsnd_resume_clk_reset(rsnd_mod_get(mix)->clk, + rsnd_mod_get(mix)->rstc); +} diff --git a/sound/soc/renesas/rcar/rsnd.h b/sound/soc/renesas/rcar/rsnd.h index f38bd92d4faf3..b480085fb0e7c 100644 --- a/sound/soc/renesas/rcar/rsnd.h +++ b/sound/soc/renesas/rcar/rsnd.h @@ -267,6 +267,8 @@ u32 rsnd_get_busif_shift(struct rsnd_dai_stream *io, struct rsnd_mod *mod); int rsnd_dma_attach(struct rsnd_dai_stream *io, struct rsnd_mod *mod, struct rsnd_mod **dma_mod); int rsnd_dma_probe(struct rsnd_priv *priv); +void rsnd_dma_suspend(struct rsnd_priv *priv); +void rsnd_dma_resume(struct rsnd_priv *priv); struct dma_chan *rsnd_dma_request_channel(struct device_node *of_node, char *name, struct rsnd_mod *mod, char *x); @@ -429,6 +431,8 @@ int rsnd_mod_init(struct rsnd_priv *priv, enum rsnd_mod_type type, int id); void rsnd_mod_quit(struct rsnd_mod *mod); +void rsnd_suspend_clk_reset(struct clk *clk, struct reset_control *rstc); +void rsnd_resume_clk_reset(struct clk *clk, struct reset_control *rstc); struct dma_chan *rsnd_mod_dma_req(struct rsnd_dai_stream *io, struct rsnd_mod *mod); void rsnd_mod_interrupt(struct rsnd_mod *mod, @@ -625,6 +629,8 @@ int rsnd_adg_ssi_clk_stop(struct rsnd_mod *ssi_mod); int rsnd_adg_ssi_clk_try_start(struct rsnd_mod *ssi_mod, unsigned int rate); int rsnd_adg_probe(struct rsnd_priv *priv); void rsnd_adg_remove(struct rsnd_priv *priv); +void rsnd_adg_suspend(struct rsnd_priv *priv); +void rsnd_adg_resume(struct rsnd_priv *priv); int rsnd_adg_set_src_timesel_gen2(struct rsnd_mod *src_mod, struct rsnd_dai_stream *io, unsigned int in_rate, @@ -822,6 +828,8 @@ extern const char * const volume_ramp_rate[]; */ int rsnd_ssi_probe(struct rsnd_priv *priv); void rsnd_ssi_remove(struct rsnd_priv *priv); +void rsnd_ssi_suspend(struct rsnd_priv *priv); +void rsnd_ssi_resume(struct rsnd_priv *priv); struct rsnd_mod *rsnd_ssi_mod_get(struct rsnd_priv *priv, int id); int rsnd_ssi_use_busif(struct rsnd_dai_stream *io); u32 rsnd_ssi_multi_secondaries_runtime(struct rsnd_dai_stream *io); @@ -845,6 +853,8 @@ int rsnd_ssiu_attach(struct rsnd_dai_stream *io, struct rsnd_mod *mod); int rsnd_ssiu_probe(struct rsnd_priv *priv); void rsnd_ssiu_remove(struct rsnd_priv *priv); +void rsnd_ssiu_suspend(struct rsnd_priv *priv); +void rsnd_ssiu_resume(struct rsnd_priv *priv); void rsnd_parse_connect_ssiu(struct rsnd_dai *rdai, struct device_node *playback, struct device_node *capture); @@ -856,6 +866,8 @@ bool rsnd_ssiu_busif_err_status_clear(struct rsnd_mod *mod); */ int rsnd_src_probe(struct rsnd_priv *priv); void rsnd_src_remove(struct rsnd_priv *priv); +void rsnd_src_suspend(struct rsnd_priv *priv); +void rsnd_src_resume(struct rsnd_priv *priv); struct rsnd_mod *rsnd_src_mod_get(struct rsnd_priv *priv, int id); #define rsnd_src_get_in_rate(priv, io) rsnd_src_get_rate(priv, io, 1) @@ -875,6 +887,8 @@ unsigned int rsnd_src_get_rate(struct rsnd_priv *priv, */ int rsnd_ctu_probe(struct rsnd_priv *priv); void rsnd_ctu_remove(struct rsnd_priv *priv); +void rsnd_ctu_suspend(struct rsnd_priv *priv); +void rsnd_ctu_resume(struct rsnd_priv *priv); struct rsnd_mod *rsnd_ctu_mod_get(struct rsnd_priv *priv, int id); #define rsnd_ctu_of_node(priv) rsnd_parse_of_node(priv, RSND_NODE_CTU) #define rsnd_parse_connect_ctu(rdai, playback, capture) \ @@ -887,6 +901,8 @@ struct rsnd_mod *rsnd_ctu_mod_get(struct rsnd_priv *priv, int id); */ int rsnd_mix_probe(struct rsnd_priv *priv); void rsnd_mix_remove(struct rsnd_priv *priv); +void rsnd_mix_suspend(struct rsnd_priv *priv); +void rsnd_mix_resume(struct rsnd_priv *priv); struct rsnd_mod *rsnd_mix_mod_get(struct rsnd_priv *priv, int id); #define rsnd_mix_of_node(priv) rsnd_parse_of_node(priv, RSND_NODE_MIX) #define rsnd_parse_connect_mix(rdai, playback, capture) \ @@ -899,6 +915,8 @@ struct rsnd_mod *rsnd_mix_mod_get(struct rsnd_priv *priv, int id); */ int rsnd_dvc_probe(struct rsnd_priv *priv); void rsnd_dvc_remove(struct rsnd_priv *priv); +void rsnd_dvc_suspend(struct rsnd_priv *priv); +void rsnd_dvc_resume(struct rsnd_priv *priv); struct rsnd_mod *rsnd_dvc_mod_get(struct rsnd_priv *priv, int id); #define rsnd_dvc_of_node(priv) rsnd_parse_of_node(priv, RSND_NODE_DVC) #define rsnd_parse_connect_dvc(rdai, playback, capture) \ diff --git a/sound/soc/renesas/rcar/src.c b/sound/soc/renesas/rcar/src.c index cad15fcc37f22..ac806bdc96d9e 100644 --- a/sound/soc/renesas/rcar/src.c +++ b/sound/soc/renesas/rcar/src.c @@ -834,3 +834,37 @@ void rsnd_src_remove(struct rsnd_priv *priv) rsnd_mod_quit(rsnd_mod_get(src)); } } + +void rsnd_src_suspend(struct rsnd_priv *priv) +{ + struct rsnd_src_ctrl *src_ctrl = rsnd_priv_to_src_ctrl(priv); + struct rsnd_src *src; + int i; + + if (!src_ctrl) + return; + + for_each_rsnd_src(src, priv, i) + rsnd_suspend_clk_reset(rsnd_mod_get(src)->clk, + rsnd_mod_get(src)->rstc); + + clk_disable_unprepare(src_ctrl->scu_x2); + clk_disable_unprepare(src_ctrl->scu); +} + +void rsnd_src_resume(struct rsnd_priv *priv) +{ + struct rsnd_src_ctrl *src_ctrl = rsnd_priv_to_src_ctrl(priv); + struct rsnd_src *src; + int i; + + if (!src_ctrl) + return; + + clk_prepare_enable(src_ctrl->scu); + clk_prepare_enable(src_ctrl->scu_x2); + + for_each_rsnd_src(src, priv, i) + rsnd_resume_clk_reset(rsnd_mod_get(src)->clk, + rsnd_mod_get(src)->rstc); +} diff --git a/sound/soc/renesas/rcar/ssi.c b/sound/soc/renesas/rcar/ssi.c index 007a7c91d4707..2fa76a0799827 100644 --- a/sound/soc/renesas/rcar/ssi.c +++ b/sound/soc/renesas/rcar/ssi.c @@ -1257,3 +1257,23 @@ void rsnd_ssi_remove(struct rsnd_priv *priv) rsnd_mod_quit(rsnd_mod_get(ssi)); } } + +void rsnd_ssi_suspend(struct rsnd_priv *priv) +{ + struct rsnd_ssi *ssi; + int i; + + for_each_rsnd_ssi(ssi, priv, i) + rsnd_suspend_clk_reset(rsnd_mod_get(ssi)->clk, + rsnd_mod_get(ssi)->rstc); +} + +void rsnd_ssi_resume(struct rsnd_priv *priv) +{ + struct rsnd_ssi *ssi; + int i; + + for_each_rsnd_ssi(ssi, priv, i) + rsnd_resume_clk_reset(rsnd_mod_get(ssi)->clk, + rsnd_mod_get(ssi)->rstc); +} diff --git a/sound/soc/renesas/rcar/ssiu.c b/sound/soc/renesas/rcar/ssiu.c index 7d3d463c21bfd..2a8593a5d4a60 100644 --- a/sound/soc/renesas/rcar/ssiu.c +++ b/sound/soc/renesas/rcar/ssiu.c @@ -629,3 +629,23 @@ void rsnd_ssiu_remove(struct rsnd_priv *priv) rsnd_mod_quit(rsnd_mod_get(ssiu)); } } + +void rsnd_ssiu_suspend(struct rsnd_priv *priv) +{ + struct rsnd_ssiu *ssiu; + int i; + + for_each_rsnd_ssiu(ssiu, priv, i) + rsnd_suspend_clk_reset(rsnd_mod_get(ssiu)->clk, + rsnd_mod_get(ssiu)->rstc); +} + +void rsnd_ssiu_resume(struct rsnd_priv *priv) +{ + struct rsnd_ssiu *ssiu; + int i; + + for_each_rsnd_ssiu(ssiu, priv, i) + rsnd_resume_clk_reset(rsnd_mod_get(ssiu)->clk, + rsnd_mod_get(ssiu)->rstc); +}