111696c5eSBiju Das // SPDX-License-Identifier: GPL-2.0+
211696c5eSBiju Das /*
311696c5eSBiju Das * R-Car Display Unit Planes
411696c5eSBiju Das *
511696c5eSBiju Das * Copyright (C) 2013-2015 Renesas Electronics Corporation
611696c5eSBiju Das *
711696c5eSBiju Das * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
811696c5eSBiju Das */
911696c5eSBiju Das
1011696c5eSBiju Das #include <drm/drm_atomic.h>
1111696c5eSBiju Das #include <drm/drm_atomic_helper.h>
1211696c5eSBiju Das #include <drm/drm_blend.h>
1311696c5eSBiju Das #include <drm/drm_crtc.h>
1411696c5eSBiju Das #include <drm/drm_device.h>
1511696c5eSBiju Das #include <drm/drm_fb_dma_helper.h>
1611696c5eSBiju Das #include <drm/drm_fourcc.h>
1711696c5eSBiju Das #include <drm/drm_framebuffer.h>
1811696c5eSBiju Das #include <drm/drm_gem_dma_helper.h>
1911696c5eSBiju Das
2011696c5eSBiju Das #include "rcar_du_drv.h"
2111696c5eSBiju Das #include "rcar_du_group.h"
2211696c5eSBiju Das #include "rcar_du_kms.h"
2311696c5eSBiju Das #include "rcar_du_plane.h"
2411696c5eSBiju Das #include "rcar_du_regs.h"
2511696c5eSBiju Das
2611696c5eSBiju Das /* -----------------------------------------------------------------------------
2711696c5eSBiju Das * Atomic hardware plane allocator
2811696c5eSBiju Das *
2911696c5eSBiju Das * The hardware plane allocator is solely based on the atomic plane states
3011696c5eSBiju Das * without keeping any external state to avoid races between .atomic_check()
3111696c5eSBiju Das * and .atomic_commit().
3211696c5eSBiju Das *
3311696c5eSBiju Das * The core idea is to avoid using a free planes bitmask that would need to be
3411696c5eSBiju Das * shared between check and commit handlers with a collective knowledge based on
3511696c5eSBiju Das * the allocated hardware plane(s) for each KMS plane. The allocator then loops
3611696c5eSBiju Das * over all plane states to compute the free planes bitmask, allocates hardware
3711696c5eSBiju Das * planes based on that bitmask, and stores the result back in the plane states.
3811696c5eSBiju Das *
3911696c5eSBiju Das * For this to work we need to access the current state of planes not touched by
4011696c5eSBiju Das * the atomic update. To ensure that it won't be modified, we need to lock all
4111696c5eSBiju Das * planes using drm_atomic_get_plane_state(). This effectively serializes atomic
4211696c5eSBiju Das * updates from .atomic_check() up to completion (when swapping the states if
4311696c5eSBiju Das * the check step has succeeded) or rollback (when freeing the states if the
4411696c5eSBiju Das * check step has failed).
4511696c5eSBiju Das *
4611696c5eSBiju Das * Allocation is performed in the .atomic_check() handler and applied
4711696c5eSBiju Das * automatically when the core swaps the old and new states.
4811696c5eSBiju Das */
4911696c5eSBiju Das
rcar_du_plane_needs_realloc(const struct rcar_du_plane_state * old_state,const struct rcar_du_plane_state * new_state)5011696c5eSBiju Das static bool rcar_du_plane_needs_realloc(
5111696c5eSBiju Das const struct rcar_du_plane_state *old_state,
5211696c5eSBiju Das const struct rcar_du_plane_state *new_state)
5311696c5eSBiju Das {
5411696c5eSBiju Das /*
5511696c5eSBiju Das * Lowering the number of planes doesn't strictly require reallocation
5611696c5eSBiju Das * as the extra hardware plane will be freed when committing, but doing
5711696c5eSBiju Das * so could lead to more fragmentation.
5811696c5eSBiju Das */
5911696c5eSBiju Das if (!old_state->format ||
6011696c5eSBiju Das old_state->format->planes != new_state->format->planes)
6111696c5eSBiju Das return true;
6211696c5eSBiju Das
6311696c5eSBiju Das /* Reallocate hardware planes if the source has changed. */
6411696c5eSBiju Das if (old_state->source != new_state->source)
6511696c5eSBiju Das return true;
6611696c5eSBiju Das
6711696c5eSBiju Das return false;
6811696c5eSBiju Das }
6911696c5eSBiju Das
rcar_du_plane_hwmask(struct rcar_du_plane_state * state)7011696c5eSBiju Das static unsigned int rcar_du_plane_hwmask(struct rcar_du_plane_state *state)
7111696c5eSBiju Das {
7211696c5eSBiju Das unsigned int mask;
7311696c5eSBiju Das
7411696c5eSBiju Das if (state->hwindex == -1)
7511696c5eSBiju Das return 0;
7611696c5eSBiju Das
7711696c5eSBiju Das mask = 1 << state->hwindex;
7811696c5eSBiju Das if (state->format->planes == 2)
7911696c5eSBiju Das mask |= 1 << ((state->hwindex + 1) % 8);
8011696c5eSBiju Das
8111696c5eSBiju Das return mask;
8211696c5eSBiju Das }
8311696c5eSBiju Das
8411696c5eSBiju Das /*
8511696c5eSBiju Das * The R8A7790 DU can source frames directly from the VSP1 devices VSPD0 and
8611696c5eSBiju Das * VSPD1. VSPD0 feeds DU0/1 plane 0, and VSPD1 feeds either DU2 plane 0 or
8711696c5eSBiju Das * DU0/1 plane 1.
8811696c5eSBiju Das *
8911696c5eSBiju Das * Allocate the correct fixed plane when sourcing frames from VSPD0 or VSPD1,
9011696c5eSBiju Das * and allocate planes in reverse index order otherwise to ensure maximum
9111696c5eSBiju Das * availability of planes 0 and 1.
9211696c5eSBiju Das *
9311696c5eSBiju Das * The caller is responsible for ensuring that the requested source is
9411696c5eSBiju Das * compatible with the DU revision.
9511696c5eSBiju Das */
rcar_du_plane_hwalloc(struct rcar_du_plane * plane,struct rcar_du_plane_state * state,unsigned int free)9611696c5eSBiju Das static int rcar_du_plane_hwalloc(struct rcar_du_plane *plane,
9711696c5eSBiju Das struct rcar_du_plane_state *state,
9811696c5eSBiju Das unsigned int free)
9911696c5eSBiju Das {
10011696c5eSBiju Das unsigned int num_planes = state->format->planes;
10111696c5eSBiju Das int fixed = -1;
10211696c5eSBiju Das int i;
10311696c5eSBiju Das
10411696c5eSBiju Das if (state->source == RCAR_DU_PLANE_VSPD0) {
10511696c5eSBiju Das /* VSPD0 feeds plane 0 on DU0/1. */
10611696c5eSBiju Das if (plane->group->index != 0)
10711696c5eSBiju Das return -EINVAL;
10811696c5eSBiju Das
10911696c5eSBiju Das fixed = 0;
11011696c5eSBiju Das } else if (state->source == RCAR_DU_PLANE_VSPD1) {
11111696c5eSBiju Das /* VSPD1 feeds plane 1 on DU0/1 or plane 0 on DU2. */
11211696c5eSBiju Das fixed = plane->group->index == 0 ? 1 : 0;
11311696c5eSBiju Das }
11411696c5eSBiju Das
11511696c5eSBiju Das if (fixed >= 0)
11611696c5eSBiju Das return free & (1 << fixed) ? fixed : -EBUSY;
11711696c5eSBiju Das
11811696c5eSBiju Das for (i = RCAR_DU_NUM_HW_PLANES - 1; i >= 0; --i) {
11911696c5eSBiju Das if (!(free & (1 << i)))
12011696c5eSBiju Das continue;
12111696c5eSBiju Das
12211696c5eSBiju Das if (num_planes == 1 || free & (1 << ((i + 1) % 8)))
12311696c5eSBiju Das break;
12411696c5eSBiju Das }
12511696c5eSBiju Das
12611696c5eSBiju Das return i < 0 ? -EBUSY : i;
12711696c5eSBiju Das }
12811696c5eSBiju Das
rcar_du_atomic_check_planes(struct drm_device * dev,struct drm_atomic_state * state)12911696c5eSBiju Das int rcar_du_atomic_check_planes(struct drm_device *dev,
13011696c5eSBiju Das struct drm_atomic_state *state)
13111696c5eSBiju Das {
13211696c5eSBiju Das struct rcar_du_device *rcdu = to_rcar_du_device(dev);
13311696c5eSBiju Das unsigned int group_freed_planes[RCAR_DU_MAX_GROUPS] = { 0, };
13411696c5eSBiju Das unsigned int group_free_planes[RCAR_DU_MAX_GROUPS] = { 0, };
13511696c5eSBiju Das bool needs_realloc = false;
13611696c5eSBiju Das unsigned int groups = 0;
13711696c5eSBiju Das unsigned int i;
13811696c5eSBiju Das struct drm_plane *drm_plane;
13911696c5eSBiju Das struct drm_plane_state *old_drm_plane_state;
14011696c5eSBiju Das struct drm_plane_state *new_drm_plane_state;
14111696c5eSBiju Das
14211696c5eSBiju Das /* Check if hardware planes need to be reallocated. */
14311696c5eSBiju Das for_each_oldnew_plane_in_state(state, drm_plane, old_drm_plane_state,
14411696c5eSBiju Das new_drm_plane_state, i) {
14511696c5eSBiju Das struct rcar_du_plane_state *old_plane_state;
14611696c5eSBiju Das struct rcar_du_plane_state *new_plane_state;
14711696c5eSBiju Das struct rcar_du_plane *plane;
14811696c5eSBiju Das unsigned int index;
14911696c5eSBiju Das
15011696c5eSBiju Das plane = to_rcar_plane(drm_plane);
15111696c5eSBiju Das old_plane_state = to_rcar_plane_state(old_drm_plane_state);
15211696c5eSBiju Das new_plane_state = to_rcar_plane_state(new_drm_plane_state);
15311696c5eSBiju Das
15411696c5eSBiju Das dev_dbg(rcdu->dev, "%s: checking plane (%u,%tu)\n", __func__,
15511696c5eSBiju Das plane->group->index, plane - plane->group->planes);
15611696c5eSBiju Das
15711696c5eSBiju Das /*
15811696c5eSBiju Das * If the plane is being disabled we don't need to go through
15911696c5eSBiju Das * the full reallocation procedure. Just mark the hardware
16011696c5eSBiju Das * plane(s) as freed.
16111696c5eSBiju Das */
16211696c5eSBiju Das if (!new_plane_state->format) {
16311696c5eSBiju Das dev_dbg(rcdu->dev, "%s: plane is being disabled\n",
16411696c5eSBiju Das __func__);
16511696c5eSBiju Das index = plane - plane->group->planes;
16611696c5eSBiju Das group_freed_planes[plane->group->index] |= 1 << index;
16711696c5eSBiju Das new_plane_state->hwindex = -1;
16811696c5eSBiju Das continue;
16911696c5eSBiju Das }
17011696c5eSBiju Das
17111696c5eSBiju Das /*
17211696c5eSBiju Das * If the plane needs to be reallocated mark it as such, and
17311696c5eSBiju Das * mark the hardware plane(s) as free.
17411696c5eSBiju Das */
17511696c5eSBiju Das if (rcar_du_plane_needs_realloc(old_plane_state, new_plane_state)) {
17611696c5eSBiju Das dev_dbg(rcdu->dev, "%s: plane needs reallocation\n",
17711696c5eSBiju Das __func__);
17811696c5eSBiju Das groups |= 1 << plane->group->index;
17911696c5eSBiju Das needs_realloc = true;
18011696c5eSBiju Das
18111696c5eSBiju Das index = plane - plane->group->planes;
18211696c5eSBiju Das group_freed_planes[plane->group->index] |= 1 << index;
18311696c5eSBiju Das new_plane_state->hwindex = -1;
18411696c5eSBiju Das }
18511696c5eSBiju Das }
18611696c5eSBiju Das
18711696c5eSBiju Das if (!needs_realloc)
18811696c5eSBiju Das return 0;
18911696c5eSBiju Das
19011696c5eSBiju Das /*
19111696c5eSBiju Das * Grab all plane states for the groups that need reallocation to ensure
19211696c5eSBiju Das * locking and avoid racy updates. This serializes the update operation,
19311696c5eSBiju Das * but there's not much we can do about it as that's the hardware
19411696c5eSBiju Das * design.
19511696c5eSBiju Das *
19611696c5eSBiju Das * Compute the used planes mask for each group at the same time to avoid
19711696c5eSBiju Das * looping over the planes separately later.
19811696c5eSBiju Das */
19911696c5eSBiju Das while (groups) {
20011696c5eSBiju Das unsigned int index = ffs(groups) - 1;
20111696c5eSBiju Das struct rcar_du_group *group = &rcdu->groups[index];
20211696c5eSBiju Das unsigned int used_planes = 0;
20311696c5eSBiju Das
20411696c5eSBiju Das dev_dbg(rcdu->dev, "%s: finding free planes for group %u\n",
20511696c5eSBiju Das __func__, index);
20611696c5eSBiju Das
20711696c5eSBiju Das for (i = 0; i < group->num_planes; ++i) {
20811696c5eSBiju Das struct rcar_du_plane *plane = &group->planes[i];
20911696c5eSBiju Das struct rcar_du_plane_state *new_plane_state;
21011696c5eSBiju Das struct drm_plane_state *s;
21111696c5eSBiju Das
21211696c5eSBiju Das s = drm_atomic_get_plane_state(state, &plane->plane);
21311696c5eSBiju Das if (IS_ERR(s))
21411696c5eSBiju Das return PTR_ERR(s);
21511696c5eSBiju Das
21611696c5eSBiju Das /*
21711696c5eSBiju Das * If the plane has been freed in the above loop its
21811696c5eSBiju Das * hardware planes must not be added to the used planes
21911696c5eSBiju Das * bitmask. However, the current state doesn't reflect
22011696c5eSBiju Das * the free state yet, as we've modified the new state
22111696c5eSBiju Das * above. Use the local freed planes list to check for
22211696c5eSBiju Das * that condition instead.
22311696c5eSBiju Das */
22411696c5eSBiju Das if (group_freed_planes[index] & (1 << i)) {
22511696c5eSBiju Das dev_dbg(rcdu->dev,
22611696c5eSBiju Das "%s: plane (%u,%tu) has been freed, skipping\n",
22711696c5eSBiju Das __func__, plane->group->index,
22811696c5eSBiju Das plane - plane->group->planes);
22911696c5eSBiju Das continue;
23011696c5eSBiju Das }
23111696c5eSBiju Das
23211696c5eSBiju Das new_plane_state = to_rcar_plane_state(s);
23311696c5eSBiju Das used_planes |= rcar_du_plane_hwmask(new_plane_state);
23411696c5eSBiju Das
23511696c5eSBiju Das dev_dbg(rcdu->dev,
23611696c5eSBiju Das "%s: plane (%u,%tu) uses %u hwplanes (index %d)\n",
23711696c5eSBiju Das __func__, plane->group->index,
23811696c5eSBiju Das plane - plane->group->planes,
23911696c5eSBiju Das new_plane_state->format ?
24011696c5eSBiju Das new_plane_state->format->planes : 0,
24111696c5eSBiju Das new_plane_state->hwindex);
24211696c5eSBiju Das }
24311696c5eSBiju Das
24411696c5eSBiju Das group_free_planes[index] = 0xff & ~used_planes;
24511696c5eSBiju Das groups &= ~(1 << index);
24611696c5eSBiju Das
24711696c5eSBiju Das dev_dbg(rcdu->dev, "%s: group %u free planes mask 0x%02x\n",
24811696c5eSBiju Das __func__, index, group_free_planes[index]);
24911696c5eSBiju Das }
25011696c5eSBiju Das
25111696c5eSBiju Das /* Reallocate hardware planes for each plane that needs it. */
25211696c5eSBiju Das for_each_oldnew_plane_in_state(state, drm_plane, old_drm_plane_state,
25311696c5eSBiju Das new_drm_plane_state, i) {
25411696c5eSBiju Das struct rcar_du_plane_state *old_plane_state;
25511696c5eSBiju Das struct rcar_du_plane_state *new_plane_state;
25611696c5eSBiju Das struct rcar_du_plane *plane;
25711696c5eSBiju Das unsigned int crtc_planes;
25811696c5eSBiju Das unsigned int free;
25911696c5eSBiju Das int idx;
26011696c5eSBiju Das
26111696c5eSBiju Das plane = to_rcar_plane(drm_plane);
26211696c5eSBiju Das old_plane_state = to_rcar_plane_state(old_drm_plane_state);
26311696c5eSBiju Das new_plane_state = to_rcar_plane_state(new_drm_plane_state);
26411696c5eSBiju Das
26511696c5eSBiju Das dev_dbg(rcdu->dev, "%s: allocating plane (%u,%tu)\n", __func__,
26611696c5eSBiju Das plane->group->index, plane - plane->group->planes);
26711696c5eSBiju Das
26811696c5eSBiju Das /*
26911696c5eSBiju Das * Skip planes that are being disabled or don't need to be
27011696c5eSBiju Das * reallocated.
27111696c5eSBiju Das */
27211696c5eSBiju Das if (!new_plane_state->format ||
27311696c5eSBiju Das !rcar_du_plane_needs_realloc(old_plane_state, new_plane_state))
27411696c5eSBiju Das continue;
27511696c5eSBiju Das
27611696c5eSBiju Das /*
27711696c5eSBiju Das * Try to allocate the plane from the free planes currently
27811696c5eSBiju Das * associated with the target CRTC to avoid restarting the CRTC
27911696c5eSBiju Das * group and thus minimize flicker. If it fails fall back to
28011696c5eSBiju Das * allocating from all free planes.
28111696c5eSBiju Das */
28211696c5eSBiju Das crtc_planes = to_rcar_crtc(new_plane_state->state.crtc)->index % 2
28311696c5eSBiju Das ? plane->group->dptsr_planes
28411696c5eSBiju Das : ~plane->group->dptsr_planes;
28511696c5eSBiju Das free = group_free_planes[plane->group->index];
28611696c5eSBiju Das
28711696c5eSBiju Das idx = rcar_du_plane_hwalloc(plane, new_plane_state,
28811696c5eSBiju Das free & crtc_planes);
28911696c5eSBiju Das if (idx < 0)
29011696c5eSBiju Das idx = rcar_du_plane_hwalloc(plane, new_plane_state,
29111696c5eSBiju Das free);
29211696c5eSBiju Das if (idx < 0) {
29311696c5eSBiju Das dev_dbg(rcdu->dev, "%s: no available hardware plane\n",
29411696c5eSBiju Das __func__);
29511696c5eSBiju Das return idx;
29611696c5eSBiju Das }
29711696c5eSBiju Das
29811696c5eSBiju Das dev_dbg(rcdu->dev, "%s: allocated %u hwplanes (index %u)\n",
29911696c5eSBiju Das __func__, new_plane_state->format->planes, idx);
30011696c5eSBiju Das
30111696c5eSBiju Das new_plane_state->hwindex = idx;
30211696c5eSBiju Das
30311696c5eSBiju Das group_free_planes[plane->group->index] &=
30411696c5eSBiju Das ~rcar_du_plane_hwmask(new_plane_state);
30511696c5eSBiju Das
30611696c5eSBiju Das dev_dbg(rcdu->dev, "%s: group %u free planes mask 0x%02x\n",
30711696c5eSBiju Das __func__, plane->group->index,
30811696c5eSBiju Das group_free_planes[plane->group->index]);
30911696c5eSBiju Das }
31011696c5eSBiju Das
31111696c5eSBiju Das return 0;
31211696c5eSBiju Das }
31311696c5eSBiju Das
31411696c5eSBiju Das /* -----------------------------------------------------------------------------
31511696c5eSBiju Das * Plane Setup
31611696c5eSBiju Das */
31711696c5eSBiju Das
31811696c5eSBiju Das #define RCAR_DU_COLORKEY_NONE (0 << 24)
31911696c5eSBiju Das #define RCAR_DU_COLORKEY_SOURCE (1 << 24)
32011696c5eSBiju Das #define RCAR_DU_COLORKEY_MASK (1 << 24)
32111696c5eSBiju Das
rcar_du_plane_write(struct rcar_du_group * rgrp,unsigned int index,u32 reg,u32 data)32211696c5eSBiju Das static void rcar_du_plane_write(struct rcar_du_group *rgrp,
32311696c5eSBiju Das unsigned int index, u32 reg, u32 data)
32411696c5eSBiju Das {
32511696c5eSBiju Das rcar_du_write(rgrp->dev, rgrp->mmio_offset + index * PLANE_OFF + reg,
32611696c5eSBiju Das data);
32711696c5eSBiju Das }
32811696c5eSBiju Das
rcar_du_plane_setup_scanout(struct rcar_du_group * rgrp,const struct rcar_du_plane_state * state)32911696c5eSBiju Das static void rcar_du_plane_setup_scanout(struct rcar_du_group *rgrp,
33011696c5eSBiju Das const struct rcar_du_plane_state *state)
33111696c5eSBiju Das {
33211696c5eSBiju Das unsigned int src_x = state->state.src.x1 >> 16;
33311696c5eSBiju Das unsigned int src_y = state->state.src.y1 >> 16;
33411696c5eSBiju Das unsigned int index = state->hwindex;
33511696c5eSBiju Das unsigned int pitch;
33611696c5eSBiju Das bool interlaced;
33711696c5eSBiju Das u32 dma[2];
33811696c5eSBiju Das
33911696c5eSBiju Das interlaced = state->state.crtc->state->adjusted_mode.flags
34011696c5eSBiju Das & DRM_MODE_FLAG_INTERLACE;
34111696c5eSBiju Das
34211696c5eSBiju Das if (state->source == RCAR_DU_PLANE_MEMORY) {
34311696c5eSBiju Das struct drm_framebuffer *fb = state->state.fb;
34411696c5eSBiju Das struct drm_gem_dma_object *gem;
34511696c5eSBiju Das unsigned int i;
34611696c5eSBiju Das
34711696c5eSBiju Das if (state->format->planes == 2)
34811696c5eSBiju Das pitch = fb->pitches[0];
34911696c5eSBiju Das else
35011696c5eSBiju Das pitch = fb->pitches[0] * 8 / state->format->bpp;
35111696c5eSBiju Das
35211696c5eSBiju Das for (i = 0; i < state->format->planes; ++i) {
35311696c5eSBiju Das gem = drm_fb_dma_get_gem_obj(fb, i);
35411696c5eSBiju Das dma[i] = gem->dma_addr + fb->offsets[i];
35511696c5eSBiju Das }
35611696c5eSBiju Das } else {
35711696c5eSBiju Das pitch = drm_rect_width(&state->state.src) >> 16;
35811696c5eSBiju Das dma[0] = 0;
35911696c5eSBiju Das dma[1] = 0;
36011696c5eSBiju Das }
36111696c5eSBiju Das
36211696c5eSBiju Das /*
36311696c5eSBiju Das * Memory pitch (expressed in pixels). Must be doubled for interlaced
36411696c5eSBiju Das * operation with 32bpp formats.
36511696c5eSBiju Das */
36611696c5eSBiju Das rcar_du_plane_write(rgrp, index, PnMWR,
36711696c5eSBiju Das (interlaced && state->format->bpp == 32) ?
36811696c5eSBiju Das pitch * 2 : pitch);
36911696c5eSBiju Das
37011696c5eSBiju Das /*
37111696c5eSBiju Das * The Y position is expressed in raster line units and must be doubled
37211696c5eSBiju Das * for 32bpp formats, according to the R8A7790 datasheet. No mention of
37311696c5eSBiju Das * doubling the Y position is found in the R8A7779 datasheet, but the
37411696c5eSBiju Das * rule seems to apply there as well.
37511696c5eSBiju Das *
37611696c5eSBiju Das * Despite not being documented, doubling seem not to be needed when
37711696c5eSBiju Das * operating in interlaced mode.
37811696c5eSBiju Das *
37911696c5eSBiju Das * Similarly, for the second plane, NV12 and NV21 formats seem to
38011696c5eSBiju Das * require a halved Y position value, in both progressive and interlaced
38111696c5eSBiju Das * modes.
38211696c5eSBiju Das */
38311696c5eSBiju Das rcar_du_plane_write(rgrp, index, PnSPXR, src_x);
38411696c5eSBiju Das rcar_du_plane_write(rgrp, index, PnSPYR, src_y *
38511696c5eSBiju Das (!interlaced && state->format->bpp == 32 ? 2 : 1));
38611696c5eSBiju Das
38711696c5eSBiju Das rcar_du_plane_write(rgrp, index, PnDSA0R, dma[0]);
38811696c5eSBiju Das
38911696c5eSBiju Das if (state->format->planes == 2) {
39011696c5eSBiju Das index = (index + 1) % 8;
39111696c5eSBiju Das
39211696c5eSBiju Das rcar_du_plane_write(rgrp, index, PnMWR, pitch);
39311696c5eSBiju Das
39411696c5eSBiju Das rcar_du_plane_write(rgrp, index, PnSPXR, src_x);
39511696c5eSBiju Das rcar_du_plane_write(rgrp, index, PnSPYR, src_y *
39611696c5eSBiju Das (state->format->bpp == 16 ? 2 : 1) / 2);
39711696c5eSBiju Das
39811696c5eSBiju Das rcar_du_plane_write(rgrp, index, PnDSA0R, dma[1]);
39911696c5eSBiju Das }
40011696c5eSBiju Das }
40111696c5eSBiju Das
rcar_du_plane_setup_mode(struct rcar_du_group * rgrp,unsigned int index,const struct rcar_du_plane_state * state)40211696c5eSBiju Das static void rcar_du_plane_setup_mode(struct rcar_du_group *rgrp,
40311696c5eSBiju Das unsigned int index,
40411696c5eSBiju Das const struct rcar_du_plane_state *state)
40511696c5eSBiju Das {
40611696c5eSBiju Das u32 colorkey;
40711696c5eSBiju Das u32 pnmr;
40811696c5eSBiju Das
40911696c5eSBiju Das /*
41011696c5eSBiju Das * The PnALPHAR register controls alpha-blending in 16bpp formats
41111696c5eSBiju Das * (ARGB1555 and XRGB1555).
41211696c5eSBiju Das *
41311696c5eSBiju Das * For ARGB, set the alpha value to 0, and enable alpha-blending when
41411696c5eSBiju Das * the A bit is 0. This maps A=0 to alpha=0 and A=1 to alpha=255.
41511696c5eSBiju Das *
41611696c5eSBiju Das * For XRGB, set the alpha value to the plane-wide alpha value and
41711696c5eSBiju Das * enable alpha-blending regardless of the X bit value.
41811696c5eSBiju Das */
41911696c5eSBiju Das if (state->format->fourcc != DRM_FORMAT_XRGB1555)
42011696c5eSBiju Das rcar_du_plane_write(rgrp, index, PnALPHAR, PnALPHAR_ABIT_0);
42111696c5eSBiju Das else
42211696c5eSBiju Das rcar_du_plane_write(rgrp, index, PnALPHAR,
42311696c5eSBiju Das PnALPHAR_ABIT_X | state->state.alpha >> 8);
42411696c5eSBiju Das
42511696c5eSBiju Das pnmr = PnMR_BM_MD | state->format->pnmr;
42611696c5eSBiju Das
42711696c5eSBiju Das /*
42811696c5eSBiju Das * Disable color keying when requested. YUV formats have the
42911696c5eSBiju Das * PnMR_SPIM_TP_OFF bit set in their pnmr field, disabling color keying
43011696c5eSBiju Das * automatically.
43111696c5eSBiju Das */
43211696c5eSBiju Das if ((state->colorkey & RCAR_DU_COLORKEY_MASK) == RCAR_DU_COLORKEY_NONE)
43311696c5eSBiju Das pnmr |= PnMR_SPIM_TP_OFF;
43411696c5eSBiju Das
43511696c5eSBiju Das /* For packed YUV formats we need to select the U/V order. */
43611696c5eSBiju Das if (state->format->fourcc == DRM_FORMAT_YUYV)
43711696c5eSBiju Das pnmr |= PnMR_YCDF_YUYV;
43811696c5eSBiju Das
43911696c5eSBiju Das rcar_du_plane_write(rgrp, index, PnMR, pnmr);
44011696c5eSBiju Das
44111696c5eSBiju Das switch (state->format->fourcc) {
44211696c5eSBiju Das case DRM_FORMAT_RGB565:
44311696c5eSBiju Das colorkey = ((state->colorkey & 0xf80000) >> 8)
44411696c5eSBiju Das | ((state->colorkey & 0x00fc00) >> 5)
44511696c5eSBiju Das | ((state->colorkey & 0x0000f8) >> 3);
44611696c5eSBiju Das rcar_du_plane_write(rgrp, index, PnTC2R, colorkey);
44711696c5eSBiju Das break;
44811696c5eSBiju Das
44911696c5eSBiju Das case DRM_FORMAT_ARGB1555:
45011696c5eSBiju Das case DRM_FORMAT_XRGB1555:
45111696c5eSBiju Das colorkey = ((state->colorkey & 0xf80000) >> 9)
45211696c5eSBiju Das | ((state->colorkey & 0x00f800) >> 6)
45311696c5eSBiju Das | ((state->colorkey & 0x0000f8) >> 3);
45411696c5eSBiju Das rcar_du_plane_write(rgrp, index, PnTC2R, colorkey);
45511696c5eSBiju Das break;
45611696c5eSBiju Das
45711696c5eSBiju Das case DRM_FORMAT_XRGB8888:
45811696c5eSBiju Das case DRM_FORMAT_ARGB8888:
45911696c5eSBiju Das rcar_du_plane_write(rgrp, index, PnTC3R,
46011696c5eSBiju Das PnTC3R_CODE | (state->colorkey & 0xffffff));
46111696c5eSBiju Das break;
46211696c5eSBiju Das }
46311696c5eSBiju Das }
46411696c5eSBiju Das
rcar_du_plane_setup_format_gen2(struct rcar_du_group * rgrp,unsigned int index,const struct rcar_du_plane_state * state)46511696c5eSBiju Das static void rcar_du_plane_setup_format_gen2(struct rcar_du_group *rgrp,
46611696c5eSBiju Das unsigned int index,
46711696c5eSBiju Das const struct rcar_du_plane_state *state)
46811696c5eSBiju Das {
46911696c5eSBiju Das u32 ddcr2 = PnDDCR2_CODE;
47011696c5eSBiju Das u32 ddcr4;
47111696c5eSBiju Das
47211696c5eSBiju Das /*
47311696c5eSBiju Das * Data format
47411696c5eSBiju Das *
47511696c5eSBiju Das * The data format is selected by the DDDF field in PnMR and the EDF
47611696c5eSBiju Das * field in DDCR4.
47711696c5eSBiju Das */
47811696c5eSBiju Das
47911696c5eSBiju Das rcar_du_plane_setup_mode(rgrp, index, state);
48011696c5eSBiju Das
48111696c5eSBiju Das if (state->format->planes == 2) {
48211696c5eSBiju Das if (state->hwindex != index) {
48311696c5eSBiju Das if (state->format->fourcc == DRM_FORMAT_NV12 ||
48411696c5eSBiju Das state->format->fourcc == DRM_FORMAT_NV21)
48511696c5eSBiju Das ddcr2 |= PnDDCR2_Y420;
48611696c5eSBiju Das
48711696c5eSBiju Das if (state->format->fourcc == DRM_FORMAT_NV21)
48811696c5eSBiju Das ddcr2 |= PnDDCR2_NV21;
48911696c5eSBiju Das
49011696c5eSBiju Das ddcr2 |= PnDDCR2_DIVU;
49111696c5eSBiju Das } else {
49211696c5eSBiju Das ddcr2 |= PnDDCR2_DIVY;
49311696c5eSBiju Das }
49411696c5eSBiju Das }
49511696c5eSBiju Das
49611696c5eSBiju Das rcar_du_plane_write(rgrp, index, PnDDCR2, ddcr2);
49711696c5eSBiju Das
49811696c5eSBiju Das ddcr4 = state->format->edf | PnDDCR4_CODE;
49911696c5eSBiju Das if (state->source != RCAR_DU_PLANE_MEMORY)
50011696c5eSBiju Das ddcr4 |= PnDDCR4_VSPS;
50111696c5eSBiju Das
50211696c5eSBiju Das rcar_du_plane_write(rgrp, index, PnDDCR4, ddcr4);
50311696c5eSBiju Das }
50411696c5eSBiju Das
rcar_du_plane_setup_format_gen3(struct rcar_du_group * rgrp,unsigned int index,const struct rcar_du_plane_state * state)50511696c5eSBiju Das static void rcar_du_plane_setup_format_gen3(struct rcar_du_group *rgrp,
50611696c5eSBiju Das unsigned int index,
50711696c5eSBiju Das const struct rcar_du_plane_state *state)
50811696c5eSBiju Das {
50911696c5eSBiju Das struct rcar_du_device *rcdu = rgrp->dev;
51011696c5eSBiju Das u32 pnmr = state->format->pnmr | PnMR_SPIM_TP_OFF;
51111696c5eSBiju Das
51211696c5eSBiju Das if (rcdu->info->features & RCAR_DU_FEATURE_NO_BLENDING) {
51311696c5eSBiju Das /* No blending. ALP and EOR are not supported. */
51411696c5eSBiju Das pnmr &= ~(PnMR_SPIM_ALP | PnMR_SPIM_EOR);
51511696c5eSBiju Das }
51611696c5eSBiju Das
51711696c5eSBiju Das rcar_du_plane_write(rgrp, index, PnMR, pnmr);
51811696c5eSBiju Das
51911696c5eSBiju Das rcar_du_plane_write(rgrp, index, PnDDCR4,
52011696c5eSBiju Das state->format->edf | PnDDCR4_CODE);
52111696c5eSBiju Das
52211696c5eSBiju Das /*
52311696c5eSBiju Das * On Gen3, some DU channels have two planes, each being wired to a
52411696c5eSBiju Das * separate VSPD instance. The DU can then blend two planes. While
52511696c5eSBiju Das * this feature isn't used by the driver, issues related to alpha
52611696c5eSBiju Das * blending (such as incorrect colors or planes being invisible) may
52711696c5eSBiju Das * still occur if the PnALPHAR register has a stale value. Set the
52811696c5eSBiju Das * register to 0 to avoid this.
52911696c5eSBiju Das */
53011696c5eSBiju Das
53111696c5eSBiju Das rcar_du_plane_write(rgrp, index, PnALPHAR, 0);
53211696c5eSBiju Das }
53311696c5eSBiju Das
rcar_du_plane_setup_format(struct rcar_du_group * rgrp,unsigned int index,const struct rcar_du_plane_state * state)53411696c5eSBiju Das static void rcar_du_plane_setup_format(struct rcar_du_group *rgrp,
53511696c5eSBiju Das unsigned int index,
53611696c5eSBiju Das const struct rcar_du_plane_state *state)
53711696c5eSBiju Das {
53811696c5eSBiju Das struct rcar_du_device *rcdu = rgrp->dev;
53911696c5eSBiju Das const struct drm_rect *dst = &state->state.dst;
54011696c5eSBiju Das
54111696c5eSBiju Das if (rcdu->info->gen < 3)
54211696c5eSBiju Das rcar_du_plane_setup_format_gen2(rgrp, index, state);
54311696c5eSBiju Das else
54411696c5eSBiju Das rcar_du_plane_setup_format_gen3(rgrp, index, state);
54511696c5eSBiju Das
54611696c5eSBiju Das /* Destination position and size */
54711696c5eSBiju Das rcar_du_plane_write(rgrp, index, PnDSXR, drm_rect_width(dst));
54811696c5eSBiju Das rcar_du_plane_write(rgrp, index, PnDSYR, drm_rect_height(dst));
54911696c5eSBiju Das rcar_du_plane_write(rgrp, index, PnDPXR, dst->x1);
55011696c5eSBiju Das rcar_du_plane_write(rgrp, index, PnDPYR, dst->y1);
55111696c5eSBiju Das
55211696c5eSBiju Das if (rcdu->info->gen < 3) {
55311696c5eSBiju Das /* Wrap-around and blinking, disabled */
55411696c5eSBiju Das rcar_du_plane_write(rgrp, index, PnWASPR, 0);
55511696c5eSBiju Das rcar_du_plane_write(rgrp, index, PnWAMWR, 4095);
55611696c5eSBiju Das rcar_du_plane_write(rgrp, index, PnBTR, 0);
55711696c5eSBiju Das rcar_du_plane_write(rgrp, index, PnMLR, 0);
55811696c5eSBiju Das }
55911696c5eSBiju Das }
56011696c5eSBiju Das
__rcar_du_plane_setup(struct rcar_du_group * rgrp,const struct rcar_du_plane_state * state)56111696c5eSBiju Das void __rcar_du_plane_setup(struct rcar_du_group *rgrp,
56211696c5eSBiju Das const struct rcar_du_plane_state *state)
56311696c5eSBiju Das {
56411696c5eSBiju Das struct rcar_du_device *rcdu = rgrp->dev;
56511696c5eSBiju Das
56611696c5eSBiju Das rcar_du_plane_setup_format(rgrp, state->hwindex, state);
56711696c5eSBiju Das if (state->format->planes == 2)
56811696c5eSBiju Das rcar_du_plane_setup_format(rgrp, (state->hwindex + 1) % 8,
56911696c5eSBiju Das state);
57011696c5eSBiju Das
57111696c5eSBiju Das if (rcdu->info->gen >= 3)
57211696c5eSBiju Das return;
57311696c5eSBiju Das
57411696c5eSBiju Das rcar_du_plane_setup_scanout(rgrp, state);
57511696c5eSBiju Das
57611696c5eSBiju Das if (state->source == RCAR_DU_PLANE_VSPD1) {
57711696c5eSBiju Das unsigned int vspd1_sink = rgrp->index ? 2 : 0;
57811696c5eSBiju Das
57911696c5eSBiju Das if (rcdu->vspd1_sink != vspd1_sink) {
58011696c5eSBiju Das rcdu->vspd1_sink = vspd1_sink;
58111696c5eSBiju Das rcar_du_set_dpad0_vsp1_routing(rcdu);
58211696c5eSBiju Das
58311696c5eSBiju Das /*
58411696c5eSBiju Das * Changes to the VSP1 sink take effect on DRES and thus
58511696c5eSBiju Das * need a restart of the group.
58611696c5eSBiju Das */
58711696c5eSBiju Das rgrp->need_restart = true;
58811696c5eSBiju Das }
58911696c5eSBiju Das }
59011696c5eSBiju Das }
59111696c5eSBiju Das
__rcar_du_plane_atomic_check(struct drm_plane * plane,struct drm_plane_state * state,const struct rcar_du_format_info ** format)59211696c5eSBiju Das int __rcar_du_plane_atomic_check(struct drm_plane *plane,
59311696c5eSBiju Das struct drm_plane_state *state,
59411696c5eSBiju Das const struct rcar_du_format_info **format)
59511696c5eSBiju Das {
59611696c5eSBiju Das struct drm_device *dev = plane->dev;
59711696c5eSBiju Das struct drm_crtc_state *crtc_state;
59811696c5eSBiju Das int ret;
59911696c5eSBiju Das
60011696c5eSBiju Das if (!state->crtc) {
60111696c5eSBiju Das /*
60211696c5eSBiju Das * The visible field is not reset by the DRM core but only
603*a0c64d15SGeert Uytterhoeven * updated by drm_atomic_helper_check_plane_state(), set it
604*a0c64d15SGeert Uytterhoeven * manually.
60511696c5eSBiju Das */
60611696c5eSBiju Das state->visible = false;
60711696c5eSBiju Das *format = NULL;
60811696c5eSBiju Das return 0;
60911696c5eSBiju Das }
61011696c5eSBiju Das
61111696c5eSBiju Das crtc_state = drm_atomic_get_crtc_state(state->state, state->crtc);
61211696c5eSBiju Das if (IS_ERR(crtc_state))
61311696c5eSBiju Das return PTR_ERR(crtc_state);
61411696c5eSBiju Das
61511696c5eSBiju Das ret = drm_atomic_helper_check_plane_state(state, crtc_state,
61611696c5eSBiju Das DRM_PLANE_NO_SCALING,
61711696c5eSBiju Das DRM_PLANE_NO_SCALING,
61811696c5eSBiju Das true, true);
61911696c5eSBiju Das if (ret < 0)
62011696c5eSBiju Das return ret;
62111696c5eSBiju Das
62211696c5eSBiju Das if (!state->visible) {
62311696c5eSBiju Das *format = NULL;
62411696c5eSBiju Das return 0;
62511696c5eSBiju Das }
62611696c5eSBiju Das
62711696c5eSBiju Das *format = rcar_du_format_info(state->fb->format->format);
62811696c5eSBiju Das if (*format == NULL) {
62911696c5eSBiju Das dev_dbg(dev->dev, "%s: unsupported format %p4cc\n", __func__,
63011696c5eSBiju Das &state->fb->format->format);
63111696c5eSBiju Das return -EINVAL;
63211696c5eSBiju Das }
63311696c5eSBiju Das
63411696c5eSBiju Das return 0;
63511696c5eSBiju Das }
63611696c5eSBiju Das
rcar_du_plane_atomic_check(struct drm_plane * plane,struct drm_atomic_state * state)63711696c5eSBiju Das static int rcar_du_plane_atomic_check(struct drm_plane *plane,
63811696c5eSBiju Das struct drm_atomic_state *state)
63911696c5eSBiju Das {
64011696c5eSBiju Das struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state,
64111696c5eSBiju Das plane);
64211696c5eSBiju Das struct rcar_du_plane_state *rstate = to_rcar_plane_state(new_plane_state);
64311696c5eSBiju Das
64411696c5eSBiju Das return __rcar_du_plane_atomic_check(plane, new_plane_state,
64511696c5eSBiju Das &rstate->format);
64611696c5eSBiju Das }
64711696c5eSBiju Das
rcar_du_plane_atomic_update(struct drm_plane * plane,struct drm_atomic_state * state)64811696c5eSBiju Das static void rcar_du_plane_atomic_update(struct drm_plane *plane,
64911696c5eSBiju Das struct drm_atomic_state *state)
65011696c5eSBiju Das {
65111696c5eSBiju Das struct drm_plane_state *old_state = drm_atomic_get_old_plane_state(state, plane);
65211696c5eSBiju Das struct drm_plane_state *new_state = drm_atomic_get_new_plane_state(state, plane);
65311696c5eSBiju Das struct rcar_du_plane *rplane = to_rcar_plane(plane);
65411696c5eSBiju Das struct rcar_du_plane_state *old_rstate;
65511696c5eSBiju Das struct rcar_du_plane_state *new_rstate;
65611696c5eSBiju Das
65711696c5eSBiju Das if (!new_state->visible)
65811696c5eSBiju Das return;
65911696c5eSBiju Das
66011696c5eSBiju Das rcar_du_plane_setup(rplane);
66111696c5eSBiju Das
66211696c5eSBiju Das /*
66311696c5eSBiju Das * Check whether the source has changed from memory to live source or
66411696c5eSBiju Das * from live source to memory. The source has been configured by the
66511696c5eSBiju Das * VSPS bit in the PnDDCR4 register. Although the datasheet states that
66611696c5eSBiju Das * the bit is updated during vertical blanking, it seems that updates
66711696c5eSBiju Das * only occur when the DU group is held in reset through the DSYSR.DRES
66811696c5eSBiju Das * bit. We thus need to restart the group if the source changes.
66911696c5eSBiju Das */
67011696c5eSBiju Das old_rstate = to_rcar_plane_state(old_state);
67111696c5eSBiju Das new_rstate = to_rcar_plane_state(new_state);
67211696c5eSBiju Das
67311696c5eSBiju Das if ((old_rstate->source == RCAR_DU_PLANE_MEMORY) !=
67411696c5eSBiju Das (new_rstate->source == RCAR_DU_PLANE_MEMORY))
67511696c5eSBiju Das rplane->group->need_restart = true;
67611696c5eSBiju Das }
67711696c5eSBiju Das
67811696c5eSBiju Das static const struct drm_plane_helper_funcs rcar_du_plane_helper_funcs = {
67911696c5eSBiju Das .atomic_check = rcar_du_plane_atomic_check,
68011696c5eSBiju Das .atomic_update = rcar_du_plane_atomic_update,
68111696c5eSBiju Das };
68211696c5eSBiju Das
68311696c5eSBiju Das static struct drm_plane_state *
rcar_du_plane_atomic_duplicate_state(struct drm_plane * plane)68411696c5eSBiju Das rcar_du_plane_atomic_duplicate_state(struct drm_plane *plane)
68511696c5eSBiju Das {
68611696c5eSBiju Das struct rcar_du_plane_state *state;
68711696c5eSBiju Das struct rcar_du_plane_state *copy;
68811696c5eSBiju Das
68911696c5eSBiju Das if (WARN_ON(!plane->state))
69011696c5eSBiju Das return NULL;
69111696c5eSBiju Das
69211696c5eSBiju Das state = to_rcar_plane_state(plane->state);
69311696c5eSBiju Das copy = kmemdup(state, sizeof(*state), GFP_KERNEL);
69411696c5eSBiju Das if (copy == NULL)
69511696c5eSBiju Das return NULL;
69611696c5eSBiju Das
69711696c5eSBiju Das __drm_atomic_helper_plane_duplicate_state(plane, ©->state);
69811696c5eSBiju Das
69911696c5eSBiju Das return ©->state;
70011696c5eSBiju Das }
70111696c5eSBiju Das
rcar_du_plane_atomic_destroy_state(struct drm_plane * plane,struct drm_plane_state * state)70211696c5eSBiju Das static void rcar_du_plane_atomic_destroy_state(struct drm_plane *plane,
70311696c5eSBiju Das struct drm_plane_state *state)
70411696c5eSBiju Das {
70511696c5eSBiju Das __drm_atomic_helper_plane_destroy_state(state);
70611696c5eSBiju Das kfree(to_rcar_plane_state(state));
70711696c5eSBiju Das }
70811696c5eSBiju Das
rcar_du_plane_reset(struct drm_plane * plane)70911696c5eSBiju Das static void rcar_du_plane_reset(struct drm_plane *plane)
71011696c5eSBiju Das {
71111696c5eSBiju Das struct rcar_du_plane_state *state;
71211696c5eSBiju Das
71311696c5eSBiju Das if (plane->state) {
71411696c5eSBiju Das rcar_du_plane_atomic_destroy_state(plane, plane->state);
71511696c5eSBiju Das plane->state = NULL;
71611696c5eSBiju Das }
71711696c5eSBiju Das
71811696c5eSBiju Das state = kzalloc(sizeof(*state), GFP_KERNEL);
71911696c5eSBiju Das if (state == NULL)
72011696c5eSBiju Das return;
72111696c5eSBiju Das
72211696c5eSBiju Das __drm_atomic_helper_plane_reset(plane, &state->state);
72311696c5eSBiju Das
72411696c5eSBiju Das state->hwindex = -1;
72511696c5eSBiju Das state->source = RCAR_DU_PLANE_MEMORY;
72611696c5eSBiju Das state->colorkey = RCAR_DU_COLORKEY_NONE;
72711696c5eSBiju Das }
72811696c5eSBiju Das
rcar_du_plane_atomic_set_property(struct drm_plane * plane,struct drm_plane_state * state,struct drm_property * property,uint64_t val)72911696c5eSBiju Das static int rcar_du_plane_atomic_set_property(struct drm_plane *plane,
73011696c5eSBiju Das struct drm_plane_state *state,
73111696c5eSBiju Das struct drm_property *property,
73211696c5eSBiju Das uint64_t val)
73311696c5eSBiju Das {
73411696c5eSBiju Das struct rcar_du_plane_state *rstate = to_rcar_plane_state(state);
73511696c5eSBiju Das struct rcar_du_device *rcdu = to_rcar_plane(plane)->group->dev;
73611696c5eSBiju Das
73711696c5eSBiju Das if (property == rcdu->props.colorkey)
73811696c5eSBiju Das rstate->colorkey = val;
73911696c5eSBiju Das else
74011696c5eSBiju Das return -EINVAL;
74111696c5eSBiju Das
74211696c5eSBiju Das return 0;
74311696c5eSBiju Das }
74411696c5eSBiju Das
rcar_du_plane_atomic_get_property(struct drm_plane * plane,const struct drm_plane_state * state,struct drm_property * property,uint64_t * val)74511696c5eSBiju Das static int rcar_du_plane_atomic_get_property(struct drm_plane *plane,
74611696c5eSBiju Das const struct drm_plane_state *state, struct drm_property *property,
74711696c5eSBiju Das uint64_t *val)
74811696c5eSBiju Das {
74911696c5eSBiju Das const struct rcar_du_plane_state *rstate =
75011696c5eSBiju Das container_of(state, const struct rcar_du_plane_state, state);
75111696c5eSBiju Das struct rcar_du_device *rcdu = to_rcar_plane(plane)->group->dev;
75211696c5eSBiju Das
75311696c5eSBiju Das if (property == rcdu->props.colorkey)
75411696c5eSBiju Das *val = rstate->colorkey;
75511696c5eSBiju Das else
75611696c5eSBiju Das return -EINVAL;
75711696c5eSBiju Das
75811696c5eSBiju Das return 0;
75911696c5eSBiju Das }
76011696c5eSBiju Das
76111696c5eSBiju Das static const struct drm_plane_funcs rcar_du_plane_funcs = {
76211696c5eSBiju Das .update_plane = drm_atomic_helper_update_plane,
76311696c5eSBiju Das .disable_plane = drm_atomic_helper_disable_plane,
76411696c5eSBiju Das .reset = rcar_du_plane_reset,
76511696c5eSBiju Das .destroy = drm_plane_cleanup,
76611696c5eSBiju Das .atomic_duplicate_state = rcar_du_plane_atomic_duplicate_state,
76711696c5eSBiju Das .atomic_destroy_state = rcar_du_plane_atomic_destroy_state,
76811696c5eSBiju Das .atomic_set_property = rcar_du_plane_atomic_set_property,
76911696c5eSBiju Das .atomic_get_property = rcar_du_plane_atomic_get_property,
77011696c5eSBiju Das };
77111696c5eSBiju Das
77211696c5eSBiju Das static const uint32_t formats[] = {
77311696c5eSBiju Das DRM_FORMAT_RGB565,
77411696c5eSBiju Das DRM_FORMAT_ARGB1555,
77511696c5eSBiju Das DRM_FORMAT_XRGB1555,
77611696c5eSBiju Das DRM_FORMAT_XRGB8888,
77711696c5eSBiju Das DRM_FORMAT_ARGB8888,
77811696c5eSBiju Das DRM_FORMAT_UYVY,
77911696c5eSBiju Das DRM_FORMAT_YUYV,
78011696c5eSBiju Das DRM_FORMAT_NV12,
78111696c5eSBiju Das DRM_FORMAT_NV21,
78211696c5eSBiju Das DRM_FORMAT_NV16,
78311696c5eSBiju Das };
78411696c5eSBiju Das
rcar_du_planes_init(struct rcar_du_group * rgrp)78511696c5eSBiju Das int rcar_du_planes_init(struct rcar_du_group *rgrp)
78611696c5eSBiju Das {
78711696c5eSBiju Das struct rcar_du_device *rcdu = rgrp->dev;
78811696c5eSBiju Das unsigned int crtcs;
78911696c5eSBiju Das unsigned int i;
79011696c5eSBiju Das int ret;
79111696c5eSBiju Das
79211696c5eSBiju Das /*
79311696c5eSBiju Das * Create one primary plane per CRTC in this group and seven overlay
79411696c5eSBiju Das * planes.
79511696c5eSBiju Das */
79611696c5eSBiju Das rgrp->num_planes = rgrp->num_crtcs + 7;
79711696c5eSBiju Das
79811696c5eSBiju Das crtcs = ((1 << rcdu->num_crtcs) - 1) & (3 << (2 * rgrp->index));
79911696c5eSBiju Das
80011696c5eSBiju Das for (i = 0; i < rgrp->num_planes; ++i) {
80111696c5eSBiju Das enum drm_plane_type type = i < rgrp->num_crtcs
80211696c5eSBiju Das ? DRM_PLANE_TYPE_PRIMARY
80311696c5eSBiju Das : DRM_PLANE_TYPE_OVERLAY;
80411696c5eSBiju Das struct rcar_du_plane *plane = &rgrp->planes[i];
80511696c5eSBiju Das
80611696c5eSBiju Das plane->group = rgrp;
80711696c5eSBiju Das
80811696c5eSBiju Das ret = drm_universal_plane_init(&rcdu->ddev, &plane->plane,
80911696c5eSBiju Das crtcs, &rcar_du_plane_funcs,
81011696c5eSBiju Das formats, ARRAY_SIZE(formats),
81111696c5eSBiju Das NULL, type, NULL);
81211696c5eSBiju Das if (ret < 0)
81311696c5eSBiju Das return ret;
81411696c5eSBiju Das
81511696c5eSBiju Das drm_plane_helper_add(&plane->plane,
81611696c5eSBiju Das &rcar_du_plane_helper_funcs);
81711696c5eSBiju Das
81811696c5eSBiju Das drm_plane_create_alpha_property(&plane->plane);
81911696c5eSBiju Das
82011696c5eSBiju Das if (type == DRM_PLANE_TYPE_PRIMARY) {
82111696c5eSBiju Das drm_plane_create_zpos_immutable_property(&plane->plane,
82211696c5eSBiju Das 0);
82311696c5eSBiju Das } else {
82411696c5eSBiju Das drm_object_attach_property(&plane->plane.base,
82511696c5eSBiju Das rcdu->props.colorkey,
82611696c5eSBiju Das RCAR_DU_COLORKEY_NONE);
82711696c5eSBiju Das drm_plane_create_zpos_property(&plane->plane, 1, 1, 7);
82811696c5eSBiju Das }
82911696c5eSBiju Das }
83011696c5eSBiju Das
83111696c5eSBiju Das return 0;
83211696c5eSBiju Das }
833