xref: /openbmc/linux/drivers/gpu/drm/renesas/rcar-du/rcar_du_plane.c (revision c900529f3d9161bfde5cca0754f83b4d3c3e0220)
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, &copy->state);
69811696c5eSBiju Das 
69911696c5eSBiju Das 	return &copy->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