]> nv-tegra.nvidia Code Review - linux-2.6.git/blobdiff - drivers/video/tegra/dc/dc.c
video: tegra: detect fbmem alignment on probe
[linux-2.6.git] / drivers / video / tegra / dc / dc.c
index 4f8da4e4735ea7f68fddab079abc81d83c4ff57b..935f18bc8cfd56953907efdcee5863a95c32d4a6 100644 (file)
@@ -4,7 +4,7 @@
  * Copyright (C) 2010 Google, Inc.
  * Author: Erik Gilling <konkers@android.com>
  *
- * Copyright (C) 2010-2011 NVIDIA Corporation
+ * Copyright (C) 2010-2012 NVIDIA Corporation
  *
  * This software is licensed under the terms of the GNU General Public
  * License version 2, as published by the Free Software Foundation, and
 #include <linux/debugfs.h>
 #include <linux/seq_file.h>
 #include <linux/backlight.h>
+#include <linux/gpio.h>
 #include <video/tegrafb.h>
 #include <drm/drm_fixed.h>
+#ifdef CONFIG_SWITCH
+#include <linux/switch.h>
+#endif
+
 
 #include <mach/clk.h>
 #include <mach/dc.h>
 #include <mach/latency_allowance.h>
 
 #include "dc_reg.h"
+#include "dc_config.h"
 #include "dc_priv.h"
-#include "overlay.h"
 #include "nvsd.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
 #endif
 
 static int no_vsync;
+static struct fb_videomode tegra_dc_hdmi_fallback_mode = {
+       .refresh = 60,
+       .xres = 640,
+       .yres = 480,
+       .pixclock = KHZ2PICOS(25200),
+       .hsync_len = 96,        /* h_sync_width */
+       .vsync_len = 2,         /* v_sync_width */
+       .left_margin = 48,      /* h_back_porch */
+       .upper_margin = 33,     /* v_back_porch */
+       .right_margin = 16,     /* h_front_porch */
+       .lower_margin = 10,     /* v_front_porch */
+       .vmode = 0,
+       .sync = 0,
+};
+
+static void _tegra_dc_controller_disable(struct tegra_dc *dc);
 
 module_param_named(no_vsync, no_vsync, int, S_IRUGO | S_IWUSR);
 
@@ -70,25 +94,14 @@ struct tegra_dc *tegra_dcs[TEGRA_MAX_DC];
 DEFINE_MUTEX(tegra_dc_lock);
 DEFINE_MUTEX(shared_lock);
 
-static const struct {
-       bool h;
-       bool v;
-} can_filter[] = {
-       /* Window A has no filtering */
-       { false, false },
-       /* Window B has both H and V filtering */
-       { true,  true  },
-       /* Window C has only H filtering */
-       { false, true  },
-};
-static inline bool win_use_v_filter(const struct tegra_dc_win *win)
+static inline bool win_use_v_filter(struct tegra_dc *dc, const struct tegra_dc_win *win)
 {
-       return can_filter[win->idx].v &&
+       return tegra_dc_feature_has_filter(dc, win->idx, HAS_V_FILTER) &&
                win->h.full != dfixed_const(win->out_h);
 }
-static inline bool win_use_h_filter(const struct tegra_dc_win *win)
+static inline bool win_use_h_filter(struct tegra_dc *dc, const struct tegra_dc_win *win)
 {
-       return can_filter[win->idx].h &&
+       return tegra_dc_feature_has_filter(dc, win->idx, HAS_H_FILTER) &&
                win->w.full != dfixed_const(win->out_w);
 }
 
@@ -124,18 +137,36 @@ static inline int tegra_dc_fmt_bpp(int fmt)
        case TEGRA_WIN_FMT_YUV420P:
        case TEGRA_WIN_FMT_YCbCr422P:
        case TEGRA_WIN_FMT_YUV422P:
+       case TEGRA_WIN_FMT_YCbCr422R:
+       case TEGRA_WIN_FMT_YUV422R:
+       case TEGRA_WIN_FMT_YCbCr422RA:
+       case TEGRA_WIN_FMT_YUV422RA:
                return 8;
 
+       /* YUYV packed into 32-bits */
+       case TEGRA_WIN_FMT_YCbCr422:
+       case TEGRA_WIN_FMT_YUV422:
+               return 16;
+       }
+       return 0;
+}
+
+static inline bool tegra_dc_is_yuv(int fmt)
+{
+       switch (fmt) {
+       case TEGRA_WIN_FMT_YUV420P:
+       case TEGRA_WIN_FMT_YCbCr420P:
+       case TEGRA_WIN_FMT_YCbCr422P:
+       case TEGRA_WIN_FMT_YUV422P:
        case TEGRA_WIN_FMT_YCbCr422:
        case TEGRA_WIN_FMT_YUV422:
        case TEGRA_WIN_FMT_YCbCr422R:
        case TEGRA_WIN_FMT_YUV422R:
        case TEGRA_WIN_FMT_YCbCr422RA:
        case TEGRA_WIN_FMT_YUV422RA:
-               /* FIXME: need to know the bpp of these formats */
-               return 0;
+               return true;
        }
-       return 0;
+       return false;
 }
 
 static inline bool tegra_dc_is_yuv_planar(int fmt)
@@ -145,6 +176,10 @@ static inline bool tegra_dc_is_yuv_planar(int fmt)
        case TEGRA_WIN_FMT_YCbCr420P:
        case TEGRA_WIN_FMT_YCbCr422P:
        case TEGRA_WIN_FMT_YUV422P:
+       case TEGRA_WIN_FMT_YCbCr422R:
+       case TEGRA_WIN_FMT_YUV422R:
+       case TEGRA_WIN_FMT_YCbCr422RA:
+       case TEGRA_WIN_FMT_YUV422RA:
                return true;
        }
        return false;
@@ -156,6 +191,34 @@ static inline bool tegra_dc_is_yuv_planar(int fmt)
        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))
 {
@@ -506,7 +569,7 @@ out:
        return ret;
 }
 
