summaryrefslogtreecommitdiff
path: root/drivers/video/tegra
diff options
context:
space:
mode:
authorRakesh Iyer <riyer@nvidia.com>2012-08-21 18:25:17 -0700
committerMrutyunjay Sawant <msawant@nvidia.com>2012-09-03 02:44:21 -0700
commitf0a2ca55641520d9a1d86a50e59c202d175c4635 (patch)
tree6da30942867adc67be1d25c5bd04588a510ab319 /drivers/video/tegra
parentf04a298e90fee39e8bcc74cbee0ef4e2645ed6da (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>
Diffstat (limited to 'drivers/video/tegra')
-rw-r--r--drivers/video/tegra/dc/dc.c46
-rw-r--r--drivers/video/tegra/dc/dc_sysfs.c14
-rw-r--r--drivers/video/tegra/dc/dsi.c66
-rw-r--r--drivers/video/tegra/fb.c13
4 files changed, 124 insertions, 15 deletions
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;
}