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