linux-alsa详解7 ASOC-machine

前面的内容我们提到,ASoC被分为Machine、Platform和Codec三大部分,其中的Machine驱动负责Platform和Codec之间的耦合以及部分和设备或板子特定的代码,再次引用上一节的内容:Machine驱动负责处理机器特有的一些控件和音频事件(例如,当播放音频时,需要先行打开一个放大器);单独的Platform和Codec驱动是不能工作的,它必须由Machine驱动把它们结合在一起才能完成整个设备的音频处理工作.

以WM8994的一款Samsung的开发板SMDK为例子做说明,WM8994是一颗Wolfson生产的多功能Codec芯片.

代码位于:/sound/soc/samsung/smdk_wm8994.c

1 注册platfrom_driver

 1 static struct platform_driver smdk_audio_driver = {
 2     .driver        = {
 3         .name    = "smdk-audio-wm8994",
 4         .of_match_table = of_match_ptr(samsung_wm8994_of_match),
 5         .pm    = &snd_soc_pm_ops,
 6     },
 7     .probe        = smdk_audio_probe,
 8 };
 9 
10 module_platform_driver(smdk_audio_driver);

 2 probe函数

 1 static int smdk_audio_probe(struct platform_device *pdev)
 2 {
 3     int ret;
 4     struct device_node *np = pdev->dev.of_node;
 5     struct snd_soc_card *card = &smdk;//给声卡赋值
 6     struct smdk_wm8994_data *board;
 7     const struct of_device_id *id;
 8 
 9     card->dev = &pdev->dev;
10 
11     board = devm_kzalloc(&pdev->dev, sizeof(*board), GFP_KERNEL);
12     if (!board)
13         return -ENOMEM;
14 
15     if (np) {
16         smdk_dai[0].cpu_dai_name = NULL;
17         smdk_dai[0].cpu_of_node = of_parse_phandle(np,
18                 "samsung,i2s-controller", 0);
19         if (!smdk_dai[0].cpu_of_node) {
20             dev_err(&pdev->dev,
21                "Property 'samsung,i2s-controller' missing or invalid\n");
22             ret = -EINVAL;
23         }
24 
25         smdk_dai[0].platform_name = NULL;
26         smdk_dai[0].platform_of_node = smdk_dai[0].cpu_of_node;
27     }
28 
29     id = of_match_device(of_match_ptr(samsung_wm8994_of_match), &pdev->dev);
30     if (id)
31         *board = *((struct smdk_wm8994_data *)id->data);
32 
33     platform_set_drvdata(pdev, board);
34 
35     ret = devm_snd_soc_register_card(&pdev->dev, card);
36 
37     if (ret)
38         dev_err(&pdev->dev, "snd_soc_register_card() failed:%d\n", ret);
39 
40     return ret;
41 }

 3 smdk的定义初始化

1 static struct snd_soc_card smdk = {
2     .name = "SMDK-I2S",
3     .owner = THIS_MODULE,
4     .dai_link = smdk_dai,
5     .num_links = ARRAY_SIZE(smdk_dai),
6 }

初始化snd_soc_dai_link 

 1 static struct snd_soc_dai_link smdk_dai[] = {
 2     { /* Primary DAI i/f */
 3         .name = "WM8994 AIF1",
 4         .stream_name = "Pri_Dai",
 5         .cpu_dai_name = "samsung-i2s.0",
 6         .codec_dai_name = "wm8994-aif1",
 7         .platform_name = "samsung-i2s.0",
 8         .codec_name = "wm8994-codec",
 9         .init = smdk_wm8994_init_paiftx,
10         .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
11             SND_SOC_DAIFMT_CBM_CFM,
12         .ops = &smdk_ops,
13     }, { /* Sec_Fifo Playback i/f */
14         .name = "Sec_FIFO TX",
15         .stream_name = "Sec_Dai",
16         .cpu_dai_name = "samsung-i2s-sec",
17         .codec_dai_name = "wm8994-aif1",
18         .platform_name = "samsung-i2s-sec",
19         .codec_name = "wm8994-codec",
20         .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
21             SND_SOC_DAIFMT_CBM_CFM,
22         .ops = &smdk_ops,
23     },
24 }

通过snd_soc_card结构,又引出了Machine驱动的另外两个个数据结构:

snd_soc_dai_link(实例:smdk_dai[] )

snd_soc_ops(实例:smdk_ops )

