video: tegra: add host1x support to driver
[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  * This software is licensed under the terms of the GNU General Public
10  * License version 2, as published by the Free Software Foundation, and
11  * may be copied, distributed, and modified under those terms.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  */
19
20 #include <linux/fb.h>
21 #include <linux/module.h>
22 #include <linux/kernel.h>
23 #include <linux/errno.h>
24 #include <linux/string.h>
25 #include <linux/mm.h>
26 #include <linux/uaccess.h>
27 #include <linux/slab.h>
28 #include <linux/file.h>
29 #include <linux/workqueue.h>
30
31 #include <asm/atomic.h>
32
33 #include <video/tegrafb.h>
34
35 #include <mach/dc.h>
36 #include <mach/fb.h>
37 #include <mach/nvhost.h>
38 #include <mach/nvmap.h>
39
40 #include "host/dev.h"
41 #include "nvmap/nvmap.h"
42
43 struct tegra_fb_info {
44         struct tegra_dc_win     *win;
45         struct nvhost_device    *ndev;
46         struct fb_info          *info;
47         bool                    valid;
48
49         struct resource         *fb_mem;
50
51         int                     xres;
52         int                     yres;
53
54         atomic_t                in_use;
55         struct nvmap_client     *user_nvmap;
56         struct nvmap_client     *fb_nvmap;
57
58         struct workqueue_struct *flip_wq;
59 };
60
61 struct tegra_fb_flip_win {
62         struct tegra_dc_win     win_data;
63         struct tegra_dc_win     *dc_win;
64         s32                     pre_syncpt_id;
65         u32                     pre_syncpt_val;
66 };
67
68 struct tegra_fb_flip_data {
69         struct work_struct      work;
70         struct tegra_fb_info    *fb;
71         struct tegra_fb_flip_win windows[TEGRA_FB_FLIP_N_WINDOWS];
72         u32                     syncpt_max;
73 };
74
75 /* palette array used by the fbcon */
76 static u32 pseudo_palette[16];
77
78 static int tegra_fb_open(struct fb_info *info, int user)
79 {
80         struct tegra_fb_info *tegra_fb = info->par;
81
82         if (atomic_xchg(&tegra_fb->in_use, 1))
83                 return -EBUSY;
84
85         tegra_fb->user_nvmap = NULL;
86
87         return 0;
88 }
89
90 static int tegra_fb_release(struct fb_info *info, int user)
91 {
92         struct tegra_fb_info *tegra_fb = info->par;
93
94         flush_workqueue(tegra_fb->flip_wq);
95
96         if (tegra_fb->user_nvmap) {
97                 nvmap_client_put(tegra_fb->user_nvmap);
98                 tegra_fb->user_nvmap = NULL;
99         }
100
101         WARN_ON(!atomic_xchg(&tegra_fb->in_use, 0));
102
103         return 0;
104 }
105
106 static int tegra_fb_check_var(struct fb_var_screeninfo *var,
107                               struct fb_info *info)
108 {
109         if ((var->yres * var->xres * var->bits_per_pixel / 8 * 2) >
110             info->screen_size)
111                 return -EINVAL;
112
113         /* double yres_virtual to allow double buffering through pan_display */
114         var->yres_virtual = var->yres * 2;
115
116         return 0;
117 }
118
119 static int tegra_fb_set_par(struct fb_info *info)
120 {
121         struct tegra_fb_info *tegra_fb = info->par;
122         struct fb_var_screeninfo *var = &info->var;
123
124         /* we only support RGB ordering for now */
125         switch (var->bits_per_pixel) {
126         case 32:
127         case 24:
128                 var->red.offset = 0;
129                 var->red.length = 8;
130                 var->green.offset = 8;
131                 var->green.length = 8;
132                 var->blue.offset = 16;
133                 var->blue.length = 8;
134                 tegra_fb->win->fmt = TEGRA_WIN_FMT_R8G8B8A8;
135                 break;
136         case 16:
137                 var->red.offset = 11;
138                 var->red.length = 5;
139                 var->green.offset = 5;
140                 var->green.length = 6;
141                 var->blue.offset = 0;
142                 var->blue.length = 5;
143                 tegra_fb->win->fmt = TEGRA_WIN_FMT_B5G6R5;
144                 break;
145
146         case 0:
147                 break;
148
149         default:
150                 return -EINVAL;
151         }
152
153         info->fix.line_length = tegra_dc_compute_stride(var->xres,
154                         var->bits_per_pixel, TEGRA_WIN_LAYOUT_PITCH);
155         tegra_fb->win->stride = info->fix.line_length;
156
157         if (var->pixclock) {
158                 struct tegra_dc_mode mode;
159
160                 info->mode = (struct fb_videomode *)
161                         fb_find_best_mode(var, &info->modelist);
162                 if (!info->mode) {
163                         dev_warn(&tegra_fb->ndev->dev, "can't match video mode\n");
164                         return -EINVAL;
165                 }
166
167                 mode.pclk = PICOS2KHZ(info->mode->pixclock) * 1000;
168                 mode.h_ref_to_sync = 1;
169                 mode.v_ref_to_sync = 1;
170                 mode.h_sync_width = info->mode->hsync_len;
171                 mode.v_sync_width = info->mode->vsync_len;
172                 mode.h_back_porch = info->mode->left_margin;
173                 mode.v_back_porch = info->mode->upper_margin;
174                 mode.h_active = info->mode->xres;
175                 mode.v_active = info->mode->yres;
176                 mode.h_front_porch = info->mode->right_margin;
177                 mode.v_front_porch = info->mode->lower_margin;
178
179                 tegra_dc_set_mode(tegra_fb->win->dc, &mode);
180
181                 tegra_fb->win->w = info->mode->xres;
182                 tegra_fb->win->h = info->mode->xres;
183                 tegra_fb->win->out_w = info->mode->xres;
184                 tegra_fb->win->out_h = info->mode->xres;
185         }
186         return 0;
187 }
188
189 static int tegra_fb_setcolreg(unsigned regno, unsigned red, unsigned green,
190         unsigned blue, unsigned transp, struct fb_info *info)
191 {
192         struct fb_var_screeninfo *var = &info->var;
193
194         if (info->fix.visual == FB_VISUAL_TRUECOLOR ||
195             info->fix.visual == FB_VISUAL_DIRECTCOLOR) {
196                 u32 v;
197
198                 if (regno >= 16)
199                         return -EINVAL;
200
201                 v = (red << var->red.offset) |
202                         (green << var->green.offset) |
203                         (blue << var->blue.offset);
204
205                 ((u32 *)info->pseudo_palette)[regno] = v;
206         }
207
208         return 0;
209 }
210
211 static int tegra_fb_blank(int blank, struct fb_info *info)
212 {
213         struct tegra_fb_info *tegra_fb = info->par;
214
215         switch (blank) {
216         case FB_BLANK_UNBLANK:
217                 dev_dbg(&tegra_fb->ndev->dev, "unblank\n");
218                 tegra_dc_enable(tegra_fb->win->dc);
219                 return 0;
220
221         case FB_BLANK_POWERDOWN:
222                 dev_dbg(&tegra_fb->ndev->dev, "blank\n");
223                 tegra_dc_disable(tegra_fb->win->dc);
224                 return 0;
225
226         default:
227                 return -ENOTTY;
228         }
229 }
230
231 static int tegra_fb_pan_display(struct fb_var_screeninfo *var,
232                                 struct fb_info *info)
233 {
234         struct tegra_fb_info *tegra_fb = info->par;
235         char __iomem *flush_start;
236         char __iomem *flush_end;
237         u32 addr;
238
239         flush_start = info->screen_base + (var->yoffset * info->fix.line_length);
240         flush_end = flush_start + (var->yres * info->fix.line_length);
241
242         info->var.xoffset = var->xoffset;
243         info->var.yoffset = var->yoffset;
244
245         addr = info->fix.smem_start + (var->yoffset * info->fix.line_length) +
246                 (var->xoffset * (var->bits_per_pixel/8));
247
248         tegra_fb->win->phys_addr = addr;
249         /* TODO: update virt_addr */
250
251         tegra_dc_update_windows(&tegra_fb->win, 1);
252         tegra_dc_sync_windows(&tegra_fb->win, 1);
253
254         if (WARN_ON(tegra_fb->win->surface)) {
255                 nvmap_unpin(tegra_fb->fb_nvmap, tegra_fb->win->surface);
256                 nvmap_free(tegra_fb->fb_nvmap, tegra_fb->win->surface);
257                 tegra_fb->win->surface = NULL;
258         }
259
260         return 0;
261 }
262
263 static void tegra_fb_fillrect(struct fb_info *info,
264                               const struct fb_fillrect *rect)
265 {
266         cfb_fillrect(info, rect);
267 }
268
269 static void tegra_fb_copyarea(struct fb_info *info,
270                               const struct fb_copyarea *region)
271 {
272         cfb_copyarea(info, region);
273 }
274
275 static void tegra_fb_imageblit(struct fb_info *info,
276                                const struct fb_image *image)
277 {
278         cfb_imageblit(info, image);
279 }
280
281 /* TODO: implement ALLOC, FREE, BLANK ioctls */
282
283 static int tegra_fb_set_nvmap_fd(struct tegra_fb_info *tegra_fb, int fd)
284 {
285         struct nvmap_client *nvmap = NULL;
286
287         if (fd < 0)
288                 return -EINVAL;
289
290         nvmap = nvmap_client_get_file(fd);
291         if (IS_ERR(nvmap))
292                 return PTR_ERR(nvmap);
293
294         if (tegra_fb->user_nvmap)
295                 nvmap_client_put(tegra_fb->user_nvmap);
296
297         tegra_fb->user_nvmap = nvmap;
298
299         return 0;
300 }
301
302 static int tegra_fb_set_windowattr(struct tegra_fb_info *tegra_fb,
303                                    struct tegra_dc_win *win,
304                                    const struct tegra_fb_windowattr *attr)
305 {
306         struct nvmap_handle_ref *r_dupe;
307         struct nvmap_handle *h_win;
308
309         if (!attr->buff_id) {
310                 win->flags = 0;
311                 win->surface = NULL;
312                 return 0;
313         }
314
315         h_win = nvmap_get_handle_id(tegra_fb->user_nvmap, attr->buff_id);
316         if (h_win == NULL) {
317                 dev_err(&tegra_fb->ndev->dev, "%s: flip invalid "
318                         "handle %08x\n", current->comm, attr->buff_id);
319                 return -EPERM;
320         }
321
322         /* duplicate the new framebuffer's handle into the fb driver's
323          * nvmap context, to ensure that the handle won't be freed as
324          * long as it is in-use by the fb driver */
325         r_dupe = nvmap_duplicate_handle_id(tegra_fb->fb_nvmap, attr->buff_id);
326         nvmap_handle_put(h_win);
327
328         if (IS_ERR(r_dupe)) {
329                 dev_err(&tegra_fb->ndev->dev, "couldn't duplicate handle\n");
330                 return PTR_ERR(r_dupe);
331         }
332
333         win->surface = r_dupe;
334
335         win->flags = TEGRA_WIN_FLAG_ENABLED;
336         if (attr->blend == TEGRA_FB_WIN_BLEND_PREMULT)
337                 win->flags |= TEGRA_WIN_FLAG_BLEND_PREMULT;
338         else if (attr->blend == TEGRA_FB_WIN_BLEND_COVERAGE)
339                 win->flags |= TEGRA_WIN_FLAG_BLEND_COVERAGE;
340         win->fmt = attr->pixformat;
341         win->x = attr->x;
342         win->y = attr->y;
343         win->w = attr->w;
344         win->h = attr->h;
345         win->out_x = attr->out_x;
346         win->out_y = attr->out_y;
347         win->out_w = attr->out_w;
348         win->out_h = attr->out_h;
349         win->z = attr->z;
350
351         win->phys_addr = nvmap_pin(tegra_fb->fb_nvmap, r_dupe);
352         if (IS_ERR((void *)win->phys_addr)) {
353                 dev_err(&tegra_fb->ndev->dev, "couldn't pin handle\n");
354                 nvmap_free(tegra_fb->fb_nvmap, r_dupe);
355                 return (int)win->phys_addr;
356         }
357         /* STOPSHIP verify that this won't read outside of the surface */
358         win->phys_addr += attr->offset;
359         win->stride = attr->stride;
360
361         return 0;
362 }
363
364 static void tegra_fb_flip_work(struct work_struct *work)
365 {
366         struct tegra_fb_flip_data *data;
367         struct tegra_dc_win *wins[TEGRA_FB_FLIP_N_WINDOWS];
368         struct nvmap_handle_ref *surfs[TEGRA_FB_FLIP_N_WINDOWS];
369         int i, nr_win = 0, nr_unpin = 0;
370
371         data = container_of(work, struct tegra_fb_flip_data, work);
372
373         for (i = 0; i < TEGRA_FB_FLIP_N_WINDOWS; i++) {
374                 struct tegra_fb_flip_win *flip_win = &data->windows[i];
375
376                 if (!flip_win->dc_win)
377                         continue;
378
379                 if (flip_win->dc_win->flags && flip_win->dc_win->surface)
380                         surfs[nr_unpin++] = flip_win->dc_win->surface;
381
382                 wins[nr_win++] = flip_win->dc_win;
383
384                 flip_win->dc_win->flags = flip_win->win_data.flags;
385                 if (!flip_win->dc_win->flags)
386                         continue;
387
388                 flip_win->dc_win->surface = flip_win->win_data.surface;
389                 flip_win->dc_win->fmt = flip_win->win_data.fmt;
390                 flip_win->dc_win->x = flip_win->win_data.x;
391                 flip_win->dc_win->y = flip_win->win_data.y;
392                 flip_win->dc_win->w = flip_win->win_data.w;
393                 flip_win->dc_win->h = flip_win->win_data.h;
394                 flip_win->dc_win->out_x = flip_win->win_data.out_x;
395                 flip_win->dc_win->out_y = flip_win->win_data.out_y;
396                 flip_win->dc_win->out_w = flip_win->win_data.out_w;
397                 flip_win->dc_win->out_h = flip_win->win_data.out_h;
398                 flip_win->dc_win->z = flip_win->win_data.z;
399                 flip_win->dc_win->phys_addr = flip_win->win_data.phys_addr;
400                 flip_win->dc_win->stride = flip_win->win_data.stride;
401
402                 if (flip_win->pre_syncpt_id < 0)
403                         continue;
404
405                 nvhost_syncpt_wait_timeout(&data->fb->ndev->host->syncpt,
406                                            flip_win->pre_syncpt_id,
407                                            flip_win->pre_syncpt_val,
408                                            msecs_to_jiffies(500));
409         }
410
411         if (!nr_win)
412                 goto free_data;
413
414         tegra_dc_update_windows(wins, nr_win);
415         /* TODO: implement swapinterval here */
416         tegra_dc_sync_windows(wins, nr_win);
417
418         tegra_dc_incr_syncpt_min(data->fb->win->dc, data->syncpt_max);
419
420         /* unpin and deref previous front buffers */
421         for (i = 0; i < nr_unpin; i++) {
422                 nvmap_unpin(data->fb->fb_nvmap, surfs[i]);
423                 nvmap_free(data->fb->fb_nvmap, surfs[i]);
424         }
425
426 free_data:
427         kfree(data);
428 }
429
430 static int tegra_fb_flip(struct tegra_fb_info *tegra_fb,
431                          struct tegra_fb_flip_args *args)
432 {
433         struct tegra_fb_flip_data *data;
434         struct tegra_fb_flip_win *flip_win;
435         struct tegra_dc *dc = tegra_fb->win->dc;
436         u32 syncpt_max;
437         int i, err;
438
439         if (WARN_ON(!tegra_fb->user_nvmap))
440                 return -EFAULT;
441
442         if (WARN_ON(!tegra_fb->ndev))
443                 return -EFAULT;
444
445         data = kmalloc(sizeof(*data), GFP_KERNEL);
446         if (!data) {
447                 dev_err(&tegra_fb->ndev->dev, "no memory for flip\n");
448                 return -ENOMEM;
449         }
450
451         INIT_WORK(&data->work, tegra_fb_flip_work);
452         data->fb = tegra_fb;
453
454         for (i = 0; i < TEGRA_FB_FLIP_N_WINDOWS; i++) {
455
456                 flip_win = &data->windows[i];
457                 flip_win->dc_win = tegra_dc_get_window(dc, args->win[i].index);
458                 flip_win->pre_syncpt_id = args->win[i].pre_syncpt_id;
459                 flip_win->pre_syncpt_val = args->win[i].pre_syncpt_val;
460
461                 if (!flip_win->dc_win)
462                         continue;
463
464                 err = tegra_fb_set_windowattr(tegra_fb, &flip_win->win_data,
465                                               &args->win[i]);
466                 if (err) {
467                         dev_err(&tegra_fb->ndev->dev, "error setting window "
468                                 "attributes\n");
469                         goto surf_err;
470                 }
471         }
472
473         syncpt_max = tegra_dc_incr_syncpt_max(dc);
474         data->syncpt_max = syncpt_max;
475
476         queue_work(tegra_fb->flip_wq, &data->work);
477
478         args->post_syncpt_val = syncpt_max;
479         args->post_syncpt_id = tegra_dc_get_syncpt_id(dc);
480
481         return 0;
482
483 surf_err:
484         while (i--) {
485                 if (data->windows[i].win_data.surface) {
486                         nvmap_unpin(tegra_fb->fb_nvmap,
487                                     data->windows[i].win_data.surface);
488                         nvmap_free(tegra_fb->fb_nvmap,
489                                    data->windows[i].win_data.surface);
490                 }
491         }
492         kfree(data);
493         return err;
494 }
495
496 /* TODO: implement private window ioctls to set overlay x,y */
497
498 static int tegra_fb_ioctl(struct fb_info *info, unsigned int cmd, unsigned long arg)
499 {
500         struct tegra_fb_info *tegra_fb = info->par;
501         struct tegra_fb_flip_args flip_args;
502         int fd;
503         int ret;
504
505         switch (cmd) {
506         case FBIO_TEGRA_SET_NVMAP_FD:
507                 if (copy_from_user(&fd, (void __user *)arg, sizeof(fd)))
508                         return -EFAULT;
509
510                 return tegra_fb_set_nvmap_fd(tegra_fb, fd);
511
512         case FBIO_TEGRA_FLIP:
513                 if (copy_from_user(&flip_args, (void __user *)arg, sizeof(flip_args)))
514                         return -EFAULT;
515
516                 ret = tegra_fb_flip(tegra_fb, &flip_args);
517
518                 if (copy_to_user((void __user *)arg, &flip_args, sizeof(flip_args)))
519                         return -EFAULT;
520
521                 return ret;
522
523         default:
524                 return -ENOTTY;
525         }
526
527         return 0;
528 }
529
530 static struct fb_ops tegra_fb_ops = {
531         .owner = THIS_MODULE,
532         .fb_open = tegra_fb_open,
533         .fb_release = tegra_fb_release,
534         .fb_check_var = tegra_fb_check_var,
535         .fb_set_par = tegra_fb_set_par,
536         .fb_setcolreg = tegra_fb_setcolreg,
537         .fb_blank = tegra_fb_blank,
538         .fb_pan_display = tegra_fb_pan_display,
539         .fb_fillrect = tegra_fb_fillrect,
540         .fb_copyarea = tegra_fb_copyarea,
541         .fb_imageblit = tegra_fb_imageblit,
542         .fb_ioctl = tegra_fb_ioctl,
543 };
544
545 void tegra_fb_update_monspecs(struct tegra_fb_info *fb_info,
546                               struct fb_monspecs *specs,
547                               bool (*mode_filter)(struct fb_videomode *mode))
548 {
549         struct fb_event event;
550         int i;
551
552         mutex_lock(&fb_info->info->lock);
553         fb_destroy_modedb(fb_info->info->monspecs.modedb);
554
555         memcpy(&fb_info->info->monspecs, specs,
556                sizeof(fb_info->info->monspecs));
557
558         fb_destroy_modelist(&fb_info->info->modelist);
559
560         for (i = 0; i < specs->modedb_len; i++) {
561                 if (mode_filter) {
562                         if (mode_filter(&specs->modedb[i]))
563                                 fb_add_videomode(&specs->modedb[i],
564                                                  &fb_info->info->modelist);
565                 } else {
566                         fb_add_videomode(&specs->modedb[i],
567                                          &fb_info->info->modelist);
568                 }
569         }
570
571         fb_info->info->mode = (struct fb_videomode *)
572                 fb_find_best_display(specs, &fb_info->info->modelist);
573
574         event.info = fb_info->info;
575         fb_notifier_call_chain(FB_EVENT_NEW_MODELIST, &event);
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
592         win = tegra_dc_get_window(dc, fb_data->win);
593         if (!win) {
594                 dev_err(&ndev->dev, "dc does not have a window at index %d\n",
595                         fb_data->win);
596                 return ERR_PTR(-ENOENT);
597         }
598
599         info = framebuffer_alloc(sizeof(struct tegra_fb_info), &ndev->dev);
600         if (!info) {
601                 ret = -ENOMEM;
602                 goto err;
603         }
604
605         tegra_fb = info->par;
606         tegra_fb->win = win;
607         tegra_fb->ndev = ndev;
608         tegra_fb->fb_mem = fb_mem;
609         tegra_fb->xres = fb_data->xres;
610         tegra_fb->yres = fb_data->yres;
611         tegra_fb->fb_nvmap = nvmap_create_client(nvmap_dev);
612         if (!tegra_fb->fb_nvmap) {
613                 dev_err(&ndev->dev, "couldn't create nvmap client\n");
614                 ret = -ENOMEM;
615                 goto err_free;
616         }
617         atomic_set(&tegra_fb->in_use, 0);
618
619         tegra_fb->flip_wq = create_singlethread_workqueue(dev_name(&ndev->dev));
620         if (!tegra_fb->flip_wq) {
621                 dev_err(&ndev->dev, "couldn't create flip work-queue\n");
622                 ret = -ENOMEM;
623                 goto err_delete_wq;
624         }
625
626         if (fb_mem) {
627                 fb_size = resource_size(fb_mem);
628                 fb_phys = fb_mem->start;
629                 fb_base = ioremap_nocache(fb_phys, fb_size);
630                 if (!fb_base) {
631                         dev_err(&ndev->dev, "fb can't be mapped\n");
632                         ret = -EBUSY;
633                         goto err_put_client;
634                 }
635                 tegra_fb->valid = true;
636         }
637
638         info->fbops = &tegra_fb_ops;
639         info->pseudo_palette = pseudo_palette;
640         info->screen_base = fb_base;
641         info->screen_size = fb_size;
642
643         strlcpy(info->fix.id, "tegra_fb", sizeof(info->fix.id));
644         info->fix.type          = FB_TYPE_PACKED_PIXELS;
645         info->fix.visual        = FB_VISUAL_TRUECOLOR;
646         info->fix.xpanstep      = 1;
647         info->fix.ypanstep      = 1;
648         info->fix.accel         = FB_ACCEL_NONE;
649         info->fix.smem_start    = fb_phys;
650         info->fix.smem_len      = fb_size;
651
652         info->var.xres                  = fb_data->xres;
653         info->var.yres                  = fb_data->yres;
654         info->var.xres_virtual          = fb_data->xres;
655         info->var.yres_virtual          = fb_data->yres * 2;
656         info->var.bits_per_pixel        = fb_data->bits_per_pixel;
657         info->var.activate              = FB_ACTIVATE_VBL;
658         /* TODO: fill in the following by querying the DC */
659         info->var.height                = -1;
660         info->var.width                 = -1;
661         info->var.pixclock              = 0;
662         info->var.left_margin           = 0;
663         info->var.right_margin          = 0;
664         info->var.upper_margin          = 0;
665         info->var.lower_margin          = 0;
666         info->var.hsync_len             = 0;
667         info->var.vsync_len             = 0;
668         info->var.vmode                 = FB_VMODE_NONINTERLACED;
669
670         win->x = 0;
671         win->y = 0;
672         win->w = fb_data->xres;
673         win->h = fb_data->yres;
674         /* TODO: set to output res dc */
675         win->out_x = 0;
676         win->out_y = 0;
677         win->out_w = fb_data->xres;
678         win->out_h = fb_data->yres;
679         win->z = 0;
680         win->phys_addr = fb_phys;
681         win->virt_addr = fb_base;
682         win->layout = TEGRA_WIN_LAYOUT_PITCH;
683         win->stride = tegra_dc_compute_stride(fb_data->xres,
684                                       fb_data->bits_per_pixel, win->layout);
685         win->flags = TEGRA_WIN_FLAG_ENABLED;
686
687         if (fb_mem)
688                 tegra_fb_set_par(info);
689
690         if (register_framebuffer(info)) {
691                 dev_err(&ndev->dev, "failed to register framebuffer\n");
692                 ret = -ENODEV;
693                 goto err_iounmap_fb;
694         }
695
696         tegra_fb->info = info;
697
698         dev_info(&ndev->dev, "probed\n");
699
700         return tegra_fb;
701
702 err_iounmap_fb:
703         iounmap(fb_base);
704 err_put_client:
705         nvmap_client_put(tegra_fb->fb_nvmap);
706 err_delete_wq:
707         destroy_workqueue(tegra_fb->flip_wq);
708 err_free:
709         framebuffer_release(info);
710 err:
711         return ERR_PTR(ret);
712 }
713
714 void tegra_fb_unregister(struct tegra_fb_info *fb_info)
715 {
716         struct fb_info *info = fb_info->info;
717
718         if (fb_info->win->surface) {
719                 nvmap_unpin(fb_info->fb_nvmap, fb_info->win->surface);
720                 nvmap_free(fb_info->fb_nvmap, fb_info->win->surface);
721         }
722
723         if (fb_info->fb_nvmap)
724                 nvmap_client_put(fb_info->fb_nvmap);
725
726         unregister_framebuffer(info);
727
728         flush_workqueue(fb_info->flip_wq);
729         destroy_workqueue(fb_info->flip_wq);
730
731         iounmap(info->screen_base);
732         framebuffer_release(info);
733 }