ASoC被分為Machine、Platform和Codec三大部分,其中的Machine驅動負責Platform和Codec之間的耦合以及部分和設備或板子特定的代碼,再次引用上一節的內容:Machine驅動負責處理機器特有的一些控制項和音頻事件(例如,當播放音頻時,需要先行打開一個放大器);單獨的Pl ...
ASoC被分為Machine、Platform和Codec三大部分,其中的Machine驅動負責Platform和Codec之間的耦合以及部分和設備或板子特定的代碼,再次引用上一節的內容:Machine驅動負責處理機器特有的一些控制項和音頻事件(例如,當播放音頻時,需要先行打開一個放大器);單獨的Platform和Codec驅動是不能工作的,它必須由Machine驅動把它們結合在一起才能完成整個設備的音頻處理工作。
ASoC的一切都從Machine驅動開始,包括音效卡的註冊,綁定Platform和Codec驅動等等;
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 }probe函數
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 LP