This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

TDA4VM: 声卡驱动芯片变更

Part Number: TDA4VM
Other Parts Discussed in Thread: PCM3168A

J721EXCPXEVM扩展板中使用的芯片为PCM3168APAP,在设备树中,此芯片的驱动程序与mscap10共同组成了sound声卡驱动,映射到sound下的j721e-evm.c中,现在音频芯片更换成了TLV320AIC3109,在修改了设备树文件后,是否要修改j721e-evm.c文件来适配该芯片驱动?请问是否有TLV320AIC3109驱动设备树例程可供参考?

  • 已转给e2e工程师,请看下面产品线工程师的回复。
    Typically dts changes with the corresponding config enabling should be good. I am not the audio expert. I am commenting more from the Linux angle.
    https://e2e.ti.com/support/processors-group/processors/f/processors-forum/1273708/tda4vm-tlv320aic3109-sound-driver

  • 麻烦请您将下面的dts设备节点代码转发给他浏览:

    sound0: sound@0
    {
    compatible = "simple-audio-card";
    simple-audio-card,name = "TI BeagleBone Black";
    #sound-dai-cells = <0>;
    clocks = <&k3_clks 184 1>,
    <&k3_clks 184 2>, <&k3_clks 184 4>,
    <&k3_clks 157 371>,
    <&k3_clks 157 400>, <&k3_clks 157 401>;
    clock-names = "cpb-mcasp-auxclk",
    "cpb-mcasp-auxclk-48000", "cpb-mcasp-auxclk-44100",
    "cpb-codec-scki",
    "cpb-codec-scki-48000", "cpb-codec-scki-44100";

    simple-audio-card,dai-link@0
    {
    format = "dsp_a";
    bitclock-master = <&sound0_master>;
    frame-master = <&sound0_master>;
    sound0_master: cpu
    {
    sound-dai = <&mcasp10>;
    };
    codec
    {
    sound-dai = <&tlv320aic3109>;
    };
    };
    };

    &mcasp10 {
    #sound-dai-cells = <0>;
    pinctrl-names = "default";
    pinctrl-0 = <&mcasp10_pins_default>;
    status = "okay";
    op-mode = <0>; /* MCASP_IIS_MODE */
    tdm-slots = <4>;
    auxclk-fs-ratio = <256>;
    serial-dir = < /* 0: INACTIVE, 1: TX, 2: RX */
    1 2 0 0
    0 0 0 0
    >;
    tx-num-evt = <32>;
    rx-num-evt = <32>;

    };

    tlv320aic3109: tlv320aic3109@18 {
    compatible = "ti,tlv320aic3x";
    reg = <0x18>;
    #sound-dai-cells = <0>;
    pinctrl-names = "default";
    pinctrl-0 = <&tlv320aic3x_reset_pin_default>;
    reset-gpios = <&main_gpio0 111 GPIO_ACTIVE_LOW>;
    };

    音频设备的供电是提前给上的,不需要在设备树中指明,I2C通讯正常,请问设备树DTS描述中有哪些错误,请指正

  • 你好,请问可否帮忙催促一下

  • 好的,已经去催了。

  • 修改驱动与设备树后,声卡检测正常

    TDA4VM与音频芯片的IIC通讯正常,驱动可正常挂载与安装

    声卡驱动挂载打印如下:

    但是挂载成功后,无论是运行aplay,还是speaker-test,都不能看到MCASP输出音频时钟信号,也没有播放出来声音,请问这是什么原因导致的?

    tlv音频芯片dts设置

    	tlv320aic31xx: tlv320aic31xx@18 {
    		compatible = "ti,tlv320aic3x";
    		reg = <0x18>;
    		reset-gpios = <&main_gpio0 111 GPIO_ACTIVE_HIGH>;
    
    		/* C_AUDIO_REFCLK2 -> RGMII6_RXC (W26) */
    		clocks = <&k3_clks 157 371>;
    		clock-names = "scki";
    
    		/* HSDIV3_16FFT_MAIN_4_HSDIVOUT2_CLK -> REFCLK2 */
    		assigned-clocks = <&k3_clks 157 371>;
    		assigned-clock-parents = <&k3_clks 157 400>;
    		assigned-clock-rates = <24576000>; /* for 48KHz */
    
    		AVDD-supply = <&vsys_3v3>;
    		IOVDD-supply = <&vsys_3v3>;
    		DRVDD-supply = <&vsys_3v3>;
    		DVDD-supply = <&vsys_3v3>;
    	};
    下面是我的驱动代码
    // SPDX-License-Identifier: GPL-2.0
    /*
     *  Copyright (C) 2020 Texas Instruments Incorporated - http://www.ti.com
     *  Author: Peter Ujfalusi <peter.ujfalusi@ti.com>
     */
    
    #include <linux/clk.h>
    #include <linux/module.h>
    #include <linux/of.h>
    #include <linux/platform_device.h>
    
    #include <sound/core.h>
    #include <sound/pcm.h>
    #include <sound/pcm_params.h>
    #include <sound/soc.h>
    
    #include "../codecs/tlv320aic23.h"
    #include "davinci-mcasp.h"
    
    
    #define CODEC_CLOCK 24000000  
    /*
     * Maximum number of configuration entries for prefixes:
     * CPB: 2 (mcasp10 + codec)
     * IVI: 3 (mcasp0 + 2x codec)
     */
    #define J721E_CODEC_CONF_COUNT	5
    
    #define J721E_AUDIO_DOMAIN_CPB	0
    #define J721E_AUDIO_DOMAIN_IVI	1
    
    #define J721E_CLK_PARENT_48000	0
    #define J721E_CLK_PARENT_44100	1
    
    #define J721E_MAX_CLK_HSDIV	128
    #define PCM1368A_MAX_SYSCLK	36864000
    
    #define J721E_DAI_FMT		(SND_SOC_DAIFMT_RIGHT_J | \
    				 SND_SOC_DAIFMT_NB_NF |   \
    				 SND_SOC_DAIFMT_CBS_CFS)
    
    enum j721e_board_type {
    	J721E_BOARD_CPB = 1,
    	J721E_BOARD_CPB_IVI,
    };
    
    struct j721e_audio_match_data {
    	enum j721e_board_type board_type;
    	int num_links;
    	unsigned int pll_rates[2];
    };
    
    static unsigned int ratios_for_pcm3168a[] = {
    	256,
    	512,
    	768,
    };
    
    struct j721e_audio_clocks {
    	struct clk *target;
    	struct clk *parent[2];
    };
    
    struct j721e_audio_domain {
    	struct j721e_audio_clocks codec;
    	struct j721e_audio_clocks mcasp;
    	int parent_clk_id;
    
    	int active;
    	unsigned int active_link;
    	unsigned int rate;
    };
    
    struct j721e_priv {
    	struct device *dev;
    	struct snd_soc_card card;
    	struct snd_soc_dai_link *dai_links;
    	struct snd_soc_codec_conf codec_conf[J721E_CODEC_CONF_COUNT];
    	struct snd_interval rate_range;
    	const struct j721e_audio_match_data *match_data;
    	u32 pll_rates[2];
    	unsigned int hsdiv_rates[2];
    
    	struct j721e_audio_domain audio_domains[2];
    
    	struct mutex mutex;
    };
    
    static const struct snd_soc_dapm_widget j721e_cpb_dapm_widgets[] = {
    	SND_SOC_DAPM_LINE("CPB Line1 Out", NULL),
    	SND_SOC_DAPM_LINE("CPB Line2 Out", NULL),
    	SND_SOC_DAPM_LINE("CPB Line3 Out", NULL),
    };
    
    static const struct snd_soc_dapm_route j721e_cpb_dapm_routes[] = {
    	{"CPB Line1 Out", NULL, "codec-1 LLOUT"},
    	{"CPB Line1 Out", NULL, "codec-1 RLOUT"},
    	{"CPB Line2 Out", NULL, "codec-1 HPLOUT"},
    	{"CPB Line2 Out", NULL, "codec-1 HPROUT"},
    	{"CPB Line3 Out", NULL, "codec-1 HPLCOM"},
    	{"CPB Line3 Out", NULL, "codec-1 HPRCOM"},
    };
    
    static int j721e_configure_refclk(struct j721e_priv *priv,
    				  unsigned int audio_domain, unsigned int rate)
    {
    	struct j721e_audio_domain *domain = &priv->audio_domains[audio_domain];
    	unsigned int scki;
    	int ret = -EINVAL;
    	int i, clk_id;
    
    	if (!(rate % 8000) && priv->pll_rates[J721E_CLK_PARENT_48000])
    		clk_id = J721E_CLK_PARENT_48000;
    	else if (!(rate % 11025) && priv->pll_rates[J721E_CLK_PARENT_44100])
    		clk_id = J721E_CLK_PARENT_44100;
    	else
    		return ret;
    
    	for (i = 0; i < ARRAY_SIZE(ratios_for_pcm3168a); i++) {
    		scki = ratios_for_pcm3168a[i] * rate;
    
    		if (priv->pll_rates[clk_id] / scki <= J721E_MAX_CLK_HSDIV) {
    			ret = 0;
    			break;
    		}
    	}
    
    	if (ret) {
    		dev_err(priv->dev, "No valid clock configuration for %u Hz\n",
    			rate);
    		return ret;
    	}
    
    	if (domain->parent_clk_id == -1 || priv->hsdiv_rates[domain->parent_clk_id] != scki) {
    		dev_dbg(priv->dev,
    			"%s configuration for %u Hz: %s, %dxFS (SCKI: %u Hz)\n",
    			audio_domain == J721E_AUDIO_DOMAIN_CPB ? "CPB" : "IVI",
    			rate,
    			clk_id == J721E_CLK_PARENT_48000 ? "PLL4" : "PLL15",
    			ratios_for_pcm3168a[i], scki);
    
    		if (domain->parent_clk_id != clk_id) {
    			ret = clk_set_parent(domain->codec.target,
    					     domain->codec.parent[clk_id]);
    			if (ret)
    				return ret;
    
    			ret = clk_set_parent(domain->mcasp.target,
    					     domain->mcasp.parent[clk_id]);
    			if (ret)
    				return ret;
    
    			domain->parent_clk_id = clk_id;
    		}
    
    		ret = clk_set_rate(domain->codec.target, scki);
    		if (ret) {
    			dev_err(priv->dev, "codec set rate failed for %u Hz\n",
    				scki);
    			return ret;
    		}
    
    		ret = clk_set_rate(domain->mcasp.target, scki);
    		if (!ret) {
    			priv->hsdiv_rates[domain->parent_clk_id] = scki;
    		} else {
    			dev_err(priv->dev, "mcasp set rate failed for %u Hz\n",
    				scki);
    			return ret;
    		}
    	}
    
    	return ret;
    }
    
    static int j721e_rule_rate(struct snd_pcm_hw_params *params,
    			   struct snd_pcm_hw_rule *rule)
    {
    	struct snd_interval *t = rule->private;
    
    	return snd_interval_refine(hw_param_interval(params, rule->var), t);
    }
    
    static int j721e_audio_startup(struct snd_pcm_substream *substream)
    {
    	struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
    	struct j721e_priv *priv = snd_soc_card_get_drvdata(rtd->card);
    	unsigned int domain_id = rtd->dai_link->id;
    	struct j721e_audio_domain *domain = &priv->audio_domains[domain_id];
    	struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0);
    	struct snd_soc_dai *codec_dai;
    	unsigned int active_rate;
    	int ret = 0;
    	int i;
    
    	mutex_lock(&priv->mutex);
    
    	domain->active++;
    
    	if (priv->audio_domains[J721E_AUDIO_DOMAIN_CPB].rate)
    		active_rate = priv->audio_domains[J721E_AUDIO_DOMAIN_CPB].rate;
    	else
    		active_rate = priv->audio_domains[J721E_AUDIO_DOMAIN_IVI].rate;
    
    	if (active_rate)
    		ret = snd_pcm_hw_constraint_single(substream->runtime,
    						   SNDRV_PCM_HW_PARAM_RATE,
    						   active_rate);
    	else
    		ret = snd_pcm_hw_rule_add(substream->runtime, 0,
    					  SNDRV_PCM_HW_PARAM_RATE,
    					  j721e_rule_rate, &priv->rate_range,
    					  SNDRV_PCM_HW_PARAM_RATE, -1);
    
    
    	if (ret)
    		goto out;
    
    	/* Reset TDM slots to 32 */
    	ret = snd_soc_dai_set_tdm_slot(cpu_dai, 0x3, 0x3, 2, 32);
    	if (ret && ret != -ENOTSUPP)
    		goto out;
    
    	for_each_rtd_codec_dais(rtd, i, codec_dai) {
    		ret = snd_soc_dai_set_tdm_slot(codec_dai, 0x3, 0x3, 2, 32);
    		if (ret && ret != -ENOTSUPP)
    			goto out;
    	}
    
    	if (ret == -ENOTSUPP)
    		ret = 0;
    out:
    	if (ret)
    		domain->active--;
    	mutex_unlock(&priv->mutex);
    
    	return ret;
    }
    
    static int j721e_audio_hw_params(struct snd_pcm_substream *substream,
    				 struct snd_pcm_hw_params *params)
    {
    	struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
    	struct snd_soc_card *card = rtd->card;
    	struct j721e_priv *priv = snd_soc_card_get_drvdata(card);
    	unsigned int domain_id = rtd->dai_link->id;
    	struct j721e_audio_domain *domain = &priv->audio_domains[domain_id];
    	struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0);
    	struct snd_soc_dai *codec_dai;
    	unsigned int sysclk_rate;
    	int slot_width = 32;
    	int ret;
    	int i;
    
    	mutex_lock(&priv->mutex);
    
    	if (domain->rate && domain->rate != params_rate(params)) {
    		ret = -EINVAL;
    		goto out;
    	}
    
    	if (params_width(params) == 16)
    		slot_width = 16;
    
    	ret = snd_soc_dai_set_tdm_slot(cpu_dai, 0x3, 0x3, 2, slot_width);
    	if (ret && ret != -ENOTSUPP)
    		goto out;
    
    	for_each_rtd_codec_dais(rtd, i, codec_dai) {
    		ret = snd_soc_dai_set_tdm_slot(codec_dai, 0x3, 0x3, 2,
    					       slot_width);
    		if (ret && ret != -ENOTSUPP)
    			goto out;
    	}
    
    	ret = j721e_configure_refclk(priv, domain_id, params_rate(params));
    	if (ret)
    		goto out;
    
    	sysclk_rate = priv->hsdiv_rates[domain->parent_clk_id];
    	for_each_rtd_codec_dais(rtd, i, codec_dai) {
    		ret = snd_soc_dai_set_sysclk(codec_dai, 0, sysclk_rate,
    					     SND_SOC_CLOCK_IN);
    		if (ret && ret != -ENOTSUPP) {
    			dev_err(priv->dev,
    				"codec set_sysclk failed for %u Hz\n",
    				sysclk_rate);
    			goto out;
    		}
    	}
    
    	ret = snd_soc_dai_set_sysclk(cpu_dai, MCASP_CLK_HCLK_AUXCLK,
    				     sysclk_rate, SND_SOC_CLOCK_IN);
    
    	if (ret && ret != -ENOTSUPP) {
    		dev_err(priv->dev, "mcasp set_sysclk failed for %u Hz\n",
    			sysclk_rate);
    	} else {
    		domain->rate = params_rate(params);
    		ret = 0;
    	}
    
    out:
    	mutex_unlock(&priv->mutex);
    	return ret;
    }
    
    static void j721e_audio_shutdown(struct snd_pcm_substream *substream)
    {
    	struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
    	struct j721e_priv *priv = snd_soc_card_get_drvdata(rtd->card);
    	unsigned int domain_id = rtd->dai_link->id;
    	struct j721e_audio_domain *domain = &priv->audio_domains[domain_id];
    
    	mutex_lock(&priv->mutex);
    
    	domain->active--;
    	if (!domain->active) {
    		domain->rate = 0;
    		domain->active_link = 0;
    	}
    
    	mutex_unlock(&priv->mutex);
    }
    
    static const struct snd_soc_ops j721e_audio_ops = {
    	.startup = j721e_audio_startup,
    	.hw_params = j721e_audio_hw_params,
    	.shutdown = j721e_audio_shutdown,
    };
    
    
    static int j721e_audio_init(struct snd_soc_pcm_runtime *rtd)
    {
    	struct j721e_priv *priv = snd_soc_card_get_drvdata(rtd->card);
    	unsigned int domain_id = rtd->dai_link->id;
    	struct j721e_audio_domain *domain = &priv->audio_domains[domain_id];
    	struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0);
    	struct snd_soc_dai *codec_dai;
    	unsigned int sysclk_rate;
    	int i, ret;
    
    	dev_info(priv->dev,"matrix sonud init 111\n");
    
    	/* Set up initial clock configuration */
    	// 配置时钟
    	ret = j721e_configure_refclk(priv, domain_id, 48000);
    	if (ret)
    		return ret;
    
    	dev_info(priv->dev,"matrix sonud init 222\n");
    
    	sysclk_rate = priv->hsdiv_rates[domain->parent_clk_id];
    	for_each_rtd_codec_dais(rtd, i, codec_dai) {
    		ret = snd_soc_dai_set_sysclk(codec_dai, 0, sysclk_rate,
    					     SND_SOC_CLOCK_IN);
    		if (ret && ret != -ENOTSUPP)
    			return ret;
    	}
    
    	dev_info(priv->dev,"matrix sonud init 333\n");
    
    	ret = snd_soc_dai_set_sysclk(cpu_dai, MCASP_CLK_HCLK_AUXCLK,
    				     sysclk_rate, SND_SOC_CLOCK_IN);
    	if (ret && ret != -ENOTSUPP)
    		return ret;
    
    	/* Set initial tdm slots */
    	ret = snd_soc_dai_set_tdm_slot(cpu_dai, 0x3, 0x3, 2, 32);
    	if (ret && ret != -ENOTSUPP)
    		return ret;
    
    	for_each_rtd_codec_dais(rtd, i, codec_dai) {
    		ret = snd_soc_dai_set_tdm_slot(codec_dai, 0x3, 0x3, 2, 32);
    		if (ret && ret != -ENOTSUPP)
    			return ret;
    	}
    
    	dev_info(priv->dev,"matrix sonud init 444\n");
    
    	return 0;
    }
    
    // static int j721e_audio_init_ivi(struct snd_soc_pcm_runtime *rtd)
    // {
    // 	struct snd_soc_dapm_context *dapm = &rtd->card->dapm;
    
    // 	snd_soc_dapm_new_controls(dapm, j721e_ivi_codec_a_dapm_widgets,
    // 				  ARRAY_SIZE(j721e_ivi_codec_a_dapm_widgets));
    // 	snd_soc_dapm_add_routes(dapm, j721e_codec_a_dapm_routes,
    // 				ARRAY_SIZE(j721e_codec_a_dapm_routes));
    // 	snd_soc_dapm_new_controls(dapm, j721e_ivi_codec_b_dapm_widgets,
    // 				  ARRAY_SIZE(j721e_ivi_codec_b_dapm_widgets));
    // 	snd_soc_dapm_add_routes(dapm, j721e_codec_b_dapm_routes,
    // 				ARRAY_SIZE(j721e_codec_b_dapm_routes));
    
    // 	return j721e_audio_init(rtd);
    // }
    
    static int j721e_get_clocks(struct device *dev,
    			    struct j721e_audio_clocks *clocks, char *prefix)
    {
    	struct clk *parent;
    	char *clk_name;
    	int ret;
    
    	clocks->target = devm_clk_get(dev, prefix);
    	if (IS_ERR(clocks->target)) {
    		ret = PTR_ERR(clocks->target);
    		if (ret != -EPROBE_DEFER)
    			dev_err(dev, "failed to acquire %s: %d\n",
    				prefix, ret);
    		return ret;
    	}
    
    	clk_name = kasprintf(GFP_KERNEL, "%s-48000", prefix);
    	if (clk_name) {
    		parent = devm_clk_get(dev, clk_name);
    		kfree(clk_name);
    		if (IS_ERR(parent)) {
    			ret = PTR_ERR(parent);
    			if (ret == -EPROBE_DEFER)
    				return ret;
    
    			dev_dbg(dev, "no 48KHz parent for %s: %d\n", prefix, ret);
    			parent = NULL;
    		}
    		clocks->parent[J721E_CLK_PARENT_48000] = parent;
    	} else {
    		return -ENOMEM;
    	}
    
    	clk_name = kasprintf(GFP_KERNEL, "%s-44100", prefix);
    	if (clk_name) {
    		parent = devm_clk_get(dev, clk_name);
    		kfree(clk_name);
    		if (IS_ERR(parent)) {
    			ret = PTR_ERR(parent);
    			if (ret == -EPROBE_DEFER)
    				return ret;
    
    			dev_dbg(dev, "no 44.1KHz parent for %s: %d\n", prefix, ret);
    			parent = NULL;
    		}
    		clocks->parent[J721E_CLK_PARENT_44100] = parent;
    	} else {
    		return -ENOMEM;
    	}
    
    	if (!clocks->parent[J721E_CLK_PARENT_44100] &&
    	    !clocks->parent[J721E_CLK_PARENT_48000]) {
    		dev_err(dev, "At least one parent clock is needed for %s\n",
    			prefix);
    		return -EINVAL;
    	}
    
    	return 0;
    }
    
    static const struct j721e_audio_match_data j721e_cpb_data = {
    	.board_type = J721E_BOARD_CPB,
    	.num_links = 1, 
    	.pll_rates = {
    		[J721E_CLK_PARENT_44100] = 1083801600, /* PLL15 */
    		[J721E_CLK_PARENT_48000] = 1179648000, /* PLL4 */
    	},
    };
    
    static const struct j721e_audio_match_data j721e_cpb_ivi_data = {
    	.board_type = J721E_BOARD_CPB_IVI,
    	.num_links = 4, /* CPB pcm3168a + 2x pcm3168a on IVI */
    	.pll_rates = {
    		[J721E_CLK_PARENT_44100] = 1083801600, /* PLL15 */
    		[J721E_CLK_PARENT_48000] = 1179648000, /* PLL4 */
    	},
    };
    
    static const struct j721e_audio_match_data j7200_cpb_data = {
    	.board_type = J721E_BOARD_CPB,
    	.num_links = 2, /* CPB pcm3168a */
    	.pll_rates = {
    		[J721E_CLK_PARENT_48000] = 2359296000u, /* PLL4 */
    	},
    };
    
    static const struct of_device_id j721e_audio_of_match[] = {
    	{
    		.compatible = "ti,j721e-cpb-audio",
    		.data = &j721e_cpb_data,
    	}, {
    		.compatible = "ti,j721e-cpb-ivi-audio",
    		.data = &j721e_cpb_ivi_data,
    	}, {
    		.compatible = "ti,j7200-cpb-audio",
    		.data = &j7200_cpb_data,
    	},
    	{ },
    };
    MODULE_DEVICE_TABLE(of, j721e_audio_of_match);
    
    static int j721e_calculate_rate_range(struct j721e_priv *priv)
    {
    	const struct j721e_audio_match_data *match_data = priv->match_data;
    	struct j721e_audio_clocks *domain_clocks;
    	unsigned int min_rate, max_rate, pll_rate;
    	struct clk *pll;
    
    	domain_clocks = &priv->audio_domains[J721E_AUDIO_DOMAIN_CPB].mcasp;
    
    	pll = clk_get_parent(domain_clocks->parent[J721E_CLK_PARENT_44100]);
    	if (IS_ERR_OR_NULL(pll)) {
    		priv->pll_rates[J721E_CLK_PARENT_44100] =
    				match_data->pll_rates[J721E_CLK_PARENT_44100];
    	} else {
    		priv->pll_rates[J721E_CLK_PARENT_44100] = clk_get_rate(pll);
    		clk_put(pll);
    	}
    
    	pll = clk_get_parent(domain_clocks->parent[J721E_CLK_PARENT_48000]);
    	if (IS_ERR_OR_NULL(pll)) {
    		priv->pll_rates[J721E_CLK_PARENT_48000] =
    				match_data->pll_rates[J721E_CLK_PARENT_48000];
    	} else {
    		priv->pll_rates[J721E_CLK_PARENT_48000] = clk_get_rate(pll);
    		clk_put(pll);
    	}
    
    	if (!priv->pll_rates[J721E_CLK_PARENT_44100] &&
    	    !priv->pll_rates[J721E_CLK_PARENT_48000]) {
    		dev_err(priv->dev, "At least one PLL is needed\n");
    		return -EINVAL;
    	}
    
    	if (priv->pll_rates[J721E_CLK_PARENT_44100])
    		pll_rate = priv->pll_rates[J721E_CLK_PARENT_44100];
    	else
    		pll_rate = priv->pll_rates[J721E_CLK_PARENT_48000];
    
    	min_rate = pll_rate / J721E_MAX_CLK_HSDIV;
    	min_rate /= ratios_for_pcm3168a[ARRAY_SIZE(ratios_for_pcm3168a) - 1];
    
    	if (priv->pll_rates[J721E_CLK_PARENT_48000])
    		pll_rate = priv->pll_rates[J721E_CLK_PARENT_48000];
    	else
    		pll_rate = priv->pll_rates[J721E_CLK_PARENT_44100];
    
    	if (pll_rate > PCM1368A_MAX_SYSCLK)
    		pll_rate = PCM1368A_MAX_SYSCLK;
    
    	max_rate = pll_rate / ratios_for_pcm3168a[0];
    
    	snd_interval_any(&priv->rate_range);
    	priv->rate_range.min = min_rate;
    	priv->rate_range.max = max_rate;
    
    	return 0;
    }
    
    static int j721e_soc_probe_cpb(struct j721e_priv *priv, int *link_idx,
    			       int *conf_idx)
    {
    	struct device_node *node = priv->dev->of_node;
    	struct snd_soc_dai_link_component *compnent;
    	struct device_node *dai_node, *codec_node;
    	struct j721e_audio_domain *domain;
    	int comp_count, comp_idx;
    	int ret;
    
    	dai_node = of_parse_phandle(node, "ti,cpb-mcasp", 0);
    	if (!dai_node) {
    		dev_err(priv->dev, "CPB McASP node is not provided\n");
    		return -EINVAL;
    	}
    
    	codec_node = of_parse_phandle(node, "ti,cpb-codec", 0);
    	if (!codec_node) {
    		dev_err(priv->dev, "CPB codec node is not provided\n");
    		ret = -EINVAL;
    		goto put_dai_node;
    	}
    
    	dev_info(priv->dev,"matrix sonud codec and dai 1 is successfully allocated\n");
    
    	domain = &priv->audio_domains[J721E_AUDIO_DOMAIN_CPB];
    
    	// 获取codec节点时钟配置
    	ret = j721e_get_clocks(priv->dev, &domain->codec, "cpb-codec-scki");
    	if (ret)
    		goto put_codec_node;
    
    	// 获取mcasp节点时钟配置
    	ret = j721e_get_clocks(priv->dev, &domain->mcasp, "cpb-mcasp-auxclk");
    	if (ret)
    		goto put_codec_node;
    
    	/*
    	 * Common Processor Board, two links
    	 * Link 1: McASP10 -> pcm3168a_1 DAC
    	 * Link 2: McASP10 <- pcm3168a_1 ADC
    	 */
    	
    	// 分配音频链路地址空间
    	comp_count = 6;
    	compnent = devm_kzalloc(priv->dev, comp_count * sizeof(*compnent),GFP_KERNEL);
    	if (!compnent) 
    	{
    		ret = -ENOMEM;
    		goto put_codec_node;
    	}
    
    	dev_info(priv->dev,"matrix sonud codec and dai 2 is successfully allocated\n");
    
    	comp_idx = 0;
    	priv->dai_links[*link_idx].cpus = &compnent[comp_idx++];
    	priv->dai_links[*link_idx].num_cpus = 1;
    	priv->dai_links[*link_idx].codecs = &compnent[comp_idx++];
    	priv->dai_links[*link_idx].num_codecs = 1;
    	priv->dai_links[*link_idx].platforms = &compnent[comp_idx++];
    	priv->dai_links[*link_idx].num_platforms = 1;
    
    	priv->dai_links[*link_idx].name = "tlv320aic3x play";
    	priv->dai_links[*link_idx].stream_name = "TLV320AIC3X";
    	priv->dai_links[*link_idx].cpus->of_node = dai_node;
    	priv->dai_links[*link_idx].platforms->of_node = dai_node;
    	priv->dai_links[*link_idx].codecs->of_node = codec_node;
    	priv->dai_links[*link_idx].codecs->dai_name = "tlv320aic3x-hifi";
    	// priv->dai_links[*link_idx].playback_only = 1;
    	// priv->dai_links[*link_idx].id = J721E_AUDIO_DOMAIN_CPB;
    	// priv->dai_links[*link_idx].dai_fmt = J721E_DAI_FMT;
    	priv->dai_links[*link_idx].init = j721e_audio_init;
    	priv->dai_links[*link_idx].ops = &j721e_audio_ops;
    	(*link_idx)++;
    
    	// priv->dai_links[*link_idx].cpus = &compnent[comp_idx++];
    	// priv->dai_links[*link_idx].num_cpus = 1;
    	// priv->dai_links[*link_idx].codecs = &compnent[comp_idx++];
    	// priv->dai_links[*link_idx].num_codecs = 1;
    	// priv->dai_links[*link_idx].platforms = &compnent[comp_idx++];
    	// priv->dai_links[*link_idx].num_platforms = 1;
    
    	// priv->dai_links[*link_idx].name = "tlv320aic3x Capture";
    	// priv->dai_links[*link_idx].stream_name = "TLV320AIC3X";
    	// priv->dai_links[*link_idx].cpus->of_node = dai_node;
    	// priv->dai_links[*link_idx].platforms->of_node = dai_node;
    	// priv->dai_links[*link_idx].codecs->of_node = codec_node;
    	// priv->dai_links[*link_idx].codecs->dai_name = "tlv320aic3x-hifi";
    	// priv->dai_links[*link_idx].capture_only = 1;
    	// priv->dai_links[*link_idx].id = J721E_AUDIO_DOMAIN_CPB;
    	// priv->dai_links[*link_idx].dai_fmt = J721E_DAI_FMT;
    	// priv->dai_links[*link_idx].init = j721e_audio_init;
    	// priv->dai_links[*link_idx].ops = &j721e_audio_ops;
    	// (*link_idx)++;
    
    	priv->codec_conf[*conf_idx].dlc.of_node = codec_node;
    	priv->codec_conf[*conf_idx].name_prefix = "codec-1";
    	(*conf_idx)++;
    	priv->codec_conf[*conf_idx].dlc.of_node = dai_node;
    	priv->codec_conf[*conf_idx].name_prefix = "McASP10";
    	(*conf_idx)++;
    
    	dev_info(priv->dev,"matrix sonud codec and dai 3 is successfully allocated\n");
    
    	return 0;
    
    put_codec_node:
    	of_node_put(codec_node);
    put_dai_node:
    	of_node_put(dai_node);
    	return ret;
    }
    
    static int j721e_soc_probe(struct platform_device *pdev)
    {
    	struct device_node *node = pdev->dev.of_node;
    	struct snd_soc_card *card;
    	const struct of_device_id *match;
    	struct j721e_priv *priv;
    	int link_cnt, conf_cnt, ret;
    	
    	if (!node) {
    		dev_err(&pdev->dev, "of node is missing.\n");
    		return -ENODEV;
    	}
    
    	match = of_match_node(j721e_audio_of_match, node);
    	if (!match) {
    		dev_err(&pdev->dev, "No compatible match found\n");
    		return -ENODEV;
    	}
    
    	priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
    	if (!priv)
    		return -ENOMEM;
    
    	priv->match_data = match->data;
    
    	priv->dai_links = devm_kcalloc(&pdev->dev, priv->match_data->num_links,
    				       sizeof(*priv->dai_links), GFP_KERNEL);
    	if (!priv->dai_links)
    		return -ENOMEM;
    
    	dev_info(&pdev->dev,"matrix dai_links is successfully allocated\n");
    
    	priv->audio_domains[J721E_AUDIO_DOMAIN_CPB].parent_clk_id = -1;
    	// priv->audio_domains[J721E_AUDIO_DOMAIN_IVI].parent_clk_id = -1;
    	priv->dev = &pdev->dev;
    	card = &priv->card;
    	card->dev = &pdev->dev;
    	card->owner = THIS_MODULE;
    	card->dapm_widgets = j721e_cpb_dapm_widgets;
    	card->num_dapm_widgets = ARRAY_SIZE(j721e_cpb_dapm_widgets);
    	card->dapm_routes = j721e_cpb_dapm_routes;
    	card->num_dapm_routes = ARRAY_SIZE(j721e_cpb_dapm_routes);
    	card->fully_routed = 1;
    	
    	// 提取设备树中的声卡名称(model)
    	if (snd_soc_of_parse_card_name(card, "model")) {
    		dev_err(&pdev->dev, "Card name is not provided\n");
    		return -ENODEV;
    	}
    
    	dev_info(&pdev->dev,"matrix sonud memory is successfully allocated\n");
    
    	link_cnt = 0;
    	conf_cnt = 0;
    	ret = j721e_soc_probe_cpb(priv, &link_cnt, &conf_cnt);
    	if (ret)
    		return ret;
    
    	// ret = j721e_soc_probe_ivi(priv, &link_cnt, &conf_cnt);
    	// if (ret)
    	// 	return ret;
    
    	card->dai_link = priv->dai_links;
    	card->num_links = link_cnt;
    
    	card->codec_conf = priv->codec_conf;
    	card->num_configs = conf_cnt;
    
    	ret = j721e_calculate_rate_range(priv);
    	if (ret)
    		return ret;
    
    	snd_soc_card_set_drvdata(card, priv);
    
    	mutex_init(&priv->mutex);
    	ret = devm_snd_soc_register_card(&pdev->dev, card);
    	if (ret)
    		dev_err(&pdev->dev, "devm_snd_soc_register_card() failed: %d\n",
    			ret);
    
    	return ret;
    }
    
    static struct platform_driver j721e_soc_driver = {
    	.driver = {
    		.name = "j721e-audio",
    		.pm = &snd_soc_pm_ops,
    		.of_match_table = j721e_audio_of_match,
    	},
    	.probe = j721e_soc_probe,
    };
    
    module_platform_driver(j721e_soc_driver);
    
    MODULE_AUTHOR("Peter Ujfalusi <peter.ujfalusi@ti.com>");
    MODULE_DESCRIPTION("ASoC machine driver for j721e Common Processor Board");
    MODULE_LICENSE("GPL v2");
    
    麻烦您再转发问一下,谢谢
  • 我将原pcm3168a的驱动替换为了simple-audio-card节点,在更换后,我的设备树如下

    sound0: sound@0 {
    	compatible = "simple-audio-card";
    	simple-audio-card,name = "tlv320aic3x-Codec";
    
    	simple-audio-card,widgets =
    		"Line", "Line Out",
    		"Line", "Line In",
    		"Headphone", "Headphone Jack",
    		"Speaker", "speaker";
    	simple-audio-card,routing =
    		"Line Out", "HPLCOM",
    		"Line Out", "HPRCOM",
    		"LINE1L", "Line In",
    		"LINE1R", "Line In",
    		"Headphone Jack", "HPLOUT",
    		"Headphone Jack", "HPROUT",
    		"speaker", "LLOUT",
    		"speaker", "RLOUT";
    	simple-audio-card,format = "i2s";
    	simple-audio-card,bitclock-master = <&cpudai1>;
    	// simple-audio-card,bitclock-slave = <&sound_master>;
    	simple-audio-card,frame-master = <&cpudai1>;
    	// simple-audio-card,frame-slave = <&sound_master>;
    	// simple-audio-card,bitclock-inversion = BITCLOCK_NORMAL;
    
    	cpudai1: simple-audio-card,cpu {
    		sound-dai = <&mcasp10>;
    		clocks = <&k3_clks 184 2>;
    	};
    
    	sound_master:simple-audio-card,codec {
    		sound-dai = <&tlv320aic3109>;
    		system-clock-frequency = <24000000>;
    	};
    };
    	
    &mcasp10 {
    	#sound-dai-cells = <0>;
    
    	pinctrl-names = "default";
    	pinctrl-0 = <&mcasp10_pins_default>;
    	status = "okay";
    
    	op-mode = <0>;          /* MCASP_IIS_MODE */
    	tdm-slots = <2>;
    
    	serial-dir = <	/* 0: INACTIVE, 1: TX, 2: RX */
    		1 2 0 0
    	>;
    	tx-num-evt = <32>;
    	rx-num-evt = <32>;
    };
    
    tlv320aic3109: audio-codec@18 {
    	compatible = "ti,tlv320aic3x";
    	reg = <0x18>;
    	#sound-dai-cells = <0>;
    
    	reset-gpios = <&main_gpio0 111 GPIO_ACTIVE_HIGH>;
    
    	ai3x-ocmv = <1>;
    
    	AVDD-supply = <&vsys_3v3>;
    	IOVDD-supply = <&vsys_3v3>;
    	DRVDD-supply = <&vsys_3v3>;
    	DVDD-supply = <&vsys_3v3>;
    };
    

    启动后声卡被成功挂载,但是在播放声音时出现了
    davinci-mcasp 2ba0000.mcasp: Too fast reference clock (196608000)

    的问题,目前我们把问题聚焦在了mcasp或音频解码器的时钟源上面,我查阅了大量的帖子,其中关于如何设置mcasp时钟源说法五花八门,请问我新加的DTS树是否是正确的。