其中,snd_soc_dai_link中,指定了Platform、Codec、codec_dai、cpu_dai的名字,稍后Machine驱动将会利用这些名字去匹配已经在系统中注册的platform,codec,dai,这些注册的部件都是在另外相应的Platform驱动和Codec驱动的代码文件中定义的,这样看来,Machine驱动的设备初始化代码无非就是选择合适Platform和Codec以及dai,用他们填充以上几个数据结构,然后注册Platform设备即可.当然还要实现连接Platform和Codec的dai_link对应的ops实现,本例就是smdk_ops,它只实现了hw_params函数:smdk_hw_params.

4 函数devm_snd_soc_register_card

probe调用此函数注册声卡

定义位于:sound\soc\soc-devres.c

 1 /**
 2  * devm_snd_soc_register_card - resource managed card registration
 3  * @dev: Device used to manage card
 4  * @card: Card to register
 5  *
 6  * Register a card with automatic unregistration when the device is
 7  * unregistered.
 8  */
 9 int devm_snd_soc_register_card(struct device *dev, struct snd_soc_card *card)
10 {
11     struct snd_soc_card **ptr;
12     int ret;
13 
14     ptr = devres_alloc(devm_card_release, sizeof(*ptr), GFP_KERNEL);
15     if (!ptr)
16         return -ENOMEM;
17 
18     ret = snd_soc_register_card(card);
19     if (ret == 0) {
20         *ptr = card;
21         devres_add(dev, ptr);
22     } else {
23         devres_free(ptr);
24     }
25 
26     return ret;
27 }

5 函数snd_soc_register_card

调用snd_soc_register_card,通过snd_soc_register_card,为snd_soc_pcm_runtime数组申请内存,每一个dai_link对应snd_soc_pcm_runtime数组的一个单元,然后把snd_soc_card中的dai_link配置复制到相应的snd_soc_pcm_runtime中,最后,大部分的工作都在snd_soc_instantiate_card中实现。

 1 /**
 2  * snd_soc_register_card - Register a card with the ASoC core
 3  *
 4  * @card: Card to register
 5  *
 6  */
 7 int snd_soc_register_card(struct snd_soc_card *card)
 8 {
 9     int i, ret;
10     struct snd_soc_pcm_runtime *rtd;
11 
12     if (!card->name || !card->dev)
13         return -EINVAL;
14 
15     for (i = 0; i < card->num_links; i++) {//循环查找声卡中的dai_link
16         struct snd_soc_dai_link *link = &card->dai_link[i];//定义dai_link,逐个拿到声卡中的dai_link
17 
18         ret = soc_init_dai_link(card, link);//检查确认dai->link中的codec/platform 信息
19         if (ret) {
20             dev_err(card->dev, "ASoC: failed to init link %s\n",
21                 link->name);
22             return ret;
23         }
24     }
25 
26     dev_set_drvdata(card->dev, card);
27     /*以下是初始化card中的一系列链表*/
28     snd_soc_initialize_card_lists(card);
29     /*初始化card中的链表*/
30     INIT_LIST_HEAD(&card->dai_link_list);
31     card->num_dai_links = 0;
32 
33     INIT_LIST_HEAD(&card->rtd_list);
34     card->num_rtd = 0;
35 
36     INIT_LIST_HEAD(&card->dapm_dirty);
37     INIT_LIST_HEAD(&card->dobj_list);
38     card->instantiated = 0;//card中的初始化标志
39     mutex_init(&card->mutex);
40     mutex_init(&card->dapm_mutex);
41 
42     ret = snd_soc_instantiate_card(card);//初始化card的重要函数
43     if (ret != 0)
44         return ret;
45 
46     /* deactivate pins to sleep state */
47     list_for_each_entry(rtd, &card->rtd_list, list)  {
48         struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
49         int j;
50 
51         for (j = 0; j < rtd->num_codecs; j++) {
52             struct snd_soc_dai *codec_dai = rtd->codec_dais[j];
53             if (!codec_dai->active)
54                 pinctrl_pm_select_sleep_state(codec_dai->dev);
55         }
56 
57         if (!cpu_dai->active)
58             pinctrl_pm_select_sleep_state(cpu_dai->dev);
59     }
60 
61     return ret;
62 }

