afe 高通_高通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- = ;
pinctrl- = ;
pinctrl- = ;
pinctrl- = ;
};
通过与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 = ;
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架构,网上搜索相应资料便可找到;贴上借鉴的资料:
网上大牛的架构:
高通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驱动代码结构 ...
高通、猎户机型Android典型bootloader分析
1.bootloader是什么? 简单地说,bootloader 就是在操作系统内核运行之前运行的一段小程序.通过这段小程序,我们可以初始化硬件设备.建立内存空间的映射图,从而将系统的软硬件环境带到一 ...
Linux ALSA声卡驱动之六:ASoC架构中的Machine
前面一节的内容我们提到,ASoC被分为Machine.Platform和Codec三大部分,其中的Machine驱动负责Platform和Codec之间的耦合以及部分和设备或板子特定的代码,再次引用上 ...
linux驱动由浅入深系列:PBL-SBL1-(bootloader)LK-Android启动过程详解之一(高通MSM8953启动实例)
转自:http://blog.csdn.net/radianceblau/article/details/73229005 http://www.aiuxian.com/article/p-14142 ...
随机推荐
jdbc java数据库连接 10)批处理
批处理 很多时候,需要批量执行sql语句! 需求:批量保存信息! 设计: AdminDao Public void save(List
android加载大图片到内存
1)演示效果: 1)代码演示: 布局代码: 权限配置:
iOS项目的目录结构和开发流程(Cocoa China)
目录结构 AppDelegate Models Macro General Helpers Vendors Sections Resources 一个合理的目录结构首先应该是清晰的,让人一眼看上去 ...
php下载文件的代码示例
php下载文件的代码示例,需要的朋友可以参考下 <?php $file = 'monkey.gif'; if (file_exists($file)) { header('Content- ...
js学习笔记—转载(闭包问题)
---恢复内容开始--- 闭包(closure)是Javascript语言的一个难点,也是它的特色,很多高级应用都要依靠闭包实现. 一.变量的作用域 要理解闭包,首先必须理解Javascrip ...
sublime text格式化插件
sublime text 软件其实是自带格式化插件的,但是它默认的格式化插件,不太好用,且没有快捷键(虽然自己可以设置). 其默认的格式化是在 Edit -> Line -> Re ...
java创建多线程(转载)
转载自:Java创建线程的两个方法 Java提供了线程类Thread来创建多线程的程序.其实,创建线程与创建普通的类的对象的操作是一样的,而线程就是Thread类或其子类的实例对象.每个Thread对 ...
【hihoCoder】#1039 : 字符消除 by C solution
#1039 : 字符消除 时间限制:1000ms 单点时限:1000ms 内存限制:256MB 描述 小Hi最近在玩一个字符消除游戏.给定一个只包含大写字母"ABC"的字符串s,消 ...
Centos Git1.7.1升级到Git2.2.1
安装需求: ># yum install curl-devel expat-devel gettext-devel openssl-devel zlib-devel asciidoc ># ...
afe 高通_高通ASOC中的machine驱动相关推荐
- eclipse经常高占用_高可用系统的设计指南
可靠的系统是业务稳定.快速发展的基石.那么,如何做到系统高可靠.高可用呢?下面从技术方面介绍几种提高系统可靠性.可用性的方法. 扩展 扩展是最常见的提升系统可靠性的方法,系统的扩展可以避免单点故障,即 ...
- 调试 高通_高通平台调整通话音量
文档说明 本文档以SC826-CN-01(msm8953平台,Android 7.1)为例,说明如何调节通话音量大小. 问题现象 客户板子通过耳机通道 HPH_L / HPH_R ->外部PA( ...
- java webservice 高并发_浅谈WEB中的高并发
今天主要是对JAVA_WEB中高并发的概念及常见的处理手段做个基本介绍,后面会每个点都做详细的介绍及实现. 何谓高并发 高并发指的是:在同时或极短时间内,有大量的请求到达服务端,每个请求都需要服务端耗 ...
- ci mysql高并发_高并发访问mysql时的问题(一):库存超减
标签: 如果在对某行记录的更新时不采取任何防范措施,在多线程访问时,就容易出现库存为负数的错误. 以下用php.mysql,apache ab工具举例说明: mysql表结构 CREATE TABLE ...
- 队列处理高并发_高并发场景下缓存处理的一些思路
在实际的开发当中,我们经常需要进行磁盘数据的读取和搜索,因此经常会有出现从数据库读取数据的场景出现. 但是当数据访问量次数增大的时候,过多的磁盘读取可能会最终成为整个系统的性能瓶颈,甚至是压垮整个数据 ...
- greenplum配置高可用_高可用hadoop集群配置就收藏这一篇,动手搭建Hadoop(5)
01 ssh免密安装 02 jdk安装 03 hadoop伪分布式安装 04 hadoop全分布式 完成了前面四步,现在做hadoop的高可用.其实和之前的lvs的高可用差不多的.如果我们有两个nam ...
- .net core高并发_高并发下的Node.js与负载均衡
阅读本文约需要6分钟 大家好,我是你们的导师,我每天都会在这里给大家分享一些干货内容(当然了,周末也要允许老师休息一下哈).上次老师跟大家分享了下浅谈前端自动化构建的相关知识,今天跟大家分享浅谈前端自 ...
- 队列处理高并发_高并发架构消息队列面试题解析
面试题 为什么使用消息队列? 消息队列有什么优点和缺点? Kafka.ActiveMQ.RabbitMQ.RocketMQ 都有什么区别,以及适合哪些场景? 面试官心理分析 其实面试官主要是想看看: ...
- keepalive+nginx实现负载均衡高可用_高可用、负载均衡 集群部署方案:Keepalived + Nginx + Tomcat...
前言:初期应用较小,一般以单机部署为主,即可满足业务的需求,随着业务的不断扩大,单机部署的模式无法承载这么大的业务量,需要进行服务集群化的部署,本文主要介绍服务器Tomcat多实例部署,搭载Keepa ...
最新文章
- 最短路径问题 java实现 源代码
- c++ 关于char *的类库函数
- iPad的无纸化办公和纯纸化办公的区别和使用场景
- HDOJ 2049 不容易系列之(4)——考新郎
- Linux ps指令
- cocos2d-x游戏实例(14)-纵版射击游戏(1)
- Java最佳实践–队列之战和链接的ConcurrentHashMap
- 2020年8个效率最高的爬虫框架
- 腾讯开源业界首个云原生标准的一站式微服务管理框架Femas
- JavaScript笔记2———js的数据类型
- 控制台模块python_04_python常用模块
- Atitit 存储方法大总结 目录 1. 存储方式分类	2 1.1. 按照数据分类为 结构化 半结构化 非结构化	2 1.2. 按照内外部可分类 内部存储和外部存储持久化	2 1.3. 按照本地远
- Nagios社区真有意思
- 计算机应用数学,计算机应用数学.PDF
- Timer的源码分析
- 二进制 计算机编程语言分类
- 计算机网络中atm意思,atm是什么(atm什么意思)
- 苹果MAC电脑双系统教程——MAC安装Windows双系统教程
- 西门子三开接线图解_西门子三位单控开关怎么接线要开关实际图
- 基于机器学习场景,如何搭建特征数据管理中台?