rcar_du_plane.c 23.4 KB
Newer Older
1 2 3
/*
 * rcar_du_plane.c  --  R-Car Display Unit Planes
 *
4
 * Copyright (C) 2013-2015 Renesas Electronics Corporation
5 6 7 8 9 10 11 12 13 14
 *
 * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 */

#include <drm/drmP.h>
15
#include <drm/drm_atomic.h>
16
#include <drm/drm_atomic_helper.h>
17 18 19 20
#include <drm/drm_crtc.h>
#include <drm/drm_crtc_helper.h>
#include <drm/drm_fb_cma_helper.h>
#include <drm/drm_gem_cma_helper.h>
21
#include <drm/drm_plane_helper.h>
22 23

#include "rcar_du_drv.h"
24
#include "rcar_du_group.h"
25 26 27 28
#include "rcar_du_kms.h"
#include "rcar_du_plane.h"
#include "rcar_du_regs.h"

29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52
/* -----------------------------------------------------------------------------
 * Atomic hardware plane allocator
 *
 * The hardware plane allocator is solely based on the atomic plane states
 * without keeping any external state to avoid races between .atomic_check()
 * and .atomic_commit().
 *
 * The core idea is to avoid using a free planes bitmask that would need to be
 * shared between check and commit handlers with a collective knowledge based on
 * the allocated hardware plane(s) for each KMS plane. The allocator then loops
 * over all plane states to compute the free planes bitmask, allocates hardware
 * planes based on that bitmask, and stores the result back in the plane states.
 *
 * For this to work we need to access the current state of planes not touched by
 * the atomic update. To ensure that it won't be modified, we need to lock all
 * planes using drm_atomic_get_plane_state(). This effectively serializes atomic
 * updates from .atomic_check() up to completion (when swapping the states if
 * the check step has succeeded) or rollback (when freeing the states if the
 * check step has failed).
 *
 * Allocation is performed in the .atomic_check() handler and applied
 * automatically when the core swaps the old and new states.
 */

53 54 55
static bool rcar_du_plane_needs_realloc(
				const struct rcar_du_plane_state *old_state,
				const struct rcar_du_plane_state *new_state)
56
{
57 58
	/*
	 * Lowering the number of planes doesn't strictly require reallocation
59 60 61
	 * as the extra hardware plane will be freed when committing, but doing
	 * so could lead to more fragmentation.
	 */
62 63
	if (!old_state->format ||
	    old_state->format->planes != new_state->format->planes)
64 65 66
		return true;

	/* Reallocate hardware planes if the source has changed. */
67
	if (old_state->source != new_state->source)
68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140
		return true;

	return false;
}

static unsigned int rcar_du_plane_hwmask(struct rcar_du_plane_state *state)
{
	unsigned int mask;

	if (state->hwindex == -1)
		return 0;

	mask = 1 << state->hwindex;
	if (state->format->planes == 2)
		mask |= 1 << ((state->hwindex + 1) % 8);

	return mask;
}

/*
 * The R8A7790 DU can source frames directly from the VSP1 devices VSPD0 and
 * VSPD1. VSPD0 feeds DU0/1 plane 0, and VSPD1 feeds either DU2 plane 0 or
 * DU0/1 plane 1.
 *
 * Allocate the correct fixed plane when sourcing frames from VSPD0 or VSPD1,
 * and allocate planes in reverse index order otherwise to ensure maximum
 * availability of planes 0 and 1.
 *
 * The caller is responsible for ensuring that the requested source is
 * compatible with the DU revision.
 */
static int rcar_du_plane_hwalloc(struct rcar_du_plane *plane,
				 struct rcar_du_plane_state *state,
				 unsigned int free)
{
	unsigned int num_planes = state->format->planes;
	int fixed = -1;
	int i;

	if (state->source == RCAR_DU_PLANE_VSPD0) {
		/* VSPD0 feeds plane 0 on DU0/1. */
		if (plane->group->index != 0)
			return -EINVAL;

		fixed = 0;
	} else if (state->source == RCAR_DU_PLANE_VSPD1) {
		/* VSPD1 feeds plane 1 on DU0/1 or plane 0 on DU2. */
		fixed = plane->group->index == 0 ? 1 : 0;
	}

	if (fixed >= 0)
		return free & (1 << fixed) ? fixed : -EBUSY;

	for (i = RCAR_DU_NUM_HW_PLANES - 1; i >= 0; --i) {
		if (!(free & (1 << i)))
			continue;

		if (num_planes == 1 || free & (1 << ((i + 1) % 8)))
			break;
	}

	return i < 0 ? -EBUSY : i;
}

int rcar_du_atomic_check_planes(struct drm_device *dev,
				struct drm_atomic_state *state)
{
	struct rcar_du_device *rcdu = dev->dev_private;
	unsigned int group_freed_planes[RCAR_DU_MAX_GROUPS] = { 0, };
	unsigned int group_free_planes[RCAR_DU_MAX_GROUPS] = { 0, };
	bool needs_realloc = false;
	unsigned int groups = 0;
	unsigned int i;
141
	struct drm_plane *drm_plane;
142 143
	struct drm_plane_state *old_drm_plane_state;
	struct drm_plane_state *new_drm_plane_state;
144 145

	/* Check if hardware planes need to be reallocated. */
146 147 148 149
	for_each_oldnew_plane_in_state(state, drm_plane, old_drm_plane_state,
				       new_drm_plane_state, i) {
		struct rcar_du_plane_state *old_plane_state;
		struct rcar_du_plane_state *new_plane_state;
150 151 152
		struct rcar_du_plane *plane;
		unsigned int index;

153
		plane = to_rcar_plane(drm_plane);
154 155
		old_plane_state = to_rcar_plane_state(old_drm_plane_state);
		new_plane_state = to_rcar_plane_state(new_drm_plane_state);
156

157
		dev_dbg(rcdu->dev, "%s: checking plane (%u,%tu)\n", __func__,
158 159
			plane->group->index, plane - plane->group->planes);

160 161
		/*
		 * If the plane is being disabled we don't need to go through
162 163 164
		 * the full reallocation procedure. Just mark the hardware
		 * plane(s) as freed.
		 */
165
		if (!new_plane_state->format) {
166 167 168 169
			dev_dbg(rcdu->dev, "%s: plane is being disabled\n",
				__func__);
			index = plane - plane->group->planes;
			group_freed_planes[plane->group->index] |= 1 << index;
170
			new_plane_state->hwindex = -1;
171 172 173
			continue;
		}

174 175
		/*
		 * If the plane needs to be reallocated mark it as such, and
176 177
		 * mark the hardware plane(s) as free.
		 */
178
		if (rcar_du_plane_needs_realloc(old_plane_state, new_plane_state)) {
179 180 181 182 183 184 185
			dev_dbg(rcdu->dev, "%s: plane needs reallocation\n",
				__func__);
			groups |= 1 << plane->group->index;
			needs_realloc = true;

			index = plane - plane->group->planes;
			group_freed_planes[plane->group->index] |= 1 << index;
186
			new_plane_state->hwindex = -1;
187 188 189 190 191 192
		}
	}