5.1 函数soc_init_dai_link

 1 static int soc_init_dai_link(struct snd_soc_card *card,
 2                    struct snd_soc_dai_link *link)
 3 {
 4     int i, ret;
 5 
 6     ret = snd_soc_init_multicodec(card, link);//用card->codec给声卡中codecs赋值
 7     if (ret) {
 8         dev_err(card->dev, "ASoC: failed to init multicodec\n");
 9         return ret;
10     }
11 
12     for (i = 0; i < link->num_codecs; i++) {
13         /*
14          * Codec must be specified by 1 of name or OF node,
15          * not both or neither.
16          */
17         if (!!link->codecs[i].name ==
18             !!link->codecs[i].of_node) {
19             dev_err(card->dev, "ASoC: Neither/both codec name/of_node are set for %s\n",
20                 link->name);
21             return -EINVAL;
22         }
23         /* Codec DAI name must be specified */
24         if (!link->codecs[i].dai_name) {
25             dev_err(card->dev, "ASoC: codec_dai_name not set for %s\n",
26                 link->name);
27             return -EINVAL;
28         }
29     }
30 
31     /*
32      * Platform may be specified by either name or OF node, but
33      * can be left unspecified, and a dummy platform will be used.
34      */
35     if (link->platform_name && link->platform_of_node) {
36         dev_err(card->dev,
37             "ASoC: Both platform name/of_node are set for %s\n",
38             link->name);
39         return -EINVAL;
40     }
41 
42     /*
43      * CPU device may be specified by either name or OF node, but
44      * can be left unspecified, and will be matched based on DAI
45      * name alone..
46      */
47     if (link->cpu_name && link->cpu_of_node) {
48         dev_err(card->dev,
49             "ASoC: Neither/both cpu name/of_node are set for %s\n",
50             link->name);
51         return -EINVAL;
52     }
53     /*
54      * At least one of CPU DAI name or CPU device name/node must be
55      * specified
56      */
57     if (!link->cpu_dai_name &&
58         !(link->cpu_name || link->cpu_of_node)) {
59         dev_err(card->dev,
60             "ASoC: Neither cpu_dai_name nor cpu_name/of_node are set for %s\n",
61             link->name);
62         return -EINVAL;
63     }
64 
65     return 0;
66 }

5.2 结构体soc_init_dai_link

定义位于:linux-4.9.73\include\sound\soc.h 

snd_soc_dai_link中,指定了Platform、Codec、codec_dai、cpu_dai的名字,Machine驱动将会利用这些名字去匹配已经在系统中注册的platform、codec和dai,这些注册的部件都是在另外相应的Platform驱动和Codec驱动的代码文件中定义,最后会赋值给结构体snd_soc_pcm_runtime 中。

6 snd_soc_instantiate_card

snd_soc_instantiate_card主要作用四方面:

作用1:遍历每一对dai_link,进行codec、platform、dai的绑定工作。

    1)ASoC定义了三个全局的链表头变量:codec_list、dai_list、platform_list,系统中所有的Codec、DAI、Platform都在注册时连接到这三个全局链表上,soc_bind_dai_link函数逐个扫描这三个链表。

    2)根据card->dai_link[]中的名称进行匹配,匹配后把相应的codec,dai和platform实例赋值到card->rtd[]中(snd_soc_pcm_runtime)。经过这个过程后,snd_soc_pcm_runtime:(card->rtd)中保存了本Machine中使用的Codec,DAI和Platform驱动的信息。

    3)之后将snd_soc_pcm_runtime加入card的链表中。

作用2:snd_soc_instantiate_card接着初始化Codec的寄存器缓存,然后调用标准的alsa函数snd_card_new创建声卡实例。

作用3:调用machine 、codec和platform的probe函数

