Reputation: 101
I'am working on a custom board based on IMX6 Solo processor and TX6S-8035 module, trying to play audio with MAX98357a codec. I just start leaning how works the device-tree I implement a simple-audio-card on the 4.1.15 linux kernel and use the following DTS as suggested in i.MX6 and MA98357a with simple-audio-card:
codec: max98357a@0 {
compatible = "maxim,max98357a";
#sound-dai-cells = <0>;
};
sound {
compatible = "simple-audio-card";
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_ssi1>;
simple-audio-card,name = "TI3 Audio";
simple-audio-card,format = "i2s";
simple-audio-card,widgets = "Speaker", "Speakers";
simple-audio-card,routing = "Speakers", "Speaker";
simple-audio-card,bitclock-master = <&cpu_dai>;
simple-audio-card,frame-master = <&cpu_dai>;
cpu_dai: simple-audio-card,cpu {
sound-dai = <&ssi1>;
system-clock-frequency = <883200>;
dai-tdm-slot-num = <2>;
dai-tdm-slot-width = <16>;
};
codec_dai: simple-audio-card,codec {
sound-dai = <&codec>;
};
};
&audmux {
status = "okay";
// Note: 'ssi1' (node of first SSI) corresponds to '_SSI0' below.
ssi1 {
fsl,audmux-port = <MX31_AUDMUX_PORT1_SSI0>;
fsl,port-config = <
0x00000000
IMX_AUDMUX_V2_PDCR_RXDSEL(MX31_AUDMUX_PORT3_SSI_PINS_3)
>;
};
aud3 {
fsl,audmux-port = <MX31_AUDMUX_PORT3_SSI_PINS_3>;
fsl,port-config = <
(IMX_AUDMUX_V2_PTCR_TFSDIR |
IMX_AUDMUX_V2_PTCR_TFSEL(MX31_AUDMUX_PORT1_SSI0) |
IMX_AUDMUX_V2_PTCR_TCLKDIR |
IMX_AUDMUX_V2_PTCR_TCSEL(MX31_AUDMUX_PORT1_SSI0))
IMX_AUDMUX_V2_PDCR_RXDSEL(MX31_AUDMUX_PORT1_SSI0)
>;
};
};
&ssi1 {
fsl,mode = "i2s-master";
assigned-clocks = <&clks IMX6QDL_CLK_SSI1_SEL>, <&clks IMX6QDL_CLK_SSI1>;
assigned-clock-parents = <&clks IMX6QDL_CLK_PLL4_AUDIO_DIV>;
assigned-clock-rates = <0>, <49152000>; // 48kHz on SSI1 clock
status = "okay";
};
The MAX98357a codec is connected to SSI1. The soundcard is detected by ALSA. But when i try to play an audio file, i have nothing out on the speaker and there is no I2S signal on the SSI1 pin. Does somebody has any idea for this issue. Thanks. Best Regard!
Upvotes: 0
Views: 2170
Reputation: 4488
I had some trouble getting the sound card (MA98357a codec with the simple-audio-card) to work with a recent revision of the imx6q (i.MX 6 Quad), so I am sharing here my solution:
CONFIG_SND_SOC_MAX98357A
needs to be enabled (associated with the compatible "maxim,max98357a"
). But "by default" this option cannot be selected under make menuconfig
. The associated Kconfig (sound/soc/codecs/Kconfig
) needs to be patched. Replace:config SND_SOC_MAX98357A
tristate
By:
config SND_SOC_MAX98357A
tristate "Maxim MAX98357A CODEC"
CONFIG_SND_SIMPLE_CARD
needs to be enabled (associated with the compatible "simple-audio-card"
)
Apply/Merge/Cherry-pick the following patch, if not already applied: Commit id in master: 3ff86050da41e072dd9fffc373c4f5691573cf4e: clk: imx6q: disable non functional divider
. I had to "merge" it manually since I am using an older kernel version 4.19.134
Write the .dts for this audio card:
audmux
, in my case the pins (TXD, CLK, FS) are connected to AUD5 pins. The first SSI (ssi1
) is used. This SSI is going to generate the bit clock (CLK) and provide it to the max98357a audio card.iomux
: the AUD5 pins needs to be configured in the right modessi1
and the associated clock. The best clock for that is the PLL4, which is the only clock that allow to generate exactly 48 kHz.codec
and the sound
cardBelow an extract of the .dts:
/dts-v1/;
#include <dt-bindings/sound/fsl-imx-audmux.h>
#include "imx6q.dtsi"
#include "imx6qdl-phytec-phycore-som.dtsi"
/ {
codec: max98357a@0 {
compatible = "maxim,max98357a";
#sound-dai-cells = <0>;
};
sound {
compatible = "simple-audio-card";
simple-audio-card,name = "max98357a";
simple-audio-card,format = "i2s";
simple-audio-card,widgets = "Speaker", "Speakers";
simple-audio-card,routing = "Speakers", "Speaker";
simple-audio-card,bitclock-master = <&cpu_dai>;
simple-audio-card,frame-master = <&cpu_dai>;
cpu_dai: simple-audio-card,cpu {
sound-dai = <&ssi1>;
dai-tdm-slot-num = <2>;
dai-tdm-slot-width = <16>;
};
codec_dai: simple-audio-card,codec {
sound-dai = <&codec>;
clocks = <&clks IMX6QDL_CLK_SSI1>;
};
};
};
&audmux {
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_audmux>;
status = "okay";
ssi1 {
fsl,audmux-port = <MX31_AUDMUX_PORT1_SSI0>;
fsl,port-config = <
0x00000000
IMX_AUDMUX_V2_PDCR_RXDSEL(MX31_AUDMUX_PORT5_SSI_PINS_5)
>;
};
aud5 {
fsl,audmux-port = <MX31_AUDMUX_PORT5_SSI_PINS_5>;
fsl,port-config = <
(IMX_AUDMUX_V2_PTCR_TFSDIR |
IMX_AUDMUX_V2_PTCR_TFSEL(MX31_AUDMUX_PORT1_SSI0) |
IMX_AUDMUX_V2_PTCR_TCLKDIR |
IMX_AUDMUX_V2_PTCR_TCSEL(MX31_AUDMUX_PORT1_SSI0))
IMX_AUDMUX_V2_PDCR_RXDSEL(MX31_AUDMUX_PORT1_SSI0)
>;
};
};
/* The clock tree used to generate the SSI1 bit clock (See IMX6DQRM reference manual):
* pll4 -- 24e6 * (DIV_SELECT + NUM/DENOM)
* pll4_bypass -- Bypass PLL4: CCM_ANALOG_PLL_AUDIO[BYPASS]
* pll4_audio -- Enable PLL4: CCM_ANALOG_PLL_AUDIO[ENABLE]
* pll4_post_div -- Divide PLL4: CCM_ANALOG_PLL_AUDIO[POST_DIV_SELECT]
* pll4_audio_div -- Reserved: Fixed factor of x1
* ssi1_sel -- Clock multiplexer
* ssi1_pred -- Divide SSI1: CS1CDR[SSI1_CLK_PRED]
* ssi1_podf -- Divide SSI1: CS1CDR[SSI1_CLK_PODF]
* ssi1 (SSI1_CLK_ROOT) -- Clock gate: CCM_CCGR5[CG9]
* ssi1 (Int. bit clock) -- Divide SSI1: STCCR[DIV2, PSR, PM] (See formula below)
*
* With the following constraint:
* - (DIV_SELECT + NUM/DENOM): Must be between 27 and 54: PLL4 clock between 650 and 1300 MHz
* - DENOM = 24e6: As implemented in clk_pllv3_av_set_rate()
* - POST_DIV_SELECT: Can divide by [1; 2; 4]
* - SSI1_CLK_PRED: Can divide by [1, 8]
* - SSI1_CLK_PODF: Can divide by [1, 64]
* - DIV2 and PSR = 0: As implemented in fsl_ssi_set_bclk()
* - PM > 0: Since DIV2, PSR and PM should not be all set to zero at the same time.
* - SSI bit clock = SSI1_CLK_ROOT / ((DIV2 + 1) * (7 * PSR + 1) * (PM + 1) * 2)
*
* SSI bit clock needs to be equal to 3072000 Hz (for 2 channels/stereo, 48kHz) since:
* - For 2 channels, slot width is fixed to 32 bits since in I2S Master mode, and STCCR[WL] are
* used to control the amount of valid data in those 32 bits.
* - 2 slots are used, one for each channel. The slot number (STCCR[DC]) is fixed to 2.
* => SSI bit clock = 48000 * 32 * 2 = 3072000
*
* SSI bit clock needs to be equal to 1536000 Hz (for 1 channel/mono, 48kHz) since:
* - Slot width is set to 16 bits, in I2S Normal mode.
* - 2 slots are used (fixed to 2), and data are only provided in first slot
* => SSI bit clock = 48000 * 16 * 2 = 1536000
*
* So SSI1_CLK_ROOT frequency needs to be equal to: 3072000 * (PM + 1) * 2 = 6144000 * (PM + 1)
*
* The following configuration is applied:
* - SSI1_SEL parent clock is configured to be PLL4_AUDIO_DIV
* - SSI1_SEL clock is automatically configured
* - PLL4 clock is set to 663.552 MHz, which is a multiple of 6144000 Hz (x 108).
* The PLL4 must be configured in the DTS otherwise Linux keeps the default/reset values, which
* are invalid. /sys/kernel/debug/clk/clk_summary report 147456000 Hz but the generated clock
* is not at this frequency.
* - SSI1_CLK_ROOT is set to 18.432 MHz. (18.432 * 36 = 663.552 MHz)
* - STCCR[PM] will be automatically set to 2. (18.432 / (2 + 1) / 2 = 3.072 MHz)
*/
&ssi1 {
fsl,mode = "i2s-master";
assigned-clocks = <&clks IMX6QDL_CLK_SSI1_SEL>, <&clks IMX6QDL_CLK_PLL4>, <&clks IMX6QDL_CLK_SSI1>;
assigned-clock-parents = <&clks IMX6QDL_CLK_PLL4_AUDIO_DIV>;
assigned-clock-rates = <0>, <663552000>, <18432000>;
status = "okay";
};
/* For iomuxc, pin configuration (pad setting value):
* - Bit 0: Slew rate: 1 = fast, 0 = slow
* - Bit 3-5: Drive Strength: 0 = HI-Z, 1 = 260 Ohm, 2 = 130 Ohm, 3 = 90 Ohm, 4 = 60 Ohm, 5 = 50 Ohm, 6 = 40 Ohm, 7 = 33 Ohm
* - Bit 6-7: Speed: 0 = Low, 1 and 2 = Medium, 3 = Maximum
* - Bit 11: Open drain: 0 = Disabled, 1 = Enabled
* - Bit 12: Pull / Keep Enable: 0 = Disabled, 1 = Enabled
* - Bit 13: Pull / Keep Select: 0 = Keep, 1 = Pull
* - Bit 14-15: Pull Up / Down config: 0 = 100K Down, 1 = 47K Up, 2 = 100K Up, 3 = 22K Up
* - Bit 16: Hysteresis Enable: 0 = Disabled, 1 = Enabled
* - Bit 30: SION: Software Input On Field.
* - Bit 31: NO_PAD_CTL: indicate this pin does not need config.
*
* See fsl,imx-pinctrl.txt, fsl,imx6q-pinctrl.txt, and pinctrl-bindings.txt
* See also https://www.nxp.com/docs/en/application-note/AN5078.pdf
*/
&iomuxc {
pinctrl_audmux: audmuxgrp {
fsl,pins = <
MX6QDL_PAD_KEY_ROW0__AUD5_TXD 0x110b0 /* AUDIO: DIN, D56, X_AUD5_TXD, SSI1_TXD */
MX6QDL_PAD_KEY_COL0__AUD5_TXC 0x130b0 /* AUDIO: BCLK, D53, X_AUD5_TXC, SSI1_CLK */
MX6QDL_PAD_KEY_COL1__AUD5_TXFS 0x130b0 /* AUDIO: LRCLK, D54, X_AUD5_TXFS, SSI1_FS */
>;
};
};
Upvotes: 1