Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0+
0002 /*
0003  * R-Car Display Unit Planes
0004  *
0005  * Copyright (C) 2013-2015 Renesas Electronics Corporation
0006  *
0007  * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
0008  */
0009 
0010 #include <drm/drm_atomic.h>
0011 #include <drm/drm_atomic_helper.h>
0012 #include <drm/drm_blend.h>
0013 #include <drm/drm_crtc.h>
0014 #include <drm/drm_device.h>
0015 #include <drm/drm_fb_cma_helper.h>
0016 #include <drm/drm_fourcc.h>
0017 #include <drm/drm_framebuffer.h>
0018 #include <drm/drm_gem_cma_helper.h>
0019 #include <drm/drm_plane_helper.h>
0020 
0021 #include "rcar_du_drv.h"
0022 #include "rcar_du_group.h"
0023 #include "rcar_du_kms.h"
0024 #include "rcar_du_plane.h"
0025 #include "rcar_du_regs.h"
0026 
0027 /* -----------------------------------------------------------------------------
0028  * Atomic hardware plane allocator
0029  *
0030  * The hardware plane allocator is solely based on the atomic plane states
0031  * without keeping any external state to avoid races between .atomic_check()
0032  * and .atomic_commit().
0033  *
0034  * The core idea is to avoid using a free planes bitmask that would need to be
0035  * shared between check and commit handlers with a collective knowledge based on
0036  * the allocated hardware plane(s) for each KMS plane. The allocator then loops
0037  * over all plane states to compute the free planes bitmask, allocates hardware
0038  * planes based on that bitmask, and stores the result back in the plane states.
0039  *
0040  * For this to work we need to access the current state of planes not touched by
0041  * the atomic update. To ensure that it won't be modified, we need to lock all
0042  * planes using drm_atomic_get_plane_state(). This effectively serializes atomic
0043  * updates from .atomic_check() up to completion (when swapping the states if
0044  * the check step has succeeded) or rollback (when freeing the states if the
0045  * check step has failed).
0046  *
0047  * Allocation is performed in the .atomic_check() handler and applied
0048  * automatically when the core swaps the old and new states.
0049  */
0050 
0051 static bool rcar_du_plane_needs_realloc(
0052                 const struct rcar_du_plane_state *old_state,
0053                 const struct rcar_du_plane_state *new_state)
0054 {
0055     /*
0056      * Lowering the number of planes doesn't strictly require reallocation
0057      * as the extra hardware plane will be freed when committing, but doing
0058      * so could lead to more fragmentation.
0059      */
0060     if (!old_state->format ||
0061         old_state->format->planes != new_state->format->planes)
0062         return true;
0063 
0064     /* Reallocate hardware planes if the source has changed. */
0065     if (old_state->source != new_state->source)
0066         return true;
0067 
0068     return false;
0069 }
0070 
0071 static unsigned int rcar_du_plane_hwmask(struct rcar_du_plane_state *state)
0072 {
0073     unsigned int mask;
0074 
0075     if (state->hwindex == -1)
0076         return 0;
0077 
0078     mask = 1 << state->hwindex;
0079     if (state->format->planes == 2)
0080         mask |= 1 << ((state->hwindex + 1) % 8);
0081 
0082     return mask;
0083 }
0084 
0085 /*
0086  * The R8A7790 DU can source frames directly from the VSP1 devices VSPD0 and
0087  * VSPD1. VSPD0 feeds DU0/1 plane 0, and VSPD1 feeds either DU2 plane 0 or
0088  * DU0/1 plane 1.
0089  *
0090  * Allocate the correct fixed plane when sourcing frames from VSPD0 or VSPD1,
0091  * and allocate planes in reverse index order otherwise to ensure maximum
0092  * availability of planes 0 and 1.
0093  *
0094  * The caller is responsible for ensuring that the requested source is
0095  * compatible with the DU revision.
0096  */
0097 static int rcar_du_plane_hwalloc(struct rcar_du_plane *plane,
0098                  struct rcar_du_plane_state *state,
0099                  unsigned int free)
0100 {
0101     unsigned int num_planes = state->format->planes;
0102     int fixed = -1;
0103     int i;
0104 
0105     if (state->source == RCAR_DU_PLANE_VSPD0) {
0106         /* VSPD0 feeds plane 0 on DU0/1. */
0107         if (plane->group->index != 0)
0108             return -EINVAL;
0109 
0110         fixed = 0;
0111     } else if (state->source == RCAR_DU_PLANE_VSPD1) {
0112         /* VSPD1 feeds plane 1 on DU0/1 or plane 0 on DU2. */
0113         fixed = plane->group->index == 0 ? 1 : 0;
0114     }
0115 
0116     if (fixed >= 0)
0117         return free & (1 << fixed) ? fixed : -EBUSY;
0118 
0119     for (i = RCAR_DU_NUM_HW_PLANES - 1; i >= 0; --i) {
0120         if (!(free & (1 << i)))
0121             continue;
0122 
0123         if (num_planes == 1 || free & (1 << ((i + 1) % 8)))
0124             break;
0125     }
0126 
0127     return i < 0 ? -EBUSY : i;
0128 }
0129 
0130 int rcar_du_atomic_check_planes(struct drm_device *dev,
0131                 struct drm_atomic_state *state)
0132 {
0133     struct rcar_du_device *rcdu = to_rcar_du_device(dev);
0134     unsigned int group_freed_planes[RCAR_DU_MAX_GROUPS] = { 0, };
0135     unsigned int group_free_planes[RCAR_DU_MAX_GROUPS] = { 0, };
0136     bool needs_realloc = false;
0137     unsigned int groups = 0;
0138     unsigned int i;
0139     struct drm_plane *drm_plane;
0140     struct drm_plane_state *old_drm_plane_state;
0141     struct drm_plane_state *new_drm_plane_state;
0142 
0143     /* Check if hardware planes need to be reallocated. */
0144     for_each_oldnew_plane_in_state(state, drm_plane, old_drm_plane_state,
0145                        new_drm_plane_state, i) {
0146         struct rcar_du_plane_state *old_plane_state;
0147         struct rcar_du_plane_state *new_plane_state;
0148         struct rcar_du_plane *plane;
0149         unsigned int index;
0150 
0151         plane = to_rcar_plane(drm_plane);
0152         old_plane_state = to_rcar_plane_state(old_drm_plane_state);
0153         new_plane_state = to_rcar_plane_state(new_drm_plane_state);
0154 
0155         dev_dbg(rcdu->dev, "%s: checking plane (%u,%tu)\n", __func__,
0156             plane->group->index, plane - plane->group->planes);
0157 
0158         /*
0159          * If the plane is being disabled we don't need to go through
0160          * the full reallocation procedure. Just mark the hardware
0161          * plane(s) as freed.
0162          */
0163         if (!new_plane_state->format) {
0164             dev_dbg(rcdu->dev, "%s: plane is being disabled\n",
0165                 __func__);
0166             index = plane - plane->group->planes;
0167             group_freed_planes[plane->group->index] |= 1 << index;
0168             new_plane_state->hwindex = -1;
0169             continue;
0170         }
0171 
0172         /*
0173          * If the plane needs to be reallocated mark it as such, and
0174          * mark the hardware plane(s) as free.
0175          */
0176         if (rcar_du_plane_needs_realloc(old_plane_state, new_plane_state)) {
0177             dev_dbg(rcdu->dev, "%s: plane needs reallocation\n",
0178                 __func__);
0179             groups |= 1 << plane->group->index;
0180             needs_realloc = true;
0181 
0182             index = plane - plane->group->planes;
0183             group_freed_planes[plane->group->index] |= 1 << index;
0184             new_plane_state->hwindex = -1;
0185         }
0186     }
0187 
0188     if (!needs_realloc)
0189         return 0;
0190 
0191     /*
0192      * Grab all plane states for the groups that need reallocation to ensure
0193      * locking and avoid racy updates. This serializes the update operation,
0194      * but there's not much we can do about it as that's the hardware
0195      * design.
0196      *
0197      * Compute the used planes mask for each group at the same time to avoid
0198      * looping over the planes separately later.
0199      */
0200     while (groups) {
0201         unsigned int index = ffs(groups) - 1;
0202         struct rcar_du_group *group = &rcdu->groups[index];
0203         unsigned int used_planes = 0;
0204 
0205         dev_dbg(rcdu->dev, "%s: finding free planes for group %u\n",
0206             __func__, index);
0207 
0208         for (i = 0; i < group->num_planes; ++i) {
0209             struct rcar_du_plane *plane = &group->planes[i];
0210             struct rcar_du_plane_state *new_plane_state;
0211             struct drm_plane_state *s;
0212 
0213             s = drm_atomic_get_plane_state(state, &plane->plane);
0214             if (IS_ERR(s))
0215                 return PTR_ERR(s);
0216 
0217             /*
0218              * If the plane has been freed in the above loop its
0219              * hardware planes must not be added to the used planes
0220              * bitmask. However, the current state doesn't reflect
0221              * the free state yet, as we've modified the new state
0222              * above. Use the local freed planes list to check for
0223              * that condition instead.
0224              */
0225             if (group_freed_planes[index] & (1 << i)) {
0226                 dev_dbg(rcdu->dev,
0227                     "%s: plane (%u,%tu) has been freed, skipping\n",
0228                     __func__, plane->group->index,
0229                     plane - plane->group->planes);
0230                 continue;
0231             }
0232 
0233             new_plane_state = to_rcar_plane_state(s);
0234             used_planes |= rcar_du_plane_hwmask(new_plane_state);
0235 
0236             dev_dbg(rcdu->dev,
0237                 "%s: plane (%u,%tu) uses %u hwplanes (index %d)\n",
0238                 __func__, plane->group->index,
0239                 plane - plane->group->planes,
0240                 new_plane_state->format ?
0241                 new_plane_state->format->planes : 0,
0242                 new_plane_state->hwindex);
0243         }
0244 
0245         group_free_planes[index] = 0xff & ~used_planes;
0246         groups &= ~(1 << index);
0247 
0248         dev_dbg(rcdu->dev, "%s: group %u free planes mask 0x%02x\n",
0249             __func__, index, group_free_planes[index]);
0250     }
0251 
0252     /* Reallocate hardware planes for each plane that needs it. */
0253     for_each_oldnew_plane_in_state(state, drm_plane, old_drm_plane_state,
0254                        new_drm_plane_state, i) {
0255         struct rcar_du_plane_state *old_plane_state;
0256         struct rcar_du_plane_state *new_plane_state;
0257         struct rcar_du_plane *plane;
0258         unsigned int crtc_planes;
0259         unsigned int free;
0260         int idx;
0261 
0262         plane = to_rcar_plane(drm_plane);
0263         old_plane_state = to_rcar_plane_state(old_drm_plane_state);
0264         new_plane_state = to_rcar_plane_state(new_drm_plane_state);
0265 
0266         dev_dbg(rcdu->dev, "%s: allocating plane (%u,%tu)\n", __func__,
0267             plane->group->index, plane - plane->group->planes);
0268 
0269         /*
0270          * Skip planes that are being disabled or don't need to be
0271          * reallocated.
0272          */
0273         if (!new_plane_state->format ||
0274             !rcar_du_plane_needs_realloc(old_plane_state, new_plane_state))
0275             continue;
0276 
0277         /*
0278          * Try to allocate the plane from the free planes currently
0279          * associated with the target CRTC to avoid restarting the CRTC
0280          * group and thus minimize flicker. If it fails fall back to
0281          * allocating from all free planes.
0282          */
0283         crtc_planes = to_rcar_crtc(new_plane_state->state.crtc)->index % 2
0284                 ? plane->group->dptsr_planes
0285                 : ~plane->group->dptsr_planes;
0286         free = group_free_planes[plane->group->index];
0287 
0288         idx = rcar_du_plane_hwalloc(plane, new_plane_state,
0289                         free & crtc_planes);
0290         if (idx < 0)
0291             idx = rcar_du_plane_hwalloc(plane, new_plane_state,
0292                             free);
0293         if (idx < 0) {
0294             dev_dbg(rcdu->dev, "%s: no available hardware plane\n",
0295                 __func__);
0296             return idx;
0297         }
0298 
0299         dev_dbg(rcdu->dev, "%s: allocated %u hwplanes (index %u)\n",
0300             __func__, new_plane_state->format->planes, idx);
0301 
0302         new_plane_state->hwindex = idx;
0303 
0304         group_free_planes[plane->group->index] &=
0305             ~rcar_du_plane_hwmask(new_plane_state);
0306 
0307         dev_dbg(rcdu->dev, "%s: group %u free planes mask 0x%02x\n",
0308             __func__, plane->group->index,
0309             group_free_planes[plane->group->index]);
0310     }
0311 
0312     return 0;
0313 }
0314 
0315 /* -----------------------------------------------------------------------------
0316  * Plane Setup
0317  */
0318 
0319 #define RCAR_DU_COLORKEY_NONE       (0 << 24)
0320 #define RCAR_DU_COLORKEY_SOURCE     (1 << 24)
0321 #define RCAR_DU_COLORKEY_MASK       (1 << 24)
0322 
0323 static void rcar_du_plane_write(struct rcar_du_group *rgrp,
0324                 unsigned int index, u32 reg, u32 data)
0325 {
0326     rcar_du_write(rgrp->dev, rgrp->mmio_offset + index * PLANE_OFF + reg,
0327               data);
0328 }
0329 
0330 static void rcar_du_plane_setup_scanout(struct rcar_du_group *rgrp,
0331                     const struct rcar_du_plane_state *state)
0332 {
0333     unsigned int src_x = state->state.src.x1 >> 16;
0334     unsigned int src_y = state->state.src.y1 >> 16;
0335     unsigned int index = state->hwindex;
0336     unsigned int pitch;
0337     bool interlaced;
0338     u32 dma[2];
0339 
0340     interlaced = state->state.crtc->state->adjusted_mode.flags
0341            & DRM_MODE_FLAG_INTERLACE;
0342 
0343     if (state->source == RCAR_DU_PLANE_MEMORY) {
0344         struct drm_framebuffer *fb = state->state.fb;
0345         struct drm_gem_cma_object *gem;
0346         unsigned int i;
0347 
0348         if (state->format->planes == 2)
0349             pitch = fb->pitches[0];
0350         else
0351             pitch = fb->pitches[0] * 8 / state->format->bpp;
0352 
0353         for (i = 0; i < state->format->planes; ++i) {
0354             gem = drm_fb_cma_get_gem_obj(fb, i);
0355             dma[i] = gem->paddr + fb->offsets[i];
0356         }
0357     } else {
0358         pitch = drm_rect_width(&state->state.src) >> 16;
0359         dma[0] = 0;
0360         dma[1] = 0;
0361     }
0362 
0363     /*
0364      * Memory pitch (expressed in pixels). Must be doubled for interlaced
0365      * operation with 32bpp formats.
0366      */
0367     rcar_du_plane_write(rgrp, index, PnMWR,
0368                 (interlaced && state->format->bpp == 32) ?
0369                 pitch * 2 : pitch);
0370 
0371     /*
0372      * The Y position is expressed in raster line units and must be doubled
0373      * for 32bpp formats, according to the R8A7790 datasheet. No mention of
0374      * doubling the Y position is found in the R8A7779 datasheet, but the
0375      * rule seems to apply there as well.
0376      *
0377      * Despite not being documented, doubling seem not to be needed when
0378      * operating in interlaced mode.
0379      *
0380      * Similarly, for the second plane, NV12 and NV21 formats seem to
0381      * require a halved Y position value, in both progressive and interlaced
0382      * modes.
0383      */
0384     rcar_du_plane_write(rgrp, index, PnSPXR, src_x);
0385     rcar_du_plane_write(rgrp, index, PnSPYR, src_y *
0386                 (!interlaced && state->format->bpp == 32 ? 2 : 1));
0387 
0388     rcar_du_plane_write(rgrp, index, PnDSA0R, dma[0]);
0389 
0390     if (state->format->planes == 2) {
0391         index = (index + 1) % 8;
0392 
0393         rcar_du_plane_write(rgrp, index, PnMWR, pitch);
0394 
0395         rcar_du_plane_write(rgrp, index, PnSPXR, src_x);
0396         rcar_du_plane_write(rgrp, index, PnSPYR, src_y *
0397                     (state->format->bpp == 16 ? 2 : 1) / 2);
0398 
0399         rcar_du_plane_write(rgrp, index, PnDSA0R, dma[1]);
0400     }
0401 }
0402 
0403 static void rcar_du_plane_setup_mode(struct rcar_du_group *rgrp,
0404                      unsigned int index,
0405                      const struct rcar_du_plane_state *state)
0406 {
0407     u32 colorkey;
0408     u32 pnmr;
0409 
0410     /*
0411      * The PnALPHAR register controls alpha-blending in 16bpp formats
0412      * (ARGB1555 and XRGB1555).
0413      *
0414      * For ARGB, set the alpha value to 0, and enable alpha-blending when
0415      * the A bit is 0. This maps A=0 to alpha=0 and A=1 to alpha=255.
0416      *
0417      * For XRGB, set the alpha value to the plane-wide alpha value and
0418      * enable alpha-blending regardless of the X bit value.
0419      */
0420     if (state->format->fourcc != DRM_FORMAT_XRGB1555)
0421         rcar_du_plane_write(rgrp, index, PnALPHAR, PnALPHAR_ABIT_0);
0422     else
0423         rcar_du_plane_write(rgrp, index, PnALPHAR,
0424                     PnALPHAR_ABIT_X | state->state.alpha >> 8);
0425 
0426     pnmr = PnMR_BM_MD | state->format->pnmr;
0427 
0428     /*
0429      * Disable color keying when requested. YUV formats have the
0430      * PnMR_SPIM_TP_OFF bit set in their pnmr field, disabling color keying
0431      * automatically.
0432      */
0433     if ((state->colorkey & RCAR_DU_COLORKEY_MASK) == RCAR_DU_COLORKEY_NONE)
0434         pnmr |= PnMR_SPIM_TP_OFF;
0435 
0436     /* For packed YUV formats we need to select the U/V order. */
0437     if (state->format->fourcc == DRM_FORMAT_YUYV)
0438         pnmr |= PnMR_YCDF_YUYV;
0439 
0440     rcar_du_plane_write(rgrp, index, PnMR, pnmr);
0441 
0442     switch (state->format->fourcc) {
0443     case DRM_FORMAT_RGB565:
0444         colorkey = ((state->colorkey & 0xf80000) >> 8)
0445              | ((state->colorkey & 0x00fc00) >> 5)
0446              | ((state->colorkey & 0x0000f8) >> 3);
0447         rcar_du_plane_write(rgrp, index, PnTC2R, colorkey);
0448         break;
0449 
0450     case DRM_FORMAT_ARGB1555:
0451     case DRM_FORMAT_XRGB1555:
0452         colorkey = ((state->colorkey & 0xf80000) >> 9)
0453              | ((state->colorkey & 0x00f800) >> 6)
0454              | ((state->colorkey & 0x0000f8) >> 3);
0455         rcar_du_plane_write(rgrp, index, PnTC2R, colorkey);
0456         break;
0457 
0458     case DRM_FORMAT_XRGB8888:
0459     case DRM_FORMAT_ARGB8888:
0460         rcar_du_plane_write(rgrp, index, PnTC3R,
0461                     PnTC3R_CODE | (state->colorkey & 0xffffff));
0462         break;
0463     }
0464 }
0465 
0466 static void rcar_du_plane_setup_format_gen2(struct rcar_du_group *rgrp,
0467                         unsigned int index,
0468                         const struct rcar_du_plane_state *state)
0469 {
0470     u32 ddcr2 = PnDDCR2_CODE;
0471     u32 ddcr4;
0472 
0473     /*
0474      * Data format
0475      *
0476      * The data format is selected by the DDDF field in PnMR and the EDF
0477      * field in DDCR4.
0478      */
0479 
0480     rcar_du_plane_setup_mode(rgrp, index, state);
0481 
0482     if (state->format->planes == 2) {
0483         if (state->hwindex != index) {
0484             if (state->format->fourcc == DRM_FORMAT_NV12 ||
0485                 state->format->fourcc == DRM_FORMAT_NV21)
0486                 ddcr2 |= PnDDCR2_Y420;
0487 
0488             if (state->format->fourcc == DRM_FORMAT_NV21)
0489                 ddcr2 |= PnDDCR2_NV21;
0490 
0491             ddcr2 |= PnDDCR2_DIVU;
0492         } else {
0493             ddcr2 |= PnDDCR2_DIVY;
0494         }
0495     }
0496 
0497     rcar_du_plane_write(rgrp, index, PnDDCR2, ddcr2);
0498 
0499     ddcr4 = state->format->edf | PnDDCR4_CODE;
0500     if (state->source != RCAR_DU_PLANE_MEMORY)
0501         ddcr4 |= PnDDCR4_VSPS;
0502 
0503     rcar_du_plane_write(rgrp, index, PnDDCR4, ddcr4);
0504 }
0505 
0506 static void rcar_du_plane_setup_format_gen3(struct rcar_du_group *rgrp,
0507                         unsigned int index,
0508                         const struct rcar_du_plane_state *state)
0509 {
0510     rcar_du_plane_write(rgrp, index, PnMR,
0511                 PnMR_SPIM_TP_OFF | state->format->pnmr);
0512 
0513     rcar_du_plane_write(rgrp, index, PnDDCR4,
0514                 state->format->edf | PnDDCR4_CODE);
0515 
0516     /*
0517      * On Gen3, some DU channels have two planes, each being wired to a
0518      * separate VSPD instance. The DU can then blend two planes. While
0519      * this feature isn't used by the driver, issues related to alpha
0520      * blending (such as incorrect colors or planes being invisible) may
0521      * still occur if the PnALPHAR register has a stale value. Set the
0522      * register to 0 to avoid this.
0523      */
0524 
0525     /* TODO: Check if alpha-blending should be disabled in PnMR. */
0526     rcar_du_plane_write(rgrp, index, PnALPHAR, 0);
0527 }
0528 
0529 static void rcar_du_plane_setup_format(struct rcar_du_group *rgrp,
0530                        unsigned int index,
0531                        const struct rcar_du_plane_state *state)
0532 {
0533     struct rcar_du_device *rcdu = rgrp->dev;
0534     const struct drm_rect *dst = &state->state.dst;
0535 
0536     if (rcdu->info->gen < 3)
0537         rcar_du_plane_setup_format_gen2(rgrp, index, state);
0538     else
0539         rcar_du_plane_setup_format_gen3(rgrp, index, state);
0540 
0541     /* Destination position and size */
0542     rcar_du_plane_write(rgrp, index, PnDSXR, drm_rect_width(dst));
0543     rcar_du_plane_write(rgrp, index, PnDSYR, drm_rect_height(dst));
0544     rcar_du_plane_write(rgrp, index, PnDPXR, dst->x1);
0545     rcar_du_plane_write(rgrp, index, PnDPYR, dst->y1);
0546 
0547     if (rcdu->info->gen < 3) {
0548         /* Wrap-around and blinking, disabled */
0549         rcar_du_plane_write(rgrp, index, PnWASPR, 0);
0550         rcar_du_plane_write(rgrp, index, PnWAMWR, 4095);
0551         rcar_du_plane_write(rgrp, index, PnBTR, 0);
0552         rcar_du_plane_write(rgrp, index, PnMLR, 0);
0553     }
0554 }
0555 
0556 void __rcar_du_plane_setup(struct rcar_du_group *rgrp,
0557                const struct rcar_du_plane_state *state)
0558 {
0559     struct rcar_du_device *rcdu = rgrp->dev;
0560 
0561     rcar_du_plane_setup_format(rgrp, state->hwindex, state);
0562     if (state->format->planes == 2)
0563         rcar_du_plane_setup_format(rgrp, (state->hwindex + 1) % 8,
0564                        state);
0565 
0566     if (rcdu->info->gen >= 3)
0567         return;
0568 
0569     rcar_du_plane_setup_scanout(rgrp, state);
0570 
0571     if (state->source == RCAR_DU_PLANE_VSPD1) {
0572         unsigned int vspd1_sink = rgrp->index ? 2 : 0;
0573 
0574         if (rcdu->vspd1_sink != vspd1_sink) {
0575             rcdu->vspd1_sink = vspd1_sink;
0576             rcar_du_set_dpad0_vsp1_routing(rcdu);
0577 
0578             /*
0579              * Changes to the VSP1 sink take effect on DRES and thus
0580              * need a restart of the group.
0581              */
0582             rgrp->need_restart = true;
0583         }
0584     }
0585 }
0586 
0587 int __rcar_du_plane_atomic_check(struct drm_plane *plane,
0588                  struct drm_plane_state *state,
0589                  const struct rcar_du_format_info **format)
0590 {
0591     struct drm_device *dev = plane->dev;
0592     struct drm_crtc_state *crtc_state;
0593     int ret;
0594 
0595     if (!state->crtc) {
0596         /*
0597          * The visible field is not reset by the DRM core but only
0598          * updated by drm_plane_helper_check_state(), set it manually.
0599          */
0600         state->visible = false;
0601         *format = NULL;
0602         return 0;
0603     }
0604 
0605     crtc_state = drm_atomic_get_crtc_state(state->state, state->crtc);
0606     if (IS_ERR(crtc_state))
0607         return PTR_ERR(crtc_state);
0608 
0609     ret = drm_atomic_helper_check_plane_state(state, crtc_state,
0610                           DRM_PLANE_HELPER_NO_SCALING,
0611                           DRM_PLANE_HELPER_NO_SCALING,
0612                           true, true);
0613     if (ret < 0)
0614         return ret;
0615 
0616     if (!state->visible) {
0617         *format = NULL;
0618         return 0;
0619     }
0620 
0621     *format = rcar_du_format_info(state->fb->format->format);
0622     if (*format == NULL) {
0623         dev_dbg(dev->dev, "%s: unsupported format %08x\n", __func__,
0624             state->fb->format->format);
0625         return -EINVAL;
0626     }
0627 
0628     return 0;
0629 }
0630 
0631 static int rcar_du_plane_atomic_check(struct drm_plane *plane,
0632                       struct drm_atomic_state *state)
0633 {
0634     struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state,
0635                                          plane);
0636     struct rcar_du_plane_state *rstate = to_rcar_plane_state(new_plane_state);
0637 
0638     return __rcar_du_plane_atomic_check(plane, new_plane_state,
0639                         &rstate->format);
0640 }
0641 
0642 static void rcar_du_plane_atomic_update(struct drm_plane *plane,
0643                     struct drm_atomic_state *state)
0644 {
0645     struct drm_plane_state *old_state = drm_atomic_get_old_plane_state(state, plane);
0646     struct drm_plane_state *new_state = drm_atomic_get_new_plane_state(state, plane);
0647     struct rcar_du_plane *rplane = to_rcar_plane(plane);
0648     struct rcar_du_plane_state *old_rstate;
0649     struct rcar_du_plane_state *new_rstate;
0650 
0651     if (!new_state->visible)
0652         return;
0653 
0654     rcar_du_plane_setup(rplane);
0655 
0656     /*
0657      * Check whether the source has changed from memory to live source or
0658      * from live source to memory. The source has been configured by the
0659      * VSPS bit in the PnDDCR4 register. Although the datasheet states that
0660      * the bit is updated during vertical blanking, it seems that updates
0661      * only occur when the DU group is held in reset through the DSYSR.DRES
0662      * bit. We thus need to restart the group if the source changes.
0663      */
0664     old_rstate = to_rcar_plane_state(old_state);
0665     new_rstate = to_rcar_plane_state(new_state);
0666 
0667     if ((old_rstate->source == RCAR_DU_PLANE_MEMORY) !=
0668         (new_rstate->source == RCAR_DU_PLANE_MEMORY))
0669         rplane->group->need_restart = true;
0670 }
0671 
0672 static const struct drm_plane_helper_funcs rcar_du_plane_helper_funcs = {
0673     .atomic_check = rcar_du_plane_atomic_check,
0674     .atomic_update = rcar_du_plane_atomic_update,
0675 };
0676 
0677 static struct drm_plane_state *
0678 rcar_du_plane_atomic_duplicate_state(struct drm_plane *plane)
0679 {
0680     struct rcar_du_plane_state *state;
0681     struct rcar_du_plane_state *copy;
0682 
0683     if (WARN_ON(!plane->state))
0684         return NULL;
0685 
0686     state = to_rcar_plane_state(plane->state);
0687     copy = kmemdup(state, sizeof(*state), GFP_KERNEL);
0688     if (copy == NULL)
0689         return NULL;
0690 
0691     __drm_atomic_helper_plane_duplicate_state(plane, &copy->state);
0692 
0693     return &copy->state;
0694 }
0695 
0696 static void rcar_du_plane_atomic_destroy_state(struct drm_plane *plane,
0697                            struct drm_plane_state *state)
0698 {
0699     __drm_atomic_helper_plane_destroy_state(state);
0700     kfree(to_rcar_plane_state(state));
0701 }
0702 
0703 static void rcar_du_plane_reset(struct drm_plane *plane)
0704 {
0705     struct rcar_du_plane_state *state;
0706 
0707     if (plane->state) {
0708         rcar_du_plane_atomic_destroy_state(plane, plane->state);
0709         plane->state = NULL;
0710     }
0711 
0712     state = kzalloc(sizeof(*state), GFP_KERNEL);
0713     if (state == NULL)
0714         return;
0715 
0716     __drm_atomic_helper_plane_reset(plane, &state->state);
0717 
0718     state->hwindex = -1;
0719     state->source = RCAR_DU_PLANE_MEMORY;
0720     state->colorkey = RCAR_DU_COLORKEY_NONE;
0721 }
0722 
0723 static int rcar_du_plane_atomic_set_property(struct drm_plane *plane,
0724                          struct drm_plane_state *state,
0725                          struct drm_property *property,
0726                          uint64_t val)
0727 {
0728     struct rcar_du_plane_state *rstate = to_rcar_plane_state(state);
0729     struct rcar_du_device *rcdu = to_rcar_plane(plane)->group->dev;
0730 
0731     if (property == rcdu->props.colorkey)
0732         rstate->colorkey = val;
0733     else
0734         return -EINVAL;
0735 
0736     return 0;
0737 }
0738 
0739 static int rcar_du_plane_atomic_get_property(struct drm_plane *plane,
0740     const struct drm_plane_state *state, struct drm_property *property,
0741     uint64_t *val)
0742 {
0743     const struct rcar_du_plane_state *rstate =
0744         container_of(state, const struct rcar_du_plane_state, state);
0745     struct rcar_du_device *rcdu = to_rcar_plane(plane)->group->dev;
0746 
0747     if (property == rcdu->props.colorkey)
0748         *val = rstate->colorkey;
0749     else
0750         return -EINVAL;
0751 
0752     return 0;
0753 }
0754 
0755 static const struct drm_plane_funcs rcar_du_plane_funcs = {
0756     .update_plane = drm_atomic_helper_update_plane,
0757     .disable_plane = drm_atomic_helper_disable_plane,
0758     .reset = rcar_du_plane_reset,
0759     .destroy = drm_plane_cleanup,
0760     .atomic_duplicate_state = rcar_du_plane_atomic_duplicate_state,
0761     .atomic_destroy_state = rcar_du_plane_atomic_destroy_state,
0762     .atomic_set_property = rcar_du_plane_atomic_set_property,
0763     .atomic_get_property = rcar_du_plane_atomic_get_property,
0764 };
0765 
0766 static const uint32_t formats[] = {
0767     DRM_FORMAT_RGB565,
0768     DRM_FORMAT_ARGB1555,
0769     DRM_FORMAT_XRGB1555,
0770     DRM_FORMAT_XRGB8888,
0771     DRM_FORMAT_ARGB8888,
0772     DRM_FORMAT_UYVY,
0773     DRM_FORMAT_YUYV,
0774     DRM_FORMAT_NV12,
0775     DRM_FORMAT_NV21,
0776     DRM_FORMAT_NV16,
0777 };
0778 
0779 int rcar_du_planes_init(struct rcar_du_group *rgrp)
0780 {
0781     struct rcar_du_device *rcdu = rgrp->dev;
0782     unsigned int crtcs;
0783     unsigned int i;
0784     int ret;
0785 
0786      /*
0787       * Create one primary plane per CRTC in this group and seven overlay
0788       * planes.
0789       */
0790     rgrp->num_planes = rgrp->num_crtcs + 7;
0791 
0792     crtcs = ((1 << rcdu->num_crtcs) - 1) & (3 << (2 * rgrp->index));
0793 
0794     for (i = 0; i < rgrp->num_planes; ++i) {
0795         enum drm_plane_type type = i < rgrp->num_crtcs
0796                      ? DRM_PLANE_TYPE_PRIMARY
0797                      : DRM_PLANE_TYPE_OVERLAY;
0798         struct rcar_du_plane *plane = &rgrp->planes[i];
0799 
0800         plane->group = rgrp;
0801 
0802         ret = drm_universal_plane_init(&rcdu->ddev, &plane->plane,
0803                            crtcs, &rcar_du_plane_funcs,
0804                            formats, ARRAY_SIZE(formats),
0805                            NULL, type, NULL);
0806         if (ret < 0)
0807             return ret;
0808 
0809         drm_plane_helper_add(&plane->plane,
0810                      &rcar_du_plane_helper_funcs);
0811 
0812         drm_plane_create_alpha_property(&plane->plane);
0813 
0814         if (type == DRM_PLANE_TYPE_PRIMARY) {
0815             drm_plane_create_zpos_immutable_property(&plane->plane,
0816                                  0);
0817         } else {
0818             drm_object_attach_property(&plane->plane.base,
0819                            rcdu->props.colorkey,
0820                            RCAR_DU_COLORKEY_NONE);
0821             drm_plane_create_zpos_property(&plane->plane, 1, 1, 7);
0822         }
0823     }
0824 
0825     return 0;
0826 }