]> nv-tegra.nvidia Code Review - linux-2.6.git/blobdiff - drivers/video/tegra/dc/dsi.c
video: tegra: dc: power optimize DC and host1x clk
[linux-2.6.git] / drivers / video / tegra / dc / dsi.c
index 69cc60f70f1c1594c1ac5df4a7ffd7e7f1352ca9..f60184bdd42cb2150d4c0326c833c8ad6af6a677 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * drivers/video/tegra/dc/dsi.c
  *
- * Copyright (c) 2011, NVIDIA Corporation.
+ * Copyright (c) 2011-2012, NVIDIA CORPORATION, All rights reserved.
  *
  * This software is licensed under the terms of the GNU General Public
  * License version 2, as published by the Free Software Foundation, and
  *
  */
 
+#include <linux/kernel.h>
 #include <linux/clk.h>
 #include <linux/delay.h>
 #include <linux/err.h>
 #include <linux/fb.h>
 #include <linux/gpio.h>
 #include <linux/interrupt.h>
-#include <linux/kernel.h>
 #include <linux/slab.h>
 #include <linux/spinlock.h>
 #include <linux/workqueue.h>
 #include <linux/debugfs.h>
 #include <linux/seq_file.h>
+#include <linux/nvhost.h>
 
 #include <mach/clk.h>
 #include <mach/dc.h>
 #include <mach/fb.h>
 #include <mach/csi.h>
+#include <mach/iomap.h>
 #include <linux/nvhost.h>
 
 #include "dc_reg.h"
@@ -38,6 +40,9 @@
 #include "dsi_regs.h"
 #include "dsi.h"
 
+#define APB_MISC_GP_MIPI_PAD_CTRL_0    (TEGRA_APB_MISC_BASE + 0x820)
+#define DSIB_MODE_ENABLE               0x2
+
 #define DSI_USE_SYNC_POINTS            1
 #define S_TO_MS(x)                     (1000 * (x))
 
@@ -121,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;
 
