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, &