Commit 7281e6c6 authored by Laurent Pinchart's avatar Laurent Pinchart

drm: rcar-du: Rework clock configuration based on hardware limits

The DU channels that have a display PLL (DPLL) can only use external
clock sources, and don't have an internal clock divider (with the
exception of H3 ES1.x where the post-divider is present and needs to be
used as a workaround for a DPLL silicon issue).

Rework the clock configuration to take this into account, avoiding
selection of non-existing clock sources or usage of a missing
post-divider.
Signed-off-by: default avatarLaurent Pinchart <laurent.pinchart+renesas@ideasonboard.com>
Reviewed-by: default avatarJacopo Mondi <jacopo+renesas@jmondi.org>
parent c6e3194a
...@@ -204,57 +204,41 @@ static void rcar_du_crtc_set_display_timing(struct rcar_du_crtc *rcrtc) ...@@ -204,57 +204,41 @@ static void rcar_du_crtc_set_display_timing(struct rcar_du_crtc *rcrtc)
const struct drm_display_mode *mode = &rcrtc->crtc.state->adjusted_mode; const struct drm_display_mode *mode = &rcrtc->crtc.state->adjusted_mode;
struct rcar_du_device *rcdu = rcrtc->group->dev; struct rcar_du_device *rcdu = rcrtc->group->dev;
unsigned long mode_clock = mode->clock * 1000; unsigned long mode_clock = mode->clock * 1000;
unsigned long clk; u32 dsmr;
u32 value;
u32 escr; u32 escr;
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;
escr = div | ESCR_DCLKSEL_CLKS;
if (rcrtc->extclock) { if (rcdu->info->dpll_ch & (1 << rcrtc->index)) {
unsigned long target = mode_clock;
struct dpll_info dpll = { 0 }; struct dpll_info dpll = { 0 };
unsigned long extclk; unsigned long extclk;
unsigned long extrate; u32 dpllcr;
unsigned long rate; u32 div = 0;
u32 extdiv;
extclk = clk_get_rate(rcrtc->extclock); /*
if (rcdu->info->dpll_ch & (1 << rcrtc->index)) { * DU channels that have a display PLL can't use the internal
unsigned long target = mode_clock; * system clock, and have no internal clock divider.
*/
if (WARN_ON(!rcrtc->extclock))
return;
/* /*
* The H3 ES1.x exhibits dot clock duty cycle stability * The H3 ES1.x exhibits dot clock duty cycle stability issues.
* issues. We can work around them by configuring the * We can work around them by configuring the DPLL to twice the
* DPLL to twice the desired frequency, coupled with a * desired frequency, coupled with a /2 post-divider. Restrict
* /2 post-divider. This isn't needed on other SoCs and * the workaround to H3 ES1.x as ES2.0 and all other SoCs have
* breaks HDMI output on M3-W for a currently unknown * no post-divider when a display PLL is present (as shown by
* reason, so restrict the workaround to H3 ES1.x. * the workaround breaking HDMI output on M3-W during testing).
*/ */
if (soc_device_match(rcar_du_r8a7795_es1)) if (soc_device_match(rcar_du_r8a7795_es1)) {
target *= 2; target *= 2;
div = 1;
rcar_du_dpll_divider(rcrtc, &dpll, extclk, target);
extclk = dpll.output;
} }
extdiv = DIV_ROUND_CLOSEST(extclk, mode_clock); extclk = clk_get_rate(rcrtc->extclock);
extdiv = clamp(extdiv, 1U, 64U) - 1; rcar_du_dpll_divider(rcrtc, &dpll, extclk, target);
rate = clk / (div + 1);
extrate = extclk / (extdiv + 1);
if (abs((long)extrate - (long)mode_clock) <
abs((long)rate - (long)mode_clock)) {
if (rcdu->info->dpll_ch & (1 << rcrtc->index)) { dpllcr = DPLLCR_CODE | DPLLCR_CLKE
u32 dpllcr = DPLLCR_CODE | DPLLCR_CLKE
| DPLLCR_FDPLL(dpll.fdpll) | DPLLCR_FDPLL(dpll.fdpll)
| DPLLCR_N(dpll.n) | DPLLCR_M(dpll.m) | DPLLCR_N(dpll.n) | DPLLCR_M(dpll.m)
| DPLLCR_STBY; | DPLLCR_STBY;
...@@ -266,28 +250,56 @@ static void rcar_du_crtc_set_display_timing(struct rcar_du_crtc *rcrtc) ...@@ -266,28 +250,56 @@ static void rcar_du_crtc_set_display_timing(struct rcar_du_crtc *rcrtc)
dpllcr |= DPLLCR_PLCS0 dpllcr |= DPLLCR_PLCS0
| DPLLCR_INCS_DOTCLKIN0; | DPLLCR_INCS_DOTCLKIN0;
rcar_du_group_write(rcrtc->group, DPLLCR, rcar_du_group_write(rcrtc->group, DPLLCR, dpllcr);
dpllcr);
} escr = ESCR_DCLKSEL_DCLKIN | div;
} else {
unsigned long clk;
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;
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;
extrate = extclk / (extdiv + 1);
rate = clk / (div + 1);
if (abs((long)extrate - (long)mode_clock) <
abs((long)rate - (long)mode_clock))
escr = ESCR_DCLKSEL_DCLKIN | extdiv; escr = ESCR_DCLKSEL_DCLKIN | extdiv;
}
dev_dbg(rcrtc->group->dev->dev, dev_dbg(rcrtc->group->dev->dev,
"mode clock %lu extrate %lu rate %lu ESCR 0x%08x\n", "mode clock %lu extrate %lu rate %lu ESCR 0x%08x\n",
mode_clock, extrate, rate, escr); mode_clock, extrate, rate, escr);
} }
}
rcar_du_group_write(rcrtc->group, rcrtc->index % 2 ? ESCR2 : ESCR, rcar_du_group_write(rcrtc->group, rcrtc->index % 2 ? ESCR2 : ESCR,
escr); escr);
rcar_du_group_write(rcrtc->group, rcrtc->index % 2 ? OTAR2 : OTAR, 0); rcar_du_group_write(rcrtc->group, rcrtc->index % 2 ? OTAR2 : OTAR, 0);
/* Signal polarities */ /* Signal polarities */
value = ((mode->flags & DRM_MODE_FLAG_PVSYNC) ? DSMR_VSL : 0) dsmr = ((mode->flags & DRM_MODE_FLAG_PVSYNC) ? DSMR_VSL : 0)
| ((mode->flags & DRM_MODE_FLAG_PHSYNC) ? DSMR_HSL : 0) | ((mode->flags & DRM_MODE_FLAG_PHSYNC) ? DSMR_HSL : 0)
| ((mode->flags & DRM_MODE_FLAG_INTERLACE) ? DSMR_ODEV : 0) | ((mode->flags & DRM_MODE_FLAG_INTERLACE) ? DSMR_ODEV : 0)
| DSMR_DIPM_DISP | DSMR_CSPM; | DSMR_DIPM_DISP | DSMR_CSPM;
rcar_du_crtc_write(rcrtc, DSMR, value); rcar_du_crtc_write(rcrtc, DSMR, dsmr);
/* Display timings */ /* Display timings */
rcar_du_crtc_write(rcrtc, HDSR, mode->htotal - mode->hsync_start - 19); rcar_du_crtc_write(rcrtc, HDSR, mode->htotal - mode->hsync_start - 19);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment