sound/soc/msm/msm8952.c// 注册平台设备static int __init msm8952_machine_init(void){ return platform_driver_register(&msm8952_asoc_machine_driver);}late_initcall(msm8952_machine_init);// 驱动和设备的匹配表static const struct of_device_id msm8952_asoc_machine_of_match[] = { { .compatible = "qcom,msm8952-audio-codec", }, {},};static struct platform_driver msm8952_asoc_machine_driver = { .driver = { .name = DRV_NAME, .owner = THIS_MODULE, .pm = &snd_soc_pm_ops, .of_match_table = msm8952_asoc_machine_of_match, }, .probe = msm8952_asoc_machine_probe, .remove = msm8952_asoc_machine_remove,};static int msm8952_asoc_machine_probe(struct platform_device *pdev){ struct snd_soc_card *card; struct msm8916_asoc_mach_data *pdata = NULL; const char *hs_micbias_type = "qcom,msm-hs-micbias-type"; const char *ext_pa = "qcom,msm-ext-pa"; const char *mclk = "qcom,msm-mclk-freq"; const char *wsa = "asoc-wsa-codec-names"; const char *wsa_prefix = "asoc-wsa-codec-prefixes"; const char *type = NULL; const char *ext_pa_str = NULL; const char *wsa_str = NULL; const char *wsa_prefix_str = NULL; int num_strings; int ret, id, i, val; struct resource *muxsel; char *temp_str = NULL; pdata = devm_kzalloc(&pdev->dev, sizeof(struct msm8916_asoc_mach_data), GFP_KERNEL); if (!pdata) return -ENOMEM; muxsel = platform_get_resource_byname(pdev, IORESOURCE_MEM, "csr_gp_io_mux_mic_ctl"); if (!muxsel) { dev_err(&pdev->dev, "MUX addr invalid for MI2S\n"); ret = -ENODEV; goto err1; } pdata->vaddr_gpio_mux_mic_ctl = ioremap(muxsel->start, resource_size(muxsel)); if (pdata->vaddr_gpio_mux_mic_ctl == NULL) { pr_err("%s ioremap failure for muxsel virt addr\n", __func__); ret = -ENOMEM; goto err1; } muxsel = platform_get_resource_byname(pdev, IORESOURCE_MEM, "csr_gp_io_mux_spkr_ctl"); if (!muxsel) { dev_err(&pdev->dev, "MUX addr invalid for MI2S\n"); ret = -ENODEV; goto err; } pdata->vaddr_gpio_mux_spkr_ctl = ioremap(muxsel->start, resource_size(muxsel)); if (pdata->vaddr_gpio_mux_spkr_ctl == NULL) { pr_err("%s ioremap failure for muxsel virt addr\n", __func__); ret = -ENOMEM; goto err; } muxsel = platform_get_resource_byname(pdev, IORESOURCE_MEM, "csr_gp_io_lpaif_pri_pcm_pri_mode_muxsel"); if (!muxsel) { dev_err(&pdev->dev, "MUX addr invalid for MI2S\n"); ret = -ENODEV; goto err; } pdata->vaddr_gpio_mux_pcm_ctl = ioremap(muxsel->start, resource_size(muxsel)); if (pdata->vaddr_gpio_mux_pcm_ctl == NULL) { pr_err("%s ioremap failure for muxsel virt addr\n", __func__); ret = -ENOMEM; goto err; } muxsel = platform_get_resource_byname(pdev, IORESOURCE_MEM, "csr_gp_io_mux_quin_ctl"); if (!muxsel) { dev_dbg(&pdev->dev, "MUX addr invalid for MI2S\n"); goto parse_mclk_freq; } pdata->vaddr_gpio_mux_quin_ctl = ioremap(muxsel->start, resource_size(muxsel)); if (pdata->vaddr_gpio_mux_quin_ctl == NULL) { pr_err("%s ioremap failure for muxsel virt addr\n", __func__); ret = -ENOMEM; goto err; }parse_mclk_freq: // 获取时钟 ret = of_property_read_u32(pdev->dev.of_node, mclk, &id); if (ret) { dev_err(&pdev->dev, "%s: missing %s in dt node\n", __func__, mclk); id = DEFAULT_MCLK_RATE; } pdata->mclk_freq = id; /*reading the gpio configurations from dtsi file*/ ret = msm_gpioset_initialize(CLIENT_WCD_INT, &pdev->dev); if (ret < 0) { dev_err(&pdev->dev, "%s: error reading dtsi files%d\n", __func__, ret); goto err; } num_strings = of_property_count_strings(pdev->dev.of_node, wsa); if (num_strings > 0) { if (wsa881x_get_probing_count() < 2) { ret = -EPROBE_DEFER; goto err; } else if (wsa881x_get_presence_count() == num_strings) { bear_card.aux_dev = msm8952_aux_dev; bear_card.num_aux_devs = num_strings; bear_card.codec_conf = msm8952_codec_conf; bear_card.num_configs = num_strings; for (i = 0; i < num_strings; i++) { ret = of_property_read_string_index( pdev->dev.of_node, wsa, i, &wsa_str); if (ret) { dev_err(&pdev->dev, "%s:of read string %s i %d error %d\n", __func__, wsa, i, ret); goto err; } temp_str = kstrdup(wsa_str, GFP_KERNEL); if (!temp_str) { ret = -ENOMEM; goto err; } msm8952_aux_dev[i].codec_name = temp_str; temp_str = NULL; temp_str = kstrdup(wsa_str, GFP_KERNEL); if (!temp_str) { ret = -ENOMEM; goto err; } msm8952_codec_conf[i].dev_name = temp_str; temp_str = NULL; ret = of_property_read_string_index( pdev->dev.of_node, wsa_prefix, i, &wsa_prefix_str); if (ret) { dev_err(&pdev->dev, "%s:of read string %s i %d error %d\n", __func__, wsa_prefix, i, ret); goto err; } temp_str = kstrdup(wsa_prefix_str, GFP_KERNEL); if (!temp_str) { ret = -ENOMEM; goto err; } msm8952_codec_conf[i].name_prefix = temp_str; temp_str = NULL; } ret = msm8952_init_wsa_switch_supply(pdev, pdata); if (ret < 0) { pr_err("%s: failed to init wsa_switch vdd supply %d\n", __func__, ret); goto err; } wsa881x_set_mclk_callback(msm8952_enable_wsa_mclk); /* update the internal speaker boost usage */ msm8x16_update_int_spk_boost(false); } } // 获取dai_links card = msm8952_populate_sndcard_dailinks(&pdev->dev); dev_info(&pdev->dev, "default codec configured\n"); num_strings = of_property_count_strings(pdev->dev.of_node, ext_pa); if (num_strings < 0) { dev_err(&pdev->dev, "%s: missing %s in dt node or length is incorrect\n", __func__, ext_pa); goto err; } for (i = 0; i < num_strings; i++) { ret = of_property_read_string_index(pdev->dev.of_node, ext_pa, i, &ext_pa_str); if (ret) { dev_err(&pdev->dev, "%s:of read string %s i %d error %d\n", __func__, ext_pa, i, ret); goto err; } if (!strcmp(ext_pa_str, "primary")) pdata->ext_pa = (pdata->ext_pa | PRI_MI2S_ID); else if (!strcmp(ext_pa_str, "secondary")) pdata->ext_pa = (pdata->ext_pa | SEC_MI2S_ID); else if (!strcmp(ext_pa_str, "tertiary")) pdata->ext_pa = (pdata->ext_pa | TER_MI2S_ID); else if (!strcmp(ext_pa_str, "quaternary")) pdata->ext_pa = (pdata->ext_pa | QUAT_MI2S_ID); else if (!strcmp(ext_pa_str, "quinary")) pdata->ext_pa = (pdata->ext_pa | QUIN_MI2S_ID); } pr_debug("%s: ext_pa = %d\n", __func__, pdata->ext_pa); ret = is_us_eu_switch_gpio_support(pdev, pdata); if (ret < 0) { pr_err("%s: failed to is_us_eu_switch_gpio_support %d\n", __func__, ret); goto err; } ret = is_ext_spk_gpio_support(pdev, pdata); if (ret < 0) pr_err("%s: doesn't support external speaker pa\n", __func__); get_dev_by_boardid(model_name); if(0 == strcmp(model_name,"eda71")){ enable_aw8738 = of_property_read_bool(pdev->dev.of_node , "action,enable-aw8738"); if(enable_aw8738) { gpio_request(pdata->spk_ext_pa_gpio, "AW8738_EN"); gpio_direction_output(pdata->spk_ext_pa_gpio,0); } } //printk("enable_aw8738:%d model_name:%s.\n",enable_aw8738,model_name); // 查看micbias类型 ret = of_property_read_string(pdev->dev.of_node, hs_micbias_type, &type); if (ret) { dev_err(&pdev->dev, "%s: missing %s in dt node\n", __func__, hs_micbias_type); goto err; } // 使用内部还是外部的micbias if (!strcmp(type, "external")) { dev_dbg(&pdev->dev, "Headset is using external micbias\n"); mbhc_cfg.hs_ext_micbias = true; } else { dev_dbg(&pdev->dev, "Headset is using internal micbias\n"); mbhc_cfg.hs_ext_micbias = false; } ret = of_property_read_u32(pdev->dev.of_node, "qcom,msm-afe-clk-ver", &val); if (ret) pdata->afe_clk_ver = AFE_CLK_VERSION_V2; else pdata->afe_clk_ver = val; /* initialize the mclk */ pdata->digital_cdc_clk.i2s_cfg_minor_version = AFE_API_VERSION_I2S_CONFIG; pdata->digital_cdc_clk.clk_val = pdata->mclk_freq; pdata->digital_cdc_clk.clk_root = 5; pdata->digital_cdc_clk.reserved = 0; /* initialize the digital codec core clk */ pdata->digital_cdc_core_clk.clk_set_minor_version = AFE_API_VERSION_I2S_CONFIG; pdata->digital_cdc_core_clk.clk_id = Q6AFE_LPASS_CLK_ID_INTERNAL_DIGITAL_CODEC_CORE; pdata->digital_cdc_core_clk.clk_freq_in_hz = pdata->mclk_freq; pdata->digital_cdc_core_clk.clk_attri = Q6AFE_LPASS_CLK_ATTRIBUTE_COUPLE_NO; pdata->digital_cdc_core_clk.clk_root = Q6AFE_LPASS_CLK_ROOT_DEFAULT; pdata->digital_cdc_core_clk.enable = 1; /* Initialize loopback mode to false */ pdata->lb_mode = false; msm8952_dt_parse_cap_info(pdev, pdata); card->dev = &pdev->dev; platform_set_drvdata(pdev, card); snd_soc_card_set_drvdata(card, pdata); // 解析设备数中声卡的名称, qcom,model = "msm8953-snd-card-mtp"; ret = snd_soc_of_parse_card_name(card, "qcom,model"); if (ret) goto err; /* initialize timer */ INIT_DELAYED_WORK(&pdata->disable_mclk_work, msm8952_disable_mclk); mutex_init(&pdata->cdc_mclk_mutex); atomic_set(&pdata->mclk_rsc_ref, 0); if (card->aux_dev) { mutex_init(&pdata->wsa_mclk_mutex); atomic_set(&pdata->wsa_mclk_rsc_ref, 0); } atomic_set(&pdata->mclk_enabled, false); atomic_set(&quat_mi2s_clk_ref, 0); atomic_set(&quin_mi2s_clk_ref, 0); atomic_set(&auxpcm_mi2s_clk_ref, 0); // 声卡的routing ret = snd_soc_of_parse_audio_routing(card, "qcom,audio-routing"); if (ret) goto err; ret = msm8952_populate_dai_link_component_of_node(card); if (ret) { ret = -EPROBE_DEFER; goto err; } // 注册声卡 ret = snd_soc_register_card(card); if (ret) { dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n", ret); goto err; } return 0;err: if (pdata->vaddr_gpio_mux_spkr_ctl) iounmap(pdata->vaddr_gpio_mux_spkr_ctl); if (pdata->vaddr_gpio_mux_mic_ctl) iounmap(pdata->vaddr_gpio_mux_mic_ctl); if (pdata->vaddr_gpio_mux_pcm_ctl) iounmap(pdata->vaddr_gpio_mux_pcm_ctl); if (pdata->vaddr_gpio_mux_quin_ctl) iounmap(pdata->vaddr_gpio_mux_quin_ctl); if (bear_card.num_aux_devs > 0) { for (i = 0; i < bear_card.num_aux_devs; i++) { kfree(msm8952_aux_dev[i].codec_name); kfree(msm8952_codec_conf[i].dev_name); kfree(msm8952_codec_conf[i].name_prefix); } }err1: devm_kfree(&pdev->dev, pdata); return ret;}static struct snd_soc_card *msm8952_populate_sndcard_dailinks( struct device *dev){ struct snd_soc_card *card = &bear_card; struct snd_soc_dai_link *dailink; int len1; card->name = dev_name(dev); len1 = ARRAY_SIZE(msm8952_dai); // dai_links复制给snd_soc_card, 注册的时候会用到 memcpy(msm8952_dai_links, msm8952_dai, sizeof(msm8952_dai)); dailink = msm8952_dai_links; if (of_property_read_bool(dev->of_node, "qcom,hdmi-dba-codec-rx")) { dev_dbg(dev, "%s(): hdmi audio support present\n", __func__); memcpy(dailink + len1, msm8952_hdmi_dba_dai_link, sizeof(msm8952_hdmi_dba_dai_link)); len1 += ARRAY_SIZE(msm8952_hdmi_dba_dai_link); } else { dev_dbg(dev, "%s(): No hdmi dba present, add quin dai\n", __func__); memcpy(dailink + len1, msm8952_quin_dai_link, sizeof(msm8952_quin_dai_link)); len1 += ARRAY_SIZE(msm8952_quin_dai_link); } if (of_property_read_bool(dev->of_node, "qcom,split-a2dp")) { dev_dbg(dev, "%s(): split a2dp support present\n", __func__); memcpy(dailink + len1, msm8952_split_a2dp_dai_link, sizeof(msm8952_split_a2dp_dai_link)); len1 += ARRAY_SIZE(msm8952_split_a2dp_dai_link); } card->dai_link = dailink; card->num_links = len1; return card;}// machine驱动会用这些参数去匹配platform,codec,adi.// 都是在platform,codec中定义/* Digital audio interface glue - connects codec <---> CPU */static struct snd_soc_dai_link msm8952_dai[] = { /* FrontEnd DAI Links */ {/* hw:x,0 */ .name = "MSM8952 Media1", // Media1 播放链路 .stream_name = "MultiMedia1", // 匹配pcm id .cpu_dai_name = "MultiMedia1", // 匹配cpu dai driver .platform_name = "msm-pcm-dsp.0", // 匹配platform driver .dynamic = 1, .async_ops = ASYNC_DPCM_SND_SOC_PREPARE, .dpcm_playback = 1, .dpcm_capture = 1, .trigger = {SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST}, .codec_dai_name = "snd-soc-dummy-dai", // 匹配codec dai driver .codec_name = "snd-soc-dummy", // 匹配codec driver .ignore_suspend = 1, /* this dainlink has playback support */ .ignore_pmdown_time = 1, .be_id = MSM_FRONTEND_DAI_MULTIMEDIA1 }, {/* hw:x,1 */ .name = "MSM8952 Media2", // Media2播放链路 .stream_name = "MultiMedia2", .cpu_dai_name = "MultiMedia2", .platform_name = "msm-pcm-dsp.0", .dynamic = 1, .dpcm_playback = 1, .dpcm_capture = 1, .codec_dai_name = "snd-soc-dummy-dai", .codec_name = "snd-soc-dummy", .trigger = {SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST}, .ignore_suspend = 1, /* this dainlink has playback support */ .ignore_pmdown_time = 1, .be_id = MSM_FRONTEND_DAI_MULTIMEDIA2, }, {/* hw:x,2 */ .name = "Circuit-Switch Voice", .stream_name = "CS-Voice", .cpu_dai_name = "CS-VOICE", .platform_name = "msm-pcm-voice", .dynamic = 1, .dpcm_playback = 1, .dpcm_capture = 1, .trigger = {SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST}, .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, .ignore_suspend = 1, /* this dainlink has playback support */ .ignore_pmdown_time = 1, .be_id = MSM_FRONTEND_DAI_CS_VOICE, .codec_dai_name = "snd-soc-dummy-dai", .codec_name = "snd-soc-dummy", }, ...}