一个芯片里有多个寄存器
一个寄存器里面某些位表示某个功能。
用一个kcontrol来表示某个功能,当打开、设置某个功能的时候,来操作某个kcontrol就可以了。
一个声卡有多个kcontrol
一个kcontrol对应一个功能,比如调整音量、开关、录音等等
一个kcontrol里面有函数来设置功能
下面进行代码分析:
/sound/soc/codecs/Wm8960.c
wm8960_probe
snd_soc_add_codec_controls(codec, wm8960_snd_controls, ARRAY_SIZE(wm8960_snd_controls));
struct snd_card *card = codec->card->snd_card;
snd_soc_add_controls(card, codec->dev, controls, num_controls,codec->name_prefix, codec);
for (i = 0; i < num_controls; i++) {
const struct snd_kcontrol_new *control = &controls[i];
//snd_soc_new:会使用snd_kcontrol_new构造出snd_kcontrol
err = snd_ctl_add(card, snd_soc_cnew(control, data,control->name, prefix));
list_add_tail(&kcontrol->list, &card->controls);//将snd_kcontrol添加到card->controls链表中
}
}
一个snd_card里面有一个controls链表,里面会有一堆的snd_kcontrol
snd_kcontrol结构体如下:
struct snd_kcontrol {
struct list_head list; /* list of controls */
struct snd_ctl_elem_id id;
unsigned int count; /* count of same elements */
snd_kcontrol_info_t *info; //获得kcontrol的信息
snd_kcontrol_get_t *get; //获得kcontrol的值(即寄存器中某些位的值)
snd_kcontrol_put_t *put; //设置kcontrol的值
union {
snd_kcontrol_tlv_rw_t *c;
const unsigned int *p;
} tlv;
unsigned long private_value; //这里面的东西供info, get, put函数使用
void *private_data; //这里面的东西供info, get, put函数使用,里面可能含有相应寄存器的地址、操作的位
void (*private_free)(struct snd_kcontrol *kcontrol);
struct snd_kcontrol_volatile vd[0]; /* volatile data */
};
snd_ctl_elem_id结构体如下:
struct snd_ctl_elem_id {
unsigned int numid; /* numeric identifier, zero = invalid */
snd_ctl_elem_iface_t iface; /* interface identifier */
unsigned int device; /* device/client number */
unsigned int subdevice; /* subdevice (substream) number */
unsigned char name[44]; /* ASCII name of item */
unsigned int index; /* index of item */
};
问:
snd_kcontrol的值由谁提供
snd_kcontrol对应功能,对应寄存器,因此应该由芯片驱动程序提供。
芯片驱动中有一系列的snd_kcontrol_new,使用函数snd_soc_cnew来构造snd_kcontrol,然后使用函数snd_ctl_add将snd_kcontrol添加到声卡中。
在snd_kcontrol_new结构体中肯定有:info get put 函数,比如说:
static const struct snd_kcontrol_new wm8960_snd_controls[] = {
SOC_DOUBLE_R_TLV("Capture Volume", WM8960_LINVOL, WM8960_RINVOL,
0, 63, 0, adc_tlv),
}
#define SOC_DOUBLE_R_TLV(xname, reg_left, reg_right, xshift, xmax, xinvert, tlv_array) \
{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname),\
.access = SNDRV_CTL_ELEM_ACCESS_TLV_READ |\
SNDRV_CTL_ELEM_ACCESS_READWRITE,\
.tlv.p = (tlv_array), \
.info = snd_soc_info_volsw, \
.get = snd_soc_get_volsw, .put = snd_soc_put_volsw, \
.private_value = SOC_DOUBLE_R_VALUE(reg_left, reg_right, xshift, \
xmax, xinvert) }
使用宏SOC_DOUBLE_R_TLV创建了一个snd_kcontrol_new结构体,该结构体中的值用来构造snd_kcontrol。构造出snd_kcontrol后,就直接放入到声卡中。
以后想操作某个功能时,就可以操作对应的snd_kcontrol,调用里面的info函数获得信息, get函数获得寄存器的值,put函数来设置相应的值。
在开发板上执行tinymix,可以列出所有的snd_kcontrol,它有如下几项:
ctl
type
num
name
value
比如说,要读取或设置capture volume
snd_kcontrol_new中的名字最终会存放在snd_kcontrol的id的name数组中
使用名字进行操作:
tinymix "Capture Volume" //读
tinymix "Capture Volume" 10 //设置
也可以使用序号进行操作:
tinymix 0 //读
tinymix 0 20 //写
问题:如何根据id来找到对应的snd_kcontrol
snd_kcontrol里面有一个snd_ctl_elem_id id, id中有个numid
总结:
1)如何构造snd_kcontrol_new
这些宏中包括:
名字:xname
左声道的寄存器:reg_left
右声道的寄存器:reg_right
寄存器是从哪一位开始:xshift
最大值:xmax
是否反转:xinvert
使用一系列的宏来构造snd_kcontrol_new,这些宏里面有默认的info函数,get、put函数。这些函数会根据私有数据来读取或设置寄存器中的相应位。
宏的原材料来源于芯片手册,读芯片手册,确定寄存器的地址、位数(可以算出最大值)
2)如何使用snd_kcontrol
在应用程序中:
open("/dev/snd/controlC0"),打开底层的设备节点。对应的file_operations结构体在control.c中的snd_ctl_ioctl函数
static const struct file_operations snd_ctl_f_ops =
{
.owner = THIS_MODULE,
.read = snd_ctl_read,
.open = snd_ctl_open,
.release = snd_ctl_release,
.llseek = no_llseek,
.poll = snd_ctl_poll,
.unlocked_ioctl = snd_ctl_ioctl,
.compat_ioctl = snd_ctl_ioctl_compat,
.fasync = snd_ctl_fasync,
};
static long snd_ctl_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
switch (cmd) {
...
case SNDRV_CTL_IOCTL_ELEM_INFO:
return snd_ctl_elem_info_user(ctl, argp);
case SNDRV_CTL_IOCTL_ELEM_READ:
return snd_ctl_elem_read_user(card, argp);
case SNDRV_CTL_IOCTL_ELEM_WRITE:
return snd_ctl_elem_write_user(ctl, argp);
...
}
}
snd_ctl_elem_info_user
snd_ctl_elem_info(ctl, &info)
struct snd_kcontrol *kctl;
kctl = snd_ctl_find_id(card, &info->id);//根据id从card中找到snd_kcontrol
kctl->info(kctl, info);//调用snd_kcontrol中的info函数,因为snd_kcontrol又是由snd_kcontrol_new构造而成,从而调用snd_kcontrol_new中的info函数,即那些宏中的info函数
snd_ctl_elem_read_user
snd_ctl_elem_read(card, control)
struct snd_kcontrol *kctl;
kctl = snd_ctl_find_id(card, &control->id);
kctl->get(kctl, control); //调用宏中的get函数
snd_ctl_elem_write_user
snd_ctl_elem_write(card, file, control)
struct snd_kcontrol *kctl;
kctl = snd_ctl_find_id(card, &control->id);
kctl->put(kctl, control);
如果想看应用程序的话,可以参考文件tinymixer.c