mdp5_crtc.c 14.3 KB
Newer Older
R
Rob Clark 已提交
1
/*
2
 * Copyright (c) 2014 The Linux Foundation. All rights reserved.
R
Rob Clark 已提交
3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
 * Copyright (C) 2013 Red Hat
 * Author: Rob Clark <robdclark@gmail.com>
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 as published by
 * the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
 * more details.
 *
 * You should have received a copy of the GNU General Public License along with
 * this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#include "mdp5_kms.h"

R
Rob Clark 已提交
21
#include <linux/sort.h>
R
Rob Clark 已提交
22 23 24 25 26
#include <drm/drm_mode.h>
#include "drm_crtc.h"
#include "drm_crtc_helper.h"
#include "drm_flip_work.h"

27 28
#define SSPP_MAX	(SSPP_RGB3 + 1) /* TODO: Add SSPP_MAX in mdp5.xml.h */

R
Rob Clark 已提交
29 30 31 32 33 34
struct mdp5_crtc {
	struct drm_crtc base;
	char name[8];
	int id;
	bool enabled;

35 36 37 38 39 40
	/* layer mixer used for this CRTC (+ its lock): */
#define GET_LM_ID(crtc_id)	((crtc_id == 3) ? 5 : crtc_id)
	int lm;
	spinlock_t lm_lock;	/* protect REG_MDP5_LM_* registers */

	/* CTL used for this CRTC: */
41
	struct mdp5_ctl *ctl;
R
Rob Clark 已提交
42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68

	/* if there is a pending flip, these will be non-null: */
	struct drm_pending_vblank_event *event;

#define PENDING_CURSOR 0x1
#define PENDING_FLIP   0x2
	atomic_t pending;

	struct mdp_irq vblank;
	struct mdp_irq err;
};
#define to_mdp5_crtc(x) container_of(x, struct mdp5_crtc, base)

static struct mdp5_kms *get_kms(struct drm_crtc *crtc)
{
	struct msm_drm_private *priv = crtc->dev->dev_private;
	return to_mdp5_kms(to_mdp_kms(priv->kms));
}

static void request_pending(struct drm_crtc *crtc, uint32_t pending)
{
	struct mdp5_crtc *mdp5_crtc = to_mdp5_crtc(crtc);

	atomic_or(pending, &mdp5_crtc->pending);
	mdp_irq_register(&get_kms(crtc)->base, &mdp5_crtc->vblank);
}

69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84
#define mdp5_lm_get_flush(lm)	mdp_ctl_flush_mask_lm(lm)

static void crtc_flush(struct drm_crtc *crtc, u32 flush_mask)
{
	struct mdp5_crtc *mdp5_crtc = to_mdp5_crtc(crtc);

	DBG("%s: flush=%08x", mdp5_crtc->name, flush_mask);
	mdp5_ctl_commit(mdp5_crtc->ctl, flush_mask);
}

/*
 * flush updates, to make sure hw is updated to new scanout fb,
 * so that we can safely queue unref to current fb (ie. next
 * vblank we know hw is done w/ previous scanout_fb).
 */
static void crtc_flush_all(struct drm_crtc *crtc)
R
Rob Clark 已提交
85 86
{
	struct mdp5_crtc *mdp5_crtc = to_mdp5_crtc(crtc);
87
	struct drm_plane *plane;
88 89 90 91 92
	uint32_t flush_mask = 0;

	/* we could have already released CTL in the disable path: */
	if (!mdp5_crtc->ctl)
		return;
R
Rob Clark 已提交
93

94
	drm_atomic_crtc_for_each_plane(plane, crtc) {
95
		flush_mask |= mdp5_plane_get_flush(plane);
R
Rob Clark 已提交
96
	}
97 98
	flush_mask |= mdp5_ctl_get_flush(mdp5_crtc->ctl);
	flush_mask |= mdp5_lm_get_flush(mdp5_crtc->lm);
99

100
	crtc_flush(crtc, flush_mask);
R
Rob Clark 已提交
101 102 103 104 105 106 107 108
}

