2006-12-19 17:49:08 +11:00
/* radeon_irq.c -- IRQ handling for radeon -*- linux-c -*- */
/*
2005-04-16 15:20:36 -07:00
* Copyright ( C ) The Weather Channel , Inc . 2002. All Rights Reserved .
2005-09-25 14:28:13 +10:00
*
2005-04-16 15:20:36 -07:00
* The Weather Channel ( TM ) funded Tungsten Graphics to develop the
* initial release of the Radeon 8500 driver under the XFree86 license .
* This notice must be preserved .
*
* 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 :
*
* 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 NONINFRINGEMENT . IN NO EVENT SHALL
* PRECISION INSIGHT 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 >
2008-09-30 12:14:26 -07:00
* Michel D <EFBFBD> zer < michel @ daenzer . net >
2005-04-16 15:20:36 -07:00
*/
# include "drmP.h"
# include "drm.h"
# include "radeon_drm.h"
# include "radeon_drv.h"
2008-09-30 12:14:26 -07:00
void radeon_irq_set_state ( struct drm_device * dev , u32 mask , int state )
2005-06-26 21:05:59 +10:00
{
2008-09-30 12:14:26 -07:00
drm_radeon_private_t * dev_priv = dev - > dev_private ;
if ( state )
dev_priv - > irq_enable_reg | = mask ;
else
dev_priv - > irq_enable_reg & = ~ mask ;
2008-12-22 17:11:02 +10:00
if ( dev - > irq_enabled )
2008-12-09 15:30:50 +10:00
RADEON_WRITE ( RADEON_GEN_INT_CNTL , dev_priv - > irq_enable_reg ) ;
2008-09-30 12:14:26 -07:00
}
static void r500_vbl_irq_set_state ( struct drm_device * dev , u32 mask , int state )
{
drm_radeon_private_t * dev_priv = dev - > dev_private ;
if ( state )
dev_priv - > r500_disp_irq_reg | = mask ;
else
dev_priv - > r500_disp_irq_reg & = ~ mask ;
2008-12-22 17:11:02 +10:00
if ( dev - > irq_enabled )
2008-12-09 15:30:50 +10:00
RADEON_WRITE ( R500_DxMODE_INT_MASK , dev_priv - > r500_disp_irq_reg ) ;
2008-09-30 12:14:26 -07:00
}
int radeon_enable_vblank ( struct drm_device * dev , int crtc )
{
drm_radeon_private_t * dev_priv = dev - > dev_private ;
2009-03-06 11:47:54 -05:00
if ( ( dev_priv - > flags & RADEON_FAMILY_MASK ) > = CHIP_RS600 ) {
2008-09-30 12:14:26 -07:00
switch ( crtc ) {
case 0 :
r500_vbl_irq_set_state ( dev , R500_D1MODE_INT_MASK , 1 ) ;
break ;
case 1 :
r500_vbl_irq_set_state ( dev , R500_D2MODE_INT_MASK , 1 ) ;
break ;
default :
DRM_ERROR ( " tried to enable vblank on non-existent crtc %d \n " ,
crtc ) ;
2010-11-14 23:08:27 +03:00
return - EINVAL ;
2008-09-30 12:14:26 -07:00
}
} else {
switch ( crtc ) {
case 0 :
radeon_irq_set_state ( dev , RADEON_CRTC_VBLANK_MASK , 1 ) ;
break ;
case 1 :
radeon_irq_set_state ( dev , RADEON_CRTC2_VBLANK_MASK , 1 ) ;
break ;
default :
DRM_ERROR ( " tried to enable vblank on non-existent crtc %d \n " ,
crtc ) ;
2010-11-14 23:08:27 +03:00
return - EINVAL ;
2008-09-30 12:14:26 -07:00
}
}
return 0 ;
}
void radeon_disable_vblank ( struct drm_device * dev , int crtc )
{
drm_radeon_private_t * dev_priv = dev - > dev_private ;
2009-03-06 11:47:54 -05:00
if ( ( dev_priv - > flags & RADEON_FAMILY_MASK ) > = CHIP_RS600 ) {
2008-09-30 12:14:26 -07:00
switch ( crtc ) {
case 0 :
r500_vbl_irq_set_state ( dev , R500_D1MODE_INT_MASK , 0 ) ;
break ;
case 1 :
r500_vbl_irq_set_state ( dev , R500_D2MODE_INT_MASK , 0 ) ;
break ;
default :
DRM_ERROR ( " tried to enable vblank on non-existent crtc %d \n " ,
crtc ) ;
break ;
}
} else {
switch ( crtc ) {
case 0 :
radeon_irq_set_state ( dev , RADEON_CRTC_VBLANK_MASK , 0 ) ;
break ;
case 1 :
radeon_irq_set_state ( dev , RADEON_CRTC2_VBLANK_MASK , 0 ) ;
break ;
default :
DRM_ERROR ( " tried to enable vblank on non-existent crtc %d \n " ,
crtc ) ;
break ;
}
}
}
static inline u32 radeon_acknowledge_irqs ( drm_radeon_private_t * dev_priv , u32 * r500_disp_int )
{
u32 irqs = RADEON_READ ( RADEON_GEN_INT_STATUS ) ;
u32 irq_mask = RADEON_SW_INT_TEST ;
* r500_disp_int = 0 ;
2009-03-06 11:47:54 -05:00
if ( ( dev_priv - > flags & RADEON_FAMILY_MASK ) > = CHIP_RS600 ) {
2008-09-30 12:14:26 -07:00
/* vbl interrupts in a different place */
if ( irqs & R500_DISPLAY_INT_STATUS ) {
/* if a display interrupt */
u32 disp_irq ;
disp_irq = RADEON_READ ( R500_DISP_INTERRUPT_STATUS ) ;
* r500_disp_int = disp_irq ;
if ( disp_irq & R500_D1_VBLANK_INTERRUPT )
RADEON_WRITE ( R500_D1MODE_VBLANK_STATUS , R500_VBLANK_ACK ) ;
if ( disp_irq & R500_D2_VBLANK_INTERRUPT )
RADEON_WRITE ( R500_D2MODE_VBLANK_STATUS , R500_VBLANK_ACK ) ;
}
irq_mask | = R500_DISPLAY_INT_STATUS ;
} else
irq_mask | = RADEON_CRTC_VBLANK_STAT | RADEON_CRTC2_VBLANK_STAT ;
irqs & = irq_mask ;
2005-06-26 21:05:59 +10:00
if ( irqs )
RADEON_WRITE ( RADEON_GEN_INT_STATUS , irqs ) ;
2008-09-30 12:14:26 -07:00
2005-06-26 21:05:59 +10:00
return irqs ;
}
2005-04-16 15:20:36 -07:00
/* Interrupts - Used for device synchronization and flushing in the
* following circumstances :
*
* - Exclusive FB access with hw idle :
* - Wait for GUI Idle ( ? ) interrupt , then do normal flush .
*
* - Frame throttling , NV_fence :
* - Drop marker irq ' s into command stream ahead of time .
* - Wait on irq ' s with lock * not held *
* - Check each for termination condition
*
* - Internally in cp_getbuffer , etc :
* - as above , but wait with lock held ? ? ?
*
* NOTE : These functions are misleadingly named - - the irq ' s aren ' t
* tied to dma at all , this is just a hangover from dri prehistory .
*/
2005-09-25 14:28:13 +10:00
irqreturn_t radeon_driver_irq_handler ( DRM_IRQ_ARGS )
2005-04-16 15:20:36 -07:00
{
2007-07-11 15:53:27 +10:00
struct drm_device * dev = ( struct drm_device * ) arg ;
2005-09-25 14:28:13 +10:00
drm_radeon_private_t * dev_priv =
( drm_radeon_private_t * ) dev - > dev_private ;
u32 stat ;
2008-09-30 12:14:26 -07:00
u32 r500_disp_int ;
2005-04-16 15:20:36 -07:00
2009-09-17 14:25:12 -04:00
if ( ( dev_priv - > flags & RADEON_FAMILY_MASK ) > = CHIP_R600 )
return IRQ_NONE ;
2005-04-16 15:20:36 -07:00
/* Only consider the bits we're interested in - others could be used
* outside the DRM
*/
2008-09-30 12:14:26 -07:00
stat = radeon_acknowledge_irqs ( dev_priv , & r500_disp_int ) ;
2005-04-16 15:20:36 -07:00
if ( ! stat )
return IRQ_NONE ;
2007-07-11 12:16:01 +10:00
stat & = dev_priv - > irq_enable_reg ;
2005-04-16 15:20:36 -07:00
/* SW interrupt */
2008-09-30 12:14:26 -07:00
if ( stat & RADEON_SW_INT_TEST )
2005-09-25 14:28:13 +10:00
DRM_WAKEUP ( & dev_priv - > swi_queue ) ;
2005-04-16 15:20:36 -07:00
/* VBLANK interrupt */
2009-03-06 11:47:54 -05:00
if ( ( dev_priv - > flags & RADEON_FAMILY_MASK ) > = CHIP_RS600 ) {
2008-09-30 12:14:26 -07:00
if ( r500_disp_int & R500_D1_VBLANK_INTERRUPT )
drm_handle_vblank ( dev , 0 ) ;
if ( r500_disp_int & R500_D2_VBLANK_INTERRUPT )
drm_handle_vblank ( dev , 1 ) ;
} else {
if ( stat & RADEON_CRTC_VBLANK_STAT )
drm_handle_vblank ( dev , 0 ) ;
if ( stat & RADEON_CRTC2_VBLANK_STAT )
drm_handle_vblank ( dev , 1 ) ;
2008-05-07 12:15:39 +10:00
}
2005-04-16 15:20:36 -07:00
return IRQ_HANDLED ;
}
2007-07-11 15:53:27 +10:00
static int radeon_emit_irq ( struct drm_device * dev )
2005-04-16 15:20:36 -07:00
{
drm_radeon_private_t * dev_priv = dev - > dev_private ;
unsigned int ret ;
RING_LOCALS ;
atomic_inc ( & dev_priv - > swi_emitted ) ;
ret = atomic_read ( & dev_priv - > swi_emitted ) ;
2005-09-25 14:28:13 +10:00
BEGIN_RING ( 4 ) ;
OUT_RING_REG ( RADEON_LAST_SWI_REG , ret ) ;
OUT_RING_REG ( RADEON_GEN_INT_STATUS , RADEON_SW_INT_FIRE ) ;
ADVANCE_RING ( ) ;
COMMIT_RING ( ) ;
2005-04-16 15:20:36 -07:00
return ret ;
}
2007-07-11 15:53:27 +10:00
static int radeon_wait_irq ( struct drm_device * dev , int swi_nr )
2005-04-16 15:20:36 -07:00
{
2005-09-25 14:28:13 +10:00
drm_radeon_private_t * dev_priv =
( drm_radeon_private_t * ) dev - > dev_private ;
2005-04-16 15:20:36 -07:00
int ret = 0 ;
2005-09-25 14:28:13 +10:00
if ( RADEON_READ ( RADEON_LAST_SWI_REG ) > = swi_nr )
return 0 ;
2005-04-16 15:20:36 -07:00
dev_priv - > stats . boxes | = RADEON_BOX_WAIT_IDLE ;
2005-09-25 14:28:13 +10:00
DRM_WAIT_ON ( ret , dev_priv - > swi_queue , 3 * DRM_HZ ,
RADEON_READ ( RADEON_LAST_SWI_REG ) > = swi_nr ) ;
2005-04-16 15:20:36 -07:00
return ret ;
}
2008-09-30 12:14:26 -07:00
u32 radeon_get_vblank_counter ( struct drm_device * dev , int crtc )
2005-04-16 15:20:36 -07:00
{
2008-09-30 12:14:26 -07:00
drm_radeon_private_t * dev_priv = dev - > dev_private ;
2005-09-25 14:28:13 +10:00
if ( ! dev_priv ) {
2008-01-24 15:58:57 +10:00
DRM_ERROR ( " called with no initialization \n " ) ;
2007-08-25 19:22:43 +10:00
return - EINVAL ;
2005-04-16 15:20:36 -07:00
}
2008-09-30 12:14:26 -07:00
if ( crtc < 0 | | crtc > 1 ) {
DRM_ERROR ( " Invalid crtc %d \n " , crtc ) ;
2007-08-25 19:22:43 +10:00
return - EINVAL ;
2008-09-30 12:14:26 -07:00
}
2007-07-11 12:16:01 +10:00
2009-03-06 11:47:54 -05:00
if ( ( dev_priv - > flags & RADEON_FAMILY_MASK ) > = CHIP_RS600 ) {
2008-09-30 12:14:26 -07:00
if ( crtc = = 0 )
return RADEON_READ ( R500_D1CRTC_FRAME_COUNT ) ;
else
return RADEON_READ ( R500_D2CRTC_FRAME_COUNT ) ;
} else {
if ( crtc = = 0 )
return RADEON_READ ( RADEON_CRTC_CRNT_FRAME ) ;
else
return RADEON_READ ( RADEON_CRTC2_CRNT_FRAME ) ;
}
2007-07-11 12:16:01 +10:00
}
2005-04-16 15:20:36 -07:00
/* Needs the lock as it touches the ring.
*/
2007-09-03 12:06:45 +10:00
int radeon_irq_emit ( struct drm_device * dev , void * data , struct drm_file * file_priv )
2005-04-16 15:20:36 -07:00
{
drm_radeon_private_t * dev_priv = dev - > dev_private ;
2007-09-03 12:06:45 +10:00
drm_radeon_irq_emit_t * emit = data ;
2005-04-16 15:20:36 -07:00
int result ;
2005-09-25 14:28:13 +10:00
if ( ! dev_priv ) {
2008-01-24 15:58:57 +10:00
DRM_ERROR ( " called with no initialization \n " ) ;
2007-08-25 19:22:43 +10:00
return - EINVAL ;
2005-04-16 15:20:36 -07:00
}
2009-12-30 12:16:35 +11:00
if ( ( dev_priv - > flags & RADEON_FAMILY_MASK ) > = CHIP_R600 )
return - EINVAL ;
LOCK_TEST_WITH_RETURN ( dev , file_priv ) ;
2005-09-25 14:28:13 +10:00
result = radeon_emit_irq ( dev ) ;
2005-04-16 15:20:36 -07:00
2007-09-03 12:06:45 +10:00
if ( DRM_COPY_TO_USER ( emit - > irq_seq , & result , sizeof ( int ) ) ) {
2005-09-25 14:28:13 +10:00
DRM_ERROR ( " copy_to_user \n " ) ;
2007-08-25 19:22:43 +10:00
return - EFAULT ;
2005-04-16 15:20:36 -07:00
}
return 0 ;
}
/* Doesn't need the hardware lock.
*/
2007-09-03 12:06:45 +10:00
int radeon_irq_wait ( struct drm_device * dev , void * data , struct drm_file * file_priv )
2005-04-16 15:20:36 -07:00
{
drm_radeon_private_t * dev_priv = dev - > dev_private ;
2007-09-03 12:06:45 +10:00
drm_radeon_irq_wait_t * irqwait = data ;
2005-04-16 15:20:36 -07:00
2005-09-25 14:28:13 +10:00
if ( ! dev_priv ) {
2008-01-24 15:58:57 +10:00
DRM_ERROR ( " called with no initialization \n " ) ;
2007-08-25 19:22:43 +10:00
return - EINVAL ;
2005-04-16 15:20:36 -07:00
}
2009-09-17 14:25:12 -04:00
if ( ( dev_priv - > flags & RADEON_FAMILY_MASK ) > = CHIP_R600 )
return - EINVAL ;
2007-09-03 12:06:45 +10:00
return radeon_wait_irq ( dev , irqwait - > irq_seq ) ;
2005-04-16 15:20:36 -07:00
}
/* drm_dma.h hooks
*/
2007-07-11 15:53:27 +10:00
void radeon_driver_irq_preinstall ( struct drm_device * dev )
2005-09-25 14:28:13 +10:00
{
2005-04-16 15:20:36 -07:00
drm_radeon_private_t * dev_priv =
2005-09-25 14:28:13 +10:00
( drm_radeon_private_t * ) dev - > dev_private ;
2008-09-30 12:14:26 -07:00
u32 dummy ;
2005-04-16 15:20:36 -07:00
2009-09-17 14:25:12 -04:00
if ( ( dev_priv - > flags & RADEON_FAMILY_MASK ) > = CHIP_R600 )
return ;
2005-09-25 14:28:13 +10:00
/* Disable *all* interrupts */
2009-03-06 11:47:54 -05:00
if ( ( dev_priv - > flags & RADEON_FAMILY_MASK ) > = CHIP_RS600 )
2008-09-30 12:14:26 -07:00
RADEON_WRITE ( R500_DxMODE_INT_MASK , 0 ) ;
2005-09-25 14:28:13 +10:00
RADEON_WRITE ( RADEON_GEN_INT_CNTL , 0 ) ;
2005-04-16 15:20:36 -07:00
/* Clear bits if they're already high */
2008-09-30 12:14:26 -07:00
radeon_acknowledge_irqs ( dev_priv , & dummy ) ;
2005-04-16 15:20:36 -07:00
}
2008-09-30 12:14:26 -07:00
int radeon_driver_irq_postinstall ( struct drm_device * dev )
2005-09-25 14:28:13 +10:00
{
2005-04-16 15:20:36 -07:00
drm_radeon_private_t * dev_priv =
2005-09-25 14:28:13 +10:00
( drm_radeon_private_t * ) dev - > dev_private ;
2005-04-16 15:20:36 -07:00
2005-09-25 14:28:13 +10:00
atomic_set ( & dev_priv - > swi_emitted , 0 ) ;
DRM_INIT_WAITQUEUE ( & dev_priv - > swi_queue ) ;
2005-04-16 15:20:36 -07:00
2008-09-30 12:14:26 -07:00
dev - > max_vblank_count = 0x001fffff ;
2009-09-17 14:25:12 -04:00
if ( ( dev_priv - > flags & RADEON_FAMILY_MASK ) > = CHIP_R600 )
return 0 ;
2008-09-30 12:14:26 -07:00
radeon_irq_set_state ( dev , RADEON_SW_INT_ENABLE , 1 ) ;
return 0 ;
2005-04-16 15:20:36 -07:00
}
2007-07-11 15:53:27 +10:00
void radeon_driver_irq_uninstall ( struct drm_device * dev )
2005-09-25 14:28:13 +10:00
{
2005-04-16 15:20:36 -07:00
drm_radeon_private_t * dev_priv =
2005-09-25 14:28:13 +10:00
( drm_radeon_private_t * ) dev - > dev_private ;
2005-04-16 15:20:36 -07:00
if ( ! dev_priv )
return ;
2009-09-17 14:25:12 -04:00
if ( ( dev_priv - > flags & RADEON_FAMILY_MASK ) > = CHIP_R600 )
return ;
2009-03-06 11:47:54 -05:00
if ( ( dev_priv - > flags & RADEON_FAMILY_MASK ) > = CHIP_RS600 )
2008-09-30 12:14:26 -07:00
RADEON_WRITE ( R500_DxMODE_INT_MASK , 0 ) ;
2005-04-16 15:20:36 -07:00
/* Disable *all* interrupts */
2005-09-25 14:28:13 +10:00
RADEON_WRITE ( RADEON_GEN_INT_CNTL , 0 ) ;
2005-04-16 15:20:36 -07:00
}
2007-07-11 12:16:01 +10:00
2007-07-11 15:53:27 +10:00
int radeon_vblank_crtc_get ( struct drm_device * dev )
2007-07-11 12:16:01 +10:00
{
drm_radeon_private_t * dev_priv = ( drm_radeon_private_t * ) dev - > dev_private ;
2008-09-30 12:14:26 -07:00
return dev_priv - > vblank_crtc ;
2007-07-11 12:16:01 +10:00
}
2007-07-11 15:53:27 +10:00
int radeon_vblank_crtc_set ( struct drm_device * dev , int64_t value )
2007-07-11 12:16:01 +10:00
{
drm_radeon_private_t * dev_priv = ( drm_radeon_private_t * ) dev - > dev_private ;
if ( value & ~ ( DRM_RADEON_VBLANK_CRTC1 | DRM_RADEON_VBLANK_CRTC2 ) ) {
DRM_ERROR ( " called with invalid crtc 0x%x \n " , ( unsigned int ) value ) ;
2007-08-25 19:22:43 +10:00
return - EINVAL ;
2007-07-11 12:16:01 +10:00
}
dev_priv - > vblank_crtc = ( unsigned int ) value ;
return 0 ;
}