	if (!needs_realloc)
		return 0;

193 194
	/*
	 * Grab all plane states for the groups that need reallocation to ensure
195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211
	 * locking and avoid racy updates. This serializes the update operation,
	 * but there's not much we can do about it as that's the hardware
	 * design.
	 *
	 * Compute the used planes mask for each group at the same time to avoid
	 * looping over the planes separately later.
	 */
	while (groups) {
		unsigned int index = ffs(groups) - 1;
		struct rcar_du_group *group = &rcdu->groups[index];
		unsigned int used_planes = 0;

		dev_dbg(rcdu->dev, "%s: finding free planes for group %u\n",
			__func__, index);

		for (i = 0; i < group->num_planes; ++i) {
			struct rcar_du_plane *plane = &group->planes[i];
212
			struct rcar_du_plane_state *new_plane_state;
213 214 215 216 217 218
			struct drm_plane_state *s;

			s = drm_atomic_get_plane_state(state, &plane->plane);
			if (IS_ERR(s))
				return PTR_ERR(s);

219 220
			/*
			 * If the plane has been freed in the above loop its
221 222 223 224 225 226 227 228
			 * hardware planes must not be added to the used planes
			 * bitmask. However, the current state doesn't reflect
			 * the free state yet, as we've modified the new state
			 * above. Use the local freed planes list to check for
			 * that condition instead.
			 */
			if (group_freed_planes[index] & (1 << i)) {
				dev_dbg(rcdu->dev,
229
					"%s: plane (%u,%tu) has been freed, skipping\n",
230 231 232 233 234
					__func__, plane->group->index,
					plane - plane->group->planes);
				continue;
			}

235 236
			new_plane_state = to_rcar_plane_state(s);
			used_planes |= rcar_du_plane_hwmask(new_plane_state);
237 238

			dev_dbg(rcdu->dev,
239
				"%s: plane (%u,%tu) uses %u hwplanes (index %d)\n",
240 241
				__func__, plane->group->index,
				plane - plane->group->planes,
242 243 244
				new_plane_state->format ?
				new_plane_state->format->planes : 0,
				new_plane_state->hwindex);
245 246 247 248 249 250 251 252 253 254
		}

		group_free_planes[index] = 0xff & ~used_planes;
		groups &= ~(1 << index);

		dev_dbg(rcdu->dev, "%s: group %u free planes mask 0x%02x\n",
			__func__, index, group_free_planes[index]);
	}

	/* Reallocate hardware planes for each plane that needs it. */
255 256 257 258
	for_each_oldnew_plane_in_state(state, drm_plane, old_drm_plane_state,
				       new_drm_plane_state, i) {
		struct rcar_du_plane_state *old_plane_state;
		struct rcar_du_plane_state *new_plane_state;
259 260 261 262 263
		struct rcar_du_plane *plane;
		unsigned int crtc_planes;
		unsigned int free;
		int idx;

264
		plane = to_rcar_plane(drm_plane);
265 266
		old_plane_state = to_rcar_plane_state(old_drm_plane_state);
		new_plane_state = to_rcar_plane_state(new_drm_plane_state);
267

268
		dev_dbg(rcdu->dev, "%s: allocating plane (%u,%tu)\n", __func__,
269 270
			plane->group->index, plane - plane->group->planes);

271 272
		/*
		 * Skip planes that are being disabled or don't need to be
273 274
		 * reallocated.
		 */
275 276
		if (!new_plane_state->format ||
		    !rcar_du_plane_needs_realloc(old_plane_state, new_plane_state))
277 278
			continue;

279 280
		/*
		 * Try to allocate the plane from the free planes currently
281 282 283 284
		 * associated with the target CRTC to avoid restarting the CRTC
		 * group and thus minimize flicker. If it fails fall back to
		 * allocating from all free planes.
		 */
285
		crtc_planes = to_rcar_crtc(new_plane_state->state.crtc)->index % 2
286 287 288 289
			    ? plane->group->dptsr_planes
			    : ~plane->group->dptsr_planes;
		free = group_free_planes[plane->group->index];

290
		idx = rcar_du_plane_hwalloc(plane, new_plane_state,
291 292
					    free & crtc_planes);
		if (idx < 0)
293
			idx = rcar_du_plane_hwalloc(plane, new_plane_state,
294 295 296 297 298 299 300 301
						    free);
		if (idx < 0) {
			dev_dbg(rcdu->dev, "%s: no available hardware plane\n",
				__func__);
			return idx;
		}

		dev_dbg(rcdu->dev, "%s: allocated %u hwplanes (index %u)\n",
302
			__func__, new_plane_state->format->planes, idx);
303

304
		new_plane_state->hwindex = idx;
305 306

		group_free_planes[plane->group->index] &=
307
			~rcar_du_plane_hwmask(new_plane_state);
308 309 310 311 312 313 314 315 316 317 318 319 320

		dev_dbg(rcdu->dev, "%s: group %u free planes mask 0x%02x\n",
			__func__, plane->group->index,
			group_free_planes[plane->group->index]);
	}

	return 0;
}

/* -----------------------------------------------------------------------------
 * Plane Setup
 */

