Configure external audio codec (Wolfson WM8978)

Good Morning everyone,

I am a software developer at Reichert GmbH in Essen Germany. We use the Wolfson WM8978 as an audio codec for a lot of our devices and use the Toradex colibri module with the iMX6DL in some other embedded devices of ours. Now we are trying to build a new device which combines different features and needs to connect the WM8978 to the iMX6DL.

I am using the codec driver from 2009 which is part of the mainline kernel. The codecs clock is at 24 MHz and I need to configure the needed registers. I know the bare metal configuration for the soc, but I am missing out how to configure the needed parameters with the mentioned codec driver.
The Angstrom linux recognizes the devicetree entries and is loading the right module. The codec driver is also configuring the device, but disabling most of the functions (including the PLL). When trying to play a wave file with aplay following errors occur:

[ 5373.680824] fsl-ssi-dai 202c000.ssi: ASoC: can't set 202c000.ssi hw params: -22
ALSA lib ../../../alsa-lib-1.1.4.1/src/pcm/pcm_direct.c:1271:(snd1_pcm_direct_initialize_slave) unable to install hw params
ALSA lib ../../../alsa-lib-1.1.4.1/src/pcm/pcm_dmix.c:1111:(snd_pcm_dmix_open) unable to initialize slave
aplay: main:807: audio open error: Invalid argument

We connect the audio codec via I2C and four wire I2S. Below is the configuration from our devicetree.

/*
 * Audio:
 * Audio Multiplexer mit Audiokanal 4
 * I2C mit Wolfson WM8978
 */
/ {
    sound2 {
        compatible = "simple-audio-card";
        simple-audio-card,name = "Wolfson WM8978";
        simple-audio-card,format = "i2s";
        simple-audio-card,widgets =
            "Microphone", "AMIC",
            "Headphone", "Headphone Jack",
            "Speaker", "Line Out Jack";
        simple-audio-card,routing =
			"LMICN", "AMIC",
            "RMICN", "AMIC",
            "LMICP", "Mic Bias",
            "RMICP", "Mic Bias",
			"Headphone Jack", "LHP",
            "Headphone Jack", "RHP",
            "Line Out Jack", "LSPK",
            "Line Out Jack", "RSPK";

        simple-audio-card,cpu {
            sound-dai = <&ssi2 0>;
        };
        simple-audio-card,codec {
            sound-dai = <&wm8978 0>;
            clocks = <&clk24m>;
            /*system-clock-frequency = <24000000>;*/
        };
    };
};
&iomuxc {
    imx6dl-colibri {
        pinctrl_audmux4: audmux4grp {
            fsl,pins = <
                MX6QDL_PAD_DISP0_DAT20__AUD4_TXC  0x130b0
                MX6QDL_PAD_DISP0_DAT21__AUD4_TXD  0x130b0
                MX6QDL_PAD_DISP0_DAT22__AUD4_TXFS 0x130b0
                MX6QDL_PAD_DISP0_DAT23__AUD4_RXD  0x130b0
            >;
        };
        pinctrl_i2c4: i2c4grp {
			fsl,pins = <
				MX6QDL_PAD_NANDF_WP_B__I2C4_SCL 0x4001b8b1
				MX6QDL_PAD_NANDF_CS3__I2C4_SDA 0x4001b8b1
			>;
		};
    };
};
&audmux {
	pinctrl-names = "default";
	pinctrl-0 = <&pinctrl_audmux4>;
	status = "okay";

    /*
     * SSI2 wird auf Port 4 gemapped
     * PTCR2: 0x0000 0800
     * TFSDIR TFSEL TCLKDIR TCSEL RFSDIR RFSEL RCLKDIR RCSEL SYN RESERVED
     * 0      0000  0       0000  1      0011  1       0011  0   00000000000
     * PDCR2: 0x0000 6000
     * RESERVED RXDSEL TXRXEN RESERVED MODE INMASK
     * 0x0000   011    0      000      0    00000000
     * PTCR4: 0x8c40 0800
     * TFSDIR TFSEL TCLKDIR TCSEL RFSDIR RFSEL RCLKDIR RCSEL SYN RESERVED
     * 1      0001  1       0001  0      0000  0       0000  1   00000000000
     * PDCR4: 0x0000 2000
     * RESERVED RXDSEL TXRXEN RESERVED MODE INMASK
     * 0x0000   001    0      000      0    00000000
     */
    port2 {
        fsl,audmux-port = <2>;
        fsl,port-config = <0x00273000 0x00006000>;
    };
    port4 {
        fsl,audmux-port = <4>;
        fsl,port-config = <0x8c400800 0x00002000>;
    };
};
&ssi2 {
	fsl,mode = "i2s-master";
	status = "okay";
};
&i2c4 {
    clock-frequency = <400000>;
	pinctrl-names = "default";
	pinctrl-0 = <&pinctrl_i2c4>;
	status = "okay";

    wm8978: wm8978@1a {
        #sound-dai-cells = <0>;
        compatible = "wlf,wm8978";
	    reg = <0x1a>;
    };
};

