概述
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 errorn",
__func__);
return -EINVAL;
}
get_ext_dai_codec_name();
pr_debug("mt_soc_snd_init dai_link = %pn", 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 %dn",
__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 multicodecn");
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 %sn",
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 %sn",
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 %sn",
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 %sn",
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 %sn",
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: %dn",
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 %dn",
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 %dn",
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 %dn",
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 '