321 322 323 324
#define RCAR_DU_COLORKEY_NONE		(0 << 24)
#define RCAR_DU_COLORKEY_SOURCE		(1 << 24)
#define RCAR_DU_COLORKEY_MASK		(1 << 24)

325
static void rcar_du_plane_write(struct rcar_du_group *rgrp,
326 327
				unsigned int index, u32 reg, u32 data)
{
328 329
	rcar_du_write(rgrp->dev, rgrp->mmio_offset + index * PLANE_OFF + reg,
		      data);
330 331
}

332 333
static void rcar_du_plane_setup_scanout(struct rcar_du_group *rgrp,
					const struct rcar_du_plane_state *state)
334
{
335 336
	unsigned int src_x = state->state.src.x1 >> 16;
	unsigned int src_y = state->state.src.y1 >> 16;
337
	unsigned int index = state->hwindex;
338
	unsigned int pitch;
339
	bool interlaced;
340
	u32 dma[2];
341

342
	interlaced = state->state.crtc->state->adjusted_mode.flags
343
		   & DRM_MODE_FLAG_INTERLACE;
344

345 346 347 348 349 350 351 352 353
	if (state->source == RCAR_DU_PLANE_MEMORY) {
		struct drm_framebuffer *fb = state->state.fb;
		struct drm_gem_cma_object *gem;
		unsigned int i;

		if (state->format->planes == 2)
			pitch = fb->pitches[0];
		else
			pitch = fb->pitches[0] * 8 / state->format->bpp;
354

355 356 357 358 359
		for (i = 0; i < state->format->planes; ++i) {
			gem = drm_fb_cma_get_gem_obj(fb, i);
			dma[i] = gem->paddr + fb->offsets[i];
		}
	} else {
360
		pitch = drm_rect_width(&state->state.src) >> 16;
361 362
		dma[0] = 0;
		dma[1] = 0;
363
	}
364

365 366
	/*
	 * Memory pitch (expressed in pixels). Must be doubled for interlaced
367 368
	 * operation with 32bpp formats.
	 */
369 370 371
	rcar_du_plane_write(rgrp, index, PnMWR,
			    (interlaced && state->format->bpp == 32) ?
			    pitch * 2 : pitch);
372

373 374
	/*
	 * The Y position is expressed in raster line units and must be doubled
375 376 377 378
	 * for 32bpp formats, according to the R8A7790 datasheet. No mention of
	 * doubling the Y position is found in the R8A7779 datasheet, but the
	 * rule seems to apply there as well.
	 *
379 380 381
	 * Despite not being documented, doubling seem not to be needed when
	 * operating in interlaced mode.
	 *
382
	 * Similarly, for the second plane, NV12 and NV21 formats seem to
383 384
	 * require a halved Y position value, in both progressive and interlaced
	 * modes.
385
	 */
386 387
	rcar_du_plane_write(rgrp, index, PnSPXR, src_x);
	rcar_du_plane_write(rgrp, index, PnSPYR, src_y *
388
			    (!interlaced && state->format->bpp == 32 ? 2 : 1));
389

390
	rcar_du_plane_write(rgrp, index, PnDSA0R, dma[0]);
391

392
	if (state->format->planes == 2) {
393 394
		index = (index + 1) % 8;

395
		rcar_du_plane_write(rgrp, index, PnMWR, pitch);
396

397 398
		rcar_du_plane_write(rgrp, index, PnSPXR, src_x);
		rcar_du_plane_write(rgrp, index, PnSPYR, src_y *
399
				    (state->format->bpp == 16 ? 2 : 1) / 2);
400

401
		rcar_du_plane_write(rgrp, index, PnDSA0R, dma[1]);
402 403 404
	}
}

405 406 407
static void rcar_du_plane_setup_mode(struct rcar_du_group *rgrp,
				     unsigned int index,
				     const struct rcar_du_plane_state *state)
