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