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:

 static int msm8x16_asoc_machine_probe(struct platform_device *pdev)
{
struct snd_soc_card *card;
struct msm8916_asoc_mach_data *pdata = NULL;
struct pinctrl *pinctrl;
const char *card_dev_id = "qcom,msm-snd-card-id";
const char *codec_type = "qcom,msm-codec-type";
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 *spk_ext_pa = "qcom,msm-spk-ext-pa";
const char *ptr = NULL;
const char *type = NULL;
const char *ext_pa_str = NULL;
int num_strings;
int ret, id, i; pr_err("'msm8x16_asoc_machine_probe ......");
pdata = devm_kzalloc(&pdev->dev,
sizeof(struct msm8916_asoc_mach_data), GFP_KERNEL);
if (!pdata) {
dev_err(&pdev->dev, "Can't allocate msm8x16_asoc_mach_data\n");
ret = -ENOMEM;
goto err1;
} pdata->vaddr_gpio_mux_spkr_ctl =
ioremap(LPASS_CSR_GP_IO_MUX_SPKR_CTL , );
if (!pdata->vaddr_gpio_mux_spkr_ctl) {
pr_err("%s ioremap failure for addr %x",
__func__, LPASS_CSR_GP_IO_MUX_SPKR_CTL);
ret = -ENOMEM;
goto err;
}
pdata->vaddr_gpio_mux_mic_ctl =
ioremap(LPASS_CSR_GP_IO_MUX_MIC_CTL , );
if (!pdata->vaddr_gpio_mux_mic_ctl) {
pr_err("%s ioremap failure for addr %x",
__func__, LPASS_CSR_GP_IO_MUX_MIC_CTL);
ret = -ENOMEM;
goto err;
} pdata->vaddr_gpio_mux_pcm_ctl =
ioremap(LPASS_CSR_GP_LPAIF_PRI_PCM_PRI_MODE_MUXSEL, );
if (!pdata->vaddr_gpio_mux_pcm_ctl) {
pr_err("%s ioremap failure for addr %x",
__func__,
LPASS_CSR_GP_LPAIF_PRI_PCM_PRI_MODE_MUXSEL);
ret = -ENOMEM;
goto err;
}
ret = of_property_read_u32(pdev->dev.of_node, card_dev_id, &id);
if (ret) {
dev_err(&pdev->dev,
"%s: missing %s in dt node\n", __func__, card_dev_id);
goto err;
} pdev->id = id;
if (!pdev->dev.of_node) {
dev_err(&pdev->dev, "No platform supplied from device tree\n");
ret = -EINVAL;
goto err;
} 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; pdata->spk_ext_pa_gpio = of_get_named_gpio(pdev->dev.of_node,
spk_ext_pa, );
if (pdata->spk_ext_pa_gpio < ) {
dev_dbg(&pdev->dev,
"%s: missing %s in dt node\n", __func__, spk_ext_pa);
} else {
if (gpio_is_valid(pdata->spk_ext_pa_gpio)) {
ret = gpio_request(pdata->spk_ext_pa_gpio, "spk_ext_pa_gpio");
if(ret) {
pr_err("spk ext pa gpio request failed");
goto err;
} ret = gpio_direction_output(pdata->spk_ext_pa_gpio, );
if(ret) {
pr_err("set_direction for spk ext pa gpio failed\n");
goto err;
}
} else {
pr_err("%s: Invaild external_speaker gpio: %d", __func__, pdata->spk_ext_pa_gpio);
ret = -EINVAL;
goto err;
} } ret = of_property_read_string(pdev->dev.of_node, codec_type, &ptr);
if (ret) {
dev_err(&pdev->dev,
"%s: missing %s in dt node\n", __func__, codec_type);
goto err;
}
if (pdev->id >= MAX_SND_CARDS) {
dev_err(&pdev->dev, "Sound Card parsed is wrong, id=%d\n",
pdev->id);
ret = -EINVAL;
goto err;
}
if (!strcmp(ptr, "external")) {
dev_info(&pdev->dev, "external codec is configured\n");
pdata->codec_type = ;
/*Populate external codec TLMM configs*/
ret = populate_ext_snd_card_dt_data(pdev);
if (ret < ) {
dev_err(&pdev->dev, "error finding the DT\n"
"params ret=%d\n", ret);
goto err;
}
card = populate_ext_snd_card_dailinks(pdev);
if (!card) {
dev_err(&pdev->dev, "%s: Card uninitialized\n",
__func__);
ret = -EPROBE_DEFER;
goto err;
}
} else {
card = populate_ext_snd_card_dailinks(pdev);
if (!card) {
dev_err(&pdev->dev, "%s: Card uninitialized\n",
__func__);
ret = -EPROBE_DEFER;
goto err;
}
dev_info(&pdev->dev, "default codec configured\n");
pdata->codec_type = ;
num_strings = of_property_count_strings(pdev->dev.of_node,
ext_pa);
if (num_strings < ) {
dev_err(&pdev->dev,
"%s: missing %s in dt node or length is incorrect\n",
__func__, ext_pa);
goto err;
}
for (i = ; 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);
}
pr_debug("%s: ext_pa = %d\n", __func__, pdata->ext_pa);
pinctrl = devm_pinctrl_get(&pdev->dev);
if (IS_ERR(pinctrl)) {
pr_err("%s: Unable to get pinctrl handle\n",
__func__);
return -EINVAL;
}
pinctrl_info.pinctrl = pinctrl;
ret = get_cdc_gpio_lines(pinctrl, pdata->ext_pa);
if (ret < ) {
pr_err("%s: failed to ger the codec gpio's %d\n",
__func__, ret);
goto err;
}
} 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;
}
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;
} /* 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 = ;
pdata->digital_cdc_clk.reserved = ;
/* 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 = ;
/* Initialize loopback mode to false */
pdata->lb_mode = false; msm8x16_setup_hs_jack(pdev, pdata);
msm8x16_dt_parse_cap_info(pdev, pdata); card->dev = &pdev->dev;
platform_set_drvdata(pdev, card);
snd_soc_card_set_drvdata(card, pdata);
ret = snd_soc_of_parse_card_name(card, "qcom,model");
if (ret)
goto err;
/* initialize timer */
INIT_DELAYED_WORK(&pdata->disable_mclk_work, disable_mclk);
mutex_init(&pdata->cdc_mclk_mutex);
atomic_set(&pdata->mclk_rsc_ref, );
atomic_set(&pdata->mclk_enabled, false);
atomic_set(&quat_mi2s_clk_ref, );
atomic_set(&auxpcm_mi2s_clk_ref, ); ret = snd_soc_of_parse_audio_routing(card,
"qcom,audio-routing");
if (ret)
goto err; ret = msm8x16_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;
} ret = core_get_adsp_ver();
if (ret < ) {
ret = -EPROBE_DEFER;
dev_info(&pdev->dev, "%s: Get adsp version failed (%d)\n",
__func__, ret);
goto err;
} return ;
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(gpio_is_valid(pdata->spk_ext_pa_gpio))
gpio_free(pdata->spk_ext_pa_gpio);
devm_kfree(&pdev->dev, pdata);
err1:
return ret;
}

