高通Audio中ASOC的machine驱动(一)

正文

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

ASoC的一切都从Machine驱动开始,包括声卡的注册,绑定Platform和Codec驱动等等;(linux内核版本为3.10.28)

 

回到顶部

1. 注册Platform driver:

ASoC把声卡注册为Platform Device:

复制代码

  1 static int msm8x16_asoc_machine_probe(struct platform_device *pdev)
  2 {
  3     struct snd_soc_card *card;
  4     struct msm8916_asoc_mach_data *pdata = NULL;
  5     struct pinctrl *pinctrl;
  6     const char *card_dev_id = "qcom,msm-snd-card-id";
  7     const char *codec_type = "qcom,msm-codec-type";
  8     const char *hs_micbias_type = "qcom,msm-hs-micbias-type";
  9     const char *ext_pa = "qcom,msm-ext-pa";
 10     const char *mclk = "qcom,msm-mclk-freq";
 11     const char *spk_ext_pa = "qcom,msm-spk-ext-pa";
 12     const char *ptr = NULL;
 13     const char *type = NULL;
 14     const char *ext_pa_str = NULL;
 15     int num_strings;
 16     int ret, id, i;
 17 
 18     pr_err("'msm8x16_asoc_machine_probe ......");
 19     pdata = devm_kzalloc(&pdev->dev,
 20             sizeof(struct msm8916_asoc_mach_data), GFP_KERNEL);
 21     if (!pdata) {
 22         dev_err(&pdev->dev, "Can't allocate msm8x16_asoc_mach_data\n");
 23         ret = -ENOMEM;
 24         goto err1;
 25     }
 26 
 27     pdata->vaddr_gpio_mux_spkr_ctl =
 28         ioremap(LPASS_CSR_GP_IO_MUX_SPKR_CTL , 4);
 29     if (!pdata->vaddr_gpio_mux_spkr_ctl) {
 30         pr_err("%s ioremap failure for addr %x",
 31                 __func__, LPASS_CSR_GP_IO_MUX_SPKR_CTL);
 32         ret = -ENOMEM;
 33         goto err;
 34     }
 35     pdata->vaddr_gpio_mux_mic_ctl =
 36         ioremap(LPASS_CSR_GP_IO_MUX_MIC_CTL , 4);
 37     if (!pdata->vaddr_gpio_mux_mic_ctl) {
 38         pr_err("%s ioremap failure for addr %x",
 39                 __func__, LPASS_CSR_GP_IO_MUX_MIC_CTL);
 40         ret = -ENOMEM;
 41         goto err;
 42     }
 43 
 44     pdata->vaddr_gpio_mux_pcm_ctl =
 45         ioremap(LPASS_CSR_GP_LPAIF_PRI_PCM_PRI_MODE_MUXSEL, 4);
 46     if (!pdata->vaddr_gpio_mux_pcm_ctl) {
 47         pr_err("%s ioremap failure for addr %x",
 48                 __func__,
 49             LPASS_CSR_GP_LPAIF_PRI_PCM_PRI_MODE_MUXSEL);
 50         ret = -ENOMEM;
 51         goto err;
 52     }
 53     ret = of_property_read_u32(pdev->dev.of_node, card_dev_id, &id);
 54     if (ret) {
 55         dev_err(&pdev->dev,
 56             "%s: missing %s in dt node\n", __func__, card_dev_id);
 57         goto err;
 58     }
 59 
 60     pdev->id = id;
 61     if (!pdev->dev.of_node) {
 62         dev_err(&pdev->dev, "No platform supplied from device tree\n");
 63         ret = -EINVAL;
 64         goto err;
 65     }
 66 
 67     ret = of_property_read_u32(pdev->dev.of_node, mclk, &id);
 68     if (ret) {
 69         dev_err(&pdev->dev,
 70             "%s: missing %s in dt node\n", __func__, mclk);
 71         id = DEFAULT_MCLK_RATE;
 72     }
 73     pdata->mclk_freq = id;
 74 
 75     pdata->spk_ext_pa_gpio = of_get_named_gpio(pdev->dev.of_node,
 76                 spk_ext_pa, 0);
 77     if (pdata->spk_ext_pa_gpio < 0) {
 78         dev_dbg(&pdev->dev,
 79             "%s: missing %s in dt node\n", __func__, spk_ext_pa);
 80     } else {
 81         if (gpio_is_valid(pdata->spk_ext_pa_gpio)) {
 82             ret = gpio_request(pdata->spk_ext_pa_gpio, "spk_ext_pa_gpio");
 83             if(ret) {
 84                 pr_err("spk ext pa gpio request failed");
 85                 goto err;
 86             }
 87 
 88             ret = gpio_direction_output(pdata->spk_ext_pa_gpio, 1);
 89             if(ret) {
 90                 pr_err("set_direction for spk ext pa gpio failed\n");
 91                 goto err;
 92             }
 93         } else {
 94                 pr_err("%s: Invaild external_speaker gpio: %d", __func__, pdata->spk_ext_pa_gpio);
 95                 ret = -EINVAL;
 96                 goto err;
 97             }
 98             
 99     }
100 
101     ret = of_property_read_string(pdev->dev.of_node, codec_type, &ptr);
102     if (ret) {
103         dev_err(&pdev->dev,
104             "%s: missing %s in dt node\n", __func__, codec_type);
105         goto err;
106     }
107     if (pdev->id >= MAX_SND_CARDS) {
108         dev_err(&pdev->dev, "Sound Card parsed is wrong, id=%d\n",
109                 pdev->id);
110         ret = -EINVAL;
111         goto err;
112     }
113     if (!strcmp(ptr, "external")) {
114         dev_info(&pdev->dev, "external codec is configured\n");
115         pdata->codec_type = 1;
116             /*Populate external codec TLMM configs*/
117         ret = populate_ext_snd_card_dt_data(pdev);
118         if (ret < 0) {
119             dev_err(&pdev->dev, "error finding the DT\n"
120                 "params ret=%d\n", ret);
121             goto err;
122         }
123         card = populate_ext_snd_card_dailinks(pdev);
124         if (!card) {
125             dev_err(&pdev->dev, "%s: Card uninitialized\n",
126                 __func__);
127             ret = -EPROBE_DEFER;
128             goto err;
129         }
130     } else {
131         card = populate_ext_snd_card_dailinks(pdev);
132         if (!card) {
133             dev_err(&pdev->dev, "%s: Card uninitialized\n",
134                 __func__);
135             ret = -EPROBE_DEFER;
136             goto err;
137         }
138         dev_info(&pdev->dev, "default codec configured\n");
139         pdata->codec_type = 0;
140         num_strings = of_property_count_strings(pdev->dev.of_node,
141                 ext_pa);
142         if (num_strings < 0) {
143             dev_err(&pdev->dev,
144             "%s: missing %s in dt node or length is incorrect\n",
145                     __func__, ext_pa);
146             goto err;
147         }
148         for (i = 0; i < num_strings; i++) {
149             ret = of_property_read_string_index(pdev->dev.of_node,
150                     ext_pa, i, &ext_pa_str);
151             if (ret) {
152                 dev_err(&pdev->dev,
153                     "%s:of read string %s i %d error %d\n",
154                         __func__, ext_pa, i, ret);
155                 goto err;
156             }
157             if (!strcmp(ext_pa_str, "primary"))
158                 pdata->ext_pa = (pdata->ext_pa | PRI_MI2S_ID);
159             else if (!strcmp(ext_pa_str, "secondary"))
160                 pdata->ext_pa = (pdata->ext_pa | SEC_MI2S_ID);
161             else if (!strcmp(ext_pa_str, "tertiary"))
162                 pdata->ext_pa = (pdata->ext_pa | TER_MI2S_ID);
163             else if (!strcmp(ext_pa_str, "quaternary"))
164                 pdata->ext_pa = (pdata->ext_pa | QUAT_MI2S_ID);
165         }
166         pr_debug("%s: ext_pa = %d\n", __func__, pdata->ext_pa);
167         pinctrl = devm_pinctrl_get(&pdev->dev);
168         if (IS_ERR(pinctrl)) {
169             pr_err("%s: Unable to get pinctrl handle\n",
170                     __func__);
171             return -EINVAL;
172         }
173         pinctrl_info.pinctrl = pinctrl;
174         ret = get_cdc_gpio_lines(pinctrl, pdata->ext_pa);
175         if (ret < 0) {
176             pr_err("%s: failed to ger the codec gpio's %d\n",
177                     __func__, ret);
178             goto err;
179         }
180     }
181 
182     ret = of_property_read_string(pdev->dev.of_node,
183         hs_micbias_type, &type);
184     if (ret) {
185         dev_err(&pdev->dev, "%s: missing %s in dt node\n",
186             __func__, hs_micbias_type);
187         goto err;
188     }
189     if (!strcmp(type, "external")) {
190         dev_dbg(&pdev->dev, "Headset is using external micbias\n");
191         mbhc_cfg.hs_ext_micbias = true;
192     } else {
193         dev_dbg(&pdev->dev, "Headset is using internal micbias\n");
194         mbhc_cfg.hs_ext_micbias = false;
195     }
196 
197     /* initialize the mclk */
198     pdata->digital_cdc_clk.i2s_cfg_minor_version =
199                     AFE_API_VERSION_I2S_CONFIG;
200     pdata->digital_cdc_clk.clk_val = pdata->mclk_freq;
201     pdata->digital_cdc_clk.clk_root = 5;
202     pdata->digital_cdc_clk.reserved = 0;
203     /* initialize the digital codec core clk */
204     pdata->digital_cdc_core_clk.clk_set_minor_version =
205             AFE_API_VERSION_I2S_CONFIG;
206     pdata->digital_cdc_core_clk.clk_id =
207             Q6AFE_LPASS_CLK_ID_INTERNAL_DIGITAL_CODEC_CORE;
208     pdata->digital_cdc_core_clk.clk_freq_in_hz =
209             pdata->mclk_freq;
210     pdata->digital_cdc_core_clk.clk_attri =
211             Q6AFE_LPASS_CLK_ATTRIBUTE_COUPLE_NO;
212     pdata->digital_cdc_core_clk.clk_root =
213             Q6AFE_LPASS_CLK_ROOT_DEFAULT;
214     pdata->digital_cdc_core_clk.enable = 1;
215     /* Initialize loopback mode to false */
216     pdata->lb_mode = false;
217 
218     msm8x16_setup_hs_jack(pdev, pdata);
219     msm8x16_dt_parse_cap_info(pdev, pdata);
220 
221     card->dev = &pdev->dev;
222     platform_set_drvdata(pdev, card);
223     snd_soc_card_set_drvdata(card, pdata);
224     ret = snd_soc_of_parse_card_name(card, "qcom,model");
225     if (ret)
226         goto err;
227     /* initialize timer */
228     INIT_DELAYED_WORK(&pdata->disable_mclk_work, disable_mclk);
229     mutex_init(&pdata->cdc_mclk_mutex);
230     atomic_set(&pdata->mclk_rsc_ref, 0);
231     atomic_set(&pdata->mclk_enabled, false);
232     atomic_set(&quat_mi2s_clk_ref, 0);
233     atomic_set(&auxpcm_mi2s_clk_ref, 0);
234 
235     ret = snd_soc_of_parse_audio_routing(card,
236             "qcom,audio-routing");
237     if (ret)
238         goto err;
239 
240     ret = msm8x16_populate_dai_link_component_of_node(card);
241     if (ret) {
242         ret = -EPROBE_DEFER;
243         goto err;
244     }
245 
246     ret = snd_soc_register_card(card);
247     if (ret) {
248         dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n",
249             ret);
250         goto err;
251     }
252 
253     ret = core_get_adsp_ver();
254     if (ret < 0) {
255         ret = -EPROBE_DEFER;
256         dev_info(&pdev->dev, "%s: Get adsp version failed (%d)\n",
257                 __func__, ret);
258         goto err;
259     }
260 
261     return 0;
262 err:
263     if (pdata->vaddr_gpio_mux_spkr_ctl)
264         iounmap(pdata->vaddr_gpio_mux_spkr_ctl);
265     if (pdata->vaddr_gpio_mux_mic_ctl)
266         iounmap(pdata->vaddr_gpio_mux_mic_ctl);
267     if (pdata->vaddr_gpio_mux_pcm_ctl)
268         iounmap(pdata->vaddr_gpio_mux_pcm_ctl);
269     if(gpio_is_valid(pdata->spk_ext_pa_gpio))
270         gpio_free(pdata->spk_ext_pa_gpio);
271     devm_kfree(&pdev->dev, pdata);
272 err1:
273     return ret;
274 }

