[Linux Audio Driver] 高通TDM总线配置

0. 背景

TDM接口与平台SOC相关,调试前需要确定平台支持TDM,目前一些中高端的平台都支持的。(转载请备注链接)

本文介绍配置: SEN_TDM_TX_0。

1. tinymix查看当前虚拟总线配置

adb shell tinymix >  tinymix_source.txt

RX_CDC_DMA_RX_0这个虚拟总线是一直用的,这个显然存在,然后检索TDM。

Line 248: 243	BOOL	1	RX_CDC_DMA_RX_0_DL_HL Switch             Off
Line 471: 466	BOOL	2	RX_CDC_DMA_RX_0 Audio Mixer MultiMedia1  Off Off
Line 472: 467	BOOL	2	RX_CDC_DMA_RX_0 Audio Mixer MultiMedia2  Off Off
Line 473: 468	BOOL	2	RX_CDC_DMA_RX_0 Audio Mixer MultiMedia3  Off Off
Line 474: 469	BOOL	2	RX_CDC_DMA_RX_0 Audio Mixer MultiMedia4  Off Off
Line 475: 470	BOOL	2	RX_CDC_DMA_RX_0 Audio Mixer MultiMedia5  Off Off
Line 476: 471	BOOL	2	RX_CDC_DMA_RX_0 Audio Mixer MultiMedia6  Off Off
Line 477: 472	BOOL	2	RX_CDC_DMA_RX_0 Audio Mixer MultiMedia7  Off Off
Line 478: 473	BOOL	2	RX_CDC_DMA_RX_0 Audio Mixer MultiMedia8  Off Off
Line 479: 474	BOOL	2	RX_CDC_DMA_RX_0 Audio Mixer MultiMedia9  Off Off
Line 480: 475	BOOL	2	RX_CDC_DMA_RX_0 Audio Mixer MultiMedia10 Off Off
Line 481: 476	BOOL	2	RX_CDC_DMA_RX_0 Audio Mixer MultiMedia11 Off Off
Line 482: 477	BOOL	2	RX_CDC_DMA_RX_0 Audio Mixer MultiMedia12 Off Off
Line 483: 478	BOOL	2	RX_CDC_DMA_RX_0 Audio Mixer MultiMedia13 Off Off
Line 484: 479	BOOL	2	RX_CDC_DMA_RX_0 Audio Mixer MultiMedia14 Off Off
Line 485: 480	BOOL	2	RX_CDC_DMA_RX_0 Audio Mixer MultiMedia15 Off Off
Line 486: 481	BOOL	2	RX_CDC_DMA_RX_0 Audio Mixer MultiMedia16 Off Off
Line 487: 482	BOOL	2	RX_CDC_DMA_RX_0 Audio Mixer MultiMedia26 Off Off
Line 1024: 1019	BOOL	2	RX_CDC_DMA_RX_0_Voice Mixer Voip         Off Off
Line 1025: 1020	BOOL	2	RX_CDC_DMA_RX_0_Voice Mixer VoiceMMode1  Off Off
Line 1026: 1021	BOOL	2	RX_CDC_DMA_RX_0_Voice Mixer VoiceMMode2  Off Off
Line 1204: 1199	BOOL	2	RX_CDC_DMA_RX_0 Port Mixer TX_CDC_DMA_TX_3 Off Off
Line 1205: 1200	BOOL	2	RX_CDC_DMA_RX_0 Port Mixer SLIM_8_TX     Off Off
Line 1607: 1602	ENUM	1	RX_CDC_DMA_RX_0 Format                   S16_LE
Line 1612: 1607	ENUM	1	RX_CDC_DMA_RX_0 SampleRate               KHZ_48
Line 2544: 2539	ENUM	1	RX_CDC_DMA_RX_0 Channels                 One

没有找到上面类似的控件,下面这个TDM log不晓得干嘛的,QUIN_TDM_TX_0是我不需要的,由于硬件设计,目标是要配置SEN_TDM。

Line 1321: 1316	ENUM	1	TERT_TDM_TX_0 LSM Function               SWAUDIO
Line 1595: A_TX_3     1583	BYTE	376	Source Tracking Audio Tx TX_CDC_DMA_TX_3
Line 1595: A_TX_3     1583	BYTE	376	Source Tracking Audio Tx TX_CDC_DMA_TX_3
Line 1595: A_TX_3     1583	BYTE	376	Source Tracking Audio Tx TX_CDC_DMA_TX_3
Line 1595: A_TX_3     1583	BYTE	376	Source Tracking Audio Tx TX_CDC_DMA_TX_3
Line 1595: A_TX_3     1583	BYTE	376	Source Tracking Audio Tx TX_CDC_DMA_TX_3
Line 1595: A_TX_3     1583	BYTE	376	Source Tracking Audio Tx TX_CDC_DMA_TX_3

