video: tegra: dc: Optimize OS idle display client wakeup
[linux-3.10.git] / drivers / video / tegra / dc / dc.c
index f9cec2e..d6b6f5d 100644 (file)
@@ -4,7 +4,7 @@
  * Copyright (C) 2010 Google, Inc.
  * Author: Erik Gilling <konkers@android.com>
  *
- * Copyright (c) 2010-2012, NVIDIA CORPORATION, All rights reserved.
+ * 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
@@ -52,7 +52,7 @@
 #include <mach/mc.h>
 #include <linux/nvhost.h>
 #include <mach/latency_allowance.h>
-#include <mach/iomap.h>
+#include <mach/pm_domains.h>
 
 #include "dc_reg.h"
 #include "dc_config.h"
@@ -60,6 +60,9 @@
 #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
@@ -268,6 +271,176 @@ 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
 
 void tegra_dc_clk_enable(struct tegra_dc *dc)
@@ -286,8 +459,25 @@ void tegra_dc_clk_disable(struct tegra_dc *dc)
        }
 }
 
+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);
 }
@@ -296,6 +486,7 @@ 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 {                       \
@@ -311,8 +502,7 @@ static void _dump_regs(struct tegra_dc *dc, void *data,
        char buff[256];
 
        mutex_lock(&dc->lock);
-       tegra_dc_io_start(dc);
-       tegra_dc_hold_dc_out(dc);
+       tegra_dc_get(dc);
 
        DUMP_REG(DC_CMD_DISPLAY_COMMAND_OPTION0);
        DUMP_REG(DC_CMD_DISPLAY_COMMAND);
@@ -465,7 +655,7 @@ 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);
-#if !defined(CONFIG_ARCH_TEGRA_2x_SOC) && !defined(CONFIG_ARCH_TEGRA_3x_SOC)
+#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);
@@ -477,8 +667,7 @@ static void _dump_regs(struct tegra_dc *dc, void *data,
        DUMP_REG(DC_COM_CMU_CSC_KBB);
 #endif
 
-       tegra_dc_release_dc_out(dc);
-       tegra_dc_io_end(dc);
+       tegra_dc_put(dc);
        mutex_unlock(&dc->lock);
 }
 
@@ -590,6 +779,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;
@@ -600,6 +798,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,
@@ -607,6 +836,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)
@@ -637,6 +879,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");
@@ -695,13 +942,11 @@ int tegra_dc_get_stride(struct tegra_dc *dc, unsigned win)
                return 0;
        BUG_ON(win > DC_N_WINDOWS);
        mutex_lock(&dc->lock);
-       tegra_dc_io_start(dc);
-       tegra_dc_hold_dc_out(dc);
+       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_release_dc_out(dc);
-       tegra_dc_io_end(dc);
+       tegra_dc_put(dc);
        mutex_unlock(&dc->lock);
        return GET_LINE_STRIDE(stride);
 }
@@ -736,7 +981,16 @@ bool tegra_dc_hpd(struct tegra_dc *dc)
        int sense;
        int level;
 
-       level = gpio_get_value(dc->out->hotplug_gpio);
+       if (WARN_ON(!dc || !dc->out))
+               return false;
+
+       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);
 
        sense = dc->out->flags & TEGRA_DC_OUT_HOTPLUG_MASK;
 
@@ -851,6 +1105,7 @@ int _tegra_dc_update_cmu(struct tegra_dc *dc, struct tegra_dc_cmu *cmu)
                return 0;
        }
 
+#ifdef CONFIG_TEGRA_DC_CMU
        if (cmu != &dc->cmu) {
                tegra_dc_cache_cmu(&dc->cmu, cmu);
 
@@ -869,6 +1124,7 @@ int _tegra_dc_update_cmu(struct tegra_dc *dc, struct tegra_dc_cmu *cmu)
 
                tegra_dc_set_cmu(dc, &dc->cmu);
        }
