833 lines · c
1// SPDX-License-Identifier: GPL-2.0+2/*3 * R-Car Display Unit Planes4 *5 * Copyright (C) 2013-2015 Renesas Electronics Corporation6 *7 * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)8 */9 10#include <drm/drm_atomic.h>11#include <drm/drm_atomic_helper.h>12#include <drm/drm_blend.h>13#include <drm/drm_crtc.h>14#include <drm/drm_device.h>15#include <drm/drm_fb_dma_helper.h>16#include <drm/drm_fourcc.h>17#include <drm/drm_framebuffer.h>18#include <drm/drm_gem_dma_helper.h>19 20#include "rcar_du_drv.h"21#include "rcar_du_group.h"22#include "rcar_du_kms.h"23#include "rcar_du_plane.h"24#include "rcar_du_regs.h"25 26/* -----------------------------------------------------------------------------27 * Atomic hardware plane allocator28 *29 * The hardware plane allocator is solely based on the atomic plane states30 * without keeping any external state to avoid races between .atomic_check()31 * and .atomic_commit().32 *33 * The core idea is to avoid using a free planes bitmask that would need to be34 * shared between check and commit handlers with a collective knowledge based on35 * the allocated hardware plane(s) for each KMS plane. The allocator then loops36 * over all plane states to compute the free planes bitmask, allocates hardware37 * planes based on that bitmask, and stores the result back in the plane states.38 *39 * For this to work we need to access the current state of planes not touched by40 * the atomic update. To ensure that it won't be modified, we need to lock all41 * planes using drm_atomic_get_plane_state(). This effectively serializes atomic42 * updates from .atomic_check() up to completion (when swapping the states if43 * the check step has succeeded) or rollback (when freeing the states if the44 * check step has failed).45 *46 * Allocation is performed in the .atomic_check() handler and applied47 * automatically when the core swaps the old and new states.48 */49 50static bool rcar_du_plane_needs_realloc(51 const struct rcar_du_plane_state *old_state,52 const struct rcar_du_plane_state *new_state)53{54 /*55 * Lowering the number of planes doesn't strictly require reallocation56 * as the extra hardware plane will be freed when committing, but doing57 * so could lead to more fragmentation.58 */59 if (!old_state->format ||60 old_state->format->planes != new_state->format->planes)61 return true;62 63 /* Reallocate hardware planes if the source has changed. */64 if (old_state->source != new_state->source)65 return true;66 67 return false;68}69 70static unsigned int rcar_du_plane_hwmask(struct rcar_du_plane_state *state)71{72 unsigned int mask;73 74 if (state->hwindex == -1)75 return 0;76 77 mask = 1 << state->hwindex;78 if (state->format->planes == 2)79 mask |= 1 << ((state->hwindex + 1) % 8);80 81 return mask;82}83 84/*85 * The R8A7790 DU can source frames directly from the VSP1 devices VSPD0 and86 * VSPD1. VSPD0 feeds DU0/1 plane 0, and VSPD1 feeds either DU2 plane 0 or87 * DU0/1 plane 1.88 *89 * Allocate the correct fixed plane when sourcing frames from VSPD0 or VSPD1,90 * and allocate planes in reverse index order otherwise to ensure maximum91 * availability of planes 0 and 1.92 *93 * The caller is responsible for ensuring that the requested source is94 * compatible with the DU revision.95 */96static int rcar_du_plane_hwalloc(struct rcar_du_plane *plane,97 struct rcar_du_plane_state *state,98 unsigned int free)99{100 unsigned int num_planes = state->format->planes;101 int fixed = -1;102 int i;103 104 if (state->source == RCAR_DU_PLANE_VSPD0) {105 /* VSPD0 feeds plane 0 on DU0/1. */106 if (plane->group->index != 0)107 return -EINVAL;108 109 fixed = 0;110 } else if (state->source == RCAR_DU_PLANE_VSPD1) {111 /* VSPD1 feeds plane 1 on DU0/1 or plane 0 on DU2. */112 fixed = plane->group->index == 0 ? 1 : 0;113 }114 115 if (fixed >= 0)116 return free & (1 << fixed) ? fixed : -EBUSY;117 118 for (i = RCAR_DU_NUM_HW_PLANES - 1; i >= 0; --i) {119 if (!(free & (1 << i)))120 continue;121 122 if (num_planes == 1 || free & (1 << ((i + 1) % 8)))123 break;124 }125 126 return i < 0 ? -EBUSY : i;127}128 129int rcar_du_atomic_check_planes(struct drm_device *dev,130 struct drm_atomic_state *state)131{132 struct rcar_du_device *rcdu = to_rcar_du_device(dev);133 unsigned int group_freed_planes[RCAR_DU_MAX_GROUPS] = { 0, };134 unsigned int group_free_planes[RCAR_DU_MAX_GROUPS] = { 0, };135 bool needs_realloc = false;136 unsigned int groups = 0;137 unsigned int i;138 struct drm_plane *drm_plane;139 struct drm_plane_state *old_drm_plane_state;140 struct drm_plane_state *new_drm_plane_state;141 142 /* Check if hardware planes need to be reallocated. */143 for_each_oldnew_plane_in_state(state, drm_plane, old_drm_plane_state,144 new_drm_plane_state, i) {145 struct rcar_du_plane_state *old_plane_state;146 struct rcar_du_plane_state *new_plane_state;147 struct rcar_du_plane *plane;148 unsigned int index;149 150 plane = to_rcar_plane(drm_plane);151 old_plane_state = to_rcar_plane_state(old_drm_plane_state);152 new_plane_state = to_rcar_plane_state(new_drm_plane_state);153 154 dev_dbg(rcdu->dev, "%s: checking plane (%u,%tu)\n", __func__,155 plane->group->index, plane - plane->group->planes);156 157 /*158 * If the plane is being disabled we don't need to go through159 * the full reallocation procedure. Just mark the hardware160 * plane(s) as freed.161 */162 if (!new_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 new_plane_state->hwindex = -1;168 continue;169 }170 171 /*172 * If the plane needs to be reallocated mark it as such, and173 * mark the hardware plane(s) as free.174 */175 if (rcar_du_plane_needs_realloc(old_plane_state, new_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 new_plane_state->hwindex = -1;184 }185 }186 187 if (!needs_realloc)188 return 0;189 190 /*191 * Grab all plane states for the groups that need reallocation to ensure192 * locking and avoid racy updates. This serializes the update operation,193 * but there's not much we can do about it as that's the hardware194 * design.195 *196 * Compute the used planes mask for each group at the same time to avoid197 * looping over the planes separately later.198 */199 while (groups) {200 unsigned int index = ffs(groups) - 1;201 struct rcar_du_group *group = &rcdu->groups[index];202 unsigned int used_planes = 0;203 204 dev_dbg(rcdu->dev, "%s: finding free planes for group %u\n",205 __func__, index);206 207 for (i = 0; i < group->num_planes; ++i) {208 struct rcar_du_plane *plane = &group->planes[i];209 struct rcar_du_plane_state *new_plane_state;210 struct drm_plane_state *s;211 212 s = drm_atomic_get_plane_state(state, &plane->plane);213 if (IS_ERR(s))214 return PTR_ERR(s);215 216 /*217 * If the plane has been freed in the above loop its218 * hardware planes must not be added to the used planes219 * bitmask. However, the current state doesn't reflect220 * the free state yet, as we've modified the new state221 * above. Use the local freed planes list to check for222 * that condition instead.223 */224 if (group_freed_planes[index] & (1 << i)) {225 dev_dbg(rcdu->dev,226 "%s: plane (%u,%tu) has been freed, skipping\n",227 __func__, plane->group->index,228 plane - plane->group->planes);229 continue;230 }231 232 new_plane_state = to_rcar_plane_state(s);233 used_planes |= rcar_du_plane_hwmask(new_plane_state);234 235 dev_dbg(rcdu->dev,236 "%s: plane (%u,%tu) uses %u hwplanes (index %d)\n",237 __func__, plane->group->index,238 plane - plane->group->planes,239 new_plane_state->format ?240 new_plane_state->format->planes : 0,241 new_plane_state->hwindex);242 }243 244 group_free_planes[index] = 0xff & ~used_planes;245 groups &= ~(1 << index);246 247 dev_dbg(rcdu->dev, "%s: group %u free planes mask 0x%02x\n",248 __func__, index, group_free_planes[index]);249 }250 251 /* Reallocate hardware planes for each plane that needs it. */252 for_each_oldnew_plane_in_state(state, drm_plane, old_drm_plane_state,253 new_drm_plane_state, i) {254 struct rcar_du_plane_state *old_plane_state;255 struct rcar_du_plane_state *new_plane_state;256 struct rcar_du_plane *plane;257 unsigned int crtc_planes;258 unsigned int free;259 int idx;260 261 plane = to_rcar_plane(drm_plane);262 old_plane_state = to_rcar_plane_state(old_drm_plane_state);263 new_plane_state = to_rcar_plane_state(new_drm_plane_state);264 265 dev_dbg(rcdu->dev, "%s: allocating plane (%u,%tu)\n", __func__,266 plane->group->index, plane - plane->group->planes);267 268 /*269 * Skip planes that are being disabled or don't need to be270 * reallocated.271 */272 if (!new_plane_state->format ||273 !rcar_du_plane_needs_realloc(old_plane_state, new_plane_state))274 continue;275 276 /*277 * Try to allocate the plane from the free planes currently278 * associated with the target CRTC to avoid restarting the CRTC279 * group and thus minimize flicker. If it fails fall back to280 * allocating from all free planes.281 */282 crtc_planes = to_rcar_crtc(new_plane_state->state.crtc)->index % 2283 ? plane->group->dptsr_planes284 : ~plane->group->dptsr_planes;285 free = group_free_planes[plane->group->index];286 287 idx = rcar_du_plane_hwalloc(plane, new_plane_state,288 free & crtc_planes);289 if (idx < 0)290 idx = rcar_du_plane_hwalloc(plane, new_plane_state,291 free);292 if (idx < 0) {293 dev_dbg(rcdu->dev, "%s: no available hardware plane\n",294 __func__);295 return idx;296 }297 298 dev_dbg(rcdu->dev, "%s: allocated %u hwplanes (index %u)\n",299 __func__, new_plane_state->format->planes, idx);300 301 new_plane_state->hwindex = idx;302 303 group_free_planes[plane->group->index] &=304 ~rcar_du_plane_hwmask(new_plane_state);305 306 dev_dbg(rcdu->dev, "%s: group %u free planes mask 0x%02x\n",307 __func__, plane->group->index,308 group_free_planes[plane->group->index]);309 }310 311 return 0;312}313 314/* -----------------------------------------------------------------------------315 * Plane Setup316 */317 318#define RCAR_DU_COLORKEY_NONE (0 << 24)319#define RCAR_DU_COLORKEY_SOURCE (1 << 24)320#define RCAR_DU_COLORKEY_MASK (1 << 24)321 322static void rcar_du_plane_write(struct rcar_du_group *rgrp,323 unsigned int index, u32 reg, u32 data)324{325 rcar_du_write(rgrp->dev, rgrp->mmio_offset + index * PLANE_OFF + reg,326 data);327}328 329static void rcar_du_plane_setup_scanout(struct rcar_du_group *rgrp,330 const struct rcar_du_plane_state *state)331{332 unsigned int src_x = state->state.src.x1 >> 16;333 unsigned int src_y = state->state.src.y1 >> 16;334 unsigned int index = state->hwindex;335 unsigned int pitch;336 bool interlaced;337 u32 dma[2];338 339 interlaced = state->state.crtc->state->adjusted_mode.flags340 & DRM_MODE_FLAG_INTERLACE;341 342 if (state->source == RCAR_DU_PLANE_MEMORY) {343 struct drm_framebuffer *fb = state->state.fb;344 struct drm_gem_dma_object *gem;345 unsigned int i;346 347 if (state->format->planes == 2)348 pitch = fb->pitches[0];349 else350 pitch = fb->pitches[0] * 8 / state->format->bpp;351 352 for (i = 0; i < state->format->planes; ++i) {353 gem = drm_fb_dma_get_gem_obj(fb, i);354 dma[i] = gem->dma_addr + fb->offsets[i];355 }356 } else {357 pitch = drm_rect_width(&state->state.src) >> 16;358 dma[0] = 0;359 dma[1] = 0;360 }361 362 /*363 * Memory pitch (expressed in pixels). Must be doubled for interlaced364 * operation with 32bpp formats.365 */366 rcar_du_plane_write(rgrp, index, PnMWR,367 (interlaced && state->format->bpp == 32) ?368 pitch * 2 : pitch);369 370 /*371 * The Y position is expressed in raster line units and must be doubled372 * for 32bpp formats, according to the R8A7790 datasheet. No mention of373 * doubling the Y position is found in the R8A7779 datasheet, but the374 * rule seems to apply there as well.375 *376 * Despite not being documented, doubling seem not to be needed when377 * operating in interlaced mode.378 *379 * Similarly, for the second plane, NV12 and NV21 formats seem to380 * require a halved Y position value, in both progressive and interlaced381 * modes.382 */383 rcar_du_plane_write(rgrp, index, PnSPXR, src_x);384 rcar_du_plane_write(rgrp, index, PnSPYR, src_y *385 (!interlaced && state->format->bpp == 32 ? 2 : 1));386 387 rcar_du_plane_write(rgrp, index, PnDSA0R, dma[0]);388 389 if (state->format->planes == 2) {390 index = (index + 1) % 8;391 392 rcar_du_plane_write(rgrp, index, PnMWR, pitch);393 394 rcar_du_plane_write(rgrp, index, PnSPXR, src_x);395 rcar_du_plane_write(rgrp, index, PnSPYR, src_y *396 (state->format->bpp == 16 ? 2 : 1) / 2);397 398 rcar_du_plane_write(rgrp, index, PnDSA0R, dma[1]);399 }400}401 402static void rcar_du_plane_setup_mode(struct rcar_du_group *rgrp,403 unsigned int index,404 const struct rcar_du_plane_state *state)405{406 u32 colorkey;407 u32 pnmr;408 409 /*410 * The PnALPHAR register controls alpha-blending in 16bpp formats411 * (ARGB1555 and XRGB1555).412 *413 * For ARGB, set the alpha value to 0, and enable alpha-blending when414 * the A bit is 0. This maps A=0 to alpha=0 and A=1 to alpha=255.415 *416 * For XRGB, set the alpha value to the plane-wide alpha value and417 * enable alpha-blending regardless of the X bit value.418 */419 if (state->format->fourcc != DRM_FORMAT_XRGB1555)420 rcar_du_plane_write(rgrp, index, PnALPHAR, PnALPHAR_ABIT_0);421 else422 rcar_du_plane_write(rgrp, index, PnALPHAR,423 PnALPHAR_ABIT_X | state->state.alpha >> 8);424 425 pnmr = PnMR_BM_MD | state->format->pnmr;426 427 /*428 * Disable color keying when requested. YUV formats have the429 * PnMR_SPIM_TP_OFF bit set in their pnmr field, disabling color keying430 * automatically.431 */432 if ((state->colorkey & RCAR_DU_COLORKEY_MASK) == RCAR_DU_COLORKEY_NONE)433 pnmr |= PnMR_SPIM_TP_OFF;434 435 /* For packed YUV formats we need to select the U/V order. */436 if (state->format->fourcc == DRM_FORMAT_YUYV)437 pnmr |= PnMR_YCDF_YUYV;438 439 rcar_du_plane_write(rgrp, index, PnMR, pnmr);440 441 switch (state->format->fourcc) {442 case DRM_FORMAT_RGB565:443 colorkey = ((state->colorkey & 0xf80000) >> 8)444 | ((state->colorkey & 0x00fc00) >> 5)445 | ((state->colorkey & 0x0000f8) >> 3);446 rcar_du_plane_write(rgrp, index, PnTC2R, colorkey);447 break;448 449 case DRM_FORMAT_ARGB1555:450 case DRM_FORMAT_XRGB1555:451 colorkey = ((state->colorkey & 0xf80000) >> 9)452 | ((state->colorkey & 0x00f800) >> 6)453 | ((state->colorkey & 0x0000f8) >> 3);454 rcar_du_plane_write(rgrp, index, PnTC2R, colorkey);455 break;456 457 case DRM_FORMAT_XRGB8888:458 case DRM_FORMAT_ARGB8888:459 rcar_du_plane_write(rgrp, index, PnTC3R,460 PnTC3R_CODE | (state->colorkey & 0xffffff));461 break;462 }463}464 465static void rcar_du_plane_setup_format_gen2(struct rcar_du_group *rgrp,466 unsigned int index,467 const struct rcar_du_plane_state *state)468{469 u32 ddcr2 = PnDDCR2_CODE;470 u32 ddcr4;471 472 /*473 * Data format474 *475 * The data format is selected by the DDDF field in PnMR and the EDF476 * field in DDCR4.477 */478 479 rcar_du_plane_setup_mode(rgrp, index, state);480 481 if (state->format->planes == 2) {482 if (state->hwindex != index) {483 if (state->format->fourcc == DRM_FORMAT_NV12 ||484 state->format->fourcc == DRM_FORMAT_NV21)485 ddcr2 |= PnDDCR2_Y420;486 487 if (state->format->fourcc == DRM_FORMAT_NV21)488 ddcr2 |= PnDDCR2_NV21;489 490 ddcr2 |= PnDDCR2_DIVU;491 } else {492 ddcr2 |= PnDDCR2_DIVY;493 }494 }495 496 rcar_du_plane_write(rgrp, index, PnDDCR2, ddcr2);497 498 ddcr4 = state->format->edf | PnDDCR4_CODE;499 if (state->source != RCAR_DU_PLANE_MEMORY)500 ddcr4 |= PnDDCR4_VSPS;501 502 rcar_du_plane_write(rgrp, index, PnDDCR4, ddcr4);503}504 505static void rcar_du_plane_setup_format_gen3(struct rcar_du_group *rgrp,506 unsigned int index,507 const struct rcar_du_plane_state *state)508{509 struct rcar_du_device *rcdu = rgrp->dev;510 u32 pnmr = state->format->pnmr | PnMR_SPIM_TP_OFF;511 512 if (rcdu->info->features & RCAR_DU_FEATURE_NO_BLENDING) {513 /* No blending. ALP and EOR are not supported. */514 pnmr &= ~(PnMR_SPIM_ALP | PnMR_SPIM_EOR);515 }516 517 rcar_du_plane_write(rgrp, index, PnMR, pnmr);518 519 rcar_du_plane_write(rgrp, index, PnDDCR4,520 state->format->edf | PnDDCR4_CODE);521 522 /*523 * On Gen3, some DU channels have two planes, each being wired to a524 * separate VSPD instance. The DU can then blend two planes. While525 * this feature isn't used by the driver, issues related to alpha526 * blending (such as incorrect colors or planes being invisible) may527 * still occur if the PnALPHAR register has a stale value. Set the528 * register to 0 to avoid this.529 */530 531 rcar_du_plane_write(rgrp, index, PnALPHAR, 0);532}533 534static void rcar_du_plane_setup_format(struct rcar_du_group *rgrp,535 unsigned int index,536 const struct rcar_du_plane_state *state)537{538 struct rcar_du_device *rcdu = rgrp->dev;539 const struct drm_rect *dst = &state->state.dst;540 541 if (rcdu->info->gen < 3)542 rcar_du_plane_setup_format_gen2(rgrp, index, state);543 else544 rcar_du_plane_setup_format_gen3(rgrp, index, state);545 546 /* Destination position and size */547 rcar_du_plane_write(rgrp, index, PnDSXR, drm_rect_width(dst));548 rcar_du_plane_write(rgrp, index, PnDSYR, drm_rect_height(dst));549 rcar_du_plane_write(rgrp, index, PnDPXR, dst->x1);550 rcar_du_plane_write(rgrp, index, PnDPYR, dst->y1);551 552 if (rcdu->info->gen < 3) {553 /* Wrap-around and blinking, disabled */554 rcar_du_plane_write(rgrp, index, PnWASPR, 0);555 rcar_du_plane_write(rgrp, index, PnWAMWR, 4095);556 rcar_du_plane_write(rgrp, index, PnBTR, 0);557 rcar_du_plane_write(rgrp, index, PnMLR, 0);558 }559}560 561void __rcar_du_plane_setup(struct rcar_du_group *rgrp,562 const struct rcar_du_plane_state *state)563{564 struct rcar_du_device *rcdu = rgrp->dev;565 566 rcar_du_plane_setup_format(rgrp, state->hwindex, state);567 if (state->format->planes == 2)568 rcar_du_plane_setup_format(rgrp, (state->hwindex + 1) % 8,569 state);570 571 if (rcdu->info->gen >= 3)572 return;573 574 rcar_du_plane_setup_scanout(rgrp, state);575 576 if (state->source == RCAR_DU_PLANE_VSPD1) {577 unsigned int vspd1_sink = rgrp->index ? 2 : 0;578 579 if (rcdu->vspd1_sink != vspd1_sink) {580 rcdu->vspd1_sink = vspd1_sink;581 rcar_du_set_dpad0_vsp1_routing(rcdu);582 583 /*584 * Changes to the VSP1 sink take effect on DRES and thus585 * need a restart of the group.586 */587 rgrp->need_restart = true;588 }589 }590}591 592int __rcar_du_plane_atomic_check(struct drm_plane *plane,593 struct drm_plane_state *state,594 const struct rcar_du_format_info **format)595{596 struct drm_device *dev = plane->dev;597 struct drm_crtc_state *crtc_state;598 int ret;599 600 if (!state->crtc) {601 /*602 * The visible field is not reset by the DRM core but only603 * updated by drm_atomic_helper_check_plane_state(), set it604 * manually.605 */606 state->visible = false;607 *format = NULL;608 return 0;609 }610 611 crtc_state = drm_atomic_get_crtc_state(state->state, state->crtc);612 if (IS_ERR(crtc_state))613 return PTR_ERR(crtc_state);614 615 ret = drm_atomic_helper_check_plane_state(state, crtc_state,616 DRM_PLANE_NO_SCALING,617 DRM_PLANE_NO_SCALING,618 true, true);619 if (ret < 0)620 return ret;621 622 if (!state->visible) {623 *format = NULL;624 return 0;625 }626 627 *format = rcar_du_format_info(state->fb->format->format);628 if (*format == NULL) {629 dev_dbg(dev->dev, "%s: unsupported format %p4cc\n", __func__,630 &state->fb->format->format);631 return -EINVAL;632 }633 634 return 0;635}636 637static int rcar_du_plane_atomic_check(struct drm_plane *plane,638 struct drm_atomic_state *state)639{640 struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state,641 plane);642 struct rcar_du_plane_state *rstate = to_rcar_plane_state(new_plane_state);643 644 return __rcar_du_plane_atomic_check(plane, new_plane_state,645 &rstate->format);646}647 648static void rcar_du_plane_atomic_update(struct drm_plane *plane,649 struct drm_atomic_state *state)650{651 struct drm_plane_state *old_state = drm_atomic_get_old_plane_state(state, plane);652 struct drm_plane_state *new_state = drm_atomic_get_new_plane_state(state, plane);653 struct rcar_du_plane *rplane = to_rcar_plane(plane);654 struct rcar_du_plane_state *old_rstate;655 struct rcar_du_plane_state *new_rstate;656 657 if (!new_state->visible)658 return;659 660 rcar_du_plane_setup(rplane);661 662 /*663 * Check whether the source has changed from memory to live source or664 * from live source to memory. The source has been configured by the665 * VSPS bit in the PnDDCR4 register. Although the datasheet states that666 * the bit is updated during vertical blanking, it seems that updates667 * only occur when the DU group is held in reset through the DSYSR.DRES668 * bit. We thus need to restart the group if the source changes.669 */670 old_rstate = to_rcar_plane_state(old_state);671 new_rstate = to_rcar_plane_state(new_state);672 673 if ((old_rstate->source == RCAR_DU_PLANE_MEMORY) !=674 (new_rstate->source == RCAR_DU_PLANE_MEMORY))675 rplane->group->need_restart = true;676}677 678static const struct drm_plane_helper_funcs rcar_du_plane_helper_funcs = {679 .atomic_check = rcar_du_plane_atomic_check,680 .atomic_update = rcar_du_plane_atomic_update,681};682 683static struct drm_plane_state *684rcar_du_plane_atomic_duplicate_state(struct drm_plane *plane)685{686 struct rcar_du_plane_state *state;687 struct rcar_du_plane_state *copy;688 689 if (WARN_ON(!plane->state))690 return NULL;691 692 state = to_rcar_plane_state(plane->state);693 copy = kmemdup(state, sizeof(*state), GFP_KERNEL);694 if (copy == NULL)695 return NULL;696 697 __drm_atomic_helper_plane_duplicate_state(plane, ©->state);698 699 return ©->state;700}701 702static void rcar_du_plane_atomic_destroy_state(struct drm_plane *plane,703 struct drm_plane_state *state)704{705 __drm_atomic_helper_plane_destroy_state(state);706 kfree(to_rcar_plane_state(state));707}708 709static void rcar_du_plane_reset(struct drm_plane *plane)710{711 struct rcar_du_plane_state *state;712 713 if (plane->state) {714 rcar_du_plane_atomic_destroy_state(plane, plane->state);715 plane->state = NULL;716 }717 718 state = kzalloc(sizeof(*state), GFP_KERNEL);719 if (state == NULL)720 return;721 722 __drm_atomic_helper_plane_reset(plane, &state->state);723 724 state->hwindex = -1;725 state->source = RCAR_DU_PLANE_MEMORY;726 state->colorkey = RCAR_DU_COLORKEY_NONE;727}728 729static int rcar_du_plane_atomic_set_property(struct drm_plane *plane,730 struct drm_plane_state *state,731 struct drm_property *property,732 uint64_t val)733{734 struct rcar_du_plane_state *rstate = to_rcar_plane_state(state);735 struct rcar_du_device *rcdu = to_rcar_plane(plane)->group->dev;736 737 if (property == rcdu->props.colorkey)738 rstate->colorkey = val;739 else740 return -EINVAL;741 742 return 0;743}744 745static int rcar_du_plane_atomic_get_property(struct drm_plane *plane,746 const struct drm_plane_state *state, struct drm_property *property,747 uint64_t *val)748{749 const struct rcar_du_plane_state *rstate =750 container_of(state, const struct rcar_du_plane_state, state);751 struct rcar_du_device *rcdu = to_rcar_plane(plane)->group->dev;752 753 if (property == rcdu->props.colorkey)754 *val = rstate->colorkey;755 else756 return -EINVAL;757 758 return 0;759}760 761static const struct drm_plane_funcs rcar_du_plane_funcs = {762 .update_plane = drm_atomic_helper_update_plane,763 .disable_plane = drm_atomic_helper_disable_plane,764 .reset = rcar_du_plane_reset,765 .destroy = drm_plane_cleanup,766 .atomic_duplicate_state = rcar_du_plane_atomic_duplicate_state,767 .atomic_destroy_state = rcar_du_plane_atomic_destroy_state,768 .atomic_set_property = rcar_du_plane_atomic_set_property,769 .atomic_get_property = rcar_du_plane_atomic_get_property,770};771 772static const uint32_t formats[] = {773 DRM_FORMAT_RGB565,774 DRM_FORMAT_ARGB1555,775 DRM_FORMAT_XRGB1555,776 DRM_FORMAT_XRGB8888,777 DRM_FORMAT_ARGB8888,778 DRM_FORMAT_UYVY,779 DRM_FORMAT_YUYV,780 DRM_FORMAT_NV12,781 DRM_FORMAT_NV21,782 DRM_FORMAT_NV16,783};784 785int rcar_du_planes_init(struct rcar_du_group *rgrp)786{787 struct rcar_du_device *rcdu = rgrp->dev;788 unsigned int crtcs;789 unsigned int i;790 int ret;791 792 /*793 * Create one primary plane per CRTC in this group and seven overlay794 * planes.795 */796 rgrp->num_planes = rgrp->num_crtcs + 7;797 798 crtcs = ((1 << rcdu->num_crtcs) - 1) & (3 << (2 * rgrp->index));799 800 for (i = 0; i < rgrp->num_planes; ++i) {801 enum drm_plane_type type = i < rgrp->num_crtcs802 ? DRM_PLANE_TYPE_PRIMARY803 : DRM_PLANE_TYPE_OVERLAY;804 struct rcar_du_plane *plane = &rgrp->planes[i];805 806 plane->group = rgrp;807 808 ret = drm_universal_plane_init(&rcdu->ddev, &plane->plane,809 crtcs, &rcar_du_plane_funcs,810 formats, ARRAY_SIZE(formats),811 NULL, type, NULL);812 if (ret < 0)813 return ret;814 815 drm_plane_helper_add(&plane->plane,816 &rcar_du_plane_helper_funcs);817 818 drm_plane_create_alpha_property(&plane->plane);819 820 if (type == DRM_PLANE_TYPE_PRIMARY) {821 drm_plane_create_zpos_immutable_property(&plane->plane,822 0);823 } else {824 drm_object_attach_property(&plane->plane.base,825 rcdu->props.colorkey,826 RCAR_DU_COLORKEY_NONE);827 drm_plane_create_zpos_property(&plane->plane, 1, 1, 7);828 }829 }830 831 return 0;832}833