1 /* 2 * (C) Copyright 2008-2017 Fuzhou Rockchip Electronics Co., Ltd 3 * 4 * SPDX-License-Identifier: GPL-2.0+ 5 */ 6 7 #include <asm/unaligned.h> 8 #include <config.h> 9 #include <common.h> 10 #include <errno.h> 11 #include <libfdt.h> 12 #include <fdtdec.h> 13 #include <fdt_support.h> 14 #include <linux/hdmi.h> 15 #include <linux/list.h> 16 #include <linux/compat.h> 17 #include <linux/media-bus-format.h> 18 #include <malloc.h> 19 #include <video.h> 20 #include <video_rockchip.h> 21 #include <dm/device.h> 22 #include <dm/uclass-internal.h> 23 #include <asm/arch-rockchip/resource_img.h> 24 25 #include "bmp_helper.h" 26 #include "rockchip_display.h" 27 #include "rockchip_crtc.h" 28 #include "rockchip_connector.h" 29 #include "rockchip_phy.h" 30 #include "rockchip_panel.h" 31 #include <dm.h> 32 #include <dm/of_access.h> 33 #include <dm/ofnode.h> 34 35 #define DRIVER_VERSION "v1.0.0" 36 37 /*********************************************************************** 38 * Rockchip UBOOT DRM driver version 39 * 40 * v1.0.0 : add basic version for rockchip drm driver(hjc) 41 * 42 **********************************************************************/ 43 44 #define RK_BLK_SIZE 512 45 46 DECLARE_GLOBAL_DATA_PTR; 47 static LIST_HEAD(rockchip_display_list); 48 static LIST_HEAD(logo_cache_list); 49 50 static unsigned long memory_start; 51 static unsigned long memory_end; 52 53 static void init_display_buffer(ulong base) 54 { 55 memory_start = base + DRM_ROCKCHIP_FB_SIZE; 56 memory_end = memory_start; 57 } 58 59 static void *get_display_buffer(int size) 60 { 61 unsigned long roundup_memory = roundup(memory_end, PAGE_SIZE); 62 void *buf; 63 64 if (roundup_memory + size > memory_start + MEMORY_POOL_SIZE) { 65 printf("failed to alloc %dbyte memory to display\n", size); 66 return NULL; 67 } 68 buf = (void *)roundup_memory; 69 70 memory_end = roundup_memory + size; 71 72 return buf; 73 } 74 75 static unsigned long get_display_size(void) 76 { 77 return memory_end - memory_start; 78 } 79 80 static bool can_direct_logo(int bpp) 81 { 82 return bpp == 24 || bpp == 32; 83 } 84 85 86 static struct udevice *get_panel_device(struct display_state *state, ofnode conn_node) 87 { 88 struct panel_state *panel_state = &state->panel_state; 89 struct udevice *dev; 90 struct connector_state *conn_state = &state->conn_state; 91 ofnode node, ports_node, port_node; 92 struct device_node *port, *panel, *ep; 93 int ph; 94 int ret; 95 96 node = dev_read_subnode(conn_state->dev, "panel"); 97 if (ofnode_valid(node) && 98 of_device_is_available(ofnode_to_np(node))) { 99 ret = uclass_get_device_by_ofnode(UCLASS_PANEL, node, &dev); 100 if (!ret) { 101 panel_state->node = node; 102 return dev; 103 } 104 } 105 106 /* TODO: this path not tested */ 107 ports_node = dev_read_subnode(conn_state->dev, "ports"); 108 if (!ofnode_valid(ports_node)) 109 return NULL; 110 111 ofnode_for_each_subnode(port_node, ports_node) { 112 ofnode_for_each_subnode(node, port_node) { 113 ph = ofnode_read_u32_default(node, "remote-endpoint", -1); 114 if (!ph) 115 continue; 116 ep = of_find_node_by_phandle(ph); 117 if (!ofnode_valid(np_to_ofnode(ep))) { 118 printf("Warn: can't find endpoint from phdl\n"); 119 continue; 120 } 121 port = of_get_parent(ep); 122 if (!ofnode_valid(np_to_ofnode(port))) { 123 printf("Warn: can't find port node\n"); 124 continue; 125 } 126 panel = of_get_parent(port); 127 if (!ofnode_valid(np_to_ofnode(panel))) { 128 printf("Warn: can't find panel node\n"); 129 continue; 130 } 131 ret = uclass_get_device_by_ofnode(UCLASS_PANEL, 132 np_to_ofnode(panel), 133 &dev); 134 if (!ret) { 135 panel_state->node = np_to_ofnode(panel); 136 return dev; 137 } 138 } 139 } 140 141 return NULL; 142 } 143 144 static int connector_phy_init(struct display_state *state) 145 { 146 struct connector_state *conn_state = &state->conn_state; 147 const struct rockchip_phy *phy; 148 struct udevice *dev; 149 int ret; 150 151 ret = uclass_get_device_by_phandle(UCLASS_PHY, conn_state->dev, "phys", 152 &dev); 153 if (ret) { 154 printf("Warn: can't find phy driver\n"); 155 return 0; 156 } 157 158 phy = (const struct rockchip_phy *)dev_get_driver_data(dev); 159 if (!phy) { 160 printf("failed to find phy driver\n"); 161 return 0; 162 } 163 164 conn_state->phy_dev = dev; 165 conn_state->phy_node = dev->node; 166 167 if (!phy->funcs || !phy->funcs->init || 168 phy->funcs->init(state)) { 169 printf("failed to init phy driver\n"); 170 return -EINVAL; 171 } 172 conn_state->phy = phy; 173 return 0; 174 } 175 176 static int connector_panel_init(struct display_state *state) 177 { 178 struct connector_state *conn_state = &state->conn_state; 179 struct panel_state *panel_state = &state->panel_state; 180 struct udevice *dev; 181 ofnode conn_node = conn_state->node; 182 const struct rockchip_panel *panel; 183 ofnode dsp_lut_node; 184 int ret, len; 185 186 dm_scan_fdt_dev(conn_state->dev); 187 188 dev = get_panel_device(state, conn_node); 189 if (!dev) { 190 return 0; 191 } 192 193 panel = (const struct rockchip_panel *)dev_get_driver_data(dev); 194 if (!panel) { 195 printf("failed to find panel driver\n"); 196 return 0; 197 } 198 199 panel_state->dev = dev; 200 panel_state->panel = panel; 201 202 if (panel->funcs && panel->funcs->init) { 203 ret = panel->funcs->init(state); 204 if (ret) { 205 printf("failed to init panel driver\n"); 206 return ret; 207 } 208 } 209 210 dsp_lut_node = dev_read_subnode(dev, "dsp-lut"); 211 if (!ofnode_valid(dsp_lut_node)) { 212 debug("%s can not find dsp-lut node\n", __func__); 213 return 0; 214 } 215 216 ofnode_get_property(dsp_lut_node, "gamma-lut", &len); 217 if (len > 0) { 218 conn_state->gamma.size = len / sizeof(u32); 219 conn_state->gamma.lut = malloc(len); 220 if (!conn_state->gamma.lut) { 221 printf("malloc gamma lut failed\n"); 222 return -ENOMEM; 223 } 224 ret = ofnode_read_u32_array(dsp_lut_node, "gamma-lut", 225 conn_state->gamma.lut, 226 conn_state->gamma.size); 227 if (ret) { 228 printf("Cannot decode gamma_lut\n"); 229 conn_state->gamma.lut = NULL; 230 return -EINVAL; 231 } 232 panel_state->dsp_lut_node = dsp_lut_node; 233 } 234 235 return 0; 236 } 237 238 int drm_mode_vrefresh(const struct drm_display_mode *mode) 239 { 240 int refresh = 0; 241 unsigned int calc_val; 242 243 if (mode->vrefresh > 0) { 244 refresh = mode->vrefresh; 245 } else if (mode->htotal > 0 && mode->vtotal > 0) { 246 int vtotal; 247 248 vtotal = mode->vtotal; 249 /* work out vrefresh the value will be x1000 */ 250 calc_val = (mode->clock * 1000); 251 calc_val /= mode->htotal; 252 refresh = (calc_val + vtotal / 2) / vtotal; 253 254 if (mode->flags & DRM_MODE_FLAG_INTERLACE) 255 refresh *= 2; 256 if (mode->flags & DRM_MODE_FLAG_DBLSCAN) 257 refresh /= 2; 258 if (mode->vscan > 1) 259 refresh /= mode->vscan; 260 } 261 return refresh; 262 } 263 264 static int display_get_timing_from_dts(struct panel_state *panel_state, 265 struct drm_display_mode *mode) 266 { 267 int phandle; 268 int hactive, vactive, pixelclock; 269 int hfront_porch, hback_porch, hsync_len; 270 int vfront_porch, vback_porch, vsync_len; 271 int val, flags = 0; 272 ofnode timing, native_mode; 273 274 timing = dev_read_subnode(panel_state->dev, "display-timings"); 275 if (!ofnode_valid(timing)) 276 return -ENODEV; 277 278 native_mode = ofnode_find_subnode(timing, "timing"); 279 if (!ofnode_valid(native_mode)) { 280 phandle = ofnode_read_u32_default(timing, "native-mode", -1); 281 native_mode = np_to_ofnode(of_find_node_by_phandle(phandle)); 282 if (!ofnode_valid(native_mode)) { 283 printf("failed to get display timings from DT\n"); 284 return -ENXIO; 285 } 286 } 287 288 #define FDT_GET_INT(val, name) \ 289 val = ofnode_read_s32_default(native_mode, name, -1); \ 290 if (val < 0) { \ 291 printf("Can't get %s\n", name); \ 292 return -ENXIO; \ 293 } 294 295 FDT_GET_INT(hactive, "hactive"); 296 FDT_GET_INT(vactive, "vactive"); 297 FDT_GET_INT(pixelclock, "clock-frequency"); 298 FDT_GET_INT(hsync_len, "hsync-len"); 299 FDT_GET_INT(hfront_porch, "hfront-porch"); 300 FDT_GET_INT(hback_porch, "hback-porch"); 301 FDT_GET_INT(vsync_len, "vsync-len"); 302 FDT_GET_INT(vfront_porch, "vfront-porch"); 303 FDT_GET_INT(vback_porch, "vback-porch"); 304 FDT_GET_INT(val, "hsync-active"); 305 flags |= val ? DRM_MODE_FLAG_PHSYNC : DRM_MODE_FLAG_NHSYNC; 306 FDT_GET_INT(val, "vsync-active"); 307 flags |= val ? DRM_MODE_FLAG_PVSYNC : DRM_MODE_FLAG_NVSYNC; 308 FDT_GET_INT(val, "pixelclk-active"); 309 flags |= val ? DRM_MODE_FLAG_PPIXDATA : 0; 310 311 mode->hdisplay = hactive; 312 mode->hsync_start = mode->hdisplay + hfront_porch; 313 mode->hsync_end = mode->hsync_start + hsync_len; 314 mode->htotal = mode->hsync_end + hback_porch; 315 316 mode->vdisplay = vactive; 317 mode->vsync_start = mode->vdisplay + vfront_porch; 318 mode->vsync_end = mode->vsync_start + vsync_len; 319 mode->vtotal = mode->vsync_end + vback_porch; 320 321 mode->clock = pixelclock / 1000; 322 mode->flags = flags; 323 324 return 0; 325 } 326 327 /** 328 * drm_mode_set_crtcinfo - set CRTC modesetting timing parameters 329 * @p: mode 330 * @adjust_flags: a combination of adjustment flags 331 * 332 * Setup the CRTC modesetting timing parameters for @p, adjusting if necessary. 333 * 334 * - The CRTC_INTERLACE_HALVE_V flag can be used to halve vertical timings of 335 * interlaced modes. 336 * - The CRTC_STEREO_DOUBLE flag can be used to compute the timings for 337 * buffers containing two eyes (only adjust the timings when needed, eg. for 338 * "frame packing" or "side by side full"). 339 * - The CRTC_NO_DBLSCAN and CRTC_NO_VSCAN flags request that adjustment *not* 340 * be performed for doublescan and vscan > 1 modes respectively. 341 */ 342 void drm_mode_set_crtcinfo(struct drm_display_mode *p, int adjust_flags) 343 { 344 if ((p == NULL) || ((p->type & DRM_MODE_TYPE_CRTC_C) == DRM_MODE_TYPE_BUILTIN)) 345 return; 346 347 if (p->flags & DRM_MODE_FLAG_DBLCLK) 348 p->crtc_clock = 2 * p->clock; 349 else 350 p->crtc_clock = p->clock; 351 p->crtc_hdisplay = p->hdisplay; 352 p->crtc_hsync_start = p->hsync_start; 353 p->crtc_hsync_end = p->hsync_end; 354 p->crtc_htotal = p->htotal; 355 p->crtc_hskew = p->hskew; 356 p->crtc_vdisplay = p->vdisplay; 357 p->crtc_vsync_start = p->vsync_start; 358 p->crtc_vsync_end = p->vsync_end; 359 p->crtc_vtotal = p->vtotal; 360 361 if (p->flags & DRM_MODE_FLAG_INTERLACE) { 362 if (adjust_flags & CRTC_INTERLACE_HALVE_V) { 363 p->crtc_vdisplay /= 2; 364 p->crtc_vsync_start /= 2; 365 p->crtc_vsync_end /= 2; 366 p->crtc_vtotal /= 2; 367 } 368 } 369 370 if (!(adjust_flags & CRTC_NO_DBLSCAN)) { 371 if (p->flags & DRM_MODE_FLAG_DBLSCAN) { 372 p->crtc_vdisplay *= 2; 373 p->crtc_vsync_start *= 2; 374 p->crtc_vsync_end *= 2; 375 p->crtc_vtotal *= 2; 376 } 377 } 378 379 if (!(adjust_flags & CRTC_NO_VSCAN)) { 380 if (p->vscan > 1) { 381 p->crtc_vdisplay *= p->vscan; 382 p->crtc_vsync_start *= p->vscan; 383 p->crtc_vsync_end *= p->vscan; 384 p->crtc_vtotal *= p->vscan; 385 } 386 } 387 388 if (adjust_flags & CRTC_STEREO_DOUBLE) { 389 unsigned int layout = p->flags & DRM_MODE_FLAG_3D_MASK; 390 391 switch (layout) { 392 case DRM_MODE_FLAG_3D_FRAME_PACKING: 393 p->crtc_clock *= 2; 394 p->crtc_vdisplay += p->crtc_vtotal; 395 p->crtc_vsync_start += p->crtc_vtotal; 396 p->crtc_vsync_end += p->crtc_vtotal; 397 p->crtc_vtotal += p->crtc_vtotal; 398 break; 399 } 400 } 401 402 p->crtc_vblank_start = min(p->crtc_vsync_start, p->crtc_vdisplay); 403 p->crtc_vblank_end = max(p->crtc_vsync_end, p->crtc_vtotal); 404 p->crtc_hblank_start = min(p->crtc_hsync_start, p->crtc_hdisplay); 405 p->crtc_hblank_end = max(p->crtc_hsync_end, p->crtc_htotal); 406 } 407 408 /** 409 * drm_mode_is_420_only - if a given videomode can be only supported in YCBCR420 410 * output format 411 * 412 * @connector: drm connector under action. 413 * @mode: video mode to be tested. 414 * 415 * Returns: 416 * true if the mode can be supported in YCBCR420 format 417 * false if not. 418 */ 419 bool drm_mode_is_420_only(const struct drm_display_info *display, 420 struct drm_display_mode *mode) 421 { 422 u8 vic = drm_match_cea_mode(mode); 423 424 return test_bit(vic, display->hdmi.y420_vdb_modes); 425 } 426 427 /** 428 * drm_mode_is_420_also - if a given videomode can be supported in YCBCR420 429 * output format also (along with RGB/YCBCR444/422) 430 * 431 * @display: display under action. 432 * @mode: video mode to be tested. 433 * 434 * Returns: 435 * true if the mode can be support YCBCR420 format 436 * false if not. 437 */ 438 bool drm_mode_is_420_also(const struct drm_display_info *display, 439 struct drm_display_mode *mode) 440 { 441 u8 vic = drm_match_cea_mode(mode); 442 443 return test_bit(vic, display->hdmi.y420_cmdb_modes); 444 } 445 446 /** 447 * drm_mode_is_420 - if a given videomode can be supported in YCBCR420 448 * output format 449 * 450 * @display: display under action. 451 * @mode: video mode to be tested. 452 * 453 * Returns: 454 * true if the mode can be supported in YCBCR420 format 455 * false if not. 456 */ 457 bool drm_mode_is_420(const struct drm_display_info *display, 458 struct drm_display_mode *mode) 459 { 460 return drm_mode_is_420_only(display, mode) || 461 drm_mode_is_420_also(display, mode); 462 } 463 464 static int display_get_timing(struct display_state *state) 465 { 466 struct connector_state *conn_state = &state->conn_state; 467 const struct rockchip_connector *conn = conn_state->connector; 468 const struct rockchip_connector_funcs *conn_funcs = conn->funcs; 469 struct drm_display_mode *mode = &conn_state->mode; 470 const struct drm_display_mode *m; 471 struct panel_state *panel_state = &state->panel_state; 472 const struct rockchip_panel *panel = panel_state->panel; 473 const struct rockchip_panel_funcs *panel_funcs = panel->funcs; 474 ofnode panel_node = panel_state->node; 475 int ret; 476 477 if (ofnode_valid(panel_node) && !display_get_timing_from_dts(panel_state, mode)) { 478 printf("Using display timing dts\n"); 479 goto done; 480 } 481 482 if (panel->data) { 483 m = (const struct drm_display_mode *)panel->data; 484 memcpy(mode, m, sizeof(*m)); 485 printf("Using display timing from compatible panel driver\n"); 486 goto done; 487 } 488 489 if (conn_funcs->get_edid && !conn_funcs->get_edid(state)) { 490 int panel_bits_per_colourp; 491 492 /* In order to read EDID, the panel needs to be powered on */ 493 if (panel_funcs->prepare) { 494 ret = panel_funcs->prepare(state); 495 if (ret) { 496 printf("failed to prepare panel\n"); 497 return ret; 498 } 499 } 500 501 if (!edid_get_drm_mode((void *)&conn_state->edid, 502 sizeof(conn_state->edid), mode, 503 &panel_bits_per_colourp)) { 504 printf("Using display timing from edid\n"); 505 edid_print_info((void *)&conn_state->edid); 506 goto done; 507 } else { 508 if (panel_funcs->unprepare) 509 panel_funcs->unprepare(state); 510 } 511 } 512 513 printf("failed to find display timing\n"); 514 return -ENODEV; 515 done: 516 printf("Detailed mode clock %u kHz, flags[%x]\n" 517 " H: %04d %04d %04d %04d\n" 518 " V: %04d %04d %04d %04d\n" 519 "bus_format: %x\n", 520 mode->clock, mode->flags, 521 mode->hdisplay, mode->hsync_start, 522 mode->hsync_end, mode->htotal, 523 mode->vdisplay, mode->vsync_start, 524 mode->vsync_end, mode->vtotal, 525 conn_state->bus_format); 526 527 return 0; 528 } 529 530 static int display_init(struct display_state *state) 531 { 532 struct connector_state *conn_state = &state->conn_state; 533 const struct rockchip_connector *conn = conn_state->connector; 534 const struct rockchip_connector_funcs *conn_funcs = conn->funcs; 535 struct crtc_state *crtc_state = &state->crtc_state; 536 const struct rockchip_crtc *crtc = crtc_state->crtc; 537 const struct rockchip_crtc_funcs *crtc_funcs = crtc->funcs; 538 struct drm_display_mode *mode = &conn_state->mode; 539 int ret = 0; 540 static bool __print_once = false; 541 542 if (!__print_once) { 543 __print_once = true; 544 printf("Rockchip UBOOT DRM driver version: %s\n", DRIVER_VERSION); 545 } 546 547 if (state->is_init) 548 return 0; 549 550 if (!conn_funcs || !crtc_funcs) { 551 printf("failed to find connector or crtc functions\n"); 552 return -ENXIO; 553 } 554 555 if (conn_funcs->init) { 556 ret = conn_funcs->init(state); 557 if (ret) 558 goto deinit; 559 } 560 /* 561 * support hotplug, but not connect; 562 */ 563 if (conn_funcs->detect) { 564 ret = conn_funcs->detect(state); 565 if (!ret) 566 goto deinit; 567 } 568 569 if (conn_funcs->get_timing) { 570 ret = conn_funcs->get_timing(state); 571 if (ret) 572 goto deinit; 573 } else { 574 ret = display_get_timing(state); 575 if (ret) 576 goto deinit; 577 } 578 drm_mode_set_crtcinfo(mode, CRTC_INTERLACE_HALVE_V); 579 580 if (crtc_funcs->init) { 581 ret = crtc_funcs->init(state); 582 if (ret) 583 goto deinit; 584 } 585 state->is_init = 1; 586 587 return 0; 588 589 deinit: 590 if (conn_funcs->deinit) 591 conn_funcs->deinit(state); 592 return ret; 593 } 594 595 int display_send_mcu_cmd(struct display_state *state, u32 type, u32 val) 596 { 597 struct crtc_state *crtc_state = &state->crtc_state; 598 const struct rockchip_crtc *crtc = crtc_state->crtc; 599 const struct rockchip_crtc_funcs *crtc_funcs = crtc->funcs; 600 int ret; 601 602 if (!state->is_init) 603 return -EINVAL; 604 605 if (crtc_funcs->send_mcu_cmd) { 606 ret = crtc_funcs->send_mcu_cmd(state, type, val); 607 if (ret) 608 return ret; 609 } 610 611 return 0; 612 } 613 614 static int display_set_plane(struct display_state *state) 615 { 616 struct crtc_state *crtc_state = &state->crtc_state; 617 const struct rockchip_crtc *crtc = crtc_state->crtc; 618 const struct rockchip_crtc_funcs *crtc_funcs = crtc->funcs; 619 int ret; 620 621 if (!state->is_init) 622 return -EINVAL; 623 624 if (crtc_funcs->set_plane) { 625 ret = crtc_funcs->set_plane(state); 626 if (ret) 627 return ret; 628 } 629 630 return 0; 631 } 632 633 static int display_panel_prepare(struct display_state *state) 634 { 635 struct panel_state *panel_state = &state->panel_state; 636 const struct rockchip_panel *panel = panel_state->panel; 637 638 if (!panel || !panel->funcs || !panel->funcs->prepare) { 639 printf("%s: failed to find panel prepare funcs\n", __func__); 640 return -ENODEV; 641 } 642 643 return panel->funcs->prepare(state); 644 } 645 646 static int display_panel_enable(struct display_state *state) 647 { 648 struct panel_state *panel_state = &state->panel_state; 649 const struct rockchip_panel *panel = panel_state->panel; 650 651 if (!panel || !panel->funcs || !panel->funcs->enable) { 652 printf("%s: failed to find panel enable funcs\n", __func__); 653 return -ENODEV; 654 } 655 656 return panel->funcs->enable(state); 657 } 658 659 static void display_panel_unprepare(struct display_state *state) 660 { 661 struct panel_state *panel_state = &state->panel_state; 662 const struct rockchip_panel *panel = panel_state->panel; 663 664 if (!panel || !panel->funcs || !panel->funcs->unprepare) { 665 printf("%s: failed to find panel unprepare funcs\n", __func__); 666 return; 667 } 668 669 panel->funcs->unprepare(state); 670 } 671 672 static void display_panel_disable(struct display_state *state) 673 { 674 struct panel_state *panel_state = &state->panel_state; 675 const struct rockchip_panel *panel = panel_state->panel; 676 677 if (!panel || !panel->funcs || !panel->funcs->disable) { 678 printf("%s: failed to find panel disable funcs\n", __func__); 679 return; 680 } 681 682 panel->funcs->disable(state); 683 } 684 685 static int display_enable(struct display_state *state) 686 { 687 struct connector_state *conn_state = &state->conn_state; 688 const struct rockchip_connector *conn = conn_state->connector; 689 const struct rockchip_connector_funcs *conn_funcs = conn->funcs; 690 struct crtc_state *crtc_state = &state->crtc_state; 691 const struct rockchip_crtc *crtc = crtc_state->crtc; 692 const struct rockchip_crtc_funcs *crtc_funcs = crtc->funcs; 693 int ret = 0; 694 695 display_init(state); 696 697 if (!state->is_init) 698 return -EINVAL; 699 700 if (state->is_enable) 701 return 0; 702 703 if (crtc_funcs->prepare) { 704 ret = crtc_funcs->prepare(state); 705 if (ret) 706 return ret; 707 } 708 709 if (conn_funcs->prepare) { 710 ret = conn_funcs->prepare(state); 711 if (ret) 712 goto unprepare_crtc; 713 } 714 715 display_panel_prepare(state); 716 717 if (crtc_funcs->enable) { 718 ret = crtc_funcs->enable(state); 719 if (ret) 720 goto unprepare_conn; 721 } 722 723 if (conn_funcs->enable) { 724 ret = conn_funcs->enable(state); 725 if (ret) 726 goto disable_crtc; 727 } 728 729 display_panel_enable(state); 730 731 state->is_enable = true; 732 return 0; 733 734 disable_crtc: 735 if (crtc_funcs->disable) 736 crtc_funcs->disable(state); 737 unprepare_conn: 738 if (conn_funcs->unprepare) 739 conn_funcs->unprepare(state); 740 unprepare_crtc: 741 if (crtc_funcs->unprepare) 742 crtc_funcs->unprepare(state); 743 return ret; 744 } 745 746 static int display_disable(struct display_state *state) 747 { 748 struct connector_state *conn_state = &state->conn_state; 749 const struct rockchip_connector *conn = conn_state->connector; 750 const struct rockchip_connector_funcs *conn_funcs = conn->funcs; 751 struct crtc_state *crtc_state = &state->crtc_state; 752 const struct rockchip_crtc *crtc = crtc_state->crtc; 753 const struct rockchip_crtc_funcs *crtc_funcs = crtc->funcs; 754 755 if (!state->is_init) 756 return 0; 757 758 if (!state->is_enable) 759 return 0; 760 761 display_panel_disable(state); 762 763 if (crtc_funcs->disable) 764 crtc_funcs->disable(state); 765 766 if (conn_funcs->disable) 767 conn_funcs->disable(state); 768 769 display_panel_unprepare(state); 770 771 if (conn_funcs->unprepare) 772 conn_funcs->unprepare(state); 773 774 state->is_enable = 0; 775 state->is_init = 0; 776 777 return 0; 778 } 779 780 static int display_logo(struct display_state *state) 781 { 782 struct crtc_state *crtc_state = &state->crtc_state; 783 struct connector_state *conn_state = &state->conn_state; 784 struct logo_info *logo = &state->logo; 785 int hdisplay, vdisplay; 786 787 display_init(state); 788 if (!state->is_init) 789 return -ENODEV; 790 791 switch (logo->bpp) { 792 case 16: 793 crtc_state->format = ROCKCHIP_FMT_RGB565; 794 break; 795 case 24: 796 crtc_state->format = ROCKCHIP_FMT_RGB888; 797 break; 798 case 32: 799 crtc_state->format = ROCKCHIP_FMT_ARGB8888; 800 break; 801 default: 802 printf("can't support bmp bits[%d]\n", logo->bpp); 803 return -EINVAL; 804 } 805 crtc_state->rb_swap = logo->bpp != 32; 806 hdisplay = conn_state->mode.hdisplay; 807 vdisplay = conn_state->mode.vdisplay; 808 crtc_state->src_w = logo->width; 809 crtc_state->src_h = logo->height; 810 crtc_state->src_x = 0; 811 crtc_state->src_y = 0; 812 crtc_state->ymirror = logo->ymirror; 813 814 crtc_state->dma_addr = (u32)(unsigned long)logo->mem + logo->offset; 815 crtc_state->xvir = ALIGN(crtc_state->src_w * logo->bpp, 32) >> 5; 816 817 if (logo->mode == ROCKCHIP_DISPLAY_FULLSCREEN) { 818 crtc_state->crtc_x = 0; 819 crtc_state->crtc_y = 0; 820 crtc_state->crtc_w = hdisplay; 821 crtc_state->crtc_h = vdisplay; 822 } else { 823 if (crtc_state->src_w >= hdisplay) { 824 crtc_state->crtc_x = 0; 825 crtc_state->crtc_w = hdisplay; 826 } else { 827 crtc_state->crtc_x = (hdisplay - crtc_state->src_w) / 2; 828 crtc_state->crtc_w = crtc_state->src_w; 829 } 830 831 if (crtc_state->src_h >= vdisplay) { 832 crtc_state->crtc_y = 0; 833 crtc_state->crtc_h = vdisplay; 834 } else { 835 crtc_state->crtc_y = (vdisplay - crtc_state->src_h) / 2; 836 crtc_state->crtc_h = crtc_state->src_h; 837 } 838 } 839 840 display_set_plane(state); 841 display_enable(state); 842 843 return 0; 844 } 845 846 static int get_crtc_id(ofnode connect) 847 { 848 int phandle; 849 struct device_node *remote; 850 int val; 851 852 phandle = ofnode_read_u32_default(connect, "remote-endpoint", -1); 853 if (phandle < 0) 854 goto err; 855 remote = of_find_node_by_phandle(phandle); 856 val = ofnode_read_u32_default(np_to_ofnode(remote), "reg", -1); 857 if (val < 0) 858 goto err; 859 860 return val; 861 err: 862 printf("Can't get crtc id, default set to id = 0\n"); 863 return 0; 864 } 865 866 static int get_crtc_mcu_mode(struct crtc_state *crtc_state) 867 { 868 ofnode mcu_node; 869 int total_pixel, cs_pst, cs_pend, rw_pst, rw_pend; 870 871 mcu_node = dev_read_subnode(crtc_state->dev, "mcu-timing"); 872 873 #define FDT_GET_MCU_INT(val, name) \ 874 do { \ 875 val = ofnode_read_s32_default(mcu_node, name, -1); \ 876 if (val < 0) { \ 877 printf("Can't get %s\n", name); \ 878 return -ENXIO; \ 879 } \ 880 } while (0) 881 882 FDT_GET_MCU_INT(total_pixel, "mcu-pix-total"); 883 FDT_GET_MCU_INT(cs_pst, "mcu-cs-pst"); 884 FDT_GET_MCU_INT(cs_pend, "mcu-cs-pend"); 885 FDT_GET_MCU_INT(rw_pst, "mcu-rw-pst"); 886 FDT_GET_MCU_INT(rw_pend, "mcu-rw-pend"); 887 888 crtc_state->mcu_timing.mcu_pix_total = total_pixel; 889 crtc_state->mcu_timing.mcu_cs_pst = cs_pst; 890 crtc_state->mcu_timing.mcu_cs_pend = cs_pend; 891 crtc_state->mcu_timing.mcu_rw_pst = rw_pst; 892 crtc_state->mcu_timing.mcu_rw_pend = rw_pend; 893 894 return 0; 895 } 896 897 struct rockchip_logo_cache *find_or_alloc_logo_cache(const char *bmp) 898 { 899 struct rockchip_logo_cache *tmp, *logo_cache = NULL; 900 901 list_for_each_entry(tmp, &logo_cache_list, head) { 902 if (!strcmp(tmp->name, bmp)) { 903 logo_cache = tmp; 904 break; 905 } 906 } 907 908 if (!logo_cache) { 909 logo_cache = malloc(sizeof(*logo_cache)); 910 if (!logo_cache) { 911 printf("failed to alloc memory for logo cache\n"); 912 return NULL; 913 } 914 memset(logo_cache, 0, sizeof(*logo_cache)); 915 strcpy(logo_cache->name, bmp); 916 INIT_LIST_HEAD(&logo_cache->head); 917 list_add_tail(&logo_cache->head, &logo_cache_list); 918 } 919 920 return logo_cache; 921 } 922 923 /* Note: used only for rkfb kernel driver */ 924 static int load_kernel_bmp_logo(struct logo_info *logo, const char *bmp_name) 925 { 926 #ifdef CONFIG_ROCKCHIP_RESOURCE_IMAGE 927 void *dst = NULL; 928 int len, size; 929 struct bmp_header *header; 930 931 if (!logo || !bmp_name) 932 return -EINVAL; 933 934 header = malloc(RK_BLK_SIZE); 935 if (!header) 936 return -ENOMEM; 937 938 len = rockchip_read_resource_file(header, bmp_name, 0, RK_BLK_SIZE); 939 if (len != RK_BLK_SIZE) { 940 free(header); 941 return -EINVAL; 942 } 943 size = get_unaligned_le32(&header->file_size); 944 dst = (void *)(memory_start + MEMORY_POOL_SIZE / 2); 945 len = rockchip_read_resource_file(dst, bmp_name, 0, size); 946 if (len != size) { 947 printf("failed to load bmp %s\n", bmp_name); 948 free(header); 949 return -ENOENT; 950 } 951 952 logo->mem = dst; 953 954 return 0; 955 #endif 956 } 957 958 static int load_bmp_logo(struct logo_info *logo, const char *bmp_name) 959 { 960 #ifdef CONFIG_ROCKCHIP_RESOURCE_IMAGE 961 struct rockchip_logo_cache *logo_cache; 962 struct bmp_header *header; 963 void *dst = NULL, *pdst; 964 int size, len; 965 int ret = 0; 966 967 if (!logo || !bmp_name) 968 return -EINVAL; 969 logo_cache = find_or_alloc_logo_cache(bmp_name); 970 if (!logo_cache) 971 return -ENOMEM; 972 973 if (logo_cache->logo.mem) { 974 memcpy(logo, &logo_cache->logo, sizeof(*logo)); 975 return 0; 976 } 977 978 header = malloc(RK_BLK_SIZE); 979 if (!header) 980 return -ENOMEM; 981 982 len = rockchip_read_resource_file(header, bmp_name, 0, RK_BLK_SIZE); 983 if (len != RK_BLK_SIZE) { 984 ret = -EINVAL; 985 goto free_header; 986 } 987 988 logo->bpp = get_unaligned_le16(&header->bit_count); 989 logo->width = get_unaligned_le32(&header->width); 990 logo->height = get_unaligned_le32(&header->height); 991 size = get_unaligned_le32(&header->file_size); 992 if (!can_direct_logo(logo->bpp)) { 993 if (size > MEMORY_POOL_SIZE) { 994 printf("failed to use boot buf as temp bmp buffer\n"); 995 ret = -ENOMEM; 996 goto free_header; 997 } 998 pdst = get_display_buffer(size); 999 1000 } else { 1001 pdst = get_display_buffer(size); 1002 dst = pdst; 1003 } 1004 1005 len = rockchip_read_resource_file(pdst, bmp_name, 0, size); 1006 if (len != size) { 1007 printf("failed to load bmp %s\n", bmp_name); 1008 ret = -ENOENT; 1009 goto free_header; 1010 } 1011 1012 if (!can_direct_logo(logo->bpp)) { 1013 int dst_size; 1014 /* 1015 * TODO: force use 16bpp if bpp less than 16; 1016 */ 1017 logo->bpp = (logo->bpp <= 16) ? 16 : logo->bpp; 1018 dst_size = logo->width * logo->height * logo->bpp >> 3; 1019 1020 dst = get_display_buffer(dst_size); 1021 if (!dst) { 1022 ret = -ENOMEM; 1023 goto free_header; 1024 } 1025 if (bmpdecoder(pdst, dst, logo->bpp)) { 1026 printf("failed to decode bmp %s\n", bmp_name); 1027 ret = -EINVAL; 1028 goto free_header; 1029 } 1030 flush_dcache_range((ulong)dst, 1031 ALIGN((ulong)dst + dst_size, 1032 CONFIG_SYS_CACHELINE_SIZE)); 1033 1034 logo->offset = 0; 1035 logo->ymirror = 0; 1036 } else { 1037 logo->offset = get_unaligned_le32(&header->data_offset); 1038 logo->ymirror = 1; 1039 } 1040 logo->mem = dst; 1041 1042 memcpy(&logo_cache->logo, logo, sizeof(*logo)); 1043 1044 free_header: 1045 1046 free(header); 1047 1048 return ret; 1049 #else 1050 return -EINVAL; 1051 #endif 1052 } 1053 1054 void rockchip_show_fbbase(ulong fbbase) 1055 { 1056 struct display_state *s; 1057 1058 list_for_each_entry(s, &rockchip_display_list, head) { 1059 s->logo.mode = ROCKCHIP_DISPLAY_FULLSCREEN; 1060 s->logo.mem = (char *)fbbase; 1061 s->logo.width = DRM_ROCKCHIP_FB_WIDTH; 1062 s->logo.height = DRM_ROCKCHIP_FB_HEIGHT; 1063 s->logo.bpp = 32; 1064 s->logo.ymirror = 0; 1065 1066 display_logo(s); 1067 } 1068 } 1069 1070 void rockchip_show_bmp(const char *bmp) 1071 { 1072 struct display_state *s; 1073 1074 if (!bmp) { 1075 list_for_each_entry(s, &rockchip_display_list, head) 1076 display_disable(s); 1077 return; 1078 } 1079 1080 list_for_each_entry(s, &rockchip_display_list, head) { 1081 s->logo.mode = s->charge_logo_mode; 1082 if (load_bmp_logo(&s->logo, bmp)) 1083 continue; 1084 display_logo(s); 1085 } 1086 } 1087 1088 void rockchip_show_logo(void) 1089 { 1090 struct display_state *s; 1091 1092 list_for_each_entry(s, &rockchip_display_list, head) { 1093 s->logo.mode = s->logo_mode; 1094 if (load_bmp_logo(&s->logo, s->ulogo_name)) 1095 printf("failed to display uboot logo\n"); 1096 else 1097 display_logo(s); 1098 1099 /* Load kernel bmp in rockchip_display_fixup() later */ 1100 } 1101 } 1102 1103 static int rockchip_display_probe(struct udevice *dev) 1104 { 1105 struct video_priv *uc_priv = dev_get_uclass_priv(dev); 1106 struct video_uc_platdata *plat = dev_get_uclass_platdata(dev); 1107 const void *blob = gd->fdt_blob; 1108 int phandle; 1109 struct udevice *crtc_dev, *conn_dev; 1110 const struct rockchip_crtc *crtc; 1111 const struct rockchip_connector *conn; 1112 struct display_state *s; 1113 const char *name; 1114 int ret; 1115 ofnode node, route_node; 1116 struct device_node *port_node, *vop_node, *ep_node; 1117 struct device_node *cnt_node, *p; 1118 1119 /* Before relocation we don't need to do anything */ 1120 if (!(gd->flags & GD_FLG_RELOC)) 1121 return 0; 1122 init_display_buffer(plat->base); 1123 1124 route_node = dev_read_subnode(dev, "route"); 1125 if (!ofnode_valid(route_node)) 1126 return -ENODEV; 1127 1128 ofnode_for_each_subnode(node, route_node) { 1129 if (!ofnode_is_available(node)) 1130 continue; 1131 phandle = ofnode_read_u32_default(node, "connect", -1); 1132 if (phandle < 0) { 1133 printf("Warn: can't find connect node's handle\n"); 1134 continue; 1135 } 1136 ep_node = of_find_node_by_phandle(phandle); 1137 if (!ofnode_valid(np_to_ofnode(ep_node))) { 1138 printf("Warn: can't find endpoint node from phandle\n"); 1139 continue; 1140 } 1141 port_node = of_get_parent(ep_node); 1142 if (!ofnode_valid(np_to_ofnode(port_node))) { 1143 printf("Warn: can't find port node from phandle\n"); 1144 continue; 1145 } 1146 vop_node = of_get_parent(port_node); 1147 if (!ofnode_valid(np_to_ofnode(vop_node))) { 1148 printf("Warn: can't find crtc node from phandle\n"); 1149 continue; 1150 } 1151 ret = uclass_get_device_by_ofnode(UCLASS_VIDEO_CRTC, 1152 np_to_ofnode(vop_node), 1153 &crtc_dev); 1154 if (ret) { 1155 printf("Warn: can't find crtc driver %d\n", ret); 1156 continue; 1157 } 1158 crtc = (const struct rockchip_crtc *)dev_get_driver_data(crtc_dev); 1159 1160 phandle = ofnode_read_u32_default(np_to_ofnode(ep_node), 1161 "remote-endpoint", -1); 1162 cnt_node = of_find_node_by_phandle(phandle); 1163 if (phandle < 0) { 1164 printf("Warn: can't find remote-endpoint's handle\n"); 1165 continue; 1166 } 1167 while (cnt_node->parent){ 1168 p = of_get_parent(cnt_node); 1169 if (!strcmp(p->full_name, "/")) 1170 break; 1171 cnt_node = p; 1172 } 1173 if (!of_device_is_available(cnt_node)) 1174 continue; 1175 ret = uclass_get_device_by_ofnode(UCLASS_DISPLAY, 1176 np_to_ofnode(cnt_node), 1177 &conn_dev); 1178 if (ret) { 1179 printf("Warn: can't find connect driver\n"); 1180 continue; 1181 } 1182 conn = (const struct rockchip_connector *)dev_get_driver_data(conn_dev); 1183 1184 s = malloc(sizeof(*s)); 1185 if (!s) 1186 continue; 1187 1188 memset(s, 0, sizeof(*s)); 1189 1190 INIT_LIST_HEAD(&s->head); 1191 ret = ofnode_read_string_index(node, "logo,uboot", 0, &s->ulogo_name); 1192 ret = ofnode_read_string_index(node, "logo,kernel", 0, &s->klogo_name); 1193 ret = ofnode_read_string_index(node, "logo,mode", 0, &name); 1194 if (!strcmp(name, "fullscreen")) 1195 s->logo_mode = ROCKCHIP_DISPLAY_FULLSCREEN; 1196 else 1197 s->logo_mode = ROCKCHIP_DISPLAY_CENTER; 1198 ret = ofnode_read_string_index(node, "charge_logo,mode", 0, &name); 1199 if (!strcmp(name, "fullscreen")) 1200 s->charge_logo_mode = ROCKCHIP_DISPLAY_FULLSCREEN; 1201 else 1202 s->charge_logo_mode = ROCKCHIP_DISPLAY_CENTER; 1203 1204 s->blob = blob; 1205 s->conn_state.node = np_to_ofnode(cnt_node); 1206 s->conn_state.dev = conn_dev; 1207 s->conn_state.connector = conn; 1208 s->conn_state.overscan.left_margin = 100; 1209 s->conn_state.overscan.right_margin = 100; 1210 s->conn_state.overscan.top_margin = 100; 1211 s->conn_state.overscan.bottom_margin = 100; 1212 s->crtc_state.node = np_to_ofnode(vop_node); 1213 s->crtc_state.dev = crtc_dev; 1214 s->crtc_state.crtc = crtc; 1215 s->crtc_state.crtc_id = get_crtc_id(np_to_ofnode(ep_node)); 1216 s->node = node; 1217 get_crtc_mcu_mode(&s->crtc_state); 1218 1219 if (connector_panel_init(s)) { 1220 printf("Warn: Failed to init panel drivers\n"); 1221 free(s); 1222 continue; 1223 } 1224 1225 if (connector_phy_init(s)) { 1226 printf("Warn: Failed to init phy drivers\n"); 1227 free(s); 1228 continue; 1229 } 1230 list_add_tail(&s->head, &rockchip_display_list); 1231 } 1232 1233 if (list_empty(&rockchip_display_list)) { 1234 printf("Failed to found available display route\n"); 1235 return -ENODEV; 1236 } 1237 1238 uc_priv->xsize = DRM_ROCKCHIP_FB_WIDTH; 1239 uc_priv->ysize = DRM_ROCKCHIP_FB_HEIGHT; 1240 uc_priv->bpix = VIDEO_BPP32; 1241 1242 #ifdef CONFIG_DRM_ROCKCHIP_VIDEO_FRAMEBUFFER 1243 rockchip_show_fbbase(plat->base); 1244 video_set_flush_dcache(dev, true); 1245 #endif 1246 1247 return 0; 1248 } 1249 1250 void rockchip_display_fixup(void *blob) 1251 { 1252 const struct rockchip_connector_funcs *conn_funcs; 1253 const struct rockchip_crtc_funcs *crtc_funcs; 1254 const struct rockchip_connector *conn; 1255 const struct rockchip_crtc *crtc; 1256 struct display_state *s; 1257 int offset; 1258 const struct device_node *np; 1259 const char *path; 1260 1261 if (!get_display_size()) 1262 return; 1263 1264 if (fdt_node_offset_by_compatible(blob, 0, "rockchip,drm-logo") >= 0) { 1265 list_for_each_entry(s, &rockchip_display_list, head) 1266 load_bmp_logo(&s->logo, s->klogo_name); 1267 offset = fdt_update_reserved_memory(blob, "rockchip,drm-logo", 1268 (u64)memory_start, 1269 (u64)get_display_size()); 1270 if (offset < 0) 1271 printf("failed to reserve drm-loader-logo memory\n"); 1272 } else { 1273 printf("can't found rockchip,drm-logo, use rockchip,fb-logo\n"); 1274 /* Compatible with rkfb display, only need reserve memory */ 1275 offset = fdt_update_reserved_memory(blob, "rockchip,fb-logo", 1276 (u64)memory_start, 1277 MEMORY_POOL_SIZE); 1278 if (offset < 0) 1279 printf("failed to reserve fb-loader-logo memory\n"); 1280 else 1281 list_for_each_entry(s, &rockchip_display_list, head) 1282 load_kernel_bmp_logo(&s->logo, s->klogo_name); 1283 return; 1284 } 1285 1286 list_for_each_entry(s, &rockchip_display_list, head) { 1287 conn = s->conn_state.connector; 1288 if (!conn) 1289 continue; 1290 conn_funcs = conn->funcs; 1291 if (!conn_funcs) { 1292 printf("failed to get exist connector\n"); 1293 continue; 1294 } 1295 1296 crtc = s->crtc_state.crtc; 1297 if (!crtc) 1298 continue; 1299 1300 crtc_funcs = crtc->funcs; 1301 if (!crtc_funcs) { 1302 printf("failed to get exist crtc\n"); 1303 continue; 1304 } 1305 1306 if (crtc_funcs->fixup_dts) 1307 crtc_funcs->fixup_dts(s, blob); 1308 1309 if (conn_funcs->fixup_dts) 1310 conn_funcs->fixup_dts(s, blob); 1311 1312 np = ofnode_to_np(s->node); 1313 path = np->full_name; 1314 fdt_increase_size(blob, 0x400); 1315 #define FDT_SET_U32(name, val) \ 1316 do_fixup_by_path_u32(blob, path, name, val, 1); 1317 1318 offset = s->logo.offset + (u32)(unsigned long)s->logo.mem 1319 - memory_start; 1320 FDT_SET_U32("logo,offset", offset); 1321 FDT_SET_U32("logo,width", s->logo.width); 1322 FDT_SET_U32("logo,height", s->logo.height); 1323 FDT_SET_U32("logo,bpp", s->logo.bpp); 1324 FDT_SET_U32("logo,ymirror", s->logo.ymirror); 1325 FDT_SET_U32("video,hdisplay", s->conn_state.mode.hdisplay); 1326 FDT_SET_U32("video,vdisplay", s->conn_state.mode.vdisplay); 1327 FDT_SET_U32("video,crtc_hsync_end", s->conn_state.mode.crtc_hsync_end); 1328 FDT_SET_U32("video,crtc_vsync_end", s->conn_state.mode.crtc_vsync_end); 1329 FDT_SET_U32("video,vrefresh", 1330 drm_mode_vrefresh(&s->conn_state.mode)); 1331 FDT_SET_U32("video,flags", s->conn_state.mode.flags); 1332 FDT_SET_U32("overscan,left_margin", s->conn_state.overscan.left_margin); 1333 FDT_SET_U32("overscan,right_margin", s->conn_state.overscan.right_margin); 1334 FDT_SET_U32("overscan,top_margin", s->conn_state.overscan.top_margin); 1335 FDT_SET_U32("overscan,bottom_margin", s->conn_state.overscan.bottom_margin); 1336 #undef FDT_SET_U32 1337 } 1338 } 1339 1340 int rockchip_display_bind(struct udevice *dev) 1341 { 1342 struct video_uc_platdata *plat = dev_get_uclass_platdata(dev); 1343 1344 plat->size = DRM_ROCKCHIP_FB_SIZE + MEMORY_POOL_SIZE; 1345 1346 return 0; 1347 } 1348 1349 static const struct udevice_id rockchip_display_ids[] = { 1350 { .compatible = "rockchip,display-subsystem" }, 1351 { } 1352 }; 1353 1354 U_BOOT_DRIVER(rockchip_display) = { 1355 .name = "rockchip_display", 1356 .id = UCLASS_VIDEO, 1357 .of_match = rockchip_display_ids, 1358 .bind = rockchip_display_bind, 1359 .probe = rockchip_display_probe, 1360 }; 1361 1362 static int do_rockchip_logo_show(cmd_tbl_t *cmdtp, int flag, int argc, 1363 char *const argv[]) 1364 { 1365 if (argc != 1) 1366 return CMD_RET_USAGE; 1367 1368 rockchip_show_logo(); 1369 1370 return 0; 1371 } 1372 1373 static int do_rockchip_show_bmp(cmd_tbl_t *cmdtp, int flag, int argc, 1374 char *const argv[]) 1375 { 1376 if (argc != 2) 1377 return CMD_RET_USAGE; 1378 1379 rockchip_show_bmp(argv[1]); 1380 1381 return 0; 1382 } 1383 1384 U_BOOT_CMD( 1385 rockchip_show_logo, 1, 1, do_rockchip_logo_show, 1386 "load and display log from resource partition", 1387 NULL 1388 ); 1389 1390 U_BOOT_CMD( 1391 rockchip_show_bmp, 2, 1, do_rockchip_show_bmp, 1392 "load and display bmp from resource partition", 1393 " <bmp_name>" 1394 ); 1395