From: Ville Syrj?l? <ville.syrj...@linux.intel.com>

Use the drm_flip helper to implement atomic page flipping.

Work in progress. Ignore the huge mess in intel_sprite.c for now.
---
 drivers/gpu/drm/i915/i915_drv.h      |    4 +
 drivers/gpu/drm/i915/i915_irq.c      |   10 +-
 drivers/gpu/drm/i915/intel_atomic.c  |  449 +++++++++++++++++++++++++++++++++-
 drivers/gpu/drm/i915/intel_display.c |  171 +++++++++----
 drivers/gpu/drm/i915/intel_drv.h     |   29 +++
 drivers/gpu/drm/i915/intel_sprite.c  |  386 ++++++++++++++++++-----------
 6 files changed, 841 insertions(+), 208 deletions(-)

diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h
index 57e4894..e29994f 100644
--- a/drivers/gpu/drm/i915/i915_drv.h
+++ b/drivers/gpu/drm/i915/i915_drv.h
@@ -41,6 +41,8 @@
 #include <linux/intel-iommu.h>
 #include <linux/kref.h>

+#include "drm_flip.h"
+
 /* General customization:
  */

@@ -845,6 +847,8 @@ typedef struct drm_i915_private {
        struct work_struct parity_error_work;
        bool hw_contexts_disabled;
        uint32_t hw_context_size;
+
+       struct drm_flip_driver flip_driver;
 } drm_i915_private_t;

 /* Iterate over initialised rings */
diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c
index 23f2ea0..78ff3e0 100644
--- a/drivers/gpu/drm/i915/i915_irq.c
+++ b/drivers/gpu/drm/i915/i915_irq.c
@@ -37,6 +37,8 @@
 #include "i915_trace.h"
 #include "intel_drv.h"

+void intel_handle_vblank(struct drm_device *dev, int pipe);
+
 /* For display hotplug interrupt */
 static void
 ironlake_enable_display_irq(drm_i915_private_t *dev_priv, u32 mask)
@@ -548,7 +550,7 @@ static irqreturn_t valleyview_irq_handler(DRM_IRQ_ARGS)

                for_each_pipe(pipe) {
                        if (pipe_stats[pipe] & PIPE_VBLANK_INTERRUPT_STATUS)
-                               drm_handle_vblank(dev, pipe);
+                               intel_handle_vblank(dev, pipe);

                        if (pipe_stats[pipe] & PLANE_FLIPDONE_INT_STATUS_VLV) {
                                intel_prepare_page_flip(dev, pipe);
@@ -686,7 +688,7 @@ static irqreturn_t ivybridge_irq_handler(DRM_IRQ_ARGS)
                                intel_finish_page_flip_plane(dev, i);
                        }
                        if (de_iir & (DE_PIPEA_VBLANK_IVB << (5 * i)))
-                               drm_handle_vblank(dev, i);
+                               intel_handle_vblank(dev, i);
                }

                /* check event from PCH */
@@ -779,10 +781,10 @@ static irqreturn_t ironlake_irq_handler(DRM_IRQ_ARGS)
        }

        if (de_iir & DE_PIPEA_VBLANK)
-               drm_handle_vblank(dev, 0);
+               intel_handle_vblank(dev, 0);

        if (de_iir & DE_PIPEB_VBLANK)
-               drm_handle_vblank(dev, 1);
+               intel_handle_vblank(dev, 1);

        /* check event from PCH */
        if (de_iir & DE_PCH_EVENT) {
diff --git a/drivers/gpu/drm/i915/intel_atomic.c 
b/drivers/gpu/drm/i915/intel_atomic.c
index 0a96d15..6d8d7d3 100644
--- a/drivers/gpu/drm/i915/intel_atomic.c
+++ b/drivers/gpu/drm/i915/intel_atomic.c
@@ -3,6 +3,7 @@

 #include <drm/drmP.h>
 #include <drm/drm_crtc.h>
+#include <drm/drm_flip.h>

 #include "intel_drv.h"

@@ -1309,6 +1310,46 @@ static void update_props(const struct drm_device *dev,
        }
 }

+static int atomic_pipe_commit(struct drm_device *dev,
+                             struct intel_atomic_state *state,
+                             int pipe);
+
+static int try_atomic_commit(struct drm_device *dev, struct intel_atomic_state 
*s)
+{
+       int ret;
+       int i;
+       u32 pipes = 0;
+
+       for (i = 0; i < dev->mode_config.num_crtc; i++) {
+               struct intel_crtc_state *st = &s->crtc[i];
+
+               if (st->mode_dirty)
+                       return -EAGAIN;
+
+               if (st->fb_dirty)
+                       pipes |= 1 << to_intel_crtc(st->crtc)->pipe;
+       }
+
+       for (i = 0; i < dev->mode_config.num_plane; i++) {
+               struct intel_plane_state *st = &s->plane[i];
+
+               if (st->dirty)
+                       pipes |= 1 << to_intel_plane(st->plane)->pipe;
+       }
+
+       if (hweight32(pipes) != 1)
+               return -EAGAIN;
+
+       ret = atomic_pipe_commit(dev, s, ffs(pipes) - 1);
+       if (ret)
+               return ret;
+
+       /* don't restore the old state in end() */
+       s->dirty = false;
+
+       return 0;
+}
+
 static int intel_atomic_commit(struct drm_device *dev, void *state)
 {
        struct intel_atomic_state *s = state;
@@ -1325,17 +1366,23 @@ static int intel_atomic_commit(struct drm_device *dev, 
void *state)
        if (ret)
                return ret;

-       ret = apply_config(dev, s);
+       /* try to apply asynchronously and atomically */
+       ret = try_atomic_commit(dev, s);
+
+       /* fall back to sync/non-atomic */
        if (ret) {
-               unpin_fbs(dev, s);
-               unpin_cursors(dev, s);
-               s->restore_hw = true;
-               return ret;
-       }
+               ret = apply_config(dev, s);
+               if (ret) {
+                       unpin_fbs(dev, s);
+                       unpin_cursors(dev, s);
+                       s->restore_hw = true;
+                       return ret;
+               }

-       unpin_old_fbs(dev, s);
+               unpin_old_fbs(dev, s);

-       unpin_old_cursors(dev, s);
+               unpin_old_cursors(dev, s);
+       }

        update_plane_obj(dev, s);

@@ -1390,6 +1437,9 @@ static struct {
        { &prop_cursor_y, "CURSOR_Y", INT_MIN, INT_MAX },
 };