-unsigned int tegra_dc_has_multiple_dc(void)
+static unsigned int tegra_dc_has_multiple_dc(void)
 {
        unsigned int idx;
        unsigned int cnt = 0;
@@ -520,6 +583,23 @@ unsigned int tegra_dc_has_multiple_dc(void)
        return (cnt > 1);
 }
 
+/* get the stride size of a window.
+ * return: stride size in bytes for window win. or 0 if unavailble. */
+int tegra_dc_get_stride(struct tegra_dc *dc, unsigned win)
+{
+       u32 tmp;
+       u32 stride;
+
+       if (!dc->enabled)
+               return 0;
+       BUG_ON(win > DC_N_WINDOWS);
+       tegra_dc_writel(dc, WINDOW_A_SELECT << win,
+               DC_CMD_DISPLAY_WINDOW_HEADER);
+       tmp = tegra_dc_readl(dc, DC_WIN_LINE_STRIDE);
+       return GET_LINE_STRIDE(tmp);
+}
+EXPORT_SYMBOL(tegra_dc_get_stride);
+
 struct tegra_dc *tegra_dc_get_dc(unsigned idx)
 {
        if (idx < TEGRA_MAX_DC)
@@ -556,6 +636,20 @@ bool tegra_dc_get_connected(struct tegra_dc *dc)
 }
 EXPORT_SYMBOL(tegra_dc_get_connected);
 
+bool tegra_dc_hpd(struct tegra_dc *dc)
+{
+       int sense;
+       int level;
+
+       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)
@@ -752,6 +846,8 @@ static int tegra_dc_update_winlut(struct tegra_dc *dc, int win_idx, int fbovr)
 
        mutex_unlock(&dc->lock);
 
+       tegra_dc_update_windows(&win, 1);
+
        return 0;
 }
 
@@ -813,12 +909,12 @@ static void tegra_dc_set_latency_allowance(struct tegra_dc *dc,
 
        /* tegra_dc_get_bandwidth() treats V filter windows as double
         * bandwidth, but LA has a seperate client for V filter */
-       if (w->idx == 1 && win_use_v_filter(w))
+       if (w->idx == 1 && win_use_v_filter(dc, w))
                bw /= 2;
 
-       /* our bandwidth is in bytes/sec, but LA takes MBps.
-        * round up bandwidth to 1MBps */
-       bw = bw / 1000000 + 1;
+       /* our bandwidth is in kbytes/sec, but LA takes MBps.
+        * round up bandwidth to next 1MBps */
+       bw = bw / 1000 + 1;
 
 #ifdef CONFIG_TEGRA_SILICON_PLATFORM
        tegra_set_latency_allowance(la_id_tab[dc->ndev->id][w->idx], bw);
@@ -902,10 +998,12 @@ static unsigned long tegra_dc_find_max_bandwidth(struct tegra_dc_win *wins[],
  * pixel_clock * win_bpp * (use_v_filter ? 2 : 1)) * H_scale_factor *
  * (windows_tiling ? 2 : 1)
  *
- *
  * note:
  * (*) We use 2 tap V filter, so need double BW if use V filter
  * (*) Tiling mode on T30 and DDR3 requires double BW
+ *
+ * return:
+ * bandwidth in kBps
  */
 static unsigned long tegra_dc_calc_win_bandwidth(struct tegra_dc *dc,
        struct tegra_dc_win *w)
@@ -929,27 +1027,15 @@ static unsigned long tegra_dc_calc_win_bandwidth(struct tegra_dc *dc,
         * is of the luma plane's size only. */
        bpp = tegra_dc_is_yuv_planar(w->fmt) ?
                2 * tegra_dc_fmt_bpp(w->fmt) : tegra_dc_fmt_bpp(w->fmt);
-       /* perform calculations with most significant bits of pixel clock
-        * to prevent overflow of long. */
-       ret = (unsigned long)(dc->pixel_clk >> 16) *
-               bpp / 8 *
-               (win_use_v_filter(w) ? 2 : 1) * dfixed_trunc(w->w) / w->out_w *
+       ret = dc->mode.pclk / 1000UL * bpp / 8 * (win_use_v_filter(dc, w) ? 2 : 1)
+               * dfixed_trunc(w->w) / w->out_w *
                (WIN_IS_TILED(w) ? tiled_windows_bw_multiplier : 1);
 
-/*
- * Assuming 48% efficiency: i.e. if we calculate we need 70MBps, we
- * will request 147MBps from EMC.
- */
-       ret = ret * 2 + ret / 10;
-
-       /* if overflowed */
-       if (ret > (1UL << 31))
-               return ULONG_MAX;
-
-       return ret << 16; /* restore the scaling we did above */
+       return ret;
 }
 
-unsigned long tegra_dc_get_bandwidth(struct tegra_dc_win *windows[], int n)
+static unsigned long tegra_dc_get_bandwidth(
+       struct tegra_dc_win *windows[], int n)
 {
        int i;
 
@@ -959,26 +1045,48 @@ unsigned long tegra_dc_get_bandwidth(struct tegra_dc_win *windows[], int n)
         * bandwidths */
        for (i = 0; i < n; i++) {
                struct tegra_dc_win *w = windows[i];
+
                if (w)
-                       w->new_bandwidth = tegra_dc_calc_win_bandwidth(w->dc, w);
+                       w->new_bandwidth =
+                               tegra_dc_calc_win_bandwidth(w->dc, w);
        }
 
        return tegra_dc_find_max_bandwidth(windows, n);
 }
 
+/* to save power, call when display memory clients would be idle */
+static void tegra_dc_clear_bandwidth(struct tegra_dc *dc)
+{
+       trace_printk("%s:%s rate=%d\n", dc->ndev->name, __func__,
+               dc->emc_clk_rate);
+       if (tegra_is_clk_enabled(dc->emc_clk))
+               clk_disable(dc->emc_clk);
+       dc->emc_clk_rate = 0;
+}
+
 static void tegra_dc_program_bandwidth(struct tegra_dc *dc)
 {
        unsigned i;
 
        if (dc->emc_clk_rate != dc->new_emc_clk_rate) {
+               /* going from 0 to non-zero */
+               if (!dc->emc_clk_rate && !tegra_is_clk_enabled(dc->emc_clk))
+                       clk_enable(dc->emc_clk);
+
                dc->emc_clk_rate = dc->new_emc_clk_rate;
                clk_set_rate(dc->emc_clk, dc->emc_clk_rate);
+
+               if (!dc->new_emc_clk_rate) /* going from non-zero to 0 */
+                       clk_disable(dc->emc_clk);
        }
 
        for (i = 0; i < DC_N_WINDOWS; i++) {
                struct tegra_dc_win *w = &dc->windows[i];
-               if (w->bandwidth != w->new_bandwidth)
+
+               if (w->bandwidth != w->new_bandwidth && w->new_bandwidth != 0)
                        tegra_dc_set_latency_allowance(dc, w);
+               trace_printk("%s:win%u bandwidth=%d\n", dc->ndev->name, w->idx,
+                       w->bandwidth);
        }
 }
 
@@ -994,11 +1102,15 @@ static int tegra_dc_set_dynamic_emc(struct tegra_dc_win *windows[], int n)
 
        /* calculate the new rate based on this POST */
        new_rate = tegra_dc_get_bandwidth(windows, n);
-       new_rate = EMC_BW_TO_FREQ(new_rate);
+       if (WARN_ONCE(new_rate > (ULONG_MAX / 1000), "bandwidth maxed out\n"))
+               new_rate = ULONG_MAX;
+       else
+               new_rate = EMC_BW_TO_FREQ(new_rate * 1000);
 
        if (tegra_dc_has_multiple_dc())
                new_rate = ULONG_MAX;
 
+       trace_printk("%s:new_emc_clk_rate=%ld\n", dc->ndev->name, new_rate);
        dc->new_emc_clk_rate = new_rate;
 
        return 0;
@@ -1062,10 +1174,19 @@ int tegra_dc_update_windows(struct tegra_dc_win *windows[], int n)
 
        dc = windows[0]->dc;
 
+       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;
        }
 
