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