408 409 410 411
{
	u32 colorkey;
	u32 pnmr;

412 413
	/*
	 * The PnALPHAR register controls alpha-blending in 16bpp formats
414 415 416 417 418 419 420 421
	 * (ARGB1555 and XRGB1555).
	 *
	 * For ARGB, set the alpha value to 0, and enable alpha-blending when
	 * the A bit is 0. This maps A=0 to alpha=0 and A=1 to alpha=255.
	 *
	 * For XRGB, set the alpha value to the plane-wide alpha value and
	 * enable alpha-blending regardless of the X bit value.
	 */
422
	if (state->format->fourcc != DRM_FORMAT_XRGB1555)
423
		rcar_du_plane_write(rgrp, index, PnALPHAR, PnALPHAR_ABIT_0);
424
	else
425
		rcar_du_plane_write(rgrp, index, PnALPHAR,
426
				    PnALPHAR_ABIT_X | state->alpha);
427

428
	pnmr = PnMR_BM_MD | state->format->pnmr;
429

430 431
	/*
	 * Disable color keying when requested. YUV formats have the
432 433 434
	 * PnMR_SPIM_TP_OFF bit set in their pnmr field, disabling color keying
	 * automatically.
	 */
435
	if ((state->colorkey & RCAR_DU_COLORKEY_MASK) == RCAR_DU_COLORKEY_NONE)
436 437 438
		pnmr |= PnMR_SPIM_TP_OFF;

	/* For packed YUV formats we need to select the U/V order. */
439
	if (state->format->fourcc == DRM_FORMAT_YUYV)
440 441
		pnmr |= PnMR_YCDF_YUYV;

442
	rcar_du_plane_write(rgrp, index, PnMR, pnmr);
443

444
	switch (state->format->fourcc) {
445
	case DRM_FORMAT_RGB565:
446 447 448
		colorkey = ((state->colorkey & 0xf80000) >> 8)
			 | ((state->colorkey & 0x00fc00) >> 5)
			 | ((state->colorkey & 0x0000f8) >> 3);
449
		rcar_du_plane_write(rgrp, index, PnTC2R, colorkey);
450 451 452 453
		break;

	case DRM_FORMAT_ARGB1555:
	case DRM_FORMAT_XRGB1555:
454 455 456
		colorkey = ((state->colorkey & 0xf80000) >> 9)
			 | ((state->colorkey & 0x00f800) >> 6)
			 | ((state->colorkey & 0x0000f8) >> 3);
457
		rcar_du_plane_write(rgrp, index, PnTC2R, colorkey);
458 459 460 461
		break;

	case DRM_FORMAT_XRGB8888:
	case DRM_FORMAT_ARGB8888:
462
		rcar_du_plane_write(rgrp, index, PnTC3R,
463
				    PnTC3R_CODE | (state->colorkey & 0xffffff));
464 465 466 467
		break;
	}
}

468 469 470
static void rcar_du_plane_setup_format_gen2(struct rcar_du_group *rgrp,
					    unsigned int index,
					    const struct rcar_du_plane_state *state)
471 472 473 474
{
	u32 ddcr2 = PnDDCR2_CODE;
	u32 ddcr4;

475 476
	/*
	 * Data format
477 478 479 480 481
	 *
	 * The data format is selected by the DDDF field in PnMR and the EDF
	 * field in DDCR4.
	 */

482
	rcar_du_plane_setup_mode(rgrp, index, state);
483

484
	if (state->format->planes == 2) {
485
		if (state->hwindex != index) {
486 487
			if (state->format->fourcc == DRM_FORMAT_NV12 ||
			    state->format->fourcc == DRM_FORMAT_NV21)
488 489
				ddcr2 |= PnDDCR2_Y420;

490
			if (state->format->fourcc == DRM_FORMAT_NV21)
491 492 493 494 495 496 497 498
				ddcr2 |= PnDDCR2_NV21;

			ddcr2 |= PnDDCR2_DIVU;
		} else {
			ddcr2 |= PnDDCR2_DIVY;
		}
	}

499
	rcar_du_plane_write(rgrp, index, PnDDCR2, ddcr2);
500 501

	ddcr4 = state->format->edf | PnDDCR4_CODE;
502 503
	if (state->source != RCAR_DU_PLANE_MEMORY)
		ddcr4 |= PnDDCR4_VSPS;
504

505
	rcar_du_plane_write(rgrp, index, PnDDCR4, ddcr4);
506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523
}

