xref: /rk3399_rockchip-uboot/drivers/video/drm/rockchip_vop.c (revision fc8b0d66767c032e08f1d74da5d0fcc366d9ae00)
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>
17186f8572SMark Yao #include <clk.h>
18186f8572SMark Yao #include <asm/arch/clock.h>
19186f8572SMark Yao #include <linux/err.h>
20e2bce6e4SKever Yang #include <dm/device.h>
21e2bce6e4SKever Yang #include <dm/read.h>
223a06149eSSandy Huang #include <syscon.h>
23*fc8b0d66SDamon Ding #include <regmap.h>
24186f8572SMark Yao 
25186f8572SMark Yao #include "rockchip_display.h"
26186f8572SMark Yao #include "rockchip_crtc.h"
27186f8572SMark Yao #include "rockchip_connector.h"
28186f8572SMark Yao #include "rockchip_vop.h"
29186f8572SMark Yao 
30186f8572SMark Yao static inline int us_to_vertical_line(struct drm_display_mode *mode, int us)
31186f8572SMark Yao {
32186f8572SMark Yao 	return us * mode->clock / mode->htotal / 1000;
33186f8572SMark Yao }
344b8c2ef1SMark Yao 
356b898587SDamon Ding static inline void set_vop_mcu_rs(struct vop *vop, int v)
366b898587SDamon Ding {
376b898587SDamon Ding 	if (dm_gpio_is_valid(&vop->mcu_rs_gpio))
386b898587SDamon Ding 		dm_gpio_set_value(&vop->mcu_rs_gpio, v);
396b898587SDamon Ding 	else
406b898587SDamon Ding 		VOP_CTRL_SET(vop, mcu_rs, v);
416b898587SDamon Ding }
426b898587SDamon Ding 
4379feefb1SSandy Huang static int to_vop_csc_mode(int csc_mode)
4479feefb1SSandy Huang {
4579feefb1SSandy Huang 	switch (csc_mode) {
4679feefb1SSandy Huang 	case V4L2_COLORSPACE_SMPTE170M:
4779feefb1SSandy Huang 		return CSC_BT601L;
4879feefb1SSandy Huang 	case V4L2_COLORSPACE_REC709:
4979feefb1SSandy Huang 	case V4L2_COLORSPACE_DEFAULT:
5079feefb1SSandy Huang 		return CSC_BT709L;
5179feefb1SSandy Huang 	case V4L2_COLORSPACE_JPEG:
5279feefb1SSandy Huang 		return CSC_BT601F;
5379feefb1SSandy Huang 	case V4L2_COLORSPACE_BT2020:
5479feefb1SSandy Huang 		return CSC_BT2020;
5579feefb1SSandy Huang 	default:
5679feefb1SSandy Huang 		return CSC_BT709L;
5779feefb1SSandy Huang 	}
5879feefb1SSandy Huang }
5979feefb1SSandy Huang 
6079feefb1SSandy Huang static bool is_yuv_output(uint32_t bus_format)
6179feefb1SSandy Huang {
6279feefb1SSandy Huang 	switch (bus_format) {
6379feefb1SSandy Huang 	case MEDIA_BUS_FMT_YUV8_1X24:
6479feefb1SSandy Huang 	case MEDIA_BUS_FMT_YUV10_1X30:
6579feefb1SSandy Huang 	case MEDIA_BUS_FMT_UYYVYY8_0_5X24:
6679feefb1SSandy Huang 	case MEDIA_BUS_FMT_UYYVYY10_0_5X30:
67f721bd04SDamon Ding 	case MEDIA_BUS_FMT_YUYV8_2X8:
68f721bd04SDamon Ding 	case MEDIA_BUS_FMT_YVYU8_2X8:
69f721bd04SDamon Ding 	case MEDIA_BUS_FMT_UYVY8_2X8:
70f721bd04SDamon Ding 	case MEDIA_BUS_FMT_VYUY8_2X8:
71f721bd04SDamon Ding 	case MEDIA_BUS_FMT_YUYV8_1X16:
72f721bd04SDamon Ding 	case MEDIA_BUS_FMT_YVYU8_1X16:
73f721bd04SDamon Ding 	case MEDIA_BUS_FMT_UYVY8_1X16:
74f721bd04SDamon Ding 	case MEDIA_BUS_FMT_VYUY8_1X16:
7579feefb1SSandy Huang 		return true;
7679feefb1SSandy Huang 	default:
7779feefb1SSandy Huang 		return false;
7879feefb1SSandy Huang 	}
7979feefb1SSandy Huang }
8079feefb1SSandy Huang 
8179feefb1SSandy Huang static bool is_uv_swap(uint32_t bus_format, uint32_t output_mode)
8279feefb1SSandy Huang {
8379feefb1SSandy Huang 	/*
8479feefb1SSandy Huang 	 * FIXME:
8579feefb1SSandy Huang 	 *
8679feefb1SSandy Huang 	 * There is no media type for YUV444 output,
8779feefb1SSandy Huang 	 * so when out_mode is AAAA or P888, assume output is YUV444 on
8879feefb1SSandy Huang 	 * yuv format.
8979feefb1SSandy Huang 	 *
9079feefb1SSandy Huang 	 * From H/W testing, YUV444 mode need a rb swap.
9179feefb1SSandy Huang 	 */
9279feefb1SSandy Huang 	if ((bus_format == MEDIA_BUS_FMT_YUV8_1X24 ||
9379feefb1SSandy Huang 	     bus_format == MEDIA_BUS_FMT_YUV10_1X30) &&
9479feefb1SSandy Huang 	    (output_mode == ROCKCHIP_OUT_MODE_AAAA ||
9579feefb1SSandy Huang 	     output_mode == ROCKCHIP_OUT_MODE_P888))
9679feefb1SSandy Huang 		return true;
9779feefb1SSandy Huang 	else
9879feefb1SSandy Huang 		return false;
9979feefb1SSandy Huang }
10079feefb1SSandy Huang 
10180fbe788SDamon Ding static bool is_rb_swap(uint32_t bus_format, uint32_t output_mode)
10280fbe788SDamon Ding {
10380fbe788SDamon Ding 	/*
10480fbe788SDamon Ding 	 * The default component order of serial rgb3x8 formats
10580fbe788SDamon Ding 	 * is BGR. So it is needed to enable RB swap.
10680fbe788SDamon Ding 	 */
107b7b383ebSDamon Ding 	if (bus_format == MEDIA_BUS_FMT_RGB888_3X8 ||
108b7b383ebSDamon Ding 	    bus_format == MEDIA_BUS_FMT_RGB888_DUMMY_4X8)
10980fbe788SDamon Ding 		return true;
11080fbe788SDamon Ding 	else
11180fbe788SDamon Ding 		return false;
11280fbe788SDamon Ding }
11380fbe788SDamon Ding 
114186f8572SMark Yao static int rockchip_vop_init_gamma(struct vop *vop, struct display_state *state)
115186f8572SMark Yao {
116186f8572SMark Yao 	struct crtc_state *crtc_state = &state->crtc_state;
117186f8572SMark Yao 	struct connector_state *conn_state = &state->conn_state;
118186f8572SMark Yao 	u32 *lut = conn_state->gamma.lut;
119186f8572SMark Yao 	fdt_size_t lut_size;
120186f8572SMark Yao 	int i, lut_len;
121186f8572SMark Yao 	u32 *lut_regs;
122186f8572SMark Yao 
123186f8572SMark Yao 	if (!conn_state->gamma.lut)
124186f8572SMark Yao 		return 0;
125186f8572SMark Yao 
126e2bce6e4SKever Yang 	i = dev_read_stringlist_search(crtc_state->dev, "reg-names", "gamma_lut");
127186f8572SMark Yao 	if (i < 0) {
128186f8572SMark Yao 		printf("Warning: vop not support gamma\n");
129186f8572SMark Yao 		return 0;
130186f8572SMark Yao 	}
131e2bce6e4SKever Yang 	lut_regs = (u32 *)dev_read_addr_size(crtc_state->dev, "reg", &lut_size);
132186f8572SMark Yao 	if (lut_regs == (u32 *)FDT_ADDR_T_NONE) {
133186f8572SMark Yao 		printf("failed to get gamma lut register\n");
134186f8572SMark Yao 		return 0;
135186f8572SMark Yao 	}
136186f8572SMark Yao 	lut_len = lut_size / 4;
137186f8572SMark Yao 	if (lut_len != 256 && lut_len != 1024) {
138186f8572SMark Yao 		printf("Warning: unsupport gamma lut table[%d]\n", lut_len);
139186f8572SMark Yao 		return 0;
140186f8572SMark Yao 	}
141186f8572SMark Yao 
142186f8572SMark Yao 	if (conn_state->gamma.size != lut_len) {
143186f8572SMark Yao 		int size = conn_state->gamma.size;
144186f8572SMark Yao 		u32 j, r, g, b, color;
145186f8572SMark Yao 
146186f8572SMark Yao 		for (i = 0; i < lut_len; i++) {
147186f8572SMark Yao 			j = i * size / lut_len;
148186f8572SMark Yao 			r = lut[j] / size / size * lut_len / size;
149186f8572SMark Yao 			g = lut[j] / size % size * lut_len / size;
150186f8572SMark Yao 			b = lut[j] % size * lut_len / size;
151186f8572SMark Yao 			color = r * lut_len * lut_len + g * lut_len + b;
152186f8572SMark Yao 
153186f8572SMark Yao 			writel(color, lut_regs + (i << 2));
154186f8572SMark Yao 		}
155186f8572SMark Yao 	} else {
156186f8572SMark Yao 		for (i = 0; i < lut_len; i++)
157186f8572SMark Yao 			writel(lut[i], lut_regs + (i << 2));
158186f8572SMark Yao 	}
159186f8572SMark Yao 
160186f8572SMark Yao 	VOP_CTRL_SET(vop, dsp_lut_en, 1);
161186f8572SMark Yao 	VOP_CTRL_SET(vop, update_gamma_lut, 1);
162186f8572SMark Yao 
163186f8572SMark Yao 	return 0;
164186f8572SMark Yao }
165186f8572SMark Yao 
1668a2a3a29SSandy Huang static void vop_post_config(struct display_state *state, struct vop *vop)
1678a2a3a29SSandy Huang {
1688a2a3a29SSandy Huang 	struct connector_state *conn_state = &state->conn_state;
1698a2a3a29SSandy Huang 	struct drm_display_mode *mode = &conn_state->mode;
1708a2a3a29SSandy Huang 	u16 vtotal = mode->crtc_vtotal;
1718a2a3a29SSandy Huang 	u16 hact_st = mode->crtc_htotal - mode->crtc_hsync_start;
1728a2a3a29SSandy Huang 	u16 vact_st = mode->crtc_vtotal - mode->crtc_vsync_start;
1738a2a3a29SSandy Huang 	u16 hdisplay = mode->crtc_hdisplay;
1748a2a3a29SSandy Huang 	u16 vdisplay = mode->crtc_vdisplay;
1758a2a3a29SSandy Huang 	u16 hsize = hdisplay * (conn_state->overscan.left_margin + conn_state->overscan.right_margin) / 200;
1768a2a3a29SSandy Huang 	u16 vsize = vdisplay * (conn_state->overscan.top_margin + conn_state->overscan.bottom_margin) / 200;
1778a2a3a29SSandy Huang 	u16 hact_end, vact_end;
1788a2a3a29SSandy Huang 	u32 val;
1798a2a3a29SSandy Huang 
1808a2a3a29SSandy Huang 	if (mode->flags & DRM_MODE_FLAG_INTERLACE)
1818a2a3a29SSandy Huang 		vsize = round_down(vsize, 2);
1828a2a3a29SSandy Huang 
1838a2a3a29SSandy Huang 	hact_st += hdisplay * (100 - conn_state->overscan.left_margin) / 200;
1848a2a3a29SSandy Huang 	hact_end = hact_st + hsize;
1858a2a3a29SSandy Huang 	val = hact_st << 16;
1868a2a3a29SSandy Huang 	val |= hact_end;
1878a2a3a29SSandy Huang 
1888a2a3a29SSandy Huang 	VOP_CTRL_SET(vop, hpost_st_end, val);
1898a2a3a29SSandy Huang 	vact_st += vdisplay * (100 - conn_state->overscan.top_margin) / 200;
1908a2a3a29SSandy Huang 	vact_end = vact_st + vsize;
1918a2a3a29SSandy Huang 	val = vact_st << 16;
1928a2a3a29SSandy Huang 	val |= vact_end;
1938a2a3a29SSandy Huang 	VOP_CTRL_SET(vop, vpost_st_end, val);
1948a2a3a29SSandy Huang 	val = scl_cal_scale2(vdisplay, vsize) << 16;
1958a2a3a29SSandy Huang 	val |= scl_cal_scale2(hdisplay, hsize);
1968a2a3a29SSandy Huang 	VOP_CTRL_SET(vop, post_scl_factor, val);
1978a2a3a29SSandy Huang #define POST_HORIZONTAL_SCALEDOWN_EN(x)		((x) << 0)
1988a2a3a29SSandy Huang #define POST_VERTICAL_SCALEDOWN_EN(x)		((x) << 1)
1998a2a3a29SSandy Huang 	VOP_CTRL_SET(vop, post_scl_ctrl,
2008a2a3a29SSandy Huang 		     POST_HORIZONTAL_SCALEDOWN_EN(hdisplay != hsize) |
2018a2a3a29SSandy Huang 		     POST_VERTICAL_SCALEDOWN_EN(vdisplay != vsize));
2028a2a3a29SSandy Huang 	if (mode->flags & DRM_MODE_FLAG_INTERLACE) {
2038a2a3a29SSandy Huang 		u16 vact_st_f1 = vtotal + vact_st + 1;
2048a2a3a29SSandy Huang 		u16 vact_end_f1 = vact_st_f1 + vsize;
2058a2a3a29SSandy Huang 
2068a2a3a29SSandy Huang 		val = vact_st_f1 << 16 | vact_end_f1;
2078a2a3a29SSandy Huang 		VOP_CTRL_SET(vop, vpost_st_end_f1, val);
2088a2a3a29SSandy Huang 	}
2098a2a3a29SSandy Huang }
2108a2a3a29SSandy Huang 
21167b9012cSSandy Huang static void vop_mcu_mode(struct display_state *state, struct vop *vop)
21267b9012cSSandy Huang {
21367b9012cSSandy Huang 	struct crtc_state *crtc_state = &state->crtc_state;
21467b9012cSSandy Huang 
21567b9012cSSandy Huang 	VOP_CTRL_SET(vop, mcu_clk_sel, 1);
21667b9012cSSandy Huang 	VOP_CTRL_SET(vop, mcu_type, 1);
21767b9012cSSandy Huang 
21867b9012cSSandy Huang 	VOP_CTRL_SET(vop, mcu_hold_mode, 1);
21967b9012cSSandy Huang 	VOP_CTRL_SET(vop, mcu_pix_total, crtc_state->mcu_timing.mcu_pix_total);
22067b9012cSSandy Huang 	VOP_CTRL_SET(vop, mcu_cs_pst, crtc_state->mcu_timing.mcu_cs_pst);
22167b9012cSSandy Huang 	VOP_CTRL_SET(vop, mcu_cs_pend, crtc_state->mcu_timing.mcu_cs_pend);
22267b9012cSSandy Huang 	VOP_CTRL_SET(vop, mcu_rw_pst, crtc_state->mcu_timing.mcu_rw_pst);
22367b9012cSSandy Huang 	VOP_CTRL_SET(vop, mcu_rw_pend, crtc_state->mcu_timing.mcu_rw_pend);
22467b9012cSSandy Huang }
22567b9012cSSandy Huang 
226cf53642aSSandy Huang static int rockchip_vop_preinit(struct display_state *state)
227cf53642aSSandy Huang {
228cf53642aSSandy Huang 	const struct vop_data *vop_data = state->crtc_state.crtc->data;
229cf53642aSSandy Huang 
230cf53642aSSandy Huang 	state->crtc_state.max_output = vop_data->max_output;
231cf53642aSSandy Huang 
232cf53642aSSandy Huang 	return 0;
233cf53642aSSandy Huang }
234cf53642aSSandy Huang 
235186f8572SMark Yao static int rockchip_vop_init(struct display_state *state)
236186f8572SMark Yao {
237186f8572SMark Yao 	struct crtc_state *crtc_state = &state->crtc_state;
238186f8572SMark Yao 	struct connector_state *conn_state = &state->conn_state;
239186f8572SMark Yao 	struct drm_display_mode *mode = &conn_state->mode;
240186f8572SMark Yao 	const struct rockchip_crtc *crtc = crtc_state->crtc;
241186f8572SMark Yao 	const struct vop_data *vop_data = crtc->data;
242186f8572SMark Yao 	struct vop *vop;
243*fc8b0d66SDamon Ding 	struct regmap *map;
244ccd843b9SSandy Huang 	u16 hsync_len = mode->crtc_hsync_end - mode->crtc_hsync_start;
245ccd843b9SSandy Huang 	u16 hdisplay = mode->crtc_hdisplay;
246ccd843b9SSandy Huang 	u16 htotal = mode->crtc_htotal;
247ccd843b9SSandy Huang 	u16 hact_st = mode->crtc_htotal - mode->crtc_hsync_start;
248186f8572SMark Yao 	u16 hact_end = hact_st + hdisplay;
249ccd843b9SSandy Huang 	u16 vdisplay = mode->crtc_vdisplay;
250ccd843b9SSandy Huang 	u16 vtotal = mode->crtc_vtotal;
251ccd843b9SSandy Huang 	u16 vsync_len = mode->crtc_vsync_end - mode->crtc_vsync_start;
252ccd843b9SSandy Huang 	u16 vact_st = mode->crtc_vtotal - mode->crtc_vsync_start;
253186f8572SMark Yao 	u16 vact_end = vact_st + vdisplay;
254504d9922SSandy Huang 	struct clk dclk;
255960080c9SSandy Huang 	u32 val, act_end;
2564b8c2ef1SMark Yao 	int ret;
25779feefb1SSandy Huang 	bool yuv_overlay = false, post_r2y_en = false, post_y2r_en = false;
25879feefb1SSandy Huang 	u16 post_csc_mode;
2593a06149eSSandy Huang 	bool dclk_inv;
2602264c88bSDamon Ding 	char output_type_name[30] = {0};
261186f8572SMark Yao 
262186f8572SMark Yao 	vop = malloc(sizeof(*vop));
263186f8572SMark Yao 	if (!vop)
264186f8572SMark Yao 		return -ENOMEM;
265186f8572SMark Yao 	memset(vop, 0, sizeof(*vop));
266186f8572SMark Yao 
267186f8572SMark Yao 	crtc_state->private = vop;
268*fc8b0d66SDamon Ding 	vop->data = vop_data;
269e2bce6e4SKever Yang 	vop->regs = dev_read_addr_ptr(crtc_state->dev);
270186f8572SMark Yao 	vop->regsbak = malloc(vop_data->reg_len);
271186f8572SMark Yao 	vop->win = vop_data->win;
272186f8572SMark Yao 	vop->win_offset = vop_data->win_offset;
273186f8572SMark Yao 	vop->ctrl = vop_data->ctrl;
2743a06149eSSandy Huang 
275*fc8b0d66SDamon Ding 	map = syscon_regmap_lookup_by_phandle(crtc_state->dev, "rockchip,grf");
276*fc8b0d66SDamon Ding 	vop->grf_ctrl = regmap_get_range(map, 0);
277*fc8b0d66SDamon Ding 	if (vop->grf_ctrl <= 0)
278*fc8b0d66SDamon Ding 		printf("%s: Get syscon grf failed (ret=%p)\n", __func__, vop->grf_ctrl);
279*fc8b0d66SDamon Ding 
280186f8572SMark Yao 	vop->line_flag = vop_data->line_flag;
281b7618fd3SSandy Huang 	vop->csc_table = vop_data->csc_table;
282b7618fd3SSandy Huang 	vop->win_csc = vop_data->win_csc;
283186f8572SMark Yao 	vop->version = vop_data->version;
284186f8572SMark Yao 
2852264c88bSDamon Ding 	printf("VOP:0x%8p update mode to: %dx%d%s%d, type:%s\n",
2862264c88bSDamon Ding 	       vop->regs, mode->crtc_hdisplay, mode->vdisplay,
2872264c88bSDamon Ding 	       mode->flags & DRM_MODE_FLAG_INTERLACE ? "i" : "p",
2882264c88bSDamon Ding 	       mode->vrefresh,
2892264c88bSDamon Ding 	       rockchip_get_output_if_name(conn_state->output_if, output_type_name));
2902264c88bSDamon Ding 
291cadc8d74SKever Yang 	/* Process 'assigned-{clocks/clock-parents/clock-rates}' properties */
292cadc8d74SKever Yang 	ret = clk_set_defaults(crtc_state->dev);
293cadc8d74SKever Yang 	if (ret)
294cadc8d74SKever Yang 		debug("%s clk_set_defaults failed %d\n", __func__, ret);
295186f8572SMark Yao 
296186f8572SMark Yao 	ret = clk_get_by_name(crtc_state->dev, "dclk_vop", &dclk);
297186f8572SMark Yao 	if (!ret)
2984d64cedbSDamon Ding 		ret = clk_set_rate(&dclk, mode->crtc_clock * 1000);
299186f8572SMark Yao 	if (IS_ERR_VALUE(ret)) {
3004b8c2ef1SMark Yao 		printf("%s: Failed to set dclk: ret=%d\n", __func__, ret);
301186f8572SMark Yao 		return ret;
302186f8572SMark Yao 	}
3032264c88bSDamon Ding 	printf("VOP:0x%8p set crtc_clock to %dKHz\n", vop->regs, mode->crtc_clock);
304186f8572SMark Yao 
305186f8572SMark Yao 	memcpy(vop->regsbak, vop->regs, vop_data->reg_len);
306186f8572SMark Yao 
3074b8c2ef1SMark Yao 	rockchip_vop_init_gamma(vop, state);
308186f8572SMark Yao 
3096b898587SDamon Ding 	ret = gpio_request_by_name(crtc_state->dev, "mcu-rs-gpios",
3106b898587SDamon Ding 				   0, &vop->mcu_rs_gpio, GPIOD_IS_OUT);
3116b898587SDamon Ding 	if (ret && ret != -ENOENT)
3126b898587SDamon Ding 		printf("%s: Cannot get mcu rs GPIO: %d\n", __func__, ret);
3136b898587SDamon Ding 
314186f8572SMark Yao 	VOP_CTRL_SET(vop, global_regdone_en, 1);
3152b34f307SMark Yao 	VOP_CTRL_SET(vop, axi_outstanding_max_num, 30);
3162b34f307SMark Yao 	VOP_CTRL_SET(vop, axi_max_outstanding_en, 1);
317b0dbe9a0SSandy Huang 	VOP_CTRL_SET(vop, reg_done_frm, 1);
318186f8572SMark Yao 	VOP_CTRL_SET(vop, win_gate[0], 1);
319186f8572SMark Yao 	VOP_CTRL_SET(vop, win_gate[1], 1);
3202b34f307SMark Yao 	VOP_CTRL_SET(vop, win_channel[0], 0x12);
3212b34f307SMark Yao 	VOP_CTRL_SET(vop, win_channel[1], 0x34);
3222b34f307SMark Yao 	VOP_CTRL_SET(vop, win_channel[2], 0x56);
323186f8572SMark Yao 	VOP_CTRL_SET(vop, dsp_blank, 0);
324186f8572SMark Yao 
32513f658dcSDamon Ding 	dclk_inv = (conn_state->bus_flags & DRM_BUS_FLAG_PIXDATA_DRIVE_NEGEDGE) ? 1 : 0;
32654f7137bSDamon Ding 	/* For improving signal quality, dclk need to be inverted by default on rv1106. */
32754f7137bSDamon Ding 	if ((VOP_MAJOR(vop->version) == 2 && VOP_MINOR(vop->version) == 12))
32854f7137bSDamon Ding 		dclk_inv = !dclk_inv;
3293a06149eSSandy Huang 	VOP_CTRL_SET(vop, dclk_pol, dclk_inv);
3303a06149eSSandy Huang 
331186f8572SMark Yao 	val = 0x8;
332186f8572SMark Yao 	val |= (mode->flags & DRM_MODE_FLAG_NHSYNC) ? 0 : 1;
333186f8572SMark Yao 	val |= (mode->flags & DRM_MODE_FLAG_NVSYNC) ? 0 : (1 << 1);
334186f8572SMark Yao 	VOP_CTRL_SET(vop, pin_pol, val);
335186f8572SMark Yao 
336186f8572SMark Yao 	switch (conn_state->type) {
337186f8572SMark Yao 	case DRM_MODE_CONNECTOR_LVDS:
338186f8572SMark Yao 		VOP_CTRL_SET(vop, rgb_en, 1);
339186f8572SMark Yao 		VOP_CTRL_SET(vop, rgb_pin_pol, val);
3403a06149eSSandy Huang 		VOP_CTRL_SET(vop, rgb_dclk_pol, dclk_inv);
3413a06149eSSandy Huang 		VOP_CTRL_SET(vop, lvds_en, 1);
3423a06149eSSandy Huang 		VOP_CTRL_SET(vop, lvds_pin_pol, val);
3433a06149eSSandy Huang 		VOP_CTRL_SET(vop, lvds_dclk_pol, dclk_inv);
344*fc8b0d66SDamon Ding 		if (!IS_ERR_OR_NULL(vop->grf_ctrl))
345*fc8b0d66SDamon Ding 			VOP_GRF_SET(vop, grf_ctrl, grf_dclk_inv, dclk_inv);
346186f8572SMark Yao 		break;
347186f8572SMark Yao 	case DRM_MODE_CONNECTOR_eDP:
348186f8572SMark Yao 		VOP_CTRL_SET(vop, edp_en, 1);
349186f8572SMark Yao 		VOP_CTRL_SET(vop, edp_pin_pol, val);
3503a06149eSSandy Huang 		VOP_CTRL_SET(vop, edp_dclk_pol, dclk_inv);
351186f8572SMark Yao 		break;
352186f8572SMark Yao 	case DRM_MODE_CONNECTOR_HDMIA:
353186f8572SMark Yao 		VOP_CTRL_SET(vop, hdmi_en, 1);
354186f8572SMark Yao 		VOP_CTRL_SET(vop, hdmi_pin_pol, val);
3553a06149eSSandy Huang 		VOP_CTRL_SET(vop, hdmi_dclk_pol, 1);
356186f8572SMark Yao 		break;
357186f8572SMark Yao 	case DRM_MODE_CONNECTOR_DSI:
358186f8572SMark Yao 		VOP_CTRL_SET(vop, mipi_en, 1);
359186f8572SMark Yao 		VOP_CTRL_SET(vop, mipi_pin_pol, val);
3603a06149eSSandy Huang 		VOP_CTRL_SET(vop, mipi_dclk_pol, dclk_inv);
361186f8572SMark Yao 		VOP_CTRL_SET(vop, mipi_dual_channel_en,
362bee25ee6SGuochun Huang 			!!(conn_state->output_flags & ROCKCHIP_OUTPUT_DUAL_CHANNEL_LEFT_RIGHT_MODE));
363186f8572SMark Yao 		VOP_CTRL_SET(vop, data01_swap,
364bee25ee6SGuochun Huang 			!!(conn_state->output_flags & ROCKCHIP_OUTPUT_DATA_SWAP) ||
365289af5f4SSandy Huang 			crtc_state->dual_channel_swap);
366186f8572SMark Yao 		break;
3673a06149eSSandy Huang 	case DRM_MODE_CONNECTOR_DisplayPort:
3683a06149eSSandy Huang 		VOP_CTRL_SET(vop, dp_dclk_pol, 0);
3693a06149eSSandy Huang 		VOP_CTRL_SET(vop, dp_pin_pol, val);
3703a06149eSSandy Huang 		VOP_CTRL_SET(vop, dp_en, 1);
3713a06149eSSandy Huang 		break;
37209b01f9eSAlgea Cao 	case DRM_MODE_CONNECTOR_TV:
37309b01f9eSAlgea Cao 		if (vdisplay == CVBS_PAL_VDISPLAY)
37409b01f9eSAlgea Cao 			VOP_CTRL_SET(vop, tve_sw_mode, 1);
37509b01f9eSAlgea Cao 		else
37609b01f9eSAlgea Cao 			VOP_CTRL_SET(vop, tve_sw_mode, 0);
37709b01f9eSAlgea Cao 		VOP_CTRL_SET(vop, tve_dclk_pol, 1);
37809b01f9eSAlgea Cao 		VOP_CTRL_SET(vop, tve_dclk_en, 1);
37909b01f9eSAlgea Cao 		/* use the same pol reg with hdmi */
38009b01f9eSAlgea Cao 		VOP_CTRL_SET(vop, hdmi_pin_pol, val);
38109b01f9eSAlgea Cao 		VOP_CTRL_SET(vop, sw_genlock, 1);
38209b01f9eSAlgea Cao 		VOP_CTRL_SET(vop, sw_uv_offset_en, 1);
38309b01f9eSAlgea Cao 		VOP_CTRL_SET(vop, dither_up, 1);
38409b01f9eSAlgea Cao 		break;
385186f8572SMark Yao 	default:
386186f8572SMark Yao 		printf("unsupport connector_type[%d]\n", conn_state->type);
387186f8572SMark Yao 	}
388186f8572SMark Yao 
389186f8572SMark Yao 	if (conn_state->output_mode == ROCKCHIP_OUT_MODE_AAAA &&
390186f8572SMark Yao 	    !(vop_data->feature & VOP_FEATURE_OUTPUT_10BIT))
391186f8572SMark Yao 		conn_state->output_mode = ROCKCHIP_OUT_MODE_P888;
392186f8572SMark Yao 
393186f8572SMark Yao 	switch (conn_state->bus_format) {
394186f8572SMark Yao 	case MEDIA_BUS_FMT_RGB565_1X16:
395186f8572SMark Yao 		val = DITHER_DOWN_EN(1) | DITHER_DOWN_MODE(RGB888_TO_RGB565);
396186f8572SMark Yao 		break;
397186f8572SMark Yao 	case MEDIA_BUS_FMT_RGB666_1X18:
398186f8572SMark Yao 	case MEDIA_BUS_FMT_RGB666_1X24_CPADHI:
3999c5e1148SWyon Bi 	case MEDIA_BUS_FMT_RGB666_1X7X3_SPWG:
4009c5e1148SWyon Bi 	case MEDIA_BUS_FMT_RGB666_1X7X3_JEIDA:
401186f8572SMark Yao 		val = DITHER_DOWN_EN(1) | DITHER_DOWN_MODE(RGB888_TO_RGB666);
402186f8572SMark Yao 		break;
40379feefb1SSandy Huang 	case MEDIA_BUS_FMT_YUV8_1X24:
40479feefb1SSandy Huang 	case MEDIA_BUS_FMT_UYYVYY8_0_5X24:
40579feefb1SSandy Huang 		val = DITHER_DOWN_EN(0) | PRE_DITHER_DOWN_EN(1);
40679feefb1SSandy Huang 		break;
40779feefb1SSandy Huang 	case MEDIA_BUS_FMT_YUV10_1X30:
40879feefb1SSandy Huang 	case MEDIA_BUS_FMT_UYYVYY10_0_5X30:
40979feefb1SSandy Huang 		val = DITHER_DOWN_EN(0) | PRE_DITHER_DOWN_EN(0);
41079feefb1SSandy Huang 		break;
411186f8572SMark Yao 	case MEDIA_BUS_FMT_RGB888_1X24:
4129c5e1148SWyon Bi 	case MEDIA_BUS_FMT_RGB888_1X7X4_SPWG:
4139c5e1148SWyon Bi 	case MEDIA_BUS_FMT_RGB888_1X7X4_JEIDA:
414186f8572SMark Yao 	default:
415186f8572SMark Yao 		val = DITHER_DOWN_EN(0) | PRE_DITHER_DOWN_EN(0);
416186f8572SMark Yao 		break;
417186f8572SMark Yao 	}
418186f8572SMark Yao 	if (conn_state->output_mode == ROCKCHIP_OUT_MODE_AAAA)
419186f8572SMark Yao 		val |= PRE_DITHER_DOWN_EN(0);
420186f8572SMark Yao 	else
421186f8572SMark Yao 		val |= PRE_DITHER_DOWN_EN(1);
422186f8572SMark Yao 	val |= DITHER_DOWN_MODE_SEL(DITHER_DOWN_ALLEGRO);
423186f8572SMark Yao 	VOP_CTRL_SET(vop, dither_down, val);
424186f8572SMark Yao 
42579feefb1SSandy Huang 	VOP_CTRL_SET(vop, dclk_ddr,
42679feefb1SSandy Huang 		     conn_state->output_mode == ROCKCHIP_OUT_MODE_YUV420 ? 1 : 0);
42779feefb1SSandy Huang 	VOP_CTRL_SET(vop, hdmi_dclk_out_en,
42879feefb1SSandy Huang 		     conn_state->output_mode == ROCKCHIP_OUT_MODE_YUV420 ? 1 : 0);
42979feefb1SSandy Huang 
43080fbe788SDamon Ding 	if (is_uv_swap(conn_state->bus_format, conn_state->output_mode) ||
43180fbe788SDamon Ding 	    is_rb_swap(conn_state->bus_format, conn_state->output_mode))
43232328971SDamon Ding 		VOP_CTRL_SET(vop, dsp_rb_swap, 1);
43379feefb1SSandy Huang 	else
43479feefb1SSandy Huang 		VOP_CTRL_SET(vop, dsp_data_swap, 0);
43579feefb1SSandy Huang 
436186f8572SMark Yao 	VOP_CTRL_SET(vop, out_mode, conn_state->output_mode);
43779feefb1SSandy Huang 
43879feefb1SSandy Huang 	if (VOP_CTRL_SUPPORT(vop, overlay_mode)) {
43979feefb1SSandy Huang 		yuv_overlay = is_yuv_output(conn_state->bus_format);
44079feefb1SSandy Huang 		VOP_CTRL_SET(vop, overlay_mode, yuv_overlay);
44179feefb1SSandy Huang 	}
44279feefb1SSandy Huang 	/*
44379feefb1SSandy Huang 	 * todo: r2y for win csc
44479feefb1SSandy Huang 	 */
44579feefb1SSandy Huang 	VOP_CTRL_SET(vop, dsp_out_yuv, is_yuv_output(conn_state->bus_format));
44679feefb1SSandy Huang 
44779feefb1SSandy Huang 	if (yuv_overlay) {
44879feefb1SSandy Huang 		if (!is_yuv_output(conn_state->bus_format))
44979feefb1SSandy Huang 			post_y2r_en = true;
45079feefb1SSandy Huang 	} else {
45179feefb1SSandy Huang 		if (is_yuv_output(conn_state->bus_format))
45279feefb1SSandy Huang 			post_r2y_en = true;
45379feefb1SSandy Huang 	}
45479feefb1SSandy Huang 
455b7618fd3SSandy Huang 	crtc_state->yuv_overlay = yuv_overlay;
45679feefb1SSandy Huang 	post_csc_mode = to_vop_csc_mode(conn_state->color_space);
45779feefb1SSandy Huang 	VOP_CTRL_SET(vop, bcsh_r2y_en, post_r2y_en);
45879feefb1SSandy Huang 	VOP_CTRL_SET(vop, bcsh_y2r_en, post_y2r_en);
45979feefb1SSandy Huang 	VOP_CTRL_SET(vop, bcsh_r2y_csc_mode, post_csc_mode);
46079feefb1SSandy Huang 	VOP_CTRL_SET(vop, bcsh_y2r_csc_mode, post_csc_mode);
46179feefb1SSandy Huang 
46279feefb1SSandy Huang 	/*
46379feefb1SSandy Huang 	 * Background color is 10bit depth if vop version >= 3.5
46479feefb1SSandy Huang 	 */
46579feefb1SSandy Huang 	if (!is_yuv_output(conn_state->bus_format))
46679feefb1SSandy Huang 		val = 0;
46779feefb1SSandy Huang 	else if (VOP_MAJOR(vop->version) == 3 &&
46879feefb1SSandy Huang 		 VOP_MINOR(vop->version) >= 5)
46979feefb1SSandy Huang 		val = 0x20010200;
47079feefb1SSandy Huang 	else
47179feefb1SSandy Huang 		val = 0x801080;
47279feefb1SSandy Huang 	VOP_CTRL_SET(vop, dsp_background, val);
47379feefb1SSandy Huang 
474186f8572SMark Yao 	VOP_CTRL_SET(vop, htotal_pw, (htotal << 16) | hsync_len);
475186f8572SMark Yao 	val = hact_st << 16;
476186f8572SMark Yao 	val |= hact_end;
477186f8572SMark Yao 	VOP_CTRL_SET(vop, hact_st_end, val);
478186f8572SMark Yao 	val = vact_st << 16;
479186f8572SMark Yao 	val |= vact_end;
480186f8572SMark Yao 	VOP_CTRL_SET(vop, vact_st_end, val);
481ccd843b9SSandy Huang 	if (mode->flags & DRM_MODE_FLAG_INTERLACE) {
482ccd843b9SSandy Huang 		u16 vact_st_f1 = vtotal + vact_st + 1;
483ccd843b9SSandy Huang 		u16 vact_end_f1 = vact_st_f1 + vdisplay;
484ccd843b9SSandy Huang 
485ccd843b9SSandy Huang 		val = vact_st_f1 << 16 | vact_end_f1;
486ccd843b9SSandy Huang 		VOP_CTRL_SET(vop, vact_st_end_f1, val);
487ccd843b9SSandy Huang 
488ccd843b9SSandy Huang 		val = vtotal << 16 | (vtotal + vsync_len);
489ccd843b9SSandy Huang 		VOP_CTRL_SET(vop, vs_st_end_f1, val);
490ccd843b9SSandy Huang 		VOP_CTRL_SET(vop, dsp_interlace, 1);
491ccd843b9SSandy Huang 		VOP_CTRL_SET(vop, p2i_en, 1);
492ccd843b9SSandy Huang 		vtotal += vtotal + 1;
493960080c9SSandy Huang 		act_end = vact_end_f1;
494ccd843b9SSandy Huang 	} else {
495ccd843b9SSandy Huang 		VOP_CTRL_SET(vop, dsp_interlace, 0);
496ccd843b9SSandy Huang 		VOP_CTRL_SET(vop, p2i_en, 0);
497960080c9SSandy Huang 		act_end = vact_end;
498ccd843b9SSandy Huang 	}
499ccd843b9SSandy Huang 	VOP_CTRL_SET(vop, vtotal_pw, (vtotal << 16) | vsync_len);
5008a2a3a29SSandy Huang 	vop_post_config(state, vop);
501ccd843b9SSandy Huang 	VOP_CTRL_SET(vop, core_dclk_div,
502ccd843b9SSandy Huang 		     !!(mode->flags & DRM_MODE_FLAG_DBLCLK));
5038a2a3a29SSandy Huang 
504960080c9SSandy Huang 	VOP_LINE_FLAG_SET(vop, line_flag_num[0], act_end - 3);
505186f8572SMark Yao 	VOP_LINE_FLAG_SET(vop, line_flag_num[1],
506960080c9SSandy Huang 			  act_end - us_to_vertical_line(mode, 1000));
50767b9012cSSandy Huang 	if (state->crtc_state.mcu_timing.mcu_pix_total > 0)
50867b9012cSSandy Huang 		vop_mcu_mode(state, vop);
509186f8572SMark Yao 	vop_cfg_done(vop);
510186f8572SMark Yao 
511186f8572SMark Yao 	return 0;
512186f8572SMark Yao }
513186f8572SMark Yao 
514186f8572SMark Yao static uint16_t scl_vop_cal_scale(enum scale_mode mode, uint32_t src,
515186f8572SMark Yao 				  uint32_t dst, bool is_horizontal,
516186f8572SMark Yao 				  int vsu_mode, int *vskiplines)
517186f8572SMark Yao {
518186f8572SMark Yao 	uint16_t val = 1 << SCL_FT_DEFAULT_FIXPOINT_SHIFT;
519186f8572SMark Yao 
520186f8572SMark Yao 	if (is_horizontal) {
521186f8572SMark Yao 		if (mode == SCALE_UP)
522186f8572SMark Yao 			val = GET_SCL_FT_BIC(src, dst);
523186f8572SMark Yao 		else if (mode == SCALE_DOWN)
524186f8572SMark Yao 			val = GET_SCL_FT_BILI_DN(src, dst);
525186f8572SMark Yao 	} else {
526186f8572SMark Yao 		if (mode == SCALE_UP) {
527186f8572SMark Yao 			if (vsu_mode == SCALE_UP_BIL)
528186f8572SMark Yao 				val = GET_SCL_FT_BILI_UP(src, dst);
529186f8572SMark Yao 			else
530186f8572SMark Yao 				val = GET_SCL_FT_BIC(src, dst);
531186f8572SMark Yao 		} else if (mode == SCALE_DOWN) {
532186f8572SMark Yao 			if (vskiplines) {
533186f8572SMark Yao 				*vskiplines = scl_get_vskiplines(src, dst);
534186f8572SMark Yao 				val = scl_get_bili_dn_vskip(src, dst,
535186f8572SMark Yao 							    *vskiplines);
536186f8572SMark Yao 			} else {
537186f8572SMark Yao 				val = GET_SCL_FT_BILI_DN(src, dst);
538186f8572SMark Yao 			}
539186f8572SMark Yao 		}
540186f8572SMark Yao 	}
541186f8572SMark Yao 
542186f8572SMark Yao 	return val;
543186f8572SMark Yao }
544186f8572SMark Yao 
545186f8572SMark Yao static void scl_vop_cal_scl_fac(struct vop *vop,
546186f8572SMark Yao 				uint32_t src_w, uint32_t src_h, uint32_t dst_w,
547186f8572SMark Yao 				uint32_t dst_h, uint32_t pixel_format)
548186f8572SMark Yao {
549186f8572SMark Yao 	uint16_t yrgb_hor_scl_mode, yrgb_ver_scl_mode;
550186f8572SMark Yao 	uint16_t cbcr_hor_scl_mode = SCALE_NONE;
551186f8572SMark Yao 	uint16_t cbcr_ver_scl_mode = SCALE_NONE;
552186f8572SMark Yao 	int hsub = drm_format_horz_chroma_subsampling(pixel_format);
553186f8572SMark Yao 	int vsub = drm_format_vert_chroma_subsampling(pixel_format);
554186f8572SMark Yao 	bool is_yuv = false;
555186f8572SMark Yao 	uint16_t cbcr_src_w = src_w / hsub;
556186f8572SMark Yao 	uint16_t cbcr_src_h = src_h / vsub;
557186f8572SMark Yao 	uint16_t vsu_mode;
558186f8572SMark Yao 	uint16_t lb_mode;
559186f8572SMark Yao 	uint32_t val;
560186f8572SMark Yao 	int vskiplines = 0;
561186f8572SMark Yao 
562186f8572SMark Yao 	if (!vop->win->scl)
563186f8572SMark Yao 		return;
564186f8572SMark Yao 
565186f8572SMark Yao 	if (!vop->win->scl->ext) {
566186f8572SMark Yao 		VOP_SCL_SET(vop, scale_yrgb_x,
567186f8572SMark Yao 			    scl_cal_scale2(src_w, dst_w));
568186f8572SMark Yao 		VOP_SCL_SET(vop, scale_yrgb_y,
569186f8572SMark Yao 			    scl_cal_scale2(src_h, dst_h));
570186f8572SMark Yao 		if (is_yuv) {
571186f8572SMark Yao 			VOP_SCL_SET(vop, scale_cbcr_x,
572186f8572SMark Yao 				    scl_cal_scale2(src_w, dst_w));
573186f8572SMark Yao 			VOP_SCL_SET(vop, scale_cbcr_y,
574186f8572SMark Yao 				    scl_cal_scale2(src_h, dst_h));
575186f8572SMark Yao 		}
576186f8572SMark Yao 		return;
577186f8572SMark Yao 	}
578186f8572SMark Yao 
579186f8572SMark Yao 	yrgb_hor_scl_mode = scl_get_scl_mode(src_w, dst_w);
580186f8572SMark Yao 	yrgb_ver_scl_mode = scl_get_scl_mode(src_h, dst_h);
581186f8572SMark Yao 
582186f8572SMark Yao 	if (is_yuv) {
583186f8572SMark Yao 		cbcr_hor_scl_mode = scl_get_scl_mode(cbcr_src_w, dst_w);
584186f8572SMark Yao 		cbcr_ver_scl_mode = scl_get_scl_mode(cbcr_src_h, dst_h);
585186f8572SMark Yao 		if (cbcr_hor_scl_mode == SCALE_DOWN)
586186f8572SMark Yao 			lb_mode = scl_vop_cal_lb_mode(dst_w, true);
587186f8572SMark Yao 		else
588186f8572SMark Yao 			lb_mode = scl_vop_cal_lb_mode(cbcr_src_w, true);
589186f8572SMark Yao 	} else {
590186f8572SMark Yao 		if (yrgb_hor_scl_mode == SCALE_DOWN)
591186f8572SMark Yao 			lb_mode = scl_vop_cal_lb_mode(dst_w, false);
592186f8572SMark Yao 		else
593186f8572SMark Yao 			lb_mode = scl_vop_cal_lb_mode(src_w, false);
594186f8572SMark Yao 	}
595186f8572SMark Yao 
596186f8572SMark Yao 	VOP_SCL_SET_EXT(vop, lb_mode, lb_mode);
597186f8572SMark Yao 	if (lb_mode == LB_RGB_3840X2) {
598186f8572SMark Yao 		if (yrgb_ver_scl_mode != SCALE_NONE) {
599186f8572SMark Yao 			printf("ERROR : not allow yrgb ver scale\n");
600186f8572SMark Yao 			return;
601186f8572SMark Yao 		}
602186f8572SMark Yao 		if (cbcr_ver_scl_mode != SCALE_NONE) {
603186f8572SMark Yao 			printf("ERROR : not allow cbcr ver scale\n");
604186f8572SMark Yao 			return;
605186f8572SMark Yao 		}
606186f8572SMark Yao 		vsu_mode = SCALE_UP_BIL;
607186f8572SMark Yao 	} else if (lb_mode == LB_RGB_2560X4) {
608186f8572SMark Yao 		vsu_mode = SCALE_UP_BIL;
609186f8572SMark Yao 	} else {
610186f8572SMark Yao 		vsu_mode = SCALE_UP_BIC;
611186f8572SMark Yao 	}
612186f8572SMark Yao 
613186f8572SMark Yao 	val = scl_vop_cal_scale(yrgb_hor_scl_mode, src_w, dst_w,
614186f8572SMark Yao 				true, 0, NULL);
615186f8572SMark Yao 	VOP_SCL_SET(vop, scale_yrgb_x, val);
616186f8572SMark Yao 	val = scl_vop_cal_scale(yrgb_ver_scl_mode, src_h, dst_h,
617186f8572SMark Yao 				false, vsu_mode, &vskiplines);
618186f8572SMark Yao 	VOP_SCL_SET(vop, scale_yrgb_y, val);
619186f8572SMark Yao 
620186f8572SMark Yao 	VOP_SCL_SET_EXT(vop, vsd_yrgb_gt4, vskiplines == 4);
621186f8572SMark Yao 	VOP_SCL_SET_EXT(vop, vsd_yrgb_gt2, vskiplines == 2);
622186f8572SMark Yao 
623186f8572SMark Yao 	VOP_SCL_SET_EXT(vop, yrgb_hor_scl_mode, yrgb_hor_scl_mode);
624186f8572SMark Yao 	VOP_SCL_SET_EXT(vop, yrgb_ver_scl_mode, yrgb_ver_scl_mode);
625186f8572SMark Yao 	VOP_SCL_SET_EXT(vop, yrgb_hsd_mode, SCALE_DOWN_BIL);
626186f8572SMark Yao 	VOP_SCL_SET_EXT(vop, yrgb_vsd_mode, SCALE_DOWN_BIL);
627186f8572SMark Yao 	VOP_SCL_SET_EXT(vop, yrgb_vsu_mode, vsu_mode);
628186f8572SMark Yao 	if (is_yuv) {
629186f8572SMark Yao 		val = scl_vop_cal_scale(cbcr_hor_scl_mode, cbcr_src_w,
630186f8572SMark Yao 					dst_w, true, 0, NULL);
631186f8572SMark Yao 		VOP_SCL_SET(vop, scale_cbcr_x, val);
632186f8572SMark Yao 		val = scl_vop_cal_scale(cbcr_ver_scl_mode, cbcr_src_h,
633186f8572SMark Yao 					dst_h, false, vsu_mode, &vskiplines);
634186f8572SMark Yao 		VOP_SCL_SET(vop, scale_cbcr_y, val);
635186f8572SMark Yao 
636186f8572SMark Yao 		VOP_SCL_SET_EXT(vop, vsd_cbcr_gt4, vskiplines == 4);
637186f8572SMark Yao 		VOP_SCL_SET_EXT(vop, vsd_cbcr_gt2, vskiplines == 2);
638186f8572SMark Yao 		VOP_SCL_SET_EXT(vop, cbcr_hor_scl_mode, cbcr_hor_scl_mode);
639186f8572SMark Yao 		VOP_SCL_SET_EXT(vop, cbcr_ver_scl_mode, cbcr_ver_scl_mode);
640186f8572SMark Yao 		VOP_SCL_SET_EXT(vop, cbcr_hsd_mode, SCALE_DOWN_BIL);
641186f8572SMark Yao 		VOP_SCL_SET_EXT(vop, cbcr_vsd_mode, SCALE_DOWN_BIL);
642186f8572SMark Yao 		VOP_SCL_SET_EXT(vop, cbcr_vsu_mode, vsu_mode);
643186f8572SMark Yao 	}
644186f8572SMark Yao }
645186f8572SMark Yao 
646b7618fd3SSandy Huang static void vop_load_csc_table(struct vop *vop, u32 offset, const u32 *table)
647b7618fd3SSandy Huang {
648b7618fd3SSandy Huang 	int i;
649b7618fd3SSandy Huang 
650b7618fd3SSandy Huang 	/*
651b7618fd3SSandy Huang 	 * so far the csc offset is not 0 and in the feature the csc offset
652b7618fd3SSandy Huang 	 * impossible be 0, so when the offset is 0, should return here.
653b7618fd3SSandy Huang 	 */
654b7618fd3SSandy Huang 	if (!table || offset == 0)
655b7618fd3SSandy Huang 		return;
656b7618fd3SSandy Huang 
657b7618fd3SSandy Huang 	for (i = 0; i < 8; i++)
658b7618fd3SSandy Huang 		vop_writel(vop, offset + i * 4, table[i]);
659b7618fd3SSandy Huang }
660b7618fd3SSandy Huang 
661b7618fd3SSandy Huang static int rockchip_vop_setup_csc_table(struct display_state *state)
662b7618fd3SSandy Huang {
663b7618fd3SSandy Huang 	struct crtc_state *crtc_state = &state->crtc_state;
664b7618fd3SSandy Huang 	struct connector_state *conn_state = &state->conn_state;
665b7618fd3SSandy Huang 	struct vop *vop = crtc_state->private;
666b7618fd3SSandy Huang 	const uint32_t *csc_table = NULL;
667b7618fd3SSandy Huang 
668b7618fd3SSandy Huang 	if (!vop->csc_table || !crtc_state->yuv_overlay)
669b7618fd3SSandy Huang 		return 0;
670b7618fd3SSandy Huang 	/* todo: only implement r2y*/
671b7618fd3SSandy Huang 	switch (conn_state->color_space) {
672b7618fd3SSandy Huang 	case V4L2_COLORSPACE_SMPTE170M:
673b7618fd3SSandy Huang 		csc_table = vop->csc_table->r2y_bt601_12_235;
674b7618fd3SSandy Huang 		break;
675b7618fd3SSandy Huang 	case V4L2_COLORSPACE_REC709:
676b7618fd3SSandy Huang 	case V4L2_COLORSPACE_DEFAULT:
677b7618fd3SSandy Huang 	case V4L2_COLORSPACE_JPEG:
678b7618fd3SSandy Huang 		csc_table = vop->csc_table->r2y_bt709;
679b7618fd3SSandy Huang 		break;
680b7618fd3SSandy Huang 	case V4L2_COLORSPACE_BT2020:
681b7618fd3SSandy Huang 		csc_table = vop->csc_table->r2y_bt2020;
682b7618fd3SSandy Huang 		break;
683b7618fd3SSandy Huang 	default:
684b7618fd3SSandy Huang 		csc_table = vop->csc_table->r2y_bt601;
685b7618fd3SSandy Huang 		break;
686b7618fd3SSandy Huang 	}
687b7618fd3SSandy Huang 
688b7618fd3SSandy Huang 	vop_load_csc_table(vop, vop->win_csc->r2y_offset, csc_table);
689b7618fd3SSandy Huang 	VOP_WIN_CSC_SET(vop, r2y_en, 1);
690b7618fd3SSandy Huang 
691b7618fd3SSandy Huang 	return 0;
692b7618fd3SSandy Huang }
693b7618fd3SSandy Huang 
694186f8572SMark Yao static int rockchip_vop_set_plane(struct display_state *state)
695186f8572SMark Yao {
696186f8572SMark Yao 	struct crtc_state *crtc_state = &state->crtc_state;
697d4a9114aSShixiang Zheng 	const struct rockchip_crtc *crtc = crtc_state->crtc;
698d4a9114aSShixiang Zheng 	const struct vop_data *vop_data = crtc->data;
699186f8572SMark Yao 	struct connector_state *conn_state = &state->conn_state;
700186f8572SMark Yao 	struct drm_display_mode *mode = &conn_state->mode;
701186f8572SMark Yao 	u32 act_info, dsp_info, dsp_st, dsp_stx, dsp_sty;
702186f8572SMark Yao 	struct vop *vop = crtc_state->private;
703ee01dbb2SDamon Ding 	int src_w = crtc_state->src_rect.w;
704ee01dbb2SDamon Ding 	int src_h = crtc_state->src_rect.h;
705ee01dbb2SDamon Ding 	int crtc_x = crtc_state->crtc_rect.x;
706ee01dbb2SDamon Ding 	int crtc_y = crtc_state->crtc_rect.y;
707ee01dbb2SDamon Ding 	int crtc_w = crtc_state->crtc_rect.w;
708ee01dbb2SDamon Ding 	int crtc_h = crtc_state->crtc_rect.h;
709186f8572SMark Yao 	int xvir = crtc_state->xvir;
710ffa55e18SShixiang Zheng 	int x_mirror = 0, y_mirror = 0;
711186f8572SMark Yao 
712eee28ceaSSandy Huang 	if (crtc_w > crtc_state->max_output.width) {
713eee28ceaSSandy Huang 		printf("ERROR: output w[%d] exceeded max width[%d]\n",
714eee28ceaSSandy Huang 		       crtc_w, crtc_state->max_output.width);
715cf53642aSSandy Huang 		return -EINVAL;
716cf53642aSSandy Huang 	}
717cf53642aSSandy Huang 
718186f8572SMark Yao 	act_info = (src_h - 1) << 16;
719186f8572SMark Yao 	act_info |= (src_w - 1) & 0xffff;
720186f8572SMark Yao 
721186f8572SMark Yao 	dsp_info = (crtc_h - 1) << 16;
722186f8572SMark Yao 	dsp_info |= (crtc_w - 1) & 0xffff;
723186f8572SMark Yao 
72444006808SAlgea Cao 	dsp_stx = crtc_x + mode->crtc_htotal - mode->crtc_hsync_start;
72544006808SAlgea Cao 	dsp_sty = crtc_y + mode->crtc_vtotal - mode->crtc_vsync_start;
726186f8572SMark Yao 	dsp_st = dsp_sty << 16 | (dsp_stx & 0xffff);
727d4a9114aSShixiang Zheng 	/*
728f0e8414bSSandy Huang 	 * vop full need to treats rgb888 as bgr888 so we reverse the rb swap to workaround
729d4a9114aSShixiang Zheng 	 */
730f0e8414bSSandy Huang 	if (crtc_state->format == ROCKCHIP_FMT_RGB888 && VOP_MAJOR(vop_data->version) == 3)
731d4a9114aSShixiang Zheng 		crtc_state->rb_swap = !crtc_state->rb_swap;
732186f8572SMark Yao 
733ffa55e18SShixiang Zheng 	if (mode->flags & DRM_MODE_FLAG_YMIRROR)
734ffa55e18SShixiang Zheng 		y_mirror = 1;
735ffa55e18SShixiang Zheng 	else
736ffa55e18SShixiang Zheng 		y_mirror = 0;
737ffa55e18SShixiang Zheng 	if (mode->flags & DRM_MODE_FLAG_XMIRROR)
738ffa55e18SShixiang Zheng 		x_mirror = 1;
739ffa55e18SShixiang Zheng 	else
740ffa55e18SShixiang Zheng 		x_mirror = 0;
741ffa55e18SShixiang Zheng 	if (crtc_state->ymirror ^ y_mirror)
742ffa55e18SShixiang Zheng 		y_mirror = 1;
743ffa55e18SShixiang Zheng 	else
744ffa55e18SShixiang Zheng 		y_mirror = 0;
745ffa55e18SShixiang Zheng 	if (y_mirror) {
746ffa55e18SShixiang Zheng 		if (VOP_CTRL_SUPPORT(vop, ymirror))
747186f8572SMark Yao 			crtc_state->dma_addr += (src_h - 1) * xvir * 4;
748620af6a3SSandy Huang 		else
749ffa55e18SShixiang Zheng 			y_mirror = 0;
750620af6a3SSandy Huang 		}
751ffa55e18SShixiang Zheng 	VOP_CTRL_SET(vop, ymirror, y_mirror);
752ffa55e18SShixiang Zheng 	VOP_CTRL_SET(vop, xmirror, x_mirror);
753ffa55e18SShixiang Zheng 
754186f8572SMark Yao 	VOP_WIN_SET(vop, format, crtc_state->format);
755186f8572SMark Yao 	VOP_WIN_SET(vop, yrgb_vir, xvir);
756186f8572SMark Yao 	VOP_WIN_SET(vop, yrgb_mst, crtc_state->dma_addr);
757186f8572SMark Yao 
758186f8572SMark Yao 	scl_vop_cal_scl_fac(vop, src_w, src_h, crtc_w, crtc_h,
759186f8572SMark Yao 			    crtc_state->format);
760186f8572SMark Yao 
761186f8572SMark Yao 	VOP_WIN_SET(vop, act_info, act_info);
762186f8572SMark Yao 	VOP_WIN_SET(vop, dsp_info, dsp_info);
763186f8572SMark Yao 	VOP_WIN_SET(vop, dsp_st, dsp_st);
764186f8572SMark Yao 	VOP_WIN_SET(vop, rb_swap, crtc_state->rb_swap);
765186f8572SMark Yao 
766186f8572SMark Yao 	VOP_WIN_SET(vop, src_alpha_ctl, 0);
767186f8572SMark Yao 
768b7618fd3SSandy Huang 	rockchip_vop_setup_csc_table(state);
769186f8572SMark Yao 	VOP_WIN_SET(vop, enable, 1);
770c33e1feaSAndy Yan 	VOP_WIN_SET(vop, gate, 1);
771186f8572SMark Yao 	vop_cfg_done(vop);
772186f8572SMark Yao 
773186f8572SMark Yao 	return 0;
774186f8572SMark Yao }
775186f8572SMark Yao 
776186f8572SMark Yao static int rockchip_vop_prepare(struct display_state *state)
777186f8572SMark Yao {
778186f8572SMark Yao 	return 0;
779186f8572SMark Yao }
780186f8572SMark Yao 
781186f8572SMark Yao static int rockchip_vop_enable(struct display_state *state)
782186f8572SMark Yao {
783186f8572SMark Yao 	struct crtc_state *crtc_state = &state->crtc_state;
784186f8572SMark Yao 	struct vop *vop = crtc_state->private;
785186f8572SMark Yao 
786186f8572SMark Yao 	VOP_CTRL_SET(vop, standby, 0);
787186f8572SMark Yao 	vop_cfg_done(vop);
78867b9012cSSandy Huang 	if (crtc_state->mcu_timing.mcu_pix_total > 0)
78967b9012cSSandy Huang 		VOP_CTRL_SET(vop, mcu_hold_mode, 0);
790186f8572SMark Yao 
791186f8572SMark Yao 	return 0;
792186f8572SMark Yao }
793186f8572SMark Yao 
794186f8572SMark Yao static int rockchip_vop_disable(struct display_state *state)
795186f8572SMark Yao {
796186f8572SMark Yao 	struct crtc_state *crtc_state = &state->crtc_state;
797186f8572SMark Yao 	struct vop *vop = crtc_state->private;
798186f8572SMark Yao 
799186f8572SMark Yao 	VOP_CTRL_SET(vop, standby, 1);
800186f8572SMark Yao 	vop_cfg_done(vop);
801186f8572SMark Yao 	return 0;
802186f8572SMark Yao }
803186f8572SMark Yao 
804186f8572SMark Yao static int rockchip_vop_fixup_dts(struct display_state *state, void *blob)
805186f8572SMark Yao {
806e2bce6e4SKever Yang #if 0
807186f8572SMark Yao 	struct crtc_state *crtc_state = &state->crtc_state;
808186f8572SMark Yao 	struct panel_state *pstate = &state->panel_state;
809186f8572SMark Yao 	uint32_t phandle;
810186f8572SMark Yao 	char path[100];
811186f8572SMark Yao 	int ret, dsp_lut_node;
812186f8572SMark Yao 
813e2bce6e4SKever Yang 	if (!ofnode_valid(pstate->dsp_lut_node))
814186f8572SMark Yao 		return 0;
815186f8572SMark Yao 	ret = fdt_get_path(state->blob, pstate->dsp_lut_node, path, sizeof(path));
816186f8572SMark Yao 	if (ret < 0) {
817186f8572SMark Yao 		printf("failed to get dsp_lut path[%s], ret=%d\n",
818186f8572SMark Yao 			path, ret);
819186f8572SMark Yao 		return ret;
820186f8572SMark Yao 	}
821186f8572SMark Yao 
822186f8572SMark Yao 	dsp_lut_node = fdt_path_offset(blob, path);
823186f8572SMark Yao 	phandle = fdt_get_phandle(blob, dsp_lut_node);
824186f8572SMark Yao 	if (!phandle) {
825186f8572SMark Yao 		phandle = fdt_alloc_phandle(blob);
826186f8572SMark Yao 		if (!phandle) {
827186f8572SMark Yao 			printf("failed to alloc phandle\n");
828186f8572SMark Yao 			return -ENOMEM;
829186f8572SMark Yao 		}
830186f8572SMark Yao 
831186f8572SMark Yao 		fdt_set_phandle(blob, dsp_lut_node, phandle);
832186f8572SMark Yao 	}
833186f8572SMark Yao 
834186f8572SMark Yao 	ret = fdt_get_path(state->blob, crtc_state->node, path, sizeof(path));
835186f8572SMark Yao 	if (ret < 0) {
836186f8572SMark Yao 		printf("failed to get route path[%s], ret=%d\n",
837186f8572SMark Yao 			path, ret);
838186f8572SMark Yao 		return ret;
839186f8572SMark Yao 	}
840186f8572SMark Yao 
841186f8572SMark Yao 	do_fixup_by_path_u32(blob, path, "dsp-lut", phandle, 1);
842e2bce6e4SKever Yang #endif
843186f8572SMark Yao 	return 0;
844186f8572SMark Yao }
845186f8572SMark Yao 
84667b9012cSSandy Huang static int rockchip_vop_send_mcu_cmd(struct display_state *state,
84767b9012cSSandy Huang 				     u32 type, u32 value)
84867b9012cSSandy Huang {
84967b9012cSSandy Huang 	struct crtc_state *crtc_state = &state->crtc_state;
85067b9012cSSandy Huang 	struct vop *vop = crtc_state->private;
85167b9012cSSandy Huang 
85267b9012cSSandy Huang 	if (vop) {
85367b9012cSSandy Huang 		switch (type) {
85467b9012cSSandy Huang 		case MCU_WRCMD:
8556b898587SDamon Ding 			set_vop_mcu_rs(vop, 0);
85667b9012cSSandy Huang 			VOP_CTRL_SET(vop, mcu_rw_bypass_port, value);
8576b898587SDamon Ding 			set_vop_mcu_rs(vop, 1);
85867b9012cSSandy Huang 			break;
85967b9012cSSandy Huang 		case MCU_WRDATA:
8606b898587SDamon Ding 			set_vop_mcu_rs(vop, 1);
86167b9012cSSandy Huang 			VOP_CTRL_SET(vop, mcu_rw_bypass_port, value);
86267b9012cSSandy Huang 			break;
86367b9012cSSandy Huang 		case MCU_SETBYPASS:
86467b9012cSSandy Huang 			VOP_CTRL_SET(vop, mcu_bypass, value ? 1 : 0);
86567b9012cSSandy Huang 			break;
86667b9012cSSandy Huang 		default:
86767b9012cSSandy Huang 			break;
86867b9012cSSandy Huang 		}
86967b9012cSSandy Huang 	}
87067b9012cSSandy Huang 
87167b9012cSSandy Huang 	return 0;
87267b9012cSSandy Huang }
87367b9012cSSandy Huang 
87422007755SDamon Ding static int rockchip_vop_mode_valid(struct display_state *state)
87522007755SDamon Ding {
87622007755SDamon Ding 	struct connector_state *conn_state = &state->conn_state;
87722007755SDamon Ding 	struct drm_display_mode *mode = &conn_state->mode;
87822007755SDamon Ding 	struct videomode vm;
87922007755SDamon Ding 
88022007755SDamon Ding 	drm_display_mode_to_videomode(mode, &vm);
88122007755SDamon Ding 
88222007755SDamon Ding 	if (vm.hactive < 32 || vm.vactive < 32 ||
88322007755SDamon Ding 	    (vm.hfront_porch * vm.hsync_len * vm.hback_porch *
88422007755SDamon Ding 	     vm.vfront_porch * vm.vsync_len * vm.vback_porch == 0)) {
88522007755SDamon Ding 		printf("ERROR: unsupported display timing\n");
88622007755SDamon Ding 		return -EINVAL;
88722007755SDamon Ding 	}
88822007755SDamon Ding 
88922007755SDamon Ding 	return 0;
89022007755SDamon Ding }
89122007755SDamon Ding 
8924c765862SDamon Ding static int rockchip_vop_plane_check(struct display_state *state)
8934c765862SDamon Ding {
8944c765862SDamon Ding 	struct crtc_state *crtc_state = &state->crtc_state;
8954c765862SDamon Ding 	const struct rockchip_crtc *crtc = crtc_state->crtc;
8964c765862SDamon Ding 	const struct vop_data *vop_data = crtc->data;
8974c765862SDamon Ding 	const struct vop_win *win = vop_data->win;
8984c765862SDamon Ding 	struct display_rect *src = &crtc_state->src_rect;
8994c765862SDamon Ding 	struct display_rect *dst = &crtc_state->crtc_rect;
9004c765862SDamon Ding 	int min_scale, max_scale;
9014c765862SDamon Ding 	int hscale, vscale;
9024c765862SDamon Ding 
9034c765862SDamon Ding 	min_scale = win->scl ? FRAC_16_16(1, 8) : VOP_PLANE_NO_SCALING;
9044c765862SDamon Ding 	max_scale = win->scl ? FRAC_16_16(8, 1) : VOP_PLANE_NO_SCALING;
9054c765862SDamon Ding 
9064c765862SDamon Ding 	hscale = display_rect_calc_hscale(src, dst, min_scale, max_scale);
9074c765862SDamon Ding 	vscale = display_rect_calc_vscale(src, dst, min_scale, max_scale);
9084c765862SDamon Ding 	if (hscale < 0 || vscale < 0) {
9094c765862SDamon Ding 		printf("ERROR: scale factor is out of range\n");
9104c765862SDamon Ding 		return -ERANGE;
9114c765862SDamon Ding 	}
9124c765862SDamon Ding 
9134c765862SDamon Ding 	return 0;
9144c765862SDamon Ding }
9154c765862SDamon Ding 
9161b5811e7SDamon Ding static int rockchip_vop_mode_fixup(struct display_state *state)
9171b5811e7SDamon Ding {
9184d64cedbSDamon Ding 	struct crtc_state *crtc_state = &state->crtc_state;
9191b5811e7SDamon Ding 	struct connector_state *conn_state = &state->conn_state;
9201b5811e7SDamon Ding 	struct drm_display_mode *mode = &conn_state->mode;
9211b5811e7SDamon Ding 
9221b5811e7SDamon Ding 	drm_mode_set_crtcinfo(mode, CRTC_INTERLACE_HALVE_V | CRTC_STEREO_DOUBLE);
9231b5811e7SDamon Ding 
9244d64cedbSDamon Ding 	mode->crtc_clock *= rockchip_drm_get_cycles_per_pixel(conn_state->bus_format);
9254d64cedbSDamon Ding 	if (crtc_state->mcu_timing.mcu_pix_total)
9264d64cedbSDamon Ding 		mode->crtc_clock *= crtc_state->mcu_timing.mcu_pix_total + 1;
9274d64cedbSDamon Ding 
9281b5811e7SDamon Ding 	return 0;
9291b5811e7SDamon Ding }
9301b5811e7SDamon Ding 
931186f8572SMark Yao const struct rockchip_crtc_funcs rockchip_vop_funcs = {
932cf53642aSSandy Huang 	.preinit = rockchip_vop_preinit,
933186f8572SMark Yao 	.init = rockchip_vop_init,
934186f8572SMark Yao 	.set_plane = rockchip_vop_set_plane,
935186f8572SMark Yao 	.prepare = rockchip_vop_prepare,
936186f8572SMark Yao 	.enable = rockchip_vop_enable,
937186f8572SMark Yao 	.disable = rockchip_vop_disable,
938186f8572SMark Yao 	.fixup_dts = rockchip_vop_fixup_dts,
93967b9012cSSandy Huang 	.send_mcu_cmd = rockchip_vop_send_mcu_cmd,
94022007755SDamon Ding 	.mode_valid = rockchip_vop_mode_valid,
9414c765862SDamon Ding 	.plane_check = rockchip_vop_plane_check,
9421b5811e7SDamon Ding 	.mode_fixup = rockchip_vop_mode_fixup,
943186f8572SMark Yao };
944