复制代码

 

DTS:

复制代码

 1 sound {
 2         compatible = "qcom,msm8x16-audio-codec";
 3         qcom,model = "msm8x16-skui-snd-card";
 4         qcom,msm-snd-card-id = <0>;
 5         qcom,msm-ext-pa = "secondary";//"primary";
 6         qcom,msm-codec-type = "internal";
 7         qcom,msm-mbhc-hphl-swh = <0>;
 8         qcom,msm-mbhc-gnd-swh = <1>;
 9         qcom,msm-hs-micbias-type = "internal";
10         qcom,audio-routing =
11             "RX_BIAS", "MCLK",
12             "SPK_RX_BIAS", "MCLK",
13             "INT_LDO_H", "MCLK",
14             "MIC BIAS Internal1", "Handset Mic",
15             "MIC BIAS Internal2", "Headset Mic",
16             "MIC BIAS Internal1", "Secondary Mic",
17             "AMIC1", "MIC BIAS Internal1",
18             "AMIC2", "MIC BIAS Internal2",
19             "AMIC3", "MIC BIAS Internal1";
20         pinctrl-names = "cdc_lines_act",
21                 "cdc_lines_sus",
22                 "cdc_lines_sec_ext_act",
23                 "cdc_lines_sec_ext_sus";
24         pinctrl-0 = <&cdc_pdm_lines_act>;
25         pinctrl-1 = <&cdc_pdm_lines_sus>;
26         pinctrl-2 = <&cdc_pdm_lines_act &cdc_ext_pa_act &cdc_ext_pa_ws_act>;
27         pinctrl-3 = <&cdc_pdm_lines_sus &cdc_ext_pa_sus &cdc_ext_pa_ws_sus>;
28     };