static void rcar_du_plane_setup_format_gen3(struct rcar_du_group *rgrp,
					    unsigned int index,
					    const struct rcar_du_plane_state *state)
{
	rcar_du_plane_write(rgrp, index, PnMR,
			    PnMR_SPIM_TP_OFF | state->format->pnmr);

	rcar_du_plane_write(rgrp, index, PnDDCR4,
			    state->format->edf | PnDDCR4_CODE);
}

static void rcar_du_plane_setup_format(struct rcar_du_group *rgrp,
				       unsigned int index,
				       const struct rcar_du_plane_state *state)
{
	struct rcar_du_device *rcdu = rgrp->dev;
524
	const struct drm_rect *dst = &state->state.dst;
525 526 527 528 529

	if (rcdu->info->gen < 3)
		rcar_du_plane_setup_format_gen2(rgrp, index, state);
	else
		rcar_du_plane_setup_format_gen3(rgrp, index, state);
530 531

	/* Destination position and size */
532 533 534 535
	rcar_du_plane_write(rgrp, index, PnDSXR, drm_rect_width(dst));
	rcar_du_plane_write(rgrp, index, PnDSYR, drm_rect_height(dst));
	rcar_du_plane_write(rgrp, index, PnDPXR, dst->x1);
	rcar_du_plane_write(rgrp, index, PnDPYR, dst->y1);
536

537 538 539 540 541 542 543
	if (rcdu->info->gen < 3) {
		/* Wrap-around and blinking, disabled */
		rcar_du_plane_write(rgrp, index, PnWASPR, 0);
		rcar_du_plane_write(rgrp, index, PnWAMWR, 4095);
		rcar_du_plane_write(rgrp, index, PnBTR, 0);
		rcar_du_plane_write(rgrp, index, PnMLR, 0);
	}
544 545
}

546 547
void __rcar_du_plane_setup(struct rcar_du_group *rgrp,
			   const struct rcar_du_plane_state *state)
548
{
549 550
	struct rcar_du_device *rcdu = rgrp->dev;

551
	rcar_du_plane_setup_format(rgrp, state->hwindex, state);
552
	if (state->format->planes == 2)
553 554 555
		rcar_du_plane_setup_format(rgrp, (state->hwindex + 1) % 8,
					   state);

556 557
	if (rcdu->info->gen < 3)
		rcar_du_plane_setup_scanout(rgrp, state);
558

559 560 561 562 563 564 565 566
	if (state->source == RCAR_DU_PLANE_VSPD1) {
		unsigned int vspd1_sink = rgrp->index ? 2 : 0;

		if (rcdu->vspd1_sink != vspd1_sink) {
			rcdu->vspd1_sink = vspd1_sink;
			rcar_du_set_dpad0_vsp1_routing(rcdu);
		}
	}
567 568
}

569 570 571
int __rcar_du_plane_atomic_check(struct drm_plane *plane,
				 struct drm_plane_state *state,
				 const struct rcar_du_format_info **format)
572
{
573
	struct drm_device *dev = plane->dev;
574
	struct drm_crtc_state *crtc_state;
575
	struct drm_rect clip = {};
576
	int ret;
577

578 579 580 581 582 583
	if (!state->crtc) {
		/*
		 * The visible field is not reset by the DRM core but only
		 * updated by drm_plane_helper_check_state(), set it manually.
		 */
		state->visible = false;
584
		*format = NULL;
585
		return 0;
586
	}
587

588 589 590 591
	crtc_state = drm_atomic_get_crtc_state(state->state, state->crtc);
	if (IS_ERR(crtc_state))
		return PTR_ERR(crtc_state);

592 593 594
	if (crtc_state->enable)
		drm_mode_get_hv_timing(&crtc_state->mode,
				       &clip.x2, &clip.y2);
595 596 597 598 599 600 601 602 603 604 605

	ret = drm_atomic_helper_check_plane_state(state, crtc_state, &clip,
						  DRM_PLANE_HELPER_NO_SCALING,
						  DRM_PLANE_HELPER_NO_SCALING,
						  true, true);
	if (ret < 0)
		return ret;

	if (!state->visible) {
		*format = NULL;
		return 0;
606 607
	}

608 609 610
	*format = rcar_du_format_info(state->fb->format->format);
	if (*format == NULL) {
		dev_dbg(dev->dev, "%s: unsupported format %08x\n", __func__,
V
Ville Syrjälä 已提交
611
			state->fb->format->format);
612 613 614 615 616 617
		return -EINVAL;
	}

	return 0;
}