probe函数

DTS:

 sound {
compatible = "qcom,msm8x16-audio-codec";
qcom,model = "msm8x16-skui-snd-card";
qcom,msm-snd-card-id = <>;
qcom,msm-ext-pa = "secondary";//"primary";
qcom,msm-codec-type = "internal";
qcom,msm-mbhc-hphl-swh = <>;
qcom,msm-mbhc-gnd-swh = <>;
qcom,msm-hs-micbias-type = "internal";
qcom,audio-routing =
"RX_BIAS", "MCLK",
"SPK_RX_BIAS", "MCLK",
"INT_LDO_H", "MCLK",
"MIC BIAS Internal1", "Handset Mic",
"MIC BIAS Internal2", "Headset Mic",
"MIC BIAS Internal1", "Secondary Mic",
"AMIC1", "MIC BIAS Internal1",
"AMIC2", "MIC BIAS Internal2",
"AMIC3", "MIC BIAS Internal1";
pinctrl-names = "cdc_lines_act",
"cdc_lines_sus",
"cdc_lines_sec_ext_act",
"cdc_lines_sec_ext_sus";
pinctrl- = <&cdc_pdm_lines_act>;
pinctrl- = <&cdc_pdm_lines_sus>;
pinctrl- = <&cdc_pdm_lines_act &cdc_ext_pa_act &cdc_ext_pa_ws_act>;
pinctrl- = <&cdc_pdm_lines_sus &cdc_ext_pa_sus &cdc_ext_pa_ws_sus>;
};

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

(1)、获取card的id:

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

(2)、设置card的名字:

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

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

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

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

假设进入internal card:

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

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

 static struct snd_soc_card bear_cards[MAX_SND_CARDS] = {
/* snd_soc_card_msm8x16 */
{
.name = "msm8x16-snd-card",
.dai_link = msm8x16_dai,
.num_links = ARRAY_SIZE(msm8x16_dai),
},
{
.name = "msm8x16-tapan-snd-card",
.dai_link = msm8x16_9306_dai_links,
.num_links = ARRAY_SIZE(msm8x16_9306_dai_links),
},
{
.name = "msm8x16-tapan9302-snd-card",
.dai_link = msm8x16_9302_dai_links,
.num_links = ARRAY_SIZE(msm8x16_9302_dai_links),
},
};

所以用到的只有msm8x16_dai;

 /* Digital audio interface glue - connects codec <---> CPU */
