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