[PATCH v2 2/2] drm: rcar-du: Improve non-DPLL clock selection
Jacopo Mondi
jacopo+renesas at jmondi.org
Mon Aug 20 15:26:17 UTC 2018
From: Jacopo Mondi <jacopo at jmondi.org>
DU channels not equipped with a DPLL use an internal (aka SoC provided) or
external clock source combined with an internal divider to generate the
desired output dot clock frequency.
The current clock selection procedure does not fully exploit the ability
of external clock sources to generate the exact dot clock frequency by
themselves, but relies instead on tuning the internal DU clock divider
only, resulting in a less precise clock generation process.
When possible, and desirable, ask the external clock source for the exact
output dot clock frequency, and test the returned frequency against the
internally generated one to better approximate the desired output dot clock.
This patch specifically targets platforms (like Salvator-X[S] and ULCBs)
where the DU's input dotclock.in is generated by the versaclock VC5 clock
source, which is capable of generating the exact rate the DU needs as pixel
clock output.
This patch fixes higher resolution modes wich requires an high pixel clock
output currently not working on non-HDMI DU channel (as VGA 1920x1080 at 60Hz).
Fixes: 1b30dbde8 "drm: rcar-du: Add support for external pixel clock"
Signed-off-by: Jacopo Mondi <jacopo at jmondi.org>
---
drivers/gpu/drm/rcar-du/rcar_du_crtc.c | 53 ++++++++++++++++++++--------------
1 file changed, 32 insertions(+), 21 deletions(-)
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_crtc.c b/drivers/gpu/drm/rcar-du/rcar_du_crtc.c
index 077e681..98ae697 100644
--- a/drivers/gpu/drm/rcar-du/rcar_du_crtc.c
+++ b/drivers/gpu/drm/rcar-du/rcar_du_crtc.c
@@ -258,42 +258,53 @@ static void rcar_du_crtc_set_display_timing(struct rcar_du_crtc *rcrtc)
escr = ESCR_DCLKSEL_DCLKIN | div;
} else {
- unsigned long clk;
+ unsigned long dotclkin_rate;
+ struct clk *dotclkin_clk;
+ unsigned long cpg_dist;
u32 div;
/*
* Compute the clock divisor and select the internal or external
* dot clock based on the requested frequency.
*/
- clk = clk_get_rate(rcrtc->clock);
- div = DIV_ROUND_CLOSEST(clk, mode_clock);
- div = clamp(div, 1U, 64U) - 1;
-
+ dotclkin_clk = rcrtc->clock;
+ dotclkin_rate = clk_get_rate(rcrtc->clock);
+ div = clamp(DIV_ROUND_CLOSEST(dotclkin_rate, mode_clock),
+ 1UL, 64UL) - 1;
+ cpg_dist = abs(dotclkin_rate / (div + 1) - mode_clock);
escr = ESCR_DCLKSEL_CLKS | div;
if (rcrtc->extclock) {
- unsigned long extclk;
- unsigned long extrate;
- unsigned long rate;
- u32 extdiv;
-
- extclk = clk_get_rate(rcrtc->extclock);
- extdiv = DIV_ROUND_CLOSEST(extclk, mode_clock);
- extdiv = clamp(extdiv, 1U, 64U) - 1;
+ unsigned long ext_rate;
+ unsigned long ext_dist;
- extrate = extclk / (extdiv + 1);
- rate = clk / (div + 1);
+ /*
+ * If an external clock source is present ask it
+ * for the exact dot clock rate, and test the
+ * returned value against the cpg provided one.
+ */
+ ext_rate = clk_round_rate(rcrtc->extclock,
+ mode_clock);
- if (abs((long)extrate - (long)mode_clock) <
- abs((long)rate - (long)mode_clock))
- escr = ESCR_DCLKSEL_DCLKIN | extdiv;
+ div = clamp(DIV_ROUND_CLOSEST(ext_rate, mode_clock),
+ 1UL, 64UL) - 1;
+ ext_dist = abs(ext_rate / (div + 1) - mode_clock);
- dev_dbg(rcrtc->group->dev->dev,
- "mode clock %lu extrate %lu rate %lu ESCR 0x%08x\n",
- mode_clock, extrate, rate, escr);
+ if (ext_dist < cpg_dist) {
+ dotclkin_clk = rcrtc->extclock;
+ dotclkin_rate = ext_rate;
+ escr = ESCR_DCLKSEL_DCLKIN | div;
+ }
}
+
+ dev_dbg(rcrtc->group->dev->dev, "mode clock %lu %s rate %lu\n",
+ mode_clock, dotclkin_clk == rcrtc->clock ? "cpg" : "ext",
+ dotclkin_rate);
+ clk_set_rate(dotclkin_clk, dotclkin_rate);
}
+ dev_dbg(rcrtc->group->dev->dev, "%s: ESCR 0x%08x\n", __func__, escr);
+
rcar_du_group_write(rcrtc->group, rcrtc->index % 2 ? ESCR2 : ESCR,
escr);
rcar_du_group_write(rcrtc->group, rcrtc->index % 2 ? OTAR2 : OTAR, 0);
--
2.7.4
More information about the dri-devel
mailing list