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