diff options
author | Animesh Kishore <ankishore@nvidia.com> | 2011-06-11 00:08:10 +0530 |
---|---|---|
committer | Niket Sirsi <nsirsi@nvidia.com> | 2011-06-15 21:26:59 -0700 |
commit | 71d2145b74851d72fad498800be5723552a7ca6d (patch) | |
tree | 16d702f9c92258cc329df45e26a6096c959545bd /drivers | |
parent | 9397b3d152923affd33e8fbf121606668c0e8732 (diff) |
video: dsi: tegra: driver power management
Implementation for:
- syncpt for ulpm
- power saving in different components of the controller
- correcting syncpt sequence for BTA
Bug 833709
Bug 793857
Change-Id: I60ff03c64dc46a3dd8e09e1c6d43ab8355b8b649
Reviewed-on: http://git-master/r/36142
Reviewed-by: Animesh Kishore <ankishore@nvidia.com>
Tested-by: Animesh Kishore <ankishore@nvidia.com>
Reviewed-by: Hanumanth Venkateswa Moganty <vmoganty@nvidia.com>
Diffstat (limited to 'drivers')
-rwxr-xr-x | drivers/video/tegra/dc/dsi.c | 229 |
1 files changed, 166 insertions, 63 deletions
diff --git a/drivers/video/tegra/dc/dsi.c b/drivers/video/tegra/dc/dsi.c index b2d011f8a41c..ce0852a79eff 100755 --- a/drivers/video/tegra/dc/dsi.c +++ b/drivers/video/tegra/dc/dsi.c @@ -128,6 +128,7 @@ struct tegra_dc_dsi_data { u32 dsi_control_val; bool ulpm; + bool enabled; }; const u32 dsi_pkt_seq_reg[NUMOF_PKT_SEQ] = { @@ -280,6 +281,34 @@ static inline void tegra_dsi_writel(struct tegra_dc_dsi_data *dsi,u32 val, writel(val, dsi->base + reg * 4); } +static int tegra_dsi_syncpt(struct tegra_dc_dsi_data *dsi) +{ + u32 val; + int ret; + + ret = 0; + + dsi->syncpt_val = nvhost_syncpt_read( + &dsi->dc->ndev->host->syncpt, dsi->syncpt_id); + + val = DSI_INCR_SYNCPT_COND(OP_DONE) | + DSI_INCR_SYNCPT_INDX(dsi->syncpt_id); + tegra_dsi_writel(dsi, val, DSI_INCR_SYNCPT); + + /* TODO: Use interrupt rather than polling */ + ret = nvhost_syncpt_wait(&dsi->dc->ndev->host->syncpt, + dsi->syncpt_id, dsi->syncpt_val + 1); + if (ret < 0) { + printk(KERN_ERR "DSI sync point failure\n"); + goto fail; + } + + (dsi->syncpt_val)++; + return 0; +fail: + return ret; +} + static u32 tegra_dsi_get_hs_clk_rate(struct tegra_dc_dsi_data *dsi) { u32 dsi_clock_rate_khz; @@ -379,6 +408,7 @@ static void tegra_dsi_init_sw(struct tegra_dc *dc, dsi->controller_index = dc->ndev->id; dsi->ulpm = false; + dsi->enabled = false; dsi->dsi_control_val = DSI_CONTROL_VIRTUAL_CHANNEL(dsi->info.virtual_channel) | @@ -1002,10 +1032,17 @@ static int tegra_dsi_init_hw(struct tegra_dc *dc, } tegra_dsi_writel(dsi, dsi->dsi_control_val, DSI_CONTROL); - val = DSI_PAD_CONTROL_PAD_PDIO(0) | + if (!dsi->ulpm) { + val = DSI_PAD_CONTROL_PAD_PDIO(0) | DSI_PAD_CONTROL_PAD_PDIO_CLK(0) | DSI_PAD_CONTROL_PAD_PULLDN_ENAB(TEGRA_DSI_DISABLE); - tegra_dsi_writel(dsi, val, DSI_PAD_CONTROL); + tegra_dsi_writel(dsi, val, DSI_PAD_CONTROL); + } else { + val = DSI_PAD_CONTROL_PAD_PDIO(0x3) | + DSI_PAD_CONTROL_PAD_PDIO_CLK(0x1) | + DSI_PAD_CONTROL_PAD_PULLDN_ENAB(TEGRA_DSI_ENABLE); + tegra_dsi_writel(dsi, val, DSI_PAD_CONTROL); + } val = DSI_POWER_CONTROL_LEG_DSI_ENABLE(TEGRA_DSI_ENABLE); tegra_dsi_writel(dsi, val, DSI_POWER_CONTROL); @@ -1125,41 +1162,30 @@ static bool tegra_dsi_is_controller_idle(struct tegra_dc_dsi_data *dsi) return retVal; } -static bool tegra_dsi_host_trigger(struct tegra_dc_dsi_data *dsi) +static int tegra_dsi_host_trigger(struct tegra_dc_dsi_data *dsi) { - bool status; + int status; u32 val; - status = false; + status = 0; - if (tegra_dsi_readl(dsi, DSI_TRIGGER)) + if (tegra_dsi_readl(dsi, DSI_TRIGGER)) { + status = -EBUSY; goto fail; - -#if DSI_USE_SYNC_POINTS - val = DSI_INCR_SYNCPT_COND(OP_DONE) | - DSI_INCR_SYNCPT_INDX(dsi->syncpt_id); - tegra_dsi_writel(dsi, val, DSI_INCR_SYNCPT); - - dsi->syncpt_val = nvhost_syncpt_read( - &dsi->dc->ndev->host->syncpt, dsi->syncpt_id); + } tegra_dsi_writel(dsi, DSI_TRIGGER_HOST_TRIGGER(TEGRA_DSI_ENABLE), DSI_TRIGGER); - /* TODO: Use interrupt rather than polling */ - if (nvhost_syncpt_wait(&dsi->dc->ndev->host->syncpt, - dsi->syncpt_id, dsi->syncpt_val + 1) < 0) { - printk(KERN_ERR "DSI sync point failure\n"); - status = false; +#if DSI_USE_SYNC_POINTS + status = tegra_dsi_syncpt(dsi); + if (status < 0) { + printk(KERN_ERR "DSI syncpt for host trigger failed\n"); goto fail; } - - (dsi->syncpt_val)++; - status = true; #else - tegra_dsi_writel(dsi, - DSI_TRIGGER_HOST_TRIGGER(TEGRA_DSI_ENABLE), DSI_TRIGGER); - status = tegra_dsi_is_controller_idle(dsi); + if (!tegra_dsi_is_controller_idle(dsi)) + status = -EIO; #endif fail: @@ -1201,8 +1227,9 @@ static int _tegra_dsi_write_data(struct tegra_dc_dsi_data *dsi, } } - if (!tegra_dsi_host_trigger(dsi)) - err = -EIO; + err = tegra_dsi_host_trigger(dsi); + if (err < 0) + printk(KERN_ERR "DSI host trigger failed\n"); return err; } @@ -1293,18 +1320,18 @@ static int tegra_dsi_bta(struct tegra_dc_dsi_data *dsi) poll_time = 0; err = 0; -#if DSI_USE_SYNC_POINTS - val = DSI_INCR_SYNCPT_COND(OP_DONE) | - DSI_INCR_SYNCPT_INDX(dsi->syncpt_id); - tegra_dsi_writel(dsi, val, DSI_INCR_SYNCPT); + val = tegra_dsi_readl(dsi, DSI_HOST_DSI_CONTROL); + val |= DSI_HOST_DSI_CONTROL_IMM_BTA(TEGRA_DSI_ENABLE); + tegra_dsi_writel(dsi, val, DSI_HOST_DSI_CONTROL); +#if DSI_USE_SYNC_POINTS /* FIXME: Workaround for nvhost_syncpt_read */ dsi->syncpt_val = nvhost_syncpt_update_min( &dsi->dc->ndev->host->syncpt, dsi->syncpt_id); - val = tegra_dsi_readl(dsi, DSI_HOST_DSI_CONTROL); - val |= DSI_HOST_DSI_CONTROL_IMM_BTA(TEGRA_DSI_ENABLE); - tegra_dsi_writel(dsi, val, DSI_HOST_DSI_CONTROL); + val = DSI_INCR_SYNCPT_COND(OP_DONE) | + DSI_INCR_SYNCPT_INDX(dsi->syncpt_id); + tegra_dsi_writel(dsi, val, DSI_INCR_SYNCPT); /* TODO: Use interrupt rather than polling */ err = nvhost_syncpt_wait(&dsi->dc->ndev->host->syncpt, @@ -1314,10 +1341,6 @@ static int tegra_dsi_bta(struct tegra_dc_dsi_data *dsi) else (dsi->syncpt_val)++; #else - val = tegra_dsi_readl(dsi, DSI_HOST_DSI_CONTROL); - val |= DSI_HOST_DSI_CONTROL_IMM_BTA(TEGRA_DSI_ENABLE); - tegra_dsi_writel(dsi, val, DSI_HOST_DSI_CONTROL); - while (poll_time < DSI_STATUS_POLLING_DURATION_USEC) { val = tegra_dsi_readl(dsi, DSI_HOST_DSI_CONTROL); val &= DSI_HOST_DSI_CONTROL_IMM_BTA(TEGRA_DSI_ENABLE); @@ -1537,40 +1560,71 @@ fail: return err; } -static void tegra_dsi_enter_ulpm(struct tegra_dc_dsi_data *dsi) +static int tegra_dsi_enter_ulpm(struct tegra_dc_dsi_data *dsi) { u32 val; + int ret; + + ret = 0; val = tegra_dsi_readl(dsi, DSI_HOST_DSI_CONTROL); val &= ~DSI_HOST_DSI_CONTROL_ULTRA_LOW_POWER(3); val |= DSI_HOST_DSI_CONTROL_ULTRA_LOW_POWER(ENTER_ULPM); tegra_dsi_writel(dsi, val, DSI_HOST_DSI_CONTROL); +#if DSI_USE_SYNC_POINTS + ret = tegra_dsi_syncpt(dsi); + if (ret < 0) { + printk(KERN_ERR "DSI syncpt for ulpm enter failed\n"); + goto fail; + } +#else + /* TODO: Find exact delay required */ + mdelay(10); +#endif dsi->ulpm = true; +fail: + return ret; } -static void tegra_dsi_exit_ulpm(struct tegra_dc_dsi_data *dsi) +static int tegra_dsi_exit_ulpm(struct tegra_dc_dsi_data *dsi) { u32 val; + int ret; + + ret = 0; val = tegra_dsi_readl(dsi, DSI_HOST_DSI_CONTROL); val &= ~DSI_HOST_DSI_CONTROL_ULTRA_LOW_POWER(3); val |= DSI_HOST_DSI_CONTROL_ULTRA_LOW_POWER(EXIT_ULPM); tegra_dsi_writel(dsi, val, DSI_HOST_DSI_CONTROL); - val &= ~DSI_HOST_DSI_CONTROL_ULTRA_LOW_POWER(3); +#if DSI_USE_SYNC_POINTS + ret = tegra_dsi_syncpt(dsi); + if (ret < 0) { + printk(KERN_ERR "DSI syncpt for ulpm exit failed\n"); + goto fail; + } +#else + /* TODO: Find exact delay required */ + mdelay(10); +#endif + dsi->ulpm = false; + + val = tegra_dsi_readl(dsi, DSI_HOST_DSI_CONTROL); + val &= ~DSI_HOST_DSI_CONTROL_ULTRA_LOW_POWER(0x3); val |= DSI_HOST_DSI_CONTROL_ULTRA_LOW_POWER(NORMAL); tegra_dsi_writel(dsi, val, DSI_HOST_DSI_CONTROL); +fail: + return ret; - /* TODO: Find exact delay required */ - mdelay(5); - dsi->ulpm = false; } static void tegra_dc_dsi_enable(struct tegra_dc *dc) { struct tegra_dc_dsi_data *dsi = tegra_dc_get_outdata(dc); int err; + u32 val; tegra_dc_io_start(dc); mutex_lock(&dsi->lock); @@ -1581,8 +1635,13 @@ static void tegra_dc_dsi_enable(struct tegra_dc *dc) */ tegra_dsi_stop_dc_stream_at_frame_end(dc, dsi); - if (dsi->ulpm) { - tegra_dsi_exit_ulpm(dsi); + if (dsi->enabled) { + if (dsi->ulpm) { + if (tegra_dsi_exit_ulpm(dsi) < 0) { + printk(KERN_ERR "DSI failed to exit ulpm\n"); + goto fail; + } + } if (dsi->info.panel_reset) { err = tegra_dsi_send_panel_cmd(dc, dsi, dsi->info.dsi_init_cmd, @@ -1590,7 +1649,7 @@ static void tegra_dc_dsi_enable(struct tegra_dc *dc) if (err < 0) { dev_err(&dc->ndev->dev, "dsi: error while sending dsi init cmd\n"); - return; + goto fail; } } } else { @@ -1598,14 +1657,29 @@ static void tegra_dc_dsi_enable(struct tegra_dc *dc) if (err < 0) { dev_err(&dc->ndev->dev, "dsi: not able to init dsi hardware\n"); - return; + goto fail; + } + + if (dsi->ulpm) { + if (tegra_dsi_enter_ulpm(dsi) < 0) { + printk(KERN_ERR "DSI failed to enter ulpm\n"); + goto fail; + } + val = DSI_PAD_CONTROL_PAD_PDIO(0) | + DSI_PAD_CONTROL_PAD_PDIO_CLK(0) | + DSI_PAD_CONTROL_PAD_PULLDN_ENAB(TEGRA_DSI_DISABLE); + tegra_dsi_writel(dsi, val, DSI_PAD_CONTROL); + if (tegra_dsi_exit_ulpm(dsi) < 0) { + printk(KERN_ERR "DSI failed to exit ulpm\n"); + goto fail; + } } err = tegra_dsi_set_to_lp_mode(dc, dsi); if (err < 0) { dev_err(&dc->ndev->dev, "dsi: not able to set to lp mode\n"); - return; + goto fail; } err = tegra_dsi_send_panel_cmd(dc, dsi, dsi->info.dsi_init_cmd, @@ -1613,21 +1687,22 @@ static void tegra_dc_dsi_enable(struct tegra_dc *dc) if (err < 0) { dev_err(&dc->ndev->dev, "dsi: error while sending dsi init cmd\n"); - return; + goto fail; } err = tegra_dsi_set_to_hs_mode(dc, dsi); if (err < 0) { - dev_err(&dc->ndev->dev, - "dsi: not able to set to hs mode\n"); - return; + dev_err(&dc->ndev->dev, + "dsi: not able to set to hs mode\n"); + goto fail; } - } - if (dsi->status.driven == DSI_DRIVEN_MODE_DC) { - tegra_dsi_start_dc_stream(dc, dsi); + dsi->enabled = true; } + if (dsi->status.driven == DSI_DRIVEN_MODE_DC) + tegra_dsi_start_dc_stream(dc, dsi); +fail: mutex_unlock(&dsi->lock); tegra_dc_io_end(dc); } @@ -1864,8 +1939,10 @@ static void tegra_dc_dsi_disable(struct tegra_dc *dc) if (dsi->status.dc_stream == DSI_DC_STREAM_ENABLE) tegra_dsi_stop_dc_stream(dc, dsi); - if (!dsi->ulpm) - tegra_dsi_enter_ulpm(dsi); + if (!dsi->ulpm) { + if (tegra_dsi_enter_ulpm(dsi) < 0) + printk(KERN_ERR "DSI failed to enter ulpm\n"); + } mutex_unlock(&dsi->lock); } @@ -1875,25 +1952,51 @@ static void tegra_dc_dsi_suspend(struct tegra_dc *dc) { struct tegra_dc_dsi_data *dsi; int err; + u32 val; dsi = tegra_dc_get_outdata(dc); tegra_dc_io_start(dc); mutex_lock(&dsi->lock); - if (dsi->ulpm) - tegra_dsi_exit_ulpm(dsi); + if (dsi->ulpm) { + if (tegra_dsi_exit_ulpm(dsi) < 0) { + printk(KERN_ERR "DSI failed to exit ulpm"); + goto fail; + } + } + /* Suspend Panel */ err = tegra_dsi_send_panel_cmd(dc, dsi, dsi->info.dsi_suspend_cmd, dsi->info.n_suspend_cmd); if (err < 0) { dev_err(&dc->ndev->dev, - "dsi: error while sending dsi suspend cmd\n"); - return; + "dsi: Error while sending dsi suspend cmd\n"); + goto fail; } - clk_disable(dsi->dsi_clk); + if (!dsi->ulpm) { + if (tegra_dsi_enter_ulpm(dsi) < 0) { + printk(KERN_ERR "DSI failed to enter ulpm\n"); + goto fail; + } + } + + /* Suspend pad */ + val = tegra_dsi_readl(dsi, DSI_PAD_CONTROL); + val = DSI_PAD_CONTROL_PAD_PDIO(0x3) | + DSI_PAD_CONTROL_PAD_PDIO_CLK(0x1) | + DSI_PAD_CONTROL_PAD_PULLDN_ENAB(TEGRA_DSI_ENABLE); + tegra_dsi_writel(dsi, val, DSI_PAD_CONTROL); + + /* Suspend core-logic */ + val = DSI_POWER_CONTROL_LEG_DSI_ENABLE(TEGRA_DSI_DISABLE); + tegra_dsi_writel(dsi, val, DSI_POWER_CONTROL); + dsi->enabled = false; + + clk_disable(dsi->dsi_clk); +fail: mutex_unlock(&dsi->lock); tegra_dc_io_end(dc); } |