驱动分两个层次,上层是平台设备驱动,底层是audio驱动与mixer驱动。
(1)标准的平台设备驱动结构,probe与remove两个函数。
probe:
获得平台资源->申请内存区域-io内存重映射->获得并使能时钟->设置gpio口->初始化iis总线-> 初始化uda1341->audio dma初始化->注册dsp和mixer->释放内存区域。
代码及注释:
|
禁用时钟->取消dsp mixer注册->清除dma
|
设置gpio口->uda1341复位->uda1341设置
|
(2)底层的audio和mixer其实就是字符设备,完成file_operations结构体后在上面说的probe函数中注册
audio驱动:
fops结构体
|
判断打开标志是否可写->判断BUFFER内存空间是否可用->判断阻塞方式还是非阻塞方式->循环写入内存块,并将写好的内存块加入dma队列->返回传输字节数
|
判断buffer内存空间是否为空,若未空则设置buffer空间,并将buffer内存块放入dma队列->循环将内存块数据读入用户空间->返回读出字节数
|
可读可写两部分判断是否可以无阻塞地读写(buffer信号量为大于0则可以)
|
根据oos audio programe guide完成相应功能
|
判断设备是否正忙->设置相关参数->初始化iis总线->清除缓冲区
|
清除缓冲区,读写计数归0
|
mixer驱动:
fops结构体
|
同样根据oos audio programe guide完成相应功能,通过audio的ioctl调用
|
空函数,略
以上ioctl参考资料:
http://manuals.opensound.com/developer/ioctl.html
2,dma分析
(1)两个相关结构体:
buffer结构:
|
|
(2)dma使用过程:
首先是在probe函数中调用audio_init_dma初始化dma:
|
接着在读写函数中调用建audio_setup_buf立dma内存空间(读为in通道建立,写为out通道建立):
|
然后再使用
int s3c2410_dma_enqueue(unsigned int channel, void *id,
dma_addr_t data, int size) 发起一次dma传输最后传输结束后调用回调函数(内核调用)
|
3,L3总线分析
uda1341 datasheet上的时序图:
地址:

|
数据:

驱动中的实现代码:
|
向指定地址写入数据,总时序图:

L3总线简述:
首 先写入地址,时序为 L3MODE置电平->L3CLOCK置高电平->L3CLOCK置低电平->写一位地址->延时->L3CLOCK置高 电平->L3CLOCK置低电平,开始写地址下一位->...8位地址写完->L3MODE,L3CLOCK置高电平
然后开始写入数据,时序为 L3CLOCK置低电平->写一位数据->延时->L3CLOCK置高电平->L3CLOCK置低电平,开始写下一位数据->...8位数据写完,向之前写入的地址地址,一次写数据完成
功能:
通过向DATA0和STATUS两个寄存器写入数据,来控制uda1341。
4,iis总线分析
L3总线是用来控制uda1341的,iis总线则用来收发音频数据。
首先在probe函数中初始化iis总线:
|
static void init_s3c2410_iis_bus_rx(void)
{
unsigned int iiscon, iismod, iisfcon;
char *dstr;
DPRINTK("init_s3c2410_iis_bus_rx\n");
//Kill everything...
writel(0, iis_base + S3C2410_IISPSR);
writel(0, iis_base + S3C2410_IISCON);
writel(0, iis_base + S3C2410_IISMOD);
writel(0, iis_base + S3C2410_IISFCON);
clk_enable(iis_clock);
iiscon = iismod = iisfcon = 0;
//Setup basic stuff
iiscon |= S3C2410_IISCON_PSCEN; // Enable prescaler
iismod |= S3C2410_IISMOD_MASTER; // Set interface to Master Mode
iismod |= S3C2410_IISMOD_LR_LLOW; // Low for left channel
iismod |= S3C2410_IISMOD_MSB; // IIS format
iismod |= S3C2410_IISMOD_16BIT; // Serial data bit/channel is 16 bit
iismod |= S3C2410_IISMOD_384FS; // Master clock freq = 384 fs
iismod |= S3C2410_IISMOD_32FS; // 32 fs
iisfcon|= S3C2410_IISFCON_RXDMA | S3C2410_IISFCON_RXENABLE; //Set RX FIFO acces mode to DMA
//iisfcon|= S3C2410_IISFCON_TXDMA; //Set RX FIFO acces mode to DMA
iiscon |= S3C2410_IISCON_RXDMAEN | S3C2410_IISCON_IISEN; //Enable RX DMA service request
//iiscon |= S3C2410_IISCON_TXIDLE; //Set TX channel idle
iiscon &= (~S3C2410_IISCON_RXIDLE);
iismod |= S3C2410_IISMOD_RXMODE; //Set RX Mode
iismod |= S3C2410_IISMOD_TXMODE;
dstr="RX";
//setup the prescaler
audio_set_dsp_speed(audio_rate);
//iiscon has to be set last - it enables the interface
writel(iismod, iis_base + S3C2410_IISMOD);
writel(iisfcon, iis_base + S3C2410_IISFCON);
writel(iiscon, iis_base + S3C2410_IISCON);
}
static void init_s3c2410_iis_bus_tx(void)
{
unsigned int iiscon, iismod, iisfcon;
char *dstr;
DPRINTK("init_s3c2410_iis_bus_tx\n");
//Kill everything...
writel(0, iis_base + S3C2410_IISPSR);
writel(0, iis_base + S3C2410_IISCON);
writel(0, iis_base + S3C2410_IISMOD);
writel(0, iis_base + S3C2410_IISFCON);
clk_enable(iis_clock);
iiscon = iismod = iisfcon = 0;
//Setup basic stuff
iiscon |= S3C2410_IISCON_PSCEN; // Enable prescaler
iismod |= S3C2410_IISMOD_MASTER; // Set interface to Master Mode
iismod |= S3C2410_IISMOD_LR_LLOW; // Low for left channel
iismod |= S3C2410_IISMOD_MSB; // MSB format
iismod |= S3C2410_IISMOD_16BIT; // Serial data bit/channel is 16 bit
iismod |= S3C2410_IISMOD_384FS; // Master clock freq = 384 fs
iismod |= S3C2410_IISMOD_32FS; // 32 fs
iisfcon|= S3C2410_IISFCON_RXDMA; //Set RX FIFO acces mode to DMA
iisfcon|= S3C2410_IISFCON_TXDMA; //Set TX FIFO acces mode to DMA
iiscon |= S3C2410_IISCON_TXDMAEN | S3C2410_IISCON_IISEN; //Enable TX DMA service request
//iiscon |= S3C2410_IISCON_RXIDLE; //Set RX channel idle
iiscon &= ~S3C2410_IISCON_TXIDLE;
iismod |= S3C2410_IISMOD_TXMODE; //Set TX Mode
iismod |= S3C2410_IISMOD_RXMODE;
iisfcon|= S3C2410_IISFCON_TXENABLE; //Enable TX Fifo
dstr="TX";
//setup the prescaler
audio_set_dsp_speed(audio_rate);
//iiscon has to be set last - it enables the interface
writel(iismod, iis_base + S3C2410_IISMOD);
writel(iisfcon, iis_base + S3C2410_IISFCON);
writel(iiscon, iis_base + S3C2410_IISCON);
}