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