On Wed, 29 Apr 2009 15:09:33 -0700
Jesse Barnes <jbar...@virtuousgeek.org> wrote:
> I'm still working through mutlihead issues on the kernel side; the
> flip waits should wait for *both* vblank events before completing the
> flip. But other than that, I'm pretty happy with things.
This incremental set fixes up the multihead handling and adds swap
interval support as a bonus. It's nice to see flipping & no tearing on
two heads at once!
--
Jesse Barnes, Intel Open Source Technology Center
diff --git a/libdrm/intel/intel_bufmgr.c b/libdrm/intel/intel_bufmgr.c
index 25a6828..80d5d3f 100644
--- a/libdrm/intel/intel_bufmgr.c
+++ b/libdrm/intel/intel_bufmgr.c
@@ -36,6 +36,7 @@
#include <errno.h>
#include <drm.h>
#include <i915_drm.h>
+#include <xf86drm.h>
#include "intel_bufmgr.h"
#include "intel_bufmgr_priv.h"
@@ -212,3 +213,16 @@ int drm_intel_bo_get_tiling(drm_intel_bo *bo, uint32_t *tiling_mode,
*swizzle_mode = I915_BIT_6_SWIZZLE_NONE;
return 0;
}
+
+int drm_intel_flip_bufs(int fd, int *crtc_ids, int crtc_count, int fb_id)
+{
+ struct drm_i915_gem_page_flip flip;
+
+ flip.fb_id = fb_id;
+ flip.crtc_ids = (uint32_t *)crtc_ids;
+ flip.crtc_count = (uint32_t)crtc_count;
+ flip.flags = 0;
+ flip.interval = 1;
+
+ return drmCommandWrite(fd, DRM_I915_GEM_PAGE_FLIP, &flip, sizeof(flip));
+}
diff --git a/libdrm/intel/intel_bufmgr.h b/libdrm/intel/intel_bufmgr.h
index 542dc06..1e32351 100644
--- a/libdrm/intel/intel_bufmgr.h
+++ b/libdrm/intel/intel_bufmgr.h
@@ -107,6 +107,7 @@ int drm_intel_bo_set_tiling(drm_intel_bo *bo, uint32_t *tiling_mode,
int drm_intel_bo_get_tiling(drm_intel_bo *bo, uint32_t *tiling_mode,
uint32_t *swizzle_mode);
int drm_intel_bo_flink(drm_intel_bo *bo, uint32_t *name);
+int drm_intel_flip_bufs(int fd, int *crtc_ids, int crtc_count, int fb_id);
/* drm_intel_bufmgr_gem.c */
drm_intel_bufmgr *drm_intel_bufmgr_gem_init(int fd, int batch_size);
diff --git a/shared-core/i915_drm.h b/shared-core/i915_drm.h
index 5456e91..066e448 100644
--- a/shared-core/i915_drm.h
+++ b/shared-core/i915_drm.h
@@ -205,6 +205,7 @@ typedef struct drm_i915_sarea {
#define DRM_I915_GEM_GET_TILING 0x22
#define DRM_I915_GEM_GET_APERTURE 0x23
#define DRM_I915_GEM_MMAP_GTT 0x24
+#define DRM_I915_GEM_PAGE_FLIP 0x25
#define DRM_IOCTL_I915_INIT DRM_IOW( DRM_COMMAND_BASE + DRM_I915_INIT, drm_i915_init_t)
#define DRM_IOCTL_I915_FLUSH DRM_IO ( DRM_COMMAND_BASE + DRM_I915_FLUSH)
@@ -769,4 +770,29 @@ struct drm_i915_gem_get_aperture {
uint64_t aper_available_size;
};
+#define I915_PAGE_FLIP_WAIT (1<<0) /* block on page flip completion */
+#define I915_PAGE_FLIP_ASYNC (1<<1) /* flip immediately */
+
+struct drm_i915_gem_page_flip {
+ /** Handle of new front buffer */
+ uint32_t fb_id;
+
+ /**
+ * crtcs to flip
+ */
+ uint32_t crtc_count;
+ uint32_t *crtc_ids;
+
+ /**
+ * interval of the flip, i.e. this flip won't complete until
+ * @interval frames have occurred
+ */
+ uint32_t interval;
+
+ /**
+ * page flip flags (wait on flip only for now)
+ */
+ uint32_t flags;
+};
+
#endif /* _I915_DRM_H_ */
diff --git a/drivers/gpu/drm/drm_crtc_helper.c b/drivers/gpu/drm/drm_crtc_helper.c
index a04639d..b4f1ae7 100644
--- a/drivers/gpu/drm/drm_crtc_helper.c
+++ b/drivers/gpu/drm/drm_crtc_helper.c
@@ -593,8 +593,10 @@ bool drm_crtc_helper_set_mode(struct drm_crtc *crtc,
if (drm_mode_equal(&saved_mode, &crtc->mode)) {
if (saved_x != crtc->x || saved_y != crtc->y ||
depth_changed || bpp_changed) {
+ mutex_lock(&dev->struct_mutex);
ret = !crtc_funcs->mode_set_base(crtc, crtc->x, crtc->y,
old_fb);
+ mutex_unlock(&dev->struct_mutex);
goto done;
}
}
@@ -864,8 +866,10 @@ int drm_crtc_helper_set_config(struct drm_mode_set *set)
old_fb = set->crtc->fb;
if (set->crtc->fb != set->fb)
set->crtc->fb = set->fb;
+ mutex_lock(&dev->struct_mutex);
ret = crtc_funcs->mode_set_base(set->crtc,
set->x, set->y, old_fb);
+ mutex_unlock(&dev->struct_mutex);
if (ret != 0)
goto fail_set_mode;
}
diff --git a/drivers/gpu/drm/i915/i915_dma.c b/drivers/gpu/drm/i915/i915_dma.c
index 2726143..bf25d39 100644
--- a/drivers/gpu/drm/i915/i915_dma.c
+++ b/drivers/gpu/drm/i915/i915_dma.c
@@ -871,6 +871,186 @@ static int i915_set_status_page(struct drm_device *dev, void *data,
return 0;
}
+int
+i915_gem_flip_pending_locked(struct drm_i915_gem_object *obj_priv)
+{
+ int pending = 0;
+ int pipe;
+
+ for (pipe = 0; pipe < 2; pipe++)
+ if (!list_empty(&obj_priv->vblank_head[pipe]))
+ pending++;
+
+ return pending;
+}
+
+int
+i915_gem_flip_pending(struct drm_gem_object *obj)
+{
+ struct drm_device *dev = obj->dev;
+ drm_i915_private_t *dev_priv = dev->dev_private;
+ unsigned long flags;
+ int pending = 0;
+
+ spin_lock_irqsave(&dev_priv->vblank_lock, flags);
+ pending = i915_gem_flip_pending_locked(obj->driver_private);
+ spin_unlock_irqrestore(&dev_priv->vblank_lock, flags);
+
+ return pending;
+}
+
+static int i915_gem_page_flip(struct drm_device *dev, void *data,
+ struct drm_file *file_priv)
+{
+ struct drm_i915_gem_page_flip *flip_data = data;
+ drm_i915_private_t *dev_priv = dev->dev_private;
+ struct drm_mode_object *drm_obj, *fb_obj;
+ struct drm_crtc *crtc;
+ struct intel_crtc *intel_crtc;
+ struct intel_framebuffer *intel_fb;
+ struct drm_framebuffer *old_fb;
+ struct drm_i915_gem_object *obj_priv;
+ unsigned long flags;
+ unsigned int pipe;
+ u32 frame, crtc_ids[2];
+ int i, ret = 0;
+ struct drm_crtc_helper_funcs *crtc_funcs;
+ bool async_flip = false, block_on_flip = false;
+
+ if (!(drm_core_check_feature(dev, DRIVER_MODESET)))
+ return -ENODEV;
+
+ /*
+ * Reject unknown flags so future userspace knows what we (don't)
+ * support
+ */
+ if (flip_data->flags & (~I915_PAGE_FLIP_FLAGS_MASK)) {
+ DRM_DEBUG("bad page flip flags\n");
+ return -EINVAL;
+ }
+
+ if (flip_data->interval == 0) {
+ DRM_DEBUG("bad flip interval\n");
+ return -EINVAL;
+ }
+
+ if (flip_data->crtc_count > 2) {
+ DRM_DEBUG("bad crtc count\n");
+ return -EINVAL;
+ }
+
+ ret = copy_from_user(crtc_ids, flip_data->crtc_ids,
+ sizeof(u32) * flip_data->crtc_count);
+ if (ret)
+ return -EFAULT;
+
+ if (flip_data->flags & I915_PAGE_FLIP_WAIT)
+ block_on_flip = true;
+ if (flip_data->flags & I915_PAGE_FLIP_ASYNC)
+ async_flip = true;
+
+ mutex_lock(&dev->struct_mutex);
+
+ fb_obj = drm_mode_object_find(dev, flip_data->fb_id,
+ DRM_MODE_OBJECT_FB);
+ if (!fb_obj) {
+ DRM_DEBUG("unknown fb %d\n", flip_data->fb_id);
+ ret = -ENOENT;
+ goto out_unlock;
+ }
+
+ intel_fb = to_intel_framebuffer(obj_to_fb(fb_obj));
+ obj_priv = intel_fb->obj->driver_private;
+
+ /* Sanity check tiling */
+ if (obj_priv->tiling_mode != I915_TILING_NONE &&
+ obj_priv->tiling_mode != I915_TILING_X) {
+ DRM_ERROR("can only flip non-tiled or X tiled pages\n");
+ ret = -EINVAL;
+ goto out_unlock;
+ }
+
+ for (i = 0; i < flip_data->crtc_count; i++) {
+ drm_obj = drm_mode_object_find(dev, crtc_ids[i],
+ DRM_MODE_OBJECT_CRTC);
+ if (!drm_obj) {
+ DRM_DEBUG("unknown crtc %d\n", flip_data->crtc_ids[i]);
+ ret = -ENOENT;
+ goto out_unlock;
+ }
+ crtc = obj_to_crtc(drm_obj);
+ if (!crtc->enabled) {
+ DRM_ERROR("crtc %d not enabled\n",
+ flip_data->crtc_ids[i]);
+ ret = -EINVAL;
+ goto out_unlock;
+ }
+ old_fb = crtc->fb;
+ intel_crtc = to_intel_crtc(crtc);
+ pipe = intel_crtc->pipe;
+
+ crtc->fb = obj_to_fb(fb_obj);
+
+ /*
+ * We always need to wait for previous flips since we'll
+ * be overwriting the flip data
+ */
+retry:
+ if (i915_gem_flip_pending(intel_fb->obj)) {
+ mutex_unlock(&dev->struct_mutex);
+ wait_for_completion(&obj_priv->vblank);
+ mutex_lock(&dev->struct_mutex);
+ goto retry;
+ }
+
+ /* Get vblank ref for completion handling */
+ ret = drm_vblank_get(dev, pipe);
+ if (ret) {
+ DRM_ERROR("failed to take vblank ref\n");
+ goto out_unlock;
+ }
+
+ /*
+ * On an async flip we don't bother to track the flip data.
+ * This may mean drawing into a buffer during retrace, but
+ * OMG MOAR FPS!
+ */
+ if (!async_flip) {
+ spin_lock_irqsave(&dev_priv->vblank_lock, flags);
+
+ /* Should be in the clear at this point, check */
+ WARN(i915_gem_flip_pending_locked(obj_priv),
+ "flip pending not clear at flip request time\n");
+
+ frame = drm_vblank_count(dev, pipe);
+ obj_priv->flip_frame[pipe] =
+ frame + flip_data->interval;
+ list_add_tail(&obj_priv->vblank_head[pipe],
+ &dev_priv->mm.vblank_list[pipe]);
+ spin_unlock_irqrestore(&dev_priv->vblank_lock, flags);
+ }
+
+ crtc_funcs = crtc->helper_private;
+ ret = crtc_funcs->mode_set_base(crtc, 0, 0, old_fb);
+ if (ret) {
+ DRM_ERROR("mode_set_base failed: %d\n", ret);
+ goto out_unlock;
+ }
+ }
+
+ mutex_unlock(&dev->struct_mutex);
+
+ if (block_on_flip && i915_gem_flip_pending(intel_fb->obj))
+ wait_for_completion(&obj_priv->vblank);
+
+ return 0;
+
+out_unlock:
+ mutex_unlock(&dev->struct_mutex);
+
+ return ret;
+}
+
/**
* i915_probe_agp - get AGP bootup configuration
* @pdev: PCI device
@@ -1350,6 +1530,7 @@ struct drm_ioctl_desc i915_ioctls[] = {
DRM_IOCTL_DEF(DRM_I915_GEM_SET_TILING, i915_gem_set_tiling, 0),
DRM_IOCTL_DEF(DRM_I915_GEM_GET_TILING, i915_gem_get_tiling, 0),
DRM_IOCTL_DEF(DRM_I915_GEM_GET_APERTURE, i915_gem_get_aperture_ioctl, 0),
+ DRM_IOCTL_DEF(DRM_I915_GEM_PAGE_FLIP, i915_gem_page_flip, DRM_AUTH|DRM_MASTER),
};
int i915_max_ioctl = DRM_ARRAY_SIZE(i915_ioctls);
diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h
index d7471fe..b77fa2a 100644
--- a/drivers/gpu/drm/i915/i915_drv.h
+++ b/drivers/gpu/drm/i915/i915_drv.h
@@ -162,6 +162,10 @@ typedef struct drm_i915_private {
u32 hotplug_supported_mask;
struct work_struct hotplug_work;
+ /** Protects vblank list */
+ spinlock_t vblank_lock;
+ struct work_struct vblank_work;
+
int tex_lru_log_granularity;
int allow_batchbuffer;
struct mem_block *agp_heap;
@@ -341,6 +345,11 @@ typedef struct drm_i915_private {
*/
struct delayed_work retire_work;
+ /**
+ * List of objects waiting on vblank events (one per pipe)
+ */
+ struct list_head vblank_list[2];
+
uint32_t next_gem_seqno;
/**
@@ -392,6 +401,11 @@ struct drm_i915_gem_object {
/** This object's place on the active/flushing/inactive lists */
struct list_head list;
+ /** Object's place on the vblank lists (protected by vblank_lock)*/
+ struct list_head vblank_head[2];
+ /** vblank frame to wait for */
+ u32 flip_frame[2];
+
/**
* This is set if the object is on the active or flushing lists
* (has pending rendering), and is not set if it's on inactive (ready
@@ -465,6 +479,9 @@ struct drm_i915_gem_object {
/** for phy allocated objects */
struct drm_i915_gem_phys_object *phys_obj;
+ /** for page flips and other vblank related blocks */
+ struct completion vblank;
+
/**
* Used for checking the object doesn't appear more than once
* in an execbuffer object list.
@@ -529,6 +546,8 @@ extern long i915_compat_ioctl(struct file *filp, unsigned int cmd,
extern int i915_emit_box(struct drm_device *dev,
struct drm_clip_rect *boxes,
int i, int DR1, int DR4);
+extern int i915_gem_flip_pending(struct drm_gem_object *obj);
+extern int i915_gem_flip_pending_locked(struct drm_i915_gem_object *obj_priv);
/* i915_irq.c */
extern int i915_irq_emit(struct drm_device *dev, void *data,
diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c
index 4642115..98f4051 100644
--- a/drivers/gpu/drm/i915/i915_gem.c
+++ b/drivers/gpu/drm/i915/i915_gem.c
@@ -3232,6 +3232,26 @@ i915_gem_execbuffer(struct drm_device *dev, void *data,
if (ret != 0)
goto pre_mutex_err;
+ /* Look up object handles */
+ for (i = 0; i < args->buffer_count; i++) {
+ object_list[i] = drm_gem_object_lookup(dev, file_priv,
+ exec_list[i].handle);
+ if (object_list[i] == NULL) {
+ DRM_ERROR("Invalid object handle %d at index %d\n",
+ exec_list[i].handle, i);
+ ret = -EBADF;
+ mutex_lock(&dev->struct_mutex);
+ goto err;
+ }
+
+ if (i915_gem_flip_pending(object_list[i])) {
+ struct drm_i915_gem_object *obj_priv;
+
+ obj_priv = object_list[i]->driver_private;
+ wait_for_completion(&obj_priv->vblank);
+ }
+ }
+
mutex_lock(&dev->struct_mutex);
i915_verify_inactive(dev, __FILE__, __LINE__);
@@ -3250,17 +3270,8 @@ i915_gem_execbuffer(struct drm_device *dev, void *data,
goto pre_mutex_err;
}
- /* Look up object handles */
+ /* Sanity check list for double entries */
for (i = 0; i < args->buffer_count; i++) {
- object_list[i] = drm_gem_object_lookup(dev, file_priv,
- exec_list[i].handle);
- if (object_list[i] == NULL) {
- DRM_ERROR("Invalid object handle %d at index %d\n",
- exec_list[i].handle, i);
- ret = -EBADF;
- goto err;
- }
-
obj_priv = object_list[i]->driver_private;
if (obj_priv->in_execbuffer) {
DRM_ERROR("Object %p appears more than once in object list\n",
@@ -3697,6 +3708,10 @@ int i915_gem_init_object(struct drm_gem_object *obj)
obj_priv->obj = obj;
obj_priv->fence_reg = I915_FENCE_REG_NONE;
INIT_LIST_HEAD(&obj_priv->list);
+ INIT_LIST_HEAD(&obj_priv->vblank_head[0]);
+ INIT_LIST_HEAD(&obj_priv->vblank_head[1]);
+
+ init_completion(&obj_priv->vblank);
return 0;
}
@@ -3758,8 +3773,10 @@ int
i915_gem_idle(struct drm_device *dev)
{
drm_i915_private_t *dev_priv = dev->dev_private;
+ struct drm_i915_gem_object *obj_priv, *tmp;
+ unsigned long irqflags;
uint32_t seqno, cur_seqno, last_seqno;
- int stuck, ret;
+ int stuck, ret, pipe;
mutex_lock(&dev->struct_mutex);
@@ -3773,9 +3790,23 @@ i915_gem_idle(struct drm_device *dev)
*/
dev_priv->mm.suspended = 1;
+ mutex_unlock(&dev->struct_mutex);
+
+ /* Wait for any outstanding flips */
+ spin_lock_irqsave(&dev_priv->vblank_lock, irqflags);
+ for (pipe = 0; pipe < 2; pipe++) {
+ list_for_each_entry_safe(obj_priv, tmp,
+ &dev_priv->mm.vblank_list[pipe],
+ vblank_head[pipe]) {
+ spin_unlock_irqrestore(&dev_priv->vblank_lock, irqflags);
+ wait_for_completion(&obj_priv->vblank);
+ spin_lock_irqsave(&dev_priv->vblank_lock, irqflags);
+ }
+ }
+ spin_unlock_irqrestore(&dev_priv->vblank_lock, irqflags);
+
/* Cancel the retire work handler, wait for it to finish if running
*/
- mutex_unlock(&dev->struct_mutex);
cancel_delayed_work_sync(&dev_priv->mm.retire_work);
mutex_lock(&dev->struct_mutex);
@@ -4142,9 +4173,12 @@ i915_gem_load(struct drm_device *dev)
INIT_LIST_HEAD(&dev_priv->mm.flushing_list);
INIT_LIST_HEAD(&dev_priv->mm.inactive_list);
INIT_LIST_HEAD(&dev_priv->mm.request_list);
+ INIT_LIST_HEAD(&dev_priv->mm.vblank_list[0]);
+ INIT_LIST_HEAD(&dev_priv->mm.vblank_list[1]);
INIT_DELAYED_WORK(&dev_priv->mm.retire_work,
i915_gem_retire_work_handler);
dev_priv->mm.next_gem_seqno = 1;
+ spin_lock_init(&dev_priv->vblank_lock);
/* Old X drivers will take 0-2 for front, back, depth buffers */
dev_priv->fence_reg_start = 3;
diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c
index 98bb4c8..5e29d29 100644
--- a/drivers/gpu/drm/i915/i915_irq.c
+++ b/drivers/gpu/drm/i915/i915_irq.c
@@ -196,6 +196,48 @@ static void i915_hotplug_work_func(struct work_struct *work)
drm_sysfs_hotplug_event(dev);
}
+#define vblank_after(a,b) ((long)(b) - (long)(a) < 0)
+
+static void i915_handle_flip_locked(struct drm_device *dev,
+ struct drm_i915_gem_object *obj_priv,
+ int pipe)
+{
+ if (vblank_after(drm_vblank_count(dev, pipe),
+ obj_priv->flip_frame[pipe])) {
+ list_del_init(&obj_priv->vblank_head[pipe]);
+ drm_vblank_put(dev, pipe);
+ /*
+ * If all the vblanks for this object have completed,
+ * we can wake up any waiters.
+ */
+ if (!i915_gem_flip_pending_locked(obj_priv))
+ complete(&obj_priv->vblank);
+ }
+}
+
+static void i915_vblank_work_func(struct work_struct *work)
+{
+ drm_i915_private_t *dev_priv = container_of(work, drm_i915_private_t,
+ vblank_work);
+ struct drm_device *dev = dev_priv->dev;
+ struct drm_i915_gem_object *obj_priv, *tmp;
+ unsigned long irqflags;
+ int pipe;
+
+ mutex_lock(&dev->struct_mutex);
+
+ spin_lock_irqsave(&dev_priv->vblank_lock, irqflags);
+ for (pipe = 0; pipe < 2; pipe++) {
+ list_for_each_entry_safe(obj_priv, tmp,
+ &dev_priv->mm.vblank_list[pipe],
+ vblank_head[pipe]) {
+ i915_handle_flip_locked(dev, obj_priv, pipe);
+ }
+ }
+ spin_unlock_irqrestore(&dev_priv->vblank_lock, irqflags);
+ mutex_unlock(&dev->struct_mutex);
+}
+
irqreturn_t i915_driver_irq_handler(DRM_IRQ_ARGS)
{
struct drm_device *dev = (struct drm_device *) arg;
@@ -292,6 +334,9 @@ irqreturn_t i915_driver_irq_handler(DRM_IRQ_ARGS)
drm_handle_vblank(dev, 1);
}
+ if (vblank)
+ schedule_work(&dev_priv->vblank_work);
+
if ((pipeb_stats & I915_LEGACY_BLC_EVENT_STATUS) ||
(iir & I915_ASLE_INTERRUPT))
opregion_asle_intr(dev);
@@ -563,6 +608,7 @@ void i915_driver_irq_preinstall(struct drm_device * dev)
I915_WRITE(IER, 0x0);
(void) I915_READ(IER);
INIT_WORK(&dev_priv->hotplug_work, i915_hotplug_work_func);
+ INIT_WORK(&dev_priv->vblank_work, i915_vblank_work_func);
}
int i915_driver_irq_postinstall(struct drm_device *dev)
diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c
index bdcda36..0178da5 100644
--- a/drivers/gpu/drm/i915/intel_display.c
+++ b/drivers/gpu/drm/i915/intel_display.c
@@ -662,6 +662,8 @@ intel_pipe_set_base(struct drm_crtc *crtc, int x, int y,
u32 dspcntr, alignment;
int ret;
+ BUG_ON(!mutex_is_locked(&dev->struct_mutex));
+
/* no fb bound */
if (!crtc->fb) {
DRM_DEBUG("No FB bound\n");
@@ -697,17 +699,14 @@ intel_pipe_set_base(struct drm_crtc *crtc, int x, int y,
BUG();
}
- mutex_lock(&dev->struct_mutex);
ret = i915_gem_object_pin(intel_fb->obj, alignment);
if (ret != 0) {
- mutex_unlock(&dev->struct_mutex);
return ret;
}
ret = i915_gem_object_set_to_gtt_domain(intel_fb->obj, 1);
if (ret != 0) {
i915_gem_object_unpin(intel_fb->obj);
- mutex_unlock(&dev->struct_mutex);
return ret;
}
@@ -731,7 +730,6 @@ intel_pipe_set_base(struct drm_crtc *crtc, int x, int y,
default:
DRM_ERROR("Unknown color depth\n");
i915_gem_object_unpin(intel_fb->obj);
- mutex_unlock(&dev->struct_mutex);
return -EINVAL;
}
if (IS_I965G(dev)) {
@@ -765,7 +763,6 @@ intel_pipe_set_base(struct drm_crtc *crtc, int x, int y,
intel_fb = to_intel_framebuffer(old_fb);
i915_gem_object_unpin(intel_fb->obj);
}
- mutex_unlock(&dev->struct_mutex);
if (!dev->primary->master)
return 0;
@@ -1314,7 +1311,9 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc,
I915_WRITE(dspcntr_reg, dspcntr);
/* Flush the plane changes */
+ mutex_lock(&dev->struct_mutex);
ret = intel_pipe_set_base(crtc, x, y, old_fb);
+ mutex_unlock(&dev->struct_mutex);
if (ret != 0)
return ret;
diff --git a/include/drm/i915_drm.h b/include/drm/i915_drm.h
index 95962fa..8e7f389 100644
--- a/include/drm/i915_drm.h
+++ b/include/drm/i915_drm.h
@@ -184,6 +184,7 @@ typedef struct _drm_i915_sarea {
#define DRM_I915_GEM_GET_TILING 0x22
#define DRM_I915_GEM_GET_APERTURE 0x23
#define DRM_I915_GEM_MMAP_GTT 0x24
+#define DRM_I915_GEM_PAGE_FLIP 0x25
#define DRM_IOCTL_I915_INIT DRM_IOW( DRM_COMMAND_BASE + DRM_I915_INIT, drm_i915_init_t)
#define DRM_IOCTL_I915_FLUSH DRM_IO ( DRM_COMMAND_BASE + DRM_I915_FLUSH)
@@ -219,6 +220,7 @@ typedef struct _drm_i915_sarea {
#define DRM_IOCTL_I915_GEM_SET_TILING DRM_IOWR (DRM_COMMAND_BASE + DRM_I915_GEM_SET_TILING, struct drm_i915_gem_set_tiling)
#define DRM_IOCTL_I915_GEM_GET_TILING DRM_IOWR (DRM_COMMAND_BASE + DRM_I915_GEM_GET_TILING, struct drm_i915_gem_get_tiling)
#define DRM_IOCTL_I915_GEM_GET_APERTURE DRM_IOR (DRM_COMMAND_BASE + DRM_I915_GEM_GET_APERTURE, struct drm_i915_gem_get_aperture)
+#define DRM_IOCTL_I915_GEM_PAGE_FLIP DRM_IOW (DRM_COMMAND_BASE + DRM_I915_GEM_PAGE_FLIP, struct drm_i915_gem_page_flip)
/* Allow drivers to submit batchbuffers directly to hardware, relying
* on the security mechanisms provided by hardware.
@@ -657,4 +659,29 @@ struct drm_i915_gem_get_aperture {
__u64 aper_available_size;
};
+#define I915_PAGE_FLIP_WAIT (1<<0) /* block on page flip completion */
+#define I915_PAGE_FLIP_ASYNC (1<<1) /* flip immediately */
+#define I915_PAGE_FLIP_FLAGS_MASK (I915_PAGE_FLIP_WAIT|I915_PAGE_FLIP_ASYNC)
+
+struct drm_i915_gem_page_flip {
+ /** Handle of new front buffer */
+ uint32_t fb_id;
+ /**
+ * crtcs to flip
+ */
+ uint32_t crtc_count;
+ uint32_t *crtc_ids;
+
+ /**
+ * interval of the flip, i.e. this flip won't complete until
+ * @interval frames have occurred
+ */
+ uint32_t interval;
+
+ /**
+ * page flip flags (wait on flip only for now)
+ */
+ uint32_t flags;
+};
+
#endif /* _I915_DRM_H_ */
diff --git a/src/drmmode_display.c b/src/drmmode_display.c
index 15ffc29..20cc9bc 100644
--- a/src/drmmode_display.c
+++ b/src/drmmode_display.c
@@ -29,6 +29,8 @@
#include "config.h"
#endif
+#include <errno.h>
+
#include "xorgVersion.h"
#include "i830.h"
@@ -338,6 +340,7 @@ static PixmapPtr
drmmode_crtc_shadow_create(xf86CrtcPtr crtc, void *data, int width, int height)
{
ScrnInfoPtr pScrn = crtc->scrn;
+ I830Ptr pI830 = I830PTR(pScrn);
drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
drmmode_ptr drmmode = drmmode_crtc->drmmode;
unsigned long rotate_pitch;
@@ -363,12 +366,16 @@ drmmode_crtc_shadow_create(xf86CrtcPtr crtc, void *data, int width, int height)
if (drmmode_crtc->rotate_bo)
i830_set_pixmap_bo(rotate_pixmap, drmmode_crtc->rotate_bo);
+ pI830->shadow_present = TRUE;
+
return rotate_pixmap;
}
static void
drmmode_crtc_shadow_destroy(xf86CrtcPtr crtc, PixmapPtr rotate_pixmap, void *data)
{
+ ScrnInfoPtr pScrn = crtc->scrn;
+ I830Ptr pI830 = I830PTR(pScrn);
drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
drmmode_ptr drmmode = drmmode_crtc->drmmode;
@@ -385,6 +392,7 @@ drmmode_crtc_shadow_destroy(xf86CrtcPtr crtc, PixmapPtr rotate_pixmap, void *dat
dri_bo_unreference(drmmode_crtc->rotate_bo);
drmmode_crtc->rotate_bo = NULL;
}
+ pI830->shadow_present = FALSE;
}
static void
@@ -886,6 +894,64 @@ drmmode_xf86crtc_resize (ScrnInfoPtr scrn, int width, int height)
return FALSE;
}
+Bool
+drmmode_do_pageflip(DrawablePtr pDraw, dri_bo *new_front, dri_bo *old_front)
+{
+ ScreenPtr pScreen = pDraw->pScreen;
+ ScrnInfoPtr pScrn = xf86Screens[pScreen->myNum];
+ I830Ptr pI830 = I830PTR(pScrn);
+ xf86CrtcConfigPtr config = XF86_CRTC_CONFIG_PTR(pScrn);
+ drmmode_crtc_private_ptr drmmode_crtc = config->crtc[0]->driver_private;
+ drmmode_ptr drmmode = drmmode_crtc->drmmode;
+ unsigned int pitch = pScrn->displayWidth * pI830->cpp;
+ int i, old_fb_id;
+ int crtc_ids[2], crtc_count = 0;
+
+ /*
+ * Create a new handle for the back buffer
+ */
+ old_fb_id = drmmode->fb_id;
+ if (drmModeAddFB(drmmode->fd, pScrn->virtualX, pScrn->virtualY,
+ pScrn->depth, pScrn->bitsPerPixel, pitch,
+ new_front->handle, &drmmode->fb_id))
+ goto error_out;
+
+ /* Queue flips on all enabled CRTCs */
+ /* FIXME: cloned configs? */
+ for (i = 0; i < config->num_crtc; i++) {
+ xf86CrtcPtr crtc = config->crtc[i];
+
+ if (!crtc->enabled)
+ continue;
+
+ drmmode_crtc = crtc->driver_private;
+ crtc_ids[crtc_count++] = drmmode_crtc->mode_crtc->crtc_id;
+ }
+
+ if (drm_intel_flip_bufs(drmmode->fd, crtc_ids, crtc_count, drmmode->fb_id))
+ goto error_undo;
+
+ dri_bo_pin(new_front, 0);
+ dri_bo_unpin(new_front);
+
+ pScrn->fbOffset = new_front->offset;
+ pI830->front_buffer->bo = new_front;
+ pI830->front_buffer->offset = new_front->offset;
+
+ drmModeRmFB(drmmode->fd, old_fb_id);
+
+ return TRUE;
+
+error_undo:
+ drmModeRmFB(drmmode->fd, drmmode->fb_id);
+ drmmode->fb_id = old_fb_id;
+
+error_out:
+ xf86DrvMsg(pScrn->scrnIndex, X_WARNING, "Page flip failed: %s\n",
+ strerror(errno));
+ return FALSE;
+}
+
static const xf86CrtcConfigFuncsRec drmmode_xf86crtc_config_funcs = {
drmmode_xf86crtc_resize
};
@@ -893,8 +959,9 @@ static const xf86CrtcConfigFuncsRec drmmode_xf86crtc_config_funcs = {
Bool drmmode_pre_init(ScrnInfoPtr pScrn, int fd, int cpp)
{
xf86CrtcConfigPtr xf86_config;
+ I830Ptr pI830 = I830PTR(pScrn);
drmmode_ptr drmmode;
- int i;
+ int i, bad_crtc = -1;
drmmode = xnfalloc(sizeof *drmmode);
drmmode->fd = fd;
@@ -918,5 +985,13 @@ Bool drmmode_pre_init(ScrnInfoPtr pScrn, int fd, int cpp)
xf86InitialConfiguration(pScrn, TRUE);
+ /* Check for swapbuffers support */
+ drm_intel_flip_bufs(drmmode->fd, &bad_crtc, 1, -1);
+ if (errno != -EBADF) { /* bad CRTC or FB number */
+ xf86DrvMsg(pScrn->scrnIndex, X_INFO,
+ "Kernel page flipping support detected, enabling\n");
+ pI830->use_swap_buffers = TRUE;
+ }
+
return TRUE;
}
diff --git a/src/i830.h b/src/i830.h
index 207d4ec..0d6ce5a 100644
--- a/src/i830.h
+++ b/src/i830.h
@@ -65,6 +65,7 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#include "sarea.h"
#define _XF86DRI_SERVER_
#include "dri.h"
+#include "dri2.h"
#include "GL/glxint.h"
#include "i830_dri.h"
#include "intel_bufmgr.h"
@@ -397,6 +398,7 @@ typedef struct _I830Rec {
#endif
XF86ModReqInfo shadowReq; /* to test for later libshadow */
+ Bool shadow_present;
Rotation rotation;
void (*PointerMoved)(int, int, int);
CreateScreenResourcesProcPtr CreateScreenResources;
@@ -489,6 +491,8 @@ typedef struct _I830Rec {
int drmSubFD;
char deviceName[64];
+ Bool use_swap_buffers;
+
/* Broken-out options. */
OptionInfoPtr Options;
@@ -678,6 +682,8 @@ Bool I830DRI2ScreenInit(ScreenPtr pScreen);
void I830DRI2CloseScreen(ScreenPtr pScreen);
extern Bool drmmode_pre_init(ScrnInfoPtr pScrn, int fd, int cpp);
+extern Bool drmmode_do_pageflip(DrawablePtr pDraw, dri_bo *new_front,
+ dri_bo *old_front);
extern Bool I830AccelInit(ScreenPtr pScreen);
extern void I830SetupForScreenToScreenCopy(ScrnInfoPtr pScrn, int xdir,
diff --git a/src/i830_dri.c b/src/i830_dri.c
index 0648249..32201f1 100644
--- a/src/i830_dri.c
+++ b/src/i830_dri.c
@@ -45,6 +45,8 @@ USE OR OTHER DEALINGS IN THE SOFTWARE.
#include <errno.h>
#include <unistd.h>
#include <fcntl.h>
+#include <sys/time.h>
+#include <time.h>
#include "xf86.h"
#include "xf86_OSproc.h"
@@ -70,13 +72,12 @@ USE OR OTHER DEALINGS IN THE SOFTWARE.
#define USE_DRI2_1_1_0
#endif
-extern XF86ModuleData dri2ModuleData;
+#if DRI2INFOREC_VERSION >= 2
+#define USE_DRI2_1_2_0
#endif
-typedef struct {
- PixmapPtr pPixmap;
- unsigned int attachment;
-} I830DRI2BufferPrivateRec, *I830DRI2BufferPrivatePtr;
+extern XF86ModuleData dri2ModuleData;
+#endif
#ifndef USE_DRI2_1_1_0
static DRI2BufferPtr
@@ -259,7 +260,7 @@ I830DRI2DestroyBuffers(DrawablePtr pDraw, DRI2BufferPtr buffers, int count)
if (buffers)
{
- xfree(buffers[0].driverPrivate);
+ xfree(buffers[i].driverPrivate);
xfree(buffers);
}
}
@@ -324,6 +325,127 @@ I830DRI2CopyRegion(DrawablePtr pDraw, RegionPtr pRegion,
}
+/* Check various flip constraints (drawable parameters vs screen params) */
+static Bool
+i830_flip_ok(DrawablePtr pDraw)
+{
+ ScreenPtr pScreen = pDraw->pScreen;
+ ScrnInfoPtr pScrn = xf86Screens[pScreen->myNum];
+ I830Ptr pI830 = I830PTR(pScrn);
+
+ if (pI830->shadow_present)
+ return FALSE;
+ if (pDraw->width != pScrn->virtualX)
+ return FALSE;
+ if (pDraw->height != pScrn->virtualY)
+ return FALSE;
+ if (pDraw->depth != pScrn->depth)
+ return FALSE;
+
+ return TRUE;
+}
+
+/*
+ * DRI2SwapBuffers should try to do a buffer swap if possible, however:
+ * - if we're swapping buffers smaller than the screen, we have to blit
+ * - if the back buffer doesn't match the screen depth, we have to blit
+ * - otherwise we try to swap, and return to the caller the new front
+ * and back buffers
+ */
+static DRI2BufferPtr *
+I830DRI2SwapBuffers(DrawablePtr pDraw, DRI2BufferPtr *buffers, int count)
+{
+ DRI2BufferPtr new_buffer, *new_buffer_ptrs;
+ I830DRI2BufferPrivatePtr private, old_priv;
+ PixmapPtr pPixmap, new_front_pixmap, old_front_pixmap;
+ int i, j, front_buffer, back_buffer, tmp;
+ dri_bo *bo, *new_front_bo, *old_front_bo;
+
+ if (!i830_flip_ok(pDraw))
+ return NULL;
+
+ new_buffer_ptrs = xcalloc(count, sizeof(DRI2BufferRec *));
+ if (new_buffer_ptrs == NULL)
+ return NULL;
+
+ for (i = 0; i < count; i++) {
+ old_priv = buffers[i]->driverPrivate;
+ pPixmap = old_priv->pPixmap;
+ pPixmap->refcnt++;
+
+ new_buffer = xcalloc(1, sizeof(DRI2BufferRec));
+ if (!new_buffer) {
+ for (j = 0; j < i; j++) {
+ xfree(new_buffer_ptrs[j]->driverPrivate);
+ xfree(new_buffer_ptrs[j]);
+ }
+ return NULL;
+ }
+
+ private = xcalloc(1, sizeof *private);
+ if (!private) {
+ for (j = 0; j < i; j++) {
+ xfree(new_buffer_ptrs[j]->driverPrivate);
+ xfree(new_buffer_ptrs[j]);
+ }
+ return NULL;
+ }
+
+
+ private->pPixmap = pPixmap;
+ private->attachment = buffers[i]->attachment;
+
+ new_buffer->attachment = buffers[i]->attachment;
+ new_buffer->pitch = buffers[i]->pitch;
+ new_buffer->cpp = buffers[i]->cpp;
+ new_buffer->driverPrivate = private;
+ new_buffer->name = buffers[i]->name;
+
+ bo = i830_get_pixmap_bo(pPixmap);
+
+ new_buffer_ptrs[i] = new_buffer;
+
+ /* Swap the attachment types */
+ if (buffers[i]->attachment == DRI2BufferFrontLeft) {
+ old_front_bo = bo;
+ old_front_pixmap = pPixmap;
+ front_buffer = i;
+ }
+ if (buffers[i]->attachment == DRI2BufferBackLeft) {
+ new_front_bo = bo;
+ new_front_pixmap = pPixmap;
+ back_buffer = i;
+ drm_intel_bo_reference(bo);
+ }
+ }
+
+ /* Swap BO names so DRI works */
+ tmp = new_buffer_ptrs[front_buffer]->name;
+ new_buffer_ptrs[front_buffer]->name = new_buffer_ptrs[back_buffer]->name;
+ new_buffer_ptrs[back_buffer]->name = tmp;
+
+ /* Swap pixmaps */
+ private = new_buffer_ptrs[back_buffer]->driverPrivate;
+ i830_set_pixmap_bo(private->pPixmap, old_front_bo);
+
+ private = new_buffer_ptrs[front_buffer]->driverPrivate;
+ i830_set_pixmap_bo(private->pPixmap, new_front_bo); /* should be screen */
+
+ /* Needed to hold refs around the set_bo calls... */
+ drm_intel_bo_unreference(old_front_bo);
+
+ /* Page flip the full screen buffer */
+ if (!drmmode_do_pageflip(pDraw, new_front_bo, old_front_bo)) {
+ for (i = 0; i < count; i++) {
+ xfree(new_buffer_ptrs[i]->driverPrivate);
+ xfree(new_buffer_ptrs[i]);
+ }
+ return NULL;
+ }
+
+ return new_buffer_ptrs;
+}
+
Bool I830DRI2ScreenInit(ScreenPtr pScreen)
{
ScrnInfoPtr pScrn = xf86Screens[pScreen->myNum];
@@ -423,6 +545,13 @@ Bool I830DRI2ScreenInit(ScreenPtr pScreen)
info.CopyRegion = I830DRI2CopyRegion;
+#ifdef USE_DRI2_1_2_0
+ if (pI830->use_swap_buffers) {
+ info.version = 3;
+ info.SwapBuffers = I830DRI2SwapBuffers;
+ }
+#endif
+
pI830->drmSubFD = info.fd;
return DRI2ScreenInit(pScreen, &info);
diff --git a/src/i830_dri.h b/src/i830_dri.h
index bedbcbe..e35e940 100644
--- a/src/i830_dri.h
+++ b/src/i830_dri.h
@@ -2,6 +2,7 @@
#ifndef _I830_DRI_H
#define _I830_DRI_H
+#include "xorg-server.h"
#include "xf86drm.h"
#include "i830_common.h"
@@ -58,4 +59,9 @@ typedef struct {
int dummy;
} I830DRIContextRec, *I830DRIContextPtr;
+typedef struct {
+ PixmapPtr pPixmap;
+ unsigned int attachment;
+} I830DRI2BufferPrivateRec, *I830DRI2BufferPrivatePtr;
+
#endif
------------------------------------------------------------------------------
Register Now & Save for Velocity, the Web Performance & Operations
Conference from O'Reilly Media. Velocity features a full day of
expert-led, hands-on workshops and two days of sessions from industry
leaders in dedicated Performance & Operations tracks. Use code vel09scf
and Save an extra 15% before 5/3. http://p.sf.net/sfu/velocityconf
--
_______________________________________________
Dri-devel mailing list
Dri-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/dri-devel