首页 | 新闻 | 新品 | 文库 | 方案 | 视频 | 下载 | 商城 | 开发板 | 数据中心 | 座谈新版 | 培训 | 工具 | 博客 | 论坛 | 百科 | GEC | 活动 | 主题月 | 电子展
返回列表 回复 发帖

IIS音频驱动程序分析(3)

IIS音频驱动程序分析(3)

/
   |IISCON_RX_IDLE  
  |IISCON_PRESCALE);
    设置IIS控制寄存器,参考S3C2410 芯片datasheet 中关于IIS 总线接口的章节,具体设置参数如下:
IISCON_TX_DMA = 1<<5 发送DMA服务请求使能选择,设为1 表示使能发送DMA 服务请求
IISCON_RX_IDLE = 1<<2 接收通道空闲命令,设为1表示接收通道空闲
IISCON_PRESCALE = 1<<1 IIS预分频器使能选择,设为1 表示使能IIS 预分频器
  IISMOD =(IISMOD_SEL_MA        
    |IISMOD_SEL_TX        
  |IISMOD_CH_RIGHT      
  |IISMOD_FMT_MSB      
  |IISMOD_BIT_16        
  |IISMOD_FREQ_384      
  |IISMOD_SFREQ_32);   
    设置IIS模式寄存器,参考S3C2410 芯片datasheet 中关于IIS 总线接口的章节,具体设置参数如下:
IISMOD_SEL_MA = 0<<8 主从模式选择,设为0表示选择主设备模式,则IISLRCK 和IISCLK 引脚为输出模式
IISMOD_SEL_TX = 2<<6 发送接收模式选择,设为2表示选择发送模式
IISMOD_CH_RIGHT = 0<<5 左右通道激活等级,设为0表示左通道为低,右通道为高
IISMOD_FMT_MSB = 1<<4 串行接口格式,设为1表示以最高位有效位MSB 为参考格式(即左对齐数据帧格式)
IISMOD_BIT_16 = 1<<3 每个通道串行数据位数,设为1表示每个通道16位数据
IISMOD_FREQ_384 = 1<<2 主设备时钟频率选择,设为1表示384fs(fs 为采样频率)
IISMOD_SFREQ_32 = 1<<0 串行位时钟频率选择,设为1表示32fs
IISFIFOC =(IISFCON_TX_DMA     
  |IISFCON_TX_EN);      
    设置IIS FIFO控制寄存器,参考S3C2410 芯片datasheet 中关于IIS 总线接口的章节,具体设置参数如下:
IISFCON_TX_DMA = 1<<15 发送FIFO存取模式选择,设为1 表示为DMA 模式
IISFCON_TX_EN = 1<<13 发送FIFO 使能选择,设为1表示使能发送FIFO
IISCON |=IISCON_EN;  
    再次设置IIS控制寄存器,参考S3C2410 芯片datasheet 中关于IIS 总线接口的章节,具体设置参数如下:
IISCON_EN = 1<<0 IIS 接口使能选择,设为1表示使能IIS 接口

------------------------------------------------------------------------
   计算预分频值函数:
static int iispsr_value(int s_bit_clock, int sample_rate)
  tmpval384 =s3c2410_get_bus_clk(GET_PCLK) / s_bit_clock;
    S3C2410主频202M,它的APH 总线频率是202/4=50M,在经过IIS 的PSR(分频比例因子)得到的一个频率用于IIS时钟输出也可以说是同步。
   首先通过调用s3c2410_get_bus_clk 函数来获得总线时钟,然后除以传入的频率参数,这里相当于:
APH/384 = N*fs
这里表示总线时钟进行384 分频后的值。
其中s3c2410_get_bus_clk及相关函数在/kernel/arch/arm/mach-s3c2410/cpu.c文件和/kernel/include/asm-arm/arch-s3c2410/cpu_s3c2410.h文件中,这里不再展开说明。

       for (i = 0; i < 32; i++) {
               tmpval = tmpval384/(i+1);
               if (PCM_ABS((sample_rate - tmpval)) < tmpval384min){
                       tmpval384min = PCM_ABS((sample_rate - tmpval));
                       prescaler = i;
               }
       }
    配置预分频控制器A的值的范围是0~31,所以这里i 也从0~31。后面的算法就不太清楚了,最后算出系统输出时钟为384fs和音频采样频率fs为44.1KHz 的情况下,所需要的预分频值,并返回。

