| .. | .. |
|---|
| 1 | +// SPDX-License-Identifier: GPL-2.0+ |
|---|
| 1 | 2 | /* |
|---|
| 2 | 3 | * rcar_du_crtc.c -- R-Car Display Unit CRTCs |
|---|
| 3 | 4 | * |
|---|
| 4 | 5 | * Copyright (C) 2013-2015 Renesas Electronics Corporation |
|---|
| 5 | 6 | * |
|---|
| 6 | 7 | * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com) |
|---|
| 7 | | - * |
|---|
| 8 | | - * This program is free software; you can redistribute it and/or modify |
|---|
| 9 | | - * it under the terms of the GNU General Public License as published by |
|---|
| 10 | | - * the Free Software Foundation; either version 2 of the License, or |
|---|
| 11 | | - * (at your option) any later version. |
|---|
| 12 | 8 | */ |
|---|
| 13 | 9 | |
|---|
| 14 | 10 | #include <linux/clk.h> |
|---|
| 15 | 11 | #include <linux/mutex.h> |
|---|
| 12 | +#include <linux/platform_device.h> |
|---|
| 16 | 13 | #include <linux/sys_soc.h> |
|---|
| 17 | 14 | |
|---|
| 18 | | -#include <drm/drmP.h> |
|---|
| 19 | 15 | #include <drm/drm_atomic.h> |
|---|
| 20 | 16 | #include <drm/drm_atomic_helper.h> |
|---|
| 17 | +#include <drm/drm_bridge.h> |
|---|
| 21 | 18 | #include <drm/drm_crtc.h> |
|---|
| 22 | | -#include <drm/drm_crtc_helper.h> |
|---|
| 19 | +#include <drm/drm_device.h> |
|---|
| 23 | 20 | #include <drm/drm_fb_cma_helper.h> |
|---|
| 24 | 21 | #include <drm/drm_gem_cma_helper.h> |
|---|
| 25 | 22 | #include <drm/drm_plane_helper.h> |
|---|
| 23 | +#include <drm/drm_vblank.h> |
|---|
| 26 | 24 | |
|---|
| 25 | +#include "rcar_cmm.h" |
|---|
| 27 | 26 | #include "rcar_du_crtc.h" |
|---|
| 28 | 27 | #include "rcar_du_drv.h" |
|---|
| 28 | +#include "rcar_du_encoder.h" |
|---|
| 29 | 29 | #include "rcar_du_kms.h" |
|---|
| 30 | 30 | #include "rcar_du_plane.h" |
|---|
| 31 | 31 | #include "rcar_du_regs.h" |
|---|
| 32 | 32 | #include "rcar_du_vsp.h" |
|---|
| 33 | +#include "rcar_lvds.h" |
|---|
| 33 | 34 | |
|---|
| 34 | 35 | static u32 rcar_du_crtc_read(struct rcar_du_crtc *rcrtc, u32 reg) |
|---|
| 35 | 36 | { |
|---|
| 36 | | - struct rcar_du_device *rcdu = rcrtc->group->dev; |
|---|
| 37 | + struct rcar_du_device *rcdu = rcrtc->dev; |
|---|
| 37 | 38 | |
|---|
| 38 | 39 | return rcar_du_read(rcdu, rcrtc->mmio_offset + reg); |
|---|
| 39 | 40 | } |
|---|
| 40 | 41 | |
|---|
| 41 | 42 | static void rcar_du_crtc_write(struct rcar_du_crtc *rcrtc, u32 reg, u32 data) |
|---|
| 42 | 43 | { |
|---|
| 43 | | - struct rcar_du_device *rcdu = rcrtc->group->dev; |
|---|
| 44 | + struct rcar_du_device *rcdu = rcrtc->dev; |
|---|
| 44 | 45 | |
|---|
| 45 | 46 | rcar_du_write(rcdu, rcrtc->mmio_offset + reg, data); |
|---|
| 46 | 47 | } |
|---|
| 47 | 48 | |
|---|
| 48 | 49 | static void rcar_du_crtc_clr(struct rcar_du_crtc *rcrtc, u32 reg, u32 clr) |
|---|
| 49 | 50 | { |
|---|
| 50 | | - struct rcar_du_device *rcdu = rcrtc->group->dev; |
|---|
| 51 | + struct rcar_du_device *rcdu = rcrtc->dev; |
|---|
| 51 | 52 | |
|---|
| 52 | 53 | rcar_du_write(rcdu, rcrtc->mmio_offset + reg, |
|---|
| 53 | 54 | rcar_du_read(rcdu, rcrtc->mmio_offset + reg) & ~clr); |
|---|
| .. | .. |
|---|
| 55 | 56 | |
|---|
| 56 | 57 | static void rcar_du_crtc_set(struct rcar_du_crtc *rcrtc, u32 reg, u32 set) |
|---|
| 57 | 58 | { |
|---|
| 58 | | - struct rcar_du_device *rcdu = rcrtc->group->dev; |
|---|
| 59 | + struct rcar_du_device *rcdu = rcrtc->dev; |
|---|
| 59 | 60 | |
|---|
| 60 | 61 | rcar_du_write(rcdu, rcrtc->mmio_offset + reg, |
|---|
| 61 | 62 | rcar_du_read(rcdu, rcrtc->mmio_offset + reg) | set); |
|---|
| 62 | 63 | } |
|---|
| 63 | 64 | |
|---|
| 64 | | -static void rcar_du_crtc_clr_set(struct rcar_du_crtc *rcrtc, u32 reg, |
|---|
| 65 | | - u32 clr, u32 set) |
|---|
| 65 | +void rcar_du_crtc_dsysr_clr_set(struct rcar_du_crtc *rcrtc, u32 clr, u32 set) |
|---|
| 66 | 66 | { |
|---|
| 67 | | - struct rcar_du_device *rcdu = rcrtc->group->dev; |
|---|
| 68 | | - u32 value = rcar_du_read(rcdu, rcrtc->mmio_offset + reg); |
|---|
| 67 | + struct rcar_du_device *rcdu = rcrtc->dev; |
|---|
| 69 | 68 | |
|---|
| 70 | | - rcar_du_write(rcdu, rcrtc->mmio_offset + reg, (value & ~clr) | set); |
|---|
| 71 | | -} |
|---|
| 72 | | - |
|---|
| 73 | | -static int rcar_du_crtc_get(struct rcar_du_crtc *rcrtc) |
|---|
| 74 | | -{ |
|---|
| 75 | | - int ret; |
|---|
| 76 | | - |
|---|
| 77 | | - ret = clk_prepare_enable(rcrtc->clock); |
|---|
| 78 | | - if (ret < 0) |
|---|
| 79 | | - return ret; |
|---|
| 80 | | - |
|---|
| 81 | | - ret = clk_prepare_enable(rcrtc->extclock); |
|---|
| 82 | | - if (ret < 0) |
|---|
| 83 | | - goto error_clock; |
|---|
| 84 | | - |
|---|
| 85 | | - ret = rcar_du_group_get(rcrtc->group); |
|---|
| 86 | | - if (ret < 0) |
|---|
| 87 | | - goto error_group; |
|---|
| 88 | | - |
|---|
| 89 | | - return 0; |
|---|
| 90 | | - |
|---|
| 91 | | -error_group: |
|---|
| 92 | | - clk_disable_unprepare(rcrtc->extclock); |
|---|
| 93 | | -error_clock: |
|---|
| 94 | | - clk_disable_unprepare(rcrtc->clock); |
|---|
| 95 | | - return ret; |
|---|
| 96 | | -} |
|---|
| 97 | | - |
|---|
| 98 | | -static void rcar_du_crtc_put(struct rcar_du_crtc *rcrtc) |
|---|
| 99 | | -{ |
|---|
| 100 | | - rcar_du_group_put(rcrtc->group); |
|---|
| 101 | | - |
|---|
| 102 | | - clk_disable_unprepare(rcrtc->extclock); |
|---|
| 103 | | - clk_disable_unprepare(rcrtc->clock); |
|---|
| 69 | + rcrtc->dsysr = (rcrtc->dsysr & ~clr) | set; |
|---|
| 70 | + rcar_du_write(rcdu, rcrtc->mmio_offset + DSYSR, rcrtc->dsysr); |
|---|
| 104 | 71 | } |
|---|
| 105 | 72 | |
|---|
| 106 | 73 | /* ----------------------------------------------------------------------------- |
|---|
| .. | .. |
|---|
| 192 | 159 | } |
|---|
| 193 | 160 | |
|---|
| 194 | 161 | done: |
|---|
| 195 | | - dev_dbg(rcrtc->group->dev->dev, |
|---|
| 162 | + dev_dbg(rcrtc->dev->dev, |
|---|
| 196 | 163 | "output:%u, fdpll:%u, n:%u, m:%u, diff:%lu\n", |
|---|
| 197 | | - dpll->output, dpll->fdpll, dpll->n, dpll->m, |
|---|
| 198 | | - best_diff); |
|---|
| 164 | + dpll->output, dpll->fdpll, dpll->n, dpll->m, best_diff); |
|---|
| 165 | +} |
|---|
| 166 | + |
|---|
| 167 | +struct du_clk_params { |
|---|
| 168 | + struct clk *clk; |
|---|
| 169 | + unsigned long rate; |
|---|
| 170 | + unsigned long diff; |
|---|
| 171 | + u32 escr; |
|---|
| 172 | +}; |
|---|
| 173 | + |
|---|
| 174 | +static void rcar_du_escr_divider(struct clk *clk, unsigned long target, |
|---|
| 175 | + u32 escr, struct du_clk_params *params) |
|---|
| 176 | +{ |
|---|
| 177 | + unsigned long rate; |
|---|
| 178 | + unsigned long diff; |
|---|
| 179 | + u32 div; |
|---|
| 180 | + |
|---|
| 181 | + /* |
|---|
| 182 | + * If the target rate has already been achieved perfectly we can't do |
|---|
| 183 | + * better. |
|---|
| 184 | + */ |
|---|
| 185 | + if (params->diff == 0) |
|---|
| 186 | + return; |
|---|
| 187 | + |
|---|
| 188 | + /* |
|---|
| 189 | + * Compute the input clock rate and internal divisor values to obtain |
|---|
| 190 | + * the clock rate closest to the target frequency. |
|---|
| 191 | + */ |
|---|
| 192 | + rate = clk_round_rate(clk, target); |
|---|
| 193 | + div = clamp(DIV_ROUND_CLOSEST(rate, target), 1UL, 64UL) - 1; |
|---|
| 194 | + diff = abs(rate / (div + 1) - target); |
|---|
| 195 | + |
|---|
| 196 | + /* |
|---|
| 197 | + * Store the parameters if the resulting frequency is better than any |
|---|
| 198 | + * previously calculated value. |
|---|
| 199 | + */ |
|---|
| 200 | + if (diff < params->diff) { |
|---|
| 201 | + params->clk = clk; |
|---|
| 202 | + params->rate = rate; |
|---|
| 203 | + params->diff = diff; |
|---|
| 204 | + params->escr = escr | div; |
|---|
| 205 | + } |
|---|
| 199 | 206 | } |
|---|
| 200 | 207 | |
|---|
| 201 | 208 | static const struct soc_device_attribute rcar_du_r8a7795_es1[] = { |
|---|
| .. | .. |
|---|
| 206 | 213 | static void rcar_du_crtc_set_display_timing(struct rcar_du_crtc *rcrtc) |
|---|
| 207 | 214 | { |
|---|
| 208 | 215 | const struct drm_display_mode *mode = &rcrtc->crtc.state->adjusted_mode; |
|---|
| 209 | | - struct rcar_du_device *rcdu = rcrtc->group->dev; |
|---|
| 216 | + struct rcar_du_device *rcdu = rcrtc->dev; |
|---|
| 210 | 217 | unsigned long mode_clock = mode->clock * 1000; |
|---|
| 211 | | - unsigned long clk; |
|---|
| 212 | | - u32 value; |
|---|
| 218 | + unsigned int hdse_offset; |
|---|
| 219 | + u32 dsmr; |
|---|
| 213 | 220 | u32 escr; |
|---|
| 214 | | - u32 div; |
|---|
| 215 | 221 | |
|---|
| 216 | | - /* |
|---|
| 217 | | - * Compute the clock divisor and select the internal or external dot |
|---|
| 218 | | - * clock based on the requested frequency. |
|---|
| 219 | | - */ |
|---|
| 220 | | - clk = clk_get_rate(rcrtc->clock); |
|---|
| 221 | | - div = DIV_ROUND_CLOSEST(clk, mode_clock); |
|---|
| 222 | | - div = clamp(div, 1U, 64U) - 1; |
|---|
| 223 | | - escr = div | ESCR_DCLKSEL_CLKS; |
|---|
| 224 | | - |
|---|
| 225 | | - if (rcrtc->extclock) { |
|---|
| 222 | + if (rcdu->info->dpll_mask & (1 << rcrtc->index)) { |
|---|
| 223 | + unsigned long target = mode_clock; |
|---|
| 226 | 224 | struct dpll_info dpll = { 0 }; |
|---|
| 227 | 225 | unsigned long extclk; |
|---|
| 228 | | - unsigned long extrate; |
|---|
| 229 | | - unsigned long rate; |
|---|
| 230 | | - u32 extdiv; |
|---|
| 226 | + u32 dpllcr; |
|---|
| 227 | + u32 div = 0; |
|---|
| 228 | + |
|---|
| 229 | + /* |
|---|
| 230 | + * DU channels that have a display PLL can't use the internal |
|---|
| 231 | + * system clock, and have no internal clock divider. |
|---|
| 232 | + */ |
|---|
| 233 | + |
|---|
| 234 | + /* |
|---|
| 235 | + * The H3 ES1.x exhibits dot clock duty cycle stability issues. |
|---|
| 236 | + * We can work around them by configuring the DPLL to twice the |
|---|
| 237 | + * desired frequency, coupled with a /2 post-divider. Restrict |
|---|
| 238 | + * the workaround to H3 ES1.x as ES2.0 and all other SoCs have |
|---|
| 239 | + * no post-divider when a display PLL is present (as shown by |
|---|
| 240 | + * the workaround breaking HDMI output on M3-W during testing). |
|---|
| 241 | + */ |
|---|
| 242 | + if (soc_device_match(rcar_du_r8a7795_es1)) { |
|---|
| 243 | + target *= 2; |
|---|
| 244 | + div = 1; |
|---|
| 245 | + } |
|---|
| 231 | 246 | |
|---|
| 232 | 247 | extclk = clk_get_rate(rcrtc->extclock); |
|---|
| 233 | | - if (rcdu->info->dpll_ch & (1 << rcrtc->index)) { |
|---|
| 234 | | - unsigned long target = mode_clock; |
|---|
| 248 | + rcar_du_dpll_divider(rcrtc, &dpll, extclk, target); |
|---|
| 235 | 249 | |
|---|
| 236 | | - /* |
|---|
| 237 | | - * The H3 ES1.x exhibits dot clock duty cycle stability |
|---|
| 238 | | - * issues. We can work around them by configuring the |
|---|
| 239 | | - * DPLL to twice the desired frequency, coupled with a |
|---|
| 240 | | - * /2 post-divider. This isn't needed on other SoCs and |
|---|
| 241 | | - * breaks HDMI output on M3-W for a currently unknown |
|---|
| 242 | | - * reason, so restrict the workaround to H3 ES1.x. |
|---|
| 243 | | - */ |
|---|
| 244 | | - if (soc_device_match(rcar_du_r8a7795_es1)) |
|---|
| 245 | | - target *= 2; |
|---|
| 250 | + dpllcr = DPLLCR_CODE | DPLLCR_CLKE |
|---|
| 251 | + | DPLLCR_FDPLL(dpll.fdpll) |
|---|
| 252 | + | DPLLCR_N(dpll.n) | DPLLCR_M(dpll.m) |
|---|
| 253 | + | DPLLCR_STBY; |
|---|
| 246 | 254 | |
|---|
| 247 | | - rcar_du_dpll_divider(rcrtc, &dpll, extclk, target); |
|---|
| 248 | | - extclk = dpll.output; |
|---|
| 249 | | - } |
|---|
| 255 | + if (rcrtc->index == 1) |
|---|
| 256 | + dpllcr |= DPLLCR_PLCS1 |
|---|
| 257 | + | DPLLCR_INCS_DOTCLKIN1; |
|---|
| 258 | + else |
|---|
| 259 | + dpllcr |= DPLLCR_PLCS0 |
|---|
| 260 | + | DPLLCR_INCS_DOTCLKIN0; |
|---|
| 250 | 261 | |
|---|
| 251 | | - extdiv = DIV_ROUND_CLOSEST(extclk, mode_clock); |
|---|
| 252 | | - extdiv = clamp(extdiv, 1U, 64U) - 1; |
|---|
| 262 | + rcar_du_group_write(rcrtc->group, DPLLCR, dpllcr); |
|---|
| 253 | 263 | |
|---|
| 254 | | - rate = clk / (div + 1); |
|---|
| 255 | | - extrate = extclk / (extdiv + 1); |
|---|
| 264 | + escr = ESCR_DCLKSEL_DCLKIN | div; |
|---|
| 265 | + } else if (rcdu->info->lvds_clk_mask & BIT(rcrtc->index)) { |
|---|
| 266 | + /* |
|---|
| 267 | + * Use the LVDS PLL output as the dot clock when outputting to |
|---|
| 268 | + * the LVDS encoder on an SoC that supports this clock routing |
|---|
| 269 | + * option. We use the clock directly in that case, without any |
|---|
| 270 | + * additional divider. |
|---|
| 271 | + */ |
|---|
| 272 | + escr = ESCR_DCLKSEL_DCLKIN; |
|---|
| 273 | + } else { |
|---|
| 274 | + struct du_clk_params params = { .diff = (unsigned long)-1 }; |
|---|
| 256 | 275 | |
|---|
| 257 | | - if (abs((long)extrate - (long)mode_clock) < |
|---|
| 258 | | - abs((long)rate - (long)mode_clock)) { |
|---|
| 276 | + rcar_du_escr_divider(rcrtc->clock, mode_clock, |
|---|
| 277 | + ESCR_DCLKSEL_CLKS, ¶ms); |
|---|
| 278 | + if (rcrtc->extclock) |
|---|
| 279 | + rcar_du_escr_divider(rcrtc->extclock, mode_clock, |
|---|
| 280 | + ESCR_DCLKSEL_DCLKIN, ¶ms); |
|---|
| 259 | 281 | |
|---|
| 260 | | - if (rcdu->info->dpll_ch & (1 << rcrtc->index)) { |
|---|
| 261 | | - u32 dpllcr = DPLLCR_CODE | DPLLCR_CLKE |
|---|
| 262 | | - | DPLLCR_FDPLL(dpll.fdpll) |
|---|
| 263 | | - | DPLLCR_N(dpll.n) | DPLLCR_M(dpll.m) |
|---|
| 264 | | - | DPLLCR_STBY; |
|---|
| 282 | + dev_dbg(rcrtc->dev->dev, "mode clock %lu %s rate %lu\n", |
|---|
| 283 | + mode_clock, params.clk == rcrtc->clock ? "cpg" : "ext", |
|---|
| 284 | + params.rate); |
|---|
| 265 | 285 | |
|---|
| 266 | | - if (rcrtc->index == 1) |
|---|
| 267 | | - dpllcr |= DPLLCR_PLCS1 |
|---|
| 268 | | - | DPLLCR_INCS_DOTCLKIN1; |
|---|
| 269 | | - else |
|---|
| 270 | | - dpllcr |= DPLLCR_PLCS0 |
|---|
| 271 | | - | DPLLCR_INCS_DOTCLKIN0; |
|---|
| 272 | | - |
|---|
| 273 | | - rcar_du_group_write(rcrtc->group, DPLLCR, |
|---|
| 274 | | - dpllcr); |
|---|
| 275 | | - } |
|---|
| 276 | | - |
|---|
| 277 | | - escr = ESCR_DCLKSEL_DCLKIN | extdiv; |
|---|
| 278 | | - } |
|---|
| 279 | | - |
|---|
| 280 | | - dev_dbg(rcrtc->group->dev->dev, |
|---|
| 281 | | - "mode clock %lu extrate %lu rate %lu ESCR 0x%08x\n", |
|---|
| 282 | | - mode_clock, extrate, rate, escr); |
|---|
| 286 | + clk_set_rate(params.clk, params.rate); |
|---|
| 287 | + escr = params.escr; |
|---|
| 283 | 288 | } |
|---|
| 284 | 289 | |
|---|
| 285 | | - rcar_du_group_write(rcrtc->group, rcrtc->index % 2 ? ESCR2 : ESCR, |
|---|
| 286 | | - escr); |
|---|
| 287 | | - rcar_du_group_write(rcrtc->group, rcrtc->index % 2 ? OTAR2 : OTAR, 0); |
|---|
| 290 | + dev_dbg(rcrtc->dev->dev, "%s: ESCR 0x%08x\n", __func__, escr); |
|---|
| 291 | + |
|---|
| 292 | + rcar_du_crtc_write(rcrtc, rcrtc->index % 2 ? ESCR13 : ESCR02, escr); |
|---|
| 293 | + rcar_du_crtc_write(rcrtc, rcrtc->index % 2 ? OTAR13 : OTAR02, 0); |
|---|
| 288 | 294 | |
|---|
| 289 | 295 | /* Signal polarities */ |
|---|
| 290 | | - value = ((mode->flags & DRM_MODE_FLAG_PVSYNC) ? DSMR_VSL : 0) |
|---|
| 291 | | - | ((mode->flags & DRM_MODE_FLAG_PHSYNC) ? DSMR_HSL : 0) |
|---|
| 292 | | - | DSMR_DIPM_DISP | DSMR_CSPM; |
|---|
| 293 | | - rcar_du_crtc_write(rcrtc, DSMR, value); |
|---|
| 296 | + dsmr = ((mode->flags & DRM_MODE_FLAG_PVSYNC) ? DSMR_VSL : 0) |
|---|
| 297 | + | ((mode->flags & DRM_MODE_FLAG_PHSYNC) ? DSMR_HSL : 0) |
|---|
| 298 | + | ((mode->flags & DRM_MODE_FLAG_INTERLACE) ? DSMR_ODEV : 0) |
|---|
| 299 | + | DSMR_DIPM_DISP | DSMR_CSPM; |
|---|
| 300 | + rcar_du_crtc_write(rcrtc, DSMR, dsmr); |
|---|
| 301 | + |
|---|
| 302 | + hdse_offset = 19; |
|---|
| 303 | + if (rcrtc->group->cmms_mask & BIT(rcrtc->index % 2)) |
|---|
| 304 | + hdse_offset += 25; |
|---|
| 294 | 305 | |
|---|
| 295 | 306 | /* Display timings */ |
|---|
| 296 | | - rcar_du_crtc_write(rcrtc, HDSR, mode->htotal - mode->hsync_start - 19); |
|---|
| 307 | + rcar_du_crtc_write(rcrtc, HDSR, mode->htotal - mode->hsync_start - |
|---|
| 308 | + hdse_offset); |
|---|
| 297 | 309 | rcar_du_crtc_write(rcrtc, HDER, mode->htotal - mode->hsync_start + |
|---|
| 298 | | - mode->hdisplay - 19); |
|---|
| 310 | + mode->hdisplay - hdse_offset); |
|---|
| 299 | 311 | rcar_du_crtc_write(rcrtc, HSWR, mode->hsync_end - |
|---|
| 300 | 312 | mode->hsync_start - 1); |
|---|
| 301 | 313 | rcar_du_crtc_write(rcrtc, HCR, mode->htotal - 1); |
|---|
| .. | .. |
|---|
| 314 | 326 | rcar_du_crtc_write(rcrtc, DEWR, mode->hdisplay); |
|---|
| 315 | 327 | } |
|---|
| 316 | 328 | |
|---|
| 317 | | -void rcar_du_crtc_route_output(struct drm_crtc *crtc, |
|---|
| 318 | | - enum rcar_du_output output) |
|---|
| 319 | | -{ |
|---|
| 320 | | - struct rcar_du_crtc *rcrtc = to_rcar_crtc(crtc); |
|---|
| 321 | | - struct rcar_du_device *rcdu = rcrtc->group->dev; |
|---|
| 322 | | - |
|---|
| 323 | | - /* |
|---|
| 324 | | - * Store the route from the CRTC output to the DU output. The DU will be |
|---|
| 325 | | - * configured when starting the CRTC. |
|---|
| 326 | | - */ |
|---|
| 327 | | - rcrtc->outputs |= BIT(output); |
|---|
| 328 | | - |
|---|
| 329 | | - /* |
|---|
| 330 | | - * Store RGB routing to DPAD0, the hardware will be configured when |
|---|
| 331 | | - * starting the CRTC. |
|---|
| 332 | | - */ |
|---|
| 333 | | - if (output == RCAR_DU_OUTPUT_DPAD0) |
|---|
| 334 | | - rcdu->dpad0_source = rcrtc->index; |
|---|
| 335 | | -} |
|---|
| 336 | | - |
|---|
| 337 | 329 | static unsigned int plane_zpos(struct rcar_du_plane *plane) |
|---|
| 338 | 330 | { |
|---|
| 339 | 331 | return plane->plane.state->normalized_zpos; |
|---|
| .. | .. |
|---|
| 348 | 340 | static void rcar_du_crtc_update_planes(struct rcar_du_crtc *rcrtc) |
|---|
| 349 | 341 | { |
|---|
| 350 | 342 | struct rcar_du_plane *planes[RCAR_DU_NUM_HW_PLANES]; |
|---|
| 351 | | - struct rcar_du_device *rcdu = rcrtc->group->dev; |
|---|
| 343 | + struct rcar_du_device *rcdu = rcrtc->dev; |
|---|
| 352 | 344 | unsigned int num_planes = 0; |
|---|
| 353 | 345 | unsigned int dptsr_planes; |
|---|
| 354 | 346 | unsigned int hwplanes = 0; |
|---|
| .. | .. |
|---|
| 478 | 470 | |
|---|
| 479 | 471 | static void rcar_du_crtc_wait_page_flip(struct rcar_du_crtc *rcrtc) |
|---|
| 480 | 472 | { |
|---|
| 481 | | - struct rcar_du_device *rcdu = rcrtc->group->dev; |
|---|
| 473 | + struct rcar_du_device *rcdu = rcrtc->dev; |
|---|
| 482 | 474 | |
|---|
| 483 | 475 | if (wait_event_timeout(rcrtc->flip_wait, |
|---|
| 484 | 476 | !rcar_du_crtc_page_flip_pending(rcrtc), |
|---|
| .. | .. |
|---|
| 488 | 480 | dev_warn(rcdu->dev, "page flip timeout\n"); |
|---|
| 489 | 481 | |
|---|
| 490 | 482 | rcar_du_crtc_finish_page_flip(rcrtc); |
|---|
| 483 | +} |
|---|
| 484 | + |
|---|
| 485 | +/* ----------------------------------------------------------------------------- |
|---|
| 486 | + * Color Management Module (CMM) |
|---|
| 487 | + */ |
|---|
| 488 | + |
|---|
| 489 | +static int rcar_du_cmm_check(struct drm_crtc *crtc, |
|---|
| 490 | + struct drm_crtc_state *state) |
|---|
| 491 | +{ |
|---|
| 492 | + struct drm_property_blob *drm_lut = state->gamma_lut; |
|---|
| 493 | + struct rcar_du_crtc *rcrtc = to_rcar_crtc(crtc); |
|---|
| 494 | + struct device *dev = rcrtc->dev->dev; |
|---|
| 495 | + |
|---|
| 496 | + if (!drm_lut) |
|---|
| 497 | + return 0; |
|---|
| 498 | + |
|---|
| 499 | + /* We only accept fully populated LUT tables. */ |
|---|
| 500 | + if (drm_color_lut_size(drm_lut) != CM2_LUT_SIZE) { |
|---|
| 501 | + dev_err(dev, "invalid gamma lut size: %zu bytes\n", |
|---|
| 502 | + drm_lut->length); |
|---|
| 503 | + return -EINVAL; |
|---|
| 504 | + } |
|---|
| 505 | + |
|---|
| 506 | + return 0; |
|---|
| 507 | +} |
|---|
| 508 | + |
|---|
| 509 | +static void rcar_du_cmm_setup(struct drm_crtc *crtc) |
|---|
| 510 | +{ |
|---|
| 511 | + struct drm_property_blob *drm_lut = crtc->state->gamma_lut; |
|---|
| 512 | + struct rcar_du_crtc *rcrtc = to_rcar_crtc(crtc); |
|---|
| 513 | + struct rcar_cmm_config cmm_config = {}; |
|---|
| 514 | + |
|---|
| 515 | + if (!rcrtc->cmm) |
|---|
| 516 | + return; |
|---|
| 517 | + |
|---|
| 518 | + if (drm_lut) |
|---|
| 519 | + cmm_config.lut.table = (struct drm_color_lut *)drm_lut->data; |
|---|
| 520 | + |
|---|
| 521 | + rcar_cmm_setup(rcrtc->cmm, &cmm_config); |
|---|
| 491 | 522 | } |
|---|
| 492 | 523 | |
|---|
| 493 | 524 | /* ----------------------------------------------------------------------------- |
|---|
| .. | .. |
|---|
| 508 | 539 | rcar_du_group_write(rcrtc->group, rcrtc->index % 2 ? DS2PR : DS1PR, 0); |
|---|
| 509 | 540 | |
|---|
| 510 | 541 | /* Enable the VSP compositor. */ |
|---|
| 511 | | - if (rcar_du_has(rcrtc->group->dev, RCAR_DU_FEATURE_VSP1_SOURCE)) |
|---|
| 542 | + if (rcar_du_has(rcrtc->dev, RCAR_DU_FEATURE_VSP1_SOURCE)) |
|---|
| 512 | 543 | rcar_du_vsp_enable(rcrtc); |
|---|
| 513 | 544 | |
|---|
| 514 | 545 | /* Turn vertical blanking interrupt reporting on. */ |
|---|
| 515 | 546 | drm_crtc_vblank_on(&rcrtc->crtc); |
|---|
| 547 | +} |
|---|
| 548 | + |
|---|
| 549 | +static int rcar_du_crtc_get(struct rcar_du_crtc *rcrtc) |
|---|
| 550 | +{ |
|---|
| 551 | + int ret; |
|---|
| 552 | + |
|---|
| 553 | + /* |
|---|
| 554 | + * Guard against double-get, as the function is called from both the |
|---|
| 555 | + * .atomic_enable() and .atomic_begin() handlers. |
|---|
| 556 | + */ |
|---|
| 557 | + if (rcrtc->initialized) |
|---|
| 558 | + return 0; |
|---|
| 559 | + |
|---|
| 560 | + ret = clk_prepare_enable(rcrtc->clock); |
|---|
| 561 | + if (ret < 0) |
|---|
| 562 | + return ret; |
|---|
| 563 | + |
|---|
| 564 | + ret = clk_prepare_enable(rcrtc->extclock); |
|---|
| 565 | + if (ret < 0) |
|---|
| 566 | + goto error_clock; |
|---|
| 567 | + |
|---|
| 568 | + ret = rcar_du_group_get(rcrtc->group); |
|---|
| 569 | + if (ret < 0) |
|---|
| 570 | + goto error_group; |
|---|
| 571 | + |
|---|
| 572 | + rcar_du_crtc_setup(rcrtc); |
|---|
| 573 | + rcrtc->initialized = true; |
|---|
| 574 | + |
|---|
| 575 | + return 0; |
|---|
| 576 | + |
|---|
| 577 | +error_group: |
|---|
| 578 | + clk_disable_unprepare(rcrtc->extclock); |
|---|
| 579 | +error_clock: |
|---|
| 580 | + clk_disable_unprepare(rcrtc->clock); |
|---|
| 581 | + return ret; |
|---|
| 582 | +} |
|---|
| 583 | + |
|---|
| 584 | +static void rcar_du_crtc_put(struct rcar_du_crtc *rcrtc) |
|---|
| 585 | +{ |
|---|
| 586 | + rcar_du_group_put(rcrtc->group); |
|---|
| 587 | + |
|---|
| 588 | + clk_disable_unprepare(rcrtc->extclock); |
|---|
| 589 | + clk_disable_unprepare(rcrtc->clock); |
|---|
| 590 | + |
|---|
| 591 | + rcrtc->initialized = false; |
|---|
| 516 | 592 | } |
|---|
| 517 | 593 | |
|---|
| 518 | 594 | static void rcar_du_crtc_start(struct rcar_du_crtc *rcrtc) |
|---|
| .. | .. |
|---|
| 525 | 601 | * actively driven). |
|---|
| 526 | 602 | */ |
|---|
| 527 | 603 | interlaced = rcrtc->crtc.mode.flags & DRM_MODE_FLAG_INTERLACE; |
|---|
| 528 | | - rcar_du_crtc_clr_set(rcrtc, DSYSR, DSYSR_TVM_MASK | DSYSR_SCM_MASK, |
|---|
| 529 | | - (interlaced ? DSYSR_SCM_INT_VIDEO : 0) | |
|---|
| 530 | | - DSYSR_TVM_MASTER); |
|---|
| 604 | + rcar_du_crtc_dsysr_clr_set(rcrtc, DSYSR_TVM_MASK | DSYSR_SCM_MASK, |
|---|
| 605 | + (interlaced ? DSYSR_SCM_INT_VIDEO : 0) | |
|---|
| 606 | + DSYSR_TVM_MASTER); |
|---|
| 531 | 607 | |
|---|
| 532 | 608 | rcar_du_group_start_stop(rcrtc->group, true); |
|---|
| 533 | 609 | } |
|---|
| 534 | 610 | |
|---|
| 535 | 611 | static void rcar_du_crtc_disable_planes(struct rcar_du_crtc *rcrtc) |
|---|
| 536 | 612 | { |
|---|
| 537 | | - struct rcar_du_device *rcdu = rcrtc->group->dev; |
|---|
| 613 | + struct rcar_du_device *rcdu = rcrtc->dev; |
|---|
| 538 | 614 | struct drm_crtc *crtc = &rcrtc->crtc; |
|---|
| 539 | 615 | u32 status; |
|---|
| 540 | 616 | |
|---|
| .. | .. |
|---|
| 587 | 663 | drm_crtc_vblank_off(crtc); |
|---|
| 588 | 664 | |
|---|
| 589 | 665 | /* Disable the VSP compositor. */ |
|---|
| 590 | | - if (rcar_du_has(rcrtc->group->dev, RCAR_DU_FEATURE_VSP1_SOURCE)) |
|---|
| 666 | + if (rcar_du_has(rcrtc->dev, RCAR_DU_FEATURE_VSP1_SOURCE)) |
|---|
| 591 | 667 | rcar_du_vsp_disable(rcrtc); |
|---|
| 668 | + |
|---|
| 669 | + if (rcrtc->cmm) |
|---|
| 670 | + rcar_cmm_disable(rcrtc->cmm); |
|---|
| 592 | 671 | |
|---|
| 593 | 672 | /* |
|---|
| 594 | 673 | * Select switch sync mode. This stops display operation and configures |
|---|
| 595 | 674 | * the HSYNC and VSYNC signals as inputs. |
|---|
| 675 | + * |
|---|
| 676 | + * TODO: Find another way to stop the display for DUs that don't support |
|---|
| 677 | + * TVM sync. |
|---|
| 596 | 678 | */ |
|---|
| 597 | | - rcar_du_crtc_clr_set(rcrtc, DSYSR, DSYSR_TVM_MASK, DSYSR_TVM_SWITCH); |
|---|
| 679 | + if (rcar_du_has(rcrtc->dev, RCAR_DU_FEATURE_TVM_SYNC)) |
|---|
| 680 | + rcar_du_crtc_dsysr_clr_set(rcrtc, DSYSR_TVM_MASK, |
|---|
| 681 | + DSYSR_TVM_SWITCH); |
|---|
| 598 | 682 | |
|---|
| 599 | 683 | rcar_du_group_start_stop(rcrtc->group, false); |
|---|
| 600 | 684 | } |
|---|
| .. | .. |
|---|
| 603 | 687 | * CRTC Functions |
|---|
| 604 | 688 | */ |
|---|
| 605 | 689 | |
|---|
| 690 | +static int rcar_du_crtc_atomic_check(struct drm_crtc *crtc, |
|---|
| 691 | + struct drm_crtc_state *state) |
|---|
| 692 | +{ |
|---|
| 693 | + struct rcar_du_crtc_state *rstate = to_rcar_crtc_state(state); |
|---|
| 694 | + struct drm_encoder *encoder; |
|---|
| 695 | + int ret; |
|---|
| 696 | + |
|---|
| 697 | + ret = rcar_du_cmm_check(crtc, state); |
|---|
| 698 | + if (ret) |
|---|
| 699 | + return ret; |
|---|
| 700 | + |
|---|
| 701 | + /* Store the routes from the CRTC output to the DU outputs. */ |
|---|
| 702 | + rstate->outputs = 0; |
|---|
| 703 | + |
|---|
| 704 | + drm_for_each_encoder_mask(encoder, crtc->dev, state->encoder_mask) { |
|---|
| 705 | + struct rcar_du_encoder *renc; |
|---|
| 706 | + |
|---|
| 707 | + /* Skip the writeback encoder. */ |
|---|
| 708 | + if (encoder->encoder_type == DRM_MODE_ENCODER_VIRTUAL) |
|---|
| 709 | + continue; |
|---|
| 710 | + |
|---|
| 711 | + renc = to_rcar_encoder(encoder); |
|---|
| 712 | + rstate->outputs |= BIT(renc->output); |
|---|
| 713 | + } |
|---|
| 714 | + |
|---|
| 715 | + return 0; |
|---|
| 716 | +} |
|---|
| 717 | + |
|---|
| 606 | 718 | static void rcar_du_crtc_atomic_enable(struct drm_crtc *crtc, |
|---|
| 607 | 719 | struct drm_crtc_state *old_state) |
|---|
| 608 | 720 | { |
|---|
| 609 | 721 | struct rcar_du_crtc *rcrtc = to_rcar_crtc(crtc); |
|---|
| 722 | + struct rcar_du_crtc_state *rstate = to_rcar_crtc_state(crtc->state); |
|---|
| 723 | + struct rcar_du_device *rcdu = rcrtc->dev; |
|---|
| 724 | + |
|---|
| 725 | + if (rcrtc->cmm) |
|---|
| 726 | + rcar_cmm_enable(rcrtc->cmm); |
|---|
| 727 | + rcar_du_crtc_get(rcrtc); |
|---|
| 610 | 728 | |
|---|
| 611 | 729 | /* |
|---|
| 612 | | - * If the CRTC has already been setup by the .atomic_begin() handler we |
|---|
| 613 | | - * can skip the setup stage. |
|---|
| 730 | + * On D3/E3 the dot clock is provided by the LVDS encoder attached to |
|---|
| 731 | + * the DU channel. We need to enable its clock output explicitly if |
|---|
| 732 | + * the LVDS output is disabled. |
|---|
| 614 | 733 | */ |
|---|
| 615 | | - if (!rcrtc->initialized) { |
|---|
| 616 | | - rcar_du_crtc_get(rcrtc); |
|---|
| 617 | | - rcar_du_crtc_setup(rcrtc); |
|---|
| 618 | | - rcrtc->initialized = true; |
|---|
| 734 | + if (rcdu->info->lvds_clk_mask & BIT(rcrtc->index) && |
|---|
| 735 | + rstate->outputs == BIT(RCAR_DU_OUTPUT_DPAD0)) { |
|---|
| 736 | + struct drm_bridge *bridge = rcdu->lvds[rcrtc->index]; |
|---|
| 737 | + const struct drm_display_mode *mode = |
|---|
| 738 | + &crtc->state->adjusted_mode; |
|---|
| 739 | + |
|---|
| 740 | + rcar_lvds_clk_enable(bridge, mode->clock * 1000); |
|---|
| 619 | 741 | } |
|---|
| 620 | 742 | |
|---|
| 621 | 743 | rcar_du_crtc_start(rcrtc); |
|---|
| 744 | + |
|---|
| 745 | + /* |
|---|
| 746 | + * TODO: The chip manual indicates that CMM tables should be written |
|---|
| 747 | + * after the DU channel has been activated. Investigate the impact |
|---|
| 748 | + * of this restriction on the first displayed frame. |
|---|
| 749 | + */ |
|---|
| 750 | + rcar_du_cmm_setup(crtc); |
|---|
| 622 | 751 | } |
|---|
| 623 | 752 | |
|---|
| 624 | 753 | static void rcar_du_crtc_atomic_disable(struct drm_crtc *crtc, |
|---|
| 625 | 754 | struct drm_crtc_state *old_state) |
|---|
| 626 | 755 | { |
|---|
| 627 | 756 | struct rcar_du_crtc *rcrtc = to_rcar_crtc(crtc); |
|---|
| 757 | + struct rcar_du_crtc_state *rstate = to_rcar_crtc_state(old_state); |
|---|
| 758 | + struct rcar_du_device *rcdu = rcrtc->dev; |
|---|
| 628 | 759 | |
|---|
| 629 | 760 | rcar_du_crtc_stop(rcrtc); |
|---|
| 630 | 761 | rcar_du_crtc_put(rcrtc); |
|---|
| 762 | + |
|---|
| 763 | + if (rcdu->info->lvds_clk_mask & BIT(rcrtc->index) && |
|---|
| 764 | + rstate->outputs == BIT(RCAR_DU_OUTPUT_DPAD0)) { |
|---|
| 765 | + struct drm_bridge *bridge = rcdu->lvds[rcrtc->index]; |
|---|
| 766 | + |
|---|
| 767 | + /* |
|---|
| 768 | + * Disable the LVDS clock output, see |
|---|
| 769 | + * rcar_du_crtc_atomic_enable(). |
|---|
| 770 | + */ |
|---|
| 771 | + rcar_lvds_clk_disable(bridge); |
|---|
| 772 | + } |
|---|
| 631 | 773 | |
|---|
| 632 | 774 | spin_lock_irq(&crtc->dev->event_lock); |
|---|
| 633 | 775 | if (crtc->state->event) { |
|---|
| .. | .. |
|---|
| 635 | 777 | crtc->state->event = NULL; |
|---|
| 636 | 778 | } |
|---|
| 637 | 779 | spin_unlock_irq(&crtc->dev->event_lock); |
|---|
| 638 | | - |
|---|
| 639 | | - rcrtc->initialized = false; |
|---|
| 640 | | - rcrtc->outputs = 0; |
|---|
| 641 | 780 | } |
|---|
| 642 | 781 | |
|---|
| 643 | 782 | static void rcar_du_crtc_atomic_begin(struct drm_crtc *crtc, |
|---|
| .. | .. |
|---|
| 649 | 788 | |
|---|
| 650 | 789 | /* |
|---|
| 651 | 790 | * If a mode set is in progress we can be called with the CRTC disabled. |
|---|
| 652 | | - * We then need to first setup the CRTC in order to configure planes. |
|---|
| 653 | | - * The .atomic_enable() handler will notice and skip the CRTC setup. |
|---|
| 791 | + * We thus need to first get and setup the CRTC in order to configure |
|---|
| 792 | + * planes. We must *not* put the CRTC in .atomic_flush(), as it must be |
|---|
| 793 | + * kept awake until the .atomic_enable() call that will follow. The get |
|---|
| 794 | + * operation in .atomic_enable() will in that case be a no-op, and the |
|---|
| 795 | + * CRTC will be put later in .atomic_disable(). |
|---|
| 796 | + * |
|---|
| 797 | + * If a mode set is not in progress the CRTC is enabled, and the |
|---|
| 798 | + * following get call will be a no-op. There is thus no need to balance |
|---|
| 799 | + * it in .atomic_flush() either. |
|---|
| 654 | 800 | */ |
|---|
| 655 | | - if (!rcrtc->initialized) { |
|---|
| 656 | | - rcar_du_crtc_get(rcrtc); |
|---|
| 657 | | - rcar_du_crtc_setup(rcrtc); |
|---|
| 658 | | - rcrtc->initialized = true; |
|---|
| 659 | | - } |
|---|
| 801 | + rcar_du_crtc_get(rcrtc); |
|---|
| 660 | 802 | |
|---|
| 661 | | - if (rcar_du_has(rcrtc->group->dev, RCAR_DU_FEATURE_VSP1_SOURCE)) |
|---|
| 803 | + /* If the active state changed, we let .atomic_enable handle CMM. */ |
|---|
| 804 | + if (crtc->state->color_mgmt_changed && !crtc->state->active_changed) |
|---|
| 805 | + rcar_du_cmm_setup(crtc); |
|---|
| 806 | + |
|---|
| 807 | + if (rcar_du_has(rcrtc->dev, RCAR_DU_FEATURE_VSP1_SOURCE)) |
|---|
| 662 | 808 | rcar_du_vsp_atomic_begin(rcrtc); |
|---|
| 663 | 809 | } |
|---|
| 664 | 810 | |
|---|
| .. | .. |
|---|
| 680 | 826 | spin_unlock_irqrestore(&dev->event_lock, flags); |
|---|
| 681 | 827 | } |
|---|
| 682 | 828 | |
|---|
| 683 | | - if (rcar_du_has(rcrtc->group->dev, RCAR_DU_FEATURE_VSP1_SOURCE)) |
|---|
| 829 | + if (rcar_du_has(rcrtc->dev, RCAR_DU_FEATURE_VSP1_SOURCE)) |
|---|
| 684 | 830 | rcar_du_vsp_atomic_flush(rcrtc); |
|---|
| 685 | 831 | } |
|---|
| 686 | 832 | |
|---|
| 833 | +static enum drm_mode_status |
|---|
| 834 | +rcar_du_crtc_mode_valid(struct drm_crtc *crtc, |
|---|
| 835 | + const struct drm_display_mode *mode) |
|---|
| 836 | +{ |
|---|
| 837 | + struct rcar_du_crtc *rcrtc = to_rcar_crtc(crtc); |
|---|
| 838 | + struct rcar_du_device *rcdu = rcrtc->dev; |
|---|
| 839 | + bool interlaced = mode->flags & DRM_MODE_FLAG_INTERLACE; |
|---|
| 840 | + unsigned int min_sync_porch; |
|---|
| 841 | + unsigned int vbp; |
|---|
| 842 | + |
|---|
| 843 | + if (interlaced && !rcar_du_has(rcdu, RCAR_DU_FEATURE_INTERLACED)) |
|---|
| 844 | + return MODE_NO_INTERLACE; |
|---|
| 845 | + |
|---|
| 846 | + /* |
|---|
| 847 | + * The hardware requires a minimum combined horizontal sync and back |
|---|
| 848 | + * porch of 20 pixels (when CMM isn't used) or 45 pixels (when CMM is |
|---|
| 849 | + * used), and a minimum vertical back porch of 3 lines. |
|---|
| 850 | + */ |
|---|
| 851 | + min_sync_porch = 20; |
|---|
| 852 | + if (rcrtc->group->cmms_mask & BIT(rcrtc->index % 2)) |
|---|
| 853 | + min_sync_porch += 25; |
|---|
| 854 | + |
|---|
| 855 | + if (mode->htotal - mode->hsync_start < min_sync_porch) |
|---|
| 856 | + return MODE_HBLANK_NARROW; |
|---|
| 857 | + |
|---|
| 858 | + vbp = (mode->vtotal - mode->vsync_end) / (interlaced ? 2 : 1); |
|---|
| 859 | + if (vbp < 3) |
|---|
| 860 | + return MODE_VBLANK_NARROW; |
|---|
| 861 | + |
|---|
| 862 | + return MODE_OK; |
|---|
| 863 | +} |
|---|
| 864 | + |
|---|
| 687 | 865 | static const struct drm_crtc_helper_funcs crtc_helper_funcs = { |
|---|
| 866 | + .atomic_check = rcar_du_crtc_atomic_check, |
|---|
| 688 | 867 | .atomic_begin = rcar_du_crtc_atomic_begin, |
|---|
| 689 | 868 | .atomic_flush = rcar_du_crtc_atomic_flush, |
|---|
| 690 | 869 | .atomic_enable = rcar_du_crtc_atomic_enable, |
|---|
| 691 | 870 | .atomic_disable = rcar_du_crtc_atomic_disable, |
|---|
| 871 | + .mode_valid = rcar_du_crtc_mode_valid, |
|---|
| 692 | 872 | }; |
|---|
| 873 | + |
|---|
| 874 | +static void rcar_du_crtc_crc_init(struct rcar_du_crtc *rcrtc) |
|---|
| 875 | +{ |
|---|
| 876 | + struct rcar_du_device *rcdu = rcrtc->dev; |
|---|
| 877 | + const char **sources; |
|---|
| 878 | + unsigned int count; |
|---|
| 879 | + int i = -1; |
|---|
| 880 | + |
|---|
| 881 | + /* CRC available only on Gen3 HW. */ |
|---|
| 882 | + if (rcdu->info->gen < 3) |
|---|
| 883 | + return; |
|---|
| 884 | + |
|---|
| 885 | + /* Reserve 1 for "auto" source. */ |
|---|
| 886 | + count = rcrtc->vsp->num_planes + 1; |
|---|
| 887 | + |
|---|
| 888 | + sources = kmalloc_array(count, sizeof(*sources), GFP_KERNEL); |
|---|
| 889 | + if (!sources) |
|---|
| 890 | + return; |
|---|
| 891 | + |
|---|
| 892 | + sources[0] = kstrdup("auto", GFP_KERNEL); |
|---|
| 893 | + if (!sources[0]) |
|---|
| 894 | + goto error; |
|---|
| 895 | + |
|---|
| 896 | + for (i = 0; i < rcrtc->vsp->num_planes; ++i) { |
|---|
| 897 | + struct drm_plane *plane = &rcrtc->vsp->planes[i].plane; |
|---|
| 898 | + char name[16]; |
|---|
| 899 | + |
|---|
| 900 | + sprintf(name, "plane%u", plane->base.id); |
|---|
| 901 | + sources[i + 1] = kstrdup(name, GFP_KERNEL); |
|---|
| 902 | + if (!sources[i + 1]) |
|---|
| 903 | + goto error; |
|---|
| 904 | + } |
|---|
| 905 | + |
|---|
| 906 | + rcrtc->sources = sources; |
|---|
| 907 | + rcrtc->sources_count = count; |
|---|
| 908 | + return; |
|---|
| 909 | + |
|---|
| 910 | +error: |
|---|
| 911 | + while (i >= 0) { |
|---|
| 912 | + kfree(sources[i]); |
|---|
| 913 | + i--; |
|---|
| 914 | + } |
|---|
| 915 | + kfree(sources); |
|---|
| 916 | +} |
|---|
| 917 | + |
|---|
| 918 | +static void rcar_du_crtc_crc_cleanup(struct rcar_du_crtc *rcrtc) |
|---|
| 919 | +{ |
|---|
| 920 | + unsigned int i; |
|---|
| 921 | + |
|---|
| 922 | + if (!rcrtc->sources) |
|---|
| 923 | + return; |
|---|
| 924 | + |
|---|
| 925 | + for (i = 0; i < rcrtc->sources_count; i++) |
|---|
| 926 | + kfree(rcrtc->sources[i]); |
|---|
| 927 | + kfree(rcrtc->sources); |
|---|
| 928 | + |
|---|
| 929 | + rcrtc->sources = NULL; |
|---|
| 930 | + rcrtc->sources_count = 0; |
|---|
| 931 | +} |
|---|
| 693 | 932 | |
|---|
| 694 | 933 | static struct drm_crtc_state * |
|---|
| 695 | 934 | rcar_du_crtc_atomic_duplicate_state(struct drm_crtc *crtc) |
|---|
| .. | .. |
|---|
| 717 | 956 | kfree(to_rcar_crtc_state(state)); |
|---|
| 718 | 957 | } |
|---|
| 719 | 958 | |
|---|
| 959 | +static void rcar_du_crtc_cleanup(struct drm_crtc *crtc) |
|---|
| 960 | +{ |
|---|
| 961 | + struct rcar_du_crtc *rcrtc = to_rcar_crtc(crtc); |
|---|
| 962 | + |
|---|
| 963 | + rcar_du_crtc_crc_cleanup(rcrtc); |
|---|
| 964 | + |
|---|
| 965 | + return drm_crtc_cleanup(crtc); |
|---|
| 966 | +} |
|---|
| 967 | + |
|---|
| 720 | 968 | static void rcar_du_crtc_reset(struct drm_crtc *crtc) |
|---|
| 721 | 969 | { |
|---|
| 722 | 970 | struct rcar_du_crtc_state *state; |
|---|
| .. | .. |
|---|
| 733 | 981 | state->crc.source = VSP1_DU_CRC_NONE; |
|---|
| 734 | 982 | state->crc.index = 0; |
|---|
| 735 | 983 | |
|---|
| 736 | | - crtc->state = &state->state; |
|---|
| 737 | | - crtc->state->crtc = crtc; |
|---|
| 984 | + __drm_atomic_helper_crtc_reset(crtc, &state->state); |
|---|
| 738 | 985 | } |
|---|
| 739 | 986 | |
|---|
| 740 | 987 | static int rcar_du_crtc_enable_vblank(struct drm_crtc *crtc) |
|---|
| .. | .. |
|---|
| 756 | 1003 | rcrtc->vblank_enable = false; |
|---|
| 757 | 1004 | } |
|---|
| 758 | 1005 | |
|---|
| 1006 | +static int rcar_du_crtc_parse_crc_source(struct rcar_du_crtc *rcrtc, |
|---|
| 1007 | + const char *source_name, |
|---|
| 1008 | + enum vsp1_du_crc_source *source) |
|---|
| 1009 | +{ |
|---|
| 1010 | + unsigned int index; |
|---|
| 1011 | + int ret; |
|---|
| 1012 | + |
|---|
| 1013 | + /* |
|---|
| 1014 | + * Parse the source name. Supported values are "plane%u" to compute the |
|---|
| 1015 | + * CRC on an input plane (%u is the plane ID), and "auto" to compute the |
|---|
| 1016 | + * CRC on the composer (VSP) output. |
|---|
| 1017 | + */ |
|---|
| 1018 | + |
|---|
| 1019 | + if (!source_name) { |
|---|
| 1020 | + *source = VSP1_DU_CRC_NONE; |
|---|
| 1021 | + return 0; |
|---|
| 1022 | + } else if (!strcmp(source_name, "auto")) { |
|---|
| 1023 | + *source = VSP1_DU_CRC_OUTPUT; |
|---|
| 1024 | + return 0; |
|---|
| 1025 | + } else if (strstarts(source_name, "plane")) { |
|---|
| 1026 | + unsigned int i; |
|---|
| 1027 | + |
|---|
| 1028 | + *source = VSP1_DU_CRC_PLANE; |
|---|
| 1029 | + |
|---|
| 1030 | + ret = kstrtouint(source_name + strlen("plane"), 10, &index); |
|---|
| 1031 | + if (ret < 0) |
|---|
| 1032 | + return ret; |
|---|
| 1033 | + |
|---|
| 1034 | + for (i = 0; i < rcrtc->vsp->num_planes; ++i) { |
|---|
| 1035 | + if (index == rcrtc->vsp->planes[i].plane.base.id) |
|---|
| 1036 | + return i; |
|---|
| 1037 | + } |
|---|
| 1038 | + } |
|---|
| 1039 | + |
|---|
| 1040 | + return -EINVAL; |
|---|
| 1041 | +} |
|---|
| 1042 | + |
|---|
| 1043 | +static int rcar_du_crtc_verify_crc_source(struct drm_crtc *crtc, |
|---|
| 1044 | + const char *source_name, |
|---|
| 1045 | + size_t *values_cnt) |
|---|
| 1046 | +{ |
|---|
| 1047 | + struct rcar_du_crtc *rcrtc = to_rcar_crtc(crtc); |
|---|
| 1048 | + enum vsp1_du_crc_source source; |
|---|
| 1049 | + |
|---|
| 1050 | + if (rcar_du_crtc_parse_crc_source(rcrtc, source_name, &source) < 0) { |
|---|
| 1051 | + DRM_DEBUG_DRIVER("unknown source %s\n", source_name); |
|---|
| 1052 | + return -EINVAL; |
|---|
| 1053 | + } |
|---|
| 1054 | + |
|---|
| 1055 | + *values_cnt = 1; |
|---|
| 1056 | + return 0; |
|---|
| 1057 | +} |
|---|
| 1058 | + |
|---|
| 1059 | +static const char *const * |
|---|
| 1060 | +rcar_du_crtc_get_crc_sources(struct drm_crtc *crtc, size_t *count) |
|---|
| 1061 | +{ |
|---|
| 1062 | + struct rcar_du_crtc *rcrtc = to_rcar_crtc(crtc); |
|---|
| 1063 | + |
|---|
| 1064 | + *count = rcrtc->sources_count; |
|---|
| 1065 | + return rcrtc->sources; |
|---|
| 1066 | +} |
|---|
| 1067 | + |
|---|
| 759 | 1068 | static int rcar_du_crtc_set_crc_source(struct drm_crtc *crtc, |
|---|
| 760 | 1069 | const char *source_name) |
|---|
| 761 | 1070 | { |
|---|
| .. | .. |
|---|
| 764 | 1073 | struct drm_crtc_state *crtc_state; |
|---|
| 765 | 1074 | struct drm_atomic_state *state; |
|---|
| 766 | 1075 | enum vsp1_du_crc_source source; |
|---|
| 767 | | - unsigned int index = 0; |
|---|
| 768 | | - unsigned int i; |
|---|
| 1076 | + unsigned int index; |
|---|
| 769 | 1077 | int ret; |
|---|
| 770 | 1078 | |
|---|
| 771 | | - /* |
|---|
| 772 | | - * Parse the source name. Supported values are "plane%u" to compute the |
|---|
| 773 | | - * CRC on an input plane (%u is the plane ID), and "auto" to compute the |
|---|
| 774 | | - * CRC on the composer (VSP) output. |
|---|
| 775 | | - */ |
|---|
| 776 | | - if (!source_name) { |
|---|
| 777 | | - source = VSP1_DU_CRC_NONE; |
|---|
| 778 | | - } else if (!strcmp(source_name, "auto")) { |
|---|
| 779 | | - source = VSP1_DU_CRC_OUTPUT; |
|---|
| 780 | | - } else if (strstarts(source_name, "plane")) { |
|---|
| 781 | | - source = VSP1_DU_CRC_PLANE; |
|---|
| 1079 | + ret = rcar_du_crtc_parse_crc_source(rcrtc, source_name, &source); |
|---|
| 1080 | + if (ret < 0) |
|---|
| 1081 | + return ret; |
|---|
| 782 | 1082 | |
|---|
| 783 | | - ret = kstrtouint(source_name + strlen("plane"), 10, &index); |
|---|
| 784 | | - if (ret < 0) |
|---|
| 785 | | - return ret; |
|---|
| 786 | | - |
|---|
| 787 | | - for (i = 0; i < rcrtc->vsp->num_planes; ++i) { |
|---|
| 788 | | - if (index == rcrtc->vsp->planes[i].plane.base.id) { |
|---|
| 789 | | - index = i; |
|---|
| 790 | | - break; |
|---|
| 791 | | - } |
|---|
| 792 | | - } |
|---|
| 793 | | - |
|---|
| 794 | | - if (i >= rcrtc->vsp->num_planes) |
|---|
| 795 | | - return -EINVAL; |
|---|
| 796 | | - } else { |
|---|
| 797 | | - return -EINVAL; |
|---|
| 798 | | - } |
|---|
| 1083 | + index = ret; |
|---|
| 799 | 1084 | |
|---|
| 800 | 1085 | /* Perform an atomic commit to set the CRC source. */ |
|---|
| 801 | 1086 | drm_modeset_acquire_init(&ctx, 0); |
|---|
| .. | .. |
|---|
| 850 | 1135 | |
|---|
| 851 | 1136 | static const struct drm_crtc_funcs crtc_funcs_gen3 = { |
|---|
| 852 | 1137 | .reset = rcar_du_crtc_reset, |
|---|
| 853 | | - .destroy = drm_crtc_cleanup, |
|---|
| 1138 | + .destroy = rcar_du_crtc_cleanup, |
|---|
| 854 | 1139 | .set_config = drm_atomic_helper_set_config, |
|---|
| 855 | 1140 | .page_flip = drm_atomic_helper_page_flip, |
|---|
| 856 | 1141 | .atomic_duplicate_state = rcar_du_crtc_atomic_duplicate_state, |
|---|
| .. | .. |
|---|
| 858 | 1143 | .enable_vblank = rcar_du_crtc_enable_vblank, |
|---|
| 859 | 1144 | .disable_vblank = rcar_du_crtc_disable_vblank, |
|---|
| 860 | 1145 | .set_crc_source = rcar_du_crtc_set_crc_source, |
|---|
| 1146 | + .verify_crc_source = rcar_du_crtc_verify_crc_source, |
|---|
| 1147 | + .get_crc_sources = rcar_du_crtc_get_crc_sources, |
|---|
| 1148 | + .gamma_set = drm_atomic_helper_legacy_gamma_set, |
|---|
| 861 | 1149 | }; |
|---|
| 862 | 1150 | |
|---|
| 863 | 1151 | /* ----------------------------------------------------------------------------- |
|---|
| .. | .. |
|---|
| 867 | 1155 | static irqreturn_t rcar_du_crtc_irq(int irq, void *arg) |
|---|
| 868 | 1156 | { |
|---|
| 869 | 1157 | struct rcar_du_crtc *rcrtc = arg; |
|---|
| 870 | | - struct rcar_du_device *rcdu = rcrtc->group->dev; |
|---|
| 1158 | + struct rcar_du_device *rcdu = rcrtc->dev; |
|---|
| 871 | 1159 | irqreturn_t ret = IRQ_NONE; |
|---|
| 872 | 1160 | u32 status; |
|---|
| 873 | 1161 | |
|---|
| .. | .. |
|---|
| 943 | 1231 | clk = devm_clk_get(rcdu->dev, clk_name); |
|---|
| 944 | 1232 | if (!IS_ERR(clk)) { |
|---|
| 945 | 1233 | rcrtc->extclock = clk; |
|---|
| 946 | | - } else if (PTR_ERR(rcrtc->clock) == -EPROBE_DEFER) { |
|---|
| 947 | | - dev_info(rcdu->dev, "can't get external clock %u\n", hwindex); |
|---|
| 1234 | + } else if (PTR_ERR(clk) == -EPROBE_DEFER) { |
|---|
| 948 | 1235 | return -EPROBE_DEFER; |
|---|
| 1236 | + } else if (rcdu->info->dpll_mask & BIT(hwindex)) { |
|---|
| 1237 | + /* |
|---|
| 1238 | + * DU channels that have a display PLL can't use the internal |
|---|
| 1239 | + * system clock and thus require an external clock. |
|---|
| 1240 | + */ |
|---|
| 1241 | + ret = PTR_ERR(clk); |
|---|
| 1242 | + dev_err(rcdu->dev, "can't get dclkin.%u: %d\n", hwindex, ret); |
|---|
| 1243 | + return ret; |
|---|
| 949 | 1244 | } |
|---|
| 950 | 1245 | |
|---|
| 951 | 1246 | init_waitqueue_head(&rcrtc->flip_wait); |
|---|
| 952 | 1247 | init_waitqueue_head(&rcrtc->vblank_wait); |
|---|
| 953 | 1248 | spin_lock_init(&rcrtc->vblank_lock); |
|---|
| 954 | 1249 | |
|---|
| 1250 | + rcrtc->dev = rcdu; |
|---|
| 955 | 1251 | rcrtc->group = rgrp; |
|---|
| 956 | 1252 | rcrtc->mmio_offset = mmio_offsets[hwindex]; |
|---|
| 957 | 1253 | rcrtc->index = hwindex; |
|---|
| 1254 | + rcrtc->dsysr = (rcrtc->index % 2 ? 0 : DSYSR_DRES) | DSYSR_TVM_TVSYNC; |
|---|
| 958 | 1255 | |
|---|
| 959 | 1256 | if (rcar_du_has(rcdu, RCAR_DU_FEATURE_VSP1_SOURCE)) |
|---|
| 960 | 1257 | primary = &rcrtc->vsp->planes[rcrtc->vsp_pipe].plane; |
|---|
| .. | .. |
|---|
| 968 | 1265 | if (ret < 0) |
|---|
| 969 | 1266 | return ret; |
|---|
| 970 | 1267 | |
|---|
| 971 | | - drm_crtc_helper_add(crtc, &crtc_helper_funcs); |
|---|
| 1268 | + /* CMM might be disabled for this CRTC. */ |
|---|
| 1269 | + if (rcdu->cmms[swindex]) { |
|---|
| 1270 | + rcrtc->cmm = rcdu->cmms[swindex]; |
|---|
| 1271 | + rgrp->cmms_mask |= BIT(hwindex % 2); |
|---|
| 972 | 1272 | |
|---|
| 973 | | - /* Start with vertical blanking interrupt reporting disabled. */ |
|---|
| 974 | | - drm_crtc_vblank_off(crtc); |
|---|
| 1273 | + drm_mode_crtc_set_gamma_size(crtc, CM2_LUT_SIZE); |
|---|
| 1274 | + drm_crtc_enable_color_mgmt(crtc, 0, false, CM2_LUT_SIZE); |
|---|
| 1275 | + } |
|---|
| 1276 | + |
|---|
| 1277 | + drm_crtc_helper_add(crtc, &crtc_helper_funcs); |
|---|
| 975 | 1278 | |
|---|
| 976 | 1279 | /* Register the interrupt handler. */ |
|---|
| 977 | 1280 | if (rcar_du_has(rcdu, RCAR_DU_FEATURE_CRTC_IRQ_CLOCK)) { |
|---|
| .. | .. |
|---|
| 996 | 1299 | return ret; |
|---|
| 997 | 1300 | } |
|---|
| 998 | 1301 | |
|---|
| 1302 | + rcar_du_crtc_crc_init(rcrtc); |
|---|
| 1303 | + |
|---|
| 999 | 1304 | return 0; |
|---|
| 1000 | 1305 | } |
|---|