static struct snd_soc_dai_link msm8x16_dai[] = {
/* FrontEnd DAI Links */
{/* hw:x,0 */
.name = "MSM8X16 Media1",
.stream_name = "MultiMedia1",
.cpu_dai_name = "MultiMedia1",
.platform_name = "msm-pcm-dsp.0",
.dynamic = ,
.trigger = {SND_SOC_DPCM_TRIGGER_POST,
SND_SOC_DPCM_TRIGGER_POST},
.codec_dai_name = "snd-soc-dummy-dai",
.codec_name = "snd-soc-dummy",
.ignore_suspend = ,
/* this dainlink has playback support */
.ignore_pmdown_time = ,
.be_id = MSM_FRONTEND_DAI_MULTIMEDIA1
},
{/* hw:x,1 */
.name = "MSM8X16 Media2",
.stream_name = "MultiMedia2",
.cpu_dai_name = "MultiMedia2",
.platform_name = "msm-pcm-dsp.0",
.dynamic = ,
.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 = ,
/* this dainlink has playback support */
.ignore_pmdown_time = ,
.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 = ,
.codec_dai_name = "snd-soc-dummy-dai",
.codec_name = "snd-soc-dummy",
.trigger = {SND_SOC_DPCM_TRIGGER_POST,
SND_SOC_DPCM_TRIGGER_POST},
.no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
.ignore_suspend = ,
/* this dainlink has playback support */
.ignore_pmdown_time = ,
.be_id = MSM_FRONTEND_DAI_CS_VOICE,
},
{/* hw:x,3 */
.name = "MSM VoIP",
.stream_name = "VoIP",
.cpu_dai_name = "VoIP",
.platform_name = "msm-voip-dsp",
.dynamic = ,
.trigger = {SND_SOC_DPCM_TRIGGER_POST,
SND_SOC_DPCM_TRIGGER_POST},
.codec_dai_name = "snd-soc-dummy-dai",
.codec_name = "snd-soc-dummy",
.ignore_suspend = ,
/* this dainlink has playback support */
.ignore_pmdown_time = ,
.be_id = MSM_FRONTEND_DAI_VOIP,
},
{/* hw:x,4 */
.name = "MSM8X16 LPA",
.stream_name = "LPA",
.cpu_dai_name = "MultiMedia3",
.platform_name = "msm-pcm-lpa",
.dynamic = ,
.trigger = {SND_SOC_DPCM_TRIGGER_POST,
SND_SOC_DPCM_TRIGGER_POST},
.codec_dai_name = "snd-soc-dummy-dai",
.codec_name = "snd-soc-dummy",
.ignore_suspend = ,
/* this dainlink has playback support */
.ignore_pmdown_time = ,
.be_id = MSM_FRONTEND_DAI_MULTIMEDIA3,
},
/* Hostless PCM purpose */
{/* hw:x,5 */
.name = "Primary MI2S_RX Hostless",
.stream_name = "Primary MI2S_RX Hostless",
.cpu_dai_name = "PRI_MI2S_RX_HOSTLESS",
.platform_name = "msm-pcm-hostless",
.dynamic = ,
.trigger = {SND_SOC_DPCM_TRIGGER_POST,
SND_SOC_DPCM_TRIGGER_POST},
.no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
.ignore_suspend = ,
.ignore_pmdown_time = ,
/* This dainlink has MI2S support */
.codec_dai_name = "snd-soc-dummy-dai",
.codec_name = "snd-soc-dummy",
},
{/* hw:x,6 */
.name = "INT_FM Hostless",
.stream_name = "INT_FM Hostless",
.cpu_dai_name = "INT_FM_HOSTLESS",
.platform_name = "msm-pcm-hostless",
.dynamic = ,
.trigger = {SND_SOC_DPCM_TRIGGER_POST,
SND_SOC_DPCM_TRIGGER_POST},
.no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
.ignore_suspend = ,
/* this dainlink has playback support */
.ignore_pmdown_time = ,
.codec_dai_name = "snd-soc-dummy-dai",
.codec_name = "snd-soc-dummy",
},
{/* hw:x,7 */
.name = "MSM AFE-PCM RX",
.stream_name = "AFE-PROXY RX",
.cpu_dai_name = "msm-dai-q6-dev.241",
.codec_name = "msm-stub-codec.1",
.codec_dai_name = "msm-stub-rx",
.platform_name = "msm-pcm-afe",
.ignore_suspend = ,
/* this dainlink has playback support */
.ignore_pmdown_time = ,
},
{/* hw:x,8 */
.name = "MSM AFE-PCM TX",
.stream_name = "AFE-PROXY TX",
.cpu_dai_name = "msm-dai-q6-dev.240",
.codec_name = "msm-stub-codec.1",
.codec_dai_name = "msm-stub-tx",
.platform_name = "msm-pcm-afe",
.ignore_suspend = ,
},
{/* hw:x,9 */
.name = "MSM8X16 Compr",
.stream_name = "COMPR",
.cpu_dai_name = "MultiMedia4",
.platform_name = "msm-compress-dsp",
.dynamic = ,
.trigger = {SND_SOC_DPCM_TRIGGER_POST,
SND_SOC_DPCM_TRIGGER_POST},
.codec_dai_name = "snd-soc-dummy-dai",
.codec_name = "snd-soc-dummy",
.ignore_suspend = ,
.ignore_pmdown_time = ,
/* this dainlink has playback support */
.be_id = MSM_FRONTEND_DAI_MULTIMEDIA4,
},
{/* hw:x,10 */
.name = "AUXPCM Hostless",
.stream_name = "AUXPCM Hostless",
.cpu_dai_name = "AUXPCM_HOSTLESS",
.platform_name = "msm-pcm-hostless",
.dynamic = ,
.trigger = {SND_SOC_DPCM_TRIGGER_POST,
SND_SOC_DPCM_TRIGGER_POST},
.no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
.ignore_suspend = ,
/* this dainlink has playback support */
.ignore_pmdown_time = ,
.codec_dai_name = "snd-soc-dummy-dai",
.codec_name = "snd-soc-dummy",
},
{/* hw:x,11 */
.name = "Tertiary MI2S_TX Hostless",
.stream_name = "Tertiary MI2S_TX Hostless",
.cpu_dai_name = "TERT_MI2S_TX_HOSTLESS",
.platform_name = "msm-pcm-hostless",
.dynamic = ,
.trigger = {SND_SOC_DPCM_TRIGGER_POST,
SND_SOC_DPCM_TRIGGER_POST},
.no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
.ignore_suspend = ,
.ignore_pmdown_time = , /* dai link has playback support */
.codec_dai_name = "snd-soc-dummy-dai",
.codec_name = "snd-soc-dummy",
},
{/* hw:x,12 */
.name = "MSM8x16 LowLatency",
.stream_name = "MultiMedia5",
.cpu_dai_name = "MultiMedia5",
.platform_name = "msm-pcm-dsp.1",
.dynamic = ,
.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 = ,
/* this dainlink has playback support */
.ignore_pmdown_time = ,
.be_id = MSM_FRONTEND_DAI_MULTIMEDIA5,
},
{/* hw:x,13 */
.name = "Voice2",
.stream_name = "Voice2",
.cpu_dai_name = "Voice2",
.platform_name = "msm-pcm-voice",
.dynamic = ,
.trigger = {SND_SOC_DPCM_TRIGGER_POST,
SND_SOC_DPCM_TRIGGER_POST},
.no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
.ignore_suspend = ,
/* this dainlink has playback support */
.ignore_pmdown_time = ,
.codec_dai_name = "snd-soc-dummy-dai",
.codec_name = "snd-soc-dummy",
},
{/* hw:x,14 */
.name = "MSM8x16 Media9",
.stream_name = "MultiMedia9",
.cpu_dai_name = "MultiMedia9",
.platform_name = "msm-pcm-dsp.0",
.dynamic = ,
.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 = ,
/* This dailink has playback support */
.ignore_pmdown_time = ,
.be_id = MSM_FRONTEND_DAI_MULTIMEDIA9,
},
{ /* hw:x,15 */
.name = "VoLTE",
.stream_name = "VoLTE",
.cpu_dai_name = "VoLTE",
.platform_name = "msm-pcm-voice",
.dynamic = ,
.trigger = {SND_SOC_DPCM_TRIGGER_POST,
SND_SOC_DPCM_TRIGGER_POST},
.no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
.ignore_suspend = ,
/* this dainlink has playback support */
.ignore_pmdown_time = ,
.codec_dai_name = "snd-soc-dummy-dai",
.codec_name = "snd-soc-dummy",
.be_id = MSM_FRONTEND_DAI_VOLTE,
},
{ /* hw:x,16 */
.name = "VoWLAN",
.stream_name = "VoWLAN",
.cpu_dai_name = "VoWLAN",
.platform_name = "msm-pcm-voice",
.dynamic = ,
.trigger = {SND_SOC_DPCM_TRIGGER_POST,
SND_SOC_DPCM_TRIGGER_POST},
.no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
.ignore_suspend = ,
.ignore_pmdown_time = ,
.codec_dai_name = "snd-soc-dummy-dai",
.codec_name = "snd-soc-dummy",
.be_id = MSM_FRONTEND_DAI_VOWLAN,
},
{/* hw:x,17 */
.name = "INT_HFP_BT Hostless",
.stream_name = "INT_HFP_BT Hostless",
.cpu_dai_name = "INT_HFP_BT_HOSTLESS",
.platform_name = "msm-pcm-hostless",
.dynamic = ,
.trigger = {SND_SOC_DPCM_TRIGGER_POST,
SND_SOC_DPCM_TRIGGER_POST},
.no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
.ignore_suspend = ,
/* this dai link has playback support */
.ignore_pmdown_time = ,
.codec_dai_name = "snd-soc-dummy-dai",
.codec_name = "snd-soc-dummy",
},
{/* hw:x,18 */
.name = "MSM8916 HFP TX",
.stream_name = "MultiMedia6",
.cpu_dai_name = "MultiMedia6",
.platform_name = "msm-pcm-loopback",
.dynamic = ,
.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 = ,
.no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
/* this dai link has playback support */
.ignore_pmdown_time = ,
.be_id = MSM_FRONTEND_DAI_MULTIMEDIA6,
},
/* LSM FE */
{/* hw:x,19 */
.name = "Listen 1 Audio Service",
.stream_name = "Listen 1 Audio Service",
.cpu_dai_name = "LSM1",
.platform_name = "msm-lsm-client",
.dynamic = ,
.trigger = {SND_SOC_DPCM_TRIGGER_POST,
SND_SOC_DPCM_TRIGGER_POST },
.no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
.ignore_suspend = ,
.ignore_pmdown_time = ,
.codec_dai_name = "snd-soc-dummy-dai",
.codec_name = "snd-soc-dummy",
.be_id = MSM_FRONTEND_DAI_LSM1,
},
{/* hw:x,20 */
.name = "Listen 2 Audio Service",
.stream_name = "Listen 2 Audio Service",
.cpu_dai_name = "LSM2",
.platform_name = "msm-lsm-client",
.dynamic = ,
.trigger = {SND_SOC_DPCM_TRIGGER_POST,
SND_SOC_DPCM_TRIGGER_POST },
.no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
.ignore_suspend = ,
.ignore_pmdown_time = ,
.codec_dai_name = "snd-soc-dummy-dai",
.codec_name = "snd-soc-dummy",
.be_id = MSM_FRONTEND_DAI_LSM2,
},
{/* hw:x,21 */
.name = "Listen 3 Audio Service",
.stream_name = "Listen 3 Audio Service",
.cpu_dai_name = "LSM3",
.platform_name = "msm-lsm-client",
.dynamic = ,
.trigger = {SND_SOC_DPCM_TRIGGER_POST,
SND_SOC_DPCM_TRIGGER_POST },
.no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
.ignore_suspend = ,
.ignore_pmdown_time = ,
.codec_dai_name = "snd-soc-dummy-dai",
.codec_name = "snd-soc-dummy",
.be_id = MSM_FRONTEND_DAI_LSM3,
},
{/* hw:x,22 */
.name = "Listen 4 Audio Service",
.stream_name = "Listen 4 Audio Service",
.cpu_dai_name = "LSM4",
.platform_name = "msm-lsm-client",
.dynamic = ,
.trigger = {SND_SOC_DPCM_TRIGGER_POST,
SND_SOC_DPCM_TRIGGER_POST },
.no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
.ignore_suspend = ,
.ignore_pmdown_time = ,
.codec_dai_name = "snd-soc-dummy-dai",
.codec_name = "snd-soc-dummy",
.be_id = MSM_FRONTEND_DAI_LSM4,
},
{/* hw:x,23 */
.name = "Listen 5 Audio Service",
.stream_name = "Listen 5 Audio Service",
.cpu_dai_name = "LSM5",
.platform_name = "msm-lsm-client",
.dynamic = ,
.trigger = {SND_SOC_DPCM_TRIGGER_POST,
SND_SOC_DPCM_TRIGGER_POST },
.no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
.ignore_suspend = ,
.ignore_pmdown_time = ,
.codec_dai_name = "snd-soc-dummy-dai",
.codec_name = "snd-soc-dummy",
.be_id = MSM_FRONTEND_DAI_LSM5,
},
{/* hw:x,24 */
.name = "MSM8916 ULL",
.stream_name = "MultiMedia7",
.cpu_dai_name = "MultiMedia7",
.platform_name = "msm-pcm-dsp.1",
.dynamic = ,
.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 = ,
/* this dainlink has playback support */
.ignore_pmdown_time = ,
.be_id = MSM_FRONTEND_DAI_MULTIMEDIA7,
},
/* Backend I2S DAI Links */
{
.name = LPASS_BE_PRI_MI2S_RX,
.stream_name = "Primary MI2S Playback",
.cpu_dai_name = "msm-dai-q6-mi2s.0",
.platform_name = "msm-pcm-routing",
.codec_name = MSM8X16_CODEC_NAME,
.codec_dai_name = "msm8x16_wcd_i2s_rx1",
.no_pcm = ,
.be_id = MSM_BACKEND_DAI_PRI_MI2S_RX,
.init = &msm_audrx_init,
.be_hw_params_fixup = msm_pri_rx_be_hw_params_fixup,
.ops = &msm8x16_mi2s_be_ops,
.ignore_suspend = ,
},
{
.name = LPASS_BE_SEC_MI2S_RX,
.stream_name = "Secondary MI2S Playback",
.cpu_dai_name = "msm-dai-q6-mi2s.1",
.platform_name = "msm-pcm-routing",
.codec_name = "msm-stub-codec.1",
.codec_dai_name = "msm-stub-rx",
.no_pcm = ,
.be_id = MSM_BACKEND_DAI_SECONDARY_MI2S_RX,
.be_hw_params_fixup = msm_be_hw_params_fixup,
.ops = &msm8x16_sec_mi2s_be_ops,
.ignore_suspend = ,
},
{
.name = LPASS_BE_TERT_MI2S_TX,
.stream_name = "Tertiary MI2S Capture",
.cpu_dai_name = "msm-dai-q6-mi2s.2",
.platform_name = "msm-pcm-routing",
.codec_name = MSM8X16_CODEC_NAME,
.codec_dai_name = "msm8x16_wcd_i2s_tx1",
.no_pcm = ,
.be_id = MSM_BACKEND_DAI_TERTIARY_MI2S_TX,
.be_hw_params_fixup = msm_tx_be_hw_params_fixup,
.ops = &msm8x16_mi2s_be_ops,
.ignore_suspend = ,
},
{
.name = LPASS_BE_INT_BT_SCO_RX,
.stream_name = "Internal BT-SCO Playback",
.cpu_dai_name = "msm-dai-q6-dev.12288",
.platform_name = "msm-pcm-routing",
.codec_name = "msm-stub-codec.1",
.codec_dai_name = "msm-stub-rx",
.no_pcm = ,
.be_id = MSM_BACKEND_DAI_INT_BT_SCO_RX,
.be_hw_params_fixup = msm_btsco_be_hw_params_fixup,
/* this dainlink has playback support */
.ignore_pmdown_time = ,
.ignore_suspend = ,
},
{
.name = LPASS_BE_INT_BT_SCO_TX,
.stream_name = "Internal BT-SCO Capture",
.cpu_dai_name = "msm-dai-q6-dev.12289",
.platform_name = "msm-pcm-routing",
.codec_name = "msm-stub-codec.1",
.codec_dai_name = "msm-stub-tx",
.no_pcm = ,
.be_id = MSM_BACKEND_DAI_INT_BT_SCO_TX,
.be_hw_params_fixup = msm_btsco_be_hw_params_fixup,
.ignore_suspend = ,
},
{
.name = LPASS_BE_INT_FM_RX,
.stream_name = "Internal FM Playback",
.cpu_dai_name = "msm-dai-q6-dev.12292",
.platform_name = "msm-pcm-routing",
.codec_name = "msm-stub-codec.1",
.codec_dai_name = "msm-stub-rx",
.no_pcm = ,
.be_id = MSM_BACKEND_DAI_INT_FM_RX,
.be_hw_params_fixup = msm_be_hw_params_fixup,
/* this dainlink has playback support */
.ignore_pmdown_time = ,
.ignore_suspend = ,
},
{
.name = LPASS_BE_INT_FM_TX,
.stream_name = "Internal FM Capture",
.cpu_dai_name = "msm-dai-q6-dev.12293",
.platform_name = "msm-pcm-routing",
.codec_name = "msm-stub-codec.1",
.codec_dai_name = "msm-stub-tx",
.no_pcm = ,
.be_id = MSM_BACKEND_DAI_INT_FM_TX,
.be_hw_params_fixup = msm_be_hw_params_fixup,
.ignore_suspend = ,
},
{
.name = LPASS_BE_AFE_PCM_RX,
.stream_name = "AFE Playback",
.cpu_dai_name = "msm-dai-q6-dev.224",
.platform_name = "msm-pcm-routing",
.codec_name = "msm-stub-codec.1",
.codec_dai_name = "msm-stub-rx",
.no_pcm = ,
.be_id = MSM_BACKEND_DAI_AFE_PCM_RX,
.be_hw_params_fixup = msm_proxy_rx_be_hw_params_fixup,
/* this dainlink has playback support */
.ignore_pmdown_time = ,
.ignore_suspend = ,
},
{
.name = LPASS_BE_AFE_PCM_TX,
.stream_name = "AFE Capture",
.cpu_dai_name = "msm-dai-q6-dev.225",
.platform_name = "msm-pcm-routing",
.codec_name = "msm-stub-codec.1",
.codec_dai_name = "msm-stub-tx",
.no_pcm = ,
.be_id = MSM_BACKEND_DAI_AFE_PCM_TX,
.be_hw_params_fixup = msm_proxy_tx_be_hw_params_fixup,
.ignore_suspend = ,
},
/* Incall Record Uplink BACK END DAI Link */
{
.name = LPASS_BE_INCALL_RECORD_TX,
.stream_name = "Voice Uplink Capture",
.cpu_dai_name = "msm-dai-q6-dev.32772",
.platform_name = "msm-pcm-routing",
.codec_name = "msm-stub-codec.1",
.codec_dai_name = "msm-stub-tx",
.no_pcm = ,
.be_id = MSM_BACKEND_DAI_INCALL_RECORD_TX,
.be_hw_params_fixup = msm_be_hw_params_fixup,
.ignore_suspend = ,
},
/* Incall Record Downlink BACK END DAI Link */
{
.name = LPASS_BE_INCALL_RECORD_RX,
.stream_name = "Voice Downlink Capture",
.cpu_dai_name = "msm-dai-q6-dev.32771",
.platform_name = "msm-pcm-routing",
.codec_name = "msm-stub-codec.1",
.codec_dai_name = "msm-stub-tx",
.no_pcm = ,
.be_id = MSM_BACKEND_DAI_INCALL_RECORD_RX,
.be_hw_params_fixup = msm_be_hw_params_fixup,
.ignore_suspend = ,
},
/* Incall Music BACK END DAI Link */
{
.name = LPASS_BE_VOICE_PLAYBACK_TX,
.stream_name = "Voice Farend Playback",
.cpu_dai_name = "msm-dai-q6-dev.32773",
.platform_name = "msm-pcm-routing",
.codec_name = "msm-stub-codec.1",
.codec_dai_name = "msm-stub-rx",
.no_pcm = ,
.be_id = MSM_BACKEND_DAI_VOICE_PLAYBACK_TX,
.be_hw_params_fixup = msm_be_hw_params_fixup,
.ignore_suspend = ,
},
/* Incall Music 2 BACK END DAI Link */
{
.name = LPASS_BE_VOICE2_PLAYBACK_TX,
.stream_name = "Voice2 Farend Playback",
.cpu_dai_name = "msm-dai-q6-dev.32770",
.platform_name = "msm-pcm-routing",
.codec_name = "msm-stub-codec.1",
.codec_dai_name = "msm-stub-rx",
.no_pcm = ,
.be_id = MSM_BACKEND_DAI_VOICE2_PLAYBACK_TX,
.be_hw_params_fixup = msm_be_hw_params_fixup,
.ignore_suspend = ,
},
};

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的信息:

 /* Backend I2S DAI Links */
{
.name = LPASS_BE_PRI_MI2S_RX,
.stream_name = "Primary MI2S Playback",
.cpu_dai_name = "msm-dai-q6-mi2s.0",
.platform_name = "msm-pcm-routing",
.codec_name = MSM8X16_CODEC_NAME,
.codec_dai_name = "msm8x16_wcd_i2s_rx1",
.no_pcm = ,
.be_id = MSM_BACKEND_DAI_PRI_MI2S_RX,
.init = &msm_audrx_init,
.be_hw_params_fixup = msm_pri_rx_be_hw_params_fixup,
.ops = &msm8x16_mi2s_be_ops,
.ignore_suspend = ,
},

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

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

