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