@@ -1081,12 +1202,13 @@ int tegra_dc_update_windows(struct tegra_dc_win *windows[], int n)
                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(win);
-               const bool filter_v = win_use_v_filter(win);
+               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;
@@ -1110,8 +1232,8 @@ int tegra_dc_update_windows(struct tegra_dc_win *windows[], int n)
                        continue;
                }
 
-               tegra_dc_writel(dc, win->fmt, DC_WIN_COLOR_DEPTH);
-               tegra_dc_writel(dc, 0, DC_WIN_BYTE_SWAP);
+               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),
@@ -1119,19 +1241,22 @@ int tegra_dc_update_windows(struct tegra_dc_win *windows[], int n)
                tegra_dc_writel(dc,
                                V_SIZE(win->out_h) | H_SIZE(win->out_w),
                                DC_WIN_SIZE);
-               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 (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);
+               }
 
                tegra_dc_writel(dc, 0, DC_WIN_BUF_STRIDE);
                tegra_dc_writel(dc, 0, DC_WIN_UV_BUF_STRIDE);
@@ -1169,19 +1294,21 @@ int tegra_dc_update_windows(struct tegra_dc_win *windows[], int n)
                tegra_dc_writel(dc, dfixed_trunc(v_offset),
                                DC_WINBUF_ADDR_V_OFFSET);
 
-               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);
+               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 (yuvp)
+               if (yuv)
                        val |= CSC_ENABLE;
                else if (tegra_dc_fmt_bpp(win->fmt) < 24)
                        val |= COLOR_EXPAND;
@@ -1201,6 +1328,14 @@ int tegra_dc_update_windows(struct tegra_dc_win *windows[], int n)
 
                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 "
@@ -1211,6 +1346,9 @@ int tegra_dc_update_windows(struct tegra_dc_win *windows[], int n)
                        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_blend) {
@@ -1226,23 +1364,33 @@ int tegra_dc_update_windows(struct tegra_dc_win *windows[], int n)
 
        tegra_dc_writel(dc, update_mask << 8, DC_CMD_STATE_CONTROL);
 
+       tegra_dc_writel(dc, FRAME_END_INT | V_BLANK_INT, DC_CMD_INT_STATUS);
        if (!no_vsync) {
-               val = tegra_dc_readl(dc, DC_CMD_INT_ENABLE);
+               val = tegra_dc_readl(dc, DC_CMD_INT_MASK);
                val |= (FRAME_END_INT | V_BLANK_INT | ALL_UF_INT);
-               tegra_dc_writel(dc, val, DC_CMD_INT_ENABLE);
+               tegra_dc_writel(dc, val, DC_CMD_INT_MASK);
        } else {
-               val = tegra_dc_readl(dc, DC_CMD_INT_ENABLE);
+               val = tegra_dc_readl(dc, DC_CMD_INT_MASK);
                val &= ~(FRAME_END_INT | V_BLANK_INT | ALL_UF_INT);
-
-               tegra_dc_writel(dc, val, DC_CMD_INT_ENABLE);
+               tegra_dc_writel(dc, val, DC_CMD_INT_MASK);
        }
 
-       tegra_dc_writel(dc, update_mask, DC_CMD_STATE_CONTROL);
+       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);
 
        if (dc->out->flags & TEGRA_DC_OUT_ONE_SHOT_MODE)
-               tegra_dc_writel(dc, NC_HOST_TRIG, DC_CMD_STATE_CONTROL);
+               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;
 }
@@ -1259,7 +1407,7 @@ u32 tegra_dc_incr_syncpt_max(struct tegra_dc *dc, int i)
        u32 max;
 
        mutex_lock(&dc->lock);
