summaryrefslogtreecommitdiff
path: root/drivers/video
diff options
context:
space:
mode:
authorShashank Sharma <shashanks@nvidia.com>2012-08-03 20:50:41 +0530
committerMatthew Pedro <mapedro@nvidia.com>2012-08-07 10:22:04 -0700
commitec7552ef36c0d0979cb31db6d2d4bd54e7f1c9fb (patch)
treed76334925f57d4b93b5378c0e0a484577559b308 /drivers/video
parentabb6a85f077757e5d30f9507406fc533ea4954ee (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.c100
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);
}