复制代码

 

通过与DTS匹配,开始分析:

(1)、获取card的id:

1 ret = of_property_read_u32(pdev->dev.of_node, card_dev_id, &id);

 

(2)、设置card的名字:

1 pdev->id = id;
2 dev_set_name(&pdev->dev, "%s.%d", "msm-snd-card", id);

 

(3)、设置codec的类型为external还是internal的:

1 ret = of_property_read_string(pdev->dev.of_node, codec_type, &ptr);
2     if (ret) {
3         dev_err(&pdev->dev,
4             "%s: missing %s in dt node\n", __func__, codec_type);
5         goto err;
6     }

 

(4)、根据external还是internal的card,进入相应的处理函数中:

假设进入internal card:

1 card = &bear_cards[pdev->id];
2 bear_cards[pdev->id].name = dev_name(&pdev->dev);

 

在这里,bear_cards是一个snd_soc_card的结构体,由设备树又可知,id=0:

复制代码

 1 static struct snd_soc_card bear_cards[MAX_SND_CARDS] = {
 2     /* snd_soc_card_msm8x16 */
 3     {
 4         .name        = "msm8x16-snd-card",
 5         .dai_link    = msm8x16_dai,
 6         .num_links    = ARRAY_SIZE(msm8x16_dai),
 7     },
 8     {
 9         .name        = "msm8x16-tapan-snd-card",
10         .dai_link    = msm8x16_9306_dai_links,
11         .num_links    = ARRAY_SIZE(msm8x16_9306_dai_links),
12     },
13     {
14         .name        = "msm8x16-tapan9302-snd-card",
15         .dai_link    = msm8x16_9302_dai_links,
16         .num_links    = ARRAY_SIZE(msm8x16_9302_dai_links),
17     },
18 };

