From 1a0d890a42bee78177ad45d5e5956d2c3c4fcdc7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michel=20D=C3=A4nzer?= Date: Thu, 22 Feb 2007 17:21:18 +0100 Subject: 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. --- shared-core/i915_dma.c | 17 +++---- shared-core/i915_drv.h | 2 + shared-core/i915_irq.c | 119 ++++++++++++++++++++++++++++++++++++++++++------- 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 @@ -37,6 +37,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. * @@ -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); -- cgit v1.2.3