1. WM9876接口和工作原理
本节使用了JZ2440开发板移植WM9876驱动,其结构如下图所示,最后利用madplay工具测试音频文件。
IIS和控制接口;
WM9876声卡是负责录音、播音、调节音量和声音合成等的一种多媒体板卡。包括两种接口:IIS接口(提供音频接收和发送)、控制接口(控制音量大小,使能各个输出通道等)
1)当我们播放声音时,将数字信号传入I2SDO脚,声卡便通过解码,产生模拟信号到喇叭/耳机;
2)当我们录音时,声卡便获取麦克风的模拟信号,编码出数字信号到I2SDI引脚上。
-----------
接口说明
----------------
-----------
接口说明
----------------
--> IIS接口相关的引脚如下:
MCLK:主机为解码芯片提供的系统同步时钟(Master/system clock input)。
BCLK(LRC):编解码芯片提供的串行时钟信号(Audio bit clock output)。
ISLRCK:采样频率信号,当为低电平时采样的是左声道信号,高电平时采样的是右声道信号。
I2SDI(ADCDAT):ADC数据输入。
I2SDO(DACDAT):DAC数据输出。
-->控制接口相关的引脚如下:
MODE: 3
线
/2
线控制选择
,
当
MODE
为高
,
表示为
3
线控制
,MODE
位低
,
表示
2
线控制,
2
线模式变为
IIC
模式
(
2440
接的高电平为
3
线模式
);
CSB/GPIO1:
控制数据使能引脚;
SCLK:
时钟引脚;
SDIN:
数据输入输出引脚。
MODE: 3
线
/2
线控制选择
,
当
MODE
为高
,
表示为
3
线控制
,MODE
位低
,
表示
2
线控制,
2
线模式变为
IIC
模式
2
线模式变为
IIC
模式
(
2440
接的高电平为
3
线模式
);
CSB/GPIO1:
控制数据使能引脚;
SCLK:
时钟引脚;
SDIN:
数据输入输出引脚。
(
2440
接的高电平为
3
线模式
);
CSB/GPIO1:
CSB/GPIO1:
控制数据使能引脚;
SCLK:
时钟引脚;
SDIN:
SCLK:
时钟引脚;
SDIN:
SDIN:
数据输入输出引脚。
数据输入输出引脚。
数据输入输出引脚。
2. WM9876驱动移植
2.1 驱动分析
先以uda1341.c
源码为例,分析
驱动程序框架。源码
目录位于:
驱动程序框架。源码
soundsocs3c24xxs3c2410-uda1341.c ,
分析:
1. s3c2410_uda1341_init
static int __init s3c2410_uda1341_init(void) {
memzero(&input_stream, sizeof(audio_stream_t));
memzero(&output_stream, sizeof(audio_stream_t));
return driver_register(&s3c2410iis_driver); //注册
}
-->
static struct device_driver s3c2410iis_driver = {
.name = "s3c2410-iis",
.bus = &platform_bus_type, //platform_bus_type类型
.probe = s3c2410iis_probe,
.remove = s3c2410iis_remove,
};
2. s3c2410iis_probe
由于同名驱动和设备,调用
s3c2410iis_probe:
由于同名驱动和设备,调用
s3c2410iis_probe:
1 static int s3c2410iis_probe(struct device *dev)
2 {
3 struct platform_device *pdev = to_platform_device(dev);
4 struct resource *res;
5 unsigned long flags; 8 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
10 iis_base = (void *)S3C24XX_VA_IIS ;
12 iis_clock = clk_get(dev, "iis");
14 clk_enable(iis_clock);
16 local_irq_save(flags);
17 /* 配置GPIO */
18 /* GPB 4: L3CLOCK, OUTPUT */
19 s3c2410_gpio_cfgpin(S3C2410_GPB4, S3C2410_GPB4_OUTP);
20 s3c2410_gpio_pullup(S3C2410_GPB4,1);
21 /* GPB 3: L3DATA, OUTPUT */
22 s3c2410_gpio_cfgpin(S3C2410_GPB3,S3C2410_GPB3_OUTP);
23 /* GPB 2: L3MODE, OUTPUT */
24 s3c2410_gpio_cfgpin(S3C2410_GPB2,S3C2410_GPB2_OUTP);
25 s3c2410_gpio_pullup(S3C2410_GPB2,1);
26 /* GPE 3: I2SSDI */
27 s3c2410_gpio_cfgpin(S3C2410_GPE3,S3C2410_GPE3_I2SSDI);
28 s3c2410_gpio_pullup(S3C2410_GPE3,0);
29 /* GPE 0: I2SLRCK */
30 s3c2410_gpio_cfgpin(S3C2410_GPE0,S3C2410_GPE0_I2SLRCK);
31 s3c2410_gpio_pullup(S3C2410_GPE0,0);
32 /* GPE 1: I2SSCLK */
33 s3c2410_gpio_cfgpin(S3C2410_GPE1,S3C2410_GPE1_I2SSCLK);
34 s3c2410_gpio_pullup(S3C2410_GPE1,0);
35 /* GPE 2: CDCLK */
36 s3c2410_gpio_cfgpin(S3C2410_GPE2,S3C2410_GPE2_CDCLK);
37 s3c2410_gpio_pullup(S3C2410_GPE2,0);
38 /* GPE 4: I2SSDO */
39 s3c2410_gpio_cfgpin(S3C2410_GPE4,S3C2410_GPE4_I2SSDO);
40 s3c2410_gpio_pullup(S3C2410_GPE4,0);
41
42 local_irq_restore(flags);
43
44 init_s3c2410_iis_bus();///* 设置S3C2440的IIS控制器 */
45
46 init_uda1341();// /* 使用L3接口初始化uda1341芯片 */
47
/* 设置两个DMA通道:一个用于播放,另一个用于录音 */
48 output_stream.dma_ch = DMACH_I2S_OUT;
49 if (audio_init_dma(&output_stream, "UDA1341 out")) {
50 audio_clear_dma(&output_stream,&s3c2410iis_dma_out);
52 }
54 input_stream.dma_ch = DMACH_I2S_IN;
55 if (audio_init_dma(&input_stream, "UDA1341 in")) {
56 audio_clear_dma(&input_stream,&s3c2410iis_dma_in);
57 }
59 audio_dev_dsp = register_sound_dsp(&smdk2410_audio_fops, -1); //
-->sound_insert_unit(&chains[3], fops, dev, 3, 131,
"dsp", S_IWUSR | S_IRUSR, NULL);
60 audio_dev_mixer = register_sound_mixer(&smdk2410_mixer_fops, -1); //
-->sound_insert_unit(&chains[0], fops, dev, 0, 128,
"mixer", S_IRUSR | S_IWUSR, NULL);
61 ......
}
其中, /dev/dsp
设备节点,实现音频的输入输出
IIS
接口(由
chains[3]
数组获得)
/dev/mixer
设备节点,实现音量调节、高音等控制(由
chains[0]
数组获得)
audio_dev_dsp = register_sound_dsp(&smdk2410_audio_fops, -1);
audio_dev_mixer = register_sound_mixer(&smdk2410_mixer_fops, -1);注册了这两个设备节点和file_operation供上层驱动函数调用;
/dev/mixer
设备节点,实现音量调节、高音等控制(由
chains[0]
数组获得)
audio_dev_dsp = register_sound_dsp(&smdk2410_audio_fops, -1);
audio_dev_mixer = register_sound_mixer(&smdk2410_mixer_fops, -1);注册了这两个设备节点和file_operation供上层驱动函数调用;
static struct sound_unit *chains[SOUND_STEP];
/*
* Allocations
* 0 *16 Mixers
* 1 *8 Sequencers
* 2 *16 Midi
* 3 *16 DSP
* 4 *16 SunDSP
* 5 *16 DSP16
* 6 -- sndstat (obsolete)
* 7 *16 unused
* 8 -- alternate sequencer (see above)
* 9 *16 raw synthesizer access
* 10 *16 unused
* 11 *16 unused
* 12 *16 unused
* 13 *16 unused
* 14 *16 unused
* 15 *16 unused
*/
接下来,看一次操作函数,源码程序:linux-2.6.22.6soundSound_core.c,(也可从该源码看起,再看soundsocs3c24xxs3c2410-uda1341.c程序),仅有一个open往后程序可得其他的操作函数:
static const struct file_operations soundcore_fops=
{
/* We must have an owner or the module locking fails */
.owner = THIS_MODULE,
.open = soundcore_open,
};
1 int soundcore_open(struct inode *inode, struct file *file)
2 {
3 int chain;
4 int unit = iminor(inode);
5 struct sound_unit *s;
6 const struct file_operations *new_fops = NULL;
7
8 chain=unit&0x0F;
9 if(chain==4 || chain==5) /* dsp/audio/dsp16 */
10 {
11 unit&=0xF0;
12 unit|=3;
13 chain=3;
14 }
15
16 spin_lock(&sound_loader_lock);
17 s = __look_for_unit(chain, unit);
18 if (s)
19 new_fops = fops_get(s->unit_fops);
20 if (!new_fops) {
21 spin_unlock(&sound_loader_lock);
22 request_module("sound-slot-%i", unit>>4);
23 request_module("sound-service-%i-%i", unit>>4, chain);
24 spin_lock(&sound_loader_lock);
25 s = __look_for_unit(chain, unit);
26 if (s)
27 new_fops = fops_get(s->unit_fops);
28 }
29 if (new_fops) {
30 int err = 0;
31 const struct file_operations *old_fops = file->f_op;
32 file->f_op = new_fops;
33 spin_unlock(&sound_loader_lock);
34 if(file->f_op->open)
35 err = file->f_op->open(inode,file);
36 if (err) {
37 fops_put(file->f_op);
38 file->f_op = fops_get(old_fops);
39 }
40 fops_put(old_fops);
41 return err;
42 }
43 spin_unlock(&sound_loader_lock);
44 return -ENODEV;
45 }
函数框架:app: open () // 假设主设备号为14
soundcore_open函数
--> int unit = iminor(inode);
s = __look_for_unit(chain, unit);
// 从chains数组里得到, 谁来设置这个数组?