My questions in detail are:

  1. Is there some sort of documentation for the driver? (Usage in the devicetree or in general)
  2. How to tell the device which systemclock ist used?
  3. Is it possible to use the WM8978 with ALSA and as simple-audio-card?
  4. If there is no documentation could you give me some sort of hind how to set up the device?

I am hoping for someone to have any experience with external audio codecs and how to configure them. I would be thankful for every help provided.

thanks in advance.

Best regards

Christoph Biethmann

Hi @cbiethmann

Thanks for writing to the Toradex Community!

  1. Is there some sort of documentation for the driver? (Usage in the devicetree or in general)

You can find the Documentation at Documentation/devicetree/bindings/sound/wlf,wm8974.txt and
Documentation/sound/alsa/soc/codec.txt.

  1. How to tell the device which systemclock ist used?

You need to define the clocks to the SSIx interface in the devicetree. Please check for example in arch/arm/boot/dts/imx6sl-evk.dts or grep for other examples.

  1. Is it possible to use the WM8978 with ALSA and as simple-audio-card?

So you want to have two different devices under Linux?

  1. If there is no documentation could you give me some sort of hind how to set up the device?

Please check above for the answer.

Best regards,
Jaski

First of all thank you Jaski for your comment. Truly I had to specify the clock for the ssi. But it had to be in the “simple-audio-card,cpu” subnode.

Two other things had to be corrected in the devicetree so that all signals were generated at the correct pin-outs. Below is the new version that worked as expected.

/*
 * Audio:
 * Audio Multiplexer mit Audiokanal 4
 * I2C mit Wolfson WM8978
 */
