xref: /rk3399_rockchip-uboot/drivers/video/drm/rockchip_dw_hdmi.c (revision 2a3fb7bb049d69d96f3bc7dae8caa756fdc8a613)
1 /*
2  * (C) Copyright 2008-2017 Fuzhou Rockchip Electronics Co., Ltd
3  *
4  * SPDX-License-Identifier:	GPL-2.0+
5  */
6 
7 #include <common.h>
8 #include <boot_rkimg.h>
9 #include <asm/io.h>
10 #include <dm/device.h>
11 #include <linux/dw_hdmi.h>
12 #include <linux/hdmi.h>
13 #include <linux/media-bus-format.h>
14 #include "rockchip_display.h"
15 #include "rockchip_crtc.h"
16 #include "rockchip_connector.h"
17 #include "dw_hdmi.h"
18 #include "rockchip_dw_hdmi.h"
19 
20 #define HDMI_SEL_LCDC(x, bit)  ((((x) & 1) << bit) | (1 << (16 + bit)))
21 #define RK3288_GRF_SOC_CON6		0x025C
22 #define RK3288_HDMI_LCDC_SEL		BIT(4)
23 #define RK3399_GRF_SOC_CON20		0x6250
24 #define RK3399_HDMI_LCDC_SEL		BIT(6)
25 
26 #define RK3228_IO_3V_DOMAIN              ((7 << 4) | (7 << (4 + 16)))
27 #define RK3328_IO_3V_DOMAIN              (7 << (9 + 16))
28 #define RK3328_IO_5V_DOMAIN              ((7 << 9) | (3 << (9 + 16)))
29 #define RK3328_IO_CTRL_BY_HDMI           ((1 << 13) | (1 << (13 + 16)))
30 #define RK3328_IO_DDC_IN_MSK             ((3 << 10) | (3 << (10 + 16)))
31 #define RK3228_IO_DDC_IN_MSK             ((3 << 13) | (3 << (13 + 16)))
32 #define RK3228_GRF_SOC_CON2              0x0408
33 #define RK3228_GRF_SOC_CON6              0x0418
34 #define RK3328_GRF_SOC_CON2              0x0408
35 #define RK3328_GRF_SOC_CON3              0x040c
36 #define RK3328_GRF_SOC_CON4              0x0410
37 
38 static const struct dw_hdmi_mpll_config rockchip_mpll_cfg[] = {
39 	{
40 		30666000, {
41 			{ 0x00b3, 0x0000 },
42 			{ 0x2153, 0x0000 },
43 			{ 0x40f3, 0x0000 },
44 		},
45 	},  {
46 		36800000, {
47 			{ 0x00b3, 0x0000 },
48 			{ 0x2153, 0x0000 },
49 			{ 0x40a2, 0x0001 },
50 		},
51 	},  {
52 		46000000, {
53 			{ 0x00b3, 0x0000 },
54 			{ 0x2142, 0x0001 },
55 			{ 0x40a2, 0x0001 },
56 		},
57 	},  {
58 		61333000, {
59 			{ 0x0072, 0x0001 },
60 			{ 0x2142, 0x0001 },
61 			{ 0x40a2, 0x0001 },
62 		},
63 	},  {
64 		73600000, {
65 			{ 0x0072, 0x0001 },
66 			{ 0x2142, 0x0001 },
67 			{ 0x4061, 0x0002 },
68 		},
69 	},  {
70 		92000000, {
71 			{ 0x0072, 0x0001 },
72 			{ 0x2145, 0x0002 },
73 			{ 0x4061, 0x0002 },
74 		},
75 	},  {
76 		122666000, {
77 			{ 0x0051, 0x0002 },
78 			{ 0x2145, 0x0002 },
79 			{ 0x4061, 0x0002 },
80 		},
81 	},  {
82 		147200000, {
83 			{ 0x0051, 0x0002 },
84 			{ 0x2145, 0x0002 },
85 			{ 0x4064, 0x0003 },
86 		},
87 	},  {
88 		184000000, {
89 			{ 0x0051, 0x0002 },
90 			{ 0x214c, 0x0003 },
91 			{ 0x4064, 0x0003 },
92 		},
93 	},  {
94 		226666000, {
95 			{ 0x0040, 0x0003 },
96 			{ 0x214c, 0x0003 },
97 			{ 0x4064, 0x0003 },
98 		},
99 	},  {
100 		272000000, {
101 			{ 0x0040, 0x0003 },
102 			{ 0x214c, 0x0003 },
103 			{ 0x5a64, 0x0003 },
104 		},
105 	},  {
106 		340000000, {
107 			{ 0x0040, 0x0003 },
108 			{ 0x3b4c, 0x0003 },
109 			{ 0x5a64, 0x0003 },
110 		},
111 	},  {
112 		600000000, {
113 			{ 0x1a40, 0x0003 },
114 			{ 0x3b4c, 0x0003 },
115 			{ 0x5a64, 0x0003 },
116 		},
117 	},  {
118 		~0UL, {
119 			{ 0x0000, 0x0000 },
120 			{ 0x0000, 0x0000 },
121 			{ 0x0000, 0x0000 },
122 		},
123 	}
124 };
125 
126 static const struct dw_hdmi_mpll_config rockchip_mpll_cfg_420[] = {
127 	{
128 		30666000, {
129 			{ 0x00b7, 0x0000 },
130 			{ 0x2157, 0x0000 },
131 			{ 0x40f7, 0x0000 },
132 		},
133 	},  {
134 		92000000, {
135 			{ 0x00b7, 0x0000 },
136 			{ 0x2143, 0x0001 },
137 			{ 0x40a3, 0x0001 },
138 		},
139 	},  {
140 		184000000, {
141 			{ 0x0073, 0x0001 },
142 			{ 0x2146, 0x0002 },
143 			{ 0x4062, 0x0002 },
144 		},
145 	},  {
146 		340000000, {
147 			{ 0x0052, 0x0003 },
148 			{ 0x214d, 0x0003 },
149 			{ 0x4065, 0x0003 },
150 		},
151 	},  {
152 		600000000, {
153 			{ 0x0041, 0x0003 },
154 			{ 0x3b4d, 0x0003 },
155 			{ 0x5a65, 0x0003 },
156 		},
157 	},  {
158 		~0UL, {
159 			{ 0x0000, 0x0000 },
160 			{ 0x0000, 0x0000 },
161 			{ 0x0000, 0x0000 },
162 		},
163 	}
164 };
165 
166 static const struct dw_hdmi_curr_ctrl rockchip_cur_ctr[] = {
167 	/*      pixelclk    bpp8    bpp10   bpp12 */
168 	{
169 		600000000, { 0x0000, 0x0000, 0x0000 },
170 	},  {
171 		~0UL,      { 0x0000, 0x0000, 0x0000},
172 	}
173 };
174 
175 static const struct dw_hdmi_phy_config rockchip_phy_config[] = {
176 	/*pixelclk   symbol   term   vlev*/
177 	{ 74250000,  0x8009, 0x0004, 0x0272},
178 	{ 165000000, 0x802b, 0x0004, 0x0209},
179 	{ 297000000, 0x8039, 0x0005, 0x028d},
180 	{ 594000000, 0x8039, 0x0000, 0x019d},
181 	{ ~0UL,	     0x0000, 0x0000, 0x0000}
182 };
183 
184 static unsigned int drm_rk_select_color(struct hdmi_edid_data *edid_data,
185 					struct base_screen_info *screen_info,
186 					enum dw_hdmi_devtype dev_type)
187 {
188 	struct drm_display_info *info = &edid_data->display_info;
189 	struct drm_display_mode *mode = edid_data->preferred_mode;
190 	int max_tmds_clock = info->max_tmds_clock;
191 	bool support_dc = false;
192 	bool mode_420 = drm_mode_is_420(info, mode);
193 	unsigned int color_depth = 8;
194 	unsigned int base_color = DRM_HDMI_OUTPUT_YCBCR444;
195 	unsigned int color_format = DRM_HDMI_OUTPUT_DEFAULT_RGB;
196 	unsigned long tmdsclock, pixclock = mode->clock;
197 
198 	if (screen_info)
199 		base_color = screen_info->format;
200 
201 	switch (base_color) {
202 	case DRM_HDMI_OUTPUT_YCBCR_HQ:
203 		if (info->color_formats & DRM_COLOR_FORMAT_YCRCB444)
204 			color_format = DRM_HDMI_OUTPUT_YCBCR444;
205 		else if (info->color_formats & DRM_COLOR_FORMAT_YCRCB422)
206 			color_format = DRM_HDMI_OUTPUT_YCBCR422;
207 		else if (mode_420)
208 			color_format = DRM_HDMI_OUTPUT_YCBCR420;
209 		break;
210 	case DRM_HDMI_OUTPUT_YCBCR_LQ:
211 		if (mode_420)
212 			color_format = DRM_HDMI_OUTPUT_YCBCR420;
213 		else if (info->color_formats & DRM_COLOR_FORMAT_YCRCB422)
214 			color_format = DRM_HDMI_OUTPUT_YCBCR422;
215 		else if (info->color_formats & DRM_COLOR_FORMAT_YCRCB444)
216 			color_format = DRM_HDMI_OUTPUT_YCBCR444;
217 		break;
218 	case DRM_HDMI_OUTPUT_YCBCR420:
219 		if (mode_420)
220 			color_format = DRM_HDMI_OUTPUT_YCBCR420;
221 		break;
222 	case DRM_HDMI_OUTPUT_YCBCR422:
223 		if (info->color_formats & DRM_COLOR_FORMAT_YCRCB422)
224 			color_format = DRM_HDMI_OUTPUT_YCBCR422;
225 		break;
226 	case DRM_HDMI_OUTPUT_YCBCR444:
227 		if (info->color_formats & DRM_COLOR_FORMAT_YCRCB444)
228 			color_format = DRM_HDMI_OUTPUT_YCBCR444;
229 		break;
230 	case DRM_HDMI_OUTPUT_DEFAULT_RGB:
231 	default:
232 		break;
233 	}
234 
235 	if (color_format == DRM_HDMI_OUTPUT_DEFAULT_RGB &&
236 	    info->edid_hdmi_dc_modes & DRM_EDID_HDMI_DC_30)
237 		support_dc = true;
238 	if (color_format == DRM_HDMI_OUTPUT_YCBCR444 &&
239 	    (info->edid_hdmi_dc_modes &
240 	     (DRM_EDID_HDMI_DC_Y444 | DRM_EDID_HDMI_DC_30)))
241 		support_dc = true;
242 	if (color_format == DRM_HDMI_OUTPUT_YCBCR422)
243 		support_dc = true;
244 	if (color_format == DRM_HDMI_OUTPUT_YCBCR420 &&
245 	    info->hdmi.y420_dc_modes & DRM_EDID_YCBCR420_DC_30)
246 		support_dc = true;
247 
248 	if (mode->flags & DRM_MODE_FLAG_DBLCLK)
249 		pixclock *= 2;
250 
251 	if (screen_info && screen_info->depth == 10)
252 		color_depth = screen_info->depth;
253 
254 	if (color_format == DRM_HDMI_OUTPUT_YCBCR422 || color_depth == 8)
255 		tmdsclock = pixclock;
256 	else
257 		tmdsclock = pixclock * color_depth / 8;
258 
259 	if (color_format == DRM_HDMI_OUTPUT_YCBCR420)
260 		tmdsclock /= 2;
261 
262 	if (!max_tmds_clock)
263 		max_tmds_clock = 340000;
264 
265 	switch (dev_type) {
266 	case RK3368_HDMI:
267 		max_tmds_clock = min(max_tmds_clock, 340000);
268 		break;
269 	case RK3328_HDMI:
270 	case RK3228_HDMI:
271 		max_tmds_clock = min(max_tmds_clock, 371250);
272 		break;
273 	default:
274 		max_tmds_clock = min(max_tmds_clock, 594000);
275 		break;
276 	}
277 
278 	if (tmdsclock > max_tmds_clock) {
279 		if (max_tmds_clock >= 594000) {
280 			color_depth = 8;
281 		} else if (max_tmds_clock > 340000) {
282 			if (drm_mode_is_420(info, mode))
283 				color_format = DRM_HDMI_OUTPUT_YCBCR420;
284 		} else {
285 			color_depth = 8;
286 			if (drm_mode_is_420(info, mode))
287 				color_format = DRM_HDMI_OUTPUT_YCBCR420;
288 		}
289 	}
290 
291 	if (color_depth > 8 && support_dc) {
292 		if (dev_type == RK3288_HDMI)
293 			return MEDIA_BUS_FMT_RGB101010_1X30;
294 		switch (color_format) {
295 		case DRM_HDMI_OUTPUT_YCBCR444:
296 			return MEDIA_BUS_FMT_YUV10_1X30;
297 		case DRM_HDMI_OUTPUT_YCBCR422:
298 			return MEDIA_BUS_FMT_UYVY10_1X20;
299 		case DRM_HDMI_OUTPUT_YCBCR420:
300 			return MEDIA_BUS_FMT_UYYVYY10_0_5X30;
301 		default:
302 			return MEDIA_BUS_FMT_RGB101010_1X30;
303 		}
304 	} else {
305 		if (dev_type == RK3288_HDMI)
306 			return MEDIA_BUS_FMT_RGB888_1X24;
307 		switch (color_format) {
308 		case DRM_HDMI_OUTPUT_YCBCR444:
309 			return MEDIA_BUS_FMT_YUV8_1X24;
310 		case DRM_HDMI_OUTPUT_YCBCR422:
311 			return MEDIA_BUS_FMT_UYVY8_1X16;
312 		case DRM_HDMI_OUTPUT_YCBCR420:
313 			return MEDIA_BUS_FMT_UYYVYY8_0_5X24;
314 		default:
315 			return MEDIA_BUS_FMT_RGB888_1X24;
316 		}
317 	}
318 }
319 
320 void drm_rk_selete_output(struct hdmi_edid_data *edid_data,
321 			  unsigned int *bus_format,
322 			  struct overscan *overscan,
323 			  enum dw_hdmi_devtype dev_type)
324 {
325 	int ret, i, screen_size;
326 	struct base_disp_info base_parameter;
327 	const struct base_overscan *scan;
328 	struct base_screen_info *screen_info = NULL;
329 	int max_scan = 100;
330 	int min_scan = 51;
331 	struct blk_desc *dev_desc;
332 	disk_partition_t part_info;
333 	char baseparameter_buf[8 * RK_BLK_SIZE] __aligned(ARCH_DMA_MINALIGN);
334 
335 	overscan->left_margin = max_scan;
336 	overscan->right_margin = max_scan;
337 	overscan->top_margin = max_scan;
338 	overscan->bottom_margin = max_scan;
339 
340 	if (dev_type == RK3288_HDMI)
341 		*bus_format = MEDIA_BUS_FMT_RGB888_1X24;
342 	else
343 		*bus_format = MEDIA_BUS_FMT_YUV8_1X24;
344 
345 	dev_desc = rockchip_get_bootdev();
346 	if (!dev_desc) {
347 		printf("%s: Could not find device\n", __func__);
348 		return;
349 	}
350 
351 	if (part_get_info_by_name(dev_desc, "baseparameter", &part_info) < 0) {
352 		printf("Could not find baseparameter partition\n");
353 		return;
354 	}
355 
356 	ret = blk_dread(dev_desc, part_info.start, 1,
357 			(void *)baseparameter_buf);
358 	if (ret < 0) {
359 		printf("read baseparameter failed\n");
360 		return;
361 	}
362 
363 	memcpy(&base_parameter, baseparameter_buf, sizeof(base_parameter));
364 	scan = &base_parameter.scan;
365 
366 	if (scan->leftscale < min_scan && scan->leftscale > 0)
367 		overscan->left_margin = min_scan;
368 	else if (scan->leftscale < max_scan && scan->leftscale > 0)
369 		overscan->left_margin = scan->leftscale;
370 
371 	if (scan->rightscale < min_scan && scan->rightscale > 0)
372 		overscan->right_margin = min_scan;
373 	else if (scan->rightscale < max_scan && scan->rightscale > 0)
374 		overscan->right_margin = scan->rightscale;
375 
376 	if (scan->topscale < min_scan && scan->topscale > 0)
377 		overscan->top_margin = min_scan;
378 	else if (scan->topscale < max_scan && scan->topscale > 0)
379 		overscan->top_margin = scan->topscale;
380 
381 	if (scan->bottomscale < min_scan && scan->bottomscale > 0)
382 		overscan->bottom_margin = min_scan;
383 	else if (scan->bottomscale < max_scan && scan->bottomscale > 0)
384 		overscan->bottom_margin = scan->bottomscale;
385 
386 	screen_size = sizeof(base_parameter.screen_list) /
387 		sizeof(base_parameter.screen_list[0]);
388 
389 	for (i = 0; i < screen_size; i++) {
390 		if (base_parameter.screen_list[i].type ==
391 		    DRM_MODE_CONNECTOR_HDMIA) {
392 			screen_info = &base_parameter.screen_list[i];
393 			break;
394 		}
395 	}
396 
397 	if (screen_info)
398 		printf("base_parameter.mode:%dx%d\n",
399 		       screen_info->mode.hdisplay,
400 		       screen_info->mode.vdisplay);
401 	drm_rk_select_mode(edid_data, screen_info);
402 
403 	*bus_format = drm_rk_select_color(edid_data, screen_info,
404 					  dev_type);
405 }
406 
407 void inno_dw_hdmi_set_domain(void *grf, int status)
408 {
409 	if (status)
410 		writel(RK3328_IO_5V_DOMAIN, grf + RK3328_GRF_SOC_CON4);
411 	else
412 		writel(RK3328_IO_3V_DOMAIN, grf + RK3328_GRF_SOC_CON4);
413 }
414 
415 void dw_hdmi_set_iomux(void *grf, int dev_type)
416 {
417 	switch (dev_type) {
418 	case RK3328_HDMI:
419 		writel(RK3328_IO_DDC_IN_MSK, grf + RK3328_GRF_SOC_CON2);
420 		writel(RK3328_IO_CTRL_BY_HDMI, grf + RK3328_GRF_SOC_CON3);
421 		break;
422 	case RK3228_HDMI:
423 		writel(RK3228_IO_3V_DOMAIN, grf + RK3228_GRF_SOC_CON6);
424 		writel(RK3228_IO_DDC_IN_MSK, grf + RK3228_GRF_SOC_CON2);
425 		break;
426 	default:
427 		break;
428 	}
429 }
430 
431 static const struct dw_hdmi_phy_ops inno_dw_hdmi_phy_ops = {
432 	.init = inno_dw_hdmi_phy_init,
433 	.disable = inno_dw_hdmi_phy_disable,
434 	.read_hpd = inno_dw_hdmi_phy_read_hpd,
435 	.mode_valid = inno_dw_hdmi_mode_valid,
436 };
437 
438 static const struct rockchip_connector_funcs rockchip_dw_hdmi_funcs = {
439 	.init = rockchip_dw_hdmi_init,
440 	.deinit = rockchip_dw_hdmi_deinit,
441 	.prepare = rockchip_dw_hdmi_prepare,
442 	.enable = rockchip_dw_hdmi_enable,
443 	.disable = rockchip_dw_hdmi_disable,
444 	.get_timing = rockchip_dw_hdmi_get_timing,
445 	.detect = rockchip_dw_hdmi_detect,
446 	.get_edid = rockchip_dw_hdmi_get_edid,
447 };
448 
449 const struct dw_hdmi_plat_data rk3288_hdmi_drv_data = {
450 	.vop_sel_bit = 4,
451 	.grf_vop_sel_reg = RK3288_GRF_SOC_CON6,
452 	.mpll_cfg   = rockchip_mpll_cfg,
453 	.cur_ctr    = rockchip_cur_ctr,
454 	.phy_config = rockchip_phy_config,
455 	.dev_type   = RK3288_HDMI,
456 };
457 
458 const struct dw_hdmi_plat_data rk3328_hdmi_drv_data = {
459 	.vop_sel_bit = 0,
460 	.grf_vop_sel_reg = 0,
461 	.phy_ops    = &inno_dw_hdmi_phy_ops,
462 	.phy_name   = "inno_dw_hdmi_phy2",
463 	.dev_type   = RK3328_HDMI,
464 };
465 
466 const struct dw_hdmi_plat_data rk3228_hdmi_drv_data = {
467 	.vop_sel_bit = 0,
468 	.grf_vop_sel_reg = 0,
469 	.phy_ops    = &inno_dw_hdmi_phy_ops,
470 	.phy_name   = "inno_dw_hdmi_phy",
471 	.dev_type   = RK3228_HDMI,
472 };
473 
474 const struct dw_hdmi_plat_data rk3368_hdmi_drv_data = {
475 	.mpll_cfg   = rockchip_mpll_cfg,
476 	.cur_ctr    = rockchip_cur_ctr,
477 	.phy_config = rockchip_phy_config,
478 	.mpll_cfg_420 = rockchip_mpll_cfg_420,
479 	.dev_type   = RK3368_HDMI,
480 };
481 
482 const struct dw_hdmi_plat_data rk3399_hdmi_drv_data = {
483 	.vop_sel_bit = 6,
484 	.grf_vop_sel_reg = RK3399_GRF_SOC_CON20,
485 	.mpll_cfg   = rockchip_mpll_cfg,
486 	.cur_ctr    = rockchip_cur_ctr,
487 	.phy_config = rockchip_phy_config,
488 	.mpll_cfg_420 = rockchip_mpll_cfg_420,
489 	.dev_type   = RK3399_HDMI,
490 };
491 
492 static int rockchip_dw_hdmi_probe(struct udevice *dev)
493 {
494 	return 0;
495 }
496 
497 static const struct rockchip_connector rk3399_dw_hdmi_data = {
498 	.funcs = &rockchip_dw_hdmi_funcs,
499 	.data = &rk3399_hdmi_drv_data,
500 };
501 
502 static const struct rockchip_connector rk3368_dw_hdmi_data = {
503 	.funcs = &rockchip_dw_hdmi_funcs,
504 	.data = &rk3368_hdmi_drv_data,
505 };
506 
507 static const struct rockchip_connector rk3288_dw_hdmi_data = {
508 	.funcs = &rockchip_dw_hdmi_funcs,
509 	.data = &rk3288_hdmi_drv_data,
510 };
511 
512 static const struct rockchip_connector rk3328_dw_hdmi_data = {
513 	.funcs = &rockchip_dw_hdmi_funcs,
514 	.data = &rk3328_hdmi_drv_data,
515 };
516 
517 static const struct rockchip_connector rk3228_dw_hdmi_data = {
518 	.funcs = &rockchip_dw_hdmi_funcs,
519 	.data = &rk3228_hdmi_drv_data,
520 };
521 
522 static const struct udevice_id rockchip_dw_hdmi_ids[] = {
523 	{
524 	 .compatible = "rockchip,rk3399-dw-hdmi",
525 	 .data = (ulong)&rk3399_dw_hdmi_data,
526 	}, {
527 	 .compatible = "rockchip,rk3368-dw-hdmi",
528 	 .data = (ulong)&rk3368_dw_hdmi_data,
529 	}, {
530 	 .compatible = "rockchip,rk3288-dw-hdmi",
531 	 .data = (ulong)&rk3288_dw_hdmi_data,
532 	}, {
533 	 .compatible = "rockchip,rk3328-dw-hdmi",
534 	 .data = (ulong)&rk3328_dw_hdmi_data,
535 	}, {
536 	 .compatible = "rockchip,rk3128-inno-hdmi",
537 	 .data = (ulong)&rk3228_dw_hdmi_data,
538 	}, {
539 	 .compatible = "rockchip,rk3228-dw-hdmi",
540 	 .data = (ulong)&rk3228_dw_hdmi_data,
541 	}, {}
542 };
543 
544 U_BOOT_DRIVER(rockchip_dw_hdmi) = {
545 	.name = "rockchip_dw_hdmi",
546 	.id = UCLASS_DISPLAY,
547 	.of_match = rockchip_dw_hdmi_ids,
548 	.probe	= rockchip_dw_hdmi_probe,
549 };
550