summaryrefslogblamecommitdiffstats
path: root/drivers/staging/cx25821/cx25821-audio-upstream.c
blob: 376c953a8786b853d26f7151d1942222e1545ae0 (plain) (tree)














































                                                                                                     

                                                                                


















                                                
                   



















                                                                 
                                                
















                                                                                        

                                                                                     



                                                                                             
                 




                                                                         

                                                                                  
 


                                                                                  




                                                            


                                   
          
          



                                                                  

                                                                        








                                        
         








                                                              









































                                                                                                                                       



























                                                                                                                                       

                                                                            
      
         








                                                                                                 

                                                                               






                                                  

                                        


                                      
                                      






                                                              
                                          





















                                                                                   

                  




                                                                          


                                                                                                               


          

















































                                                                                                                  










                                                                                              

                                                                                     














                                                                                             
         





                                                                          


                                                                                                               


          


























































                                                                                                                      





                                                                          

                                                                                           



                                         
         











                                                                                                              
                                                                                                                  













                                                                                                                      
                                                                                                                  








                                                                          

                                       


























                                                                                                                            
                                                  
     


                                                                                                                

                                             












































                                                                                                                                                               



                                                

                                                                                
 

                                                                            
 




                                                                                  

      
         

                                                

                                                                                         




                                                             
                











                                                                     

                   








                                                                             
                                                                                                      




                      
                                          


         
                                












                                                                                            










                                                                                       
                                                  
             



                                                              
                                                                                   






































































                                                                                                                                                   

                                                               
      
         








                                                                                             
                                                                                      

                                
          







                                                        
         

                                   












                                                                                    


         






                                                                                    


























                                                                                                                     
/*
 *  Driver for the Conexant CX25821 PCIe bridge
 *
 *  Copyright (C) 2009 Conexant Systems Inc. 
 *  Authors  <hiep.huynh@conexant.com>, <shu.lin@conexant.com>
 *
 *  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., 675 Mass Ave, Cambridge, MA 02139, USA.
 */


#include "cx25821-video.h"
#include "cx25821-audio-upstream.h"

#include <linux/fs.h> 
#include <linux/errno.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/syscalls.h>
#include <linux/file.h>
#include <linux/fcntl.h>
#include <linux/delay.h>
#include <asm/uaccess.h>


MODULE_DESCRIPTION("v4l2 driver module for cx25821 based TV cards");
MODULE_AUTHOR("Hiep Huynh <hiep.huynh@conexant.com>");
MODULE_LICENSE("GPL");


static int _intr_msk = FLD_AUD_SRC_RISCI1 | FLD_AUD_SRC_OF | FLD_AUD_SRC_SYNC | FLD_AUD_SRC_OPC_ERR;


int cx25821_sram_channel_setup_upstream_audio(struct cx25821_dev *dev,
						    struct sram_channel *ch,
						    unsigned int bpl, u32 risc)
{
	unsigned int i, lines;
	u32 cdt;

    
	if (ch->cmds_start == 0) {
		cx_write(ch->ptr1_reg, 0);
		cx_write(ch->ptr2_reg, 0);
		cx_write(ch->cnt2_reg, 0);
		cx_write(ch->cnt1_reg, 0);
		return 0;
	} 

	bpl   = (bpl + 7) & ~7; /* alignment */
	cdt   = ch->cdt;
	lines = ch->fifo_size / bpl;
    
    if (lines > 3)	
    {
	lines = 3;
    }
    
	BUG_ON(lines < 2);


	/* write CDT */
	for (i = 0; i < lines; i++) {
		cx_write(cdt + 16*i, ch->fifo_start + bpl*i);
		cx_write(cdt + 16*i +  4, 0);
		cx_write(cdt + 16*i +  8, 0);
		cx_write(cdt + 16*i + 12, 0);
	}

	/* write CMDS */    
    cx_write(ch->cmds_start + 0, risc);         
    
	cx_write(ch->cmds_start +  4, 0);                       
	cx_write(ch->cmds_start +  8, cdt);                     
	cx_write(ch->cmds_start + 12, AUDIO_CDT_SIZE_QW);       
	cx_write(ch->cmds_start + 16, ch->ctrl_start);          
					       
	//IQ size
	cx_write(ch->cmds_start + 20, AUDIO_IQ_SIZE_DW);        

	for (i = 24; i < 80; i += 4)
		cx_write(ch->cmds_start + i, 0);

	/* fill registers */
	cx_write(ch->ptr1_reg, ch->fifo_start);
	cx_write(ch->ptr2_reg, cdt);
	cx_write(ch->cnt2_reg, AUDIO_CDT_SIZE_QW);        
	cx_write(ch->cnt1_reg, AUDIO_CLUSTER_SIZE_QW - 1);         

	return 0;
}


