diff options
author | Rakesh Iyer <riyer@nvidia.com> | 2012-08-21 18:25:17 -0700 |
---|---|---|
committer | Mrutyunjay Sawant <msawant@nvidia.com> | 2012-09-03 02:44:21 -0700 |
commit | f0a2ca55641520d9a1d86a50e59c202d175c4635 (patch) | |
tree | 6da30942867adc67be1d25c5bd04588a510ab319 | |
parent | f04a298e90fee39e8bcc74cbee0ef4e2645ed6da (diff) |
video: tegra: dc: support for vblank sync
Add wait for vsync support for one-shot panels. The code supports extension
of this feature to other panels.
Bug 1033411.
Change-Id: Ie4d6cb45e5de81083458169ccdfa33230235ed72
Signed-off-by: Rakesh Iyer <riyer@nvidia.com>
Reviewed-on: http://git-master/r/128927
Reviewed-by: Mrutyunjay Sawant <msawant@nvidia.com>
Tested-by: Mrutyunjay Sawant <msawant@nvidia.com>
-rw-r--r-- | arch/arm/mach-tegra/include/mach/dc.h | 5 | ||||
-rw-r--r-- | drivers/video/tegra/dc/dc.c | 46 | ||||
-rw-r--r-- | drivers/video/tegra/dc/dc_sysfs.c | 14 | ||||
-rw-r--r-- | drivers/video/tegra/dc/dsi.c | 66 | ||||
-rw-r--r-- | drivers/video/tegra/fb.c | 13 |
5 files changed, 129 insertions, 15 deletions
diff --git a/arch/arm/mach-tegra/include/mach/dc.h b/arch/arm/mach-tegra/include/mach/dc.h index 3483fe91ce66..d7aaed0bdcdc 100644 --- a/arch/arm/mach-tegra/include/mach/dc.h +++ b/arch/arm/mach-tegra/include/mach/dc.h @@ -24,6 +24,7 @@ #include <linux/pm.h> #include <linux/types.h> +#include <linux/fb.h> #include <drm/drm_fixed.h> #define TEGRA_MAX_DC 2 @@ -368,6 +369,8 @@ struct tegra_dc_out { u8 *out_sel_configs; unsigned n_out_sel_configs; + bool user_needs_vblank; + struct completion user_vblank_comp; int (*enable)(void); int (*postpoweron)(void); @@ -525,6 +528,8 @@ bool tegra_dc_get_connected(struct tegra_dc *); bool tegra_dc_hpd(struct tegra_dc *dc); +void tegra_dc_get_fbvblank(struct tegra_dc *dc, struct fb_vblank *vblank); +int tegra_dc_wait_for_vsync(struct tegra_dc *dc); void tegra_dc_blank(struct tegra_dc *dc); void tegra_dc_enable(struct tegra_dc *dc); diff --git a/drivers/video/tegra/dc/dc.c b/drivers/video/tegra/dc/dc.c index a3011b873aae..80d052a61e12 100644 --- a/drivers/video/tegra/dc/dc.c +++ b/drivers/video/tegra/dc/dc.c @@ -873,6 +873,36 @@ static inline void enable_dc_irq(unsigned int irq) #endif } +void tegra_dc_get_fbvblank(struct tegra_dc *dc, struct fb_vblank *vblank) +{ + if (dc->out->flags & TEGRA_DC_OUT_ONE_SHOT_MODE) + vblank->flags = FB_VBLANK_HAVE_VSYNC; +} + +int tegra_dc_wait_for_vsync(struct tegra_dc *dc) +{ + int ret = -ENOTTY; + + if (!(dc->out->flags & TEGRA_DC_OUT_ONE_SHOT_MODE) || !dc->enabled) + return ret; + + /* + * Logic is as follows + * a) Indicate we need a vblank. + * b) Wait for completion to be signalled from isr. + * c) Initialize completion for next iteration. + */ + + tegra_dc_hold_dc_out(dc); + dc->out->user_needs_vblank = true; + + ret = wait_for_completion_interruptible(&dc->out->user_vblank_comp); + init_completion(&dc->out->user_vblank_comp); + tegra_dc_release_dc_out(dc); + + return ret; +} + static void tegra_dc_vblank(struct work_struct *work) { struct tegra_dc *dc = container_of(work, struct tegra_dc, vblank_work); @@ -1023,6 +1053,13 @@ static void tegra_dc_underflow_handler(struct tegra_dc *dc) #ifndef CONFIG_TEGRA_FPGA_PLATFORM static void tegra_dc_one_shot_irq(struct tegra_dc *dc, unsigned long status) { + /* pending user vblank, so wakeup */ + if ((status & (V_BLANK_INT | MSF_INT)) && + (dc->out->user_needs_vblank)) { + dc->out->user_needs_vblank = false; + complete(&dc->out->user_vblank_comp); + } + if (status & V_BLANK_INT) { /* Sync up windows. */ tegra_dc_trigger_windows(dc); @@ -1232,6 +1269,7 @@ static u32 get_syncpt(struct tegra_dc *dc, int idx) static int tegra_dc_init(struct tegra_dc *dc) { int i; + int int_enable; tegra_dc_writel(dc, 0x00000100, DC_CMD_GENERAL_INCR_SYNCPT_CNTRL); if (dc->ndev->id == 0) { @@ -1267,8 +1305,12 @@ static int tegra_dc_init(struct tegra_dc *dc) tegra_dc_writel(dc, 0x00000000, DC_DISP_DISP_MISC_CONTROL); #endif /* enable interrupts for vblank, frame_end and underflows */ - tegra_dc_writel(dc, (FRAME_END_INT | V_BLANK_INT | ALL_UF_INT), - DC_CMD_INT_ENABLE); + int_enable = (FRAME_END_INT | V_BLANK_INT | ALL_UF_INT); + /* for panels with one-shot mode enable tearing effect interrupt */ + if (dc->out->flags & TEGRA_DC_OUT_ONE_SHOT_MODE) + int_enable |= MSF_INT; + + tegra_dc_writel(dc, int_enable, DC_CMD_INT_ENABLE); tegra_dc_writel(dc, ALL_UF_INT, DC_CMD_INT_MASK); tegra_dc_writel(dc, 0x00000000, DC_DISP_BORDER_COLOR); diff --git a/drivers/video/tegra/dc/dc_sysfs.c b/drivers/video/tegra/dc/dc_sysfs.c index bf27e963f233..09a8e2dbb5b1 100644 --- a/drivers/video/tegra/dc/dc_sysfs.c +++ b/drivers/video/tegra/dc/dc_sysfs.c @@ -313,6 +313,14 @@ static ssize_t nvdps_store(struct device *dev, static DEVICE_ATTR(nvdps, S_IRUGO|S_IWUSR, nvdps_show, nvdps_store); +static ssize_t smart_panel_show(struct device *device, + struct device_attribute *attr, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "1\n"); +} + +static DEVICE_ATTR(smart_panel, S_IRUGO, smart_panel_show, NULL); + void __devexit tegra_dc_remove_sysfs(struct device *dev) { struct nvhost_device *ndev = to_nvhost_device(dev); @@ -332,6 +340,9 @@ void __devexit tegra_dc_remove_sysfs(struct device *dev) if (sd_settings) nvsd_remove_sysfs(dev); + + if (dc->out->flags & TEGRA_DC_OUT_ONE_SHOT_MODE) + device_remove_file(dev, &dev_attr_smart_panel); } void tegra_dc_create_sysfs(struct device *dev) @@ -355,6 +366,9 @@ void tegra_dc_create_sysfs(struct device *dev) if (sd_settings) error |= nvsd_create_sysfs(dev); + if (dc->out->flags & TEGRA_DC_OUT_ONE_SHOT_MODE) + error |= device_create_file(dev, &dev_attr_smart_panel); + if (error) dev_err(&ndev->dev, "Failed to create sysfs attributes!\n"); } diff --git a/drivers/video/tegra/dc/dsi.c b/drivers/video/tegra/dc/dsi.c index 4a9e507bcdb9..d3b1d40d535b 100644 --- a/drivers/video/tegra/dc/dsi.c +++ b/drivers/video/tegra/dc/dsi.c @@ -79,6 +79,8 @@ #define DSI_LP_OP_WRITE 0x1 #define DSI_LP_OP_READ 0x2 +#define DSI_HOST_IDLE_PERIOD 1000 + static bool enable_read_debug; module_param(enable_read_debug, bool, 0644); MODULE_PARM_DESC(enable_read_debug, @@ -129,6 +131,11 @@ struct tegra_dc_dsi_data { bool ulpm; bool enabled; bool host_suspended; + struct mutex host_resume_lock; + struct delayed_work idle_work; + unsigned long idle_delay; + spinlock_t host_ref_lock; + u8 host_ref; u8 driven_mode; u8 controller_index; @@ -296,6 +303,7 @@ const u32 init_reg[] = { static int tegra_dsi_host_suspend(struct tegra_dc *dc); static int tegra_dsi_host_resume(struct tegra_dc *dc); +static void tegra_dc_dsi_idle_work(struct work_struct *work); inline unsigned long tegra_dsi_readl(struct tegra_dc_dsi_data *dsi, u32 reg) { @@ -654,6 +662,13 @@ static void tegra_dsi_init_sw(struct tegra_dc *dc, dsi->info.video_clock_mode = TEGRA_DSI_VIDEO_CLOCK_CONTINUOUS; } + dsi->host_ref = 0; + dsi->host_suspended = false; + spin_lock_init(&dsi->host_ref_lock); + mutex_init(&dsi->host_resume_lock); + init_completion(&dc->out->user_vblank_comp); + INIT_DELAYED_WORK(&dsi->idle_work, tegra_dc_dsi_idle_work); + dsi->idle_delay = msecs_to_jiffies(DSI_HOST_IDLE_PERIOD); } #define SELECT_T_PHY(platform_t_phy_ns, default_phy, clk_ns, hw_inc) ( \ @@ -1373,6 +1388,10 @@ static void tegra_dsi_soft_reset(struct tegra_dc_dsi_data *dsi) static void tegra_dsi_stop_dc_stream(struct tegra_dc *dc, struct tegra_dc_dsi_data *dsi) { + /* Mask the MSF interrupt. */ + if (dc->out->flags & TEGRA_DC_OUT_ONE_SHOT_MODE) + tegra_dc_mask_interrupt(dc, MSF_INT); + tegra_dc_writel(dc, DISP_CTRL_MODE_STOP, DC_CMD_DISPLAY_COMMAND); tegra_dc_writel(dc, 0, DC_DISP_DISP_WIN_OPTIONS); tegra_dc_writel(dc, GENERAL_UPDATE, DC_CMD_STATE_CONTROL); @@ -1453,6 +1472,9 @@ static void tegra_dsi_start_dc_stream(struct tegra_dc *dc, tegra_dc_writel(dc, GENERAL_UPDATE, DC_CMD_STATE_CONTROL); tegra_dc_writel(dc, GENERAL_ACT_REQ | NC_HOST_TRIG, DC_CMD_STATE_CONTROL); + + /* Unmask the MSF interrupt. */ + tegra_dc_unmask_interrupt(dc, MSF_INT); } else { /* set continuous mode */ tegra_dc_writel(dc, DISP_CTRL_MODE_C_DISPLAY, @@ -2114,13 +2136,10 @@ static void tegra_dc_dsi_hold_host(struct tegra_dc *dc) struct tegra_dc_dsi_data *dsi = tegra_dc_get_outdata(dc); if (dc->out->flags & TEGRA_DC_OUT_ONE_SHOT_LP_MODE) { - /* - * The reference count should never be more than 1. - */ - BUG_ON(tegra_is_clk_enabled(dc->clk) > 1); - - if (dsi->host_suspended) - tegra_dsi_host_resume(dc); + spin_lock(&dsi->host_ref_lock); + dsi->host_ref++; + spin_unlock(&dsi->host_ref_lock); + tegra_dsi_host_resume(dc); /* * Take an extra refrence to count for the clk_disable in @@ -2132,14 +2151,27 @@ static void tegra_dc_dsi_hold_host(struct tegra_dc *dc) static void tegra_dc_dsi_release_host(struct tegra_dc *dc) { - if (dc->out->flags & TEGRA_DC_OUT_ONE_SHOT_LP_MODE) + struct tegra_dc_dsi_data *dsi = tegra_dc_get_outdata(dc); + if (dc->out->flags & TEGRA_DC_OUT_ONE_SHOT_LP_MODE) { clk_disable(dc->clk); + spin_lock(&dsi->host_ref_lock); + dsi->host_ref--; + + if (!dsi->host_ref && + (dsi->status.dc_stream == DSI_DC_STREAM_ENABLE)) + schedule_delayed_work(&dsi->idle_work, dsi->idle_delay); + + spin_unlock(&dsi->host_ref_lock); + } } -static void tegra_dc_dsi_idle(struct tegra_dc *dc) +static void tegra_dc_dsi_idle_work(struct work_struct *work) { - if (dc->out->flags & TEGRA_DC_OUT_ONE_SHOT_LP_MODE) - tegra_dsi_host_suspend(dc); + struct tegra_dc_dsi_data *dsi = container_of( + to_delayed_work(work), struct tegra_dc_dsi_data, idle_work); + + if (dsi->dc->out->flags & TEGRA_DC_OUT_ONE_SHOT_LP_MODE) + tegra_dsi_host_suspend(dsi->dc); } int tegra_dsi_write_data(struct tegra_dc *dc, @@ -3297,6 +3329,8 @@ static int tegra_dsi_host_suspend(struct tegra_dc *dc) if (dsi->host_suspended) return 0; + BUG_ON(!tegra_is_clk_enabled(dc->clk)); + tegra_dc_io_start(dc); dsi->host_suspended = true; tegra_dsi_stop_dc_stream(dc, dsi); @@ -3308,6 +3342,7 @@ static int tegra_dsi_host_suspend(struct tegra_dc *dc) tegra_dc_clk_disable(dc); + tegra_dc_io_end(dc); return err; } @@ -3317,8 +3352,12 @@ static int tegra_dsi_host_resume(struct tegra_dc *dc) int err = 0; struct tegra_dc_dsi_data *dsi = tegra_dc_get_outdata(dc); - if (!dsi->host_suspended) + mutex_lock(&dsi->host_resume_lock); + cancel_delayed_work_sync(&dsi->idle_work); + if (!dsi->host_suspended) { + mutex_unlock(&dsi->host_resume_lock); return 0; + } tegra_dc_clk_enable(dc); switch (dsi->info.suspend_aggr) { @@ -3366,8 +3405,10 @@ static int tegra_dsi_host_resume(struct tegra_dc *dc) } tegra_dsi_start_dc_stream(dc, dsi); + dsi->enabled = true; dsi->host_suspended = false; + mutex_unlock(&dsi->host_resume_lock); fail: return err; } @@ -3462,7 +3503,6 @@ struct tegra_dc_out_ops tegra_dc_dsi_ops = { .disable = tegra_dc_dsi_disable, .hold = tegra_dc_dsi_hold_host, .release = tegra_dc_dsi_release_host, - .idle = tegra_dc_dsi_idle, #ifdef CONFIG_PM .suspend = tegra_dc_dsi_suspend, .resume = tegra_dc_dsi_resume, diff --git a/drivers/video/tegra/fb.c b/drivers/video/tegra/fb.c index a9964946f2cc..69b30884e4df 100644 --- a/drivers/video/tegra/fb.c +++ b/drivers/video/tegra/fb.c @@ -325,8 +325,10 @@ static void tegra_fb_imageblit(struct fb_info *info, static int tegra_fb_ioctl(struct fb_info *info, unsigned int cmd, unsigned long arg) { + struct tegra_fb_info *tegra_fb = (struct tegra_fb_info *)info->par; struct tegra_fb_modedb modedb; struct fb_modelist *modelist; + struct fb_vblank vblank = {}; int i; switch (cmd) { @@ -369,6 +371,17 @@ static int tegra_fb_ioctl(struct fb_info *info, unsigned int cmd, unsigned long return -EFAULT; break; + case FBIOGET_VBLANK: + tegra_dc_get_fbvblank(tegra_fb->win->dc, &vblank); + + if (copy_to_user( + (void __user *)arg, &vblank, sizeof(vblank))) + return -EFAULT; + break; + + case FBIO_WAITFORVSYNC: + return tegra_dc_wait_for_vsync(tegra_fb->win->dc); + default: return -ENOTTY; } |