/ {
    sound-wm8978 {
        compatible = "simple-audio-card";
        simple-audio-card,name = "Wolfson WM8978";
        simple-audio-card,format = "i2s";
        simple-audio-card,widgets =
            "Microphone", "AMIC",
            "Headphone", "Headphone Jack",
            "Speaker", "Line Out Jack";
        simple-audio-card,routing =
			"LMICN", "AMIC",
            "RMICN", "AMIC",
			"Headphone Jack", "LSPK",
            "Headphone Jack", "RSPK",
            "Line Out Jack", "LHP",
            "Line Out Jack", "RHP";

        simple-audio-card,cpu {
            sound-dai = <&ssi2 0>;
            system-clock-frequency = <44100>;
        };
        simple-audio-card,codec {
            sound-dai = <&wm8978 0>;
            clocks = <&clk24m>;
        };
    };
};
&iomuxc {
    imx6dl-colibri {
        pinctrl_audmux4: audmux4grp {
            fsl,pins = <
                MX6QDL_PAD_DISP0_DAT20__AUD4_TXC  0x130b0
                MX6QDL_PAD_DISP0_DAT21__AUD4_TXD  0x130b0
                MX6QDL_PAD_DISP0_DAT22__AUD4_TXFS 0x130b0
                MX6QDL_PAD_DISP0_DAT23__AUD4_RXD  0x130b0
            >;
        };
        pinctrl_i2c4: i2c4grp {
			fsl,pins = <
				MX6QDL_PAD_NANDF_WP_B__I2C4_SCL 0x4001b8b1
				MX6QDL_PAD_NANDF_CS3__I2C4_SDA 0x4001b8b1
			>;
		};
    };
};
&audmux {
	pinctrl-names = "default";
	pinctrl-0 = <&pinctrl_audmux4 &pinctrl_audmux_mclk &pinctrl_mic_gnd>;
	status = "okay";

    /*
     * SSI2 wird auf Port 4 gemapped
     * PTCR2: 0x0000 0800
     * TFSDIR TFSEL TCLKDIR TCSEL RFSDIR RFSEL RCLKDIR RCSEL SYN RESERVED
     * 0      0000  0       0000  0      0000  0       0000  1   00000000000
     * PDCR2: 0x0000 6000
     * RESERVED RXDSEL TXRXEN RESERVED MODE INMASK
     * 0x0000   011    0      000      0    00000000
     * PTCR4: 0x8c40 0800 or 0x8c63 1000
     * TFSDIR TFSEL TCLKDIR TCSEL RFSDIR RFSEL RCLKDIR RCSEL SYN RESERVED
     * 1      0001  1       0001  0      0000  0       0000  1   00000000000
     * or
     * 1      0001  1       0001  1      0001  1       0001  0   00000000000
     * PDCR4: 0x0000 2000
     * RESERVED RXDSEL TXRXEN RESERVED MODE INMASK
     * 0x0000   001    0      000      0    00000000
     */
    port2 {
        fsl,audmux-port = <1>;
        fsl,port-config = <0x00000800 0x00006000>;
    };
    port4 {
        fsl,audmux-port = <3>;
        fsl,port-config = <0x8c400800 0x00002000>;
    };
};
&ssi2 {
	fsl,mode = "i2s-master";
	status = "okay";
};
&i2c4 {
    clock-frequency = <400000>;
	pinctrl-names = "default";
	pinctrl-0 = <&pinctrl_i2c4>;
	status = "okay";

    wm8978: wm8978@1a {
        #sound-dai-cells = <0>;
        compatible = "wlf,wm8978";
	    reg = <0x1a>;
    };
};

In the end it took me a while to figure out, that port 2 is actually number 1 because the audiomuxer starts counting internally with ‘0’.
Thanks again for your help.

Best regards,
Christoph

Hi Christoph,

Perfect that it works. Thanks for the sharing this answer.

Best regards,
Jaski

Hi Together,
for this simple AudioCard I need some further information.

My problem is as followed:

I can play an audio file on my imx6 but anyway it’s too slow.
When playing an audio file on my PC the file will be played inbetween 5 seconds. Doing the Same on the toradex module it takes 2 Minutes to play the same file.

As Master Clock we have connected a 24 Mhz external Clock.

PLL has following value: 86C227

N: 7

Prescale: 2

/*
 * Audio:
 * Audio Multiplexer mit Audiokanal 4
 * I2C mit Wolfson WM8978
 */
