Initial checkin of vblank rework. Code attempts to reduce the number
authorJesse Barnes <jbarnes@hobbes.virtuousgeek.org>
Tue, 12 Jun 2007 17:44:21 +0000 (10:44 -0700)
committerJesse Barnes <jbarnes@hobbes.virtuousgeek.org>
Tue, 12 Jun 2007 17:44:21 +0000 (10:44 -0700)
of vblank interrupt in order to save power.

linux-core/drmP.h
linux-core/drm_irq.c
linux-core/i915_drv.c
shared-core/i915_dma.c
shared-core/i915_drm.h
shared-core/i915_drv.h
shared-core/i915_irq.c
shared-core/radeon_drv.h
shared-core/radeon_irq.c

index dd3a69d..c3f2031 100644 (file)
@@ -627,8 +627,9 @@ struct drm_driver {
        int (*kernel_context_switch) (struct drm_device * dev, int old,
                                      int new);
        void (*kernel_context_switch_unlock) (struct drm_device * dev);
-       int (*vblank_wait) (struct drm_device * dev, unsigned int *sequence);
-       int (*vblank_wait2) (struct drm_device * dev, unsigned int *sequence);
+       u32 (*get_vblank_counter) (struct drm_device *dev, int crtc);
+       void (*enable_vblank) (struct drm_device *dev, int crtc);
+       void (*disable_vblank) (struct drm_device *dev, int crtc);
        int (*dri_library_name) (struct drm_device * dev, char * buf);
 
        /**
@@ -783,12 +784,13 @@ typedef struct drm_device {
        /*@{ */
 
        wait_queue_head_t vbl_queue;    /**< VBLANK wait queue */
-       atomic_t vbl_received;
-       atomic_t vbl_received2;         /**< number of secondary VBLANK interrupts */
+       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;
+       atomic_t *vbl_pending;
+       u32 *last_vblank; /* protected by dev->vbl_lock */
+       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);
 
index 8871671..f229f77 100644 (file)
@@ -221,6 +221,44 @@ int drm_control(struct inode *inode, struct file *filp,
        }
 }
 