+static void intel_flip_init(struct drm_device *dev);
+static void intel_flip_fini(struct drm_device *dev);
+
 int intel_atomic_init(struct drm_device *dev)
 {
        struct drm_crtc *crtc;
@@ -1452,6 +1502,8 @@ int intel_atomic_init(struct drm_device *dev)

        unpin_workqueue = create_workqueue("i915_fb_unpin");

+       intel_flip_init(dev);
+
        return 0;

  out:
@@ -1465,6 +1517,8 @@ void intel_atomic_fini(struct drm_device *dev)
 {
        struct drm_crtc *crtc;

+       intel_flip_fini(dev);
+
        destroy_workqueue(unpin_workqueue);

        list_for_each_entry(crtc, &dev->mode_config.crtc_list, head)
@@ -1485,3 +1539,382 @@ void intel_atomic_fini(struct drm_device *dev)
        drm_property_destroy(dev, prop_src_y);
        drm_property_destroy(dev, prop_src_x);
 }
+
+void intel_plane_calc(struct drm_crtc *crtc, struct drm_framebuffer *fb, int 
x, int y);
+void intel_plane_prepare(struct drm_crtc *crtc);
+void intel_plane_commit(struct drm_crtc *crtc);
+void intel_sprite_calc(struct drm_plane *plane, struct drm_framebuffer *fb, 
const struct intel_plane_coords *coords);
+void intel_sprite_prepare(struct drm_plane *plane);
+void intel_sprite_commit(struct drm_plane *plane);
+
+enum {
+       /* somwehat arbitrary value */
+       INTEL_VBL_CNT_TIMEOUT = 5,
+};
+
+struct intel_flip
+{
+       struct drm_flip base;
+       u32 vbl_count;
+       bool vblank_ref;
+       struct drm_crtc *crtc;
+       struct drm_plane *plane;
+       struct drm_i915_gem_object *old_bo;
+};
+
+static void intel_flip_complete(struct drm_flip *flip)
+{
+       struct intel_flip *intel_flip =
+               container_of(flip, struct intel_flip, base);
+       struct drm_device *dev = intel_flip->crtc->dev;
+       int pipe = to_intel_crtc(intel_flip->crtc)->pipe;
+
+       // send flip event
+
+       if (intel_flip->vblank_ref)
+               drm_vblank_put(dev, pipe);
+
+       // allow GPU to render to old_obj
+}
+
+static void intel_flip_finish(struct drm_flip *flip)
+{
+       struct intel_flip *intel_flip =
+               container_of(flip, struct intel_flip, base);
+       struct drm_device *dev = intel_flip->crtc->dev;
+
+       if (intel_flip->old_bo) {
+               mutex_lock(&dev->struct_mutex);
+
+               intel_finish_fb(intel_flip->old_bo);
+               intel_unpin_fb_obj(intel_flip->old_bo);
+
+               mutex_unlock(&dev->struct_mutex);
+       }
+
+}
+
+static void intel_flip_cleanup(struct drm_flip *flip)
+{
+       struct intel_flip *intel_flip =
+               container_of(flip, struct intel_flip, base);
+
+       kfree(intel_flip);
+}
+
+static void intel_flip_driver_flush(struct drm_flip_driver *driver)
+{
+       struct drm_i915_private *dev_priv =
+               container_of(driver, struct drm_i915_private, flip_driver);
+
+       /* Flush posted writes */
+       I915_READ(PIPESTAT(PIPE_A));
+}
+
+static u32 get_vbl_count(struct drm_crtc *crtc)
+{
+       struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
+       struct drm_i915_private *dev_priv = crtc->dev->dev_private;
+       int pipe = intel_crtc->pipe;
+       u32 high, low1, low2, dsl;
+       unsigned int timeout = 0;
+
+       /*
+        * FIXME check where the frame counter increments, and if
+        * it happens in the middle of some line, take appropriate
+        * measures to get a sensible reading.
+        */
+
+       /* All reads must be satisfied during the same frame */
+       do {
+               low1 = I915_READ(PIPEFRAMEPIXEL(pipe)) >> PIPE_FRAME_LOW_SHIFT;
+               high = I915_READ(PIPEFRAME(pipe)) << 8;
+               dsl = I915_READ(PIPEDSL(pipe));
+               low2 = I915_READ(PIPEFRAMEPIXEL(pipe)) >> PIPE_FRAME_LOW_SHIFT;
+       } while (low1 != low2 && timeout++ < INTEL_VBL_CNT_TIMEOUT);
+
+       if (timeout >= INTEL_VBL_CNT_TIMEOUT)
+               dev_warn(crtc->dev->dev,
+                        "Timed out while determining VBL count for pipe %d\n",
+                        intel_crtc->pipe);
+
+       return ((high | low2) +
+               ((dsl >= crtc->hwmode.crtc_vdisplay) &&
+                (dsl < crtc->hwmode.crtc_vtotal - 1))) & 0xffffff;
+}
+
+static unsigned int usecs_to_scanlines(struct drm_crtc *crtc,
+                                      unsigned int usecs)
+{
+       /* paranoia */
+       if (!crtc->hwmode.crtc_htotal)
+               return 1;
+
+       return DIV_ROUND_UP(usecs * crtc->hwmode.clock,
+                           1000 * crtc->hwmode.crtc_htotal);
+}
+
+static void intel_pipe_vblank_evade(struct drm_crtc *crtc)
+{
+       struct drm_device *dev = crtc->dev;
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
+       int pipe = intel_crtc->pipe;
+       /* FIXME needs to be calibrated sensibly */
+       u32 min = crtc->hwmode.crtc_vdisplay - usecs_to_scanlines(crtc, 60);
+       u32 max = crtc->hwmode.crtc_vdisplay - 1;
+       long timeout = msecs_to_jiffies(1);
+       u32 val;
+
+       bool vblank_ref = drm_vblank_get(dev, pipe) == 0;
+
+       intel_crtc->vbl_received = false;
+
+       val = I915_READ(PIPEDSL(pipe));
+
+       while (val >= min && val <= max && timeout > 0) {
+               local_irq_enable();
+
+               timeout = wait_event_timeout(intel_crtc->vbl_wait,
+                                            intel_crtc->vbl_received,
+                                            timeout);
+
+               local_irq_disable();
+
+               intel_crtc->vbl_received = false;
+
+               val = I915_READ(PIPEDSL(pipe));
+       }
+
+       if (vblank_ref)
+               drm_vblank_put(dev, pipe);
+
+       if (val >= min && val <= max)
+               dev_warn(dev->dev,
+                        "Page flipping close to vblank start (DSL=%u, 
VBL=%u)\n",
+                        val, crtc->hwmode.crtc_vdisplay);
+}
+
+static bool vbl_count_after_eq(u32 a, u32 b)
+{
+       return !((a - b) & 0x800000);
+}
+
+static bool intel_vbl_check(struct drm_flip *pending_flip, u32 vbl_count)
+{
+       struct intel_flip *old_intel_flip =
+               container_of(pending_flip, struct intel_flip, base);
+
+       return vbl_count_after_eq(vbl_count, old_intel_flip->vbl_count);
+}
+
+static void intel_flip_prepare(struct drm_flip *flip)
+{
+       struct intel_flip *intel_flip =
+               container_of(flip, struct intel_flip, base);
+
+       /* FIXME some other pipe/pf stuff could be performed here as well. */
+
+       /* stage double buffer updates which need arming by something else */
+       if (intel_flip->plane)
+               intel_sprite_prepare(intel_flip->plane);
+       else
+               intel_plane_prepare(intel_flip->crtc);
+}
+
+static bool intel_flip_flip(struct drm_flip *flip,
+                           struct drm_flip *pending_flip)
+{
+       struct intel_flip *intel_flip = container_of(flip, struct intel_flip, 
base);
+       struct drm_crtc *crtc = intel_flip->crtc;
+       struct drm_device *dev = crtc->dev;
+       int pipe = to_intel_crtc(crtc)->pipe;
+       u32 vbl_count;
+
+       struct intel_flip *old_intel_flip = NULL;
+       if (pending_flip)
+               old_intel_flip = container_of(pending_flip, struct intel_flip, 
base);
+
+       intel_flip->vblank_ref = drm_vblank_get(dev, pipe) == 0;
+
+       vbl_count = get_vbl_count(crtc);
+
+       /* arm all the double buffer registers */
+       if (intel_flip->plane)
+               intel_sprite_commit(intel_flip->plane);
+       else
+               intel_plane_commit(crtc);
+
+       /* This flip will happen on the next vblank */
+       intel_flip->vbl_count = (vbl_count + 1) & 0xffffff;
+
+       /* FIXME oops in unpin when swapping old bos */
+#if 0
+       if (pending_flip) {
+               struct intel_flip *old_intel_flip =
+                       container_of(pending_flip, struct intel_flip, base);
+               bool flipped = intel_vbl_check(pending_flip, vbl_count);
+
+               if (!flipped)
+                       swap(intel_flip->old_bo, old_intel_flip->old_bo);
+
+               return flipped;
+       }
+#endif
+
+       return false;
+}
+
+static bool intel_flip_vblank(struct drm_flip *pending_flip)
+{
+       struct intel_flip *old_intel_flip =
+               container_of(pending_flip, struct intel_flip, base);
+
+       u32 vbl_count = get_vbl_count(old_intel_flip->crtc);
+
+       return intel_vbl_check(pending_flip, vbl_count);
+}
+
+static const struct drm_flip_helper_funcs intel_flip_funcs = {
+       .prepare = intel_flip_prepare,
+       .flip = intel_flip_flip,
+       .vblank = intel_flip_vblank,
+       .complete = intel_flip_complete,
+       .finish = intel_flip_finish,
+       .cleanup = intel_flip_cleanup,
+};
+
+static const struct drm_flip_driver_funcs intel_flip_driver_funcs = {
+       .flush = intel_flip_driver_flush,
+};
+
+static void intel_flip_init(struct drm_device *dev)
+{
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       struct intel_crtc *intel_crtc;
+       struct intel_plane *intel_plane;
+
+       drm_flip_driver_init(&dev_priv->flip_driver, &intel_flip_driver_funcs);
+
+       list_for_each_entry(intel_crtc, &dev->mode_config.crtc_list, base.head) 
{
+               init_waitqueue_head(&intel_crtc->vbl_wait);
+
+               drm_flip_helper_init(&intel_crtc->flip_helper,
+                                    &dev_priv->flip_driver, &intel_flip_funcs);
+       }
+
+       list_for_each_entry(intel_plane, &dev->mode_config.plane_list, 
base.head)
+               drm_flip_helper_init(&intel_plane->flip_helper,
+                                    &dev_priv->flip_driver, &intel_flip_funcs);
+}
+
+static void intel_flip_fini(struct drm_device *dev)
+{
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       struct drm_crtc *crtc;
+       struct drm_plane *plane;
+
+       list_for_each_entry(plane, &dev->mode_config.plane_list, head)
+               drm_flip_helper_fini(&to_intel_plane(plane)->flip_helper);
+
+       list_for_each_entry(crtc, &dev->mode_config.crtc_list, head)
+               drm_flip_helper_fini(&to_intel_crtc(crtc)->flip_helper);
+
+       drm_flip_driver_fini(&dev_priv->flip_driver);
+}
+
+static int atomic_pipe_commit(struct drm_device *dev,
+                             struct intel_atomic_state *state,
+                             int pipe)
+{
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       struct intel_flip *intel_flip, *next;
+       LIST_HEAD(flips);
+       int i;
+
+       for (i = 0; i < dev->mode_config.num_crtc; i++) {
+               struct intel_crtc_state *st = &state->crtc[i];
+               struct drm_crtc *crtc = st->crtc;
+               struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
+
+               if (!st->fb_dirty)
+                       continue;
+
+               if (intel_crtc->pipe != pipe)
+                       continue;
+
+               intel_flip = kzalloc(sizeof *intel_flip, GFP_KERNEL);
+               if (!intel_flip)
+                       goto out;
+
+               intel_plane_calc(crtc, crtc->fb, crtc->x, crtc->y);
+
+               drm_flip_init(&intel_flip->base, &intel_crtc->flip_helper);
+               intel_flip->crtc = crtc;
+               if (st->old_fb)
+                       intel_flip->old_bo = 
to_intel_framebuffer(st->old_fb)->obj;
+
+               list_add_tail(&intel_flip->base.list, &flips);
+       }
+
+       for (i = 0; i < dev->mode_config.num_plane; i++) {
+               struct intel_plane_state *st = &state->plane[i];
+               struct drm_plane *plane = st->plane;
+               struct intel_plane *intel_plane = to_intel_plane(plane);
+
+               if (!st->dirty)
+                       continue;
+
+               if (intel_plane->pipe != pipe)
+                       continue;
+
+               intel_flip = kzalloc(sizeof *intel_flip, GFP_KERNEL);
+               if (!intel_flip)
+                       goto out;
+
+               intel_sprite_calc(plane, plane->fb, &st->coords);
+
+               drm_flip_init(&intel_flip->base, &intel_plane->flip_helper);
+               intel_flip->crtc = intel_get_crtc_for_pipe(dev, pipe);
+               intel_flip->plane = plane;
+               if (st->old_fb)
+                       intel_flip->old_bo = 
to_intel_framebuffer(st->old_fb)->obj;
+
+               list_add_tail(&intel_flip->base.list, &flips);
+       }
+
+       /* FIXME if pipe is not enabled complete flip immediately */
+
+       drm_flip_driver_prepare_flips(&dev_priv->flip_driver, &flips);
+
+       local_irq_disable();
+
+       intel_pipe_vblank_evade(intel_get_crtc_for_pipe(dev, pipe));
+
+       drm_flip_driver_schedule_flips(&dev_priv->flip_driver, &flips);
+
+       local_irq_enable();
+
+       return 0;
+
+ out:
+       list_for_each_entry_safe(intel_flip, next, &flips, base.list)
+               kfree(intel_flip);
+
+       return -ENOMEM;
+}
+
+void intel_handle_vblank(struct drm_device *dev, int pipe)
+{
+       struct intel_crtc *intel_crtc = 
to_intel_crtc(intel_get_crtc_for_pipe(dev, pipe));
+       struct intel_plane *intel_plane;
+
+       drm_handle_vblank(dev, pipe);
+
+       drm_flip_helper_vblank(&intel_crtc->flip_helper);
+
+       list_for_each_entry(intel_plane, &dev->mode_config.plane_list, 
base.head) {
+               if (intel_plane->pipe == pipe)
+                       drm_flip_helper_vblank(&intel_plane->flip_helper);
+       }
+}
diff --git a/drivers/gpu/drm/i915/intel_display.c 
b/drivers/gpu/drm/i915/intel_display.c
index 01c1a19..23fbc0b 100644
--- a/drivers/gpu/drm/i915/intel_display.c
+++ b/drivers/gpu/drm/i915/intel_display.c
@@ -1988,8 +1988,36 @@ static unsigned long 
gen4_compute_dspaddr_offset_xtiled(int *x, int *y,
        return tile_rows * pitch * 8 + tiles * 4096;
 }

