diff options
-rw-r--r-- | linux-core/drmP.h | 15 | ||||
-rw-r--r-- | linux-core/drm_dma.c | 82 | ||||
-rw-r--r-- | linux/drm.h | 21 | ||||
-rw-r--r-- | linux/drmP.h | 15 | ||||
-rw-r--r-- | linux/drm_dma.h | 82 | ||||
-rw-r--r-- | shared-core/drm.h | 21 | ||||
-rw-r--r-- | shared-core/mga_irq.c | 4 | ||||
-rw-r--r-- | shared-core/r128_irq.c | 4 | ||||
-rw-r--r-- | shared-core/radeon_irq.c | 8 | ||||
-rw-r--r-- | shared/drm.h | 21 | ||||
-rw-r--r-- | shared/mga_irq.c | 4 | ||||
-rw-r--r-- | shared/r128_irq.c | 4 | ||||
-rw-r--r-- | shared/radeon_irq.c | 8 |
13 files changed, 255 insertions, 34 deletions
diff --git a/linux-core/drmP.h b/linux-core/drmP.h index 6f6f91c4..181f61f1 100644 --- a/linux-core/drmP.h +++ b/linux-core/drmP.h @@ -518,6 +518,17 @@ typedef struct drm_map_list { drm_map_t *map; } drm_map_list_t; +#if __HAVE_VBL_IRQ + +typedef struct drm_vbl_sig { + struct list_head head; + unsigned int sequence; + struct siginfo info; + struct task_struct *task; +} drm_vbl_sig_t; + +#endif + typedef struct drm_device { const char *name; /* Simple driver name */ char *unique; /* Unique identifier: e.g., busid */ @@ -580,6 +591,9 @@ typedef struct drm_device { #if __HAVE_VBL_IRQ wait_queue_head_t vbl_queue; atomic_t vbl_received; + struct tq_struct vbl_tq; + struct semaphore vbl_sem; + drm_vbl_sig_t vbl_sigs; #endif cycles_t ctx_start; cycles_t lck_start; @@ -820,6 +834,7 @@ extern void DRM(driver_irq_uninstall)( drm_device_t *dev ); 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); +extern void DRM(vbl_immediate_bh)( void *arg ); #endif #if __HAVE_DMA_IRQ_BH extern void DRM(dma_immediate_bh)( void *dev ); diff --git a/linux-core/drm_dma.c b/linux-core/drm_dma.c index dc041592..01121ce9 100644 --- a/linux-core/drm_dma.c +++ b/linux-core/drm_dma.c @@ -540,6 +540,17 @@ int DRM(irq_install)( drm_device_t *dev, int irq ) #if __HAVE_VBL_IRQ init_waitqueue_head(&dev->vbl_queue); + + sema_init( &dev->vbl_sem, 0 ); + + INIT_LIST_HEAD( &dev->vbl_sigs.head ); + + up( &dev->vbl_sem ); + + INIT_LIST_HEAD( &dev->vbl_tq.list ); + dev->vbl_tq.sync = 0; + dev->vbl_tq.routine = DRM(vbl_immediate_bh); + dev->vbl_tq.data = dev; #endif /* Before installing handler */ @@ -610,7 +621,8 @@ int DRM(wait_vblank)( DRM_IOCTL_ARGS ) drm_device_t *dev = priv->dev; drm_wait_vblank_t vblwait; struct timeval now; - int ret; + int ret = 0; + unsigned int flags; if (!dev->irq) return -EINVAL; @@ -618,15 +630,44 @@ int DRM(wait_vblank)( DRM_IOCTL_ARGS ) 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 ); + switch ( vblwait.request.type & ~_DRM_VBLANK_FLAGS_MASK ) { + case _DRM_VBLANK_RELATIVE: + vblwait.request.sequence += atomic_read( &dev->vbl_received ); + case _DRM_VBLANK_ABSOLUTE: + break; + default: + return -EINVAL; } - ret = DRM(vblank_wait)( dev, &vblwait.sequence ); + flags = vblwait.request.type & _DRM_VBLANK_FLAGS_MASK; + + if ( flags & _DRM_VBLANK_SIGNAL ) { + drm_vbl_sig_t *vbl_sig = DRM_MALLOC( sizeof( drm_vbl_sig_t ) ); + + if ( !vbl_sig ) + return -ENOMEM; - do_gettimeofday( &now ); - vblwait.tval_sec = now.tv_sec; - vblwait.tval_usec = now.tv_usec; + memset( (void *)vbl_sig, 0, sizeof(*vbl_sig) ); + + vbl_sig->sequence = vblwait.request.sequence; + vbl_sig->info.si_signo = vblwait.request.signal; + vbl_sig->task = current; + + vblwait.reply.sequence = atomic_read( &dev->vbl_received ); + + /* Hook signal entry into list */ + down( &dev->vbl_sem ); + + list_add_tail( (struct list_head *) vbl_sig, &dev->vbl_sigs.head ); + + up( &dev->vbl_sem ); + } else { + ret = DRM(vblank_wait)( dev, &vblwait.request.sequence ); + + do_gettimeofday( &now ); + vblwait.reply.tval_sec = now.tv_sec; + vblwait.reply.tval_usec = now.tv_usec; + } DRM_COPY_TO_USER_IOCTL( (drm_wait_vblank_t *)data, vblwait, sizeof(vblwait) ); @@ -634,6 +675,33 @@ int DRM(wait_vblank)( DRM_IOCTL_ARGS ) return ret; } +void DRM(vbl_immediate_bh)( void *arg ) +{ + drm_device_t *dev = (drm_device_t *) arg; + struct list_head *entry, *tmp; + drm_vbl_sig_t *vbl_sig; + unsigned int vbl_seq = atomic_read( &dev->vbl_received ); + + down( &dev->vbl_sem ); + + list_for_each_safe( entry, tmp, &dev->vbl_sigs.head ) { + + vbl_sig = (drm_vbl_sig_t *) entry; + + if ( ( vbl_seq + ~vbl_sig->sequence + 1 ) <= (1<<23) ) { + + vbl_sig->info.si_code = atomic_read( &dev->vbl_received ); + send_sig_info( vbl_sig->info.si_signo, &vbl_sig->info, vbl_sig->task ); + + list_del( entry ); + + DRM_FREE( entry ); + } + } + + up( &dev->vbl_sem ); +} + #endif /* __HAVE_VBL_IRQ */ #else diff --git a/linux/drm.h b/linux/drm.h index f26d4442..d3c9f158 100644 --- a/linux/drm.h +++ b/linux/drm.h @@ -346,17 +346,30 @@ typedef struct drm_irq_busid { } 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_ABSOLUTE = 0x0, /* Wait for specific vblank sequence number */ + _DRM_VBLANK_RELATIVE = 0x1, /* Wait for given number of vblanks */ + _DRM_VBLANK_SIGNAL = 0x80000000 /* Send signal instead of blocking */ } drm_vblank_seq_type_t; -typedef struct drm_radeon_vbl_wait { +#define _DRM_VBLANK_FLAGS_MASK _DRM_VBLANK_SIGNAL + +struct drm_wait_vblank_request { + drm_vblank_seq_type_t type; + unsigned int sequence; + unsigned long signal; +}; + +struct drm_wait_vblank_reply { drm_vblank_seq_type_t type; unsigned int sequence; long tval_sec; long tval_usec; -} drm_wait_vblank_t; +}; +typedef union drm_wait_vblank { + struct drm_wait_vblank_request request; + struct drm_wait_vblank_reply reply; +} drm_wait_vblank_t; typedef struct drm_agp_mode { unsigned long mode; diff --git a/linux/drmP.h b/linux/drmP.h index 6f6f91c4..181f61f1 100644 --- a/linux/drmP.h +++ b/linux/drmP.h @@ -518,6 +518,17 @@ typedef struct drm_map_list { drm_map_t *map; } drm_map_list_t; +#if __HAVE_VBL_IRQ + +typedef struct drm_vbl_sig { + struct list_head head; + unsigned int sequence; + struct siginfo info; + struct task_struct *task; +} drm_vbl_sig_t; + +#endif + typedef struct drm_device { const char *name; /* Simple driver name */ char *unique; /* Unique identifier: e.g., busid */ @@ -580,6 +591,9 @@ typedef struct drm_device { #if __HAVE_VBL_IRQ wait_queue_head_t vbl_queue; atomic_t vbl_received; + struct tq_struct vbl_tq; + struct semaphore vbl_sem; + drm_vbl_sig_t vbl_sigs; #endif cycles_t ctx_start; cycles_t lck_start; @@ -820,6 +834,7 @@ extern void DRM(driver_irq_uninstall)( drm_device_t *dev ); 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); +extern void DRM(vbl_immediate_bh)( void *arg ); #endif #if __HAVE_DMA_IRQ_BH extern void DRM(dma_immediate_bh)( void *dev ); diff --git a/linux/drm_dma.h b/linux/drm_dma.h index dc041592..01121ce9 100644 --- a/linux/drm_dma.h +++ b/linux/drm_dma.h @@ -540,6 +540,17 @@ int DRM(irq_install)( drm_device_t *dev, int irq ) #if __HAVE_VBL_IRQ init_waitqueue_head(&dev->vbl_queue); + + sema_init( &dev->vbl_sem, 0 ); + + INIT_LIST_HEAD( &dev->vbl_sigs.head ); + + up( &dev->vbl_sem ); + + INIT_LIST_HEAD( &dev->vbl_tq.list ); + dev->vbl_tq.sync = 0; + dev->vbl_tq.routine = DRM(vbl_immediate_bh); + dev->vbl_tq.data = dev; #endif /* Before installing handler */ @@ -610,7 +621,8 @@ int DRM(wait_vblank)( DRM_IOCTL_ARGS ) drm_device_t *dev = priv->dev; drm_wait_vblank_t vblwait; struct timeval now; - int ret; + int ret = 0; + unsigned int flags; if (!dev->irq) return -EINVAL; @@ -618,15 +630,44 @@ int DRM(wait_vblank)( DRM_IOCTL_ARGS ) 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 ); + switch ( vblwait.request.type & ~_DRM_VBLANK_FLAGS_MASK ) { + case _DRM_VBLANK_RELATIVE: + vblwait.request.sequence += atomic_read( &dev->vbl_received ); + case _DRM_VBLANK_ABSOLUTE: + break; + default: + return -EINVAL; } - ret = DRM(vblank_wait)( dev, &vblwait.sequence ); + flags = vblwait.request.type & _DRM_VBLANK_FLAGS_MASK; + + if ( flags & _DRM_VBLANK_SIGNAL ) { + drm_vbl_sig_t *vbl_sig = DRM_MALLOC( sizeof( drm_vbl_sig_t ) ); + + if ( !vbl_sig ) + return -ENOMEM; - do_gettimeofday( &now ); - vblwait.tval_sec = now.tv_sec; - vblwait.tval_usec = now.tv_usec; + memset( (void *)vbl_sig, 0, sizeof(*vbl_sig) ); + + vbl_sig->sequence = vblwait.request.sequence; + vbl_sig->info.si_signo = vblwait.request.signal; + vbl_sig->task = current; + + vblwait.reply.sequence = atomic_read( &dev->vbl_received ); + + /* Hook signal entry into list */ + down( &dev->vbl_sem ); + + list_add_tail( (struct list_head *) vbl_sig, &dev->vbl_sigs.head ); + + up( &dev->vbl_sem ); + } else { + ret = DRM(vblank_wait)( dev, &vblwait.request.sequence ); + + do_gettimeofday( &now ); + vblwait.reply.tval_sec = now.tv_sec; + vblwait.reply.tval_usec = now.tv_usec; + } DRM_COPY_TO_USER_IOCTL( (drm_wait_vblank_t *)data, vblwait, sizeof(vblwait) ); @@ -634,6 +675,33 @@ int DRM(wait_vblank)( DRM_IOCTL_ARGS ) return ret; } +void DRM(vbl_immediate_bh)( void *arg ) +{ + drm_device_t *dev = (drm_device_t *) arg; + struct list_head *entry, *tmp; + drm_vbl_sig_t *vbl_sig; + unsigned int vbl_seq = atomic_read( &dev->vbl_received ); + + down( &dev->vbl_sem ); + + list_for_each_safe( entry, tmp, &dev->vbl_sigs.head ) { + + vbl_sig = (drm_vbl_sig_t *) entry; + + if ( ( vbl_seq + ~vbl_sig->sequence + 1 ) <= (1<<23) ) { + + vbl_sig->info.si_code = atomic_read( &dev->vbl_received ); + send_sig_info( vbl_sig->info.si_signo, &vbl_sig->info, vbl_sig->task ); + + list_del( entry ); + + DRM_FREE( entry ); + } + } + + up( &dev->vbl_sem ); +} + #endif /* __HAVE_VBL_IRQ */ #else diff --git a/shared-core/drm.h b/shared-core/drm.h index f26d4442..d3c9f158 100644 --- a/shared-core/drm.h +++ b/shared-core/drm.h @@ -346,17 +346,30 @@ typedef struct drm_irq_busid { } 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_ABSOLUTE = 0x0, /* Wait for specific vblank sequence number */ + _DRM_VBLANK_RELATIVE = 0x1, /* Wait for given number of vblanks */ + _DRM_VBLANK_SIGNAL = 0x80000000 /* Send signal instead of blocking */ } drm_vblank_seq_type_t; -typedef struct drm_radeon_vbl_wait { +#define _DRM_VBLANK_FLAGS_MASK _DRM_VBLANK_SIGNAL + +struct drm_wait_vblank_request { + drm_vblank_seq_type_t type; + unsigned int sequence; + unsigned long signal; +}; + +struct drm_wait_vblank_reply { drm_vblank_seq_type_t type; unsigned int sequence; long tval_sec; long tval_usec; -} drm_wait_vblank_t; +}; +typedef union drm_wait_vblank { + struct drm_wait_vblank_request request; + struct drm_wait_vblank_reply reply; +} drm_wait_vblank_t; typedef struct drm_agp_mode { unsigned long mode; diff --git a/shared-core/mga_irq.c b/shared-core/mga_irq.c index 568d193f..30ee7834 100644 --- a/shared-core/mga_irq.c +++ b/shared-core/mga_irq.c @@ -50,6 +50,10 @@ void mga_dma_service( DRM_IRQ_ARGS ) MGA_WRITE( MGA_ICLEAR, MGA_VLINEICLR ); atomic_inc(&dev->vbl_received); DRM_WAKEUP(&dev->vbl_queue); + + /* kick off bottom half for signals */ + queue_task(&dev->vbl_tq, &tq_immediate); + mark_bh(IMMEDIATE_BH); } } diff --git a/shared-core/r128_irq.c b/shared-core/r128_irq.c index a29a81b5..722f2362 100644 --- a/shared-core/r128_irq.c +++ b/shared-core/r128_irq.c @@ -50,6 +50,10 @@ void r128_dma_service( DRM_IRQ_ARGS ) R128_WRITE( R128_GEN_INT_STATUS, R128_CRTC_VBLANK_INT_AK ); atomic_inc(&dev->vbl_received); DRM_WAKEUP(&dev->vbl_queue); + + /* kick off bottom half for signals */ + queue_task(&dev->vbl_tq, &tq_immediate); + mark_bh(IMMEDIATE_BH); } } diff --git a/shared-core/radeon_irq.c b/shared-core/radeon_irq.c index 54702bee..b8f33f95 100644 --- a/shared-core/radeon_irq.c +++ b/shared-core/radeon_irq.c @@ -70,13 +70,15 @@ void DRM(dma_service)( DRM_IRQ_ARGS ) DRM_WAKEUP( &dev_priv->swi_queue ); } -#if __HAVE_VBL_IRQ /* VBLANK interrupt */ if (stat & RADEON_CRTC_VBLANK_STAT) { atomic_inc(&dev->vbl_received); DRM_WAKEUP(&dev->vbl_queue); + + /* kick off bottom half for signals */ + queue_task(&dev->vbl_tq, &tq_immediate); + mark_bh(IMMEDIATE_BH); } -#endif /* Acknowledge all the bits in GEN_INT_STATUS -- seem to get * more than we asked for... @@ -138,7 +140,6 @@ int radeon_emit_and_wait_irq(drm_device_t *dev) } -#if __HAVE_VBL_IRQ int DRM(vblank_wait)(drm_device_t *dev, unsigned int *sequence) { drm_radeon_private_t *dev_priv = @@ -167,7 +168,6 @@ int DRM(vblank_wait)(drm_device_t *dev, unsigned int *sequence) return ret; } -#endif /* Needs the lock as it touches the ring. diff --git a/shared/drm.h b/shared/drm.h index f26d4442..d3c9f158 100644 --- a/shared/drm.h +++ b/shared/drm.h @@ -346,17 +346,30 @@ typedef struct drm_irq_busid { } 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_ABSOLUTE = 0x0, /* Wait for specific vblank sequence number */ + _DRM_VBLANK_RELATIVE = 0x1, /* Wait for given number of vblanks */ + _DRM_VBLANK_SIGNAL = 0x80000000 /* Send signal instead of blocking */ } drm_vblank_seq_type_t; -typedef struct drm_radeon_vbl_wait { +#define _DRM_VBLANK_FLAGS_MASK _DRM_VBLANK_SIGNAL + +struct drm_wait_vblank_request { + drm_vblank_seq_type_t type; + unsigned int sequence; + unsigned long signal; +}; + +struct drm_wait_vblank_reply { drm_vblank_seq_type_t type; unsigned int sequence; long tval_sec; long tval_usec; -} drm_wait_vblank_t; +}; +typedef union drm_wait_vblank { + struct drm_wait_vblank_request request; + struct drm_wait_vblank_reply reply; +} drm_wait_vblank_t; typedef struct drm_agp_mode { unsigned long mode; diff --git a/shared/mga_irq.c b/shared/mga_irq.c index 568d193f..30ee7834 100644 --- a/shared/mga_irq.c +++ b/shared/mga_irq.c @@ -50,6 +50,10 @@ void mga_dma_service( DRM_IRQ_ARGS ) MGA_WRITE( MGA_ICLEAR, MGA_VLINEICLR ); atomic_inc(&dev->vbl_received); DRM_WAKEUP(&dev->vbl_queue); + + /* kick off bottom half for signals */ + queue_task(&dev->vbl_tq, &tq_immediate); + mark_bh(IMMEDIATE_BH); } } diff --git a/shared/r128_irq.c b/shared/r128_irq.c index a29a81b5..722f2362 100644 --- a/shared/r128_irq.c +++ b/shared/r128_irq.c @@ -50,6 +50,10 @@ void r128_dma_service( DRM_IRQ_ARGS ) R128_WRITE( R128_GEN_INT_STATUS, R128_CRTC_VBLANK_INT_AK ); atomic_inc(&dev->vbl_received); DRM_WAKEUP(&dev->vbl_queue); + + /* kick off bottom half for signals */ + queue_task(&dev->vbl_tq, &tq_immediate); + mark_bh(IMMEDIATE_BH); } } diff --git a/shared/radeon_irq.c b/shared/radeon_irq.c index 54702bee..b8f33f95 100644 --- a/shared/radeon_irq.c +++ b/shared/radeon_irq.c @@ -70,13 +70,15 @@ void DRM(dma_service)( DRM_IRQ_ARGS ) DRM_WAKEUP( &dev_priv->swi_queue ); } -#if __HAVE_VBL_IRQ /* VBLANK interrupt */ if (stat & RADEON_CRTC_VBLANK_STAT) { atomic_inc(&dev->vbl_received); DRM_WAKEUP(&dev->vbl_queue); + + /* kick off bottom half for signals */ + queue_task(&dev->vbl_tq, &tq_immediate); + mark_bh(IMMEDIATE_BH); } -#endif /* Acknowledge all the bits in GEN_INT_STATUS -- seem to get * more than we asked for... @@ -138,7 +140,6 @@ int radeon_emit_and_wait_irq(drm_device_t *dev) } -#if __HAVE_VBL_IRQ int DRM(vblank_wait)(drm_device_t *dev, unsigned int *sequence) { drm_radeon_private_t *dev_priv = @@ -167,7 +168,6 @@ int DRM(vblank_wait)(drm_device_t *dev, unsigned int *sequence) return ret; } -#endif /* Needs the lock as it touches the ring. |