summaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
authorAnimesh Kishore <ankishore@nvidia.com>2011-06-11 00:08:10 +0530
committerNiket Sirsi <nsirsi@nvidia.com>2011-06-15 21:26:59 -0700
commit71d2145b74851d72fad498800be5723552a7ca6d (patch)
tree16d702f9c92258cc329df45e26a6096c959545bd /drivers
parent9397b3d152923affd33e8fbf121606668c0e8732 (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-xdrivers/video/tegra/dc/dsi.c229
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);
}