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