+static void i915_enable_interrupt (drm_device_t *dev)
+{
+ drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
+ u16 flag;
+
+ flag = 0;
+ if (dev_priv->vblank_pipe & DRM_I915_VBLANK_PIPE_A)
+ flag |= VSYNC_PIPEA_FLAG;
+ if (dev_priv->vblank_pipe & DRM_I915_VBLANK_PIPE_B)
+ flag |= VSYNC_PIPEB_FLAG;
+
+ I915_WRITE16(I915REG_INT_ENABLE_R, USER_INT_FLAG | flag);
+}
+
+/* Set the vblank monitor pipe
+ */
+int i915_vblank_pipe_set(DRM_IOCTL_ARGS)
+{
+ DRM_DEVICE;
+ drm_i915_private_t *dev_priv = dev->dev_private;
+ drm_i915_vblank_pipe_t pipe;
+
+ if (!dev_priv) {
+ DRM_ERROR("%s called with no initialization\n", __FUNCTION__);
+ return DRM_ERR(EINVAL);
+ }
+
+ DRM_COPY_FROM_USER_IOCTL(pipe, (drm_i915_vblank_pipe_t __user *) data,
+ sizeof(pipe));
+
+ if (pipe.pipe & ~(DRM_I915_VBLANK_PIPE_A|DRM_I915_VBLANK_PIPE_B)) {
+ DRM_ERROR("%s called with invalid pipe 0x%x\n",
+ __FUNCTION__, pipe.pipe);
+ return DRM_ERR(EINVAL);
+ }
+
+ dev_priv->vblank_pipe = pipe.pipe;
+
+ i915_enable_interrupt (dev);
+
+ return 0;
+}
+
+int i915_vblank_pipe_get(DRM_IOCTL_ARGS)
+{
+ DRM_DEVICE;
+ drm_i915_private_t *dev_priv = dev->dev_private;
+ drm_i915_vblank_pipe_t pipe;
+ u16 flag;
+
+ if (!dev_priv) {
+ DRM_ERROR("%s called with no initialization\n", __FUNCTION__);
+ return DRM_ERR(EINVAL);
+ }
+
+ flag = I915_READ(I915REG_INT_ENABLE_R);
+ pipe.pipe = 0;
+ if (flag & VSYNC_PIPEA_FLAG)
+ pipe.pipe |= DRM_I915_VBLANK_PIPE_A;
+ if (flag & VSYNC_PIPEB_FLAG)
+ pipe.pipe |= DRM_I915_VBLANK_PIPE_B;
+ DRM_COPY_TO_USER_IOCTL((drm_i915_vblank_pipe_t __user *) data, pipe,
+ sizeof(pipe));
+ return 0;
+}
+
+/**
+ * Schedule buffer swap at given vertical blank.
+ */
+int i915_vblank_swap(DRM_IOCTL_ARGS)
+{
+ DRM_DEVICE;
+ drm_i915_private_t *dev_priv = dev->dev_private;
+ drm_i915_vblank_swap_t swap;
+ drm_i915_vbl_swap_t *vbl_swap;
+ unsigned int pipe, seqtype, curseq;
+ unsigned long irqflags;
+ struct list_head *list;
+
+ if (!dev_priv) {
+ DRM_ERROR("%s called with no initialization\n", __func__);
+ return DRM_ERR(EINVAL);
+ }
+
+ if (dev_priv->sarea_priv->rotation) {
+ DRM_DEBUG("Rotation not supported\n");
+ return DRM_ERR(EINVAL);
+ }
+
+ DRM_COPY_FROM_USER_IOCTL(swap, (drm_i915_vblank_swap_t __user *) data,
+ sizeof(swap));
+
+ if (swap.seqtype & ~(_DRM_VBLANK_RELATIVE | _DRM_VBLANK_ABSOLUTE |
+ _DRM_VBLANK_SECONDARY | _DRM_VBLANK_NEXTONMISS)) {
+ DRM_ERROR("Invalid sequence type 0x%x\n", swap.seqtype);
+ return DRM_ERR(EINVAL);
+ }
+
+ pipe = (swap.seqtype & _DRM_VBLANK_SECONDARY) ? 1 : 0;
+
+ seqtype = swap.seqtype & (_DRM_VBLANK_RELATIVE | _DRM_VBLANK_ABSOLUTE);
+
+ if (!(dev_priv->vblank_pipe & (1 << pipe))) {
+ DRM_ERROR("Invalid pipe %d\n", pipe);
+ return DRM_ERR(EINVAL);
+ }
+
+ spin_lock_irqsave(&dev->drw_lock, irqflags);
+
+ if (!drm_get_drawable_info(dev, swap.drawable)) {
+ spin_unlock_irqrestore(&dev->drw_lock, irqflags);
+ DRM_DEBUG("Invalid drawable ID %d\n", swap.drawable);
+ return DRM_ERR(EINVAL);
+ }
+
+ spin_unlock_irqrestore(&dev->drw_lock, irqflags);
+
+ curseq = atomic_read(pipe ? &dev->vbl_received2 : &dev->vbl_received);
+
+ if (seqtype == _DRM_VBLANK_RELATIVE)
+ swap.sequence += curseq;
+
+ if ((curseq - swap.sequence) <= (1<<23)) {
+ if (swap.seqtype & _DRM_VBLANK_NEXTONMISS) {
+ swap.sequence = curseq + 1;
+ } else {
+ DRM_DEBUG("Missed target sequence\n");
+ return DRM_ERR(EINVAL);
+ }
+ }
+
+ spin_lock_irqsave(&dev_priv->swaps_lock, irqflags);
+
+ list_for_each(list, &dev_priv->vbl_swaps.head) {
+ vbl_swap = list_entry(list, drm_i915_vbl_swap_t, head);
+
+ if (vbl_swap->drw_id == swap.drawable &&
+ vbl_swap->pipe == pipe &&
+ vbl_swap->sequence == swap.sequence) {
+ spin_unlock_irqrestore(&dev_priv->swaps_lock, irqflags);
+ DRM_DEBUG("Already scheduled\n");
+ return 0;
+ }
+ }
+
+ spin_unlock_irqrestore(&dev_priv->swaps_lock, irqflags);
+
+ if (dev_priv->swaps_pending >= 100) {
+ DRM_DEBUG("Too many swaps queued\n");
+ return DRM_ERR(EBUSY);
+ }
+
+ vbl_swap = drm_calloc(1, sizeof(vbl_swap), DRM_MEM_DRIVER);
+
+ if (!vbl_swap) {
+ DRM_ERROR("Failed to allocate memory to queue swap\n");
+ return DRM_ERR(ENOMEM);
+ }
+
+ DRM_DEBUG("\n");
+
+ vbl_swap->drw_id = swap.drawable;
+ vbl_swap->pipe = pipe;
+ vbl_swap->sequence = swap.sequence;
+
+ spin_lock_irqsave(&dev_priv->swaps_lock, irqflags);
+
+ list_add_tail((struct list_head *)vbl_swap, &dev_priv->vbl_swaps.head);
+ dev_priv->swaps_pending++;
+
+ spin_unlock_irqrestore(&dev_priv->swaps_lock, irqflags);
+
+ DRM_COPY_TO_USER_IOCTL((drm_i915_vblank_swap_t __user *) data, swap,
+ sizeof(swap));
+
+ return 0;
+}
+