------------------------------------------------------------------------
   接下来init_s3c2410_iis_bus_rx 函数与前面的init_s3c2410_iis_bus_tx函数形式上也差不多:
static void init_s3c2410_iis_bus_rx(void)
IISCON =0;
       IISMOD = 0;
       IISFIFOC = 0;
    首先初始化IIS控制寄存器,IIS 模式寄存器和IIS FIFO 控制寄存器都为0。
      
       IISPSR = (IISPSR_A(iispsr_value(S_CLOCK_FREQ, 44100))
               | IISPSR_B(iispsr_value(S_CLOCK_FREQ, 44100)));
    设置IIS预分频寄存器,参考S3C2410 芯片datasheet 中关于IIS 总线接口的章节,具体设置参数如下:
IISPSR_A(iispsr_value(S_CLOCK_FREQ, 44100)) =IISPSR_A(iispsr_value(384, 44100)) = (一个0~31之间的值)<<5 预分频控制器A,用于内部时钟块
IISPSR_B(iispsr_value(S_CLOCK_FREQ, 44100))) = (一个0~31之间的值)<<0 预分频控制器B,用于外部时钟块
       IISCON =(IISCON_RX_DMA        
               |IISCON_TX_IDLE        
               |IISCON_PRESCALE);     
    设置IIS控制寄存器,参考S3C2410 芯片datasheet 中关于IIS 总线接口的章节,具体设置参数如下:
IISCON_RX_DMA = 1<<4 接收DMA服务请求使能选择,设为1 表示使能接收DMA 服务请求
IISCON_TX_IDLE = 1<<3 发送通道空闲命令,设为1表示发送通道空闲
IISCON_PRESCALE = 1<<1 IIS预分频器使能选择,设为1 表示使能IIS 预分频器
       IISMOD =(IISMOD_SEL_MA        
               |IISMOD_SEL_RX        
               |IISMOD_CH_RIGHT      
               |IISMOD_FMT_MSB      
               |IISMOD_BIT_16        
               |IISMOD_FREQ_384      
               |IISMOD_SFREQ_32);   
    设置IIS模式寄存器,参考S3C2410 芯片datasheet 中关于IIS 总线接口的章节,具体设置参数如下:
IISMOD_SEL_MA = 0<<8 主从模式选择,设为0表示选择主设备模式,则IISLRCK 和IISCLK 引脚为输出模式
IISMOD_SEL_RX = 1<<6 发送接收模式选择,设为1表示选择接收模式
IISMOD_CH_RIGHT = 0<<5 左右通道激活等级,设为0表示左通道为低,右通道为高
IISMOD_FMT_MSB = 1<<4 串行接口格式,设为1表示以最高位有效位MSB 为参考格式(即左对齐数据帧格式)
IISMOD_BIT_16 = 1<<3 每个通道串行数据位数,设为1表示每个通道16位数据
IISMOD_FREQ_384 = 1<<2 主设备时钟频率选择,设为1表示384fs(fs 为采样频率)
IISMOD_SFREQ_32 = 1<<0 串行位时钟频率选择,设为1表示32fs
       IISFIFOC =(IISFCON_RX_DMA     
               |IISFCON_RX_EN);      
    设置IIS FIFO控制寄存器,参考S3C2410 芯片datasheet 中关于IIS 总线接口的章节,具体设置参数如下:
IISFCON_RX_DMA = 1<<14 接收FIFO存取模式选择,设为1 表示为DMA 模式
IISFCON_RX_EN = 1<<12 接收FIFO 使能选择,设为1表示使能接收FIFO
       IISCON |=IISCON_EN;           
    再次设置IIS控制寄存器,参考S3C2410 芯片datasheet 中关于IIS 总线接口的章节,具体设置参数如下:
IISCON_EN = 1<<0 IIS 接口使能选择,设为1表示使能IIS 接口

    以上两个对S3C2410芯片的IIS 相关寄存器进行配置的函数只是分别针对收发模式配置了相应的收发功能,其他配置方面都一样。

