msm audio machine 代码跟踪


sound/soc/msm/msm8952.c

// 注册平台设备
static int __init msm8952_machine_init(void)
{
	return platform_driver_register(&msm8952_asoc_machine_driver);
}
late_initcall(msm8952_machine_init);

// 驱动和设备的匹配表
static const struct of_device_id msm8952_asoc_machine_of_match[]  = {
	{ .compatible = "qcom,msm8952-audio-codec", },
	{},
};

static struct platform_driver msm8952_asoc_machine_driver = {
	.driver = {
		.name = DRV_NAME,
		.owner = THIS_MODULE,
		.pm = &snd_soc_pm_ops,
		.of_match_table = msm8952_asoc_machine_of_match,
	},
	.probe = msm8952_asoc_machine_probe,
	.remove = msm8952_asoc_machine_remove,
};

static int msm8952_asoc_machine_probe(struct platform_device *pdev)
{
	struct snd_soc_card *card;
	struct msm8916_asoc_mach_data *pdata = NULL;
	const char *hs_micbias_type = "qcom,msm-hs-micbias-type";
	const char *ext_pa = "qcom,msm-ext-pa";
	const char *mclk = "qcom,msm-mclk-freq";
	const char *wsa = "asoc-wsa-codec-names";
	const char *wsa_prefix = "asoc-wsa-codec-prefixes";
	const char *type = NULL;
	const char *ext_pa_str = NULL;
	const char *wsa_str = NULL;
	const char *wsa_prefix_str = NULL;
	int num_strings;
	int ret, id, i, val;
	struct resource	*muxsel;
	char *temp_str = NULL;

	pdata = devm_kzalloc(&pdev->dev,
			sizeof(struct msm8916_asoc_mach_data), GFP_KERNEL);
	if (!pdata)
		return -ENOMEM;

	muxsel = platform_get_resource_byname(pdev, IORESOURCE_MEM,
			"csr_gp_io_mux_mic_ctl");
	if (!muxsel) {
		dev_err(&pdev->dev, "MUX addr invalid for MI2S\n");
		ret = -ENODEV;
		goto err1;
	}
	pdata->vaddr_gpio_mux_mic_ctl =
		ioremap(muxsel->start, resource_size(muxsel));
	if (pdata->vaddr_gpio_mux_mic_ctl == NULL) {
		pr_err("%s ioremap failure for muxsel virt addr\n",
				__func__);
		ret = -ENOMEM;
		goto err1;
	}

	muxsel = platform_get_resource_byname(pdev, IORESOURCE_MEM,
			"csr_gp_io_mux_spkr_ctl");
	if (!muxsel) {
		dev_err(&pdev->dev, "MUX addr invalid for MI2S\n");
		ret = -ENODEV;
		goto err;
	}
	pdata->vaddr_gpio_mux_spkr_ctl =
		ioremap(muxsel->start, resource_size(muxsel));
	if (pdata->vaddr_gpio_mux_spkr_ctl == NULL) {
		pr_err("%s ioremap failure for muxsel virt addr\n",
				__func__);
		ret = -ENOMEM;
		goto err;
	}

	muxsel = platform_get_resource_byname(pdev, IORESOURCE_MEM,
			"csr_gp_io_lpaif_pri_pcm_pri_mode_muxsel");
	if (!muxsel) {
		dev_err(&pdev->dev, "MUX addr invalid for MI2S\n");
		ret = -ENODEV;
		goto err;
	}
	pdata->vaddr_gpio_mux_pcm_ctl =
		ioremap(muxsel->start, resource_size(muxsel));
	if (pdata->vaddr_gpio_mux_pcm_ctl == NULL) {
		pr_err("%s ioremap failure for muxsel virt addr\n",
				__func__);
		ret = -ENOMEM;
		goto err;
	}

	muxsel = platform_get_resource_byname(pdev, IORESOURCE_MEM,
			"csr_gp_io_mux_quin_ctl");
	if (!muxsel) {
		dev_dbg(&pdev->dev, "MUX addr invalid for MI2S\n");
		goto parse_mclk_freq;
	}
	pdata->vaddr_gpio_mux_quin_ctl =
		ioremap(muxsel->start, resource_size(muxsel));
	if (pdata->vaddr_gpio_mux_quin_ctl == NULL) {
		pr_err("%s ioremap failure for muxsel virt addr\n",
				__func__);
		ret = -ENOMEM;
		goto err;
	}
parse_mclk_freq:
	// 获取时钟
	ret = of_property_read_u32(pdev->dev.of_node, mclk, &id);
	if (ret) {
		dev_err(&pdev->dev,
				"%s: missing %s in dt node\n", __func__, mclk);
		id = DEFAULT_MCLK_RATE;
	}
	pdata->mclk_freq = id;

	/*reading the gpio configurations from dtsi file*/
	ret = msm_gpioset_initialize(CLIENT_WCD_INT, &pdev->dev);
	if (ret < 0) {
		dev_err(&pdev->dev,
			"%s: error reading dtsi files%d\n", __func__, ret);
		goto err;
	}

	num_strings = of_property_count_strings(pdev->dev.of_node,
			wsa);
	if (num_strings > 0) {
		if (wsa881x_get_probing_count() < 2) {
			ret = -EPROBE_DEFER;
			goto err;
		} else if (wsa881x_get_presence_count() == num_strings) {
			bear_card.aux_dev = msm8952_aux_dev;
			bear_card.num_aux_devs = num_strings;
			bear_card.codec_conf = msm8952_codec_conf;
			bear_card.num_configs = num_strings;

			for (i = 0; i < num_strings; i++) {
				ret = of_property_read_string_index(
						pdev->dev.of_node, wsa,
						i, &wsa_str);
				if (ret) {
					dev_err(&pdev->dev,
						"%s:of read string %s i %d error %d\n",
						__func__, wsa, i, ret);
					goto err;
				}
				temp_str = kstrdup(wsa_str, GFP_KERNEL);
				if (!temp_str) {
					ret = -ENOMEM;
					goto err;
				}
				msm8952_aux_dev[i].codec_name = temp_str;
				temp_str = NULL;

				temp_str = kstrdup(wsa_str, GFP_KERNEL);
				if (!temp_str) {
					ret = -ENOMEM;
					goto err;
				}
				msm8952_codec_conf[i].dev_name = temp_str;
				temp_str = NULL;

				ret = of_property_read_string_index(
						pdev->dev.of_node, wsa_prefix,
						i, &wsa_prefix_str);
				if (ret) {
					dev_err(&pdev->dev,
						"%s:of read string %s i %d error %d\n",
						__func__, wsa_prefix, i, ret);
					goto err;
				}
				temp_str = kstrdup(wsa_prefix_str, GFP_KERNEL);
				if (!temp_str) {
					ret = -ENOMEM;
					goto err;
				}
				msm8952_codec_conf[i].name_prefix = temp_str;
				temp_str = NULL;
			}

			ret = msm8952_init_wsa_switch_supply(pdev, pdata);
			if (ret < 0) {
				pr_err("%s: failed to init wsa_switch vdd supply %d\n",
						__func__, ret);
				goto err;
			}
			wsa881x_set_mclk_callback(msm8952_enable_wsa_mclk);
			/* update the internal speaker boost usage */
			msm8x16_update_int_spk_boost(false);
		}
	}
	// 获取dai_links
	card = msm8952_populate_sndcard_dailinks(&pdev->dev);
	dev_info(&pdev->dev, "default codec configured\n");
	num_strings = of_property_count_strings(pdev->dev.of_node,
			ext_pa);
	if (num_strings < 0) {
		dev_err(&pdev->dev,
				"%s: missing %s in dt node or length is incorrect\n",
				__func__, ext_pa);
		goto err;
	}
	for (i = 0; i < num_strings; i++) {
		ret = of_property_read_string_index(pdev->dev.of_node,
				ext_pa, i, &ext_pa_str);
		if (ret) {
			dev_err(&pdev->dev, "%s:of read string %s i %d error %d\n",
					__func__, ext_pa, i, ret);
			goto err;
		}
		if (!strcmp(ext_pa_str, "primary"))
			pdata->ext_pa = (pdata->ext_pa | PRI_MI2S_ID);
		else if (!strcmp(ext_pa_str, "secondary"))
			pdata->ext_pa = (pdata->ext_pa | SEC_MI2S_ID);
		else if (!strcmp(ext_pa_str, "tertiary"))
			pdata->ext_pa = (pdata->ext_pa | TER_MI2S_ID);
		else if (!strcmp(ext_pa_str, "quaternary"))
			pdata->ext_pa = (pdata->ext_pa | QUAT_MI2S_ID);
		else if (!strcmp(ext_pa_str, "quinary"))
			pdata->ext_pa = (pdata->ext_pa | QUIN_MI2S_ID);
	}
	pr_debug("%s: ext_pa = %d\n", __func__, pdata->ext_pa);

	ret = is_us_eu_switch_gpio_support(pdev, pdata);
	if (ret < 0) {
		pr_err("%s: failed to is_us_eu_switch_gpio_support %d\n",
				__func__, ret);
		goto err;
	}

	ret = is_ext_spk_gpio_support(pdev, pdata);
	if (ret < 0)
		pr_err("%s:  doesn't support external speaker pa\n",
				__func__);

		get_dev_by_boardid(model_name);
		if(0 == strcmp(model_name,"eda71")){
			enable_aw8738 = of_property_read_bool(pdev->dev.of_node , "action,enable-aw8738");
			if(enable_aw8738)
			{
				gpio_request(pdata->spk_ext_pa_gpio, "AW8738_EN");
				gpio_direction_output(pdata->spk_ext_pa_gpio,0);
			}
		}
		//printk("enable_aw8738:%d model_name:%s.\n",enable_aw8738,model_name);
	// 查看micbias类型
	ret = of_property_read_string(pdev->dev.of_node,
		hs_micbias_type, &type);
	if (ret) {
		dev_err(&pdev->dev, "%s: missing %s in dt node\n",
			__func__, hs_micbias_type);
		goto err;
	}
	// 使用内部还是外部的micbias
	if (!strcmp(type, "external")) {
		dev_dbg(&pdev->dev, "Headset is using external micbias\n");
		mbhc_cfg.hs_ext_micbias = true;
	} else {
		dev_dbg(&pdev->dev, "Headset is using internal micbias\n");
		mbhc_cfg.hs_ext_micbias = false;
	}

	ret = of_property_read_u32(pdev->dev.of_node,
				  "qcom,msm-afe-clk-ver", &val);
	if (ret)
		pdata->afe_clk_ver = AFE_CLK_VERSION_V2;
	else
		pdata->afe_clk_ver = val;
	/* initialize the mclk */
	pdata->digital_cdc_clk.i2s_cfg_minor_version =
					AFE_API_VERSION_I2S_CONFIG;
	pdata->digital_cdc_clk.clk_val = pdata->mclk_freq;
	pdata->digital_cdc_clk.clk_root = 5;
	pdata->digital_cdc_clk.reserved = 0;
	/* initialize the digital codec core clk */
	pdata->digital_cdc_core_clk.clk_set_minor_version =
			AFE_API_VERSION_I2S_CONFIG;
	pdata->digital_cdc_core_clk.clk_id =
			Q6AFE_LPASS_CLK_ID_INTERNAL_DIGITAL_CODEC_CORE;
	pdata->digital_cdc_core_clk.clk_freq_in_hz =
			pdata->mclk_freq;
	pdata->digital_cdc_core_clk.clk_attri =
			Q6AFE_LPASS_CLK_ATTRIBUTE_COUPLE_NO;
	pdata->digital_cdc_core_clk.clk_root =
			Q6AFE_LPASS_CLK_ROOT_DEFAULT;
	pdata->digital_cdc_core_clk.enable = 1;

	/* Initialize loopback mode to false */
	pdata->lb_mode = false;

	msm8952_dt_parse_cap_info(pdev, pdata);

	card->dev = &pdev->dev;
	platform_set_drvdata(pdev, card);
	snd_soc_card_set_drvdata(card, pdata);
	// 解析设备数中声卡的名称, qcom,model = "msm8953-snd-card-mtp";
	ret = snd_soc_of_parse_card_name(card, "qcom,model");
	if (ret)
		goto err;
	/* initialize timer */
	INIT_DELAYED_WORK(&pdata->disable_mclk_work, msm8952_disable_mclk);
	mutex_init(&pdata->cdc_mclk_mutex);
	atomic_set(&pdata->mclk_rsc_ref, 0);
	if (card->aux_dev) {
		mutex_init(&pdata->wsa_mclk_mutex);
		atomic_set(&pdata->wsa_mclk_rsc_ref, 0);
	}
	atomic_set(&pdata->mclk_enabled, false);
	atomic_set(&quat_mi2s_clk_ref, 0);
	atomic_set(&quin_mi2s_clk_ref, 0);
	atomic_set(&auxpcm_mi2s_clk_ref, 0);
	// 声卡的routing
	ret = snd_soc_of_parse_audio_routing(card,
			"qcom,audio-routing");
	if (ret)
		goto err;

	ret = msm8952_populate_dai_link_component_of_node(card);
	if (ret) {
		ret = -EPROBE_DEFER;
		goto err;
	}
	// 注册声卡
	ret = snd_soc_register_card(card);
	if (ret) {
		dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n",
			ret);
		goto err;
	}
	return 0;
err:
	if (pdata->vaddr_gpio_mux_spkr_ctl)
		iounmap(pdata->vaddr_gpio_mux_spkr_ctl);
	if (pdata->vaddr_gpio_mux_mic_ctl)
		iounmap(pdata->vaddr_gpio_mux_mic_ctl);
	if (pdata->vaddr_gpio_mux_pcm_ctl)
		iounmap(pdata->vaddr_gpio_mux_pcm_ctl);
	if (pdata->vaddr_gpio_mux_quin_ctl)
		iounmap(pdata->vaddr_gpio_mux_quin_ctl);
	if (bear_card.num_aux_devs > 0) {
		for (i = 0; i < bear_card.num_aux_devs; i++) {
			kfree(msm8952_aux_dev[i].codec_name);
			kfree(msm8952_codec_conf[i].dev_name);
			kfree(msm8952_codec_conf[i].name_prefix);
		}
	}
err1:
	devm_kfree(&pdev->dev, pdata);
	return ret;
}

static struct snd_soc_card *msm8952_populate_sndcard_dailinks(
						struct device *dev)
{
	struct snd_soc_card *card = &bear_card;
	struct snd_soc_dai_link *dailink;
	int len1;

	card->name = dev_name(dev);
	len1 = ARRAY_SIZE(msm8952_dai);
	// dai_links复制给snd_soc_card, 注册的时候会用到
	memcpy(msm8952_dai_links, msm8952_dai, sizeof(msm8952_dai));
	dailink = msm8952_dai_links;
	if (of_property_read_bool(dev->of_node,
				"qcom,hdmi-dba-codec-rx")) {
		dev_dbg(dev, "%s(): hdmi audio support present\n",
				__func__);
		memcpy(dailink + len1, msm8952_hdmi_dba_dai_link,
				sizeof(msm8952_hdmi_dba_dai_link));
		len1 += ARRAY_SIZE(msm8952_hdmi_dba_dai_link);
	} else {
		dev_dbg(dev, "%s(): No hdmi dba present, add quin dai\n",
				__func__);
		memcpy(dailink + len1, msm8952_quin_dai_link,
				sizeof(msm8952_quin_dai_link));
		len1 += ARRAY_SIZE(msm8952_quin_dai_link);
	}
	if (of_property_read_bool(dev->of_node,
				"qcom,split-a2dp")) {
		dev_dbg(dev, "%s(): split a2dp support present\n",
				__func__);
		memcpy(dailink + len1, msm8952_split_a2dp_dai_link,
				sizeof(msm8952_split_a2dp_dai_link));
		len1 += ARRAY_SIZE(msm8952_split_a2dp_dai_link);
	}
	card->dai_link = dailink;
	card->num_links = len1;
	return card;
}



// machine驱动会用这些参数去匹配platform,codec,adi.
// 都是在platform,codec中定义
/* Digital audio interface glue - connects codec <---> CPU */
static struct snd_soc_dai_link msm8952_dai[] = {
	/* FrontEnd DAI Links */
	{/* hw:x,0 */
		.name = "MSM8952 Media1",		// Media1 播放链路
		.stream_name = "MultiMedia1",	// 匹配pcm id
		.cpu_dai_name	= "MultiMedia1",	// 匹配cpu dai driver
		.platform_name  = "msm-pcm-dsp.0",	// 匹配platform driver
		.dynamic = 1,
		.async_ops = ASYNC_DPCM_SND_SOC_PREPARE,
		.dpcm_playback = 1,
		.dpcm_capture = 1,
		.trigger = {SND_SOC_DPCM_TRIGGER_POST,
			SND_SOC_DPCM_TRIGGER_POST},
		.codec_dai_name = "snd-soc-dummy-dai",	// 匹配codec dai driver
		.codec_name = "snd-soc-dummy",	// 匹配codec driver
		.ignore_suspend = 1,
		/* this dainlink has playback support */
		.ignore_pmdown_time = 1,
		.be_id = MSM_FRONTEND_DAI_MULTIMEDIA1
	},
	{/* hw:x,1 */
		.name = "MSM8952 Media2",		// Media2播放链路
		.stream_name = "MultiMedia2",
		.cpu_dai_name   = "MultiMedia2",
		.platform_name  = "msm-pcm-dsp.0",
		.dynamic = 1,
		.dpcm_playback = 1,
		.dpcm_capture = 1,
		.codec_dai_name = "snd-soc-dummy-dai",
		.codec_name = "snd-soc-dummy",
		.trigger = {SND_SOC_DPCM_TRIGGER_POST,
			SND_SOC_DPCM_TRIGGER_POST},
		.ignore_suspend = 1,
		/* this dainlink has playback support */
		.ignore_pmdown_time = 1,
		.be_id = MSM_FRONTEND_DAI_MULTIMEDIA2,
	},
	{/* hw:x,2 */
		.name = "Circuit-Switch Voice",
		.stream_name = "CS-Voice",
		.cpu_dai_name   = "CS-VOICE",
		.platform_name  = "msm-pcm-voice",
		.dynamic = 1,
		.dpcm_playback = 1,
		.dpcm_capture = 1,
		.trigger = {SND_SOC_DPCM_TRIGGER_POST,
			SND_SOC_DPCM_TRIGGER_POST},
		.no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
		.ignore_suspend = 1,
		/* this dainlink has playback support */
		.ignore_pmdown_time = 1,
		.be_id = MSM_FRONTEND_DAI_CS_VOICE,
		.codec_dai_name = "snd-soc-dummy-dai",
		.codec_name = "snd-soc-dummy",
	},
	...
}
posted @ 2019-03-09 10:38  SuperTao1024  阅读(746)  评论(2编辑  收藏  举报