xref: /OK3568_Linux_fs/u-boot/drivers/video/tegra.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun /*
2*4882a593Smuzhiyun  * Copyright (c) 2011 The Chromium OS Authors.
3*4882a593Smuzhiyun  * SPDX-License-Identifier:	GPL-2.0+
4*4882a593Smuzhiyun  */
5*4882a593Smuzhiyun 
6*4882a593Smuzhiyun #include <common.h>
7*4882a593Smuzhiyun #include <dm.h>
8*4882a593Smuzhiyun #include <fdtdec.h>
9*4882a593Smuzhiyun #include <panel.h>
10*4882a593Smuzhiyun #include <pwm.h>
11*4882a593Smuzhiyun #include <video.h>
12*4882a593Smuzhiyun #include <asm/system.h>
13*4882a593Smuzhiyun #include <asm/gpio.h>
14*4882a593Smuzhiyun #include <asm/io.h>
15*4882a593Smuzhiyun 
16*4882a593Smuzhiyun #include <asm/arch/clock.h>
17*4882a593Smuzhiyun #include <asm/arch/funcmux.h>
18*4882a593Smuzhiyun #include <asm/arch/pinmux.h>
19*4882a593Smuzhiyun #include <asm/arch/pwm.h>
20*4882a593Smuzhiyun #include <asm/arch/display.h>
21*4882a593Smuzhiyun #include <asm/arch-tegra/timer.h>
22*4882a593Smuzhiyun 
23*4882a593Smuzhiyun DECLARE_GLOBAL_DATA_PTR;
24*4882a593Smuzhiyun 
25*4882a593Smuzhiyun /* Information about the display controller */
26*4882a593Smuzhiyun struct tegra_lcd_priv {
27*4882a593Smuzhiyun 	int width;			/* width in pixels */
28*4882a593Smuzhiyun 	int height;			/* height in pixels */
29*4882a593Smuzhiyun 	enum video_log2_bpp log2_bpp;	/* colour depth */
30*4882a593Smuzhiyun 	struct display_timing timing;
31*4882a593Smuzhiyun 	struct udevice *panel;
32*4882a593Smuzhiyun 	struct disp_ctlr *disp;		/* Display controller to use */
33*4882a593Smuzhiyun 	fdt_addr_t frame_buffer;	/* Address of frame buffer */
34*4882a593Smuzhiyun 	unsigned pixel_clock;		/* Pixel clock in Hz */
35*4882a593Smuzhiyun };
36*4882a593Smuzhiyun 
37*4882a593Smuzhiyun enum {
38*4882a593Smuzhiyun 	/* Maximum LCD size we support */
39*4882a593Smuzhiyun 	LCD_MAX_WIDTH		= 1366,
40*4882a593Smuzhiyun 	LCD_MAX_HEIGHT		= 768,
41*4882a593Smuzhiyun 	LCD_MAX_LOG2_BPP	= VIDEO_BPP16,
42*4882a593Smuzhiyun };
43*4882a593Smuzhiyun 
update_window(struct dc_ctlr * dc,struct disp_ctl_win * win)44*4882a593Smuzhiyun static void update_window(struct dc_ctlr *dc, struct disp_ctl_win *win)
45*4882a593Smuzhiyun {
46*4882a593Smuzhiyun 	unsigned h_dda, v_dda;
47*4882a593Smuzhiyun 	unsigned long val;
48*4882a593Smuzhiyun 
49*4882a593Smuzhiyun 	val = readl(&dc->cmd.disp_win_header);
50*4882a593Smuzhiyun 	val |= WINDOW_A_SELECT;
51*4882a593Smuzhiyun 	writel(val, &dc->cmd.disp_win_header);
52*4882a593Smuzhiyun 
53*4882a593Smuzhiyun 	writel(win->fmt, &dc->win.color_depth);
54*4882a593Smuzhiyun 
55*4882a593Smuzhiyun 	clrsetbits_le32(&dc->win.byte_swap, BYTE_SWAP_MASK,
56*4882a593Smuzhiyun 			BYTE_SWAP_NOSWAP << BYTE_SWAP_SHIFT);
57*4882a593Smuzhiyun 
58*4882a593Smuzhiyun 	val = win->out_x << H_POSITION_SHIFT;
59*4882a593Smuzhiyun 	val |= win->out_y << V_POSITION_SHIFT;
60*4882a593Smuzhiyun 	writel(val, &dc->win.pos);
61*4882a593Smuzhiyun 
62*4882a593Smuzhiyun 	val = win->out_w << H_SIZE_SHIFT;
63*4882a593Smuzhiyun 	val |= win->out_h << V_SIZE_SHIFT;
64*4882a593Smuzhiyun 	writel(val, &dc->win.size);
65*4882a593Smuzhiyun 
66*4882a593Smuzhiyun 	val = (win->w * win->bpp / 8) << H_PRESCALED_SIZE_SHIFT;
67*4882a593Smuzhiyun 	val |= win->h << V_PRESCALED_SIZE_SHIFT;
68*4882a593Smuzhiyun 	writel(val, &dc->win.prescaled_size);
69*4882a593Smuzhiyun 
70*4882a593Smuzhiyun 	writel(0, &dc->win.h_initial_dda);
71*4882a593Smuzhiyun 	writel(0, &dc->win.v_initial_dda);
72*4882a593Smuzhiyun 
73*4882a593Smuzhiyun 	h_dda = (win->w * 0x1000) / max(win->out_w - 1, 1U);
74*4882a593Smuzhiyun 	v_dda = (win->h * 0x1000) / max(win->out_h - 1, 1U);
75*4882a593Smuzhiyun 
76*4882a593Smuzhiyun 	val = h_dda << H_DDA_INC_SHIFT;
77*4882a593Smuzhiyun 	val |= v_dda << V_DDA_INC_SHIFT;
78*4882a593Smuzhiyun 	writel(val, &dc->win.dda_increment);
79*4882a593Smuzhiyun 
80*4882a593Smuzhiyun 	writel(win->stride, &dc->win.line_stride);
81*4882a593Smuzhiyun 	writel(0, &dc->win.buf_stride);
82*4882a593Smuzhiyun 
83*4882a593Smuzhiyun 	val = WIN_ENABLE;
84*4882a593Smuzhiyun 	if (win->bpp < 24)
85*4882a593Smuzhiyun 		val |= COLOR_EXPAND;
86*4882a593Smuzhiyun 	writel(val, &dc->win.win_opt);
87*4882a593Smuzhiyun 
88*4882a593Smuzhiyun 	writel((unsigned long)win->phys_addr, &dc->winbuf.start_addr);
89*4882a593Smuzhiyun 	writel(win->x, &dc->winbuf.addr_h_offset);
90*4882a593Smuzhiyun 	writel(win->y, &dc->winbuf.addr_v_offset);
91*4882a593Smuzhiyun 
92*4882a593Smuzhiyun 	writel(0xff00, &dc->win.blend_nokey);
93*4882a593Smuzhiyun 	writel(0xff00, &dc->win.blend_1win);
94*4882a593Smuzhiyun 
95*4882a593Smuzhiyun 	val = GENERAL_ACT_REQ | WIN_A_ACT_REQ;
96*4882a593Smuzhiyun 	val |= GENERAL_UPDATE | WIN_A_UPDATE;
97*4882a593Smuzhiyun 	writel(val, &dc->cmd.state_ctrl);
98*4882a593Smuzhiyun }
99*4882a593Smuzhiyun 
update_display_mode(struct dc_disp_reg * disp,struct tegra_lcd_priv * priv)100*4882a593Smuzhiyun static int update_display_mode(struct dc_disp_reg *disp,
101*4882a593Smuzhiyun 			       struct tegra_lcd_priv *priv)
102*4882a593Smuzhiyun {
103*4882a593Smuzhiyun 	struct display_timing *dt = &priv->timing;
104*4882a593Smuzhiyun 	unsigned long val;
105*4882a593Smuzhiyun 	unsigned long rate;
106*4882a593Smuzhiyun 	unsigned long div;
107*4882a593Smuzhiyun 
108*4882a593Smuzhiyun 	writel(0x0, &disp->disp_timing_opt);
109*4882a593Smuzhiyun 
110*4882a593Smuzhiyun 	writel(1 | 1 << 16, &disp->ref_to_sync);
111*4882a593Smuzhiyun 	writel(dt->hsync_len.typ | dt->vsync_len.typ << 16, &disp->sync_width);
112*4882a593Smuzhiyun 	writel(dt->hback_porch.typ | dt->vback_porch.typ << 16,
113*4882a593Smuzhiyun 	       &disp->back_porch);
114*4882a593Smuzhiyun 	writel((dt->hfront_porch.typ - 1) | (dt->vfront_porch.typ - 1) << 16,
115*4882a593Smuzhiyun 	       &disp->front_porch);
116*4882a593Smuzhiyun 	writel(dt->hactive.typ | (dt->vactive.typ << 16), &disp->disp_active);
117*4882a593Smuzhiyun 
118*4882a593Smuzhiyun 	val = DE_SELECT_ACTIVE << DE_SELECT_SHIFT;
119*4882a593Smuzhiyun 	val |= DE_CONTROL_NORMAL << DE_CONTROL_SHIFT;
120*4882a593Smuzhiyun 	writel(val, &disp->data_enable_opt);
121*4882a593Smuzhiyun 
122*4882a593Smuzhiyun 	val = DATA_FORMAT_DF1P1C << DATA_FORMAT_SHIFT;
123*4882a593Smuzhiyun 	val |= DATA_ALIGNMENT_MSB << DATA_ALIGNMENT_SHIFT;
124*4882a593Smuzhiyun 	val |= DATA_ORDER_RED_BLUE << DATA_ORDER_SHIFT;
125*4882a593Smuzhiyun 	writel(val, &disp->disp_interface_ctrl);
126*4882a593Smuzhiyun 
127*4882a593Smuzhiyun 	/*
128*4882a593Smuzhiyun 	 * The pixel clock divider is in 7.1 format (where the bottom bit
129*4882a593Smuzhiyun 	 * represents 0.5). Here we calculate the divider needed to get from
130*4882a593Smuzhiyun 	 * the display clock (typically 600MHz) to the pixel clock. We round
131*4882a593Smuzhiyun 	 * up or down as requried.
132*4882a593Smuzhiyun 	 */
133*4882a593Smuzhiyun 	rate = clock_get_periph_rate(PERIPH_ID_DISP1, CLOCK_ID_CGENERAL);
134*4882a593Smuzhiyun 	div = ((rate * 2 + priv->pixel_clock / 2) / priv->pixel_clock) - 2;
135*4882a593Smuzhiyun 	debug("Display clock %lu, divider %lu\n", rate, div);
136*4882a593Smuzhiyun 
137*4882a593Smuzhiyun 	writel(0x00010001, &disp->shift_clk_opt);
138*4882a593Smuzhiyun 
139*4882a593Smuzhiyun 	val = PIXEL_CLK_DIVIDER_PCD1 << PIXEL_CLK_DIVIDER_SHIFT;
140*4882a593Smuzhiyun 	val |= div << SHIFT_CLK_DIVIDER_SHIFT;
141*4882a593Smuzhiyun 	writel(val, &disp->disp_clk_ctrl);
142*4882a593Smuzhiyun 
143*4882a593Smuzhiyun 	return 0;
144*4882a593Smuzhiyun }
145*4882a593Smuzhiyun 
146*4882a593Smuzhiyun /* Start up the display and turn on power to PWMs */
basic_init(struct dc_cmd_reg * cmd)147*4882a593Smuzhiyun static void basic_init(struct dc_cmd_reg *cmd)
148*4882a593Smuzhiyun {
149*4882a593Smuzhiyun 	u32 val;
150*4882a593Smuzhiyun 
151*4882a593Smuzhiyun 	writel(0x00000100, &cmd->gen_incr_syncpt_ctrl);
152*4882a593Smuzhiyun 	writel(0x0000011a, &cmd->cont_syncpt_vsync);
153*4882a593Smuzhiyun 	writel(0x00000000, &cmd->int_type);
154*4882a593Smuzhiyun 	writel(0x00000000, &cmd->int_polarity);
155*4882a593Smuzhiyun 	writel(0x00000000, &cmd->int_mask);
156*4882a593Smuzhiyun 	writel(0x00000000, &cmd->int_enb);
157*4882a593Smuzhiyun 
158*4882a593Smuzhiyun 	val = PW0_ENABLE | PW1_ENABLE | PW2_ENABLE;
159*4882a593Smuzhiyun 	val |= PW3_ENABLE | PW4_ENABLE | PM0_ENABLE;
160*4882a593Smuzhiyun 	val |= PM1_ENABLE;
161*4882a593Smuzhiyun 	writel(val, &cmd->disp_pow_ctrl);
162*4882a593Smuzhiyun 
163*4882a593Smuzhiyun 	val = readl(&cmd->disp_cmd);
164*4882a593Smuzhiyun 	val |= CTRL_MODE_C_DISPLAY << CTRL_MODE_SHIFT;
165*4882a593Smuzhiyun 	writel(val, &cmd->disp_cmd);
166*4882a593Smuzhiyun }
167*4882a593Smuzhiyun 
basic_init_timer(struct dc_disp_reg * disp)168*4882a593Smuzhiyun static void basic_init_timer(struct dc_disp_reg *disp)
169*4882a593Smuzhiyun {
170*4882a593Smuzhiyun 	writel(0x00000020, &disp->mem_high_pri);
171*4882a593Smuzhiyun 	writel(0x00000001, &disp->mem_high_pri_timer);
172*4882a593Smuzhiyun }
173*4882a593Smuzhiyun 
174*4882a593Smuzhiyun static const u32 rgb_enb_tab[PIN_REG_COUNT] = {
175*4882a593Smuzhiyun 	0x00000000,
176*4882a593Smuzhiyun 	0x00000000,
177*4882a593Smuzhiyun 	0x00000000,
178*4882a593Smuzhiyun 	0x00000000,
179*4882a593Smuzhiyun };
180*4882a593Smuzhiyun 
181*4882a593Smuzhiyun static const u32 rgb_polarity_tab[PIN_REG_COUNT] = {
182*4882a593Smuzhiyun 	0x00000000,
183*4882a593Smuzhiyun 	0x01000000,
184*4882a593Smuzhiyun 	0x00000000,
185*4882a593Smuzhiyun 	0x00000000,
186*4882a593Smuzhiyun };
187*4882a593Smuzhiyun 
188*4882a593Smuzhiyun static const u32 rgb_data_tab[PIN_REG_COUNT] = {
189*4882a593Smuzhiyun 	0x00000000,
190*4882a593Smuzhiyun 	0x00000000,
191*4882a593Smuzhiyun 	0x00000000,
192*4882a593Smuzhiyun 	0x00000000,
193*4882a593Smuzhiyun };
194*4882a593Smuzhiyun 
195*4882a593Smuzhiyun static const u32 rgb_sel_tab[PIN_OUTPUT_SEL_COUNT] = {
196*4882a593Smuzhiyun 	0x00000000,
197*4882a593Smuzhiyun 	0x00000000,
198*4882a593Smuzhiyun 	0x00000000,
199*4882a593Smuzhiyun 	0x00000000,
200*4882a593Smuzhiyun 	0x00210222,
201*4882a593Smuzhiyun 	0x00002200,
202*4882a593Smuzhiyun 	0x00020000,
203*4882a593Smuzhiyun };
204*4882a593Smuzhiyun 
rgb_enable(struct dc_com_reg * com)205*4882a593Smuzhiyun static void rgb_enable(struct dc_com_reg *com)
206*4882a593Smuzhiyun {
207*4882a593Smuzhiyun 	int i;
208*4882a593Smuzhiyun 
209*4882a593Smuzhiyun 	for (i = 0; i < PIN_REG_COUNT; i++) {
210*4882a593Smuzhiyun 		writel(rgb_enb_tab[i], &com->pin_output_enb[i]);
211*4882a593Smuzhiyun 		writel(rgb_polarity_tab[i], &com->pin_output_polarity[i]);
212*4882a593Smuzhiyun 		writel(rgb_data_tab[i], &com->pin_output_data[i]);
213*4882a593Smuzhiyun 	}
214*4882a593Smuzhiyun 
215*4882a593Smuzhiyun 	for (i = 0; i < PIN_OUTPUT_SEL_COUNT; i++)
216*4882a593Smuzhiyun 		writel(rgb_sel_tab[i], &com->pin_output_sel[i]);
217*4882a593Smuzhiyun }
218*4882a593Smuzhiyun 
setup_window(struct disp_ctl_win * win,struct tegra_lcd_priv * priv)219*4882a593Smuzhiyun static int setup_window(struct disp_ctl_win *win,
220*4882a593Smuzhiyun 			struct tegra_lcd_priv *priv)
221*4882a593Smuzhiyun {
222*4882a593Smuzhiyun 	win->x = 0;
223*4882a593Smuzhiyun 	win->y = 0;
224*4882a593Smuzhiyun 	win->w = priv->width;
225*4882a593Smuzhiyun 	win->h = priv->height;
226*4882a593Smuzhiyun 	win->out_x = 0;
227*4882a593Smuzhiyun 	win->out_y = 0;
228*4882a593Smuzhiyun 	win->out_w = priv->width;
229*4882a593Smuzhiyun 	win->out_h = priv->height;
230*4882a593Smuzhiyun 	win->phys_addr = priv->frame_buffer;
231*4882a593Smuzhiyun 	win->stride = priv->width * (1 << priv->log2_bpp) / 8;
232*4882a593Smuzhiyun 	debug("%s: depth = %d\n", __func__, priv->log2_bpp);
233*4882a593Smuzhiyun 	switch (priv->log2_bpp) {
234*4882a593Smuzhiyun 	case VIDEO_BPP32:
235*4882a593Smuzhiyun 		win->fmt = COLOR_DEPTH_R8G8B8A8;
236*4882a593Smuzhiyun 		win->bpp = 32;
237*4882a593Smuzhiyun 		break;
238*4882a593Smuzhiyun 	case VIDEO_BPP16:
239*4882a593Smuzhiyun 		win->fmt = COLOR_DEPTH_B5G6R5;
240*4882a593Smuzhiyun 		win->bpp = 16;
241*4882a593Smuzhiyun 		break;
242*4882a593Smuzhiyun 
243*4882a593Smuzhiyun 	default:
244*4882a593Smuzhiyun 		debug("Unsupported LCD bit depth");
245*4882a593Smuzhiyun 		return -1;
246*4882a593Smuzhiyun 	}
247*4882a593Smuzhiyun 
248*4882a593Smuzhiyun 	return 0;
249*4882a593Smuzhiyun }
250*4882a593Smuzhiyun 
251*4882a593Smuzhiyun /**
252*4882a593Smuzhiyun  * Register a new display based on device tree configuration.
253*4882a593Smuzhiyun  *
254*4882a593Smuzhiyun  * The frame buffer can be positioned by U-Boot or overridden by the fdt.
255*4882a593Smuzhiyun  * You should pass in the U-Boot address here, and check the contents of
256*4882a593Smuzhiyun  * struct tegra_lcd_priv to see what was actually chosen.
257*4882a593Smuzhiyun  *
258*4882a593Smuzhiyun  * @param blob			Device tree blob
259*4882a593Smuzhiyun  * @param priv			Driver's private data
260*4882a593Smuzhiyun  * @param default_lcd_base	Default address of LCD frame buffer
261*4882a593Smuzhiyun  * @return 0 if ok, -1 on error (unsupported bits per pixel)
262*4882a593Smuzhiyun  */
tegra_display_probe(const void * blob,struct tegra_lcd_priv * priv,void * default_lcd_base)263*4882a593Smuzhiyun static int tegra_display_probe(const void *blob, struct tegra_lcd_priv *priv,
264*4882a593Smuzhiyun 			       void *default_lcd_base)
265*4882a593Smuzhiyun {
266*4882a593Smuzhiyun 	struct disp_ctl_win window;
267*4882a593Smuzhiyun 	struct dc_ctlr *dc;
268*4882a593Smuzhiyun 
269*4882a593Smuzhiyun 	priv->frame_buffer = (u32)default_lcd_base;
270*4882a593Smuzhiyun 
271*4882a593Smuzhiyun 	dc = (struct dc_ctlr *)priv->disp;
272*4882a593Smuzhiyun 
273*4882a593Smuzhiyun 	/*
274*4882a593Smuzhiyun 	 * A header file for clock constants was NAKed upstream.
275*4882a593Smuzhiyun 	 * TODO: Put this into the FDT and fdt_lcd struct when we have clock
276*4882a593Smuzhiyun 	 * support there
277*4882a593Smuzhiyun 	 */
278*4882a593Smuzhiyun 	clock_start_periph_pll(PERIPH_ID_HOST1X, CLOCK_ID_PERIPH,
279*4882a593Smuzhiyun 			       144 * 1000000);
280*4882a593Smuzhiyun 	clock_start_periph_pll(PERIPH_ID_DISP1, CLOCK_ID_CGENERAL,
281*4882a593Smuzhiyun 			       600 * 1000000);
282*4882a593Smuzhiyun 	basic_init(&dc->cmd);
283*4882a593Smuzhiyun 	basic_init_timer(&dc->disp);
284*4882a593Smuzhiyun 	rgb_enable(&dc->com);
285*4882a593Smuzhiyun 
286*4882a593Smuzhiyun 	if (priv->pixel_clock)
287*4882a593Smuzhiyun 		update_display_mode(&dc->disp, priv);
288*4882a593Smuzhiyun 
289*4882a593Smuzhiyun 	if (setup_window(&window, priv))
290*4882a593Smuzhiyun 		return -1;
291*4882a593Smuzhiyun 
292*4882a593Smuzhiyun 	update_window(dc, &window);
293*4882a593Smuzhiyun 
294*4882a593Smuzhiyun 	return 0;
295*4882a593Smuzhiyun }
296*4882a593Smuzhiyun 
tegra_lcd_probe(struct udevice * dev)297*4882a593Smuzhiyun static int tegra_lcd_probe(struct udevice *dev)
298*4882a593Smuzhiyun {
299*4882a593Smuzhiyun 	struct video_uc_platdata *plat = dev_get_uclass_platdata(dev);
300*4882a593Smuzhiyun 	struct video_priv *uc_priv = dev_get_uclass_priv(dev);
301*4882a593Smuzhiyun 	struct tegra_lcd_priv *priv = dev_get_priv(dev);
302*4882a593Smuzhiyun 	const void *blob = gd->fdt_blob;
303*4882a593Smuzhiyun 	int ret;
304*4882a593Smuzhiyun 
305*4882a593Smuzhiyun 	/* Initialize the Tegra display controller */
306*4882a593Smuzhiyun 	funcmux_select(PERIPH_ID_DISP1, FUNCMUX_DEFAULT);
307*4882a593Smuzhiyun 	if (tegra_display_probe(blob, priv, (void *)plat->base)) {
308*4882a593Smuzhiyun 		printf("%s: Failed to probe display driver\n", __func__);
309*4882a593Smuzhiyun 		return -1;
310*4882a593Smuzhiyun 	}
311*4882a593Smuzhiyun 
312*4882a593Smuzhiyun 	pinmux_set_func(PMUX_PINGRP_GPU, PMUX_FUNC_PWM);
313*4882a593Smuzhiyun 	pinmux_tristate_disable(PMUX_PINGRP_GPU);
314*4882a593Smuzhiyun 
315*4882a593Smuzhiyun 	ret = panel_enable_backlight(priv->panel);
316*4882a593Smuzhiyun 	if (ret) {
317*4882a593Smuzhiyun 		debug("%s: Cannot enable backlight, ret=%d\n", __func__, ret);
318*4882a593Smuzhiyun 		return ret;
319*4882a593Smuzhiyun 	}
320*4882a593Smuzhiyun 
321*4882a593Smuzhiyun 	mmu_set_region_dcache_behaviour(priv->frame_buffer, plat->size,
322*4882a593Smuzhiyun 					DCACHE_WRITETHROUGH);
323*4882a593Smuzhiyun 
324*4882a593Smuzhiyun 	/* Enable flushing after LCD writes if requested */
325*4882a593Smuzhiyun 	video_set_flush_dcache(dev, true);
326*4882a593Smuzhiyun 
327*4882a593Smuzhiyun 	uc_priv->xsize = priv->width;
328*4882a593Smuzhiyun 	uc_priv->ysize = priv->height;
329*4882a593Smuzhiyun 	uc_priv->bpix = priv->log2_bpp;
330*4882a593Smuzhiyun 	debug("LCD frame buffer at %pa, size %x\n", &priv->frame_buffer,
331*4882a593Smuzhiyun 	      plat->size);
332*4882a593Smuzhiyun 
333*4882a593Smuzhiyun 	return 0;
334*4882a593Smuzhiyun }
335*4882a593Smuzhiyun 
tegra_lcd_ofdata_to_platdata(struct udevice * dev)336*4882a593Smuzhiyun static int tegra_lcd_ofdata_to_platdata(struct udevice *dev)
337*4882a593Smuzhiyun {
338*4882a593Smuzhiyun 	struct tegra_lcd_priv *priv = dev_get_priv(dev);
339*4882a593Smuzhiyun 	const void *blob = gd->fdt_blob;
340*4882a593Smuzhiyun 	struct display_timing *timing;
341*4882a593Smuzhiyun 	int node = dev_of_offset(dev);
342*4882a593Smuzhiyun 	int panel_node;
343*4882a593Smuzhiyun 	int rgb;
344*4882a593Smuzhiyun 	int ret;
345*4882a593Smuzhiyun 
346*4882a593Smuzhiyun 	priv->disp = (struct disp_ctlr *)devfdt_get_addr(dev);
347*4882a593Smuzhiyun 	if (!priv->disp) {
348*4882a593Smuzhiyun 		debug("%s: No display controller address\n", __func__);
349*4882a593Smuzhiyun 		return -EINVAL;
350*4882a593Smuzhiyun 	}
351*4882a593Smuzhiyun 
352*4882a593Smuzhiyun 	rgb = fdt_subnode_offset(blob, node, "rgb");
353*4882a593Smuzhiyun 	if (rgb < 0) {
354*4882a593Smuzhiyun 		debug("%s: Cannot find rgb subnode for '%s' (ret=%d)\n",
355*4882a593Smuzhiyun 		      __func__, dev->name, rgb);
356*4882a593Smuzhiyun 		return -EINVAL;
357*4882a593Smuzhiyun 	}
358*4882a593Smuzhiyun 
359*4882a593Smuzhiyun 	ret = fdtdec_decode_display_timing(blob, rgb, 0, &priv->timing);
360*4882a593Smuzhiyun 	if (ret) {
361*4882a593Smuzhiyun 		debug("%s: Cannot read display timing for '%s' (ret=%d)\n",
362*4882a593Smuzhiyun 		      __func__, dev->name, ret);
363*4882a593Smuzhiyun 		return -EINVAL;
364*4882a593Smuzhiyun 	}
365*4882a593Smuzhiyun 	timing = &priv->timing;
366*4882a593Smuzhiyun 	priv->width = timing->hactive.typ;
367*4882a593Smuzhiyun 	priv->height = timing->vactive.typ;
368*4882a593Smuzhiyun 	priv->pixel_clock = timing->pixelclock.typ;
369*4882a593Smuzhiyun 	priv->log2_bpp = VIDEO_BPP16;
370*4882a593Smuzhiyun 
371*4882a593Smuzhiyun 	/*
372*4882a593Smuzhiyun 	 * Sadly the panel phandle is in an rgb subnode so we cannot use
373*4882a593Smuzhiyun 	 * uclass_get_device_by_phandle().
374*4882a593Smuzhiyun 	 */
375*4882a593Smuzhiyun 	panel_node = fdtdec_lookup_phandle(blob, rgb, "nvidia,panel");
376*4882a593Smuzhiyun 	if (panel_node < 0) {
377*4882a593Smuzhiyun 		debug("%s: Cannot find panel information\n", __func__);
378*4882a593Smuzhiyun 		return -EINVAL;
379*4882a593Smuzhiyun 	}
380*4882a593Smuzhiyun 	ret = uclass_get_device_by_of_offset(UCLASS_PANEL, panel_node,
381*4882a593Smuzhiyun 					     &priv->panel);
382*4882a593Smuzhiyun 	if (ret) {
383*4882a593Smuzhiyun 		debug("%s: Cannot find panel for '%s' (ret=%d)\n", __func__,
384*4882a593Smuzhiyun 		      dev->name, ret);
385*4882a593Smuzhiyun 		return ret;
386*4882a593Smuzhiyun 	}
387*4882a593Smuzhiyun 
388*4882a593Smuzhiyun 	return 0;
389*4882a593Smuzhiyun }
390*4882a593Smuzhiyun 
tegra_lcd_bind(struct udevice * dev)391*4882a593Smuzhiyun static int tegra_lcd_bind(struct udevice *dev)
392*4882a593Smuzhiyun {
393*4882a593Smuzhiyun 	struct video_uc_platdata *plat = dev_get_uclass_platdata(dev);
394*4882a593Smuzhiyun 	const void *blob = gd->fdt_blob;
395*4882a593Smuzhiyun 	int node = dev_of_offset(dev);
396*4882a593Smuzhiyun 	int rgb;
397*4882a593Smuzhiyun 
398*4882a593Smuzhiyun 	rgb = fdt_subnode_offset(blob, node, "rgb");
399*4882a593Smuzhiyun 	if ((rgb < 0) || !fdtdec_get_is_enabled(blob, rgb))
400*4882a593Smuzhiyun 		return -ENODEV;
401*4882a593Smuzhiyun 
402*4882a593Smuzhiyun 	plat->size = LCD_MAX_WIDTH * LCD_MAX_HEIGHT *
403*4882a593Smuzhiyun 		(1 << LCD_MAX_LOG2_BPP) / 8;
404*4882a593Smuzhiyun 
405*4882a593Smuzhiyun 	return 0;
406*4882a593Smuzhiyun }
407*4882a593Smuzhiyun 
408*4882a593Smuzhiyun static const struct video_ops tegra_lcd_ops = {
409*4882a593Smuzhiyun };
410*4882a593Smuzhiyun 
411*4882a593Smuzhiyun static const struct udevice_id tegra_lcd_ids[] = {
412*4882a593Smuzhiyun 	{ .compatible = "nvidia,tegra20-dc" },
413*4882a593Smuzhiyun 	{ }
414*4882a593Smuzhiyun };
415*4882a593Smuzhiyun 
416*4882a593Smuzhiyun U_BOOT_DRIVER(tegra_lcd) = {
417*4882a593Smuzhiyun 	.name	= "tegra_lcd",
418*4882a593Smuzhiyun 	.id	= UCLASS_VIDEO,
419*4882a593Smuzhiyun 	.of_match = tegra_lcd_ids,
420*4882a593Smuzhiyun 	.ops	= &tegra_lcd_ops,
421*4882a593Smuzhiyun 	.bind	= tegra_lcd_bind,
422*4882a593Smuzhiyun 	.probe	= tegra_lcd_probe,
423*4882a593Smuzhiyun 	.ofdata_to_platdata	= tegra_lcd_ofdata_to_platdata,
424*4882a593Smuzhiyun 	.priv_auto_alloc_size	= sizeof(struct tegra_lcd_priv),
425*4882a593Smuzhiyun };
426