diff options
| author | John Madieu <john.madieu.xa@bp.renesas.com> | 2026-05-25 14:02:25 +0300 |
|---|---|---|
| committer | Mark Brown <broonie@kernel.org> | 2026-06-01 17:30:23 +0300 |
| commit | 47899d53f86f5ab8a65a8d7bfa53d4ba79fa9e82 (patch) | |
| tree | 10342df2fa6db9e3811f47f5494e3a5b0058c2d2 | |
| parent | 9267b89985de699a6bf721d90c319b7568391890 (diff) | |
| download | linux-47899d53f86f5ab8a65a8d7bfa53d4ba79fa9e82.tar.xz | |
ASoC: rsnd: adg: Add per-SSI ADG and SSIF supply clock management
RZ/G3E's ADG module requires explicit clock management for SSI audio
interfaces that differs from R-Car Gen2/Gen3/Gen4:
- Per-SSI ADG clocks (adg-ssi-N, or adg.ssi.N in legacy bindings)
for each SSI module
- A shared SSIF supply clock for the SSI subsystem
These clocks are acquired using optional APIs, making them transparent
to platforms that do not require them.
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-14-john.madieu.xa@bp.renesas.com
Signed-off-by: Mark Brown <broonie@kernel.org>
| -rw-r--r-- | sound/soc/renesas/rcar/adg.c | 124 |
1 files changed, 123 insertions, 1 deletions
diff --git a/sound/soc/renesas/rcar/adg.c b/sound/soc/renesas/rcar/adg.c index 813ad5eabba6..5dce62287d20 100644 --- a/sound/soc/renesas/rcar/adg.c +++ b/sound/soc/renesas/rcar/adg.c @@ -19,6 +19,9 @@ #define CLKOUT3 3 #define CLKOUTMAX 4 +/* Maximum SSI count for per-SSI clocks */ +#define ADG_SSI_MAX 10 + #define BRGCKR_31 (1 << 31) #define BRRx_MASK(x) (0x3FF & x) @@ -34,10 +37,14 @@ struct rsnd_adg { struct clk *adg; struct clk *clkin[CLKINMAX]; struct clk *clkout[CLKOUTMAX]; + /* RZ/G3E: per-SSI ADG clocks (adg-ssi-0 through adg-ssi-9) */ + struct clk *clk_adg_ssi[ADG_SSI_MAX]; + struct clk *clk_ssif_supply; struct clk *null_clk; struct clk_onecell_data onecell; struct rsnd_mod mod; int clkin_rate[CLKINMAX]; + bool ssi_clk_prepared; int clkin_size; int clkout_size; u32 ckr; @@ -343,8 +350,16 @@ int rsnd_adg_clk_query(struct rsnd_priv *priv, unsigned int rate) int rsnd_adg_ssi_clk_stop(struct rsnd_mod *ssi_mod) { + struct rsnd_priv *priv = rsnd_mod_to_priv(ssi_mod); + struct rsnd_adg *adg = rsnd_priv_to_adg(priv); + int id = rsnd_mod_id(ssi_mod); + rsnd_adg_set_ssi_clk(ssi_mod, 0); + /* RZ/G3E: only disable here, unprepare is done in hw_free */ + clk_disable(adg->clk_adg_ssi[id]); + clk_disable(adg->clk_ssif_supply); + return 0; } @@ -354,7 +369,8 @@ int rsnd_adg_ssi_clk_try_start(struct rsnd_mod *ssi_mod, unsigned int rate) struct rsnd_adg *adg = rsnd_priv_to_adg(priv); struct device *dev = rsnd_priv_to_dev(priv); struct rsnd_mod *adg_mod = rsnd_mod_get(adg); - int data; + int id = rsnd_mod_id(ssi_mod); + int ret, data; u32 ckr = 0; data = rsnd_adg_clk_query(priv, rate); @@ -376,9 +392,63 @@ int rsnd_adg_ssi_clk_try_start(struct rsnd_mod *ssi_mod, unsigned int rate) (ckr) ? adg->brg_rate[ADG_HZ_48] : adg->brg_rate[ADG_HZ_441]); + /* + * RZ/G3E: enable per-SSI and supply clocks + */ + ret = clk_enable(adg->clk_adg_ssi[id]); + if (ret) { + dev_err(dev, "Cannot enable adg-ssi-%d ADG clock\n", id); + return ret; + } + + ret = clk_enable(adg->clk_ssif_supply); + if (ret) { + dev_err(dev, "Cannot enable SSIF supply clock\n"); + clk_disable(adg->clk_adg_ssi[id]); + return ret; + } + return 0; } +static int rsnd_adg_ssi_clk_prepare(struct rsnd_adg *adg) +{ + int i, ret; + + if (adg->ssi_clk_prepared) + return 0; + + for (i = 0; i < ADG_SSI_MAX; i++) { + ret = clk_prepare(adg->clk_adg_ssi[i]); + if (ret) + goto unwind; + } + ret = clk_prepare(adg->clk_ssif_supply); + if (ret) + goto unwind; + + adg->ssi_clk_prepared = true; + return 0; + +unwind: + while (i--) + clk_unprepare(adg->clk_adg_ssi[i]); + return ret; +} + +static void rsnd_adg_ssi_clk_unprepare(struct rsnd_adg *adg) +{ + int i; + + if (!adg->ssi_clk_prepared) + return; + adg->ssi_clk_prepared = false; + + clk_unprepare(adg->clk_ssif_supply); + for (i = 0; i < ADG_SSI_MAX; i++) + clk_unprepare(adg->clk_adg_ssi[i]); +} + int rsnd_adg_clk_control(struct rsnd_priv *priv, int enable) { struct rsnd_adg *adg = rsnd_priv_to_adg(priv); @@ -421,6 +491,28 @@ 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 */ + /* + * RZ/G3E per-SSI ADG and SSIF supply clocks. + * + * Follow the same style as for_each_rsnd_clkin() above: on enable, + * try to prepare every clock and accumulate the error. On disable, + * unprepare every clock. Absent optional clocks are NULL, for + * which clk_prepare() and clk_unprepare() are no-ops. + */ + if (enable) { + int sub_ret = rsnd_adg_ssi_clk_prepare(adg); + + /* Preserve the first error from the clkin loop above. */ + if (sub_ret && !ret) + ret = sub_ret; + } else { + rsnd_adg_ssi_clk_unprepare(adg); + } + + /* + * rsnd_adg_clk_enable() might return error (_disable() will not). + * We need to rollback in such case + */ if (ret < 0) rsnd_adg_clk_disable(priv); @@ -769,6 +861,31 @@ void rsnd_adg_clk_dbg_info(struct rsnd_priv *priv, struct seq_file *m) #define rsnd_adg_clk_dbg_info(priv, m) #endif +static int rsnd_adg_get_ssi_clks(struct rsnd_priv *priv) +{ + struct rsnd_adg *adg = rsnd_priv_to_adg(priv); + struct device *dev = rsnd_priv_to_dev(priv); + char name[16]; + int i; + + /* SSIF supply clock */ + adg->clk_ssif_supply = devm_clk_get_optional(dev, "ssif_supply"); + if (IS_ERR(adg->clk_ssif_supply)) + return dev_err_probe(dev, PTR_ERR(adg->clk_ssif_supply), + "failed to get ssif_supply clock\n"); + + /* Per-SSI ADG clocks (RZ/G3E-only; no legacy dotted form exists) */ + for (i = 0; i < ADG_SSI_MAX; i++) { + snprintf(name, sizeof(name), "adg-ssi-%d", i); + adg->clk_adg_ssi[i] = devm_clk_get_optional(dev, name); + if (IS_ERR(adg->clk_adg_ssi[i])) + return dev_err_probe(dev, PTR_ERR(adg->clk_adg_ssi[i]), + "failed to get %s clock\n", name); + } + + return 0; +} + int rsnd_adg_probe(struct rsnd_priv *priv) { struct reset_control *rstc; @@ -798,6 +915,11 @@ int rsnd_adg_probe(struct rsnd_priv *priv) if (ret) return ret; + /* RZ/G3E-specific: per-SSI ADG and SSIF supply clocks */ + ret = rsnd_adg_get_ssi_clks(priv); + if (ret) + return ret; + ret = rsnd_adg_clk_enable(priv); if (ret) return ret; |