作用4:注册声卡snd_card_register(card->snd_card);

  1 static int snd_soc_instantiate_card(struct snd_soc_card *card)
  2 {
  3     struct snd_soc_codec *codec;
  4     struct snd_soc_pcm_runtime *rtd;//建立snd_soc_pcm_runtime结构体,很重要的一个结构体
  5     struct snd_soc_dai_link *dai_link;
  6     int ret, i, order;
  7 
  8     mutex_lock(&client_mutex);
  9     mutex_lock_nested(&card->mutex, SND_SOC_CARD_CLASS_INIT);
 10 
 11     /* bind DAIs */
 12     for (i = 0; i < card->num_links; i++) {
 13         ret = soc_bind_dai_link(card, &card->dai_link[i]);//初始化rtd
 14         if (ret != 0)
 15             goto base_error;
 16     }
 17 
 18     /* bind aux_devs too */
 19     for (i = 0; i < card->num_aux_devs; i++) {
 20         ret = soc_bind_aux_dev(card, i);
 21         if (ret != 0)
 22             goto base_error;
 23     }
 24 
 25     /* add predefined DAI links to the list */
 26     for (i = 0; i < card->num_links; i++)
 27         snd_soc_add_dai_link(card, card->dai_link+i);
 28 
 29     /* initialize the register cache for each available codec */
 30     list_for_each_entry(codec, &codec_list, list) {
 31         if (codec->cache_init)
 32             continue;
 33         ret = snd_soc_init_codec_cache(codec);
 34         if (ret < 0)
 35             goto base_error;
 36     }
 37 
 38     /* card bind complete so register a sound card */
 39     ret = snd_card_new(card->dev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1,//建立声卡设备snd_card,生成声卡设备节点。snd_soc_instantiate_card函数作用2
 40             card->owner, 0, &card->snd_card);
 41     if (ret < 0) {
 42         dev_err(card->dev,
 43             "ASoC: can't create sound card for card %s: %d\n",
 44             card->name, ret);
 45         goto base_error;
 46     }
 47 
 48     soc_init_card_debugfs(card);
 49 
 50     card->dapm.bias_level = SND_SOC_BIAS_OFF;
 51     card->dapm.dev = card->dev;
 52     card->dapm.card = card;
 53     list_add(&card->dapm.list, &card->dapm_list);
 54 
 55 #ifdef CONFIG_DEBUG_FS
 56     snd_soc_dapm_debugfs_init(&card->dapm, card->debugfs_card_root);
 57 #endif
 58 
 59 #ifdef CONFIG_PM_SLEEP
 60     /* deferred resume work */
 61     INIT_WORK(&card->deferred_resume_work, soc_resume_deferred);
 62 #endif
 63 
 64     if (card->dapm_widgets)
 65         snd_soc_dapm_new_controls(&card->dapm, card->dapm_widgets,
 66                       card->num_dapm_widgets);
 67 
 68     if (card->of_dapm_widgets)
 69         snd_soc_dapm_new_controls(&card->dapm, card->of_dapm_widgets,
 70                       card->num_of_dapm_widgets);
 71      /*依次调用各个子结构的probe函数*/
 72     /* initialise the sound card only once */
 73     if (card->probe) { machine的probe函数
 74         ret = card->probe(card);
 75         if (ret < 0)
 76             goto card_probe_error;
 77     }
 78 
 79     /* probe all components used by DAI links on this card */
 80     for (order = SND_SOC_COMP_ORDER_FIRST; order <= SND_SOC_COMP_ORDER_LAST;
 81             order++) {
 82         list_for_each_entry(rtd, &card->rtd_list, list) {
 83             ret = soc_probe_link_components(card, rtd, order);//调用codec和plafform的probe函数
 84             if (ret < 0) {
 85                 dev_err(card->dev,
 86                     "ASoC: failed to instantiate card %d\n",
 87                     ret);
 88                 goto probe_dai_err;
 89             }
 90         }
 91     }
 92 
 93     /* probe auxiliary components */
 94     ret = soc_probe_aux_devices(card);
 95     if (ret < 0)
 96         goto probe_dai_err;
 97 
 98     /* Find new DAI links added during probing components and bind them.
 99      * Components with topology may bring new DAIs and DAI links.
100      */
101     list_for_each_entry(dai_link, &card->dai_link_list, list) {
102         if (soc_is_dai_link_bound(card, dai_link))
103             continue;
104 
105         ret = soc_init_dai_link(card, dai_link);
106         if (ret)
107             goto probe_dai_err;
108         ret = soc_bind_dai_link(card, dai_link);
109         if (ret)
110             goto probe_dai_err;
111     }
112 
113     /* probe all DAI links on this card */
114     for (order = SND_SOC_COMP_ORDER_FIRST; order <= SND_SOC_COMP_ORDER_LAST;
115             order++) {
116         list_for_each_entry(rtd, &card->rtd_list, list) {
117             ret = soc_probe_link_dais(card, rtd, order);
118             if (ret < 0) {
119                 dev_err(card->dev,
120                     "ASoC: failed to instantiate card %d\n",
121                     ret);
122                 goto probe_dai_err;
123             }
124         }
125     }
126 
127     snd_soc_dapm_link_dai_widgets(card);
128     snd_soc_dapm_connect_dai_link_widgets(card);
129 
130     if (card->controls)
131         snd_soc_add_card_controls(card, card->controls, card->num_controls);
132 
133     if (card->dapm_routes)
134         snd_soc_dapm_add_routes(&card->dapm, card->dapm_routes,
135                     card->num_dapm_routes);
136 
137     if (card->of_dapm_routes)
138         snd_soc_dapm_add_routes(&card->dapm, card->of_dapm_routes,
139                     card->num_of_dapm_routes);
140 
141     snprintf(card->snd_card->shortname, sizeof(card->snd_card->shortname),
142          "%s", card->name);
143     snprintf(card->snd_card->longname, sizeof(card->snd_card->longname),
144          "%s", card->long_name ? card->long_name : card->name);
145     snprintf(card->snd_card->driver, sizeof(card->snd_card->driver),
146          "%s", card->driver_name ? card->driver_name : card->name);
147     for (i = 0; i < ARRAY_SIZE(card->snd_card->driver); i++) {
148         switch (card->snd_card->driver[i]) {
149         case '_':
150         case '-':
151         case '\0':
152             break;
153         default:
154             if (!isalnum(card->snd_card->driver[i]))
155                 card->snd_card->driver[i] = '_';
156             break;
157         }
158     }
159 
160     if (card->late_probe) {
161         ret = card->late_probe(card);
162         if (ret < 0) {
163             dev_err(card->dev, "ASoC: %s late_probe() failed: %d\n",
164                 card->name, ret);
165             goto probe_aux_dev_err;
166         }
167     }
168 
169     snd_soc_dapm_new_widgets(card);
170 
171     ret = snd_card_register(card->snd_card);//注册声卡snd_card,即snd_soc_instantiate_card函数作用4
172     if (ret < 0) {
173         dev_err(card->dev, "ASoC: failed to register soundcard %d\n",
174                 ret);
175         goto probe_aux_dev_err;
176     }
177 
178     card->instantiated = 1;//给声卡初始化标志位置1标志已初始化
179     snd_soc_dapm_sync(&card->dapm);
180     mutex_unlock(&card->mutex);
181     mutex_unlock(&client_mutex);
182 
183     return 0;
184 
185 probe_aux_dev_err:
186     soc_remove_aux_devices(card);
187 
188 probe_dai_err:
189     soc_remove_dai_links(card);
190 
191 card_probe_error:
192     if (card->remove)
193         card->remove(card);
194 
195     snd_soc_dapm_free(&card->dapm);
196     soc_cleanup_card_debugfs(card);
197     snd_card_free(card->snd_card);
198 
199 base_error:
200     soc_remove_pcm_runtimes(card);
201     mutex_unlock(&card->mutex);
202     mutex_unlock(&client_mutex);
203 
204     return ret;
205 }

