xref: /rk3399_rockchip-uboot/drivers/video/drm/rockchip_vop.c (revision efd10ad6e1e5e8c0bfb545a455a0d19108aeb2de)
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\n", vop->regs, mode->crtc_clock);
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_MAJOR(vop->version) == 3 &&
556 		 VOP_MINOR(vop->version) >= 5)
557 		val = 0x20010200;
558 	else
559 		val = 0x801080;
560 	VOP_CTRL_SET(vop, dsp_background, val);
561 
562 	VOP_CTRL_SET(vop, htotal_pw, (htotal << 16) | hsync_len);
563 	val = hact_st << 16;
564 	val |= hact_end;
565 	VOP_CTRL_SET(vop, hact_st_end, val);
566 	val = vact_st << 16;
567 	val |= vact_end;
568 	VOP_CTRL_SET(vop, vact_st_end, val);
569 	if (mode->flags & DRM_MODE_FLAG_INTERLACE) {
570 		u16 vact_st_f1 = vtotal + vact_st + 1;
571 		u16 vact_end_f1 = vact_st_f1 + vdisplay;
572 
573 		val = vact_st_f1 << 16 | vact_end_f1;
574 		VOP_CTRL_SET(vop, vact_st_end_f1, val);
575 
576 		val = vtotal << 16 | (vtotal + vsync_len);
577 		VOP_CTRL_SET(vop, vs_st_end_f1, val);
578 		VOP_CTRL_SET(vop, dsp_interlace, 1);
579 		VOP_CTRL_SET(vop, p2i_en, 1);
580 		vtotal += vtotal + 1;
581 		act_end = vact_end_f1;
582 	} else {
583 		VOP_CTRL_SET(vop, dsp_interlace, 0);
584 		VOP_CTRL_SET(vop, p2i_en, 0);
585 		act_end = vact_end;
586 	}
587 	VOP_CTRL_SET(vop, vtotal_pw, (vtotal << 16) | vsync_len);
588 	vop_post_config(state, vop);
589 	VOP_CTRL_SET(vop, core_dclk_div,
590 		     !!(mode->flags & DRM_MODE_FLAG_DBLCLK));
591 
592 	VOP_LINE_FLAG_SET(vop, line_flag_num[0], act_end - 3);
593 	VOP_LINE_FLAG_SET(vop, line_flag_num[1],
594 			  act_end - us_to_vertical_line(mode, 1000));
595 	if (state->crtc_state.mcu_timing.mcu_pix_total > 0)
596 		vop_mcu_mode_setup(state, vop);
597 	vop_cfg_done(vop);
598 
599 	return 0;
600 }
601 
602 static uint16_t scl_vop_cal_scale(enum scale_mode mode, uint32_t src,
603 				  uint32_t dst, bool is_horizontal,
604 				  int vsu_mode, int *vskiplines)
605 {
606 	uint16_t val = 1 << SCL_FT_DEFAULT_FIXPOINT_SHIFT;
607 
608 	if (is_horizontal) {
609 		if (mode == SCALE_UP)
610 			val = GET_SCL_FT_BIC(src, dst);
611 		else if (mode == SCALE_DOWN)
612 			val = GET_SCL_FT_BILI_DN(src, dst);
613 	} else {
614 		if (mode == SCALE_UP) {
615 			if (vsu_mode == SCALE_UP_BIL)
616 				val = GET_SCL_FT_BILI_UP(src, dst);
617 			else
618 				val = GET_SCL_FT_BIC(src, dst);
619 		} else if (mode == SCALE_DOWN) {
620 			if (vskiplines) {
621 				*vskiplines = scl_get_vskiplines(src, dst);
622 				val = scl_get_bili_dn_vskip(src, dst,
623 							    *vskiplines);
624 			} else {
625 				val = GET_SCL_FT_BILI_DN(src, dst);
626 			}
627 		}
628 	}
629 
630 	return val;
631 }
632 
633 static void scl_vop_cal_scl_fac(struct vop *vop,
634 				uint32_t src_w, uint32_t src_h, uint32_t dst_w,
635 				uint32_t dst_h, uint32_t pixel_format)
636 {
637 	uint16_t yrgb_hor_scl_mode, yrgb_ver_scl_mode;
638 	uint16_t cbcr_hor_scl_mode = SCALE_NONE;
639 	uint16_t cbcr_ver_scl_mode = SCALE_NONE;
640 	int hsub = drm_format_horz_chroma_subsampling(pixel_format);
641 	int vsub = drm_format_vert_chroma_subsampling(pixel_format);
642 	bool is_yuv = false;
643 	uint16_t cbcr_src_w = src_w / hsub;
644 	uint16_t cbcr_src_h = src_h / vsub;
645 	uint16_t vsu_mode;
646 	uint16_t lb_mode;
647 	uint32_t val;
648 	int vskiplines = 0;
649 
650 	if (!vop->win->scl)
651 		return;
652 
653 	if (!vop->win->scl->ext) {
654 		VOP_SCL_SET(vop, scale_yrgb_x,
655 			    scl_cal_scale2(src_w, dst_w));
656 		VOP_SCL_SET(vop, scale_yrgb_y,
657 			    scl_cal_scale2(src_h, dst_h));
658 		if (is_yuv) {
659 			VOP_SCL_SET(vop, scale_cbcr_x,
660 				    scl_cal_scale2(src_w, dst_w));
661 			VOP_SCL_SET(vop, scale_cbcr_y,
662 				    scl_cal_scale2(src_h, dst_h));
663 		}
664 		return;
665 	}
666 
667 	yrgb_hor_scl_mode = scl_get_scl_mode(src_w, dst_w);
668 	yrgb_ver_scl_mode = scl_get_scl_mode(src_h, dst_h);
669 
670 	if (is_yuv) {
671 		cbcr_hor_scl_mode = scl_get_scl_mode(cbcr_src_w, dst_w);
672 		cbcr_ver_scl_mode = scl_get_scl_mode(cbcr_src_h, dst_h);
673 		if (cbcr_hor_scl_mode == SCALE_DOWN)
674 			lb_mode = scl_vop_cal_lb_mode(dst_w, true);
675 		else
676 			lb_mode = scl_vop_cal_lb_mode(cbcr_src_w, true);
677 	} else {
678 		if (yrgb_hor_scl_mode == SCALE_DOWN)
679 			lb_mode = scl_vop_cal_lb_mode(dst_w, false);
680 		else
681 			lb_mode = scl_vop_cal_lb_mode(src_w, false);
682 	}
683 
684 	VOP_SCL_SET_EXT(vop, lb_mode, lb_mode);
685 	if (lb_mode == LB_RGB_3840X2) {
686 		if (yrgb_ver_scl_mode != SCALE_NONE) {
687 			printf("ERROR : not allow yrgb ver scale\n");
688 			return;
689 		}
690 		if (cbcr_ver_scl_mode != SCALE_NONE) {
691 			printf("ERROR : not allow cbcr ver scale\n");
692 			return;
693 		}
694 		vsu_mode = SCALE_UP_BIL;
695 	} else if (lb_mode == LB_RGB_2560X4) {
696 		vsu_mode = SCALE_UP_BIL;
697 	} else {
698 		vsu_mode = SCALE_UP_BIC;
699 	}
700 
701 	val = scl_vop_cal_scale(yrgb_hor_scl_mode, src_w, dst_w,
702 				true, 0, NULL);
703 	VOP_SCL_SET(vop, scale_yrgb_x, val);
704 	val = scl_vop_cal_scale(yrgb_ver_scl_mode, src_h, dst_h,
705 				false, vsu_mode, &vskiplines);
706 	VOP_SCL_SET(vop, scale_yrgb_y, val);
707 
708 	VOP_SCL_SET_EXT(vop, vsd_yrgb_gt4, vskiplines == 4);
709 	VOP_SCL_SET_EXT(vop, vsd_yrgb_gt2, vskiplines == 2);
710 
711 	VOP_SCL_SET_EXT(vop, yrgb_hor_scl_mode, yrgb_hor_scl_mode);
712 	VOP_SCL_SET_EXT(vop, yrgb_ver_scl_mode, yrgb_ver_scl_mode);
713 	VOP_SCL_SET_EXT(vop, yrgb_hsd_mode, SCALE_DOWN_BIL);
714 	VOP_SCL_SET_EXT(vop, yrgb_vsd_mode, SCALE_DOWN_BIL);
715 	VOP_SCL_SET_EXT(vop, yrgb_vsu_mode, vsu_mode);
716 	if (is_yuv) {
717 		val = scl_vop_cal_scale(cbcr_hor_scl_mode, cbcr_src_w,
718 					dst_w, true, 0, NULL);
719 		VOP_SCL_SET(vop, scale_cbcr_x, val);
720 		val = scl_vop_cal_scale(cbcr_ver_scl_mode, cbcr_src_h,
721 					dst_h, false, vsu_mode, &vskiplines);
722 		VOP_SCL_SET(vop, scale_cbcr_y, val);
723 
724 		VOP_SCL_SET_EXT(vop, vsd_cbcr_gt4, vskiplines == 4);
725 		VOP_SCL_SET_EXT(vop, vsd_cbcr_gt2, vskiplines == 2);
726 		VOP_SCL_SET_EXT(vop, cbcr_hor_scl_mode, cbcr_hor_scl_mode);
727 		VOP_SCL_SET_EXT(vop, cbcr_ver_scl_mode, cbcr_ver_scl_mode);
728 		VOP_SCL_SET_EXT(vop, cbcr_hsd_mode, SCALE_DOWN_BIL);
729 		VOP_SCL_SET_EXT(vop, cbcr_vsd_mode, SCALE_DOWN_BIL);
730 		VOP_SCL_SET_EXT(vop, cbcr_vsu_mode, vsu_mode);
731 	}
732 }
733 
734 static void vop_load_csc_table(struct vop *vop, u32 offset, const u32 *table)
735 {
736 	int i;
737 
738 	/*
739 	 * so far the csc offset is not 0 and in the feature the csc offset
740 	 * impossible be 0, so when the offset is 0, should return here.
741 	 */
742 	if (!table || offset == 0)
743 		return;
744 
745 	for (i = 0; i < 8; i++)
746 		vop_writel(vop, offset + i * 4, table[i]);
747 }
748 
749 static int rockchip_vop_setup_csc_table(struct display_state *state)
750 {
751 	struct crtc_state *crtc_state = &state->crtc_state;
752 	struct connector_state *conn_state = &state->conn_state;
753 	struct vop *vop = crtc_state->private;
754 	const uint32_t *csc_table = NULL;
755 
756 	if (!vop->csc_table || !crtc_state->yuv_overlay)
757 		return 0;
758 	/* todo: only implement r2y*/
759 	switch (conn_state->color_encoding) {
760 	case DRM_COLOR_YCBCR_BT601:
761 		if (conn_state->color_range == DRM_COLOR_YCBCR_LIMITED_RANGE)
762 			csc_table = vop->csc_table->r2y_bt601_12_235; /* bt601 limit */
763 		else
764 			csc_table = vop->csc_table->r2y_bt601; /* bt601 full */
765 		break;
766 	case DRM_COLOR_YCBCR_BT709:
767 		csc_table = vop->csc_table->r2y_bt709;
768 		break;
769 	case DRM_COLOR_YCBCR_BT2020:
770 		csc_table = vop->csc_table->r2y_bt2020;
771 		break;
772 	default:
773 		csc_table = vop->csc_table->r2y_bt601; /* bt601 full */
774 		break;
775 	}
776 
777 	vop_load_csc_table(vop, vop->win_csc->r2y_offset, csc_table);
778 	VOP_WIN_CSC_SET(vop, r2y_en, 1);
779 
780 	return 0;
781 }
782 
783 static int rockchip_vop_set_plane(struct display_state *state)
784 {
785 	struct crtc_state *crtc_state = &state->crtc_state;
786 	const struct rockchip_crtc *crtc = crtc_state->crtc;
787 	const struct vop_data *vop_data = crtc->data;
788 	struct connector_state *conn_state = &state->conn_state;
789 	struct drm_display_mode *mode = &conn_state->mode;
790 	u32 act_info, dsp_info, dsp_st, dsp_stx, dsp_sty;
791 	struct vop *vop = crtc_state->private;
792 	int src_w = crtc_state->src_rect.w;
793 	int src_h = crtc_state->src_rect.h;
794 	int crtc_x = crtc_state->crtc_rect.x;
795 	int crtc_y = crtc_state->crtc_rect.y;
796 	int crtc_w = crtc_state->crtc_rect.w;
797 	int crtc_h = crtc_state->crtc_rect.h;
798 	int xvir = crtc_state->xvir;
799 	int x_mirror = 0, y_mirror = 0;
800 
801 	if (crtc_w > crtc_state->max_output.width) {
802 		printf("ERROR: output w[%d] exceeded max width[%d]\n",
803 		       crtc_w, crtc_state->max_output.width);
804 		return -EINVAL;
805 	}
806 
807 	if ((vop->version == VOP_VERSION(2, 2) || vop->version == VOP_VERSION(2, 0xd)) &&
808 	    (mode->flags & DRM_MODE_FLAG_INTERLACE))
809 		crtc_h = crtc_h / 2;
810 
811 	act_info = (src_h - 1) << 16;
812 	act_info |= (src_w - 1) & 0xffff;
813 
814 	dsp_info = (crtc_h - 1) << 16;
815 	dsp_info |= (crtc_w - 1) & 0xffff;
816 
817 	dsp_stx = crtc_x + mode->crtc_htotal - mode->crtc_hsync_start;
818 	dsp_sty = crtc_y + mode->crtc_vtotal - mode->crtc_vsync_start;
819 	if ((vop->version == VOP_VERSION(2, 2) || vop->version == VOP_VERSION(2, 0xd)) &&
820 	    (mode->flags & DRM_MODE_FLAG_INTERLACE))
821 		dsp_sty = crtc_y / 2 + mode->crtc_vtotal - mode->crtc_vsync_start;
822 	dsp_st = dsp_sty << 16 | (dsp_stx & 0xffff);
823 	/*
824 	 * vop full need to treats rgb888 as bgr888 so we reverse the rb swap to workaround
825 	 */
826 	if (crtc_state->format == ROCKCHIP_FMT_RGB888 && VOP_MAJOR(vop_data->version) == 3)
827 		crtc_state->rb_swap = !crtc_state->rb_swap;
828 
829 	if (mode->flags & DRM_MODE_FLAG_YMIRROR)
830 		y_mirror = 1;
831 	else
832 		y_mirror = 0;
833 	if (mode->flags & DRM_MODE_FLAG_XMIRROR)
834 		x_mirror = 1;
835 	else
836 		x_mirror = 0;
837 	if (crtc_state->ymirror ^ y_mirror)
838 		y_mirror = 1;
839 	else
840 		y_mirror = 0;
841 	if (y_mirror) {
842 		if (VOP_CTRL_SUPPORT(vop, ymirror))
843 			crtc_state->dma_addr += (src_h - 1) * xvir * 4;
844 		else
845 			y_mirror = 0;
846 		}
847 	VOP_CTRL_SET(vop, ymirror, y_mirror);
848 	VOP_CTRL_SET(vop, xmirror, x_mirror);
849 
850 	VOP_WIN_SET(vop, format, crtc_state->format);
851 	VOP_WIN_SET(vop, yrgb_vir, xvir);
852 	VOP_WIN_SET(vop, yrgb_mst, crtc_state->dma_addr);
853 
854 	scl_vop_cal_scl_fac(vop, src_w, src_h, crtc_w, crtc_h,
855 			    crtc_state->format);
856 
857 	VOP_WIN_SET(vop, act_info, act_info);
858 	VOP_WIN_SET(vop, dsp_info, dsp_info);
859 	VOP_WIN_SET(vop, dsp_st, dsp_st);
860 	VOP_WIN_SET(vop, rb_swap, crtc_state->rb_swap);
861 
862 	VOP_WIN_SET(vop, src_alpha_ctl, 0);
863 
864 	rockchip_vop_setup_csc_table(state);
865 	VOP_WIN_SET(vop, enable, 1);
866 	VOP_WIN_SET(vop, gate, 1);
867 	vop_cfg_done(vop);
868 
869 	return 0;
870 }
871 
872 static int rockchip_vop_prepare(struct display_state *state)
873 {
874 	return 0;
875 }
876 
877 static int rockchip_vop_enable(struct display_state *state)
878 {
879 	struct crtc_state *crtc_state = &state->crtc_state;
880 	struct vop *vop = crtc_state->private;
881 
882 	VOP_CTRL_SET(vop, standby, 0);
883 	vop_cfg_done(vop);
884 	if (crtc_state->mcu_timing.mcu_pix_total > 0)
885 		VOP_CTRL_SET(vop, mcu_hold_mode, 0);
886 
887 	return 0;
888 }
889 
890 static int rockchip_vop_disable(struct display_state *state)
891 {
892 	struct crtc_state *crtc_state = &state->crtc_state;
893 	struct vop *vop = crtc_state->private;
894 
895 	VOP_CTRL_SET(vop, standby, 1);
896 	vop_cfg_done(vop);
897 	return 0;
898 }
899 
900 static int rockchip_vop_fixup_dts(struct display_state *state, void *blob)
901 {
902 #if 0
903 	struct crtc_state *crtc_state = &state->crtc_state;
904 	struct panel_state *pstate = &state->panel_state;
905 	uint32_t phandle;
906 	char path[100];
907 	int ret, dsp_lut_node;
908 
909 	if (!ofnode_valid(pstate->dsp_lut_node))
910 		return 0;
911 	ret = fdt_get_path(state->blob, pstate->dsp_lut_node, path, sizeof(path));
912 	if (ret < 0) {
913 		printf("failed to get dsp_lut path[%s], ret=%d\n",
914 			path, ret);
915 		return ret;
916 	}
917 
918 	dsp_lut_node = fdt_path_offset(blob, path);
919 	phandle = fdt_get_phandle(blob, dsp_lut_node);
920 	if (!phandle) {
921 		phandle = fdt_alloc_phandle(blob);
922 		if (!phandle) {
923 			printf("failed to alloc phandle\n");
924 			return -ENOMEM;
925 		}
926 
927 		fdt_set_phandle(blob, dsp_lut_node, phandle);
928 	}
929 
930 	ret = fdt_get_path(state->blob, crtc_state->node, path, sizeof(path));
931 	if (ret < 0) {
932 		printf("failed to get route path[%s], ret=%d\n",
933 			path, ret);
934 		return ret;
935 	}
936 
937 	do_fixup_by_path_u32(blob, path, "dsp-lut", phandle, 1);
938 #endif
939 	return 0;
940 }
941 
942 static int rockchip_vop_send_mcu_cmd(struct display_state *state, u32 type, u32 value)
943 {
944 	struct crtc_state *crtc_state = &state->crtc_state;
945 	struct connector_state *conn_state = &state->conn_state;
946 	struct drm_display_mode *mode = &conn_state->mode;
947 	struct vop *vop = crtc_state->private;
948 	int ret;
949 
950 	if (vop->version == VOP_VERSION(2, 0xd)) {
951 		/*
952 		 * 1.set mcu bypass mode timing.
953 		 * 2.set dclk rate to 150M.
954 		 */
955 		if ((type == MCU_SETBYPASS) && value) {
956 			vop_mcu_bypass_mode_setup(state, vop);
957 			ret = clk_set_rate(&crtc_state->dclk, 150000000);
958 			if (IS_ERR_VALUE(ret)) {
959 				printf("%s: Failed to set dclk: ret=%d\n", __func__, ret);
960 				return ret;
961 			}
962 		}
963 	}
964 
965 	if (vop) {
966 		switch (type) {
967 		case MCU_WRCMD:
968 			set_vop_mcu_rs(vop, 0);
969 			VOP_CTRL_SET(vop, mcu_rw_bypass_port, value);
970 			set_vop_mcu_rs(vop, 1);
971 			break;
972 		case MCU_WRDATA:
973 			set_vop_mcu_rs(vop, 1);
974 			VOP_CTRL_SET(vop, mcu_rw_bypass_port, value);
975 			break;
976 		case MCU_SETBYPASS:
977 			VOP_CTRL_SET(vop, mcu_bypass, value ? 1 : 0);
978 			break;
979 		default:
980 			break;
981 		}
982 	}
983 
984 	if (vop->version == VOP_VERSION(2, 0xd)) {
985 		/*
986 		 * 1.restore mcu data mode timing.
987 		 * 2.restore dclk rate to crtc_clock.
988 		 */
989 		if ((type == MCU_SETBYPASS) && !value) {
990 			vop_mcu_mode_setup(state, vop);
991 			ret = clk_set_rate(&crtc_state->dclk, mode->crtc_clock * 1000);
992 			if (IS_ERR_VALUE(ret)) {
993 				printf("%s: Failed to set dclk: ret=%d\n", __func__, ret);
994 				return ret;
995 			}
996 		}
997 	}
998 
999 	return 0;
1000 }
1001 
1002 static int rockchip_vop_mode_valid(struct display_state *state)
1003 {
1004 	struct connector_state *conn_state = &state->conn_state;
1005 	struct drm_display_mode *mode = &conn_state->mode;
1006 	struct videomode vm;
1007 
1008 	drm_display_mode_to_videomode(mode, &vm);
1009 
1010 	if (vm.hactive < 32 || vm.vactive < 32 ||
1011 	    (vm.hfront_porch * vm.hsync_len * vm.hback_porch *
1012 	     vm.vfront_porch * vm.vsync_len * vm.vback_porch == 0)) {
1013 		printf("ERROR: unsupported display timing\n");
1014 		return -EINVAL;
1015 	}
1016 
1017 	return 0;
1018 }
1019 
1020 static int rockchip_vop_plane_check(struct display_state *state)
1021 {
1022 	struct crtc_state *crtc_state = &state->crtc_state;
1023 	const struct rockchip_crtc *crtc = crtc_state->crtc;
1024 	const struct vop_data *vop_data = crtc->data;
1025 	const struct vop_win *win = vop_data->win;
1026 	struct display_rect *src = &crtc_state->src_rect;
1027 	struct display_rect *dst = &crtc_state->crtc_rect;
1028 	int min_scale, max_scale;
1029 	int hscale, vscale;
1030 
1031 	min_scale = win->scl ? FRAC_16_16(1, 8) : VOP_PLANE_NO_SCALING;
1032 	max_scale = win->scl ? FRAC_16_16(8, 1) : VOP_PLANE_NO_SCALING;
1033 
1034 	hscale = display_rect_calc_hscale(src, dst, min_scale, max_scale);
1035 	vscale = display_rect_calc_vscale(src, dst, min_scale, max_scale);
1036 	if (hscale < 0 || vscale < 0) {
1037 		printf("ERROR: scale factor is out of range\n");
1038 		return -ERANGE;
1039 	}
1040 
1041 	return 0;
1042 }
1043 
1044 static int rockchip_vop_mode_fixup(struct display_state *state)
1045 {
1046 	struct crtc_state *crtc_state = &state->crtc_state;
1047 	struct connector_state *conn_state = &state->conn_state;
1048 	struct drm_display_mode *mode = &conn_state->mode;
1049 
1050 	drm_mode_set_crtcinfo(mode, CRTC_INTERLACE_HALVE_V | CRTC_STEREO_DOUBLE);
1051 
1052 	mode->crtc_clock *= rockchip_drm_get_cycles_per_pixel(conn_state->bus_format);
1053 	if (crtc_state->mcu_timing.mcu_pix_total)
1054 		mode->crtc_clock *= crtc_state->mcu_timing.mcu_pix_total + 1;
1055 
1056 	return 0;
1057 }
1058 
1059 const struct rockchip_crtc_funcs rockchip_vop_funcs = {
1060 	.preinit = rockchip_vop_preinit,
1061 	.init = rockchip_vop_init,
1062 	.set_plane = rockchip_vop_set_plane,
1063 	.prepare = rockchip_vop_prepare,
1064 	.enable = rockchip_vop_enable,
1065 	.disable = rockchip_vop_disable,
1066 	.fixup_dts = rockchip_vop_fixup_dts,
1067 	.send_mcu_cmd = rockchip_vop_send_mcu_cmd,
1068 	.mode_valid = rockchip_vop_mode_valid,
1069 	.plane_check = rockchip_vop_plane_check,
1070 	.mode_fixup = rockchip_vop_mode_fixup,
1071 };
1072