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; | |
143 | ||
144 | /* Check if hardware planes need to be reallocated. */ | |
145 | for (i = 0; i < dev->mode_config.num_total_plane; ++i) { | |
146 | struct rcar_du_plane_state *plane_state; | |
147 | struct rcar_du_plane *plane; | |
148 | unsigned int index; | |
149 | ||
150 | if (!state->planes[i]) | |
151 | continue; | |
152 | ||
153 | plane = to_rcar_plane(state->planes[i]); | |
154 | plane_state = to_rcar_plane_state(state->plane_states[i]); | |
155 | ||
07ece19b | 156 | dev_dbg(rcdu->dev, "%s: checking plane (%u,%tu)\n", __func__, |
ab334e13 LP |
157 | plane->group->index, plane - plane->group->planes); |
158 | ||
159 | /* If the plane is being disabled we don't need to go through | |
160 | * the full reallocation procedure. Just mark the hardware | |
161 | * plane(s) as freed. | |
162 | */ | |
163 | if (!plane_state->format) { | |
164 | dev_dbg(rcdu->dev, "%s: plane is being disabled\n", | |
165 | __func__); | |
166 | index = plane - plane->group->planes; | |
167 | group_freed_planes[plane->group->index] |= 1 << index; | |
168 | plane_state->hwindex = -1; | |
169 | continue; | |
170 | } | |
171 | ||
172 | /* If the plane needs to be reallocated mark it as such, and | |
173 | * mark the hardware plane(s) as free. | |
174 | */ | |
175 | if (rcar_du_plane_needs_realloc(plane, plane_state)) { | |
176 | dev_dbg(rcdu->dev, "%s: plane needs reallocation\n", | |
177 | __func__); | |
178 | groups |= 1 << plane->group->index; | |
179 | needs_realloc = true; | |
180 | ||
181 | index = plane - plane->group->planes; | |
182 | group_freed_planes[plane->group->index] |= 1 << index; | |
183 | plane_state->hwindex = -1; | |
184 | } | |
185 | } | |
186 | ||
187 | if (!needs_realloc) | |
188 | return 0; | |
189 | ||
190 | /* Grab all plane states for the groups that need reallocation to ensure | |
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]; | |
208 | struct rcar_du_plane_state *plane_state; | |
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 | ||
215 | /* If the plane has been freed in the above loop its | |
216 | * hardware planes must not be added to the used planes | |
217 | * bitmask. However, the current state doesn't reflect | |
218 | * the free state yet, as we've modified the new state | |
219 | * above. Use the local freed planes list to check for | |
220 | * that condition instead. | |
221 | */ | |
222 | if (group_freed_planes[index] & (1 << i)) { | |
223 | dev_dbg(rcdu->dev, | |
07ece19b | 224 | "%s: plane (%u,%tu) has been freed, skipping\n", |
ab334e13 LP |
225 | __func__, plane->group->index, |
226 | plane - plane->group->planes); | |
227 | continue; | |
228 | } | |
229 | ||
230 | plane_state = to_rcar_plane_state(plane->plane.state); | |
231 | used_planes |= rcar_du_plane_hwmask(plane_state); | |
232 | ||
233 | dev_dbg(rcdu->dev, | |
07ece19b | 234 | "%s: plane (%u,%tu) uses %u hwplanes (index %d)\n", |
ab334e13 LP |
235 | __func__, plane->group->index, |
236 | plane - plane->group->planes, | |
237 | plane_state->format ? | |
238 | plane_state->format->planes : 0, | |
239 | plane_state->hwindex); | |
240 | } | |
241 | ||
242 | group_free_planes[index] = 0xff & ~used_planes; | |
243 | groups &= ~(1 << index); | |
244 | ||
245 | dev_dbg(rcdu->dev, "%s: group %u free planes mask 0x%02x\n", | |
246 | __func__, index, group_free_planes[index]); | |
247 | } | |
248 | ||
249 | /* Reallocate hardware planes for each plane that needs it. */ | |
250 | for (i = 0; i < dev->mode_config.num_total_plane; ++i) { | |
251 | struct rcar_du_plane_state *plane_state; | |
252 | struct rcar_du_plane *plane; | |
253 | unsigned int crtc_planes; | |
254 | unsigned int free; | |
255 | int idx; | |
256 | ||
257 | if (!state->planes[i]) | |
258 | continue; | |
259 | ||
260 | plane = to_rcar_plane(state->planes[i]); | |
261 | plane_state = to_rcar_plane_state(state->plane_states[i]); | |
262 | ||
07ece19b | 263 | dev_dbg(rcdu->dev, "%s: allocating plane (%u,%tu)\n", __func__, |
ab334e13 LP |
264 | plane->group->index, plane - plane->group->planes); |
265 | ||
266 | /* Skip planes that are being disabled or don't need to be | |
267 | * reallocated. | |
268 | */ | |
269 | if (!plane_state->format || | |
270 | !rcar_du_plane_needs_realloc(plane, plane_state)) | |
271 | continue; | |
272 | ||
273 | /* Try to allocate the plane from the free planes currently | |
274 | * associated with the target CRTC to avoid restarting the CRTC | |
275 | * group and thus minimize flicker. If it fails fall back to | |
276 | * allocating from all free planes. | |
277 | */ | |
278 | crtc_planes = to_rcar_crtc(plane_state->state.crtc)->index % 2 | |
279 | ? plane->group->dptsr_planes | |
280 | : ~plane->group->dptsr_planes; | |
281 | free = group_free_planes[plane->group->index]; | |
282 | ||
283 | idx = rcar_du_plane_hwalloc(plane, plane_state, | |
284 | free & crtc_planes); | |
285 | if (idx < 0) | |
286 | idx = rcar_du_plane_hwalloc(plane, plane_state, | |
287 | free); | |
288 | if (idx < 0) { | |
289 | dev_dbg(rcdu->dev, "%s: no available hardware plane\n", | |
290 | __func__); | |
291 | return idx; | |
292 | } | |
293 | ||
294 | dev_dbg(rcdu->dev, "%s: allocated %u hwplanes (index %u)\n", | |
295 | __func__, plane_state->format->planes, idx); | |
296 | ||
297 | plane_state->hwindex = idx; | |
298 | ||
299 | group_free_planes[plane->group->index] &= | |
300 | ~rcar_du_plane_hwmask(plane_state); | |
301 | ||
302 | dev_dbg(rcdu->dev, "%s: group %u free planes mask 0x%02x\n", | |
303 | __func__, plane->group->index, | |
304 | group_free_planes[plane->group->index]); | |
305 | } | |
306 | ||
307 | return 0; | |
308 | } | |
309 | ||
310 | /* ----------------------------------------------------------------------------- | |
311 | * Plane Setup | |
312 | */ | |
313 | ||
4bf8e196 LP |
314 | #define RCAR_DU_COLORKEY_NONE (0 << 24) |
315 | #define RCAR_DU_COLORKEY_SOURCE (1 << 24) | |
316 | #define RCAR_DU_COLORKEY_MASK (1 << 24) | |
317 | ||
cb2025d2 | 318 | static void rcar_du_plane_write(struct rcar_du_group *rgrp, |
4bf8e196 LP |
319 | unsigned int index, u32 reg, u32 data) |
320 | { | |
cb2025d2 LP |
321 | rcar_du_write(rgrp->dev, rgrp->mmio_offset + index * PLANE_OFF + reg, |
322 | data); | |
4bf8e196 LP |
323 | } |
324 | ||
34a04f2b LP |
325 | static void rcar_du_plane_setup_scanout(struct rcar_du_group *rgrp, |
326 | const struct rcar_du_plane_state *state) | |
4bf8e196 | 327 | { |
5bfcbce0 LP |
328 | unsigned int src_x = state->state.src_x >> 16; |
329 | unsigned int src_y = state->state.src_y >> 16; | |
5ee5a81d | 330 | unsigned int index = state->hwindex; |
2f13c529 | 331 | unsigned int pitch; |
906eff7f | 332 | bool interlaced; |
2f13c529 | 333 | u32 dma[2]; |
eb86301f | 334 | |
5bfcbce0 | 335 | interlaced = state->state.crtc->state->adjusted_mode.flags |
47094194 | 336 | & DRM_MODE_FLAG_INTERLACE; |
906eff7f | 337 | |
34a04f2b LP |
338 | if (state->source == RCAR_DU_PLANE_MEMORY) { |
339 | struct drm_framebuffer *fb = state->state.fb; | |
340 | struct drm_gem_cma_object *gem; | |
341 | unsigned int i; | |
342 | ||
343 | if (state->format->planes == 2) | |
344 | pitch = fb->pitches[0]; | |
345 | else | |
346 | pitch = fb->pitches[0] * 8 / state->format->bpp; | |
eb86301f | 347 | |
34a04f2b LP |
348 | for (i = 0; i < state->format->planes; ++i) { |
349 | gem = drm_fb_cma_get_gem_obj(fb, i); | |
350 | dma[i] = gem->paddr + fb->offsets[i]; | |
351 | } | |
352 | } else { | |
353 | pitch = state->state.src_w >> 16; | |
354 | dma[0] = 0; | |
355 | dma[1] = 0; | |
2f13c529 | 356 | } |
906eff7f | 357 | |
34a04f2b LP |
358 | /* Memory pitch (expressed in pixels). Must be doubled for interlaced |
359 | * operation with 32bpp formats. | |
360 | */ | |
2f13c529 LP |
361 | rcar_du_plane_write(rgrp, index, PnMWR, |
362 | (interlaced && state->format->bpp == 32) ? | |
363 | pitch * 2 : pitch); | |
4bf8e196 | 364 | |
9e7db06d LP |
365 | /* The Y position is expressed in raster line units and must be doubled |
366 | * for 32bpp formats, according to the R8A7790 datasheet. No mention of | |
367 | * doubling the Y position is found in the R8A7779 datasheet, but the | |
368 | * rule seems to apply there as well. | |
369 | * | |
906eff7f LP |
370 | * Despite not being documented, doubling seem not to be needed when |
371 | * operating in interlaced mode. | |
372 | * | |
9e7db06d | 373 | * Similarly, for the second plane, NV12 and NV21 formats seem to |
906eff7f LP |
374 | * require a halved Y position value, in both progressive and interlaced |
375 | * modes. | |
4bf8e196 | 376 | */ |
287bdf03 LP |
377 | rcar_du_plane_write(rgrp, index, PnSPXR, src_x); |
378 | rcar_du_plane_write(rgrp, index, PnSPYR, src_y * | |
5bfcbce0 | 379 | (!interlaced && state->format->bpp == 32 ? 2 : 1)); |
f398f344 | 380 | |
2f13c529 | 381 | rcar_du_plane_write(rgrp, index, PnDSA0R, dma[0]); |
4bf8e196 | 382 | |
5bfcbce0 | 383 | if (state->format->planes == 2) { |
4bf8e196 LP |
384 | index = (index + 1) % 8; |
385 | ||
2f13c529 | 386 | rcar_du_plane_write(rgrp, index, PnMWR, pitch); |
49785e25 | 387 | |
287bdf03 LP |
388 | rcar_du_plane_write(rgrp, index, PnSPXR, src_x); |
389 | rcar_du_plane_write(rgrp, index, PnSPYR, src_y * | |
5bfcbce0 | 390 | (state->format->bpp == 16 ? 2 : 1) / 2); |
eb86301f | 391 | |
2f13c529 | 392 | rcar_du_plane_write(rgrp, index, PnDSA0R, dma[1]); |
4bf8e196 LP |
393 | } |
394 | } | |
395 | ||
34a04f2b LP |
396 | static void rcar_du_plane_setup_mode(struct rcar_du_group *rgrp, |
397 | unsigned int index, | |
398 | const struct rcar_du_plane_state *state) | |
4bf8e196 | 399 | { |
4bf8e196 LP |
400 | u32 colorkey; |
401 | u32 pnmr; | |
402 | ||
403 | /* The PnALPHAR register controls alpha-blending in 16bpp formats | |
404 | * (ARGB1555 and XRGB1555). | |
405 | * | |
406 | * For ARGB, set the alpha value to 0, and enable alpha-blending when | |
407 | * the A bit is 0. This maps A=0 to alpha=0 and A=1 to alpha=255. | |
408 | * | |
409 | * For XRGB, set the alpha value to the plane-wide alpha value and | |
410 | * enable alpha-blending regardless of the X bit value. | |
411 | */ | |
5bfcbce0 | 412 | if (state->format->fourcc != DRM_FORMAT_XRGB1555) |
cb2025d2 | 413 | rcar_du_plane_write(rgrp, index, PnALPHAR, PnALPHAR_ABIT_0); |
4bf8e196 | 414 | else |
cb2025d2 | 415 | rcar_du_plane_write(rgrp, index, PnALPHAR, |
4407cc02 | 416 | PnALPHAR_ABIT_X | state->alpha); |
4bf8e196 | 417 | |
5bfcbce0 | 418 | pnmr = PnMR_BM_MD | state->format->pnmr; |
4bf8e196 LP |
419 | |
420 | /* Disable color keying when requested. YUV formats have the | |
421 | * PnMR_SPIM_TP_OFF bit set in their pnmr field, disabling color keying | |
422 | * automatically. | |
423 | */ | |
4407cc02 | 424 | if ((state->colorkey & RCAR_DU_COLORKEY_MASK) == RCAR_DU_COLORKEY_NONE) |
4bf8e196 LP |
425 | pnmr |= PnMR_SPIM_TP_OFF; |
426 | ||
427 | /* For packed YUV formats we need to select the U/V order. */ | |
5bfcbce0 | 428 | if (state->format->fourcc == DRM_FORMAT_YUYV) |
4bf8e196 LP |
429 | pnmr |= PnMR_YCDF_YUYV; |
430 | ||
cb2025d2 | 431 | rcar_du_plane_write(rgrp, index, PnMR, pnmr); |
4bf8e196 | 432 | |
5bfcbce0 | 433 | switch (state->format->fourcc) { |
4bf8e196 | 434 | case DRM_FORMAT_RGB565: |
4407cc02 LP |
435 | colorkey = ((state->colorkey & 0xf80000) >> 8) |
436 | | ((state->colorkey & 0x00fc00) >> 5) | |
437 | | ((state->colorkey & 0x0000f8) >> 3); | |
cb2025d2 | 438 | rcar_du_plane_write(rgrp, index, PnTC2R, colorkey); |
4bf8e196 LP |
439 | break; |
440 | ||
441 | case DRM_FORMAT_ARGB1555: | |
442 | case DRM_FORMAT_XRGB1555: | |
4407cc02 LP |
443 | colorkey = ((state->colorkey & 0xf80000) >> 9) |
444 | | ((state->colorkey & 0x00f800) >> 6) | |
445 | | ((state->colorkey & 0x0000f8) >> 3); | |
cb2025d2 | 446 | rcar_du_plane_write(rgrp, index, PnTC2R, colorkey); |
4bf8e196 LP |
447 | break; |
448 | ||
449 | case DRM_FORMAT_XRGB8888: | |
450 | case DRM_FORMAT_ARGB8888: | |
cb2025d2 | 451 | rcar_du_plane_write(rgrp, index, PnTC3R, |
4407cc02 | 452 | PnTC3R_CODE | (state->colorkey & 0xffffff)); |
4bf8e196 LP |
453 | break; |
454 | } | |
455 | } | |
456 | ||
2427b303 LP |
457 | static void rcar_du_plane_setup_format_gen2(struct rcar_du_group *rgrp, |
458 | unsigned int index, | |
459 | const struct rcar_du_plane_state *state) | |
4bf8e196 | 460 | { |
4bf8e196 LP |
461 | u32 ddcr2 = PnDDCR2_CODE; |
462 | u32 ddcr4; | |
4bf8e196 LP |
463 | |
464 | /* Data format | |
465 | * | |
466 | * The data format is selected by the DDDF field in PnMR and the EDF | |
467 | * field in DDCR4. | |
468 | */ | |
4bf8e196 | 469 | |
34a04f2b | 470 | rcar_du_plane_setup_mode(rgrp, index, state); |
4bf8e196 | 471 | |
5bfcbce0 | 472 | if (state->format->planes == 2) { |
5ee5a81d | 473 | if (state->hwindex != index) { |
5bfcbce0 LP |
474 | if (state->format->fourcc == DRM_FORMAT_NV12 || |
475 | state->format->fourcc == DRM_FORMAT_NV21) | |
4bf8e196 LP |
476 | ddcr2 |= PnDDCR2_Y420; |
477 | ||
5bfcbce0 | 478 | if (state->format->fourcc == DRM_FORMAT_NV21) |
4bf8e196 LP |
479 | ddcr2 |= PnDDCR2_NV21; |
480 | ||
481 | ddcr2 |= PnDDCR2_DIVU; | |
482 | } else { | |
483 | ddcr2 |= PnDDCR2_DIVY; | |
484 | } | |
485 | } | |
486 | ||
cb2025d2 | 487 | rcar_du_plane_write(rgrp, index, PnDDCR2, ddcr2); |
ff967363 LP |
488 | |
489 | ddcr4 = state->format->edf | PnDDCR4_CODE; | |
34a04f2b LP |
490 | if (state->source != RCAR_DU_PLANE_MEMORY) |
491 | ddcr4 |= PnDDCR4_VSPS; | |
ff967363 | 492 | |
cb2025d2 | 493 | rcar_du_plane_write(rgrp, index, PnDDCR4, ddcr4); |
2427b303 LP |
494 | } |
495 | ||
496 | static void rcar_du_plane_setup_format_gen3(struct rcar_du_group *rgrp, | |
497 | unsigned int index, | |
498 | const struct rcar_du_plane_state *state) | |
499 | { | |
500 | rcar_du_plane_write(rgrp, index, PnMR, | |
501 | PnMR_SPIM_TP_OFF | state->format->pnmr); | |
502 | ||
503 | rcar_du_plane_write(rgrp, index, PnDDCR4, | |
504 | state->format->edf | PnDDCR4_CODE); | |
505 | } | |
506 | ||
507 | static void rcar_du_plane_setup_format(struct rcar_du_group *rgrp, | |
508 | unsigned int index, | |
509 | const struct rcar_du_plane_state *state) | |
510 | { | |
511 | struct rcar_du_device *rcdu = rgrp->dev; | |
512 | ||
513 | if (rcdu->info->gen < 3) | |
514 | rcar_du_plane_setup_format_gen2(rgrp, index, state); | |
515 | else | |
516 | rcar_du_plane_setup_format_gen3(rgrp, index, state); | |
4bf8e196 | 517 | |
4bf8e196 | 518 | /* Destination position and size */ |
34a04f2b LP |
519 | rcar_du_plane_write(rgrp, index, PnDSXR, state->state.crtc_w); |
520 | rcar_du_plane_write(rgrp, index, PnDSYR, state->state.crtc_h); | |
521 | rcar_du_plane_write(rgrp, index, PnDPXR, state->state.crtc_x); | |
522 | rcar_du_plane_write(rgrp, index, PnDPYR, state->state.crtc_y); | |
4bf8e196 | 523 | |
2427b303 LP |
524 | if (rcdu->info->gen < 3) { |
525 | /* Wrap-around and blinking, disabled */ | |
526 | rcar_du_plane_write(rgrp, index, PnWASPR, 0); | |
527 | rcar_du_plane_write(rgrp, index, PnWAMWR, 4095); | |
528 | rcar_du_plane_write(rgrp, index, PnBTR, 0); | |
529 | rcar_du_plane_write(rgrp, index, PnMLR, 0); | |
530 | } | |
4bf8e196 LP |
531 | } |
532 | ||
6d62ef3a LP |
533 | void __rcar_du_plane_setup(struct rcar_du_group *rgrp, |
534 | const struct rcar_du_plane_state *state) | |
4bf8e196 | 535 | { |
2427b303 LP |
536 | struct rcar_du_device *rcdu = rgrp->dev; |
537 | ||
34a04f2b | 538 | rcar_du_plane_setup_format(rgrp, state->hwindex, state); |
5bfcbce0 | 539 | if (state->format->planes == 2) |
34a04f2b LP |
540 | rcar_du_plane_setup_format(rgrp, (state->hwindex + 1) % 8, |
541 | state); | |
542 | ||
2427b303 LP |
543 | if (rcdu->info->gen < 3) |
544 | rcar_du_plane_setup_scanout(rgrp, state); | |
4bf8e196 | 545 | |
34a04f2b LP |
546 | if (state->source == RCAR_DU_PLANE_VSPD1) { |
547 | unsigned int vspd1_sink = rgrp->index ? 2 : 0; | |
34a04f2b LP |
548 | |
549 | if (rcdu->vspd1_sink != vspd1_sink) { | |
550 | rcdu->vspd1_sink = vspd1_sink; | |
551 | rcar_du_set_dpad0_vsp1_routing(rcdu); | |
552 | } | |
553 | } | |
4bf8e196 LP |
554 | } |
555 | ||
920888a2 LP |
556 | static int rcar_du_plane_atomic_check(struct drm_plane *plane, |
557 | struct drm_plane_state *state) | |
4bf8e196 | 558 | { |
ec69a406 | 559 | struct rcar_du_plane_state *rstate = to_rcar_plane_state(state); |
4bf8e196 | 560 | struct rcar_du_plane *rplane = to_rcar_plane(plane); |
cb2025d2 | 561 | struct rcar_du_device *rcdu = rplane->group->dev; |
4bf8e196 | 562 | |
5ee5a81d LP |
563 | if (!state->fb || !state->crtc) { |
564 | rstate->format = NULL; | |
920888a2 | 565 | return 0; |
5ee5a81d | 566 | } |
917de180 | 567 | |
920888a2 LP |
568 | if (state->src_w >> 16 != state->crtc_w || |
569 | state->src_h >> 16 != state->crtc_h) { | |
570 | dev_dbg(rcdu->dev, "%s: scaling not supported\n", __func__); | |
4bf8e196 LP |
571 | return -EINVAL; |
572 | } | |
573 | ||
5ee5a81d LP |
574 | rstate->format = rcar_du_format_info(state->fb->pixel_format); |
575 | if (rstate->format == NULL) { | |
920888a2 LP |
576 | dev_dbg(rcdu->dev, "%s: unsupported format %08x\n", __func__, |
577 | state->fb->pixel_format); | |
4bf8e196 LP |
578 | return -EINVAL; |
579 | } | |
580 | ||
4bf8e196 LP |
581 | return 0; |
582 | } | |
583 | ||
920888a2 LP |
584 | static void rcar_du_plane_atomic_update(struct drm_plane *plane, |
585 | struct drm_plane_state *old_state) | |
586 | { | |
587 | struct rcar_du_plane *rplane = to_rcar_plane(plane); | |
2af03944 LP |
588 | struct rcar_du_plane_state *old_rstate; |
589 | struct rcar_du_plane_state *new_rstate; | |
5bfcbce0 | 590 | |
2af03944 LP |
591 | if (!plane->state->crtc) |
592 | return; | |
593 | ||
594 | rcar_du_plane_setup(rplane); | |
595 | ||
596 | /* Check whether the source has changed from memory to live source or | |
597 | * from live source to memory. The source has been configured by the | |
598 | * VSPS bit in the PnDDCR4 register. Although the datasheet states that | |
599 | * the bit is updated during vertical blanking, it seems that updates | |
600 | * only occur when the DU group is held in reset through the DSYSR.DRES | |
601 | * bit. We thus need to restart the group if the source changes. | |
602 | */ | |
603 | old_rstate = to_rcar_plane_state(old_state); | |
604 | new_rstate = to_rcar_plane_state(plane->state); | |
605 | ||
606 | if ((old_rstate->source == RCAR_DU_PLANE_MEMORY) != | |
607 | (new_rstate->source == RCAR_DU_PLANE_MEMORY)) | |
608 | rplane->group->need_restart = true; | |
4bf8e196 LP |
609 | } |
610 | ||
920888a2 LP |
611 | static const struct drm_plane_helper_funcs rcar_du_plane_helper_funcs = { |
612 | .atomic_check = rcar_du_plane_atomic_check, | |
613 | .atomic_update = rcar_du_plane_atomic_update, | |
614 | }; | |
615 | ||
4407cc02 LP |
616 | static struct drm_plane_state * |
617 | rcar_du_plane_atomic_duplicate_state(struct drm_plane *plane) | |
4bf8e196 | 618 | { |
4407cc02 LP |
619 | struct rcar_du_plane_state *state; |
620 | struct rcar_du_plane_state *copy; | |
4bf8e196 | 621 | |
263b39fe LP |
622 | if (WARN_ON(!plane->state)) |
623 | return NULL; | |
624 | ||
ec69a406 | 625 | state = to_rcar_plane_state(plane->state); |
4407cc02 LP |
626 | copy = kmemdup(state, sizeof(*state), GFP_KERNEL); |
627 | if (copy == NULL) | |
628 | return NULL; | |
629 | ||
263b39fe | 630 | __drm_atomic_helper_plane_duplicate_state(plane, ©->state); |
4bf8e196 | 631 | |
4407cc02 | 632 | return ©->state; |
4bf8e196 LP |
633 | } |
634 | ||
4407cc02 LP |
635 | static void rcar_du_plane_atomic_destroy_state(struct drm_plane *plane, |
636 | struct drm_plane_state *state) | |
4bf8e196 | 637 | { |
2f701695 | 638 | __drm_atomic_helper_plane_destroy_state(state); |
ec69a406 | 639 | kfree(to_rcar_plane_state(state)); |
4407cc02 | 640 | } |
4bf8e196 | 641 | |
a32a3c80 LP |
642 | static void rcar_du_plane_reset(struct drm_plane *plane) |
643 | { | |
644 | struct rcar_du_plane_state *state; | |
645 | ||
646 | if (plane->state) { | |
647 | rcar_du_plane_atomic_destroy_state(plane, plane->state); | |
648 | plane->state = NULL; | |
649 | } | |
650 | ||
651 | state = kzalloc(sizeof(*state), GFP_KERNEL); | |
652 | if (state == NULL) | |
653 | return; | |
654 | ||
655 | state->hwindex = -1; | |
af8ad962 | 656 | state->source = RCAR_DU_PLANE_MEMORY; |
a32a3c80 LP |
657 | state->alpha = 255; |
658 | state->colorkey = RCAR_DU_COLORKEY_NONE; | |
659 | state->zpos = plane->type == DRM_PLANE_TYPE_PRIMARY ? 0 : 1; | |
660 | ||
661 | plane->state = &state->state; | |
662 | plane->state->plane = plane; | |
663 | } | |
664 | ||
4407cc02 LP |
665 | static int rcar_du_plane_atomic_set_property(struct drm_plane *plane, |
666 | struct drm_plane_state *state, | |
667 | struct drm_property *property, | |
668 | uint64_t val) | |
669 | { | |
ec69a406 | 670 | struct rcar_du_plane_state *rstate = to_rcar_plane_state(state); |
9f6aee95 | 671 | struct rcar_du_device *rcdu = to_rcar_plane(plane)->group->dev; |
4bf8e196 | 672 | |
9f6aee95 | 673 | if (property == rcdu->props.alpha) |
4407cc02 | 674 | rstate->alpha = val; |
9f6aee95 | 675 | else if (property == rcdu->props.colorkey) |
4407cc02 | 676 | rstate->colorkey = val; |
9f6aee95 | 677 | else if (property == rcdu->props.zpos) |
4407cc02 LP |
678 | rstate->zpos = val; |
679 | else | |
680 | return -EINVAL; | |
4bf8e196 | 681 | |
4407cc02 | 682 | return 0; |
4bf8e196 LP |
683 | } |
684 | ||
4407cc02 LP |
685 | static int rcar_du_plane_atomic_get_property(struct drm_plane *plane, |
686 | const struct drm_plane_state *state, struct drm_property *property, | |
687 | uint64_t *val) | |
4bf8e196 | 688 | { |
4407cc02 LP |
689 | const struct rcar_du_plane_state *rstate = |
690 | container_of(state, const struct rcar_du_plane_state, state); | |
9f6aee95 | 691 | struct rcar_du_device *rcdu = to_rcar_plane(plane)->group->dev; |
4bf8e196 | 692 | |
9f6aee95 | 693 | if (property == rcdu->props.alpha) |
4407cc02 | 694 | *val = rstate->alpha; |
9f6aee95 | 695 | else if (property == rcdu->props.colorkey) |
4407cc02 | 696 | *val = rstate->colorkey; |
9f6aee95 | 697 | else if (property == rcdu->props.zpos) |
4407cc02 | 698 | *val = rstate->zpos; |
4bf8e196 LP |
699 | else |
700 | return -EINVAL; | |
701 | ||
702 | return 0; | |
703 | } | |
704 | ||
705 | static const struct drm_plane_funcs rcar_du_plane_funcs = { | |
336d04a1 LP |
706 | .update_plane = drm_atomic_helper_update_plane, |
707 | .disable_plane = drm_atomic_helper_disable_plane, | |
4407cc02 LP |
708 | .reset = rcar_du_plane_reset, |
709 | .set_property = drm_atomic_helper_plane_set_property, | |
4bf8e196 | 710 | .destroy = drm_plane_cleanup, |
4407cc02 LP |
711 | .atomic_duplicate_state = rcar_du_plane_atomic_duplicate_state, |
712 | .atomic_destroy_state = rcar_du_plane_atomic_destroy_state, | |
713 | .atomic_set_property = rcar_du_plane_atomic_set_property, | |
714 | .atomic_get_property = rcar_du_plane_atomic_get_property, | |
4bf8e196 LP |
715 | }; |
716 | ||
717 | static const uint32_t formats[] = { | |
718 | DRM_FORMAT_RGB565, | |
719 | DRM_FORMAT_ARGB1555, | |
720 | DRM_FORMAT_XRGB1555, | |
721 | DRM_FORMAT_XRGB8888, | |
722 | DRM_FORMAT_ARGB8888, | |
723 | DRM_FORMAT_UYVY, | |
724 | DRM_FORMAT_YUYV, | |
725 | DRM_FORMAT_NV12, | |
726 | DRM_FORMAT_NV21, | |
727 | DRM_FORMAT_NV16, | |
728 | }; | |
729 | ||
cb2025d2 | 730 | int rcar_du_planes_init(struct rcar_du_group *rgrp) |
4bf8e196 | 731 | { |
cb2025d2 | 732 | struct rcar_du_device *rcdu = rgrp->dev; |
917de180 | 733 | unsigned int crtcs; |
4bf8e196 | 734 | unsigned int i; |
917de180 | 735 | int ret; |
4bf8e196 | 736 | |
9f6aee95 | 737 | /* Create one primary plane per CRTC in this group and seven overlay |
917de180 LP |
738 | * planes. |
739 | */ | |
d6aed574 | 740 | rgrp->num_planes = rgrp->num_crtcs + 7; |
917de180 LP |
741 | |
742 | crtcs = ((1 << rcdu->num_crtcs) - 1) & (3 << (2 * rgrp->index)); | |
743 | ||
d6aed574 | 744 | for (i = 0; i < rgrp->num_planes; ++i) { |
fe6fbe9a | 745 | enum drm_plane_type type = i < rgrp->num_crtcs |
917de180 LP |
746 | ? DRM_PLANE_TYPE_PRIMARY |
747 | : DRM_PLANE_TYPE_OVERLAY; | |
99caede1 | 748 | struct rcar_du_plane *plane = &rgrp->planes[i]; |
4bf8e196 | 749 | |
cb2025d2 | 750 | plane->group = rgrp; |
4bf8e196 | 751 | |
917de180 LP |
752 | ret = drm_universal_plane_init(rcdu->ddev, &plane->plane, crtcs, |
753 | &rcar_du_plane_funcs, formats, | |
b0b3b795 VS |
754 | ARRAY_SIZE(formats), type, |
755 | NULL); | |
4bf8e196 LP |
756 | if (ret < 0) |
757 | return ret; | |
758 | ||
920888a2 LP |
759 | drm_plane_helper_add(&plane->plane, |
760 | &rcar_du_plane_helper_funcs); | |
761 | ||
917de180 LP |
762 | if (type == DRM_PLANE_TYPE_PRIMARY) |
763 | continue; | |
764 | ||
4bf8e196 | 765 | drm_object_attach_property(&plane->plane.base, |
9f6aee95 | 766 | rcdu->props.alpha, 255); |
4bf8e196 | 767 | drm_object_attach_property(&plane->plane.base, |
9f6aee95 | 768 | rcdu->props.colorkey, |
4bf8e196 LP |
769 | RCAR_DU_COLORKEY_NONE); |
770 | drm_object_attach_property(&plane->plane.base, | |
9f6aee95 | 771 | rcdu->props.zpos, 1); |
4bf8e196 LP |
772 | } |
773 | ||
774 | return 0; | |
775 | } |