.. | .. |
---|
| 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 | } |
---|