如何匹配?

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

这下就来到重要的函数:

 ret = snd_soc_register_card(card);
if (ret) {
dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n",
ret);
goto err;
}
 /**
* snd_soc_register_card - Register a card with the ASoC core
*
* @card: Card to register
*
*/
int snd_soc_register_card(struct snd_soc_card *card)
{
int i, ret; if (!card->name || !card->dev)
return -EINVAL; for (i = ; i < card->num_links; i++) {
struct snd_soc_dai_link *link = &card->dai_link[i]; /*
* Codec must be specified by 1 of name or OF node,
* not both or neither.
*/
if (!!link->codec_name == !!link->codec_of_node) {
dev_err(card->dev, "ASoC: Neither/both codec"
" name/of_node are set for %s\n", link->name);
return -EINVAL;
}
/* Codec DAI name must be specified */
if (!link->codec_dai_name) {
dev_err(card->dev, "ASoC: codec_dai_name not"
" set for %s\n", link->name);
return -EINVAL;
} /*
* Platform may be specified by either name or OF node, but
* can be left unspecified, and a dummy platform will be used.
*/
if (link->platform_name && link->platform_of_node) {
dev_err(card->dev, "ASoC: Both platform name/of_node"
" are set for %s\n", link->name);
return -EINVAL;
} /*
* CPU device may be specified by either name or OF node, but
* can be left unspecified, and will be matched based on DAI
* name alone..
*/
if (link->cpu_name && link->cpu_of_node) {
dev_err(card->dev, "ASoC: Neither/both "
"cpu name/of_node are set for %s\n",link->name);
return -EINVAL;
}
/*
* At least one of CPU DAI name or CPU device name/node must be
* specified
*/
if (!link->cpu_dai_name &&
!(link->cpu_name || link->cpu_of_node)) {
dev_err(card->dev, "ASoC: Neither cpu_dai_name nor "
"cpu_name/of_node are set for %s\n", link->name);
return -EINVAL;
}
} dev_set_drvdata(card->dev, card); snd_soc_initialize_card_lists(card); soc_init_card_debugfs(card); card->rtd = devm_kzalloc(card->dev,
sizeof(struct snd_soc_pcm_runtime) *
(card->num_links + card->num_aux_devs),
GFP_KERNEL);
if (card->rtd == NULL)
return -ENOMEM;
card->num_rtd = ;
card->rtd_aux = &card->rtd[card->num_links]; for (i = ; i < card->num_links; i++)
card->rtd[i].dai_link = &card->dai_link[i]; INIT_LIST_HEAD(&card->list);
INIT_LIST_HEAD(&card->dapm_dirty);
card->instantiated = ;
mutex_init(&card->mutex);
mutex_init(&card->dapm_mutex);
mutex_init(&card->dapm_power_mutex);
ret = snd_soc_instantiate_card(card);
if (ret != )
soc_cleanup_card_debugfs(card); return ret;
}

