mirror of
https://github.com/torvalds/linux.git
synced 2026-07-28 10:09:10 +02:00
clk: renesas: rzv2h: Add PLLDSI clk mux support
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().
The CLK_SMUX2_DSI{0,1}_CLK clock multiplexers select between two paths
with different duty cycles:
- CDIV7_DSIx_CLK (LVDS path, parent index 0): asymmetric H/L=4/3 duty (4/7)
- CSDIV_DSIx (DSI/RGB path, parent index 1): symmetric 50% duty (1/2)
Implement rzv2h_cpg_plldsi_smux_{get,set}_duty_cycle clock operations to
allow the DRM driver to query and configure the appropriate clock path
based on the required output duty cycle.
Signed-off-by: Tommaso Merciai <tommaso.merciai.xr@bp.renesas.com>
Reviewed-by: Geert Uytterhoeven <geert+renesas@glider.be>
Link: https://patch.msgid.link/24d3853ca2522df21e6a071a23e23ba4ca4b7276.1775636898.git.tommaso.merciai.xr@bp.renesas.com
Signed-off-by: Geert Uytterhoeven <geert+renesas@glider.be>
This commit is contained in:
parent
254f49634e
commit
3afccee4a3
|
|
@ -76,6 +76,11 @@
|
|||
/* On RZ/G3E SoC we have two DSI PLLs */
|
||||
#define MAX_CPG_DSI_PLL 2
|
||||
|
||||
#define CPG_PLLDSI_SMUX_LVDS_DUTY_NUM 4
|
||||
#define CPG_PLLDSI_SMUX_LVDS_DUTY_DEN 7
|
||||
#define CPG_PLLDSI_SMUX_DSI_RGB_DUTY_NUM 1
|
||||
#define CPG_PLLDSI_SMUX_DSI_RGB_DUTY_DEN 2
|
||||
|
||||
/**
|
||||
* struct rzv2h_pll_dsi_info - PLL DSI information, holds the limits and parameters
|
||||
*
|
||||
|
|
@ -418,6 +423,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 +668,165 @@ 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, ARRAY_SIZE(lvds_table), 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;
|
||||
|
||||
/*
|
||||
* For LVDS output (parent index 0), calculate PLL parameters with
|
||||
* fixed divider value of 7. For DSI/RGB output (parent index 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))
|
||||
return rzv2h_cpg_plldsi_smux_lvds_determine_rate(priv, pll_clk, req);
|
||||
|
||||
req->best_parent_rate = req->rate;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rzv2h_cpg_plldsi_smux_get_duty_cycle(struct clk_hw *hw,
|
||||
struct clk_duty *duty)
|
||||
{
|
||||
u8 parent = clk_mux_ops.get_parent(hw);
|
||||
|
||||
/*
|
||||
* CDIV7_DSIx_CLK - LVDS path (div7) - duty 4/7.
|
||||
* CSDIV_DSIx - DSI/RGB path (csdiv) - duty 1/2.
|
||||
*/
|
||||
if (parent == 0) {
|
||||
duty->num = CPG_PLLDSI_SMUX_LVDS_DUTY_NUM;
|
||||
duty->den = CPG_PLLDSI_SMUX_LVDS_DUTY_DEN;
|
||||
} else {
|
||||
duty->num = CPG_PLLDSI_SMUX_DSI_RGB_DUTY_NUM;
|
||||
duty->den = CPG_PLLDSI_SMUX_DSI_RGB_DUTY_DEN;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rzv2h_cpg_plldsi_smux_set_duty_cycle(struct clk_hw *hw,
|
||||
struct clk_duty *duty)
|
||||
{
|
||||
struct clk_hw *parent_hw;
|
||||
u8 parent_idx;
|
||||
|
||||
/*
|
||||
* 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)
|
||||
*/
|
||||
if (duty->num * CPG_PLLDSI_SMUX_DSI_RGB_DUTY_DEN >
|
||||
duty->den * CPG_PLLDSI_SMUX_DSI_RGB_DUTY_NUM)
|
||||
parent_idx = 0;
|
||||
else
|
||||
parent_idx = 1;
|
||||
|
||||
if (parent_idx >= clk_hw_get_num_parents(hw))
|
||||
return -EINVAL;
|
||||
|
||||
parent_hw = clk_hw_get_parent_by_index(hw, parent_idx);
|
||||
if (!parent_hw)
|
||||
return -EINVAL;
|
||||
|
||||
return clk_hw_set_parent(hw, parent_hw);
|
||||
}
|
||||
|
||||
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,
|
||||
.get_duty_cycle = rzv2h_cpg_plldsi_smux_get_duty_cycle,
|
||||
.set_duty_cycle = rzv2h_cpg_plldsi_smux_set_duty_cycle,
|
||||
};
|
||||
|
||||
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;
|
||||
int ret;
|
||||
|
||||
smux = core->cfg.smux;
|
||||
|
||||
if (smux.shift + 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 = clk_div_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 +1263,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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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, \
|
||||
.mux_flags = CLK_MUX_HIWORD_MASK)
|
||||
|
||||
/**
|
||||
* struct rzv2h_mod_clk - Module Clocks definitions
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user