static __le32 *cx25821_risc_field_upstream_audio( struct cx25821_dev *dev, __le32 *rp, 
						  dma_addr_t databuf_phys_addr,
						  unsigned int bpl, int fifo_enable)
{
	unsigned int line;
    struct sram_channel *sram_ch = &dev->sram_channels[dev->_audio_upstream_channel_select];
    int offset = 0;
		
    
	/* scan lines */
	for (line = 0; line < LINES_PER_AUDIO_BUFFER; line++) 
    {      	
		*(rp++) = cpu_to_le32(RISC_READ|RISC_SOL|RISC_EOL|bpl); 
	*(rp++) = cpu_to_le32(databuf_phys_addr + offset);          
		*(rp++) = cpu_to_le32(0); /* bits 63-32 */			 

	// Check if we need to enable the FIFO after the first 3 lines 
	// For the upstream audio channel, the risc engine will enable the FIFO. 
	if ( fifo_enable && line == 2 )
		{
			*(rp++) = RISC_WRITECR;
			*(rp++) = sram_ch->dma_ctl;
			*(rp++) = sram_ch->fld_aud_fifo_en;
			*(rp++) = 0x00000020;
	}        
	
	offset += AUDIO_LINE_SIZE;
	}
	 
	return rp;
}

int cx25821_risc_buffer_upstream_audio( struct cx25821_dev *dev, 
				  struct pci_dev *pci, 
				  unsigned int bpl, unsigned int lines)
{
	__le32 *rp;
    int fifo_enable = 0;
    int frame = 0, i = 0;
    int frame_size = AUDIO_DATA_BUF_SZ;
    int databuf_offset = 0;
    int risc_flag = RISC_CNT_INC;
    dma_addr_t   risc_phys_jump_addr;

	
	/* Virtual address of Risc buffer program */
	rp = dev->_risc_virt_addr;
    
	/* sync instruction */
    *(rp++) = cpu_to_le32(RISC_RESYNC | AUDIO_SYNC_LINE);    
    
    
    for( frame = 0; frame < NUM_AUDIO_FRAMES; frame++ )
    {        
	databuf_offset = frame_size * frame;        
		
	if( frame == 0 )
	{
	    fifo_enable = 1;
	    risc_flag = RISC_CNT_RESET;   
	}
	else
	{
	    fifo_enable = 0;
	    risc_flag = RISC_CNT_INC;
	}
	
	//Calculate physical jump address 
	if( (frame+1) == NUM_AUDIO_FRAMES )
	{
	    risc_phys_jump_addr = dev->_risc_phys_start_addr + RISC_SYNC_INSTRUCTION_SIZE;        
	}
	else
	{
	    risc_phys_jump_addr = dev->_risc_phys_start_addr + RISC_SYNC_INSTRUCTION_SIZE + AUDIO_RISC_DMA_BUF_SIZE*(frame+1);        
	}
	
	rp = cx25821_risc_field_upstream_audio(dev, rp, dev->_audiodata_buf_phys_addr+databuf_offset, bpl, fifo_enable);       
	 
	
	if( USE_RISC_NOOP_AUDIO )
	{
	    for( i = 0; i < NUM_NO_OPS; i++ )
	    {
		*(rp++) = cpu_to_le32(RISC_NOOP);
	    }
	}
	

	// Loop to (Nth)FrameRISC or to Start of Risc program & generate IRQ 
	*(rp++)  = cpu_to_le32(RISC_JUMP|RISC_IRQ1|risc_flag);      
	*(rp++)  = cpu_to_le32(risc_phys_jump_addr);            
	*(rp++)  = cpu_to_le32(0);  

	//Recalculate virtual address based on frame index 
	rp = dev->_risc_virt_addr + RISC_SYNC_INSTRUCTION_SIZE/4 + (AUDIO_RISC_DMA_BUF_SIZE*(frame+1)/4 ) ;         
    }
    
	return 0;
}