1584	BYTE	26	Sound Focus Voice Tx QUIN_TDM_TX_0             
1584	BYTE	26	Sound Focus Voice Tx QUIN_TDM_TX_0             
1584	BYTE	26	Sound Focus Voice Tx QUIN_TDM_TX_0             
1584	BYTE	26	Sound Focus Voice Tx QUIN_TDM_TX_0             
1584	BYTE	26	Sound Focus Voice Tx QUIN_TDM_TX_0             
1584	BYTE	26	Sound Focus Voice Tx QUIN_TDM_TX_0       

1589	BYTE	376	Source Tracking Audio Tx PRIMARY_TDM     1590	
1589	BYTE	376	Source Tracking Audio Tx PRIMARY_TDM     1590	
1589	BYTE	376	Source Tracking Audio Tx PRIMARY_TDM     1590	
1589	BYTE	376	Source Tracking Audio Tx PRIMARY_TDM     1590	
1589	BYTE	376	Source Tracking Audio Tx PRIMARY_TDM     1590	

ENUM	1	SLIM7_RX ADM Channels                    Zero
ENUM	1	SLIM7_RX ADM Channels                    Zero
ENUM	1	SLIM7_RX ADM Channels                    Zero
ENUM	1	SLIM7_RX ADM Channels                    Zero
ENUM	1	SLIM7_RX ADM Channels                    Zero

从当前的情况来看,应该是要修改一些设备树配置,或者代码里面的一些宏定义没有打开。于是进入下一步,在machine driver里面加log。

2. 加log调试分析

./vendor/qcom/opensource/audio-kernel/asoc/kona.c

在machine driver里面检索TDM,发现了大量的结构体以及宏定义,于是准备在所有TDM的调用代码里面加log分析。
(其实后面都没分析log,因为在加log的过程中就把代码看的差不多了-_-。)

