OSDN Git Service

common ioctl to wait for vertical blank IRQs
authorMichel Daenzer <michel@daenzer.net>
Wed, 25 Sep 2002 17:18:19 +0000 (17:18 +0000)
committerMichel Daenzer <michel@daenzer.net>
Wed, 25 Sep 2002 17:18:19 +0000 (17:18 +0000)
15 files changed:
libdrm/xf86drm.c
linux-core/drmP.h
linux-core/drm_dma.c
linux-core/drm_drv.c
linux/drm.h
linux/drmP.h
linux/drm_dma.h
linux/drm_drv.h
shared-core/drm.h
shared-core/radeon_drv.h
shared-core/radeon_irq.c
shared/drm.h
shared/radeon.h
shared/radeon_drv.h
shared/radeon_irq.c

index d2ce5e1..8d3a20d 100644 (file)
@@ -1105,6 +1105,17 @@ int drmScatterGatherFree(int fd, unsigned long handle)
     return 0;
 }
 
+int drmWaitVBlank(int fd, drmVBlankPtr vbl)
+{
+    int ret;
+
+    do {
+       ret = ioctl(fd, DRM_IOCTL_WAIT_VBLANK, vbl);
+    } while (ret && errno == EINTR);
+
+    return ret;
+}
+
 int drmError(int err, const char *label)
 {
     switch (err) {
index 7d82fce..6f6f91c 100644 (file)
@@ -577,6 +577,10 @@ typedef struct drm_device {
        int               last_context; /* Last current context            */
        unsigned long     last_switch;  /* jiffies at last context switch  */
        struct tq_struct  tq;
+#if __HAVE_VBL_IRQ
+       wait_queue_head_t vbl_queue;
+       atomic_t          vbl_received;
+#endif
        cycles_t          ctx_start;
        cycles_t          lck_start;
 #if __HAVE_DMA_HISTOGRAM
@@ -809,6 +813,14 @@ extern int           DRM(irq_install)( drm_device_t *dev, int irq );
 extern int           DRM(irq_uninstall)( drm_device_t *dev );
 extern void          DRM(dma_service)( int irq, void *device,
                                       struct pt_regs *regs );
+extern void          DRM(driver_irq_preinstall)( drm_device_t *dev );
+extern void          DRM(driver_irq_postinstall)( drm_device_t *dev );
+extern void          DRM(driver_irq_uninstall)( drm_device_t *dev );
+#if __HAVE_VBL_IRQ
+extern int           DRM(wait_vblank)(struct inode *inode, struct file *filp,
+                                     unsigned int cmd, unsigned long arg);
+extern int           DRM(vblank_wait)(drm_device_t *dev, unsigned int *vbl_seq);
+#endif
 #if __HAVE_DMA_IRQ_BH
 extern void          DRM(dma_immediate_bh)( void *dev );
 #endif
index dce376f..2938c77 100644 (file)
@@ -538,8 +538,12 @@ int DRM(irq_install)( drm_device_t *dev, int irq )
        dev->tq.data = dev;
 #endif
 
+#if __HAVE_VBL_IRQ
+       init_waitqueue_head(&dev->vbl_queue);
+#endif
+
                                /* Before installing handler */
-       DRIVER_PREINSTALL();
+       DRM(driver_irq_preinstall)(dev);
 
                                /* Install handler */
        ret = request_irq( dev->irq, DRM(dma_service),
@@ -552,7 +556,7 @@ int DRM(irq_install)( drm_device_t *dev, int irq )
        }
 
                                /* After installing handler */
-       DRIVER_POSTINSTALL();
+       DRM(driver_irq_postinstall)(dev);
 
        return 0;
 }
@@ -571,7 +575,7 @@ int DRM(irq_uninstall)( drm_device_t *dev )
 
        DRM_DEBUG( "%s: irq=%d\n", __FUNCTION__, irq );
 
-       DRIVER_UNINSTALL();
+       DRM(driver_irq_uninstall)( dev );
 
        free_irq( irq, dev );
 
@@ -598,6 +602,78 @@ int DRM(control)( struct inode *inode, struct file *filp,
        }
 }
 
+#if __HAVE_VBL_IRQ
+
+int DRM(vblank_wait)(drm_device_t *dev, unsigned int *sequence)
+{
+       drm_radeon_private_t *dev_priv = 
+          (drm_radeon_private_t *)dev->dev_private;
+       unsigned int cur_vblank;
+       int ret = 0;
+
+       if ( !dev_priv ) {
+               DRM_ERROR( "%s called with no initialization\n", __func__ );
+               return DRM_ERR(EINVAL);
+       }
+
+       /* 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 :)
+        */
+       while ( ( ( cur_vblank = atomic_read(&dev->vbl_received ) )
+               + ~*sequence + 1 ) > (1<<23) ) {
+               dev_priv->stats.boxes |= RADEON_BOX_WAIT_IDLE;
+#ifdef __linux__
+               interruptible_sleep_on( &dev->vbl_queue );
+
+               if (signal_pending(current)) {
+                       ret = -EINTR;
+                       break;
+               }
+#endif /* __linux__ */
+#ifdef __FreeBSD__
+               ret = tsleep( &dev_priv->vbl_queue, 3*hz, "rdnvbl", PZERO | PCATCH);
+               if (ret)
+                       break;
+#endif /* __FreeBSD__ */
+       }
+
+       *sequence = cur_vblank;
+
+       return ret;
+}
+
+int DRM(wait_vblank)( DRM_IOCTL_ARGS )
+{
+       drm_file_t *priv = filp->private_data;
+       drm_device_t *dev = priv->dev;
+       drm_wait_vblank_t vblwait;
+       struct timeval now;
+       int ret;
+
+       if (!dev->irq)
+               return -EINVAL;
+
+       DRM_COPY_FROM_USER_IOCTL( vblwait, (drm_wait_vblank_t *)data,
+                                 sizeof(vblwait) );
+
+       if ( vblwait.type == _DRM_VBLANK_RELATIVE ) {
+               vblwait.sequence += atomic_read( &dev->vbl_received );
+       }
+
+       ret = DRM(vblank_wait)( dev, &vblwait.sequence );
+
+       do_gettimeofday( &now );
+       vblwait.tval_sec = now.tv_sec;
+       vblwait.tval_usec = now.tv_usec;
+
+       DRM_COPY_TO_USER_IOCTL( (drm_wait_vblank_t *)data, vblwait,
+                               sizeof(vblwait) );
+
+       return ret;
+}
+
+#endif /* __HAVE_VBL_IRQ */
+
 #else
 
 int DRM(control)( struct inode *inode, struct file *filp,
index 7e2cfd8..3ebe781 100644 (file)
@@ -222,6 +222,10 @@ static drm_ioctl_desc_t              DRM(ioctls)[] = {
        [DRM_IOCTL_NR(DRM_IOCTL_SG_FREE)]       = { DRM(sg_free),     1, 1 },
 #endif
 
+#if __HAVE_VBL_IRQ
+       [DRM_IOCTL_NR(DRM_IOCTL_WAIT_VBLANK)]   = { DRM(wait_vblank), 0, 0 },
+#endif
+
        DRIVER_IOCTLS
 };
 
index 376568e..f26d444 100644 (file)
@@ -345,6 +345,19 @@ typedef struct drm_irq_busid {
        int funcnum;
 } drm_irq_busid_t;
 
+typedef enum {
+    _DRM_VBLANK_ABSOLUTE = 0x0,        /* Wait for specific vblank sequence number */
+    _DRM_VBLANK_RELATIVE = 0x1 /* Wait for given number of vblanks */
+} drm_vblank_seq_type_t;
+
+typedef struct drm_radeon_vbl_wait {
+       drm_vblank_seq_type_t type;
+       unsigned int sequence;
+       long tval_sec;
+       long tval_usec;
+} drm_wait_vblank_t;
+
+
 typedef struct drm_agp_mode {
        unsigned long mode;
 } drm_agp_mode_t;
@@ -439,6 +452,8 @@ typedef struct drm_scatter_gather {
 #define DRM_IOCTL_SG_ALLOC             DRM_IOW( 0x38, drm_scatter_gather_t)
 #define DRM_IOCTL_SG_FREE              DRM_IOW( 0x39, drm_scatter_gather_t)
 
+#define DRM_IOCTL_WAIT_VBLANK          DRM_IOWR(0x3a, drm_wait_vblank_t)
+
 /* Device specfic ioctls should only be in their respective headers
  * The device specific ioctl range is 0x40 to 0x79.                  */
 #define DRM_COMMAND_BASE                0x40
index 7d82fce..6f6f91c 100644 (file)
@@ -577,6 +577,10 @@ typedef struct drm_device {
        int               last_context; /* Last current context            */
        unsigned long     last_switch;  /* jiffies at last context switch  */
        struct tq_struct  tq;
+#if __HAVE_VBL_IRQ
+       wait_queue_head_t vbl_queue;
+       atomic_t          vbl_received;
+#endif
        cycles_t          ctx_start;
        cycles_t          lck_start;
 #if __HAVE_DMA_HISTOGRAM
@@ -809,6 +813,14 @@ extern int           DRM(irq_install)( drm_device_t *dev, int irq );
 extern int           DRM(irq_uninstall)( drm_device_t *dev );
 extern void          DRM(dma_service)( int irq, void *device,
                                       struct pt_regs *regs );
+extern void          DRM(driver_irq_preinstall)( drm_device_t *dev );
+extern void          DRM(driver_irq_postinstall)( drm_device_t *dev );
+extern void          DRM(driver_irq_uninstall)( drm_device_t *dev );
+#if __HAVE_VBL_IRQ
+extern int           DRM(wait_vblank)(struct inode *inode, struct file *filp,
+                                     unsigned int cmd, unsigned long arg);
+extern int           DRM(vblank_wait)(drm_device_t *dev, unsigned int *vbl_seq);
+#endif
 #if __HAVE_DMA_IRQ_BH
 extern void          DRM(dma_immediate_bh)( void *dev );
 #endif
index dce376f..2938c77 100644 (file)
@@ -538,8 +538,12 @@ int DRM(irq_install)( drm_device_t *dev, int irq )
        dev->tq.data = dev;
 #endif
 
+#if __HAVE_VBL_IRQ
+       init_waitqueue_head(&dev->vbl_queue);
+#endif
+
                                /* Before installing handler */
-       DRIVER_PREINSTALL();
+       DRM(driver_irq_preinstall)(dev);
 
                                /* Install handler */
        ret = request_irq( dev->irq, DRM(dma_service),
@@ -552,7 +556,7 @@ int DRM(irq_install)( drm_device_t *dev, int irq )
        }
 
                                /* After installing handler */
-       DRIVER_POSTINSTALL();
+       DRM(driver_irq_postinstall)(dev);
 
        return 0;
 }
@@ -571,7 +575,7 @@ int DRM(irq_uninstall)( drm_device_t *dev )
 
        DRM_DEBUG( "%s: irq=%d\n", __FUNCTION__, irq );
 
-       DRIVER_UNINSTALL();
+       DRM(driver_irq_uninstall)( dev );
 
        free_irq( irq, dev );
 
@@ -598,6 +602,78 @@ int DRM(control)( struct inode *inode, struct file *filp,
        }
 }
 
+#if __HAVE_VBL_IRQ
+
+int DRM(vblank_wait)(drm_device_t *dev, unsigned int *sequence)
+{
+       drm_radeon_private_t *dev_priv = 
+          (drm_radeon_private_t *)dev->dev_private;
+       unsigned int cur_vblank;
+       int ret = 0;
+
+       if ( !dev_priv ) {
+               DRM_ERROR( "%s called with no initialization\n", __func__ );
+               return DRM_ERR(EINVAL);
+       }
+
+       /* 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 :)
+        */
+       while ( ( ( cur_vblank = atomic_read(&dev->vbl_received ) )
+               + ~*sequence + 1 ) > (1<<23) ) {
+               dev_priv->stats.boxes |= RADEON_BOX_WAIT_IDLE;
+#ifdef __linux__
+               interruptible_sleep_on( &dev->vbl_queue );
+
+               if (signal_pending(current)) {
+                       ret = -EINTR;
+                       break;
+               }
+#endif /* __linux__ */
+#ifdef __FreeBSD__
+               ret = tsleep( &dev_priv->vbl_queue, 3*hz, "rdnvbl", PZERO | PCATCH);
+               if (ret)
+                       break;
+#endif /* __FreeBSD__ */
+       }
+
+       *sequence = cur_vblank;
+
+       return ret;
+}
+
+int DRM(wait_vblank)( DRM_IOCTL_ARGS )
+{
+       drm_file_t *priv = filp->private_data;
+       drm_device_t *dev = priv->dev;
+       drm_wait_vblank_t vblwait;
+       struct timeval now;
+       int ret;
+
+       if (!dev->irq)
+               return -EINVAL;
+
+       DRM_COPY_FROM_USER_IOCTL( vblwait, (drm_wait_vblank_t *)data,
+                                 sizeof(vblwait) );
+
+       if ( vblwait.type == _DRM_VBLANK_RELATIVE ) {
+               vblwait.sequence += atomic_read( &dev->vbl_received );
+       }
+
+       ret = DRM(vblank_wait)( dev, &vblwait.sequence );
+
+       do_gettimeofday( &now );
+       vblwait.tval_sec = now.tv_sec;
+       vblwait.tval_usec = now.tv_usec;
+
+       DRM_COPY_TO_USER_IOCTL( (drm_wait_vblank_t *)data, vblwait,
+                               sizeof(vblwait) );
+
+       return ret;
+}
+
+#endif /* __HAVE_VBL_IRQ */
+
 #else
 
 int DRM(control)( struct inode *inode, struct file *filp,
index 7e2cfd8..3ebe781 100644 (file)
@@ -222,6 +222,10 @@ static drm_ioctl_desc_t              DRM(ioctls)[] = {
        [DRM_IOCTL_NR(DRM_IOCTL_SG_FREE)]       = { DRM(sg_free),     1, 1 },
 #endif
 
+#if __HAVE_VBL_IRQ
+       [DRM_IOCTL_NR(DRM_IOCTL_WAIT_VBLANK)]   = { DRM(wait_vblank), 0, 0 },
+#endif
+
        DRIVER_IOCTLS
 };
 
index 376568e..f26d444 100644 (file)
@@ -345,6 +345,19 @@ typedef struct drm_irq_busid {
        int funcnum;
 } drm_irq_busid_t;
 
+typedef enum {
+    _DRM_VBLANK_ABSOLUTE = 0x0,        /* Wait for specific vblank sequence number */
+    _DRM_VBLANK_RELATIVE = 0x1 /* Wait for given number of vblanks */
+} drm_vblank_seq_type_t;
+
+typedef struct drm_radeon_vbl_wait {
+       drm_vblank_seq_type_t type;
+       unsigned int sequence;
+       long tval_sec;
+       long tval_usec;
+} drm_wait_vblank_t;
+
+
 typedef struct drm_agp_mode {
        unsigned long mode;
 } drm_agp_mode_t;
@@ -439,6 +452,8 @@ typedef struct drm_scatter_gather {
 #define DRM_IOCTL_SG_ALLOC             DRM_IOW( 0x38, drm_scatter_gather_t)
 #define DRM_IOCTL_SG_FREE              DRM_IOW( 0x39, drm_scatter_gather_t)
 
+#define DRM_IOCTL_WAIT_VBLANK          DRM_IOWR(0x3a, drm_wait_vblank_t)
+
 /* Device specfic ioctls should only be in their respective headers
  * The device specific ioctl range is 0x40 to 0x79.                  */
 #define DRM_COMMAND_BASE                0x40
index efe3020..5c43ebc 100644 (file)
@@ -139,9 +139,10 @@ typedef struct drm_radeon_private {
        struct mem_block *agp_heap;
        struct mem_block *fb_heap;
 
-       wait_queue_head_t irq_queue;
-       atomic_t irq_received;
-       atomic_t irq_emitted;
+       /* SW interrupt */
+       wait_queue_head_t swi_queue;
+       atomic_t swi_received;
+       atomic_t swi_emitted;
 
 } drm_radeon_private_t;
 
@@ -187,12 +188,14 @@ extern int radeon_mem_init_heap( DRM_IOCTL_ARGS );
 extern void radeon_mem_takedown( struct mem_block **heap );
 extern void radeon_mem_release( struct mem_block *heap );
 
+                               /* radeon_irq.c */
 extern int radeon_irq_emit( DRM_IOCTL_ARGS );
 extern int radeon_irq_wait( DRM_IOCTL_ARGS );
 
 extern int radeon_emit_and_wait_irq(drm_device_t *dev);
-extern int radeon_wait_irq(drm_device_t *dev, int irq_nr);
+extern int radeon_wait_irq(drm_device_t *dev, int swi_nr);
 extern int radeon_emit_irq(drm_device_t *dev);
+extern int radeon_vblank_wait(drm_device_t *dev, unsigned int *vbl_seq);
 
 
 /* Flags for stats.boxes
@@ -271,11 +274,15 @@ extern int radeon_emit_irq(drm_device_t *dev);
 
 
 #define RADEON_GEN_INT_CNTL            0x0040
+#      define RADEON_CRTC_VBLANK_MASK          (1 << 0)
 #      define RADEON_GUI_IDLE_INT_ENABLE       (1 << 19)
 #      define RADEON_SW_INT_ENABLE             (1 << 25)
 
 #define RADEON_GEN_INT_STATUS          0x0044
+#      define RADEON_CRTC_VBLANK_STAT          (1 << 0)
+#      define RADEON_CRTC_VBLANK_STAT_ACK      (1 << 0)
 #      define RADEON_GUI_IDLE_INT_TEST_ACK     (1 << 19)
+#      define RADEON_SW_INT_TEST               (1 << 25)
 #      define RADEON_SW_INT_TEST_ACK           (1 << 25)
 #      define RADEON_SW_INT_FIRE               (1 << 26)
 
index 1160939..7b17038 100644 (file)
@@ -1,4 +1,4 @@
-/* radeon_mem.c -- Simple agp/fb memory manager for radeon -*- linux-c -*-
+/* radeon_irq.c -- IRQ handling for radeon -*- linux-c -*-
  *
  * Copyright (C) The Weather Channel, Inc.  2002.  All Rights Reserved.
  * 
@@ -58,46 +58,47 @@ void DRM(dma_service)( DRM_IRQ_ARGS )
        drm_device_t *dev = (drm_device_t *) arg;
        drm_radeon_private_t *dev_priv = 
           (drm_radeon_private_t *)dev->dev_private;
-       u32 temp;
+       u32 stat, ack = 0;
 
        /* Need to wait for fifo to drain?
         */
-       temp = RADEON_READ(RADEON_GEN_INT_STATUS);  
-       temp = temp & RADEON_SW_INT_TEST_ACK;  
-       if (temp == 0) return;  
-       RADEON_WRITE(RADEON_GEN_INT_STATUS, temp);  
+       stat = RADEON_READ(RADEON_GEN_INT_STATUS);
 
-       atomic_inc(&dev_priv->irq_received);
+       /* SW interrupt */
+       if (stat & RADEON_SW_INT_TEST) {
+               ack |= RADEON_SW_INT_TEST_ACK;
+               atomic_inc(&dev_priv->swi_received);
 #ifdef __linux__
-       queue_task(&dev->tq, &tq_immediate);  
-       mark_bh(IMMEDIATE_BH);  
-#endif /* __linux__ */
+               wake_up_interruptible(&dev_priv->swi_queue);
+#endif
 #ifdef __FreeBSD__
-       taskqueue_enqueue(taskqueue_swi, &dev->task);
-#endif /* __FreeBSD__ */
-}
-
-void DRM(dma_immediate_bh)( DRM_TASKQUEUE_ARGS )
-{
-       drm_device_t *dev = (drm_device_t *) arg;
-       drm_radeon_private_t *dev_priv = 
-          (drm_radeon_private_t *)dev->dev_private;
+               wakeup(&dev->vbl_queue);
+#endif
+       }
 
+       /* VBLANK interrupt */
+       if (stat & RADEON_CRTC_VBLANK_STAT) {
+               ack |= RADEON_CRTC_VBLANK_STAT_ACK;
+               atomic_inc(&dev->vbl_received);
 #ifdef __linux__
-       wake_up_interruptible(&dev_priv->irq_queue); 
-#endif /* __linux__ */
+               wake_up_interruptible(&dev->vbl_queue);
+#endif
 #ifdef __FreeBSD__
-       wakeup( &dev_priv->irq_queue );
+               wakeup(&dev->vbl_queue);
 #endif
-}
+       }
 
+       if (ack)
+               RADEON_WRITE(RADEON_GEN_INT_STATUS, ack);
+}
 
 int radeon_emit_irq(drm_device_t *dev)
 {
        drm_radeon_private_t *dev_priv = dev->dev_private;
        RING_LOCALS;
 
-       atomic_inc(&dev_priv->irq_emitted);
+       atomic_inc(&dev_priv->swi_emitted);
 
        BEGIN_RING(2); 
        OUT_RING( CP_PACKET0( RADEON_GEN_INT_STATUS, 0 ) );
@@ -105,11 +106,11 @@ int radeon_emit_irq(drm_device_t *dev)
        ADVANCE_RING(); 
        COMMIT_RING();
 
-       return atomic_read(&dev_priv->irq_emitted);
+       return atomic_read(&dev_priv->swi_emitted);
 }
 
 
-int radeon_wait_irq(drm_device_t *dev, int irq_nr)
+int radeon_wait_irq(drm_device_t *dev, int swi_nr)
 {
        drm_radeon_private_t *dev_priv = 
           (drm_radeon_private_t *)dev->dev_private;
@@ -119,17 +120,17 @@ int radeon_wait_irq(drm_device_t *dev, int irq_nr)
 #endif /* __linux__ */
        int ret = 0;
 
-       if (atomic_read(&dev_priv->irq_received) >= irq_nr)  
+       if (atomic_read(&dev_priv->swi_received) >= swi_nr)  
                return 0; 
 
        dev_priv->stats.boxes |= RADEON_BOX_WAIT_IDLE;
 
 #ifdef __linux__
-       add_wait_queue(&dev_priv->irq_queue, &entry);
+       add_wait_queue(&dev_priv->swi_queue, &entry);
 
        for (;;) {
                current->state = TASK_INTERRUPTIBLE;
-               if (atomic_read(&dev_priv->irq_received) >= irq_nr) 
+               if (atomic_read(&dev_priv->swi_received) >= swi_nr) 
                   break;
                if((signed)(end - jiffies) <= 0) {
                        ret = -EBUSY;   /* Lockup?  Missed irq? */
@@ -143,12 +144,12 @@ int radeon_wait_irq(drm_device_t *dev, int irq_nr)
        }
 
        current->state = TASK_RUNNING;
-       remove_wait_queue(&dev_priv->irq_queue, &entry);
+       remove_wait_queue(&dev_priv->swi_queue, &entry);
        return ret;
 #endif /* __linux__ */
        
 #ifdef __FreeBSD__
-       ret = tsleep( &dev_priv->irq_queue, PZERO | PCATCH, \
+       ret = tsleep( &dev_priv->swi_queue, PZERO | PCATCH, \
                "rdnirq", 3*hz );
        if ( (ret == EWOULDBLOCK) || (ret == EINTR) )
                return DRM_ERR(EBUSY);
@@ -156,7 +157,6 @@ int radeon_wait_irq(drm_device_t *dev, int irq_nr)
 #endif /* __FreeBSD__ */
 }
 
-
 int radeon_emit_and_wait_irq(drm_device_t *dev)
 {
        return radeon_wait_irq( dev, radeon_emit_irq(dev) );
@@ -212,3 +212,41 @@ int radeon_irq_wait( DRM_IOCTL_ARGS )
        return radeon_wait_irq( dev, irqwait.irq_seq );
 }
 
+void DRM(driver_irq_preinstall)( drm_device_t *dev ) {
+       drm_radeon_private_t *dev_priv =
+               (drm_radeon_private_t *)dev->dev_private;
+       u32 tmp;
+
+       /* Disable *all* interrupts */
+       RADEON_WRITE( RADEON_GEN_INT_CNTL, 0 );
+
+       /* Clear bits if they're already high */
+       tmp = RADEON_READ( RADEON_GEN_INT_STATUS );
+       RADEON_WRITE( RADEON_GEN_INT_STATUS, tmp );
+}
+
+void DRM(driver_irq_postinstall)( drm_device_t *dev ) {
+       drm_radeon_private_t *dev_priv =
+               (drm_radeon_private_t *)dev->dev_private;
+
+       atomic_set(&dev_priv->swi_received, 0);
+       atomic_set(&dev_priv->swi_emitted, 0);
+#ifdef __linux__
+       init_waitqueue_head(&dev_priv->swi_queue);
+#endif
+
+       /* Turn on SW and VBL ints */
+       RADEON_WRITE( RADEON_GEN_INT_CNTL,
+                     RADEON_CRTC_VBLANK_MASK | 
+                     RADEON_SW_INT_ENABLE );
+}
+
+void DRM(driver_irq_uninstall)( drm_device_t *dev ) {
+       drm_radeon_private_t *dev_priv =
+               (drm_radeon_private_t *)dev->dev_private;
+       if ( dev_priv ) {
+               /* Disable *all* interrupts */
+               RADEON_WRITE( RADEON_GEN_INT_CNTL, 0 );
+       }
+}
index 376568e..f26d444 100644 (file)
@@ -345,6 +345,19 @@ typedef struct drm_irq_busid {
        int funcnum;
 } drm_irq_busid_t;
 
+typedef enum {
+    _DRM_VBLANK_ABSOLUTE = 0x0,        /* Wait for specific vblank sequence number */
+    _DRM_VBLANK_RELATIVE = 0x1 /* Wait for given number of vblanks */
+} drm_vblank_seq_type_t;
+
+typedef struct drm_radeon_vbl_wait {
+       drm_vblank_seq_type_t type;
+       unsigned int sequence;
+       long tval_sec;
+       long tval_usec;
+} drm_wait_vblank_t;
+
+
 typedef struct drm_agp_mode {
        unsigned long mode;
 } drm_agp_mode_t;
@@ -439,6 +452,8 @@ typedef struct drm_scatter_gather {
 #define DRM_IOCTL_SG_ALLOC             DRM_IOW( 0x38, drm_scatter_gather_t)
 #define DRM_IOCTL_SG_FREE              DRM_IOW( 0x39, drm_scatter_gather_t)
 
+#define DRM_IOCTL_WAIT_VBLANK          DRM_IOWR(0x3a, drm_wait_vblank_t)
+
 /* Device specfic ioctls should only be in their respective headers
  * The device specific ioctl range is 0x40 to 0x79.                  */
 #define DRM_COMMAND_BASE                0x40
index 4cb0098..aa613c4 100644 (file)
  */
 #define __HAVE_DMA             1
 
-
 #define __HAVE_DMA_IRQ         1
-#define __HAVE_DMA_IRQ_BH      1 
+#define __HAVE_VBL_IRQ         1
 #define __HAVE_SHARED_IRQ       1
 
-#define DRIVER_PREINSTALL() do {                               \
-       drm_radeon_private_t *dev_priv =                        \
-               (drm_radeon_private_t *)dev->dev_private;       \
-       u32 tmp;                                                \
-                                                               \
-       /* Clear bit if it's already high: */                   \
-       tmp = RADEON_READ( RADEON_GEN_INT_STATUS );             \
-       tmp = tmp & RADEON_SW_INT_TEST_ACK;                     \
-       RADEON_WRITE( RADEON_GEN_INT_STATUS, tmp );             \
-                                                               \
-       /* Disable *all* interrupts */                          \
-       RADEON_WRITE( RADEON_GEN_INT_CNTL, 0 );                 \
-} while (0)
-
-#ifdef __linux__ 
-#define IWH(x) init_waitqueue_head(x) 
-#else 
-#define IWH(x) 
-#endif 
-
-#define DRIVER_POSTINSTALL() do {                              \
-       drm_radeon_private_t *dev_priv =                        \
-               (drm_radeon_private_t *)dev->dev_private;       \
-                                                               \
-       atomic_set(&dev_priv->irq_received, 0);                 \
-       atomic_set(&dev_priv->irq_emitted, 0);                  \
-       IWH(&dev_priv->irq_queue);              \
-                                                               \
-       /* Turn on SW_INT only */                               \
-       RADEON_WRITE( RADEON_GEN_INT_CNTL,                      \
-                     RADEON_SW_INT_ENABLE );                   \
-} while (0)
-
-#define DRIVER_UNINSTALL() do {                                        \
-       drm_radeon_private_t *dev_priv =                        \
-               (drm_radeon_private_t *)dev->dev_private;       \
-       if ( dev_priv ) {                                       \
-               /* Disable *all* interrupts */                  \
-               RADEON_WRITE( RADEON_GEN_INT_CNTL, 0 );         \
-       }                                                       \
-} while (0)
-
 /* Buffer customization:
  */
 #define DRIVER_BUF_PRIV_T      drm_radeon_buf_priv_t
index efe3020..5c43ebc 100644 (file)
@@ -139,9 +139,10 @@ typedef struct drm_radeon_private {
        struct mem_block *agp_heap;
        struct mem_block *fb_heap;
 
-       wait_queue_head_t irq_queue;
-       atomic_t irq_received;
-       atomic_t irq_emitted;
+       /* SW interrupt */
+       wait_queue_head_t swi_queue;
+       atomic_t swi_received;
+       atomic_t swi_emitted;
 
 } drm_radeon_private_t;
 
@@ -187,12 +188,14 @@ extern int radeon_mem_init_heap( DRM_IOCTL_ARGS );
 extern void radeon_mem_takedown( struct mem_block **heap );
 extern void radeon_mem_release( struct mem_block *heap );
 
+                               /* radeon_irq.c */
 extern int radeon_irq_emit( DRM_IOCTL_ARGS );
 extern int radeon_irq_wait( DRM_IOCTL_ARGS );
 
 extern int radeon_emit_and_wait_irq(drm_device_t *dev);
-extern int radeon_wait_irq(drm_device_t *dev, int irq_nr);
+extern int radeon_wait_irq(drm_device_t *dev, int swi_nr);
 extern int radeon_emit_irq(drm_device_t *dev);
+extern int radeon_vblank_wait(drm_device_t *dev, unsigned int *vbl_seq);
 
 
 /* Flags for stats.boxes
@@ -271,11 +274,15 @@ extern int radeon_emit_irq(drm_device_t *dev);
 
 
 #define RADEON_GEN_INT_CNTL            0x0040
+#      define RADEON_CRTC_VBLANK_MASK          (1 << 0)
 #      define RADEON_GUI_IDLE_INT_ENABLE       (1 << 19)
 #      define RADEON_SW_INT_ENABLE             (1 << 25)
 
 #define RADEON_GEN_INT_STATUS          0x0044
+#      define RADEON_CRTC_VBLANK_STAT          (1 << 0)
+#      define RADEON_CRTC_VBLANK_STAT_ACK      (1 << 0)
 #      define RADEON_GUI_IDLE_INT_TEST_ACK     (1 << 19)
+#      define RADEON_SW_INT_TEST               (1 << 25)
 #      define RADEON_SW_INT_TEST_ACK           (1 << 25)
 #      define RADEON_SW_INT_FIRE               (1 << 26)
 
index 1160939..7b17038 100644 (file)
@@ -1,4 +1,4 @@
-/* radeon_mem.c -- Simple agp/fb memory manager for radeon -*- linux-c -*-
+/* radeon_irq.c -- IRQ handling for radeon -*- linux-c -*-
  *
  * Copyright (C) The Weather Channel, Inc.  2002.  All Rights Reserved.
  * 
@@ -58,46 +58,47 @@ void DRM(dma_service)( DRM_IRQ_ARGS )
        drm_device_t *dev = (drm_device_t *) arg;
        drm_radeon_private_t *dev_priv = 
           (drm_radeon_private_t *)dev->dev_private;
-       u32 temp;
+       u32 stat, ack = 0;
 
        /* Need to wait for fifo to drain?
         */
-       temp = RADEON_READ(RADEON_GEN_INT_STATUS);  
-       temp = temp & RADEON_SW_INT_TEST_ACK;  
-       if (temp == 0) return;  
-       RADEON_WRITE(RADEON_GEN_INT_STATUS, temp);  
+       stat = RADEON_READ(RADEON_GEN_INT_STATUS);
 
-       atomic_inc(&dev_priv->irq_received);
+       /* SW interrupt */
+       if (stat & RADEON_SW_INT_TEST) {
+               ack |= RADEON_SW_INT_TEST_ACK;
+               atomic_inc(&dev_priv->swi_received);
 #ifdef __linux__
-       queue_task(&dev->tq, &tq_immediate);  
-       mark_bh(IMMEDIATE_BH);  
-#endif /* __linux__ */
+               wake_up_interruptible(&dev_priv->swi_queue);
+#endif
 #ifdef __FreeBSD__
-       taskqueue_enqueue(taskqueue_swi, &dev->task);
-#endif /* __FreeBSD__ */
-}
-
-void DRM(dma_immediate_bh)( DRM_TASKQUEUE_ARGS )
-{
-       drm_device_t *dev = (drm_device_t *) arg;
-       drm_radeon_private_t *dev_priv = 
-          (drm_radeon_private_t *)dev->dev_private;
+               wakeup(&dev->vbl_queue);
+#endif
+       }
 
+       /* VBLANK interrupt */
+       if (stat & RADEON_CRTC_VBLANK_STAT) {
+               ack |= RADEON_CRTC_VBLANK_STAT_ACK;
+               atomic_inc(&dev->vbl_received);
 #ifdef __linux__
-       wake_up_interruptible(&dev_priv->irq_queue); 
-#endif /* __linux__ */
+               wake_up_interruptible(&dev->vbl_queue);
+#endif
 #ifdef __FreeBSD__
-       wakeup( &dev_priv->irq_queue );
+               wakeup(&dev->vbl_queue);
 #endif
-}
+       }
 
+       if (ack)
+               RADEON_WRITE(RADEON_GEN_INT_STATUS, ack);
+}
 
 int radeon_emit_irq(drm_device_t *dev)
 {
        drm_radeon_private_t *dev_priv = dev->dev_private;
        RING_LOCALS;
 
-       atomic_inc(&dev_priv->irq_emitted);
+       atomic_inc(&dev_priv->swi_emitted);
 
        BEGIN_RING(2); 
        OUT_RING( CP_PACKET0( RADEON_GEN_INT_STATUS, 0 ) );
@@ -105,11 +106,11 @@ int radeon_emit_irq(drm_device_t *dev)
        ADVANCE_RING(); 
        COMMIT_RING();
 
-       return atomic_read(&dev_priv->irq_emitted);
+       return atomic_read(&dev_priv->swi_emitted);
 }
 
 
-int radeon_wait_irq(drm_device_t *dev, int irq_nr)
+int radeon_wait_irq(drm_device_t *dev, int swi_nr)
 {
        drm_radeon_private_t *dev_priv = 
           (drm_radeon_private_t *)dev->dev_private;
@@ -119,17 +120,17 @@ int radeon_wait_irq(drm_device_t *dev, int irq_nr)
 #endif /* __linux__ */
        int ret = 0;
 
-       if (atomic_read(&dev_priv->irq_received) >= irq_nr)  
+       if (atomic_read(&dev_priv->swi_received) >= swi_nr)  
                return 0; 
 
        dev_priv->stats.boxes |= RADEON_BOX_WAIT_IDLE;
 
 #ifdef __linux__
-       add_wait_queue(&dev_priv->irq_queue, &entry);
+       add_wait_queue(&dev_priv->swi_queue, &entry);
 
        for (;;) {
                current->state = TASK_INTERRUPTIBLE;
-               if (atomic_read(&dev_priv->irq_received) >= irq_nr) 
+               if (atomic_read(&dev_priv->swi_received) >= swi_nr) 
                   break;
                if((signed)(end - jiffies) <= 0) {
                        ret = -EBUSY;   /* Lockup?  Missed irq? */
@@ -143,12 +144,12 @@ int radeon_wait_irq(drm_device_t *dev, int irq_nr)
        }
 
        current->state = TASK_RUNNING;
-       remove_wait_queue(&dev_priv->irq_queue, &entry);
+       remove_wait_queue(&dev_priv->swi_queue, &entry);
        return ret;
 #endif /* __linux__ */
        
 #ifdef __FreeBSD__
-       ret = tsleep( &dev_priv->irq_queue, PZERO | PCATCH, \
+       ret = tsleep( &dev_priv->swi_queue, PZERO | PCATCH, \
                "rdnirq", 3*hz );
        if ( (ret == EWOULDBLOCK) || (ret == EINTR) )
                return DRM_ERR(EBUSY);
@@ -156,7 +157,6 @@ int radeon_wait_irq(drm_device_t *dev, int irq_nr)
 #endif /* __FreeBSD__ */
 }
 
-
 int radeon_emit_and_wait_irq(drm_device_t *dev)
 {
        return radeon_wait_irq( dev, radeon_emit_irq(dev) );
@@ -212,3 +212,41 @@ int radeon_irq_wait( DRM_IOCTL_ARGS )
        return radeon_wait_irq( dev, irqwait.irq_seq );
 }
 
+void DRM(driver_irq_preinstall)( drm_device_t *dev ) {
+       drm_radeon_private_t *dev_priv =
+               (drm_radeon_private_t *)dev->dev_private;
+       u32 tmp;
+
+       /* Disable *all* interrupts */
+       RADEON_WRITE( RADEON_GEN_INT_CNTL, 0 );
+
+       /* Clear bits if they're already high */
+       tmp = RADEON_READ( RADEON_GEN_INT_STATUS );
+       RADEON_WRITE( RADEON_GEN_INT_STATUS, tmp );
+}
+
+void DRM(driver_irq_postinstall)( drm_device_t *dev ) {
+       drm_radeon_private_t *dev_priv =
+               (drm_radeon_private_t *)dev->dev_private;
+
+       atomic_set(&dev_priv->swi_received, 0);
+       atomic_set(&dev_priv->swi_emitted, 0);
+#ifdef __linux__
+       init_waitqueue_head(&dev_priv->swi_queue);
+#endif
+
+       /* Turn on SW and VBL ints */
+       RADEON_WRITE( RADEON_GEN_INT_CNTL,
+                     RADEON_CRTC_VBLANK_MASK | 
+                     RADEON_SW_INT_ENABLE );
+}
+
+void DRM(driver_irq_uninstall)( drm_device_t *dev ) {
+       drm_radeon_private_t *dev_priv =
+               (drm_radeon_private_t *)dev->dev_private;
+       if ( dev_priv ) {
+               /* Disable *all* interrupts */
+               RADEON_WRITE( RADEON_GEN_INT_CNTL, 0 );
+       }
+}