void cx25821_free_memory_audio(struct cx25821_dev *dev)
{        
	if (dev->_risc_virt_addr) 
    {
		pci_free_consistent(dev->pci, dev->_audiorisc_size, dev->_risc_virt_addr, dev->_risc_phys_addr);
		dev->_risc_virt_addr = NULL;
	}
    
    if (dev->_audiodata_buf_virt_addr) 
    {
		pci_free_consistent(dev->pci, dev->_audiodata_buf_size, dev->_audiodata_buf_virt_addr, dev->_audiodata_buf_phys_addr);
		dev->_audiodata_buf_virt_addr = NULL;
	}
}

void cx25821_stop_upstream_audio(struct cx25821_dev *dev)
{    
    struct sram_channel *sram_ch = &dev->sram_channels[AUDIO_UPSTREAM_SRAM_CHANNEL_B];  
    u32 tmp = 0;
    
    if( !dev->_audio_is_running )
    {
	printk("cx25821: No audio file is currently running so return!\n");
	return;
    }
	
    //Disable RISC interrupts
    cx_write( sram_ch->int_msk, 0 );
    
    //Turn OFF risc and fifo enable in AUD_DMA_CNTRL
    tmp = cx_read( sram_ch->dma_ctl );
    cx_write( sram_ch->dma_ctl, tmp & ~(sram_ch->fld_aud_fifo_en | sram_ch->fld_aud_risc_en) ); 

    //Clear data buffer memory
    if( dev->_audiodata_buf_virt_addr )
	memset( dev->_audiodata_buf_virt_addr, 0, dev->_audiodata_buf_size ); 
		
    dev->_audio_is_running       = 0;
    dev->_is_first_audio_frame   = 0;
    dev->_audioframe_count       = 0;
    dev->_audiofile_status       = END_OF_FILE;  
    
    if( dev->_irq_audio_queues )
    {
	kfree(dev->_irq_audio_queues); 
	dev->_irq_audio_queues = NULL; 
    }        
    
    if( dev->_audiofilename != NULL )
	kfree(dev->_audiofilename);  
}


void cx25821_free_mem_upstream_audio(struct cx25821_dev *dev)
{
    if( dev->_audio_is_running )
    {
	cx25821_stop_upstream_audio(dev);
    }

    cx25821_free_memory_audio(dev);
}


int cx25821_get_audio_data(struct cx25821_dev *dev, struct sram_channel *sram_ch )
{
    struct file * myfile;    
    int frame_index_temp = dev->_audioframe_index;
    int i = 0;
    int line_size = AUDIO_LINE_SIZE;  
    int frame_size = AUDIO_DATA_BUF_SZ;
    int frame_offset = frame_size * frame_index_temp;           
    ssize_t vfs_read_retval = 0;
    char mybuf[line_size];      
    loff_t file_offset = dev->_audioframe_count * frame_size; 
    loff_t pos;        
    mm_segment_t old_fs;

    
    if( dev->_audiofile_status == END_OF_FILE )   
	return 0;
	
    myfile = filp_open( dev->_audiofilename, O_RDONLY | O_LARGEFILE, 0 );
    
    
    if (IS_ERR(myfile)) 
    {
	const int open_errno = -PTR_ERR(myfile);
	printk("%s(): ERROR opening file(%s) with errno = %d! \n", __func__, dev->_audiofilename, open_errno);
	return PTR_ERR(myfile);
    } 
    else 
    {   
	if( !(myfile->f_op) )
	{            
	    printk("%s: File has no file operations registered!\n", __func__);
	    filp_close(myfile, NULL);
	    return -EIO;
	}
	
	
	if( !myfile->f_op->read )       
	{            
	    printk("%s: File has no READ operations registered! \n", __func__);
	    filp_close(myfile, NULL);
	    return -EIO;
	}
	
	pos = myfile->f_pos;            
	old_fs = get_fs();  
	set_fs(KERNEL_DS);
		
	      
	for( i = 0; i < dev->_audio_lines_count; i++ )
	{
	    pos = file_offset;    
	    
	    vfs_read_retval = vfs_read(myfile, mybuf, line_size, &pos);     
	    
	    
	    if( vfs_read_retval > 0 && vfs_read_retval == line_size && dev->_audiodata_buf_virt_addr != NULL )   
	    {
		memcpy( (void*)(dev->_audiodata_buf_virt_addr+frame_offset/4), mybuf, vfs_read_retval);      
	    }            
	    
	    file_offset += vfs_read_retval;
	    frame_offset += vfs_read_retval;

	    if( vfs_read_retval < line_size )
	    {      
		printk(KERN_INFO "Done: exit %s() since no more bytes to read from Audio file.\n", __func__ );
		break;
	    }      
	}
	
	if( i > 0 )         
	    dev->_audioframe_count++;           

	dev->_audiofile_status = (vfs_read_retval == line_size) ? IN_PROGRESS : END_OF_FILE;      

	
	set_fs(old_fs);                 
	filp_close(myfile, NULL);          
    }
    
    return 0;
}

