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>
This commit is contained in:
John Madieu 2026-05-25 11:02:25 +00:00 committed by Mark Brown
parent 9267b89985
commit 47899d53f8
No known key found for this signature in database
GPG Key ID: 24D68B725D5487D0

View File

@ -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);
@ -417,6 +487,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
@ -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;