/include/linux/spi/

2019'/>
path: root/drivers/media/pci/cx25821/cx25821-core.c
blob: 04aa4a68a0aefa11132182e56d7231122158b93e (plain) (tree)
1
2
3
4


                                               
                                            












                                                                        

   

                                           
                      
                       







                                              



                                                 
                                                                        
                                          

                                    
                                                     
                       



















                                                     

                       



















                                                     

                       



















                                                     

                       



















                                                     

                       



















                                                     

                       



















                                                     

                       



















                                                     

                       



















                                                     

                       










                                                    

                       























                                                         

                       























                                                         

                       






















                                                         
  
                                     
 

                                        
                                               










                                                 
                                     







                                         
                                            






                                             

                                                                         

                                                     
                                                
         
                                               
                                                       



                                                                 


                                                      

 

                                                           
                
 
                                      
                                   
 


                                                                             
                                          
 
                                  
                                                                     
                                  
 
                                                      
                                             
 
                                                       
                                                   
 
                                  

                                                   
 
                                                            
                                             
 
                                                      
                                                   
 
                                  

                                                   
 
                                                      
                                             
 
                                                      
                                                   
 
                                  

                                                   
 
                                                      
                                             
 
                                                       
                                                   
 
                                  

                                                   
 

                                                                             

                                                                  
 


                                                                            
                                       
                                                           
                                                    
 
                                                
                               

                                                                         
                                                                          
 

                    
 
                                                       
                                                             











                                                          
 


                                               
 
                      
                          
 













                                                                 
                                       



                                                    
                         
                                                
            
                                                   


















                                                                      
 
                 


                                                             
                                                                   
                                                                
 














                                               

                                               
 














                                                                 
                         
                                                
            
                                                   





                                                                   

                         
                                                                      
            
                                                       
 
                      







                                                  
 
                 
 
                                                
 
                                                                                      
 


















                                
                                                                           
                                              

                                                                         



                                                              
                                                                  






                                                       

                                                          


                                                                     

                                                                               
                 
         
 











                                                                

 
                                                             
                                                                   
 
                                            














                                

                             
                             
 

                                                             
 
                                              


                                                              
 


                                                              
                                                                  

                                               
 


                                                       
 

                                                          
                                              
 

                                                                     

                                                                               

                 
 











                                                                


                                                              
                                                            
         
 
                                                 
                                  
                                               

                                          
                                    
         
                        

                                 
                                                   

                                              
                                                                
                                        
                                                          

                                              
                                                                
                                        
                                                          

                                              
                                                                
                                        
                                                          

                                              
                                                                
                                        
                                                          

                                                                           
                                                                         
 
                                               


                                                     
              
 

                                     
 

                                               

                                                                     
         
 



                                                                     
         
 

                                     
 

                                 
 


                                   

 

                                                                          
 
                                                         

                                                                               
         
                                                             

 
                                                         
                                                               
 

                                                 



                                                       
              
 
                                       
 

                                           
 
                                             
                                                                               
 




                                             
 
                                                             

                                                                  
                    
 
                                               



                                                                               
                                                        
         
 
                                             

                                                        
                                                                
                                                                          
         
 
                                             
                                                                        
 
                               

 
                                                         
 

                                                                          
                         
 
                                                      
                                                                                
 
                      

 

                                                              
                                                   
 
                                                                 



                                                    




                                 


                                    

 
                                                     
 
                                             
              
 

                               


                                                   
                                         

                                                                              
                               
         
                                                                       
 

                                                                  


                                                   
                                                                           
         
 
                        
                                             
 
                          
                                                    















                                                                     
                                             
                                                                               



                                                             
                              
         
 

                                                            
 



                                                                   
 
                                                                               
 
                          
                                                                                              




                                               
 




                                                                            
 

                                
 
                                               

                                              
 
                                       
                                                                              
 
                                    
 
                                       
                 
 
 

                                                    
              
 

                               
 
                                                                             
 
                                                           

                                                       


                                                             

                                                                     
                   

                                                 
 

                                                 
 
                                      
 




















                                                                           


                                                                          
                                                                
 


                                





                                                          
                              
                                      
                                                               
 




                                                            
                                         


                                                     

                                                                                





                                                                           
                                                                     




                                                                           
                                         
                                                       

                                                                  


                                                                                
                                                 








                                                                            
 
                  

 
                                                                          


                                                                            
 











                                                                       
                                                                          


                                                                         

                                                                            
                          
                                                                 








                                                                                
                                                     
         
 

                                                                              
                                                                             

         

                                                     
                                                                              




















                                                                                
                                         








                                                                  

                                                                           






                                                                           
                                                                   



                                                                           
                                         

                                                                  
                                                                


                                                                                
                                                 






                                                                            
         
 
                  


                                                      
                                                               


                                                                       
 











                                                                            
                                                                 
                   










                                                                              
 
                                             
 
                                                                             
 
                               


                                         
                                                                      
                                                 

 

                                                     
                                         
                       


                                                      
 
                                           
 

                            
 

                                               

                                                              
 
                                       

                                                                    
 

                                                        
         
 
    
                                   


                                                                 
                                                       
 

                       
                                                                    




                                       
                                                   
                    
                                          

                                       
                             
         
                      
 
                                     
 
                                                        
 

                                                       
 
                               
 

                                                              
 

                                
 


                                                
 


                                                                  
 



                                         
 
                                                
 

                                            
 
                                     
                
                                         
 


                                                                         


                                                                                   

                                

                                                    
                                                                        


                              
 

                                                     
 
                      
                                                                          

                              
 
                 
 
         
                                                       
                                    
 

                                    
                       
                                               
 
          

                   

 
                                                    
 

                                                                
 

                                    
 


                                            
 


                                         

 
                                                       
         




                                    






                                      

                                         



                                          



                                               
                                  


                        

  
                                    
 
                                   
                                                        

 
                                     
 
                                                   

 

                          
/*
 *  Driver for the Conexant CX25821 PCIe bridge
 *
 *  Copyright (C) 2009 Conexant Systems Inc.
 *  Authors  <shu.lin@conexant.com>, <hiep.huynh@conexant.com>
 *  Based on Steven Toth <stoth@linuxtv.org> cx23885 driver
 *
 *  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.
 */

#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt

#include <linux/i2c.h>
#include <linux/slab.h>
#include "cx25821.h"
#include "cx25821-sram.h"
#include "cx25821-video.h"