snd_soc_register_card

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

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

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

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

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

 /* Codec DAI name must be specified */
if (!link->codec_dai_name) {
dev_err(card->dev, "ASoC: codec_dai_name not"
" set for %s\n", link->name);
return -EINVAL;
} /*
* Platform may be specified by either name or OF node, but
* can be left unspecified, and a dummy platform will be used.
*/
if (link->platform_name && link->platform_of_node) {
dev_err(card->dev, "ASoC: Both platform name/of_node"
" are set for %s\n", link->name);
return -EINVAL;
} /*
* CPU device may be specified by either name or OF node, but
* can be left unspecified, and will be matched based on DAI
* name alone..
*/
if (link->cpu_name && link->cpu_of_node) {
dev_err(card->dev, "ASoC: Neither/both "
"cpu name/of_node are set for %s\n",link->name);
return -EINVAL;
}
/*
* At least one of CPU DAI name or CPU device name/node must be
* specified
*/
if (!link->cpu_dai_name &&
!(link->cpu_name || link->cpu_of_node)) {
dev_err(card->dev, "ASoC: Neither cpu_dai_name nor "
"cpu_name/of_node are set for %s\n", link->name);
return -EINVAL;
}

继续向下看:

        //设置声卡设备驱动信息
dev_set_drvdata(card->dev,card);
//初始化声卡设备列表
snd_soc_initialize_card_lists(card); soc_init_card_debugfs(card);
//为声卡中的snd_soc_pcm_runtime数据结构分配内存空间
card->rtd= devm_kzalloc(card->dev,
sizeof(struct snd_soc_pcm_runtime) *
(card->num_links + card->num_aux_devs),
GFP_KERNEL);
if(card->rtd == NULL)
return-ENOMEM;
card->num_rtd= ;
card->rtd_aux= &card->rtd[card->num_links]; for(i = ; i < card->num_links; i++)
card->rtd[i].dai_link= &card->dai_link[i]; INIT_LIST_HEAD(&card->list);
INIT_LIST_HEAD(&card->dapm_dirty);
card->instantiated= ; //表明声卡还没有被初始化
mutex_init(&card->mutex);
mutex_init(&card->dapm_mutex);
//初始化声卡
ret= snd_soc_instantiate_card(card);
if(ret != )
soc_cleanup_card_debugfs(card);

在snd_soc_instantiate_card函数中:

 static int snd_soc_instantiate_card(struct snd_soc_card *card)
{
struct snd_soc_codec *codec;
struct snd_soc_codec_conf *codec_conf;
enum snd_soc_compress_type compress_type;
struct snd_soc_dai_link *dai_link;
int ret, i, order, dai_fmt; mutex_lock_nested(&card->mutex, SND_SOC_CARD_CLASS_INIT); /* bind DAIs */
for (i = ; i < card->num_links; i++) {
ret = soc_bind_dai_link(card, i);
if (ret != )
goto base_error;
} /* check aux_devs too */
for (i = ; i < card->num_aux_devs; i++) {
ret = soc_check_aux_dev(card, i);
if (ret != )
goto base_error;
} /* initialize the register cache for each available codec */
list_for_each_entry(codec, &codec_list, list) {
if (codec->cache_init)
continue;
/* by default we don't override the compress_type */
compress_type = ;
/* check to see if we need to override the compress_type */
for (i = ; i < card->num_configs; ++i) {
codec_conf = &card->codec_conf[i];
if (!strcmp(codec->name, codec_conf->dev_name)) {
compress_type = codec_conf->compress_type;
if (compress_type && compress_type
!= codec->compress_type)
break;
}
}
ret = snd_soc_init_codec_cache(codec, compress_type);
if (ret < )
goto base_error;
} /* card bind complete so register a sound card */
ret = snd_card_create(SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1,
card->owner, , &card->snd_card);
if (ret < ) {
dev_err(card->dev, "ASoC: can't create sound card for"
" card %s: %d\n", card->name, ret);
goto base_error;
}
card->snd_card->dev = card->dev; card->dapm.bias_level = SND_SOC_BIAS_OFF;
card->dapm.dev = card->dev;
card->dapm.card = card;
list_add(&card->dapm.list, &card->dapm_list); #ifdef CONFIG_DEBUG_FS
snd_soc_dapm_debugfs_init(&card->dapm, card->debugfs_card_root);
#endif #ifdef CONFIG_PM_SLEEP
/* deferred resume work */
INIT_WORK(&card->deferred_resume_work, soc_resume_deferred);
#endif if (card->dapm_widgets)
snd_soc_dapm_new_controls(&card->dapm, card->dapm_widgets,
card->num_dapm_widgets); /* initialise the sound card only once */
if (card->probe) {
ret = card->probe(card);
if (ret < )
goto card_probe_error;
} /* probe all components used by DAI links on this card */
for (order = SND_SOC_COMP_ORDER_FIRST; order <= SND_SOC_COMP_ORDER_LAST;
order++) {
for (i = ; i < card->num_links; i++) {
ret = soc_probe_link_components(card, i, order);
if (ret < ) {
dev_err(card->dev,
"ASoC: failed to instantiate card %d\n",
ret);
goto probe_dai_err;
}
}
} /* probe all DAI links on this card */
for (order = SND_SOC_COMP_ORDER_FIRST; order <= SND_SOC_COMP_ORDER_LAST;
order++) {
for (i = ; i < card->num_links; i++) {
ret = soc_probe_link_dais(card, i, order);
if (ret < ) {
dev_err(card->dev,
"ASoC: failed to instantiate card %d\n",
ret);
goto probe_dai_err;
}
}
} for (i = ; i < card->num_aux_devs; i++) {
ret = soc_probe_aux_dev(card, i);
if (ret < ) {
dev_err(card->dev,
"ASoC: failed to add auxiliary devices %d\n",
ret);
goto probe_aux_dev_err;
}
} snd_soc_dapm_link_dai_widgets(card); if (card->controls)
snd_soc_add_card_controls(card, card->controls, card->num_controls); if (card->dapm_routes)
snd_soc_dapm_add_routes(&card->dapm, card->dapm_routes,
card->num_dapm_routes); snd_soc_dapm_new_widgets(&card->dapm); for (i = ; i < card->num_links; i++) {
dai_link = &card->dai_link[i];
dai_fmt = dai_link->dai_fmt; if (dai_fmt) {
ret = snd_soc_dai_set_fmt(card->rtd[i].codec_dai,
dai_fmt);
if (ret != && ret != -ENOTSUPP)
dev_warn(card->rtd[i].codec_dai->dev,
"ASoC: Failed to set DAI format: %d\n",
ret);
} /* If this is a regular CPU link there will be a platform */
if (dai_fmt &&
(dai_link->platform_name || dai_link->platform_of_node)) {
ret = snd_soc_dai_set_fmt(card->rtd[i].cpu_dai,
dai_fmt);
if (ret != && ret != -ENOTSUPP)
dev_warn(card->rtd[i].cpu_dai->dev,
"ASoC: Failed to set DAI format: %d\n",
ret);
} else if (dai_fmt) {
/* Flip the polarity for the "CPU" end */
dai_fmt &= ~SND_SOC_DAIFMT_MASTER_MASK;
switch (dai_link->dai_fmt &
SND_SOC_DAIFMT_MASTER_MASK) {
case SND_SOC_DAIFMT_CBM_CFM:
dai_fmt |= SND_SOC_DAIFMT_CBS_CFS;
break;
case SND_SOC_DAIFMT_CBM_CFS:
dai_fmt |= SND_SOC_DAIFMT_CBS_CFM;
break;
case SND_SOC_DAIFMT_CBS_CFM:
dai_fmt |= SND_SOC_DAIFMT_CBM_CFS;
break;
case SND_SOC_DAIFMT_CBS_CFS:
dai_fmt |= SND_SOC_DAIFMT_CBM_CFM;
break;
} ret = snd_soc_dai_set_fmt(card->rtd[i].cpu_dai,
dai_fmt);
if (ret != && ret != -ENOTSUPP)
dev_warn(card->rtd[i].cpu_dai->dev,
"ASoC: Failed to set DAI format: %d\n",
ret);
}
} snprintf(card->snd_card->shortname, sizeof(card->snd_card->shortname),
"%s", card->name);
snprintf(card->snd_card->longname, sizeof(card->snd_card->longname),
"%s", card->long_name ? card->long_name : card->name);
snprintf(card->snd_card->driver, sizeof(card->snd_card->driver),
"%s", card->driver_name ? card->driver_name : card->name);
for (i = ; i < ARRAY_SIZE(card->snd_card->driver); i++) {
switch (card->snd_card->driver[i]) {
case '_':
case '-':
case '\0':
break;
default:
if (!isalnum(card->snd_card->driver[i]))
card->snd_card->driver[i] = '_';
break;
}
} if (card->late_probe) {
ret = card->late_probe(card);
if (ret < ) {
dev_err(card->dev, "ASoC: %s late_probe() failed: %d\n",
card->name, ret);
goto probe_aux_dev_err;
}
} snd_soc_dapm_new_widgets(&card->dapm); if (card->fully_routed)
list_for_each_entry(codec, &card->codec_dev_list, card_list)
snd_soc_dapm_auto_nc_codec_pins(codec); ret = snd_card_register(card->snd_card);
if (ret < ) {
dev_err(card->dev, "ASoC: failed to register soundcard %d\n",
ret);
goto probe_aux_dev_err;
} #ifdef CONFIG_SND_SOC_AC97_BUS
/* register any AC97 codecs */
for (i = ; i < card->num_rtd; i++) {
ret = soc_register_ac97_dai_link(&card->rtd[i]);
if (ret < ) {
dev_err(card->dev, "ASoC: failed to register AC97:"
" %d\n", ret);
while (--i >= )
soc_unregister_ac97_dai_link(card->rtd[i].codec);
goto probe_aux_dev_err;
}
}
#endif card->instantiated = ;
snd_soc_dapm_sync(&card->dapm);
mutex_unlock(&card->mutex); return ; probe_aux_dev_err:
for (i = ; i < card->num_aux_devs; i++)
soc_remove_aux_dev(card, i); probe_dai_err:
soc_remove_dai_links(card); card_probe_error:
if (card->remove)
card->remove(card); snd_card_free(card->snd_card); base_error:
mutex_unlock(&card->mutex); return ret;
}

