2005-04-17 02:20:36 +04:00
/* radeon_irq.c -- IRQ handling for radeon -*- linux-c -*-
*
* Copyright ( C ) The Weather Channel , Inc . 2002. All Rights Reserved .
2005-09-25 08:28:13 +04:00
*
2005-04-17 02:20:36 +04: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 >
* Michel D <EFBFBD> zer < michel @ daenzer . net >
*/
# include "drmP.h"
# include "drm.h"
# include "radeon_drm.h"
# include "radeon_drv.h"
2005-09-25 08:28:13 +04:00
static __inline__ u32 radeon_acknowledge_irqs ( drm_radeon_private_t * dev_priv ,
u32 mask )
2005-06-26 15:05:59 +04:00
{
u32 irqs = RADEON_READ ( RADEON_GEN_INT_STATUS ) & mask ;
if ( irqs )
RADEON_WRITE ( RADEON_GEN_INT_STATUS , irqs ) ;
return irqs ;
}
2005-04-17 02:20:36 +04: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 08:28:13 +04:00
irqreturn_t radeon_driver_irq_handler ( DRM_IRQ_ARGS )
2005-04-17 02:20:36 +04:00
{
drm_device_t * dev = ( drm_device_t * ) arg ;
2005-09-25 08:28:13 +04:00
drm_radeon_private_t * dev_priv =
( drm_radeon_private_t * ) dev - > dev_private ;
u32 stat ;
2005-04-17 02:20:36 +04:00
/* Only consider the bits we're interested in - others could be used
* outside the DRM
*/
2005-09-25 08:28:13 +04:00
stat = radeon_acknowledge_irqs ( dev_priv , ( RADEON_SW_INT_TEST_ACK |
2005-06-26 15:05:59 +04:00
RADEON_CRTC_VBLANK_STAT ) ) ;
2005-04-17 02:20:36 +04:00
if ( ! stat )
return IRQ_NONE ;
/* SW interrupt */
if ( stat & RADEON_SW_INT_TEST ) {
2005-09-25 08:28:13 +04:00
DRM_WAKEUP ( & dev_priv - > swi_queue ) ;
2005-04-17 02:20:36 +04:00
}
/* VBLANK interrupt */
if ( stat & RADEON_CRTC_VBLANK_STAT ) {
atomic_inc ( & dev - > vbl_received ) ;
DRM_WAKEUP ( & dev - > vbl_queue ) ;
2005-09-25 08:28:13 +04:00
drm_vbl_send_signals ( dev ) ;
2005-04-17 02:20:36 +04:00
}
return IRQ_HANDLED ;
}
2005-09-25 08:28:13 +04:00
static int radeon_emit_irq ( drm_device_t * dev )
2005-04-17 02:20:36 +04: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 08:28:13 +04: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-17 02:20:36 +04:00
return ret ;
}
2005-09-25 08:28:13 +04:00
static int radeon_wait_irq ( drm_device_t * dev , int swi_nr )
2005-04-17 02:20:36 +04:00
{
2005-09-25 08:28:13 +04:00
drm_radeon_private_t * dev_priv =
( drm_radeon_private_t * ) dev - > dev_private ;
2005-04-17 02:20:36 +04:00
int ret = 0 ;
2005-09-25 08:28:13 +04:00
if ( RADEON_READ ( RADEON_LAST_SWI_REG ) > = swi_nr )
return 0 ;
2005-04-17 02:20:36 +04:00
dev_priv - > stats . boxes | = RADEON_BOX_WAIT_IDLE ;
2005-09-25 08:28:13 +04:00
DRM_WAIT_ON ( ret , dev_priv - > swi_queue , 3 * DRM_HZ ,
RADEON_READ ( RADEON_LAST_SWI_REG ) > = swi_nr ) ;
2005-04-17 02:20:36 +04:00
return ret ;
}
2005-09-25 08:28:13 +04:00
int radeon_driver_vblank_wait ( drm_device_t * dev , unsigned int * sequence )
2005-04-17 02:20:36 +04:00
{
2005-09-25 08:28:13 +04:00
drm_radeon_private_t * dev_priv =
( drm_radeon_private_t * ) dev - > dev_private ;
2005-04-17 02:20:36 +04:00
unsigned int cur_vblank ;
int ret = 0 ;
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 DRM_ERR ( EINVAL ) ;
}
2005-06-26 15:05:59 +04:00
radeon_acknowledge_irqs ( dev_priv , RADEON_CRTC_VBLANK_STAT ) ;
2005-04-17 02:20:36 +04:00
dev_priv - > stats . boxes | = RADEON_BOX_WAIT_IDLE ;
/* Assume that the user has missed the current sequence number
* by about a day rather than she wants to wait for years
2005-09-25 08:28:13 +04:00
* using vertical blanks . . .
2005-04-17 02:20:36 +04:00
*/
2005-09-25 08:28:13 +04:00
DRM_WAIT_ON ( ret , dev - > vbl_queue , 3 * DRM_HZ ,
( ( ( cur_vblank = atomic_read ( & dev - > vbl_received ) )
- * sequence ) < = ( 1 < < 23 ) ) ) ;
2005-04-17 02:20:36 +04:00
* sequence = cur_vblank ;
return ret ;
}
/* Needs the lock as it touches the ring.
*/
2005-09-25 08:28:13 +04:00
int radeon_irq_emit ( DRM_IOCTL_ARGS )
2005-04-17 02:20:36 +04:00
{
DRM_DEVICE ;
drm_radeon_private_t * dev_priv = dev - > dev_private ;
drm_radeon_irq_emit_t emit ;
int result ;
2005-09-25 08:28:13 +04:00
LOCK_TEST_WITH_RETURN ( dev , filp ) ;
2005-04-17 02:20:36 +04:00
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 DRM_ERR ( EINVAL ) ;
}
2005-09-25 08:28:13 +04:00
DRM_COPY_FROM_USER_IOCTL ( emit , ( drm_radeon_irq_emit_t __user * ) data ,
sizeof ( emit ) ) ;
2005-04-17 02:20:36 +04:00
2005-09-25 08:28:13 +04:00
result = radeon_emit_irq ( dev ) ;
2005-04-17 02:20:36 +04:00
2005-09-25 08:28:13 +04:00
if ( DRM_COPY_TO_USER ( emit . irq_seq , & result , sizeof ( int ) ) ) {
DRM_ERROR ( " copy_to_user \n " ) ;
2005-04-17 02:20:36 +04:00
return DRM_ERR ( EFAULT ) ;
}
return 0 ;
}
/* Doesn't need the hardware lock.
*/
2005-09-25 08:28:13 +04:00
int radeon_irq_wait ( DRM_IOCTL_ARGS )
2005-04-17 02:20:36 +04:00
{
DRM_DEVICE ;
drm_radeon_private_t * dev_priv = dev - > dev_private ;
drm_radeon_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 DRM_ERR ( EINVAL ) ;
}
2005-09-25 08:28:13 +04:00
DRM_COPY_FROM_USER_IOCTL ( irqwait , ( drm_radeon_irq_wait_t __user * ) data ,
sizeof ( irqwait ) ) ;
2005-04-17 02:20:36 +04:00
2005-09-25 08:28:13 +04:00
return radeon_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 radeon_driver_irq_preinstall ( drm_device_t * dev )
{
2005-04-17 02:20:36 +04:00
drm_radeon_private_t * dev_priv =
2005-09-25 08:28:13 +04:00
( drm_radeon_private_t * ) dev - > dev_private ;
2005-04-17 02:20:36 +04:00
2005-09-25 08:28:13 +04:00
/* Disable *all* interrupts */
RADEON_WRITE ( RADEON_GEN_INT_CNTL , 0 ) ;
2005-04-17 02:20:36 +04:00
/* Clear bits if they're already high */
2005-06-26 15:05:59 +04:00
radeon_acknowledge_irqs ( dev_priv , ( RADEON_SW_INT_TEST_ACK |
RADEON_CRTC_VBLANK_STAT ) ) ;
2005-04-17 02:20:36 +04:00
}
2005-09-25 08:28:13 +04:00
void radeon_driver_irq_postinstall ( drm_device_t * dev )
{
2005-04-17 02:20:36 +04:00
drm_radeon_private_t * dev_priv =
2005-09-25 08:28:13 +04:00
( drm_radeon_private_t * ) dev - > dev_private ;
2005-04-17 02:20:36 +04:00
2005-09-25 08:28:13 +04:00
atomic_set ( & dev_priv - > swi_emitted , 0 ) ;
DRM_INIT_WAITQUEUE ( & dev_priv - > swi_queue ) ;
2005-04-17 02:20:36 +04:00
/* Turn on SW and VBL ints */
2005-09-25 08:28:13 +04:00
RADEON_WRITE ( RADEON_GEN_INT_CNTL ,
RADEON_CRTC_VBLANK_MASK | RADEON_SW_INT_ENABLE ) ;
2005-04-17 02:20:36 +04:00
}
2005-09-25 08:28:13 +04:00
void radeon_driver_irq_uninstall ( drm_device_t * dev )
{
2005-04-17 02:20:36 +04:00
drm_radeon_private_t * dev_priv =
2005-09-25 08:28:13 +04:00
( drm_radeon_private_t * ) dev - > dev_private ;
2005-04-17 02:20:36 +04:00
if ( ! dev_priv )
return ;
/* Disable *all* interrupts */
2005-09-25 08:28:13 +04:00
RADEON_WRITE ( RADEON_GEN_INT_CNTL , 0 ) ;
2005-04-17 02:20:36 +04:00
}