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