snd_soc_instantiate_card

 /*bind DAIs */
for(i = ; i < card->num_links; i++) {
//逐一绑定声卡的各类DAI链接,下面会详细介绍该函数
ret= soc_bind_dai_link(card, i);
if(ret != )
gotobase_error;
}
 /* initialize the register cache for each available codec */
//遍历CODEC列表中的所有CODEC
list_for_each_entry(codec, &codec_list, list) {
//CODEC缓存是否已初始化
if (codec->cache_init)
continue;
/* by default we don't override the compress_type */
//设置压缩类型
compress_type = ;
/* check to see if we need to override the compress_type */
for (i = ; i < card->num_configs; ++i) {
codec_conf = &card->codec_conf[i];
if (!strcmp(codec->name, codec_conf->dev_name)) {
compress_type = codec_conf->compress_type;
if (compress_type && compress_type
!= codec->compress_type)
break;
}
}
/*初始化CODEC缓存,该函数最终调用sound/soc/soc-cache.c文件内的snd_soc_flat_cache_init函数,为缓存(reg_cache)开辟内存空间,并把codec->cache_init置为1*/
ret = snd_soc_init_codec_cache(codec, compress_type);
if (ret < )
goto base_error;
}
 //创建声卡
ret= snd_card_create(SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1,
card->owner,, &card->snd_card);

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

         pm8916_tombak_dig: msm8x16_wcd_codec@f000{
compatible = "qcom,wcd-spmi";
reg = <0xf000 0x100>;
interrupt-parent = <&spmi_bus>;
interrupts = <0x1 0xf0 0x0>,
<0x1 0xf0 0x1>,
<0x1 0xf0 0x2>,
<0x1 0xf0 0x3>,
<0x1 0xf0 0x4>,
<0x1 0xf0 0x5>,
<0x1 0xf0 0x6>,
<0x1 0xf0 0x7>;
interrupt-names = "spk_cnp_int",
"spk_clip_int",
"spk_ocp_int",
"ins_rem_det1",
"but_rel_det",
"but_press_det",
"ins_rem_det",
"mbhc_int";

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

 /*注册codec的时候,会将所有注册的codec链接到codec_list中*/
list_for_each_entry(codec, &codec_list, list) {
if (dai_link->codec_of_node) {
/*根据设备数节点句柄进行匹配*/
if (codec->dev->of_node != dai_link->codec_of_node)
continue;
} else {
/*如果句柄为空,根据,codec_name进行匹配,在这里不会走这里,其实codec_name是 wcd-spmi-core.1*/
if (strcmp(codec->name, dai_link->codec_name))
continue;
} rtd->codec = codec; /*找到codec之后,根据codec_dai的名字找到对应的codec_dai*/
list_for_each_entry(codec_dai, &dai_list, list) {
if (codec->dev == codec_dai->dev &&
!strcmp(codec_dai->name,
dai_link->codec_dai_name)) { rtd->codec_dai = codec_dai;
}
}
}

所以,我们可以根据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);
 pinctrl_info.pinctrl = pinctrl;
