The STM32MP25 provides four Serial Audio Interfaces (SAI) each with two independent audio subblocks.
On the ConnectCore MP25 Development Kit, the SAI4 interface is connected to a Maxim 98089 low-power stereo codec.
-
SAI4 subblock B is master and generates the clock and the audio output to the codec (playback).
-
SAI4 subblock A is a synchronous slave that only uses the data line as input from the codec (recording).
The following features of the codec are available on the ConnectCore MP25 Development Kit:
-
Analog inputs: line-in A, line-in B, microphone:
-
Two line-in audio inputs through the
LINE_IN_A_andLINE_IN_B_lines of the AUDIO and SPK connectors. -
Microphone audio input through the
MIC_lines of the AUDIO connector.
-
-
Analog outputs: line-out, headphone, speakers:
-
Line-out audio output through the
LINE_OUT_lines of the AUDIO connector. -
Headphone audio output through the on-board jack connector.
-
Speaker audio output through the
SPKL_andSPKR_lines of the SPK connector.
-
-
Digital processing, filters, volume control, amplifiers.
Kernel configuration
You can manage the audio driver support through the following kernel configuration options:
-
STM32 SAI interface (Serial Audio Interface) support (
CONFIG_SND_SOC_STM32_SAI) -
ASoC Audio Graph sound card support (
CONFIG_SND_AUDIO_GRAPH_CARD) -
SoC Audio support for i.MX boards with max98088/max98089 (
CONFIG_SND_SOC_IMX_MAX98088)
These options are enabled as built-in on the default ConnectCore MP25 kernel configuration file.
Kernel driver
The drivers for the audio interface and MAX98089 codec are located at:
| File | Description |
|---|---|
SAI driver |
|
SAI subblock driver |
|
ASoC Audio Graph sound card driver |
|
MAX98088/9 codec driver |
Device tree bindings and customization
The SAI interface is documented at
Documentation/devicetree/bindings/sound/st,stm32-sai.yaml.
The device tree must contain entries for:
-
The SAI interface
-
The interface between the SAI and the audio codec
-
The audio codec
Example: SAI4 on ConnectCore MP25 Development Kit
&sai4 {
pinctrl-names = "default", "sleep";
pinctrl-0 = <&ccmp25_sai4a_pins>, <&ccmp25_sai4b_pins>;
pinctrl-1 = <&ccmp25_sai4a_sleep_pins>, <&ccmp25_sai4b_sleep_pins>;
status = "okay";
sai4a: audio-controller@40340004 {
/* Sub-block A is a slave of sub-block B */
st,sync = <&sai4b 1>;
/* Sub-block A is only capture (rx) */
dmas = <&hpdma 79 0x43 0x12 0>;
dma-names = "rx";
clocks = <&rcc CK_KER_SAI4>, <&sai4b>;
clock-names = "sai_ck", "mclk";
status = "okay";
sai4a_port: port {
sai4a_endpoint: endpoint {
remote-endpoint = <&max98089_in_endpoint>;
format = "i2s";
mclk-fs = <256>;
dai-tdm-slot-num = <2>;
dai-tdm-slot-width = <32>;
};
};
};
sai4b: audio-controller@40340024 {
/* Sub-block B is master */
#clock-cells = <0>;
/* Sub-block B is only playback (tx) */
dmas = <&hpdma 80 0x63 0x21 0>;
dma-names = "tx";
status = "okay";
sai4b_port: port {
sai4b_endpoint: endpoint {
remote-endpoint = <&max98089_out_endpoint>;
format = "i2s";
mclk-fs = <256>;
dai-tdm-slot-num = <2>;
dai-tdm-slot-width = <32>;
};
};
};
};
&pinctrl {
[...]
ccmp25_sai4a_pins: ccmp25-sai4a-1 {
pins {
pinmux = <STM32_PINMUX('D', 1, AF5)>; /* SAI4_SD_A */
bias-disable;
};
};
ccmp25_sai4a_sleep_pins: ccmp25-sai4a-sleep-1 {
pins {
pinmux = <STM32_PINMUX('D', 1, ANALOG)>; /* SAI4_SD_A */
};
};
ccmp25_sai4b_pins: ccmp25-sai2b-0 {
pins1 {
pinmux = <STM32_PINMUX('I', 2, AF4)>, /* SAI4_SCK_B */
<STM32_PINMUX('I', 4, AF4)>, /* SAI4_FS_B */
<STM32_PINMUX('I', 0, AF4)>; /* SAI4_MCLK_B */
slew-rate = <0>;
drive-push-pull;
bias-disable;
};
pins2 {
pinmux = <STM32_PINMUX('I', 3, AF4)>; /* SAI4_SD_B */
bias-disable;
};
};
ccmp25_sai4b_sleep_pins: ccmp25-sai2b-sleep-0 {
pins {
pinmux = <STM32_PINMUX('I', 3, ANALOG)>, /* SAI4_SDO_B */
<STM32_PINMUX('I', 2, ANALOG)>, /* SAI4_SCK_B */
<STM32_PINMUX('I', 4, ANALOG)>, /* SAI4_FS_B */
<STM32_PINMUX('I', 0, ANALOG)>; /* SAI4_MCLK_B */
};
};
[...]
};
Interface between SAI and audio codec
/ {
[...]
/* AUDIO */
sound_max98089: sound-max98089 {
compatible = "audio-graph-card";
label = "ccmp25-dvk";
dais = <&sai4b_port &sai4a_port>;
status = "okay";
};
[...]
};
Audio codec (i2c2 slave)
&i2c2 {
[...]
max98089: codec@10 {
compatible = "maxim,max98089";
reg = <0x10>;
clocks = <&sai4b>;
clock-names = "mclk";
#sound-dai-cells = <0>;
#address-cells = <1>;
#size-cells = <0>;
status = "okay";
max98089_port: port@0{
reg = <0>;
max98089_in_endpoint: endpoint@0 {
remote-endpoint = <&sai4a_endpoint>;
frame-master = <&max98089_in_endpoint>;
bitclock-master = <&max98089_in_endpoint>;
};
max98089_out_endpoint: endpoint@1 {
remote-endpoint = <&sai4b_endpoint>;
frame-master = <&max98089_out_endpoint>;
bitclock-master = <&max98089_out_endpoint>;
};
};
};
[...]
};
Using the audio interface
Audio controls
The audio codec is controlled from Linux via the ALSA framework. Digi Embedded Yocto provides an initial configuration that enables:
-
Line-in A
-
Line-in B
-
Microphone
-
Headphones
-
Speakers
-
Line-out
You can use the command line application alsamixer to manage these interfaces and their associated controls (volumes, gains, filters).
Audio playback
| Since all output routes are enabled by default, any sound will play on headphones, speakers, and line-out. |
To play a wav file:
# aplay sound.wav
To play an mp3 file:
# mpg123 sound.mp3
Audio recording
| Since microphone, line-in A, and line-in B input routes are enabled by default, any recorded sound will mix the audio coming from these inputs. |
To record a stereo WAV audio file with 10 seconds duration from line-in:
# arecord -f cd sound.wav --duration 10 -D "hw:0,1"
To record a mono WAV audio file from the microphone:
# arecord --format=S16_LE --channels=1 --rate=44100 micro.wav --duration=10 -D "hw:0,1"