summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--arch/arm/mach-tegra/include/mach/dc.h1
-rw-r--r--drivers/video/tegra/dc/dc.c41
-rw-r--r--drivers/video/tegra/dc/dc_priv.h3
-rw-r--r--drivers/video/tegra/dc/dsi.c6
4 files changed, 43 insertions, 8 deletions
diff --git a/arch/arm/mach-tegra/include/mach/dc.h b/arch/arm/mach-tegra/include/mach/dc.h
index 4137a8f11017..cce961643257 100644
--- a/arch/arm/mach-tegra/include/mach/dc.h
+++ b/arch/arm/mach-tegra/include/mach/dc.h
@@ -537,6 +537,7 @@ struct tegra_dc_pwm_params {
void tegra_dc_config_pwm(struct tegra_dc *dc, struct tegra_dc_pwm_params *cfg);
int tegra_dsi_send_panel_short_cmd(struct tegra_dc *dc, u8 *pdata, u8 data_len);
+void tegra_dc_host_trigger(struct tegra_dc *dc);
int tegra_dc_update_csc(struct tegra_dc *dc, int win_index);
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);
diff --git a/drivers/video/tegra/dc/dc_priv.h b/drivers/video/tegra/dc/dc_priv.h
index 30b5ea996922..3d4d092c753c 100644
--- a/drivers/video/tegra/dc/dc_priv.h
+++ b/drivers/video/tegra/dc/dc_priv.h
@@ -139,7 +139,8 @@ struct tegra_dc {
#endif
struct tegra_dc_lut fb_lut;
struct delayed_work underflow_work;
- struct work_struct one_shot_work;
+ u32 one_shot_delay_ms;
+ struct delayed_work one_shot_work;
};
static inline void tegra_dc_io_start(struct tegra_dc *dc)
diff --git a/drivers/video/tegra/dc/dsi.c b/drivers/video/tegra/dc/dsi.c
index b3dd079d449e..544703dc8a48 100644
--- a/drivers/video/tegra/dc/dsi.c
+++ b/drivers/video/tegra/dc/dsi.c
@@ -954,6 +954,12 @@ static void tegra_dsi_set_dsi_clk(struct tegra_dc *dc,
/* Set up pixel clock */
dc->shift_clk_div = dsi->shift_clk_div;
dc->mode.pclk = (clk * 1000) / dsi->shift_clk_div;
+ /* TODO: Define one shot work delay in board file. */
+ /* Since for one-shot mode, refresh rate is usually set larger than
+ * expected refresh rate, it needs at least 3 frame period. Less
+ * delay one shot work is, more powering saving we have. */
+ dc->one_shot_delay_ms = 4 *
+ DIV_ROUND_UP(S_TO_MS(1), dsi->info.refresh_rate);
/* Enable DSI clock */
tegra_dc_setup_clk(dc, dsi->dsi_clk);