@@ -142,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] = {
@@ -288,11 +294,14 @@ const u32 init_reg[] = {
        DSI_PKT_LEN_6_7,
 };
 
+static int tegra_dsi_host_suspend(struct tegra_dc *dc);
+static int tegra_dsi_host_resume(struct tegra_dc *dc);
+
 inline unsigned long tegra_dsi_readl(struct tegra_dc_dsi_data *dsi, u32 reg)
 {
        unsigned long ret;
 
-       BUG_ON(!nvhost_module_powered(nvhost_get_host(dsi->dc->ndev)->dev));
+       BUG_ON(!nvhost_module_powered_ext(nvhost_get_parent(dsi->dc->ndev)));
        ret = readl(dsi->base + reg * 4);
        trace_printk("readl %p=%#08lx\n", dsi->base + reg * 4, ret);
        return ret;
@@ -301,7 +310,7 @@ EXPORT_SYMBOL(tegra_dsi_readl);
 
 inline void tegra_dsi_writel(struct tegra_dc_dsi_data *dsi, u32 val, u32 reg)
 {
-       BUG_ON(!nvhost_module_powered(nvhost_get_host(dsi->dc->ndev)->dev));
+       BUG_ON(!nvhost_module_powered_ext(nvhost_get_parent(dsi->dc->ndev)));
        trace_printk("writel %p=%#08x\n", dsi->base + reg * 4, val);
        writel(val, dsi->base + reg * 4);
 }
@@ -325,6 +334,7 @@ static int dbg_dsi_show(struct seq_file *s, void *unused)
        DUMP_REG(DSI_CTXSW);
        DUMP_REG(DSI_POWER_CONTROL);
        DUMP_REG(DSI_INT_ENABLE);
+       DUMP_REG(DSI_HOST_DSI_CONTROL);
        DUMP_REG(DSI_CONTROL);
        DUMP_REG(DSI_SOL_DELAY);
        DUMP_REG(DSI_MAX_THRESHOLD);
@@ -412,6 +422,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;
@@ -419,17 +445,15 @@ static int tegra_dsi_syncpt(struct tegra_dc_dsi_data *dsi)
 
        ret = 0;
 
-       dsi->syncpt_val = nvhost_syncpt_read(
-                       &nvhost_get_host(dsi->dc->ndev)->syncpt,
-                       dsi->syncpt_id);
+       dsi->syncpt_val = nvhost_syncpt_read_ext(dsi->dc->ndev, 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(&nvhost_get_host(dsi->dc->ndev)->syncpt,
-               dsi->syncpt_id, dsi->syncpt_val + 1);
+       ret = nvhost_syncpt_wait_timeout_ext(dsi->dc->ndev, dsi->syncpt_id,
+               dsi->syncpt_val + 1, MAX_SCHEDULE_TIMEOUT, NULL);
        if (ret < 0) {
                dev_err(&dsi->dc->ndev->dev, "DSI sync point failure\n");
                goto fail;
@@ -1053,7 +1077,7 @@ static u32 tegra_dsi_sol_delay_burst(struct tegra_dc *dc,
        u32 dsi_to_pixel_clk_ratio;
        u32 temp;
        u32 temp1;
-       u32 mipi_clk_adj_kHz;
+       u32 mipi_clk_adj_kHz = 0;
        u32 sol_delay;
        struct tegra_dc_mode *dc_modes = &dc->mode;
 
@@ -1321,6 +1345,31 @@ static void tegra_dsi_reset_underflow_overflow
        }
 }
 
+static void tegra_dsi_soft_reset(struct tegra_dc_dsi_data *dsi)
+{
+       u32 trigger;
+
+       tegra_dsi_writel(dsi,
+               DSI_POWER_CONTROL_LEG_DSI_ENABLE(TEGRA_DSI_DISABLE),
+               DSI_POWER_CONTROL);
+       /* stabilization delay */
+       udelay(300);
+
+       tegra_dsi_writel(dsi,
+               DSI_POWER_CONTROL_LEG_DSI_ENABLE(TEGRA_DSI_ENABLE),
+               DSI_POWER_CONTROL);
+       /* stabilization delay */
+       udelay(300);
+
+       /* dsi HW does not clear host trigger bit automatically
+        * on dsi interface disable if host fifo is empty or in mid
+        * of host transmission
+        */
+       trigger = tegra_dsi_readl(dsi, DSI_TRIGGER);
+       if (trigger)
+               tegra_dsi_writel(dsi, 0x0, DSI_TRIGGER);
+}
+
 static void tegra_dsi_stop_dc_stream(struct tegra_dc *dc,
                                        struct tegra_dc_dsi_data *dsi)
 {
@@ -1339,13 +1388,12 @@ static void tegra_dsi_stop_dc_stream_at_frame_end(struct tegra_dc *dc,
        long timeout;
        u32 frame_period = DIV_ROUND_UP(S_TO_MS(1), dsi->info.refresh_rate);
 
-       /* stop dc */
-       tegra_dsi_stop_dc_stream(dc, dsi);
+       INIT_COMPLETION(dc->frame_end_complete);
 
-       /* enable frame end interrupt */
-       val = tegra_dc_readl(dc, DC_CMD_INT_MASK);
-       val |= FRAME_END_INT;
-       tegra_dc_writel(dc, val, DC_CMD_INT_MASK);
+       /* unmask frame end interrupt */
+       val = tegra_dc_unmask_interrupt(dc, FRAME_END_INT);
+
+       tegra_dsi_stop_dc_stream(dc, dsi);
 
        /* wait for frame_end completion.
         * timeout is 2 frame duration to accomodate for
@@ -1355,10 +1403,15 @@ static void tegra_dsi_stop_dc_stream_at_frame_end(struct tegra_dc *dc,
                        &dc->frame_end_complete,
                        msecs_to_jiffies(2 * frame_period));
 
-       /* disable frame end interrupt */
-       val = tegra_dc_readl(dc, DC_CMD_INT_MASK);
-       val &= ~FRAME_END_INT;
-       tegra_dc_writel(dc, val, DC_CMD_INT_MASK);
+       /* give 2 line time to dsi HW to catch up
+        * with pixels sent by dc
+        */
+       udelay(50);
+
+       tegra_dsi_soft_reset(dsi);
+
+       /* reinstate interrupt mask */
+       tegra_dc_restore_interrupt(dc, val); /* potentially a race? */
 
        if (timeout == 0)
                dev_warn(&dc->ndev->dev,
@@ -1451,12 +1504,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);
 }
@@ -1542,7 +1592,8 @@ static void tegra_dsi_set_control_reg_lp(struct tegra_dc_dsi_data *dsi)
        dsi->status.vtype = DSI_VIDEO_TYPE_NOT_INIT;
 }
 
-static void tegra_dsi_set_control_reg_hs(struct tegra_dc_dsi_data *dsi)
+static void tegra_dsi_set_control_reg_hs(struct tegra_dc_dsi_data *dsi,
+                                               u8 driven_mode)
 {
        u32 dsi_control;
        u32 host_dsi_control;
@@ -1554,7 +1605,7 @@ static void tegra_dsi_set_control_reg_hs(struct tegra_dc_dsi_data *dsi)
        max_threshold = 0;
        dcs_cmd = 0;
 
-       if (dsi->driven_mode == TEGRA_DSI_DRIVEN_BY_HOST) {
+       if (driven_mode == TEGRA_DSI_DRIVEN_BY_HOST) {
                dsi_control |= DSI_CTRL_HOST_DRIVEN;
                host_dsi_control |= HOST_DSI_CTRL_HOST_DRIVEN;
                max_threshold =
@@ -1566,17 +1617,19 @@ static void tegra_dsi_set_control_reg_hs(struct tegra_dc_dsi_data *dsi)
                max_threshold =
                        DSI_MAX_THRESHOLD_MAX_THRESHOLD(DSI_VIDEO_FIFO_DEPTH);
                dsi->status.driven = DSI_DRIVEN_MODE_DC;
-       }
 
-       if (dsi->info.video_data_type == TEGRA_DSI_VIDEO_TYPE_COMMAND_MODE) {
-               dsi_control |= DSI_CTRL_CMD_MODE;
-               dcs_cmd = DSI_DCS_CMDS_LT5_DCS_CMD(DSI_WRITE_MEMORY_START)|
-                       DSI_DCS_CMDS_LT3_DCS_CMD(DSI_WRITE_MEMORY_CONTINUE);
-               dsi->status.vtype = DSI_VIDEO_TYPE_CMD_MODE;
-
-       } else {
-               dsi_control |= DSI_CTRL_VIDEO_MODE;
-               dsi->status.vtype = DSI_VIDEO_TYPE_VIDEO_MODE;
+               if (dsi->info.video_data_type ==
+                       TEGRA_DSI_VIDEO_TYPE_COMMAND_MODE) {
+                       dsi_control |= DSI_CTRL_CMD_MODE;
+                       dcs_cmd = DSI_DCS_CMDS_LT5_DCS_CMD(
+                               DSI_WRITE_MEMORY_START)|
+                               DSI_DCS_CMDS_LT3_DCS_CMD(
+                               DSI_WRITE_MEMORY_CONTINUE);
+                       dsi->status.vtype = DSI_VIDEO_TYPE_CMD_MODE;
+               } else {
+                       dsi_control |= DSI_CTRL_VIDEO_MODE;
+                       dsi->status.vtype = DSI_VIDEO_TYPE_VIDEO_MODE;
+               }
        }
 
        tegra_dsi_writel(dsi, max_threshold, DSI_MAX_THRESHOLD);
@@ -1621,6 +1674,15 @@ static void tegra_dsi_pad_calibration(struct tegra_dc_dsi_data *dsi)
        tegra_vi_csi_writel(val, CSI_CIL_PAD_CONFIG);
 }
 
+static void tegra_dsi_panelB_enable(void)
+{
+       unsigned int val;
+
+       val = readl(IO_ADDRESS(APB_MISC_GP_MIPI_PAD_CTRL_0));
+       val |= DSIB_MODE_ENABLE;
+       writel(val, (IO_ADDRESS(APB_MISC_GP_MIPI_PAD_CTRL_0)));
+}
+
 static int tegra_dsi_init_hw(struct tegra_dc *dc,
                                                struct tegra_dc_dsi_data *dsi)
 {
@@ -1634,7 +1696,7 @@ static int tegra_dsi_init_hw(struct tegra_dc *dc,
 
        tegra_dsi_set_dsi_clk(dc, dsi, dsi->target_lp_clk_khz);
        if (dsi->info.dsi_instance) {
-               /* TODO:Set the misc register*/
+               tegra_dsi_panelB_enable();
        }
 
        /* TODO: only need to change the timing for bta */
@@ -1708,6 +1770,7 @@ static int tegra_dsi_set_to_lp_mode(struct tegra_dc *dc,
 
        dsi->status.lphs = DSI_LPHS_IN_LP_MODE;
        dsi->status.lp_op = lp_op;
+       dsi->driven_mode = TEGRA_DSI_DRIVEN_BY_HOST;
 success:
        err = 0;
 fail:
@@ -1715,7 +1778,8 @@ fail:
 }
 
 static int tegra_dsi_set_to_hs_mode(struct tegra_dc *dc,
-                                       struct tegra_dc_dsi_data *dsi)
+                                       struct tegra_dc_dsi_data *dsi,
+                                       u8 driven_mode)
 {
        int err;
 
@@ -1724,9 +1788,12 @@ static int tegra_dsi_set_to_hs_mode(struct tegra_dc *dc,
                goto fail;
        }
 
-       if (dsi->status.lphs == DSI_LPHS_IN_HS_MODE)
+       if (dsi->status.lphs == DSI_LPHS_IN_HS_MODE &&
+               dsi->driven_mode == driven_mode)
                goto success;
 
+       dsi->driven_mode = driven_mode;
+
        if (dsi->status.dc_stream == DSI_DC_STREAM_ENABLE)
                tegra_dsi_stop_dc_stream_at_frame_end(dc, dsi);
 
@@ -1741,14 +1808,14 @@ static int tegra_dsi_set_to_hs_mode(struct tegra_dc *dc,
 
        tegra_dsi_set_phy_timing(dsi, DSI_LPHS_IN_HS_MODE);
 
-       if (dsi->driven_mode == TEGRA_DSI_DRIVEN_BY_DC) {
+       if (driven_mode == TEGRA_DSI_DRIVEN_BY_DC) {
                tegra_dsi_set_pkt_seq(dc, dsi);
                tegra_dsi_set_pkt_length(dc, dsi);
                tegra_dsi_set_sol_delay(dc, dsi);
                tegra_dsi_set_dc_clk(dc, dsi);
        }
 
-       tegra_dsi_set_control_reg_hs(dsi);
+       tegra_dsi_set_control_reg_hs(dsi, driven_mode);
 
        if (dsi->status.clk_out == DSI_PHYCLK_OUT_DIS ||
                dsi->info.enable_hs_clock_on_lp_cmd_mode)
@@ -1819,35 +1886,6 @@ fail:
        return (err < 0 ? true : false);
 }
 
-static void tegra_dsi_soft_reset(struct tegra_dc_dsi_data *dsi)
-{
-       u32 trigger;
-       u32 status;
-
-       tegra_dsi_writel(dsi,
-               DSI_POWER_CONTROL_LEG_DSI_ENABLE(TEGRA_DSI_DISABLE),
-               DSI_POWER_CONTROL);
-       /* stabilization delay */
-       udelay(300);
-
-       tegra_dsi_writel(dsi,
-               DSI_POWER_CONTROL_LEG_DSI_ENABLE(TEGRA_DSI_ENABLE),
-               DSI_POWER_CONTROL);
-       /* stabilization delay */
-       udelay(300);
-
-       /* dsi HW does not clear host trigger bit automatically
-        * on dsi interface disable if host fifo is empty
-        */
-       trigger = tegra_dsi_readl(dsi, DSI_TRIGGER);
-       status = tegra_dsi_readl(dsi, DSI_STATUS);
-       if (trigger & DSI_TRIGGER_HOST_TRIGGER(0x1) &&
-               status & DSI_STATUS_IDLE(0x1)) {
-               trigger &= ~(DSI_TRIGGER_HOST_TRIGGER(0x1));
-               tegra_dsi_writel(dsi, trigger, DSI_TRIGGER);
-       }
-}
-
 static void tegra_dsi_reset_read_count(struct tegra_dc_dsi_data *dsi)
 {
        u32 val;
@@ -1866,49 +1904,42 @@ static struct dsi_status *tegra_dsi_save_state_switch_to_host_cmd_mode(
                                                struct tegra_dc *dc,
                                                u8 lp_op)
 {
-       struct dsi_status *init_status;
+       struct dsi_status *init_status = NULL;
        int err;
 
+       if (dsi->status.init != DSI_MODULE_INIT ||
+               dsi->status.lphs == DSI_LPHS_NOT_INIT) {
+               err = -EPERM;
+               goto fail;
+       }
+
        init_status = kzalloc(sizeof(*init_status), GFP_KERNEL);
        if (!init_status)
                return ERR_PTR(-ENOMEM);
 
        *init_status = dsi->status;
 
-       if (dsi->status.lphs == DSI_LPHS_IN_HS_MODE) {
-               if (dsi->status.driven == DSI_DRIVEN_MODE_DC) {
-                       if (dsi->status.dc_stream == DSI_DC_STREAM_ENABLE)
-                               tegra_dsi_stop_dc_stream_at_frame_end(dc, dsi);
-                       dsi->driven_mode = TEGRA_DSI_DRIVEN_BY_HOST;
-                       if (dsi->info.hs_cmd_mode_supported) {
-                               err = tegra_dsi_set_to_hs_mode(dc, dsi);
-                               if (err < 0) {
-                                       dev_err(&dc->ndev->dev,
-                                       "Switch to HS host mode failed\n");
-                                       goto fail;
-                               }
-                       }
-               }
-               if (!dsi->info.hs_cmd_mode_supported) {
-                       err =
-                       tegra_dsi_set_to_lp_mode(dc, dsi, lp_op);
-                       if (err < 0) {
-                               dev_err(&dc->ndev->dev,
-                               "DSI failed to go to LP mode\n");
-                               goto fail;
-                       }
-               }
-       } else if (dsi->status.lphs == DSI_LPHS_IN_LP_MODE) {
-               if (dsi->status.lp_op != lp_op) {
-                       err = tegra_dsi_set_to_lp_mode(dc, dsi, lp_op);
-                       if (err < 0) {
-                               dev_err(&dc->ndev->dev,
-                               "DSI failed to go to LP mode\n");
-                               goto fail;
-                       }
+       if (dsi->info.hs_cmd_mode_supported) {
+               err = tegra_dsi_set_to_hs_mode(dc, dsi,
+                               TEGRA_DSI_DRIVEN_BY_HOST);
+               if (err < 0) {
+                       dev_err(&dc->ndev->dev,
+                       "Switch to HS host mode failed\n");
+                       goto fail;
                }
+
+               goto success;
        }
 
+       if (dsi->status.lp_op != lp_op) {
+               err = tegra_dsi_set_to_lp_mode(dc, dsi, lp_op);
+               if (err < 0) {
+                       dev_err(&dc->ndev->dev,
+                       "DSI failed to go to LP mode\n");
+                       goto fail;
+               }
+       }
+success:
        return init_status;
 fail:
        kfree(init_status);
@@ -1922,6 +1953,7 @@ static struct dsi_status *tegra_dsi_prepare_host_transmission(
 {
        int err = 0;
        struct dsi_status *init_status;
+       bool restart_dc_stream = false;
 
        if (dsi->status.init != DSI_MODULE_INIT ||
                dsi->ulpm) {
@@ -1929,12 +1961,13 @@ static struct dsi_status *tegra_dsi_prepare_host_transmission(
                goto fail;
        }
 
+       if (dsi->status.dc_stream == DSI_DC_STREAM_ENABLE) {
+               restart_dc_stream = true;
+               tegra_dsi_stop_dc_stream_at_frame_end(dc, dsi);
+       }
+
        if (tegra_dsi_host_busy(dsi)) {
                tegra_dsi_soft_reset(dsi);
-
-               /* WAR to stop host write in middle */
-               tegra_dsi_writel(dsi, TEGRA_DSI_DISABLE, DSI_TRIGGER);
-
                if (tegra_dsi_host_busy(dsi)) {
                        err = -EBUSY;
                        dev_err(&dc->ndev->dev, "DSI host busy\n");
@@ -1961,6 +1994,9 @@ static struct dsi_status *tegra_dsi_prepare_host_transmission(
                goto fail;
        }
 
+       if (restart_dc_stream)
+               init_status->dc_stream = DSI_DC_STREAM_ENABLE;
+
        return init_status;
 fail:
        return ERR_PTR(err);
@@ -1970,50 +2006,30 @@ static int tegra_dsi_restore_state(struct tegra_dc *dc,
                                struct tegra_dc_dsi_data *dsi,
                                struct dsi_status *init_status)
 {
-       bool switch_back_to_dc_mode = false;
-       bool switch_back_to_hs_mode = false;
-       bool restart_dc_stream;
        int err = 0;
 
-       switch_back_to_dc_mode = (dsi->status.driven ==
-                               DSI_DRIVEN_MODE_HOST &&
-                               init_status->driven ==
-                               DSI_DRIVEN_MODE_DC);
-       switch_back_to_hs_mode = (dsi->status.lphs ==
-                               DSI_LPHS_IN_LP_MODE &&
-                               init_status->lphs ==
-                               DSI_LPHS_IN_HS_MODE);
-       restart_dc_stream = (dsi->status.dc_stream ==
-                               DSI_DC_STREAM_DISABLE &&
-                               init_status->dc_stream ==
-                               DSI_DC_STREAM_ENABLE);
-
-       if (dsi->status.lphs == DSI_LPHS_IN_LP_MODE &&
-               init_status->lphs == DSI_LPHS_IN_LP_MODE) {
-               if (dsi->status.lp_op != init_status->lp_op) {
-                       err =
-                       tegra_dsi_set_to_lp_mode(dc, dsi, init_status->lp_op);
-                       if (err < 0) {
-                               dev_err(&dc->ndev->dev,
-                                       "Failed to config LP mode\n");
-                               goto fail;
-                       }
+       if (init_status->lphs == DSI_LPHS_IN_LP_MODE) {
+               err = tegra_dsi_set_to_lp_mode(dc, dsi, init_status->lp_op);
+               if (err < 0) {
+                       dev_err(&dc->ndev->dev,
+                               "Failed to config LP mode\n");
+                       goto fail;
                }
                goto success;
        }
 
-       if (switch_back_to_dc_mode)
-               dsi->driven_mode = TEGRA_DSI_DRIVEN_BY_DC;
-       if (switch_back_to_dc_mode || switch_back_to_hs_mode) {
-               err = tegra_dsi_set_to_hs_mode(dc, dsi);
+       if (init_status->lphs == DSI_LPHS_IN_HS_MODE) {
+               u8 driven = (init_status->driven == DSI_DRIVEN_MODE_DC) ?
+                       TEGRA_DSI_DRIVEN_BY_DC : TEGRA_DSI_DRIVEN_BY_HOST;
+               err = tegra_dsi_set_to_hs_mode(dc, dsi, driven);
                if (err < 0) {
                        dev_err(&dc->ndev->dev, "Failed to config HS mode\n");
                        goto fail;
                }
        }
-       if (restart_dc_stream)
-               tegra_dsi_start_dc_stream(dc, dsi);
 
+       if (init_status->dc_stream == DSI_DC_STREAM_ENABLE)
+               tegra_dsi_start_dc_stream(dc, dsi);
 success:
 fail:
        kfree(init_status);
@@ -2055,7 +2071,6 @@ static int _tegra_dsi_write_data(struct tegra_dc_dsi_data *dsi,
                                        u8 *pdata, u8 data_id, u16 data_len)
 {
        u8 virtual_channel;
-       u8 *pval;
        u32 val;
        int err;
 
@@ -2078,10 +2093,9 @@ static int _tegra_dsi_write_data(struct tegra_dc_dsi_data *dsi,
                                pdata += 4;
                        } else {
                                val = 0;
-                               pval = (u8 *) &val;
-                               do
-                                       *pval++ = *pdata++;
-                               while (--data_len);
+                               memcpy(&val, pdata, data_len);
+                               pdata += data_len;
+                               data_len = 0;
                        }
                        tegra_dsi_writel(dsi, val, DSI_WR_DATA);
                }
@@ -2094,15 +2108,46 @@ static int _tegra_dsi_write_data(struct tegra_dc_dsi_data *dsi,
        return err;
 }
 
-int tegra_dsi_write_data(struct tegra_dc *dc,
+static void tegra_dc_dsi_hold_host(struct tegra_dc *dc)
+{
+       struct tegra_dc_dsi_data *dsi = tegra_dc_get_outdata(dc);
+
+       if (dc->out->flags & TEGRA_DC_OUT_ONE_SHOT_LP_MODE) {
+               /*
+                * The reference count should never be more than 1.
+                */
+               BUG_ON(tegra_is_clk_enabled(dc->clk) > 1);
+
+               if (dsi->host_suspended)
+                       tegra_dsi_host_resume(dc);
+
+               /*
+                * Take an extra refrence to count for the clk_disable in
+                * tegra_dc_release_host.
+                */
+               clk_enable(dc->clk);
+       }
+}
+
+static void tegra_dc_dsi_release_host(struct tegra_dc *dc)
+{
+       if (dc->out->flags & TEGRA_DC_OUT_ONE_SHOT_LP_MODE)
+               clk_disable(dc->clk);
+}
+
+static void tegra_dc_dsi_idle(struct tegra_dc *dc)
+{
+       if (dc->out->flags & TEGRA_DC_OUT_ONE_SHOT_LP_MODE)
+               tegra_dsi_host_suspend(dc);
+}
+
+static int tegra_dsi_write_data_nosync(struct tegra_dc *dc,
                        struct tegra_dc_dsi_data *dsi,
                        u8 *pdata, u8 data_id, u16 data_len)
 {
        int err = 0;
        struct dsi_status *init_status;
 
-       tegra_dc_io_start(dc);
-
        init_status = tegra_dsi_prepare_host_transmission(
                                dc, dsi, DSI_LP_OP_WRITE);
        if (IS_ERR_OR_NULL(init_status)) {
@@ -2116,9 +2161,27 @@ fail:
        err = tegra_dsi_restore_state(dc, dsi, init_status);
        if (err < 0)
                dev_err(&dc->ndev->dev, "Failed to restore prev state\n");
+
+       return err;
+}
+
+int tegra_dsi_write_data(struct tegra_dc *dc,
+                       struct tegra_dc_dsi_data *dsi,
+                       u8 *pdata, u8 data_id, u16 data_len)
+{
+       int err;
+
+       tegra_dc_io_start(dc);
+       tegra_dc_dsi_hold_host(dc);
+
+       err = tegra_dsi_write_data_nosync(dc, dsi, pdata, data_id, data_len);
+
+       tegra_dc_dsi_release_host(dc);
        tegra_dc_io_end(dc);
+
        return err;
 }
+
 EXPORT_SYMBOL(tegra_dsi_write_data);
 
 static int tegra_dsi_send_panel_cmd(struct tegra_dc *dc,
@@ -2137,7 +2200,7 @@ static int tegra_dsi_send_panel_cmd(struct tegra_dc *dc,
                if (cur_cmd->cmd_type == TEGRA_DSI_DELAY_MS)
                        mdelay(cur_cmd->sp_len_dly.delay_ms);
                else {
-                       err = tegra_dsi_write_data(dc, dsi,
+                       err = tegra_dsi_write_data_nosync(dc, dsi,
                                                cur_cmd->pdata,
                                                cur_cmd->data_id,
                                                cur_cmd->sp_len_dly.data_len);
@@ -2148,7 +2211,7 @@ static int tegra_dsi_send_panel_cmd(struct tegra_dc *dc,
        return err;
 }
 
-static u8 get_8bit_ecc(u32 header)
+static u8 tegra_dsi_ecc(u32 header)
 {
        char ecc_parity[24] = {
                0x07, 0x0b, 0x0d, 0x0e, 0x13, 0x15, 0x16, 0x19,
@@ -2165,72 +2228,156 @@ static u8 get_8bit_ecc(u32 header)
        return ecc_byte;
 }
 
-/* This function is written to send DCS short write (1 parameter) only.
- * This means the cmd will contain only 1 byte of index and 1 byte of value.
- * The data type ID is fixed at 0x15 and the ECC is calculated based on the
- * data in pdata.
- * The command will be sent by hardware every frame.
- * pdata should contain both the index + value for each cmd.
- * data_len will be the total number of bytes in pdata.
- */
-int tegra_dsi_send_panel_short_cmd(struct tegra_dc *dc, u8 *pdata, u8 data_len)
+static u16 tegra_dsi_cs(char *pdata, u16 data_len)
 {
-       u8 ecc8bits = 0, data_len_orig = 0;
-       u32 val = 0, pkthdr = 0;
-       int err = 0, count = 0;
-       struct tegra_dc_dsi_data *dsi = tegra_dc_get_outdata(dc);
+       u16 byte_cnt;
+       u8 bit_cnt;
+       char curr_byte;
+       u16 crc = 0xFFFF;
+       u16 poly = 0x8408;
 
-       data_len_orig = data_len;
-       if (pdata != NULL) {
-               while (data_len) {
-                       if (data_len >= 2) {
-                               pkthdr = (CMD_SHORTW |
-                                       (((u16 *)pdata)[0]) << 8 | 0x00 << 24);
-                               ecc8bits = get_8bit_ecc(pkthdr);
-                               val = (pkthdr | (ecc8bits << 24));
-                               data_len -= 2;
-                               pdata += 2;
-                               count++;
+       if (data_len > 0) {
+               for (byte_cnt = 0; byte_cnt < data_len; byte_cnt++) {
+                       curr_byte = pdata[byte_cnt];
+                       for (bit_cnt = 0; bit_cnt < 8; bit_cnt++) {
+                               if (((crc & 0x0001 ) ^
+                                       (curr_byte & 0x0001)) > 0)
+                                       crc = ((crc >> 1) & 0x7FFF) ^ poly;
+                               else
+                                       crc = (crc >> 1) & 0x7FFF;
+
+                               curr_byte = (curr_byte >> 1 ) & 0x7F;
                        }
-                       switch (count) {
-                       case 1:
-                               tegra_dsi_writel(dsi, val, DSI_INIT_SEQ_DATA_0);
-                               break;
-                       case 2:
-                               tegra_dsi_writel(dsi, val, DSI_INIT_SEQ_DATA_1);
-                               break;
-                       case 3:
-                               tegra_dsi_writel(dsi, val, DSI_INIT_SEQ_DATA_2);
-                               break;
-                       case 4:
-                               tegra_dsi_writel(dsi, val, DSI_INIT_SEQ_DATA_3);
-                               break;
-                       case 5:
-                               tegra_dsi_writel(dsi, val, DSI_INIT_SEQ_DATA_4);
-                               break;
-                       case 6:
-                               tegra_dsi_writel(dsi, val, DSI_INIT_SEQ_DATA_5);
-                               break;
-                       case 7:
-                               tegra_dsi_writel(dsi, val, DSI_INIT_SEQ_DATA_6);
-                               break;
-                       case 8:
-                               tegra_dsi_writel(dsi, val, DSI_INIT_SEQ_DATA_7);
-                               break;
-                       default:
-                               err = 1;
-                               break;
+               }
+       }
+       return crc;
+}
+
+static int tegra_dsi_dcs_pkt_seq_ctrl_init(struct tegra_dc_dsi_data *dsi,
+                                               struct tegra_dsi_cmd *cmd)
+{
+       u8 virtual_channel;
+       u32 val;
+       u16 data_len = cmd->sp_len_dly.data_len;
+       u8 seq_ctrl_reg = 0;
+
+       virtual_channel = dsi->info.virtual_channel <<
+                               DSI_VIR_CHANNEL_BIT_POSITION;
+
+       val = (virtual_channel | cmd->data_id) << 0 |
+               data_len << 8;
+
+       val |= tegra_dsi_ecc(val) << 24;
+
+       tegra_dsi_writel(dsi, val, DSI_INIT_SEQ_DATA_0 + seq_ctrl_reg++);
+
+       /* if pdata != NULL, pkt type is long pkt */
+       if (cmd->pdata != NULL) {
+               u8 *pdata;
+               u8 *pdata_mem;
+               /*  allocate memory for pdata + 2 bytes checksum */
+               pdata_mem = kzalloc(sizeof(u8) * data_len + 2, GFP_KERNEL);
+               if (!pdata_mem) {
+                       dev_err(&dsi->dc->ndev->dev, "dsi: memory err\n");
+                       tegra_dsi_soft_reset(dsi);
+                       return -ENOMEM;
+               }
+
+               memcpy(pdata_mem, cmd->pdata, data_len);
+               pdata = pdata_mem;
+               *((u16 *)(pdata + data_len)) = tegra_dsi_cs(pdata, data_len);
+
+               /* data_len = length of pdata + 2 byte checksum */
+               data_len += 2;
+
+               while (data_len) {
+                       if (data_len >= 4) {
+                               val = ((u32 *) pdata)[0];
+                               data_len -= 4;
+                               pdata += 4;
+                       } else {
+                               val = 0;
+                               memcpy(&val, pdata, data_len);
+                               pdata += data_len;
+                               data_len = 0;
                        }
+                       tegra_dsi_writel(dsi, val, DSI_INIT_SEQ_DATA_0 +
+                                                       seq_ctrl_reg++);
                }
+               kfree(pdata_mem);
+       }
+
+       return 0;
+}
+
+int tegra_dsi_start_host_cmd_v_blank_dcs(struct tegra_dc_dsi_data * dsi,
+                                               struct tegra_dsi_cmd *cmd)
+{
+#define PKT_HEADER_LEN_BYTE    4
+#define CHECKSUM_LEN_BYTE      2
+
+       int err = 0;
+       u32 val;
+       u16 tot_pkt_len = PKT_HEADER_LEN_BYTE;
+       struct tegra_dc *dc = dsi->dc;
+
+       if (cmd->cmd_type != TEGRA_DSI_PACKET_CMD)
+               return -EINVAL;
+
+       mutex_lock(&dsi->lock);
+       tegra_dc_io_start(dc);
+       tegra_dc_dsi_hold_host(dc);
+
+
+       err = tegra_dsi_dcs_pkt_seq_ctrl_init(dsi, cmd);
+       if (err < 0) {
+               dev_err(&dsi->dc->ndev->dev,
+                       "dsi: dcs pkt seq ctrl init failed\n");
+               goto fail;
        }
 
-       val = DSI_INIT_SEQ_CONTROL_DSI_FRAME_INIT_BYTE_COUNT(data_len_orig * 2)
-               | DSI_INIT_SEQ_CONTROL_DSI_SEND_INIT_SEQUENCE(1);
+       if (cmd->pdata) {
+               u16 data_len = cmd->sp_len_dly.data_len;
+               tot_pkt_len += data_len + CHECKSUM_LEN_BYTE;
+       }
+
+       val = DSI_INIT_SEQ_CONTROL_DSI_FRAME_INIT_BYTE_COUNT(tot_pkt_len) |
+               DSI_INIT_SEQ_CONTROL_DSI_SEND_INIT_SEQUENCE(
+                                               TEGRA_DSI_ENABLE);
        tegra_dsi_writel(dsi, val, DSI_INIT_SEQ_CONTROL);
 
+fail:
+       tegra_dc_dsi_release_host(dc);
+       tegra_dc_io_end(dc);
+       mutex_unlock(&dsi->lock);
        return err;
+
+#undef PKT_HEADER_LEN_BYTE
+#undef CHECKSUM_LEN_BYTE
 }
-EXPORT_SYMBOL(tegra_dsi_send_panel_short_cmd);
+EXPORT_SYMBOL(tegra_dsi_start_host_cmd_v_blank_dcs);
+
+void tegra_dsi_stop_host_cmd_v_blank_dcs(struct tegra_dc_dsi_data * dsi)
+{
+       struct tegra_dc *dc = dsi->dc;
+       u32 cnt;
+
+       mutex_lock(&dsi->lock);
+       tegra_dc_io_start(dc);
+       tegra_dc_dsi_hold_host(dc);
+
+       tegra_dsi_writel(dsi, TEGRA_DSI_DISABLE, DSI_INIT_SEQ_CONTROL);
+
+       /* clear seq data registers */
+       for (cnt = 0; cnt < 8; cnt++)
+               tegra_dsi_writel(dsi, 0, DSI_INIT_SEQ_DATA_0 + cnt);
+
+       tegra_dc_dsi_release_host(dc);
+       tegra_dc_io_end(dc);
+
+       mutex_unlock(&dsi->lock);
+}
+EXPORT_SYMBOL(tegra_dsi_stop_host_cmd_v_blank_dcs);
 
 static int tegra_dsi_bta(struct tegra_dc_dsi_data *dsi)
 {
@@ -2246,9 +2393,7 @@ static int tegra_dsi_bta(struct tegra_dc_dsi_data *dsi)
        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(
-                       &nvhost_get_host(dsi->dc->ndev)->syncpt,
+       dsi->syncpt_val = nvhost_syncpt_read_ext(dsi->dc->ndev,
                        dsi->syncpt_id);
 
        val = DSI_INCR_SYNCPT_COND(OP_DONE) |
@@ -2256,8 +2401,8 @@ static int tegra_dsi_bta(struct tegra_dc_dsi_data *dsi)
        tegra_dsi_writel(dsi, val, DSI_INCR_SYNCPT);
 
        /* TODO: Use interrupt rather than polling */
-       err = nvhost_syncpt_wait(&nvhost_get_host(dsi->dc->ndev)->syncpt,
-               dsi->syncpt_id, dsi->syncpt_val + 1);
+       err = nvhost_syncpt_wait_timeout_ext(dsi->dc->ndev, dsi->syncpt_id,
+               dsi->syncpt_val + 1, MAX_SCHEDULE_TIMEOUT, NULL);
        if (err < 0)
                dev_err(&dsi->dc->ndev->dev,
                        "DSI sync point failure\n");
@@ -2402,6 +2547,7 @@ int tegra_dsi_read_data(struct tegra_dc *dc,
        int err = 0;
        struct dsi_status *init_status;
 
+       mutex_lock(&dsi->lock);
        tegra_dc_io_start(dc);
 
        init_status = tegra_dsi_prepare_host_transmission(
@@ -2460,6 +2606,7 @@ fail:
        if (err < 0)
                dev_err(&dc->ndev->dev, "Failed to restore prev state\n");
        tegra_dc_io_end(dc);
+       mutex_unlock(&dsi->lock);
        return err;
 }
 EXPORT_SYMBOL(tegra_dsi_read_data);
@@ -2588,14 +2735,63 @@ fail:
 
 }
 
+static void tegra_dsi_send_dc_frames(struct tegra_dc *dc,
+                                    struct tegra_dc_dsi_data *dsi,
+                                    int no_of_frames)
+{
+       int err;
+       u32 frame_period = DIV_ROUND_UP(S_TO_MS(1), dsi->info.refresh_rate);
+       u8 lp_op = dsi->status.lp_op;
+       bool switch_to_lp = (dsi->status.lphs == DSI_LPHS_IN_LP_MODE);
+
+       if (dsi->status.lphs != DSI_LPHS_IN_HS_MODE) {
+               err = tegra_dsi_set_to_hs_mode(dc, dsi,
+                               TEGRA_DSI_DRIVEN_BY_DC);
+               if (err < 0) {
+                       dev_err(&dc->ndev->dev,
+                               "Switch to HS host mode failed\n");
+                       return;
+               }
+       }
+
+       /*
+        * Some panels need DC frames be sent under certain
+        * conditions. We are working on the right fix for this
+        * requirement, while using this current fix.
+        */
+       tegra_dsi_start_dc_stream(dc, dsi);
+
+       /*
+        * Send frames in Continuous or One-shot mode.
+        */
+       if (dc->out->flags & TEGRA_DC_OUT_ONE_SHOT_MODE) {
+               while (no_of_frames--) {
+                       tegra_dc_writel(dc, GENERAL_ACT_REQ | NC_HOST_TRIG,
+                                       DC_CMD_STATE_CONTROL);
+                       mdelay(frame_period);
+               }
+       } else
+               mdelay(no_of_frames * frame_period);
+
+       tegra_dsi_stop_dc_stream_at_frame_end(dc, dsi);
+
+       if (switch_to_lp) {
+               err = tegra_dsi_set_to_lp_mode(dc, dsi, lp_op);
+               if (err < 0)
+                       dev_err(&dc->ndev->dev,
+                               "DSI failed to go to LP mode\n");
+       }
+}
+
 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);
+       tegra_dc_io_start(dc);
+       tegra_dc_dsi_hold_host(dc);
 
        /* Stop DC stream before configuring DSI registers
         * to avoid visible glitches on panel during transition
@@ -2613,6 +2809,13 @@ static void tegra_dc_dsi_enable(struct tegra_dc *dc)
                }
 
                if (dsi->info.panel_reset) {
+                       /*
+                        * Certain panels need dc frames be sent before
+                        * waking panel.
+                        */
+                       if (dsi->info.panel_send_dc_frames)
+                               tegra_dsi_send_dc_frames(dc, dsi, 2);
+
                        err = tegra_dsi_send_panel_cmd(dc, dsi,
                                                        dsi->info.dsi_init_cmd,
                                                        dsi->info.n_init_cmd);
@@ -2666,6 +2869,13 @@ static void tegra_dc_dsi_enable(struct tegra_dc *dc)
                        }
                }
 
+               /*
+                * Certain panels need dc frames be sent before
+                * waking panel.
+                */
+               if (dsi->info.panel_send_dc_frames)
+                       tegra_dsi_send_dc_frames(dc, dsi, 2);
+
                err = tegra_dsi_set_to_lp_mode(dc, dsi, DSI_LP_OP_WRITE);
                if (err < 0) {
                        dev_err(&dc->ndev->dev,
@@ -2681,7 +2891,8 @@ static void tegra_dc_dsi_enable(struct tegra_dc *dc)
                        goto fail;
                }
 
-               err = tegra_dsi_set_to_hs_mode(dc, dsi);
+               err = tegra_dsi_set_to_hs_mode(dc, dsi,
+                               TEGRA_DSI_DRIVEN_BY_DC);
                if (err < 0) {
                        dev_err(&dc->ndev->dev,
                                "dsi: not able to set to hs mode\n");
@@ -2694,8 +2905,9 @@ static void tegra_dc_dsi_enable(struct tegra_dc *dc)
        if (dsi->status.driven == DSI_DRIVEN_MODE_DC)
                tegra_dsi_start_dc_stream(dc, dsi);
 fail:
-       mutex_unlock(&dsi->lock);
+       tegra_dc_dsi_release_host(dc);
        tegra_dc_io_end(dc);
+       mutex_unlock(&dsi->lock);
 }
 
 static void _tegra_dc_dsi_init(struct tegra_dc *dc)
@@ -2738,8 +2950,8 @@ static int tegra_dc_dsi_cp_info(struct tegra_dc_dsi_data *dsi,
                                        struct tegra_dsi_out *p_dsi)
 {
        struct tegra_dsi_cmd *p_init_cmd;
-       struct tegra_dsi_cmd *p_early_suspend_cmd;
-       struct tegra_dsi_cmd *p_late_resume_cmd;
+       struct tegra_dsi_cmd *p_early_suspend_cmd = NULL;
+       struct tegra_dsi_cmd *p_late_resume_cmd = NULL;
        struct tegra_dsi_cmd *p_suspend_cmd;
        int err;
 
@@ -2993,79 +3205,181 @@ 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;
 
+       /* 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);
+}
+
+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;
+
        if (!dsi->enabled) {
                err = -EPERM;
                goto fail;
        }
 
-       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;
-       }
+       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 go to LP mode\n");
+                       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 sending suspend cmd\n");
-               goto fail;
-       }
+               err = tegra_dsi_send_panel_cmd(dc, dsi,
+                               dsi->info.dsi_suspend_cmd,
+                               dsi->info.n_suspend_cmd);
+               /*
+                * Certain panels need dc frames be sent after
+                * putting panel to sleep.
+                */
+               if (dsi->info.panel_send_dc_frames)
+                       tegra_dsi_send_dc_frames(dc, dsi, 2);
 
-       if (!dsi->ulpm) {
-               err = tegra_dsi_enter_ulpm(dsi);
                if (err < 0) {
                        dev_err(&dc->ndev->dev,
-                               "DSI failed to enter ulpm\n");
+                               "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;
+                       }
+               }
+
+               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");
        }
 
-       /*
-        * 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);
+       dsi->enabled = false;
 
-       /* Suspend core-logic */
-       val = DSI_POWER_CONTROL_LEG_DSI_ENABLE(TEGRA_DSI_DISABLE);
-       tegra_dsi_writel(dsi, val, DSI_POWER_CONTROL);
+       return 0;
+fail:
+       return err;
+}
 
-       /* 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);
+static int tegra_dsi_host_suspend(struct tegra_dc *dc)
+{
+       int err = 0;
+       struct tegra_dc_dsi_data *dsi = tegra_dc_get_outdata(dc);
 
-       /* Disable dsi source clock */
-       clk_disable(dsi->dsi_clk);
-       clk_disable(dsi->dsi_fixed_clk);
+       if (dsi->host_suspended)
+               return 0;
 
-       dsi->clk_ref = false;
-       dsi->enabled = false;
+       dsi->host_suspended = true;
 
-       return 0;
+       tegra_dsi_stop_dc_stream(dc, 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");
+
+       tegra_dc_clk_disable(dc);
+
+       return err;
+}
+
+static 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);
+
+       if (!dsi->host_suspended)
+               return 0;
+
+       tegra_dc_clk_enable(dc);
+       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");
+       }
+
+       tegra_dsi_start_dc_stream(dc, dsi);
+       dsi->enabled = true;
+       dsi->host_suspended = false;
 fail:
        return err;
 }
@@ -3075,14 +3389,14 @@ static void tegra_dc_dsi_disable(struct tegra_dc *dc)
        int err;
        struct tegra_dc_dsi_data *dsi = tegra_dc_get_outdata(dc);
 
-       tegra_dc_io_start(dc);
        mutex_lock(&dsi->lock);
+       tegra_dc_io_start(dc);
 
        if (dsi->status.dc_stream == DSI_DC_STREAM_ENABLE)
                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;
@@ -3107,7 +3421,6 @@ static void tegra_dc_dsi_disable(struct tegra_dc *dc)
                        }
                }
        }
-
 fail:
        mutex_unlock(&dsi->lock);
        tegra_dc_io_end(dc);
@@ -3135,7 +3448,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;
@@ -3159,6 +3472,9 @@ struct tegra_dc_out_ops tegra_dc_dsi_ops = {
        .destroy = tegra_dc_dsi_destroy,
        .enable = tegra_dc_dsi_enable,
        .disable = tegra_dc_dsi_disable,
+       .hold = tegra_dc_dsi_hold_host,
+       .release = tegra_dc_dsi_release_host,
+       .idle = tegra_dc_dsi_idle,
 #ifdef CONFIG_PM
        .suspend = tegra_dc_dsi_suspend,
        .resume = tegra_dc_dsi_resume,