/* if file!=NULL, this is preclose potential cancel-flip path */
static void complete_flip(struct drm_crtc *crtc, struct drm_file *file)
{
	struct mdp5_crtc *mdp5_crtc = to_mdp5_crtc(crtc);
	struct drm_device *dev = crtc->dev;
	struct drm_pending_vblank_event *event;
109 110
	struct drm_plane *plane;
	unsigned long flags;
R
Rob Clark 已提交
111 112 113 114 115 116 117 118 119 120

	spin_lock_irqsave(&dev->event_lock, flags);
	event = mdp5_crtc->event;
	if (event) {
		/* if regular vblank case (!file) or if cancel-flip from
		 * preclose on file that requested flip, then send the
		 * event:
		 */
		if (!file || (event->base.file_priv == file)) {
			mdp5_crtc->event = NULL;
R
Rob Clark 已提交
121
			DBG("%s: send event: %p", mdp5_crtc->name, event);
R
Rob Clark 已提交
122 123 124 125 126
			drm_send_vblank_event(dev, mdp5_crtc->id, event);
		}
	}
	spin_unlock_irqrestore(&dev->event_lock, flags);

127
	drm_atomic_crtc_for_each_plane(plane, crtc) {
128
		mdp5_plane_complete_flip(plane);
129
	}
R
Rob Clark 已提交
130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153
}

static void mdp5_crtc_destroy(struct drm_crtc *crtc)
{
	struct mdp5_crtc *mdp5_crtc = to_mdp5_crtc(crtc);

	drm_crtc_cleanup(crtc);

	kfree(mdp5_crtc);
}

static void mdp5_crtc_dpms(struct drm_crtc *crtc, int mode)
{
	struct mdp5_crtc *mdp5_crtc = to_mdp5_crtc(crtc);
	struct mdp5_kms *mdp5_kms = get_kms(crtc);
	bool enabled = (mode == DRM_MODE_DPMS_ON);

	DBG("%s: mode=%d", mdp5_crtc->name, mode);

	if (enabled != mdp5_crtc->enabled) {
		if (enabled) {
			mdp5_enable(mdp5_kms);
			mdp_irq_register(&mdp5_kms->base, &mdp5_crtc->err);
		} else {
R
Rob Clark 已提交
154 155
			/* set STAGE_UNUSED for all layers */
			mdp5_ctl_blend(mdp5_crtc->ctl, mdp5_crtc->lm, 0x00000000);
R
Rob Clark 已提交
156 157 158 159 160 161 162 163 164 165 166 167 168 169
			mdp_irq_unregister(&mdp5_kms->base, &mdp5_crtc->err);
			mdp5_disable(mdp5_kms);
		}
		mdp5_crtc->enabled = enabled;
	}
}

static bool mdp5_crtc_mode_fixup(struct drm_crtc *crtc,
		const struct drm_display_mode *mode,
		struct drm_display_mode *adjusted_mode)
{
	return true;
}

170 171 172 173 174 175 176 177 178 179 180
/*
 * blend_setup() - blend all the planes of a CRTC
 *
 * When border is enabled, the border color will ALWAYS be the base layer.
 * Therefore, the first plane (private RGB pipe) will start at STAGE0.
 * If disabled, the first plane starts at STAGE_BASE.
 *
 * Note:
 * Border is not enabled here because the private plane is exactly
 * the CRTC resolution.
 */