-static int i9xx_update_plane(struct drm_crtc *crtc, struct drm_framebuffer *fb,
-                            int x, int y)
+void intel_plane_prepare(struct drm_crtc *crtc)
+{
+       struct drm_device *dev = crtc->dev;
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
+       int plane = intel_crtc->plane;
+       const struct intel_plane_regs *regs = &intel_crtc->primary_regs;
+
+       I915_WRITE(DSPCNTR(plane), regs->cntr);
+       I915_WRITE(DSPSTRIDE(plane), regs->stride);
+}
+
+void intel_plane_commit(struct drm_crtc *crtc)
+{
+       struct drm_device *dev = crtc->dev;
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
+       int plane = intel_crtc->plane;
+       const struct intel_plane_regs *regs = &intel_crtc->primary_regs;
+
+       if (INTEL_INFO(dev)->gen >= 4) {
+               I915_WRITE(DSPSURF(plane), regs->surf);
+               I915_WRITE(DSPTILEOFF(plane), regs->tileoff);
+               I915_WRITE(DSPLINOFF(plane), regs->linoff);
+       } else
+               I915_WRITE(DSPADDR(plane), regs->addr);
+}
+
+static int i9xx_calc_plane(struct drm_crtc *crtc, struct drm_framebuffer *fb,
+                          int x, int y)
 {
        struct drm_device *dev = crtc->dev;
        struct drm_i915_private *dev_priv = dev->dev_private;
@@ -1998,9 +2026,8 @@ static int i9xx_update_plane(struct drm_crtc *crtc, 
struct drm_framebuffer *fb,
        struct drm_i915_gem_object *obj;
        int plane = intel_crtc->plane;
        unsigned long linear_offset;
-       u32 dspcntr;
-       u32 reg;
        unsigned int cpp = drm_format_plane_cpp(fb->pixel_format, 0);
+       struct intel_plane_regs *regs = &intel_crtc->primary_regs;

        switch (plane) {
        case 0:
@@ -2014,36 +2041,35 @@ static int i9xx_update_plane(struct drm_crtc *crtc, 
struct drm_framebuffer *fb,
        intel_fb = to_intel_framebuffer(fb);
        obj = intel_fb->obj;

-       reg = DSPCNTR(plane);
-       dspcntr = I915_READ(reg);
+       regs->cntr = I915_READ(DSPCNTR(plane));
        /* Mask out pixel format bits in case we change it */
-       dspcntr &= ~DISPPLANE_PIXFORMAT_MASK;
+       regs->cntr &= ~DISPPLANE_PIXFORMAT_MASK;
        switch (fb->pixel_format) {
        case DRM_FORMAT_C8:
-               dspcntr |= DISPPLANE_8BPP;
+               regs->cntr |= DISPPLANE_8BPP;
                break;
        case DRM_FORMAT_XRGB1555:
        case DRM_FORMAT_ARGB1555:
-               dspcntr |= DISPPLANE_BGRX555;
+               regs->cntr |= DISPPLANE_BGRX555;
                break;
        case DRM_FORMAT_RGB565:
-               dspcntr |= DISPPLANE_BGRX565;
+               regs->cntr |= DISPPLANE_BGRX565;
                break;
        case DRM_FORMAT_XRGB8888:
        case DRM_FORMAT_ARGB8888:
-               dspcntr |= DISPPLANE_BGRX888;
+               regs->cntr |= DISPPLANE_BGRX888;
                break;
        case DRM_FORMAT_XBGR8888:
        case DRM_FORMAT_ABGR8888:
-               dspcntr |= DISPPLANE_RGBX888;
+               regs->cntr |= DISPPLANE_RGBX888;
                break;
        case DRM_FORMAT_XRGB2101010:
        case DRM_FORMAT_ARGB2101010:
-               dspcntr |= DISPPLANE_BGRX101010;
+               regs->cntr |= DISPPLANE_BGRX101010;
                break;
        case DRM_FORMAT_XBGR2101010:
        case DRM_FORMAT_ABGR2101010:
-               dspcntr |= DISPPLANE_RGBX101010;
+               regs->cntr |= DISPPLANE_RGBX101010;
                break;
        default:
                DRM_ERROR("Unknown pixel format 0x%08x\n", fb->pixel_format);
@@ -2052,13 +2078,11 @@ static int i9xx_update_plane(struct drm_crtc *crtc, 
struct drm_framebuffer *fb,

        if (INTEL_INFO(dev)->gen >= 4) {
                if (obj->tiling_mode != I915_TILING_NONE)
-                       dspcntr |= DISPPLANE_TILED;
+                       regs->cntr |= DISPPLANE_TILED;
                else
-                       dspcntr &= ~DISPPLANE_TILED;
+                       regs->cntr &= ~DISPPLANE_TILED;
        }

-       I915_WRITE(reg, dspcntr);
-
        linear_offset = fb->offsets[0] + y * fb->pitches[0] + x * cpp;

        if (INTEL_INFO(dev)->gen >= 4) {
@@ -2072,21 +2096,42 @@ static int i9xx_update_plane(struct drm_crtc *crtc, 
struct drm_framebuffer *fb,

        DRM_DEBUG_KMS("Writing base %08X %08lX %d %d %d\n",
                      obj->gtt_offset, linear_offset, x, y, fb->pitches[0]);
-       I915_WRITE(DSPSTRIDE(plane), fb->pitches[0]);
+
+       regs->stride = fb->pitches[0];
+
        if (INTEL_INFO(dev)->gen >= 4) {
-               I915_MODIFY_DISPBASE(DSPSURF(plane),
-                                    obj->gtt_offset + 
intel_crtc->dspaddr_offset);
-               I915_WRITE(DSPTILEOFF(plane), (y << 16) | x);
-               I915_WRITE(DSPLINOFF(plane), linear_offset);
+               regs->surf = I915_LO_DISPBASE(I915_READ(DSPSURF(plane)));
+               regs->surf |= obj->gtt_offset + intel_crtc->dspaddr_offset;
+               regs->tileoff = (y << 16) | x;
+               regs->linoff = linear_offset;
        } else
-               I915_WRITE(DSPADDR(plane), obj->gtt_offset + linear_offset);
-       POSTING_READ(reg);
+               regs->addr = obj->gtt_offset + linear_offset;

        return 0;
 }

-static int ironlake_update_plane(struct drm_crtc *crtc,
-                                struct drm_framebuffer *fb, int x, int y)
+static int i9xx_update_plane(struct drm_crtc *crtc, struct drm_framebuffer *fb,
+                            int x, int y)
+{
+       struct drm_i915_private *dev_priv = crtc->dev->dev_private;
+       struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
+       int plane = intel_crtc->plane;
+       int ret;
+
+       ret = i9xx_calc_plane(crtc, fb, x, y);
+       if (ret)
+               return ret;
+
+       intel_plane_prepare(crtc);
+       intel_plane_commit(crtc);
+
+       POSTING_READ(DSPCNTR(plane));
+
+       return 0;
+}
+
+static int ironlake_calc_plane(struct drm_crtc *crtc,
+                              struct drm_framebuffer *fb, int x, int y)
 {
        struct drm_device *dev = crtc->dev;
        struct drm_i915_private *dev_priv = dev->dev_private;
@@ -2095,9 +2140,8 @@ static int ironlake_update_plane(struct drm_crtc *crtc,
        struct drm_i915_gem_object *obj;
        int plane = intel_crtc->plane;
        unsigned long linear_offset;
-       u32 dspcntr;
-       u32 reg;
        unsigned int cpp = drm_format_plane_cpp(fb->pixel_format, 0);
+       struct intel_plane_regs *regs = &intel_crtc->primary_regs;

        switch (plane) {
        case 0:
@@ -2112,32 +2156,31 @@ static int ironlake_update_plane(struct drm_crtc *crtc,
        intel_fb = to_intel_framebuffer(fb);
        obj = intel_fb->obj;

-       reg = DSPCNTR(plane);
-       dspcntr = I915_READ(reg);
+       regs->cntr = I915_READ(DSPCNTR(plane));
        /* Mask out pixel format bits in case we change it */
-       dspcntr &= ~DISPPLANE_PIXFORMAT_MASK;
+       regs->cntr &= ~DISPPLANE_PIXFORMAT_MASK;
        switch (fb->pixel_format) {
        case DRM_FORMAT_C8:
-               dspcntr |= DISPPLANE_8BPP;
+               regs->cntr |= DISPPLANE_8BPP;
                break;
        case DRM_FORMAT_RGB565:
-               dspcntr |= DISPPLANE_BGRX565;
+               regs->cntr |= DISPPLANE_BGRX565;
                break;
        case DRM_FORMAT_XRGB8888:
        case DRM_FORMAT_ARGB8888:
-               dspcntr |= DISPPLANE_BGRX888;
+               regs->cntr |= DISPPLANE_BGRX888;
                break;
        case DRM_FORMAT_XBGR8888:
        case DRM_FORMAT_ABGR8888:
-               dspcntr |= DISPPLANE_RGBX888;
+               regs->cntr |= DISPPLANE_RGBX888;
                break;
        case DRM_FORMAT_XRGB2101010:
        case DRM_FORMAT_ARGB2101010:
-               dspcntr |= DISPPLANE_BGRX101010;
+               regs->cntr |= DISPPLANE_BGRX101010;
                break;
        case DRM_FORMAT_XBGR2101010:
        case DRM_FORMAT_ABGR2101010:
-               dspcntr |= DISPPLANE_RGBX101010;
+               regs->cntr |= DISPPLANE_RGBX101010;
                break;
        default:
                DRM_ERROR("Unknown pixel format 0x%08x\n", fb->pixel_format);
@@ -2145,33 +2188,57 @@ static int ironlake_update_plane(struct drm_crtc *crtc,
        }

        if (obj->tiling_mode != I915_TILING_NONE)
-               dspcntr |= DISPPLANE_TILED;
+               regs->cntr |= DISPPLANE_TILED;
        else
-               dspcntr &= ~DISPPLANE_TILED;
+               regs->cntr &= ~DISPPLANE_TILED;

        /* must disable */
-       dspcntr |= DISPPLANE_TRICKLE_FEED_DISABLE;
-
-       I915_WRITE(reg, dspcntr);
+       regs->cntr |= DISPPLANE_TRICKLE_FEED_DISABLE;

        linear_offset = fb->offsets[0] + y * fb->pitches[0] + x * 
(fb->bits_per_pixel / 8);
-       intel_crtc->dspaddr_offset =
-               gen4_compute_dspaddr_offset_xtiled(&x, &y,
-                                                  cpp, fb->pitches[0]);
+       intel_crtc->dspaddr_offset = gen4_compute_dspaddr_offset_xtiled(&x, &y, 
cpp, fb->pitches[0]);
        linear_offset -= intel_crtc->dspaddr_offset;

        DRM_DEBUG_KMS("Writing base %08X %08lX %d %d %d\n",
                      obj->gtt_offset, linear_offset, x, y, fb->pitches[0]);
-       I915_WRITE(DSPSTRIDE(plane), fb->pitches[0]);
-       I915_MODIFY_DISPBASE(DSPSURF(plane),
-                            obj->gtt_offset + intel_crtc->dspaddr_offset);
-       I915_WRITE(DSPTILEOFF(plane), (y << 16) | x);
-       I915_WRITE(DSPLINOFF(plane), linear_offset);
-       POSTING_READ(reg);
+       regs->stride = fb->pitches[0];
+       regs->surf = I915_LO_DISPBASE(I915_READ(DSPSURF(plane)));
+       regs->surf |= obj->gtt_offset + intel_crtc->dspaddr_offset;
+       regs->tileoff = (y << 16) | x;
+       regs->linoff = linear_offset;

        return 0;
 }

+static int ironlake_update_plane(struct drm_crtc *crtc,
+                                struct drm_framebuffer *fb, int x, int y)
+{
+       struct drm_i915_private *dev_priv = crtc->dev->dev_private;
+       struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
+       int plane = intel_crtc->plane;
+       int ret;
+
+       ret = ironlake_calc_plane(crtc, fb, x, y);
+       if (ret)
+               return ret;
+
+       intel_plane_prepare(crtc);
+       intel_plane_commit(crtc);
+
+       POSTING_READ(DSPCNTR(plane));
+
+       return 0;
+}
+
+int intel_plane_calc(struct drm_crtc *crtc,
+                    struct drm_framebuffer *fb, int x, int y)
+{
+       if (HAS_PCH_SPLIT(crtc->dev))
+               return ironlake_calc_plane(crtc, fb, x, y);
+       else
+               return i9xx_calc_plane(crtc, fb, x, y);
+}
+
 /* Assume fb object is pinned & idle & fenced and just update base pointers */
 static int
 intel_pipe_set_base_atomic(struct drm_crtc *crtc, struct drm_framebuffer *fb,
diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h
index fa81676..52b0cc7 100644
--- a/drivers/gpu/drm/i915/intel_drv.h
+++ b/drivers/gpu/drm/i915/intel_drv.h
@@ -31,6 +31,7 @@
 #include "drm_crtc.h"
 #include "drm_crtc_helper.h"
 #include "drm_fb_helper.h"
+#include "drm_flip.h"

 #define _wait_for(COND, MS, W) ({ \
        unsigned long timeout__ = jiffies + msecs_to_jiffies(MS);       \
@@ -162,6 +163,21 @@ struct intel_connector {
        struct intel_encoder *encoder;
 };

+struct intel_plane_regs {
+       u32 cntr;
+       u32 addr;
+       u32 linoff;
+       u32 stride;
+       u32 pos;
+       u32 size;
+       u32 keyval;
+       u32 keymsk;
+       u32 surf;
+       u32 keymaxval;
+       u32 tileoff;
+       u32 scale;
+};
+
 struct intel_crtc {
        struct drm_crtc base;
        enum pipe pipe;
@@ -191,6 +207,12 @@ struct intel_crtc {

        /* We can share PLLs across outputs if the timings match */
        struct intel_pch_pll *pch_pll;
+
+       struct intel_plane_regs primary_regs;
+
+       struct drm_flip_helper flip_helper;
+       wait_queue_head_t vbl_wait;
+       bool vbl_received;
 };

 struct intel_plane_coords {
@@ -208,6 +230,7 @@ struct intel_plane {
        struct drm_i915_gem_object *obj;
        int max_downscale;
        u32 lut_r[1024], lut_g[1024], lut_b[1024];
+       struct intel_plane_regs regs;
        void (*update_plane)(struct drm_plane *plane,
                             struct drm_framebuffer *fb,
                             const struct intel_plane_coords *clip);
@@ -216,6 +239,7 @@ struct intel_plane {
                               struct drm_intel_sprite_colorkey *key);
        void (*get_colorkey)(struct drm_plane *plane,
                             struct drm_intel_sprite_colorkey *key);
+       struct drm_flip_helper flip_helper;
 };

 struct intel_watermark_params {
@@ -525,4 +549,9 @@ extern void intel_crtc_cursor_commit(struct drm_crtc *crtc, 
uint32_t handle,
                                     struct drm_i915_gem_object *obj,
                                     uint32_t addr);

+extern int intel_calc_primary(struct drm_crtc *crtc,
+                           struct drm_framebuffer *fb, int x, int y);
+extern void intel_prepare_primary(struct drm_crtc *crtc);
+extern void intel_commit_primary(struct drm_crtc *crtc);
+
 #endif /* __INTEL_DRV_H__ */
diff --git a/drivers/gpu/drm/i915/intel_sprite.c 
b/drivers/gpu/drm/i915/intel_sprite.c
index d456cd2..3049a62 100644
--- a/drivers/gpu/drm/i915/intel_sprite.c
+++ b/drivers/gpu/drm/i915/intel_sprite.c
@@ -187,12 +187,10 @@ int intel_check_plane(const struct drm_plane *plane,
 }

 static void
-ivb_update_plane(struct drm_plane *plane,
-                struct drm_framebuffer *fb,
-                const struct intel_plane_coords *coords)
+ivb_calc_plane(struct drm_plane *plane,
+              struct drm_framebuffer *fb,
+              const struct intel_plane_coords *coords)
 {
-       struct drm_device *dev = plane->dev;
-       struct drm_i915_private *dev_priv = dev->dev_private;
        struct intel_plane *intel_plane = to_intel_plane(plane);
        const struct drm_i915_gem_object *obj = to_intel_framebuffer(fb)->obj;
        int crtc_x = coords->crtc_x;
@@ -203,47 +201,48 @@ ivb_update_plane(struct drm_plane *plane,
        uint32_t y = coords->src_y;
        uint32_t src_w = coords->src_w;
        uint32_t src_h = coords->src_h;
-       int pipe = intel_plane->pipe;
-       u32 sprctl, sprscale = 0;
        int pixel_size = drm_format_plane_cpp(fb->pixel_format, 0);
-
-       sprctl = I915_READ(SPRCTL(pipe));
+       struct intel_plane_regs *regs = &intel_plane->regs;

        /* Mask out pixel format bits in case we change it */
-       sprctl &= ~SPRITE_PIXFORMAT_MASK;
-       sprctl &= ~SPRITE_RGB_ORDER_RGBX;
-       sprctl &= ~SPRITE_YUV_BYTE_ORDER_MASK;
-       sprctl &= ~SPRITE_TILED;
+       regs->cntr &= ~(SPRITE_PIXFORMAT_MASK |
+                       SPRITE_RGB_ORDER_RGBX |
+                       SPRITE_YUV_BYTE_ORDER_MASK |
+                       SPRITE_TRICKLE_FEED_DISABLE |
+                       SPRITE_TILED |
+                       SPRITE_ENABLE);

        switch (fb->pixel_format) {
        case DRM_FORMAT_XBGR8888:
-               sprctl |= SPRITE_FORMAT_RGBX888;
+               regs->cntr |= SPRITE_FORMAT_RGBX888;
                break;
        case DRM_FORMAT_XRGB8888:
-               sprctl |= SPRITE_FORMAT_RGBX888 | SPRITE_RGB_ORDER_RGBX;
+               regs->cntr |= SPRITE_FORMAT_RGBX888 | SPRITE_RGB_ORDER_RGBX;
                break;
        case DRM_FORMAT_YUYV:
-               sprctl |= SPRITE_FORMAT_YUV422 | SPRITE_YUV_ORDER_YUYV;
+               regs->cntr |= SPRITE_FORMAT_YUV422 | SPRITE_YUV_ORDER_YUYV;
                break;
        case DRM_FORMAT_YVYU:
-               sprctl |= SPRITE_FORMAT_YUV422 | SPRITE_YUV_ORDER_YVYU;
+               regs->cntr |= SPRITE_FORMAT_YUV422 | SPRITE_YUV_ORDER_YVYU;
                break;
        case DRM_FORMAT_UYVY:
-               sprctl |= SPRITE_FORMAT_YUV422 | SPRITE_YUV_ORDER_UYVY;
+               regs->cntr |= SPRITE_FORMAT_YUV422 | SPRITE_YUV_ORDER_UYVY;
                break;
        case DRM_FORMAT_VYUY:
-               sprctl |= SPRITE_FORMAT_YUV422 | SPRITE_YUV_ORDER_VYUY;
+               regs->cntr |= SPRITE_FORMAT_YUV422 | SPRITE_YUV_ORDER_VYUY;
                break;
        default:
                BUG();
        }

        if (obj->tiling_mode != I915_TILING_NONE)
-               sprctl |= SPRITE_TILED;
+               regs->cntr |= SPRITE_TILED;

        /* must disable */
-       sprctl |= SPRITE_TRICKLE_FEED_DISABLE;
-       sprctl |= SPRITE_ENABLE;
+       regs->cntr |= SPRITE_TRICKLE_FEED_DISABLE;
+
+       if (coords->visible)
+               regs->cntr |= SPRITE_ENABLE;

        /* Sizes are 0 based */
        src_w--;
@@ -251,20 +250,54 @@ ivb_update_plane(struct drm_plane *plane,
        crtc_w--;
        crtc_h--;

-       intel_update_sprite_watermarks(dev, pipe, crtc_w, pixel_size);
+       /*
+        * IVB workaround: must disable low power watermarks for at least
+        * one frame before enabling scaling.  LP watermarks can be re-enabled
+        * when scaling is disabled.
+        */
+       if (coords->visible && (crtc_w != src_w || crtc_h != src_h))
+               regs->scale = SPRITE_SCALE_ENABLE | (src_w << 16) | src_h;
+       else
+               regs->scale = 0;
+
+       regs->stride = fb->pitches[0];
+       regs->pos =  (crtc_y << 16) | crtc_x;
+
+       if (obj->tiling_mode != I915_TILING_NONE) {
+               y += fb->offsets[0] / fb->pitches[0];
+               x += fb->offsets[0] % fb->pitches[0] / pixel_size;
+
+               regs->tileoff = (y << 16) | x;
+       } else
+               regs->linoff = fb->offsets[0] + y * fb->pitches[0] + x * 
pixel_size;
+
+       regs->size = (crtc_h << 16) | crtc_w;
+       regs->surf = I915_LO_DISPBASE(regs->surf) | obj->gtt_offset;
+}
+
+static void
+ivb_commit_plane(struct drm_plane *plane, struct drm_framebuffer *fb)
+{
+       struct drm_device *dev = plane->dev;
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       struct intel_plane *intel_plane = to_intel_plane(plane);
+       int pipe = intel_plane->pipe;
+       int pixel_size = fb ? drm_format_plane_cpp(fb->pixel_format, 0) : 0;
+       const struct intel_plane_regs *regs = &intel_plane->regs;
+
+       intel_update_sprite_watermarks(dev, pipe, regs->size & 0xffff, 
pixel_size);

        /*
         * IVB workaround: must disable low power watermarks for at least
         * one frame before enabling scaling.  LP watermarks can be re-enabled
         * when scaling is disabled.
         */
-       if (crtc_w != src_w || crtc_h != src_h) {
+       if (regs->scale & SPRITE_SCALE_ENABLE) {
                if (!dev_priv->sprite_scaling_enabled) {
                        dev_priv->sprite_scaling_enabled = true;
                        intel_update_watermarks(dev);
                        intel_wait_for_vblank(dev, pipe);
                }
-               sprscale = SPRITE_SCALE_ENABLE | (src_w << 16) | src_h;
        } else {
                if (dev_priv->sprite_scaling_enabled) {
                        dev_priv->sprite_scaling_enabled = false;
@@ -273,23 +306,31 @@ ivb_update_plane(struct drm_plane *plane,
                }
        }

-       I915_WRITE(SPRSTRIDE(pipe), fb->pitches[0]);
-       I915_WRITE(SPRPOS(pipe), (crtc_y << 16) | crtc_x);
-       if (obj->tiling_mode != I915_TILING_NONE) {
-               y += fb->offsets[0] / fb->pitches[0];
-               x += fb->offsets[0] % fb->pitches[0] / pixel_size;
+       I915_WRITE(SPRKEYVAL(pipe), regs->keyval);
+       I915_WRITE(SPRKEYMAX(pipe), regs->keymaxval);
+       I915_WRITE(SPRKEYMSK(pipe), regs->keymsk);
+       I915_WRITE(SPRSTRIDE(pipe), regs->stride);
+       I915_WRITE(SPRPOS(pipe), regs->pos);
+       I915_WRITE(SPRTILEOFF(pipe), regs->tileoff);
+       I915_WRITE(SPRLINOFF(pipe), regs->linoff);
+       I915_WRITE(SPRSIZE(pipe), regs->size);
+       I915_WRITE(SPRSCALE(pipe), regs->scale);
+       I915_WRITE(SPRCTL(pipe), regs->cntr);
+       I915_WRITE(SPRSURF(pipe), regs->surf);
+}

-               I915_WRITE(SPRTILEOFF(pipe), (y << 16) | x);
-       } else {
-               unsigned long offset;
+static void
+ivb_update_plane(struct drm_plane *plane,
+                struct drm_framebuffer *fb,
+                const struct intel_plane_coords *coords)
+{
+       struct drm_device *dev = plane->dev;
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       int pipe = to_intel_plane(plane)->pipe;
+
+       ivb_calc_plane(plane, fb, coords);
+       ivb_commit_plane(plane, fb);

-               offset = fb->offsets[0] + y * fb->pitches[0] + x * pixel_size;
-               I915_WRITE(SPRLINOFF(pipe), offset);
-       }
-       I915_WRITE(SPRSIZE(pipe), (crtc_h << 16) | crtc_w);
-       I915_WRITE(SPRSCALE(pipe), sprscale);
-       I915_WRITE(SPRCTL(pipe), sprctl);
-       I915_MODIFY_DISPBASE(SPRSURF(pipe), obj->gtt_offset);
        POSTING_READ(SPRSURF(pipe));
 }

@@ -300,12 +341,13 @@ ivb_disable_plane(struct drm_plane *plane)
        struct drm_i915_private *dev_priv = dev->dev_private;
        struct intel_plane *intel_plane = to_intel_plane(plane);
        int pipe = intel_plane->pipe;
+       struct intel_plane_regs *regs = &intel_plane->regs;

-       I915_WRITE(SPRCTL(pipe), I915_READ(SPRCTL(pipe)) & ~SPRITE_ENABLE);
+       regs->cntr &= ~SPRITE_ENABLE;
        /* Can't leave the scaler enabled... */
-       I915_WRITE(SPRSCALE(pipe), 0);
-       /* Activate double buffered register update */
-       I915_MODIFY_DISPBASE(SPRSURF(pipe), 0);
+       regs->scale = 0;
+
+       ivb_commit_plane(plane, plane->fb);
        POSTING_READ(SPRSURF(pipe));

        dev_priv->sprite_scaling_enabled = false;
@@ -318,61 +360,50 @@ ivb_update_colorkey(struct drm_plane *plane,
 {
        struct drm_device *dev = plane->dev;
        struct drm_i915_private *dev_priv = dev->dev_private;
-       struct intel_plane *intel_plane;
-       u32 sprctl;
-       int ret = 0;
-
-       intel_plane = to_intel_plane(plane);
+       struct intel_plane *intel_plane = to_intel_plane(plane);
+       struct intel_plane_regs *regs = &intel_plane->regs;

-       I915_WRITE(SPRKEYVAL(intel_plane->pipe), key->min_value);
-       I915_WRITE(SPRKEYMAX(intel_plane->pipe), key->max_value);
-       I915_WRITE(SPRKEYMSK(intel_plane->pipe), key->channel_mask);
+       regs->keyval = key->min_value;
+       regs->keymaxval = key->max_value;
+       regs->keymsk = key->channel_mask;

-       sprctl = I915_READ(SPRCTL(intel_plane->pipe));
-       sprctl &= ~(SPRITE_SOURCE_KEY | SPRITE_DEST_KEY);
+       regs->cntr &= ~(SPRITE_SOURCE_KEY | SPRITE_DEST_KEY);
        if (key->flags & I915_SET_COLORKEY_DESTINATION)
-               sprctl |= SPRITE_DEST_KEY;
+               regs->cntr |= SPRITE_DEST_KEY;
        else if (key->flags & I915_SET_COLORKEY_SOURCE)
-               sprctl |= SPRITE_SOURCE_KEY;
-       I915_WRITE(SPRCTL(intel_plane->pipe), sprctl);
+               regs->cntr |= SPRITE_SOURCE_KEY;

+       ivb_commit_plane(plane, plane->fb);
        POSTING_READ(SPRKEYMSK(intel_plane->pipe));

-       return ret;
+       return 0;
 }

 static void
 ivb_get_colorkey(struct drm_plane *plane, struct drm_intel_sprite_colorkey 
*key)
 {
-       struct drm_device *dev = plane->dev;
-       struct drm_i915_private *dev_priv = dev->dev_private;
-       struct intel_plane *intel_plane;
-       u32 sprctl;
-
-       intel_plane = to_intel_plane(plane);
+       struct intel_plane *intel_plane = to_intel_plane(plane);
+       const struct intel_plane_regs *regs = &intel_plane->regs;

-       key->min_value = I915_READ(SPRKEYVAL(intel_plane->pipe));
-       key->max_value = I915_READ(SPRKEYMAX(intel_plane->pipe));
-       key->channel_mask = I915_READ(SPRKEYMSK(intel_plane->pipe));
+       key->min_value = regs->keyval;
+       key->max_value = regs->keymaxval;
+       key->channel_mask = regs->keymsk;
        key->flags = 0;

-       sprctl = I915_READ(SPRCTL(intel_plane->pipe));
-
-       if (sprctl & SPRITE_DEST_KEY)
+       if (regs->cntr & SPRITE_DEST_KEY)
                key->flags = I915_SET_COLORKEY_DESTINATION;
-       else if (sprctl & SPRITE_SOURCE_KEY)
+       else if (regs->cntr & SPRITE_SOURCE_KEY)
                key->flags = I915_SET_COLORKEY_SOURCE;
        else
                key->flags = I915_SET_COLORKEY_NONE;
 }

 static void
-ilk_update_plane(struct drm_plane *plane,
-                struct drm_framebuffer *fb,
-                const struct intel_plane_coords *coords)
+ilk_calc_plane(struct drm_plane *plane,
+              struct drm_framebuffer *fb,
+              const struct intel_plane_coords *coords)
 {
        struct drm_device *dev = plane->dev;
-       struct drm_i915_private *dev_priv = dev->dev_private;
        struct intel_plane *intel_plane = to_intel_plane(plane);
        const struct drm_i915_gem_object *obj = to_intel_framebuffer(fb)->obj;
        int crtc_x = coords->crtc_x;
@@ -383,46 +414,48 @@ ilk_update_plane(struct drm_plane *plane,
        uint32_t y = coords->src_y;
        uint32_t src_w = coords->src_w;
        uint32_t src_h = coords->src_h;
-       int pipe = intel_plane->pipe;
-       u32 dvscntr, dvsscale;
        int pixel_size = drm_format_plane_cpp(fb->pixel_format, 0);
-
-       dvscntr = I915_READ(DVSCNTR(pipe));
+       struct intel_plane_regs *regs = &intel_plane->regs;

        /* Mask out pixel format bits in case we change it */
-       dvscntr &= ~DVS_PIXFORMAT_MASK;
-       dvscntr &= ~DVS_RGB_ORDER_XBGR;
-       dvscntr &= ~DVS_YUV_BYTE_ORDER_MASK;
+       regs->cntr &= ~(DVS_PIXFORMAT_MASK |
+                       DVS_RGB_ORDER_XBGR |
+                       DVS_YUV_BYTE_ORDER_MASK |
+                       DVS_TRICKLE_FEED_DISABLE |
+                       DVS_TILED |
+                       DVS_ENABLE);

        switch (fb->pixel_format) {
        case DRM_FORMAT_XBGR8888:
-               dvscntr |= DVS_FORMAT_RGBX888 | DVS_RGB_ORDER_XBGR;
+               regs->cntr |= DVS_FORMAT_RGBX888 | DVS_RGB_ORDER_XBGR;
                break;
        case DRM_FORMAT_XRGB8888:
-               dvscntr |= DVS_FORMAT_RGBX888;
+               regs->cntr |= DVS_FORMAT_RGBX888;
                break;
        case DRM_FORMAT_YUYV:
-               dvscntr |= DVS_FORMAT_YUV422 | DVS_YUV_ORDER_YUYV;
+               regs->cntr |= DVS_FORMAT_YUV422 | DVS_YUV_ORDER_YUYV;
                break;
        case DRM_FORMAT_YVYU:
-               dvscntr |= DVS_FORMAT_YUV422 | DVS_YUV_ORDER_YVYU;
+               regs->cntr |= DVS_FORMAT_YUV422 | DVS_YUV_ORDER_YVYU;
                break;
        case DRM_FORMAT_UYVY:
-               dvscntr |= DVS_FORMAT_YUV422 | DVS_YUV_ORDER_UYVY;
+               regs->cntr |= DVS_FORMAT_YUV422 | DVS_YUV_ORDER_UYVY;
                break;
        case DRM_FORMAT_VYUY:
-               dvscntr |= DVS_FORMAT_YUV422 | DVS_YUV_ORDER_VYUY;
+               regs->cntr |= DVS_FORMAT_YUV422 | DVS_YUV_ORDER_VYUY;
                break;
        default:
                BUG();
        }

        if (obj->tiling_mode != I915_TILING_NONE)
-               dvscntr |= DVS_TILED;
+               regs->cntr |= DVS_TILED;

        if (IS_GEN6(dev))
-               dvscntr |= DVS_TRICKLE_FEED_DISABLE; /* must disable */
-       dvscntr |= DVS_ENABLE;
+               regs->cntr |= DVS_TRICKLE_FEED_DISABLE; /* must disable */
+
+       if (coords->visible)
+               regs->cntr |= DVS_ENABLE;

        /* Sizes are 0 based */
        src_w--;
@@ -430,29 +463,64 @@ ilk_update_plane(struct drm_plane *plane,
        crtc_w--;
        crtc_h--;

-       intel_update_sprite_watermarks(dev, pipe, crtc_w, pixel_size);
+       if (coords->visible && (IS_GEN5(dev) || crtc_w != src_w || crtc_h != 
src_h))
+               regs->scale = DVS_SCALE_ENABLE | (src_w << 16) | src_h;
+       else
+               regs->scale = 0;

-       dvsscale = 0;
-       if (IS_GEN5(dev) || crtc_w != src_w || crtc_h != src_h)
-               dvsscale = DVS_SCALE_ENABLE | (src_w << 16) | src_h;
+       regs->stride = fb->pitches[0];
+       regs->pos = (crtc_y << 16) | crtc_x;

-       I915_WRITE(DVSSTRIDE(pipe), fb->pitches[0]);
-       I915_WRITE(DVSPOS(pipe), (crtc_y << 16) | crtc_x);
        if (obj->tiling_mode != I915_TILING_NONE) {
                y += fb->offsets[0] / fb->pitches[0];
                x += fb->offsets[0] % fb->pitches[0] / pixel_size;

-               I915_WRITE(DVSTILEOFF(pipe), (y << 16) | x);
-       } else {
-               unsigned long offset;
+               regs->tileoff = (y << 16) | x;
+       } else
+               regs->linoff = fb->offsets[0] + y * fb->pitches[0] + x * 
pixel_size;
+
+       regs->size = (crtc_h << 16) | crtc_w;
+       regs->surf = I915_LO_DISPBASE(regs->surf) | obj->gtt_offset;
+}
+
+static void
+ilk_commit_plane(struct drm_plane *plane, struct drm_framebuffer *fb)
+{
+       struct drm_device *dev = plane->dev;
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       struct intel_plane *intel_plane = to_intel_plane(plane);
+       int pipe = intel_plane->pipe;
+       int pixel_size = fb ? drm_format_plane_cpp(fb->pixel_format, 0) : 0;
+       const struct intel_plane_regs *regs = &intel_plane->regs;
+
+       /* FIXME move somewhere appropriate */
+       intel_update_sprite_watermarks(dev, pipe, regs->size & 0xffff, 
pixel_size);
+
+       I915_WRITE(DVSKEYVAL(pipe), regs->keyval);
+       I915_WRITE(DVSKEYMAX(pipe), regs->keymaxval);
+       I915_WRITE(DVSKEYMSK(pipe), regs->keymsk);
+       I915_WRITE(DVSSTRIDE(pipe), regs->stride);
+       I915_WRITE(DVSPOS(pipe), regs->pos);
+       I915_WRITE(DVSTILEOFF(pipe), regs->tileoff);
+       I915_WRITE(DVSLINOFF(pipe), regs->linoff);
+       I915_WRITE(DVSSIZE(pipe), regs->size);
+       I915_WRITE(DVSSCALE(pipe), regs->scale);
+       I915_WRITE(DVSCNTR(pipe), regs->cntr);
+       I915_WRITE(DVSSURF(pipe), regs->surf);
+}
+
+static void
+ilk_update_plane(struct drm_plane *plane,
+                struct drm_framebuffer *fb,
+                const struct intel_plane_coords *coords)
+{
+       struct drm_device *dev = plane->dev;
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       int pipe = to_intel_plane(plane)->pipe;
+
+       ilk_calc_plane(plane, fb, coords);
+       ilk_commit_plane(plane, fb);

-               offset = fb->offsets[0] + y * fb->pitches[0] + x * pixel_size;
-               I915_WRITE(DVSLINOFF(pipe), offset);
-       }
-       I915_WRITE(DVSSIZE(pipe), (crtc_h << 16) | crtc_w);
-       I915_WRITE(DVSSCALE(pipe), dvsscale);
-       I915_WRITE(DVSCNTR(pipe), dvscntr);
-       I915_MODIFY_DISPBASE(DVSSURF(pipe), obj->gtt_offset);
        POSTING_READ(DVSSURF(pipe));
 }

@@ -463,22 +531,50 @@ ilk_disable_plane(struct drm_plane *plane)
        struct drm_i915_private *dev_priv = dev->dev_private;
        struct intel_plane *intel_plane = to_intel_plane(plane);
        int pipe = intel_plane->pipe;
+       struct intel_plane_regs *regs = &intel_plane->regs;

-       I915_WRITE(DVSCNTR(pipe), I915_READ(DVSCNTR(pipe)) & ~DVS_ENABLE);
+       regs->cntr &= ~DVS_ENABLE;
        /* Disable the scaler */
-       I915_WRITE(DVSSCALE(pipe), 0);
-       /* Flush double buffered register updates */
-       I915_MODIFY_DISPBASE(DVSSURF(pipe), 0);
+       regs->scale = 0;
+
+       ilk_commit_plane(plane, NULL);
        POSTING_READ(DVSSURF(pipe));
 }

+
+void intel_sprite_calc(struct drm_plane *plane, struct drm_framebuffer *fb,
+                      const struct intel_plane_coords *coords)
+{
+       struct drm_device *dev = plane->dev;
+
+       if (INTEL_INFO(dev)->gen == 7)
+               ivb_calc_plane(plane, fb, coords);
+       else
+               ilk_calc_plane(plane, fb, coords);
+}
+
+void intel_sprite_prepare(struct drm_plane *plane)
+{
+}
+
+void intel_sprite_commit(struct drm_plane *plane)
+{
+       struct drm_device *dev = plane->dev;
+
+       if (INTEL_INFO(dev)->gen == 7)
+               ivb_commit_plane(plane, plane->fb);
+       else
+               ilk_commit_plane(plane, plane->fb);
+}
+
+void intel_plane_commit(struct drm_crtc *crtc);
+
 static void
 intel_enable_primary(struct drm_crtc *crtc)
 {
        struct drm_device *dev = crtc->dev;
-       struct drm_i915_private *dev_priv = dev->dev_private;
        struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
-       int reg = DSPCNTR(intel_crtc->plane);
+       struct intel_plane_regs *regs = &intel_crtc->primary_regs;

        if (!intel_crtc->primary_disabled)
                return;
@@ -486,21 +582,26 @@ intel_enable_primary(struct drm_crtc *crtc)
        intel_crtc->primary_disabled = false;
        intel_update_fbc(dev);

-       I915_WRITE(reg, I915_READ(reg) | DISPLAY_PLANE_ENABLE);
+       regs->cntr |= DISPLAY_PLANE_ENABLE;
+
+       // FIXME
+       intel_plane_commit(crtc);
 }

 static void
 intel_disable_primary(struct drm_crtc *crtc)
 {
        struct drm_device *dev = crtc->dev;
-       struct drm_i915_private *dev_priv = dev->dev_private;
        struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
-       int reg = DSPCNTR(intel_crtc->plane);
+       struct intel_plane_regs *regs = &intel_crtc->primary_regs;

        if (intel_crtc->primary_disabled)
                return;

-       I915_WRITE(reg, I915_READ(reg) & ~DISPLAY_PLANE_ENABLE);
+       regs->cntr &= ~DISPLAY_PLANE_ENABLE;
+
+       // FIXME
+       intel_plane_commit(crtc);

        intel_crtc->primary_disabled = true;
        intel_update_fbc(dev);
@@ -512,49 +613,39 @@ ilk_update_colorkey(struct drm_plane *plane,
 {
        struct drm_device *dev = plane->dev;
        struct drm_i915_private *dev_priv = dev->dev_private;
-       struct intel_plane *intel_plane;
-       u32 dvscntr;
-       int ret = 0;
-
-       intel_plane = to_intel_plane(plane);
+       struct intel_plane *intel_plane = to_intel_plane(plane);
+       struct intel_plane_regs *regs = &intel_plane->regs;

-       I915_WRITE(DVSKEYVAL(intel_plane->pipe), key->min_value);
-       I915_WRITE(DVSKEYMAX(intel_plane->pipe), key->max_value);
-       I915_WRITE(DVSKEYMSK(intel_plane->pipe), key->channel_mask);
+       regs->keyval = key->min_value;
+       regs->keymaxval = key->max_value;
+       regs->keymsk = key->channel_mask;

-       dvscntr = I915_READ(DVSCNTR(intel_plane->pipe));
-       dvscntr &= ~(DVS_SOURCE_KEY | DVS_DEST_KEY);
+       regs->cntr &= ~(DVS_SOURCE_KEY | DVS_DEST_KEY);
        if (key->flags & I915_SET_COLORKEY_DESTINATION)
-               dvscntr |= DVS_DEST_KEY;
+               regs->cntr |= DVS_DEST_KEY;
        else if (key->flags & I915_SET_COLORKEY_SOURCE)
-               dvscntr |= DVS_SOURCE_KEY;
-       I915_WRITE(DVSCNTR(intel_plane->pipe), dvscntr);
+               regs->cntr |= DVS_SOURCE_KEY;

+       ilk_commit_plane(plane, plane->fb);
        POSTING_READ(DVSKEYMSK(intel_plane->pipe));

-       return ret;
+       return 0;
 }

 static void
 ilk_get_colorkey(struct drm_plane *plane, struct drm_intel_sprite_colorkey 
*key)
 {
-       struct drm_device *dev = plane->dev;
-       struct drm_i915_private *dev_priv = dev->dev_private;
-       struct intel_plane *intel_plane;
-       u32 dvscntr;
-
-       intel_plane = to_intel_plane(plane);
+       struct intel_plane *intel_plane = to_intel_plane(plane);
+       const struct intel_plane_regs *regs = &intel_plane->regs;

-       key->min_value = I915_READ(DVSKEYVAL(intel_plane->pipe));
-       key->max_value = I915_READ(DVSKEYMAX(intel_plane->pipe));
-       key->channel_mask = I915_READ(DVSKEYMSK(intel_plane->pipe));
+       key->min_value = regs->keyval;
+       key->max_value = regs->keymaxval;
+       key->channel_mask = regs->keymsk;
        key->flags = 0;

-       dvscntr = I915_READ(DVSCNTR(intel_plane->pipe));
-
-       if (dvscntr & DVS_DEST_KEY)
+       if (regs->cntr & DVS_DEST_KEY)
                key->flags = I915_SET_COLORKEY_DESTINATION;
-       else if (dvscntr & DVS_SOURCE_KEY)
+       else if (regs->cntr & DVS_SOURCE_KEY)
                key->flags = I915_SET_COLORKEY_SOURCE;
        else
                key->flags = I915_SET_COLORKEY_NONE;
@@ -803,6 +894,7 @@ static uint32_t snb_plane_formats[] = {
 int
 intel_plane_init(struct drm_device *dev, enum pipe pipe)
 {
+       struct drm_i915_private *dev_priv = dev->dev_private;
        struct intel_plane *intel_plane;
        unsigned long possible_crtcs;
        const uint32_t *plane_formats;
@@ -825,6 +917,9 @@ intel_plane_init(struct drm_device *dev, enum pipe pipe)
                intel_plane->update_colorkey = ilk_update_colorkey;
                intel_plane->get_colorkey = ilk_get_colorkey;

+               intel_plane->regs.cntr = I915_READ(DVSCNTR(pipe));
+               intel_plane->regs.surf = I915_READ(DVSSURF(pipe));
+
                if (IS_GEN6(dev)) {
                        plane_formats = snb_plane_formats;
                        num_plane_formats = ARRAY_SIZE(snb_plane_formats);
@@ -841,6 +936,9 @@ intel_plane_init(struct drm_device *dev, enum pipe pipe)
                intel_plane->update_colorkey = ivb_update_colorkey;
                intel_plane->get_colorkey = ivb_get_colorkey;

+               intel_plane->regs.cntr = I915_READ(SPRCTL(pipe));
+               intel_plane->regs.surf = I915_READ(SPRSURF(pipe));
+
                plane_formats = snb_plane_formats;
                num_plane_formats = ARRAY_SIZE(snb_plane_formats);
                break;
-- 
1.7.8.6

Reply via email to