rockchip_drm_vop.c 48.3 KB
Newer Older
M
Mark Yao 已提交
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
/*
 * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
 * Author:Mark Yao <mark.yao@rock-chips.com>
 *
 * This software is licensed under the terms of the GNU General Public
 * License version 2, as published by the Free Software Foundation, and
 * may be copied, distributed, and modified under those terms.
 *
 * 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.
 */

#include <drm/drm.h>
#include <drm/drmP.h>
17
#include <drm/drm_atomic.h>
18
#include <drm/drm_atomic_uapi.h>
M
Mark Yao 已提交
19
#include <drm/drm_crtc.h>
20
#include <drm/drm_flip_work.h>
21
#include <drm/drm_gem_framebuffer_helper.h>
M
Mark Yao 已提交
22
#include <drm/drm_plane_helper.h>
23
#include <drm/drm_probe_helper.h>
24
#ifdef CONFIG_DRM_ANALOGIX_DP
25
#include <drm/bridge/analogix_dp.h>
26
#endif
M
Mark Yao 已提交
27 28

#include <linux/kernel.h>
29
#include <linux/module.h>
M
Mark Yao 已提交
30 31
#include <linux/platform_device.h>
#include <linux/clk.h>
32
#include <linux/iopoll.h>
M
Mark Yao 已提交
33 34 35 36
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/pm_runtime.h>
#include <linux/component.h>
37
#include <linux/overflow.h>
M
Mark Yao 已提交
38 39 40 41 42 43 44

#include <linux/reset.h>
#include <linux/delay.h>

#include "rockchip_drm_drv.h"
#include "rockchip_drm_gem.h"
#include "rockchip_drm_fb.h"
45
#include "rockchip_drm_psr.h"
M
Mark Yao 已提交
46
#include "rockchip_drm_vop.h"
47
#include "rockchip_rgb.h"
M
Mark Yao 已提交
48