6.1 函数soc_bind_dai_link

即snd_soc_instantiate_card的作用1:遍历每一对dai_link,进行codec、platform、dai的绑定工作。

作用:(1)ASoC定义了三个全局的链表头变量:codec_list、dai_list、platform_list,系统中所有的Codec、DAI、Platform都在注册时连接到这三个全局链表上

(2)函数逐个扫描这三个链表,根据card->dai_link[]中的名称进行匹配,匹配后把相应的codec,dai和platform实例赋值到card->rtd[]中(snd_soc_pcm_runtime).经过这个过程后,snd_soc_pcm_runtime:(card->rtd)中保存了本Machine中使用的Codec,DAI和Platform驱动的信息

 1 static int soc_bind_dai_link(struct snd_soc_card *card,
 2     struct snd_soc_dai_link *dai_link)
 3 {
 4     struct snd_soc_pcm_runtime *rtd;
 5     struct snd_soc_dai_link_component *codecs = dai_link->codecs;
 6     struct snd_soc_dai_link_component cpu_dai_component;
 7     struct snd_soc_dai **codec_dais;
 8     struct snd_soc_platform *platform;
 9     const char *platform_name;
10     int i;
11 
12     dev_dbg(card->dev, "ASoC: binding %s\n", dai_link->name);
13 
14     if (soc_is_dai_link_bound(card, dai_link)) {//确认card->dai_link和rtd->dai_link信息一致
15         dev_dbg(card->dev, "ASoC: dai link %s already bound\n",
16             dai_link->name);
17         return 0;
18     }
19 
20     rtd = soc_new_pcm_runtime(card, dai_link);//分配rtd结构体并初始化部分成员
21     if (!rtd)
22         return -ENOMEM;
23     /*用dai_link中信息给dai component赋值*/
24     cpu_dai_component.name = dai_link->cpu_name;
25     cpu_dai_component.of_node = dai_link->cpu_of_node;
26     cpu_dai_component.dai_name = dai_link->cpu_dai_name;
27     rtd->cpu_dai = snd_soc_find_dai(&cpu_dai_component);//查找已注册的dai,拿到cpu_dai信息给rtd赋值。实参就是dai_link中的platform的dai信息
28     if (!rtd->cpu_dai) {
29         dev_err(card->dev, "ASoC: CPU DAI %s not registered\n",
30             dai_link->cpu_dai_name);
31         goto _err_defer;
32     }
33     /*用card中的codec 给rtd赋值*/
34     rtd->num_codecs = dai_link->num_codecs;//codec的个数
35 
36     /* Find CODEC from registered CODECs */
37     codec_dais = rtd->codec_dais;
38     for (i = 0; i < rtd->num_codecs; i++) {
39         codec_dais[i] = snd_soc_find_dai(&codecs[i]);//实参codecs就是dai_link信息,拿到codec dai信息
40         if (!codec_dais[i]) {
41             dev_err(card->dev, "ASoC: CODEC DAI %s not registered\n",
42                 codecs[i].dai_name);
43             goto _err_defer;
44         }
45     }
46     /*将查找到的codec dai信息赋给rtd*/
47     /* Single codec links expect codec and codec_dai in runtime data */
48     rtd->codec_dai = codec_dais[0];
49     rtd->codec = rtd->codec_dai->codec;
50 
51     /* if there's no platform we match on the empty platform */
52     platform_name = dai_link->platform_name;
53     if (!platform_name && !dai_link->platform_of_node)
54         platform_name = "snd-soc-dummy";
55 
56     /* find one from the set of registered platforms */
57     list_for_each_entry(platform, &platform_list, list) {
58         if (dai_link->platform_of_node) {
59             if (platform->dev->of_node !=
60                 dai_link->platform_of_node)
61                 continue;
62         } else {
63             if (strcmp(platform->component.name, platform_name))
64                 continue;
65         }
66 
67         rtd->platform = platform;
68     }
69     if (!rtd->platform) {
70         dev_err(card->dev, "ASoC: platform %s not registered\n",
71             dai_link->platform_name);
72         goto _err_defer;
73     }
74 
75     soc_add_pcm_runtime(card, rtd);//将上述赋值好的rtd,见到card中的rtd链表中
76     return 0;
77 
78 _err_defer:
79     soc_free_pcm_runtime(rtd);
80     return  -EPROBE_DEFER;
81 }

