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.
Michel Dänzer 2007-02-22 17:21:18 +01:00
parent 5a40c043cc
commit 1a0d890a42
3 changed files with 115 additions and 23 deletions

View 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;
}

View 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);

View File

@ -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);