+#endif
 
        return 0;
 }
@@ -882,14 +1138,13 @@ int tegra_dc_update_cmu(struct tegra_dc *dc, struct tegra_dc_cmu *cmu)
                mutex_unlock(&dc->lock);
                return 0;
        }
-       tegra_dc_io_start(dc);
-       tegra_dc_hold_dc_out(dc);
+
+       tegra_dc_get(dc);
 
        ret = _tegra_dc_update_cmu(dc, cmu);
        tegra_dc_set_color_control(dc);
 
-       tegra_dc_release_dc_out(dc);
-       tegra_dc_io_end(dc);
+       tegra_dc_put(dc);
        mutex_unlock(&dc->lock);
 
        return ret;
@@ -928,13 +1183,11 @@ u32 tegra_dc_incr_syncpt_max(struct tegra_dc *dc, int i)
        u32 max;
 
        mutex_lock(&dc->lock);
-       tegra_dc_io_start(dc);
-       tegra_dc_hold_dc_out(dc);
+       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_release_dc_out(dc);
-       tegra_dc_io_end(dc);
+       tegra_dc_put(dc);
        mutex_unlock(&dc->lock);
 
        return max;
@@ -944,14 +1197,12 @@ void tegra_dc_incr_syncpt_min(struct tegra_dc *dc, int i, u32 val)
 {
        mutex_lock(&dc->lock);
        if (dc->enabled) {
-               tegra_dc_io_start(dc);
-               tegra_dc_hold_dc_out(dc);
+               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);
                }
-               tegra_dc_release_dc_out(dc);
-               tegra_dc_io_end(dc);
+               tegra_dc_put(dc);
        }
        mutex_unlock(&dc->lock);
 }
@@ -969,8 +1220,7 @@ tegra_dc_config_pwm(struct tegra_dc *dc, struct tegra_dc_pwm_params *cfg)
                return;
        }
 
-       tegra_dc_io_start(dc);
-       tegra_dc_hold_dc_out(dc);
+       tegra_dc_get(dc);
 
        ctrl = ((cfg->period << PM_PERIOD_SHIFT) |
                (cfg->clk_div << PM_CLK_DIVIDER_SHIFT) |
@@ -1004,8 +1254,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_release_dc_out(dc);
-       tegra_dc_io_end(dc);
+       tegra_dc_put(dc);
        mutex_unlock(&dc->lock);
 }
 EXPORT_SYMBOL(tegra_dc_config_pwm);
@@ -1162,30 +1411,26 @@ void tegra_dc_enable_crc(struct tegra_dc *dc)
        u32 val;
 
        mutex_lock(&dc->lock);
-       tegra_dc_io_start(dc);
-       tegra_dc_hold_dc_out(dc);
+       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_release_dc_out(dc);
-       tegra_dc_io_end(dc);
+       tegra_dc_put(dc);
        mutex_unlock(&dc->lock);
 }
 
 void tegra_dc_disable_crc(struct tegra_dc *dc)
 {
        mutex_lock(&dc->lock);
-       tegra_dc_io_start(dc);
-       tegra_dc_hold_dc_out(dc);
+       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_release_dc_out(dc);
-       tegra_dc_io_end(dc);
+       tegra_dc_put(dc);
        mutex_unlock(&dc->lock);
 }
 
@@ -1198,18 +1443,15 @@ u32 tegra_dc_read_checksum_latched(struct tegra_dc *dc)
                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_io_start(dc);
-       tegra_dc_hold_dc_out(dc);
+       tegra_dc_get(dc);
        crc = tegra_dc_readl(dc, DC_COM_CRC_CHECKSUM_LATCHED);
-       tegra_dc_release_dc_out(dc);
-       tegra_dc_io_end(dc);
+       tegra_dc_put(dc);
        mutex_unlock(&dc->lock);
 crc_error:
        return crc;
