drm/i915: fix pipe source image setting in flip command
[safe/jmp/linux-2.6] / drivers / gpu / drm / i915 / intel_display.c
index 2cc489b..c161ace 100644 (file)
@@ -240,33 +240,86 @@ struct intel_limit {
 #define IRONLAKE_DOT_MAX         350000
 #define IRONLAKE_VCO_MIN         1760000
 #define IRONLAKE_VCO_MAX         3510000
-#define IRONLAKE_N_MIN           1
-#define IRONLAKE_N_MAX           6
-#define IRONLAKE_M_MIN           79
-#define IRONLAKE_M_MAX           127
 #define IRONLAKE_M1_MIN          12
 #define IRONLAKE_M1_MAX          22
 #define IRONLAKE_M2_MIN          5
 #define IRONLAKE_M2_MAX          9
-#define IRONLAKE_P_SDVO_DAC_MIN  5
-#define IRONLAKE_P_SDVO_DAC_MAX  80
-#define IRONLAKE_P_LVDS_MIN      28
-#define IRONLAKE_P_LVDS_MAX      112
-#define IRONLAKE_P1_MIN          1
-#define IRONLAKE_P1_MAX          8
-#define IRONLAKE_P2_SDVO_DAC_SLOW 10
-#define IRONLAKE_P2_SDVO_DAC_FAST 5
-#define IRONLAKE_P2_LVDS_SLOW    14 /* single channel */
-#define IRONLAKE_P2_LVDS_FAST    7  /* double channel */
 #define IRONLAKE_P2_DOT_LIMIT    225000 /* 225Mhz */
 
-#define IRONLAKE_P_DISPLAY_PORT_MIN    10
-#define IRONLAKE_P_DISPLAY_PORT_MAX    20
-#define IRONLAKE_P2_DISPLAY_PORT_FAST  10
-#define IRONLAKE_P2_DISPLAY_PORT_SLOW  10
-#define IRONLAKE_P2_DISPLAY_PORT_LIMIT 0
-#define IRONLAKE_P1_DISPLAY_PORT_MIN   1
-#define IRONLAKE_P1_DISPLAY_PORT_MAX   2
+/* We have parameter ranges for different type of outputs. */
+
+/* DAC & HDMI Refclk 120Mhz */
+#define IRONLAKE_DAC_N_MIN     1
+#define IRONLAKE_DAC_N_MAX     5
+#define IRONLAKE_DAC_M_MIN     79
+#define IRONLAKE_DAC_M_MAX     127
+#define IRONLAKE_DAC_P_MIN     5
+#define IRONLAKE_DAC_P_MAX     80
+#define IRONLAKE_DAC_P1_MIN    1
+#define IRONLAKE_DAC_P1_MAX    8
+#define IRONLAKE_DAC_P2_SLOW   10
+#define IRONLAKE_DAC_P2_FAST   5
+
+/* LVDS single-channel 120Mhz refclk */
+#define IRONLAKE_LVDS_S_N_MIN  1
+#define IRONLAKE_LVDS_S_N_MAX  3
+#define IRONLAKE_LVDS_S_M_MIN  79
+#define IRONLAKE_LVDS_S_M_MAX  118
+#define IRONLAKE_LVDS_S_P_MIN  28
+#define IRONLAKE_LVDS_S_P_MAX  112
+#define IRONLAKE_LVDS_S_P1_MIN 2
+#define IRONLAKE_LVDS_S_P1_MAX 8
+#define IRONLAKE_LVDS_S_P2_SLOW        14
+#define IRONLAKE_LVDS_S_P2_FAST        14
+
+/* LVDS dual-channel 120Mhz refclk */
+#define IRONLAKE_LVDS_D_N_MIN  1
+#define IRONLAKE_LVDS_D_N_MAX  3
+#define IRONLAKE_LVDS_D_M_MIN  79
+#define IRONLAKE_LVDS_D_M_MAX  127
+#define IRONLAKE_LVDS_D_P_MIN  14
+#define IRONLAKE_LVDS_D_P_MAX  56
+#define IRONLAKE_LVDS_D_P1_MIN 2
+#define IRONLAKE_LVDS_D_P1_MAX 8
+#define IRONLAKE_LVDS_D_P2_SLOW        7
+#define IRONLAKE_LVDS_D_P2_FAST        7
+
+/* LVDS single-channel 100Mhz refclk */
+#define IRONLAKE_LVDS_S_SSC_N_MIN      1
+#define IRONLAKE_LVDS_S_SSC_N_MAX      2
+#define IRONLAKE_LVDS_S_SSC_M_MIN      79
+#define IRONLAKE_LVDS_S_SSC_M_MAX      126
+#define IRONLAKE_LVDS_S_SSC_P_MIN      28
+#define IRONLAKE_LVDS_S_SSC_P_MAX      112
+#define IRONLAKE_LVDS_S_SSC_P1_MIN     2
+#define IRONLAKE_LVDS_S_SSC_P1_MAX     8
+#define IRONLAKE_LVDS_S_SSC_P2_SLOW    14
+#define IRONLAKE_LVDS_S_SSC_P2_FAST    14
+
+/* LVDS dual-channel 100Mhz refclk */
+#define IRONLAKE_LVDS_D_SSC_N_MIN      1
+#define IRONLAKE_LVDS_D_SSC_N_MAX      3
+#define IRONLAKE_LVDS_D_SSC_M_MIN      79
+#define IRONLAKE_LVDS_D_SSC_M_MAX      126
+#define IRONLAKE_LVDS_D_SSC_P_MIN      14
+#define IRONLAKE_LVDS_D_SSC_P_MAX      42
+#define IRONLAKE_LVDS_D_SSC_P1_MIN     2
+#define IRONLAKE_LVDS_D_SSC_P1_MAX     6
+#define IRONLAKE_LVDS_D_SSC_P2_SLOW    7
+#define IRONLAKE_LVDS_D_SSC_P2_FAST    7
+
+/* DisplayPort */
+#define IRONLAKE_DP_N_MIN              1
+#define IRONLAKE_DP_N_MAX              2
+#define IRONLAKE_DP_M_MIN              81
+#define IRONLAKE_DP_M_MAX              90
+#define IRONLAKE_DP_P_MIN              10
+#define IRONLAKE_DP_P_MAX              20
+#define IRONLAKE_DP_P2_FAST            10
+#define IRONLAKE_DP_P2_SLOW            10
+#define IRONLAKE_DP_P2_LIMIT           0
+#define IRONLAKE_DP_P1_MIN             1
+#define IRONLAKE_DP_P1_MAX             2
 
 static bool
 intel_find_best_PLL(const intel_limit_t *limit, struct drm_crtc *crtc,
@@ -474,33 +527,78 @@ static const intel_limit_t intel_limits_pineview_lvds = {
        .find_pll = intel_find_best_PLL,
 };
 
-static const intel_limit_t intel_limits_ironlake_sdvo = {
+static const intel_limit_t intel_limits_ironlake_dac = {
        .dot = { .min = IRONLAKE_DOT_MIN,          .max = IRONLAKE_DOT_MAX },
        .vco = { .min = IRONLAKE_VCO_MIN,          .max = IRONLAKE_VCO_MAX },
-       .n   = { .min = IRONLAKE_N_MIN,            .max = IRONLAKE_N_MAX },
-       .m   = { .min = IRONLAKE_M_MIN,            .max = IRONLAKE_M_MAX },
+       .n   = { .min = IRONLAKE_DAC_N_MIN,        .max = IRONLAKE_DAC_N_MAX },
+       .m   = { .min = IRONLAKE_DAC_M_MIN,        .max = IRONLAKE_DAC_M_MAX },
        .m1  = { .min = IRONLAKE_M1_MIN,           .max = IRONLAKE_M1_MAX },
        .m2  = { .min = IRONLAKE_M2_MIN,           .max = IRONLAKE_M2_MAX },
-       .p   = { .min = IRONLAKE_P_SDVO_DAC_MIN,   .max = IRONLAKE_P_SDVO_DAC_MAX },
-       .p1  = { .min = IRONLAKE_P1_MIN,           .max = IRONLAKE_P1_MAX },
+       .p   = { .min = IRONLAKE_DAC_P_MIN,        .max = IRONLAKE_DAC_P_MAX },
+       .p1  = { .min = IRONLAKE_DAC_P1_MIN,       .max = IRONLAKE_DAC_P1_MAX },
        .p2  = { .dot_limit = IRONLAKE_P2_DOT_LIMIT,
-                .p2_slow = IRONLAKE_P2_SDVO_DAC_SLOW,
-                .p2_fast = IRONLAKE_P2_SDVO_DAC_FAST },
+                .p2_slow = IRONLAKE_DAC_P2_SLOW,
+                .p2_fast = IRONLAKE_DAC_P2_FAST },
        .find_pll = intel_g4x_find_best_PLL,
 };
 
-static const intel_limit_t intel_limits_ironlake_lvds = {
+static const intel_limit_t intel_limits_ironlake_single_lvds = {
        .dot = { .min = IRONLAKE_DOT_MIN,          .max = IRONLAKE_DOT_MAX },
        .vco = { .min = IRONLAKE_VCO_MIN,          .max = IRONLAKE_VCO_MAX },
-       .n   = { .min = IRONLAKE_N_MIN,            .max = IRONLAKE_N_MAX },
-       .m   = { .min = IRONLAKE_M_MIN,            .max = IRONLAKE_M_MAX },
+       .n   = { .min = IRONLAKE_LVDS_S_N_MIN,     .max = IRONLAKE_LVDS_S_N_MAX },
+       .m   = { .min = IRONLAKE_LVDS_S_M_MIN,     .max = IRONLAKE_LVDS_S_M_MAX },
        .m1  = { .min = IRONLAKE_M1_MIN,           .max = IRONLAKE_M1_MAX },
        .m2  = { .min = IRONLAKE_M2_MIN,           .max = IRONLAKE_M2_MAX },
-       .p   = { .min = IRONLAKE_P_LVDS_MIN,       .max = IRONLAKE_P_LVDS_MAX },
-       .p1  = { .min = IRONLAKE_P1_MIN,           .max = IRONLAKE_P1_MAX },
+       .p   = { .min = IRONLAKE_LVDS_S_P_MIN,     .max = IRONLAKE_LVDS_S_P_MAX },
+       .p1  = { .min = IRONLAKE_LVDS_S_P1_MIN,    .max = IRONLAKE_LVDS_S_P1_MAX },
        .p2  = { .dot_limit = IRONLAKE_P2_DOT_LIMIT,
-                .p2_slow = IRONLAKE_P2_LVDS_SLOW,
-                .p2_fast = IRONLAKE_P2_LVDS_FAST },
+                .p2_slow = IRONLAKE_LVDS_S_P2_SLOW,
+                .p2_fast = IRONLAKE_LVDS_S_P2_FAST },
+       .find_pll = intel_g4x_find_best_PLL,
+};
+
+static const intel_limit_t intel_limits_ironlake_dual_lvds = {
+       .dot = { .min = IRONLAKE_DOT_MIN,          .max = IRONLAKE_DOT_MAX },
+       .vco = { .min = IRONLAKE_VCO_MIN,          .max = IRONLAKE_VCO_MAX },
+       .n   = { .min = IRONLAKE_LVDS_D_N_MIN,     .max = IRONLAKE_LVDS_D_N_MAX },
+       .m   = { .min = IRONLAKE_LVDS_D_M_MIN,     .max = IRONLAKE_LVDS_D_M_MAX },
+       .m1  = { .min = IRONLAKE_M1_MIN,           .max = IRONLAKE_M1_MAX },
+       .m2  = { .min = IRONLAKE_M2_MIN,           .max = IRONLAKE_M2_MAX },
+       .p   = { .min = IRONLAKE_LVDS_D_P_MIN,     .max = IRONLAKE_LVDS_D_P_MAX },
+       .p1  = { .min = IRONLAKE_LVDS_D_P1_MIN,    .max = IRONLAKE_LVDS_D_P1_MAX },
+       .p2  = { .dot_limit = IRONLAKE_P2_DOT_LIMIT,
+                .p2_slow = IRONLAKE_LVDS_D_P2_SLOW,
+                .p2_fast = IRONLAKE_LVDS_D_P2_FAST },
+       .find_pll = intel_g4x_find_best_PLL,
+};
+
+static const intel_limit_t intel_limits_ironlake_single_lvds_100m = {
+       .dot = { .min = IRONLAKE_DOT_MIN,          .max = IRONLAKE_DOT_MAX },
+       .vco = { .min = IRONLAKE_VCO_MIN,          .max = IRONLAKE_VCO_MAX },
+       .n   = { .min = IRONLAKE_LVDS_S_SSC_N_MIN, .max = IRONLAKE_LVDS_S_SSC_N_MAX },
+       .m   = { .min = IRONLAKE_LVDS_S_SSC_M_MIN, .max = IRONLAKE_LVDS_S_SSC_M_MAX },
+       .m1  = { .min = IRONLAKE_M1_MIN,           .max = IRONLAKE_M1_MAX },
+       .m2  = { .min = IRONLAKE_M2_MIN,           .max = IRONLAKE_M2_MAX },
+       .p   = { .min = IRONLAKE_LVDS_S_SSC_P_MIN, .max = IRONLAKE_LVDS_S_SSC_P_MAX },
+       .p1  = { .min = IRONLAKE_LVDS_S_SSC_P1_MIN,.max = IRONLAKE_LVDS_S_SSC_P1_MAX },
+       .p2  = { .dot_limit = IRONLAKE_P2_DOT_LIMIT,
+                .p2_slow = IRONLAKE_LVDS_S_SSC_P2_SLOW,
+                .p2_fast = IRONLAKE_LVDS_S_SSC_P2_FAST },
+       .find_pll = intel_g4x_find_best_PLL,
+};
+
+static const intel_limit_t intel_limits_ironlake_dual_lvds_100m = {
+       .dot = { .min = IRONLAKE_DOT_MIN,          .max = IRONLAKE_DOT_MAX },
+       .vco = { .min = IRONLAKE_VCO_MIN,          .max = IRONLAKE_VCO_MAX },
+       .n   = { .min = IRONLAKE_LVDS_D_SSC_N_MIN, .max = IRONLAKE_LVDS_D_SSC_N_MAX },
+       .m   = { .min = IRONLAKE_LVDS_D_SSC_M_MIN, .max = IRONLAKE_LVDS_D_SSC_M_MAX },
+       .m1  = { .min = IRONLAKE_M1_MIN,           .max = IRONLAKE_M1_MAX },
+       .m2  = { .min = IRONLAKE_M2_MIN,           .max = IRONLAKE_M2_MAX },
+       .p   = { .min = IRONLAKE_LVDS_D_SSC_P_MIN, .max = IRONLAKE_LVDS_D_SSC_P_MAX },
+       .p1  = { .min = IRONLAKE_LVDS_D_SSC_P1_MIN,.max = IRONLAKE_LVDS_D_SSC_P1_MAX },
+       .p2  = { .dot_limit = IRONLAKE_P2_DOT_LIMIT,
+                .p2_slow = IRONLAKE_LVDS_D_SSC_P2_SLOW,
+                .p2_fast = IRONLAKE_LVDS_D_SSC_P2_FAST },
        .find_pll = intel_g4x_find_best_PLL,
 };
 
@@ -509,34 +607,53 @@ static const intel_limit_t intel_limits_ironlake_display_port = {
                  .max = IRONLAKE_DOT_MAX },
         .vco = { .min = IRONLAKE_VCO_MIN,
                  .max = IRONLAKE_VCO_MAX},
-        .n   = { .min = IRONLAKE_N_MIN,
-                 .max = IRONLAKE_N_MAX },
-        .m   = { .min = IRONLAKE_M_MIN,
-                 .max = IRONLAKE_M_MAX },
+        .n   = { .min = IRONLAKE_DP_N_MIN,
+                 .max = IRONLAKE_DP_N_MAX },
+        .m   = { .min = IRONLAKE_DP_M_MIN,
+                 .max = IRONLAKE_DP_M_MAX },
         .m1  = { .min = IRONLAKE_M1_MIN,
                  .max = IRONLAKE_M1_MAX },
         .m2  = { .min = IRONLAKE_M2_MIN,
                  .max = IRONLAKE_M2_MAX },
-        .p   = { .min = IRONLAKE_P_DISPLAY_PORT_MIN,
-                 .max = IRONLAKE_P_DISPLAY_PORT_MAX },
-        .p1  = { .min = IRONLAKE_P1_DISPLAY_PORT_MIN,
-                 .max = IRONLAKE_P1_DISPLAY_PORT_MAX},
-        .p2  = { .dot_limit = IRONLAKE_P2_DISPLAY_PORT_LIMIT,
-                 .p2_slow = IRONLAKE_P2_DISPLAY_PORT_SLOW,
-                 .p2_fast = IRONLAKE_P2_DISPLAY_PORT_FAST },
+        .p   = { .min = IRONLAKE_DP_P_MIN,
+                 .max = IRONLAKE_DP_P_MAX },
+        .p1  = { .min = IRONLAKE_DP_P1_MIN,
+                 .max = IRONLAKE_DP_P1_MAX},
+        .p2  = { .dot_limit = IRONLAKE_DP_P2_LIMIT,
+                 .p2_slow = IRONLAKE_DP_P2_SLOW,
+                 .p2_fast = IRONLAKE_DP_P2_FAST },
         .find_pll = intel_find_pll_ironlake_dp,
 };
 
 static const intel_limit_t *intel_ironlake_limit(struct drm_crtc *crtc)
 {
+       struct drm_device *dev = crtc->dev;
+       struct drm_i915_private *dev_priv = dev->dev_private;
        const intel_limit_t *limit;
-       if (intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS))
-               limit = &intel_limits_ironlake_lvds;
-       else if (intel_pipe_has_type(crtc, INTEL_OUTPUT_DISPLAYPORT) ||
+       int refclk = 120;
+
+       if (intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS)) {
+               if (dev_priv->lvds_use_ssc && dev_priv->lvds_ssc_freq == 100)
+                       refclk = 100;
+
+               if ((I915_READ(PCH_LVDS) & LVDS_CLKB_POWER_MASK) ==
+                   LVDS_CLKB_POWER_UP) {
+                       /* LVDS dual channel */
+                       if (refclk == 100)
+                               limit = &intel_limits_ironlake_dual_lvds_100m;
+                       else
+                               limit = &intel_limits_ironlake_dual_lvds;
+               } else {
+                       if (refclk == 100)
+                               limit = &intel_limits_ironlake_single_lvds_100m;
+                       else
+                               limit = &intel_limits_ironlake_single_lvds;
+               }
+       } else if (intel_pipe_has_type(crtc, INTEL_OUTPUT_DISPLAYPORT) ||
                        HAS_eDP)
                limit = &intel_limits_ironlake_display_port;
        else
-               limit = &intel_limits_ironlake_sdvo;
+               limit = &intel_limits_ironlake_dac;
 
        return limit;
 }
