可以在/proc/asound查看系统使用的声卡.
以7715芯片为例
shell@scx15_sp7715ga:/proc/asound $ ll
lrwxrwxrwx root root 2014-09-06 00:56 VIRTUALAUDIOW -> card1
lrwxrwxrwx root root 2014-09-06 00:56 alli2s -> card3
dr-xr-xr-x root root 2014-09-06 00:56 card0
dr-xr-xr-x root root 2014-09-06 00:56 card1
dr-xr-xr-x root root 2014-09-06 00:56 card2
dr-xr-xr-x root root 2014-09-06 00:56 card3
-r--r--r-- root root 0 2014-09-06 00:56 cards
-r--r--r-- root root 0 2014-09-06 00:56 devices
-r--r--r-- root root 0 2014-09-06 00:56 pcm
lrwxrwxrwx root root 2014-09-06 00:56 saudiovoip -> card2
lrwxrwxrwx root root 2014-09-06 00:56 sprdphone -> card0
-r--r--r-- root root 0 2014-09-06 00:56 timers
-r--r--r-- root root 0 2014-09-06 00:56 version
这里有四张声卡。
接下来看声卡的创建过程
sound card
注册声卡使用
snd_card_register接口,这个函数被调用四次,分别注册上面的4个声卡
card 1
snd_card_register card->number 1 driver VIRTUAL AUDIO W
c0 CPU: 0 PID: 65 Comm: saudio-2-10 Tainted: G W 3.10.17-dirty #3
snd_card_register
saudio_snd_init_card
saudio_ctrl_thread
card 2
snd_card_register card->number 2 driver saudiovoip
c0 CPU: 0 PID: 69 Comm: saudio-2-14 Tainted: G W 3.10.17-dirty #3
snd_card_register
saudio_snd_init_card
saudio_ctrl_thread
card 0
snd_card_register card->number 0 driver sprdphone
c0 CPU: 0 PID: 1 Comm: swapper Tainted: G W 3.10.17-dirty #3
c0 [<c0013a1c>] (unwind_backtrace+0x0/0x11c) from [<c0012174>] (show_stack+0x10/0x14)
c0 [<c0012174>] (show_stack+0x10/0x14) from [<c03da6d8>] (snd_card_register+0x150/0x214)
c0 [<c03da6d8>] (snd_card_register+0x150/0x214) from [<c03f4dc8>] (snd_soc_register_card+0x10dc/0x11b8)
c0 [<c03f4dc8>] (snd_soc_register_card+0x10dc/0x11b8) from [<c0288eb4>] (platform_drv_probe+0x14/0x18)
c0 [<c0288eb4>] (platform_drv_probe+0x14/0x18) from [<c0287ba4>] (driver_probe_device+0x11c/0x330)
c0 [<c0287ba4>] (driver_probe_device+0x11c/0x330) from [<c0286020>] (bus_for_each_drv+0x48/0x8c)
c0 [<c0286020>] (bus_for_each_drv+0x48/0x8c) from [<c0287a10>] (device_attach+0x64/0x88)
c0 [<c0287a10>] (device_attach+0x64/0x88) from [<c0286f44>] (bus_probe_device+0x28/0x98)
c0 [<c0286f44>] (bus_probe_device+0x28/0x98) from [<c0285580>] (device_add+0x3f4/0x5a8)
c0 [<c0285580>] (device_add+0x3f4/0x5a8) from [<c0289400>] (platform_device_add+0x168/0x200)
c0 [<c0289400>] (platform_device_add+0x168/0x200) from [<c028960c>] (platform_add_devices+0x20/0x5c)
c0 [<c028960c>] (platform_add_devices+0x20/0x5c) from [<c07904a0>] (init_machine_late+0x1c/0x28)
c0 [<c07904a0>] (init_machine_late+0x1c/0x28) from [<c0009470>] (do_one_initcall_debug+0x34/0xac)
c0 [<c0009470>] (do_one_initcall_debug+0x34/0xac) from [<c0009524>] (do_one_initcall+0x28/0xbc)
c0 [<c0009524>] (do_one_initcall+0x28/0xbc) from [<c078eaf0>] (kernel_init_freeable+0xe4/0x1ac)
c0 [<c078eaf0>] (kernel_init_freeable+0xe4/0x1ac) from [<c055cdd0>] (kernel_init+0x8/0xe4)
c0 [<c055cdd0>] (kernel_init+0x8/0xe4) from [<c000f4d8>] (ret_from_fork+0x14/0x3c)
card 3
snd_card_register card->number 3 driver all-i2s shortname all-i2s longname all-i2s
c0 CPU: 0 PID: 1 Comm: swapper Tainted: G W 3.10.17-dirty #3
c0 [<c0013a1c>] (unwind_backtrace+0x0/0x11c) from [<c0012174>] (show_stack+0x10/0x14)
c0 [<c0012174>] (show_stack+0x10/0x14) from [<c03da6d8>] (snd_card_register+0x150/0x214)
c0 [<c03da6d8>] (snd_card_register+0x150/0x214) from [<c03f4dc8>] (snd_soc_register_card+0x10dc/0x11b8)
c0 [<c03f4dc8>] (snd_soc_register_card+0x10dc/0x11b8) from [<c0288eb4>] (platform_drv_probe+0x14/0x18)
c0 [<c0288eb4>] (platform_drv_probe+0x14/0x18) from [<c0287ba4>] (driver_probe_device+0x11c/0x330)
c0 [<c0287ba4>] (driver_probe_device+0x11c/0x330) from [<c0286020>] (bus_for_each_drv+0x48/0x8c)
c0 [<c0286020>] (bus_for_each_drv+0x48/0x8c) from [<c0287a10>] (device_attach+0x64/0x88)
c0 [<c0287a10>] (device_attach+0x64/0x88) from [<c0286f44>] (bus_probe_device+0x28/0x98)
c0 [<c0286f44>] (bus_probe_device+0x28/0x98) from [<c0285580>] (device_add+0x3f4/0x5a8)
c0 [<c0285580>] (device_add+0x3f4/0x5a8) from [<c0289400>] (platform_device_add+0x168/0x200)
c0 [<c0289400>] (platform_device_add+0x168/0x200) from [<c028960c>] (platform_add_devices+0x20/0x5c)
c0 [<c028960c>] (platform_add_devices+0x20/0x5c) from [<c07904a0>] (init_machine_late+0x1c/0x28)
c0 [<c07904a0>] (init_machine_late+0x1c/0x28) from [<c0009470>] (do_one_initcall_debug+0x34/0xac)
c0 [<c0009470>] (do_one_initcall_debug+0x34/0xac) from [<c0009524>] (do_one_initcall+0x28/0xbc)
c0 [<c0009524>] (do_one_initcall+0x28/0xbc) from [<c078eaf0>] (kernel_init_freeable+0xe4/0x1ac)
c0 [<c078eaf0>] (kernel_init_freeable+0xe4/0x1ac) from [<c055cdd0>] (kernel_init+0x8/0xe4)
c0 [<c055cdd0>] (kernel_init+0x8/0xe4) from [<c000f4d8>] (ret_from_fork+0x14/0x3c)
接下来一card0注册分析snd_soc_register_card的流程
snd_soc_register_card
先把card 0注册的相关的代码贴出来
static struct snd_soc_dai_link vbc_r2p0_codec_v3_dai[] = {
{
.name = "vbc(r2)-codec(v3)-ap",
.stream_name = "HiFi",
.codec_name = "sprd-codec-v3",
.platform_name = "sprd-pcm-audio",
.cpu_dai_name = "vbc-r2p0",
.codec_dai_name = "sprd-codec-v3-i2s",
},
#ifdef CONFIG_SND_SOC_SPRD_VAUDIO
{
.name = "vbc(r2)-codec(v3)-dsp",
.stream_name = "Voice",
.codec_name = "sprd-codec-v3",
.platform_name = "sprd-pcm-audio",
.cpu_dai_name = "vaudio",
.codec_dai_name = "sprd-codec-v3-vaudio",
},
#endif
{
.name = "aux-captrue",
.stream_name = "AuxRecord",
.codec_name = "sprd-codec-v3",
.platform_name = "sprd-pcm-audio",
.cpu_dai_name = "vbc-r2p0-ad23",
.codec_dai_name = "codec-i2s-ext",
},
#ifdef CONFIG_SND_SOC_SPRD_VAUDIO
{
.name = "aux-dsp-captrue",
.stream_name = "AuxDSPCaptrue",
.codec_name = "sprd-codec-v3",
.platform_name = "sprd-pcm-audio",
.cpu_dai_name = "vaudio-ad23",
.codec_dai_name = "codec-vaudio-ext",
},
#endif
{
.name = "vbc(r2)-dfm",
.stream_name = "Dfm",
.codec_name = "sprd-codec-v3",
.platform_name = "sprd-pcm-audio",
.cpu_dai_name = "vbc-dfm",
.codec_dai_name = "sprd-codec-v3-fm",
.ops = &dfm_ops,
},
};
static struct snd_soc_card vbc_r2p0_codec_v3_card = {
.name = "sprdphone",
.dai_link = vbc_r2p0_codec_v3_dai,
.num_links = ARRAY_SIZE(vbc_r2p0_codec_v3_dai),
.owner = THIS_MODULE,
.controls = vbc_r2p0_codec_v3_controls,
.num_controls = ARRAY_SIZE(vbc_r2p0_codec_v3_controls),
.dapm_widgets = sprd_codec_dapm_widgets,
.num_dapm_widgets = ARRAY_SIZE(sprd_codec_dapm_widgets),
.dapm_routes = vbc_r2p0_codec_v3_map,
.num_dapm_routes = ARRAY_SIZE(vbc_r2p0_codec_v3_map),
.late_probe = board_late_probe,
};
static int sprd_asoc_probe(struct platform_device *pdev,
struct snd_soc_card *card)
{
snd_soc_register_card(card);
}
static int vbc_r2p0_codec_v3_probe(struct platform_device *pdev)
{
return sprd_asoc_probe(pdev, &vbc_r2p0_codec_v3_card);
}
static struct platform_driver vbc_r2p0_codec_v3_driver = {
.driver = {
.name = "vbc-r2p0-sprd-codec-v3",
.owner = THIS_MODULE,
.pm = &snd_soc_pm_ops,
.of_match_table = of_match_ptr(vbc_r2p0_codec_v3_of_match),
},
.probe = vbc_r2p0_codec_v3_probe,
.remove = sprd_asoc_remove,
.shutdown = sprd_asoc_shutdown,
};
module_platform_driver(vbc_r2p0_codec_v3_driver);
看snd_soc_register_card这个函数
snd_soc_register_card
{
for (i = 0; i < card->num_links; i++)
card->rtd[i].dai_link = &card->dai_link[i];
snd_soc_instantiate_card(card);
}
static int snd_soc_instantiate_card(struct snd_soc_card *card)
{
/* bind DAIs */
for (i = 0; i < card->num_links; i++) {
ret = soc_bind_dai_link(card, i);
if (ret != 0)
goto base_error;
}
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;
}
}
}
}
soc_bind_dai_link
{
/* Find CPU DAI from registered DAIs*/
list_for_each_entry(cpu_dai, &dai_list, list) {
if (dai_link->cpu_of_node &&
(cpu_dai->dev->of_node != dai_link->cpu_of_node))
continue;
if (dai_link->cpu_name &&
strcmp(dev_name(cpu_dai->dev), dai_link->cpu_name))
continue;
if (dai_link->cpu_dai_name &&
strcmp(cpu_dai->name, dai_link->cpu_dai_name))
continue;
rtd->cpu_dai = cpu_dai;
}
rtd->codec = codec;
rtd->codec_dai = codec_dai;
rtd->platform = platform;
}
snd_soc_register_card中最主要的是调用snd_soc_instantiate_card
snd_soc_instantiate_card中有一些比较重要的函数,如soc_bind_dai_link和soc_probe_link_dais。后面在分析soc_probe_link_dais
看soc_bind_dai_link,主要是从注册的dai driver\platform driver和codec 找到与该dai_link对应的驱动。匹配主要是通过name进行
以dai为例,看下dai_list怎么来的
dai_list
dai_list来自两个接口snd_soc_register_dais/snd_soc_register_dai
上层通过snd_soc_register_codec/snd_soc_register_component调用上面两个函数注册dai driver
c0 [<c0012174>] (show_stack+0x10/0x14) from [<c03f1010>] (snd_soc_register_dais+0x174/0x220)
c0 [<c03f1010>] (snd_soc_register_dais+0x174/0x220) from [<c03f16f0>] (snd_soc_register_codec+0x1f8/0x2e0)
c0 [<c03f16f0>] (snd_soc_register_codec+0x1f8/0x2e0) from [<c040bd90>] (sprd_codec_probe+0x194/0x32c)
c0 [<c040bd90>] (sprd_codec_probe+0x194/0x32c) from [<c0288eb4>] (platform_drv_probe+0x14/0x18)
c0 [<c0288eb4>] (platform_drv_probe+0x14/0x18) from [<c0287ba4>] (driver_probe_device+0x11c/0x330)
c0 [<c0287ba4>] (driver_probe_device+0x11c/0x330) from [<c0286020>] (bus_for_each_drv+0x48/0x8c)
c0 [<c0286020>] (bus_for_each_drv+0x48/0x8c) from [<c0287a10>] (device_attach+0x64/0x88)
c0 [<c0287a10>] (device_attach+0x64/0x88) from [<c0286f44>] (bus_probe_device+0x28/0x98)
c0 [<c0286f44>] (bus_probe_device+0x28/0x98) from [<c0285580>] (device_add+0x3f4/0x5a8)
c0 [<c0285580>] (device_add+0x3f4/0x5a8) from [<c0289400>] (platform_device_add+0x168/0x200)
c0 [<c0289400>] (platform_device_add+0x168/0x200) from [<c028960c>] (platform_add_devices+0x20/0x5c)
c0 [<c028960c>] (platform_add_devices+0x20/0x5c) from [<c07904a0>] (init_machine_late+0x1c/0x28)
c0 [<c07904a0>] (init_machine_late+0x1c/0x28) from [<c0009470>] (do_one_initcall_debug+0x34/0xac)
c0 [<c0009470>] (do_one_initcall_debug+0x34/0xac) from [<c0009524>] (do_one_initcall+0x28/0xbc)
c0 [<c0009524>] (do_one_initcall+0x28/0xbc) from [<c078eaf0>] (kernel_init_freeable+0xe4/0x1ac)
c0 [<c078eaf0>] (kernel_init_freeable+0xe4/0x1ac) from [<c055cdb4>] (kernel_init+0x8/0xe4)
c0 [<c055cdb4>] (kernel_init+0x8/0xe4) from [<c000f4d8>] (ret_from_fork+0x14/0x3c)