]> nv-tegra.nvidia Code Review - linux-2.6.git/blob - drivers/video/tegra/fb.c
media: ov5640: Add Omnivision OV5640 support
[linux-2.6.git] / drivers / video / tegra / fb.c
1 /*
2  * drivers/video/tegra/fb.c
3  *
4  * Copyright (C) 2010 Google, Inc.
5  * Author: Erik Gilling <konkers@android.com>
6  *         Colin Cross <ccross@android.com>
7  *         Travis Geiselbrecht <travis@palm.com>
8  *
9  * Copyright (c) 2010-2012, NVIDIA CORPORATION, All rights reserved.
10  *
11  * This software is licensed under the terms of the GNU General Public
12  * License version 2, as published by the Free Software Foundation, and
13  * may be copied, distributed, and modified under those terms.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU General Public License for more details.
19  *
20  */
21
22 #include <linux/fb.h>
23 #include <linux/module.h>
24 #include <linux/kernel.h>
25 #include <linux/errno.h>
26 #include <linux/string.h>
27 #include <linux/mm.h>
28 #include <linux/uaccess.h>
29 #include <linux/slab.h>
30 #include <linux/file.h>
31 #include <linux/workqueue.h>
32
33 #include <asm/atomic.h>
34
35 #include <video/tegrafb.h>
36
37 #include <mach/dc.h>
38 #include <mach/fb.h>
39 #include <linux/nvhost.h>
40 #include <linux/nvmap.h>
41 #include <linux/console.h>
42
43
44 #include "host/dev.h"
45 #include "nvmap/nvmap.h"
46 #include "dc/dc_priv.h"
47
48 /* Pad pitch to 16-byte boundary. */
49 #define TEGRA_LINEAR_PITCH_ALIGNMENT 32
50
51 struct tegra_fb_info {
52         struct tegra_dc_win     *win;
53         struct nvhost_device    *ndev;
54         struct fb_info          *info;
55         bool                    valid;
56
57         struct resource         *fb_mem;
58
59         int                     xres;
60         int                     yres;
61 };
62
63 /* palette array used by the fbcon */
64 static u32 pseudo_palette[16];
65
66 static int tegra_fb_check_var(struct fb_var_screeninfo *var,
67                               struct fb_info *info)
68 {
69         struct tegra_fb_info *tegra_fb = info->par;
70         struct tegra_dc *dc = tegra_fb->win->dc;
71         struct tegra_dc_out_ops *ops = dc->out_ops;
72         struct fb_videomode mode;
73
74         if ((var->yres * var->xres * var->bits_per_pixel / 8 * 2) >
75             info->screen_size)
76                 return -EINVAL;
77
78         /* Apply mode filter for HDMI only -LVDS supports only fix mode */
79         if (ops && ops->mode_filter) {
80
81                 fb_var_to_videomode(&mode, var);
82                 if (!ops->mode_filter(dc, &mode))
83                         return -EINVAL;
84
85                 /* Mode filter may have modified the mode */
86                 fb_videomode_to_var(var, &mode);
87         }
88
89         /* Double yres_virtual to allow double buffering through pan_display */
90         var->yres_virtual = var->yres * 2;
91
92         return 0;
93 }
94
95 static int tegra_fb_set_par(struct fb_info *info)
96 {
97         struct tegra_fb_info *tegra_fb = info->par;
98         struct fb_var_screeninfo *var = &info->var;
99         struct tegra_dc *dc = tegra_fb->win->dc;
100
101         if (var->bits_per_pixel) {
102                 /* we only support RGB ordering for now */
103                 switch (var->bits_per_pixel) {
104                 case 32:
105                         var->red.offset = 0;
106                         var->red.length = 8;
107                         var->green.offset = 8;
108                         var->green.length = 8;
109                         var->blue.offset = 16;
110                         var->blue.length = 8;
111                         var->transp.offset = 24;
112                         var->transp.length = 8;
113                         tegra_fb->win->fmt = TEGRA_WIN_FMT_R8G8B8A8;
114                         break;
115                 case 16:
116                         var->red.offset = 11;
117                         var->red.length = 5;
118                         var->green.offset = 5;
119                         var->green.length = 6;
120                         var->blue.offset = 0;
121                         var->blue.length = 5;
122                         tegra_fb->win->fmt = TEGRA_WIN_FMT_B5G6R5;
123                         break;
124
125                 default:
126                         return -EINVAL;
127                 }
128                 /* if line_length unset, then pad the stride */
129                 if (!info->fix.line_length) {
130                         info->fix.line_length = var->xres * var->bits_per_pixel
131                                 / 8;
132                         info->fix.line_length = round_up(info->fix.line_length,
133                                                 TEGRA_LINEAR_PITCH_ALIGNMENT);
134                 }
135                 tegra_fb->win->stride = info->fix.line_length;
136                 tegra_fb->win->stride_uv = 0;
137                 tegra_fb->win->phys_addr_u = 0;
138                 tegra_fb->win->phys_addr_v = 0;
139         }
140
141         if (var->pixclock) {
142                 bool stereo;
143                 unsigned old_len = 0;
144                 struct fb_videomode m;
145                 struct fb_videomode *old_mode = NULL;
146
147                 fb_var_to_videomode(&m, var);
148
149                 /* Load framebuffer info with new mode details*/
150                 old_mode = info->mode;
151                 old_len  = info->fix.line_length;
152
153                 info->mode = (struct fb_videomode *)
154                         fb_find_nearest_mode(&m, &info->modelist);
155                 if (!info->mode) {
156                         dev_warn(&tegra_fb->ndev->dev, "can't match video mode\n");
157                         info->mode = old_mode;
158                         return -EINVAL;
159                 }
160
161                 /* Update fix line_length and window stride as per new mode */
162                 info->fix.line_length = var->xres * var->bits_per_pixel / 8;
163                 info->fix.line_length = round_up(info->fix.line_length,
164                         TEGRA_LINEAR_PITCH_ALIGNMENT);
165                 tegra_fb->win->stride = info->fix.line_length;
166
167                 /*
168                  * only enable stereo if the mode supports it and
169                  * client requests it
170                  */
171                 stereo = !!(var->vmode & info->mode->vmode &
172 #ifndef CONFIG_TEGRA_HDMI_74MHZ_LIMIT
173                                         FB_VMODE_STEREO_FRAME_PACK);
174 #else
175                                         FB_VMODE_STEREO_LEFT_RIGHT);
176 #endif
177
178                 /* Configure DC with new mode */
179                 if (tegra_dc_set_fb_mode(dc, info->mode, stereo)) {
180                         /* Error while configuring DC, fallback to old mode */
181                         dev_warn(&tegra_fb->ndev->dev, "can't configure dc with mode %ux%u\n",
182                                 info->mode->xres, info->mode->yres);
183                         info->mode = old_mode;
184                         info->fix.line_length = old_len;
185                         tegra_fb->win->stride = old_len;
186                         return -EINVAL;
187                 }
188
189                 /* Reflect mode chnage on DC HW */
190                 if (dc->enabled)
191                         tegra_dc_disable(dc);
192                 tegra_dc_enable(dc);
193
194                 tegra_fb->win->w.full = dfixed_const(info->mode->xres);
195                 tegra_fb->win->h.full = dfixed_const(info->mode->yres);
196                 tegra_fb->win->out_w = info->mode->xres;
197                 tegra_fb->win->out_h = info->mode->yres;
198         }
199         return 0;
200 }
201
202 static int tegra_fb_setcolreg(unsigned regno, unsigned red, unsigned green,
203         unsigned blue, unsigned transp, struct fb_info *info)
204 {
205         struct fb_var_screeninfo *var = &info->var;
206
207         if (info->fix.visual == FB_VISUAL_TRUECOLOR ||
208             info->fix.visual == FB_VISUAL_DIRECTCOLOR) {
209                 u32 v;
210
211                 if (regno >= 16)
212                         return -EINVAL;
213
214                 red = (red >> (16 - info->var.red.length));
215                 green = (green >> (16 - info->var.green.length));
216                 blue = (blue >> (16 - info->var.blue.length));
217
218                 v = (red << var->red.offset) |
219                         (green << var->green.offset) |
220                         (blue << var->blue.offset);
221
222                 ((u32 *)info->pseudo_palette)[regno] = v;
223         }
224
225         return 0;
226 }
227
228
229 static int tegra_fb_setcmap(struct fb_cmap *cmap, struct fb_info *info)
230 {
231         struct tegra_fb_info *tegra_fb = info->par;
232         struct tegra_dc *dc = tegra_fb->win->dc;
233         int i;
234         u16 *red = cmap->red;
235         u16 *green = cmap->green;
236         u16 *blue = cmap->blue;
237         int start = cmap->start;
238
239         if (((unsigned)start > 255) || ((start + cmap->len) > 256))
240                 return -EINVAL;
241
242         if (info->fix.visual == FB_VISUAL_TRUECOLOR ||
243                 info->fix.visual == FB_VISUAL_DIRECTCOLOR) {
244                 /*
245                  * For now we are considering color schemes with
246                  * cmap->len <=16 as special case of basic color
247                  * scheme to support fbconsole.But for DirectColor
248                  * visuals(like the one we actually have, that include
249                  * a HW LUT),the way it's intended to work is that the
250                  * actual LUT HW is programmed to the intended values,
251                  * even for small color maps like those with 16 or fewer
252                  * entries. The pseudo_palette is then programmed to the
253                  * identity transform.
254                  */
255                 if (cmap->len <= 16) {
256                         /* Low-color schemes like fbconsole*/
257                         u16 *transp = cmap->transp;
258                         u_int vtransp = 0xffff;
259
260                         for (i = 0; i < cmap->len; i++) {
261                                 if (transp)
262                                         vtransp = *transp++;
263                                 if (tegra_fb_setcolreg(start++, *red++,
264                                         *green++, *blue++,
265                                         vtransp, info))
266                                                 return -EINVAL;
267                         }
268                 } else {
269                         /* High-color schemes*/
270                         for (i = 0; i < cmap->len; i++) {
271                                 dc->fb_lut.r[start+i] = *red++ >> 8;
272                                 dc->fb_lut.g[start+i] = *green++ >> 8;
273                                 dc->fb_lut.b[start+i] = *blue++ >> 8;
274                         }
275                         tegra_dc_update_lut(dc, -1, -1);
276                 }
277         }
278         return 0;
279 }
280
281 static int tegra_fb_blank(int blank, struct fb_info *info)
282 {
283         struct tegra_fb_info *tegra_fb = info->par;
284
285         switch (blank) {
286         case FB_BLANK_UNBLANK:
287                 dev_dbg(&tegra_fb->ndev->dev, "unblank\n");
288                 tegra_fb->win->flags = TEGRA_WIN_FLAG_ENABLED;
289                 tegra_dc_enable(tegra_fb->win->dc);
290                 return 0;
291
292         case FB_BLANK_NORMAL:
293                 dev_dbg(&tegra_fb->ndev->dev, "blank - normal\n");
294                 tegra_dc_blank(tegra_fb->win->dc);
295                 return 0;
296
297         case FB_BLANK_VSYNC_SUSPEND:
298         case FB_BLANK_HSYNC_SUSPEND:
299         case FB_BLANK_POWERDOWN:
300                 dev_dbg(&tegra_fb->ndev->dev, "blank - powerdown\n");
301                 tegra_dc_disable(tegra_fb->win->dc);
302                 return 0;
303
304         default:
305                 return -ENOTTY;
306         }
307 }
308
309 static int tegra_fb_pan_display(struct fb_var_screeninfo *var,
310                                 struct fb_info *info)
311 {
312         struct tegra_fb_info *tegra_fb = info->par;
313         char __iomem *flush_start;
314         char __iomem *flush_end;
315         u32 addr;
316
317         if (!tegra_fb->win->cur_handle) {
318                 flush_start = info->screen_base + (var->yoffset * info->fix.line_length);
319                 flush_end = flush_start + (var->yres * info->fix.line_length);
320
321                 info->var.xoffset = var->xoffset;
322                 info->var.yoffset = var->yoffset;
323
324                 addr = info->fix.smem_start + (var->yoffset * info->fix.line_length) +
325                         (var->xoffset * (var->bits_per_pixel/8));
326
327                 tegra_fb->win->phys_addr = addr;
328                 tegra_fb->win->flags = TEGRA_WIN_FLAG_ENABLED;
329                 tegra_fb->win->virt_addr = info->screen_base;
330
331                 tegra_dc_update_windows(&tegra_fb->win, 1);
332                 tegra_dc_sync_windows(&tegra_fb->win, 1);
333         }
334
335         return 0;
336 }
337
338 static void tegra_fb_fillrect(struct fb_info *info,
339                               const struct fb_fillrect *rect)
340 {
341         cfb_fillrect(info, rect);
342 }
343
344 static void tegra_fb_copyarea(struct fb_info *info,
345                               const struct fb_copyarea *region)
346 {
347         cfb_copyarea(info, region);
348 }
349
350 static void tegra_fb_imageblit(struct fb_info *info,
351                                const struct fb_image *image)
352 {
353         cfb_imageblit(info, image);
354 }
355
356 static int tegra_fb_ioctl(struct fb_info *info, unsigned int cmd, unsigned long arg)
357 {
358         struct tegra_fb_info *tegra_fb = (struct tegra_fb_info *)info->par;
359         struct tegra_fb_modedb modedb;
360         struct fb_modelist *modelist;
361         struct fb_vblank vblank = {};
362         int i;
363
364         switch (cmd) {
365         case FBIO_TEGRA_GET_MODEDB:
366                 if (copy_from_user(&modedb, (void __user *)arg, sizeof(modedb)))
367                         return -EFAULT;
368
369                 i = 0;
370                 list_for_each_entry(modelist, &info->modelist, list) {
371                         struct fb_var_screeninfo var;
372
373                         if (i >= modedb.modedb_len)
374                                 break;
375
376                         /* fb_videomode_to_var doesn't fill out all the members
377                            of fb_var_screeninfo */
378                         memset(&var, 0x0, sizeof(var));
379
380                         fb_videomode_to_var(&var, &modelist->mode);
381
382                         if (copy_to_user((void __user *)&modedb.modedb[i],
383                                          &var, sizeof(var)))
384                                 return -EFAULT;
385                         i++;
386
387                         if (var.vmode & FB_VMODE_STEREO_MASK) {
388                                 if (i >= modedb.modedb_len)
389                                         break;
390                                 var.vmode &= ~FB_VMODE_STEREO_MASK;
391                                 if (copy_to_user(
392                                         (void __user *)&modedb.modedb[i],
393                                          &var, sizeof(var)))
394                                         return -EFAULT;
395                                 i++;
396                         }
397                 }
398                 modedb.modedb_len = i;
399
400                 if (copy_to_user((void __user *)arg, &modedb, sizeof(modedb)))
401                         return -EFAULT;
402                 break;
403
404         case FBIOGET_VBLANK:
405                 tegra_dc_get_fbvblank(tegra_fb->win->dc, &vblank);
406
407                 if (copy_to_user(
408                         (void __user *)arg, &vblank, sizeof(vblank)))
409                         return -EFAULT;
410                 break;
411
412         case FBIO_WAITFORVSYNC:
413                 return tegra_dc_wait_for_vsync(tegra_fb->win->dc);
414
415         default:
416                 return -ENOTTY;
417         }
418
419         return 0;
420 }
421
422 int tegra_fb_get_mode(struct tegra_dc *dc) {
423         return dc->fb->info->mode->refresh;
424 }
425
426 int tegra_fb_set_mode(struct tegra_dc *dc, int fps) {
427         size_t stereo;
428         struct list_head *pos;
429         struct fb_videomode *best_mode = NULL;
430         int curr_diff = INT_MAX; /* difference of best_mode refresh rate */
431         struct fb_modelist *modelist;
432         struct fb_info *info = dc->fb->info;
433
434         list_for_each(pos, &info->modelist) {
435                 struct fb_videomode *mode;
436
437                 modelist = list_entry(pos, struct fb_modelist, list);
438                 mode = &modelist->mode;
439                 if (fps <= mode->refresh && curr_diff > (mode->refresh - fps)) {
440                         curr_diff = mode->refresh - fps;
441                         best_mode = mode;
442                 }
443         }
444         if (best_mode) {
445                 info->mode = best_mode;
446                 stereo = !!(info->var.vmode & info->mode->vmode &
447 #ifndef CONFIG_TEGRA_HDMI_74MHZ_LIMIT
448                                 FB_VMODE_STEREO_FRAME_PACK);
449 #else
450                                 FB_VMODE_STEREO_LEFT_RIGHT);
451 #endif
452                 return tegra_dc_set_fb_mode(dc, best_mode, stereo);
453         }
454         return -EIO;
455 }
456
457 static struct fb_ops tegra_fb_ops = {
458         .owner = THIS_MODULE,
459         .fb_check_var = tegra_fb_check_var,
460         .fb_set_par = tegra_fb_set_par,
461         .fb_setcmap = tegra_fb_setcmap,
462         .fb_blank = tegra_fb_blank,
463         .fb_pan_display = tegra_fb_pan_display,
464         .fb_fillrect = tegra_fb_fillrect,
465         .fb_copyarea = tegra_fb_copyarea,
466         .fb_imageblit = tegra_fb_imageblit,
467         .fb_ioctl = tegra_fb_ioctl,
468 };
469
470 const struct fb_videomode *tegra_fb_find_best_mode(
471         struct fb_var_screeninfo *var,
472         struct list_head *head)
473 {
474         struct list_head *pos;
475         struct fb_modelist *modelist;
476         struct fb_videomode *mode, *best = NULL;
477         int diff = 0;
478
479         list_for_each(pos, head) {
480                 int d;
481
482                 modelist = list_entry(pos, struct fb_modelist, list);
483                 mode = &modelist->mode;
484
485                 if (mode->xres >= var->xres && mode->yres >= var->yres) {
486                         d = (mode->xres - var->xres) +
487                                 (mode->yres - var->yres);
488                         if (diff < d) {
489                                 diff = d;
490                                 best = mode;
491                         } else if (diff == d && best &&
492                                    mode->refresh > best->refresh)
493                                 best = mode;
494                 }
495         }
496         return best;
497 }
498
499 static int tegra_fb_activate_mode(struct tegra_fb_info *fb_info,
500                                 struct fb_var_screeninfo *var)
501 {
502         int err;
503         struct fb_info *info = fb_info->info;
504
505         var->activate |= FB_ACTIVATE_FORCE;
506         console_lock();
507         info->flags |= FBINFO_MISC_USEREVENT;
508         err = fb_set_var(info, var);
509         info->flags &= ~FBINFO_MISC_USEREVENT;
510         console_unlock();
511         if (err)
512                 return err;
513         return 0;
514 }
515
516 void tegra_fb_update_monspecs(struct tegra_fb_info *fb_info,
517                               struct fb_monspecs *specs,
518                               bool (*mode_filter)(const struct tegra_dc *dc,
519                                                   struct fb_videomode *mode))
520 {
521         int i;
522         int ret = 0;
523         struct fb_event event;
524         struct fb_info *info = fb_info->info;
525         const struct fb_videomode *best_mode = NULL;
526         struct fb_var_screeninfo var = {0,};
527
528         mutex_lock(&fb_info->info->lock);
529         fb_destroy_modedb(fb_info->info->monspecs.modedb);
530
531         fb_destroy_modelist(&fb_info->info->modelist);
532
533         if (specs == NULL) {
534                 struct tegra_dc_mode mode;
535                 memset(&fb_info->info->monspecs, 0x0,
536                        sizeof(fb_info->info->monspecs));
537                 memset(&mode, 0x0, sizeof(mode));
538
539                 /*
540                  * reset video mode properties to prevent garbage being displayed on 'mode' device.
541                  */
542                 fb_info->info->mode = (struct fb_videomode*) NULL;
543
544                 tegra_dc_set_mode(fb_info->win->dc, &mode);
545                 mutex_unlock(&fb_info->info->lock);
546                 return;
547         }
548
549         memcpy(&fb_info->info->monspecs, specs,
550                sizeof(fb_info->info->monspecs));
551         fb_info->info->mode = specs->modedb;
552
553         /* Prepare a mode db */
554         for (i = 0; i < specs->modedb_len; i++) {
555                 if (info->fbops->fb_check_var) {
556                         struct fb_videomode m;
557
558                         /* Call mode filter to check mode */
559                         fb_videomode_to_var(&var, &specs->modedb[i]);
560                         if (!(info->fbops->fb_check_var(&var, info))) {
561                                 fb_var_to_videomode(&m, &var);
562                                 fb_add_videomode(&m,
563                                                  &fb_info->info->modelist);
564                         }
565                 } else {
566                         fb_add_videomode(&specs->modedb[i],
567                                          &fb_info->info->modelist);
568                 }
569         }
570
571         /* Get the best mode from modedb and apply on fb */
572         var.xres = 0;
573         var.yres = 0;
574         best_mode = tegra_fb_find_best_mode(&var, &info->modelist);
575
576         /* Update framebuffer with best mode */
577         fb_videomode_to_var(&var, best_mode);
578
579         /* TODO: Get proper way of getting rid of a 0 bpp */
580         if (!var.bits_per_pixel)
581                 var.bits_per_pixel = 32;
582
583         memcpy(&info->var, &var, sizeof(struct fb_var_screeninfo));
584
585         ret = tegra_fb_activate_mode(fb_info, &var);
586         if (ret)
587                 return;
588
589         event.info = fb_info->info;
590
591 #ifdef CONFIG_FRAMEBUFFER_CONSOLE
592 /* Lock the console before sending the noti. Fbconsole
593   * on HDMI might be using console
594   */
595         console_lock();
596 #endif
597         fb_notifier_call_chain(FB_EVENT_NEW_MODELIST, &event);
598 #ifdef CONFIG_FRAMEBUFFER_CONSOLE
599 /* Unlock the console */
600         console_unlock();
601 #endif
602
603         mutex_unlock(&fb_info->info->lock);
604 }
605
606 struct tegra_fb_info *tegra_fb_register(struct nvhost_device *ndev,
607                                         struct tegra_dc *dc,
608                                         struct tegra_fb_data *fb_data,
609                                         struct resource *fb_mem)
610 {
611         struct tegra_dc_win *win;
612         struct fb_info *info;
613         struct tegra_fb_info *tegra_fb;
614         void __iomem *fb_base = NULL;
615         unsigned long fb_size = 0;
616         unsigned long fb_phys = 0;
617         int ret = 0;
618         unsigned stride;
619
620         win = tegra_dc_get_window(dc, fb_data->win);
621         if (!win) {
622                 dev_err(&ndev->dev, "dc does not have a window at index %d\n",
623                         fb_data->win);
624                 return ERR_PTR(-ENOENT);
625         }
626
627         info = framebuffer_alloc(sizeof(struct tegra_fb_info), &ndev->dev);
628         if (!info) {
629                 ret = -ENOMEM;
630                 goto err;
631         }
632
633         tegra_fb = info->par;
634         tegra_fb->win = win;
635         tegra_fb->ndev = ndev;
636         tegra_fb->fb_mem = fb_mem;
637         tegra_fb->xres = fb_data->xres;
638         tegra_fb->yres = fb_data->yres;
639
640         if (fb_mem) {
641                 fb_size = resource_size(fb_mem);
642                 fb_phys = fb_mem->start;
643                 fb_base = ioremap_nocache(fb_phys, fb_size);
644                 if (!fb_base) {
645                         dev_err(&ndev->dev, "fb can't be mapped\n");
646                         ret = -EBUSY;
647                         goto err_free;
648                 }
649                 tegra_fb->valid = true;
650         }
651
652         info->fix.line_length = fb_data->xres * fb_data->bits_per_pixel / 8;
653
654         stride = tegra_dc_get_stride(dc, 0);
655         if (!stride) /* default to pad the stride */
656                 stride = round_up(info->fix.line_length,
657                         TEGRA_LINEAR_PITCH_ALIGNMENT);
658
659         info->fbops = &tegra_fb_ops;
660         info->pseudo_palette = pseudo_palette;
661         info->screen_base = fb_base;
662         info->screen_size = fb_size;
663
664         strlcpy(info->fix.id, "tegra_fb", sizeof(info->fix.id));
665         info->fix.type          = FB_TYPE_PACKED_PIXELS;
666         info->fix.visual        = FB_VISUAL_TRUECOLOR;
667         info->fix.xpanstep      = 1;
668         info->fix.ypanstep      = 1;
669         info->fix.accel         = FB_ACCEL_NONE;
670         info->fix.smem_start    = fb_phys;
671         info->fix.smem_len      = fb_size;
672         info->fix.line_length = stride;
673
674         info->var.xres                  = fb_data->xres;
675         info->var.yres                  = fb_data->yres;
676         info->var.xres_virtual          = fb_data->xres;
677         info->var.yres_virtual          = fb_data->yres * 2;
678         info->var.bits_per_pixel        = fb_data->bits_per_pixel;
679         info->var.activate              = FB_ACTIVATE_VBL;
680         info->var.height                = tegra_dc_get_out_height(dc);
681         info->var.width                 = tegra_dc_get_out_width(dc);
682         info->var.pixclock              = 0;
683         info->var.left_margin           = 0;
684         info->var.right_margin          = 0;
685         info->var.upper_margin          = 0;
686         info->var.lower_margin          = 0;
687         info->var.hsync_len             = 0;
688         info->var.vsync_len             = 0;
689         info->var.vmode                 = FB_VMODE_NONINTERLACED;
690
691         win->x.full = dfixed_const(0);
692         win->y.full = dfixed_const(0);
693         win->w.full = dfixed_const(fb_data->xres);
694         win->h.full = dfixed_const(fb_data->yres);
695         /* TODO: set to output res dc */
696         win->out_x = 0;
697         win->out_y = 0;
698         win->out_w = fb_data->xres;
699         win->out_h = fb_data->yres;
700         win->z = 0;
701         win->phys_addr = fb_phys;
702         win->virt_addr = fb_base;
703         win->phys_addr_u = 0;
704         win->phys_addr_v = 0;
705         win->stride = info->fix.line_length;
706         win->stride_uv = 0;
707         win->flags = TEGRA_WIN_FLAG_ENABLED;
708
709         if (fb_mem)
710                 tegra_fb_set_par(info);
711
712         if (register_framebuffer(info)) {
713                 dev_err(&ndev->dev, "failed to register framebuffer\n");
714                 ret = -ENODEV;
715                 goto err_iounmap_fb;
716         }
717
718         tegra_fb->info = info;
719
720         dev_info(&ndev->dev, "probed\n");
721
722         if (fb_data->flags & TEGRA_FB_FLIP_ON_PROBE) {
723                 tegra_dc_update_windows(&tegra_fb->win, 1);
724                 tegra_dc_sync_windows(&tegra_fb->win, 1);
725         }
726
727         if (dc->mode.pclk > 1000) {
728                 struct tegra_dc_mode *mode = &dc->mode;
729                 struct fb_videomode vmode;
730
731                 if (dc->out->flags & TEGRA_DC_OUT_ONE_SHOT_MODE)
732                         info->var.pixclock = KHZ2PICOS(mode->rated_pclk / 1000);
733                 else
734                         info->var.pixclock = KHZ2PICOS(mode->pclk / 1000);
735                 info->var.left_margin = mode->h_back_porch;
736                 info->var.right_margin = mode->h_front_porch;
737                 info->var.upper_margin = mode->v_back_porch;
738                 info->var.lower_margin = mode->v_front_porch;
739                 info->var.hsync_len = mode->h_sync_width;
740                 info->var.vsync_len = mode->v_sync_width;
741
742                 /* Keep info->var consistent with info->modelist. */
743                 fb_var_to_videomode(&vmode, &info->var);
744                 fb_add_videomode(&vmode, &info->modelist);
745         }
746
747         return tegra_fb;
748
749 err_iounmap_fb:
750         if (fb_base)
751                 iounmap(fb_base);
752 err_free:
753         framebuffer_release(info);
754 err:
755         return ERR_PTR(ret);
756 }
757
758 void tegra_fb_unregister(struct tegra_fb_info *fb_info)
759 {
760         struct fb_info *info = fb_info->info;
761
762         unregister_framebuffer(info);
763
764         iounmap(info->screen_base);
765         framebuffer_release(info);
766 }