From e4e2e776a3d4bf1adf37fc061cfdfb92281f3ace Mon Sep 17 00:00:00 2001 From: Raghavendra VK Date: Fri, 3 Aug 2012 21:25:43 -0700 Subject: video: tegra: dc: Add timestamp support bug 1021221 Change-Id: Ifbe007de5bdeafaa15a0b3f2a138086045eba160 Signed-off-by: Raghavendra VK Reviewed-on: http://git-master/r/118179 (cherry picked from commit 74be8d4e7210d7bcea0d55565a7cbb06d6cc960e) Reviewed-on: http://git-master/r/121087 Reviewed-by: Jon Mayo Reviewed-by: Automatic_Commit_Validation_User Reviewed-by: Robert Morell --- drivers/video/tegra/dc/dc.c | 19 +++++++ drivers/video/tegra/dc/dc_priv.h | 3 ++ drivers/video/tegra/dc/ext/dev.c | 73 ++++++++++++++++++++++---- drivers/video/tegra/dc/ext/tegra_dc_ext_priv.h | 4 ++ drivers/video/tegra/dc/mode.c | 11 ++++ drivers/video/tegra/dc/window.c | 20 +++++-- 6 files changed, 116 insertions(+), 14 deletions(-) (limited to 'drivers') 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); } -- cgit v1.2.3