video: tegra: fb: Configure dc from fb
[linux-2.6.git] / drivers / video / tegra / fb.c
index 1656a6e..da7b9d1 100644 (file)
@@ -6,6 +6,8 @@
  *         Colin Cross <ccross@android.com>
  *         Travis Geiselbrecht <travis@palm.com>
  *
+ * Copyright (c) 2010-2012, NVIDIA CORPORATION, All rights reserved.
+ *
  * This software is licensed under the terms of the GNU General Public
  * License version 2, as published by the Free Software Foundation, and
  * may be copied, distributed, and modified under those terms.
 
 #include <mach/dc.h>
 #include <mach/fb.h>
-#include <mach/nvhost.h>
-#include <mach/nvmap.h>
+#include <linux/nvhost.h>
+#include <linux/nvmap.h>
+#include <linux/console.h>
+
 
 #include "host/dev.h"
 #include "nvmap/nvmap.h"
+#include "dc/dc_priv.h"
+
+/* Pad pitch to 16-byte boundary. */
+#define TEGRA_LINEAR_PITCH_ALIGNMENT 32
 
 struct tegra_fb_info {
        struct tegra_dc_win     *win;
@@ -50,67 +58,35 @@ struct tegra_fb_info {
 
        int                     xres;
        int                     yres;
-
-       atomic_t                in_use;
-       struct nvmap_client     *user_nvmap;
-       struct nvmap_client     *fb_nvmap;
-
-       struct workqueue_struct *flip_wq;
-};
-
-struct tegra_fb_flip_win {
-       struct tegra_dc_win     win_data;
-       struct tegra_dc_win     *dc_win;
-       s32                     pre_syncpt_id;
-       u32                     pre_syncpt_val;
-};
-
-struct tegra_fb_flip_data {
-       struct work_struct      work;
-       struct tegra_fb_info    *fb;
-       struct tegra_fb_flip_win windows[TEGRA_FB_FLIP_N_WINDOWS];
-       u32                     syncpt_max;
 };
 
 /* palette array used by the fbcon */
 static u32 pseudo_palette[16];
 
-static int tegra_fb_open(struct fb_info *info, int user)
+static int tegra_fb_check_var(struct fb_var_screeninfo *var,
+                             struct fb_info *info)
 {
        struct tegra_fb_info *tegra_fb = info->par;
+       struct tegra_dc *dc = tegra_fb->win->dc;
+       struct tegra_dc_out_ops *ops = dc->out_ops;
+       struct fb_videomode mode;
 
-       if (atomic_xchg(&tegra_fb->in_use, 1))
-               return -EBUSY;
-
-       tegra_fb->user_nvmap = NULL;
-
-       return 0;
-}
+       if ((var->yres * var->xres * var->bits_per_pixel / 8 * 2) >
+           info->screen_size)
+               return -EINVAL;
 
-static int tegra_fb_release(struct fb_info *info, int user)
-{
-       struct tegra_fb_info *tegra_fb = info->par;
+       /* Apply mode filter for HDMI only -LVDS supports only fix mode */
+       if (ops && ops->mode_filter) {
 
-       flush_workqueue(tegra_fb->flip_wq);
+               fb_var_to_videomode(&mode, var);
+               if (!ops->mode_filter(dc, &mode))
+                       return -EINVAL;
 
-       if (tegra_fb->user_nvmap) {
-               nvmap_client_put(tegra_fb->user_nvmap);
-               tegra_fb->user_nvmap = NULL;
+               /* Mode filter may have modified the mode */
+               fb_videomode_to_var(var, &mode);
        }
 
-       WARN_ON(!atomic_xchg(&tegra_fb->in_use, 0));
-
-       return 0;
-}
-
-static int tegra_fb_check_var(struct fb_var_screeninfo *var,
-                             struct fb_info *info)
-{
-       if ((var->yres * var->xres * var->bits_per_pixel / 8 * 2) >
-           info->screen_size)
-               return -EINVAL;
-
-       /* double yres_virtual to allow double buffering through pan_display */
+       /* Double yres_virtual to allow double buffering through pan_display */
        var->yres_virtual = var->yres * 2;
 
        return 0;
@@ -121,67 +97,78 @@ static int tegra_fb_set_par(struct fb_info *info)
        struct tegra_fb_info *tegra_fb = info->par;
        struct fb_var_screeninfo *var = &info->var;
 
-       /* we only support RGB ordering for now */
-       switch (var->bits_per_pixel) {
-       case 32:
-       case 24:
-               var->red.offset = 0;
-               var->red.length = 8;
-               var->green.offset = 8;
-               var->green.length = 8;
-               var->blue.offset = 16;
-               var->blue.length = 8;
-               tegra_fb->win->fmt = TEGRA_WIN_FMT_R8G8B8A8;
-               break;
-       case 16:
-               var->red.offset = 11;
-               var->red.length = 5;
-               var->green.offset = 5;
-               var->green.length = 6;
-               var->blue.offset = 0;
-               var->blue.length = 5;
-               tegra_fb->win->fmt = TEGRA_WIN_FMT_B5G6R5;
-               break;
-
-       case 0:
-               break;
-
-       default:
-               return -EINVAL;
+       if (var->bits_per_pixel) {
+               /* we only support RGB ordering for now */
+               switch (var->bits_per_pixel) {
+               case 32:
+                       var->red.offset = 0;
+                       var->red.length = 8;
+                       var->green.offset = 8;
+                       var->green.length = 8;
+                       var->blue.offset = 16;
+                       var->blue.length = 8;
+                       var->transp.offset = 24;
+                       var->transp.length = 8;
+                       tegra_fb->win->fmt = TEGRA_WIN_FMT_R8G8B8A8;
+                       break;
+               case 16:
+                       var->red.offset = 11;
+                       var->red.length = 5;
+                       var->green.offset = 5;
+                       var->green.length = 6;
+                       var->blue.offset = 0;
+                       var->blue.length = 5;
+                       tegra_fb->win->fmt = TEGRA_WIN_FMT_B5G6R5;
+                       break;
+
+               default:
+                       return -EINVAL;
+               }
+               /* if line_length unset, then pad the stride */
+               if (!info->fix.line_length) {
+                       info->fix.line_length = var->xres * var->bits_per_pixel
+                               / 8;
+                       info->fix.line_length = round_up(info->fix.line_length,
+                                               TEGRA_LINEAR_PITCH_ALIGNMENT);
+               }
+               tegra_fb->win->stride = info->fix.line_length;
+               tegra_fb->win->stride_uv = 0;
+               tegra_fb->win->phys_addr_u = 0;
+               tegra_fb->win->phys_addr_v = 0;
        }
 
-       info->fix.line_length = tegra_dc_compute_stride(var->xres,
-                       var->bits_per_pixel, TEGRA_WIN_LAYOUT_PITCH);
-       tegra_fb->win->stride = info->fix.line_length;
-
        if (var->pixclock) {
-               struct tegra_dc_mode mode;
+               bool stereo;
+               struct fb_videomode m;
+
+               fb_var_to_videomode(&m, var);
 
                info->mode = (struct fb_videomode *)
-                       fb_find_best_mode(var, &info->modelist);
+                       fb_find_nearest_mode(&m, &info->modelist);
                if (!info->mode) {
                        dev_warn(&tegra_fb->ndev->dev, "can't match video mode\n");
                        return -EINVAL;
                }
 
-               mode.pclk = PICOS2KHZ(info->mode->pixclock) * 1000;
-               mode.h_ref_to_sync = 1;
-               mode.v_ref_to_sync = 1;
-               mode.h_sync_width = info->mode->hsync_len;
-               mode.v_sync_width = info->mode->vsync_len;
-               mode.h_back_porch = info->mode->left_margin;
-               mode.v_back_porch = info->mode->upper_margin;
-               mode.h_active = info->mode->xres;
-               mode.v_active = info->mode->yres;
-               mode.h_front_porch = info->mode->right_margin;
-               mode.v_front_porch = info->mode->lower_margin;
-
-               tegra_dc_set_mode(tegra_fb->win->dc, &mode);
-
-               tegra_fb->win->w = info->mode->xres;
-               tegra_fb->win->h = info->mode->xres;
+               /*
+                * only enable stereo if the mode supports it and
+                * client requests it
+                */
+               stereo = !!(var->vmode & info->mode->vmode &
+#ifndef CONFIG_TEGRA_HDMI_74MHZ_LIMIT
+                                       FB_VMODE_STEREO_FRAME_PACK);
+#else
+                                       FB_VMODE_STEREO_LEFT_RIGHT);
+#endif
+               tegra_dc_set_fb_mode(tegra_fb->win->dc, info->mode, stereo);
+               /* Reflect the mode change on dc */
+               tegra_dc_disable(tegra_fb->win->dc);
+               tegra_dc_enable(tegra_fb->win->dc);
+
+               tegra_fb->win->w.full = dfixed_const(info->mode->xres);
+               tegra_fb->win->h.full = dfixed_const(info->mode->yres);
                tegra_fb->win->out_w = info->mode->xres;
-               tegra_fb->win->out_h = info->mode->xres;
+               tegra_fb->win->out_h = info->mode->yres;
        }
        return 0;
 }
@@ -198,6 +185,10 @@ static int tegra_fb_setcolreg(unsigned regno, unsigned red, unsigned green,
                if (regno >= 16)
                        return -EINVAL;
 
+               red = (red >> (16 - info->var.red.length));
+               green = (green >> (16 - info->var.green.length));
+               blue = (blue >> (16 - info->var.blue.length));
+
                v = (red << var->red.offset) |
                        (green << var->green.offset) |
                        (blue << var->blue.offset);
@@ -208,6 +199,59 @@ static int tegra_fb_setcolreg(unsigned regno, unsigned red, unsigned green,
        return 0;
 }
 
+
+static int tegra_fb_setcmap(struct fb_cmap *cmap, struct fb_info *info)
+{
+       struct tegra_fb_info *tegra_fb = info->par;
+       struct tegra_dc *dc = tegra_fb->win->dc;
+       int i;
+       u16 *red = cmap->red;
+       u16 *green = cmap->green;
+       u16 *blue = cmap->blue;
+       int start = cmap->start;
+
+       if (((unsigned)start > 255) || ((start + cmap->len) > 256))
+               return -EINVAL;
+
+       if (info->fix.visual == FB_VISUAL_TRUECOLOR ||
+               info->fix.visual == FB_VISUAL_DIRECTCOLOR) {
+               /*
+                * For now we are considering color schemes with
+                * cmap->len <=16 as special case of basic color
+                * scheme to support fbconsole.But for DirectColor
+                * visuals(like the one we actually have, that include
+                * a HW LUT),the way it's intended to work is that the
+                * actual LUT HW is programmed to the intended values,
+                * even for small color maps like those with 16 or fewer
+                * entries. The pseudo_palette is then programmed to the
+                * identity transform.
+                */
+               if (cmap->len <= 16) {
+                       /* Low-color schemes like fbconsole*/
+                       u16 *transp = cmap->transp;
+                       u_int vtransp = 0xffff;
+
+                       for (i = 0; i < cmap->len; i++) {
+                               if (transp)
+                                       vtransp = *transp++;
+                               if (tegra_fb_setcolreg(start++, *red++,
+                                       *green++, *blue++,
+                                       vtransp, info))
+                                               return -EINVAL;
+                       }
+               } else {
+                       /* High-color schemes*/
+                       for (i = 0; i < cmap->len; i++) {
+                               dc->fb_lut.r[start+i] = *red++ >> 8;
+                               dc->fb_lut.g[start+i] = *green++ >> 8;
+                               dc->fb_lut.b[start+i] = *blue++ >> 8;
+                       }
+                       tegra_dc_update_lut(dc, -1, -1);
+               }
+       }
+       return 0;
+}
+
 static int tegra_fb_blank(int blank, struct fb_info *info)
 {
        struct tegra_fb_info *tegra_fb = info->par;
@@ -215,11 +259,19 @@ static int tegra_fb_blank(int blank, struct fb_info *info)
        switch (blank) {
        case FB_BLANK_UNBLANK:
                dev_dbg(&tegra_fb->ndev->dev, "unblank\n");
+               tegra_fb->win->flags = TEGRA_WIN_FLAG_ENABLED;
                tegra_dc_enable(tegra_fb->win->dc);
                return 0;
 
+       case FB_BLANK_NORMAL:
+               dev_dbg(&tegra_fb->ndev->dev, "blank - normal\n");
+               tegra_dc_blank(tegra_fb->win->dc);
+               return 0;
+
+       case FB_BLANK_VSYNC_SUSPEND:
+       case FB_BLANK_HSYNC_SUSPEND:
        case FB_BLANK_POWERDOWN:
-               dev_dbg(&tegra_fb->ndev->dev, "blank\n");
+               dev_dbg(&tegra_fb->ndev->dev, "blank - powerdown\n");
                tegra_dc_disable(tegra_fb->win->dc);
                return 0;
 
@@ -236,25 +288,22 @@ static int tegra_fb_pan_display(struct fb_var_screeninfo *var,
        char __iomem *flush_end;
        u32 addr;
 
-       flush_start = info->screen_base + (var->yoffset * info->fix.line_length);
-       flush_end = flush_start + (var->yres * info->fix.line_length);
-
-       info->var.xoffset = var->xoffset;
-       info->var.yoffset = var->yoffset;
+       if (!tegra_fb->win->cur_handle) {
+               flush_start = info->screen_base + (var->yoffset * info->fix.line_length);
+               flush_end = flush_start + (var->yres * info->fix.line_length);
 
-       addr = info->fix.smem_start + (var->yoffset * info->fix.line_length) +
-               (var->xoffset * (var->bits_per_pixel/8));
+               info->var.xoffset = var->xoffset;
+               info->var.yoffset = var->yoffset;
 
-       tegra_fb->win->phys_addr = addr;
-       /* TODO: update virt_addr */
+               addr = info->fix.smem_start + (var->yoffset * info->fix.line_length) +
+                       (var->xoffset * (var->bits_per_pixel/8));
 
-       tegra_dc_update_windows(&tegra_fb->win, 1);
-       tegra_dc_sync_windows(&tegra_fb->win, 1);
+               tegra_fb->win->phys_addr = addr;
+               tegra_fb->win->flags = TEGRA_WIN_FLAG_ENABLED;
+               tegra_fb->win->virt_addr = info->screen_base;
 
-       if (WARN_ON(tegra_fb->win->surface)) {
-               nvmap_unpin(tegra_fb->fb_nvmap, tegra_fb->win->surface);
-               nvmap_free(tegra_fb->fb_nvmap, tegra_fb->win->surface);
-               tegra_fb->win->surface = NULL;
+               tegra_dc_update_windows(&tegra_fb->win, 1);
+               tegra_dc_sync_windows(&tegra_fb->win, 1);
        }
 
        return 0;
@@ -278,247 +327,64 @@ static void tegra_fb_imageblit(struct fb_info *info,
        cfb_imageblit(info, image);
 }
 
-/* TODO: implement ALLOC, FREE, BLANK ioctls */
-
-static int tegra_fb_set_nvmap_fd(struct tegra_fb_info *tegra_fb, int fd)
-{
-       struct nvmap_client *nvmap = NULL;
-
-       if (fd < 0)
-               return -EINVAL;
-
-       nvmap = nvmap_client_get_file(fd);
-       if (IS_ERR(nvmap))
-               return PTR_ERR(nvmap);
-
-       if (tegra_fb->user_nvmap)
-               nvmap_client_put(tegra_fb->user_nvmap);
-
-       tegra_fb->user_nvmap = nvmap;
-
-       return 0;
-}
-
-static int tegra_fb_set_windowattr(struct tegra_fb_info *tegra_fb,
-                                  struct tegra_dc_win *win,
-                                  const struct tegra_fb_windowattr *attr)
-{
-       struct nvmap_handle_ref *r_dupe;
-       struct nvmap_handle *h_win;
-
-       if (!attr->buff_id) {
-               win->flags = 0;
-               win->surface = NULL;
-               return 0;
-       }
-
-       h_win = nvmap_get_handle_id(tegra_fb->user_nvmap, attr->buff_id);
-       if (h_win == NULL) {
-               dev_err(&tegra_fb->ndev->dev, "%s: flip invalid "
-                       "handle %08x\n", current->comm, attr->buff_id);
-               return -EPERM;
-       }
-
-       /* duplicate the new framebuffer's handle into the fb driver's
-        * nvmap context, to ensure that the handle won't be freed as
-        * long as it is in-use by the fb driver */
-       r_dupe = nvmap_duplicate_handle_id(tegra_fb->fb_nvmap, attr->buff_id);
-       nvmap_handle_put(h_win);
-
-       if (IS_ERR(r_dupe)) {
-               dev_err(&tegra_fb->ndev->dev, "couldn't duplicate handle\n");
-               return PTR_ERR(r_dupe);
-       }
-
-       win->surface = r_dupe;
-
-       win->flags = TEGRA_WIN_FLAG_ENABLED;
-       if (attr->blend == TEGRA_FB_WIN_BLEND_PREMULT)
-               win->flags |= TEGRA_WIN_FLAG_BLEND_PREMULT;
-       else if (attr->blend == TEGRA_FB_WIN_BLEND_COVERAGE)
-               win->flags |= TEGRA_WIN_FLAG_BLEND_COVERAGE;
-       win->fmt = attr->pixformat;
-       win->x = attr->x;
-       win->y = attr->y;
-       win->w = attr->w;
-       win->h = attr->h;
-       win->out_x = attr->out_x;
-       win->out_y = attr->out_y;
-       win->out_w = attr->out_w;
-       win->out_h = attr->out_h;
-       win->z = attr->z;
-
-       win->phys_addr = nvmap_pin(tegra_fb->fb_nvmap, r_dupe);
-       if (IS_ERR((void *)win->phys_addr)) {
-               dev_err(&tegra_fb->ndev->dev, "couldn't pin handle\n");
-               nvmap_free(tegra_fb->fb_nvmap, r_dupe);
-               return (int)win->phys_addr;
-       }
-       /* STOPSHIP verify that this won't read outside of the surface */
-       win->phys_addr += attr->offset;
-       win->stride = attr->stride;
-
-       return 0;
-}
-
-static void tegra_fb_flip_work(struct work_struct *work)
-{
-       struct tegra_fb_flip_data *data;
-       struct tegra_dc_win *wins[TEGRA_FB_FLIP_N_WINDOWS];
-       struct nvmap_handle_ref *surfs[TEGRA_FB_FLIP_N_WINDOWS];
-       int i, nr_win = 0, nr_unpin = 0;
-
-       data = container_of(work, struct tegra_fb_flip_data, work);
-
-       for (i = 0; i < TEGRA_FB_FLIP_N_WINDOWS; i++) {
-               struct tegra_fb_flip_win *flip_win = &data->windows[i];
-
-               if (!flip_win->dc_win)
-                       continue;
-
-               if (flip_win->dc_win->flags && flip_win->dc_win->surface)
-                       surfs[nr_unpin++] = flip_win->dc_win->surface;
-
-               wins[nr_win++] = flip_win->dc_win;
-
-               flip_win->dc_win->flags = flip_win->win_data.flags;
-               if (!flip_win->dc_win->flags)
-                       continue;
-
-               flip_win->dc_win->surface = flip_win->win_data.surface;
-               flip_win->dc_win->fmt = flip_win->win_data.fmt;
-               flip_win->dc_win->x = flip_win->win_data.x;
-               flip_win->dc_win->y = flip_win->win_data.y;
-               flip_win->dc_win->w = flip_win->win_data.w;
-               flip_win->dc_win->h = flip_win->win_data.h;
-               flip_win->dc_win->out_x = flip_win->win_data.out_x;
-               flip_win->dc_win->out_y = flip_win->win_data.out_y;
-               flip_win->dc_win->out_w = flip_win->win_data.out_w;
-               flip_win->dc_win->out_h = flip_win->win_data.out_h;
-               flip_win->dc_win->z = flip_win->win_data.z;
-               flip_win->dc_win->phys_addr = flip_win->win_data.phys_addr;
-               flip_win->dc_win->stride = flip_win->win_data.stride;
-
-               if (flip_win->pre_syncpt_id < 0)
-                       continue;
-
-               nvhost_syncpt_wait_timeout(&data->fb->ndev->host->syncpt,
-                                          flip_win->pre_syncpt_id,
-                                          flip_win->pre_syncpt_val,
-                                          msecs_to_jiffies(500));
-       }
-
-       if (!nr_win)
-               goto free_data;
-
-       tegra_dc_update_windows(wins, nr_win);
-       /* TODO: implement swapinterval here */
-       tegra_dc_sync_windows(wins, nr_win);
-
-       tegra_dc_incr_syncpt_min(data->fb->win->dc, data->syncpt_max);
-
-       /* unpin and deref previous front buffers */
-       for (i = 0; i < nr_unpin; i++) {
-               nvmap_unpin(data->fb->fb_nvmap, surfs[i]);
-               nvmap_free(data->fb->fb_nvmap, surfs[i]);
-       }
-
-free_data:
-       kfree(data);
-}
-
-static int tegra_fb_flip(struct tegra_fb_info *tegra_fb,
-                        struct tegra_fb_flip_args *args)
-{
-       struct tegra_fb_flip_data *data;
-       struct tegra_fb_flip_win *flip_win;
-       struct tegra_dc *dc = tegra_fb->win->dc;
-       u32 syncpt_max;
-       int i, err;
-
-       if (WARN_ON(!tegra_fb->user_nvmap))
-               return -EFAULT;
-
-       if (WARN_ON(!tegra_fb->ndev))
-               return -EFAULT;
-
-       data = kmalloc(sizeof(*data), GFP_KERNEL);
-       if (!data) {
-               dev_err(&tegra_fb->ndev->dev, "no memory for flip\n");
-               return -ENOMEM;
-       }
-
-       INIT_WORK(&data->work, tegra_fb_flip_work);
-       data->fb = tegra_fb;
-
-       for (i = 0; i < TEGRA_FB_FLIP_N_WINDOWS; i++) {
-
-               flip_win = &data->windows[i];
-               flip_win->dc_win = tegra_dc_get_window(dc, args->win[i].index);
-               flip_win->pre_syncpt_id = args->win[i].pre_syncpt_id;
-               flip_win->pre_syncpt_val = args->win[i].pre_syncpt_val;
-
-               if (!flip_win->dc_win)
-                       continue;
-
-               err = tegra_fb_set_windowattr(tegra_fb, &flip_win->win_data,
-                                             &args->win[i]);
-               if (err) {
-                       dev_err(&tegra_fb->ndev->dev, "error setting window "
-                               "attributes\n");
-                       goto surf_err;
-               }
-       }
-
-       syncpt_max = tegra_dc_incr_syncpt_max(dc);
-       data->syncpt_max = syncpt_max;
-
-       queue_work(tegra_fb->flip_wq, &data->work);
-
-       args->post_syncpt_val = syncpt_max;
-       args->post_syncpt_id = tegra_dc_get_syncpt_id(dc);
-
-       return 0;
-
-surf_err:
-       while (i--) {
-               if (data->windows[i].win_data.surface) {
-                       nvmap_unpin(tegra_fb->fb_nvmap,
-                                   data->windows[i].win_data.surface);
-                       nvmap_free(tegra_fb->fb_nvmap,
-                                  data->windows[i].win_data.surface);
-               }
-       }
-       kfree(data);
-       return err;
-}
-
-/* TODO: implement private window ioctls to set overlay x,y */
-
 static int tegra_fb_ioctl(struct fb_info *info, unsigned int cmd, unsigned long arg)
 {
-       struct tegra_fb_info *tegra_fb = info->par;
-       struct tegra_fb_flip_args flip_args;
-       int fd;
-       int ret;
+       struct tegra_fb_info *tegra_fb = (struct tegra_fb_info *)info->par;
+       struct tegra_fb_modedb modedb;
+       struct fb_modelist *modelist;
+       struct fb_vblank vblank = {};
+       int i;
 
        switch (cmd) {
-       case FBIO_TEGRA_SET_NVMAP_FD:
-               if (copy_from_user(&fd, (void __user *)arg, sizeof(fd)))
+       case FBIO_TEGRA_GET_MODEDB:
+               if (copy_from_user(&modedb, (void __user *)arg, sizeof(modedb)))
                        return -EFAULT;
 
-               return tegra_fb_set_nvmap_fd(tegra_fb, fd);
+               i = 0;
+               list_for_each_entry(modelist, &info->modelist, list) {
+                       struct fb_var_screeninfo var;
+
+                       if (i >= modedb.modedb_len)
+                               break;
+
+                       /* fb_videomode_to_var doesn't fill out all the members
+                          of fb_var_screeninfo */
+                       memset(&var, 0x0, sizeof(var));
+
+                       fb_videomode_to_var(&var, &modelist->mode);
+
+                       if (copy_to_user((void __user *)&modedb.modedb[i],
+                                        &var, sizeof(var)))
+                               return -EFAULT;
+                       i++;
+
+                       if (var.vmode & FB_VMODE_STEREO_MASK) {
+                               if (i >= modedb.modedb_len)
+                                       break;
+                               var.vmode &= ~FB_VMODE_STEREO_MASK;
+                               if (copy_to_user(
+                                       (void __user *)&modedb.modedb[i],
+                                        &var, sizeof(var)))
+                                       return -EFAULT;
+                               i++;
+                       }
+               }
+               modedb.modedb_len = i;
 
-       case FBIO_TEGRA_FLIP:
-               if (copy_from_user(&flip_args, (void __user *)arg, sizeof(flip_args)))
+               if (copy_to_user((void __user *)arg, &modedb, sizeof(modedb)))
                        return -EFAULT;
+               break;
 
-               ret = tegra_fb_flip(tegra_fb, &flip_args);
+       case FBIOGET_VBLANK:
+               tegra_dc_get_fbvblank(tegra_fb->win->dc, &vblank);
 
-               if (copy_to_user((void __user *)arg, &flip_args, sizeof(flip_args)))
+               if (copy_to_user(
+                       (void __user *)arg, &vblank, sizeof(vblank)))
                        return -EFAULT;
+               break;
 
-               return ret;
+       case FBIO_WAITFORVSYNC:
+               return tegra_dc_wait_for_vsync(tegra_fb->win->dc);
 
        default:
                return -ENOTTY;
@@ -527,13 +393,46 @@ static int tegra_fb_ioctl(struct fb_info *info, unsigned int cmd, unsigned long
        return 0;
 }
 
+int tegra_fb_get_mode(struct tegra_dc *dc) {
+       return dc->fb->info->mode->refresh;
+}
+
+int tegra_fb_set_mode(struct tegra_dc *dc, int fps) {
+       size_t stereo;
+       struct list_head *pos;
+       struct fb_videomode *best_mode = NULL;
+       int curr_diff = INT_MAX; /* difference of best_mode refresh rate */
+       struct fb_modelist *modelist;
+       struct fb_info *info = dc->fb->info;
+
+       list_for_each(pos, &info->modelist) {
+               struct fb_videomode *mode;
+
+               modelist = list_entry(pos, struct fb_modelist, list);
+               mode = &modelist->mode;
+               if (fps <= mode->refresh && curr_diff > (mode->refresh - fps)) {
+                       curr_diff = mode->refresh - fps;
+                       best_mode = mode;
+               }
+       }
+       if (best_mode) {
+               info->mode = best_mode;
+               stereo = !!(info->var.vmode & info->mode->vmode &
+#ifndef CONFIG_TEGRA_HDMI_74MHZ_LIMIT
+                               FB_VMODE_STEREO_FRAME_PACK);
+#else
+                               FB_VMODE_STEREO_LEFT_RIGHT);
+#endif
+               return tegra_dc_set_fb_mode(dc, best_mode, stereo);
+       }
+       return -EIO;
+}
+
 static struct fb_ops tegra_fb_ops = {
        .owner = THIS_MODULE,
-       .fb_open = tegra_fb_open,
-       .fb_release = tegra_fb_release,
        .fb_check_var = tegra_fb_check_var,
        .fb_set_par = tegra_fb_set_par,
-       .fb_setcolreg = tegra_fb_setcolreg,
+       .fb_setcmap = tegra_fb_setcmap,
        .fb_blank = tegra_fb_blank,
        .fb_pan_display = tegra_fb_pan_display,
        .fb_fillrect = tegra_fb_fillrect,
@@ -542,37 +441,138 @@ static struct fb_ops tegra_fb_ops = {
        .fb_ioctl = tegra_fb_ioctl,
 };
 
+const struct fb_videomode *tegra_fb_find_best_mode(
+       struct fb_var_screeninfo *var,
+       struct list_head *head)
+{
+       struct list_head *pos;
+       struct fb_modelist *modelist;
+       struct fb_videomode *mode, *best = NULL;
+       int diff = 0;
+
+       list_for_each(pos, head) {
+               int d;
+
+               modelist = list_entry(pos, struct fb_modelist, list);
+               mode = &modelist->mode;
+
+               if (mode->xres >= var->xres && mode->yres >= var->yres) {
+                       d = (mode->xres - var->xres) +
+                               (mode->yres - var->yres);
+                       if (diff < d) {
+                               diff = d;
+                               best = mode;
+                       } else if (diff == d && best &&
+                                  mode->refresh > best->refresh)
+                               best = mode;
+               }
+       }
+       return best;
+}
+
+static int tegra_fb_activate_mode(struct tegra_fb_info *fb_info,
+                               struct fb_var_screeninfo *var)
+{
+       int err;
+       struct fb_info *info = fb_info->info;
+
+       console_lock();
+       info->flags |= FBINFO_MISC_USEREVENT;
+       err = fb_set_var(info, var);
+       info->flags &= ~FBINFO_MISC_USEREVENT;
+       console_unlock();
+       if (err)
+               return err;
+       return 0;
+}
+
 void tegra_fb_update_monspecs(struct tegra_fb_info *fb_info,
                              struct fb_monspecs *specs,
-                             bool (*mode_filter)(struct fb_videomode *mode))
+                             bool (*mode_filter)(const struct tegra_dc *dc,
+                                                 struct fb_videomode *mode))
 {
-       struct fb_event event;
        int i;
+       int ret = 0;
+       struct fb_event event;
+       struct fb_info *info = fb_info->info;
+       const struct fb_videomode *best_mode = NULL;
+       struct fb_var_screeninfo var = {0,};
 
        mutex_lock(&fb_info->info->lock);
        fb_destroy_modedb(fb_info->info->monspecs.modedb);
 
+       fb_destroy_modelist(&fb_info->info->modelist);
+
+       if (specs == NULL) {
+               struct tegra_dc_mode mode;
+               memset(&fb_info->info->monspecs, 0x0,
+                      sizeof(fb_info->info->monspecs));
+               memset(&mode, 0x0, sizeof(mode));
+
+               /*
+                * reset video mode properties to prevent garbage being displayed on 'mode' device.
+                */
+               fb_info->info->mode = (struct fb_videomode*) NULL;
+
+               tegra_dc_set_mode(fb_info->win->dc, &mode);
+               mutex_unlock(&fb_info->info->lock);
+               return;
+       }
+
        memcpy(&fb_info->info->monspecs, specs,
               sizeof(fb_info->info->monspecs));
+       fb_info->info->mode = specs->modedb;
 
-       fb_destroy_modelist(&fb_info->info->modelist);
-
+       /* Prepare a mode db */
        for (i = 0; i < specs->modedb_len; i++) {
-               if (mode_filter) {
-                       if (mode_filter(&specs->modedb[i]))
-                               fb_add_videomode(&specs->modedb[i],
+               if (info->fbops->fb_check_var) {
+                       struct fb_videomode m;
+
+                       /* Call mode filter to check mode */
+                       fb_videomode_to_var(&var, &specs->modedb[i]);
+                       if (!(info->fbops->fb_check_var(&var, info))) {
+                               fb_var_to_videomode(&m, &var);
+                               fb_add_videomode(&m,
                                                 &fb_info->info->modelist);
+                       }
                } else {
                        fb_add_videomode(&specs->modedb[i],
                                         &fb_info->info->modelist);
                }
        }
 
-       fb_info->info->mode = (struct fb_videomode *)
-               fb_find_best_display(specs, &fb_info->info->modelist);
+       /* Get the best mode from modedb and apply on fb */
+       var.xres = 0;
+       var.yres = 0;
+       best_mode = tegra_fb_find_best_mode(&var, &info->modelist);
+
+       /* Update framebuffer with best mode */
+       fb_videomode_to_var(&var, best_mode);
+
+       /* TODO: Get proper way of getting rid of a 0 bpp */
+       if (!var.bits_per_pixel)
+               var.bits_per_pixel = 32;
+
+       memcpy(&info->var, &var, sizeof(struct fb_var_screeninfo));
+
+       ret = tegra_fb_activate_mode(fb_info, &var);
+       if (ret)
+               return;
 
        event.info = fb_info->info;
+
+#ifdef CONFIG_FRAMEBUFFER_CONSOLE
+/* Lock the console before sending the noti. Fbconsole
+  * on HDMI might be using console
+  */
+       console_lock();
+#endif
        fb_notifier_call_chain(FB_EVENT_NEW_MODELIST, &event);
+#ifdef CONFIG_FRAMEBUFFER_CONSOLE
+/* Unlock the console */
+       console_unlock();
+#endif
+
        mutex_unlock(&fb_info->info->lock);
 }
 
@@ -588,6 +588,7 @@ struct tegra_fb_info *tegra_fb_register(struct nvhost_device *ndev,
        unsigned long fb_size = 0;
        unsigned long fb_phys = 0;
        int ret = 0;
+       unsigned stride;
 
        win = tegra_dc_get_window(dc, fb_data->win);
        if (!win) {
@@ -608,20 +609,6 @@ struct tegra_fb_info *tegra_fb_register(struct nvhost_device *ndev,
        tegra_fb->fb_mem = fb_mem;
        tegra_fb->xres = fb_data->xres;
        tegra_fb->yres = fb_data->yres;
-       tegra_fb->fb_nvmap = nvmap_create_client(nvmap_dev);
-       if (!tegra_fb->fb_nvmap) {
-               dev_err(&ndev->dev, "couldn't create nvmap client\n");
-               ret = -ENOMEM;
-               goto err_free;
-       }
-       atomic_set(&tegra_fb->in_use, 0);
-
-       tegra_fb->flip_wq = create_singlethread_workqueue(dev_name(&ndev->dev));
-       if (!tegra_fb->flip_wq) {
-               dev_err(&ndev->dev, "couldn't create flip work-queue\n");
-               ret = -ENOMEM;
-               goto err_delete_wq;
-       }
 
        if (fb_mem) {
                fb_size = resource_size(fb_mem);
@@ -630,11 +617,18 @@ struct tegra_fb_info *tegra_fb_register(struct nvhost_device *ndev,
                if (!fb_base) {
                        dev_err(&ndev->dev, "fb can't be mapped\n");
                        ret = -EBUSY;
-                       goto err_put_client;
+                       goto err_free;
                }
                tegra_fb->valid = true;
        }
 
+       info->fix.line_length = fb_data->xres * fb_data->bits_per_pixel / 8;
+
+       stride = tegra_dc_get_stride(dc, 0);
+       if (!stride) /* default to pad the stride */
+               stride = round_up(info->fix.line_length,
+                       TEGRA_LINEAR_PITCH_ALIGNMENT);
+
        info->fbops = &tegra_fb_ops;
        info->pseudo_palette = pseudo_palette;
        info->screen_base = fb_base;
@@ -648,6 +642,7 @@ struct tegra_fb_info *tegra_fb_register(struct nvhost_device *ndev,
        info->fix.accel         = FB_ACCEL_NONE;
        info->fix.smem_start    = fb_phys;
        info->fix.smem_len      = fb_size;
+       info->fix.line_length = stride;
 
        info->var.xres                  = fb_data->xres;
        info->var.yres                  = fb_data->yres;
@@ -655,9 +650,8 @@ struct tegra_fb_info *tegra_fb_register(struct nvhost_device *ndev,
        info->var.yres_virtual          = fb_data->yres * 2;
        info->var.bits_per_pixel        = fb_data->bits_per_pixel;
        info->var.activate              = FB_ACTIVATE_VBL;
-       /* TODO: fill in the following by querying the DC */
-       info->var.height                = -1;
-       info->var.width                 = -1;
+       info->var.height                = tegra_dc_get_out_height(dc);
+       info->var.width                 = tegra_dc_get_out_width(dc);
        info->var.pixclock              = 0;
        info->var.left_margin           = 0;
        info->var.right_margin          = 0;
@@ -667,10 +661,10 @@ struct tegra_fb_info *tegra_fb_register(struct nvhost_device *ndev,
        info->var.vsync_len             = 0;
        info->var.vmode                 = FB_VMODE_NONINTERLACED;
 
-       win->x = 0;
-       win->y = 0;
-       win->w = fb_data->xres;
-       win->h = fb_data->yres;
+       win->x.full = dfixed_const(0);
+       win->y.full = dfixed_const(0);
+       win->w.full = dfixed_const(fb_data->xres);
+       win->h.full = dfixed_const(fb_data->yres);
        /* TODO: set to output res dc */
        win->out_x = 0;
        win->out_y = 0;
@@ -679,9 +673,10 @@ struct tegra_fb_info *tegra_fb_register(struct nvhost_device *ndev,
        win->z = 0;
        win->phys_addr = fb_phys;
        win->virt_addr = fb_base;
-       win->layout = TEGRA_WIN_LAYOUT_PITCH;
-       win->stride = tegra_dc_compute_stride(fb_data->xres,
-                                     fb_data->bits_per_pixel, win->layout);
+       win->phys_addr_u = 0;
+       win->phys_addr_v = 0;
+       win->stride = info->fix.line_length;
+       win->stride_uv = 0;
        win->flags = TEGRA_WIN_FLAG_ENABLED;
 
        if (fb_mem)
@@ -697,14 +692,36 @@ struct tegra_fb_info *tegra_fb_register(struct nvhost_device *ndev,
 
        dev_info(&ndev->dev, "probed\n");
 
+       if (fb_data->flags & TEGRA_FB_FLIP_ON_PROBE) {
+               tegra_dc_update_windows(&tegra_fb->win, 1);
+               tegra_dc_sync_windows(&tegra_fb->win, 1);
+       }
+
+       if (dc->mode.pclk > 1000) {
+               struct tegra_dc_mode *mode = &dc->mode;
+               struct fb_videomode vmode;
+
+               if (dc->out->flags & TEGRA_DC_OUT_ONE_SHOT_MODE)
+                       info->var.pixclock = KHZ2PICOS(mode->rated_pclk / 1000);
+               else
+                       info->var.pixclock = KHZ2PICOS(mode->pclk / 1000);
+               info->var.left_margin = mode->h_back_porch;
+               info->var.right_margin = mode->h_front_porch;
+               info->var.upper_margin = mode->v_back_porch;
+               info->var.lower_margin = mode->v_front_porch;
+               info->var.hsync_len = mode->h_sync_width;
+               info->var.vsync_len = mode->v_sync_width;
+
+               /* Keep info->var consistent with info->modelist. */
+               fb_var_to_videomode(&vmode, &info->var);
+               fb_add_videomode(&vmode, &info->modelist);
+       }
+
        return tegra_fb;
 
 err_iounmap_fb:
-       iounmap(fb_base);
-err_put_client:
-       nvmap_client_put(tegra_fb->fb_nvmap);
-err_delete_wq:
-       destroy_workqueue(tegra_fb->flip_wq);
+       if (fb_base)
+               iounmap(fb_base);
 err_free:
        framebuffer_release(info);
 err:
@@ -715,19 +732,8 @@ void tegra_fb_unregister(struct tegra_fb_info *fb_info)
 {
        struct fb_info *info = fb_info->info;
 
-       if (fb_info->win->surface) {
-               nvmap_unpin(fb_info->fb_nvmap, fb_info->win->surface);
-               nvmap_free(fb_info->fb_nvmap, fb_info->win->surface);
-       }
-
-       if (fb_info->fb_nvmap)
-               nvmap_client_put(fb_info->fb_nvmap);
-
        unregister_framebuffer(info);
 
-       flush_workqueue(fb_info->flip_wq);
-       destroy_workqueue(fb_info->flip_wq);
-
        iounmap(info->screen_base);
        framebuffer_release(info);
 }