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:
- Configure
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.
- Configure the
iomux
: the AUD5 pins needs to be configured in the right mode
- Configure
ssi1
and the associated clock. The best clock for that is the PLL4, which is the only clock that allow to generate exactly 48 kHz.
- Add the
codec
and the sound
card
Below an extract of the .dts:
/dts-v1/;
/ {
codec: max98357a@0 {
compatible = "maxim,max98357a";
};
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 */
>;
};
};
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) >; };
– vlad zouth