@@ -914,6 +1031,8 @@ static void i8xx_enable_fbc(struct drm_crtc *crtc, unsigned long interval)
 
        /* enable it... */
        fbc_ctl = FBC_CTL_EN | FBC_CTL_PERIODIC;
+       if (IS_I945GM(dev))
+               fbc_ctl |= FBC_C3_IDLE; /* 945 needs special SR handling */
        fbc_ctl |= (dev_priv->cfb_pitch & 0xff) << FBC_CTL_STRIDE_SHIFT;
        fbc_ctl |= (interval & 0x2fff) << FBC_CTL_INTERVAL_SHIFT;
        if (obj_priv->tiling_mode != I915_TILING_NONE)
@@ -2520,6 +2639,10 @@ static void g4x_update_wm(struct drm_device *dev,  int planea_clock,
                sr_entries = roundup(sr_entries / cacheline_size, 1);
                DRM_DEBUG("self-refresh entries: %d\n", sr_entries);
                I915_WRITE(FW_BLC_SELF, FW_BLC_SELF_EN);
+       } else {
+               /* Turn off self refresh if both pipes are enabled */
+               I915_WRITE(FW_BLC_SELF, I915_READ(FW_BLC_SELF)
+                                       & ~FW_BLC_SELF_EN);
        }
 
        DRM_DEBUG("Setting FIFO watermarks - A: %d, B: %d, SR %d\n",
@@ -2563,6 +2686,10 @@ static void i965_update_wm(struct drm_device *dev, int planea_clock,
                        srwm = 1;
                srwm &= 0x3f;
                I915_WRITE(FW_BLC_SELF, FW_BLC_SELF_EN);
+       } else {
+               /* Turn off self refresh if both pipes are enabled */
+               I915_WRITE(FW_BLC_SELF, I915_READ(FW_BLC_SELF)
+                                       & ~FW_BLC_SELF_EN);
        }
 
        DRM_DEBUG_KMS("Setting FIFO watermarks - A: 8, B: 8, C: 8, SR %d\n",
@@ -2631,6 +2758,10 @@ static void i9xx_update_wm(struct drm_device *dev, int planea_clock,
                if (srwm < 0)
                        srwm = 1;
                I915_WRITE(FW_BLC_SELF, FW_BLC_SELF_EN | (srwm & 0x3f));
+       } else {
+               /* Turn off self refresh if both pipes are enabled */
+               I915_WRITE(FW_BLC_SELF, I915_READ(FW_BLC_SELF)
+                                       & ~FW_BLC_SELF_EN);
        }
 
        DRM_DEBUG_KMS("Setting FIFO watermarks - A: %d, B: %d, C: %d, SR %d\n",
@@ -3950,7 +4081,8 @@ static void intel_crtc_destroy(struct drm_crtc *crtc)
 struct intel_unpin_work {
        struct work_struct work;
        struct drm_device *dev;
-       struct drm_gem_object *obj;
+       struct drm_gem_object *old_fb_obj;
+       struct drm_gem_object *pending_flip_obj;
        struct drm_pending_vblank_event *event;
        int pending;
 };
@@ -3961,8 +4093,8 @@ static void intel_unpin_work_fn(struct work_struct *__work)
                container_of(__work, struct intel_unpin_work, work);
 
        mutex_lock(&work->dev->struct_mutex);
-       i915_gem_object_unpin(work->obj);
-       drm_gem_object_unreference(work->obj);
+       i915_gem_object_unpin(work->old_fb_obj);
+       drm_gem_object_unreference(work->old_fb_obj);
        mutex_unlock(&work->dev->struct_mutex);
        kfree(work);
 }
@@ -3985,6 +4117,12 @@ void intel_finish_page_flip(struct drm_device *dev, int pipe)
        spin_lock_irqsave(&dev->event_lock, flags);
        work = intel_crtc->unpin_work;
        if (work == NULL || !work->pending) {
+               if (work && !work->pending) {
+                       obj_priv = work->pending_flip_obj->driver_private;
+                       DRM_DEBUG_DRIVER("flip finish: %p (%d) not pending?\n",
+                                        obj_priv,
+                                        atomic_read(&obj_priv->pending_flip));
+               }
                spin_unlock_irqrestore(&dev->event_lock, flags);
                return;
        }
@@ -4005,8 +4143,11 @@ void intel_finish_page_flip(struct drm_device *dev, int pipe)
 
        spin_unlock_irqrestore(&dev->event_lock, flags);
 
-       obj_priv = work->obj->driver_private;
-       if (atomic_dec_and_test(&obj_priv->pending_flip))
+       obj_priv = work->pending_flip_obj->driver_private;
+
+       /* Initial scanout buffer will have a 0 pending flip count */
+       if ((atomic_read(&obj_priv->pending_flip) == 0) ||
+           atomic_dec_and_test(&obj_priv->pending_flip))
                DRM_WAKEUP(&dev_priv->pending_flip_queue);
        schedule_work(&work->work);
 }
@@ -4019,8 +4160,11 @@ void intel_prepare_page_flip(struct drm_device *dev, int plane)
        unsigned long flags;
 
        spin_lock_irqsave(&dev->event_lock, flags);
-       if (intel_crtc->unpin_work)
+       if (intel_crtc->unpin_work) {
                intel_crtc->unpin_work->pending = 1;
+       } else {
+               DRM_DEBUG_DRIVER("preparing flip with no unpin work?\n");
+       }
        spin_unlock_irqrestore(&dev->event_lock, flags);
 }
 
