高通ASOC中的machine驱动
ASoC被分为Machine、Platform和Codec三大部分,其中的Machine驱动负责Platform和Codec之间的耦合以及部分和设备或板子特定的代码,再次引用上一节的内容:Machine驱动负责处理机器特有的一些控件和音频事件(例如,当播放音频时,需要先行打开一个放大器);单独的Platform和Codec驱动是不能工作的,它必须由Machine驱动把它们结合在一起才能完成整个设备的音频处理工作。
ASoC的一切都从Machine驱动开始,包括声卡的注册,绑定Platform和Codec驱动等等;(linux内核版本为3.10.28)
1. 注册Platform driver:
ASoC把声卡注册为Platform Device:
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
高通ASOC中的machine驱动的更多相关文章
- 高通ASOC中的codec驱动
ASOC的出现是为了让codec独立于CPU,减少和CPU之间的耦合,这样同一个codec驱动就无需修改就可以匹配任何一款平台. 在Machine中已经知道,snd_soc_dai_link结构就指明 ...
- 高通Audio中ASOC的machine驱动(一)
ASoC被分为Machine.Platform和Codec三大部分,其中的Machine驱动负责Platform和Codec之间的耦合以及部分和设备或板子特定的代码,再次引用上一节的内容:Machin ...
- 高通LCD的pwm背光驱动
发生异常的现象: msm8953 lcd在快速亮灭的情况下背光概率性休眠不灭:测量高通pwm,发现正常的时候pwm的管脚LCM_BL_PWM为低电平,失败的时候为高电平: 根据原理图: mpp是什么? ...
- 【转】高通平台android 环境配置编译及开发经验总结
原文网址:http://blog.csdn.net/dongwuming/article/details/12784535 1.高通平台android开发总结 1.1 搭建高通平台环境开发环境 在高通 ...
- 高通android开发摘要
一部分是开源的,可以从codeaurora.org上下载,还有一部分是高通产权的,需要从高通的网站上下载. 将高通产权的代码放到:vendor/qcom/proprietary 1. 设置bms一些参 ...
- 高通adsp架构下sensor
一.高通sensor架构: linux驱动由浅入深系列:高通sensor架构实例分析之一(整体概览+AP侧代码分析) linux驱动由浅入深系列:高通sensor架构实例分析之二(adsp驱动代码结构 ...
- 高通(QCOM)sensor bring up
高通7150平台 1.添加驱动文件 2.添加编译 3.配置json文件 4.高通默认配置 5.部分sensor外挂电源 6.遇到的问题 1.添加驱动文件 路径:adsp_proc/ssc/sensor ...
- 高通、猎户机型Android典型bootloader分析
1.bootloader是什么? 简单地说,bootloader 就是在操作系统内核运行之前运行的一段小程序.通过这段小程序,我们可以初始化硬件设备.建立内存空间的映射图,从而将系统的软硬件环境带到一 ...
- Linux ALSA声卡驱动之六:ASoC架构中的Machine
前面一节的内容我们提到,ASoC被分为Machine.Platform和Codec三大部分,其中的Machine驱动负责Platform和Codec之间的耦合以及部分和设备或板子特定的代码,再次引用上 ...
随机推荐
- cs231n spring 2017 lecture2 Image Classification 听课笔记
1. 相比于传统的人工提取特征(边.角等),深度学习是一种Data-Driven Approach.深度学习有统一的框架,喂不同的数据集,可以训练识别不同的物体.而人工提取特征的方式很脆弱,换一个物体 ...
- 学习笔记-nodejs报错
端口被占用 解决办法:打开控制管理器关掉node.exe进程 如果找不到可以使用命令行:netstat -ano | findstr 8000 找到进程的pid,然后到控制管理器找到进程杀掉. net ...
- 【请您听我说】PHP语法特点的一些看法
一.基本认识 PHP是干什么的?百度百科上提到说:PHP就是一门脚本语言,开发用的,相信这个你们只要去搜一下,就会有一大堆关于PHP概念的解释. 相信我们对PHP的初步认识是从浏览器开始的吧,当我们每 ...
- BZOJ 1061: [Noi2008]志愿者招募【单纯形裸题】
1061: [Noi2008]志愿者招募 Time Limit: 20 Sec Memory Limit: 162 MBSubmit: 4813 Solved: 2877[Submit][Stat ...
- HDU 2079 dp解法
选课时间(题目已修改,注意读题) Time Limit: 1000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others) ...
- JAVA爬虫实践(实践四:webMagic和phantomjs和淘宝爬虫)
webMagic虽然方便,但是也有它不适用的地方,比如定向的某个单页面爬虫,或者存在大量ajax请求,页面的跳转请求全都混淆在js里. 这时可以用webMagic结合phantomjs来真实模拟页面请 ...
- Lua语言的介绍和编程语言的归类
Lua 本条目介绍的是一种编程语言.关于关于Lua在维基百科中的使用,请见"维基百科:Lua".关于"Lua"一词的其他意思,请见"卢阿". ...
- django入门基础
首先要说的是django与其他的框架不同,django是一个封装的及其完善的框架,我们使用django也不会像之前写学生系统那样,django操作数据库使用自带的ORM来进行操作,Torando与Fl ...
- 基于Vue的小日历(支持按周切换)
基于Vue的日历小功能,可根据实际开发情况按每年.每月.每周.进行切换 <template> <div class="date"> <!-- 年份 ...
- [SinGuLaRiTy] 复习模板-图论
[SinGuLaRiTy-1041] Copyright (c) SinGuLaRiTy 2017. All Rights Reserved. 计算树的直径 //方法:任选一个点作为起点进行一次BFS ...