-
Notifications
You must be signed in to change notification settings - Fork 45
/
声卡驱动程序
405 lines (283 loc) · 14.8 KB
/
声卡驱动程序
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
声音采集播放及IIS接口介绍
2410/2440里面有一个IIS模块,IIS信号线连接至---音频编解码芯片(Codec),原理图上的芯片是UDA1341.
注意了,IIS接口只传声音数据。
用声卡是怎么用的?
除了有声音之外,我们还可以调节音量大小/静音/左声道/右声道之类,所以除了IIS接口外,应该还有传这些控制信息的引脚。
比如UDA1341芯片上的:
L3-MODE/L3-CLOCK/L3-DATA 这三条线是用来传输控制信号的--调节音量之类--接到了S3C2440的 GPIO管脚。
SYSCLK(CDCLK)/BCK(I2S-SCLK)/WS(I2S-LRCK)/DATA0(I2S-SDI)/DATA1(I2S-SDO) 这几条就是IIS信号引脚了--这几个引脚只能用来传声音或收声音。
SYSCLK(CDCLK):芯片的工作频率,由2440提供给Codec芯片让它工作的。
BCK(I2S-SCLK): 也叫BCK(Bit Clock),位时钟。每个声音数据有很多位,一个BCK传输一位数据。
WS(I2S-LRCK): 低电平时,传输的是左声道(LEFT)数据;高电平时传输的是右声道(RIGHT)数据。
DATA0(I2S-SDI):声音数据
DATA1(I2S-SDO):声音数据
先看下声音是怎么录制/采集的?
在时间轴上,按固定的时间采集(记录)下来。所谓采集,就是把声音信号(模拟)转换成电压信号等(数字)。
采集的越密(采集频率越高),最后恢复出来的声音就越逼真。。。对于人的耳朵来说,不用太高。
估计:
对于左声道/右声道: 人的左右俩耳朵上都有个ADC,左边采集到的就是左声道,右边采集到的就是右声道,同时采集就是立体声音了?---有可能是错的,瞎估计的。
采样率到 96kHz 时,声音其实就已经很饱满了,耳朵已经分辨不出来失真率了。
采样率慢到 8kHz或4kHz(一秒采集4000次),人耳朵听起来效果就很差了。
采样:
采样: 当然是用ADC进行模-数转换。一次采集,可能采集的是左声道,或者右声道,或者左右都能同时采集.
采样率:1秒钟内采集声音多少次。
ADC采集精度: 一般有8/16/16bit,24bit等等,位数越高越精细。
播放: 用DAC按照采集的速度播放出来。
IIS: 是一种硬件接口,用来传输声音数据。
播放: 2440的IIS控制器要播放时,就把数据从SDRAM用IIS传到Codec芯片里的DAC,经过数-模转换之后播放到喇叭上。
录音: 声音从麦克风进来至Codec芯片里的ADC,经过模-数转换之后,经IIS接口传到SDRAM里。
IIS接口协议:
LRCK:当LRCK为低电平时,传输的是左声道(LEFT)数据;当LRCK为高电平时,传输的是右声道(RIGHT)数据。周而复始的传。
SCLK:也叫BCK(Bit Clock),位时钟。
SD: 2440的IIS控制器可以输出2种格式的数据:
IIS-bus Format(N=8 or 16)格式:
MSB-justified Format(N=8 or 16)格式:
声音存储举例:
buf或者声音文件:
左声道数据 右声道数据 采样点0数据
左声道数据 右声道数据 采样点1数据
左声道数据 右声道数据 采样点2数据
左声道数据 右声道数据 采样点3数据
左声道数据 右声道数据 采样点4数据
左声道数据 右声道数据 ...
左声道数据 右声道数据 采样点N-1数据
左声道数据 右声道数据 采样点N数据
比如Windows上的wav文件,存储格式就是如上存储的。
wav文件的头部分,就有采集频率,采集精度,是左or右声道or左右都有 等的信息。之后就是声音数据了。
怎么发送出去呢?
LRCLK:低电平/高电平周而复始,左声道/右声道数据切换着...
BCK: 对于S3C2440,IIS-Bus控制器可外接8/16Bit Audio CODEC IC,所以一次最多传输16位数据,想再高的精度都不可以。不那么高级。。。所以,
左声道数据最多是16bit的(LRCLK为低电平期间,BCK最多出16个时钟),
右声道数据最多是16bit的(LRCLK为高电平期间,BCK最多出16个时钟)。
SDO:数据。左声道数据如果是8bit的,后面8bit全为0.右声道数据也一样。
怎么调整音量呢?
---UDA1341 Codec芯片上L3接口(控制)说明。
声音传输接口IIS是个标准协议了,但是控制接口就各种各样了。
比如:
UDA1341芯片:用的接口叫L3接口。
WM8976芯片: 可用接口有IIC接口/它自己定义的3线接口。它的3线接口跟UDA1341的3线接口是不一样的。
UDA1341 Codec芯片上L3接口:
读写某地址的寄存器,来达到控制。看芯片手册,超级简单:
L3接口引脚: 分为地址模式/数据传输模式。
L3-MODE: 0时传地址模式,1时传数据模式。
传地址模式时,第一个字节的Data Bits [7:2]是设备地址(UDA1341芯片的地址是000101),
Data Bits [1:0]是传输类型:
00:表示DATA0,是音量控制,boost,treble,detection position,de-emphasis mute静音 and mode...AGC(自动增益控制)...MIC灵敏度控制..
01:表示DATA1,是读回的信息。所以L3接口既可以发送数据,又可以接受数据。
10:表示状态信息,Clock,数据位宽(IIS上传输的数据是多少bit的等),过滤,增益, 极性,采样率等信息...
11:表示扩展地址(Extended Address)。
第二个字节内容的话,就跟第一个字节的最后[1:0]bit是什么息息相关了。比如[1:0]=11的话,那么第二个字节内容就得是扩展地址了。
看UDA1341手册的System clock settings说明,发现当芯片的系统时钟定死了,采样率也跟着能算出来了。很正常的搞法。比如系统时钟/512 = 采样率时钟。
L3-CLOCK: 每一个时钟传1位。地址8个时钟,数据8个时钟。
L3-DATA: L3-MODE为0时,传输的数据是传输类型/地址,L3-MODE为1时,传输的是数据。
Linux内核里面自带了UDA1341芯片的驱动:
入口处注册了个platform driver,假设同名匹配上了系统中已经注册好了的platform device,那么platform driver里的probe就能被调用,开始初始化了。
probe:
platform_get_resource(pdev, IORESOURCE_MEM, 0);
iis_base = (void *)S3C24XX_VA_IIS;
iis_clock = clk_get(dev, "iis");
clk_enable(iis_clock); //使能时钟
local_irq_save(flags);
/* L3 接口 的 GPIO 设置(配置为输出引脚) */
s3c2410_gpio_cfgpin(S3C2410_GPB4, S3C2410_GPB4_OUTP);
s3c2410_gpio_pullup(s3c2410_GPB4, 1);
...
/* IIS 接口的GPIO设置 */
...
local_irq_restore(flags);
init_s3c2410_iis_bus(); //初始化IIS控制器
init_uda1341(); //使用L3接口初始化芯片
/* 设置了两个DMA通道: 一个用于播放,另一个用于录音...代码就长了
当想把内存里面的数据想传给IIS控制器时,设置好源/目的/长度之后,DMA就自动把数据传给它。
当控制器里有数据了,DMA也会自动的把数据传入到SDRAM里 */
output_stream.dma_ch = DMACH_I2S_OUT;
if (audio_init_dma(&output_stream, "UDA1341_out")) {
audio_clear_dma(&output_stream, &s3c2410iis_dma_out);
printk(...);
return -EBUSY;
}
input_stream.dma_ch = DMACH_I2S_IN;
if (audio_init_dma(&input_stream, "UDA1341_in")) {
audio_clear_dma(&input_stream, &s3c2410iis_dma_in);
printk(...);
return -EBUSY;
}
audio_dev_dsp = register_sound_dsp(&smdk2410_audio_fops, -1); //有点料
audio_dev_mixer = register_sound_mixer(&smdk2410_mixer_fops, -1); //
printk(AUDIO_NAME_VERBOSE "initialized\n");
以前的字符设备时,是 主设备号,构造file_operations结构体, 注册register_chrdev()...看看声卡驱动有没有这些:
APP: open/read/write...
-------------------------------------------------
Driver: drv_open, drv_read, drv_write... ---> file_operations
-------------------------------------------------
Hardware:
0. 确定主设备号
1. 构造file_operations结构体
2. 注册...
3. 入口
4. 出口
继续分析:
register_sound_dsp():
sound_insert_unit(&chains[3], fops, dev, 3, 131, "dsp", S_IWUSR|S_IRUSR, NULL); //调用了 sound_core.c 文件里的...核心曾... 所以分层可能出来了:
看看sound_core.c的入口又是怎么写的:
module_init(init_soundcore);
module_exit(cleanup_soundcore);
static int __init init_soundcore()
{
if (register_chrdev(SOUND_MAJOR, "sound", &soundcore_fops)) {
printk(KERN_ERR "soundcore: sound device already in use.\n");
return -EBUSY;
}
sound_class = class_create(THIS_MODULE, "sound");
if (IS_ERR(sound_class))
return PTR_ERR(sound_class);
}
static const struct file_operations soundcore_fops = {
.owner = THIS_MODULE,
.open = soundcore_open, //只有一个open,显然只是个中转作用...(因为是核心曾?)。那么还得继续看看它是从哪里取出真正的file_operations结构体的。
};
假设app打开open了某设备文件,它的主设备号为14:
soundcore_open:
用__look_for_unit()函数,以次设备号为下标,从 chains[] 数组当中得到一个所谓的struct sound_unit, 如果存在,再从它里面得到一个新的file_operations结构体new_fops, 再调用它里面的open函数...。
这个new_fops里面才有open/read/write...等等操作函数。跟LCD驱动框架/Input subsystem驱动框架是一样的。
file->f_op = new_fops;
err = file->f_op->open(inode, file);
录音:
APP: read
-------------------------------------------------
Driver:
file->f_op = new_fops;
err = file->f_op->read();
-------------------------------------------------
Hardware:
播放:
APP: write
-------------------------------------------------
Driver:
file->f_op = new_fops;
err = file->f_op->write ();
-------------------------------------------------
Hardware:
问: 既然录音/播放时,都是从 chains[] 数组当中得到一个所谓的struct sound_unit结构体,再调用的它里面提供的f_op的。那么 chains[] 数组又是谁设置的?
答: 是s3c2440_uda1341.c驱动文件被加载时候的 register_sound_dsp(), register_sound_mixer() 设置好的。
是s3c2440_uda1341.c 向 sound_core.c 注册register_sound_dsp/register_sound_mixer上的。
register_sound_dsp:
sound_insert_unit(&chains[3], fops, dev, 3, 131, "dsp", S_IWUSR|S_IRUSR, NULL); //把fops放到chains[]数组第3项,并会创建设备节点/dev/dsp。
register_sound_mixer:
sound_insert_unit(&chains[0], fops, dev, 3, 131, "mixer", S_IWUSR|S_IRUSR, NULL); //把fops放到chains[]数组第0项,并会创建设备节点/dev/mixer。
那么/dev/dsp 和 /dev/mixer设备节点, 到底是干嘛用的,就要看它们的fops了。
/dev/dsp节点用于播放or录音
/dev/mixer设备用于调整音量/增益/等
所以,并没有脱离linux字符设备的框架。
sound驱动框架:
------------------------------------------------------------------------------------------------------------------------------
sound\soc\s3c24xx\s3c2410-uda1341.c
s3c2410_uda1341_init
driver_register(&s3c2410iis_driver);
.....
s3c2410iis_probe
/* 使能时钟 */
/* 配置GPIO */
/* 设置S3C2440的IIS控制器 */
init_s3c2410_iis_bus
/* 使用L3接口初始化uda1341芯片 */
init_uda1341();
/* 设置两个DMA通道:一个用于播放,另一个用于录音 */
.....
register_sound_dsp(&smdk2410_audio_fops, -1);
sound_insert_unit(&chains[3], fops, dev, 3, 131, "dsp", S_IWUSR | S_IRUSR, NULL); // /dev/dsp
register_sound_mixer(&smdk2410_mixer_fops, -1);
sound_insert_unit(&chains[0], fops, dev, 0, 128, "mixer", S_IRUSR | S_IWUSR, NULL); // /dev/mixer
/dev/dsp: 用于播放/录音
/dev/mixer: 调整音量
1. 主设备号
2. file_operations
3. register_chrdev
app: open () // 假设主设备号为14
-------------------------------------------
soundcore_open
int unit = iminor(inode);
s = __look_for_unit(chain, unit);
// 从chains数组里得到, 谁来设置这个数组?
new_fops = fops_get(s->unit_fops);
file->f_op = new_fops;
err = file->f_op->open(inode,file);
录音:
app: read
----------------------
file->f_op->read
播放:
app: write
-------------------------
file->f_op->write
测试:
1. 确定内核里已经配置了sound\soc\s3c24xx\s3c2410-uda1341.c
-> Device Drivers
-> Sound
-> Advanced Linux Sound Architecture
-> Advanced Linux Sound Architecture
-> System on Chip audio support
<*> I2S of the Samsung S3C24XX chips
2. make uImage
使用新内核启动
3. ls -l /dev/dsp /dev/mixer
4. 播放:
在WINDOWS PC里找一个wav文件,放到开发板根文件系统里
cat Windows.wav > /dev/dsp
5. 录音:
cat /dev/dsp > sound.bin
然后对着麦克风说话
ctrl+c退出
cat sound.bin > /dev/dsp // 就可以听到录下的声音
怎么写WM8976驱动程序?
1. IIS部分一样,保持不变
2. 控制部分不同,重写
测试WM8976:
1. 确定内核里已经配置了sound\soc\s3c24xx\s3c2410-uda1341.c
-> Device Drivers
-> Sound
-> Advanced Linux Sound Architecture // 兼容OSS
-> Advanced Linux Sound Architecture
-> System on Chip audio support
<*> I2S of the Samsung S3C24XX chips
2. 修改sound/soc/s3c24xx/Makefile
obj-y += s3c2410-uda1341.o
改为:
obj-y += s3c-wm8976.o
3. make uImage
使用新内核启动
4. ls -l /dev/dsp /dev/mixer
5. 播放:
在WINDOWS PC里找一个wav文件,放到开发板根文件系统里
cat Windows.wav > /dev/dsp
6. 录音:
cat /dev/dsp > sound.bin
然后对着麦克风说话
ctrl+c退出
cat sound.bin > /dev/dsp // 就可以听到录下的声音
使用madplay测试声卡:
1. 解压:
tar xzf libid3tag-0.15.1b.tar.gz // 库
tar xzf libmad-0.15.1b.tar.gz // 库
tar xzf madplay-0.15.2b.tar.gz // APP
2. 编译 libid3tag-0.15.1b
mkdir tmp
cd libid3tag-0.15.1b
./configure --host=arm-linux --prefix=/work/drivers_and_test/21th_sound/app/tmp
make
make install
3. 编译 libmad-0.15.1b
cd libmad-0.15.1b
./configure --host=arm-linux --prefix=/work/drivers_and_test/21th_sound/app/tmp
make
make install
4. 编译madplay
cd madplay-0.15.2b/
./configure --host=arm-linux --prefix=/work/drivers_and_test/21th_sound/app/tmp LDFLAGS="-L/work/drivers_and_test/21th_sound/app/tmp/lib" CFLAGS="-I /work/drivers_and_test/21th_sound/app/tmp/include"
make
make install
5. 把tmp/bin/* tmp/lib/*so* 复制到根文件系统:
6. 把一个mp3文件复制到根文件系统
7. madplay --tty-control /1.mp3
播放过程中不断按小键盘的减号("-")会降低音量
不断按小键盘的加号("+")会降低音量