18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * rcar_du_plane.c  --  R-Car Display Unit Planes
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (C) 2013-2015 Renesas Electronics Corporation
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
88c2ecf20Sopenharmony_ci */
98c2ecf20Sopenharmony_ci
108c2ecf20Sopenharmony_ci#include <drm/drm_atomic.h>
118c2ecf20Sopenharmony_ci#include <drm/drm_atomic_helper.h>
128c2ecf20Sopenharmony_ci#include <drm/drm_crtc.h>
138c2ecf20Sopenharmony_ci#include <drm/drm_device.h>
148c2ecf20Sopenharmony_ci#include <drm/drm_fb_cma_helper.h>
158c2ecf20Sopenharmony_ci#include <drm/drm_fourcc.h>
168c2ecf20Sopenharmony_ci#include <drm/drm_gem_cma_helper.h>
178c2ecf20Sopenharmony_ci#include <drm/drm_plane_helper.h>
188c2ecf20Sopenharmony_ci
198c2ecf20Sopenharmony_ci#include "rcar_du_drv.h"
208c2ecf20Sopenharmony_ci#include "rcar_du_group.h"
218c2ecf20Sopenharmony_ci#include "rcar_du_kms.h"
228c2ecf20Sopenharmony_ci#include "rcar_du_plane.h"
238c2ecf20Sopenharmony_ci#include "rcar_du_regs.h"
248c2ecf20Sopenharmony_ci
258c2ecf20Sopenharmony_ci/* -----------------------------------------------------------------------------
268c2ecf20Sopenharmony_ci * Atomic hardware plane allocator
278c2ecf20Sopenharmony_ci *
288c2ecf20Sopenharmony_ci * The hardware plane allocator is solely based on the atomic plane states
298c2ecf20Sopenharmony_ci * without keeping any external state to avoid races between .atomic_check()
308c2ecf20Sopenharmony_ci * and .atomic_commit().
318c2ecf20Sopenharmony_ci *
328c2ecf20Sopenharmony_ci * The core idea is to avoid using a free planes bitmask that would need to be
338c2ecf20Sopenharmony_ci * shared between check and commit handlers with a collective knowledge based on
348c2ecf20Sopenharmony_ci * the allocated hardware plane(s) for each KMS plane. The allocator then loops
358c2ecf20Sopenharmony_ci * over all plane states to compute the free planes bitmask, allocates hardware
368c2ecf20Sopenharmony_ci * planes based on that bitmask, and stores the result back in the plane states.
378c2ecf20Sopenharmony_ci *
388c2ecf20Sopenharmony_ci * For this to work we need to access the current state of planes not touched by
398c2ecf20Sopenharmony_ci * the atomic update. To ensure that it won't be modified, we need to lock all
408c2ecf20Sopenharmony_ci * planes using drm_atomic_get_plane_state(). This effectively serializes atomic
418c2ecf20Sopenharmony_ci * updates from .atomic_check() up to completion (when swapping the states if
428c2ecf20Sopenharmony_ci * the check step has succeeded) or rollback (when freeing the states if the
438c2ecf20Sopenharmony_ci * check step has failed).
448c2ecf20Sopenharmony_ci *
458c2ecf20Sopenharmony_ci * Allocation is performed in the .atomic_check() handler and applied
468c2ecf20Sopenharmony_ci * automatically when the core swaps the old and new states.
478c2ecf20Sopenharmony_ci */
488c2ecf20Sopenharmony_ci
498c2ecf20Sopenharmony_cistatic bool rcar_du_plane_needs_realloc(
508c2ecf20Sopenharmony_ci				const struct rcar_du_plane_state *old_state,
518c2ecf20Sopenharmony_ci				const struct rcar_du_plane_state *new_state)
528c2ecf20Sopenharmony_ci{
538c2ecf20Sopenharmony_ci	/*
548c2ecf20Sopenharmony_ci	 * Lowering the number of planes doesn't strictly require reallocation
558c2ecf20Sopenharmony_ci	 * as the extra hardware plane will be freed when committing, but doing
568c2ecf20Sopenharmony_ci	 * so could lead to more fragmentation.
578c2ecf20Sopenharmony_ci	 */
588c2ecf20Sopenharmony_ci	if (!old_state->format ||
598c2ecf20Sopenharmony_ci	    old_state->format->planes != new_state->format->planes)
608c2ecf20Sopenharmony_ci		return true;
618c2ecf20Sopenharmony_ci
628c2ecf20Sopenharmony_ci	/* Reallocate hardware planes if the source has changed. */
638c2ecf20Sopenharmony_ci	if (old_state->source != new_state->source)
648c2ecf20Sopenharmony_ci		return true;
658c2ecf20Sopenharmony_ci
668c2ecf20Sopenharmony_ci	return false;
678c2ecf20Sopenharmony_ci}
688c2ecf20Sopenharmony_ci
698c2ecf20Sopenharmony_cistatic unsigned int rcar_du_plane_hwmask(struct rcar_du_plane_state *state)
708c2ecf20Sopenharmony_ci{
718c2ecf20Sopenharmony_ci	unsigned int mask;
728c2ecf20Sopenharmony_ci
738c2ecf20Sopenharmony_ci	if (state->hwindex == -1)
748c2ecf20Sopenharmony_ci		return 0;
758c2ecf20Sopenharmony_ci
768c2ecf20Sopenharmony_ci	mask = 1 << state->hwindex;
778c2ecf20Sopenharmony_ci	if (state->format->planes == 2)
788c2ecf20Sopenharmony_ci		mask |= 1 << ((state->hwindex + 1) % 8);
798c2ecf20Sopenharmony_ci
808c2ecf20Sopenharmony_ci	return mask;
818c2ecf20Sopenharmony_ci}
828c2ecf20Sopenharmony_ci
838c2ecf20Sopenharmony_ci/*
848c2ecf20Sopenharmony_ci * The R8A7790 DU can source frames directly from the VSP1 devices VSPD0 and
858c2ecf20Sopenharmony_ci * VSPD1. VSPD0 feeds DU0/1 plane 0, and VSPD1 feeds either DU2 plane 0 or
868c2ecf20Sopenharmony_ci * DU0/1 plane 1.
878c2ecf20Sopenharmony_ci *
888c2ecf20Sopenharmony_ci * Allocate the correct fixed plane when sourcing frames from VSPD0 or VSPD1,
898c2ecf20Sopenharmony_ci * and allocate planes in reverse index order otherwise to ensure maximum
908c2ecf20Sopenharmony_ci * availability of planes 0 and 1.
918c2ecf20Sopenharmony_ci *
928c2ecf20Sopenharmony_ci * The caller is responsible for ensuring that the requested source is
938c2ecf20Sopenharmony_ci * compatible with the DU revision.
948c2ecf20Sopenharmony_ci */
958c2ecf20Sopenharmony_cistatic int rcar_du_plane_hwalloc(struct rcar_du_plane *plane,
968c2ecf20Sopenharmony_ci				 struct rcar_du_plane_state *state,
978c2ecf20Sopenharmony_ci				 unsigned int free)
988c2ecf20Sopenharmony_ci{
998c2ecf20Sopenharmony_ci	unsigned int num_planes = state->format->planes;
1008c2ecf20Sopenharmony_ci	int fixed = -1;
1018c2ecf20Sopenharmony_ci	int i;
1028c2ecf20Sopenharmony_ci
1038c2ecf20Sopenharmony_ci	if (state->source == RCAR_DU_PLANE_VSPD0) {
1048c2ecf20Sopenharmony_ci		/* VSPD0 feeds plane 0 on DU0/1. */
1058c2ecf20Sopenharmony_ci		if (plane->group->index != 0)
1068c2ecf20Sopenharmony_ci			return -EINVAL;
1078c2ecf20Sopenharmony_ci
1088c2ecf20Sopenharmony_ci		fixed = 0;
1098c2ecf20Sopenharmony_ci	} else if (state->source == RCAR_DU_PLANE_VSPD1) {
1108c2ecf20Sopenharmony_ci		/* VSPD1 feeds plane 1 on DU0/1 or plane 0 on DU2. */
1118c2ecf20Sopenharmony_ci		fixed = plane->group->index == 0 ? 1 : 0;
1128c2ecf20Sopenharmony_ci	}
1138c2ecf20Sopenharmony_ci
1148c2ecf20Sopenharmony_ci	if (fixed >= 0)
1158c2ecf20Sopenharmony_ci		return free & (1 << fixed) ? fixed : -EBUSY;
1168c2ecf20Sopenharmony_ci
1178c2ecf20Sopenharmony_ci	for (i = RCAR_DU_NUM_HW_PLANES - 1; i >= 0; --i) {
1188c2ecf20Sopenharmony_ci		if (!(free & (1 << i)))
1198c2ecf20Sopenharmony_ci			continue;
1208c2ecf20Sopenharmony_ci
1218c2ecf20Sopenharmony_ci		if (num_planes == 1 || free & (1 << ((i + 1) % 8)))
1228c2ecf20Sopenharmony_ci			break;
1238c2ecf20Sopenharmony_ci	}
1248c2ecf20Sopenharmony_ci
1258c2ecf20Sopenharmony_ci	return i < 0 ? -EBUSY : i;
1268c2ecf20Sopenharmony_ci}
1278c2ecf20Sopenharmony_ci
1288c2ecf20Sopenharmony_ciint rcar_du_atomic_check_planes(struct drm_device *dev,
1298c2ecf20Sopenharmony_ci				struct drm_atomic_state *state)
1308c2ecf20Sopenharmony_ci{
1318c2ecf20Sopenharmony_ci	struct rcar_du_device *rcdu = dev->dev_private;
1328c2ecf20Sopenharmony_ci	unsigned int group_freed_planes[RCAR_DU_MAX_GROUPS] = { 0, };
1338c2ecf20Sopenharmony_ci	unsigned int group_free_planes[RCAR_DU_MAX_GROUPS] = { 0, };
1348c2ecf20Sopenharmony_ci	bool needs_realloc = false;
1358c2ecf20Sopenharmony_ci	unsigned int groups = 0;
1368c2ecf20Sopenharmony_ci	unsigned int i;
1378c2ecf20Sopenharmony_ci	struct drm_plane *drm_plane;
1388c2ecf20Sopenharmony_ci	struct drm_plane_state *old_drm_plane_state;
1398c2ecf20Sopenharmony_ci	struct drm_plane_state *new_drm_plane_state;
1408c2ecf20Sopenharmony_ci
1418c2ecf20Sopenharmony_ci	/* Check if hardware planes need to be reallocated. */
1428c2ecf20Sopenharmony_ci	for_each_oldnew_plane_in_state(state, drm_plane, old_drm_plane_state,
1438c2ecf20Sopenharmony_ci				       new_drm_plane_state, i) {
1448c2ecf20Sopenharmony_ci		struct rcar_du_plane_state *old_plane_state;
1458c2ecf20Sopenharmony_ci		struct rcar_du_plane_state *new_plane_state;
1468c2ecf20Sopenharmony_ci		struct rcar_du_plane *plane;
1478c2ecf20Sopenharmony_ci		unsigned int index;
1488c2ecf20Sopenharmony_ci
1498c2ecf20Sopenharmony_ci		plane = to_rcar_plane(drm_plane);
1508c2ecf20Sopenharmony_ci		old_plane_state = to_rcar_plane_state(old_drm_plane_state);
1518c2ecf20Sopenharmony_ci		new_plane_state = to_rcar_plane_state(new_drm_plane_state);
1528c2ecf20Sopenharmony_ci
1538c2ecf20Sopenharmony_ci		dev_dbg(rcdu->dev, "%s: checking plane (%u,%tu)\n", __func__,
1548c2ecf20Sopenharmony_ci			plane->group->index, plane - plane->group->planes);
1558c2ecf20Sopenharmony_ci
1568c2ecf20Sopenharmony_ci		/*
1578c2ecf20Sopenharmony_ci		 * If the plane is being disabled we don't need to go through
1588c2ecf20Sopenharmony_ci		 * the full reallocation procedure. Just mark the hardware
1598c2ecf20Sopenharmony_ci		 * plane(s) as freed.
1608c2ecf20Sopenharmony_ci		 */
1618c2ecf20Sopenharmony_ci		if (!new_plane_state->format) {
1628c2ecf20Sopenharmony_ci			dev_dbg(rcdu->dev, "%s: plane is being disabled\n",
1638c2ecf20Sopenharmony_ci				__func__);
1648c2ecf20Sopenharmony_ci			index = plane - plane->group->planes;
1658c2ecf20Sopenharmony_ci			group_freed_planes[plane->group->index] |= 1 << index;
1668c2ecf20Sopenharmony_ci			new_plane_state->hwindex = -1;
1678c2ecf20Sopenharmony_ci			continue;
1688c2ecf20Sopenharmony_ci		}
1698c2ecf20Sopenharmony_ci
1708c2ecf20Sopenharmony_ci		/*
1718c2ecf20Sopenharmony_ci		 * If the plane needs to be reallocated mark it as such, and
1728c2ecf20Sopenharmony_ci		 * mark the hardware plane(s) as free.
1738c2ecf20Sopenharmony_ci		 */
1748c2ecf20Sopenharmony_ci		if (rcar_du_plane_needs_realloc(old_plane_state, new_plane_state)) {
1758c2ecf20Sopenharmony_ci			dev_dbg(rcdu->dev, "%s: plane needs reallocation\n",
1768c2ecf20Sopenharmony_ci				__func__);
1778c2ecf20Sopenharmony_ci			groups |= 1 << plane->group->index;
1788c2ecf20Sopenharmony_ci			needs_realloc = true;
1798c2ecf20Sopenharmony_ci
1808c2ecf20Sopenharmony_ci			index = plane - plane->group->planes;
1818c2ecf20Sopenharmony_ci			group_freed_planes[plane->group->index] |= 1 << index;
1828c2ecf20Sopenharmony_ci			new_plane_state->hwindex = -1;
1838c2ecf20Sopenharmony_ci		}
1848c2ecf20Sopenharmony_ci	}
1858c2ecf20Sopenharmony_ci
1868c2ecf20Sopenharmony_ci	if (!needs_realloc)
1878c2ecf20Sopenharmony_ci		return 0;
1888c2ecf20Sopenharmony_ci
1898c2ecf20Sopenharmony_ci	/*
1908c2ecf20Sopenharmony_ci	 * Grab all plane states for the groups that need reallocation to ensure
1918c2ecf20Sopenharmony_ci	 * locking and avoid racy updates. This serializes the update operation,
1928c2ecf20Sopenharmony_ci	 * but there's not much we can do about it as that's the hardware
1938c2ecf20Sopenharmony_ci	 * design.
1948c2ecf20Sopenharmony_ci	 *
1958c2ecf20Sopenharmony_ci	 * Compute the used planes mask for each group at the same time to avoid
1968c2ecf20Sopenharmony_ci	 * looping over the planes separately later.
1978c2ecf20Sopenharmony_ci	 */
1988c2ecf20Sopenharmony_ci	while (groups) {
1998c2ecf20Sopenharmony_ci		unsigned int index = ffs(groups) - 1;
2008c2ecf20Sopenharmony_ci		struct rcar_du_group *group = &rcdu->groups[index];
2018c2ecf20Sopenharmony_ci		unsigned int used_planes = 0;
2028c2ecf20Sopenharmony_ci
2038c2ecf20Sopenharmony_ci		dev_dbg(rcdu->dev, "%s: finding free planes for group %u\n",
2048c2ecf20Sopenharmony_ci			__func__, index);
2058c2ecf20Sopenharmony_ci
2068c2ecf20Sopenharmony_ci		for (i = 0; i < group->num_planes; ++i) {
2078c2ecf20Sopenharmony_ci			struct rcar_du_plane *plane = &group->planes[i];
2088c2ecf20Sopenharmony_ci			struct rcar_du_plane_state *new_plane_state;
2098c2ecf20Sopenharmony_ci			struct drm_plane_state *s;
2108c2ecf20Sopenharmony_ci
2118c2ecf20Sopenharmony_ci			s = drm_atomic_get_plane_state(state, &plane->plane);
2128c2ecf20Sopenharmony_ci			if (IS_ERR(s))
2138c2ecf20Sopenharmony_ci				return PTR_ERR(s);
2148c2ecf20Sopenharmony_ci
2158c2ecf20Sopenharmony_ci			/*
2168c2ecf20Sopenharmony_ci			 * If the plane has been freed in the above loop its
2178c2ecf20Sopenharmony_ci			 * hardware planes must not be added to the used planes
2188c2ecf20Sopenharmony_ci			 * bitmask. However, the current state doesn't reflect
2198c2ecf20Sopenharmony_ci			 * the free state yet, as we've modified the new state
2208c2ecf20Sopenharmony_ci			 * above. Use the local freed planes list to check for
2218c2ecf20Sopenharmony_ci			 * that condition instead.
2228c2ecf20Sopenharmony_ci			 */
2238c2ecf20Sopenharmony_ci			if (group_freed_planes[index] & (1 << i)) {
2248c2ecf20Sopenharmony_ci				dev_dbg(rcdu->dev,
2258c2ecf20Sopenharmony_ci					"%s: plane (%u,%tu) has been freed, skipping\n",
2268c2ecf20Sopenharmony_ci					__func__, plane->group->index,
2278c2ecf20Sopenharmony_ci					plane - plane->group->planes);
2288c2ecf20Sopenharmony_ci				continue;
2298c2ecf20Sopenharmony_ci			}
2308c2ecf20Sopenharmony_ci
2318c2ecf20Sopenharmony_ci			new_plane_state = to_rcar_plane_state(s);
2328c2ecf20Sopenharmony_ci			used_planes |= rcar_du_plane_hwmask(new_plane_state);
2338c2ecf20Sopenharmony_ci
2348c2ecf20Sopenharmony_ci			dev_dbg(rcdu->dev,
2358c2ecf20Sopenharmony_ci				"%s: plane (%u,%tu) uses %u hwplanes (index %d)\n",
2368c2ecf20Sopenharmony_ci				__func__, plane->group->index,
2378c2ecf20Sopenharmony_ci				plane - plane->group->planes,
2388c2ecf20Sopenharmony_ci				new_plane_state->format ?
2398c2ecf20Sopenharmony_ci				new_plane_state->format->planes : 0,
2408c2ecf20Sopenharmony_ci				new_plane_state->hwindex);
2418c2ecf20Sopenharmony_ci		}
2428c2ecf20Sopenharmony_ci
2438c2ecf20Sopenharmony_ci		group_free_planes[index] = 0xff & ~used_planes;
2448c2ecf20Sopenharmony_ci		groups &= ~(1 << index);
2458c2ecf20Sopenharmony_ci
2468c2ecf20Sopenharmony_ci		dev_dbg(rcdu->dev, "%s: group %u free planes mask 0x%02x\n",
2478c2ecf20Sopenharmony_ci			__func__, index, group_free_planes[index]);
2488c2ecf20Sopenharmony_ci	}
2498c2ecf20Sopenharmony_ci
2508c2ecf20Sopenharmony_ci	/* Reallocate hardware planes for each plane that needs it. */
2518c2ecf20Sopenharmony_ci	for_each_oldnew_plane_in_state(state, drm_plane, old_drm_plane_state,
2528c2ecf20Sopenharmony_ci				       new_drm_plane_state, i) {
2538c2ecf20Sopenharmony_ci		struct rcar_du_plane_state *old_plane_state;
2548c2ecf20Sopenharmony_ci		struct rcar_du_plane_state *new_plane_state;
2558c2ecf20Sopenharmony_ci		struct rcar_du_plane *plane;
2568c2ecf20Sopenharmony_ci		unsigned int crtc_planes;
2578c2ecf20Sopenharmony_ci		unsigned int free;
2588c2ecf20Sopenharmony_ci		int idx;
2598c2ecf20Sopenharmony_ci
2608c2ecf20Sopenharmony_ci		plane = to_rcar_plane(drm_plane);
2618c2ecf20Sopenharmony_ci		old_plane_state = to_rcar_plane_state(old_drm_plane_state);
2628c2ecf20Sopenharmony_ci		new_plane_state = to_rcar_plane_state(new_drm_plane_state);
2638c2ecf20Sopenharmony_ci
2648c2ecf20Sopenharmony_ci		dev_dbg(rcdu->dev, "%s: allocating plane (%u,%tu)\n", __func__,
2658c2ecf20Sopenharmony_ci			plane->group->index, plane - plane->group->planes);
2668c2ecf20Sopenharmony_ci
2678c2ecf20Sopenharmony_ci		/*
2688c2ecf20Sopenharmony_ci		 * Skip planes that are being disabled or don't need to be
2698c2ecf20Sopenharmony_ci		 * reallocated.
2708c2ecf20Sopenharmony_ci		 */
2718c2ecf20Sopenharmony_ci		if (!new_plane_state->format ||
2728c2ecf20Sopenharmony_ci		    !rcar_du_plane_needs_realloc(old_plane_state, new_plane_state))
2738c2ecf20Sopenharmony_ci			continue;
2748c2ecf20Sopenharmony_ci
2758c2ecf20Sopenharmony_ci		/*
2768c2ecf20Sopenharmony_ci		 * Try to allocate the plane from the free planes currently
2778c2ecf20Sopenharmony_ci		 * associated with the target CRTC to avoid restarting the CRTC
2788c2ecf20Sopenharmony_ci		 * group and thus minimize flicker. If it fails fall back to
2798c2ecf20Sopenharmony_ci		 * allocating from all free planes.
2808c2ecf20Sopenharmony_ci		 */
2818c2ecf20Sopenharmony_ci		crtc_planes = to_rcar_crtc(new_plane_state->state.crtc)->index % 2
2828c2ecf20Sopenharmony_ci			    ? plane->group->dptsr_planes
2838c2ecf20Sopenharmony_ci			    : ~plane->group->dptsr_planes;
2848c2ecf20Sopenharmony_ci		free = group_free_planes[plane->group->index];
2858c2ecf20Sopenharmony_ci
2868c2ecf20Sopenharmony_ci		idx = rcar_du_plane_hwalloc(plane, new_plane_state,
2878c2ecf20Sopenharmony_ci					    free & crtc_planes);
2888c2ecf20Sopenharmony_ci		if (idx < 0)
2898c2ecf20Sopenharmony_ci			idx = rcar_du_plane_hwalloc(plane, new_plane_state,
2908c2ecf20Sopenharmony_ci						    free);
2918c2ecf20Sopenharmony_ci		if (idx < 0) {
2928c2ecf20Sopenharmony_ci			dev_dbg(rcdu->dev, "%s: no available hardware plane\n",
2938c2ecf20Sopenharmony_ci				__func__);
2948c2ecf20Sopenharmony_ci			return idx;
2958c2ecf20Sopenharmony_ci		}
2968c2ecf20Sopenharmony_ci
2978c2ecf20Sopenharmony_ci		dev_dbg(rcdu->dev, "%s: allocated %u hwplanes (index %u)\n",
2988c2ecf20Sopenharmony_ci			__func__, new_plane_state->format->planes, idx);
2998c2ecf20Sopenharmony_ci
3008c2ecf20Sopenharmony_ci		new_plane_state->hwindex = idx;
3018c2ecf20Sopenharmony_ci
3028c2ecf20Sopenharmony_ci		group_free_planes[plane->group->index] &=
3038c2ecf20Sopenharmony_ci			~rcar_du_plane_hwmask(new_plane_state);
3048c2ecf20Sopenharmony_ci
3058c2ecf20Sopenharmony_ci		dev_dbg(rcdu->dev, "%s: group %u free planes mask 0x%02x\n",
3068c2ecf20Sopenharmony_ci			__func__, plane->group->index,
3078c2ecf20Sopenharmony_ci			group_free_planes[plane->group->index]);
3088c2ecf20Sopenharmony_ci	}
3098c2ecf20Sopenharmony_ci
3108c2ecf20Sopenharmony_ci	return 0;
3118c2ecf20Sopenharmony_ci}
3128c2ecf20Sopenharmony_ci
3138c2ecf20Sopenharmony_ci/* -----------------------------------------------------------------------------
3148c2ecf20Sopenharmony_ci * Plane Setup
3158c2ecf20Sopenharmony_ci */
3168c2ecf20Sopenharmony_ci
3178c2ecf20Sopenharmony_ci#define RCAR_DU_COLORKEY_NONE		(0 << 24)
3188c2ecf20Sopenharmony_ci#define RCAR_DU_COLORKEY_SOURCE		(1 << 24)
3198c2ecf20Sopenharmony_ci#define RCAR_DU_COLORKEY_MASK		(1 << 24)
3208c2ecf20Sopenharmony_ci
3218c2ecf20Sopenharmony_cistatic void rcar_du_plane_write(struct rcar_du_group *rgrp,
3228c2ecf20Sopenharmony_ci				unsigned int index, u32 reg, u32 data)
3238c2ecf20Sopenharmony_ci{
3248c2ecf20Sopenharmony_ci	rcar_du_write(rgrp->dev, rgrp->mmio_offset + index * PLANE_OFF + reg,
3258c2ecf20Sopenharmony_ci		      data);
3268c2ecf20Sopenharmony_ci}
3278c2ecf20Sopenharmony_ci
3288c2ecf20Sopenharmony_cistatic void rcar_du_plane_setup_scanout(struct rcar_du_group *rgrp,
3298c2ecf20Sopenharmony_ci					const struct rcar_du_plane_state *state)
3308c2ecf20Sopenharmony_ci{
3318c2ecf20Sopenharmony_ci	unsigned int src_x = state->state.src.x1 >> 16;
3328c2ecf20Sopenharmony_ci	unsigned int src_y = state->state.src.y1 >> 16;
3338c2ecf20Sopenharmony_ci	unsigned int index = state->hwindex;
3348c2ecf20Sopenharmony_ci	unsigned int pitch;
3358c2ecf20Sopenharmony_ci	bool interlaced;
3368c2ecf20Sopenharmony_ci	u32 dma[2];
3378c2ecf20Sopenharmony_ci
3388c2ecf20Sopenharmony_ci	interlaced = state->state.crtc->state->adjusted_mode.flags
3398c2ecf20Sopenharmony_ci		   & DRM_MODE_FLAG_INTERLACE;
3408c2ecf20Sopenharmony_ci
3418c2ecf20Sopenharmony_ci	if (state->source == RCAR_DU_PLANE_MEMORY) {
3428c2ecf20Sopenharmony_ci		struct drm_framebuffer *fb = state->state.fb;
3438c2ecf20Sopenharmony_ci		struct drm_gem_cma_object *gem;
3448c2ecf20Sopenharmony_ci		unsigned int i;
3458c2ecf20Sopenharmony_ci
3468c2ecf20Sopenharmony_ci		if (state->format->planes == 2)
3478c2ecf20Sopenharmony_ci			pitch = fb->pitches[0];
3488c2ecf20Sopenharmony_ci		else
3498c2ecf20Sopenharmony_ci			pitch = fb->pitches[0] * 8 / state->format->bpp;
3508c2ecf20Sopenharmony_ci
3518c2ecf20Sopenharmony_ci		for (i = 0; i < state->format->planes; ++i) {
3528c2ecf20Sopenharmony_ci			gem = drm_fb_cma_get_gem_obj(fb, i);
3538c2ecf20Sopenharmony_ci			dma[i] = gem->paddr + fb->offsets[i];
3548c2ecf20Sopenharmony_ci		}
3558c2ecf20Sopenharmony_ci	} else {
3568c2ecf20Sopenharmony_ci		pitch = drm_rect_width(&state->state.src) >> 16;
3578c2ecf20Sopenharmony_ci		dma[0] = 0;
3588c2ecf20Sopenharmony_ci		dma[1] = 0;
3598c2ecf20Sopenharmony_ci	}
3608c2ecf20Sopenharmony_ci
3618c2ecf20Sopenharmony_ci	/*
3628c2ecf20Sopenharmony_ci	 * Memory pitch (expressed in pixels). Must be doubled for interlaced
3638c2ecf20Sopenharmony_ci	 * operation with 32bpp formats.
3648c2ecf20Sopenharmony_ci	 */
3658c2ecf20Sopenharmony_ci	rcar_du_plane_write(rgrp, index, PnMWR,
3668c2ecf20Sopenharmony_ci			    (interlaced && state->format->bpp == 32) ?
3678c2ecf20Sopenharmony_ci			    pitch * 2 : pitch);
3688c2ecf20Sopenharmony_ci
3698c2ecf20Sopenharmony_ci	/*
3708c2ecf20Sopenharmony_ci	 * The Y position is expressed in raster line units and must be doubled
3718c2ecf20Sopenharmony_ci	 * for 32bpp formats, according to the R8A7790 datasheet. No mention of
3728c2ecf20Sopenharmony_ci	 * doubling the Y position is found in the R8A7779 datasheet, but the
3738c2ecf20Sopenharmony_ci	 * rule seems to apply there as well.
3748c2ecf20Sopenharmony_ci	 *
3758c2ecf20Sopenharmony_ci	 * Despite not being documented, doubling seem not to be needed when
3768c2ecf20Sopenharmony_ci	 * operating in interlaced mode.
3778c2ecf20Sopenharmony_ci	 *
3788c2ecf20Sopenharmony_ci	 * Similarly, for the second plane, NV12 and NV21 formats seem to
3798c2ecf20Sopenharmony_ci	 * require a halved Y position value, in both progressive and interlaced
3808c2ecf20Sopenharmony_ci	 * modes.
3818c2ecf20Sopenharmony_ci	 */
3828c2ecf20Sopenharmony_ci	rcar_du_plane_write(rgrp, index, PnSPXR, src_x);
3838c2ecf20Sopenharmony_ci	rcar_du_plane_write(rgrp, index, PnSPYR, src_y *
3848c2ecf20Sopenharmony_ci			    (!interlaced && state->format->bpp == 32 ? 2 : 1));
3858c2ecf20Sopenharmony_ci
3868c2ecf20Sopenharmony_ci	rcar_du_plane_write(rgrp, index, PnDSA0R, dma[0]);
3878c2ecf20Sopenharmony_ci
3888c2ecf20Sopenharmony_ci	if (state->format->planes == 2) {
3898c2ecf20Sopenharmony_ci		index = (index + 1) % 8;
3908c2ecf20Sopenharmony_ci
3918c2ecf20Sopenharmony_ci		rcar_du_plane_write(rgrp, index, PnMWR, pitch);
3928c2ecf20Sopenharmony_ci
3938c2ecf20Sopenharmony_ci		rcar_du_plane_write(rgrp, index, PnSPXR, src_x);
3948c2ecf20Sopenharmony_ci		rcar_du_plane_write(rgrp, index, PnSPYR, src_y *
3958c2ecf20Sopenharmony_ci				    (state->format->bpp == 16 ? 2 : 1) / 2);
3968c2ecf20Sopenharmony_ci
3978c2ecf20Sopenharmony_ci		rcar_du_plane_write(rgrp, index, PnDSA0R, dma[1]);
3988c2ecf20Sopenharmony_ci	}
3998c2ecf20Sopenharmony_ci}
4008c2ecf20Sopenharmony_ci
4018c2ecf20Sopenharmony_cistatic void rcar_du_plane_setup_mode(struct rcar_du_group *rgrp,
4028c2ecf20Sopenharmony_ci				     unsigned int index,
4038c2ecf20Sopenharmony_ci				     const struct rcar_du_plane_state *state)
4048c2ecf20Sopenharmony_ci{
4058c2ecf20Sopenharmony_ci	u32 colorkey;
4068c2ecf20Sopenharmony_ci	u32 pnmr;
4078c2ecf20Sopenharmony_ci
4088c2ecf20Sopenharmony_ci	/*
4098c2ecf20Sopenharmony_ci	 * The PnALPHAR register controls alpha-blending in 16bpp formats
4108c2ecf20Sopenharmony_ci	 * (ARGB1555 and XRGB1555).
4118c2ecf20Sopenharmony_ci	 *
4128c2ecf20Sopenharmony_ci	 * For ARGB, set the alpha value to 0, and enable alpha-blending when
4138c2ecf20Sopenharmony_ci	 * the A bit is 0. This maps A=0 to alpha=0 and A=1 to alpha=255.
4148c2ecf20Sopenharmony_ci	 *
4158c2ecf20Sopenharmony_ci	 * For XRGB, set the alpha value to the plane-wide alpha value and
4168c2ecf20Sopenharmony_ci	 * enable alpha-blending regardless of the X bit value.
4178c2ecf20Sopenharmony_ci	 */
4188c2ecf20Sopenharmony_ci	if (state->format->fourcc != DRM_FORMAT_XRGB1555)
4198c2ecf20Sopenharmony_ci		rcar_du_plane_write(rgrp, index, PnALPHAR, PnALPHAR_ABIT_0);
4208c2ecf20Sopenharmony_ci	else
4218c2ecf20Sopenharmony_ci		rcar_du_plane_write(rgrp, index, PnALPHAR,
4228c2ecf20Sopenharmony_ci				    PnALPHAR_ABIT_X | state->state.alpha >> 8);
4238c2ecf20Sopenharmony_ci
4248c2ecf20Sopenharmony_ci	pnmr = PnMR_BM_MD | state->format->pnmr;
4258c2ecf20Sopenharmony_ci
4268c2ecf20Sopenharmony_ci	/*
4278c2ecf20Sopenharmony_ci	 * Disable color keying when requested. YUV formats have the
4288c2ecf20Sopenharmony_ci	 * PnMR_SPIM_TP_OFF bit set in their pnmr field, disabling color keying
4298c2ecf20Sopenharmony_ci	 * automatically.
4308c2ecf20Sopenharmony_ci	 */
4318c2ecf20Sopenharmony_ci	if ((state->colorkey & RCAR_DU_COLORKEY_MASK) == RCAR_DU_COLORKEY_NONE)
4328c2ecf20Sopenharmony_ci		pnmr |= PnMR_SPIM_TP_OFF;
4338c2ecf20Sopenharmony_ci
4348c2ecf20Sopenharmony_ci	/* For packed YUV formats we need to select the U/V order. */
4358c2ecf20Sopenharmony_ci	if (state->format->fourcc == DRM_FORMAT_YUYV)
4368c2ecf20Sopenharmony_ci		pnmr |= PnMR_YCDF_YUYV;
4378c2ecf20Sopenharmony_ci
4388c2ecf20Sopenharmony_ci	rcar_du_plane_write(rgrp, index, PnMR, pnmr);
4398c2ecf20Sopenharmony_ci
4408c2ecf20Sopenharmony_ci	switch (state->format->fourcc) {
4418c2ecf20Sopenharmony_ci	case DRM_FORMAT_RGB565:
4428c2ecf20Sopenharmony_ci		colorkey = ((state->colorkey & 0xf80000) >> 8)
4438c2ecf20Sopenharmony_ci			 | ((state->colorkey & 0x00fc00) >> 5)
4448c2ecf20Sopenharmony_ci			 | ((state->colorkey & 0x0000f8) >> 3);
4458c2ecf20Sopenharmony_ci		rcar_du_plane_write(rgrp, index, PnTC2R, colorkey);
4468c2ecf20Sopenharmony_ci		break;
4478c2ecf20Sopenharmony_ci
4488c2ecf20Sopenharmony_ci	case DRM_FORMAT_ARGB1555:
4498c2ecf20Sopenharmony_ci	case DRM_FORMAT_XRGB1555:
4508c2ecf20Sopenharmony_ci		colorkey = ((state->colorkey & 0xf80000) >> 9)
4518c2ecf20Sopenharmony_ci			 | ((state->colorkey & 0x00f800) >> 6)
4528c2ecf20Sopenharmony_ci			 | ((state->colorkey & 0x0000f8) >> 3);
4538c2ecf20Sopenharmony_ci		rcar_du_plane_write(rgrp, index, PnTC2R, colorkey);
4548c2ecf20Sopenharmony_ci		break;
4558c2ecf20Sopenharmony_ci
4568c2ecf20Sopenharmony_ci	case DRM_FORMAT_XRGB8888:
4578c2ecf20Sopenharmony_ci	case DRM_FORMAT_ARGB8888:
4588c2ecf20Sopenharmony_ci		rcar_du_plane_write(rgrp, index, PnTC3R,
4598c2ecf20Sopenharmony_ci				    PnTC3R_CODE | (state->colorkey & 0xffffff));
4608c2ecf20Sopenharmony_ci		break;
4618c2ecf20Sopenharmony_ci	}
4628c2ecf20Sopenharmony_ci}
4638c2ecf20Sopenharmony_ci
4648c2ecf20Sopenharmony_cistatic void rcar_du_plane_setup_format_gen2(struct rcar_du_group *rgrp,
4658c2ecf20Sopenharmony_ci					    unsigned int index,
4668c2ecf20Sopenharmony_ci					    const struct rcar_du_plane_state *state)
4678c2ecf20Sopenharmony_ci{
4688c2ecf20Sopenharmony_ci	u32 ddcr2 = PnDDCR2_CODE;
4698c2ecf20Sopenharmony_ci	u32 ddcr4;
4708c2ecf20Sopenharmony_ci
4718c2ecf20Sopenharmony_ci	/*
4728c2ecf20Sopenharmony_ci	 * Data format
4738c2ecf20Sopenharmony_ci	 *
4748c2ecf20Sopenharmony_ci	 * The data format is selected by the DDDF field in PnMR and the EDF
4758c2ecf20Sopenharmony_ci	 * field in DDCR4.
4768c2ecf20Sopenharmony_ci	 */
4778c2ecf20Sopenharmony_ci
4788c2ecf20Sopenharmony_ci	rcar_du_plane_setup_mode(rgrp, index, state);
4798c2ecf20Sopenharmony_ci
4808c2ecf20Sopenharmony_ci	if (state->format->planes == 2) {
4818c2ecf20Sopenharmony_ci		if (state->hwindex != index) {
4828c2ecf20Sopenharmony_ci			if (state->format->fourcc == DRM_FORMAT_NV12 ||
4838c2ecf20Sopenharmony_ci			    state->format->fourcc == DRM_FORMAT_NV21)
4848c2ecf20Sopenharmony_ci				ddcr2 |= PnDDCR2_Y420;
4858c2ecf20Sopenharmony_ci
4868c2ecf20Sopenharmony_ci			if (state->format->fourcc == DRM_FORMAT_NV21)
4878c2ecf20Sopenharmony_ci				ddcr2 |= PnDDCR2_NV21;
4888c2ecf20Sopenharmony_ci
4898c2ecf20Sopenharmony_ci			ddcr2 |= PnDDCR2_DIVU;
4908c2ecf20Sopenharmony_ci		} else {
4918c2ecf20Sopenharmony_ci			ddcr2 |= PnDDCR2_DIVY;
4928c2ecf20Sopenharmony_ci		}
4938c2ecf20Sopenharmony_ci	}
4948c2ecf20Sopenharmony_ci
4958c2ecf20Sopenharmony_ci	rcar_du_plane_write(rgrp, index, PnDDCR2, ddcr2);
4968c2ecf20Sopenharmony_ci
4978c2ecf20Sopenharmony_ci	ddcr4 = state->format->edf | PnDDCR4_CODE;
4988c2ecf20Sopenharmony_ci	if (state->source != RCAR_DU_PLANE_MEMORY)
4998c2ecf20Sopenharmony_ci		ddcr4 |= PnDDCR4_VSPS;
5008c2ecf20Sopenharmony_ci
5018c2ecf20Sopenharmony_ci	rcar_du_plane_write(rgrp, index, PnDDCR4, ddcr4);
5028c2ecf20Sopenharmony_ci}
5038c2ecf20Sopenharmony_ci
5048c2ecf20Sopenharmony_cistatic void rcar_du_plane_setup_format_gen3(struct rcar_du_group *rgrp,
5058c2ecf20Sopenharmony_ci					    unsigned int index,
5068c2ecf20Sopenharmony_ci					    const struct rcar_du_plane_state *state)
5078c2ecf20Sopenharmony_ci{
5088c2ecf20Sopenharmony_ci	rcar_du_plane_write(rgrp, index, PnMR,
5098c2ecf20Sopenharmony_ci			    PnMR_SPIM_TP_OFF | state->format->pnmr);
5108c2ecf20Sopenharmony_ci
5118c2ecf20Sopenharmony_ci	rcar_du_plane_write(rgrp, index, PnDDCR4,
5128c2ecf20Sopenharmony_ci			    state->format->edf | PnDDCR4_CODE);
5138c2ecf20Sopenharmony_ci}
5148c2ecf20Sopenharmony_ci
5158c2ecf20Sopenharmony_cistatic void rcar_du_plane_setup_format(struct rcar_du_group *rgrp,
5168c2ecf20Sopenharmony_ci				       unsigned int index,
5178c2ecf20Sopenharmony_ci				       const struct rcar_du_plane_state *state)
5188c2ecf20Sopenharmony_ci{
5198c2ecf20Sopenharmony_ci	struct rcar_du_device *rcdu = rgrp->dev;
5208c2ecf20Sopenharmony_ci	const struct drm_rect *dst = &state->state.dst;
5218c2ecf20Sopenharmony_ci
5228c2ecf20Sopenharmony_ci	if (rcdu->info->gen < 3)
5238c2ecf20Sopenharmony_ci		rcar_du_plane_setup_format_gen2(rgrp, index, state);
5248c2ecf20Sopenharmony_ci	else
5258c2ecf20Sopenharmony_ci		rcar_du_plane_setup_format_gen3(rgrp, index, state);
5268c2ecf20Sopenharmony_ci
5278c2ecf20Sopenharmony_ci	/* Destination position and size */
5288c2ecf20Sopenharmony_ci	rcar_du_plane_write(rgrp, index, PnDSXR, drm_rect_width(dst));
5298c2ecf20Sopenharmony_ci	rcar_du_plane_write(rgrp, index, PnDSYR, drm_rect_height(dst));
5308c2ecf20Sopenharmony_ci	rcar_du_plane_write(rgrp, index, PnDPXR, dst->x1);
5318c2ecf20Sopenharmony_ci	rcar_du_plane_write(rgrp, index, PnDPYR, dst->y1);
5328c2ecf20Sopenharmony_ci
5338c2ecf20Sopenharmony_ci	if (rcdu->info->gen < 3) {
5348c2ecf20Sopenharmony_ci		/* Wrap-around and blinking, disabled */
5358c2ecf20Sopenharmony_ci		rcar_du_plane_write(rgrp, index, PnWASPR, 0);
5368c2ecf20Sopenharmony_ci		rcar_du_plane_write(rgrp, index, PnWAMWR, 4095);
5378c2ecf20Sopenharmony_ci		rcar_du_plane_write(rgrp, index, PnBTR, 0);
5388c2ecf20Sopenharmony_ci		rcar_du_plane_write(rgrp, index, PnMLR, 0);
5398c2ecf20Sopenharmony_ci	}
5408c2ecf20Sopenharmony_ci}
5418c2ecf20Sopenharmony_ci
5428c2ecf20Sopenharmony_civoid __rcar_du_plane_setup(struct rcar_du_group *rgrp,
5438c2ecf20Sopenharmony_ci			   const struct rcar_du_plane_state *state)
5448c2ecf20Sopenharmony_ci{
5458c2ecf20Sopenharmony_ci	struct rcar_du_device *rcdu = rgrp->dev;
5468c2ecf20Sopenharmony_ci
5478c2ecf20Sopenharmony_ci	rcar_du_plane_setup_format(rgrp, state->hwindex, state);
5488c2ecf20Sopenharmony_ci	if (state->format->planes == 2)
5498c2ecf20Sopenharmony_ci		rcar_du_plane_setup_format(rgrp, (state->hwindex + 1) % 8,
5508c2ecf20Sopenharmony_ci					   state);
5518c2ecf20Sopenharmony_ci
5528c2ecf20Sopenharmony_ci	if (rcdu->info->gen < 3)
5538c2ecf20Sopenharmony_ci		rcar_du_plane_setup_scanout(rgrp, state);
5548c2ecf20Sopenharmony_ci
5558c2ecf20Sopenharmony_ci	if (state->source == RCAR_DU_PLANE_VSPD1) {
5568c2ecf20Sopenharmony_ci		unsigned int vspd1_sink = rgrp->index ? 2 : 0;
5578c2ecf20Sopenharmony_ci
5588c2ecf20Sopenharmony_ci		if (rcdu->vspd1_sink != vspd1_sink) {
5598c2ecf20Sopenharmony_ci			rcdu->vspd1_sink = vspd1_sink;
5608c2ecf20Sopenharmony_ci			rcar_du_set_dpad0_vsp1_routing(rcdu);
5618c2ecf20Sopenharmony_ci		}
5628c2ecf20Sopenharmony_ci	}
5638c2ecf20Sopenharmony_ci}
5648c2ecf20Sopenharmony_ci
5658c2ecf20Sopenharmony_ciint __rcar_du_plane_atomic_check(struct drm_plane *plane,
5668c2ecf20Sopenharmony_ci				 struct drm_plane_state *state,
5678c2ecf20Sopenharmony_ci				 const struct rcar_du_format_info **format)
5688c2ecf20Sopenharmony_ci{
5698c2ecf20Sopenharmony_ci	struct drm_device *dev = plane->dev;
5708c2ecf20Sopenharmony_ci	struct drm_crtc_state *crtc_state;
5718c2ecf20Sopenharmony_ci	int ret;
5728c2ecf20Sopenharmony_ci
5738c2ecf20Sopenharmony_ci	if (!state->crtc) {
5748c2ecf20Sopenharmony_ci		/*
5758c2ecf20Sopenharmony_ci		 * The visible field is not reset by the DRM core but only
5768c2ecf20Sopenharmony_ci		 * updated by drm_plane_helper_check_state(), set it manually.
5778c2ecf20Sopenharmony_ci		 */
5788c2ecf20Sopenharmony_ci		state->visible = false;
5798c2ecf20Sopenharmony_ci		*format = NULL;
5808c2ecf20Sopenharmony_ci		return 0;
5818c2ecf20Sopenharmony_ci	}
5828c2ecf20Sopenharmony_ci
5838c2ecf20Sopenharmony_ci	crtc_state = drm_atomic_get_crtc_state(state->state, state->crtc);
5848c2ecf20Sopenharmony_ci	if (IS_ERR(crtc_state))
5858c2ecf20Sopenharmony_ci		return PTR_ERR(crtc_state);
5868c2ecf20Sopenharmony_ci
5878c2ecf20Sopenharmony_ci	ret = drm_atomic_helper_check_plane_state(state, crtc_state,
5888c2ecf20Sopenharmony_ci						  DRM_PLANE_HELPER_NO_SCALING,
5898c2ecf20Sopenharmony_ci						  DRM_PLANE_HELPER_NO_SCALING,
5908c2ecf20Sopenharmony_ci						  true, true);
5918c2ecf20Sopenharmony_ci	if (ret < 0)
5928c2ecf20Sopenharmony_ci		return ret;
5938c2ecf20Sopenharmony_ci
5948c2ecf20Sopenharmony_ci	if (!state->visible) {
5958c2ecf20Sopenharmony_ci		*format = NULL;
5968c2ecf20Sopenharmony_ci		return 0;
5978c2ecf20Sopenharmony_ci	}
5988c2ecf20Sopenharmony_ci
5998c2ecf20Sopenharmony_ci	*format = rcar_du_format_info(state->fb->format->format);
6008c2ecf20Sopenharmony_ci	if (*format == NULL) {
6018c2ecf20Sopenharmony_ci		dev_dbg(dev->dev, "%s: unsupported format %08x\n", __func__,
6028c2ecf20Sopenharmony_ci			state->fb->format->format);
6038c2ecf20Sopenharmony_ci		return -EINVAL;
6048c2ecf20Sopenharmony_ci	}
6058c2ecf20Sopenharmony_ci
6068c2ecf20Sopenharmony_ci	return 0;
6078c2ecf20Sopenharmony_ci}
6088c2ecf20Sopenharmony_ci
6098c2ecf20Sopenharmony_cistatic int rcar_du_plane_atomic_check(struct drm_plane *plane,
6108c2ecf20Sopenharmony_ci				      struct drm_plane_state *state)
6118c2ecf20Sopenharmony_ci{
6128c2ecf20Sopenharmony_ci	struct rcar_du_plane_state *rstate = to_rcar_plane_state(state);
6138c2ecf20Sopenharmony_ci
6148c2ecf20Sopenharmony_ci	return __rcar_du_plane_atomic_check(plane, state, &rstate->format);
6158c2ecf20Sopenharmony_ci}
6168c2ecf20Sopenharmony_ci
6178c2ecf20Sopenharmony_cistatic void rcar_du_plane_atomic_update(struct drm_plane *plane,
6188c2ecf20Sopenharmony_ci					struct drm_plane_state *old_state)
6198c2ecf20Sopenharmony_ci{
6208c2ecf20Sopenharmony_ci	struct rcar_du_plane *rplane = to_rcar_plane(plane);
6218c2ecf20Sopenharmony_ci	struct rcar_du_plane_state *old_rstate;
6228c2ecf20Sopenharmony_ci	struct rcar_du_plane_state *new_rstate;
6238c2ecf20Sopenharmony_ci
6248c2ecf20Sopenharmony_ci	if (!plane->state->visible)
6258c2ecf20Sopenharmony_ci		return;
6268c2ecf20Sopenharmony_ci
6278c2ecf20Sopenharmony_ci	rcar_du_plane_setup(rplane);
6288c2ecf20Sopenharmony_ci
6298c2ecf20Sopenharmony_ci	/*
6308c2ecf20Sopenharmony_ci	 * Check whether the source has changed from memory to live source or
6318c2ecf20Sopenharmony_ci	 * from live source to memory. The source has been configured by the
6328c2ecf20Sopenharmony_ci	 * VSPS bit in the PnDDCR4 register. Although the datasheet states that
6338c2ecf20Sopenharmony_ci	 * the bit is updated during vertical blanking, it seems that updates
6348c2ecf20Sopenharmony_ci	 * only occur when the DU group is held in reset through the DSYSR.DRES
6358c2ecf20Sopenharmony_ci	 * bit. We thus need to restart the group if the source changes.
6368c2ecf20Sopenharmony_ci	 */
6378c2ecf20Sopenharmony_ci	old_rstate = to_rcar_plane_state(old_state);
6388c2ecf20Sopenharmony_ci	new_rstate = to_rcar_plane_state(plane->state);
6398c2ecf20Sopenharmony_ci
6408c2ecf20Sopenharmony_ci	if ((old_rstate->source == RCAR_DU_PLANE_MEMORY) !=
6418c2ecf20Sopenharmony_ci	    (new_rstate->source == RCAR_DU_PLANE_MEMORY))
6428c2ecf20Sopenharmony_ci		rplane->group->need_restart = true;
6438c2ecf20Sopenharmony_ci}
6448c2ecf20Sopenharmony_ci
6458c2ecf20Sopenharmony_cistatic const struct drm_plane_helper_funcs rcar_du_plane_helper_funcs = {
6468c2ecf20Sopenharmony_ci	.atomic_check = rcar_du_plane_atomic_check,
6478c2ecf20Sopenharmony_ci	.atomic_update = rcar_du_plane_atomic_update,
6488c2ecf20Sopenharmony_ci};
6498c2ecf20Sopenharmony_ci
6508c2ecf20Sopenharmony_cistatic struct drm_plane_state *
6518c2ecf20Sopenharmony_circar_du_plane_atomic_duplicate_state(struct drm_plane *plane)
6528c2ecf20Sopenharmony_ci{
6538c2ecf20Sopenharmony_ci	struct rcar_du_plane_state *state;
6548c2ecf20Sopenharmony_ci	struct rcar_du_plane_state *copy;
6558c2ecf20Sopenharmony_ci
6568c2ecf20Sopenharmony_ci	if (WARN_ON(!plane->state))
6578c2ecf20Sopenharmony_ci		return NULL;
6588c2ecf20Sopenharmony_ci
6598c2ecf20Sopenharmony_ci	state = to_rcar_plane_state(plane->state);
6608c2ecf20Sopenharmony_ci	copy = kmemdup(state, sizeof(*state), GFP_KERNEL);
6618c2ecf20Sopenharmony_ci	if (copy == NULL)
6628c2ecf20Sopenharmony_ci		return NULL;
6638c2ecf20Sopenharmony_ci
6648c2ecf20Sopenharmony_ci	__drm_atomic_helper_plane_duplicate_state(plane, &copy->state);
6658c2ecf20Sopenharmony_ci
6668c2ecf20Sopenharmony_ci	return &copy->state;
6678c2ecf20Sopenharmony_ci}
6688c2ecf20Sopenharmony_ci
6698c2ecf20Sopenharmony_cistatic void rcar_du_plane_atomic_destroy_state(struct drm_plane *plane,
6708c2ecf20Sopenharmony_ci					       struct drm_plane_state *state)
6718c2ecf20Sopenharmony_ci{
6728c2ecf20Sopenharmony_ci	__drm_atomic_helper_plane_destroy_state(state);
6738c2ecf20Sopenharmony_ci	kfree(to_rcar_plane_state(state));
6748c2ecf20Sopenharmony_ci}
6758c2ecf20Sopenharmony_ci
6768c2ecf20Sopenharmony_cistatic void rcar_du_plane_reset(struct drm_plane *plane)
6778c2ecf20Sopenharmony_ci{
6788c2ecf20Sopenharmony_ci	struct rcar_du_plane_state *state;
6798c2ecf20Sopenharmony_ci
6808c2ecf20Sopenharmony_ci	if (plane->state) {
6818c2ecf20Sopenharmony_ci		rcar_du_plane_atomic_destroy_state(plane, plane->state);
6828c2ecf20Sopenharmony_ci		plane->state = NULL;
6838c2ecf20Sopenharmony_ci	}
6848c2ecf20Sopenharmony_ci
6858c2ecf20Sopenharmony_ci	state = kzalloc(sizeof(*state), GFP_KERNEL);
6868c2ecf20Sopenharmony_ci	if (state == NULL)
6878c2ecf20Sopenharmony_ci		return;
6888c2ecf20Sopenharmony_ci
6898c2ecf20Sopenharmony_ci	__drm_atomic_helper_plane_reset(plane, &state->state);
6908c2ecf20Sopenharmony_ci
6918c2ecf20Sopenharmony_ci	state->hwindex = -1;
6928c2ecf20Sopenharmony_ci	state->source = RCAR_DU_PLANE_MEMORY;
6938c2ecf20Sopenharmony_ci	state->colorkey = RCAR_DU_COLORKEY_NONE;
6948c2ecf20Sopenharmony_ci	state->state.zpos = plane->type == DRM_PLANE_TYPE_PRIMARY ? 0 : 1;
6958c2ecf20Sopenharmony_ci}
6968c2ecf20Sopenharmony_ci
6978c2ecf20Sopenharmony_cistatic int rcar_du_plane_atomic_set_property(struct drm_plane *plane,
6988c2ecf20Sopenharmony_ci					     struct drm_plane_state *state,
6998c2ecf20Sopenharmony_ci					     struct drm_property *property,
7008c2ecf20Sopenharmony_ci					     uint64_t val)
7018c2ecf20Sopenharmony_ci{
7028c2ecf20Sopenharmony_ci	struct rcar_du_plane_state *rstate = to_rcar_plane_state(state);
7038c2ecf20Sopenharmony_ci	struct rcar_du_device *rcdu = to_rcar_plane(plane)->group->dev;
7048c2ecf20Sopenharmony_ci
7058c2ecf20Sopenharmony_ci	if (property == rcdu->props.colorkey)
7068c2ecf20Sopenharmony_ci		rstate->colorkey = val;
7078c2ecf20Sopenharmony_ci	else
7088c2ecf20Sopenharmony_ci		return -EINVAL;
7098c2ecf20Sopenharmony_ci
7108c2ecf20Sopenharmony_ci	return 0;
7118c2ecf20Sopenharmony_ci}
7128c2ecf20Sopenharmony_ci
7138c2ecf20Sopenharmony_cistatic int rcar_du_plane_atomic_get_property(struct drm_plane *plane,
7148c2ecf20Sopenharmony_ci	const struct drm_plane_state *state, struct drm_property *property,
7158c2ecf20Sopenharmony_ci	uint64_t *val)
7168c2ecf20Sopenharmony_ci{
7178c2ecf20Sopenharmony_ci	const struct rcar_du_plane_state *rstate =
7188c2ecf20Sopenharmony_ci		container_of(state, const struct rcar_du_plane_state, state);
7198c2ecf20Sopenharmony_ci	struct rcar_du_device *rcdu = to_rcar_plane(plane)->group->dev;
7208c2ecf20Sopenharmony_ci
7218c2ecf20Sopenharmony_ci	if (property == rcdu->props.colorkey)
7228c2ecf20Sopenharmony_ci		*val = rstate->colorkey;
7238c2ecf20Sopenharmony_ci	else
7248c2ecf20Sopenharmony_ci		return -EINVAL;
7258c2ecf20Sopenharmony_ci
7268c2ecf20Sopenharmony_ci	return 0;
7278c2ecf20Sopenharmony_ci}
7288c2ecf20Sopenharmony_ci
7298c2ecf20Sopenharmony_cistatic const struct drm_plane_funcs rcar_du_plane_funcs = {
7308c2ecf20Sopenharmony_ci	.update_plane = drm_atomic_helper_update_plane,
7318c2ecf20Sopenharmony_ci	.disable_plane = drm_atomic_helper_disable_plane,
7328c2ecf20Sopenharmony_ci	.reset = rcar_du_plane_reset,
7338c2ecf20Sopenharmony_ci	.destroy = drm_plane_cleanup,
7348c2ecf20Sopenharmony_ci	.atomic_duplicate_state = rcar_du_plane_atomic_duplicate_state,
7358c2ecf20Sopenharmony_ci	.atomic_destroy_state = rcar_du_plane_atomic_destroy_state,
7368c2ecf20Sopenharmony_ci	.atomic_set_property = rcar_du_plane_atomic_set_property,
7378c2ecf20Sopenharmony_ci	.atomic_get_property = rcar_du_plane_atomic_get_property,
7388c2ecf20Sopenharmony_ci};
7398c2ecf20Sopenharmony_ci
7408c2ecf20Sopenharmony_cistatic const uint32_t formats[] = {
7418c2ecf20Sopenharmony_ci	DRM_FORMAT_RGB565,
7428c2ecf20Sopenharmony_ci	DRM_FORMAT_ARGB1555,
7438c2ecf20Sopenharmony_ci	DRM_FORMAT_XRGB1555,
7448c2ecf20Sopenharmony_ci	DRM_FORMAT_XRGB8888,
7458c2ecf20Sopenharmony_ci	DRM_FORMAT_ARGB8888,
7468c2ecf20Sopenharmony_ci	DRM_FORMAT_UYVY,
7478c2ecf20Sopenharmony_ci	DRM_FORMAT_YUYV,
7488c2ecf20Sopenharmony_ci	DRM_FORMAT_NV12,
7498c2ecf20Sopenharmony_ci	DRM_FORMAT_NV21,
7508c2ecf20Sopenharmony_ci	DRM_FORMAT_NV16,
7518c2ecf20Sopenharmony_ci};
7528c2ecf20Sopenharmony_ci
7538c2ecf20Sopenharmony_ciint rcar_du_planes_init(struct rcar_du_group *rgrp)
7548c2ecf20Sopenharmony_ci{
7558c2ecf20Sopenharmony_ci	struct rcar_du_device *rcdu = rgrp->dev;
7568c2ecf20Sopenharmony_ci	unsigned int crtcs;
7578c2ecf20Sopenharmony_ci	unsigned int i;
7588c2ecf20Sopenharmony_ci	int ret;
7598c2ecf20Sopenharmony_ci
7608c2ecf20Sopenharmony_ci	 /*
7618c2ecf20Sopenharmony_ci	  * Create one primary plane per CRTC in this group and seven overlay
7628c2ecf20Sopenharmony_ci	  * planes.
7638c2ecf20Sopenharmony_ci	  */
7648c2ecf20Sopenharmony_ci	rgrp->num_planes = rgrp->num_crtcs + 7;
7658c2ecf20Sopenharmony_ci
7668c2ecf20Sopenharmony_ci	crtcs = ((1 << rcdu->num_crtcs) - 1) & (3 << (2 * rgrp->index));
7678c2ecf20Sopenharmony_ci
7688c2ecf20Sopenharmony_ci	for (i = 0; i < rgrp->num_planes; ++i) {
7698c2ecf20Sopenharmony_ci		enum drm_plane_type type = i < rgrp->num_crtcs
7708c2ecf20Sopenharmony_ci					 ? DRM_PLANE_TYPE_PRIMARY
7718c2ecf20Sopenharmony_ci					 : DRM_PLANE_TYPE_OVERLAY;
7728c2ecf20Sopenharmony_ci		struct rcar_du_plane *plane = &rgrp->planes[i];
7738c2ecf20Sopenharmony_ci
7748c2ecf20Sopenharmony_ci		plane->group = rgrp;
7758c2ecf20Sopenharmony_ci
7768c2ecf20Sopenharmony_ci		ret = drm_universal_plane_init(rcdu->ddev, &plane->plane, crtcs,
7778c2ecf20Sopenharmony_ci					       &rcar_du_plane_funcs, formats,
7788c2ecf20Sopenharmony_ci					       ARRAY_SIZE(formats),
7798c2ecf20Sopenharmony_ci					       NULL, type, NULL);
7808c2ecf20Sopenharmony_ci		if (ret < 0)
7818c2ecf20Sopenharmony_ci			return ret;
7828c2ecf20Sopenharmony_ci
7838c2ecf20Sopenharmony_ci		drm_plane_helper_add(&plane->plane,
7848c2ecf20Sopenharmony_ci				     &rcar_du_plane_helper_funcs);
7858c2ecf20Sopenharmony_ci
7868c2ecf20Sopenharmony_ci		drm_plane_create_alpha_property(&plane->plane);
7878c2ecf20Sopenharmony_ci
7888c2ecf20Sopenharmony_ci		if (type == DRM_PLANE_TYPE_PRIMARY) {
7898c2ecf20Sopenharmony_ci			drm_plane_create_zpos_immutable_property(&plane->plane,
7908c2ecf20Sopenharmony_ci								 0);
7918c2ecf20Sopenharmony_ci		} else {
7928c2ecf20Sopenharmony_ci			drm_object_attach_property(&plane->plane.base,
7938c2ecf20Sopenharmony_ci						   rcdu->props.colorkey,
7948c2ecf20Sopenharmony_ci						   RCAR_DU_COLORKEY_NONE);
7958c2ecf20Sopenharmony_ci			drm_plane_create_zpos_property(&plane->plane, 1, 1, 7);
7968c2ecf20Sopenharmony_ci		}
7978c2ecf20Sopenharmony_ci	}
7988c2ecf20Sopenharmony_ci
7998c2ecf20Sopenharmony_ci	return 0;
8008c2ecf20Sopenharmony_ci}
801