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