Processor: MIMX8MM5
Documentation: i.MX 8M Mijni Applications Processor Reference Manual
Document number: IMX8MMRM Rev. 3
Page#4211
I am trying to write a SPDIF driver for the MIMX8MM5 processor. The reference manual I'm using has missing information about the clock source for the SPDIF TX.
Specifically, the TxClk_Source field of the SPDIFTxClk register, has only one option:
000b - Click Selection from Audio Clock Mux (ACM)
1-Why is there only one option? It's a 3 bit field
2-What is the Audio Clock Mux? I cannot find references to this ANYWHERE in this manual
TRegarding the clocking fo
The thread is quite vague about how SPDIF1 TxClock is generated. I *THINK* it is implying that SPDIF1_CLOCK_ROOT controls the Tx clock of SPDIF1. Howver, I tried changing the divide registers of SPDIF1_CLOCK_ROOT (CCM_BASE + 0xA880) and the signal output by my SPDIF1-TX didn't change.
I still feel I don't have all the information available on clocking the Transmit section of SPDIF1
Hi @sgordon777
Please check the mux mode of with IOMUXC_SW_MUX_CTL_PAD_SPDIF_TX, the default mux mode is 101b ALT5 Select signal GPIO5_IO03
In Linux dts, the setting is
pinctrl_spdif1: spdif1grp {
fsl,pins = <
MX8MM_IOMUXC_SPDIF_TX_SPDIF1_OUT 0xd6
MX8MM_IOMUXC_SPDIF_RX_SPDIF1_IN 0xd6
>;
};
And for the SPDIF1 node, the SPDIF1 root clock is from AUDIO_PLL1, and the TX clock is from SPDIF1 root clock.
&spdif1 {
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_spdif1>;
assigned-clocks = <&clk IMX8MM_CLK_SPDIF1>;
assigned-clock-parents = <&clk IMX8MM_AUDIO_PLL1_OUT>;
assigned-clock-rates = <24576000>;
clocks = <&clk IMX8MM_CLK_AUDIO_AHB>, <&clk IMX8MM_CLK_24M>,
<&clk IMX8MM_CLK_SPDIF1>, <&clk IMX8MM_CLK_DUMMY>,
<&clk IMX8MM_CLK_DUMMY>, <&clk IMX8MM_CLK_DUMMY>,
<&clk IMX8MM_CLK_AUDIO_AHB>, <&clk IMX8MM_CLK_DUMMY>,
<&clk IMX8MM_CLK_DUMMY>, <&clk IMX8MM_CLK_DUMMY>,
<&clk IMX8MM_AUDIO_PLL1_OUT>, <&clk IMX8MM_AUDIO_PLL2_OUT>;
clock-names = "core", "rxtx0", "rxtx1", "rxtx2", "rxtx3",
"rxtx4", "rxtx5", "rxtx6", "rxtx7", "spba", "pll8k", "pll11k";
status = "okay";
};
For the driver call, please trace spdif_priv->txclk in sound/soc/fsl/fsl_spdif.c,
Best Regards,
Zhiming
Hi @sgordon777
It looks like you are using zephyr, right? Are you sure the PLLs are generated from 24Mhz? I checked the zephyr source code, there is no such code to generate PLLs like Linux. In Linux, the PLLs are generated from drivers/clk/imx/clk-pll14xx.c with imx_pll1443x_tbl
Best Regards,
Zhiming
Yes we are using zephyr, and thanks for the information about the PLLs.
However, I'm still lacking even the most basic confirmation about how the clocks are routed to the SPDIF1 Tx module.
Can we please confirm whether SPDIF TX is clocked from SPDIF_CLK_ROOT (CCM offset 0xA880), and we have the seven options shown in the table above (of which it seems 000 is selected (because I see a 24Mhz output on SPDIF-TX.)
Hi @sgordon777
Can we please confirm whether SPDIF TX is clocked from SPDIF_CLK_ROOT
-->Yes, we can refer the setting in Linux dts, the TX clock is from SPDIF_CLK_ROOT.
For zephyr, the other PLLs in the seven options will not be generated, and the default source of SPDIF_CLK_ROOT is 24MHz. Since the PLL wasn't generated, but you wanted to use it, that's what caused the system to crash. If you need to use a PLL, these are the three steps:
1.generate the AUDIO PLL
2.set the source of SPDIF_CLK_ROOT to be the AUDIO PLL
3.cancel the gate of SPDIF_CLK_ROOT so the clock will flow correctly
Best Regards,
Zhiming
>> 3.cancel the gate of SPDIF_CLK_ROOT so the clock will flow correctly
What does this mean exactly? Can you refer to a section in the REF manual?
>> 2.set the source of SPDIF_CLK_ROOT to be the AUDIO PLL
You mean using the bitfields in the CCM_BASE + 0xA880 register right?
>>Since the PLL wasn't generated, but you wanted to use it, that's what caused the system to crash.
-Even if I choose the EXT_CLK_2 or EXT_CLK_3 options (b110, b111) I get the crash though. Are EXT_CLK_X associated with PLLs?
Hi @sgordon777
Below is the generic clock path:
24Mhz-->PLLs-->CCM-->xxx_CLK_ROOT-->LPCG gate control
External clock input-->CCM-->xxx_CLK_ROOT-->LPCG gate control
The cg unit is clock gate.
EXT_CLK_x is not PLLs, external clock from a clock input external to the chip. PLLs, 24MHz and 32KHz are clocks that can be used by default. But need generate PLLs with PLL driver to get basic PLLs.
The CCM_BASE + 0xA880 is used to select sources, EXT_CLK, PLLs or 24MHz.
Most clocks have LPCG control. To generate the SPDIF clock, you need to refer to the diagram below for the steps you need to take.
1.In zephyr os, generate PLLs, then set CCM_BASE + 0xA880 to select the source of SPDIF_CLK_ROOT.
2.disable gate of PLLs you select as you can see that there is a cg unit before you set the PRE and POST divider.
3. Then set the PRE and POST divider to get the SPDIF clock rate you want.
Best Regards,
Zhiming
Thanks you. Yes, I'm aware of the clock tree chapter, I've tried to follow it for the purpose of this discussion. However, It's not very clear at all what registers these slice diagrams are referrring to:
- There is no MUX_A field in the CCM_POST_ROOT88 register, as the above diagram implies.
- There *IS* a MUX_A field in the CCM_PRE_ROOT88 register.
- There is a "MUX field in the CCM_TARGET_ROOT88 register. This is what I've been trying to use. However as I've been saying, it locks the system if I choose a value other than 000, even if I select NON-PLL (external clocks). Also, the values I write in PREDIV, POSTDIV, and ENABLE fields for this register are completely ignored.
I hope you can clarify, Thanks
Hi,
1. Select source to VIDEO_PLL1_CLK:
The MUX_A here is MUX bits in CCM_TARGET_ROOT81. To write CCM_TARGET_ROOTn, write 1 to the bits you want in CCM_TARGET_ROOTn_SET. To clear bits, write 1 to the bits you want in CCM_TARGET_ROOTn_CLR. You need to use xxx_SET, xxx_CLR, and xxx_TOG interfaces.
2. AUDIO_PLL1_OUT gate control.
Best Regards,
Zhiming
>> You need to use xxx_SET, xxx_CLR, and xxx_TOG interfaces.
Thanks, A couple comments on this:
1-First, I have tried this long ago (before writing to the forum) , and get EXACT same results as writing to base.
2-The manual implies that the set/clr/tog interface are for convenience, and not required
3-How could I use the set/clr/tog interface to set multi-bit patterns with both 1s and 0s, like MUX, as opposed to traditional &(~mask) | value ???
4-The FSL library definitely writes to "BASE"
Have you had success in generating the base PLLs so far? To generate the base PLLs, the base address for them is 0x3036_0000. If you are porting ccm driver in zephyr, please refer functions in drivers/clk/imx/clk-pll14xx.c, like clk_pll14xx_prepare, clk_pll1443x_set_rate. Is that what you said about writes being ignored happening base PLLs generation?
Best Regards,
Zhiming
Ok I have found the main problem: It was that my MMU configuration didn't have the PLL and CCM segments defined propperly. Once I defined these everything has fallen into place and I was able to configure, and select AudioPLL1 and confirm it's correct frequency in the SPDIF Tx BiPhase signal.
BUT
I'm still confused about the TxClkSource field of the SPDIF1's STC register. I was led to believe (see manual below) that 000 was the only valid option for this field. However, I could not get AudioPLL1 to rout to the SPDIF until I set this field to 001!? (I was lucky to stumble up upon this)
I'd really like some insight into this as I don;t like setting fields by trial and error. I have a simmilar question about the ClkSrc_Sel field of the SRPC register.
2) - field (bits 8:10).
#2 is where the Reference manual is flat wrong. The manual implies that 000b is the only choice, but you must set this field to the same value you've set to the CCM_TARGET_ROOTn - MUX field
Hi @sgordon777
You've reminded me to check the spdif driver. This field is written in SPDIF driver which cause the TxClk_Source =111 before setting the source and clock rate. At least that's what's done from the linux driver. The reason why these bits are hidden is that for most customers there is no need to manually adjust these bits, the BSP are already done. Only core software development has the most detailed information, and support team doesn't have it. You can refer to the settings in the Linux SPDIF driver to set up the SRPC.
if (!fsl_spdif_can_set_clk_rate(spdif_priv, clk))
goto clk_set_bypass;
/* The S/PDIF block needs a clock of 64 * fs * txclk_df */
ret = clk_set_rate(spdif_priv->txclk[clk],
64 * sample_rate * txclk_df);
if (ret) {
dev_err(&pdev->dev, "failed to set tx clock rate\n");
return ret;
}
clk_set_bypass:
dev_dbg(&pdev->dev, "expected clock rate = %d\n",
(64 * sample_rate * txclk_df * sysclk_df));
dev_dbg(&pdev->dev, "actual clock rate = %ld\n",
clk_get_rate(spdif_priv->txclk[clk]));
/* set fs field in consumer channel status */
spdif_set_cstatus(ctrl, IEC958_AES3_CON_FS, csfs);
/* select clock source and divisor */
stc = STC_TXCLK_ALL_EN | STC_TXCLK_SRC_SET(clk) |
STC_TXCLK_DF(txclk_df) | STC_SYSCLK_DF(sysclk_df);
mask = STC_TXCLK_ALL_EN_MASK | STC_TXCLK_SRC_MASK |
STC_TXCLK_DF_MASK | STC_SYSCLK_DF_MASK;
regmap_update_bits(regmap, REG_SPDIF_STC, mask, stc);
dev_dbg(&pdev->dev, "set sample rate to %dHz for %dHz playback\n",
spdif_priv->txrate[rate], sample_rate);
return 0;
Best Regards,
Zhiming
Thank Zhiming,
>> Only core software development has the most detailed information
>> You can refer to the settings in the Linux SPDIF driver to set up the SRPC.
I appreciate that (looking at the Linux source was next on my list, but I dont even know where to get it). I left the SRPC at 0
However, looking at Linux drivers isn't a substitute for having good bit-level documentation because I might be trying to do something in my Zephyr driver that the Linux driver doesnt do. We don't have BSP because we're designing the boards.
Can you tell me the difference between the SRPC:clocksrc and STC:clkcrc, and what each is used for?
-Field descriptions (fig2) say 0000 is the only option for both
-but the SPDIF system diagram(fig1) manual implies that SRPC:clksrc is used for muxing.
-But as I mentioned in the above pos, I had to set STC:TxClk_Source [bits 8:10] to 001 to get the AudioPLL1 to come through.
FIG1: SPDIF System diagram
FIG2: STC vs SRPC (both have txclk soruce field)
Would really appreciate clarification,
Steve
Hi @sgordon777
1. In Figure 13-85, SRPC[CLKSRC_SEL] should be STC[TxClk_Source].
2. ClkSrc_Sel
0000b – if (DPLL Locked) SPDIF Rx Clock else 24M_REF_CLK
0001b – if (DPLL Locked) SPDIF Rx Clock else SPDIF1_CLK_ROOT
0010b – if (DPLL Locked) SPDIF Rx Clock else SPDIF1_CLK_ROOT
0011b – if (DPLL Locked) SPDIF Rx Clock else SPDIF1_CLK_ROOT
0100b – if (DPLL Locked) SPDIF Rx Clock else SPDIF1_CLK_ROOT
0101b – 24M_REF_CLK
0110b – SPDIF1_CLK_ROOT
0111b – SPDIF1_CLK_ROOT
1000b – SPDIF1_CLK_ROOT
1001b – SPDIF1_CLK_ROOT
1010b – if (DPLL Locked) SPDIF Rx Clock else SPDIF1_CLK_ROOT
1011b – if (DPLL Locked) SPDIF Rx Clock else SPDIF1_CLK_ROOT
1100b – SPDIF1_CLK_ROOT
1101b – SPDIF1_CLK_ROOT
Others: Reserved
This information is from design team.
Best Regards,
Zhiming
Thanks Zhiming, I appreciate you getting the design team involved. I think we're "almost there". Unfortuantely however, I was not able to see the effects of switching between RX-derived clock which is described in the above table. Here's what I did:
1-SPDIF1_STC[TxClk_Source] = 0, build, run
2-Unplug SPDIF_IN cable: Measure SPDIF_Tx Biphase clock = 24.000 Mhz
3-Plug in SPDIF_IN cable and Verify PLL lock: Measrure SPDIF_Tx Biphase clock = 24.000 Mhz (according to the diagram above, this should be 24.576 Hz (the frequency of the measured PLL from SPDIF_IN)
I tried many of the options in your list but none worked.
Is there something I need to turn on or some register to set in order to enable this automatic switching between RX-derived clock and the ROOT-Slice options?
Automatic switching is not a requirement. However clocking SPDIF-TX from SPDIF-RX derived block is a hard requirement for our product and we were told that this is possible.
I also am still hoping to answer the question of what is SRPC[CLKSRC_SEL] used for? (though this is secondary to the above concern).
Again, we are using a MIMX8MM5
Thanks, Steve
Hi @sgordon777
Here are reference driver in Linux driver:linux/sound/soc/fsl/fsl_spdif.c at master · torvalds/linux
The DPLL lock will trigger interrupt and enter spdif_isr, set spdif_priv->dpll_locked=true.
if (sis & INT_DPLL_LOCKED)
spdif_irq_dpll_lock(spdif_priv);
/* DPLL locked and lock loss interrupt handler */
static void spdif_irq_dpll_lock(struct fsl_spdif_priv *spdif_priv)
{
struct regmap *regmap = spdif_priv->regmap;
struct platform_device *pdev = spdif_priv->pdev;
u32 locked;
regmap_read(regmap, REG_SPDIF_SRPC, &locked);
locked &= SRPC_DPLL_LOCKED;
dev_dbg(&pdev->dev, "isr: Rx dpll %s \n",
locked ? "locked" : "loss lock");
spdif_priv->dpll_locked = locked ? true : false;
if (spdif_priv->snd_card && spdif_priv->rxrate_kcontrol) {
snd_ctl_notify(spdif_priv->snd_card,
SNDRV_CTL_EVENT_MASK_VALUE,
&spdif_priv->rxrate_kcontrol->id);
}
}
The following function is not suitable for your application scenario. But we need to make sure the DPLL is locked.
Make sure spdif_private->rxrate_kcontrol can be got.
spdif_private->rxrate_kcontrol = snd_soc_card_get_kcontrol(dai->component->card,
RX_SAMPLE_RATE_KCONTROL);
/* Index list for the values that has if (DPLL Locked) condition */
static u8 srpc_dpll_locked[] = { 0x0, 0x1, 0x2, 0x3, 0x4, 0xa, 0xb };
static int fsl_spdif_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
{
struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
struct fsl_spdif_priv *spdif_priv = snd_soc_dai_get_drvdata(snd_soc_rtd_to_cpu(rtd, 0));
struct spdif_mixer_control *ctrl = &spdif_priv->fsl_spdif_control;
struct platform_device *pdev = spdif_priv->pdev;
u32 sample_rate = params_rate(params);
int ret = 0;
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
ret = spdif_reparent_rootclk(spdif_priv, sample_rate);
if (ret) {
dev_err(&pdev->dev, "%s: reparent root clk failed: %d\n",
__func__, sample_rate);
return ret;
}
ret = spdif_set_sample_rate(substream, sample_rate);
if (ret) {
dev_err(&pdev->dev, "%s: set sample rate failed: %d\n",
__func__, sample_rate);
return ret;
}
spdif_set_cstatus(ctrl, IEC958_AES3_CON_CLOCK,
IEC958_AES3_CON_CLOCK_1000PPM);
spdif_write_channel_status(spdif_priv);
} else {
/* Setup rx clock source */
ret = spdif_set_rx_clksrc(spdif_priv, SPDIF_DEFAULT_GAINSEL, 1);
}
return ret;
}
/* Set SPDIF PhaseConfig register for rx clock */
static int spdif_set_rx_clksrc(struct fsl_spdif_priv *spdif_priv,
enum spdif_gainsel gainsel, int dpll_locked)
{
struct regmap *regmap = spdif_priv->regmap;
u8 clksrc=spdif_priv->rxclk_src;
if (clksrc >= SRPC_CLKSRC_MAX || gainsel >= GAINSEL_MULTI_MAX)
return -EINVAL;
regmap_update_bits(regmap, REG_SPDIF_SRPC,
SRPC_CLKSRC_SEL_MASK | SRPC_GAINSEL_MASK,
SRPC_CLKSRC_SEL_SET(clksrc) | SRPC_GAINSEL_SET(gainsel));
return 0;
}
/*
* Get DPLL lock or not info from stable interrupt status register.
* User application must use this control to get locked,
* then can do next PCM operation
*/
static int fsl_spdif_rxrate_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kcontrol);
struct fsl_spdif_priv *spdif_priv = snd_soc_dai_get_drvdata(cpu_dai);
int rate = 0;
if (spdif_priv->dpll_locked)
rate = spdif_get_rxclk_rate(spdif_priv, SPDIF_DEFAULT_GAINSEL);
ucontrol->value.integer.value[0] = rate;
return 0;
}
/* Get RX data clock rate given the SPDIF bus_clk */
static int spdif_get_rxclk_rate(struct fsl_spdif_priv *spdif_priv,
enum spdif_gainsel gainsel)
{
struct regmap *regmap = spdif_priv->regmap;
struct platform_device *pdev = spdif_priv->pdev;
u64 tmpval64, busclk_freq = 0;
u32 freqmeas, phaseconf;
u8 clksrc;
regmap_read(regmap, REG_SPDIF_SRFM, &freqmeas);
regmap_read(regmap, REG_SPDIF_SRPC, &phaseconf);
clksrc=(phaseconf >> SRPC_CLKSRC_SEL_OFFSET) & 0xf;
/* Get bus clock from system */
if (srpc_dpll_locked[clksrc] && (phaseconf & SRPC_DPLL_LOCKED))
busclk_freq = clk_get_rate(spdif_priv->sysclk);
/* FreqMeas_CLK = (BUS_CLK * FreqMeas) / 2 ^ 10 / GAINSEL / 128 */
tmpval64 = (u64) busclk_freq * freqmeas;
do_div(tmpval64, gainsel_multi[gainsel] * 1024);
do_div(tmpval64, 128 * 1024);
dev_dbg(&pdev->dev, "FreqMeas: %d\n", freqmeas);
dev_dbg(&pdev->dev, "BusclkFreq: %lld\n", busclk_freq);
dev_dbg(&pdev->dev, "RxRate: %lld\n", tmpval64);
return (int)tmpval64;
}
Best Regards,
Zhiming
>> Make sure the DPLL is locked
I absolutely did do this, as you can see in my query. Not only did I verify the DPLL was locked, I also verified that FreqMeas is measuring the exact right frequency of the RX signal. Look forward to hearing more.
Original query:
"3-Plug in SPDIF_IN cable and Verify PLL lock: Measrure SPDIF_Tx Biphase clock = 24.000 Mhz (according to the diagram above, this should be 24.576 Hz (the frequency of the measured PLL from SPDIF_IN)"