diff options
author | Kevin Huang <kevinh@nvidia.com> | 2012-02-03 18:02:42 -0800 |
---|---|---|
committer | Rohan Somvanshi <rsomvanshi@nvidia.com> | 2012-02-15 01:08:18 -0800 |
commit | 24ee99196e2b8a56f12b867b4e1e6e6c54b36e76 (patch) | |
tree | 2714eaeb3d0c35fc7bd02a0ce5944e712c30995a /drivers | |
parent | 48c516dc71c28bb1eae8e5e451e22c467cc139b1 (diff) |
video: tegra: dc: Schedule delayed work to clear emc bandwidth.
Bug 932840
Change-Id: I12d8d2d2cd42d0dafea38463ad77b44f7e64d7c1
Signed-off-by: Kevin Huang <kevinh@nvidia.com>
Reviewed-on: http://git-master/r/83645
Reviewed-by: Rohan Somvanshi <rsomvanshi@nvidia.com>
Tested-by: Rohan Somvanshi <rsomvanshi@nvidia.com>
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/video/tegra/dc/dc.c | 41 | ||||
-rw-r--r-- | drivers/video/tegra/dc/dc_priv.h | 3 | ||||
-rw-r--r-- | drivers/video/tegra/dc/dsi.c | 6 |
3 files changed, 42 insertions, 8 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); 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); |