summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRaghavendra VK <rvk@nvidia.com>2012-08-03 21:25:43 -0700
committerLokesh Pathak <lpathak@nvidia.com>2012-08-21 05:37:09 -0700
commite4e2e776a3d4bf1adf37fc061cfdfb92281f3ace (patch)
tree702feb9db306273fa53aab678f18abd8e3354816
parent1c9102f23b9140a045e2be4dc80735f1268b7961 (diff)
video: tegra: dc: Add timestamp support
bug 1021221 Change-Id: Ifbe007de5bdeafaa15a0b3f2a138086045eba160 Signed-off-by: Raghavendra VK <rvk@nvidia.com> Reviewed-on: http://git-master/r/118179 (cherry picked from commit 74be8d4e7210d7bcea0d55565a7cbb06d6cc960e) Reviewed-on: http://git-master/r/121087 Reviewed-by: Jon Mayo <jmayo@nvidia.com> Reviewed-by: Automatic_Commit_Validation_User Reviewed-by: Robert Morell <rmorell@nvidia.com>
-rw-r--r--arch/arm/mach-tegra/include/mach/dc.h3
-rw-r--r--drivers/video/tegra/dc/dc.c19
-rw-r--r--drivers/video/tegra/dc/dc_priv.h3
-rw-r--r--drivers/video/tegra/dc/ext/dev.c73
-rw-r--r--drivers/video/tegra/dc/ext/tegra_dc_ext_priv.h4
-rw-r--r--drivers/video/tegra/dc/mode.c11
-rw-r--r--drivers/video/tegra/dc/window.c20
7 files changed, 119 insertions, 14 deletions
diff --git a/arch/arm/mach-tegra/include/mach/dc.h b/arch/arm/mach-tegra/include/mach/dc.h
index b60a913ff083..3483fe91ce66 100644
--- a/arch/arm/mach-tegra/include/mach/dc.h
+++ b/arch/arm/mach-tegra/include/mach/dc.h
@@ -539,6 +539,9 @@ void tegra_dc_incr_syncpt_min(struct tegra_dc *dc, int i, u32 val);
*/
int tegra_dc_update_windows(struct tegra_dc_win *windows[], int n);
int tegra_dc_sync_windows(struct tegra_dc_win *windows[], int n);
+int tegra_dc_config_frame_end_intr(struct tegra_dc *dc, bool enable);
+bool tegra_dc_is_within_n_vsync(struct tegra_dc *dc, s64 ts);
+bool tegra_dc_does_vsync_separate(struct tegra_dc *dc, s64 new_ts, s64 old_ts);
int tegra_dc_set_mode(struct tegra_dc *dc, const struct tegra_dc_mode *mode);
struct fb_videomode;
diff --git a/drivers/video/tegra/dc/dc.c b/drivers/video/tegra/dc/dc.c
index db97ed3d3c13..de865e4e2da3 100644
--- a/drivers/video/tegra/dc/dc.c
+++ b/drivers/video/tegra/dc/dc.c
@@ -1045,6 +1045,10 @@ static void tegra_dc_continuous_irq(struct tegra_dc *dc, unsigned long status)
queue_work(system_freezable_wq, &dc->vblank_work);
if (status & FRAME_END_INT) {
+ struct timespec tm = CURRENT_TIME;
+ dc->frame_end_timestamp = timespec_to_ns(&tm);
+ wake_up(&dc->timestamp_wq);
+
/* Mark the frame_end as complete. */
if (!completion_done(&dc->frame_end_complete))
complete(&dc->frame_end_complete);
@@ -1052,6 +1056,20 @@ static void tegra_dc_continuous_irq(struct tegra_dc *dc, unsigned long status)
tegra_dc_trigger_windows(dc);
}
}
+
+/* XXX: Not sure if we limit look ahead to 1 frame */
+bool tegra_dc_is_within_n_vsync(struct tegra_dc *dc, s64 ts)
+{
+ return ((ts - dc->frame_end_timestamp) < dc->frametime_ns);
+}
+
+bool tegra_dc_does_vsync_separate(struct tegra_dc *dc, s64 new_ts, s64 old_ts)
+{
+ return (((new_ts - old_ts) > dc->frametime_ns)
+ || (div_s64((new_ts - dc->frame_end_timestamp), dc->frametime_ns)
+ != div_s64((old_ts - dc->frame_end_timestamp),
+ dc->frametime_ns)));
+}
#endif
static irqreturn_t tegra_dc_irq(int irq, void *ptr)
@@ -1759,6 +1777,7 @@ static int tegra_dc_probe(struct nvhost_device *ndev,
mutex_init(&dc->one_shot_lock);
init_completion(&dc->frame_end_complete);
init_waitqueue_head(&dc->wq);
+ init_waitqueue_head(&dc->timestamp_wq);
#ifdef CONFIG_ARCH_TEGRA_2x_SOC
INIT_WORK(&dc->reset_work, tegra_dc_reset_worker);
#endif
diff --git a/drivers/video/tegra/dc/dc_priv.h b/drivers/video/tegra/dc/dc_priv.h
index 759d64da7052..75c3a2a29658 100644
--- a/drivers/video/tegra/dc/dc_priv.h
+++ b/drivers/video/tegra/dc/dc_priv.h
@@ -113,12 +113,14 @@ struct tegra_dc {
void *out_data;
struct tegra_dc_mode mode;
+ s64 frametime_ns;
struct tegra_dc_win windows[DC_N_WINDOWS];
struct tegra_dc_blend blend;
int n_windows;
wait_queue_head_t wq;
+ wait_queue_head_t timestamp_wq;
struct mutex lock;
struct mutex one_shot_lock;
@@ -163,6 +165,7 @@ struct tegra_dc {
struct delayed_work underflow_work;
u32 one_shot_delay_ms;
struct delayed_work one_shot_work;
+ s64 frame_end_timestamp;
};
#define print_mode_info(dc, mode) do { \
diff --git a/drivers/video/tegra/dc/ext/dev.c b/drivers/video/tegra/dc/ext/dev.c
index 8ec015cbde9b..88273e26c51c 100644
--- a/drivers/video/tegra/dc/ext/dev.c
+++ b/drivers/video/tegra/dc/ext/dev.c
@@ -56,6 +56,7 @@ struct tegra_dc_ext_flip_data {
struct tegra_dc_ext *ext;
struct work_struct work;
struct tegra_dc_ext_flip_win win[DC_N_WINDOWS];
+ struct list_head timestamp_node;
};
int tegra_dc_ext_get_num_outputs(void)
@@ -207,6 +208,7 @@ static int tegra_dc_ext_set_windowattr(struct tegra_dc_ext *ext,
{
int err = 0;
struct tegra_dc_ext_win *ext_win = &ext->win[win->idx];
+ s64 timestamp_ns;
if (flip_win->handle[TEGRA_DC_Y] == NULL) {
win->flags = 0;
@@ -270,8 +272,20 @@ static int tegra_dc_ext_set_windowattr(struct tegra_dc_ext *ext,
msecs_to_jiffies(500), NULL);
}
-
- return 0;
+#ifndef CONFIG_TEGRA_SIMULATION_PLATFORM
+ timestamp_ns = timespec_to_ns(&flip_win->attr.timestamp);
+
+ if (timestamp_ns) {
+ /* XXX: Should timestamping be overridden by "no_vsync" flag */
+ tegra_dc_config_frame_end_intr(win->dc, true);
+ trace_printk("%s:Before timestamp wait\n", win->dc->ndev->name);
+ err = wait_event_interruptible(win->dc->timestamp_wq,
+ tegra_dc_is_within_n_vsync(win->dc, timestamp_ns));
+ trace_printk("%s:After timestamp wait\n", win->dc->ndev->name);
+ tegra_dc_config_frame_end_intr(win->dc, false);
+ }
+#endif
+ return err;
}
static void (*flip_callback)(void);
@@ -323,9 +337,11 @@ static void tegra_dc_ext_flip_worker(struct work_struct *work)
for (i = 0; i < DC_N_WINDOWS; i++) {
struct tegra_dc_ext_flip_win *flip_win = &data->win[i];
- int index = flip_win->attr.index;
+ int j = 0, index = flip_win->attr.index;
struct tegra_dc_win *win;
struct tegra_dc_ext_win *ext_win;
+ struct tegra_dc_ext_flip_data *temp = NULL;
+ s64 head_timestamp = 0;
if (index < 0)
continue;
@@ -337,6 +353,31 @@ static void tegra_dc_ext_flip_worker(struct work_struct *work)
(flip_win->attr.flags & TEGRA_DC_EXT_FLIP_FLAG_CURSOR))
skip_flip = true;
+ mutex_lock(&ext_win->queue_lock);
+ list_for_each_entry(temp, &ext_win->timestamp_queue,
+ timestamp_node) {
+ if (j == 0) {
+ if (unlikely(temp != data))
+ dev_err(&win->dc->ndev->dev,
+ "work queue did NOT dequeue head!!!");
+ else
+ head_timestamp =
+ timespec_to_ns(&flip_win->attr.timestamp);
+ } else {
+ s64 timestamp =
+ timespec_to_ns(&temp->win[i].attr.timestamp);
+
+ skip_flip = !tegra_dc_does_vsync_separate(ext->dc,
+ timestamp, head_timestamp);
+ /* Look ahead only one flip */
+ break;
+ }
+ j++;
+ }
+ if (!list_empty(&ext_win->timestamp_queue))
+ list_del(&data->timestamp_node);
+ mutex_unlock(&ext_win->queue_lock);
+
if (win->flags & TEGRA_WIN_FLAG_ENABLED) {
int j;
for (j = 0; j < TEGRA_DC_NUM_PLANES; j++) {
@@ -368,17 +409,17 @@ static void tegra_dc_ext_flip_worker(struct work_struct *work)
flip_callback();
spin_unlock(&flip_callback_lock);
}
- }
- for (i = 0; i < DC_N_WINDOWS; i++) {
- struct tegra_dc_ext_flip_win *flip_win = &data->win[i];
- int index = flip_win->attr.index;
+ for (i = 0; i < DC_N_WINDOWS; i++) {
+ struct tegra_dc_ext_flip_win *flip_win = &data->win[i];
+ int index = flip_win->attr.index;
- if (index < 0)
- continue;
+ if (index < 0)
+ continue;
- tegra_dc_incr_syncpt_min(ext->dc, index,
- flip_win->syncpt_max);
+ tegra_dc_incr_syncpt_min(ext->dc, index,
+ flip_win->syncpt_max);
+ }
}
/* unpin and deref previous front buffers */
@@ -490,6 +531,7 @@ static int tegra_dc_ext_flip(struct tegra_dc_ext_user *user,
struct tegra_dc_ext_flip_data *data;
int work_index = -1;
int i, ret = 0;
+ bool has_timestamp = false;
#ifdef CONFIG_ANDROID
int index_check[DC_N_WINDOWS] = {0, };
@@ -530,6 +572,8 @@ static int tegra_dc_ext_flip(struct tegra_dc_ext_user *user,
int index = args->win[i].index;
memcpy(&flip_win->attr, &args->win[i], sizeof(flip_win->attr));
+ if (timespec_to_ns(&flip_win->attr.timestamp))
+ has_timestamp = true;
if (index < 0)
continue;
@@ -604,6 +648,11 @@ static int tegra_dc_ext_flip(struct tegra_dc_ext_user *user,
ret = -EINVAL;
goto unlock;
}
+ if (has_timestamp) {
+ mutex_lock(&ext->win[work_index].queue_lock);
+ list_add_tail(&data->timestamp_node, &ext->win[work_index].timestamp_queue);
+ mutex_unlock(&ext->win[work_index].queue_lock);
+ }
queue_work(ext->win[work_index].flip_wq, &data->work);
unlock_windows_for_flip(user, args);
@@ -944,6 +993,8 @@ static int tegra_dc_ext_setup_windows(struct tegra_dc_ext *ext)
}
mutex_init(&win->lock);
+ mutex_init(&win->queue_lock);
+ INIT_LIST_HEAD(&win->timestamp_queue);
}
return 0;
diff --git a/drivers/video/tegra/dc/ext/tegra_dc_ext_priv.h b/drivers/video/tegra/dc/ext/tegra_dc_ext_priv.h
index f68c7d5c93c2..ef7361d1d933 100644
--- a/drivers/video/tegra/dc/ext/tegra_dc_ext_priv.h
+++ b/drivers/video/tegra/dc/ext/tegra_dc_ext_priv.h
@@ -58,6 +58,10 @@ struct tegra_dc_ext_win {
struct workqueue_struct *flip_wq;
atomic_t nr_pending_flips;
+
+ struct mutex queue_lock;
+
+ struct list_head timestamp_queue;
};
struct tegra_dc_ext {
diff --git a/drivers/video/tegra/dc/mode.c b/drivers/video/tegra/dc/mode.c
index be909691b957..45cfe8dbd188 100644
--- a/drivers/video/tegra/dc/mode.c
+++ b/drivers/video/tegra/dc/mode.c
@@ -137,6 +137,16 @@ static bool check_ref_to_sync(struct tegra_dc_mode *mode)
return true;
}
+static s64 calc_frametime_ns(const struct tegra_dc_mode *m)
+{
+ long h_total, v_total;
+ h_total = m->h_active + m->h_front_porch + m->h_back_porch +
+ m->h_sync_width;
+ v_total = m->v_active + m->v_front_porch + m->v_back_porch +
+ m->v_sync_width;
+ return (s64)(div_s64(((s64)h_total * v_total * 1000000000ULL), m->pclk));
+}
+
/* return in 1000ths of a Hertz */
int tegra_dc_calc_refresh(const struct tegra_dc_mode *m)
{
@@ -265,6 +275,7 @@ int tegra_dc_set_mode(struct tegra_dc *dc, const struct tegra_dc_mode *mode)
panel_sync_rate = dc->out->dsi->rated_refresh_rate * 1000;
print_mode(dc, mode, __func__);
+ dc->frametime_ns = calc_frametime_ns(mode);
return 0;
}
diff --git a/drivers/video/tegra/dc/window.c b/drivers/video/tegra/dc/window.c
index 0b4350d49418..cd91fab428ed 100644
--- a/drivers/video/tegra/dc/window.c
+++ b/drivers/video/tegra/dc/window.c
@@ -24,6 +24,7 @@
#include "dc_priv.h"
static int no_vsync;
+static atomic_t frame_end_ref = ATOMIC_INIT(0);
module_param_named(no_vsync, no_vsync, int, S_IRUGO | S_IWUSR);
@@ -40,6 +41,17 @@ static bool tegra_dc_windows_are_clean(struct tegra_dc_win *windows[],
return true;
}
+int tegra_dc_config_frame_end_intr(struct tegra_dc *dc, bool enable)
+{
+ tegra_dc_writel(dc, FRAME_END_INT, DC_CMD_INT_STATUS);
+ if (enable) {
+ atomic_inc(&frame_end_ref);
+ tegra_dc_unmask_interrupt(dc, FRAME_END_INT);
+ } else if (!atomic_dec_return(&frame_end_ref))
+ tegra_dc_mask_interrupt(dc, FRAME_END_INT);
+ return 0;
+}
+
static int get_topmost_window(u32 *depths, unsigned long *wins)
{
int idx, best = -1;
@@ -406,8 +418,9 @@ int tegra_dc_update_windows(struct tegra_dc_win *windows[], int n)
FRAME_END_INT | V_BLANK_INT | ALL_UF_INT);
} else {
clear_bit(V_BLANK_FLIP, &dc->vblank_ref_count);
- tegra_dc_mask_interrupt(dc,
- FRAME_END_INT | V_BLANK_INT | ALL_UF_INT);
+ tegra_dc_mask_interrupt(dc, V_BLANK_INT | ALL_UF_INT);
+ if (!atomic_read(&frame_end_ref))
+ tegra_dc_mask_interrupt(dc, FRAME_END_INT);
}
if (dc->out->flags & TEGRA_DC_OUT_ONE_SHOT_MODE)
@@ -456,7 +469,8 @@ void tegra_dc_trigger_windows(struct tegra_dc *dc)
}
if (!dirty) {
- if (!(dc->out->flags & TEGRA_DC_OUT_ONE_SHOT_MODE))
+ if (!(dc->out->flags & TEGRA_DC_OUT_ONE_SHOT_MODE)
+ && !atomic_read(&frame_end_ref))
tegra_dc_mask_interrupt(dc, FRAME_END_INT);
}