tegra: dc: Fix div by zero in frame time computation.
[linux-2.6.git] / drivers / video / tegra / dc / dc.c
index e0c4027..d01df2f 100644 (file)
@@ -73,6 +73,8 @@ static struct fb_videomode tegra_dc_hdmi_fallback_mode = {
        .sync = 0,
 };
 
+static struct tegra_dc_mode override_disp_mode[3];
+
 static void _tegra_dc_controller_disable(struct tegra_dc *dc);
 
 struct tegra_dc *tegra_dcs[TEGRA_MAX_DC];
@@ -80,7 +82,7 @@ struct tegra_dc *tegra_dcs[TEGRA_MAX_DC];
 DEFINE_MUTEX(tegra_dc_lock);
 DEFINE_MUTEX(shared_lock);
 
-static inline void tegra_dc_clk_enable(struct tegra_dc *dc)
+void tegra_dc_clk_enable(struct tegra_dc *dc)
 {
        if (!tegra_is_clk_enabled(dc->clk)) {
                clk_enable(dc->clk);
@@ -88,7 +90,7 @@ static inline void tegra_dc_clk_enable(struct tegra_dc *dc)
        }
 }
 
-static inline void tegra_dc_clk_disable(struct tegra_dc *dc)
+void tegra_dc_clk_disable(struct tegra_dc *dc)
 {
        if (tegra_is_clk_enabled(dc->clk)) {
                clk_disable(dc->clk);
@@ -96,6 +98,18 @@ static inline void tegra_dc_clk_disable(struct tegra_dc *dc)
        }
 }
 
+void tegra_dc_hold_dc_out(struct tegra_dc *dc)
+{
+       if (dc->out_ops->hold)
+               dc->out_ops->hold(dc);
+}
+
+void tegra_dc_release_dc_out(struct tegra_dc *dc)
+{
+       if (dc->out_ops->release)
+               dc->out_ops->release(dc);
+}
+
 #define DUMP_REG(a) do {                       \
        snprintf(buff, sizeof(buff), "%-32s\t%03x\t%08lx\n", \
                 #a, a, tegra_dc_readl(dc, a));               \
@@ -119,8 +133,9 @@ static void _dump_regs(struct tegra_dc *dc, void *data,
        int i;
        char buff[256];
 
+       mutex_lock(&dc->lock);
+       tegra_dc_hold_dc_out(dc);
        tegra_dc_io_start(dc);
-       tegra_dc_clk_enable(dc);
 
        DUMP_REG(DC_CMD_DISPLAY_COMMAND_OPTION0);
        DUMP_REG(DC_CMD_DISPLAY_COMMAND);
@@ -270,8 +285,9 @@ static void _dump_regs(struct tegra_dc *dc, void *data,
        DUMP_REG(DC_COM_PM1_DUTY_CYCLE);
        DUMP_REG(DC_DISP_SD_CONTROL);
 
-       tegra_dc_clk_disable(dc);
        tegra_dc_io_end(dc);
+       tegra_dc_release_dc_out(dc);
+       mutex_unlock(&dc->lock);
 }
 
 #undef DUMP_REG
@@ -486,9 +502,13 @@ int tegra_dc_get_stride(struct tegra_dc *dc, unsigned win)
        if (!dc->enabled)
                return 0;
        BUG_ON(win > DC_N_WINDOWS);
+       mutex_lock(&dc->lock);
+       tegra_dc_hold_dc_out(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);
+       mutex_unlock(&dc->lock);
        return GET_LINE_STRIDE(stride);
 }
 EXPORT_SYMBOL(tegra_dc_get_stride);
@@ -548,17 +568,6 @@ static void tegra_dc_set_scaling_filter(struct tegra_dc *dc)
        }
 }
 
-void tegra_dc_host_suspend(struct tegra_dc *dc)
-{
-       tegra_dsi_host_suspend(dc);
-       tegra_dc_clk_disable(dc);
-}
-
-void tegra_dc_host_resume(struct tegra_dc *dc) {
-       tegra_dc_clk_enable(dc);
-       tegra_dsi_host_resume(dc);
-}
-
 static inline void disable_dc_irq(unsigned int irq)
 {
        disable_irq(irq);
@@ -575,9 +584,11 @@ u32 tegra_dc_incr_syncpt_max(struct tegra_dc *dc, int i)
        u32 max;
 
        mutex_lock(&dc->lock);
+       tegra_dc_hold_dc_out(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);
        mutex_unlock(&dc->lock);
 
        return max;
@@ -586,11 +597,14 @@ u32 tegra_dc_incr_syncpt_max(struct tegra_dc *dc, int i)
 void tegra_dc_incr_syncpt_min(struct tegra_dc *dc, int i, u32 val)
 {
        mutex_lock(&dc->lock);
-       if ( dc->enabled )
+       if (dc->enabled) {
+               tegra_dc_hold_dc_out(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);
+       }
        mutex_unlock(&dc->lock);
 }
 
@@ -607,8 +621,7 @@ tegra_dc_config_pwm(struct tegra_dc *dc, struct tegra_dc_pwm_params *cfg)
                return;
        }
 
-       if (dc->out->flags & TEGRA_DC_OUT_ONE_SHOT_LP_MODE)
-               tegra_dc_host_resume(dc);
+       tegra_dc_hold_dc_out(dc);
 
        ctrl = ((cfg->period << PM_PERIOD_SHIFT) |
                (cfg->clk_div << PM_CLK_DIVIDER_SHIFT) |
@@ -642,6 +655,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);
        mutex_unlock(&dc->lock);
 }
 EXPORT_SYMBOL(tegra_dc_config_pwm);
@@ -712,11 +726,27 @@ void tegra_dc_set_out_pin_polars(struct tegra_dc *dc,
        tegra_dc_writel(dc, pol3, DC_COM_PIN_OUTPUT_POLARITY3);
 }
 
+static struct tegra_dc_mode *tegra_dc_get_override_mode(struct tegra_dc *dc)
+{
+       if (dc->out->type == TEGRA_DC_OUT_RGB ||
+               dc->out->type == TEGRA_DC_OUT_HDMI ||
+               dc->out->type == TEGRA_DC_OUT_DSI)
+               return override_disp_mode[dc->out->type].pclk ?
+                       &override_disp_mode[dc->out->type] : NULL;
+       else
+               return NULL;
+}
+
 static void tegra_dc_set_out(struct tegra_dc *dc, struct tegra_dc_out *out)
 {
+       struct tegra_dc_mode *mode;
+
        dc->out = out;
+       mode = tegra_dc_get_override_mode(dc);
 
-       if (out->n_modes > 0)
+       if (mode)
+               tegra_dc_set_mode(dc, mode);
+       else if (out->n_modes > 0)
                tegra_dc_set_mode(dc, &dc->out->modes[0]);
 
        switch (out->type) {
@@ -772,6 +802,9 @@ EXPORT_SYMBOL(tegra_dc_get_out_max_pixclock);
 void tegra_dc_enable_crc(struct tegra_dc *dc)
 {
        u32 val;
+
+       mutex_lock(&dc->lock);
+       tegra_dc_hold_dc_out(dc);
        tegra_dc_io_start(dc);
 
        val = CRC_ALWAYS_ENABLE | CRC_INPUT_DATA_ACTIVE_DATA |
@@ -779,15 +812,21 @@ void tegra_dc_enable_crc(struct tegra_dc *dc)
        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);
+       mutex_unlock(&dc->lock);
 }
 
 void tegra_dc_disable_crc(struct tegra_dc *dc)
 {
+       mutex_lock(&dc->lock);
+       tegra_dc_hold_dc_out(dc);
        tegra_dc_writel(dc, 0x0, DC_COM_CRC_CONTROL);
        tegra_dc_writel(dc, GENERAL_UPDATE, DC_CMD_STATE_CONTROL);
        tegra_dc_writel(dc, GENERAL_ACT_REQ, DC_CMD_STATE_CONTROL);
 
        tegra_dc_io_end(dc);
+       tegra_dc_release_dc_out(dc);
+       mutex_unlock(&dc->lock);
 }
 
 u32 tegra_dc_read_checksum_latched(struct tegra_dc *dc)
@@ -803,7 +842,11 @@ u32 tegra_dc_read_checksum_latched(struct tegra_dc *dc)
         * DC_COM_CRC_CHECKSUM_LATCHED is available after VBLANK */
        mdelay(TEGRA_CRC_LATCHED_DELAY);
 
+       mutex_lock(&dc->lock);
+       tegra_dc_hold_dc_out(dc);
        crc = tegra_dc_readl(dc, DC_COM_CRC_CHECKSUM_LATCHED);
+       tegra_dc_release_dc_out(dc);
+       mutex_unlock(&dc->lock);
 crc_error:
        return crc;
 }
