From 55acd0d5a64a2ee6b0cecc75872fbf8c4bb42a0c Mon Sep 17 00:00:00 2001 From: Michel Daenzer Date: Wed, 25 Sep 2002 17:18:19 +0000 Subject: common ioctl to wait for vertical blank IRQs --- libdrm/xf86drm.c | 11 +++++ linux-core/drmP.h | 12 ++++++ linux-core/drm_dma.c | 82 +++++++++++++++++++++++++++++++++++-- linux-core/drm_drv.c | 4 ++ linux/drm.h | 15 +++++++ linux/drmP.h | 12 ++++++ linux/drm_dma.h | 82 +++++++++++++++++++++++++++++++++++-- linux/drm_drv.h | 4 ++ shared-core/drm.h | 15 +++++++ shared-core/radeon_drv.h | 15 +++++-- shared-core/radeon_irq.c | 102 ++++++++++++++++++++++++++++++++--------------- shared/drm.h | 15 +++++++ shared/radeon.h | 45 +-------------------- shared/radeon_drv.h | 15 +++++-- shared/radeon_irq.c | 102 ++++++++++++++++++++++++++++++++--------------- 15 files changed, 409 insertions(+), 122 deletions(-) diff --git a/libdrm/xf86drm.c b/libdrm/xf86drm.c index d2ce5e1e..8d3a20d1 100644 --- a/libdrm/xf86drm.c +++ b/libdrm/xf86drm.c @@ -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) { diff --git a/linux-core/drmP.h b/linux-core/drmP.h index 7d82fce3..6f6f91c4 100644 --- a/linux-core/drmP.h +++ b/linux-core/drmP.h @@ -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 diff --git a/linux-core/drm_dma.c b/linux-core/drm_dma.c index dce376f6..2938c77a 100644 --- a/linux-core/drm_dma.c +++ b/linux-core/drm_dma.c @@ -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, diff --git a/linux-core/drm_drv.c b/linux-core/drm_drv.c index 7e2cfd8c..3ebe7811 100644 --- a/linux-core/drm_drv.c +++ b/linux-core/drm_drv.c @@ -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 }; diff --git a/linux/drm.h b/linux/drm.h index 376568e0..f26d4442 100644 --- a/linux/drm.h +++ b/linux/drm.h @@ -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 diff --git a/linux/drmP.h b/linux/drmP.h index 7d82fce3..6f6f91c4 100644 --- a/linux/drmP.h +++ b/linux/drmP.h @@ -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 diff --git a/linux/drm_dma.h b/linux/drm_dma.h index dce376f6..2938c77a 100644 --- a/linux/drm_dma.h +++ b/linux/drm_dma.h @@ -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, diff --git a/linux/drm_drv.h b/linux/drm_drv.h index 7e2cfd8c..3ebe7811 100644 --- a/linux/drm_drv.h +++ b/linux/drm_drv.h @@ -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 }; diff --git a/shared-core/drm.h b/shared-core/drm.h index 376568e0..f26d4442 100644 --- a/shared-core/drm.h +++ b/shared-core/drm.h @@ -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 diff --git a/shared-core/radeon_drv.h b/shared-core/radeon_drv.h index efe3020c..5c43ebc6 100644 --- a/shared-core/radeon_drv.h +++ b/shared-core/radeon_drv.h @@ -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) diff --git a/shared-core/radeon_irq.c b/shared-core/radeon_irq.c index 11609393..7b170389 100644 --- a/shared-core/radeon_irq.c +++ b/shared-core/radeon_irq.c @@ -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 ); + } +} diff --git a/shared/drm.h b/shared/drm.h index 376568e0..f26d4442 100644 --- a/shared/drm.h +++ b/shared/drm.h @@ -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 diff --git a/shared/radeon.h b/shared/radeon.h index 4cb00986..aa613c40 100644 --- a/shared/radeon.h +++ b/shared/radeon.h @@ -132,53 +132,10 @@ */ #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 diff --git a/shared/radeon_drv.h b/shared/radeon_drv.h index efe3020c..5c43ebc6 100644 --- a/shared/radeon_drv.h +++ b/shared/radeon_drv.h @@ -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) diff --git a/shared/radeon_irq.c b/shared/radeon_irq.c index 11609393..7b170389 100644 --- a/shared/radeon_irq.c +++ b/shared/radeon_irq.c @@ -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 ); + } +} -- cgit v1.2.3