static void cx25821_audioups_handler(struct work_struct *work)
{
	struct cx25821_dev *dev = container_of(work, struct cx25821_dev, _audio_work_entry); 
       
    if( !dev )
    {
	printk("ERROR %s(): since container_of(work_struct) FAILED! \n", __func__ );
	return;
    }
  
    cx25821_get_audio_data( dev, &dev->sram_channels[dev->_audio_upstream_channel_select] );
}

int cx25821_openfile_audio(struct cx25821_dev *dev, struct sram_channel *sram_ch)
{
    struct file * myfile;    
    int i = 0, j = 0;
    int line_size = AUDIO_LINE_SIZE;   
    ssize_t vfs_read_retval = 0;
    char mybuf[line_size];
    loff_t pos;
    loff_t offset = (unsigned long)0;
    mm_segment_t old_fs;
	

    myfile = filp_open( dev->_audiofilename, O_RDONLY | O_LARGEFILE, 0 );
    
    
    if (IS_ERR(myfile)) 
    {
	const int open_errno = -PTR_ERR(myfile);
	printk("%s(): ERROR opening file(%s) with errno = %d! \n", __func__, dev->_audiofilename, open_errno);
	return PTR_ERR(myfile);
    } 
    else 
    {
	if( !(myfile->f_op) )
	{            
	    printk("%s: File has no file operations registered! \n", __func__);
	    filp_close(myfile, NULL);
	    return -EIO;
	}
	
	
	if( !myfile->f_op->read )
	{            
	    printk("%s: File has no READ operations registered! \n", __func__);
	    filp_close(myfile, NULL);
	    return -EIO;
	}
	
	pos = myfile->f_pos;
	old_fs = get_fs();  
	set_fs(KERNEL_DS);
		
	
	for( j = 0; j < NUM_AUDIO_FRAMES; j++ )       
	{          
	    for( i = 0; i < dev->_audio_lines_count; i++ )
	    {
		pos = offset;

		vfs_read_retval = vfs_read(myfile, mybuf, line_size, &pos);                
		
		
		if( vfs_read_retval > 0 && vfs_read_retval == line_size && dev->_audiodata_buf_virt_addr != NULL )   
		{ 
		    memcpy( (void*)(dev->_audiodata_buf_virt_addr+offset/4), mybuf, vfs_read_retval);   
		}                
		
		offset += vfs_read_retval;

		if( vfs_read_retval < line_size )
		{      
		    printk(KERN_INFO "Done: exit %s() since no more bytes to read from Audio file.\n", __func__ );
		    break;
		}      
	    }
	    
	    if( i > 0 )     
	    {
		dev->_audioframe_count++;
	    }            
	    
	    if( vfs_read_retval < line_size )
	    {
		break;
	    }            
	}
	
	dev->_audiofile_status = (vfs_read_retval == line_size) ? IN_PROGRESS : END_OF_FILE;   
	    
	set_fs(old_fs);        
	myfile->f_pos = 0;
	filp_close(myfile, NULL);         
    }
    
    return 0;
}

