2005-04-17 02:20:36 +04:00
/* i830_dma.c -- DMA support for the I830 -*- linux-c -*-
*
* Copyright 2002 Tungsten Graphics , Inc .
* All Rights Reserved .
*
* 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 , sublicense ,
* 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 :
2005-09-25 08:28:13 +04:00
*
2005-04-17 02:20:36 +04:00
* The above copyright notice and this permission notice ( including the next
* paragraph ) shall be included in all copies or substantial portions of the
* Software .
2005-09-25 08:28:13 +04:00
*
2005-04-17 02:20:36 +04:00
* 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 NONINFRINGEMENT . 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 .
*
* Authors : Keith Whitwell < keith @ tungstengraphics . com >
*
*/
# include "drmP.h"
# include "drm.h"
# include "i830_drm.h"
# include "i830_drv.h"
# include <linux/interrupt.h> /* For task queue support */
# include <linux/delay.h>
2005-09-25 08:28:13 +04:00
irqreturn_t i830_driver_irq_handler ( DRM_IRQ_ARGS )
2005-04-17 02:20:36 +04:00
{
2005-09-25 08:28:13 +04:00
drm_device_t * dev = ( drm_device_t * ) arg ;
drm_i830_private_t * dev_priv = ( drm_i830_private_t * ) dev - > dev_private ;
u16 temp ;
2005-04-17 02:20:36 +04:00
2005-09-25 08:28:13 +04:00
temp = I830_READ16 ( I830REG_INT_IDENTITY_R ) ;
2005-04-17 02:20:36 +04:00
DRM_DEBUG ( " %x \n " , temp ) ;
2005-09-25 08:28:13 +04:00
if ( ! ( temp & 2 ) )
2005-04-17 02:20:36 +04:00
return IRQ_NONE ;
2005-09-25 08:28:13 +04:00
I830_WRITE16 ( I830REG_INT_IDENTITY_R , temp ) ;
2005-04-17 02:20:36 +04:00
atomic_inc ( & dev_priv - > irq_received ) ;
2005-09-25 08:28:13 +04:00
wake_up_interruptible ( & dev_priv - > irq_queue ) ;
2005-04-17 02:20:36 +04:00
return IRQ_HANDLED ;
}
2005-09-25 08:28:13 +04:00
static int i830_emit_irq ( drm_device_t * dev )
2005-04-17 02:20:36 +04:00
{
drm_i830_private_t * dev_priv = dev - > dev_private ;
RING_LOCALS ;
DRM_DEBUG ( " %s \n " , __FUNCTION__ ) ;
atomic_inc ( & dev_priv - > irq_emitted ) ;
2005-09-25 08:28:13 +04:00
BEGIN_LP_RING ( 2 ) ;
OUT_RING ( 0 ) ;
OUT_RING ( GFX_OP_USER_INTERRUPT ) ;
ADVANCE_LP_RING ( ) ;
2005-04-17 02:20:36 +04:00
return atomic_read ( & dev_priv - > irq_emitted ) ;
}
2005-09-25 08:28:13 +04:00
static int i830_wait_irq ( drm_device_t * dev , int irq_nr )
2005-04-17 02:20:36 +04:00
{
2005-09-25 08:28:13 +04:00
drm_i830_private_t * dev_priv = ( drm_i830_private_t * ) dev - > dev_private ;
2005-04-17 02:20:36 +04:00
DECLARE_WAITQUEUE ( entry , current ) ;
2005-09-25 08:28:13 +04:00
unsigned long end = jiffies + HZ * 3 ;
2005-04-17 02:20:36 +04:00
int ret = 0 ;
DRM_DEBUG ( " %s \n " , __FUNCTION__ ) ;
2005-09-25 08:28:13 +04:00
if ( atomic_read ( & dev_priv - > irq_received ) > = irq_nr )
return 0 ;
2005-04-17 02:20:36 +04:00
dev_priv - > sarea_priv - > perf_boxes | = I830_BOX_WAIT ;
add_wait_queue ( & dev_priv - > irq_queue , & entry ) ;
for ( ; ; ) {
__set_current_state ( TASK_INTERRUPTIBLE ) ;
2005-09-25 08:28:13 +04:00
if ( atomic_read ( & dev_priv - > irq_received ) > = irq_nr )
break ;
if ( ( signed ) ( end - jiffies ) < = 0 ) {
2005-04-17 02:20:36 +04:00
DRM_ERROR ( " timeout iir %x imr %x ier %x hwstam %x \n " ,
2005-09-25 08:28:13 +04:00
I830_READ16 ( I830REG_INT_IDENTITY_R ) ,
I830_READ16 ( I830REG_INT_MASK_R ) ,
I830_READ16 ( I830REG_INT_ENABLE_R ) ,
I830_READ16 ( I830REG_HWSTAM ) ) ;
2005-04-17 02:20:36 +04:00
2005-09-25 08:28:13 +04:00
ret = - EBUSY ; /* Lockup? Missed irq? */
2005-04-17 02:20:36 +04:00
break ;
}
2005-09-25 08:28:13 +04:00
schedule_timeout ( HZ * 3 ) ;
if ( signal_pending ( current ) ) {
ret = - EINTR ;
2005-04-17 02:20:36 +04:00
break ;
}
}
__set_current_state ( TASK_RUNNING ) ;
remove_wait_queue ( & dev_priv - > irq_queue , & entry ) ;
return ret ;
}
/* Needs the lock as it touches the ring.
*/
2005-09-25 08:28:13 +04:00
int i830_irq_emit ( struct inode * inode , struct file * filp , unsigned int cmd ,
unsigned long arg )
2005-04-17 02:20:36 +04:00
{
2005-09-25 08:28:13 +04:00
drm_file_t * priv = filp - > private_data ;
drm_device_t * dev = priv - > head - > dev ;
2005-04-17 02:20:36 +04:00
drm_i830_private_t * dev_priv = dev - > dev_private ;
drm_i830_irq_emit_t emit ;
int result ;
LOCK_TEST_WITH_RETURN ( dev , filp ) ;
2005-09-25 08:28:13 +04:00
if ( ! dev_priv ) {
DRM_ERROR ( " %s called with no initialization \n " , __FUNCTION__ ) ;
2005-04-17 02:20:36 +04:00
return - EINVAL ;
}
2005-09-25 08:28:13 +04:00
if ( copy_from_user
( & emit , ( drm_i830_irq_emit_t __user * ) arg , sizeof ( emit ) ) )
2005-04-17 02:20:36 +04:00
return - EFAULT ;
2005-09-25 08:28:13 +04:00
result = i830_emit_irq ( dev ) ;
2005-04-17 02:20:36 +04:00
2005-09-25 08:28:13 +04:00
if ( copy_to_user ( emit . irq_seq , & result , sizeof ( int ) ) ) {
DRM_ERROR ( " copy_to_user \n " ) ;
2005-04-17 02:20:36 +04:00
return - EFAULT ;
}
return 0 ;
}
/* Doesn't need the hardware lock.
*/
2005-09-25 08:28:13 +04:00
int i830_irq_wait ( struct inode * inode , struct file * filp , unsigned int cmd ,
unsigned long arg )
2005-04-17 02:20:36 +04:00
{
2005-09-25 08:28:13 +04:00
drm_file_t * priv = filp - > private_data ;
drm_device_t * dev = priv - > head - > dev ;
2005-04-17 02:20:36 +04:00
drm_i830_private_t * dev_priv = dev - > dev_private ;
drm_i830_irq_wait_t irqwait ;
2005-09-25 08:28:13 +04:00
if ( ! dev_priv ) {
DRM_ERROR ( " %s called with no initialization \n " , __FUNCTION__ ) ;
2005-04-17 02:20:36 +04:00
return - EINVAL ;
}
2005-09-25 08:28:13 +04:00
if ( copy_from_user ( & irqwait , ( drm_i830_irq_wait_t __user * ) arg ,
sizeof ( irqwait ) ) )
2005-04-17 02:20:36 +04:00
return - EFAULT ;
2005-09-25 08:28:13 +04:00
return i830_wait_irq ( dev , irqwait . irq_seq ) ;
2005-04-17 02:20:36 +04:00
}
/* drm_dma.h hooks
*/
2005-09-25 08:28:13 +04:00
void i830_driver_irq_preinstall ( drm_device_t * dev )
{
drm_i830_private_t * dev_priv = ( drm_i830_private_t * ) dev - > dev_private ;
2005-04-17 02:20:36 +04:00
2005-09-25 08:28:13 +04:00
I830_WRITE16 ( I830REG_HWSTAM , 0xffff ) ;
I830_WRITE16 ( I830REG_INT_MASK_R , 0x0 ) ;
I830_WRITE16 ( I830REG_INT_ENABLE_R , 0x0 ) ;
2005-04-17 02:20:36 +04:00
atomic_set ( & dev_priv - > irq_received , 0 ) ;
atomic_set ( & dev_priv - > irq_emitted , 0 ) ;
init_waitqueue_head ( & dev_priv - > irq_queue ) ;
}
2005-09-25 08:28:13 +04:00
void i830_driver_irq_postinstall ( drm_device_t * dev )
{
drm_i830_private_t * dev_priv = ( drm_i830_private_t * ) dev - > dev_private ;
2005-04-17 02:20:36 +04:00
2005-09-25 08:28:13 +04:00
I830_WRITE16 ( I830REG_INT_ENABLE_R , 0x2 ) ;
2005-04-17 02:20:36 +04:00
}
2005-09-25 08:28:13 +04:00
void i830_driver_irq_uninstall ( drm_device_t * dev )
{
drm_i830_private_t * dev_priv = ( drm_i830_private_t * ) dev - > dev_private ;
2005-04-17 02:20:36 +04:00
if ( ! dev_priv )
return ;
2005-09-25 08:28:13 +04:00
I830_WRITE16 ( I830REG_INT_MASK_R , 0xffff ) ;
I830_WRITE16 ( I830REG_INT_ENABLE_R , 0x0 ) ;
2005-04-17 02:20:36 +04:00
}