drm/vbl rework: rework how the drm deals with vblank.
authorJesse Barnes <jbarnes@virtuousgeek.org>
Tue, 22 Apr 2008 06:03:07 +0000 (16:03 +1000)
committerDave Airlie <airlied@linux.ie>
Sat, 26 Apr 2008 08:01:07 +0000 (18:01 +1000)
Other Authors: Michel Dänzer <michel@tungstengraphics.com>
mga: Ian Romanick <idr@us.ibm.com>
via: Thomas Hellstrom <thomas-at-tungstengraphics-dot-com>

This re-works the DRM internals to provide a better interface for drivers
to expose vblank on multiple crtcs.

It also includes work done by Michel on making i915 triple buffering and pageflipping work properly.

Signed-off-by: Dave Airlie <airlied@redhat.com>
20 files changed:
drivers/char/drm/drm.h
drivers/char/drm/drmP.h
drivers/char/drm/drm_irq.c
drivers/char/drm/i915_dma.c
drivers/char/drm/i915_drm.h
drivers/char/drm/i915_drv.c
drivers/char/drm/i915_drv.h
drivers/char/drm/i915_irq.c
drivers/char/drm/mga_drv.c
drivers/char/drm/mga_drv.h
drivers/char/drm/mga_irq.c
drivers/char/drm/r128_drv.c
drivers/char/drm/r128_drv.h
drivers/char/drm/r128_irq.c
drivers/char/drm/radeon_drv.c
drivers/char/drm/radeon_drv.h
drivers/char/drm/radeon_irq.c
drivers/char/drm/via_drv.c
drivers/char/drm/via_drv.h
drivers/char/drm/via_irq.c

index 3a05c6d5ebe1f9306bca6a3e11740d54b9e6e5ef..6874f31ca8caa329da769f13960bc0f08ae58f01 100644 (file)
@@ -471,6 +471,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 */
@@ -503,6 +504,21 @@ union drm_wait_vblank {
        struct drm_wait_vblank_reply reply;
 };
 
+enum drm_modeset_ctl_cmd {
+       _DRM_PRE_MODESET = 1,
+       _DRM_POST_MODESET = 2,
+};
+
+/**
+ * DRM_IOCTL_MODESET_CTL ioctl argument type
+ *
+ * \sa drmModesetCtl().
+ */
+struct drm_modeset_ctl {
+       unsigned long arg;
+       enum drm_modeset_ctl_cmd cmd;
+};
+
 /**
  * DRM_IOCTL_AGP_ENABLE ioctl argument type.
  *
@@ -587,6 +603,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)
index 5446235094d826fc8016d3bc78488d73aef685d8..ecee3547a13f2216d076c62708293b7ee168767d 100644 (file)
@@ -100,10 +100,8 @@ struct drm_device;
 #define DRIVER_HAVE_DMA    0x20
 #define DRIVER_HAVE_IRQ    0x40
 #define DRIVER_IRQ_SHARED  0x80
-#define DRIVER_IRQ_VBL     0x100
 #define DRIVER_DMA_QUEUE   0x200
 #define DRIVER_FB_DMA      0x400
-#define DRIVER_IRQ_VBL2    0x800
 
 /***********************************************************************/
 /** \name Begin the DRM... */
@@ -579,10 +577,52 @@ struct drm_driver {
        int (*context_dtor) (struct drm_device *dev, int context);
        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);
+       void (*kernel_context_switch_unlock) (struct drm_device * dev);
+       /**
+        * 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);
+       int (*dri_library_name) (struct drm_device *dev, char * buf);
 
        /**
         * Called by \c drm_device_is_agp.  Typically used to determine if a
@@ -601,7 +641,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 +770,21 @@ 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 */
+       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 interrupts per crtc */
+       u32 *last_vblank;               /* protected by dev->vbl_lock, used */
+                                       /* for wraparound handling */
+       u32 *vblank_offset;             /* used to track how many vblanks */
+       int *vblank_enabled;            /* so we don't call enable more than
+                                          once per disable */
+       u32 *vblank_premodeset;         /*  were lost during modeset */
+       struct timer_list vblank_disable_timer;
+
+       unsigned long max_vblank_count; /**< size of vblank counter register */
        spinlock_t tasklet_lock;        /**< For drm_locked_tasklet */
        void (*locked_tasklet_func)(struct drm_device *dev);
 
@@ -756,6 +804,7 @@ struct drm_device {
 #ifdef __alpha__
        struct pci_controller *hose;
 #endif
+       int num_crtcs;                  /**< Number of CRTCs on this device */
        struct drm_sg_mem *sg;  /**< Scatter gather memory */
        void *dev_private;              /**< device private data */
        struct drm_sigdata sigdata;        /**< For block_all_signals */
@@ -990,11 +1039,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_wait_vblank(struct drm_device *dev, void *data,
-                          struct drm_file *file_priv);
-extern int drm_vblank_wait(struct drm_device *dev, unsigned int *vbl_seq);
-extern void drm_vbl_send_signals(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 *filp);
+extern int drm_vblank_wait(struct drm_device * dev, unsigned int *vbl_seq);
 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_update_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);
 
                                /* AGP/GART support (drm_agpsupport.h) */
 extern struct drm_agp_head *drm_agp_init(struct drm_device *dev);
index 089c015c01d148234fcd24abac9f935c9fd185ef..286f9d61e7d5df50e89f05985c20e246cbeacba9 100644 (file)
@@ -71,6 +71,117 @@ 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;
+
+       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]) {
+                       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_premodeset, sizeof(*dev->vblank_premodeset) *
+                dev->num_crtcs, DRM_MEM_DRIVER);
+       drm_free(dev->vblank_offset, sizeof(*dev->vblank_offset) * 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_premodeset = drm_calloc(num_crtcs, sizeof(u32),
+                                           DRM_MEM_DRIVER);
+       if (!dev->vblank_premodeset)
+               goto err;
+
+       dev->vblank_offset = drm_calloc(num_crtcs, sizeof(u32), DRM_MEM_DRIVER);
+       if (!dev->vblank_offset)
+               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);
+       }
+
+       return 0;
+
+err:
+       drm_vblank_cleanup(dev);
+       return ret;
+}
+EXPORT_SYMBOL(drm_vblank_init);
+
 /**
  * Install IRQ handler.
  *
@@ -109,17 +220,6 @@ static int drm_irq_install(struct drm_device * dev)
 
        DRM_DEBUG("irq=%d\n", dev->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);
 
@@ -137,9 +237,14 @@ 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;
 }
 
 /**
@@ -170,6 +275,8 @@ int drm_irq_uninstall(struct drm_device * dev)
 
        free_irq(dev->irq, dev);
 
+       drm_vblank_cleanup(dev);
+
        dev->locked_tasklet_func = NULL;
 
        return 0;
@@ -213,6 +320,148 @@ 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]) +
+               dev->vblank_offset[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.
+ */
+void drm_update_vblank_count(struct drm_device *dev, int crtc)
+{
+       unsigned long irqflags;
+       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);
+       spin_lock_irqsave(&dev->vbl_lock, irqflags);
+       if (cur_vblank < dev->last_vblank[crtc]) {
+               diff = dev->max_vblank_count -
+                       dev->last_vblank[crtc];
+               diff += cur_vblank;
+       } else {
+               diff = cur_vblank - dev->last_vblank[crtc];
+       }
+       dev->last_vblank[crtc] = cur_vblank;
+       spin_unlock_irqrestore(&dev->vbl_lock, irqflags);
+
+       atomic_add(diff, &dev->_vblank_count[crtc]);
+}
+EXPORT_SYMBOL(drm_update_vblank_count);
+
+/**
+ * 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.  Note callers will probably want to update the master counter
+ * using drm_update_vblank_count() above before calling this routine so that
+ * wakeups occur on the right vblank event.
+ *
+ * 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);
+               if (ret)
+                       atomic_dec(&dev->vblank_refcount[crtc]);
+               else
+                       dev->vblank_enabled[crtc] = 1;
+       }
+       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.
+ */
+int drm_modeset_ctl(struct drm_device *dev, void *data,
+                   struct drm_file *file_priv)
+{
+       struct drm_modeset_ctl *modeset = data;
+       int crtc, ret = 0;
+       u32 new;
+
+       crtc = modeset->arg;
+       if (crtc >= dev->num_crtcs) {
+               ret = -EINVAL;
+               goto out;
+       }
+
+       switch (modeset->cmd) {
+       case _DRM_PRE_MODESET:
+               dev->vblank_premodeset[crtc] =
+                       dev->driver->get_vblank_counter(dev, crtc);
+               break;
+       case _DRM_POST_MODESET:
+               new = dev->driver->get_vblank_counter(dev, crtc);
+               dev->vblank_offset[crtc] = dev->vblank_premodeset[crtc] - new;
+               break;
+       default:
+               ret = -EINVAL;
+               break;
+       }
+
+out:
+       return ret;
+}
+
 /**
  * Wait for VBLANK.
  *
@@ -232,12 +481,13 @@ 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->irq) || (!dev->irq_enabled))
                return -EINVAL;
@@ -251,13 +501,13 @@ 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);
+       drm_update_vblank_count(dev, crtc);
+       seq = drm_vblank_count(dev, crtc);
 
        switch (vblwait->request.type & _DRM_VBLANK_TYPES_MASK) {
        case _DRM_VBLANK_RELATIVE:
@@ -276,8 +526,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);
@@ -298,22 +547,26 @@ 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;
                }
 
-               dev->vbl_pending++;
-
                spin_unlock_irqrestore(&dev->vbl_lock, irqflags);
 
-               if (!
-                   (vbl_sig =
-                    drm_alloc(sizeof(struct drm_vbl_sig), DRM_MEM_DRIVER))) {
+               vbl_sig = drm_calloc(1, sizeof(struct drm_vbl_sig),
+                                    DRM_MEM_DRIVER);
+               if (!vbl_sig)
                        return -ENOMEM;
+
+               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;
@@ -327,17 +580,20 @@ 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);
-
+               unsigned long cur_vblank;
+
+               ret = drm_vblank_get(dev, crtc);
+               if (ret)
+                       return ret;
+               DRM_WAIT_ON(ret, dev->vbl_queue[crtc], 3 * DRM_HZ,
+                           (((cur_vblank = drm_vblank_count(dev, crtc))
+                             - vblwait->request.sequence) <= (1 << 23)));
+               drm_vblank_put(dev, crtc);
                do_gettimeofday(&now);
+
                vblwait->reply.tval_sec = now.tv_sec;
                vblwait->reply.tval_usec = now.tv_usec;
+               vblwait->reply.sequence = cur_vblank;
        }
 
       done:
@@ -348,44 +604,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);
+               list_del(&vbl_sig->head);
 
-                               drm_free(vbl_sig, sizeof(*vbl_sig),
-                                        DRM_MEM_DRIVER);
-
-                               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)
+{
+       drm_update_vblank_count(dev, crtc);
+       DRM_WAKEUP(&dev->vbl_queue[crtc]);
+       drm_vbl_send_signals(dev, crtc);
+}
+EXPORT_SYMBOL(drm_handle_vblank);
 
 /**
  * Tasklet wrapper function.
index a043bb12301acd9c1cf087663659c775316dd9f2..ef7bf143a80c632a9c8044fa7e0d77f4fbc3a57f 100644 (file)
@@ -415,10 +415,13 @@ static void i915_emit_breadcrumb(struct drm_device *dev)
        drm_i915_private_t *dev_priv = dev->dev_private;
        RING_LOCALS;
 
-       dev_priv->sarea_priv->last_enqueue = ++dev_priv->counter;
+       if (++dev_priv->counter > BREADCRUMB_MASK) {
+                dev_priv->counter = 1;
+                DRM_DEBUG("Breadcrumb counter wrapped around\n");
+       }
 
-       if (dev_priv->counter > 0x7FFFFFFFUL)
-               dev_priv->sarea_priv->last_enqueue = dev_priv->counter = 1;
+       if (dev_priv->sarea_priv)
+               dev_priv->sarea_priv->last_enqueue = dev_priv->counter;
 
        BEGIN_LP_RING(4);
        OUT_RING(CMD_STORE_DWORD_IDX);
@@ -428,6 +431,26 @@ static void i915_emit_breadcrumb(struct drm_device *dev)
        ADVANCE_LP_RING();
 }
 
+int i915_emit_mi_flush(struct drm_device *dev, uint32_t flush)
+{
+       drm_i915_private_t *dev_priv = dev->dev_private;
+       uint32_t flush_cmd = CMD_MI_FLUSH;
+       RING_LOCALS;
+
+       flush_cmd |= flush;
+
+       i915_kernel_lost_context(dev);
+
+       BEGIN_LP_RING(4);
+       OUT_RING(flush_cmd);
+       OUT_RING(0);
+       OUT_RING(0);
+       OUT_RING(0);
+       ADVANCE_LP_RING();
+
+       return 0;
+}
+
 static int i915_dispatch_cmdbuffer(struct drm_device * dev,
                                   drm_i915_cmdbuffer_t * cmd)
 {
@@ -511,52 +534,74 @@ static int i915_dispatch_batchbuffer(struct drm_device * dev,
        return 0;
 }
 
-static int i915_dispatch_flip(struct drm_device * dev)
+static void i915_do_dispatch_flip(struct drm_device * dev, int plane, int sync)
 {
        drm_i915_private_t *dev_priv = dev->dev_private;
+       u32 num_pages, current_page, next_page, dspbase;
+       int shift = 2 * plane, x, y;
        RING_LOCALS;
 
-       DRM_DEBUG("%s: page=%d pfCurrentPage=%d\n",
-                 __FUNCTION__,
-                 dev_priv->current_page,
-                 dev_priv->sarea_priv->pf_current_page);
+       /* Calculate display base offset */
+       num_pages = dev_priv->sarea_priv->third_handle ? 3 : 2;
+       current_page = (dev_priv->sarea_priv->pf_current_page >> shift) & 0x3;
+       next_page = (current_page + 1) % num_pages;
 
