diff options
| author | Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com> | 2018-07-27 08:29:08 -0400 |
|---|---|---|
| committer | Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com> | 2018-09-15 10:28:25 -0400 |
| commit | 7281e6c6a5bdbde9cae6eb3c6d2bf2706b94807d (patch) | |
| tree | 5114d21f48e482d03abfddfeaeaccb54294060d9 /drivers/gpu | |
| parent | c6e3194a3b55a9365e40c3a25f8e31afa154c26c (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.c | 134 |
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); |
