video: tegra: dc: resolve deltas - address past merge issues
[linux-3.10.git] / drivers / video / tegra / dc / dc.c
index 64bda03..a8f6833 100644 (file)
@@ -4,7 +4,7 @@
  * Copyright (C) 2010 Google, Inc.
  * Author: Erik Gilling <konkers@android.com>
  *
- * Copyright (C) 2010-2012 NVIDIA Corporation
+ * Copyright (c) 2010-2013, 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
@@ -24,6 +24,8 @@
 #include <linux/interrupt.h>
 #include <linux/slab.h>
 #include <linux/io.h>
+#include <linux/pm.h>
+#include <linux/pm_runtime.h>
 #include <linux/clk.h>
 #include <linux/mutex.h>
 #include <linux/delay.h>
 #include <linux/switch.h>
 #endif
 
+#define CREATE_TRACE_POINTS
+#include <trace/events/display.h>
+
 #include <mach/clk.h>
 #include <mach/dc.h>
 #include <mach/fb.h>
 #include <mach/mc.h>
 #include <linux/nvhost.h>
 #include <mach/latency_allowance.h>
+#include <mach/pm_domains.h>
 
 #include "dc_reg.h"
 #include "dc_config.h"
 #include "dev.h"
 #include "nvsd.h"
 
+/* HACK! This needs to come from DT */
+#include "../../../../arch/arm/mach-tegra/iomap.h"
+
 #define TEGRA_CRC_LATCHED_DELAY                34
 
 #define DC_COM_PIN_OUTPUT_POLARITY1_INIT_VAL   0x01000000
 #define DC_COM_PIN_OUTPUT_POLARITY3_INIT_VAL   0x0
 
-#ifndef CONFIG_TEGRA_FPGA_PLATFORM
-#define ALL_UF_INT (WIN_A_UF_INT | WIN_B_UF_INT | WIN_C_UF_INT)
-#else
-/* ignore underflows when on simulation and fpga platform */
-#define ALL_UF_INT (0)
-#endif
-
-static int no_vsync;
 static struct fb_videomode tegra_dc_hdmi_fallback_mode = {
        .refresh = 60,
        .xres = 640,
@@ -82,9 +83,9 @@ static struct fb_videomode tegra_dc_hdmi_fallback_mode = {
        .sync = 0,
 };
 
-static void _tegra_dc_controller_disable(struct tegra_dc *dc);
+static struct tegra_dc_mode override_disp_mode[3];
 
-module_param_named(no_vsync, no_vsync, int, S_IRUGO | S_IWUSR);
+static void _tegra_dc_controller_disable(struct tegra_dc *dc);
 
 struct tegra_dc *tegra_dcs[TEGRA_MAX_DC];
 
@@ -270,66 +271,240 @@ static struct tegra_dc_cmu default_cmu = {
                253,  253,  254,  254,  254,  254,  255,  255,
        },
 };
+
+static struct tegra_dc_cmu default_limited_cmu = {
+       /* lut1 maps sRGB to linear space. */
+       {
+               0,    1,    2,    4,    5,    6,    7,    9,
+               10,   11,   12,   14,   15,   16,   18,   20,
+               21,   23,   25,   27,   29,   31,   33,   35,
+               37,   40,   42,   45,   48,   50,   53,   56,
+               59,   62,   66,   69,   72,   76,   79,   83,
+               87,   91,   95,   99,   103,  107,  112,  116,
+               121,  126,  131,  136,  141,  146,  151,  156,
+               162,  168,  173,  179,  185,  191,  197,  204,
+               210,  216,  223,  230,  237,  244,  251,  258,
+               265,  273,  280,  288,  296,  304,  312,  320,
+               329,  337,  346,  354,  363,  372,  381,  390,
+               400,  409,  419,  428,  438,  448,  458,  469,
+               479,  490,  500,  511,  522,  533,  544,  555,
+               567,  578,  590,  602,  614,  626,  639,  651,
+               664,  676,  689,  702,  715,  728,  742,  755,
+               769,  783,  797,  811,  825,  840,  854,  869,
+               884,  899,  914,  929,  945,  960,  976,  992,
+               1008, 1024, 1041, 1057, 1074, 1091, 1108, 1125,
+               1142, 1159, 1177, 1195, 1213, 1231, 1249, 1267,
+               1286, 1304, 1323, 1342, 1361, 1381, 1400, 1420,
+               1440, 1459, 1480, 1500, 1520, 1541, 1562, 1582,
+               1603, 1625, 1646, 1668, 1689, 1711, 1733, 1755,
+               1778, 1800, 1823, 1846, 1869, 1892, 1916, 1939,
+               1963, 1987, 2011, 2035, 2059, 2084, 2109, 2133,
+               2159, 2184, 2209, 2235, 2260, 2286, 2312, 2339,
+               2365, 2392, 2419, 2446, 2473, 2500, 2527, 2555,
+               2583, 2611, 2639, 2668, 2696, 2725, 2754, 2783,
+               2812, 2841, 2871, 2901, 2931, 2961, 2991, 3022,
+               3052, 3083, 3114, 3146, 3177, 3209, 3240, 3272,
+               3304, 3337, 3369, 3402, 3435, 3468, 3501, 3535,
+               3568, 3602, 3636, 3670, 3705, 3739, 3774, 3809,
+               3844, 3879, 3915, 3950, 3986, 4022, 4059, 4095,
+       },
+       /* csc */
+       {
+               0x100, 0x000, 0x000,
+               0x000, 0x100, 0x000,
+               0x000, 0x000, 0x100,
+       },
+       /*
+        * lut2 maps linear space back to sRGB, where
+        * the output range is [16...235] (limited).
+        */
+       {
+               16,  17,  17,  18,  19,  19,  20,  21,
+               22,  22,  23,  24,  24,  25,  26,  26,
+               27,  27,  28,  29,  29,  30,  30,  31,
+               31,  32,  32,  32,  33,  33,  34,  34,
+               35,  35,  35,  36,  36,  36,  37,  37,
+               38,  38,  38,  39,  39,  39,  40,  40,
+               40,  41,  41,  41,  41,  42,  42,  42,
+               43,  43,  43,  43,  44,  44,  44,  45,
+               45,  45,  45,  46,  46,  46,  46,  47,
+               47,  47,  47,  48,  48,  48,  48,  49,
+               49,  49,  49,  49,  50,  50,  50,  50,
+               51,  51,  51,  51,  51,  52,  52,  52,
+               52,  53,  53,  53,  53,  53,  54,  54,
+               54,  54,  54,  55,  55,  55,  55,  55,
+               56,  56,  56,  56,  56,  56,  57,  57,
+               57,  57,  57,  58,  58,  58,  58,  58,
+               58,  59,  59,  59,  59,  59,  60,  60,
+               60,  60,  60,  60,  61,  61,  61,  61,
+               61,  61,  62,  62,  62,  62,  62,  62,
+               63,  63,  63,  63,  63,  63,  63,  64,
+               64,  64,  64,  64,  64,  65,  65,  65,
+               65,  65,  65,  65,  66,  66,  66,  66,
+               66,  66,  67,  67,  67,  67,  67,  67,
+               67,  68,  68,  68,  68,  68,  68,  68,
+               69,  69,  69,  69,  69,  69,  69,  69,
+               70,  70,  70,  70,  70,  70,  70,  71,
+               71,  71,  71,  71,  71,  71,  72,  72,
+               72,  72,  72,  72,  72,  72,  73,  73,
+               73,  73,  73,  73,  73,  73,  74,  74,
+               74,  74,  74,  74,  74,  74,  75,  75,
+               75,  75,  75,  75,  75,  75,  76,  76,
+               76,  76,  76,  76,  76,  76,  76,  77,
+               77,  77,  77,  77,  77,  77,  77,  78,
+               78,  78,  78,  78,  78,  78,  78,  78,
+               79,  79,  79,  79,  79,  79,  79,  79,
+               80,  80,  80,  80,  80,  80,  80,  80,
+               80,  81,  81,  81,  81,  81,  81,  81,
+               81,  81,  81,  82,  82,  82,  82,  82,
+               82,  82,  82,  82,  83,  83,  83,  83,
+               83,  83,  83,  83,  83,  84,  84,  84,
+               84,  84,  84,  84,  84,  84,  84,  85,
+               85,  85,  85,  85,  85,  85,  85,  85,
+               85,  86,  86,  86,  86,  86,  86,  86,
+               86,  86,  86,  87,  87,  87,  87,  87,
+               87,  87,  87,  87,  87,  88,  88,  88,
+               88,  88,  88,  88,  88,  88,  88,  89,
+               89,  89,  89,  89,  89,  89,  89,  89,
+               89,  89,  90,  90,  90,  90,  90,  90,
+               90,  90,  90,  90,  91,  91,  91,  91,
+               91,  91,  91,  91,  91,  91,  91,  92,
+               92,  92,  92,  92,  92,  92,  92,  92,
+               92,  92,  93,  93,  93,  93,  93,  93,
+               93,  93,  93,  93,  93,  94,  94,  94,
+               94,  94,  94,  94,  94,  94,  94,  94,
+               94,  95,  95,  95,  95,  95,  95,  95,
+               95,  95,  95,  95,  96,  96,  96,  96,
+               96,  96,  96,  96,  96,  96,  96,  96,
+               97,  97,  97,  97,  97,  97,  97,  97,
+               97,  97,  97,  97,  98,  98,  98,  98,
+               98,  98,  98,  98,  98,  98,  98,  98,
+               99,  99,  99,  99,  99,  99,  99,  99,
+               99,  99,  99,  99, 100, 100, 100, 100,
+               100, 100, 100, 100, 100, 100, 100, 100,
+               100, 101, 101, 101, 101, 101, 101, 101,
+               102, 102, 103, 104, 104, 105, 105, 106,
+               107, 107, 108, 108, 109, 109, 110, 111,
+               111, 112, 112, 113, 113, 114, 114, 115,
+               115, 116, 116, 117, 117, 118, 118, 119,
+               119, 120, 120, 121, 121, 122, 122, 123,
+               123, 124, 124, 125, 125, 126, 126, 127,
+               127, 127, 128, 128, 129, 129, 130, 130,
+               131, 131, 131, 132, 132, 133, 133, 134,
+               134, 134, 135, 135, 136, 136, 136, 137,
+               137, 138, 138, 139, 139, 139, 140, 140,
+               141, 141, 141, 142, 142, 142, 143, 143,
+               144, 144, 144, 145, 145, 145, 146, 146,
+               147, 147, 147, 148, 148, 148, 149, 149,
+               150, 150, 150, 151, 151, 151, 152, 152,
+               152, 153, 153, 153, 154, 154, 154, 155,
+               155, 155, 156, 156, 156, 157, 157, 157,
+               158, 158, 158, 159, 159, 159, 160, 160,
+               160, 161, 161, 161, 162, 162, 162, 163,
+               163, 163, 164, 164, 164, 165, 165, 165,
+               166, 166, 166, 166, 167, 167, 167, 168,
+               168, 168, 169, 169, 169, 169, 170, 170,
+               170, 171, 171, 171, 172, 172, 172, 172,
+               173, 173, 173, 174, 174, 174, 174, 175,
+               175, 175, 176, 176, 176, 176, 177, 177,
+               177, 178, 178, 178, 178, 179, 179, 179,
+               180, 180, 180, 180, 181, 181, 181, 181,
+               182, 182, 182, 183, 183, 183, 183, 184,
+               184, 184, 184, 185, 185, 185, 185, 186,
+               186, 186, 187, 187, 187, 187, 188, 188,
+               188, 188, 189, 189, 189, 189, 190, 190,
+               190, 190, 191, 191, 191, 191, 192, 192,
+               192, 192, 193, 193, 193, 193, 194, 194,
+               194, 194, 195, 195, 195, 195, 196, 196,
+               196, 196, 197, 197, 197, 197, 198, 198,
+               198, 198, 199, 199, 199, 199, 199, 200,
+               200, 200, 200, 201, 201, 201, 201, 202,
+               202, 202, 202, 203, 203, 203, 203, 203,
+               204, 204, 204, 204, 205, 205, 205, 205,
+               206, 206, 206, 206, 206, 207, 207, 207,
+               207, 208, 208, 208, 208, 208, 209, 209,
+               209, 209, 210, 210, 210, 210, 210, 211,
+               211, 211, 211, 212, 212, 212, 212, 212,
+               213, 213, 213, 213, 213, 214, 214, 214,
+               214, 215, 215, 215, 215, 215, 216, 216,
+               216, 216, 216, 217, 217, 217, 217, 218,
+               218, 218, 218, 218, 219, 219, 219, 219,
+               219, 220, 220, 220, 220, 220, 221, 221,
+               221, 221, 221, 222, 222, 222, 222, 222,
+               223, 223, 223, 223, 224, 224, 224, 224,
+               224, 225, 225, 225, 225, 225, 226, 226,
+               226, 226, 226, 227, 227, 227, 227, 227,
+               227, 228, 228, 228, 228, 228, 229, 229,
+               229, 229, 229, 230, 230, 230, 230, 230,
+               231, 231, 231, 231, 231, 232, 232, 232,
+               232, 232, 233, 233, 233, 233, 233, 233,
+               234, 234, 234, 234, 234, 235, 235, 235,
+       },
+};
 #endif
 
-static inline void tegra_dc_clk_enable(struct tegra_dc *dc)
+void tegra_dc_clk_enable(struct tegra_dc *dc)
 {
        if (!tegra_is_clk_enabled(dc->clk)) {
-               clk_enable(dc->clk);
+               clk_prepare_enable(dc->clk);
                tegra_dvfs_set_rate(dc->clk, dc->mode.pclk);
        }
 }
 
-static inline void tegra_dc_clk_disable(struct tegra_dc *dc)
+void tegra_dc_clk_disable(struct tegra_dc *dc)
 {
        if (tegra_is_clk_enabled(dc->clk)) {
-               clk_disable(dc->clk);
+               clk_disable_unprepare(dc->clk);
                tegra_dvfs_set_rate(dc->clk, 0);
        }
 }
 
+void tegra_dc_get(struct tegra_dc *dc)
+{
+       tegra_dc_io_start(dc);
+
+       /* extra reference to dc clk */
+       clk_prepare_enable(dc->clk);
+}
+
+void tegra_dc_put(struct tegra_dc *dc)
+{
+       /* balance extra dc clk reference */
+       clk_disable_unprepare(dc->clk);
+
+       tegra_dc_io_end(dc);
+}
+
+void tegra_dc_hold_dc_out(struct tegra_dc *dc)
+{
+       tegra_dc_get(dc);
+       if (dc->out_ops->hold)
+               dc->out_ops->hold(dc);
+}
+
+void tegra_dc_release_dc_out(struct tegra_dc *dc)
+{
+       if (dc->out_ops->release)
+               dc->out_ops->release(dc);
+       tegra_dc_put(dc);
+}
+
 #define DUMP_REG(a) do {                       \
