xref: /rk3399_rockchip-uboot/drivers/video/drm/rockchip_vop.c (revision fc8b0d66767c032e08f1d74da5d0fcc366d9ae00)
1 /*
2  * (C) Copyright 2008-2017 Fuzhou Rockchip Electronics Co., Ltd
3  *
4  * SPDX-License-Identifier:	GPL-2.0+
5  */
6 
7 #include <config.h>
8 #include <common.h>
9 #include <errno.h>
10 #include <malloc.h>
11 #include <fdtdec.h>
12 #include <fdt_support.h>
13 #include <asm/unaligned.h>
14 #include <asm/io.h>
15 #include <linux/list.h>
16 #include <linux/media-bus-format.h>
17 #include <clk.h>
18 #include <asm/arch/clock.h>
19 #include <linux/err.h>
20 #include <dm/device.h>
21 #include <dm/read.h>
22 #include <syscon.h>
23 #include <regmap.h>
24 
25 #include "rockchip_display.h"
26 #include "rockchip_crtc.h"
27 #include "rockchip_connector.h"
28 #include "rockchip_vop.h"
29 
30 static inline int us_to_vertical_line(struct drm_display_mode *mode, int us)
31 {
32 	return us * mode->clock / mode->htotal / 1000;
33 }
34 
35 static inline void set_vop_mcu_rs(struct vop *vop, int v)
36 {
37 	if (dm_gpio_is_valid(&vop->mcu_rs_gpio))
38 		dm_gpio_set_value(&vop->mcu_rs_gpio, v);
39 	else
40 		VOP_CTRL_SET(vop, mcu_rs, v);
41 }
42 
43 static int to_vop_csc_mode(int csc_mode)
44 {
45 	switch (csc_mode) {
46 	case V4L2_COLORSPACE_SMPTE170M:
47 		return CSC_BT601L;
48 	case V4L2_COLORSPACE_REC709:
49 	case V4L2_COLORSPACE_DEFAULT:
50 		return CSC_BT709L;
51 	case V4L2_COLORSPACE_JPEG:
52 		return CSC_BT601F;
53 	case V4L2_COLORSPACE_BT2020:
54 		return CSC_BT2020;
55 	default:
56 		return CSC_BT709L;
57 	}
58 }
59 
60 static bool is_yuv_output(uint32_t bus_format)
61 {
62 	switch (bus_format) {
63 	case MEDIA_BUS_FMT_YUV8_1X24:
64 	case MEDIA_BUS_FMT_YUV10_1X30:
65 	case MEDIA_BUS_FMT_UYYVYY8_0_5X24:
66 	case MEDIA_BUS_FMT_UYYVYY10_0_5X30:
67 	case MEDIA_BUS_FMT_YUYV8_2X8:
68 	case MEDIA_BUS_FMT_YVYU8_2X8:
69 	case MEDIA_BUS_FMT_UYVY8_2X8:
70 	case MEDIA_BUS_FMT_VYUY8_2X8:
71 	case MEDIA_BUS_FMT_YUYV8_1X16:
72 	case MEDIA_BUS_FMT_YVYU8_1X16:
73 	case MEDIA_BUS_FMT_UYVY8_1X16:
74 	case MEDIA_BUS_FMT_VYUY8_1X16:
75 		return true;
76 	default:
77 		return false;
78 	}
79 }
80 
81 static bool is_uv_swap(uint32_t bus_format, uint32_t output_mode)
82 {
83 	/*
84 	 * FIXME:
85 	 *
86 	 * There is no media type for YUV444 output,
87 	 * so when out_mode is AAAA or P888, assume output is YUV444 on
88 	 * yuv format.
89 	 *
90 	 * From H/W testing, YUV444 mode need a rb swap.
91 	 */
92 	if ((bus_format == MEDIA_BUS_FMT_YUV8_1X24 ||
93 	     bus_format == MEDIA_BUS_FMT_YUV10_1X30) &&
94 	    (output_mode == ROCKCHIP_OUT_MODE_AAAA ||
95 	     output_mode == ROCKCHIP_OUT_MODE_P888))
96 		return true;
97 	else
98 		return false;
99 }
100 
101 static bool is_rb_swap(uint32_t bus_format, uint32_t output_mode)
102 {
103 	/*
104 	 * The default component order of serial rgb3x8 formats
105 	 * is BGR. So it is needed to enable RB swap.
106 	 */
107 	if (bus_format == MEDIA_BUS_FMT_RGB888_3X8 ||
108 	    bus_format == MEDIA_BUS_FMT_RGB888_DUMMY_4X8)
109 		return true;
110 	else
111 		return false;
112 }
113 
114 static int rockchip_vop_init_gamma(struct vop *vop, struct display_state *state)
115 {
116 	struct crtc_state *crtc_state = &state->crtc_state;
117 	struct connector_state *conn_state = &state->conn_state;
118 	u32 *lut = conn_state->gamma.lut;
119 	fdt_size_t lut_size;
120 	int i, lut_len;
121 	u32 *lut_regs;
122 
123 	if (!conn_state->gamma.lut)
124 		return 0;
125 
126 	i = dev_read_stringlist_search(crtc_state->dev, "reg-names", "gamma_lut");
127 	if (i < 0) {
128 		printf("Warning: vop not support gamma\n");
129 		return 0;
130 	}
131 	lut_regs = (u32 *)dev_read_addr_size(crtc_state->dev, "reg", &lut_size);
132 	if (lut_regs == (u32 *)FDT_ADDR_T_NONE) {
133 		printf("failed to get gamma lut register\n");
134 		return 0;
135 	}
136 	lut_len = lut_size / 4;
137 	if (lut_len != 256 && lut_len != 1024) {
138 		printf("Warning: unsupport gamma lut table[%d]\n", lut_len);
139 		return 0;
140 	}
141 
142 	if (conn_state->gamma.size != lut_len) {
143 		int size = conn_state->gamma.size;
144 		u32 j, r, g, b, color;
145 
146 		for (i = 0; i < lut_len; i++) {
147 			j = i * size / lut_len;
148 			r = lut[j] / size / size * lut_len / size;
149 			g = lut[j] / size % size * lut_len / size;
150 			b = lut[j] % size * lut_len / size;
151 			color = r * lut_len * lut_len + g * lut_len + b;
152 
153 			writel(color, lut_regs + (i << 2));
154 		}
155 	} else {
156 		for (i = 0; i < lut_len; i++)
157 			writel(lut[i], lut_regs + (i << 2));
158 	}
159 
160 	VOP_CTRL_SET(vop, dsp_lut_en, 1);
161 	VOP_CTRL_SET(vop, update_gamma_lut, 1);
162 
163 	return 0;
164 }
165 
166 static void vop_post_config(struct display_state *state, struct vop *vop)
167 {
168 	struct connector_state *conn_state = &state->conn_state;
169 	struct drm_display_mode *mode = &conn_state->mode;
170 	u16 vtotal = mode->crtc_vtotal;
171 	u16 hact_st = mode->crtc_htotal - mode->crtc_hsync_start;
172 	u16 vact_st = mode->crtc_vtotal - mode->crtc_vsync_start;
173 	u16 hdisplay = mode->crtc_hdisplay;
174 	u16 vdisplay = mode->crtc_vdisplay;
175 	u16 hsize = hdisplay * (conn_state->overscan.left_margin + conn_state->overscan.right_margin) / 200;
176 	u16 vsize = vdisplay * (conn_state->overscan.top_margin + conn_state->overscan.bottom_margin) / 200;
177 	u16 hact_end, vact_end;
178 	u32 val;
179 
180 	if (mode->flags & DRM_MODE_FLAG_INTERLACE)
181 		vsize = round_down(vsize, 2);
182 
183 	hact_st += hdisplay * (100 - conn_state->overscan.left_margin) / 200;
184 	hact_end = hact_st + hsize;
185 	val = hact_st << 16;
186 	val |= hact_end;
187 
188 	VOP_CTRL_SET(vop, hpost_st_end, val);
189 	vact_st += vdisplay * (100 - conn_state->overscan.top_margin) / 200;
190 	vact_end = vact_st + vsize;
191 	val = vact_st << 16;
192 	val |= vact_end;
193 	VOP_CTRL_SET(vop, vpost_st_end, val);
194 	val = scl_cal_scale2(vdisplay, vsize) << 16;
195 	val |= scl_cal_scale2(hdisplay, hsize);
196 	VOP_CTRL_SET(vop, post_scl_factor, val);
197 #define POST_HORIZONTAL_SCALEDOWN_EN(x)		((x) << 0)
198 #define POST_VERTICAL_SCALEDOWN_EN(x)		((x) << 1)
199 	VOP_CTRL_SET(vop, post_scl_ctrl,
200 		     POST_HORIZONTAL_SCALEDOWN_EN(hdisplay != hsize) |
201 		     POST_VERTICAL_SCALEDOWN_EN(vdisplay != vsize));
202 	if (mode->flags & DRM_MODE_FLAG_INTERLACE) {
203 		u16 vact_st_f1 = vtotal + vact_st + 1;
204 		u16 vact_end_f1 = vact_st_f1 + vsize;
205 
206 		val = vact_st_f1 << 16 | vact_end_f1;
207 		VOP_CTRL_SET(vop, vpost_st_end_f1, val);
208 	}
209 }
210 
211 static void vop_mcu_mode(struct display_state *state, struct vop *vop)
212 {
213 	struct crtc_state *crtc_state = &state->crtc_state;
214 
215 	VOP_CTRL_SET(vop, mcu_clk_sel, 1);
216 	VOP_CTRL_SET(vop, mcu_type, 1);
217 
218 	VOP_CTRL_SET(vop, mcu_hold_mode, 1);
219 	VOP_CTRL_SET(vop, mcu_pix_total, crtc_state->mcu_timing.mcu_pix_total);
220 	VOP_CTRL_SET(vop, mcu_cs_pst, crtc_state->mcu_timing.mcu_cs_pst);
221 	VOP_CTRL_SET(vop, mcu_cs_pend, crtc_state->mcu_timing.mcu_cs_pend);
222 	VOP_CTRL_SET(vop, mcu_rw_pst, crtc_state->mcu_timing.mcu_rw_pst);
223 	VOP_CTRL_SET(vop, mcu_rw_pend, crtc_state->mcu_timing.mcu_rw_pend);
224 }
225 
226 static int rockchip_vop_preinit(struct display_state *state)
227 {
228 	const struct vop_data *vop_data = state->crtc_state.crtc->data;
229 
230 	state->crtc_state.max_output = vop_data->max_output;
231 
232 	return 0;
233 }
234 
235 static int rockchip_vop_init(struct display_state *state)
236 {
237 	struct crtc_state *crtc_state = &state->crtc_state;
238 	struct connector_state *conn_state = &state->conn_state;
239 	struct drm_display_mode *mode = &conn_state->mode;
240 	const struct rockchip_crtc *crtc = crtc_state->crtc;
241 	const struct vop_data *vop_data = crtc->data;
242 	struct vop *vop;
243 	struct regmap *map;
244 	u16 hsync_len = mode->crtc_hsync_end - mode->crtc_hsync_start;
245 	u16 hdisplay = mode->crtc_hdisplay;
246 	u16 htotal = mode->crtc_htotal;
247 	u16 hact_st = mode->crtc_htotal - mode->crtc_hsync_start;
248 	u16 hact_end = hact_st + hdisplay;
249 	u16 vdisplay = mode->crtc_vdisplay;
250 	u16 vtotal = mode->crtc_vtotal;
251 	u16 vsync_len = mode->crtc_vsync_end - mode->crtc_vsync_start;
252 	u16 vact_st = mode->crtc_vtotal - mode->crtc_vsync_start;
253 	u16 vact_end = vact_st + vdisplay;
254 	struct clk dclk;
255 	u32 val, act_end;
256 	int ret;
257 	bool yuv_overlay = false, post_r2y_en = false, post_y2r_en = false;
258 	u16 post_csc_mode;
259 	bool dclk_inv;
260 	char output_type_name[30] = {0};
261 
262 	vop = malloc(sizeof(*vop));
263 	if (!vop)
264 		return -ENOMEM;
265 	memset(vop, 0, sizeof(*vop));
266 
267 	crtc_state->private = vop;
268 	vop->data = vop_data;
269 	vop->regs = dev_read_addr_ptr(crtc_state->dev);
270 	vop->regsbak = malloc(vop_data->reg_len);
271 	vop->win = vop_data->win;
272 	vop->win_offset = vop_data->win_offset;
273 	vop->ctrl = vop_data->ctrl;
274 
275 	map = syscon_regmap_lookup_by_phandle(crtc_state->dev, "rockchip,grf");
276 	vop->grf_ctrl = regmap_get_range(map, 0);
277 	if (vop->grf_ctrl <= 0)
278 		printf("%s: Get syscon grf failed (ret=%p)\n", __func__, vop->grf_ctrl);
279 
280 	vop->line_flag = vop_data->line_flag;
281 	vop->csc_table = vop_data->csc_table;
282 	vop->win_csc = vop_data->win_csc;
283 	vop->version = vop_data->version;
284 
285 	printf("VOP:0x%8p update mode to: %dx%d%s%d, type:%s\n",
286 	       vop->regs, mode->crtc_hdisplay, mode->vdisplay,
287 	       mode->flags & DRM_MODE_FLAG_INTERLACE ? "i" : "p",
288 	       mode->vrefresh,
289 	       rockchip_get_output_if_name(conn_state->output_if, output_type_name));
290 
291 	/* Process 'assigned-{clocks/clock-parents/clock-rates}' properties */
292 	ret = clk_set_defaults(crtc_state->dev);
293 	if (ret)
294 		debug("%s clk_set_defaults failed %d\n", __func__, ret);
295 
296 	ret = clk_get_by_name(crtc_state->dev, "dclk_vop", &dclk);
297 	if (!ret)
298 		ret = clk_set_rate(&dclk, mode->crtc_clock * 1000);
299 	if (IS_ERR_VALUE(ret)) {
300 		printf("%s: Failed to set dclk: ret=%d\n", __func__, ret);
301 		return ret;
302 	}
303 	printf("VOP:0x%8p set crtc_clock to %dKHz\n", vop->regs, mode->crtc_clock);
304 
305 	memcpy(vop->regsbak, vop->regs, vop_data->reg_len);
306 
307 	rockchip_vop_init_gamma(vop, state);
308 
309 	ret = gpio_request_by_name(crtc_state->dev, "mcu-rs-gpios",
310 				   0, &vop->mcu_rs_gpio, GPIOD_IS_OUT);
311 	if (ret && ret != -ENOENT)
312 		printf("%s: Cannot get mcu rs GPIO: %d\n", __func__, ret);
313 
314 	VOP_CTRL_SET(vop, global_regdone_en, 1);
315 	VOP_CTRL_SET(vop, axi_outstanding_max_num, 30);
316 	VOP_CTRL_SET(vop, axi_max_outstanding_en, 1);
317 	VOP_CTRL_SET(vop, reg_done_frm, 1);
318 	VOP_CTRL_SET(vop, win_gate[0], 1);
319 	VOP_CTRL_SET(vop, win_gate[1], 1);
320 	VOP_CTRL_SET(vop, win_channel[0], 0x12);
321 	VOP_CTRL_SET(vop, win_channel[1], 0x34);
322 	VOP_CTRL_SET(vop, win_channel[2], 0x56);
323 	VOP_CTRL_SET(vop, dsp_blank, 0);
324 
325 	dclk_inv = (conn_state->bus_flags & DRM_BUS_FLAG_PIXDATA_DRIVE_NEGEDGE) ? 1 : 0;
326 	/* For improving signal quality, dclk need to be inverted by default on rv1106. */
327 	if ((VOP_MAJOR(vop->version) == 2 && VOP_MINOR(vop->version) == 12))
328 		dclk_inv = !dclk_inv;
329 	VOP_CTRL_SET(vop, dclk_pol, dclk_inv);
330 
331 	val = 0x8;
332 	val |= (mode->flags & DRM_MODE_FLAG_NHSYNC) ? 0 : 1;
333 	val |= (mode->flags & DRM_MODE_FLAG_NVSYNC) ? 0 : (1 << 1);
334 	VOP_CTRL_SET(vop, pin_pol, val);
335 
336 	switch (conn_state->type) {
337 	case DRM_MODE_CONNECTOR_LVDS:
338 		VOP_CTRL_SET(vop, rgb_en, 1);
339 		VOP_CTRL_SET(vop, rgb_pin_pol, val);
340 		VOP_CTRL_SET(vop, rgb_dclk_pol, dclk_inv);
341 		VOP_CTRL_SET(vop, lvds_en, 1);
342 		VOP_CTRL_SET(vop, lvds_pin_pol, val);
343 		VOP_CTRL_SET(vop, lvds_dclk_pol, dclk_inv);
344 		if (!IS_ERR_OR_NULL(vop->grf_ctrl))
345 			VOP_GRF_SET(vop, grf_ctrl, grf_dclk_inv, dclk_inv);
346 		break;
347 	case DRM_MODE_CONNECTOR_eDP:
348 		VOP_CTRL_SET(vop, edp_en, 1);
349 		VOP_CTRL_SET(vop, edp_pin_pol, val);
350 		VOP_CTRL_SET(vop, edp_dclk_pol, dclk_inv);
351 		break;
352 	case DRM_MODE_CONNECTOR_HDMIA:
353 		VOP_CTRL_SET(vop, hdmi_en, 1);
354 		VOP_CTRL_SET(vop, hdmi_pin_pol, val);
355 		VOP_CTRL_SET(vop, hdmi_dclk_pol, 1);
356 		break;
357 	case DRM_MODE_CONNECTOR_DSI:
358 		VOP_CTRL_SET(vop, mipi_en, 1);
359 		VOP_CTRL_SET(vop, mipi_pin_pol, val);
360 		VOP_CTRL_SET(vop, mipi_dclk_pol, dclk_inv);
361 		VOP_CTRL_SET(vop, mipi_dual_channel_en,
362 			!!(conn_state->output_flags & ROCKCHIP_OUTPUT_DUAL_CHANNEL_LEFT_RIGHT_MODE));
363 		VOP_CTRL_SET(vop, data01_swap,
364 			!!(conn_state->output_flags & ROCKCHIP_OUTPUT_DATA_SWAP) ||
365 			crtc_state->dual_channel_swap);
366 		break;
367 	case DRM_MODE_CONNECTOR_DisplayPort:
368 		VOP_CTRL_SET(vop, dp_dclk_pol, 0);
369 		VOP_CTRL_SET(vop, dp_pin_pol, val);
370 		VOP_CTRL_SET(vop, dp_en, 1);
371 		break;
372 	case DRM_MODE_CONNECTOR_TV:
373 		if (vdisplay == CVBS_PAL_VDISPLAY)
374 			VOP_CTRL_SET(vop, tve_sw_mode, 1);
375 		else
376 			VOP_CTRL_SET(vop, tve_sw_mode, 0);
377 		VOP_CTRL_SET(vop, tve_dclk_pol, 1);
378 		VOP_CTRL_SET(vop, tve_dclk_en, 1);
379 		/* use the same pol reg with hdmi */
380 		VOP_CTRL_SET(vop, hdmi_pin_pol, val);
381 		VOP_CTRL_SET(vop, sw_genlock, 1);
382 		VOP_CTRL_SET(vop, sw_uv_offset_en, 1);
383 		VOP_CTRL_SET(vop, dither_up, 1);
384 		break;
385 	default:
386 		printf("unsupport connector_type[%d]\n", conn_state->type);
387 	}
388 
389 	if (conn_state->output_mode == ROCKCHIP_OUT_MODE_AAAA &&
390 	    !(vop_data->feature & VOP_FEATURE_OUTPUT_10BIT))
391 		conn_state->output_mode = ROCKCHIP_OUT_MODE_P888;
392 
393 	switch (conn_state->bus_format) {
394 	case MEDIA_BUS_FMT_RGB565_1X16:
395 		val = DITHER_DOWN_EN(1) | DITHER_DOWN_MODE(RGB888_TO_RGB565);
396 		break;
397 	case MEDIA_BUS_FMT_RGB666_1X18:
398 	case MEDIA_BUS_FMT_RGB666_1X24_CPADHI:
399 	case MEDIA_BUS_FMT_RGB666_1X7X3_SPWG:
400 	case MEDIA_BUS_FMT_RGB666_1X7X3_JEIDA:
401 		val = DITHER_DOWN_EN(1) | DITHER_DOWN_MODE(RGB888_TO_RGB666);
402 		break;
403 	case MEDIA_BUS_FMT_YUV8_1X24:
404 	case MEDIA_BUS_FMT_UYYVYY8_0_5X24:
405 		val = DITHER_DOWN_EN(0) | PRE_DITHER_DOWN_EN(1);
406 		break;
407 	case MEDIA_BUS_FMT_YUV10_1X30:
408 	case MEDIA_BUS_FMT_UYYVYY10_0_5X30:
409 		val = DITHER_DOWN_EN(0) | PRE_DITHER_DOWN_EN(0);
410 		break;
411 	case MEDIA_BUS_FMT_RGB888_1X24:
412 	case MEDIA_BUS_FMT_RGB888_1X7X4_SPWG:
413 	case MEDIA_BUS_FMT_RGB888_1X7X4_JEIDA:
414 	default:
415 		val = DITHER_DOWN_EN(0) | PRE_DITHER_DOWN_EN(0);
416 		break;
417 	}
418 	if (conn_state->output_mode == ROCKCHIP_OUT_MODE_AAAA)
419 		val |= PRE_DITHER_DOWN_EN(0);
420 	else
421 		val |= PRE_DITHER_DOWN_EN(1);
422 	val |= DITHER_DOWN_MODE_SEL(DITHER_DOWN_ALLEGRO);
423 	VOP_CTRL_SET(vop, dither_down, val);
424 
425 	VOP_CTRL_SET(vop, dclk_ddr,
426 		     conn_state->output_mode == ROCKCHIP_OUT_MODE_YUV420 ? 1 : 0);
427 	VOP_CTRL_SET(vop, hdmi_dclk_out_en,
428 		     conn_state->output_mode == ROCKCHIP_OUT_MODE_YUV420 ? 1 : 0);
429 
430 	if (is_uv_swap(conn_state->bus_format, conn_state->output_mode) ||
431 	    is_rb_swap(conn_state->bus_format, conn_state->output_mode))
432 		VOP_CTRL_SET(vop, dsp_rb_swap, 1);
433 	else
434 		VOP_CTRL_SET(vop, dsp_data_swap, 0);
435 
436 	VOP_CTRL_SET(vop, out_mode, conn_state->output_mode);
437 
438 	if (VOP_CTRL_SUPPORT(vop, overlay_mode)) {
439 		yuv_overlay = is_yuv_output(conn_state->bus_format);
440 		VOP_CTRL_SET(vop, overlay_mode, yuv_overlay);
441 	}
442 	/*
443 	 * todo: r2y for win csc
444 	 */
445 	VOP_CTRL_SET(vop, dsp_out_yuv, is_yuv_output(conn_state->bus_format));
446 
447 	if (yuv_overlay) {
448 		if (!is_yuv_output(conn_state->bus_format))
449 			post_y2r_en = true;
450 	} else {
451 		if (is_yuv_output(conn_state->bus_format))
452 			post_r2y_en = true;
453 	}
454 
455 	crtc_state->yuv_overlay = yuv_overlay;
456 	post_csc_mode = to_vop_csc_mode(conn_state->color_space);
457 	VOP_CTRL_SET(vop, bcsh_r2y_en, post_r2y_en);
458 	VOP_CTRL_SET(vop, bcsh_y2r_en, post_y2r_en);
459 	VOP_CTRL_SET(vop, bcsh_r2y_csc_mode, post_csc_mode);
460 	VOP_CTRL_SET(vop, bcsh_y2r_csc_mode, post_csc_mode);
461 
462 	/*
463 	 * Background color is 10bit depth if vop version >= 3.5
464 	 */
465 	if (!is_yuv_output(conn_state->bus_format))
466 		val = 0;
467 	else if (VOP_MAJOR(vop->version) == 3 &&
468 		 VOP_MINOR(vop->version) >= 5)
469 		val = 0x20010200;
470 	else
471 		val = 0x801080;
472 	VOP_CTRL_SET(vop, dsp_background, val);
473 
474 	VOP_CTRL_SET(vop, htotal_pw, (htotal << 16) | hsync_len);
475 	val = hact_st << 16;
476 	val |= hact_end;
477 	VOP_CTRL_SET(vop, hact_st_end, val);
478 	val = vact_st << 16;
479 	val |= vact_end;
480 	VOP_CTRL_SET(vop, vact_st_end, val);
481 	if (mode->flags & DRM_MODE_FLAG_INTERLACE) {
482 		u16 vact_st_f1 = vtotal + vact_st + 1;
483 		u16 vact_end_f1 = vact_st_f1 + vdisplay;
484 
485 		val = vact_st_f1 << 16 | vact_end_f1;
486 		VOP_CTRL_SET(vop, vact_st_end_f1, val);
487 
488 		val = vtotal << 16 | (vtotal + vsync_len);
489 		VOP_CTRL_SET(vop, vs_st_end_f1, val);
490 		VOP_CTRL_SET(vop, dsp_interlace, 1);
491 		VOP_CTRL_SET(vop, p2i_en, 1);
492 		vtotal += vtotal + 1;
493 		act_end = vact_end_f1;
494 	} else {
495 		VOP_CTRL_SET(vop, dsp_interlace, 0);
496 		VOP_CTRL_SET(vop, p2i_en, 0);
497 		act_end = vact_end;
498 	}
499 	VOP_CTRL_SET(vop, vtotal_pw, (vtotal << 16) | vsync_len);
500 	vop_post_config(state, vop);
501 	VOP_CTRL_SET(vop, core_dclk_div,
502 		     !!(mode->flags & DRM_MODE_FLAG_DBLCLK));
503 
504 	VOP_LINE_FLAG_SET(vop, line_flag_num[0], act_end - 3);
505 	VOP_LINE_FLAG_SET(vop, line_flag_num[1],
506 			  act_end - us_to_vertical_line(mode, 1000));
507 	if (state->crtc_state.mcu_timing.mcu_pix_total > 0)
508 		vop_mcu_mode(state, vop);
509 	vop_cfg_done(vop);
510 
511 	return 0;
512 }
513 
514 static uint16_t scl_vop_cal_scale(enum scale_mode mode, uint32_t src,
515 				  uint32_t dst, bool is_horizontal,
516 				  int vsu_mode, int *vskiplines)
517 {
518 	uint16_t val = 1 << SCL_FT_DEFAULT_FIXPOINT_SHIFT;
519 
520 	if (is_horizontal) {
521 		if (mode == SCALE_UP)
522 			val = GET_SCL_FT_BIC(src, dst);
523 		else if (mode == SCALE_DOWN)
524 			val = GET_SCL_FT_BILI_DN(src, dst);
525 	} else {
526 		if (mode == SCALE_UP) {
527 			if (vsu_mode == SCALE_UP_BIL)
528 				val = GET_SCL_FT_BILI_UP(src, dst);
529 			else
530 				val = GET_SCL_FT_BIC(src, dst);
531 		} else if (mode == SCALE_DOWN) {
532 			if (vskiplines) {
533 				*vskiplines = scl_get_vskiplines(src, dst);
534 				val = scl_get_bili_dn_vskip(src, dst,
535 							    *vskiplines);
536 			} else {
537 				val = GET_SCL_FT_BILI_DN(src, dst);
538 			}
539 		}
540 	}
541 
542 	return val;
543 }
544 
545 static void scl_vop_cal_scl_fac(struct vop *vop,
546 				uint32_t src_w, uint32_t src_h, uint32_t dst_w,
547 				uint32_t dst_h, uint32_t pixel_format)
548 {
549 	uint16_t yrgb_hor_scl_mode, yrgb_ver_scl_mode;
550 	uint16_t cbcr_hor_scl_mode = SCALE_NONE;
551 	uint16_t cbcr_ver_scl_mode = SCALE_NONE;
552 	int hsub = drm_format_horz_chroma_subsampling(pixel_format);
553 	int vsub = drm_format_vert_chroma_subsampling(pixel_format);
554 	bool is_yuv = false;
555 	uint16_t cbcr_src_w = src_w / hsub;
556 	uint16_t cbcr_src_h = src_h / vsub;
557 	uint16_t vsu_mode;
558 	uint16_t lb_mode;
559 	uint32_t val;
560 	int vskiplines = 0;
561 
562 	if (!vop->win->scl)
563 		return;
564 
565 	if (!vop->win->scl->ext) {
566 		VOP_SCL_SET(vop, scale_yrgb_x,
567 			    scl_cal_scale2(src_w, dst_w));
568 		VOP_SCL_SET(vop, scale_yrgb_y,
569 			    scl_cal_scale2(src_h, dst_h));
570 		if (is_yuv) {
571 			VOP_SCL_SET(vop, scale_cbcr_x,
572 				    scl_cal_scale2(src_w, dst_w));
573 			VOP_SCL_SET(vop, scale_cbcr_y,
574 				    scl_cal_scale2(src_h, dst_h));
575 		}
576 		return;
577 	}
578 
579 	yrgb_hor_scl_mode = scl_get_scl_mode(src_w, dst_w);
580 	yrgb_ver_scl_mode = scl_get_scl_mode(src_h, dst_h);
581 
582 	if (is_yuv) {
583 		cbcr_hor_scl_mode = scl_get_scl_mode(cbcr_src_w, dst_w);
584 		cbcr_ver_scl_mode = scl_get_scl_mode(cbcr_src_h, dst_h);
585 		if (cbcr_hor_scl_mode == SCALE_DOWN)
586 			lb_mode = scl_vop_cal_lb_mode(dst_w, true);
587 		else
588 			lb_mode = scl_vop_cal_lb_mode(cbcr_src_w, true);
589 	} else {
590 		if (yrgb_hor_scl_mode == SCALE_DOWN)
591 			lb_mode = scl_vop_cal_lb_mode(dst_w, false);
592 		else
593 			lb_mode = scl_vop_cal_lb_mode(src_w, false);
594 	}
595 
596 	VOP_SCL_SET_EXT(vop, lb_mode, lb_mode);
597 	if (lb_mode == LB_RGB_3840X2) {
598 		if (yrgb_ver_scl_mode != SCALE_NONE) {
599 			printf("ERROR : not allow yrgb ver scale\n");
600 			return;
601 		}
602 		if (cbcr_ver_scl_mode != SCALE_NONE) {
603 			printf("ERROR : not allow cbcr ver scale\n");
604 			return;
605 		}
606 		vsu_mode = SCALE_UP_BIL;
607 	} else if (lb_mode == LB_RGB_2560X4) {
608 		vsu_mode = SCALE_UP_BIL;
609 	} else {
610 		vsu_mode = SCALE_UP_BIC;
611 	}
612 
613 	val = scl_vop_cal_scale(yrgb_hor_scl_mode, src_w, dst_w,
614 				true, 0, NULL);
615 	VOP_SCL_SET(vop, scale_yrgb_x, val);
616 	val = scl_vop_cal_scale(yrgb_ver_scl_mode, src_h, dst_h,
617 				false, vsu_mode, &vskiplines);
618 	VOP_SCL_SET(vop, scale_yrgb_y, val);
619 
620 	VOP_SCL_SET_EXT(vop, vsd_yrgb_gt4, vskiplines == 4);
621 	VOP_SCL_SET_EXT(vop, vsd_yrgb_gt2, vskiplines == 2);
622 
623 	VOP_SCL_SET_EXT(vop, yrgb_hor_scl_mode, yrgb_hor_scl_mode);
624 	VOP_SCL_SET_EXT(vop, yrgb_ver_scl_mode, yrgb_ver_scl_mode);
625 	VOP_SCL_SET_EXT(vop, yrgb_hsd_mode, SCALE_DOWN_BIL);
626 	VOP_SCL_SET_EXT(vop, yrgb_vsd_mode, SCALE_DOWN_BIL);
627 	VOP_SCL_SET_EXT(vop, yrgb_vsu_mode, vsu_mode);
628 	if (is_yuv) {
629 		val = scl_vop_cal_scale(cbcr_hor_scl_mode, cbcr_src_w,
630 					dst_w, true, 0, NULL);
631 		VOP_SCL_SET(vop, scale_cbcr_x, val);
632 		val = scl_vop_cal_scale(cbcr_ver_scl_mode, cbcr_src_h,
633 					dst_h, false, vsu_mode, &vskiplines);
634 		VOP_SCL_SET(vop, scale_cbcr_y, val);
635 
636 		VOP_SCL_SET_EXT(vop, vsd_cbcr_gt4, vskiplines == 4);
637 		VOP_SCL_SET_EXT(vop, vsd_cbcr_gt2, vskiplines == 2);
638 		VOP_SCL_SET_EXT(vop, cbcr_hor_scl_mode, cbcr_hor_scl_mode);
639 		VOP_SCL_SET_EXT(vop, cbcr_ver_scl_mode, cbcr_ver_scl_mode);
640 		VOP_SCL_SET_EXT(vop, cbcr_hsd_mode, SCALE_DOWN_BIL);
641 		VOP_SCL_SET_EXT(vop, cbcr_vsd_mode, SCALE_DOWN_BIL);
642 		VOP_SCL_SET_EXT(vop, cbcr_vsu_mode, vsu_mode);
643 	}
644 }
645 
646 static void vop_load_csc_table(struct vop *vop, u32 offset, const u32 *table)
647 {
648 	int i;
649 
650 	/*
651 	 * so far the csc offset is not 0 and in the feature the csc offset
652 	 * impossible be 0, so when the offset is 0, should return here.
653 	 */
654 	if (!table || offset == 0)
655 		return;
656 
657 	for (i = 0; i < 8; i++)
658 		vop_writel(vop, offset + i * 4, table[i]);
659 }
660 
661 static int rockchip_vop_setup_csc_table(struct display_state *state)
662 {
663 	struct crtc_state *crtc_state = &state->crtc_state;
664 	struct connector_state *conn_state = &state->conn_state;
665 	struct vop *vop = crtc_state->private;
666 	const uint32_t *csc_table = NULL;
667 
668 	if (!vop->csc_table || !crtc_state->yuv_overlay)
669 		return 0;
670 	/* todo: only implement r2y*/
671 	switch (conn_state->color_space) {
672 	case V4L2_COLORSPACE_SMPTE170M:
673 		csc_table = vop->csc_table->r2y_bt601_12_235;
674 		break;
675 	case V4L2_COLORSPACE_REC709:
676 	case V4L2_COLORSPACE_DEFAULT:
677 	case V4L2_COLORSPACE_JPEG:
678 		csc_table = vop->csc_table->r2y_bt709;
679 		break;
680 	case V4L2_COLORSPACE_BT2020:
681 		csc_table = vop->csc_table->r2y_bt2020;
682 		break;
683 	default:
684 		csc_table = vop->csc_table->r2y_bt601;
685 		break;
686 	}
687 
688 	vop_load_csc_table(vop, vop->win_csc->r2y_offset, csc_table);
689 	VOP_WIN_CSC_SET(vop, r2y_en, 1);
690 
691 	return 0;
692 }
693 
694 static int rockchip_vop_set_plane(struct display_state *state)
695 {
696 	struct crtc_state *crtc_state = &state->crtc_state;
697 	const struct rockchip_crtc *crtc = crtc_state->crtc;
698 	const struct vop_data *vop_data = crtc->data;
699 	struct connector_state *conn_state = &state->conn_state;
700 	struct drm_display_mode *mode = &conn_state->mode;
701 	u32 act_info, dsp_info, dsp_st, dsp_stx, dsp_sty;
702 	struct vop *vop = crtc_state->private;
703 	int src_w = crtc_state->src_rect.w;
704 	int src_h = crtc_state->src_rect.h;
705 	int crtc_x = crtc_state->crtc_rect.x;
706 	int crtc_y = crtc_state->crtc_rect.y;
707 	int crtc_w = crtc_state->crtc_rect.w;
708 	int crtc_h = crtc_state->crtc_rect.h;
709 	int xvir = crtc_state->xvir;
710 	int x_mirror = 0, y_mirror = 0;
711 
712 	if (crtc_w > crtc_state->max_output.width) {
713 		printf("ERROR: output w[%d] exceeded max width[%d]\n",
714 		       crtc_w, crtc_state->max_output.width);
715 		return -EINVAL;
716 	}
717 
718 	act_info = (src_h - 1) << 16;
719 	act_info |= (src_w - 1) & 0xffff;
720 
721 	dsp_info = (crtc_h - 1) << 16;
722 	dsp_info |= (crtc_w - 1) & 0xffff;
723 
724 	dsp_stx = crtc_x + mode->crtc_htotal - mode->crtc_hsync_start;
725 	dsp_sty = crtc_y + mode->crtc_vtotal - mode->crtc_vsync_start;
726 	dsp_st = dsp_sty << 16 | (dsp_stx & 0xffff);
727 	/*
728 	 * vop full need to treats rgb888 as bgr888 so we reverse the rb swap to workaround
729 	 */
730 	if (crtc_state->format == ROCKCHIP_FMT_RGB888 && VOP_MAJOR(vop_data->version) == 3)
731 		crtc_state->rb_swap = !crtc_state->rb_swap;
732 
733 	if (mode->flags & DRM_MODE_FLAG_YMIRROR)
734 		y_mirror = 1;
735 	else
736 		y_mirror = 0;
737 	if (mode->flags & DRM_MODE_FLAG_XMIRROR)
738 		x_mirror = 1;
739 	else
740 		x_mirror = 0;
741 	if (crtc_state->ymirror ^ y_mirror)
742 		y_mirror = 1;
743 	else
744 		y_mirror = 0;
745 	if (y_mirror) {
746 		if (VOP_CTRL_SUPPORT(vop, ymirror))
747 			crtc_state->dma_addr += (src_h - 1) * xvir * 4;
748 		else
749 			y_mirror = 0;
750 		}
751 	VOP_CTRL_SET(vop, ymirror, y_mirror);
752 	VOP_CTRL_SET(vop, xmirror, x_mirror);
753 
754 	VOP_WIN_SET(vop, format, crtc_state->format);
755 	VOP_WIN_SET(vop, yrgb_vir, xvir);
756 	VOP_WIN_SET(vop, yrgb_mst, crtc_state->dma_addr);
757 
758 	scl_vop_cal_scl_fac(vop, src_w, src_h, crtc_w, crtc_h,
759 			    crtc_state->format);
760 
761 	VOP_WIN_SET(vop, act_info, act_info);
762 	VOP_WIN_SET(vop, dsp_info, dsp_info);
763 	VOP_WIN_SET(vop, dsp_st, dsp_st);
764 	VOP_WIN_SET(vop, rb_swap, crtc_state->rb_swap);
765 
766 	VOP_WIN_SET(vop, src_alpha_ctl, 0);
767 
768 	rockchip_vop_setup_csc_table(state);
769 	VOP_WIN_SET(vop, enable, 1);
770 	VOP_WIN_SET(vop, gate, 1);
771 	vop_cfg_done(vop);
772 
773 	return 0;
774 }
775 
776 static int rockchip_vop_prepare(struct display_state *state)
777 {
778 	return 0;
779 }
780 
781 static int rockchip_vop_enable(struct display_state *state)
782 {
783 	struct crtc_state *crtc_state = &state->crtc_state;
784 	struct vop *vop = crtc_state->private;
785 
786 	VOP_CTRL_SET(vop, standby, 0);
787 	vop_cfg_done(vop);
788 	if (crtc_state->mcu_timing.mcu_pix_total > 0)
789 		VOP_CTRL_SET(vop, mcu_hold_mode, 0);
790 
791 	return 0;
792 }
793 
794 static int rockchip_vop_disable(struct display_state *state)
795 {
796 	struct crtc_state *crtc_state = &state->crtc_state;
797 	struct vop *vop = crtc_state->private;
798 
799 	VOP_CTRL_SET(vop, standby, 1);
800 	vop_cfg_done(vop);
801 	return 0;
802 }
803 
804 static int rockchip_vop_fixup_dts(struct display_state *state, void *blob)
805 {
806 #if 0
807 	struct crtc_state *crtc_state = &state->crtc_state;
808 	struct panel_state *pstate = &state->panel_state;
809 	uint32_t phandle;
810 	char path[100];
811 	int ret, dsp_lut_node;
812 
813 	if (!ofnode_valid(pstate->dsp_lut_node))
814 		return 0;
815 	ret = fdt_get_path(state->blob, pstate->dsp_lut_node, path, sizeof(path));
816 	if (ret < 0) {
817 		printf("failed to get dsp_lut path[%s], ret=%d\n",
818 			path, ret);
819 		return ret;
820 	}
821 
822 	dsp_lut_node = fdt_path_offset(blob, path);
823 	phandle = fdt_get_phandle(blob, dsp_lut_node);
824 	if (!phandle) {
825 		phandle = fdt_alloc_phandle(blob);
826 		if (!phandle) {
827 			printf("failed to alloc phandle\n");
828 			return -ENOMEM;
829 		}
830 
831 		fdt_set_phandle(blob, dsp_lut_node, phandle);
832 	}
833 
834 	ret = fdt_get_path(state->blob, crtc_state->node, path, sizeof(path));
835 	if (ret < 0) {
836 		printf("failed to get route path[%s], ret=%d\n",
837 			path, ret);
838 		return ret;
839 	}
840 
841 	do_fixup_by_path_u32(blob, path, "dsp-lut", phandle, 1);
842 #endif
843 	return 0;
844 }
845 
846 static int rockchip_vop_send_mcu_cmd(struct display_state *state,
847 				     u32 type, u32 value)
848 {
849 	struct crtc_state *crtc_state = &state->crtc_state;
850 	struct vop *vop = crtc_state->private;
851 
852 	if (vop) {
853 		switch (type) {
854 		case MCU_WRCMD:
855 			set_vop_mcu_rs(vop, 0);
856 			VOP_CTRL_SET(vop, mcu_rw_bypass_port, value);
857 			set_vop_mcu_rs(vop, 1);
858 			break;
859 		case MCU_WRDATA:
860 			set_vop_mcu_rs(vop, 1);
861 			VOP_CTRL_SET(vop, mcu_rw_bypass_port, value);
862 			break;
863 		case MCU_SETBYPASS:
864 			VOP_CTRL_SET(vop, mcu_bypass, value ? 1 : 0);
865 			break;
866 		default:
867 			break;
868 		}
869 	}
870 
871 	return 0;
872 }
873 
874 static int rockchip_vop_mode_valid(struct display_state *state)
875 {
876 	struct connector_state *conn_state = &state->conn_state;
877 	struct drm_display_mode *mode = &conn_state->mode;
878 	struct videomode vm;
879 
880 	drm_display_mode_to_videomode(mode, &vm);
881 
882 	if (vm.hactive < 32 || vm.vactive < 32 ||
883 	    (vm.hfront_porch * vm.hsync_len * vm.hback_porch *
884 	     vm.vfront_porch * vm.vsync_len * vm.vback_porch == 0)) {
885 		printf("ERROR: unsupported display timing\n");
886 		return -EINVAL;
887 	}
888 
889 	return 0;
890 }
891 
892 static int rockchip_vop_plane_check(struct display_state *state)
893 {
894 	struct crtc_state *crtc_state = &state->crtc_state;
895 	const struct rockchip_crtc *crtc = crtc_state->crtc;
896 	const struct vop_data *vop_data = crtc->data;
897 	const struct vop_win *win = vop_data->win;
898 	struct display_rect *src = &crtc_state->src_rect;
899 	struct display_rect *dst = &crtc_state->crtc_rect;
900 	int min_scale, max_scale;
901 	int hscale, vscale;
902 
903 	min_scale = win->scl ? FRAC_16_16(1, 8) : VOP_PLANE_NO_SCALING;
904 	max_scale = win->scl ? FRAC_16_16(8, 1) : VOP_PLANE_NO_SCALING;
905 
906 	hscale = display_rect_calc_hscale(src, dst, min_scale, max_scale);
907 	vscale = display_rect_calc_vscale(src, dst, min_scale, max_scale);
908 	if (hscale < 0 || vscale < 0) {
909 		printf("ERROR: scale factor is out of range\n");
910 		return -ERANGE;
911 	}
912 
913 	return 0;
914 }
915 
916 static int rockchip_vop_mode_fixup(struct display_state *state)
917 {
918 	struct crtc_state *crtc_state = &state->crtc_state;
919 	struct connector_state *conn_state = &state->conn_state;
920 	struct drm_display_mode *mode = &conn_state->mode;
921 
922 	drm_mode_set_crtcinfo(mode, CRTC_INTERLACE_HALVE_V | CRTC_STEREO_DOUBLE);
923 
924 	mode->crtc_clock *= rockchip_drm_get_cycles_per_pixel(conn_state->bus_format);
925 	if (crtc_state->mcu_timing.mcu_pix_total)
926 		mode->crtc_clock *= crtc_state->mcu_timing.mcu_pix_total + 1;
927 
928 	return 0;
929 }
930 
931 const struct rockchip_crtc_funcs rockchip_vop_funcs = {
932 	.preinit = rockchip_vop_preinit,
933 	.init = rockchip_vop_init,
934 	.set_plane = rockchip_vop_set_plane,
935 	.prepare = rockchip_vop_prepare,
936 	.enable = rockchip_vop_enable,
937 	.disable = rockchip_vop_disable,
938 	.fixup_dts = rockchip_vop_fixup_dts,
939 	.send_mcu_cmd = rockchip_vop_send_mcu_cmd,
940 	.mode_valid = rockchip_vop_mode_valid,
941 	.plane_check = rockchip_vop_plane_check,
942 	.mode_fixup = rockchip_vop_mode_fixup,
943 };
944