ret = get_cdc_gpio_lines(pinctrl, pdata->ext_pa);    //获取gpio状态
if (ret < ) {
pr_err("%s: failed to ger the codec gpio's %d\n",
__func__, ret);
goto err;
}

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

 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

高通Audio中ASOC的machine驱动(一)的更多相关文章

  1. 高通ASOC中的codec驱动

    ASOC的出现是为了让codec独立于CPU,减少和CPU之间的耦合,这样同一个codec驱动就无需修改就可以匹配任何一款平台. 在Machine中已经知道,snd_soc_dai_link结构就指明 ...

  2. 【转】高通平台android 环境配置编译及开发经验总结

    原文网址:http://blog.csdn.net/dongwuming/article/details/12784535 1.高通平台android开发总结 1.1 搭建高通平台环境开发环境 在高通 ...

  3. 高通android开发摘要

    一部分是开源的,可以从codeaurora.org上下载,还有一部分是高通产权的,需要从高通的网站上下载. 将高通产权的代码放到:vendor/qcom/proprietary 1. 设置bms一些参 ...

  4. 高通、猎户机型Android典型bootloader分析

    1.bootloader是什么? 简单地说,bootloader 就是在操作系统内核运行之前运行的一段小程序.通过这段小程序,我们可以初始化硬件设备.建立内存空间的映射图,从而将系统的软硬件环境带到一 ...

  5. 高通ASOC中的machine驱动

    ASoC被分为Machine.Platform和Codec三大部分,其中的Machine驱动负责Platform和Codec之间的耦合以及部分和设备或板子特定的代码,再次引用上一节的内容:Machin ...

  6. linux驱动由浅入深系列:PBL-SBL1-(bootloader)LK-Android启动过程详解之一(高通MSM8953启动实例)

    转自:http://blog.csdn.net/radianceblau/article/details/73229005 http://www.aiuxian.com/article/p-14142 ...

  7. 高通LCD的pwm背光驱动

    发生异常的现象: msm8953 lcd在快速亮灭的情况下背光概率性休眠不灭:测量高通pwm,发现正常的时候pwm的管脚LCM_BL_PWM为低电平,失败的时候为高电平: 根据原理图: mpp是什么? ...

  8. 高通 display 驱动【转】

    高通display驱动 0. 关键字 MDSS : 高通平台lcd multimedia Display sub system DSI: Display Serial Interface qcom,m ...

  9. 高通LCD驱动调试

    本文转载自:http://www.itgo.me/a/x6305658852004979994/lcd%20qcom 来自 :http://blog.csdn.net/dacaozuo/article ...

随机推荐

  1. mysql通信协议的半双工机制理解

    一.通信知识中的半双工概念 通信的方式分为:单工通信,半双工,全双工. 全双工的典型例子是:打电话.电话在接到声音的同时也会传递声音.在一个时刻,线路上允许两个方向上的数据传输.网卡也是双工模式.在接 ...

  2. 安装Windows 8.1过程中出现的各种问题(无损从MBR转GPT磁盘、不能定位已有分区)

    这个周末就安装了个系统,本以为一个小时就能搞定,没想到花费了将近一天. 我的机子是6G内存.500G硬盘,原装系统是Windows 7,现在想换成Windows 8.1,于是下载了64位的Window ...

  3. JavaScript 经典笔记

    JavaScript 是弱类型的语言,所以编译器不能检测出类型错误. JavaScript 依赖于全局变量来进行链接.所有编译单元的所有顶级变量被撮合到一个被称为全局对象(the global obj ...

  4. 【IT笔试面试题整理】链表

    如何准备 Linked list questions are extremely common These can range from simple (delete a node ina linke ...

  5. GoogLeNetv1 论文研读笔记

    Going deeper with convolutions 原文链接 摘要 研究提出了一个名为"Inception"的深度卷积神经网结构,其目标是将分类.识别ILSVRC14数据 ...

  6. MVC应用程序显示上传的图片(续)

    上一篇<MVC应用程序显示上传的图片>http://www.cnblogs.com/insus/p/3597543.html 最后有提及没有实现用户点击图片,显示原图的功能.此篇Insus ...

  7. Entity framework 6.0 简明教程 ef6

    http://www.entityframeworktutorial.net/code-first/entity-framework-code-first.aspx Ef好的教程 Entity Fra ...

  8. C#基础知识回顾--串行化与反串行化

    串行化是指存储和获取磁盘文件.内存或其他地方中的对象.在串行化时,所有的实例数据都保存到存储介质上, 在取消串行化时,对象会被还原,且不能与其原实例区别开来.只需给类添加Serializable属性, ...

  9. 50道sql练习题和答案

    最近两年的工作没有写过多少SQL,感觉水平下降十分严重,网上找了50道练习题学习和复习 原文地址:50道SQL练习题及答案与详细分析 1.0数据表介绍 --1.学生表 Student(SId,Snam ...

  10. Oracle数据库基本常用命令汇总

    1.得到数据库名和创建日期SELECT name, created, log_mode, open_mode FROM v$database; 2.ORACLE数据库的计算机的主机名,ORACLE数据 ...