@ -3,6 +3,7 @@
* Support for audio capture
* PCI function # 1 of the cx2388x .
*
* ( c ) 2007 Trent Piepho < xyzzy @ speakeasy . org >
* ( c ) 2005 , 2006 Ricardo Cerqueira < v4l @ cerqueira . org >
* ( c ) 2005 Mauro Carvalho Chehab < mchehab @ infradead . org >
* Based on a dummy cx88 module by Gerd Knorr < kraxel @ bytesex . org >
@ -47,7 +48,6 @@
# define dprintk_core(level,fmt, arg...) if (debug >= level) \
printk ( KERN_DEBUG " %s/1: " fmt , chip - > core - > name , # # arg )
/****************************************************************************
Data type declarations - Can be moded to a header file later
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
@ -58,6 +58,7 @@
struct cx88_audio_dev {
struct cx88_core * core ;
struct cx88_dmaqueue q ;
u64 starttime ;
/* pci i/o */
struct pci_dev * pci ;
@ -68,24 +69,20 @@ struct cx88_audio_dev {
struct snd_card * card ;
spinlock_t reg_lock ;
atomic_t count ;
unsigned int dma_size ;
unsigned int period_size ;
unsigned int num_periods ;
struct videobuf_dmabuf dma_risc ;
struct videobuf_dmabuf dma_risc ;
int mixer_volume [ MIXER_ADDR_LAST + 1 ] [ 2 ] ;
int capture_source [ MIXER_ADDR_LAST + 1 ] [ 2 ] ;
long int read_count ;
long int read_offset ;
struct cx88_buffer * buf ;
long opened ;
struct snd_pcm_substream * substream ;
struct cx88_buffer * buf ;
struct snd_pcm_substream * substream ;
} ;
typedef struct cx88_audio_dev snd_cx88_card_t ;
@ -146,16 +143,13 @@ static int _cx88_start_audio_dma(snd_cx88_card_t *chip)
cx_write ( MO_AUDD_LNGTH , buf - > bpl ) ;
/* reset counter */
cx_write ( MO_AUDD_GPCNTRL , GP_COUNT_CONTROL_RESET ) ;
cx_write ( MO_AUDD_GPCNTRL , GP_COUNT_CONTROL_RESET ) ;
atomic_set ( & chip - > count , 0 ) ;
dprintk ( 1 , " Start audio DMA, %d B/line, %d lines/FIFO, %d lines/irq, "
" %d B/irq \n " , buf - > bpl , cx_read ( audio_ch - > cmds_start + 8 ) > > 1 ,
dprintk ( 1 , " Start audio DMA, %d B/line, %d lines/FIFO, %d periods, %d "
" byte buffer \n " , buf - > bpl , cx_read ( audio_ch - > cmds_start + 8 ) > > 1 ,
chip - > num_periods , buf - > bpl * chip - > num_periods ) ;
dprintk ( 1 , " Enabling IRQ, setting mask from 0x%x to 0x%x \n " ,
chip - > core - > pci_irqmask ,
chip - > core - > pci_irqmask | PCI_INT_AUDINT ) ;
/* Enables corresponding bits at AUD_INT_STAT */
cx_write ( MO_AUD_INTMSK , AUD_INT_OPC_ERR | AUD_INT_DN_SYNC |
AUD_INT_DN_RISCI2 | AUD_INT_DN_RISCI1 ) ;
@ -198,7 +192,7 @@ static int _cx88_stop_audio_dma(snd_cx88_card_t *chip)
return 0 ;
}
# define MAX_IRQ_LOOP 1 0
# define MAX_IRQ_LOOP 5 0
/*
* BOARD Specific : IRQ dma bits
@ -223,14 +217,11 @@ static void cx8801_aud_irq(snd_cx88_card_t *chip)
{
struct cx88_core * core = chip - > core ;
u32 status , mask ;
u32 count ;
status = cx_read ( MO_AUD_INTSTAT ) ;
mask = cx_read ( MO_AUD_INTMSK ) ;
if ( 0 = = ( status & mask ) ) {
spin_unlock ( & chip - > reg_lock ) ;
if ( 0 = = ( status & mask ) )
return ;
}
cx_write ( MO_AUD_INTSTAT , status ) ;
if ( debug > 1 | | ( status & mask & ~ 0xff ) )
cx88_print_irqbits ( core - > name , " irq aud " ,
@ -238,27 +229,20 @@ static void cx8801_aud_irq(snd_cx88_card_t *chip)
status , mask ) ;
/* risc op code error */
if ( status & AUD_INT_OPC_ERR ) {
printk ( KERN_WARNING " %s/0: a udio risc op code error \n " , core - > name ) ;
printk ( KERN_WARNING " %s/1: A udio risc op code error \n " , core - > name ) ;
cx_clear ( MO_AUD_DMACNTRL , 0x11 ) ;
cx88_sram_channel_dump ( core , & cx88_sram_channels [ SRAM_CH25 ] ) ;
}
if ( status & AUD_INT_DN_SYNC ) {
dprintk ( 1 , " Downstream sync error \n " ) ;
cx_write ( MO_AUDD_GPCNTRL , GP_COUNT_CONTROL_RESET ) ;
return ;
}
/* risc1 downstream */
if ( status & AUD_INT_DN_RISCI1 ) {
spin_lock ( & chip - > reg_lock ) ;
count = cx_read ( MO_AUDD_GPCNT ) ;
spin_unlock ( & chip - > reg_lock ) ;
if ( chip - > read_count = = 0 )
chip - > read_count + = chip - > dma_size ;
}
if ( chip - > read_count > = chip - > period_size ) {
dprintk ( 2 , " Elapsing period \n " ) ;
atomic_set ( & chip - > count , cx_read ( MO_AUDD_GPCNT ) ) ;
snd_pcm_period_elapsed ( chip - > substream ) ;
}
dprintk ( 3 , " Leaving audio IRQ handler... \n " ) ;
/* FIXME: Any other status should deserve a special handling? */
}
@ -277,23 +261,20 @@ static irqreturn_t cx8801_irq(int irq, void *dev_id)
( core - > pci_irqmask | PCI_INT_AUDINT ) ;
if ( 0 = = status )
goto out ;
dprintk ( 3 , " cx8801_irq \n " ) ;
dprintk ( 3 , " loop: %d/%d \n " , loop , MAX_IRQ_LOOP ) ;
dprintk ( 3 , " status: %d \n " , status ) ;
dprintk ( 3 , " cx8801_irq loop %d/%d, status %x \n " ,
loop , MAX_IRQ_LOOP , status ) ;
handled = 1 ;
cx_write ( MO_PCI_INTSTAT , status ) ;
if ( status & core - > pci_irqmask )
cx88_core_irq ( core , status ) ;
if ( status & PCI_INT_AUDINT ) {
dprintk ( 2 , " ALSA IRQ handling \n " ) ;
if ( status & PCI_INT_AUDINT )
cx8801_aud_irq ( chip ) ;
}
} ;
}
if ( MAX_IRQ_LOOP = = loop ) {
dprintk ( 0 , " clearing mask \n " ) ;
dprintk ( 1 , " %s/0: irq loop -- clearing mask \n " ,
printk ( KERN_ERR
" %s/1: IRQ loop detected, disabling interrupts \n " ,
core - > name ) ;
cx_clear ( MO_PCI_INTMSK , PCI_INT_AUDINT ) ;
}
@ -315,7 +296,7 @@ static int dsp_buffer_free(snd_cx88_card_t *chip)
chip - > dma_size = 0 ;
return 0 ;
return 0 ;
}
/****************************************************************************
@ -325,6 +306,7 @@ static int dsp_buffer_free(snd_cx88_card_t *chip)
/*
* Digital hardware definition
*/
# define DEFAULT_FIFO_SIZE 4096
static struct snd_pcm_hardware snd_cx88_digital_hw = {
. info = SNDRV_PCM_INFO_MMAP |
SNDRV_PCM_INFO_INTERLEAVED |
@ -337,19 +319,15 @@ static struct snd_pcm_hardware snd_cx88_digital_hw = {
. rate_max = 48000 ,
. channels_min = 2 ,
. channels_max = 2 ,
. buffer_bytes_max = ( 2 * 2048 ) ,
. period_bytes_min = 2048 ,
. period_bytes_max = 2048 ,
. periods_min = 2 ,
. periods_max = 2 ,
/* Analog audio output will be full of clicks and pops if there
are not exactly four lines in the SRAM FIFO buffer . */
. period_bytes_min = DEFAULT_FIFO_SIZE / 4 ,
. period_bytes_max = DEFAULT_FIFO_SIZE / 4 ,
. periods_min = 1 ,
. periods_max = 1024 ,
. buffer_bytes_max = ( 1024 * 1024 ) ,
} ;
/*
* audio pcm capture runtime free
*/
static void snd_card_cx88_runtime_free ( struct snd_pcm_runtime * runtime )
{
}
/*
* audio pcm capture open callback
*/
@ -359,26 +337,24 @@ static int snd_cx88_pcm_open(struct snd_pcm_substream *substream)
struct snd_pcm_runtime * runtime = substream - > runtime ;
int err ;
if ( test_and_set_bit ( 0 , & chip - > opened ) )
return - EBUSY ;
err = snd_pcm_hw_constraint_integer ( runtime , SNDRV_PCM_HW_PARAM_PERIODS ) ;
err = snd_pcm_hw_constraint_pow2 ( runtime , 0 , SNDRV_PCM_HW_PARAM_PERIODS ) ;
if ( err < 0 )
goto _error ;
chip - > substream = substream ;
chip - > read_count = 0 ;
chip - > read_offset = 0 ;
runtime - > private_free = snd_card_cx88_runtime_free ;
runtime - > hw = snd_cx88_digital_hw ;
if ( cx88_sram_channels [ SRAM_CH25 ] . fifo_size ! = DEFAULT_FIFO_SIZE ) {
unsigned int bpl = cx88_sram_channels [ SRAM_CH25 ] . fifo_size / 4 ;
bpl & = ~ 7 ; /* must be multiple of 8 */
runtime - > hw . period_bytes_min = bpl ;
runtime - > hw . period_bytes_max = bpl ;
}
return 0 ;
_error :
dprintk ( 1 , " Error opening PCM! \n " ) ;
clear_bit ( 0 , & chip - > opened ) ;
smp_mb__after_clear_bit ( ) ;
return err ;
}
@ -387,11 +363,6 @@ _error:
*/
static int snd_cx88_close ( struct snd_pcm_substream * substream )
{
snd_cx88_card_t * chip = snd_pcm_substream_chip ( substream ) ;
clear_bit ( 0 , & chip - > opened ) ;
smp_mb__after_clear_bit ( ) ;
return 0 ;
}
@ -403,54 +374,61 @@ static int snd_cx88_hw_params(struct snd_pcm_substream * substream,
{
snd_cx88_card_t * chip = snd_pcm_substream_chip ( substream ) ;
struct cx88_buffer * buf ;
int ret ;
if ( substream - > runtime - > dma_area ) {
dsp_buffer_free ( chip ) ;
substream - > runtime - > dma_area = NULL ;
}
chip - > period_size = params_period_bytes ( hw_params ) ;
chip - > num_periods = params_periods ( hw_params ) ;
chip - > dma_size = chip - > period_size * params_periods ( hw_params ) ;
BUG_ON ( ! chip - > dma_size ) ;
BUG_ON ( chip - > num_periods & ( chip - > num_periods - 1 ) ) ;
dprintk ( 1 , " Setting buffer \n " ) ;
buf = kzalloc ( sizeof ( * buf ) , GFP_KERNEL ) ;
buf = kzalloc ( sizeof ( * buf ) , GFP_KERNEL ) ;
if ( NULL = = buf )
return - ENOMEM ;
buf - > vb . memory = V4L2_MEMORY_MMAP ;
buf - > vb . field = V4L2_FIELD_NONE ;
buf - > vb . width = chip - > period_size ;
buf - > bpl = chip - > period_size ;
buf - > vb . height = chip - > num_periods ;
buf - > vb . size = chip - > dma_size ;
buf - > vb . field = V4L2_FIELD_NONE ;
videobuf_dma_init ( & buf - > vb . dma ) ;
videobuf_dma_init_kernel ( & buf - > vb . dma , PCI_DMA_FROMDEVICE ,
ret = videobuf_dma_init_kernel ( & buf - > vb . dma , PCI_DMA_FROMDEVICE ,
( PAGE_ALIGN ( buf - > vb . size ) > > PAGE_SHIFT ) ) ;
if ( ret < 0 )
goto error ;
videobuf_pci_dma_map ( chip - > pci , & buf - > vb . dma ) ;
ret = videobuf_pci_dma_map ( chip - > pci , & buf - > vb . dma ) ;
if ( ret < 0 )
goto error ;
cx88_risc_databuffer ( chip - > pci , & buf - > risc ,
buf - > vb . dma . sglist ,
buf - > vb . width , buf - > vb . height ) ;
ret = cx88_risc_databuffer ( chip - > pci , & buf - > risc , buf - > vb . dma . sglist ,
buf - > vb . width , buf - > vb . height , 1 ) ;
if ( ret < 0 )
goto error ;
buf - > risc . jmp [ 0 ] = cpu_to_le32 ( RISC_JUMP | RISC_IRQ1 | RISC_CNT_INC ) ;
/* Loop back to start of program */
buf - > risc . jmp [ 0 ] = cpu_to_le32 ( RISC_JUMP | RISC_IRQ1 | RISC_CNT_INC ) ;
buf - > risc . jmp [ 1 ] = cpu_to_le32 ( buf - > risc . dma ) ;
buf - > vb . state = STATE_PREPARED ;
buf - > bpl = chip - > period_size ;
chip - > buf = buf ;
chip - > dma_risc = buf - > vb . dma ;
dprintk ( 1 , " Buffer ready at %u \n " , chip - > dma_risc . nr_pages ) ;
substream - > runtime - > dma_area = chip - > dma_risc . vmalloc ;
return 0 ;
error :
kfree ( buf ) ;
return ret ;
}
/*
@ -477,7 +455,6 @@ static int snd_cx88_prepare(struct snd_pcm_substream *substream)
return 0 ;
}
/*
* trigger callback
*/
@ -486,6 +463,7 @@ static int snd_cx88_card_trigger(struct snd_pcm_substream *substream, int cmd)
snd_cx88_card_t * chip = snd_pcm_substream_chip ( substream ) ;
int err ;
/* Local interrupts are already disabled by ALSA */
spin_lock ( & chip - > reg_lock ) ;
switch ( cmd ) {
@ -512,17 +490,14 @@ static snd_pcm_uframes_t snd_cx88_pointer(struct snd_pcm_substream *substream)
{
snd_cx88_card_t * chip = snd_pcm_substream_chip ( substream ) ;
struct snd_pcm_runtime * runtime = substream - > runtime ;
u16 count ;
if ( chip - > read_count ) {
chip - > read_count - = snd_pcm_lib_period_bytes ( substream ) ;
chip - > read_offset + = snd_pcm_lib_period_bytes ( substream ) ;
if ( chip - > read_offset = = chip - > dma_size )
chip - > read_offset = 0 ;
}
dprintk ( 2 , " Pointer time, will return %li, read %li \n " , chip - > read_offset , chip - > read_count ) ;
return bytes_to_frames ( runtime , chip - > read_offset ) ;
count = atomic_read ( & chip - > count ) ;
// dprintk(2, "%s - count %d (+%u), period %d, frame %lu\n", __FUNCTION__,
// count, new, count & (runtime->periods-1),
// runtime->period_size * (count & (runtime->periods-1)));
return runtime - > period_size * ( count & ( runtime - > periods - 1 ) ) ;
}
/*
@ -592,10 +567,13 @@ static int snd_cx88_capture_volume_put(struct snd_kcontrol *kcontrol,
int v ;
u32 old_control ;
/* Do we really know this will always be called with IRQs on? */
spin_lock_irq ( & chip - > reg_lock ) ;
old_control = 0x3f - ( cx_read ( AUD_VOL_CTL ) & 0x3f ) ;
v = 0x3f - ( value - > value . integer . value [ 0 ] & 0x3f ) ;
cx_andor ( AUD_VOL_CTL , 0x3f , v ) ;
spin_unlock_irq ( & chip - > reg_lock ) ;
return v ! = old_control ;