diff options
author | Shashank Sharma <shashanks@nvidia.com> | 2012-08-03 20:50:41 +0530 |
---|---|---|
committer | Matthew Pedro <mapedro@nvidia.com> | 2012-08-07 10:22:04 -0700 |
commit | ec7552ef36c0d0979cb31db6d2d4bd54e7f1c9fb (patch) | |
tree | d76334925f57d4b93b5378c0e0a484577559b308 /drivers/video | |
parent | abb6a85f077757e5d30f9507406fc533ea4954ee (diff) |
video: tegra: fb: Configure dc from fb
This patch contains following changes:
1. Modify tegra_fb_update_monspecs to select best mode to apply
on framebuffer, in HDMI hotplug.
2. Add a console_lock to prevent corruption while fbcon running
in multiple threads.
3. Add a tegra_dc_disable/enable in tegra_fb_set_par to reflect
the programmed new mode applied on DC.
Bug: 1005399
Change-Id: I133b38b00f25f767afc05549550fecde8d092fb2
Signed-off-by: Shashank Sharma <shashanks@nvidia.com>
Reviewed-on: http://git-master/r/120932
Reviewed-by: Automatic_Commit_Validation_User
Reviewed-by: Jon Mayo <jmayo@nvidia.com>
Reviewed-by: Kiran Adduri <kadduri@nvidia.com>
Reviewed-by: Winnie Hsu <whsu@nvidia.com>
GVS: Gerrit_Virtual_Submit
Reviewed-by: Matthew Pedro <mapedro@nvidia.com>
Diffstat (limited to 'drivers/video')
-rw-r--r-- | drivers/video/tegra/fb.c | 100 |
1 files changed, 95 insertions, 5 deletions
diff --git a/drivers/video/tegra/fb.c b/drivers/video/tegra/fb.c index f1cd652c3a57..835264d5fbfa 100644 --- a/drivers/video/tegra/fb.c +++ b/drivers/video/tegra/fb.c @@ -38,6 +38,8 @@ #include <mach/fb.h> #include <linux/nvhost.h> #include <linux/nvmap.h> +#include <linux/console.h> + #include "host/dev.h" #include "nvmap/nvmap.h" @@ -155,8 +157,10 @@ static int tegra_fb_set_par(struct fb_info *info) #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); @@ -421,13 +425,62 @@ 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)(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); @@ -454,19 +507,56 @@ void tegra_fb_update_monspecs(struct tegra_fb_info *fb_info, sizeof(fb_info->info->monspecs)); fb_info->info->mode = specs->modedb; + /* Prepare a mode db */ for (i = 0; i < specs->modedb_len; i++) { - if (mode_filter) { - if (mode_filter(fb_info->win->dc, &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); } } + /* 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); } |