@@ -830,6 +873,36 @@ static inline void enable_dc_irq(unsigned int irq)
 #endif
 }
 
+void tegra_dc_get_fbvblank(struct tegra_dc *dc, struct fb_vblank *vblank)
+{
+       if (dc->out->flags & TEGRA_DC_OUT_ONE_SHOT_MODE)
+               vblank->flags = FB_VBLANK_HAVE_VSYNC;
+}
+
+int tegra_dc_wait_for_vsync(struct tegra_dc *dc)
+{
+       int ret = -ENOTTY;
+
+       if (!(dc->out->flags & TEGRA_DC_OUT_ONE_SHOT_MODE) || !dc->enabled)
+               return ret;
+
+       /*
+        * Logic is as follows
+        * a) Indicate we need a vblank.
+        * b) Wait for completion to be signalled from isr.
+        * c) Initialize completion for next iteration.
+        */
+
+       tegra_dc_hold_dc_out(dc);
+       dc->out->user_needs_vblank = true;
+
+       ret = wait_for_completion_interruptible(&dc->out->user_vblank_comp);
+       init_completion(&dc->out->user_vblank_comp);
+       tegra_dc_release_dc_out(dc);
+
+       return ret;
+}
+
 static void tegra_dc_vblank(struct work_struct *work)
 {
        struct tegra_dc *dc = container_of(work, struct tegra_dc, vblank_work);
@@ -842,6 +915,7 @@ static void tegra_dc_vblank(struct work_struct *work)
                return;
        }
 
+       tegra_dc_hold_dc_out(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))
@@ -868,6 +942,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);
        mutex_unlock(&dc->lock);
 
        /* Do the actual brightness update outside of the mutex */
@@ -890,8 +965,8 @@ static void tegra_dc_one_shot_worker(struct work_struct *work)
        /* memory client has gone idle */
        tegra_dc_clear_bandwidth(dc);
 
-       if (dc->out->flags & TEGRA_DC_OUT_ONE_SHOT_LP_MODE)
-               tegra_dc_host_suspend(dc);
+       if (dc->out_ops->idle)
+               dc->out_ops->idle(dc);
 
        mutex_unlock(&dc->lock);
 }
