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