📄 armlinux-uda1341.c
字号:
//*******************************************************
//* 2007.7.9
//*******************************************************
IISCON = ( IISCON_TX_DMA /* Transmit DMA service request */
|IISCON_RX_IDLE /* Receive Channel idle */
|IISCON_PRESCALE); /* IIS Prescaler Enable */
//设置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 /* Master mode */
| IISMOD_SEL_TX /* Transmit */
| IISMOD_CH_RIGHT /* Low for left channel */
| IISMOD_FMT_MSB /* MSB-justified format */
| IISMOD_BIT_16 /* Serial data bit/channel is 16 bit */
| IISMOD_FREQ_384 /* Master clock freq = 384 fs */
| IISMOD_SFREQ_32); /* 32 fs */
//设置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 /* Transmit FIFO access mode: DMA */
| IISFCON_TX_EN); /* Transmit FIFO enable */
//设置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 enable(start) */
//再次设置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。
/* 44 KHz , 384fs */
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 /* Transmit DMA service request */
|IISCON_TX_IDLE /* Receive Channel idle */
|IISCON_PRESCALE); /* IIS Prescaler Enable */
//设置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 /* Master mode */
| IISMOD_SEL_RX /* Transmit */
| IISMOD_CH_RIGHT /* Low for left channel */
| IISMOD_FMT_MSB /* MSB-justified format */
| IISMOD_BIT_16 /* Serial data bit/channel is 16 bit */
| IISMOD_FREQ_384 /* Master clock freq = 384 fs */
| IISMOD_SFREQ_32); /* 32 fs */
//设置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 /* Transmit FIFO access mode: DMA */
| IISFCON_RX_EN); /* Transmit FIFO enable */
// 设置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 enable(start) */
// 再次设置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 文件中:
/*
* free a page as defined by the above mapping. We expressly forbid
* calling this from interrupt context.
*/
//void consistent_free(void *vaddr, size_t size, dma_addr_t handle)
//该函数的参数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 文件中:
/* DMA buffer struct */
typedef struct dma_buf_s {
int size; /* buffer size */
dma_addr_t dma_start; /* starting DMA address */
int ref; /* number of DMA references */
void *id; /* to identify buffer from outside */
int write; /* 1: buf to write , 0: but to read */
struct dma_buf_s *next; /* next buf to process */
} dma_buf_t;
/* DMA channel structure */
typedef struct {
dmach_t channel;
unsigned int in_use; /* Device is allocated */
const char *device_id; /* Device name */
dma_buf_t *head; /* where to insert buffers */
dma_buf_t *tail; /* where to remove buffers */
dma_buf_t *curr; /* buffer currently DMA'ed */
unsigned long queue_count; /* number of buffers in the queue */
int active; /* 1 if DMA is actually processing data */
dma_regs_t *regs; /* points to appropriate DMA registers */
int irq; /* IRQ used by the channel */
dma_device_t write; /* to write */
dma_device_t read; /* to read */
} s3c2410_dma_t;
// 然后根据buf->write 这个DMA 读写标志来对callback 函数指针进行设置,
//是指向写DMA 函数dma->write.callback,还是读DMA 函数dma->read.callback。
//最后在调用该函数指针所指的函数时将buf->id,buf->size 这两个值作为参数传入,
//即是原来定义在dma_irq_handler 函数中的dma 变量的dma->curr->id 和dma->curr->size,
//分别表示当前DMA 缓冲区的id 号和缓冲区大小。
// 现在可以先来看一下DMA 写入中断处理函数audio_dmaout_done_callback:
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -