summaryrefslogtreecommitdiff
path: root/drivers/video/tegra
diff options
context:
space:
mode:
authorKevin Huang <kevinh@nvidia.com>2012-06-15 11:09:12 -0700
committerSimone Willett <swillett@nvidia.com>2012-06-27 17:35:39 -0700
commit2706d59f63e2897959362f2f1a1be840801ada90 (patch)
tree78e7d25fad734098ccd5f22421cd77bdf61843df /drivers/video/tegra
parentef6acc75bcf5715d126188a711a70b338d98c028 (diff)
video: tegra: dc: Deactivate DSI runtime when DC is idle.
We support 3 different aggressiveness levels of disabling DSI runtime. The larger the aggressive level is, the higher DSI power we can save. Bug 936337 Change-Id: Idadcb49b364e29ddd0a05dde1c6d3dfda6cd493e Signed-off-by: Kevin Huang <kevinh@nvidia.com> Reviewed-on: http://git-master/r/106361 Reviewed-by: Simone Willett <swillett@nvidia.com> Tested-by: Simone Willett <swillett@nvidia.com>
Diffstat (limited to 'drivers/video/tegra')
-rw-r--r--drivers/video/tegra/dc/dsi.c198
1 files changed, 138 insertions, 60 deletions
diff --git a/drivers/video/tegra/dc/dsi.c b/drivers/video/tegra/dc/dsi.c
index ff80aac7149d..e8e7eceeeaf8 100644
--- a/drivers/video/tegra/dc/dsi.c
+++ b/drivers/video/tegra/dc/dsi.c
@@ -126,6 +126,10 @@ struct tegra_dc_dsi_data {
struct dsi_phy_timing_inclk phy_timing;
+ bool ulpm;
+ bool enabled;
+ bool host_suspended;
+
u8 driven_mode;
u8 controller_index;
@@ -147,9 +151,6 @@ struct tegra_dc_dsi_data {
u32 current_dsi_clk_khz;
u32 dsi_control_val;
-
- bool ulpm;
- bool enabled;
};
const u32 dsi_pkt_seq_reg[NUMOF_PKT_SEQ] = {
@@ -3021,73 +3022,87 @@ static void tegra_dc_dsi_destroy(struct tegra_dc *dc)
kfree(dsi);
}
-static int tegra_dsi_deep_sleep(struct tegra_dc *dc,
- struct tegra_dc_dsi_data *dsi)
+static void tegra_dsi_config_phy_clk(struct tegra_dc_dsi_data *dsi,
+ u32 settings)
{
- int err = 0;
- int val;
struct clk *parent_clk = NULL;
struct clk *base_clk = NULL;
- if (!dsi->enabled) {
- err = -EPERM;
- goto fail;
- }
+ /* Disable dsi fast and slow clock */
+ parent_clk = clk_get_parent(dsi->dsi_clk);
+ base_clk = clk_get_parent(parent_clk);
+ if (dsi->info.dsi_instance)
+ tegra_clk_cfg_ex(base_clk,
+ TEGRA_CLK_PLLD_CSI_OUT_ENB,
+ settings);
+ else
+ tegra_clk_cfg_ex(base_clk,
+ TEGRA_CLK_PLLD_DSI_OUT_ENB,
+ settings);
+}
- err = tegra_dsi_set_to_lp_mode(dc, dsi, DSI_LP_OP_WRITE);
- if (err < 0) {
- dev_err(&dc->ndev->dev,
- "DSI failed to go to LP mode\n");
- goto fail;
- }
+static int tegra_dsi_deep_sleep(struct tegra_dc *dc,
+ struct tegra_dc_dsi_data *dsi, u32 suspend_aggr)
+{
+ int val = 0;
+ int err = 0;
- /* 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 sending suspend cmd\n");
+ if (!dsi->enabled) {
+ err = -EPERM;
goto fail;
}
- if (!dsi->ulpm) {
- err = tegra_dsi_enter_ulpm(dsi);
+ switch (suspend_aggr) {
+ case DSI_SUSPEND_FULL:
+ /* Suspend DSI panel */
+ err = tegra_dsi_set_to_lp_mode(dc, dsi, DSI_LP_OP_WRITE);
if (err < 0) {
dev_err(&dc->ndev->dev,
- "DSI failed to enter ulpm\n");
+ "DSI failed to go to LP mode\n");
goto fail;
}
- }
-
- /*
- * Suspend pad
- * It is ok to overwrite previous value of DSI_PAD_CONTROL reg
- * because it will be restored properly in resume sequence
- */
- 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);
-
- /* Disable dsi fast and slow clock */
- parent_clk = clk_get_parent(dsi->dsi_clk);
- base_clk = clk_get_parent(parent_clk);
- if (dsi->info.dsi_instance)
- tegra_clk_cfg_ex(base_clk,
- TEGRA_CLK_PLLD_CSI_OUT_ENB,
- 0);
- else
- tegra_clk_cfg_ex(base_clk,
- TEGRA_CLK_PLLD_DSI_OUT_ENB,
- 0);
+ 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 sending suspend cmd\n");
+ goto fail;
+ }
+ case DSI_HOST_SUSPEND_LV2:
+ /* Set DSI to ULPM and suspend pads. DSI will be set to the
+ * lowest power state in this level. */
+ if (!dsi->ulpm) {
+ err = tegra_dsi_enter_ulpm(dsi);
+ if (err < 0) {
+ dev_err(&dc->ndev->dev,
+ "DSI failed to enter ulpm\n");
+ goto fail;
+ }
+ }
- /* Disable dsi source clock */
- tegra_dsi_clk_disable(dsi);
+ 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);
+ case DSI_HOST_SUSPEND_LV1:
+ /* Disable dsi fast and slow clock */
+ tegra_dsi_config_phy_clk(dsi, TEGRA_DSI_DISABLE);
+ case DSI_HOST_SUSPEND_LV0:
+ /* Disable dsi source clock */
+ tegra_dsi_clk_disable(dsi);
+ break;
+ case DSI_NO_SUSPEND:
+ break;
+ default:
+ dev_err(&dc->ndev->dev, "DSI suspend aggressiveness"
+ "is not supported.\n");
+ }
dsi->enabled = false;
@@ -3099,20 +3114,83 @@ fail:
int tegra_dsi_host_suspend(struct tegra_dc *dc)
{
+ int err = 0;
struct tegra_dc_dsi_data *dsi = tegra_dc_get_outdata(dc);
+ if (dsi->host_suspended)
+ return 0;
+
tegra_dsi_stop_dc_stream(dc, dsi);
- tegra_dsi_clk_disable(dsi);
+ err = tegra_dsi_deep_sleep(dc, dsi, dsi->info.suspend_aggr);
+ if (err < 0)
+ dev_err(&dc->ndev->dev,
+ "DSI failed to enter deep sleep\n");
+
+ dsi->host_suspended = true;
+
+ return err;
}
-void tegra_dsi_host_resume(struct tegra_dc *dc)
+
+int tegra_dsi_host_resume(struct tegra_dc *dc)
{
+ int val = 0;
+ int err = 0;
struct tegra_dc_dsi_data *dsi = tegra_dc_get_outdata(dc);
- tegra_dsi_clk_enable(dsi);
+ if (!dsi->host_suspended)
+ return 0;
+
+ switch (dsi->info.suspend_aggr) {
+ case DSI_HOST_SUSPEND_LV0:
+ tegra_dsi_clk_enable(dsi);
+ break;
+ case DSI_HOST_SUSPEND_LV1:
+ tegra_dsi_config_phy_clk(dsi, TEGRA_DSI_ENABLE);
+ tegra_dsi_clk_enable(dsi);
+ break;
+ case DSI_HOST_SUSPEND_LV2:
+ tegra_dsi_config_phy_clk(dsi, TEGRA_DSI_ENABLE);
+ tegra_dsi_clk_enable(dsi);
+ tegra_dsi_writel(dsi,
+ DSI_POWER_CONTROL_LEG_DSI_ENABLE(TEGRA_DSI_ENABLE),
+ DSI_POWER_CONTROL);
+
+ if (dsi->ulpm) {
+ err = tegra_dsi_enter_ulpm(dsi);
+ if (err < 0) {
+ dev_err(&dc->ndev->dev,
+ "DSI failed to enter ulpm\n");
+ goto fail;
+ }
+
+ 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(0x1));
+ tegra_dsi_writel(dsi, val, DSI_PAD_CONTROL);
+
+ if (tegra_dsi_exit_ulpm(dsi) < 0) {
+ dev_err(&dc->ndev->dev,
+ "DSI failed to exit ulpm\n");
+ goto fail;
+ }
+ }
+ break;
+ case DSI_NO_SUSPEND:
+ break;
+ default:
+ dev_err(&dc->ndev->dev, "DSI suspend aggressivenes"
+ "is not supported.\n");
+ }
+
+ dsi->enabled = true;
+ dsi->host_suspended = false;
tegra_dsi_start_dc_stream(dc, dsi);
+fail:
+ return err;
}
static void tegra_dc_dsi_disable(struct tegra_dc *dc)
@@ -3127,7 +3205,7 @@ static void tegra_dc_dsi_disable(struct tegra_dc *dc)
tegra_dsi_stop_dc_stream_at_frame_end(dc, dsi);
if (dsi->info.power_saving_suspend) {
- if (tegra_dsi_deep_sleep(dc, dsi) < 0) {
+ if (tegra_dsi_deep_sleep(dc, dsi, DSI_SUSPEND_FULL) < 0) {
dev_err(&dc->ndev->dev,
"DSI failed to enter deep sleep\n");
goto fail;
@@ -3180,7 +3258,7 @@ static void tegra_dc_dsi_suspend(struct tegra_dc *dc)
}
}
- if (tegra_dsi_deep_sleep(dc, dsi) < 0) {
+ if (tegra_dsi_deep_sleep(dc, dsi, DSI_SUSPEND_FULL) < 0) {
dev_err(&dc->ndev->dev,
"DSI failed to enter deep sleep\n");
goto fail;