zoukankan      html  css  js  c++  java
  • 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
    ");
    		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
    ",
    				__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
    ");
    		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
    ",
    				__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
    ");
    		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
    ",
    				__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
    ");
    		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
    ",
    				__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
    ", __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
    ", __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
    ",
    						__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
    ",
    						__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
    ",
    						__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
    ");
    	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
    ",
    				__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
    ",
    					__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
    ", __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
    ",
    				__func__, ret);
    		goto err;
    	}
    
    	ret = is_ext_spk_gpio_support(pdev, pdata);
    	if (ret < 0)
    		pr_err("%s:  doesn't support external speaker pa
    ",
    				__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.
    ",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
    ",
    			__func__, hs_micbias_type);
    		goto err;
    	}
    	// 使用内部还是外部的micbias
    	if (!strcmp(type, "external")) {
    		dev_dbg(&pdev->dev, "Headset is using external micbias
    ");
    		mbhc_cfg.hs_ext_micbias = true;
    	} else {
    		dev_dbg(&pdev->dev, "Headset is using internal micbias
    ");
    		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)
    ",
    			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
    ",
    				__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
    ",
    				__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
    ",
    				__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",
    	},
    	...
    }
    
  • 相关阅读:
    SCCM2012分发脚本
    MPIO配置
    创建快捷方式,修改注册表
    常用口语 一
    3463工厂频道预置方法
    xargs 主要用于不支持管道的shell命令*****
    RDA DEBUG
    linux shell 实例1
    MSD3458开发资料
    MSD6A628开发资料与技术支持
  • 原文地址:https://www.cnblogs.com/helloworldtoyou/p/10499787.html
Copyright © 2011-2022 走看看