xref: /rk3399_rockchip-uboot/drivers/video/drm/rockchip_vop.c (revision 463abfccb179f8e678f3b4e143208569d2ff9bb0)
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 	vop->grf_ctrl = regmap_get_range(map, 0);
323 	if (vop->grf_ctrl <= 0)
324 		printf("%s: Get syscon grf failed (ret=%p)\n", __func__, vop->grf_ctrl);
325 	map = syscon_regmap_lookup_by_phandle(crtc_state->dev, "rockchip,vo0-grf");
326 	vop->vo0_grf_ctrl = regmap_get_range(map, 0);
327 	if (vop->vo0_grf_ctrl <= 0)
328 		printf("%s: Get syscon vo0_grf failed (ret=%p)\n", __func__, vop->vo0_grf_ctrl);
329 
330 	vop->line_flag = vop_data->line_flag;
331 	vop->csc_table = vop_data->csc_table;
332 	vop->win_csc = vop_data->win_csc;
333 	vop->version = vop_data->version;
334 
335 	printf("VOP:0x%8p update mode to: %dx%d%s%d, type:%s\n",
336 	       vop->regs, mode->crtc_hdisplay, mode->vdisplay,
337 	       mode->flags & DRM_MODE_FLAG_INTERLACE ? "i" : "p",
338 	       mode->vrefresh,
339 	       rockchip_get_output_if_name(conn_state->output_if, output_type_name));
340 
341 	/* Process 'assigned-{clocks/clock-parents/clock-rates}' properties */
342 	ret = clk_set_defaults(crtc_state->dev);
343 	if (ret)
344 		debug("%s clk_set_defaults failed %d\n", __func__, ret);
345 
346 	ret = clk_get_by_name(crtc_state->dev, "dclk_vop", &crtc_state->dclk);
347 	if (!ret)
348 		ret = clk_set_rate(&crtc_state->dclk, mode->crtc_clock * 1000);
349 	if (IS_ERR_VALUE(ret)) {
350 		printf("%s: Failed to set dclk: ret=%d\n", __func__, ret);
351 		return ret;
352 	}
353 	printf("VOP:0x%8p set crtc_clock to %dKHz\n", vop->regs, mode->crtc_clock);
354 
355 	memcpy(vop->regsbak, vop->regs, vop_data->reg_len);
356 
357 	rockchip_vop_init_gamma(vop, state);
358 
359 	ret = gpio_request_by_name(crtc_state->dev, "mcu-rs-gpios",
360 				   0, &vop->mcu_rs_gpio, GPIOD_IS_OUT);
361 	if (ret && ret != -ENOENT)
362 		printf("%s: Cannot get mcu rs GPIO: %d\n", __func__, ret);
363 
364 	VOP_CTRL_SET(vop, global_regdone_en, 1);
365 	VOP_CTRL_SET(vop, axi_outstanding_max_num, 30);
366 	VOP_CTRL_SET(vop, axi_max_outstanding_en, 1);
367 	VOP_CTRL_SET(vop, reg_done_frm, 1);
368 	VOP_CTRL_SET(vop, win_gate[0], 1);
369 	VOP_CTRL_SET(vop, win_gate[1], 1);
370 	VOP_CTRL_SET(vop, win_channel[0], 0x12);
371 	VOP_CTRL_SET(vop, win_channel[1], 0x34);
372 	VOP_CTRL_SET(vop, win_channel[2], 0x56);
373 	VOP_CTRL_SET(vop, dsp_blank, 0);
374 
375 	if (vop->version == VOP_VERSION(2, 0xd)) {
376 		VOP_GRF_SET(vop, grf_ctrl, grf_vopl_sel, 1);
377 		VOP_CTRL_SET(vop, enable, 1);
378 	}
379 
380 	dclk_inv = (conn_state->bus_flags & DRM_BUS_FLAG_PIXDATA_DRIVE_NEGEDGE) ? 1 : 0;
381 	/* For improving signal quality, dclk need to be inverted by default on rv1106. */
382 	if ((VOP_MAJOR(vop->version) == 2 && VOP_MINOR(vop->version) == 12))
383 		dclk_inv = !dclk_inv;
384 	VOP_CTRL_SET(vop, dclk_pol, dclk_inv);
385 
386 	val = 0x8;
387 	val |= (mode->flags & DRM_MODE_FLAG_NHSYNC) ? 0 : 1;
388 	val |= (mode->flags & DRM_MODE_FLAG_NVSYNC) ? 0 : (1 << 1);
389 	VOP_CTRL_SET(vop, pin_pol, val);
390 
391 	switch (conn_state->type) {
392 	case DRM_MODE_CONNECTOR_LVDS:
393 		VOP_CTRL_SET(vop, rgb_en, 1);
394 		VOP_CTRL_SET(vop, rgb_pin_pol, val);
395 		VOP_CTRL_SET(vop, rgb_dclk_pol, dclk_inv);
396 		VOP_CTRL_SET(vop, lvds_en, 1);
397 		VOP_CTRL_SET(vop, lvds_pin_pol, val);
398 		VOP_CTRL_SET(vop, lvds_dclk_pol, dclk_inv);
399 		if (!IS_ERR_OR_NULL(vop->grf_ctrl))
400 			VOP_GRF_SET(vop, grf_ctrl, grf_dclk_inv, dclk_inv);
401 		break;
402 	case DRM_MODE_CONNECTOR_eDP:
403 		VOP_CTRL_SET(vop, edp_en, 1);
404 		VOP_CTRL_SET(vop, edp_pin_pol, val);
405 		VOP_CTRL_SET(vop, edp_dclk_pol, dclk_inv);
406 		VOP_CTRL_SET(vop, inf_out_en, 1);
407 		VOP_GRF_SET(vop, vo0_grf_ctrl, grf_edp_ch_sel, 1);
408 		break;
409 	case DRM_MODE_CONNECTOR_HDMIA:
410 		VOP_CTRL_SET(vop, hdmi_en, 1);
411 		VOP_CTRL_SET(vop, hdmi_pin_pol, val);
412 		VOP_CTRL_SET(vop, hdmi_dclk_pol, 1);
413 		VOP_CTRL_SET(vop, inf_out_en, 1);
414 		VOP_GRF_SET(vop, vo0_grf_ctrl, grf_hdmi_ch_sel, 1);
415 		VOP_GRF_SET(vop, vo0_grf_ctrl, grf_hdmi_pin_pol, val);
416 		VOP_GRF_SET(vop, vo0_grf_ctrl, grf_hdmi_1to4_en, val);
417 		break;
418 	case DRM_MODE_CONNECTOR_DSI:
419 		/*
420 		 * RK3576 DSI CTRL hsync/vsync polarity is positive and can't update,
421 		 * so set VOP hsync/vsync polarity as positive by default.
422 		 */
423 		if (vop->version == VOP_VERSION(2, 0xd))
424 			val = BIT(HSYNC_POSITIVE) | BIT(VSYNC_POSITIVE);
425 		VOP_CTRL_SET(vop, mipi_en, 1);
426 		VOP_CTRL_SET(vop, mipi_pin_pol, val);
427 		VOP_CTRL_SET(vop, mipi_dclk_pol, dclk_inv);
428 		VOP_CTRL_SET(vop, mipi_dual_channel_en,
429 			!!(conn_state->output_flags & ROCKCHIP_OUTPUT_DUAL_CHANNEL_LEFT_RIGHT_MODE));
430 		VOP_CTRL_SET(vop, data01_swap,
431 			!!(conn_state->output_flags & ROCKCHIP_OUTPUT_DATA_SWAP) ||
432 			crtc_state->dual_channel_swap);
433 		VOP_CTRL_SET(vop, inf_out_en, 1);
434 		VOP_GRF_SET(vop, vo0_grf_ctrl, grf_mipi_ch_sel, 1);
435 		VOP_GRF_SET(vop, vo0_grf_ctrl, grf_mipi_mode, 1);
436 		VOP_GRF_SET(vop, vo0_grf_ctrl, grf_mipi_pin_pol, val);
437 		VOP_GRF_SET(vop, vo0_grf_ctrl, grf_mipi_1to4_en, 1);
438 		break;
439 	case DRM_MODE_CONNECTOR_DisplayPort:
440 		VOP_CTRL_SET(vop, dp_dclk_pol, 0);
441 		VOP_CTRL_SET(vop, dp_pin_pol, val);
442 		VOP_CTRL_SET(vop, dp_en, 1);
443 		break;
444 	case DRM_MODE_CONNECTOR_TV:
445 		if (vdisplay == CVBS_PAL_VDISPLAY)
446 			VOP_CTRL_SET(vop, tve_sw_mode, 1);
447 		else
448 			VOP_CTRL_SET(vop, tve_sw_mode, 0);
449 		VOP_CTRL_SET(vop, tve_dclk_pol, 1);
450 		VOP_CTRL_SET(vop, tve_dclk_en, 1);
451 		/* use the same pol reg with hdmi */
452 		VOP_CTRL_SET(vop, hdmi_pin_pol, val);
453 		VOP_CTRL_SET(vop, sw_genlock, 1);
454 		VOP_CTRL_SET(vop, sw_uv_offset_en, 1);
455 		VOP_CTRL_SET(vop, dither_up, 1);
456 		break;
457 	default:
458 		printf("unsupport connector_type[%d]\n", conn_state->type);
459 	}
460 
461 	if (conn_state->output_mode == ROCKCHIP_OUT_MODE_AAAA &&
462 	    !(vop_data->feature & VOP_FEATURE_OUTPUT_10BIT))
463 		conn_state->output_mode = ROCKCHIP_OUT_MODE_P888;
464 
465 	switch (conn_state->bus_format) {
466 	case MEDIA_BUS_FMT_RGB565_1X16:
467 		val = DITHER_DOWN_EN(1) | DITHER_DOWN_MODE(RGB888_TO_RGB565);
468 		break;
469 	case MEDIA_BUS_FMT_RGB666_1X18:
470 	case MEDIA_BUS_FMT_RGB666_1X24_CPADHI:
471 	case MEDIA_BUS_FMT_RGB666_1X7X3_SPWG:
472 	case MEDIA_BUS_FMT_RGB666_1X7X3_JEIDA:
473 		val = DITHER_DOWN_EN(1) | DITHER_DOWN_MODE(RGB888_TO_RGB666);
474 		break;
475 	case MEDIA_BUS_FMT_YUV8_1X24:
476 	case MEDIA_BUS_FMT_UYYVYY8_0_5X24:
477 		val = DITHER_DOWN_EN(0) | PRE_DITHER_DOWN_EN(1);
478 		break;
479 	case MEDIA_BUS_FMT_YUV10_1X30:
480 	case MEDIA_BUS_FMT_UYYVYY10_0_5X30:
481 		val = DITHER_DOWN_EN(0) | PRE_DITHER_DOWN_EN(0);
482 		break;
483 	case MEDIA_BUS_FMT_RGB888_1X24:
484 	case MEDIA_BUS_FMT_RGB888_1X7X4_SPWG:
485 	case MEDIA_BUS_FMT_RGB888_1X7X4_JEIDA:
486 	default:
487 		val = DITHER_DOWN_EN(0) | PRE_DITHER_DOWN_EN(0);
488 		break;
489 	}
490 	if (conn_state->output_mode == ROCKCHIP_OUT_MODE_AAAA)
491 		val |= PRE_DITHER_DOWN_EN(0);
492 	else
493 		val |= PRE_DITHER_DOWN_EN(1);
494 	val |= DITHER_DOWN_MODE_SEL(DITHER_DOWN_ALLEGRO);
495 	VOP_CTRL_SET(vop, dither_down, val);
496 
497 	VOP_CTRL_SET(vop, dclk_ddr,
498 		     conn_state->output_mode == ROCKCHIP_OUT_MODE_YUV420 ? 1 : 0);
499 	VOP_CTRL_SET(vop, hdmi_dclk_out_en,
500 		     conn_state->output_mode == ROCKCHIP_OUT_MODE_YUV420 ? 1 : 0);
501 
502 	if (is_uv_swap(conn_state->bus_format, conn_state->output_mode) ||
503 	    is_rb_swap(conn_state->bus_format, conn_state->output_mode))
504 		VOP_CTRL_SET(vop, dsp_rb_swap, 1);
505 	else
506 		VOP_CTRL_SET(vop, dsp_data_swap, 0);
507 
508 	VOP_CTRL_SET(vop, out_mode, conn_state->output_mode);
509 
510 	if (VOP_CTRL_SUPPORT(vop, overlay_mode)) {
511 		yuv_overlay = is_yuv_output(conn_state->bus_format);
512 		VOP_CTRL_SET(vop, overlay_mode, yuv_overlay);
513 	}
514 	/*
515 	 * todo: r2y for win csc
516 	 */
517 	VOP_CTRL_SET(vop, dsp_out_yuv, is_yuv_output(conn_state->bus_format));
518 
519 	if (yuv_overlay) {
520 		if (!is_yuv_output(conn_state->bus_format))
521 			post_y2r_en = true;
522 	} else {
523 		if (is_yuv_output(conn_state->bus_format))
524 			post_r2y_en = true;
525 	}
526 
527 	crtc_state->yuv_overlay = yuv_overlay;
528 	post_csc_mode = to_vop_csc_mode(conn_state->color_encoding, conn_state->color_range);
529 	VOP_CTRL_SET(vop, bcsh_r2y_en, post_r2y_en);
530 	VOP_CTRL_SET(vop, bcsh_y2r_en, post_y2r_en);
531 	VOP_CTRL_SET(vop, bcsh_r2y_csc_mode, post_csc_mode);
532 	VOP_CTRL_SET(vop, bcsh_y2r_csc_mode, post_csc_mode);
533 
534 	/*
535 	 * Background color is 10bit depth if vop version >= 3.5
536 	 */
537 	if (!is_yuv_output(conn_state->bus_format))
538 		val = 0;
539 	else if (VOP_MAJOR(vop->version) == 3 &&
540 		 VOP_MINOR(vop->version) >= 5)
541 		val = 0x20010200;
542 	else
543 		val = 0x801080;
544 	VOP_CTRL_SET(vop, dsp_background, val);
545 
546 	VOP_CTRL_SET(vop, htotal_pw, (htotal << 16) | hsync_len);
547 	val = hact_st << 16;
548 	val |= hact_end;
549 	VOP_CTRL_SET(vop, hact_st_end, val);
550 	val = vact_st << 16;
551 	val |= vact_end;
552 	VOP_CTRL_SET(vop, vact_st_end, val);
553 	if (mode->flags & DRM_MODE_FLAG_INTERLACE) {
554 		u16 vact_st_f1 = vtotal + vact_st + 1;
555 		u16 vact_end_f1 = vact_st_f1 + vdisplay;
556 
557 		val = vact_st_f1 << 16 | vact_end_f1;
558 		VOP_CTRL_SET(vop, vact_st_end_f1, val);
559 
560 		val = vtotal << 16 | (vtotal + vsync_len);
561 		VOP_CTRL_SET(vop, vs_st_end_f1, val);
562 		VOP_CTRL_SET(vop, dsp_interlace, 1);
563 		VOP_CTRL_SET(vop, p2i_en, 1);
564 		vtotal += vtotal + 1;
565 		act_end = vact_end_f1;
566 	} else {
567 		VOP_CTRL_SET(vop, dsp_interlace, 0);
568 		VOP_CTRL_SET(vop, p2i_en, 0);
569 		act_end = vact_end;
570 	}
571 	VOP_CTRL_SET(vop, vtotal_pw, (vtotal << 16) | vsync_len);
572 	vop_post_config(state, vop);
573 	VOP_CTRL_SET(vop, core_dclk_div,
574 		     !!(mode->flags & DRM_MODE_FLAG_DBLCLK));
575 
576 	VOP_LINE_FLAG_SET(vop, line_flag_num[0], act_end - 3);
577 	VOP_LINE_FLAG_SET(vop, line_flag_num[1],
578 			  act_end - us_to_vertical_line(mode, 1000));
579 	if (state->crtc_state.mcu_timing.mcu_pix_total > 0)
580 		vop_mcu_mode_setup(state, vop);
581 	vop_cfg_done(vop);
582 
583 	return 0;
584 }
585 
586 static uint16_t scl_vop_cal_scale(enum scale_mode mode, uint32_t src,
587 				  uint32_t dst, bool is_horizontal,
588 				  int vsu_mode, int *vskiplines)
589 {
590 	uint16_t val = 1 << SCL_FT_DEFAULT_FIXPOINT_SHIFT;
591 
592 	if (is_horizontal) {
593 		if (mode == SCALE_UP)
594 			val = GET_SCL_FT_BIC(src, dst);
595 		else if (mode == SCALE_DOWN)
596 			val = GET_SCL_FT_BILI_DN(src, dst);
597 	} else {
598 		if (mode == SCALE_UP) {
599 			if (vsu_mode == SCALE_UP_BIL)
600 				val = GET_SCL_FT_BILI_UP(src, dst);
601 			else
602 				val = GET_SCL_FT_BIC(src, dst);
603 		} else if (mode == SCALE_DOWN) {
604 			if (vskiplines) {
605 				*vskiplines = scl_get_vskiplines(src, dst);
606 				val = scl_get_bili_dn_vskip(src, dst,
607 							    *vskiplines);
608 			} else {
609 				val = GET_SCL_FT_BILI_DN(src, dst);
610 			}
611 		}
612 	}
613 
614 	return val;
615 }
616 
617 static void scl_vop_cal_scl_fac(struct vop *vop,
618 				uint32_t src_w, uint32_t src_h, uint32_t dst_w,
619 				uint32_t dst_h, uint32_t pixel_format)
620 {
621 	uint16_t yrgb_hor_scl_mode, yrgb_ver_scl_mode;
622 	uint16_t cbcr_hor_scl_mode = SCALE_NONE;
623 	uint16_t cbcr_ver_scl_mode = SCALE_NONE;
624 	int hsub = drm_format_horz_chroma_subsampling(pixel_format);
625 	int vsub = drm_format_vert_chroma_subsampling(pixel_format);
626 	bool is_yuv = false;
627 	uint16_t cbcr_src_w = src_w / hsub;
628 	uint16_t cbcr_src_h = src_h / vsub;
629 	uint16_t vsu_mode;
630 	uint16_t lb_mode;
631 	uint32_t val;
632 	int vskiplines = 0;
633 
634 	if (!vop->win->scl)
635 		return;
636 
637 	if (!vop->win->scl->ext) {
638 		VOP_SCL_SET(vop, scale_yrgb_x,
639 			    scl_cal_scale2(src_w, dst_w));
640 		VOP_SCL_SET(vop, scale_yrgb_y,
641 			    scl_cal_scale2(src_h, dst_h));
642 		if (is_yuv) {
643 			VOP_SCL_SET(vop, scale_cbcr_x,
644 				    scl_cal_scale2(src_w, dst_w));
645 			VOP_SCL_SET(vop, scale_cbcr_y,
646 				    scl_cal_scale2(src_h, dst_h));
647 		}
648 		return;
649 	}
650 
651 	yrgb_hor_scl_mode = scl_get_scl_mode(src_w, dst_w);
652 	yrgb_ver_scl_mode = scl_get_scl_mode(src_h, dst_h);
653 
654 	if (is_yuv) {
655 		cbcr_hor_scl_mode = scl_get_scl_mode(cbcr_src_w, dst_w);
656 		cbcr_ver_scl_mode = scl_get_scl_mode(cbcr_src_h, dst_h);
657 		if (cbcr_hor_scl_mode == SCALE_DOWN)
658 			lb_mode = scl_vop_cal_lb_mode(dst_w, true);
659 		else
660 			lb_mode = scl_vop_cal_lb_mode(cbcr_src_w, true);
661 	} else {
662 		if (yrgb_hor_scl_mode == SCALE_DOWN)
663 			lb_mode = scl_vop_cal_lb_mode(dst_w, false);
664 		else
665 			lb_mode = scl_vop_cal_lb_mode(src_w, false);
666 	}
667 
668 	VOP_SCL_SET_EXT(vop, lb_mode, lb_mode);
669 	if (lb_mode == LB_RGB_3840X2) {
670 		if (yrgb_ver_scl_mode != SCALE_NONE) {
671 			printf("ERROR : not allow yrgb ver scale\n");
672 			return;
673 		}
674 		if (cbcr_ver_scl_mode != SCALE_NONE) {
675 			printf("ERROR : not allow cbcr ver scale\n");
676 			return;
677 		}
678 		vsu_mode = SCALE_UP_BIL;
679 	} else if (lb_mode == LB_RGB_2560X4) {
680 		vsu_mode = SCALE_UP_BIL;
681 	} else {
682 		vsu_mode = SCALE_UP_BIC;
683 	}
684 
685 	val = scl_vop_cal_scale(yrgb_hor_scl_mode, src_w, dst_w,
686 				true, 0, NULL);
687 	VOP_SCL_SET(vop, scale_yrgb_x, val);
688 	val = scl_vop_cal_scale(yrgb_ver_scl_mode, src_h, dst_h,
689 				false, vsu_mode, &vskiplines);
690 	VOP_SCL_SET(vop, scale_yrgb_y, val);
691 
692 	VOP_SCL_SET_EXT(vop, vsd_yrgb_gt4, vskiplines == 4);
693 	VOP_SCL_SET_EXT(vop, vsd_yrgb_gt2, vskiplines == 2);
694 
695 	VOP_SCL_SET_EXT(vop, yrgb_hor_scl_mode, yrgb_hor_scl_mode);
696 	VOP_SCL_SET_EXT(vop, yrgb_ver_scl_mode, yrgb_ver_scl_mode);
697 	VOP_SCL_SET_EXT(vop, yrgb_hsd_mode, SCALE_DOWN_BIL);
698 	VOP_SCL_SET_EXT(vop, yrgb_vsd_mode, SCALE_DOWN_BIL);
699 	VOP_SCL_SET_EXT(vop, yrgb_vsu_mode, vsu_mode);
700 	if (is_yuv) {
701 		val = scl_vop_cal_scale(cbcr_hor_scl_mode, cbcr_src_w,
702 					dst_w, true, 0, NULL);
703 		VOP_SCL_SET(vop, scale_cbcr_x, val);
704 		val = scl_vop_cal_scale(cbcr_ver_scl_mode, cbcr_src_h,
705 					dst_h, false, vsu_mode, &vskiplines);
706 		VOP_SCL_SET(vop, scale_cbcr_y, val);
707 
708 		VOP_SCL_SET_EXT(vop, vsd_cbcr_gt4, vskiplines == 4);
709 		VOP_SCL_SET_EXT(vop, vsd_cbcr_gt2, vskiplines == 2);
710 		VOP_SCL_SET_EXT(vop, cbcr_hor_scl_mode, cbcr_hor_scl_mode);
711 		VOP_SCL_SET_EXT(vop, cbcr_ver_scl_mode, cbcr_ver_scl_mode);
712 		VOP_SCL_SET_EXT(vop, cbcr_hsd_mode, SCALE_DOWN_BIL);
713 		VOP_SCL_SET_EXT(vop, cbcr_vsd_mode, SCALE_DOWN_BIL);
714 		VOP_SCL_SET_EXT(vop, cbcr_vsu_mode, vsu_mode);
715 	}
716 }
717 
718 static void vop_load_csc_table(struct vop *vop, u32 offset, const u32 *table)
719 {
720 	int i;
721 
722 	/*
723 	 * so far the csc offset is not 0 and in the feature the csc offset
724 	 * impossible be 0, so when the offset is 0, should return here.
725 	 */
726 	if (!table || offset == 0)
727 		return;
728 
729 	for (i = 0; i < 8; i++)
730 		vop_writel(vop, offset + i * 4, table[i]);
731 }
732 
733 static int rockchip_vop_setup_csc_table(struct display_state *state)
734 {
735 	struct crtc_state *crtc_state = &state->crtc_state;
736 	struct connector_state *conn_state = &state->conn_state;
737 	struct vop *vop = crtc_state->private;
738 	const uint32_t *csc_table = NULL;
739 
740 	if (!vop->csc_table || !crtc_state->yuv_overlay)
741 		return 0;
742 	/* todo: only implement r2y*/
743 	switch (conn_state->color_encoding) {
744 	case DRM_COLOR_YCBCR_BT601:
745 		if (conn_state->color_range == DRM_COLOR_YCBCR_LIMITED_RANGE)
746 			csc_table = vop->csc_table->r2y_bt601_12_235; /* bt601 limit */
747 		else
748 			csc_table = vop->csc_table->r2y_bt601; /* bt601 full */
749 		break;
750 	case DRM_COLOR_YCBCR_BT709:
751 		csc_table = vop->csc_table->r2y_bt709;
752 		break;
753 	case DRM_COLOR_YCBCR_BT2020:
754 		csc_table = vop->csc_table->r2y_bt2020;
755 		break;
756 	default:
757 		csc_table = vop->csc_table->r2y_bt601; /* bt601 full */
758 		break;
759 	}
760 
761 	vop_load_csc_table(vop, vop->win_csc->r2y_offset, csc_table);
762 	VOP_WIN_CSC_SET(vop, r2y_en, 1);
763 
764 	return 0;
765 }
766 
767 static int rockchip_vop_set_plane(struct display_state *state)
768 {
769 	struct crtc_state *crtc_state = &state->crtc_state;
770 	const struct rockchip_crtc *crtc = crtc_state->crtc;
771 	const struct vop_data *vop_data = crtc->data;
772 	struct connector_state *conn_state = &state->conn_state;
773 	struct drm_display_mode *mode = &conn_state->mode;
774 	u32 act_info, dsp_info, dsp_st, dsp_stx, dsp_sty;
775 	struct vop *vop = crtc_state->private;
776 	int src_w = crtc_state->src_rect.w;
777 	int src_h = crtc_state->src_rect.h;
778 	int crtc_x = crtc_state->crtc_rect.x;
779 	int crtc_y = crtc_state->crtc_rect.y;
780 	int crtc_w = crtc_state->crtc_rect.w;
781 	int crtc_h = crtc_state->crtc_rect.h;
782 	int xvir = crtc_state->xvir;
783 	int x_mirror = 0, y_mirror = 0;
784 
785 	if (crtc_w > crtc_state->max_output.width) {
786 		printf("ERROR: output w[%d] exceeded max width[%d]\n",
787 		       crtc_w, crtc_state->max_output.width);
788 		return -EINVAL;
789 	}
790 
791 	if ((vop->version == VOP_VERSION(2, 2) || vop->version == VOP_VERSION(2, 0xd)) &&
792 	    (mode->flags & DRM_MODE_FLAG_INTERLACE))
793 		crtc_h = crtc_h / 2;
794 
795 	act_info = (src_h - 1) << 16;
796 	act_info |= (src_w - 1) & 0xffff;
797 
798 	dsp_info = (crtc_h - 1) << 16;
799 	dsp_info |= (crtc_w - 1) & 0xffff;
800 
801 	dsp_stx = crtc_x + mode->crtc_htotal - mode->crtc_hsync_start;
802 	dsp_sty = crtc_y + mode->crtc_vtotal - mode->crtc_vsync_start;
803 	if ((vop->version == VOP_VERSION(2, 2) || vop->version == VOP_VERSION(2, 0xd)) &&
804 	    (mode->flags & DRM_MODE_FLAG_INTERLACE))
805 		dsp_sty = crtc_y / 2 + mode->crtc_vtotal - mode->crtc_vsync_start;
806 	dsp_st = dsp_sty << 16 | (dsp_stx & 0xffff);
807 	/*
808 	 * vop full need to treats rgb888 as bgr888 so we reverse the rb swap to workaround
809 	 */
810 	if (crtc_state->format == ROCKCHIP_FMT_RGB888 && VOP_MAJOR(vop_data->version) == 3)
811 		crtc_state->rb_swap = !crtc_state->rb_swap;
812 
813 	if (mode->flags & DRM_MODE_FLAG_YMIRROR)
814 		y_mirror = 1;
815 	else
816 		y_mirror = 0;
817 	if (mode->flags & DRM_MODE_FLAG_XMIRROR)
818 		x_mirror = 1;
819 	else
820 		x_mirror = 0;
821 	if (crtc_state->ymirror ^ y_mirror)
822 		y_mirror = 1;
823 	else
824 		y_mirror = 0;
825 	if (y_mirror) {
826 		if (VOP_CTRL_SUPPORT(vop, ymirror))
827 			crtc_state->dma_addr += (src_h - 1) * xvir * 4;
828 		else
829 			y_mirror = 0;
830 		}
831 	VOP_CTRL_SET(vop, ymirror, y_mirror);
832 	VOP_CTRL_SET(vop, xmirror, x_mirror);
833 
834 	VOP_WIN_SET(vop, format, crtc_state->format);
835 	VOP_WIN_SET(vop, yrgb_vir, xvir);
836 	VOP_WIN_SET(vop, yrgb_mst, crtc_state->dma_addr);
837 
838 	scl_vop_cal_scl_fac(vop, src_w, src_h, crtc_w, crtc_h,
839 			    crtc_state->format);
840 
841 	VOP_WIN_SET(vop, act_info, act_info);
842 	VOP_WIN_SET(vop, dsp_info, dsp_info);
843 	VOP_WIN_SET(vop, dsp_st, dsp_st);
844 	VOP_WIN_SET(vop, rb_swap, crtc_state->rb_swap);
845 
846 	VOP_WIN_SET(vop, src_alpha_ctl, 0);
847 
848 	rockchip_vop_setup_csc_table(state);
849 	VOP_WIN_SET(vop, enable, 1);
850 	VOP_WIN_SET(vop, gate, 1);
851 	vop_cfg_done(vop);
852 
853 	return 0;
854 }
855 
856 static int rockchip_vop_prepare(struct display_state *state)
857 {
858 	return 0;
859 }
860 
861 static int rockchip_vop_enable(struct display_state *state)
862 {
863 	struct crtc_state *crtc_state = &state->crtc_state;
864 	struct vop *vop = crtc_state->private;
865 
866 	VOP_CTRL_SET(vop, standby, 0);
867 	vop_cfg_done(vop);
868 	if (crtc_state->mcu_timing.mcu_pix_total > 0)
869 		VOP_CTRL_SET(vop, mcu_hold_mode, 0);
870 
871 	return 0;
872 }
873 
874 static int rockchip_vop_disable(struct display_state *state)
875 {
876 	struct crtc_state *crtc_state = &state->crtc_state;
877 	struct vop *vop = crtc_state->private;
878 
879 	VOP_CTRL_SET(vop, standby, 1);
880 	vop_cfg_done(vop);
881 	return 0;
882 }
883 
884 static int rockchip_vop_fixup_dts(struct display_state *state, void *blob)
885 {
886 #if 0
887 	struct crtc_state *crtc_state = &state->crtc_state;
888 	struct panel_state *pstate = &state->panel_state;
889 	uint32_t phandle;
890 	char path[100];
891 	int ret, dsp_lut_node;
892 
893 	if (!ofnode_valid(pstate->dsp_lut_node))
894 		return 0;
895 	ret = fdt_get_path(state->blob, pstate->dsp_lut_node, path, sizeof(path));
896 	if (ret < 0) {
897 		printf("failed to get dsp_lut path[%s], ret=%d\n",
898 			path, ret);
899 		return ret;
900 	}
901 
902 	dsp_lut_node = fdt_path_offset(blob, path);
903 	phandle = fdt_get_phandle(blob, dsp_lut_node);
904 	if (!phandle) {
905 		phandle = fdt_alloc_phandle(blob);
906 		if (!phandle) {
907 			printf("failed to alloc phandle\n");
908 			return -ENOMEM;
909 		}
910 
911 		fdt_set_phandle(blob, dsp_lut_node, phandle);
912 	}
913 
914 	ret = fdt_get_path(state->blob, crtc_state->node, path, sizeof(path));
915 	if (ret < 0) {
916 		printf("failed to get route path[%s], ret=%d\n",
917 			path, ret);
918 		return ret;
919 	}
920 
921 	do_fixup_by_path_u32(blob, path, "dsp-lut", phandle, 1);
922 #endif
923 	return 0;
924 }
925 
926 static int rockchip_vop_send_mcu_cmd(struct display_state *state, u32 type, u32 value)
927 {
928 	struct crtc_state *crtc_state = &state->crtc_state;
929 	struct connector_state *conn_state = &state->conn_state;
930 	struct drm_display_mode *mode = &conn_state->mode;
931 	struct vop *vop = crtc_state->private;
932 	int ret;
933 
934 	if (vop->version == VOP_VERSION(2, 0xd)) {
935 		/*
936 		 * 1.set mcu bypass mode timing.
937 		 * 2.set dclk rate to 150M.
938 		 */
939 		if ((type == MCU_SETBYPASS) && value) {
940 			vop_mcu_bypass_mode_setup(state, vop);
941 			ret = clk_set_rate(&crtc_state->dclk, 150000000);
942 			if (IS_ERR_VALUE(ret)) {
943 				printf("%s: Failed to set dclk: ret=%d\n", __func__, ret);
944 				return ret;
945 			}
946 		}
947 	}
948 
949 	if (vop) {
950 		switch (type) {
951 		case MCU_WRCMD:
952 			set_vop_mcu_rs(vop, 0);
953 			VOP_CTRL_SET(vop, mcu_rw_bypass_port, value);
954 			set_vop_mcu_rs(vop, 1);
955 			break;
956 		case MCU_WRDATA:
957 			set_vop_mcu_rs(vop, 1);
958 			VOP_CTRL_SET(vop, mcu_rw_bypass_port, value);
959 			break;
960 		case MCU_SETBYPASS:
961 			VOP_CTRL_SET(vop, mcu_bypass, value ? 1 : 0);
962 			break;
963 		default:
964 			break;
965 		}
966 	}
967 
968 	if (vop->version == VOP_VERSION(2, 0xd)) {
969 		/*
970 		 * 1.restore mcu data mode timing.
971 		 * 2.restore dclk rate to crtc_clock.
972 		 */
973 		if ((type == MCU_SETBYPASS) && !value) {
974 			vop_mcu_mode_setup(state, vop);
975 			ret = clk_set_rate(&crtc_state->dclk, mode->crtc_clock * 1000);
976 			if (IS_ERR_VALUE(ret)) {
977 				printf("%s: Failed to set dclk: ret=%d\n", __func__, ret);
978 				return ret;
979 			}
980 		}
981 	}
982 
983 	return 0;
984 }
985 
986 static int rockchip_vop_mode_valid(struct display_state *state)
987 {
988 	struct connector_state *conn_state = &state->conn_state;
989 	struct drm_display_mode *mode = &conn_state->mode;
990 	struct videomode vm;
991 
992 	drm_display_mode_to_videomode(mode, &vm);
993 
994 	if (vm.hactive < 32 || vm.vactive < 32 ||
995 	    (vm.hfront_porch * vm.hsync_len * vm.hback_porch *
996 	     vm.vfront_porch * vm.vsync_len * vm.vback_porch == 0)) {
997 		printf("ERROR: unsupported display timing\n");
998 		return -EINVAL;
999 	}
1000 
1001 	return 0;
1002 }
1003 
1004 static int rockchip_vop_plane_check(struct display_state *state)
1005 {
1006 	struct crtc_state *crtc_state = &state->crtc_state;
1007 	const struct rockchip_crtc *crtc = crtc_state->crtc;
1008 	const struct vop_data *vop_data = crtc->data;
1009 	const struct vop_win *win = vop_data->win;
1010 	struct display_rect *src = &crtc_state->src_rect;
1011 	struct display_rect *dst = &crtc_state->crtc_rect;
1012 	int min_scale, max_scale;
1013 	int hscale, vscale;
1014 
1015 	min_scale = win->scl ? FRAC_16_16(1, 8) : VOP_PLANE_NO_SCALING;
1016 	max_scale = win->scl ? FRAC_16_16(8, 1) : VOP_PLANE_NO_SCALING;
1017 
1018 	hscale = display_rect_calc_hscale(src, dst, min_scale, max_scale);
1019 	vscale = display_rect_calc_vscale(src, dst, min_scale, max_scale);
1020 	if (hscale < 0 || vscale < 0) {
1021 		printf("ERROR: scale factor is out of range\n");
1022 		return -ERANGE;
1023 	}
1024 
1025 	return 0;
1026 }
1027 
1028 static int rockchip_vop_mode_fixup(struct display_state *state)
1029 {
1030 	struct crtc_state *crtc_state = &state->crtc_state;
1031 	struct connector_state *conn_state = &state->conn_state;
1032 	struct drm_display_mode *mode = &conn_state->mode;
1033 
1034 	drm_mode_set_crtcinfo(mode, CRTC_INTERLACE_HALVE_V | CRTC_STEREO_DOUBLE);
1035 
1036 	mode->crtc_clock *= rockchip_drm_get_cycles_per_pixel(conn_state->bus_format);
1037 	if (crtc_state->mcu_timing.mcu_pix_total)
1038 		mode->crtc_clock *= crtc_state->mcu_timing.mcu_pix_total + 1;
1039 
1040 	return 0;
1041 }
1042 
1043 const struct rockchip_crtc_funcs rockchip_vop_funcs = {
1044 	.preinit = rockchip_vop_preinit,
1045 	.init = rockchip_vop_init,
1046 	.set_plane = rockchip_vop_set_plane,
1047 	.prepare = rockchip_vop_prepare,
1048 	.enable = rockchip_vop_enable,
1049 	.disable = rockchip_vop_disable,
1050 	.fixup_dts = rockchip_vop_fixup_dts,
1051 	.send_mcu_cmd = rockchip_vop_send_mcu_cmd,
1052 	.mode_valid = rockchip_vop_mode_valid,
1053 	.plane_check = rockchip_vop_plane_check,
1054 	.mode_fixup = rockchip_vop_mode_fixup,
1055 };
1056