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> 850a9508eSSandy Huang #include <boot_rkimg.h> 9186f8572SMark Yao #include <config.h> 10186f8572SMark Yao #include <common.h> 11186f8572SMark Yao #include <errno.h> 120e00a84cSMasahiro Yamada #include <linux/libfdt.h> 13186f8572SMark Yao #include <fdtdec.h> 14186f8572SMark Yao #include <fdt_support.h> 158e2bab3fSAlgea Cao #include <linux/hdmi.h> 16*d00abaefSWenping Zhang #include <linux/log2.h> 17186f8572SMark Yao #include <linux/list.h> 18186f8572SMark Yao #include <linux/compat.h> 19186f8572SMark Yao #include <linux/media-bus-format.h> 20186f8572SMark Yao #include <malloc.h> 21967b0f99SYifeng Zhao #include <memalign.h> 22186f8572SMark Yao #include <video.h> 23f8a3e587SJoseph Chen #include <video_rockchip.h> 241a8d717cSWyon Bi #include <video_bridge.h> 25186f8572SMark Yao #include <dm/device.h> 26186f8572SMark Yao #include <dm/uclass-internal.h> 274b8c2ef1SMark Yao #include <asm/arch-rockchip/resource_img.h> 280fda4887SDamon Ding #include <asm/arch-rockchip/cpu.h> 29186f8572SMark Yao 30186f8572SMark Yao #include "bmp_helper.h" 317a71aa91SDamon Ding #include "libnsbmp.h" 32186f8572SMark Yao #include "rockchip_display.h" 33186f8572SMark Yao #include "rockchip_crtc.h" 34186f8572SMark Yao #include "rockchip_connector.h" 351a8d717cSWyon Bi #include "rockchip_bridge.h" 36186f8572SMark Yao #include "rockchip_phy.h" 37186f8572SMark Yao #include "rockchip_panel.h" 38e2bce6e4SKever Yang #include <dm.h> 39e2bce6e4SKever Yang #include <dm/of_access.h> 40e2bce6e4SKever Yang #include <dm/ofnode.h> 4120618a45SGuochun Huang #include <asm/io.h> 42186f8572SMark Yao 431e4c51caSSandy Huang #define DRIVER_VERSION "v1.0.1" 44e559407dSSandy Huang 45e559407dSSandy Huang /*********************************************************************** 46e559407dSSandy Huang * Rockchip UBOOT DRM driver version 47e559407dSSandy Huang * 48e559407dSSandy Huang * v1.0.0 : add basic version for rockchip drm driver(hjc) 491e4c51caSSandy Huang * v1.0.1 : add much dsi update(hjc) 50e559407dSSandy Huang * 51e559407dSSandy Huang **********************************************************************/ 52e559407dSSandy Huang 534b8c2ef1SMark Yao #define RK_BLK_SIZE 512 547e72214dSShixiang Zheng #define BMP_PROCESSED_FLAG 8399 557a71aa91SDamon Ding #define BYTES_PER_PIXEL sizeof(uint32_t) 567a71aa91SDamon Ding #define MAX_IMAGE_BYTES (8 * 1024 * 1024) 574b8c2ef1SMark Yao 58186f8572SMark Yao DECLARE_GLOBAL_DATA_PTR; 59186f8572SMark Yao static LIST_HEAD(rockchip_display_list); 60186f8572SMark Yao static LIST_HEAD(logo_cache_list); 61186f8572SMark Yao 62186f8572SMark Yao static unsigned long memory_start; 636414e3bcSSandy Huang static unsigned long cubic_lut_memory_start; 64186f8572SMark Yao static unsigned long memory_end; 6550a9508eSSandy Huang static struct base2_info base_parameter; 66882f8a2dSSandy Huang static u32 align_size = PAGE_SIZE; 67186f8572SMark Yao 682a48727aSAlgea Cao /* 692a48727aSAlgea Cao * the phy types are used by different connectors in public. 702a48727aSAlgea Cao * The current version only has inno hdmi phy for hdmi and tve. 712a48727aSAlgea Cao */ 722a48727aSAlgea Cao enum public_use_phy { 732a48727aSAlgea Cao NONE, 742a48727aSAlgea Cao INNO_HDMI_PHY 752a48727aSAlgea Cao }; 762a48727aSAlgea Cao 772a48727aSAlgea Cao /* save public phy data */ 782a48727aSAlgea Cao struct public_phy_data { 792a48727aSAlgea Cao const struct rockchip_phy *phy_drv; 802a48727aSAlgea Cao int phy_node; 812a48727aSAlgea Cao int public_phy_type; 822a48727aSAlgea Cao bool phy_init; 832a48727aSAlgea Cao }; 842a48727aSAlgea Cao 852264c88bSDamon Ding char* rockchip_get_output_if_name(u32 output_if, char *name) 862264c88bSDamon Ding { 872264c88bSDamon Ding if (output_if & VOP_OUTPUT_IF_RGB) 882264c88bSDamon Ding strcat(name, " RGB"); 892264c88bSDamon Ding if (output_if & VOP_OUTPUT_IF_BT1120) 902264c88bSDamon Ding strcat(name, " BT1120"); 912264c88bSDamon Ding if (output_if & VOP_OUTPUT_IF_BT656) 922264c88bSDamon Ding strcat(name, " BT656"); 932264c88bSDamon Ding if (output_if & VOP_OUTPUT_IF_LVDS0) 942264c88bSDamon Ding strcat(name, " LVDS0"); 952264c88bSDamon Ding if (output_if & VOP_OUTPUT_IF_LVDS1) 962264c88bSDamon Ding strcat(name, " LVDS1"); 972264c88bSDamon Ding if (output_if & VOP_OUTPUT_IF_MIPI0) 982264c88bSDamon Ding strcat(name, " MIPI0"); 992264c88bSDamon Ding if (output_if & VOP_OUTPUT_IF_MIPI1) 1002264c88bSDamon Ding strcat(name, " MIPI1"); 1012264c88bSDamon Ding if (output_if & VOP_OUTPUT_IF_eDP0) 1022264c88bSDamon Ding strcat(name, " eDP0"); 1032264c88bSDamon Ding if (output_if & VOP_OUTPUT_IF_eDP1) 1042264c88bSDamon Ding strcat(name, " eDP1"); 1052264c88bSDamon Ding if (output_if & VOP_OUTPUT_IF_DP0) 1062264c88bSDamon Ding strcat(name, " DP0"); 1072264c88bSDamon Ding if (output_if & VOP_OUTPUT_IF_DP1) 1082264c88bSDamon Ding strcat(name, " DP1"); 1092264c88bSDamon Ding if (output_if & VOP_OUTPUT_IF_HDMI0) 1102264c88bSDamon Ding strcat(name, " HDMI0"); 1112264c88bSDamon Ding if (output_if & VOP_OUTPUT_IF_HDMI1) 1122264c88bSDamon Ding strcat(name, " HDMI1"); 1132264c88bSDamon Ding 1142264c88bSDamon Ding return name; 1152264c88bSDamon Ding } 1162264c88bSDamon Ding 1174d64cedbSDamon Ding u32 rockchip_drm_get_cycles_per_pixel(u32 bus_format) 1184d64cedbSDamon Ding { 1194d64cedbSDamon Ding switch (bus_format) { 1204d64cedbSDamon Ding case MEDIA_BUS_FMT_RGB565_1X16: 1214d64cedbSDamon Ding case MEDIA_BUS_FMT_RGB666_1X18: 1224d64cedbSDamon Ding case MEDIA_BUS_FMT_RGB888_1X24: 1234d64cedbSDamon Ding case MEDIA_BUS_FMT_RGB666_1X24_CPADHI: 1244d64cedbSDamon Ding return 1; 1254d64cedbSDamon Ding case MEDIA_BUS_FMT_RGB565_2X8_LE: 1264d64cedbSDamon Ding case MEDIA_BUS_FMT_BGR565_2X8_LE: 1274d64cedbSDamon Ding return 2; 1284d64cedbSDamon Ding case MEDIA_BUS_FMT_RGB666_3X6: 1294d64cedbSDamon Ding case MEDIA_BUS_FMT_RGB888_3X8: 1304d64cedbSDamon Ding case MEDIA_BUS_FMT_BGR888_3X8: 1314d64cedbSDamon Ding return 3; 1324d64cedbSDamon Ding case MEDIA_BUS_FMT_RGB888_DUMMY_4X8: 1334d64cedbSDamon Ding case MEDIA_BUS_FMT_BGR888_DUMMY_4X8: 1344d64cedbSDamon Ding return 4; 1354d64cedbSDamon Ding default: 1364d64cedbSDamon Ding return 1; 1374d64cedbSDamon Ding } 1384d64cedbSDamon Ding } 1394d64cedbSDamon Ding 14050a9508eSSandy Huang int rockchip_get_baseparameter(void) 14150a9508eSSandy Huang { 14250a9508eSSandy Huang struct blk_desc *dev_desc; 14350a9508eSSandy Huang disk_partition_t part_info; 144967b0f99SYifeng Zhao int block_num; 145967b0f99SYifeng Zhao char *baseparameter_buf; 14650a9508eSSandy Huang int ret = 0; 14750a9508eSSandy Huang 14850a9508eSSandy Huang dev_desc = rockchip_get_bootdev(); 14950a9508eSSandy Huang if (!dev_desc) { 15050a9508eSSandy Huang printf("%s: Could not find device\n", __func__); 15150a9508eSSandy Huang return -ENOENT; 15250a9508eSSandy Huang } 15350a9508eSSandy Huang 15450a9508eSSandy Huang if (part_get_info_by_name(dev_desc, "baseparameter", &part_info) < 0) { 15550a9508eSSandy Huang printf("Could not find baseparameter partition\n"); 15650a9508eSSandy Huang return -ENOENT; 15750a9508eSSandy Huang } 15850a9508eSSandy Huang 159967b0f99SYifeng Zhao block_num = BLOCK_CNT(sizeof(base_parameter), dev_desc); 160967b0f99SYifeng Zhao baseparameter_buf = memalign(ARCH_DMA_MINALIGN, block_num * dev_desc->blksz); 161967b0f99SYifeng Zhao if (!baseparameter_buf) { 162967b0f99SYifeng Zhao printf("failed to alloc memory for baseparameter buffer\n"); 163967b0f99SYifeng Zhao return -ENOMEM; 164967b0f99SYifeng Zhao } 165967b0f99SYifeng Zhao 16650a9508eSSandy Huang ret = blk_dread(dev_desc, part_info.start, block_num, (void *)baseparameter_buf); 16750a9508eSSandy Huang if (ret < 0) { 16850a9508eSSandy Huang printf("read baseparameter failed\n"); 169967b0f99SYifeng Zhao goto out; 17050a9508eSSandy Huang } 17150a9508eSSandy Huang 17250a9508eSSandy Huang memcpy(&base_parameter, baseparameter_buf, sizeof(base_parameter)); 17350a9508eSSandy Huang if (strncasecmp(base_parameter.head_flag, "BASP", 4)) { 17450a9508eSSandy Huang printf("warning: bad baseparameter\n"); 17550a9508eSSandy Huang memset(&base_parameter, 0, sizeof(base_parameter)); 17650a9508eSSandy Huang } 17750a9508eSSandy Huang rockchip_display_make_crc32_table(); 17850a9508eSSandy Huang 179967b0f99SYifeng Zhao out: 180967b0f99SYifeng Zhao free(baseparameter_buf); 18150a9508eSSandy Huang return ret; 18250a9508eSSandy Huang } 18350a9508eSSandy Huang 18450a9508eSSandy Huang struct base2_disp_info *rockchip_get_disp_info(int type, int id) 18550a9508eSSandy Huang { 18650a9508eSSandy Huang struct base2_disp_info *disp_info; 18737a2a1a7SSandy Huang struct base2_disp_header *disp_header; 18837a2a1a7SSandy Huang int i = 0, offset = -1; 18950a9508eSSandy Huang u32 crc_val; 1906027c871SZhang Yubing u32 base2_length; 19137a2a1a7SSandy Huang void *base_parameter_addr = (void *)&base_parameter; 19250a9508eSSandy Huang 19350a9508eSSandy Huang for (i = 0; i < 8; i++) { 19437a2a1a7SSandy Huang disp_header = &base_parameter.disp_header[i]; 19537a2a1a7SSandy Huang if (disp_header->connector_type == type && 19637a2a1a7SSandy Huang disp_header->connector_id == id) { 19750a9508eSSandy Huang printf("disp info %d, type:%d, id:%d\n", i, type, id); 19837a2a1a7SSandy Huang offset = disp_header->offset; 19950a9508eSSandy Huang break; 20050a9508eSSandy Huang } 20150a9508eSSandy Huang } 20237a2a1a7SSandy Huang 20337a2a1a7SSandy Huang if (offset < 0) 20437a2a1a7SSandy Huang return NULL; 20537a2a1a7SSandy Huang disp_info = base_parameter_addr + offset; 20637a2a1a7SSandy Huang if (disp_info->screen_info[0].type != type || 20737a2a1a7SSandy Huang disp_info->screen_info[0].id != id) { 208b3e1cbdeSDamon Ding printf("base2_disp_info couldn't be found, screen_info type[%d] or id[%d] mismatched\n", 20937a2a1a7SSandy Huang disp_info->screen_info[0].type, 21037a2a1a7SSandy Huang disp_info->screen_info[0].id); 21137a2a1a7SSandy Huang return NULL; 21237a2a1a7SSandy Huang } 21337a2a1a7SSandy Huang 21450a9508eSSandy Huang if (strncasecmp(disp_info->disp_head_flag, "DISP", 4)) 21550a9508eSSandy Huang return NULL; 21650a9508eSSandy Huang 2176027c871SZhang Yubing if (base_parameter.major_version == 3 && base_parameter.minor_version == 0) { 2186027c871SZhang Yubing crc_val = rockchip_display_crc32c_cal((unsigned char *)disp_info, 2196027c871SZhang Yubing sizeof(struct base2_disp_info) - 4); 2206027c871SZhang Yubing if (crc_val != disp_info->crc2) { 2216027c871SZhang Yubing printf("error: connector type[%d], id[%d] disp info crc2 check error\n", 2226027c871SZhang Yubing type, id); 22350a9508eSSandy Huang return NULL; 22450a9508eSSandy Huang } 2256027c871SZhang Yubing } else { 2266027c871SZhang Yubing base2_length = sizeof(struct base2_disp_info) - sizeof(struct csc_info) - 2276027c871SZhang Yubing sizeof(struct acm_data) - 10 * 1024 - 4; 2286027c871SZhang Yubing crc_val = rockchip_display_crc32c_cal((unsigned char *)disp_info, base2_length - 4); 2296027c871SZhang Yubing if (crc_val != disp_info->crc) { 2306027c871SZhang Yubing printf("error: connector type[%d], id[%d] disp info crc check error\n", 2316027c871SZhang Yubing type, id); 2326027c871SZhang Yubing return NULL; 2336027c871SZhang Yubing } 2346027c871SZhang Yubing } 23550a9508eSSandy Huang 23650a9508eSSandy Huang return disp_info; 23750a9508eSSandy Huang } 23850a9508eSSandy Huang 2392a48727aSAlgea Cao /* check which kind of public phy does connector use */ 2400594ce39SZhang Yubing static int check_public_use_phy(struct rockchip_connector *conn) 2412a48727aSAlgea Cao { 2422a48727aSAlgea Cao int ret = NONE; 2432a48727aSAlgea Cao #ifdef CONFIG_ROCKCHIP_INNO_HDMI_PHY 2442a48727aSAlgea Cao 2450594ce39SZhang Yubing if (!strncmp(dev_read_name(conn->dev), "tve", 3) || 2460594ce39SZhang Yubing !strncmp(dev_read_name(conn->dev), "hdmi", 4)) 2472a48727aSAlgea Cao ret = INNO_HDMI_PHY; 2482a48727aSAlgea Cao #endif 2492a48727aSAlgea Cao 2502a48727aSAlgea Cao return ret; 2512a48727aSAlgea Cao } 2522a48727aSAlgea Cao 2532a48727aSAlgea Cao /* 2542a48727aSAlgea Cao * get public phy driver and initialize it. 2552a48727aSAlgea Cao * The current version only has inno hdmi phy for hdmi and tve. 2562a48727aSAlgea Cao */ 2570594ce39SZhang Yubing static int get_public_phy(struct rockchip_connector *conn, 2582a48727aSAlgea Cao struct public_phy_data *data) 2592a48727aSAlgea Cao { 26015081c50SWyon Bi struct rockchip_phy *phy; 2612a48727aSAlgea Cao struct udevice *dev; 2622a48727aSAlgea Cao int ret = 0; 2632a48727aSAlgea Cao 2642a48727aSAlgea Cao switch (data->public_phy_type) { 2652a48727aSAlgea Cao case INNO_HDMI_PHY: 2662a48727aSAlgea Cao #if defined(CONFIG_ROCKCHIP_RK3328) 26715081c50SWyon Bi ret = uclass_get_device_by_name(UCLASS_PHY, 2682a48727aSAlgea Cao "hdmiphy@ff430000", &dev); 2692a48727aSAlgea Cao #elif defined(CONFIG_ROCKCHIP_RK322X) 27015081c50SWyon Bi ret = uclass_get_device_by_name(UCLASS_PHY, 2712a48727aSAlgea Cao "hdmi-phy@12030000", &dev); 2722a48727aSAlgea Cao #else 2732a48727aSAlgea Cao ret = -EINVAL; 2742a48727aSAlgea Cao #endif 2752a48727aSAlgea Cao if (ret) { 2762a48727aSAlgea Cao printf("Warn: can't find phy driver\n"); 2772a48727aSAlgea Cao return 0; 2782a48727aSAlgea Cao } 2792a48727aSAlgea Cao 28015081c50SWyon Bi phy = (struct rockchip_phy *)dev_get_driver_data(dev); 2812a48727aSAlgea Cao if (!phy) { 2822a48727aSAlgea Cao printf("failed to get phy driver\n"); 2832a48727aSAlgea Cao return 0; 2842a48727aSAlgea Cao } 2852a48727aSAlgea Cao 28615081c50SWyon Bi ret = rockchip_phy_init(phy); 28715081c50SWyon Bi if (ret) { 2882a48727aSAlgea Cao printf("failed to init phy driver\n"); 28915081c50SWyon Bi return ret; 2902a48727aSAlgea Cao } 2910594ce39SZhang Yubing conn->phy = phy; 2922a48727aSAlgea Cao 2934ba1647cSKever Yang debug("inno hdmi phy init success, save it\n"); 2940594ce39SZhang Yubing data->phy_drv = conn->phy; 2952a48727aSAlgea Cao data->phy_init = true; 2962a48727aSAlgea Cao return 0; 2972a48727aSAlgea Cao default: 2982a48727aSAlgea Cao return -EINVAL; 2992a48727aSAlgea Cao } 3002a48727aSAlgea Cao } 3012a48727aSAlgea Cao 3024b8c2ef1SMark Yao static void init_display_buffer(ulong base) 303186f8572SMark Yao { 304882f8a2dSSandy Huang memory_start = ALIGN(base + DRM_ROCKCHIP_FB_SIZE, align_size); 305186f8572SMark Yao memory_end = memory_start; 306882f8a2dSSandy Huang cubic_lut_memory_start = ALIGN(memory_start + MEMORY_POOL_SIZE, align_size); 307186f8572SMark Yao } 308186f8572SMark Yao 30923b55d3dSJoseph Chen void *get_display_buffer(int size) 310186f8572SMark Yao { 311186f8572SMark Yao unsigned long roundup_memory = roundup(memory_end, PAGE_SIZE); 312186f8572SMark Yao void *buf; 313186f8572SMark Yao 314186f8572SMark Yao if (roundup_memory + size > memory_start + MEMORY_POOL_SIZE) { 315186f8572SMark Yao printf("failed to alloc %dbyte memory to display\n", size); 316186f8572SMark Yao return NULL; 317186f8572SMark Yao } 318186f8572SMark Yao buf = (void *)roundup_memory; 319186f8572SMark Yao 320186f8572SMark Yao memory_end = roundup_memory + size; 321186f8572SMark Yao 322186f8572SMark Yao return buf; 323186f8572SMark Yao } 324186f8572SMark Yao 325186f8572SMark Yao static unsigned long get_display_size(void) 326186f8572SMark Yao { 327186f8572SMark Yao return memory_end - memory_start; 328186f8572SMark Yao } 329186f8572SMark Yao 3306414e3bcSSandy Huang static unsigned long get_single_cubic_lut_size(void) 3316414e3bcSSandy Huang { 3326414e3bcSSandy Huang ulong cubic_lut_size; 3336414e3bcSSandy Huang int cubic_lut_step = CONFIG_ROCKCHIP_CUBIC_LUT_SIZE; 3346414e3bcSSandy Huang 3356414e3bcSSandy Huang /* This is depend on IC designed */ 3366414e3bcSSandy Huang cubic_lut_size = (cubic_lut_step * cubic_lut_step * cubic_lut_step + 1) / 2 * 16; 3376414e3bcSSandy Huang cubic_lut_size = roundup(cubic_lut_size, PAGE_SIZE); 3386414e3bcSSandy Huang 3396414e3bcSSandy Huang return cubic_lut_size; 3406414e3bcSSandy Huang } 3416414e3bcSSandy Huang 3426414e3bcSSandy Huang static unsigned long get_cubic_lut_offset(int crtc_id) 3436414e3bcSSandy Huang { 3446414e3bcSSandy Huang return crtc_id * get_single_cubic_lut_size(); 3456414e3bcSSandy Huang } 3466414e3bcSSandy Huang 3476414e3bcSSandy Huang unsigned long get_cubic_lut_buffer(int crtc_id) 3486414e3bcSSandy Huang { 3496414e3bcSSandy Huang return cubic_lut_memory_start + crtc_id * get_single_cubic_lut_size(); 3506414e3bcSSandy Huang } 3516414e3bcSSandy Huang 3526414e3bcSSandy Huang static unsigned long get_cubic_memory_size(void) 3536414e3bcSSandy Huang { 3546414e3bcSSandy Huang /* Max support 4 cubic lut */ 3556414e3bcSSandy Huang return get_single_cubic_lut_size() * 4; 3566414e3bcSSandy Huang } 3576414e3bcSSandy Huang 35823b55d3dSJoseph Chen bool can_direct_logo(int bpp) 359186f8572SMark Yao { 360e54e81e8SDamon Ding return bpp == 16 || bpp == 32; 361186f8572SMark Yao } 362186f8572SMark Yao 3630594ce39SZhang Yubing static int connector_phy_init(struct rockchip_connector *conn, 3642a48727aSAlgea Cao struct public_phy_data *data) 365186f8572SMark Yao { 3667cacd0a8SWyon Bi int type; 367186f8572SMark Yao 3682a48727aSAlgea Cao /* does this connector use public phy with others */ 3690594ce39SZhang Yubing type = check_public_use_phy(conn); 3702a48727aSAlgea Cao if (type == INNO_HDMI_PHY) { 3712a48727aSAlgea Cao /* there is no public phy was initialized */ 3722a48727aSAlgea Cao if (!data->phy_init) { 3734ba1647cSKever Yang debug("start get public phy\n"); 3742a48727aSAlgea Cao data->public_phy_type = type; 3750594ce39SZhang Yubing if (get_public_phy(conn, data)) { 3762a48727aSAlgea Cao printf("can't find correct public phy type\n"); 3772a48727aSAlgea Cao free(data); 3782a48727aSAlgea Cao return -EINVAL; 3792a48727aSAlgea Cao } 3802a48727aSAlgea Cao return 0; 3812a48727aSAlgea Cao } 3822a48727aSAlgea Cao 3832a48727aSAlgea Cao /* if this phy has been initialized, get it directly */ 3840594ce39SZhang Yubing conn->phy = (struct rockchip_phy *)data->phy_drv; 3852a48727aSAlgea Cao return 0; 3862a48727aSAlgea Cao } 3872a48727aSAlgea Cao 388186f8572SMark Yao return 0; 389186f8572SMark Yao } 390186f8572SMark Yao 39113f658dcSDamon Ding int rockchip_ofnode_get_display_mode(ofnode node, struct drm_display_mode *mode, u32 *bus_flags) 392186f8572SMark Yao { 393186f8572SMark Yao int hactive, vactive, pixelclock; 394186f8572SMark Yao int hfront_porch, hback_porch, hsync_len; 395186f8572SMark Yao int vfront_porch, vback_porch, vsync_len; 396186f8572SMark Yao int val, flags = 0; 397186f8572SMark Yao 39813f658dcSDamon Ding #define FDT_GET_BOOL(val, name) \ 39913f658dcSDamon Ding val = ofnode_read_bool(node, name); 40013f658dcSDamon Ding 401186f8572SMark Yao #define FDT_GET_INT(val, name) \ 4022bfb6166SSandy Huang val = ofnode_read_s32_default(node, name, -1); \ 403186f8572SMark Yao if (val < 0) { \ 404186f8572SMark Yao printf("Can't get %s\n", name); \ 405186f8572SMark Yao return -ENXIO; \ 406186f8572SMark Yao } 407186f8572SMark Yao 408ffa55e18SShixiang Zheng #define FDT_GET_INT_DEFAULT(val, name, default) \ 4092bfb6166SSandy Huang val = ofnode_read_s32_default(node, name, default); 410ffa55e18SShixiang Zheng 411186f8572SMark Yao FDT_GET_INT(hactive, "hactive"); 412186f8572SMark Yao FDT_GET_INT(vactive, "vactive"); 413186f8572SMark Yao FDT_GET_INT(pixelclock, "clock-frequency"); 414186f8572SMark Yao FDT_GET_INT(hsync_len, "hsync-len"); 415186f8572SMark Yao FDT_GET_INT(hfront_porch, "hfront-porch"); 416186f8572SMark Yao FDT_GET_INT(hback_porch, "hback-porch"); 417186f8572SMark Yao FDT_GET_INT(vsync_len, "vsync-len"); 418186f8572SMark Yao FDT_GET_INT(vfront_porch, "vfront-porch"); 419186f8572SMark Yao FDT_GET_INT(vback_porch, "vback-porch"); 420186f8572SMark Yao FDT_GET_INT(val, "hsync-active"); 421186f8572SMark Yao flags |= val ? DRM_MODE_FLAG_PHSYNC : DRM_MODE_FLAG_NHSYNC; 422186f8572SMark Yao FDT_GET_INT(val, "vsync-active"); 423186f8572SMark Yao flags |= val ? DRM_MODE_FLAG_PVSYNC : DRM_MODE_FLAG_NVSYNC; 42413f658dcSDamon Ding 42513f658dcSDamon Ding FDT_GET_BOOL(val, "interlaced"); 42613f658dcSDamon Ding flags |= val ? DRM_MODE_FLAG_INTERLACE : 0; 42713f658dcSDamon Ding FDT_GET_BOOL(val, "doublescan"); 42813f658dcSDamon Ding flags |= val ? DRM_MODE_FLAG_DBLSCAN : 0; 42913f658dcSDamon Ding FDT_GET_BOOL(val, "doubleclk"); 430679e3783SDamon Ding flags |= val ? DRM_MODE_FLAG_DBLCLK : 0; 43113f658dcSDamon Ding 43213f658dcSDamon Ding FDT_GET_INT(val, "de-active"); 43313f658dcSDamon Ding *bus_flags |= val ? DRM_BUS_FLAG_DE_HIGH : DRM_BUS_FLAG_DE_LOW; 4343a06149eSSandy Huang FDT_GET_INT(val, "pixelclk-active"); 43513f658dcSDamon Ding *bus_flags |= val ? DRM_BUS_FLAG_PIXDATA_DRIVE_POSEDGE : DRM_BUS_FLAG_PIXDATA_DRIVE_NEGEDGE; 436186f8572SMark Yao 437ffa55e18SShixiang Zheng FDT_GET_INT_DEFAULT(val, "screen-rotate", 0); 438ffa55e18SShixiang Zheng if (val == DRM_MODE_FLAG_XMIRROR) { 439ffa55e18SShixiang Zheng flags |= DRM_MODE_FLAG_XMIRROR; 440ffa55e18SShixiang Zheng } else if (val == DRM_MODE_FLAG_YMIRROR) { 441ffa55e18SShixiang Zheng flags |= DRM_MODE_FLAG_YMIRROR; 442ffa55e18SShixiang Zheng } else if (val == DRM_MODE_FLAG_XYMIRROR) { 443ffa55e18SShixiang Zheng flags |= DRM_MODE_FLAG_XMIRROR; 444ffa55e18SShixiang Zheng flags |= DRM_MODE_FLAG_YMIRROR; 445ffa55e18SShixiang Zheng } 446186f8572SMark Yao mode->hdisplay = hactive; 447186f8572SMark Yao mode->hsync_start = mode->hdisplay + hfront_porch; 448186f8572SMark Yao mode->hsync_end = mode->hsync_start + hsync_len; 449186f8572SMark Yao mode->htotal = mode->hsync_end + hback_porch; 450186f8572SMark Yao 451186f8572SMark Yao mode->vdisplay = vactive; 452186f8572SMark Yao mode->vsync_start = mode->vdisplay + vfront_porch; 453186f8572SMark Yao mode->vsync_end = mode->vsync_start + vsync_len; 454186f8572SMark Yao mode->vtotal = mode->vsync_end + vback_porch; 455186f8572SMark Yao 456186f8572SMark Yao mode->clock = pixelclock / 1000; 457186f8572SMark Yao mode->flags = flags; 458973d9ce8SWyon Bi mode->vrefresh = drm_mode_vrefresh(mode); 459186f8572SMark Yao 460186f8572SMark Yao return 0; 461186f8572SMark Yao } 462186f8572SMark Yao 46313f658dcSDamon Ding static int display_get_force_timing_from_dts(ofnode node, 46413f658dcSDamon Ding struct drm_display_mode *mode, 46513f658dcSDamon Ding u32 *bus_flags) 4662bfb6166SSandy Huang { 4672bfb6166SSandy Huang int ret = 0; 4682bfb6166SSandy Huang 46913f658dcSDamon Ding ret = rockchip_ofnode_get_display_mode(node, mode, bus_flags); 4702bfb6166SSandy Huang 4712bfb6166SSandy Huang if (ret) { 4722bfb6166SSandy Huang mode->clock = 74250; 4732bfb6166SSandy Huang mode->flags = 0x5; 4742bfb6166SSandy Huang mode->hdisplay = 1280; 4752bfb6166SSandy Huang mode->hsync_start = 1390; 4762bfb6166SSandy Huang mode->hsync_end = 1430; 4772bfb6166SSandy Huang mode->htotal = 1650; 4782bfb6166SSandy Huang mode->hskew = 0; 4792bfb6166SSandy Huang mode->vdisplay = 720; 4802bfb6166SSandy Huang mode->vsync_start = 725; 4812bfb6166SSandy Huang mode->vsync_end = 730; 4822bfb6166SSandy Huang mode->vtotal = 750; 4832bfb6166SSandy Huang mode->vrefresh = 60; 4842bfb6166SSandy Huang mode->picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9; 4852bfb6166SSandy Huang mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED; 4862bfb6166SSandy Huang } 4872bfb6166SSandy Huang 4882bfb6166SSandy Huang printf("route node %s force_timing, use %dx%dp%d as default mode\n", 4892bfb6166SSandy Huang ret ? "undefine" : "define", mode->hdisplay, mode->vdisplay, 4902bfb6166SSandy Huang mode->vscan); 4912bfb6166SSandy Huang 4922bfb6166SSandy Huang return 0; 4932bfb6166SSandy Huang } 4942bfb6166SSandy Huang 4950594ce39SZhang Yubing static int display_get_timing_from_dts(struct rockchip_panel *panel, 49613f658dcSDamon Ding struct drm_display_mode *mode, 49713f658dcSDamon Ding u32 *bus_flags) 4982bfb6166SSandy Huang { 49972a8959eSWyon Bi struct ofnode_phandle_args args; 50033c521a7SDamon Ding ofnode dt, timing, mcu_panel; 50172a8959eSWyon Bi int ret; 5022bfb6166SSandy Huang 50333c521a7SDamon Ding mcu_panel = dev_read_subnode(panel->dev, "mcu-panel"); 50472a8959eSWyon Bi dt = dev_read_subnode(panel->dev, "display-timings"); 50572a8959eSWyon Bi if (ofnode_valid(dt)) { 50672a8959eSWyon Bi ret = ofnode_parse_phandle_with_args(dt, "native-mode", NULL, 50772a8959eSWyon Bi 0, 0, &args); 50872a8959eSWyon Bi if (ret) 50972a8959eSWyon Bi return ret; 5102bfb6166SSandy Huang 51172a8959eSWyon Bi timing = args.node; 51233c521a7SDamon Ding } else if (ofnode_valid(mcu_panel)) { 51333c521a7SDamon Ding dt = ofnode_find_subnode(mcu_panel, "display-timings"); 51433c521a7SDamon Ding ret = ofnode_parse_phandle_with_args(dt, "native-mode", NULL, 51533c521a7SDamon Ding 0, 0, &args); 51633c521a7SDamon Ding if (ret) 51733c521a7SDamon Ding return ret; 51833c521a7SDamon Ding 51933c521a7SDamon Ding timing = args.node; 52072a8959eSWyon Bi } else { 52172a8959eSWyon Bi timing = dev_read_subnode(panel->dev, "panel-timing"); 52272a8959eSWyon Bi } 52372a8959eSWyon Bi 52472a8959eSWyon Bi if (!ofnode_valid(timing)) { 5252bfb6166SSandy Huang printf("failed to get display timings from DT\n"); 5262bfb6166SSandy Huang return -ENXIO; 5272bfb6166SSandy Huang } 5282bfb6166SSandy Huang 52913f658dcSDamon Ding rockchip_ofnode_get_display_mode(timing, mode, bus_flags); 5302bfb6166SSandy Huang 5312bfb6166SSandy Huang return 0; 5322bfb6166SSandy Huang } 5332bfb6166SSandy Huang 534186f8572SMark Yao static int display_get_timing(struct display_state *state) 535186f8572SMark Yao { 536186f8572SMark Yao struct connector_state *conn_state = &state->conn_state; 537186f8572SMark Yao struct drm_display_mode *mode = &conn_state->mode; 538186f8572SMark Yao const struct drm_display_mode *m; 5390594ce39SZhang Yubing struct rockchip_panel *panel = conn_state->connector->panel; 540186f8572SMark Yao 541b752fb5dSWyon Bi if (panel->funcs->get_mode) 542b752fb5dSWyon Bi return panel->funcs->get_mode(panel, mode); 543b752fb5dSWyon Bi 5441a8d717cSWyon Bi if (dev_of_valid(panel->dev) && 54513f658dcSDamon Ding !display_get_timing_from_dts(panel, mode, &conn_state->bus_flags)) { 546186f8572SMark Yao printf("Using display timing dts\n"); 54734d0c224SWyon Bi return 0; 548186f8572SMark Yao } 549186f8572SMark Yao 550c493d00eSWyon Bi if (panel->data) { 551c493d00eSWyon Bi m = (const struct drm_display_mode *)panel->data; 552186f8572SMark Yao memcpy(mode, m, sizeof(*m)); 553c493d00eSWyon Bi printf("Using display timing from compatible panel driver\n"); 55434d0c224SWyon Bi return 0; 555186f8572SMark Yao } 556186f8572SMark Yao 557186f8572SMark Yao return -ENODEV; 558186f8572SMark Yao } 559186f8572SMark Yao 56058c17f51SSandy Huang static int display_pre_init(void) 56158c17f51SSandy Huang { 56258c17f51SSandy Huang struct display_state *state; 56358c17f51SSandy Huang int ret = 0; 56458c17f51SSandy Huang 56558c17f51SSandy Huang list_for_each_entry(state, &rockchip_display_list, head) { 56658c17f51SSandy Huang struct connector_state *conn_state = &state->conn_state; 56758c17f51SSandy Huang struct crtc_state *crtc_state = &state->crtc_state; 56858c17f51SSandy Huang struct rockchip_crtc *crtc = crtc_state->crtc; 56958c17f51SSandy Huang 5700594ce39SZhang Yubing ret = rockchip_connector_pre_init(state); 57158c17f51SSandy Huang if (ret) 57258c17f51SSandy Huang printf("pre init conn error\n"); 5730594ce39SZhang Yubing 57458c17f51SSandy Huang crtc->vps[crtc_state->crtc_id].output_type = conn_state->type; 57558c17f51SSandy Huang } 57658c17f51SSandy Huang return ret; 57758c17f51SSandy Huang } 57858c17f51SSandy Huang 5792bfb6166SSandy Huang static int display_use_force_mode(struct display_state *state) 5802bfb6166SSandy Huang { 5812bfb6166SSandy Huang struct connector_state *conn_state = &state->conn_state; 5822bfb6166SSandy Huang struct drm_display_mode *mode = &conn_state->mode; 5832bfb6166SSandy Huang 5842bfb6166SSandy Huang conn_state->bpc = 8; 5852bfb6166SSandy Huang memcpy(mode, &state->force_mode, sizeof(struct drm_display_mode)); 5862bfb6166SSandy Huang conn_state->bus_format = state->force_bus_format; 5872bfb6166SSandy Huang 5882bfb6166SSandy Huang return 0; 5892bfb6166SSandy Huang } 5902bfb6166SSandy Huang 5912bfb6166SSandy Huang static int display_get_edid_mode(struct display_state *state) 5922bfb6166SSandy Huang { 5932bfb6166SSandy Huang int ret = 0; 5942bfb6166SSandy Huang struct connector_state *conn_state = &state->conn_state; 5952bfb6166SSandy Huang struct drm_display_mode *mode = &conn_state->mode; 5962bfb6166SSandy Huang int bpc; 5972bfb6166SSandy Huang 598cf7d9af3SSandy Huang ret = edid_get_drm_mode(conn_state->edid, sizeof(conn_state->edid), mode, &bpc); 5992bfb6166SSandy Huang if (!ret) { 6002bfb6166SSandy Huang conn_state->bpc = bpc; 6012bfb6166SSandy Huang edid_print_info((void *)&conn_state->edid); 6022bfb6166SSandy Huang } else { 6032bfb6166SSandy Huang conn_state->bpc = 8; 6042bfb6166SSandy Huang mode->clock = 74250; 6052bfb6166SSandy Huang mode->flags = 0x5; 6062bfb6166SSandy Huang mode->hdisplay = 1280; 6072bfb6166SSandy Huang mode->hsync_start = 1390; 6082bfb6166SSandy Huang mode->hsync_end = 1430; 6092bfb6166SSandy Huang mode->htotal = 1650; 6102bfb6166SSandy Huang mode->hskew = 0; 6112bfb6166SSandy Huang mode->vdisplay = 720; 6122bfb6166SSandy Huang mode->vsync_start = 725; 6132bfb6166SSandy Huang mode->vsync_end = 730; 6142bfb6166SSandy Huang mode->vtotal = 750; 6152bfb6166SSandy Huang mode->vrefresh = 60; 6162bfb6166SSandy Huang mode->picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9; 6172bfb6166SSandy Huang mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED; 6182bfb6166SSandy Huang 6190594ce39SZhang Yubing printf("error: %s get mode from edid failed, use 720p60 as default mode\n", 6200594ce39SZhang Yubing state->conn_state.connector->dev->name); 6212bfb6166SSandy Huang } 6222bfb6166SSandy Huang 6232bfb6166SSandy Huang return ret; 6242bfb6166SSandy Huang } 6252bfb6166SSandy Huang 6267929c09fSGuochun Huang static int display_mode_valid(struct display_state *state) 6277929c09fSGuochun Huang { 6287929c09fSGuochun Huang struct connector_state *conn_state = &state->conn_state; 6297929c09fSGuochun Huang struct rockchip_connector *conn = conn_state->connector; 6307929c09fSGuochun Huang const struct rockchip_connector_funcs *conn_funcs = conn->funcs; 6317929c09fSGuochun Huang struct crtc_state *crtc_state = &state->crtc_state; 6327929c09fSGuochun Huang const struct rockchip_crtc *crtc = crtc_state->crtc; 6337929c09fSGuochun Huang const struct rockchip_crtc_funcs *crtc_funcs = crtc->funcs; 6347929c09fSGuochun Huang int ret; 6357929c09fSGuochun Huang 636690e9ed1SSandy Huang if (conn_funcs->mode_valid && state->enabled_at_spl == false) { 6377929c09fSGuochun Huang ret = conn_funcs->mode_valid(conn, state); 6387929c09fSGuochun Huang if (ret) 6397929c09fSGuochun Huang return ret; 6407929c09fSGuochun Huang } 6417929c09fSGuochun Huang 6427929c09fSGuochun Huang if (crtc_funcs->mode_valid) { 6437929c09fSGuochun Huang ret = crtc_funcs->mode_valid(state); 6447929c09fSGuochun Huang if (ret) 6457929c09fSGuochun Huang return ret; 6467929c09fSGuochun Huang } 6477929c09fSGuochun Huang 6487929c09fSGuochun Huang return 0; 6497929c09fSGuochun Huang } 6507929c09fSGuochun Huang 651b02eb70bSDamon Ding static int display_mode_fixup(struct display_state *state) 652b02eb70bSDamon Ding { 653b02eb70bSDamon Ding struct crtc_state *crtc_state = &state->crtc_state; 654b02eb70bSDamon Ding const struct rockchip_crtc *crtc = crtc_state->crtc; 655b02eb70bSDamon Ding const struct rockchip_crtc_funcs *crtc_funcs = crtc->funcs; 656b02eb70bSDamon Ding int ret; 657b02eb70bSDamon Ding 658b02eb70bSDamon Ding if (crtc_funcs->mode_fixup) { 659b02eb70bSDamon Ding ret = crtc_funcs->mode_fixup(state); 660b02eb70bSDamon Ding if (ret) 661b02eb70bSDamon Ding return ret; 662b02eb70bSDamon Ding } 663b02eb70bSDamon Ding 664b02eb70bSDamon Ding return 0; 665b02eb70bSDamon Ding } 666b02eb70bSDamon Ding 667186f8572SMark Yao static int display_init(struct display_state *state) 668186f8572SMark Yao { 669186f8572SMark Yao struct connector_state *conn_state = &state->conn_state; 6700594ce39SZhang Yubing struct rockchip_connector *conn = conn_state->connector; 671186f8572SMark Yao struct crtc_state *crtc_state = &state->crtc_state; 6722a48727aSAlgea Cao struct rockchip_crtc *crtc = crtc_state->crtc; 673186f8572SMark Yao const struct rockchip_crtc_funcs *crtc_funcs = crtc->funcs; 674ccd843b9SSandy Huang struct drm_display_mode *mode = &conn_state->mode; 675b014c5e2SGuochun Huang const char *compatible; 676186f8572SMark Yao int ret = 0; 67752015e97SSandy Huang static bool __print_once = false; 678690e9ed1SSandy Huang #ifdef CONFIG_SPL_BUILD 679690e9ed1SSandy Huang struct spl_display_info *spl_disp_info = (struct spl_display_info *)CONFIG_SPL_VIDEO_BUF; 680690e9ed1SSandy Huang #endif 68152015e97SSandy Huang if (!__print_once) { 68252015e97SSandy Huang __print_once = true; 683e559407dSSandy Huang printf("Rockchip UBOOT DRM driver version: %s\n", DRIVER_VERSION); 68452015e97SSandy Huang } 685e559407dSSandy Huang 686186f8572SMark Yao if (state->is_init) 687186f8572SMark Yao return 0; 688186f8572SMark Yao 6890594ce39SZhang Yubing if (!crtc_funcs) { 6900594ce39SZhang Yubing printf("failed to find crtc functions\n"); 691186f8572SMark Yao return -ENXIO; 692186f8572SMark Yao } 693186f8572SMark Yao 694690e9ed1SSandy Huang #ifdef CONFIG_SPL_BUILD 695690e9ed1SSandy Huang if (state->conn_state.type == DRM_MODE_CONNECTOR_HDMIA) 696690e9ed1SSandy Huang state->enabled_at_spl = spl_disp_info->enabled == 1 ? true : false; 697690e9ed1SSandy Huang if (state->enabled_at_spl) 698690e9ed1SSandy Huang printf("HDMI enabled at SPL\n"); 699690e9ed1SSandy Huang #endif 700a5afbabdSSandy Huang if (crtc_state->crtc->active && !crtc_state->ports_node && 7012d7c136fSSandy Huang memcmp(&crtc_state->crtc->active_mode, &conn_state->mode, 7022d7c136fSSandy Huang sizeof(struct drm_display_mode))) { 7032d7c136fSSandy Huang printf("%s has been used for output type: %d, mode: %dx%dp%d\n", 7042d7c136fSSandy Huang crtc_state->dev->name, 7052d7c136fSSandy Huang crtc_state->crtc->active_mode.type, 7062d7c136fSSandy Huang crtc_state->crtc->active_mode.hdisplay, 7072d7c136fSSandy Huang crtc_state->crtc->active_mode.vdisplay, 7082d7c136fSSandy Huang crtc_state->crtc->active_mode.vrefresh); 7092d7c136fSSandy Huang return -ENODEV; 7102d7c136fSSandy Huang } 7112d7c136fSSandy Huang 712cf53642aSSandy Huang if (crtc_funcs->preinit) { 713cf53642aSSandy Huang ret = crtc_funcs->preinit(state); 714cf53642aSSandy Huang if (ret) 715cf53642aSSandy Huang return ret; 716cf53642aSSandy Huang } 717cf53642aSSandy Huang 718690e9ed1SSandy Huang if (state->enabled_at_spl == false) { 7190594ce39SZhang Yubing ret = rockchip_connector_init(state); 720186f8572SMark Yao if (ret) 7214b8c2ef1SMark Yao goto deinit; 722690e9ed1SSandy Huang } 7237cacd0a8SWyon Bi 724186f8572SMark Yao /* 725186f8572SMark Yao * support hotplug, but not connect; 726186f8572SMark Yao */ 727ffaa1c66SDamon Ding #ifdef CONFIG_DRM_ROCKCHIP_TVE 7282a48727aSAlgea Cao if (crtc->hdmi_hpd && conn_state->type == DRM_MODE_CONNECTOR_TV) { 7292a48727aSAlgea Cao printf("hdmi plugin ,skip tve\n"); 7302a48727aSAlgea Cao goto deinit; 7312a48727aSAlgea Cao } 7329c9eff43SAlgea Cao #elif defined(CONFIG_DRM_ROCKCHIP_RK1000) 7332a48727aSAlgea Cao if (crtc->hdmi_hpd && conn_state->type == DRM_MODE_CONNECTOR_LVDS) { 7342a48727aSAlgea Cao printf("hdmi plugin ,skip tve\n"); 7352a48727aSAlgea Cao goto deinit; 7362a48727aSAlgea Cao } 7372a48727aSAlgea Cao #endif 7380594ce39SZhang Yubing 7390594ce39SZhang Yubing ret = rockchip_connector_detect(state); 740ffaa1c66SDamon Ding #if defined(CONFIG_DRM_ROCKCHIP_TVE) || defined(CONFIG_DRM_ROCKCHIP_RK1000) 7412a48727aSAlgea Cao if (conn_state->type == DRM_MODE_CONNECTOR_HDMIA) 7422a48727aSAlgea Cao crtc->hdmi_hpd = ret; 743690e9ed1SSandy Huang if (state->enabled_at_spl) 744690e9ed1SSandy Huang crtc->hdmi_hpd = true; 7452a48727aSAlgea Cao #endif 7460594ce39SZhang Yubing if (!ret && !state->force_output) 747186f8572SMark Yao goto deinit; 748186f8572SMark Yao 749690e9ed1SSandy Huang ret = 0; 750690e9ed1SSandy Huang if (state->enabled_at_spl == true) { 751690e9ed1SSandy Huang #ifdef CONFIG_SPL_BUILD 752690e9ed1SSandy Huang struct drm_display_mode *mode = &conn_state->mode; 753690e9ed1SSandy Huang 754690e9ed1SSandy Huang memcpy(mode, &spl_disp_info->mode, sizeof(*mode)); 755690e9ed1SSandy Huang conn_state->bus_format = spl_disp_info->bus_format; 756690e9ed1SSandy Huang 757690e9ed1SSandy Huang printf("%s get display mode from spl:%dx%d, bus format:0x%x\n", 758690e9ed1SSandy Huang conn->dev->name, mode->hdisplay, mode->vdisplay, conn_state->bus_format); 759690e9ed1SSandy Huang #endif 760690e9ed1SSandy Huang } else if (conn->panel) { 761186f8572SMark Yao ret = display_get_timing(state); 7622a74799bSJianqun Xu if (!ret) 7630594ce39SZhang Yubing conn_state->bpc = conn->panel->bpc; 76434d0c224SWyon Bi #if defined(CONFIG_I2C_EDID) 7650594ce39SZhang Yubing if (ret < 0 && conn->funcs->get_edid) { 7660594ce39SZhang Yubing rockchip_panel_prepare(conn->panel); 7670594ce39SZhang Yubing ret = conn->funcs->get_edid(conn, state); 7682bfb6166SSandy Huang if (!ret) 7692bfb6166SSandy Huang display_get_edid_mode(state); 7702a74799bSJianqun Xu } 77134d0c224SWyon Bi #endif 7720594ce39SZhang Yubing } else if (conn->bridge) { 7730594ce39SZhang Yubing ret = video_bridge_read_edid(conn->bridge->dev, 7741a8d717cSWyon Bi conn_state->edid, EDID_SIZE); 7751a8d717cSWyon Bi if (ret > 0) { 776081dec1bSAndy Yan #if defined(CONFIG_I2C_EDID) 777b1b989dbSDamon Ding ret = display_get_edid_mode(state); 778081dec1bSAndy Yan #endif 77975eb6fceSAlgea Cao } else { 7800594ce39SZhang Yubing ret = video_bridge_get_timing(conn->bridge->dev); 7811a8d717cSWyon Bi } 7820594ce39SZhang Yubing } else if (conn->funcs->get_timing) { 7830594ce39SZhang Yubing ret = conn->funcs->get_timing(conn, state); 7840594ce39SZhang Yubing } else if (conn->funcs->get_edid) { 7850594ce39SZhang Yubing ret = conn->funcs->get_edid(conn, state); 786081dec1bSAndy Yan #if defined(CONFIG_I2C_EDID) 7872bfb6166SSandy Huang if (!ret) 7882bfb6166SSandy Huang display_get_edid_mode(state); 789081dec1bSAndy Yan #endif 7901a8d717cSWyon Bi } 7911a8d717cSWyon Bi 792b752fb5dSWyon Bi if (!ret && conn_state->secondary) { 793b752fb5dSWyon Bi struct rockchip_connector *connector = conn_state->secondary; 794b752fb5dSWyon Bi 795b752fb5dSWyon Bi if (connector->panel) { 796b752fb5dSWyon Bi if (connector->panel->funcs->get_mode) { 797b752fb5dSWyon Bi struct drm_display_mode *_mode = drm_mode_create(); 798b752fb5dSWyon Bi 799b752fb5dSWyon Bi ret = connector->panel->funcs->get_mode(connector->panel, _mode); 800b752fb5dSWyon Bi if (!ret && !drm_mode_equal(_mode, mode)) 801b752fb5dSWyon Bi ret = -EINVAL; 802b752fb5dSWyon Bi 803b752fb5dSWyon Bi drm_mode_destroy(_mode); 804b752fb5dSWyon Bi } 805b752fb5dSWyon Bi } 806b752fb5dSWyon Bi } 807b752fb5dSWyon Bi 8082bfb6166SSandy Huang if (ret && !state->force_output) 809186f8572SMark Yao goto deinit; 8102bfb6166SSandy Huang if (state->force_output) 8112bfb6166SSandy Huang display_use_force_mode(state); 8121a8d717cSWyon Bi 8137929c09fSGuochun Huang if (display_mode_valid(state)) 8147929c09fSGuochun Huang goto deinit; 8157929c09fSGuochun Huang 816b014c5e2SGuochun Huang /* rk356x series drive mipi pixdata on posedge */ 8170594ce39SZhang Yubing compatible = dev_read_string(conn->dev, "compatible"); 818ab8d4385SZhang Yubing if (compatible && !strcmp(compatible, "rockchip,rk3568-mipi-dsi")) { 81913f658dcSDamon Ding conn_state->bus_flags &= ~DRM_BUS_FLAG_PIXDATA_DRIVE_NEGEDGE; 82013f658dcSDamon Ding conn_state->bus_flags |= DRM_BUS_FLAG_PIXDATA_DRIVE_POSEDGE; 82113f658dcSDamon Ding } 822b014c5e2SGuochun Huang 823b1b989dbSDamon Ding if (display_mode_fixup(state)) 824b1b989dbSDamon Ding goto deinit; 825b1b989dbSDamon Ding 826b1b989dbSDamon Ding if (conn->bridge) 827b1b989dbSDamon Ding rockchip_bridge_mode_set(conn->bridge, &conn_state->mode); 828b1b989dbSDamon Ding 8292bfb6166SSandy Huang printf("%s: %s detailed mode clock %u kHz, flags[%x]\n" 83034d0c224SWyon Bi " H: %04d %04d %04d %04d\n" 83134d0c224SWyon Bi " V: %04d %04d %04d %04d\n" 83234d0c224SWyon Bi "bus_format: %x\n", 8330594ce39SZhang Yubing conn->dev->name, 8342bfb6166SSandy Huang state->force_output ? "use force output" : "", 83534d0c224SWyon Bi mode->clock, mode->flags, 83634d0c224SWyon Bi mode->hdisplay, mode->hsync_start, 83734d0c224SWyon Bi mode->hsync_end, mode->htotal, 83834d0c224SWyon Bi mode->vdisplay, mode->vsync_start, 83934d0c224SWyon Bi mode->vsync_end, mode->vtotal, 84034d0c224SWyon Bi conn_state->bus_format); 84134d0c224SWyon Bi 842690e9ed1SSandy Huang if (crtc_funcs->init && state->enabled_at_spl == false) { 843186f8572SMark Yao ret = crtc_funcs->init(state); 844186f8572SMark Yao if (ret) 845186f8572SMark Yao goto deinit; 846186f8572SMark Yao } 847186f8572SMark Yao state->is_init = 1; 848186f8572SMark Yao 8492d7c136fSSandy Huang crtc_state->crtc->active = true; 8502d7c136fSSandy Huang memcpy(&crtc_state->crtc->active_mode, 8512d7c136fSSandy Huang &conn_state->mode, sizeof(struct drm_display_mode)); 8522d7c136fSSandy Huang 853186f8572SMark Yao return 0; 854186f8572SMark Yao 855186f8572SMark Yao deinit: 8560594ce39SZhang Yubing rockchip_connector_deinit(state); 857186f8572SMark Yao return ret; 858186f8572SMark Yao } 859186f8572SMark Yao 86067b9012cSSandy Huang int display_send_mcu_cmd(struct display_state *state, u32 type, u32 val) 86167b9012cSSandy Huang { 86267b9012cSSandy Huang struct crtc_state *crtc_state = &state->crtc_state; 86367b9012cSSandy Huang const struct rockchip_crtc *crtc = crtc_state->crtc; 86467b9012cSSandy Huang const struct rockchip_crtc_funcs *crtc_funcs = crtc->funcs; 86567b9012cSSandy Huang int ret; 86667b9012cSSandy Huang 86767b9012cSSandy Huang if (!state->is_init) 86867b9012cSSandy Huang return -EINVAL; 86967b9012cSSandy Huang 87067b9012cSSandy Huang if (crtc_funcs->send_mcu_cmd) { 87167b9012cSSandy Huang ret = crtc_funcs->send_mcu_cmd(state, type, val); 87267b9012cSSandy Huang if (ret) 87367b9012cSSandy Huang return ret; 87467b9012cSSandy Huang } 87567b9012cSSandy Huang 87667b9012cSSandy Huang return 0; 87767b9012cSSandy Huang } 87867b9012cSSandy Huang 879186f8572SMark Yao static int display_set_plane(struct display_state *state) 880186f8572SMark Yao { 881186f8572SMark Yao struct crtc_state *crtc_state = &state->crtc_state; 882186f8572SMark Yao const struct rockchip_crtc *crtc = crtc_state->crtc; 883186f8572SMark Yao const struct rockchip_crtc_funcs *crtc_funcs = crtc->funcs; 884186f8572SMark Yao int ret; 885186f8572SMark Yao 886186f8572SMark Yao if (!state->is_init) 887186f8572SMark Yao return -EINVAL; 888186f8572SMark Yao 889186f8572SMark Yao if (crtc_funcs->set_plane) { 890186f8572SMark Yao ret = crtc_funcs->set_plane(state); 891186f8572SMark Yao if (ret) 892186f8572SMark Yao return ret; 893186f8572SMark Yao } 894186f8572SMark Yao 895186f8572SMark Yao return 0; 896186f8572SMark Yao } 897186f8572SMark Yao 898186f8572SMark Yao static int display_enable(struct display_state *state) 899186f8572SMark Yao { 900186f8572SMark Yao struct crtc_state *crtc_state = &state->crtc_state; 901186f8572SMark Yao const struct rockchip_crtc *crtc = crtc_state->crtc; 902186f8572SMark Yao const struct rockchip_crtc_funcs *crtc_funcs = crtc->funcs; 903186f8572SMark Yao 904186f8572SMark Yao if (!state->is_init) 905186f8572SMark Yao return -EINVAL; 906186f8572SMark Yao 907186f8572SMark Yao if (state->is_enable) 908186f8572SMark Yao return 0; 909186f8572SMark Yao 91049ae8667SWyon Bi if (crtc_funcs->prepare) 91149ae8667SWyon Bi crtc_funcs->prepare(state); 912186f8572SMark Yao 913690e9ed1SSandy Huang if (state->enabled_at_spl == false) 9140594ce39SZhang Yubing rockchip_connector_pre_enable(state); 915186f8572SMark Yao 91649ae8667SWyon Bi if (crtc_funcs->enable) 91749ae8667SWyon Bi crtc_funcs->enable(state); 918186f8572SMark Yao 919690e9ed1SSandy Huang if (state->enabled_at_spl == false) 9200594ce39SZhang Yubing rockchip_connector_enable(state); 921186f8572SMark Yao 922efa01fe4SZhang Yubing if (crtc_funcs->post_enable) 923efa01fe4SZhang Yubing crtc_funcs->post_enable(state); 924efa01fe4SZhang Yubing 925117c0524SGuochun Huang #ifdef CONFIG_DRM_ROCKCHIP_RK628 926117c0524SGuochun Huang /* 927117c0524SGuochun Huang * trigger .probe helper of U_BOOT_DRIVER(rk628) in ./rk628/rk628.c 928117c0524SGuochun Huang */ 929117c0524SGuochun Huang struct udevice * dev; 930117c0524SGuochun Huang int phandle, ret; 931117c0524SGuochun Huang 932117c0524SGuochun Huang phandle = ofnode_read_u32_default(state->node, "bridge", -1); 933117c0524SGuochun Huang if (phandle < 0) 934117c0524SGuochun Huang printf("%s failed to find bridge phandle\n", ofnode_get_name(state->node)); 935117c0524SGuochun Huang 936117c0524SGuochun Huang ret = uclass_get_device_by_phandle_id(UCLASS_I2C_GENERIC, phandle, &dev); 937117c0524SGuochun Huang if (ret && ret != -ENOENT) 938117c0524SGuochun Huang printf("%s:%d failed to get rk628 device ret:%d\n", __func__, __LINE__, ret); 939117c0524SGuochun Huang 940117c0524SGuochun Huang #endif 941117c0524SGuochun Huang 9428e7ef808SDamon Ding if (crtc_state->soft_te) 9438e7ef808SDamon Ding crtc_funcs->apply_soft_te(state); 9448e7ef808SDamon Ding 945186f8572SMark Yao state->is_enable = true; 946c493d00eSWyon Bi 94749ae8667SWyon Bi return 0; 948186f8572SMark Yao } 949186f8572SMark Yao 950186f8572SMark Yao static int display_disable(struct display_state *state) 951186f8572SMark Yao { 952186f8572SMark Yao struct crtc_state *crtc_state = &state->crtc_state; 953186f8572SMark Yao const struct rockchip_crtc *crtc = crtc_state->crtc; 954186f8572SMark Yao const struct rockchip_crtc_funcs *crtc_funcs = crtc->funcs; 955186f8572SMark Yao 956186f8572SMark Yao if (!state->is_init) 957186f8572SMark Yao return 0; 958186f8572SMark Yao 959186f8572SMark Yao if (!state->is_enable) 960186f8572SMark Yao return 0; 961186f8572SMark Yao 9620594ce39SZhang Yubing rockchip_connector_disable(state); 963186f8572SMark Yao 9641a8d717cSWyon Bi if (crtc_funcs->disable) 9651a8d717cSWyon Bi crtc_funcs->disable(state); 9661a8d717cSWyon Bi 9670594ce39SZhang Yubing rockchip_connector_post_disable(state); 968186f8572SMark Yao 969186f8572SMark Yao state->is_enable = 0; 970186f8572SMark Yao state->is_init = 0; 971186f8572SMark Yao 972186f8572SMark Yao return 0; 973186f8572SMark Yao } 974186f8572SMark Yao 975820a5c17SDamon Ding static int display_check(struct display_state *state) 976820a5c17SDamon Ding { 977820a5c17SDamon Ding struct connector_state *conn_state = &state->conn_state; 978820a5c17SDamon Ding struct rockchip_connector *conn = conn_state->connector; 979820a5c17SDamon Ding const struct rockchip_connector_funcs *conn_funcs = conn->funcs; 980820a5c17SDamon Ding struct crtc_state *crtc_state = &state->crtc_state; 981820a5c17SDamon Ding const struct rockchip_crtc *crtc = crtc_state->crtc; 982820a5c17SDamon Ding const struct rockchip_crtc_funcs *crtc_funcs = crtc->funcs; 983820a5c17SDamon Ding int ret; 984820a5c17SDamon Ding 985820a5c17SDamon Ding if (!state->is_init) 986aa3695c1SDamon Ding return -EINVAL; 987820a5c17SDamon Ding 988820a5c17SDamon Ding if (conn_funcs->check) { 989820a5c17SDamon Ding ret = conn_funcs->check(conn, state); 990820a5c17SDamon Ding if (ret) 991820a5c17SDamon Ding goto check_fail; 992820a5c17SDamon Ding } 993820a5c17SDamon Ding 994820a5c17SDamon Ding if (crtc_funcs->check) { 995820a5c17SDamon Ding ret = crtc_funcs->check(state); 996820a5c17SDamon Ding if (ret) 997820a5c17SDamon Ding goto check_fail; 998820a5c17SDamon Ding } 999820a5c17SDamon Ding 10004c765862SDamon Ding if (crtc_funcs->plane_check) { 10014c765862SDamon Ding ret = crtc_funcs->plane_check(state); 10024c765862SDamon Ding if (ret) 10034c765862SDamon Ding goto check_fail; 10044c765862SDamon Ding } 10054c765862SDamon Ding 1006820a5c17SDamon Ding return 0; 1007820a5c17SDamon Ding 1008820a5c17SDamon Ding check_fail: 1009820a5c17SDamon Ding state->is_init = false; 1010820a5c17SDamon Ding return ret; 1011820a5c17SDamon Ding } 1012820a5c17SDamon Ding 1013186f8572SMark Yao static int display_logo(struct display_state *state) 1014186f8572SMark Yao { 1015186f8572SMark Yao struct crtc_state *crtc_state = &state->crtc_state; 1016186f8572SMark Yao struct connector_state *conn_state = &state->conn_state; 1017668e6278SDamon Ding struct overscan *overscan = &conn_state->overscan; 1018186f8572SMark Yao struct logo_info *logo = &state->logo; 1019668e6278SDamon Ding u32 crtc_x, crtc_y, crtc_w, crtc_h; 1020668e6278SDamon Ding u32 overscan_w, overscan_h; 10212d7c136fSSandy Huang int hdisplay, vdisplay, ret; 1022186f8572SMark Yao 1023*d00abaefSWenping Zhang if (state->is_init) 1024*d00abaefSWenping Zhang return 0; 1025*d00abaefSWenping Zhang 10262d7c136fSSandy Huang ret = display_init(state); 10272d7c136fSSandy Huang if (!state->is_init || ret) 1028186f8572SMark Yao return -ENODEV; 1029186f8572SMark Yao 1030186f8572SMark Yao switch (logo->bpp) { 1031186f8572SMark Yao case 16: 1032186f8572SMark Yao crtc_state->format = ROCKCHIP_FMT_RGB565; 1033186f8572SMark Yao break; 1034186f8572SMark Yao case 24: 1035186f8572SMark Yao crtc_state->format = ROCKCHIP_FMT_RGB888; 1036186f8572SMark Yao break; 1037186f8572SMark Yao case 32: 1038186f8572SMark Yao crtc_state->format = ROCKCHIP_FMT_ARGB8888; 1039186f8572SMark Yao break; 1040186f8572SMark Yao default: 1041186f8572SMark Yao printf("can't support bmp bits[%d]\n", logo->bpp); 1042186f8572SMark Yao return -EINVAL; 1043186f8572SMark Yao } 1044ecf57f93SZhang Yubing hdisplay = conn_state->mode.crtc_hdisplay; 104571ac76f7SDamon Ding vdisplay = conn_state->mode.vdisplay; 1046ee01dbb2SDamon Ding crtc_state->src_rect.w = logo->width; 1047ee01dbb2SDamon Ding crtc_state->src_rect.h = logo->height; 1048ee01dbb2SDamon Ding crtc_state->src_rect.x = 0; 1049ee01dbb2SDamon Ding crtc_state->src_rect.y = 0; 1050186f8572SMark Yao crtc_state->ymirror = logo->ymirror; 1051a5f116a3SDamon Ding crtc_state->rb_swap = 0; 1052186f8572SMark Yao 10534b8c2ef1SMark Yao crtc_state->dma_addr = (u32)(unsigned long)logo->mem + logo->offset; 1054ee01dbb2SDamon Ding crtc_state->xvir = ALIGN(crtc_state->src_rect.w * logo->bpp, 32) >> 5; 1055186f8572SMark Yao 10569dc06118SDamon Ding if (state->logo_mode == ROCKCHIP_DISPLAY_FULLSCREEN) { 1057ee01dbb2SDamon Ding crtc_state->crtc_rect.x = 0; 1058ee01dbb2SDamon Ding crtc_state->crtc_rect.y = 0; 1059ee01dbb2SDamon Ding crtc_state->crtc_rect.w = hdisplay; 1060ee01dbb2SDamon Ding crtc_state->crtc_rect.h = vdisplay; 1061186f8572SMark Yao } else { 1062ee01dbb2SDamon Ding if (crtc_state->src_rect.w >= hdisplay) { 1063ee01dbb2SDamon Ding crtc_state->crtc_rect.x = 0; 1064ee01dbb2SDamon Ding crtc_state->crtc_rect.w = hdisplay; 1065186f8572SMark Yao } else { 1066ee01dbb2SDamon Ding crtc_state->crtc_rect.x = (hdisplay - crtc_state->src_rect.w) / 2; 1067ee01dbb2SDamon Ding crtc_state->crtc_rect.w = crtc_state->src_rect.w; 1068186f8572SMark Yao } 1069186f8572SMark Yao 1070ee01dbb2SDamon Ding if (crtc_state->src_rect.h >= vdisplay) { 1071ee01dbb2SDamon Ding crtc_state->crtc_rect.y = 0; 1072ee01dbb2SDamon Ding crtc_state->crtc_rect.h = vdisplay; 1073186f8572SMark Yao } else { 1074ee01dbb2SDamon Ding crtc_state->crtc_rect.y = (vdisplay - crtc_state->src_rect.h) / 2; 1075ee01dbb2SDamon Ding crtc_state->crtc_rect.h = crtc_state->src_rect.h; 1076186f8572SMark Yao } 1077186f8572SMark Yao } 1078186f8572SMark Yao 1079668e6278SDamon Ding /* 1080668e6278SDamon Ding * For some platforms, such as RK3576, use the win scale instead 1081668e6278SDamon Ding * of the post scale to configure overscan parameters, because the 1082668e6278SDamon Ding * sharp/post scale/split functions are mutually exclusice. 1083668e6278SDamon Ding */ 1084668e6278SDamon Ding if (crtc_state->overscan_by_win_scale) { 1085668e6278SDamon Ding overscan_w = crtc_state->crtc_rect.w * (200 - overscan->left_margin * 2) / 200; 1086668e6278SDamon Ding overscan_h = crtc_state->crtc_rect.h * (200 - overscan->top_margin * 2) / 200; 1087668e6278SDamon Ding 1088668e6278SDamon Ding crtc_x = crtc_state->crtc_rect.x + overscan_w / 2; 1089668e6278SDamon Ding crtc_y = crtc_state->crtc_rect.y + overscan_h / 2; 1090668e6278SDamon Ding crtc_w = crtc_state->crtc_rect.w - overscan_w; 1091668e6278SDamon Ding crtc_h = crtc_state->crtc_rect.h - overscan_h; 1092668e6278SDamon Ding 1093668e6278SDamon Ding crtc_state->crtc_rect.x = crtc_x; 1094668e6278SDamon Ding crtc_state->crtc_rect.y = crtc_y; 1095668e6278SDamon Ding crtc_state->crtc_rect.w = crtc_w; 1096668e6278SDamon Ding crtc_state->crtc_rect.h = crtc_h; 1097668e6278SDamon Ding } 1098668e6278SDamon Ding 1099aa3695c1SDamon Ding ret = display_check(state); 1100aa3695c1SDamon Ding if (ret) 1101aa3695c1SDamon Ding return ret; 1102aa3695c1SDamon Ding 11031c9572c7SDamon Ding ret = display_set_plane(state); 11041c9572c7SDamon Ding if (ret) 11051c9572c7SDamon Ding return ret; 1106186f8572SMark Yao display_enable(state); 1107186f8572SMark Yao 1108186f8572SMark Yao return 0; 1109186f8572SMark Yao } 1110186f8572SMark Yao 1111ed797993SDamon Ding static int get_crtc_id(ofnode connect, bool is_ports_node) 1112186f8572SMark Yao { 11132bf72cbbSDamon Ding struct device_node *port_node; 1114ed797993SDamon Ding struct device_node *remote; 1115ed797993SDamon Ding int phandle; 1116186f8572SMark Yao int val; 1117186f8572SMark Yao 1118ed797993SDamon Ding if (is_ports_node) { 1119ed797993SDamon Ding port_node = of_get_parent(connect.np); 11202bf72cbbSDamon Ding if (!port_node) 1121186f8572SMark Yao goto err; 1122ed797993SDamon Ding 11232bf72cbbSDamon Ding val = ofnode_read_u32_default(np_to_ofnode(port_node), "reg", -1); 1124186f8572SMark Yao if (val < 0) 1125186f8572SMark Yao goto err; 1126ed797993SDamon Ding } else { 1127ed797993SDamon Ding phandle = ofnode_read_u32_default(connect, "remote-endpoint", -1); 1128ed797993SDamon Ding if (phandle < 0) 1129ed797993SDamon Ding goto err; 1130ed797993SDamon Ding 1131ed797993SDamon Ding remote = of_find_node_by_phandle(phandle); 1132ed797993SDamon Ding if (!remote) 1133ed797993SDamon Ding goto err; 1134ed797993SDamon Ding 1135ed797993SDamon Ding val = ofnode_read_u32_default(np_to_ofnode(remote), "reg", -1); 1136ed797993SDamon Ding if (val < 0) 1137ed797993SDamon Ding goto err; 1138ed797993SDamon Ding } 1139186f8572SMark Yao 1140186f8572SMark Yao return val; 1141186f8572SMark Yao err: 1142186f8572SMark Yao printf("Can't get crtc id, default set to id = 0\n"); 1143186f8572SMark Yao return 0; 1144186f8572SMark Yao } 1145186f8572SMark Yao 11460675a2a4SDamon Ding static int get_crtc_mcu_mode(struct crtc_state *crtc_state, struct device_node *port_node, 11470675a2a4SDamon Ding bool is_ports_node) 114867b9012cSSandy Huang { 11490675a2a4SDamon Ding ofnode mcu_node, vp_node; 115067b9012cSSandy Huang int total_pixel, cs_pst, cs_pend, rw_pst, rw_pend; 115167b9012cSSandy Huang 11520675a2a4SDamon Ding if (is_ports_node) { 11530675a2a4SDamon Ding vp_node = np_to_ofnode(port_node); 11540675a2a4SDamon Ding mcu_node = ofnode_find_subnode(vp_node, "mcu-timing"); 11550675a2a4SDamon Ding if (!ofnode_valid(mcu_node)) 11560675a2a4SDamon Ding return -ENODEV; 11570675a2a4SDamon Ding } else { 115867b9012cSSandy Huang mcu_node = dev_read_subnode(crtc_state->dev, "mcu-timing"); 1159a196d7fcSKever Yang if (!ofnode_valid(mcu_node)) 1160a196d7fcSKever Yang return -ENODEV; 11610675a2a4SDamon Ding } 116267b9012cSSandy Huang 116367b9012cSSandy Huang #define FDT_GET_MCU_INT(val, name) \ 116467b9012cSSandy Huang do { \ 116567b9012cSSandy Huang val = ofnode_read_s32_default(mcu_node, name, -1); \ 116667b9012cSSandy Huang if (val < 0) { \ 116767b9012cSSandy Huang printf("Can't get %s\n", name); \ 116867b9012cSSandy Huang return -ENXIO; \ 116967b9012cSSandy Huang } \ 117067b9012cSSandy Huang } while (0) 117167b9012cSSandy Huang 117267b9012cSSandy Huang FDT_GET_MCU_INT(total_pixel, "mcu-pix-total"); 117367b9012cSSandy Huang FDT_GET_MCU_INT(cs_pst, "mcu-cs-pst"); 117467b9012cSSandy Huang FDT_GET_MCU_INT(cs_pend, "mcu-cs-pend"); 117567b9012cSSandy Huang FDT_GET_MCU_INT(rw_pst, "mcu-rw-pst"); 117667b9012cSSandy Huang FDT_GET_MCU_INT(rw_pend, "mcu-rw-pend"); 117767b9012cSSandy Huang 117867b9012cSSandy Huang crtc_state->mcu_timing.mcu_pix_total = total_pixel; 117967b9012cSSandy Huang crtc_state->mcu_timing.mcu_cs_pst = cs_pst; 118067b9012cSSandy Huang crtc_state->mcu_timing.mcu_cs_pend = cs_pend; 118167b9012cSSandy Huang crtc_state->mcu_timing.mcu_rw_pst = rw_pst; 118267b9012cSSandy Huang crtc_state->mcu_timing.mcu_rw_pend = rw_pend; 118367b9012cSSandy Huang 118467b9012cSSandy Huang return 0; 118567b9012cSSandy Huang } 118667b9012cSSandy Huang 11870fda4887SDamon Ding struct rockchip_logo_cache *find_or_alloc_logo_cache(const char *bmp, int rotate) 1188186f8572SMark Yao { 1189186f8572SMark Yao struct rockchip_logo_cache *tmp, *logo_cache = NULL; 1190186f8572SMark Yao 1191186f8572SMark Yao list_for_each_entry(tmp, &logo_cache_list, head) { 11920fda4887SDamon Ding if ((!strcmp(tmp->name, bmp) && rotate == tmp->logo_rotate) || 11930fda4887SDamon Ding (soc_is_rk3566() && tmp->logo_rotate)) { 1194186f8572SMark Yao logo_cache = tmp; 1195186f8572SMark Yao break; 1196186f8572SMark Yao } 1197186f8572SMark Yao } 1198186f8572SMark Yao 1199186f8572SMark Yao if (!logo_cache) { 1200186f8572SMark Yao logo_cache = malloc(sizeof(*logo_cache)); 1201186f8572SMark Yao if (!logo_cache) { 1202186f8572SMark Yao printf("failed to alloc memory for logo cache\n"); 1203186f8572SMark Yao return NULL; 1204186f8572SMark Yao } 1205186f8572SMark Yao memset(logo_cache, 0, sizeof(*logo_cache)); 1206186f8572SMark Yao strcpy(logo_cache->name, bmp); 1207186f8572SMark Yao INIT_LIST_HEAD(&logo_cache->head); 1208186f8572SMark Yao list_add_tail(&logo_cache->head, &logo_cache_list); 1209186f8572SMark Yao } 1210186f8572SMark Yao 1211186f8572SMark Yao return logo_cache; 1212186f8572SMark Yao } 1213186f8572SMark Yao 12145eb61944SSandy Huang /* Note: used only for rkfb kernel driver */ 12155eb61944SSandy Huang static int load_kernel_bmp_logo(struct logo_info *logo, const char *bmp_name) 12165eb61944SSandy Huang { 12175eb61944SSandy Huang #ifdef CONFIG_ROCKCHIP_RESOURCE_IMAGE 12185eb61944SSandy Huang void *dst = NULL; 12195eb61944SSandy Huang int len, size; 12205eb61944SSandy Huang struct bmp_header *header; 12215eb61944SSandy Huang 12225eb61944SSandy Huang if (!logo || !bmp_name) 12235eb61944SSandy Huang return -EINVAL; 12245eb61944SSandy Huang 12255eb61944SSandy Huang header = malloc(RK_BLK_SIZE); 12265eb61944SSandy Huang if (!header) 12275eb61944SSandy Huang return -ENOMEM; 12285eb61944SSandy Huang 12295eb61944SSandy Huang len = rockchip_read_resource_file(header, bmp_name, 0, RK_BLK_SIZE); 12305eb61944SSandy Huang if (len != RK_BLK_SIZE) { 12315eb61944SSandy Huang free(header); 12325eb61944SSandy Huang return -EINVAL; 12335eb61944SSandy Huang } 12345eb61944SSandy Huang size = get_unaligned_le32(&header->file_size); 12355eb61944SSandy Huang dst = (void *)(memory_start + MEMORY_POOL_SIZE / 2); 12365eb61944SSandy Huang len = rockchip_read_resource_file(dst, bmp_name, 0, size); 12375eb61944SSandy Huang if (len != size) { 12385eb61944SSandy Huang printf("failed to load bmp %s\n", bmp_name); 12395eb61944SSandy Huang free(header); 12405eb61944SSandy Huang return -ENOENT; 12415eb61944SSandy Huang } 12425eb61944SSandy Huang 12435eb61944SSandy Huang logo->mem = dst; 124423b55d3dSJoseph Chen #endif 12455eb61944SSandy Huang 12465eb61944SSandy Huang return 0; 12475eb61944SSandy Huang } 12485eb61944SSandy Huang 12497a71aa91SDamon Ding #ifdef BMP_DECODEER_LEGACY 12507a71aa91SDamon Ding static int load_bmp_logo_legacy(struct logo_info *logo, const char *bmp_name) 1251186f8572SMark Yao { 12524b8c2ef1SMark Yao #ifdef CONFIG_ROCKCHIP_RESOURCE_IMAGE 1253186f8572SMark Yao struct rockchip_logo_cache *logo_cache; 1254186f8572SMark Yao struct bmp_header *header; 1255186f8572SMark Yao void *dst = NULL, *pdst; 12564b8c2ef1SMark Yao int size, len; 12574b8c2ef1SMark Yao int ret = 0; 12587e72214dSShixiang Zheng int reserved = 0; 12597a589732SChris Zhong int dst_size; 1260186f8572SMark Yao 1261186f8572SMark Yao if (!logo || !bmp_name) 1262186f8572SMark Yao return -EINVAL; 12630fda4887SDamon Ding logo_cache = find_or_alloc_logo_cache(bmp_name, logo->rotate); 1264186f8572SMark Yao if (!logo_cache) 1265186f8572SMark Yao return -ENOMEM; 1266186f8572SMark Yao 1267186f8572SMark Yao if (logo_cache->logo.mem) { 1268186f8572SMark Yao memcpy(logo, &logo_cache->logo, sizeof(*logo)); 1269186f8572SMark Yao return 0; 1270186f8572SMark Yao } 1271186f8572SMark Yao 12724b8c2ef1SMark Yao header = malloc(RK_BLK_SIZE); 1273186f8572SMark Yao if (!header) 12744b8c2ef1SMark Yao return -ENOMEM; 12754b8c2ef1SMark Yao 12764b8c2ef1SMark Yao len = rockchip_read_resource_file(header, bmp_name, 0, RK_BLK_SIZE); 12774b8c2ef1SMark Yao if (len != RK_BLK_SIZE) { 12784b8c2ef1SMark Yao ret = -EINVAL; 12794b8c2ef1SMark Yao goto free_header; 12804b8c2ef1SMark Yao } 1281186f8572SMark Yao 1282186f8572SMark Yao logo->bpp = get_unaligned_le16(&header->bit_count); 1283186f8572SMark Yao logo->width = get_unaligned_le32(&header->width); 1284186f8572SMark Yao logo->height = get_unaligned_le32(&header->height); 12857a589732SChris Zhong dst_size = logo->width * logo->height * logo->bpp >> 3; 12867e72214dSShixiang Zheng reserved = get_unaligned_le32(&header->reserved); 12877e72214dSShixiang Zheng if (logo->height < 0) 12887e72214dSShixiang Zheng logo->height = -logo->height; 1289186f8572SMark Yao size = get_unaligned_le32(&header->file_size); 1290861ce1a0SSandy Huang if (!can_direct_logo(logo->bpp)) { 1291186f8572SMark Yao if (size > MEMORY_POOL_SIZE) { 1292186f8572SMark Yao printf("failed to use boot buf as temp bmp buffer\n"); 12934b8c2ef1SMark Yao ret = -ENOMEM; 12944b8c2ef1SMark Yao goto free_header; 1295186f8572SMark Yao } 1296861ce1a0SSandy Huang pdst = get_display_buffer(size); 1297186f8572SMark Yao 1298861ce1a0SSandy Huang } else { 1299186f8572SMark Yao pdst = get_display_buffer(size); 1300186f8572SMark Yao dst = pdst; 1301861ce1a0SSandy Huang } 1302186f8572SMark Yao 13034b8c2ef1SMark Yao len = rockchip_read_resource_file(pdst, bmp_name, 0, size); 13044b8c2ef1SMark Yao if (len != size) { 1305186f8572SMark Yao printf("failed to load bmp %s\n", bmp_name); 13064b8c2ef1SMark Yao ret = -ENOENT; 13074b8c2ef1SMark Yao goto free_header; 1308186f8572SMark Yao } 130955e2f86dSSandy Huang 1310861ce1a0SSandy Huang if (!can_direct_logo(logo->bpp)) { 1311186f8572SMark Yao /* 1312186f8572SMark Yao * TODO: force use 16bpp if bpp less than 16; 1313186f8572SMark Yao */ 1314186f8572SMark Yao logo->bpp = (logo->bpp <= 16) ? 16 : logo->bpp; 1315186f8572SMark Yao dst_size = logo->width * logo->height * logo->bpp >> 3; 1316186f8572SMark Yao dst = get_display_buffer(dst_size); 13174b8c2ef1SMark Yao if (!dst) { 13184b8c2ef1SMark Yao ret = -ENOMEM; 13194b8c2ef1SMark Yao goto free_header; 13204b8c2ef1SMark Yao } 132155e2f86dSSandy Huang if (bmpdecoder(pdst, dst, logo->bpp)) { 1322186f8572SMark Yao printf("failed to decode bmp %s\n", bmp_name); 13234b8c2ef1SMark Yao ret = -EINVAL; 13244b8c2ef1SMark Yao goto free_header; 1325186f8572SMark Yao } 132655e2f86dSSandy Huang 1327186f8572SMark Yao logo->offset = 0; 132855e2f86dSSandy Huang logo->ymirror = 0; 1329186f8572SMark Yao } else { 1330186f8572SMark Yao logo->offset = get_unaligned_le32(&header->data_offset); 13317e72214dSShixiang Zheng if (reserved == BMP_PROCESSED_FLAG) 13327e72214dSShixiang Zheng logo->ymirror = 0; 13337e72214dSShixiang Zheng else 133455e2f86dSSandy Huang logo->ymirror = 1; 1335186f8572SMark Yao } 13364b8c2ef1SMark Yao logo->mem = dst; 1337186f8572SMark Yao 1338186f8572SMark Yao memcpy(&logo_cache->logo, logo, sizeof(*logo)); 1339186f8572SMark Yao 13407a589732SChris Zhong flush_dcache_range((ulong)dst, ALIGN((ulong)dst + dst_size, CONFIG_SYS_CACHELINE_SIZE)); 13417a589732SChris Zhong 13424b8c2ef1SMark Yao free_header: 13434b8c2ef1SMark Yao 13444b8c2ef1SMark Yao free(header); 13454b8c2ef1SMark Yao 13464b8c2ef1SMark Yao return ret; 13474b8c2ef1SMark Yao #else 13484b8c2ef1SMark Yao return -EINVAL; 13494b8c2ef1SMark Yao #endif 1350186f8572SMark Yao } 13517a71aa91SDamon Ding #endif 13527a71aa91SDamon Ding 13537a71aa91SDamon Ding static void *bitmap_create(int width, int height, unsigned int state) 13547a71aa91SDamon Ding { 13557a71aa91SDamon Ding /* Ensure a stupidly large bitmap is not created */ 13567a71aa91SDamon Ding if (width > 4096 || height > 4096) 13577a71aa91SDamon Ding return NULL; 13587a71aa91SDamon Ding 13597a71aa91SDamon Ding return calloc(width * height, BYTES_PER_PIXEL); 13607a71aa91SDamon Ding } 13617a71aa91SDamon Ding 13627a71aa91SDamon Ding static unsigned char *bitmap_get_buffer(void *bitmap) 13637a71aa91SDamon Ding { 13647a71aa91SDamon Ding return bitmap; 13657a71aa91SDamon Ding } 13667a71aa91SDamon Ding 13677a71aa91SDamon Ding static void bitmap_destroy(void *bitmap) 13687a71aa91SDamon Ding { 13697a71aa91SDamon Ding free(bitmap); 13707a71aa91SDamon Ding } 13717a71aa91SDamon Ding 13727a71aa91SDamon Ding static void bmp_copy(void *dst, bmp_image *bmp) 13737a71aa91SDamon Ding { 13747a71aa91SDamon Ding u16 row, col; 13757a71aa91SDamon Ding u8 *image; 13767a71aa91SDamon Ding u8 *pdst = (u8 *)dst; 13777a71aa91SDamon Ding 13787a71aa91SDamon Ding image = (u8 *)bmp->bitmap; 13797a71aa91SDamon Ding for (row = 0; row != bmp->height; row++) { 13807a71aa91SDamon Ding for (col = 0; col != bmp->width; col++) { 13817a71aa91SDamon Ding size_t z = (row * bmp->width + col) * BYTES_PER_PIXEL; 13827a71aa91SDamon Ding 13837a71aa91SDamon Ding *pdst++ = image[z + 2]; 13847a71aa91SDamon Ding *pdst++ = image[z + 1]; 13857a71aa91SDamon Ding *pdst++ = image[z + 0]; 13867a71aa91SDamon Ding *pdst++ = image[z + 3]; 13877a71aa91SDamon Ding } 13887a71aa91SDamon Ding } 13897a71aa91SDamon Ding } 13907a71aa91SDamon Ding 13910fda4887SDamon Ding static void *rockchip_logo_rotate(struct logo_info *logo, void *src) 13920fda4887SDamon Ding { 13930fda4887SDamon Ding void *dst_rotate; 13940fda4887SDamon Ding int width = logo->width; 13950fda4887SDamon Ding int height = logo->height; 13960fda4887SDamon Ding int width_rotate = logo->height & 0x3 ? (logo->height & ~0x3) + 4 : logo->height; 13970fda4887SDamon Ding int height_rotate = logo->width; 13980fda4887SDamon Ding int dst_size = width * height * logo->bpp >> 3; 13990fda4887SDamon Ding int dst_size_rotate = width_rotate * height_rotate * logo->bpp >> 3; 14000fda4887SDamon Ding int bytes_per_pixel = logo->bpp >> 3; 14010fda4887SDamon Ding int padded_width; 14020fda4887SDamon Ding int i, j; 14030fda4887SDamon Ding char *img_data; 14040fda4887SDamon Ding 14050fda4887SDamon Ding if (!(logo->rotate == 90 || logo->rotate == 180 || logo->rotate == 270)) { 14060fda4887SDamon Ding printf("Unsupported rotation angle\n"); 14070fda4887SDamon Ding return NULL; 14080fda4887SDamon Ding } 14090fda4887SDamon Ding 14100fda4887SDamon Ding img_data = (char *)malloc(dst_size); 14110fda4887SDamon Ding if (!img_data) { 14120fda4887SDamon Ding printf("failed to alloc memory for image data\n"); 14130fda4887SDamon Ding return NULL; 14140fda4887SDamon Ding } 14150fda4887SDamon Ding memcpy(img_data, src, dst_size); 14160fda4887SDamon Ding 14170fda4887SDamon Ding dst_rotate = get_display_buffer(dst_size_rotate); 14180fda4887SDamon Ding if (!dst_rotate) 14190fda4887SDamon Ding return NULL; 14200fda4887SDamon Ding memset(dst_rotate, 0, dst_size_rotate); 14210fda4887SDamon Ding 14220fda4887SDamon Ding switch (logo->rotate) { 14230fda4887SDamon Ding case 90: 14240fda4887SDamon Ding logo->width = width_rotate; 14250fda4887SDamon Ding logo->height = height_rotate; 14260fda4887SDamon Ding padded_width = height & 0x3 ? (height & ~0x3) + 4 : height; 14270fda4887SDamon Ding for (i = 0; i < height; i++) { 14280fda4887SDamon Ding for (j = 0; j < width; j++) { 14290fda4887SDamon Ding memcpy(dst_rotate + (j * padded_width * bytes_per_pixel) + 14300fda4887SDamon Ding (height - i - 1) * bytes_per_pixel, 14310fda4887SDamon Ding img_data + i * width * bytes_per_pixel + j * bytes_per_pixel, 14320fda4887SDamon Ding bytes_per_pixel); 14330fda4887SDamon Ding } 14340fda4887SDamon Ding } 14350fda4887SDamon Ding break; 14360fda4887SDamon Ding case 180: 14370fda4887SDamon Ding for (i = 0; i < height; i++) { 14380fda4887SDamon Ding for (j = 0; j < width; j++) { 14390fda4887SDamon Ding memcpy(dst_rotate + (height - i - 1) * width * bytes_per_pixel + 14400fda4887SDamon Ding (width - j - 1) * bytes_per_pixel, 14410fda4887SDamon Ding img_data + i * width * bytes_per_pixel + j * bytes_per_pixel, 14420fda4887SDamon Ding bytes_per_pixel); 14430fda4887SDamon Ding } 14440fda4887SDamon Ding } 14450fda4887SDamon Ding break; 14460fda4887SDamon Ding case 270: 14470fda4887SDamon Ding logo->width = width_rotate; 14480fda4887SDamon Ding logo->height = height_rotate; 14490fda4887SDamon Ding padded_width = height & 0x3 ? (height & ~0x3) + 4 : height; 14500fda4887SDamon Ding for (i = 0; i < height; i++) { 14510fda4887SDamon Ding for (j = 0; j < width; j++) { 14520fda4887SDamon Ding memcpy(dst_rotate + (width - j - 1) * padded_width * bytes_per_pixel + 14530fda4887SDamon Ding i * bytes_per_pixel, 14540fda4887SDamon Ding img_data + i * width * bytes_per_pixel + j * bytes_per_pixel, 14550fda4887SDamon Ding bytes_per_pixel); 14560fda4887SDamon Ding } 14570fda4887SDamon Ding } 14580fda4887SDamon Ding break; 14590fda4887SDamon Ding default: 14600fda4887SDamon Ding break; 14610fda4887SDamon Ding } 14620fda4887SDamon Ding 14630fda4887SDamon Ding free(img_data); 14640fda4887SDamon Ding 14650fda4887SDamon Ding return dst_rotate; 14660fda4887SDamon Ding } 14670fda4887SDamon Ding 14687a71aa91SDamon Ding static int load_bmp_logo(struct logo_info *logo, const char *bmp_name) 14697a71aa91SDamon Ding { 14707a71aa91SDamon Ding #ifdef CONFIG_ROCKCHIP_RESOURCE_IMAGE 14717a71aa91SDamon Ding struct rockchip_logo_cache *logo_cache; 14727a71aa91SDamon Ding bmp_bitmap_callback_vt bitmap_callbacks = { 14737a71aa91SDamon Ding bitmap_create, 14747a71aa91SDamon Ding bitmap_destroy, 14757a71aa91SDamon Ding bitmap_get_buffer, 14767a71aa91SDamon Ding }; 14777a71aa91SDamon Ding bmp_result code; 14787a71aa91SDamon Ding bmp_image bmp; 14797a71aa91SDamon Ding void *bmp_data; 14807a71aa91SDamon Ding void *dst = NULL; 14810fda4887SDamon Ding void *dst_rotate = NULL; 14827a71aa91SDamon Ding int len, dst_size; 14837a71aa91SDamon Ding int ret = 0; 14847a71aa91SDamon Ding 14857a71aa91SDamon Ding if (!logo || !bmp_name) 14867a71aa91SDamon Ding return -EINVAL; 14870fda4887SDamon Ding 14880fda4887SDamon Ding logo_cache = find_or_alloc_logo_cache(bmp_name, logo->rotate); 14897a71aa91SDamon Ding if (!logo_cache) 14907a71aa91SDamon Ding return -ENOMEM; 14917a71aa91SDamon Ding 14927a71aa91SDamon Ding if (logo_cache->logo.mem) { 14937a71aa91SDamon Ding memcpy(logo, &logo_cache->logo, sizeof(*logo)); 14947a71aa91SDamon Ding return 0; 14957a71aa91SDamon Ding } 14967a71aa91SDamon Ding 14977a71aa91SDamon Ding bmp_data = malloc(MAX_IMAGE_BYTES); 1498ecc3b257SDamon Ding if (!bmp_data) { 1499ecc3b257SDamon Ding printf("failed to alloc bmp data\n"); 15007a71aa91SDamon Ding return -ENOMEM; 1501ecc3b257SDamon Ding } 15027a71aa91SDamon Ding 15037a71aa91SDamon Ding bmp_create(&bmp, &bitmap_callbacks); 15047a71aa91SDamon Ding 15057a71aa91SDamon Ding len = rockchip_read_resource_file(bmp_data, bmp_name, 0, MAX_IMAGE_BYTES); 15067a71aa91SDamon Ding if (len < 0) { 15077a71aa91SDamon Ding ret = -EINVAL; 15087a71aa91SDamon Ding goto free_bmp_data; 15097a71aa91SDamon Ding } 15107a71aa91SDamon Ding 15117a71aa91SDamon Ding /* analyse the BMP */ 15127a71aa91SDamon Ding code = bmp_analyse(&bmp, len, bmp_data); 15137a71aa91SDamon Ding if (code != BMP_OK) { 15147a71aa91SDamon Ding printf("failed to parse bmp:%s header\n", bmp_name); 15157a71aa91SDamon Ding ret = -EINVAL; 15167a71aa91SDamon Ding goto free_bmp_data; 15177a71aa91SDamon Ding } 1518ecc3b257SDamon Ding 1519ecc3b257SDamon Ding if (bmp.buffer_size > MAX_IMAGE_BYTES) { 1520ecc3b257SDamon Ding printf("bmp[%s] data size[%dKB] is over the limitation MAX_IMAGE_BYTES[%dKB]\n", 1521ecc3b257SDamon Ding bmp_name, bmp.buffer_size / 1024, MAX_IMAGE_BYTES / 1024); 1522ecc3b257SDamon Ding ret = -EINVAL; 1523ecc3b257SDamon Ding goto free_bmp_data; 1524ecc3b257SDamon Ding } 1525ecc3b257SDamon Ding 15267a71aa91SDamon Ding /* fix bpp to 32 */ 15277a71aa91SDamon Ding logo->bpp = 32; 15287a71aa91SDamon Ding logo->offset = 0; 15297a71aa91SDamon Ding logo->ymirror = 0; 15307a71aa91SDamon Ding logo->width = get_unaligned_le32(&bmp.width); 15317a71aa91SDamon Ding logo->height = get_unaligned_le32(&bmp.height); 15327a71aa91SDamon Ding dst_size = logo->width * logo->height * logo->bpp >> 3; 15337a71aa91SDamon Ding /* decode the image to RGBA8888 format */ 15347a71aa91SDamon Ding code = bmp_decode(&bmp); 15357a71aa91SDamon Ding if (code != BMP_OK) { 15367a71aa91SDamon Ding /* allow partially decoded images */ 15377a71aa91SDamon Ding if (code != BMP_INSUFFICIENT_DATA && code != BMP_DATA_ERROR) { 15387a71aa91SDamon Ding printf("failed to allocate the buffer of bmp:%s\n", bmp_name); 15397a71aa91SDamon Ding ret = -EINVAL; 15407a71aa91SDamon Ding goto free_bmp_data; 15417a71aa91SDamon Ding } 15427a71aa91SDamon Ding } 15437a71aa91SDamon Ding 15447a71aa91SDamon Ding dst = get_display_buffer(dst_size); 15457a71aa91SDamon Ding if (!dst) { 15467a71aa91SDamon Ding ret = -ENOMEM; 15477a71aa91SDamon Ding goto free_bmp_data; 15487a71aa91SDamon Ding } 15497a71aa91SDamon Ding bmp_copy(dst, &bmp); 15500fda4887SDamon Ding 15510fda4887SDamon Ding if (logo->rotate) { 15520fda4887SDamon Ding dst_rotate = rockchip_logo_rotate(logo, dst); 15530fda4887SDamon Ding if (dst_rotate) { 15540fda4887SDamon Ding dst = dst_rotate; 15550fda4887SDamon Ding dst_size = logo->width * logo->height * logo->bpp >> 3; 15560fda4887SDamon Ding } 15570fda4887SDamon Ding printf("logo ratate %d\n", logo->rotate); 15580fda4887SDamon Ding } 15597a71aa91SDamon Ding logo->mem = dst; 15607a71aa91SDamon Ding 15617a71aa91SDamon Ding memcpy(&logo_cache->logo, logo, sizeof(*logo)); 15620fda4887SDamon Ding logo_cache->logo_rotate = logo->rotate; 15637a71aa91SDamon Ding 15647a71aa91SDamon Ding flush_dcache_range((ulong)dst, ALIGN((ulong)dst + dst_size, CONFIG_SYS_CACHELINE_SIZE)); 15657a71aa91SDamon Ding free_bmp_data: 15667a71aa91SDamon Ding /* clean up */ 15677a71aa91SDamon Ding bmp_finalise(&bmp); 15687a71aa91SDamon Ding free(bmp_data); 15697a71aa91SDamon Ding 15707a71aa91SDamon Ding return ret; 15717a71aa91SDamon Ding #else 15727a71aa91SDamon Ding return -EINVAL; 15737a71aa91SDamon Ding #endif 15747a71aa91SDamon Ding } 1575186f8572SMark Yao 1576*d00abaefSWenping Zhang #ifdef CONFIG_ROCKCHIP_VIDCONSOLE 1577*d00abaefSWenping Zhang static int vidconsole_init(struct udevice *dev, struct display_state *state) 1578186f8572SMark Yao { 1579*d00abaefSWenping Zhang struct video_priv *uc_priv = dev_get_uclass_priv(dev); 1580*d00abaefSWenping Zhang struct video_uc_platdata *plat = dev_get_uclass_platdata(dev); 1581*d00abaefSWenping Zhang struct crtc_state *crtc_state = &state->crtc_state; 1582*d00abaefSWenping Zhang struct connector_state *conn_state = &state->conn_state; 1583*d00abaefSWenping Zhang struct overscan *overscan = &conn_state->overscan; 1584*d00abaefSWenping Zhang u32 crtc_x, crtc_y, crtc_w, crtc_h; 1585*d00abaefSWenping Zhang u32 overscan_w, overscan_h; 1586*d00abaefSWenping Zhang int ret; 1587186f8572SMark Yao 1588*d00abaefSWenping Zhang switch (DRM_ROCKCHIP_FB_BPP) { 1589*d00abaefSWenping Zhang case 16: 1590*d00abaefSWenping Zhang crtc_state->format = ROCKCHIP_FMT_RGB565; 1591*d00abaefSWenping Zhang break; 1592*d00abaefSWenping Zhang case 24: 1593*d00abaefSWenping Zhang crtc_state->format = ROCKCHIP_FMT_RGB888; 1594*d00abaefSWenping Zhang break; 1595*d00abaefSWenping Zhang case 32: 1596*d00abaefSWenping Zhang crtc_state->format = ROCKCHIP_FMT_ARGB8888; 1597*d00abaefSWenping Zhang break; 1598*d00abaefSWenping Zhang default: 1599*d00abaefSWenping Zhang printf("can't support video console bpp[%d]\n", DRM_ROCKCHIP_FB_BPP); 1600*d00abaefSWenping Zhang return -EINVAL; 1601*d00abaefSWenping Zhang } 1602186f8572SMark Yao 1603*d00abaefSWenping Zhang crtc_state->src_rect.w = uc_priv->xsize; 1604*d00abaefSWenping Zhang crtc_state->src_rect.h = uc_priv->ysize; 1605*d00abaefSWenping Zhang crtc_state->src_rect.x = 0; 1606*d00abaefSWenping Zhang crtc_state->src_rect.y = 0; 1607*d00abaefSWenping Zhang /* the video console mode is fullscreen display */ 1608*d00abaefSWenping Zhang crtc_state->crtc_rect.w = conn_state->mode.crtc_hdisplay; 1609*d00abaefSWenping Zhang crtc_state->crtc_rect.h = conn_state->mode.crtc_vdisplay; 1610*d00abaefSWenping Zhang crtc_state->crtc_rect.x = 0; 1611*d00abaefSWenping Zhang crtc_state->crtc_rect.y = 0; 1612*d00abaefSWenping Zhang 1613*d00abaefSWenping Zhang crtc_state->dma_addr = (u32)plat->base; 1614*d00abaefSWenping Zhang crtc_state->xvir = ALIGN(crtc_state->src_rect.w * DRM_ROCKCHIP_FB_BPP, 32) >> 5; 1615*d00abaefSWenping Zhang 1616*d00abaefSWenping Zhang /* 1617*d00abaefSWenping Zhang * For some platforms, such as RK3576, use the win scale instead 1618*d00abaefSWenping Zhang * of the post scale to configure overscan parameters, because the 1619*d00abaefSWenping Zhang * sharp/post scale/split functions are mutually exclusice. 1620*d00abaefSWenping Zhang */ 1621*d00abaefSWenping Zhang if (crtc_state->overscan_by_win_scale) { 1622*d00abaefSWenping Zhang overscan_w = crtc_state->crtc_rect.w * (200 - overscan->left_margin * 2) / 200; 1623*d00abaefSWenping Zhang overscan_h = crtc_state->crtc_rect.h * (200 - overscan->top_margin * 2) / 200; 1624*d00abaefSWenping Zhang 1625*d00abaefSWenping Zhang crtc_x = crtc_state->crtc_rect.x + overscan_w / 2; 1626*d00abaefSWenping Zhang crtc_y = crtc_state->crtc_rect.y + overscan_h / 2; 1627*d00abaefSWenping Zhang crtc_w = crtc_state->crtc_rect.w - overscan_w; 1628*d00abaefSWenping Zhang crtc_h = crtc_state->crtc_rect.h - overscan_h; 1629*d00abaefSWenping Zhang 1630*d00abaefSWenping Zhang crtc_state->crtc_rect.x = crtc_x; 1631*d00abaefSWenping Zhang crtc_state->crtc_rect.y = crtc_y; 1632*d00abaefSWenping Zhang crtc_state->crtc_rect.w = crtc_w; 1633*d00abaefSWenping Zhang crtc_state->crtc_rect.h = crtc_h; 1634*d00abaefSWenping Zhang } 1635*d00abaefSWenping Zhang 1636*d00abaefSWenping Zhang ret = display_check(state); 1637*d00abaefSWenping Zhang if (ret) 1638*d00abaefSWenping Zhang return ret; 1639*d00abaefSWenping Zhang 1640*d00abaefSWenping Zhang ret = display_set_plane(state); 1641*d00abaefSWenping Zhang if (ret) 1642*d00abaefSWenping Zhang return ret; 1643*d00abaefSWenping Zhang 1644*d00abaefSWenping Zhang return 0; 1645*d00abaefSWenping Zhang } 1646*d00abaefSWenping Zhang 1647*d00abaefSWenping Zhang int rockchip_vidconsole_display(struct udevice *dev) 1648*d00abaefSWenping Zhang { 1649*d00abaefSWenping Zhang struct video_priv *uc_priv = dev_get_uclass_priv(dev); 1650*d00abaefSWenping Zhang struct video_uc_platdata *plat = dev_get_uclass_platdata(dev); 1651*d00abaefSWenping Zhang struct display_state *state; 1652*d00abaefSWenping Zhang bool is_init = false; 1653*d00abaefSWenping Zhang u32 fb_size; 1654*d00abaefSWenping Zhang int ret; 1655*d00abaefSWenping Zhang 1656*d00abaefSWenping Zhang uc_priv->xsize = U16_MAX; 1657*d00abaefSWenping Zhang uc_priv->ysize = U16_MAX; 1658*d00abaefSWenping Zhang /* convert bpp 32/16/8 to VIDEO_BPP32/VIDEO_BPP16/VIDEO_BPP8 */ 1659*d00abaefSWenping Zhang uc_priv->bpix = ilog2(DRM_ROCKCHIP_FB_BPP); 1660*d00abaefSWenping Zhang list_for_each_entry(state, &rockchip_display_list, head) { 1661*d00abaefSWenping Zhang ret = display_init(state); 1662*d00abaefSWenping Zhang is_init |= state->is_init; 1663*d00abaefSWenping Zhang if (!state->is_init) 1664*d00abaefSWenping Zhang continue; 1665*d00abaefSWenping Zhang 1666*d00abaefSWenping Zhang /* 1667*d00abaefSWenping Zhang * The default horizontal resolution of video console is the minimum 1668*d00abaefSWenping Zhang * &drm_display_mode.crtc_hdisplay of the VPs, and the vertical 1669*d00abaefSWenping Zhang * resolution is the minimum &drm_display_mode.crtc_vdisplay. 1670*d00abaefSWenping Zhang */ 1671*d00abaefSWenping Zhang if (uc_priv->xsize > state->conn_state.mode.crtc_hdisplay) 1672*d00abaefSWenping Zhang uc_priv->xsize = state->conn_state.mode.crtc_hdisplay; 1673*d00abaefSWenping Zhang 1674*d00abaefSWenping Zhang if (uc_priv->ysize > state->conn_state.mode.crtc_vdisplay) 1675*d00abaefSWenping Zhang uc_priv->ysize = state->conn_state.mode.crtc_vdisplay; 1676*d00abaefSWenping Zhang 1677*d00abaefSWenping Zhang state->vidcon_fb_addr = plat->base; 1678*d00abaefSWenping Zhang } 1679*d00abaefSWenping Zhang if (!is_init) { 1680*d00abaefSWenping Zhang ret = -ENODEV; 1681*d00abaefSWenping Zhang goto display_deinit; 1682*d00abaefSWenping Zhang } 1683*d00abaefSWenping Zhang 1684*d00abaefSWenping Zhang if (CONFIG_ROCKCHIP_VIDCONSOLE_WIDTH > 0 && CONFIG_ROCKCHIP_VIDCONSOLE_HEIGHT > 0) { 1685*d00abaefSWenping Zhang uc_priv->xsize = CONFIG_ROCKCHIP_VIDCONSOLE_WIDTH; 1686*d00abaefSWenping Zhang uc_priv->ysize = CONFIG_ROCKCHIP_VIDCONSOLE_HEIGHT; 1687*d00abaefSWenping Zhang } 1688*d00abaefSWenping Zhang 1689*d00abaefSWenping Zhang fb_size = uc_priv->xsize * uc_priv->ysize * VNBYTES(uc_priv->bpix); 1690*d00abaefSWenping Zhang if (fb_size > CONFIG_ROCKCHIP_VIDCONSOLE_MEM_RESERVED_SIZE_MBYTES * 1024 * 1024) { 1691*d00abaefSWenping Zhang printf("video console fb size [%d Bytes] is over reserved [%d Bytes], then back to show uboot logo\n", 1692*d00abaefSWenping Zhang fb_size, CONFIG_ROCKCHIP_VIDCONSOLE_MEM_RESERVED_SIZE_MBYTES * 1024 * 1024); 1693*d00abaefSWenping Zhang ret = -EINVAL; 1694*d00abaefSWenping Zhang goto display_deinit; 1695*d00abaefSWenping Zhang } 1696*d00abaefSWenping Zhang memset((void *)plat->base, 0, fb_size); 1697*d00abaefSWenping Zhang 1698*d00abaefSWenping Zhang list_for_each_entry(state, &rockchip_display_list, head) { 1699*d00abaefSWenping Zhang if (!state->is_init) 1700*d00abaefSWenping Zhang continue; 1701*d00abaefSWenping Zhang 1702*d00abaefSWenping Zhang ret = vidconsole_init(dev, state); 1703*d00abaefSWenping Zhang if (ret) { 1704*d00abaefSWenping Zhang printf("failed to init video console, then back to show uboot logo\n"); 1705*d00abaefSWenping Zhang goto display_deinit; 1706186f8572SMark Yao } 1707186f8572SMark Yao } 1708186f8572SMark Yao 1709*d00abaefSWenping Zhang list_for_each_entry(state, &rockchip_display_list, head) { 1710*d00abaefSWenping Zhang if (!state->is_init) 1711*d00abaefSWenping Zhang continue; 1712*d00abaefSWenping Zhang 1713*d00abaefSWenping Zhang display_enable(state); 1714*d00abaefSWenping Zhang } 1715*d00abaefSWenping Zhang 1716*d00abaefSWenping Zhang printf("Enable video console mode: resolution[%dx%d] bpp[%d]\n", 1717*d00abaefSWenping Zhang uc_priv->xsize, uc_priv->ysize, DRM_ROCKCHIP_FB_BPP); 1718*d00abaefSWenping Zhang 1719*d00abaefSWenping Zhang return 0; 1720*d00abaefSWenping Zhang 1721*d00abaefSWenping Zhang display_deinit: 1722*d00abaefSWenping Zhang list_for_each_entry(state, &rockchip_display_list, head) { 1723*d00abaefSWenping Zhang state->is_init = false; 1724*d00abaefSWenping Zhang state->vidcon_fb_addr = 0; 1725*d00abaefSWenping Zhang } 1726*d00abaefSWenping Zhang return ret; 1727*d00abaefSWenping Zhang } 1728*d00abaefSWenping Zhang #endif 1729*d00abaefSWenping Zhang 1730a2d2b88eSSandy Huang int rockchip_show_bmp(const char *bmp) 1731186f8572SMark Yao { 1732186f8572SMark Yao struct display_state *s; 1733a2d2b88eSSandy Huang int ret = 0; 1734186f8572SMark Yao 1735186f8572SMark Yao if (!bmp) { 1736186f8572SMark Yao list_for_each_entry(s, &rockchip_display_list, head) 1737186f8572SMark Yao display_disable(s); 1738a2d2b88eSSandy Huang return -ENOENT; 1739186f8572SMark Yao } 1740186f8572SMark Yao 1741186f8572SMark Yao list_for_each_entry(s, &rockchip_display_list, head) { 1742186f8572SMark Yao s->logo.mode = s->charge_logo_mode; 1743186f8572SMark Yao if (load_bmp_logo(&s->logo, bmp)) 1744186f8572SMark Yao continue; 1745a2d2b88eSSandy Huang ret = display_logo(s); 1746186f8572SMark Yao } 1747186f8572SMark Yao 1748a2d2b88eSSandy Huang return ret; 1749a2d2b88eSSandy Huang } 1750a2d2b88eSSandy Huang 1751a2d2b88eSSandy Huang int rockchip_show_logo(void) 1752186f8572SMark Yao { 1753186f8572SMark Yao struct display_state *s; 17541c9572c7SDamon Ding struct display_state *ms = NULL; 1755a2d2b88eSSandy Huang int ret = 0; 17561c9572c7SDamon Ding int count = 0; 1757186f8572SMark Yao 1758186f8572SMark Yao list_for_each_entry(s, &rockchip_display_list, head) { 1759186f8572SMark Yao s->logo.mode = s->logo_mode; 17600fda4887SDamon Ding s->logo.rotate = s->logo_rotate; 17611c9572c7SDamon Ding if (load_bmp_logo(&s->logo, s->ulogo_name)) { 1762186f8572SMark Yao printf("failed to display uboot logo\n"); 17631c9572c7SDamon Ding } else { 1764a2d2b88eSSandy Huang ret = display_logo(s); 17651c9572c7SDamon Ding if (ret == -EAGAIN) 17661c9572c7SDamon Ding ms = s; 17671c9572c7SDamon Ding } 17685eb61944SSandy Huang /* Load kernel bmp in rockchip_display_fixup() later */ 1769186f8572SMark Yao } 1770a2d2b88eSSandy Huang 17711c9572c7SDamon Ding /* 17721c9572c7SDamon Ding * For rk3566, the mirror win must be enabled after the related 17731c9572c7SDamon Ding * source win. If error code is EAGAIN, the mirror win may be 17741c9572c7SDamon Ding * first enabled unexpectedly, and we will move the enabling process 17751c9572c7SDamon Ding * as follows. 17761c9572c7SDamon Ding */ 17771c9572c7SDamon Ding if (ms) { 17781c9572c7SDamon Ding while (count < 5) { 17791c9572c7SDamon Ding ret = display_logo(ms); 17801c9572c7SDamon Ding if (ret != -EAGAIN) 17811c9572c7SDamon Ding break; 17821c9572c7SDamon Ding mdelay(10); 17831c9572c7SDamon Ding count++; 17841c9572c7SDamon Ding } 17851c9572c7SDamon Ding } 17861c9572c7SDamon Ding 1787a2d2b88eSSandy Huang return ret; 1788186f8572SMark Yao } 1789186f8572SMark Yao 179072388c26SDamon Ding int rockchip_vop_dump(const char *cmd) 179172388c26SDamon Ding { 179272388c26SDamon Ding struct display_state *state; 179372388c26SDamon Ding struct crtc_state *crtc_state; 179472388c26SDamon Ding struct rockchip_crtc *crtc; 179572388c26SDamon Ding const struct rockchip_crtc_funcs *crtc_funcs; 179672388c26SDamon Ding int ret = -EINVAL; 179772388c26SDamon Ding 179872388c26SDamon Ding list_for_each_entry(state, &rockchip_display_list, head) { 179972388c26SDamon Ding if (!state->is_init) 180072388c26SDamon Ding continue; 180172388c26SDamon Ding crtc_state = &state->crtc_state; 180272388c26SDamon Ding crtc = crtc_state->crtc; 180372388c26SDamon Ding crtc_funcs = crtc->funcs; 180472388c26SDamon Ding 180572388c26SDamon Ding if (!cmd) 180672388c26SDamon Ding ret = crtc_funcs->active_regs_dump(state); 180772388c26SDamon Ding else if (!strcmp(cmd, "a") || !strcmp(cmd, "all")) 180872388c26SDamon Ding ret = crtc_funcs->regs_dump(state); 180972388c26SDamon Ding if (!ret) 181072388c26SDamon Ding break; 181172388c26SDamon Ding } 181272388c26SDamon Ding 181372388c26SDamon Ding if (ret) 181472388c26SDamon Ding ret = CMD_RET_USAGE; 181572388c26SDamon Ding 181672388c26SDamon Ding return ret; 181772388c26SDamon Ding } 181872388c26SDamon Ding 18191a8d717cSWyon Bi enum { 18201a8d717cSWyon Bi PORT_DIR_IN, 18211a8d717cSWyon Bi PORT_DIR_OUT, 18221a8d717cSWyon Bi }; 18231a8d717cSWyon Bi 182417e6e1a5SChaoyi Chen const struct device_node *rockchip_of_graph_get_port_by_id(ofnode node, int id) 18251a8d717cSWyon Bi { 18260594ce39SZhang Yubing ofnode ports, port; 18271a8d717cSWyon Bi u32 reg; 18281a8d717cSWyon Bi 1829f8e67e0fSWyon Bi ports = ofnode_find_subnode(node, "ports"); 1830f8e67e0fSWyon Bi if (!ofnode_valid(ports)) 18310594ce39SZhang Yubing return NULL; 1832f8e67e0fSWyon Bi 1833f8e67e0fSWyon Bi ofnode_for_each_subnode(port, ports) { 18341a8d717cSWyon Bi if (ofnode_read_u32(port, "reg", ®)) 18351a8d717cSWyon Bi continue; 18361a8d717cSWyon Bi 18370594ce39SZhang Yubing if (reg == id) 1838f8e67e0fSWyon Bi break; 1839f8e67e0fSWyon Bi } 1840f8e67e0fSWyon Bi 18410594ce39SZhang Yubing if (reg == id) 18420594ce39SZhang Yubing return ofnode_to_np(port); 18431a8d717cSWyon Bi 18440594ce39SZhang Yubing return NULL; 18450594ce39SZhang Yubing } 18460594ce39SZhang Yubing 18470594ce39SZhang Yubing static const struct device_node *rockchip_of_graph_get_port_parent(ofnode port) 18480594ce39SZhang Yubing { 18490594ce39SZhang Yubing ofnode parent; 18500594ce39SZhang Yubing int is_ports_node; 18510594ce39SZhang Yubing 18520594ce39SZhang Yubing parent = ofnode_get_parent(port); 18530594ce39SZhang Yubing is_ports_node = strstr(ofnode_to_np(parent)->full_name, "ports") ? 1 : 0; 18540594ce39SZhang Yubing if (is_ports_node) 18550594ce39SZhang Yubing parent = ofnode_get_parent(parent); 18560594ce39SZhang Yubing 18570594ce39SZhang Yubing return ofnode_to_np(parent); 18580594ce39SZhang Yubing } 18590594ce39SZhang Yubing 1860cce9b06aSWyon Bi const struct device_node * 1861cce9b06aSWyon Bi rockchip_of_graph_get_endpoint_by_regs(ofnode node, int port, int endpoint) 18620594ce39SZhang Yubing { 18630594ce39SZhang Yubing const struct device_node *port_node; 18640594ce39SZhang Yubing ofnode ep; 18650594ce39SZhang Yubing u32 reg; 18661a8d717cSWyon Bi 18670594ce39SZhang Yubing port_node = rockchip_of_graph_get_port_by_id(node, port); 18680594ce39SZhang Yubing if (!port_node) 18690594ce39SZhang Yubing return NULL; 18701a8d717cSWyon Bi 18710594ce39SZhang Yubing ofnode_for_each_subnode(ep, np_to_ofnode(port_node)) { 18720594ce39SZhang Yubing if (ofnode_read_u32(ep, "reg", ®)) 18730594ce39SZhang Yubing break; 18740594ce39SZhang Yubing if (reg == endpoint) 1875f8e67e0fSWyon Bi break; 18761a8d717cSWyon Bi } 18771a8d717cSWyon Bi 18780594ce39SZhang Yubing if (!ofnode_valid(ep)) 18790594ce39SZhang Yubing return NULL; 18800594ce39SZhang Yubing 1881cce9b06aSWyon Bi return ofnode_to_np(ep); 1882cce9b06aSWyon Bi } 1883cce9b06aSWyon Bi 1884cce9b06aSWyon Bi static const struct device_node * 1885cce9b06aSWyon Bi rockchip_of_graph_get_remote_node(ofnode node, int port, int endpoint) 1886cce9b06aSWyon Bi { 1887cce9b06aSWyon Bi const struct device_node *ep_node; 1888cce9b06aSWyon Bi ofnode ep; 1889cce9b06aSWyon Bi uint phandle; 1890cce9b06aSWyon Bi 1891cce9b06aSWyon Bi ep_node = rockchip_of_graph_get_endpoint_by_regs(node, port, endpoint); 1892cce9b06aSWyon Bi if (!ep_node) 1893cce9b06aSWyon Bi return NULL; 1894cce9b06aSWyon Bi 1895cce9b06aSWyon Bi if (ofnode_read_u32(np_to_ofnode(ep_node), "remote-endpoint", &phandle)) 18960594ce39SZhang Yubing return NULL; 18970594ce39SZhang Yubing 18980594ce39SZhang Yubing ep = ofnode_get_by_phandle(phandle); 18990594ce39SZhang Yubing if (!ofnode_valid(ep)) 19000594ce39SZhang Yubing return NULL; 19010594ce39SZhang Yubing 19020594ce39SZhang Yubing return ofnode_to_np(ep); 19030594ce39SZhang Yubing } 19040594ce39SZhang Yubing 19050594ce39SZhang Yubing static int rockchip_of_find_panel(struct udevice *dev, struct rockchip_panel **panel) 19060594ce39SZhang Yubing { 19070594ce39SZhang Yubing const struct device_node *ep_node, *panel_node; 19080594ce39SZhang Yubing ofnode panel_ofnode, port; 19090594ce39SZhang Yubing struct udevice *panel_dev; 19100594ce39SZhang Yubing int ret = 0; 19110594ce39SZhang Yubing 19120594ce39SZhang Yubing *panel = NULL; 19130594ce39SZhang Yubing panel_ofnode = dev_read_subnode(dev, "panel"); 19140594ce39SZhang Yubing if (ofnode_valid(panel_ofnode) && ofnode_is_available(panel_ofnode)) { 19150594ce39SZhang Yubing ret = uclass_get_device_by_ofnode(UCLASS_PANEL, panel_ofnode, 19160594ce39SZhang Yubing &panel_dev); 19170594ce39SZhang Yubing if (!ret) 19180594ce39SZhang Yubing goto found; 19190594ce39SZhang Yubing } 19200594ce39SZhang Yubing 19210594ce39SZhang Yubing ep_node = rockchip_of_graph_get_remote_node(dev->node, PORT_DIR_OUT, 0); 19220594ce39SZhang Yubing if (!ep_node) 19230594ce39SZhang Yubing return -ENODEV; 19240594ce39SZhang Yubing 19250594ce39SZhang Yubing port = ofnode_get_parent(np_to_ofnode(ep_node)); 19260594ce39SZhang Yubing if (!ofnode_valid(port)) 19270594ce39SZhang Yubing return -ENODEV; 19280594ce39SZhang Yubing 19290594ce39SZhang Yubing panel_node = rockchip_of_graph_get_port_parent(port); 19300594ce39SZhang Yubing if (!panel_node) 19310594ce39SZhang Yubing return -ENODEV; 19320594ce39SZhang Yubing 19330594ce39SZhang Yubing ret = uclass_get_device_by_ofnode(UCLASS_PANEL, np_to_ofnode(panel_node), &panel_dev); 19340594ce39SZhang Yubing if (!ret) 19350594ce39SZhang Yubing goto found; 19360594ce39SZhang Yubing 19370594ce39SZhang Yubing return -ENODEV; 19380594ce39SZhang Yubing 19390594ce39SZhang Yubing found: 19400594ce39SZhang Yubing *panel = (struct rockchip_panel *)dev_get_driver_data(panel_dev); 19410594ce39SZhang Yubing return 0; 19420594ce39SZhang Yubing } 19430594ce39SZhang Yubing 19440594ce39SZhang Yubing static int rockchip_of_find_bridge(struct udevice *dev, struct rockchip_bridge **bridge) 19450594ce39SZhang Yubing { 19460594ce39SZhang Yubing const struct device_node *ep_node, *bridge_node; 19470594ce39SZhang Yubing ofnode port; 19480594ce39SZhang Yubing struct udevice *bridge_dev; 19490594ce39SZhang Yubing int ret = 0; 19500594ce39SZhang Yubing 19510594ce39SZhang Yubing ep_node = rockchip_of_graph_get_remote_node(dev->node, PORT_DIR_OUT, 0); 19520594ce39SZhang Yubing if (!ep_node) 19530594ce39SZhang Yubing return -ENODEV; 19540594ce39SZhang Yubing 19550594ce39SZhang Yubing port = ofnode_get_parent(np_to_ofnode(ep_node)); 19560594ce39SZhang Yubing if (!ofnode_valid(port)) 19570594ce39SZhang Yubing return -ENODEV; 19580594ce39SZhang Yubing 19590594ce39SZhang Yubing bridge_node = rockchip_of_graph_get_port_parent(port); 19600594ce39SZhang Yubing if (!bridge_node) 19610594ce39SZhang Yubing return -ENODEV; 19620594ce39SZhang Yubing 19630594ce39SZhang Yubing ret = uclass_get_device_by_ofnode(UCLASS_VIDEO_BRIDGE, np_to_ofnode(bridge_node), 19640594ce39SZhang Yubing &bridge_dev); 19650594ce39SZhang Yubing if (!ret) 19660594ce39SZhang Yubing goto found; 19670594ce39SZhang Yubing 19680594ce39SZhang Yubing return -ENODEV; 19690594ce39SZhang Yubing 19700594ce39SZhang Yubing found: 19710594ce39SZhang Yubing *bridge = (struct rockchip_bridge *)dev_get_driver_data(bridge_dev); 19720594ce39SZhang Yubing return 0; 19730594ce39SZhang Yubing } 19740594ce39SZhang Yubing 19750594ce39SZhang Yubing static int rockchip_of_find_panel_or_bridge(struct udevice *dev, struct rockchip_panel **panel, 19760594ce39SZhang Yubing struct rockchip_bridge **bridge) 19770594ce39SZhang Yubing { 19780594ce39SZhang Yubing int ret = 0; 197933c521a7SDamon Ding 198033c521a7SDamon Ding if (*panel) 198133c521a7SDamon Ding return 0; 198233c521a7SDamon Ding 19830594ce39SZhang Yubing *panel = NULL; 19840594ce39SZhang Yubing *bridge = NULL; 19850594ce39SZhang Yubing 19860594ce39SZhang Yubing if (panel) { 19870594ce39SZhang Yubing ret = rockchip_of_find_panel(dev, panel); 19880594ce39SZhang Yubing if (!ret) 19890594ce39SZhang Yubing return 0; 19900594ce39SZhang Yubing } 19910594ce39SZhang Yubing 19920594ce39SZhang Yubing if (ret) { 19930594ce39SZhang Yubing ret = rockchip_of_find_bridge(dev, bridge); 19940594ce39SZhang Yubing if (!ret) 19950594ce39SZhang Yubing ret = rockchip_of_find_panel_or_bridge((*bridge)->dev, panel, 19960594ce39SZhang Yubing &(*bridge)->next_bridge); 19970594ce39SZhang Yubing } 1998f8e67e0fSWyon Bi 1999f8e67e0fSWyon Bi return ret; 20001a8d717cSWyon Bi } 20011a8d717cSWyon Bi 20020594ce39SZhang Yubing static struct rockchip_phy *rockchip_of_find_phy(struct udevice *dev) 20030594ce39SZhang Yubing { 20040594ce39SZhang Yubing struct udevice *phy_dev; 20050594ce39SZhang Yubing int ret; 20060594ce39SZhang Yubing 20070594ce39SZhang Yubing ret = uclass_get_device_by_phandle(UCLASS_PHY, dev, "phys", &phy_dev); 20080594ce39SZhang Yubing if (ret) 20090594ce39SZhang Yubing return NULL; 20100594ce39SZhang Yubing 20110594ce39SZhang Yubing return (struct rockchip_phy *)dev_get_driver_data(phy_dev); 20120594ce39SZhang Yubing } 20130594ce39SZhang Yubing 20140594ce39SZhang Yubing static struct udevice *rockchip_of_find_connector_device(ofnode endpoint) 2015747dfc26SWyon Bi { 2016f8281ef0SWyon Bi ofnode ep, port, ports, conn; 2017f8281ef0SWyon Bi uint phandle; 2018f8281ef0SWyon Bi struct udevice *dev; 2019747dfc26SWyon Bi int ret; 2020747dfc26SWyon Bi 2021f8281ef0SWyon Bi if (ofnode_read_u32(endpoint, "remote-endpoint", &phandle)) 2022f8281ef0SWyon Bi return NULL; 2023f8281ef0SWyon Bi 2024f8281ef0SWyon Bi ep = ofnode_get_by_phandle(phandle); 2025f8281ef0SWyon Bi if (!ofnode_valid(ep) || !ofnode_is_available(ep)) 2026f8281ef0SWyon Bi return NULL; 2027f8281ef0SWyon Bi 2028f8281ef0SWyon Bi port = ofnode_get_parent(ep); 2029747dfc26SWyon Bi if (!ofnode_valid(port)) 2030747dfc26SWyon Bi return NULL; 2031747dfc26SWyon Bi 2032f8281ef0SWyon Bi ports = ofnode_get_parent(port); 2033f8281ef0SWyon Bi if (!ofnode_valid(ports)) 2034747dfc26SWyon Bi return NULL; 2035f8281ef0SWyon Bi 2036f8281ef0SWyon Bi conn = ofnode_get_parent(ports); 2037f8281ef0SWyon Bi if (!ofnode_valid(conn) || !ofnode_is_available(conn)) 2038f8281ef0SWyon Bi return NULL; 2039f8281ef0SWyon Bi 2040f8281ef0SWyon Bi ret = uclass_get_device_by_ofnode(UCLASS_DISPLAY, conn, &dev); 2041f8281ef0SWyon Bi if (ret) 2042f8281ef0SWyon Bi return NULL; 2043f8281ef0SWyon Bi 2044f8281ef0SWyon Bi return dev; 2045747dfc26SWyon Bi } 2046747dfc26SWyon Bi 20470594ce39SZhang Yubing static struct rockchip_connector *rockchip_of_get_connector(ofnode endpoint) 20480594ce39SZhang Yubing { 20490594ce39SZhang Yubing struct rockchip_connector *conn; 20500594ce39SZhang Yubing struct udevice *dev; 20510594ce39SZhang Yubing int ret; 20520594ce39SZhang Yubing 20530594ce39SZhang Yubing dev = rockchip_of_find_connector_device(endpoint); 20540594ce39SZhang Yubing if (!dev) { 20550594ce39SZhang Yubing printf("Warn: can't find connect driver\n"); 20560594ce39SZhang Yubing return NULL; 20570594ce39SZhang Yubing } 20580594ce39SZhang Yubing 20590594ce39SZhang Yubing conn = get_rockchip_connector_by_device(dev); 20600594ce39SZhang Yubing if (!conn) 20610594ce39SZhang Yubing return NULL; 20620594ce39SZhang Yubing ret = rockchip_of_find_panel_or_bridge(dev, &conn->panel, &conn->bridge); 20630594ce39SZhang Yubing if (ret) 20640594ce39SZhang Yubing debug("Warn: no find panel or bridge\n"); 20650594ce39SZhang Yubing 20660594ce39SZhang Yubing conn->phy = rockchip_of_find_phy(dev); 20670594ce39SZhang Yubing 20680594ce39SZhang Yubing return conn; 20690594ce39SZhang Yubing } 20700594ce39SZhang Yubing 2071ecf57f93SZhang Yubing static struct rockchip_connector *rockchip_get_split_connector(struct rockchip_connector *conn) 2072ecf57f93SZhang Yubing { 2073ecf57f93SZhang Yubing char *conn_name; 2074ecf57f93SZhang Yubing struct device_node *split_node; 2075ecf57f93SZhang Yubing struct udevice *split_dev; 2076ecf57f93SZhang Yubing struct rockchip_connector *split_conn; 2077ecf57f93SZhang Yubing int ret; 2078ecf57f93SZhang Yubing 20791e77886eSDamon Ding conn->split_mode = ofnode_read_bool(conn->dev->node, "split-mode"); 20801e77886eSDamon Ding conn->dual_channel_mode = ofnode_read_bool(conn->dev->node, "dual-channel"); 20811e77886eSDamon Ding if (!conn->split_mode && !conn->dual_channel_mode) 2082ecf57f93SZhang Yubing return NULL; 2083ecf57f93SZhang Yubing 2084ecf57f93SZhang Yubing switch (conn->type) { 2085ecf57f93SZhang Yubing case DRM_MODE_CONNECTOR_DisplayPort: 2086ecf57f93SZhang Yubing conn_name = "dp"; 2087ecf57f93SZhang Yubing break; 2088ecf57f93SZhang Yubing case DRM_MODE_CONNECTOR_eDP: 2089ecf57f93SZhang Yubing conn_name = "edp"; 2090ecf57f93SZhang Yubing break; 2091ecf57f93SZhang Yubing case DRM_MODE_CONNECTOR_HDMIA: 2092ecf57f93SZhang Yubing conn_name = "hdmi"; 2093ecf57f93SZhang Yubing break; 209417e6e1a5SChaoyi Chen case DRM_MODE_CONNECTOR_LVDS: 209517e6e1a5SChaoyi Chen conn_name = "lvds"; 209617e6e1a5SChaoyi Chen break; 2097ecf57f93SZhang Yubing default: 2098ecf57f93SZhang Yubing return NULL; 2099ecf57f93SZhang Yubing } 2100ecf57f93SZhang Yubing 2101ecf57f93SZhang Yubing split_node = of_alias_get_dev(conn_name, !conn->id); 2102ecf57f93SZhang Yubing if (!split_node || !of_device_is_available(split_node)) 2103ecf57f93SZhang Yubing return NULL; 2104ecf57f93SZhang Yubing 2105ecf57f93SZhang Yubing ret = uclass_get_device_by_ofnode(UCLASS_DISPLAY, np_to_ofnode(split_node), &split_dev); 2106ecf57f93SZhang Yubing if (ret) 2107ecf57f93SZhang Yubing return NULL; 2108ecf57f93SZhang Yubing 2109ecf57f93SZhang Yubing split_conn = get_rockchip_connector_by_device(split_dev); 2110ecf57f93SZhang Yubing if (!split_conn) 2111ecf57f93SZhang Yubing return NULL; 2112ecf57f93SZhang Yubing ret = rockchip_of_find_panel_or_bridge(split_dev, &split_conn->panel, &split_conn->bridge); 2113ecf57f93SZhang Yubing if (ret) 2114ecf57f93SZhang Yubing debug("Warn: no find panel or bridge\n"); 2115ecf57f93SZhang Yubing 2116ecf57f93SZhang Yubing split_conn->phy = rockchip_of_find_phy(split_dev); 21171e77886eSDamon Ding split_conn->split_mode = conn->split_mode; 21181e77886eSDamon Ding split_conn->dual_channel_mode = conn->dual_channel_mode; 2119ecf57f93SZhang Yubing 2120ecf57f93SZhang Yubing return split_conn; 2121ecf57f93SZhang Yubing } 2122ecf57f93SZhang Yubing 21236eff7620SSandy Huang static bool rockchip_get_display_path_status(ofnode endpoint) 21246eff7620SSandy Huang { 21256eff7620SSandy Huang ofnode ep; 21266eff7620SSandy Huang uint phandle; 21276eff7620SSandy Huang 21286eff7620SSandy Huang if (ofnode_read_u32(endpoint, "remote-endpoint", &phandle)) 21296eff7620SSandy Huang return false; 21306eff7620SSandy Huang 21316eff7620SSandy Huang ep = ofnode_get_by_phandle(phandle); 21326eff7620SSandy Huang if (!ofnode_valid(ep) || !ofnode_is_available(ep)) 21336eff7620SSandy Huang return false; 21346eff7620SSandy Huang 21356eff7620SSandy Huang return true; 21366eff7620SSandy Huang } 21376eff7620SSandy Huang 213820618a45SGuochun Huang #if defined(CONFIG_ROCKCHIP_RK3568) 213920618a45SGuochun Huang static int rockchip_display_fixup_dts(void *blob) 214020618a45SGuochun Huang { 214120618a45SGuochun Huang ofnode route_node, route_subnode, conn_ep, conn_port; 214220618a45SGuochun Huang const struct device_node *route_sub_devnode; 214320618a45SGuochun Huang const struct device_node *ep_node, *conn_ep_dev_node; 214420618a45SGuochun Huang u32 phandle; 214520618a45SGuochun Huang int conn_ep_offset; 214620618a45SGuochun Huang const char *route_sub_path, *path; 214720618a45SGuochun Huang 214820618a45SGuochun Huang /* Don't go further if new variant after 214920618a45SGuochun Huang * reading PMUGRF_SOC_CON15 215020618a45SGuochun Huang */ 215120618a45SGuochun Huang if ((readl(0xfdc20100) & GENMASK(15, 14))) 215220618a45SGuochun Huang return 0; 215320618a45SGuochun Huang 215420618a45SGuochun Huang route_node = ofnode_path("/display-subsystem/route"); 215520618a45SGuochun Huang if (!ofnode_valid(route_node)) 215620618a45SGuochun Huang return -EINVAL; 215720618a45SGuochun Huang 215820618a45SGuochun Huang ofnode_for_each_subnode(route_subnode, route_node) { 215920618a45SGuochun Huang if (!ofnode_is_available(route_subnode)) 216020618a45SGuochun Huang continue; 216120618a45SGuochun Huang 216220618a45SGuochun Huang route_sub_devnode = ofnode_to_np(route_subnode); 216320618a45SGuochun Huang route_sub_path = route_sub_devnode->full_name; 216420618a45SGuochun Huang if (!strstr(ofnode_get_name(route_subnode), "dsi") && 216520618a45SGuochun Huang !strstr(ofnode_get_name(route_subnode), "edp")) 216620618a45SGuochun Huang return 0; 216720618a45SGuochun Huang 216820618a45SGuochun Huang phandle = ofnode_read_u32_default(route_subnode, "connect", -1); 216920618a45SGuochun Huang if (phandle < 0) { 217020618a45SGuochun Huang printf("Warn: can't find connect node's handle\n"); 217120618a45SGuochun Huang continue; 217220618a45SGuochun Huang } 217320618a45SGuochun Huang 217420618a45SGuochun Huang ep_node = of_find_node_by_phandle(phandle); 217520618a45SGuochun Huang if (!ofnode_valid(np_to_ofnode(ep_node))) { 217620618a45SGuochun Huang printf("Warn: can't find endpoint node from phandle\n"); 217720618a45SGuochun Huang continue; 217820618a45SGuochun Huang } 217920618a45SGuochun Huang 218020618a45SGuochun Huang ofnode_read_u32(np_to_ofnode(ep_node), "remote-endpoint", &phandle); 218120618a45SGuochun Huang conn_ep = ofnode_get_by_phandle(phandle); 218220618a45SGuochun Huang if (!ofnode_valid(conn_ep) || !ofnode_is_available(conn_ep)) 218320618a45SGuochun Huang return -ENODEV; 218420618a45SGuochun Huang 218520618a45SGuochun Huang conn_port = ofnode_get_parent(conn_ep); 218620618a45SGuochun Huang if (!ofnode_valid(conn_port)) 218720618a45SGuochun Huang return -ENODEV; 218820618a45SGuochun Huang 218920618a45SGuochun Huang ofnode_for_each_subnode(conn_ep, conn_port) { 219020618a45SGuochun Huang conn_ep_dev_node = ofnode_to_np(conn_ep); 219120618a45SGuochun Huang path = conn_ep_dev_node->full_name; 219220618a45SGuochun Huang ofnode_read_u32(conn_ep, "remote-endpoint", &phandle); 219320618a45SGuochun Huang conn_ep_offset = fdt_path_offset(blob, path); 219420618a45SGuochun Huang 219520618a45SGuochun Huang if (!ofnode_is_available(conn_ep) && 219620618a45SGuochun Huang strstr(ofnode_get_name(conn_ep), "endpoint@0")) { 219720618a45SGuochun Huang do_fixup_by_path_u32(blob, route_sub_path, 219820618a45SGuochun Huang "connect", phandle, 1); 219920618a45SGuochun Huang fdt_status_okay(blob, conn_ep_offset); 220020618a45SGuochun Huang 220120618a45SGuochun Huang } else if (ofnode_is_available(conn_ep) && 220220618a45SGuochun Huang strstr(ofnode_get_name(conn_ep), "endpoint@1")) { 220320618a45SGuochun Huang fdt_status_disabled(blob, conn_ep_offset); 220420618a45SGuochun Huang } 220520618a45SGuochun Huang } 220620618a45SGuochun Huang } 220720618a45SGuochun Huang 220820618a45SGuochun Huang return 0; 220920618a45SGuochun Huang } 221020618a45SGuochun Huang #endif 221120618a45SGuochun Huang 2212186f8572SMark Yao static int rockchip_display_probe(struct udevice *dev) 2213186f8572SMark Yao { 2214186f8572SMark Yao struct video_priv *uc_priv = dev_get_uclass_priv(dev); 2215186f8572SMark Yao struct video_uc_platdata *plat = dev_get_uclass_platdata(dev); 2216186f8572SMark Yao const void *blob = gd->fdt_blob; 2217e2bce6e4SKever Yang int phandle; 22180594ce39SZhang Yubing struct udevice *crtc_dev; 22192a48727aSAlgea Cao struct rockchip_crtc *crtc; 2220ecf57f93SZhang Yubing struct rockchip_connector *conn, *split_conn; 2221186f8572SMark Yao struct display_state *s; 2222186f8572SMark Yao const char *name; 2223186f8572SMark Yao int ret; 22242bfb6166SSandy Huang ofnode node, route_node, timing_node; 2225cdb300bdSSandy Huang struct device_node *port_node, *vop_node, *ep_node, *port_parent_node; 22262a48727aSAlgea Cao struct public_phy_data *data; 2227cdb300bdSSandy Huang bool is_ports_node = false; 2228186f8572SMark Yao 222920618a45SGuochun Huang #if defined(CONFIG_ROCKCHIP_RK3568) 223020618a45SGuochun Huang rockchip_display_fixup_dts((void *)blob); 223120618a45SGuochun Huang #endif 2232186f8572SMark Yao /* Before relocation we don't need to do anything */ 2233186f8572SMark Yao if (!(gd->flags & GD_FLG_RELOC)) 2234186f8572SMark Yao return 0; 22352a48727aSAlgea Cao 22362a48727aSAlgea Cao data = malloc(sizeof(struct public_phy_data)); 22372a48727aSAlgea Cao if (!data) { 22382a48727aSAlgea Cao printf("failed to alloc phy data\n"); 22392a48727aSAlgea Cao return -ENOMEM; 22402a48727aSAlgea Cao } 22412a48727aSAlgea Cao data->phy_init = false; 22422a48727aSAlgea Cao 22434b8c2ef1SMark Yao init_display_buffer(plat->base); 2244186f8572SMark Yao 2245e2bce6e4SKever Yang route_node = dev_read_subnode(dev, "route"); 2246e2bce6e4SKever Yang if (!ofnode_valid(route_node)) 2247e2bce6e4SKever Yang return -ENODEV; 2248186f8572SMark Yao 2249e2bce6e4SKever Yang ofnode_for_each_subnode(node, route_node) { 22501e44acfcSWyon Bi if (!ofnode_is_available(node)) 22511e44acfcSWyon Bi continue; 2252e2bce6e4SKever Yang phandle = ofnode_read_u32_default(node, "connect", -1); 2253186f8572SMark Yao if (phandle < 0) { 2254e2bce6e4SKever Yang printf("Warn: can't find connect node's handle\n"); 2255186f8572SMark Yao continue; 2256186f8572SMark Yao } 2257e2bce6e4SKever Yang ep_node = of_find_node_by_phandle(phandle); 2258e2bce6e4SKever Yang if (!ofnode_valid(np_to_ofnode(ep_node))) { 2259e2bce6e4SKever Yang printf("Warn: can't find endpoint node from phandle\n"); 2260186f8572SMark Yao continue; 2261186f8572SMark Yao } 2262e2bce6e4SKever Yang port_node = of_get_parent(ep_node); 2263e2bce6e4SKever Yang if (!ofnode_valid(np_to_ofnode(port_node))) { 2264e2bce6e4SKever Yang printf("Warn: can't find port node from phandle\n"); 2265186f8572SMark Yao continue; 2266186f8572SMark Yao } 2267cdb300bdSSandy Huang 2268cdb300bdSSandy Huang port_parent_node = of_get_parent(port_node); 2269cdb300bdSSandy Huang if (!ofnode_valid(np_to_ofnode(port_parent_node))) { 2270cdb300bdSSandy Huang printf("Warn: can't find port parent node from phandle\n"); 2271cdb300bdSSandy Huang continue; 2272cdb300bdSSandy Huang } 2273cdb300bdSSandy Huang 2274cdb300bdSSandy Huang is_ports_node = strstr(port_parent_node->full_name, "ports") ? 1 : 0; 2275cdb300bdSSandy Huang if (is_ports_node) { 2276cdb300bdSSandy Huang vop_node = of_get_parent(port_parent_node); 2277e2bce6e4SKever Yang if (!ofnode_valid(np_to_ofnode(vop_node))) { 2278e2bce6e4SKever Yang printf("Warn: can't find crtc node from phandle\n"); 2279e2bce6e4SKever Yang continue; 2280e2bce6e4SKever Yang } 2281cdb300bdSSandy Huang } else { 2282cdb300bdSSandy Huang vop_node = port_parent_node; 2283cdb300bdSSandy Huang } 2284cdb300bdSSandy Huang 2285e2bce6e4SKever Yang ret = uclass_get_device_by_ofnode(UCLASS_VIDEO_CRTC, 2286e2bce6e4SKever Yang np_to_ofnode(vop_node), 2287e2bce6e4SKever Yang &crtc_dev); 2288186f8572SMark Yao if (ret) { 2289335adcb5SKever Yang printf("Warn: can't find crtc driver %d\n", ret); 2290186f8572SMark Yao continue; 2291186f8572SMark Yao } 22922a48727aSAlgea Cao crtc = (struct rockchip_crtc *)dev_get_driver_data(crtc_dev); 2293186f8572SMark Yao 22940594ce39SZhang Yubing conn = rockchip_of_get_connector(np_to_ofnode(ep_node)); 22950594ce39SZhang Yubing if (!conn) { 22960594ce39SZhang Yubing printf("Warn: can't get connect driver\n"); 2297186f8572SMark Yao continue; 2298186f8572SMark Yao } 2299ecf57f93SZhang Yubing split_conn = rockchip_get_split_connector(conn); 2300747dfc26SWyon Bi 2301186f8572SMark Yao s = malloc(sizeof(*s)); 2302186f8572SMark Yao if (!s) 23034b8c2ef1SMark Yao continue; 2304186f8572SMark Yao 2305186f8572SMark Yao memset(s, 0, sizeof(*s)); 2306186f8572SMark Yao 2307186f8572SMark Yao INIT_LIST_HEAD(&s->head); 230854fc9addSSandy Huang ret = ofnode_read_string_index(node, "logo,uboot", 0, &name); 230954fc9addSSandy Huang if (!ret) 231054fc9addSSandy Huang memcpy(s->ulogo_name, name, strlen(name)); 231154fc9addSSandy Huang ret = ofnode_read_string_index(node, "logo,kernel", 0, &name); 231254fc9addSSandy Huang if (!ret) 231354fc9addSSandy Huang memcpy(s->klogo_name, name, strlen(name)); 2314e2bce6e4SKever Yang ret = ofnode_read_string_index(node, "logo,mode", 0, &name); 2315186f8572SMark Yao if (!strcmp(name, "fullscreen")) 2316186f8572SMark Yao s->logo_mode = ROCKCHIP_DISPLAY_FULLSCREEN; 2317186f8572SMark Yao else 2318186f8572SMark Yao s->logo_mode = ROCKCHIP_DISPLAY_CENTER; 2319e2bce6e4SKever Yang ret = ofnode_read_string_index(node, "charge_logo,mode", 0, &name); 2320186f8572SMark Yao if (!strcmp(name, "fullscreen")) 2321186f8572SMark Yao s->charge_logo_mode = ROCKCHIP_DISPLAY_FULLSCREEN; 2322186f8572SMark Yao else 2323186f8572SMark Yao s->charge_logo_mode = ROCKCHIP_DISPLAY_CENTER; 2324186f8572SMark Yao 23250fda4887SDamon Ding s->logo_rotate = ofnode_read_u32_default(node, "logo,rotate", 0); 23260fda4887SDamon Ding 23272bfb6166SSandy Huang s->force_output = ofnode_read_bool(node, "force-output"); 23282bfb6166SSandy Huang 23292bfb6166SSandy Huang if (s->force_output) { 23302bfb6166SSandy Huang timing_node = ofnode_find_subnode(node, "force_timing"); 233113f658dcSDamon Ding ret = display_get_force_timing_from_dts(timing_node, 233213f658dcSDamon Ding &s->force_mode, 233313f658dcSDamon Ding &s->conn_state.bus_flags); 23342bfb6166SSandy Huang if (ofnode_read_u32(node, "force-bus-format", &s->force_bus_format)) 23352bfb6166SSandy Huang s->force_bus_format = MEDIA_BUS_FMT_RGB888_1X24; 23362bfb6166SSandy Huang } 23372bfb6166SSandy Huang 2338186f8572SMark Yao s->blob = blob; 2339186f8572SMark Yao s->conn_state.connector = conn; 23400594ce39SZhang Yubing s->conn_state.secondary = NULL; 23410594ce39SZhang Yubing s->conn_state.type = conn->type; 2342ecf57f93SZhang Yubing if (split_conn) { 2343ecf57f93SZhang Yubing s->conn_state.secondary = split_conn; 2344ecf57f93SZhang Yubing s->conn_state.output_flags |= ROCKCHIP_OUTPUT_DUAL_CHANNEL_LEFT_RIGHT_MODE; 2345ecf57f93SZhang Yubing s->conn_state.output_flags |= conn->id ? ROCKCHIP_OUTPUT_DATA_SWAP : 0; 2346ecf57f93SZhang Yubing } 23478a2a3a29SSandy Huang s->conn_state.overscan.left_margin = 100; 23488a2a3a29SSandy Huang s->conn_state.overscan.right_margin = 100; 23498a2a3a29SSandy Huang s->conn_state.overscan.top_margin = 100; 23508a2a3a29SSandy Huang s->conn_state.overscan.bottom_margin = 100; 2351e2bce6e4SKever Yang s->crtc_state.node = np_to_ofnode(vop_node); 23520669ab1fSDamon Ding s->crtc_state.port_node = port_node; 2353186f8572SMark Yao s->crtc_state.dev = crtc_dev; 2354186f8572SMark Yao s->crtc_state.crtc = crtc; 2355ed797993SDamon Ding s->crtc_state.crtc_id = get_crtc_id(np_to_ofnode(ep_node), is_ports_node); 2356e2bce6e4SKever Yang s->node = node; 23576eff7620SSandy Huang 23586eff7620SSandy Huang if (is_ports_node) { /* only vop2 will get into here */ 23596eff7620SSandy Huang ofnode vp_node = np_to_ofnode(port_node); 23606eff7620SSandy Huang static bool get_plane_mask_from_dts; 23616eff7620SSandy Huang 2362a5afbabdSSandy Huang s->crtc_state.ports_node = port_parent_node; 23636eff7620SSandy Huang if (!get_plane_mask_from_dts) { 23646eff7620SSandy Huang ofnode vp_sub_node; 23656eff7620SSandy Huang int vp_id = 0; 23666eff7620SSandy Huang bool vp_enable = false; 23676eff7620SSandy Huang 23686eff7620SSandy Huang ofnode_for_each_subnode(vp_node, np_to_ofnode(port_parent_node)) { 23696eff7620SSandy Huang vp_id = ofnode_read_u32_default(vp_node, "reg", 0); 2370c2b1fe35SDamon Ding 2371c2b1fe35SDamon Ding s->crtc_state.crtc->vps[vp_id].xmirror_en = 2372c2b1fe35SDamon Ding ofnode_read_bool(vp_node, "xmirror-enable"); 2373c2b1fe35SDamon Ding 23746fea745cSDamon Ding s->crtc_state.crtc->vps[vp_id].primary_plane_id = -1; 2375ee008497SSandy Huang 2376b4fa21deSDamon Ding /* 2377b4fa21deSDamon Ding * Cursor plane can be assigned and then fixed up to DTS 2378b4fa21deSDamon Ding * without the specific plane mask. 2379b4fa21deSDamon Ding */ 2380b4fa21deSDamon Ding s->crtc_state.crtc->vps[vp_id].cursor_plane_id = 2381b4fa21deSDamon Ding ofnode_read_u32_default(vp_node, "cursor-win-id", -1); 2382b4fa21deSDamon Ding 2383b4fa21deSDamon Ding s->crtc_state.crtc->vps[vp_id].plane_mask = 2384b4fa21deSDamon Ding ofnode_read_u32_default(vp_node, "rockchip,plane-mask", 0); 2385b4fa21deSDamon Ding if (s->crtc_state.crtc->vps[vp_id].plane_mask) { 23866eff7620SSandy Huang s->crtc_state.crtc->assign_plane |= true; 23875fc2b656SDamon Ding s->crtc_state.crtc->vps[vp_id].primary_plane_id = 23884bc0811dSDamon Ding ofnode_read_u32_default(vp_node, "rockchip,primary-plane", -1); 2389b4fa21deSDamon Ding printf("get vp%d plane mask:0x%x, primary id:%d, cursor id:%d, from dts\n", 23908b1fe597SSandy Huang vp_id, 23918b1fe597SSandy Huang s->crtc_state.crtc->vps[vp_id].plane_mask, 23924bc0811dSDamon Ding (int8_t)s->crtc_state.crtc->vps[vp_id].primary_plane_id, 2393b4fa21deSDamon Ding (int8_t)s->crtc_state.crtc->vps[vp_id].cursor_plane_id); 23946eff7620SSandy Huang } 23956eff7620SSandy Huang 23966eff7620SSandy Huang /* To check current vp status */ 23976eff7620SSandy Huang vp_enable = false; 23986eff7620SSandy Huang ofnode_for_each_subnode(vp_sub_node, vp_node) 23996eff7620SSandy Huang vp_enable |= rockchip_get_display_path_status(vp_sub_node); 24006eff7620SSandy Huang s->crtc_state.crtc->vps[vp_id].enable = vp_enable; 24016eff7620SSandy Huang } 24026eff7620SSandy Huang get_plane_mask_from_dts = true; 24036eff7620SSandy Huang } 24046eff7620SSandy Huang } 24051a8d717cSWyon Bi 24060675a2a4SDamon Ding get_crtc_mcu_mode(&s->crtc_state, port_node, is_ports_node); 2407186f8572SMark Yao 2408289af5f4SSandy Huang ret = ofnode_read_u32_default(s->crtc_state.node, 2409289af5f4SSandy Huang "rockchip,dual-channel-swap", 0); 2410289af5f4SSandy Huang s->crtc_state.dual_channel_swap = ret; 24114b8c2ef1SMark Yao 24120594ce39SZhang Yubing if (connector_phy_init(conn, data)) { 2413e2bce6e4SKever Yang printf("Warn: Failed to init phy drivers\n"); 24144b8c2ef1SMark Yao free(s); 24154b8c2ef1SMark Yao continue; 24164b8c2ef1SMark Yao } 2417186f8572SMark Yao list_add_tail(&s->head, &rockchip_display_list); 2418186f8572SMark Yao } 2419186f8572SMark Yao 24204b8c2ef1SMark Yao if (list_empty(&rockchip_display_list)) { 24215cfabef4SWyon Bi debug("Failed to found available display route\n"); 24224b8c2ef1SMark Yao return -ENODEV; 24234b8c2ef1SMark Yao } 242450a9508eSSandy Huang rockchip_get_baseparameter(); 242558c17f51SSandy Huang display_pre_init(); 24264b8c2ef1SMark Yao 2427186f8572SMark Yao uc_priv->xsize = DRM_ROCKCHIP_FB_WIDTH; 2428186f8572SMark Yao uc_priv->ysize = DRM_ROCKCHIP_FB_HEIGHT; 2429186f8572SMark Yao uc_priv->bpix = VIDEO_BPP32; 2430186f8572SMark Yao 2431*d00abaefSWenping Zhang #ifdef CONFIG_ROCKCHIP_VIDCONSOLE 2432*d00abaefSWenping Zhang rockchip_vidconsole_display(dev); 2433186f8572SMark Yao video_set_flush_dcache(dev, true); 24344b8c2ef1SMark Yao #endif 2435186f8572SMark Yao return 0; 24364b8c2ef1SMark Yao } 2437186f8572SMark Yao 2438186f8572SMark Yao void rockchip_display_fixup(void *blob) 2439186f8572SMark Yao { 2440186f8572SMark Yao const struct rockchip_connector_funcs *conn_funcs; 2441186f8572SMark Yao const struct rockchip_crtc_funcs *crtc_funcs; 24420594ce39SZhang Yubing struct rockchip_connector *conn; 2443186f8572SMark Yao const struct rockchip_crtc *crtc; 2444186f8572SMark Yao struct display_state *s; 2445694afdc8SKever Yang int offset; 24469764efebSDamon Ding int ret; 244751619d03SKever Yang const struct device_node *np; 244851619d03SKever Yang const char *path; 24496027c871SZhang Yubing const char *cacm_header; 2450882f8a2dSSandy Huang u64 aligned_memory_size; 2451*d00abaefSWenping Zhang ulong vidcon_fb_addr = 0; 24527543e826SDamon Ding bool is_logo_init = 0; 2453186f8572SMark Yao 24545eb61944SSandy Huang if (fdt_node_offset_by_compatible(blob, 0, "rockchip,drm-logo") >= 0) { 24559764efebSDamon Ding list_for_each_entry(s, &rockchip_display_list, head) { 24567543e826SDamon Ding if (s->is_init) { 24579764efebSDamon Ding ret = load_bmp_logo(&s->logo, s->klogo_name); 24589764efebSDamon Ding if (ret < 0) { 24599764efebSDamon Ding s->is_klogo_valid = false; 24607543e826SDamon Ding printf("VP%d fail to load kernel logo\n", 24617543e826SDamon Ding s->crtc_state.crtc_id); 24629764efebSDamon Ding } else { 24639764efebSDamon Ding s->is_klogo_valid = true; 24649764efebSDamon Ding } 2465*d00abaefSWenping Zhang vidcon_fb_addr = s->vidcon_fb_addr; 24669764efebSDamon Ding } 24677543e826SDamon Ding is_logo_init |= s->is_init; 24687543e826SDamon Ding } 24697543e826SDamon Ding 24707543e826SDamon Ding if (!is_logo_init) { 24717543e826SDamon Ding printf("The display is not initialized, skip display fixup\n"); 24727543e826SDamon Ding return; 24737543e826SDamon Ding } 2474ac6274b3SWyon Bi 2475ac6274b3SWyon Bi if (!get_display_size()) 2476ac6274b3SWyon Bi return; 2477ac6274b3SWyon Bi 2478*d00abaefSWenping Zhang if (vidcon_fb_addr) { 2479*d00abaefSWenping Zhang aligned_memory_size = (u64)ALIGN(memory_end - vidcon_fb_addr, align_size); 2480*d00abaefSWenping Zhang offset = fdt_update_reserved_memory(blob, "rockchip,drm-logo", 2481*d00abaefSWenping Zhang (u64)vidcon_fb_addr, 2482*d00abaefSWenping Zhang aligned_memory_size); 2483*d00abaefSWenping Zhang } else { 2484882f8a2dSSandy Huang aligned_memory_size = (u64)ALIGN(get_display_size(), align_size); 248551619d03SKever Yang offset = fdt_update_reserved_memory(blob, "rockchip,drm-logo", 2486186f8572SMark Yao (u64)memory_start, 2487882f8a2dSSandy Huang aligned_memory_size); 2488*d00abaefSWenping Zhang } 24895eb61944SSandy Huang if (offset < 0) 24905eb61944SSandy Huang printf("failed to reserve drm-loader-logo memory\n"); 24916414e3bcSSandy Huang 2492882f8a2dSSandy Huang if (get_cubic_memory_size()) { 2493882f8a2dSSandy Huang aligned_memory_size = (u64)ALIGN(get_cubic_memory_size(), align_size); 24946414e3bcSSandy Huang offset = fdt_update_reserved_memory(blob, "rockchip,drm-cubic-lut", 24956414e3bcSSandy Huang (u64)cubic_lut_memory_start, 2496882f8a2dSSandy Huang aligned_memory_size); 24976414e3bcSSandy Huang if (offset < 0) 24986414e3bcSSandy Huang printf("failed to reserve drm-cubic-lut memory\n"); 2499882f8a2dSSandy Huang } 25005eb61944SSandy Huang } else { 25015eb61944SSandy Huang printf("can't found rockchip,drm-logo, use rockchip,fb-logo\n"); 2502694afdc8SKever Yang /* Compatible with rkfb display, only need reserve memory */ 2503694afdc8SKever Yang offset = fdt_update_reserved_memory(blob, "rockchip,fb-logo", 2504694afdc8SKever Yang (u64)memory_start, 25055eb61944SSandy Huang MEMORY_POOL_SIZE); 2506694afdc8SKever Yang if (offset < 0) 25075eb61944SSandy Huang printf("failed to reserve fb-loader-logo memory\n"); 25085eb61944SSandy Huang else 25095eb61944SSandy Huang list_for_each_entry(s, &rockchip_display_list, head) 25105eb61944SSandy Huang load_kernel_bmp_logo(&s->logo, s->klogo_name); 2511186f8572SMark Yao return; 2512186f8572SMark Yao } 2513186f8572SMark Yao 2514186f8572SMark Yao list_for_each_entry(s, &rockchip_display_list, head) { 2515fc24a3b1SDamon Ding /* 2516fc24a3b1SDamon Ding * If plane mask is not set in dts, fixup dts to assign it 2517fc24a3b1SDamon Ding * whether crtc is initialized or not. 2518fc24a3b1SDamon Ding */ 2519fc24a3b1SDamon Ding if (s->crtc_state.crtc->funcs->fixup_dts && !s->crtc_state.crtc->assign_plane) 2520fc24a3b1SDamon Ding s->crtc_state.crtc->funcs->fixup_dts(s, blob); 2521fc24a3b1SDamon Ding 25229764efebSDamon Ding if (!s->is_init || !s->is_klogo_valid) 25231867b356SDamon Ding continue; 25241867b356SDamon Ding 2525186f8572SMark Yao conn = s->conn_state.connector; 2526186f8572SMark Yao if (!conn) 2527186f8572SMark Yao continue; 2528186f8572SMark Yao conn_funcs = conn->funcs; 2529186f8572SMark Yao if (!conn_funcs) { 2530186f8572SMark Yao printf("failed to get exist connector\n"); 2531186f8572SMark Yao continue; 2532186f8572SMark Yao } 2533186f8572SMark Yao 2534186f8572SMark Yao crtc = s->crtc_state.crtc; 2535186f8572SMark Yao if (!crtc) 2536186f8572SMark Yao continue; 2537186f8572SMark Yao 2538186f8572SMark Yao crtc_funcs = crtc->funcs; 2539186f8572SMark Yao if (!crtc_funcs) { 2540186f8572SMark Yao printf("failed to get exist crtc\n"); 2541186f8572SMark Yao continue; 2542186f8572SMark Yao } 2543186f8572SMark Yao 254451619d03SKever Yang np = ofnode_to_np(s->node); 254551619d03SKever Yang path = np->full_name; 254651619d03SKever Yang fdt_increase_size(blob, 0x400); 2547186f8572SMark Yao #define FDT_SET_U32(name, val) \ 2548186f8572SMark Yao do_fixup_by_path_u32(blob, path, name, val, 1); 2549186f8572SMark Yao 2550*d00abaefSWenping Zhang if (vidcon_fb_addr) 2551*d00abaefSWenping Zhang offset = s->logo.offset + (u32)(unsigned long)s->logo.mem - vidcon_fb_addr; 2552*d00abaefSWenping Zhang else 2553*d00abaefSWenping Zhang offset = s->logo.offset + (u32)(unsigned long)s->logo.mem - memory_start; 2554186f8572SMark Yao FDT_SET_U32("logo,offset", offset); 2555186f8572SMark Yao FDT_SET_U32("logo,width", s->logo.width); 2556186f8572SMark Yao FDT_SET_U32("logo,height", s->logo.height); 2557186f8572SMark Yao FDT_SET_U32("logo,bpp", s->logo.bpp); 2558186f8572SMark Yao FDT_SET_U32("logo,ymirror", s->logo.ymirror); 25593fb05486SAlgea Cao FDT_SET_U32("video,clock", s->conn_state.mode.clock); 2560186f8572SMark Yao FDT_SET_U32("video,hdisplay", s->conn_state.mode.hdisplay); 2561186f8572SMark Yao FDT_SET_U32("video,vdisplay", s->conn_state.mode.vdisplay); 2562f11b858fSSandy Huang FDT_SET_U32("video,crtc_hsync_end", s->conn_state.mode.crtc_hsync_end); 2563f11b858fSSandy Huang FDT_SET_U32("video,crtc_vsync_end", s->conn_state.mode.crtc_vsync_end); 2564186f8572SMark Yao FDT_SET_U32("video,vrefresh", 2565186f8572SMark Yao drm_mode_vrefresh(&s->conn_state.mode)); 25668a2a3a29SSandy Huang FDT_SET_U32("video,flags", s->conn_state.mode.flags); 256794d85f7bSSandy Huang FDT_SET_U32("video,aspect_ratio", s->conn_state.mode.picture_aspect_ratio); 25688a2a3a29SSandy Huang FDT_SET_U32("overscan,left_margin", s->conn_state.overscan.left_margin); 25698a2a3a29SSandy Huang FDT_SET_U32("overscan,right_margin", s->conn_state.overscan.right_margin); 25708a2a3a29SSandy Huang FDT_SET_U32("overscan,top_margin", s->conn_state.overscan.top_margin); 25718a2a3a29SSandy Huang FDT_SET_U32("overscan,bottom_margin", s->conn_state.overscan.bottom_margin); 2572668e6278SDamon Ding FDT_SET_U32("overscan,win_scale", s->crtc_state.overscan_by_win_scale); 25736414e3bcSSandy Huang 2574ac500a1fSSandy Huang if (s->conn_state.disp_info) { 25756027c871SZhang Yubing cacm_header = (const char*)&s->conn_state.disp_info->cacm_header; 25766027c871SZhang Yubing 2577ac500a1fSSandy Huang FDT_SET_U32("bcsh,brightness", s->conn_state.disp_info->bcsh_info.brightness); 2578ac500a1fSSandy Huang FDT_SET_U32("bcsh,contrast", s->conn_state.disp_info->bcsh_info.contrast); 2579ac500a1fSSandy Huang FDT_SET_U32("bcsh,saturation", s->conn_state.disp_info->bcsh_info.saturation); 2580ac500a1fSSandy Huang FDT_SET_U32("bcsh,hue", s->conn_state.disp_info->bcsh_info.hue); 25816027c871SZhang Yubing 25826027c871SZhang Yubing if (!strncasecmp(cacm_header, "CACM", 4)) { 258355b1e6b1SZhang Yubing FDT_SET_U32("post-csc,hue", 25846027c871SZhang Yubing s->conn_state.disp_info->csc_info.hue); 258555b1e6b1SZhang Yubing FDT_SET_U32("post-csc,saturation", 25866027c871SZhang Yubing s->conn_state.disp_info->csc_info.saturation); 258755b1e6b1SZhang Yubing FDT_SET_U32("post-csc,contrast", 25886027c871SZhang Yubing s->conn_state.disp_info->csc_info.contrast); 258955b1e6b1SZhang Yubing FDT_SET_U32("post-csc,brightness", 25906027c871SZhang Yubing s->conn_state.disp_info->csc_info.brightness); 259155b1e6b1SZhang Yubing FDT_SET_U32("post-csc,r-gain", 25926027c871SZhang Yubing s->conn_state.disp_info->csc_info.r_gain); 259355b1e6b1SZhang Yubing FDT_SET_U32("post-csc,g-gain", 25946027c871SZhang Yubing s->conn_state.disp_info->csc_info.g_gain); 259555b1e6b1SZhang Yubing FDT_SET_U32("post-csc,b-gain", 25966027c871SZhang Yubing s->conn_state.disp_info->csc_info.b_gain); 259755b1e6b1SZhang Yubing FDT_SET_U32("post-csc,r-offset", 25986027c871SZhang Yubing s->conn_state.disp_info->csc_info.r_offset); 259955b1e6b1SZhang Yubing FDT_SET_U32("post-csc,g-offset", 26006027c871SZhang Yubing s->conn_state.disp_info->csc_info.g_offset); 260155b1e6b1SZhang Yubing FDT_SET_U32("post-csc,b-offset", 26026027c871SZhang Yubing s->conn_state.disp_info->csc_info.b_offset); 260355b1e6b1SZhang Yubing FDT_SET_U32("post-csc,enable", 26046027c871SZhang Yubing s->conn_state.disp_info->csc_info.csc_enable); 26056027c871SZhang Yubing } 2606ac500a1fSSandy Huang } 2607ac500a1fSSandy Huang 26086414e3bcSSandy Huang if (s->conn_state.disp_info->cubic_lut_data.size && 26096414e3bcSSandy Huang CONFIG_ROCKCHIP_CUBIC_LUT_SIZE) 26106414e3bcSSandy Huang FDT_SET_U32("cubic_lut,offset", get_cubic_lut_offset(s->crtc_state.crtc_id)); 26116414e3bcSSandy Huang 2612186f8572SMark Yao #undef FDT_SET_U32 2613186f8572SMark Yao } 2614186f8572SMark Yao } 2615186f8572SMark Yao 2616186f8572SMark Yao int rockchip_display_bind(struct udevice *dev) 2617186f8572SMark Yao { 2618186f8572SMark Yao struct video_uc_platdata *plat = dev_get_uclass_platdata(dev); 2619186f8572SMark Yao 26204b8c2ef1SMark Yao plat->size = DRM_ROCKCHIP_FB_SIZE + MEMORY_POOL_SIZE; 2621186f8572SMark Yao 2622186f8572SMark Yao return 0; 2623186f8572SMark Yao } 2624186f8572SMark Yao 2625186f8572SMark Yao static const struct udevice_id rockchip_display_ids[] = { 2626186f8572SMark Yao { .compatible = "rockchip,display-subsystem" }, 2627186f8572SMark Yao { } 2628186f8572SMark Yao }; 2629186f8572SMark Yao 2630186f8572SMark Yao U_BOOT_DRIVER(rockchip_display) = { 2631186f8572SMark Yao .name = "rockchip_display", 2632186f8572SMark Yao .id = UCLASS_VIDEO, 2633186f8572SMark Yao .of_match = rockchip_display_ids, 2634186f8572SMark Yao .bind = rockchip_display_bind, 2635186f8572SMark Yao .probe = rockchip_display_probe, 2636186f8572SMark Yao }; 26374b8c2ef1SMark Yao 26384b8c2ef1SMark Yao static int do_rockchip_logo_show(cmd_tbl_t *cmdtp, int flag, int argc, 26394b8c2ef1SMark Yao char *const argv[]) 26404b8c2ef1SMark Yao { 26414b8c2ef1SMark Yao if (argc != 1) 26424b8c2ef1SMark Yao return CMD_RET_USAGE; 26434b8c2ef1SMark Yao 26444b8c2ef1SMark Yao rockchip_show_logo(); 26454b8c2ef1SMark Yao 26464b8c2ef1SMark Yao return 0; 26474b8c2ef1SMark Yao } 26484b8c2ef1SMark Yao 26494b8c2ef1SMark Yao static int do_rockchip_show_bmp(cmd_tbl_t *cmdtp, int flag, int argc, 26504b8c2ef1SMark Yao char *const argv[]) 26514b8c2ef1SMark Yao { 26524b8c2ef1SMark Yao if (argc != 2) 26534b8c2ef1SMark Yao return CMD_RET_USAGE; 26544b8c2ef1SMark Yao 26554b8c2ef1SMark Yao rockchip_show_bmp(argv[1]); 26564b8c2ef1SMark Yao 26574b8c2ef1SMark Yao return 0; 26584b8c2ef1SMark Yao } 26594b8c2ef1SMark Yao 266072388c26SDamon Ding static int do_rockchip_vop_dump(cmd_tbl_t *cmdtp, int flag, int argc, 266172388c26SDamon Ding char *const argv[]) 266272388c26SDamon Ding { 266372388c26SDamon Ding int ret; 266472388c26SDamon Ding 266572388c26SDamon Ding if (argc < 1 || argc > 2) 266672388c26SDamon Ding return CMD_RET_USAGE; 266772388c26SDamon Ding 266872388c26SDamon Ding ret = rockchip_vop_dump(argv[1]); 266972388c26SDamon Ding 267072388c26SDamon Ding return ret; 267172388c26SDamon Ding } 267272388c26SDamon Ding 26734b8c2ef1SMark Yao U_BOOT_CMD( 26744b8c2ef1SMark Yao rockchip_show_logo, 1, 1, do_rockchip_logo_show, 26754b8c2ef1SMark Yao "load and display log from resource partition", 26764b8c2ef1SMark Yao NULL 26774b8c2ef1SMark Yao ); 26784b8c2ef1SMark Yao 26794b8c2ef1SMark Yao U_BOOT_CMD( 26804b8c2ef1SMark Yao rockchip_show_bmp, 2, 1, do_rockchip_show_bmp, 26814b8c2ef1SMark Yao "load and display bmp from resource partition", 26824b8c2ef1SMark Yao " <bmp_name>" 26834b8c2ef1SMark Yao ); 268472388c26SDamon Ding 268572388c26SDamon Ding U_BOOT_CMD( 268672388c26SDamon Ding vop_dump, 2, 1, do_rockchip_vop_dump, 268772388c26SDamon Ding "dump vop regs", 268872388c26SDamon Ding " [a/all]" 268972388c26SDamon Ding ); 2690