static int cx25821_audio_upstream_buffer_prepare(struct cx25821_dev *dev,
							     struct sram_channel *sram_ch,
						 int bpl)
{
	int ret = 0;
	dma_addr_t dma_addr;
	dma_addr_t data_dma_addr;	
	

    cx25821_free_memory_audio(dev);     
    
    dev->_risc_virt_addr       = pci_alloc_consistent(dev->pci, dev->audio_upstream_riscbuf_size, &dma_addr);
    dev->_risc_virt_start_addr = dev->_risc_virt_addr;      
    dev->_risc_phys_start_addr = dma_addr;        
    dev->_risc_phys_addr       = dma_addr;
    dev->_audiorisc_size       = dev->audio_upstream_riscbuf_size;
       
   
    if (!dev->_risc_virt_addr)
    {
	printk("cx25821 ERROR: pci_alloc_consistent() FAILED to allocate memory for RISC program! Returning.\n");
		return -ENOMEM;
    }
  
    //Clear out memory at address
    memset( dev->_risc_virt_addr, 0, dev->_audiorisc_size );

    
    //For Audio Data buffer allocation  
    dev->_audiodata_buf_virt_addr = pci_alloc_consistent(dev->pci, dev->audio_upstream_databuf_size, &data_dma_addr);
    dev->_audiodata_buf_phys_addr = data_dma_addr;
    dev->_audiodata_buf_size      = dev->audio_upstream_databuf_size;
       
    if (!dev->_audiodata_buf_virt_addr)
    {        
	printk("cx25821 ERROR: pci_alloc_consistent() FAILED to allocate memory for data buffer! Returning. \n");
		return -ENOMEM;
    }
  
    //Clear out memory at address
    memset( dev->_audiodata_buf_virt_addr, 0, dev->_audiodata_buf_size );
    
   
    ret = cx25821_openfile_audio(dev, sram_ch);     
    if( ret < 0 )
	return ret;
				      
    
    //Creating RISC programs
    ret = cx25821_risc_buffer_upstream_audio(dev, dev->pci, bpl, dev->_audio_lines_count );                                
	if (ret < 0)
	{
		printk(KERN_DEBUG "cx25821 ERROR creating audio upstream RISC programs! \n");
		goto error;
	}
    
	return 0;

error:
	return ret;
}

int cx25821_audio_upstream_irq(struct cx25821_dev *dev, int chan_num, u32 status)
{
	int i = 0;
    u32 int_msk_tmp;
    struct sram_channel *channel = &dev->sram_channels[chan_num];
    dma_addr_t   risc_phys_jump_addr;
    __le32 * rp;


    if (status & FLD_AUD_SRC_RISCI1)      
    {
	    //Get interrupt_index of the program that interrupted
	u32 prog_cnt = cx_read( channel->gpcnt );
    
	//Since we've identified our IRQ, clear our bits from the interrupt mask and interrupt status registers
	cx_write(channel->int_msk, 0);  
	cx_write(channel->int_stat, cx_read(channel->int_stat) );
    
		spin_lock(&dev->slock);     
	    
	     
	while(prog_cnt != dev->_last_index_irq)
	{            
	    //Update _last_index_irq
	    if(dev->_last_index_irq < (NUMBER_OF_PROGRAMS-1))
	    {
		dev->_last_index_irq++;
	    }
	    else
	    {
		dev->_last_index_irq = 0;
	    }

	    dev->_audioframe_index = dev->_last_index_irq;
	    
	    queue_work(dev->_irq_audio_queues, &dev->_audio_work_entry);        
	}

    
	if ( dev->_is_first_audio_frame )
	{
	    dev->_is_first_audio_frame = 0;           
	    
	    if( dev->_risc_virt_start_addr != NULL )
	    {
		risc_phys_jump_addr = dev->_risc_phys_start_addr + RISC_SYNC_INSTRUCTION_SIZE + AUDIO_RISC_DMA_BUF_SIZE;        
		
		rp = cx25821_risc_field_upstream_audio(dev, dev->_risc_virt_start_addr+1, dev->_audiodata_buf_phys_addr, AUDIO_LINE_SIZE, FIFO_DISABLE);      

		if( USE_RISC_NOOP_AUDIO )
		{
		    for( i = 0; i < NUM_NO_OPS; i++ )
		    {
			*(rp++) = cpu_to_le32(RISC_NOOP);
		    }
		}
	
		// Jump to 2nd Audio Frame
		*(rp++)  = cpu_to_le32(RISC_JUMP | RISC_IRQ1 | RISC_CNT_RESET);      
		*(rp++)  = cpu_to_le32(risc_phys_jump_addr);            
		*(rp++)  = cpu_to_le32(0); 
	    }
	}
	
		spin_unlock(&dev->slock);      
	}
    else
    {
	if(status & FLD_AUD_SRC_OF)
	    printk("%s: Audio Received Overflow Error Interrupt!\n", __func__);

	if(status & FLD_AUD_SRC_SYNC)
	    printk("%s: Audio Received Sync Error Interrupt!\n", __func__);

	if(status & FLD_AUD_SRC_OPC_ERR)
	    printk("%s: Audio Received OpCode Error Interrupt!\n", __func__);    
	    
	// Read and write back the interrupt status register to clear our bits
	cx_write(channel->int_stat, cx_read(channel->int_stat) );   
    }
     
	
    if( dev->_audiofile_status == END_OF_FILE )
    {                       
	printk("cx25821: EOF Channel Audio Framecount = %d\n", dev->_audioframe_count );
	return -1;
    }
    
    //ElSE, set the interrupt mask register, re-enable irq. 
    int_msk_tmp = cx_read( channel->int_msk );              
    cx_write( channel->int_msk, int_msk_tmp |= _intr_msk ); 
	       
    return 0;
}

