上篇说到Machine驱动中的snd_soc_register_card函数初始化到了最核心的snd_soc_instantiate_card函数,可以说这才是重中之重,本篇分三部分来讲这个内容。
Linux 4.9.123 可从以下地址获得
https://mirrors.edge.kernel.org/pub/linux/kernel/v4.x/
本文Codec基于wm8994。
1 soc_bind_dai_link
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19static int snd_soc_instantiate_card(struct snd_soc_card *card) { struct snd_soc_codec *codec; struct snd_soc_pcm_runtime *rtd; struct snd_soc_dai_link *dai_link; int ret, i, order; ... /* bind DAIs */ for (i = 0; i < card->num_links; i++) { ret = soc_bind_dai_link(card, &card->dai_link[i]); //绑定 if (ret != 0) goto base_error; } ... }
其函数定义如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53static int soc_bind_dai_link(struct snd_soc_card *card, struct snd_soc_dai_link *dai_link) { struct snd_soc_pcm_runtime *rtd; struct snd_soc_dai_link_component *codecs = dai_link->codecs; struct snd_soc_dai_link_component cpu_dai_component; struct snd_soc_dai **codec_dais; struct snd_soc_platform *platform; const char *platform_name; int i; dev_dbg(card->dev, "ASoC: binding %sn", dai_link->name); if (soc_is_dai_link_bound(card, dai_link)) { //判断是否已经绑定 dev_dbg(card->dev, "ASoC: dai link %s already boundn", dai_link->name); return 0; } rtd = soc_new_pcm_runtime(card, dai_link); if (!rtd) return -ENOMEM; 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); if (!rtd->cpu_dai) { //如果cpu_dai没有初始化过,其实就是dai_link的cpu_*几个成员 dev_err(card->dev, "ASoC: CPU DAI %s not registeredn", 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) { //如果platform没有初始化过 dev_err(card->dev, "ASoC: platform %s not registeredn", dai_link->platform_name); goto _err_defer; } soc_add_pcm_runtime(card, rtd); return 0; _err_defer: soc_free_pcm_runtime(rtd); return -EPROBE_DEFER; }
首先,绑定每一个dai_link到card: ASoC定义了三个全局的链表头变量:codec_list、dai_list、platform_list,系统中所有的Codec、DAI、Platform都在注册时连接到这三个全局链表上。soc_bind_dai_link函数逐个扫描这三个链表,根据card->dai_link[]中的名称进行匹配,匹配后把相应的codec,dai和platform实例赋值到card->rtd[]中(snd_soc_pcm_runtime)。经过这个过程后,snd_soc_pcm_runtime:(card->rtd)中保存了本Machine中使用的Codec,DAI和Platform驱动的信息。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30/* bind aux_devs too */ for (i = 0; i < card->num_aux_devs; i++) { ret = soc_bind_aux_dev(card, i); if (ret != 0) goto base_error; } /* add predefined DAI links to the list */ for (i = 0; i < card->num_links; i++) snd_soc_add_dai_link(card, card->dai_link+i); /* initialize the register cache for each available 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 */ 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; }
然后绑定aux_dev;动态地添加dai_link到链表里,这样,就可以从链表里找到这些dai_link;为每个codec初始化寄存器缓存,基本上该绑定地都已经绑定到struct snd_soc_card card;
里了,现在可以添加这个声卡了。
<2> 创建一个card专用的debugfs,关于debugfs可以看这篇文章: [Linux]使用debugfs文件系统,但是这里只是留了一个接口,追进去以后可以看到,这是一个空函数
1
2soc_init_card_debugfs(card);
<3> 声卡创建好了,下面开始进行dapm的工作,毕竟这部分工作堪称alsa的点睛之笔。
1
2
3
4
5
6
7
8if (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);
添加snd_soc_dapm_new_controls,调用probe函数初始化card,仅一次
1
2
3
4
5
6
7/* initialise the sound card only once */ if (card->probe) { ret = card->probe(card); if (ret < 0) goto card_probe_error; }
对于之前维护的rtd_list链表,probe 所有 这个card上DAI links用到的components。
1
2
3
4
5
6
7
8
9
10
11
12
13/* probe all components used by DAI links on this card */ for (order = SND_SOC_COMP_ORDER_FIRST; order <= SND_SOC_COMP_ORDER_LAST; order++) { list_for_each_entry(rtd, &card->rtd_list, list) { ret = soc_probe_link_components(card, rtd, order); if (ret < 0) { dev_err(card->dev, "ASoC: failed to instantiate card %dn", ret); goto probe_dai_err; } } }
/* 对于每个card->aux_comp_list链表,probe 辅助 components */
1
2
3
4ret = soc_probe_aux_devices(card); if (ret < 0) goto probe_dai_err;
查找在探测components期间添加的新DAI链接并绑定它们,因为具有拓扑结构的组件可能会带来新的DAI和DAI链接。
1
2
3
4
5
6
7
8
9
10
11
12list_for_each_entry(dai_link, &card->dai_link_list, list) { if (soc_is_dai_link_bound(card, dai_link)) continue; ret = soc_init_dai_link(card, dai_link); if (ret) goto probe_dai_err; ret = soc_bind_dai_link(card, dai_link); if (ret) goto probe_dai_err; }
/* probe the platform */
接下来soc_probe_link_dais,在soc_probe_link_dais函数里挨个调用了cpu-dai, codec-dai(可能有n个)的probe函数,然后调用了dai_link的init函数,在这里我们看到一条注释/* do machine specific initialization */
,也就是说dai_link就是machine。在snd_soc_runtime_set_dai_fmt
中更新连接到指定运行时的DAI link的所有DAI的DAI链接格式。然后使用snd_soc_pcm_runtime
和name
再次进行component的初始化,然后创建cpu-dai端的compress_device,最后调用soc_new_pcm()函数用于创建标准alsa驱动的pcm逻辑设备。
1
2
3
4
5
6
7
8
9
10
11
12for (order = SND_SOC_COMP_ORDER_FIRST; order <= SND_SOC_COMP_ORDER_LAST; order++) { list_for_each_entry(rtd, &card->rtd_list, list) { ret = soc_probe_link_dais(card, rtd, order); if (ret < 0) { dev_err(card->dev, "ASoC: failed to instantiate card %dn", ret); goto probe_dai_err; } } }
soc_new_pcm
函数首先初始化snd_soc_runtime中的snd_pcm_ops字段,也就是rtd->ops中的部分成员,例如open,close,hw_params等,紧接着调用标准alsa驱动中的创建pcm的函数snd_pcm_new()创建声卡的pcm实例,pcm的private_data字段设置为该runtime变量rtd,然后用platform驱动中的snd_pcm_ops替换部分pcm中的snd_pcm_ops字段,最后,调用platform驱动的pcm_new回调,该回调实现该platform下的dma内存申请和dma初始化等相关工作。到这里,声卡和他的pcm实例创建完成。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132/* create a new 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; 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); 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 %sn", rtd->dai_link->name); return ret; } dev_dbg(rtd->card->dev, "ASoC: registered pcm #%d %sn",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 */ 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); if (capture) snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &rtd->ops); if (platform->driver->pcm_new) { ret = platform->driver->pcm_new(rtd); if (ret < 0) { dev_err(platform->dev, "ASoC: pcm constructor failed: %dn", ret); return ret; } } pcm->private_free = platform->driver->pcm_free; out: dev_info(rtd->card->dev, "%s <-> %s mapping okn", (rtd->num_codecs > 1) ? "multicodec" : rtd->codec_dai->name, cpu_dai->name); return ret; }
接下来snd_soc_dapm_link_dai_widgets
找到具有相同stream的所有widgets并链接它们
1
2
3snd_soc_dapm_link_dai_widgets(card); snd_soc_dapm_connect_dai_link_widgets(card);
1
2
3
4
5
6
7
8
9
10
11if (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);
执行late_probe函数,这个函数对应于在imx-wm8524.c
中定义的imx_wm8524_late_probe
1
2
3
4
5
6
7
8
9if (card->late_probe) { ret = card->late_probe(card); if (ret < 0) { dev_err(card->dev, "ASoC: %s late_probe() failed: %dn", card->name, ret); goto probe_aux_dev_err; } }
检查codec是否有任何新的dapm widget,如果找到则创建它们。
1
2snd_soc_dapm_new_widgets(card);
最后,调用snd_card_register函数,注册分配给声卡的所有设备。在调用此函数之前,ALSA控制接口将被阻止从外部访问。 因此,应该在声卡初始化结束时调用此函数。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46int snd_card_register(struct snd_card *card) { int err; if (snd_BUG_ON(!card)) return -EINVAL; if (!card->registered) { err = device_add(&card->card_dev); if (err < 0) return err; card->registered = true; } 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; } EXPORT_SYMBOL(snd_card_register);
最后
以上就是迷人热狗最近收集整理的关于[Alsa]9, Machine驱动的编写(2)1 soc_bind_dai_link的全部内容,更多相关[Alsa]9,内容请搜索靠谱客的其他文章。
发表评论 取消回复