Hi,
I'm having some difficulty getting SAI1 MCLK to act as an external clock input in order to drive the SAI1 I2S signals to our codec. I've tried a number of things based on other users experiences on this forum and have researched the iMX8 Mini Applications Processor Reference Manual extensively. When it comes to the manual and sections describing the CCM, SAIs, clock muxing there appears to be a bit of inconsistency on how these sections tie together.
So to start off, the manual states my objective is possible:
13.1.1.4.1 SAI Master Clock Inputs/Outputs
"When configured as an input, the external input to the pad will be used as SAIn_MCLK and is routed to SAIn_MCLK_IN, which can be used as master clock for SAI."
The above excerpt states that the MCLK pin can be used as the SAI 'master clock'; I assuming this will be clock source for all I2S signals (SCLK,LRCK,DATA) etc, relative to frequency for MCLK input? (Y/N?)
--------------------------------------------------------------------------------------------
Page 3895 Figure 13-1 clearly shows how MCLK can be sourced from an external clock source resulting in SAIn_MCLK signal (alias SAIn_MCLK_IN)
The general view on this forum is that there are two mux's that require setting to route an externally applied clock to SAI1.
IOMUXC_GPR_GPR2 -> GPR_SAI1_EXT_MCLK_EN which sets the direction control of the corresponding pad as and input
IOMUXC_GPR_GPR6 -> GPR_SAI1_SEL1 (bit 5) IPG_CLK_SAI_MCLK of SAIn source select being SAIn.MCLK
Apparently setting these two registers should result in the externally applied clock (on MCLK pin) being routed SAI1 for use as the master clock. According to the advice on this forum these registers should be set in u-boot or at least sometime before the SAI and 12C drivers get probed to prevent lockups etc.
-----------------------------------------------------------------------------------------------
Throughout the manual the clock names appear to alter somewhat depending on chapter and topic which makes it difficult to trace certain clocks around the manual.
For example:
Page 3895 - Each SAI module supports up to 3 master clock inputs - SAIn_MCLK[1] to SAIn_MCLK[3]
Page 3896 - Figure 13-2 which follows uses - ipg_clk_sai_mclk[1] to ipg_clk_sai_mclk[3].
Are these the same?
------------------------------------------------------------------------------------------------
To use SAI1 MCLK pin as an input, providing these two IOMUX_GPR registers are set, what else needs to be added, i.e in the device tree?
I have seen a few forum users NOT use "assigned-clock-parents" and by what I've worked out that's correct as this property sets the 'clock source' in the CCM mux as per the diagram on page 313 of the manual. Using an external MCLK there is no need for CCM clocks such as 24M_REF_CLK, AUDIO_PLL_CLK etc. Setting the two IOMUX_GPR registers above would switch out that mux entirely.
That leaves the question of what to put in the device tree. Some users use only one set of clock definitions like below. Exactly what are IMX8MM_CLK_SAI1_IPG and IMX8MM_CLK_SAI1_ROOT is unclear in relation to the manual.
Another clock with little description is IMX8MM_CLK_SAI1_SRC. Lots of clock definitions with little information describing them.
Using more that one clock to drive the SAI (as per page 3895) appears to correlate to mclk1,2,3 below but as afar as I have researched there no mention of the "bus" SAI clock in the manual. What does a 'bus' clock do in respect a SAI?
clocks = <&clk IMX8MM_CLK_SAI1_IPG>,
<&clk IMX8MM_CLK_SAI1_ROOT>,
<&clk IMX8MM_CLK_DUMMY>,
<&clk IMX8MM_CLK_DUMMY>;
clock-names = "bus", "mclk1", "mclk2", "mclk3";
Just to be clear the above is just an example and I'm only requiring the one clock and I2S transmit only. Our product uses SAI1 connected to a standard I2S codec for audio output only. No recording.
Considering the clock defines (IMX8MM_CLK_xxxxx) what would be the appropriate one to represent the signal SAI.MCLK from IOMUX (via the two IOMUXC_GPR gates)?
----------------------------------------------------------------------------------------------------
So my actual objective is simple, I'd like to use the MCLK on SAI1 as an input from an external clock source to clock the internals of the SAI thus providing synchronised SCLK and other I2S signals. What am I missing? The furthest I can get is a build that stalls at the probing of the SAI; I'm assuming it's missing a vital clock (tested with the two IOMUX_GPR registers set and external clock applied to the MCLK pin).
As a side point, there has been mention of a clock patch that tidies up the clocking but this could be a red herring.
My build is Yocto Honister - kernel 5.15.5
Manual - i.MX 8M Mini Applications Processor Reference Manual, Rev. 3, 11/2020
Thanks!
Solved! Go to Solution.
Hi @Zhiming_Liu
It's appears to be all going now. Here's the breakdown:
#1 U-Boot patches as supplied by yourself
IOMUXC_GPR_GPR2 -> GPR_SAI1_EXT_MCLK_EN which sets the direction control of the corresponding pad as and input
IOMUXC_GPR_GPR6 -> GPR_SAI1_SEL1 (bit 5) IPG_CLK_SAI_MCLK of SAIn source select being SAIn.MCLK
#2 Device Tree changes
i2s_mclk: fixed-24576 {
compatible = "fixed-clock";
#clock-cells = <0>;
clock-frequency = <24576000>;
clock-accuracy = <20000>;
};
&sai1 {
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_sai1>;
clocks = <&clk IMX8MM_CLK_SAI1_IPG>,
// MCLK1 is switched to SAI1.MCLK in the IOMUXC_GPR_GPR6 register in uboot code
<&i2s_mclk>,
<&clk IMX8MM_CLK_DUMMY>, <&clk IMX8MM_CLK_DUMMY>;
clock-names = "bus", "mclk1", "mclk2", "mclk3";
status = "okay";
}
In order to get the correct clock frequency for the rate divisor code with the modified WM8523 driver, i2s_mclk needed to be passed into the simple-audio-card based driver instead of the normal clk_sai1_root
simple-audio-card,codec {
sound-dai = <&wm8523>;
//clocks = <&clk IMX8MM_CLK_SAI1_ROOT>;
//clock-names = "mclk";
clocks = <&i2s_mclk>; <== MCLK input
clock-names = "mclk1";
};
#3 fsl_sai.c patch
'Select MCLK direction' code in two places but mainly in fsl_sai_set_bclk(). The SAI FSL_SAI_MCTL register was setting MCLK as an output in the default SAI mode.
So the codec now renders all 44100 and 48000 audio rates natively right through to 352.8 KHz and 384KHz respectively. So no ALSA plugins (plughw) or dmix are needed; just a gpio line to switch between the two external clock rates.
Appreciated your help on this one!
Cheers.
The dts is used for normal case which doesn't contain the GPR register. So you need config it in uboot. Here is demo code for this:
static int setup_sai1_mclk(void)
{
struct iomuxc_gpr_base_regs *gpr =
(struct iomuxc_gpr_base_regs *)IOMUXC_GPR_BASE_ADDR;
// GPR6
// GPR_SAI1_EXT_MCLK_EN = 1
setbits_le32(&gpr->gpr[6], (1 << 2));
// GPR_SAI1_SEL1 = 1
setbits_le32(&gpr->gpr[6], (1 << 5));
return 0;
}
Then you need call this function in board_init(board/freescale/imx8mm_evk/imx8mm_evk.c).
For dts part, you need config it like here: https://community.nxp.com/t5/i-MX-Processors/iMX8M-Mini-How-to-use-MCLK-pad-as-master-clock-input/m-...
The i2s_mclk is the external clock.
i2s_mclk: fixed-24576 {
compatible = "fixed-clock";
#clock-cells = <0>;
clock-frequency = <24576000>;
clock-accuracy = <20000>;
};
&sai1 {
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_sai1>;
status = "okay";
fsl,sai-synchronous-rx;
clocks = <&clk IMX8MM_CLK_SAI1_IPG>,
// MCLK1 is switched to SAI1.MCLK in the IOMUXC_GPR_GPR6 register in uboot code
<&i2s_mclk>,
<&clk IMX8MM_CLK_DUMMY>, <&clk IMX8MM_CLK_DUMMY>;
clock-names = "bus", "mclk1", "mclk2", "mclk3";
fsl,sai-multi-lane;
};
Hi @Zhiming_Liu
I have actually tried the same device tree code multiple times in the past that you have posted but with no success. One of the problems is that the codec driver probe function wm8523_set_dai_sysclk() is passed a clock frequency of 24000000 not the value of i2s_mclk:fixed-24576; this incorrect frequency won't divide correctly for any audio rates so the function fails. The original wm8523 code used PLL1 in which the correct frequency was passed as defined in the device tree. The wm8523 driver has been heavily modified to suit another codec but it still requires a divisible clock.
In the function below, do you know what clock is passed? Can the clock i2s_mclk:fixed-24576 be passed instead?
static int wm8523_set_dai_sysclk(struct snd_soc_dai *codec_dai,int clk_id, unsigned int freq, int dir)
The original device tree always passed the correct frequency using assigned-clock-parents, but using the device tree setting you posted clocks = <&clk IMX8MM_CLK_SAI1_IPG> it defaults to 24000000.
Original and correct frequency passed to unsigned int freq - This cannot be used now that an external MCLK clock is required..
assigned-clock-parents = <&clk IMX8MM_AUDIO_PLL1_OUT>;
assigned-clock-rates = <49152000>;
Cheers.
Cheers.
In the function below, do you know what clock is passed? Can the clock i2s_mclk:fixed-24576 be passed instead?
-->It should be called by snd_soc_dai_set_sysclk which is used for the DAI master (MCLK) or system (SYSCLK) clocking.
In here, it's setting "bus" clock, you can change this clock by replacing <&clk IMX8MM_CLK_SAI1_IPG> with <&i2s_mclk> if you need .
Hi @Zhiming_Liu
It's appears to be all going now. Here's the breakdown:
#1 U-Boot patches as supplied by yourself
IOMUXC_GPR_GPR2 -> GPR_SAI1_EXT_MCLK_EN which sets the direction control of the corresponding pad as and input
IOMUXC_GPR_GPR6 -> GPR_SAI1_SEL1 (bit 5) IPG_CLK_SAI_MCLK of SAIn source select being SAIn.MCLK
#2 Device Tree changes
i2s_mclk: fixed-24576 {
compatible = "fixed-clock";
#clock-cells = <0>;
clock-frequency = <24576000>;
clock-accuracy = <20000>;
};
&sai1 {
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_sai1>;
clocks = <&clk IMX8MM_CLK_SAI1_IPG>,
// MCLK1 is switched to SAI1.MCLK in the IOMUXC_GPR_GPR6 register in uboot code
<&i2s_mclk>,
<&clk IMX8MM_CLK_DUMMY>, <&clk IMX8MM_CLK_DUMMY>;
clock-names = "bus", "mclk1", "mclk2", "mclk3";
status = "okay";
}
In order to get the correct clock frequency for the rate divisor code with the modified WM8523 driver, i2s_mclk needed to be passed into the simple-audio-card based driver instead of the normal clk_sai1_root
simple-audio-card,codec {
sound-dai = <&wm8523>;
//clocks = <&clk IMX8MM_CLK_SAI1_ROOT>;
//clock-names = "mclk";
clocks = <&i2s_mclk>; <== MCLK input
clock-names = "mclk1";
};
#3 fsl_sai.c patch
'Select MCLK direction' code in two places but mainly in fsl_sai_set_bclk(). The SAI FSL_SAI_MCTL register was setting MCLK as an output in the default SAI mode.
So the codec now renders all 44100 and 48000 audio rates natively right through to 352.8 KHz and 384KHz respectively. So no ALSA plugins (plughw) or dmix are needed; just a gpio line to switch between the two external clock rates.
Appreciated your help on this one!
Cheers.
Hi @Zhiming_Liu
Thanks for your quick reply. Is there a mistake in the uboot code? As far as the documentation goes this should be GRP2 bit 0 not GPR6 for the iMX8M?
// GPR6
// GPR_SAI1_EXT_MCLK_EN = 1
setbits_le32(&gpr->gpr[6], (1<<2));
The manual states:
Bit 0 of IOMUXC_GPR_GPR2 is GPR_SAI1_EXT_MCLK_EN (page 1288)
Thanks again.
Yes , this is a mistake, this just for reference of GPR operation
Hi @Zhiming_Liu
Thanks! Just to clarify our objective, this is our overall requirement.
We require an I2S transmit only codec to be able to play all 44100 and 48000 based audio rates up to 176400 / 192000 natively. This means two clock frequencies are required - 24576000 and 22579200 for example, to get the rate dividers to function properly. The the reason for an external clock requirement is that device tree PLL1 and PLL2s cannot be both passed into the driver at run time hence we have an external PLL that can switch between both frequencies via a GPIO line..
Cheers.