MODULE_DESCRIPTION("Driver for Athena cards");
MODULE_AUTHOR("Shu Lin - Hiep Huynh");
MODULE_LICENSE("GPL");

static unsigned int debug;
module_param(debug, int, 0644);
MODULE_PARM_DESC(debug, "enable debug messages");

static unsigned int card[] = {[0 ... (CX25821_MAXBOARDS - 1)] = UNSET };
module_param_array(card, int, NULL, 0444);
MODULE_PARM_DESC(card, "card type");

const struct sram_channel cx25821_sram_channels[] = {
	[SRAM_CH00] = {
		.i = SRAM_CH00,
		.name = "VID A",
		.cmds_start = VID_A_DOWN_CMDS,
		.ctrl_start = VID_A_IQ,
		.cdt = VID_A_CDT,
		.fifo_start = VID_A_DOWN_CLUSTER_1,
		.fifo_size = (VID_CLUSTER_SIZE << 2),
		.ptr1_reg = DMA1_PTR1,
		.ptr2_reg = DMA1_PTR2,
		.cnt1_reg = DMA1_CNT1,
		.cnt2_reg = DMA1_CNT2,
		.int_msk = VID_A_INT_MSK,
		.int_stat = VID_A_INT_STAT,
		.int_mstat = VID_A_INT_MSTAT,
		.dma_ctl = VID_DST_A_DMA_CTL,
		.gpcnt_ctl = VID_DST_A_GPCNT_CTL,
		.gpcnt = VID_DST_A_GPCNT,
		.vip_ctl = VID_DST_A_VIP_CTL,
		.pix_frmt = VID_DST_A_PIX_FRMT,
	},

	[SRAM_CH01] = {
		.i = SRAM_CH01,
		.name = "VID B",
		.cmds_start = VID_B_DOWN_CMDS,
		.ctrl_start = VID_B_IQ,
		.cdt = VID_B_CDT,
		.fifo_start = VID_B_DOWN_CLUSTER_1,
		.fifo_size = (VID_CLUSTER_SIZE << 2),
		.ptr1_reg = DMA2_PTR1,
		.ptr2_reg = DMA2_PTR2,
		.cnt1_reg = DMA2_CNT1,
		.cnt2_reg = DMA2_CNT2,
		.int_msk = VID_B_INT_MSK,
		.int_stat = VID_B_INT_STAT,
		.int_mstat = VID_B_INT_MSTAT,
		.dma_ctl = VID_DST_B_DMA_CTL,
		.gpcnt_ctl = VID_DST_B_GPCNT_CTL,
		.gpcnt = VID_DST_B_GPCNT,
		.vip_ctl = VID_DST_B_VIP_CTL,
		.pix_frmt = VID_DST_B_PIX_FRMT,
	},

	[SRAM_CH02] = {
		.i = SRAM_CH02,
		.name = "VID C",
		.cmds_start = VID_C_DOWN_CMDS,
		.ctrl_start = VID_C_IQ,
		.cdt = VID_C_CDT,
		.fifo_start = VID_C_DOWN_CLUSTER_1,
		.fifo_size = (VID_CLUSTER_SIZE << 2),
		.ptr1_reg = DMA3_PTR1,
		.ptr2_reg = DMA3_PTR2,
		.cnt1_reg = DMA3_CNT1,
		.cnt2_reg = DMA3_CNT2,
		.int_msk = VID_C_INT_MSK,
		.int_stat = VID_C_INT_STAT,
		.int_mstat = VID_C_INT_MSTAT,
		.dma_ctl = VID_DST_C_DMA_CTL,
		.gpcnt_ctl = VID_DST_C_GPCNT_CTL,
		.gpcnt = VID_DST_C_GPCNT,
		.vip_ctl = VID_DST_C_VIP_CTL,
		.pix_frmt = VID_DST_C_PIX_FRMT,
	},

	[SRAM_CH03] = {
		.i = SRAM_CH03,
		.name = "VID D",
		.cmds_start = VID_D_DOWN_CMDS,
		.ctrl_start = VID_D_IQ,
		.cdt = VID_D_CDT,
		.fifo_start = VID_D_DOWN_CLUSTER_1,
		.fifo_size = (VID_CLUSTER_SIZE << 2),
		.ptr1_reg = DMA4_PTR1,
		.ptr2_reg = DMA4_PTR2,
		.cnt1_reg = DMA4_CNT1,
		.cnt2_reg = DMA4_CNT2,
		.int_msk = VID_D_INT_MSK,
		.int_stat = VID_D_INT_STAT,
		.int_mstat = VID_D_INT_MSTAT,
		.dma_ctl = VID_DST_D_DMA_CTL,
		.gpcnt_ctl = VID_DST_D_GPCNT_CTL,
		.gpcnt = VID_DST_D_GPCNT,
		.vip_ctl = VID_DST_D_VIP_CTL,
		.pix_frmt = VID_DST_D_PIX_FRMT,
	},

	[SRAM_CH04] = {
		.i = SRAM_CH04,
		.name = "VID E",
		.cmds_start = VID_E_DOWN_CMDS,
		.ctrl_start = VID_E_IQ,
		.cdt = VID_E_CDT,
		.fifo_start = VID_E_DOWN_CLUSTER_1,
		.fifo_size = (VID_CLUSTER_SIZE << 2),
		.ptr1_reg = DMA5_PTR1,
		.ptr2_reg = DMA5_PTR2,
		.cnt1_reg = DMA5_CNT1,
		.cnt2_reg = DMA5_CNT2,
		.int_msk = VID_E_INT_MSK,
		.int_stat = VID_E_INT_STAT,
		.int_mstat = VID_E_INT_MSTAT,
		.dma_ctl = VID_DST_E_DMA_CTL,
		.gpcnt_ctl = VID_DST_E_GPCNT_CTL,
		.gpcnt = VID_DST_E_GPCNT,
		.vip_ctl = VID_DST_E_VIP_CTL,
		.pix_frmt = VID_DST_E_PIX_FRMT,
	},

	[SRAM_CH05] = {
		.i = SRAM_CH05,
		.name = "VID F",
		.cmds_start = VID_F_DOWN_CMDS,
		.ctrl_start = VID_F_IQ,
		.cdt = VID_F_CDT,
		.fifo_start = VID_F_DOWN_CLUSTER_1,
		.fifo_size = (VID_CLUSTER_SIZE << 2),
		.ptr1_reg = DMA6_PTR1,
		.ptr2_reg = DMA6_PTR2,
		.cnt1_reg = DMA6_CNT1,
		.cnt2_reg = DMA6_CNT2,
		.int_msk = VID_F_INT_MSK,
		.int_stat = VID_F_INT_STAT,
		.int_mstat = VID_F_INT_MSTAT,
		.dma_ctl = VID_DST_F_DMA_CTL,
		.gpcnt_ctl = VID_DST_F_GPCNT_CTL,
		.gpcnt = VID_DST_F_GPCNT,
		.vip_ctl = VID_DST_F_VIP_CTL,
		.pix_frmt = VID_DST_F_PIX_FRMT,
	},

	[SRAM_CH06] = {
		.i = SRAM_CH06,
		.name = "VID G",
		.cmds_start = VID_G_DOWN_CMDS,
		.ctrl_start = VID_G_IQ,
		.cdt = VID_G_CDT,
		.fifo_start = VID_G_DOWN_CLUSTER_1,
		.fifo_size = (VID_CLUSTER_SIZE << 2),
		.ptr1_reg = DMA7_PTR1,
		.ptr2_reg = DMA7_PTR2,
		.cnt1_reg = DMA7_CNT1,
		.cnt2_reg = DMA7_CNT2,
		.int_msk = VID_G_INT_MSK,
		.int_stat = VID_G_INT_STAT,
		.int_mstat = VID_G_INT_MSTAT,
		.dma_ctl = VID_DST_G_DMA_CTL,
		.gpcnt_ctl = VID_DST_G_GPCNT_CTL,
		.gpcnt = VID_DST_G_GPCNT,
		.vip_ctl = VID_DST_G_VIP_CTL,
		.pix_frmt = VID_DST_G_PIX_FRMT,
	},

	[SRAM_CH07] = {
		.i = SRAM_CH07,
		.name = "VID H",
		.cmds_start = VID_H_DOWN_CMDS,
		.ctrl_start = VID_H_IQ,
		.cdt = VID_H_CDT,
		.fifo_start = VID_H_DOWN_CLUSTER_1,
		.fifo_size = (VID_CLUSTER_SIZE << 2),
		.ptr1_reg = DMA8_PTR1,
		.ptr2_reg = DMA8_PTR2,
		.cnt1_reg = DMA8_CNT1,
		.cnt2_reg = DMA8_CNT2,
		.int_msk = VID_H_INT_MSK,
		.int_stat = VID_H_INT_STAT,
		.int_mstat = VID_H_INT_MSTAT,
		.dma_ctl = VID_DST_H_DMA_CTL,
		.gpcnt_ctl = VID_DST_H_GPCNT_CTL,
		.gpcnt = VID_DST_H_GPCNT,
		.vip_ctl = VID_DST_H_VIP_CTL,
		.pix_frmt = VID_DST_H_PIX_FRMT,
	},

	[SRAM_CH08] = {
		.name = "audio from",
		.cmds_start = AUD_A_DOWN_CMDS,
		.ctrl_start = AUD_A_IQ,
		.cdt = AUD_A_CDT,
		.fifo_start = AUD_A_DOWN_CLUSTER_1,
		.fifo_size = AUDIO_CLUSTER_SIZE * 3,
		.ptr1_reg = DMA17_PTR1,
		.ptr2_reg = DMA17_PTR2,
		.cnt1_reg = DMA17_CNT1,
		.cnt2_reg = DMA17_CNT2,
	},

	[SRAM_CH09] = {
		.i = SRAM_CH09,
		.name = "VID Upstream I",
		.cmds_start = VID_I_UP_CMDS,
		.ctrl_start = VID_I_IQ,
		.cdt = VID_I_CDT,
		.fifo_start = VID_I_UP_CLUSTER_1,
		.fifo_size = (VID_CLUSTER_SIZE << 2),
		.ptr1_reg = DMA15_PTR1,
		.ptr2_reg = DMA15_PTR2,
		.cnt1_reg = DMA15_CNT1,
		.cnt2_reg = DMA15_CNT2,
		.int_msk = VID_I_INT_MSK,
		.int_stat = VID_I_INT_STAT,
		.int_mstat = VID_I_INT_MSTAT,
		.dma_ctl = VID_SRC_I_DMA_CTL,
		.gpcnt_ctl = VID_SRC_I_GPCNT_CTL,
		.gpcnt = VID_SRC_I_GPCNT,

		.vid_fmt_ctl = VID_SRC_I_FMT_CTL,
		.vid_active_ctl1 = VID_SRC_I_ACTIVE_CTL1,
		.vid_active_ctl2 = VID_SRC_I_ACTIVE_CTL2,
		.vid_cdt_size = VID_SRC_I_CDT_SZ,
		.irq_bit = 8,
	},

	[SRAM_CH10] = {
		.i = SRAM_CH10,
		.name = "VID Upstream J",
		.cmds_start = VID_J_UP_CMDS,
		.ctrl_start = VID_J_IQ,
		.cdt = VID_J_CDT,
		.fifo_start = VID_J_UP_CLUSTER_1,
		.fifo_size = (VID_CLUSTER_SIZE << 2),
		.ptr1_reg = DMA16_PTR1,
		.ptr2_reg = DMA16_PTR2,
		.cnt1_reg = DMA16_CNT1,
		.cnt2_reg = DMA16_CNT2,
		.int_msk = VID_J_INT_MSK,
		.int_stat = VID_J_INT_STAT,
		.int_mstat = VID_J_INT_MSTAT,
		.dma_ctl = VID_SRC_J_DMA_CTL,
		.gpcnt_ctl = VID_SRC_J_GPCNT_CTL,
		.gpcnt = VID_SRC_J_GPCNT,

		.vid_fmt_ctl = VID_SRC_J_FMT_CTL,
		.vid_active_ctl1 = VID_SRC_J_ACTIVE_CTL1,
		.vid_active_ctl2 = VID_SRC_J_ACTIVE_CTL2,
		.vid_cdt_size = VID_SRC_J_CDT_SZ,
		.irq_bit = 9,
	},

	[SRAM_CH11] = {
		.i = SRAM_CH11,
		.name = "Audio Upstream Channel B",
		.cmds_start = AUD_B_UP_CMDS,
		.ctrl_start = AUD_B_IQ,
		.cdt = AUD_B_CDT,
		.fifo_start = AUD_B_UP_CLUSTER_1,
		.fifo_size = (AUDIO_CLUSTER_SIZE * 3),
		.ptr1_reg = DMA22_PTR1,
		.ptr2_reg = DMA22_PTR2,
		.cnt1_reg = DMA22_CNT1,
		.cnt2_reg = DMA22_CNT2,
		.int_msk = AUD_B_INT_MSK,
		.int_stat = AUD_B_INT_STAT,
		.int_mstat = AUD_B_INT_MSTAT,
		.dma_ctl = AUD_INT_DMA_CTL,
		.gpcnt_ctl = AUD_B_GPCNT_CTL,
		.gpcnt = AUD_B_GPCNT,
		.aud_length = AUD_B_LNGTH,
		.aud_cfg = AUD_B_CFG,
		.fld_aud_fifo_en = FLD_AUD_SRC_B_FIFO_EN,
		.fld_aud_risc_en = FLD_AUD_SRC_B_RISC_EN,
		.irq_bit = 11,
	},
};
EXPORT_SYMBOL(cx25821_sram_channels);

static int cx25821_risc_decode(u32 risc)
{
	static const char * const instr[16] = {
		[RISC_SYNC >> 28] = "sync",
		[RISC_WRITE >> 28] = "write",
		[RISC_WRITEC >> 28] = "writec",
		[RISC_READ >> 28] = "read",
		[RISC_READC >> 28] = "readc",
		[RISC_JUMP >> 28] = "jump",
		[RISC_SKIP >> 28] = "skip",
		[RISC_WRITERM >> 28] = "writerm",
		[RISC_WRITECM >> 28] = "writecm",
		[RISC_WRITECR >> 28] = "writecr",
	};
	static const int incr[16] = {
		[RISC_WRITE >> 28] = 3,
		[RISC_JUMP >> 28] = 3,
		[RISC_SKIP >> 28] = 1,
		[RISC_SYNC >> 28] = 1,
		[RISC_WRITERM >> 28] = 3,
		[RISC_WRITECM >> 28] = 3,
		[RISC_WRITECR >> 28] = 4,
	};
	static const char * const bits[] = {
		"12", "13", "14", "resync",
		"cnt0", "cnt1", "18", "19",
		"20", "21", "22", "23",
		"irq1", "irq2", "eol", "sol",
	};
	int i;

	pr_cont("0x%08x [ %s",
		risc, instr[risc >> 28] ? instr[risc >> 28] : "INVALID");
	for (i = ARRAY_SIZE(bits) - 1; i >= 0; i--) {
		if (risc & (1 << (i + 12)))
			pr_cont(" %s", bits[i]);
	}
	pr_cont(" count=%d ]\n", risc & 0xfff);
	return incr[risc >> 28] ? incr[risc >> 28] : 1;
}

static inline int i2c_slave_did_ack(struct i2c_adapter *i2c_adap)
{
	struct cx25821_i2c *bus = i2c_adap->algo_data;
	struct cx25821_dev *dev = bus->dev;
	return cx_read(bus->reg_stat) & 0x01;
}

static void cx25821_registers_init(struct cx25821_dev *dev)
{
	u32 tmp;

	/* enable RUN_RISC in Pecos */
	cx_write(DEV_CNTRL2, 0x20);

	/* Set the master PCI interrupt masks to enable video, audio, MBIF,
	 * and GPIO interrupts
	 * I2C interrupt masking is handled by the I2C objects themselves. */
	cx_write(PCI_INT_MSK, 0x2001FFFF);

	tmp = cx_read(RDR_TLCTL0);
	tmp &= ~FLD_CFG_RCB_CK_EN;	/* Clear the RCB_CK_EN bit */
	cx_write(RDR_TLCTL0, tmp);

	/* PLL-A setting for the Audio Master Clock */
	cx_write(PLL_A_INT_FRAC, 0x9807A58B);

	/* PLL_A_POST = 0x1C, PLL_A_OUT_TO_PIN = 0x1 */
	cx_write(PLL_A_POST_STAT_BIST, 0x8000019C);

	/* clear reset bit [31] */
	tmp = cx_read(PLL_A_INT_FRAC);
	cx_write(PLL_A_INT_FRAC, tmp & 0x7FFFFFFF);

	/* PLL-B setting for Mobilygen Host Bus Interface */
	cx_write(PLL_B_INT_FRAC, 0x9883A86F);

	/* PLL_B_POST = 0xD, PLL_B_OUT_TO_PIN = 0x0 */
	cx_write(PLL_B_POST_STAT_BIST, 0x8000018D);

	/* clear reset bit [31] */
	tmp = cx_read(PLL_B_INT_FRAC);
	cx_write(PLL_B_INT_FRAC, tmp & 0x7FFFFFFF);

	/* PLL-C setting for video upstream channel */
	cx_write(PLL_C_INT_FRAC, 0x96A0EA3F);

	/* PLL_C_POST = 0x3, PLL_C_OUT_TO_PIN = 0x0 */
	cx_write(PLL_C_POST_STAT_BIST, 0x80000103);

	/* clear reset bit [31] */
	tmp = cx_read(PLL_C_INT_FRAC);
	cx_write(PLL_C_INT_FRAC, tmp & 0x7FFFFFFF);

	/* PLL-D setting for audio upstream channel */
	cx_write(PLL_D_INT_FRAC, 0x98757F5B);

	/* PLL_D_POST = 0x13, PLL_D_OUT_TO_PIN = 0x0 */
	cx_write(PLL_D_POST_STAT_BIST, 0x80000113);

	/* clear reset bit [31] */
	tmp = cx_read(PLL_D_INT_FRAC);
	cx_write(PLL_D_INT_FRAC, tmp & 0x7FFFFFFF);

	/* This selects the PLL C clock source for the video upstream channel
	 * I and J */
	tmp = cx_read(VID_CH_CLK_SEL);
	cx_write(VID_CH_CLK_SEL, (tmp & 0x00FFFFFF) | 0x24000000);

	/* 656/VIP SRC Upstream Channel I & J and 7 - Host Bus Interface for
	 * channel A-C
	 * select 656/VIP DST for downstream Channel A - C */
	tmp = cx_read(VID_CH_MODE_SEL);
	/* cx_write( VID_CH_MODE_SEL, tmp | 0x1B0001FF); */
	cx_write(VID_CH_MODE_SEL, tmp & 0xFFFFFE00);

	/* enables 656 port I and J as output */
	tmp = cx_read(CLK_RST);
	/* use external ALT_PLL_REF pin as its reference clock instead */
	tmp |= FLD_USE_ALT_PLL_REF;
	cx_write(CLK_RST, tmp & ~(FLD_VID_I_CLK_NOE | FLD_VID_J_CLK_NOE));

	mdelay(100);
}

int cx25821_sram_channel_setup(struct cx25821_dev *dev,
			       const 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 > 4)
		lines = 4;

	BUG_ON(lines < 2);

	cx_write(8 + 0, RISC_JUMP | RISC_IRQ1 | RISC_CNT_INC);
	cx_write(8 + 4, 8);
	cx_write(8 + 8, 0);

	/* 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);
	}

	/* init the first cdt buffer */
	for (i = 0; i < 128; i++)
		cx_write(ch->fifo_start + 4 * i, i);

	/* write CMDS */
	if (ch->jumponly)
		cx_write(ch->cmds_start + 0, 8);
	else
		cx_write(ch->cmds_start + 0, risc);

	cx_write(ch->cmds_start + 4, 0);	/* 64 bits 63-32 */
	cx_write(ch->cmds_start + 8, cdt);
	cx_write(ch->cmds_start + 12, (lines * 16) >> 3);
	cx_write(ch->cmds_start + 16, ch->ctrl_start);

	if (ch->jumponly)
		cx_write(ch->cmds_start + 20, 0x80000000 | (64 >> 2));
	else
		cx_write(ch->cmds_start + 20, 64 >> 2);

	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, (lines * 16) >> 3);
	cx_write(ch->cnt1_reg, (bpl >> 3) - 1);

	return 0;
}

int cx25821_sram_channel_setup_audio(struct cx25821_dev *dev,
				     const 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;	/* for AUDIO */

	BUG_ON(lines < 2);

	cx_write(8 + 0, RISC_JUMP | RISC_IRQ1 | RISC_CNT_INC);
	cx_write(8 + 4, 8);
	cx_write(8 + 8, 0);

	/* 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 */
	if (ch->jumponly)
		cx_write(ch->cmds_start + 0, 8);
	else
		cx_write(ch->cmds_start + 0, risc);

	cx_write(ch->cmds_start + 4, 0);	/* 64 bits 63-32 */
	cx_write(ch->cmds_start + 8, cdt);
	cx_write(ch->cmds_start + 12, (lines * 16) >> 3);
	cx_write(ch->cmds_start + 16, ch->ctrl_start);

	/* IQ size */
	if (ch->jumponly)
		cx_write(ch->cmds_start + 20, 0x80000000 | (64 >> 2));
	else
		cx_write(ch->cmds_start + 20, 64 >> 2);

	/* zero out */
	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, (lines * 16) >> 3);
	cx_write(ch->cnt1_reg, (bpl >> 3) - 1);

	return 0;
}
EXPORT_SYMBOL(cx25821_sram_channel_setup_audio);

void cx25821_sram_channel_dump(struct cx25821_dev *dev, const struct sram_channel *ch)
{
	static char *name[] = {
		"init risc lo",
		"init risc hi",
		"cdt base",
		"cdt size",
		"iq base",
		"iq size",
		"risc pc lo",
		"risc pc hi",
		"iq wr ptr",
		"iq rd ptr",
		"cdt current",
		"pci target lo",
		"pci target hi",
		"line / byte",
	};
	u32 risc;
	unsigned int i, j, n;

	pr_warn("%s: %s - dma channel status dump\n", dev->name, ch->name);
	for (i = 0; i < ARRAY_SIZE(name); i++)
		pr_warn("cmds + 0x%2x:   %-15s: 0x%08x\n",
			i * 4, name[i], cx_read(ch->cmds_start + 4 * i));

	j = i * 4;
	for (i = 0; i < 4;) {
		risc = cx_read(ch->cmds_start + 4 * (i + 14));
		pr_warn("cmds + 0x%2x:   risc%d: ", j + i * 4, i);
		i += cx25821_risc_decode(risc);
	}

	for (i = 0; i < (64 >> 2); i += n) {
		risc = cx_read(ch->ctrl_start + 4 * i);
		/* No consideration for bits 63-32 */

		pr_warn("ctrl + 0x%2x (0x%08x): iq %x: ",
			i * 4, ch->ctrl_start + 4 * i, i);
		n = cx25821_risc_decode(risc);
		for (j = 1; j < n; j++) {
			risc = cx_read(ch->ctrl_start + 4 * (i + j));
			pr_warn("ctrl + 0x%2x :   iq %x: 0x%08x [ arg #%d ]\n",
				4 * (i + j), i + j, risc, j);
		}
	}

	pr_warn("        :   fifo: 0x%08x -> 0x%x\n",
		ch->fifo_start, ch->fifo_start + ch->fifo_size);
	pr_warn("        :   ctrl: 0x%08x -> 0x%x\n",
		ch->ctrl_start, ch->ctrl_start + 6 * 16);
	pr_warn("        :   ptr1_reg: 0x%08x\n",
		cx_read(ch->ptr1_reg));
	pr_warn("        :   ptr2_reg: 0x%08x\n",
		cx_read(ch->ptr2_reg));
	pr_warn("        :   cnt1_reg: 0x%08x\n",
		cx_read(ch->cnt1_reg));
	pr_warn("        :   cnt2_reg: 0x%08x\n",
		cx_read(ch->cnt2_reg));
}

void cx25821_sram_channel_dump_audio(struct cx25821_dev *dev,
				     const struct sram_channel *ch)
{
	static const char * const name[] = {
		"init risc lo",
		"init risc hi",
		"cdt base",
		"cdt size",
		"iq base",
		"iq size",
		"risc pc lo",
		"risc pc hi",
		"iq wr ptr",
		"iq rd ptr",
		"cdt current",
		"pci target lo",
		"pci target hi",
		"line / byte",
	};

	u32 risc, value, tmp;
	unsigned int i, j, n;

	pr_info("\n%s: %s - dma Audio channel status dump\n",
		dev->name, ch->name);

	for (i = 0; i < ARRAY_SIZE(name); i++)
		pr_info("%s: cmds + 0x%2x:   %-15s: 0x%08x\n",
			dev->name, i * 4, name[i],
			cx_read(ch->cmds_start + 4 * i));

	j = i * 4;
	for (i = 0; i < 4;) {
		risc = cx_read(ch->cmds_start + 4 * (i + 14));
		pr_warn("cmds + 0x%2x:   risc%d: ", j + i * 4, i);
		i += cx25821_risc_decode(risc);
	}

	for (i = 0; i < (64 >> 2); i += n) {
		risc = cx_read(ch->ctrl_start + 4 * i);
		/* No consideration for bits 63-32 */

		pr_warn("ctrl + 0x%2x (0x%08x): iq %x: ",
			i * 4, ch->ctrl_start + 4 * i, i);
		n = cx25821_risc_decode(risc);

		for (j = 1; j < n; j++) {
			risc = cx_read(ch->ctrl_start + 4 * (i + j));
			pr_warn("ctrl + 0x%2x :   iq %x: 0x%08x [ arg #%d ]\n",
				4 * (i + j), i + j, risc, j);
		}
	}

	pr_warn("        :   fifo: 0x%08x -> 0x%x\n",
		ch->fifo_start, ch->fifo_start + ch->fifo_size);
	pr_warn("        :   ctrl: 0x%08x -> 0x%x\n",
		ch->ctrl_start, ch->ctrl_start + 6 * 16);
	pr_warn("        :   ptr1_reg: 0x%08x\n",
		cx_read(ch->ptr1_reg));
	pr_warn("        :   ptr2_reg: 0x%08x\n",
		cx_read(ch->ptr2_reg));
	pr_warn("        :   cnt1_reg: 0x%08x\n",
		cx_read(ch->cnt1_reg));
	pr_warn("        :   cnt2_reg: 0x%08x\n",
		cx_read(ch->cnt2_reg));

	for (i = 0; i < 4; i++) {
		risc = cx_read(ch->cmds_start + 56 + (i * 4));
		pr_warn("instruction %d = 0x%x\n", i, risc);
	}

	/* read data from the first cdt buffer */
	risc = cx_read(AUD_A_CDT);
	pr_warn("\nread cdt loc=0x%x\n", risc);
	for (i = 0; i < 8; i++) {
		n = cx_read(risc + i * 4);
		pr_cont("0x%x ", n);
	}
	pr_cont("\n\n");

	value = cx_read(CLK_RST);
	CX25821_INFO(" CLK_RST = 0x%x\n\n", value);

	value = cx_read(PLL_A_POST_STAT_BIST);
	CX25821_INFO(" PLL_A_POST_STAT_BIST = 0x%x\n\n", value);
	value = cx_read(PLL_A_INT_FRAC);
	CX25821_INFO(" PLL_A_INT_FRAC = 0x%x\n\n", value);

	value = cx_read(PLL_B_POST_STAT_BIST);
	CX25821_INFO(" PLL_B_POST_STAT_BIST = 0x%x\n\n", value);
	value = cx_read(PLL_B_INT_FRAC);
	CX25821_INFO(" PLL_B_INT_FRAC = 0x%x\n\n", value);

	value = cx_read(PLL_C_POST_STAT_BIST);
	CX25821_INFO(" PLL_C_POST_STAT_BIST = 0x%x\n\n", value);
	value = cx_read(PLL_C_INT_FRAC);
	CX25821_INFO(" PLL_C_INT_FRAC = 0x%x\n\n", value);

	value = cx_read(PLL_D_POST_STAT_BIST);
	CX25821_INFO(" PLL_D_POST_STAT_BIST = 0x%x\n\n", value);
	value = cx_read(PLL_D_INT_FRAC);
	CX25821_INFO(" PLL_D_INT_FRAC = 0x%x\n\n", value);

	value = cx25821_i2c_read(&dev->i2c_bus[0], AFE_AB_DIAG_CTRL, &tmp);
	CX25821_INFO(" AFE_AB_DIAG_CTRL (0x10900090) = 0x%x\n\n", value);
}
EXPORT_SYMBOL(cx25821_sram_channel_dump_audio);

static void cx25821_shutdown(struct cx25821_dev *dev)
{
	int i;

	/* disable RISC controller */
	cx_write(DEV_CNTRL2, 0);

	/* Disable Video A/B activity */
	for (i = 0; i < VID_CHANNEL_NUM; i++) {
		cx_write(dev->channels[i].sram_channels->dma_ctl, 0);
		cx_write(dev->channels[i].sram_channels->int_msk, 0);
	}

	for (i = VID_UPSTREAM_SRAM_CHANNEL_I;
		i <= VID_UPSTREAM_SRAM_CHANNEL_J; i++) {
		cx_write(dev->channels[i].sram_channels->dma_ctl, 0);
		cx_write(dev->channels[i].sram_channels->int_msk, 0);
	}

	/* Disable Audio activity */
	cx_write(AUD_INT_DMA_CTL, 0);

	/* Disable Serial port */
	cx_write(UART_CTL, 0);

	/* Disable Interrupts */
	cx_write(PCI_INT_MSK, 0);
	cx_write(AUD_A_INT_MSK, 0);
}

void cx25821_set_pixel_format(struct cx25821_dev *dev, int channel_select,
			      u32 format)
{
	if (channel_select <= 7 && channel_select >= 0) {
		cx_write(dev->channels[channel_select].sram_channels->pix_frmt,
				format);
	}
	dev->channels[channel_select].pixel_formats = format;
}

static void cx25821_set_vip_mode(struct cx25821_dev *dev,
				 const struct sram_channel *ch)
{
	cx_write(ch->pix_frmt, PIXEL_FRMT_422);
	cx_write(ch->vip_ctl, PIXEL_ENGINE_VIP1);
}

static void cx25821_initialize(struct cx25821_dev *dev)
{
	int i;

	dprintk(1, "%s()\n", __func__);

	cx25821_shutdown(dev);
	cx_write(PCI_INT_STAT, 0xffffffff);

	for (i = 0; i < VID_CHANNEL_NUM; i++)
		cx_write(dev->channels[i].sram_channels->int_stat, 0xffffffff);

	cx_write(AUD_A_INT_STAT, 0xffffffff);
	cx_write(AUD_B_INT_STAT, 0xffffffff);
	cx_write(AUD_C_INT_STAT, 0xffffffff);
	cx_write(AUD_D_INT_STAT, 0xffffffff);
	cx_write(AUD_E_INT_STAT, 0xffffffff);

	cx_write(CLK_DELAY, cx_read(CLK_DELAY) & 0x80000000);
	cx_write(PAD_CTRL, 0x12);	/* for I2C */
	cx25821_registers_init(dev);	/* init Pecos registers */
	mdelay(100);

	for (i = 0; i < VID_CHANNEL_NUM; i++) {
		cx25821_set_vip_mode(dev, dev->channels[i].sram_channels);
		cx25821_sram_channel_setup(dev, dev->channels[i].sram_channels,
						1440, 0);
		dev->channels[i].pixel_formats = PIXEL_FRMT_422;
		dev->channels[i].use_cif_resolution = 0;
	}

	/* Probably only affect Downstream */
	for (i = VID_UPSTREAM_SRAM_CHANNEL_I;
		i <= VID_UPSTREAM_SRAM_CHANNEL_J; i++) {
		dev->channels[i].pixel_formats = PIXEL_FRMT_422;
		cx25821_set_vip_mode(dev, dev->channels[i].sram_channels);
	}

	cx25821_sram_channel_setup_audio(dev,
			dev->channels[SRAM_CH08].sram_channels, 128, 0);

	cx25821_gpio_init(dev);
}

