diff options
author | Jon Mayo <jmayo@nvidia.com> | 2012-03-16 12:50:59 -0700 |
---|---|---|
committer | Simone Willett <swillett@nvidia.com> | 2012-04-11 15:00:58 -0700 |
commit | 44c7bd82653b68c04fef5db9650dc739f7b76eee (patch) | |
tree | 6f02e55a9086f6a50c77063c6c97f79c32afaab6 /drivers/video | |
parent | ca4fdaf787a68ff8eb1a31999725a80c2b4f084f (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.c | 84 | ||||
-rw-r--r-- | drivers/video/tegra/dc/dc_priv.h | 4 | ||||
-rw-r--r-- | drivers/video/tegra/fb.c | 20 |
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); |