-       max = nvhost_syncpt_incr_max(&dc->ndev->host->syncpt,
+       max = nvhost_syncpt_incr_max(&nvhost_get_host(dc->ndev)->syncpt,
                dc->syncpt[i].id, ((dc->enabled) ? 1 : 0));
        dc->syncpt[i].max = max;
        mutex_unlock(&dc->lock);
@@ -1273,7 +1421,8 @@ void tegra_dc_incr_syncpt_min(struct tegra_dc *dc, int i, u32 val)
        if ( dc->enabled )
                while (dc->syncpt[i].min < val) {
                        dc->syncpt[i].min++;
-                       nvhost_syncpt_cpu_incr(&dc->ndev->host->syncpt,
+                       nvhost_syncpt_cpu_incr(
+                                       &nvhost_get_host(dc->ndev)->syncpt,
                                        dc->syncpt[i].id);
                }
        mutex_unlock(&dc->lock);
@@ -1295,6 +1444,7 @@ static bool tegra_dc_windows_are_clean(struct tegra_dc_win *windows[],
 /* 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;
 
@@ -1303,13 +1453,18 @@ int tegra_dc_sync_windows(struct tegra_dc_win *windows[], int n)
 
 #ifdef CONFIG_TEGRA_SIMULATION_PLATFORM
        /* Don't want to timeout on simulator */
-       return wait_event_interruptible(windows[0]->dc->wq,
+       ret = wait_event_interruptible(windows[0]->dc->wq,
                tegra_dc_windows_are_clean(windows, n));
 #else
-       return wait_event_interruptible_timeout(windows[0]->dc->wq,
+       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);
 #endif
+       return ret;
 }
 EXPORT_SYMBOL(tegra_dc_sync_windows);
 
@@ -1337,6 +1492,21 @@ static unsigned long tegra_dc_pclk_round_rate(struct tegra_dc *dc, int pclk)
        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;
@@ -1346,6 +1516,17 @@ void tegra_dc_setup_clk(struct tegra_dc *dc, struct clk *clk)
                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);
 
@@ -1366,15 +1547,13 @@ void tegra_dc_setup_clk(struct tegra_dc *dc, struct clk *clk)
                        clk_get_sys(NULL, dc->out->parent_clk ? : "pll_d_out0");
                struct clk *base_clk = clk_get_parent(parent_clk);
 
-               /* needs to match tegra_dc_hdmi_supported_modes[]
-               and tegra_pll_d_freq_table[] */
-               if (dc->mode.pclk > 70000000)
-                       rate = 594000000;
-               else if (dc->mode.pclk > 25200000)
-                       rate = 216000000;
-               else
-                       rate = 504000000;
+               /*
+                * 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);
 
@@ -1409,7 +1588,7 @@ void tegra_dc_setup_clk(struct tegra_dc *dc, struct clk *clk)
                        }
                }
 
-               rate = dc->mode.pclk;
+               rate = dc->mode.pclk * dc->shift_clk_div * 2;
                if (rate != clk_get_rate(base_clk))
                        clk_set_rate(base_clk, rate);
 
@@ -1534,14 +1713,14 @@ static bool check_ref_to_sync(struct tegra_dc_mode *mode)
 
 #ifdef DEBUG
 /* return in 1000ths of a Hertz */
-static int calc_refresh(struct tegra_dc *dc, const struct tegra_dc_mode *m)
+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 = dc->pixel_clk / h_total;
+       refresh = m->pclk / h_total;
        refresh *= 1000;
        refresh /= v_total;
        return refresh;
@@ -1607,18 +1786,6 @@ static int tegra_dc_program_mode(struct tegra_dc *dc, struct tegra_dc_mode *mode
        tegra_dc_writel(dc, DE_SELECT_ACTIVE | DE_CONTROL_NORMAL,
                        DC_DISP_DATA_ENABLE_OPTIONS);
 
-       val = tegra_dc_readl(dc, DC_COM_PIN_OUTPUT_POLARITY1);
-       if (mode->flags & TEGRA_DC_MODE_FLAG_NEG_V_SYNC)
-               val |= PIN1_LVS_OUTPUT;
-       else
-               val &= ~PIN1_LVS_OUTPUT;
-
-       if (mode->flags & TEGRA_DC_MODE_FLAG_NEG_H_SYNC)
-               val |= PIN1_LHS_OUTPUT;
-       else
-               val &= ~PIN1_LHS_OUTPUT;
-       tegra_dc_writel(dc, val, DC_COM_PIN_OUTPUT_POLARITY1);
-
        /* TODO: MIPI/CRT/HDMI clock cals */
 
        val = DISP_DATA_FORMAT_DF1P1C;
@@ -1638,6 +1805,7 @@ static int tegra_dc_program_mode(struct tegra_dc *dc, struct tegra_dc_mode *mode
        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,
@@ -1649,14 +1817,22 @@ static int tegra_dc_program_mode(struct tegra_dc *dc, struct tegra_dc_mode *mode
        }
 
        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);
 
-       dc->pixel_clk = dc->mode.pclk;
+#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;
 }
 
@@ -1706,6 +1882,8 @@ int tegra_dc_set_fb_mode(struct tegra_dc *dc,
                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 */
@@ -1714,6 +1892,7 @@ int tegra_dc_set_fb_mode(struct tegra_dc *dc,
                                fbmode->upper_margin +
                                fbmode->lower_margin;
        }
+#endif
 
        mode.flags = 0;
 
@@ -1781,7 +1960,7 @@ tegra_dc_config_pwm(struct tegra_dc *dc, struct tegra_dc_pwm_params *cfg)
 }
 EXPORT_SYMBOL(tegra_dc_config_pwm);
 
-static void tegra_dc_set_out_pin_polars(struct tegra_dc *dc,
+void tegra_dc_set_out_pin_polars(struct tegra_dc *dc,
                                const struct tegra_dc_out_pin *pins,
                                const unsigned int n_pins)
 {
@@ -1834,8 +2013,8 @@ static void tegra_dc_set_out_pin_polars(struct tegra_dc *dc,
                }
        }
 
-       pol1 = tegra_dc_readl(dc, DC_COM_PIN_OUTPUT_POLARITY1);
-       pol3 = tegra_dc_readl(dc, DC_COM_PIN_OUTPUT_POLARITY3);
+       pol1 = DC_COM_PIN_OUTPUT_POLARITY1_INIT_VAL;
+       pol3 = DC_COM_PIN_OUTPUT_POLARITY3_INIT_VAL;
 
        pol1 |= set1;
        pol1 &= ~unset1;
@@ -1929,7 +2108,7 @@ u32 tegra_dc_read_checksum_latched(struct tegra_dc *dc)
 {
        int crc = 0;
 
-       if(!dc) {
+       if (!dc) {
                dev_err(&dc->ndev->dev, "Failed to get dc.\n");
                goto crc_error;
        }
@@ -1950,9 +2129,6 @@ static void tegra_dc_vblank(struct work_struct *work)
 
        mutex_lock(&dc->lock);
 
-       /* update EMC clock if calculated bandwidth has changed */
-       tegra_dc_program_bandwidth(dc);
-
        /* Update the SD brightness */
        if (dc->enabled && dc->out->sd_settings)
                nvsd_updated = nvsd_update_brightness(dc);
@@ -1970,10 +2146,63 @@ static void tegra_dc_vblank(struct work_struct *work)
        }
 }
 
-#ifndef CONFIG_TEGRA_FPGA_PLATFORM
+/* Must acquire dc lock and dc one-shot lock before invoking this function.
+ * Acquire dc one-shot lock first and then dc lock. */
+void tegra_dc_host_trigger(struct tegra_dc *dc)
+{
+       /* We release the lock here to prevent deadlock between
+        * cancel_delayed_work_sync and one-shot work. */
+       mutex_unlock(&dc->lock);
+
+       cancel_delayed_work_sync(&dc->one_shot_work);
+       mutex_lock(&dc->lock);
+
+       schedule_delayed_work(&dc->one_shot_work,
+                               msecs_to_jiffies(dc->one_shot_delay_ms));
+       tegra_dc_program_bandwidth(dc);
+       tegra_dc_writel(dc, NC_HOST_TRIG, DC_CMD_STATE_CONTROL);
+}
+
+static void tegra_dc_one_shot_worker(struct work_struct *work)
+{
+       struct tegra_dc *dc = container_of(
+               to_delayed_work(work), struct tegra_dc, one_shot_work);
+       mutex_lock(&dc->lock);
+       /* memory client has gone idle */
+       tegra_dc_clear_bandwidth(dc);
+       mutex_unlock(&dc->lock);
+}
+
+/* return an arbitrarily large number if count overflow occurs.
+ * make it a nice base-10 number to show up in stats output */
+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, i;
+       u32 val;
+       int i;
+
+       dc->stats.underflows++;
+       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) {
+               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) {
+               dc->stats.underflows_c += tegra_dc_underflow_count(dc,
+                       DC_WINBUF_CD_UFLOW_STATUS);
+               trace_printk("%s:Window C Underflow\n", dc->ndev->name);
+       }
 
        /* Check for any underflow reset conditions */
        for (i = 0; i < DC_N_WINDOWS; i++) {
@@ -1981,27 +2210,39 @@ static void tegra_dc_underflow_handler(struct tegra_dc *dc)
                        dc->windows[i].underflows++;
 
 #ifdef CONFIG_ARCH_TEGRA_2x_SOC
-                       if (dc->windows[i].underflows > 4)
+                       if (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));
+                       }
 #endif
                } else {
                        dc->windows[i].underflows = 0;
                }
        }
 
