162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
462306a36Sopenharmony_ci * Author:Mark Yao <mark.yao@rock-chips.com>
562306a36Sopenharmony_ci */
662306a36Sopenharmony_ci
762306a36Sopenharmony_ci#include <linux/clk.h>
862306a36Sopenharmony_ci#include <linux/component.h>
962306a36Sopenharmony_ci#include <linux/delay.h>
1062306a36Sopenharmony_ci#include <linux/iopoll.h>
1162306a36Sopenharmony_ci#include <linux/kernel.h>
1262306a36Sopenharmony_ci#include <linux/log2.h>
1362306a36Sopenharmony_ci#include <linux/module.h>
1462306a36Sopenharmony_ci#include <linux/of.h>
1562306a36Sopenharmony_ci#include <linux/overflow.h>
1662306a36Sopenharmony_ci#include <linux/platform_device.h>
1762306a36Sopenharmony_ci#include <linux/pm_runtime.h>
1862306a36Sopenharmony_ci#include <linux/reset.h>
1962306a36Sopenharmony_ci
2062306a36Sopenharmony_ci#include <drm/drm.h>
2162306a36Sopenharmony_ci#include <drm/drm_atomic.h>
2262306a36Sopenharmony_ci#include <drm/drm_atomic_uapi.h>
2362306a36Sopenharmony_ci#include <drm/drm_blend.h>
2462306a36Sopenharmony_ci#include <drm/drm_crtc.h>
2562306a36Sopenharmony_ci#include <drm/drm_flip_work.h>
2662306a36Sopenharmony_ci#include <drm/drm_fourcc.h>
2762306a36Sopenharmony_ci#include <drm/drm_framebuffer.h>
2862306a36Sopenharmony_ci#include <drm/drm_gem_atomic_helper.h>
2962306a36Sopenharmony_ci#include <drm/drm_gem_framebuffer_helper.h>
3062306a36Sopenharmony_ci#include <drm/drm_probe_helper.h>
3162306a36Sopenharmony_ci#include <drm/drm_self_refresh_helper.h>
3262306a36Sopenharmony_ci#include <drm/drm_vblank.h>
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_ci#ifdef CONFIG_DRM_ANALOGIX_DP
3562306a36Sopenharmony_ci#include <drm/bridge/analogix_dp.h>
3662306a36Sopenharmony_ci#endif
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_ci#include "rockchip_drm_drv.h"
3962306a36Sopenharmony_ci#include "rockchip_drm_gem.h"
4062306a36Sopenharmony_ci#include "rockchip_drm_fb.h"
4162306a36Sopenharmony_ci#include "rockchip_drm_vop.h"
4262306a36Sopenharmony_ci#include "rockchip_rgb.h"
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_ci#define VOP_WIN_SET(vop, win, name, v) \
4562306a36Sopenharmony_ci		vop_reg_set(vop, &win->phy->name, win->base, ~0, v, #name)
4662306a36Sopenharmony_ci#define VOP_SCL_SET(vop, win, name, v) \
4762306a36Sopenharmony_ci		vop_reg_set(vop, &win->phy->scl->name, win->base, ~0, v, #name)
4862306a36Sopenharmony_ci#define VOP_SCL_SET_EXT(vop, win, name, v) \
4962306a36Sopenharmony_ci		vop_reg_set(vop, &win->phy->scl->ext->name, \
5062306a36Sopenharmony_ci			    win->base, ~0, v, #name)
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_ci#define VOP_WIN_YUV2YUV_SET(vop, win_yuv2yuv, name, v) \
5362306a36Sopenharmony_ci	do { \
5462306a36Sopenharmony_ci		if (win_yuv2yuv && win_yuv2yuv->name.mask) \
5562306a36Sopenharmony_ci			vop_reg_set(vop, &win_yuv2yuv->name, 0, ~0, v, #name); \
5662306a36Sopenharmony_ci	} while (0)
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_ci#define VOP_WIN_YUV2YUV_COEFFICIENT_SET(vop, win_yuv2yuv, name, v) \
5962306a36Sopenharmony_ci	do { \
6062306a36Sopenharmony_ci		if (win_yuv2yuv && win_yuv2yuv->phy->name.mask) \
6162306a36Sopenharmony_ci			vop_reg_set(vop, &win_yuv2yuv->phy->name, win_yuv2yuv->base, ~0, v, #name); \
6262306a36Sopenharmony_ci	} while (0)
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_ci#define VOP_INTR_SET_MASK(vop, name, mask, v) \
6562306a36Sopenharmony_ci		vop_reg_set(vop, &vop->data->intr->name, 0, mask, v, #name)
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_ci#define VOP_REG_SET(vop, group, name, v) \
6862306a36Sopenharmony_ci		    vop_reg_set(vop, &vop->data->group->name, 0, ~0, v, #name)
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_ci#define VOP_HAS_REG(vop, group, name) \
7162306a36Sopenharmony_ci		(!!(vop->data->group->name.mask))
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_ci#define VOP_INTR_SET_TYPE(vop, name, type, v) \
7462306a36Sopenharmony_ci	do { \
7562306a36Sopenharmony_ci		int i, reg = 0, mask = 0; \
7662306a36Sopenharmony_ci		for (i = 0; i < vop->data->intr->nintrs; i++) { \
7762306a36Sopenharmony_ci			if (vop->data->intr->intrs[i] & type) { \
7862306a36Sopenharmony_ci				reg |= (v) << i; \
7962306a36Sopenharmony_ci				mask |= 1 << i; \
8062306a36Sopenharmony_ci			} \
8162306a36Sopenharmony_ci		} \
8262306a36Sopenharmony_ci		VOP_INTR_SET_MASK(vop, name, mask, reg); \
8362306a36Sopenharmony_ci	} while (0)
8462306a36Sopenharmony_ci#define VOP_INTR_GET_TYPE(vop, name, type) \
8562306a36Sopenharmony_ci		vop_get_intr_type(vop, &vop->data->intr->name, type)
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_ci#define VOP_WIN_GET(vop, win, name) \
8862306a36Sopenharmony_ci		vop_read_reg(vop, win->base, &win->phy->name)
8962306a36Sopenharmony_ci
9062306a36Sopenharmony_ci#define VOP_WIN_HAS_REG(win, name) \
9162306a36Sopenharmony_ci	(!!(win->phy->name.mask))
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_ci#define VOP_WIN_GET_YRGBADDR(vop, win) \
9462306a36Sopenharmony_ci		vop_readl(vop, win->base + win->phy->yrgb_mst.offset)
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_ci#define VOP_WIN_TO_INDEX(vop_win) \
9762306a36Sopenharmony_ci	((vop_win) - (vop_win)->vop->win)
9862306a36Sopenharmony_ci
9962306a36Sopenharmony_ci#define VOP_AFBC_SET(vop, name, v) \
10062306a36Sopenharmony_ci	do { \
10162306a36Sopenharmony_ci		if ((vop)->data->afbc) \
10262306a36Sopenharmony_ci			vop_reg_set((vop), &(vop)->data->afbc->name, \
10362306a36Sopenharmony_ci				    0, ~0, v, #name); \
10462306a36Sopenharmony_ci	} while (0)
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_ci#define to_vop(x) container_of(x, struct vop, crtc)
10762306a36Sopenharmony_ci#define to_vop_win(x) container_of(x, struct vop_win, base)
10862306a36Sopenharmony_ci
10962306a36Sopenharmony_ci#define AFBC_FMT_RGB565		0x0
11062306a36Sopenharmony_ci#define AFBC_FMT_U8U8U8U8	0x5
11162306a36Sopenharmony_ci#define AFBC_FMT_U8U8U8		0x4
11262306a36Sopenharmony_ci
11362306a36Sopenharmony_ci#define AFBC_TILE_16x16		BIT(4)
11462306a36Sopenharmony_ci
11562306a36Sopenharmony_ci/*
11662306a36Sopenharmony_ci * The coefficients of the following matrix are all fixed points.
11762306a36Sopenharmony_ci * The format is S2.10 for the 3x3 part of the matrix, and S9.12 for the offsets.
11862306a36Sopenharmony_ci * They are all represented in two's complement.
11962306a36Sopenharmony_ci */
12062306a36Sopenharmony_cistatic const uint32_t bt601_yuv2rgb[] = {
12162306a36Sopenharmony_ci	0x4A8, 0x0,    0x662,
12262306a36Sopenharmony_ci	0x4A8, 0x1E6F, 0x1CBF,
12362306a36Sopenharmony_ci	0x4A8, 0x812,  0x0,
12462306a36Sopenharmony_ci	0x321168, 0x0877CF, 0x2EB127
12562306a36Sopenharmony_ci};
12662306a36Sopenharmony_ci
12762306a36Sopenharmony_cienum vop_pending {
12862306a36Sopenharmony_ci	VOP_PENDING_FB_UNREF,
12962306a36Sopenharmony_ci};
13062306a36Sopenharmony_ci
13162306a36Sopenharmony_cistruct vop_win {
13262306a36Sopenharmony_ci	struct drm_plane base;
13362306a36Sopenharmony_ci	const struct vop_win_data *data;
13462306a36Sopenharmony_ci	const struct vop_win_yuv2yuv_data *yuv2yuv_data;
13562306a36Sopenharmony_ci	struct vop *vop;
13662306a36Sopenharmony_ci};
13762306a36Sopenharmony_ci
13862306a36Sopenharmony_cistruct rockchip_rgb;
13962306a36Sopenharmony_cistruct vop {
14062306a36Sopenharmony_ci	struct drm_crtc crtc;
14162306a36Sopenharmony_ci	struct device *dev;
14262306a36Sopenharmony_ci	struct drm_device *drm_dev;
14362306a36Sopenharmony_ci	bool is_enabled;
14462306a36Sopenharmony_ci
14562306a36Sopenharmony_ci	struct completion dsp_hold_completion;
14662306a36Sopenharmony_ci	unsigned int win_enabled;
14762306a36Sopenharmony_ci
14862306a36Sopenharmony_ci	/* protected by dev->event_lock */
14962306a36Sopenharmony_ci	struct drm_pending_vblank_event *event;
15062306a36Sopenharmony_ci
15162306a36Sopenharmony_ci	struct drm_flip_work fb_unref_work;
15262306a36Sopenharmony_ci	unsigned long pending;
15362306a36Sopenharmony_ci
15462306a36Sopenharmony_ci	struct completion line_flag_completion;
15562306a36Sopenharmony_ci
15662306a36Sopenharmony_ci	const struct vop_data *data;
15762306a36Sopenharmony_ci
15862306a36Sopenharmony_ci	uint32_t *regsbak;
15962306a36Sopenharmony_ci	void __iomem *regs;
16062306a36Sopenharmony_ci	void __iomem *lut_regs;
16162306a36Sopenharmony_ci
16262306a36Sopenharmony_ci	/* physical map length of vop register */
16362306a36Sopenharmony_ci	uint32_t len;
16462306a36Sopenharmony_ci
16562306a36Sopenharmony_ci	/* one time only one process allowed to config the register */
16662306a36Sopenharmony_ci	spinlock_t reg_lock;
16762306a36Sopenharmony_ci	/* lock vop irq reg */
16862306a36Sopenharmony_ci	spinlock_t irq_lock;
16962306a36Sopenharmony_ci	/* protects crtc enable/disable */
17062306a36Sopenharmony_ci	struct mutex vop_lock;
17162306a36Sopenharmony_ci
17262306a36Sopenharmony_ci	unsigned int irq;
17362306a36Sopenharmony_ci
17462306a36Sopenharmony_ci	/* vop AHP clk */
17562306a36Sopenharmony_ci	struct clk *hclk;
17662306a36Sopenharmony_ci	/* vop dclk */
17762306a36Sopenharmony_ci	struct clk *dclk;
17862306a36Sopenharmony_ci	/* vop share memory frequency */
17962306a36Sopenharmony_ci	struct clk *aclk;
18062306a36Sopenharmony_ci
18162306a36Sopenharmony_ci	/* vop dclk reset */
18262306a36Sopenharmony_ci	struct reset_control *dclk_rst;
18362306a36Sopenharmony_ci
18462306a36Sopenharmony_ci	/* optional internal rgb encoder */
18562306a36Sopenharmony_ci	struct rockchip_rgb *rgb;
18662306a36Sopenharmony_ci
18762306a36Sopenharmony_ci	struct vop_win win[];
18862306a36Sopenharmony_ci};
18962306a36Sopenharmony_ci
19062306a36Sopenharmony_cistatic inline uint32_t vop_readl(struct vop *vop, uint32_t offset)
19162306a36Sopenharmony_ci{
19262306a36Sopenharmony_ci	return readl(vop->regs + offset);
19362306a36Sopenharmony_ci}
19462306a36Sopenharmony_ci
19562306a36Sopenharmony_cistatic inline uint32_t vop_read_reg(struct vop *vop, uint32_t base,
19662306a36Sopenharmony_ci				    const struct vop_reg *reg)
19762306a36Sopenharmony_ci{
19862306a36Sopenharmony_ci	return (vop_readl(vop, base + reg->offset) >> reg->shift) & reg->mask;
19962306a36Sopenharmony_ci}
20062306a36Sopenharmony_ci
20162306a36Sopenharmony_cistatic void vop_reg_set(struct vop *vop, const struct vop_reg *reg,
20262306a36Sopenharmony_ci			uint32_t _offset, uint32_t _mask, uint32_t v,
20362306a36Sopenharmony_ci			const char *reg_name)
20462306a36Sopenharmony_ci{
20562306a36Sopenharmony_ci	int offset, mask, shift;
20662306a36Sopenharmony_ci
20762306a36Sopenharmony_ci	if (!reg || !reg->mask) {
20862306a36Sopenharmony_ci		DRM_DEV_DEBUG(vop->dev, "Warning: not support %s\n", reg_name);
20962306a36Sopenharmony_ci		return;
21062306a36Sopenharmony_ci	}
21162306a36Sopenharmony_ci
21262306a36Sopenharmony_ci	offset = reg->offset + _offset;
21362306a36Sopenharmony_ci	mask = reg->mask & _mask;
21462306a36Sopenharmony_ci	shift = reg->shift;
21562306a36Sopenharmony_ci
21662306a36Sopenharmony_ci	if (reg->write_mask) {
21762306a36Sopenharmony_ci		v = ((v << shift) & 0xffff) | (mask << (shift + 16));
21862306a36Sopenharmony_ci	} else {
21962306a36Sopenharmony_ci		uint32_t cached_val = vop->regsbak[offset >> 2];
22062306a36Sopenharmony_ci
22162306a36Sopenharmony_ci		v = (cached_val & ~(mask << shift)) | ((v & mask) << shift);
22262306a36Sopenharmony_ci		vop->regsbak[offset >> 2] = v;
22362306a36Sopenharmony_ci	}
22462306a36Sopenharmony_ci
22562306a36Sopenharmony_ci	if (reg->relaxed)
22662306a36Sopenharmony_ci		writel_relaxed(v, vop->regs + offset);
22762306a36Sopenharmony_ci	else
22862306a36Sopenharmony_ci		writel(v, vop->regs + offset);
22962306a36Sopenharmony_ci}
23062306a36Sopenharmony_ci
23162306a36Sopenharmony_cistatic inline uint32_t vop_get_intr_type(struct vop *vop,
23262306a36Sopenharmony_ci					 const struct vop_reg *reg, int type)
23362306a36Sopenharmony_ci{
23462306a36Sopenharmony_ci	uint32_t i, ret = 0;
23562306a36Sopenharmony_ci	uint32_t regs = vop_read_reg(vop, 0, reg);
23662306a36Sopenharmony_ci
23762306a36Sopenharmony_ci	for (i = 0; i < vop->data->intr->nintrs; i++) {
23862306a36Sopenharmony_ci		if ((type & vop->data->intr->intrs[i]) && (regs & 1 << i))
23962306a36Sopenharmony_ci			ret |= vop->data->intr->intrs[i];
24062306a36Sopenharmony_ci	}
24162306a36Sopenharmony_ci
24262306a36Sopenharmony_ci	return ret;
24362306a36Sopenharmony_ci}
24462306a36Sopenharmony_ci
24562306a36Sopenharmony_cistatic inline void vop_cfg_done(struct vop *vop)
24662306a36Sopenharmony_ci{
24762306a36Sopenharmony_ci	VOP_REG_SET(vop, common, cfg_done, 1);
24862306a36Sopenharmony_ci}
24962306a36Sopenharmony_ci
25062306a36Sopenharmony_cistatic bool has_rb_swapped(uint32_t version, uint32_t format)
25162306a36Sopenharmony_ci{
25262306a36Sopenharmony_ci	switch (format) {
25362306a36Sopenharmony_ci	case DRM_FORMAT_XBGR8888:
25462306a36Sopenharmony_ci	case DRM_FORMAT_ABGR8888:
25562306a36Sopenharmony_ci	case DRM_FORMAT_BGR565:
25662306a36Sopenharmony_ci		return true;
25762306a36Sopenharmony_ci	/*
25862306a36Sopenharmony_ci	 * full framework (IP version 3.x) only need rb swapped for RGB888 and
25962306a36Sopenharmony_ci	 * little framework (IP version 2.x) only need rb swapped for BGR888,
26062306a36Sopenharmony_ci	 * check for 3.x to also only rb swap BGR888 for unknown vop version
26162306a36Sopenharmony_ci	 */
26262306a36Sopenharmony_ci	case DRM_FORMAT_RGB888:
26362306a36Sopenharmony_ci		return VOP_MAJOR(version) == 3;
26462306a36Sopenharmony_ci	case DRM_FORMAT_BGR888:
26562306a36Sopenharmony_ci		return VOP_MAJOR(version) != 3;
26662306a36Sopenharmony_ci	default:
26762306a36Sopenharmony_ci		return false;
26862306a36Sopenharmony_ci	}
26962306a36Sopenharmony_ci}
27062306a36Sopenharmony_ci
27162306a36Sopenharmony_cistatic bool has_uv_swapped(uint32_t format)
27262306a36Sopenharmony_ci{
27362306a36Sopenharmony_ci	switch (format) {
27462306a36Sopenharmony_ci	case DRM_FORMAT_NV21:
27562306a36Sopenharmony_ci	case DRM_FORMAT_NV61:
27662306a36Sopenharmony_ci	case DRM_FORMAT_NV42:
27762306a36Sopenharmony_ci		return true;
27862306a36Sopenharmony_ci	default:
27962306a36Sopenharmony_ci		return false;
28062306a36Sopenharmony_ci	}
28162306a36Sopenharmony_ci}
28262306a36Sopenharmony_ci
28362306a36Sopenharmony_cistatic enum vop_data_format vop_convert_format(uint32_t format)
28462306a36Sopenharmony_ci{
28562306a36Sopenharmony_ci	switch (format) {
28662306a36Sopenharmony_ci	case DRM_FORMAT_XRGB8888:
28762306a36Sopenharmony_ci	case DRM_FORMAT_ARGB8888:
28862306a36Sopenharmony_ci	case DRM_FORMAT_XBGR8888:
28962306a36Sopenharmony_ci	case DRM_FORMAT_ABGR8888:
29062306a36Sopenharmony_ci		return VOP_FMT_ARGB8888;
29162306a36Sopenharmony_ci	case DRM_FORMAT_RGB888:
29262306a36Sopenharmony_ci	case DRM_FORMAT_BGR888:
29362306a36Sopenharmony_ci		return VOP_FMT_RGB888;
29462306a36Sopenharmony_ci	case DRM_FORMAT_RGB565:
29562306a36Sopenharmony_ci	case DRM_FORMAT_BGR565:
29662306a36Sopenharmony_ci		return VOP_FMT_RGB565;
29762306a36Sopenharmony_ci	case DRM_FORMAT_NV12:
29862306a36Sopenharmony_ci	case DRM_FORMAT_NV21:
29962306a36Sopenharmony_ci		return VOP_FMT_YUV420SP;
30062306a36Sopenharmony_ci	case DRM_FORMAT_NV16:
30162306a36Sopenharmony_ci	case DRM_FORMAT_NV61:
30262306a36Sopenharmony_ci		return VOP_FMT_YUV422SP;
30362306a36Sopenharmony_ci	case DRM_FORMAT_NV24:
30462306a36Sopenharmony_ci	case DRM_FORMAT_NV42:
30562306a36Sopenharmony_ci		return VOP_FMT_YUV444SP;
30662306a36Sopenharmony_ci	default:
30762306a36Sopenharmony_ci		DRM_ERROR("unsupported format[%08x]\n", format);
30862306a36Sopenharmony_ci		return -EINVAL;
30962306a36Sopenharmony_ci	}
31062306a36Sopenharmony_ci}
31162306a36Sopenharmony_ci
31262306a36Sopenharmony_cistatic int vop_convert_afbc_format(uint32_t format)
31362306a36Sopenharmony_ci{
31462306a36Sopenharmony_ci	switch (format) {
31562306a36Sopenharmony_ci	case DRM_FORMAT_XRGB8888:
31662306a36Sopenharmony_ci	case DRM_FORMAT_ARGB8888:
31762306a36Sopenharmony_ci	case DRM_FORMAT_XBGR8888:
31862306a36Sopenharmony_ci	case DRM_FORMAT_ABGR8888:
31962306a36Sopenharmony_ci		return AFBC_FMT_U8U8U8U8;
32062306a36Sopenharmony_ci	case DRM_FORMAT_RGB888:
32162306a36Sopenharmony_ci	case DRM_FORMAT_BGR888:
32262306a36Sopenharmony_ci		return AFBC_FMT_U8U8U8;
32362306a36Sopenharmony_ci	case DRM_FORMAT_RGB565:
32462306a36Sopenharmony_ci	case DRM_FORMAT_BGR565:
32562306a36Sopenharmony_ci		return AFBC_FMT_RGB565;
32662306a36Sopenharmony_ci	default:
32762306a36Sopenharmony_ci		DRM_DEBUG_KMS("unsupported AFBC format[%08x]\n", format);
32862306a36Sopenharmony_ci		return -EINVAL;
32962306a36Sopenharmony_ci	}
33062306a36Sopenharmony_ci}
33162306a36Sopenharmony_ci
33262306a36Sopenharmony_cistatic uint16_t scl_vop_cal_scale(enum scale_mode mode, uint32_t src,
33362306a36Sopenharmony_ci				  uint32_t dst, bool is_horizontal,
33462306a36Sopenharmony_ci				  int vsu_mode, int *vskiplines)
33562306a36Sopenharmony_ci{
33662306a36Sopenharmony_ci	uint16_t val = 1 << SCL_FT_DEFAULT_FIXPOINT_SHIFT;
33762306a36Sopenharmony_ci
33862306a36Sopenharmony_ci	if (vskiplines)
33962306a36Sopenharmony_ci		*vskiplines = 0;
34062306a36Sopenharmony_ci
34162306a36Sopenharmony_ci	if (is_horizontal) {
34262306a36Sopenharmony_ci		if (mode == SCALE_UP)
34362306a36Sopenharmony_ci			val = GET_SCL_FT_BIC(src, dst);
34462306a36Sopenharmony_ci		else if (mode == SCALE_DOWN)
34562306a36Sopenharmony_ci			val = GET_SCL_FT_BILI_DN(src, dst);
34662306a36Sopenharmony_ci	} else {
34762306a36Sopenharmony_ci		if (mode == SCALE_UP) {
34862306a36Sopenharmony_ci			if (vsu_mode == SCALE_UP_BIL)
34962306a36Sopenharmony_ci				val = GET_SCL_FT_BILI_UP(src, dst);
35062306a36Sopenharmony_ci			else
35162306a36Sopenharmony_ci				val = GET_SCL_FT_BIC(src, dst);
35262306a36Sopenharmony_ci		} else if (mode == SCALE_DOWN) {
35362306a36Sopenharmony_ci			if (vskiplines) {
35462306a36Sopenharmony_ci				*vskiplines = scl_get_vskiplines(src, dst);
35562306a36Sopenharmony_ci				val = scl_get_bili_dn_vskip(src, dst,
35662306a36Sopenharmony_ci							    *vskiplines);
35762306a36Sopenharmony_ci			} else {
35862306a36Sopenharmony_ci				val = GET_SCL_FT_BILI_DN(src, dst);
35962306a36Sopenharmony_ci			}
36062306a36Sopenharmony_ci		}
36162306a36Sopenharmony_ci	}
36262306a36Sopenharmony_ci
36362306a36Sopenharmony_ci	return val;
36462306a36Sopenharmony_ci}
36562306a36Sopenharmony_ci
36662306a36Sopenharmony_cistatic void scl_vop_cal_scl_fac(struct vop *vop, const struct vop_win_data *win,
36762306a36Sopenharmony_ci			     uint32_t src_w, uint32_t src_h, uint32_t dst_w,
36862306a36Sopenharmony_ci			     uint32_t dst_h, const struct drm_format_info *info)
36962306a36Sopenharmony_ci{
37062306a36Sopenharmony_ci	uint16_t yrgb_hor_scl_mode, yrgb_ver_scl_mode;
37162306a36Sopenharmony_ci	uint16_t cbcr_hor_scl_mode = SCALE_NONE;
37262306a36Sopenharmony_ci	uint16_t cbcr_ver_scl_mode = SCALE_NONE;
37362306a36Sopenharmony_ci	bool is_yuv = false;
37462306a36Sopenharmony_ci	uint16_t cbcr_src_w = src_w / info->hsub;
37562306a36Sopenharmony_ci	uint16_t cbcr_src_h = src_h / info->vsub;
37662306a36Sopenharmony_ci	uint16_t vsu_mode;
37762306a36Sopenharmony_ci	uint16_t lb_mode;
37862306a36Sopenharmony_ci	uint32_t val;
37962306a36Sopenharmony_ci	int vskiplines;
38062306a36Sopenharmony_ci
38162306a36Sopenharmony_ci	if (info->is_yuv)
38262306a36Sopenharmony_ci		is_yuv = true;
38362306a36Sopenharmony_ci
38462306a36Sopenharmony_ci	if (dst_w > 3840) {
38562306a36Sopenharmony_ci		DRM_DEV_ERROR(vop->dev, "Maximum dst width (3840) exceeded\n");
38662306a36Sopenharmony_ci		return;
38762306a36Sopenharmony_ci	}
38862306a36Sopenharmony_ci
38962306a36Sopenharmony_ci	if (!win->phy->scl->ext) {
39062306a36Sopenharmony_ci		VOP_SCL_SET(vop, win, scale_yrgb_x,
39162306a36Sopenharmony_ci			    scl_cal_scale2(src_w, dst_w));
39262306a36Sopenharmony_ci		VOP_SCL_SET(vop, win, scale_yrgb_y,
39362306a36Sopenharmony_ci			    scl_cal_scale2(src_h, dst_h));
39462306a36Sopenharmony_ci		if (is_yuv) {
39562306a36Sopenharmony_ci			VOP_SCL_SET(vop, win, scale_cbcr_x,
39662306a36Sopenharmony_ci				    scl_cal_scale2(cbcr_src_w, dst_w));
39762306a36Sopenharmony_ci			VOP_SCL_SET(vop, win, scale_cbcr_y,
39862306a36Sopenharmony_ci				    scl_cal_scale2(cbcr_src_h, dst_h));
39962306a36Sopenharmony_ci		}
40062306a36Sopenharmony_ci		return;
40162306a36Sopenharmony_ci	}
40262306a36Sopenharmony_ci
40362306a36Sopenharmony_ci	yrgb_hor_scl_mode = scl_get_scl_mode(src_w, dst_w);
40462306a36Sopenharmony_ci	yrgb_ver_scl_mode = scl_get_scl_mode(src_h, dst_h);
40562306a36Sopenharmony_ci
40662306a36Sopenharmony_ci	if (is_yuv) {
40762306a36Sopenharmony_ci		cbcr_hor_scl_mode = scl_get_scl_mode(cbcr_src_w, dst_w);
40862306a36Sopenharmony_ci		cbcr_ver_scl_mode = scl_get_scl_mode(cbcr_src_h, dst_h);
40962306a36Sopenharmony_ci		if (cbcr_hor_scl_mode == SCALE_DOWN)
41062306a36Sopenharmony_ci			lb_mode = scl_vop_cal_lb_mode(dst_w, true);
41162306a36Sopenharmony_ci		else
41262306a36Sopenharmony_ci			lb_mode = scl_vop_cal_lb_mode(cbcr_src_w, true);
41362306a36Sopenharmony_ci	} else {
41462306a36Sopenharmony_ci		if (yrgb_hor_scl_mode == SCALE_DOWN)
41562306a36Sopenharmony_ci			lb_mode = scl_vop_cal_lb_mode(dst_w, false);
41662306a36Sopenharmony_ci		else
41762306a36Sopenharmony_ci			lb_mode = scl_vop_cal_lb_mode(src_w, false);
41862306a36Sopenharmony_ci	}
41962306a36Sopenharmony_ci
42062306a36Sopenharmony_ci	VOP_SCL_SET_EXT(vop, win, lb_mode, lb_mode);
42162306a36Sopenharmony_ci	if (lb_mode == LB_RGB_3840X2) {
42262306a36Sopenharmony_ci		if (yrgb_ver_scl_mode != SCALE_NONE) {
42362306a36Sopenharmony_ci			DRM_DEV_ERROR(vop->dev, "not allow yrgb ver scale\n");
42462306a36Sopenharmony_ci			return;
42562306a36Sopenharmony_ci		}
42662306a36Sopenharmony_ci		if (cbcr_ver_scl_mode != SCALE_NONE) {
42762306a36Sopenharmony_ci			DRM_DEV_ERROR(vop->dev, "not allow cbcr ver scale\n");
42862306a36Sopenharmony_ci			return;
42962306a36Sopenharmony_ci		}
43062306a36Sopenharmony_ci		vsu_mode = SCALE_UP_BIL;
43162306a36Sopenharmony_ci	} else if (lb_mode == LB_RGB_2560X4) {
43262306a36Sopenharmony_ci		vsu_mode = SCALE_UP_BIL;
43362306a36Sopenharmony_ci	} else {
43462306a36Sopenharmony_ci		vsu_mode = SCALE_UP_BIC;
43562306a36Sopenharmony_ci	}
43662306a36Sopenharmony_ci
43762306a36Sopenharmony_ci	val = scl_vop_cal_scale(yrgb_hor_scl_mode, src_w, dst_w,
43862306a36Sopenharmony_ci				true, 0, NULL);
43962306a36Sopenharmony_ci	VOP_SCL_SET(vop, win, scale_yrgb_x, val);
44062306a36Sopenharmony_ci	val = scl_vop_cal_scale(yrgb_ver_scl_mode, src_h, dst_h,
44162306a36Sopenharmony_ci				false, vsu_mode, &vskiplines);
44262306a36Sopenharmony_ci	VOP_SCL_SET(vop, win, scale_yrgb_y, val);
44362306a36Sopenharmony_ci
44462306a36Sopenharmony_ci	VOP_SCL_SET_EXT(vop, win, vsd_yrgb_gt4, vskiplines == 4);
44562306a36Sopenharmony_ci	VOP_SCL_SET_EXT(vop, win, vsd_yrgb_gt2, vskiplines == 2);
44662306a36Sopenharmony_ci
44762306a36Sopenharmony_ci	VOP_SCL_SET_EXT(vop, win, yrgb_hor_scl_mode, yrgb_hor_scl_mode);
44862306a36Sopenharmony_ci	VOP_SCL_SET_EXT(vop, win, yrgb_ver_scl_mode, yrgb_ver_scl_mode);
44962306a36Sopenharmony_ci	VOP_SCL_SET_EXT(vop, win, yrgb_hsd_mode, SCALE_DOWN_BIL);
45062306a36Sopenharmony_ci	VOP_SCL_SET_EXT(vop, win, yrgb_vsd_mode, SCALE_DOWN_BIL);
45162306a36Sopenharmony_ci	VOP_SCL_SET_EXT(vop, win, yrgb_vsu_mode, vsu_mode);
45262306a36Sopenharmony_ci	if (is_yuv) {
45362306a36Sopenharmony_ci		val = scl_vop_cal_scale(cbcr_hor_scl_mode, cbcr_src_w,
45462306a36Sopenharmony_ci					dst_w, true, 0, NULL);
45562306a36Sopenharmony_ci		VOP_SCL_SET(vop, win, scale_cbcr_x, val);
45662306a36Sopenharmony_ci		val = scl_vop_cal_scale(cbcr_ver_scl_mode, cbcr_src_h,
45762306a36Sopenharmony_ci					dst_h, false, vsu_mode, &vskiplines);
45862306a36Sopenharmony_ci		VOP_SCL_SET(vop, win, scale_cbcr_y, val);
45962306a36Sopenharmony_ci
46062306a36Sopenharmony_ci		VOP_SCL_SET_EXT(vop, win, vsd_cbcr_gt4, vskiplines == 4);
46162306a36Sopenharmony_ci		VOP_SCL_SET_EXT(vop, win, vsd_cbcr_gt2, vskiplines == 2);
46262306a36Sopenharmony_ci		VOP_SCL_SET_EXT(vop, win, cbcr_hor_scl_mode, cbcr_hor_scl_mode);
46362306a36Sopenharmony_ci		VOP_SCL_SET_EXT(vop, win, cbcr_ver_scl_mode, cbcr_ver_scl_mode);
46462306a36Sopenharmony_ci		VOP_SCL_SET_EXT(vop, win, cbcr_hsd_mode, SCALE_DOWN_BIL);
46562306a36Sopenharmony_ci		VOP_SCL_SET_EXT(vop, win, cbcr_vsd_mode, SCALE_DOWN_BIL);
46662306a36Sopenharmony_ci		VOP_SCL_SET_EXT(vop, win, cbcr_vsu_mode, vsu_mode);
46762306a36Sopenharmony_ci	}
46862306a36Sopenharmony_ci}
46962306a36Sopenharmony_ci
47062306a36Sopenharmony_cistatic void vop_dsp_hold_valid_irq_enable(struct vop *vop)
47162306a36Sopenharmony_ci{
47262306a36Sopenharmony_ci	unsigned long flags;
47362306a36Sopenharmony_ci
47462306a36Sopenharmony_ci	if (WARN_ON(!vop->is_enabled))
47562306a36Sopenharmony_ci		return;
47662306a36Sopenharmony_ci
47762306a36Sopenharmony_ci	spin_lock_irqsave(&vop->irq_lock, flags);
47862306a36Sopenharmony_ci
47962306a36Sopenharmony_ci	VOP_INTR_SET_TYPE(vop, clear, DSP_HOLD_VALID_INTR, 1);
48062306a36Sopenharmony_ci	VOP_INTR_SET_TYPE(vop, enable, DSP_HOLD_VALID_INTR, 1);
48162306a36Sopenharmony_ci
48262306a36Sopenharmony_ci	spin_unlock_irqrestore(&vop->irq_lock, flags);
48362306a36Sopenharmony_ci}
48462306a36Sopenharmony_ci
48562306a36Sopenharmony_cistatic void vop_dsp_hold_valid_irq_disable(struct vop *vop)
48662306a36Sopenharmony_ci{
48762306a36Sopenharmony_ci	unsigned long flags;
48862306a36Sopenharmony_ci
48962306a36Sopenharmony_ci	if (WARN_ON(!vop->is_enabled))
49062306a36Sopenharmony_ci		return;
49162306a36Sopenharmony_ci
49262306a36Sopenharmony_ci	spin_lock_irqsave(&vop->irq_lock, flags);
49362306a36Sopenharmony_ci
49462306a36Sopenharmony_ci	VOP_INTR_SET_TYPE(vop, enable, DSP_HOLD_VALID_INTR, 0);
49562306a36Sopenharmony_ci
49662306a36Sopenharmony_ci	spin_unlock_irqrestore(&vop->irq_lock, flags);
49762306a36Sopenharmony_ci}
49862306a36Sopenharmony_ci
49962306a36Sopenharmony_ci/*
50062306a36Sopenharmony_ci * (1) each frame starts at the start of the Vsync pulse which is signaled by
50162306a36Sopenharmony_ci *     the "FRAME_SYNC" interrupt.
50262306a36Sopenharmony_ci * (2) the active data region of each frame ends at dsp_vact_end
50362306a36Sopenharmony_ci * (3) we should program this same number (dsp_vact_end) into dsp_line_frag_num,
50462306a36Sopenharmony_ci *      to get "LINE_FLAG" interrupt at the end of the active on screen data.
50562306a36Sopenharmony_ci *
50662306a36Sopenharmony_ci * VOP_INTR_CTRL0.dsp_line_frag_num = VOP_DSP_VACT_ST_END.dsp_vact_end
50762306a36Sopenharmony_ci * Interrupts
50862306a36Sopenharmony_ci * LINE_FLAG -------------------------------+
50962306a36Sopenharmony_ci * FRAME_SYNC ----+                         |
51062306a36Sopenharmony_ci *                |                         |
51162306a36Sopenharmony_ci *                v                         v
51262306a36Sopenharmony_ci *                | Vsync | Vbp |  Vactive  | Vfp |
51362306a36Sopenharmony_ci *                        ^     ^           ^     ^
51462306a36Sopenharmony_ci *                        |     |           |     |
51562306a36Sopenharmony_ci *                        |     |           |     |
51662306a36Sopenharmony_ci * dsp_vs_end ------------+     |           |     |   VOP_DSP_VTOTAL_VS_END
51762306a36Sopenharmony_ci * dsp_vact_start --------------+           |     |   VOP_DSP_VACT_ST_END
51862306a36Sopenharmony_ci * dsp_vact_end ----------------------------+     |   VOP_DSP_VACT_ST_END
51962306a36Sopenharmony_ci * dsp_total -------------------------------------+   VOP_DSP_VTOTAL_VS_END
52062306a36Sopenharmony_ci */
52162306a36Sopenharmony_cistatic bool vop_line_flag_irq_is_enabled(struct vop *vop)
52262306a36Sopenharmony_ci{
52362306a36Sopenharmony_ci	uint32_t line_flag_irq;
52462306a36Sopenharmony_ci	unsigned long flags;
52562306a36Sopenharmony_ci
52662306a36Sopenharmony_ci	spin_lock_irqsave(&vop->irq_lock, flags);
52762306a36Sopenharmony_ci
52862306a36Sopenharmony_ci	line_flag_irq = VOP_INTR_GET_TYPE(vop, enable, LINE_FLAG_INTR);
52962306a36Sopenharmony_ci
53062306a36Sopenharmony_ci	spin_unlock_irqrestore(&vop->irq_lock, flags);
53162306a36Sopenharmony_ci
53262306a36Sopenharmony_ci	return !!line_flag_irq;
53362306a36Sopenharmony_ci}
53462306a36Sopenharmony_ci
53562306a36Sopenharmony_cistatic void vop_line_flag_irq_enable(struct vop *vop)
53662306a36Sopenharmony_ci{
53762306a36Sopenharmony_ci	unsigned long flags;
53862306a36Sopenharmony_ci
53962306a36Sopenharmony_ci	if (WARN_ON(!vop->is_enabled))
54062306a36Sopenharmony_ci		return;
54162306a36Sopenharmony_ci
54262306a36Sopenharmony_ci	spin_lock_irqsave(&vop->irq_lock, flags);
54362306a36Sopenharmony_ci
54462306a36Sopenharmony_ci	VOP_INTR_SET_TYPE(vop, clear, LINE_FLAG_INTR, 1);
54562306a36Sopenharmony_ci	VOP_INTR_SET_TYPE(vop, enable, LINE_FLAG_INTR, 1);
54662306a36Sopenharmony_ci
54762306a36Sopenharmony_ci	spin_unlock_irqrestore(&vop->irq_lock, flags);
54862306a36Sopenharmony_ci}
54962306a36Sopenharmony_ci
55062306a36Sopenharmony_cistatic void vop_line_flag_irq_disable(struct vop *vop)
55162306a36Sopenharmony_ci{
55262306a36Sopenharmony_ci	unsigned long flags;
55362306a36Sopenharmony_ci
55462306a36Sopenharmony_ci	if (WARN_ON(!vop->is_enabled))
55562306a36Sopenharmony_ci		return;
55662306a36Sopenharmony_ci
55762306a36Sopenharmony_ci	spin_lock_irqsave(&vop->irq_lock, flags);
55862306a36Sopenharmony_ci
55962306a36Sopenharmony_ci	VOP_INTR_SET_TYPE(vop, enable, LINE_FLAG_INTR, 0);
56062306a36Sopenharmony_ci
56162306a36Sopenharmony_ci	spin_unlock_irqrestore(&vop->irq_lock, flags);
56262306a36Sopenharmony_ci}
56362306a36Sopenharmony_ci
56462306a36Sopenharmony_cistatic int vop_core_clks_enable(struct vop *vop)
56562306a36Sopenharmony_ci{
56662306a36Sopenharmony_ci	int ret;
56762306a36Sopenharmony_ci
56862306a36Sopenharmony_ci	ret = clk_enable(vop->hclk);
56962306a36Sopenharmony_ci	if (ret < 0)
57062306a36Sopenharmony_ci		return ret;
57162306a36Sopenharmony_ci
57262306a36Sopenharmony_ci	ret = clk_enable(vop->aclk);
57362306a36Sopenharmony_ci	if (ret < 0)
57462306a36Sopenharmony_ci		goto err_disable_hclk;
57562306a36Sopenharmony_ci
57662306a36Sopenharmony_ci	return 0;
57762306a36Sopenharmony_ci
57862306a36Sopenharmony_cierr_disable_hclk:
57962306a36Sopenharmony_ci	clk_disable(vop->hclk);
58062306a36Sopenharmony_ci	return ret;
58162306a36Sopenharmony_ci}
58262306a36Sopenharmony_ci
58362306a36Sopenharmony_cistatic void vop_core_clks_disable(struct vop *vop)
58462306a36Sopenharmony_ci{
58562306a36Sopenharmony_ci	clk_disable(vop->aclk);
58662306a36Sopenharmony_ci	clk_disable(vop->hclk);
58762306a36Sopenharmony_ci}
58862306a36Sopenharmony_ci
58962306a36Sopenharmony_cistatic void vop_win_disable(struct vop *vop, const struct vop_win *vop_win)
59062306a36Sopenharmony_ci{
59162306a36Sopenharmony_ci	const struct vop_win_data *win = vop_win->data;
59262306a36Sopenharmony_ci
59362306a36Sopenharmony_ci	if (win->phy->scl && win->phy->scl->ext) {
59462306a36Sopenharmony_ci		VOP_SCL_SET_EXT(vop, win, yrgb_hor_scl_mode, SCALE_NONE);
59562306a36Sopenharmony_ci		VOP_SCL_SET_EXT(vop, win, yrgb_ver_scl_mode, SCALE_NONE);
59662306a36Sopenharmony_ci		VOP_SCL_SET_EXT(vop, win, cbcr_hor_scl_mode, SCALE_NONE);
59762306a36Sopenharmony_ci		VOP_SCL_SET_EXT(vop, win, cbcr_ver_scl_mode, SCALE_NONE);
59862306a36Sopenharmony_ci	}
59962306a36Sopenharmony_ci
60062306a36Sopenharmony_ci	VOP_WIN_SET(vop, win, enable, 0);
60162306a36Sopenharmony_ci	vop->win_enabled &= ~BIT(VOP_WIN_TO_INDEX(vop_win));
60262306a36Sopenharmony_ci}
60362306a36Sopenharmony_ci
60462306a36Sopenharmony_cistatic int vop_enable(struct drm_crtc *crtc, struct drm_crtc_state *old_state)
60562306a36Sopenharmony_ci{
60662306a36Sopenharmony_ci	struct vop *vop = to_vop(crtc);
60762306a36Sopenharmony_ci	int ret, i;
60862306a36Sopenharmony_ci
60962306a36Sopenharmony_ci	ret = pm_runtime_resume_and_get(vop->dev);
61062306a36Sopenharmony_ci	if (ret < 0) {
61162306a36Sopenharmony_ci		DRM_DEV_ERROR(vop->dev, "failed to get pm runtime: %d\n", ret);
61262306a36Sopenharmony_ci		return ret;
61362306a36Sopenharmony_ci	}
61462306a36Sopenharmony_ci
61562306a36Sopenharmony_ci	ret = vop_core_clks_enable(vop);
61662306a36Sopenharmony_ci	if (WARN_ON(ret < 0))
61762306a36Sopenharmony_ci		goto err_put_pm_runtime;
61862306a36Sopenharmony_ci
61962306a36Sopenharmony_ci	ret = clk_enable(vop->dclk);
62062306a36Sopenharmony_ci	if (WARN_ON(ret < 0))
62162306a36Sopenharmony_ci		goto err_disable_core;
62262306a36Sopenharmony_ci
62362306a36Sopenharmony_ci	/*
62462306a36Sopenharmony_ci	 * Slave iommu shares power, irq and clock with vop.  It was associated
62562306a36Sopenharmony_ci	 * automatically with this master device via common driver code.
62662306a36Sopenharmony_ci	 * Now that we have enabled the clock we attach it to the shared drm
62762306a36Sopenharmony_ci	 * mapping.
62862306a36Sopenharmony_ci	 */
62962306a36Sopenharmony_ci	ret = rockchip_drm_dma_attach_device(vop->drm_dev, vop->dev);
63062306a36Sopenharmony_ci	if (ret) {
63162306a36Sopenharmony_ci		DRM_DEV_ERROR(vop->dev,
63262306a36Sopenharmony_ci			      "failed to attach dma mapping, %d\n", ret);
63362306a36Sopenharmony_ci		goto err_disable_dclk;
63462306a36Sopenharmony_ci	}
63562306a36Sopenharmony_ci
63662306a36Sopenharmony_ci	spin_lock(&vop->reg_lock);
63762306a36Sopenharmony_ci	for (i = 0; i < vop->len; i += 4)
63862306a36Sopenharmony_ci		writel_relaxed(vop->regsbak[i / 4], vop->regs + i);
63962306a36Sopenharmony_ci
64062306a36Sopenharmony_ci	/*
64162306a36Sopenharmony_ci	 * We need to make sure that all windows are disabled before we
64262306a36Sopenharmony_ci	 * enable the crtc. Otherwise we might try to scan from a destroyed
64362306a36Sopenharmony_ci	 * buffer later.
64462306a36Sopenharmony_ci	 *
64562306a36Sopenharmony_ci	 * In the case of enable-after-PSR, we don't need to worry about this
64662306a36Sopenharmony_ci	 * case since the buffer is guaranteed to be valid and disabling the
64762306a36Sopenharmony_ci	 * window will result in screen glitches on PSR exit.
64862306a36Sopenharmony_ci	 */
64962306a36Sopenharmony_ci	if (!old_state || !old_state->self_refresh_active) {
65062306a36Sopenharmony_ci		for (i = 0; i < vop->data->win_size; i++) {
65162306a36Sopenharmony_ci			struct vop_win *vop_win = &vop->win[i];
65262306a36Sopenharmony_ci
65362306a36Sopenharmony_ci			vop_win_disable(vop, vop_win);
65462306a36Sopenharmony_ci		}
65562306a36Sopenharmony_ci	}
65662306a36Sopenharmony_ci
65762306a36Sopenharmony_ci	if (vop->data->afbc) {
65862306a36Sopenharmony_ci		struct rockchip_crtc_state *s;
65962306a36Sopenharmony_ci		/*
66062306a36Sopenharmony_ci		 * Disable AFBC and forget there was a vop window with AFBC
66162306a36Sopenharmony_ci		 */
66262306a36Sopenharmony_ci		VOP_AFBC_SET(vop, enable, 0);
66362306a36Sopenharmony_ci		s = to_rockchip_crtc_state(crtc->state);
66462306a36Sopenharmony_ci		s->enable_afbc = false;
66562306a36Sopenharmony_ci	}
66662306a36Sopenharmony_ci
66762306a36Sopenharmony_ci	vop_cfg_done(vop);
66862306a36Sopenharmony_ci
66962306a36Sopenharmony_ci	spin_unlock(&vop->reg_lock);
67062306a36Sopenharmony_ci
67162306a36Sopenharmony_ci	/*
67262306a36Sopenharmony_ci	 * At here, vop clock & iommu is enable, R/W vop regs would be safe.
67362306a36Sopenharmony_ci	 */
67462306a36Sopenharmony_ci	vop->is_enabled = true;
67562306a36Sopenharmony_ci
67662306a36Sopenharmony_ci	spin_lock(&vop->reg_lock);
67762306a36Sopenharmony_ci
67862306a36Sopenharmony_ci	VOP_REG_SET(vop, common, standby, 1);
67962306a36Sopenharmony_ci
68062306a36Sopenharmony_ci	spin_unlock(&vop->reg_lock);
68162306a36Sopenharmony_ci
68262306a36Sopenharmony_ci	drm_crtc_vblank_on(crtc);
68362306a36Sopenharmony_ci
68462306a36Sopenharmony_ci	return 0;
68562306a36Sopenharmony_ci
68662306a36Sopenharmony_cierr_disable_dclk:
68762306a36Sopenharmony_ci	clk_disable(vop->dclk);
68862306a36Sopenharmony_cierr_disable_core:
68962306a36Sopenharmony_ci	vop_core_clks_disable(vop);
69062306a36Sopenharmony_cierr_put_pm_runtime:
69162306a36Sopenharmony_ci	pm_runtime_put_sync(vop->dev);
69262306a36Sopenharmony_ci	return ret;
69362306a36Sopenharmony_ci}
69462306a36Sopenharmony_ci
69562306a36Sopenharmony_cistatic void rockchip_drm_set_win_enabled(struct drm_crtc *crtc, bool enabled)
69662306a36Sopenharmony_ci{
69762306a36Sopenharmony_ci        struct vop *vop = to_vop(crtc);
69862306a36Sopenharmony_ci        int i;
69962306a36Sopenharmony_ci
70062306a36Sopenharmony_ci        spin_lock(&vop->reg_lock);
70162306a36Sopenharmony_ci
70262306a36Sopenharmony_ci        for (i = 0; i < vop->data->win_size; i++) {
70362306a36Sopenharmony_ci                struct vop_win *vop_win = &vop->win[i];
70462306a36Sopenharmony_ci                const struct vop_win_data *win = vop_win->data;
70562306a36Sopenharmony_ci
70662306a36Sopenharmony_ci                VOP_WIN_SET(vop, win, enable,
70762306a36Sopenharmony_ci                            enabled && (vop->win_enabled & BIT(i)));
70862306a36Sopenharmony_ci        }
70962306a36Sopenharmony_ci        vop_cfg_done(vop);
71062306a36Sopenharmony_ci
71162306a36Sopenharmony_ci        spin_unlock(&vop->reg_lock);
71262306a36Sopenharmony_ci}
71362306a36Sopenharmony_ci
71462306a36Sopenharmony_cistatic void vop_crtc_atomic_disable(struct drm_crtc *crtc,
71562306a36Sopenharmony_ci				    struct drm_atomic_state *state)
71662306a36Sopenharmony_ci{
71762306a36Sopenharmony_ci	struct vop *vop = to_vop(crtc);
71862306a36Sopenharmony_ci
71962306a36Sopenharmony_ci	WARN_ON(vop->event);
72062306a36Sopenharmony_ci
72162306a36Sopenharmony_ci	if (crtc->state->self_refresh_active)
72262306a36Sopenharmony_ci		rockchip_drm_set_win_enabled(crtc, false);
72362306a36Sopenharmony_ci
72462306a36Sopenharmony_ci	if (crtc->state->self_refresh_active)
72562306a36Sopenharmony_ci		goto out;
72662306a36Sopenharmony_ci
72762306a36Sopenharmony_ci	mutex_lock(&vop->vop_lock);
72862306a36Sopenharmony_ci
72962306a36Sopenharmony_ci	drm_crtc_vblank_off(crtc);
73062306a36Sopenharmony_ci
73162306a36Sopenharmony_ci	/*
73262306a36Sopenharmony_ci	 * Vop standby will take effect at end of current frame,
73362306a36Sopenharmony_ci	 * if dsp hold valid irq happen, it means standby complete.
73462306a36Sopenharmony_ci	 *
73562306a36Sopenharmony_ci	 * we must wait standby complete when we want to disable aclk,
73662306a36Sopenharmony_ci	 * if not, memory bus maybe dead.
73762306a36Sopenharmony_ci	 */
73862306a36Sopenharmony_ci	reinit_completion(&vop->dsp_hold_completion);
73962306a36Sopenharmony_ci	vop_dsp_hold_valid_irq_enable(vop);
74062306a36Sopenharmony_ci
74162306a36Sopenharmony_ci	spin_lock(&vop->reg_lock);
74262306a36Sopenharmony_ci
74362306a36Sopenharmony_ci	VOP_REG_SET(vop, common, standby, 1);
74462306a36Sopenharmony_ci
74562306a36Sopenharmony_ci	spin_unlock(&vop->reg_lock);
74662306a36Sopenharmony_ci
74762306a36Sopenharmony_ci	if (!wait_for_completion_timeout(&vop->dsp_hold_completion,
74862306a36Sopenharmony_ci					 msecs_to_jiffies(200)))
74962306a36Sopenharmony_ci		WARN(1, "%s: timed out waiting for DSP hold", crtc->name);
75062306a36Sopenharmony_ci
75162306a36Sopenharmony_ci	vop_dsp_hold_valid_irq_disable(vop);
75262306a36Sopenharmony_ci
75362306a36Sopenharmony_ci	vop->is_enabled = false;
75462306a36Sopenharmony_ci
75562306a36Sopenharmony_ci	/*
75662306a36Sopenharmony_ci	 * vop standby complete, so iommu detach is safe.
75762306a36Sopenharmony_ci	 */
75862306a36Sopenharmony_ci	rockchip_drm_dma_detach_device(vop->drm_dev, vop->dev);
75962306a36Sopenharmony_ci
76062306a36Sopenharmony_ci	clk_disable(vop->dclk);
76162306a36Sopenharmony_ci	vop_core_clks_disable(vop);
76262306a36Sopenharmony_ci	pm_runtime_put(vop->dev);
76362306a36Sopenharmony_ci
76462306a36Sopenharmony_ci	mutex_unlock(&vop->vop_lock);
76562306a36Sopenharmony_ci
76662306a36Sopenharmony_ciout:
76762306a36Sopenharmony_ci	if (crtc->state->event && !crtc->state->active) {
76862306a36Sopenharmony_ci		spin_lock_irq(&crtc->dev->event_lock);
76962306a36Sopenharmony_ci		drm_crtc_send_vblank_event(crtc, crtc->state->event);
77062306a36Sopenharmony_ci		spin_unlock_irq(&crtc->dev->event_lock);
77162306a36Sopenharmony_ci
77262306a36Sopenharmony_ci		crtc->state->event = NULL;
77362306a36Sopenharmony_ci	}
77462306a36Sopenharmony_ci}
77562306a36Sopenharmony_ci
77662306a36Sopenharmony_cistatic void vop_plane_destroy(struct drm_plane *plane)
77762306a36Sopenharmony_ci{
77862306a36Sopenharmony_ci	drm_plane_cleanup(plane);
77962306a36Sopenharmony_ci}
78062306a36Sopenharmony_ci
78162306a36Sopenharmony_cistatic inline bool rockchip_afbc(u64 modifier)
78262306a36Sopenharmony_ci{
78362306a36Sopenharmony_ci	return modifier == ROCKCHIP_AFBC_MOD;
78462306a36Sopenharmony_ci}
78562306a36Sopenharmony_ci
78662306a36Sopenharmony_cistatic bool rockchip_mod_supported(struct drm_plane *plane,
78762306a36Sopenharmony_ci				   u32 format, u64 modifier)
78862306a36Sopenharmony_ci{
78962306a36Sopenharmony_ci	if (modifier == DRM_FORMAT_MOD_LINEAR)
79062306a36Sopenharmony_ci		return true;
79162306a36Sopenharmony_ci
79262306a36Sopenharmony_ci	if (!rockchip_afbc(modifier)) {
79362306a36Sopenharmony_ci		DRM_DEBUG_KMS("Unsupported format modifier 0x%llx\n", modifier);
79462306a36Sopenharmony_ci
79562306a36Sopenharmony_ci		return false;
79662306a36Sopenharmony_ci	}
79762306a36Sopenharmony_ci
79862306a36Sopenharmony_ci	return vop_convert_afbc_format(format) >= 0;
79962306a36Sopenharmony_ci}
80062306a36Sopenharmony_ci
80162306a36Sopenharmony_cistatic int vop_plane_atomic_check(struct drm_plane *plane,
80262306a36Sopenharmony_ci			   struct drm_atomic_state *state)
80362306a36Sopenharmony_ci{
80462306a36Sopenharmony_ci	struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state,
80562306a36Sopenharmony_ci										 plane);
80662306a36Sopenharmony_ci	struct drm_crtc *crtc = new_plane_state->crtc;
80762306a36Sopenharmony_ci	struct drm_crtc_state *crtc_state;
80862306a36Sopenharmony_ci	struct drm_framebuffer *fb = new_plane_state->fb;
80962306a36Sopenharmony_ci	struct vop_win *vop_win = to_vop_win(plane);
81062306a36Sopenharmony_ci	const struct vop_win_data *win = vop_win->data;
81162306a36Sopenharmony_ci	int ret;
81262306a36Sopenharmony_ci	int min_scale = win->phy->scl ? FRAC_16_16(1, 8) :
81362306a36Sopenharmony_ci					DRM_PLANE_NO_SCALING;
81462306a36Sopenharmony_ci	int max_scale = win->phy->scl ? FRAC_16_16(8, 1) :
81562306a36Sopenharmony_ci					DRM_PLANE_NO_SCALING;
81662306a36Sopenharmony_ci
81762306a36Sopenharmony_ci	if (!crtc || WARN_ON(!fb))
81862306a36Sopenharmony_ci		return 0;
81962306a36Sopenharmony_ci
82062306a36Sopenharmony_ci	crtc_state = drm_atomic_get_existing_crtc_state(state,
82162306a36Sopenharmony_ci							crtc);
82262306a36Sopenharmony_ci	if (WARN_ON(!crtc_state))
82362306a36Sopenharmony_ci		return -EINVAL;
82462306a36Sopenharmony_ci
82562306a36Sopenharmony_ci	ret = drm_atomic_helper_check_plane_state(new_plane_state, crtc_state,
82662306a36Sopenharmony_ci						  min_scale, max_scale,
82762306a36Sopenharmony_ci						  true, true);
82862306a36Sopenharmony_ci	if (ret)
82962306a36Sopenharmony_ci		return ret;
83062306a36Sopenharmony_ci
83162306a36Sopenharmony_ci	if (!new_plane_state->visible)
83262306a36Sopenharmony_ci		return 0;
83362306a36Sopenharmony_ci
83462306a36Sopenharmony_ci	ret = vop_convert_format(fb->format->format);
83562306a36Sopenharmony_ci	if (ret < 0)
83662306a36Sopenharmony_ci		return ret;
83762306a36Sopenharmony_ci
83862306a36Sopenharmony_ci	/*
83962306a36Sopenharmony_ci	 * Src.x1 can be odd when do clip, but yuv plane start point
84062306a36Sopenharmony_ci	 * need align with 2 pixel.
84162306a36Sopenharmony_ci	 */
84262306a36Sopenharmony_ci	if (fb->format->is_yuv && ((new_plane_state->src.x1 >> 16) % 2)) {
84362306a36Sopenharmony_ci		DRM_DEBUG_KMS("Invalid Source: Yuv format not support odd xpos\n");
84462306a36Sopenharmony_ci		return -EINVAL;
84562306a36Sopenharmony_ci	}
84662306a36Sopenharmony_ci
84762306a36Sopenharmony_ci	if (fb->format->is_yuv && new_plane_state->rotation & DRM_MODE_REFLECT_Y) {
84862306a36Sopenharmony_ci		DRM_DEBUG_KMS("Invalid Source: Yuv format does not support this rotation\n");
84962306a36Sopenharmony_ci		return -EINVAL;
85062306a36Sopenharmony_ci	}
85162306a36Sopenharmony_ci
85262306a36Sopenharmony_ci	if (rockchip_afbc(fb->modifier)) {
85362306a36Sopenharmony_ci		struct vop *vop = to_vop(crtc);
85462306a36Sopenharmony_ci
85562306a36Sopenharmony_ci		if (!vop->data->afbc) {
85662306a36Sopenharmony_ci			DRM_DEBUG_KMS("vop does not support AFBC\n");
85762306a36Sopenharmony_ci			return -EINVAL;
85862306a36Sopenharmony_ci		}
85962306a36Sopenharmony_ci
86062306a36Sopenharmony_ci		ret = vop_convert_afbc_format(fb->format->format);
86162306a36Sopenharmony_ci		if (ret < 0)
86262306a36Sopenharmony_ci			return ret;
86362306a36Sopenharmony_ci
86462306a36Sopenharmony_ci		if (new_plane_state->src.x1 || new_plane_state->src.y1) {
86562306a36Sopenharmony_ci			DRM_DEBUG_KMS("AFBC does not support offset display, " \
86662306a36Sopenharmony_ci				      "xpos=%d, ypos=%d, offset=%d\n",
86762306a36Sopenharmony_ci				      new_plane_state->src.x1, new_plane_state->src.y1,
86862306a36Sopenharmony_ci				      fb->offsets[0]);
86962306a36Sopenharmony_ci			return -EINVAL;
87062306a36Sopenharmony_ci		}
87162306a36Sopenharmony_ci
87262306a36Sopenharmony_ci		if (new_plane_state->rotation && new_plane_state->rotation != DRM_MODE_ROTATE_0) {
87362306a36Sopenharmony_ci			DRM_DEBUG_KMS("No rotation support in AFBC, rotation=%d\n",
87462306a36Sopenharmony_ci				      new_plane_state->rotation);
87562306a36Sopenharmony_ci			return -EINVAL;
87662306a36Sopenharmony_ci		}
87762306a36Sopenharmony_ci	}
87862306a36Sopenharmony_ci
87962306a36Sopenharmony_ci	return 0;
88062306a36Sopenharmony_ci}
88162306a36Sopenharmony_ci
88262306a36Sopenharmony_cistatic void vop_plane_atomic_disable(struct drm_plane *plane,
88362306a36Sopenharmony_ci				     struct drm_atomic_state *state)
88462306a36Sopenharmony_ci{
88562306a36Sopenharmony_ci	struct drm_plane_state *old_state = drm_atomic_get_old_plane_state(state,
88662306a36Sopenharmony_ci									   plane);
88762306a36Sopenharmony_ci	struct vop_win *vop_win = to_vop_win(plane);
88862306a36Sopenharmony_ci	struct vop *vop = to_vop(old_state->crtc);
88962306a36Sopenharmony_ci
89062306a36Sopenharmony_ci	if (!old_state->crtc)
89162306a36Sopenharmony_ci		return;
89262306a36Sopenharmony_ci
89362306a36Sopenharmony_ci	spin_lock(&vop->reg_lock);
89462306a36Sopenharmony_ci
89562306a36Sopenharmony_ci	vop_win_disable(vop, vop_win);
89662306a36Sopenharmony_ci
89762306a36Sopenharmony_ci	spin_unlock(&vop->reg_lock);
89862306a36Sopenharmony_ci}
89962306a36Sopenharmony_ci
90062306a36Sopenharmony_cistatic void vop_plane_atomic_update(struct drm_plane *plane,
90162306a36Sopenharmony_ci		struct drm_atomic_state *state)
90262306a36Sopenharmony_ci{
90362306a36Sopenharmony_ci	struct drm_plane_state *new_state = drm_atomic_get_new_plane_state(state,
90462306a36Sopenharmony_ci									   plane);
90562306a36Sopenharmony_ci	struct drm_crtc *crtc = new_state->crtc;
90662306a36Sopenharmony_ci	struct vop_win *vop_win = to_vop_win(plane);
90762306a36Sopenharmony_ci	const struct vop_win_data *win = vop_win->data;
90862306a36Sopenharmony_ci	const struct vop_win_yuv2yuv_data *win_yuv2yuv = vop_win->yuv2yuv_data;
90962306a36Sopenharmony_ci	struct vop *vop = to_vop(new_state->crtc);
91062306a36Sopenharmony_ci	struct drm_framebuffer *fb = new_state->fb;
91162306a36Sopenharmony_ci	unsigned int actual_w, actual_h;
91262306a36Sopenharmony_ci	unsigned int dsp_stx, dsp_sty;
91362306a36Sopenharmony_ci	uint32_t act_info, dsp_info, dsp_st;
91462306a36Sopenharmony_ci	struct drm_rect *src = &new_state->src;
91562306a36Sopenharmony_ci	struct drm_rect *dest = &new_state->dst;
91662306a36Sopenharmony_ci	struct drm_gem_object *obj, *uv_obj;
91762306a36Sopenharmony_ci	struct rockchip_gem_object *rk_obj, *rk_uv_obj;
91862306a36Sopenharmony_ci	unsigned long offset;
91962306a36Sopenharmony_ci	dma_addr_t dma_addr;
92062306a36Sopenharmony_ci	uint32_t val;
92162306a36Sopenharmony_ci	bool rb_swap, uv_swap;
92262306a36Sopenharmony_ci	int win_index = VOP_WIN_TO_INDEX(vop_win);
92362306a36Sopenharmony_ci	int format;
92462306a36Sopenharmony_ci	int is_yuv = fb->format->is_yuv;
92562306a36Sopenharmony_ci	int i;
92662306a36Sopenharmony_ci
92762306a36Sopenharmony_ci	/*
92862306a36Sopenharmony_ci	 * can't update plane when vop is disabled.
92962306a36Sopenharmony_ci	 */
93062306a36Sopenharmony_ci	if (WARN_ON(!crtc))
93162306a36Sopenharmony_ci		return;
93262306a36Sopenharmony_ci
93362306a36Sopenharmony_ci	if (WARN_ON(!vop->is_enabled))
93462306a36Sopenharmony_ci		return;
93562306a36Sopenharmony_ci
93662306a36Sopenharmony_ci	if (!new_state->visible) {
93762306a36Sopenharmony_ci		vop_plane_atomic_disable(plane, state);
93862306a36Sopenharmony_ci		return;
93962306a36Sopenharmony_ci	}
94062306a36Sopenharmony_ci
94162306a36Sopenharmony_ci	obj = fb->obj[0];
94262306a36Sopenharmony_ci	rk_obj = to_rockchip_obj(obj);
94362306a36Sopenharmony_ci
94462306a36Sopenharmony_ci	actual_w = drm_rect_width(src) >> 16;
94562306a36Sopenharmony_ci	actual_h = drm_rect_height(src) >> 16;
94662306a36Sopenharmony_ci	act_info = (actual_h - 1) << 16 | ((actual_w - 1) & 0xffff);
94762306a36Sopenharmony_ci
94862306a36Sopenharmony_ci	dsp_info = (drm_rect_height(dest) - 1) << 16;
94962306a36Sopenharmony_ci	dsp_info |= (drm_rect_width(dest) - 1) & 0xffff;
95062306a36Sopenharmony_ci
95162306a36Sopenharmony_ci	dsp_stx = dest->x1 + crtc->mode.htotal - crtc->mode.hsync_start;
95262306a36Sopenharmony_ci	dsp_sty = dest->y1 + crtc->mode.vtotal - crtc->mode.vsync_start;
95362306a36Sopenharmony_ci	dsp_st = dsp_sty << 16 | (dsp_stx & 0xffff);
95462306a36Sopenharmony_ci
95562306a36Sopenharmony_ci	offset = (src->x1 >> 16) * fb->format->cpp[0];
95662306a36Sopenharmony_ci	offset += (src->y1 >> 16) * fb->pitches[0];
95762306a36Sopenharmony_ci	dma_addr = rk_obj->dma_addr + offset + fb->offsets[0];
95862306a36Sopenharmony_ci
95962306a36Sopenharmony_ci	/*
96062306a36Sopenharmony_ci	 * For y-mirroring we need to move address
96162306a36Sopenharmony_ci	 * to the beginning of the last line.
96262306a36Sopenharmony_ci	 */
96362306a36Sopenharmony_ci	if (new_state->rotation & DRM_MODE_REFLECT_Y)
96462306a36Sopenharmony_ci		dma_addr += (actual_h - 1) * fb->pitches[0];
96562306a36Sopenharmony_ci
96662306a36Sopenharmony_ci	format = vop_convert_format(fb->format->format);
96762306a36Sopenharmony_ci
96862306a36Sopenharmony_ci	spin_lock(&vop->reg_lock);
96962306a36Sopenharmony_ci
97062306a36Sopenharmony_ci	if (rockchip_afbc(fb->modifier)) {
97162306a36Sopenharmony_ci		int afbc_format = vop_convert_afbc_format(fb->format->format);
97262306a36Sopenharmony_ci
97362306a36Sopenharmony_ci		VOP_AFBC_SET(vop, format, afbc_format | AFBC_TILE_16x16);
97462306a36Sopenharmony_ci		VOP_AFBC_SET(vop, hreg_block_split, 0);
97562306a36Sopenharmony_ci		VOP_AFBC_SET(vop, win_sel, VOP_WIN_TO_INDEX(vop_win));
97662306a36Sopenharmony_ci		VOP_AFBC_SET(vop, hdr_ptr, dma_addr);
97762306a36Sopenharmony_ci		VOP_AFBC_SET(vop, pic_size, act_info);
97862306a36Sopenharmony_ci	}
97962306a36Sopenharmony_ci
98062306a36Sopenharmony_ci	VOP_WIN_SET(vop, win, format, format);
98162306a36Sopenharmony_ci	VOP_WIN_SET(vop, win, yrgb_vir, DIV_ROUND_UP(fb->pitches[0], 4));
98262306a36Sopenharmony_ci	VOP_WIN_SET(vop, win, yrgb_mst, dma_addr);
98362306a36Sopenharmony_ci	VOP_WIN_YUV2YUV_SET(vop, win_yuv2yuv, y2r_en, is_yuv);
98462306a36Sopenharmony_ci	VOP_WIN_SET(vop, win, y_mir_en,
98562306a36Sopenharmony_ci		    (new_state->rotation & DRM_MODE_REFLECT_Y) ? 1 : 0);
98662306a36Sopenharmony_ci	VOP_WIN_SET(vop, win, x_mir_en,
98762306a36Sopenharmony_ci		    (new_state->rotation & DRM_MODE_REFLECT_X) ? 1 : 0);
98862306a36Sopenharmony_ci
98962306a36Sopenharmony_ci	if (is_yuv) {
99062306a36Sopenharmony_ci		int hsub = fb->format->hsub;
99162306a36Sopenharmony_ci		int vsub = fb->format->vsub;
99262306a36Sopenharmony_ci		int bpp = fb->format->cpp[1];
99362306a36Sopenharmony_ci
99462306a36Sopenharmony_ci		uv_obj = fb->obj[1];
99562306a36Sopenharmony_ci		rk_uv_obj = to_rockchip_obj(uv_obj);
99662306a36Sopenharmony_ci
99762306a36Sopenharmony_ci		offset = (src->x1 >> 16) * bpp / hsub;
99862306a36Sopenharmony_ci		offset += (src->y1 >> 16) * fb->pitches[1] / vsub;
99962306a36Sopenharmony_ci
100062306a36Sopenharmony_ci		dma_addr = rk_uv_obj->dma_addr + offset + fb->offsets[1];
100162306a36Sopenharmony_ci		VOP_WIN_SET(vop, win, uv_vir, DIV_ROUND_UP(fb->pitches[1], 4));
100262306a36Sopenharmony_ci		VOP_WIN_SET(vop, win, uv_mst, dma_addr);
100362306a36Sopenharmony_ci
100462306a36Sopenharmony_ci		for (i = 0; i < NUM_YUV2YUV_COEFFICIENTS; i++) {
100562306a36Sopenharmony_ci			VOP_WIN_YUV2YUV_COEFFICIENT_SET(vop,
100662306a36Sopenharmony_ci							win_yuv2yuv,
100762306a36Sopenharmony_ci							y2r_coefficients[i],
100862306a36Sopenharmony_ci							bt601_yuv2rgb[i]);
100962306a36Sopenharmony_ci		}
101062306a36Sopenharmony_ci
101162306a36Sopenharmony_ci		uv_swap = has_uv_swapped(fb->format->format);
101262306a36Sopenharmony_ci		VOP_WIN_SET(vop, win, uv_swap, uv_swap);
101362306a36Sopenharmony_ci	}
101462306a36Sopenharmony_ci
101562306a36Sopenharmony_ci	if (win->phy->scl)
101662306a36Sopenharmony_ci		scl_vop_cal_scl_fac(vop, win, actual_w, actual_h,
101762306a36Sopenharmony_ci				    drm_rect_width(dest), drm_rect_height(dest),
101862306a36Sopenharmony_ci				    fb->format);
101962306a36Sopenharmony_ci
102062306a36Sopenharmony_ci	VOP_WIN_SET(vop, win, act_info, act_info);
102162306a36Sopenharmony_ci	VOP_WIN_SET(vop, win, dsp_info, dsp_info);
102262306a36Sopenharmony_ci	VOP_WIN_SET(vop, win, dsp_st, dsp_st);
102362306a36Sopenharmony_ci
102462306a36Sopenharmony_ci	rb_swap = has_rb_swapped(vop->data->version, fb->format->format);
102562306a36Sopenharmony_ci	VOP_WIN_SET(vop, win, rb_swap, rb_swap);
102662306a36Sopenharmony_ci
102762306a36Sopenharmony_ci	/*
102862306a36Sopenharmony_ci	 * Blending win0 with the background color doesn't seem to work
102962306a36Sopenharmony_ci	 * correctly. We only get the background color, no matter the contents
103062306a36Sopenharmony_ci	 * of the win0 framebuffer.  However, blending pre-multiplied color
103162306a36Sopenharmony_ci	 * with the default opaque black default background color is a no-op,
103262306a36Sopenharmony_ci	 * so we can just disable blending to get the correct result.
103362306a36Sopenharmony_ci	 */
103462306a36Sopenharmony_ci	if (fb->format->has_alpha && win_index > 0) {
103562306a36Sopenharmony_ci		VOP_WIN_SET(vop, win, dst_alpha_ctl,
103662306a36Sopenharmony_ci			    DST_FACTOR_M0(ALPHA_SRC_INVERSE));
103762306a36Sopenharmony_ci		val = SRC_ALPHA_EN(1) | SRC_COLOR_M0(ALPHA_SRC_PRE_MUL) |
103862306a36Sopenharmony_ci			SRC_ALPHA_M0(ALPHA_STRAIGHT) |
103962306a36Sopenharmony_ci			SRC_BLEND_M0(ALPHA_PER_PIX) |
104062306a36Sopenharmony_ci			SRC_ALPHA_CAL_M0(ALPHA_NO_SATURATION) |
104162306a36Sopenharmony_ci			SRC_FACTOR_M0(ALPHA_ONE);
104262306a36Sopenharmony_ci		VOP_WIN_SET(vop, win, src_alpha_ctl, val);
104362306a36Sopenharmony_ci
104462306a36Sopenharmony_ci		VOP_WIN_SET(vop, win, alpha_pre_mul, ALPHA_SRC_PRE_MUL);
104562306a36Sopenharmony_ci		VOP_WIN_SET(vop, win, alpha_mode, ALPHA_PER_PIX);
104662306a36Sopenharmony_ci		VOP_WIN_SET(vop, win, alpha_en, 1);
104762306a36Sopenharmony_ci	} else {
104862306a36Sopenharmony_ci		VOP_WIN_SET(vop, win, src_alpha_ctl, SRC_ALPHA_EN(0));
104962306a36Sopenharmony_ci		VOP_WIN_SET(vop, win, alpha_en, 0);
105062306a36Sopenharmony_ci	}
105162306a36Sopenharmony_ci
105262306a36Sopenharmony_ci	VOP_WIN_SET(vop, win, enable, 1);
105362306a36Sopenharmony_ci	vop->win_enabled |= BIT(win_index);
105462306a36Sopenharmony_ci	spin_unlock(&vop->reg_lock);
105562306a36Sopenharmony_ci}
105662306a36Sopenharmony_ci
105762306a36Sopenharmony_cistatic int vop_plane_atomic_async_check(struct drm_plane *plane,
105862306a36Sopenharmony_ci					struct drm_atomic_state *state)
105962306a36Sopenharmony_ci{
106062306a36Sopenharmony_ci	struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state,
106162306a36Sopenharmony_ci										 plane);
106262306a36Sopenharmony_ci	struct vop_win *vop_win = to_vop_win(plane);
106362306a36Sopenharmony_ci	const struct vop_win_data *win = vop_win->data;
106462306a36Sopenharmony_ci	int min_scale = win->phy->scl ? FRAC_16_16(1, 8) :
106562306a36Sopenharmony_ci					DRM_PLANE_NO_SCALING;
106662306a36Sopenharmony_ci	int max_scale = win->phy->scl ? FRAC_16_16(8, 1) :
106762306a36Sopenharmony_ci					DRM_PLANE_NO_SCALING;
106862306a36Sopenharmony_ci	struct drm_crtc_state *crtc_state;
106962306a36Sopenharmony_ci
107062306a36Sopenharmony_ci	if (plane != new_plane_state->crtc->cursor)
107162306a36Sopenharmony_ci		return -EINVAL;
107262306a36Sopenharmony_ci
107362306a36Sopenharmony_ci	if (!plane->state)
107462306a36Sopenharmony_ci		return -EINVAL;
107562306a36Sopenharmony_ci
107662306a36Sopenharmony_ci	if (!plane->state->fb)
107762306a36Sopenharmony_ci		return -EINVAL;
107862306a36Sopenharmony_ci
107962306a36Sopenharmony_ci	if (state)
108062306a36Sopenharmony_ci		crtc_state = drm_atomic_get_existing_crtc_state(state,
108162306a36Sopenharmony_ci								new_plane_state->crtc);
108262306a36Sopenharmony_ci	else /* Special case for asynchronous cursor updates. */
108362306a36Sopenharmony_ci		crtc_state = plane->crtc->state;
108462306a36Sopenharmony_ci
108562306a36Sopenharmony_ci	return drm_atomic_helper_check_plane_state(plane->state, crtc_state,
108662306a36Sopenharmony_ci						   min_scale, max_scale,
108762306a36Sopenharmony_ci						   true, true);
108862306a36Sopenharmony_ci}
108962306a36Sopenharmony_ci
109062306a36Sopenharmony_cistatic void vop_plane_atomic_async_update(struct drm_plane *plane,
109162306a36Sopenharmony_ci					  struct drm_atomic_state *state)
109262306a36Sopenharmony_ci{
109362306a36Sopenharmony_ci	struct drm_plane_state *new_state = drm_atomic_get_new_plane_state(state,
109462306a36Sopenharmony_ci									   plane);
109562306a36Sopenharmony_ci	struct vop *vop = to_vop(plane->state->crtc);
109662306a36Sopenharmony_ci	struct drm_framebuffer *old_fb = plane->state->fb;
109762306a36Sopenharmony_ci
109862306a36Sopenharmony_ci	plane->state->crtc_x = new_state->crtc_x;
109962306a36Sopenharmony_ci	plane->state->crtc_y = new_state->crtc_y;
110062306a36Sopenharmony_ci	plane->state->crtc_h = new_state->crtc_h;
110162306a36Sopenharmony_ci	plane->state->crtc_w = new_state->crtc_w;
110262306a36Sopenharmony_ci	plane->state->src_x = new_state->src_x;
110362306a36Sopenharmony_ci	plane->state->src_y = new_state->src_y;
110462306a36Sopenharmony_ci	plane->state->src_h = new_state->src_h;
110562306a36Sopenharmony_ci	plane->state->src_w = new_state->src_w;
110662306a36Sopenharmony_ci	swap(plane->state->fb, new_state->fb);
110762306a36Sopenharmony_ci
110862306a36Sopenharmony_ci	if (vop->is_enabled) {
110962306a36Sopenharmony_ci		vop_plane_atomic_update(plane, state);
111062306a36Sopenharmony_ci		spin_lock(&vop->reg_lock);
111162306a36Sopenharmony_ci		vop_cfg_done(vop);
111262306a36Sopenharmony_ci		spin_unlock(&vop->reg_lock);
111362306a36Sopenharmony_ci
111462306a36Sopenharmony_ci		/*
111562306a36Sopenharmony_ci		 * A scanout can still be occurring, so we can't drop the
111662306a36Sopenharmony_ci		 * reference to the old framebuffer. To solve this we get a
111762306a36Sopenharmony_ci		 * reference to old_fb and set a worker to release it later.
111862306a36Sopenharmony_ci		 * FIXME: if we perform 500 async_update calls before the
111962306a36Sopenharmony_ci		 * vblank, then we can have 500 different framebuffers waiting
112062306a36Sopenharmony_ci		 * to be released.
112162306a36Sopenharmony_ci		 */
112262306a36Sopenharmony_ci		if (old_fb && plane->state->fb != old_fb) {
112362306a36Sopenharmony_ci			drm_framebuffer_get(old_fb);
112462306a36Sopenharmony_ci			WARN_ON(drm_crtc_vblank_get(plane->state->crtc) != 0);
112562306a36Sopenharmony_ci			drm_flip_work_queue(&vop->fb_unref_work, old_fb);
112662306a36Sopenharmony_ci			set_bit(VOP_PENDING_FB_UNREF, &vop->pending);
112762306a36Sopenharmony_ci		}
112862306a36Sopenharmony_ci	}
112962306a36Sopenharmony_ci}
113062306a36Sopenharmony_ci
113162306a36Sopenharmony_cistatic const struct drm_plane_helper_funcs plane_helper_funcs = {
113262306a36Sopenharmony_ci	.atomic_check = vop_plane_atomic_check,
113362306a36Sopenharmony_ci	.atomic_update = vop_plane_atomic_update,
113462306a36Sopenharmony_ci	.atomic_disable = vop_plane_atomic_disable,
113562306a36Sopenharmony_ci	.atomic_async_check = vop_plane_atomic_async_check,
113662306a36Sopenharmony_ci	.atomic_async_update = vop_plane_atomic_async_update,
113762306a36Sopenharmony_ci};
113862306a36Sopenharmony_ci
113962306a36Sopenharmony_cistatic const struct drm_plane_funcs vop_plane_funcs = {
114062306a36Sopenharmony_ci	.update_plane	= drm_atomic_helper_update_plane,
114162306a36Sopenharmony_ci	.disable_plane	= drm_atomic_helper_disable_plane,
114262306a36Sopenharmony_ci	.destroy = vop_plane_destroy,
114362306a36Sopenharmony_ci	.reset = drm_atomic_helper_plane_reset,
114462306a36Sopenharmony_ci	.atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state,
114562306a36Sopenharmony_ci	.atomic_destroy_state = drm_atomic_helper_plane_destroy_state,
114662306a36Sopenharmony_ci	.format_mod_supported = rockchip_mod_supported,
114762306a36Sopenharmony_ci};
114862306a36Sopenharmony_ci
114962306a36Sopenharmony_cistatic int vop_crtc_enable_vblank(struct drm_crtc *crtc)
115062306a36Sopenharmony_ci{
115162306a36Sopenharmony_ci	struct vop *vop = to_vop(crtc);
115262306a36Sopenharmony_ci	unsigned long flags;
115362306a36Sopenharmony_ci
115462306a36Sopenharmony_ci	if (WARN_ON(!vop->is_enabled))
115562306a36Sopenharmony_ci		return -EPERM;
115662306a36Sopenharmony_ci
115762306a36Sopenharmony_ci	spin_lock_irqsave(&vop->irq_lock, flags);
115862306a36Sopenharmony_ci
115962306a36Sopenharmony_ci	VOP_INTR_SET_TYPE(vop, clear, FS_INTR, 1);
116062306a36Sopenharmony_ci	VOP_INTR_SET_TYPE(vop, enable, FS_INTR, 1);
116162306a36Sopenharmony_ci
116262306a36Sopenharmony_ci	spin_unlock_irqrestore(&vop->irq_lock, flags);
116362306a36Sopenharmony_ci
116462306a36Sopenharmony_ci	return 0;
116562306a36Sopenharmony_ci}
116662306a36Sopenharmony_ci
116762306a36Sopenharmony_cistatic void vop_crtc_disable_vblank(struct drm_crtc *crtc)
116862306a36Sopenharmony_ci{
116962306a36Sopenharmony_ci	struct vop *vop = to_vop(crtc);
117062306a36Sopenharmony_ci	unsigned long flags;
117162306a36Sopenharmony_ci
117262306a36Sopenharmony_ci	if (WARN_ON(!vop->is_enabled))
117362306a36Sopenharmony_ci		return;
117462306a36Sopenharmony_ci
117562306a36Sopenharmony_ci	spin_lock_irqsave(&vop->irq_lock, flags);
117662306a36Sopenharmony_ci
117762306a36Sopenharmony_ci	VOP_INTR_SET_TYPE(vop, enable, FS_INTR, 0);
117862306a36Sopenharmony_ci
117962306a36Sopenharmony_ci	spin_unlock_irqrestore(&vop->irq_lock, flags);
118062306a36Sopenharmony_ci}
118162306a36Sopenharmony_ci
118262306a36Sopenharmony_cistatic enum drm_mode_status vop_crtc_mode_valid(struct drm_crtc *crtc,
118362306a36Sopenharmony_ci						const struct drm_display_mode *mode)
118462306a36Sopenharmony_ci{
118562306a36Sopenharmony_ci	struct vop *vop = to_vop(crtc);
118662306a36Sopenharmony_ci
118762306a36Sopenharmony_ci	if (vop->data->max_output.width && mode->hdisplay > vop->data->max_output.width)
118862306a36Sopenharmony_ci		return MODE_BAD_HVALUE;
118962306a36Sopenharmony_ci
119062306a36Sopenharmony_ci	return MODE_OK;
119162306a36Sopenharmony_ci}
119262306a36Sopenharmony_ci
119362306a36Sopenharmony_cistatic bool vop_crtc_mode_fixup(struct drm_crtc *crtc,
119462306a36Sopenharmony_ci				const struct drm_display_mode *mode,
119562306a36Sopenharmony_ci				struct drm_display_mode *adjusted_mode)
119662306a36Sopenharmony_ci{
119762306a36Sopenharmony_ci	struct vop *vop = to_vop(crtc);
119862306a36Sopenharmony_ci	unsigned long rate;
119962306a36Sopenharmony_ci
120062306a36Sopenharmony_ci	/*
120162306a36Sopenharmony_ci	 * Clock craziness.
120262306a36Sopenharmony_ci	 *
120362306a36Sopenharmony_ci	 * Key points:
120462306a36Sopenharmony_ci	 *
120562306a36Sopenharmony_ci	 * - DRM works in kHz.
120662306a36Sopenharmony_ci	 * - Clock framework works in Hz.
120762306a36Sopenharmony_ci	 * - Rockchip's clock driver picks the clock rate that is the
120862306a36Sopenharmony_ci	 *   same _OR LOWER_ than the one requested.
120962306a36Sopenharmony_ci	 *
121062306a36Sopenharmony_ci	 * Action plan:
121162306a36Sopenharmony_ci	 *
121262306a36Sopenharmony_ci	 * 1. Try to set the exact rate first, and confirm the clock framework
121362306a36Sopenharmony_ci	 *    can provide it.
121462306a36Sopenharmony_ci	 *
121562306a36Sopenharmony_ci	 * 2. If the clock framework cannot provide the exact rate, we should
121662306a36Sopenharmony_ci	 *    add 999 Hz to the requested rate.  That way if the clock we need
121762306a36Sopenharmony_ci	 *    is 60000001 Hz (~60 MHz) and DRM tells us to make 60000 kHz then
121862306a36Sopenharmony_ci	 *    the clock framework will actually give us the right clock.
121962306a36Sopenharmony_ci	 *
122062306a36Sopenharmony_ci	 * 3. Get the clock framework to round the rate for us to tell us
122162306a36Sopenharmony_ci	 *    what it will actually make.
122262306a36Sopenharmony_ci	 *
122362306a36Sopenharmony_ci	 * 4. Store the rounded up rate so that we don't need to worry about
122462306a36Sopenharmony_ci	 *    this in the actual clk_set_rate().
122562306a36Sopenharmony_ci	 */
122662306a36Sopenharmony_ci	rate = clk_round_rate(vop->dclk, adjusted_mode->clock * 1000);
122762306a36Sopenharmony_ci	if (rate / 1000 != adjusted_mode->clock)
122862306a36Sopenharmony_ci		rate = clk_round_rate(vop->dclk,
122962306a36Sopenharmony_ci				      adjusted_mode->clock * 1000 + 999);
123062306a36Sopenharmony_ci	adjusted_mode->clock = DIV_ROUND_UP(rate, 1000);
123162306a36Sopenharmony_ci
123262306a36Sopenharmony_ci	return true;
123362306a36Sopenharmony_ci}
123462306a36Sopenharmony_ci
123562306a36Sopenharmony_cistatic bool vop_dsp_lut_is_enabled(struct vop *vop)
123662306a36Sopenharmony_ci{
123762306a36Sopenharmony_ci	return vop_read_reg(vop, 0, &vop->data->common->dsp_lut_en);
123862306a36Sopenharmony_ci}
123962306a36Sopenharmony_ci
124062306a36Sopenharmony_cistatic u32 vop_lut_buffer_index(struct vop *vop)
124162306a36Sopenharmony_ci{
124262306a36Sopenharmony_ci	return vop_read_reg(vop, 0, &vop->data->common->lut_buffer_index);
124362306a36Sopenharmony_ci}
124462306a36Sopenharmony_ci
124562306a36Sopenharmony_cistatic void vop_crtc_write_gamma_lut(struct vop *vop, struct drm_crtc *crtc)
124662306a36Sopenharmony_ci{
124762306a36Sopenharmony_ci	struct drm_color_lut *lut = crtc->state->gamma_lut->data;
124862306a36Sopenharmony_ci	unsigned int i, bpc = ilog2(vop->data->lut_size);
124962306a36Sopenharmony_ci
125062306a36Sopenharmony_ci	for (i = 0; i < crtc->gamma_size; i++) {
125162306a36Sopenharmony_ci		u32 word;
125262306a36Sopenharmony_ci
125362306a36Sopenharmony_ci		word = (drm_color_lut_extract(lut[i].red, bpc) << (2 * bpc)) |
125462306a36Sopenharmony_ci		       (drm_color_lut_extract(lut[i].green, bpc) << bpc) |
125562306a36Sopenharmony_ci			drm_color_lut_extract(lut[i].blue, bpc);
125662306a36Sopenharmony_ci		writel(word, vop->lut_regs + i * 4);
125762306a36Sopenharmony_ci	}
125862306a36Sopenharmony_ci}
125962306a36Sopenharmony_ci
126062306a36Sopenharmony_cistatic void vop_crtc_gamma_set(struct vop *vop, struct drm_crtc *crtc,
126162306a36Sopenharmony_ci			       struct drm_crtc_state *old_state)
126262306a36Sopenharmony_ci{
126362306a36Sopenharmony_ci	struct drm_crtc_state *state = crtc->state;
126462306a36Sopenharmony_ci	unsigned int idle;
126562306a36Sopenharmony_ci	u32 lut_idx, old_idx;
126662306a36Sopenharmony_ci	int ret;
126762306a36Sopenharmony_ci
126862306a36Sopenharmony_ci	if (!vop->lut_regs)
126962306a36Sopenharmony_ci		return;
127062306a36Sopenharmony_ci
127162306a36Sopenharmony_ci	if (!state->gamma_lut || !VOP_HAS_REG(vop, common, update_gamma_lut)) {
127262306a36Sopenharmony_ci		/*
127362306a36Sopenharmony_ci		 * To disable gamma (gamma_lut is null) or to write
127462306a36Sopenharmony_ci		 * an update to the LUT, clear dsp_lut_en.
127562306a36Sopenharmony_ci		 */
127662306a36Sopenharmony_ci		spin_lock(&vop->reg_lock);
127762306a36Sopenharmony_ci		VOP_REG_SET(vop, common, dsp_lut_en, 0);
127862306a36Sopenharmony_ci		vop_cfg_done(vop);
127962306a36Sopenharmony_ci		spin_unlock(&vop->reg_lock);
128062306a36Sopenharmony_ci
128162306a36Sopenharmony_ci		/*
128262306a36Sopenharmony_ci		 * In order to write the LUT to the internal memory,
128362306a36Sopenharmony_ci		 * we need to first make sure the dsp_lut_en bit is cleared.
128462306a36Sopenharmony_ci		 */
128562306a36Sopenharmony_ci		ret = readx_poll_timeout(vop_dsp_lut_is_enabled, vop,
128662306a36Sopenharmony_ci					 idle, !idle, 5, 30 * 1000);
128762306a36Sopenharmony_ci		if (ret) {
128862306a36Sopenharmony_ci			DRM_DEV_ERROR(vop->dev, "display LUT RAM enable timeout!\n");
128962306a36Sopenharmony_ci			return;
129062306a36Sopenharmony_ci		}
129162306a36Sopenharmony_ci
129262306a36Sopenharmony_ci		if (!state->gamma_lut)
129362306a36Sopenharmony_ci			return;
129462306a36Sopenharmony_ci	} else {
129562306a36Sopenharmony_ci		/*
129662306a36Sopenharmony_ci		 * On RK3399 the gamma LUT can updated without clearing dsp_lut_en,
129762306a36Sopenharmony_ci		 * by setting update_gamma_lut then waiting for lut_buffer_index change
129862306a36Sopenharmony_ci		 */
129962306a36Sopenharmony_ci		old_idx = vop_lut_buffer_index(vop);
130062306a36Sopenharmony_ci	}
130162306a36Sopenharmony_ci
130262306a36Sopenharmony_ci	spin_lock(&vop->reg_lock);
130362306a36Sopenharmony_ci	vop_crtc_write_gamma_lut(vop, crtc);
130462306a36Sopenharmony_ci	VOP_REG_SET(vop, common, dsp_lut_en, 1);
130562306a36Sopenharmony_ci	VOP_REG_SET(vop, common, update_gamma_lut, 1);
130662306a36Sopenharmony_ci	vop_cfg_done(vop);
130762306a36Sopenharmony_ci	spin_unlock(&vop->reg_lock);
130862306a36Sopenharmony_ci
130962306a36Sopenharmony_ci	if (VOP_HAS_REG(vop, common, update_gamma_lut)) {
131062306a36Sopenharmony_ci		ret = readx_poll_timeout(vop_lut_buffer_index, vop,
131162306a36Sopenharmony_ci					 lut_idx, lut_idx != old_idx, 5, 30 * 1000);
131262306a36Sopenharmony_ci		if (ret) {
131362306a36Sopenharmony_ci			DRM_DEV_ERROR(vop->dev, "gamma LUT update timeout!\n");
131462306a36Sopenharmony_ci			return;
131562306a36Sopenharmony_ci		}
131662306a36Sopenharmony_ci
131762306a36Sopenharmony_ci		/*
131862306a36Sopenharmony_ci		 * update_gamma_lut is auto cleared by HW, but write 0 to clear the bit
131962306a36Sopenharmony_ci		 * in our backup of the regs.
132062306a36Sopenharmony_ci		 */
132162306a36Sopenharmony_ci		spin_lock(&vop->reg_lock);
132262306a36Sopenharmony_ci		VOP_REG_SET(vop, common, update_gamma_lut, 0);
132362306a36Sopenharmony_ci		spin_unlock(&vop->reg_lock);
132462306a36Sopenharmony_ci	}
132562306a36Sopenharmony_ci}
132662306a36Sopenharmony_ci
132762306a36Sopenharmony_cistatic void vop_crtc_atomic_begin(struct drm_crtc *crtc,
132862306a36Sopenharmony_ci				  struct drm_atomic_state *state)
132962306a36Sopenharmony_ci{
133062306a36Sopenharmony_ci	struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state,
133162306a36Sopenharmony_ci									  crtc);
133262306a36Sopenharmony_ci	struct drm_crtc_state *old_crtc_state = drm_atomic_get_old_crtc_state(state,
133362306a36Sopenharmony_ci									      crtc);
133462306a36Sopenharmony_ci	struct vop *vop = to_vop(crtc);
133562306a36Sopenharmony_ci
133662306a36Sopenharmony_ci	/*
133762306a36Sopenharmony_ci	 * Only update GAMMA if the 'active' flag is not changed,
133862306a36Sopenharmony_ci	 * otherwise it's updated by .atomic_enable.
133962306a36Sopenharmony_ci	 */
134062306a36Sopenharmony_ci	if (crtc_state->color_mgmt_changed &&
134162306a36Sopenharmony_ci	    !crtc_state->active_changed)
134262306a36Sopenharmony_ci		vop_crtc_gamma_set(vop, crtc, old_crtc_state);
134362306a36Sopenharmony_ci}
134462306a36Sopenharmony_ci
134562306a36Sopenharmony_cistatic void vop_crtc_atomic_enable(struct drm_crtc *crtc,
134662306a36Sopenharmony_ci				   struct drm_atomic_state *state)
134762306a36Sopenharmony_ci{
134862306a36Sopenharmony_ci	struct drm_crtc_state *old_state = drm_atomic_get_old_crtc_state(state,
134962306a36Sopenharmony_ci									 crtc);
135062306a36Sopenharmony_ci	struct vop *vop = to_vop(crtc);
135162306a36Sopenharmony_ci	const struct vop_data *vop_data = vop->data;
135262306a36Sopenharmony_ci	struct rockchip_crtc_state *s = to_rockchip_crtc_state(crtc->state);
135362306a36Sopenharmony_ci	struct drm_display_mode *adjusted_mode = &crtc->state->adjusted_mode;
135462306a36Sopenharmony_ci	u16 hsync_len = adjusted_mode->hsync_end - adjusted_mode->hsync_start;
135562306a36Sopenharmony_ci	u16 hdisplay = adjusted_mode->hdisplay;
135662306a36Sopenharmony_ci	u16 htotal = adjusted_mode->htotal;
135762306a36Sopenharmony_ci	u16 hact_st = adjusted_mode->htotal - adjusted_mode->hsync_start;
135862306a36Sopenharmony_ci	u16 hact_end = hact_st + hdisplay;
135962306a36Sopenharmony_ci	u16 vdisplay = adjusted_mode->vdisplay;
136062306a36Sopenharmony_ci	u16 vtotal = adjusted_mode->vtotal;
136162306a36Sopenharmony_ci	u16 vsync_len = adjusted_mode->vsync_end - adjusted_mode->vsync_start;
136262306a36Sopenharmony_ci	u16 vact_st = adjusted_mode->vtotal - adjusted_mode->vsync_start;
136362306a36Sopenharmony_ci	u16 vact_end = vact_st + vdisplay;
136462306a36Sopenharmony_ci	uint32_t pin_pol, val;
136562306a36Sopenharmony_ci	int dither_bpc = s->output_bpc ? s->output_bpc : 10;
136662306a36Sopenharmony_ci	int ret;
136762306a36Sopenharmony_ci
136862306a36Sopenharmony_ci	if (old_state && old_state->self_refresh_active) {
136962306a36Sopenharmony_ci		drm_crtc_vblank_on(crtc);
137062306a36Sopenharmony_ci		rockchip_drm_set_win_enabled(crtc, true);
137162306a36Sopenharmony_ci		return;
137262306a36Sopenharmony_ci	}
137362306a36Sopenharmony_ci
137462306a36Sopenharmony_ci	mutex_lock(&vop->vop_lock);
137562306a36Sopenharmony_ci
137662306a36Sopenharmony_ci	WARN_ON(vop->event);
137762306a36Sopenharmony_ci
137862306a36Sopenharmony_ci	ret = vop_enable(crtc, old_state);
137962306a36Sopenharmony_ci	if (ret) {
138062306a36Sopenharmony_ci		mutex_unlock(&vop->vop_lock);
138162306a36Sopenharmony_ci		DRM_DEV_ERROR(vop->dev, "Failed to enable vop (%d)\n", ret);
138262306a36Sopenharmony_ci		return;
138362306a36Sopenharmony_ci	}
138462306a36Sopenharmony_ci	pin_pol = (adjusted_mode->flags & DRM_MODE_FLAG_PHSYNC) ?
138562306a36Sopenharmony_ci		   BIT(HSYNC_POSITIVE) : 0;
138662306a36Sopenharmony_ci	pin_pol |= (adjusted_mode->flags & DRM_MODE_FLAG_PVSYNC) ?
138762306a36Sopenharmony_ci		   BIT(VSYNC_POSITIVE) : 0;
138862306a36Sopenharmony_ci	VOP_REG_SET(vop, output, pin_pol, pin_pol);
138962306a36Sopenharmony_ci	VOP_REG_SET(vop, output, mipi_dual_channel_en, 0);
139062306a36Sopenharmony_ci
139162306a36Sopenharmony_ci	switch (s->output_type) {
139262306a36Sopenharmony_ci	case DRM_MODE_CONNECTOR_LVDS:
139362306a36Sopenharmony_ci		VOP_REG_SET(vop, output, rgb_dclk_pol, 1);
139462306a36Sopenharmony_ci		VOP_REG_SET(vop, output, rgb_pin_pol, pin_pol);
139562306a36Sopenharmony_ci		VOP_REG_SET(vop, output, rgb_en, 1);
139662306a36Sopenharmony_ci		break;
139762306a36Sopenharmony_ci	case DRM_MODE_CONNECTOR_eDP:
139862306a36Sopenharmony_ci		VOP_REG_SET(vop, output, edp_dclk_pol, 1);
139962306a36Sopenharmony_ci		VOP_REG_SET(vop, output, edp_pin_pol, pin_pol);
140062306a36Sopenharmony_ci		VOP_REG_SET(vop, output, edp_en, 1);
140162306a36Sopenharmony_ci		break;
140262306a36Sopenharmony_ci	case DRM_MODE_CONNECTOR_HDMIA:
140362306a36Sopenharmony_ci		VOP_REG_SET(vop, output, hdmi_dclk_pol, 1);
140462306a36Sopenharmony_ci		VOP_REG_SET(vop, output, hdmi_pin_pol, pin_pol);
140562306a36Sopenharmony_ci		VOP_REG_SET(vop, output, hdmi_en, 1);
140662306a36Sopenharmony_ci		break;
140762306a36Sopenharmony_ci	case DRM_MODE_CONNECTOR_DSI:
140862306a36Sopenharmony_ci		VOP_REG_SET(vop, output, mipi_dclk_pol, 1);
140962306a36Sopenharmony_ci		VOP_REG_SET(vop, output, mipi_pin_pol, pin_pol);
141062306a36Sopenharmony_ci		VOP_REG_SET(vop, output, mipi_en, 1);
141162306a36Sopenharmony_ci		VOP_REG_SET(vop, output, mipi_dual_channel_en,
141262306a36Sopenharmony_ci			    !!(s->output_flags & ROCKCHIP_OUTPUT_DSI_DUAL));
141362306a36Sopenharmony_ci		break;
141462306a36Sopenharmony_ci	case DRM_MODE_CONNECTOR_DisplayPort:
141562306a36Sopenharmony_ci		VOP_REG_SET(vop, output, dp_dclk_pol, 0);
141662306a36Sopenharmony_ci		VOP_REG_SET(vop, output, dp_pin_pol, pin_pol);
141762306a36Sopenharmony_ci		VOP_REG_SET(vop, output, dp_en, 1);
141862306a36Sopenharmony_ci		break;
141962306a36Sopenharmony_ci	default:
142062306a36Sopenharmony_ci		DRM_DEV_ERROR(vop->dev, "unsupported connector_type [%d]\n",
142162306a36Sopenharmony_ci			      s->output_type);
142262306a36Sopenharmony_ci	}
142362306a36Sopenharmony_ci
142462306a36Sopenharmony_ci	/*
142562306a36Sopenharmony_ci	 * if vop is not support RGB10 output, need force RGB10 to RGB888.
142662306a36Sopenharmony_ci	 */
142762306a36Sopenharmony_ci	if (s->output_mode == ROCKCHIP_OUT_MODE_AAAA &&
142862306a36Sopenharmony_ci	    !(vop_data->feature & VOP_FEATURE_OUTPUT_RGB10))
142962306a36Sopenharmony_ci		s->output_mode = ROCKCHIP_OUT_MODE_P888;
143062306a36Sopenharmony_ci
143162306a36Sopenharmony_ci	if (s->output_mode == ROCKCHIP_OUT_MODE_AAAA && dither_bpc <= 8)
143262306a36Sopenharmony_ci		VOP_REG_SET(vop, common, pre_dither_down, 1);
143362306a36Sopenharmony_ci	else
143462306a36Sopenharmony_ci		VOP_REG_SET(vop, common, pre_dither_down, 0);
143562306a36Sopenharmony_ci
143662306a36Sopenharmony_ci	if (dither_bpc == 6) {
143762306a36Sopenharmony_ci		VOP_REG_SET(vop, common, dither_down_sel, DITHER_DOWN_ALLEGRO);
143862306a36Sopenharmony_ci		VOP_REG_SET(vop, common, dither_down_mode, RGB888_TO_RGB666);
143962306a36Sopenharmony_ci		VOP_REG_SET(vop, common, dither_down_en, 1);
144062306a36Sopenharmony_ci	} else {
144162306a36Sopenharmony_ci		VOP_REG_SET(vop, common, dither_down_en, 0);
144262306a36Sopenharmony_ci	}
144362306a36Sopenharmony_ci
144462306a36Sopenharmony_ci	VOP_REG_SET(vop, common, out_mode, s->output_mode);
144562306a36Sopenharmony_ci
144662306a36Sopenharmony_ci	VOP_REG_SET(vop, modeset, htotal_pw, (htotal << 16) | hsync_len);
144762306a36Sopenharmony_ci	val = hact_st << 16;
144862306a36Sopenharmony_ci	val |= hact_end;
144962306a36Sopenharmony_ci	VOP_REG_SET(vop, modeset, hact_st_end, val);
145062306a36Sopenharmony_ci	VOP_REG_SET(vop, modeset, hpost_st_end, val);
145162306a36Sopenharmony_ci
145262306a36Sopenharmony_ci	VOP_REG_SET(vop, modeset, vtotal_pw, (vtotal << 16) | vsync_len);
145362306a36Sopenharmony_ci	val = vact_st << 16;
145462306a36Sopenharmony_ci	val |= vact_end;
145562306a36Sopenharmony_ci	VOP_REG_SET(vop, modeset, vact_st_end, val);
145662306a36Sopenharmony_ci	VOP_REG_SET(vop, modeset, vpost_st_end, val);
145762306a36Sopenharmony_ci
145862306a36Sopenharmony_ci	VOP_REG_SET(vop, intr, line_flag_num[0], vact_end);
145962306a36Sopenharmony_ci
146062306a36Sopenharmony_ci	clk_set_rate(vop->dclk, adjusted_mode->clock * 1000);
146162306a36Sopenharmony_ci
146262306a36Sopenharmony_ci	VOP_REG_SET(vop, common, standby, 0);
146362306a36Sopenharmony_ci	mutex_unlock(&vop->vop_lock);
146462306a36Sopenharmony_ci
146562306a36Sopenharmony_ci	/*
146662306a36Sopenharmony_ci	 * If we have a GAMMA LUT in the state, then let's make sure
146762306a36Sopenharmony_ci	 * it's updated. We might be coming out of suspend,
146862306a36Sopenharmony_ci	 * which means the LUT internal memory needs to be re-written.
146962306a36Sopenharmony_ci	 */
147062306a36Sopenharmony_ci	if (crtc->state->gamma_lut)
147162306a36Sopenharmony_ci		vop_crtc_gamma_set(vop, crtc, old_state);
147262306a36Sopenharmony_ci}
147362306a36Sopenharmony_ci
147462306a36Sopenharmony_cistatic bool vop_fs_irq_is_pending(struct vop *vop)
147562306a36Sopenharmony_ci{
147662306a36Sopenharmony_ci	return VOP_INTR_GET_TYPE(vop, status, FS_INTR);
147762306a36Sopenharmony_ci}
147862306a36Sopenharmony_ci
147962306a36Sopenharmony_cistatic void vop_wait_for_irq_handler(struct vop *vop)
148062306a36Sopenharmony_ci{
148162306a36Sopenharmony_ci	bool pending;
148262306a36Sopenharmony_ci	int ret;
148362306a36Sopenharmony_ci
148462306a36Sopenharmony_ci	/*
148562306a36Sopenharmony_ci	 * Spin until frame start interrupt status bit goes low, which means
148662306a36Sopenharmony_ci	 * that interrupt handler was invoked and cleared it. The timeout of
148762306a36Sopenharmony_ci	 * 10 msecs is really too long, but it is just a safety measure if
148862306a36Sopenharmony_ci	 * something goes really wrong. The wait will only happen in the very
148962306a36Sopenharmony_ci	 * unlikely case of a vblank happening exactly at the same time and
149062306a36Sopenharmony_ci	 * shouldn't exceed microseconds range.
149162306a36Sopenharmony_ci	 */
149262306a36Sopenharmony_ci	ret = readx_poll_timeout_atomic(vop_fs_irq_is_pending, vop, pending,
149362306a36Sopenharmony_ci					!pending, 0, 10 * 1000);
149462306a36Sopenharmony_ci	if (ret)
149562306a36Sopenharmony_ci		DRM_DEV_ERROR(vop->dev, "VOP vblank IRQ stuck for 10 ms\n");
149662306a36Sopenharmony_ci
149762306a36Sopenharmony_ci	synchronize_irq(vop->irq);
149862306a36Sopenharmony_ci}
149962306a36Sopenharmony_ci
150062306a36Sopenharmony_cistatic int vop_crtc_atomic_check(struct drm_crtc *crtc,
150162306a36Sopenharmony_ci				 struct drm_atomic_state *state)
150262306a36Sopenharmony_ci{
150362306a36Sopenharmony_ci	struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state,
150462306a36Sopenharmony_ci									  crtc);
150562306a36Sopenharmony_ci	struct vop *vop = to_vop(crtc);
150662306a36Sopenharmony_ci	struct drm_plane *plane;
150762306a36Sopenharmony_ci	struct drm_plane_state *plane_state;
150862306a36Sopenharmony_ci	struct rockchip_crtc_state *s;
150962306a36Sopenharmony_ci	int afbc_planes = 0;
151062306a36Sopenharmony_ci
151162306a36Sopenharmony_ci	if (vop->lut_regs && crtc_state->color_mgmt_changed &&
151262306a36Sopenharmony_ci	    crtc_state->gamma_lut) {
151362306a36Sopenharmony_ci		unsigned int len;
151462306a36Sopenharmony_ci
151562306a36Sopenharmony_ci		len = drm_color_lut_size(crtc_state->gamma_lut);
151662306a36Sopenharmony_ci		if (len != crtc->gamma_size) {
151762306a36Sopenharmony_ci			DRM_DEBUG_KMS("Invalid LUT size; got %d, expected %d\n",
151862306a36Sopenharmony_ci				      len, crtc->gamma_size);
151962306a36Sopenharmony_ci			return -EINVAL;
152062306a36Sopenharmony_ci		}
152162306a36Sopenharmony_ci	}
152262306a36Sopenharmony_ci
152362306a36Sopenharmony_ci	drm_atomic_crtc_state_for_each_plane(plane, crtc_state) {
152462306a36Sopenharmony_ci		plane_state =
152562306a36Sopenharmony_ci			drm_atomic_get_plane_state(crtc_state->state, plane);
152662306a36Sopenharmony_ci		if (IS_ERR(plane_state)) {
152762306a36Sopenharmony_ci			DRM_DEBUG_KMS("Cannot get plane state for plane %s\n",
152862306a36Sopenharmony_ci				      plane->name);
152962306a36Sopenharmony_ci			return PTR_ERR(plane_state);
153062306a36Sopenharmony_ci		}
153162306a36Sopenharmony_ci
153262306a36Sopenharmony_ci		if (drm_is_afbc(plane_state->fb->modifier))
153362306a36Sopenharmony_ci			++afbc_planes;
153462306a36Sopenharmony_ci	}
153562306a36Sopenharmony_ci
153662306a36Sopenharmony_ci	if (afbc_planes > 1) {
153762306a36Sopenharmony_ci		DRM_DEBUG_KMS("Invalid number of AFBC planes; got %d, expected at most 1\n", afbc_planes);
153862306a36Sopenharmony_ci		return -EINVAL;
153962306a36Sopenharmony_ci	}
154062306a36Sopenharmony_ci
154162306a36Sopenharmony_ci	s = to_rockchip_crtc_state(crtc_state);
154262306a36Sopenharmony_ci	s->enable_afbc = afbc_planes > 0;
154362306a36Sopenharmony_ci
154462306a36Sopenharmony_ci	return 0;
154562306a36Sopenharmony_ci}
154662306a36Sopenharmony_ci
154762306a36Sopenharmony_cistatic void vop_crtc_atomic_flush(struct drm_crtc *crtc,
154862306a36Sopenharmony_ci				  struct drm_atomic_state *state)
154962306a36Sopenharmony_ci{
155062306a36Sopenharmony_ci	struct drm_crtc_state *old_crtc_state = drm_atomic_get_old_crtc_state(state,
155162306a36Sopenharmony_ci									      crtc);
155262306a36Sopenharmony_ci	struct drm_atomic_state *old_state = old_crtc_state->state;
155362306a36Sopenharmony_ci	struct drm_plane_state *old_plane_state, *new_plane_state;
155462306a36Sopenharmony_ci	struct vop *vop = to_vop(crtc);
155562306a36Sopenharmony_ci	struct drm_plane *plane;
155662306a36Sopenharmony_ci	struct rockchip_crtc_state *s;
155762306a36Sopenharmony_ci	int i;
155862306a36Sopenharmony_ci
155962306a36Sopenharmony_ci	if (WARN_ON(!vop->is_enabled))
156062306a36Sopenharmony_ci		return;
156162306a36Sopenharmony_ci
156262306a36Sopenharmony_ci	spin_lock(&vop->reg_lock);
156362306a36Sopenharmony_ci
156462306a36Sopenharmony_ci	/* Enable AFBC if there is some AFBC window, disable otherwise. */
156562306a36Sopenharmony_ci	s = to_rockchip_crtc_state(crtc->state);
156662306a36Sopenharmony_ci	VOP_AFBC_SET(vop, enable, s->enable_afbc);
156762306a36Sopenharmony_ci	vop_cfg_done(vop);
156862306a36Sopenharmony_ci
156962306a36Sopenharmony_ci	spin_unlock(&vop->reg_lock);
157062306a36Sopenharmony_ci
157162306a36Sopenharmony_ci	/*
157262306a36Sopenharmony_ci	 * There is a (rather unlikely) possiblity that a vblank interrupt
157362306a36Sopenharmony_ci	 * fired before we set the cfg_done bit. To avoid spuriously
157462306a36Sopenharmony_ci	 * signalling flip completion we need to wait for it to finish.
157562306a36Sopenharmony_ci	 */
157662306a36Sopenharmony_ci	vop_wait_for_irq_handler(vop);
157762306a36Sopenharmony_ci
157862306a36Sopenharmony_ci	spin_lock_irq(&crtc->dev->event_lock);
157962306a36Sopenharmony_ci	if (crtc->state->event) {
158062306a36Sopenharmony_ci		WARN_ON(drm_crtc_vblank_get(crtc) != 0);
158162306a36Sopenharmony_ci		WARN_ON(vop->event);
158262306a36Sopenharmony_ci
158362306a36Sopenharmony_ci		vop->event = crtc->state->event;
158462306a36Sopenharmony_ci		crtc->state->event = NULL;
158562306a36Sopenharmony_ci	}
158662306a36Sopenharmony_ci	spin_unlock_irq(&crtc->dev->event_lock);
158762306a36Sopenharmony_ci
158862306a36Sopenharmony_ci	for_each_oldnew_plane_in_state(old_state, plane, old_plane_state,
158962306a36Sopenharmony_ci				       new_plane_state, i) {
159062306a36Sopenharmony_ci		if (!old_plane_state->fb)
159162306a36Sopenharmony_ci			continue;
159262306a36Sopenharmony_ci
159362306a36Sopenharmony_ci		if (old_plane_state->fb == new_plane_state->fb)
159462306a36Sopenharmony_ci			continue;
159562306a36Sopenharmony_ci
159662306a36Sopenharmony_ci		drm_framebuffer_get(old_plane_state->fb);
159762306a36Sopenharmony_ci		WARN_ON(drm_crtc_vblank_get(crtc) != 0);
159862306a36Sopenharmony_ci		drm_flip_work_queue(&vop->fb_unref_work, old_plane_state->fb);
159962306a36Sopenharmony_ci		set_bit(VOP_PENDING_FB_UNREF, &vop->pending);
160062306a36Sopenharmony_ci	}
160162306a36Sopenharmony_ci}
160262306a36Sopenharmony_ci
160362306a36Sopenharmony_cistatic const struct drm_crtc_helper_funcs vop_crtc_helper_funcs = {
160462306a36Sopenharmony_ci	.mode_valid = vop_crtc_mode_valid,
160562306a36Sopenharmony_ci	.mode_fixup = vop_crtc_mode_fixup,
160662306a36Sopenharmony_ci	.atomic_check = vop_crtc_atomic_check,
160762306a36Sopenharmony_ci	.atomic_begin = vop_crtc_atomic_begin,
160862306a36Sopenharmony_ci	.atomic_flush = vop_crtc_atomic_flush,
160962306a36Sopenharmony_ci	.atomic_enable = vop_crtc_atomic_enable,
161062306a36Sopenharmony_ci	.atomic_disable = vop_crtc_atomic_disable,
161162306a36Sopenharmony_ci};
161262306a36Sopenharmony_ci
161362306a36Sopenharmony_cistatic void vop_crtc_destroy(struct drm_crtc *crtc)
161462306a36Sopenharmony_ci{
161562306a36Sopenharmony_ci	drm_crtc_cleanup(crtc);
161662306a36Sopenharmony_ci}
161762306a36Sopenharmony_ci
161862306a36Sopenharmony_cistatic struct drm_crtc_state *vop_crtc_duplicate_state(struct drm_crtc *crtc)
161962306a36Sopenharmony_ci{
162062306a36Sopenharmony_ci	struct rockchip_crtc_state *rockchip_state;
162162306a36Sopenharmony_ci
162262306a36Sopenharmony_ci	if (WARN_ON(!crtc->state))
162362306a36Sopenharmony_ci		return NULL;
162462306a36Sopenharmony_ci
162562306a36Sopenharmony_ci	rockchip_state = kmemdup(to_rockchip_crtc_state(crtc->state),
162662306a36Sopenharmony_ci				 sizeof(*rockchip_state), GFP_KERNEL);
162762306a36Sopenharmony_ci	if (!rockchip_state)
162862306a36Sopenharmony_ci		return NULL;
162962306a36Sopenharmony_ci
163062306a36Sopenharmony_ci	__drm_atomic_helper_crtc_duplicate_state(crtc, &rockchip_state->base);
163162306a36Sopenharmony_ci	return &rockchip_state->base;
163262306a36Sopenharmony_ci}
163362306a36Sopenharmony_ci
163462306a36Sopenharmony_cistatic void vop_crtc_destroy_state(struct drm_crtc *crtc,
163562306a36Sopenharmony_ci				   struct drm_crtc_state *state)
163662306a36Sopenharmony_ci{
163762306a36Sopenharmony_ci	struct rockchip_crtc_state *s = to_rockchip_crtc_state(state);
163862306a36Sopenharmony_ci
163962306a36Sopenharmony_ci	__drm_atomic_helper_crtc_destroy_state(&s->base);
164062306a36Sopenharmony_ci	kfree(s);
164162306a36Sopenharmony_ci}
164262306a36Sopenharmony_ci
164362306a36Sopenharmony_cistatic void vop_crtc_reset(struct drm_crtc *crtc)
164462306a36Sopenharmony_ci{
164562306a36Sopenharmony_ci	struct rockchip_crtc_state *crtc_state =
164662306a36Sopenharmony_ci		kzalloc(sizeof(*crtc_state), GFP_KERNEL);
164762306a36Sopenharmony_ci
164862306a36Sopenharmony_ci	if (crtc->state)
164962306a36Sopenharmony_ci		vop_crtc_destroy_state(crtc, crtc->state);
165062306a36Sopenharmony_ci
165162306a36Sopenharmony_ci	if (crtc_state)
165262306a36Sopenharmony_ci		__drm_atomic_helper_crtc_reset(crtc, &crtc_state->base);
165362306a36Sopenharmony_ci	else
165462306a36Sopenharmony_ci		__drm_atomic_helper_crtc_reset(crtc, NULL);
165562306a36Sopenharmony_ci}
165662306a36Sopenharmony_ci
165762306a36Sopenharmony_ci#ifdef CONFIG_DRM_ANALOGIX_DP
165862306a36Sopenharmony_cistatic struct drm_connector *vop_get_edp_connector(struct vop *vop)
165962306a36Sopenharmony_ci{
166062306a36Sopenharmony_ci	struct drm_connector *connector;
166162306a36Sopenharmony_ci	struct drm_connector_list_iter conn_iter;
166262306a36Sopenharmony_ci
166362306a36Sopenharmony_ci	drm_connector_list_iter_begin(vop->drm_dev, &conn_iter);
166462306a36Sopenharmony_ci	drm_for_each_connector_iter(connector, &conn_iter) {
166562306a36Sopenharmony_ci		if (connector->connector_type == DRM_MODE_CONNECTOR_eDP) {
166662306a36Sopenharmony_ci			drm_connector_list_iter_end(&conn_iter);
166762306a36Sopenharmony_ci			return connector;
166862306a36Sopenharmony_ci		}
166962306a36Sopenharmony_ci	}
167062306a36Sopenharmony_ci	drm_connector_list_iter_end(&conn_iter);
167162306a36Sopenharmony_ci
167262306a36Sopenharmony_ci	return NULL;
167362306a36Sopenharmony_ci}
167462306a36Sopenharmony_ci
167562306a36Sopenharmony_cistatic int vop_crtc_set_crc_source(struct drm_crtc *crtc,
167662306a36Sopenharmony_ci				   const char *source_name)
167762306a36Sopenharmony_ci{
167862306a36Sopenharmony_ci	struct vop *vop = to_vop(crtc);
167962306a36Sopenharmony_ci	struct drm_connector *connector;
168062306a36Sopenharmony_ci	int ret;
168162306a36Sopenharmony_ci
168262306a36Sopenharmony_ci	connector = vop_get_edp_connector(vop);
168362306a36Sopenharmony_ci	if (!connector)
168462306a36Sopenharmony_ci		return -EINVAL;
168562306a36Sopenharmony_ci
168662306a36Sopenharmony_ci	if (source_name && strcmp(source_name, "auto") == 0)
168762306a36Sopenharmony_ci		ret = analogix_dp_start_crc(connector);
168862306a36Sopenharmony_ci	else if (!source_name)
168962306a36Sopenharmony_ci		ret = analogix_dp_stop_crc(connector);
169062306a36Sopenharmony_ci	else
169162306a36Sopenharmony_ci		ret = -EINVAL;
169262306a36Sopenharmony_ci
169362306a36Sopenharmony_ci	return ret;
169462306a36Sopenharmony_ci}
169562306a36Sopenharmony_ci
169662306a36Sopenharmony_cistatic int
169762306a36Sopenharmony_civop_crtc_verify_crc_source(struct drm_crtc *crtc, const char *source_name,
169862306a36Sopenharmony_ci			   size_t *values_cnt)
169962306a36Sopenharmony_ci{
170062306a36Sopenharmony_ci	if (source_name && strcmp(source_name, "auto") != 0)
170162306a36Sopenharmony_ci		return -EINVAL;
170262306a36Sopenharmony_ci
170362306a36Sopenharmony_ci	*values_cnt = 3;
170462306a36Sopenharmony_ci	return 0;
170562306a36Sopenharmony_ci}
170662306a36Sopenharmony_ci
170762306a36Sopenharmony_ci#else
170862306a36Sopenharmony_cistatic int vop_crtc_set_crc_source(struct drm_crtc *crtc,
170962306a36Sopenharmony_ci				   const char *source_name)
171062306a36Sopenharmony_ci{
171162306a36Sopenharmony_ci	return -ENODEV;
171262306a36Sopenharmony_ci}
171362306a36Sopenharmony_ci
171462306a36Sopenharmony_cistatic int
171562306a36Sopenharmony_civop_crtc_verify_crc_source(struct drm_crtc *crtc, const char *source_name,
171662306a36Sopenharmony_ci			   size_t *values_cnt)
171762306a36Sopenharmony_ci{
171862306a36Sopenharmony_ci	return -ENODEV;
171962306a36Sopenharmony_ci}
172062306a36Sopenharmony_ci#endif
172162306a36Sopenharmony_ci
172262306a36Sopenharmony_cistatic const struct drm_crtc_funcs vop_crtc_funcs = {
172362306a36Sopenharmony_ci	.set_config = drm_atomic_helper_set_config,
172462306a36Sopenharmony_ci	.page_flip = drm_atomic_helper_page_flip,
172562306a36Sopenharmony_ci	.destroy = vop_crtc_destroy,
172662306a36Sopenharmony_ci	.reset = vop_crtc_reset,
172762306a36Sopenharmony_ci	.atomic_duplicate_state = vop_crtc_duplicate_state,
172862306a36Sopenharmony_ci	.atomic_destroy_state = vop_crtc_destroy_state,
172962306a36Sopenharmony_ci	.enable_vblank = vop_crtc_enable_vblank,
173062306a36Sopenharmony_ci	.disable_vblank = vop_crtc_disable_vblank,
173162306a36Sopenharmony_ci	.set_crc_source = vop_crtc_set_crc_source,
173262306a36Sopenharmony_ci	.verify_crc_source = vop_crtc_verify_crc_source,
173362306a36Sopenharmony_ci};
173462306a36Sopenharmony_ci
173562306a36Sopenharmony_cistatic void vop_fb_unref_worker(struct drm_flip_work *work, void *val)
173662306a36Sopenharmony_ci{
173762306a36Sopenharmony_ci	struct vop *vop = container_of(work, struct vop, fb_unref_work);
173862306a36Sopenharmony_ci	struct drm_framebuffer *fb = val;
173962306a36Sopenharmony_ci
174062306a36Sopenharmony_ci	drm_crtc_vblank_put(&vop->crtc);
174162306a36Sopenharmony_ci	drm_framebuffer_put(fb);
174262306a36Sopenharmony_ci}
174362306a36Sopenharmony_ci
174462306a36Sopenharmony_cistatic void vop_handle_vblank(struct vop *vop)
174562306a36Sopenharmony_ci{
174662306a36Sopenharmony_ci	struct drm_device *drm = vop->drm_dev;
174762306a36Sopenharmony_ci	struct drm_crtc *crtc = &vop->crtc;
174862306a36Sopenharmony_ci
174962306a36Sopenharmony_ci	spin_lock(&drm->event_lock);
175062306a36Sopenharmony_ci	if (vop->event) {
175162306a36Sopenharmony_ci		drm_crtc_send_vblank_event(crtc, vop->event);
175262306a36Sopenharmony_ci		drm_crtc_vblank_put(crtc);
175362306a36Sopenharmony_ci		vop->event = NULL;
175462306a36Sopenharmony_ci	}
175562306a36Sopenharmony_ci	spin_unlock(&drm->event_lock);
175662306a36Sopenharmony_ci
175762306a36Sopenharmony_ci	if (test_and_clear_bit(VOP_PENDING_FB_UNREF, &vop->pending))
175862306a36Sopenharmony_ci		drm_flip_work_commit(&vop->fb_unref_work, system_unbound_wq);
175962306a36Sopenharmony_ci}
176062306a36Sopenharmony_ci
176162306a36Sopenharmony_cistatic irqreturn_t vop_isr(int irq, void *data)
176262306a36Sopenharmony_ci{
176362306a36Sopenharmony_ci	struct vop *vop = data;
176462306a36Sopenharmony_ci	struct drm_crtc *crtc = &vop->crtc;
176562306a36Sopenharmony_ci	uint32_t active_irqs;
176662306a36Sopenharmony_ci	int ret = IRQ_NONE;
176762306a36Sopenharmony_ci
176862306a36Sopenharmony_ci	/*
176962306a36Sopenharmony_ci	 * The irq is shared with the iommu. If the runtime-pm state of the
177062306a36Sopenharmony_ci	 * vop-device is disabled the irq has to be targeted at the iommu.
177162306a36Sopenharmony_ci	 */
177262306a36Sopenharmony_ci	if (!pm_runtime_get_if_in_use(vop->dev))
177362306a36Sopenharmony_ci		return IRQ_NONE;
177462306a36Sopenharmony_ci
177562306a36Sopenharmony_ci	if (vop_core_clks_enable(vop)) {
177662306a36Sopenharmony_ci		DRM_DEV_ERROR_RATELIMITED(vop->dev, "couldn't enable clocks\n");
177762306a36Sopenharmony_ci		goto out;
177862306a36Sopenharmony_ci	}
177962306a36Sopenharmony_ci
178062306a36Sopenharmony_ci	/*
178162306a36Sopenharmony_ci	 * interrupt register has interrupt status, enable and clear bits, we
178262306a36Sopenharmony_ci	 * must hold irq_lock to avoid a race with enable/disable_vblank().
178362306a36Sopenharmony_ci	*/
178462306a36Sopenharmony_ci	spin_lock(&vop->irq_lock);
178562306a36Sopenharmony_ci
178662306a36Sopenharmony_ci	active_irqs = VOP_INTR_GET_TYPE(vop, status, INTR_MASK);
178762306a36Sopenharmony_ci	/* Clear all active interrupt sources */
178862306a36Sopenharmony_ci	if (active_irqs)
178962306a36Sopenharmony_ci		VOP_INTR_SET_TYPE(vop, clear, active_irqs, 1);
179062306a36Sopenharmony_ci
179162306a36Sopenharmony_ci	spin_unlock(&vop->irq_lock);
179262306a36Sopenharmony_ci
179362306a36Sopenharmony_ci	/* This is expected for vop iommu irqs, since the irq is shared */
179462306a36Sopenharmony_ci	if (!active_irqs)
179562306a36Sopenharmony_ci		goto out_disable;
179662306a36Sopenharmony_ci
179762306a36Sopenharmony_ci	if (active_irqs & DSP_HOLD_VALID_INTR) {
179862306a36Sopenharmony_ci		complete(&vop->dsp_hold_completion);
179962306a36Sopenharmony_ci		active_irqs &= ~DSP_HOLD_VALID_INTR;
180062306a36Sopenharmony_ci		ret = IRQ_HANDLED;
180162306a36Sopenharmony_ci	}
180262306a36Sopenharmony_ci
180362306a36Sopenharmony_ci	if (active_irqs & LINE_FLAG_INTR) {
180462306a36Sopenharmony_ci		complete(&vop->line_flag_completion);
180562306a36Sopenharmony_ci		active_irqs &= ~LINE_FLAG_INTR;
180662306a36Sopenharmony_ci		ret = IRQ_HANDLED;
180762306a36Sopenharmony_ci	}
180862306a36Sopenharmony_ci
180962306a36Sopenharmony_ci	if (active_irqs & FS_INTR) {
181062306a36Sopenharmony_ci		drm_crtc_handle_vblank(crtc);
181162306a36Sopenharmony_ci		vop_handle_vblank(vop);
181262306a36Sopenharmony_ci		active_irqs &= ~FS_INTR;
181362306a36Sopenharmony_ci		ret = IRQ_HANDLED;
181462306a36Sopenharmony_ci	}
181562306a36Sopenharmony_ci
181662306a36Sopenharmony_ci	/* Unhandled irqs are spurious. */
181762306a36Sopenharmony_ci	if (active_irqs)
181862306a36Sopenharmony_ci		DRM_DEV_ERROR(vop->dev, "Unknown VOP IRQs: %#02x\n",
181962306a36Sopenharmony_ci			      active_irqs);
182062306a36Sopenharmony_ci
182162306a36Sopenharmony_ciout_disable:
182262306a36Sopenharmony_ci	vop_core_clks_disable(vop);
182362306a36Sopenharmony_ciout:
182462306a36Sopenharmony_ci	pm_runtime_put(vop->dev);
182562306a36Sopenharmony_ci	return ret;
182662306a36Sopenharmony_ci}
182762306a36Sopenharmony_ci
182862306a36Sopenharmony_cistatic void vop_plane_add_properties(struct drm_plane *plane,
182962306a36Sopenharmony_ci				     const struct vop_win_data *win_data)
183062306a36Sopenharmony_ci{
183162306a36Sopenharmony_ci	unsigned int flags = 0;
183262306a36Sopenharmony_ci
183362306a36Sopenharmony_ci	flags |= VOP_WIN_HAS_REG(win_data, x_mir_en) ? DRM_MODE_REFLECT_X : 0;
183462306a36Sopenharmony_ci	flags |= VOP_WIN_HAS_REG(win_data, y_mir_en) ? DRM_MODE_REFLECT_Y : 0;
183562306a36Sopenharmony_ci	if (flags)
183662306a36Sopenharmony_ci		drm_plane_create_rotation_property(plane, DRM_MODE_ROTATE_0,
183762306a36Sopenharmony_ci						   DRM_MODE_ROTATE_0 | flags);
183862306a36Sopenharmony_ci}
183962306a36Sopenharmony_ci
184062306a36Sopenharmony_cistatic int vop_create_crtc(struct vop *vop)
184162306a36Sopenharmony_ci{
184262306a36Sopenharmony_ci	const struct vop_data *vop_data = vop->data;
184362306a36Sopenharmony_ci	struct device *dev = vop->dev;
184462306a36Sopenharmony_ci	struct drm_device *drm_dev = vop->drm_dev;
184562306a36Sopenharmony_ci	struct drm_plane *primary = NULL, *cursor = NULL, *plane, *tmp;
184662306a36Sopenharmony_ci	struct drm_crtc *crtc = &vop->crtc;
184762306a36Sopenharmony_ci	struct device_node *port;
184862306a36Sopenharmony_ci	int ret;
184962306a36Sopenharmony_ci	int i;
185062306a36Sopenharmony_ci
185162306a36Sopenharmony_ci	/*
185262306a36Sopenharmony_ci	 * Create drm_plane for primary and cursor planes first, since we need
185362306a36Sopenharmony_ci	 * to pass them to drm_crtc_init_with_planes, which sets the
185462306a36Sopenharmony_ci	 * "possible_crtcs" to the newly initialized crtc.
185562306a36Sopenharmony_ci	 */
185662306a36Sopenharmony_ci	for (i = 0; i < vop_data->win_size; i++) {
185762306a36Sopenharmony_ci		struct vop_win *vop_win = &vop->win[i];
185862306a36Sopenharmony_ci		const struct vop_win_data *win_data = vop_win->data;
185962306a36Sopenharmony_ci
186062306a36Sopenharmony_ci		if (win_data->type != DRM_PLANE_TYPE_PRIMARY &&
186162306a36Sopenharmony_ci		    win_data->type != DRM_PLANE_TYPE_CURSOR)
186262306a36Sopenharmony_ci			continue;
186362306a36Sopenharmony_ci
186462306a36Sopenharmony_ci		ret = drm_universal_plane_init(vop->drm_dev, &vop_win->base,
186562306a36Sopenharmony_ci					       0, &vop_plane_funcs,
186662306a36Sopenharmony_ci					       win_data->phy->data_formats,
186762306a36Sopenharmony_ci					       win_data->phy->nformats,
186862306a36Sopenharmony_ci					       win_data->phy->format_modifiers,
186962306a36Sopenharmony_ci					       win_data->type, NULL);
187062306a36Sopenharmony_ci		if (ret) {
187162306a36Sopenharmony_ci			DRM_DEV_ERROR(vop->dev, "failed to init plane %d\n",
187262306a36Sopenharmony_ci				      ret);
187362306a36Sopenharmony_ci			goto err_cleanup_planes;
187462306a36Sopenharmony_ci		}
187562306a36Sopenharmony_ci
187662306a36Sopenharmony_ci		plane = &vop_win->base;
187762306a36Sopenharmony_ci		drm_plane_helper_add(plane, &plane_helper_funcs);
187862306a36Sopenharmony_ci		vop_plane_add_properties(plane, win_data);
187962306a36Sopenharmony_ci		if (plane->type == DRM_PLANE_TYPE_PRIMARY)
188062306a36Sopenharmony_ci			primary = plane;
188162306a36Sopenharmony_ci		else if (plane->type == DRM_PLANE_TYPE_CURSOR)
188262306a36Sopenharmony_ci			cursor = plane;
188362306a36Sopenharmony_ci	}
188462306a36Sopenharmony_ci
188562306a36Sopenharmony_ci	ret = drm_crtc_init_with_planes(drm_dev, crtc, primary, cursor,
188662306a36Sopenharmony_ci					&vop_crtc_funcs, NULL);
188762306a36Sopenharmony_ci	if (ret)
188862306a36Sopenharmony_ci		goto err_cleanup_planes;
188962306a36Sopenharmony_ci
189062306a36Sopenharmony_ci	drm_crtc_helper_add(crtc, &vop_crtc_helper_funcs);
189162306a36Sopenharmony_ci	if (vop->lut_regs) {
189262306a36Sopenharmony_ci		drm_mode_crtc_set_gamma_size(crtc, vop_data->lut_size);
189362306a36Sopenharmony_ci		drm_crtc_enable_color_mgmt(crtc, 0, false, vop_data->lut_size);
189462306a36Sopenharmony_ci	}
189562306a36Sopenharmony_ci
189662306a36Sopenharmony_ci	/*
189762306a36Sopenharmony_ci	 * Create drm_planes for overlay windows with possible_crtcs restricted
189862306a36Sopenharmony_ci	 * to the newly created crtc.
189962306a36Sopenharmony_ci	 */
190062306a36Sopenharmony_ci	for (i = 0; i < vop_data->win_size; i++) {
190162306a36Sopenharmony_ci		struct vop_win *vop_win = &vop->win[i];
190262306a36Sopenharmony_ci		const struct vop_win_data *win_data = vop_win->data;
190362306a36Sopenharmony_ci		unsigned long possible_crtcs = drm_crtc_mask(crtc);
190462306a36Sopenharmony_ci
190562306a36Sopenharmony_ci		if (win_data->type != DRM_PLANE_TYPE_OVERLAY)
190662306a36Sopenharmony_ci			continue;
190762306a36Sopenharmony_ci
190862306a36Sopenharmony_ci		ret = drm_universal_plane_init(vop->drm_dev, &vop_win->base,
190962306a36Sopenharmony_ci					       possible_crtcs,
191062306a36Sopenharmony_ci					       &vop_plane_funcs,
191162306a36Sopenharmony_ci					       win_data->phy->data_formats,
191262306a36Sopenharmony_ci					       win_data->phy->nformats,
191362306a36Sopenharmony_ci					       win_data->phy->format_modifiers,
191462306a36Sopenharmony_ci					       win_data->type, NULL);
191562306a36Sopenharmony_ci		if (ret) {
191662306a36Sopenharmony_ci			DRM_DEV_ERROR(vop->dev, "failed to init overlay %d\n",
191762306a36Sopenharmony_ci				      ret);
191862306a36Sopenharmony_ci			goto err_cleanup_crtc;
191962306a36Sopenharmony_ci		}
192062306a36Sopenharmony_ci		drm_plane_helper_add(&vop_win->base, &plane_helper_funcs);
192162306a36Sopenharmony_ci		vop_plane_add_properties(&vop_win->base, win_data);
192262306a36Sopenharmony_ci	}
192362306a36Sopenharmony_ci
192462306a36Sopenharmony_ci	port = of_get_child_by_name(dev->of_node, "port");
192562306a36Sopenharmony_ci	if (!port) {
192662306a36Sopenharmony_ci		DRM_DEV_ERROR(vop->dev, "no port node found in %pOF\n",
192762306a36Sopenharmony_ci			      dev->of_node);
192862306a36Sopenharmony_ci		ret = -ENOENT;
192962306a36Sopenharmony_ci		goto err_cleanup_crtc;
193062306a36Sopenharmony_ci	}
193162306a36Sopenharmony_ci
193262306a36Sopenharmony_ci	drm_flip_work_init(&vop->fb_unref_work, "fb_unref",
193362306a36Sopenharmony_ci			   vop_fb_unref_worker);
193462306a36Sopenharmony_ci
193562306a36Sopenharmony_ci	init_completion(&vop->dsp_hold_completion);
193662306a36Sopenharmony_ci	init_completion(&vop->line_flag_completion);
193762306a36Sopenharmony_ci	crtc->port = port;
193862306a36Sopenharmony_ci
193962306a36Sopenharmony_ci	ret = drm_self_refresh_helper_init(crtc);
194062306a36Sopenharmony_ci	if (ret)
194162306a36Sopenharmony_ci		DRM_DEV_DEBUG_KMS(vop->dev,
194262306a36Sopenharmony_ci			"Failed to init %s with SR helpers %d, ignoring\n",
194362306a36Sopenharmony_ci			crtc->name, ret);
194462306a36Sopenharmony_ci
194562306a36Sopenharmony_ci	return 0;
194662306a36Sopenharmony_ci
194762306a36Sopenharmony_cierr_cleanup_crtc:
194862306a36Sopenharmony_ci	drm_crtc_cleanup(crtc);
194962306a36Sopenharmony_cierr_cleanup_planes:
195062306a36Sopenharmony_ci	list_for_each_entry_safe(plane, tmp, &drm_dev->mode_config.plane_list,
195162306a36Sopenharmony_ci				 head)
195262306a36Sopenharmony_ci		drm_plane_cleanup(plane);
195362306a36Sopenharmony_ci	return ret;
195462306a36Sopenharmony_ci}
195562306a36Sopenharmony_ci
195662306a36Sopenharmony_cistatic void vop_destroy_crtc(struct vop *vop)
195762306a36Sopenharmony_ci{
195862306a36Sopenharmony_ci	struct drm_crtc *crtc = &vop->crtc;
195962306a36Sopenharmony_ci	struct drm_device *drm_dev = vop->drm_dev;
196062306a36Sopenharmony_ci	struct drm_plane *plane, *tmp;
196162306a36Sopenharmony_ci
196262306a36Sopenharmony_ci	drm_self_refresh_helper_cleanup(crtc);
196362306a36Sopenharmony_ci
196462306a36Sopenharmony_ci	of_node_put(crtc->port);
196562306a36Sopenharmony_ci
196662306a36Sopenharmony_ci	/*
196762306a36Sopenharmony_ci	 * We need to cleanup the planes now.  Why?
196862306a36Sopenharmony_ci	 *
196962306a36Sopenharmony_ci	 * The planes are "&vop->win[i].base".  That means the memory is
197062306a36Sopenharmony_ci	 * all part of the big "struct vop" chunk of memory.  That memory
197162306a36Sopenharmony_ci	 * was devm allocated and associated with this component.  We need to
197262306a36Sopenharmony_ci	 * free it ourselves before vop_unbind() finishes.
197362306a36Sopenharmony_ci	 */
197462306a36Sopenharmony_ci	list_for_each_entry_safe(plane, tmp, &drm_dev->mode_config.plane_list,
197562306a36Sopenharmony_ci				 head)
197662306a36Sopenharmony_ci		vop_plane_destroy(plane);
197762306a36Sopenharmony_ci
197862306a36Sopenharmony_ci	/*
197962306a36Sopenharmony_ci	 * Destroy CRTC after vop_plane_destroy() since vop_disable_plane()
198062306a36Sopenharmony_ci	 * references the CRTC.
198162306a36Sopenharmony_ci	 */
198262306a36Sopenharmony_ci	drm_crtc_cleanup(crtc);
198362306a36Sopenharmony_ci	drm_flip_work_cleanup(&vop->fb_unref_work);
198462306a36Sopenharmony_ci}
198562306a36Sopenharmony_ci
198662306a36Sopenharmony_cistatic int vop_initial(struct vop *vop)
198762306a36Sopenharmony_ci{
198862306a36Sopenharmony_ci	struct reset_control *ahb_rst;
198962306a36Sopenharmony_ci	int i, ret;
199062306a36Sopenharmony_ci
199162306a36Sopenharmony_ci	vop->hclk = devm_clk_get(vop->dev, "hclk_vop");
199262306a36Sopenharmony_ci	if (IS_ERR(vop->hclk)) {
199362306a36Sopenharmony_ci		DRM_DEV_ERROR(vop->dev, "failed to get hclk source\n");
199462306a36Sopenharmony_ci		return PTR_ERR(vop->hclk);
199562306a36Sopenharmony_ci	}
199662306a36Sopenharmony_ci	vop->aclk = devm_clk_get(vop->dev, "aclk_vop");
199762306a36Sopenharmony_ci	if (IS_ERR(vop->aclk)) {
199862306a36Sopenharmony_ci		DRM_DEV_ERROR(vop->dev, "failed to get aclk source\n");
199962306a36Sopenharmony_ci		return PTR_ERR(vop->aclk);
200062306a36Sopenharmony_ci	}
200162306a36Sopenharmony_ci	vop->dclk = devm_clk_get(vop->dev, "dclk_vop");
200262306a36Sopenharmony_ci	if (IS_ERR(vop->dclk)) {
200362306a36Sopenharmony_ci		DRM_DEV_ERROR(vop->dev, "failed to get dclk source\n");
200462306a36Sopenharmony_ci		return PTR_ERR(vop->dclk);
200562306a36Sopenharmony_ci	}
200662306a36Sopenharmony_ci
200762306a36Sopenharmony_ci	ret = pm_runtime_resume_and_get(vop->dev);
200862306a36Sopenharmony_ci	if (ret < 0) {
200962306a36Sopenharmony_ci		DRM_DEV_ERROR(vop->dev, "failed to get pm runtime: %d\n", ret);
201062306a36Sopenharmony_ci		return ret;
201162306a36Sopenharmony_ci	}
201262306a36Sopenharmony_ci
201362306a36Sopenharmony_ci	ret = clk_prepare(vop->dclk);
201462306a36Sopenharmony_ci	if (ret < 0) {
201562306a36Sopenharmony_ci		DRM_DEV_ERROR(vop->dev, "failed to prepare dclk\n");
201662306a36Sopenharmony_ci		goto err_put_pm_runtime;
201762306a36Sopenharmony_ci	}
201862306a36Sopenharmony_ci
201962306a36Sopenharmony_ci	/* Enable both the hclk and aclk to setup the vop */
202062306a36Sopenharmony_ci	ret = clk_prepare_enable(vop->hclk);
202162306a36Sopenharmony_ci	if (ret < 0) {
202262306a36Sopenharmony_ci		DRM_DEV_ERROR(vop->dev, "failed to prepare/enable hclk\n");
202362306a36Sopenharmony_ci		goto err_unprepare_dclk;
202462306a36Sopenharmony_ci	}
202562306a36Sopenharmony_ci
202662306a36Sopenharmony_ci	ret = clk_prepare_enable(vop->aclk);
202762306a36Sopenharmony_ci	if (ret < 0) {
202862306a36Sopenharmony_ci		DRM_DEV_ERROR(vop->dev, "failed to prepare/enable aclk\n");
202962306a36Sopenharmony_ci		goto err_disable_hclk;
203062306a36Sopenharmony_ci	}
203162306a36Sopenharmony_ci
203262306a36Sopenharmony_ci	/*
203362306a36Sopenharmony_ci	 * do hclk_reset, reset all vop registers.
203462306a36Sopenharmony_ci	 */
203562306a36Sopenharmony_ci	ahb_rst = devm_reset_control_get(vop->dev, "ahb");
203662306a36Sopenharmony_ci	if (IS_ERR(ahb_rst)) {
203762306a36Sopenharmony_ci		DRM_DEV_ERROR(vop->dev, "failed to get ahb reset\n");
203862306a36Sopenharmony_ci		ret = PTR_ERR(ahb_rst);
203962306a36Sopenharmony_ci		goto err_disable_aclk;
204062306a36Sopenharmony_ci	}
204162306a36Sopenharmony_ci	reset_control_assert(ahb_rst);
204262306a36Sopenharmony_ci	usleep_range(10, 20);
204362306a36Sopenharmony_ci	reset_control_deassert(ahb_rst);
204462306a36Sopenharmony_ci
204562306a36Sopenharmony_ci	VOP_INTR_SET_TYPE(vop, clear, INTR_MASK, 1);
204662306a36Sopenharmony_ci	VOP_INTR_SET_TYPE(vop, enable, INTR_MASK, 0);
204762306a36Sopenharmony_ci
204862306a36Sopenharmony_ci	for (i = 0; i < vop->len; i += sizeof(u32))
204962306a36Sopenharmony_ci		vop->regsbak[i / 4] = readl_relaxed(vop->regs + i);
205062306a36Sopenharmony_ci
205162306a36Sopenharmony_ci	VOP_REG_SET(vop, misc, global_regdone_en, 1);
205262306a36Sopenharmony_ci	VOP_REG_SET(vop, common, dsp_blank, 0);
205362306a36Sopenharmony_ci
205462306a36Sopenharmony_ci	for (i = 0; i < vop->data->win_size; i++) {
205562306a36Sopenharmony_ci		struct vop_win *vop_win = &vop->win[i];
205662306a36Sopenharmony_ci		const struct vop_win_data *win = vop_win->data;
205762306a36Sopenharmony_ci		int channel = i * 2 + 1;
205862306a36Sopenharmony_ci
205962306a36Sopenharmony_ci		VOP_WIN_SET(vop, win, channel, (channel + 1) << 4 | channel);
206062306a36Sopenharmony_ci		vop_win_disable(vop, vop_win);
206162306a36Sopenharmony_ci		VOP_WIN_SET(vop, win, gate, 1);
206262306a36Sopenharmony_ci	}
206362306a36Sopenharmony_ci
206462306a36Sopenharmony_ci	vop_cfg_done(vop);
206562306a36Sopenharmony_ci
206662306a36Sopenharmony_ci	/*
206762306a36Sopenharmony_ci	 * do dclk_reset, let all config take affect.
206862306a36Sopenharmony_ci	 */
206962306a36Sopenharmony_ci	vop->dclk_rst = devm_reset_control_get(vop->dev, "dclk");
207062306a36Sopenharmony_ci	if (IS_ERR(vop->dclk_rst)) {
207162306a36Sopenharmony_ci		DRM_DEV_ERROR(vop->dev, "failed to get dclk reset\n");
207262306a36Sopenharmony_ci		ret = PTR_ERR(vop->dclk_rst);
207362306a36Sopenharmony_ci		goto err_disable_aclk;
207462306a36Sopenharmony_ci	}
207562306a36Sopenharmony_ci	reset_control_assert(vop->dclk_rst);
207662306a36Sopenharmony_ci	usleep_range(10, 20);
207762306a36Sopenharmony_ci	reset_control_deassert(vop->dclk_rst);
207862306a36Sopenharmony_ci
207962306a36Sopenharmony_ci	clk_disable(vop->hclk);
208062306a36Sopenharmony_ci	clk_disable(vop->aclk);
208162306a36Sopenharmony_ci
208262306a36Sopenharmony_ci	vop->is_enabled = false;
208362306a36Sopenharmony_ci
208462306a36Sopenharmony_ci	pm_runtime_put_sync(vop->dev);
208562306a36Sopenharmony_ci
208662306a36Sopenharmony_ci	return 0;
208762306a36Sopenharmony_ci
208862306a36Sopenharmony_cierr_disable_aclk:
208962306a36Sopenharmony_ci	clk_disable_unprepare(vop->aclk);
209062306a36Sopenharmony_cierr_disable_hclk:
209162306a36Sopenharmony_ci	clk_disable_unprepare(vop->hclk);
209262306a36Sopenharmony_cierr_unprepare_dclk:
209362306a36Sopenharmony_ci	clk_unprepare(vop->dclk);
209462306a36Sopenharmony_cierr_put_pm_runtime:
209562306a36Sopenharmony_ci	pm_runtime_put_sync(vop->dev);
209662306a36Sopenharmony_ci	return ret;
209762306a36Sopenharmony_ci}
209862306a36Sopenharmony_ci
209962306a36Sopenharmony_ci/*
210062306a36Sopenharmony_ci * Initialize the vop->win array elements.
210162306a36Sopenharmony_ci */
210262306a36Sopenharmony_cistatic void vop_win_init(struct vop *vop)
210362306a36Sopenharmony_ci{
210462306a36Sopenharmony_ci	const struct vop_data *vop_data = vop->data;
210562306a36Sopenharmony_ci	unsigned int i;
210662306a36Sopenharmony_ci
210762306a36Sopenharmony_ci	for (i = 0; i < vop_data->win_size; i++) {
210862306a36Sopenharmony_ci		struct vop_win *vop_win = &vop->win[i];
210962306a36Sopenharmony_ci		const struct vop_win_data *win_data = &vop_data->win[i];
211062306a36Sopenharmony_ci
211162306a36Sopenharmony_ci		vop_win->data = win_data;
211262306a36Sopenharmony_ci		vop_win->vop = vop;
211362306a36Sopenharmony_ci
211462306a36Sopenharmony_ci		if (vop_data->win_yuv2yuv)
211562306a36Sopenharmony_ci			vop_win->yuv2yuv_data = &vop_data->win_yuv2yuv[i];
211662306a36Sopenharmony_ci	}
211762306a36Sopenharmony_ci}
211862306a36Sopenharmony_ci
211962306a36Sopenharmony_ci/**
212062306a36Sopenharmony_ci * rockchip_drm_wait_vact_end
212162306a36Sopenharmony_ci * @crtc: CRTC to enable line flag
212262306a36Sopenharmony_ci * @mstimeout: millisecond for timeout
212362306a36Sopenharmony_ci *
212462306a36Sopenharmony_ci * Wait for vact_end line flag irq or timeout.
212562306a36Sopenharmony_ci *
212662306a36Sopenharmony_ci * Returns:
212762306a36Sopenharmony_ci * Zero on success, negative errno on failure.
212862306a36Sopenharmony_ci */
212962306a36Sopenharmony_ciint rockchip_drm_wait_vact_end(struct drm_crtc *crtc, unsigned int mstimeout)
213062306a36Sopenharmony_ci{
213162306a36Sopenharmony_ci	struct vop *vop = to_vop(crtc);
213262306a36Sopenharmony_ci	unsigned long jiffies_left;
213362306a36Sopenharmony_ci	int ret = 0;
213462306a36Sopenharmony_ci
213562306a36Sopenharmony_ci	if (!crtc || !vop->is_enabled)
213662306a36Sopenharmony_ci		return -ENODEV;
213762306a36Sopenharmony_ci
213862306a36Sopenharmony_ci	mutex_lock(&vop->vop_lock);
213962306a36Sopenharmony_ci	if (mstimeout <= 0) {
214062306a36Sopenharmony_ci		ret = -EINVAL;
214162306a36Sopenharmony_ci		goto out;
214262306a36Sopenharmony_ci	}
214362306a36Sopenharmony_ci
214462306a36Sopenharmony_ci	if (vop_line_flag_irq_is_enabled(vop)) {
214562306a36Sopenharmony_ci		ret = -EBUSY;
214662306a36Sopenharmony_ci		goto out;
214762306a36Sopenharmony_ci	}
214862306a36Sopenharmony_ci
214962306a36Sopenharmony_ci	reinit_completion(&vop->line_flag_completion);
215062306a36Sopenharmony_ci	vop_line_flag_irq_enable(vop);
215162306a36Sopenharmony_ci
215262306a36Sopenharmony_ci	jiffies_left = wait_for_completion_timeout(&vop->line_flag_completion,
215362306a36Sopenharmony_ci						   msecs_to_jiffies(mstimeout));
215462306a36Sopenharmony_ci	vop_line_flag_irq_disable(vop);
215562306a36Sopenharmony_ci
215662306a36Sopenharmony_ci	if (jiffies_left == 0) {
215762306a36Sopenharmony_ci		DRM_DEV_ERROR(vop->dev, "Timeout waiting for IRQ\n");
215862306a36Sopenharmony_ci		ret = -ETIMEDOUT;
215962306a36Sopenharmony_ci		goto out;
216062306a36Sopenharmony_ci	}
216162306a36Sopenharmony_ci
216262306a36Sopenharmony_ciout:
216362306a36Sopenharmony_ci	mutex_unlock(&vop->vop_lock);
216462306a36Sopenharmony_ci	return ret;
216562306a36Sopenharmony_ci}
216662306a36Sopenharmony_ciEXPORT_SYMBOL(rockchip_drm_wait_vact_end);
216762306a36Sopenharmony_ci
216862306a36Sopenharmony_cistatic int vop_bind(struct device *dev, struct device *master, void *data)
216962306a36Sopenharmony_ci{
217062306a36Sopenharmony_ci	struct platform_device *pdev = to_platform_device(dev);
217162306a36Sopenharmony_ci	const struct vop_data *vop_data;
217262306a36Sopenharmony_ci	struct drm_device *drm_dev = data;
217362306a36Sopenharmony_ci	struct vop *vop;
217462306a36Sopenharmony_ci	struct resource *res;
217562306a36Sopenharmony_ci	int ret, irq;
217662306a36Sopenharmony_ci
217762306a36Sopenharmony_ci	vop_data = of_device_get_match_data(dev);
217862306a36Sopenharmony_ci	if (!vop_data)
217962306a36Sopenharmony_ci		return -ENODEV;
218062306a36Sopenharmony_ci
218162306a36Sopenharmony_ci	/* Allocate vop struct and its vop_win array */
218262306a36Sopenharmony_ci	vop = devm_kzalloc(dev, struct_size(vop, win, vop_data->win_size),
218362306a36Sopenharmony_ci			   GFP_KERNEL);
218462306a36Sopenharmony_ci	if (!vop)
218562306a36Sopenharmony_ci		return -ENOMEM;
218662306a36Sopenharmony_ci
218762306a36Sopenharmony_ci	vop->dev = dev;
218862306a36Sopenharmony_ci	vop->data = vop_data;
218962306a36Sopenharmony_ci	vop->drm_dev = drm_dev;
219062306a36Sopenharmony_ci	dev_set_drvdata(dev, vop);
219162306a36Sopenharmony_ci
219262306a36Sopenharmony_ci	vop_win_init(vop);
219362306a36Sopenharmony_ci
219462306a36Sopenharmony_ci	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
219562306a36Sopenharmony_ci	vop->regs = devm_ioremap_resource(dev, res);
219662306a36Sopenharmony_ci	if (IS_ERR(vop->regs))
219762306a36Sopenharmony_ci		return PTR_ERR(vop->regs);
219862306a36Sopenharmony_ci	vop->len = resource_size(res);
219962306a36Sopenharmony_ci
220062306a36Sopenharmony_ci	res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
220162306a36Sopenharmony_ci	if (res) {
220262306a36Sopenharmony_ci		if (vop_data->lut_size != 1024 && vop_data->lut_size != 256) {
220362306a36Sopenharmony_ci			DRM_DEV_ERROR(dev, "unsupported gamma LUT size %d\n", vop_data->lut_size);
220462306a36Sopenharmony_ci			return -EINVAL;
220562306a36Sopenharmony_ci		}
220662306a36Sopenharmony_ci		vop->lut_regs = devm_ioremap_resource(dev, res);
220762306a36Sopenharmony_ci		if (IS_ERR(vop->lut_regs))
220862306a36Sopenharmony_ci			return PTR_ERR(vop->lut_regs);
220962306a36Sopenharmony_ci	}
221062306a36Sopenharmony_ci
221162306a36Sopenharmony_ci	vop->regsbak = devm_kzalloc(dev, vop->len, GFP_KERNEL);
221262306a36Sopenharmony_ci	if (!vop->regsbak)
221362306a36Sopenharmony_ci		return -ENOMEM;
221462306a36Sopenharmony_ci
221562306a36Sopenharmony_ci	irq = platform_get_irq(pdev, 0);
221662306a36Sopenharmony_ci	if (irq < 0) {
221762306a36Sopenharmony_ci		DRM_DEV_ERROR(dev, "cannot find irq for vop\n");
221862306a36Sopenharmony_ci		return irq;
221962306a36Sopenharmony_ci	}
222062306a36Sopenharmony_ci	vop->irq = (unsigned int)irq;
222162306a36Sopenharmony_ci
222262306a36Sopenharmony_ci	spin_lock_init(&vop->reg_lock);
222362306a36Sopenharmony_ci	spin_lock_init(&vop->irq_lock);
222462306a36Sopenharmony_ci	mutex_init(&vop->vop_lock);
222562306a36Sopenharmony_ci
222662306a36Sopenharmony_ci	ret = vop_create_crtc(vop);
222762306a36Sopenharmony_ci	if (ret)
222862306a36Sopenharmony_ci		return ret;
222962306a36Sopenharmony_ci
223062306a36Sopenharmony_ci	pm_runtime_enable(&pdev->dev);
223162306a36Sopenharmony_ci
223262306a36Sopenharmony_ci	ret = vop_initial(vop);
223362306a36Sopenharmony_ci	if (ret < 0) {
223462306a36Sopenharmony_ci		DRM_DEV_ERROR(&pdev->dev,
223562306a36Sopenharmony_ci			      "cannot initial vop dev - err %d\n", ret);
223662306a36Sopenharmony_ci		goto err_disable_pm_runtime;
223762306a36Sopenharmony_ci	}
223862306a36Sopenharmony_ci
223962306a36Sopenharmony_ci	ret = devm_request_irq(dev, vop->irq, vop_isr,
224062306a36Sopenharmony_ci			       IRQF_SHARED, dev_name(dev), vop);
224162306a36Sopenharmony_ci	if (ret)
224262306a36Sopenharmony_ci		goto err_disable_pm_runtime;
224362306a36Sopenharmony_ci
224462306a36Sopenharmony_ci	if (vop->data->feature & VOP_FEATURE_INTERNAL_RGB) {
224562306a36Sopenharmony_ci		vop->rgb = rockchip_rgb_init(dev, &vop->crtc, vop->drm_dev, 0);
224662306a36Sopenharmony_ci		if (IS_ERR(vop->rgb)) {
224762306a36Sopenharmony_ci			ret = PTR_ERR(vop->rgb);
224862306a36Sopenharmony_ci			goto err_disable_pm_runtime;
224962306a36Sopenharmony_ci		}
225062306a36Sopenharmony_ci	}
225162306a36Sopenharmony_ci
225262306a36Sopenharmony_ci	rockchip_drm_dma_init_device(drm_dev, dev);
225362306a36Sopenharmony_ci
225462306a36Sopenharmony_ci	return 0;
225562306a36Sopenharmony_ci
225662306a36Sopenharmony_cierr_disable_pm_runtime:
225762306a36Sopenharmony_ci	pm_runtime_disable(&pdev->dev);
225862306a36Sopenharmony_ci	vop_destroy_crtc(vop);
225962306a36Sopenharmony_ci	return ret;
226062306a36Sopenharmony_ci}
226162306a36Sopenharmony_ci
226262306a36Sopenharmony_cistatic void vop_unbind(struct device *dev, struct device *master, void *data)
226362306a36Sopenharmony_ci{
226462306a36Sopenharmony_ci	struct vop *vop = dev_get_drvdata(dev);
226562306a36Sopenharmony_ci
226662306a36Sopenharmony_ci	if (vop->rgb)
226762306a36Sopenharmony_ci		rockchip_rgb_fini(vop->rgb);
226862306a36Sopenharmony_ci
226962306a36Sopenharmony_ci	pm_runtime_disable(dev);
227062306a36Sopenharmony_ci	vop_destroy_crtc(vop);
227162306a36Sopenharmony_ci
227262306a36Sopenharmony_ci	clk_unprepare(vop->aclk);
227362306a36Sopenharmony_ci	clk_unprepare(vop->hclk);
227462306a36Sopenharmony_ci	clk_unprepare(vop->dclk);
227562306a36Sopenharmony_ci}
227662306a36Sopenharmony_ci
227762306a36Sopenharmony_ciconst struct component_ops vop_component_ops = {
227862306a36Sopenharmony_ci	.bind = vop_bind,
227962306a36Sopenharmony_ci	.unbind = vop_unbind,
228062306a36Sopenharmony_ci};
228162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(vop_component_ops);
2282