summaryrefslogtreecommitdiff
path: root/drivers/video/tegra/dc/dc.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/video/tegra/dc/dc.c')
-rw-r--r--drivers/video/tegra/dc/dc.c41
1 files changed, 34 insertions, 7 deletions
diff --git a/drivers/video/tegra/dc/dc.c b/drivers/video/tegra/dc/dc.c
index 348637e4b846..7e614589c314 100644
--- a/drivers/video/tegra/dc/dc.c
+++ b/drivers/video/tegra/dc/dc.c
@@ -1083,6 +1083,9 @@ int tegra_dc_update_windows(struct tegra_dc_win *windows[], int n)
dc = windows[0]->dc;
+ if (dc->out->flags & TEGRA_DC_OUT_ONE_SHOT_MODE)
+ cancel_delayed_work_sync(&dc->one_shot_work);
+
mutex_lock(&dc->lock);
if (!dc->enabled) {
@@ -1259,13 +1262,17 @@ int tegra_dc_update_windows(struct tegra_dc_win *windows[], int n)
}
if (dc->out->flags & TEGRA_DC_OUT_ONE_SHOT_MODE)
- update_mask |= NC_HOST_TRIG;
-
- tegra_dc_writel(dc, update_mask, DC_CMD_STATE_CONTROL);
+ schedule_delayed_work(&dc->one_shot_work,
+ msecs_to_jiffies(dc->one_shot_delay_ms));
/* update EMC clock if calculated bandwidth has changed */
tegra_dc_program_bandwidth(dc);
+ if (dc->out->flags & TEGRA_DC_OUT_ONE_SHOT_MODE)
+ update_mask |= NC_HOST_TRIG;
+
+ tegra_dc_writel(dc, update_mask, DC_CMD_STATE_CONTROL);
+
mutex_unlock(&dc->lock);
return 0;
@@ -2009,11 +2016,28 @@ static void tegra_dc_vblank(struct work_struct *work)
}
}
+/* Must acquire dc lock before invoking this function. */
+void tegra_dc_host_trigger(struct tegra_dc *dc)
+{
+ /* We release the lock here to prevent deadlock between
+ * cancel_delayed_work_sync and one-shot work. */
+ mutex_unlock(&dc->lock);
+ cancel_delayed_work_sync(&dc->one_shot_work);
+ mutex_lock(&dc->lock);
+ schedule_delayed_work(&dc->one_shot_work,
+ msecs_to_jiffies(dc->one_shot_delay_ms));
+ tegra_dc_program_bandwidth(dc);
+ tegra_dc_writel(dc, NC_HOST_TRIG, DC_CMD_STATE_CONTROL);
+}
+
static void tegra_dc_one_shot_worker(struct work_struct *work)
{
- struct tegra_dc *dc = container_of(work, struct tegra_dc, one_shot_work);
+ struct tegra_dc *dc = container_of(
+ to_delayed_work(work), struct tegra_dc, one_shot_work);
+ mutex_lock(&dc->lock);
/* memory client has gone idle */
tegra_dc_clear_bandwidth(dc);
+ mutex_unlock(&dc->lock);
}
/* return an arbitrarily large number if count overflow occurs.
@@ -2119,8 +2143,6 @@ static void tegra_dc_one_shot_irq(struct tegra_dc *dc, unsigned long status)
}
if (status & FRAME_END_INT) {
- schedule_work(&dc->one_shot_work);
-
/* Mark the frame_end as complete. */
if (!completion_done(&dc->frame_end_complete))
complete(&dc->frame_end_complete);
@@ -2581,6 +2603,8 @@ void tegra_dc_disable(struct tegra_dc *dc)
/* it's important that new underflow work isn't scheduled before the
* lock is acquired. */
cancel_delayed_work_sync(&dc->underflow_work);
+ if (dc->out->flags & TEGRA_DC_OUT_ONE_SHOT_MODE)
+ cancel_delayed_work_sync(&dc->one_shot_work);
mutex_lock(&dc->lock);
@@ -2740,6 +2764,9 @@ static int tegra_dc_probe(struct nvhost_device *ndev)
dc->clk = clk;
dc->emc_clk = emc_clk;
dc->shift_clk_div = 1;
+ /* Initialize one shot work delay, it will be assigned by dsi
+ * according to refresh rate later. */
+ dc->one_shot_delay_ms = 40;
dc->base_res = base_res;
dc->base = base;
@@ -2764,7 +2791,7 @@ static int tegra_dc_probe(struct nvhost_device *ndev)
#endif
INIT_WORK(&dc->vblank_work, tegra_dc_vblank);
INIT_DELAYED_WORK(&dc->underflow_work, tegra_dc_underflow_worker);
- INIT_WORK(&dc->one_shot_work, tegra_dc_one_shot_worker);
+ INIT_DELAYED_WORK(&dc->one_shot_work, tegra_dc_one_shot_worker);
tegra_dc_init_lut_defaults(&dc->fb_lut);