This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

am335x new audio codec integration

Hi All,

I've posted this again only because I cannot seem to find my original one...

------------------
We have a new AM335x based design that utilizes a wm8737 codec. After reviewing a number of the related forum posts for new codec integration, the Sitara Linux Audio DAC Example, AM335x Audio Driver's Guide and a steep learning curve it appears much is in place. We are now at  point when attempting arecord we see the following error

arecord: pcm_read:2031: read error: Input/output error

We also built the user land record app suggested in the AM335x Audio Driver's Guide and it fails with the same i/o error. The driver for the wm8737 has been part of the 3.1x kernel. The codec is configured for master / I2S mode and the pins are muxed and shown below. Probing the signals look generally correct, the wm8737 is in master mode, the I2C communication is functional and a register dump shows the codec is in the expected state.

At this point the focus is on the machine layer and am looking for some input on how to proceed with debugging this. I am not sure it I have the wiget routing correct and to verify I have the serial-dir in the DTS correct. Any Input would be desired. Thanks, Tom


HW ==========================================

MCASP0 <-->        wm8737

MCASP0_AHCLKR (out) <--> 12MHZ (input)
MCASP0_ACLKR (in) <--> MIC_BCLK (output)
MCASP0_FSR (in) <--> MIC_FRAME (output)
MCASP0_AXR1 (in) <--> MIC_DATA (output)

Device Tree ================================

sound {
    compatible = "ti,wm8737-evm-audio";
    ti,model = "AM335x-EVM";
    ti,audio-codec = <&wm8737>;
    ti,mcasp-controller = <&mcasp0>;
    ti,codec-clock-rate = <12000000>;
     ti,audio-routing =
        "LINPUT1", "Line In",
        "RINPUT1", "Line In";
        clock-names = "mclk";
};
    am335x_evm_audio_pins: am335x_evm_audio_pins {
        pinctrl-single,pins = <
            0x1ac ( PIN_INPUT_PULLDOWN | MUX_MODE0 ) /* (A14) mcasp0_ahclkx.mcasp0_ahclkx */
            0x19c ( PIN_INPUT_PULLDOWN | MUX_MODE0 ) /* (C12) mcasp0_ahclkr.mcasp0_ahclkr */
            0x190 ( PIN_INPUT_PULLDOWN | MUX_MODE0 ) /* (A13) mcasp0_aclkx.mcasp0_aclkx */
            0x194 ( PIN_INPUT_PULLDOWN | MUX_MODE0 ) /* (B13) mcasp0_fsx.mcasp0_fsx */
            0x1a0 ( PIN_INPUT_PULLDOWN | MUX_MODE0 ) /* (B12) mcasp0_aclkr.mcasp0_aclkr */
            0x1a4 ( PIN_INPUT_PULLDOWN | MUX_MODE0 ) /* (C13) mcasp0_fsr.mcasp0_fsr */
            0x198 ( PIN_INPUT_PULLDOWN | MUX_MODE0 ) /* (D12) mcasp0_axr0.mcasp0_axr0 */
            0x1a8 ( PIN_INPUT_PULLDOWN | MUX_MODE0 ) /* (D13) mcasp0_axr1.mcasp0_axr1 */
            /* GPIO I2C tranceiver enable */
            0x28 ( PIN_OUTPUT | MUX_MODE7 ) /* (T11) gpmc_ad10.gpio0[26] */
        >;
    };

    am335x_evm_audio_pins_sleep: am335x_evm_audio_pins_sleep {
        pinctrl-single,pins = <
            0x1ac ( PIN_INPUT_PULLDOWN | MUX_MODE0 ) /* (A14) mcasp0_ahclkx.mcasp0_ahclkx */
            0x19c ( PIN_INPUT_PULLDOWN | MUX_MODE0 ) /* (C12) mcasp0_ahclkr.mcasp0_ahclkr */
            0x190 ( PIN_INPUT_PULLDOWN | MUX_MODE0 ) /* (A13) mcasp0_aclkx.mcasp0_aclkx */
            0x194 ( PIN_INPUT_PULLDOWN | MUX_MODE0 ) /* (B13) mcasp0_fsx.mcasp0_fsx */
            0x1a0 ( PIN_INPUT_PULLDOWN | MUX_MODE0 ) /* (B12) mcasp0_aclkr.mcasp0_aclkr */
            0x1a4 ( PIN_INPUT_PULLDOWN | MUX_MODE0 ) /* (C13) mcasp0_fsr.mcasp0_fsr */
            0x198 ( PIN_INPUT_PULLDOWN | MUX_MODE0 ) /* (D12) mcasp0_axr0.mcasp0_axr0 */
            0x1a8 ( PIN_INPUT_PULLDOWN | MUX_MODE0 ) /* (D13) mcasp0_axr1.mcasp0_axr1 */
            /* GPIO I2C tranceiver enable */
            0x28 ( PIN_OUTPUT | MUX_MODE7 ) /* (T11) gpmc_ad10.gpio0[26] */
        >;
    };


&mcasp0 {
    pinctrl-names = "default", "sleep";
    pinctrl-0 = <&am335x_evm_audio_pins>;
    pinctrl-1 = <&am335x_evm_audio_pins_sleep>;

    status = "okay";

    op-mode = <0>;          /* MCASP_IIS_MODE */
    tdm-slots = <2>;
    /* 4 serializers */
    serial-dir = <  /* 0: INACTIVE, 1: TX, 2: RX */
        0 2 0 0
    >;
    tx-num-evt = <32>;
    rx-num-evt = <32>;
};

MACHINE LAYER ================


/* add for wm8737 configure hw_params */
static struct snd_soc_ops wm8737_ops = {
    .startup = evm_startup,
    .shutdown = evm_shutdown,
// XXX conflicts with wm8737_hw_params in the driver .hw_params = wm8737_hw_params,
};

/* wm8737 widgets */
static const struct snd_soc_dapm_widget wm8737_dapm_widgets[] = {
    SND_SOC_DAPM_MIC("Mic Jack", NULL),
    SND_SOC_DAPM_LINE("Line In", NULL),
};

/* also added  the following */
static int evm_wm8737_init(struct snd_soc_pcm_runtime *rtd) {

    struct snd_soc_card *card = rtd->card;
    struct snd_soc_codec *codec = rtd->codec;
    struct device_node *np = card->dev->of_node;
    int ret;

    unsigned sysclk;
    struct snd_soc_dai *codec_dai = rtd->codec_dai;
    struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
    struct snd_soc_card *soc_card = codec->card;

    /* Add davinci-evm specific widgets */
    snd_soc_dapm_new_controls(&card->dapm, wm8737_dapm_widgets,
                  ARRAY_SIZE(wm8737_dapm_widgets));

    printk("TR_DBG: evm_wm8737_init of_node [%p]\n", np);

    if (np) {
        ret = snd_soc_of_parse_audio_routing(card, "ti,audio-routing");
        if (ret)
            return ret;
    } else {
        /* Set up davinci-evm specific audio path audio_map */
        snd_soc_dapm_add_routes(&card->dapm, audio_map,
                    ARRAY_SIZE(audio_map));
    }


    snd_soc_dapm_nc_pin(&codec->dapm, "LINPUT1");
    snd_soc_dapm_nc_pin(&codec->dapm, "RINPUT1");

    /* wm8737 moved clock init to here since the hw_params conflicted with the wm8737 driver */
#if 1

    sysclk = ((struct snd_soc_card_drvdata_davinci *)
               snd_soc_card_get_drvdata(soc_card))->sysclk;

    printk("TR_DBG: evm_wm8737_init sysclk [%u]\n", sysclk);

    ret = snd_soc_dai_set_sysclk(codec_dai, 0, sysclk, SND_SOC_CLOCK_OUT);
    if (ret < 0) {
      dev_err(codec_dai->platform->dev, "TR_DBG evm_wm8737_init failed to set codec_dai clock: %d.\n",ret);
       return ret;
    }

    ret = snd_soc_dai_set_sysclk(cpu_dai, 0, sysclk, SND_SOC_CLOCK_OUT);
    if (ret < 0) {
      dev_err(codec_dai->platform->dev, "TR_DBG evm_wm8737_init failed to set cpu_dai clock: %d.\n",ret);
      return ret;
    }

#endif


}

  • Hi Tom,

    I will forward this to the SW team.

  • Hi Tom,

    Serial-dir & davinci_evm widgets look OK.

    Can you run arecord -vvv /dev/NULL & arecord -vvv -D plughw:0 /dev/null ?

    Also check the output of amixer, maybe it is a problem with amixer settings.

    Best Regards,
    Yordan
  • Hi Yordan, Thanks for the suggestions. Here's the output , I had to cntl-c it to stop it. Tom

    root@scepter2_sdcard:~# arecord -vvv /dev/NULL
    Recording WAVE '/dev/NULL' : Unsigned 8 bit, Rate 8000 Hz, Mono
    [   95.972859] TR_DBG: wm8737_hw_params codec [wm8737.0-001a] set_sysclk ret [0]
    [   95.980481] TR_DBG: wm8737_hw_params -- clocking [0x9]
    [   95.987003] TR_DBG: snd_soc_update_bits using_regmap change [1] ret[0]
    [   95.994532] TR_DBG: snd_soc_update_bits using_regmap change [1] ret[0]
    [   96.001900] TR_DBG: snd_soc_update_bits using_regmap change [1] ret[0]
    [   96.009128] wm8737 0-001a: TR_DBG: I2C read 0x0 = 0x1db
    [   96.014625] TR_DBG: wm8737_hw_params -- REG [0x0] REG Val [0x1db]
    [   96.021496] wm8737 0-001a: TR_DBG: I2C read 0x1 = 0x1db
    [   96.027040] TR_DBG: wm8737_hw_params -- REG [0x1] REG Val [0x1db]
    [   96.033702] wm8737 0-001a: TR_DBG: I2C read 0x2 = 0x7
    [   96.039055] TR_DBG: wm8737_hw_params -- REG [0x2] REG Val [0x7]
    [   96.045940] wm8737 0-001a: TR_DBG: I2C read 0x3 = 0x7
    [   96.051315] TR_DBG: wm8737_hw_params -- REG [0x3] REG Val [0x7]
    [   96.057819] wm8737 0-001a: TR_DBG: I2C read 0x4 = 0x0
    [   96.063129] TR_DBG: wm8737_hw_params -- REG [0x4] REG Val [0x0]
    [   96.070218] wm8737 0-001a: TR_DBG: I2C read 0x5 = 0x0
    [   96.075532] TR_DBG: wm8737_hw_params -- REG [0x5] REG Val [0x0]
    [   96.082206] wm8737 0-001a: TR_DBG: I2C read 0x6 = 0x1c0
    [   96.087749] TR_DBG: wm8737_hw_params -- REG [0x6] REG Val [0x1c0]
    [   96.094405] wm8737 0-001a: TR_DBG: I2C read 0x7 = 0x42
    [   96.099847] TR_DBG: wm8737_hw_params -- REG [0x7] REG Val [0x42]
    [   96.106875] wm8737 0-001a: TR_DBG: I2C read 0x8 = 0x9
    [   96.112187] TR_DBG: wm8737_hw_params -- REG [0x8] REG Val [0x9]
    [   96.118909] wm8737 0-001a: TR_DBG: I2C read 0x9 = 0xf
    [   96.124221] TR_DBG: wm8737_hw_params -- REG [0x9] REG Val [0xf]
    [   96.131598] TR_DBG: snd_soc_update_bits using_regmap change [0] ret[0]
    Plug PCM: Route conversion PCM (sformat=S16_LE)
      Transformation table:
        0 <- 0*0.5 + 1*0.5
    Its setup is:
      stream       : CAPTURE
      access       : RW_INTERLEAVED
      format       : U8
      subformat    : STD
      channels     : 1
      rate         : 8000
      exact rate   : 8000 (8000/1)
      msbits       : 8
      buffer_size  : 4000
      period_size  : 1000
      period_time  : 125000
      tstamp_mode  : NONE
      period_step  : 1
      avail_min    : 1000
      period_event : 0
      start_threshold  : 1
      stop_threshold   : 4000
      silence_threshold: 0
      silence_size : 0
      boundary     : 2097152000
    Slave: Hardware PCM card 0 'AM335x-EVM' device 0 subdevice 0
    Its setup is:
      stream       : CAPTURE
      access       : MMAP_INTERLEAVED
      format       : S16_LE
      subformat    : STD
      channels     : 2
      rate         : 8000
      exact rate   : 8000 (8000/1)
      msbits       : 16
      buffer_size  : 4000
      period_size  : 1000
      period_time  : 125000
      tstamp_mode  : NONE
      period_step  : 1
      avail_min    : 1000
      period_event : 0
      start_threshold  : 1
      stop_threshold   : 4000
      silence_threshold: 0
      silence_size : 0
      boundary     : 2097152000
      appl_ptr     : 0
      hw_ptr       : 0


    ^CAborted by signal Interrupt...
    arecord: pcm_rea[  145.038894] TR_DBG: snd_soc_update_bits using_regmap change [0] ret[0]
    d:2031: read error: Interrupted system call
    [  145.050709] TR_DBG: snd_soc_update_bits using_regmap change [0] ret[0]
    root@scepter2_sdcard:~# arecord -vvv -D plughw:0 /dev/NULL                                                                                                          
    Recording WAVE '/dev/NULL' : Unsigned 8 bit, Rate 8000 Hz, Mono
    [  169.232898] TR_DBG: wm8737_hw_params codec [wm8737.0-001a] set_sysclk ret [0]
    [  169.240521] TR_DBG: wm8737_hw_params -- clocking [0x9]
    [  169.246769] TR_DBG: snd_soc_update_bits using_regmap change [0] ret[0]
    [  169.253638] TR_DBG: snd_soc_update_bits using_regmap change [0] ret[0]
    [  169.261636] TR_DBG: snd_soc_update_bits using_regmap change [1] ret[0]
    [  169.268899] wm8737 0-001a: TR_DBG: I2C read 0x0 = 0x1db
    [  169.274396] TR_DBG: wm8737_hw_params -- REG [0x0] REG Val [0x1db]
    [  169.281280] wm8737 0-001a: TR_DBG: I2C read 0x1 = 0x1db
    [  169.286825] TR_DBG: wm8737_hw_params -- REG [0x1] REG Val [0x1db]
    [  169.293494] wm8737 0-001a: TR_DBG: I2C read 0x2 = 0x7
    [  169.298846] TR_DBG: wm8737_hw_params -- REG [0x2] REG Val [0x7]
    [  169.305740] wm8737 0-001a: TR_DBG: I2C read 0x3 = 0x7
    [  169.311099] TR_DBG: wm8737_hw_params -- REG [0x3] REG Val [0x7]
    [  169.317599] wm8737 0-001a: TR_DBG: I2C read 0x4 = 0x0
    [  169.322910] TR_DBG: wm8737_hw_params -- REG [0x4] REG Val [0x0]
    [  169.329824] wm8737 0-001a: TR_DBG: I2C read 0x5 = 0x0
    [  169.335137] TR_DBG: wm8737_hw_params -- REG [0x5] REG Val [0x0]
    [  169.341788] wm8737 0-001a: TR_DBG: I2C read 0x6 = 0x1c0
    [  169.347343] TR_DBG: wm8737_hw_params -- REG [0x6] REG Val [0x1c0]
    [  169.354049] wm8737 0-001a: TR_DBG: I2C read 0x7 = 0x42
    [  169.359494] TR_DBG: wm8737_hw_params -- REG [0x7] REG Val [0x42]
    [  169.366483] wm8737 0-001a: TR_DBG: I2C read 0x8 = 0x9
    [  169.371845] TR_DBG: wm8737_hw_params -- REG [0x8] REG Val [0x9]
    [  169.378348] wm8737 0-001a: TR_DBG: I2C read 0x9 = 0xf
    [  169.383657] TR_DBG: wm8737_hw_params -- REG [0x9] REG Val [0xf]
    [  169.391272] TR_DBG: snd_soc_update_bits using_regmap change [0] ret[0]
    Plug PCM: Route conversion PCM (sformat=S16_LE)
      Transformation table:
        0 <- 0*0.5 + 1*0.5
    Its setup is:
      stream       : CAPTURE
      access       : RW_INTERLEAVED
      format       : U8
      subformat    : STD
      channels     : 1
      rate         : 8000
      exact rate   : 8000 (8000/1)
      msbits       : 8
      buffer_size  : 4000
      period_size  : 1000
      period_time  : 125000
      tstamp_mode  : NONE
      period_step  : 1
      avail_min    : 1000
      period_event : 0
      start_threshold  : 1
      stop_threshold   : 4000
      silence_threshold: 0
      silence_size : 0
      boundary     : 2097152000
    Slave: Hardware PCM card 0 'AM335x-EVM' device 0 subdevice 0
    Its setup is:
      stream       : CAPTURE
      access       : MMAP_INTERLEAVED
      format       : S16_LE
      subformat    : STD
      channels     : 2
      rate         : 8000
      exact rate   : 8000 (8000/1)
      msbits       : 16
      buffer_size  : 4000
      period_size  : 1000
      period_time  : 125000
      tstamp_mode  : NONE
      period_step  : 1
      avail_min    : 1000
      period_event : 0
      start_threshold  : 1
      stop_threshold   : 4000
      silence_threshold: 0
      silence_size : 0
      boundary     : 2097152000
      appl_ptr     : 0
      hw_ptr       : 0
    ^CAborted by signal Interrupt...
    arecord: pcm_rea[  215.421989] TR_DBG: snd_soc_update_bits using_regmap change [0] ret[0]
    d:2031: read error: Interrupted system call
    [  215.433798] TR_DBG: snd_soc_update_bits using_regmap change [0] ret[0]

  • Hi,

    So with the codec as a master all of the signals sitting at the mcasp0 pins. Is there a way to verify at a low level if there is any action in the mcasp? ie: registers to poll, etc? Thanks, Tom
  • Also would like to verify the proper understanding of the clocking. We are using the cpu's clock as a 12Mhz output to drive the codec in Master mode.

    MCASP0_AHCLKR (out) <--> 12MHZ (wm8737 MCLK input)

    We set the sysclk here

    sysclk = ((struct snd_soc_card_drvdata_davinci *) snd_soc_card_get_drvdata(soc_card))->sysclk;

    [ 8.116785] TR_DBG: evm_wm8737_init sysclk [12000000]

    The MCASP0_AHCLKR pin has a 12Mhz clock on it
    ----
    ret = snd_soc_dai_set_sysclk(cpu_dai, 0, sysclk, SND_SOC_CLOCK_OUT);

    This line is making sure that ALSA knows that we will be using the internal 12 MHz clock as sysclk for the McASP
    ---
    ret = snd_soc_dai_set_sysclk(codec_dai, 0, sysclk, SND_SOC_CLOCK_OUT);

    set the codec system clock
    ---

    We are not changing the clock divider as we do not call the function as below

    ret = snd_soc_dai_set_clkdiv(cpu_dai, 0, 2);

    Since the sysclk is 12mhz and measured as such does the mcasp see 12mhz or 24Mhz? According to this forum post it should be left alone... correct?

    e2e.ti.com/.../1392297
  • Hi,


    I am really hoping to get some input here on the mcasp setup. While we initially had the codec as the master, we are reconfiguring now to run in slave mode such as to verify that the mcasp is functional. So we put the codec in slave mode then made the following changes in the machine layer davinci-evm.c

    Switching the above snd_soc_dai_set_sysclk calls to SND_SOC_CLOCK_IN

    changed the

    .dai_fmt  = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBS_CFS | /* changed to slave */ SND_SOC_DAIFMT_NB_NF

    and configured the DTS pins to            

    0x1a0 ( PIN_OUTPUT_PULLDOWN | MUX_MODE0 ) /* (B12) mcasp0_aclkr.mcasp0_aclkr */
    0x1a4 ( PIN_OUTPUT_PULLDOWN | MUX_MODE0 ) /* (C13) mcasp0_fsr.mcasp0_fsr */

    We now get the expected BCLK output on ACLKR, and it scales according to the rate, but there is no output on FSR (LRCLK). What am I missing, Please advise? Thanks Tom

  • Hi Tom,

    Sorry for the delayed response.

    "According to this forum post it should be left alone... correct?"
    Yes, this is correct. You can also refer to the audio dac example.

    "We now get the expected BCLK output on ACLKR, and it scales according to the rate, but there is no output on FSR (LRCLK). What am I missing, Please advise? "
    I am not familiar with the used codec, but have you tried setting different ACLKX/R & FSX/R polarity (you have the following options: SND_SOC_DAIFMT_NB_NF, SND_SOC_DAIFMT_IB_NF, SND_SOC_DAIFMT_IB_I, SND_SOC_DAIFMT_NB_IF) ?

    Also I see you've configured 2 AXRs on device pads: AXR0 & AXR1, but later in the dts you use only AXR1 as RX:
    serial-dir = < /* 0: INACTIVE, 1: TX, 2: RX */
    0 2 0 0
    >;
    Can you add the AXR1 as TX, serial dir -> <1 2 0 0>?

    Best Regards,
    Yordan
  • Hi Yordan, Thanks for the input. Here are a few follow-on questions as I need additional clarification to assist  in my understanding. 

    First we have an ADC based codec only (no DAC), even though the DTS pinmap has the Tx side of the mcasp0 enabled, the 4 Tx related pins are just taken out to testpoints, the MCASP0_AHCLKX currently is not driven. I am thinking that this is ok to do. We have now modified one of our boards with an external 12.288Mhz oscillator and want to run the mcasp as a master and the codec as the slave.    

    We make two calls to sysclk setup that reference cpu_dai & the codec_dai, and in our app we set sysclk = 12288000 via the DTS ti,codec-clock-rate property

    snd_soc_dai_set_sysclk(cpu_dai, 0, sysclk, SND_SOC_CLOCK_IN);

    snd_soc_dai_set_sysclk(codec_dai, 0, sysclk, SND_SOC_CLOCK_IN);

    What specifically does each do? So I'm thinking the cpu_dai sets the McASP system clock direction so that the mcasp_AHCLKR is expecting an input of 12.288Mhz. The codec_dai would set the mcasp_AHCLKX as an input and expected a  12.288Mhz. clock? These are set with SND_SOC_CLOCK_IN Otherwise stated these calls are individual setups for the Tx & the Rx sides of the mcasp, correct?

    The current observations are that we see a 12.288Mhz BCLK during "idle" and a correctly scaled BCLK from McASP_ACLKR when we run arecord. Is this what would be the expected behavior?


    Next is the McASP_FSR, we set the pin as an output in the DTS file and the SND_SOC_DAIFMT_CBS_CFS with the dai_fmt to tell the mcasp the codec is a slave. Is there anything else that would effect the FSR signal from being output? I assume since we get the right BLCK that the McASP is setup properly but perhaps I'm missing something else. The codec datasheet calls for std polarities on it's I2S frame sync., but will test you suggestion later today

    Thanks, Tom

  • Hi Again,


    I have tried setting AXR0 below with no luck

        /* 4 serializers */
        serial-dir = <  /* 0: INACTIVE, 1: TX, 2: RX - 0 2 0 0 */
            1 2 0 0

    When selecting  SND_SOC_DAIFMT_CBS_CFS for the mcasp dai_fmt. The AFSRCTL Register sees bit 1 FSRM = 1 for Internally-generated receive frame sync, bit 4 FRWID=1 for Single word. RMOD field is set for burst mode. This would appear to be correct when considering Figure 22-19. Frame Sync Generator Block Diagram of the TRM.

    Now here's another question. Table 22-39. ACLKXCTL Register Field Descriptions bit 6 - Transmit/receive operation asynchronous enable bit. Since we only are using the Rx side I would think we need to be in async mode 1h = Asynchronous. Separate clock and frame sync used by transmit and receive sections. Please confirm, Thanks, Tom