R
Rob Clark 已提交
181 182 183 184
static void blend_setup(struct drm_crtc *crtc)
{
	struct mdp5_crtc *mdp5_crtc = to_mdp5_crtc(crtc);
	struct mdp5_kms *mdp5_kms = get_kms(crtc);
185 186 187 188 189
	struct drm_plane *plane;
	const struct mdp5_cfg_hw *hw_cfg;
	uint32_t lm = mdp5_crtc->lm, blend_cfg = 0;
	unsigned long flags;
#define blender(stage)	((stage) - STAGE_BASE)
R
Rob Clark 已提交
190

191
	hw_cfg = mdp5_cfg_get_hw_config(mdp5_kms->cfg);
R
Rob Clark 已提交
192

193 194 195 196 197 198
	spin_lock_irqsave(&mdp5_crtc->lm_lock, flags);

	/* ctl could be released already when we are shutting down: */
	if (!mdp5_crtc->ctl)
		goto out;

199
	drm_atomic_crtc_for_each_plane(plane, crtc) {
R
Rob Clark 已提交
200 201
		enum mdp_mixer_stage_id stage =
			to_mdp5_plane_state(plane->state)->stage;
R
Rob Clark 已提交
202

203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228
		/*
		 * Note: This cannot happen with current implementation but
		 * we need to check this condition once z property is added
		 */
		BUG_ON(stage > hw_cfg->lm.nb_stages);

		/* LM */
		mdp5_write(mdp5_kms,
				REG_MDP5_LM_BLEND_OP_MODE(lm, blender(stage)),
				MDP5_LM_BLEND_OP_MODE_FG_ALPHA(FG_CONST) |
				MDP5_LM_BLEND_OP_MODE_BG_ALPHA(BG_CONST));
		mdp5_write(mdp5_kms, REG_MDP5_LM_BLEND_FG_ALPHA(lm,
				blender(stage)), 0xff);
		mdp5_write(mdp5_kms, REG_MDP5_LM_BLEND_BG_ALPHA(lm,
				blender(stage)), 0x00);
		/* CTL */
		blend_cfg |= mdp_ctl_blend_mask(mdp5_plane_pipe(plane), stage);
		DBG("%s: blending pipe %s on stage=%d", mdp5_crtc->name,
				pipe2name(mdp5_plane_pipe(plane)), stage);
	}

	DBG("%s: lm%d: blend config = 0x%08x", mdp5_crtc->name, lm, blend_cfg);
	mdp5_ctl_blend(mdp5_crtc->ctl, lm, blend_cfg);

out:
	spin_unlock_irqrestore(&mdp5_crtc->lm_lock, flags);
R
Rob Clark 已提交
229 230
}

R
Rob Clark 已提交
231
static void mdp5_crtc_mode_set_nofb(struct drm_crtc *crtc)
R
Rob Clark 已提交
232 233 234
{
	struct mdp5_crtc *mdp5_crtc = to_mdp5_crtc(crtc);
	struct mdp5_kms *mdp5_kms = get_kms(crtc);
235
	unsigned long flags;
R
Rob Clark 已提交
236 237 238 239
	struct drm_display_mode *mode;

	if (WARN_ON(!crtc->state))
		return;
R
Rob Clark 已提交
240

R
Rob Clark 已提交
241
	mode = &crtc->state->adjusted_mode;
R
Rob Clark 已提交
242 243 244 245 246 247 248 249 250 251

	DBG("%s: set mode: %d:\"%s\" %d %d %d %d %d %d %d %d %d %d 0x%x 0x%x",
			mdp5_crtc->name, mode->base.id, mode->name,
			mode->vrefresh, mode->clock,
			mode->hdisplay, mode->hsync_start,
			mode->hsync_end, mode->htotal,
			mode->vdisplay, mode->vsync_start,
			mode->vsync_end, mode->vtotal,
			mode->type, mode->flags);

252 253
	spin_lock_irqsave(&mdp5_crtc->lm_lock, flags);
	mdp5_write(mdp5_kms, REG_MDP5_LM_OUT_SIZE(mdp5_crtc->lm),
R
Rob Clark 已提交
254 255
			MDP5_LM_OUT_SIZE_WIDTH(mode->hdisplay) |
			MDP5_LM_OUT_SIZE_HEIGHT(mode->vdisplay));
256
	spin_unlock_irqrestore(&mdp5_crtc->lm_lock, flags);
R
Rob Clark 已提交
257 258 259 260 261 262 263 264 265 266 267 268 269
}

static void mdp5_crtc_prepare(struct drm_crtc *crtc)
{
	struct mdp5_crtc *mdp5_crtc = to_mdp5_crtc(crtc);
	DBG("%s", mdp5_crtc->name);
	/* make sure we hold a ref to mdp clks while setting up mode: */
	mdp5_enable(get_kms(crtc));
	mdp5_crtc_dpms(crtc, DRM_MODE_DPMS_OFF);
}

static void mdp5_crtc_commit(struct drm_crtc *crtc)
{
R
Rob Clark 已提交
270 271
	struct mdp5_crtc *mdp5_crtc = to_mdp5_crtc(crtc);
	DBG("%s", mdp5_crtc->name);
R
Rob Clark 已提交
272
	mdp5_crtc_dpms(crtc, DRM_MODE_DPMS_ON);
273
	crtc_flush_all(crtc);
R
Rob Clark 已提交
274 275 276 277
	/* drop the ref to mdp clk's that we got in prepare: */
	mdp5_disable(get_kms(crtc));
}

R
Rob Clark 已提交
278 279 280 281 282 283
struct plane_state {
	struct drm_plane *plane;
	struct mdp5_plane_state *state;
};

static int pstate_cmp(const void *a, const void *b)
R
Rob Clark 已提交
284
{
R
Rob Clark 已提交
285 286 287
	struct plane_state *pa = (struct plane_state *)a;
	struct plane_state *pb = (struct plane_state *)b;
	return pa->state->zpos - pb->state->zpos;
R
Rob Clark 已提交
288 289
}

R
Rob Clark 已提交
290 291
static int mdp5_crtc_atomic_check(struct drm_crtc *crtc,
		struct drm_crtc_state *state)
292 293
{
	struct mdp5_crtc *mdp5_crtc = to_mdp5_crtc(crtc);
R
Rob Clark 已提交
294 295 296 297 298
	struct mdp5_kms *mdp5_kms = get_kms(crtc);
	struct drm_plane *plane;
	struct drm_device *dev = crtc->dev;
	struct plane_state pstates[STAGE3 + 1];
	int cnt = 0, i;
299

R
Rob Clark 已提交
300
	DBG("%s: check", mdp5_crtc->name);
301

R
Rob Clark 已提交
302 303 304 305 306
	/* request a free CTL, if none is already allocated for this CRTC */
	if (state->enable && !mdp5_crtc->ctl) {
		mdp5_crtc->ctl = mdp5_ctlm_request(mdp5_kms->ctlm, crtc);
		if (WARN_ON(!mdp5_crtc->ctl))
			return -EINVAL;
307
	}
R
Rob Clark 已提交
308 309 310 311

	/* verify that there are not too many planes attached to crtc
	 * and that we don't have conflicting mixer stages:
	 */
312
	drm_atomic_crtc_state_for_each_plane(plane, state) {
R
Rob Clark 已提交
313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343
		struct drm_plane_state *pstate;

		if (cnt >= ARRAY_SIZE(pstates)) {
			dev_err(dev->dev, "too many planes!\n");
			return -EINVAL;
		}

		pstate = state->state->plane_states[drm_plane_index(plane)];

		/* plane might not have changed, in which case take
		 * current state:
		 */
		if (!pstate)
			pstate = plane->state;

		pstates[cnt].plane = plane;
		pstates[cnt].state = to_mdp5_plane_state(pstate);

		cnt++;
	}

	sort(pstates, cnt, sizeof(pstates[0]), pstate_cmp, NULL);

	for (i = 0; i < cnt; i++) {
		pstates[i].state->stage = STAGE_BASE + i;
		DBG("%s: assign pipe %s on stage=%d", mdp5_crtc->name,
				pipe2name(mdp5_plane_pipe(pstates[i].plane)),
				pstates[i].state->stage);
	}

	return 0;
344 345
}

R
Rob Clark 已提交
346 347 348 349 350
static void mdp5_crtc_atomic_begin(struct drm_crtc *crtc)
{
	struct mdp5_crtc *mdp5_crtc = to_mdp5_crtc(crtc);
	DBG("%s: begin", mdp5_crtc->name);
}
351

R
Rob Clark 已提交
352
static void mdp5_crtc_atomic_flush(struct drm_crtc *crtc)
R
Rob Clark 已提交
353 354 355 356 357
{
	struct mdp5_crtc *mdp5_crtc = to_mdp5_crtc(crtc);
	struct drm_device *dev = crtc->dev;
	unsigned long flags;

358
	DBG("%s: event: %p", mdp5_crtc->name, crtc->state->event);
R
Rob Clark 已提交
359

R
Rob Clark 已提交
360
	WARN_ON(mdp5_crtc->event);
R
Rob Clark 已提交
361 362

	spin_lock_irqsave(&dev->event_lock, flags);
R
Rob Clark 已提交
363
	mdp5_crtc->event = crtc->state->event;
R
Rob Clark 已提交
364 365
	spin_unlock_irqrestore(&dev->event_lock, flags);

R
Rob Clark 已提交
366 367 368
	blend_setup(crtc);
	crtc_flush_all(crtc);
	request_pending(crtc, PENDING_FLIP);
R
Rob Clark 已提交
369

R
Rob Clark 已提交
370 371 372 373
	if (mdp5_crtc->ctl && !crtc->state->enable) {
		mdp5_ctl_release(mdp5_crtc->ctl);
		mdp5_crtc->ctl = NULL;
	}
R
Rob Clark 已提交
374 375 376 377 378 379 380 381 382 383
}