@@ -944,9 +1019,9 @@ static void tegra_dc_underflow_handler(struct tegra_dc *dc)
 #endif
 #ifdef CONFIG_ARCH_TEGRA_3x_SOC
                        if (dc->windows[i].underflows > 4) {
-                               printk("%s:dc in underflow state."
+                               trace_printk("%s:window %c in underflow state."
                                        " enable UF_LINE_FLUSH to clear up\n",
-                                       __func__);
+                                       dc->ndev->name, (65 + i));
                                tegra_dc_writel(dc, UF_LINE_FLUSH,
                                                DC_DISP_DISP_MISC_CONTROL);
                                tegra_dc_writel(dc, GENERAL_UPDATE,
@@ -978,6 +1053,13 @@ static void tegra_dc_underflow_handler(struct tegra_dc *dc)
 #ifndef CONFIG_TEGRA_FPGA_PLATFORM
 static void tegra_dc_one_shot_irq(struct tegra_dc *dc, unsigned long status)
 {
+       /* pending user vblank, so wakeup */
+       if ((status & (V_BLANK_INT | MSF_INT)) &&
+           (dc->out->user_needs_vblank)) {
+               dc->out->user_needs_vblank = false;
+               complete(&dc->out->user_vblank_comp);
+       }
+
        if (status & V_BLANK_INT) {
                /* Sync up windows. */
                tegra_dc_trigger_windows(dc);
@@ -1000,6 +1082,10 @@ static void tegra_dc_continuous_irq(struct tegra_dc *dc, unsigned long status)
                queue_work(system_freezable_wq, &dc->vblank_work);
 
        if (status & FRAME_END_INT) {
+               struct timespec tm = CURRENT_TIME;
+               dc->frame_end_timestamp = timespec_to_ns(&tm);
+               wake_up(&dc->timestamp_wq);
+
                /* Mark the frame_end as complete. */
                if (!completion_done(&dc->frame_end_complete))
                        complete(&dc->frame_end_complete);
@@ -1007,6 +1093,22 @@ static void tegra_dc_continuous_irq(struct tegra_dc *dc, unsigned long status)
                tegra_dc_trigger_windows(dc);
        }
 }
+
+/* XXX: Not sure if we limit look ahead to 1 frame */
+bool tegra_dc_is_within_n_vsync(struct tegra_dc *dc, s64 ts)
+{
+       BUG_ON(!dc->frametime_ns);
+       return ((ts - dc->frame_end_timestamp) < dc->frametime_ns);
+}
+
+bool tegra_dc_does_vsync_separate(struct tegra_dc *dc, s64 new_ts, s64 old_ts)
+{
+       BUG_ON(!dc->frametime_ns);
+       return (((new_ts - old_ts) > dc->frametime_ns)
+               || (div_s64((new_ts - dc->frame_end_timestamp), dc->frametime_ns)
+                       != div_s64((old_ts - dc->frame_end_timestamp),
+                               dc->frametime_ns)));
+}
 #endif
 
 static irqreturn_t tegra_dc_irq(int irq, void *ptr)
@@ -1169,6 +1271,7 @@ static u32 get_syncpt(struct tegra_dc *dc, int idx)
 static int tegra_dc_init(struct tegra_dc *dc)
 {
        int i;
+       int int_enable;
 
        tegra_dc_writel(dc, 0x00000100, DC_CMD_GENERAL_INCR_SYNCPT_CNTRL);
        if (dc->ndev->id == 0) {
@@ -1204,8 +1307,12 @@ static int tegra_dc_init(struct tegra_dc *dc)
        tegra_dc_writel(dc, 0x00000000, DC_DISP_DISP_MISC_CONTROL);
 #endif
        /* enable interrupts for vblank, frame_end and underflows */
-       tegra_dc_writel(dc, (FRAME_END_INT | V_BLANK_INT | ALL_UF_INT),
-               DC_CMD_INT_ENABLE);
+       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, 0x00000000, DC_DISP_BORDER_COLOR);
@@ -1254,29 +1361,37 @@ static bool _tegra_dc_controller_enable(struct tegra_dc *dc)
        tegra_dc_clk_enable(dc);
 
        /* do not accept interrupts during initialization */
-       tegra_dc_writel(dc, 0, DC_CMD_INT_ENABLE);
        tegra_dc_writel(dc, 0, DC_CMD_INT_MASK);
 
        enable_dc_irq(dc->irq);
 
        failed_init = tegra_dc_init(dc);
        if (failed_init) {
-               _tegra_dc_controller_disable(dc);
+               tegra_dc_writel(dc, 0, DC_CMD_INT_MASK);
+               disable_irq(dc->irq);
+               tegra_dc_clear_bandwidth(dc);
+               tegra_dc_clk_disable(dc);
+               if (dc->out && dc->out->disable)
+                       dc->out->disable();
                return false;
        }
 
        if (dc->out_ops && dc->out_ops->enable)
                dc->out_ops->enable(dc);
 
-       if (dc->out->postpoweron)
-               dc->out->postpoweron();
-
        /* force a full blending update */
        dc->blend.z[0] = -1;
 
        tegra_dc_ext_enable(dc->ext);
 
        trace_printk("%s:enable\n", dc->ndev->name);
+
+       tegra_dc_writel(dc, GENERAL_UPDATE, DC_CMD_STATE_CONTROL);
+       tegra_dc_writel(dc, GENERAL_ACT_REQ, DC_CMD_STATE_CONTROL);
+
+       if (dc->out->postpoweron)
+               dc->out->postpoweron();
+
        return true;
 }
 
@@ -1345,19 +1460,14 @@ static bool _tegra_dc_controller_reset_enable(struct tegra_dc *dc)
 
 static int _tegra_dc_set_default_videomode(struct tegra_dc *dc)
 {
-       return tegra_dc_set_fb_mode(dc, &tegra_dc_hdmi_fallback_mode, 0);
-}
-
-static bool _tegra_dc_enable(struct tegra_dc *dc)
-{
        if (dc->mode.pclk == 0) {
                switch (dc->out->type) {
                case TEGRA_DC_OUT_HDMI:
                /* DC enable called but no videomode is loaded.
                     Check if HDMI is connected, then set fallback mdoe */
                if (tegra_dc_hpd(dc)) {
-                       if (_tegra_dc_set_default_videomode(dc))
-                               return false;
+                       return tegra_dc_set_fb_mode(dc,
+                                       &tegra_dc_hdmi_fallback_mode, 0);
                } else
                        return false;
 
@@ -1373,12 +1483,24 @@ static bool _tegra_dc_enable(struct tegra_dc *dc)
                }
        }
 
+       return false;
+}
+
+static bool _tegra_dc_enable(struct tegra_dc *dc)
+{
+       if (dc->mode.pclk == 0)
+               return false;
+
        if (!dc->out)
                return false;
 
        tegra_dc_io_start(dc);
 
-       return _tegra_dc_controller_enable(dc);
+       if (!_tegra_dc_controller_enable(dc)) {
+               tegra_dc_io_end(dc);
+               return false;
+       }
+       return true;
 }
 
 void tegra_dc_enable(struct tegra_dc *dc)
@@ -1483,8 +1605,20 @@ void tegra_dc_blank(struct tegra_dc *dc)
 
 static void _tegra_dc_disable(struct tegra_dc *dc)
 {
+       if (dc->out->flags & TEGRA_DC_OUT_ONE_SHOT_MODE) {
+               mutex_lock(&dc->one_shot_lock);
+               cancel_delayed_work_sync(&dc->one_shot_work);
+       }
+
+       tegra_dc_hold_dc_out(dc);
+
        _tegra_dc_controller_disable(dc);
        tegra_dc_io_end(dc);
+
+       tegra_dc_release_dc_out(dc);
+
+       if (dc->out->flags & TEGRA_DC_OUT_ONE_SHOT_MODE)
+               mutex_unlock(&dc->one_shot_lock);
 }
 
 void tegra_dc_disable(struct tegra_dc *dc)
@@ -1494,16 +1628,9 @@ void tegra_dc_disable(struct tegra_dc *dc)
        /* it's important that new underflow work isn't scheduled before the
         * lock is acquired. */
        cancel_delayed_work_sync(&dc->underflow_work);
-       if (dc->out->flags & TEGRA_DC_OUT_ONE_SHOT_MODE) {
-               mutex_lock(&dc->one_shot_lock);
-               cancel_delayed_work_sync(&dc->one_shot_work);
-       }
 
        mutex_lock(&dc->lock);
 
-       if (dc->out->flags & TEGRA_DC_OUT_ONE_SHOT_LP_MODE)
-               tegra_dc_host_resume(dc);
-
        if (dc->enabled) {
                dc->enabled = false;
 
@@ -1516,8 +1643,6 @@ void tegra_dc_disable(struct tegra_dc *dc)
 #endif
 
        mutex_unlock(&dc->lock);
-       if (dc->out->flags & TEGRA_DC_OUT_ONE_SHOT_MODE)
-               mutex_unlock(&dc->one_shot_lock);
        print_mode_info(dc, dc->mode);
 }
 
@@ -1583,12 +1708,12 @@ static void tegra_dc_underflow_worker(struct work_struct *work)
                to_delayed_work(work), struct tegra_dc, underflow_work);
 
        mutex_lock(&dc->lock);
-       if (dc->out->flags & TEGRA_DC_OUT_ONE_SHOT_LP_MODE)
-               tegra_dc_host_resume(dc);
+       tegra_dc_hold_dc_out(dc);
 
        if (dc->enabled) {
                tegra_dc_underflow_handler(dc);
        }
+       tegra_dc_release_dc_out(dc);
        mutex_unlock(&dc->lock);
 }
 
@@ -1609,6 +1734,7 @@ static int tegra_dc_probe(struct nvhost_device *ndev,
        struct nvhost_device_id *id_table)
 {
        struct tegra_dc *dc;
+       struct tegra_dc_mode *mode;
        struct clk *clk;
        struct clk *emc_clk;
        struct resource *res;
@@ -1698,6 +1824,7 @@ static int tegra_dc_probe(struct nvhost_device *ndev,
        mutex_init(&dc->one_shot_lock);
        init_completion(&dc->frame_end_complete);
        init_waitqueue_head(&dc->wq);
+       init_waitqueue_head(&dc->timestamp_wq);
 #ifdef CONFIG_ARCH_TEGRA_2x_SOC
        INIT_WORK(&dc->reset_work, tegra_dc_reset_worker);
 #endif
@@ -1749,8 +1876,10 @@ static int tegra_dc_probe(struct nvhost_device *ndev,
        }
 
        mutex_lock(&dc->lock);
-       if (dc->pdata->flags & TEGRA_DC_FLAG_ENABLED)
+       if (dc->pdata->flags & TEGRA_DC_FLAG_ENABLED) {
+               _tegra_dc_set_default_videomode(dc);
                dc->enabled = _tegra_dc_enable(dc);
+       }
        mutex_unlock(&dc->lock);
 
        /* interrupt handler must be registered before tegra_fb_register() */
@@ -1766,7 +1895,7 @@ static int tegra_dc_probe(struct nvhost_device *ndev,
        dev_info(&ndev->dev, "probed\n");
 
        if (dc->pdata->fb) {
-               if (dc->pdata->fb->bits_per_pixel == -1) {
+               if (dc->enabled && dc->pdata->fb->bits_per_pixel == -1) {
                        unsigned long fmt;
                        tegra_dc_writel(dc,
                                        WINDOW_A_SELECT << dc->pdata->fb->win,
@@ -1777,6 +1906,12 @@ static int tegra_dc_probe(struct nvhost_device *ndev,
                                tegra_dc_fmt_bpp(fmt);
                }
 
+               mode = tegra_dc_get_override_mode(dc);
+               if (mode) {
+                       dc->pdata->fb->xres = mode->h_active;
+                       dc->pdata->fb->yres = mode->v_active;
+               }
+
                dc->fb = tegra_fb_register(ndev, dc, dc->pdata->fb, fb_mem);
                if (IS_ERR_OR_NULL(dc->fb))
                        dc->fb = NULL;
@@ -1892,8 +2027,10 @@ static int tegra_dc_resume(struct nvhost_device *ndev)
        mutex_lock(&dc->lock);
        dc->suspended = false;
 
-       if (dc->enabled)
+       if (dc->enabled) {
+               _tegra_dc_set_default_videomode(dc);
                _tegra_dc_enable(dc);
+       }
 
        if (dc->out && dc->out->hotplug_init)
                dc->out->hotplug_init();
@@ -1955,6 +2092,74 @@ struct nvhost_driver tegra_dc_driver = {
        .shutdown = tegra_dc_shutdown,
 };
 
+#ifndef MODULE
+static int __init parse_disp_params(char *options, struct tegra_dc_mode *mode)
+{
+       int i, params[11];
+       char *p;
+
+       for (i = 0; i < ARRAY_SIZE(params); i++) {
+               if ((p = strsep(&options, ",")) != NULL) {
+                       if (*p)
+                               params[i] = simple_strtoul(p, &p, 10);
+               } else
+                       return -EINVAL;
+       }
+
+       if ((mode->pclk = params[0]) == 0)
+               return -EINVAL;
+
+       mode->h_active      = params[1];
+       mode->v_active      = params[2];
+       mode->h_ref_to_sync = params[3];
+       mode->v_ref_to_sync = params[4];
+       mode->h_sync_width  = params[5];
+       mode->v_sync_width  = params[6];
+       mode->h_back_porch  = params[7];
+       mode->v_back_porch  = params[8];
+       mode->h_front_porch = params[9];
+       mode->v_front_porch = params[10];
+
+       return 0;
+}
+
+static int __init tegra_dc_mode_override(char *str)
+{
+       char *p = str, *options;
+
+       if (!p || !*p)
+               return -EINVAL;
+
+       p = strstr(str, "hdmi:");
+       if (p) {
+               p += 5;
+               options = strsep(&p, ";");
+               if (parse_disp_params(options, &override_disp_mode[TEGRA_DC_OUT_HDMI]))
+                       return -EINVAL;
+       }
+
+       p = strstr(str, "rgb:");
+       if (p) {
+               p += 4;
+               options = strsep(&p, ";");
+               if (parse_disp_params(options, &override_disp_mode[TEGRA_DC_OUT_RGB]))
+                       return -EINVAL;
+       }
+
+       p = strstr(str, "dsi:");
+       if (p) {
+               p += 4;
+               options = strsep(&p, ";");
+               if (parse_disp_params(options, &override_disp_mode[TEGRA_DC_OUT_DSI]))
+                       return -EINVAL;
+       }
+
+       return 0;
+}
+
+__setup("disp_params=", tegra_dc_mode_override);
+#endif
+
 static int __init tegra_dc_module_init(void)
 {
        int ret = tegra_dc_ext_module_init();