一個芯片里有多個寄存器
一個寄存器里面某些位表示某個功能。
用一個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