本文共 13670 字,大约阅读时间需要 45 分钟。
Linux 下的IIS 音频驱动程序主要都在/kernel/drivers/sound/s3c2410-uda1341.c 文件中。 在音频驱动程序中有2个比较重要的结构体:
typedef struct {
int size;
char *start; (内存虚拟地址起始地址) dma_addr_t dma_addr; (内存物理地址起始地址) struct semaphore sem; int master; (内存大小) } audio_buf_t; typedef struct {
audio_buf_t *buffers; audio_buf_t *buf; u_int buf_idx; u_int fragsize; (音频缓冲区片大小) u_int nbfrags; (音频缓冲区片数量) dmach_t dma_ch; } audio_stream_t; 这是一个管理多缓冲区的结构体,结构体audio_stream_t 为音频流数据组成了一个环形缓冲区。(audio_buf_t *buffers 同触摸屏驱动中struct TS_DEV 结构中的TS_RET buf[MAX_TS_BUF] 意义一样,都为环形缓冲区)用audio_buf_t 来管理一段内存,在用audio_stream_t 来管理N 个audio_buf_t。
音频驱动的file_operations 结构定义如下: static struct file_operations smdk2410_audio_fops = { llseek: smdk2410_audio_llseek, write: smdk2410_audio_write, read: smdk2410_audio_read, poll: smdk2410_audio_poll, ioctl: smdk2410_audio_ioctl, open: smdk2410_audio_open, release: smdk2410_audio_release };
static struct file_operations smdk2410_mixer_fops = {
ioctl: smdk2410_mixer_ioctl, open: smdk2410_mixer_open, release: smdk2410_mixer_release }; 这里定义了两种类型设备的file_operations 结构,前者是DSP 设备,后者是混频器设备。
------------------------------------------------------------------------ 和往常一样,先来看一下加载驱动模块时的初始化函数: int __init s3c2410_uda1341_init(void) 该函数首先会初始化I/O 和UDA1341 芯片,然后申请2个DMA 通道用于音频传输。 local_irq_save(flags);
调用该宏函数来保存IRQ 中断使能状态,并禁止IRQ 中断。
在/kernel/include/asm-arm/system.h 文件中:
#define local_irq_save(x) __save_flags_cli(x) #define local_irq_restore(x) __restore_flags(x) 在/kernel/include/asm-arm/proc-armo/system.h 文件中:
#define __save_flags_cli(x) \ do { \ unsigned long temp; \ __asm__ __volatile__( \ " mov %0, pc @ save_flags_cli\n" \ " orr %1, %0, #0x08000000\n" \ " and %0, %0, #0x0c000000\n" \ " teqp %1, #0\n" \ : "=r" (x), "=r" (temp) \ : \ : "memory"); \ } while (0) 最后用ARM 汇编指令实现了保存IRQ 和FIQ 的中断使能状态,并禁止IRQ 中断。
#define __restore_flags(x) \ do { \ unsigned long temp; \ __asm__ __volatile__( \ " mov %0, pc @ restore_flags\n" \ " bic %0, %0, #0x0c000000\n" \ " orr %0, %0, %1\n" \ " teqp %0, #0\n" \ : "=&r" (temp) \ : "r" (x) \ : "memory"); \ } while (0) 最后用ARM 汇编指令实现了恢复IRQ 和FIQ 的中断使能状态。
set_gpio_ctrl(GPIO_L3CLOCK); set_gpio_ctrl(GPIO_L3DATA); set_gpio_ctrl(GPIO_L3MODE); set_gpio_ctrl(GPIO_E3 | GPIO_PULLUP_EN | GPIO_MODE_I2SSDI); set_gpio_ctrl(GPIO_E0 | GPIO_PULLUP_EN | GPIO_MODE_I2SSDI); set_gpio_ctrl(GPIO_E1 | GPIO_PULLUP_EN | GPIO_MODE_I2SSCLK); set_gpio_ctrl(GPIO_E2 | GPIO_PULLUP_EN | GPIO_MODE_CDCLK); set_gpio_ctrl(GPIO_E4 | GPIO_PULLUP_EN | GPIO_MODE_I2SSDO); 接下来马上设置与UDA1341 芯片相关GPIO 引脚。这里首先将GPB4,GPB3,GPB2 这3个GPIO 引脚设置为输出模式,参考原理图后,得知这3个引脚分别连接UDA1341 芯片的L3CLOCK,L3DATA,L3MODE 这3个引脚,作为这3个信号的输入。 在/kernel/drivers/sound/s3c2410-uda1341.c 文件中:
#define GPIO_L3CLOCK (GPIO_MODE_OUT | GPIO_PULLUP_DIS | GPIO_B4) #define GPIO_L3DATA (GPIO_MODE_OUT | GPIO_PULLUP_DIS | GPIO_B3) #define GPIO_L3MODE (GPIO_MODE_OUT | GPIO_PULLUP_DIS | GPIO_B2) 然后继续设置与IIS 控制器输出信号相关GPIO 引脚。将GPE0~GPE4 这5个引脚设置为IIS 接口的信号模式。需要通过配置GPECON 寄存器来设定该端口管脚的输出模式,对应位如下: [9:8] [7:6] [5:4] [3:2] [1:0]
GPE4 GPE3 GPE2 GPE1 GPE0 参考S3C2410 芯片datasheet 的I/O口章节,都要设为10(二进制)。 local_irq_restore(flags);
设置完GPIO 口的工作模式,就可以前面已经分析过的local_irq_restore 宏函数来恢复IRQ 和FIQ 的中断使能状态。
init_uda1341();
这里调用了init_uda1341 函数来初始化UDA1341 芯片,该函数会在后面说明。
output_stream.dma_ch = DMA_CH2;
if (audio_init_dma(&output_stream, "UDA1341 out")) {
audio_clear_dma(&output_stream); printk( KERN_WARNING AUDIO_NAME_VERBOSE ": unable to get DMA channels\n" ); return -EBUSY; } input_stream.dma_ch = DMA_CH1;
if (audio_init_dma(&input_stream, "UDA1341 in")) { audio_clear_dma(&input_stream); printk( KERN_WARNING AUDIO_NAME_VERBOSE