Line 79: #define TDM_CHANNEL_MAX		8
Line 108: 	TDM_0 = 0,
Line 109: 	TDM_1,
Line 110: 	TDM_2,
Line 111: 	TDM_3,
Line 112: 	TDM_4,
Line 113: 	TDM_5,
Line 114: 	TDM_6,
Line 115: 	TDM_7,
Line 116: 	TDM_PORT_MAX,
Line 119: #define TDM_MAX_SLOTS 8
Line 120: #define TDM_SLOT_WIDTH_BITS 32
Line 121: #define TDM_SLOT_WIDTH_BYTES TDM_SLOT_WIDTH_BITS/8
Line 121: #define TDM_SLOT_WIDTH_BYTES TDM_SLOT_WIDTH_BITS/8
Line 124: 	TDM_PRI = 0,
Line 125: 	TDM_SEC,
Line 126: 	TDM_TERT,
Line 127: 	TDM_QUAT,
Line 128: 	TDM_QUIN,
Line 129: 	TDM_SEN,
Line 130: 	TDM_INTERFACE_MAX,
Line 212: 	u32 tdm_max_slots; /* Max TDM slots used */
Line 212: 	u32 tdm_max_slots; /* Max TDM slots used */
Line 217: struct tdm_port {
Line 222: struct tdm_dev_config {
Line 223: 	unsigned int tdm_slot_offset[TDM_MAX_SLOTS];
Line 223: 	unsigned int tdm_slot_offset[TDM_MAX_SLOTS];
Line 347: /* Default configuration of TDM channels */
Line 348: static struct dev_config tdm_rx_cfg[TDM_INTERFACE_MAX][TDM_PORT_MAX] = {
Line 348: static struct dev_config tdm_rx_cfg[TDM_INTERFACE_MAX][TDM_PORT_MAX] = {
Line 348: static struct dev_config tdm_rx_cfg[TDM_INTERFACE_MAX][TDM_PORT_MAX] = {
Line 349: 	{ /* PRI TDM */
Line 359: 	{ /* SEC TDM */
Line 369: 	{ /* TERT TDM */
Line 379: 	{ /* QUAT TDM */
Line 389: 	{ /* QUIN TDM */
Line 399: 	{ /* SEN TDM */
Line 411: static struct dev_config tdm_tx_cfg[TDM_INTERFACE_MAX][TDM_PORT_MAX] = {
Line 411: static struct dev_config tdm_tx_cfg[TDM_INTERFACE_MAX][TDM_PORT_MAX] = {
Line 411: static struct dev_config tdm_tx_cfg[TDM_INTERFACE_MAX][TDM_PORT_MAX] = {

就像下面这样,相关函数都加上log。

static int tdm_rx_sample_rate_put(struct snd_kcontrol *kcontrol,
				   struct snd_ctl_elem_value *ucontrol)
{
	struct tdm_port port;
	int ret = tdm_get_port_idx(kcontrol, &port);
	+ pr_err("#nigulasi#,debug,fun=%s,ret=%d",__func__,ret);
	if (ret) {
		pr_err("%s: unsupported control: %s\n",
			__func__, kcontrol->id.name);
	} else {
		tdm_rx_cfg[port.mode][port.channel].sample_rate =
			tdm_get_sample_rate(ucontrol->value.enumerated.item[0]);

		pr_debug("%s: tdm_rx_sample_rate = %d, item = %d\n", __func__,
			 tdm_rx_cfg[port.mode][port.channel].sample_rate,
			 ucontrol->value.enumerated.item[0]);
	}
	return ret;
}

在加log的时候,发现几个地方可能是重点:

static int msm_asoc_machine_probe(struct platform_device *pdev)
    ...
	ret = of_property_read_u32(pdev->dev.of_node, "qcom,tdm-max-slots",
				   &pdata->tdm_max_slots);

	pr_err("#nigulasi#,debug,fun=%s,ret=%d",__func__,ret);
	if (ret) {
		dev_err(&pdev->dev, "%s: No DT match for tdm max slots\n",
				__func__);
	}
		if ((pdata->tdm_max_slots <= 0) || (pdata->tdm_max_slots >
	     TDM_MAX_SLOTS)) {
		pdata->tdm_max_slots = TDM_MAX_SLOTS;
		dev_err(&pdev->dev, "%s: Using default tdm max slot: %d\n",
				__func__, pdata->tdm_max_slots);
	}
#define TDM_MAX_SLOTS 8

qcom,tdm-max-slots这个设备树属性要解析放在pdata->tdm_max_slots里面,代码里面还有控制,超出范围强制配置为8,这个后面看了下参考链接1,意思应该是LRCK/WS包含的声道数,这个就是同时包含8声道数据。

static struct snd_soc_card *populate_snd_card_dailinks(struct device *dev)
...

		rc = of_property_read_u32(dev->of_node, "qcom,tdm-audio-intf",
				&val);
				
		pr_err("#nigulasi#,debug,fun=%s,rc=%d,val=%d",__func__,rc,val);

		if (!rc && val) {
			memcpy(msm_kona_dai_links + total_links,
				msm_tdm_be_dai_links,
				sizeof(msm_tdm_be_dai_links));
			total_links +=
				ARRAY_SIZE(msm_tdm_be_dai_links);
		}
static struct snd_soc_dai_link msm_tdm_be_dai_links[] = {
	{
		.name = LPASS_BE_PRI_TDM_RX_0,
		.stream_name = "Primary TDM0 Playback",
		.cpu_dai_name = "msm-dai-q6-tdm.36864",
		.platform_name = "msm-pcm-routing",
		.codec_name = "msm-stub-codec.1",
		.codec_dai_name = "msm-stub-rx",
		.no_pcm = 1,
		.dpcm_playback = 1,
		.id = MSM_BACKEND_DAI_PRI_TDM_RX_0,
		.be_hw_params_fixup = msm_be_hw_params_fixup,
		.ops = &kona_tdm_be_ops,
		.ignore_suspend = 1,
		.ignore_pmdown_time = 1,
	},
	....

这个地方来看qcom,tdm-audio-intf的值必须为1(true),原因是结构体数组msm_tdm_be_dai_links里面全是TDM相关的东西,

于是在对应的音频设备树里面加上两个修改:

./vendor/qcom/proprietary/devicetree-4.19/qcom/lagoon-audio.dtsi
&q6core {
	lagoon_snd: sound {
		compatible = "qcom,kona-asoc-snd";
       ......
		clock-names = "lpass_audio_hw_vote";
		clocks = <&lpass_audio_hw_vote 0>;
		+ qcom,tdm-audio-intf = <1>;//config TDM 
		+ qcom,tdm-max-slots = <8>;

还有个宏,

#ifndef CONFIG_TDM_DISABLE
static void msm_add_tdm_snd_controls(struct snd_soc_component *component)
{
	pr_err("#nigulasi#,debug,fun=%s",__func__);
	snd_soc_add_component_controls(component, msm_tdm_snd_controls,
				ARRAY_SIZE(msm_tdm_snd_controls));
}
#else
static void msm_add_tdm_snd_controls(struct snd_soc_component *component)
{
	pr_err("#nigulasi#,debug,fun=%s",__func__);
	return;
}
#endif

#ifndef CONFIG_MI2S_DISABLE
static void msm_add_mi2s_snd_controls(struct snd_soc_component *component)
{
	snd_soc_add_component_controls(component, msm_mi2s_snd_controls,
				ARRAY_SIZE(msm_mi2s_snd_controls));
}
#else
static void msm_add_mi2s_snd_controls(struct snd_soc_component *component)
{
	return;
}
#endif

这里说如果没定义CONFIG_TDM_DISABLE,那么就走下面这个函数,然后就走空函数,我们需要TDM功能,那么得把这个宏定义给干掉,

grep  -nr "CONFIG_TDM_DISABLE"  ./vendor/

vim ./vendor/qcom/opensource/audio-kernel/config/litoautoconf.h
//#define CONFIG_TDM_DISABLE 1

3. 编译报错与继续分析

/kernel/msm-4.19/../../vendor/qcom/opensource/audio-kernel/asoc/msm-pcm-routing-v2.c:25533:5: 

error: use of undeclared identifier 'sen_mi2s_rx_voice_mixer_controls'; 
did you mean 'slimbus_rx_voice_mixer_controls'?
 sen_mi2s_rx_voice_mixer_controls,

这个报错我有点懵逼, 它说用了没定义的sen_mi2s_rx_voice_mixer_controls,找到报错的地方,它被宏:
CONFIG_MI2S_DISABLE包含了,

#ifndef CONFIG_MI2S_DISABLE
...
static const struct snd_kcontrol_new sen_mi2s_rx_voice_mixer_controls[] = {
	SOC_DOUBLE_EXT("Voip", SND_SOC_NOPM,
	MSM_BACKEND_DAI_SENARY_MI2S_RX,
	MSM_FRONTEND_DAI_VOIP, 1, 0, msm_routing_get_voice_mixer,
	msm_routing_put_voice_mixer),
	SOC_DOUBLE_EXT("DTMF", SND_SOC_NOPM,
	MSM_BACKEND_DAI_SENARY_MI2S_RX,
	MSM_FRONTEND_DAI_DTMF_RX, 1, 0, msm_routing_get_voice_mixer,
	msm_routing_put_voice_mixer),
	SOC_DOUBLE_EXT("QCHAT", SND_SOC_NOPM,
	MSM_BACKEND_DAI_SENARY_MI2S_RX,
	MSM_FRONTEND_DAI_QCHAT, 1, 0, msm_routing_get_voice_mixer,
	msm_routing_put_voice_mixer),
	SOC_DOUBLE_EXT("VoiceMMode1", SND_SOC_NOPM,
	MSM_BACKEND_DAI_SENARY_MI2S_RX,
	MSM_FRONTEND_DAI_VOICEMMODE1, 1, 0, msm_routing_get_voice_mixer,
	msm_routing_put_voice_mixer),
	SOC_DOUBLE_EXT("VoiceMMode2", SND_SOC_NOPM,
	MSM_BACKEND_DAI_SENARY_MI2S_RX,
	MSM_FRONTEND_DAI_VOICEMMODE2, 1, 0, msm_routing_get_voice_mixer,
	msm_routing_put_voice_mixer),
};

在这里插入图片描述
这个宏就在我注释掉CONFIG_TDM_DISABLE的前面,默认就是打开的,这里没毛病,配置TDM嘛,就关掉MI2S。

也就是说这个结构体数组的原型被宏注释掉了,那直接把调用的函数干掉即可:

//	SND_SOC_DAPM_MIXER("SEN_MI2S_RX_Voice Mixer",
//				SND_SOC_NOPM, 0, 0,
//				sen_mi2s_rx_voice_mixer_controls,
//				ARRAY_SIZE(sen_mi2s_rx_voice_mixer_controls)),

编译好了之后,烧录vendor.img,把tinymix控件都打印出来。

adb shell tinymix  >  tinymix.txt

检索TDM,好家伙匹配了1920次,SEN_TDM_TX_0这个虚拟总线是我们需要的。但是这里还不算完,我们总要看看TDM波形才能说配置好了吧。

在这里插入图片描述

4. 抓TDM波形

比如我们要配置一个12.288MHz,BCLK的TDM总线, WS/LRCK一般都是配置48Khz,位深度是32 bit,然后有8个声道,计算起来SCLK/BCLK就是12.288MHz。

 12.288 MHz =  48 kHz * 32 bits per slot * 8 slots/channels
./vendor/qcom/proprietary/devicetree-4.19/qcom/lagoon-audio-overlay.dtsi

然后我们修改SEN总线相关的GPIO配置,这里LPI对应10、11、12、13;正常GPIO号对应137、138、139、140;其实他们的本质是一致的。

lpi_i2s2_clk – LPI_GPIO_10
lpi_i2s2_ws – LPI_GPIO_11
lpi_i2s2_data0 – LPI_GPIO_12
lpi_i2s2_data1 – LPI_GPIO_13
lpi_i2s2_clk – GPIO_137
lpi_i2s2_ws – GPIO_138
lpi_i2s2_data0 – GPIO_139
lpi_i2s2_data1 – GPIO_140

代码里面发现sen的几个gpio在如下位置被解析。

./vendor/qcom/opensource/audio-kernel/asoc/kona.c
static int msm_asoc_machine_probe(struct platform_device *pdev)
...

pdata->mi2s_gpio_p[SEN_MI2S] = of_parse_phandle(pdev->dev.of_node,
					"qcom,sen-mi2s-gpios", 0);

于是对设备树增加修改如下(就是把占用gpio的干掉,其他抄相邻的格式修改):

        qcom,cdc-dmic23-gpios = <&cdc_dmic23_gpios>;
-       qcom,cdc-dmic45-gpios = <&cdc_dmic45_gpios>;
+       //qcom,cdc-dmic45-gpios = <&cdc_dmic45_gpios>;
+       qcom,sen-mi2s-gpios = <&lpi_tdm2_gpios>;
        asoc-codec  = <&stub_codec>, <&bolero>;
        asoc-codec-names = "msm-stub-codec.1", "bolero_codec";
        qcom,wsa-max-devs = <0>;
@@ -364,6 +365,7 @@
        };

        cdc_dmic45_gpios: cdc_dmic45_pinctrl {
+               status = "disabled";
                compatible = "qcom,msm-cdc-pinctrl";
                pinctrl-names = "aud_active", "aud_sleep";
                pinctrl-0 = <&cdc_dmic45_clk_active &cdc_dmic45_data_active>;
@@ -372,6 +374,16 @@
                qcom,tlmm-gpio = <139 140>;
        };

+       lpi_tdm2_gpios: lpi_tdm2_pinctrl {
+               status = "ok";
+               compatible = "qcom,msm-cdc-pinctrl";
+               pinctrl-names = "aud_active", "aud_sleep";
+               pinctrl-0 = <&lpi_tdm2_sck_active &lpi_tdm2_ws_active 
+                  &lpi_tdm2_sd0_active &lpi_tdm2_sd1_active>;
+               pinctrl-1 = <&lpi_tdm2_sck_sleep &lpi_tdm2_ws_sleep 
+                  &lpi_tdm2_sd0_sleep &lpi_tdm2_sd1_sleep>;
+               qcom,lpi-gpios;
+               qcom,tlmm-gpio = <137 138 139 140>;
+       };

改了之后用示波器抓波形,然后执行以下tinyalsa命令使得总线工作:

tinymix "MultiMedia1 Mixer SEN_TDM_TX_0" "1"

WS以及LRCK都是稳稳的一条直线,木得一点反应。。。

在这里插入图片描述
之后反应过来,要让他起个进程才能让总线一直工作,所以这回除了配置AFE,还加上了tinycap命令。

tinymix "MultiMedia1 Mixer SEN_TDM_TX_0" "1"
tinycap /sdcard/test.wav

在这里插入图片描述
然后就有波形了,WS(48Khz),BCLK(12.288Mhz)

ws从高通文档里面看到三种模式:

./vendor/qcom/proprietary/devicetree-4.19/qcom/msm-audio-lpass.dtsi
AFE_PORT_TDM_SHORT_SYNC_BIT_MODE 0
AFE_PORT_TDM_LONG_SYNC_MODE 1
AFE_PORT_TDM_SHORT_SYNC_MODE 2
qcom,msm-cpudai-tdm-sync-mode = <1>;

代码默认为1就是上面那种方波,

在这里插入图片描述

如果改成0,就是这种脉冲波形,为2不晓得,当初没抓波形-_-,(后来板子被烧了也没法抓了)。

TDM总线配置出来并不是终点,从这里就看出来芯片的WS就至少有两种,这个应该要和调试的外部CODEC 芯片相匹配,需要和FAE沟通。

5. 参考链接

  1. https://www.cnblogs.com/fellow1988/p/11055680.html
评论 13
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值