]> Gitweb @ Texas Instruments - Open Source Git Repositories - git.TI.com/gitweb - glsdk/libdrm.git/commitdiff
i915: Add support for scheduled buffer swaps to be done as flips.
authorMichel Dänzer <michel@tungstengraphics.com>
Thu, 22 Feb 2007 16:21:18 +0000 (17:21 +0100)
committerMichel Dänzer <michel@tungstengraphics.com>
Thu, 22 Feb 2007 16:21:18 +0000 (17:21 +0100)
Unfortunately, emitting asynchronous flips during vertical blank results in
tearing. So we have to wait for the previous vertical blank and emit a
synchronous flip.

shared-core/i915_dma.c
shared-core/i915_drv.h
shared-core/i915_irq.c

index dee381e6e3149d36ac598c049281cf43e2e87468..883f3b9de14e50146444d8e419f2be7cb38d10ac 100644 (file)
@@ -551,7 +551,7 @@ static int i915_dispatch_batchbuffer(drm_device_t * dev,
        return 0;
 }
 
-static void i915_do_dispatch_flip(drm_device_t * dev, int pipe)
+static void i915_do_dispatch_flip(drm_device_t * dev, int pipe, int sync)
 {
        drm_i915_private_t *dev_priv = dev->dev_private;
        u32 num_pages, current_page, next_page, dspbase;
@@ -590,9 +590,9 @@ static void i915_do_dispatch_flip(drm_device_t * dev, int pipe)
                  dspbase);
 
        BEGIN_LP_RING(4);
-       OUT_RING(CMD_OP_DISPLAYBUFFER_INFO | ASYNC_FLIP |
+       OUT_RING(CMD_OP_DISPLAYBUFFER_INFO | (sync ? 0 : ASYNC_FLIP) |
                 (pipe ? DISPLAY_PLANE_B : DISPLAY_PLANE_A));
-       OUT_RING(0);
+       OUT_RING(dev_priv->sarea_priv->pitch * dev_priv->cpp);
        OUT_RING(dspbase);
        OUT_RING(0);
        ADVANCE_LP_RING();
@@ -601,7 +601,7 @@ static void i915_do_dispatch_flip(drm_device_t * dev, int pipe)
        dev_priv->current_page |= next_page << shift;
 }
 
-static void i915_dispatch_flip(drm_device_t * dev, int pipes)
+void i915_dispatch_flip(drm_device_t * dev, int pipes, int sync)
 {
        drm_i915_private_t *dev_priv = dev->dev_private;
        u32 mi_wait = MI_WAIT_FOR_EVENT;
@@ -634,11 +634,12 @@ static void i915_dispatch_flip(drm_device_t * dev, int pipes)
 
        for (i = 0; i < 2; i++)
                if (pipes & (1 << i))
-                       i915_do_dispatch_flip(dev, i);
+                       i915_do_dispatch_flip(dev, i, sync);
 
        i915_emit_breadcrumb(dev);
 #ifdef I915_HAVE_FENCE
-       drm_fence_flush_old(dev, 0, dev_priv->counter);
+       if (!sync)
+               drm_fence_flush_old(dev, 0, dev_priv->counter);
 #endif
 
        dev_priv->sarea_priv->pf_current_page = dev_priv->current_page;
@@ -745,7 +746,7 @@ static int i915_do_cleanup_pageflip(drm_device_t * dev)
                        if (dev_priv->current_page & (0x3 << (2 * i)))
                                pipes |= 1 << i;
 
-               i915_dispatch_flip(dev, pipes);
+               i915_dispatch_flip(dev, pipes, 0);
        }
 
        return 0;
@@ -769,7 +770,7 @@ static int i915_flip_bufs(DRM_IOCTL_ARGS)
                return DRM_ERR(EINVAL);
        }
 
-       i915_dispatch_flip(dev, param.pipes);
+       i915_dispatch_flip(dev, param.pipes, 0);
 
        return 0;
 }