618 619 620 621 622 623 624 625
static int rcar_du_plane_atomic_check(struct drm_plane *plane,
				      struct drm_plane_state *state)
{
	struct rcar_du_plane_state *rstate = to_rcar_plane_state(state);

	return __rcar_du_plane_atomic_check(plane, state, &rstate->format);
}

626 627 628 629
static void rcar_du_plane_atomic_update(struct drm_plane *plane,
					struct drm_plane_state *old_state)
{
	struct rcar_du_plane *rplane = to_rcar_plane(plane);
630 631
	struct rcar_du_plane_state *old_rstate;
	struct rcar_du_plane_state *new_rstate;
632

633
	if (!plane->state->visible)
634 635 636 637
		return;

	rcar_du_plane_setup(rplane);

638 639
	/*
	 * Check whether the source has changed from memory to live source or
640 641 642 643 644 645 646 647 648 649 650 651
	 * from live source to memory. The source has been configured by the
	 * VSPS bit in the PnDDCR4 register. Although the datasheet states that
	 * the bit is updated during vertical blanking, it seems that updates
	 * only occur when the DU group is held in reset through the DSYSR.DRES
	 * bit. We thus need to restart the group if the source changes.
	 */
	old_rstate = to_rcar_plane_state(old_state);
	new_rstate = to_rcar_plane_state(plane->state);

	if ((old_rstate->source == RCAR_DU_PLANE_MEMORY) !=
	    (new_rstate->source == RCAR_DU_PLANE_MEMORY))
		rplane->group->need_restart = true;
652 653
}

654 655 656 657 658
static const struct drm_plane_helper_funcs rcar_du_plane_helper_funcs = {
	.atomic_check = rcar_du_plane_atomic_check,
	.atomic_update = rcar_du_plane_atomic_update,
};

659 660
static struct drm_plane_state *
rcar_du_plane_atomic_duplicate_state(struct drm_plane *plane)
661
{
662 663
	struct rcar_du_plane_state *state;
	struct rcar_du_plane_state *copy;
664

665 666 667
	if (WARN_ON(!plane->state))
		return NULL;

668
	state = to_rcar_plane_state(plane->state);
669 670 671 672
	copy = kmemdup(state, sizeof(*state), GFP_KERNEL);
	if (copy == NULL)
		return NULL;

673
	__drm_atomic_helper_plane_duplicate_state(plane, &copy->state);
674

675
	return &copy->state;
676 677
}

678 679
static void rcar_du_plane_atomic_destroy_state(struct drm_plane *plane,
					       struct drm_plane_state *state)
680
{
681
	__drm_atomic_helper_plane_destroy_state(state);
682
	kfree(to_rcar_plane_state(state));
683
}
684

685 686 687 688 689 690 691 692 693 694 695 696 697 698
static void rcar_du_plane_reset(struct drm_plane *plane)
{
	struct rcar_du_plane_state *state;

	if (plane->state) {
		rcar_du_plane_atomic_destroy_state(plane, plane->state);
		plane->state = NULL;
	}

	state = kzalloc(sizeof(*state), GFP_KERNEL);
	if (state == NULL)
		return;

	state->hwindex = -1;
699
	state->source = RCAR_DU_PLANE_MEMORY;
700 701
	state->alpha = 255;
	state->colorkey = RCAR_DU_COLORKEY_NONE;
702
	state->state.zpos = plane->type == DRM_PLANE_TYPE_PRIMARY ? 0 : 1;
703 704 705 706 707

	plane->state = &state->state;
	plane->state->plane = plane;
}