@@ -1217,24 +1459,25 @@ crc_error:
 
 static bool tegra_dc_windows_are_dirty(struct tegra_dc *dc)
 {
-#ifndef CONFIG_TEGRA_SIMULATION_PLATFORM
        u32 val;
 
+       if (tegra_platform_is_linsim())
+               return 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
+       if (val & WIN_ALL_ACT_REQ)
+               return true;
+
        return false;
 }
 
 static inline void enable_dc_irq(const struct tegra_dc *dc)
 {
-#ifndef CONFIG_TEGRA_FPGA_PLATFORM
-       enable_irq(dc->irq);
-#else
-       /* Always disable DC interrupts on FPGA. */
-       disable_irq(dc->irq);
-#endif
+       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)
@@ -1257,12 +1500,14 @@ int tegra_dc_wait_for_vsync(struct tegra_dc *dc)
         * c) Initialize completion for next iteration.
         */
 
-       tegra_dc_hold_dc_out(dc);
+       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_release_dc_out(dc);
+       tegra_dc_mask_interrupt(dc, MSF_INT);
+       tegra_dc_put(dc);
 
        return ret;
 }
@@ -1296,8 +1541,7 @@ static void tegra_dc_vblank(struct work_struct *work)
                return;
        }
 
-       tegra_dc_io_start(dc);
-       tegra_dc_hold_dc_out(dc);
+       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))
@@ -1324,8 +1568,7 @@ static void tegra_dc_vblank(struct work_struct *work)
        if (!dc->vblank_ref_count)
                tegra_dc_mask_interrupt(dc, V_BLANK_INT);
 
-       tegra_dc_release_dc_out(dc);
-       tegra_dc_io_end(dc);
+       tegra_dc_put(dc);
        mutex_unlock(&dc->lock);
 
        /* Do the actual brightness update outside of the mutex dc->lock */
@@ -1375,14 +1618,42 @@ static void tegra_dc_underflow_handler(struct tegra_dc *dc)
        if (dc->underflow_mask & WIN_C_UF_INT)
                dc->stats.underflows_c += tegra_dc_underflow_count(dc,
                        DC_WINBUF_CD_UFLOW_STATUS);
+#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;
@@ -1390,7 +1661,7 @@ static void tegra_dc_underflow_handler(struct tegra_dc *dc)
                        }
 #endif
 #ifdef CONFIG_ARCH_TEGRA_3x_SOC
-                       if (dc->windows[i].underflows > 4) {
+                       if (i < 3 && dc->windows[i].underflows > 4) {
                                trace_display_reset(dc);
                                tegra_dc_writel(dc, UF_LINE_FLUSH,
                                                DC_DISP_DISP_MISC_CONTROL);
@@ -1415,7 +1686,7 @@ 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;
-       tegra_dc_unmask_interrupt(dc, ALL_UF_INT);
+       tegra_dc_unmask_interrupt(dc, ALL_UF_INT());
        trace_underflow(dc);
 }
 
@@ -1432,8 +1703,7 @@ static void tegra_dc_vpulse2(struct work_struct *work)
                return;
        }
 
-       tegra_dc_io_start(dc);
-       tegra_dc_hold_dc_out(dc);
+       tegra_dc_get(dc);
 
        /* Clear the V_PULSE2_FLIP if no update */
        if (!tegra_dc_windows_are_dirty(dc))
@@ -1454,8 +1724,7 @@ static void tegra_dc_vpulse2(struct work_struct *work)
        if (!dc->vpulse2_ref_count)
                tegra_dc_mask_interrupt(dc, V_PULSE2_INT);
 
-       tegra_dc_release_dc_out(dc);
-       tegra_dc_io_end(dc);
+       tegra_dc_put(dc);
        mutex_unlock(&dc->lock);
 
        /* Do the actual brightness update outside of the mutex dc->lock */
@@ -1464,7 +1733,6 @@ static void tegra_dc_vpulse2(struct work_struct *work)
 }
 #endif
 
