summaryrefslogtreecommitdiff
path: root/drivers/video
diff options
context:
space:
mode:
authorJon Mayo <jmayo@nvidia.com>2012-03-16 12:50:59 -0700
committerSimone Willett <swillett@nvidia.com>2012-04-11 15:00:58 -0700
commit44c7bd82653b68c04fef5db9650dc739f7b76eee (patch)
tree6f02e55a9086f6a50c77063c6c97f79c32afaab6 /drivers/video
parentca4fdaf787a68ff8eb1a31999725a80c2b4f084f (diff)
video: tegra: dc: load video mode during vblank
Handle mode set for FBIOPUT_VSCREENINFO at the end of a frame (during vblank). This elimiates the work around that requires disabling then enabling display to change modes. Adds a spinlock to protect irq code from updates to tegra_dc_mode structure. Bug 560152 Change-Id: I5d2175f01a177a32d685b46e5af4f78efeec0786 Signed-off-by: Jon Mayo <jmayo@nvidia.com> Reviewed-on: http://git-master/r/90688 Reviewed-by: Simone Willett <swillett@nvidia.com> Tested-by: Simone Willett <swillett@nvidia.com>
Diffstat (limited to 'drivers/video')
-rw-r--r--drivers/video/tegra/dc/dc.c84
-rw-r--r--drivers/video/tegra/dc/dc_priv.h4
-rw-r--r--drivers/video/tegra/fb.c20
3 files changed, 96 insertions, 12 deletions
diff --git a/drivers/video/tegra/dc/dc.c b/drivers/video/tegra/dc/dc.c
index 20f80f940650..51af1e008e8b 100644
--- a/drivers/video/tegra/dc/dc.c
+++ b/drivers/video/tegra/dc/dc.c
@@ -23,6 +23,7 @@
#include <linux/errno.h>
#include <linux/interrupt.h>
#include <linux/slab.h>
+#include <linux/spinlock.h>
#include <linux/io.h>
#include <linux/clk.h>
#include <linux/mutex.h>
@@ -63,6 +64,8 @@
#define ALL_UF_INT (0)
#endif
+static int calc_refresh(const struct tegra_dc_mode *m);
+
static int no_vsync;
static void _tegra_dc_controller_disable(struct tegra_dc *dc);
@@ -1611,7 +1614,6 @@ static bool check_ref_to_sync(struct tegra_dc_mode *mode)
return true;
}
-#ifdef DEBUG
/* return in 1000ths of a Hertz */
static int calc_refresh(const struct tegra_dc_mode *m)
{
@@ -1626,6 +1628,7 @@ static int calc_refresh(const struct tegra_dc_mode *m)
return refresh;
}
+#ifdef DEBUG
static void print_mode(struct tegra_dc *dc,
const struct tegra_dc_mode *mode, const char *note)
{
@@ -1736,7 +1739,12 @@ static int tegra_dc_program_mode(struct tegra_dc *dc, struct tegra_dc_mode *mode
int tegra_dc_set_mode(struct tegra_dc *dc, const struct tegra_dc_mode *mode)
{
+ unsigned long flags;
+
+ spin_lock_irqsave(&dc->mode_lock, flags);
memcpy(&dc->mode, mode, sizeof(dc->mode));
+ dc->mode_dirty = true;
+ spin_unlock_irqrestore(&dc->mode_lock, flags);
print_mode(dc, mode, __func__);
@@ -1744,6 +1752,46 @@ int tegra_dc_set_mode(struct tegra_dc *dc, const struct tegra_dc_mode *mode)
}
EXPORT_SYMBOL(tegra_dc_set_mode);
+/* convert tegra_dc_mode to fb_videomode
+ * return non-zero on error. */
+int tegra_dc_to_fb_videomode(struct fb_videomode *fbmode,
+ const struct tegra_dc_mode *mode)
+{
+ if (!fbmode || !mode || !mode->pclk)
+ return -EINVAL;
+
+ memset(fbmode, 0, sizeof(*fbmode));
+
+ if (mode->rated_pclk >= 1000)
+ fbmode->pixclock = KHZ2PICOS(mode->rated_pclk / 1000);
+ else if (mode->pclk >= 1000)
+ fbmode->pixclock = KHZ2PICOS(mode->pclk / 1000);
+ fbmode->hsync_len = mode->h_sync_width;
+ fbmode->vsync_len = mode->v_sync_width;
+ fbmode->left_margin = mode->h_back_porch;
+ fbmode->upper_margin = mode->v_back_porch;
+ fbmode->xres = mode->h_active;
+ fbmode->yres = mode->v_active;
+ fbmode->right_margin = mode->h_front_porch;
+ fbmode->lower_margin = mode->v_front_porch;
+ fbmode->vmode = FB_VMODE_NONINTERLACED;
+ fbmode->vmode |= mode->stereo_mode ?
+#ifndef CONFIG_TEGRA_HDMI_74MHZ_LIMIT
+ FB_VMODE_STEREO_FRAME_PACK
+#else
+ FB_VMODE_STEREO_LEFT_RIGHT
+#endif
+ : 0;
+ fbmode->sync |= (mode->flags & TEGRA_DC_MODE_FLAG_NEG_H_SYNC) ?
+ 0 : FB_SYNC_HOR_HIGH_ACT;
+ fbmode->sync |= (mode->flags & TEGRA_DC_MODE_FLAG_NEG_V_SYNC) ?
+ 0 : FB_SYNC_VERT_HIGH_ACT;
+ fbmode->refresh = (calc_refresh(mode) + 500) / 1000;
+
+ return 0;
+}
+EXPORT_SYMBOL(tegra_dc_to_fb_videomode);
+
int tegra_dc_set_fb_mode(struct tegra_dc *dc,
const struct fb_videomode *fbmode, bool stereo_mode)
{
@@ -2132,6 +2180,18 @@ static bool tegra_dc_windows_are_dirty(struct tegra_dc *dc)
return false;
}
+static void tegra_dc_update_mode(struct tegra_dc *dc)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&dc->mode_lock, flags);
+ if (dc->mode_dirty) {
+ tegra_dc_program_mode(dc, &dc->mode);
+ dc->mode_dirty = false;
+ }
+ spin_unlock_irqrestore(&dc->mode_lock, flags);
+}
+
static void tegra_dc_trigger_windows(struct tegra_dc *dc)
{
u32 val, i;
@@ -2191,6 +2251,9 @@ static void tegra_dc_one_shot_irq(struct tegra_dc *dc, unsigned long status)
/* Mark the frame_end as complete. */
if (!completion_done(&dc->frame_end_complete))
complete(&dc->frame_end_complete);
+
+ /* handle a mode change, if it was scheduled */
+ tegra_dc_update_mode(dc);
}
}
@@ -2215,6 +2278,8 @@ static void tegra_dc_continuous_irq(struct tegra_dc *dc, unsigned long status)
if (!completion_done(&dc->frame_end_complete))
complete(&dc->frame_end_complete);
+ /* handle a mode change, if it was scheduled */
+ tegra_dc_update_mode(dc);
tegra_dc_trigger_windows(dc);
}
}
@@ -2867,6 +2932,7 @@ static int tegra_dc_probe(struct nvhost_device *ndev)
mutex_init(&dc->lock);
mutex_init(&dc->one_shot_lock);
+ spin_lock_init(&dc->mode_lock);
init_completion(&dc->frame_end_complete);
init_waitqueue_head(&dc->wq);
#ifdef CONFIG_ARCH_TEGRA_2x_SOC
@@ -2953,6 +3019,22 @@ static int tegra_dc_probe(struct nvhost_device *ndev)
dc->fb = NULL;
}
+ if (dc->out && dc->out->n_modes) {
+ struct fb_monspecs specs;
+ int i;
+
+ memset(&specs, 0, sizeof(specs));
+ specs.max_x = dc->mode.h_active * 1000;
+ specs.max_y = dc->mode.v_active * 1000;
+ specs.modedb_len = dc->out->n_modes;
+ specs.modedb = kzalloc(specs.modedb_len *
+ sizeof(struct fb_videomode), GFP_KERNEL);
+ for (i = 0; i < dc->out->n_modes; i++)
+ tegra_dc_to_fb_videomode(&specs.modedb[i],
+ &dc->out->modes[i]);
+ tegra_fb_update_monspecs(dc->fb, &specs, NULL);
+ }
+
if (dc->out && dc->out->hotplug_init)
dc->out->hotplug_init();
diff --git a/drivers/video/tegra/dc/dc_priv.h b/drivers/video/tegra/dc/dc_priv.h
index a10e648debc9..5cba88f9e806 100644
--- a/drivers/video/tegra/dc/dc_priv.h
+++ b/drivers/video/tegra/dc/dc_priv.h
@@ -4,6 +4,8 @@
* Copyright (C) 2010 Google, Inc.
* Author: Erik Gilling <konkers@android.com>
*
+ * Copyright (C) 2010-2012 NVIDIA Corporation
+ *
* 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.
@@ -93,6 +95,8 @@ struct tegra_dc {
void *out_data;
struct tegra_dc_mode mode;
+ bool mode_dirty;
+ spinlock_t mode_lock;
struct tegra_dc_win windows[DC_N_WINDOWS];
struct tegra_dc_blend blend;
diff --git a/drivers/video/tegra/fb.c b/drivers/video/tegra/fb.c
index 7bc3ab06c47f..ac4c84ba9a1c 100644
--- a/drivers/video/tegra/fb.c
+++ b/drivers/video/tegra/fb.c
@@ -6,7 +6,7 @@
* Colin Cross <ccross@android.com>
* Travis Geiselbrecht <travis@palm.com>
*
- * Copyright (C) 2010-2011 NVIDIA Corporation
+ * Copyright (C) 2010-2012 NVIDIA Corporation
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
@@ -79,6 +79,10 @@ 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;
+ BUG_ON(info == NULL);
+ if (!info)
+ return -EINVAL;
+
if (var->bits_per_pixel) {
/* we only support RGB ordering for now */
switch (var->bits_per_pixel) {
@@ -484,6 +488,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;
+ struct fb_videomode m;
win = tegra_dc_get_window(dc, fb_data->win);
if (!win) {
@@ -535,22 +540,15 @@ struct tegra_fb_info *tegra_fb_register(struct nvhost_device *ndev,
info->fix.line_length = round_up(info->fix.line_length,
TEGRA_LINEAR_PITCH_ALIGNMENT);
- info->var.xres = fb_data->xres;
- info->var.yres = fb_data->yres;
+ INIT_LIST_HEAD(&info->modelist);
+ tegra_dc_to_fb_videomode(&m, &dc->mode);
+ fb_videomode_to_var(&info->var, &m);
info->var.xres_virtual = fb_data->xres;
info->var.yres_virtual = fb_data->yres * 2;
info->var.bits_per_pixel = fb_data->bits_per_pixel;
info->var.activate = FB_ACTIVATE_VBL;
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;
- info->var.upper_margin = 0;
- info->var.lower_margin = 0;
- info->var.hsync_len = 0;
- info->var.vsync_len = 0;
- info->var.vmode = FB_VMODE_NONINTERLACED;
win->x.full = dfixed_const(0);
win->y.full = dfixed_const(0);