-       if (!dc->underflow_mask) {
-               /* If we have no underflow to check, go ahead
-                  and disable the interrupt */
-               val = tegra_dc_readl(dc, DC_CMD_INT_ENABLE);
-               if (dc->out->flags & TEGRA_DC_OUT_ONE_SHOT_MODE)
-                       val &= ~FRAME_END_INT;
-               else
-                       val &= ~V_BLANK_INT;
-               tegra_dc_writel(dc, val, DC_CMD_INT_ENABLE);
-       }
-
        /* 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);
+}
+
+#ifndef CONFIG_TEGRA_FPGA_PLATFORM
+static bool tegra_dc_windows_are_dirty(struct tegra_dc *dc)
+{
+#ifndef CONFIG_TEGRA_SIMULATION_PLATFORM
+       u32 val;
+
+       val = tegra_dc_readl(dc, DC_CMD_STATE_CONTROL);
+       if (val & (WIN_A_UPDATE | WIN_B_UPDATE | WIN_C_UPDATE))
+           return true;
+#endif
+       return false;
 }
 
 static void tegra_dc_trigger_windows(struct tegra_dc *dc)
@@ -2028,22 +2269,18 @@ static void tegra_dc_trigger_windows(struct tegra_dc *dc)
        }
 
        if (!dirty) {
-               val = tegra_dc_readl(dc, DC_CMD_INT_ENABLE);
+               val = tegra_dc_readl(dc, DC_CMD_INT_MASK);
                if (dc->out->flags & TEGRA_DC_OUT_ONE_SHOT_MODE)
                        val &= ~V_BLANK_INT;
                else
                        val &= ~FRAME_END_INT;
-               tegra_dc_writel(dc, val, DC_CMD_INT_ENABLE);
+               tegra_dc_writel(dc, val, DC_CMD_INT_MASK);
        }
 
        if (completed) {
                if (!dirty) {
                        /* With the last completed window, go ahead
                           and enable the vblank interrupt for nvsd. */
-                       val = tegra_dc_readl(dc, DC_CMD_INT_ENABLE);
-                       val |= V_BLANK_INT;
-                       tegra_dc_writel(dc, val, DC_CMD_INT_ENABLE);
-
                        val = tegra_dc_readl(dc, DC_CMD_INT_MASK);
                        val |= V_BLANK_INT;
                        tegra_dc_writel(dc, val, DC_CMD_INT_MASK);
@@ -2061,54 +2298,63 @@ static void tegra_dc_one_shot_irq(struct tegra_dc *dc, unsigned long status)
 
                /* Schedule any additional bottom-half vblank actvities. */
                schedule_work(&dc->vblank_work);
-
-               /* Mark the vblank as complete. */
-               complete(&dc->vblank_complete);
-
        }
 