-#ifndef CONFIG_TEGRA_FPGA_PLATFORM
 static void tegra_dc_one_shot_irq(struct tegra_dc *dc, unsigned long status)
 {
        /* pending user vblank, so wakeup */
@@ -1533,43 +1801,40 @@ bool tegra_dc_does_vsync_separate(struct tegra_dc *dc, s64 new_ts, s64 old_ts)
                        != div_s64((old_ts - dc->frame_end_timestamp),
                                dc->frametime_ns)));
 }
-#endif
 
 static irqreturn_t tegra_dc_irq(int irq, void *ptr)
 {
-#ifndef CONFIG_TEGRA_FPGA_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) {
+       if (!dc->enabled || !tegra_dc_is_powered(dc)) {
                mutex_unlock(&dc->lock);
                return IRQ_HANDLED;
        }
 
-       clk_prepare_enable(dc->clk);
-       tegra_dc_io_start(dc);
-       tegra_dc_hold_dc_out(dc);
+       tegra_dc_get(dc);
 
        if (!nvhost_module_powered_ext(dc->ndev)) {
                WARN(1, "IRQ when DC not powered!\n");
                status = tegra_dc_readl(dc, DC_CMD_INT_STATUS);
                tegra_dc_writel(dc, status, DC_CMD_INT_STATUS);
-               tegra_dc_release_dc_out(dc);
-               tegra_dc_io_end(dc);
-               clk_disable_unprepare(dc->clk);
+               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
@@ -1577,7 +1842,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) {
@@ -1596,17 +1861,12 @@ static irqreturn_t tegra_dc_irq(int irq, void *ptr)
                if (tegra_dc_update_mode(dc))
                        need_disable = 1; /* force display off on error */
 
-       tegra_dc_release_dc_out(dc);
-       tegra_dc_io_end(dc);
-       clk_disable_unprepare(dc->clk);
+       tegra_dc_put(dc);
        mutex_unlock(&dc->lock);
 
        if (need_disable)
                tegra_dc_disable(dc);
        return IRQ_HANDLED;
-#else /* CONFIG_TEGRA_FPGA_PLATFORM */
-       return IRQ_NONE;
-#endif /* !CONFIG_TEGRA_FPGA_PLATFORM */
 }
 
 void tegra_dc_set_color_control(struct tegra_dc *dc)
@@ -1755,22 +2015,26 @@ 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 */
-       int_enable = (FRAME_END_INT | V_BLANK_INT | ALL_UF_INT);
+       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_writel(dc, ALL_UF_INT(), DC_CMD_INT_MASK);
        tegra_dc_init_vpulse2_int(dc);
 
        tegra_dc_writel(dc, 0x00000000, DC_DISP_BORDER_COLOR);
 
 #ifdef CONFIG_TEGRA_DC_CMU
-       if (dc->pdata->cmu)
+       if (dc->pdata->cmu) {
                _tegra_dc_update_cmu(dc, dc->pdata->cmu);
-       else
-               _tegra_dc_update_cmu(dc, &default_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++) {
@@ -1782,6 +2046,12 @@ static int tegra_dc_init(struct tegra_dc *dc)
                tegra_dc_set_scaling_filter(dc);
        }
 
+#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);
 
@@ -1857,6 +2127,8 @@ static bool _tegra_dc_controller_enable(struct tegra_dc *dc)
        if (dc->out->postpoweron)
                dc->out->postpoweron();
 
+       tegra_log_resume_time();
+
        tegra_dc_io_end(dc);
        return true;
 }
@@ -1883,10 +2155,10 @@ static bool _tegra_dc_controller_reset_enable(struct tegra_dc *dc)
        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]);
