2008-04-28 20:24:33 -03:00
/*
* cx18 interrupt handling
*
* Copyright ( C ) 2007 Hans Verkuil < hverkuil @ xs4all . nl >
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation ; either version 2 of the License , or
* ( at your option ) any later version .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*
* You should have received a copy of the GNU General Public License
* along with this program ; if not , write to the Free Software
* Foundation , Inc . , 59 Temple Place , Suite 330 , Boston , MA
* 02111 - 1307 USA
*/
# include "cx18-driver.h"
2008-08-30 16:03:44 -03:00
# include "cx18-io.h"
2008-04-28 20:24:33 -03:00
# include "cx18-firmware.h"
# include "cx18-fileops.h"
# include "cx18-queue.h"
# include "cx18-irq.h"
# include "cx18-ioctl.h"
# include "cx18-mailbox.h"
# include "cx18-vbi.h"
# include "cx18-scb.h"
# define DMA_MAGIC_COOKIE 0x000001fe
static void epu_dma_done ( struct cx18 * cx , struct cx18_mailbox * mb )
{
u32 handle = mb - > args [ 0 ] ;
struct cx18_stream * s = NULL ;
struct cx18_buffer * buf ;
u32 off ;
int i ;
int id ;
for ( i = 0 ; i < CX18_MAX_STREAMS ; i + + ) {
s = & cx - > streams [ i ] ;
if ( ( handle = = s - > handle ) & & ( s - > dvb . enabled ) )
break ;
if ( s - > v4l2dev & & handle = = s - > handle )
break ;
}
if ( i = = CX18_MAX_STREAMS ) {
2008-10-04 19:09:41 -03:00
CX18_WARN ( " Got DMA done notification for unknown/inactive "
" handle %d \n " , handle ) ;
2008-04-28 20:24:33 -03:00
mb - > error = CXERR_NOT_OPEN ;
mb - > cmd = 0 ;
cx18_mb_ack ( cx , mb ) ;
return ;
}
off = mb - > args [ 1 ] ;
if ( mb - > args [ 2 ] ! = 1 )
CX18_WARN ( " Ack struct = %d for %s \n " ,
mb - > args [ 2 ] , s - > name ) ;
2008-08-30 16:03:44 -03:00
id = cx18_read_enc ( cx , off ) ;
buf = cx18_queue_get_buf_irq ( s , id , cx18_read_enc ( cx , off + 4 ) ) ;
2008-04-28 20:24:33 -03:00
CX18_DEBUG_HI_DMA ( " DMA DONE for %s (buffer %d) \n " , s - > name , id ) ;
if ( buf ) {
cx18_buf_sync_for_cpu ( s , buf ) ;
if ( s - > type = = CX18_ENC_STREAM_TYPE_TS & & s - > dvb . enabled ) {
/* process the buffer here */
CX18_DEBUG_HI_DMA ( " TS recv and sent bytesused=%d \n " ,
buf - > bytesused ) ;
dvb_dmx_swfilter ( & s - > dvb . demux , buf - > buf ,
buf - > bytesused ) ;
cx18_buf_sync_for_device ( s , buf ) ;
cx18_vapi ( cx , CX18_CPU_DE_SET_MDL , 5 , s - > handle ,
2008-05-21 00:32:01 -03:00
( void __iomem * ) & cx - > scb - > cpu_mdl [ buf - > id ] - cx - > enc_mem ,
2008-04-28 20:24:33 -03:00
1 , buf - > id , s - > buf_size ) ;
} else
set_bit ( CX18_F_B_NEED_BUF_SWAP , & buf - > b_flags ) ;
} else {
CX18_WARN ( " Could not find buf %d for stream %s \n " ,
2008-08-30 16:03:44 -03:00
cx18_read_enc ( cx , off ) , s - > name ) ;
2008-04-28 20:24:33 -03:00
}
mb - > error = 0 ;
mb - > cmd = 0 ;
cx18_mb_ack ( cx , mb ) ;
wake_up ( & cx - > dma_waitq ) ;
if ( s - > id ! = - 1 )
wake_up ( & s - > waitq ) ;
}
static void epu_debug ( struct cx18 * cx , struct cx18_mailbox * mb )
{
char str [ 256 ] = { 0 } ;
char * p ;
if ( mb - > args [ 1 ] ) {
2008-08-30 16:03:44 -03:00
cx18_setup_page ( cx , mb - > args [ 1 ] ) ;
cx18_memcpy_fromio ( cx , str , cx - > enc_mem + mb - > args [ 1 ] , 252 ) ;
2008-04-28 20:24:33 -03:00
str [ 252 ] = 0 ;
}
cx18_mb_ack ( cx , mb ) ;
CX18_DEBUG_INFO ( " %x %s \n " , mb - > args [ 0 ] , str ) ;
p = strchr ( str , ' . ' ) ;
if ( ! test_bit ( CX18_F_I_LOADED_FW , & cx - > i_flags ) & & p & & p > str )
CX18_INFO ( " FW version: %s \n " , p - 1 ) ;
}
static void hpu_cmd ( struct cx18 * cx , u32 sw1 )
{
struct cx18_mailbox mb ;
if ( sw1 & IRQ_CPU_TO_EPU ) {
2008-08-30 16:03:44 -03:00
cx18_memcpy_fromio ( cx , & mb , & cx - > scb - > cpu2epu_mb , sizeof ( mb ) ) ;
2008-04-28 20:24:33 -03:00
mb . error = 0 ;
switch ( mb . cmd ) {
case CX18_EPU_DMA_DONE :
epu_dma_done ( cx , & mb ) ;
break ;
case CX18_EPU_DEBUG :
epu_debug ( cx , & mb ) ;
break ;
default :
CX18_WARN ( " Unexpected mailbox command %08x \n " , mb . cmd ) ;
break ;
}
}
if ( sw1 & ( IRQ_APU_TO_EPU | IRQ_HPU_TO_EPU ) )
CX18_WARN ( " Unexpected interrupt %08x \n " , sw1 ) ;
}
irqreturn_t cx18_irq_handler ( int irq , void * dev_id )
{
struct cx18 * cx = ( struct cx18 * ) dev_id ;
u32 sw1 , sw1_mask ;
u32 sw2 , sw2_mask ;
u32 hw2 , hw2_mask ;
spin_lock ( & cx - > dma_reg_lock ) ;
2008-08-30 16:03:44 -03:00
hw2_mask = cx18_read_reg ( cx , HW2_INT_MASK5_PCI ) ;
hw2 = cx18_read_reg ( cx , HW2_INT_CLR_STATUS ) & hw2_mask ;
sw2_mask = cx18_read_reg ( cx , SW2_INT_ENABLE_PCI ) | IRQ_EPU_TO_HPU_ACK ;
sw2 = cx18_read_reg ( cx , SW2_INT_STATUS ) & sw2_mask ;
sw1_mask = cx18_read_reg ( cx , SW1_INT_ENABLE_PCI ) | IRQ_EPU_TO_HPU ;
sw1 = cx18_read_reg ( cx , SW1_INT_STATUS ) & sw1_mask ;
2008-04-28 20:24:33 -03:00
2008-08-30 16:03:44 -03:00
cx18_write_reg ( cx , sw2 & sw2_mask , SW2_INT_STATUS ) ;
cx18_write_reg ( cx , sw1 & sw1_mask , SW1_INT_STATUS ) ;
cx18_write_reg ( cx , hw2 & hw2_mask , HW2_INT_CLR_STATUS ) ;
2008-04-28 20:24:33 -03:00
if ( sw1 | | sw2 | | hw2 )
CX18_DEBUG_HI_IRQ ( " SW1: %x SW2: %x HW2: %x \n " , sw1 , sw2 , hw2 ) ;
/* To do: interrupt-based I2C handling
if ( hw2 & 0x00c00000 ) {
}
*/
if ( sw2 ) {
2008-08-30 16:03:44 -03:00
if ( sw2 & ( cx18_readl ( cx , & cx - > scb - > cpu2hpu_irq_ack ) |
cx18_readl ( cx , & cx - > scb - > cpu2epu_irq_ack ) ) )
2008-04-28 20:24:33 -03:00
wake_up ( & cx - > mb_cpu_waitq ) ;
2008-08-30 16:03:44 -03:00
if ( sw2 & ( cx18_readl ( cx , & cx - > scb - > apu2hpu_irq_ack ) |
cx18_readl ( cx , & cx - > scb - > apu2epu_irq_ack ) ) )
2008-04-28 20:24:33 -03:00
wake_up ( & cx - > mb_apu_waitq ) ;
2008-08-30 16:03:44 -03:00
if ( sw2 & cx18_readl ( cx , & cx - > scb - > epu2hpu_irq_ack ) )
2008-04-28 20:24:33 -03:00
wake_up ( & cx - > mb_epu_waitq ) ;
2008-08-30 16:03:44 -03:00
if ( sw2 & cx18_readl ( cx , & cx - > scb - > hpu2epu_irq_ack ) )
2008-04-28 20:24:33 -03:00
wake_up ( & cx - > mb_hpu_waitq ) ;
}
if ( sw1 )
hpu_cmd ( cx , sw1 ) ;
spin_unlock ( & cx - > dma_reg_lock ) ;
return ( hw2 | sw1 | sw2 ) ? IRQ_HANDLED : IRQ_NONE ;
}