Linux音频设备驱动-6

4/27/2010 11:46:27 PM

17.6.3 控制接口的实现
代码清单17.33第21行调用的snd_chip_uda1341_mixer_new()可以认为是UDA1341 ALSA驱动mixer控制组件的“构造函数”,其中会创建的控制元素的定义如代码清单17.39,包括一些枚举和单值元素。
代码清单17.39 UDA1341 ALSA驱动控制接口snd_kcontrol_new结构体
1 #define UDA1341_SINGLE(xname, where, reg, shift, mask, invert) /
2 { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .info = snd_uda1341_info_single, /
3 .get = snd_uda1341_get_single, .put = snd_uda1341_put_single, /
4 .private_value = where | (reg << 5) | (shift << 9) | (mask << 12) | (invert << 18) /
5 }
6
7 #define UDA1341_ENUM(xname, where, reg, shift, mask, invert) /
8 { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .info = snd_uda1341_info_enum, /
9 .get = snd_uda1341_get_enum, .put = snd_uda1341_put_enum, /
10 .private_value = where | (reg << 5) | (shift << 9) | (mask << 12) | (invert << 18) /
11 }
12
13 static struct snd_kcontrol_new snd_uda1341_controls[] =
14 {
15 UDA1341_SINGLE("Master Playback Switch", CMD_MUTE, data0_2, 2, 1, 1),
16 UDA1341_SINGLE("Master Playback Volume", CMD_VOLUME, data0_0, 0, 63, 1),
17
18 UDA1341_SINGLE("Bass Playback Volume", CMD_BASS, data0_1, 2, 15, 0),
19 UDA1341_SINGLE("Treble Playback Volume", CMD_TREBBLE, data0_1, 0, 3, 0),
20
21 UDA1341_SINGLE("Input Gain Switch", CMD_IGAIN, stat1, 5, 1, 0),
22 UDA1341_SINGLE("Output Gain Switch", CMD_OGAIN, stat1, 6, 1, 0),
23
24 UDA1341_SINGLE("Mixer Gain Channel 1 Volume", CMD_CH1, ext0, 0, 31, 1),
25 UDA1341_SINGLE("Mixer Gain Channel 2 Volume", CMD_CH2, ext1, 0, 31, 1),
26
27 UDA1341_SINGLE("Mic Sensitivity Volume", CMD_MIC, ext2, 2, 7, 0),
28
29 UDA1341_SINGLE("AGC Output Level", CMD_AGC_LEVEL, ext6, 0, 3, 0),
30 UDA1341_SINGLE("AGC Time Constant", CMD_AGC_TIME, ext6, 2, 7, 0),
31 UDA1341_SINGLE("AGC Time Constant Switch", CMD_AGC, ext4, 4, 1, 0),
32
33 UDA1341_SINGLE("DAC Power", CMD_DAC, stat1, 0, 1, 0),
34 UDA1341_SINGLE("ADC Power", CMD_ADC, stat1, 1, 1, 0),
35
36 UDA1341_ENUM("Peak detection", CMD_PEAK, data0_2, 5, 1, 0),
37 UDA1341_ENUM("De-emphasis", CMD_DEEMP, data0_2, 3, 3, 0),
38 UDA1341_ENUM("Mixer mode", CMD_MIXER, ext2, 0, 3, 0),
39 UDA1341_ENUM("Filter mode", CMD_FILTER, data0_2, 0, 3, 0),
40
41 UDA1341_2REGS("Gain Input Amplifier Gain (channel 2)", CMD_IG, ext4, ext5, 0, 0, 3, 31, 0),
42 };
从上述代码中宏UDA1341_SINGLE和UDA1341_ENUM的定义可知,单值元素的info()、get()、put()成员函数分别为 snd_uda1341_info_single()、snd_uda1341_get_single()和 snd_uda1341_put_single();枚举元素的info()、get()、put()成员函数分别为 snd_uda1341_info_enum()、snd_uda1341_get_enum()、和snd_uda1341_put_enum()。作为例子,代码清单17.40给出了单值元素相关函数的实现。
代码清单17.40 UDA1341 ALSA驱动控制接口单值元素info/get/put函数
1 static int snd_uda1341_info_single(struct snd_kcontrol *kcontrol, struct
2 snd_ctl_elem_info *uinfo)
3 {
4 int mask = (kcontrol->private_value >> 12) &63;
5
6 uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN :
7 SNDRV_CTL_ELEM_TYPE_INTEGER;
8 uinfo->count = 1; //数量为1
9 uinfo->value.integer.min = 0; //最小值
10 uinfo->value.integer.max = mask; //最大值
11 return 0;
12 }
13
14 static int snd_uda1341_get_single(struct snd_kcontrol *kcontrol, struct
15 snd_ctl_elem_value *ucontrol)
16 {
17 struct l3_client *clnt = snd_kcontrol_chip(kcontrol);
18 struct uda1341 *uda = clnt->driver_data;
19 int where = kcontrol->private_value &31;
20 int mask = (kcontrol->private_value >> 12) &63;
21 int invert = (kcontrol->private_value >> 18) &1;
22
23 ucontrol->value.integer.value[0] = uda->cfg[where]; //返回给ucontrol
24 if (invert) //如果反转
25 ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0];
26
27 return 0;
28 }
29
30 static int snd_uda1341_put_single(struct snd_kcontrol *kcontrol, struct
31 snd_ctl_elem_value *ucontrol)
32 {
33 struct l3_client *clnt = snd_kcontrol_chip(kcontrol);
34 struct uda1341 *uda = clnt->driver_data;
35 int where = kcontrol->private_value &31;
36 int reg = (kcontrol->private_value >> 5) &15;
37 int shift = (kcontrol->private_value >> 9) &7;
38 int mask = (kcontrol->private_value >> 12) &63;
39 int invert = (kcontrol->private_value >> 18) &1;
40 unsigned short val;
41
42 val = (ucontrol->value.integer.value[0] &mask);//从ucontrol获得值
43 if (invert) //如果反转
44 val = mask - val;
45
46 uda->cfg[where] = val;
47 return snd_uda1341_update_bits(clnt, reg, mask, shift, val, FLUSH);//更新位
48 }
17.7实例3:PXA255+AC97 ALSA驱动
Intel 公司的XScale PXA255是一款基于ARM5TE内核技术的嵌入式处理器。它提供了符合AC97 rev2.0标准的AC97控制单元(ACUNIT)和音频控制连接(AC-Link)。ACUNIT就是CODEC控制器,它通过AC-Link连接和控制AC97 CODEC芯片,例如Philips公司出品的一款符合AC97标准的多功能CODEC芯片UCB1400。它不仅是一枚CODEC芯片,还集成了触摸和能量管理两个功能模块,在嵌入式系统中应用广泛。
AC-Link 连接了ACUNIT 和UCB1400Codec,只要对ACUNIT寄存器操作就可以实现同UCBI400之间的数据传输。通过ACUNIT还可读写CODEC内部寄存器,实现对音频采样和混音处理的控制。当然这些读写操作也是经由AC-Link传输的。
Intel Xscale PXA255提供了16个DMA通道,可以很方便的为外围设备提供数据传送。ACUNIT也为CODEC的立体声输入输出和话筒输入提供了独立的16 bit数据通道。每个通道都有一个专门的FIFO。
由于它是一个标准的AC97设备,因此,其驱动的控制部分实现可以说是非常的简单,按照17.4.4节的要求,需要实现AC97 codec寄存器读写的硬件级函数pxa2xx_ac97_read()和pxa2xx_ac97_write(),并在模块初始化时注册相关的AC97 组件,如代码清单17.41。
代码清单17.41 PXA255连接AC97 codec ALSA驱动控制组件
1 static unsigned short pxa2xx_ac97_read(struct snd_ac97 *ac97, unsigned short
2 reg)
3 {
4 unsigned short val = - 1;
5 volatile u32 *reg_addr;
6
7 down(&car_mutex);
8
9 /* 设置首/次codec空间 */
10 reg_addr = (ac97->num &1) ? &SAC_REG_BASE: &PAC_REG_BASE;
11 reg_addr += (reg >> 1);
12
13 /* 通过ac97 link读 */
14 GSR = GSR_CDONE | GSR_SDONE;
15 gsr_bits = 0;
16 val = *reg_addr;
17 if (reg == AC97_GPIO_STATUS)
18 goto out;
19 if (wait_event_timeout(gsr_wq, (GSR | gsr_bits) &GSR_SDONE, 1) <= 0
20 && !((GSR | gsr_bits) &GSR_SDONE))
21 //等待
22 {
23 printk(KERN_ERR "%s: read error (ac97_reg=%d GSR=%#lx)/n",
24 __FUNCTION__,reg, GSR | gsr_bits);
25 val = - 1;
26 goto out;
27 }
28
29 /* 置数据有效 */
30 GSR = GSR_CDONE | GSR_SDONE;
31 gsr_bits = 0;
32 val = *reg_addr;
33 /* 但是我们已经开启另一个周期... */
34 wait_event_timeout(gsr_wq, (GSR | gsr_bits) &GSR_SDONE, 1);
35
36 out: up(&car_mutex);
37 return val;
38 }
39
40 static void pxa2xx_ac97_write(struct snd_ac97 *ac97, unsigned short reg,
41 unsigned short val)
42 {
43 volatile u32 *reg_addr;
44
45 down(&car_mutex);
46
47 /*设置首/次codec空间*/
48 reg_addr = (ac97->num &1) ? &SAC_REG_BASE: &PAC_REG_BASE;
49 reg_addr += (reg >> 1);
50
51 GSR = GSR_CDONE | GSR_SDONE;
52 gsr_bits = 0;
53 *reg_addr = val; //通过ac97 link写
54 if (wait_event_timeout(gsr_wq, (GSR | gsr_bits) &GSR_CDONE, 1) <= 0
55 && !((GSR | gsr_bits) &GSR_CDONE))
56 printk(KERN_ERR "%s: write error (ac97_reg=%d GSR=%#lx)/n",
57 __FUNCTION__,reg, GSR | gsr_bits);
58
59 up(&car_mutex);
60 }
61
62 static int pxa2xx_ac97_probe(struct platform_device *dev)
63 {
64 struct snd_card *card;
65 struct snd_ac97_bus *ac97_bus;
66 struct snd_ac97_template ac97_template;
67 int ret;
68
69 ret = - ENOMEM;
70 /* 新建card */
71 card = snd_card_new(SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1, THIS_MODULE, 0);
72 if (!card)
73 goto err;
74 card->dev = &dev->dev;
75 strncpy(card->driver, dev->dev.driver->name, sizeof(card->driver));
76
77 /* 构造pcm组件 */
78 ret = pxa2xx_pcm_new(card, &pxa2xx_ac97_pcm_client, &pxa2xx_ac97_pcm);
79 if (ret)
80 goto err;
81
82 /* 申请中断 */
83 ret = request_irq(IRQ_AC97, pxa2xx_ac97_irq, 0, "AC97", NULL);
84 if (ret < 0)
85 goto err;
86
87 ...
88
89 /* 初始化ac97 bus */
90 ret = snd_ac97_bus(card, 0, &pxa2xx_ac97_ops, NULL, &ac97_bus);
91 if (ret)
92 goto err;
93 memset(&ac97_template, 0, sizeof(ac97_template));
94 ret = snd_ac97_mixer(ac97_bus, &ac97_template, &pxa2xx_ac97_ac97);
95 if (ret)
96 goto err;
97 ...
98
99 /* 注册card */
100 ret = snd_card_register(card);
101 if (ret == 0)
102 {
103 platform_set_drvdata(dev, card);
104 return 0;
105 }
106
107 err: if (card)
108 snd_card_free(card);
109 ...
110
111 returns ret;
112 }
17.8总结
音频设备接口包括PCM、IIS和AC97几种,分别适用于不同的应用场合。针对音频设备,Linux内核中包含了2类音频设备驱动框架,OSS和 ALSA,前者包含dsp和mixer字符设备接口,在用户空间的编程中,完全使用文件操作;后者以card和组件(pcm、mixer等)为主线,在用户空间的编程中不使用文件接口而使用alsalib。
在音频设备驱动中,几乎必须使用DMA,而DMA的缓冲区会被分割成一个一个的段,每次 DMA操作进行其中的一段。OSS驱动的阻塞读写具有流控能力,在用户空间不需要进行流量方面的定时工作,但是它需要及时的写(播放)和读(录音),以免出现缓冲区的underflow或overflow。

你可能感兴趣的:(linux)