AnsweredAssumed Answered

ESAI peripheral generating incorrect I2S frequencies

Question asked by Remington Furman on Jun 24, 2019
Latest reply on Jul 2, 2019 by Remington Furman

I'm having trouble getting my ESAI peripheral to generate the right I2S clock frequencies on the SCK, BCK, and LRCK pins going to my codec.  The codec is a TI PCM1753 and it can only be a clock slave, so the ESAI has to be the master and generate SCK.


I think I have the clocks configured correctly in the device tree and my drivers, but when I scope the pins they are wildly different from the target frequencies.


As an example, to play stereo audio at a 8kHz sample rate I need:

LRCK =    8000 Hz
BCK  =  512000 Hz (LRCK * 2 channels * 32 bits per channel)
SCK  = 1024000 Hz (LRCK * 128)


When I probe the pins I currently get these frequencies (with a lot of jitter):
LRCK =   ~220 Hz

BCK = ~670000 Hz

SCK = 0 Hz (stuck high)


My main question is why are these values so different, and what would cause a LRCK to be so low?


I suppose the answer might be found in the actual register values in the ESAI, but so far I've only tackled the problem through the Linux 4.9.9 source code and my board's device tree config.  I've been using a JTAG debugger to learn help sort this out.


I have a .dts file that configures the ESAI_EXTAL clock to use PLL4 and OSC as the source clock.   The target frequency for EXTAL is 983040000Hz (~983MHz) so that the ESAI can divide down to the I2S SCK values I would like (128 times 8kHz, 16kHz, or 48kHz).


&esai {
    pinctrl-names = "default";
    pinctrl-0 = <&pinctrl_esai>;
    fsl,fifo-depth = <128>;

    assigned-clocks = <&clks IMX6QDL_CLK_ESAI_SEL>,
                      <&clks IMX6QDL_CLK_PLL4_POST_DIV>,
                      <&clks IMX6QDL_CLK_ESAI_EXTAL>;
    assigned-clock-parents = <&clks IMX6QDL_CLK_PLL4_AUDIO_DIV>;
    assigned-clock-rates = <983040000>, <983040000>, <983040000>;

    status = "okay";

&clks {
    assigned-clocks = <&clks IMX6QDL_CLK_PLL4>,
                      <&clks IMX6QDL_PLL4_BYPASS>;
    assigned-clock-parents = <&clks IMX6QDL_CLK_OSC>,
                             <&clks IMX6QDL_CLK_PLL4>;
    assigned-clock-rates = <983040000>, <983040000>;

        pinctrl_esai: esaigrp {
            fsl,pins = <
                MX6QDL_PAD_GPIO_4__ESAI_TX_HF_CLK   0x1b030
                MX6QDL_PAD_GPIO_17__ESAI_TX0        0x1b030
                MX6QDL_PAD_GPIO_6__ESAI_TX_CLK      0x1b030
                MX6QDL_PAD_GPIO_2__ESAI_TX_FS       0x1b030

At runtime, cat /sys/kernel/debug/clk/clk_summary shows that the EXTAL clock is indeed set to 983MHz:

    pll4                                  0            0   983040000          0 0  
       pll4_bypass                        0            0   983040000          0 0  
          pll4_audio                      0            0   983040000          0 0  
             pll4_post_div                0            0   983040000          0 0  
                pll4_audio_div            0            0   983040000          0 0  
                   esai_sel               0            0   983040000          0 0  
                      esai_pred           0            0   983040000          0 0  
                         esai_podf           0            0   983040000          0 0  
                            esai_extal           0            0   983040000          0 0


I believe 938MHz is allowed for the ESAI EXTAL clock frequency, but I can't find documentation on the allowed frequencies in the reference manual.  It does say that PLL4 can go from 650 MHz to 1300 MHz, but is that whole range valid for the ESAI?


I am using the simple-card.c ASoC driver instead of writing my own ASoC machine driver.  I don't think I need the ASRC peripheral, since all the audio sample rates I need can be divided from a 983040000Hz.  (I have a spreadsheet to double check that math.)  The function fsl_esai_set_dai_sysclk() in fsl_esai.c does not return an error with this EXTAL frequency, so I believe that it is able to set the dividers to generate SCK correctly.


This is the device tree config I am using for that driver:

    sound {

        compatible = "simple-audio-card";

        simple-audio-card,name = "Texas Instruments PCM1753 Audio";

        simple-audio-card,format = "i2s";

        simple-audio-card,widgets = "Speaker", "Internal Speaker"; /* template, widget name */

        simple-audio-card,routing = "Internal Speaker", "VOUTL",   /* sink, source */

                                    "Internal Speaker", "VOUTR";

        simple-audio-card,bitclock-master = <&dailink0_master>;

        simple-audio-card,frame-master = <&dailink0_master>;

        simple-audio-card,mclk-fs = <128>; /* 128, 192, 256, 384, 512, 768, 1152 */

        status = "okay";

        dailink0_master: simple-audio-card,cpu {

            sound-dai = <&esai>;

            dai-tdm-slot-num = <2>;    /* stereo, left and right channels */

            dai-tdm-slot-width = <32>; /* 32 bit clocks per channel */


        simple-audio-card,codec {

            sound-dai = <&pcm1753>;



I'm setting the slot-num and slot-width parameters above so that the BCK is 64*LRCK, as required by my codec.


Please let me know if you have any suggestions or see anything incorrect in what I've shared so far.


Thank you,

Remington Furman


Board details:

Custom board

NXP iMX6 Solo
TI PCM1753 Codec
Linux kernel version 4.9.9
Built with Yocto