-       i915_kernel_lost_context(dev);
-
-       BEGIN_LP_RING(2);
-       OUT_RING(INST_PARSER_CLIENT | INST_OP_FLUSH | INST_FLUSH_MAP_CACHE);
-       OUT_RING(0);
-       ADVANCE_LP_RING();
+       switch (next_page) {
+       default:
+       case 0:
+               dspbase = dev_priv->sarea_priv->front_offset;
+               break;
+       case 1:
+               dspbase = dev_priv->sarea_priv->back_offset;
+               break;
+       case 2:
+               dspbase = dev_priv->sarea_priv->third_offset;
+               break;
+       }
 
-       BEGIN_LP_RING(6);
-       OUT_RING(CMD_OP_DISPLAYBUFFER_INFO | ASYNC_FLIP);
-       OUT_RING(0);
-       if (dev_priv->current_page == 0) {
-               OUT_RING(dev_priv->back_offset);
-               dev_priv->current_page = 1;
+       if (plane == 0) {
+               x = dev_priv->sarea_priv->planeA_x;
+               y = dev_priv->sarea_priv->planeA_y;
        } else {
-               OUT_RING(dev_priv->front_offset);
-               dev_priv->current_page = 0;
+               x = dev_priv->sarea_priv->planeB_x;
+               y = dev_priv->sarea_priv->planeB_y;
        }
-       OUT_RING(0);
-       ADVANCE_LP_RING();
 
-       BEGIN_LP_RING(2);
-       OUT_RING(MI_WAIT_FOR_EVENT | MI_WAIT_FOR_PLANE_A_FLIP);
-       OUT_RING(0);
-       ADVANCE_LP_RING();
+       dspbase += (y * dev_priv->sarea_priv->pitch + x) * dev_priv->cpp;
 
-       dev_priv->sarea_priv->last_enqueue = dev_priv->counter++;
+       DRM_DEBUG("plane=%d current_page=%d dspbase=0x%x\n", plane, current_page,
+                 dspbase);
 
        BEGIN_LP_RING(4);
-       OUT_RING(CMD_STORE_DWORD_IDX);
-       OUT_RING(20);
-       OUT_RING(dev_priv->counter);
-       OUT_RING(0);
+       OUT_RING(sync ? 0 :
+                (MI_WAIT_FOR_EVENT | (plane ? MI_WAIT_FOR_PLANE_B_FLIP :
+                                      MI_WAIT_FOR_PLANE_A_FLIP)));
+       OUT_RING(CMD_OP_DISPLAYBUFFER_INFO | (sync ? 0 : ASYNC_FLIP) |
+                (plane ? DISPLAY_PLANE_B : DISPLAY_PLANE_A));
+       OUT_RING(dev_priv->sarea_priv->pitch * dev_priv->cpp);
+       OUT_RING(dspbase);
        ADVANCE_LP_RING();
 
-       dev_priv->sarea_priv->pf_current_page = dev_priv->current_page;
-       return 0;
+       dev_priv->sarea_priv->pf_current_page &= ~(0x3 << shift);
+       dev_priv->sarea_priv->pf_current_page |= next_page << shift;
+}
+
+void i915_dispatch_flip(struct drm_device * dev, int planes, int sync)
+{
+       drm_i915_private_t *dev_priv = dev->dev_private;
+       int i;
+
+       DRM_DEBUG("planes=0x%x pfCurrentPage=%d\n",
+                 planes, dev_priv->sarea_priv->pf_current_page);
+
+       i915_emit_mi_flush(dev, MI_READ_FLUSH | MI_EXE_FLUSH);
+
+       for (i = 0; i < 2; i++)
+               if (planes & (1 << i))
+                       i915_do_dispatch_flip(dev, i, sync);
+
+       i915_emit_breadcrumb(dev);
+
 }
 
 static int i915_quiescent(struct drm_device * dev)
