xref: /rk3399_rockchip-uboot/drivers/video/drm/rockchip_vop.c (revision b899f9cc52a89a296bb7fb20ae923e9dd77b13a8)
1186f8572SMark Yao /*
2186f8572SMark Yao  * (C) Copyright 2008-2017 Fuzhou Rockchip Electronics Co., Ltd
3186f8572SMark Yao  *
4186f8572SMark Yao  * SPDX-License-Identifier:	GPL-2.0+
5186f8572SMark Yao  */
6186f8572SMark Yao 
7186f8572SMark Yao #include <config.h>
8186f8572SMark Yao #include <common.h>
9186f8572SMark Yao #include <errno.h>
10186f8572SMark Yao #include <malloc.h>
11186f8572SMark Yao #include <fdtdec.h>
12186f8572SMark Yao #include <fdt_support.h>
13186f8572SMark Yao #include <asm/unaligned.h>
14186f8572SMark Yao #include <asm/io.h>
15186f8572SMark Yao #include <linux/list.h>
16186f8572SMark Yao #include <linux/media-bus-format.h>
170a0e1dcdSDamon Ding #include <linux/iopoll.h>
18186f8572SMark Yao #include <clk.h>
19186f8572SMark Yao #include <asm/arch/clock.h>
20186f8572SMark Yao #include <linux/err.h>
21e2bce6e4SKever Yang #include <dm/device.h>
22e2bce6e4SKever Yang #include <dm/read.h>
233a06149eSSandy Huang #include <syscon.h>
24fc8b0d66SDamon Ding #include <regmap.h>
25186f8572SMark Yao 
26186f8572SMark Yao #include "rockchip_display.h"
27186f8572SMark Yao #include "rockchip_crtc.h"
28186f8572SMark Yao #include "rockchip_connector.h"
29186f8572SMark Yao #include "rockchip_vop.h"
30186f8572SMark Yao 
us_to_vertical_line(struct drm_display_mode * mode,int us)31186f8572SMark Yao static inline int us_to_vertical_line(struct drm_display_mode *mode, int us)
32186f8572SMark Yao {
33186f8572SMark Yao 	return us * mode->clock / mode->htotal / 1000;
34186f8572SMark Yao }
354b8c2ef1SMark Yao 
set_vop_mcu_rs(struct vop * vop,int v)366b898587SDamon Ding static inline void set_vop_mcu_rs(struct vop *vop, int v)
376b898587SDamon Ding {
386b898587SDamon Ding 	if (dm_gpio_is_valid(&vop->mcu_rs_gpio))
396b898587SDamon Ding 		dm_gpio_set_value(&vop->mcu_rs_gpio, v);
406b898587SDamon Ding 	else
416b898587SDamon Ding 		VOP_CTRL_SET(vop, mcu_rs, v);
426b898587SDamon Ding }
436b898587SDamon Ding 
to_vop_csc_mode(enum drm_color_encoding color_encoding,enum drm_color_range color_range)44df0a5c43SDamon Ding static enum vop_csc_format to_vop_csc_mode(enum drm_color_encoding color_encoding,
45df0a5c43SDamon Ding 					   enum drm_color_range color_range)
4679feefb1SSandy Huang {
47df0a5c43SDamon Ding 	bool full_range = color_range == DRM_COLOR_YCBCR_FULL_RANGE ? 1 : 0;
48df0a5c43SDamon Ding 	enum vop_csc_format csc_mode = CSC_BT709L;
49df0a5c43SDamon Ding 
50df0a5c43SDamon Ding 	switch (color_encoding) {
51df0a5c43SDamon Ding 	case DRM_COLOR_YCBCR_BT601:
52df0a5c43SDamon Ding 		if (full_range)
53df0a5c43SDamon Ding 			csc_mode = CSC_BT601F;
54df0a5c43SDamon Ding 		else
55df0a5c43SDamon Ding 			csc_mode = CSC_BT601L;
56df0a5c43SDamon Ding 		break;
57df0a5c43SDamon Ding 
58df0a5c43SDamon Ding 	case DRM_COLOR_YCBCR_BT709:
59df0a5c43SDamon Ding 		if (full_range) {
60df0a5c43SDamon Ding 			csc_mode = CSC_BT601F;
61df0a5c43SDamon Ding 			printf("Unsupported bt709f at 10bit csc depth, use bt601f instead\n");
62df0a5c43SDamon Ding 		} else {
63df0a5c43SDamon Ding 			csc_mode = CSC_BT709L;
6479feefb1SSandy Huang 		}
65df0a5c43SDamon Ding 		break;
66df0a5c43SDamon Ding 
67df0a5c43SDamon Ding 	case DRM_COLOR_YCBCR_BT2020:
68df0a5c43SDamon Ding 		if (full_range) {
69df0a5c43SDamon Ding 			csc_mode = CSC_BT601F;
70df0a5c43SDamon Ding 			printf("Unsupported bt2020f at 10bit csc depth, use bt601f instead\n");
71df0a5c43SDamon Ding 		} else {
72df0a5c43SDamon Ding 			csc_mode = CSC_BT2020L;
73df0a5c43SDamon Ding 		}
74df0a5c43SDamon Ding 		break;
75df0a5c43SDamon Ding 
76df0a5c43SDamon Ding 	default:
77df0a5c43SDamon Ding 		printf("Unsuport color_encoding:%d\n", color_encoding);
78df0a5c43SDamon Ding 	}
79df0a5c43SDamon Ding 
80df0a5c43SDamon Ding 	return csc_mode;
8179feefb1SSandy Huang }
8279feefb1SSandy Huang 
is_yuv_output(uint32_t bus_format)8379feefb1SSandy Huang static bool is_yuv_output(uint32_t bus_format)
8479feefb1SSandy Huang {
8579feefb1SSandy Huang 	switch (bus_format) {
8679feefb1SSandy Huang 	case MEDIA_BUS_FMT_YUV8_1X24:
8779feefb1SSandy Huang 	case MEDIA_BUS_FMT_YUV10_1X30:
8879feefb1SSandy Huang 	case MEDIA_BUS_FMT_UYYVYY8_0_5X24:
8979feefb1SSandy Huang 	case MEDIA_BUS_FMT_UYYVYY10_0_5X30:
90f721bd04SDamon Ding 	case MEDIA_BUS_FMT_YUYV8_2X8:
91f721bd04SDamon Ding 	case MEDIA_BUS_FMT_YVYU8_2X8:
92f721bd04SDamon Ding 	case MEDIA_BUS_FMT_UYVY8_2X8:
93f721bd04SDamon Ding 	case MEDIA_BUS_FMT_VYUY8_2X8:
94f721bd04SDamon Ding 	case MEDIA_BUS_FMT_YUYV8_1X16:
95f721bd04SDamon Ding 	case MEDIA_BUS_FMT_YVYU8_1X16:
96f721bd04SDamon Ding 	case MEDIA_BUS_FMT_UYVY8_1X16:
97f721bd04SDamon Ding 	case MEDIA_BUS_FMT_VYUY8_1X16:
9879feefb1SSandy Huang 		return true;
9979feefb1SSandy Huang 	default:
10079feefb1SSandy Huang 		return false;
10179feefb1SSandy Huang 	}
10279feefb1SSandy Huang }
10379feefb1SSandy Huang 
is_uv_swap(uint32_t bus_format,uint32_t output_mode)10479feefb1SSandy Huang static bool is_uv_swap(uint32_t bus_format, uint32_t output_mode)
10579feefb1SSandy Huang {
10679feefb1SSandy Huang 	/*
10779feefb1SSandy Huang 	 * FIXME:
10879feefb1SSandy Huang 	 *
10979feefb1SSandy Huang 	 * There is no media type for YUV444 output,
11079feefb1SSandy Huang 	 * so when out_mode is AAAA or P888, assume output is YUV444 on
11179feefb1SSandy Huang 	 * yuv format.
11279feefb1SSandy Huang 	 *
11379feefb1SSandy Huang 	 * From H/W testing, YUV444 mode need a rb swap.
11479feefb1SSandy Huang 	 */
11579feefb1SSandy Huang 	if ((bus_format == MEDIA_BUS_FMT_YUV8_1X24 ||
11679feefb1SSandy Huang 	     bus_format == MEDIA_BUS_FMT_YUV10_1X30) &&
11779feefb1SSandy Huang 	    (output_mode == ROCKCHIP_OUT_MODE_AAAA ||
11879feefb1SSandy Huang 	     output_mode == ROCKCHIP_OUT_MODE_P888))
11979feefb1SSandy Huang 		return true;
12079feefb1SSandy Huang 	else
12179feefb1SSandy Huang 		return false;
12279feefb1SSandy Huang }
12379feefb1SSandy Huang 
is_rb_swap(uint32_t bus_format,uint32_t output_mode)12480fbe788SDamon Ding static bool is_rb_swap(uint32_t bus_format, uint32_t output_mode)
12580fbe788SDamon Ding {
12680fbe788SDamon Ding 	/*
12780fbe788SDamon Ding 	 * The default component order of serial rgb3x8 formats
12880fbe788SDamon Ding 	 * is BGR. So it is needed to enable RB swap.
12980fbe788SDamon Ding 	 */
130b7b383ebSDamon Ding 	if (bus_format == MEDIA_BUS_FMT_RGB888_3X8 ||
131b7b383ebSDamon Ding 	    bus_format == MEDIA_BUS_FMT_RGB888_DUMMY_4X8)
13280fbe788SDamon Ding 		return true;
13380fbe788SDamon Ding 	else
13480fbe788SDamon Ding 		return false;
13580fbe788SDamon Ding }
13680fbe788SDamon Ding 
is_yc_swap(uint32_t bus_format)1370c4bb35cSDamon Ding static bool is_yc_swap(uint32_t bus_format)
1380c4bb35cSDamon Ding {
1390c4bb35cSDamon Ding 	switch (bus_format) {
1400c4bb35cSDamon Ding 	case MEDIA_BUS_FMT_YUYV8_1X16:
1410c4bb35cSDamon Ding 	case MEDIA_BUS_FMT_YVYU8_1X16:
1420c4bb35cSDamon Ding 	case MEDIA_BUS_FMT_YUYV8_2X8:
1430c4bb35cSDamon Ding 	case MEDIA_BUS_FMT_YVYU8_2X8:
1440c4bb35cSDamon Ding 		return true;
1450c4bb35cSDamon Ding 	default:
1460c4bb35cSDamon Ding 		return false;
1470c4bb35cSDamon Ding 	}
1480c4bb35cSDamon Ding }
1490c4bb35cSDamon Ding 
rockchip_vop_init_gamma(struct vop * vop,struct display_state * state)150186f8572SMark Yao static int rockchip_vop_init_gamma(struct vop *vop, struct display_state *state)
151186f8572SMark Yao {
152186f8572SMark Yao 	struct crtc_state *crtc_state = &state->crtc_state;
153186f8572SMark Yao 	struct connector_state *conn_state = &state->conn_state;
154186f8572SMark Yao 	u32 *lut = conn_state->gamma.lut;
155186f8572SMark Yao 	fdt_size_t lut_size;
156186f8572SMark Yao 	int i, lut_len;
157186f8572SMark Yao 	u32 *lut_regs;
158186f8572SMark Yao 
159186f8572SMark Yao 	if (!conn_state->gamma.lut)
160186f8572SMark Yao 		return 0;
161186f8572SMark Yao 
162e2bce6e4SKever Yang 	i = dev_read_stringlist_search(crtc_state->dev, "reg-names", "gamma_lut");
163186f8572SMark Yao 	if (i < 0) {
164186f8572SMark Yao 		printf("Warning: vop not support gamma\n");
165186f8572SMark Yao 		return 0;
166186f8572SMark Yao 	}
167e2bce6e4SKever Yang 	lut_regs = (u32 *)dev_read_addr_size(crtc_state->dev, "reg", &lut_size);
168186f8572SMark Yao 	if (lut_regs == (u32 *)FDT_ADDR_T_NONE) {
169186f8572SMark Yao 		printf("failed to get gamma lut register\n");
170186f8572SMark Yao 		return 0;
171186f8572SMark Yao 	}
172186f8572SMark Yao 	lut_len = lut_size / 4;
173186f8572SMark Yao 	if (lut_len != 256 && lut_len != 1024) {
174186f8572SMark Yao 		printf("Warning: unsupport gamma lut table[%d]\n", lut_len);
175186f8572SMark Yao 		return 0;
176186f8572SMark Yao 	}
177186f8572SMark Yao 
178186f8572SMark Yao 	if (conn_state->gamma.size != lut_len) {
179186f8572SMark Yao 		int size = conn_state->gamma.size;
180186f8572SMark Yao 		u32 j, r, g, b, color;
181186f8572SMark Yao 
182186f8572SMark Yao 		for (i = 0; i < lut_len; i++) {
183186f8572SMark Yao 			j = i * size / lut_len;
184186f8572SMark Yao 			r = lut[j] / size / size * lut_len / size;
185186f8572SMark Yao 			g = lut[j] / size % size * lut_len / size;
186186f8572SMark Yao 			b = lut[j] % size * lut_len / size;
187186f8572SMark Yao 			color = r * lut_len * lut_len + g * lut_len + b;
188186f8572SMark Yao 
189186f8572SMark Yao 			writel(color, lut_regs + (i << 2));
190186f8572SMark Yao 		}
191186f8572SMark Yao 	} else {
192186f8572SMark Yao 		for (i = 0; i < lut_len; i++)
193186f8572SMark Yao 			writel(lut[i], lut_regs + (i << 2));
194186f8572SMark Yao 	}
195186f8572SMark Yao 
196186f8572SMark Yao 	VOP_CTRL_SET(vop, dsp_lut_en, 1);
197186f8572SMark Yao 	VOP_CTRL_SET(vop, update_gamma_lut, 1);
198186f8572SMark Yao 
199186f8572SMark Yao 	return 0;
200186f8572SMark Yao }
201186f8572SMark Yao 
vop_post_config(struct display_state * state,struct vop * vop)2028a2a3a29SSandy Huang static void vop_post_config(struct display_state *state, struct vop *vop)
2038a2a3a29SSandy Huang {
2048a2a3a29SSandy Huang 	struct connector_state *conn_state = &state->conn_state;
2058a2a3a29SSandy Huang 	struct drm_display_mode *mode = &conn_state->mode;
2068a2a3a29SSandy Huang 	u16 vtotal = mode->crtc_vtotal;
2078a2a3a29SSandy Huang 	u16 hact_st = mode->crtc_htotal - mode->crtc_hsync_start;
2088a2a3a29SSandy Huang 	u16 vact_st = mode->crtc_vtotal - mode->crtc_vsync_start;
2098a2a3a29SSandy Huang 	u16 hdisplay = mode->crtc_hdisplay;
2108a2a3a29SSandy Huang 	u16 vdisplay = mode->crtc_vdisplay;
2118a2a3a29SSandy Huang 	u16 hsize = hdisplay * (conn_state->overscan.left_margin + conn_state->overscan.right_margin) / 200;
2128a2a3a29SSandy Huang 	u16 vsize = vdisplay * (conn_state->overscan.top_margin + conn_state->overscan.bottom_margin) / 200;
2138a2a3a29SSandy Huang 	u16 hact_end, vact_end;
2148a2a3a29SSandy Huang 	u32 val;
2158a2a3a29SSandy Huang 
2168a2a3a29SSandy Huang 	if (mode->flags & DRM_MODE_FLAG_INTERLACE)
2178a2a3a29SSandy Huang 		vsize = round_down(vsize, 2);
2188a2a3a29SSandy Huang 
2198a2a3a29SSandy Huang 	hact_st += hdisplay * (100 - conn_state->overscan.left_margin) / 200;
2208a2a3a29SSandy Huang 	hact_end = hact_st + hsize;
2218a2a3a29SSandy Huang 	val = hact_st << 16;
2228a2a3a29SSandy Huang 	val |= hact_end;
2238a2a3a29SSandy Huang 
2248a2a3a29SSandy Huang 	VOP_CTRL_SET(vop, hpost_st_end, val);
2258a2a3a29SSandy Huang 	vact_st += vdisplay * (100 - conn_state->overscan.top_margin) / 200;
2268a2a3a29SSandy Huang 	vact_end = vact_st + vsize;
2278a2a3a29SSandy Huang 	val = vact_st << 16;
2288a2a3a29SSandy Huang 	val |= vact_end;
2298a2a3a29SSandy Huang 	VOP_CTRL_SET(vop, vpost_st_end, val);
2308a2a3a29SSandy Huang 	val = scl_cal_scale2(vdisplay, vsize) << 16;
2318a2a3a29SSandy Huang 	val |= scl_cal_scale2(hdisplay, hsize);
2328a2a3a29SSandy Huang 	VOP_CTRL_SET(vop, post_scl_factor, val);
2338a2a3a29SSandy Huang #define POST_HORIZONTAL_SCALEDOWN_EN(x)		((x) << 0)
2348a2a3a29SSandy Huang #define POST_VERTICAL_SCALEDOWN_EN(x)		((x) << 1)
2358a2a3a29SSandy Huang 	VOP_CTRL_SET(vop, post_scl_ctrl,
2368a2a3a29SSandy Huang 		     POST_HORIZONTAL_SCALEDOWN_EN(hdisplay != hsize) |
2378a2a3a29SSandy Huang 		     POST_VERTICAL_SCALEDOWN_EN(vdisplay != vsize));
2388a2a3a29SSandy Huang 	if (mode->flags & DRM_MODE_FLAG_INTERLACE) {
2398a2a3a29SSandy Huang 		u16 vact_st_f1 = vtotal + vact_st + 1;
2408a2a3a29SSandy Huang 		u16 vact_end_f1 = vact_st_f1 + vsize;
2418a2a3a29SSandy Huang 
2428a2a3a29SSandy Huang 		val = vact_st_f1 << 16 | vact_end_f1;
2438a2a3a29SSandy Huang 		VOP_CTRL_SET(vop, vpost_st_end_f1, val);
2448a2a3a29SSandy Huang 	}
2458a2a3a29SSandy Huang }
2468a2a3a29SSandy Huang 
vop_mcu_bypass_mode_setup(struct display_state * state,struct vop * vop)24772f233a9SDamon Ding static void vop_mcu_bypass_mode_setup(struct display_state *state, struct vop *vop)
24872f233a9SDamon Ding {
24972f233a9SDamon Ding 	/*
25072f233a9SDamon Ding 	 * If mcu_hold_mode is 1, set 1 to mcu_frame_st will
25172f233a9SDamon Ding 	 * refresh one frame from ddr. So mcu_frame_st is needed
25272f233a9SDamon Ding 	 * to be initialized as 0.
25372f233a9SDamon Ding 	 */
25472f233a9SDamon Ding 	VOP_CTRL_SET(vop, mcu_frame_st, 0);
25572f233a9SDamon Ding 	VOP_CTRL_SET(vop, mcu_clk_sel, 1);
25672f233a9SDamon Ding 	VOP_CTRL_SET(vop, mcu_type, 1);
25772f233a9SDamon Ding 
25872f233a9SDamon Ding 	VOP_CTRL_SET(vop, mcu_hold_mode, 1);
25972f233a9SDamon Ding 	VOP_CTRL_SET(vop, mcu_pix_total, 53);
26072f233a9SDamon Ding 	VOP_CTRL_SET(vop, mcu_cs_pst, 6);
26172f233a9SDamon Ding 	VOP_CTRL_SET(vop, mcu_cs_pend, 48);
26272f233a9SDamon Ding 	VOP_CTRL_SET(vop, mcu_rw_pst, 12);
26372f233a9SDamon Ding 	VOP_CTRL_SET(vop, mcu_rw_pend, 30);
26472f233a9SDamon Ding }
26572f233a9SDamon Ding 
vop_mcu_mode_setup(struct display_state * state,struct vop * vop)26672f233a9SDamon Ding static void vop_mcu_mode_setup(struct display_state *state, struct vop *vop)
26767b9012cSSandy Huang {
26867b9012cSSandy Huang 	struct crtc_state *crtc_state = &state->crtc_state;
26967b9012cSSandy Huang 
27072f233a9SDamon Ding 	/*
27172f233a9SDamon Ding 	 * If mcu_hold_mode is 1, set 1 to mcu_frame_st will
27272f233a9SDamon Ding 	 * refresh one frame from ddr. So mcu_frame_st is needed
27372f233a9SDamon Ding 	 * to be initialized as 0.
27472f233a9SDamon Ding 	 */
27572f233a9SDamon Ding 	VOP_CTRL_SET(vop, mcu_frame_st, 0);
27667b9012cSSandy Huang 	VOP_CTRL_SET(vop, mcu_clk_sel, 1);
27767b9012cSSandy Huang 	VOP_CTRL_SET(vop, mcu_type, 1);
27867b9012cSSandy Huang 
27967b9012cSSandy Huang 	VOP_CTRL_SET(vop, mcu_hold_mode, 1);
28067b9012cSSandy Huang 	VOP_CTRL_SET(vop, mcu_pix_total, crtc_state->mcu_timing.mcu_pix_total);
28167b9012cSSandy Huang 	VOP_CTRL_SET(vop, mcu_cs_pst, crtc_state->mcu_timing.mcu_cs_pst);
28267b9012cSSandy Huang 	VOP_CTRL_SET(vop, mcu_cs_pend, crtc_state->mcu_timing.mcu_cs_pend);
28367b9012cSSandy Huang 	VOP_CTRL_SET(vop, mcu_rw_pst, crtc_state->mcu_timing.mcu_rw_pst);
28467b9012cSSandy Huang 	VOP_CTRL_SET(vop, mcu_rw_pend, crtc_state->mcu_timing.mcu_rw_pend);
28567b9012cSSandy Huang }
28667b9012cSSandy Huang 
rockchip_vop_preinit(struct display_state * state)287cf53642aSSandy Huang static int rockchip_vop_preinit(struct display_state *state)
288cf53642aSSandy Huang {
289cf53642aSSandy Huang 	const struct vop_data *vop_data = state->crtc_state.crtc->data;
290cf53642aSSandy Huang 
291cf53642aSSandy Huang 	state->crtc_state.max_output = vop_data->max_output;
292cf53642aSSandy Huang 
293cf53642aSSandy Huang 	return 0;
294cf53642aSSandy Huang }
295cf53642aSSandy Huang 
vop_mode_done(struct vop * vop)2960a0e1dcdSDamon Ding static u32 vop_mode_done(struct vop *vop)
2970a0e1dcdSDamon Ding {
2980a0e1dcdSDamon Ding 	return VOP_CTRL_GET(vop, out_mode);
2990a0e1dcdSDamon Ding }
3000a0e1dcdSDamon Ding 
vop_set_out_mode(struct vop * vop,u32 mode)3010a0e1dcdSDamon Ding static void vop_set_out_mode(struct vop *vop, u32 mode)
3020a0e1dcdSDamon Ding {
3030a0e1dcdSDamon Ding 	int ret;
3040a0e1dcdSDamon Ding 	u32 val;
3050a0e1dcdSDamon Ding 
3060a0e1dcdSDamon Ding 	VOP_CTRL_SET(vop, out_mode, mode);
3070a0e1dcdSDamon Ding 	vop_cfg_done(vop);
3080a0e1dcdSDamon Ding 	ret = readx_poll_timeout(vop_mode_done, vop, val, val == mode, 1000 * 1000);
3090a0e1dcdSDamon Ding 	if (ret)
3100a0e1dcdSDamon Ding 		printf("wait for setting mode 0x%x timeout\n", mode);
3110a0e1dcdSDamon Ding }
3120a0e1dcdSDamon Ding 
rockchip_vop_init(struct display_state * state)313186f8572SMark Yao static int rockchip_vop_init(struct display_state *state)
314186f8572SMark Yao {
315186f8572SMark Yao 	struct crtc_state *crtc_state = &state->crtc_state;
316186f8572SMark Yao 	struct connector_state *conn_state = &state->conn_state;
317186f8572SMark Yao 	struct drm_display_mode *mode = &conn_state->mode;
318186f8572SMark Yao 	const struct rockchip_crtc *crtc = crtc_state->crtc;
319186f8572SMark Yao 	const struct vop_data *vop_data = crtc->data;
320186f8572SMark Yao 	struct vop *vop;
321fc8b0d66SDamon Ding 	struct regmap *map;
322ccd843b9SSandy Huang 	u16 hsync_len = mode->crtc_hsync_end - mode->crtc_hsync_start;
323ccd843b9SSandy Huang 	u16 hdisplay = mode->crtc_hdisplay;
324ccd843b9SSandy Huang 	u16 htotal = mode->crtc_htotal;
325ccd843b9SSandy Huang 	u16 hact_st = mode->crtc_htotal - mode->crtc_hsync_start;
326186f8572SMark Yao 	u16 hact_end = hact_st + hdisplay;
327ccd843b9SSandy Huang 	u16 vdisplay = mode->crtc_vdisplay;
328ccd843b9SSandy Huang 	u16 vtotal = mode->crtc_vtotal;
329ccd843b9SSandy Huang 	u16 vsync_len = mode->crtc_vsync_end - mode->crtc_vsync_start;
330ccd843b9SSandy Huang 	u16 vact_st = mode->crtc_vtotal - mode->crtc_vsync_start;
331186f8572SMark Yao 	u16 vact_end = vact_st + vdisplay;
332960080c9SSandy Huang 	u32 val, act_end;
3334b8c2ef1SMark Yao 	int ret;
33479feefb1SSandy Huang 	bool yuv_overlay = false, post_r2y_en = false, post_y2r_en = false;
33579feefb1SSandy Huang 	u16 post_csc_mode;
3360c4bb35cSDamon Ding 	bool dclk_inv, yc_swap = false;
3372264c88bSDamon Ding 	char output_type_name[30] = {0};
338186f8572SMark Yao 
339186f8572SMark Yao 	vop = malloc(sizeof(*vop));
340186f8572SMark Yao 	if (!vop)
341186f8572SMark Yao 		return -ENOMEM;
342186f8572SMark Yao 	memset(vop, 0, sizeof(*vop));
343186f8572SMark Yao 
344186f8572SMark Yao 	crtc_state->private = vop;
345fc8b0d66SDamon Ding 	vop->data = vop_data;
346e2bce6e4SKever Yang 	vop->regs = dev_read_addr_ptr(crtc_state->dev);
347186f8572SMark Yao 	vop->regsbak = malloc(vop_data->reg_len);
348186f8572SMark Yao 	vop->win = vop_data->win;
349186f8572SMark Yao 	vop->win_offset = vop_data->win_offset;
350186f8572SMark Yao 	vop->ctrl = vop_data->ctrl;
3513a06149eSSandy Huang 
352fc8b0d66SDamon Ding 	map = syscon_regmap_lookup_by_phandle(crtc_state->dev, "rockchip,grf");
353e9058b80SDamon Ding 	if (!IS_ERR_OR_NULL(map)) {
354fc8b0d66SDamon Ding 		vop->grf_ctrl = regmap_get_range(map, 0);
355fc8b0d66SDamon Ding 		if (vop->grf_ctrl <= 0)
356fc8b0d66SDamon Ding 			printf("%s: Get syscon grf failed (ret=%p)\n", __func__, vop->grf_ctrl);
357e9058b80SDamon Ding 	}
35872f233a9SDamon Ding 	map = syscon_regmap_lookup_by_phandle(crtc_state->dev, "rockchip,vo0-grf");
359e9058b80SDamon Ding 	if (!IS_ERR_OR_NULL(map)) {
36072f233a9SDamon Ding 		vop->vo0_grf_ctrl = regmap_get_range(map, 0);
36172f233a9SDamon Ding 		if (vop->vo0_grf_ctrl <= 0)
36272f233a9SDamon Ding 			printf("%s: Get syscon vo0_grf failed (ret=%p)\n", __func__, vop->vo0_grf_ctrl);
363e9058b80SDamon Ding 	}
364fc8b0d66SDamon Ding 
365186f8572SMark Yao 	vop->line_flag = vop_data->line_flag;
366b7618fd3SSandy Huang 	vop->csc_table = vop_data->csc_table;
367b7618fd3SSandy Huang 	vop->win_csc = vop_data->win_csc;
368186f8572SMark Yao 	vop->version = vop_data->version;
369186f8572SMark Yao 
3702264c88bSDamon Ding 	printf("VOP:0x%8p update mode to: %dx%d%s%d, type:%s\n",
3712264c88bSDamon Ding 	       vop->regs, mode->crtc_hdisplay, mode->vdisplay,
3722264c88bSDamon Ding 	       mode->flags & DRM_MODE_FLAG_INTERLACE ? "i" : "p",
373e32f1beaSDamon Ding 	       drm_mode_vrefresh(mode),
3742264c88bSDamon Ding 	       rockchip_get_output_if_name(conn_state->output_if, output_type_name));
3752264c88bSDamon Ding 
376cadc8d74SKever Yang 	/* Process 'assigned-{clocks/clock-parents/clock-rates}' properties */
377cadc8d74SKever Yang 	ret = clk_set_defaults(crtc_state->dev);
378cadc8d74SKever Yang 	if (ret)
379cadc8d74SKever Yang 		debug("%s clk_set_defaults failed %d\n", __func__, ret);
380186f8572SMark Yao 
38172f233a9SDamon Ding 	ret = clk_get_by_name(crtc_state->dev, "dclk_vop", &crtc_state->dclk);
382186f8572SMark Yao 	if (!ret)
38372f233a9SDamon Ding 		ret = clk_set_rate(&crtc_state->dclk, mode->crtc_clock * 1000);
384186f8572SMark Yao 	if (IS_ERR_VALUE(ret)) {
3854b8c2ef1SMark Yao 		printf("%s: Failed to set dclk: ret=%d\n", __func__, ret);
386186f8572SMark Yao 		return ret;
387186f8572SMark Yao 	}
388ddef0aacSDamon Ding 	printf("VOP:0x%8p set crtc_clock to %dKHz, get %dHz\n", vop->regs, mode->crtc_clock, ret);
389186f8572SMark Yao 
390186f8572SMark Yao 	memcpy(vop->regsbak, vop->regs, vop_data->reg_len);
391186f8572SMark Yao 
3924b8c2ef1SMark Yao 	rockchip_vop_init_gamma(vop, state);
393186f8572SMark Yao 
3946b898587SDamon Ding 	ret = gpio_request_by_name(crtc_state->dev, "mcu-rs-gpios",
3956b898587SDamon Ding 				   0, &vop->mcu_rs_gpio, GPIOD_IS_OUT);
3966b898587SDamon Ding 	if (ret && ret != -ENOENT)
3976b898587SDamon Ding 		printf("%s: Cannot get mcu rs GPIO: %d\n", __func__, ret);
3986b898587SDamon Ding 
399186f8572SMark Yao 	VOP_CTRL_SET(vop, global_regdone_en, 1);
4002b34f307SMark Yao 	VOP_CTRL_SET(vop, axi_outstanding_max_num, 30);
4012b34f307SMark Yao 	VOP_CTRL_SET(vop, axi_max_outstanding_en, 1);
402b0dbe9a0SSandy Huang 	VOP_CTRL_SET(vop, reg_done_frm, 1);
403186f8572SMark Yao 	VOP_CTRL_SET(vop, win_gate[0], 1);
404186f8572SMark Yao 	VOP_CTRL_SET(vop, win_gate[1], 1);
4052b34f307SMark Yao 	VOP_CTRL_SET(vop, win_channel[0], 0x12);
4062b34f307SMark Yao 	VOP_CTRL_SET(vop, win_channel[1], 0x34);
4072b34f307SMark Yao 	VOP_CTRL_SET(vop, win_channel[2], 0x56);
408186f8572SMark Yao 	VOP_CTRL_SET(vop, dsp_blank, 0);
409186f8572SMark Yao 
410de3f896fSChaoyi Chen 	if (vop->version == VOP_VERSION_RK3576_LITE) {
41172f233a9SDamon Ding 		VOP_GRF_SET(vop, grf_ctrl, grf_vopl_sel, 1);
41272f233a9SDamon Ding 		VOP_CTRL_SET(vop, enable, 1);
41372f233a9SDamon Ding 	}
41472f233a9SDamon Ding 
41513f658dcSDamon Ding 	dclk_inv = (conn_state->bus_flags & DRM_BUS_FLAG_PIXDATA_DRIVE_NEGEDGE) ? 1 : 0;
41654f7137bSDamon Ding 	/* For improving signal quality, dclk need to be inverted by default on rv1106. */
417de3f896fSChaoyi Chen 	if (vop->version == VOP_VERSION_RV1106)
41854f7137bSDamon Ding 		dclk_inv = !dclk_inv;
4193a06149eSSandy Huang 	VOP_CTRL_SET(vop, dclk_pol, dclk_inv);
4203a06149eSSandy Huang 
421186f8572SMark Yao 	val = 0x8;
422186f8572SMark Yao 	val |= (mode->flags & DRM_MODE_FLAG_NHSYNC) ? 0 : 1;
423186f8572SMark Yao 	val |= (mode->flags & DRM_MODE_FLAG_NVSYNC) ? 0 : (1 << 1);
424186f8572SMark Yao 	VOP_CTRL_SET(vop, pin_pol, val);
425186f8572SMark Yao 
426186f8572SMark Yao 	switch (conn_state->type) {
427186f8572SMark Yao 	case DRM_MODE_CONNECTOR_LVDS:
428186f8572SMark Yao 		VOP_CTRL_SET(vop, rgb_en, 1);
429186f8572SMark Yao 		VOP_CTRL_SET(vop, rgb_pin_pol, val);
4303a06149eSSandy Huang 		VOP_CTRL_SET(vop, rgb_dclk_pol, dclk_inv);
4313a06149eSSandy Huang 		VOP_CTRL_SET(vop, lvds_en, 1);
4323a06149eSSandy Huang 		VOP_CTRL_SET(vop, lvds_pin_pol, val);
4333a06149eSSandy Huang 		VOP_CTRL_SET(vop, lvds_dclk_pol, dclk_inv);
434fc8b0d66SDamon Ding 		if (!IS_ERR_OR_NULL(vop->grf_ctrl))
435fc8b0d66SDamon Ding 			VOP_GRF_SET(vop, grf_ctrl, grf_dclk_inv, dclk_inv);
4360c4bb35cSDamon Ding 		if (conn_state->output_if & VOP_OUTPUT_IF_BT1120) {
4370c4bb35cSDamon Ding 			VOP_CTRL_SET(vop, bt1120_en, 1);
4380c4bb35cSDamon Ding 			yc_swap = is_yc_swap(conn_state->bus_format);
4390c4bb35cSDamon Ding 			VOP_CTRL_SET(vop, bt1120_yc_swap, yc_swap);
4400c4bb35cSDamon Ding 			VOP_CTRL_SET(vop, yuv_clip, 1);
4410c4bb35cSDamon Ding 		} else if (conn_state->output_if & VOP_OUTPUT_IF_BT656) {
4420c4bb35cSDamon Ding 			VOP_CTRL_SET(vop, bt656_en, 1);
4430c4bb35cSDamon Ding 			yc_swap = is_yc_swap(conn_state->bus_format);
4440c4bb35cSDamon Ding 			VOP_CTRL_SET(vop, bt1120_yc_swap, yc_swap);
4450c4bb35cSDamon Ding 		}
446186f8572SMark Yao 		break;
447186f8572SMark Yao 	case DRM_MODE_CONNECTOR_eDP:
448186f8572SMark Yao 		VOP_CTRL_SET(vop, edp_en, 1);
449186f8572SMark Yao 		VOP_CTRL_SET(vop, edp_pin_pol, val);
4503a06149eSSandy Huang 		VOP_CTRL_SET(vop, edp_dclk_pol, dclk_inv);
45172f233a9SDamon Ding 		VOP_CTRL_SET(vop, inf_out_en, 1);
45249d669f5SDamon Ding 		VOP_CTRL_SET(vop, out_dresetn, 1);
45372f233a9SDamon Ding 		VOP_GRF_SET(vop, vo0_grf_ctrl, grf_edp_ch_sel, 1);
454186f8572SMark Yao 		break;
455186f8572SMark Yao 	case DRM_MODE_CONNECTOR_HDMIA:
456186f8572SMark Yao 		VOP_CTRL_SET(vop, hdmi_en, 1);
457186f8572SMark Yao 		VOP_CTRL_SET(vop, hdmi_pin_pol, val);
4583a06149eSSandy Huang 		VOP_CTRL_SET(vop, hdmi_dclk_pol, 1);
45972f233a9SDamon Ding 		VOP_CTRL_SET(vop, inf_out_en, 1);
46049d669f5SDamon Ding 		VOP_CTRL_SET(vop, out_dresetn, 1);
46172f233a9SDamon Ding 		VOP_GRF_SET(vop, vo0_grf_ctrl, grf_hdmi_ch_sel, 1);
46272f233a9SDamon Ding 		VOP_GRF_SET(vop, vo0_grf_ctrl, grf_hdmi_pin_pol, val);
46349d669f5SDamon Ding 		VOP_GRF_SET(vop, vo0_grf_ctrl, grf_hdmi_1to4_en, 1);
464186f8572SMark Yao 		break;
465186f8572SMark Yao 	case DRM_MODE_CONNECTOR_DSI:
46672f233a9SDamon Ding 		/*
46772f233a9SDamon Ding 		 * RK3576 DSI CTRL hsync/vsync polarity is positive and can't update,
46872f233a9SDamon Ding 		 * so set VOP hsync/vsync polarity as positive by default.
46972f233a9SDamon Ding 		 */
470de3f896fSChaoyi Chen 		if (vop->version == VOP_VERSION_RK3576_LITE)
47172f233a9SDamon Ding 			val = BIT(HSYNC_POSITIVE) | BIT(VSYNC_POSITIVE);
472186f8572SMark Yao 		VOP_CTRL_SET(vop, mipi_en, 1);
473186f8572SMark Yao 		VOP_CTRL_SET(vop, mipi_pin_pol, val);
4743a06149eSSandy Huang 		VOP_CTRL_SET(vop, mipi_dclk_pol, dclk_inv);
475186f8572SMark Yao 		VOP_CTRL_SET(vop, mipi_dual_channel_en,
476bee25ee6SGuochun Huang 			!!(conn_state->output_flags & ROCKCHIP_OUTPUT_DUAL_CHANNEL_LEFT_RIGHT_MODE));
477186f8572SMark Yao 		VOP_CTRL_SET(vop, data01_swap,
478bee25ee6SGuochun Huang 			!!(conn_state->output_flags & ROCKCHIP_OUTPUT_DATA_SWAP) ||
479289af5f4SSandy Huang 			crtc_state->dual_channel_swap);
48072f233a9SDamon Ding 		VOP_CTRL_SET(vop, inf_out_en, 1);
48149d669f5SDamon Ding 		VOP_CTRL_SET(vop, out_dresetn, 1);
48272f233a9SDamon Ding 		VOP_GRF_SET(vop, vo0_grf_ctrl, grf_mipi_ch_sel, 1);
48349d669f5SDamon Ding 		VOP_GRF_SET(vop, vo0_grf_ctrl, grf_mipi_mode, 0);
48472f233a9SDamon Ding 		VOP_GRF_SET(vop, vo0_grf_ctrl, grf_mipi_pin_pol, val);
48572f233a9SDamon Ding 		VOP_GRF_SET(vop, vo0_grf_ctrl, grf_mipi_1to4_en, 1);
486186f8572SMark Yao 		break;
4873a06149eSSandy Huang 	case DRM_MODE_CONNECTOR_DisplayPort:
4883a06149eSSandy Huang 		VOP_CTRL_SET(vop, dp_dclk_pol, 0);
4893a06149eSSandy Huang 		VOP_CTRL_SET(vop, dp_pin_pol, val);
4903a06149eSSandy Huang 		VOP_CTRL_SET(vop, dp_en, 1);
4913a06149eSSandy Huang 		break;
49209b01f9eSAlgea Cao 	case DRM_MODE_CONNECTOR_TV:
49309b01f9eSAlgea Cao 		if (vdisplay == CVBS_PAL_VDISPLAY)
49409b01f9eSAlgea Cao 			VOP_CTRL_SET(vop, tve_sw_mode, 1);
49509b01f9eSAlgea Cao 		else
49609b01f9eSAlgea Cao 			VOP_CTRL_SET(vop, tve_sw_mode, 0);
49709b01f9eSAlgea Cao 		VOP_CTRL_SET(vop, tve_dclk_pol, 1);
49809b01f9eSAlgea Cao 		VOP_CTRL_SET(vop, tve_dclk_en, 1);
49909b01f9eSAlgea Cao 		/* use the same pol reg with hdmi */
50009b01f9eSAlgea Cao 		VOP_CTRL_SET(vop, hdmi_pin_pol, val);
50109b01f9eSAlgea Cao 		VOP_CTRL_SET(vop, sw_genlock, 1);
50209b01f9eSAlgea Cao 		VOP_CTRL_SET(vop, sw_uv_offset_en, 1);
50309b01f9eSAlgea Cao 		VOP_CTRL_SET(vop, dither_up, 1);
50409b01f9eSAlgea Cao 		break;
505186f8572SMark Yao 	default:
506186f8572SMark Yao 		printf("unsupport connector_type[%d]\n", conn_state->type);
507186f8572SMark Yao 	}
508186f8572SMark Yao 
5090c4bb35cSDamon Ding 	if ((conn_state->output_mode == ROCKCHIP_OUT_MODE_AAAA &&
5100c4bb35cSDamon Ding 	     !(vop_data->feature & VOP_FEATURE_OUTPUT_10BIT)) ||
511de3f896fSChaoyi Chen 	    (vop->version >= VOP_VERSION_RV1106 && vop->version < VOP_VERSION_RK3288 &&
5120c4bb35cSDamon Ding 	     conn_state->output_if & VOP_OUTPUT_IF_BT656))
513186f8572SMark Yao 		conn_state->output_mode = ROCKCHIP_OUT_MODE_P888;
514186f8572SMark Yao 
515186f8572SMark Yao 	switch (conn_state->bus_format) {
516186f8572SMark Yao 	case MEDIA_BUS_FMT_RGB565_1X16:
517186f8572SMark Yao 		val = DITHER_DOWN_EN(1) | DITHER_DOWN_MODE(RGB888_TO_RGB565);
518186f8572SMark Yao 		break;
519186f8572SMark Yao 	case MEDIA_BUS_FMT_RGB666_1X18:
520186f8572SMark Yao 	case MEDIA_BUS_FMT_RGB666_1X24_CPADHI:
5219c5e1148SWyon Bi 	case MEDIA_BUS_FMT_RGB666_1X7X3_SPWG:
5229c5e1148SWyon Bi 	case MEDIA_BUS_FMT_RGB666_1X7X3_JEIDA:
523186f8572SMark Yao 		val = DITHER_DOWN_EN(1) | DITHER_DOWN_MODE(RGB888_TO_RGB666);
524186f8572SMark Yao 		break;
52579feefb1SSandy Huang 	case MEDIA_BUS_FMT_YUV8_1X24:
52679feefb1SSandy Huang 	case MEDIA_BUS_FMT_UYYVYY8_0_5X24:
52779feefb1SSandy Huang 		val = DITHER_DOWN_EN(0) | PRE_DITHER_DOWN_EN(1);
52879feefb1SSandy Huang 		break;
52979feefb1SSandy Huang 	case MEDIA_BUS_FMT_YUV10_1X30:
53079feefb1SSandy Huang 	case MEDIA_BUS_FMT_UYYVYY10_0_5X30:
53179feefb1SSandy Huang 		val = DITHER_DOWN_EN(0) | PRE_DITHER_DOWN_EN(0);
53279feefb1SSandy Huang 		break;
533186f8572SMark Yao 	case MEDIA_BUS_FMT_RGB888_1X24:
5349c5e1148SWyon Bi 	case MEDIA_BUS_FMT_RGB888_1X7X4_SPWG:
5359c5e1148SWyon Bi 	case MEDIA_BUS_FMT_RGB888_1X7X4_JEIDA:
536186f8572SMark Yao 	default:
537186f8572SMark Yao 		val = DITHER_DOWN_EN(0) | PRE_DITHER_DOWN_EN(0);
538186f8572SMark Yao 		break;
539186f8572SMark Yao 	}
540186f8572SMark Yao 	if (conn_state->output_mode == ROCKCHIP_OUT_MODE_AAAA)
541186f8572SMark Yao 		val |= PRE_DITHER_DOWN_EN(0);
542186f8572SMark Yao 	else
543186f8572SMark Yao 		val |= PRE_DITHER_DOWN_EN(1);
544186f8572SMark Yao 	val |= DITHER_DOWN_MODE_SEL(DITHER_DOWN_ALLEGRO);
545186f8572SMark Yao 	VOP_CTRL_SET(vop, dither_down, val);
546186f8572SMark Yao 
54779feefb1SSandy Huang 	VOP_CTRL_SET(vop, dclk_ddr,
54879feefb1SSandy Huang 		     conn_state->output_mode == ROCKCHIP_OUT_MODE_YUV420 ? 1 : 0);
54979feefb1SSandy Huang 	VOP_CTRL_SET(vop, hdmi_dclk_out_en,
55079feefb1SSandy Huang 		     conn_state->output_mode == ROCKCHIP_OUT_MODE_YUV420 ? 1 : 0);
55179feefb1SSandy Huang 
55280fbe788SDamon Ding 	if (is_uv_swap(conn_state->bus_format, conn_state->output_mode) ||
55380fbe788SDamon Ding 	    is_rb_swap(conn_state->bus_format, conn_state->output_mode))
55432328971SDamon Ding 		VOP_CTRL_SET(vop, dsp_rb_swap, 1);
55579feefb1SSandy Huang 	else
55679feefb1SSandy Huang 		VOP_CTRL_SET(vop, dsp_data_swap, 0);
55779feefb1SSandy Huang 
558efd10ad6SDamon Ding 	/*
559efd10ad6SDamon Ding 	 * For RK3576 vopl, rg_swap and rb_swap need to be enabled in
560efd10ad6SDamon Ding 	 * YUV444 bus_format.
561efd10ad6SDamon Ding 	 */
562de3f896fSChaoyi Chen 	if (vop->version == VOP_VERSION_RK3576_LITE) {
563efd10ad6SDamon Ding 		if (conn_state->bus_format == MEDIA_BUS_FMT_YUV8_1X24)
564efd10ad6SDamon Ding 			VOP_CTRL_SET(vop, dsp_data_swap, DSP_RG_SWAP | DSP_RB_SWAP);
565efd10ad6SDamon Ding 	}
566efd10ad6SDamon Ding 
567186f8572SMark Yao 	VOP_CTRL_SET(vop, out_mode, conn_state->output_mode);
56879feefb1SSandy Huang 
56979feefb1SSandy Huang 	if (VOP_CTRL_SUPPORT(vop, overlay_mode)) {
57079feefb1SSandy Huang 		yuv_overlay = is_yuv_output(conn_state->bus_format);
57179feefb1SSandy Huang 		VOP_CTRL_SET(vop, overlay_mode, yuv_overlay);
57279feefb1SSandy Huang 	}
57379feefb1SSandy Huang 	/*
57479feefb1SSandy Huang 	 * todo: r2y for win csc
57579feefb1SSandy Huang 	 */
57679feefb1SSandy Huang 	VOP_CTRL_SET(vop, dsp_out_yuv, is_yuv_output(conn_state->bus_format));
57779feefb1SSandy Huang 
57879feefb1SSandy Huang 	if (yuv_overlay) {
57979feefb1SSandy Huang 		if (!is_yuv_output(conn_state->bus_format))
58079feefb1SSandy Huang 			post_y2r_en = true;
58179feefb1SSandy Huang 	} else {
58279feefb1SSandy Huang 		if (is_yuv_output(conn_state->bus_format))
58379feefb1SSandy Huang 			post_r2y_en = true;
58479feefb1SSandy Huang 	}
58579feefb1SSandy Huang 
586b7618fd3SSandy Huang 	crtc_state->yuv_overlay = yuv_overlay;
587df0a5c43SDamon Ding 	post_csc_mode = to_vop_csc_mode(conn_state->color_encoding, conn_state->color_range);
58879feefb1SSandy Huang 	VOP_CTRL_SET(vop, bcsh_r2y_en, post_r2y_en);
58979feefb1SSandy Huang 	VOP_CTRL_SET(vop, bcsh_y2r_en, post_y2r_en);
59079feefb1SSandy Huang 	VOP_CTRL_SET(vop, bcsh_r2y_csc_mode, post_csc_mode);
59179feefb1SSandy Huang 	VOP_CTRL_SET(vop, bcsh_y2r_csc_mode, post_csc_mode);
59279feefb1SSandy Huang 
59379feefb1SSandy Huang 	/*
59479feefb1SSandy Huang 	 * Background color is 10bit depth if vop version >= 3.5
59579feefb1SSandy Huang 	 */
59679feefb1SSandy Huang 	if (!is_yuv_output(conn_state->bus_format))
59779feefb1SSandy Huang 		val = 0;
598de3f896fSChaoyi Chen 	else if (vop->version == VOP_VERSION_RK3576_LITE)
5994fd29a21SDamon Ding 		val = 0;
600de3f896fSChaoyi Chen 	else if (vop->version >= VOP_VERSION_RK3399_BIG)
60179feefb1SSandy Huang 		val = 0x20010200;
60279feefb1SSandy Huang 	else
60379feefb1SSandy Huang 		val = 0x801080;
60479feefb1SSandy Huang 	VOP_CTRL_SET(vop, dsp_background, val);
60579feefb1SSandy Huang 
606186f8572SMark Yao 	VOP_CTRL_SET(vop, htotal_pw, (htotal << 16) | hsync_len);
607186f8572SMark Yao 	val = hact_st << 16;
608186f8572SMark Yao 	val |= hact_end;
609186f8572SMark Yao 	VOP_CTRL_SET(vop, hact_st_end, val);
610186f8572SMark Yao 	val = vact_st << 16;
611186f8572SMark Yao 	val |= vact_end;
612186f8572SMark Yao 	VOP_CTRL_SET(vop, vact_st_end, val);
613ccd843b9SSandy Huang 	if (mode->flags & DRM_MODE_FLAG_INTERLACE) {
614ccd843b9SSandy Huang 		u16 vact_st_f1 = vtotal + vact_st + 1;
615ccd843b9SSandy Huang 		u16 vact_end_f1 = vact_st_f1 + vdisplay;
616ccd843b9SSandy Huang 
617ccd843b9SSandy Huang 		val = vact_st_f1 << 16 | vact_end_f1;
618ccd843b9SSandy Huang 		VOP_CTRL_SET(vop, vact_st_end_f1, val);
619ccd843b9SSandy Huang 
620ccd843b9SSandy Huang 		val = vtotal << 16 | (vtotal + vsync_len);
621ccd843b9SSandy Huang 		VOP_CTRL_SET(vop, vs_st_end_f1, val);
622ccd843b9SSandy Huang 		VOP_CTRL_SET(vop, dsp_interlace, 1);
623ccd843b9SSandy Huang 		VOP_CTRL_SET(vop, p2i_en, 1);
624ccd843b9SSandy Huang 		vtotal += vtotal + 1;
625960080c9SSandy Huang 		act_end = vact_end_f1;
626ccd843b9SSandy Huang 	} else {
627ccd843b9SSandy Huang 		VOP_CTRL_SET(vop, dsp_interlace, 0);
628ccd843b9SSandy Huang 		VOP_CTRL_SET(vop, p2i_en, 0);
629960080c9SSandy Huang 		act_end = vact_end;
630ccd843b9SSandy Huang 	}
631ccd843b9SSandy Huang 	VOP_CTRL_SET(vop, vtotal_pw, (vtotal << 16) | vsync_len);
6328a2a3a29SSandy Huang 	vop_post_config(state, vop);
633ccd843b9SSandy Huang 	VOP_CTRL_SET(vop, core_dclk_div,
6340c4bb35cSDamon Ding 		     !!(mode->flags & DRM_MODE_FLAG_DBLCLK) ||
6350c4bb35cSDamon Ding 		     conn_state->output_if & VOP_OUTPUT_IF_BT656);
6368a2a3a29SSandy Huang 
637960080c9SSandy Huang 	VOP_LINE_FLAG_SET(vop, line_flag_num[0], act_end - 3);
638186f8572SMark Yao 	VOP_LINE_FLAG_SET(vop, line_flag_num[1],
639960080c9SSandy Huang 			  act_end - us_to_vertical_line(mode, 1000));
6400a0e1dcdSDamon Ding 
6410a0e1dcdSDamon Ding 	if (state->crtc_state.mcu_timing.mcu_pix_total > 0) {
642de3f896fSChaoyi Chen 		if (vop->version >= VOP_VERSION_RK3576_LITE) {
6430a0e1dcdSDamon Ding 			VOP_CTRL_SET(vop, standby, 0);
6440a0e1dcdSDamon Ding 			vop_set_out_mode(vop, conn_state->output_mode);
6450a0e1dcdSDamon Ding 		}
64672f233a9SDamon Ding 		vop_mcu_mode_setup(state, vop);
6470a0e1dcdSDamon Ding 	}
6480a0e1dcdSDamon Ding 
649186f8572SMark Yao 	vop_cfg_done(vop);
650186f8572SMark Yao 
651186f8572SMark Yao 	return 0;
652186f8572SMark Yao }
653186f8572SMark Yao 
scl_vop_cal_scale(enum scale_mode mode,uint32_t src,uint32_t dst,bool is_horizontal,int vsu_mode,int * vskiplines)654186f8572SMark Yao static uint16_t scl_vop_cal_scale(enum scale_mode mode, uint32_t src,
655186f8572SMark Yao 				  uint32_t dst, bool is_horizontal,
656186f8572SMark Yao 				  int vsu_mode, int *vskiplines)
657186f8572SMark Yao {
658186f8572SMark Yao 	uint16_t val = 1 << SCL_FT_DEFAULT_FIXPOINT_SHIFT;
659186f8572SMark Yao 
660186f8572SMark Yao 	if (is_horizontal) {
661186f8572SMark Yao 		if (mode == SCALE_UP)
662186f8572SMark Yao 			val = GET_SCL_FT_BIC(src, dst);
663186f8572SMark Yao 		else if (mode == SCALE_DOWN)
664186f8572SMark Yao 			val = GET_SCL_FT_BILI_DN(src, dst);
665186f8572SMark Yao 	} else {
666186f8572SMark Yao 		if (mode == SCALE_UP) {
667186f8572SMark Yao 			if (vsu_mode == SCALE_UP_BIL)
668186f8572SMark Yao 				val = GET_SCL_FT_BILI_UP(src, dst);
669186f8572SMark Yao 			else
670186f8572SMark Yao 				val = GET_SCL_FT_BIC(src, dst);
671186f8572SMark Yao 		} else if (mode == SCALE_DOWN) {
672186f8572SMark Yao 			if (vskiplines) {
673186f8572SMark Yao 				*vskiplines = scl_get_vskiplines(src, dst);
674186f8572SMark Yao 				val = scl_get_bili_dn_vskip(src, dst,
675186f8572SMark Yao 							    *vskiplines);
676186f8572SMark Yao 			} else {
677186f8572SMark Yao 				val = GET_SCL_FT_BILI_DN(src, dst);
678186f8572SMark Yao 			}
679186f8572SMark Yao 		}
680186f8572SMark Yao 	}
681186f8572SMark Yao 
682186f8572SMark Yao 	return val;
683186f8572SMark Yao }
684186f8572SMark Yao 
scl_vop_cal_scl_fac(struct vop * vop,uint32_t src_w,uint32_t src_h,uint32_t dst_w,uint32_t dst_h,uint32_t pixel_format)685186f8572SMark Yao static void scl_vop_cal_scl_fac(struct vop *vop,
686186f8572SMark Yao 				uint32_t src_w, uint32_t src_h, uint32_t dst_w,
687186f8572SMark Yao 				uint32_t dst_h, uint32_t pixel_format)
688186f8572SMark Yao {
689186f8572SMark Yao 	uint16_t yrgb_hor_scl_mode, yrgb_ver_scl_mode;
690186f8572SMark Yao 	uint16_t cbcr_hor_scl_mode = SCALE_NONE;
691186f8572SMark Yao 	uint16_t cbcr_ver_scl_mode = SCALE_NONE;
692186f8572SMark Yao 	int hsub = drm_format_horz_chroma_subsampling(pixel_format);
693186f8572SMark Yao 	int vsub = drm_format_vert_chroma_subsampling(pixel_format);
694186f8572SMark Yao 	bool is_yuv = false;
695186f8572SMark Yao 	uint16_t cbcr_src_w = src_w / hsub;
696186f8572SMark Yao 	uint16_t cbcr_src_h = src_h / vsub;
697186f8572SMark Yao 	uint16_t vsu_mode;
698186f8572SMark Yao 	uint16_t lb_mode;
699186f8572SMark Yao 	uint32_t val;
700186f8572SMark Yao 	int vskiplines = 0;
701186f8572SMark Yao 
702186f8572SMark Yao 	if (!vop->win->scl)
703186f8572SMark Yao 		return;
704186f8572SMark Yao 
705186f8572SMark Yao 	if (!vop->win->scl->ext) {
706186f8572SMark Yao 		VOP_SCL_SET(vop, scale_yrgb_x,
707186f8572SMark Yao 			    scl_cal_scale2(src_w, dst_w));
708186f8572SMark Yao 		VOP_SCL_SET(vop, scale_yrgb_y,
709186f8572SMark Yao 			    scl_cal_scale2(src_h, dst_h));
710186f8572SMark Yao 		if (is_yuv) {
711186f8572SMark Yao 			VOP_SCL_SET(vop, scale_cbcr_x,
712186f8572SMark Yao 				    scl_cal_scale2(src_w, dst_w));
713186f8572SMark Yao 			VOP_SCL_SET(vop, scale_cbcr_y,
714186f8572SMark Yao 				    scl_cal_scale2(src_h, dst_h));
715186f8572SMark Yao 		}
716186f8572SMark Yao 		return;
717186f8572SMark Yao 	}
718186f8572SMark Yao 
719186f8572SMark Yao 	yrgb_hor_scl_mode = scl_get_scl_mode(src_w, dst_w);
720186f8572SMark Yao 	yrgb_ver_scl_mode = scl_get_scl_mode(src_h, dst_h);
721186f8572SMark Yao 
722186f8572SMark Yao 	if (is_yuv) {
723186f8572SMark Yao 		cbcr_hor_scl_mode = scl_get_scl_mode(cbcr_src_w, dst_w);
724186f8572SMark Yao 		cbcr_ver_scl_mode = scl_get_scl_mode(cbcr_src_h, dst_h);
725186f8572SMark Yao 		if (cbcr_hor_scl_mode == SCALE_DOWN)
726186f8572SMark Yao 			lb_mode = scl_vop_cal_lb_mode(dst_w, true);
727186f8572SMark Yao 		else
728186f8572SMark Yao 			lb_mode = scl_vop_cal_lb_mode(cbcr_src_w, true);
729186f8572SMark Yao 	} else {
730186f8572SMark Yao 		if (yrgb_hor_scl_mode == SCALE_DOWN)
731186f8572SMark Yao 			lb_mode = scl_vop_cal_lb_mode(dst_w, false);
732186f8572SMark Yao 		else
733186f8572SMark Yao 			lb_mode = scl_vop_cal_lb_mode(src_w, false);
734186f8572SMark Yao 	}
735186f8572SMark Yao 
736186f8572SMark Yao 	VOP_SCL_SET_EXT(vop, lb_mode, lb_mode);
737186f8572SMark Yao 	if (lb_mode == LB_RGB_3840X2) {
738186f8572SMark Yao 		if (yrgb_ver_scl_mode != SCALE_NONE) {
739186f8572SMark Yao 			printf("ERROR : not allow yrgb ver scale\n");
740186f8572SMark Yao 			return;
741186f8572SMark Yao 		}
742186f8572SMark Yao 		if (cbcr_ver_scl_mode != SCALE_NONE) {
743186f8572SMark Yao 			printf("ERROR : not allow cbcr ver scale\n");
744186f8572SMark Yao 			return;
745186f8572SMark Yao 		}
746186f8572SMark Yao 		vsu_mode = SCALE_UP_BIL;
747186f8572SMark Yao 	} else if (lb_mode == LB_RGB_2560X4) {
748186f8572SMark Yao 		vsu_mode = SCALE_UP_BIL;
749186f8572SMark Yao 	} else {
750186f8572SMark Yao 		vsu_mode = SCALE_UP_BIC;
751186f8572SMark Yao 	}
752186f8572SMark Yao 
753186f8572SMark Yao 	val = scl_vop_cal_scale(yrgb_hor_scl_mode, src_w, dst_w,
754186f8572SMark Yao 				true, 0, NULL);
755186f8572SMark Yao 	VOP_SCL_SET(vop, scale_yrgb_x, val);
756186f8572SMark Yao 	val = scl_vop_cal_scale(yrgb_ver_scl_mode, src_h, dst_h,
757186f8572SMark Yao 				false, vsu_mode, &vskiplines);
758186f8572SMark Yao 	VOP_SCL_SET(vop, scale_yrgb_y, val);
759186f8572SMark Yao 
760186f8572SMark Yao 	VOP_SCL_SET_EXT(vop, vsd_yrgb_gt4, vskiplines == 4);
761186f8572SMark Yao 	VOP_SCL_SET_EXT(vop, vsd_yrgb_gt2, vskiplines == 2);
762186f8572SMark Yao 
763186f8572SMark Yao 	VOP_SCL_SET_EXT(vop, yrgb_hor_scl_mode, yrgb_hor_scl_mode);
764186f8572SMark Yao 	VOP_SCL_SET_EXT(vop, yrgb_ver_scl_mode, yrgb_ver_scl_mode);
765186f8572SMark Yao 	VOP_SCL_SET_EXT(vop, yrgb_hsd_mode, SCALE_DOWN_BIL);
766186f8572SMark Yao 	VOP_SCL_SET_EXT(vop, yrgb_vsd_mode, SCALE_DOWN_BIL);
767186f8572SMark Yao 	VOP_SCL_SET_EXT(vop, yrgb_vsu_mode, vsu_mode);
768186f8572SMark Yao 	if (is_yuv) {
769186f8572SMark Yao 		val = scl_vop_cal_scale(cbcr_hor_scl_mode, cbcr_src_w,
770186f8572SMark Yao 					dst_w, true, 0, NULL);
771186f8572SMark Yao 		VOP_SCL_SET(vop, scale_cbcr_x, val);
772186f8572SMark Yao 		val = scl_vop_cal_scale(cbcr_ver_scl_mode, cbcr_src_h,
773186f8572SMark Yao 					dst_h, false, vsu_mode, &vskiplines);
774186f8572SMark Yao 		VOP_SCL_SET(vop, scale_cbcr_y, val);
775186f8572SMark Yao 
776186f8572SMark Yao 		VOP_SCL_SET_EXT(vop, vsd_cbcr_gt4, vskiplines == 4);
777186f8572SMark Yao 		VOP_SCL_SET_EXT(vop, vsd_cbcr_gt2, vskiplines == 2);
778186f8572SMark Yao 		VOP_SCL_SET_EXT(vop, cbcr_hor_scl_mode, cbcr_hor_scl_mode);
779186f8572SMark Yao 		VOP_SCL_SET_EXT(vop, cbcr_ver_scl_mode, cbcr_ver_scl_mode);
780186f8572SMark Yao 		VOP_SCL_SET_EXT(vop, cbcr_hsd_mode, SCALE_DOWN_BIL);
781186f8572SMark Yao 		VOP_SCL_SET_EXT(vop, cbcr_vsd_mode, SCALE_DOWN_BIL);
782186f8572SMark Yao 		VOP_SCL_SET_EXT(vop, cbcr_vsu_mode, vsu_mode);
783186f8572SMark Yao 	}
784186f8572SMark Yao }
785186f8572SMark Yao 
vop_load_csc_table(struct vop * vop,u32 offset,const u32 * table)786b7618fd3SSandy Huang static void vop_load_csc_table(struct vop *vop, u32 offset, const u32 *table)
787b7618fd3SSandy Huang {
788b7618fd3SSandy Huang 	int i;
789b7618fd3SSandy Huang 
790b7618fd3SSandy Huang 	/*
791b7618fd3SSandy Huang 	 * so far the csc offset is not 0 and in the feature the csc offset
792b7618fd3SSandy Huang 	 * impossible be 0, so when the offset is 0, should return here.
793b7618fd3SSandy Huang 	 */
794b7618fd3SSandy Huang 	if (!table || offset == 0)
795b7618fd3SSandy Huang 		return;
796b7618fd3SSandy Huang 
797b7618fd3SSandy Huang 	for (i = 0; i < 8; i++)
798b7618fd3SSandy Huang 		vop_writel(vop, offset + i * 4, table[i]);
799b7618fd3SSandy Huang }
800b7618fd3SSandy Huang 
rockchip_vop_setup_csc_table(struct display_state * state)801b7618fd3SSandy Huang static int rockchip_vop_setup_csc_table(struct display_state *state)
802b7618fd3SSandy Huang {
803b7618fd3SSandy Huang 	struct crtc_state *crtc_state = &state->crtc_state;
804b7618fd3SSandy Huang 	struct connector_state *conn_state = &state->conn_state;
805b7618fd3SSandy Huang 	struct vop *vop = crtc_state->private;
806b7618fd3SSandy Huang 	const uint32_t *csc_table = NULL;
807b7618fd3SSandy Huang 
808b7618fd3SSandy Huang 	if (!vop->csc_table || !crtc_state->yuv_overlay)
809b7618fd3SSandy Huang 		return 0;
810b7618fd3SSandy Huang 	/* todo: only implement r2y*/
811df0a5c43SDamon Ding 	switch (conn_state->color_encoding) {
812df0a5c43SDamon Ding 	case DRM_COLOR_YCBCR_BT601:
813df0a5c43SDamon Ding 		if (conn_state->color_range == DRM_COLOR_YCBCR_LIMITED_RANGE)
814df0a5c43SDamon Ding 			csc_table = vop->csc_table->r2y_bt601_12_235; /* bt601 limit */
815df0a5c43SDamon Ding 		else
816df0a5c43SDamon Ding 			csc_table = vop->csc_table->r2y_bt601; /* bt601 full */
817b7618fd3SSandy Huang 		break;
818df0a5c43SDamon Ding 	case DRM_COLOR_YCBCR_BT709:
819b7618fd3SSandy Huang 		csc_table = vop->csc_table->r2y_bt709;
820b7618fd3SSandy Huang 		break;
821df0a5c43SDamon Ding 	case DRM_COLOR_YCBCR_BT2020:
822b7618fd3SSandy Huang 		csc_table = vop->csc_table->r2y_bt2020;
823b7618fd3SSandy Huang 		break;
824b7618fd3SSandy Huang 	default:
825df0a5c43SDamon Ding 		csc_table = vop->csc_table->r2y_bt601; /* bt601 full */
826b7618fd3SSandy Huang 		break;
827b7618fd3SSandy Huang 	}
828b7618fd3SSandy Huang 
829b7618fd3SSandy Huang 	vop_load_csc_table(vop, vop->win_csc->r2y_offset, csc_table);
830b7618fd3SSandy Huang 	VOP_WIN_CSC_SET(vop, r2y_en, 1);
831b7618fd3SSandy Huang 
832b7618fd3SSandy Huang 	return 0;
833b7618fd3SSandy Huang }
834b7618fd3SSandy Huang 
rockchip_vop_set_plane(struct display_state * state)835186f8572SMark Yao static int rockchip_vop_set_plane(struct display_state *state)
836186f8572SMark Yao {
837186f8572SMark Yao 	struct crtc_state *crtc_state = &state->crtc_state;
838d4a9114aSShixiang Zheng 	const struct rockchip_crtc *crtc = crtc_state->crtc;
839d4a9114aSShixiang Zheng 	const struct vop_data *vop_data = crtc->data;
840186f8572SMark Yao 	struct connector_state *conn_state = &state->conn_state;
841186f8572SMark Yao 	struct drm_display_mode *mode = &conn_state->mode;
842186f8572SMark Yao 	u32 act_info, dsp_info, dsp_st, dsp_stx, dsp_sty;
843186f8572SMark Yao 	struct vop *vop = crtc_state->private;
844ee01dbb2SDamon Ding 	int src_w = crtc_state->src_rect.w;
845ee01dbb2SDamon Ding 	int src_h = crtc_state->src_rect.h;
846ee01dbb2SDamon Ding 	int crtc_x = crtc_state->crtc_rect.x;
847ee01dbb2SDamon Ding 	int crtc_y = crtc_state->crtc_rect.y;
848ee01dbb2SDamon Ding 	int crtc_w = crtc_state->crtc_rect.w;
849ee01dbb2SDamon Ding 	int crtc_h = crtc_state->crtc_rect.h;
850186f8572SMark Yao 	int xvir = crtc_state->xvir;
851ffa55e18SShixiang Zheng 	int x_mirror = 0, y_mirror = 0;
852186f8572SMark Yao 
853eee28ceaSSandy Huang 	if (crtc_w > crtc_state->max_output.width) {
854eee28ceaSSandy Huang 		printf("ERROR: output w[%d] exceeded max width[%d]\n",
855eee28ceaSSandy Huang 		       crtc_w, crtc_state->max_output.width);
856cf53642aSSandy Huang 		return -EINVAL;
857cf53642aSSandy Huang 	}
858cf53642aSSandy Huang 
859de3f896fSChaoyi Chen 	if ((vop->version == VOP_VERSION_RK3036 || vop->version >= VOP_VERSION_RK3576_LITE) &&
86072f233a9SDamon Ding 	    (mode->flags & DRM_MODE_FLAG_INTERLACE))
86172f233a9SDamon Ding 		crtc_h = crtc_h / 2;
86272f233a9SDamon Ding 
863186f8572SMark Yao 	act_info = (src_h - 1) << 16;
864186f8572SMark Yao 	act_info |= (src_w - 1) & 0xffff;
865186f8572SMark Yao 
866186f8572SMark Yao 	dsp_info = (crtc_h - 1) << 16;
867186f8572SMark Yao 	dsp_info |= (crtc_w - 1) & 0xffff;
868186f8572SMark Yao 
86944006808SAlgea Cao 	dsp_stx = crtc_x + mode->crtc_htotal - mode->crtc_hsync_start;
87044006808SAlgea Cao 	dsp_sty = crtc_y + mode->crtc_vtotal - mode->crtc_vsync_start;
871de3f896fSChaoyi Chen 	if ((vop->version == VOP_VERSION_RK3036 || vop->version >= VOP_VERSION_RK3576_LITE) &&
87272f233a9SDamon Ding 	    (mode->flags & DRM_MODE_FLAG_INTERLACE))
87372f233a9SDamon Ding 		dsp_sty = crtc_y / 2 + mode->crtc_vtotal - mode->crtc_vsync_start;
874186f8572SMark Yao 	dsp_st = dsp_sty << 16 | (dsp_stx & 0xffff);
875d4a9114aSShixiang Zheng 	/*
876f0e8414bSSandy Huang 	 * vop full need to treats rgb888 as bgr888 so we reverse the rb swap to workaround
877d4a9114aSShixiang Zheng 	 */
878f0e8414bSSandy Huang 	if (crtc_state->format == ROCKCHIP_FMT_RGB888 && VOP_MAJOR(vop_data->version) == 3)
879d4a9114aSShixiang Zheng 		crtc_state->rb_swap = !crtc_state->rb_swap;
880186f8572SMark Yao 
881ffa55e18SShixiang Zheng 	if (mode->flags & DRM_MODE_FLAG_YMIRROR)
882ffa55e18SShixiang Zheng 		y_mirror = 1;
883ffa55e18SShixiang Zheng 	else
884ffa55e18SShixiang Zheng 		y_mirror = 0;
885ffa55e18SShixiang Zheng 	if (mode->flags & DRM_MODE_FLAG_XMIRROR)
886ffa55e18SShixiang Zheng 		x_mirror = 1;
887ffa55e18SShixiang Zheng 	else
888ffa55e18SShixiang Zheng 		x_mirror = 0;
889ffa55e18SShixiang Zheng 	if (crtc_state->ymirror ^ y_mirror)
890ffa55e18SShixiang Zheng 		y_mirror = 1;
891ffa55e18SShixiang Zheng 	else
892ffa55e18SShixiang Zheng 		y_mirror = 0;
893ffa55e18SShixiang Zheng 	if (y_mirror) {
894ffa55e18SShixiang Zheng 		if (VOP_CTRL_SUPPORT(vop, ymirror))
895186f8572SMark Yao 			crtc_state->dma_addr += (src_h - 1) * xvir * 4;
896620af6a3SSandy Huang 		else
897ffa55e18SShixiang Zheng 			y_mirror = 0;
898620af6a3SSandy Huang 		}
899ffa55e18SShixiang Zheng 	VOP_CTRL_SET(vop, ymirror, y_mirror);
900ffa55e18SShixiang Zheng 	VOP_CTRL_SET(vop, xmirror, x_mirror);
901ffa55e18SShixiang Zheng 
902186f8572SMark Yao 	VOP_WIN_SET(vop, format, crtc_state->format);
9030c4bb35cSDamon Ding 
9040c4bb35cSDamon Ding 	VOP_WIN_SET(vop, interlace_read, (mode->flags & DRM_MODE_FLAG_INTERLACE) ? 1 : 0);
9050c4bb35cSDamon Ding 
906186f8572SMark Yao 	VOP_WIN_SET(vop, yrgb_vir, xvir);
907186f8572SMark Yao 	VOP_WIN_SET(vop, yrgb_mst, crtc_state->dma_addr);
908186f8572SMark Yao 
909186f8572SMark Yao 	scl_vop_cal_scl_fac(vop, src_w, src_h, crtc_w, crtc_h,
910186f8572SMark Yao 			    crtc_state->format);
911186f8572SMark Yao 
912186f8572SMark Yao 	VOP_WIN_SET(vop, act_info, act_info);
913186f8572SMark Yao 	VOP_WIN_SET(vop, dsp_info, dsp_info);
914186f8572SMark Yao 	VOP_WIN_SET(vop, dsp_st, dsp_st);
915186f8572SMark Yao 	VOP_WIN_SET(vop, rb_swap, crtc_state->rb_swap);
916186f8572SMark Yao 
917186f8572SMark Yao 	VOP_WIN_SET(vop, src_alpha_ctl, 0);
918186f8572SMark Yao 
919b7618fd3SSandy Huang 	rockchip_vop_setup_csc_table(state);
920186f8572SMark Yao 	VOP_WIN_SET(vop, enable, 1);
921c33e1feaSAndy Yan 	VOP_WIN_SET(vop, gate, 1);
922186f8572SMark Yao 	vop_cfg_done(vop);
923186f8572SMark Yao 
924224ad886SDamon Ding 	printf("VOP:0x%8p set plane [%dx%d->%dx%d@%dx%d] fmt[%d] addr[0x%x]\n",
925224ad886SDamon Ding 		vop->regs, crtc_state->src_rect.w, crtc_state->src_rect.h,
926224ad886SDamon Ding 		crtc_state->crtc_rect.w, crtc_state->crtc_rect.h,
927224ad886SDamon Ding 		crtc_state->crtc_rect.x, crtc_state->crtc_rect.y,
928224ad886SDamon Ding 		crtc_state->format, crtc_state->dma_addr);
929224ad886SDamon Ding 
930186f8572SMark Yao 	return 0;
931186f8572SMark Yao }
932186f8572SMark Yao 
rockchip_vop_prepare(struct display_state * state)933186f8572SMark Yao static int rockchip_vop_prepare(struct display_state *state)
934186f8572SMark Yao {
935186f8572SMark Yao 	return 0;
936186f8572SMark Yao }
937186f8572SMark Yao 
rockchip_vop_enable(struct display_state * state)938186f8572SMark Yao static int rockchip_vop_enable(struct display_state *state)
939186f8572SMark Yao {
940186f8572SMark Yao 	struct crtc_state *crtc_state = &state->crtc_state;
941186f8572SMark Yao 	struct vop *vop = crtc_state->private;
942186f8572SMark Yao 
943186f8572SMark Yao 	VOP_CTRL_SET(vop, standby, 0);
944186f8572SMark Yao 	vop_cfg_done(vop);
94567b9012cSSandy Huang 	if (crtc_state->mcu_timing.mcu_pix_total > 0)
94667b9012cSSandy Huang 		VOP_CTRL_SET(vop, mcu_hold_mode, 0);
947186f8572SMark Yao 
948186f8572SMark Yao 	return 0;
949186f8572SMark Yao }
950186f8572SMark Yao 
rockchip_vop_disable(struct display_state * state)951186f8572SMark Yao static int rockchip_vop_disable(struct display_state *state)
952186f8572SMark Yao {
953186f8572SMark Yao 	struct crtc_state *crtc_state = &state->crtc_state;
954186f8572SMark Yao 	struct vop *vop = crtc_state->private;
955186f8572SMark Yao 
956186f8572SMark Yao 	VOP_CTRL_SET(vop, standby, 1);
957186f8572SMark Yao 	vop_cfg_done(vop);
958186f8572SMark Yao 	return 0;
959186f8572SMark Yao }
960186f8572SMark Yao 
rockchip_vop_fixup_dts(struct display_state * state,void * blob)961186f8572SMark Yao static int rockchip_vop_fixup_dts(struct display_state *state, void *blob)
962186f8572SMark Yao {
963e2bce6e4SKever Yang #if 0
964186f8572SMark Yao 	struct crtc_state *crtc_state = &state->crtc_state;
965186f8572SMark Yao 	struct panel_state *pstate = &state->panel_state;
966186f8572SMark Yao 	uint32_t phandle;
967186f8572SMark Yao 	char path[100];
968186f8572SMark Yao 	int ret, dsp_lut_node;
969186f8572SMark Yao 
970e2bce6e4SKever Yang 	if (!ofnode_valid(pstate->dsp_lut_node))
971186f8572SMark Yao 		return 0;
972186f8572SMark Yao 	ret = fdt_get_path(state->blob, pstate->dsp_lut_node, path, sizeof(path));
973186f8572SMark Yao 	if (ret < 0) {
974186f8572SMark Yao 		printf("failed to get dsp_lut path[%s], ret=%d\n",
975186f8572SMark Yao 			path, ret);
976186f8572SMark Yao 		return ret;
977186f8572SMark Yao 	}
978186f8572SMark Yao 
979186f8572SMark Yao 	dsp_lut_node = fdt_path_offset(blob, path);
980186f8572SMark Yao 	phandle = fdt_get_phandle(blob, dsp_lut_node);
981186f8572SMark Yao 	if (!phandle) {
982186f8572SMark Yao 		phandle = fdt_alloc_phandle(blob);
983186f8572SMark Yao 		if (!phandle) {
984186f8572SMark Yao 			printf("failed to alloc phandle\n");
985186f8572SMark Yao 			return -ENOMEM;
986186f8572SMark Yao 		}
987186f8572SMark Yao 
988186f8572SMark Yao 		fdt_set_phandle(blob, dsp_lut_node, phandle);
989186f8572SMark Yao 	}
990186f8572SMark Yao 
991186f8572SMark Yao 	ret = fdt_get_path(state->blob, crtc_state->node, path, sizeof(path));
992186f8572SMark Yao 	if (ret < 0) {
993186f8572SMark Yao 		printf("failed to get route path[%s], ret=%d\n",
994186f8572SMark Yao 			path, ret);
995186f8572SMark Yao 		return ret;
996186f8572SMark Yao 	}
997186f8572SMark Yao 
998186f8572SMark Yao 	do_fixup_by_path_u32(blob, path, "dsp-lut", phandle, 1);
999e2bce6e4SKever Yang #endif
1000186f8572SMark Yao 	return 0;
1001186f8572SMark Yao }
1002186f8572SMark Yao 
rockchip_vop_send_mcu_cmd(struct display_state * state,u32 type,u32 value)100372f233a9SDamon Ding static int rockchip_vop_send_mcu_cmd(struct display_state *state, u32 type, u32 value)
100467b9012cSSandy Huang {
100567b9012cSSandy Huang 	struct crtc_state *crtc_state = &state->crtc_state;
100672f233a9SDamon Ding 	struct connector_state *conn_state = &state->conn_state;
100772f233a9SDamon Ding 	struct drm_display_mode *mode = &conn_state->mode;
100867b9012cSSandy Huang 	struct vop *vop = crtc_state->private;
100972f233a9SDamon Ding 	int ret;
101072f233a9SDamon Ding 
1011de3f896fSChaoyi Chen 	if (vop->version >= VOP_VERSION_RK3576_LITE) {
101272f233a9SDamon Ding 		/*
101372f233a9SDamon Ding 		 * 1.set mcu bypass mode timing.
101472f233a9SDamon Ding 		 * 2.set dclk rate to 150M.
101572f233a9SDamon Ding 		 */
101672f233a9SDamon Ding 		if ((type == MCU_SETBYPASS) && value) {
101772f233a9SDamon Ding 			vop_mcu_bypass_mode_setup(state, vop);
101872f233a9SDamon Ding 			ret = clk_set_rate(&crtc_state->dclk, 150000000);
101972f233a9SDamon Ding 			if (IS_ERR_VALUE(ret)) {
102072f233a9SDamon Ding 				printf("%s: Failed to set dclk: ret=%d\n", __func__, ret);
102172f233a9SDamon Ding 				return ret;
102272f233a9SDamon Ding 			}
102372f233a9SDamon Ding 		}
102472f233a9SDamon Ding 	}
102567b9012cSSandy Huang 
102667b9012cSSandy Huang 	if (vop) {
102767b9012cSSandy Huang 		switch (type) {
102867b9012cSSandy Huang 		case MCU_WRCMD:
1029*b899f9ccSChaoyi Chen 			VOP_CTRL_SET(vop, mcu_force_rdn, 1);
10306b898587SDamon Ding 			set_vop_mcu_rs(vop, 0);
103167b9012cSSandy Huang 			VOP_CTRL_SET(vop, mcu_rw_bypass_port, value);
10326b898587SDamon Ding 			set_vop_mcu_rs(vop, 1);
103367b9012cSSandy Huang 			break;
103467b9012cSSandy Huang 		case MCU_WRDATA:
1035*b899f9ccSChaoyi Chen 			VOP_CTRL_SET(vop, mcu_force_rdn, 1);
10366b898587SDamon Ding 			set_vop_mcu_rs(vop, 1);
103767b9012cSSandy Huang 			VOP_CTRL_SET(vop, mcu_rw_bypass_port, value);
103867b9012cSSandy Huang 			break;
103967b9012cSSandy Huang 		case MCU_SETBYPASS:
104067b9012cSSandy Huang 			VOP_CTRL_SET(vop, mcu_bypass, value ? 1 : 0);
1041*b899f9ccSChaoyi Chen 			if (!value)
1042*b899f9ccSChaoyi Chen 				VOP_CTRL_SET(vop, mcu_force_rdn, 1);
104367b9012cSSandy Huang 			break;
104467b9012cSSandy Huang 		default:
104567b9012cSSandy Huang 			break;
104667b9012cSSandy Huang 		}
104767b9012cSSandy Huang 	}
104867b9012cSSandy Huang 
1049de3f896fSChaoyi Chen 	if (vop->version >= VOP_VERSION_RK3576_LITE) {
105072f233a9SDamon Ding 		/*
105172f233a9SDamon Ding 		 * 1.restore mcu data mode timing.
105272f233a9SDamon Ding 		 * 2.restore dclk rate to crtc_clock.
105372f233a9SDamon Ding 		 */
105472f233a9SDamon Ding 		if ((type == MCU_SETBYPASS) && !value) {
105572f233a9SDamon Ding 			vop_mcu_mode_setup(state, vop);
105672f233a9SDamon Ding 			ret = clk_set_rate(&crtc_state->dclk, mode->crtc_clock * 1000);
105772f233a9SDamon Ding 			if (IS_ERR_VALUE(ret)) {
105872f233a9SDamon Ding 				printf("%s: Failed to set dclk: ret=%d\n", __func__, ret);
105972f233a9SDamon Ding 				return ret;
106072f233a9SDamon Ding 			}
106172f233a9SDamon Ding 		}
106272f233a9SDamon Ding 	}
106372f233a9SDamon Ding 
106467b9012cSSandy Huang 	return 0;
106567b9012cSSandy Huang }
106667b9012cSSandy Huang 
rockchip_vop_mode_valid(struct display_state * state)106722007755SDamon Ding static int rockchip_vop_mode_valid(struct display_state *state)
106822007755SDamon Ding {
106922007755SDamon Ding 	struct connector_state *conn_state = &state->conn_state;
107022007755SDamon Ding 	struct drm_display_mode *mode = &conn_state->mode;
107122007755SDamon Ding 	struct videomode vm;
107222007755SDamon Ding 
107322007755SDamon Ding 	drm_display_mode_to_videomode(mode, &vm);
107422007755SDamon Ding 
107522007755SDamon Ding 	if (vm.hactive < 32 || vm.vactive < 32 ||
107622007755SDamon Ding 	    (vm.hfront_porch * vm.hsync_len * vm.hback_porch *
107722007755SDamon Ding 	     vm.vfront_porch * vm.vsync_len * vm.vback_porch == 0)) {
107822007755SDamon Ding 		printf("ERROR: unsupported display timing\n");
107922007755SDamon Ding 		return -EINVAL;
108022007755SDamon Ding 	}
108122007755SDamon Ding 
108222007755SDamon Ding 	return 0;
108322007755SDamon Ding }
108422007755SDamon Ding 
rockchip_vop_plane_check(struct display_state * state)10854c765862SDamon Ding static int rockchip_vop_plane_check(struct display_state *state)
10864c765862SDamon Ding {
10874c765862SDamon Ding 	struct crtc_state *crtc_state = &state->crtc_state;
10884c765862SDamon Ding 	const struct rockchip_crtc *crtc = crtc_state->crtc;
10894c765862SDamon Ding 	const struct vop_data *vop_data = crtc->data;
10904c765862SDamon Ding 	const struct vop_win *win = vop_data->win;
10914c765862SDamon Ding 	struct display_rect *src = &crtc_state->src_rect;
10924c765862SDamon Ding 	struct display_rect *dst = &crtc_state->crtc_rect;
10934c765862SDamon Ding 	int min_scale, max_scale;
10944c765862SDamon Ding 	int hscale, vscale;
10954c765862SDamon Ding 
10964c765862SDamon Ding 	min_scale = win->scl ? FRAC_16_16(1, 8) : VOP_PLANE_NO_SCALING;
10974c765862SDamon Ding 	max_scale = win->scl ? FRAC_16_16(8, 1) : VOP_PLANE_NO_SCALING;
10984c765862SDamon Ding 
10994c765862SDamon Ding 	hscale = display_rect_calc_hscale(src, dst, min_scale, max_scale);
11004c765862SDamon Ding 	vscale = display_rect_calc_vscale(src, dst, min_scale, max_scale);
11014c765862SDamon Ding 	if (hscale < 0 || vscale < 0) {
11024c765862SDamon Ding 		printf("ERROR: scale factor is out of range\n");
11034c765862SDamon Ding 		return -ERANGE;
11044c765862SDamon Ding 	}
11054c765862SDamon Ding 
11064c765862SDamon Ding 	return 0;
11074c765862SDamon Ding }
11084c765862SDamon Ding 
rockchip_vop_mode_fixup(struct display_state * state)11091b5811e7SDamon Ding static int rockchip_vop_mode_fixup(struct display_state *state)
11101b5811e7SDamon Ding {
11114d64cedbSDamon Ding 	struct crtc_state *crtc_state = &state->crtc_state;
11120c4bb35cSDamon Ding 	const struct rockchip_crtc *crtc = crtc_state->crtc;
11130c4bb35cSDamon Ding 	const struct vop_data *vop_data = crtc->data;
11141b5811e7SDamon Ding 	struct connector_state *conn_state = &state->conn_state;
11151b5811e7SDamon Ding 	struct drm_display_mode *mode = &conn_state->mode;
11161b5811e7SDamon Ding 
11171b5811e7SDamon Ding 	drm_mode_set_crtcinfo(mode, CRTC_INTERLACE_HALVE_V | CRTC_STEREO_DOUBLE);
11181b5811e7SDamon Ding 
11190c4bb35cSDamon Ding 	/*
11200c4bb35cSDamon Ding 	 * Dclk need to be double if BT656 interface and vop version >= 2.12.
11210c4bb35cSDamon Ding 	 */
11220c4bb35cSDamon Ding 	if (mode->flags & DRM_MODE_FLAG_DBLCLK ||
1123de3f896fSChaoyi Chen 	    (vop_data->version >= VOP_VERSION_RV1106 && vop_data->version < VOP_VERSION_RK3288 &&
11240c4bb35cSDamon Ding 	     conn_state->output_if & VOP_OUTPUT_IF_BT656))
11250c4bb35cSDamon Ding 		mode->crtc_clock *= 2;
11260c4bb35cSDamon Ding 
11274d64cedbSDamon Ding 	mode->crtc_clock *= rockchip_drm_get_cycles_per_pixel(conn_state->bus_format);
11284d64cedbSDamon Ding 	if (crtc_state->mcu_timing.mcu_pix_total)
11294d64cedbSDamon Ding 		mode->crtc_clock *= crtc_state->mcu_timing.mcu_pix_total + 1;
11304d64cedbSDamon Ding 
11311b5811e7SDamon Ding 	return 0;
11321b5811e7SDamon Ding }
11331b5811e7SDamon Ding 
1134186f8572SMark Yao const struct rockchip_crtc_funcs rockchip_vop_funcs = {
1135cf53642aSSandy Huang 	.preinit = rockchip_vop_preinit,
1136186f8572SMark Yao 	.init = rockchip_vop_init,
1137186f8572SMark Yao 	.set_plane = rockchip_vop_set_plane,
1138186f8572SMark Yao 	.prepare = rockchip_vop_prepare,
1139186f8572SMark Yao 	.enable = rockchip_vop_enable,
1140186f8572SMark Yao 	.disable = rockchip_vop_disable,
1141186f8572SMark Yao 	.fixup_dts = rockchip_vop_fixup_dts,
114267b9012cSSandy Huang 	.send_mcu_cmd = rockchip_vop_send_mcu_cmd,
114322007755SDamon Ding 	.mode_valid = rockchip_vop_mode_valid,
11444c765862SDamon Ding 	.plane_check = rockchip_vop_plane_check,
11451b5811e7SDamon Ding 	.mode_fixup = rockchip_vop_mode_fixup,
1146186f8572SMark Yao };
1147