index 1b261d6f52e1f4bc7380ea77f322e2ecfce3f9fe..cc6c12d185d1f92dcad9d84274405dccc3ec6edb 100644 (file)
@@ -85,6 +85,7 @@ typedef struct _drm_i915_vbl_swap {
        drm_drawable_t drw_id;
        unsigned int pipe;
        unsigned int sequence;
+       int flip;
 } drm_i915_vbl_swap_t;
 
 typedef struct drm_i915_private {
@@ -151,6 +152,7 @@ extern int i915_driver_device_is_agp(drm_device_t * dev);
 extern long i915_compat_ioctl(struct file *filp, unsigned int cmd,
                              unsigned long arg);
 extern void i915_emit_breadcrumb(drm_device_t *dev);
+extern void i915_dispatch_flip(drm_device_t * dev, int pipes, int sync);
 extern int i915_emit_mi_flush(drm_device_t *dev, uint32_t flush);
 
 
index cd2adbf35a84af4e91c35fff462a90ddb72bd0a2..e6c88d2f751ebc054f9c47dc5014ed9a9aea9442 100644 (file)
 
 #define MAX_NOPID ((u32)~0)
 
+/**
+ * Emit a synchronous flip.
+ *
+ * This function must be called with the drawable spinlock held.
+ */
+static void
+i915_dispatch_vsync_flip(drm_device_t *dev, drm_drawable_info_t *drw, int pipe)
+{
+       drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
+       drm_i915_sarea_t *sarea_priv = dev_priv->sarea_priv;
+       u16 x1, y1, x2, y2;
+       int pf_pipes = 1 << pipe;
+
+       /* If the window is visible on the other pipe, we have to flip on that
+        * pipe as well.
+        */
+       if (pipe == 1) {
+               x1 = sarea_priv->pipeA_x;
+               y1 = sarea_priv->pipeA_y;
+               x2 = x1 + sarea_priv->pipeA_w;
+               y2 = y1 + sarea_priv->pipeA_h;
+       } else {
+               x1 = sarea_priv->pipeB_x;
+               y1 = sarea_priv->pipeB_y;
+               x2 = x1 + sarea_priv->pipeB_w;
+               y2 = y1 + sarea_priv->pipeB_h;
+       }
+
+       if (x2 > 0 && y2 > 0) {
+               int i, num_rects = drw->num_rects;
+               drm_clip_rect_t *rect = drw->rects;
+
+               for (i = 0; i < num_rects; i++)
+                       if (!((rect[i].x1 > x2 && rect[i].y1 > y2) ||
+                             (rect[i].x2 < x1 && rect[i].y2 < y1))) {
+                               pf_pipes = 0x3;
+
+                               break;
+                       }
+       }
+
+       i915_dispatch_flip(dev, pf_pipes, 1);
+}
+
 /**
  * Emit blits for scheduled buffer swaps.
  *
@@ -125,19 +169,6 @@ static void i915_vblank_tasklet(drm_device_t *dev)
 
        i915_kernel_lost_context(dev);
 
-       BEGIN_LP_RING(6);
-
-       OUT_RING(GFX_OP_DRAWRECT_INFO);
-       OUT_RING(0);
-       OUT_RING(0);
-       OUT_RING(sarea_priv->width | sarea_priv->height << 16);
-       OUT_RING(sarea_priv->width | sarea_priv->height << 16);
-       OUT_RING(0);
-
-       ADVANCE_LP_RING();
-
-       sarea_priv->ctxOwner = DRM_KERNEL_CONTEXT;
-
        upper[0] = upper[1] = 0;
        slice[0] = max(sarea_priv->pipeA_h / nhits, 1);
        slice[1] = max(sarea_priv->pipeB_h / nhits, 1);
@@ -159,6 +190,8 @@ static void i915_vblank_tasklet(drm_device_t *dev)
        for (i = 0; i++ < nhits;
             upper[0] = lower[0], lower[0] += slice[0],
             upper[1] = lower[1], lower[1] += slice[1]) {
+               int init_drawrect = 1;
+
                if (i == nhits)
                        lower[0] = lower[1] = sarea_priv->height;
 
@@ -174,8 +207,31 @@ static void i915_vblank_tasklet(drm_device_t *dev)
                        if (!drw)
                                continue;
 
-                       rect = drw->rects;
                        pipe = swap_hit->pipe;
+
+                       if (swap_hit->flip) {
+                               i915_dispatch_vsync_flip(dev, drw, pipe);
+                               continue;
+                       }
+
+                       if (init_drawrect) {
+                               BEGIN_LP_RING(6);
+
+                               OUT_RING(GFX_OP_DRAWRECT_INFO);
+                               OUT_RING(0);
+                               OUT_RING(0);
+                               OUT_RING(sarea_priv->width | sarea_priv->height << 16);
+                               OUT_RING(sarea_priv->width | sarea_priv->height << 16);
+                               OUT_RING(0);
+
+                               ADVANCE_LP_RING();
+
+                               sarea_priv->ctxOwner = DRM_KERNEL_CONTEXT;
+
+                               init_drawrect = 0;
+                       }
+
+                       rect = drw->rects;
                        top = upper[pipe];
                        bottom = lower[pipe];
 
@@ -523,7 +579,8 @@ int i915_vblank_swap(DRM_IOCTL_ARGS)
                                 sizeof(swap));
 
        if (swap.seqtype & ~(_DRM_VBLANK_RELATIVE | _DRM_VBLANK_ABSOLUTE |
-                            _DRM_VBLANK_SECONDARY | _DRM_VBLANK_NEXTONMISS)) {
+                            _DRM_VBLANK_SECONDARY | _DRM_VBLANK_NEXTONMISS |
+                            _DRM_VBLANK_FLIP)) {
                DRM_ERROR("Invalid sequence type 0x%x\n", swap.seqtype);
                return DRM_ERR(EINVAL);
        }
@@ -561,6 +618,33 @@ int i915_vblank_swap(DRM_IOCTL_ARGS)
                }
        }
 
+       if (swap.seqtype & _DRM_VBLANK_FLIP) {
+               swap.sequence--;
+
+               if ((curseq - swap.sequence) <= (1<<23)) {
+                       drm_drawable_info_t *drw;
+
+                       LOCK_TEST_WITH_RETURN(dev, filp);
+
+                       spin_lock_irqsave(&dev->drw_lock, irqflags);
+
+                       drw = drm_get_drawable_info(dev, swap.drawable);
+
+                       if (!drw) {
+                               spin_unlock_irqrestore(&dev->drw_lock, irqflags);
+                               DRM_DEBUG("Invalid drawable ID %d\n",
+                                         swap.drawable);
+                               return DRM_ERR(EINVAL);
+                       }
+
+                       i915_dispatch_vsync_flip(dev, drw, pipe);
+
+                       spin_unlock_irqrestore(&dev->drw_lock, irqflags);
+
+                       return 0;
+               }
+       }
+
        spin_lock_irqsave(&dev_priv->swaps_lock, irqflags);
 
        list_for_each(list, &dev_priv->vbl_swaps.head) {
@@ -569,6 +653,7 @@ int i915_vblank_swap(DRM_IOCTL_ARGS)
                if (vbl_swap->drw_id == swap.drawable &&
                    vbl_swap->pipe == pipe &&
                    vbl_swap->sequence == swap.sequence) {
+                       vbl_swap->flip = (swap.seqtype & _DRM_VBLANK_FLIP);
                        spin_unlock_irqrestore(&dev_priv->swaps_lock, irqflags);
                        DRM_DEBUG("Already scheduled\n");
                        return 0;
@@ -594,6 +679,10 @@ int i915_vblank_swap(DRM_IOCTL_ARGS)
        vbl_swap->drw_id = swap.drawable;
        vbl_swap->pipe = pipe;
        vbl_swap->sequence = swap.sequence;
+       vbl_swap->flip = (swap.seqtype & _DRM_VBLANK_FLIP);
+
+       if (vbl_swap->flip)
+               swap.sequence++;
 
        spin_lock_irqsave(&dev_priv->swaps_lock, irqflags);