]>
Commit | Line | Data |
---|---|---|
0bbce9eb | 1 | // SPDX-License-Identifier: GPL-2.0+ |
4bf8e196 LP |
2 | /* |
3 | * rcar_du_plane.c -- R-Car Display Unit Planes | |
4 | * | |
2427b303 | 5 | * Copyright (C) 2013-2015 Renesas Electronics Corporation |
4bf8e196 LP |
6 | * |
7 | * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com) | |
4bf8e196 LP |
8 | */ |
9 | ||
ab334e13 | 10 | #include <drm/drm_atomic.h> |
3e8da87d | 11 | #include <drm/drm_atomic_helper.h> |
4bf8e196 | 12 | #include <drm/drm_crtc.h> |
7d0b0ada | 13 | #include <drm/drm_device.h> |
4bf8e196 | 14 | #include <drm/drm_fb_cma_helper.h> |
7d0b0ada | 15 | #include <drm/drm_fourcc.h> |
4bf8e196 | 16 | #include <drm/drm_gem_cma_helper.h> |
920888a2 | 17 | #include <drm/drm_plane_helper.h> |
4bf8e196 LP |
18 | |
19 | #include "rcar_du_drv.h" | |
34a04f2b | 20 | #include "rcar_du_group.h" |
4bf8e196 LP |
21 | #include "rcar_du_kms.h" |
22 | #include "rcar_du_plane.h" | |
23 | #include "rcar_du_regs.h" | |
24 | ||
ab334e13 LP |
25 | /* ----------------------------------------------------------------------------- |
26 | * Atomic hardware plane allocator | |
27 | * | |
28 | * The hardware plane allocator is solely based on the atomic plane states | |
29 | * without keeping any external state to avoid races between .atomic_check() | |
30 | * and .atomic_commit(). | |
31 | * | |
32 | * The core idea is to avoid using a free planes bitmask that would need to be | |
33 | * shared between check and commit handlers with a collective knowledge based on | |
34 | * the allocated hardware plane(s) for each KMS plane. The allocator then loops | |
35 | * over all plane states to compute the free planes bitmask, allocates hardware | |
36 | * planes based on that bitmask, and stores the result back in the plane states. | |
37 | * | |
38 | * For this to work we need to access the current state of planes not touched by | |
39 | * the atomic update. To ensure that it won't be modified, we need to lock all | |
40 | * planes using drm_atomic_get_plane_state(). This effectively serializes atomic | |
41 | * updates from .atomic_check() up to completion (when swapping the states if | |
42 | * the check step has succeeded) or rollback (when freeing the states if the | |
43 | * check step has failed). | |
44 | * | |
45 | * Allocation is performed in the .atomic_check() handler and applied | |
46 | * automatically when the core swaps the old and new states. | |
47 | */ | |
48 | ||
a01ce667 ML |
49 | static bool rcar_du_plane_needs_realloc( |
50 | const struct rcar_du_plane_state *old_state, | |
51 | const struct rcar_du_plane_state *new_state) | |
ab334e13 | 52 | { |
f3bafc12 LP |
53 | /* |
54 | * Lowering the number of planes doesn't strictly require reallocation | |
ab334e13 LP |
55 | * as the extra hardware plane will be freed when committing, but doing |
56 | * so could lead to more fragmentation. | |
57 | */ | |
a01ce667 ML |
58 | if (!old_state->format || |
59 | old_state->format->planes != new_state->format->planes) | |
ab334e13 LP |
60 | return true; |
61 | ||
62 | /* Reallocate hardware planes if the source has changed. */ | |
a01ce667 | 63 | if (old_state->source != new_state->source) |
ab334e13 LP |
64 | return true; |
65 | ||
66 | return false; | |
67 | } | |
68 | ||
69 | static unsigned int rcar_du_plane_hwmask(struct rcar_du_plane_state *state) | |
70 | { | |
71 | unsigned int mask; | |
72 | ||
73 | if (state->hwindex == -1) | |
74 | return 0; | |
75 | ||
76 | mask = 1 << state->hwindex; | |
77 | if (state->format->planes == 2) | |
78 | mask |= 1 << ((state->hwindex + 1) % 8); | |
79 | ||
80 | return mask; | |
81 | } | |
82 | ||
83 | /* | |
84 | * The R8A7790 DU can source frames directly from the VSP1 devices VSPD0 and | |
85 | * VSPD1. VSPD0 feeds DU0/1 plane 0, and VSPD1 feeds either DU2 plane 0 or | |
86 | * DU0/1 plane 1. | |
87 | * | |
88 | * Allocate the correct fixed plane when sourcing frames from VSPD0 or VSPD1, | |
89 | * and allocate planes in reverse index order otherwise to ensure maximum | |
90 | * availability of planes 0 and 1. | |
91 | * | |
92 | * The caller is responsible for ensuring that the requested source is | |
93 | * compatible with the DU revision. | |
94 | */ | |
95 | static int rcar_du_plane_hwalloc(struct rcar_du_plane *plane, | |
96 | struct rcar_du_plane_state *state, | |
97 | unsigned int free) | |
98 | { | |
99 | unsigned int num_planes = state->format->planes; | |
100 | int fixed = -1; | |
101 | int i; | |
102 | ||
103 | if (state->source == RCAR_DU_PLANE_VSPD0) { | |
104 | /* VSPD0 feeds plane 0 on DU0/1. */ | |
105 | if (plane->group->index != 0) | |
106 | return -EINVAL; | |
107 | ||
108 | fixed = 0; | |
109 | } else if (state->source == RCAR_DU_PLANE_VSPD1) { | |
110 | /* VSPD1 feeds plane 1 on DU0/1 or plane 0 on DU2. */ | |
111 | fixed = plane->group->index == 0 ? 1 : 0; | |
112 | } | |
113 | ||
114 | if (fixed >= 0) | |
115 | return free & (1 << fixed) ? fixed : -EBUSY; | |
116 | ||
117 | for (i = RCAR_DU_NUM_HW_PLANES - 1; i >= 0; --i) { | |
118 | if (!(free & (1 << i))) | |
119 | continue; | |
120 | ||
121 | if (num_planes == 1 || free & (1 << ((i + 1) % 8))) | |
122 | break; | |
123 | } | |
124 | ||
125 | return i < 0 ? -EBUSY : i; | |
126 | } | |
127 | ||
128 | int rcar_du_atomic_check_planes(struct drm_device *dev, | |
129 | struct drm_atomic_state *state) | |
130 | { | |
131 | struct rcar_du_device *rcdu = dev->dev_private; | |
132 | unsigned int group_freed_planes[RCAR_DU_MAX_GROUPS] = { 0, }; | |
133 | unsigned int group_free_planes[RCAR_DU_MAX_GROUPS] = { 0, }; | |
134 | bool needs_realloc = false; | |
135 | unsigned int groups = 0; | |
136 | unsigned int i; | |
09ad807b | 137 | struct drm_plane *drm_plane; |
a01ce667 ML |
138 | struct drm_plane_state *old_drm_plane_state; |
139 | struct drm_plane_state *new_drm_plane_state; | |
ab334e13 LP |
140 | |
141 | /* Check if hardware planes need to be reallocated. */ | |
a01ce667 ML |
142 | for_each_oldnew_plane_in_state(state, drm_plane, old_drm_plane_state, |
143 | new_drm_plane_state, i) { | |
144 | struct rcar_du_plane_state *old_plane_state; | |
145 | struct rcar_du_plane_state *new_plane_state; | |
ab334e13 LP |
146 | struct rcar_du_plane *plane; |
147 | unsigned int index; | |
148 | ||
09ad807b | 149 | plane = to_rcar_plane(drm_plane); |
a01ce667 ML |
150 | old_plane_state = to_rcar_plane_state(old_drm_plane_state); |
151 | new_plane_state = to_rcar_plane_state(new_drm_plane_state); | |
ab334e13 | 152 | |
07ece19b | 153 | dev_dbg(rcdu->dev, "%s: checking plane (%u,%tu)\n", __func__, |
ab334e13 LP |
154 | plane->group->index, plane - plane->group->planes); |
155 | ||
f3bafc12 LP |
156 | /* |
157 | * If the plane is being disabled we don't need to go through | |
ab334e13 LP |
158 | * the full reallocation procedure. Just mark the hardware |
159 | * plane(s) as freed. | |
160 | */ | |
a01ce667 | 161 | if (!new_plane_state->format) { |
ab334e13 LP |
162 | dev_dbg(rcdu->dev, "%s: plane is being disabled\n", |
163 | __func__); | |
164 | index = plane - plane->group->planes; | |
165 | group_freed_planes[plane->group->index] |= 1 << index; | |
a01ce667 | 166 | new_plane_state->hwindex = -1; |
ab334e13 LP |
167 | continue; |
168 | } | |
169 | ||
f3bafc12 LP |
170 | /* |
171 | * If the plane needs to be reallocated mark it as such, and | |
ab334e13 LP |
172 | * mark the hardware plane(s) as free. |
173 | */ | |
a01ce667 | 174 | if (rcar_du_plane_needs_realloc(old_plane_state, new_plane_state)) { |
ab334e13 LP |
175 | dev_dbg(rcdu->dev, "%s: plane needs reallocation\n", |
176 | __func__); | |
177 | groups |= 1 << plane->group->index; | |
178 | needs_realloc = true; | |
179 | ||
180 | index = plane - plane->group->planes; | |
181 | group_freed_planes[plane->group->index] |= 1 << index; | |
a01ce667 | 182 | new_plane_state->hwindex = -1; |
ab334e13 LP |
183 | } |
184 | } | |
185 | ||
186 | if (!needs_realloc) | |
187 | return 0; | |
188 | ||
f3bafc12 LP |
189 | /* |
190 | * Grab all plane states for the groups that need reallocation to ensure | |
ab334e13 LP |
191 | * locking and avoid racy updates. This serializes the update operation, |
192 | * but there's not much we can do about it as that's the hardware | |
193 | * design. | |
194 | * | |
195 | * Compute the used planes mask for each group at the same time to avoid | |
196 | * looping over the planes separately later. | |
197 | */ | |
198 | while (groups) { | |
199 | unsigned int index = ffs(groups) - 1; | |
200 | struct rcar_du_group *group = &rcdu->groups[index]; | |
201 | unsigned int used_planes = 0; | |
202 | ||
203 | dev_dbg(rcdu->dev, "%s: finding free planes for group %u\n", | |
204 | __func__, index); | |
205 | ||
206 | for (i = 0; i < group->num_planes; ++i) { | |
207 | struct rcar_du_plane *plane = &group->planes[i]; | |
a01ce667 | 208 | struct rcar_du_plane_state *new_plane_state; |
ab334e13 LP |
209 | struct drm_plane_state *s; |
210 | ||
211 | s = drm_atomic_get_plane_state(state, &plane->plane); | |
212 | if (IS_ERR(s)) | |
213 | return PTR_ERR(s); | |
214 | ||
f3bafc12 LP |
215 | /* |
216 | * If the plane has been freed in the above loop its | |
ab334e13 LP |
217 | * hardware planes must not be added to the used planes |
218 | * bitmask. However, the current state doesn't reflect | |
219 | * the free state yet, as we've modified the new state | |
220 | * above. Use the local freed planes list to check for | |
221 | * that condition instead. | |
222 | */ | |
223 | if (group_freed_planes[index] & (1 << i)) { | |
224 | dev_dbg(rcdu->dev, | |
07ece19b | 225 | "%s: plane (%u,%tu) has been freed, skipping\n", |
ab334e13 LP |
226 | __func__, plane->group->index, |
227 | plane - plane->group->planes); | |
228 | continue; | |
229 | } | |
230 | ||
a01ce667 ML |
231 | new_plane_state = to_rcar_plane_state(s); |
232 | used_planes |= rcar_du_plane_hwmask(new_plane_state); | |
ab334e13 LP |
233 | |
234 | dev_dbg(rcdu->dev, | |
07ece19b | 235 | "%s: plane (%u,%tu) uses %u hwplanes (index %d)\n", |
ab334e13 LP |
236 | __func__, plane->group->index, |
237 | plane - plane->group->planes, | |
a01ce667 ML |
238 | new_plane_state->format ? |
239 | new_plane_state->format->planes : 0, | |
240 | new_plane_state->hwindex); | |
ab334e13 LP |
241 | } |
242 | ||
243 | group_free_planes[index] = 0xff & ~used_planes; | |
244 | groups &= ~(1 << index); | |
245 | ||
246 | dev_dbg(rcdu->dev, "%s: group %u free planes mask 0x%02x\n", | |
247 | __func__, index, group_free_planes[index]); | |
248 | } | |
249 | ||
250 | /* Reallocate hardware planes for each plane that needs it. */ | |
a01ce667 ML |
251 | for_each_oldnew_plane_in_state(state, drm_plane, old_drm_plane_state, |
252 | new_drm_plane_state, i) { | |
253 | struct rcar_du_plane_state *old_plane_state; | |
254 | struct rcar_du_plane_state *new_plane_state; | |
ab334e13 LP |
255 | struct rcar_du_plane *plane; |
256 | unsigned int crtc_planes; | |
257 | unsigned int free; | |
258 | int idx; | |
259 | ||
09ad807b | 260 | plane = to_rcar_plane(drm_plane); |
a01ce667 ML |
261 | old_plane_state = to_rcar_plane_state(old_drm_plane_state); |
262 | new_plane_state = to_rcar_plane_state(new_drm_plane_state); | |
ab334e13 | 263 | |
07ece19b | 264 | dev_dbg(rcdu->dev, "%s: allocating plane (%u,%tu)\n", __func__, |
ab334e13 LP |
265 | plane->group->index, plane - plane->group->planes); |
266 | ||
f3bafc12 LP |
267 | /* |
268 | * Skip planes that are being disabled or don't need to be | |
ab334e13 LP |
269 | * reallocated. |
270 | */ | |
a01ce667 ML |
271 | if (!new_plane_state->format || |
272 | !rcar_du_plane_needs_realloc(old_plane_state, new_plane_state)) | |
ab334e13 LP |
273 | continue; |
274 | ||
f3bafc12 LP |
275 | /* |
276 | * Try to allocate the plane from the free planes currently | |
ab334e13 LP |
277 | * associated with the target CRTC to avoid restarting the CRTC |
278 | * group and thus minimize flicker. If it fails fall back to | |
279 | * allocating from all free planes. | |
280 | */ | |
a01ce667 | 281 | crtc_planes = to_rcar_crtc(new_plane_state->state.crtc)->index % 2 |
ab334e13 LP |
282 | ? plane->group->dptsr_planes |
283 | : ~plane->group->dptsr_planes; | |
284 | free = group_free_planes[plane->group->index]; | |
285 | ||
a01ce667 | 286 | idx = rcar_du_plane_hwalloc(plane, new_plane_state, |
ab334e13 LP |
287 | free & crtc_planes); |
288 | if (idx < 0) | |
a01ce667 | 289 | idx = rcar_du_plane_hwalloc(plane, new_plane_state, |
ab334e13 LP |
290 | free); |
291 | if (idx < 0) { | |
292 | dev_dbg(rcdu->dev, "%s: no available hardware plane\n", | |
293 | __func__); | |
294 | return idx; | |
295 | } | |
296 | ||
297 | dev_dbg(rcdu->dev, "%s: allocated %u hwplanes (index %u)\n", | |
a01ce667 | 298 | __func__, new_plane_state->format->planes, idx); |
ab334e13 | 299 | |
a01ce667 | 300 | new_plane_state->hwindex = idx; |
ab334e13 LP |
301 | |
302 | group_free_planes[plane->group->index] &= | |
a01ce667 | 303 | ~rcar_du_plane_hwmask(new_plane_state); |
ab334e13 LP |
304 | |
305 | dev_dbg(rcdu->dev, "%s: group %u free planes mask 0x%02x\n", | |
306 | __func__, plane->group->index, | |
307 | group_free_planes[plane->group->index]); | |
308 | } | |
309 | ||
310 | return 0; | |
311 | } | |
312 | ||
313 | /* ----------------------------------------------------------------------------- | |
314 | * Plane Setup | |
315 | */ | |
316 | ||
4bf8e196 LP |
317 | #define RCAR_DU_COLORKEY_NONE (0 << 24) |
318 | #define RCAR_DU_COLORKEY_SOURCE (1 << 24) | |
319 | #define RCAR_DU_COLORKEY_MASK (1 << 24) | |
320 | ||
cb2025d2 | 321 | static void rcar_du_plane_write(struct rcar_du_group *rgrp, |
4bf8e196 LP |
322 | unsigned int index, u32 reg, u32 data) |
323 | { | |
cb2025d2 LP |
324 | rcar_du_write(rgrp->dev, rgrp->mmio_offset + index * PLANE_OFF + reg, |
325 | data); | |
4bf8e196 LP |
326 | } |
327 | ||
34a04f2b LP |
328 | static void rcar_du_plane_setup_scanout(struct rcar_du_group *rgrp, |
329 | const struct rcar_du_plane_state *state) | |
4bf8e196 | 330 | { |
401712e0 LP |
331 | unsigned int src_x = state->state.src.x1 >> 16; |
332 | unsigned int src_y = state->state.src.y1 >> 16; | |
5ee5a81d | 333 | unsigned int index = state->hwindex; |
2f13c529 | 334 | unsigned int pitch; |
906eff7f | 335 | bool interlaced; |
2f13c529 | 336 | u32 dma[2]; |
eb86301f | 337 | |
5bfcbce0 | 338 | interlaced = state->state.crtc->state->adjusted_mode.flags |
47094194 | 339 | & DRM_MODE_FLAG_INTERLACE; |
906eff7f | 340 | |
34a04f2b LP |
341 | if (state->source == RCAR_DU_PLANE_MEMORY) { |
342 | struct drm_framebuffer *fb = state->state.fb; | |
343 | struct drm_gem_cma_object *gem; | |
344 | unsigned int i; | |
345 | ||
346 | if (state->format->planes == 2) | |
347 | pitch = fb->pitches[0]; | |
348 | else | |
349 | pitch = fb->pitches[0] * 8 / state->format->bpp; | |
eb86301f | 350 | |
34a04f2b LP |
351 | for (i = 0; i < state->format->planes; ++i) { |
352 | gem = drm_fb_cma_get_gem_obj(fb, i); | |
353 | dma[i] = gem->paddr + fb->offsets[i]; | |
354 | } | |
355 | } else { | |
401712e0 | 356 | pitch = drm_rect_width(&state->state.src) >> 16; |
34a04f2b LP |
357 | dma[0] = 0; |
358 | dma[1] = 0; | |
2f13c529 | 359 | } |
906eff7f | 360 | |
f3bafc12 LP |
361 | /* |
362 | * Memory pitch (expressed in pixels). Must be doubled for interlaced | |
34a04f2b LP |
363 | * operation with 32bpp formats. |
364 | */ | |
2f13c529 LP |
365 | rcar_du_plane_write(rgrp, index, PnMWR, |
366 | (interlaced && state->format->bpp == 32) ? | |
367 | pitch * 2 : pitch); | |
4bf8e196 | 368 | |
f3bafc12 LP |
369 | /* |
370 | * The Y position is expressed in raster line units and must be doubled | |
9e7db06d LP |
371 | * for 32bpp formats, according to the R8A7790 datasheet. No mention of |
372 | * doubling the Y position is found in the R8A7779 datasheet, but the | |
373 | * rule seems to apply there as well. | |
374 | * | |
906eff7f LP |
375 | * Despite not being documented, doubling seem not to be needed when |
376 | * operating in interlaced mode. | |
377 | * | |
9e7db06d | 378 | * Similarly, for the second plane, NV12 and NV21 formats seem to |
906eff7f LP |
379 | * require a halved Y position value, in both progressive and interlaced |
380 | * modes. | |
4bf8e196 | 381 | */ |
287bdf03 LP |
382 | rcar_du_plane_write(rgrp, index, PnSPXR, src_x); |
383 | rcar_du_plane_write(rgrp, index, PnSPYR, src_y * | |
5bfcbce0 | 384 | (!interlaced && state->format->bpp == 32 ? 2 : 1)); |
f398f344 | 385 | |
2f13c529 | 386 | rcar_du_plane_write(rgrp, index, PnDSA0R, dma[0]); |
4bf8e196 | 387 | |
5bfcbce0 | 388 | if (state->format->planes == 2) { |
4bf8e196 LP |
389 | index = (index + 1) % 8; |
390 | ||
2f13c529 | 391 | rcar_du_plane_write(rgrp, index, PnMWR, pitch); |
49785e25 | 392 | |
287bdf03 LP |
393 | rcar_du_plane_write(rgrp, index, PnSPXR, src_x); |
394 | rcar_du_plane_write(rgrp, index, PnSPYR, src_y * | |
5bfcbce0 | 395 | (state->format->bpp == 16 ? 2 : 1) / 2); |
eb86301f | 396 | |
2f13c529 | 397 | rcar_du_plane_write(rgrp, index, PnDSA0R, dma[1]); |
4bf8e196 LP |
398 | } |
399 | } | |
400 | ||
34a04f2b LP |
401 | static void rcar_du_plane_setup_mode(struct rcar_du_group *rgrp, |
402 | unsigned int index, | |
403 | const struct rcar_du_plane_state *state) | |
4bf8e196 | 404 | { |
4bf8e196 LP |
405 | u32 colorkey; |
406 | u32 pnmr; | |
407 | ||
f3bafc12 LP |
408 | /* |
409 | * The PnALPHAR register controls alpha-blending in 16bpp formats | |
4bf8e196 LP |
410 | * (ARGB1555 and XRGB1555). |
411 | * | |
412 | * For ARGB, set the alpha value to 0, and enable alpha-blending when | |
413 | * the A bit is 0. This maps A=0 to alpha=0 and A=1 to alpha=255. | |
414 | * | |
415 | * For XRGB, set the alpha value to the plane-wide alpha value and | |
416 | * enable alpha-blending regardless of the X bit value. | |
417 | */ | |
5bfcbce0 | 418 | if (state->format->fourcc != DRM_FORMAT_XRGB1555) |
cb2025d2 | 419 | rcar_du_plane_write(rgrp, index, PnALPHAR, PnALPHAR_ABIT_0); |
4bf8e196 | 420 | else |
cb2025d2 | 421 | rcar_du_plane_write(rgrp, index, PnALPHAR, |
301a9b8d | 422 | PnALPHAR_ABIT_X | state->state.alpha >> 8); |
4bf8e196 | 423 | |
5bfcbce0 | 424 | pnmr = PnMR_BM_MD | state->format->pnmr; |
4bf8e196 | 425 | |
f3bafc12 LP |
426 | /* |
427 | * Disable color keying when requested. YUV formats have the | |
4bf8e196 LP |
428 | * PnMR_SPIM_TP_OFF bit set in their pnmr field, disabling color keying |
429 | * automatically. | |
430 | */ | |
4407cc02 | 431 | if ((state->colorkey & RCAR_DU_COLORKEY_MASK) == RCAR_DU_COLORKEY_NONE) |
4bf8e196 LP |
432 | pnmr |= PnMR_SPIM_TP_OFF; |
433 | ||
434 | /* For packed YUV formats we need to select the U/V order. */ | |
5bfcbce0 | 435 | if (state->format->fourcc == DRM_FORMAT_YUYV) |
4bf8e196 LP |
436 | pnmr |= PnMR_YCDF_YUYV; |
437 | ||
cb2025d2 | 438 | rcar_du_plane_write(rgrp, index, PnMR, pnmr); |
4bf8e196 | 439 | |
5bfcbce0 | 440 | switch (state->format->fourcc) { |
4bf8e196 | 441 | case DRM_FORMAT_RGB565: |
4407cc02 LP |
442 | colorkey = ((state->colorkey & 0xf80000) >> 8) |
443 | | ((state->colorkey & 0x00fc00) >> 5) | |
444 | | ((state->colorkey & 0x0000f8) >> 3); | |
cb2025d2 | 445 | rcar_du_plane_write(rgrp, index, PnTC2R, colorkey); |
4bf8e196 LP |
446 | break; |
447 | ||
448 | case DRM_FORMAT_ARGB1555: | |
449 | case DRM_FORMAT_XRGB1555: | |
4407cc02 LP |
450 | colorkey = ((state->colorkey & 0xf80000) >> 9) |
451 | | ((state->colorkey & 0x00f800) >> 6) | |
452 | | ((state->colorkey & 0x0000f8) >> 3); | |
cb2025d2 | 453 | rcar_du_plane_write(rgrp, index, PnTC2R, colorkey); |
4bf8e196 LP |
454 | break; |
455 | ||
456 | case DRM_FORMAT_XRGB8888: | |
457 | case DRM_FORMAT_ARGB8888: | |
cb2025d2 | 458 | rcar_du_plane_write(rgrp, index, PnTC3R, |
4407cc02 | 459 | PnTC3R_CODE | (state->colorkey & 0xffffff)); |
4bf8e196 LP |
460 | break; |
461 | } | |
462 | } | |
463 | ||
2427b303 LP |
464 | static void rcar_du_plane_setup_format_gen2(struct rcar_du_group *rgrp, |
465 | unsigned int index, | |
466 | const struct rcar_du_plane_state *state) | |
4bf8e196 | 467 | { |
4bf8e196 LP |
468 | u32 ddcr2 = PnDDCR2_CODE; |
469 | u32 ddcr4; | |
4bf8e196 | 470 | |
f3bafc12 LP |
471 | /* |
472 | * Data format | |
4bf8e196 LP |
473 | * |
474 | * The data format is selected by the DDDF field in PnMR and the EDF | |
475 | * field in DDCR4. | |
476 | */ | |
4bf8e196 | 477 | |
34a04f2b | 478 | rcar_du_plane_setup_mode(rgrp, index, state); |
4bf8e196 | 479 | |
5bfcbce0 | 480 | if (state->format->planes == 2) { |
5ee5a81d | 481 | if (state->hwindex != index) { |
5bfcbce0 LP |
482 | if (state->format->fourcc == DRM_FORMAT_NV12 || |
483 | state->format->fourcc == DRM_FORMAT_NV21) | |
4bf8e196 LP |
484 | ddcr2 |= PnDDCR2_Y420; |
485 | ||
5bfcbce0 | 486 | if (state->format->fourcc == DRM_FORMAT_NV21) |
4bf8e196 LP |
487 | ddcr2 |= PnDDCR2_NV21; |
488 | ||
489 | ddcr2 |= PnDDCR2_DIVU; | |
490 | } else { | |
491 | ddcr2 |= PnDDCR2_DIVY; | |
492 | } | |
493 | } | |
494 | ||
cb2025d2 | 495 | rcar_du_plane_write(rgrp, index, PnDDCR2, ddcr2); |
ff967363 LP |
496 | |
497 | ddcr4 = state->format->edf | PnDDCR4_CODE; | |
34a04f2b LP |
498 | if (state->source != RCAR_DU_PLANE_MEMORY) |
499 | ddcr4 |= PnDDCR4_VSPS; | |
ff967363 | 500 | |
cb2025d2 | 501 | rcar_du_plane_write(rgrp, index, PnDDCR4, ddcr4); |
2427b303 LP |
502 | } |
503 | ||
504 | static void rcar_du_plane_setup_format_gen3(struct rcar_du_group *rgrp, | |
505 | unsigned int index, | |
506 | const struct rcar_du_plane_state *state) | |
507 | { | |
508 | rcar_du_plane_write(rgrp, index, PnMR, | |
509 | PnMR_SPIM_TP_OFF | state->format->pnmr); | |
510 | ||
511 | rcar_du_plane_write(rgrp, index, PnDDCR4, | |
512 | state->format->edf | PnDDCR4_CODE); | |
513 | } | |
514 | ||
515 | static void rcar_du_plane_setup_format(struct rcar_du_group *rgrp, | |
516 | unsigned int index, | |
517 | const struct rcar_du_plane_state *state) | |
518 | { | |
519 | struct rcar_du_device *rcdu = rgrp->dev; | |
401712e0 | 520 | const struct drm_rect *dst = &state->state.dst; |
2427b303 LP |
521 | |
522 | if (rcdu->info->gen < 3) | |
523 | rcar_du_plane_setup_format_gen2(rgrp, index, state); | |
524 | else | |
525 | rcar_du_plane_setup_format_gen3(rgrp, index, state); | |
4bf8e196 | 526 | |
4bf8e196 | 527 | /* Destination position and size */ |
401712e0 LP |
528 | rcar_du_plane_write(rgrp, index, PnDSXR, drm_rect_width(dst)); |
529 | rcar_du_plane_write(rgrp, index, PnDSYR, drm_rect_height(dst)); | |
530 | rcar_du_plane_write(rgrp, index, PnDPXR, dst->x1); | |
531 | rcar_du_plane_write(rgrp, index, PnDPYR, dst->y1); | |
4bf8e196 | 532 | |
2427b303 LP |
533 | if (rcdu->info->gen < 3) { |
534 | /* Wrap-around and blinking, disabled */ | |
535 | rcar_du_plane_write(rgrp, index, PnWASPR, 0); | |
536 | rcar_du_plane_write(rgrp, index, PnWAMWR, 4095); | |
537 | rcar_du_plane_write(rgrp, index, PnBTR, 0); | |
538 | rcar_du_plane_write(rgrp, index, PnMLR, 0); | |
539 | } | |
4bf8e196 LP |
540 | } |
541 | ||
6d62ef3a LP |
542 | void __rcar_du_plane_setup(struct rcar_du_group *rgrp, |
543 | const struct rcar_du_plane_state *state) | |
4bf8e196 | 544 | { |
2427b303 LP |
545 | struct rcar_du_device *rcdu = rgrp->dev; |
546 | ||
34a04f2b | 547 | rcar_du_plane_setup_format(rgrp, state->hwindex, state); |
5bfcbce0 | 548 | if (state->format->planes == 2) |
34a04f2b LP |
549 | rcar_du_plane_setup_format(rgrp, (state->hwindex + 1) % 8, |
550 | state); | |
551 | ||
2427b303 LP |
552 | if (rcdu->info->gen < 3) |
553 | rcar_du_plane_setup_scanout(rgrp, state); | |
4bf8e196 | 554 | |
34a04f2b LP |
555 | if (state->source == RCAR_DU_PLANE_VSPD1) { |
556 | unsigned int vspd1_sink = rgrp->index ? 2 : 0; | |
34a04f2b LP |
557 | |
558 | if (rcdu->vspd1_sink != vspd1_sink) { | |
559 | rcdu->vspd1_sink = vspd1_sink; | |
560 | rcar_du_set_dpad0_vsp1_routing(rcdu); | |
561 | } | |
562 | } | |
4bf8e196 LP |
563 | } |
564 | ||
9c893a61 LP |
565 | int __rcar_du_plane_atomic_check(struct drm_plane *plane, |
566 | struct drm_plane_state *state, | |
567 | const struct rcar_du_format_info **format) | |
4bf8e196 | 568 | { |
9c893a61 | 569 | struct drm_device *dev = plane->dev; |
401712e0 | 570 | struct drm_crtc_state *crtc_state; |
401712e0 | 571 | int ret; |
4bf8e196 | 572 | |
401712e0 LP |
573 | if (!state->crtc) { |
574 | /* | |
575 | * The visible field is not reset by the DRM core but only | |
576 | * updated by drm_plane_helper_check_state(), set it manually. | |
577 | */ | |
578 | state->visible = false; | |
9c893a61 | 579 | *format = NULL; |
920888a2 | 580 | return 0; |
5ee5a81d | 581 | } |
917de180 | 582 | |
401712e0 LP |
583 | crtc_state = drm_atomic_get_crtc_state(state->state, state->crtc); |
584 | if (IS_ERR(crtc_state)) | |
585 | return PTR_ERR(crtc_state); | |
586 | ||
81af63a4 | 587 | ret = drm_atomic_helper_check_plane_state(state, crtc_state, |
401712e0 LP |
588 | DRM_PLANE_HELPER_NO_SCALING, |
589 | DRM_PLANE_HELPER_NO_SCALING, | |
590 | true, true); | |
591 | if (ret < 0) | |
592 | return ret; | |
593 | ||
594 | if (!state->visible) { | |
595 | *format = NULL; | |
596 | return 0; | |
4bf8e196 LP |
597 | } |
598 | ||
9c893a61 LP |
599 | *format = rcar_du_format_info(state->fb->format->format); |
600 | if (*format == NULL) { | |
601 | dev_dbg(dev->dev, "%s: unsupported format %08x\n", __func__, | |
438b74a5 | 602 | state->fb->format->format); |
4bf8e196 LP |
603 | return -EINVAL; |
604 | } | |
605 | ||
4bf8e196 LP |
606 | return 0; |
607 | } | |
608 | ||
9c893a61 LP |
609 | static int rcar_du_plane_atomic_check(struct drm_plane *plane, |
610 | struct drm_plane_state *state) | |
611 | { | |
612 | struct rcar_du_plane_state *rstate = to_rcar_plane_state(state); | |
613 | ||
614 | return __rcar_du_plane_atomic_check(plane, state, &rstate->format); | |
615 | } | |
616 | ||
920888a2 LP |
617 | static void rcar_du_plane_atomic_update(struct drm_plane *plane, |
618 | struct drm_plane_state *old_state) | |
619 | { | |
620 | struct rcar_du_plane *rplane = to_rcar_plane(plane); | |
2af03944 LP |
621 | struct rcar_du_plane_state *old_rstate; |
622 | struct rcar_du_plane_state *new_rstate; | |
5bfcbce0 | 623 | |
401712e0 | 624 | if (!plane->state->visible) |
2af03944 LP |
625 | return; |
626 | ||
627 | rcar_du_plane_setup(rplane); | |
628 | ||
f3bafc12 LP |
629 | /* |
630 | * Check whether the source has changed from memory to live source or | |
2af03944 LP |
631 | * from live source to memory. The source has been configured by the |
632 | * VSPS bit in the PnDDCR4 register. Although the datasheet states that | |
633 | * the bit is updated during vertical blanking, it seems that updates | |
634 | * only occur when the DU group is held in reset through the DSYSR.DRES | |
635 | * bit. We thus need to restart the group if the source changes. | |
636 | */ | |
637 | old_rstate = to_rcar_plane_state(old_state); | |
638 | new_rstate = to_rcar_plane_state(plane->state); | |
639 | ||
640 | if ((old_rstate->source == RCAR_DU_PLANE_MEMORY) != | |
641 | (new_rstate->source == RCAR_DU_PLANE_MEMORY)) | |
642 | rplane->group->need_restart = true; | |
4bf8e196 LP |
643 | } |
644 | ||
920888a2 LP |
645 | static const struct drm_plane_helper_funcs rcar_du_plane_helper_funcs = { |
646 | .atomic_check = rcar_du_plane_atomic_check, | |
647 | .atomic_update = rcar_du_plane_atomic_update, | |
648 | }; | |
649 | ||
4407cc02 LP |
650 | static struct drm_plane_state * |
651 | rcar_du_plane_atomic_duplicate_state(struct drm_plane *plane) | |
4bf8e196 | 652 | { |
4407cc02 LP |
653 | struct rcar_du_plane_state *state; |
654 | struct rcar_du_plane_state *copy; | |
4bf8e196 | 655 | |
263b39fe LP |
656 | if (WARN_ON(!plane->state)) |
657 | return NULL; | |
658 | ||
ec69a406 | 659 | state = to_rcar_plane_state(plane->state); |
4407cc02 LP |
660 | copy = kmemdup(state, sizeof(*state), GFP_KERNEL); |
661 | if (copy == NULL) | |
662 | return NULL; | |
663 | ||
263b39fe | 664 | __drm_atomic_helper_plane_duplicate_state(plane, ©->state); |
4bf8e196 | 665 | |
4407cc02 | 666 | return ©->state; |
4bf8e196 LP |
667 | } |
668 | ||
4407cc02 LP |
669 | static void rcar_du_plane_atomic_destroy_state(struct drm_plane *plane, |
670 | struct drm_plane_state *state) | |
4bf8e196 | 671 | { |
2f701695 | 672 | __drm_atomic_helper_plane_destroy_state(state); |
ec69a406 | 673 | kfree(to_rcar_plane_state(state)); |
4407cc02 | 674 | } |
4bf8e196 | 675 | |
a32a3c80 LP |
676 | static void rcar_du_plane_reset(struct drm_plane *plane) |
677 | { | |
678 | struct rcar_du_plane_state *state; | |
679 | ||
680 | if (plane->state) { | |
681 | rcar_du_plane_atomic_destroy_state(plane, plane->state); | |
682 | plane->state = NULL; | |
683 | } | |
684 | ||
685 | state = kzalloc(sizeof(*state), GFP_KERNEL); | |
686 | if (state == NULL) | |
687 | return; | |
688 | ||
161ad653 AG |
689 | __drm_atomic_helper_plane_reset(plane, &state->state); |
690 | ||
a32a3c80 | 691 | state->hwindex = -1; |
af8ad962 | 692 | state->source = RCAR_DU_PLANE_MEMORY; |
a32a3c80 | 693 | state->colorkey = RCAR_DU_COLORKEY_NONE; |
2fc4d838 | 694 | state->state.zpos = plane->type == DRM_PLANE_TYPE_PRIMARY ? 0 : 1; |
a32a3c80 LP |
695 | } |
696 | ||
4407cc02 LP |
697 | static int rcar_du_plane_atomic_set_property(struct drm_plane *plane, |
698 | struct drm_plane_state *state, | |
699 | struct drm_property *property, | |
700 | uint64_t val) | |
701 | { | |
ec69a406 | 702 | struct rcar_du_plane_state *rstate = to_rcar_plane_state(state); |
9f6aee95 | 703 | struct rcar_du_device *rcdu = to_rcar_plane(plane)->group->dev; |
4bf8e196 | 704 | |
301a9b8d | 705 | if (property == rcdu->props.colorkey) |
4407cc02 | 706 | rstate->colorkey = val; |
4407cc02 LP |
707 | else |
708 | return -EINVAL; | |
4bf8e196 | 709 | |
4407cc02 | 710 | return 0; |
4bf8e196 LP |
711 | } |
712 | ||
4407cc02 LP |
713 | static int rcar_du_plane_atomic_get_property(struct drm_plane *plane, |
714 | const struct drm_plane_state *state, struct drm_property *property, | |
715 | uint64_t *val) | |
4bf8e196 | 716 | { |
4407cc02 LP |
717 | const struct rcar_du_plane_state *rstate = |
718 | container_of(state, const struct rcar_du_plane_state, state); | |
9f6aee95 | 719 | struct rcar_du_device *rcdu = to_rcar_plane(plane)->group->dev; |
4bf8e196 | 720 | |
301a9b8d | 721 | if (property == rcdu->props.colorkey) |
4407cc02 | 722 | *val = rstate->colorkey; |
4bf8e196 LP |
723 | else |
724 | return -EINVAL; | |
725 | ||
726 | return 0; | |
727 | } | |
728 | ||
729 | static const struct drm_plane_funcs rcar_du_plane_funcs = { | |
336d04a1 LP |
730 | .update_plane = drm_atomic_helper_update_plane, |
731 | .disable_plane = drm_atomic_helper_disable_plane, | |
4407cc02 | 732 | .reset = rcar_du_plane_reset, |
4bf8e196 | 733 | .destroy = drm_plane_cleanup, |
4407cc02 LP |
734 | .atomic_duplicate_state = rcar_du_plane_atomic_duplicate_state, |
735 | .atomic_destroy_state = rcar_du_plane_atomic_destroy_state, | |
736 | .atomic_set_property = rcar_du_plane_atomic_set_property, | |
737 | .atomic_get_property = rcar_du_plane_atomic_get_property, | |
4bf8e196 LP |
738 | }; |
739 | ||
740 | static const uint32_t formats[] = { | |
741 | DRM_FORMAT_RGB565, | |
742 | DRM_FORMAT_ARGB1555, | |
743 | DRM_FORMAT_XRGB1555, | |
744 | DRM_FORMAT_XRGB8888, | |
745 | DRM_FORMAT_ARGB8888, | |
746 | DRM_FORMAT_UYVY, | |
747 | DRM_FORMAT_YUYV, | |
748 | DRM_FORMAT_NV12, | |
749 | DRM_FORMAT_NV21, | |
750 | DRM_FORMAT_NV16, | |
751 | }; | |
752 | ||
cb2025d2 | 753 | int rcar_du_planes_init(struct rcar_du_group *rgrp) |
4bf8e196 | 754 | { |
cb2025d2 | 755 | struct rcar_du_device *rcdu = rgrp->dev; |
917de180 | 756 | unsigned int crtcs; |
4bf8e196 | 757 | unsigned int i; |
917de180 | 758 | int ret; |
4bf8e196 | 759 | |
f3bafc12 LP |
760 | /* |
761 | * Create one primary plane per CRTC in this group and seven overlay | |
917de180 LP |
762 | * planes. |
763 | */ | |
d6aed574 | 764 | rgrp->num_planes = rgrp->num_crtcs + 7; |
917de180 LP |
765 | |
766 | crtcs = ((1 << rcdu->num_crtcs) - 1) & (3 << (2 * rgrp->index)); | |
767 | ||
d6aed574 | 768 | for (i = 0; i < rgrp->num_planes; ++i) { |
fe6fbe9a | 769 | enum drm_plane_type type = i < rgrp->num_crtcs |
917de180 LP |
770 | ? DRM_PLANE_TYPE_PRIMARY |
771 | : DRM_PLANE_TYPE_OVERLAY; | |
99caede1 | 772 | struct rcar_du_plane *plane = &rgrp->planes[i]; |
4bf8e196 | 773 | |
cb2025d2 | 774 | plane->group = rgrp; |
4bf8e196 | 775 | |
917de180 LP |
776 | ret = drm_universal_plane_init(rcdu->ddev, &plane->plane, crtcs, |
777 | &rcar_du_plane_funcs, formats, | |
e6fc3b68 BW |
778 | ARRAY_SIZE(formats), |
779 | NULL, type, NULL); | |
4bf8e196 LP |
780 | if (ret < 0) |
781 | return ret; | |
782 | ||
920888a2 LP |
783 | drm_plane_helper_add(&plane->plane, |
784 | &rcar_du_plane_helper_funcs); | |
785 | ||
bf17cda6 KB |
786 | drm_plane_create_alpha_property(&plane->plane); |
787 | ||
7982471d TE |
788 | if (type == DRM_PLANE_TYPE_PRIMARY) { |
789 | drm_plane_create_zpos_immutable_property(&plane->plane, | |
790 | 0); | |
791 | } else { | |
792 | drm_object_attach_property(&plane->plane.base, | |
793 | rcdu->props.colorkey, | |
794 | RCAR_DU_COLORKEY_NONE); | |
795 | drm_plane_create_zpos_property(&plane->plane, 1, 1, 7); | |
796 | } | |
4bf8e196 LP |
797 | } |
798 | ||
799 | return 0; | |
800 | } |