ALSA声卡驱动:
1.Linux ALSA声卡驱动之一:ALSA架构简介和ASOC架构简介
2.Linux ALSA声卡驱动之二:Platform
3. Linux ALSA声卡驱动之三:Platform之Cpu_dai
4. Linux ALSA声卡驱动之四:Codec 以及Codec_dai
5.Linux ALSA声卡驱动之五:Machine 以及ALSA声卡的注册
6.Linux ALSA声卡驱动之六:PCM的注册流程
7.Linux ALSA声卡驱动之七:录音(Capture) 调用流程
一、Machine 简介
Machine 是指某一款机器,可以是某款设备,某款开发板,又或者是某款智能手机,由此可以看出Machine几乎是不可重用的,每个Machine上的硬件实现可能都不一样,CPU不一样,Codec不一样,音频的输入、输出设备也不一样,Machine为CPU、Codec、输入输出设备提供了一个载体,用于描述一块电路板, 它指明此块电路板上用的是哪个Platform和哪个Codec, 由电路板商负责编写此部分代码。绑定platform driver和codec driver
ASoC的一切都从Machine驱动开始,包括声卡的注册,绑定Platform和Codec驱动等等
二、Machine以及声卡驱动的注册
2.1 machine驱动注册的时序图
machine 驱动注册从mt_soc_snd_init 调用 devm_snd_soc_register_card ,而devm_snd_soc_register_card 会调用snd_soc_register_card 。声卡驱动的起始是在soc-core.c soc_probe函数的调用 soc_probe函数最终也会调用snd_soc_register_card。因此后续在讲解声卡驱动的注册,我们就从snd_soc_register_card 函数开始。
下面的图像不够清晰可以下载这个pdf文档查看:https://download.csdn.net/download/Bill_xiao/12239033
2.2 mt_soc_snd_init 函数相关的结构体图
下面的图形不够清晰可以查看这个pdf:https://download.csdn.net/download/Bill_xiao/12239047
2.3 mt_soc_snd_init 主要做了以下两件事:
- 通过函数platform_set_drvdata,device->driver_data = card ,把card的数据挂载到platform_device->device->driver_data,这个由platform_set_drvdata保存的数据会被soc_probe 函数被调用 struct snd_soc_card *card = platform_get_drvdata(pdev)。
- 注册声卡驱动devm_snd_soc_register_card 最终会调用snd_soc_register_card
static int mt_soc_snd_init(struct platform_device *pdev)
{
struct snd_soc_card *card = &mt_snd_soc_card_mt;
int ret;
int daiLinkNum = 0;
ret = mtk_spk_update_dai_link(mt_soc_extspk_dai, pdev);
if (ret) {
dev_err(&pdev->dev, "%s(), mtk_spk_update_dai_link error\n",
__func__);
return -EINVAL;
}
get_ext_dai_codec_name();
pr_debug("mt_soc_snd_init dai_link = %p\n", mt_snd_soc_card_mt.dai_link);
/* DEAL WITH DAI LINK */
memcpy(mt_soc_dai_component, mt_soc_dai_common, sizeof(mt_soc_dai_common));
daiLinkNum += ARRAY_SIZE(mt_soc_dai_common);
memcpy(mt_soc_dai_component + daiLinkNum,
mt_soc_exthp_dai, sizeof(mt_soc_exthp_dai));
daiLinkNum += ARRAY_SIZE(mt_soc_exthp_dai);
memcpy(mt_soc_dai_component + daiLinkNum,
mt_soc_extspk_dai, sizeof(mt_soc_extspk_dai));
daiLinkNum += ARRAY_SIZE(mt_soc_extspk_dai);
//对dai_link重新赋值
mt_snd_soc_card_mt.dai_link = mt_soc_dai_component;
mt_snd_soc_card_mt.num_links = daiLinkNum;
card->dev = &pdev->dev;
//device->driver_data = card ,把card的数据挂载到platform_device->device->driver_data
platform_set_drvdata(pdev, card);
ret = devm_snd_soc_register_card(&pdev->dev, card);//注册声卡驱动
if (ret)
dev_err(&pdev->dev, "%s snd_soc_register_card fail %d\n",
__func__, ret);
/* create debug file */
mt_sco_audio_debugfs = debugfs_create_file(DEBUG_FS_NAME,
S_IFREG | S_IRUGO, NULL, (void *) DEBUG_FS_NAME, &mtaudio_debug_ops);
/* create analog debug file */
mt_sco_audio_debugfs = debugfs_create_file(DEBUG_ANA_FS_NAME,
S_IFREG | S_IRUGO, NULL, (void *) DEBUG_ANA_FS_NAME, &mtaudio_ana_debug_ops);
return ret;
}
static int soc_probe(struct platform_device *pdev)
{
snd_soc_card的结构体是在mtk-soc-machine.c 由函数platform_set_drvdata(pdev, card)来设置的
struct snd_soc_card *card = platform_get_drvdata(pdev);
/*
* no card, so machine driver should be registering card
* we should not be here in that case so ret error
*/
if (!card)
return -EINVAL;
dev_warn(&pdev->dev,
"ASoC: machine %s should use snd_soc_register_card()\n",
card->name);
/* Bodge while we unpick instantiation */
card->dev = &pdev->dev;
return snd_soc_register_card(card);
}
- mt_soc_dai_common 是对platform 、cpu_dai 、 code 、code_dai 的配置。他们之间的联系通过 name的方式来关联 cpu_dai_name platform_name codec_dai_name codec_name
static struct snd_soc_dai_link mt_soc_dai_common[] = {
/* FrontEnd DAI Links */
{
.name = "MultiMedia1",
.stream_name = MT_SOC_DL1_STREAM_NAME,
.cpu_dai_name = MT_SOC_DL1DAI_NAME,
.platform_name = MT_SOC_DL1_PCM,
.codec_dai_name = MT_SOC_CODEC_TXDAI_NAME,
.codec_name = MT_SOC_CODEC_NAME,
},
{
.name = "MultiMedia2",
.stream_name = MT_SOC_UL1_STREAM_NAME,
.cpu_dai_name = MT_SOC_UL1DAI_NAME,
.platform_name = MT_SOC_UL1_PCM,
.codec_dai_name = MT_SOC_CODEC_RXDAI_NAME,
.codec_name = MT_SOC_CODEC_NAME,
},
{
.name = "Voice_MD1",
.stream_name = MT_SOC_VOICE_MD1_STREAM_NAME,
.cpu_dai_name = MT_SOC_VOICE_MD1_NAME,
.platform_name = MT_SOC_VOICE_MD1,
.codec_dai_name = MT_SOC_CODEC_VOICE_MD1DAI_NAME,
.codec_name = MT_SOC_CODEC_NAME,
},
............
2.4 snd_soc_register_card的作用:
- 检查 mt_soc_dai_common 数组里面的成员的合法性
- 调用 snd_soc_instantiate_card
int snd_soc_register_card(struct snd_soc_card *card)
{
int i, j, ret;
if (!card->name || !card->dev)
return -EINVAL;
for (i = 0; i < card->num_links; i++) {
struct snd_soc_dai_link *link = &card->dai_link[i];
//dai_link->codec_name,dai_link->codec_of_node,dai_link->codec_dai_name 赋值到snd_soc_dai_link_component *codecs
ret = snd_soc_init_multicodec(card, link);
if (ret) {
dev_err(card->dev, "ASoC: failed to init multicodec\n");
return ret;
}
//num_codecs=1
for (j = 0; j < link->num_codecs; j++) {
/*
* Codec must be specified by 1 of name or OF node,
* not both or neither.
*/
//name字段与of_node不能同时赋值
if (!!link->codecs[j].name ==
!!link->codecs[j].of_node) {
dev_err(card->dev, "ASoC: Neither/both codec name/of_node are set for %s\n",
link->name);
return -EINVAL;
}
/* Codec DAI name must be specified */
if (!link->codecs[j].dai_name) {
dev_err(card->dev, "ASoC: codec_dai_name not set for %s\n",
link->name);
return -EINVAL;
}
}
/*
* Platform may be specified by either name or OF node, but
* can be left unspecified, and a dummy platform will be used.
*/
//如果platform_name和platform_of_node已经存在就返回
if (link->platform_name && link->platform_of_node) {
dev_err(card->dev,
"ASoC: Both platform name/of_node are set for %s\n",
link->name);
return -EINVAL;
}
/*
* CPU device may be specified by either name or OF node, but
* can be left unspecified, and will be matched based on DAI
* name alone..
*/
if (link->cpu_name && link->cpu_of_node) {
dev_err(card->dev,
"ASoC: Neither/both cpu name/of_node are set for %s\n",
link->name);
return -EINVAL;
}
/*
* At least one of CPU DAI name or CPU device name/node must be
* specified
*/
if (!link->cpu_dai_name &&
!(link->cpu_name || link->cpu_of_node)) {
dev_err(card->dev,
"ASoC: Neither cpu_dai_name nor cpu_name/of_node are set for %s\n",
link->name);
return -EINVAL;
}
}
dev_set_drvdata(card->dev, card);//把card赋值到device->driver_data
snd_soc_initialize_card_lists(card);
card->rtd = devm_kzalloc(card->dev,
sizeof(struct snd_soc_pcm_runtime) *
(card->num_links + card->num_aux_devs),
GFP_KERNEL);
if (card->rtd == NULL)
return -ENOMEM;
card->num_rtd = 0;
card->rtd_aux = &card->rtd[card->num_links];
//初始化card->rtd数组的值
for (i = 0; i < card->num_links; i++) {
card->rtd[i].card = card;
card->rtd[i].dai_link = &card->dai_link[i];
card->rtd[i].codec_dais = devm_kzalloc(card->dev,
sizeof(struct snd_soc_dai *) *
(card->rtd[i].dai_link->num_codecs),
GFP_KERNEL);
if (card->rtd[i].codec_dais == NULL)
return -ENOMEM;
}
for (i = 0; i < card->num_aux_devs; i++)
card->rtd_aux[i].card = card;
INIT_LIST_HEAD(&card->dapm_dirty);
INIT_LIST_HEAD(&card->dobj_list);
card->instantiated = 0;
mutex_init(&card->mutex);
mutex_init(&card->dapm_mutex);
ret = snd_soc_instantiate_card(card);
if (ret != 0)
return ret;
/* deactivate pins to sleep state */
for (i = 0; i < card->num_rtd; i++) {
struct snd_soc_pcm_runtime *rtd = &card->rtd[i];
struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
int j;
for (j = 0; j < rtd->num_codecs; j++) {
struct snd_soc_dai *codec_dai = rtd->codec_dais[j];
if (!codec_dai->active)
pinctrl_pm_select_sleep_state(codec_dai->dev);//codec_dai->dev->pins->sleep_state 这个状态更新
}
if (!cpu_dai->active)
pinctrl_pm_select_sleep_state(cpu_dai->dev);
}
return ret;
}
2.5 snd_soc_instantiate_card 工作比较重,有以下作用:
- 绑定cpu_dai 、codec_dai platform 到rtd中,具体是由soc_bind_dai_link函数实现,到现在我们之前分析代码都关联一起了
- 创建声卡结构体 创建control设备
- 创建一些文件系统
- 执行cpu_dai probe ,codes probe 以及platform probe 函数
- 创建pcm设备
- 声卡注册
static int snd_soc_instantiate_card(struct snd_soc_card *card)
{
struct snd_soc_codec *codec;
int ret, i, order;
mutex_lock(&client_mutex);
mutex_lock_nested(&card->mutex, SND_SOC_CARD_CLASS_INIT);
/* bind DAIs */
//绑定cpu_dai 、codec_dai platform 到rtd中
for (i = 0; i < card->num_links; i++) {
ret = soc_bind_dai_link(card, i);
if (ret != 0)
goto base_error;
}
/* bind aux_devs too */
//绑定rtd_aux
for (i = 0; i < card->num_aux_devs; i++) {
ret = soc_bind_aux_dev(card, i);
if (ret != 0)
goto base_error;
}
/* initialize the register cache for each available codec */
//初始化 codec->reg_cache codec_list 由snd_soc_register_codec注册得来的
list_for_each_entry(codec, &codec_list, list) {
if (codec->cache_init)
continue;
ret = snd_soc_init_codec_cache(codec);
if (ret < 0)
goto base_error;
}
/* card bind complete so register a sound card */
//创建声卡结构体 创建control设备
ret = snd_card_new(card->dev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1,
card->owner, 0, &card->snd_card);
if (ret < 0) {
dev_err(card->dev,
"ASoC: can't create sound card for card %s: %d\n",
card->name, ret);
goto base_error;
}
soc_init_card_debugfs(card);//创建文件系统
//card->dapm.list添加到dapm_list
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);
#ifdef CONFIG_DEBUG_FS
snd_soc_dapm_debugfs_init(&card->dapm, card->debugfs_card_root);
#endif
#ifdef CONFIG_PM_SLEEP
/* deferred resume work */
INIT_WORK(&card->deferred_resume_work, soc_resume_deferred);
#endif
if (card->dapm_widgets)
snd_soc_dapm_new_controls(&card->dapm, card->dapm_widgets,
card->num_dapm_widgets);
if (card->of_dapm_widgets)
snd_soc_dapm_new_controls(&card->dapm, card->of_dapm_widgets,
card->num_of_dapm_widgets);
/* initialise the sound card only once */
//初始化card->probe函数
if (card->probe) {
ret = card->probe(card);
if (ret < 0)
goto card_probe_error;
}
/* probe all components used by DAI links on this card */
//执行cpu dai probe ,codes probe 以及platform probe 函数
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);
if (ret < 0) {
dev_err(card->dev,
"ASoC: failed to instantiate card %d\n",
ret);
goto probe_dai_err;
}
}
}
/* probe all DAI links on this card */
//card->num_links=23,所以for循环会执行23次soc_probe_link_dais函数
//ret = cpu_dai->driver->probe(cpu_dai);//Null,没有提供probe函数调用
//ret = codec_dai->driver->probe(codec_dai);//Null,没有提供probe函数调用
//创建pcm设备
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 (ret < 0) {
dev_err(card->dev,
"ASoC: failed to instantiate card %d\n",
ret);
goto probe_dai_err;
}
}
}
for (i = 0; i < card->num_aux_devs; i++) {
ret = soc_probe_aux_dev(card, i);
if (ret < 0) {
dev_err(card->dev,
"ASoC: failed to add auxiliary devices %d\n",
ret);
goto probe_aux_dev_err;
}
}
snd_soc_dapm_link_dai_widgets(card);
snd_soc_dapm_connect_dai_link_widgets(card);
if (card->controls)
snd_soc_add_card_controls(card, card->controls, card->num_controls);
if (card->dapm_routes)
snd_soc_dapm_add_routes(&card->dapm, card->dapm_routes,
card->num_dapm_routes);
if (card->of_dapm_routes)
snd_soc_dapm_add_routes(&card->dapm, card->of_dapm_routes,
card->num_of_dapm_routes);
snprintf(card->snd_card->shortname, sizeof(card->snd_card->shortname),
"%s", card->name);
snprintf(card->snd_card->longname, sizeof(card->snd_card->longname),
"%s", card->long_name ? card->long_name : card->name);
snprintf(card->snd_card->driver, sizeof(card->snd_card->driver),
"%s", card->driver_name ? card->driver_name : card->name);
for (i = 0; i < ARRAY_SIZE(card->snd_card->driver); i++) {
switch (card->snd_card->driver[i]) {
case '_':
case '-':
case '\0':
break;
default:
if (!isalnum(card->snd_card->driver[i]))
card->snd_card->driver[i] = '_';
break;
}
}
if (card->late_probe) {
ret = card->late_probe(card);
if (ret < 0) {
dev_err(card->dev, "ASoC: %s late_probe() failed: %d\n",
card->name, ret);
goto probe_aux_dev_err;
}
}
snd_soc_dapm_new_widgets(card);
ret = snd_card_register(card->snd_card);//声卡注册
if (ret < 0) {
dev_err(card->dev, "ASoC: failed to register soundcard %d\n",
ret);
goto probe_aux_dev_err;
}
card->instantiated = 1;//初始化完,改变状态
snd_soc_dapm_sync(&card->dapm);
mutex_unlock(&card->mutex);
mutex_unlock(&client_mutex);
return 0;
probe_aux_dev_err:
for (i = 0; i < card->num_aux_devs; i++)
soc_remove_aux_dev(card, i);
probe_dai_err:
soc_remove_dai_links(card);
card_probe_error:
if (card->remove)
card->remove(card);
snd_soc_dapm_free(&card->dapm);
soc_cleanup_card_debugfs(card);
snd_card_free(card->snd_card);
base_error:
mutex_unlock(&card->mutex);
mutex_unlock(&client_mutex);
return ret;
}
2.5.1 soc_bind_dai_link:绑定cpu_dai 、codec_dai 、codec和 platform 到rtd中
static int soc_bind_dai_link(struct snd_soc_card *card, int num)
{
struct snd_soc_dai_link *dai_link = &card->dai_link[num];
struct snd_soc_pcm_runtime *rtd = &card->rtd[num];
struct snd_soc_dai_link_component *codecs = dai_link->codecs;//codec dai
struct snd_soc_dai_link_component cpu_dai_component;
struct snd_soc_dai **codec_dais = rtd->codec_dais;//codec_dai
struct snd_soc_platform *platform;
const char *platform_name;
int i;
dev_dbg(card->dev, "ASoC: binding %s at idx %d\n", dai_link->name, num);
cpu_dai_component.name = dai_link->cpu_name;
cpu_dai_component.of_node = dai_link->cpu_of_node;
cpu_dai_component.dai_name = dai_link->cpu_dai_name;
rtd->cpu_dai = snd_soc_find_dai(&cpu_dai_component); //component_list 中找到component 再从component中获取snd_soc_dai :cpu dai
if (!rtd->cpu_dai) {
dev_err(card->dev, "ASoC: CPU DAI %s not registered\n",
dai_link->cpu_dai_name);
return -EPROBE_DEFER;
}
rtd->num_codecs = dai_link->num_codecs;
/* Find CODEC from registered CODECs */
for (i = 0; i < rtd->num_codecs; i++) {
codec_dais[i] = snd_soc_find_dai(&codecs[i]);//component_list 中找到component 再从component中获取snd_soc_dai :codec dai
if (!codec_dais[i]) {
dev_err(card->dev, "ASoC: CODEC DAI %s not registered\n",
codecs[i].dai_name);
return -EPROBE_DEFER;
}
}
/* Single codec links expect codec and codec_dai in runtime data */
//把codec和codec_dai赋值到rtd。填充codec_dais数组
rtd->codec_dai = codec_dais[0];
rtd->codec = rtd->codec_dai->codec;
/* if there's no platform we match on the empty platform */
platform_name = dai_link->platform_name;
if (!platform_name && !dai_link->platform_of_node)
platform_name = "snd-soc-dummy";
/* find one from the set of registered platforms */
//通过对platform_of_node ,从platform_list 取出snd_soc_platform platform 赋值到rtd->platform
// platform_list 是由snd_soc_register_platform 注册所得
list_for_each_entry(platform, &platform_list, list) {
if (dai_link->platform_of_node) {
if (platform->dev->of_node !=
dai_link->platform_of_node)
continue;
} else {
if (strcmp(platform->component.name, platform_name))
continue;
}
rtd->platform = platform;
}
if (!rtd->platform) {
dev_err(card->dev, "ASoC: platform %s not registered\n",
dai_link->platform_name);
return -EPROBE_DEFER;
}
card->num_rtd++;
return 0;
}
2.5.2 snd_card_new 创建声卡结构体 创建control设备。也就是说control设备的创建是跟声卡一起的。通过代码追踪我们不难发现control设备的创建是通过snd_device_ops.dev_register = snd_ctl_dev_register来实现的。
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 的错误的可能预估的概率性
if (snd_BUG_ON(!card_ret))
return -EINVAL;
*card_ret = NULL;
if (extra_size < 0)
extra_size = 0;
card = kzalloc(sizeof(*card) + extra_size, GFP_KERNEL);//申请内存
if (!card)
return -ENOMEM;
if (extra_size > 0)
card->private_data = (char *)card + sizeof(struct snd_card);//结构体指针
//设置声卡ID
if (xid)
strlcpy(card->id, xid, sizeof(card->id));
err = 0;
mutex_lock(&snd_card_mutex);
if (idx < 0) /* first check the matching module-name slot */
idx = get_slot_from_bitmask(idx, module_slot_match, module);
if (idx < 0) /* if not matched, assign an empty slot */
idx = get_slot_from_bitmask(idx, check_empty_slot, module);
if (idx < 0)
err = -ENODEV;
else if (idx < snd_ecards_limit) {//snd_ecards_limit = cards_limit; cards_limit=1
if (test_bit(idx, snd_cards_lock))
err = -EBUSY; /* invalid */
} else if (idx >= SNDRV_CARDS)
err = -ENODEV;
if (err < 0) {
mutex_unlock(&snd_card_mutex);
dev_err(parent, "cannot find the slot for index %d (range 0-%i), error: %d\n",
idx, snd_ecards_limit - 1, err);
kfree(card);
return err;
}
set_bit(idx, snd_cards_lock); /* lock it */
if (idx >= snd_ecards_limit)
snd_ecards_limit = idx + 1; /* increase the limit */
mutex_unlock(&snd_card_mutex);
//初始化赋值
card->dev = parent;
card->number = idx;
card->module = module;
INIT_LIST_HEAD(&card->devices);
init_rwsem(&card->controls_rwsem);
rwlock_init(&card->ctl_files_rwlock);
mutex_init(&card->user_ctl_lock);
INIT_LIST_HEAD(&card->controls);
INIT_LIST_HEAD(&card->ctl_files);
spin_lock_init(&card->files_lock);
INIT_LIST_HEAD(&card->files_list);
#ifdef CONFIG_PM
mutex_init(&card->power_lock);
init_waitqueue_head(&card->power_sleep);
#endif
device_initialize(&card->card_dev);//结构体device 初始化
card->card_dev.parent = parent;
card->card_dev.class = sound_class;
card->card_dev.release = release_card_device;
card->card_dev.groups = card->dev_groups;
card->dev_groups[0] = &card_dev_attr_group;
err = kobject_set_name(&card->card_dev.kobj, "card%d", idx);
if (err < 0)
goto __error;
/* the control interface cannot be accessed from the user space until */
/* snd_cards_bitmask and snd_cards are set with snd_card_register */
//control设备创建注册
err = snd_ctl_create(card);
if (err < 0) {
dev_err(parent, "unable to register control minors\n");
goto __error;
}
err = snd_info_card_create(card);//信息的初始化,在proc文件夹下面创建asound/card0
if (err < 0) {
dev_err(parent, "unable to create card info\n");
goto __error_ctl;
}
*card_ret = card;
return 0;
__error_ctl:
snd_device_free_all(card);
__error:
put_device(&card->card_dev);
return err;
}
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,//注册声卡 control设备
.dev_disconnect = snd_ctl_dev_disconnect,
};
int err;
if (snd_BUG_ON(!card))
return -ENXIO;
if (snd_BUG_ON(card->number < 0 || card->number >= SNDRV_CARDS))
return -ENXIO;
snd_device_initialize(&card->ctl_dev, card);
dev_set_name(&card->ctl_dev, "controlC%d", card->number);//设置controlC的值
err = snd_device_new(card, SNDRV_DEV_CONTROL, card, &ops);//snd_device 添加到snd_card下面去
if (err < 0)
put_device(&card->ctl_dev);
return err;
}
2.5.3 soc_probe_link_components 执行cpu dai probe ,codes probe 以及platform probe 函数
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;
/* probe the CPU-side component, if it is a CODEC */
//执行cpu dai probe 函数
component = rtd->cpu_dai->component;
if (component->driver->probe_order == order) {
ret = soc_probe_component(card, component);
if (ret < 0)
return ret;
}
/* probe the CODEC-side components */
//执行codes 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;
}
}
/* probe the platform */
//执行platform probe 函数
if (platform->component.driver->probe_order == order) {
ret = soc_probe_component(card, &platform->component);
if (ret < 0)
return ret;
}
return 0;
}
2.5.4 soc_probe_link_dais 的作用:
- card->num_links=23,所以for循环会执行23次soc_probe_link_dais函数
- ret = cpu_dai->driver->probe(cpu_dai);//Null,没有提供probe函数调用
- ret = codec_dai->driver->probe(codec_dai);//Null,没有提供probe函数调用
- 创建pcm设备
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_dai *cpu_dai = rtd->cpu_dai;
int i, ret;
dev_dbg(card->dev, "ASoC: probe %s dai link %d late %d\n",
card->name, num, order);
/* set default power off timeout */
rtd->pmdown_time = pmdown_time;
//dai->driver->probe
ret = soc_probe_dai(cpu_dai, order);
if (ret)
return ret;
/* probe the CODEC DAI */
for (i = 0; i < rtd->num_codecs; i++) {
ret = soc_probe_dai(rtd->codec_dais[i], order);
if (ret)
return ret;
}
/* complete DAI probe during last probe */
if (order != SND_SOC_COMP_ORDER_LAST)
return 0;
/* do machine specific initialization */
if (dai_link->init) {
ret = dai_link->init(rtd);
if (ret < 0) {
dev_err(card->dev, "ASoC: failed to init %s: %d\n",
dai_link->name, ret);
return ret;
}
}
if (dai_link->dai_fmt)
snd_soc_runtime_set_dai_fmt(rtd, dai_link->dai_fmt);
ret = soc_post_component_init(rtd, dai_link->name);
if (ret)
return ret;
#ifdef CONFIG_DEBUG_FS
/* add DPCM sysfs entries */
if (dai_link->dynamic)
soc_dpcm_debugfs_add(rtd);
#endif
if (cpu_dai->driver->compress_new) {
/*create compress_device"*/
ret = cpu_dai->driver->compress_new(rtd, num);
if (ret < 0) {
dev_err(card->dev, "ASoC: can't create compress %s\n",
dai_link->stream_name);
return ret;
}
} else {
if (!dai_link->params) {
/* create the pcm */
//创建pcm 设备
ret = soc_new_pcm(rtd, num);
if (ret < 0) {
dev_err(card->dev, "ASoC: can't create pcm %s :%d\n",
dai_link->stream_name, ret);
return ret;
}
} else {
INIT_DELAYED_WORK(&rtd->delayed_work,
codec2codec_close_delayed_work);
/* link the DAI widgets */
ret = soc_link_dai_widgets(card, dai_link, rtd);
if (ret)
return ret;
}
}
return 0;
}
2.5.5 soc_new_pcm
- 确认rtd下面是否有playback和 capture,有的rtd有capture而没有playback
- 对rtd->ops进行赋值 open close等这些赋值的意义在于上层对pcm设备的操作比如pcm_open
- 创建substream 并且通过substream->next挂载到pcm下面
- 此处pcm设备创建完成
int soc_new_pcm(struct snd_soc_pcm_runtime *rtd, int num)
{
struct snd_soc_platform *platform = rtd->platform;
struct snd_soc_dai *codec_dai;
struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
struct snd_pcm *pcm;
char new_name[64];
int ret = 0, playback = 0, capture = 0;
int i;
//确认rtd下面是否有playback和 capture
if (rtd->dai_link->dynamic || rtd->dai_link->no_pcm) {
playback = rtd->dai_link->dpcm_playback;
capture = rtd->dai_link->dpcm_capture;
} else {
for (i = 0; i < rtd->num_codecs; i++) {
codec_dai = rtd->codec_dais[i];
if (codec_dai->driver->playback.channels_min)
playback = 1;
if (codec_dai->driver->capture.channels_min)
capture = 1;
}
capture = capture && cpu_dai->driver->capture.channels_min;
playback = playback && cpu_dai->driver->playback.channels_min;
}
if (rtd->dai_link->playback_only) {
playback = 1;
capture = 0;
}
if (rtd->dai_link->capture_only) {
playback = 0;
capture = 1;
}
/* create the PCM */
if (rtd->dai_link->no_pcm) {
snprintf(new_name, sizeof(new_name), "(%s)",
rtd->dai_link->stream_name);
ret = snd_pcm_new_internal(rtd->card->snd_card, new_name, num,
playback, capture, &pcm);
} else {
if (rtd->dai_link->dynamic)
snprintf(new_name, sizeof(new_name), "%s (*)",
rtd->dai_link->stream_name);
else
snprintf(new_name, sizeof(new_name), "%s %s-%d",
rtd->dai_link->stream_name,
(rtd->num_codecs > 1) ?
"multicodec" : rtd->codec_dai->name, num);
//创建pcm以及pcm下面挂载的substream
ret = snd_pcm_new(rtd->card->snd_card, new_name, num, playback,
capture, &pcm);
}
if (ret < 0) {
dev_err(rtd->card->dev, "ASoC: can't create pcm for %s\n",
rtd->dai_link->name);
return ret;
}
dev_dbg(rtd->card->dev, "ASoC: registered pcm #%d %s\n",num, new_name);
/* DAPM dai link stream work */
INIT_DELAYED_WORK(&rtd->delayed_work, close_delayed_work);
pcm->nonatomic = rtd->dai_link->nonatomic;
rtd->pcm = pcm;
pcm->private_data = rtd;
if (rtd->dai_link->no_pcm) {
if (playback)
pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream->private_data = rtd;
if (capture)
pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream->private_data = rtd;
goto out;
}
/* ASoC PCM operations */
//对rtd->ops进行赋值 open close等这些赋值的意义在于上层对pcm设备的操作比如pcm_open
if (rtd->dai_link->dynamic) {
rtd->ops.open = dpcm_fe_dai_open;
rtd->ops.hw_params = dpcm_fe_dai_hw_params;
rtd->ops.prepare = dpcm_fe_dai_prepare;
rtd->ops.trigger = dpcm_fe_dai_trigger;
rtd->ops.hw_free = dpcm_fe_dai_hw_free;
rtd->ops.close = dpcm_fe_dai_close;
rtd->ops.pointer = soc_pcm_pointer;
rtd->ops.ioctl = soc_pcm_ioctl;
} else {
rtd->ops.open = soc_pcm_open;
rtd->ops.hw_params = soc_pcm_hw_params;
rtd->ops.prepare = soc_pcm_prepare;
rtd->ops.trigger = soc_pcm_trigger;
rtd->ops.hw_free = soc_pcm_hw_free;
rtd->ops.close = soc_pcm_close;
rtd->ops.pointer = soc_pcm_pointer;
rtd->ops.ioctl = soc_pcm_ioctl;
}
if (platform->driver->ops) {
rtd->ops.ack = platform->driver->ops->ack;
rtd->ops.copy = platform->driver->ops->copy;
rtd->ops.silence = platform->driver->ops->silence;
rtd->ops.page = platform->driver->ops->page;
rtd->ops.mmap = platform->driver->ops->mmap;
}
if (playback)
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &rtd->ops);//通过substream->next,挂载pcm下面的所有substream
if (capture)
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &rtd->ops);//通过substream->next,挂载pcm下面的所有substream
if (platform->driver->pcm_new) {
ret = platform->driver->pcm_new(rtd);
if (ret < 0) {
dev_err(platform->dev,
"ASoC: pcm constructor failed: %d\n",
ret);
return ret;
}
}
pcm->private_free = platform->driver->pcm_free;
out:
dev_info(rtd->card->dev, "%s <-> %s mapping ok\n",
(rtd->num_codecs > 1) ? "multicodec" : rtd->codec_dai->name,
cpu_dai->name);
return ret;
}
static int _snd_pcm_new(struct snd_card *card, const char *id, int device,
int playback_count, int capture_count, bool internal,
struct snd_pcm **rpcm)
{
struct snd_pcm *pcm;
int err;
static struct snd_device_ops ops = {
.dev_free = snd_pcm_dev_free,
.dev_register = snd_pcm_dev_register,
.dev_disconnect = snd_pcm_dev_disconnect,
};
if (snd_BUG_ON(!card))
return -ENXIO;
if (rpcm)
*rpcm = NULL;
pcm = kzalloc(sizeof(*pcm), GFP_KERNEL);
if (!pcm)
return -ENOMEM;
pcm->card = card;
pcm->device = device;
pcm->internal = internal;
mutex_init(&pcm->open_mutex);
init_waitqueue_head(&pcm->open_wait);
INIT_LIST_HEAD(&pcm->list);
if (id)
strlcpy(pcm->id, id, sizeof(pcm->id));
//新建playback
if ((err = snd_pcm_new_stream(pcm, SNDRV_PCM_STREAM_PLAYBACK, playback_count)) < 0) {
snd_pcm_free(pcm);
return err;
}
//新建capture
if ((err = snd_pcm_new_stream(pcm, SNDRV_PCM_STREAM_CAPTURE, capture_count)) < 0) {
snd_pcm_free(pcm);
return err;
}
//创建snd_device 挂载到snd_card上面
if ((err = snd_device_new(card, SNDRV_DEV_PCM, pcm, &ops)) < 0) {
snd_pcm_free(pcm);
return err;
}
if (rpcm)
*rpcm = pcm;
return 0;
}
2.5.6 snd_card_register 会调用snd_device_register_all来对pcm设备和control 设备的注册
int snd_card_register(struct snd_card *card)
{
int err;
if (snd_BUG_ON(!card))
return -EINVAL;
//card是否已经注册过
if (!card->registered) {
err = device_add(&card->card_dev);
if (err < 0)
return err;
card->registered = true;
}
//pcm 和control 设备的注册,通过snd_device_ops ops..dev_register 的方式注册
if ((err = snd_device_register_all(card)) < 0)
return err;
mutex_lock(&snd_card_mutex);
if (snd_cards[card->number]) {
/* already registered */
mutex_unlock(&snd_card_mutex);
return snd_info_card_register(card); /* register pending info *///注册系统文件的信息
}
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;
mutex_unlock(&snd_card_mutex);
init_info_for_card(card);
#if IS_ENABLED(CONFIG_SND_MIXER_OSS)
if (snd_mixer_oss_notify_callback)
snd_mixer_oss_notify_callback(card, SND_MIXER_OSS_NOTIFY_REGISTER);
#endif
return 0;
}