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/tegra/dc | |
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/tegra/dc')
-rw-r--r-- | drivers/video/tegra/dc/dc.c | 84 | ||||
-rw-r--r-- | drivers/video/tegra/dc/dc_priv.h | 4 |
2 files changed, 87 insertions, 1 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; |