soc-core.c
soc_probe
platform_get_drvdata(pdev)//獲取聲卡
snd_soc_register_card
snd_soc_instantiate_card
static int snd_soc_instantiate_card(struct snd_soc_card *card)
{
struct snd_soc_codec *codec;
struct snd_soc_dai_link *dai_link;
int ret, i, order, dai_fmt;
//card->rtd對象綁定codec dai、codec、cpu dai、platform
for (i = 0; i < card->num_links; i++) {
ret = soc_bind_dai_link(card, i);
}
/* card bind complete so register a sound card */
ret = snd_card_new(card->dev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1,
card->owner, 0, &card->snd_card);
card->dapm.bias_level = SND_SOC_BIAS_OFF;
card->dapm.dev = card->dev;
card->dapm.card = card;
list_add(&card->dapm.list, &card->dapm_list);
/* initialise the sound card only once */
if (card->probe) {
ret = card->probe(card);
}
/* probe all components used by DAI links on this card */
for (order = SND_SOC_COMP_ORDER_FIRST; order <= SND_SOC_COMP_ORDER_LAST; order++) {
for (i = 0; i < card->num_links; i++) {
ret = soc_probe_link_components(card, i, order);
}
}
/* probe all DAI links on this card */
for (order = SND_SOC_COMP_ORDER_FIRST; order <= SND_SOC_COMP_ORDER_LAST; order++) {
for (i = 0; i < card->num_links; i++) {
ret = soc_probe_link_dais(card, i, order);
}
}
if (card->controls)
snd_soc_add_card_controls(card, card->controls, card->num_controls);
ret = snd_card_register(card->snd_card);//註冊聲卡
card->instantiated = 1;
return 0;
}
int snd_card_new(struct device *parent, int idx, const char *xid,
struct module *module, int extra_size,
struct snd_card **card_ret)
{
struct snd_card *card;
int err;
*card_ret = NULL;
card = kzalloc(sizeof(*card) + extra_size, GFP_KERNEL);
card->dev = parent;
card->number = idx;
card->module = module;
INIT_LIST_HEAD(&card->devices);
INIT_LIST_HEAD(&card->controls);
INIT_LIST_HEAD(&card->ctl_files);
INIT_LIST_HEAD(&card->files_list);
device_initialize(&card->card_dev);
card->card_dev.parent = parent;
card->card_dev.class = sound_class;
card->card_dev.release = release_card_device;
card->card_dev.groups = card_dev_attr_groups;
err = kobject_set_name(&card->card_dev.kobj, "card%d", idx);
err = snd_ctl_create(card);//創建control的設備節點
err = snd_info_card_create(card);//創建該聲卡的proc/asound/card0文件
*card_ret = card;
return 0;
}
創建control對應的device並加入到聲卡的device鏈表,後續time和pcm都有類似的操作。
int snd_ctl_create(struct snd_card *card)
{
static struct snd_device_ops ops = {
.dev_free = snd_ctl_dev_free,
.dev_register = snd_ctl_dev_register,
.dev_disconnect = snd_ctl_dev_disconnect,
};
return snd_device_new(card, SNDRV_DEV_CONTROL, card, &ops);//將ctl device加入到聲卡的device鏈表
}
int snd_device_new(struct snd_card *card, enum snd_device_type type,
void *device_data, struct snd_device_ops *ops)
{
struct snd_device *dev;
struct list_head *p;
dev = kzalloc(sizeof(*dev), GFP_KERNEL);
INIT_LIST_HEAD(&dev->list);
dev->card = card;
dev->type = type;
dev->state = SNDRV_DEV_BUILD;
dev->device_data = device_data;
dev->ops = ops;
/* insert the entry in an incrementally sorted list */
list_for_each_prev(p, &card->devices) {//加入聲卡設備鏈表
struct snd_device *pdev = list_entry(p, struct snd_device, list);
if ((unsigned int)pdev->type <= (unsigned int)type)
break;
}
list_add(&dev->list, p);
return 0;
}
創建該聲卡的proc/asound/card0文件
int snd_info_card_create(struct snd_card *card)
{
char str[8];
struct snd_info_entry *entry;
sprintf(str, "card%i", card->number);//生成聲卡名字card0
if ((entry = snd_info_create_module_entry(card->module, str, NULL)) == NULL)
return -ENOMEM;
entry->mode = S_IFDIR | S_IRUGO | S_IXUGO;
if (snd_info_register(entry) < 0) {//創建card相關的proc文件
snd_info_free_entry(entry);
return -ENOMEM;
}
card->proc_root = entry;
return 0;
}
Anroid N:/ # cd proc/asound/
card0/ cards devices hwdep mtsndcard/ oss/ pcm seq/ timers version
static int soc_probe_link_components(struct snd_soc_card *card, int num,
int order)
{
struct snd_soc_pcm_runtime *rtd = &card->rtd[num];
struct snd_soc_platform *platform = rtd->platform;
struct snd_soc_component *component;
int i, ret;
//調用cpu->component的probe函數,沒有實現
component = rtd->cpu_dai->component;
if (component->driver->probe_order == order) {
ret = soc_probe_component(card, component);
if (ret < 0)
return ret;
}
//調用codec driver的probe函數,沒有實現
for (i = 0; i < rtd->num_codecs; i++) {
component = rtd->codec_dais[i]->component;
if (component->driver->probe_order == order) {
ret = soc_probe_component(card, component);
if (ret < 0)
return ret;
}
}
//調用platform driver的probe函數
if (platform->component.driver->probe_order == order) {
ret = soc_probe_component(card, &platform->component);
if (ret < 0)
return ret;
}
return 0;
}
static int soc_probe_link_dais(struct snd_soc_card *card, int num, int order)
{
struct snd_soc_dai_link *dai_link = &card->dai_link[num];
struct snd_soc_pcm_runtime *rtd = &card->rtd[num];
struct snd_soc_platform *platform = rtd->platform;
struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
int i, ret;
/* config components */
cpu_dai->platform = platform;
cpu_dai->card = card;
for (i = 0; i < rtd->num_codecs; i++)
rtd->codec_dais[i]->card = card;
//調用cpu_dai driver的probe函數,沒有實現
if (!cpu_dai->probed && cpu_dai->driver->probe_order == order) {
if (cpu_dai->driver->probe) {
ret = cpu_dai->driver->probe(cpu_dai);
}
cpu_dai->probed = 1;
}
//調用codec_dai driver的probe函數,沒有實現
for (i = 0; i < rtd->num_codecs; i++) {
ret = soc_probe_codec_dai(card, rtd->codec_dais[i], order);
}
ret = soc_post_component_init(rtd, dai_link->name);
ret = device_create_file(rtd->dev, &dev_attr_pmdown_time);
if (cpu_dai->driver->compress_dai) {
ret = soc_new_compress(rtd, num);
} else {
if (!dai_link->params) {
/* create the pcm */
ret = soc_new_pcm(rtd, num);//創建pcm相關的信息和設備節點
} else {
INIT_DELAYED_WORK(&rtd->delayed_work,codec2codec_close_delayed_work);
/* link the DAI widgets */
ret = soc_link_dai_widgets(card, dai_link, rtd);
}
}
return 0;
}
static int soc_probe_codec_dai(struct snd_soc_card *card,
struct snd_soc_dai *codec_dai,
int order)
{
int ret;
if (!codec_dai->probed && codec_dai->driver->probe_order == order) {
if (codec_dai->driver->probe) {
ret = codec_dai->driver->probe(codec_dai);
}
/* mark codec_dai as probed and add to card dai list */
codec_dai->probed = 1;
}
return 0;
}
註冊聲卡,在這個階段會遍歷聲卡下的所有邏輯設備,主要是PCM、MIX、CONTROL創建時創建的device,
並且調用各設備的註冊回調函數。
int snd_card_register(struct snd_card *card)
{
int err;
if (!card->registered) {
err = device_add(&card->card_dev);
card->registered = true;
}
if ((err = snd_device_register_all(card)) < 0)
return err;
if (*card->id) {
/* make a unique id name from the given string */
char tmpid[sizeof(card->id)];
memcpy(tmpid, card->id, sizeof(card->id));
snd_card_set_id_no_lock(card, tmpid, tmpid);
} else {
/* create an id from either shortname or longname */
const char *src;
src = *card->shortname ? card->shortname : card->longname;
snd_card_set_id_no_lock(card, src,
retrieve_id_from_card_name(src));
}
snd_cards[card->number] = card;
init_info_for_card(card);
return 0;
}
int snd_device_register_all(struct snd_card *card)
{
struct snd_device *dev;
int err;
list_for_each_entry(dev, &card->devices, list) {
err = __snd_device_register(dev);
}
return 0;
}
static int __snd_device_register(struct snd_device *dev)
{
if (dev->state == SNDRV_DEV_BUILD) {
if (dev->ops->dev_register) {
int err = dev->ops->dev_register(dev);
}
dev->state = SNDRV_DEV_REGISTERED;
}
return 0;
}
對於pcm,就是snd_pcm_dev_register函數,該回調函數建立了和用戶空間應用程序(alsa-lib)
通信所用的設備文件節點:/dev/snd/pcmCxxDxxp和/dev/snd/pcmCxxDxxc
static int snd_pcm_dev_register(struct snd_device *device)
{
int cidx, err;
struct snd_pcm_substream *substream;
struct snd_pcm_notify *notify;
char str[16];
struct snd_pcm *pcm;
struct device *dev;
pcm = device->device_data;
//將pcm加入到全局變量snd_pcm_devices,之後會創建/proc/asound/pcm文件
err = snd_pcm_add(pcm);,
for (cidx = 0; cidx < 2; cidx++) {
int devtype = -1;
if (pcm->streams[cidx].substream == NULL || pcm->internal)
continue;
switch (cidx) {
case SNDRV_PCM_STREAM_PLAYBACK:
sprintf(str, "pcmC%iD%ip", pcm->card->number, pcm->device);//生成pcm playback設備節點的名字
devtype = SNDRV_DEVICE_TYPE_PCM_PLAYBACK;
break;
case SNDRV_PCM_STREAM_CAPTURE:
sprintf(str, "pcmC%iD%ic", pcm->card->number, pcm->device);//生成pcm capture設備節點的名字
devtype = SNDRV_DEVICE_TYPE_PCM_CAPTURE;
break;
}
dev = pcm->dev;
if (!dev)
dev = snd_card_get_device_link(pcm->card);
/* register pcm */
err = snd_register_device_for_dev(devtype, pcm->card,
pcm->device,
&snd_pcm_f_ops[cidx],
pcm, str, dev);
//返回當前註冊的pcm設備,然後設置該pcm的屬性
dev = snd_get_device(devtype, pcm->card, pcm->device);
if (dev) {
err = sysfs_create_groups(&dev->kobj, pcm_dev_attr_groups);
put_device(dev);
}
//進行pcm定時器的初始化
for (substream = pcm->streams[cidx].substream; substream; substream = substream->next)
snd_pcm_timer_init(substream);
}
list_for_each_entry(notify, &snd_pcm_notify_list, list)
notify->n_register(pcm);
return 0;
}