aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/gpu
diff options
context:
space:
mode:
authorLaurent Pinchart <laurent.pinchart+renesas@ideasonboard.com>2018-07-27 08:29:08 -0400
committerLaurent Pinchart <laurent.pinchart+renesas@ideasonboard.com>2018-09-15 10:28:25 -0400
commit7281e6c6a5bdbde9cae6eb3c6d2bf2706b94807d (patch)
tree5114d21f48e482d03abfddfeaeaccb54294060d9 /drivers/gpu
parentc6e3194a3b55a9365e40c3a25f8e31afa154c26c (diff)
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: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com> Reviewed-by: Jacopo Mondi <jacopo+renesas@jmondi.org>
Diffstat (limited to 'drivers/gpu')
-rw-r--r--drivers/gpu/drm/rcar-du/rcar_du_crtc.c134
1 files changed, 73 insertions, 61 deletions
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_crtc.c b/drivers/gpu/drm/rcar-du/rcar_du_crtc.c
index 175c36ca89c5..687e8129adbd 100644
--- a/drivers/gpu/drm/rcar-du/rcar_du_crtc.c
+++ b/drivers/gpu/drm/rcar-du/rcar_du_crtc.c
@@ -204,78 +204,90 @@ static void rcar_du_crtc_set_display_timing(struct rcar_du_crtc *rcrtc)
204 const struct drm_display_mode *mode = &rcrtc->crtc.state->adjusted_mode; 204 const struct drm_display_mode *mode = &rcrtc->crtc.state->adjusted_mode;
205 struct rcar_du_device *rcdu = rcrtc->group->dev; 205 struct rcar_du_device *rcdu = rcrtc->group->dev;
206 unsigned long mode_clock = mode->clock * 1000; 206 unsigned long mode_clock = mode->clock * 1000;
207 unsigned long clk; 207 u32 dsmr;
208 u32 value;
209 u32 escr; 208 u32 escr;
210 u32 div;
211 209
212 /* 210 if (rcdu->info->dpll_ch & (1 << rcrtc->index)) {
213 * Compute the clock divisor and select the internal or external dot 211 unsigned long target = mode_clock;
214 * clock based on the requested frequency.
215 */
216 clk = clk_get_rate(rcrtc->clock);
217 div = DIV_ROUND_CLOSEST(clk, mode_clock);
218 div = clamp(div, 1U, 64U) - 1;
219 escr = div | ESCR_DCLKSEL_CLKS;
220
221 if (rcrtc->extclock) {
222 struct dpll_info dpll = { 0 }; 212 struct dpll_info dpll = { 0 };
223 unsigned long extclk; 213 unsigned long extclk;
224 unsigned long extrate; 214 u32 dpllcr;
225 unsigned long rate; 215 u32 div = 0;
226 u32 extdiv;
227 216
228 extclk = clk_get_rate(rcrtc->extclock); 217 /*
229 if (rcdu->info->dpll_ch & (1 << rcrtc->index)) { 218 * DU channels that have a display PLL can't use the internal
230 unsigned long target = mode_clock; 219 * system clock, and have no internal clock divider.
220 */
231 221
232 /* 222 if (WARN_ON(!rcrtc->extclock))
233 * The H3 ES1.x exhibits dot clock duty cycle stability 223 return;
234 * issues. We can work around them by configuring the
235 * DPLL to twice the desired frequency, coupled with a
236 * /2 post-divider. This isn't needed on other SoCs and
237 * breaks HDMI output on M3-W for a currently unknown
238 * reason, so restrict the workaround to H3 ES1.x.
239 */
240 if (soc_device_match(rcar_du_r8a7795_es1))
241 target *= 2;
242 224
243 rcar_du_dpll_divider(rcrtc, &dpll, extclk, target); 225 /*
244 extclk = dpll.output; 226 * The H3 ES1.x exhibits dot clock duty cycle stability issues.
227 * We can work around them by configuring the DPLL to twice the
228 * desired frequency, coupled with a /2 post-divider. Restrict
229 * the workaround to H3 ES1.x as ES2.0 and all other SoCs have
230 * no post-divider when a display PLL is present (as shown by
231 * the workaround breaking HDMI output on M3-W during testing).
232 */
233 if (soc_device_match(rcar_du_r8a7795_es1)) {
234 target *= 2;
235 div = 1;
245 } 236 }
246 237
247 extdiv = DIV_ROUND_CLOSEST(extclk, mode_clock); 238 extclk = clk_get_rate(rcrtc->extclock);
248 extdiv = clamp(extdiv, 1U, 64U) - 1; 239 rcar_du_dpll_divider(rcrtc, &dpll, extclk, target);
249 240
250 rate = clk / (div + 1); 241 dpllcr = DPLLCR_CODE | DPLLCR_CLKE
251 extrate = extclk / (extdiv + 1); 242 | DPLLCR_FDPLL(dpll.fdpll)
243 | DPLLCR_N(dpll.n) | DPLLCR_M(dpll.m)
244 | DPLLCR_STBY;
252 245
253 if (abs((long)extrate - (long)mode_clock) < 246 if (rcrtc->index == 1)
254 abs((long)rate - (long)mode_clock)) { 247 dpllcr |= DPLLCR_PLCS1
248 | DPLLCR_INCS_DOTCLKIN1;
249 else
250 dpllcr |= DPLLCR_PLCS0
251 | DPLLCR_INCS_DOTCLKIN0;
255 252
256 if (rcdu->info->dpll_ch & (1 << rcrtc->index)) { 253 rcar_du_group_write(rcrtc->group, DPLLCR, dpllcr);
257 u32 dpllcr = DPLLCR_CODE | DPLLCR_CLKE
258 | DPLLCR_FDPLL(dpll.fdpll)
259 | DPLLCR_N(dpll.n) | DPLLCR_M(dpll.m)
260 | DPLLCR_STBY;
261 254
262 if (rcrtc->index == 1) 255 escr = ESCR_DCLKSEL_DCLKIN | div;
263 dpllcr |= DPLLCR_PLCS1 256 } else {
264 | DPLLCR_INCS_DOTCLKIN1; 257 unsigned long clk;
265 else 258 u32 div;
266 dpllcr |= DPLLCR_PLCS0
267 | DPLLCR_INCS_DOTCLKIN0;
268 259
269 rcar_du_group_write(rcrtc->group, DPLLCR, 260 /*
270 dpllcr); 261 * Compute the clock divisor and select the internal or external
271 } 262 * dot clock based on the requested frequency.
263 */
264 clk = clk_get_rate(rcrtc->clock);
265 div = DIV_ROUND_CLOSEST(clk, mode_clock);
266 div = clamp(div, 1U, 64U) - 1;
272 267
273 escr = ESCR_DCLKSEL_DCLKIN | extdiv; 268 escr = ESCR_DCLKSEL_CLKS | div;
274 }
275 269
276 dev_dbg(rcrtc->group->dev->dev, 270 if (rcrtc->extclock) {
277 "mode clock %lu extrate %lu rate %lu ESCR 0x%08x\n", 271 unsigned long extclk;
278 mode_clock, extrate, rate, escr); 272 unsigned long extrate;
273 unsigned long rate;
274 u32 extdiv;
275
276 extclk = clk_get_rate(rcrtc->extclock);
277 extdiv = DIV_ROUND_CLOSEST(extclk, mode_clock);
278 extdiv = clamp(extdiv, 1U, 64U) - 1;
279
280 extrate = extclk / (extdiv + 1);
281 rate = clk / (div + 1);
282
283 if (abs((long)extrate - (long)mode_clock) <
284 abs((long)rate - (long)mode_clock))
285 escr = ESCR_DCLKSEL_DCLKIN | extdiv;
286
287 dev_dbg(rcrtc->group->dev->dev,
288 "mode clock %lu extrate %lu rate %lu ESCR 0x%08x\n",
289 mode_clock, extrate, rate, escr);
290 }
279 } 291 }
280 292
281 rcar_du_group_write(rcrtc->group, rcrtc->index % 2 ? ESCR2 : ESCR, 293 rcar_du_group_write(rcrtc->group, rcrtc->index % 2 ? ESCR2 : ESCR,
@@ -283,11 +295,11 @@ static void rcar_du_crtc_set_display_timing(struct rcar_du_crtc *rcrtc)
283 rcar_du_group_write(rcrtc->group, rcrtc->index % 2 ? OTAR2 : OTAR, 0); 295 rcar_du_group_write(rcrtc->group, rcrtc->index % 2 ? OTAR2 : OTAR, 0);
284 296
285 /* Signal polarities */ 297 /* Signal polarities */
286 value = ((mode->flags & DRM_MODE_FLAG_PVSYNC) ? DSMR_VSL : 0) 298 dsmr = ((mode->flags & DRM_MODE_FLAG_PVSYNC) ? DSMR_VSL : 0)
287 | ((mode->flags & DRM_MODE_FLAG_PHSYNC) ? DSMR_HSL : 0) 299 | ((mode->flags & DRM_MODE_FLAG_PHSYNC) ? DSMR_HSL : 0)
288 | ((mode->flags & DRM_MODE_FLAG_INTERLACE) ? DSMR_ODEV : 0) 300 | ((mode->flags & DRM_MODE_FLAG_INTERLACE) ? DSMR_ODEV : 0)
289 | DSMR_DIPM_DISP | DSMR_CSPM; 301 | DSMR_DIPM_DISP | DSMR_CSPM;
290 rcar_du_crtc_write(rcrtc, DSMR, value); 302 rcar_du_crtc_write(rcrtc, DSMR, dsmr);
291 303
292 /* Display timings */ 304 /* Display timings */
293 rcar_du_crtc_write(rcrtc, HDSR, mode->htotal - mode->hsync_start - 19); 305 rcar_du_crtc_write(rcrtc, HDSR, mode->htotal - mode->hsync_start - 19);