1186f8572SMark Yao /* 2186f8572SMark Yao * (C) Copyright 2008-2017 Fuzhou Rockchip Electronics Co., Ltd 3186f8572SMark Yao * 4186f8572SMark Yao * SPDX-License-Identifier: GPL-2.0+ 5186f8572SMark Yao */ 6186f8572SMark Yao 7186f8572SMark Yao #include <asm/unaligned.h> 8186f8572SMark Yao #include <config.h> 9186f8572SMark Yao #include <common.h> 10186f8572SMark Yao #include <errno.h> 11186f8572SMark Yao #include <libfdt.h> 12186f8572SMark Yao #include <fdtdec.h> 13186f8572SMark Yao #include <fdt_support.h> 14*8e2bab3fSAlgea Cao #include <linux/hdmi.h> 15186f8572SMark Yao #include <linux/list.h> 16186f8572SMark Yao #include <linux/compat.h> 17186f8572SMark Yao #include <linux/media-bus-format.h> 18186f8572SMark Yao #include <malloc.h> 19186f8572SMark Yao #include <video.h> 20f8a3e587SJoseph Chen #include <video_rockchip.h> 21186f8572SMark Yao #include <dm/device.h> 22186f8572SMark Yao #include <dm/uclass-internal.h> 234b8c2ef1SMark Yao #include <asm/arch-rockchip/resource_img.h> 24186f8572SMark Yao 25186f8572SMark Yao #include "bmp_helper.h" 26186f8572SMark Yao #include "rockchip_display.h" 27186f8572SMark Yao #include "rockchip_crtc.h" 28186f8572SMark Yao #include "rockchip_connector.h" 29186f8572SMark Yao #include "rockchip_phy.h" 30186f8572SMark Yao #include "rockchip_panel.h" 31e2bce6e4SKever Yang #include <dm.h> 32e2bce6e4SKever Yang #include <dm/of_access.h> 33e2bce6e4SKever Yang #include <dm/ofnode.h> 34186f8572SMark Yao 35e559407dSSandy Huang #define DRIVER_VERSION "v1.0.0" 36e559407dSSandy Huang 37e559407dSSandy Huang /*********************************************************************** 38e559407dSSandy Huang * Rockchip UBOOT DRM driver version 39e559407dSSandy Huang * 40e559407dSSandy Huang * v1.0.0 : add basic version for rockchip drm driver(hjc) 41e559407dSSandy Huang * 42e559407dSSandy Huang **********************************************************************/ 43e559407dSSandy Huang 444b8c2ef1SMark Yao #define RK_BLK_SIZE 512 454b8c2ef1SMark Yao 46186f8572SMark Yao DECLARE_GLOBAL_DATA_PTR; 47186f8572SMark Yao static LIST_HEAD(rockchip_display_list); 48186f8572SMark Yao static LIST_HEAD(logo_cache_list); 49186f8572SMark Yao 50186f8572SMark Yao static unsigned long memory_start; 51186f8572SMark Yao static unsigned long memory_end; 52186f8572SMark Yao 534b8c2ef1SMark Yao static void init_display_buffer(ulong base) 54186f8572SMark Yao { 554b8c2ef1SMark Yao memory_start = base + DRM_ROCKCHIP_FB_SIZE; 56186f8572SMark Yao memory_end = memory_start; 57186f8572SMark Yao } 58186f8572SMark Yao 59186f8572SMark Yao static void *get_display_buffer(int size) 60186f8572SMark Yao { 61186f8572SMark Yao unsigned long roundup_memory = roundup(memory_end, PAGE_SIZE); 62186f8572SMark Yao void *buf; 63186f8572SMark Yao 64186f8572SMark Yao if (roundup_memory + size > memory_start + MEMORY_POOL_SIZE) { 65186f8572SMark Yao printf("failed to alloc %dbyte memory to display\n", size); 66186f8572SMark Yao return NULL; 67186f8572SMark Yao } 68186f8572SMark Yao buf = (void *)roundup_memory; 69186f8572SMark Yao 70186f8572SMark Yao memory_end = roundup_memory + size; 71186f8572SMark Yao 72186f8572SMark Yao return buf; 73186f8572SMark Yao } 74186f8572SMark Yao 75186f8572SMark Yao static unsigned long get_display_size(void) 76186f8572SMark Yao { 77186f8572SMark Yao return memory_end - memory_start; 78186f8572SMark Yao } 79186f8572SMark Yao 80186f8572SMark Yao static bool can_direct_logo(int bpp) 81186f8572SMark Yao { 82186f8572SMark Yao return bpp == 24 || bpp == 32; 83186f8572SMark Yao } 84186f8572SMark Yao 854b8c2ef1SMark Yao 86e2bce6e4SKever Yang static struct udevice *get_panel_device(struct display_state *state, ofnode conn_node) 874b8c2ef1SMark Yao { 884b8c2ef1SMark Yao struct panel_state *panel_state = &state->panel_state; 894b8c2ef1SMark Yao struct udevice *dev; 90e2bce6e4SKever Yang struct connector_state *conn_state = &state->conn_state; 91e2bce6e4SKever Yang ofnode node, ports_node, port_node; 92e2bce6e4SKever Yang struct device_node *port, *panel, *ep; 93e2bce6e4SKever Yang int ph; 94e2bce6e4SKever Yang int ret; 95186f8572SMark Yao 96e2bce6e4SKever Yang node = dev_read_subnode(conn_state->dev, "panel"); 97e2bce6e4SKever Yang if (ofnode_valid(node) && 98e2bce6e4SKever Yang of_device_is_available(ofnode_to_np(node))) { 99e2bce6e4SKever Yang ret = uclass_get_device_by_ofnode(UCLASS_PANEL, node, &dev); 100e2bce6e4SKever Yang if (!ret) { 101e2bce6e4SKever Yang panel_state->node = node; 1024b8c2ef1SMark Yao return dev; 1034b8c2ef1SMark Yao } 1044b8c2ef1SMark Yao } 105186f8572SMark Yao 106e2bce6e4SKever Yang /* TODO: this path not tested */ 107e2bce6e4SKever Yang ports_node = dev_read_subnode(conn_state->dev, "ports"); 108e2bce6e4SKever Yang if (!ofnode_valid(ports_node)) 1094b8c2ef1SMark Yao return NULL; 110186f8572SMark Yao 111e2bce6e4SKever Yang ofnode_for_each_subnode(port_node, ports_node) { 112e2bce6e4SKever Yang ofnode_for_each_subnode(node, port_node) { 113e2bce6e4SKever Yang ph = ofnode_read_u32_default(node, "remote-endpoint", -1); 114186f8572SMark Yao if (!ph) 115186f8572SMark Yao continue; 116e2bce6e4SKever Yang ep = of_find_node_by_phandle(ph); 117e2bce6e4SKever Yang if (!ofnode_valid(np_to_ofnode(ep))) { 118e2bce6e4SKever Yang printf("Warn: can't find endpoint from phdl\n"); 1194b8c2ef1SMark Yao continue; 1204b8c2ef1SMark Yao } 121e2bce6e4SKever Yang port = of_get_parent(ep); 122e2bce6e4SKever Yang if (!ofnode_valid(np_to_ofnode(port))) { 123e2bce6e4SKever Yang printf("Warn: can't find port node\n"); 124e2bce6e4SKever Yang continue; 125e2bce6e4SKever Yang } 126e2bce6e4SKever Yang panel = of_get_parent(port); 127e2bce6e4SKever Yang if (!ofnode_valid(np_to_ofnode(panel))) { 128e2bce6e4SKever Yang printf("Warn: can't find panel node\n"); 129e2bce6e4SKever Yang continue; 130e2bce6e4SKever Yang } 131e2bce6e4SKever Yang ret = uclass_get_device_by_ofnode(UCLASS_PANEL, 132e2bce6e4SKever Yang np_to_ofnode(panel), 133e2bce6e4SKever Yang &dev); 1341e44acfcSWyon Bi if (!ret) { 135335adcb5SKever Yang panel_state->node = np_to_ofnode(panel); 1364b8c2ef1SMark Yao return dev; 1374b8c2ef1SMark Yao } 138186f8572SMark Yao } 1391e44acfcSWyon Bi } 140186f8572SMark Yao 1414b8c2ef1SMark Yao return NULL; 142186f8572SMark Yao } 143186f8572SMark Yao 144186f8572SMark Yao static int connector_phy_init(struct display_state *state) 145186f8572SMark Yao { 146186f8572SMark Yao struct connector_state *conn_state = &state->conn_state; 147186f8572SMark Yao const struct rockchip_phy *phy; 1484b8c2ef1SMark Yao struct udevice *dev; 1494b8c2ef1SMark Yao int ret; 150186f8572SMark Yao 151e2bce6e4SKever Yang ret = uclass_get_device_by_phandle(UCLASS_PHY, conn_state->dev, "phys", 152e2bce6e4SKever Yang &dev); 1534b8c2ef1SMark Yao if (ret) { 154*8e2bab3fSAlgea Cao printf("Warn: can't find phy driver\n"); 155335adcb5SKever Yang return 0; 1564b8c2ef1SMark Yao } 1571e44acfcSWyon Bi 1584b8c2ef1SMark Yao phy = (const struct rockchip_phy *)dev_get_driver_data(dev); 159186f8572SMark Yao if (!phy) { 160186f8572SMark Yao printf("failed to find phy driver\n"); 161186f8572SMark Yao return 0; 162186f8572SMark Yao } 163186f8572SMark Yao 1644b8c2ef1SMark Yao conn_state->phy_dev = dev; 165e2bce6e4SKever Yang conn_state->phy_node = dev->node; 166186f8572SMark Yao 167186f8572SMark Yao if (!phy->funcs || !phy->funcs->init || 168186f8572SMark Yao phy->funcs->init(state)) { 169186f8572SMark Yao printf("failed to init phy driver\n"); 170186f8572SMark Yao return -EINVAL; 171186f8572SMark Yao } 172186f8572SMark Yao conn_state->phy = phy; 173186f8572SMark Yao return 0; 174186f8572SMark Yao } 175186f8572SMark Yao 176186f8572SMark Yao static int connector_panel_init(struct display_state *state) 177186f8572SMark Yao { 178186f8572SMark Yao struct connector_state *conn_state = &state->conn_state; 179186f8572SMark Yao struct panel_state *panel_state = &state->panel_state; 180186f8572SMark Yao struct udevice *dev; 181e2bce6e4SKever Yang ofnode conn_node = conn_state->node; 182186f8572SMark Yao const struct rockchip_panel *panel; 183e2bce6e4SKever Yang ofnode dsp_lut_node; 184186f8572SMark Yao int ret, len; 185186f8572SMark Yao 186186f8572SMark Yao dm_scan_fdt_dev(conn_state->dev); 187186f8572SMark Yao 1884b8c2ef1SMark Yao dev = get_panel_device(state, conn_node); 1894b8c2ef1SMark Yao if (!dev) { 1904b8c2ef1SMark Yao return 0; 191186f8572SMark Yao } 1924b8c2ef1SMark Yao 193186f8572SMark Yao panel = (const struct rockchip_panel *)dev_get_driver_data(dev); 194186f8572SMark Yao if (!panel) { 195186f8572SMark Yao printf("failed to find panel driver\n"); 196186f8572SMark Yao return 0; 197186f8572SMark Yao } 198186f8572SMark Yao 199186f8572SMark Yao panel_state->dev = dev; 200186f8572SMark Yao panel_state->panel = panel; 201186f8572SMark Yao 202c493d00eSWyon Bi if (panel->funcs && panel->funcs->init) { 203c493d00eSWyon Bi ret = panel->funcs->init(state); 204186f8572SMark Yao if (ret) { 205186f8572SMark Yao printf("failed to init panel driver\n"); 206186f8572SMark Yao return ret; 207186f8572SMark Yao } 208c493d00eSWyon Bi } 2091e44acfcSWyon Bi 210e2bce6e4SKever Yang dsp_lut_node = dev_read_subnode(dev, "dsp-lut"); 211e2bce6e4SKever Yang if (!ofnode_valid(dsp_lut_node)) { 2121e44acfcSWyon Bi debug("%s can not find dsp-lut node\n", __func__); 2131e44acfcSWyon Bi return 0; 214e2bce6e4SKever Yang } 2151e44acfcSWyon Bi 216e2bce6e4SKever Yang ofnode_get_property(dsp_lut_node, "gamma-lut", &len); 217186f8572SMark Yao if (len > 0) { 218186f8572SMark Yao conn_state->gamma.size = len / sizeof(u32); 219186f8572SMark Yao conn_state->gamma.lut = malloc(len); 220186f8572SMark Yao if (!conn_state->gamma.lut) { 221186f8572SMark Yao printf("malloc gamma lut failed\n"); 222186f8572SMark Yao return -ENOMEM; 223186f8572SMark Yao } 224e2bce6e4SKever Yang ret = ofnode_read_u32_array(dsp_lut_node, "gamma-lut", 225186f8572SMark Yao conn_state->gamma.lut, 226e2bce6e4SKever Yang conn_state->gamma.size); 227e2bce6e4SKever Yang if (ret) { 228186f8572SMark Yao printf("Cannot decode gamma_lut\n"); 229186f8572SMark Yao conn_state->gamma.lut = NULL; 230186f8572SMark Yao return -EINVAL; 231186f8572SMark Yao } 232186f8572SMark Yao panel_state->dsp_lut_node = dsp_lut_node; 233186f8572SMark Yao } 234186f8572SMark Yao 235186f8572SMark Yao return 0; 236186f8572SMark Yao } 237186f8572SMark Yao 238186f8572SMark Yao int drm_mode_vrefresh(const struct drm_display_mode *mode) 239186f8572SMark Yao { 240186f8572SMark Yao int refresh = 0; 241186f8572SMark Yao unsigned int calc_val; 242186f8572SMark Yao 243186f8572SMark Yao if (mode->vrefresh > 0) { 244186f8572SMark Yao refresh = mode->vrefresh; 245186f8572SMark Yao } else if (mode->htotal > 0 && mode->vtotal > 0) { 246186f8572SMark Yao int vtotal; 247186f8572SMark Yao 248186f8572SMark Yao vtotal = mode->vtotal; 249186f8572SMark Yao /* work out vrefresh the value will be x1000 */ 250186f8572SMark Yao calc_val = (mode->clock * 1000); 251186f8572SMark Yao calc_val /= mode->htotal; 252186f8572SMark Yao refresh = (calc_val + vtotal / 2) / vtotal; 253186f8572SMark Yao 254186f8572SMark Yao if (mode->flags & DRM_MODE_FLAG_INTERLACE) 255186f8572SMark Yao refresh *= 2; 256186f8572SMark Yao if (mode->flags & DRM_MODE_FLAG_DBLSCAN) 257186f8572SMark Yao refresh /= 2; 258186f8572SMark Yao if (mode->vscan > 1) 259186f8572SMark Yao refresh /= mode->vscan; 260186f8572SMark Yao } 261186f8572SMark Yao return refresh; 262186f8572SMark Yao } 263186f8572SMark Yao 264e2bce6e4SKever Yang static int display_get_timing_from_dts(struct panel_state *panel_state, 265186f8572SMark Yao struct drm_display_mode *mode) 266186f8572SMark Yao { 267e2bce6e4SKever Yang int phandle; 268186f8572SMark Yao int hactive, vactive, pixelclock; 269186f8572SMark Yao int hfront_porch, hback_porch, hsync_len; 270186f8572SMark Yao int vfront_porch, vback_porch, vsync_len; 271186f8572SMark Yao int val, flags = 0; 272e2bce6e4SKever Yang ofnode timing, native_mode; 273186f8572SMark Yao 274e2bce6e4SKever Yang timing = dev_read_subnode(panel_state->dev, "display-timings"); 275e2bce6e4SKever Yang if (!ofnode_valid(timing)) 276186f8572SMark Yao return -ENODEV; 277186f8572SMark Yao 278e2bce6e4SKever Yang native_mode = ofnode_find_subnode(timing, "timing"); 279e2bce6e4SKever Yang if (!ofnode_valid(native_mode)) { 280e2bce6e4SKever Yang phandle = ofnode_read_u32_default(timing, "native-mode", -1); 281e2bce6e4SKever Yang native_mode = np_to_ofnode(of_find_node_by_phandle(phandle)); 282e2bce6e4SKever Yang if (!ofnode_valid(native_mode)) { 283186f8572SMark Yao printf("failed to get display timings from DT\n"); 284186f8572SMark Yao return -ENXIO; 285186f8572SMark Yao } 286186f8572SMark Yao } 287186f8572SMark Yao 288186f8572SMark Yao #define FDT_GET_INT(val, name) \ 289e2bce6e4SKever Yang val = ofnode_read_s32_default(native_mode, name, -1); \ 290186f8572SMark Yao if (val < 0) { \ 291186f8572SMark Yao printf("Can't get %s\n", name); \ 292186f8572SMark Yao return -ENXIO; \ 293186f8572SMark Yao } 294186f8572SMark Yao 295186f8572SMark Yao FDT_GET_INT(hactive, "hactive"); 296186f8572SMark Yao FDT_GET_INT(vactive, "vactive"); 297186f8572SMark Yao FDT_GET_INT(pixelclock, "clock-frequency"); 298186f8572SMark Yao FDT_GET_INT(hsync_len, "hsync-len"); 299186f8572SMark Yao FDT_GET_INT(hfront_porch, "hfront-porch"); 300186f8572SMark Yao FDT_GET_INT(hback_porch, "hback-porch"); 301186f8572SMark Yao FDT_GET_INT(vsync_len, "vsync-len"); 302186f8572SMark Yao FDT_GET_INT(vfront_porch, "vfront-porch"); 303186f8572SMark Yao FDT_GET_INT(vback_porch, "vback-porch"); 304186f8572SMark Yao FDT_GET_INT(val, "hsync-active"); 305186f8572SMark Yao flags |= val ? DRM_MODE_FLAG_PHSYNC : DRM_MODE_FLAG_NHSYNC; 306186f8572SMark Yao FDT_GET_INT(val, "vsync-active"); 307186f8572SMark Yao flags |= val ? DRM_MODE_FLAG_PVSYNC : DRM_MODE_FLAG_NVSYNC; 3083a06149eSSandy Huang FDT_GET_INT(val, "pixelclk-active"); 3093a06149eSSandy Huang flags |= val ? DRM_MODE_FLAG_PPIXDATA : 0; 310186f8572SMark Yao 311186f8572SMark Yao mode->hdisplay = hactive; 312186f8572SMark Yao mode->hsync_start = mode->hdisplay + hfront_porch; 313186f8572SMark Yao mode->hsync_end = mode->hsync_start + hsync_len; 314186f8572SMark Yao mode->htotal = mode->hsync_end + hback_porch; 315186f8572SMark Yao 316186f8572SMark Yao mode->vdisplay = vactive; 317186f8572SMark Yao mode->vsync_start = mode->vdisplay + vfront_porch; 318186f8572SMark Yao mode->vsync_end = mode->vsync_start + vsync_len; 319186f8572SMark Yao mode->vtotal = mode->vsync_end + vback_porch; 320186f8572SMark Yao 321186f8572SMark Yao mode->clock = pixelclock / 1000; 322186f8572SMark Yao mode->flags = flags; 323186f8572SMark Yao 324186f8572SMark Yao return 0; 325186f8572SMark Yao } 326186f8572SMark Yao 327ccd843b9SSandy Huang /** 328ccd843b9SSandy Huang * drm_mode_set_crtcinfo - set CRTC modesetting timing parameters 329ccd843b9SSandy Huang * @p: mode 330ccd843b9SSandy Huang * @adjust_flags: a combination of adjustment flags 331ccd843b9SSandy Huang * 332ccd843b9SSandy Huang * Setup the CRTC modesetting timing parameters for @p, adjusting if necessary. 333ccd843b9SSandy Huang * 334ccd843b9SSandy Huang * - The CRTC_INTERLACE_HALVE_V flag can be used to halve vertical timings of 335ccd843b9SSandy Huang * interlaced modes. 336ccd843b9SSandy Huang * - The CRTC_STEREO_DOUBLE flag can be used to compute the timings for 337ccd843b9SSandy Huang * buffers containing two eyes (only adjust the timings when needed, eg. for 338ccd843b9SSandy Huang * "frame packing" or "side by side full"). 339ccd843b9SSandy Huang * - The CRTC_NO_DBLSCAN and CRTC_NO_VSCAN flags request that adjustment *not* 340ccd843b9SSandy Huang * be performed for doublescan and vscan > 1 modes respectively. 341ccd843b9SSandy Huang */ 342ccd843b9SSandy Huang void drm_mode_set_crtcinfo(struct drm_display_mode *p, int adjust_flags) 343ccd843b9SSandy Huang { 344ccd843b9SSandy Huang if ((p == NULL) || ((p->type & DRM_MODE_TYPE_CRTC_C) == DRM_MODE_TYPE_BUILTIN)) 345ccd843b9SSandy Huang return; 346ccd843b9SSandy Huang 347ccd843b9SSandy Huang if (p->flags & DRM_MODE_FLAG_DBLCLK) 348ccd843b9SSandy Huang p->crtc_clock = 2 * p->clock; 349ccd843b9SSandy Huang else 350ccd843b9SSandy Huang p->crtc_clock = p->clock; 351ccd843b9SSandy Huang p->crtc_hdisplay = p->hdisplay; 352ccd843b9SSandy Huang p->crtc_hsync_start = p->hsync_start; 353ccd843b9SSandy Huang p->crtc_hsync_end = p->hsync_end; 354ccd843b9SSandy Huang p->crtc_htotal = p->htotal; 355ccd843b9SSandy Huang p->crtc_hskew = p->hskew; 356ccd843b9SSandy Huang p->crtc_vdisplay = p->vdisplay; 357ccd843b9SSandy Huang p->crtc_vsync_start = p->vsync_start; 358ccd843b9SSandy Huang p->crtc_vsync_end = p->vsync_end; 359ccd843b9SSandy Huang p->crtc_vtotal = p->vtotal; 360ccd843b9SSandy Huang 361ccd843b9SSandy Huang if (p->flags & DRM_MODE_FLAG_INTERLACE) { 362ccd843b9SSandy Huang if (adjust_flags & CRTC_INTERLACE_HALVE_V) { 363ccd843b9SSandy Huang p->crtc_vdisplay /= 2; 364ccd843b9SSandy Huang p->crtc_vsync_start /= 2; 365ccd843b9SSandy Huang p->crtc_vsync_end /= 2; 366ccd843b9SSandy Huang p->crtc_vtotal /= 2; 367ccd843b9SSandy Huang } 368ccd843b9SSandy Huang } 369ccd843b9SSandy Huang 370ccd843b9SSandy Huang if (!(adjust_flags & CRTC_NO_DBLSCAN)) { 371ccd843b9SSandy Huang if (p->flags & DRM_MODE_FLAG_DBLSCAN) { 372ccd843b9SSandy Huang p->crtc_vdisplay *= 2; 373ccd843b9SSandy Huang p->crtc_vsync_start *= 2; 374ccd843b9SSandy Huang p->crtc_vsync_end *= 2; 375ccd843b9SSandy Huang p->crtc_vtotal *= 2; 376ccd843b9SSandy Huang } 377ccd843b9SSandy Huang } 378ccd843b9SSandy Huang 379ccd843b9SSandy Huang if (!(adjust_flags & CRTC_NO_VSCAN)) { 380ccd843b9SSandy Huang if (p->vscan > 1) { 381ccd843b9SSandy Huang p->crtc_vdisplay *= p->vscan; 382ccd843b9SSandy Huang p->crtc_vsync_start *= p->vscan; 383ccd843b9SSandy Huang p->crtc_vsync_end *= p->vscan; 384ccd843b9SSandy Huang p->crtc_vtotal *= p->vscan; 385ccd843b9SSandy Huang } 386ccd843b9SSandy Huang } 387ccd843b9SSandy Huang 388ccd843b9SSandy Huang if (adjust_flags & CRTC_STEREO_DOUBLE) { 389ccd843b9SSandy Huang unsigned int layout = p->flags & DRM_MODE_FLAG_3D_MASK; 390ccd843b9SSandy Huang 391ccd843b9SSandy Huang switch (layout) { 392ccd843b9SSandy Huang case DRM_MODE_FLAG_3D_FRAME_PACKING: 393ccd843b9SSandy Huang p->crtc_clock *= 2; 394ccd843b9SSandy Huang p->crtc_vdisplay += p->crtc_vtotal; 395ccd843b9SSandy Huang p->crtc_vsync_start += p->crtc_vtotal; 396ccd843b9SSandy Huang p->crtc_vsync_end += p->crtc_vtotal; 397ccd843b9SSandy Huang p->crtc_vtotal += p->crtc_vtotal; 398ccd843b9SSandy Huang break; 399ccd843b9SSandy Huang } 400ccd843b9SSandy Huang } 401ccd843b9SSandy Huang 402ccd843b9SSandy Huang p->crtc_vblank_start = min(p->crtc_vsync_start, p->crtc_vdisplay); 403ccd843b9SSandy Huang p->crtc_vblank_end = max(p->crtc_vsync_end, p->crtc_vtotal); 404ccd843b9SSandy Huang p->crtc_hblank_start = min(p->crtc_hsync_start, p->crtc_hdisplay); 405ccd843b9SSandy Huang p->crtc_hblank_end = max(p->crtc_hsync_end, p->crtc_htotal); 406ccd843b9SSandy Huang } 407ccd843b9SSandy Huang 408*8e2bab3fSAlgea Cao /** 409*8e2bab3fSAlgea Cao * drm_mode_is_420_only - if a given videomode can be only supported in YCBCR420 410*8e2bab3fSAlgea Cao * output format 411*8e2bab3fSAlgea Cao * 412*8e2bab3fSAlgea Cao * @connector: drm connector under action. 413*8e2bab3fSAlgea Cao * @mode: video mode to be tested. 414*8e2bab3fSAlgea Cao * 415*8e2bab3fSAlgea Cao * Returns: 416*8e2bab3fSAlgea Cao * true if the mode can be supported in YCBCR420 format 417*8e2bab3fSAlgea Cao * false if not. 418*8e2bab3fSAlgea Cao */ 419*8e2bab3fSAlgea Cao bool drm_mode_is_420_only(const struct drm_display_info *display, 420*8e2bab3fSAlgea Cao struct drm_display_mode *mode) 421*8e2bab3fSAlgea Cao { 422*8e2bab3fSAlgea Cao u8 vic = drm_match_cea_mode(mode); 423*8e2bab3fSAlgea Cao 424*8e2bab3fSAlgea Cao return test_bit(vic, display->hdmi.y420_vdb_modes); 425*8e2bab3fSAlgea Cao } 426*8e2bab3fSAlgea Cao 427*8e2bab3fSAlgea Cao /** 428*8e2bab3fSAlgea Cao * drm_mode_is_420_also - if a given videomode can be supported in YCBCR420 429*8e2bab3fSAlgea Cao * output format also (along with RGB/YCBCR444/422) 430*8e2bab3fSAlgea Cao * 431*8e2bab3fSAlgea Cao * @display: display under action. 432*8e2bab3fSAlgea Cao * @mode: video mode to be tested. 433*8e2bab3fSAlgea Cao * 434*8e2bab3fSAlgea Cao * Returns: 435*8e2bab3fSAlgea Cao * true if the mode can be support YCBCR420 format 436*8e2bab3fSAlgea Cao * false if not. 437*8e2bab3fSAlgea Cao */ 438*8e2bab3fSAlgea Cao bool drm_mode_is_420_also(const struct drm_display_info *display, 439*8e2bab3fSAlgea Cao struct drm_display_mode *mode) 440*8e2bab3fSAlgea Cao { 441*8e2bab3fSAlgea Cao u8 vic = drm_match_cea_mode(mode); 442*8e2bab3fSAlgea Cao 443*8e2bab3fSAlgea Cao return test_bit(vic, display->hdmi.y420_cmdb_modes); 444*8e2bab3fSAlgea Cao } 445*8e2bab3fSAlgea Cao 446*8e2bab3fSAlgea Cao /** 447*8e2bab3fSAlgea Cao * drm_mode_is_420 - if a given videomode can be supported in YCBCR420 448*8e2bab3fSAlgea Cao * output format 449*8e2bab3fSAlgea Cao * 450*8e2bab3fSAlgea Cao * @display: display under action. 451*8e2bab3fSAlgea Cao * @mode: video mode to be tested. 452*8e2bab3fSAlgea Cao * 453*8e2bab3fSAlgea Cao * Returns: 454*8e2bab3fSAlgea Cao * true if the mode can be supported in YCBCR420 format 455*8e2bab3fSAlgea Cao * false if not. 456*8e2bab3fSAlgea Cao */ 457*8e2bab3fSAlgea Cao bool drm_mode_is_420(const struct drm_display_info *display, 458*8e2bab3fSAlgea Cao struct drm_display_mode *mode) 459*8e2bab3fSAlgea Cao { 460*8e2bab3fSAlgea Cao return drm_mode_is_420_only(display, mode) || 461*8e2bab3fSAlgea Cao drm_mode_is_420_also(display, mode); 462*8e2bab3fSAlgea Cao } 463*8e2bab3fSAlgea Cao 464186f8572SMark Yao static int display_get_timing(struct display_state *state) 465186f8572SMark Yao { 466186f8572SMark Yao struct connector_state *conn_state = &state->conn_state; 467186f8572SMark Yao const struct rockchip_connector *conn = conn_state->connector; 468186f8572SMark Yao const struct rockchip_connector_funcs *conn_funcs = conn->funcs; 469186f8572SMark Yao struct drm_display_mode *mode = &conn_state->mode; 470186f8572SMark Yao const struct drm_display_mode *m; 4714b8c2ef1SMark Yao struct panel_state *panel_state = &state->panel_state; 472c493d00eSWyon Bi const struct rockchip_panel *panel = panel_state->panel; 473c493d00eSWyon Bi const struct rockchip_panel_funcs *panel_funcs = panel->funcs; 474c493d00eSWyon Bi ofnode panel_node = panel_state->node; 475c493d00eSWyon Bi int ret; 476186f8572SMark Yao 477c493d00eSWyon Bi if (ofnode_valid(panel_node) && !display_get_timing_from_dts(panel_state, mode)) { 478186f8572SMark Yao printf("Using display timing dts\n"); 479186f8572SMark Yao goto done; 480186f8572SMark Yao } 481186f8572SMark Yao 482c493d00eSWyon Bi if (panel->data) { 483c493d00eSWyon Bi m = (const struct drm_display_mode *)panel->data; 484186f8572SMark Yao memcpy(mode, m, sizeof(*m)); 485c493d00eSWyon Bi printf("Using display timing from compatible panel driver\n"); 486186f8572SMark Yao goto done; 487186f8572SMark Yao } 488186f8572SMark Yao 489186f8572SMark Yao if (conn_funcs->get_edid && !conn_funcs->get_edid(state)) { 490186f8572SMark Yao int panel_bits_per_colourp; 491186f8572SMark Yao 492c493d00eSWyon Bi /* In order to read EDID, the panel needs to be powered on */ 493c493d00eSWyon Bi if (panel_funcs->prepare) { 494c493d00eSWyon Bi ret = panel_funcs->prepare(state); 495c493d00eSWyon Bi if (ret) { 496c493d00eSWyon Bi printf("failed to prepare panel\n"); 497c493d00eSWyon Bi return ret; 498c493d00eSWyon Bi } 499c493d00eSWyon Bi } 500c493d00eSWyon Bi 501186f8572SMark Yao if (!edid_get_drm_mode((void *)&conn_state->edid, 502186f8572SMark Yao sizeof(conn_state->edid), mode, 503186f8572SMark Yao &panel_bits_per_colourp)) { 504186f8572SMark Yao printf("Using display timing from edid\n"); 505186f8572SMark Yao edid_print_info((void *)&conn_state->edid); 506186f8572SMark Yao goto done; 507c493d00eSWyon Bi } else { 508c493d00eSWyon Bi if (panel_funcs->unprepare) 509c493d00eSWyon Bi panel_funcs->unprepare(state); 510186f8572SMark Yao } 511186f8572SMark Yao } 512186f8572SMark Yao 513186f8572SMark Yao printf("failed to find display timing\n"); 514186f8572SMark Yao return -ENODEV; 515186f8572SMark Yao done: 516186f8572SMark Yao printf("Detailed mode clock %u kHz, flags[%x]\n" 517186f8572SMark Yao " H: %04d %04d %04d %04d\n" 518186f8572SMark Yao " V: %04d %04d %04d %04d\n" 519186f8572SMark Yao "bus_format: %x\n", 520186f8572SMark Yao mode->clock, mode->flags, 521186f8572SMark Yao mode->hdisplay, mode->hsync_start, 522186f8572SMark Yao mode->hsync_end, mode->htotal, 523186f8572SMark Yao mode->vdisplay, mode->vsync_start, 524186f8572SMark Yao mode->vsync_end, mode->vtotal, 525186f8572SMark Yao conn_state->bus_format); 526186f8572SMark Yao 527186f8572SMark Yao return 0; 528186f8572SMark Yao } 529186f8572SMark Yao 530186f8572SMark Yao static int display_init(struct display_state *state) 531186f8572SMark Yao { 532186f8572SMark Yao struct connector_state *conn_state = &state->conn_state; 533186f8572SMark Yao const struct rockchip_connector *conn = conn_state->connector; 534186f8572SMark Yao const struct rockchip_connector_funcs *conn_funcs = conn->funcs; 535186f8572SMark Yao struct crtc_state *crtc_state = &state->crtc_state; 536186f8572SMark Yao const struct rockchip_crtc *crtc = crtc_state->crtc; 537186f8572SMark Yao const struct rockchip_crtc_funcs *crtc_funcs = crtc->funcs; 538ccd843b9SSandy Huang struct drm_display_mode *mode = &conn_state->mode; 539186f8572SMark Yao int ret = 0; 54052015e97SSandy Huang static bool __print_once = false; 541186f8572SMark Yao 54252015e97SSandy Huang if (!__print_once) { 54352015e97SSandy Huang __print_once = true; 544e559407dSSandy Huang printf("Rockchip UBOOT DRM driver version: %s\n", DRIVER_VERSION); 54552015e97SSandy Huang } 546e559407dSSandy Huang 547186f8572SMark Yao if (state->is_init) 548186f8572SMark Yao return 0; 549186f8572SMark Yao 550186f8572SMark Yao if (!conn_funcs || !crtc_funcs) { 551186f8572SMark Yao printf("failed to find connector or crtc functions\n"); 552186f8572SMark Yao return -ENXIO; 553186f8572SMark Yao } 554186f8572SMark Yao 555186f8572SMark Yao if (conn_funcs->init) { 556186f8572SMark Yao ret = conn_funcs->init(state); 557186f8572SMark Yao if (ret) 5584b8c2ef1SMark Yao goto deinit; 559186f8572SMark Yao } 560186f8572SMark Yao /* 561186f8572SMark Yao * support hotplug, but not connect; 562186f8572SMark Yao */ 563186f8572SMark Yao if (conn_funcs->detect) { 564186f8572SMark Yao ret = conn_funcs->detect(state); 565186f8572SMark Yao if (!ret) 566186f8572SMark Yao goto deinit; 567186f8572SMark Yao } 568186f8572SMark Yao 569186f8572SMark Yao if (conn_funcs->get_timing) { 570186f8572SMark Yao ret = conn_funcs->get_timing(state); 571186f8572SMark Yao if (ret) 572186f8572SMark Yao goto deinit; 573186f8572SMark Yao } else { 574186f8572SMark Yao ret = display_get_timing(state); 575186f8572SMark Yao if (ret) 576186f8572SMark Yao goto deinit; 577186f8572SMark Yao } 578ccd843b9SSandy Huang drm_mode_set_crtcinfo(mode, CRTC_INTERLACE_HALVE_V); 579186f8572SMark Yao 580186f8572SMark Yao if (crtc_funcs->init) { 581186f8572SMark Yao ret = crtc_funcs->init(state); 582186f8572SMark Yao if (ret) 583186f8572SMark Yao goto deinit; 584186f8572SMark Yao } 585186f8572SMark Yao state->is_init = 1; 586186f8572SMark Yao 587186f8572SMark Yao return 0; 588186f8572SMark Yao 589186f8572SMark Yao deinit: 590186f8572SMark Yao if (conn_funcs->deinit) 591186f8572SMark Yao conn_funcs->deinit(state); 592186f8572SMark Yao return ret; 593186f8572SMark Yao } 594186f8572SMark Yao 59567b9012cSSandy Huang int display_send_mcu_cmd(struct display_state *state, u32 type, u32 val) 59667b9012cSSandy Huang { 59767b9012cSSandy Huang struct crtc_state *crtc_state = &state->crtc_state; 59867b9012cSSandy Huang const struct rockchip_crtc *crtc = crtc_state->crtc; 59967b9012cSSandy Huang const struct rockchip_crtc_funcs *crtc_funcs = crtc->funcs; 60067b9012cSSandy Huang int ret; 60167b9012cSSandy Huang 60267b9012cSSandy Huang if (!state->is_init) 60367b9012cSSandy Huang return -EINVAL; 60467b9012cSSandy Huang 60567b9012cSSandy Huang if (crtc_funcs->send_mcu_cmd) { 60667b9012cSSandy Huang ret = crtc_funcs->send_mcu_cmd(state, type, val); 60767b9012cSSandy Huang if (ret) 60867b9012cSSandy Huang return ret; 60967b9012cSSandy Huang } 61067b9012cSSandy Huang 61167b9012cSSandy Huang return 0; 61267b9012cSSandy Huang } 61367b9012cSSandy Huang 614186f8572SMark Yao static int display_set_plane(struct display_state *state) 615186f8572SMark Yao { 616186f8572SMark Yao struct crtc_state *crtc_state = &state->crtc_state; 617186f8572SMark Yao const struct rockchip_crtc *crtc = crtc_state->crtc; 618186f8572SMark Yao const struct rockchip_crtc_funcs *crtc_funcs = crtc->funcs; 619186f8572SMark Yao int ret; 620186f8572SMark Yao 621186f8572SMark Yao if (!state->is_init) 622186f8572SMark Yao return -EINVAL; 623186f8572SMark Yao 624186f8572SMark Yao if (crtc_funcs->set_plane) { 625186f8572SMark Yao ret = crtc_funcs->set_plane(state); 626186f8572SMark Yao if (ret) 627186f8572SMark Yao return ret; 628186f8572SMark Yao } 629186f8572SMark Yao 630186f8572SMark Yao return 0; 631186f8572SMark Yao } 632186f8572SMark Yao 633bcf90936SSandy Huang static int display_panel_prepare(struct display_state *state) 634bcf90936SSandy Huang { 635bcf90936SSandy Huang struct panel_state *panel_state = &state->panel_state; 636bcf90936SSandy Huang const struct rockchip_panel *panel = panel_state->panel; 637bcf90936SSandy Huang 638bcf90936SSandy Huang if (!panel || !panel->funcs || !panel->funcs->prepare) { 639bcf90936SSandy Huang printf("%s: failed to find panel prepare funcs\n", __func__); 640bcf90936SSandy Huang return -ENODEV; 641bcf90936SSandy Huang } 642bcf90936SSandy Huang 643bcf90936SSandy Huang return panel->funcs->prepare(state); 644bcf90936SSandy Huang } 645bcf90936SSandy Huang 646bcf90936SSandy Huang static int display_panel_enable(struct display_state *state) 647bcf90936SSandy Huang { 648bcf90936SSandy Huang struct panel_state *panel_state = &state->panel_state; 649bcf90936SSandy Huang const struct rockchip_panel *panel = panel_state->panel; 650bcf90936SSandy Huang 651bcf90936SSandy Huang if (!panel || !panel->funcs || !panel->funcs->enable) { 652bcf90936SSandy Huang printf("%s: failed to find panel enable funcs\n", __func__); 653bcf90936SSandy Huang return -ENODEV; 654bcf90936SSandy Huang } 655bcf90936SSandy Huang 656bcf90936SSandy Huang return panel->funcs->enable(state); 657bcf90936SSandy Huang } 658bcf90936SSandy Huang 659bcf90936SSandy Huang static void display_panel_unprepare(struct display_state *state) 660bcf90936SSandy Huang { 661bcf90936SSandy Huang struct panel_state *panel_state = &state->panel_state; 662bcf90936SSandy Huang const struct rockchip_panel *panel = panel_state->panel; 663bcf90936SSandy Huang 664bcf90936SSandy Huang if (!panel || !panel->funcs || !panel->funcs->unprepare) { 665bcf90936SSandy Huang printf("%s: failed to find panel unprepare funcs\n", __func__); 666bcf90936SSandy Huang return; 667bcf90936SSandy Huang } 668bcf90936SSandy Huang 669bcf90936SSandy Huang panel->funcs->unprepare(state); 670bcf90936SSandy Huang } 671bcf90936SSandy Huang 672bcf90936SSandy Huang static void display_panel_disable(struct display_state *state) 673bcf90936SSandy Huang { 674bcf90936SSandy Huang struct panel_state *panel_state = &state->panel_state; 675bcf90936SSandy Huang const struct rockchip_panel *panel = panel_state->panel; 676bcf90936SSandy Huang 677bcf90936SSandy Huang if (!panel || !panel->funcs || !panel->funcs->disable) { 678bcf90936SSandy Huang printf("%s: failed to find panel disable funcs\n", __func__); 679bcf90936SSandy Huang return; 680bcf90936SSandy Huang } 681bcf90936SSandy Huang 682bcf90936SSandy Huang panel->funcs->disable(state); 683bcf90936SSandy Huang } 684bcf90936SSandy Huang 685186f8572SMark Yao static int display_enable(struct display_state *state) 686186f8572SMark Yao { 687186f8572SMark Yao struct connector_state *conn_state = &state->conn_state; 688186f8572SMark Yao const struct rockchip_connector *conn = conn_state->connector; 689186f8572SMark Yao const struct rockchip_connector_funcs *conn_funcs = conn->funcs; 690186f8572SMark Yao struct crtc_state *crtc_state = &state->crtc_state; 691186f8572SMark Yao const struct rockchip_crtc *crtc = crtc_state->crtc; 692186f8572SMark Yao const struct rockchip_crtc_funcs *crtc_funcs = crtc->funcs; 693186f8572SMark Yao int ret = 0; 694186f8572SMark Yao 695186f8572SMark Yao display_init(state); 696186f8572SMark Yao 697186f8572SMark Yao if (!state->is_init) 698186f8572SMark Yao return -EINVAL; 699186f8572SMark Yao 700186f8572SMark Yao if (state->is_enable) 701186f8572SMark Yao return 0; 702186f8572SMark Yao 703186f8572SMark Yao if (crtc_funcs->prepare) { 704186f8572SMark Yao ret = crtc_funcs->prepare(state); 705186f8572SMark Yao if (ret) 706186f8572SMark Yao return ret; 707186f8572SMark Yao } 708186f8572SMark Yao 709186f8572SMark Yao if (conn_funcs->prepare) { 710186f8572SMark Yao ret = conn_funcs->prepare(state); 711186f8572SMark Yao if (ret) 712186f8572SMark Yao goto unprepare_crtc; 713186f8572SMark Yao } 714186f8572SMark Yao 715bcf90936SSandy Huang display_panel_prepare(state); 716186f8572SMark Yao 717186f8572SMark Yao if (crtc_funcs->enable) { 718186f8572SMark Yao ret = crtc_funcs->enable(state); 719186f8572SMark Yao if (ret) 720bcf90936SSandy Huang goto unprepare_conn; 721186f8572SMark Yao } 722186f8572SMark Yao 723186f8572SMark Yao if (conn_funcs->enable) { 724186f8572SMark Yao ret = conn_funcs->enable(state); 725186f8572SMark Yao if (ret) 726186f8572SMark Yao goto disable_crtc; 727186f8572SMark Yao } 728186f8572SMark Yao 729bcf90936SSandy Huang display_panel_enable(state); 730186f8572SMark Yao 731186f8572SMark Yao state->is_enable = true; 732186f8572SMark Yao return 0; 733c493d00eSWyon Bi 734186f8572SMark Yao disable_crtc: 735186f8572SMark Yao if (crtc_funcs->disable) 736186f8572SMark Yao crtc_funcs->disable(state); 737c493d00eSWyon Bi unprepare_conn: 738c493d00eSWyon Bi if (conn_funcs->unprepare) 739c493d00eSWyon Bi conn_funcs->unprepare(state); 740bcf90936SSandy Huang unprepare_crtc: 741bcf90936SSandy Huang if (crtc_funcs->unprepare) 742bcf90936SSandy Huang crtc_funcs->unprepare(state); 743186f8572SMark Yao return ret; 744186f8572SMark Yao } 745186f8572SMark Yao 746186f8572SMark Yao static int display_disable(struct display_state *state) 747186f8572SMark Yao { 748186f8572SMark Yao struct connector_state *conn_state = &state->conn_state; 749186f8572SMark Yao const struct rockchip_connector *conn = conn_state->connector; 750186f8572SMark Yao const struct rockchip_connector_funcs *conn_funcs = conn->funcs; 751186f8572SMark Yao struct crtc_state *crtc_state = &state->crtc_state; 752186f8572SMark Yao const struct rockchip_crtc *crtc = crtc_state->crtc; 753186f8572SMark Yao const struct rockchip_crtc_funcs *crtc_funcs = crtc->funcs; 754186f8572SMark Yao 755186f8572SMark Yao if (!state->is_init) 756186f8572SMark Yao return 0; 757186f8572SMark Yao 758186f8572SMark Yao if (!state->is_enable) 759186f8572SMark Yao return 0; 760186f8572SMark Yao 761bcf90936SSandy Huang display_panel_disable(state); 762186f8572SMark Yao 763186f8572SMark Yao if (crtc_funcs->disable) 764186f8572SMark Yao crtc_funcs->disable(state); 765186f8572SMark Yao 766186f8572SMark Yao if (conn_funcs->disable) 767186f8572SMark Yao conn_funcs->disable(state); 768186f8572SMark Yao 769bcf90936SSandy Huang display_panel_unprepare(state); 770186f8572SMark Yao 771186f8572SMark Yao if (conn_funcs->unprepare) 772186f8572SMark Yao conn_funcs->unprepare(state); 773186f8572SMark Yao 774186f8572SMark Yao state->is_enable = 0; 775186f8572SMark Yao state->is_init = 0; 776186f8572SMark Yao 777186f8572SMark Yao return 0; 778186f8572SMark Yao } 779186f8572SMark Yao 780186f8572SMark Yao static int display_logo(struct display_state *state) 781186f8572SMark Yao { 782186f8572SMark Yao struct crtc_state *crtc_state = &state->crtc_state; 783186f8572SMark Yao struct connector_state *conn_state = &state->conn_state; 784186f8572SMark Yao struct logo_info *logo = &state->logo; 785186f8572SMark Yao int hdisplay, vdisplay; 786186f8572SMark Yao 787186f8572SMark Yao display_init(state); 788186f8572SMark Yao if (!state->is_init) 789186f8572SMark Yao return -ENODEV; 790186f8572SMark Yao 791186f8572SMark Yao switch (logo->bpp) { 792186f8572SMark Yao case 16: 793186f8572SMark Yao crtc_state->format = ROCKCHIP_FMT_RGB565; 794186f8572SMark Yao break; 795186f8572SMark Yao case 24: 796186f8572SMark Yao crtc_state->format = ROCKCHIP_FMT_RGB888; 797186f8572SMark Yao break; 798186f8572SMark Yao case 32: 799186f8572SMark Yao crtc_state->format = ROCKCHIP_FMT_ARGB8888; 800186f8572SMark Yao break; 801186f8572SMark Yao default: 802186f8572SMark Yao printf("can't support bmp bits[%d]\n", logo->bpp); 803186f8572SMark Yao return -EINVAL; 804186f8572SMark Yao } 805186f8572SMark Yao crtc_state->rb_swap = logo->bpp != 32; 806186f8572SMark Yao hdisplay = conn_state->mode.hdisplay; 807186f8572SMark Yao vdisplay = conn_state->mode.vdisplay; 808186f8572SMark Yao crtc_state->src_w = logo->width; 809186f8572SMark Yao crtc_state->src_h = logo->height; 810186f8572SMark Yao crtc_state->src_x = 0; 811186f8572SMark Yao crtc_state->src_y = 0; 812186f8572SMark Yao crtc_state->ymirror = logo->ymirror; 813186f8572SMark Yao 8144b8c2ef1SMark Yao crtc_state->dma_addr = (u32)(unsigned long)logo->mem + logo->offset; 815186f8572SMark Yao crtc_state->xvir = ALIGN(crtc_state->src_w * logo->bpp, 32) >> 5; 816186f8572SMark Yao 817186f8572SMark Yao if (logo->mode == ROCKCHIP_DISPLAY_FULLSCREEN) { 818186f8572SMark Yao crtc_state->crtc_x = 0; 819186f8572SMark Yao crtc_state->crtc_y = 0; 820186f8572SMark Yao crtc_state->crtc_w = hdisplay; 821186f8572SMark Yao crtc_state->crtc_h = vdisplay; 822186f8572SMark Yao } else { 823186f8572SMark Yao if (crtc_state->src_w >= hdisplay) { 824186f8572SMark Yao crtc_state->crtc_x = 0; 825186f8572SMark Yao crtc_state->crtc_w = hdisplay; 826186f8572SMark Yao } else { 827186f8572SMark Yao crtc_state->crtc_x = (hdisplay - crtc_state->src_w) / 2; 828186f8572SMark Yao crtc_state->crtc_w = crtc_state->src_w; 829186f8572SMark Yao } 830186f8572SMark Yao 831186f8572SMark Yao if (crtc_state->src_h >= vdisplay) { 832186f8572SMark Yao crtc_state->crtc_y = 0; 833186f8572SMark Yao crtc_state->crtc_h = vdisplay; 834186f8572SMark Yao } else { 835186f8572SMark Yao crtc_state->crtc_y = (vdisplay - crtc_state->src_h) / 2; 836186f8572SMark Yao crtc_state->crtc_h = crtc_state->src_h; 837186f8572SMark Yao } 838186f8572SMark Yao } 839186f8572SMark Yao 840186f8572SMark Yao display_set_plane(state); 841186f8572SMark Yao display_enable(state); 842186f8572SMark Yao 843186f8572SMark Yao return 0; 844186f8572SMark Yao } 845186f8572SMark Yao 846e2bce6e4SKever Yang static int get_crtc_id(ofnode connect) 847186f8572SMark Yao { 848e2bce6e4SKever Yang int phandle; 849e2bce6e4SKever Yang struct device_node *remote; 850186f8572SMark Yao int val; 851186f8572SMark Yao 852e2bce6e4SKever Yang phandle = ofnode_read_u32_default(connect, "remote-endpoint", -1); 853186f8572SMark Yao if (phandle < 0) 854186f8572SMark Yao goto err; 855e2bce6e4SKever Yang remote = of_find_node_by_phandle(phandle); 856e2bce6e4SKever Yang val = ofnode_read_u32_default(np_to_ofnode(remote), "reg", -1); 857186f8572SMark Yao if (val < 0) 858186f8572SMark Yao goto err; 859186f8572SMark Yao 860186f8572SMark Yao return val; 861186f8572SMark Yao err: 862186f8572SMark Yao printf("Can't get crtc id, default set to id = 0\n"); 863186f8572SMark Yao return 0; 864186f8572SMark Yao } 865186f8572SMark Yao 86667b9012cSSandy Huang static int get_crtc_mcu_mode(struct crtc_state *crtc_state) 86767b9012cSSandy Huang { 86867b9012cSSandy Huang ofnode mcu_node; 86967b9012cSSandy Huang int total_pixel, cs_pst, cs_pend, rw_pst, rw_pend; 87067b9012cSSandy Huang 87167b9012cSSandy Huang mcu_node = dev_read_subnode(crtc_state->dev, "mcu-timing"); 87267b9012cSSandy Huang 87367b9012cSSandy Huang #define FDT_GET_MCU_INT(val, name) \ 87467b9012cSSandy Huang do { \ 87567b9012cSSandy Huang val = ofnode_read_s32_default(mcu_node, name, -1); \ 87667b9012cSSandy Huang if (val < 0) { \ 87767b9012cSSandy Huang printf("Can't get %s\n", name); \ 87867b9012cSSandy Huang return -ENXIO; \ 87967b9012cSSandy Huang } \ 88067b9012cSSandy Huang } while (0) 88167b9012cSSandy Huang 88267b9012cSSandy Huang FDT_GET_MCU_INT(total_pixel, "mcu-pix-total"); 88367b9012cSSandy Huang FDT_GET_MCU_INT(cs_pst, "mcu-cs-pst"); 88467b9012cSSandy Huang FDT_GET_MCU_INT(cs_pend, "mcu-cs-pend"); 88567b9012cSSandy Huang FDT_GET_MCU_INT(rw_pst, "mcu-rw-pst"); 88667b9012cSSandy Huang FDT_GET_MCU_INT(rw_pend, "mcu-rw-pend"); 88767b9012cSSandy Huang 88867b9012cSSandy Huang crtc_state->mcu_timing.mcu_pix_total = total_pixel; 88967b9012cSSandy Huang crtc_state->mcu_timing.mcu_cs_pst = cs_pst; 89067b9012cSSandy Huang crtc_state->mcu_timing.mcu_cs_pend = cs_pend; 89167b9012cSSandy Huang crtc_state->mcu_timing.mcu_rw_pst = rw_pst; 89267b9012cSSandy Huang crtc_state->mcu_timing.mcu_rw_pend = rw_pend; 89367b9012cSSandy Huang 89467b9012cSSandy Huang return 0; 89567b9012cSSandy Huang } 89667b9012cSSandy Huang 897186f8572SMark Yao struct rockchip_logo_cache *find_or_alloc_logo_cache(const char *bmp) 898186f8572SMark Yao { 899186f8572SMark Yao struct rockchip_logo_cache *tmp, *logo_cache = NULL; 900186f8572SMark Yao 901186f8572SMark Yao list_for_each_entry(tmp, &logo_cache_list, head) { 902186f8572SMark Yao if (!strcmp(tmp->name, bmp)) { 903186f8572SMark Yao logo_cache = tmp; 904186f8572SMark Yao break; 905186f8572SMark Yao } 906186f8572SMark Yao } 907186f8572SMark Yao 908186f8572SMark Yao if (!logo_cache) { 909186f8572SMark Yao logo_cache = malloc(sizeof(*logo_cache)); 910186f8572SMark Yao if (!logo_cache) { 911186f8572SMark Yao printf("failed to alloc memory for logo cache\n"); 912186f8572SMark Yao return NULL; 913186f8572SMark Yao } 914186f8572SMark Yao memset(logo_cache, 0, sizeof(*logo_cache)); 915186f8572SMark Yao strcpy(logo_cache->name, bmp); 916186f8572SMark Yao INIT_LIST_HEAD(&logo_cache->head); 917186f8572SMark Yao list_add_tail(&logo_cache->head, &logo_cache_list); 918186f8572SMark Yao } 919186f8572SMark Yao 920186f8572SMark Yao return logo_cache; 921186f8572SMark Yao } 922186f8572SMark Yao 9235eb61944SSandy Huang /* Note: used only for rkfb kernel driver */ 9245eb61944SSandy Huang static int load_kernel_bmp_logo(struct logo_info *logo, const char *bmp_name) 9255eb61944SSandy Huang { 9265eb61944SSandy Huang #ifdef CONFIG_ROCKCHIP_RESOURCE_IMAGE 9275eb61944SSandy Huang void *dst = NULL; 9285eb61944SSandy Huang int len, size; 9295eb61944SSandy Huang struct bmp_header *header; 9305eb61944SSandy Huang 9315eb61944SSandy Huang if (!logo || !bmp_name) 9325eb61944SSandy Huang return -EINVAL; 9335eb61944SSandy Huang 9345eb61944SSandy Huang header = malloc(RK_BLK_SIZE); 9355eb61944SSandy Huang if (!header) 9365eb61944SSandy Huang return -ENOMEM; 9375eb61944SSandy Huang 9385eb61944SSandy Huang len = rockchip_read_resource_file(header, bmp_name, 0, RK_BLK_SIZE); 9395eb61944SSandy Huang if (len != RK_BLK_SIZE) { 9405eb61944SSandy Huang free(header); 9415eb61944SSandy Huang return -EINVAL; 9425eb61944SSandy Huang } 9435eb61944SSandy Huang size = get_unaligned_le32(&header->file_size); 9445eb61944SSandy Huang dst = (void *)(memory_start + MEMORY_POOL_SIZE / 2); 9455eb61944SSandy Huang len = rockchip_read_resource_file(dst, bmp_name, 0, size); 9465eb61944SSandy Huang if (len != size) { 9475eb61944SSandy Huang printf("failed to load bmp %s\n", bmp_name); 9485eb61944SSandy Huang free(header); 9495eb61944SSandy Huang return -ENOENT; 9505eb61944SSandy Huang } 9515eb61944SSandy Huang 9525eb61944SSandy Huang logo->mem = dst; 9535eb61944SSandy Huang 9545eb61944SSandy Huang return 0; 9555eb61944SSandy Huang #endif 9565eb61944SSandy Huang } 9575eb61944SSandy Huang 958186f8572SMark Yao static int load_bmp_logo(struct logo_info *logo, const char *bmp_name) 959186f8572SMark Yao { 9604b8c2ef1SMark Yao #ifdef CONFIG_ROCKCHIP_RESOURCE_IMAGE 961186f8572SMark Yao struct rockchip_logo_cache *logo_cache; 962186f8572SMark Yao struct bmp_header *header; 963186f8572SMark Yao void *dst = NULL, *pdst; 9644b8c2ef1SMark Yao int size, len; 9654b8c2ef1SMark Yao int ret = 0; 966186f8572SMark Yao 967186f8572SMark Yao if (!logo || !bmp_name) 968186f8572SMark Yao return -EINVAL; 969186f8572SMark Yao logo_cache = find_or_alloc_logo_cache(bmp_name); 970186f8572SMark Yao if (!logo_cache) 971186f8572SMark Yao return -ENOMEM; 972186f8572SMark Yao 973186f8572SMark Yao if (logo_cache->logo.mem) { 974186f8572SMark Yao memcpy(logo, &logo_cache->logo, sizeof(*logo)); 975186f8572SMark Yao return 0; 976186f8572SMark Yao } 977186f8572SMark Yao 9784b8c2ef1SMark Yao header = malloc(RK_BLK_SIZE); 979186f8572SMark Yao if (!header) 9804b8c2ef1SMark Yao return -ENOMEM; 9814b8c2ef1SMark Yao 9824b8c2ef1SMark Yao len = rockchip_read_resource_file(header, bmp_name, 0, RK_BLK_SIZE); 9834b8c2ef1SMark Yao if (len != RK_BLK_SIZE) { 9844b8c2ef1SMark Yao ret = -EINVAL; 9854b8c2ef1SMark Yao goto free_header; 9864b8c2ef1SMark Yao } 987186f8572SMark Yao 988186f8572SMark Yao logo->bpp = get_unaligned_le16(&header->bit_count); 989186f8572SMark Yao logo->width = get_unaligned_le32(&header->width); 990186f8572SMark Yao logo->height = get_unaligned_le32(&header->height); 991186f8572SMark Yao size = get_unaligned_le32(&header->file_size); 992186f8572SMark Yao if (!can_direct_logo(logo->bpp)) { 993186f8572SMark Yao if (size > MEMORY_POOL_SIZE) { 994186f8572SMark Yao printf("failed to use boot buf as temp bmp buffer\n"); 9954b8c2ef1SMark Yao ret = -ENOMEM; 9964b8c2ef1SMark Yao goto free_header; 997186f8572SMark Yao } 9984b8c2ef1SMark Yao pdst = get_display_buffer(size); 999186f8572SMark Yao 1000186f8572SMark Yao } else { 1001186f8572SMark Yao pdst = get_display_buffer(size); 1002186f8572SMark Yao dst = pdst; 1003186f8572SMark Yao } 1004186f8572SMark Yao 10054b8c2ef1SMark Yao len = rockchip_read_resource_file(pdst, bmp_name, 0, size); 10064b8c2ef1SMark Yao if (len != size) { 1007186f8572SMark Yao printf("failed to load bmp %s\n", bmp_name); 10084b8c2ef1SMark Yao ret = -ENOENT; 10094b8c2ef1SMark Yao goto free_header; 1010186f8572SMark Yao } 1011186f8572SMark Yao 1012186f8572SMark Yao if (!can_direct_logo(logo->bpp)) { 1013186f8572SMark Yao int dst_size; 1014186f8572SMark Yao /* 1015186f8572SMark Yao * TODO: force use 16bpp if bpp less than 16; 1016186f8572SMark Yao */ 1017186f8572SMark Yao logo->bpp = (logo->bpp <= 16) ? 16 : logo->bpp; 1018186f8572SMark Yao dst_size = logo->width * logo->height * logo->bpp >> 3; 1019186f8572SMark Yao 1020186f8572SMark Yao dst = get_display_buffer(dst_size); 10214b8c2ef1SMark Yao if (!dst) { 10224b8c2ef1SMark Yao ret = -ENOMEM; 10234b8c2ef1SMark Yao goto free_header; 10244b8c2ef1SMark Yao } 1025186f8572SMark Yao if (bmpdecoder(pdst, dst, logo->bpp)) { 1026186f8572SMark Yao printf("failed to decode bmp %s\n", bmp_name); 10274b8c2ef1SMark Yao ret = -EINVAL; 10284b8c2ef1SMark Yao goto free_header; 1029186f8572SMark Yao } 10304b8c2ef1SMark Yao flush_dcache_range((ulong)dst, 10314b8c2ef1SMark Yao ALIGN((ulong)dst + dst_size, 10324b8c2ef1SMark Yao CONFIG_SYS_CACHELINE_SIZE)); 10334b8c2ef1SMark Yao 1034186f8572SMark Yao logo->offset = 0; 1035186f8572SMark Yao logo->ymirror = 0; 1036186f8572SMark Yao } else { 1037186f8572SMark Yao logo->offset = get_unaligned_le32(&header->data_offset); 1038186f8572SMark Yao logo->ymirror = 1; 1039186f8572SMark Yao } 10404b8c2ef1SMark Yao logo->mem = dst; 1041186f8572SMark Yao 1042186f8572SMark Yao memcpy(&logo_cache->logo, logo, sizeof(*logo)); 1043186f8572SMark Yao 10444b8c2ef1SMark Yao free_header: 10454b8c2ef1SMark Yao 10464b8c2ef1SMark Yao free(header); 10474b8c2ef1SMark Yao 10484b8c2ef1SMark Yao return ret; 10494b8c2ef1SMark Yao #else 10504b8c2ef1SMark Yao return -EINVAL; 10514b8c2ef1SMark Yao #endif 1052186f8572SMark Yao } 1053186f8572SMark Yao 1054186f8572SMark Yao void rockchip_show_fbbase(ulong fbbase) 1055186f8572SMark Yao { 1056186f8572SMark Yao struct display_state *s; 1057186f8572SMark Yao 1058186f8572SMark Yao list_for_each_entry(s, &rockchip_display_list, head) { 1059186f8572SMark Yao s->logo.mode = ROCKCHIP_DISPLAY_FULLSCREEN; 10604b8c2ef1SMark Yao s->logo.mem = (char *)fbbase; 1061186f8572SMark Yao s->logo.width = DRM_ROCKCHIP_FB_WIDTH; 1062186f8572SMark Yao s->logo.height = DRM_ROCKCHIP_FB_HEIGHT; 1063186f8572SMark Yao s->logo.bpp = 32; 1064186f8572SMark Yao s->logo.ymirror = 0; 1065186f8572SMark Yao 1066186f8572SMark Yao display_logo(s); 1067186f8572SMark Yao } 1068186f8572SMark Yao } 1069186f8572SMark Yao 1070186f8572SMark Yao void rockchip_show_bmp(const char *bmp) 1071186f8572SMark Yao { 1072186f8572SMark Yao struct display_state *s; 1073186f8572SMark Yao 1074186f8572SMark Yao if (!bmp) { 1075186f8572SMark Yao list_for_each_entry(s, &rockchip_display_list, head) 1076186f8572SMark Yao display_disable(s); 1077186f8572SMark Yao return; 1078186f8572SMark Yao } 1079186f8572SMark Yao 1080186f8572SMark Yao list_for_each_entry(s, &rockchip_display_list, head) { 1081186f8572SMark Yao s->logo.mode = s->charge_logo_mode; 1082186f8572SMark Yao if (load_bmp_logo(&s->logo, bmp)) 1083186f8572SMark Yao continue; 1084186f8572SMark Yao display_logo(s); 1085186f8572SMark Yao } 1086186f8572SMark Yao } 1087186f8572SMark Yao 1088186f8572SMark Yao void rockchip_show_logo(void) 1089186f8572SMark Yao { 1090186f8572SMark Yao struct display_state *s; 1091186f8572SMark Yao 1092186f8572SMark Yao list_for_each_entry(s, &rockchip_display_list, head) { 1093186f8572SMark Yao s->logo.mode = s->logo_mode; 1094186f8572SMark Yao if (load_bmp_logo(&s->logo, s->ulogo_name)) 1095186f8572SMark Yao printf("failed to display uboot logo\n"); 1096186f8572SMark Yao else 1097186f8572SMark Yao display_logo(s); 10985eb61944SSandy Huang 10995eb61944SSandy Huang /* Load kernel bmp in rockchip_display_fixup() later */ 1100186f8572SMark Yao } 1101186f8572SMark Yao } 1102186f8572SMark Yao 1103186f8572SMark Yao static int rockchip_display_probe(struct udevice *dev) 1104186f8572SMark Yao { 1105186f8572SMark Yao struct video_priv *uc_priv = dev_get_uclass_priv(dev); 1106186f8572SMark Yao struct video_uc_platdata *plat = dev_get_uclass_platdata(dev); 1107186f8572SMark Yao const void *blob = gd->fdt_blob; 1108e2bce6e4SKever Yang int phandle; 1109186f8572SMark Yao struct udevice *crtc_dev, *conn_dev; 1110186f8572SMark Yao const struct rockchip_crtc *crtc; 1111186f8572SMark Yao const struct rockchip_connector *conn; 1112186f8572SMark Yao struct display_state *s; 1113186f8572SMark Yao const char *name; 1114186f8572SMark Yao int ret; 1115e2bce6e4SKever Yang ofnode node, route_node; 1116e2bce6e4SKever Yang struct device_node *port_node, *vop_node, *ep_node; 1117e2bce6e4SKever Yang struct device_node *cnt_node, *p; 1118186f8572SMark Yao 1119186f8572SMark Yao /* Before relocation we don't need to do anything */ 1120186f8572SMark Yao if (!(gd->flags & GD_FLG_RELOC)) 1121186f8572SMark Yao return 0; 11224b8c2ef1SMark Yao init_display_buffer(plat->base); 1123186f8572SMark Yao 1124e2bce6e4SKever Yang route_node = dev_read_subnode(dev, "route"); 1125e2bce6e4SKever Yang if (!ofnode_valid(route_node)) 1126e2bce6e4SKever Yang return -ENODEV; 1127186f8572SMark Yao 1128e2bce6e4SKever Yang ofnode_for_each_subnode(node, route_node) { 11291e44acfcSWyon Bi if (!ofnode_is_available(node)) 11301e44acfcSWyon Bi continue; 1131e2bce6e4SKever Yang phandle = ofnode_read_u32_default(node, "connect", -1); 1132186f8572SMark Yao if (phandle < 0) { 1133e2bce6e4SKever Yang printf("Warn: can't find connect node's handle\n"); 1134186f8572SMark Yao continue; 1135186f8572SMark Yao } 1136e2bce6e4SKever Yang ep_node = of_find_node_by_phandle(phandle); 1137e2bce6e4SKever Yang if (!ofnode_valid(np_to_ofnode(ep_node))) { 1138e2bce6e4SKever Yang printf("Warn: can't find endpoint node from phandle\n"); 1139186f8572SMark Yao continue; 1140186f8572SMark Yao } 1141e2bce6e4SKever Yang port_node = of_get_parent(ep_node); 1142e2bce6e4SKever Yang if (!ofnode_valid(np_to_ofnode(port_node))) { 1143e2bce6e4SKever Yang printf("Warn: can't find port node from phandle\n"); 1144186f8572SMark Yao continue; 1145186f8572SMark Yao } 1146e2bce6e4SKever Yang vop_node = of_get_parent(port_node); 1147e2bce6e4SKever Yang if (!ofnode_valid(np_to_ofnode(vop_node))) { 1148e2bce6e4SKever Yang printf("Warn: can't find crtc node from phandle\n"); 1149e2bce6e4SKever Yang continue; 1150e2bce6e4SKever Yang } 1151e2bce6e4SKever Yang ret = uclass_get_device_by_ofnode(UCLASS_VIDEO_CRTC, 1152e2bce6e4SKever Yang np_to_ofnode(vop_node), 1153e2bce6e4SKever Yang &crtc_dev); 1154186f8572SMark Yao if (ret) { 1155335adcb5SKever Yang printf("Warn: can't find crtc driver %d\n", ret); 1156186f8572SMark Yao continue; 1157186f8572SMark Yao } 1158186f8572SMark Yao crtc = (const struct rockchip_crtc *)dev_get_driver_data(crtc_dev); 1159186f8572SMark Yao 1160e2bce6e4SKever Yang phandle = ofnode_read_u32_default(np_to_ofnode(ep_node), 1161e2bce6e4SKever Yang "remote-endpoint", -1); 1162e2bce6e4SKever Yang cnt_node = of_find_node_by_phandle(phandle); 1163e2bce6e4SKever Yang if (phandle < 0) { 1164e2bce6e4SKever Yang printf("Warn: can't find remote-endpoint's handle\n"); 1165186f8572SMark Yao continue; 1166186f8572SMark Yao } 1167e2bce6e4SKever Yang while (cnt_node->parent){ 1168e2bce6e4SKever Yang p = of_get_parent(cnt_node); 1169e2bce6e4SKever Yang if (!strcmp(p->full_name, "/")) 1170e2bce6e4SKever Yang break; 1171e2bce6e4SKever Yang cnt_node = p; 1172e2bce6e4SKever Yang } 1173e2bce6e4SKever Yang if (!of_device_is_available(cnt_node)) 1174e2bce6e4SKever Yang continue; 1175e2bce6e4SKever Yang ret = uclass_get_device_by_ofnode(UCLASS_DISPLAY, 1176e2bce6e4SKever Yang np_to_ofnode(cnt_node), 1177e2bce6e4SKever Yang &conn_dev); 1178186f8572SMark Yao if (ret) { 1179e2bce6e4SKever Yang printf("Warn: can't find connect driver\n"); 1180186f8572SMark Yao continue; 1181186f8572SMark Yao } 1182186f8572SMark Yao conn = (const struct rockchip_connector *)dev_get_driver_data(conn_dev); 1183186f8572SMark Yao 1184186f8572SMark Yao s = malloc(sizeof(*s)); 1185186f8572SMark Yao if (!s) 11864b8c2ef1SMark Yao continue; 1187186f8572SMark Yao 1188186f8572SMark Yao memset(s, 0, sizeof(*s)); 1189186f8572SMark Yao 1190186f8572SMark Yao INIT_LIST_HEAD(&s->head); 1191e2bce6e4SKever Yang ret = ofnode_read_string_index(node, "logo,uboot", 0, &s->ulogo_name); 1192e2bce6e4SKever Yang ret = ofnode_read_string_index(node, "logo,kernel", 0, &s->klogo_name); 1193e2bce6e4SKever Yang ret = ofnode_read_string_index(node, "logo,mode", 0, &name); 1194186f8572SMark Yao if (!strcmp(name, "fullscreen")) 1195186f8572SMark Yao s->logo_mode = ROCKCHIP_DISPLAY_FULLSCREEN; 1196186f8572SMark Yao else 1197186f8572SMark Yao s->logo_mode = ROCKCHIP_DISPLAY_CENTER; 1198e2bce6e4SKever Yang ret = ofnode_read_string_index(node, "charge_logo,mode", 0, &name); 1199186f8572SMark Yao if (!strcmp(name, "fullscreen")) 1200186f8572SMark Yao s->charge_logo_mode = ROCKCHIP_DISPLAY_FULLSCREEN; 1201186f8572SMark Yao else 1202186f8572SMark Yao s->charge_logo_mode = ROCKCHIP_DISPLAY_CENTER; 1203186f8572SMark Yao 1204186f8572SMark Yao s->blob = blob; 1205e2bce6e4SKever Yang s->conn_state.node = np_to_ofnode(cnt_node); 1206186f8572SMark Yao s->conn_state.dev = conn_dev; 1207186f8572SMark Yao s->conn_state.connector = conn; 12088a2a3a29SSandy Huang s->conn_state.overscan.left_margin = 100; 12098a2a3a29SSandy Huang s->conn_state.overscan.right_margin = 100; 12108a2a3a29SSandy Huang s->conn_state.overscan.top_margin = 100; 12118a2a3a29SSandy Huang s->conn_state.overscan.bottom_margin = 100; 1212e2bce6e4SKever Yang s->crtc_state.node = np_to_ofnode(vop_node); 1213186f8572SMark Yao s->crtc_state.dev = crtc_dev; 1214186f8572SMark Yao s->crtc_state.crtc = crtc; 1215e2bce6e4SKever Yang s->crtc_state.crtc_id = get_crtc_id(np_to_ofnode(ep_node)); 1216e2bce6e4SKever Yang s->node = node; 121767b9012cSSandy Huang get_crtc_mcu_mode(&s->crtc_state); 1218186f8572SMark Yao 1219e2bce6e4SKever Yang if (connector_panel_init(s)) { 1220e2bce6e4SKever Yang printf("Warn: Failed to init panel drivers\n"); 12214b8c2ef1SMark Yao free(s); 12224b8c2ef1SMark Yao continue; 12234b8c2ef1SMark Yao } 12244b8c2ef1SMark Yao 1225e2bce6e4SKever Yang if (connector_phy_init(s)) { 1226e2bce6e4SKever Yang printf("Warn: Failed to init phy drivers\n"); 12274b8c2ef1SMark Yao free(s); 12284b8c2ef1SMark Yao continue; 12294b8c2ef1SMark Yao } 1230186f8572SMark Yao list_add_tail(&s->head, &rockchip_display_list); 1231186f8572SMark Yao } 1232186f8572SMark Yao 12334b8c2ef1SMark Yao if (list_empty(&rockchip_display_list)) { 12344b8c2ef1SMark Yao printf("Failed to found available display route\n"); 12354b8c2ef1SMark Yao return -ENODEV; 12364b8c2ef1SMark Yao } 12374b8c2ef1SMark Yao 1238186f8572SMark Yao uc_priv->xsize = DRM_ROCKCHIP_FB_WIDTH; 1239186f8572SMark Yao uc_priv->ysize = DRM_ROCKCHIP_FB_HEIGHT; 1240186f8572SMark Yao uc_priv->bpix = VIDEO_BPP32; 1241186f8572SMark Yao 12424b8c2ef1SMark Yao #ifdef CONFIG_DRM_ROCKCHIP_VIDEO_FRAMEBUFFER 1243186f8572SMark Yao rockchip_show_fbbase(plat->base); 1244186f8572SMark Yao video_set_flush_dcache(dev, true); 12454b8c2ef1SMark Yao #endif 1246186f8572SMark Yao 1247186f8572SMark Yao return 0; 12484b8c2ef1SMark Yao } 1249186f8572SMark Yao 1250186f8572SMark Yao void rockchip_display_fixup(void *blob) 1251186f8572SMark Yao { 1252186f8572SMark Yao const struct rockchip_connector_funcs *conn_funcs; 1253186f8572SMark Yao const struct rockchip_crtc_funcs *crtc_funcs; 1254186f8572SMark Yao const struct rockchip_connector *conn; 1255186f8572SMark Yao const struct rockchip_crtc *crtc; 1256186f8572SMark Yao struct display_state *s; 1257694afdc8SKever Yang int offset; 125851619d03SKever Yang const struct device_node *np; 125951619d03SKever Yang const char *path; 1260186f8572SMark Yao 1261186f8572SMark Yao if (!get_display_size()) 1262186f8572SMark Yao return; 1263186f8572SMark Yao 12645eb61944SSandy Huang if (fdt_node_offset_by_compatible(blob, 0, "rockchip,drm-logo") >= 0) { 12655eb61944SSandy Huang list_for_each_entry(s, &rockchip_display_list, head) 12665eb61944SSandy Huang load_bmp_logo(&s->logo, s->klogo_name); 126751619d03SKever Yang offset = fdt_update_reserved_memory(blob, "rockchip,drm-logo", 1268186f8572SMark Yao (u64)memory_start, 1269186f8572SMark Yao (u64)get_display_size()); 12705eb61944SSandy Huang if (offset < 0) 12715eb61944SSandy Huang printf("failed to reserve drm-loader-logo memory\n"); 12725eb61944SSandy Huang } else { 12735eb61944SSandy Huang printf("can't found rockchip,drm-logo, use rockchip,fb-logo\n"); 1274694afdc8SKever Yang /* Compatible with rkfb display, only need reserve memory */ 1275694afdc8SKever Yang offset = fdt_update_reserved_memory(blob, "rockchip,fb-logo", 1276694afdc8SKever Yang (u64)memory_start, 12775eb61944SSandy Huang MEMORY_POOL_SIZE); 1278694afdc8SKever Yang if (offset < 0) 12795eb61944SSandy Huang printf("failed to reserve fb-loader-logo memory\n"); 12805eb61944SSandy Huang else 12815eb61944SSandy Huang list_for_each_entry(s, &rockchip_display_list, head) 12825eb61944SSandy Huang load_kernel_bmp_logo(&s->logo, s->klogo_name); 1283186f8572SMark Yao return; 1284186f8572SMark Yao } 1285186f8572SMark Yao 1286186f8572SMark Yao list_for_each_entry(s, &rockchip_display_list, head) { 1287186f8572SMark Yao conn = s->conn_state.connector; 1288186f8572SMark Yao if (!conn) 1289186f8572SMark Yao continue; 1290186f8572SMark Yao conn_funcs = conn->funcs; 1291186f8572SMark Yao if (!conn_funcs) { 1292186f8572SMark Yao printf("failed to get exist connector\n"); 1293186f8572SMark Yao continue; 1294186f8572SMark Yao } 1295186f8572SMark Yao 1296186f8572SMark Yao crtc = s->crtc_state.crtc; 1297186f8572SMark Yao if (!crtc) 1298186f8572SMark Yao continue; 1299186f8572SMark Yao 1300186f8572SMark Yao crtc_funcs = crtc->funcs; 1301186f8572SMark Yao if (!crtc_funcs) { 1302186f8572SMark Yao printf("failed to get exist crtc\n"); 1303186f8572SMark Yao continue; 1304186f8572SMark Yao } 1305186f8572SMark Yao 1306186f8572SMark Yao if (crtc_funcs->fixup_dts) 1307186f8572SMark Yao crtc_funcs->fixup_dts(s, blob); 1308186f8572SMark Yao 1309186f8572SMark Yao if (conn_funcs->fixup_dts) 1310186f8572SMark Yao conn_funcs->fixup_dts(s, blob); 1311186f8572SMark Yao 131251619d03SKever Yang np = ofnode_to_np(s->node); 131351619d03SKever Yang path = np->full_name; 131451619d03SKever Yang fdt_increase_size(blob, 0x400); 1315186f8572SMark Yao #define FDT_SET_U32(name, val) \ 1316186f8572SMark Yao do_fixup_by_path_u32(blob, path, name, val, 1); 1317186f8572SMark Yao 131851619d03SKever Yang offset = s->logo.offset + (u32)(unsigned long)s->logo.mem 131951619d03SKever Yang - memory_start; 1320186f8572SMark Yao FDT_SET_U32("logo,offset", offset); 1321186f8572SMark Yao FDT_SET_U32("logo,width", s->logo.width); 1322186f8572SMark Yao FDT_SET_U32("logo,height", s->logo.height); 1323186f8572SMark Yao FDT_SET_U32("logo,bpp", s->logo.bpp); 1324186f8572SMark Yao FDT_SET_U32("logo,ymirror", s->logo.ymirror); 1325186f8572SMark Yao FDT_SET_U32("video,hdisplay", s->conn_state.mode.hdisplay); 1326186f8572SMark Yao FDT_SET_U32("video,vdisplay", s->conn_state.mode.vdisplay); 1327f11b858fSSandy Huang FDT_SET_U32("video,crtc_hsync_end", s->conn_state.mode.crtc_hsync_end); 1328f11b858fSSandy Huang FDT_SET_U32("video,crtc_vsync_end", s->conn_state.mode.crtc_vsync_end); 1329186f8572SMark Yao FDT_SET_U32("video,vrefresh", 1330186f8572SMark Yao drm_mode_vrefresh(&s->conn_state.mode)); 13318a2a3a29SSandy Huang FDT_SET_U32("video,flags", s->conn_state.mode.flags); 13328a2a3a29SSandy Huang FDT_SET_U32("overscan,left_margin", s->conn_state.overscan.left_margin); 13338a2a3a29SSandy Huang FDT_SET_U32("overscan,right_margin", s->conn_state.overscan.right_margin); 13348a2a3a29SSandy Huang FDT_SET_U32("overscan,top_margin", s->conn_state.overscan.top_margin); 13358a2a3a29SSandy Huang FDT_SET_U32("overscan,bottom_margin", s->conn_state.overscan.bottom_margin); 1336186f8572SMark Yao #undef FDT_SET_U32 1337186f8572SMark Yao } 1338186f8572SMark Yao } 1339186f8572SMark Yao 1340186f8572SMark Yao int rockchip_display_bind(struct udevice *dev) 1341186f8572SMark Yao { 1342186f8572SMark Yao struct video_uc_platdata *plat = dev_get_uclass_platdata(dev); 1343186f8572SMark Yao 13444b8c2ef1SMark Yao plat->size = DRM_ROCKCHIP_FB_SIZE + MEMORY_POOL_SIZE; 1345186f8572SMark Yao 1346186f8572SMark Yao return 0; 1347186f8572SMark Yao } 1348186f8572SMark Yao 1349186f8572SMark Yao static const struct udevice_id rockchip_display_ids[] = { 1350186f8572SMark Yao { .compatible = "rockchip,display-subsystem" }, 1351186f8572SMark Yao { } 1352186f8572SMark Yao }; 1353186f8572SMark Yao 1354186f8572SMark Yao U_BOOT_DRIVER(rockchip_display) = { 1355186f8572SMark Yao .name = "rockchip_display", 1356186f8572SMark Yao .id = UCLASS_VIDEO, 1357186f8572SMark Yao .of_match = rockchip_display_ids, 1358186f8572SMark Yao .bind = rockchip_display_bind, 1359186f8572SMark Yao .probe = rockchip_display_probe, 1360186f8572SMark Yao }; 13614b8c2ef1SMark Yao 13624b8c2ef1SMark Yao static int do_rockchip_logo_show(cmd_tbl_t *cmdtp, int flag, int argc, 13634b8c2ef1SMark Yao char *const argv[]) 13644b8c2ef1SMark Yao { 13654b8c2ef1SMark Yao if (argc != 1) 13664b8c2ef1SMark Yao return CMD_RET_USAGE; 13674b8c2ef1SMark Yao 13684b8c2ef1SMark Yao rockchip_show_logo(); 13694b8c2ef1SMark Yao 13704b8c2ef1SMark Yao return 0; 13714b8c2ef1SMark Yao } 13724b8c2ef1SMark Yao 13734b8c2ef1SMark Yao static int do_rockchip_show_bmp(cmd_tbl_t *cmdtp, int flag, int argc, 13744b8c2ef1SMark Yao char *const argv[]) 13754b8c2ef1SMark Yao { 13764b8c2ef1SMark Yao if (argc != 2) 13774b8c2ef1SMark Yao return CMD_RET_USAGE; 13784b8c2ef1SMark Yao 13794b8c2ef1SMark Yao rockchip_show_bmp(argv[1]); 13804b8c2ef1SMark Yao 13814b8c2ef1SMark Yao return 0; 13824b8c2ef1SMark Yao } 13834b8c2ef1SMark Yao 13844b8c2ef1SMark Yao U_BOOT_CMD( 13854b8c2ef1SMark Yao rockchip_show_logo, 1, 1, do_rockchip_logo_show, 13864b8c2ef1SMark Yao "load and display log from resource partition", 13874b8c2ef1SMark Yao NULL 13884b8c2ef1SMark Yao ); 13894b8c2ef1SMark Yao 13904b8c2ef1SMark Yao U_BOOT_CMD( 13914b8c2ef1SMark Yao rockchip_show_bmp, 2, 1, do_rockchip_show_bmp, 13924b8c2ef1SMark Yao "load and display bmp from resource partition", 13934b8c2ef1SMark Yao " <bmp_name>" 13944b8c2ef1SMark Yao ); 1395