@@ -1993,7 +2265,7 @@ static void _tegra_dc_controller_disable(struct tegra_dc *dc)
 {
        unsigned i;
 
-       tegra_dc_hold_dc_out(dc);
+       tegra_dc_get(dc);
 
        if (dc->out && dc->out->prepoweroff)
                dc->out->prepoweroff();
@@ -2007,13 +2279,6 @@ static void _tegra_dc_controller_disable(struct tegra_dc *dc)
 
        tegra_dc_clear_bandwidth(dc);
 
-       /* ugly hack */
-       if (dc->out_ops->release &&
-               (dc->out->flags & TEGRA_DC_OUT_ONE_SHOT_LP_MODE))
-               tegra_dc_release_dc_out(dc);
-       else
-               tegra_dc_clk_disable(dc);
-
        if (dc->out && dc->out->disable)
                dc->out->disable();
 
@@ -2036,6 +2301,9 @@ static void _tegra_dc_controller_disable(struct tegra_dc *dc)
                }
        }
        trace_display_disable(dc);
+
+       tegra_dc_clk_disable(dc);
+       tegra_dc_put(dc);
 }
 
 void tegra_dc_stats_enable(struct tegra_dc *dc, bool enable)
@@ -2102,6 +2370,12 @@ static void _tegra_dc_disable(struct tegra_dc *dc)
        if (dc->out->flags & TEGRA_DC_OUT_ONE_SHOT_MODE)
                mutex_unlock(&dc->one_shot_lock);
        pm_runtime_put(&dc->ndev->dev);
+
+       /*
+        * We will need to reinitialize the display the next time panel
+        * is enabled.
+        */
+       dc->out->flags &= ~TEGRA_DC_OUT_INITIALIZED_MODE;
 }
 
 void tegra_dc_disable(struct tegra_dc *dc)
@@ -2192,67 +2466,15 @@ static void tegra_dc_underflow_worker(struct work_struct *work)
                to_delayed_work(work), struct tegra_dc, underflow_work);
 
        mutex_lock(&dc->lock);
-       tegra_dc_io_start(dc);
-       tegra_dc_hold_dc_out(dc);
+       tegra_dc_get(dc);
 
        if (dc->enabled) {
                tegra_dc_underflow_handler(dc);
        }
-       tegra_dc_release_dc_out(dc);
-       tegra_dc_io_end(dc);
+       tegra_dc_put(dc);
        mutex_unlock(&dc->lock);
 }
 
