Linux音频设备驱动-6

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 }67 #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 }1213 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),1718 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),2021 UDA1341_SINGLE("Input Gain Switch", CMD_IGAIN, stat1, 5, 1, 0),22 UDA1341_SINGLE("Output Gain Switch", CMD_OGAIN, stat1, 6, 1, 0),2324 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),2627 UDA1341_SINGLE("Mic Sensitivity Volume", CMD_MIC, ext2, 2, 7, 0),2829 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),3233 UDA1341_SINGLE("DAC Power", CMD_DAC, stat1, 0, 1, 0),34 UDA1341_SINGLE("ADC Power", CMD_ADC, stat1, 1, 1, 0),3536 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),4041 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, struct2 snd_ctl_elem_info *uinfo)3 {4 int mask = (kcontrol->private_value >> 12) &63;56 uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN :7 SNDRV_CTL_ELEM_TYPE_INTEGER;8 uinfo->count = 1; //数量为19 uinfo->value.integer.min = 0; //最小值10 uinfo->value.integer.max = mask; //最大值11 return 0;12 }1314 static int snd_uda1341_get_single(struct snd_kcontrol *kcontrol, struct15 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;2223 ucontrol->value.integer.value[0] = uda->cfg[where]; //返回给ucontrol24 if (invert) //如果反转25 ucontrol->value.integer.value[0] = mask – ucontrol->value.integer.value[0];2627 return 0;28 }2930 static int snd_uda1341_put_single(struct snd_kcontrol *kcontrol, struct31 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;4142 val = (ucontrol->value.integer.value[0] &mask);//从ucontrol获得值43 if (invert) //如果反转44 val = mask – val;4546 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 short2 reg)3 {4 unsigned short val = – 1;5 volatile u32 *reg_addr;67 down(&car_mutex);89 /* 设置首/次codec空间 */10 reg_addr = (ac97->num &1) ? &SAC_REG_BASE: &PAC_REG_BASE;11 reg_addr += (reg >> 1);1213 /* 通过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) <= 020 && !((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 }2829 /* 置数据有效 */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);3536 out: up(&car_mutex);37 return val;38 }3940 static void pxa2xx_ac97_write(struct snd_ac97 *ac97, unsigned short reg,41 unsigned short val)42 {43 volatile u32 *reg_addr;4445 down(&car_mutex);4647 /*设置首/次codec空间*/48 reg_addr = (ac97->num &1) ? &SAC_REG_BASE: &PAC_REG_BASE;49 reg_addr += (reg >> 1);5051 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) <= 055 && !((GSR | gsr_bits) &GSR_CDONE))56 printk(KERN_ERR "%s: write error (ac97_reg=%d GSR=%#lx)\n",57 __FUNCTION__,reg, GSR | gsr_bits);5859 up(&car_mutex);60 }6162 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;6869 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));7677 /* 构造pcm组件 */78 ret = pxa2xx_pcm_new(card, &pxa2xx_ac97_pcm_client, &pxa2xx_ac97_pcm);79 if (ret)80 goto err;8182 /* 申请中断 */83 ret = request_irq(IRQ_AC97, pxa2xx_ac97_irq, 0, "AC97", NULL);84 if (ret < 0)85 goto err;8687 …8889 /* 初始化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 …9899 /* 注册card */100 ret = snd_card_register(card);101 if (ret == 0)102 {103 platform_set_drvdata(dev, card);104 return 0;105 }106107 err: if (card)108 snd_card_free(card);109 …110111 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音频设备驱动-6

相关文章:

你感兴趣的文章:

标签云: