2006-01-02 20:14:23 +11:00
/* i915_irq.c -- IRQ support for the I915 -*- linux-c -*-
2005-04-16 15:20:36 -07:00
*/
2006-01-02 20:14:23 +11:00
/*
2005-04-16 15:20:36 -07:00
* Copyright 2003 Tungsten Graphics , Inc . , Cedar Park , Texas .
* All Rights Reserved .
2005-06-23 22:46:46 +10:00
*
* Permission is hereby granted , free of charge , to any person obtaining a
* copy of this software and associated documentation files ( the
* " Software " ) , to deal in the Software without restriction , including
* without limitation the rights to use , copy , modify , merge , publish ,
* distribute , sub license , and / or sell copies of the Software , and to
* permit persons to whom the Software is furnished to do so , subject to
* the following conditions :
*
* The above copyright notice and this permission notice ( including the
* next paragraph ) shall be included in all copies or substantial portions
* of the Software .
*
* THE SOFTWARE IS PROVIDED " AS IS " , WITHOUT WARRANTY OF ANY KIND , EXPRESS
* OR IMPLIED , INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY , FITNESS FOR A PARTICULAR PURPOSE AND NON - INFRINGEMENT .
* IN NO EVENT SHALL TUNGSTEN GRAPHICS AND / OR ITS SUPPLIERS BE LIABLE FOR
* ANY CLAIM , DAMAGES OR OTHER LIABILITY , WHETHER IN AN ACTION OF CONTRACT ,
* TORT OR OTHERWISE , ARISING FROM , OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE .
*
2006-01-02 20:14:23 +11:00
*/
2005-04-16 15:20:36 -07:00
# include "drmP.h"
# include "drm.h"
# include "i915_drm.h"
# include "i915_drv.h"
2006-01-02 20:14:23 +11:00
# define USER_INT_FLAG (1<<1)
# define VSYNC_PIPEB_FLAG (1<<5)
# define VSYNC_PIPEA_FLAG (1<<7)
2005-04-16 15:20:36 -07:00
# define MAX_NOPID ((u32)~0)
irqreturn_t i915_driver_irq_handler ( DRM_IRQ_ARGS )
{
drm_device_t * dev = ( drm_device_t * ) arg ;
drm_i915_private_t * dev_priv = ( drm_i915_private_t * ) dev - > dev_private ;
u16 temp ;
temp = I915_READ16 ( I915REG_INT_IDENTITY_R ) ;
2006-06-24 17:07:34 +10:00
temp & = ( USER_INT_FLAG | VSYNC_PIPEA_FLAG | VSYNC_PIPEB_FLAG ) ;
2005-04-16 15:20:36 -07:00
DRM_DEBUG ( " %s flag=%08x \n " , __FUNCTION__ , temp ) ;
if ( temp = = 0 )
return IRQ_NONE ;
I915_WRITE16 ( I915REG_INT_IDENTITY_R , temp ) ;
2006-01-02 20:14:23 +11:00
2006-03-20 18:34:29 +11:00
dev_priv - > sarea_priv - > last_dispatch = READ_BREADCRUMB ( dev_priv ) ;
2006-01-02 20:14:23 +11:00
if ( temp & USER_INT_FLAG )
DRM_WAKEUP ( & dev_priv - > irq_queue ) ;
2006-06-24 17:07:34 +10:00
if ( temp & ( VSYNC_PIPEA_FLAG | VSYNC_PIPEB_FLAG ) ) {
2006-01-02 20:14:23 +11:00
atomic_inc ( & dev - > vbl_received ) ;
DRM_WAKEUP ( & dev - > vbl_queue ) ;
drm_vbl_send_signals ( dev ) ;
}
2005-04-16 15:20:36 -07:00
return IRQ_HANDLED ;
}
2005-07-07 21:03:38 +10:00
static int i915_emit_irq ( drm_device_t * dev )
2005-04-16 15:20:36 -07:00
{
drm_i915_private_t * dev_priv = dev - > dev_private ;
u32 ret ;
RING_LOCALS ;
i915_kernel_lost_context ( dev ) ;
DRM_DEBUG ( " %s \n " , __FUNCTION__ ) ;
ret = dev_priv - > counter ;
BEGIN_LP_RING ( 2 ) ;
OUT_RING ( 0 ) ;
OUT_RING ( GFX_OP_USER_INTERRUPT ) ;
ADVANCE_LP_RING ( ) ;
return ret ;
}
2005-07-07 21:03:38 +10:00
static int i915_wait_irq ( drm_device_t * dev , int irq_nr )
2005-04-16 15:20:36 -07:00
{
drm_i915_private_t * dev_priv = ( drm_i915_private_t * ) dev - > dev_private ;
int ret = 0 ;
DRM_DEBUG ( " %s irq_nr=%d breadcrumb=%d \n " , __FUNCTION__ , irq_nr ,
READ_BREADCRUMB ( dev_priv ) ) ;
if ( READ_BREADCRUMB ( dev_priv ) > = irq_nr )
return 0 ;
dev_priv - > sarea_priv - > perf_boxes | = I915_BOX_WAIT ;
DRM_WAIT_ON ( ret , dev_priv - > irq_queue , 3 * DRM_HZ ,
READ_BREADCRUMB ( dev_priv ) > = irq_nr ) ;
if ( ret = = DRM_ERR ( EBUSY ) ) {
DRM_ERROR ( " %s: EBUSY -- rec: %d emitted: %d \n " ,
__FUNCTION__ ,
READ_BREADCRUMB ( dev_priv ) , ( int ) dev_priv - > counter ) ;
}
dev_priv - > sarea_priv - > last_dispatch = READ_BREADCRUMB ( dev_priv ) ;
return ret ;
}
2006-01-02 20:14:23 +11:00
int i915_driver_vblank_wait ( drm_device_t * dev , unsigned int * sequence )
{
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 ( & dev - > vbl_received ) )
- * sequence ) < = ( 1 < < 23 ) ) ) ;
* sequence = cur_vblank ;
return ret ;
}
2005-04-16 15:20:36 -07:00
/* Needs the lock as it touches the ring.
*/
int i915_irq_emit ( DRM_IOCTL_ARGS )
{
DRM_DEVICE ;
drm_i915_private_t * dev_priv = dev - > dev_private ;
drm_i915_irq_emit_t emit ;
int result ;
LOCK_TEST_WITH_RETURN ( dev , filp ) ;
if ( ! dev_priv ) {
DRM_ERROR ( " %s called with no initialization \n " , __FUNCTION__ ) ;
return DRM_ERR ( EINVAL ) ;
}
DRM_COPY_FROM_USER_IOCTL ( emit , ( drm_i915_irq_emit_t __user * ) data ,
sizeof ( emit ) ) ;
result = i915_emit_irq ( dev ) ;
if ( DRM_COPY_TO_USER ( emit . irq_seq , & result , sizeof ( int ) ) ) {
DRM_ERROR ( " copy_to_user \n " ) ;
return DRM_ERR ( EFAULT ) ;
}
return 0 ;
}
/* Doesn't need the hardware lock.
*/
int i915_irq_wait ( DRM_IOCTL_ARGS )
{
DRM_DEVICE ;
drm_i915_private_t * dev_priv = dev - > dev_private ;
drm_i915_irq_wait_t irqwait ;
if ( ! dev_priv ) {
DRM_ERROR ( " %s called with no initialization \n " , __FUNCTION__ ) ;
return DRM_ERR ( EINVAL ) ;
}
DRM_COPY_FROM_USER_IOCTL ( irqwait , ( drm_i915_irq_wait_t __user * ) data ,
sizeof ( irqwait ) ) ;
return i915_wait_irq ( dev , irqwait . irq_seq ) ;
}
2006-06-24 17:07:34 +10:00
static int i915_enable_interrupt ( drm_device_t * dev )
{
drm_i915_private_t * dev_priv = ( drm_i915_private_t * ) dev - > dev_private ;
u16 flag ;
flag = 0 ;
if ( dev_priv - > vblank_pipe & DRM_I915_VBLANK_PIPE_A )
flag | = VSYNC_PIPEA_FLAG ;
if ( dev_priv - > vblank_pipe & DRM_I915_VBLANK_PIPE_B )
flag | = VSYNC_PIPEB_FLAG ;
if ( dev_priv - > vblank_pipe & ~ ( DRM_I915_VBLANK_PIPE_A | DRM_I915_VBLANK_PIPE_B ) ) {
DRM_ERROR ( " %s called with invalid pipe 0x%x \n " ,
__FUNCTION__ , dev_priv - > vblank_pipe ) ;
return DRM_ERR ( EINVAL ) ;
}
I915_WRITE16 ( I915REG_INT_ENABLE_R , USER_INT_FLAG | flag ) ;
return 0 ;
}
/* Set the vblank monitor pipe
*/
int i915_vblank_pipe_set ( DRM_IOCTL_ARGS )
{
DRM_DEVICE ;
drm_i915_private_t * dev_priv = dev - > dev_private ;
drm_i915_vblank_pipe_t pipe ;
if ( ! dev_priv ) {
DRM_ERROR ( " %s called with no initialization \n " , __FUNCTION__ ) ;
return DRM_ERR ( EINVAL ) ;
}
DRM_COPY_FROM_USER_IOCTL ( pipe , ( drm_i915_vblank_pipe_t __user * ) data ,
sizeof ( pipe ) ) ;
dev_priv - > vblank_pipe = pipe . pipe ;
return i915_enable_interrupt ( dev ) ;
}
int i915_vblank_pipe_get ( DRM_IOCTL_ARGS )
{
DRM_DEVICE ;
drm_i915_private_t * dev_priv = dev - > dev_private ;
drm_i915_vblank_pipe_t pipe ;
u16 flag ;
if ( ! dev_priv ) {
DRM_ERROR ( " %s called with no initialization \n " , __FUNCTION__ ) ;
return DRM_ERR ( EINVAL ) ;
}
flag = I915_READ ( I915REG_INT_ENABLE_R ) ;
pipe . pipe = 0 ;
if ( flag & VSYNC_PIPEA_FLAG )
pipe . pipe | = DRM_I915_VBLANK_PIPE_A ;
if ( flag & VSYNC_PIPEB_FLAG )
pipe . pipe | = DRM_I915_VBLANK_PIPE_B ;
DRM_COPY_TO_USER_IOCTL ( ( drm_i915_vblank_pipe_t __user * ) data , pipe ,
sizeof ( pipe ) ) ;
return 0 ;
}
2005-04-16 15:20:36 -07:00
/* drm_dma.h hooks
*/
void i915_driver_irq_preinstall ( drm_device_t * dev )
{
drm_i915_private_t * dev_priv = ( drm_i915_private_t * ) dev - > dev_private ;
I915_WRITE16 ( I915REG_HWSTAM , 0xfffe ) ;
I915_WRITE16 ( I915REG_INT_MASK_R , 0x0 ) ;
I915_WRITE16 ( I915REG_INT_ENABLE_R , 0x0 ) ;
}
void i915_driver_irq_postinstall ( drm_device_t * dev )
{
drm_i915_private_t * dev_priv = ( drm_i915_private_t * ) dev - > dev_private ;
2006-06-24 17:07:34 +10:00
i915_enable_interrupt ( dev ) ;
2005-04-16 15:20:36 -07:00
DRM_INIT_WAITQUEUE ( & dev_priv - > irq_queue ) ;
}
void i915_driver_irq_uninstall ( drm_device_t * dev )
{
drm_i915_private_t * dev_priv = ( drm_i915_private_t * ) dev - > dev_private ;
2006-02-18 15:17:04 +11:00
u16 temp ;
2005-04-16 15:20:36 -07:00
if ( ! dev_priv )
return ;
I915_WRITE16 ( I915REG_HWSTAM , 0xffff ) ;
I915_WRITE16 ( I915REG_INT_MASK_R , 0xffff ) ;
I915_WRITE16 ( I915REG_INT_ENABLE_R , 0x0 ) ;
2006-02-18 15:17:04 +11:00
temp = I915_READ16 ( I915REG_INT_IDENTITY_R ) ;
I915_WRITE16 ( I915REG_INT_IDENTITY_R , temp ) ;
2005-04-16 15:20:36 -07:00
}