-#ifdef CONFIG_ARCH_TEGRA_11x_SOC
-/* A mutex used to protect the critical section used by both DC heads. */
-static struct mutex tegra_dc_powergate_status_lock;
-
-/* defer turning off DISA until DISB is turned off */
-void tegra_dc_powergate_locked(struct tegra_dc *dc)
-{
-       struct tegra_dc *dc_partner;
-
-       mutex_lock(&tegra_dc_powergate_status_lock);
-       /* Get the handler of the other display controller. */
-       dc_partner = tegra_dc_get_dc(dc->ndev->id ^ 1);
-       if (!dc_partner)
-               _tegra_dc_powergate_locked(dc);
-       else if (dc->powergate_id == TEGRA_POWERGATE_DISA) {
-               /* If DISB is powergated, then powergate DISA. */
-               if (!dc_partner->powered)
-                       _tegra_dc_powergate_locked(dc);
-       } else if (dc->powergate_id == TEGRA_POWERGATE_DISB) {
-               /* If DISA is enabled, only powergate DISB;
-                * otherwise, powergate DISA and DISB.
-                * */
-               if (dc_partner->enabled) {
-                       _tegra_dc_powergate_locked(dc);
-               } else {
-                       _tegra_dc_powergate_locked(dc);
-                       _tegra_dc_powergate_locked(dc_partner);
-               }
-       }
-       mutex_unlock(&tegra_dc_powergate_status_lock);
-}
-
-
-/* to turn on DISB we must first power on DISA */
-void tegra_dc_unpowergate_locked(struct tegra_dc *dc)
-{
-       mutex_lock(&tegra_dc_powergate_status_lock);
-       if (dc->powergate_id == TEGRA_POWERGATE_DISB) {
-               struct tegra_dc *dc_partner;
-
-               /* Get the handler of the other display controller. */
-               dc_partner = tegra_dc_get_dc(dc->ndev->id ^ 1);
-               if (dc_partner)
-                       _tegra_dc_unpowergate_locked(dc_partner);
-       }
-       _tegra_dc_unpowergate_locked(dc);
-       mutex_unlock(&tegra_dc_powergate_status_lock);
-}
-#endif
-
 #ifdef CONFIG_SWITCH
 static ssize_t switch_modeset_print_mode(struct switch_dev *sdev, char *buf)
 {
@@ -2289,7 +2511,10 @@ 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;
@@ -2342,13 +2567,22 @@ static int tegra_dc_probe(struct platform_device *ndev)
                dc->win_syncpt[0] = NVSYNCPT_DISP0_A;
                dc->win_syncpt[1] = NVSYNCPT_DISP0_B;
                dc->win_syncpt[2] = NVSYNCPT_DISP0_C;
+#ifdef CONFIG_ARCH_TEGRA_14x_SOC
+               dc->win_syncpt[3] = NVSYNCPT_DISP0_D;
+               dc->win_syncpt[4] = NVSYNCPT_DISP0_H;
+#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;
+#ifdef CONFIG_ARCH_TEGRA_14x_SOC
+               dc->win_syncpt[4] = NVSYNCPT_DISP1_H;
+#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",
@@ -2365,15 +2599,7 @@ static int tegra_dc_probe(struct platform_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.mul = dc->shift_clk_div.div = 1;
        /* Initialize one shot work delay, it will be assigned by dsi
         * according to refresh rate later. */
@@ -2385,17 +2611,10 @@ static int tegra_dc_probe(struct platform_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);
-#ifdef CONFIG_ARCH_TEGRA_11x_SOC
-       mutex_init(&tegra_dc_powergate_status_lock);
-#endif
        init_completion(&dc->frame_end_complete);
        init_waitqueue_head(&dc->wq);
        init_waitqueue_head(&dc->timestamp_wq);
@@ -2425,7 +2644,7 @@ static int tegra_dc_probe(struct platform_device *ndev)
        ret = tegra_dc_set(dc, ndev->id);
        if (ret < 0) {
                dev_err(&ndev->dev, "can't add dc\n");
-               goto err_put_emc_clk;
+               goto err_put_clk;
        }
 
        platform_set_drvdata(ndev, dc);
@@ -2447,6 +2666,35 @@ static int tegra_dc_probe(struct platform_device *ndev)
                dev_err(&ndev->dev, "No default output specified.  Leaving output disabled.\n");
        dc->mode_dirty = false; /* ignore changes tegra_dc_set_out has done */
 
+#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)) {
                dev_warn(&ndev->dev, "Failed to enable Tegra DC extensions.\n");
@@ -2462,6 +2710,7 @@ static int tegra_dc_probe(struct platform_device *ndev)
        }
        disable_dc_irq(dc);
 
+       tegra_pd_add_device(&tegra_mc_chain_a, &ndev->dev);
        pm_runtime_use_autosuspend(&ndev->dev);
        pm_runtime_set_autosuspend_delay(&ndev->dev, 100);
        pm_runtime_enable(&ndev->dev);
@@ -2538,8 +2787,11 @@ err_disable_dc:
 #ifdef CONFIG_SWITCH
        switch_dev_unregister(&dc->modeset_switch);
 #endif
-err_put_emc_clk:
+#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:
@@ -2583,7 +2835,14 @@ static int tegra_dc_remove(struct platform_device *ndev)
        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)
@@ -2642,6 +2901,9 @@ static int tegra_dc_resume(struct platform_device *ndev)
        mutex_lock(&dc->lock);
        dc->suspended = false;
 
+       /* 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);
@@ -2667,7 +2929,9 @@ static void tegra_dc_shutdown(struct platform_device *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);
 }