tegra: dc: Fix div by zero in frame time computation.
[linux-2.6.git] / drivers / video / tegra / dc / dc.c
index c33858a..d01df2f 100644 (file)
@@ -873,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);
@@ -1023,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);
@@ -1045,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);
@@ -1052,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)
@@ -1214,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) {
@@ -1249,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);
@@ -1762,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
@@ -1832,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,