+static void drm_vblank_get(drm_device_t *dev, int crtc)
+{
+       unsigned long irqflags;
+       u32 cur_vblank, diff;
+
+       if (atomic_add_return(1, &dev->vbl_pending[crtc]) != 1)
+               return;
+
+       /*
+        * 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 - last_vblank;
+       }
+       dev->last_vblank[crtc] = cur_vblank;
+       spin_lock_irqrestore(&dev->vbl_lock, irqflags);
+
+       atomic_add(diff, &dev->vblank_count[crtc]);
+       dev->driver->enable_vblank(dev, crtc);
+}
+
+static void drm_vblank_put(drm_device_t *dev, int crtc)
+{
+       if (atomic_dec_and_test(&dev->vbl_pending[crtc]))
+               dev->driver->disable_vblank(dev, crtc);
+}
+
+
 /**
  * Wait for VBLANK.
  *
@@ -248,7 +286,7 @@ int drm_wait_vblank(DRM_IOCTL_ARGS)
        drm_wait_vblank_t vblwait;
        struct timeval now;
        int ret = 0;
-       unsigned int flags, seq;
+       unsigned int flags, seq, crtc;
 
        if ((!dev->irq) || (!dev->irq_enabled))
                return -EINVAL;
@@ -265,13 +303,14 @@ int drm_wait_vblank(DRM_IOCTL_ARGS)
        }
 
        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))
                return -EINVAL;
 
-       seq = atomic_read((flags & _DRM_VBLANK_SECONDARY) ? &dev->vbl_received2
-                         : &dev->vbl_received);
+       drm_vblank_get(dev, crtc);
+       seq = atomic_read(&dev->vblank_count[crtc]);
 
        switch (vblwait.request.type & _DRM_VBLANK_TYPES_MASK) {
        case _DRM_VBLANK_RELATIVE:
@@ -307,22 +346,23 @@ int drm_wait_vblank(DRM_IOCTL_ARGS)
                                spin_unlock_irqrestore(&dev->vbl_lock,
                                                       irqflags);
                                vblwait.reply.sequence = seq;
+                               drm_vblank_put(dev, crtc);
                                goto done;
                        }
                }
 
-               if (dev->vbl_pending >= 100) {
+               if (atomic_read(&dev->vbl_pending[crtc]) >= 100) {
                        spin_unlock_irqrestore(&dev->vbl_lock, irqflags);
+                       drm_vblank_put(dev, crtc);
                        return -EBUSY;
                }
 
-               dev->vbl_pending++;
-
                spin_unlock_irqrestore(&dev->vbl_lock, irqflags);
 
                if (!
                    (vbl_sig =
                     drm_alloc(sizeof(drm_vbl_sig_t), DRM_MEM_DRIVER))) {
+                       drm_vblank_put(dev, crtc);
                        return -ENOMEM;
                }
 
@@ -340,14 +380,12 @@ int drm_wait_vblank(DRM_IOCTL_ARGS)
 
                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;
 
+               DRM_WAIT_ON(ret, dev->vbl_queue, 3 * DRM_HZ,
+                           (((cur_vblank = atomic_read(&dev->vblank_count[crtc]))
+                             - seq) <= (1 << 23)));
+               drm_vblank_put(dev, crtc);
                do_gettimeofday(&now);
                vblwait.reply.tval_sec = now.tv_sec;
                vblwait.reply.tval_usec = now.tv_usec;
@@ -379,8 +417,7 @@ void drm_vbl_send_signals(drm_device_t * dev)
        for (i = 0; i < 2; i++) {
                drm_vbl_sig_t *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);
+               unsigned int vbl_seq = atomic_read(&dev->vblank_count[i]);
 
                list_for_each_entry_safe(vbl_sig, tmp, vbl_sigs, head) {
                        if ((vbl_seq - vbl_sig->sequence) <= (1 << 23)) {
@@ -393,7 +430,7 @@ void drm_vbl_send_signals(drm_device_t * dev)
                                drm_free(vbl_sig, sizeof(*vbl_sig),
                                         DRM_MEM_DRIVER);
 
-                               dev->vbl_pending--;
+                               drm_vblank_put(dev, i);
                        }
                }
        }
index 7fdb083..e7a9bfd 100644 (file)
@@ -83,8 +83,9 @@ static struct drm_driver driver = {
        .lastclose = i915_driver_lastclose,
        .preclose = i915_driver_preclose,
        .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 ebb184c..73972d5 100644 (file)
@@ -833,6 +833,20 @@ static int i915_setparam(DRM_IOCTL_ARGS)
        case I915_SETPARAM_ALLOW_BATCHBUFFER:
                dev_priv->allow_batchbuffer = param.value;
                break;
+       case I915_SETPARAM_PREMODESET:
+               if (param.value > 1) {
+                       DRM_ERROR("bad crtc\n");
+                       return -EINVAL;
+               }
+               i915_premodeset(dev, param.value);
+               break;
+       case I915_SETPARAM_POSTMODESET:
+               if (param.value > 1) {
+                       DRM_ERROR("bad crtc\n");
+                       return -EINVAL;
+               }
+               i915_postmodeset(dev, param.value);
+               break;
        default:
                DRM_ERROR("unknown parameter %d\n", param.param);
                return DRM_ERR(EINVAL);
index 1c6ff4d..aea048f 100644 (file)
@@ -235,6 +235,8 @@ typedef struct drm_i915_getparam {
 #define I915_SETPARAM_USE_MI_BATCHBUFFER_START            1
 #define I915_SETPARAM_TEX_LRU_LOG_GRANULARITY             2
 #define I915_SETPARAM_ALLOW_BATCHBUFFER                   3
+#define I915_SETPARAM_PREMODESET                          4
+#define I915_SETPARAM_POSTMODESET                         5
 
 typedef struct drm_i915_setparam {
        int param;
index 9deee8e..88949ad 100644 (file)
@@ -132,6 +132,8 @@ typedef struct drm_i915_private {
        spinlock_t swaps_lock;
        drm_i915_vbl_swap_t vbl_swaps;
        unsigned int swaps_pending;
+       unsigned long vblank_offset[2];
+       unsigned long vblank_premodeset[2];
 } drm_i915_private_t;
 
 enum intel_chip_family {
@@ -161,8 +163,6 @@ extern int i915_driver_firstopen(struct drm_device *dev);
 extern int i915_irq_emit(DRM_IOCTL_ARGS);
 extern int i915_irq_wait(DRM_IOCTL_ARGS);
 
-extern int i915_driver_vblank_wait(drm_device_t *dev, unsigned int *sequence);
-extern int i915_driver_vblank_wait2(drm_device_t *dev, unsigned int *sequence);
 extern irqreturn_t i915_driver_irq_handler(DRM_IRQ_ARGS);
 extern void i915_driver_irq_preinstall(drm_device_t * dev);
 extern void i915_driver_irq_postinstall(drm_device_t * dev);
@@ -173,6 +173,11 @@ extern int i915_emit_irq(drm_device_t * dev);
 extern void i915_user_irq_on(drm_i915_private_t *dev_priv);
 extern void i915_user_irq_off(drm_i915_private_t *dev_priv);
 extern int i915_vblank_swap(DRM_IOCTL_ARGS);
+extern void i915_enable_vblank(drm_device_t *dev, int crtc);
+extern void i915_disable_vblank(drm_device_t *dev, int crtc);
+extern u32 i915_get_vblank_counter(drm_device_t *dev, int crtc);
+extern void i915_premodeset(drm_device_t *dev, int crtc);
+extern void i915_postmodeset(drm_device_t *dev, int crtc);
 
 /* i915_mem.c */
 extern int i915_mem_alloc(DRM_IOCTL_ARGS);
@@ -271,6 +276,36 @@ extern int i915_wait_ring(drm_device_t * dev, int n, const char *caller);
 
 #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 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_VBLANK_INTERRUPT_ENABLE   (1UL<<17)
 #define I915_VBLANK_CLEAR              (1UL<<1)
index dc00f98..1019c94 100644 (file)
@@ -92,8 +92,8 @@ static void i915_vblank_tasklet(drm_device_t *dev)
        unsigned long irqflags;
        struct list_head *list, *tmp, hits, *hit;
        int nhits, nrects, slice[2], upper[2], lower[2], i, num_pages;
-       unsigned counter[2] = { atomic_read(&dev->vbl_received),
-                               atomic_read(&dev->vbl_received2) };
+       unsigned counter[2] = { atomic_read(&dev->vblank_count[0]),
+                               atomic_read(&dev->vblank_count[1]) };
        drm_drawable_info_t *drw;
        drm_i915_sarea_t *sarea_priv = dev_priv->sarea_priv;
        u32 cpp = dev_priv->cpp,  offsets[3];
@@ -313,14 +313,14 @@ irqreturn_t i915_driver_irq_handler(DRM_IRQ_ARGS)
                     (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);
+                               atomic_inc(&dev->vblank_count[0]);
                        if (temp & VSYNC_PIPEB_FLAG)
-                               atomic_inc(&dev->vbl_received2);
+                               atomic_inc(&dev->vblank_count[1]);
                } 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);