复制代码

所以用到的只有msm8x16_dai;

 snd_dai_link msm8x16_dai

 

其中,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实现;

msm8x16_sec_mi2s_be_ops的结构体是一个函数指针结构体,里面注册了相应的回调函数:

1 static struct snd_soc_ops msm8x16_mi2s_be_ops = {
2     .startup = msm_mi2s_snd_startup,
3     .hw_params = msm_mi2s_snd_hw_params,
4     .shutdown = msm_mi2s_snd_shutdown,
5 };

 

在高通平台中,这primary_mi2s这一路i2s,都是留给内部codec用的,所以,这路的dai_link上的codec_name和codec_dai_name,就是对应着内部codec的信息:

复制代码

 1 /* Backend I2S DAI Links */
 2 {
 3         .name = LPASS_BE_PRI_MI2S_RX,
 4         .stream_name = "Primary MI2S Playback",
 5         .cpu_dai_name = "msm-dai-q6-mi2s.0",
 6         .platform_name = "msm-pcm-routing",
 7         .codec_name     = MSM8X16_CODEC_NAME,
 8         .codec_dai_name = "msm8x16_wcd_i2s_rx1",
 9         .no_pcm = 1,
10         .be_id = MSM_BACKEND_DAI_PRI_MI2S_RX,
11         .init = &msm_audrx_init,
12         .be_hw_params_fixup = msm_pri_rx_be_hw_params_fixup,
13         .ops = &msm8x16_mi2s_be_ops,
14         .ignore_suspend = 1,
15 },