static int mdp5_crtc_set_property(struct drm_crtc *crtc,
		struct drm_property *property, uint64_t val)
{
	// XXX
	return -EINVAL;
}

static const struct drm_crtc_funcs mdp5_crtc_funcs = {
R
Rob Clark 已提交
384
	.set_config = drm_atomic_helper_set_config,
R
Rob Clark 已提交
385
	.destroy = mdp5_crtc_destroy,
R
Rob Clark 已提交
386
	.page_flip = drm_atomic_helper_page_flip,
R
Rob Clark 已提交
387
	.set_property = mdp5_crtc_set_property,
R
Rob Clark 已提交
388 389 390
	.reset = drm_atomic_helper_crtc_reset,
	.atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state,
	.atomic_destroy_state = drm_atomic_helper_crtc_destroy_state,
R
Rob Clark 已提交
391 392 393 394 395
};

static const struct drm_crtc_helper_funcs mdp5_crtc_helper_funcs = {
	.dpms = mdp5_crtc_dpms,
	.mode_fixup = mdp5_crtc_mode_fixup,
R
Rob Clark 已提交
396
	.mode_set_nofb = mdp5_crtc_mode_set_nofb,
R
Rob Clark 已提交
397 398
	.prepare = mdp5_crtc_prepare,
	.commit = mdp5_crtc_commit,
R
Rob Clark 已提交
399 400 401
	.atomic_check = mdp5_crtc_atomic_check,
	.atomic_begin = mdp5_crtc_atomic_begin,
	.atomic_flush = mdp5_crtc_atomic_flush,
R
Rob Clark 已提交
402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421
};

static void mdp5_crtc_vblank_irq(struct mdp_irq *irq, uint32_t irqstatus)
{
	struct mdp5_crtc *mdp5_crtc = container_of(irq, struct mdp5_crtc, vblank);
	struct drm_crtc *crtc = &mdp5_crtc->base;
	unsigned pending;

	mdp_irq_unregister(&get_kms(crtc)->base, &mdp5_crtc->vblank);

	pending = atomic_xchg(&mdp5_crtc->pending, 0);

	if (pending & PENDING_FLIP) {
		complete_flip(crtc, NULL);
	}
}

static void mdp5_crtc_err_irq(struct mdp_irq *irq, uint32_t irqstatus)
{
	struct mdp5_crtc *mdp5_crtc = container_of(irq, struct mdp5_crtc, err);
422

R
Rob Clark 已提交
423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443
	DBG("%s: error: %08x", mdp5_crtc->name, irqstatus);
}

uint32_t mdp5_crtc_vblank(struct drm_crtc *crtc)
{
	struct mdp5_crtc *mdp5_crtc = to_mdp5_crtc(crtc);
	return mdp5_crtc->vblank.irqmask;
}

void mdp5_crtc_cancel_pending_flip(struct drm_crtc *crtc, struct drm_file *file)
{
	DBG("cancel: %p", file);
	complete_flip(crtc, file);
}

/* set interface for routing crtc->encoder: */
void mdp5_crtc_set_intf(struct drm_crtc *crtc, int intf,
		enum mdp5_intf intf_id)
{
	struct mdp5_crtc *mdp5_crtc = to_mdp5_crtc(crtc);
	struct mdp5_kms *mdp5_kms = get_kms(crtc);
444
	uint32_t flush_mask = 0;
R
Rob Clark 已提交
445
	uint32_t intf_sel;
446
	unsigned long flags;
R
Rob Clark 已提交
447 448 449 450

