video: tegra: dc: Clock-gate display modules dynamically.
Kevin Huang [Wed, 6 Jun 2012 17:48:18 +0000 (10:48 -0700)]
Bug 936337
Bug 899053

Change-Id: I2b3d8cfc8a00881338c1e17d03f2844d15ba7d3e
Signed-off-by: Kevin Huang <kevinh@nvidia.com>
Reviewed-on: http://git-master/r/106313
Reviewed-by: Simone Willett <swillett@nvidia.com>
Tested-by: Simone Willett <swillett@nvidia.com>

arch/arm/mach-tegra/include/mach/dc.h
drivers/video/tegra/dc/bandwidth.c
drivers/video/tegra/dc/dc.c
drivers/video/tegra/dc/dc_priv.h
drivers/video/tegra/dc/dsi.c

index 87af944..8f8b0b7 100644 (file)
@@ -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);
 
index a1da7ef..ed5bf6e 100644 (file)
@@ -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);
        }
 
index f4844f1..f1bf5d9 100644 (file)
@@ -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);
        }
index 4f53f60..06f2a60 100644 (file)
@@ -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);
 }
index 6624a8e..0ea3947 100644 (file)
@@ -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;