xref: /rk3399_rockchip-uboot/drivers/video/drm/rockchip_display.c (revision 0e00a84cdedf7a1949486746225b35984b351eca)
1186f8572SMark Yao /*
2186f8572SMark Yao  * (C) Copyright 2008-2017 Fuzhou Rockchip Electronics Co., Ltd
3186f8572SMark Yao  *
4186f8572SMark Yao  * SPDX-License-Identifier:	GPL-2.0+
5186f8572SMark Yao  */
6186f8572SMark Yao 
7186f8572SMark Yao #include <asm/unaligned.h>
8186f8572SMark Yao #include <config.h>
9186f8572SMark Yao #include <common.h>
10186f8572SMark Yao #include <errno.h>
11*0e00a84cSMasahiro Yamada #include <linux/libfdt.h>
12186f8572SMark Yao #include <fdtdec.h>
13186f8572SMark Yao #include <fdt_support.h>
148e2bab3fSAlgea Cao #include <linux/hdmi.h>
15186f8572SMark Yao #include <linux/list.h>
16186f8572SMark Yao #include <linux/compat.h>
17186f8572SMark Yao #include <linux/media-bus-format.h>
18186f8572SMark Yao #include <malloc.h>
19186f8572SMark Yao #include <video.h>
20f8a3e587SJoseph Chen #include <video_rockchip.h>
21186f8572SMark Yao #include <dm/device.h>
22186f8572SMark Yao #include <dm/uclass-internal.h>
234b8c2ef1SMark Yao #include <asm/arch-rockchip/resource_img.h>
24186f8572SMark Yao 
25186f8572SMark Yao #include "bmp_helper.h"
26186f8572SMark Yao #include "rockchip_display.h"
27186f8572SMark Yao #include "rockchip_crtc.h"
28186f8572SMark Yao #include "rockchip_connector.h"
29186f8572SMark Yao #include "rockchip_phy.h"
30186f8572SMark Yao #include "rockchip_panel.h"
31e2bce6e4SKever Yang #include <dm.h>
32e2bce6e4SKever Yang #include <dm/of_access.h>
33e2bce6e4SKever Yang #include <dm/ofnode.h>
34186f8572SMark Yao 
351e4c51caSSandy Huang #define DRIVER_VERSION	"v1.0.1"
36e559407dSSandy Huang 
37e559407dSSandy Huang /***********************************************************************
38e559407dSSandy Huang  *  Rockchip UBOOT DRM driver version
39e559407dSSandy Huang  *
40e559407dSSandy Huang  *  v1.0.0	: add basic version for rockchip drm driver(hjc)
411e4c51caSSandy Huang  *  v1.0.1	: add much dsi update(hjc)
42e559407dSSandy Huang  *
43e559407dSSandy Huang  **********************************************************************/
44e559407dSSandy Huang 
454b8c2ef1SMark Yao #define RK_BLK_SIZE 512
464b8c2ef1SMark Yao 
47186f8572SMark Yao DECLARE_GLOBAL_DATA_PTR;
48186f8572SMark Yao static LIST_HEAD(rockchip_display_list);
49186f8572SMark Yao static LIST_HEAD(logo_cache_list);
50186f8572SMark Yao 
51186f8572SMark Yao static unsigned long memory_start;
52186f8572SMark Yao static unsigned long memory_end;
53186f8572SMark Yao 
542a48727aSAlgea Cao /*
552a48727aSAlgea Cao  * the phy types are used by different connectors in public.
562a48727aSAlgea Cao  * The current version only has inno hdmi phy for hdmi and tve.
572a48727aSAlgea Cao  */
582a48727aSAlgea Cao enum public_use_phy {
592a48727aSAlgea Cao 	NONE,
602a48727aSAlgea Cao 	INNO_HDMI_PHY
612a48727aSAlgea Cao };
622a48727aSAlgea Cao 
632a48727aSAlgea Cao /* save public phy data */
642a48727aSAlgea Cao struct public_phy_data {
652a48727aSAlgea Cao 	const struct rockchip_phy *phy_drv;
662a48727aSAlgea Cao 	int phy_node;
672a48727aSAlgea Cao 	int public_phy_type;
682a48727aSAlgea Cao 	bool phy_init;
692a48727aSAlgea Cao };
702a48727aSAlgea Cao 
712a48727aSAlgea Cao /* check which kind of public phy does connector use */
722a48727aSAlgea Cao static int check_public_use_phy(struct display_state *state)
732a48727aSAlgea Cao {
742a48727aSAlgea Cao 	int ret = NONE;
752a48727aSAlgea Cao #ifdef CONFIG_ROCKCHIP_INNO_HDMI_PHY
762a48727aSAlgea Cao 	struct connector_state *conn_state = &state->conn_state;
772a48727aSAlgea Cao 
782a48727aSAlgea Cao 	if (!strncmp(dev_read_name(conn_state->dev), "tve", 3) ||
792a48727aSAlgea Cao 	    !strncmp(dev_read_name(conn_state->dev), "hdmi", 4))
802a48727aSAlgea Cao 		ret = INNO_HDMI_PHY;
812a48727aSAlgea Cao #endif
822a48727aSAlgea Cao 
832a48727aSAlgea Cao 	return ret;
842a48727aSAlgea Cao }
852a48727aSAlgea Cao 
862a48727aSAlgea Cao /*
872a48727aSAlgea Cao  * get public phy driver and initialize it.
882a48727aSAlgea Cao  * The current version only has inno hdmi phy for hdmi and tve.
892a48727aSAlgea Cao  */
902a48727aSAlgea Cao static int get_public_phy(struct display_state *state,
912a48727aSAlgea Cao 			  struct public_phy_data *data)
922a48727aSAlgea Cao {
932a48727aSAlgea Cao 	struct connector_state *conn_state = &state->conn_state;
9415081c50SWyon Bi 	struct rockchip_phy *phy;
952a48727aSAlgea Cao 	struct udevice *dev;
962a48727aSAlgea Cao 	int ret = 0;
972a48727aSAlgea Cao 
982a48727aSAlgea Cao 	switch (data->public_phy_type) {
992a48727aSAlgea Cao 	case INNO_HDMI_PHY:
1002a48727aSAlgea Cao #if defined(CONFIG_ROCKCHIP_RK3328)
10115081c50SWyon Bi 		ret = uclass_get_device_by_name(UCLASS_PHY,
1022a48727aSAlgea Cao 						"hdmiphy@ff430000", &dev);
1032a48727aSAlgea Cao #elif defined(CONFIG_ROCKCHIP_RK322X)
10415081c50SWyon Bi 		ret = uclass_get_device_by_name(UCLASS_PHY,
1052a48727aSAlgea Cao 						"hdmi-phy@12030000", &dev);
1062a48727aSAlgea Cao #else
1072a48727aSAlgea Cao 		ret = -EINVAL;
1082a48727aSAlgea Cao #endif
1092a48727aSAlgea Cao 		if (ret) {
1102a48727aSAlgea Cao 			printf("Warn: can't find phy driver\n");
1112a48727aSAlgea Cao 			return 0;
1122a48727aSAlgea Cao 		}
1132a48727aSAlgea Cao 
11415081c50SWyon Bi 		phy = (struct rockchip_phy *)dev_get_driver_data(dev);
1152a48727aSAlgea Cao 		if (!phy) {
1162a48727aSAlgea Cao 			printf("failed to get phy driver\n");
1172a48727aSAlgea Cao 			return 0;
1182a48727aSAlgea Cao 		}
1192a48727aSAlgea Cao 
1202a48727aSAlgea Cao 		conn_state->phy_dev = dev;
1212a48727aSAlgea Cao 		conn_state->phy_node = dev->node;
12215081c50SWyon Bi 
12315081c50SWyon Bi 		ret = rockchip_phy_init(phy);
12415081c50SWyon Bi 		if (ret) {
1252a48727aSAlgea Cao 			printf("failed to init phy driver\n");
12615081c50SWyon Bi 			return ret;
1272a48727aSAlgea Cao 		}
1282a48727aSAlgea Cao 		conn_state->phy = phy;
1292a48727aSAlgea Cao 
1302a48727aSAlgea Cao 		printf("inno hdmi phy init success, save it\n");
1312a48727aSAlgea Cao 		data->phy_node = ofnode_to_offset(conn_state->phy_node);
1322a48727aSAlgea Cao 		data->phy_drv = conn_state->phy;
1332a48727aSAlgea Cao 		data->phy_init = true;
1342a48727aSAlgea Cao 		return 0;
1352a48727aSAlgea Cao 	default:
1362a48727aSAlgea Cao 		return -EINVAL;
1372a48727aSAlgea Cao 	}
1382a48727aSAlgea Cao }
1392a48727aSAlgea Cao 
1404b8c2ef1SMark Yao static void init_display_buffer(ulong base)
141186f8572SMark Yao {
1424b8c2ef1SMark Yao 	memory_start = base + DRM_ROCKCHIP_FB_SIZE;
143186f8572SMark Yao 	memory_end = memory_start;
144186f8572SMark Yao }
145186f8572SMark Yao 
146186f8572SMark Yao static void *get_display_buffer(int size)
147186f8572SMark Yao {
148186f8572SMark Yao 	unsigned long roundup_memory = roundup(memory_end, PAGE_SIZE);
149186f8572SMark Yao 	void *buf;
150186f8572SMark Yao 
151186f8572SMark Yao 	if (roundup_memory + size > memory_start + MEMORY_POOL_SIZE) {
152186f8572SMark Yao 		printf("failed to alloc %dbyte memory to display\n", size);
153186f8572SMark Yao 		return NULL;
154186f8572SMark Yao 	}
155186f8572SMark Yao 	buf = (void *)roundup_memory;
156186f8572SMark Yao 
157186f8572SMark Yao 	memory_end = roundup_memory + size;
158186f8572SMark Yao 
159186f8572SMark Yao 	return buf;
160186f8572SMark Yao }
161186f8572SMark Yao 
162186f8572SMark Yao static unsigned long get_display_size(void)
163186f8572SMark Yao {
164186f8572SMark Yao 	return memory_end - memory_start;
165186f8572SMark Yao }
166186f8572SMark Yao 
167861ce1a0SSandy Huang static bool can_direct_logo(int bpp)
168186f8572SMark Yao {
169861ce1a0SSandy Huang 	return bpp == 24 || bpp == 32;
170186f8572SMark Yao }
171186f8572SMark Yao 
1724b8c2ef1SMark Yao 
173e2bce6e4SKever Yang static struct udevice *get_panel_device(struct display_state *state, ofnode conn_node)
1744b8c2ef1SMark Yao {
1754b8c2ef1SMark Yao 	struct panel_state *panel_state = &state->panel_state;
1764b8c2ef1SMark Yao 	struct udevice *dev;
177e2bce6e4SKever Yang 	struct connector_state *conn_state = &state->conn_state;
178e2bce6e4SKever Yang 	ofnode node, ports_node, port_node;
179e2bce6e4SKever Yang 	struct device_node *port, *panel, *ep;
180e2bce6e4SKever Yang 	int ph;
181e2bce6e4SKever Yang 	int ret;
182186f8572SMark Yao 
183e2bce6e4SKever Yang 	node = dev_read_subnode(conn_state->dev, "panel");
184e2bce6e4SKever Yang 	if (ofnode_valid(node) &&
185e2bce6e4SKever Yang 	    of_device_is_available(ofnode_to_np(node))) {
186e2bce6e4SKever Yang 		ret = uclass_get_device_by_ofnode(UCLASS_PANEL, node, &dev);
187e2bce6e4SKever Yang 		if (!ret) {
188e2bce6e4SKever Yang 			panel_state->node = node;
1894b8c2ef1SMark Yao 			return dev;
1904b8c2ef1SMark Yao 		}
1914b8c2ef1SMark Yao 	}
192186f8572SMark Yao 
193e2bce6e4SKever Yang 	/* TODO: this path not tested */
194e2bce6e4SKever Yang 	ports_node = dev_read_subnode(conn_state->dev, "ports");
195e2bce6e4SKever Yang 	if (!ofnode_valid(ports_node))
1964b8c2ef1SMark Yao 		return NULL;
197186f8572SMark Yao 
198e2bce6e4SKever Yang 	ofnode_for_each_subnode(port_node, ports_node) {
199e2bce6e4SKever Yang 		ofnode_for_each_subnode(node, port_node) {
200e2bce6e4SKever Yang 			ph = ofnode_read_u32_default(node, "remote-endpoint", -1);
201186f8572SMark Yao 			if (!ph)
202186f8572SMark Yao 				continue;
203e2bce6e4SKever Yang 			ep = of_find_node_by_phandle(ph);
204e2bce6e4SKever Yang 			if (!ofnode_valid(np_to_ofnode(ep))) {
205e2bce6e4SKever Yang 				printf("Warn: can't find endpoint from phdl\n");
2064b8c2ef1SMark Yao 				continue;
2074b8c2ef1SMark Yao 			}
208e2bce6e4SKever Yang 			port = of_get_parent(ep);
209e2bce6e4SKever Yang 			if (!ofnode_valid(np_to_ofnode(port))) {
210e2bce6e4SKever Yang 				printf("Warn: can't find port node\n");
211e2bce6e4SKever Yang 				continue;
212e2bce6e4SKever Yang 			}
213e2bce6e4SKever Yang 			panel = of_get_parent(port);
214e2bce6e4SKever Yang 			if (!ofnode_valid(np_to_ofnode(panel))) {
215e2bce6e4SKever Yang 				printf("Warn: can't find panel node\n");
216e2bce6e4SKever Yang 				continue;
217e2bce6e4SKever Yang 			}
218e2bce6e4SKever Yang 			ret = uclass_get_device_by_ofnode(UCLASS_PANEL,
219e2bce6e4SKever Yang 							  np_to_ofnode(panel),
220e2bce6e4SKever Yang 							  &dev);
2211e44acfcSWyon Bi 			if (!ret) {
222335adcb5SKever Yang 				panel_state->node = np_to_ofnode(panel);
2234b8c2ef1SMark Yao 				return dev;
2244b8c2ef1SMark Yao 			}
225186f8572SMark Yao 		}
2261e44acfcSWyon Bi 	}
227186f8572SMark Yao 
2284b8c2ef1SMark Yao 	return NULL;
229186f8572SMark Yao }
230186f8572SMark Yao 
2312a48727aSAlgea Cao static int connector_phy_init(struct display_state *state,
2322a48727aSAlgea Cao 			      struct public_phy_data *data)
233186f8572SMark Yao {
234186f8572SMark Yao 	struct connector_state *conn_state = &state->conn_state;
23515081c50SWyon Bi 	struct rockchip_phy *phy;
2364b8c2ef1SMark Yao 	struct udevice *dev;
2372a48727aSAlgea Cao 	int ret, type;
238186f8572SMark Yao 
2392a48727aSAlgea Cao 	/* does this connector use public phy with others */
2402a48727aSAlgea Cao 	type = check_public_use_phy(state);
2412a48727aSAlgea Cao 	if (type == INNO_HDMI_PHY) {
2422a48727aSAlgea Cao 		/* there is no public phy was initialized */
2432a48727aSAlgea Cao 		if (!data->phy_init) {
2442a48727aSAlgea Cao 			printf("start get public phy\n");
2452a48727aSAlgea Cao 			data->public_phy_type = type;
2462a48727aSAlgea Cao 			if (get_public_phy(state, data)) {
2472a48727aSAlgea Cao 				printf("can't find correct public phy type\n");
2482a48727aSAlgea Cao 				free(data);
2492a48727aSAlgea Cao 				return -EINVAL;
2502a48727aSAlgea Cao 			}
2512a48727aSAlgea Cao 			return 0;
2522a48727aSAlgea Cao 		}
2532a48727aSAlgea Cao 
2542a48727aSAlgea Cao 		/* if this phy has been initialized, get it directly */
2552a48727aSAlgea Cao 		conn_state->phy_node = offset_to_ofnode(data->phy_node);
25615081c50SWyon Bi 		conn_state->phy = (struct rockchip_phy *)data->phy_drv;
2572a48727aSAlgea Cao 		return 0;
2582a48727aSAlgea Cao 	}
2592a48727aSAlgea Cao 
2602a48727aSAlgea Cao 	/*
2612a48727aSAlgea Cao 	 * if this connector don't use the same phy with others,
2622a48727aSAlgea Cao 	 * just get phy as original method.
2632a48727aSAlgea Cao 	 */
264e2bce6e4SKever Yang 	ret = uclass_get_device_by_phandle(UCLASS_PHY, conn_state->dev, "phys",
265e2bce6e4SKever Yang 					   &dev);
2664b8c2ef1SMark Yao 	if (ret) {
2678e2bab3fSAlgea Cao 		printf("Warn: can't find phy driver\n");
268335adcb5SKever Yang 		return 0;
2694b8c2ef1SMark Yao 	}
2701e44acfcSWyon Bi 
27115081c50SWyon Bi 	phy = (struct rockchip_phy *)dev_get_driver_data(dev);
272186f8572SMark Yao 	if (!phy) {
273186f8572SMark Yao 		printf("failed to find phy driver\n");
274186f8572SMark Yao 		return 0;
275186f8572SMark Yao 	}
276186f8572SMark Yao 
2774b8c2ef1SMark Yao 	conn_state->phy_dev = dev;
278e2bce6e4SKever Yang 	conn_state->phy_node = dev->node;
279186f8572SMark Yao 
28015081c50SWyon Bi 	ret = rockchip_phy_init(phy);
28115081c50SWyon Bi 	if (ret) {
282186f8572SMark Yao 		printf("failed to init phy driver\n");
28315081c50SWyon Bi 		return ret;
284186f8572SMark Yao 	}
28515081c50SWyon Bi 
286186f8572SMark Yao 	conn_state->phy = phy;
28715081c50SWyon Bi 
288186f8572SMark Yao 	return 0;
289186f8572SMark Yao }
290186f8572SMark Yao 
291186f8572SMark Yao static int connector_panel_init(struct display_state *state)
292186f8572SMark Yao {
293186f8572SMark Yao 	struct connector_state *conn_state = &state->conn_state;
294186f8572SMark Yao 	struct panel_state *panel_state = &state->panel_state;
295186f8572SMark Yao 	struct udevice *dev;
296e2bce6e4SKever Yang 	ofnode conn_node = conn_state->node;
297186f8572SMark Yao 	const struct rockchip_panel *panel;
298e2bce6e4SKever Yang 	ofnode dsp_lut_node;
299186f8572SMark Yao 	int ret, len;
300186f8572SMark Yao 
301186f8572SMark Yao 	dm_scan_fdt_dev(conn_state->dev);
302186f8572SMark Yao 
3034b8c2ef1SMark Yao 	dev = get_panel_device(state, conn_node);
3044b8c2ef1SMark Yao 	if (!dev) {
3054b8c2ef1SMark Yao 		return 0;
306186f8572SMark Yao 	}
3074b8c2ef1SMark Yao 
308186f8572SMark Yao 	panel = (const struct rockchip_panel *)dev_get_driver_data(dev);
309186f8572SMark Yao 	if (!panel) {
310186f8572SMark Yao 		printf("failed to find panel driver\n");
311186f8572SMark Yao 		return 0;
312186f8572SMark Yao 	}
313186f8572SMark Yao 
314186f8572SMark Yao 	panel_state->dev = dev;
315186f8572SMark Yao 	panel_state->panel = panel;
316186f8572SMark Yao 
317c493d00eSWyon Bi 	if (panel->funcs && panel->funcs->init) {
318c493d00eSWyon Bi 		ret = panel->funcs->init(state);
319186f8572SMark Yao 		if (ret) {
320186f8572SMark Yao 			printf("failed to init panel driver\n");
321186f8572SMark Yao 			return ret;
322186f8572SMark Yao 		}
323c493d00eSWyon Bi 	}
3241e44acfcSWyon Bi 
325e2bce6e4SKever Yang 	dsp_lut_node = dev_read_subnode(dev, "dsp-lut");
326e2bce6e4SKever Yang 	if (!ofnode_valid(dsp_lut_node)) {
327861ce1a0SSandy Huang 		debug("%s can not find dsp-lut node\n", __func__);
3281e44acfcSWyon Bi 		return 0;
329e2bce6e4SKever Yang 	}
3301e44acfcSWyon Bi 
331e2bce6e4SKever Yang 	ofnode_get_property(dsp_lut_node, "gamma-lut", &len);
332186f8572SMark Yao 	if (len > 0) {
333186f8572SMark Yao 		conn_state->gamma.size = len / sizeof(u32);
334186f8572SMark Yao 		conn_state->gamma.lut = malloc(len);
335186f8572SMark Yao 		if (!conn_state->gamma.lut) {
336186f8572SMark Yao 			printf("malloc gamma lut failed\n");
337186f8572SMark Yao 			return -ENOMEM;
338186f8572SMark Yao 		}
339e2bce6e4SKever Yang 		ret = ofnode_read_u32_array(dsp_lut_node, "gamma-lut",
340186f8572SMark Yao 					    conn_state->gamma.lut,
341e2bce6e4SKever Yang 					    conn_state->gamma.size);
342e2bce6e4SKever Yang 		if (ret) {
343186f8572SMark Yao 			printf("Cannot decode gamma_lut\n");
344186f8572SMark Yao 			conn_state->gamma.lut = NULL;
345186f8572SMark Yao 			return -EINVAL;
346186f8572SMark Yao 		}
347186f8572SMark Yao 		panel_state->dsp_lut_node = dsp_lut_node;
348186f8572SMark Yao 	}
349186f8572SMark Yao 
350186f8572SMark Yao 	return 0;
351186f8572SMark Yao }
352186f8572SMark Yao 
353186f8572SMark Yao int drm_mode_vrefresh(const struct drm_display_mode *mode)
354186f8572SMark Yao {
355186f8572SMark Yao 	int refresh = 0;
356186f8572SMark Yao 	unsigned int calc_val;
357186f8572SMark Yao 
358186f8572SMark Yao 	if (mode->vrefresh > 0) {
359186f8572SMark Yao 		refresh = mode->vrefresh;
360186f8572SMark Yao 	} else if (mode->htotal > 0 && mode->vtotal > 0) {
361186f8572SMark Yao 		int vtotal;
362186f8572SMark Yao 
363186f8572SMark Yao 		vtotal = mode->vtotal;
364186f8572SMark Yao 		/* work out vrefresh the value will be x1000 */
365186f8572SMark Yao 		calc_val = (mode->clock * 1000);
366186f8572SMark Yao 		calc_val /= mode->htotal;
367186f8572SMark Yao 		refresh = (calc_val + vtotal / 2) / vtotal;
368186f8572SMark Yao 
369186f8572SMark Yao 		if (mode->flags & DRM_MODE_FLAG_INTERLACE)
370186f8572SMark Yao 			refresh *= 2;
371186f8572SMark Yao 		if (mode->flags & DRM_MODE_FLAG_DBLSCAN)
372186f8572SMark Yao 			refresh /= 2;
373186f8572SMark Yao 		if (mode->vscan > 1)
374186f8572SMark Yao 			refresh /= mode->vscan;
375186f8572SMark Yao 	}
376186f8572SMark Yao 	return refresh;
377186f8572SMark Yao }
378186f8572SMark Yao 
379e2bce6e4SKever Yang static int display_get_timing_from_dts(struct panel_state *panel_state,
380186f8572SMark Yao 				       struct drm_display_mode *mode)
381186f8572SMark Yao {
382e2bce6e4SKever Yang 	int phandle;
383186f8572SMark Yao 	int hactive, vactive, pixelclock;
384186f8572SMark Yao 	int hfront_porch, hback_porch, hsync_len;
385186f8572SMark Yao 	int vfront_porch, vback_porch, vsync_len;
386186f8572SMark Yao 	int val, flags = 0;
387e2bce6e4SKever Yang 	ofnode timing, native_mode;
388186f8572SMark Yao 
389e2bce6e4SKever Yang 	timing = dev_read_subnode(panel_state->dev, "display-timings");
390e2bce6e4SKever Yang 	if (!ofnode_valid(timing))
391186f8572SMark Yao 		return -ENODEV;
392186f8572SMark Yao 
393e2bce6e4SKever Yang 	native_mode = ofnode_find_subnode(timing, "timing");
394e2bce6e4SKever Yang 	if (!ofnode_valid(native_mode)) {
395e2bce6e4SKever Yang 		phandle = ofnode_read_u32_default(timing, "native-mode", -1);
396e2bce6e4SKever Yang 		native_mode = np_to_ofnode(of_find_node_by_phandle(phandle));
397e2bce6e4SKever Yang 		if (!ofnode_valid(native_mode)) {
398186f8572SMark Yao 			printf("failed to get display timings from DT\n");
399186f8572SMark Yao 			return -ENXIO;
400186f8572SMark Yao 		}
401186f8572SMark Yao 	}
402186f8572SMark Yao 
403186f8572SMark Yao #define FDT_GET_INT(val, name) \
404e2bce6e4SKever Yang 	val = ofnode_read_s32_default(native_mode, name, -1); \
405186f8572SMark Yao 	if (val < 0) { \
406186f8572SMark Yao 		printf("Can't get %s\n", name); \
407186f8572SMark Yao 		return -ENXIO; \
408186f8572SMark Yao 	}
409186f8572SMark Yao 
410186f8572SMark Yao 	FDT_GET_INT(hactive, "hactive");
411186f8572SMark Yao 	FDT_GET_INT(vactive, "vactive");
412186f8572SMark Yao 	FDT_GET_INT(pixelclock, "clock-frequency");
413186f8572SMark Yao 	FDT_GET_INT(hsync_len, "hsync-len");
414186f8572SMark Yao 	FDT_GET_INT(hfront_porch, "hfront-porch");
415186f8572SMark Yao 	FDT_GET_INT(hback_porch, "hback-porch");
416186f8572SMark Yao 	FDT_GET_INT(vsync_len, "vsync-len");
417186f8572SMark Yao 	FDT_GET_INT(vfront_porch, "vfront-porch");
418186f8572SMark Yao 	FDT_GET_INT(vback_porch, "vback-porch");
419186f8572SMark Yao 	FDT_GET_INT(val, "hsync-active");
420186f8572SMark Yao 	flags |= val ? DRM_MODE_FLAG_PHSYNC : DRM_MODE_FLAG_NHSYNC;
421186f8572SMark Yao 	FDT_GET_INT(val, "vsync-active");
422186f8572SMark Yao 	flags |= val ? DRM_MODE_FLAG_PVSYNC : DRM_MODE_FLAG_NVSYNC;
4233a06149eSSandy Huang 	FDT_GET_INT(val, "pixelclk-active");
4243a06149eSSandy Huang 	flags |= val ? DRM_MODE_FLAG_PPIXDATA : 0;
425186f8572SMark Yao 
426186f8572SMark Yao 	mode->hdisplay = hactive;
427186f8572SMark Yao 	mode->hsync_start = mode->hdisplay + hfront_porch;
428186f8572SMark Yao 	mode->hsync_end = mode->hsync_start + hsync_len;
429186f8572SMark Yao 	mode->htotal = mode->hsync_end + hback_porch;
430186f8572SMark Yao 
431186f8572SMark Yao 	mode->vdisplay = vactive;
432186f8572SMark Yao 	mode->vsync_start = mode->vdisplay + vfront_porch;
433186f8572SMark Yao 	mode->vsync_end = mode->vsync_start + vsync_len;
434186f8572SMark Yao 	mode->vtotal = mode->vsync_end + vback_porch;
435186f8572SMark Yao 
436186f8572SMark Yao 	mode->clock = pixelclock / 1000;
437186f8572SMark Yao 	mode->flags = flags;
438186f8572SMark Yao 
439186f8572SMark Yao 	return 0;
440186f8572SMark Yao }
441186f8572SMark Yao 
442ccd843b9SSandy Huang /**
443ccd843b9SSandy Huang  * drm_mode_set_crtcinfo - set CRTC modesetting timing parameters
444ccd843b9SSandy Huang  * @p: mode
445ccd843b9SSandy Huang  * @adjust_flags: a combination of adjustment flags
446ccd843b9SSandy Huang  *
447ccd843b9SSandy Huang  * Setup the CRTC modesetting timing parameters for @p, adjusting if necessary.
448ccd843b9SSandy Huang  *
449ccd843b9SSandy Huang  * - The CRTC_INTERLACE_HALVE_V flag can be used to halve vertical timings of
450ccd843b9SSandy Huang  *   interlaced modes.
451ccd843b9SSandy Huang  * - The CRTC_STEREO_DOUBLE flag can be used to compute the timings for
452ccd843b9SSandy Huang  *   buffers containing two eyes (only adjust the timings when needed, eg. for
453ccd843b9SSandy Huang  *   "frame packing" or "side by side full").
454ccd843b9SSandy Huang  * - The CRTC_NO_DBLSCAN and CRTC_NO_VSCAN flags request that adjustment *not*
455ccd843b9SSandy Huang  *   be performed for doublescan and vscan > 1 modes respectively.
456ccd843b9SSandy Huang  */
457ccd843b9SSandy Huang void drm_mode_set_crtcinfo(struct drm_display_mode *p, int adjust_flags)
458ccd843b9SSandy Huang {
459ccd843b9SSandy Huang 	if ((p == NULL) || ((p->type & DRM_MODE_TYPE_CRTC_C) == DRM_MODE_TYPE_BUILTIN))
460ccd843b9SSandy Huang 		return;
461ccd843b9SSandy Huang 
462ccd843b9SSandy Huang 	if (p->flags & DRM_MODE_FLAG_DBLCLK)
463ccd843b9SSandy Huang 		p->crtc_clock = 2 * p->clock;
464ccd843b9SSandy Huang 	else
465ccd843b9SSandy Huang 		p->crtc_clock = p->clock;
466ccd843b9SSandy Huang 	p->crtc_hdisplay = p->hdisplay;
467ccd843b9SSandy Huang 	p->crtc_hsync_start = p->hsync_start;
468ccd843b9SSandy Huang 	p->crtc_hsync_end = p->hsync_end;
469ccd843b9SSandy Huang 	p->crtc_htotal = p->htotal;
470ccd843b9SSandy Huang 	p->crtc_hskew = p->hskew;
471ccd843b9SSandy Huang 	p->crtc_vdisplay = p->vdisplay;
472ccd843b9SSandy Huang 	p->crtc_vsync_start = p->vsync_start;
473ccd843b9SSandy Huang 	p->crtc_vsync_end = p->vsync_end;
474ccd843b9SSandy Huang 	p->crtc_vtotal = p->vtotal;
475ccd843b9SSandy Huang 
476ccd843b9SSandy Huang 	if (p->flags & DRM_MODE_FLAG_INTERLACE) {
477ccd843b9SSandy Huang 		if (adjust_flags & CRTC_INTERLACE_HALVE_V) {
478ccd843b9SSandy Huang 			p->crtc_vdisplay /= 2;
479ccd843b9SSandy Huang 			p->crtc_vsync_start /= 2;
480ccd843b9SSandy Huang 			p->crtc_vsync_end /= 2;
481ccd843b9SSandy Huang 			p->crtc_vtotal /= 2;
482ccd843b9SSandy Huang 		}
483ccd843b9SSandy Huang 	}
484ccd843b9SSandy Huang 
485ccd843b9SSandy Huang 	if (!(adjust_flags & CRTC_NO_DBLSCAN)) {
486ccd843b9SSandy Huang 		if (p->flags & DRM_MODE_FLAG_DBLSCAN) {
487ccd843b9SSandy Huang 			p->crtc_vdisplay *= 2;
488ccd843b9SSandy Huang 			p->crtc_vsync_start *= 2;
489ccd843b9SSandy Huang 			p->crtc_vsync_end *= 2;
490ccd843b9SSandy Huang 			p->crtc_vtotal *= 2;
491ccd843b9SSandy Huang 		}
492ccd843b9SSandy Huang 	}
493ccd843b9SSandy Huang 
494ccd843b9SSandy Huang 	if (!(adjust_flags & CRTC_NO_VSCAN)) {
495ccd843b9SSandy Huang 		if (p->vscan > 1) {
496ccd843b9SSandy Huang 			p->crtc_vdisplay *= p->vscan;
497ccd843b9SSandy Huang 			p->crtc_vsync_start *= p->vscan;
498ccd843b9SSandy Huang 			p->crtc_vsync_end *= p->vscan;
499ccd843b9SSandy Huang 			p->crtc_vtotal *= p->vscan;
500ccd843b9SSandy Huang 		}
501ccd843b9SSandy Huang 	}
502ccd843b9SSandy Huang 
503ccd843b9SSandy Huang 	if (adjust_flags & CRTC_STEREO_DOUBLE) {
504ccd843b9SSandy Huang 		unsigned int layout = p->flags & DRM_MODE_FLAG_3D_MASK;
505ccd843b9SSandy Huang 
506ccd843b9SSandy Huang 		switch (layout) {
507ccd843b9SSandy Huang 		case DRM_MODE_FLAG_3D_FRAME_PACKING:
508ccd843b9SSandy Huang 			p->crtc_clock *= 2;
509ccd843b9SSandy Huang 			p->crtc_vdisplay += p->crtc_vtotal;
510ccd843b9SSandy Huang 			p->crtc_vsync_start += p->crtc_vtotal;
511ccd843b9SSandy Huang 			p->crtc_vsync_end += p->crtc_vtotal;
512ccd843b9SSandy Huang 			p->crtc_vtotal += p->crtc_vtotal;
513ccd843b9SSandy Huang 			break;
514ccd843b9SSandy Huang 		}
515ccd843b9SSandy Huang 	}
516ccd843b9SSandy Huang 
517ccd843b9SSandy Huang 	p->crtc_vblank_start = min(p->crtc_vsync_start, p->crtc_vdisplay);
518ccd843b9SSandy Huang 	p->crtc_vblank_end = max(p->crtc_vsync_end, p->crtc_vtotal);
519ccd843b9SSandy Huang 	p->crtc_hblank_start = min(p->crtc_hsync_start, p->crtc_hdisplay);
520ccd843b9SSandy Huang 	p->crtc_hblank_end = max(p->crtc_hsync_end, p->crtc_htotal);
521ccd843b9SSandy Huang }
522ccd843b9SSandy Huang 
5238e2bab3fSAlgea Cao /**
5248e2bab3fSAlgea Cao  * drm_mode_is_420_only - if a given videomode can be only supported in YCBCR420
5258e2bab3fSAlgea Cao  * output format
5268e2bab3fSAlgea Cao  *
5278e2bab3fSAlgea Cao  * @connector: drm connector under action.
5288e2bab3fSAlgea Cao  * @mode: video mode to be tested.
5298e2bab3fSAlgea Cao  *
5308e2bab3fSAlgea Cao  * Returns:
5318e2bab3fSAlgea Cao  * true if the mode can be supported in YCBCR420 format
5328e2bab3fSAlgea Cao  * false if not.
5338e2bab3fSAlgea Cao  */
5348e2bab3fSAlgea Cao bool drm_mode_is_420_only(const struct drm_display_info *display,
5358e2bab3fSAlgea Cao 			  struct drm_display_mode *mode)
5368e2bab3fSAlgea Cao {
5378e2bab3fSAlgea Cao 	u8 vic = drm_match_cea_mode(mode);
5388e2bab3fSAlgea Cao 
5398e2bab3fSAlgea Cao 	return test_bit(vic, display->hdmi.y420_vdb_modes);
5408e2bab3fSAlgea Cao }
5418e2bab3fSAlgea Cao 
5428e2bab3fSAlgea Cao /**
5438e2bab3fSAlgea Cao  * drm_mode_is_420_also - if a given videomode can be supported in YCBCR420
5448e2bab3fSAlgea Cao  * output format also (along with RGB/YCBCR444/422)
5458e2bab3fSAlgea Cao  *
5468e2bab3fSAlgea Cao  * @display: display under action.
5478e2bab3fSAlgea Cao  * @mode: video mode to be tested.
5488e2bab3fSAlgea Cao  *
5498e2bab3fSAlgea Cao  * Returns:
5508e2bab3fSAlgea Cao  * true if the mode can be support YCBCR420 format
5518e2bab3fSAlgea Cao  * false if not.
5528e2bab3fSAlgea Cao  */
5538e2bab3fSAlgea Cao bool drm_mode_is_420_also(const struct drm_display_info *display,
5548e2bab3fSAlgea Cao 			  struct drm_display_mode *mode)
5558e2bab3fSAlgea Cao {
5568e2bab3fSAlgea Cao 	u8 vic = drm_match_cea_mode(mode);
5578e2bab3fSAlgea Cao 
5588e2bab3fSAlgea Cao 	return test_bit(vic, display->hdmi.y420_cmdb_modes);
5598e2bab3fSAlgea Cao }
5608e2bab3fSAlgea Cao 
5618e2bab3fSAlgea Cao /**
5628e2bab3fSAlgea Cao  * drm_mode_is_420 - if a given videomode can be supported in YCBCR420
5638e2bab3fSAlgea Cao  * output format
5648e2bab3fSAlgea Cao  *
5658e2bab3fSAlgea Cao  * @display: display under action.
5668e2bab3fSAlgea Cao  * @mode: video mode to be tested.
5678e2bab3fSAlgea Cao  *
5688e2bab3fSAlgea Cao  * Returns:
5698e2bab3fSAlgea Cao  * true if the mode can be supported in YCBCR420 format
5708e2bab3fSAlgea Cao  * false if not.
5718e2bab3fSAlgea Cao  */
5728e2bab3fSAlgea Cao bool drm_mode_is_420(const struct drm_display_info *display,
5738e2bab3fSAlgea Cao 		     struct drm_display_mode *mode)
5748e2bab3fSAlgea Cao {
5758e2bab3fSAlgea Cao 	return drm_mode_is_420_only(display, mode) ||
5768e2bab3fSAlgea Cao 		drm_mode_is_420_also(display, mode);
5778e2bab3fSAlgea Cao }
5788e2bab3fSAlgea Cao 
579186f8572SMark Yao static int display_get_timing(struct display_state *state)
580186f8572SMark Yao {
581186f8572SMark Yao 	struct connector_state *conn_state = &state->conn_state;
582186f8572SMark Yao 	const struct rockchip_connector *conn = conn_state->connector;
583186f8572SMark Yao 	const struct rockchip_connector_funcs *conn_funcs = conn->funcs;
584186f8572SMark Yao 	struct drm_display_mode *mode = &conn_state->mode;
585186f8572SMark Yao 	const struct drm_display_mode *m;
5864b8c2ef1SMark Yao 	struct panel_state *panel_state = &state->panel_state;
587c493d00eSWyon Bi 	const struct rockchip_panel *panel = panel_state->panel;
588c493d00eSWyon Bi 	const struct rockchip_panel_funcs *panel_funcs = panel->funcs;
589c493d00eSWyon Bi 	ofnode panel_node = panel_state->node;
590c493d00eSWyon Bi 	int ret;
591186f8572SMark Yao 
592c493d00eSWyon Bi 	if (ofnode_valid(panel_node) && !display_get_timing_from_dts(panel_state, mode)) {
593186f8572SMark Yao 		printf("Using display timing dts\n");
594186f8572SMark Yao 		goto done;
595186f8572SMark Yao 	}
596186f8572SMark Yao 
597c493d00eSWyon Bi 	if (panel->data) {
598c493d00eSWyon Bi 		m = (const struct drm_display_mode *)panel->data;
599186f8572SMark Yao 		memcpy(mode, m, sizeof(*m));
600c493d00eSWyon Bi 		printf("Using display timing from compatible panel driver\n");
601186f8572SMark Yao 		goto done;
602186f8572SMark Yao 	}
603186f8572SMark Yao 
604186f8572SMark Yao 	if (conn_funcs->get_edid && !conn_funcs->get_edid(state)) {
605186f8572SMark Yao 		int panel_bits_per_colourp;
606186f8572SMark Yao 
607c493d00eSWyon Bi 		/* In order to read EDID, the panel needs to be powered on */
608c493d00eSWyon Bi 		if (panel_funcs->prepare) {
609c493d00eSWyon Bi 			ret = panel_funcs->prepare(state);
610c493d00eSWyon Bi 			if (ret) {
611c493d00eSWyon Bi 				printf("failed to prepare panel\n");
612c493d00eSWyon Bi 				return ret;
613c493d00eSWyon Bi 			}
614c493d00eSWyon Bi 		}
615c493d00eSWyon Bi 
616186f8572SMark Yao 		if (!edid_get_drm_mode((void *)&conn_state->edid,
617186f8572SMark Yao 				     sizeof(conn_state->edid), mode,
618186f8572SMark Yao 				     &panel_bits_per_colourp)) {
619186f8572SMark Yao 			printf("Using display timing from edid\n");
620186f8572SMark Yao 			edid_print_info((void *)&conn_state->edid);
621186f8572SMark Yao 			goto done;
622c493d00eSWyon Bi 		} else {
623c493d00eSWyon Bi 			if (panel_funcs->unprepare)
624c493d00eSWyon Bi 				panel_funcs->unprepare(state);
625186f8572SMark Yao 		}
626186f8572SMark Yao 	}
627186f8572SMark Yao 
628186f8572SMark Yao 	printf("failed to find display timing\n");
629186f8572SMark Yao 	return -ENODEV;
630186f8572SMark Yao done:
631186f8572SMark Yao 	printf("Detailed mode clock %u kHz, flags[%x]\n"
632186f8572SMark Yao 	       "    H: %04d %04d %04d %04d\n"
633186f8572SMark Yao 	       "    V: %04d %04d %04d %04d\n"
634186f8572SMark Yao 	       "bus_format: %x\n",
635186f8572SMark Yao 	       mode->clock, mode->flags,
636186f8572SMark Yao 	       mode->hdisplay, mode->hsync_start,
637186f8572SMark Yao 	       mode->hsync_end, mode->htotal,
638186f8572SMark Yao 	       mode->vdisplay, mode->vsync_start,
639186f8572SMark Yao 	       mode->vsync_end, mode->vtotal,
640186f8572SMark Yao 	       conn_state->bus_format);
641186f8572SMark Yao 
642186f8572SMark Yao 	return 0;
643186f8572SMark Yao }
644186f8572SMark Yao 
645186f8572SMark Yao static int display_init(struct display_state *state)
646186f8572SMark Yao {
647186f8572SMark Yao 	struct connector_state *conn_state = &state->conn_state;
648186f8572SMark Yao 	const struct rockchip_connector *conn = conn_state->connector;
649186f8572SMark Yao 	const struct rockchip_connector_funcs *conn_funcs = conn->funcs;
650186f8572SMark Yao 	struct crtc_state *crtc_state = &state->crtc_state;
6512a48727aSAlgea Cao 	struct rockchip_crtc *crtc = crtc_state->crtc;
652186f8572SMark Yao 	const struct rockchip_crtc_funcs *crtc_funcs = crtc->funcs;
653ccd843b9SSandy Huang 	struct drm_display_mode *mode = &conn_state->mode;
654186f8572SMark Yao 	int ret = 0;
65552015e97SSandy Huang 	static bool __print_once = false;
656186f8572SMark Yao 
65752015e97SSandy Huang 	if (!__print_once) {
65852015e97SSandy Huang 		__print_once = true;
659e559407dSSandy Huang 		printf("Rockchip UBOOT DRM driver version: %s\n", DRIVER_VERSION);
66052015e97SSandy Huang 	}
661e559407dSSandy Huang 
662186f8572SMark Yao 	if (state->is_init)
663186f8572SMark Yao 		return 0;
664186f8572SMark Yao 
665186f8572SMark Yao 	if (!conn_funcs || !crtc_funcs) {
666186f8572SMark Yao 		printf("failed to find connector or crtc functions\n");
667186f8572SMark Yao 		return -ENXIO;
668186f8572SMark Yao 	}
669186f8572SMark Yao 
670186f8572SMark Yao 	if (conn_funcs->init) {
671186f8572SMark Yao 		ret = conn_funcs->init(state);
672186f8572SMark Yao 		if (ret)
6734b8c2ef1SMark Yao 			goto deinit;
674186f8572SMark Yao 	}
675186f8572SMark Yao 	/*
676186f8572SMark Yao 	 * support hotplug, but not connect;
677186f8572SMark Yao 	 */
6782a48727aSAlgea Cao #ifdef CONFIG_ROCKCHIP_DRM_TVE
6792a48727aSAlgea Cao 	if (crtc->hdmi_hpd && conn_state->type == DRM_MODE_CONNECTOR_TV) {
6802a48727aSAlgea Cao 		printf("hdmi plugin ,skip tve\n");
6812a48727aSAlgea Cao 		goto deinit;
6822a48727aSAlgea Cao 	}
6832a48727aSAlgea Cao #elif defined(CONFIG_ROCKCHIP_DRM_RK1000)
6842a48727aSAlgea Cao 	if (crtc->hdmi_hpd && conn_state->type == DRM_MODE_CONNECTOR_LVDS) {
6852a48727aSAlgea Cao 		printf("hdmi plugin ,skip tve\n");
6862a48727aSAlgea Cao 		goto deinit;
6872a48727aSAlgea Cao 	}
6882a48727aSAlgea Cao #endif
689186f8572SMark Yao 	if (conn_funcs->detect) {
690186f8572SMark Yao 		ret = conn_funcs->detect(state);
6912a48727aSAlgea Cao #if defined(CONFIG_ROCKCHIP_DRM_TVE) || defined(CONFIG_ROCKCHIP_DRM_RK1000)
6922a48727aSAlgea Cao 		if (conn_state->type == DRM_MODE_CONNECTOR_HDMIA)
6932a48727aSAlgea Cao 			crtc->hdmi_hpd = ret;
6942a48727aSAlgea Cao #endif
695186f8572SMark Yao 		if (!ret)
696186f8572SMark Yao 			goto deinit;
697186f8572SMark Yao 	}
698186f8572SMark Yao 
699186f8572SMark Yao 	if (conn_funcs->get_timing) {
700186f8572SMark Yao 		ret = conn_funcs->get_timing(state);
701186f8572SMark Yao 		if (ret)
702186f8572SMark Yao 			goto deinit;
703186f8572SMark Yao 	} else {
704186f8572SMark Yao 		ret = display_get_timing(state);
705186f8572SMark Yao 		if (ret)
706186f8572SMark Yao 			goto deinit;
707186f8572SMark Yao 	}
708ccd843b9SSandy Huang 	drm_mode_set_crtcinfo(mode, CRTC_INTERLACE_HALVE_V);
709186f8572SMark Yao 
710186f8572SMark Yao 	if (crtc_funcs->init) {
711186f8572SMark Yao 		ret = crtc_funcs->init(state);
712186f8572SMark Yao 		if (ret)
713186f8572SMark Yao 			goto deinit;
714186f8572SMark Yao 	}
715186f8572SMark Yao 	state->is_init = 1;
716186f8572SMark Yao 
717186f8572SMark Yao 	return 0;
718186f8572SMark Yao 
719186f8572SMark Yao deinit:
720186f8572SMark Yao 	if (conn_funcs->deinit)
721186f8572SMark Yao 		conn_funcs->deinit(state);
722186f8572SMark Yao 	return ret;
723186f8572SMark Yao }
724186f8572SMark Yao 
72567b9012cSSandy Huang int display_send_mcu_cmd(struct display_state *state, u32 type, u32 val)
72667b9012cSSandy Huang {
72767b9012cSSandy Huang 	struct crtc_state *crtc_state = &state->crtc_state;
72867b9012cSSandy Huang 	const struct rockchip_crtc *crtc = crtc_state->crtc;
72967b9012cSSandy Huang 	const struct rockchip_crtc_funcs *crtc_funcs = crtc->funcs;
73067b9012cSSandy Huang 	int ret;
73167b9012cSSandy Huang 
73267b9012cSSandy Huang 	if (!state->is_init)
73367b9012cSSandy Huang 		return -EINVAL;
73467b9012cSSandy Huang 
73567b9012cSSandy Huang 	if (crtc_funcs->send_mcu_cmd) {
73667b9012cSSandy Huang 		ret = crtc_funcs->send_mcu_cmd(state, type, val);
73767b9012cSSandy Huang 		if (ret)
73867b9012cSSandy Huang 			return ret;
73967b9012cSSandy Huang 	}
74067b9012cSSandy Huang 
74167b9012cSSandy Huang 	return 0;
74267b9012cSSandy Huang }
74367b9012cSSandy Huang 
744186f8572SMark Yao static int display_set_plane(struct display_state *state)
745186f8572SMark Yao {
746186f8572SMark Yao 	struct crtc_state *crtc_state = &state->crtc_state;
747186f8572SMark Yao 	const struct rockchip_crtc *crtc = crtc_state->crtc;
748186f8572SMark Yao 	const struct rockchip_crtc_funcs *crtc_funcs = crtc->funcs;
749186f8572SMark Yao 	int ret;
750186f8572SMark Yao 
751186f8572SMark Yao 	if (!state->is_init)
752186f8572SMark Yao 		return -EINVAL;
753186f8572SMark Yao 
754186f8572SMark Yao 	if (crtc_funcs->set_plane) {
755186f8572SMark Yao 		ret = crtc_funcs->set_plane(state);
756186f8572SMark Yao 		if (ret)
757186f8572SMark Yao 			return ret;
758186f8572SMark Yao 	}
759186f8572SMark Yao 
760186f8572SMark Yao 	return 0;
761186f8572SMark Yao }
762186f8572SMark Yao 
763bcf90936SSandy Huang static int display_panel_prepare(struct display_state *state)
764bcf90936SSandy Huang {
765bcf90936SSandy Huang 	struct panel_state *panel_state = &state->panel_state;
766bcf90936SSandy Huang 	const struct rockchip_panel *panel = panel_state->panel;
767bcf90936SSandy Huang 
768bcf90936SSandy Huang 	if (!panel || !panel->funcs || !panel->funcs->prepare) {
769bcf90936SSandy Huang 		printf("%s: failed to find panel prepare funcs\n", __func__);
770bcf90936SSandy Huang 		return -ENODEV;
771bcf90936SSandy Huang 	}
772bcf90936SSandy Huang 
773bcf90936SSandy Huang 	return panel->funcs->prepare(state);
774bcf90936SSandy Huang }
775bcf90936SSandy Huang 
776bcf90936SSandy Huang static int display_panel_enable(struct display_state *state)
777bcf90936SSandy Huang {
778bcf90936SSandy Huang 	struct panel_state *panel_state = &state->panel_state;
779bcf90936SSandy Huang 	const struct rockchip_panel *panel = panel_state->panel;
780bcf90936SSandy Huang 
781bcf90936SSandy Huang 	if (!panel || !panel->funcs || !panel->funcs->enable) {
782bcf90936SSandy Huang 		printf("%s: failed to find panel enable funcs\n", __func__);
783bcf90936SSandy Huang 		return -ENODEV;
784bcf90936SSandy Huang 	}
785bcf90936SSandy Huang 
786bcf90936SSandy Huang 	return panel->funcs->enable(state);
787bcf90936SSandy Huang }
788bcf90936SSandy Huang 
789bcf90936SSandy Huang static void display_panel_unprepare(struct display_state *state)
790bcf90936SSandy Huang {
791bcf90936SSandy Huang 	struct panel_state *panel_state = &state->panel_state;
792bcf90936SSandy Huang 	const struct rockchip_panel *panel = panel_state->panel;
793bcf90936SSandy Huang 
794bcf90936SSandy Huang 	if (!panel || !panel->funcs || !panel->funcs->unprepare) {
795bcf90936SSandy Huang 		printf("%s: failed to find panel unprepare funcs\n", __func__);
796bcf90936SSandy Huang 		return;
797bcf90936SSandy Huang 	}
798bcf90936SSandy Huang 
799bcf90936SSandy Huang 	panel->funcs->unprepare(state);
800bcf90936SSandy Huang }
801bcf90936SSandy Huang 
802bcf90936SSandy Huang static void display_panel_disable(struct display_state *state)
803bcf90936SSandy Huang {
804bcf90936SSandy Huang 	struct panel_state *panel_state = &state->panel_state;
805bcf90936SSandy Huang 	const struct rockchip_panel *panel = panel_state->panel;
806bcf90936SSandy Huang 
807bcf90936SSandy Huang 	if (!panel || !panel->funcs || !panel->funcs->disable) {
808bcf90936SSandy Huang 		printf("%s: failed to find panel disable funcs\n", __func__);
809bcf90936SSandy Huang 		return;
810bcf90936SSandy Huang 	}
811bcf90936SSandy Huang 
812bcf90936SSandy Huang 	panel->funcs->disable(state);
813bcf90936SSandy Huang }
814bcf90936SSandy Huang 
815186f8572SMark Yao static int display_enable(struct display_state *state)
816186f8572SMark Yao {
817186f8572SMark Yao 	struct connector_state *conn_state = &state->conn_state;
818186f8572SMark Yao 	const struct rockchip_connector *conn = conn_state->connector;
819186f8572SMark Yao 	const struct rockchip_connector_funcs *conn_funcs = conn->funcs;
820186f8572SMark Yao 	struct crtc_state *crtc_state = &state->crtc_state;
821186f8572SMark Yao 	const struct rockchip_crtc *crtc = crtc_state->crtc;
822186f8572SMark Yao 	const struct rockchip_crtc_funcs *crtc_funcs = crtc->funcs;
823186f8572SMark Yao 	int ret = 0;
824186f8572SMark Yao 
825186f8572SMark Yao 	display_init(state);
826186f8572SMark Yao 
827186f8572SMark Yao 	if (!state->is_init)
828186f8572SMark Yao 		return -EINVAL;
829186f8572SMark Yao 
830186f8572SMark Yao 	if (state->is_enable)
831186f8572SMark Yao 		return 0;
832186f8572SMark Yao 
833186f8572SMark Yao 	if (crtc_funcs->prepare) {
834186f8572SMark Yao 		ret = crtc_funcs->prepare(state);
835186f8572SMark Yao 		if (ret)
836186f8572SMark Yao 			return ret;
837186f8572SMark Yao 	}
838186f8572SMark Yao 
839186f8572SMark Yao 	if (conn_funcs->prepare) {
840186f8572SMark Yao 		ret = conn_funcs->prepare(state);
841186f8572SMark Yao 		if (ret)
842186f8572SMark Yao 			goto unprepare_crtc;
843186f8572SMark Yao 	}
844186f8572SMark Yao 
845bcf90936SSandy Huang 	display_panel_prepare(state);
846186f8572SMark Yao 
847186f8572SMark Yao 	if (crtc_funcs->enable) {
848186f8572SMark Yao 		ret = crtc_funcs->enable(state);
849186f8572SMark Yao 		if (ret)
850bcf90936SSandy Huang 			goto unprepare_conn;
851186f8572SMark Yao 	}
852186f8572SMark Yao 
853186f8572SMark Yao 	if (conn_funcs->enable) {
854186f8572SMark Yao 		ret = conn_funcs->enable(state);
855186f8572SMark Yao 		if (ret)
856186f8572SMark Yao 			goto disable_crtc;
857186f8572SMark Yao 	}
858186f8572SMark Yao 
859bcf90936SSandy Huang 	display_panel_enable(state);
860186f8572SMark Yao 
861186f8572SMark Yao 	state->is_enable = true;
862186f8572SMark Yao 	return 0;
863c493d00eSWyon Bi 
864186f8572SMark Yao disable_crtc:
865186f8572SMark Yao 	if (crtc_funcs->disable)
866186f8572SMark Yao 		crtc_funcs->disable(state);
867c493d00eSWyon Bi unprepare_conn:
868c493d00eSWyon Bi 	if (conn_funcs->unprepare)
869c493d00eSWyon Bi 		conn_funcs->unprepare(state);
870bcf90936SSandy Huang unprepare_crtc:
871bcf90936SSandy Huang 	if (crtc_funcs->unprepare)
872bcf90936SSandy Huang 		crtc_funcs->unprepare(state);
873186f8572SMark Yao 	return ret;
874186f8572SMark Yao }
875186f8572SMark Yao 
876186f8572SMark Yao static int display_disable(struct display_state *state)
877186f8572SMark Yao {
878186f8572SMark Yao 	struct connector_state *conn_state = &state->conn_state;
879186f8572SMark Yao 	const struct rockchip_connector *conn = conn_state->connector;
880186f8572SMark Yao 	const struct rockchip_connector_funcs *conn_funcs = conn->funcs;
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 
885186f8572SMark Yao 	if (!state->is_init)
886186f8572SMark Yao 		return 0;
887186f8572SMark Yao 
888186f8572SMark Yao 	if (!state->is_enable)
889186f8572SMark Yao 		return 0;
890186f8572SMark Yao 
891bcf90936SSandy Huang 	display_panel_disable(state);
892186f8572SMark Yao 
893186f8572SMark Yao 	if (crtc_funcs->disable)
894186f8572SMark Yao 		crtc_funcs->disable(state);
895186f8572SMark Yao 
896186f8572SMark Yao 	if (conn_funcs->disable)
897186f8572SMark Yao 		conn_funcs->disable(state);
898186f8572SMark Yao 
899bcf90936SSandy Huang 	display_panel_unprepare(state);
900186f8572SMark Yao 
901186f8572SMark Yao 	if (conn_funcs->unprepare)
902186f8572SMark Yao 		conn_funcs->unprepare(state);
903186f8572SMark Yao 
904186f8572SMark Yao 	state->is_enable = 0;
905186f8572SMark Yao 	state->is_init = 0;
906186f8572SMark Yao 
907186f8572SMark Yao 	return 0;
908186f8572SMark Yao }
909186f8572SMark Yao 
910186f8572SMark Yao static int display_logo(struct display_state *state)
911186f8572SMark Yao {
912186f8572SMark Yao 	struct crtc_state *crtc_state = &state->crtc_state;
913186f8572SMark Yao 	struct connector_state *conn_state = &state->conn_state;
914186f8572SMark Yao 	struct logo_info *logo = &state->logo;
915186f8572SMark Yao 	int hdisplay, vdisplay;
916186f8572SMark Yao 
917186f8572SMark Yao 	display_init(state);
918186f8572SMark Yao 	if (!state->is_init)
919186f8572SMark Yao 		return -ENODEV;
920186f8572SMark Yao 
921186f8572SMark Yao 	switch (logo->bpp) {
922186f8572SMark Yao 	case 16:
923186f8572SMark Yao 		crtc_state->format = ROCKCHIP_FMT_RGB565;
924186f8572SMark Yao 		break;
925186f8572SMark Yao 	case 24:
926186f8572SMark Yao 		crtc_state->format = ROCKCHIP_FMT_RGB888;
927186f8572SMark Yao 		break;
928186f8572SMark Yao 	case 32:
929186f8572SMark Yao 		crtc_state->format = ROCKCHIP_FMT_ARGB8888;
930186f8572SMark Yao 		break;
931186f8572SMark Yao 	default:
932186f8572SMark Yao 		printf("can't support bmp bits[%d]\n", logo->bpp);
933186f8572SMark Yao 		return -EINVAL;
934186f8572SMark Yao 	}
935861ce1a0SSandy Huang 	crtc_state->rb_swap = logo->bpp != 32;
936186f8572SMark Yao 	hdisplay = conn_state->mode.hdisplay;
937186f8572SMark Yao 	vdisplay = conn_state->mode.vdisplay;
938186f8572SMark Yao 	crtc_state->src_w = logo->width;
939186f8572SMark Yao 	crtc_state->src_h = logo->height;
940186f8572SMark Yao 	crtc_state->src_x = 0;
941186f8572SMark Yao 	crtc_state->src_y = 0;
942186f8572SMark Yao 	crtc_state->ymirror = logo->ymirror;
943186f8572SMark Yao 
9444b8c2ef1SMark Yao 	crtc_state->dma_addr = (u32)(unsigned long)logo->mem + logo->offset;
945186f8572SMark Yao 	crtc_state->xvir = ALIGN(crtc_state->src_w * logo->bpp, 32) >> 5;
946186f8572SMark Yao 
947186f8572SMark Yao 	if (logo->mode == ROCKCHIP_DISPLAY_FULLSCREEN) {
948186f8572SMark Yao 		crtc_state->crtc_x = 0;
949186f8572SMark Yao 		crtc_state->crtc_y = 0;
950186f8572SMark Yao 		crtc_state->crtc_w = hdisplay;
951186f8572SMark Yao 		crtc_state->crtc_h = vdisplay;
952186f8572SMark Yao 	} else {
953186f8572SMark Yao 		if (crtc_state->src_w >= hdisplay) {
954186f8572SMark Yao 			crtc_state->crtc_x = 0;
955186f8572SMark Yao 			crtc_state->crtc_w = hdisplay;
956186f8572SMark Yao 		} else {
957186f8572SMark Yao 			crtc_state->crtc_x = (hdisplay - crtc_state->src_w) / 2;
958186f8572SMark Yao 			crtc_state->crtc_w = crtc_state->src_w;
959186f8572SMark Yao 		}
960186f8572SMark Yao 
961186f8572SMark Yao 		if (crtc_state->src_h >= vdisplay) {
962186f8572SMark Yao 			crtc_state->crtc_y = 0;
963186f8572SMark Yao 			crtc_state->crtc_h = vdisplay;
964186f8572SMark Yao 		} else {
965186f8572SMark Yao 			crtc_state->crtc_y = (vdisplay - crtc_state->src_h) / 2;
966186f8572SMark Yao 			crtc_state->crtc_h = crtc_state->src_h;
967186f8572SMark Yao 		}
968186f8572SMark Yao 	}
969186f8572SMark Yao 
970186f8572SMark Yao 	display_set_plane(state);
971186f8572SMark Yao 	display_enable(state);
972186f8572SMark Yao 
973186f8572SMark Yao 	return 0;
974186f8572SMark Yao }
975186f8572SMark Yao 
976e2bce6e4SKever Yang static int get_crtc_id(ofnode connect)
977186f8572SMark Yao {
978e2bce6e4SKever Yang 	int phandle;
979e2bce6e4SKever Yang 	struct device_node *remote;
980186f8572SMark Yao 	int val;
981186f8572SMark Yao 
982e2bce6e4SKever Yang 	phandle = ofnode_read_u32_default(connect, "remote-endpoint", -1);
983186f8572SMark Yao 	if (phandle < 0)
984186f8572SMark Yao 		goto err;
985e2bce6e4SKever Yang 	remote = of_find_node_by_phandle(phandle);
986e2bce6e4SKever Yang 	val = ofnode_read_u32_default(np_to_ofnode(remote), "reg", -1);
987186f8572SMark Yao 	if (val < 0)
988186f8572SMark Yao 		goto err;
989186f8572SMark Yao 
990186f8572SMark Yao 	return val;
991186f8572SMark Yao err:
992186f8572SMark Yao 	printf("Can't get crtc id, default set to id = 0\n");
993186f8572SMark Yao 	return 0;
994186f8572SMark Yao }
995186f8572SMark Yao 
99667b9012cSSandy Huang static int get_crtc_mcu_mode(struct crtc_state *crtc_state)
99767b9012cSSandy Huang {
99867b9012cSSandy Huang 	ofnode mcu_node;
99967b9012cSSandy Huang 	int total_pixel, cs_pst, cs_pend, rw_pst, rw_pend;
100067b9012cSSandy Huang 
100167b9012cSSandy Huang 	mcu_node = dev_read_subnode(crtc_state->dev, "mcu-timing");
1002a196d7fcSKever Yang 	if (!ofnode_valid(mcu_node))
1003a196d7fcSKever Yang 		return -ENODEV;
100467b9012cSSandy Huang 
100567b9012cSSandy Huang #define FDT_GET_MCU_INT(val, name) \
100667b9012cSSandy Huang 	do { \
100767b9012cSSandy Huang 		val = ofnode_read_s32_default(mcu_node, name, -1); \
100867b9012cSSandy Huang 		if (val < 0) { \
100967b9012cSSandy Huang 			printf("Can't get %s\n", name); \
101067b9012cSSandy Huang 			return -ENXIO; \
101167b9012cSSandy Huang 		} \
101267b9012cSSandy Huang 	} while (0)
101367b9012cSSandy Huang 
101467b9012cSSandy Huang 	FDT_GET_MCU_INT(total_pixel, "mcu-pix-total");
101567b9012cSSandy Huang 	FDT_GET_MCU_INT(cs_pst, "mcu-cs-pst");
101667b9012cSSandy Huang 	FDT_GET_MCU_INT(cs_pend, "mcu-cs-pend");
101767b9012cSSandy Huang 	FDT_GET_MCU_INT(rw_pst, "mcu-rw-pst");
101867b9012cSSandy Huang 	FDT_GET_MCU_INT(rw_pend, "mcu-rw-pend");
101967b9012cSSandy Huang 
102067b9012cSSandy Huang 	crtc_state->mcu_timing.mcu_pix_total = total_pixel;
102167b9012cSSandy Huang 	crtc_state->mcu_timing.mcu_cs_pst = cs_pst;
102267b9012cSSandy Huang 	crtc_state->mcu_timing.mcu_cs_pend = cs_pend;
102367b9012cSSandy Huang 	crtc_state->mcu_timing.mcu_rw_pst = rw_pst;
102467b9012cSSandy Huang 	crtc_state->mcu_timing.mcu_rw_pend = rw_pend;
102567b9012cSSandy Huang 
102667b9012cSSandy Huang 	return 0;
102767b9012cSSandy Huang }
102867b9012cSSandy Huang 
1029186f8572SMark Yao struct rockchip_logo_cache *find_or_alloc_logo_cache(const char *bmp)
1030186f8572SMark Yao {
1031186f8572SMark Yao 	struct rockchip_logo_cache *tmp, *logo_cache = NULL;
1032186f8572SMark Yao 
1033186f8572SMark Yao 	list_for_each_entry(tmp, &logo_cache_list, head) {
1034186f8572SMark Yao 		if (!strcmp(tmp->name, bmp)) {
1035186f8572SMark Yao 			logo_cache = tmp;
1036186f8572SMark Yao 			break;
1037186f8572SMark Yao 		}
1038186f8572SMark Yao 	}
1039186f8572SMark Yao 
1040186f8572SMark Yao 	if (!logo_cache) {
1041186f8572SMark Yao 		logo_cache = malloc(sizeof(*logo_cache));
1042186f8572SMark Yao 		if (!logo_cache) {
1043186f8572SMark Yao 			printf("failed to alloc memory for logo cache\n");
1044186f8572SMark Yao 			return NULL;
1045186f8572SMark Yao 		}
1046186f8572SMark Yao 		memset(logo_cache, 0, sizeof(*logo_cache));
1047186f8572SMark Yao 		strcpy(logo_cache->name, bmp);
1048186f8572SMark Yao 		INIT_LIST_HEAD(&logo_cache->head);
1049186f8572SMark Yao 		list_add_tail(&logo_cache->head, &logo_cache_list);
1050186f8572SMark Yao 	}
1051186f8572SMark Yao 
1052186f8572SMark Yao 	return logo_cache;
1053186f8572SMark Yao }
1054186f8572SMark Yao 
10555eb61944SSandy Huang /* Note: used only for rkfb kernel driver */
10565eb61944SSandy Huang static int load_kernel_bmp_logo(struct logo_info *logo, const char *bmp_name)
10575eb61944SSandy Huang {
10585eb61944SSandy Huang #ifdef CONFIG_ROCKCHIP_RESOURCE_IMAGE
10595eb61944SSandy Huang 	void *dst = NULL;
10605eb61944SSandy Huang 	int len, size;
10615eb61944SSandy Huang 	struct bmp_header *header;
10625eb61944SSandy Huang 
10635eb61944SSandy Huang 	if (!logo || !bmp_name)
10645eb61944SSandy Huang 		return -EINVAL;
10655eb61944SSandy Huang 
10665eb61944SSandy Huang 	header = malloc(RK_BLK_SIZE);
10675eb61944SSandy Huang 	if (!header)
10685eb61944SSandy Huang 		return -ENOMEM;
10695eb61944SSandy Huang 
10705eb61944SSandy Huang 	len = rockchip_read_resource_file(header, bmp_name, 0, RK_BLK_SIZE);
10715eb61944SSandy Huang 	if (len != RK_BLK_SIZE) {
10725eb61944SSandy Huang 		free(header);
10735eb61944SSandy Huang 		return -EINVAL;
10745eb61944SSandy Huang 	}
10755eb61944SSandy Huang 	size = get_unaligned_le32(&header->file_size);
10765eb61944SSandy Huang 	dst = (void *)(memory_start + MEMORY_POOL_SIZE / 2);
10775eb61944SSandy Huang 	len = rockchip_read_resource_file(dst, bmp_name, 0, size);
10785eb61944SSandy Huang 	if (len != size) {
10795eb61944SSandy Huang 		printf("failed to load bmp %s\n", bmp_name);
10805eb61944SSandy Huang 		free(header);
10815eb61944SSandy Huang 		return -ENOENT;
10825eb61944SSandy Huang 	}
10835eb61944SSandy Huang 
10845eb61944SSandy Huang 	logo->mem = dst;
10855eb61944SSandy Huang 
10865eb61944SSandy Huang 	return 0;
10875eb61944SSandy Huang #endif
10885eb61944SSandy Huang }
10895eb61944SSandy Huang 
1090186f8572SMark Yao static int load_bmp_logo(struct logo_info *logo, const char *bmp_name)
1091186f8572SMark Yao {
10924b8c2ef1SMark Yao #ifdef CONFIG_ROCKCHIP_RESOURCE_IMAGE
1093186f8572SMark Yao 	struct rockchip_logo_cache *logo_cache;
1094186f8572SMark Yao 	struct bmp_header *header;
1095186f8572SMark Yao 	void *dst = NULL, *pdst;
10964b8c2ef1SMark Yao 	int size, len;
10974b8c2ef1SMark Yao 	int ret = 0;
1098186f8572SMark Yao 
1099186f8572SMark Yao 	if (!logo || !bmp_name)
1100186f8572SMark Yao 		return -EINVAL;
1101186f8572SMark Yao 	logo_cache = find_or_alloc_logo_cache(bmp_name);
1102186f8572SMark Yao 	if (!logo_cache)
1103186f8572SMark Yao 		return -ENOMEM;
1104186f8572SMark Yao 
1105186f8572SMark Yao 	if (logo_cache->logo.mem) {
1106186f8572SMark Yao 		memcpy(logo, &logo_cache->logo, sizeof(*logo));
1107186f8572SMark Yao 		return 0;
1108186f8572SMark Yao 	}
1109186f8572SMark Yao 
11104b8c2ef1SMark Yao 	header = malloc(RK_BLK_SIZE);
1111186f8572SMark Yao 	if (!header)
11124b8c2ef1SMark Yao 		return -ENOMEM;
11134b8c2ef1SMark Yao 
11144b8c2ef1SMark Yao 	len = rockchip_read_resource_file(header, bmp_name, 0, RK_BLK_SIZE);
11154b8c2ef1SMark Yao 	if (len != RK_BLK_SIZE) {
11164b8c2ef1SMark Yao 		ret = -EINVAL;
11174b8c2ef1SMark Yao 		goto free_header;
11184b8c2ef1SMark Yao 	}
1119186f8572SMark Yao 
1120186f8572SMark Yao 	logo->bpp = get_unaligned_le16(&header->bit_count);
1121186f8572SMark Yao 	logo->width = get_unaligned_le32(&header->width);
1122186f8572SMark Yao 	logo->height = get_unaligned_le32(&header->height);
1123186f8572SMark Yao 	size = get_unaligned_le32(&header->file_size);
1124861ce1a0SSandy Huang 	if (!can_direct_logo(logo->bpp)) {
1125186f8572SMark Yao 		if (size > MEMORY_POOL_SIZE) {
1126186f8572SMark Yao 			printf("failed to use boot buf as temp bmp buffer\n");
11274b8c2ef1SMark Yao 			ret = -ENOMEM;
11284b8c2ef1SMark Yao 			goto free_header;
1129186f8572SMark Yao 		}
1130861ce1a0SSandy Huang 		pdst = get_display_buffer(size);
1131186f8572SMark Yao 
1132861ce1a0SSandy Huang 	} else {
1133186f8572SMark Yao 		pdst = get_display_buffer(size);
1134186f8572SMark Yao 		dst = pdst;
1135861ce1a0SSandy Huang 	}
1136186f8572SMark Yao 
11374b8c2ef1SMark Yao 	len = rockchip_read_resource_file(pdst, bmp_name, 0, size);
11384b8c2ef1SMark Yao 	if (len != size) {
1139186f8572SMark Yao 		printf("failed to load bmp %s\n", bmp_name);
11404b8c2ef1SMark Yao 		ret = -ENOENT;
11414b8c2ef1SMark Yao 		goto free_header;
1142186f8572SMark Yao 	}
114355e2f86dSSandy Huang 
1144861ce1a0SSandy Huang 	if (!can_direct_logo(logo->bpp)) {
1145186f8572SMark Yao 		int dst_size;
1146186f8572SMark Yao 		/*
1147186f8572SMark Yao 		 * TODO: force use 16bpp if bpp less than 16;
1148186f8572SMark Yao 		 */
1149186f8572SMark Yao 		logo->bpp = (logo->bpp <= 16) ? 16 : logo->bpp;
1150186f8572SMark Yao 		dst_size = logo->width * logo->height * logo->bpp >> 3;
115155e2f86dSSandy Huang 
1152186f8572SMark Yao 		dst = get_display_buffer(dst_size);
11534b8c2ef1SMark Yao 		if (!dst) {
11544b8c2ef1SMark Yao 			ret = -ENOMEM;
11554b8c2ef1SMark Yao 			goto free_header;
11564b8c2ef1SMark Yao 		}
115755e2f86dSSandy Huang 		if (bmpdecoder(pdst, dst, logo->bpp)) {
1158186f8572SMark Yao 			printf("failed to decode bmp %s\n", bmp_name);
11594b8c2ef1SMark Yao 			ret = -EINVAL;
11604b8c2ef1SMark Yao 			goto free_header;
1161186f8572SMark Yao 		}
11624b8c2ef1SMark Yao 		flush_dcache_range((ulong)dst,
11634b8c2ef1SMark Yao 				   ALIGN((ulong)dst + dst_size,
11644b8c2ef1SMark Yao 					 CONFIG_SYS_CACHELINE_SIZE));
116555e2f86dSSandy Huang 
1166186f8572SMark Yao 		logo->offset = 0;
116755e2f86dSSandy Huang 		logo->ymirror = 0;
1168186f8572SMark Yao 	} else {
1169186f8572SMark Yao 		logo->offset = get_unaligned_le32(&header->data_offset);
117055e2f86dSSandy Huang 		logo->ymirror = 1;
1171186f8572SMark Yao 	}
11724b8c2ef1SMark Yao 	logo->mem = dst;
1173186f8572SMark Yao 
1174186f8572SMark Yao 	memcpy(&logo_cache->logo, logo, sizeof(*logo));
1175186f8572SMark Yao 
11764b8c2ef1SMark Yao free_header:
11774b8c2ef1SMark Yao 
11784b8c2ef1SMark Yao 	free(header);
11794b8c2ef1SMark Yao 
11804b8c2ef1SMark Yao 	return ret;
11814b8c2ef1SMark Yao #else
11824b8c2ef1SMark Yao 	return -EINVAL;
11834b8c2ef1SMark Yao #endif
1184186f8572SMark Yao }
1185186f8572SMark Yao 
1186186f8572SMark Yao void rockchip_show_fbbase(ulong fbbase)
1187186f8572SMark Yao {
1188186f8572SMark Yao 	struct display_state *s;
1189186f8572SMark Yao 
1190186f8572SMark Yao 	list_for_each_entry(s, &rockchip_display_list, head) {
1191186f8572SMark Yao 		s->logo.mode = ROCKCHIP_DISPLAY_FULLSCREEN;
11924b8c2ef1SMark Yao 		s->logo.mem = (char *)fbbase;
1193186f8572SMark Yao 		s->logo.width = DRM_ROCKCHIP_FB_WIDTH;
1194186f8572SMark Yao 		s->logo.height = DRM_ROCKCHIP_FB_HEIGHT;
1195186f8572SMark Yao 		s->logo.bpp = 32;
1196186f8572SMark Yao 		s->logo.ymirror = 0;
1197186f8572SMark Yao 
1198186f8572SMark Yao 		display_logo(s);
1199186f8572SMark Yao 	}
1200186f8572SMark Yao }
1201186f8572SMark Yao 
1202186f8572SMark Yao void rockchip_show_bmp(const char *bmp)
1203186f8572SMark Yao {
1204186f8572SMark Yao 	struct display_state *s;
1205186f8572SMark Yao 
1206186f8572SMark Yao 	if (!bmp) {
1207186f8572SMark Yao 		list_for_each_entry(s, &rockchip_display_list, head)
1208186f8572SMark Yao 			display_disable(s);
1209186f8572SMark Yao 		return;
1210186f8572SMark Yao 	}
1211186f8572SMark Yao 
1212186f8572SMark Yao 	list_for_each_entry(s, &rockchip_display_list, head) {
1213186f8572SMark Yao 		s->logo.mode = s->charge_logo_mode;
1214186f8572SMark Yao 		if (load_bmp_logo(&s->logo, bmp))
1215186f8572SMark Yao 			continue;
1216186f8572SMark Yao 		display_logo(s);
1217186f8572SMark Yao 	}
1218186f8572SMark Yao }
1219186f8572SMark Yao 
1220186f8572SMark Yao void rockchip_show_logo(void)
1221186f8572SMark Yao {
1222186f8572SMark Yao 	struct display_state *s;
1223186f8572SMark Yao 
1224186f8572SMark Yao 	list_for_each_entry(s, &rockchip_display_list, head) {
1225186f8572SMark Yao 		s->logo.mode = s->logo_mode;
1226186f8572SMark Yao 		if (load_bmp_logo(&s->logo, s->ulogo_name))
1227186f8572SMark Yao 			printf("failed to display uboot logo\n");
1228186f8572SMark Yao 		else
1229186f8572SMark Yao 			display_logo(s);
12305eb61944SSandy Huang 
12315eb61944SSandy Huang 		/* Load kernel bmp in rockchip_display_fixup() later */
1232186f8572SMark Yao 	}
1233186f8572SMark Yao }
1234186f8572SMark Yao 
1235f8281ef0SWyon Bi static struct udevice *rockchip_of_find_connector(ofnode endpoint)
1236747dfc26SWyon Bi {
1237f8281ef0SWyon Bi 	ofnode ep, port, ports, conn;
1238f8281ef0SWyon Bi 	uint phandle;
1239f8281ef0SWyon Bi 	struct udevice *dev;
1240747dfc26SWyon Bi 	int ret;
1241747dfc26SWyon Bi 
1242f8281ef0SWyon Bi 	if (ofnode_read_u32(endpoint, "remote-endpoint", &phandle))
1243f8281ef0SWyon Bi 		return NULL;
1244f8281ef0SWyon Bi 
1245f8281ef0SWyon Bi 	ep = ofnode_get_by_phandle(phandle);
1246f8281ef0SWyon Bi 	if (!ofnode_valid(ep) || !ofnode_is_available(ep))
1247f8281ef0SWyon Bi 		return NULL;
1248f8281ef0SWyon Bi 
1249f8281ef0SWyon Bi 	port = ofnode_get_parent(ep);
1250747dfc26SWyon Bi 	if (!ofnode_valid(port))
1251747dfc26SWyon Bi 		return NULL;
1252747dfc26SWyon Bi 
1253f8281ef0SWyon Bi 	ports = ofnode_get_parent(port);
1254f8281ef0SWyon Bi 	if (!ofnode_valid(ports))
1255747dfc26SWyon Bi 		return NULL;
1256f8281ef0SWyon Bi 
1257f8281ef0SWyon Bi 	conn = ofnode_get_parent(ports);
1258f8281ef0SWyon Bi 	if (!ofnode_valid(conn) || !ofnode_is_available(conn))
1259f8281ef0SWyon Bi 		return NULL;
1260f8281ef0SWyon Bi 
1261f8281ef0SWyon Bi 	ret = uclass_get_device_by_ofnode(UCLASS_DISPLAY, conn, &dev);
1262f8281ef0SWyon Bi 	if (ret)
1263f8281ef0SWyon Bi 		return NULL;
1264f8281ef0SWyon Bi 
1265f8281ef0SWyon Bi 	return dev;
1266747dfc26SWyon Bi }
1267747dfc26SWyon Bi 
1268186f8572SMark Yao static int rockchip_display_probe(struct udevice *dev)
1269186f8572SMark Yao {
1270186f8572SMark Yao 	struct video_priv *uc_priv = dev_get_uclass_priv(dev);
1271186f8572SMark Yao 	struct video_uc_platdata *plat = dev_get_uclass_platdata(dev);
1272186f8572SMark Yao 	const void *blob = gd->fdt_blob;
1273e2bce6e4SKever Yang 	int phandle;
1274186f8572SMark Yao 	struct udevice *crtc_dev, *conn_dev;
12752a48727aSAlgea Cao 	struct rockchip_crtc *crtc;
1276186f8572SMark Yao 	const struct rockchip_connector *conn;
1277186f8572SMark Yao 	struct display_state *s;
1278186f8572SMark Yao 	const char *name;
1279186f8572SMark Yao 	int ret;
1280e2bce6e4SKever Yang 	ofnode node, route_node;
1281e2bce6e4SKever Yang 	struct device_node *port_node, *vop_node, *ep_node;
12822a48727aSAlgea Cao 	struct public_phy_data *data;
1283186f8572SMark Yao 
1284186f8572SMark Yao 	/* Before relocation we don't need to do anything */
1285186f8572SMark Yao 	if (!(gd->flags & GD_FLG_RELOC))
1286186f8572SMark Yao 		return 0;
12872a48727aSAlgea Cao 
12882a48727aSAlgea Cao 	data = malloc(sizeof(struct public_phy_data));
12892a48727aSAlgea Cao 	if (!data) {
12902a48727aSAlgea Cao 		printf("failed to alloc phy data\n");
12912a48727aSAlgea Cao 		return -ENOMEM;
12922a48727aSAlgea Cao 	}
12932a48727aSAlgea Cao 	data->phy_init = false;
12942a48727aSAlgea Cao 
12954b8c2ef1SMark Yao 	init_display_buffer(plat->base);
1296186f8572SMark Yao 
1297e2bce6e4SKever Yang 	route_node = dev_read_subnode(dev, "route");
1298e2bce6e4SKever Yang 	if (!ofnode_valid(route_node))
1299e2bce6e4SKever Yang 		return -ENODEV;
1300186f8572SMark Yao 
1301e2bce6e4SKever Yang 	ofnode_for_each_subnode(node, route_node) {
13021e44acfcSWyon Bi 		if (!ofnode_is_available(node))
13031e44acfcSWyon Bi 			continue;
1304e2bce6e4SKever Yang 		phandle = ofnode_read_u32_default(node, "connect", -1);
1305186f8572SMark Yao 		if (phandle < 0) {
1306e2bce6e4SKever Yang 			printf("Warn: can't find connect node's handle\n");
1307186f8572SMark Yao 			continue;
1308186f8572SMark Yao 		}
1309e2bce6e4SKever Yang 		ep_node = of_find_node_by_phandle(phandle);
1310e2bce6e4SKever Yang 		if (!ofnode_valid(np_to_ofnode(ep_node))) {
1311e2bce6e4SKever Yang 			printf("Warn: can't find endpoint node from phandle\n");
1312186f8572SMark Yao 			continue;
1313186f8572SMark Yao 		}
1314e2bce6e4SKever Yang 		port_node = of_get_parent(ep_node);
1315e2bce6e4SKever Yang 		if (!ofnode_valid(np_to_ofnode(port_node))) {
1316e2bce6e4SKever Yang 			printf("Warn: can't find port node from phandle\n");
1317186f8572SMark Yao 			continue;
1318186f8572SMark Yao 		}
1319e2bce6e4SKever Yang 		vop_node = of_get_parent(port_node);
1320e2bce6e4SKever Yang 		if (!ofnode_valid(np_to_ofnode(vop_node))) {
1321e2bce6e4SKever Yang 			printf("Warn: can't find crtc node from phandle\n");
1322e2bce6e4SKever Yang 			continue;
1323e2bce6e4SKever Yang 		}
1324e2bce6e4SKever Yang 		ret = uclass_get_device_by_ofnode(UCLASS_VIDEO_CRTC,
1325e2bce6e4SKever Yang 						  np_to_ofnode(vop_node),
1326e2bce6e4SKever Yang 						  &crtc_dev);
1327186f8572SMark Yao 		if (ret) {
1328335adcb5SKever Yang 			printf("Warn: can't find crtc driver %d\n", ret);
1329186f8572SMark Yao 			continue;
1330186f8572SMark Yao 		}
13312a48727aSAlgea Cao 		crtc = (struct rockchip_crtc *)dev_get_driver_data(crtc_dev);
1332186f8572SMark Yao 
1333f8281ef0SWyon Bi 		conn_dev = rockchip_of_find_connector(np_to_ofnode(ep_node));
1334747dfc26SWyon Bi 		if (!conn_dev) {
1335e2bce6e4SKever Yang 			printf("Warn: can't find connect driver\n");
1336186f8572SMark Yao 			continue;
1337186f8572SMark Yao 		}
1338747dfc26SWyon Bi 
1339186f8572SMark Yao 		conn = (const struct rockchip_connector *)dev_get_driver_data(conn_dev);
1340186f8572SMark Yao 
1341186f8572SMark Yao 		s = malloc(sizeof(*s));
1342186f8572SMark Yao 		if (!s)
13434b8c2ef1SMark Yao 			continue;
1344186f8572SMark Yao 
1345186f8572SMark Yao 		memset(s, 0, sizeof(*s));
1346186f8572SMark Yao 
1347186f8572SMark Yao 		INIT_LIST_HEAD(&s->head);
134854fc9addSSandy Huang 		ret = ofnode_read_string_index(node, "logo,uboot", 0, &name);
134954fc9addSSandy Huang 		if (!ret)
135054fc9addSSandy Huang 			memcpy(s->ulogo_name, name, strlen(name));
135154fc9addSSandy Huang 		ret = ofnode_read_string_index(node, "logo,kernel", 0, &name);
135254fc9addSSandy Huang 		if (!ret)
135354fc9addSSandy Huang 			memcpy(s->klogo_name, name, strlen(name));
1354e2bce6e4SKever Yang 		ret = ofnode_read_string_index(node, "logo,mode", 0, &name);
1355186f8572SMark Yao 		if (!strcmp(name, "fullscreen"))
1356186f8572SMark Yao 			s->logo_mode = ROCKCHIP_DISPLAY_FULLSCREEN;
1357186f8572SMark Yao 		else
1358186f8572SMark Yao 			s->logo_mode = ROCKCHIP_DISPLAY_CENTER;
1359e2bce6e4SKever Yang 		ret = ofnode_read_string_index(node, "charge_logo,mode", 0, &name);
1360186f8572SMark Yao 		if (!strcmp(name, "fullscreen"))
1361186f8572SMark Yao 			s->charge_logo_mode = ROCKCHIP_DISPLAY_FULLSCREEN;
1362186f8572SMark Yao 		else
1363186f8572SMark Yao 			s->charge_logo_mode = ROCKCHIP_DISPLAY_CENTER;
1364186f8572SMark Yao 
1365186f8572SMark Yao 		s->blob = blob;
1366747dfc26SWyon Bi 		s->conn_state.node = conn_dev->node;
1367186f8572SMark Yao 		s->conn_state.dev = conn_dev;
1368186f8572SMark Yao 		s->conn_state.connector = conn;
13698a2a3a29SSandy Huang 		s->conn_state.overscan.left_margin = 100;
13708a2a3a29SSandy Huang 		s->conn_state.overscan.right_margin = 100;
13718a2a3a29SSandy Huang 		s->conn_state.overscan.top_margin = 100;
13728a2a3a29SSandy Huang 		s->conn_state.overscan.bottom_margin = 100;
1373e2bce6e4SKever Yang 		s->crtc_state.node = np_to_ofnode(vop_node);
1374186f8572SMark Yao 		s->crtc_state.dev = crtc_dev;
1375186f8572SMark Yao 		s->crtc_state.crtc = crtc;
1376e2bce6e4SKever Yang 		s->crtc_state.crtc_id = get_crtc_id(np_to_ofnode(ep_node));
1377e2bce6e4SKever Yang 		s->node = node;
137867b9012cSSandy Huang 		get_crtc_mcu_mode(&s->crtc_state);
1379186f8572SMark Yao 
1380e2bce6e4SKever Yang 		if (connector_panel_init(s)) {
1381e2bce6e4SKever Yang 			printf("Warn: Failed to init panel drivers\n");
13824b8c2ef1SMark Yao 			free(s);
13834b8c2ef1SMark Yao 			continue;
13844b8c2ef1SMark Yao 		}
13854b8c2ef1SMark Yao 
13862a48727aSAlgea Cao 		if (connector_phy_init(s, data)) {
1387e2bce6e4SKever Yang 			printf("Warn: Failed to init phy drivers\n");
13884b8c2ef1SMark Yao 			free(s);
13894b8c2ef1SMark Yao 			continue;
13904b8c2ef1SMark Yao 		}
1391186f8572SMark Yao 		list_add_tail(&s->head, &rockchip_display_list);
1392186f8572SMark Yao 	}
1393186f8572SMark Yao 
13944b8c2ef1SMark Yao 	if (list_empty(&rockchip_display_list)) {
13954b8c2ef1SMark Yao 		printf("Failed to found available display route\n");
13964b8c2ef1SMark Yao 		return -ENODEV;
13974b8c2ef1SMark Yao 	}
13984b8c2ef1SMark Yao 
1399186f8572SMark Yao 	uc_priv->xsize = DRM_ROCKCHIP_FB_WIDTH;
1400186f8572SMark Yao 	uc_priv->ysize = DRM_ROCKCHIP_FB_HEIGHT;
1401186f8572SMark Yao 	uc_priv->bpix = VIDEO_BPP32;
1402186f8572SMark Yao 
14034b8c2ef1SMark Yao 	#ifdef CONFIG_DRM_ROCKCHIP_VIDEO_FRAMEBUFFER
1404186f8572SMark Yao 	rockchip_show_fbbase(plat->base);
1405186f8572SMark Yao 	video_set_flush_dcache(dev, true);
14064b8c2ef1SMark Yao 	#endif
1407186f8572SMark Yao 
1408186f8572SMark Yao 	return 0;
14094b8c2ef1SMark Yao }
1410186f8572SMark Yao 
1411186f8572SMark Yao void rockchip_display_fixup(void *blob)
1412186f8572SMark Yao {
1413186f8572SMark Yao 	const struct rockchip_connector_funcs *conn_funcs;
1414186f8572SMark Yao 	const struct rockchip_crtc_funcs *crtc_funcs;
1415186f8572SMark Yao 	const struct rockchip_connector *conn;
1416186f8572SMark Yao 	const struct rockchip_crtc *crtc;
1417186f8572SMark Yao 	struct display_state *s;
1418694afdc8SKever Yang 	int offset;
141951619d03SKever Yang 	const struct device_node *np;
142051619d03SKever Yang 	const char *path;
1421186f8572SMark Yao 
1422186f8572SMark Yao 	if (!get_display_size())
1423186f8572SMark Yao 		return;
1424186f8572SMark Yao 
14255eb61944SSandy Huang 	if (fdt_node_offset_by_compatible(blob, 0, "rockchip,drm-logo") >= 0) {
14265eb61944SSandy Huang 		list_for_each_entry(s, &rockchip_display_list, head)
14275eb61944SSandy Huang 			load_bmp_logo(&s->logo, s->klogo_name);
142851619d03SKever Yang 		offset = fdt_update_reserved_memory(blob, "rockchip,drm-logo",
1429186f8572SMark Yao 						    (u64)memory_start,
1430186f8572SMark Yao 						    (u64)get_display_size());
14315eb61944SSandy Huang 		if (offset < 0)
14325eb61944SSandy Huang 			printf("failed to reserve drm-loader-logo memory\n");
14335eb61944SSandy Huang 	} else {
14345eb61944SSandy Huang 		printf("can't found rockchip,drm-logo, use rockchip,fb-logo\n");
1435694afdc8SKever Yang 		/* Compatible with rkfb display, only need reserve memory */
1436694afdc8SKever Yang 		offset = fdt_update_reserved_memory(blob, "rockchip,fb-logo",
1437694afdc8SKever Yang 						    (u64)memory_start,
14385eb61944SSandy Huang 						    MEMORY_POOL_SIZE);
1439694afdc8SKever Yang 		if (offset < 0)
14405eb61944SSandy Huang 			printf("failed to reserve fb-loader-logo memory\n");
14415eb61944SSandy Huang 		else
14425eb61944SSandy Huang 			list_for_each_entry(s, &rockchip_display_list, head)
14435eb61944SSandy Huang 				load_kernel_bmp_logo(&s->logo, s->klogo_name);
1444186f8572SMark Yao 		return;
1445186f8572SMark Yao 	}
1446186f8572SMark Yao 
1447186f8572SMark Yao 	list_for_each_entry(s, &rockchip_display_list, head) {
1448186f8572SMark Yao 		conn = s->conn_state.connector;
1449186f8572SMark Yao 		if (!conn)
1450186f8572SMark Yao 			continue;
1451186f8572SMark Yao 		conn_funcs = conn->funcs;
1452186f8572SMark Yao 		if (!conn_funcs) {
1453186f8572SMark Yao 			printf("failed to get exist connector\n");
1454186f8572SMark Yao 			continue;
1455186f8572SMark Yao 		}
1456186f8572SMark Yao 
1457186f8572SMark Yao 		crtc = s->crtc_state.crtc;
1458186f8572SMark Yao 		if (!crtc)
1459186f8572SMark Yao 			continue;
1460186f8572SMark Yao 
1461186f8572SMark Yao 		crtc_funcs = crtc->funcs;
1462186f8572SMark Yao 		if (!crtc_funcs) {
1463186f8572SMark Yao 			printf("failed to get exist crtc\n");
1464186f8572SMark Yao 			continue;
1465186f8572SMark Yao 		}
1466186f8572SMark Yao 
1467186f8572SMark Yao 		if (crtc_funcs->fixup_dts)
1468186f8572SMark Yao 			crtc_funcs->fixup_dts(s, blob);
1469186f8572SMark Yao 
1470186f8572SMark Yao 		if (conn_funcs->fixup_dts)
1471186f8572SMark Yao 			conn_funcs->fixup_dts(s, blob);
1472186f8572SMark Yao 
147351619d03SKever Yang 		np = ofnode_to_np(s->node);
147451619d03SKever Yang 		path = np->full_name;
147551619d03SKever Yang 		fdt_increase_size(blob, 0x400);
1476186f8572SMark Yao #define FDT_SET_U32(name, val) \
1477186f8572SMark Yao 		do_fixup_by_path_u32(blob, path, name, val, 1);
1478186f8572SMark Yao 
147951619d03SKever Yang 		offset = s->logo.offset + (u32)(unsigned long)s->logo.mem
148051619d03SKever Yang 			 - memory_start;
1481186f8572SMark Yao 		FDT_SET_U32("logo,offset", offset);
1482186f8572SMark Yao 		FDT_SET_U32("logo,width", s->logo.width);
1483186f8572SMark Yao 		FDT_SET_U32("logo,height", s->logo.height);
1484186f8572SMark Yao 		FDT_SET_U32("logo,bpp", s->logo.bpp);
1485186f8572SMark Yao 		FDT_SET_U32("logo,ymirror", s->logo.ymirror);
1486186f8572SMark Yao 		FDT_SET_U32("video,hdisplay", s->conn_state.mode.hdisplay);
1487186f8572SMark Yao 		FDT_SET_U32("video,vdisplay", s->conn_state.mode.vdisplay);
1488f11b858fSSandy Huang 		FDT_SET_U32("video,crtc_hsync_end", s->conn_state.mode.crtc_hsync_end);
1489f11b858fSSandy Huang 		FDT_SET_U32("video,crtc_vsync_end", s->conn_state.mode.crtc_vsync_end);
1490186f8572SMark Yao 		FDT_SET_U32("video,vrefresh",
1491186f8572SMark Yao 			    drm_mode_vrefresh(&s->conn_state.mode));
14928a2a3a29SSandy Huang 		FDT_SET_U32("video,flags", s->conn_state.mode.flags);
14938a2a3a29SSandy Huang 		FDT_SET_U32("overscan,left_margin", s->conn_state.overscan.left_margin);
14948a2a3a29SSandy Huang 		FDT_SET_U32("overscan,right_margin", s->conn_state.overscan.right_margin);
14958a2a3a29SSandy Huang 		FDT_SET_U32("overscan,top_margin", s->conn_state.overscan.top_margin);
14968a2a3a29SSandy Huang 		FDT_SET_U32("overscan,bottom_margin", s->conn_state.overscan.bottom_margin);
1497186f8572SMark Yao #undef FDT_SET_U32
1498186f8572SMark Yao 	}
1499186f8572SMark Yao }
1500186f8572SMark Yao 
1501186f8572SMark Yao int rockchip_display_bind(struct udevice *dev)
1502186f8572SMark Yao {
1503186f8572SMark Yao 	struct video_uc_platdata *plat = dev_get_uclass_platdata(dev);
1504186f8572SMark Yao 
15054b8c2ef1SMark Yao 	plat->size = DRM_ROCKCHIP_FB_SIZE + MEMORY_POOL_SIZE;
1506186f8572SMark Yao 
1507186f8572SMark Yao 	return 0;
1508186f8572SMark Yao }
1509186f8572SMark Yao 
1510186f8572SMark Yao static const struct udevice_id rockchip_display_ids[] = {
1511186f8572SMark Yao 	{ .compatible = "rockchip,display-subsystem" },
1512186f8572SMark Yao 	{ }
1513186f8572SMark Yao };
1514186f8572SMark Yao 
1515186f8572SMark Yao U_BOOT_DRIVER(rockchip_display) = {
1516186f8572SMark Yao 	.name	= "rockchip_display",
1517186f8572SMark Yao 	.id	= UCLASS_VIDEO,
1518186f8572SMark Yao 	.of_match = rockchip_display_ids,
1519186f8572SMark Yao 	.bind	= rockchip_display_bind,
1520186f8572SMark Yao 	.probe	= rockchip_display_probe,
1521186f8572SMark Yao };
15224b8c2ef1SMark Yao 
15234b8c2ef1SMark Yao static int do_rockchip_logo_show(cmd_tbl_t *cmdtp, int flag, int argc,
15244b8c2ef1SMark Yao 			char *const argv[])
15254b8c2ef1SMark Yao {
15264b8c2ef1SMark Yao 	if (argc != 1)
15274b8c2ef1SMark Yao 		return CMD_RET_USAGE;
15284b8c2ef1SMark Yao 
15294b8c2ef1SMark Yao 	rockchip_show_logo();
15304b8c2ef1SMark Yao 
15314b8c2ef1SMark Yao 	return 0;
15324b8c2ef1SMark Yao }
15334b8c2ef1SMark Yao 
15344b8c2ef1SMark Yao static int do_rockchip_show_bmp(cmd_tbl_t *cmdtp, int flag, int argc,
15354b8c2ef1SMark Yao 				char *const argv[])
15364b8c2ef1SMark Yao {
15374b8c2ef1SMark Yao 	if (argc != 2)
15384b8c2ef1SMark Yao 		return CMD_RET_USAGE;
15394b8c2ef1SMark Yao 
15404b8c2ef1SMark Yao 	rockchip_show_bmp(argv[1]);
15414b8c2ef1SMark Yao 
15424b8c2ef1SMark Yao 	return 0;
15434b8c2ef1SMark Yao }
15444b8c2ef1SMark Yao 
15454b8c2ef1SMark Yao U_BOOT_CMD(
15464b8c2ef1SMark Yao 	rockchip_show_logo, 1, 1, do_rockchip_logo_show,
15474b8c2ef1SMark Yao 	"load and display log from resource partition",
15484b8c2ef1SMark Yao 	NULL
15494b8c2ef1SMark Yao );
15504b8c2ef1SMark Yao 
15514b8c2ef1SMark Yao U_BOOT_CMD(
15524b8c2ef1SMark Yao 	rockchip_show_bmp, 2, 1, do_rockchip_show_bmp,
15534b8c2ef1SMark Yao 	"load and display bmp from resource partition",
15544b8c2ef1SMark Yao 	"    <bmp_name>"
15554b8c2ef1SMark Yao );
1556