brintos

brintos / linux-shallow public Read only

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