-       /* Check underflow at frame end */
-       if (status & FRAME_END_INT)
-               tegra_dc_underflow_handler(dc);
+       if (status & FRAME_END_INT) {
+               /* Mark the frame_end as complete. */
+               if (!completion_done(&dc->frame_end_complete))
+                       complete(&dc->frame_end_complete);
+       }
 }
 
 static void tegra_dc_continuous_irq(struct tegra_dc *dc, unsigned long status)
 {
        if (status & V_BLANK_INT) {
-               /* Check underflow */
-               tegra_dc_underflow_handler(dc);
-
                /* Schedule any additional bottom-half vblank actvities. */
                schedule_work(&dc->vblank_work);
 
-               /* Mark the vblank as complete. */
-               complete(&dc->vblank_complete);
+               /* All windows updated. Mask subsequent V_BLANK interrupts */
+               if (!tegra_dc_windows_are_dirty(dc)) {
+                       u32 val;
+
+                       val = tegra_dc_readl(dc, DC_CMD_INT_MASK);
+                       val &= ~V_BLANK_INT;
+                       tegra_dc_writel(dc, val, DC_CMD_INT_MASK);
+               }
        }
 
-       if (status & FRAME_END_INT)
+       if (status & FRAME_END_INT) {
+               /* Mark the frame_end as complete. */
+               if (!completion_done(&dc->frame_end_complete))
+                       complete(&dc->frame_end_complete);
+
                tegra_dc_trigger_windows(dc);
+       }
 }
 #endif
 
-/* return an arbitrarily large number if count overflow occurs.
- * make it a nice base-10 number to show up in stats output */
-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 irqreturn_t tegra_dc_irq(int irq, void *ptr)
 {
 #ifndef CONFIG_TEGRA_FPGA_PLATFORM
        struct tegra_dc *dc = ptr;
        unsigned long status;
-       unsigned long val;
        unsigned long underflow_mask;
+       u32 val;
+
+       if (!nvhost_module_powered(nvhost_get_host(dc->ndev)->dev)) {
+               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);
+               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, 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);
 
        /*
         * Overlays can get thier internal state corrupted during and underflow
@@ -2118,21 +2364,11 @@ static irqreturn_t tegra_dc_irq(int irq, void *ptr)
         */
        underflow_mask = status & ALL_UF_INT;
 
+       /* Check underflow */
        if (underflow_mask) {
-               val = tegra_dc_readl(dc, DC_CMD_INT_ENABLE);
-               val |= V_BLANK_INT;
-               tegra_dc_writel(dc, val, DC_CMD_INT_ENABLE);
                dc->underflow_mask |= underflow_mask;
-               dc->stats.underflows++;
-               if (status & WIN_A_UF_INT)
-                       dc->stats.underflows_a += tegra_dc_underflow_count(dc,
-                               DC_WINBUF_AD_UFLOW_STATUS);
-               if (status & WIN_B_UF_INT)
-                       dc->stats.underflows_b += tegra_dc_underflow_count(dc,
-                               DC_WINBUF_BD_UFLOW_STATUS);
-               if (status & WIN_C_UF_INT)
-                       dc->stats.underflows_c += tegra_dc_underflow_count(dc,
-                               DC_WINBUF_CD_UFLOW_STATUS);
+               schedule_delayed_work(&dc->underflow_work,
+                       msecs_to_jiffies(1));
        }
 
        if (dc->out->flags & TEGRA_DC_OUT_ONE_SHOT_MODE)
@@ -2254,7 +2490,7 @@ static u32 get_syncpt(struct tegra_dc *dc, int idx)
        return syncpt_id;
 }
 
-static void tegra_dc_init(struct tegra_dc *dc)
+static int tegra_dc_init(struct tegra_dc *dc)
 {
        int i;
 
@@ -2289,10 +2525,10 @@ static void tegra_dc_init(struct tegra_dc *dc)
        tegra_dc_writel(dc, 0x00202020, DC_DISP_MEM_HIGH_PRIORITY);
        tegra_dc_writel(dc, 0x00010101, DC_DISP_MEM_HIGH_PRIORITY_TIMER);
 
-       tegra_dc_writel(dc, (FRAME_END_INT |
-                            V_BLANK_INT |
-                            ALL_UF_INT), DC_CMD_INT_MASK);
-       tegra_dc_writel(dc, ALL_UF_INT, DC_CMD_INT_ENABLE);
+       /* 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);
 
        tegra_dc_writel(dc, 0x00000000, DC_DISP_BORDER_COLOR);
 
@@ -2313,28 +2549,32 @@ static void tegra_dc_init(struct tegra_dc *dc)
                dc->syncpt[i].id = syncpt;
 
                dc->syncpt[i].min = dc->syncpt[i].max =
-                       nvhost_syncpt_read(&dc->ndev->host->syncpt, syncpt);
+                       nvhost_syncpt_read(&nvhost_get_host(dc->ndev)->syncpt,
+                                       syncpt);
        }
 
        print_mode(dc, &dc->mode, __func__);
 
        if (dc->mode.pclk)
-               tegra_dc_program_mode(dc, &dc->mode);
+               if (tegra_dc_program_mode(dc, &dc->mode))
+                       return -EINVAL;
 
        /* Initialize SD AFTER the modeset.
           nvsd_init handles the sd_settings = NULL case. */
        nvsd_init(dc, dc->out->sd_settings);
+
+       return 0;
 }
 
 static bool _tegra_dc_controller_enable(struct tegra_dc *dc)
 {
+       int failed_init = 0;
+
        if (dc->out->enable)
                dc->out->enable();
 
        tegra_dc_setup_clk(dc, dc->clk);
-       tegra_periph_reset_assert(dc->clk);
        clk_enable(dc->clk);
-       clk_enable(dc->emc_clk);
 
        /* do not accept interrupts during initialization */
        tegra_dc_writel(dc, 0, DC_CMD_INT_ENABLE);
@@ -2342,15 +2582,15 @@ static bool _tegra_dc_controller_enable(struct tegra_dc *dc)
 
        enable_dc_irq(dc->irq);
 
-       tegra_dc_init(dc);
+       failed_init = tegra_dc_init(dc);
+       if (failed_init) {
+               _tegra_dc_controller_disable(dc);
+               return false;
+       }
 
        if (dc->out_ops && dc->out_ops->enable)
                dc->out_ops->enable(dc);
 
-       if (dc->out->out_pins)
-               tegra_dc_set_out_pin_polars(dc, dc->out->out_pins,
-                                           dc->out->n_out_pins);
-
        if (dc->out->postpoweron)
                dc->out->postpoweron();
 
@@ -2359,18 +2599,20 @@ static bool _tegra_dc_controller_enable(struct tegra_dc *dc)
 
        tegra_dc_ext_enable(dc->ext);
 
+       trace_printk("%s:enable\n", dc->ndev->name);
        return true;
 }
 
 #ifdef CONFIG_ARCH_TEGRA_2x_SOC
 static bool _tegra_dc_controller_reset_enable(struct tegra_dc *dc)
 {
+       bool ret = true;
+
        if (dc->out->enable)
                dc->out->enable();
 
        tegra_dc_setup_clk(dc, dc->clk);
        clk_enable(dc->clk);
-       clk_enable(dc->emc_clk);
 
        if (dc->ndev->id == 0 && tegra_dcs[1] != NULL) {
                mutex_lock(&tegra_dcs[1]->lock);
@@ -2398,29 +2640,61 @@ static bool _tegra_dc_controller_reset_enable(struct tegra_dc *dc)
 
        enable_dc_irq(dc->irq);
 
-       tegra_dc_init(dc);
+       if (tegra_dc_init(dc)) {
+               dev_err(&dc->ndev->dev, "cannot initialize\n");
+               ret = false;
+       }
 
        if (dc->out_ops && dc->out_ops->enable)
                dc->out_ops->enable(dc);
 
-       if (dc->out->out_pins)
-               tegra_dc_set_out_pin_polars(dc, dc->out->out_pins,
-                                           dc->out->n_out_pins);
-
        if (dc->out->postpoweron)
                dc->out->postpoweron();
 
        /* force a full blending update */
        dc->blend.z[0] = -1;
 
-       return true;
+       tegra_dc_ext_enable(dc->ext);
+
+       if (!ret) {
+               dev_err(&dc->ndev->dev, "initialization failed,disabling");
+               _tegra_dc_controller_disable(dc);
+       }
+
+       trace_printk("%s:reset enable\n", dc->ndev->name);
+       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)
-               return false;
+       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;
+               } else
+                       return false;
+
+               break;
+
+               /* Do nothing for other outputs for now */
+               case TEGRA_DC_OUT_RGB:
+
+               case TEGRA_DC_OUT_DSI:
+
+               default:
+                       return false;
+               }
+       }
 
        if (!dc->out)
                return false;
