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