]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
ASoC: rsnd: Add system suspend/resume support
authorJohn Madieu <john.madieu.xa@bp.renesas.com>
Mon, 25 May 2026 11:02:30 +0000 (11:02 +0000)
committerMark Brown <broonie@kernel.org>
Mon, 1 Jun 2026 14:30:27 +0000 (15:30 +0100)
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 <john.madieu.xa@bp.renesas.com>
Acked-by: Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
Link: https://patch.msgid.link/20260525110230.4014435-19-john.madieu.xa@bp.renesas.com
Signed-off-by: Mark Brown <broonie@kernel.org>
sound/soc/renesas/rcar/adg.c
sound/soc/renesas/rcar/core.c
sound/soc/renesas/rcar/ctu.c
sound/soc/renesas/rcar/dma.c
sound/soc/renesas/rcar/dvc.c
sound/soc/renesas/rcar/mix.c
sound/soc/renesas/rcar/rsnd.h
sound/soc/renesas/rcar/src.c
sound/soc/renesas/rcar/ssi.c
sound/soc/renesas/rcar/ssiu.c

index 483979c243198371c7e8dbb0f9a236bb8177bd23..5479cefb6dbebe8e6cd280e0ba3332a97dcb42d3 100644 (file)
@@ -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);
+}
index fbf7f72364600720a304b57d02c37e0049fc913c..9ce56cd84f46d649729b5bc4fcd982e239d319c2 100644 (file)
@@ -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 = {
index 293b0eec1dedc9bd34ac508bc55038206c9a5479..7db0fb3612bc0cfe3c4f88b5e97566204a5bb897 100644 (file)
@@ -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);
+}
index 537b71841f8e2ec1772741296a9241b0ca823b4a..793dd4adbe5c640e561af06756502ead87d73e50 100644 (file)
@@ -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);
+       }
+}
index 26f80d542da87e7fd6d6bb971c6a070e1f6165ce..7601dfb0810a7202f4be3521527ebb6f739e2ba6 100644 (file)
@@ -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);
+}
index 9ffa591aa4a433fee1b3ddf817c59795ed6ec89d..c4da4c4bedb34e17cecd189dfac7e0e1072a6620 100644 (file)
@@ -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);
+}
index f38bd92d4faf3c9b45f6887c36912fc29a861acc..b480085fb0e7c1c829144784cfe4d9ce26dcd894 100644 (file)
@@ -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)                        \
index cad15fcc37f2214e58c6604011562fce395403fa..ac806bdc96d9ef8f0e19f654f8c5a3fc8e6bc294 100644 (file)
@@ -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);
+}
index 007a7c91d47070db989ba5f1bf349ed3a5b8da87..2fa76a07998279121fbd5ce653fd2187bf352dd9 100644 (file)
@@ -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);
+}
index 7d3d463c21bfd580cd494c59d12c41644859151a..2a8593a5d4a60c8ab50f219ff8f63fc911fe3ab9 100644 (file)
@@ -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);
+}