6.2 函数snd_soc_find_dai

根据dai_link已经注册的dai信息查找,返回snd_soc_dai

 1 /**
 2  * snd_soc_find_dai - Find a registered DAI
 3  *
 4  * @dlc: name of the DAI and optional component info to match
 5  *
 6  * This function will search all regsitered components and their DAIs to
 7  * find the DAI of the same name. The component's of_node and name
 8  * should also match if being specified.
 9  *
10  * Return: pointer of DAI, or NULL if not found.
11  */
12 struct snd_soc_dai *snd_soc_find_dai(
13     const struct snd_soc_dai_link_component *dlc)
14 {
15     struct snd_soc_component *component;
16     struct snd_soc_dai *dai;
17     struct device_node *component_of_node;
18 
19     lockdep_assert_held(&client_mutex);
20 
21     /* Find CPU DAI from registered DAIs*/
22     list_for_each_entry(component, &component_list, list) {
23         component_of_node = component->dev->of_node;
24         if (!component_of_node && component->dev->parent)
25             component_of_node = component->dev->parent->of_node;
26 
27         if (dlc->of_node && component_of_node != dlc->of_node)
28             continue;
29         if (dlc->name && strcmp(component->name, dlc->name))
30             continue;
31         list_for_each_entry(dai, &component->dai_list, list) {
32             if (dlc->dai_name && strcmp(dai->name, dlc->dai_name))
33                 continue;
34 
35             return dai;
36         }
37     }
38 
39     return NULL;
40 }

6.3 函数soc_add_pcm_runtime

将上述赋值好的rtd,见到card中的rtd链表中

1 static void soc_add_pcm_runtime(struct snd_soc_card *card,
2         struct snd_soc_pcm_runtime *rtd)
3 {
4     list_add_tail(&rtd->list, &card->rtd_list);
5     rtd->num = card->num_rtd;
6     card->num_rtd++;
7 }

7 snd_soc_instantiate_card函数作用3