@@ -579,7 +624,6 @@ static int i915_batchbuffer(struct drm_device *dev, void *data,
                            struct drm_file *file_priv)
 {
        drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
-       u32 *hw_status = dev_priv->hw_status_page;
        drm_i915_sarea_t *sarea_priv = (drm_i915_sarea_t *)
            dev_priv->sarea_priv;
        drm_i915_batchbuffer_t *batch = data;
@@ -602,7 +646,7 @@ static int i915_batchbuffer(struct drm_device *dev, void *data,
 
        ret = i915_dispatch_batchbuffer(dev, batch);
 
-       sarea_priv->last_dispatch = (int)hw_status[5];
+       sarea_priv->last_dispatch = READ_BREADCRUMB(dev_priv);
        return ret;
 }
 
@@ -610,7 +654,6 @@ static int i915_cmdbuffer(struct drm_device *dev, void *data,
                          struct drm_file *file_priv)
 {
        drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
-       u32 *hw_status = dev_priv->hw_status_page;
        drm_i915_sarea_t *sarea_priv = (drm_i915_sarea_t *)
            dev_priv->sarea_priv;
        drm_i915_cmdbuffer_t *cmdbuf = data;
@@ -635,18 +678,51 @@ static int i915_cmdbuffer(struct drm_device *dev, void *data,
                return ret;
        }
 
-       sarea_priv->last_dispatch = (int)hw_status[5];
+       sarea_priv->last_dispatch = READ_BREADCRUMB(dev_priv);
+       return 0;
+}
+
+static int i915_do_cleanup_pageflip(struct drm_device * dev)
+{
+       drm_i915_private_t *dev_priv = dev->dev_private;
+       int i, planes, num_pages = dev_priv->sarea_priv->third_handle ? 3 : 2;
+
+       DRM_DEBUG("\n");
+
+       for (i = 0, planes = 0; i < 2; i++)
+               if (dev_priv->sarea_priv->pf_current_page & (0x3 << (2 * i))) {
+                       dev_priv->sarea_priv->pf_current_page =
+                               (dev_priv->sarea_priv->pf_current_page &
+                                ~(0x3 << (2 * i))) | ((num_pages - 1) << (2 * i));
+
+                       planes |= 1 << i;
+               }
+
+       if (planes)
+               i915_dispatch_flip(dev, planes, 0);
+
        return 0;
 }
 
 static int i915_flip_bufs(struct drm_device *dev, void *data,
                          struct drm_file *file_priv)
 {
-       DRM_DEBUG("%s\n", __FUNCTION__);
+       drm_i915_flip_t *param = data;
+
+       DRM_DEBUG("\n");
 
        LOCK_TEST_WITH_RETURN(dev, file_priv);
 
-       return i915_dispatch_flip(dev);
+       /* This is really planes */
+       if (param->pipes & ~0x3) {
+               DRM_ERROR("Invalid planes 0x%x, only <= 0x3 is valid\n",
+                         param->pipes);
+               return -EINVAL;
+       }
+
+       i915_dispatch_flip(dev, param->pipes, 0);
+
+       return 0;
 }
 
 static int i915_getparam(struct drm_device *dev, void *data,
@@ -807,6 +883,8 @@ void i915_driver_lastclose(struct drm_device * dev)
        if (!dev_priv)
                return;
 
+       if (drm_getsarea(dev) && dev_priv->sarea_priv)
+               i915_do_cleanup_pageflip(dev);
        if (dev_priv->agp_heap)
                i915_mem_takedown(&(dev_priv->agp_heap));
 
index 05c66cf03a9eb02a0da22304ab4d07d95209b16b..0431c00e2289b6e6fcf03db0c2e8d7c1107e3238 100644 (file)
@@ -105,14 +105,29 @@ typedef struct _drm_i915_sarea {
        unsigned int rotated_tiled;
        unsigned int rotated2_tiled;
 
-       int pipeA_x;
-       int pipeA_y;
-       int pipeA_w;
-       int pipeA_h;
-       int pipeB_x;
-       int pipeB_y;
-       int pipeB_w;
-       int pipeB_h;
+       int planeA_x;
+       int planeA_y;
+       int planeA_w;
+       int planeA_h;
+       int planeB_x;
+       int planeB_y;
+       int planeB_w;
+       int planeB_h;
+
+       /* Triple buffering */
+       drm_handle_t third_handle;
+       int third_offset;
+       int third_size;
+       unsigned int third_tiled;
+
+       /* buffer object handles for the static buffers.  May change
+        * over the lifetime of the client, though it doesn't in our current
+        * implementation.
+        */
+       unsigned int front_bo_handle;
+       unsigned int back_bo_handle;
+       unsigned int third_bo_handle;
+       unsigned int depth_bo_handle;
 } drm_i915_sarea_t;
 
 /* Flags for perf_boxes
@@ -146,7 +161,7 @@ typedef struct _drm_i915_sarea {
 
 #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)
-#define DRM_IOCTL_I915_FLIP            DRM_IO ( DRM_COMMAND_BASE + DRM_I915_FLIP)
+#define DRM_IOCTL_I915_FLIP            DRM_IOW( DRM_COMMAND_BASE + DRM_I915_FLIP, drm_i915_flip_t)
 #define DRM_IOCTL_I915_BATCHBUFFER     DRM_IOW( DRM_COMMAND_BASE + DRM_I915_BATCHBUFFER, drm_i915_batchbuffer_t)
 #define DRM_IOCTL_I915_IRQ_EMIT         DRM_IOWR(DRM_COMMAND_BASE + DRM_I915_IRQ_EMIT, drm_i915_irq_emit_t)
 #define DRM_IOCTL_I915_IRQ_WAIT         DRM_IOW( DRM_COMMAND_BASE + DRM_I915_IRQ_WAIT, drm_i915_irq_wait_t)
@@ -161,6 +176,18 @@ typedef struct _drm_i915_sarea {
 #define DRM_IOCTL_I915_GET_VBLANK_PIPE DRM_IOR( DRM_COMMAND_BASE + DRM_I915_GET_VBLANK_PIPE, drm_i915_vblank_pipe_t)
 #define DRM_IOCTL_I915_VBLANK_SWAP     DRM_IOWR(DRM_COMMAND_BASE + DRM_I915_VBLANK_SWAP, drm_i915_vblank_swap_t)
 
+/* Asynchronous page flipping:
+ */
+typedef struct drm_i915_flip {
+       /*
+        * This is really talking about planes, and we could rename it
+        * except for the fact that some of the duplicated i915_drm.h files
+        * out there check for HAVE_I915_FLIP and so might pick up this
+        * version.
+        */
+       int pipes;
+} drm_i915_flip_t;
+
 /* Allow drivers to submit batchbuffers directly to hardware, relying
  * on the security mechanisms provided by hardware.
  */
index b2b451dc44608870a48cbfea56b7de522044aa40..bb8f1b2fb3835588026e00d1f961663e08e6c910 100644 (file)
@@ -533,8 +533,7 @@ static struct drm_driver driver = {
         */
        .driver_features =
            DRIVER_USE_AGP | DRIVER_REQUIRE_AGP | /* DRIVER_USE_MTRR |*/
-           DRIVER_HAVE_IRQ | DRIVER_IRQ_SHARED | DRIVER_IRQ_VBL |
-           DRIVER_IRQ_VBL2,
+           DRIVER_HAVE_IRQ | DRIVER_IRQ_SHARED,
        .load = i915_driver_load,
        .unload = i915_driver_unload,
        .lastclose = i915_driver_lastclose,
@@ -542,8 +541,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,
index 50cd68d6b6cc996baa4a3db2a9dbccbe077fc103..c614d78b3dfdfca2a69ba4bc5ee8c85e376760f1 100644 (file)
@@ -76,8 +76,9 @@ 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;
+       int flip;
 } drm_i915_vbl_swap_t;
 
 typedef struct drm_i915_private {
@@ -90,7 +91,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;
 
@@ -103,13 +104,18 @@ typedef struct drm_i915_private {
 
        wait_queue_head_t irq_queue;
        atomic_t irq_received;
-       atomic_t irq_emitted;
+       atomic_t irq_emited;
 
        int tex_lru_log_granularity;
        int allow_batchbuffer;
        struct mem_block *agp_heap;
        unsigned int sr01, adpa, ppcr, dvob, dvoc, lvds;
        int vblank_pipe;
+       spinlock_t user_irq_lock;
+       int user_irq_refcount;
+       int fence_irq_on;
+       uint32_t irq_enable_reg;
+       int irq_enabled;
 
        spinlock_t swaps_lock;
        drm_i915_vbl_swap_t vbl_swaps;
@@ -216,7 +222,7 @@ extern void i915_driver_preclose(struct drm_device *dev,
 extern int i915_driver_device_is_agp(struct drm_device * dev);
 extern long i915_compat_ioctl(struct file *filp, unsigned int cmd,
                              unsigned long arg);
-
+extern void i915_dispatch_flip(struct drm_device * dev, int pipes, int sync);
 /* i915_irq.c */
 extern int i915_irq_emit(struct drm_device *dev, void *data,
                         struct drm_file *file_priv);
@@ -227,7 +233,7 @@ extern int i915_driver_vblank_wait(struct drm_device *dev, unsigned int *sequenc
 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);
@@ -235,6 +241,9 @@ extern int i915_vblank_pipe_get(struct drm_device *dev, void *data,
                                struct drm_file *file_priv);
 extern int i915_vblank_swap(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);
 
 /* i915_mem.c */
 extern int i915_mem_alloc(struct drm_device *dev, void *data,
@@ -379,21 +388,91 @@ extern int i915_wait_ring(struct drm_device * dev, int n, const char *caller);
 
 /* Interrupt bits:
  */
-#define USER_INT_FLAG    (1<<1)
-#define VSYNC_PIPEB_FLAG (1<<5)
-#define VSYNC_PIPEA_FLAG (1<<7)
-#define HWB_OOM_FLAG     (1<<13) /* binner out of memory */
+#define I915_PIPE_CONTROL_NOTIFY_INTERRUPT             (1<<18)
+#define I915_DISPLAY_PORT_INTERRUPT                    (1<<17)
+#define I915_RENDER_COMMAND_PARSER_ERROR_INTERRUPT     (1<<15)
+#define I915_GMCH_THERMAL_SENSOR_EVENT_INTERRUPT       (1<<14)
+#define I915_HWB_OOM_INTERRUPT                         (1<<13) /* binner out of memory */
+#define I915_SYNC_STATUS_INTERRUPT                     (1<<12)
+#define I915_DISPLAY_PLANE_A_FLIP_PENDING_INTERRUPT    (1<<11)
+#define I915_DISPLAY_PLANE_B_FLIP_PENDING_INTERRUPT    (1<<10)
+#define I915_OVERLAY_PLANE_FLIP_PENDING_INTERRUPT      (1<<9)
+#define I915_DISPLAY_PLANE_C_FLIP_PENDING_INTERRUPT    (1<<8)
+#define I915_DISPLAY_PIPE_A_VBLANK_INTERRUPT           (1<<7)
+#define I915_DISPLAY_PIPE_A_EVENT_INTERRUPT            (1<<6)
+#define I915_DISPLAY_PIPE_B_VBLANK_INTERRUPT           (1<<5)
+#define I915_DISPLAY_PIPE_B_EVENT_INTERRUPT            (1<<4)
+#define I915_DEBUG_INTERRUPT                           (1<<2)
+#define I915_USER_INTERRUPT                            (1<<1)
+
 
 #define I915REG_HWSTAM         0x02098
 #define I915REG_INT_IDENTITY_R 0x020a4
 #define I915REG_INT_MASK_R     0x020a8
 #define I915REG_INT_ENABLE_R   0x020a0
+#define I915REG_INSTPM         0x020c0
+
+#define PIPEADSL               0x70000
+#define PIPEBDSL               0x71000
 
 #define I915REG_PIPEASTAT      0x70024
 #define I915REG_PIPEBSTAT      0x71024
+/*
+ * The two pipe frame counter registers are not synchronized, so
+ * reading a stable value is somewhat tricky. The following code
+ * should work:
+ *
+ *  do {
+ *    high1 = ((INREG(PIPEAFRAMEHIGH) & PIPE_FRAME_HIGH_MASK) >>
+ *             PIPE_FRAME_HIGH_SHIFT;
+ *    low1 =  ((INREG(PIPEAFRAMEPIXEL) & PIPE_FRAME_LOW_MASK) >>
+ *             PIPE_FRAME_LOW_SHIFT);
+ *    high2 = ((INREG(PIPEAFRAMEHIGH) & PIPE_FRAME_HIGH_MASK) >>
+ *             PIPE_FRAME_HIGH_SHIFT);
+ *  } while (high1 != high2);
+ *  frame = (high1 << 8) | low1;
+ */
+#define PIPEAFRAMEHIGH          0x70040
+#define PIPEBFRAMEHIGH         0x71040
+#define PIPE_FRAME_HIGH_MASK    0x0000ffff
+#define PIPE_FRAME_HIGH_SHIFT   0
+#define PIPEAFRAMEPIXEL         0x70044
+#define PIPEBFRAMEPIXEL                0x71044
 
-#define I915_VBLANK_INTERRUPT_ENABLE   (1UL<<17)
-#define I915_VBLANK_CLEAR              (1UL<<1)
+#define PIPE_FRAME_LOW_MASK     0xff000000
+#define PIPE_FRAME_LOW_SHIFT    24
+/*
+ * Pixel within the current frame is counted in the PIPEAFRAMEPIXEL register
+ * and is 24 bits wide.
+ */
+#define PIPE_PIXEL_MASK         0x00ffffff
+#define PIPE_PIXEL_SHIFT        0
+
+#define I915_FIFO_UNDERRUN_STATUS              (1UL<<31)
+#define I915_CRC_ERROR_ENABLE                  (1UL<<29)
+#define I915_CRC_DONE_ENABLE                   (1UL<<28)
+#define I915_GMBUS_EVENT_ENABLE                        (1UL<<27)
+#define I915_VSYNC_INTERRUPT_ENABLE            (1UL<<25)
+#define I915_DISPLAY_LINE_COMPARE_ENABLE       (1UL<<24)
+#define I915_DPST_EVENT_ENABLE                 (1UL<<23)
+#define I915_LEGACY_BLC_EVENT_ENABLE           (1UL<<22)
+#define I915_ODD_FIELD_INTERRUPT_ENABLE                (1UL<<21)
+#define I915_EVEN_FIELD_INTERRUPT_ENABLE       (1UL<<20)
+#define I915_START_VBLANK_INTERRUPT_ENABLE     (1UL<<18)       /* 965 or later */
+#define I915_VBLANK_INTERRUPT_ENABLE           (1UL<<17)
+#define I915_OVERLAY_UPDATED_ENABLE            (1UL<<16)
+#define I915_CRC_ERROR_INTERRUPT_STATUS                (1UL<<13)
+#define I915_CRC_DONE_INTERRUPT_STATUS         (1UL<<12)
+#define I915_GMBUS_INTERRUPT_STATUS            (1UL<<11)
+#define I915_VSYNC_INTERRUPT_STATUS            (1UL<<9)
+#define I915_DISPLAY_LINE_COMPARE_STATUS       (1UL<<8)
+#define I915_DPST_EVENT_STATUS                 (1UL<<7)
+#define I915_LEGACY_BLC_EVENT_STATUS           (1UL<<6)
+#define I915_ODD_FIELD_INTERRUPT_STATUS                (1UL<<5)
+#define I915_EVEN_FIELD_INTERRUPT_STATUS       (1UL<<4)
+#define I915_START_VBLANK_INTERRUPT_STATUS     (1UL<<2)        /* 965 or later */
+#define I915_VBLANK_INTERRUPT_STATUS           (1UL<<1)
+#define I915_OVERLAY_UPDATED_STATUS            (1UL<<0)
 
 #define SRX_INDEX              0x3c4
 #define SRX_DATA               0x3c5
index f7f16e7a8bf3c8d724065268052b9659c99a2d61..023ce66ef3ab2f8858b3946f9777484836c8a799 100644 (file)
 
 #define MAX_NOPID ((u32)~0)
 
+/**
+ * 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 a synchronous flip.
+ *
+ * This function must be called with the drawable spinlock held.
+ */
+static void
+i915_dispatch_vsync_flip(struct drm_device *dev, struct drm_drawable_info *drw,
+                        int plane)
+{
+       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_planes = 1 << plane;
+
+       /* If the window is visible on the other plane, we have to flip on that
+        * plane as well.
+        */
+       if (plane == 1) {
+               x1 = sarea_priv->planeA_x;
+               y1 = sarea_priv->planeA_y;
+               x2 = x1 + sarea_priv->planeA_w;
+               y2 = y1 + sarea_priv->planeA_h;
+       } else {
+               x1 = sarea_priv->planeB_x;
+               y1 = sarea_priv->planeB_y;
+               x2 = x1 + sarea_priv->planeB_w;
+               y2 = y1 + sarea_priv->planeB_h;
+       }
+
+       if (x2 > 0 && y2 > 0) {
+               int i, num_rects = drw->num_rects;
+               struct drm_clip_rect *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_planes = 0x3;
+
+                               break;
+                       }
+       }
+
+       i915_dispatch_flip(dev, pf_planes, 1);
+}
+
 /**
  * Emit blits for scheduled buffer swaps.
  *
 static void i915_vblank_tasklet(struct drm_device *dev)
 {
        drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
-       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) };
+       int nhits, nrects, slice[2], upper[2], lower[2], i, num_pages;
+       unsigned counter[2];
        struct drm_drawable_info *drw;
        drm_i915_sarea_t *sarea_priv = dev_priv->sarea_priv;
-       u32 cpp = dev_priv->cpp;
+       u32 cpp = dev_priv->cpp,  offsets[3];
        u32 cmd = (cpp == 4) ? (XY_SRC_COPY_BLT_CMD |
                                XY_SRC_COPY_BLT_WRITE_ALPHA |
                                XY_SRC_COPY_BLT_WRITE_RGB)
                             : XY_SRC_COPY_BLT_CMD;
        u32 src_pitch = sarea_priv->pitch * cpp;
        u32 dst_pitch = sarea_priv->pitch * cpp;
+       /* COPY rop (0xcc), map cpp to magic color depth constants */
        u32 ropcpp = (0xcc << 16) | ((cpp - 1) << 24);
        RING_LOCALS;
 
@@ -71,24 +173,34 @@ 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);
 
        nhits = nrects = 0;
 
-       spin_lock_irqsave(&dev_priv->swaps_lock, irqflags);
+       /* No irqsave/restore necessary.  This tasklet may be run in an
+        * interrupt context or normal context, but we don't have to worry
+        * about getting interrupted by something acquiring the lock, because
+        * we are the interrupt context thing that acquires the lock.
+        */
+       spin_lock(&dev_priv->swaps_lock);
 
        /* Find buffer swaps scheduled for this vertical blank */
        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);
@@ -126,43 +238,23 @@ static void i915_vblank_tasklet(struct drm_device *dev)
                spin_lock(&dev_priv->swaps_lock);
        }
 
-       if (nhits == 0) {
-               spin_unlock_irqrestore(&dev_priv->swaps_lock, irqflags);
-               return;
-       }
-
        spin_unlock(&dev_priv->swaps_lock);
 
-       i915_kernel_lost_context(dev);
-
-       if (IS_I965G(dev)) {
-               BEGIN_LP_RING(4);
-
-               OUT_RING(GFX_OP_DRAWRECT_INFO_I965);
-               OUT_RING(0);
-               OUT_RING(((sarea_priv->width - 1) & 0xffff) | ((sarea_priv->height - 1) << 16));
-               OUT_RING(0);
-               ADVANCE_LP_RING();
-       } else {
-               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();
-       }
+       if (nhits == 0)
+               return;
 
-       sarea_priv->ctxOwner = DRM_KERNEL_CONTEXT;
+       i915_kernel_lost_context(dev);
 
        upper[0] = upper[1] = 0;
-       slice[0] = max(sarea_priv->pipeA_h / nhits, 1);
-       slice[1] = max(sarea_priv->pipeB_h / nhits, 1);
-       lower[0] = sarea_priv->pipeA_y + slice[0];
-       lower[1] = sarea_priv->pipeB_y + slice[0];
+       slice[0] = max(sarea_priv->planeA_h / nhits, 1);
+       slice[1] = max(sarea_priv->planeB_h / nhits, 1);
+       lower[0] = sarea_priv->planeA_y + slice[0];
+       lower[1] = sarea_priv->planeB_y + slice[0];
+
+       offsets[0] = sarea_priv->front_offset;
+       offsets[1] = sarea_priv->back_offset;
+       offsets[2] = sarea_priv->third_offset;
+       num_pages = sarea_priv->third_handle ? 3 : 2;
 
        spin_lock(&dev->drw_lock);
 
@@ -174,6 +266,8 @@ static void i915_vblank_tasklet(struct drm_device *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;
 
@@ -181,7 +275,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, front, back;
                        unsigned short top, bottom;
 
                        drw = drm_get_drawable_info(dev, swap_hit->drw_id);
@@ -189,10 +283,50 @@ static void i915_vblank_tasklet(struct drm_device *dev)
                        if (!drw)
                                continue;
 
+                       plane = swap_hit->plane;
+
+                       if (swap_hit->flip) {
+                               i915_dispatch_vsync_flip(dev, drw, plane);
+                               continue;
+                       }
+
+                       if (init_drawrect) {
+                               int width  = sarea_priv->width;
+                               int height = sarea_priv->height;
+                               if (IS_I965G(dev)) {
+                                       BEGIN_LP_RING(4);
+
+                                       OUT_RING(GFX_OP_DRAWRECT_INFO_I965);
+                                       OUT_RING(0);
+                                       OUT_RING(((width - 1) & 0xffff) | ((height - 1) << 16));
+                                       OUT_RING(0);
+
+                                       ADVANCE_LP_RING();
+                               } else {
+                                       BEGIN_LP_RING(6);
+
+                                       OUT_RING(GFX_OP_DRAWRECT_INFO);
+                                       OUT_RING(0);
+                                       OUT_RING(0);
+                                       OUT_RING(((width - 1) & 0xffff) | ((height - 1) << 16));
+                                       OUT_RING(0);
+                                       OUT_RING(0);
+
+                                       ADVANCE_LP_RING();
+                               }
+
+                               sarea_priv->ctxOwner = DRM_KERNEL_CONTEXT;
+
+                               init_drawrect = 0;
+                       }
+
                        rect = drw->rects;
-                       pipe = swap_hit->pipe;
-                       top = upper[pipe];
-                       bottom = lower[pipe];
+                       top = upper[plane];
+                       bottom = lower[plane];
+
+                       front = (dev_priv->sarea_priv->pf_current_page >>
+                                (2 * plane)) & 0x3;
+                       back = (front + 1) % num_pages;
 
                        for (num_rects = drw->num_rects; num_rects--; rect++) {
                                int y1 = max(rect->y1, top);
@@ -207,17 +341,17 @@ static void i915_vblank_tasklet(struct drm_device *dev)
                                OUT_RING(ropcpp | dst_pitch);
                                OUT_RING((y1 << 16) | rect->x1);
                                OUT_RING((y2 << 16) | rect->x2);
-                               OUT_RING(sarea_priv->front_offset);
+                               OUT_RING(offsets[front]);
                                OUT_RING((y1 << 16) | rect->x1);
                                OUT_RING(src_pitch);
-                               OUT_RING(sarea_priv->back_offset);
+                               OUT_RING(offsets[back]);
 
                                ADVANCE_LP_RING();
                        }
                }
        }
 
-       spin_unlock_irqrestore(&dev->drw_lock, irqflags);
+       spin_unlock(&dev->drw_lock);
 
        list_for_each_safe(hit, tmp, &hits) {
                drm_i915_vbl_swap_t *swap_hit =
@@ -229,67 +363,112 @@ 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)) {
+           printk(KERN_ERR "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;
+
+       /* count may be reset by other driver(e.g. 2D driver),
+          we have no way to know if it is wrapped or resetted
+          when count is zero. do a rough guess.
+       */
+       if (count == 0 && dev->last_vblank[pipe] < dev->max_vblank_count/2)
+               dev->last_vblank[pipe] = 0;
+
+       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;
-       u16 temp;
+       u32 iir;
        u32 pipea_stats, pipeb_stats;
-
-       pipea_stats = I915_READ(I915REG_PIPEASTAT);
-       pipeb_stats = I915_READ(I915REG_PIPEBSTAT);
-
-       temp = I915_READ16(I915REG_INT_IDENTITY_R);
-
-       temp &= (USER_INT_FLAG | VSYNC_PIPEA_FLAG | VSYNC_PIPEB_FLAG);
-
-       DRM_DEBUG("%s flag=%08x\n", __FUNCTION__, temp);
-
-       if (temp == 0)
+       int vblank = 0;
+
+       iir = I915_READ(I915REG_INT_IDENTITY_R);
+       if (iir == 0) {
+               DRM_DEBUG ("iir 0x%08x im 0x%08x ie 0x%08x pipea 0x%08x pipeb 0x%08x\n",
+                          iir,
+                          I915_READ(I915REG_INT_MASK_R),
+                          I915_READ(I915REG_INT_ENABLE_R),
+                          I915_READ(I915REG_PIPEASTAT),
+                          I915_READ(I915REG_PIPEBSTAT));
                return IRQ_NONE;
+       }
 
-       I915_WRITE16(I915REG_INT_IDENTITY_R, temp);
-       (void) I915_READ16(I915REG_INT_IDENTITY_R);
-       DRM_READMEMORYBARRIER();
-
-       dev_priv->sarea_priv->last_dispatch = READ_BREADCRUMB(dev_priv);
+       /*
+        * 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(I915REG_PIPEASTAT);
+               if (pipea_stats & (I915_START_VBLANK_INTERRUPT_STATUS|
+                                  I915_VBLANK_INTERRUPT_STATUS))
+               {
+                       vblank++;
+                       drm_handle_vblank(dev, i915_get_plane(dev, 0));
+               }
+               I915_WRITE(I915REG_PIPEASTAT, pipea_stats);
+       }
+       if (iir & I915_DISPLAY_PIPE_B_EVENT_INTERRUPT) {
+               pipeb_stats = I915_READ(I915REG_PIPEBSTAT);
+               if (pipeb_stats & (I915_START_VBLANK_INTERRUPT_STATUS|
+                                  I915_VBLANK_INTERRUPT_STATUS))
+               {
+                       vblank++;
+                       drm_handle_vblank(dev, i915_get_plane(dev, 1));
+               }
+               I915_WRITE(I915REG_PIPEBSTAT, pipeb_stats);
+       }
 
-       if (temp & USER_INT_FLAG)
-               DRM_WAKEUP(&dev_priv->irq_queue);
+       if (dev_priv->sarea_priv)
+           dev_priv->sarea_priv->last_dispatch = READ_BREADCRUMB(dev_priv);
 
-       if (temp & (VSYNC_PIPEA_FLAG | VSYNC_PIPEB_FLAG)) {
-               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 (temp & VSYNC_PIPEA_FLAG)
-                               atomic_inc(&dev->vbl_received);
-                       if (temp & VSYNC_PIPEB_FLAG)
-                               atomic_inc(&dev->vbl_received2);
-               } else if (((temp & VSYNC_PIPEA_FLAG) &&
-                           (vblank_pipe & DRM_I915_VBLANK_PIPE_A)) ||
-                          ((temp & VSYNC_PIPEB_FLAG) &&
-                           (vblank_pipe & DRM_I915_VBLANK_PIPE_B)))
-                       atomic_inc(&dev->vbl_received);
-
-               DRM_WAKEUP(&dev->vbl_queue);
-               drm_vbl_send_signals(dev);
+       I915_WRITE(I915REG_INT_IDENTITY_R, iir);
+       (void) I915_READ(I915REG_INT_IDENTITY_R); /* Flush posted write */
 
+       if (iir & I915_USER_INTERRUPT) {
+               DRM_WAKEUP(&dev_priv->irq_queue);
+       }
+       if (vblank) {
                if (dev_priv->swaps_pending > 0)
                        drm_locked_tasklet(dev, i915_vblank_tasklet);
-               I915_WRITE(I915REG_PIPEASTAT,
-                       pipea_stats|I915_VBLANK_INTERRUPT_ENABLE|
-                       I915_VBLANK_CLEAR);
-               I915_WRITE(I915REG_PIPEBSTAT,
-                       pipeb_stats|I915_VBLANK_INTERRUPT_ENABLE|
-                       I915_VBLANK_CLEAR);
        }
 
        return IRQ_HANDLED;
 }
 
-static int i915_emit_irq(struct drm_device * dev)
+static int i915_emit_irq(struct drm_device *dev)
 {
        drm_i915_private_t *dev_priv = dev->dev_private;
        RING_LOCALS;
@@ -336,42 +515,12 @@ static int i915_wait_irq(struct drm_device * dev, int irq_nr)
                          READ_BREADCRUMB(dev_priv), (int)dev_priv->counter);
        }
 