49
#define VOP_WIN_SET(vop, win, name, v) \
50
		vop_reg_set(vop, &win->phy->name, win->base, ~0, v, #name)
51
#define VOP_SCL_SET(vop, win, name, v) \
52
		vop_reg_set(vop, &win->phy->scl->name, win->base, ~0, v, #name)
53
#define VOP_SCL_SET_EXT(vop, win, name, v) \
54 55
		vop_reg_set(vop, &win->phy->scl->ext->name, \
			    win->base, ~0, v, #name)
56

57
#define VOP_WIN_YUV2YUV_SET(vop, win_yuv2yuv, name, v) \
58 59 60 61 62
	do { \
		if (win_yuv2yuv && win_yuv2yuv->name.mask) \
			vop_reg_set(vop, &win_yuv2yuv->name, 0, ~0, v, #name); \
	} while (0)

63
#define VOP_WIN_YUV2YUV_COEFFICIENT_SET(vop, win_yuv2yuv, name, v) \
64 65 66 67 68
	do { \
		if (win_yuv2yuv && win_yuv2yuv->phy->name.mask) \
			vop_reg_set(vop, &win_yuv2yuv->phy->name, win_yuv2yuv->base, ~0, v, #name); \
	} while (0)

69
#define VOP_INTR_SET_MASK(vop, name, mask, v) \
70 71 72 73
		vop_reg_set(vop, &vop->data->intr->name, 0, mask, v, #name)

#define VOP_REG_SET(vop, group, name, v) \
		    vop_reg_set(vop, &vop->data->group->name, 0, ~0, v, #name)
74

75 76
#define VOP_INTR_SET_TYPE(vop, name, type, v) \
	do { \
77
		int i, reg = 0, mask = 0; \
78
		for (i = 0; i < vop->data->intr->nintrs; i++) { \
79
			if (vop->data->intr->intrs[i] & type) { \
80
				reg |= (v) << i; \
81 82
				mask |= 1 << i; \
			} \
83
		} \
84
		VOP_INTR_SET_MASK(vop, name, mask, reg); \
85 86 87 88
	} while (0)
#define VOP_INTR_GET_TYPE(vop, name, type) \
		vop_get_intr_type(vop, &vop->data->intr->name, type)

89 90
#define VOP_WIN_GET(vop, win, name) \
		vop_read_reg(vop, win->offset, win->phy->name)
M
Mark Yao 已提交
91

92 93 94
#define VOP_WIN_HAS_REG(win, name) \
	(!!(win->phy->name.mask))

M
Mark Yao 已提交
95 96 97
#define VOP_WIN_GET_YRGBADDR(vop, win) \
		vop_readl(vop, win->base + win->phy->yrgb_mst.offset)

98 99 100
#define VOP_WIN_TO_INDEX(vop_win) \
	((vop_win) - (vop_win)->vop->win)

M
Mark Yao 已提交
101 102 103
#define to_vop(x) container_of(x, struct vop, crtc)
#define to_vop_win(x) container_of(x, struct vop_win, base)

104 105 106 107 108 109 110 111 112 113 114 115
/*
 * The coefficients of the following matrix are all fixed points.
 * The format is S2.10 for the 3x3 part of the matrix, and S9.12 for the offsets.
 * They are all represented in two's complement.
 */
static const uint32_t bt601_yuv2rgb[] = {
	0x4A8, 0x0,    0x662,
	0x4A8, 0x1E6F, 0x1CBF,
	0x4A8, 0x812,  0x0,
	0x321168, 0x0877CF, 0x2EB127
};

116 117 118 119
enum vop_pending {
	VOP_PENDING_FB_UNREF,
};

M
Mark Yao 已提交
120 121 122
struct vop_win {
	struct drm_plane base;
	const struct vop_win_data *data;
123
	const struct vop_win_yuv2yuv_data *yuv2yuv_data;
M
Mark Yao 已提交
124 125 126
	struct vop *vop;
};

127
struct rockchip_rgb;
M
Mark Yao 已提交
128 129 130 131
struct vop {
	struct drm_crtc crtc;
	struct device *dev;
	struct drm_device *drm_dev;
132
	bool is_enabled;
M
Mark Yao 已提交
133

134
	struct completion dsp_hold_completion;
135 136

	/* protected by dev->event_lock */
137
	struct drm_pending_vblank_event *event;
M
Mark Yao 已提交
138

139 140 141
	struct drm_flip_work fb_unref_work;
	unsigned long pending;

142 143
	struct completion line_flag_completion;

M
Mark Yao 已提交
144 145 146 147 148 149 150 151 152 153 154 155
	const struct vop_data *data;

	uint32_t *regsbak;
	void __iomem *regs;

	/* physical map length of vop register */
	uint32_t len;

	/* one time only one process allowed to config the register */
	spinlock_t reg_lock;
	/* lock vop irq reg */
	spinlock_t irq_lock;
Z
zain wang 已提交
156 157
	/* protects crtc enable/disable */
	struct mutex vop_lock;
M
Mark Yao 已提交
158 159 160 161 162 163 164 165 166 167 168 169 170

	unsigned int irq;

	/* vop AHP clk */
	struct clk *hclk;
	/* vop dclk */
	struct clk *dclk;
	/* vop share memory frequency */
	struct clk *aclk;

	/* vop dclk reset */
	struct reset_control *dclk_rst;

171 172 173
	/* optional internal rgb encoder */
	struct rockchip_rgb *rgb;

M
Mark Yao 已提交
174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193
	struct vop_win win[];
};

static inline void vop_writel(struct vop *vop, uint32_t offset, uint32_t v)
{
	writel(v, vop->regs + offset);
	vop->regsbak[offset >> 2] = v;
}

static inline uint32_t vop_readl(struct vop *vop, uint32_t offset)
{
	return readl(vop->regs + offset);
}

static inline uint32_t vop_read_reg(struct vop *vop, uint32_t base,
				    const struct vop_reg *reg)
{
	return (vop_readl(vop, base + reg->offset) >> reg->shift) & reg->mask;
}

194 195 196
static void vop_reg_set(struct vop *vop, const struct vop_reg *reg,
			uint32_t _offset, uint32_t _mask, uint32_t v,
			const char *reg_name)
M
Mark Yao 已提交
197
{
198 199 200
	int offset, mask, shift;

	if (!reg || !reg->mask) {
201
		DRM_DEV_DEBUG(vop->dev, "Warning: not support %s\n", reg_name);
202
		return;
203 204 205 206 207
	}

	offset = reg->offset + _offset;
	mask = reg->mask & _mask;
	shift = reg->shift;
M
Mark Yao 已提交
208

209
	if (reg->write_mask) {
210 211
		v = ((v << shift) & 0xffff) | (mask << (shift + 16));
	} else {
M
Mark Yao 已提交
212 213
		uint32_t cached_val = vop->regsbak[offset >> 2];

214 215
		v = (cached_val & ~(mask << shift)) | ((v & mask) << shift);
		vop->regsbak[offset >> 2] = v;
M
Mark Yao 已提交
216
	}
217

218
	if (reg->relaxed)
219 220 221
		writel_relaxed(v, vop->regs + offset);
	else
		writel(v, vop->regs + offset);
M
Mark Yao 已提交
222 223
}

224 225 226 227 228 229 230 231 232 233 234 235 236 237
static inline uint32_t vop_get_intr_type(struct vop *vop,
					 const struct vop_reg *reg, int type)
{
	uint32_t i, ret = 0;
	uint32_t regs = vop_read_reg(vop, 0, reg);

	for (i = 0; i < vop->data->intr->nintrs; i++) {
		if ((type & vop->data->intr->intrs[i]) && (regs & 1 << i))
			ret |= vop->data->intr->intrs[i];
	}

	return ret;
}

238 239
static inline void vop_cfg_done(struct vop *vop)
{
240
	VOP_REG_SET(vop, common, cfg_done, 1);
241 242
}

243 244 245 246 247 248 249 250 251 252 253 254 255
static bool has_rb_swapped(uint32_t format)
{
	switch (format) {
	case DRM_FORMAT_XBGR8888:
	case DRM_FORMAT_ABGR8888:
	case DRM_FORMAT_BGR888:
	case DRM_FORMAT_BGR565:
		return true;
	default:
		return false;
	}
}

M
Mark Yao 已提交
256 257 258 259 260
static enum vop_data_format vop_convert_format(uint32_t format)
{
	switch (format) {
	case DRM_FORMAT_XRGB8888:
	case DRM_FORMAT_ARGB8888:
261 262
	case DRM_FORMAT_XBGR8888:
	case DRM_FORMAT_ABGR8888:
M
Mark Yao 已提交
263 264
		return VOP_FMT_ARGB8888;
	case DRM_FORMAT_RGB888:
265
	case DRM_FORMAT_BGR888:
M
Mark Yao 已提交
266 267
		return VOP_FMT_RGB888;
	case DRM_FORMAT_RGB565:
268
	case DRM_FORMAT_BGR565:
M
Mark Yao 已提交
269 270 271 272 273 274 275 276
		return VOP_FMT_RGB565;
	case DRM_FORMAT_NV12:
		return VOP_FMT_YUV420SP;
	case DRM_FORMAT_NV16:
		return VOP_FMT_YUV422SP;
	case DRM_FORMAT_NV24:
		return VOP_FMT_YUV444SP;
	default:
277
		DRM_ERROR("unsupported format[%08x]\n", format);
M
Mark Yao 已提交
278 279 280 281
		return -EINVAL;
	}
}

282 283 284 285 286 287
static uint16_t scl_vop_cal_scale(enum scale_mode mode, uint32_t src,
				  uint32_t dst, bool is_horizontal,
				  int vsu_mode, int *vskiplines)
{
	uint16_t val = 1 << SCL_FT_DEFAULT_FIXPOINT_SHIFT;

288 289 290
	if (vskiplines)
		*vskiplines = 0;

291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317
	if (is_horizontal) {
		if (mode == SCALE_UP)
			val = GET_SCL_FT_BIC(src, dst);
		else if (mode == SCALE_DOWN)
			val = GET_SCL_FT_BILI_DN(src, dst);
	} else {
		if (mode == SCALE_UP) {
			if (vsu_mode == SCALE_UP_BIL)
				val = GET_SCL_FT_BILI_UP(src, dst);
			else
				val = GET_SCL_FT_BIC(src, dst);
		} else if (mode == SCALE_DOWN) {
			if (vskiplines) {
				*vskiplines = scl_get_vskiplines(src, dst);
				val = scl_get_bili_dn_vskip(src, dst,
							    *vskiplines);
			} else {
				val = GET_SCL_FT_BILI_DN(src, dst);
			}
		}
	}

	return val;
}

static void scl_vop_cal_scl_fac(struct vop *vop, const struct vop_win_data *win,
			     uint32_t src_w, uint32_t src_h, uint32_t dst_w,
318
			     uint32_t dst_h, const struct drm_format_info *info)
319 320 321 322
{
	uint16_t yrgb_hor_scl_mode, yrgb_ver_scl_mode;
	uint16_t cbcr_hor_scl_mode = SCALE_NONE;
	uint16_t cbcr_ver_scl_mode = SCALE_NONE;
323 324
	int hsub = drm_format_horz_chroma_subsampling(info->format);
	int vsub = drm_format_vert_chroma_subsampling(info->format);
325
	bool is_yuv = false;
326 327 328 329 330
	uint16_t cbcr_src_w = src_w / hsub;
	uint16_t cbcr_src_h = src_h / vsub;
	uint16_t vsu_mode;
	uint16_t lb_mode;
	uint32_t val;
331
	int vskiplines;
332

333 334 335
	if (info->is_yuv)
		is_yuv = true;

336
	if (dst_w > 3840) {
337
		DRM_DEV_ERROR(vop->dev, "Maximum dst width (3840) exceeded\n");
338 339 340
		return;
	}

M
Mark Yao 已提交
341 342 343 344 345 346 347
	if (!win->phy->scl->ext) {
		VOP_SCL_SET(vop, win, scale_yrgb_x,
			    scl_cal_scale2(src_w, dst_w));
		VOP_SCL_SET(vop, win, scale_yrgb_y,
			    scl_cal_scale2(src_h, dst_h));
		if (is_yuv) {
			VOP_SCL_SET(vop, win, scale_cbcr_x,
348
				    scl_cal_scale2(cbcr_src_w, dst_w));
M
Mark Yao 已提交
349
			VOP_SCL_SET(vop, win, scale_cbcr_y,
350
				    scl_cal_scale2(cbcr_src_h, dst_h));
M
Mark Yao 已提交
351 352 353 354
		}
		return;
	}

355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371
	yrgb_hor_scl_mode = scl_get_scl_mode(src_w, dst_w);
	yrgb_ver_scl_mode = scl_get_scl_mode(src_h, dst_h);

	if (is_yuv) {
		cbcr_hor_scl_mode = scl_get_scl_mode(cbcr_src_w, dst_w);
		cbcr_ver_scl_mode = scl_get_scl_mode(cbcr_src_h, dst_h);
		if (cbcr_hor_scl_mode == SCALE_DOWN)
			lb_mode = scl_vop_cal_lb_mode(dst_w, true);
		else
			lb_mode = scl_vop_cal_lb_mode(cbcr_src_w, true);
	} else {
		if (yrgb_hor_scl_mode == SCALE_DOWN)
			lb_mode = scl_vop_cal_lb_mode(dst_w, false);
		else
			lb_mode = scl_vop_cal_lb_mode(src_w, false);
	}

M
Mark Yao 已提交
372
	VOP_SCL_SET_EXT(vop, win, lb_mode, lb_mode);
373 374
	if (lb_mode == LB_RGB_3840X2) {
		if (yrgb_ver_scl_mode != SCALE_NONE) {
375
			DRM_DEV_ERROR(vop->dev, "not allow yrgb ver scale\n");
376 377 378
			return;
		}
		if (cbcr_ver_scl_mode != SCALE_NONE) {
379
			DRM_DEV_ERROR(vop->dev, "not allow cbcr ver scale\n");
380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395
			return;
		}
		vsu_mode = SCALE_UP_BIL;
	} else if (lb_mode == LB_RGB_2560X4) {
		vsu_mode = SCALE_UP_BIL;
	} else {
		vsu_mode = SCALE_UP_BIC;
	}

	val = scl_vop_cal_scale(yrgb_hor_scl_mode, src_w, dst_w,
				true, 0, NULL);
	VOP_SCL_SET(vop, win, scale_yrgb_x, val);
	val = scl_vop_cal_scale(yrgb_ver_scl_mode, src_h, dst_h,
				false, vsu_mode, &vskiplines);
	VOP_SCL_SET(vop, win, scale_yrgb_y, val);

M
Mark Yao 已提交
396 397
	VOP_SCL_SET_EXT(vop, win, vsd_yrgb_gt4, vskiplines == 4);
	VOP_SCL_SET_EXT(vop, win, vsd_yrgb_gt2, vskiplines == 2);
398

M
Mark Yao 已提交
399 400 401 402 403
	VOP_SCL_SET_EXT(vop, win, yrgb_hor_scl_mode, yrgb_hor_scl_mode);
	VOP_SCL_SET_EXT(vop, win, yrgb_ver_scl_mode, yrgb_ver_scl_mode);
	VOP_SCL_SET_EXT(vop, win, yrgb_hsd_mode, SCALE_DOWN_BIL);
	VOP_SCL_SET_EXT(vop, win, yrgb_vsd_mode, SCALE_DOWN_BIL);
	VOP_SCL_SET_EXT(vop, win, yrgb_vsu_mode, vsu_mode);
404 405 406 407 408 409 410 411
	if (is_yuv) {
		val = scl_vop_cal_scale(cbcr_hor_scl_mode, cbcr_src_w,
					dst_w, true, 0, NULL);
		VOP_SCL_SET(vop, win, scale_cbcr_x, val);
		val = scl_vop_cal_scale(cbcr_ver_scl_mode, cbcr_src_h,
					dst_h, false, vsu_mode, &vskiplines);
		VOP_SCL_SET(vop, win, scale_cbcr_y, val);

M
Mark Yao 已提交
412 413 414 415 416 417 418
		VOP_SCL_SET_EXT(vop, win, vsd_cbcr_gt4, vskiplines == 4);
		VOP_SCL_SET_EXT(vop, win, vsd_cbcr_gt2, vskiplines == 2);
		VOP_SCL_SET_EXT(vop, win, cbcr_hor_scl_mode, cbcr_hor_scl_mode);
		VOP_SCL_SET_EXT(vop, win, cbcr_ver_scl_mode, cbcr_ver_scl_mode);
		VOP_SCL_SET_EXT(vop, win, cbcr_hsd_mode, SCALE_DOWN_BIL);
		VOP_SCL_SET_EXT(vop, win, cbcr_vsd_mode, SCALE_DOWN_BIL);
		VOP_SCL_SET_EXT(vop, win, cbcr_vsu_mode, vsu_mode);
419 420 421
	}
}

422 423 424 425 426 427 428 429 430
static void vop_dsp_hold_valid_irq_enable(struct vop *vop)
{
	unsigned long flags;

	if (WARN_ON(!vop->is_enabled))
		return;

	spin_lock_irqsave(&vop->irq_lock, flags);

431
	VOP_INTR_SET_TYPE(vop, clear, DSP_HOLD_VALID_INTR, 1);
432
	VOP_INTR_SET_TYPE(vop, enable, DSP_HOLD_VALID_INTR, 1);
433 434 435 436 437 438 439 440 441 442 443 444 445

	spin_unlock_irqrestore(&vop->irq_lock, flags);
}

static void vop_dsp_hold_valid_irq_disable(struct vop *vop)
{
	unsigned long flags;

	if (WARN_ON(!vop->is_enabled))
		return;

	spin_lock_irqsave(&vop->irq_lock, flags);

446
	VOP_INTR_SET_TYPE(vop, enable, DSP_HOLD_VALID_INTR, 0);
447 448 449 450

	spin_unlock_irqrestore(&vop->irq_lock, flags);
}

451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486
/*
 * (1) each frame starts at the start of the Vsync pulse which is signaled by
 *     the "FRAME_SYNC" interrupt.
 * (2) the active data region of each frame ends at dsp_vact_end
 * (3) we should program this same number (dsp_vact_end) into dsp_line_frag_num,
 *      to get "LINE_FLAG" interrupt at the end of the active on screen data.
 *
 * VOP_INTR_CTRL0.dsp_line_frag_num = VOP_DSP_VACT_ST_END.dsp_vact_end
 * Interrupts
 * LINE_FLAG -------------------------------+
 * FRAME_SYNC ----+                         |
 *                |                         |
 *                v                         v
 *                | Vsync | Vbp |  Vactive  | Vfp |
 *                        ^     ^           ^     ^
 *                        |     |           |     |
 *                        |     |           |     |
 * dsp_vs_end ------------+     |           |     |   VOP_DSP_VTOTAL_VS_END
 * dsp_vact_start --------------+           |     |   VOP_DSP_VACT_ST_END
 * dsp_vact_end ----------------------------+     |   VOP_DSP_VACT_ST_END
 * dsp_total -------------------------------------+   VOP_DSP_VTOTAL_VS_END
 */
static bool vop_line_flag_irq_is_enabled(struct vop *vop)
{
	uint32_t line_flag_irq;
	unsigned long flags;

	spin_lock_irqsave(&vop->irq_lock, flags);

	line_flag_irq = VOP_INTR_GET_TYPE(vop, enable, LINE_FLAG_INTR);

	spin_unlock_irqrestore(&vop->irq_lock, flags);

	return !!line_flag_irq;
}

487
static void vop_line_flag_irq_enable(struct vop *vop)
488 489 490 491 492 493 494 495
{
	unsigned long flags;

	if (WARN_ON(!vop->is_enabled))
		return;

	spin_lock_irqsave(&vop->irq_lock, flags);

496
	VOP_INTR_SET_TYPE(vop, clear, LINE_FLAG_INTR, 1);
497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515
	VOP_INTR_SET_TYPE(vop, enable, LINE_FLAG_INTR, 1);

	spin_unlock_irqrestore(&vop->irq_lock, flags);
}

static void vop_line_flag_irq_disable(struct vop *vop)
{
	unsigned long flags;

	if (WARN_ON(!vop->is_enabled))
		return;

	spin_lock_irqsave(&vop->irq_lock, flags);

	VOP_INTR_SET_TYPE(vop, enable, LINE_FLAG_INTR, 0);

	spin_unlock_irqrestore(&vop->irq_lock, flags);
}

516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540
static int vop_core_clks_enable(struct vop *vop)
{
	int ret;

	ret = clk_enable(vop->hclk);
	if (ret < 0)
		return ret;

	ret = clk_enable(vop->aclk);
	if (ret < 0)
		goto err_disable_hclk;

	return 0;

err_disable_hclk:
	clk_disable(vop->hclk);
	return ret;
}

static void vop_core_clks_disable(struct vop *vop)
{
	clk_disable(vop->aclk);
	clk_disable(vop->hclk);
}

541 542 543 544 545 546 547 548 549 550 551 552
static void vop_win_disable(struct vop *vop, const struct vop_win_data *win)
{
	if (win->phy->scl && win->phy->scl->ext) {
		VOP_SCL_SET_EXT(vop, win, yrgb_hor_scl_mode, SCALE_NONE);
		VOP_SCL_SET_EXT(vop, win, yrgb_ver_scl_mode, SCALE_NONE);
		VOP_SCL_SET_EXT(vop, win, cbcr_hor_scl_mode, SCALE_NONE);
		VOP_SCL_SET_EXT(vop, win, cbcr_ver_scl_mode, SCALE_NONE);
	}

	VOP_WIN_SET(vop, win, enable, 0);
}

553
static int vop_enable(struct drm_crtc *crtc)
M
Mark Yao 已提交
554 555
{
	struct vop *vop = to_vop(crtc);
556
	int ret, i;
M
Mark Yao 已提交
557

558 559
	ret = pm_runtime_get_sync(vop->dev);
	if (ret < 0) {
560
		DRM_DEV_ERROR(vop->dev, "failed to get pm runtime: %d\n", ret);
561
		return ret;
562 563
	}

564
	ret = vop_core_clks_enable(vop);
565 566
	if (WARN_ON(ret < 0))
		goto err_put_pm_runtime;
M
Mark Yao 已提交
567 568

	ret = clk_enable(vop->dclk);
569
	if (WARN_ON(ret < 0))
570
		goto err_disable_core;
M
Mark Yao 已提交
571 572 573 574 575 576 577 578 579

	/*
	 * Slave iommu shares power, irq and clock with vop.  It was associated
	 * automatically with this master device via common driver code.
	 * Now that we have enabled the clock we attach it to the shared drm
	 * mapping.
	 */
	ret = rockchip_drm_dma_attach_device(vop->drm_dev, vop->dev);
	if (ret) {
580 581
		DRM_DEV_ERROR(vop->dev,
			      "failed to attach dma mapping, %d\n", ret);
582
		goto err_disable_dclk;
M
Mark Yao 已提交
583 584
	}

585 586 587 588
	spin_lock(&vop->reg_lock);
	for (i = 0; i < vop->len; i += 4)
		writel_relaxed(vop->regsbak[i / 4], vop->regs + i);

589 590 591 592 593 594 595 596 597
	/*
	 * We need to make sure that all windows are disabled before we
	 * enable the crtc. Otherwise we might try to scan from a destroyed
	 * buffer later.
	 */
	for (i = 0; i < vop->data->win_size; i++) {
		struct vop_win *vop_win = &vop->win[i];
		const struct vop_win_data *win = vop_win->data;

598
		vop_win_disable(vop, win);
599
	}
600
	spin_unlock(&vop->reg_lock);
601

602 603
	vop_cfg_done(vop);

604 605 606 607 608
	/*
	 * At here, vop clock & iommu is enable, R/W vop regs would be safe.
	 */
	vop->is_enabled = true;

M
Mark Yao 已提交
609 610
	spin_lock(&vop->reg_lock);

611
	VOP_REG_SET(vop, common, standby, 1);
M
Mark Yao 已提交
612 613 614

	spin_unlock(&vop->reg_lock);

615
	drm_crtc_vblank_on(crtc);
M
Mark Yao 已提交
616

617
	return 0;
M
Mark Yao 已提交
618 619 620

err_disable_dclk:
	clk_disable(vop->dclk);
621 622
err_disable_core:
	vop_core_clks_disable(vop);
623 624 625
err_put_pm_runtime:
	pm_runtime_put_sync(vop->dev);
	return ret;
M
Mark Yao 已提交
626 627
}

628 629
static void vop_crtc_atomic_disable(struct drm_crtc *crtc,
				    struct drm_crtc_state *old_state)
M
Mark Yao 已提交
630 631 632
{
	struct vop *vop = to_vop(crtc);

633 634
	WARN_ON(vop->event);

Z
zain wang 已提交
635
	mutex_lock(&vop->vop_lock);
636
	drm_crtc_vblank_off(crtc);
M
Mark Yao 已提交
637 638

	/*
639 640 641 642 643
	 * Vop standby will take effect at end of current frame,
	 * if dsp hold valid irq happen, it means standby complete.
	 *
	 * we must wait standby complete when we want to disable aclk,
	 * if not, memory bus maybe dead.
M
Mark Yao 已提交
644
	 */
645 646 647
	reinit_completion(&vop->dsp_hold_completion);
	vop_dsp_hold_valid_irq_enable(vop);

M
Mark Yao 已提交
648 649
	spin_lock(&vop->reg_lock);

650
	VOP_REG_SET(vop, common, standby, 1);
M
Mark Yao 已提交
651 652

	spin_unlock(&vop->reg_lock);
653

654 655 656 657
	wait_for_completion(&vop->dsp_hold_completion);

	vop_dsp_hold_valid_irq_disable(vop);

658
	vop->is_enabled = false;
659

M
Mark Yao 已提交
660
	/*
661
	 * vop standby complete, so iommu detach is safe.
M
Mark Yao 已提交
662 663 664
	 */
	rockchip_drm_dma_detach_device(vop->drm_dev, vop->dev);

665
	clk_disable(vop->dclk);
666
	vop_core_clks_disable(vop);
667
	pm_runtime_put(vop->dev);
Z
zain wang 已提交
668
	mutex_unlock(&vop->vop_lock);
669 670 671 672 673 674 675 676

	if (crtc->state->event && !crtc->state->active) {
		spin_lock_irq(&crtc->dev->event_lock);
		drm_crtc_send_vblank_event(crtc, crtc->state->event);
		spin_unlock_irq(&crtc->dev->event_lock);

		crtc->state->event = NULL;
	}
M
Mark Yao 已提交
677 678
}

679
static void vop_plane_destroy(struct drm_plane *plane)
M
Mark Yao 已提交
680
{
681
	drm_plane_cleanup(plane);
M
Mark Yao 已提交
682 683
}

684 685
static int vop_plane_atomic_check(struct drm_plane *plane,
			   struct drm_plane_state *state)
M
Mark Yao 已提交
686
{
687
	struct drm_crtc *crtc = state->crtc;
688
	struct drm_crtc_state *crtc_state;
689
	struct drm_framebuffer *fb = state->fb;
M
Mark Yao 已提交
690 691 692
	struct vop_win *vop_win = to_vop_win(plane);
	const struct vop_win_data *win = vop_win->data;
	int ret;
693 694 695 696
	int min_scale = win->phy->scl ? FRAC_16_16(1, 8) :
					DRM_PLANE_HELPER_NO_SCALING;
	int max_scale = win->phy->scl ? FRAC_16_16(8, 1) :
					DRM_PLANE_HELPER_NO_SCALING;
M
Mark Yao 已提交
697

698
	if (!crtc || !fb)
T
Tomasz Figa 已提交
699
		return 0;
700 701 702 703 704

	crtc_state = drm_atomic_get_existing_crtc_state(state->state, crtc);
	if (WARN_ON(!crtc_state))
		return -EINVAL;

705
	ret = drm_atomic_helper_check_plane_state(state, crtc_state,
706 707
						  min_scale, max_scale,
						  true, true);
M
Mark Yao 已提交
708 709 710
	if (ret)
		return ret;

711
	if (!state->visible)
T
Tomasz Figa 已提交
712
		return 0;
M
Mark Yao 已提交
713

V
Ville Syrjälä 已提交
714
	ret = vop_convert_format(fb->format->format);
T
Tomasz Figa 已提交
715 716
	if (ret < 0)
		return ret;
717

718 719 720 721
	/*
	 * Src.x1 can be odd when do clip, but yuv plane start point
	 * need align with 2 pixel.
	 */
722
	if (fb->format->is_yuv && ((state->src.x1 >> 16) % 2)) {
723
		DRM_ERROR("Invalid Source: Yuv format not support odd xpos\n");
M
Mark Yao 已提交
724
		return -EINVAL;
725
	}
M
Mark Yao 已提交
726

727 728 729 730 731
	if (fb->format->is_yuv && state->rotation & DRM_MODE_REFLECT_Y) {
		DRM_ERROR("Invalid Source: Yuv format does not support this rotation\n");
		return -EINVAL;
	}

732 733
	return 0;
}
M
Mark Yao 已提交
734

735 736 737 738 739 740
static void vop_plane_atomic_disable(struct drm_plane *plane,
				     struct drm_plane_state *old_state)
{
	struct vop_win *vop_win = to_vop_win(plane);
	const struct vop_win_data *win = vop_win->data;
	struct vop *vop = to_vop(old_state->crtc);
M
Mark Yao 已提交
741

742 743
	if (!old_state->crtc)
		return;
M
Mark Yao 已提交
744

745
	spin_lock(&vop->reg_lock);
M
Mark Yao 已提交
746

747
	vop_win_disable(vop, win);
748

749 750
	spin_unlock(&vop->reg_lock);
}
751

752 753 754 755 756 757 758
static void vop_plane_atomic_update(struct drm_plane *plane,
		struct drm_plane_state *old_state)
{
	struct drm_plane_state *state = plane->state;
	struct drm_crtc *crtc = state->crtc;
	struct vop_win *vop_win = to_vop_win(plane);
	const struct vop_win_data *win = vop_win->data;
759
	const struct vop_win_yuv2yuv_data *win_yuv2yuv = vop_win->yuv2yuv_data;
760 761 762 763 764
	struct vop *vop = to_vop(state->crtc);
	struct drm_framebuffer *fb = state->fb;
	unsigned int actual_w, actual_h;
	unsigned int dsp_stx, dsp_sty;
	uint32_t act_info, dsp_info, dsp_st;
765 766
	struct drm_rect *src = &state->src;
	struct drm_rect *dest = &state->dst;
767 768 769 770 771 772
	struct drm_gem_object *obj, *uv_obj;
	struct rockchip_gem_object *rk_obj, *rk_uv_obj;
	unsigned long offset;
	dma_addr_t dma_addr;
	uint32_t val;
	bool rb_swap;
773
	int win_index = VOP_WIN_TO_INDEX(vop_win);
T
Tomasz Figa 已提交
774
	int format;
775 776
	int is_yuv = fb->format->is_yuv;
	int i;
777

M
Mark Yao 已提交
778
	/*
779
	 * can't update plane when vop is disabled.
M
Mark Yao 已提交
780
	 */
781
	if (WARN_ON(!crtc))
782
		return;
M
Mark Yao 已提交
783

784 785
	if (WARN_ON(!vop->is_enabled))
		return;
M
Mark Yao 已提交
786

T
Tomasz Figa 已提交
787
	if (!state->visible) {
788 789
		vop_plane_atomic_disable(plane, old_state);
		return;
M
Mark Yao 已提交
790
	}
791

792
	obj = fb->obj[0];
793 794 795 796 797 798 799 800 801 802 803 804 805
	rk_obj = to_rockchip_obj(obj);

	actual_w = drm_rect_width(src) >> 16;
	actual_h = drm_rect_height(src) >> 16;
	act_info = (actual_h - 1) << 16 | ((actual_w - 1) & 0xffff);

	dsp_info = (drm_rect_height(dest) - 1) << 16;
	dsp_info |= (drm_rect_width(dest) - 1) & 0xffff;

	dsp_stx = dest->x1 + crtc->mode.htotal - crtc->mode.hsync_start;
	dsp_sty = dest->y1 + crtc->mode.vtotal - crtc->mode.vsync_start;
	dsp_st = dsp_sty << 16 | (dsp_stx & 0xffff);

806
	offset = (src->x1 >> 16) * fb->format->cpp[0];
807
	offset += (src->y1 >> 16) * fb->pitches[0];
T
Tomasz Figa 已提交
808 809
	dma_addr = rk_obj->dma_addr + offset + fb->offsets[0];

810 811 812 813 814 815 816
	/*
	 * For y-mirroring we need to move address
	 * to the beginning of the last line.
	 */
	if (state->rotation & DRM_MODE_REFLECT_Y)
		dma_addr += (actual_h - 1) * fb->pitches[0];

V
Ville Syrjälä 已提交
817
	format = vop_convert_format(fb->format->format);
M
Mark Yao 已提交
818 819 820

	spin_lock(&vop->reg_lock);

T
Tomasz Figa 已提交
821
	VOP_WIN_SET(vop, win, format, format);
822
	VOP_WIN_SET(vop, win, yrgb_vir, DIV_ROUND_UP(fb->pitches[0], 4));
T
Tomasz Figa 已提交
823
	VOP_WIN_SET(vop, win, yrgb_mst, dma_addr);
824
	VOP_WIN_YUV2YUV_SET(vop, win_yuv2yuv, y2r_en, is_yuv);
825 826 827 828
	VOP_WIN_SET(vop, win, y_mir_en,
		    (state->rotation & DRM_MODE_REFLECT_Y) ? 1 : 0);
	VOP_WIN_SET(vop, win, x_mir_en,
		    (state->rotation & DRM_MODE_REFLECT_X) ? 1 : 0);
829 830

	if (is_yuv) {
V
Ville Syrjälä 已提交
831 832
		int hsub = drm_format_horz_chroma_subsampling(fb->format->format);
		int vsub = drm_format_vert_chroma_subsampling(fb->format->format);
833
		int bpp = fb->format->cpp[1];
834

835
		uv_obj = fb->obj[1];
836 837 838 839 840 841
		rk_uv_obj = to_rockchip_obj(uv_obj);

		offset = (src->x1 >> 16) * bpp / hsub;
		offset += (src->y1 >> 16) * fb->pitches[1] / vsub;

		dma_addr = rk_uv_obj->dma_addr + offset + fb->offsets[1];
842
		VOP_WIN_SET(vop, win, uv_vir, DIV_ROUND_UP(fb->pitches[1], 4));
843
		VOP_WIN_SET(vop, win, uv_mst, dma_addr);
844 845 846 847 848 849 850

		for (i = 0; i < NUM_YUV2YUV_COEFFICIENTS; i++) {
			VOP_WIN_YUV2YUV_COEFFICIENT_SET(vop,
							win_yuv2yuv,
							y2r_coefficients[i],
							bt601_yuv2rgb[i]);
		}
851
	}
852 853 854

	if (win->phy->scl)
		scl_vop_cal_scl_fac(vop, win, actual_w, actual_h,
855
				    drm_rect_width(dest), drm_rect_height(dest),
856
				    fb->format);
857

858 859 860
	VOP_WIN_SET(vop, win, act_info, act_info);
	VOP_WIN_SET(vop, win, dsp_info, dsp_info);
	VOP_WIN_SET(vop, win, dsp_st, dsp_st);
861

V
Ville Syrjälä 已提交
862
	rb_swap = has_rb_swapped(fb->format->format);
863
	VOP_WIN_SET(vop, win, rb_swap, rb_swap);
M
Mark Yao 已提交
864

865 866 867 868 869 870 871 872
	/*
	 * Blending win0 with the background color doesn't seem to work
	 * correctly. We only get the background color, no matter the contents
	 * of the win0 framebuffer.  However, blending pre-multiplied color
	 * with the default opaque black default background color is a no-op,
	 * so we can just disable blending to get the correct result.
	 */
	if (fb->format->has_alpha && win_index > 0) {
M
Mark Yao 已提交
873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888
		VOP_WIN_SET(vop, win, dst_alpha_ctl,
			    DST_FACTOR_M0(ALPHA_SRC_INVERSE));
		val = SRC_ALPHA_EN(1) | SRC_COLOR_M0(ALPHA_SRC_PRE_MUL) |
			SRC_ALPHA_M0(ALPHA_STRAIGHT) |
			SRC_BLEND_M0(ALPHA_PER_PIX) |
			SRC_ALPHA_CAL_M0(ALPHA_NO_SATURATION) |
			SRC_FACTOR_M0(ALPHA_ONE);
		VOP_WIN_SET(vop, win, src_alpha_ctl, val);
	} else {
		VOP_WIN_SET(vop, win, src_alpha_ctl, SRC_ALPHA_EN(0));
	}

	VOP_WIN_SET(vop, win, enable, 1);
	spin_unlock(&vop->reg_lock);
}

889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959
static int vop_plane_atomic_async_check(struct drm_plane *plane,
					struct drm_plane_state *state)
{
	struct vop_win *vop_win = to_vop_win(plane);
	const struct vop_win_data *win = vop_win->data;
	int min_scale = win->phy->scl ? FRAC_16_16(1, 8) :
					DRM_PLANE_HELPER_NO_SCALING;
	int max_scale = win->phy->scl ? FRAC_16_16(8, 1) :
					DRM_PLANE_HELPER_NO_SCALING;
	struct drm_crtc_state *crtc_state;

	if (plane != state->crtc->cursor)
		return -EINVAL;

	if (!plane->state)
		return -EINVAL;

	if (!plane->state->fb)
		return -EINVAL;

	if (state->state)
		crtc_state = drm_atomic_get_existing_crtc_state(state->state,
								state->crtc);
	else /* Special case for asynchronous cursor updates. */
		crtc_state = plane->crtc->state;

	return drm_atomic_helper_check_plane_state(plane->state, crtc_state,
						   min_scale, max_scale,
						   true, true);
}

static void vop_plane_atomic_async_update(struct drm_plane *plane,
					  struct drm_plane_state *new_state)
{
	struct vop *vop = to_vop(plane->state->crtc);
	struct drm_plane_state *plane_state;

	plane_state = plane->funcs->atomic_duplicate_state(plane);
	plane_state->crtc_x = new_state->crtc_x;
	plane_state->crtc_y = new_state->crtc_y;
	plane_state->crtc_h = new_state->crtc_h;
	plane_state->crtc_w = new_state->crtc_w;
	plane_state->src_x = new_state->src_x;
	plane_state->src_y = new_state->src_y;
	plane_state->src_h = new_state->src_h;
	plane_state->src_w = new_state->src_w;

	if (plane_state->fb != new_state->fb)
		drm_atomic_set_fb_for_plane(plane_state, new_state->fb);

	swap(plane_state, plane->state);

	if (plane->state->fb && plane->state->fb != new_state->fb) {
		drm_framebuffer_get(plane->state->fb);
		WARN_ON(drm_crtc_vblank_get(plane->state->crtc) != 0);
		drm_flip_work_queue(&vop->fb_unref_work, plane->state->fb);
		set_bit(VOP_PENDING_FB_UNREF, &vop->pending);
	}

	if (vop->is_enabled) {
		rockchip_drm_psr_inhibit_get_state(new_state->state);
		vop_plane_atomic_update(plane, plane->state);
		spin_lock(&vop->reg_lock);
		vop_cfg_done(vop);
		spin_unlock(&vop->reg_lock);
		rockchip_drm_psr_inhibit_put_state(new_state->state);
	}

	plane->funcs->atomic_destroy_state(plane, plane_state);
}

960 961 962 963
static const struct drm_plane_helper_funcs plane_helper_funcs = {
	.atomic_check = vop_plane_atomic_check,
	.atomic_update = vop_plane_atomic_update,
	.atomic_disable = vop_plane_atomic_disable,
964 965
	.atomic_async_check = vop_plane_atomic_async_check,
	.atomic_async_update = vop_plane_atomic_async_update,
966
	.prepare_fb = drm_gem_fb_prepare_fb,
967
};
M
Mark Yao 已提交
968 969

static const struct drm_plane_funcs vop_plane_funcs = {
970 971
	.update_plane	= drm_atomic_helper_update_plane,
	.disable_plane	= drm_atomic_helper_disable_plane,
M
Mark Yao 已提交
972
	.destroy = vop_plane_destroy,
T
Tomasz Figa 已提交
973 974 975
	.reset = drm_atomic_helper_plane_reset,
	.atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state,
	.atomic_destroy_state = drm_atomic_helper_plane_destroy_state,
M
Mark Yao 已提交
976 977 978 979 980 981 982
};

static int vop_crtc_enable_vblank(struct drm_crtc *crtc)
{
	struct vop *vop = to_vop(crtc);
	unsigned long flags;

983
	if (WARN_ON(!vop->is_enabled))
M
Mark Yao 已提交
984 985 986 987
		return -EPERM;

	spin_lock_irqsave(&vop->irq_lock, flags);

988
	VOP_INTR_SET_TYPE(vop, clear, FS_INTR, 1);
989
	VOP_INTR_SET_TYPE(vop, enable, FS_INTR, 1);
M
Mark Yao 已提交
990 991 992 993 994 995 996 997 998 999 1000

	spin_unlock_irqrestore(&vop->irq_lock, flags);

	return 0;
}

static void vop_crtc_disable_vblank(struct drm_crtc *crtc)
{
	struct vop *vop = to_vop(crtc);
	unsigned long flags;

1001
	if (WARN_ON(!vop->is_enabled))
M
Mark Yao 已提交
1002
		return;
1003

M
Mark Yao 已提交
1004
	spin_lock_irqsave(&vop->irq_lock, flags);
1005 1006 1007

	VOP_INTR_SET_TYPE(vop, enable, FS_INTR, 0);

M
Mark Yao 已提交
1008 1009 1010 1011 1012 1013 1014
	spin_unlock_irqrestore(&vop->irq_lock, flags);
}

static bool vop_crtc_mode_fixup(struct drm_crtc *crtc,
				const struct drm_display_mode *mode,
				struct drm_display_mode *adjusted_mode)
{
1015 1016 1017 1018 1019
	struct vop *vop = to_vop(crtc);

	adjusted_mode->clock =
		clk_round_rate(vop->dclk, mode->clock * 1000) / 1000;

M
Mark Yao 已提交
1020 1021 1022
	return true;
}

1023 1024
static void vop_crtc_atomic_enable(struct drm_crtc *crtc,
				   struct drm_crtc_state *old_state)
M
Mark Yao 已提交
1025 1026
{
	struct vop *vop = to_vop(crtc);
1027
	const struct vop_data *vop_data = vop->data;
1028
	struct rockchip_crtc_state *s = to_rockchip_crtc_state(crtc->state);
1029
	struct drm_display_mode *adjusted_mode = &crtc->state->adjusted_mode;
M
Mark Yao 已提交
1030 1031 1032 1033 1034 1035 1036 1037 1038 1039
	u16 hsync_len = adjusted_mode->hsync_end - adjusted_mode->hsync_start;
	u16 hdisplay = adjusted_mode->hdisplay;
	u16 htotal = adjusted_mode->htotal;
	u16 hact_st = adjusted_mode->htotal - adjusted_mode->hsync_start;
	u16 hact_end = hact_st + hdisplay;
	u16 vdisplay = adjusted_mode->vdisplay;
	u16 vtotal = adjusted_mode->vtotal;
	u16 vsync_len = adjusted_mode->vsync_end - adjusted_mode->vsync_start;
	u16 vact_st = adjusted_mode->vtotal - adjusted_mode->vsync_start;
	u16 vact_end = vact_st + vdisplay;
1040
	uint32_t pin_pol, val;
1041
	int dither_bpc = s->output_bpc ? s->output_bpc : 10;
1042
	int ret;
M
Mark Yao 已提交
1043

Z
zain wang 已提交
1044 1045
	mutex_lock(&vop->vop_lock);

1046 1047
	WARN_ON(vop->event);

1048 1049
	ret = vop_enable(crtc);
	if (ret) {
Z
zain wang 已提交
1050
		mutex_unlock(&vop->vop_lock);
1051 1052 1053 1054
		DRM_DEV_ERROR(vop->dev, "Failed to enable vop (%d)\n", ret);
		return;
	}

1055
	pin_pol = BIT(DCLK_INVERT);
1056 1057 1058 1059
	pin_pol |= (adjusted_mode->flags & DRM_MODE_FLAG_PHSYNC) ?
		   BIT(HSYNC_POSITIVE) : 0;
	pin_pol |= (adjusted_mode->flags & DRM_MODE_FLAG_PVSYNC) ?
		   BIT(VSYNC_POSITIVE) : 0;
1060
	VOP_REG_SET(vop, output, pin_pol, pin_pol);
1061
	VOP_REG_SET(vop, output, mipi_dual_channel_en, 0);
1062

1063 1064
	switch (s->output_type) {
	case DRM_MODE_CONNECTOR_LVDS:
1065 1066
		VOP_REG_SET(vop, output, rgb_en, 1);
		VOP_REG_SET(vop, output, rgb_pin_pol, pin_pol);
1067 1068
		break;
	case DRM_MODE_CONNECTOR_eDP:
1069 1070
		VOP_REG_SET(vop, output, edp_pin_pol, pin_pol);
		VOP_REG_SET(vop, output, edp_en, 1);
1071 1072
		break;
	case DRM_MODE_CONNECTOR_HDMIA:
1073 1074
		VOP_REG_SET(vop, output, hdmi_pin_pol, pin_pol);
		VOP_REG_SET(vop, output, hdmi_en, 1);
1075 1076
		break;
	case DRM_MODE_CONNECTOR_DSI:
1077 1078
		VOP_REG_SET(vop, output, mipi_pin_pol, pin_pol);
		VOP_REG_SET(vop, output, mipi_en, 1);
1079 1080
		VOP_REG_SET(vop, output, mipi_dual_channel_en,
			    !!(s->output_flags & ROCKCHIP_OUTPUT_DSI_DUAL));
1081
		break;
1082 1083
	case DRM_MODE_CONNECTOR_DisplayPort:
		pin_pol &= ~BIT(DCLK_INVERT);
1084 1085
		VOP_REG_SET(vop, output, dp_pin_pol, pin_pol);
		VOP_REG_SET(vop, output, dp_en, 1);
1086
		break;
1087
	default:
1088 1089
		DRM_DEV_ERROR(vop->dev, "unsupported connector_type [%d]\n",
			      s->output_type);
1090
	}
1091 1092 1093 1094 1095 1096 1097

	/*
	 * if vop is not support RGB10 output, need force RGB10 to RGB888.
	 */
	if (s->output_mode == ROCKCHIP_OUT_MODE_AAAA &&
	    !(vop_data->feature & VOP_FEATURE_OUTPUT_RGB10))
		s->output_mode = ROCKCHIP_OUT_MODE_P888;
1098

1099
	if (s->output_mode == ROCKCHIP_OUT_MODE_AAAA && dither_bpc <= 8)
1100 1101 1102 1103
		VOP_REG_SET(vop, common, pre_dither_down, 1);
	else
		VOP_REG_SET(vop, common, pre_dither_down, 0);

1104 1105 1106 1107 1108 1109 1110 1111
	if (dither_bpc == 6) {
		VOP_REG_SET(vop, common, dither_down_sel, DITHER_DOWN_ALLEGRO);
		VOP_REG_SET(vop, common, dither_down_mode, RGB888_TO_RGB666);
		VOP_REG_SET(vop, common, dither_down_en, 1);
	} else {
		VOP_REG_SET(vop, common, dither_down_en, 0);
	}

1112
	VOP_REG_SET(vop, common, out_mode, s->output_mode);
M
Mark Yao 已提交
1113

1114
	VOP_REG_SET(vop, modeset, htotal_pw, (htotal << 16) | hsync_len);
M
Mark Yao 已提交
1115 1116
	val = hact_st << 16;
	val |= hact_end;
1117 1118
	VOP_REG_SET(vop, modeset, hact_st_end, val);
	VOP_REG_SET(vop, modeset, hpost_st_end, val);
M
Mark Yao 已提交
1119

1120
	VOP_REG_SET(vop, modeset, vtotal_pw, (vtotal << 16) | vsync_len);
M
Mark Yao 已提交
1121 1122
	val = vact_st << 16;
	val |= vact_end;
1123 1124
	VOP_REG_SET(vop, modeset, vact_st_end, val);
	VOP_REG_SET(vop, modeset, vpost_st_end, val);
M
Mark Yao 已提交
1125

1126
	VOP_REG_SET(vop, intr, line_flag_num[0], vact_end);
1127

M
Mark Yao 已提交
1128
	clk_set_rate(vop->dclk, adjusted_mode->clock * 1000);
M
Mark Yao 已提交
1129

1130
	VOP_REG_SET(vop, common, standby, 0);
Z
zain wang 已提交
1131
	mutex_unlock(&vop->vop_lock);
M
Mark Yao 已提交
1132 1133
}

1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159
static bool vop_fs_irq_is_pending(struct vop *vop)
{
	return VOP_INTR_GET_TYPE(vop, status, FS_INTR);
}

static void vop_wait_for_irq_handler(struct vop *vop)
{
	bool pending;
	int ret;

	/*
	 * Spin until frame start interrupt status bit goes low, which means
	 * that interrupt handler was invoked and cleared it. The timeout of
	 * 10 msecs is really too long, but it is just a safety measure if
	 * something goes really wrong. The wait will only happen in the very
	 * unlikely case of a vblank happening exactly at the same time and
	 * shouldn't exceed microseconds range.
	 */
	ret = readx_poll_timeout_atomic(vop_fs_irq_is_pending, vop, pending,
					!pending, 0, 10 * 1000);
	if (ret)
		DRM_DEV_ERROR(vop->dev, "VOP vblank IRQ stuck for 10 ms\n");

	synchronize_irq(vop->irq);
}

1160 1161
static void vop_crtc_atomic_flush(struct drm_crtc *crtc,
				  struct drm_crtc_state *old_crtc_state)
M
Mark Yao 已提交
1162
{
1163
	struct drm_atomic_state *old_state = old_crtc_state->state;
1164
	struct drm_plane_state *old_plane_state, *new_plane_state;
M
Mark Yao 已提交
1165
	struct vop *vop = to_vop(crtc);
1166 1167
	struct drm_plane *plane;
	int i;
M
Mark Yao 已提交
1168

1169 1170
	if (WARN_ON(!vop->is_enabled))
		return;
M
Mark Yao 已提交
1171

1172
	spin_lock(&vop->reg_lock);
M
Mark Yao 已提交
1173

1174
	vop_cfg_done(vop);
M
Mark Yao 已提交
1175

1176
	spin_unlock(&vop->reg_lock);
1177 1178 1179 1180 1181 1182 1183

	/*
	 * There is a (rather unlikely) possiblity that a vblank interrupt
	 * fired before we set the cfg_done bit. To avoid spuriously
	 * signalling flip completion we need to wait for it to finish.
	 */
	vop_wait_for_irq_handler(vop);
1184

1185 1186 1187 1188 1189 1190 1191 1192 1193 1194
	spin_lock_irq(&crtc->dev->event_lock);
	if (crtc->state->event) {
		WARN_ON(drm_crtc_vblank_get(crtc) != 0);
		WARN_ON(vop->event);

		vop->event = crtc->state->event;
		crtc->state->event = NULL;
	}
	spin_unlock_irq(&crtc->dev->event_lock);

1195 1196
	for_each_oldnew_plane_in_state(old_state, plane, old_plane_state,
				       new_plane_state, i) {
1197 1198 1199
		if (!old_plane_state->fb)
			continue;

1200
		if (old_plane_state->fb == new_plane_state->fb)
1201 1202
			continue;

1203
		drm_framebuffer_get(old_plane_state->fb);
J
John Keeping 已提交
1204
		WARN_ON(drm_crtc_vblank_get(crtc) != 0);
1205 1206 1207
		drm_flip_work_queue(&vop->fb_unref_work, old_plane_state->fb);
		set_bit(VOP_PENDING_FB_UNREF, &vop->pending);
	}
M
Mark Yao 已提交
1208 1209
}

1210 1211 1212
static const struct drm_crtc_helper_funcs vop_crtc_helper_funcs = {
	.mode_fixup = vop_crtc_mode_fixup,
	.atomic_flush = vop_crtc_atomic_flush,
1213
	.atomic_enable = vop_crtc_atomic_enable,
1214
	.atomic_disable = vop_crtc_atomic_disable,
1215 1216
};

M
Mark Yao 已提交
1217 1218 1219 1220 1221
static void vop_crtc_destroy(struct drm_crtc *crtc)
{
	drm_crtc_cleanup(crtc);
}

1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238
static struct drm_crtc_state *vop_crtc_duplicate_state(struct drm_crtc *crtc)
{
	struct rockchip_crtc_state *rockchip_state;

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

	__drm_atomic_helper_crtc_duplicate_state(crtc, &rockchip_state->base);
	return &rockchip_state->base;
}

static void vop_crtc_destroy_state(struct drm_crtc *crtc,
				   struct drm_crtc_state *state)
{
	struct rockchip_crtc_state *s = to_rockchip_crtc_state(state);

1239
	__drm_atomic_helper_crtc_destroy_state(&s->base);
1240 1241 1242
	kfree(s);
}

1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253
static void vop_crtc_reset(struct drm_crtc *crtc)
{
	struct rockchip_crtc_state *crtc_state =
		kzalloc(sizeof(*crtc_state), GFP_KERNEL);

	if (crtc->state)
		vop_crtc_destroy_state(crtc, crtc->state);

	__drm_atomic_helper_crtc_reset(crtc, &crtc_state->base);
}

1254
#ifdef CONFIG_DRM_ANALOGIX_DP
1255 1256 1257
static struct drm_connector *vop_get_edp_connector(struct vop *vop)
{
	struct drm_connector *connector;
1258
	struct drm_connector_list_iter conn_iter;
1259

1260 1261
	drm_connector_list_iter_begin(vop->drm_dev, &conn_iter);
	drm_for_each_connector_iter(connector, &conn_iter) {
1262
		if (connector->connector_type == DRM_MODE_CONNECTOR_eDP) {
1263
			drm_connector_list_iter_end(&conn_iter);
1264 1265
			return connector;
		}
1266 1267
	}
	drm_connector_list_iter_end(&conn_iter);
1268 1269 1270 1271 1272

	return NULL;
}

static int vop_crtc_set_crc_source(struct drm_crtc *crtc,
1273
				   const char *source_name)
1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291
{
	struct vop *vop = to_vop(crtc);
	struct drm_connector *connector;
	int ret;

	connector = vop_get_edp_connector(vop);
	if (!connector)
		return -EINVAL;

	if (source_name && strcmp(source_name, "auto") == 0)
		ret = analogix_dp_start_crc(connector);
	else if (!source_name)
		ret = analogix_dp_stop_crc(connector);
	else
		ret = -EINVAL;

	return ret;
}
1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303

static int
vop_crtc_verify_crc_source(struct drm_crtc *crtc, const char *source_name,
			   size_t *values_cnt)
{
	if (source_name && strcmp(source_name, "auto") != 0)
		return -EINVAL;

	*values_cnt = 3;
	return 0;
}

1304 1305
#else
static int vop_crtc_set_crc_source(struct drm_crtc *crtc,
1306
				   const char *source_name)
1307 1308 1309
{
	return -ENODEV;
}
1310 1311 1312 1313 1314 1315 1316

static int
vop_crtc_verify_crc_source(struct drm_crtc *crtc, const char *source_name,
			   size_t *values_cnt)
{
	return -ENODEV;
}
1317
#endif
1318

M
Mark Yao 已提交
1319
static const struct drm_crtc_funcs vop_crtc_funcs = {
1320 1321
	.set_config = drm_atomic_helper_set_config,
	.page_flip = drm_atomic_helper_page_flip,
M
Mark Yao 已提交
1322
	.destroy = vop_crtc_destroy,
1323
	.reset = vop_crtc_reset,
1324 1325
	.atomic_duplicate_state = vop_crtc_duplicate_state,
	.atomic_destroy_state = vop_crtc_destroy_state,
1326 1327
	.enable_vblank = vop_crtc_enable_vblank,
	.disable_vblank = vop_crtc_disable_vblank,
1328
	.set_crc_source = vop_crtc_set_crc_source,
1329
	.verify_crc_source = vop_crtc_verify_crc_source,
M
Mark Yao 已提交
1330 1331
};

1332 1333 1334 1335 1336 1337
static void vop_fb_unref_worker(struct drm_flip_work *work, void *val)
{
	struct vop *vop = container_of(work, struct vop, fb_unref_work);
	struct drm_framebuffer *fb = val;

	drm_crtc_vblank_put(&vop->crtc);
1338
	drm_framebuffer_put(fb);
1339 1340
}

1341
static void vop_handle_vblank(struct vop *vop)
M
Mark Yao 已提交
1342
{
1343 1344
	struct drm_device *drm = vop->drm_dev;
	struct drm_crtc *crtc = &vop->crtc;
M
Mark Yao 已提交
1345

1346
	spin_lock(&drm->event_lock);
1347 1348
	if (vop->event) {
		drm_crtc_send_vblank_event(crtc, vop->event);
1349
		drm_crtc_vblank_put(crtc);
1350
		vop->event = NULL;
1351
	}
1352
	spin_unlock(&drm->event_lock);
1353

1354 1355
	if (test_and_clear_bit(VOP_PENDING_FB_UNREF, &vop->pending))
		drm_flip_work_commit(&vop->fb_unref_work, system_unbound_wq);
M
Mark Yao 已提交
1356 1357 1358 1359 1360
}

static irqreturn_t vop_isr(int irq, void *data)
{
	struct vop *vop = data;
1361
	struct drm_crtc *crtc = &vop->crtc;
1362
	uint32_t active_irqs;
1363
	int ret = IRQ_NONE;
M
Mark Yao 已提交
1364

1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376
	/*
	 * The irq is shared with the iommu. If the runtime-pm state of the
	 * vop-device is disabled the irq has to be targeted at the iommu.
	 */
	if (!pm_runtime_get_if_in_use(vop->dev))
		return IRQ_NONE;

	if (vop_core_clks_enable(vop)) {
		DRM_DEV_ERROR_RATELIMITED(vop->dev, "couldn't enable clocks\n");
		goto out;
	}

M
Mark Yao 已提交
1377
	/*
1378
	 * interrupt register has interrupt status, enable and clear bits, we
M
Mark Yao 已提交
1379 1380
	 * must hold irq_lock to avoid a race with enable/disable_vblank().
	*/
1381
	spin_lock(&vop->irq_lock);
1382 1383

	active_irqs = VOP_INTR_GET_TYPE(vop, status, INTR_MASK);
M
Mark Yao 已提交
1384 1385
	/* Clear all active interrupt sources */
	if (active_irqs)
1386 1387
		VOP_INTR_SET_TYPE(vop, clear, active_irqs, 1);

1388
	spin_unlock(&vop->irq_lock);
M
Mark Yao 已提交
1389 1390 1391

	/* This is expected for vop iommu irqs, since the irq is shared */
	if (!active_irqs)
1392
		goto out_disable;
M
Mark Yao 已提交
1393

1394 1395 1396 1397
	if (active_irqs & DSP_HOLD_VALID_INTR) {
		complete(&vop->dsp_hold_completion);
		active_irqs &= ~DSP_HOLD_VALID_INTR;
		ret = IRQ_HANDLED;
M
Mark Yao 已提交
1398 1399
	}

1400 1401 1402 1403 1404 1405
	if (active_irqs & LINE_FLAG_INTR) {
		complete(&vop->line_flag_completion);
		active_irqs &= ~LINE_FLAG_INTR;
		ret = IRQ_HANDLED;
	}

1406
	if (active_irqs & FS_INTR) {
1407
		drm_crtc_handle_vblank(crtc);
1408
		vop_handle_vblank(vop);
1409
		active_irqs &= ~FS_INTR;
1410
		ret = IRQ_HANDLED;
1411
	}
M
Mark Yao 已提交
1412

1413 1414
	/* Unhandled irqs are spurious. */
	if (active_irqs)
1415 1416
		DRM_DEV_ERROR(vop->dev, "Unknown VOP IRQs: %#02x\n",
			      active_irqs);
1417

1418 1419 1420 1421
out_disable:
	vop_core_clks_disable(vop);
out:
	pm_runtime_put(vop->dev);
1422
	return ret;
M
Mark Yao 已提交
1423 1424
}

1425 1426 1427 1428 1429 1430 1431 1432 1433 1434 1435 1436
static void vop_plane_add_properties(struct drm_plane *plane,
				     const struct vop_win_data *win_data)
{
	unsigned int flags = 0;

	flags |= VOP_WIN_HAS_REG(win_data, x_mir_en) ? DRM_MODE_REFLECT_X : 0;
	flags |= VOP_WIN_HAS_REG(win_data, y_mir_en) ? DRM_MODE_REFLECT_Y : 0;
	if (flags)
		drm_plane_create_rotation_property(plane, DRM_MODE_ROTATE_0,
						   DRM_MODE_ROTATE_0 | flags);
}

M
Mark Yao 已提交
1437 1438 1439 1440 1441
static int vop_create_crtc(struct vop *vop)
{
	const struct vop_data *vop_data = vop->data;
	struct device *dev = vop->dev;
	struct drm_device *drm_dev = vop->drm_dev;
1442
	struct drm_plane *primary = NULL, *cursor = NULL, *plane, *tmp;
M
Mark Yao 已提交
1443 1444 1445 1446 1447 1448 1449 1450 1451 1452 1453 1454 1455 1456 1457 1458 1459 1460 1461 1462 1463 1464
	struct drm_crtc *crtc = &vop->crtc;
	struct device_node *port;
	int ret;
	int i;

	/*
	 * Create drm_plane for primary and cursor planes first, since we need
	 * to pass them to drm_crtc_init_with_planes, which sets the
	 * "possible_crtcs" to the newly initialized crtc.
	 */
	for (i = 0; i < vop_data->win_size; i++) {
		struct vop_win *vop_win = &vop->win[i];
		const struct vop_win_data *win_data = vop_win->data;

		if (win_data->type != DRM_PLANE_TYPE_PRIMARY &&
		    win_data->type != DRM_PLANE_TYPE_CURSOR)
			continue;

		ret = drm_universal_plane_init(vop->drm_dev, &vop_win->base,
					       0, &vop_plane_funcs,
					       win_data->phy->data_formats,
					       win_data->phy->nformats,
1465
					       NULL, win_data->type, NULL);
M
Mark Yao 已提交
1466
		if (ret) {
1467 1468
			DRM_DEV_ERROR(vop->dev, "failed to init plane %d\n",
				      ret);
M
Mark Yao 已提交
1469 1470 1471 1472
			goto err_cleanup_planes;
		}

		plane = &vop_win->base;
1473
		drm_plane_helper_add(plane, &plane_helper_funcs);
1474
		vop_plane_add_properties(plane, win_data);
M
Mark Yao 已提交
1475 1476 1477 1478 1479 1480 1481
		if (plane->type == DRM_PLANE_TYPE_PRIMARY)
			primary = plane;
		else if (plane->type == DRM_PLANE_TYPE_CURSOR)
			cursor = plane;
	}

	ret = drm_crtc_init_with_planes(drm_dev, crtc, primary, cursor,
1482
					&vop_crtc_funcs, NULL);
M
Mark Yao 已提交
1483
	if (ret)
1484
		goto err_cleanup_planes;
M
Mark Yao 已提交
1485 1486 1487 1488 1489 1490 1491 1492 1493 1494

	drm_crtc_helper_add(crtc, &vop_crtc_helper_funcs);

	/*
	 * Create drm_planes for overlay windows with possible_crtcs restricted
	 * to the newly created crtc.
	 */
	for (i = 0; i < vop_data->win_size; i++) {
		struct vop_win *vop_win = &vop->win[i];
		const struct vop_win_data *win_data = vop_win->data;
1495
		unsigned long possible_crtcs = drm_crtc_mask(crtc);
M
Mark Yao 已提交
1496 1497 1498 1499 1500 1501 1502 1503 1504

		if (win_data->type != DRM_PLANE_TYPE_OVERLAY)
			continue;

		ret = drm_universal_plane_init(vop->drm_dev, &vop_win->base,
					       possible_crtcs,
					       &vop_plane_funcs,
					       win_data->phy->data_formats,
					       win_data->phy->nformats,
1505
					       NULL, win_data->type, NULL);
M
Mark Yao 已提交
1506
		if (ret) {
1507 1508
			DRM_DEV_ERROR(vop->dev, "failed to init overlay %d\n",
				      ret);
M
Mark Yao 已提交
1509 1510
			goto err_cleanup_crtc;
		}
1511
		drm_plane_helper_add(&vop_win->base, &plane_helper_funcs);
1512
		vop_plane_add_properties(&vop_win->base, win_data);
M
Mark Yao 已提交
1513 1514 1515 1516
	}

	port = of_get_child_by_name(dev->of_node, "port");
	if (!port) {
1517 1518
		DRM_DEV_ERROR(vop->dev, "no port node found in %pOF\n",
			      dev->of_node);
1519
		ret = -ENOENT;
M
Mark Yao 已提交
1520 1521 1522
		goto err_cleanup_crtc;
	}

1523 1524 1525
	drm_flip_work_init(&vop->fb_unref_work, "fb_unref",
			   vop_fb_unref_worker);

1526
	init_completion(&vop->dsp_hold_completion);
1527
	init_completion(&vop->line_flag_completion);
M
Mark Yao 已提交
1528 1529 1530 1531 1532 1533 1534
	crtc->port = port;

	return 0;

err_cleanup_crtc:
	drm_crtc_cleanup(crtc);
err_cleanup_planes:
1535 1536
	list_for_each_entry_safe(plane, tmp, &drm_dev->mode_config.plane_list,
				 head)
M
Mark Yao 已提交
1537 1538 1539 1540 1541 1542 1543
		drm_plane_cleanup(plane);
	return ret;
}

static void vop_destroy_crtc(struct vop *vop)
{
	struct drm_crtc *crtc = &vop->crtc;
1544 1545
	struct drm_device *drm_dev = vop->drm_dev;
	struct drm_plane *plane, *tmp;
M
Mark Yao 已提交
1546 1547

	of_node_put(crtc->port);
1548 1549 1550 1551 1552 1553 1554 1555 1556 1557 1558 1559 1560 1561 1562 1563 1564

	/*
	 * We need to cleanup the planes now.  Why?
	 *
	 * The planes are "&vop->win[i].base".  That means the memory is
	 * all part of the big "struct vop" chunk of memory.  That memory
	 * was devm allocated and associated with this component.  We need to
	 * free it ourselves before vop_unbind() finishes.
	 */
	list_for_each_entry_safe(plane, tmp, &drm_dev->mode_config.plane_list,
				 head)
		vop_plane_destroy(plane);

	/*
	 * Destroy CRTC after vop_plane_destroy() since vop_disable_plane()
	 * references the CRTC.
	 */
M
Mark Yao 已提交
1565
	drm_crtc_cleanup(crtc);
1566
	drm_flip_work_cleanup(&vop->fb_unref_work);
M
Mark Yao 已提交
1567 1568 1569 1570 1571 1572 1573 1574 1575 1576
}

static int vop_initial(struct vop *vop)
{
	const struct vop_data *vop_data = vop->data;
	struct reset_control *ahb_rst;
	int i, ret;

	vop->hclk = devm_clk_get(vop->dev, "hclk_vop");
	if (IS_ERR(vop->hclk)) {
1577
		DRM_DEV_ERROR(vop->dev, "failed to get hclk source\n");
M
Mark Yao 已提交
1578 1579 1580 1581
		return PTR_ERR(vop->hclk);
	}
	vop->aclk = devm_clk_get(vop->dev, "aclk_vop");
	if (IS_ERR(vop->aclk)) {
1582
		DRM_DEV_ERROR(vop->dev, "failed to get aclk source\n");
M
Mark Yao 已提交
1583 1584 1585 1586
		return PTR_ERR(vop->aclk);
	}
	vop->dclk = devm_clk_get(vop->dev, "dclk_vop");
	if (IS_ERR(vop->dclk)) {
1587
		DRM_DEV_ERROR(vop->dev, "failed to get dclk source\n");
M
Mark Yao 已提交
1588 1589 1590
		return PTR_ERR(vop->dclk);
	}

1591 1592
	ret = pm_runtime_get_sync(vop->dev);
	if (ret < 0) {
1593
		DRM_DEV_ERROR(vop->dev, "failed to get pm runtime: %d\n", ret);
1594 1595 1596
		return ret;
	}

M
Mark Yao 已提交
1597 1598
	ret = clk_prepare(vop->dclk);
	if (ret < 0) {
1599
		DRM_DEV_ERROR(vop->dev, "failed to prepare dclk\n");
1600
		goto err_put_pm_runtime;
M
Mark Yao 已提交
1601 1602
	}

1603 1604
	/* Enable both the hclk and aclk to setup the vop */
	ret = clk_prepare_enable(vop->hclk);
M
Mark Yao 已提交
1605
	if (ret < 0) {
1606
		DRM_DEV_ERROR(vop->dev, "failed to prepare/enable hclk\n");
M
Mark Yao 已提交
1607 1608 1609
		goto err_unprepare_dclk;
	}

1610
	ret = clk_prepare_enable(vop->aclk);
M
Mark Yao 已提交
1611
	if (ret < 0) {
1612
		DRM_DEV_ERROR(vop->dev, "failed to prepare/enable aclk\n");
1613
		goto err_disable_hclk;
M
Mark Yao 已提交
1614
	}
1615

M
Mark Yao 已提交
1616 1617 1618 1619 1620
	/*
	 * do hclk_reset, reset all vop registers.
	 */
	ahb_rst = devm_reset_control_get(vop->dev, "ahb");
	if (IS_ERR(ahb_rst)) {
1621
		DRM_DEV_ERROR(vop->dev, "failed to get ahb reset\n");
M
Mark Yao 已提交
1622
		ret = PTR_ERR(ahb_rst);
1623
		goto err_disable_aclk;
M
Mark Yao 已提交
1624 1625 1626 1627 1628
	}
	reset_control_assert(ahb_rst);
	usleep_range(10, 20);
	reset_control_deassert(ahb_rst);

1629 1630 1631
	VOP_INTR_SET_TYPE(vop, clear, INTR_MASK, 1);
	VOP_INTR_SET_TYPE(vop, enable, INTR_MASK, 0);

1632 1633
	for (i = 0; i < vop->len; i += sizeof(u32))
		vop->regsbak[i / 4] = readl_relaxed(vop->regs + i);
M
Mark Yao 已提交
1634

1635 1636
	VOP_REG_SET(vop, misc, global_regdone_en, 1);
	VOP_REG_SET(vop, common, dsp_blank, 0);
M
Mark Yao 已提交
1637 1638 1639

	for (i = 0; i < vop_data->win_size; i++) {
		const struct vop_win_data *win = &vop_data->win[i];
1640
		int channel = i * 2 + 1;
M
Mark Yao 已提交
1641

1642
		VOP_WIN_SET(vop, win, channel, (channel + 1) << 4 | channel);
1643
		vop_win_disable(vop, win);
1644
		VOP_WIN_SET(vop, win, gate, 1);
M
Mark Yao 已提交
1645 1646 1647 1648 1649 1650 1651 1652 1653
	}

	vop_cfg_done(vop);

	/*
	 * do dclk_reset, let all config take affect.
	 */
	vop->dclk_rst = devm_reset_control_get(vop->dev, "dclk");
	if (IS_ERR(vop->dclk_rst)) {
1654
		DRM_DEV_ERROR(vop->dev, "failed to get dclk reset\n");
M
Mark Yao 已提交
1655
		ret = PTR_ERR(vop->dclk_rst);
1656
		goto err_disable_aclk;
M
Mark Yao 已提交
1657 1658 1659 1660 1661 1662
	}
	reset_control_assert(vop->dclk_rst);
	usleep_range(10, 20);
	reset_control_deassert(vop->dclk_rst);

	clk_disable(vop->hclk);
1663
	clk_disable(vop->aclk);
M
Mark Yao 已提交
1664

1665
	vop->is_enabled = false;
M
Mark Yao 已提交
1666

1667 1668
	pm_runtime_put_sync(vop->dev);

M
Mark Yao 已提交
1669 1670
	return 0;

1671 1672
err_disable_aclk:
	clk_disable_unprepare(vop->aclk);
M
Mark Yao 已提交
1673
err_disable_hclk:
1674
	clk_disable_unprepare(vop->hclk);
M
Mark Yao 已提交
1675 1676
err_unprepare_dclk:
	clk_unprepare(vop->dclk);
1677 1678
err_put_pm_runtime:
	pm_runtime_put_sync(vop->dev);
M
Mark Yao 已提交
1679 1680 1681 1682 1683 1684 1685 1686 1687 1688 1689 1690 1691 1692 1693 1694 1695
	return ret;
}

/*
 * Initialize the vop->win array elements.
 */
static void vop_win_init(struct vop *vop)
{
	const struct vop_data *vop_data = vop->data;
	unsigned int i;

	for (i = 0; i < vop_data->win_size; i++) {
		struct vop_win *vop_win = &vop->win[i];
		const struct vop_win_data *win_data = &vop_data->win[i];

		vop_win->data = win_data;
		vop_win->vop = vop;
1696 1697 1698

		if (vop_data->win_yuv2yuv)
			vop_win->yuv2yuv_data = &vop_data->win_yuv2yuv[i];
M
Mark Yao 已提交
1699 1700 1701
	}
}

1702
/**
1703
 * rockchip_drm_wait_vact_end
1704 1705 1706
 * @crtc: CRTC to enable line flag
 * @mstimeout: millisecond for timeout
 *
1707
 * Wait for vact_end line flag irq or timeout.
1708 1709 1710 1711
 *
 * Returns:
 * Zero on success, negative errno on failure.
 */
1712
int rockchip_drm_wait_vact_end(struct drm_crtc *crtc, unsigned int mstimeout)
1713 1714 1715
{
	struct vop *vop = to_vop(crtc);
	unsigned long jiffies_left;
Z
zain wang 已提交
1716
	int ret = 0;
1717 1718 1719 1720

	if (!crtc || !vop->is_enabled)
		return -ENODEV;

Z
zain wang 已提交
1721 1722 1723 1724 1725
	mutex_lock(&vop->vop_lock);
	if (mstimeout <= 0) {
		ret = -EINVAL;
		goto out;
	}
1726

Z
zain wang 已提交
1727 1728 1729 1730
	if (vop_line_flag_irq_is_enabled(vop)) {
		ret = -EBUSY;
		goto out;
	}
1731 1732

	reinit_completion(&vop->line_flag_completion);
1733
	vop_line_flag_irq_enable(vop);
1734 1735 1736 1737 1738 1739

	jiffies_left = wait_for_completion_timeout(&vop->line_flag_completion,
						   msecs_to_jiffies(mstimeout));
	vop_line_flag_irq_disable(vop);

	if (jiffies_left == 0) {
1740
		DRM_DEV_ERROR(vop->dev, "Timeout waiting for IRQ\n");
Z
zain wang 已提交
1741 1742
		ret = -ETIMEDOUT;
		goto out;
1743 1744
	}

Z
zain wang 已提交
1745 1746 1747
out:
	mutex_unlock(&vop->vop_lock);
	return ret;
1748
}
1749
EXPORT_SYMBOL(rockchip_drm_wait_vact_end);
1750

M
Mark Yao 已提交
1751 1752 1753 1754 1755 1756 1757
static int vop_bind(struct device *dev, struct device *master, void *data)
{
	struct platform_device *pdev = to_platform_device(dev);
	const struct vop_data *vop_data;
	struct drm_device *drm_dev = data;
	struct vop *vop;
	struct resource *res;
1758
	int ret, irq;
M
Mark Yao 已提交
1759

1760
	vop_data = of_device_get_match_data(dev);
M
Mark Yao 已提交
1761 1762 1763 1764
	if (!vop_data)
		return -ENODEV;

	/* Allocate vop struct and its vop_win array */
1765 1766
	vop = devm_kzalloc(dev, struct_size(vop, win, vop_data->win_size),
			   GFP_KERNEL);
M
Mark Yao 已提交
1767 1768 1769 1770 1771 1772 1773 1774 1775 1776 1777 1778 1779 1780 1781 1782 1783 1784 1785 1786
	if (!vop)
		return -ENOMEM;

	vop->dev = dev;
	vop->data = vop_data;
	vop->drm_dev = drm_dev;
	dev_set_drvdata(dev, vop);

	vop_win_init(vop);

	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
	vop->len = resource_size(res);
	vop->regs = devm_ioremap_resource(dev, res);
	if (IS_ERR(vop->regs))
		return PTR_ERR(vop->regs);

	vop->regsbak = devm_kzalloc(dev, vop->len, GFP_KERNEL);
	if (!vop->regsbak)
		return -ENOMEM;

1787 1788
	irq = platform_get_irq(pdev, 0);
	if (irq < 0) {
1789
		DRM_DEV_ERROR(dev, "cannot find irq for vop\n");
1790
		return irq;
M
Mark Yao 已提交
1791
	}
1792
	vop->irq = (unsigned int)irq;
M
Mark Yao 已提交
1793 1794 1795

	spin_lock_init(&vop->reg_lock);
	spin_lock_init(&vop->irq_lock);
Z
zain wang 已提交
1796
	mutex_init(&vop->vop_lock);
M
Mark Yao 已提交
1797 1798 1799

	ret = vop_create_crtc(vop);
	if (ret)
1800
		return ret;
M
Mark Yao 已提交
1801 1802

	pm_runtime_enable(&pdev->dev);
1803

1804 1805
	ret = vop_initial(vop);
	if (ret < 0) {
1806 1807
		DRM_DEV_ERROR(&pdev->dev,
			      "cannot initial vop dev - err %d\n", ret);
1808 1809 1810
		goto err_disable_pm_runtime;
	}

1811 1812 1813 1814 1815
	ret = devm_request_irq(dev, vop->irq, vop_isr,
			       IRQF_SHARED, dev_name(dev), vop);
	if (ret)
		goto err_disable_pm_runtime;

1816 1817 1818 1819 1820 1821 1822 1823
	if (vop->data->feature & VOP_FEATURE_INTERNAL_RGB) {
		vop->rgb = rockchip_rgb_init(dev, &vop->crtc, vop->drm_dev);
		if (IS_ERR(vop->rgb)) {
			ret = PTR_ERR(vop->rgb);
			goto err_disable_pm_runtime;
		}
	}

M
Mark Yao 已提交
1824
	return 0;
1825

1826 1827 1828
err_disable_pm_runtime:
	pm_runtime_disable(&pdev->dev);
	vop_destroy_crtc(vop);
1829
	return ret;
M
Mark Yao 已提交
1830 1831 1832 1833 1834 1835
}

static void vop_unbind(struct device *dev, struct device *master, void *data)
{
	struct vop *vop = dev_get_drvdata(dev);

1836 1837 1838
	if (vop->rgb)
		rockchip_rgb_fini(vop->rgb);

M
Mark Yao 已提交
1839 1840
	pm_runtime_disable(dev);
	vop_destroy_crtc(vop);
1841 1842 1843 1844

	clk_unprepare(vop->aclk);
	clk_unprepare(vop->hclk);
	clk_unprepare(vop->dclk);
M
Mark Yao 已提交
1845 1846
}

1847
const struct component_ops vop_component_ops = {
M
Mark Yao 已提交
1848 1849 1850
	.bind = vop_bind,
	.unbind = vop_unbind,
};
1851
EXPORT_SYMBOL_GPL(vop_component_ops);