+                       atomic_inc(&dev->vblank_count[0]);
 
                DRM_WAKEUP(&dev->vbl_queue);
                drm_vbl_send_signals(dev);
@@ -410,37 +410,6 @@ static int i915_wait_irq(drm_device_t * dev, int irq_nr)
        return ret;
 }
 
-static int i915_driver_vblank_do_wait(drm_device_t *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("%s called with no initialization\n", __FUNCTION__);
-               return DRM_ERR(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(drm_device_t *dev, unsigned int *sequence)
-{
-       return i915_driver_vblank_do_wait(dev, sequence, &dev->vbl_received);
-}
-
-int i915_driver_vblank_wait2(drm_device_t *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(DRM_IOCTL_ARGS)
@@ -489,15 +458,99 @@ int i915_irq_wait(DRM_IOCTL_ARGS)
        return i915_wait_irq(dev, irqwait.irq_seq);
 }
 
-static void i915_enable_interrupt (drm_device_t *dev)
+void i915_enable_vblank(drm_device_t *dev, int crtc)
 {
        drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
        
-       dev_priv->irq_enable_reg = USER_INT_FLAG; 
-       if (dev_priv->vblank_pipe & DRM_I915_VBLANK_PIPE_A)
+       switch (crtc) {
+       case 0:
                dev_priv->irq_enable_reg |= VSYNC_PIPEA_FLAG;
-       if (dev_priv->vblank_pipe & DRM_I915_VBLANK_PIPE_B)
+               break;
+       case 1:
                dev_priv->irq_enable_reg |= VSYNC_PIPEB_FLAG;
+               break;
+       default:
+               DRM_ERROR("tried to enable vblank on non-existent crtc %d\n",
+                         crtc);
+               break;
+       }
+
+       I915_WRITE16(I915REG_INT_ENABLE_R, dev_priv->irq_enable_reg);
+}
+
+void i915_disable_vblank(drm_device_t *dev, int crtc)
+{
+       drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
+       
+       switch (crtc) {
+       case 0:
+               dev_priv->irq_enable_reg &= ~VSYNC_PIPEA_FLAG;
+               break;
+       case 1:
+               dev_priv->irq_enable_reg &= ~VSYNC_PIPEB_FLAG;
+               break;
+       default:
+               DRM_ERROR("tried to disable vblank on non-existent crtc %d\n",
+                         crtc);
+               break;
+       }
+
+       I915_WRITE16(I915REG_INT_ENABLE_R, dev_priv->irq_enable_reg);
+}
+
+static u32 i915_vblank_counter(drm_device_t *dev, int crtc)
+{
+       drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
+       unsigned long high_frame = crtc ? PIPEBFRAMEHIGH : PIPEAFRAMEHIGH;
+       unsigned long low_frame = crtc ? PIPEBFRAMEPIXEL : PIPEAFRAMEPIXEL;
+       u32 high1, high2, low, count;
+
+       /*
+        * High & low register fields aren't synchronized, so make sure
+        * we get a low value that's stable across two reads of the high
+        * register.
+        */
+       do {
+               high1 = ((I915_READ(high_frame) & PIPE_FRAME_HIGH_MASK) >>
+                        PIPE_FRAME_HIGH_SHIFT);
+               low =  ((I915_READ(low_frame) & PIPE_FRAME_LOW_MASK) >>
+                       PIPE_FRAME_LOW_SHIFT);
+               high2 = ((I915_READ(high_frame) & PIPE_FRAME_HIGH_MASK) >>
+                        PIPE_FRAME_HIGH_SHIFT);
+       } while (high1 != high2);
+
+       count = (high1 << 8) | low;
+
+       return count;
+}
+
+u32 i915_get_vblank_counter(drm_device_t *dev, int crtc)
+{
+       drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
+       return i915_vblank_counter(dev, crtc) + dev_priv->vblank_offset[crtc];
+}
+
+void i915_premodeset(drm_device_t *dev, int crtc)
+{
+       drm_i915_private_t *dev_priv = dev->dev_private;
+
+       dev_priv->vblank_premodeset[crtc] = i915_vblank_counter(dev, crtc);
+}
+
+void i915_postmodeset(drm_device_t *dev, int crtc)
+{
+       drm_i915_private_t *dev_priv = dev->dev_private;
+       u32 new;
+
+       new = i915_vblank_counter(dev, crtc);
+       dev_priv->vblank_offset[crtc] = dev_priv->vblank_premodeset[crtc] - new;
+}
+
+static void i915_enable_interrupt (drm_device_t *dev)
+{
+       drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
+       
+       dev_priv->irq_enable_reg |= USER_INT_FLAG;
 
        I915_WRITE16(I915REG_INT_ENABLE_R, dev_priv->irq_enable_reg);
        dev_priv->irq_enabled = 1;
@@ -607,7 +660,7 @@ int i915_vblank_swap(DRM_IOCTL_ARGS)
 
        spin_unlock_irqrestore(&dev->drw_lock, irqflags);
 
-       curseq = atomic_read(pipe ? &dev->vbl_received2 : &dev->vbl_received);
+       curseq = atomic_read(&dev->vblank_count[pipe]);
 
        if (seqtype == _DRM_VBLANK_RELATIVE)
                swap.sequence += curseq;
@@ -714,6 +767,7 @@ void i915_driver_irq_preinstall(drm_device_t * dev)
 void i915_driver_irq_postinstall(drm_device_t * dev)
 {
        drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
+       int i;
 
        spin_lock_init(&dev_priv->swaps_lock);
        INIT_LIST_HEAD(&dev_priv->vbl_swaps.head);
@@ -721,6 +775,34 @@ void i915_driver_irq_postinstall(drm_device_t * dev)
 
        dev_priv->user_irq_lock = SPIN_LOCK_UNLOCKED;
        dev_priv->user_irq_refcount = 0;
+       dev_priv->irq_enable_reg = 0;
+       dev->vblank_count = kmalloc(sizeof(atomic_t) * 2, GFP_KERNEL);
+       if (!dev->vblank_count) {
+               DRM_ERROR("out of memory\n");
+               return;
+       }
+       dev->vbl_pending = kmalloc(sizeof(atomic_t) * 2, GFP_KERNEL);
+       if (!dev->vbl_pending) {
+               DRM_ERROR("out of memory\n");
+               kfree(dev->vblank_count);
+               return;
+       }
+       dev->last_vblank = kmalloc(sizeof(atomic_t) * 2, GFP_KERNEL);
+       if (!dev->last_vblank) {
+               DRM_ERROR("out of memory\n");
+               kfree(dev->vblank_count);
+               return;
+       }
+
+       /* Zero per-crtc vblank stuff */
+       for (i = 0; i < 2; i++) {
+               atomic_set(&dev->vbl_pending[i], 0);
+               atomic_set(&dev->vblank_count[i], 0);
+               dev->last_vblank[i] = 0;
+               dev_priv->vblank_offset[i] = 0;
+       }
+
+       dev->max_vblank_count = 0xffffff; /* only 24 bits of frame count */
 
        i915_enable_interrupt(dev);
        DRM_INIT_WAITQUEUE(&dev_priv->irq_queue);
index 92a9b65..283dee3 100644 (file)
@@ -299,6 +299,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 */
 
@@ -364,9 +367,9 @@ extern int radeon_irq_wait(DRM_IOCTL_ARGS);
 
 extern void radeon_do_release(drm_device_t * dev);
 extern int radeon_driver_vblank_wait(drm_device_t * dev,
-                                    unsigned int *sequence);
+                                    unsigned int *sequence, int relative);
 extern int radeon_driver_vblank_wait2(drm_device_t * dev,
-                                     unsigned int *sequence);
+                                     unsigned int *sequence, int relative);
 extern irqreturn_t radeon_driver_irq_handler(DRM_IRQ_ARGS);
 extern void radeon_driver_irq_preinstall(drm_device_t * dev);
 extern void radeon_driver_irq_postinstall(drm_device_t * dev);
@@ -507,6 +510,9 @@ extern int r300_do_cp_cmdbuf(drm_device_t *dev, DRMFILE filp,
                                ? 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_GEN_INT_CNTL            0x0040
 #      define RADEON_CRTC_VBLANK_MASK          (1 << 0)
 #      define RADEON_CRTC2_VBLANK_MASK         (1 << 9)
index 5151b4d..2534ff1 100644 (file)
 #include "radeon_drm.h"
 #include "radeon_drv.h"
 
+static void radeon_irq_set_state(drm_device_t *dev, u32 mask, int state)
+{
+       drm_radeon_private_t *dev_priv = (drm_radeon_private_t *) 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);
+}
+
 static __inline__ u32 radeon_acknowledge_irqs(drm_radeon_private_t * dev_priv,
                                              u32 mask)
 {
@@ -141,14 +153,17 @@ static int radeon_wait_irq(drm_device_t * dev, int swi_nr)
 }
 
 int radeon_driver_vblank_do_wait(drm_device_t * dev, unsigned int *sequence,
-                                int crtc)
+                                int crtc, int relative)
 {
        drm_radeon_private_t *dev_priv =
            (drm_radeon_private_t *) dev->dev_private;
-       unsigned int cur_vblank;
+       unsigned int cur_vblank, diff, irqflags, current_cnt;
        int ret = 0;
        int ack = 0;
        atomic_t *counter;
+       unsigned int *last_cnt;
+       int crtc_cnt_reg;
+       
        if (!dev_priv) {
                DRM_ERROR("%s called with no initialization\n", __FUNCTION__);
                return DRM_ERR(EINVAL);
@@ -156,10 +171,14 @@ int radeon_driver_vblank_do_wait(drm_device_t * dev, unsigned int *sequence,
 
        if (crtc == DRM_RADEON_VBLANK_CRTC1) {
                counter = &dev->vbl_received;
-               ack |= RADEON_CRTC_VBLANK_STAT;
+               ack = RADEON_CRTC_VBLANK_STAT;
+               last_cnt = &dev_priv->crtc_last_cnt;
+               crtc_cnt_reg = RADEON_CRTC_CRNT_FRAME;
        } else if (crtc == DRM_RADEON_VBLANK_CRTC2) {
                counter = &dev->vbl_received2;
-               ack |= RADEON_CRTC2_VBLANK_STAT;
+               ack = RADEON_CRTC2_VBLANK_STAT;
+               last_cnt = &dev_priv->crtc2_last_cnt;
+               crtc_cnt_reg = RADEON_CRTC2_CRNT_FRAME;
        } else
                return DRM_ERR(EINVAL);
 
@@ -167,27 +186,46 @@ int radeon_driver_vblank_do_wait(drm_device_t * dev, unsigned int *sequence,
 
        dev_priv->stats.boxes |= RADEON_BOX_WAIT_IDLE;
 
+       if (!relative) {
+               /*
+                * Assume we haven't missed more than several hours of vblank
+                * events, or that it won't matter if they're not accounted
+                * for in the master counter.
+                */
+               spin_lock_irqsave(&dev->vbl_lock, irqflags);
+               current_cnt = RADEON_READ(crtc_cnt_reg);
+               if (current_cnt < *last_cnt) {
+                       current_cnt += (1 << 21) - *last_cnt;
+                       *last_cnt = 0;
+               }
+               diff = current_cnt - *last_cnt;
+               *last_cnt = RADEON_READ(crtc_cnt_reg);
+               spin_unlock_irqrestore(&dev->vbl_lock, irqflags);
+               atomic_add(diff, counter);
+       }
+
        /* 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...
         */
+       radeon_irq_set_state(dev, ack, 1);
        DRM_WAIT_ON(ret, dev->vbl_queue, 3 * DRM_HZ,
                    (((cur_vblank = atomic_read(counter))
                      - *sequence) <= (1 << 23)));
-
+       radeon_irq_set_state(dev, ack, 0);
        *sequence = cur_vblank;
 
        return ret;
 }
 
-int radeon_driver_vblank_wait(drm_device_t *dev, unsigned int *sequence)
+int radeon_driver_vblank_wait(drm_device_t *dev, unsigned int *sequence, int relative)
 {
-       return radeon_driver_vblank_do_wait(dev, sequence, DRM_RADEON_VBLANK_CRTC1);
+       return radeon_driver_vblank_do_wait(dev, sequence, DRM_RADEON_VBLANK_CRTC1, relative);
 }
 
-int radeon_driver_vblank_wait2(drm_device_t *dev, unsigned int *sequence)
+int radeon_driver_vblank_wait2(drm_device_t *dev, unsigned int *sequence, int relative)
 {
-       return radeon_driver_vblank_do_wait(dev, sequence, DRM_RADEON_VBLANK_CRTC2);
+       return radeon_driver_vblank_do_wait(dev, sequence, DRM_RADEON_VBLANK_CRTC2, relative);
 }
 
 /* Needs the lock as it touches the ring.
@@ -238,20 +276,7 @@ int radeon_irq_wait(DRM_IOCTL_ARGS)
        return radeon_wait_irq(dev, irqwait.irq_seq);
 }
 
-static void radeon_enable_interrupt(drm_device_t *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
 */
@@ -265,7 +290,8 @@ void radeon_driver_irq_preinstall(drm_device_t * dev)
 
        /* Clear bits if they're already high */
        radeon_acknowledge_irqs(dev_priv, (RADEON_SW_INT_TEST_ACK |
-                                          RADEON_CRTC_VBLANK_STAT));
+                                          RADEON_CRTC_VBLANK_STAT |
+                                          RADEON_CRTC2_VBLANK_STAT));
 }
 
 void radeon_driver_irq_postinstall(drm_device_t * dev)
@@ -276,7 +302,7 @@ void radeon_driver_irq_postinstall(drm_device_t * dev)
        atomic_set(&dev_priv->swi_emitted, 0);
        DRM_INIT_WAITQUEUE(&dev_priv->swi_queue);
 
-       radeon_enable_interrupt(dev);
+       radeon_irq_set_state(dev, RADEON_SW_INT_ENABLE, 1);
 }
 
 void radeon_driver_irq_uninstall(drm_device_t * dev)
@@ -318,6 +344,5 @@ int radeon_vblank_crtc_set(drm_device_t *dev, int64_t value)
                return DRM_ERR(EINVAL);
        }
        dev_priv->vblank_crtc = (unsigned int)value;
-       radeon_enable_interrupt(dev);
        return 0;
 }