-       dev_priv->sarea_priv->last_dispatch = READ_BREADCRUMB(dev_priv);
+       if (dev_priv->sarea_priv)
+               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,
@@ -414,18 +563,96 @@ 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 = I915REG_PIPEASTAT;
+               dev_priv->irq_enable_reg |= I915_DISPLAY_PIPE_A_EVENT_INTERRUPT;
+               break;
+       case 1:
+               pipestat_reg = I915REG_PIPEBSTAT;
+               dev_priv->irq_enable_reg |= 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);
+               /*
+                * Older chips didn't have the start vblank interrupt,
+                * but
+                */
+               if (IS_I965G (dev))
+                       pipestat |= I915_START_VBLANK_INTERRUPT_ENABLE;
+               else
+                       pipestat |= I915_VBLANK_INTERRUPT_ENABLE;
+               /*
+                * Clear any pending status
+                */
+               pipestat |= (I915_START_VBLANK_INTERRUPT_STATUS |
+                            I915_VBLANK_INTERRUPT_STATUS);
+               I915_WRITE(pipestat_reg, pipestat);
+       }
+       I915_WRITE(I915REG_INT_ENABLE_R, dev_priv->irq_enable_reg);
+
+       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 = I915REG_PIPEASTAT;
+               dev_priv->irq_enable_reg &= ~I915_DISPLAY_PIPE_A_EVENT_INTERRUPT;
+               break;
+       case 1:
+               pipestat_reg = I915REG_PIPEBSTAT;
+               dev_priv->irq_enable_reg &= ~I915_DISPLAY_PIPE_B_EVENT_INTERRUPT;
+               break;
+       default:
+               DRM_ERROR("tried to disable vblank on non-existent pipe %d\n",
+                         pipe);
+               break;
+       }
+
+       I915_WRITE(I915REG_INT_ENABLE_R, dev_priv->irq_enable_reg);
+       if (pipestat_reg)
+       {
+               pipestat = I915_READ (pipestat_reg);
+               pipestat &= ~(I915_START_VBLANK_INTERRUPT_ENABLE |
+                             I915_VBLANK_INTERRUPT_ENABLE);
+               /*
+                * Clear any pending status
+                */
+               pipestat |= (I915_START_VBLANK_INTERRUPT_STATUS |
+                            I915_VBLANK_INTERRUPT_STATUS);
+               I915_WRITE(pipestat_reg, pipestat);
+       }
+}
+
 static void i915_enable_interrupt (struct drm_device *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;
+       dev_priv->irq_enable_reg |= I915_USER_INTERRUPT;
 
-       I915_WRITE16(I915REG_INT_ENABLE_R, USER_INT_FLAG | flag);
+       I915_WRITE(I915REG_INT_ENABLE_R, dev_priv->irq_enable_reg);
+       dev_priv->irq_enabled = 1;
 }
 
 /* Set the vblank monitor pipe
@@ -448,8 +675,6 @@ int i915_vblank_pipe_set(struct drm_device *dev, void *data,
 
        dev_priv->vblank_pipe = pipe->pipe;
 
-       i915_enable_interrupt (dev);
-
        return 0;
 }
 
@@ -467,9 +692,9 @@ int i915_vblank_pipe_get(struct drm_device *dev, void *data,
 
        flag = I915_READ(I915REG_INT_ENABLE_R);
        pipe->pipe = 0;
-       if (flag & VSYNC_PIPEA_FLAG)
+       if (flag & I915_DISPLAY_PIPE_A_EVENT_INTERRUPT)
                pipe->pipe |= DRM_I915_VBLANK_PIPE_A;
-       if (flag & VSYNC_PIPEB_FLAG)
+       if (flag & I915_DISPLAY_PIPE_B_EVENT_INTERRUPT)
                pipe->pipe |= DRM_I915_VBLANK_PIPE_B;
 
        return 0;
@@ -484,27 +709,30 @@ 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__);
                return -EINVAL;
        }
 
-       if (dev_priv->sarea_priv->rotation) {
+       if (!dev_priv->sarea_priv || dev_priv->sarea_priv->rotation) {
                DRM_DEBUG("Rotation not supported\n");
                return -EINVAL;
        }
 
        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 -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);
 
@@ -515,6 +743,11 @@ int i915_vblank_swap(struct drm_device *dev, void *data,
 
        spin_lock_irqsave(&dev->drw_lock, irqflags);
 
+       /* It makes no sense to schedule a swap for a drawable that doesn't have
+        * valid information at this point. E.g. this could mean that the X
+        * server is too old to push drawable information to the DRM, in which
+        * case all such swaps would become ineffective.
+        */
        if (!drm_get_drawable_info(dev, swap->drawable)) {
                spin_unlock_irqrestore(&dev->drw_lock, irqflags);
                DRM_DEBUG("Invalid drawable ID %d\n", swap->drawable);
@@ -523,7 +756,8 @@ 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);
+       drm_update_vblank_count(dev, pipe);
+       curseq = drm_vblank_count(dev, pipe);
 
        if (seqtype == _DRM_VBLANK_RELATIVE)
                swap->sequence += curseq;