	/* now that we know what irq's we want: */
	mdp5_crtc->err.irqmask = intf2err(intf);
	mdp5_crtc->vblank.irqmask = intf2vblank(intf);
451
	mdp_irq_update(&mdp5_kms->base);
R
Rob Clark 已提交
452

453
	spin_lock_irqsave(&mdp5_kms->resource_lock, flags);
R
Rob Clark 已提交
454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477
	intf_sel = mdp5_read(mdp5_kms, REG_MDP5_DISP_INTF_SEL);

	switch (intf) {
	case 0:
		intf_sel &= ~MDP5_DISP_INTF_SEL_INTF0__MASK;
		intf_sel |= MDP5_DISP_INTF_SEL_INTF0(intf_id);
		break;
	case 1:
		intf_sel &= ~MDP5_DISP_INTF_SEL_INTF1__MASK;
		intf_sel |= MDP5_DISP_INTF_SEL_INTF1(intf_id);
		break;
	case 2:
		intf_sel &= ~MDP5_DISP_INTF_SEL_INTF2__MASK;
		intf_sel |= MDP5_DISP_INTF_SEL_INTF2(intf_id);
		break;
	case 3:
		intf_sel &= ~MDP5_DISP_INTF_SEL_INTF3__MASK;
		intf_sel |= MDP5_DISP_INTF_SEL_INTF3(intf_id);
		break;
	default:
		BUG();
		break;
	}

478 479
	mdp5_write(mdp5_kms, REG_MDP5_DISP_INTF_SEL, intf_sel);
	spin_unlock_irqrestore(&mdp5_kms->resource_lock, flags);
R
Rob Clark 已提交
480 481

	DBG("%s: intf_sel=%08x", mdp5_crtc->name, intf_sel);
482 483 484
	mdp5_ctl_set_intf(mdp5_crtc->ctl, intf);
	flush_mask |= mdp5_ctl_get_flush(mdp5_crtc->ctl);
	flush_mask |= mdp5_lm_get_flush(mdp5_crtc->lm);
R
Rob Clark 已提交
485

486 487
	crtc_flush(crtc, flush_mask);
}
R
Rob Clark 已提交
488

489 490 491 492 493 494 495 496 497 498
int mdp5_crtc_get_lm(struct drm_crtc *crtc)
{
	struct mdp5_crtc *mdp5_crtc = to_mdp5_crtc(crtc);

	if (WARN_ON(!crtc))
		return -EINVAL;

	return mdp5_crtc->lm;
}

R
Rob Clark 已提交
499 500 501 502 503 504 505 506
/* initialize crtc */
struct drm_crtc *mdp5_crtc_init(struct drm_device *dev,
		struct drm_plane *plane, int id)
{
	struct drm_crtc *crtc = NULL;
	struct mdp5_crtc *mdp5_crtc;

	mdp5_crtc = kzalloc(sizeof(*mdp5_crtc), GFP_KERNEL);
507 508
	if (!mdp5_crtc)
		return ERR_PTR(-ENOMEM);
R
Rob Clark 已提交
509 510 511 512

	crtc = &mdp5_crtc->base;

	mdp5_crtc->id = id;
513 514 515
	mdp5_crtc->lm = GET_LM_ID(id);

	spin_lock_init(&mdp5_crtc->lm_lock);
R
Rob Clark 已提交
516 517 518 519 520 521 522

	mdp5_crtc->vblank.irq = mdp5_crtc_vblank_irq;
	mdp5_crtc->err.irq = mdp5_crtc_err_irq;

	snprintf(mdp5_crtc->name, sizeof(mdp5_crtc->name), "%s:%d",
			pipe2name(mdp5_plane_pipe(plane)), id);

523
	drm_crtc_init_with_planes(dev, crtc, plane, NULL, &mdp5_crtc_funcs);
R
Rob Clark 已提交
524
	drm_crtc_helper_add(crtc, &mdp5_crtc_helper_funcs);
525
	plane->crtc = crtc;
R
Rob Clark 已提交
526

R
Rob Clark 已提交
527
	mdp5_plane_install_properties(plane, &crtc->base);
R
Rob Clark 已提交
528 529 530

	return crtc;
}