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;

1 /* Digital audio interface glue - connects codec <---> CPU */ 2 static struct snd_soc_dai_link msm8x16_dai[] = { 3 /* FrontEnd DAI Links */ 4 {/* hw:x,0 */ 5 .name = "MSM8X16 Media1", 6 .stream_name = "MultiMedia1", 7 .cpu_dai_name = "MultiMedia1", 8 .platform_name = "msm-pcm-dsp.0", 9 .dynamic = 1, 10 .trigger = {SND_SOC_DPCM_TRIGGER_POST, 11 SND_SOC_DPCM_TRIGGER_POST}, 12 .codec_dai_name = "snd-soc-dummy-dai", 13 .codec_name = "snd-soc-dummy", 14 .ignore_suspend = 1, 15 /* this dainlink has playback support */ 16 .ignore_pmdown_time = 1, 17 .be_id = MSM_FRONTEND_DAI_MULTIMEDIA1 18 }, 19 {/* hw:x,1 */ 20 .name = "MSM8X16 Media2", 21 .stream_name = "MultiMedia2", 22 .cpu_dai_name = "MultiMedia2", 23 .platform_name = "msm-pcm-dsp.0", 24 .dynamic = 1, 25 .codec_dai_name = "snd-soc-dummy-dai", 26 .codec_name = "snd-soc-dummy", 27 .trigger = {SND_SOC_DPCM_TRIGGER_POST, 28 SND_SOC_DPCM_TRIGGER_POST}, 29 .ignore_suspend = 1, 30 /* this dainlink has playback support */ 31 .ignore_pmdown_time = 1, 32 .be_id = MSM_FRONTEND_DAI_MULTIMEDIA2, 33 }, 34 {/* hw:x,2 */ 35 .name = "Circuit-Switch Voice", 36 .stream_name = "CS-Voice", 37 .cpu_dai_name = "CS-VOICE", 38 .platform_name = "msm-pcm-voice", 39 .dynamic = 1, 40 .codec_dai_name = "snd-soc-dummy-dai", 41 .codec_name = "snd-soc-dummy", 42 .trigger = {SND_SOC_DPCM_TRIGGER_POST, 43 SND_SOC_DPCM_TRIGGER_POST}, 44 .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, 45 .ignore_suspend = 1, 46 /* this dainlink has playback support */ 47 .ignore_pmdown_time = 1, 48 .be_id = MSM_FRONTEND_DAI_CS_VOICE, 49 }, 50 {/* hw:x,3 */ 51 .name = "MSM VoIP", 52 .stream_name = "VoIP", 53 .cpu_dai_name = "VoIP", 54 .platform_name = "msm-voip-dsp", 55 .dynamic = 1, 56 .trigger = {SND_SOC_DPCM_TRIGGER_POST, 57 SND_SOC_DPCM_TRIGGER_POST}, 58 .codec_dai_name = "snd-soc-dummy-dai", 59 .codec_name = "snd-soc-dummy", 60 .ignore_suspend = 1, 61 /* this dainlink has playback support */ 62 .ignore_pmdown_time = 1, 63 .be_id = MSM_FRONTEND_DAI_VOIP, 64 }, 65 {/* hw:x,4 */ 66 .name = "MSM8X16 LPA", 67 .stream_name = "LPA", 68 .cpu_dai_name = "MultiMedia3", 69 .platform_name = "msm-pcm-lpa", 70 .dynamic = 1, 71 .trigger = {SND_SOC_DPCM_TRIGGER_POST, 72 SND_SOC_DPCM_TRIGGER_POST}, 73 .codec_dai_name = "snd-soc-dummy-dai", 74 .codec_name = "snd-soc-dummy", 75 .ignore_suspend = 1, 76 /* this dainlink has playback support */ 77 .ignore_pmdown_time = 1, 78 .be_id = MSM_FRONTEND_DAI_MULTIMEDIA3, 79 }, 80 /* Hostless PCM purpose */ 81 {/* hw:x,5 */ 82 .name = "Primary MI2S_RX Hostless", 83 .stream_name = "Primary MI2S_RX Hostless", 84 .cpu_dai_name = "PRI_MI2S_RX_HOSTLESS", 85 .platform_name = "msm-pcm-hostless", 86 .dynamic = 1, 87 .trigger = {SND_SOC_DPCM_TRIGGER_POST, 88 SND_SOC_DPCM_TRIGGER_POST}, 89 .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, 90 .ignore_suspend = 1, 91 .ignore_pmdown_time = 1, 92 /* This dainlink has MI2S support */ 93 .codec_dai_name = "snd-soc-dummy-dai", 94 .codec_name = "snd-soc-dummy", 95 }, 96 {/* hw:x,6 */ 97 .name = "INT_FM Hostless", 98 .stream_name = "INT_FM Hostless", 99 .cpu_dai_name = "INT_FM_HOSTLESS", 100 .platform_name = "msm-pcm-hostless", 101 .dynamic = 1, 102 .trigger = {SND_SOC_DPCM_TRIGGER_POST, 103 SND_SOC_DPCM_TRIGGER_POST}, 104 .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, 105 .ignore_suspend = 1, 106 /* this dainlink has playback support */ 107 .ignore_pmdown_time = 1, 108 .codec_dai_name = "snd-soc-dummy-dai", 109 .codec_name = "snd-soc-dummy", 110 }, 111 {/* hw:x,7 */ 112 .name = "MSM AFE-PCM RX", 113 .stream_name = "AFE-PROXY RX", 114 .cpu_dai_name = "msm-dai-q6-dev.241", 115 .codec_name = "msm-stub-codec.1", 116 .codec_dai_name = "msm-stub-rx", 117 .platform_name = "msm-pcm-afe", 118 .ignore_suspend = 1, 119 /* this dainlink has playback support */ 120 .ignore_pmdown_time = 1, 121 }, 122 {/* hw:x,8 */ 123 .name = "MSM AFE-PCM TX", 124 .stream_name = "AFE-PROXY TX", 125 .cpu_dai_name = "msm-dai-q6-dev.240", 126 .codec_name = "msm-stub-codec.1", 127 .codec_dai_name = "msm-stub-tx", 128 .platform_name = "msm-pcm-afe", 129 .ignore_suspend = 1, 130 }, 131 {/* hw:x,9 */ 132 .name = "MSM8X16 Compr", 133 .stream_name = "COMPR", 134 .cpu_dai_name = "MultiMedia4", 135 .platform_name = "msm-compress-dsp", 136 .dynamic = 1, 137 .trigger = {SND_SOC_DPCM_TRIGGER_POST, 138 SND_SOC_DPCM_TRIGGER_POST}, 139 .codec_dai_name = "snd-soc-dummy-dai", 140 .codec_name = "snd-soc-dummy", 141 .ignore_suspend = 1, 142 .ignore_pmdown_time = 1, 143 /* this dainlink has playback support */ 144 .be_id = MSM_FRONTEND_DAI_MULTIMEDIA4, 145 }, 146 {/* hw:x,10 */ 147 .name = "AUXPCM Hostless", 148 .stream_name = "AUXPCM Hostless", 149 .cpu_dai_name = "AUXPCM_HOSTLESS", 150 .platform_name = "msm-pcm-hostless", 151 .dynamic = 1, 152 .trigger = {SND_SOC_DPCM_TRIGGER_POST, 153 SND_SOC_DPCM_TRIGGER_POST}, 154 .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, 155 .ignore_suspend = 1, 156 /* this dainlink has playback support */ 157 .ignore_pmdown_time = 1, 158 .codec_dai_name = "snd-soc-dummy-dai", 159 .codec_name = "snd-soc-dummy", 160 }, 161 {/* hw:x,11 */ 162 .name = "Tertiary MI2S_TX Hostless", 163 .stream_name = "Tertiary MI2S_TX Hostless", 164 .cpu_dai_name = "TERT_MI2S_TX_HOSTLESS", 165 .platform_name = "msm-pcm-hostless", 166 .dynamic = 1, 167 .trigger = {SND_SOC_DPCM_TRIGGER_POST, 168 SND_SOC_DPCM_TRIGGER_POST}, 169 .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, 170 .ignore_suspend = 1, 171 .ignore_pmdown_time = 1, /* dai link has playback support */ 172 .codec_dai_name = "snd-soc-dummy-dai", 173 .codec_name = "snd-soc-dummy", 174 }, 175 {/* hw:x,12 */ 176 .name = "MSM8x16 LowLatency", 177 .stream_name = "MultiMedia5", 178 .cpu_dai_name = "MultiMedia5", 179 .platform_name = "msm-pcm-dsp.1", 180 .dynamic = 1, 181 .codec_dai_name = "snd-soc-dummy-dai", 182 .codec_name = "snd-soc-dummy", 183 .trigger = {SND_SOC_DPCM_TRIGGER_POST, 184 SND_SOC_DPCM_TRIGGER_POST}, 185 .ignore_suspend = 1, 186 /* this dainlink has playback support */ 187 .ignore_pmdown_time = 1, 188 .be_id = MSM_FRONTEND_DAI_MULTIMEDIA5, 189 }, 190 {/* hw:x,13 */ 191 .name = "Voice2", 192 .stream_name = "Voice2", 193 .cpu_dai_name = "Voice2", 194 .platform_name = "msm-pcm-voice", 195 .dynamic = 1, 196 .trigger = {SND_SOC_DPCM_TRIGGER_POST, 197 SND_SOC_DPCM_TRIGGER_POST}, 198 .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, 199 .ignore_suspend = 1, 200 /* this dainlink has playback support */ 201 .ignore_pmdown_time = 1, 202 .codec_dai_name = "snd-soc-dummy-dai", 203 .codec_name = "snd-soc-dummy", 204 }, 205 {/* hw:x,14 */ 206 .name = "MSM8x16 Media9", 207 .stream_name = "MultiMedia9", 208 .cpu_dai_name = "MultiMedia9", 209 .platform_name = "msm-pcm-dsp.0", 210 .dynamic = 1, 211 .codec_dai_name = "snd-soc-dummy-dai", 212 .codec_name = "snd-soc-dummy", 213 .trigger = {SND_SOC_DPCM_TRIGGER_POST, 214 SND_SOC_DPCM_TRIGGER_POST}, 215 .ignore_suspend = 1, 216 /* This dailink has playback support */ 217 .ignore_pmdown_time = 1, 218 .be_id = MSM_FRONTEND_DAI_MULTIMEDIA9, 219 }, 220 { /* hw:x,15 */ 221 .name = "VoLTE", 222 .stream_name = "VoLTE", 223 .cpu_dai_name = "VoLTE", 224 .platform_name = "msm-pcm-voice", 225 .dynamic = 1, 226 .trigger = {SND_SOC_DPCM_TRIGGER_POST, 227 SND_SOC_DPCM_TRIGGER_POST}, 228 .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, 229 .ignore_suspend = 1, 230 /* this dainlink has playback support */ 231 .ignore_pmdown_time = 1, 232 .codec_dai_name = "snd-soc-dummy-dai", 233 .codec_name = "snd-soc-dummy", 234 .be_id = MSM_FRONTEND_DAI_VOLTE, 235 }, 236 { /* hw:x,16 */ 237 .name = "VoWLAN", 238 .stream_name = "VoWLAN", 239 .cpu_dai_name = "VoWLAN", 240 .platform_name = "msm-pcm-voice", 241 .dynamic = 1, 242 .trigger = {SND_SOC_DPCM_TRIGGER_POST, 243 SND_SOC_DPCM_TRIGGER_POST}, 244 .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, 245 .ignore_suspend = 1, 246 .ignore_pmdown_time = 1, 247 .codec_dai_name = "snd-soc-dummy-dai", 248 .codec_name = "snd-soc-dummy", 249 .be_id = MSM_FRONTEND_DAI_VOWLAN, 250 }, 251 {/* hw:x,17 */ 252 .name = "INT_HFP_BT Hostless", 253 .stream_name = "INT_HFP_BT Hostless", 254 .cpu_dai_name = "INT_HFP_BT_HOSTLESS", 255 .platform_name = "msm-pcm-hostless", 256 .dynamic = 1, 257 .trigger = {SND_SOC_DPCM_TRIGGER_POST, 258 SND_SOC_DPCM_TRIGGER_POST}, 259 .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, 260 .ignore_suspend = 1, 261 /* this dai link has playback support */ 262 .ignore_pmdown_time = 1, 263 .codec_dai_name = "snd-soc-dummy-dai", 264 .codec_name = "snd-soc-dummy", 265 }, 266 {/* hw:x,18 */ 267 .name = "MSM8916 HFP TX", 268 .stream_name = "MultiMedia6", 269 .cpu_dai_name = "MultiMedia6", 270 .platform_name = "msm-pcm-loopback", 271 .dynamic = 1, 272 .codec_dai_name = "snd-soc-dummy-dai", 273 .codec_name = "snd-soc-dummy", 274 .trigger = {SND_SOC_DPCM_TRIGGER_POST, 275 SND_SOC_DPCM_TRIGGER_POST}, 276 .ignore_suspend = 1, 277 .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, 278 /* this dai link has playback support */ 279 .ignore_pmdown_time = 1, 280 .be_id = MSM_FRONTEND_DAI_MULTIMEDIA6, 281 }, 282 /* LSM FE */ 283 {/* hw:x,19 */ 284 .name = "Listen 1 Audio Service", 285 .stream_name = "Listen 1 Audio Service", 286 .cpu_dai_name = "LSM1", 287 .platform_name = "msm-lsm-client", 288 .dynamic = 1, 289 .trigger = {SND_SOC_DPCM_TRIGGER_POST, 290 SND_SOC_DPCM_TRIGGER_POST }, 291 .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, 292 .ignore_suspend = 1, 293 .ignore_pmdown_time = 1, 294 .codec_dai_name = "snd-soc-dummy-dai", 295 .codec_name = "snd-soc-dummy", 296 .be_id = MSM_FRONTEND_DAI_LSM1, 297 }, 298 {/* hw:x,20 */ 299 .name = "Listen 2 Audio Service", 300 .stream_name = "Listen 2 Audio Service", 301 .cpu_dai_name = "LSM2", 302 .platform_name = "msm-lsm-client", 303 .dynamic = 1, 304 .trigger = {SND_SOC_DPCM_TRIGGER_POST, 305 SND_SOC_DPCM_TRIGGER_POST }, 306 .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, 307 .ignore_suspend = 1, 308 .ignore_pmdown_time = 1, 309 .codec_dai_name = "snd-soc-dummy-dai", 310 .codec_name = "snd-soc-dummy", 311 .be_id = MSM_FRONTEND_DAI_LSM2, 312 }, 313 {/* hw:x,21 */ 314 .name = "Listen 3 Audio Service", 315 .stream_name = "Listen 3 Audio Service", 316 .cpu_dai_name = "LSM3", 317 .platform_name = "msm-lsm-client", 318 .dynamic = 1, 319 .trigger = {SND_SOC_DPCM_TRIGGER_POST, 320 SND_SOC_DPCM_TRIGGER_POST }, 321 .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, 322 .ignore_suspend = 1, 323 .ignore_pmdown_time = 1, 324 .codec_dai_name = "snd-soc-dummy-dai", 325 .codec_name = "snd-soc-dummy", 326 .be_id = MSM_FRONTEND_DAI_LSM3, 327 }, 328 {/* hw:x,22 */ 329 .name = "Listen 4 Audio Service", 330 .stream_name = "Listen 4 Audio Service", 331 .cpu_dai_name = "LSM4", 332 .platform_name = "msm-lsm-client", 333 .dynamic = 1, 334 .trigger = {SND_SOC_DPCM_TRIGGER_POST, 335 SND_SOC_DPCM_TRIGGER_POST }, 336 .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, 337 .ignore_suspend = 1, 338 .ignore_pmdown_time = 1, 339 .codec_dai_name = "snd-soc-dummy-dai", 340 .codec_name = "snd-soc-dummy", 341 .be_id = MSM_FRONTEND_DAI_LSM4, 342 }, 343 {/* hw:x,23 */ 344 .name = "Listen 5 Audio Service", 345 .stream_name = "Listen 5 Audio Service", 346 .cpu_dai_name = "LSM5", 347 .platform_name = "msm-lsm-client", 348 .dynamic = 1, 349 .trigger = {SND_SOC_DPCM_TRIGGER_POST, 350 SND_SOC_DPCM_TRIGGER_POST }, 351 .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, 352 .ignore_suspend = 1, 353 .ignore_pmdown_time = 1, 354 .codec_dai_name = "snd-soc-dummy-dai", 355 .codec_name = "snd-soc-dummy", 356 .be_id = MSM_FRONTEND_DAI_LSM5, 357 }, 358 {/* hw:x,24 */ 359 .name = "MSM8916 ULL", 360 .stream_name = "MultiMedia7", 361 .cpu_dai_name = "MultiMedia7", 362 .platform_name = "msm-pcm-dsp.1", 363 .dynamic = 1, 364 .codec_dai_name = "snd-soc-dummy-dai", 365 .codec_name = "snd-soc-dummy", 366 .trigger = {SND_SOC_DPCM_TRIGGER_POST, 367 SND_SOC_DPCM_TRIGGER_POST}, 368 .ignore_suspend = 1, 369 /* this dainlink has playback support */ 370 .ignore_pmdown_time = 1, 371 .be_id = MSM_FRONTEND_DAI_MULTIMEDIA7, 372 }, 373 /* Backend I2S DAI Links */ 374 { 375 .name = LPASS_BE_PRI_MI2S_RX, 376 .stream_name = "Primary MI2S Playback", 377 .cpu_dai_name = "msm-dai-q6-mi2s.0", 378 .platform_name = "msm-pcm-routing", 379 .codec_name = MSM8X16_CODEC_NAME, 380 .codec_dai_name = "msm8x16_wcd_i2s_rx1", 381 .no_pcm = 1, 382 .be_id = MSM_BACKEND_DAI_PRI_MI2S_RX, 383 .init = &msm_audrx_init, 384 .be_hw_params_fixup = msm_pri_rx_be_hw_params_fixup, 385 .ops = &msm8x16_mi2s_be_ops, 386 .ignore_suspend = 1, 387 }, 388 { 389 .name = LPASS_BE_SEC_MI2S_RX, 390 .stream_name = "Secondary MI2S Playback", 391 .cpu_dai_name = "msm-dai-q6-mi2s.1", 392 .platform_name = "msm-pcm-routing", 393 .codec_name = "msm-stub-codec.1", 394 .codec_dai_name = "msm-stub-rx", 395 .no_pcm = 1, 396 .be_id = MSM_BACKEND_DAI_SECONDARY_MI2S_RX, 397 .be_hw_params_fixup = msm_be_hw_params_fixup, 398 .ops = &msm8x16_sec_mi2s_be_ops, 399 .ignore_suspend = 1, 400 }, 401 { 402 .name = LPASS_BE_TERT_MI2S_TX, 403 .stream_name = "Tertiary MI2S Capture", 404 .cpu_dai_name = "msm-dai-q6-mi2s.2", 405 .platform_name = "msm-pcm-routing", 406 .codec_name = MSM8X16_CODEC_NAME, 407 .codec_dai_name = "msm8x16_wcd_i2s_tx1", 408 .no_pcm = 1, 409 .be_id = MSM_BACKEND_DAI_TERTIARY_MI2S_TX, 410 .be_hw_params_fixup = msm_tx_be_hw_params_fixup, 411 .ops = &msm8x16_mi2s_be_ops, 412 .ignore_suspend = 1, 413 }, 414 { 415 .name = LPASS_BE_INT_BT_SCO_RX, 416 .stream_name = "Internal BT-SCO Playback", 417 .cpu_dai_name = "msm-dai-q6-dev.12288", 418 .platform_name = "msm-pcm-routing", 419 .codec_name = "msm-stub-codec.1", 420 .codec_dai_name = "msm-stub-rx", 421 .no_pcm = 1, 422 .be_id = MSM_BACKEND_DAI_INT_BT_SCO_RX, 423 .be_hw_params_fixup = msm_btsco_be_hw_params_fixup, 424 /* this dainlink has playback support */ 425 .ignore_pmdown_time = 1, 426 .ignore_suspend = 1, 427 }, 428 { 429 .name = LPASS_BE_INT_BT_SCO_TX, 430 .stream_name = "Internal BT-SCO Capture", 431 .cpu_dai_name = "msm-dai-q6-dev.12289", 432 .platform_name = "msm-pcm-routing", 433 .codec_name = "msm-stub-codec.1", 434 .codec_dai_name = "msm-stub-tx", 435 .no_pcm = 1, 436 .be_id = MSM_BACKEND_DAI_INT_BT_SCO_TX, 437 .be_hw_params_fixup = msm_btsco_be_hw_params_fixup, 438 .ignore_suspend = 1, 439 }, 440 { 441 .name = LPASS_BE_INT_FM_RX, 442 .stream_name = "Internal FM Playback", 443 .cpu_dai_name = "msm-dai-q6-dev.12292", 444 .platform_name = "msm-pcm-routing", 445 .codec_name = "msm-stub-codec.1", 446 .codec_dai_name = "msm-stub-rx", 447 .no_pcm = 1, 448 .be_id = MSM_BACKEND_DAI_INT_FM_RX, 449 .be_hw_params_fixup = msm_be_hw_params_fixup, 450 /* this dainlink has playback support */ 451 .ignore_pmdown_time = 1, 452 .ignore_suspend = 1, 453 }, 454 { 455 .name = LPASS_BE_INT_FM_TX, 456 .stream_name = "Internal FM Capture", 457 .cpu_dai_name = "msm-dai-q6-dev.12293", 458 .platform_name = "msm-pcm-routing", 459 .codec_name = "msm-stub-codec.1", 460 .codec_dai_name = "msm-stub-tx", 461 .no_pcm = 1, 462 .be_id = MSM_BACKEND_DAI_INT_FM_TX, 463 .be_hw_params_fixup = msm_be_hw_params_fixup, 464 .ignore_suspend = 1, 465 }, 466 { 467 .name = LPASS_BE_AFE_PCM_RX, 468 .stream_name = "AFE Playback", 469 .cpu_dai_name = "msm-dai-q6-dev.224", 470 .platform_name = "msm-pcm-routing", 471 .codec_name = "msm-stub-codec.1", 472 .codec_dai_name = "msm-stub-rx", 473 .no_pcm = 1, 474 .be_id = MSM_BACKEND_DAI_AFE_PCM_RX, 475 .be_hw_params_fixup = msm_proxy_rx_be_hw_params_fixup, 476 /* this dainlink has playback support */ 477 .ignore_pmdown_time = 1, 478 .ignore_suspend = 1, 479 }, 480 { 481 .name = LPASS_BE_AFE_PCM_TX, 482 .stream_name = "AFE Capture", 483 .cpu_dai_name = "msm-dai-q6-dev.225", 484 .platform_name = "msm-pcm-routing", 485 .codec_name = "msm-stub-codec.1", 486 .codec_dai_name = "msm-stub-tx", 487 .no_pcm = 1, 488 .be_id = MSM_BACKEND_DAI_AFE_PCM_TX, 489 .be_hw_params_fixup = msm_proxy_tx_be_hw_params_fixup, 490 .ignore_suspend = 1, 491 }, 492 /* Incall Record Uplink BACK END DAI Link */ 493 { 494 .name = LPASS_BE_INCALL_RECORD_TX, 495 .stream_name = "Voice Uplink Capture", 496 .cpu_dai_name = "msm-dai-q6-dev.32772", 497 .platform_name = "msm-pcm-routing", 498 .codec_name = "msm-stub-codec.1", 499 .codec_dai_name = "msm-stub-tx", 500 .no_pcm = 1, 501 .be_id = MSM_BACKEND_DAI_INCALL_RECORD_TX, 502 .be_hw_params_fixup = msm_be_hw_params_fixup, 503 .ignore_suspend = 1, 504 }, 505 /* Incall Record Downlink BACK END DAI Link */ 506 { 507 .name = LPASS_BE_INCALL_RECORD_RX, 508 .stream_name = "Voice Downlink Capture", 509 .cpu_dai_name = "msm-dai-q6-dev.32771", 510 .platform_name = "msm-pcm-routing", 511 .codec_name = "msm-stub-codec.1", 512 .codec_dai_name = "msm-stub-tx", 513 .no_pcm = 1, 514 .be_id = MSM_BACKEND_DAI_INCALL_RECORD_RX, 515 .be_hw_params_fixup = msm_be_hw_params_fixup, 516 .ignore_suspend = 1, 517 }, 518 /* Incall Music BACK END DAI Link */ 519 { 520 .name = LPASS_BE_VOICE_PLAYBACK_TX, 521 .stream_name = "Voice Farend Playback", 522 .cpu_dai_name = "msm-dai-q6-dev.32773", 523 .platform_name = "msm-pcm-routing", 524 .codec_name = "msm-stub-codec.1", 525 .codec_dai_name = "msm-stub-rx", 526 .no_pcm = 1, 527 .be_id = MSM_BACKEND_DAI_VOICE_PLAYBACK_TX, 528 .be_hw_params_fixup = msm_be_hw_params_fixup, 529 .ignore_suspend = 1, 530 }, 531 /* Incall Music 2 BACK END DAI Link */ 532 { 533 .name = LPASS_BE_VOICE2_PLAYBACK_TX, 534 .stream_name = "Voice2 Farend Playback", 535 .cpu_dai_name = "msm-dai-q6-dev.32770", 536 .platform_name = "msm-pcm-routing", 537 .codec_name = "msm-stub-codec.1", 538 .codec_dai_name = "msm-stub-rx", 539 .no_pcm = 1, 540 .be_id = MSM_BACKEND_DAI_VOICE2_PLAYBACK_TX, 541 .be_hw_params_fixup = msm_be_hw_params_fixup, 542 .ignore_suspend = 1, 543 }, 544 };
其中,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 }

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 11 if (!card->name || !card->dev) 12 return -EINVAL; 13 14 for (i = 0; i < card->num_links; i++) { 15 struct snd_soc_dai_link *link = &card->dai_link[i]; 16 17 /* 18 * Codec must be specified by 1 of name or OF node, 19 * not both or neither. 20 */ 21 if (!!link->codec_name == !!link->codec_of_node) { 22 dev_err(card->dev, "ASoC: Neither/both codec" 23 " name/of_node are set for %s\n", link->name); 24 return -EINVAL; 25 } 26 /* Codec DAI name must be specified */ 27 if (!link->codec_dai_name) { 28 dev_err(card->dev, "ASoC: codec_dai_name not" 29 " set for %s\n", link->name); 30 return -EINVAL; 31 } 32 33 /* 34 * Platform may be specified by either name or OF node, but 35 * can be left unspecified, and a dummy platform will be used. 36 */ 37 if (link->platform_name && link->platform_of_node) { 38 dev_err(card->dev, "ASoC: Both platform name/of_node" 39 " are set for %s\n", link->name); 40 return -EINVAL; 41 } 42 43 /* 44 * CPU device may be specified by either name or OF node, but 45 * can be left unspecified, and will be matched based on DAI 46 * name alone.. 47 */ 48 if (link->cpu_name && link->cpu_of_node) { 49 dev_err(card->dev, "ASoC: Neither/both " 50 "cpu name/of_node are set for %s\n",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, "ASoC: Neither cpu_dai_name nor " 60 "cpu_name/of_node are set for %s\n", link->name); 61 return -EINVAL; 62 } 63 } 64 65 dev_set_drvdata(card->dev, card); 66 67 snd_soc_initialize_card_lists(card); 68 69 soc_init_card_debugfs(card); 70 71 card->rtd = devm_kzalloc(card->dev, 72 sizeof(struct snd_soc_pcm_runtime) * 73 (card->num_links + card->num_aux_devs), 74 GFP_KERNEL); 75 if (card->rtd == NULL) 76 return -ENOMEM; 77 card->num_rtd = 0; 78 card->rtd_aux = &card->rtd[card->num_links]; 79 80 for (i = 0; i < card->num_links; i++) 81 card->rtd[i].dai_link = &card->dai_link[i]; 82 83 INIT_LIST_HEAD(&card->list); 84 INIT_LIST_HEAD(&card->dapm_dirty); 85 card->instantiated = 0; 86 mutex_init(&card->mutex); 87 mutex_init(&card->dapm_mutex); 88 mutex_init(&card->dapm_power_mutex); 89 ret = snd_soc_instantiate_card(card); 90 if (ret != 0) 91 soc_cleanup_card_debugfs(card); 92 93 return ret; 94 }
將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 }
同理,下面的函數也是一樣:

1 /* Codec DAI name must be specified */ 2 if (!link->codec_dai_name) { 3 dev_err(card->dev, "ASoC: codec_dai_name not" 4 " set for %s\n", link->name); 5 return -EINVAL; 6 } 7 8 /* 9 * Platform may be specified by either name or OF node, but 10 * can be left unspecified, and a dummy platform will be used. 11 */ 12 if (link->platform_name && link->platform_of_node) { 13 dev_err(card->dev, "ASoC: Both platform name/of_node" 14 " are set for %s\n", link->name); 15 return -EINVAL; 16 } 17 18 /* 19 * CPU device may be specified by either name or OF node, but 20 * can be left unspecified, and will be matched based on DAI 21 * name alone.. 22 */ 23 if (link->cpu_name && link->cpu_of_node) { 24 dev_err(card->dev, "ASoC: Neither/both " 25 "cpu name/of_node are set for %s\n",link->name); 26 return -EINVAL; 27 } 28 /* 29 * At least one of CPU DAI name or CPU device name/node must be 30 * specified 31 */ 32 if (!link->cpu_dai_name && 33 !(link->cpu_name || link->cpu_of_node)) { 34 dev_err(card->dev, "ASoC: Neither cpu_dai_name nor " 35 "cpu_name/of_node are set for %s\n", link->name); 36 return -EINVAL; 37 }
繼續向下看:
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函數中:

1 static int snd_soc_instantiate_card(struct snd_soc_card *card) 2 { 3 struct snd_soc_codec *codec; 4 struct snd_soc_codec_conf *codec_conf; 5 enum snd_soc_compress_type compress_type; 6 struct snd_soc_dai_link *dai_link; 7 int ret, i, order, dai_fmt; 8 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, i); 14 if (ret != 0) 15 goto base_error; 16 } 17 18 /* check aux_devs too */ 19 for (i = 0; i < card->num_aux_devs; i++) { 20 ret = soc_check_aux_dev(card, i); 21 if (ret != 0) 22 goto base_error; 23 } 24 25 /* initialize the register cache for each available codec */ 26 list_for_each_entry(codec, &codec_list, list) { 27 if (codec->cache_init) 28 continue; 29 /* by default we don't override the compress_type */ 30 compress_type = 0; 31 /* check to see if we need to override the compress_type */ 32 for (i = 0; i < card->num_configs; ++i) { 33 codec_conf = &card->codec_conf[i]; 34 if (!strcmp(codec->name, codec_conf->dev_name)) { 35 compress_type = codec_conf->compress_type; 36 if (compress_type && compress_type 37 != codec->compress_type) 38 break; 39 } 40 } 41 ret = snd_soc_init_codec_cache(codec, compress_type); 42 if (ret < 0) 43 goto base_error; 44 } 45 46 /* card bind complete so register a sound card */ 47 ret = snd_card_create(SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1, 48 card->owner, 0, &card->snd_card); 49 if (ret < 0) { 50 dev_err(card->dev, "ASoC: can't create sound card for" 51 " card %s: %d\n", card->name, ret); 52 goto base_error; 53 } 54 card->snd_card->dev = card->dev; 55 56 card->dapm.bias_level = SND_SOC_BIAS_OFF; 57 card->dapm.dev = card->dev; 58 card->dapm.card = card; 59 list_add(&card->dapm.list, &card->dapm_list); 60 61 #ifdef CONFIG_DEBUG_FS 62 snd_soc_dapm_debugfs_init(&card->dapm, card->debugfs_card_root); 63 #endif 64 65 #ifdef CONFIG_PM_SLEEP 66 /* deferred resume work */ 67 INIT_WORK(&card->deferred_resume_work, soc_resume_deferred); 68 #endif 69 70 if (card->dapm_widgets) 71 snd_soc_dapm_new_controls(&card->dapm, card->dapm_widgets, 72 card->num_dapm_widgets); 73 74 /* initialise the sound card only once */ 75 if (card->probe) { 76 ret = card->probe(card); 77 if (ret < 0) 78 goto card_probe_error; 79 } 80 81 /* probe all components used by DAI links on this card */ 82 for (order = SND_SOC_COMP_ORDER_FIRST; order <= SND_SOC_COMP_ORDER_LAST; 83 order++) { 84 for (i = 0; i < card->num_links; i++) { 85 ret = soc_probe_link_components(card, i, order); 86 if (ret < 0) { 87 dev_err(card->dev, 88 "ASoC: failed to instantiate card %d\n", 89 ret); 90 goto probe_dai_err; 91 } 92 } 93 } 94 95 /* probe all DAI links on this card */ 96 for (order = SND_SOC_COMP_ORDER_FIRST; order <= SND_SOC_COMP_ORDER_LAST; 97 order++) { 98 for (i = 0; i < card->num_links; i++) { 99 ret = soc_probe_link_dais(card, i, order); 100 if (ret < 0) { 101 dev_err(card->dev, 102 "ASoC: failed to instantiate card %d\n", 103 ret); 104 goto probe_dai_err; 105 } 106 } 107 } 108 109 for (i = 0; i < card->num_aux_devs; i++) { 110 ret = soc_probe_aux_dev(card, i); 111 if (ret < 0) { 112 dev_err(card->dev, 113 "ASoC: failed to add auxiliary devices %d\n", 114 ret); 115 goto probe_aux_dev_err; 116 } 117 } 118 119 snd_soc_dapm_link_dai_widgets(card); 120 121 if (card->controls) 122 snd_soc_add_card_controls(card, card->controls, card->num_controls); 123 124 if (card->dapm_routes) 125 snd_soc_dapm_add_routes(&card->dapm, card->dapm_routes, 126 card->num_dapm_routes); 127 128 snd_soc_dapm_new_widgets(&card->dapm); 129 130 for (i = 0; i < card->num_links; i++) { 131 dai_link = &card->dai_link[i]; 132 dai_fmt = dai_link->dai_fmt; 133 134 if (dai_fmt) { 135 ret = snd_soc_dai_set_fmt(card->rtd[i].codec_dai, 136 dai_fmt); 137 if (ret != 0 && ret != -ENOTSUPP) 138 dev_warn(card->rtd[i].codec_dai->dev, 139 "ASoC: Failed to set DAI format: %d\n", 140 ret); 141 } 142 143 /* If this is a regular CPU link there will be a platform */ 144 if (dai_fmt && 145 (dai_link->platform_name || dai_link->platform_of_node)) { 146 ret = snd_soc_dai_set_fmt(card->rtd[i].cpu_dai, 147 dai_fmt); 148 if (ret != 0 && ret != -ENOTSUPP) 149 dev_warn(card->rtd[i].cpu_dai->dev, 150 "ASoC: Failed to set DAI format: %d\n", 151 ret); 152 } else if (dai_fmt) { 153 /* Flip the polarity for the "CPU" end */ 154 dai_fmt &= ~SND_SOC_DAIFMT_MASTER_MASK; 155 switch (dai_link->dai_fmt & 156 SND_SOC_DAIFMT_MASTER_MASK) { 157 case SND_SOC_DAIFMT_CBM_CFM: 158 dai_fmt |= SND_SOC_DAIFMT_CBS_CFS; 159 break; 160 case SND_SOC_DAIFMT_CBM_CFS: 161 dai_fmt |= SND_SOC_DAIFMT_CBS_CFM; 162 break; 163 case SND_SOC_DAIFMT_CBS_CFM: 164 dai_fmt |= SND_SOC_DAIFMT_CBM_CFS; 165 break; 166 case SND_SOC_DAIFMT_CBS_CFS: 167 dai_fmt |= SND_SOC_DAIFMT_CBM_CFM; 168 break; 169 } 170 171 ret = snd_soc_dai_set_fmt(card->rtd[i].cpu_dai, 172 dai_fmt); 173 if (ret != 0 && ret != -ENOTSUPP) 174 dev_warn(card->rtd[i].cpu_dai->dev, 175 "ASoC: Failed to set DAI format: %d\n", 176 ret); 177 } 178 } 179 180 snprintf(card->snd_card->shortname, sizeof(card->snd_card->shortname), 181 "%s", card->name); 182 snprintf(card->snd_card->longname, sizeof(card->snd_card->longname), 183 "%s", card->long_name ? card->long_name : card->name); 184 snprintf(card->snd_card->driver, sizeof(card->snd_card->driver), 185 "%s", card->driver_name ? card->driver_name : card->name); 186 for (i = 0; i < ARRAY_SIZE(card->snd_card->driver); i++) { 187 switch (card->snd_card->driver[i]) { 188 case '_': 189 case '-': 190 case '\0': 191 break; 192 default: 193 if (!isalnum(card->snd_card->driver[i])) 194 card->snd_card->driver[i] = '_'; 195 break; 196 } 197 } 198 199 if (card->late_probe) { 200 ret = card->late_probe(card); 201 if (ret < 0) { 202 dev_err(card->dev, "ASoC: %s late_probe() failed: %d\n", 203 card->name, ret); 204 goto probe_aux_dev_err; 205 } 206 } 207 208 snd_soc_dapm_new_widgets(&card->dapm); 209 210 if (card->fully_routed) 211 list_for_each_entry(codec, &card->codec_dev_list, card_list) 212 snd_soc_dapm_auto_nc_codec_pins(codec); 213 214 ret = snd_card_register(card->snd_card); 215 if (ret < 0) { 216 dev_err(card->dev, "ASoC: failed to register soundcard %d\n", 217 ret); 218 goto probe_aux_dev_err; 219 } 220 221 #ifdef CONFIG_SND_SOC_AC97_BUS 222 /* register any AC97 codecs */ 223 for (i = 0; i < card->num_rtd; i++) { 224 ret = soc_register_ac97_dai_link(&card->rtd[i]); 225 if (ret < 0) { 226 dev_err(card->dev, "ASoC: failed to register AC97:" 227 " %d\n", ret); 228 while (--i >= 0) 229 soc_unregister_ac97_dai_link(card->rtd[i].codec); 230 goto probe_aux_dev_err; 231 } 232 } 233 #endif 234 235 card->instantiated = 1; 236 snd_soc_dapm_sync(&card->dapm); 237 mutex_unlock(&card->mutex); 238 239 return 0; 240 241 probe_aux_dev_err: 242 for (i = 0; i < card->num_aux_devs; i++) 243 soc_remove_aux_dev(card, i); 244 245 probe_dai_err: 246 soc_remove_dai_links(card); 247 248 card_probe_error: 249 if (card->remove) 250 card->remove(card); 251 252 snd_card_free(card->snd_card); 253 254 base_error: 255 mutex_unlock(&card->mutex); 256 257 return ret; 258 }
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
網上大牛的架構: