summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--arch/arm/mach-tegra/include/mach/dc.h5
-rw-r--r--drivers/video/tegra/dc/bandwidth.c3
-rw-r--r--drivers/video/tegra/dc/dc.c55
-rw-r--r--drivers/video/tegra/dc/dc_priv.h7
-rw-r--r--drivers/video/tegra/dc/dsi.c53
5 files changed, 107 insertions, 16 deletions
diff --git a/arch/arm/mach-tegra/include/mach/dc.h b/arch/arm/mach-tegra/include/mach/dc.h
index 87af9441c28c..8f8b0b71bebe 100644
--- a/arch/arm/mach-tegra/include/mach/dc.h
+++ b/arch/arm/mach-tegra/include/mach/dc.h
@@ -377,6 +377,7 @@ struct tegra_dc_out {
#define TEGRA_DC_OUT_CONTINUOUS_MODE (0 << 3)
#define TEGRA_DC_OUT_ONE_SHOT_MODE (1 << 3)
#define TEGRA_DC_OUT_N_SHOT_MODE (1 << 4)
+#define TEGRA_DC_OUT_ONE_SHOT_LP_MODE (1 << 5)
#define TEGRA_DC_ALIGN_MSB 0
#define TEGRA_DC_ALIGN_LSB 1
@@ -551,6 +552,10 @@ 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_suspend(struct tegra_dc *dc);
+void tegra_dc_host_resume(struct tegra_dc *dc);
+int tegra_dsi_host_suspend(struct tegra_dc *dc);
+void tegra_dsi_host_resume(struct tegra_dc *dc);
int tegra_dc_update_csc(struct tegra_dc *dc, int win_index);
diff --git a/drivers/video/tegra/dc/bandwidth.c b/drivers/video/tegra/dc/bandwidth.c
index a1da7ef0a995..ed5bf6e06948 100644
--- a/drivers/video/tegra/dc/bandwidth.c
+++ b/drivers/video/tegra/dc/bandwidth.c
@@ -234,7 +234,8 @@ void tegra_dc_program_bandwidth(struct tegra_dc *dc)
max(dc->emc_clk_rate, dc->new_emc_clk_rate));
dc->emc_clk_rate = dc->new_emc_clk_rate;
- if (!dc->new_emc_clk_rate) /* going from non-zero to 0 */
+ /* going from non-zero to 0 */
+ if (!dc->new_emc_clk_rate && tegra_is_clk_enabled(dc->emc_clk))
clk_disable(dc->emc_clk);
}
diff --git a/drivers/video/tegra/dc/dc.c b/drivers/video/tegra/dc/dc.c
index f4844f1eecfc..f1bf5d99cc29 100644
--- a/drivers/video/tegra/dc/dc.c
+++ b/drivers/video/tegra/dc/dc.c
@@ -90,6 +90,22 @@ struct tegra_dc *tegra_dcs[TEGRA_MAX_DC];
DEFINE_MUTEX(tegra_dc_lock);
DEFINE_MUTEX(shared_lock);
+static inline void tegra_dc_clk_enable(struct tegra_dc *dc)
+{
+ if (!tegra_is_clk_enabled(dc->clk)) {
+ clk_enable(dc->clk);
+ tegra_dvfs_set_rate(dc->clk, dc->mode.pclk);
+ }
+}
+
+static inline void tegra_dc_clk_disable(struct tegra_dc *dc)
+{
+ if (tegra_is_clk_enabled(dc->clk)) {
+ clk_disable(dc->clk);
+ tegra_dvfs_set_rate(dc->clk, 0);
+ }
+}
+
#define DUMP_REG(a) do { \
snprintf(buff, sizeof(buff), "%-32s\t%03x\t%08lx\n", \
#a, a, tegra_dc_readl(dc, a)); \
@@ -131,7 +147,7 @@ static void _dump_regs(struct tegra_dc *dc, void *data,
char buff[256];
tegra_dc_io_start(dc);
- clk_enable(dc->clk);
+ tegra_dc_clk_enable(dc);
DUMP_REG(DC_CMD_DISPLAY_COMMAND_OPTION0);
DUMP_REG(DC_CMD_DISPLAY_COMMAND);
@@ -281,7 +297,7 @@ static void _dump_regs(struct tegra_dc *dc, void *data,
DUMP_REG(DC_COM_PM1_DUTY_CYCLE);
DUMP_REG(DC_DISP_SD_CONTROL);
- clk_disable(dc->clk);
+ tegra_dc_clk_disable(dc->clk);
tegra_dc_io_end(dc);
}
@@ -787,6 +803,17 @@ static void tegra_dc_set_scaling_filter(struct tegra_dc *dc)
}
}
+void tegra_dc_host_suspend(struct tegra_dc *dc)
+{
+ tegra_dsi_host_suspend(dc);
+ tegra_dc_clk_disable(dc);
+}
+
+void tegra_dc_host_resume(struct tegra_dc *dc) {
+ tegra_dc_clk_enable(dc);
+ tegra_dsi_host_resume(dc);
+}
+
static inline u32 compute_dda_inc(fixed20_12 in, unsigned out_int,
bool v, unsigned Bpp)
{
@@ -861,6 +888,9 @@ int tegra_dc_update_windows(struct tegra_dc_win *windows[], int n)
return -EFAULT;
}
+ if (dc->out->flags & TEGRA_DC_OUT_ONE_SHOT_LP_MODE)
+ tegra_dc_host_resume(dc);
+
if (no_vsync)
tegra_dc_writel(dc, WRITE_MUX_ACTIVE | READ_MUX_ACTIVE, DC_CMD_STATE_ACCESS);
else
@@ -1590,6 +1620,9 @@ tegra_dc_config_pwm(struct tegra_dc *dc, struct tegra_dc_pwm_params *cfg)
return;
}
+ if (dc->out->flags & TEGRA_DC_OUT_ONE_SHOT_LP_MODE)
+ tegra_dc_host_resume(dc);
+
ctrl = ((cfg->period << PM_PERIOD_SHIFT) |
(cfg->clk_div << PM_CLK_DIVIDER_SHIFT) |
cfg->clk_select);
@@ -1826,8 +1859,13 @@ static void tegra_dc_one_shot_worker(struct work_struct *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);
+
+ if (dc->out->flags & TEGRA_DC_OUT_ONE_SHOT_LP_MODE)
+ tegra_dc_host_suspend(dc);
+
mutex_unlock(&dc->lock);
}
@@ -2254,7 +2292,7 @@ static bool _tegra_dc_controller_enable(struct tegra_dc *dc)
dc->out->enable();
tegra_dc_setup_clk(dc, dc->clk);
- clk_enable(dc->clk);
+ tegra_dc_clk_enable(dc);
/* do not accept interrupts during initialization */
tegra_dc_writel(dc, 0, DC_CMD_INT_ENABLE);
@@ -2292,7 +2330,7 @@ static bool _tegra_dc_controller_reset_enable(struct tegra_dc *dc)
dc->out->enable();
tegra_dc_setup_clk(dc, dc->clk);
- clk_enable(dc->clk);
+ tegra_dc_clk_enable(dc);
if (dc->ndev->id == 0 && tegra_dcs[1] != NULL) {
mutex_lock(&tegra_dcs[1]->lock);
@@ -2407,8 +2445,7 @@ static void _tegra_dc_controller_disable(struct tegra_dc *dc)
disable_irq(dc->irq);
tegra_dc_clear_bandwidth(dc);
- clk_disable(dc->clk);
- tegra_dvfs_set_rate(dc->clk, 0);
+ tegra_dc_clk_disable(dc);
if (dc->out && dc->out->disable)
dc->out->disable();
@@ -2504,6 +2541,9 @@ void tegra_dc_disable(struct tegra_dc *dc)
mutex_lock(&dc->lock);
+ if (dc->out->flags & TEGRA_DC_OUT_ONE_SHOT_LP_MODE)
+ tegra_dc_host_resume(dc);
+
if (dc->enabled) {
dc->enabled = false;
@@ -2582,6 +2622,9 @@ static void tegra_dc_underflow_worker(struct work_struct *work)
to_delayed_work(work), struct tegra_dc, underflow_work);
mutex_lock(&dc->lock);
+ if (dc->out->flags & TEGRA_DC_OUT_ONE_SHOT_LP_MODE)
+ tegra_dc_host_resume(dc);
+
if (dc->enabled) {
tegra_dc_underflow_handler(dc);
}
diff --git a/drivers/video/tegra/dc/dc_priv.h b/drivers/video/tegra/dc/dc_priv.h
index 4f53f60f2599..06f2a6061bf6 100644
--- a/drivers/video/tegra/dc/dc_priv.h
+++ b/drivers/video/tegra/dc/dc_priv.h
@@ -32,6 +32,7 @@
#include "../host/host1x/host1x_syncpt.h"
#include <mach/tegra_dc_ext.h>
+#include <mach/clk.h>
#define WIN_IS_TILED(win) ((win)->flags & TEGRA_WIN_FLAG_TILED)
#define WIN_IS_ENABLED(win) ((win)->flags & TEGRA_WIN_FLAG_ENABLED)
@@ -164,6 +165,9 @@ static inline unsigned long tegra_dc_readl(struct tegra_dc *dc,
unsigned long ret;
BUG_ON(!nvhost_module_powered(nvhost_get_host(dc->ndev)->dev));
+ if (!tegra_is_clk_enabled(dc->clk))
+ WARN(1, "DC is clock-gated.\n");
+
ret = readl(dc->base + reg * 4);
trace_printk("readl %p=%#08lx\n", dc->base + reg * 4, ret);
return ret;
@@ -173,6 +177,9 @@ static inline void tegra_dc_writel(struct tegra_dc *dc, unsigned long val,
unsigned long reg)
{
BUG_ON(!nvhost_module_powered(nvhost_get_host(dc->ndev)->dev));
+ if (!tegra_is_clk_enabled(dc->clk))
+ WARN(1, "DC is clock-gated.\n");
+
trace_printk("writel %p=%#08lx\n", dc->base + reg * 4, val);
writel(val, dc->base + reg * 4);
}
diff --git a/drivers/video/tegra/dc/dsi.c b/drivers/video/tegra/dc/dsi.c
index 6624a8e8f52c..0ea3947176fe 100644
--- a/drivers/video/tegra/dc/dsi.c
+++ b/drivers/video/tegra/dc/dsi.c
@@ -416,6 +416,22 @@ static inline void tegra_dc_dsi_debug_create(struct tegra_dc_dsi_data *dsi)
{ }
#endif
+static inline void tegra_dsi_clk_enable(struct tegra_dc_dsi_data *dsi)
+{
+ if (!tegra_is_clk_enabled(dsi->dsi_clk)) {
+ clk_enable(dsi->dsi_clk);
+ clk_enable(dsi->dsi_fixed_clk);
+ }
+}
+
+static inline void tegra_dsi_clk_disable(struct tegra_dc_dsi_data *dsi)
+{
+ if (tegra_is_clk_enabled(dsi->dsi_clk)) {
+ clk_disable(dsi->dsi_clk);
+ clk_disable(dsi->dsi_fixed_clk);
+ }
+}
+
static int tegra_dsi_syncpt(struct tegra_dc_dsi_data *dsi)
{
u32 val;
@@ -1455,12 +1471,9 @@ static void tegra_dsi_set_dsi_clk(struct tegra_dc *dc,
/* Enable DSI clock */
tegra_dc_setup_clk(dc, dsi->dsi_clk);
- if (!dsi->clk_ref) {
- dsi->clk_ref = true;
- clk_enable(dsi->dsi_clk);
- clk_enable(dsi->dsi_fixed_clk);
- tegra_periph_reset_deassert(dsi->dsi_clk);
- }
+ tegra_dsi_clk_enable(dsi);
+ tegra_periph_reset_deassert(dsi->dsi_clk);
+
dsi->current_dsi_clk_khz = clk_get_rate(dsi->dsi_clk) / 1000;
dsi->current_bit_clk_ns = 1000*1000 / (dsi->current_dsi_clk_khz * 2);
}
@@ -2193,6 +2206,9 @@ int tegra_dsi_send_panel_short_cmd(struct tegra_dc *dc, u8 *pdata, u8 data_len)
int err = 0, count = 0;
struct tegra_dc_dsi_data *dsi = tegra_dc_get_outdata(dc);
+ if (dc->out->flags & TEGRA_DC_OUT_ONE_SHOT_LP_MODE)
+ tegra_dc_host_resume(dc);
+
data_len_orig = data_len;
if (pdata != NULL) {
while (data_len) {
@@ -2610,6 +2626,8 @@ static void tegra_dc_dsi_enable(struct tegra_dc *dc)
tegra_dc_io_start(dc);
mutex_lock(&dsi->lock);
+ if (dc->out->flags & TEGRA_DC_OUT_ONE_SHOT_LP_MODE)
+ tegra_dc_host_resume(dc);
/* Stop DC stream before configuring DSI registers
* to avoid visible glitches on panel during transition
* from bootloader to kernel driver
@@ -3072,10 +3090,8 @@ static int tegra_dsi_deep_sleep(struct tegra_dc *dc,
0);
/* Disable dsi source clock */
- clk_disable(dsi->dsi_clk);
- clk_disable(dsi->dsi_fixed_clk);
+ tegra_dsi_clk_disable(dsi);
- dsi->clk_ref = false;
dsi->enabled = false;
return 0;
@@ -3083,6 +3099,25 @@ fail:
return err;
}
+
+int tegra_dsi_host_suspend(struct tegra_dc *dc)
+{
+ struct tegra_dc_dsi_data *dsi = tegra_dc_get_outdata(dc);
+
+ tegra_dsi_stop_dc_stream(dc, dsi);
+
+ tegra_dsi_clk_disable(dsi);
+}
+
+void tegra_dsi_host_resume(struct tegra_dc *dc)
+{
+ struct tegra_dc_dsi_data *dsi = tegra_dc_get_outdata(dc);
+
+ tegra_dsi_clk_enable(dsi);
+
+ tegra_dsi_start_dc_stream(dc, dsi);
+}
+
static void tegra_dc_dsi_disable(struct tegra_dc *dc)
{
int err;