当前位置:嗨网首页>书籍在线阅读

17-AC97 API接口

  
选择背景色: 黄橙 洋红 淡粉 水蓝 草绿 白色 选择字体: 宋体 黑体 微软雅黑 楷体 选择字体大小: 恢复默认

17.4.5 AC97 API接口

ALSA AC97编解码层被很好地定义,利用它,驱动工程师只需编写少量底层的控制函数。

1.AC97实例构造

为了创建一个AC97实例,首先需要调用snd_ac97_bus()函数构建AC97总线及其操作,这个函数的原型为:

int snd_ac97_bus(struct snd_card card, int num, struct snd_ac97_bus_ops ops,

void *private_data, struct snd_ac97_bus **rbus);

该函数的第3个参数ops是一个snd_ac97_bus_ops结构体,其定义如代码清单17.23所示。

代码清单17.23 snd_ac97_bus_ops结构体

1 struct snd_ac97_bus_ops {

2 void(reset)(struct snd_ac97 ac97); / 复位函数/

3 / 写入函数/

4 void(write)(struct snd_ac97 ac97, unsigned short reg, unsigned short val);

5 / 读取函数/

6 unsigned short(read)(struct snd_ac97 ac97, unsigned short reg);

7 void(wait)(struct snd_ac97 ac97);

8 void(init)(struct snd_ac97 ac97);

9 };

接下来,调用snd_ac97_mixer()函数注册混音器,这个函数的原型为:

int snd_ac97_mixer(struct snd_ac97_bus bus, struct snd_ac97_template template, struct

snd_ac97 **rac97);

代码清单17.24所示为AC97实例的创建过程。

代码清单17.24 AC97实例的创建过程范例

1 struct snd_ac97_bus *bus;

2 / AC97总线操作/

3 static struct snd_ac97_bus_ops ops = {

4 .write = snd_mychip_ac97_write,

5 .read = snd_mychip_ac97_read,

6 };

7 / AC97总线与操作创建/

8 snd_ac97_bus(card, 0, &ops, NULL, &bus);

9 / AC97模板/

10 struct snd_ac97_template ac97;

11 int err;

12 memset(&ac97, 0, sizeof(ac97));

13 ac97.private_data = chip;/ 私有数据/

14 / 注册混音器/

15 snd_ac97_mixer(bus, &ac97, &chip->ac97);

上述代码第一行的snd_ac97_bus结构体指针bus的指针被传入第8行的snd_ac97_bus()函数并被赋值,chip->ac97的指针被传入第15行的snd_ac97_mixer()并被赋值,chip->ac97将成员新创建AC97实例的指针。

如果一个声卡上包含多个编解码器,这种情况下,需要多次调用snd_ac97_mixer()并对snd_ac97的num成员(编解码器序号)赋予相应的序号。驱动中可以为不同的编解码器编写不同的snd_ac97_bus_ops成员函数中,或者只是在相同的一套成员函数中通过ac97.num获得序号后再区分进行具体的操作。

2.snd_ac97_bus_ops成员函数

snd_ac97_bus_ops结构体中的read()和write()成员函数完成底层的硬件访问,reset()函数用于复位编解码器,wait()函数用于编解码器标准初始化过程中的特定等待,如果芯片要求额外的等待时间,则应实现这个函数,init()用于完成编解码器附加的初始化。代码清单17.25所示为read()和write()函数的范例。

代码清单17.25 snd_ac97_bus_ops结构体中的read()和write()函数范例

1 static unsigned short snd_xxxchip_ac97_read(struct snd_ac97 *ac97, unsigned

2 short reg)

3 {

4 struct xxxchip *chip = ac97->private_data;

5 ...

6 return the_register_value; / 返回寄存器值/

7 }

8

9 static void snd_xxxchip_ac97_write(struct snd_ac97 *ac97, unsigned short reg,

10 unsigned short val)

11 {

12 struct xxxchip *chip = ac97->private_data;

13 ...

14 /* 将被给的寄存器值写入codec

15 }

3.修改寄存器

如果需要在驱动中访问编解码器,可使用如下函数:

void snd_ac97_write(struct snd_ac97 *ac97, unsigned short reg, unsigned short value);

int snd_ac97_update(struct snd_ac97 *ac97, unsigned short reg, unsigned short value);

int snd_ac97_update_bits(struct snd_ac97 *ac97, unsigned short reg, unsigned short mask,

unsigned short value);

unsigned short snd_ac97_read(struct snd_ac97 *ac97, unsigned short reg);

snd_ac97_update()与void snd_ac97_write()的区别在于前者在值已经设置的情况下不会再设置,而后者则会再写一次。snd_ac97_update_bits()用于更新寄存器的某些位,由mask决定。

除此之外,还有一个函数可用于设置采样率:

int snd_ac97_set_rate(struct snd_ac97 *ac97, int reg, unsigned int rate);

这个函数的第二个参数reg可以是AC97_PCM_MIC_ADC_RATE、AC97_PCM_FRONTDAC RATE、AC97_PCM_LR_ADC_RATE和AC97_SPDIF,对于AC97 _SPDIF而言,寄存器并非真地被改变了,只是相应的IEC958状态位将被更新。

4.时钟调整

在一些芯片上,编解码器的时钟频率不是48000Hz,而是使用PCI时钟以节省一个晶振,在这种情况下,我们应该改变bus->clock为相应的值,例如intel8x0和es1968包含时钟的自动测量函数。

5.proc文件

ALSA AC97接口会创建如/proc/asound/card0/codec97#0/ac97#0-0和ac97#0-0+regs这样的proc文件,通过这些文件可以查看编解码器目前的状态和寄存器。

如果一个芯片上有多个codecs,可多次调用snd_ac97_mixer()。