summaryrefslogtreecommitdiff
path: root/meta-moblin/packages/linux/linux-moblin-2.6.27-rc6/0011-drm-vblank-rework.patch
diff options
context:
space:
mode:
Diffstat (limited to 'meta-moblin/packages/linux/linux-moblin-2.6.27-rc6/0011-drm-vblank-rework.patch')
-rw-r--r--meta-moblin/packages/linux/linux-moblin-2.6.27-rc6/0011-drm-vblank-rework.patch1534
1 files changed, 0 insertions, 1534 deletions
diff --git a/meta-moblin/packages/linux/linux-moblin-2.6.27-rc6/0011-drm-vblank-rework.patch b/meta-moblin/packages/linux/linux-moblin-2.6.27-rc6/0011-drm-vblank-rework.patch
deleted file mode 100644
index 6161a71f0..000000000
--- a/meta-moblin/packages/linux/linux-moblin-2.6.27-rc6/0011-drm-vblank-rework.patch
+++ /dev/null
@@ -1,1534 +0,0 @@
-commit 2aebb4e4e62d09b4a95be7be7c24a7f6528385b7
-Author: Jesse Barnes <jbarnes@virtuousgeek.org>
-Date: Tue Sep 30 12:14:26 2008 -0700
-
- drm: Rework vblank-wait handling to allow interrupt reduction.
-
- Previously, drivers supporting vblank interrupt waits would run the interrupt
- all the time, or all the time that any 3d client was running, preventing the
- CPU from sleeping for long when the system was otherwise idle. Now, interrupts
- are disabled any time that no client is waiting on a vblank event. The new
- method uses vblank counters on the chipsets when the interrupts are turned
- off, rather than counting interrupts, so that we can continue to present
- accurate vblank numbers.
-
- Co-author: Michel Dänzer <michel@tungstengraphics.com>
- Signed-off-by: Jesse Barnes <jbarnes@virtuousgeek.org>
- Signed-off-by: Eric Anholt <eric@anholt.net>
- Signed-off-by: Dave Airlie <airlied@redhat.com>
-
-diff --git a/drivers/gpu/drm/drm_drv.c b/drivers/gpu/drm/drm_drv.c
-index 452c2d8..fb45fe7 100644
---- a/drivers/gpu/drm/drm_drv.c
-+++ b/drivers/gpu/drm/drm_drv.c
-@@ -116,6 +116,8 @@ static struct drm_ioctl_desc drm_ioctls[] = {
-
- DRM_IOCTL_DEF(DRM_IOCTL_WAIT_VBLANK, drm_wait_vblank, 0),
-
-+ DRM_IOCTL_DEF(DRM_IOCTL_MODESET_CTL, drm_modeset_ctl, 0),
-+
- DRM_IOCTL_DEF(DRM_IOCTL_UPDATE_DRAW, drm_update_drawable_info, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY),
- };
-
-diff --git a/drivers/gpu/drm/drm_irq.c b/drivers/gpu/drm/drm_irq.c
-index 61ed515..d0c13d9 100644
---- a/drivers/gpu/drm/drm_irq.c
-+++ b/drivers/gpu/drm/drm_irq.c
-@@ -71,19 +71,131 @@ int drm_irq_by_busid(struct drm_device *dev, void *data,
- return 0;
- }
-
-+static void vblank_disable_fn(unsigned long arg)
-+{
-+ struct drm_device *dev = (struct drm_device *)arg;
-+ unsigned long irqflags;
-+ int i;
-+
-+ if (!dev->vblank_disable_allowed)
-+ return;
-+
-+ for (i = 0; i < dev->num_crtcs; i++) {
-+ spin_lock_irqsave(&dev->vbl_lock, irqflags);
-+ if (atomic_read(&dev->vblank_refcount[i]) == 0 &&
-+ dev->vblank_enabled[i]) {
-+ DRM_DEBUG("disabling vblank on crtc %d\n", i);
-+ dev->last_vblank[i] =
-+ dev->driver->get_vblank_counter(dev, i);
-+ dev->driver->disable_vblank(dev, i);
-+ dev->vblank_enabled[i] = 0;
-+ }
-+ spin_unlock_irqrestore(&dev->vbl_lock, irqflags);
-+ }
-+}
-+
-+static void drm_vblank_cleanup(struct drm_device *dev)
-+{
-+ /* Bail if the driver didn't call drm_vblank_init() */
-+ if (dev->num_crtcs == 0)
-+ return;
-+
-+ del_timer(&dev->vblank_disable_timer);
-+
-+ vblank_disable_fn((unsigned long)dev);
-+
-+ drm_free(dev->vbl_queue, sizeof(*dev->vbl_queue) * dev->num_crtcs,
-+ DRM_MEM_DRIVER);
-+ drm_free(dev->vbl_sigs, sizeof(*dev->vbl_sigs) * dev->num_crtcs,
-+ DRM_MEM_DRIVER);
-+ drm_free(dev->_vblank_count, sizeof(*dev->_vblank_count) *
-+ dev->num_crtcs, DRM_MEM_DRIVER);
-+ drm_free(dev->vblank_refcount, sizeof(*dev->vblank_refcount) *
-+ dev->num_crtcs, DRM_MEM_DRIVER);
-+ drm_free(dev->vblank_enabled, sizeof(*dev->vblank_enabled) *
-+ dev->num_crtcs, DRM_MEM_DRIVER);
-+ drm_free(dev->last_vblank, sizeof(*dev->last_vblank) * dev->num_crtcs,
-+ DRM_MEM_DRIVER);
-+ drm_free(dev->vblank_inmodeset, sizeof(*dev->vblank_inmodeset) *
-+ dev->num_crtcs, DRM_MEM_DRIVER);
-+
-+ dev->num_crtcs = 0;
-+}
-+
-+int drm_vblank_init(struct drm_device *dev, int num_crtcs)
-+{
-+ int i, ret = -ENOMEM;
-+
-+ setup_timer(&dev->vblank_disable_timer, vblank_disable_fn,
-+ (unsigned long)dev);
-+ spin_lock_init(&dev->vbl_lock);
-+ atomic_set(&dev->vbl_signal_pending, 0);
-+ dev->num_crtcs = num_crtcs;
-+
-+ dev->vbl_queue = drm_alloc(sizeof(wait_queue_head_t) * num_crtcs,
-+ DRM_MEM_DRIVER);
-+ if (!dev->vbl_queue)
-+ goto err;
-+
-+ dev->vbl_sigs = drm_alloc(sizeof(struct list_head) * num_crtcs,
-+ DRM_MEM_DRIVER);
-+ if (!dev->vbl_sigs)
-+ goto err;
-+
-+ dev->_vblank_count = drm_alloc(sizeof(atomic_t) * num_crtcs,
-+ DRM_MEM_DRIVER);
-+ if (!dev->_vblank_count)
-+ goto err;
-+
-+ dev->vblank_refcount = drm_alloc(sizeof(atomic_t) * num_crtcs,
-+ DRM_MEM_DRIVER);
-+ if (!dev->vblank_refcount)
-+ goto err;
-+
-+ dev->vblank_enabled = drm_calloc(num_crtcs, sizeof(int),
-+ DRM_MEM_DRIVER);
-+ if (!dev->vblank_enabled)
-+ goto err;
-+
-+ dev->last_vblank = drm_calloc(num_crtcs, sizeof(u32), DRM_MEM_DRIVER);
-+ if (!dev->last_vblank)
-+ goto err;
-+
-+ dev->vblank_inmodeset = drm_calloc(num_crtcs, sizeof(int),
-+ DRM_MEM_DRIVER);
-+ if (!dev->vblank_inmodeset)
-+ goto err;
-+
-+ /* Zero per-crtc vblank stuff */
-+ for (i = 0; i < num_crtcs; i++) {
-+ init_waitqueue_head(&dev->vbl_queue[i]);
-+ INIT_LIST_HEAD(&dev->vbl_sigs[i]);
-+ atomic_set(&dev->_vblank_count[i], 0);
-+ atomic_set(&dev->vblank_refcount[i], 0);
-+ }
-+
-+ dev->vblank_disable_allowed = 0;
-+
-+ return 0;
-+
-+err:
-+ drm_vblank_cleanup(dev);
-+ return ret;
-+}
-+EXPORT_SYMBOL(drm_vblank_init);
-+
- /**
- * Install IRQ handler.
- *
- * \param dev DRM device.
-- * \param irq IRQ number.
- *
-- * Initializes the IRQ related data, and setups drm_device::vbl_queue. Installs the handler, calling the driver
-+ * Initializes the IRQ related data. Installs the handler, calling the driver
- * \c drm_driver_irq_preinstall() and \c drm_driver_irq_postinstall() functions
- * before and after the installation.
- */
--static int drm_irq_install(struct drm_device * dev)
-+int drm_irq_install(struct drm_device *dev)
- {
-- int ret;
-+ int ret = 0;
- unsigned long sh_flags = 0;
-
- if (!drm_core_check_feature(dev, DRIVER_HAVE_IRQ))
-@@ -109,17 +221,6 @@ static int drm_irq_install(struct drm_device * dev)
-
- DRM_DEBUG("irq=%d\n", dev->pdev->irq);
-
-- if (drm_core_check_feature(dev, DRIVER_IRQ_VBL)) {
-- init_waitqueue_head(&dev->vbl_queue);
--
-- spin_lock_init(&dev->vbl_lock);
--
-- INIT_LIST_HEAD(&dev->vbl_sigs);
-- INIT_LIST_HEAD(&dev->vbl_sigs2);
--
-- dev->vbl_pending = 0;
-- }
--
- /* Before installing handler */
- dev->driver->irq_preinstall(dev);
-
-@@ -141,10 +242,16 @@ static int drm_irq_install(struct drm_device * dev)
- }
-
- /* After installing handler */
-- dev->driver->irq_postinstall(dev);
-+ ret = dev->driver->irq_postinstall(dev);
-+ if (ret < 0) {
-+ mutex_lock(&dev->struct_mutex);
-+ dev->irq_enabled = 0;
-+ mutex_unlock(&dev->struct_mutex);
-+ }
-
-- return 0;
-+ return ret;
- }
-+EXPORT_SYMBOL(drm_irq_install);
-
- /**
- * Uninstall the IRQ handler.
-@@ -174,11 +281,12 @@ int drm_irq_uninstall(struct drm_device * dev)
-
- free_irq(dev->pdev->irq, dev);
-
-+ drm_vblank_cleanup(dev);
-+
- dev->locked_tasklet_func = NULL;
-
- return 0;
- }
--
- EXPORT_SYMBOL(drm_irq_uninstall);
-
- /**
-@@ -218,6 +326,174 @@ int drm_control(struct drm_device *dev, void *data,
- }
-
- /**
-+ * drm_vblank_count - retrieve "cooked" vblank counter value
-+ * @dev: DRM device
-+ * @crtc: which counter to retrieve
-+ *
-+ * Fetches the "cooked" vblank count value that represents the number of
-+ * vblank events since the system was booted, including lost events due to
-+ * modesetting activity.
-+ */
-+u32 drm_vblank_count(struct drm_device *dev, int crtc)
-+{
-+ return atomic_read(&dev->_vblank_count[crtc]);
-+}
-+EXPORT_SYMBOL(drm_vblank_count);
-+
-+/**
-+ * drm_update_vblank_count - update the master vblank counter
-+ * @dev: DRM device
-+ * @crtc: counter to update
-+ *
-+ * Call back into the driver to update the appropriate vblank counter
-+ * (specified by @crtc). Deal with wraparound, if it occurred, and
-+ * update the last read value so we can deal with wraparound on the next
-+ * call if necessary.
-+ *
-+ * Only necessary when going from off->on, to account for frames we
-+ * didn't get an interrupt for.
-+ *
-+ * Note: caller must hold dev->vbl_lock since this reads & writes
-+ * device vblank fields.
-+ */
-+static void drm_update_vblank_count(struct drm_device *dev, int crtc)
-+{
-+ u32 cur_vblank, diff;
-+
-+ /*
-+ * Interrupts were disabled prior to this call, so deal with counter
-+ * wrap if needed.
-+ * NOTE! It's possible we lost a full dev->max_vblank_count events
-+ * here if the register is small or we had vblank interrupts off for
-+ * a long time.
-+ */
-+ cur_vblank = dev->driver->get_vblank_counter(dev, crtc);
-+ diff = cur_vblank - dev->last_vblank[crtc];
-+ if (cur_vblank < dev->last_vblank[crtc]) {
-+ diff += dev->max_vblank_count;
-+
-+ DRM_DEBUG("last_vblank[%d]=0x%x, cur_vblank=0x%x => diff=0x%x\n",
-+ crtc, dev->last_vblank[crtc], cur_vblank, diff);
-+ }
-+
-+ DRM_DEBUG("enabling vblank interrupts on crtc %d, missed %d\n",
-+ crtc, diff);
-+
-+ atomic_add(diff, &dev->_vblank_count[crtc]);
-+}
-+
-+/**
-+ * drm_vblank_get - get a reference count on vblank events
-+ * @dev: DRM device
-+ * @crtc: which CRTC to own
-+ *
-+ * Acquire a reference count on vblank events to avoid having them disabled
-+ * while in use.
-+ *
-+ * RETURNS
-+ * Zero on success, nonzero on failure.
-+ */
-+int drm_vblank_get(struct drm_device *dev, int crtc)
-+{
-+ unsigned long irqflags;
-+ int ret = 0;
-+
-+ spin_lock_irqsave(&dev->vbl_lock, irqflags);
-+ /* Going from 0->1 means we have to enable interrupts again */
-+ if (atomic_add_return(1, &dev->vblank_refcount[crtc]) == 1 &&
-+ !dev->vblank_enabled[crtc]) {
-+ ret = dev->driver->enable_vblank(dev, crtc);
-+ DRM_DEBUG("enabling vblank on crtc %d, ret: %d\n", crtc, ret);
-+ if (ret)
-+ atomic_dec(&dev->vblank_refcount[crtc]);
-+ else {
-+ dev->vblank_enabled[crtc] = 1;
-+ drm_update_vblank_count(dev, crtc);
-+ }
-+ }
-+ spin_unlock_irqrestore(&dev->vbl_lock, irqflags);
-+
-+ return ret;
-+}
-+EXPORT_SYMBOL(drm_vblank_get);
-+
-+/**
-+ * drm_vblank_put - give up ownership of vblank events
-+ * @dev: DRM device
-+ * @crtc: which counter to give up
-+ *
-+ * Release ownership of a given vblank counter, turning off interrupts
-+ * if possible.
-+ */
-+void drm_vblank_put(struct drm_device *dev, int crtc)
-+{
-+ /* Last user schedules interrupt disable */
-+ if (atomic_dec_and_test(&dev->vblank_refcount[crtc]))
-+ mod_timer(&dev->vblank_disable_timer, jiffies + 5*DRM_HZ);
-+}
-+EXPORT_SYMBOL(drm_vblank_put);
-+
-+/**
-+ * drm_modeset_ctl - handle vblank event counter changes across mode switch
-+ * @DRM_IOCTL_ARGS: standard ioctl arguments
-+ *
-+ * Applications should call the %_DRM_PRE_MODESET and %_DRM_POST_MODESET
-+ * ioctls around modesetting so that any lost vblank events are accounted for.
-+ *
-+ * Generally the counter will reset across mode sets. If interrupts are
-+ * enabled around this call, we don't have to do anything since the counter
-+ * will have already been incremented.
-+ */
-+int drm_modeset_ctl(struct drm_device *dev, void *data,
-+ struct drm_file *file_priv)
-+{
-+ struct drm_modeset_ctl *modeset = data;
-+ unsigned long irqflags;
-+ int crtc, ret = 0;
-+
-+ /* If drm_vblank_init() hasn't been called yet, just no-op */
-+ if (!dev->num_crtcs)
-+ goto out;
-+
-+ crtc = modeset->crtc;
-+ if (crtc >= dev->num_crtcs) {
-+ ret = -EINVAL;
-+ goto out;
-+ }
-+
-+ /*
-+ * To avoid all the problems that might happen if interrupts
-+ * were enabled/disabled around or between these calls, we just
-+ * have the kernel take a reference on the CRTC (just once though
-+ * to avoid corrupting the count if multiple, mismatch calls occur),
-+ * so that interrupts remain enabled in the interim.
-+ */
-+ switch (modeset->cmd) {
-+ case _DRM_PRE_MODESET:
-+ if (!dev->vblank_inmodeset[crtc]) {
-+ dev->vblank_inmodeset[crtc] = 1;
-+ drm_vblank_get(dev, crtc);
-+ }
-+ break;
-+ case _DRM_POST_MODESET:
-+ if (dev->vblank_inmodeset[crtc]) {
-+ spin_lock_irqsave(&dev->vbl_lock, irqflags);
-+ dev->vblank_disable_allowed = 1;
-+ dev->vblank_inmodeset[crtc] = 0;
-+ spin_unlock_irqrestore(&dev->vbl_lock, irqflags);
-+ drm_vblank_put(dev, crtc);
-+ }
-+ break;
-+ default:
-+ ret = -EINVAL;
-+ break;
-+ }
-+
-+out:
-+ return ret;
-+}
-+
-+/**
- * Wait for VBLANK.
- *
- * \param inode device inode.
-@@ -236,12 +512,12 @@ int drm_control(struct drm_device *dev, void *data,
- *
- * If a signal is not requested, then calls vblank_wait().
- */
--int drm_wait_vblank(struct drm_device *dev, void *data, struct drm_file *file_priv)
-+int drm_wait_vblank(struct drm_device *dev, void *data,
-+ struct drm_file *file_priv)
- {
- union drm_wait_vblank *vblwait = data;
-- struct timeval now;
- int ret = 0;
-- unsigned int flags, seq;
-+ unsigned int flags, seq, crtc;
-
- if ((!dev->pdev->irq) || (!dev->irq_enabled))
- return -EINVAL;
-@@ -255,13 +531,17 @@ int drm_wait_vblank(struct drm_device *dev, void *data, struct drm_file *file_pr
- }
-
- flags = vblwait->request.type & _DRM_VBLANK_FLAGS_MASK;
-+ crtc = flags & _DRM_VBLANK_SECONDARY ? 1 : 0;
-
-- if (!drm_core_check_feature(dev, (flags & _DRM_VBLANK_SECONDARY) ?
-- DRIVER_IRQ_VBL2 : DRIVER_IRQ_VBL))
-+ if (crtc >= dev->num_crtcs)
- return -EINVAL;
-
-- seq = atomic_read((flags & _DRM_VBLANK_SECONDARY) ? &dev->vbl_received2
-- : &dev->vbl_received);
-+ ret = drm_vblank_get(dev, crtc);
-+ if (ret) {
-+ DRM_ERROR("failed to acquire vblank counter, %d\n", ret);
-+ return ret;
-+ }
-+ seq = drm_vblank_count(dev, crtc);
-
- switch (vblwait->request.type & _DRM_VBLANK_TYPES_MASK) {
- case _DRM_VBLANK_RELATIVE:
-@@ -270,7 +550,8 @@ int drm_wait_vblank(struct drm_device *dev, void *data, struct drm_file *file_pr
- case _DRM_VBLANK_ABSOLUTE:
- break;
- default:
-- return -EINVAL;
-+ ret = -EINVAL;
-+ goto done;
- }
-
- if ((flags & _DRM_VBLANK_NEXTONMISS) &&
-@@ -280,8 +561,7 @@ int drm_wait_vblank(struct drm_device *dev, void *data, struct drm_file *file_pr
-
- if (flags & _DRM_VBLANK_SIGNAL) {
- unsigned long irqflags;
-- struct list_head *vbl_sigs = (flags & _DRM_VBLANK_SECONDARY)
-- ? &dev->vbl_sigs2 : &dev->vbl_sigs;
-+ struct list_head *vbl_sigs = &dev->vbl_sigs[crtc];
- struct drm_vbl_sig *vbl_sig;
-
- spin_lock_irqsave(&dev->vbl_lock, irqflags);
-@@ -302,22 +582,29 @@ int drm_wait_vblank(struct drm_device *dev, void *data, struct drm_file *file_pr
- }
- }
-
-- if (dev->vbl_pending >= 100) {
-+ if (atomic_read(&dev->vbl_signal_pending) >= 100) {
- spin_unlock_irqrestore(&dev->vbl_lock, irqflags);
-- return -EBUSY;
-+ ret = -EBUSY;
-+ goto done;
- }
-
-- dev->vbl_pending++;
--
- spin_unlock_irqrestore(&dev->vbl_lock, irqflags);
-
-- if (!
-- (vbl_sig =
-- drm_alloc(sizeof(struct drm_vbl_sig), DRM_MEM_DRIVER))) {
-- return -ENOMEM;
-+ vbl_sig = drm_calloc(1, sizeof(struct drm_vbl_sig),
-+ DRM_MEM_DRIVER);
-+ if (!vbl_sig) {
-+ ret = -ENOMEM;
-+ goto done;
-+ }
-+
-+ ret = drm_vblank_get(dev, crtc);
-+ if (ret) {
-+ drm_free(vbl_sig, sizeof(struct drm_vbl_sig),
-+ DRM_MEM_DRIVER);
-+ return ret;
- }
-
-- memset((void *)vbl_sig, 0, sizeof(*vbl_sig));
-+ atomic_inc(&dev->vbl_signal_pending);
-
- vbl_sig->sequence = vblwait->request.sequence;
- vbl_sig->info.si_signo = vblwait->request.signal;
-@@ -331,20 +618,29 @@ int drm_wait_vblank(struct drm_device *dev, void *data, struct drm_file *file_pr
-
- vblwait->reply.sequence = seq;
- } else {
-- if (flags & _DRM_VBLANK_SECONDARY) {
-- if (dev->driver->vblank_wait2)
-- ret = dev->driver->vblank_wait2(dev, &vblwait->request.sequence);
-- } else if (dev->driver->vblank_wait)
-- ret =
-- dev->driver->vblank_wait(dev,
-- &vblwait->request.sequence);
--
-- do_gettimeofday(&now);
-- vblwait->reply.tval_sec = now.tv_sec;
-- vblwait->reply.tval_usec = now.tv_usec;
-+ DRM_DEBUG("waiting on vblank count %d, crtc %d\n",
-+ vblwait->request.sequence, crtc);
-+ DRM_WAIT_ON(ret, dev->vbl_queue[crtc], 3 * DRM_HZ,
-+ ((drm_vblank_count(dev, crtc)
-+ - vblwait->request.sequence) <= (1 << 23)));
-+
-+ if (ret != -EINTR) {
-+ struct timeval now;
-+
-+ do_gettimeofday(&now);
-+
-+ vblwait->reply.tval_sec = now.tv_sec;
-+ vblwait->reply.tval_usec = now.tv_usec;
-+ vblwait->reply.sequence = drm_vblank_count(dev, crtc);
-+ DRM_DEBUG("returning %d to client\n",
-+ vblwait->reply.sequence);
-+ } else {
-+ DRM_DEBUG("vblank wait interrupted by signal\n");
-+ }
- }
-
-- done:
-+done:
-+ drm_vblank_put(dev, crtc);
- return ret;
- }
-
-@@ -352,44 +648,57 @@ int drm_wait_vblank(struct drm_device *dev, void *data, struct drm_file *file_pr
- * Send the VBLANK signals.
- *
- * \param dev DRM device.
-+ * \param crtc CRTC where the vblank event occurred
- *
- * Sends a signal for each task in drm_device::vbl_sigs and empties the list.
- *
- * If a signal is not requested, then calls vblank_wait().
- */
--void drm_vbl_send_signals(struct drm_device * dev)
-+static void drm_vbl_send_signals(struct drm_device *dev, int crtc)
- {
-+ struct drm_vbl_sig *vbl_sig, *tmp;
-+ struct list_head *vbl_sigs;
-+ unsigned int vbl_seq;
- unsigned long flags;
-- int i;
-
- spin_lock_irqsave(&dev->vbl_lock, flags);
-
-- for (i = 0; i < 2; i++) {
-- struct drm_vbl_sig *vbl_sig, *tmp;
-- struct list_head *vbl_sigs = i ? &dev->vbl_sigs2 : &dev->vbl_sigs;
-- unsigned int vbl_seq = atomic_read(i ? &dev->vbl_received2 :
-- &dev->vbl_received);
-+ vbl_sigs = &dev->vbl_sigs[crtc];
-+ vbl_seq = drm_vblank_count(dev, crtc);
-
-- list_for_each_entry_safe(vbl_sig, tmp, vbl_sigs, head) {
-- if ((vbl_seq - vbl_sig->sequence) <= (1 << 23)) {
-- vbl_sig->info.si_code = vbl_seq;
-- send_sig_info(vbl_sig->info.si_signo,
-- &vbl_sig->info, vbl_sig->task);
-+ list_for_each_entry_safe(vbl_sig, tmp, vbl_sigs, head) {
-+ if ((vbl_seq - vbl_sig->sequence) <= (1 << 23)) {
-+ vbl_sig->info.si_code = vbl_seq;
-+ send_sig_info(vbl_sig->info.si_signo,
-+ &vbl_sig->info, vbl_sig->task);
-
-- list_del(&vbl_sig->head);
--
-- drm_free(vbl_sig, sizeof(*vbl_sig),
-- DRM_MEM_DRIVER);
-+ list_del(&vbl_sig->head);
-
-- dev->vbl_pending--;
-- }
-- }
-+ drm_free(vbl_sig, sizeof(*vbl_sig),
-+ DRM_MEM_DRIVER);
-+ atomic_dec(&dev->vbl_signal_pending);
-+ drm_vblank_put(dev, crtc);
-+ }
- }
-
- spin_unlock_irqrestore(&dev->vbl_lock, flags);
- }
-
--EXPORT_SYMBOL(drm_vbl_send_signals);
-+/**
-+ * drm_handle_vblank - handle a vblank event
-+ * @dev: DRM device
-+ * @crtc: where this event occurred
-+ *
-+ * Drivers should call this routine in their vblank interrupt handlers to
-+ * update the vblank counter and send any signals that may be pending.
-+ */
-+void drm_handle_vblank(struct drm_device *dev, int crtc)
-+{
-+ atomic_inc(&dev->_vblank_count[crtc]);
-+ DRM_WAKEUP(&dev->vbl_queue[crtc]);
-+ drm_vbl_send_signals(dev, crtc);
-+}
-+EXPORT_SYMBOL(drm_handle_vblank);
-
- /**
- * Tasklet wrapper function.
-diff --git a/drivers/gpu/drm/i915/i915_dma.c b/drivers/gpu/drm/i915/i915_dma.c
-index cead62f..8609ec2 100644
---- a/drivers/gpu/drm/i915/i915_dma.c
-+++ b/drivers/gpu/drm/i915/i915_dma.c
-@@ -673,7 +673,7 @@ static int i915_getparam(struct drm_device *dev, void *data,
-
- switch (param->param) {
- case I915_PARAM_IRQ_ACTIVE:
-- value = dev->irq_enabled;
-+ value = dev->pdev->irq ? 1 : 0;
- break;
- case I915_PARAM_ALLOW_BATCHBUFFER:
- value = dev_priv->allow_batchbuffer ? 1 : 0;
-@@ -808,7 +808,8 @@ int i915_driver_load(struct drm_device *dev, unsigned long flags)
- * and the registers being closely associated.
- */
- if (!IS_I945G(dev) && !IS_I945GM(dev))
-- pci_enable_msi(dev->pdev);
-+ if (pci_enable_msi(dev->pdev))
-+ DRM_ERROR("failed to enable MSI\n");
-
- intel_opregion_init(dev);
-
-diff --git a/drivers/gpu/drm/i915/i915_drv.c b/drivers/gpu/drm/i915/i915_drv.c
-index eff66ed..37af03f 100644
---- a/drivers/gpu/drm/i915/i915_drv.c
-+++ b/drivers/gpu/drm/i915/i915_drv.c
-@@ -85,10 +85,8 @@ static struct drm_driver driver = {
- /* don't use mtrr's here, the Xserver or user space app should
- * deal with them for intel hardware.
- */
-- .driver_features =
-- DRIVER_USE_AGP | DRIVER_REQUIRE_AGP | /* DRIVER_USE_MTRR |*/
-- DRIVER_HAVE_IRQ | DRIVER_IRQ_SHARED | DRIVER_IRQ_VBL |
-- DRIVER_IRQ_VBL2,
-+ .driver_features = DRIVER_USE_AGP | DRIVER_REQUIRE_AGP |
-+ DRIVER_HAVE_IRQ | DRIVER_IRQ_SHARED,
- .load = i915_driver_load,
- .unload = i915_driver_unload,
- .lastclose = i915_driver_lastclose,
-@@ -96,8 +94,9 @@ static struct drm_driver driver = {
- .suspend = i915_suspend,
- .resume = i915_resume,
- .device_is_agp = i915_driver_device_is_agp,
-- .vblank_wait = i915_driver_vblank_wait,
-- .vblank_wait2 = i915_driver_vblank_wait2,
-+ .get_vblank_counter = i915_get_vblank_counter,
-+ .enable_vblank = i915_enable_vblank,
-+ .disable_vblank = i915_disable_vblank,
- .irq_preinstall = i915_driver_irq_preinstall,
- .irq_postinstall = i915_driver_irq_postinstall,
- .irq_uninstall = i915_driver_irq_uninstall,
-diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h
-index 71326ca..d1a02be 100644
---- a/drivers/gpu/drm/i915/i915_drv.h
-+++ b/drivers/gpu/drm/i915/i915_drv.h
-@@ -83,10 +83,15 @@ struct mem_block {
- typedef struct _drm_i915_vbl_swap {
- struct list_head head;
- drm_drawable_t drw_id;
-- unsigned int pipe;
-+ unsigned int plane;
- unsigned int sequence;
- } drm_i915_vbl_swap_t;
-
-+struct opregion_header;
-+struct opregion_acpi;
-+struct opregion_swsci;
-+struct opregion_asle;
-+
- struct intel_opregion {
- struct opregion_header *header;
- struct opregion_acpi *acpi;
-@@ -105,7 +110,7 @@ typedef struct drm_i915_private {
- drm_dma_handle_t *status_page_dmah;
- void *hw_status_page;
- dma_addr_t dma_status_page;
-- unsigned long counter;
-+ uint32_t counter;
- unsigned int status_gfx_addr;
- drm_local_map_t hws_map;
-
-@@ -247,16 +252,17 @@ extern int i915_irq_emit(struct drm_device *dev, void *data,
- extern int i915_irq_wait(struct drm_device *dev, void *data,
- struct drm_file *file_priv);
-
--extern int i915_driver_vblank_wait(struct drm_device *dev, unsigned int *sequence);
--extern int i915_driver_vblank_wait2(struct drm_device *dev, unsigned int *sequence);
- extern irqreturn_t i915_driver_irq_handler(DRM_IRQ_ARGS);
- extern void i915_driver_irq_preinstall(struct drm_device * dev);
--extern void i915_driver_irq_postinstall(struct drm_device * dev);
-+extern int i915_driver_irq_postinstall(struct drm_device *dev);
- extern void i915_driver_irq_uninstall(struct drm_device * dev);
- extern int i915_vblank_pipe_set(struct drm_device *dev, void *data,
- struct drm_file *file_priv);
- extern int i915_vblank_pipe_get(struct drm_device *dev, void *data,
- struct drm_file *file_priv);
-+extern int i915_enable_vblank(struct drm_device *dev, int crtc);
-+extern void i915_disable_vblank(struct drm_device *dev, int crtc);
-+extern u32 i915_get_vblank_counter(struct drm_device *dev, int crtc);
- extern int i915_vblank_swap(struct drm_device *dev, void *data,
- struct drm_file *file_priv);
- extern void i915_enable_irq(drm_i915_private_t *dev_priv, u32 mask);
-@@ -278,6 +284,10 @@ extern void i915_mem_release(struct drm_device * dev,
- extern int i915_save_state(struct drm_device *dev);
- extern int i915_restore_state(struct drm_device *dev);
-
-+/* i915_suspend.c */
-+extern int i915_save_state(struct drm_device *dev);
-+extern int i915_restore_state(struct drm_device *dev);
-+
- /* i915_opregion.c */
- extern int intel_opregion_init(struct drm_device *dev);
- extern void intel_opregion_free(struct drm_device *dev);
-diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c
-index ae7d3a8..f875959 100644
---- a/drivers/gpu/drm/i915/i915_irq.c
-+++ b/drivers/gpu/drm/i915/i915_irq.c
-@@ -35,9 +35,8 @@
-
- /** These are the interrupts used by the driver */
- #define I915_INTERRUPT_ENABLE_MASK (I915_USER_INTERRUPT | \
-- I915_DISPLAY_PIPE_A_VBLANK_INTERRUPT | \
-- I915_DISPLAY_PIPE_B_VBLANK_INTERRUPT | \
- I915_ASLE_INTERRUPT | \
-+ I915_DISPLAY_PIPE_A_EVENT_INTERRUPT | \
- I915_DISPLAY_PIPE_B_EVENT_INTERRUPT)
-
- void
-@@ -61,6 +60,64 @@ i915_disable_irq(drm_i915_private_t *dev_priv, u32 mask)
- }
-
- /**
-+ * i915_get_pipe - return the the pipe associated with a given plane
-+ * @dev: DRM device
-+ * @plane: plane to look for
-+ *
-+ * The Intel Mesa & 2D drivers call the vblank routines with a plane number
-+ * rather than a pipe number, since they may not always be equal. This routine
-+ * maps the given @plane back to a pipe number.
-+ */
-+static int
-+i915_get_pipe(struct drm_device *dev, int plane)
-+{
-+ drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
-+ u32 dspcntr;
-+
-+ dspcntr = plane ? I915_READ(DSPBCNTR) : I915_READ(DSPACNTR);
-+
-+ return dspcntr & DISPPLANE_SEL_PIPE_MASK ? 1 : 0;
-+}
-+
-+/**
-+ * i915_get_plane - return the the plane associated with a given pipe
-+ * @dev: DRM device
-+ * @pipe: pipe to look for
-+ *
-+ * The Intel Mesa & 2D drivers call the vblank routines with a plane number
-+ * rather than a plane number, since they may not always be equal. This routine
-+ * maps the given @pipe back to a plane number.
-+ */
-+static int
-+i915_get_plane(struct drm_device *dev, int pipe)
-+{
-+ if (i915_get_pipe(dev, 0) == pipe)
-+ return 0;
-+ return 1;
-+}
-+
-+/**
-+ * i915_pipe_enabled - check if a pipe is enabled
-+ * @dev: DRM device
-+ * @pipe: pipe to check
-+ *
-+ * Reading certain registers when the pipe is disabled can hang the chip.
-+ * Use this routine to make sure the PLL is running and the pipe is active
-+ * before reading such registers if unsure.
-+ */
-+static int
-+i915_pipe_enabled(struct drm_device *dev, int pipe)
-+{
-+ drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
-+ unsigned long pipeconf = pipe ? PIPEBCONF : PIPEACONF;
-+
-+ if (I915_READ(pipeconf) & PIPEACONF_ENABLE)
-+ return 1;
-+
-+ return 0;
-+}
-+
-+/**
- * Emit blits for scheduled buffer swaps.
- *
- * This function will be called with the HW lock held.
-@@ -71,8 +128,7 @@ static void i915_vblank_tasklet(struct drm_device *dev)
- unsigned long irqflags;
- struct list_head *list, *tmp, hits, *hit;
- int nhits, nrects, slice[2], upper[2], lower[2], i;
-- unsigned counter[2] = { atomic_read(&dev->vbl_received),
-- atomic_read(&dev->vbl_received2) };
-+ unsigned counter[2];
- struct drm_drawable_info *drw;
- drm_i915_sarea_t *sarea_priv = dev_priv->sarea_priv;
- u32 cpp = dev_priv->cpp;
-@@ -94,6 +150,9 @@ static void i915_vblank_tasklet(struct drm_device *dev)
- src_pitch >>= 2;
- }
-
-+ counter[0] = drm_vblank_count(dev, 0);
-+ counter[1] = drm_vblank_count(dev, 1);
-+
- DRM_DEBUG("\n");
-
- INIT_LIST_HEAD(&hits);
-@@ -106,12 +165,14 @@ static void i915_vblank_tasklet(struct drm_device *dev)
- list_for_each_safe(list, tmp, &dev_priv->vbl_swaps.head) {
- drm_i915_vbl_swap_t *vbl_swap =
- list_entry(list, drm_i915_vbl_swap_t, head);
-+ int pipe = i915_get_pipe(dev, vbl_swap->plane);
-
-- if ((counter[vbl_swap->pipe] - vbl_swap->sequence) > (1<<23))
-+ if ((counter[pipe] - vbl_swap->sequence) > (1<<23))
- continue;
-
- list_del(list);
- dev_priv->swaps_pending--;
-+ drm_vblank_put(dev, pipe);
-
- spin_unlock(&dev_priv->swaps_lock);
- spin_lock(&dev->drw_lock);
-@@ -204,7 +265,7 @@ static void i915_vblank_tasklet(struct drm_device *dev)
- drm_i915_vbl_swap_t *swap_hit =
- list_entry(hit, drm_i915_vbl_swap_t, head);
- struct drm_clip_rect *rect;
-- int num_rects, pipe;
-+ int num_rects, plane;
- unsigned short top, bottom;
-
- drw = drm_get_drawable_info(dev, swap_hit->drw_id);
-@@ -213,9 +274,9 @@ static void i915_vblank_tasklet(struct drm_device *dev)
- continue;
-
- rect = drw->rects;
-- pipe = swap_hit->pipe;
-- top = upper[pipe];
-- bottom = lower[pipe];
-+ plane = swap_hit->plane;
-+ top = upper[plane];
-+ bottom = lower[plane];
-
- for (num_rects = drw->num_rects; num_rects--; rect++) {
- int y1 = max(rect->y1, top);
-@@ -252,22 +313,54 @@ static void i915_vblank_tasklet(struct drm_device *dev)
- }
- }
-
-+u32 i915_get_vblank_counter(struct drm_device *dev, int plane)
-+{
-+ drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
-+ unsigned long high_frame;
-+ unsigned long low_frame;
-+ u32 high1, high2, low, count;
-+ int pipe;
-+
-+ pipe = i915_get_pipe(dev, plane);
-+ high_frame = pipe ? PIPEBFRAMEHIGH : PIPEAFRAMEHIGH;
-+ low_frame = pipe ? PIPEBFRAMEPIXEL : PIPEAFRAMEPIXEL;
-+
-+ if (!i915_pipe_enabled(dev, pipe)) {
-+ DRM_ERROR("trying to get vblank count for disabled pipe %d\n", pipe);
-+ return 0;
-+ }
-+
-+ /*
-+ * High & low register fields aren't synchronized, so make sure
-+ * we get a low value that's stable across two reads of the high
-+ * register.
-+ */
-+ do {
-+ high1 = ((I915_READ(high_frame) & PIPE_FRAME_HIGH_MASK) >>
-+ PIPE_FRAME_HIGH_SHIFT);
-+ low = ((I915_READ(low_frame) & PIPE_FRAME_LOW_MASK) >>
-+ PIPE_FRAME_LOW_SHIFT);
-+ high2 = ((I915_READ(high_frame) & PIPE_FRAME_HIGH_MASK) >>
-+ PIPE_FRAME_HIGH_SHIFT);
-+ } while (high1 != high2);
-+
-+ count = (high1 << 8) | low;
-+
-+ return count;
-+}
-+
- irqreturn_t i915_driver_irq_handler(DRM_IRQ_ARGS)
- {
- struct drm_device *dev = (struct drm_device *) arg;
- drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
-- u32 pipea_stats, pipeb_stats;
- u32 iir;
--
-- pipea_stats = I915_READ(PIPEASTAT);
-- pipeb_stats = I915_READ(PIPEBSTAT);
-+ u32 pipea_stats, pipeb_stats;
-+ int vblank = 0;
-
- if (dev->pdev->msi_enabled)
- I915_WRITE(IMR, ~0);
- iir = I915_READ(IIR);
-
-- DRM_DEBUG("iir=%08x\n", iir);
--
- if (iir == 0) {
- if (dev->pdev->msi_enabled) {
- I915_WRITE(IMR, dev_priv->irq_mask_reg);
-@@ -276,48 +369,56 @@ irqreturn_t i915_driver_irq_handler(DRM_IRQ_ARGS)
- return IRQ_NONE;
- }
-
-- I915_WRITE(PIPEASTAT, pipea_stats);
-- I915_WRITE(PIPEBSTAT, pipeb_stats);
--
-- I915_WRITE(IIR, iir);
-- if (dev->pdev->msi_enabled)
-- I915_WRITE(IMR, dev_priv->irq_mask_reg);
-- (void) I915_READ(IIR); /* Flush posted writes */
--
-- dev_priv->sarea_priv->last_dispatch = READ_BREADCRUMB(dev_priv);
--
-- if (iir & I915_USER_INTERRUPT)
-- DRM_WAKEUP(&dev_priv->irq_queue);
--
-- if (iir & (I915_DISPLAY_PIPE_A_VBLANK_INTERRUPT |
-- I915_DISPLAY_PIPE_B_VBLANK_INTERRUPT)) {
-- int vblank_pipe = dev_priv->vblank_pipe;
--
-- if ((vblank_pipe &
-- (DRM_I915_VBLANK_PIPE_A | DRM_I915_VBLANK_PIPE_B))
-- == (DRM_I915_VBLANK_PIPE_A | DRM_I915_VBLANK_PIPE_B)) {
-- if (iir & I915_DISPLAY_PIPE_A_VBLANK_INTERRUPT)
-- atomic_inc(&dev->vbl_received);
-- if (iir & I915_DISPLAY_PIPE_B_VBLANK_INTERRUPT)
-- atomic_inc(&dev->vbl_received2);
-- } else if (((iir & I915_DISPLAY_PIPE_A_VBLANK_INTERRUPT) &&
-- (vblank_pipe & DRM_I915_VBLANK_PIPE_A)) ||
-- ((iir & I915_DISPLAY_PIPE_B_VBLANK_INTERRUPT) &&
-- (vblank_pipe & DRM_I915_VBLANK_PIPE_B)))
-- atomic_inc(&dev->vbl_received);
-+ /*
-+ * Clear the PIPE(A|B)STAT regs before the IIR otherwise
-+ * we may get extra interrupts.
-+ */
-+ if (iir & I915_DISPLAY_PIPE_A_EVENT_INTERRUPT) {
-+ pipea_stats = I915_READ(PIPEASTAT);
-+ if (!(dev_priv->vblank_pipe & DRM_I915_VBLANK_PIPE_A))
-+ pipea_stats &= ~(PIPE_START_VBLANK_INTERRUPT_ENABLE |
-+ PIPE_VBLANK_INTERRUPT_ENABLE);
-+ else if (pipea_stats & (PIPE_START_VBLANK_INTERRUPT_STATUS|
-+ PIPE_VBLANK_INTERRUPT_STATUS)) {
-+ vblank++;
-+ drm_handle_vblank(dev, i915_get_plane(dev, 0));
-+ }
-
-- DRM_WAKEUP(&dev->vbl_queue);
-- drm_vbl_send_signals(dev);
-+ I915_WRITE(PIPEASTAT, pipea_stats);
-+ }
-+ if (iir & I915_DISPLAY_PIPE_B_EVENT_INTERRUPT) {
-+ pipeb_stats = I915_READ(PIPEBSTAT);
-+ /* Ack the event */
-+ I915_WRITE(PIPEBSTAT, pipeb_stats);
-+
-+ /* The vblank interrupt gets enabled even if we didn't ask for
-+ it, so make sure it's shut down again */
-+ if (!(dev_priv->vblank_pipe & DRM_I915_VBLANK_PIPE_B))
-+ pipeb_stats &= ~(PIPE_START_VBLANK_INTERRUPT_ENABLE |
-+ PIPE_VBLANK_INTERRUPT_ENABLE);
-+ else if (pipeb_stats & (PIPE_START_VBLANK_INTERRUPT_STATUS|
-+ PIPE_VBLANK_INTERRUPT_STATUS)) {
-+ vblank++;
-+ drm_handle_vblank(dev, i915_get_plane(dev, 1));
-+ }
-
-- if (dev_priv->swaps_pending > 0)
-- drm_locked_tasklet(dev, i915_vblank_tasklet);
-+ if (pipeb_stats & I915_LEGACY_BLC_EVENT_STATUS)
-+ opregion_asle_intr(dev);
-+ I915_WRITE(PIPEBSTAT, pipeb_stats);
- }
-
- if (iir & I915_ASLE_INTERRUPT)
- opregion_asle_intr(dev);
-
-- if (iir & I915_DISPLAY_PIPE_B_EVENT_INTERRUPT)
-- opregion_asle_intr(dev);
-+ dev_priv->sarea_priv->last_dispatch = READ_BREADCRUMB(dev_priv);
-+
-+ if (dev->pdev->msi_enabled)
-+ I915_WRITE(IMR, dev_priv->irq_mask_reg);
-+ I915_WRITE(IIR, iir);
-+ (void) I915_READ(IIR);
-+
-+ if (vblank && dev_priv->swaps_pending > 0)
-+ drm_locked_tasklet(dev, i915_vblank_tasklet);
-
- return IRQ_HANDLED;
- }
-@@ -358,7 +459,7 @@ static void i915_user_irq_get(struct drm_device *dev)
- spin_unlock(&dev_priv->user_irq_lock);
- }
-
--static void i915_user_irq_put(struct drm_device *dev)
-+void i915_user_irq_put(struct drm_device *dev)
- {
- drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
-
-@@ -395,41 +496,10 @@ static int i915_wait_irq(struct drm_device * dev, int irq_nr)
- }
-
- dev_priv->sarea_priv->last_dispatch = READ_BREADCRUMB(dev_priv);
-- return ret;
--}
--
--static int i915_driver_vblank_do_wait(struct drm_device *dev, unsigned int *sequence,
-- atomic_t *counter)
--{
-- drm_i915_private_t *dev_priv = dev->dev_private;
-- unsigned int cur_vblank;
-- int ret = 0;
--
-- if (!dev_priv) {
-- DRM_ERROR("called with no initialization\n");
-- return -EINVAL;
-- }
--
-- DRM_WAIT_ON(ret, dev->vbl_queue, 3 * DRM_HZ,
-- (((cur_vblank = atomic_read(counter))
-- - *sequence) <= (1<<23)));
--
-- *sequence = cur_vblank;
-
- return ret;
- }
-
--
--int i915_driver_vblank_wait(struct drm_device *dev, unsigned int *sequence)
--{
-- return i915_driver_vblank_do_wait(dev, sequence, &dev->vbl_received);
--}
--
--int i915_driver_vblank_wait2(struct drm_device *dev, unsigned int *sequence)
--{
-- return i915_driver_vblank_do_wait(dev, sequence, &dev->vbl_received2);
--}
--
- /* Needs the lock as it touches the ring.
- */
- int i915_irq_emit(struct drm_device *dev, void *data,
-@@ -472,40 +542,88 @@ int i915_irq_wait(struct drm_device *dev, void *data,
- return i915_wait_irq(dev, irqwait->irq_seq);
- }
-
-+int i915_enable_vblank(struct drm_device *dev, int plane)
-+{
-+ drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
-+ int pipe = i915_get_pipe(dev, plane);
-+ u32 pipestat_reg = 0;
-+ u32 pipestat;
-+
-+ switch (pipe) {
-+ case 0:
-+ pipestat_reg = PIPEASTAT;
-+ i915_enable_irq(dev_priv, I915_DISPLAY_PIPE_A_EVENT_INTERRUPT);
-+ break;
-+ case 1:
-+ pipestat_reg = PIPEBSTAT;
-+ i915_enable_irq(dev_priv, I915_DISPLAY_PIPE_B_EVENT_INTERRUPT);
-+ break;
-+ default:
-+ DRM_ERROR("tried to enable vblank on non-existent pipe %d\n",
-+ pipe);
-+ break;
-+ }
-+
-+ if (pipestat_reg) {
-+ pipestat = I915_READ(pipestat_reg);
-+ if (IS_I965G(dev))
-+ pipestat |= PIPE_START_VBLANK_INTERRUPT_ENABLE;
-+ else
-+ pipestat |= PIPE_VBLANK_INTERRUPT_ENABLE;
-+ /* Clear any stale interrupt status */
-+ pipestat |= (PIPE_START_VBLANK_INTERRUPT_STATUS |
-+ PIPE_VBLANK_INTERRUPT_STATUS);
-+ I915_WRITE(pipestat_reg, pipestat);
-+ }
-+
-+ return 0;
-+}
-+
-+void i915_disable_vblank(struct drm_device *dev, int plane)
-+{
-+ drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
-+ int pipe = i915_get_pipe(dev, plane);
-+ u32 pipestat_reg = 0;
-+ u32 pipestat;
-+
-+ switch (pipe) {
-+ case 0:
-+ pipestat_reg = PIPEASTAT;
-+ i915_disable_irq(dev_priv, I915_DISPLAY_PIPE_A_EVENT_INTERRUPT);
-+ break;
-+ case 1:
-+ pipestat_reg = PIPEBSTAT;
-+ i915_disable_irq(dev_priv, I915_DISPLAY_PIPE_B_EVENT_INTERRUPT);
-+ break;
-+ default:
-+ DRM_ERROR("tried to disable vblank on non-existent pipe %d\n",
-+ pipe);
-+ break;
-+ }
-+
-+ if (pipestat_reg) {
-+ pipestat = I915_READ(pipestat_reg);
-+ pipestat &= ~(PIPE_START_VBLANK_INTERRUPT_ENABLE |
-+ PIPE_VBLANK_INTERRUPT_ENABLE);
-+ /* Clear any stale interrupt status */
-+ pipestat |= (PIPE_START_VBLANK_INTERRUPT_STATUS |
-+ PIPE_VBLANK_INTERRUPT_STATUS);
-+ I915_WRITE(pipestat_reg, pipestat);
-+ }
-+}
-+
- /* Set the vblank monitor pipe
- */
- int i915_vblank_pipe_set(struct drm_device *dev, void *data,
- struct drm_file *file_priv)
- {
- drm_i915_private_t *dev_priv = dev->dev_private;
-- drm_i915_vblank_pipe_t *pipe = data;
-- u32 enable_mask = 0, disable_mask = 0;
-
- if (!dev_priv) {
- DRM_ERROR("called with no initialization\n");
- return -EINVAL;
- }
-
-- if (pipe->pipe & ~(DRM_I915_VBLANK_PIPE_A|DRM_I915_VBLANK_PIPE_B)) {
-- DRM_ERROR("called with invalid pipe 0x%x\n", pipe->pipe);
-- return -EINVAL;
-- }
--
-- if (pipe->pipe & DRM_I915_VBLANK_PIPE_A)
-- enable_mask |= I915_DISPLAY_PIPE_A_VBLANK_INTERRUPT;
-- else
-- disable_mask |= I915_DISPLAY_PIPE_A_VBLANK_INTERRUPT;
--
-- if (pipe->pipe & DRM_I915_VBLANK_PIPE_B)
-- enable_mask |= I915_DISPLAY_PIPE_B_VBLANK_INTERRUPT;
-- else
-- disable_mask |= I915_DISPLAY_PIPE_B_VBLANK_INTERRUPT;
--
-- i915_enable_irq(dev_priv, enable_mask);
-- i915_disable_irq(dev_priv, disable_mask);
--
-- dev_priv->vblank_pipe = pipe->pipe;
--
- return 0;
- }
-
-@@ -514,19 +632,13 @@ int i915_vblank_pipe_get(struct drm_device *dev, void *data,
- {
- drm_i915_private_t *dev_priv = dev->dev_private;
- drm_i915_vblank_pipe_t *pipe = data;
-- u16 flag;
-
- if (!dev_priv) {
- DRM_ERROR("called with no initialization\n");
- return -EINVAL;
- }
-
-- flag = I915_READ(IMR);
-- pipe->pipe = 0;
-- if (flag & I915_DISPLAY_PIPE_A_VBLANK_INTERRUPT)
-- pipe->pipe |= DRM_I915_VBLANK_PIPE_A;
-- if (flag & I915_DISPLAY_PIPE_B_VBLANK_INTERRUPT)
-- pipe->pipe |= DRM_I915_VBLANK_PIPE_B;
-+ pipe->pipe = DRM_I915_VBLANK_PIPE_A | DRM_I915_VBLANK_PIPE_B;
-
- return 0;
- }
-@@ -540,9 +652,10 @@ int i915_vblank_swap(struct drm_device *dev, void *data,
- drm_i915_private_t *dev_priv = dev->dev_private;
- drm_i915_vblank_swap_t *swap = data;
- drm_i915_vbl_swap_t *vbl_swap;
-- unsigned int pipe, seqtype, curseq;
-+ unsigned int pipe, seqtype, curseq, plane;
- unsigned long irqflags;
- struct list_head *list;
-+ int ret;
-
- if (!dev_priv) {
- DRM_ERROR("%s called with no initialization\n", __func__);
-@@ -560,7 +673,8 @@ int i915_vblank_swap(struct drm_device *dev, void *data,
- return -EINVAL;
- }
-
-- pipe = (swap->seqtype & _DRM_VBLANK_SECONDARY) ? 1 : 0;
-+ plane = (swap->seqtype & _DRM_VBLANK_SECONDARY) ? 1 : 0;
-+ pipe = i915_get_pipe(dev, plane);
-
- seqtype = swap->seqtype & (_DRM_VBLANK_RELATIVE | _DRM_VBLANK_ABSOLUTE);
-
-@@ -579,7 +693,14 @@ int i915_vblank_swap(struct drm_device *dev, void *data,
-
- spin_unlock_irqrestore(&dev->drw_lock, irqflags);
-
-- curseq = atomic_read(pipe ? &dev->vbl_received2 : &dev->vbl_received);
-+ /*
-+ * We take the ref here and put it when the swap actually completes
-+ * in the tasklet.
-+ */
-+ ret = drm_vblank_get(dev, pipe);
-+ if (ret)
-+ return ret;
-+ curseq = drm_vblank_count(dev, pipe);
-
- if (seqtype == _DRM_VBLANK_RELATIVE)
- swap->sequence += curseq;
-@@ -589,6 +710,7 @@ int i915_vblank_swap(struct drm_device *dev, void *data,
- swap->sequence = curseq + 1;
- } else {
- DRM_DEBUG("Missed target sequence\n");
-+ drm_vblank_put(dev, pipe);
- return -EINVAL;
- }
- }
-@@ -599,7 +721,7 @@ int i915_vblank_swap(struct drm_device *dev, void *data,
- vbl_swap = list_entry(list, drm_i915_vbl_swap_t, head);
-
- if (vbl_swap->drw_id == swap->drawable &&
-- vbl_swap->pipe == pipe &&
-+ vbl_swap->plane == plane &&
- vbl_swap->sequence == swap->sequence) {
- spin_unlock_irqrestore(&dev_priv->swaps_lock, irqflags);
- DRM_DEBUG("Already scheduled\n");
-@@ -611,6 +733,7 @@ int i915_vblank_swap(struct drm_device *dev, void *data,
-
- if (dev_priv->swaps_pending >= 100) {
- DRM_DEBUG("Too many swaps queued\n");
-+ drm_vblank_put(dev, pipe);
- return -EBUSY;
- }
-
-@@ -618,13 +741,14 @@ int i915_vblank_swap(struct drm_device *dev, void *data,
-
- if (!vbl_swap) {
- DRM_ERROR("Failed to allocate memory to queue swap\n");
-+ drm_vblank_put(dev, pipe);
- return -ENOMEM;
- }
-
- DRM_DEBUG("\n");
-
- vbl_swap->drw_id = swap->drawable;
-- vbl_swap->pipe = pipe;
-+ vbl_swap->plane = plane;
- vbl_swap->sequence = swap->sequence;
-
- spin_lock_irqsave(&dev_priv->swaps_lock, irqflags);
-@@ -643,28 +767,32 @@ void i915_driver_irq_preinstall(struct drm_device * dev)
- {
- drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
-
-- I915_WRITE(HWSTAM, 0xfffe);
-- I915_WRITE(IMR, 0x0);
-+ I915_WRITE(HWSTAM, 0xeffe);
-+ I915_WRITE(IMR, 0xffffffff);
- I915_WRITE(IER, 0x0);
- }
-
--void i915_driver_irq_postinstall(struct drm_device * dev)
-+int i915_driver_irq_postinstall(struct drm_device *dev)
- {
- drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
-+ int ret, num_pipes = 2;
-
- spin_lock_init(&dev_priv->swaps_lock);
- INIT_LIST_HEAD(&dev_priv->vbl_swaps.head);
- dev_priv->swaps_pending = 0;
-
-- if (!dev_priv->vblank_pipe)
-- dev_priv->vblank_pipe = DRM_I915_VBLANK_PIPE_A;
--
- /* Set initial unmasked IRQs to just the selected vblank pipes. */
- dev_priv->irq_mask_reg = ~0;
-- if (dev_priv->vblank_pipe & DRM_I915_VBLANK_PIPE_A)
-- dev_priv->irq_mask_reg &= ~I915_DISPLAY_PIPE_A_VBLANK_INTERRUPT;
-- if (dev_priv->vblank_pipe & DRM_I915_VBLANK_PIPE_B)
-- dev_priv->irq_mask_reg &= ~I915_DISPLAY_PIPE_B_VBLANK_INTERRUPT;
-+
-+ ret = drm_vblank_init(dev, num_pipes);
-+ if (ret)
-+ return ret;
-+
-+ dev_priv->vblank_pipe = DRM_I915_VBLANK_PIPE_A | DRM_I915_VBLANK_PIPE_B;
-+ dev_priv->irq_mask_reg &= ~I915_DISPLAY_PIPE_A_VBLANK_INTERRUPT;
-+ dev_priv->irq_mask_reg &= ~I915_DISPLAY_PIPE_B_VBLANK_INTERRUPT;
-+
-+ dev->max_vblank_count = 0xffffff; /* only 24 bits of frame count */
-
- dev_priv->irq_mask_reg &= I915_INTERRUPT_ENABLE_MASK;
-
-@@ -673,22 +801,29 @@ void i915_driver_irq_postinstall(struct drm_device * dev)
- (void) I915_READ(IER);
-
- opregion_enable_asle(dev);
--
- DRM_INIT_WAITQUEUE(&dev_priv->irq_queue);
-+
-+ return 0;
- }
-
- void i915_driver_irq_uninstall(struct drm_device * dev)
- {
- drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
-- u16 temp;
-+ u32 temp;
-
- if (!dev_priv)
- return;
-
-- I915_WRITE(HWSTAM, 0xffff);
-- I915_WRITE(IMR, 0xffff);
-+ dev_priv->vblank_pipe = 0;
-+
-+ I915_WRITE(HWSTAM, 0xffffffff);
-+ I915_WRITE(IMR, 0xffffffff);
- I915_WRITE(IER, 0x0);
-
-+ temp = I915_READ(PIPEASTAT);
-+ I915_WRITE(PIPEASTAT, temp);
-+ temp = I915_READ(PIPEBSTAT);
-+ I915_WRITE(PIPEBSTAT, temp);
- temp = I915_READ(IIR);
- I915_WRITE(IIR, temp);
- }
-diff --git a/include/drm/drm.h b/include/drm/drm.h
-index 0864c69..15e5503 100644
---- a/include/drm/drm.h
-+++ b/include/drm/drm.h
-@@ -454,6 +454,7 @@ struct drm_irq_busid {
- enum drm_vblank_seq_type {
- _DRM_VBLANK_ABSOLUTE = 0x0, /**< Wait for specific vblank sequence number */
- _DRM_VBLANK_RELATIVE = 0x1, /**< Wait for given number of vblanks */
-+ _DRM_VBLANK_FLIP = 0x8000000, /**< Scheduled buffer swap should flip */
- _DRM_VBLANK_NEXTONMISS = 0x10000000, /**< If missed, wait for next vblank */
- _DRM_VBLANK_SECONDARY = 0x20000000, /**< Secondary display controller */
- _DRM_VBLANK_SIGNAL = 0x40000000 /**< Send signal instead of blocking */
-@@ -486,6 +487,19 @@ union drm_wait_vblank {
- struct drm_wait_vblank_reply reply;
- };
-
-+#define _DRM_PRE_MODESET 1
-+#define _DRM_POST_MODESET 2
-+
-+/**
-+ * DRM_IOCTL_MODESET_CTL ioctl argument type
-+ *
-+ * \sa drmModesetCtl().
-+ */
-+struct drm_modeset_ctl {
-+ uint32_t crtc;
-+ uint32_t cmd;
-+};
-+
- /**
- * DRM_IOCTL_AGP_ENABLE ioctl argument type.
- *
-@@ -570,6 +584,7 @@ struct drm_set_version {
- #define DRM_IOCTL_GET_CLIENT DRM_IOWR(0x05, struct drm_client)
- #define DRM_IOCTL_GET_STATS DRM_IOR( 0x06, struct drm_stats)
- #define DRM_IOCTL_SET_VERSION DRM_IOWR(0x07, struct drm_set_version)
-+#define DRM_IOCTL_MODESET_CTL DRM_IOW(0x08, struct drm_modeset_ctl)
-
- #define DRM_IOCTL_SET_UNIQUE DRM_IOW( 0x10, struct drm_unique)
- #define DRM_IOCTL_AUTH_MAGIC DRM_IOW( 0x11, struct drm_auth)
-diff --git a/include/drm/drmP.h b/include/drm/drmP.h
-index 1c1b13e..e79ce07 100644
---- a/include/drm/drmP.h
-+++ b/include/drm/drmP.h
-@@ -580,11 +580,54 @@ struct drm_driver {
- int (*kernel_context_switch) (struct drm_device *dev, int old,
- int new);
- void (*kernel_context_switch_unlock) (struct drm_device *dev);
-- int (*vblank_wait) (struct drm_device *dev, unsigned int *sequence);
-- int (*vblank_wait2) (struct drm_device *dev, unsigned int *sequence);
- int (*dri_library_name) (struct drm_device *dev, char *buf);
-
- /**
-+ * get_vblank_counter - get raw hardware vblank counter
-+ * @dev: DRM device
-+ * @crtc: counter to fetch
-+ *
-+ * Driver callback for fetching a raw hardware vblank counter
-+ * for @crtc. If a device doesn't have a hardware counter, the
-+ * driver can simply return the value of drm_vblank_count and
-+ * make the enable_vblank() and disable_vblank() hooks into no-ops,
-+ * leaving interrupts enabled at all times.
-+ *
-+ * Wraparound handling and loss of events due to modesetting is dealt
-+ * with in the DRM core code.
-+ *
-+ * RETURNS
-+ * Raw vblank counter value.
-+ */
-+ u32 (*get_vblank_counter) (struct drm_device *dev, int crtc);
-+
-+ /**
-+ * enable_vblank - enable vblank interrupt events
-+ * @dev: DRM device
-+ * @crtc: which irq to enable
-+ *
-+ * Enable vblank interrupts for @crtc. If the device doesn't have
-+ * a hardware vblank counter, this routine should be a no-op, since
-+ * interrupts will have to stay on to keep the count accurate.
-+ *
-+ * RETURNS
-+ * Zero on success, appropriate errno if the given @crtc's vblank
-+ * interrupt cannot be enabled.
-+ */
-+ int (*enable_vblank) (struct drm_device *dev, int crtc);
-+
-+ /**
-+ * disable_vblank - disable vblank interrupt events
-+ * @dev: DRM device
-+ * @crtc: which irq to enable
-+ *
-+ * Disable vblank interrupts for @crtc. If the device doesn't have
-+ * a hardware vblank counter, this routine should be a no-op, since
-+ * interrupts will have to stay on to keep the count accurate.
-+ */
-+ void (*disable_vblank) (struct drm_device *dev, int crtc);
-+
-+ /**
- * Called by \c drm_device_is_agp. Typically used to determine if a
- * card is really attached to AGP or not.
- *
-@@ -601,7 +644,7 @@ struct drm_driver {
-
- irqreturn_t(*irq_handler) (DRM_IRQ_ARGS);
- void (*irq_preinstall) (struct drm_device *dev);
-- void (*irq_postinstall) (struct drm_device *dev);
-+ int (*irq_postinstall) (struct drm_device *dev);
- void (*irq_uninstall) (struct drm_device *dev);
- void (*reclaim_buffers) (struct drm_device *dev,
- struct drm_file * file_priv);
-@@ -730,13 +773,28 @@ struct drm_device {
- /** \name VBLANK IRQ support */
- /*@{ */
-
-- wait_queue_head_t vbl_queue; /**< VBLANK wait queue */
-- atomic_t vbl_received;
-- atomic_t vbl_received2; /**< number of secondary VBLANK interrupts */
-+ /*
-+ * At load time, disabling the vblank interrupt won't be allowed since
-+ * old clients may not call the modeset ioctl and therefore misbehave.
-+ * Once the modeset ioctl *has* been called though, we can safely
-+ * disable them when unused.
-+ */
-+ int vblank_disable_allowed;
-+
-+ wait_queue_head_t *vbl_queue; /**< VBLANK wait queue */
-+ atomic_t *_vblank_count; /**< number of VBLANK interrupts (driver must alloc the right number of counters) */
- spinlock_t vbl_lock;
-- struct list_head vbl_sigs; /**< signal list to send on VBLANK */
-- struct list_head vbl_sigs2; /**< signals to send on secondary VBLANK */
-- unsigned int vbl_pending;
-+ struct list_head *vbl_sigs; /**< signal list to send on VBLANK */
-+ atomic_t vbl_signal_pending; /* number of signals pending on all crtcs*/
-+ atomic_t *vblank_refcount; /* number of users of vblank interruptsper crtc */
-+ u32 *last_vblank; /* protected by dev->vbl_lock, used */
-+ /* for wraparound handling */
-+ int *vblank_enabled; /* so we don't call enable more than
-+ once per disable */
-+ int *vblank_inmodeset; /* Display driver is setting mode */
-+ struct timer_list vblank_disable_timer;
-+
-+ u32 max_vblank_count; /**< size of vblank counter register */
- spinlock_t tasklet_lock; /**< For drm_locked_tasklet */
- void (*locked_tasklet_func)(struct drm_device *dev);
-
-@@ -757,6 +815,7 @@ struct drm_device {
- struct pci_controller *hose;
- #endif
- struct drm_sg_mem *sg; /**< Scatter gather memory */
-+ int num_crtcs; /**< Number of CRTCs on this device */
- void *dev_private; /**< device private data */
- struct drm_sigdata sigdata; /**< For block_all_signals */
- sigset_t sigmask;
-@@ -990,10 +1049,19 @@ extern void drm_driver_irq_preinstall(struct drm_device *dev);
- extern void drm_driver_irq_postinstall(struct drm_device *dev);
- extern void drm_driver_irq_uninstall(struct drm_device *dev);
-
-+extern int drm_vblank_init(struct drm_device *dev, int num_crtcs);
- extern int drm_wait_vblank(struct drm_device *dev, void *data,
-- struct drm_file *file_priv);
-+ struct drm_file *filp);
- extern int drm_vblank_wait(struct drm_device *dev, unsigned int *vbl_seq);
--extern void drm_vbl_send_signals(struct drm_device *dev);
-+extern void drm_locked_tasklet(struct drm_device *dev,
-+ void(*func)(struct drm_device *));
-+extern u32 drm_vblank_count(struct drm_device *dev, int crtc);
-+extern void drm_handle_vblank(struct drm_device *dev, int crtc);
-+extern int drm_vblank_get(struct drm_device *dev, int crtc);
-+extern void drm_vblank_put(struct drm_device *dev, int crtc);
-+/* Modesetting support */
-+extern int drm_modeset_ctl(struct drm_device *dev, void *data,
-+ struct drm_file *file_priv);
- extern void drm_locked_tasklet(struct drm_device *dev, void(*func)(struct drm_device*));
-
- /* AGP/GART support (drm_agpsupport.h) */