708 709 710 711 712
static int rcar_du_plane_atomic_set_property(struct drm_plane *plane,
					     struct drm_plane_state *state,
					     struct drm_property *property,
					     uint64_t val)
{
713
	struct rcar_du_plane_state *rstate = to_rcar_plane_state(state);
714
	struct rcar_du_device *rcdu = to_rcar_plane(plane)->group->dev;
715

716
	if (property == rcdu->props.alpha)
717
		rstate->alpha = val;
718
	else if (property == rcdu->props.colorkey)
719 720 721
		rstate->colorkey = val;
	else
		return -EINVAL;
722

723
	return 0;
724 725
}

726 727 728
static int rcar_du_plane_atomic_get_property(struct drm_plane *plane,
	const struct drm_plane_state *state, struct drm_property *property,
	uint64_t *val)
729
{
730 731
	const struct rcar_du_plane_state *rstate =
		container_of(state, const struct rcar_du_plane_state, state);
732
	struct rcar_du_device *rcdu = to_rcar_plane(plane)->group->dev;
733

734
	if (property == rcdu->props.alpha)
735
		*val = rstate->alpha;
736
	else if (property == rcdu->props.colorkey)
737
		*val = rstate->colorkey;
738 739 740 741 742 743 744
	else
		return -EINVAL;

	return 0;
}

static const struct drm_plane_funcs rcar_du_plane_funcs = {
745 746
	.update_plane = drm_atomic_helper_update_plane,
	.disable_plane = drm_atomic_helper_disable_plane,
747
	.reset = rcar_du_plane_reset,
748
	.destroy = drm_plane_cleanup,
749 750 751 752
	.atomic_duplicate_state = rcar_du_plane_atomic_duplicate_state,
	.atomic_destroy_state = rcar_du_plane_atomic_destroy_state,
	.atomic_set_property = rcar_du_plane_atomic_set_property,
	.atomic_get_property = rcar_du_plane_atomic_get_property,
753 754 755 756 757 758 759 760 761 762 763 764 765 766 767
};

static const uint32_t formats[] = {
	DRM_FORMAT_RGB565,
	DRM_FORMAT_ARGB1555,
	DRM_FORMAT_XRGB1555,
	DRM_FORMAT_XRGB8888,
	DRM_FORMAT_ARGB8888,
	DRM_FORMAT_UYVY,
	DRM_FORMAT_YUYV,
	DRM_FORMAT_NV12,
	DRM_FORMAT_NV21,
	DRM_FORMAT_NV16,
};

768
int rcar_du_planes_init(struct rcar_du_group *rgrp)
769
{
770
	struct rcar_du_device *rcdu = rgrp->dev;
771
	unsigned int crtcs;
772
	unsigned int i;
773
	int ret;
774

775 776
	 /*
	  * Create one primary plane per CRTC in this group and seven overlay
777 778
	  * planes.
	  */
779
	rgrp->num_planes = rgrp->num_crtcs + 7;
780 781 782

	crtcs = ((1 << rcdu->num_crtcs) - 1) & (3 << (2 * rgrp->index));

783
	for (i = 0; i < rgrp->num_planes; ++i) {
784
		enum drm_plane_type type = i < rgrp->num_crtcs
785 786
					 ? DRM_PLANE_TYPE_PRIMARY
					 : DRM_PLANE_TYPE_OVERLAY;
787
		struct rcar_du_plane *plane = &rgrp->planes[i];
788

789
		plane->group = rgrp;
790

791 792
		ret = drm_universal_plane_init(rcdu->ddev, &plane->plane, crtcs,
					       &rcar_du_plane_funcs, formats,
793 794
					       ARRAY_SIZE(formats),
					       NULL, type, NULL);
795 796 797
		if (ret < 0)
			return ret;

798 799 800
		drm_plane_helper_add(&plane->plane,
				     &rcar_du_plane_helper_funcs);

801 802 803
		if (type == DRM_PLANE_TYPE_PRIMARY)
			continue;

804
		drm_object_attach_property(&plane->plane.base,
805
					   rcdu->props.alpha, 255);
806
		drm_object_attach_property(&plane->plane.base,
807
					   rcdu->props.colorkey,
808
					   RCAR_DU_COLORKEY_NONE);
809
		drm_plane_create_zpos_property(&plane->plane, 1, 1, 7);
810 811 812 813
	}

	return 0;
}