[PATCH 3/4] drm: renesas: rz-du: mipi_dsi: Improve DSI PLL parameter selection and cache results

From: Prabhakar

Date: Tue Nov 03 2026 - 12:32:37 EST


From: Lad Prabhakar <prabhakar.mahadev-lad.rj@xxxxxxxxxxxxxx>

Mode validation searched the divider table once, kept the best CPG match
and only then checked the DSI PLL. A divider losing on CPG error alone
was never tried against it, so reachable modes were rejected.

Evaluate each divider on its own and keep the one with the smallest DSI
PLL error, handing it to the CPG via rzv2h_cpg_cache_pll_pars(). The
search also takes the PLLDSI instance as an argument and records it,
walking the instances described by the new num_limits field.

Signed-off-by: Lad Prabhakar <prabhakar.mahadev-lad.rj@xxxxxxxxxxxxxx>
Signed-off-by: Tommaso Merciai <tommaso.merciai.xr@xxxxxxxxxxxxxx>
---
.../gpu/drm/renesas/rz-du/rzg2l_mipi_dsi.c | 87 ++++++++++++++-----
1 file changed, 66 insertions(+), 21 deletions(-)

diff --git a/drivers/gpu/drm/renesas/rz-du/rzg2l_mipi_dsi.c b/drivers/gpu/drm/renesas/rz-du/rzg2l_mipi_dsi.c
index 5b257f5adf06..682bbb339bef 100644
--- a/drivers/gpu/drm/renesas/rz-du/rzg2l_mipi_dsi.c
+++ b/drivers/gpu/drm/renesas/rz-du/rzg2l_mipi_dsi.c
@@ -12,6 +12,7 @@
#include <linux/dma-mapping.h>
#include <linux/io.h>
#include <linux/iopoll.h>
+#include <linux/limits.h>
#include <linux/math.h>
#include <linux/module.h>
#include <linux/of.h>
@@ -55,6 +56,7 @@ struct rzg2l_mipi_dsi_hw_info {
const struct rzv2h_pll_limits **limits;
const u8 *table;
const u8 table_size;
+ const u8 num_limits;
} cpg_plldsi;
const struct rzg2l_mipi_dsi_timings *dsi_global_timings;
unsigned int num_dsi_global_timings;
@@ -69,7 +71,9 @@ struct rzg2l_mipi_dsi_hw_info {

struct rzv2h_dsi_mode_calc {
unsigned long mode_freq_khz;
+ unsigned int pll_instance;
struct rzv2h_pll_pars dsi_parameters;
+ struct rzv2h_pll_div_pars cpg_dsi_div_parameters;
};

struct rzg2l_mipi_dsi {
@@ -671,43 +675,83 @@ static int rzg2l_dphy_conf_clks(struct rzg2l_mipi_dsi *dsi, unsigned long mode_f
return 0;
}

-static unsigned int rzv2h_dphy_mode_clk_check(struct rzg2l_mipi_dsi *dsi,
- unsigned long mode_freq)
+static unsigned int rzv2h_dphy_check_instance(struct rzg2l_mipi_dsi *dsi,
+ unsigned long mode_freq,
+ unsigned int pll_instance)
{
u64 hsfreq_millihz, mode_freq_hz, mode_freq_millihz;
+ struct rzv2h_pll_div_pars best_cpg_dsi_parameters;
struct rzv2h_pll_div_pars cpg_dsi_parameters;
+ struct rzv2h_pll_pars best_dsi_parameters;
struct rzv2h_pll_pars dsi_parameters;
+ bool found_valid_parameters = false;
bool parameters_found;
- unsigned int bpp;
+ unsigned int bpp, i;

+ best_dsi_parameters.error_millihz = S64_MAX;
bpp = mipi_dsi_pixel_format_to_bpp(dsi->format);
mode_freq_hz = mul_u32_u32(mode_freq, KILO);
mode_freq_millihz = mode_freq_hz * MILLI;
- parameters_found =
- rzv2h_get_pll_divs_pars(dsi->info->cpg_plldsi.limits[0],
- &cpg_dsi_parameters,
- dsi->info->cpg_plldsi.table,
- dsi->info->cpg_plldsi.table_size,
- mode_freq_millihz);
- if (!parameters_found)
- return MODE_CLOCK_RANGE;
-
- hsfreq_millihz = DIV_ROUND_CLOSEST_ULL(cpg_dsi_parameters.div.freq_millihz * bpp,
- dsi->lanes);
- parameters_found = rzv2h_get_pll_pars(&rzv2h_plldsi_div_limits,
- &dsi_parameters, hsfreq_millihz);
- if (!parameters_found)
- return MODE_CLOCK_RANGE;
+ for (i = 0; i < dsi->info->cpg_plldsi.table_size; i++) {
+ parameters_found =
+ rzv2h_get_pll_divs_pars(dsi->info->cpg_plldsi.limits[pll_instance],
+ &cpg_dsi_parameters,
+ &dsi->info->cpg_plldsi.table[i], 1,
+ mode_freq_millihz);
+ if (!parameters_found)
+ continue;
+
+ hsfreq_millihz = DIV_ROUND_CLOSEST_ULL(cpg_dsi_parameters.div.freq_millihz * bpp,
+ dsi->lanes);
+ parameters_found = rzv2h_get_pll_pars(&rzv2h_plldsi_div_limits,
+ &dsi_parameters, hsfreq_millihz);
+ if (!parameters_found)
+ continue;
+
+ if (abs(dsi_parameters.error_millihz) >= 500)
+ continue;
+
+ if (abs(best_dsi_parameters.error_millihz) > abs(dsi_parameters.error_millihz)) {
+ best_cpg_dsi_parameters = cpg_dsi_parameters;
+ best_dsi_parameters = dsi_parameters;
+ found_valid_parameters = true;
+ }
+ }

- if (abs(dsi_parameters.error_millihz) >= 500)
+ if (!found_valid_parameters)
return MODE_CLOCK_RANGE;

- memcpy(&dsi->mode_calc.dsi_parameters, &dsi_parameters, sizeof(dsi_parameters));
+ memcpy(&dsi->mode_calc.dsi_parameters, &best_dsi_parameters,
+ sizeof(best_dsi_parameters));
+ memcpy(&dsi->mode_calc.cpg_dsi_div_parameters, &best_cpg_dsi_parameters,
+ sizeof(best_cpg_dsi_parameters));
dsi->mode_calc.mode_freq_khz = mode_freq;
+ dsi->mode_calc.pll_instance = pll_instance;
+
+ rzv2h_cpg_cache_pll_pars(best_cpg_dsi_parameters, pll_instance);

return MODE_OK;
}

+static unsigned int rzv2h_dphy_mode_clk_check(struct rzg2l_mipi_dsi *dsi,
+ unsigned long mode_freq)
+{
+ bool found = false;
+ unsigned int i;
+
+ /*
+ * Check every instance instead of stopping at the first match, as
+ * rzv2h_dphy_check_instance() hands the parameters it computes over to
+ * the CPG, which needs them for whichever PLLDSI ends up feeding the
+ * encoder.
+ */
+ for (i = 0; i < dsi->info->cpg_plldsi.num_limits; i++)
+ if (rzv2h_dphy_check_instance(dsi, mode_freq, i) == MODE_OK)
+ found = true;
+
+ return found ? MODE_OK : MODE_CLOCK_RANGE;
+}
+
static int rzv2h_dphy_conf_clks(struct rzg2l_mipi_dsi *dsi, unsigned long mode_freq,
u64 *hsfreq_millihz)
{
@@ -715,7 +759,7 @@ static int rzv2h_dphy_conf_clks(struct rzg2l_mipi_dsi *dsi, unsigned long mode_f
unsigned long status;

if (dsi->mode_calc.mode_freq_khz != mode_freq) {
- status = rzv2h_dphy_mode_clk_check(dsi, mode_freq);
+ status = rzv2h_dphy_check_instance(dsi, mode_freq, 0);
if (status != MODE_OK) {
dev_err(dsi->dev, "No PLL parameters found for mode clk %lu\n",
mode_freq);
@@ -1647,6 +1691,7 @@ static const struct rzg2l_mipi_dsi_hw_info rzv2h_mipi_dsi_info = {
.dphy_mode_clk_check = rzv2h_dphy_mode_clk_check,
.dphy_conf_clks = rzv2h_dphy_conf_clks,
.cpg_plldsi.limits = rzv2h_plldsi_limits,
+ .cpg_plldsi.num_limits = ARRAY_SIZE(rzv2h_plldsi_limits),
.cpg_plldsi.table = rzv2h_cpg_div_table,
.cpg_plldsi.table_size = ARRAY_SIZE(rzv2h_cpg_div_table),
.phy_reg_offset = 0x10000,
--
2.56.0