------------------------------------------------------------------------
   再来看一下audio_clear_buf 这个函数,该函数的主要任务就是对DMA 缓冲区进行清空:
static void audio_clear_buf(audio_stream_t * s)
s3c2410_dma_flush_all(s->dma_ch);
   调用该函数来刷新所指定的DMA 通道缓冲区。
在/kernel/arch/arm/mach-s3c2410/dma.c 文件中:
int s3c2410_dma_flush_all(dmach_t channel)
这个函数会释放所指定的DMA 通道对应的内存缓冲区。
if(s->buffers) {
  int frag;
  for (frag = 0; frag< s->nbfrags; frag++) {
   if(!s->buffers[frag].master)
    continue;
   consistent_free(s->buffers[frag].start,
     s->buffers[frag].master,
     s->buffers[frag].dma_addr);
  }
  kfree(s->buffers);
  s->buffers =NULL;
}
   接下来判断,如果环形缓冲区不为空,通过调用consistent_free函数来释放环形缓冲区中的s->nbfrags 个buffer所分配的内存空间,其中s->buffers[frag].master 表示buffer所分配的内存大小。最后调用kfree 函数,将整个s->buffers指针所指的已分配的内存释放掉,并将它设为空指针。
在/kernel/arch/arm/mm/consistent.c文件中:

void consistent_free(void *vaddr, size_t size, dma_addr_thandle)
该函数的参数vaddr 为指向内存虚拟地址起始地址的指针,size为要释放的内存大小,handle 为所分配的内存物理地址的起始地址。
s->buf_idx = 0;
s->buf = NULL;
   最后将环形缓冲区buffer 索引号和当前buf 指针都清空,返回。

------------------------------------------------------------------------
    下面来看一下,DMA写入和读取的两个回调函数audio_dmaout_done_callback,audio_dmain_done_callback,当DMA写入或读取完成就会产生中断,并调用这两个中断处理函数。在分析这两个函数之前,需要重新了解一下这两个函数被调用的过程以及传入参数的意义。
    从前面对申请DMA通道函数的分析中,可以知道DMA 写入和读取的中断处理函数是在s3c2410_dma_done函数中被调用的,而s3c2410_dma_done 函数又是在真正的DMA 中断处理函数dma_irq_handler中被调用的。
   
在/kernel/arch/arm/mach-s3c2410/dma.c 文件中:
static void dma_irq_handler(int irq, void *dev_id, struct pt_regs*regs)
{
s3c2410_dma_t *dma = (s3c2410_dma_t*)dev_id;
DPRINTK(__FUNCTION__"\n");
s3c2410_dma_done(dma);
}
在该函数中,首先定义了一个s3c2410_dma_t结构的指针变量指向中断处理程序的参数dev_id,然后将它再作为参数传入s3c2410_dma_done 函数中。
   接着在s3c2410_dma_done 函数中做如下操作:
static inline void s3c2410_dma_done(s3c2410_dma_t *dma)
{
dma_buf_t *buf =dma->curr;
dma_callback_t callback;
if(buf->write) callback =dma->write.callback;
else callback =dma->read.callback;
#ifdef HOOK_LOST_INT
stop_dma_timer();
#endif
DPRINTK("IRQ: b=%#x st=%ld\n",(int)buf->id,(long)dma->regs->DSTAT);
if (callback)
  callback(buf->id,buf->size);
kfree(buf);
dma->active = 0;
process_dma(dma);
}
在该函数中又定义了一个dma_buf_t结构的指针变量,指向了参数中的dma->curr,即指向当前DMA 缓冲区的指针。
在/kernel/arch/arm/mach-s3c2410/dma.h 文件中:

typedef struct dma_buf_s {
intsize;  
dma_addr_t dma_start;
intref;  
void*id;  
intwrite;  
struct dma_buf_s*next;
} dma_buf_t;

typedef struct {
dmach_t channel;
unsigned int in_use;
const char*device_id;
dma_buf_t *head;
dma_buf_t *tail;
dma_buf_t *curr;
unsigned longqueue_count;
intactive;  
dma_regs_t *regs;
intirq;  
dma_device_t write;
dma_device_t read;
} s3c2410_dma_t;
继承事业,薪火相传
返回列表