static irqreturn_t cx25821_upstream_irq_audio(int irq, void *dev_id)
{
	struct cx25821_dev *dev = dev_id;
	u32 msk_stat, audio_status;
	int handled = 0;    
    struct sram_channel *sram_ch;
    
    
    if( !dev )
	return -1;
	

    sram_ch = &dev->sram_channels[dev->_audio_upstream_channel_select];     

    msk_stat   = cx_read(sram_ch->int_mstat);     
    audio_status = cx_read(sram_ch->int_stat); 
    
    // Only deal with our interrupt
    if(audio_status)
    {
	handled = cx25821_audio_upstream_irq(dev, dev->_audio_upstream_channel_select, audio_status);
    }
    
    
    if( handled < 0 )
    {    
	cx25821_stop_upstream_audio(dev);
    }
    else
    {
	handled += handled;    
    }   

	return IRQ_RETVAL(handled);
}


static void cx25821_wait_fifo_enable(struct cx25821_dev *dev, struct sram_channel *sram_ch)
{
    int count = 0;
	u32 tmp;
    
    do
    {
	//Wait 10 microsecond before checking to see if the FIFO is turned ON.
	udelay(10);

	tmp = cx_read( sram_ch->dma_ctl );
	
	if(count++ > 1000) //10 millisecond timeout         
	{
	    printk("cx25821 ERROR: %s() fifo is NOT turned on. Timeout!\n", __func__);
	    return;
	}
	
    } while( !(tmp & sram_ch->fld_aud_fifo_en) );
	    
}


int cx25821_start_audio_dma_upstream(struct cx25821_dev *dev,
						     struct sram_channel *sram_ch)
{
    u32 tmp = 0;
    int err = 0;
    
    
    // Set the physical start address of the RISC program in the initial program counter(IPC) member of the CMDS.
    cx_write(sram_ch->cmds_start + 0, dev->_risc_phys_addr);
    cx_write(sram_ch->cmds_start +  4, 0); /* Risc IPC High 64 bits 63-32 */

    
	/* reset counter */
	cx_write(sram_ch->gpcnt_ctl, 3); 
    
    //Set the line length	(It looks like we do not need to set the line length)
	cx_write(sram_ch->aud_length, AUDIO_LINE_SIZE & FLD_AUD_DST_LN_LNGTH); 

    //Set the input mode to 16-bit
    tmp = cx_read( sram_ch->aud_cfg );
	tmp |= FLD_AUD_SRC_ENABLE | FLD_AUD_DST_PK_MODE | FLD_AUD_CLK_ENABLE | FLD_AUD_MASTER_MODE | FLD_AUD_CLK_SELECT_PLL_D | FLD_AUD_SONY_MODE;
    cx_write( sram_ch->aud_cfg, tmp );
    
    // Read and write back the interrupt status register to clear it
    tmp = cx_read( sram_ch->int_stat);
    cx_write( sram_ch->int_stat, tmp);