@@ -4036,7 +4180,8 @@ static int intel_crtc_page_flip(struct drm_crtc *crtc,
        struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
        struct intel_unpin_work *work;
        unsigned long flags;
-       int ret;
+       int pipesrc_reg = (intel_crtc->pipe == 0) ? PIPEASRC : PIPEBSRC;
+       int ret, pipesrc;
        RING_LOCALS;
 
        work = kzalloc(sizeof *work, GFP_KERNEL);
@@ -4048,12 +4193,13 @@ static int intel_crtc_page_flip(struct drm_crtc *crtc,
        work->event = event;
        work->dev = crtc->dev;
        intel_fb = to_intel_framebuffer(crtc->fb);
-       work->obj = intel_fb->obj;
+       work->old_fb_obj = intel_fb->obj;
        INIT_WORK(&work->work, intel_unpin_work_fn);
 
        /* We borrow the event spin lock for protecting unpin_work */
        spin_lock_irqsave(&dev->event_lock, flags);
        if (intel_crtc->unpin_work) {
+               DRM_DEBUG_DRIVER("flip queue: crtc already busy\n");
                spin_unlock_irqrestore(&dev->event_lock, flags);
                kfree(work);
                mutex_unlock(&dev->struct_mutex);
@@ -4067,19 +4213,23 @@ static int intel_crtc_page_flip(struct drm_crtc *crtc,
 
        ret = intel_pin_and_fence_fb_obj(dev, obj);
        if (ret != 0) {
+               DRM_DEBUG_DRIVER("flip queue: %p pin & fence failed\n",
+                         obj->driver_private);
                kfree(work);
+               intel_crtc->unpin_work = NULL;
                mutex_unlock(&dev->struct_mutex);
                return ret;
        }
 
        /* Reference the old fb object for the scheduled work. */
-       drm_gem_object_reference(work->obj);
+       drm_gem_object_reference(work->old_fb_obj);
 
        crtc->fb = fb;
        i915_gem_object_flush_write_domain(obj);
        drm_vblank_get(dev, intel_crtc->pipe);
        obj_priv = obj->driver_private;
        atomic_inc(&obj_priv->pending_flip);
+       work->pending_flip_obj = obj;
 
        BEGIN_LP_RING(4);
        OUT_RING(MI_DISPLAY_FLIP |
@@ -4087,7 +4237,8 @@ static int intel_crtc_page_flip(struct drm_crtc *crtc,
        OUT_RING(fb->pitch);
        if (IS_I965G(dev)) {
                OUT_RING(obj_priv->gtt_offset | obj_priv->tiling_mode);
-               OUT_RING((fb->width << 16) | fb->height);
+               pipesrc = I915_READ(pipesrc_reg); 
+               OUT_RING(pipesrc & 0x0fff0fff);
        } else {
                OUT_RING(obj_priv->gtt_offset);
                OUT_RING(MI_NOOP);