Add PLLDSI clk mux support to select PLLDSI clock from different clock
sources.
Introduce the DEF_PLLDSI_SMUX() macro to define these muxes and register
them in the clock driver.
Extend the determine_rate callback to calculate and propagate PLL
parameters via rzv2h_get_pll_dtable_pars() when LVDS output is selected,
using a new helper function rzv2h_cpg_plldsi_smux_lvds_determine_rate().
Signed-off-by: Tommaso Merciai <tommaso.merciai.xr@bp.renesas.com>
---
drivers/clk/renesas/rzv2h-cpg.c | 131 ++++++++++++++++++++++++++++++++
drivers/clk/renesas/rzv2h-cpg.h | 8 ++
2 files changed, 139 insertions(+)
diff --git a/drivers/clk/renesas/rzv2h-cpg.c b/drivers/clk/renesas/rzv2h-cpg.c
index 3f6299b9fec0..dd782fa269d7 100644
--- a/drivers/clk/renesas/rzv2h-cpg.c
+++ b/drivers/clk/renesas/rzv2h-cpg.c
@@ -418,6 +418,20 @@ bool rzv2h_get_pll_divs_pars(const struct rzv2h_pll_limits *limits,
}
EXPORT_SYMBOL_NS_GPL(rzv2h_get_pll_divs_pars, "RZV2H_CPG");
+/**
+ * struct rzv2h_plldsi_mux_clk - PLL DSI MUX clock
+ *
+ * @priv: CPG private data
+ * @mux: mux clk
+ */
+struct rzv2h_plldsi_mux_clk {
+ struct rzv2h_cpg_priv *priv;
+ struct clk_mux mux;
+};
+
+#define to_plldsi_clk_mux(_mux) \
+ container_of(_mux, struct rzv2h_plldsi_mux_clk, mux)
+
static unsigned long rzv2h_cpg_plldsi_div_recalc_rate(struct clk_hw *hw,
unsigned long parent_rate)
{
@@ -649,6 +663,120 @@ static int rzv2h_cpg_plldsi_set_rate(struct clk_hw *hw, unsigned long rate,
return rzv2h_cpg_pll_set_rate(pll_clk, &dsi_info->pll_dsi_parameters.pll, true);
}
+static u8 rzv2h_cpg_plldsi_smux_get_parent(struct clk_hw *hw)
+{
+ return clk_mux_ops.get_parent(hw);
+}
+
+static int rzv2h_cpg_plldsi_smux_set_parent(struct clk_hw *hw, u8 index)
+{
+ return clk_mux_ops.set_parent(hw, index);
+}
+
+static int rzv2h_cpg_plldsi_smux_lvds_determine_rate(struct rzv2h_cpg_priv *priv,
+ struct pll_clk *pll_clk,
+ struct clk_rate_request *req)
+{
+ struct rzv2h_pll_div_pars *dsi_params;
+ struct rzv2h_pll_dsi_info *dsi_info;
+ u8 lvds_table[] = { 7 };
+ u64 rate_millihz;
+
+ dsi_info = &priv->pll_dsi_info[pll_clk->pll.instance];
+ dsi_params = &dsi_info->pll_dsi_parameters;
+
+ rate_millihz = mul_u32_u32(req->rate, MILLI);
+ if (!rzv2h_get_pll_divs_pars(dsi_info->pll_dsi_limits, dsi_params,
+ lvds_table, 1, rate_millihz)) {
+ dev_err(priv->dev, "failed to determine rate for req->rate: %lu\n",
+ req->rate);
+ return -EINVAL;
+ }
+
+ req->rate = DIV_ROUND_CLOSEST_ULL(dsi_params->div.freq_millihz, MILLI);
+ req->best_parent_rate = req->rate;
+ dsi_info->req_pll_dsi_rate = req->best_parent_rate * dsi_params->div.divider_value;
+
+ return 0;
+}
+
+static int rzv2h_cpg_plldsi_smux_determine_rate(struct clk_hw *hw,
+ struct clk_rate_request *req)
+{
+ struct clk_mux *mux = to_clk_mux(hw);
+ struct rzv2h_plldsi_mux_clk *dsi_mux = to_plldsi_clk_mux(mux);
+ struct pll_clk *pll_clk = to_pll(clk_hw_get_parent(hw));
+ struct rzv2h_cpg_priv *priv = dsi_mux->priv;
+ int ret;
+
+ /*
+ * For LVDS output (parent_idx == 0), calculate PLL parameters with
+ * fixed divider value of 7. For DSI/RGB output (parent_idx == 1) skip
+ * PLL calculation here as it's handled by determine_rate of the
+ * divider (up one level).
+ */
+ if (!clk_mux_ops.get_parent(hw))
+ ret = rzv2h_cpg_plldsi_smux_lvds_determine_rate(priv, pll_clk, req);
+ else
+ ret = clk_mux_determine_rate_flags(hw, req, mux->flags);
+
+ return ret;
+}
+
+static const struct clk_ops rzv2h_cpg_plldsi_smux_ops = {
+ .determine_rate = rzv2h_cpg_plldsi_smux_determine_rate,
+ .get_parent = rzv2h_cpg_plldsi_smux_get_parent,
+ .set_parent = rzv2h_cpg_plldsi_smux_set_parent,
+};
+
+static struct clk * __init
+rzv2h_cpg_plldsi_smux_clk_register(const struct cpg_core_clk *core,
+ struct rzv2h_cpg_priv *priv)
+{
+ struct rzv2h_plldsi_mux_clk *clk_hw_data;
+ struct clk_init_data init;
+ struct clk_hw *clk_hw;
+ struct smuxed smux;
+ u8 width;
+ int ret;
+
+ smux = core->cfg.smux;
+ width = fls(smux.width) - ffs(smux.width) + 1;
+
+ if (width + smux.width > 16) {
+ dev_err(priv->dev, "mux value exceeds LOWORD field\n");
+ return ERR_PTR(-EINVAL);
+ }
+
+ clk_hw_data = devm_kzalloc(priv->dev, sizeof(*clk_hw_data), GFP_KERNEL);
+ if (!clk_hw_data)
+ return ERR_PTR(-ENOMEM);
+
+ clk_hw_data->priv = priv;
+
+ init.name = core->name;
+ init.ops = &rzv2h_cpg_plldsi_smux_ops;
+ init.flags = core->flag;
+ init.parent_names = core->parent_names;
+ init.num_parents = core->num_parents;
+
+ clk_hw_data->mux.reg = priv->base + smux.offset;
+
+ clk_hw_data->mux.shift = smux.shift;
+ clk_hw_data->mux.mask = smux.width;
+ clk_hw_data->mux.flags = core->mux_flags;
+ clk_hw_data->mux.lock = &priv->rmw_lock;
+
+ clk_hw = &clk_hw_data->mux.hw;
+ clk_hw->init = &init;
+
+ ret = devm_clk_hw_register(priv->dev, clk_hw);
+ if (ret)
+ return ERR_PTR(ret);
+
+ return clk_hw->clk;
+}
+
static int rzv2h_cpg_pll_clk_is_enabled(struct clk_hw *hw)
{
struct pll_clk *pll_clk = to_pll(hw);
@@ -1085,6 +1213,9 @@ rzv2h_cpg_register_core_clk(const struct cpg_core_clk *core,
case CLK_TYPE_PLLDSI_DIV:
clk = rzv2h_cpg_plldsi_div_clk_register(core, priv);
break;
+ case CLK_TYPE_PLLDSI_SMUX:
+ clk = rzv2h_cpg_plldsi_smux_clk_register(core, priv);
+ break;
default:
goto fail;
}
diff --git a/drivers/clk/renesas/rzv2h-cpg.h b/drivers/clk/renesas/rzv2h-cpg.h
index dc957bdaf5e9..5f6e775612e7 100644
--- a/drivers/clk/renesas/rzv2h-cpg.h
+++ b/drivers/clk/renesas/rzv2h-cpg.h
@@ -203,6 +203,7 @@ enum clk_types {
CLK_TYPE_SMUX, /* Static Mux */
CLK_TYPE_PLLDSI, /* PLLDSI */
CLK_TYPE_PLLDSI_DIV, /* PLLDSI divider */
+ CLK_TYPE_PLLDSI_SMUX, /* PLLDSI Static Mux */
};
#define DEF_TYPE(_name, _id, _type...) \
@@ -241,6 +242,13 @@ enum clk_types {
.dtable = _dtable, \
.parent = _parent, \
.flag = CLK_SET_RATE_PARENT)
+#define DEF_PLLDSI_SMUX(_name, _id, _smux_packed, _parent_names) \
+ DEF_TYPE(_name, _id, CLK_TYPE_PLLDSI_SMUX, \
+ .cfg.smux = _smux_packed, \
+ .parent_names = _parent_names, \
+ .num_parents = ARRAY_SIZE(_parent_names), \
+ .flag = CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT, \
+ .mux_flags = CLK_MUX_HIWORD_MASK)
/**
* struct rzv2h_mod_clk - Module Clocks definitions
--
2.43.0
Hi Tommaso,
On Wed, 26 Nov 2025 at 15:08, Tommaso Merciai
<tommaso.merciai.xr@bp.renesas.com> wrote:
> Add PLLDSI clk mux support to select PLLDSI clock from different clock
> sources.
>
> Introduce the DEF_PLLDSI_SMUX() macro to define these muxes and register
> them in the clock driver.
>
> Extend the determine_rate callback to calculate and propagate PLL
> parameters via rzv2h_get_pll_dtable_pars() when LVDS output is selected,
> using a new helper function rzv2h_cpg_plldsi_smux_lvds_determine_rate().
>
> Signed-off-by: Tommaso Merciai <tommaso.merciai.xr@bp.renesas.com>
Thanks for your patch!
> --- a/drivers/clk/renesas/rzv2h-cpg.c
> +++ b/drivers/clk/renesas/rzv2h-cpg.c
> +
> +static int rzv2h_cpg_plldsi_smux_lvds_determine_rate(struct rzv2h_cpg_priv *priv,
> + struct pll_clk *pll_clk,
> + struct clk_rate_request *req)
> +{
> + struct rzv2h_pll_div_pars *dsi_params;
> + struct rzv2h_pll_dsi_info *dsi_info;
> + u8 lvds_table[] = { 7 };
> + u64 rate_millihz;
> +
> + dsi_info = &priv->pll_dsi_info[pll_clk->pll.instance];
> + dsi_params = &dsi_info->pll_dsi_parameters;
> +
> + rate_millihz = mul_u32_u32(req->rate, MILLI);
> + if (!rzv2h_get_pll_divs_pars(dsi_info->pll_dsi_limits, dsi_params,
> + lvds_table, 1, rate_millihz)) {
s/1/ARRAY_SIZE(lvds_table)/
> + dev_err(priv->dev, "failed to determine rate for req->rate: %lu\n",
> + req->rate);
> + return -EINVAL;
> + }
> +
> + req->rate = DIV_ROUND_CLOSEST_ULL(dsi_params->div.freq_millihz, MILLI);
> + req->best_parent_rate = req->rate;
> + dsi_info->req_pll_dsi_rate = req->best_parent_rate * dsi_params->div.divider_value;
> +
> + return 0;
> +}
Gr{oetje,eeting}s,
Geert
--
Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- geert@linux-m68k.org
In personal conversations with technical people, I call myself a hacker. But
when I'm talking to journalists I just say "programmer" or something like that.
-- Linus Torvalds
Hi Tommaso,
On Wed, 26 Nov 2025 at 15:08, Tommaso Merciai
<tommaso.merciai.xr@bp.renesas.com> wrote:
> Add PLLDSI clk mux support to select PLLDSI clock from different clock
> sources.
>
> Introduce the DEF_PLLDSI_SMUX() macro to define these muxes and register
> them in the clock driver.
>
> Extend the determine_rate callback to calculate and propagate PLL
> parameters via rzv2h_get_pll_dtable_pars() when LVDS output is selected,
> using a new helper function rzv2h_cpg_plldsi_smux_lvds_determine_rate().
>
> Signed-off-by: Tommaso Merciai <tommaso.merciai.xr@bp.renesas.com>
Thanks for your patch!
> --- a/drivers/clk/renesas/rzv2h-cpg.c
> +++ b/drivers/clk/renesas/rzv2h-cpg.c
[...]
> static int rzv2h_cpg_pll_clk_is_enabled(struct clk_hw *hw)
> {
> struct pll_clk *pll_clk = to_pll(hw);
> @@ -1085,6 +1213,9 @@ rzv2h_cpg_register_core_clk(const struct cpg_core_clk *core,
> case CLK_TYPE_PLLDSI_DIV:
> clk = rzv2h_cpg_plldsi_div_clk_register(core, priv);
> break;
> + case CLK_TYPE_PLLDSI_SMUX:
> + clk = rzv2h_cpg_plldsi_smux_clk_register(core, priv);
> + break;
> default:
> goto fail;
> }
> diff --git a/drivers/clk/renesas/rzv2h-cpg.h b/drivers/clk/renesas/rzv2h-cpg.h
> index dc957bdaf5e9..5f6e775612e7 100644
> --- a/drivers/clk/renesas/rzv2h-cpg.h
> +++ b/drivers/clk/renesas/rzv2h-cpg.h
> @@ -203,6 +203,7 @@ enum clk_types {
> CLK_TYPE_SMUX, /* Static Mux */
> CLK_TYPE_PLLDSI, /* PLLDSI */
> CLK_TYPE_PLLDSI_DIV, /* PLLDSI divider */
> + CLK_TYPE_PLLDSI_SMUX, /* PLLDSI Static Mux */
> };
>
> #define DEF_TYPE(_name, _id, _type...) \
> @@ -241,6 +242,13 @@ enum clk_types {
> .dtable = _dtable, \
> .parent = _parent, \
> .flag = CLK_SET_RATE_PARENT)
> +#define DEF_PLLDSI_SMUX(_name, _id, _smux_packed, _parent_names) \
> + DEF_TYPE(_name, _id, CLK_TYPE_PLLDSI_SMUX, \
> + .cfg.smux = _smux_packed, \
> + .parent_names = _parent_names, \
> + .num_parents = ARRAY_SIZE(_parent_names), \
> + .flag = CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT, \
> + .mux_flags = CLK_MUX_HIWORD_MASK)
>
> /**
> * struct rzv2h_mod_clk - Module Clocks definitions
Why do you need a completely new clock type, and can't you just use
the existing CLK_TYPE_SMUX?
Gr{oetje,eeting}s,
Geert
--
Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- geert@linux-m68k.org
In personal conversations with technical people, I call myself a hacker. But
when I'm talking to journalists I just say "programmer" or something like that.
-- Linus Torvalds
Hi Geert,
Thanks for your review!
On Fri, Jan 09, 2026 at 07:27:04PM +0100, Geert Uytterhoeven wrote:
> Hi Tommaso,
>
> On Wed, 26 Nov 2025 at 15:08, Tommaso Merciai
> <tommaso.merciai.xr@bp.renesas.com> wrote:
> > Add PLLDSI clk mux support to select PLLDSI clock from different clock
> > sources.
> >
> > Introduce the DEF_PLLDSI_SMUX() macro to define these muxes and register
> > them in the clock driver.
> >
> > Extend the determine_rate callback to calculate and propagate PLL
> > parameters via rzv2h_get_pll_dtable_pars() when LVDS output is selected,
> > using a new helper function rzv2h_cpg_plldsi_smux_lvds_determine_rate().
> >
> > Signed-off-by: Tommaso Merciai <tommaso.merciai.xr@bp.renesas.com>
>
> Thanks for your patch!
>
> > --- a/drivers/clk/renesas/rzv2h-cpg.c
> > +++ b/drivers/clk/renesas/rzv2h-cpg.c
>
> [...]
>
> > static int rzv2h_cpg_pll_clk_is_enabled(struct clk_hw *hw)
> > {
> > struct pll_clk *pll_clk = to_pll(hw);
> > @@ -1085,6 +1213,9 @@ rzv2h_cpg_register_core_clk(const struct cpg_core_clk *core,
> > case CLK_TYPE_PLLDSI_DIV:
> > clk = rzv2h_cpg_plldsi_div_clk_register(core, priv);
> > break;
> > + case CLK_TYPE_PLLDSI_SMUX:
> > + clk = rzv2h_cpg_plldsi_smux_clk_register(core, priv);
> > + break;
> > default:
> > goto fail;
> > }
> > diff --git a/drivers/clk/renesas/rzv2h-cpg.h b/drivers/clk/renesas/rzv2h-cpg.h
> > index dc957bdaf5e9..5f6e775612e7 100644
> > --- a/drivers/clk/renesas/rzv2h-cpg.h
> > +++ b/drivers/clk/renesas/rzv2h-cpg.h
> > @@ -203,6 +203,7 @@ enum clk_types {
> > CLK_TYPE_SMUX, /* Static Mux */
> > CLK_TYPE_PLLDSI, /* PLLDSI */
> > CLK_TYPE_PLLDSI_DIV, /* PLLDSI divider */
> > + CLK_TYPE_PLLDSI_SMUX, /* PLLDSI Static Mux */
> > };
> >
> > #define DEF_TYPE(_name, _id, _type...) \
> > @@ -241,6 +242,13 @@ enum clk_types {
> > .dtable = _dtable, \
> > .parent = _parent, \
> > .flag = CLK_SET_RATE_PARENT)
> > +#define DEF_PLLDSI_SMUX(_name, _id, _smux_packed, _parent_names) \
> > + DEF_TYPE(_name, _id, CLK_TYPE_PLLDSI_SMUX, \
> > + .cfg.smux = _smux_packed, \
> > + .parent_names = _parent_names, \
> > + .num_parents = ARRAY_SIZE(_parent_names), \
> > + .flag = CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT, \
> > + .mux_flags = CLK_MUX_HIWORD_MASK)
> >
> > /**
> > * struct rzv2h_mod_clk - Module Clocks definitions
>
> Why do you need a completely new clock type, and can't you just use
> the existing CLK_TYPE_SMUX?
From reference manual (Table 4.4-10 Specifications of the CPG_SSELm
Registers)
We have the following:
- SMUX2_DSI0_CLK*2
0b: CDIV7_DSI0_CLK (default)
1b: CSDIV_2to16_PLLDSI0
- SMUX2_DSI1_CLK*2
0b: CDIV7_DSI1_CLK (default)
1b: CSDIV_2to16_PLLDSI1
Note 2.If LVDS0 / LVDS1 is used, be sure to set 0b.
For this reason these clocks needs an ad hoc determine_rate function:
- rzv2h_cpg_plldsi_smux_determine_rate()
For that CLK_TYPE_PLLDSI_SMUX has been introduced.
What do you think?
Thanks & Regards,
Tommaso
>
> Gr{oetje,eeting}s,
>
> Geert
>
> --
> Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- geert@linux-m68k.org
>
> In personal conversations with technical people, I call myself a hacker. But
> when I'm talking to journalists I just say "programmer" or something like that.
> -- Linus Torvalds
Hi Tommaso,
On Mon, 12 Jan 2026 at 09:13, Tommaso Merciai
<tommaso.merciai.xr@bp.renesas.com> wrote:
> On Fri, Jan 09, 2026 at 07:27:04PM +0100, Geert Uytterhoeven wrote:
> > On Wed, 26 Nov 2025 at 15:08, Tommaso Merciai
> > <tommaso.merciai.xr@bp.renesas.com> wrote:
> > > Add PLLDSI clk mux support to select PLLDSI clock from different clock
> > > sources.
> > >
> > > Introduce the DEF_PLLDSI_SMUX() macro to define these muxes and register
> > > them in the clock driver.
> > >
> > > Extend the determine_rate callback to calculate and propagate PLL
> > > parameters via rzv2h_get_pll_dtable_pars() when LVDS output is selected,
> > > using a new helper function rzv2h_cpg_plldsi_smux_lvds_determine_rate().
> > >
> > > Signed-off-by: Tommaso Merciai <tommaso.merciai.xr@bp.renesas.com>
> >
> > Thanks for your patch!
> >
> > > --- a/drivers/clk/renesas/rzv2h-cpg.c
> > > +++ b/drivers/clk/renesas/rzv2h-cpg.c
> >
> > [...]
> >
> > > static int rzv2h_cpg_pll_clk_is_enabled(struct clk_hw *hw)
> > > {
> > > struct pll_clk *pll_clk = to_pll(hw);
> > > @@ -1085,6 +1213,9 @@ rzv2h_cpg_register_core_clk(const struct cpg_core_clk *core,
> > > case CLK_TYPE_PLLDSI_DIV:
> > > clk = rzv2h_cpg_plldsi_div_clk_register(core, priv);
> > > break;
> > > + case CLK_TYPE_PLLDSI_SMUX:
> > > + clk = rzv2h_cpg_plldsi_smux_clk_register(core, priv);
> > > + break;
> > > default:
> > > goto fail;
> > > }
> > > diff --git a/drivers/clk/renesas/rzv2h-cpg.h b/drivers/clk/renesas/rzv2h-cpg.h
> > > index dc957bdaf5e9..5f6e775612e7 100644
> > > --- a/drivers/clk/renesas/rzv2h-cpg.h
> > > +++ b/drivers/clk/renesas/rzv2h-cpg.h
> > > @@ -203,6 +203,7 @@ enum clk_types {
> > > CLK_TYPE_SMUX, /* Static Mux */
> > > CLK_TYPE_PLLDSI, /* PLLDSI */
> > > CLK_TYPE_PLLDSI_DIV, /* PLLDSI divider */
> > > + CLK_TYPE_PLLDSI_SMUX, /* PLLDSI Static Mux */
> > > };
> > >
> > > #define DEF_TYPE(_name, _id, _type...) \
> > > @@ -241,6 +242,13 @@ enum clk_types {
> > > .dtable = _dtable, \
> > > .parent = _parent, \
> > > .flag = CLK_SET_RATE_PARENT)
> > > +#define DEF_PLLDSI_SMUX(_name, _id, _smux_packed, _parent_names) \
> > > + DEF_TYPE(_name, _id, CLK_TYPE_PLLDSI_SMUX, \
> > > + .cfg.smux = _smux_packed, \
> > > + .parent_names = _parent_names, \
> > > + .num_parents = ARRAY_SIZE(_parent_names), \
> > > + .flag = CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT, \
> > > + .mux_flags = CLK_MUX_HIWORD_MASK)
> > >
> > > /**
> > > * struct rzv2h_mod_clk - Module Clocks definitions
> >
> > Why do you need a completely new clock type, and can't you just use
> > the existing CLK_TYPE_SMUX?
>
> From reference manual (Table 4.4-10 Specifications of the CPG_SSELm
> Registers)
>
> We have the following:
>
> - SMUX2_DSI0_CLK*2
> 0b: CDIV7_DSI0_CLK (default)
> 1b: CSDIV_2to16_PLLDSI0
>
> - SMUX2_DSI1_CLK*2
> 0b: CDIV7_DSI1_CLK (default)
> 1b: CSDIV_2to16_PLLDSI1
>
> Note 2.If LVDS0 / LVDS1 is used, be sure to set 0b.
>
> For this reason these clocks needs an ad hoc determine_rate function:
> - rzv2h_cpg_plldsi_smux_determine_rate()
>
> For that CLK_TYPE_PLLDSI_SMUX has been introduced.
> What do you think?
OK, your solution sounds good to me. Still, as this is used from the
DRM driver, I would like to get some feedback from the DRM people, too.
BTW, I just noticed in the RZ/G3E clock system diagram that
CDIV7_DSI0_CLK has a duty cycle "DUTY H/L=4/3", while all other clocks
use the symmetrical 50%. Perhaps the DRM driver can request a duty
cycle of 4/7 when using LVDS? Currently the DRM driver communicates
its requirements by explicitly setting the parent.
Gr{oetje,eeting}s,
Geert
--
Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- geert@linux-m68k.org
In personal conversations with technical people, I call myself a hacker. But
when I'm talking to journalists I just say "programmer" or something like that.
-- Linus Torvalds
Hi Geert,
Thanks for your comment.
On Wed, Jan 14, 2026 at 02:07:49PM +0100, Geert Uytterhoeven wrote:
> Hi Tommaso,
>
> On Mon, 12 Jan 2026 at 09:13, Tommaso Merciai
> <tommaso.merciai.xr@bp.renesas.com> wrote:
> > On Fri, Jan 09, 2026 at 07:27:04PM +0100, Geert Uytterhoeven wrote:
> > > On Wed, 26 Nov 2025 at 15:08, Tommaso Merciai
> > > <tommaso.merciai.xr@bp.renesas.com> wrote:
> > > > Add PLLDSI clk mux support to select PLLDSI clock from different clock
> > > > sources.
> > > >
> > > > Introduce the DEF_PLLDSI_SMUX() macro to define these muxes and register
> > > > them in the clock driver.
> > > >
> > > > Extend the determine_rate callback to calculate and propagate PLL
> > > > parameters via rzv2h_get_pll_dtable_pars() when LVDS output is selected,
> > > > using a new helper function rzv2h_cpg_plldsi_smux_lvds_determine_rate().
> > > >
> > > > Signed-off-by: Tommaso Merciai <tommaso.merciai.xr@bp.renesas.com>
> > >
> > > Thanks for your patch!
> > >
> > > > --- a/drivers/clk/renesas/rzv2h-cpg.c
> > > > +++ b/drivers/clk/renesas/rzv2h-cpg.c
> > >
> > > [...]
> > >
> > > > static int rzv2h_cpg_pll_clk_is_enabled(struct clk_hw *hw)
> > > > {
> > > > struct pll_clk *pll_clk = to_pll(hw);
> > > > @@ -1085,6 +1213,9 @@ rzv2h_cpg_register_core_clk(const struct cpg_core_clk *core,
> > > > case CLK_TYPE_PLLDSI_DIV:
> > > > clk = rzv2h_cpg_plldsi_div_clk_register(core, priv);
> > > > break;
> > > > + case CLK_TYPE_PLLDSI_SMUX:
> > > > + clk = rzv2h_cpg_plldsi_smux_clk_register(core, priv);
> > > > + break;
> > > > default:
> > > > goto fail;
> > > > }
> > > > diff --git a/drivers/clk/renesas/rzv2h-cpg.h b/drivers/clk/renesas/rzv2h-cpg.h
> > > > index dc957bdaf5e9..5f6e775612e7 100644
> > > > --- a/drivers/clk/renesas/rzv2h-cpg.h
> > > > +++ b/drivers/clk/renesas/rzv2h-cpg.h
> > > > @@ -203,6 +203,7 @@ enum clk_types {
> > > > CLK_TYPE_SMUX, /* Static Mux */
> > > > CLK_TYPE_PLLDSI, /* PLLDSI */
> > > > CLK_TYPE_PLLDSI_DIV, /* PLLDSI divider */
> > > > + CLK_TYPE_PLLDSI_SMUX, /* PLLDSI Static Mux */
> > > > };
> > > >
> > > > #define DEF_TYPE(_name, _id, _type...) \
> > > > @@ -241,6 +242,13 @@ enum clk_types {
> > > > .dtable = _dtable, \
> > > > .parent = _parent, \
> > > > .flag = CLK_SET_RATE_PARENT)
> > > > +#define DEF_PLLDSI_SMUX(_name, _id, _smux_packed, _parent_names) \
> > > > + DEF_TYPE(_name, _id, CLK_TYPE_PLLDSI_SMUX, \
> > > > + .cfg.smux = _smux_packed, \
> > > > + .parent_names = _parent_names, \
> > > > + .num_parents = ARRAY_SIZE(_parent_names), \
> > > > + .flag = CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT, \
> > > > + .mux_flags = CLK_MUX_HIWORD_MASK)
> > > >
> > > > /**
> > > > * struct rzv2h_mod_clk - Module Clocks definitions
> > >
> > > Why do you need a completely new clock type, and can't you just use
> > > the existing CLK_TYPE_SMUX?
> >
> > From reference manual (Table 4.4-10 Specifications of the CPG_SSELm
> > Registers)
> >
> > We have the following:
> >
> > - SMUX2_DSI0_CLK*2
> > 0b: CDIV7_DSI0_CLK (default)
> > 1b: CSDIV_2to16_PLLDSI0
> >
> > - SMUX2_DSI1_CLK*2
> > 0b: CDIV7_DSI1_CLK (default)
> > 1b: CSDIV_2to16_PLLDSI1
> >
> > Note 2.If LVDS0 / LVDS1 is used, be sure to set 0b.
> >
> > For this reason these clocks needs an ad hoc determine_rate function:
> > - rzv2h_cpg_plldsi_smux_determine_rate()
> >
> > For that CLK_TYPE_PLLDSI_SMUX has been introduced.
> > What do you think?
>
> OK, your solution sounds good to me. Still, as this is used from the
> DRM driver, I would like to get some feedback from the DRM people, too.
>
> BTW, I just noticed in the RZ/G3E clock system diagram that
> CDIV7_DSI0_CLK has a duty cycle "DUTY H/L=4/3", while all other clocks
> use the symmetrical 50%. Perhaps the DRM driver can request a duty
> cycle of 4/7 when using LVDS? Currently the DRM driver communicates
> its requirements by explicitly setting the parent.
Based on your idea we can add at cpg lvl:
.get_duty_cycle = rzv2h_cpg_plldsi_smux_get_duty_cycle,
.set_duty_cycle = rzv2h_cpg_plldsi_smux_set_duty_cycle,
That select parent based on requested duty cycle:
- If duty > 50% (num/den > 1/2), select LVDS path (parent 0)
- Otherwise, select DSI/RGB path (parent 1)
Then at DRM lvl we can go for:
if (rzg2l_du_has(rcdu, RG2L_DU_FEATURE_SMUX2_DSI_CLK)) {
struct clk *clk_parent;
clk_parent = clk_get_parent(rcrtc->rzg2l_clocks.dclk);
/*
* Request appropriate duty cycle to let clock driver select
* the correct parent:
* - CDIV7_DSIx_CLK (LVDS path) has DUTY H/L=4/3, 4/7 duty cycle.
* - CSDIV_2to16_PLLDSIx (DSI/RGB path) has symmetric 50% duty cycle.
*/
if (rstate->outputs == BIT(RZG2L_DU_OUTPUT_LVDS0) ||
rstate->outputs == BIT(RZG2L_DU_OUTPUT_LVDS1))
clk_set_duty_cycle(clk_parent, 4, 7);
else
clk_set_duty_cycle(clk_parent, 1, 2);
}
What do you think? Please correct me if I'm wrong.
Glad to hear from Laurent's input too.
Thank you both in advance.
Kind Regards,
Tommaso
>
> Gr{oetje,eeting}s,
>
> Geert
>
> --
> Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- geert@linux-m68k.org
>
> In personal conversations with technical people, I call myself a hacker. But
> when I'm talking to journalists I just say "programmer" or something like that.
> -- Linus Torvalds
>
© 2016 - 2026 Red Hat, Inc.