API for AC97 Codec
ALSA 的 AC97 编解码器层是一个定义良好的层,你无需编写太多代码即可对其进行控制。只需要实现一些底层控制例程即可。AC97 编解码器的 API 定义在 <sound/ac97_codec.h> 头文件中 。
完整代码示例 :
struct mychip {
....
struct snd_ac97 *ac97;
....
};
static unsigned short snd_mychip_ac97_read(struct snd_ac97 *ac97,
unsigned short reg)
{
struct mychip *chip = ac97->private_data;
....
/* read a register value here from the codec */
return the_register_value;
}
static void snd_mychip_ac97_write(struct snd_ac97 *ac97,
unsigned short reg, unsigned short val)
{
struct mychip *chip = ac97->private_data;
....
/* write the given register value to the codec */
}
static int snd_mychip_ac97(struct mychip *chip)
{
struct snd_ac97_bus *bus;
struct snd_ac97_template ac97;
int err;
static struct snd_ac97_bus_ops ops = {
.write = snd_mychip_ac97_write,
.read = snd_mychip_ac97_read,
};
err = snd_ac97_bus(chip->card, 0, &ops, NULL, &bus);
if (err < 0)
return err;
memset(&ac97, 0, sizeof(ac97));
ac97.private_data = chip;
return snd_ac97_mixer(bus, &ac97, &chip->ac97);
}
AC97 构造函数
要创建一个 AC97 实例,首先需要调用 snd_ac97_bus(),并传入一个包含回调函数的 ac97_bus_ops_t 结构体:
struct snd_ac97_bus *bus;
static struct snd_ac97_bus_ops ops = {
.write = snd_mychip_ac97_write,
.read = snd_mychip_ac97_read,
};
snd_ac97_bus(card, 0, &ops, NULL, &pbus);
总线记录(bus record)在所有属于同一个 AC97 实例之间是共享的。
接着,调用 snd_ac97_mixer() 函数,并传入一个 struct snd_ac97_template 结构体以及上面创建的总线指针(bus pointer):
struct snd_ac97_template ac97;
int err;
memset(&ac97, 0, sizeof(ac97));
ac97.private_data = chip;
snd_ac97_mixer(bus, &ac97, &chip->ac97);
其中,chip->ac97 是一个指向新创建的 ac97_t 实例的指针。在这个例子中,将 chip 指针设置为私有数据(private data),以便读写回调函数能够引用该 chip 实例。这个实例不一定非要保存在 chip 结构体中。但如果你需要在驱动中修改寄存器值,或者需要对 AC97 编解码器执行挂起/恢复(suspend/resume)操作,那么就需要保留这个指针,以便传递给相应的函数。
AC97 回调函数
标准的回调函数是 read 和 write。显然,它们对应于对硬件底层寄存器的读写访问功能。
read 回调函数的作用是返回指定参数中所表示寄存器的值:
static unsigned short snd_mychip_ac97_read(struct snd_ac97 *ac97,
unsigned short reg)
{
struct mychip *chip = ac97->private_data;
....
return the_register_value;
}
这里,chip 可以通过 ac97->private_data 进行类型转换得到。
与此同时,write 回调函数用于设置寄存器的值:
static void snd_mychip_ac97_write(struct snd_ac97 *ac97,
unsigned short reg, unsigned short val)
这些回调函数与控制接口(control API)的回调函数一样,都是非原子性的。
另外还有其他几个回调函数:reset、wait 和 init。
-
reset 回调函数用于对编解码器(codec)进行复位操作。如果芯片需要特殊方式的复位,可以实现这个回调函数。
-
wait 回调函数用于在编解码器的标准初始化过程中添加一些等待时间。如果芯片在初始化过程中需要额外的延时,可以定义这个回调函数。
-
init 回调函数用于对编解码器进行额外的初始化操作。
在驱动中更新寄存器
如果你需要从驱动中访问编解码器(codec),可以调用以下函数:
snd_ac97_write()、
snd_ac97_read()、
snd_ac97_update() 、
和snd_ac97_update_bits()。
snd_ac97_write()
snd_ac97_update()
函数都用于向指定寄存器(如 AC97_XXX)写入一个值。
它们之间的区别在于:
-
snd_ac97_update() 在目标寄存器已经设置为相同的值时不会进行写操作;
-
而 snd_ac97_write() 则无论值是否相同,都会强制重写寄存器的值。
snd_ac97_write(ac97, AC97_MASTER, 0x8080);
snd_ac97_update(ac97, AC97_MASTER, 0x8080);
snd_ac97_read() 用于读取指定寄存器的值。例如:
value = snd_ac97_read(ac97, AC97_MASTER);
snd_ac97_update_bits() 用于更新指定寄存器中的某些位 :
snd_ac97_update_bits(ac97, reg, mask, value);
此外,还有一个函数可以在编解码器支持 VRA 或 DRA 的情况下更改某个寄存器(如 AC97_PCM_FRONT_DAC_RATE)的采样率:snd_ac97_set_rate():
snd_ac97_set_rate(ac97, AC97_PCM_FRONT_DAC_RATE, 44100);
以下寄存器可用于设置采样率:AC97_PCM_MIC_ADC_RATE、AC97_PCM_FRONT_DAC_RATE、AC97_PCM_LR_ADC_RATE 和 AC97_SPDIF。当指定 AC97_SPDIF 时,并不会真正更改寄存器的值,而是会更新相应的 IEC958 状态位。
时钟调整
在某些芯片中,编解码器的时钟不是 48000Hz,而是使用 PCI 时钟(为了节省一个晶振!)。在这种情况下,需要将 bus->clock 字段修改为相应的时钟值。例如,intel8x0 和 es1968 驱动就有各自的函数用于读取该时钟值。
Proc 文件
ALSA 的 AC97 接口会创建一个 proc 文件,例如: /proc/asound/card0/codec97#0/ac97#0-0 和 ac97#0-0+regs。 你可以通过查看这些文件,了解当前编解码器(codec)的状态以及寄存器的内容。
多个编解码器
当一张声卡上有多个编解码器(Codec)时,你需要多次调用 snd_ac97_mixer(),并将 ac97.num 设置为 1 或更大的值。
num 字段用于指定编解码器的编号。
如果你配置了多个编解码器,那么你要么为每个编解码器分别编写不同的回调函数,要么在回调函数中通过检查 ac97->num 来区分不同的编解码器。
2万+

被折叠的 条评论
为什么被折叠?



