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. 参考链接
- https://www.cnblogs.com/fellow1988/p/11055680.html