static int cx25821_get_resources(struct cx25821_dev *dev)
{
	if (request_mem_region(pci_resource_start(dev->pci, 0),
				pci_resource_len(dev->pci, 0), dev->name))
		return 0;

	pr_err("%s: can't get MMIO memory @ 0x%llx\n",
		dev->name, (unsigned long long)pci_resource_start(dev->pci, 0));

	return -EBUSY;
}

static void cx25821_dev_checkrevision(struct cx25821_dev *dev)
{
	dev->hwrevision = cx_read(RDR_CFG2) & 0xff;

	pr_info("Hardware revision = 0x%02x\n", dev->hwrevision);
}

static void cx25821_iounmap(struct cx25821_dev *dev)
{
	if (dev == NULL)
		return;

	/* Releasing IO memory */
	if (dev->lmmio != NULL) {
		iounmap(dev->lmmio);
		dev->lmmio = NULL;
	}
}

static int cx25821_dev_setup(struct cx25821_dev *dev)
{
	static unsigned int cx25821_devcount;
	int i;

	mutex_init(&dev->lock);

	dev->nr = ++cx25821_devcount;
	sprintf(dev->name, "cx25821[%d]", dev->nr);

	if (dev->pci->device != 0x8210) {
		pr_info("%s(): Exiting. Incorrect Hardware device = 0x%02x\n",
			__func__, dev->pci->device);
		return -ENODEV;
	}
	pr_info("Athena Hardware device = 0x%02x\n", dev->pci->device);

	/* Apply a sensible clock frequency for the PCIe bridge */
	dev->clk_freq = 28000000;
	for (i = 0; i < MAX_VID_CHANNEL_NUM; i++) {
		dev->channels[i].dev = dev;
		dev->channels[i].id = i;
		dev->channels[i].sram_channels = &cx25821_sram_channels[i];
	}

	if (dev->nr > 1)
		CX25821_INFO("dev->nr > 1!");

	/* board config */
	dev->board = 1;		/* card[dev->nr]; */
	dev->_max_num_decoders = MAX_DECODERS;

	dev->pci_bus = dev->pci->bus->number;
	dev->pci_slot = PCI_SLOT(dev->pci->devfn);
	dev->pci_irqmask = 0x001f00;

	/* External Master 1 Bus */
	dev->i2c_bus[0].nr = 0;
	dev->i2c_bus[0].dev = dev;
	dev->i2c_bus[0].reg_stat = I2C1_STAT;
	dev->i2c_bus[0].reg_ctrl = I2C1_CTRL;
	dev->i2c_bus[0].reg_addr = I2C1_ADDR;
	dev->i2c_bus[0].reg_rdata = I2C1_RDATA;
	dev->i2c_bus[0].reg_wdata = I2C1_WDATA;
	dev->i2c_bus[0].i2c_period = (0x07 << 24);	/* 1.95MHz */

	if (cx25821_get_resources(dev) < 0) {
		pr_err("%s: No more PCIe resources for subsystem: %04x:%04x\n",
		       dev->name, dev->pci->subsystem_vendor,
		       dev->pci->subsystem_device);

		cx25821_devcount--;
		return -EBUSY;
	}

	/* PCIe stuff */
	dev->base_io_addr = pci_resource_start(dev->pci, 0);

	if (!dev->base_io_addr) {
		CX25821_ERR("No PCI Memory resources, exiting!\n");
		return -ENODEV;
	}

	dev->lmmio = ioremap(dev->base_io_addr, pci_resource_len(dev->pci, 0));

	if (!dev->lmmio) {
		CX25821_ERR("ioremap failed, maybe increasing __VMALLOC_RESERVE in page.h\n");
		cx25821_iounmap(dev);
		return -ENOMEM;
	}

	dev->bmmio = (u8 __iomem *) dev->lmmio;

	pr_info("%s: subsystem: %04x:%04x, board: %s [card=%d,%s]\n",
		dev->name, dev->pci->subsystem_vendor,
		dev->pci->subsystem_device, cx25821_boards[dev->board].name,
		dev->board, card[dev->nr] == dev->board ?
		"insmod option" : "autodetected");

	/* init hardware */
	cx25821_initialize(dev);

	cx25821_i2c_register(&dev->i2c_bus[0]);
/*  cx25821_i2c_register(&dev->i2c_bus[1]);
 *  cx25821_i2c_register(&dev->i2c_bus[2]); */

	if (medusa_video_init(dev) < 0)
		CX25821_ERR("%s(): Failed to initialize medusa!\n", __func__);

	cx25821_video_register(dev);

	cx25821_dev_checkrevision(dev);
	return 0;
}

void cx25821_dev_unregister(struct cx25821_dev *dev)
{
	int i;

	if (!dev->base_io_addr)
		return;

	release_mem_region(dev->base_io_addr, pci_resource_len(dev->pci, 0));

	for (i = 0; i < MAX_VID_CAP_CHANNEL_NUM - 1; i++) {
		if (i == SRAM_CH08) /* audio channel */
			continue;
		/*
		 * TODO: enable when video output is properly
		 * supported.
		if (i == SRAM_CH09 || i == SRAM_CH10)
			cx25821_free_mem_upstream(&dev->channels[i]);
		 */
		cx25821_video_unregister(dev, i);
	}

	cx25821_i2c_unregister(&dev->i2c_bus[0]);
	cx25821_iounmap(dev);
}
EXPORT_SYMBOL(cx25821_dev_unregister);