@@ -2438,18 +2712,21 @@ void tegra_dc_enable(struct tegra_dc *dc)
                dc->enabled = _tegra_dc_enable(dc);
 
        mutex_unlock(&dc->lock);
+       print_mode_info(dc, dc->mode);
 }
 
 static void _tegra_dc_controller_disable(struct tegra_dc *dc)
 {
        unsigned i;
 
-       disable_irq(dc->irq);
-
        if (dc->out_ops && dc->out_ops->disable)
                dc->out_ops->disable(dc);
 
-       clk_disable(dc->emc_clk);
+       tegra_dc_writel(dc, 0, DC_CMD_INT_MASK);
+       tegra_dc_writel(dc, 0, DC_CMD_INT_ENABLE);
+       disable_irq(dc->irq);
+
+       tegra_dc_clear_bandwidth(dc);
        clk_disable(dc->clk);
        tegra_dvfs_set_rate(dc->clk, 0);
 
@@ -2468,11 +2745,15 @@ 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);
                        dc->syncpt[i].min++;
-                       nvhost_syncpt_cpu_incr(&dc->ndev->host->syncpt,
+                       nvhost_syncpt_cpu_incr(
+                               &nvhost_get_host(dc->ndev)->syncpt,
                                dc->syncpt[i].id);
                }
        }
+       trace_printk("%s:disabled\n", dc->ndev->name);
 }
 
 void tegra_dc_stats_enable(struct tegra_dc *dc, bool enable)
@@ -2531,11 +2812,16 @@ static void _tegra_dc_disable(struct tegra_dc *dc)
 
 void tegra_dc_disable(struct tegra_dc *dc)
 {
-       if (dc->overlay)
-               tegra_overlay_disable(dc->overlay);
-
        tegra_dc_ext_disable(dc->ext);
 
+       /* 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->enabled) {
@@ -2545,7 +2831,14 @@ void tegra_dc_disable(struct tegra_dc *dc)
                        _tegra_dc_disable(dc);
        }
 
+#ifdef CONFIG_SWITCH
+       switch_set_state(&dc->modeset_switch, 0);
+#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);
 }
 
 #ifdef CONFIG_ARCH_TEGRA_2x_SOC
@@ -2556,11 +2849,12 @@ static void tegra_dc_reset_worker(struct work_struct *work)
 
        unsigned long val = 0;
 
+       mutex_lock(&shared_lock);
+
        dev_warn(&dc->ndev->dev, "overlay stuck in underflow state.  resetting.\n");
 
        tegra_dc_ext_disable(dc->ext);
 
-       mutex_lock(&shared_lock);
        mutex_lock(&dc->lock);
 
        if (dc->enabled == false)
@@ -2588,14 +2882,47 @@ static void tegra_dc_reset_worker(struct work_struct *work)
        _tegra_dc_controller_reset_enable(dc);
 
        dc->enabled = true;
+
+       /* reopen host read bus */
+       val = tegra_dc_readl(dc, DC_CMD_CONT_SYNCPT_VSYNC);
+       val &= ~(0x00000100);
+       val |= 0x100;
+       tegra_dc_writel(dc, val, DC_CMD_CONT_SYNCPT_VSYNC);
+
 unlock:
        mutex_unlock(&dc->lock);
        mutex_unlock(&shared_lock);
+       trace_printk("%s:reset complete\n", dc->ndev->name);
 }
 #endif
 
