mirror of
https://github.com/torvalds/linux.git
synced 2026-07-27 17:47:41 +02:00
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:
parent
9267b89985
commit
47899d53f8
|
|
@ -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;
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user