int cx25821_riscmem_alloc(struct pci_dev *pci,
		       struct cx25821_riscmem *risc,
		       unsigned int size)
{
	__le32 *cpu;
	dma_addr_t dma = 0;

	if (NULL != risc->cpu && risc->size < size)
		pci_free_consistent(pci, risc->size, risc->cpu, risc->dma);
	if (NULL == risc->cpu) {
		cpu = pci_zalloc_consistent(pci, size, &dma);
		if (NULL == cpu)
			return -ENOMEM;
		risc->cpu  = cpu;
		risc->dma  = dma;
		risc->size = size;
	}
	return 0;
}
EXPORT_SYMBOL(cx25821_riscmem_alloc);

static __le32 *cx25821_risc_field(__le32 * rp, struct scatterlist *sglist,
				  unsigned int offset, u32 sync_line,
				  unsigned int bpl, unsigned int padding,
				  unsigned int lines, bool jump)
{
	struct scatterlist *sg;
	unsigned int line, todo;

	if (jump) {
		*(rp++) = cpu_to_le32(RISC_JUMP);
		*(rp++) = cpu_to_le32(0);
		*(rp++) = cpu_to_le32(0); /* bits 63-32 */
	}

	/* sync instruction */
	if (sync_line != NO_SYNC_LINE)
		*(rp++) = cpu_to_le32(RISC_RESYNC | sync_line);

	/* scan lines */
	sg = sglist;
	for (line = 0; line < lines; line++) {
		while (offset && offset >= sg_dma_len(sg)) {
			offset -= sg_dma_len(sg);
			sg = sg_next(sg);
		}
		if (bpl <= sg_dma_len(sg) - offset) {
			/* fits into current chunk */
			*(rp++) = cpu_to_le32(RISC_WRITE | RISC_SOL | RISC_EOL |
					bpl);
			*(rp++) = cpu_to_le32(sg_dma_address(sg) + offset);
			*(rp++) = cpu_to_le32(0);	/* bits 63-32 */
			offset += bpl;
		} else {
			/* scanline needs to be split */
			todo = bpl;
			*(rp++) = cpu_to_le32(RISC_WRITE | RISC_SOL |
					(sg_dma_len(sg) - offset));
			*(rp++) = cpu_to_le32(sg_dma_address(sg) + offset);
			*(rp++) = cpu_to_le32(0);	/* bits 63-32 */
			todo -= (sg_dma_len(sg) - offset);
			offset = 0;
			sg = sg_next(sg);
			while (todo > sg_dma_len(sg)) {
				*(rp++) = cpu_to_le32(RISC_WRITE |
						sg_dma_len(sg));
				*(rp++) = cpu_to_le32(sg_dma_address(sg));
				*(rp++) = cpu_to_le32(0);	/* bits 63-32 */
				todo -= sg_dma_len(sg);
				sg = sg_next(sg);
			}
			*(rp++) = cpu_to_le32(RISC_WRITE | RISC_EOL | todo);
			*(rp++) = cpu_to_le32(sg_dma_address(sg));
			*(rp++) = cpu_to_le32(0);	/* bits 63-32 */
			offset += todo;
		}

		offset += padding;
	}

	return rp;
}

int cx25821_risc_buffer(struct pci_dev *pci, struct cx25821_riscmem *risc,
			struct scatterlist *sglist, unsigned int top_offset,
			unsigned int bottom_offset, unsigned int bpl,
			unsigned int padding, unsigned int lines)
{
	u32 instructions;
	u32 fields;
	__le32 *rp;
	int rc;

	fields = 0;
	if (UNSET != top_offset)
		fields++;
	if (UNSET != bottom_offset)
		fields++;

	/* estimate risc mem: worst case is one write per page border +
	   one write per scan line + syncs + jump (all 3 dwords).  Padding
	   can cause next bpl to start close to a page border.  First DMA
	   region may be smaller than PAGE_SIZE */
	/* write and jump need and extra dword */
	instructions = fields * (1 + ((bpl + padding) * lines) / PAGE_SIZE +
			lines);
	instructions += 5;
	rc = cx25821_riscmem_alloc(pci, risc, instructions * 12);

	if (rc < 0)
		return rc;

	/* write risc instructions */
	rp = risc->cpu;

	if (UNSET != top_offset) {
		rp = cx25821_risc_field(rp, sglist, top_offset, 0, bpl, padding,
					lines, true);
	}

	if (UNSET != bottom_offset) {
		rp = cx25821_risc_field(rp, sglist, bottom_offset, 0x200, bpl,
					padding, lines, UNSET == top_offset);
	}

	/* save pointer to jmp instruction address */
	risc->jmp = rp;
	BUG_ON((risc->jmp - risc->cpu + 3) * sizeof(*risc->cpu) > risc->size);

	return 0;
}

static __le32 *cx25821_risc_field_audio(__le32 * rp, struct scatterlist *sglist,
					unsigned int offset, u32 sync_line,
					unsigned int bpl, unsigned int padding,
					unsigned int lines, unsigned int lpi)
{
	struct scatterlist *sg;
	unsigned int line, todo, sol;

	/* sync instruction */
	if (sync_line != NO_SYNC_LINE)
		*(rp++) = cpu_to_le32(RISC_RESYNC | sync_line);

	/* scan lines */
	sg = sglist;
	for (line = 0; line < lines; line++) {
		while (offset && offset >= sg_dma_len(sg)) {
			offset -= sg_dma_len(sg);
			sg = sg_next(sg);
		}

		if (lpi && line > 0 && !(line % lpi))
			sol = RISC_SOL | RISC_IRQ1 | RISC_CNT_INC;
		else
			sol = RISC_SOL;

		if (bpl <= sg_dma_len(sg) - offset) {
			/* fits into current chunk */
			*(rp++) = cpu_to_le32(RISC_WRITE | sol | RISC_EOL |
					bpl);
			*(rp++) = cpu_to_le32(sg_dma_address(sg) + offset);
			*(rp++) = cpu_to_le32(0);	/* bits 63-32 */
			offset += bpl;
		} else {
			/* scanline needs to be split */
			todo = bpl;
			*(rp++) = cpu_to_le32(RISC_WRITE | sol |
					(sg_dma_len(sg) - offset));
			*(rp++) = cpu_to_le32(sg_dma_address(sg) + offset);
			*(rp++) = cpu_to_le32(0);	/* bits 63-32 */
			todo -= (sg_dma_len(sg) - offset);
			offset = 0;