1. TDM硬件接口介绍:
I2S只能传2个声道的数据,PCM可以传多达16路数据,采用时分复用的方式,就是TDM。
TDM不像I2S有统一的标准,不同的IC厂商在应用TDM时可能略有差异,这些差异表现在时钟的极性、声道配置的触发条件和对闲置声道的处理等。
特点:
- 比I2S节省管脚数量
- 支持多路传输
- 最多支持16路=16通道
TDM系统框图:
2. Kernel Dtsi 配置:
参考:Documentation/devicetree/bindings/sound/qcom-audio-dev.txt
sa8155-audio.dtsi :
/*TDM Pri组RX端口组配置*/
tdm_pri_rx: qcom,msm-dai-tdm-pri-rx {
/*匹配节点,驱动和设备的compatible属性相同时,才会调用probe函数。*/
compatible = "qcom,msm-dai-tdm";
/*TDM 群组ID:
支持列表如下:
Primary RX: 37120
Primary TX: 37121
Secondary RX: 37136
Secondary TX: 37137
Tertiary RX: 37152
Tertiary TX: 37153
Quaternary RX: 37168
Quaternary TX: 37169
*/
qcom,msm-cpudai-tdm-group-id = <37120>;
/*端口数,最大支持端口数量为8*/
qcom,msm-cpudai-tdm-group-num-ports = <4>;
/*TDM端口ID数组,数组的大小由msm-cpudai-tdm-group-num-ports 中的值决定。每组最多支持 8 个端口:
Primary RX: 36864, 36866, 36868, 36870,
36872, 36874, 36876, 36878
Primary TX: 36865, 36867, 36869, 36871,
36873, 36875, 36877, 36879
Secondary RX: 36880, 36882, 36884, 36886,
36888, 36890, 36892, 36894
Secondary TX: 36881, 36883, 36885, 36887,
36889, 36891, 36893, 36895
Tertiary RX: 36896, 36898, 36900, 36902,
36904, 36906, 36908, 36910
Tertiary TX: 36897, 36899, 36901, 36903,
36905, 36907, 36909, 36911
Quaternary RX: 36912, 36914, 36916, 36918,
36920, 36922, 36924, 36926
Quaternary TX: 36913, 36915, 36917, 36919,
36921, 36923, 36925, 36927
*/
qcom,msm-cpudai-tdm-group-port-id = <36864 36866 36868 36870>;
/*TDM时钟速率,设置为0说明使用外部时钟*/
qcom,msm-cpudai-tdm-clk-rate = <12288000>;
/*时钟源。
0 - EBIT 时钟
1 - IBIT 时钟
*/
qcom,msm-cpudai-tdm-clk-internal = <1>;
/*同步设置。
0 - 短同步位模式
1 - 长同步模式
2 - 短同步槽模式
*/
qcom,msm-cpudai-tdm-sync-mode = <0>;
/*同步源。
0 - 外部源
1 - 内部来源
*/
qcom,msm-cpudai-tdm-sync-src = <1>;
/*其他主机驱动的数据输出。
0 - 禁用
1 - 启用
*/
qcom,msm-cpudai-tdm-data-out = <0>;
/*反转同步。
0 - 正常
1 - 反转
*/
qcom,msm-cpudai-tdm-invert-sync = <0>;
/*相对于同步边沿延迟数据的位时钟数。
0 - 0 位时钟周期
1 - 1 位时钟周期
2 - 2 位时钟周期
*/
qcom,msm-cpudai-tdm-data-delay = <1>;
/*TDM CLK 属性配置*/
qcom,msm-cpudai-tdm-clk-attribute = /bits/ 16 <1>;
/*TDM RX端口配置,几路端口由msm-cpudai-tdm-group-num-ports 中的值决定
msm-cpudai-tdm-dev-id 由 msm-cpudai-tdm-group-port-id 端口ID数组决定
*/
dai_pri_tdm_rx_0: qcom,msm-dai-q6-tdm-pri-rx-0 {
compatible = "qcom,msm-dai-q6-tdm";
qcom,msm-cpudai-tdm-dev-id = <36864>;
qcom,msm-cpudai-tdm-data-align = <0>;
};
dai_pri_tdm_rx_1: qcom,msm-dai-q6-tdm-pri-rx-1 {
compatible = "qcom,msm-dai-q6-tdm";
qcom,msm-cpudai-tdm-dev-id = <36866>;
qcom,msm-cpudai-tdm-data-align = <0>;
};
dai_pri_tdm_rx_2: qcom,msm-dai-q6-tdm-pri-rx-2 {
compatible = "qcom,msm-dai-q6-tdm";
qcom,msm-cpudai-tdm-dev-id = <36868>;
qcom,msm-cpudai-tdm-data-align = <0>;
};
dai_pri_tdm_rx_3: qcom,msm-dai-q6-tdm-pri-rx-3 {
compatible = "qcom,msm-dai-q6-tdm";
qcom,msm-cpudai-tdm-dev-id = <36870>;
qcom,msm-cpudai-tdm-data-align = <0>;
};
};
/*TDM Pri组TX端口组配置*/
tdm_pri_tx: qcom,msm-dai-tdm-pri-tx {
/*匹配节点,驱动和设备的compatible属性相同时,才会调用probe函数。*/
compatible = "qcom,msm-dai-tdm";
qcom,msm-cpudai-tdm-group-id = <37121>;
qcom,msm-cpudai-tdm-group-num-ports = <4>;
qcom,msm-cpudai-tdm-group-port-id = <36865 36867 36869 36871>;
qcom,msm-cpudai-tdm-clk-rate = <12288000>;
qcom,msm-cpudai-tdm-clk-internal = <1>;
qcom,msm-cpudai-tdm-sync-mode = <0>;
qcom,msm-cpudai-tdm-sync-src = <1>;
qcom,msm-cpudai-tdm-data-out = <0>;
qcom,msm-cpudai-tdm-invert-sync = <0>;
qcom,msm-cpudai-tdm-data-delay = <1>;
qcom,msm-cpudai-tdm-clk-attribute = /bits/ 16 <1>;
dai_pri_tdm_tx_0: qcom,msm-dai-q6-tdm-pri-tx-0 {
compatible = "qcom,msm-dai-q6-tdm";
qcom,msm-cpudai-tdm-dev-id = <36865>;
qcom,msm-cpudai-tdm-data-align = <0>;
};
dai_pri_tdm_tx_1: qcom,msm-dai-q6-tdm-pri-tx-1 {
compatible = "qcom,msm-dai-q6-tdm";
qcom,msm-cpudai-tdm-dev-id = <36867>;
qcom,msm-cpudai-tdm-data-align = <0>;
};
dai_pri_tdm_tx_2: qcom,msm-dai-q6-tdm-pri-tx-2 {
compatible = "qcom,msm-dai-q6-tdm";
qcom,msm-cpudai-tdm-dev-id = <36869>;
qcom,msm-cpudai-tdm-data-align = <0>;
};
dai_pri_tdm_tx_3: qcom,msm-dai-q6-tdm-pri-tx-3 {
compatible = "qcom,msm-dai-q6-tdm";
qcom,msm-cpudai-tdm-dev-id = <36871>;
qcom,msm-cpudai-tdm-data-align = <0>;
};
};
3. Kernel Driver:
3.1 tdm驱动(audio front-end (AFE) port 端):kernel/techpack/audio/asoc/msm-dai-q6-v2.c
/*
DTM设备驱动,匹配dtsi中 dtm组端口的
tdm_pri_rx: qcom,msm-dai-tdm-pri-rx {
compatible = "qcom,msm-dai-tdm";
...
dai_pri_tdm_rx_0: qcom,msm-dai-q6-tdm-pri-rx-0 {
compatible = "qcom,msm-dai-q6-tdm";
qcom,msm-cpudai-tdm-dev-id = <36864>;
qcom,msm-cpudai-tdm-data-align = <0>;
};
...
};
*/
static const struct of_device_id msm_dai_q6_tdm_dev_dt_match[] = {
{ .compatible = "qcom,msm-dai-q6-tdm", },
{}
};
MODULE_DEVICE_TABLE(of, msm_dai_q6_tdm_dev_dt_match);
static struct platform_driver msm_dai_q6_tdm_driver = {
.probe = msm_dai_q6_tdm_dev_probe,
.remove = msm_dai_q6_tdm_dev_remove,
.driver = {
.name = "msm-dai-q6-tdm",
.owner = THIS_MODULE,
.of_match_table = msm_dai_q6_tdm_dev_dt_match,
},
};
/*DTM驱动*/
static const struct of_device_id msm_dai_tdm_dt_match[] = {
{ .compatible = "qcom,msm-dai-tdm", },
{}
};
MODULE_DEVICE_TABLE(of, msm_dai_tdm_dt_match);
static struct platform_driver msm_dai_tdm_q6 = {
.probe = msm_dai_tdm_q6_probe,
.remove = msm_dai_tdm_q6_remove,
.driver = {
.name = "msm-dai-tdm",
.owner = THIS_MODULE,
.of_match_table = msm_dai_tdm_dt_match,
},
};
/*DTM 设备驱动注册*/
rc = platform_driver_register(&msm_dai_q6_tdm_driver);
if (rc) {
pr_err("%s: fail to register dai TDM dev drv\n", __func__);
goto dai_q6_tdm_drv_fail;
}
/*DTM驱动注册*/
rc = platform_driver_register(&msm_dai_tdm_q6);
if (rc) {
pr_err("%s: fail to register dai TDM\n", __func__);
goto dai_tdm_q6_fail;
}
3.2 声卡驱动(audio back-end (ABE) port 端):kernel/techpack/audio/asoc/sa8155.c
目的:用户态驱动,注册PCM声卡,给User空间调用。
具体详情,请看Linux alsa声卡驱动模型。
/*后端Dai Link 链路配置
后端 <---> DSP/CODEC <---> DAC/ADC <---> SPK/MIC
*/
static struct snd_soc_dai_link msm_auto_be_dai_links[] = {
/* Backend DAI Links */
{
.name = LPASS_BE_PRI_TDM_RX_1,
.stream_name = "Primary TDM1 Playback", //名称(播放流)
.cpu_dai_name = "msm-dai-q6-tdm.36866", //查询DTSI可以查到该dtm设备id
.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_1,//根据dtsi tdm设备组ID(36866)配置
.be_hw_params_fixup = msm_tdm_be_hw_params_fixup,
.ops = &sa8155_tdm_be_ops,
.ignore_suspend = 1,
},
{
.name = LPASS_BE_PRI_TDM_RX_2,
.stream_name = "Primary TDM2 Playback",
.cpu_dai_name = "msm-dai-q6-tdm.36868",
.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_2,
.be_hw_params_fixup = msm_tdm_be_hw_params_fixup,
.ops = &sa8155_tdm_be_ops,
.ignore_suspend = 1,
},
ABE snd_soc_dai_link 如何与 AFE的 snd_soc_dai_driver 关联?
- Stream Name关联
- CPU dai name 关联
易混技术点:DPCM (Dynamic PCM)
DAPM (Dynamic Audio Power Manager)
Alsa驱动模型参考:
Audio System 三 之 Linux ALSA音频系统分析_|~~~热爱生活、努力学习的小伙汁~~~|-CSDN博客