]> git.sur5r.net Git - u-boot/blob - drivers/video/tegra.c
tegra: video: Convert tegra20 LCD driver to driver model
[u-boot] / drivers / video / tegra.c
1 /*
2  * Copyright (c) 2011 The Chromium OS Authors.
3  * SPDX-License-Identifier:     GPL-2.0+
4  */
5 #define DEBUG
6 #include <common.h>
7 #include <dm.h>
8 #include <fdtdec.h>
9 #include <video.h>
10 #include <asm/system.h>
11 #include <asm/gpio.h>
12 #include <asm/io.h>
13
14 #include <asm/arch/clock.h>
15 #include <asm/arch/funcmux.h>
16 #include <asm/arch/pinmux.h>
17 #include <asm/arch/pwm.h>
18 #include <asm/arch/display.h>
19 #include <asm/arch-tegra/timer.h>
20
21 DECLARE_GLOBAL_DATA_PTR;
22
23 /* These are the stages we go throuh in enabling the LCD */
24 enum stage_t {
25         STAGE_START,
26         STAGE_PANEL_VDD,
27         STAGE_LVDS,
28         STAGE_BACKLIGHT_VDD,
29         STAGE_PWM,
30         STAGE_BACKLIGHT_EN,
31         STAGE_DONE,
32 };
33
34 static enum stage_t stage;      /* Current stage we are at */
35 static unsigned long timer_next; /* Time we can move onto next stage */
36
37 /* Information about the display controller */
38 struct tegra_lcd_priv {
39         int width;                      /* width in pixels */
40         int height;                     /* height in pixels */
41         int bpp;                        /* number of bits per pixel */
42
43         /*
44          * log2 of number of bpp, in general, unless it bpp is 24 in which
45          * case this field holds 24 also! This is a U-Boot thing.
46          */
47         int log2_bpp;
48         struct disp_ctlr *disp;         /* Display controller to use */
49         fdt_addr_t frame_buffer;        /* Address of frame buffer */
50         unsigned pixel_clock;           /* Pixel clock in Hz */
51         uint horiz_timing[FDT_LCD_TIMING_COUNT];        /* Horizontal timing */
52         uint vert_timing[FDT_LCD_TIMING_COUNT];         /* Vertical timing */
53         int panel_node;                 /* node offset of panel information */
54         int pwm_channel;                /* PWM channel to use for backlight */
55         enum lcd_cache_t cache_type;
56
57         struct gpio_desc backlight_en;  /* GPIO for backlight enable */
58         struct gpio_desc lvds_shutdown; /* GPIO for lvds shutdown */
59         struct gpio_desc backlight_vdd; /* GPIO for backlight vdd */
60         struct gpio_desc panel_vdd;     /* GPIO for panel vdd */
61         /*
62          * Panel required timings
63          * Timing 1: delay between panel_vdd-rise and data-rise
64          * Timing 2: delay between data-rise and backlight_vdd-rise
65          * Timing 3: delay between backlight_vdd and pwm-rise
66          * Timing 4: delay between pwm-rise and backlight_en-rise
67          */
68         uint panel_timings[FDT_LCD_TIMINGS];
69 };
70
71 enum {
72         /* Maximum LCD size we support */
73         LCD_MAX_WIDTH           = 1366,
74         LCD_MAX_HEIGHT          = 768,
75         LCD_MAX_LOG2_BPP        = VIDEO_BPP16,
76 };
77
78 static void update_window(struct dc_ctlr *dc, struct disp_ctl_win *win)
79 {
80         unsigned h_dda, v_dda;
81         unsigned long val;
82
83         val = readl(&dc->cmd.disp_win_header);
84         val |= WINDOW_A_SELECT;
85         writel(val, &dc->cmd.disp_win_header);
86
87         writel(win->fmt, &dc->win.color_depth);
88
89         clrsetbits_le32(&dc->win.byte_swap, BYTE_SWAP_MASK,
90                         BYTE_SWAP_NOSWAP << BYTE_SWAP_SHIFT);
91
92         val = win->out_x << H_POSITION_SHIFT;
93         val |= win->out_y << V_POSITION_SHIFT;
94         writel(val, &dc->win.pos);
95
96         val = win->out_w << H_SIZE_SHIFT;
97         val |= win->out_h << V_SIZE_SHIFT;
98         writel(val, &dc->win.size);
99
100         val = (win->w * win->bpp / 8) << H_PRESCALED_SIZE_SHIFT;
101         val |= win->h << V_PRESCALED_SIZE_SHIFT;
102         writel(val, &dc->win.prescaled_size);
103
104         writel(0, &dc->win.h_initial_dda);
105         writel(0, &dc->win.v_initial_dda);
106
107         h_dda = (win->w * 0x1000) / max(win->out_w - 1, 1U);
108         v_dda = (win->h * 0x1000) / max(win->out_h - 1, 1U);
109
110         val = h_dda << H_DDA_INC_SHIFT;
111         val |= v_dda << V_DDA_INC_SHIFT;
112         writel(val, &dc->win.dda_increment);
113
114         writel(win->stride, &dc->win.line_stride);
115         writel(0, &dc->win.buf_stride);
116
117         val = WIN_ENABLE;
118         if (win->bpp < 24)
119                 val |= COLOR_EXPAND;
120         writel(val, &dc->win.win_opt);
121
122         writel((unsigned long)win->phys_addr, &dc->winbuf.start_addr);
123         writel(win->x, &dc->winbuf.addr_h_offset);
124         writel(win->y, &dc->winbuf.addr_v_offset);
125
126         writel(0xff00, &dc->win.blend_nokey);
127         writel(0xff00, &dc->win.blend_1win);
128
129         val = GENERAL_ACT_REQ | WIN_A_ACT_REQ;
130         val |= GENERAL_UPDATE | WIN_A_UPDATE;
131         writel(val, &dc->cmd.state_ctrl);
132 }
133
134 static void write_pair(struct tegra_lcd_priv *priv, int item, u32 *reg)
135 {
136         writel(priv->horiz_timing[item] |
137                         (priv->vert_timing[item] << 16), reg);
138 }
139
140 static int update_display_mode(struct dc_disp_reg *disp,
141                                struct tegra_lcd_priv *priv)
142 {
143         unsigned long val;
144         unsigned long rate;
145         unsigned long div;
146
147         writel(0x0, &disp->disp_timing_opt);
148         write_pair(priv, FDT_LCD_TIMING_REF_TO_SYNC, &disp->ref_to_sync);
149         write_pair(priv, FDT_LCD_TIMING_SYNC_WIDTH, &disp->sync_width);
150         write_pair(priv, FDT_LCD_TIMING_BACK_PORCH, &disp->back_porch);
151         write_pair(priv, FDT_LCD_TIMING_FRONT_PORCH, &disp->front_porch);
152
153         writel(priv->width | (priv->height << 16), &disp->disp_active);
154
155         val = DE_SELECT_ACTIVE << DE_SELECT_SHIFT;
156         val |= DE_CONTROL_NORMAL << DE_CONTROL_SHIFT;
157         writel(val, &disp->data_enable_opt);
158
159         val = DATA_FORMAT_DF1P1C << DATA_FORMAT_SHIFT;
160         val |= DATA_ALIGNMENT_MSB << DATA_ALIGNMENT_SHIFT;
161         val |= DATA_ORDER_RED_BLUE << DATA_ORDER_SHIFT;
162         writel(val, &disp->disp_interface_ctrl);
163
164         /*
165          * The pixel clock divider is in 7.1 format (where the bottom bit
166          * represents 0.5). Here we calculate the divider needed to get from
167          * the display clock (typically 600MHz) to the pixel clock. We round
168          * up or down as requried.
169          */
170         rate = clock_get_periph_rate(PERIPH_ID_DISP1, CLOCK_ID_CGENERAL);
171         div = ((rate * 2 + priv->pixel_clock / 2) / priv->pixel_clock) - 2;
172         debug("Display clock %lu, divider %lu\n", rate, div);
173
174         writel(0x00010001, &disp->shift_clk_opt);
175
176         val = PIXEL_CLK_DIVIDER_PCD1 << PIXEL_CLK_DIVIDER_SHIFT;
177         val |= div << SHIFT_CLK_DIVIDER_SHIFT;
178         writel(val, &disp->disp_clk_ctrl);
179
180         return 0;
181 }
182
183 /* Start up the display and turn on power to PWMs */
184 static void basic_init(struct dc_cmd_reg *cmd)
185 {
186         u32 val;
187
188         writel(0x00000100, &cmd->gen_incr_syncpt_ctrl);
189         writel(0x0000011a, &cmd->cont_syncpt_vsync);
190         writel(0x00000000, &cmd->int_type);
191         writel(0x00000000, &cmd->int_polarity);
192         writel(0x00000000, &cmd->int_mask);
193         writel(0x00000000, &cmd->int_enb);
194
195         val = PW0_ENABLE | PW1_ENABLE | PW2_ENABLE;
196         val |= PW3_ENABLE | PW4_ENABLE | PM0_ENABLE;
197         val |= PM1_ENABLE;
198         writel(val, &cmd->disp_pow_ctrl);
199
200         val = readl(&cmd->disp_cmd);
201         val |= CTRL_MODE_C_DISPLAY << CTRL_MODE_SHIFT;
202         writel(val, &cmd->disp_cmd);
203 }
204
205 static void basic_init_timer(struct dc_disp_reg *disp)
206 {
207         writel(0x00000020, &disp->mem_high_pri);
208         writel(0x00000001, &disp->mem_high_pri_timer);
209 }
210
211 static const u32 rgb_enb_tab[PIN_REG_COUNT] = {
212         0x00000000,
213         0x00000000,
214         0x00000000,
215         0x00000000,
216 };
217
218 static const u32 rgb_polarity_tab[PIN_REG_COUNT] = {
219         0x00000000,
220         0x01000000,
221         0x00000000,
222         0x00000000,
223 };
224
225 static const u32 rgb_data_tab[PIN_REG_COUNT] = {
226         0x00000000,
227         0x00000000,
228         0x00000000,
229         0x00000000,
230 };
231
232 static const u32 rgb_sel_tab[PIN_OUTPUT_SEL_COUNT] = {
233         0x00000000,
234         0x00000000,
235         0x00000000,
236         0x00000000,
237         0x00210222,
238         0x00002200,
239         0x00020000,
240 };
241
242 static void rgb_enable(struct dc_com_reg *com)
243 {
244         int i;
245
246         for (i = 0; i < PIN_REG_COUNT; i++) {
247                 writel(rgb_enb_tab[i], &com->pin_output_enb[i]);
248                 writel(rgb_polarity_tab[i], &com->pin_output_polarity[i]);
249                 writel(rgb_data_tab[i], &com->pin_output_data[i]);
250         }
251
252         for (i = 0; i < PIN_OUTPUT_SEL_COUNT; i++)
253                 writel(rgb_sel_tab[i], &com->pin_output_sel[i]);
254 }
255
256 static int setup_window(struct disp_ctl_win *win,
257                         struct tegra_lcd_priv *priv)
258 {
259         win->x = 0;
260         win->y = 0;
261         win->w = priv->width;
262         win->h = priv->height;
263         win->out_x = 0;
264         win->out_y = 0;
265         win->out_w = priv->width;
266         win->out_h = priv->height;
267         win->phys_addr = priv->frame_buffer;
268         win->stride = priv->width * (1 << priv->log2_bpp) / 8;
269         debug("%s: depth = %d\n", __func__, priv->log2_bpp);
270         switch (priv->log2_bpp) {
271         case 5:
272         case 24:
273                 win->fmt = COLOR_DEPTH_R8G8B8A8;
274                 win->bpp = 32;
275                 break;
276         case 4:
277                 win->fmt = COLOR_DEPTH_B5G6R5;
278                 win->bpp = 16;
279                 break;
280
281         default:
282                 debug("Unsupported LCD bit depth");
283                 return -1;
284         }
285
286         return 0;
287 }
288
289 static void debug_timing(const char *name, unsigned int timing[])
290 {
291 #ifdef DEBUG
292         int i;
293
294         debug("%s timing: ", name);
295         for (i = 0; i < FDT_LCD_TIMING_COUNT; i++)
296                 debug("%d ", timing[i]);
297         debug("\n");
298 #endif
299 }
300
301 /**
302  * Decode panel information from the fdt, according to a standard binding
303  *
304  * @param blob          fdt blob
305  * @param node          offset of fdt node to read from
306  * @param priv          structure to store fdt config into
307  * @return 0 if ok, -ve on error
308  */
309 static int tegra_decode_panel(const void *blob, int node,
310                               struct tegra_lcd_priv *priv)
311 {
312         int front, back, ref;
313
314         priv->width = fdtdec_get_int(blob, node, "xres", -1);
315         priv->height = fdtdec_get_int(blob, node, "yres", -1);
316         priv->pixel_clock = fdtdec_get_int(blob, node, "clock", 0);
317         if (!priv->pixel_clock || priv->width == -1 || priv->height == -1) {
318                 debug("%s: Pixel parameters missing\n", __func__);
319                 return -FDT_ERR_NOTFOUND;
320         }
321
322         back = fdtdec_get_int(blob, node, "left-margin", -1);
323         front = fdtdec_get_int(blob, node, "right-margin", -1);
324         ref = fdtdec_get_int(blob, node, "hsync-len", -1);
325         if ((back | front | ref) == -1) {
326                 debug("%s: Horizontal parameters missing\n", __func__);
327                 return -FDT_ERR_NOTFOUND;
328         }
329
330         /* Use a ref-to-sync of 1 always, and take this from the front porch */
331         priv->horiz_timing[FDT_LCD_TIMING_REF_TO_SYNC] = 1;
332         priv->horiz_timing[FDT_LCD_TIMING_SYNC_WIDTH] = ref;
333         priv->horiz_timing[FDT_LCD_TIMING_BACK_PORCH] = back;
334         priv->horiz_timing[FDT_LCD_TIMING_FRONT_PORCH] = front -
335                 priv->horiz_timing[FDT_LCD_TIMING_REF_TO_SYNC];
336         debug_timing("horiz", priv->horiz_timing);
337
338         back = fdtdec_get_int(blob, node, "upper-margin", -1);
339         front = fdtdec_get_int(blob, node, "lower-margin", -1);
340         ref = fdtdec_get_int(blob, node, "vsync-len", -1);
341         if ((back | front | ref) == -1) {
342                 debug("%s: Vertical parameters missing\n", __func__);
343                 return -FDT_ERR_NOTFOUND;
344         }
345
346         priv->vert_timing[FDT_LCD_TIMING_REF_TO_SYNC] = 1;
347         priv->vert_timing[FDT_LCD_TIMING_SYNC_WIDTH] = ref;
348         priv->vert_timing[FDT_LCD_TIMING_BACK_PORCH] = back;
349         priv->vert_timing[FDT_LCD_TIMING_FRONT_PORCH] = front -
350                 priv->vert_timing[FDT_LCD_TIMING_REF_TO_SYNC];
351         debug_timing("vert", priv->vert_timing);
352
353         return 0;
354 }
355
356 /**
357  * Decode the display controller information from the fdt.
358  *
359  * @param blob          fdt blob
360  * @param priv          structure to store fdt priv into
361  * @return 0 if ok, -ve on error
362  */
363 static int tegra_display_decode_config(const void *blob, int node,
364                                        struct tegra_lcd_priv *priv)
365 {
366         int rgb;
367         int bpp, bit;
368
369         priv->disp = (struct disp_ctlr *)fdtdec_get_addr(blob, node, "reg");
370         if (!priv->disp) {
371                 debug("%s: No display controller address\n", __func__);
372                 return -1;
373         }
374
375         rgb = fdt_subnode_offset(blob, node, "rgb");
376
377         priv->panel_node = fdtdec_lookup_phandle(blob, rgb, "nvidia,panel");
378         if (priv->panel_node < 0) {
379                 debug("%s: Cannot find panel information\n", __func__);
380                 return -1;
381         }
382
383         if (tegra_decode_panel(blob, priv->panel_node, priv)) {
384                 debug("%s: Failed to decode panel information\n", __func__);
385                 return -1;
386         }
387
388         bpp = fdtdec_get_int(blob, priv->panel_node, "nvidia,bits-per-pixel",
389                              -1);
390         bit = ffs(bpp) - 1;
391         if (bpp == (1 << bit))
392                 priv->log2_bpp = bit;
393         else
394                 priv->log2_bpp = bpp;
395         if (bpp == -1) {
396                 debug("%s: Pixel bpp parameters missing\n", __func__);
397                 return -FDT_ERR_NOTFOUND;
398         }
399         priv->bpp = bpp;
400
401         return 0;
402 }
403
404 /**
405  * Register a new display based on device tree configuration.
406  *
407  * The frame buffer can be positioned by U-Boot or overriden by the fdt.
408  * You should pass in the U-Boot address here, and check the contents of
409  * struct tegra_lcd_priv to see what was actually chosen.
410  *
411  * @param blob                  Device tree blob
412  * @param priv                  Driver's private data
413  * @param default_lcd_base      Default address of LCD frame buffer
414  * @return 0 if ok, -1 on error (unsupported bits per pixel)
415  */
416 static int tegra_display_probe(const void *blob, struct tegra_lcd_priv *priv,
417                                void *default_lcd_base)
418 {
419         struct disp_ctl_win window;
420         struct dc_ctlr *dc;
421
422         priv->frame_buffer = (u32)default_lcd_base;
423
424         dc = (struct dc_ctlr *)priv->disp;
425
426         /*
427          * A header file for clock constants was NAKed upstream.
428          * TODO: Put this into the FDT and fdt_lcd struct when we have clock
429          * support there
430          */
431         clock_start_periph_pll(PERIPH_ID_HOST1X, CLOCK_ID_PERIPH,
432                                144 * 1000000);
433         clock_start_periph_pll(PERIPH_ID_DISP1, CLOCK_ID_CGENERAL,
434                                600 * 1000000);
435         basic_init(&dc->cmd);
436         basic_init_timer(&dc->disp);
437         rgb_enable(&dc->com);
438
439         if (priv->pixel_clock)
440                 update_display_mode(&dc->disp, priv);
441
442         if (setup_window(&window, priv))
443                 return -1;
444
445         update_window(dc, &window);
446
447         return 0;
448 }
449
450 /**
451  * Decode the panel information from the fdt.
452  *
453  * @param blob          fdt blob
454  * @param priv          structure to store fdt config into
455  * @return 0 if ok, -ve on error
456  */
457 static int fdt_decode_lcd(const void *blob, struct tegra_lcd_priv *priv)
458 {
459         int display_node;
460
461         display_node = priv->panel_node;
462         if (display_node < 0) {
463                 debug("%s: No panel configuration available\n", __func__);
464                 return -1;
465         }
466
467         priv->pwm_channel = pwm_request(blob, display_node, "nvidia,pwm");
468         if (priv->pwm_channel < 0) {
469                 debug("%s: Unable to request PWM channel\n", __func__);
470                 return -1;
471         }
472
473         priv->cache_type = fdtdec_get_int(blob, display_node,
474                                             "nvidia,cache-type",
475                                             FDT_LCD_CACHE_WRITE_BACK_FLUSH);
476
477         /* These GPIOs are all optional */
478         gpio_request_by_name_nodev(blob, display_node,
479                                    "nvidia,backlight-enable-gpios", 0,
480                                    &priv->backlight_en, GPIOD_IS_OUT);
481         gpio_request_by_name_nodev(blob, display_node,
482                                    "nvidia,lvds-shutdown-gpios", 0,
483                                    &priv->lvds_shutdown, GPIOD_IS_OUT);
484         gpio_request_by_name_nodev(blob, display_node,
485                                    "nvidia,backlight-vdd-gpios", 0,
486                                    &priv->backlight_vdd, GPIOD_IS_OUT);
487         gpio_request_by_name_nodev(blob, display_node,
488                                    "nvidia,panel-vdd-gpios", 0,
489                                    &priv->panel_vdd, GPIOD_IS_OUT);
490
491         return fdtdec_get_int_array(blob, display_node, "nvidia,panel-timings",
492                         priv->panel_timings, FDT_LCD_TIMINGS);
493 }
494
495 /**
496  * Handle the next stage of device init
497  */
498 static int handle_stage(const void *blob, struct tegra_lcd_priv *priv)
499 {
500         debug("%s: stage %d\n", __func__, stage);
501
502         /* do the things for this stage */
503         switch (stage) {
504         case STAGE_START:
505                 /*
506                  * It is possible that the FDT has requested that the LCD be
507                  * disabled. We currently don't support this. It would require
508                  * changes to U-Boot LCD subsystem to have LCD support
509                  * compiled in but not used. An easier option might be to
510                  * still have a frame buffer, but leave the backlight off and
511                  * remove all mention of lcd in the stdout environment
512                  * variable.
513                  */
514
515                 funcmux_select(PERIPH_ID_DISP1, FUNCMUX_DEFAULT);
516                 break;
517         case STAGE_PANEL_VDD:
518                 if (dm_gpio_is_valid(&priv->panel_vdd))
519                         dm_gpio_set_value(&priv->panel_vdd, 1);
520                 break;
521         case STAGE_LVDS:
522                 if (dm_gpio_is_valid(&priv->lvds_shutdown))
523                         dm_gpio_set_value(&priv->lvds_shutdown, 1);
524                 break;
525         case STAGE_BACKLIGHT_VDD:
526                 if (dm_gpio_is_valid(&priv->backlight_vdd))
527                         dm_gpio_set_value(&priv->backlight_vdd, 1);
528                 break;
529         case STAGE_PWM:
530                 /* Enable PWM at 15/16 high, 32768 Hz with divider 1 */
531                 pinmux_set_func(PMUX_PINGRP_GPU, PMUX_FUNC_PWM);
532                 pinmux_tristate_disable(PMUX_PINGRP_GPU);
533
534                 pwm_enable(priv->pwm_channel, 32768, 0xdf, 1);
535                 break;
536         case STAGE_BACKLIGHT_EN:
537                 if (dm_gpio_is_valid(&priv->backlight_en))
538                         dm_gpio_set_value(&priv->backlight_en, 1);
539                 break;
540         case STAGE_DONE:
541                 break;
542         }
543
544         /* set up timer for next stage */
545         timer_next = timer_get_us();
546         if (stage < FDT_LCD_TIMINGS)
547                 timer_next += priv->panel_timings[stage] * 1000;
548
549         /* move to next stage */
550         stage++;
551         return 0;
552 }
553
554 /**
555  * Perform the next stage of the LCD init if it is time to do so.
556  *
557  * LCD init can be time-consuming because of the number of delays we need
558  * while waiting for the backlight power supply, etc. This function can
559  * be called at various times during U-Boot operation to advance the
560  * initialization of the LCD to the next stage if sufficient time has
561  * passed since the last stage. It keeps track of what stage it is up to
562  * and the time that it is permitted to move to the next stage.
563  *
564  * The final call should have wait=1 to complete the init.
565  *
566  * @param blob  fdt blob containing LCD information
567  * @param wait  1 to wait until all init is complete, and then return
568  *              0 to return immediately, potentially doing nothing if it is
569  *              not yet time for the next init.
570  */
571 static int tegra_lcd_check_next_stage(const void *blob,
572                                       struct tegra_lcd_priv *priv, int wait)
573 {
574         if (stage == STAGE_DONE)
575                 return 0;
576
577         do {
578                 /* wait if we need to */
579                 debug("%s: stage %d\n", __func__, stage);
580                 if (stage != STAGE_START) {
581                         int delay = timer_next - timer_get_us();
582
583                         if (delay > 0) {
584                                 if (wait)
585                                         udelay(delay);
586                                 else
587                                         return 0;
588                         }
589                 }
590
591                 if (handle_stage(blob, priv))
592                         return -1;
593         } while (wait && stage != STAGE_DONE);
594         if (stage == STAGE_DONE)
595                 debug("%s: LCD init complete\n", __func__);
596
597         return 0;
598 }
599
600 static int tegra_lcd_probe(struct udevice *dev)
601 {
602         struct video_uc_platdata *plat = dev_get_uclass_platdata(dev);
603         struct video_priv *uc_priv = dev_get_uclass_priv(dev);
604         struct tegra_lcd_priv *priv = dev_get_priv(dev);
605         const void *blob = gd->fdt_blob;
606         int type = DCACHE_OFF;
607
608         if (tegra_display_decode_config(blob, dev->of_offset, priv))
609                 return -1;
610
611         /* get panel details */
612         if (fdt_decode_lcd(blob, priv)) {
613                 printf("No valid LCD information in device tree\n");
614                 return -1;
615         }
616
617         /* Initialize the Tegra display controller */
618         if (tegra_display_probe(blob, priv, (void *)plat->base)) {
619                 printf("%s: Failed to probe display driver\n", __func__);
620                 return -1;
621         }
622
623         tegra_lcd_check_next_stage(blob, priv, 1);
624
625         /* Set up the LCD caching as requested */
626         if (priv->cache_type & FDT_LCD_CACHE_WRITE_THROUGH)
627                 type = DCACHE_WRITETHROUGH;
628         else if (priv->cache_type & FDT_LCD_CACHE_WRITE_BACK)
629                 type = DCACHE_WRITEBACK;
630         mmu_set_region_dcache_behaviour(priv->frame_buffer, plat->size, type);
631
632         /* Enable flushing after LCD writes if requested */
633         video_set_flush_dcache(dev, priv->cache_type & FDT_LCD_CACHE_FLUSH);
634
635         uc_priv->xsize = priv->width;
636         uc_priv->ysize = priv->height;
637         uc_priv->bpix = priv->log2_bpp;
638         debug("LCD frame buffer at %pa, size %x\n", &priv->frame_buffer,
639               plat->size);
640
641         return 0;
642 }
643
644 static int tegra_lcd_bind(struct udevice *dev)
645 {
646         struct video_uc_platdata *plat = dev_get_uclass_platdata(dev);
647
648         plat->size = LCD_MAX_WIDTH * LCD_MAX_HEIGHT *
649                 (1 << LCD_MAX_LOG2_BPP) / 8;
650
651         return 0;
652 }
653
654 static const struct video_ops tegra_lcd_ops = {
655 };
656
657 static const struct udevice_id tegra_lcd_ids[] = {
658         { .compatible = "nvidia,tegra20-dc" },
659         { }
660 };
661
662 U_BOOT_DRIVER(tegra_lcd) = {
663         .name   = "tegra_lcd",
664         .id     = UCLASS_VIDEO,
665         .of_match = tegra_lcd_ids,
666         .ops    = &tegra_lcd_ops,
667         .bind   = tegra_lcd_bind,
668         .probe  = tegra_lcd_probe,
669         .priv_auto_alloc_size   = sizeof(struct tegra_lcd_priv),
670 };