/ {
    sound-wm8978 {
        compatible = "simple-audio-card";
        simple-audio-card,name = "Wolfson WM8978";
        simple-audio-card,format = "i2s";
        simple-audio-card,widgets =
           // "AMIC", "Microphone",
            //"Headphone", "Headphone Jack",
            "Speaker", "Speaker";

           //Fehler nach einkommentieren! Die Route kann nicht aufgebaut werdden
        simple-audio-card,routing =
			"LMICN", "Microphone",
            "RMICN", "Microphone",
			/*"Headphone Jack", "LHP",
            "Headphone Jack", "RHP",*/
            /*"Speaker", "LSPK",
            "Speaker", "RSPK";*/
            "Speaker", "LSPK",
            "Speaker", "RSPK";

        simple-audio-card,cpu {
            sound-dai = <&ssi2 0>;
            system-clock-frequency = <48000>;
            bitclock-master;
            frame-master;
        };
        simple-audio-card,codec {
            sound-dai = <&wm8978 0>;
            //clocks = <&clk24m>;
            //system-clock-frequency = <12 288 000>;
            system-clock-frequency  = <24000000>;
        };
    };
};
&audmux {
	pinctrl-names = "default";
	pinctrl-0 = <&pinctrl_audmux4 &pinctrl_audmux_mclk &pinctrl_mic_gnd>;
	status = "okay";
    /*
     * SSI2 wird auf Port 4 gemapped
     * PTCR2: 0x0000 0800
     * TFSDIR TFSEL TCLKDIR TCSEL RFSDIR RFSEL RCLKDIR RCSEL SYN RESERVED
     * 0      0000  0       0000  0      0000  0       0000  1   00000000000
     * PDCR2: 0x0000 6000
     * RESERVED RXDSEL TXRXEN RESERVED MODE INMASK
     * 0x0000   011    0      000      0    00000000
     * PTCR4: 0x8c40 0800 or 0x8c63 1000
     * TFSDIR TFSEL TCLKDIR TCSEL RFSDIR RFSEL RCLKDIR RCSEL SYN RESERVED
     * 1      0001  1       0001  0      0000  0       0000  1   00000000000
     * or
     * 1      0001  1       0001  1      0001  1       0001  0   00000000000
     * PDCR4: 0x0000 2000
     * RESERVED RXDSEL TXRXEN RESERVED MODE INMASK
     * 0x0000   001    0      000      0    00000000
     */
    port2 {
        fsl,audmux-port = <1>;
        fsl,port-config = <0x00000800 0x00006000>;
    };
    port4 {
        fsl,audmux-port = <3>;
        fsl,port-config = <0x8c400800 0x00002000>;
    };
};
&ssi2 {
	fsl,mode = "i2s-master";
	status = "okay";
};
&i2c4 {
    clock-frequency = <400000>;
	pinctrl-names = "default";
	pinctrl-0 = <&pinctrl_i2c4>;
	status = "okay";

    wm8978: wm8978@1a {
        #sound-dai-cells = <0>;
        compatible = "wlf,wm8978";
	    reg = <0x1a>;
    };
};

I’m happy about any help.

Best regards, Patrick

Hi Patrick and Welcome to the Toradex Community!

I can play an audio file on my imx6 but anyway it’s too slow. When playing an audio file on my PC the file will be played inbetween 5 seconds. Doing the Same on the toradex module it takes 2 Minutes to play the same file.

What exactly do you mean? A 5 seconds file is played over 2minutes in the module?

How are you playing the file?

Best regards,
Jaski

Hi Jaski,

I tried to play a simple wav file with Aplay Command with “V” to get the volume meter.
“aplay piano2.wav -v-V stereo”

When doing It just with the internal Audio Card connected, this file takes about 5 Seconds to play.
When doing the same with the WM it takes “ages”.

I’m not sure but I think It’s kind of a clock Problem.

I’ve taken a look at some other people that used this specific sound Card. They all took a codec clock frequency of 12288000. Anyway we have attached an 24MHz external Clock to the Card so I’ve put a clock of 24000000 at codec clock.

Maybe there’s a misunderstanding on my side but I’m not sure about.

Can you maybe tell me If my DeviceTree is correct so far (at least this part of it)?
The description of simple-audio-card is a little bit confusing.

Best regards
Patrick

Hi together,

finally I got sound working!
The last problem was just a wrong clock on cpu.
We’ve put in just 48000 (because of 48khz output) but this have to be recalculated with following formula:

Output Frequency x Channels x Wordlen = (48000 * 32 * 2) = 3072000

I was just wondering, why linux won’t do in background based on driver but anyway it works now :wink:

  simple-audio-card,cpu {
             sound-dai = <&ssi2 0>;
             system-clock-frequency = <3072000>;
             bitclock-master;
             frame-master;
         };

Best regards

Hi Patrick

Perfect that you found a solution.
Thanks for your feedback.

Best regards,
Jaski