summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMichel Dänzer <michel@tungstengraphics.com>2007-02-22 17:21:18 +0100
committerMichel Dänzer <michel@tungstengraphics.com>2007-02-22 17:21:18 +0100
commit1a0d890a42bee78177ad45d5e5956d2c3c4fcdc7 (patch)
tree12519711c9157d33836dae9d170600389164de4e
parent5a40c043ccf965b1c3c74c80828090d2bc4438d4 (diff)
i915: Add support for scheduled buffer swaps to be done as flips.
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.
-rw-r--r--shared-core/i915_dma.c17
-rw-r--r--shared-core/i915_drv.h2
-rw-r--r--shared-core/i915_irq.c119
3 files changed, 115 insertions, 23 deletions
diff --git a/shared-core/i915_dma.c b/shared-core/i915_dma.c
index dee381e6..883f3b9d 100644
--- a/shared-core/i915_dma.c
+++ b/shared-core/i915_dma.c
@@ -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;
}
diff --git a/shared-core/i915_drv.h b/shared-core/i915_drv.h
index 1b261d6f..cc6c12d1 100644
--- a/shared-core/i915_drv.h
+++ b/shared-core/i915_drv.h
@@ -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);
diff --git a/shared-core/i915_irq.c b/shared-core/i915_irq.c
index cd2adbf3..e6c88d2f 100644
--- a/shared-core/i915_irq.c
+++ b/shared-core/i915_irq.c
@@ -38,6 +38,50 @@
#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.
*
* This function will be called with the HW lock held.
@@ -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);