-       snprintf(buff, sizeof(buff), "%-32s\t%03x\t%08lx\n", \
+       snprintf(buff, sizeof(buff), "%-32s\t%03x\t%08lx\n",  \
                 #a, a, tegra_dc_readl(dc, a));               \
        print(data, buff);                                    \
        } while (0)
 
-#define print_mode_info(dc, mode) do {                        \
-       trace_printk("%s:Mode settings: "                              \
-                       "ref_to_sync: H = %d V = %d, "                  \
-                       "sync_width: H = %d V = %d, "                   \
-                       "back_porch: H = %d V = %d, "                   \
-                       "active: H = %d V = %d, "                       \
-                       "front_porch: H = %d V = %d, "                  \
-                       "pclk = %d, stereo mode = %d\n",                \
-                       dc->ndev->name,                                 \
-                       mode.h_ref_to_sync, mode.v_ref_to_sync,         \
-                       mode.h_sync_width, mode.v_sync_width,           \
-                       mode.h_back_porch, mode.v_back_porch,           \
-                       mode.h_active, mode.v_active,                   \
-                       mode.h_front_porch, mode.v_front_porch,         \
-                       mode.pclk, mode.stereo_mode);                   \
-       } while (0)
-
-#define print_underflow_info(dc) do {                 \
-       trace_printk("%s:Underflow stats: underflows : %llu, "      \
-                       "undeflows_a : %llu, "                          \
-                       "underflows_b : %llu, "                         \
-                       "underflows_c : %llu\n",                        \
-                       dc->ndev->name,                                 \
-                       dc->stats.underflows,                           \
-                       dc->stats.underflows_a, dc->stats.underflows_b, \
-                       dc->stats.underflows_c);                        \
-       } while (0)
-
 static void _dump_regs(struct tegra_dc *dc, void *data,
                       void (* print)(void *data, const char *str))
 {
        int i;
        char buff[256];
 
-       tegra_dc_io_start(dc);
-       tegra_dc_clk_enable(dc);
+       mutex_lock(&dc->lock);
+       tegra_dc_get(dc);
+       tegra_dc_writel(dc, WRITE_MUX_ACTIVE | READ_MUX_ACTIVE,
+               DC_CMD_STATE_ACCESS);
 
        DUMP_REG(DC_CMD_DISPLAY_COMMAND_OPTION0);
        DUMP_REG(DC_CMD_DISPLAY_COMMAND);
@@ -400,7 +575,9 @@ static void _dump_regs(struct tegra_dc *dc, void *data,
        DUMP_REG(DC_DISP_DATA_ENABLE_OPTIONS);
        DUMP_REG(DC_DISP_SERIAL_INTERFACE_OPTIONS);
        DUMP_REG(DC_DISP_LCD_SPI_OPTIONS);
+#if !defined(CONFIG_TEGRA_DC_BLENDER_GEN2)
        DUMP_REG(DC_DISP_BORDER_COLOR);
+#endif
        DUMP_REG(DC_DISP_COLOR_KEY0_LOWER);
        DUMP_REG(DC_DISP_COLOR_KEY0_UPPER);
        DUMP_REG(DC_DISP_COLOR_KEY1_LOWER);
@@ -420,7 +597,9 @@ static void _dump_regs(struct tegra_dc *dc, void *data,
        DUMP_REG(DC_DISP_MCCIF_DISPLAY0A_HYST);
        DUMP_REG(DC_DISP_MCCIF_DISPLAY0B_HYST);
        DUMP_REG(DC_DISP_MCCIF_DISPLAY0C_HYST);
+#if defined(CONFIG_ARCH_TEGRA_2x_SOC) || defined(CONFIG_ARCH_TEGRA_3x_SOC)
        DUMP_REG(DC_DISP_MCCIF_DISPLAY1B_HYST);
+#endif
        DUMP_REG(DC_DISP_DAC_CRT_CTRL);
        DUMP_REG(DC_DISP_DISP_MISC_CONTROL);
 
@@ -444,15 +623,17 @@ static void _dump_regs(struct tegra_dc *dc, void *data,
                DUMP_REG(DC_WIN_V_INITIAL_DDA);
                DUMP_REG(DC_WIN_DDA_INCREMENT);
                DUMP_REG(DC_WIN_LINE_STRIDE);
-#if defined(CONFIG_ARCH_TEGRA_2x_SOC) || defined(CONFIG_ARCH_TEGRA_3x_SOC)
+#if !defined(CONFIG_TEGRA_DC_BLENDER_GEN2)
                DUMP_REG(DC_WIN_BUF_STRIDE);
                DUMP_REG(DC_WIN_UV_BUF_STRIDE);
 #endif
+#if !defined(CONFIG_TEGRA_DC_BLENDER_GEN2) || defined(CONFIG_ARCH_TEGRA_14x_SOC)
                DUMP_REG(DC_WIN_BLEND_NOKEY);
                DUMP_REG(DC_WIN_BLEND_1WIN);
                DUMP_REG(DC_WIN_BLEND_2WIN_X);
                DUMP_REG(DC_WIN_BLEND_2WIN_Y);
                DUMP_REG(DC_WIN_BLEND_3WIN_XY);
+#endif
                DUMP_REG(DC_WINBUF_START_ADDR);
                DUMP_REG(DC_WINBUF_START_ADDR_U);
                DUMP_REG(DC_WINBUF_START_ADDR_V);
@@ -480,9 +661,20 @@ static void _dump_regs(struct tegra_dc *dc, void *data,
        DUMP_REG(DC_COM_PM1_CONTROL);
        DUMP_REG(DC_COM_PM1_DUTY_CYCLE);
        DUMP_REG(DC_DISP_SD_CONTROL);
+#ifdef CONFIG_TEGRA_DC_CMU
+       DUMP_REG(DC_COM_CMU_CSC_KRR);
+       DUMP_REG(DC_COM_CMU_CSC_KGR);
+       DUMP_REG(DC_COM_CMU_CSC_KBR);
+       DUMP_REG(DC_COM_CMU_CSC_KRG);
+       DUMP_REG(DC_COM_CMU_CSC_KGG);
+       DUMP_REG(DC_COM_CMU_CSC_KBR);
+       DUMP_REG(DC_COM_CMU_CSC_KRB);
+       DUMP_REG(DC_COM_CMU_CSC_KGB);
+       DUMP_REG(DC_COM_CMU_CSC_KBB);
+#endif
 
-       tegra_dc_clk_disable(dc->clk);
-       tegra_dc_io_end(dc);
+       tegra_dc_put(dc);
+       mutex_unlock(&dc->lock);
 }
 
 #undef DUMP_REG
@@ -593,6 +785,15 @@ static int dbg_dc_stats_show(struct seq_file *s, void *unused)
                dc->stats.underflows_a,
                dc->stats.underflows_b,
                dc->stats.underflows_c);
+#if defined(CONFIG_ARCH_TEGRA_14x_SOC)
+       seq_printf(s,
+               "underflows_d: %llu\n"
+               "underflows_h: %llu\n"
+               "underflows_t: %llu\n",
+               dc->stats.underflows_d,
+               dc->stats.underflows_h,
+               dc->stats.underflows_t);
+#endif
        mutex_unlock(&dc->lock);
 
        return 0;
@@ -603,6 +804,37 @@ static int dbg_dc_stats_open(struct inode *inode, struct file *file)
        return single_open(file, dbg_dc_stats_show, inode->i_private);
 }
 
+static int dbg_dc_event_inject_show(struct seq_file *s, void *unused)
+{
+       return 0;
+}
+
+static int dbg_dc_event_inject_write(struct file *file, const char __user *addr,
+       size_t len, loff_t *pos)
+{
+       struct seq_file *m = file->private_data; /* single_open() initialized */
+       struct tegra_dc *dc = m ? m->private : NULL;
+       long event;
+       int ret;
+
+       if (!dc)
+               return -EINVAL;
+
+       ret = kstrtol_from_user(addr, len, 10, &event);
+       if (ret < 0)
+               return ret;
+
+       if (event == 0x1) /* TEGRA_DC_EXT_EVENT_HOTPLUG */
+               tegra_dc_ext_process_hotplug(dc->ndev->id);
+       else if (event == 0x2) /* TEGRA_DC_EXT_EVENT_BANDWIDTH */
+               tegra_dc_ext_process_bandwidth_renegotiate(dc->ndev->id);
+       else {
+               dev_err(&dc->ndev->dev, "Unknown event 0x%lx\n", event);
+               return -EINVAL; /* unknown event number */
+       }
+       return len;
+}
+
 static const struct file_operations stats_fops = {
        .open           = dbg_dc_stats_open,
        .read           = seq_read,
@@ -610,6 +842,19 @@ static const struct file_operations stats_fops = {
        .release        = single_release,
 };
 
+static int dbg_dc_event_inject_open(struct inode *inode, struct file *file)
+{
+       return single_open(file, dbg_dc_event_inject_show, inode->i_private);
+}
+
+static const struct file_operations event_inject_fops = {
+       .open           = dbg_dc_event_inject_open,
+       .read           = seq_read,
+       .write          = dbg_dc_event_inject_write,
+       .llseek         = seq_lseek,
+       .release        = single_release,
+};
+
 static void tegra_dc_remove_debugfs(struct tegra_dc *dc)
 {
        if (dc->debugdir)
@@ -640,6 +885,11 @@ static void tegra_dc_create_debugfs(struct tegra_dc *dc)
        if (!retval)
                goto remove_out;
 
+       retval = debugfs_create_file("event_inject", S_IRUGO, dc->debugdir, dc,
+               &event_inject_fops);
+       if (!retval)
+               goto remove_out;
+
        return;
 remove_out:
        dev_err(&dc->ndev->dev, "could not create debugfs\n");
@@ -697,9 +947,13 @@ int tegra_dc_get_stride(struct tegra_dc *dc, unsigned win)
        if (!dc->enabled)
                return 0;
        BUG_ON(win > DC_N_WINDOWS);
+       mutex_lock(&dc->lock);
+       tegra_dc_get(dc);
        tegra_dc_writel(dc, WINDOW_A_SELECT << win,
                DC_CMD_DISPLAY_WINDOW_HEADER);
        stride = tegra_dc_readl(dc, DC_WIN_LINE_STRIDE);
+       tegra_dc_put(dc);
+       mutex_unlock(&dc->lock);
        return GET_LINE_STRIDE(stride);
 }
 EXPORT_SYMBOL(tegra_dc_get_stride);
@@ -722,18 +976,6 @@ struct tegra_dc_win *tegra_dc_get_window(struct tegra_dc *dc, unsigned win)
 }
 EXPORT_SYMBOL(tegra_dc_get_window);
 
-static int get_topmost_window(u32 *depths, unsigned long *wins, int win_num)
-{
-       int idx, best = -1;
-
-       for_each_set_bit(idx, wins, win_num) {
-               if (best == -1 || depths[idx] < depths[best])
-                       best = idx;
-       }
-       clear_bit(best, wins);
-       return best;
-}
-
 bool tegra_dc_get_connected(struct tegra_dc *dc)
 {
        return dc->connected;
@@ -744,297 +986,37 @@ bool tegra_dc_hpd(struct tegra_dc *dc)
 {
        int sense;
        int level;
+       int hpd;
 
-       level = gpio_get_value(dc->out->hotplug_gpio);
-
-       sense = dc->out->flags & TEGRA_DC_OUT_HOTPLUG_MASK;
-
-       return (sense == TEGRA_DC_OUT_HOTPLUG_HIGH && level) ||
-               (sense == TEGRA_DC_OUT_HOTPLUG_LOW && !level);
-}
-EXPORT_SYMBOL(tegra_dc_hpd);
-
-static u32 blend_topwin(u32 flags)
-{
-       if (flags & TEGRA_WIN_FLAG_BLEND_COVERAGE)
-               return BLEND(NOKEY, ALPHA, 0xff, 0xff);
-       else if (flags & TEGRA_WIN_FLAG_BLEND_PREMULT)
-               return BLEND(NOKEY, PREMULT, 0xff, 0xff);
-       else
-               return BLEND(NOKEY, FIX, 0xff, 0xff);
-}
-
-static u32 blend_2win(int idx, unsigned long behind_mask,
-                                               u32* flags, int xy, int win_num)
-{
-       int other;
-
-       for (other = 0; other < win_num; other++) {
-               if (other != idx && (xy-- == 0))
-                       break;
-       }
-       if (BIT(other) & behind_mask)
-               return blend_topwin(flags[idx]);
-       else if (flags[other])
-               return BLEND(NOKEY, DEPENDANT, 0x00, 0x00);
-       else
-               return BLEND(NOKEY, FIX, 0x00, 0x00);
-}
-
-static u32 blend_3win(int idx, unsigned long behind_mask,
-                                               u32* flags, int win_num)
-{
-       unsigned long infront_mask;
-       int first;
-
-       infront_mask = ~(behind_mask | BIT(idx));
-       infront_mask &= (BIT(win_num) - 1);
-       first = ffs(infront_mask) - 1;
-
-       if (!infront_mask)
-               return blend_topwin(flags[idx]);
-       else if (behind_mask && first != -1 && flags[first])
-               return BLEND(NOKEY, DEPENDANT, 0x00, 0x00);
-       else
-               return BLEND(NOKEY, FIX, 0x0, 0x0);
-}
-
-static void tegra_dc_set_gen1_blending(struct tegra_dc *dc, struct tegra_dc_blend *blend)
-{
-       int win_num = dc->gen1_blend_num;
-       unsigned long mask = BIT(win_num) - 1;
-
-       while (mask) {
-               int idx = get_topmost_window(blend->z, &mask, win_num);
-
-               tegra_dc_writel(dc, WINDOW_A_SELECT << idx,
-                               DC_CMD_DISPLAY_WINDOW_HEADER);
-               tegra_dc_writel(dc, BLEND(NOKEY, FIX, 0xff, 0xff),
-                               DC_WIN_BLEND_NOKEY);
-               tegra_dc_writel(dc, BLEND(NOKEY, FIX, 0xff, 0xff),
-                               DC_WIN_BLEND_1WIN);
-               tegra_dc_writel(dc, blend_2win(idx, mask, blend->flags, 0,
-                               win_num), DC_WIN_BLEND_2WIN_X);
-               tegra_dc_writel(dc, blend_2win(idx, mask, blend->flags, 1,
-                               win_num), DC_WIN_BLEND_2WIN_Y);
-               tegra_dc_writel(dc, blend_3win(idx, mask, blend->flags,
-                               win_num), DC_WIN_BLEND_3WIN_XY);
-       }
-}
-
-static void tegra_dc_set_gen2_blending(struct tegra_dc *dc,
-                                               struct tegra_dc_blend *blend)
-{
-       long val;
-       int i = 0;
-
-       for (i = 0; i < DC_N_WINDOWS; i++) {
-               if (!tegra_dc_feature_is_gen2_blender(dc, i))
-                       continue;
-
-               tegra_dc_writel(dc, WINDOW_A_SELECT << i,
-                               DC_CMD_DISPLAY_WINDOW_HEADER);
-
-               if (blend->flags[i] & TEGRA_WIN_FLAG_BLEND_COVERAGE) {
-                       tegra_dc_writel(dc,
-                                       WIN_K1(0xff) |
-                                       WIN_K2(0xff) |
-                                       WIN_BLEND_ENABLE,
-                                       DC_WINBUF_BLEND_LAYER_CONTROL);
-
-                       tegra_dc_writel(dc,
-                       WIN_BLEND_FACT_SRC_COLOR_MATCH_SEL_K1_TIMES_SRC |
-                       WIN_BLEND_FACT_DST_COLOR_MATCH_SEL_NEG_K1_TIMES_SRC |
-                       WIN_BLEND_FACT_SRC_ALPHA_MATCH_SEL_K2 |
-                       WIN_BLEND_FACT_DST_ALPHA_MATCH_SEL_ZERO,
-                       DC_WINBUF_BLEND_MATCH_SELECT);
-
-                       tegra_dc_writel(dc,
-                                       WIN_ALPHA_1BIT_WEIGHT0(0) |
-                                       WIN_ALPHA_1BIT_WEIGHT1(0xff),
-                                       DC_WINBUF_BLEND_ALPHA_1BIT);
-               } else if (blend->flags[i] & TEGRA_WIN_FLAG_BLEND_PREMULT) {
-                       tegra_dc_writel(dc,
-                                       WIN_K1(0xff) |
-                                       WIN_K2(0xff) |
-                                       WIN_BLEND_ENABLE,
-                                       DC_WINBUF_BLEND_LAYER_CONTROL);
-
-                       tegra_dc_writel(dc,
-                       WIN_BLEND_FACT_SRC_COLOR_MATCH_SEL_K1 |
-                       WIN_BLEND_FACT_DST_COLOR_MATCH_SEL_NEG_K1 |
-                       WIN_BLEND_FACT_SRC_ALPHA_MATCH_SEL_K2 |
-                       WIN_BLEND_FACT_DST_ALPHA_MATCH_SEL_ZERO,
-                       DC_WINBUF_BLEND_MATCH_SELECT);
-
-                       tegra_dc_writel(dc,
-                                       WIN_ALPHA_1BIT_WEIGHT0(0) |
-                                       WIN_ALPHA_1BIT_WEIGHT1(0xff),
-                                       DC_WINBUF_BLEND_ALPHA_1BIT);
-               } else {
-                       tegra_dc_writel(dc,
-                                       WIN_BLEND_BYPASS,
-                                       DC_WINBUF_BLEND_LAYER_CONTROL);
-               }
-       }
-}
-
-static void tegra_dc_init_csc_defaults(struct tegra_dc_csc *csc)
-{
-       csc->yof   = 0x00f0;
-       csc->kyrgb = 0x012a;
-       csc->kur   = 0x0000;
-       csc->kvr   = 0x0198;
-       csc->kug   = 0x039b;
-       csc->kvg   = 0x032f;
-       csc->kub   = 0x0204;
-       csc->kvb   = 0x0000;
-}
-
-static void tegra_dc_set_csc(struct tegra_dc *dc, struct tegra_dc_csc *csc)
-{
-       tegra_dc_writel(dc, csc->yof,   DC_WIN_CSC_YOF);
-       tegra_dc_writel(dc, csc->kyrgb, DC_WIN_CSC_KYRGB);
-       tegra_dc_writel(dc, csc->kur,   DC_WIN_CSC_KUR);
-       tegra_dc_writel(dc, csc->kvr,   DC_WIN_CSC_KVR);
-       tegra_dc_writel(dc, csc->kug,   DC_WIN_CSC_KUG);
-       tegra_dc_writel(dc, csc->kvg,   DC_WIN_CSC_KVG);
-       tegra_dc_writel(dc, csc->kub,   DC_WIN_CSC_KUB);
-       tegra_dc_writel(dc, csc->kvb,   DC_WIN_CSC_KVB);
-}
-
-int tegra_dc_update_csc(struct tegra_dc *dc, int win_idx)
-{
-       mutex_lock(&dc->lock);
-
-       if (!dc->enabled) {
-               mutex_unlock(&dc->lock);
-               return -EFAULT;
-       }
-
-       tegra_dc_writel(dc, WINDOW_A_SELECT << win_idx,
-                       DC_CMD_DISPLAY_WINDOW_HEADER);
-
-       tegra_dc_set_csc(dc, &dc->windows[win_idx].csc);
-
-       mutex_unlock(&dc->lock);
-
-       return 0;
-}
-EXPORT_SYMBOL(tegra_dc_update_csc);
-
-static void tegra_dc_init_lut_defaults(struct tegra_dc_lut *lut)
-{
-       int i;
-       for (i = 0; i < 256; i++)
-               lut->r[i] = lut->g[i] = lut->b[i] = (u8)i;
-}
-
-static int tegra_dc_loop_lut(struct tegra_dc *dc,
-                            struct tegra_dc_win *win,
-                            int(*lambda)(struct tegra_dc *dc, int i, u32 rgb))
-{
-       struct tegra_dc_lut *lut = &win->lut;
-       struct tegra_dc_lut *global_lut = &dc->fb_lut;
-       int i;
-       for (i = 0; i < 256; i++) {
-
-               u32 r = (u32)lut->r[i];
-               u32 g = (u32)lut->g[i];
-               u32 b = (u32)lut->b[i];
-
-               if (!(win->ppflags & TEGRA_WIN_PPFLAG_CP_FBOVERRIDE)) {
-                       r = (u32)global_lut->r[r];
-                       g = (u32)global_lut->g[g];
-                       b = (u32)global_lut->b[b];
-               }
-
-               if (!lambda(dc, i, r | (g<<8) | (b<<16)))
-                       return 0;
-       }
-       return 1;
-}
-
-static int tegra_dc_lut_isdefaults_lambda(struct tegra_dc *dc, int i, u32 rgb)
-{
-       if (rgb != (i | (i<<8) | (i<<16)))
-               return 0;
-       return 1;
-}
-
-static int tegra_dc_set_lut_setreg_lambda(struct tegra_dc *dc, int i, u32 rgb)
-{
-       tegra_dc_writel(dc, rgb, DC_WIN_COLOR_PALETTE(i));
-       return 1;
-}
-
-static void tegra_dc_set_lut(struct tegra_dc *dc, struct tegra_dc_win* win)
-{
-       unsigned long val = tegra_dc_readl(dc, DC_WIN_WIN_OPTIONS);
-
-       tegra_dc_loop_lut(dc, win, tegra_dc_set_lut_setreg_lambda);
-
-       if (win->ppflags & TEGRA_WIN_PPFLAG_CP_ENABLE)
-               val |= CP_ENABLE;
-       else
-               val &= ~CP_ENABLE;
-
-       tegra_dc_writel(dc, val, DC_WIN_WIN_OPTIONS);
-}
-
-static int tegra_dc_update_winlut(struct tegra_dc *dc, int win_idx, int fbovr)
-{
-       struct tegra_dc_win *win = &dc->windows[win_idx];
-
-       mutex_lock(&dc->lock);
+       if (WARN_ON(!dc || !dc->out))
+               return false;
 
-       if (!dc->enabled) {
-               mutex_unlock(&dc->lock);
-               return -EFAULT;
+       if (dc->out->hotplug_state != 0) {
+               if (dc->out->hotplug_state == 1) /* force on */
+                       return true;
+               if (dc->out->hotplug_state == -1) /* force off */
+                       return false;
        }
+       level = gpio_get_value_cansleep(dc->out->hotplug_gpio);
 
-       if (fbovr > 0)
-               win->ppflags |= TEGRA_WIN_PPFLAG_CP_FBOVERRIDE;
-       else if (fbovr == 0)
-               win->ppflags &= ~TEGRA_WIN_PPFLAG_CP_FBOVERRIDE;
-
-       if (!tegra_dc_loop_lut(dc, win, tegra_dc_lut_isdefaults_lambda))
-               win->ppflags |= TEGRA_WIN_PPFLAG_CP_ENABLE;
-       else
-               win->ppflags &= ~TEGRA_WIN_PPFLAG_CP_ENABLE;
-
-       tegra_dc_writel(dc, WINDOW_A_SELECT << win_idx,
-                       DC_CMD_DISPLAY_WINDOW_HEADER);
-
-       tegra_dc_set_lut(dc, win);
-
-       mutex_unlock(&dc->lock);
-
-       tegra_dc_update_windows(&win, 1);
-
-       return 0;
-}
+       sense = dc->out->flags & TEGRA_DC_OUT_HOTPLUG_MASK;
 
-int tegra_dc_update_lut(struct tegra_dc *dc, int win_idx, int fboveride)
-{
-       if (win_idx > -1)
-               return tegra_dc_update_winlut(dc, win_idx, fboveride);
+       hpd = (sense == TEGRA_DC_OUT_HOTPLUG_HIGH && level) ||
+               (sense == TEGRA_DC_OUT_HOTPLUG_LOW && !level);
 
-       for (win_idx = 0; win_idx < DC_N_WINDOWS; win_idx++) {
-               int err = tegra_dc_update_winlut(dc, win_idx, fboveride);
-               if (err)
-                       return err;
-       }
+       if (dc->out->hotplug_report)
+               dc->out->hotplug_report(hpd);
 
-       return 0;
+       return hpd;
 }
-EXPORT_SYMBOL(tegra_dc_update_lut);
+EXPORT_SYMBOL(tegra_dc_hpd);
 
 static void tegra_dc_set_scaling_filter(struct tegra_dc *dc)
 {
        unsigned i;
        unsigned v0 = 128;
        unsigned v1 = 0;
+
        /* linear horizontal and vertical filters */
        for (i = 0; i < 16; i++) {
                tegra_dc_writel(dc, (v1 << 16) | (v0 << 8),
@@ -1048,8 +1030,8 @@ static void tegra_dc_set_scaling_filter(struct tegra_dc *dc)
 }
 
 #ifdef CONFIG_TEGRA_DC_CMU
-static void tegra_dc_init_cmu_defaults(struct tegra_dc_cmu *dst_cmu,
-                                               struct tegra_dc_cmu *src_cmu)
+static void tegra_dc_cache_cmu(struct tegra_dc_cmu *dst_cmu,
+                                       struct tegra_dc_cmu *src_cmu)
 {
        memcpy(dst_cmu, src_cmu, sizeof(struct tegra_dc_cmu));
 }
@@ -1080,7 +1062,7 @@ static void tegra_dc_set_cmu(struct tegra_dc *dc, struct tegra_dc_cmu *cmu)
        }
 }
 
-static void tegra_dc_get_cmu(struct tegra_dc *dc, struct tegra_dc_cmu *cmu)
+void tegra_dc_get_cmu(struct tegra_dc *dc, struct tegra_dc_cmu *cmu)
 {
        u32 val;
        u32 i;
@@ -1122,339 +1104,89 @@ static void tegra_dc_get_cmu(struct tegra_dc *dc, struct tegra_dc_cmu *cmu)
                cmu->lut2[i] = LUT2_READ_DATA(val);
        }
 }
+EXPORT_SYMBOL(tegra_dc_get_cmu);
 
-static int tegra_dc_update_cmu(struct tegra_dc *dc, bool cmu_enable)
+int _tegra_dc_update_cmu(struct tegra_dc *dc, struct tegra_dc_cmu *cmu)
 {
-       mutex_lock(&dc->lock);
-
-       if (!dc->enabled) {
-               mutex_unlock(&dc->lock);
-               return -EFAULT;
-       }
+       u32 val;
 
-       if (cmu_enable) {
-               dc->out->flags |= TEGRA_DC_OUT_CMU_ENABLE;
+       if (dc->pdata->cmu_enable) {
+               dc->pdata->flags |= TEGRA_DC_FLAG_CMU_ENABLE;
        } else {
-               dc->out->flags &= ~TEGRA_DC_OUT_CMU_ENABLE;
+               dc->pdata->flags &= ~TEGRA_DC_FLAG_CMU_ENABLE;
                return 0;
        }
 
-       tegra_dc_set_cmu(dc, &dc->cmu);
-
-       mutex_unlock(&dc->lock);
-
-       return 0;
-}
-EXPORT_SYMBOL(tegra_dc_update_cmu);
-#else
-#define tegra_dc_init_cmu_defaults(dst_cmu, src_cmu)
-#define tegra_dc_set_cmu(dc, cmu)
-#endif
-
-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)
-{
-       /*
-        * min(round((prescaled_size_in_pixels - 1) * 0x1000 /
-        *           (post_scaled_size_in_pixels - 1)), MAX)
-        * Where the value of MAX is as follows:
-        * For V_DDA_INCREMENT: 15.0 (0xF000)
-        * For H_DDA_INCREMENT:  4.0 (0x4000) for 4 Bytes/pix formats.
-        *                       8.0 (0x8000) for 2 Bytes/pix formats.
-        */
-
-       fixed20_12 out = dfixed_init(out_int);
-       u32 dda_inc;
-       int max;
-
-       if (v) {
-               max = 15;
-       } else {
-               switch (Bpp) {
-               default:
-                       WARN_ON_ONCE(1);
-                       /* fallthrough */
-               case 4:
-                       max = 4;
-                       break;
-               case 2:
-                       max = 8;
-                       break;
+#ifdef CONFIG_TEGRA_DC_CMU
+       if (cmu != &dc->cmu) {
+               tegra_dc_cache_cmu(&dc->cmu, cmu);
+
+               /* Disable CMU */
+               val = tegra_dc_readl(dc, DC_DISP_DISP_COLOR_CONTROL);
+               if (val & CMU_ENABLE) {
+                       val &= ~CMU_ENABLE;
+                       tegra_dc_writel(dc, val, DC_DISP_DISP_COLOR_CONTROL);
+                       val = GENERAL_UPDATE;
+                       tegra_dc_writel(dc, val, DC_CMD_STATE_CONTROL);
+                       val = GENERAL_ACT_REQ;
+                       tegra_dc_writel(dc, val, DC_CMD_STATE_CONTROL);
+                       /*TODO: Sync up with vsync */
+                       mdelay(20);
                }
-       }
-
-       out.full = max_t(u32, out.full - dfixed_const(1), dfixed_const(1));
-       in.full -= dfixed_const(1);
 
-       dda_inc = dfixed_div(in, out);
-
-       dda_inc = min_t(u32, dda_inc, dfixed_const(max));
-
-       return dda_inc;
-}
+               tegra_dc_set_cmu(dc, &dc->cmu);
+       }
+#endif
 
-static inline u32 compute_initial_dda(fixed20_12 in)
-{
-       return dfixed_frac(in);
+       return 0;
 }
 
-/* does not support updating windows on multiple dcs in one call */
-int tegra_dc_update_windows(struct tegra_dc_win *windows[], int n)
+int tegra_dc_update_cmu(struct tegra_dc *dc, struct tegra_dc_cmu *cmu)
 {
-       struct tegra_dc *dc;
-       unsigned long update_mask = GENERAL_ACT_REQ;
-       unsigned long val;
-       bool update_gen1_blend = false;
-       bool update_gen2_blend = false;
-       int i;
-
-       dc = windows[0]->dc;
+       int ret;
 
-       if (dc->out->flags & TEGRA_DC_OUT_ONE_SHOT_MODE) {
-               /* Acquire one_shot_lock to avoid race condition between
-                * cancellation of old delayed work and schedule of new
-                * delayed work. */
-               mutex_lock(&dc->one_shot_lock);
-               cancel_delayed_work_sync(&dc->one_shot_work);
-       }
        mutex_lock(&dc->lock);
-
        if (!dc->enabled) {
                mutex_unlock(&dc->lock);
-               if (dc->out->flags & TEGRA_DC_OUT_ONE_SHOT_MODE)
-                       mutex_unlock(&dc->one_shot_lock);
-               return -EFAULT;
+               return 0;
        }
 
-       if (dc->out->flags & TEGRA_DC_OUT_ONE_SHOT_LP_MODE)
-               tegra_dc_host_resume(dc);
+       tegra_dc_get(dc);
 
-       if (no_vsync)
-               tegra_dc_writel(dc, WRITE_MUX_ACTIVE | READ_MUX_ACTIVE, DC_CMD_STATE_ACCESS);
-       else
-               tegra_dc_writel(dc, WRITE_MUX_ASSEMBLY | READ_MUX_ASSEMBLY, DC_CMD_STATE_ACCESS);
-
-       for (i = 0; i < n; i++) {
-               struct tegra_dc_win *win = windows[i];
-               unsigned h_dda;
-               unsigned v_dda;
-               fixed20_12 h_offset, v_offset;
-               bool invert_h = (win->flags & TEGRA_WIN_FLAG_INVERT_H) != 0;
-               bool invert_v = (win->flags & TEGRA_WIN_FLAG_INVERT_V) != 0;
-               bool yuv = tegra_dc_is_yuv(win->fmt);
-               bool yuvp = tegra_dc_is_yuv_planar(win->fmt);
-               unsigned Bpp = tegra_dc_fmt_bpp(win->fmt) / 8;
-               /* Bytes per pixel of bandwidth, used for dda_inc calculation */
-               unsigned Bpp_bw = Bpp * (yuvp ? 2 : 1);
-               const bool filter_h = win_use_h_filter(dc, win);
-               const bool filter_v = win_use_v_filter(dc, win);
-
-               if (win->z != dc->blend.z[win->idx]) {
-                       dc->blend.z[win->idx] = win->z;
-                       if (tegra_dc_feature_is_gen2_blender(dc, win->idx))
-                               update_gen2_blend = true;
-                       else
-                               update_gen1_blend = true;
-               }
-               if ((win->flags & TEGRA_WIN_BLEND_FLAGS_MASK) !=
-                       dc->blend.flags[win->idx]) {
-                       dc->blend.flags[win->idx] =
-                               win->flags & TEGRA_WIN_BLEND_FLAGS_MASK;
-                       if (tegra_dc_feature_is_gen2_blender(dc, win->idx))
-                               update_gen2_blend = true;
-                       else
-                               update_gen1_blend = true;
-               }
-
-               tegra_dc_writel(dc, WINDOW_A_SELECT << win->idx,
-                               DC_CMD_DISPLAY_WINDOW_HEADER);
-
-               if (!no_vsync)
-                       update_mask |= WIN_A_ACT_REQ << win->idx;
-
-               if (!WIN_IS_ENABLED(win)) {
-                       dc->windows[i].dirty = 1;
-                       tegra_dc_writel(dc, 0, DC_WIN_WIN_OPTIONS);
-                       continue;
-               }
-
-               tegra_dc_writel(dc, win->fmt & 0x1f, DC_WIN_COLOR_DEPTH);
-               tegra_dc_writel(dc, win->fmt >> 6, DC_WIN_BYTE_SWAP);
-
-               tegra_dc_writel(dc,
-                               V_POSITION(win->out_y) | H_POSITION(win->out_x),
-                               DC_WIN_POSITION);
-               tegra_dc_writel(dc,
-                               V_SIZE(win->out_h) | H_SIZE(win->out_w),
-                               DC_WIN_SIZE);
-
-               if (tegra_dc_feature_has_scaling(dc, win->idx)) {
-                       tegra_dc_writel(dc,
-                                       V_PRESCALED_SIZE(dfixed_trunc(win->h)) |
-                                       H_PRESCALED_SIZE(dfixed_trunc(win->w) * Bpp),
-                                       DC_WIN_PRESCALED_SIZE);
-
-                       h_dda = compute_dda_inc(win->w, win->out_w, false, Bpp_bw);
-                       v_dda = compute_dda_inc(win->h, win->out_h, true, Bpp_bw);
-                       tegra_dc_writel(dc, V_DDA_INC(v_dda) | H_DDA_INC(h_dda),
-                                       DC_WIN_DDA_INCREMENT);
-                       h_dda = compute_initial_dda(win->x);
-                       v_dda = compute_initial_dda(win->y);
-                       tegra_dc_writel(dc, h_dda, DC_WIN_H_INITIAL_DDA);
-                       tegra_dc_writel(dc, v_dda, DC_WIN_V_INITIAL_DDA);
-               }
-
-#if defined(CONFIG_ARCH_TEGRA_2x_SOC) || defined(CONFIG_ARCH_TEGRA_3x_SOC)
-               tegra_dc_writel(dc, 0, DC_WIN_BUF_STRIDE);
-               tegra_dc_writel(dc, 0, DC_WIN_UV_BUF_STRIDE);
-#endif
-               tegra_dc_writel(dc,
-                               (unsigned long)win->phys_addr,
-                               DC_WINBUF_START_ADDR);
-
-               if (!yuvp) {
-                       tegra_dc_writel(dc, win->stride, DC_WIN_LINE_STRIDE);
-               } else {
-                       tegra_dc_writel(dc,
-                                       (unsigned long)win->phys_addr_u,
-                                       DC_WINBUF_START_ADDR_U);
-                       tegra_dc_writel(dc,
-                                       (unsigned long)win->phys_addr_v,
-                                       DC_WINBUF_START_ADDR_V);
-                       tegra_dc_writel(dc,
-                                       LINE_STRIDE(win->stride) |
-                                       UV_LINE_STRIDE(win->stride_uv),
-                                       DC_WIN_LINE_STRIDE);
-               }
-
-               h_offset = win->x;
-               if (invert_h) {
-                       h_offset.full += win->w.full - dfixed_const(1);
-               }
-
-               v_offset = win->y;
-               if (invert_v) {
-                       v_offset.full += win->h.full - dfixed_const(1);
-               }
-
-               tegra_dc_writel(dc, dfixed_trunc(h_offset) * Bpp,
-                               DC_WINBUF_ADDR_H_OFFSET);
-               tegra_dc_writel(dc, dfixed_trunc(v_offset),
-                               DC_WINBUF_ADDR_V_OFFSET);
-
-               if (tegra_dc_feature_has_tiling(dc, win->idx)) {
-                       if (WIN_IS_TILED(win))
-                               tegra_dc_writel(dc,
-                                               DC_WIN_BUFFER_ADDR_MODE_TILE |
-                                               DC_WIN_BUFFER_ADDR_MODE_TILE_UV,
-                                               DC_WIN_BUFFER_ADDR_MODE);
-                       else
-                               tegra_dc_writel(dc,
-                                               DC_WIN_BUFFER_ADDR_MODE_LINEAR |
-                                               DC_WIN_BUFFER_ADDR_MODE_LINEAR_UV,
-                                               DC_WIN_BUFFER_ADDR_MODE);
-               }
-
-               val = WIN_ENABLE;
-               if (yuv)
-                       val |= CSC_ENABLE;
-               else if (tegra_dc_fmt_bpp(win->fmt) < 24)
-                       val |= COLOR_EXPAND;
-
-               if (win->ppflags & TEGRA_WIN_PPFLAG_CP_ENABLE)
-                       val |= CP_ENABLE;
-
-               if (filter_h)
-                       val |= H_FILTER_ENABLE;
-               if (filter_v)
-                       val |= V_FILTER_ENABLE;
-
-               if (invert_h)
-                       val |= H_DIRECTION_DECREMENT;
-               if (invert_v)
-                       val |= V_DIRECTION_DECREMENT;
-
-               tegra_dc_writel(dc, val, DC_WIN_WIN_OPTIONS);
-
-#ifdef CONFIG_ARCH_TEGRA_3x_SOC
-               if (win->global_alpha == 255)
-                       tegra_dc_writel(dc, 0, DC_WIN_GLOBAL_ALPHA);
-               else
-                       tegra_dc_writel(dc, GLOBAL_ALPHA_ENABLE |
-                               win->global_alpha, DC_WIN_GLOBAL_ALPHA);
-#endif
-
-               win->dirty = no_vsync ? 0 : 1;
-
-               dev_dbg(&dc->ndev->dev, "%s():idx=%d z=%d x=%d y=%d w=%d h=%d "
-                       "out_x=%u out_y=%u out_w=%u out_h=%u "
-                       "fmt=%d yuvp=%d Bpp=%u filter_h=%d filter_v=%d",
-                       __func__, win->idx, win->z,
-                       dfixed_trunc(win->x), dfixed_trunc(win->y),
-                       dfixed_trunc(win->w), dfixed_trunc(win->h),
-                       win->out_x, win->out_y, win->out_w, win->out_h,
-                       win->fmt, yuvp, Bpp, filter_h, filter_v);
-               trace_printk("%s:win%u in:%ux%u out:%ux%u fmt=%d\n",
-                       dc->ndev->name, win->idx, dfixed_trunc(win->w),
-                       dfixed_trunc(win->h), win->out_w, win->out_h, win->fmt);
-       }
-
-       if (update_gen1_blend || update_gen2_blend) {
-               if (update_gen1_blend)
-                       tegra_dc_set_gen1_blending(dc, &dc->blend);
-               if (update_gen2_blend)
-                       tegra_dc_set_gen2_blending(dc, &dc->blend);
-               for (i = 0; i < DC_N_WINDOWS; i++) {
-                       if (!no_vsync)
-                               dc->windows[i].dirty = 1;
-                       update_mask |= WIN_A_ACT_REQ << i;
-               }
-       }
+       ret = _tegra_dc_update_cmu(dc, cmu);
+       tegra_dc_set_color_control(dc);
 
-       tegra_dc_set_dynamic_emc(windows, n);
+       tegra_dc_put(dc);
+       mutex_unlock(&dc->lock);
 
-       tegra_dc_writel(dc, update_mask << 8, DC_CMD_STATE_CONTROL);
+       return ret;
+}
+EXPORT_SYMBOL(tegra_dc_update_cmu);
 
-       tegra_dc_writel(dc, FRAME_END_INT | V_BLANK_INT, DC_CMD_INT_STATUS);
-       if (!no_vsync) {
-               set_bit(V_BLANK_FLIP, &dc->vblank_ref_count);
-               tegra_dc_unmask_interrupt(dc, FRAME_END_INT | V_BLANK_INT | ALL_UF_INT);
+void tegra_dc_cmu_enable(struct tegra_dc *dc, bool cmu_enable)
+{
+       dc->pdata->cmu_enable = cmu_enable;
+       if (dc->pdata->cmu) {
+               tegra_dc_update_cmu(dc, dc->pdata->cmu);
        } else {
-               clear_bit(V_BLANK_FLIP, &dc->vblank_ref_count);
-               tegra_dc_mask_interrupt(dc, FRAME_END_INT | V_BLANK_INT | ALL_UF_INT);
+               if (dc->out->type == TEGRA_DC_OUT_HDMI)
+                       tegra_dc_update_cmu(dc, &default_limited_cmu);
+               else
+                       tegra_dc_update_cmu(dc, &default_cmu);
        }
+}
+#else
+#define tegra_dc_cache_cmu(dst_cmu, src_cmu)
+#define tegra_dc_set_cmu(dc, cmu)
+#define tegra_dc_update_cmu(dc, cmu)
+#endif
 
-       if (dc->out->flags & TEGRA_DC_OUT_ONE_SHOT_MODE)
-               schedule_delayed_work(&dc->one_shot_work,
-                               msecs_to_jiffies(dc->one_shot_delay_ms));
-
-       /* update EMC clock if calculated bandwidth has changed */
-       tegra_dc_program_bandwidth(dc, false);
-
-       if (dc->out->flags & TEGRA_DC_OUT_ONE_SHOT_MODE)
-               update_mask |= NC_HOST_TRIG;
-
-       tegra_dc_writel(dc, update_mask, DC_CMD_STATE_CONTROL);
-       trace_printk("%s:update_mask=%#lx\n", dc->ndev->name, update_mask);
-
-       mutex_unlock(&dc->lock);
-       if (dc->out->flags & TEGRA_DC_OUT_ONE_SHOT_MODE)
-               mutex_unlock(&dc->one_shot_lock);
-
-       return 0;
+/* disable_irq() blocks until handler completes, calling this function while
+ * holding dc->lock can deadlock. */
+static inline void disable_dc_irq(const struct tegra_dc *dc)
+{
+       disable_irq(dc->irq);
 }
-EXPORT_SYMBOL(tegra_dc_update_windows);
 
 u32 tegra_dc_get_syncpt_id(const struct tegra_dc *dc, int i)
 {
@@ -1467,9 +1199,11 @@ u32 tegra_dc_incr_syncpt_max(struct tegra_dc *dc, int i)
        u32 max;
 
        mutex_lock(&dc->lock);
+       tegra_dc_get(dc);
        max = nvhost_syncpt_incr_max_ext(dc->ndev,
                dc->syncpt[i].id, ((dc->enabled) ? 1 : 0));
        dc->syncpt[i].max = max;
+       tegra_dc_put(dc);
        mutex_unlock(&dc->lock);
 
        return max;
@@ -1478,503 +1212,16 @@ u32 tegra_dc_incr_syncpt_max(struct tegra_dc *dc, int i)
 void tegra_dc_incr_syncpt_min(struct tegra_dc *dc, int i, u32 val)
 {
        mutex_lock(&dc->lock);
-       if ( dc->enabled )
+       if (dc->enabled) {
+               tegra_dc_get(dc);
                while (dc->syncpt[i].min < val) {
                        dc->syncpt[i].min++;
                        nvhost_syncpt_cpu_incr_ext(dc->ndev, dc->syncpt[i].id);
                }
-       mutex_unlock(&dc->lock);
-}
-
-static bool tegra_dc_windows_are_clean(struct tegra_dc_win *windows[],
-                                            int n)
-{
-       int i;
-
-       for (i = 0; i < n; i++) {
-               if (windows[i]->dirty)
-                       return false;
-       }
-
-       return true;
-}
-
-/* does not support syncing windows on multiple dcs in one call */
-int tegra_dc_sync_windows(struct tegra_dc_win *windows[], int n)
-{
-       int ret;
-       if (n < 1 || n > DC_N_WINDOWS)
-               return -EINVAL;
-
-       if (!windows[0]->dc->enabled)
-               return -EFAULT;
-
-       trace_printk("%s:Before wait_event_interruptible_timeout\n",
-                       windows[0]->dc->ndev->name);
-       ret = wait_event_interruptible_timeout(windows[0]->dc->wq,
-                                        tegra_dc_windows_are_clean(windows, n),
-                                        HZ);
-       trace_printk("%s:After wait_event_interruptible_timeout\n",
-                       windows[0]->dc->ndev->name);
-       return ret;
-}
-EXPORT_SYMBOL(tegra_dc_sync_windows);
-
-static unsigned long tegra_dc_clk_get_rate(struct tegra_dc *dc)
-{
-#ifdef CONFIG_TEGRA_SILICON_PLATFORM
-       return clk_get_rate(dc->clk);
-#else
-       return 27000000;
-#endif
-}
-
-static unsigned long tegra_dc_pclk_round_rate(struct tegra_dc *dc, int pclk)
-{
-       unsigned long rate;
-       unsigned long div;
-
-       rate = tegra_dc_clk_get_rate(dc);
-
-       div = DIV_ROUND_CLOSEST(rate * 2, pclk);
-
-       if (div < 2)
-               return 0;
-
-       return rate * 2 / div;
-}
-
-static unsigned long tegra_dc_pclk_predict_rate(struct clk *parent, int pclk)
-{
-       unsigned long rate;
-       unsigned long div;
-
-       rate = clk_get_rate(parent);
-
-       div = DIV_ROUND_CLOSEST(rate * 2, pclk);
-
-       if (div < 2)
-               return 0;
-
-       return rate * 2 / div;
-}
-
-void tegra_dc_setup_clk(struct tegra_dc *dc, struct clk *clk)
-{
-       int pclk;
-
-       if (dc->out->type == TEGRA_DC_OUT_RGB) {
-               unsigned long rate;
-               struct clk *parent_clk =
-                       clk_get_sys(NULL, dc->out->parent_clk ? : "pll_p");
-
-               if (dc->out->parent_clk_backup &&
-                   (parent_clk == clk_get_sys(NULL, "pll_p"))) {
-                       rate = tegra_dc_pclk_predict_rate(
-                               parent_clk, dc->mode.pclk);
-                       /* use pll_d as last resort */
-                       if (rate < (dc->mode.pclk / 100 * 99) ||
-                           rate > (dc->mode.pclk / 100 * 109))
-                               parent_clk = clk_get_sys(
-                                       NULL, dc->out->parent_clk_backup);
-               }
-
-               if (clk_get_parent(clk) != parent_clk)
-                       clk_set_parent(clk, parent_clk);
-
-               if (parent_clk != clk_get_sys(NULL, "pll_p")) {
-                       struct clk *base_clk = clk_get_parent(parent_clk);
-
-                       /* Assuming either pll_d or pll_d2 is used */
-                       rate = dc->mode.pclk * 2;
-
-                       if (rate != clk_get_rate(base_clk))
-                               clk_set_rate(base_clk, rate);
-               }
-       }
-
-       if (dc->out->type == TEGRA_DC_OUT_HDMI) {
-               unsigned long rate;
-               struct clk *parent_clk =
-                       clk_get_sys(NULL, dc->out->parent_clk ? : "pll_d_out0");
-               struct clk *base_clk = clk_get_parent(parent_clk);
-
-               /*
-                * Providing dynamic frequency rate setting for T20/T30 HDMI.
-                * The required rate needs to be setup at 4x multiplier,
-                * as out0 is 1/2 of the actual PLL output.
-                */
-
-               rate = dc->mode.pclk * 4;
-               if (rate != clk_get_rate(base_clk))
-                       clk_set_rate(base_clk, rate);
-
-               if (clk_get_parent(clk) != parent_clk)
-                       clk_set_parent(clk, parent_clk);
-       }
-
-       if (dc->out->type == TEGRA_DC_OUT_DSI) {
-               unsigned long rate;
-               struct clk *parent_clk;
-               struct clk *base_clk;
-
-               if (clk == dc->clk) {
-                       parent_clk = clk_get_sys(NULL,
-                                       dc->out->parent_clk ? : "pll_d_out0");
-                       base_clk = clk_get_parent(parent_clk);
-                       tegra_clk_cfg_ex(base_clk,
-                                       TEGRA_CLK_PLLD_DSI_OUT_ENB, 1);
-               } else {
-                       if (dc->pdata->default_out->dsi->dsi_instance) {
-                               parent_clk = clk_get_sys(NULL,
-                                       dc->out->parent_clk ? : "pll_d2_out0");
-                               base_clk = clk_get_parent(parent_clk);
-                               tegra_clk_cfg_ex(base_clk,
-                                               TEGRA_CLK_PLLD_CSI_OUT_ENB, 1);
-                       } else {
-                               parent_clk = clk_get_sys(NULL,
-                                       dc->out->parent_clk ? : "pll_d_out0");
-                               base_clk = clk_get_parent(parent_clk);
-                               tegra_clk_cfg_ex(base_clk,
-                                               TEGRA_CLK_PLLD_DSI_OUT_ENB, 1);
-                       }
-               }
-
-               rate = dc->mode.pclk * dc->shift_clk_div * 2;
-               if (rate != clk_get_rate(base_clk))
-                       clk_set_rate(base_clk, rate);
-
-               if (clk_get_parent(clk) != parent_clk)
-                       clk_set_parent(clk, parent_clk);
-       }
-
-       pclk = tegra_dc_pclk_round_rate(dc, dc->mode.pclk);
-       tegra_dvfs_set_rate(clk, pclk);
-}
-
-/* return non-zero if constraint is violated */
-static int calc_h_ref_to_sync(const struct tegra_dc_mode *mode, int *href)
-{
-       long a, b;
-
-       /* Constraint 5: H_REF_TO_SYNC >= 0 */
-       a = 0;
-
-       /* Constraint 6: H_FRONT_PORT >= (H_REF_TO_SYNC + 1) */
-       b = mode->h_front_porch - 1;
-
-       /* Constraint 1: H_REF_TO_SYNC + H_SYNC_WIDTH + H_BACK_PORCH > 11 */
-       if (a + mode->h_sync_width + mode->h_back_porch <= 11)
-               a = 1 + 11 - mode->h_sync_width - mode->h_back_porch;
-       /* check Constraint 1 and 6 */
-       if (a > b)
-               return 1;
-
-       /* Constraint 4: H_SYNC_WIDTH >= 1 */
-       if (mode->h_sync_width < 1)
-               return 4;
-
-       /* Constraint 7: H_DISP_ACTIVE >= 16 */
-       if (mode->h_active < 16)
-               return 7;
-
-       if (href) {
-               if (b > a && a % 2)
-                       *href = a + 1; /* use smallest even value */
-               else
-                       *href = a; /* even or only possible value */
-       }
-
-       return 0;
-}
-
-static int calc_v_ref_to_sync(const struct tegra_dc_mode *mode, int *vref)
-{
-       long a;
-       a = 1; /* Constraint 5: V_REF_TO_SYNC >= 1 */
-
-       /* Constraint 2: V_REF_TO_SYNC + V_SYNC_WIDTH + V_BACK_PORCH > 1 */
-       if (a + mode->v_sync_width + mode->v_back_porch <= 1)
-               a = 1 + 1 - mode->v_sync_width - mode->v_back_porch;
-
-       /* Constraint 6 */
-       if (mode->v_front_porch < a + 1)
-               a = mode->v_front_porch - 1;
-
-       /* Constraint 4: V_SYNC_WIDTH >= 1 */
-       if (mode->v_sync_width < 1)
-               return 4;
-
-       /* Constraint 7: V_DISP_ACTIVE >= 16 */
-       if (mode->v_active < 16)
-               return 7;
-
-       if (vref)
-               *vref = a;
-       return 0;
-}
-
-static int calc_ref_to_sync(struct tegra_dc_mode *mode)
-{
-       int ret;
-       ret = calc_h_ref_to_sync(mode, &mode->h_ref_to_sync);
-       if (ret)
-               return ret;
-       ret = calc_v_ref_to_sync(mode, &mode->v_ref_to_sync);
-       if (ret)
-               return ret;
-
-       return 0;
-}
-
-static bool check_ref_to_sync(struct tegra_dc_mode *mode)
-{
-       /* Constraint 1: H_REF_TO_SYNC + H_SYNC_WIDTH + H_BACK_PORCH > 11. */
-       if (mode->h_ref_to_sync + mode->h_sync_width + mode->h_back_porch <= 11)
-               return false;
-
-       /* Constraint 2: V_REF_TO_SYNC + V_SYNC_WIDTH + V_BACK_PORCH > 1. */
-       if (mode->v_ref_to_sync + mode->v_sync_width + mode->v_back_porch <= 1)
-               return false;
-
-       /* Constraint 3: V_FRONT_PORCH + V_SYNC_WIDTH + V_BACK_PORCH > 1
-        * (vertical blank). */
-       if (mode->v_front_porch + mode->v_sync_width + mode->v_back_porch <= 1)
-               return false;
-
-       /* Constraint 4: V_SYNC_WIDTH >= 1; H_SYNC_WIDTH >= 1. */
-       if (mode->v_sync_width < 1 || mode->h_sync_width < 1)
-               return false;
-
-       /* Constraint 5: V_REF_TO_SYNC >= 1; H_REF_TO_SYNC >= 0. */
-       if (mode->v_ref_to_sync < 1 || mode->h_ref_to_sync < 0)
-               return false;
-
-       /* Constraint 6: V_FRONT_PORT >= (V_REF_TO_SYNC + 1);
-        * H_FRONT_PORT >= (H_REF_TO_SYNC + 1). */
-       if (mode->v_front_porch < mode->v_ref_to_sync + 1 ||
-               mode->h_front_porch < mode->h_ref_to_sync + 1)
-               return false;
-
-       /* Constraint 7: H_DISP_ACTIVE >= 16; V_DISP_ACTIVE >= 16. */
-       if (mode->h_active < 16 || mode->v_active < 16)
-               return false;
-
-       return true;
-}
-
-#ifdef DEBUG
-/* return in 1000ths of a Hertz */
-static int calc_refresh(const struct tegra_dc_mode *m)
-{
-       long h_total, v_total, refresh;
-       h_total = m->h_active + m->h_front_porch + m->h_back_porch +
-               m->h_sync_width;
-       v_total = m->v_active + m->v_front_porch + m->v_back_porch +
-               m->v_sync_width;
-       refresh = m->pclk / h_total;
-       refresh *= 1000;
-       refresh /= v_total;
-       return refresh;
-}
-
-static void print_mode(struct tegra_dc *dc,
-                       const struct tegra_dc_mode *mode, const char *note)
-{
-       if (mode) {
-               int refresh = calc_refresh(dc, mode);
-               dev_info(&dc->ndev->dev, "%s():MODE:%dx%d@%d.%03uHz pclk=%d\n",
-                       note ? note : "",
-                       mode->h_active, mode->v_active,
-                       refresh / 1000, refresh % 1000,
-                       mode->pclk);
+               tegra_dc_put(dc);
        }
+       mutex_unlock(&dc->lock);
 }
-#else /* !DEBUG */
-static inline void print_mode(struct tegra_dc *dc,
-                       const struct tegra_dc_mode *mode, const char *note) { }
-#endif /* DEBUG */
-
-static inline void enable_dc_irq(unsigned int irq)
-{
-#ifdef CONFIG_TEGRA_SILICON_PLATFORM
-       enable_irq(irq);
-#else
-       /* Always disable DC interrupts on FPGA. */
-       disable_irq(irq);
-#endif
-}
-
-static inline void disable_dc_irq(unsigned int irq)
-{
-       disable_irq(irq);
-}
-
-static inline void tegra_dc_unmask_interrupt(struct tegra_dc *dc, u32 int_val)
-{
-       u32 val;
-
-       val = tegra_dc_readl(dc, DC_CMD_INT_MASK);
-       val |= int_val;
-       tegra_dc_writel(dc, val, DC_CMD_INT_MASK);
-}
-
-static inline void tegra_dc_mask_interrupt(struct tegra_dc *dc, u32 int_val)
-{
-       u32 val;
-
-       val = tegra_dc_readl(dc, DC_CMD_INT_MASK);
-       val &= ~int_val;
-       tegra_dc_writel(dc, val, DC_CMD_INT_MASK);
-}
-
-static int tegra_dc_program_mode(struct tegra_dc *dc, struct tegra_dc_mode *mode)
-{
-       unsigned long val;
-       unsigned long rate;
-       unsigned long div;
-       unsigned long pclk;
-
-       print_mode(dc, mode, __func__);
-
-       /* use default EMC rate when switching modes */
-       dc->new_emc_clk_rate = tegra_dc_get_default_emc_clk_rate(dc);
-       tegra_dc_program_bandwidth(dc, true);
-
-       tegra_dc_writel(dc, 0x0, DC_DISP_DISP_TIMING_OPTIONS);
-       tegra_dc_writel(dc, mode->h_ref_to_sync | (mode->v_ref_to_sync << 16),
-                       DC_DISP_REF_TO_SYNC);
-       tegra_dc_writel(dc, mode->h_sync_width | (mode->v_sync_width << 16),
-                       DC_DISP_SYNC_WIDTH);
-       tegra_dc_writel(dc, mode->h_back_porch | (mode->v_back_porch << 16),
-                       DC_DISP_BACK_PORCH);
-       tegra_dc_writel(dc, mode->h_active | (mode->v_active << 16),
-                       DC_DISP_DISP_ACTIVE);
-       tegra_dc_writel(dc, mode->h_front_porch | (mode->v_front_porch << 16),
-                       DC_DISP_FRONT_PORCH);
-
-       tegra_dc_writel(dc, DE_SELECT_ACTIVE | DE_CONTROL_NORMAL,
-                       DC_DISP_DATA_ENABLE_OPTIONS);
-
-       /* TODO: MIPI/CRT/HDMI clock cals */
-
-       val = DISP_DATA_FORMAT_DF1P1C;
-
-       if (dc->out->align == TEGRA_DC_ALIGN_MSB)
-               val |= DISP_DATA_ALIGNMENT_MSB;
-       else
-               val |= DISP_DATA_ALIGNMENT_LSB;
-
-       if (dc->out->order == TEGRA_DC_ORDER_RED_BLUE)
-               val |= DISP_DATA_ORDER_RED_BLUE;
-       else
-               val |= DISP_DATA_ORDER_BLUE_RED;
-
-       tegra_dc_writel(dc, val, DC_DISP_DISP_INTERFACE_CONTROL);
-
-       rate = tegra_dc_clk_get_rate(dc);
-
-       pclk = tegra_dc_pclk_round_rate(dc, mode->pclk);
-       trace_printk("%s:pclk=%ld\n", dc->ndev->name, pclk);
-       if (pclk < (mode->pclk / 100 * 99) ||
-           pclk > (mode->pclk / 100 * 109)) {
-               dev_err(&dc->ndev->dev,
-                       "can't divide %ld clock to %d -1/+9%% %ld %d %d\n",
-                       rate, mode->pclk,
-                       pclk, (mode->pclk / 100 * 99),
-                       (mode->pclk / 100 * 109));
-               return -EINVAL;
-       }
-
-       div = (rate * 2 / pclk) - 2;
-       trace_printk("%s:div=%ld\n", dc->ndev->name, div);
-
-       tegra_dc_writel(dc, 0x00010001,
-                       DC_DISP_SHIFT_CLOCK_OPTIONS);
-       tegra_dc_writel(dc, PIXEL_CLK_DIVIDER_PCD1 | SHIFT_CLK_DIVIDER(div),
-                       DC_DISP_DISP_CLOCK_CONTROL);
-
-#ifdef CONFIG_SWITCH
-       switch_set_state(&dc->modeset_switch,
-                        (mode->h_active << 16) | mode->v_active);
-#endif
-
-       tegra_dc_writel(dc, GENERAL_UPDATE, DC_CMD_STATE_CONTROL);
-       tegra_dc_writel(dc, GENERAL_ACT_REQ, DC_CMD_STATE_CONTROL);
-
-       print_mode_info(dc, dc->mode);
-       return 0;
-}
-
-
-int tegra_dc_set_mode(struct tegra_dc *dc, const struct tegra_dc_mode *mode)
-{
-       memcpy(&dc->mode, mode, sizeof(dc->mode));
-
-       print_mode(dc, mode, __func__);
-
-       return 0;
-}
-EXPORT_SYMBOL(tegra_dc_set_mode);
-
-int tegra_dc_set_fb_mode(struct tegra_dc *dc,
-               const struct fb_videomode *fbmode, bool stereo_mode)
-{
-       struct tegra_dc_mode mode;
-
-       if (!fbmode->pixclock)
-               return -EINVAL;
-
-       mode.pclk = PICOS2KHZ(fbmode->pixclock) * 1000;
-       mode.h_sync_width = fbmode->hsync_len;
-       mode.v_sync_width = fbmode->vsync_len;
-       mode.h_back_porch = fbmode->left_margin;
-       mode.v_back_porch = fbmode->upper_margin;
-       mode.h_active = fbmode->xres;
-       mode.v_active = fbmode->yres;
-       mode.h_front_porch = fbmode->right_margin;
-       mode.v_front_porch = fbmode->lower_margin;
-       mode.stereo_mode = stereo_mode;
-       if (dc->out->type == TEGRA_DC_OUT_HDMI) {
-               /* HDMI controller requires h_ref=1, v_ref=1 */
-               mode.h_ref_to_sync = 1;
-               mode.v_ref_to_sync = 1;
-       } else {
-               calc_ref_to_sync(&mode);
-       }
-       if (!check_ref_to_sync(&mode)) {
-               dev_err(&dc->ndev->dev,
-                               "Display timing doesn't meet restrictions.\n");
-               return -EINVAL;
-       }
-       dev_info(&dc->ndev->dev, "Using mode %dx%d pclk=%d href=%d vref=%d\n",
-               mode.h_active, mode.v_active, mode.pclk,
-               mode.h_ref_to_sync, mode.v_ref_to_sync
-       );
-
-#ifndef CONFIG_TEGRA_HDMI_74MHZ_LIMIT
-       /* Double the pixel clock and update v_active only for frame packed mode */
-       if (mode.stereo_mode) {
-               mode.pclk *= 2;
-               /* total v_active = yres*2 + activespace */
-               mode.v_active = fbmode->yres*2 +
-                               fbmode->vsync_len +
-                               fbmode->upper_margin +
-                               fbmode->lower_margin;
-       }
-#endif
-
-       mode.flags = 0;
-
-       if (!(fbmode->sync & FB_SYNC_HOR_HIGH_ACT))
-               mode.flags |= TEGRA_DC_MODE_FLAG_NEG_H_SYNC;
-
-       if (!(fbmode->sync & FB_SYNC_VERT_HIGH_ACT))
-               mode.flags |= TEGRA_DC_MODE_FLAG_NEG_V_SYNC;
-
-       return tegra_dc_set_mode(dc, &mode);
-}
-EXPORT_SYMBOL(tegra_dc_set_fb_mode);
 
 void
 tegra_dc_config_pwm(struct tegra_dc *dc, struct tegra_dc_pwm_params *cfg)
@@ -1989,8 +1236,7 @@ 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);
+       tegra_dc_get(dc);
 
        ctrl = ((cfg->period << PM_PERIOD_SHIFT) |
                (cfg->clk_div << PM_CLK_DIVIDER_SHIFT) |
@@ -2000,11 +1246,6 @@ tegra_dc_config_pwm(struct tegra_dc *dc, struct tegra_dc_pwm_params *cfg)
        cmd_state = tegra_dc_readl(dc, DC_CMD_STATE_ACCESS);
        tegra_dc_writel(dc, (cmd_state | (1 << 2)), DC_CMD_STATE_ACCESS);
 
-       if (cfg->switch_to_sfio && cfg->gpio_conf_to_sfio)
-               cfg->switch_to_sfio(cfg->gpio_conf_to_sfio);
-       else
-               dev_err(&dc->ndev->dev, "Error: Need gpio_conf_to_sfio\n");
-
        switch (cfg->which_pwm) {
        case TEGRA_PWM_PM0:
                /* Select the LM0 on PM0 */
@@ -2029,6 +1270,7 @@ tegra_dc_config_pwm(struct tegra_dc *dc, struct tegra_dc_pwm_params *cfg)
                break;
        }
        tegra_dc_writel(dc, cmd_state, DC_CMD_STATE_ACCESS);
+       tegra_dc_put(dc);
        mutex_unlock(&dc->lock);
 }
 EXPORT_SYMBOL(tegra_dc_config_pwm);
@@ -2099,11 +1341,27 @@ void tegra_dc_set_out_pin_polars(struct tegra_dc *dc,
        tegra_dc_writel(dc, pol3, DC_COM_PIN_OUTPUT_POLARITY3);
 }
 
+static struct tegra_dc_mode *tegra_dc_get_override_mode(struct tegra_dc *dc)
+{
+       if (dc->out->type == TEGRA_DC_OUT_RGB ||
+               dc->out->type == TEGRA_DC_OUT_HDMI ||
+               dc->out->type == TEGRA_DC_OUT_DSI)
+               return override_disp_mode[dc->out->type].pclk ?
+                       &override_disp_mode[dc->out->type] : NULL;
+       else
+               return NULL;
+}
+
 static void tegra_dc_set_out(struct tegra_dc *dc, struct tegra_dc_out *out)
 {
+       struct tegra_dc_mode *mode;
+
        dc->out = out;
+       mode = tegra_dc_get_override_mode(dc);
 
-       if (out->n_modes > 0)
+       if (mode)
+               tegra_dc_set_mode(dc, mode);
+       else if (out->n_modes > 0)
                tegra_dc_set_mode(dc, &dc->out->modes[0]);
 
        switch (out->type) {
@@ -2119,6 +1377,12 @@ static void tegra_dc_set_out(struct tegra_dc *dc, struct tegra_dc_out *out)
                dc->out_ops = &tegra_dc_dsi_ops;
                break;
 
+#ifdef CONFIG_TEGRA_DP
+       case TEGRA_DC_OUT_DP:
+               dc->out_ops = &tegra_dc_dp_ops;
+               break;
+#endif
+
        default:
                dc->out_ops = NULL;
                break;
@@ -2126,7 +1390,15 @@ static void tegra_dc_set_out(struct tegra_dc *dc, struct tegra_dc_out *out)
 
        if (dc->out_ops && dc->out_ops->init)
                dc->out_ops->init(dc);
+}
 
+/* returns on error: -EINVAL
+ * on success: TEGRA_DC_OUT_RGB, TEGRA_DC_OUT_HDMI, or TEGRA_DC_OUT_DSI. */
+int tegra_dc_get_out(const struct tegra_dc *dc)
+{
+       if (dc && dc->out)
+               return dc->out->type;
+       return -EINVAL;
 }
 
 unsigned tegra_dc_get_out_height(const struct tegra_dc *dc)
@@ -2159,22 +1431,29 @@ EXPORT_SYMBOL(tegra_dc_get_out_max_pixclock);
 void tegra_dc_enable_crc(struct tegra_dc *dc)
 {
        u32 val;
-       tegra_dc_io_start(dc);
+
+       mutex_lock(&dc->lock);
+       tegra_dc_get(dc);
 
        val = CRC_ALWAYS_ENABLE | CRC_INPUT_DATA_ACTIVE_DATA |
                CRC_ENABLE_ENABLE;
        tegra_dc_writel(dc, val, DC_COM_CRC_CONTROL);
        tegra_dc_writel(dc, GENERAL_UPDATE, DC_CMD_STATE_CONTROL);
        tegra_dc_writel(dc, GENERAL_ACT_REQ, DC_CMD_STATE_CONTROL);
+       tegra_dc_put(dc);
+       mutex_unlock(&dc->lock);
 }
 
 void tegra_dc_disable_crc(struct tegra_dc *dc)
 {
+       mutex_lock(&dc->lock);
+       tegra_dc_get(dc);
        tegra_dc_writel(dc, 0x0, DC_COM_CRC_CONTROL);
        tegra_dc_writel(dc, GENERAL_UPDATE, DC_CMD_STATE_CONTROL);
        tegra_dc_writel(dc, GENERAL_ACT_REQ, DC_CMD_STATE_CONTROL);
 
-       tegra_dc_io_end(dc);
+       tegra_dc_put(dc);
+       mutex_unlock(&dc->lock);
 }
 
 u32 tegra_dc_read_checksum_latched(struct tegra_dc *dc)
@@ -2182,21 +1461,98 @@ u32 tegra_dc_read_checksum_latched(struct tegra_dc *dc)
        int crc = 0;
 
        if (!dc) {
-               dev_err(&dc->ndev->dev, "Failed to get dc.\n");
+               pr_err("Failed to get dc: NULL parameter.\n");
                goto crc_error;
        }
 
-#ifndef CONFIG_TEGRA_SIMULATION_PLATFORM
-       /* TODO: Replace mdelay with code to sync VBlANK, since
-        * DC_COM_CRC_CHECKSUM_LATCHED is available after VBLANK */
-       mdelay(TEGRA_CRC_LATCHED_DELAY);
-#endif
+       if (!tegra_platform_is_linsim())
+               /* TODO: Replace mdelay with code to sync VBlANK, since
+                * DC_COM_CRC_CHECKSUM_LATCHED is available after VBLANK */
+               mdelay(TEGRA_CRC_LATCHED_DELAY);
 
+       mutex_lock(&dc->lock);
+       tegra_dc_get(dc);
        crc = tegra_dc_readl(dc, DC_COM_CRC_CHECKSUM_LATCHED);
+       tegra_dc_put(dc);
+       mutex_unlock(&dc->lock);
 crc_error:
        return crc;
 }
 
+static bool tegra_dc_windows_are_dirty(struct tegra_dc *dc)
+{
+       u32 val;
+
+       if (tegra_platform_is_linsim())
+               return false;
+
+       val = tegra_dc_readl(dc, DC_CMD_STATE_CONTROL);
+       if (val & WIN_ALL_ACT_REQ)
+               return true;
+
+       return false;
+}
+
+static inline void enable_dc_irq(const struct tegra_dc *dc)
+{
+       if (tegra_platform_is_fpga())
+               /* Always disable DC interrupts on FPGA. */
+               disable_irq(dc->irq);
+       else
+               enable_irq(dc->irq);
+}
+
+void tegra_dc_get_fbvblank(struct tegra_dc *dc, struct fb_vblank *vblank)
+{
+       if (dc->out->flags & TEGRA_DC_OUT_ONE_SHOT_MODE)
+               vblank->flags = FB_VBLANK_HAVE_VSYNC;
+}
+
+int tegra_dc_wait_for_vsync(struct tegra_dc *dc)
+{
+       int ret = -ENOTTY;
+
+       if (!(dc->out->flags & TEGRA_DC_OUT_ONE_SHOT_MODE) || !dc->enabled)
+               return ret;
+
+       /*
+        * Logic is as follows
+        * a) Indicate we need a vblank.
+        * b) Wait for completion to be signalled from isr.
+        * c) Initialize completion for next iteration.
+        */
+
+       mutex_lock(&dc->one_shot_lp_lock);
+       tegra_dc_get(dc);
+       dc->out->user_needs_vblank = true;
+       tegra_dc_unmask_interrupt(dc, MSF_INT);
+
+       ret = wait_for_completion_interruptible(&dc->out->user_vblank_comp);
+       init_completion(&dc->out->user_vblank_comp);
+       tegra_dc_mask_interrupt(dc, MSF_INT);
+       tegra_dc_put(dc);
+       mutex_unlock(&dc->one_shot_lp_lock);
+
+       return ret;
+}
+
+static void tegra_dc_prism_update_backlight(struct tegra_dc *dc)
+{
+       /* Do the actual brightness update outside of the mutex dc->lock */
+       if (dc->out->sd_settings && !dc->out->sd_settings->bl_device &&
+               dc->out->sd_settings->bl_device_name) {
+               char *bl_device_name =
+                       dc->out->sd_settings->bl_device_name;
+               dc->out->sd_settings->bl_device =
+                       get_backlight_device_by_name(bl_device_name);
+       }
+
+       if (dc->out->sd_settings && dc->out->sd_settings->bl_device) {
+               struct backlight_device *bl = dc->out->sd_settings->bl_device;
+               backlight_update_status(bl);
+       }
+}
+
 static void tegra_dc_vblank(struct work_struct *work)
 {
        struct tegra_dc *dc = container_of(work, struct tegra_dc, vblank_work);
@@ -2209,6 +1565,7 @@ static void tegra_dc_vblank(struct work_struct *work)
                return;
        }
 
+       tegra_dc_get(dc);
        /* use the new frame's bandwidth setting instead of max(current, new),
         * skip this if we're using tegra_dc_one_shot_worker() */
        if (!(dc->out->flags & TEGRA_DC_OUT_ONE_SHOT_MODE))
@@ -2219,7 +1576,7 @@ static void tegra_dc_vblank(struct work_struct *work)
                clear_bit(V_BLANK_FLIP, &dc->vblank_ref_count);
 
        /* Update the SD brightness */
-       if (dc->enabled && dc->out->sd_settings) {
+       if (dc->out->sd_settings && !dc->out->sd_settings->use_vpulse2) {
                nvsd_updated = nvsd_update_brightness(dc);
                /* Ref-count vblank if nvsd is on-going. Otherwise, clean the
                 * V_BLANK_NVSD bit of vblank ref-count. */
@@ -2235,17 +1592,12 @@ static void tegra_dc_vblank(struct work_struct *work)
        if (!dc->vblank_ref_count)
                tegra_dc_mask_interrupt(dc, V_BLANK_INT);
 
+       tegra_dc_put(dc);
        mutex_unlock(&dc->lock);
 
-       /* Do the actual brightness update outside of the mutex */
-       if (nvsd_updated && dc->out->sd_settings &&
-           dc->out->sd_settings->bl_device) {
-
-               struct platform_device *pdev = dc->out->sd_settings->bl_device;
-               struct backlight_device *bl = platform_get_drvdata(pdev);
-               if (bl)
-                       backlight_update_status(bl);
-       }
+       /* Do the actual brightness update outside of the mutex dc->lock */
+       if (nvsd_updated)
+               tegra_dc_prism_update_backlight(dc);
 }
 
 static void tegra_dc_one_shot_worker(struct work_struct *work)
@@ -2257,8 +1609,11 @@ static void tegra_dc_one_shot_worker(struct work_struct *work)
        /* 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);
+       if (dc->out_ops->idle) {
+               tegra_dc_io_start(dc);
+               dc->out_ops->idle(dc);
+               tegra_dc_io_end(dc);
+       }
 
        mutex_unlock(&dc->lock);
 }
@@ -2268,52 +1623,70 @@ static void tegra_dc_one_shot_worker(struct work_struct *work)
 static u64 tegra_dc_underflow_count(struct tegra_dc *dc, unsigned reg)
 {
        unsigned count = tegra_dc_readl(dc, reg);
+
        tegra_dc_writel(dc, 0, reg);
        return ((count & 0x80000000) == 0) ? count : 10000000000ll;
 }
 
 static void tegra_dc_underflow_handler(struct tegra_dc *dc)
 {
-       u32 val;
        int i;
 
        dc->stats.underflows++;
-       if (dc->underflow_mask & WIN_A_UF_INT) {
+       if (dc->underflow_mask & WIN_A_UF_INT)
                dc->stats.underflows_a += tegra_dc_underflow_count(dc,
                        DC_WINBUF_AD_UFLOW_STATUS);
-               trace_printk("%s:Window A Underflow\n", dc->ndev->name);
-       }
-       if (dc->underflow_mask & WIN_B_UF_INT) {
+       if (dc->underflow_mask & WIN_B_UF_INT)
                dc->stats.underflows_b += tegra_dc_underflow_count(dc,
                        DC_WINBUF_BD_UFLOW_STATUS);
-               trace_printk("%s:Window B Underflow\n", dc->ndev->name);
-       }
-       if (dc->underflow_mask & WIN_C_UF_INT) {
+       if (dc->underflow_mask & WIN_C_UF_INT)
                dc->stats.underflows_c += tegra_dc_underflow_count(dc,
                        DC_WINBUF_CD_UFLOW_STATUS);
-               trace_printk("%s:Window C Underflow\n", dc->ndev->name);
-       }
+#if defined(CONFIG_ARCH_TEGRA_14x_SOC)
+       if (dc->underflow_mask & HC_UF_INT)
+               dc->stats.underflows_h += tegra_dc_underflow_count(dc,
+                       DC_WINBUF_HD_UFLOW_STATUS);
+       if (dc->underflow_mask & WIN_D_UF_INT)
+               dc->stats.underflows_d += tegra_dc_underflow_count(dc,
+                       DC_WINBUF_DD_UFLOW_STATUS);
+       if (dc->underflow_mask & WIN_T_UF_INT)
+               dc->stats.underflows_t += tegra_dc_underflow_count(dc,
+                       DC_WINBUF_TD_UFLOW_STATUS);
+#endif
 
        /* Check for any underflow reset conditions */
        for (i = 0; i < DC_N_WINDOWS; i++) {
-               if (dc->underflow_mask & (WIN_A_UF_INT << i)) {
+               u32 masks[] = {
+                       WIN_A_UF_INT,
+                       WIN_B_UF_INT,
+                       WIN_C_UF_INT,
+#if defined(CONFIG_ARCH_TEGRA_14x_SOC)
+                       WIN_D_UF_INT,
+                       HC_UF_INT,
+                       WIN_T_UF_INT,
+#endif
+               };
+
+               if (WARN_ONCE(i >= ARRAY_SIZE(masks),
+                       "underflow stats unsupported"))
+                       break; /* bail if the table above is missing entries */
+               if (!masks[i])
+                       continue; /* skip empty entries */
+
+               if (dc->underflow_mask & masks[i]) {
                        dc->windows[i].underflows++;
 
 #ifdef CONFIG_ARCH_TEGRA_2x_SOC
-                       if (dc->windows[i].underflows > 4) {
+                       if (i < 3 && dc->windows[i].underflows > 4) {
                                schedule_work(&dc->reset_work);
                                /* reset counter */
                                dc->windows[i].underflows = 0;
-                               trace_printk("%s:Reset work scheduled for "
-                                               "window %c\n",
-                                               dc->ndev->name, (65 + i));
+                               trace_display_reset(dc);
                        }
 #endif
 #ifdef CONFIG_ARCH_TEGRA_3x_SOC
-                       if (dc->windows[i].underflows > 4) {
-                               printk("%s:dc in underflow state."
-                                       " enable UF_LINE_FLUSH to clear up\n",
-                                       __func__);
+                       if (i < 3 && dc->windows[i].underflows > 4) {
+                               trace_display_reset(dc);
                                tegra_dc_writel(dc, UF_LINE_FLUSH,
                                                DC_DISP_DISP_MISC_CONTROL);
                                tegra_dc_writel(dc, GENERAL_UPDATE,
@@ -2337,57 +1710,68 @@ static void tegra_dc_underflow_handler(struct tegra_dc *dc)
        /* Clear the underflow mask now that we've checked it. */
        tegra_dc_writel(dc, dc->underflow_mask, DC_CMD_INT_STATUS);
        dc->underflow_mask = 0;
-       val = tegra_dc_readl(dc, DC_CMD_INT_MASK);
-       tegra_dc_writel(dc, val | ALL_UF_INT, DC_CMD_INT_MASK);
-       print_underflow_info(dc);
+       tegra_dc_unmask_interrupt(dc, ALL_UF_INT());
+       trace_underflow(dc);
 }
 
-#ifndef CONFIG_TEGRA_FPGA_PLATFORM
-static bool tegra_dc_windows_are_dirty(struct tegra_dc *dc)
+#if !defined(CONFIG_ARCH_TEGRA_2x_SOC) && !defined(CONFIG_ARCH_TEGRA_3x_SOC)
+static void tegra_dc_vpulse2(struct work_struct *work)
 {
-#ifndef CONFIG_TEGRA_SIMULATION_PLATFORM
-       u32 val;
+       struct tegra_dc *dc = container_of(work, struct tegra_dc, vpulse2_work);
+       bool nvsd_updated = false;
 
-       val = tegra_dc_readl(dc, DC_CMD_STATE_CONTROL);
-       if (val & (WIN_A_ACT_REQ | WIN_B_ACT_REQ | WIN_C_ACT_REQ))
-           return true;
-#endif
-       return false;
-}
+       mutex_lock(&dc->lock);
 
-static void tegra_dc_trigger_windows(struct tegra_dc *dc)
-{
-       u32 val, i;
-       u32 completed = 0;
-       u32 dirty = 0;
+       if (!dc->enabled) {
+               mutex_unlock(&dc->lock);
+               return;
+       }
 
-       val = tegra_dc_readl(dc, DC_CMD_STATE_CONTROL);
-       for (i = 0; i < DC_N_WINDOWS; i++) {
-               if (!(val & (WIN_A_ACT_REQ << i))) {
-                       dc->windows[i].dirty = 0;
-                       completed = 1;
+       tegra_dc_get(dc);
+
+       /* Clear the V_PULSE2_FLIP if no update */
+       if (!tegra_dc_windows_are_dirty(dc))
+               clear_bit(V_PULSE2_FLIP, &dc->vpulse2_ref_count);
+
+       /* Update the SD brightness */
+       if (dc->out->sd_settings && dc->out->sd_settings->use_vpulse2) {
+               nvsd_updated = nvsd_update_brightness(dc);
+               if (nvsd_updated) {
+                       set_bit(V_PULSE2_NVSD, &dc->vpulse2_ref_count);
+                       tegra_dc_unmask_interrupt(dc, V_PULSE2_INT);
                } else {
-                       dirty = 1;
+                       clear_bit(V_PULSE2_NVSD, &dc->vpulse2_ref_count);
                }
        }
 
-       if (!dirty) {
-               if (!(dc->out->flags & TEGRA_DC_OUT_ONE_SHOT_MODE))
-                       tegra_dc_mask_interrupt(dc, FRAME_END_INT);
-       }
+       /* Mask vpulse2 interrupt if ref-count is zero. */
+       if (!dc->vpulse2_ref_count)
+               tegra_dc_mask_interrupt(dc, V_PULSE2_INT);
+
+       tegra_dc_put(dc);
+       mutex_unlock(&dc->lock);
 
-       if (completed)
-               wake_up(&dc->wq);
+       /* Do the actual brightness update outside of the mutex dc->lock */
+       if (nvsd_updated)
+               tegra_dc_prism_update_backlight(dc);
 }
+#endif
 
 static void tegra_dc_one_shot_irq(struct tegra_dc *dc, unsigned long status)
 {
+       /* pending user vblank, so wakeup */
+       if ((status & (V_BLANK_INT | MSF_INT)) &&
+           (dc->out->user_needs_vblank)) {
+               dc->out->user_needs_vblank = false;
+               complete(&dc->out->user_vblank_comp);
+       }
+
        if (status & V_BLANK_INT) {
                /* Sync up windows. */
                tegra_dc_trigger_windows(dc);
 
                /* Schedule any additional bottom-half vblank actvities. */
-               schedule_work(&dc->vblank_work);
+               queue_work(system_freezable_wq, &dc->vblank_work);
        }
 
        if (status & FRAME_END_INT) {
@@ -2395,46 +1779,86 @@ static void tegra_dc_one_shot_irq(struct tegra_dc *dc, unsigned long status)
                if (!completion_done(&dc->frame_end_complete))
                        complete(&dc->frame_end_complete);
        }
+
+#if !defined(CONFIG_ARCH_TEGRA_2x_SOC) && !defined(CONFIG_ARCH_TEGRA_3x_SOC)
+       if (status & V_PULSE2_INT)
+               queue_work(system_freezable_wq, &dc->vpulse2_work);
+#endif
 }
 
 static void tegra_dc_continuous_irq(struct tegra_dc *dc, unsigned long status)
 {
        /* Schedule any additional bottom-half vblank actvities. */
        if (status & V_BLANK_INT)
-               schedule_work(&dc->vblank_work);
+               queue_work(system_freezable_wq, &dc->vblank_work);
 
        if (status & FRAME_END_INT) {
+               struct timespec tm = CURRENT_TIME;
+               dc->frame_end_timestamp = timespec_to_ns(&tm);
+               wake_up(&dc->timestamp_wq);
+
                /* Mark the frame_end as complete. */
                if (!completion_done(&dc->frame_end_complete))
                        complete(&dc->frame_end_complete);
 
                tegra_dc_trigger_windows(dc);
        }
-}
+
+#if !defined(CONFIG_ARCH_TEGRA_2x_SOC) && !defined(CONFIG_ARCH_TEGRA_3x_SOC)
+       if (status & V_PULSE2_INT)
+               queue_work(system_freezable_wq, &dc->vpulse2_work);
 #endif
+}
+
+/* XXX: Not sure if we limit look ahead to 1 frame */
+bool tegra_dc_is_within_n_vsync(struct tegra_dc *dc, s64 ts)
+{
+       BUG_ON(!dc->frametime_ns);
+       return ((ts - dc->frame_end_timestamp) < dc->frametime_ns);
+}
+
+bool tegra_dc_does_vsync_separate(struct tegra_dc *dc, s64 new_ts, s64 old_ts)
+{
+       BUG_ON(!dc->frametime_ns);
+       return (((new_ts - old_ts) > dc->frametime_ns)
+               || (div_s64((new_ts - dc->frame_end_timestamp), dc->frametime_ns)
+                       != div_s64((old_ts - dc->frame_end_timestamp),
+                               dc->frametime_ns)));
+}
 
 static irqreturn_t tegra_dc_irq(int irq, void *ptr)
 {
-#ifdef CONFIG_TEGRA_SILICON_PLATFORM
        struct tegra_dc *dc = ptr;
        unsigned long status;
        unsigned long underflow_mask;
        u32 val;
+       int need_disable = 0;
+
+       if (tegra_platform_is_fpga())
+               return IRQ_NONE;
+
+       mutex_lock(&dc->lock);
+       if (!dc->enabled || !tegra_dc_is_powered(dc)) {
+               mutex_unlock(&dc->lock);
+               return IRQ_HANDLED;
+       }
 
-       if (!nvhost_module_powered_ext(nvhost_get_parent(dc->ndev))) {
+       tegra_dc_get(dc);
+
+       if (!nvhost_module_powered_ext(dc->ndev)) {
                WARN(1, "IRQ when DC not powered!\n");
-               tegra_dc_io_start(dc);
                status = tegra_dc_readl(dc, DC_CMD_INT_STATUS);
                tegra_dc_writel(dc, status, DC_CMD_INT_STATUS);
-               tegra_dc_io_end(dc);
+               tegra_dc_put(dc);
+               mutex_unlock(&dc->lock);
                return IRQ_HANDLED;
        }
 
        /* clear all status flags except underflow, save those for the worker */
        status = tegra_dc_readl(dc, DC_CMD_INT_STATUS);
-       tegra_dc_writel(dc, status & ~ALL_UF_INT, DC_CMD_INT_STATUS);
+       tegra_dc_writel(dc, status & ~ALL_UF_INT(), DC_CMD_INT_STATUS);
        val = tegra_dc_readl(dc, DC_CMD_INT_MASK);
-       tegra_dc_writel(dc, val & ~ALL_UF_INT, DC_CMD_INT_MASK);
+       tegra_dc_writel(dc, val & ~ALL_UF_INT(), DC_CMD_INT_MASK);
 
        /*
         * Overlays can get thier internal state corrupted during and underflow
@@ -2442,7 +1866,7 @@ static irqreturn_t tegra_dc_irq(int irq, void *ptr)
         * if we get 4 consecutive frames with underflows, assume we're
         * hosed and reset.
         */
-       underflow_mask = status & ALL_UF_INT;
+       underflow_mask = status & ALL_UF_INT();
 
        /* Check underflow */
        if (underflow_mask) {
@@ -2456,13 +1880,20 @@ static irqreturn_t tegra_dc_irq(int irq, void *ptr)
        else
                tegra_dc_continuous_irq(dc, status);
 
+       /* update video mode if it has changed since the last frame */
+       if (status & (FRAME_END_INT | V_BLANK_INT))
+               if (tegra_dc_update_mode(dc))
+                       need_disable = 1; /* force display off on error */
+
+       tegra_dc_put(dc);
+       mutex_unlock(&dc->lock);
+
+       if (need_disable)
+               tegra_dc_disable(dc);
        return IRQ_HANDLED;
-#else /* CONFIG_TEGRA_SILICON_PLATFORM */
-       return IRQ_NONE;
-#endif /* !CONFIG_TEGRA_SILICON_PLATFORM */
 }
 
-static void tegra_dc_set_color_control(struct tegra_dc *dc)
+void tegra_dc_set_color_control(struct tegra_dc *dc)
 {
        u32 color_control;
 
@@ -2511,6 +1942,11 @@ static void tegra_dc_set_color_control(struct tegra_dc *dc)
        case TEGRA_DC_ORDERED_DITHER:
                color_control |= DITHER_CONTROL_ORDERED;
                break;
+#ifdef CONFIG_TEGRA_DC_TEMPORAL_DITHER
+       case TEGRA_DC_TEMPORAL_DITHER:
+               color_control |= DITHER_CONTROL_TEMPORAL;
+               break;
+#else
        case TEGRA_DC_ERRDIFF_DITHER:
                /* The line buffer for error-diffusion dither is limited
                 * to 1280 pixels per line. This limits the maximum
@@ -2520,10 +1956,13 @@ static void tegra_dc_set_color_control(struct tegra_dc *dc)
                BUG_ON(dc->mode.h_active > 1280);
                color_control |= DITHER_CONTROL_ERRDIFF;
                break;
+#endif
+       default:
+               dev_err(&dc->ndev->dev, "Error: Unsupported dithering mode\n");
        }
 
 #ifdef CONFIG_TEGRA_DC_CMU
-       if (dc->out->flags & TEGRA_DC_OUT_CMU_ENABLE)
+       if (dc->pdata->flags & TEGRA_DC_FLAG_CMU_ENABLE)
                color_control |= CMU_ENABLE;
 #endif
 
@@ -2532,53 +1971,41 @@ static void tegra_dc_set_color_control(struct tegra_dc *dc)
 
 static u32 get_syncpt(struct tegra_dc *dc, int idx)
 {
-       u32 syncpt_id;
+       if (idx >= 0 && idx < ARRAY_SIZE(dc->win_syncpt))
+               return dc->win_syncpt[idx];
+       BUG();
+}
 
-       switch (dc->ndev->id) {
-       case 0:
-               switch (idx) {
-               case 0:
-                       syncpt_id = NVSYNCPT_DISP0_A;
-                       break;
-               case 1:
-                       syncpt_id = NVSYNCPT_DISP0_B;
-                       break;
-               case 2:
-                       syncpt_id = NVSYNCPT_DISP0_C;
-                       break;
-               default:
-                       BUG();
-                       break;
-               }
-               break;
-       case 1:
-               switch (idx) {
-               case 0:
-                       syncpt_id = NVSYNCPT_DISP1_A;
-                       break;
-               case 1:
-                       syncpt_id = NVSYNCPT_DISP1_B;
-                       break;
-               case 2:
-                       syncpt_id = NVSYNCPT_DISP1_C;
-                       break;
-               default:
-                       BUG();
-                       break;
-               }
-               break;
-       default:
-               BUG();
-               break;
-       }
+static void tegra_dc_init_vpulse2_int(struct tegra_dc *dc)
+{
+#if !defined(CONFIG_ARCH_TEGRA_2x_SOC) && !defined(CONFIG_ARCH_TEGRA_3x_SOC)
+       u32 start, end;
+       unsigned long val;
+
+       val = V_PULSE2_H_POSITION(0) | V_PULSE2_LAST(0x1);
+       tegra_dc_writel(dc, val, DC_DISP_V_PULSE2_CONTROL);
+
+       start = dc->mode.v_ref_to_sync + dc->mode.v_sync_width +
+               dc->mode.v_back_porch + dc->mode.v_active;
+       end = start + 1;
+       val = V_PULSE2_START_A(start) + V_PULSE2_END_A(end);
+       tegra_dc_writel(dc, val, DC_DISP_V_PULSE2_POSITION_A);
 
-       return syncpt_id;
+       val = tegra_dc_readl(dc, DC_CMD_INT_ENABLE);
+       val |= V_PULSE2_INT;
+       tegra_dc_writel(dc, val , DC_CMD_INT_ENABLE);
+
+       tegra_dc_mask_interrupt(dc, V_PULSE2_INT);
+       tegra_dc_writel(dc, V_PULSE_2_ENABLE, DC_DISP_DISP_SIGNAL_OPTIONS0);
+#endif
 }
 
 static int tegra_dc_init(struct tegra_dc *dc)
 {
        int i;
+       int int_enable;
 
+       tegra_dc_io_start(dc);
        tegra_dc_writel(dc, 0x00000100, DC_CMD_GENERAL_INCR_SYNCPT_CNTRL);
        if (dc->ndev->id == 0) {
                tegra_mc_set_priority(TEGRA_MC_CLIENT_DISPLAY0A,
@@ -2587,8 +2014,11 @@ static int tegra_dc_init(struct tegra_dc *dc)
                                      TEGRA_MC_PRIO_MED);
                tegra_mc_set_priority(TEGRA_MC_CLIENT_DISPLAY0C,
                                      TEGRA_MC_PRIO_MED);
+#if defined(CONFIG_ARCH_TEGRA_2x_SOC) || defined(CONFIG_ARCH_TEGRA_3x_SOC)
+               /* only present on Tegra2 and 3 */
                tegra_mc_set_priority(TEGRA_MC_CLIENT_DISPLAY1B,
                                      TEGRA_MC_PRIO_MED);
+#endif
                tegra_mc_set_priority(TEGRA_MC_CLIENT_DISPLAYHC,
                                      TEGRA_MC_PRIO_HIGH);
        } else if (dc->ndev->id == 1) {
@@ -2598,13 +2028,17 @@ static int tegra_dc_init(struct tegra_dc *dc)
                                      TEGRA_MC_PRIO_MED);
                tegra_mc_set_priority(TEGRA_MC_CLIENT_DISPLAY0CB,
                                      TEGRA_MC_PRIO_MED);
+#if defined(CONFIG_ARCH_TEGRA_2x_SOC) || defined(CONFIG_ARCH_TEGRA_3x_SOC)
+               /* only present on Tegra2 and 3 */
                tegra_mc_set_priority(TEGRA_MC_CLIENT_DISPLAY1BB,
                                      TEGRA_MC_PRIO_MED);
+#endif
                tegra_mc_set_priority(TEGRA_MC_CLIENT_DISPLAYHCB,
                                      TEGRA_MC_PRIO_HIGH);
        }
        tegra_dc_writel(dc, 0x00000100 | dc->vblank_syncpt,
                        DC_CMD_CONT_SYNCPT_VSYNC);
+
        tegra_dc_writel(dc, 0x00004700, DC_CMD_INT_TYPE);
        tegra_dc_writel(dc, 0x0001c700, DC_CMD_INT_POLARITY);
        tegra_dc_writel(dc, 0x00202020, DC_DISP_MEM_HIGH_PRIORITY);
@@ -2613,12 +2047,31 @@ static int tegra_dc_init(struct tegra_dc *dc)
        tegra_dc_writel(dc, 0x00000000, DC_DISP_DISP_MISC_CONTROL);
 #endif
        /* enable interrupts for vblank, frame_end and underflows */
-       tegra_dc_writel(dc, (FRAME_END_INT | V_BLANK_INT | ALL_UF_INT),
-               DC_CMD_INT_ENABLE);
-       tegra_dc_writel(dc, ALL_UF_INT, DC_CMD_INT_MASK);
+       int_enable = (FRAME_END_INT | V_BLANK_INT | ALL_UF_INT());
+       /* for panels with one-shot mode enable tearing effect interrupt */
+       if (dc->out->flags & TEGRA_DC_OUT_ONE_SHOT_MODE)
+               int_enable |= MSF_INT;
 
+       tegra_dc_writel(dc, int_enable, DC_CMD_INT_ENABLE);
+       tegra_dc_writel(dc, ALL_UF_INT(), DC_CMD_INT_MASK);
+       tegra_dc_init_vpulse2_int(dc);
+
+#if !defined(CONFIG_TEGRA_DC_BLENDER_GEN2)
        tegra_dc_writel(dc, 0x00000000, DC_DISP_BORDER_COLOR);
+#else
+       tegra_dc_writel(dc, 0x00000000, DC_DISP_BLEND_BACKGROUND_COLOR);
+#endif
 
+#ifdef CONFIG_TEGRA_DC_CMU
+       if (dc->pdata->cmu) {
+               _tegra_dc_update_cmu(dc, dc->pdata->cmu);
+       } else {
+               if (dc->out->type == TEGRA_DC_OUT_HDMI)
+                       _tegra_dc_update_cmu(dc, &default_limited_cmu);
+               else
+                       _tegra_dc_update_cmu(dc, &default_cmu);
+       }
+#endif
        tegra_dc_set_color_control(dc);
        for (i = 0; i < DC_N_WINDOWS; i++) {
                struct tegra_dc_win *win = &dc->windows[i];
@@ -2629,8 +2082,11 @@ static int tegra_dc_init(struct tegra_dc *dc)
                tegra_dc_set_scaling_filter(dc);
        }
 
-       tegra_dc_init_cmu_defaults(&dc->cmu, &default_cmu);
-       tegra_dc_set_cmu(dc, &dc->cmu);
+#ifdef CONFIG_TEGRA_DC_WIN_H
+       /* Window H is set to window mode by default for t14x. */
+       tegra_dc_writel(dc, WINH_CURS_SELECT(1),
+                       DC_DISP_BLEND_CURSOR_CONTROL);
+#endif
 
        for (i = 0; i < dc->n_windows; i++) {
                u32 syncpt = get_syncpt(dc, i);
@@ -2641,53 +2097,82 @@ static int tegra_dc_init(struct tegra_dc *dc)
                        nvhost_syncpt_read_ext(dc->ndev, syncpt);
        }
 
-       print_mode(dc, &dc->mode, __func__);
+       trace_display_mode(dc, &dc->mode);
 
-       if (dc->mode.pclk)
-               if (tegra_dc_program_mode(dc, &dc->mode))
+       if (dc->mode.pclk) {
+               if (tegra_dc_program_mode(dc, &dc->mode)) {
+                       tegra_dc_io_end(dc);
                        return -EINVAL;
+               }
+       }
 
        /* Initialize SD AFTER the modeset.
           nvsd_init handles the sd_settings = NULL case. */
        nvsd_init(dc, dc->out->sd_settings);
 
+       tegra_dc_io_end(dc);
+
        return 0;
 }
 
 static bool _tegra_dc_controller_enable(struct tegra_dc *dc)
 {
        int failed_init = 0;
+       int i;
+
+       tegra_dc_unpowergate_locked(dc);
 
        if (dc->out->enable)
-               dc->out->enable();
+               dc->out->enable(&dc->ndev->dev);
 
        tegra_dc_setup_clk(dc, dc->clk);
        tegra_dc_clk_enable(dc);
+       tegra_dc_io_start(dc);
+
+       tegra_dc_power_on(dc);
 
        /* do not accept interrupts during initialization */
-       tegra_dc_writel(dc, 0, DC_CMD_INT_ENABLE);
        tegra_dc_writel(dc, 0, DC_CMD_INT_MASK);
 
-       enable_dc_irq(dc->irq);
+       enable_dc_irq(dc);
 
        failed_init = tegra_dc_init(dc);
        if (failed_init) {
-               _tegra_dc_controller_disable(dc);
+               tegra_dc_writel(dc, 0, DC_CMD_INT_MASK);
+               disable_irq_nosync(dc->irq);
+               tegra_dc_clear_bandwidth(dc);
+               tegra_dc_clk_disable(dc);
+               if (dc->out && dc->out->disable)
+                       dc->out->disable();
+               tegra_dc_io_end(dc);
                return false;
        }
 
        if (dc->out_ops && dc->out_ops->enable)
                dc->out_ops->enable(dc);
 
-       if (dc->out->postpoweron)
-               dc->out->postpoweron();
-
        /* force a full blending update */
-       dc->blend.z[0] = -1;
+       for (i = 0; i < DC_N_WINDOWS; i++)
+               dc->blend.z[i] = -1;
 
        tegra_dc_ext_enable(dc->ext);
 
-       trace_printk("%s:enable\n", dc->ndev->name);
+       trace_display_enable(dc);
+
+       tegra_dc_writel(dc, GENERAL_UPDATE, DC_CMD_STATE_CONTROL);
+       tegra_dc_writel(dc, GENERAL_ACT_REQ, DC_CMD_STATE_CONTROL);
+
+       if (dc->out->postpoweron)
+               dc->out->postpoweron();
+
+       tegra_log_resume_time();
+       /*
+        * We will need to reinitialize the display the next time panel
+        * is enabled.
+        */
+       dc->out->flags &= ~TEGRA_DC_OUT_INITIALIZED_MODE;
+
+       tegra_dc_io_end(dc);
        return true;
 }
 
@@ -2697,36 +2182,36 @@ static bool _tegra_dc_controller_reset_enable(struct tegra_dc *dc)
        bool ret = true;
 
        if (dc->out->enable)
-               dc->out->enable();
+               dc->out->enable(&dc->ndev->dev);
 
        tegra_dc_setup_clk(dc, dc->clk);
        tegra_dc_clk_enable(dc);
 
        if (dc->ndev->id == 0 && tegra_dcs[1] != NULL) {
                mutex_lock(&tegra_dcs[1]->lock);
-               disable_irq(tegra_dcs[1]->irq);
+               disable_irq_nosync(tegra_dcs[1]->irq);
        } else if (dc->ndev->id == 1 && tegra_dcs[0] != NULL) {
                mutex_lock(&tegra_dcs[0]->lock);
-               disable_irq(tegra_dcs[0]->irq);
+               disable_irq_nosync(tegra_dcs[0]->irq);
        }
 
        msleep(5);
        tegra_periph_reset_assert(dc->clk);
        msleep(2);
-#ifdef CONFIG_TEGRA_SILICON_PLATFORM
-       tegra_periph_reset_deassert(dc->clk);
-       msleep(1);
-#endif
+       if (tegra_platform_is_silicon()) {
+               tegra_periph_reset_deassert(dc->clk);
+               msleep(1);
+       }
 
        if (dc->ndev->id == 0 && tegra_dcs[1] != NULL) {
-               enable_dc_irq(tegra_dcs[1]->irq);
+               enable_dc_irq(tegra_dcs[1]);
                mutex_unlock(&tegra_dcs[1]->lock);
        } else if (dc->ndev->id == 1 && tegra_dcs[0] != NULL) {
-               enable_dc_irq(tegra_dcs[0]->irq);
+               enable_dc_irq(tegra_dcs[0]);
                mutex_unlock(&tegra_dcs[0]->lock);
        }
 
-       enable_dc_irq(dc->irq);
+       enable_dc_irq(dc);
 
        if (tegra_dc_init(dc)) {
                dev_err(&dc->ndev->dev, "cannot initialize\n");
@@ -2749,26 +2234,21 @@ static bool _tegra_dc_controller_reset_enable(struct tegra_dc *dc)
                _tegra_dc_controller_disable(dc);
        }
 
-       trace_printk("%s:reset enable\n", dc->ndev->name);
+       trace_display_reset(dc);
        return ret;
 }
 #endif
 
 static int _tegra_dc_set_default_videomode(struct tegra_dc *dc)
 {
-       return tegra_dc_set_fb_mode(dc, &tegra_dc_hdmi_fallback_mode, 0);
-}
-
-static bool _tegra_dc_enable(struct tegra_dc *dc)
-{
        if (dc->mode.pclk == 0) {
                switch (dc->out->type) {
                case TEGRA_DC_OUT_HDMI:
                /* DC enable called but no videomode is loaded.
                     Check if HDMI is connected, then set fallback mdoe */
                if (tegra_dc_hpd(dc)) {
-                       if (_tegra_dc_set_default_videomode(dc))
-                               return false;
+                       return tegra_dc_set_fb_mode(dc,
+                                       &tegra_dc_hdmi_fallback_mode, 0);
                } else
                        return false;
 
@@ -2784,12 +2264,33 @@ static bool _tegra_dc_enable(struct tegra_dc *dc)
                }
        }
 
+       return false;
+}
+
+int tegra_dc_set_default_videomode(struct tegra_dc *dc)
+{
+       return _tegra_dc_set_default_videomode(dc);
+}
+
+static bool _tegra_dc_enable(struct tegra_dc *dc)
+{
+       if (dc->mode.pclk == 0)
+               return false;
+
        if (!dc->out)
                return false;
 
-       tegra_dc_io_start(dc);
+       if (dc->enabled)
+               return true;
 
-       return _tegra_dc_controller_enable(dc);
+       pm_runtime_get_sync(&dc->ndev->dev);
+
+       if (!_tegra_dc_controller_enable(dc)) {
+               pm_runtime_put_sync(&dc->ndev->dev);
+               return false;
+       }
+
+       return true;
 }
 
 void tegra_dc_enable(struct tegra_dc *dc)
@@ -2800,13 +2301,15 @@ void tegra_dc_enable(struct tegra_dc *dc)
                dc->enabled = _tegra_dc_enable(dc);
 
        mutex_unlock(&dc->lock);
-       print_mode_info(dc, dc->mode);
+       trace_display_mode(dc, &dc->mode);
 }
 
 static void _tegra_dc_controller_disable(struct tegra_dc *dc)
 {
        unsigned i;
 
+       tegra_dc_get(dc);
+
        if (dc->out && dc->out->prepoweroff)
                dc->out->prepoweroff();
 
@@ -2814,11 +2317,10 @@ static void _tegra_dc_controller_disable(struct tegra_dc *dc)
                dc->out_ops->disable(dc);
 
        tegra_dc_writel(dc, 0, DC_CMD_INT_MASK);
-       tegra_dc_writel(dc, 0, DC_CMD_INT_ENABLE);
-       disable_irq(dc->irq);
+
+       disable_irq_nosync(dc->irq);
 
        tegra_dc_clear_bandwidth(dc);
-       tegra_dc_clk_disable(dc);
 
        if (dc->out && dc->out->disable)
                dc->out->disable();
@@ -2835,13 +2337,16 @@ static void _tegra_dc_controller_disable(struct tegra_dc *dc)
 
                /* flush any pending syncpt waits */
                while (dc->syncpt[i].min < dc->syncpt[i].max) {
-                       trace_printk("%s:syncpt flush id=%d\n", dc->ndev->name,
-                               dc->syncpt[i].id);
+                       trace_display_syncpt_flush(dc, dc->syncpt[i].id,
+                               dc->syncpt[i].min, dc->syncpt[i].max);
                        dc->syncpt[i].min++;
                        nvhost_syncpt_cpu_incr_ext(dc->ndev, dc->syncpt[i].id);
                }
        }
-       trace_printk("%s:disabled\n", dc->ndev->name);
+       trace_display_disable(dc);
+
+       tegra_dc_clk_disable(dc);
+       tegra_dc_put(dc);
 }
 
 void tegra_dc_stats_enable(struct tegra_dc *dc, bool enable)
@@ -2894,8 +2399,22 @@ void tegra_dc_blank(struct tegra_dc *dc)
 
 static void _tegra_dc_disable(struct tegra_dc *dc)
 {
+       if (dc->out->flags & TEGRA_DC_OUT_ONE_SHOT_MODE) {
+               mutex_lock(&dc->one_shot_lock);
+               cancel_delayed_work_sync(&dc->one_shot_work);
+       }
+
+       tegra_dc_io_start(dc);
        _tegra_dc_controller_disable(dc);
        tegra_dc_io_end(dc);
+
+       tegra_dc_powergate_locked(dc);
+
+       if (dc->out->flags & TEGRA_DC_OUT_ONE_SHOT_MODE)
+               mutex_unlock(&dc->one_shot_lock);
+       pm_runtime_put(&dc->ndev->dev);
+
+       tegra_log_suspend_time();
 }
 
 void tegra_dc_disable(struct tegra_dc *dc)
@@ -2905,16 +2424,9 @@ void tegra_dc_disable(struct tegra_dc *dc)
        /* it's important that new underflow work isn't scheduled before the
         * lock is acquired. */
        cancel_delayed_work_sync(&dc->underflow_work);
-       if (dc->out->flags & TEGRA_DC_OUT_ONE_SHOT_MODE) {
-               mutex_lock(&dc->one_shot_lock);
-               cancel_delayed_work_sync(&dc->one_shot_work);
-       }
 
        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;
 
@@ -2927,9 +2439,8 @@ void tegra_dc_disable(struct tegra_dc *dc)
 #endif
 
        mutex_unlock(&dc->lock);
-       if (dc->out->flags & TEGRA_DC_OUT_ONE_SHOT_MODE)
-               mutex_unlock(&dc->one_shot_lock);
-       print_mode_info(dc, dc->mode);
+       synchronize_irq(dc->irq);
+       trace_display_mode(dc, &dc->mode);
 }
 
 #ifdef CONFIG_ARCH_TEGRA_2x_SOC
@@ -2942,7 +2453,8 @@ static void tegra_dc_reset_worker(struct work_struct *work)
 
        mutex_lock(&shared_lock);
 
-       dev_warn(&dc->ndev->dev, "overlay stuck in underflow state.  resetting.\n");
+       dev_warn(&dc->ndev->dev,
+               "overlay stuck in underflow state.  resetting.\n");
 
        tegra_dc_ext_disable(dc->ext);
 
@@ -2983,7 +2495,7 @@ static void tegra_dc_reset_worker(struct work_struct *work)
 unlock:
        mutex_unlock(&dc->lock);
        mutex_unlock(&shared_lock);
-       trace_printk("%s:reset complete\n", dc->ndev->name);
+       trace_display_reset(dc);
 }
 #endif
 
@@ -2993,12 +2505,12 @@ 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);
+       tegra_dc_get(dc);
 
        if (dc->enabled) {
                tegra_dc_underflow_handler(dc);
        }
+       tegra_dc_put(dc);
        mutex_unlock(&dc->lock);
 }
 
@@ -3015,12 +2527,33 @@ static ssize_t switch_modeset_print_mode(struct switch_dev *sdev, char *buf)
 }
 #endif
 
-static int tegra_dc_probe(struct nvhost_device *ndev,
-       struct nvhost_device_id *id_table)
+static void tegra_dc_add_modes(struct tegra_dc *dc)
+{
+       struct fb_monspecs specs;
+       int i;
+
+       memset(&specs, 0, sizeof(specs));
+       specs.max_x = dc->mode.h_active * 1000;
+       specs.max_y = dc->mode.v_active * 1000;
+       specs.modedb_len = dc->out->n_modes;
+       specs.modedb = kzalloc(specs.modedb_len *
+               sizeof(struct fb_videomode), GFP_KERNEL);
+       for (i = 0; i < dc->out->n_modes; i++)
+               tegra_dc_to_fb_videomode(&specs.modedb[i],
+                       &dc->out->modes[i]);
+       tegra_fb_update_monspecs(dc->fb, &specs, NULL);
+       kfree(specs.modedb);
+}
+
+static int tegra_dc_probe(struct platform_device *ndev)
 {
        struct tegra_dc *dc;
+       struct tegra_dc_mode *mode;
        struct clk *clk;
+#ifndef CONFIG_TEGRA_ISOMGR
        struct clk *emc_clk;
+#endif
+       int isomgr_client_id = -1;
        struct resource *res;
        struct resource *base_res;
        struct resource *fb_mem = NULL;
@@ -3040,21 +2573,22 @@ static int tegra_dc_probe(struct nvhost_device *ndev,
                return -ENOMEM;
        }
 
-       irq = nvhost_get_irq_byname(ndev, "irq");
+       irq = platform_get_irq_byname(ndev, "irq");
        if (irq <= 0) {
                dev_err(&ndev->dev, "no irq\n");
                ret = -ENOENT;
                goto err_free;
        }
 
-       res = nvhost_get_resource_byname(ndev, IORESOURCE_MEM, "regs");
+       res = platform_get_resource_byname(ndev, IORESOURCE_MEM, "regs");
        if (!res) {
                dev_err(&ndev->dev, "no mem resource\n");
                ret = -ENOENT;
                goto err_free;
        }
 
-       base_res = request_mem_region(res->start, resource_size(res), ndev->name);
+       base_res = request_mem_region(res->start, resource_size(res),
+               ndev->name);
        if (!base_res) {
                dev_err(&ndev->dev, "request_mem_region failed\n");
                ret = -EBUSY;
@@ -3067,8 +2601,39 @@ static int tegra_dc_probe(struct nvhost_device *ndev,
                ret = -EBUSY;
                goto err_release_resource_reg;
        }
+       if (TEGRA_DISPLAY_BASE == res->start) {
+               dc->vblank_syncpt = NVSYNCPT_VBLANK0;
+               dc->win_syncpt[0] = NVSYNCPT_DISP0_A;
+               dc->win_syncpt[1] = NVSYNCPT_DISP0_B;
+               dc->win_syncpt[2] = NVSYNCPT_DISP0_C;
+               dc->valid_windows = 0x07;
+#ifdef CONFIG_ARCH_TEGRA_14x_SOC
+               dc->win_syncpt[3] = NVSYNCPT_DISP0_D;
+               dc->win_syncpt[4] = NVSYNCPT_DISP0_H;
+               dc->valid_windows |= 0x18;
+#endif
+               dc->powergate_id = TEGRA_POWERGATE_DISA;
+               isomgr_client_id = TEGRA_ISO_CLIENT_DISP_0;
+       } else if (TEGRA_DISPLAY2_BASE == res->start) {
+               dc->vblank_syncpt = NVSYNCPT_VBLANK1;
+               dc->win_syncpt[0] = NVSYNCPT_DISP1_A;
+               dc->win_syncpt[1] = NVSYNCPT_DISP1_B;
+               dc->win_syncpt[2] = NVSYNCPT_DISP1_C;
+               dc->valid_windows = 0x07;
+#ifdef CONFIG_ARCH_TEGRA_14x_SOC
+               dc->win_syncpt[4] = NVSYNCPT_DISP1_H;
+               dc->valid_windows |= 0x10;
+#endif
+               dc->powergate_id = TEGRA_POWERGATE_DISB;
+               isomgr_client_id = TEGRA_ISO_CLIENT_DISP_1;
+       } else {
+               dev_err(&ndev->dev,
+                       "Unknown base address %#08x: unable to assign syncpt\n",
+                       res->start);
+       }
+
 
-       fb_mem = nvhost_get_resource_byname(ndev, IORESOURCE_MEM, "fbmem");
+       fb_mem = platform_get_resource_byname(ndev, IORESOURCE_MEM, "fbmem");
 
        clk = clk_get(&ndev->dev, NULL);
        if (IS_ERR_OR_NULL(clk)) {
@@ -3077,16 +2642,8 @@ static int tegra_dc_probe(struct nvhost_device *ndev,
                goto err_iounmap_reg;
        }
 
-       emc_clk = clk_get(&ndev->dev, "emc");
-       if (IS_ERR_OR_NULL(emc_clk)) {
-               dev_err(&ndev->dev, "can't get emc clock\n");
-               ret = -ENOENT;
-               goto err_put_clk;
-       }
-
        dc->clk = clk;
-       dc->emc_clk = emc_clk;
-       dc->shift_clk_div = 1;
+       dc->shift_clk_div.mul = dc->shift_clk_div.div = 1;
        /* Initialize one shot work delay, it will be assigned by dsi
         * according to refresh rate later. */
        dc->one_shot_delay_ms = 40;
@@ -3097,21 +2654,23 @@ static int tegra_dc_probe(struct nvhost_device *ndev,
        dc->ndev = ndev;
        dc->pdata = ndev->dev.platform_data;
 
-       /*
-        * The emc is a shared clock, it will be set based on
-        * the requirements for each user on the bus.
-        */
-       dc->emc_clk_rate = 0;
+       dc->bw_kbps = 0;
 
        mutex_init(&dc->lock);
        mutex_init(&dc->one_shot_lock);
+       mutex_init(&dc->one_shot_lp_lock);
        init_completion(&dc->frame_end_complete);
        init_waitqueue_head(&dc->wq);
+       init_waitqueue_head(&dc->timestamp_wq);
 #ifdef CONFIG_ARCH_TEGRA_2x_SOC
        INIT_WORK(&dc->reset_work, tegra_dc_reset_worker);
 #endif
        INIT_WORK(&dc->vblank_work, tegra_dc_vblank);
        dc->vblank_ref_count = 0;
+#if !defined(CONFIG_ARCH_TEGRA_2x_SOC) && !defined(CONFIG_ARCH_TEGRA_3x_SOC)
+       INIT_WORK(&dc->vpulse2_work, tegra_dc_vpulse2);
+#endif
+       dc->vpulse2_ref_count = 0;
        INIT_DELAYED_WORK(&dc->underflow_work, tegra_dc_underflow_worker);
        INIT_DELAYED_WORK(&dc->one_shot_work, tegra_dc_one_shot_worker);
 
@@ -3129,16 +2688,18 @@ static int tegra_dc_probe(struct nvhost_device *ndev,
        ret = tegra_dc_set(dc, ndev->id);
        if (ret < 0) {
                dev_err(&ndev->dev, "can't add dc\n");
-               goto err_free_irq;
+               goto err_put_clk;
        }
 
-       nvhost_set_drvdata(ndev, dc);
+       platform_set_drvdata(ndev, dc);
 
 #ifdef CONFIG_SWITCH
        dc->modeset_switch.name = dev_name(&ndev->dev);
        dc->modeset_switch.state = 0;
        dc->modeset_switch.print_state = switch_modeset_print_mode;
-       switch_dev_register(&dc->modeset_switch);
+       ret = switch_dev_register(&dc->modeset_switch);
+       if (ret < 0)
+               dev_err(&ndev->dev, "failed to register switch driver\n");
 #endif
 
        tegra_dc_feature_register(dc);
@@ -3147,9 +2708,36 @@ static int tegra_dc_probe(struct nvhost_device *ndev,
                tegra_dc_set_out(dc, dc->pdata->default_out);
        else
                dev_err(&ndev->dev, "No default output specified.  Leaving output disabled.\n");
+       dc->mode_dirty = false; /* ignore changes tegra_dc_set_out has done */
 
-       dc->vblank_syncpt = (dc->ndev->id == 0) ?
-               NVSYNCPT_VBLANK0 : NVSYNCPT_VBLANK1;
+#ifdef CONFIG_TEGRA_ISOMGR
+       if (isomgr_client_id == -1) {
+               dc->isomgr_handle = NULL;
+       } else {
+               dc->isomgr_handle = tegra_isomgr_register(isomgr_client_id,
+                       tegra_dc_calc_min_bandwidth(dc),
+                       tegra_dc_bandwidth_renegotiate, dc);
+               if (IS_ERR(dc->isomgr_handle)) {
+                       dev_err(&dc->ndev->dev,
+                               "could not register isomgr. err=%ld\n",
+                               PTR_ERR(dc->isomgr_handle));
+                       ret = -ENOENT;
+                       goto err_put_clk;
+               }
+       }
+#else
+       /*
+        * The emc is a shared clock, it will be set based on
+        * the requirements for each user on the bus.
+        */
+       emc_clk = clk_get(&ndev->dev, "emc");
+       if (IS_ERR_OR_NULL(emc_clk)) {
+               dev_err(&ndev->dev, "can't get emc clock\n");
+               ret = -ENOENT;
+               goto err_put_clk;
+       }
+       dc->emc_clk = emc_clk;
+#endif
 
        dc->ext = tegra_dc_ext_register(ndev, dc);
        if (IS_ERR_OR_NULL(dc->ext)) {
@@ -3157,17 +2745,23 @@ static int tegra_dc_probe(struct nvhost_device *ndev,
                dc->ext = NULL;
        }
 
-       mutex_lock(&dc->lock);
-       if (dc->pdata->flags & TEGRA_DC_FLAG_ENABLED)
-               dc->enabled = _tegra_dc_enable(dc);
-       mutex_unlock(&dc->lock);
-
        /* interrupt handler must be registered before tegra_fb_register() */
-       if (request_irq(irq, tegra_dc_irq, 0,
+       if (request_threaded_irq(irq, NULL, tegra_dc_irq, IRQF_ONESHOT,
                        dev_name(&ndev->dev), dc)) {
                dev_err(&ndev->dev, "request_irq %d failed\n", irq);
                ret = -EBUSY;
-               goto err_put_emc_clk;
+               goto err_disable_dc;
+       }
+       disable_dc_irq(dc);
+
+       tegra_pd_add_device(&ndev->dev);
+       pm_runtime_use_autosuspend(&ndev->dev);
+       pm_runtime_set_autosuspend_delay(&ndev->dev, 100);
+       pm_runtime_enable(&ndev->dev);
+
+       if (dc->pdata->flags & TEGRA_DC_FLAG_ENABLED) {
+               _tegra_dc_set_default_videomode(dc);
+               dc->enabled = _tegra_dc_enable(dc);
        }
 
        tegra_dc_create_debugfs(dc);
@@ -3175,7 +2769,7 @@ static int tegra_dc_probe(struct nvhost_device *ndev,
        dev_info(&ndev->dev, "probed\n");
 
        if (dc->pdata->fb) {
-               if (dc->pdata->fb->bits_per_pixel == -1) {
+               if (dc->enabled && dc->pdata->fb->bits_per_pixel == -1) {
                        unsigned long fmt;
                        tegra_dc_writel(dc,
                                        WINDOW_A_SELECT << dc->pdata->fb->win,
@@ -3186,27 +2780,62 @@ static int tegra_dc_probe(struct nvhost_device *ndev,
                                tegra_dc_fmt_bpp(fmt);
                }
 
+               mode = tegra_dc_get_override_mode(dc);
+               if (mode) {
+                       dc->pdata->fb->xres = mode->h_active;
+                       dc->pdata->fb->yres = mode->v_active;
+               }
+
+               tegra_dc_io_start(dc);
                dc->fb = tegra_fb_register(ndev, dc, dc->pdata->fb, fb_mem);
-               if (IS_ERR_OR_NULL(dc->fb))
+               tegra_dc_io_end(dc);
+               if (IS_ERR_OR_NULL(dc->fb)) {
                        dc->fb = NULL;
+                       dev_err(&ndev->dev, "failed to register fb\n");
+                       goto err_remove_debugfs;
+               }
        }
 
+       if (dc->out && dc->out->n_modes)
+               tegra_dc_add_modes(dc);
+
        if (dc->out && dc->out->hotplug_init)
-               dc->out->hotplug_init();
+               dc->out->hotplug_init(&ndev->dev);
 
        if (dc->out_ops && dc->out_ops->detect)
                dc->out_ops->detect(dc);
        else
                dc->connected = true;
 
+       /* Powergate display module when it's unconnected. */
+       if (!tegra_dc_get_connected(dc))
+               tegra_dc_powergate_locked(dc);
+
        tegra_dc_create_sysfs(&dc->ndev->dev);
 
        return 0;
 
-err_free_irq:
+err_remove_debugfs:
+       tegra_dc_remove_debugfs(dc);
        free_irq(irq, dc);
-err_put_emc_clk:
+err_disable_dc:
+       if (dc->ext) {
+               tegra_dc_ext_disable(dc->ext);
+               tegra_dc_ext_unregister(dc->ext);
+       }
+       mutex_lock(&dc->lock);
+       if (dc->enabled)
+               _tegra_dc_disable(dc);
+       dc->enabled = false;
+       mutex_unlock(&dc->lock);
+#ifdef CONFIG_SWITCH
+       switch_dev_unregister(&dc->modeset_switch);
+#endif
+#ifdef CONFIG_TEGRA_ISOMGR
+       tegra_isomgr_unregister(dc->isomgr_handle);
+#else
        clk_put(emc_clk);
+#endif
 err_put_clk:
        clk_put(clk);
 err_iounmap_reg:
@@ -3221,9 +2850,9 @@ err_free:
        return ret;
 }
 
-static int tegra_dc_remove(struct nvhost_device *ndev)
+static int tegra_dc_remove(struct platform_device *ndev)
 {
-       struct tegra_dc *dc = nvhost_get_drvdata(ndev);
+       struct tegra_dc *dc = platform_get_drvdata(ndev);
 
        tegra_dc_remove_sysfs(&dc->ndev->dev);
        tegra_dc_remove_debugfs(dc);
@@ -3239,34 +2868,47 @@ static int tegra_dc_remove(struct nvhost_device *ndev)
        if (dc->ext)
                tegra_dc_ext_unregister(dc->ext);
 
+       mutex_lock(&dc->lock);
        if (dc->enabled)
                _tegra_dc_disable(dc);
+       dc->enabled = false;
+       mutex_unlock(&dc->lock);
+       synchronize_irq(dc->irq); /* wait for IRQ handlers to finish */
 
 #ifdef CONFIG_SWITCH
        switch_dev_unregister(&dc->modeset_switch);
 #endif
        free_irq(dc->irq, dc);
+#ifdef CONFIG_TEGRA_ISOMGR
+       if (dc->isomgr_handle) {
+               tegra_isomgr_unregister(dc->isomgr_handle);
+               dc->isomgr_handle = NULL;
+       }
+#else
        clk_put(dc->emc_clk);
+#endif
        clk_put(dc->clk);
        iounmap(dc->base);
        if (dc->fb_mem)
                release_resource(dc->base_res);
        kfree(dc);
        tegra_dc_set(NULL, ndev->id);
+
        return 0;
 }
 
 #ifdef CONFIG_PM
-static int tegra_dc_suspend(struct nvhost_device *ndev, pm_message_t state)
+static int tegra_dc_suspend(struct platform_device *ndev, pm_message_t state)
 {
-       struct tegra_dc *dc = nvhost_get_drvdata(ndev);
+       struct tegra_dc *dc = platform_get_drvdata(ndev);
 
-       trace_printk("%s:suspend\n", dc->ndev->name);
+       trace_display_suspend(dc);
        dev_info(&ndev->dev, "suspend\n");
 
        tegra_dc_ext_disable(dc->ext);
 
        mutex_lock(&dc->lock);
+       tegra_dc_io_start(dc);
 
        if (dc->out_ops && dc->out_ops->suspend)
                dc->out_ops->suspend(dc);
@@ -3286,26 +2928,34 @@ static int tegra_dc_suspend(struct nvhost_device *ndev, pm_message_t state)
                        msleep(100);
        }
 
+       tegra_dc_io_end(dc);
        mutex_unlock(&dc->lock);
+       synchronize_irq(dc->irq); /* wait for IRQ handlers to finish */
 
        return 0;
 }
 
-static int tegra_dc_resume(struct nvhost_device *ndev)
+static int tegra_dc_resume(struct platform_device *ndev)
 {
-       struct tegra_dc *dc = nvhost_get_drvdata(ndev);
+       struct tegra_dc *dc = platform_get_drvdata(ndev);
 
-       trace_printk("%s:resume\n", dc->ndev->name);
+       trace_display_resume(dc);
        dev_info(&ndev->dev, "resume\n");
 
        mutex_lock(&dc->lock);
        dc->suspended = false;
 
-       if (dc->enabled)
-               _tegra_dc_enable(dc);
+       /* To pan the fb on resume */
+       tegra_fb_pan_display_reset(dc->fb);
+
+       if (dc->enabled) {
+               dc->enabled = false;
+               _tegra_dc_set_default_videomode(dc);
+               dc->enabled = _tegra_dc_enable(dc);
+       }
 
        if (dc->out && dc->out->hotplug_init)
-               dc->out->hotplug_init();
+               dc->out->hotplug_init(&ndev->dev);
 
        if (dc->out_ops && dc->out_ops->resume)
                dc->out_ops->resume(dc);
@@ -3316,14 +2966,16 @@ static int tegra_dc_resume(struct nvhost_device *ndev)
 
 #endif /* CONFIG_PM */
 
-static void tegra_dc_shutdown(struct nvhost_device *ndev)
+static void tegra_dc_shutdown(struct platform_device *ndev)
 {
-       struct tegra_dc *dc = nvhost_get_drvdata(ndev);
+       struct tegra_dc *dc = platform_get_drvdata(ndev);
 
        if (!dc || !dc->enabled)
                return;
 
-       tegra_dc_blank(dc);
+       /* Hack: no windows blanking for simulation to save shutdown time */
+       if (!tegra_platform_is_linsim())
+               tegra_dc_blank(dc);
        tegra_dc_disable(dc);
 }
 
@@ -3350,7 +3002,7 @@ int suspend;
 
 module_param_call(suspend, suspend_set, suspend_get, &suspend, 0644);
 
-struct nvhost_driver tegra_dc_driver = {
+struct platform_driver tegra_dc_driver = {
        .driver = {
                .name = "tegradc",
                .owner = THIS_MODULE,
@@ -3364,17 +3016,85 @@ struct nvhost_driver tegra_dc_driver = {
        .shutdown = tegra_dc_shutdown,
 };
 
+#ifndef MODULE
+static int __init parse_disp_params(char *options, struct tegra_dc_mode *mode)
+{
+       int i, params[11];
+       char *p;
+
+       for (i = 0; i < ARRAY_SIZE(params); i++) {
+               if ((p = strsep(&options, ",")) != NULL) {
+                       if (*p)
+                               params[i] = simple_strtoul(p, &p, 10);
+               } else
+                       return -EINVAL;
+       }
+
+       if ((mode->pclk = params[0]) == 0)
+               return -EINVAL;
+
+       mode->h_active      = params[1];
+       mode->v_active      = params[2];
+       mode->h_ref_to_sync = params[3];
+       mode->v_ref_to_sync = params[4];
+       mode->h_sync_width  = params[5];
+       mode->v_sync_width  = params[6];
+       mode->h_back_porch  = params[7];
+       mode->v_back_porch  = params[8];
+       mode->h_front_porch = params[9];
+       mode->v_front_porch = params[10];
+
+       return 0;
+}
+
+static int __init tegra_dc_mode_override(char *str)
+{
+       char *p = str, *options;
+
+       if (!p || !*p)
+               return -EINVAL;
+
+       p = strstr(str, "hdmi:");
+       if (p) {
+               p += 5;
+               options = strsep(&p, ";");
+               if (parse_disp_params(options, &override_disp_mode[TEGRA_DC_OUT_HDMI]))
+                       return -EINVAL;
+       }
+
+       p = strstr(str, "rgb:");
+       if (p) {
+               p += 4;
+               options = strsep(&p, ";");
+               if (parse_disp_params(options, &override_disp_mode[TEGRA_DC_OUT_RGB]))
+                       return -EINVAL;
+       }
+
+       p = strstr(str, "dsi:");
+       if (p) {
+               p += 4;
+               options = strsep(&p, ";");
+               if (parse_disp_params(options, &override_disp_mode[TEGRA_DC_OUT_DSI]))
+                       return -EINVAL;
+       }
+
+       return 0;
+}
+
+__setup("disp_params=", tegra_dc_mode_override);
+#endif
+
 static int __init tegra_dc_module_init(void)
 {
        int ret = tegra_dc_ext_module_init();
        if (ret)
                return ret;
-       return nvhost_driver_register(&tegra_dc_driver);
+       return platform_driver_register(&tegra_dc_driver);
 }
 
 static void __exit tegra_dc_module_exit(void)
 {
-       nvhost_driver_unregister(&tegra_dc_driver);
+       platform_driver_unregister(&tegra_dc_driver);
        tegra_dc_ext_module_exit();
 }