From bd357c7068cae05922252db989acc511cef86111 Mon Sep 17 00:00:00 2001 From: Shashank Sharma Date: Fri, 3 Aug 2012 20:50:41 +0530 Subject: 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 Reviewed-on: http://git-master/r/120932 (cherry picked from commit ec7552ef36c0d0979cb31db6d2d4bd54e7f1c9fb) Reviewed-on: http://git-master/r/140031 Reviewed-by: Automatic_Commit_Validation_User GVS: Gerrit_Virtual_Submit Reviewed-by: Matthew Pedro Tested-by: Matthew Pedro Reviewed-by: Winnie Hsu --- drivers/video/tegra/fb.c | 100 ++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 95 insertions(+), 5 deletions(-) (limited to 'drivers/video/tegra') diff --git a/drivers/video/tegra/fb.c b/drivers/video/tegra/fb.c index 69b30884e4df..da7b9d157402 100644 --- a/drivers/video/tegra/fb.c +++ b/drivers/video/tegra/fb.c @@ -38,6 +38,8 @@ #include #include #include +#include + #include "host/dev.h" #include "nvmap/nvmap.h" @@ -158,8 +160,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); @@ -437,13 +441,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); @@ -470,19 +523,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); } -- cgit v1.2.3