复制代码

从msm8x16_wcd_i2s_rx1我们便可以找到高通平台默认的msm8x16-wcd.c,在该文件中,注册了snd_soc_codec_driver:

 

(5)、匹配并注册相应的驱动:

如何匹配?

 那这里就要谈论一个问题,在初始化的时候,如何凭借dai_link中的codec信息找到对应的codec,答案是codec_name。但注意,这里并不是通过这个名字直接寻找的,例如8916平台。 

这下就来到重要的函数:

1 ret = snd_soc_register_card(card);
2 if (ret) {
3         dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n",
4             ret);
5         goto err;
6 }

 snd_soc_register_card

 将link指针遍历msm8x16_dai结构体数组的每一个成员:

1 for (i = 0; i < card->num_links; i++) {
2     struct snd_soc_dai_link *link = &card->dai_link[i];

 这里定义了codec_name,名字为msm8x16_wcd_codec,所以不执行if的内容:

1 if (!!link->codec_name == !!link->codec_of_node) {
2             dev_err(card->dev, "ASoC: Neither/both codec"
3                 " name/of_node are set for %s\n", link->name);
4             return -EINVAL;
5 }

  同理,下面的函数也是一样:

 View Code

 继续向下看:

复制代码

 1        //设置声卡设备驱动信息
 2        dev_set_drvdata(card->dev,card);
 3        //初始化声卡设备列表
 4        snd_soc_initialize_card_lists(card);
 5  
 6        soc_init_card_debugfs(card);
 7        //为声卡中的snd_soc_pcm_runtime数据结构分配内存空间
 8        card->rtd= devm_kzalloc(card->dev,
 9                              sizeof(struct snd_soc_pcm_runtime) *
10                              (card->num_links + card->num_aux_devs),
11                              GFP_KERNEL);
12        if(card->rtd == NULL)
13               return-ENOMEM;
14        card->num_rtd= 0;
15        card->rtd_aux= &card->rtd[card->num_links];
16  
17        for(i = 0; i < card->num_links; i++)
18               card->rtd[i].dai_link= &card->dai_link[i];
19  
20        INIT_LIST_HEAD(&card->list);
21        INIT_LIST_HEAD(&card->dapm_dirty);
22        card->instantiated= 0;          //表明声卡还没有被初始化
23        mutex_init(&card->mutex);
24        mutex_init(&card->dapm_mutex);
25        //初始化声卡
26        ret= snd_soc_instantiate_card(card);
27        if(ret != 0)
28               soc_cleanup_card_debugfs(card);

复制代码

 在snd_soc_instantiate_card函数中:

 snd_soc_instantiate_card

复制代码

1 /*bind DAIs */
2  for(i = 0; i < card->num_links; i++) {
3         //逐一绑定声卡的各类DAI链接,下面会详细介绍该函数
4         ret= soc_bind_dai_link(card, i);
5         if(ret != 0)
6              gotobase_error;
7  }

复制代码

复制代码

 1 /* initialize the register cache for each available codec */
 2 //遍历CODEC列表中的所有CODEC
 3 list_for_each_entry(codec, &codec_list, list) {
 4     //CODEC缓存是否已初始化
 5     if (codec->cache_init)
 6         continue;
 7     /* by default we don't override the compress_type */
 8     //设置压缩类型
 9     compress_type = 0;
10     /* check to see if we need to override the compress_type */
11     for (i = 0; i < card->num_configs; ++i) {
12         codec_conf = &card->codec_conf[i];
13         if (!strcmp(codec->name, codec_conf->dev_name)) {
14             compress_type = codec_conf->compress_type;
15             if (compress_type && compress_type
16                 != codec->compress_type)
17                 break;
18         }
19     }
20     /*初始化CODEC缓存,该函数最终调用sound/soc/soc-cache.c文件内的snd_soc_flat_cache_init函数,为缓存(reg_cache)开辟内存空间,并把codec->cache_init置为1*/
21     ret = snd_soc_init_codec_cache(codec, compress_type);
22     if (ret < 0)
23         goto base_error;
24 }

复制代码

1 //创建声卡
2 ret= snd_card_create(SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1,
3              card->owner,0, &card->snd_card);

 

继续看soc_bind_dai_link函数,通过msm8x16-wcd.c中probe的of_device_id就可以知道:

复制代码

554         pm8916_tombak_dig: msm8x16_wcd_codec@f000{
555             compatible = "qcom,wcd-spmi";
556             reg = <0xf000 0x100>;
557             interrupt-parent = <&spmi_bus>;
558             interrupts = <0x1 0xf0 0x0>,
559                      <0x1 0xf0 0x1>,
560                      <0x1 0xf0 0x2>,
561                      <0x1 0xf0 0x3>,
562                      <0x1 0xf0 0x4>,
563                      <0x1 0xf0 0x5>,
564                      <0x1 0xf0 0x6>,
565                      <0x1 0xf0 0x7>;
566             interrupt-names = "spk_cnp_int",
567                       "spk_clip_int",
568                       "spk_ocp_int",
569                       "ins_rem_det1",
570                       "but_rel_det",
571                       "but_press_det",
572                       "ins_rem_det",
573                       "mbhc_int";

复制代码

 

 而这个node节点正式codec驱动的设备树节点。在soc_bind_dai_link()函数中,会做出如下处理:

复制代码

 1 /*注册codec的时候,会将所有注册的codec链接到codec_list中*/
 2     list_for_each_entry(codec, &codec_list, list) {
 3         if (dai_link->codec_of_node) {
 4             /*根据设备数节点句柄进行匹配*/
 5             if (codec->dev->of_node != dai_link->codec_of_node)
 6                 continue;
 7         } else {
 8             /*如果句柄为空,根据,codec_name进行匹配,在这里不会走这里,其实codec_name是 wcd-spmi-core.1*/
 9             if (strcmp(codec->name, dai_link->codec_name))
10                 continue;
11         }
12 
13         rtd->codec = codec;
14 
15         /*找到codec之后,根据codec_dai的名字找到对应的codec_dai*/
16         list_for_each_entry(codec_dai, &dai_list, list) {
17             if (codec->dev == codec_dai->dev &&
18                 !strcmp(codec_dai->name,
19                     dai_link->codec_dai_name)) {
20 
21                 rtd->codec_dai = codec_dai;
22             }
23         }
24     }

复制代码

 所以,我们可以根据dai_link中的codec_dai的名字或者codec名字来找到对应的codec驱动。

 

 

另外,当播放音频的时候需要打开一个外部pa,所以probe函数中有一部分是external pa的初始化:

(6)、获取external pa:(也就是放大器):

复制代码

 1   for (i = 0; i < num_strings; i++) {
 2             ret = of_property_read_string_index(pdev->dev.of_node,
 3                     ext_pa, i, &ext_pa_str);
 4             if (ret) {
 5                 dev_err(&pdev->dev, "%s:of read string %s i %d error %d\n",
 6                         __func__, ext_pa, i, ret);
 7                 goto err;
 8             }
 9             if (!strcmp(ext_pa_str, "primary"))
10                 pdata->ext_pa = (pdata->ext_pa | PRI_MI2S_ID);
11             else if (!strcmp(ext_pa_str, "secondary"))
12                 pdata->ext_pa = (pdata->ext_pa | SEC_MI2S_ID);
13             else if (!strcmp(ext_pa_str, "tertiary"))
14                 pdata->ext_pa = (pdata->ext_pa | TER_MI2S_ID);
15             else if (!strcmp(ext_pa_str, "quaternary"))
16                 pdata->ext_pa = (pdata->ext_pa | QUAT_MI2S_ID);
17         }
18         pr_err("%s: ext_pa = %d\n", __func__, pdata->ext_pa);

复制代码

复制代码

1 pinctrl_info.pinctrl = pinctrl;
2 ret = get_cdc_gpio_lines(pinctrl, pdata->ext_pa);    //获取gpio状态
3 if (ret < 0) {
4         pr_err("%s: failed to ger the codec gpio's %d\n",
5                     __func__, ret);
6         goto err;
7 }

复制代码

 可以根据高通手册来看,所以设备树上的配置为secondary:

1 qcom,msm-ext-pa = "secondary";//"primary";

 

 

回到顶部

2. 相应的资料:

其实以上便是linux3.10以上的audio内核machine架构,网上搜索相应资料便可找到;贴上借鉴的资料:

http://blog.csdn.net/zhaocj/article/details/20533369

网上大牛的架构:

http://www.cnblogs.com/linhaostudy/p/8169383.html

  • 1
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值