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。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值