.. include:: /keyword.rst ============== ETDM (I2S/TDM) ============== .. contents:: Sections :local: :depth: 2 ETDM (Enhanced Time Division Multiplexer) is an advanced version of the TDM technology, designed to improve data transmission efficiency and flexibility for audio applications. The ETDM interface is notable for its ability to support a larger number of audio channels and higher data rates compared to standard TDM interfaces. The ETDM interface can operate as an I2S interface for transmitting stereo or mono data, and also as a TDM interface capable of handling multiple independent audio channels simultaneously. This section will provide some common use cases for the ETDM driver, and will introduce the ETDM configurations, such as DAI settings in the machine driver. It will also explain how to make customized modifications in the DTS. Finally, it will also cover the porting of I2S. Driver Command Usage -------------------- The MT8370/MT8390/MT8395 features 6 ETDM interfaces, with two dedicated to capture and the remaining four allocated for playback. - `etdm_in1`: TDM capture - `etdm_in2`: I2S capture - `etdm_out1`: TDM playback - `etdm_out2`: I2S playback - `etdm_out3`: HDMI playback - `DPTX`: DP playback - Capture Example for *I2SIN*, use ``ETDM_XXX_Clock_Source`` control to set the clock source. The default clock source is in low power mode, it saves power but may be jitter at high sample rates. You can set the control to ``a1sys_a2sys`` to activates the low jitter mode, which means either the 48K clock generated by APLL1 or the 44.1K clock generated by APLL2 will be chosen. .. prompt:: bash # auto # amixer -c 0 cset name='ETDM_IN2_Clock_Source' 'a1sys_a2sys' Here are two examples of ETDM capture, `etdm_in1` interface is connected to `UL8`, and `etdm_in2` interface is connected to `UL3` in the direct path. .. prompt:: bash # auto etdm_in1: # arecord -Dhw:0,13 -c 2 -r 48000 -f S32_LE -d 10 etdm_rec1.wav etdm_in2: # arecord -Dhw:0,9 -c 2 -r 48000 -f S32_LE -d 10 etdm_rec2.wav .. figure:: /_asset/sw_rity_porting-guide_audio_etdm-capture.svg :align: center :scale: 80% ETDM capture - Playback Example for *I2S OUT*, set clock source from low power mode to low jitter mode. .. prompt:: bash # auto ETDM out1: # amixer -c 0 cset name='ETDM_OUT1_Clock_Source' 'a1sys_a2sys' ETDM out2: # amixer -c 0 cset name='ETDM_OUT2_Clock_Source' 'a1sys_a2sys' Here are two examples of ETDM playback. .. prompt:: bash # auto ETDM out1: # amixer -c 0 cset name='O072 I022 Switch' 1 # amixer -c 0 cset name='O073 I023 Switch' 1 # aplay test.wav -Dhw:0,6 ETDM out2: # amixer -c 0 cset name='O048 I022 Switch' 1 # amixer -c 0 cset name='O049 I023 Switch' 1 # aplay test.wav -Dhw:0,6 - HDMI/DP playback Set clock source from low power mode to low jitter mode. .. prompt:: bash # auto # amixer -c 0 cset name='ETDM_OUT3_Clock_Source' 'a1sys_a2sys' - Switch to HDMI playback .. prompt:: bash # auto # amixer -c 0 cset name='DPTX_OUT_MUX' 0 # amixer -c 0 cset name='HDMI_OUT_MUX' 1 - Switch to DP playback .. prompt:: bash # auto # amixer -c 0 cset name='HDMI_OUT_MUX' 0 # amixer -c 0 cset name='DPTX_OUT_MUX' 1 - Using HDMI playback and DP playback simultaneously .. prompt:: bash # auto # amixer -c 0 cset name='HDMI_OUT_MUX' 1 # amixer -c 0 cset name='DPTX_OUT_MUX' 1 .. note:: MT8370/MT8390/MT8395 only supports 2 channels of playback for this configuration! Machine Driver Configuration ---------------------------- To specify ETDM DAI configurations in the MT8390/MT8395 machine driver. - Master/Slave mode In master mode, the ETDM device generates the clock signals needed for the communication. The devices operating in master mode controls the timing of the data transmission, dictating when the slave device should send or receive data. In slave mode, the ETDM device does not generate its own clock signals. Instead, it relies on an external source, the slave device receives the clock signals and synchronizes its data transmission or reception according to these external clocks. .. csv-table:: DAI master/slave mode configurations :header: "Parameters", "Mode" :encoding: utf-8 :width: 100% :widths: 50 50 "SND_SOC_DAIFMT_CBC_CFC", "Master mode" "SND_SOC_DAIFMT_CBS_CFS", "Master mode (Deprecated since Linux v5.11)" "SND_SOC_DAIFMT_CBP_CFP", "Slave mode" "SND_SOC_DAIFMT_CBM_CFM", "Slave mode (Deprecated since Linux v5.11)" Currently, the new definition has been adopted for MT8365, MT8370 and MT8390, but the old definition is still being used for MT8395. - Format There are various formats for DAI, and the user can specify the format in the machine driver. .. csv-table:: DAI format configurations :header: "Parameters", "DAI format" :encoding: utf-8 :width: 100% :widths: 50 50 "SND_SOC_DAIFMT_I2S", "FORMAT_I2S" "SND_SOC_DAIFMT_LEFT_J", "FORMAT_LJ" "SND_SOC_DAIFMT_RIGHT_J", "FORMAT_RJ" "SND_SOC_DAIFMT_DSP_A", "FORMAT_DSPA" "SND_SOC_DAIFMT_DSP_B", "FORMAT_DSPB" - Clock Inverse Clock inverse refers to the inversion of the clock signal polarity within a DAI, it means that the rising and falling edges of the clock signal are reversed. The clock signal is crucial for synchronizing the digital audio data stream during transmission. For instance, if one device's DAI expects to receive data on the falling edge of the clock, while another device by default sends data on the rising edge, the clock inversion feature can ensure that the two devices can synchronize their data transfer correctly. .. csv-table:: DAI clock inverse configurations :header: "Parameters", "BCK inverse", "LRCK inverse" :encoding: utf-8 :width: 100% :widths: 50 25 25 "SND_SOC_DAIFMT_NB_NF", "False", "False" "SND_SOC_DAIFMT_NB_IF", "False", "True" "SND_SOC_DAIFMT_IB_NF", "True", "False" "SND_SOC_DAIFMT_IB_IF", "True", "True" E.g. .. csv-table:: Parameter settings :header: "Parameters", "Value" :encoding: utf-8 :width: 100% :widths: 50 50 "MCLK rate", "128 * sampling rate" "Master/Slave mode", "master" "Format", "I2S" "Clock inverse", "False" Step1: Set the MCLK rate in the ``mclk_fs_ratio`` property within the `machine driver `_:: static int mt8195_etdm_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_soc_card *card = rtd->card; struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0); unsigned int rate = params_rate(params); unsigned int mclk_fs_ratio = 128; unsigned int mclk_fs = rate * mclk_fs_ratio; int ret; ret = snd_soc_dai_set_sysclk(cpu_dai, 0, mclk_fs, SND_SOC_CLOCK_OUT); if (ret) { dev_err(card->dev, "failed to set sysclk\n"); return ret; } return 0; } Step2: Add ops for ETDM MCLK rate:: static const struct snd_soc_ops mt8195_etdm_ops = { .hw_params = mt8195_etdm_hw_params, }; Step3: Update the properties for ``.dai_format`` and ``.ops`` in the respective `dai-link `_:: [DAI_LINK_ETDM1_IN_BE] = { .name = "ETDM1_IN_BE", .no_pcm = 1, .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS, .dpcm_capture = 1, .ops = &mt8195_etdm_ops, .be_hw_params_fixup = mt8195_etdm_hw_params_fixup, SND_SOC_DAILINK_REG(ETDM1_IN_BE), }, DTS Setting ----------- There are four customizable DTS properties for ETDM. - ``mediatek,X-multi-pin-mode`` - ``mediatek,X-mclk-always-on-rate`` - ``mediatek,X-cowork-source`` - ``mediatek,X-chn-disabled`` Here, we use **X** to represent the specific item names corresponding to different digital audio interfaces in the DTS file. .. csv-table:: ETDM Interface :header: "DTS item name", "Interface" :encoding: utf-8 :width: 100% :widths: 50 50 `etdm-in1`, "TDMIN" `etdm-in2`, "I2SIN" `etdm-out1`, "I2SO1" `etdm-out2`, "I2SO2" - `mediatek,X-multi-pin-mode` By default, systems are configured to operate in one-pin mode, where one data pin handles the input from multiple devices. However, if your hardware setup requires multi-pin mode, where each device has a dedicated data pin, then this needs to be specified in the DTS file. Here's an example of how you would specify a property in the DTS file to set up in multi-pin mode of *I2SIN*: .. prompt:: bash # auto &afe { mediatek,etdm-in2-multi-pin-mode; }; - One-pin: Only one pin is responsible for transmitting/receiving data .. figure:: /_asset/sw_rity_porting-guide_audio_etdm-one-pin.svg :align: center :scale: 80% One-pin mode - Multi-pin: Each pin transmits/receives 2-channel data .. figure:: /_asset/sw_rity_porting-guide_audio_etdm-multi-pin.svg :align: center :scale: 80% Multi-pin mode - `mediatek,X-mclk-always-on-rate` Specify the MCLK output rate and MCLK will keep active all the time. MCLK rate is divided from APLL rate 196608000 or 180633600. .. note:: ``mclk-always-on-rate`` is only supported in MT8395. Here's an example of how you would specify ``mclk-always-on-rate`` property in the DTS file of *I2SO1*: .. prompt:: bash # auto &afe { mediatek,etdm-out1-mclk-always-on-rate = <24576000>; }; In this way, I2SO1_MCLK always outputs 24.576MHz. - `mediatek,X-cowork-source` When configuring an ETDM interface, specifying the co-clock source is crucial when you have multiple audio devices sharing the same clock signal. A co-clock source is a common clock shared by different devices to ensure they are synchronized. .. csv-table:: co-clock source list :header: "Index", "Interface" :encoding: utf-8 :width: 100% :widths: 30 70 "0", `etdm-in1` (TDMIN) "1", `etdm-in2` (I2SIN) "2", `etdm-out1` (I2SO1) "3", `etdm-out2` (I2SO2) Here's an example of how you would specify the co-clock source property in the DTS file. For instance, *etdm-in1* and *etdm-out1* share the same clock. In this case, clock input or output only requires *I2SO1* clock pins. .. prompt:: bash # auto &afe { mediatek,etdm-in1-cowork-source = <2>; //out1 }; - `mediatek,X-chn-disabled` This property is used to disable ETDM in the channel for the direct path. The direct path refers to the data route connecting to memory interface UL without going through ``afe_interconn``. Only ``etdm_in1`` and ``etdm_in2`` in the direct path supports the feature. When using this DTS property, the user also needs to add ``be_hw_params_fixup`` in the machine driver. .. prompt:: bash # auto static int mt8390_etdm_in_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, struct snd_pcm_hw_params *params) { struct snd_interval *channels = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS); channels->min = channels->max = 4; return 0; } [DAI_LINK_ETDM2_IN_BE] = { .name = "ETDM2_IN_BE", ... .be_hw_params_fixup = mt8390_etdm_in_hw_params_fixup, }, Here's an example of how you would specify the channel disabled property in the DTS file. For example, if you are recording data from four channels (0~3) sourced from *I2SIN*, you may only be interested in retrieving the data from channel 1. .. prompt:: bash # auto &afe { mediatek,etdm-in2-chn-disabled = /bits/ 8 <0x0 0x2 0x3>; }; By following this method, you can obtain stereo data where both channels are sourced from the channel 1 source. I2S Porting ----------- This section focuses on porting the I2S feature, starting from the hardware aspect, to the configuration in the DTS, and finally explaining how to connect the data path and set up the clock source. The following is an explanation using the |G1200-EVK| as an example. .. figure:: /_asset/sw_rity_porting-guide_audio_i2s_capture_playback_g1200.svg :align: center :scale: 100% I2S audio data complete route - Please ensure that pins have been allocated for I2S use on your hardware On the |G1200-EVK|, for instance, there are no I2S pins allocated, so even if the software part is configured correctly, external devices cannot be connected for use. Therefore it can only be used for internal loopback testing. - Configure the I2S pins that will be used in the DTS file In the DTS file, add all the I2S pins that will be used, and please make sure that these pins are not being used by other functions. Sometimes, to save on hardware, a single pin may have multiple functions, which can be switched depending on user requirements. For example, on |G1200-EVK|, GPIO47 and GPIO48 are by default used for the DSI panel. Here is an example of how to add I2S pins configuration in the `DTS file `_:: aud_pins_default: audiodefault { pins-cmd-dat { pinmux = ... , , , , , , , , ... }; }; - Confirm that the I2S audio data path is connected By default, the platform has configured the audio data transmission paths within the AFE for each audio device. It is important to ensure that the switches on the path are turned on before use. .. prompt:: bash # auto # amixer -c mt8390evk cset name='O036 I012 Switch' on # amixer -c mt8390evk cset name='O037 I013 Switch' on # amixer -c mt8390evk cset name='O048 I022 Switch' on # amixer -c mt8390evk cset name='O049 I023 Switch' on Configure the clock source for the eTDM interface, where ``a1sys`` and ``a2sys`` respectively represent the clock sources generated from ``APLL1`` and ``APLL2`` for 48K and 44.1K. .. prompt:: bash # auto # amixer -c mt8390evk cset name='ETDM_IN2_Clock_Source' a1sys_a2sys # amixer -c mt8390evk cset name='ETDM_OUT2_Clock_Source' a1sys_a2sys The above is just an example, and the actual situation still needs to refer to the hardware schematic, application processor functional specification, GPIO list, and other relevant documentation. If you want to integrate an external audio codec into the platform, please refer to :ref:`here `.