    // Clear our bits from the interrupt status register.  
    cx_write( sram_ch->int_stat, _intr_msk );       

    
    //Set the interrupt mask register, enable irq.  
    cx_set(PCI_INT_MSK, cx_read(PCI_INT_MSK) | (1 << sram_ch->irq_bit));
    tmp = cx_read( sram_ch->int_msk );
    cx_write( sram_ch->int_msk, tmp |= _intr_msk );    
    
    
    err = request_irq(dev->pci->irq, cx25821_upstream_irq_audio, IRQF_SHARED | IRQF_DISABLED, dev->name, dev);
	if (err < 0) 
	{
		printk(KERN_ERR "%s: can't get upstream IRQ %d\n", dev->name, dev->pci->irq);
		goto fail_irq;
	} 
    
    
    // Start the DMA  engine
    tmp = cx_read( sram_ch->dma_ctl );
    cx_set( sram_ch->dma_ctl, tmp | sram_ch->fld_aud_risc_en );  

    dev->_audio_is_running = 1; 
    dev->_is_first_audio_frame = 1;          
    
    // The fifo_en bit turns on by the first Risc program
    cx25821_wait_fifo_enable(dev, sram_ch);          

	return 0;
    

fail_irq:
	cx25821_dev_unregister(dev);  
    return err;
}


int cx25821_audio_upstream_init(struct cx25821_dev *dev, int channel_select)
{
    struct sram_channel *sram_ch; 
    int retval = 0;
    int err = 0;    
    int str_length = 0;
    
    if( dev->_audio_is_running )
    {
	printk("Audio Channel is still running so return!\n");
	return 0;
    }
	
    dev->_audio_upstream_channel_select = channel_select;      
    sram_ch = &dev->sram_channels[channel_select];   
    
    //Work queue
	INIT_WORK(&dev->_audio_work_entry, cx25821_audioups_handler);
	dev->_irq_audio_queues = create_singlethread_workqueue("cx25821_audioworkqueue");   

	if(!dev->_irq_audio_queues)
    {
	printk("cx25821 ERROR: create_singlethread_workqueue() for Audio FAILED!\n");
		return -ENOMEM;
    }
	 
    
    dev->_last_index_irq     = 0;        
    dev->_audio_is_running   = 0;    
    dev->_audioframe_count   = 0;
    dev->_audiofile_status   = RESET_STATUS;  
    dev->_audio_lines_count  = LINES_PER_AUDIO_BUFFER; 
    _line_size          = AUDIO_LINE_SIZE; 

	
    if( dev->input_audiofilename )
    {
	str_length = strlen(dev->input_audiofilename);
	dev->_audiofilename = (char *) kmalloc(str_length + 1, GFP_KERNEL);        
	
	if( !dev->_audiofilename )
	    goto error;
	    
	memcpy(dev->_audiofilename, dev->input_audiofilename, str_length + 1);  

	//Default if filename is empty string
	if( strcmp(dev->input_audiofilename,"") == 0)        
	{
	    dev->_audiofilename = "/root/audioGOOD.wav";    
	}         
    }
    else
    {
	str_length = strlen(_defaultAudioName);       
	dev->_audiofilename = (char *) kmalloc(str_length + 1, GFP_KERNEL);        
	
	if( !dev->_audiofilename )
	    goto error;
	    
	memcpy(dev->_audiofilename, _defaultAudioName, str_length + 1);
    }  
     
    
    retval = cx25821_sram_channel_setup_upstream_audio(dev, sram_ch, _line_size, 0);      

    dev->audio_upstream_riscbuf_size = AUDIO_RISC_DMA_BUF_SIZE * NUM_AUDIO_PROGS + RISC_SYNC_INSTRUCTION_SIZE;      
    dev->audio_upstream_databuf_size = AUDIO_DATA_BUF_SZ * NUM_AUDIO_PROGS;       

     
    //Allocating buffers and prepare RISC program
    retval = cx25821_audio_upstream_buffer_prepare(dev, sram_ch, _line_size);
	if (retval < 0) 
	{
		printk(KERN_ERR "%s: Failed to set up Audio upstream buffers!\n", dev->name);
		goto error;
	}
    
    //Start RISC engine 
    cx25821_start_audio_dma_upstream(dev, sram_ch);                                     
    
    return 0;
    
error:
	cx25821_dev_unregister(dev);
    
    return err;
}