调用machine 、codec和platform的probe函数

 1 static int snd_soc_instantiate_card(struct snd_soc_card *card)
 2 {
 3     ...
 4 
 5     /* initialise the sound card only once */
 6     if (card->probe) {//machine对应的probe函数
 7         ret = card->probe(card);
 8         if (ret < 0)
 9             goto card_probe_error;
10     }
11 
12     /* probe all components used by DAI links on this card */
13     for (order = SND_SOC_COMP_ORDER_FIRST; order <= SND_SOC_COMP_ORDER_LAST;
14             order++) {
15         list_for_each_entry(rtd, &card->rtd_list, list) {
16             ret = soc_probe_link_components(card, rtd, order);
17             if (ret < 0) {
18                 dev_err(card->dev,
19                     "ASoC: failed to instantiate card %d\n",
20                     ret);
21                 goto probe_dai_err;
22             }
23         }
24     }
25 
26     /* probe auxiliary components */
27     ret = soc_probe_aux_devices(card);
28     if (ret < 0)
29         goto probe_dai_err;
30 
31     ...
32     /* probe all DAI links on this card */
33     for (order = SND_SOC_COMP_ORDER_FIRST; order <= SND_SOC_COMP_ORDER_LAST;
34             order++) {
35         list_for_each_entry(rtd, &card->rtd_list, list) {
36             ret = soc_probe_link_dais(card, rtd, order);
37             if (ret < 0) {
38                 dev_err(card->dev,
39                     "ASoC: failed to instantiate card %d\n",
40                     ret);
41                 goto probe_dai_err;
42             }
43         }
44     }
45 
46     ...
47 
48     if (card->late_probe) {
49         ret = card->late_probe(card);
50         if (ret < 0) {
51             dev_err(card->dev, "ASoC: %s late_probe() 
52     ...
53 }

 7.1 machine的probe函数

也就是struct snd_soc_card card->probe函数

struct snd_soc_card card是从\sound\soc\samsung\smdk_wm8994.c的smdk_audio_probe函数中定义初始化card,然后传下去的,没有发现card->probe函数的定义。定义的地方不知道在哪里???待分析

7.2 函数soc_probe_link_components

 1 static int soc_probe_link_components(struct snd_soc_card *card,
 2             struct snd_soc_pcm_runtime *rtd,
 3                      int order)
 4 {
 5     struct snd_soc_platform *platform = rtd->platform;
 6     struct snd_soc_component *component;
 7     int i, ret;
 8 
 9     /* probe the CPU-side component, if it is a CODEC */
10     component = rtd->cpu_dai->component;//找到 cpu component
11     if (component->driver->probe_order == order) {
12         ret = soc_probe_component(card, component);
13         if (ret < 0)
14             return ret;
15     }
16 
17     /* probe the CODEC-side components */
18     for (i = 0; i < rtd->num_codecs; i++) {//获取codec component
19         component = rtd->codec_dais[i]->component;
20         if (component->driver->probe_order == order) {
21             ret = soc_probe_component(card, component);
22             if (ret < 0)
23                 return ret;
24         }
25     }
26 
27     /* probe the platform */
28     if (platform->component.driver->probe_order == order) {//获取 platform component
29         ret = soc_probe_component(card, &platform->component);
30         if (ret < 0)
31             return ret;
32     }
33 
34     return 0;
35 }

7.3 函数soc_probe_component

根据cpu component /codec component/platform component调用对应的probe函数

 1 static int soc_probe_component(struct snd_soc_card *card,
 2     struct snd_soc_component *component)
 3 {
 4     struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component);
 5     struct snd_soc_dai *dai;
 6     int ret;
 7 
 8     if (!strcmp(component->name, "snd-soc-dummy"))
 9         return 0;
10 
11     if (component->card) {
12         if (component->card != card) {
13             dev_err(component->dev,
14                 "Trying to bind component to card \"%s\" but is already bound to card \"%s\"\n",
15                 card->name, component->card->name);
16             return -ENODEV;
17         }
18         return 0;
19     }
20 
21     if (!try_module_get(component->dev->driver->owner))
22         return -ENODEV;
23 
24     component->card = card;
25     dapm->card = card;
26     soc_set_name_prefix(card, component);
27 
28     soc_init_component_debugfs(component);
29 
30     if (component->dapm_widgets) {
31         ret = snd_soc_dapm_new_controls(dapm, component->dapm_widgets,
32             component->num_dapm_widgets);
33 
34         if (ret != 0) {
35             dev_err(component->dev,
36                 "Failed to create new controls %d\n", ret);
37             goto err_probe;
38         }
39     }
40 
41     list_for_each_entry(dai, &component->dai_list, list) {
42         ret = snd_soc_dapm_new_dai_widgets(dapm, dai);
43         if (ret != 0) {
44             dev_err(component->dev,
45                 "Failed to create DAI widgets %d\n", ret);
46             goto err_probe;
47         }
48     }
49 
50     if (component->probe) {
51         ret = component->probe(component);//调用probe函数
52         if (ret < 0) {
53             dev_err(component->dev,
54                 "ASoC: failed to probe component %d\n", ret);
55             goto err_probe;
56         }
57 
58         WARN(dapm->idle_bias_off &&
59             dapm->bias_level != SND_SOC_BIAS_OFF,
60             "codec %s can not start from non-off bias with idle_bias_off==1\n",
61             component->name);
62     }
63 
64     /* machine specific init */
65     if (component->init) {
66         ret = component->init(component);
67         if (ret < 0) {
68             dev_err(component->dev,
69                 "Failed to do machine specific init %d\n", ret);
70             goto err_probe;
71         }
72     }
73 
74     if (component->controls)
75         snd_soc_add_component_controls(component, component->controls,
76                      component->num_controls);
77     if (component->dapm_routes)
78         snd_soc_dapm_add_routes(dapm, component->dapm_routes,
79                     component->num_dapm_routes);
80 
81     list_add(&dapm->list, &card->dapm_list);
82 
83     /* This is a HACK and will be removed soon */
84     if (component->codec)
85         list_add(&component->codec->card_list, &card->codec_dev_list);
86 
87     return 0;
88 
89 err_probe:
90     soc_cleanup_component_debugfs(component);
91     component->card = NULL;
92     module_put(component->dev->driver->owner);
93 
94     return ret;
95 }

7.3 codec probe来源

7.3.1 codec driver 的probe函数赋值

linux-alsa详解6 asoc-codec中3.1

1 static int wm8994_probe(struct platform_device *pdev)
2 {
3     ...
4 
5     return snd_soc_register_codec(&pdev->dev, &soc_codec_dev_wm8994,
6             wm8994_dai, ARRAY_SIZE(wm8994_dai));
7 }

调用函数snd_soc_register_codec的实参snd_soc_codec_driver定义了probe函数

1 static const struct snd_soc_codec_driver soc_codec_dev_wm8994 = {
2     .probe =    wm8994_codec_probe,
3     .remove =    wm8994_codec_remove,
4     .suspend =    wm8994_codec_suspend,
5     .resume =    wm8994_codec_resume,
6     .get_regmap =   wm8994_get_regmap,
7     .set_bias_level = wm8994_set_bias_level,
8 };

函数wm8994_codec_probe完成codec的初始化,如linux-alsa详解6 asoc-codec中第5节所述。

7.3.2 获取codec driver的probe函数

在codec 注册过程中

 1 int snd_soc_register_codec(struct device *dev,
 2                const struct snd_soc_codec_driver *codec_drv,
 3                struct snd_soc_dai_driver *dai_drv,
 4                int num_dai)
 5 {
 6     ...
 7 
 8     if (codec_drv->probe)
 9         codec->component.probe = snd_soc_codec_drv_probe;
10     if (codec_drv->remove)
11         codec->component.remove = snd_soc_codec_drv_remove;
12     if (codec_drv->write)
13         codec->component.write = snd_soc_codec_drv_write;
14     if (codec_drv->read)
15         codec->component.read = snd_soc_codec_drv_read;
16     codec->component.ignore_pmdown_time = codec_drv->ignore_pmdown_time;
17 
18     ...
19 }

函数snd_soc_codec_drv_probe

根据codec的component获取到结构体codec,然后找到codec driver。codec driver中probe已在上述过程中初始化。

至此codec的probe函数已完美找到。

1 static int snd_soc_codec_drv_probe(struct snd_soc_component *component)
2 {
3     struct snd_soc_codec *codec = snd_soc_component_to_codec(component);
4 
5     return codec->driver->probe(codec);
6 }

7.4 platform probe来源

 1 int snd_soc_add_platform(struct device *dev, struct snd_soc_platform *platform,
 2         const struct snd_soc_platform_driver *platform_drv)
 3 {
 4     int ret;
 5 
 6     ret = snd_soc_component_initialize(&platform->component,
 7             &platform_drv->component_driver, dev);
 8     if (ret)
 9         return ret;
10 
11     platform->dev = dev;
12     platform->driver = platform_drv;
13 
14     if (platform_drv->probe)
15         platform->component.probe = snd_soc_platform_drv_probe;
16     if (platform_drv->remove)
17         platform->component.remove = snd_soc_platform_drv_remove;
18 
19    ...
20 }

继续追踪函数snd_soc_platform_drv_probe

 根据platform->component获取到对应的platform结构体,然后调用platform->driver中的probe

1 static int snd_soc_platform_drv_probe(struct snd_soc_component *component)
2 {
3     struct snd_soc_platform *platform = snd_soc_component_to_platform(component);
4 
5     return platform->driver->probe(platform);
6 }

platform->driver中的probe就是结构体snd_soc_platform_driver中的probe的函数,但是在platform注册过程中没有找到其probe函数的定义,待分析???

7.5 cpu probe来源

待追踪???

8 machine/platform/codec数据结构体总结

(1)machine所用的数据结构体:

snd_soc_card

snd_soc_dai_link

snd_soc_ops

snd_soc_pcm_runtime

全局list:codec_list、component_list、platform_list;dai_list(是挂在component中)

(2)platform所用结构体:

snd_soc_platform和snd_soc_platform_driver

(3)codec所用结构体:

snd_soc_codec,snd_soc_codec_driver;

snd_soc_dai_ops;

(4)都用到的结构体:

snd_soc_component,snd_soc_component_driver;

snd_soc_dai,snd_soc_dai_driver;

 

参考博文:https://www.cnblogs.com/jason-lu/archive/2013/06/07/3123978.html

posted @ 2020-06-18 22:51  Action_er  阅读(1425)  评论(0)    收藏  举报