+static void tegra_dc_underflow_worker(struct work_struct *work)
+{
+       struct tegra_dc *dc = container_of(
+               to_delayed_work(work), struct tegra_dc, underflow_work);
 
-static int tegra_dc_probe(struct nvhost_device *ndev)
+       mutex_lock(&dc->lock);
+       if (dc->enabled) {
+               tegra_dc_underflow_handler(dc);
+       }
+       mutex_unlock(&dc->lock);
+}
+
+#ifdef CONFIG_SWITCH
+static ssize_t switch_modeset_print_mode(struct switch_dev *sdev, char *buf)
+{
+       struct tegra_dc *dc =
+               container_of(sdev, struct tegra_dc, modeset_switch);
+
+       if (!sdev->state)
+               return sprintf(buf, "offline\n");
+
+       return sprintf(buf, "%dx%d\n", dc->mode.h_active, dc->mode.v_active);
+}
+#endif
+
+static int tegra_dc_probe(struct nvhost_device *ndev,
+       struct nvhost_device_id *id_table)
 {
        struct tegra_dc *dc;
        struct clk *clk;
@@ -2665,6 +2992,10 @@ static int tegra_dc_probe(struct nvhost_device *ndev)
 
        dc->clk = clk;
        dc->emc_clk = emc_clk;
+       dc->shift_clk_div = 1;
+       /* Initialize one shot work delay, it will be assigned by dsi
+        * according to refresh rate later. */
+       dc->one_shot_delay_ms = 40;
 
        dc->base_res = base_res;
        dc->base = base;
@@ -2676,19 +3007,18 @@ static int tegra_dc_probe(struct nvhost_device *ndev)
         * The emc is a shared clock, it will be set based on
         * the requirements for each user on the bus.
         */
-       dc->emc_clk_rate = tegra_dc_get_default_emc_clk_rate(dc);
-       clk_set_rate(emc_clk, dc->emc_clk_rate);
-
-       if (dc->pdata->flags & TEGRA_DC_FLAG_ENABLED)
-               dc->enabled = true;
+       dc->emc_clk_rate = 0;
 
        mutex_init(&dc->lock);
-       init_completion(&dc->vblank_complete);
+       mutex_init(&dc->one_shot_lock);
+       init_completion(&dc->frame_end_complete);
        init_waitqueue_head(&dc->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);
+       INIT_DELAYED_WORK(&dc->underflow_work, tegra_dc_underflow_worker);
+       INIT_DELAYED_WORK(&dc->one_shot_work, tegra_dc_one_shot_worker);
 
        tegra_dc_init_lut_defaults(&dc->fb_lut);
 
@@ -2709,6 +3039,15 @@ static int tegra_dc_probe(struct nvhost_device *ndev)
 
        nvhost_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);
+#endif
+
+       tegra_dc_feature_register(dc);
+
        if (dc->pdata->default_out)
                tegra_dc_set_out(dc, dc->pdata->default_out);
        else
@@ -2723,22 +3062,19 @@ 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, IRQF_DISABLED,
+       if (request_irq(irq, tegra_dc_irq, 0,
                        dev_name(&ndev->dev), dc)) {
                dev_err(&ndev->dev, "request_irq %d failed\n", irq);
                ret = -EBUSY;
                goto err_put_emc_clk;
        }
 
-       /* hack to balance enable_irq calls in _tegra_dc_enable() */
-       disable_dc_irq(dc->irq);
-
-       mutex_lock(&dc->lock);
-       if (dc->enabled)
-               _tegra_dc_enable(dc);
-       mutex_unlock(&dc->lock);
-
        tegra_dc_create_debugfs(dc);
 
        dev_info(&ndev->dev, "probed\n");
@@ -2760,12 +3096,6 @@ static int tegra_dc_probe(struct nvhost_device *ndev)
                        dc->fb = NULL;
        }
 
-       if (dc->fb) {
-               dc->overlay = tegra_overlay_register(ndev, dc);
-               if (IS_ERR_OR_NULL(dc->overlay))
-                       dc->overlay = NULL;
-       }
-
        if (dc->out && dc->out->hotplug_init)
                dc->out->hotplug_init();
 
@@ -2803,10 +3133,6 @@ static int tegra_dc_remove(struct nvhost_device *ndev)
        tegra_dc_remove_sysfs(&dc->ndev->dev);
        tegra_dc_remove_debugfs(dc);
 
-       if (dc->overlay) {
-               tegra_overlay_unregister(dc->overlay);
-       }
-
        if (dc->fb) {
                tegra_fb_unregister(dc->fb);
                if (dc->fb_mem)
@@ -2821,6 +3147,9 @@ static int tegra_dc_remove(struct nvhost_device *ndev)
        if (dc->enabled)
                _tegra_dc_disable(dc);
 
+#ifdef CONFIG_SWITCH
+       switch_dev_unregister(&dc->modeset_switch);
+#endif
        free_irq(dc->irq, dc);
        clk_put(dc->emc_clk);
        clk_put(dc->clk);
@@ -2837,11 +3166,9 @@ static int tegra_dc_suspend(struct nvhost_device *ndev, pm_message_t state)
 {
        struct tegra_dc *dc = nvhost_get_drvdata(ndev);
 
+       trace_printk("%s:suspend\n", dc->ndev->name);
        dev_info(&ndev->dev, "suspend\n");
 
-       if (dc->overlay)
-               tegra_overlay_disable(dc->overlay);
-
        tegra_dc_ext_disable(dc->ext);
 
        mutex_lock(&dc->lock);
@@ -2857,7 +3184,11 @@ static int tegra_dc_suspend(struct nvhost_device *ndev, pm_message_t state)
 
        if (dc->out && dc->out->postsuspend) {
                dc->out->postsuspend();
-               msleep(100); /* avoid resume event due to voltage falling */
+               if (dc->out->type && dc->out->type == TEGRA_DC_OUT_HDMI)
+                       /*
+                        * avoid resume event due to voltage falling
+                        */
+                       msleep(100);
        }
 
        mutex_unlock(&dc->lock);
@@ -2869,6 +3200,7 @@ static int tegra_dc_resume(struct nvhost_device *ndev)
 {
        struct tegra_dc *dc = nvhost_get_drvdata(ndev);
 
+       trace_printk("%s:resume\n", dc->ndev->name);
        dev_info(&ndev->dev, "resume\n");
 
        mutex_lock(&dc->lock);