@@ -537,14 +771,43 @@ int i915_vblank_swap(struct drm_device *dev, void *data,
                }
        }
 
+       if (swap->seqtype & _DRM_VBLANK_FLIP) {
+               swap->sequence--;
+
+               if ((curseq - swap->sequence) <= (1<<23)) {
+                       struct drm_drawable_info *drw;
+
+                       LOCK_TEST_WITH_RETURN(dev, file_priv);
+
+                       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 -EINVAL;
+                       }
+
+                       i915_dispatch_vsync_flip(dev, drw, plane);
+
+                       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) {
                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) {
+                       vbl_swap->flip = (swap->seqtype & _DRM_VBLANK_FLIP);
                        spin_unlock_irqrestore(&dev_priv->swaps_lock, irqflags);
                        DRM_DEBUG("Already scheduled\n");
                        return 0;
@@ -567,9 +830,19 @@ int i915_vblank_swap(struct drm_device *dev, void *data,
 
        DRM_DEBUG("\n");
 
+       ret = drm_vblank_get(dev, pipe);
+       if (ret) {
+               drm_free(vbl_swap, sizeof(*vbl_swap), DRM_MEM_DRIVER);
+               return ret;
+       }
+
        vbl_swap->drw_id = swap->drawable;
-       vbl_swap->pipe = pipe;
+       vbl_swap->plane = plane;
        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);
 
@@ -587,37 +860,57 @@ void i915_driver_irq_preinstall(struct drm_device * dev)
 {
        drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
 
-       I915_WRITE16(I915REG_HWSTAM, 0xfffe);
+       I915_WRITE16(I915REG_HWSTAM, 0xeffe);
        I915_WRITE16(I915REG_INT_MASK_R, 0x0);
        I915_WRITE16(I915REG_INT_ENABLE_R, 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;
+       dev_priv->user_irq_refcount = 0;
+       dev_priv->irq_enable_reg = 0;
+
+       ret = drm_vblank_init(dev, num_pipes);
+       if (ret)
+               return ret;
+
+       dev->max_vblank_count = 0xffffff; /* only 24 bits of frame count */
+
        i915_enable_interrupt(dev);
        DRM_INIT_WAITQUEUE(&dev_priv->irq_queue);
+
+       /*
+        * Initialize the hardware status page IRQ location.
+        */
+
+       I915_WRITE(I915REG_INSTPM, (1 << 5) | (1 << 21));
+       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_WRITE16(I915REG_HWSTAM, 0xffff);
-       I915_WRITE16(I915REG_INT_MASK_R, 0xffff);
-       I915_WRITE16(I915REG_INT_ENABLE_R, 0x0);
-
-       temp = I915_READ16(I915REG_INT_IDENTITY_R);
-       I915_WRITE16(I915REG_INT_IDENTITY_R, temp);
+       dev_priv->irq_enabled = 0;
+       I915_WRITE(I915REG_HWSTAM, 0xffffffff);
+       I915_WRITE(I915REG_INT_MASK_R, 0xffffffff);
+       I915_WRITE(I915REG_INT_ENABLE_R, 0x0);
+
+       temp = I915_READ(I915REG_PIPEASTAT);
+       I915_WRITE(I915REG_PIPEASTAT, temp);
+       temp = I915_READ(I915REG_PIPEBSTAT);
+       I915_WRITE(I915REG_PIPEBSTAT, temp);
+       temp = I915_READ(I915REG_INT_IDENTITY_R);
+       I915_WRITE(I915REG_INT_IDENTITY_R, temp);
 }
index 5572939fc7d19cb1f986833a22ce79a21ad1da0a..6b3790939e762d91e7ed4c7a8bcb4d6ac6eb7f9e 100644 (file)
@@ -45,15 +45,16 @@ static struct pci_device_id pciidlist[] = {
 static struct drm_driver driver = {
        .driver_features =
            DRIVER_USE_AGP | DRIVER_USE_MTRR | DRIVER_PCI_DMA |
-           DRIVER_HAVE_DMA | DRIVER_HAVE_IRQ | DRIVER_IRQ_SHARED |
-           DRIVER_IRQ_VBL,
+           DRIVER_HAVE_DMA | DRIVER_HAVE_IRQ | DRIVER_IRQ_SHARED,
        .dev_priv_size = sizeof(drm_mga_buf_priv_t),
        .load = mga_driver_load,
        .unload = mga_driver_unload,
        .lastclose = mga_driver_lastclose,
        .dma_quiescent = mga_driver_dma_quiescent,
        .device_is_agp = mga_driver_device_is_agp,
-       .vblank_wait = mga_driver_vblank_wait,
+       .get_vblank_counter = mga_get_vblank_counter,
+       .enable_vblank = mga_enable_vblank,
+       .disable_vblank = mga_disable_vblank,
        .irq_preinstall = mga_driver_irq_preinstall,
        .irq_postinstall = mga_driver_irq_postinstall,
        .irq_uninstall = mga_driver_irq_uninstall,
index f6ebd24bd5871ebad2204c64195e3e3e31219401..8f7291f36363590c3431570d78c364b81cb4bbea 100644 (file)
@@ -120,6 +120,7 @@ typedef struct drm_mga_private {
        u32 clear_cmd;
        u32 maccess;
 
+       atomic_t vbl_received;          /**< Number of vblanks received. */
        wait_queue_head_t fence_queue;
        atomic_t last_fence_retired;
        u32 next_fence_to_post;
@@ -181,11 +182,14 @@ extern int mga_warp_install_microcode(drm_mga_private_t * dev_priv);
 extern int mga_warp_init(drm_mga_private_t * dev_priv);
 
                                /* mga_irq.c */
+extern int mga_enable_vblank(struct drm_device *dev, int crtc);
+extern void mga_disable_vblank(struct drm_device *dev, int crtc);
+extern u32 mga_get_vblank_counter(struct drm_device *dev, int crtc);
 extern int mga_driver_fence_wait(struct drm_device * dev, unsigned int *sequence);
 extern int mga_driver_vblank_wait(struct drm_device * dev, unsigned int *sequence);
 extern irqreturn_t mga_driver_irq_handler(DRM_IRQ_ARGS);
 extern void mga_driver_irq_preinstall(struct drm_device * dev);
-extern void mga_driver_irq_postinstall(struct drm_device * dev);
+extern int mga_driver_irq_postinstall(struct drm_device * dev);
 extern void mga_driver_irq_uninstall(struct drm_device * dev);
 extern long mga_compat_ioctl(struct file *filp, unsigned int cmd,
                             unsigned long arg);
index 9302cb8f0f83bef1e99be219a9b977884d3f6d28..06852fb4b278fd0c5b0f3d84ff0d915858135913 100644 (file)
 #include "mga_drm.h"
 #include "mga_drv.h"
 
+u32 mga_get_vblank_counter(struct drm_device *dev, int crtc)
+{
+       const drm_mga_private_t *const dev_priv =
+               (drm_mga_private_t *) dev->dev_private;
+
+       if (crtc != 0) {
+               return 0;
+       }
+
+
+       return atomic_read(&dev_priv->vbl_received);
+}
+
+
 irqreturn_t mga_driver_irq_handler(DRM_IRQ_ARGS)
 {
        struct drm_device *dev = (struct drm_device *) arg;
@@ -47,9 +61,8 @@ irqreturn_t mga_driver_irq_handler(DRM_IRQ_ARGS)
        /* VBLANK interrupt */
        if (status & MGA_VLINEPEN) {
                MGA_WRITE(MGA_ICLEAR, MGA_VLINEICLR);
-               atomic_inc(&dev->vbl_received);
-               DRM_WAKEUP(&dev->vbl_queue);
-               drm_vbl_send_signals(dev);
+               atomic_inc(&dev_priv->vbl_received);
+               drm_handle_vblank(dev, 0);
                handled = 1;
        }
 
@@ -78,22 +91,34 @@ irqreturn_t mga_driver_irq_handler(DRM_IRQ_ARGS)
        return IRQ_NONE;
 }
 
-int mga_driver_vblank_wait(struct drm_device * dev, unsigned int *sequence)
+int mga_enable_vblank(struct drm_device *dev, int crtc)
 {
-       unsigned int cur_vblank;
-       int ret = 0;
+       drm_mga_private_t *dev_priv = (drm_mga_private_t *) dev->dev_private;
 
-       /* Assume that the user has missed the current sequence number
-        * by about a day rather than she wants to wait for years
-        * using vertical blanks...
-        */
-       DRM_WAIT_ON(ret, dev->vbl_queue, 3 * DRM_HZ,
-                   (((cur_vblank = atomic_read(&dev->vbl_received))
-                     - *sequence) <= (1 << 23)));
+       if (crtc != 0) {
+               DRM_ERROR("tried to enable vblank on non-existent crtc %d\n",
+                         crtc);
+               return 0;
+       }
 
-       *sequence = cur_vblank;
+       MGA_WRITE(MGA_IEN, MGA_VLINEIEN | MGA_SOFTRAPEN);
+       return 0;
+}
 
-       return ret;
+
+void mga_disable_vblank(struct drm_device *dev, int crtc)
+{
+       if (crtc != 0) {
+               DRM_ERROR("tried to disable vblank on non-existent crtc %d\n",
+                         crtc);
+       }
+
+       /* Do *NOT* disable the vertical refresh interrupt.  MGA doesn't have
+        * a nice hardware counter that tracks the number of refreshes when
+        * the interrupt is disabled, and the kernel doesn't know the refresh
+        * rate to calculate an estimate.
+        */
+       /* MGA_WRITE(MGA_IEN, MGA_VLINEIEN | MGA_SOFTRAPEN); */
 }
 
 int mga_driver_fence_wait(struct drm_device * dev, unsigned int *sequence)
@@ -125,14 +150,22 @@ void mga_driver_irq_preinstall(struct drm_device * dev)
        MGA_WRITE(MGA_ICLEAR, ~0);
 }
 
-void mga_driver_irq_postinstall(struct drm_device * dev)
+int mga_driver_irq_postinstall(struct drm_device * dev)
 {
        drm_mga_private_t *dev_priv = (drm_mga_private_t *) dev->dev_private;
+       int ret;
+
+       ret = drm_vblank_init(dev, 1);
+       if (ret)
+               return ret;
 
        DRM_INIT_WAITQUEUE(&dev_priv->fence_queue);
 
-       /* Turn on vertical blank interrupt and soft trap interrupt. */
-       MGA_WRITE(MGA_IEN, MGA_VLINEIEN | MGA_SOFTRAPEN);
+       /* Turn on soft trap interrupt.  Vertical blank interrupts are enabled
+        * in mga_enable_vblank.
+        */
+       MGA_WRITE(MGA_IEN, MGA_SOFTRAPEN);
+       return 0;
 }
 
 void mga_driver_irq_uninstall(struct drm_device * dev)
index 6108e7587e12470daba9576ef185458f5fc545c1..2888aa01ebc78779c28a63a63a1382108d33e8e4 100644 (file)
@@ -43,12 +43,13 @@ static struct pci_device_id pciidlist[] = {
 static struct drm_driver driver = {
        .driver_features =
            DRIVER_USE_AGP | DRIVER_USE_MTRR | DRIVER_PCI_DMA | DRIVER_SG |
-           DRIVER_HAVE_DMA | DRIVER_HAVE_IRQ | DRIVER_IRQ_SHARED |
-           DRIVER_IRQ_VBL,
+           DRIVER_HAVE_DMA | DRIVER_HAVE_IRQ | DRIVER_IRQ_SHARED,
        .dev_priv_size = sizeof(drm_r128_buf_priv_t),
        .preclose = r128_driver_preclose,
        .lastclose = r128_driver_lastclose,
-       .vblank_wait = r128_driver_vblank_wait,
+       .get_vblank_counter = r128_get_vblank_counter,
+       .enable_vblank = r128_enable_vblank,
+       .disable_vblank = r128_disable_vblank,
        .irq_preinstall = r128_driver_irq_preinstall,
        .irq_postinstall = r128_driver_irq_postinstall,
        .irq_uninstall = r128_driver_irq_uninstall,
index 011105e51ac660117c88a1e1a3d9db860702b325..80af9e09e75da49610fb54b1539022df20d789bd 100644 (file)
@@ -97,6 +97,8 @@ typedef struct drm_r128_private {
        u32 crtc_offset;
        u32 crtc_offset_cntl;
 
+       atomic_t vbl_received;
+
        u32 color_fmt;
        unsigned int front_offset;
        unsigned int front_pitch;
@@ -149,11 +151,12 @@ extern int r128_wait_ring(drm_r128_private_t * dev_priv, int n);
 extern int r128_do_cce_idle(drm_r128_private_t * dev_priv);
 extern int r128_do_cleanup_cce(struct drm_device * dev);
 
-extern int r128_driver_vblank_wait(struct drm_device * dev, unsigned int *sequence);
-
+extern int r128_enable_vblank(struct drm_device *dev, int crtc);
+extern void r128_disable_vblank(struct drm_device *dev, int crtc);
+extern u32 r128_get_vblank_counter(struct drm_device *dev, int crtc);
 extern irqreturn_t r128_driver_irq_handler(DRM_IRQ_ARGS);
 extern void r128_driver_irq_preinstall(struct drm_device * dev);
-extern void r128_driver_irq_postinstall(struct drm_device * dev);
+extern int r128_driver_irq_postinstall(struct drm_device * dev);
 extern void r128_driver_irq_uninstall(struct drm_device * dev);
 extern void r128_driver_lastclose(struct drm_device * dev);
 extern void r128_driver_preclose(struct drm_device * dev,
index c76fdca7662d78bcdb0cb83684ce2c9005a471c2..5b95bd898f95c54128d6238e34a68b7a7b713865 100644 (file)
 #include "r128_drm.h"
 #include "r128_drv.h"
 
+u32 r128_get_vblank_counter(struct drm_device *dev, int crtc)
+{
+       const drm_r128_private_t *dev_priv = dev->dev_private;
+
+       if (crtc != 0)
+               return 0;
+
+       return atomic_read(&dev_priv->vbl_received);
+}
+
 irqreturn_t r128_driver_irq_handler(DRM_IRQ_ARGS)
 {
        struct drm_device *dev = (struct drm_device *) arg;
@@ -46,30 +56,38 @@ irqreturn_t r128_driver_irq_handler(DRM_IRQ_ARGS)
        /* VBLANK interrupt */
        if (status & R128_CRTC_VBLANK_INT) {
                R128_WRITE(R128_GEN_INT_STATUS, R128_CRTC_VBLANK_INT_AK);
-               atomic_inc(&dev->vbl_received);
-               DRM_WAKEUP(&dev->vbl_queue);
-               drm_vbl_send_signals(dev);
+               atomic_inc(&dev_priv->vbl_received);
+               drm_handle_vblank(dev, 0);
                return IRQ_HANDLED;
        }
        return IRQ_NONE;
 }
 
-int r128_driver_vblank_wait(struct drm_device * dev, unsigned int *sequence)
+int r128_enable_vblank(struct drm_device *dev, int crtc)
 {
-       unsigned int cur_vblank;
-       int ret = 0;
+       drm_r128_private_t *dev_priv = dev->dev_private;
 
-       /* Assume that the user has missed the current sequence number
-        * by about a day rather than she wants to wait for years
-        * using vertical blanks...
-        */
-       DRM_WAIT_ON(ret, dev->vbl_queue, 3 * DRM_HZ,
-                   (((cur_vblank = atomic_read(&dev->vbl_received))
-                     - *sequence) <= (1 << 23)));
+       if (crtc != 0) {
+               DRM_ERROR("%s:  bad crtc %d\n", __FUNCTION__, crtc);
+               return -EINVAL;
+       }
 
-       *sequence = cur_vblank;
+       R128_WRITE(R128_GEN_INT_CNTL, R128_CRTC_VBLANK_INT_EN);
+       return 0;
+}
+
+void r128_disable_vblank(struct drm_device *dev, int crtc)
+{
+       if (crtc != 0)
+               DRM_ERROR("%s:  bad crtc %d\n", __FUNCTION__, crtc);
 
-       return ret;
+       /*
+        * FIXME: implement proper interrupt disable by using the vblank
+        * counter register (if available)
+        *
+        * R128_WRITE(R128_GEN_INT_CNTL,
+        *            R128_READ(R128_GEN_INT_CNTL) & ~R128_CRTC_VBLANK_INT_EN);
+        */
 }
 
 void r128_driver_irq_preinstall(struct drm_device * dev)
@@ -82,12 +100,9 @@ void r128_driver_irq_preinstall(struct drm_device * dev)
        R128_WRITE(R128_GEN_INT_STATUS, R128_CRTC_VBLANK_INT_AK);
 }
 
-void r128_driver_irq_postinstall(struct drm_device * dev)
+int r128_driver_irq_postinstall(struct drm_device * dev)
 {
-       drm_r128_private_t *dev_priv = (drm_r128_private_t *) dev->dev_private;
-
-       /* Turn on VBL interrupt */
-       R128_WRITE(R128_GEN_INT_CNTL, R128_CRTC_VBLANK_INT_EN);
+       return drm_vblank_init(dev, 1);
 }
 
 void r128_driver_irq_uninstall(struct drm_device * dev)
index 349ac3d3b8480c13d91c850f917819774f92edbf..a2610319624d3509fe98d9e1c93215357b586160 100644 (file)
@@ -59,8 +59,7 @@ static struct pci_device_id pciidlist[] = {
 static struct drm_driver driver = {
        .driver_features =
            DRIVER_USE_AGP | DRIVER_USE_MTRR | DRIVER_PCI_DMA | DRIVER_SG |
-           DRIVER_HAVE_IRQ | DRIVER_HAVE_DMA | DRIVER_IRQ_SHARED |
-           DRIVER_IRQ_VBL | DRIVER_IRQ_VBL2,
+           DRIVER_HAVE_IRQ | DRIVER_HAVE_DMA | DRIVER_IRQ_SHARED,
        .dev_priv_size = sizeof(drm_radeon_buf_priv_t),
        .load = radeon_driver_load,
        .firstopen = radeon_driver_firstopen,
@@ -69,8 +68,9 @@ static struct drm_driver driver = {
        .postclose = radeon_driver_postclose,
        .lastclose = radeon_driver_lastclose,
        .unload = radeon_driver_unload,
-       .vblank_wait = radeon_driver_vblank_wait,
-       .vblank_wait2 = radeon_driver_vblank_wait2,
+       .get_vblank_counter = radeon_get_vblank_counter,
+       .enable_vblank = radeon_enable_vblank,
+       .disable_vblank = radeon_disable_vblank,
        .dri_library_name = dri_library_name,
        .irq_preinstall = radeon_driver_irq_preinstall,
        .irq_postinstall = radeon_driver_irq_postinstall,
index 173ae620223a052238a0a212711e25290bff4f0c..b791420bd3d98f4886a1d95e3ebf863282133f13 100644 (file)
@@ -304,6 +304,9 @@ typedef struct drm_radeon_private {
 
        u32 scratch_ages[5];
 
+       unsigned int crtc_last_cnt;
+       unsigned int crtc2_last_cnt;
+
        /* starting from here on, data is preserved accross an open */
        uint32_t flags;         /* see radeon_chip_flags */
        unsigned long fb_aper_offset;
@@ -374,13 +377,13 @@ extern int radeon_irq_emit(struct drm_device *dev, void *data, struct drm_file *
 extern int radeon_irq_wait(struct drm_device *dev, void *data, struct drm_file *file_priv);
 
 extern void radeon_do_release(struct drm_device * dev);
-extern int radeon_driver_vblank_wait(struct drm_device * dev,
-                                    unsigned int *sequence);
-extern int radeon_driver_vblank_wait2(struct drm_device * dev,
-                                     unsigned int *sequence);
+extern u32 radeon_get_vblank_counter(struct drm_device *dev, int crtc);
+extern int radeon_enable_vblank(struct drm_device *dev, int crtc);
+extern void radeon_disable_vblank(struct drm_device *dev, int crtc);
+extern void radeon_do_release(struct drm_device * dev);
 extern irqreturn_t radeon_driver_irq_handler(DRM_IRQ_ARGS);
 extern void radeon_driver_irq_preinstall(struct drm_device * dev);
-extern void radeon_driver_irq_postinstall(struct drm_device * dev);
+extern int radeon_driver_irq_postinstall(struct drm_device * dev);
 extern void radeon_driver_irq_uninstall(struct drm_device * dev);
 extern int radeon_vblank_crtc_get(struct drm_device *dev);
 extern int radeon_vblank_crtc_set(struct drm_device *dev, int64_t value);
@@ -558,6 +561,12 @@ extern int r300_do_cp_cmdbuf(struct drm_device * dev,
                                ? DRM_READ32( dev_priv->ring_rptr, RADEON_SCRATCHOFF(x) ) \
                                : RADEON_READ( RADEON_SCRATCH_REG0 + 4*(x) ) )
 
+#define RADEON_CRTC_CRNT_FRAME 0x0214
+#define RADEON_CRTC2_CRNT_FRAME 0x0314
+
+#define RADEON_CRTC_STATUS             0x005c
+#define RADEON_CRTC2_STATUS            0x03fc
+
 #define RADEON_GEN_INT_CNTL            0x0040
 #      define RADEON_CRTC_VBLANK_MASK          (1 << 0)
 #      define RADEON_CRTC2_VBLANK_MASK         (1 << 9)
index 009af3814b6f12ec4dcf34aff23cffff79e0d5db..507d6b747a13139cf3aeeb8e0a4f8339a950eaab 100644 (file)
 #include "radeon_drm.h"
 #include "radeon_drv.h"
 
-static __inline__ u32 radeon_acknowledge_irqs(drm_radeon_private_t * dev_priv,
-                                             u32 mask)
+static void radeon_irq_set_state(struct drm_device *dev, u32 mask, int state)
 {
-       u32 irqs = RADEON_READ(RADEON_GEN_INT_STATUS) & mask;
+       drm_radeon_private_t *dev_priv = dev->dev_private;
+
+       if (state)
+               dev_priv->irq_enable_reg |= mask;
+       else
+               dev_priv->irq_enable_reg &= ~mask;
+
+       RADEON_WRITE(RADEON_GEN_INT_CNTL, dev_priv->irq_enable_reg);
+}
+
+int radeon_enable_vblank(struct drm_device *dev, int crtc)
+{
+       switch (crtc) {
+       case 0:
+               radeon_irq_set_state(dev, RADEON_CRTC_VBLANK_MASK, 1);
+               break;
+       case 1:
+               radeon_irq_set_state(dev, RADEON_CRTC2_VBLANK_MASK, 1);
+               break;
+       default:
+               DRM_ERROR("tried to enable vblank on non-existent crtc %d\n",
+                         crtc);
+               return EINVAL;
+       }
+
+       return 0;
+}
+
+void radeon_disable_vblank(struct drm_device *dev, int crtc)
+{
+       switch (crtc) {
+       case 0:
+               radeon_irq_set_state(dev, RADEON_CRTC_VBLANK_MASK, 0);
+               break;
+       case 1:
+               radeon_irq_set_state(dev, RADEON_CRTC2_VBLANK_MASK, 0);
+               break;
+       default:
+               DRM_ERROR("tried to enable vblank on non-existent crtc %d\n",
+                         crtc);
+               break;
+       }
+}
+
+static __inline__ u32 radeon_acknowledge_irqs(drm_radeon_private_t * dev_priv)
+{
+       u32 irqs = RADEON_READ(RADEON_GEN_INT_STATUS) &
+               (RADEON_SW_INT_TEST | RADEON_CRTC_VBLANK_STAT |
+                RADEON_CRTC2_VBLANK_STAT);
+
        if (irqs)
                RADEON_WRITE(RADEON_GEN_INT_STATUS, irqs);
+
        return irqs;
 }
 
@@ -72,39 +121,21 @@ irqreturn_t radeon_driver_irq_handler(DRM_IRQ_ARGS)
        /* Only consider the bits we're interested in - others could be used
         * outside the DRM
         */
-       stat = radeon_acknowledge_irqs(dev_priv, (RADEON_SW_INT_TEST_ACK |
-                                                 RADEON_CRTC_VBLANK_STAT |
-                                                 RADEON_CRTC2_VBLANK_STAT));
+       stat = radeon_acknowledge_irqs(dev_priv);
        if (!stat)
                return IRQ_NONE;
 
        stat &= dev_priv->irq_enable_reg;
 
        /* SW interrupt */
-       if (stat & RADEON_SW_INT_TEST) {
+       if (stat & RADEON_SW_INT_TEST)
                DRM_WAKEUP(&dev_priv->swi_queue);
-       }
 
        /* VBLANK interrupt */
-       if (stat & (RADEON_CRTC_VBLANK_STAT|RADEON_CRTC2_VBLANK_STAT)) {
-               int vblank_crtc = dev_priv->vblank_crtc;
-
-               if ((vblank_crtc &
-                    (DRM_RADEON_VBLANK_CRTC1 | DRM_RADEON_VBLANK_CRTC2)) ==
-                   (DRM_RADEON_VBLANK_CRTC1 | DRM_RADEON_VBLANK_CRTC2)) {
-                       if (stat & RADEON_CRTC_VBLANK_STAT)
-                               atomic_inc(&dev->vbl_received);
-                       if (stat & RADEON_CRTC2_VBLANK_STAT)
-                               atomic_inc(&dev->vbl_received2);
-               } else if (((stat & RADEON_CRTC_VBLANK_STAT) &&
-                          (vblank_crtc & DRM_RADEON_VBLANK_CRTC1)) ||
-                          ((stat & RADEON_CRTC2_VBLANK_STAT) &&
-                           (vblank_crtc & DRM_RADEON_VBLANK_CRTC2)))
-                       atomic_inc(&dev->vbl_received);
-
-               DRM_WAKEUP(&dev->vbl_queue);
-               drm_vbl_send_signals(dev);
-       }
+       if (stat & RADEON_CRTC_VBLANK_STAT)
+               drm_handle_vblank(dev, 0);
+       if (stat & RADEON_CRTC2_VBLANK_STAT)
+               drm_handle_vblank(dev, 1);
 
        return IRQ_HANDLED;
 }
@@ -144,54 +175,27 @@ static int radeon_wait_irq(struct drm_device * dev, int swi_nr)
        return ret;
 }
 
-static int radeon_driver_vblank_do_wait(struct drm_device * dev,
-                                       unsigned int *sequence, int crtc)
+u32 radeon_get_vblank_counter(struct drm_device *dev, int crtc)
 {
-       drm_radeon_private_t *dev_priv =
-           (drm_radeon_private_t *) dev->dev_private;
-       unsigned int cur_vblank;
-       int ret = 0;
-       int ack = 0;
-       atomic_t *counter;
+       drm_radeon_private_t *dev_priv = dev->dev_private;
+       u32 crtc_cnt_reg, crtc_status_reg;
+
        if (!dev_priv) {
                DRM_ERROR("called with no initialization\n");
                return -EINVAL;
        }
 
-       if (crtc == DRM_RADEON_VBLANK_CRTC1) {
-               counter = &dev->vbl_received;
-               ack |= RADEON_CRTC_VBLANK_STAT;
-       } else if (crtc == DRM_RADEON_VBLANK_CRTC2) {
-               counter = &dev->vbl_received2;
-               ack |= RADEON_CRTC2_VBLANK_STAT;
-       } else
+       if (crtc == 0) {
+               crtc_cnt_reg = RADEON_CRTC_CRNT_FRAME;
+               crtc_status_reg = RADEON_CRTC_STATUS;
+       } else if (crtc == 1) {
+               crtc_cnt_reg = RADEON_CRTC2_CRNT_FRAME;
+               crtc_status_reg = RADEON_CRTC2_STATUS;
+       } else {
                return -EINVAL;
+       }
 
-       radeon_acknowledge_irqs(dev_priv, ack);
-
-       dev_priv->stats.boxes |= RADEON_BOX_WAIT_IDLE;
-
-       /* Assume that the user has missed the current sequence number
-        * by about a day rather than she wants to wait for years
-        * using vertical blanks...
-        */
-       DRM_WAIT_ON(ret, dev->vbl_queue, 3 * DRM_HZ,
-                   (((cur_vblank = atomic_read(counter))
-                     - *sequence) <= (1 << 23)));
-
-       *sequence = cur_vblank;
-
-       return ret;
-}
-
-int radeon_driver_vblank_wait(struct drm_device *dev, unsigned int *sequence)
-{
-       return radeon_driver_vblank_do_wait(dev, sequence, DRM_RADEON_VBLANK_CRTC1);
-}
-
-int radeon_driver_vblank_wait2(struct drm_device *dev, unsigned int *sequence)
-{
-       return radeon_driver_vblank_do_wait(dev, sequence, DRM_RADEON_VBLANK_CRTC2);
+       return RADEON_READ(crtc_cnt_reg) + (RADEON_READ(crtc_status_reg) & 1);
 }
 
 /* Needs the lock as it touches the ring.
@@ -234,21 +238,6 @@ int radeon_irq_wait(struct drm_device *dev, void *data, struct drm_file *file_pr
        return radeon_wait_irq(dev, irqwait->irq_seq);
 }
 
-static void radeon_enable_interrupt(struct drm_device *dev)
-{
-       drm_radeon_private_t *dev_priv = (drm_radeon_private_t *) dev->dev_private;
-
-       dev_priv->irq_enable_reg = RADEON_SW_INT_ENABLE;
-       if (dev_priv->vblank_crtc & DRM_RADEON_VBLANK_CRTC1)
-               dev_priv->irq_enable_reg |= RADEON_CRTC_VBLANK_MASK;
-
-       if (dev_priv->vblank_crtc & DRM_RADEON_VBLANK_CRTC2)
-               dev_priv->irq_enable_reg |= RADEON_CRTC2_VBLANK_MASK;
-
-       RADEON_WRITE(RADEON_GEN_INT_CNTL, dev_priv->irq_enable_reg);
-       dev_priv->irq_enabled = 1;
-}
-
 /* drm_dma.h hooks
 */
 void radeon_driver_irq_preinstall(struct drm_device * dev)
@@ -260,20 +249,27 @@ void radeon_driver_irq_preinstall(struct drm_device * dev)
        RADEON_WRITE(RADEON_GEN_INT_CNTL, 0);
 
        /* Clear bits if they're already high */
-       radeon_acknowledge_irqs(dev_priv, (RADEON_SW_INT_TEST_ACK |
-                                          RADEON_CRTC_VBLANK_STAT |
-                                          RADEON_CRTC2_VBLANK_STAT));
+       radeon_acknowledge_irqs(dev_priv);
 }
 
-void radeon_driver_irq_postinstall(struct drm_device * dev)
+int radeon_driver_irq_postinstall(struct drm_device * dev)
 {
        drm_radeon_private_t *dev_priv =
            (drm_radeon_private_t *) dev->dev_private;
+       int ret;
 
        atomic_set(&dev_priv->swi_emitted, 0);
        DRM_INIT_WAITQUEUE(&dev_priv->swi_queue);
 
-       radeon_enable_interrupt(dev);
+       ret = drm_vblank_init(dev, 2);
+       if (ret)
+               return ret;
+
+       dev->max_vblank_count = 0x001fffff;
+
+       radeon_irq_set_state(dev, RADEON_SW_INT_ENABLE, 1);
+
+       return 0;
 }
 
 void radeon_driver_irq_uninstall(struct drm_device * dev)
@@ -315,6 +311,5 @@ int radeon_vblank_crtc_set(struct drm_device *dev, int64_t value)
                return -EINVAL;
        }
        dev_priv->vblank_crtc = (unsigned int)value;
-       radeon_enable_interrupt(dev);
        return 0;
 }
index 80c01cdfa37d54db94ec5a382f83ec4e6b026432..37870a4a3dc729877a4d659018260778783982ce 100644 (file)
@@ -40,11 +40,13 @@ static struct pci_device_id pciidlist[] = {
 static struct drm_driver driver = {
        .driver_features =
            DRIVER_USE_AGP | DRIVER_USE_MTRR | DRIVER_HAVE_IRQ |
-           DRIVER_IRQ_SHARED | DRIVER_IRQ_VBL,
+           DRIVER_IRQ_SHARED,
        .load = via_driver_load,
        .unload = via_driver_unload,
        .context_dtor = via_final_context,
-       .vblank_wait = via_driver_vblank_wait,
+       .get_vblank_counter = via_get_vblank_counter,
+       .enable_vblank = via_enable_vblank,
+       .disable_vblank = via_disable_vblank,
        .irq_preinstall = via_driver_irq_preinstall,
        .irq_postinstall = via_driver_irq_postinstall,
        .irq_uninstall = via_driver_irq_uninstall,
index 2daae81874cdd049fdcd2ff9f0783eb15994fb53..fe67030e39ac6b8f3dadb3624fe23a6bed7a958a 100644 (file)
@@ -75,6 +75,7 @@ typedef struct drm_via_private {
        struct timeval last_vblank;
        int last_vblank_valid;
        unsigned usec_per_vblank;
+       atomic_t vbl_received;
        drm_via_state_t hc_state;
        char pci_buf[VIA_PCI_BUF_SIZE];
        const uint32_t *fire_offsets[VIA_FIRE_BUF_SIZE];
@@ -130,11 +131,13 @@ extern int via_init_context(struct drm_device * dev, int context);
 extern int via_final_context(struct drm_device * dev, int context);
 
 extern int via_do_cleanup_map(struct drm_device * dev);
-extern int via_driver_vblank_wait(struct drm_device * dev, unsigned int *sequence);
+extern u32 via_get_vblank_counter(struct drm_device *dev, int crtc);
+extern int via_enable_vblank(struct drm_device *dev, int crtc);
+extern void via_disable_vblank(struct drm_device *dev, int crtc);
 
 extern irqreturn_t via_driver_irq_handler(DRM_IRQ_ARGS);
 extern void via_driver_irq_preinstall(struct drm_device * dev);
-extern void via_driver_irq_postinstall(struct drm_device * dev);
+extern int via_driver_irq_postinstall(struct drm_device * dev);
 extern void via_driver_irq_uninstall(struct drm_device * dev);
 
 extern int via_dma_cleanup(struct drm_device * dev);
index c6bb978a110673821a3aa6db6e3cefd32bcc20ce..f1ab6fc7c07e8a32227ae7516bc65add4c61d030 100644 (file)
@@ -92,8 +92,17 @@ static int via_irqmap_unichrome[] = {-1, -1, -1, 0, -1, 1};
 static unsigned time_diff(struct timeval *now, struct timeval *then)
 {
        return (now->tv_usec >= then->tv_usec) ?
-           now->tv_usec - then->tv_usec :
-           1000000 - (then->tv_usec - now->tv_usec);
+               now->tv_usec - then->tv_usec :
+               1000000 - (then->tv_usec - now->tv_usec);
+}
+
+u32 via_get_vblank_counter(struct drm_device *dev, int crtc)
+{
+       drm_via_private_t *dev_priv = dev->dev_private;
+       if (crtc != 0)
+               return 0;
+
+       return atomic_read(&dev_priv->vbl_received);
 }
 
 irqreturn_t via_driver_irq_handler(DRM_IRQ_ARGS)
@@ -108,8 +117,8 @@ irqreturn_t via_driver_irq_handler(DRM_IRQ_ARGS)
 
        status = VIA_READ(VIA_REG_INTERRUPT);
        if (status & VIA_IRQ_VBLANK_PENDING) {
-               atomic_inc(&dev->vbl_received);
-               if (!(atomic_read(&dev->vbl_received) & 0x0F)) {
+               atomic_inc(&dev_priv->vbl_received);
+               if (!(atomic_read(&dev_priv->vbl_received) & 0x0F)) {
                        do_gettimeofday(&cur_vblank);
                        if (dev_priv->last_vblank_valid) {
                                dev_priv->usec_per_vblank =
@@ -119,12 +128,11 @@ irqreturn_t via_driver_irq_handler(DRM_IRQ_ARGS)
                        dev_priv->last_vblank = cur_vblank;
                        dev_priv->last_vblank_valid = 1;
                }
-               if (!(atomic_read(&dev->vbl_received) & 0xFF)) {
+               if (!(atomic_read(&dev_priv->vbl_received) & 0xFF)) {
                        DRM_DEBUG("US per vblank is: %u\n",
                                  dev_priv->usec_per_vblank);
                }
-               DRM_WAKEUP(&dev->vbl_queue);
-               drm_vbl_send_signals(dev);
+               drm_handle_vblank(dev, 0);
                handled = 1;
        }
 
@@ -163,31 +171,34 @@ static __inline__ void viadrv_acknowledge_irqs(drm_via_private_t * dev_priv)
        }
 }
 
-int via_driver_vblank_wait(struct drm_device * dev, unsigned int *sequence)
+int via_enable_vblank(struct drm_device *dev, int crtc)
 {
-       drm_via_private_t *dev_priv = (drm_via_private_t *) dev->dev_private;
-       unsigned int cur_vblank;
-       int ret = 0;
+       drm_via_private_t *dev_priv = dev->dev_private;
+       u32 status;
 
-       DRM_DEBUG("\n");
-       if (!dev_priv) {
-               DRM_ERROR("called with no initialization\n");
+       if (crtc != 0) {
+               DRM_ERROR("%s:  bad crtc %d\n", __FUNCTION__, crtc);
                return -EINVAL;
        }
 
-       viadrv_acknowledge_irqs(dev_priv);
+       status = VIA_READ(VIA_REG_INTERRUPT);
+       VIA_WRITE(VIA_REG_INTERRUPT, status & VIA_IRQ_VBLANK_ENABLE);
 
-       /* Assume that the user has missed the current sequence number
-        * by about a day rather than she wants to wait for years
-        * using vertical blanks...
-        */
+       VIA_WRITE8(0x83d4, 0x11);
+       VIA_WRITE8(0x83d5, VIA_READ8(0x83d5) | 0x30);
 
-       DRM_WAIT_ON(ret, dev->vbl_queue, 3 * DRM_HZ,
-                   (((cur_vblank = atomic_read(&dev->vbl_received)) -
-                     *sequence) <= (1 << 23)));
+       return 0;
+}
 
-       *sequence = cur_vblank;
-       return ret;
+void via_disable_vblank(struct drm_device *dev, int crtc)
+{
+       drm_via_private_t *dev_priv = dev->dev_private;
+
+       VIA_WRITE8(0x83d4, 0x11);
+       VIA_WRITE8(0x83d5, VIA_READ8(0x83d5) & ~0x30);
+
+       if (crtc != 0)
+               DRM_ERROR("%s:  bad crtc %d\n", __FUNCTION__, crtc);
 }
 
 static int
@@ -292,23 +303,25 @@ void via_driver_irq_preinstall(struct drm_device * dev)
        }
 }
 
-void via_driver_irq_postinstall(struct drm_device * dev)
+int via_driver_irq_postinstall(struct drm_device * dev)
 {
        drm_via_private_t *dev_priv = (drm_via_private_t *) dev->dev_private;
        u32 status;
 
-       DRM_DEBUG("\n");
-       if (dev_priv) {
-               status = VIA_READ(VIA_REG_INTERRUPT);
-               VIA_WRITE(VIA_REG_INTERRUPT, status | VIA_IRQ_GLOBAL
-                         | dev_priv->irq_enable_mask);
+       DRM_DEBUG("via_driver_irq_postinstall\n");
+       if (!dev_priv)
+               return -EINVAL;
 
-               /* Some magic, oh for some data sheets ! */
+       drm_vblank_init(dev, 1);
+       status = VIA_READ(VIA_REG_INTERRUPT);
+       VIA_WRITE(VIA_REG_INTERRUPT, status | VIA_IRQ_GLOBAL
+                 | dev_priv->irq_enable_mask);
 
-               VIA_WRITE8(0x83d4, 0x11);
-               VIA_WRITE8(0x83d5, VIA_READ8(0x83d5) | 0x30);
+       /* Some magic, oh for some data sheets ! */
+       VIA_WRITE8(0x83d4, 0x11);
+       VIA_WRITE8(0x83d5, VIA_READ8(0x83d5) | 0x30);
 
-       }
+       return 0;
 }
 
 void via_driver_irq_uninstall(struct drm_device * dev)