Audio with an external codec
Run the S31 commands as root on the standard full board image. Complete the common pin and overlay checks first. Commands explicitly marked for the build host run on the Linux host.
Choose the clock arrangement
The shipped i2s0 and i2s1 overlays use a dummy codec and consume external
BCLK and frame clock for both playback and capture. For I2S0 these inputs
are GPIO42/BCLK and GPIO43/WS, with playback data on GPIO44 and capture data
on GPIO45. A clock-producing codec or test peer must start the matching clocks
before the transfer. The repository’s I2S HIL flow
uses a P4 peer for this purpose. Applying the stock overlay does not make an
ordinary clock-consuming DAC work.
The following is a complete new board-integration example for stereo playback through a PCM5102A. It is derived from the S31 DAI, simple-card and PCM5102A drivers; it is not a shipped or hardware-validated S31 board configuration. The S31 produces BCLK and WS; the DAC consumes both. Capture is not provided by this codec.
Wire the PCM5102A
Use a powered module with 3.3 V-compatible digital inputs, common ground, and the following signal connections. Set its hardware straps to I2S format (FMT low), SCK low for BCK-derived PLL operation, and XSMT high to unmute; DEMP low disables de-emphasis. Follow the module schematic for supplies and other straps. Connect its line outputs to a suitable line input or amplifier. These requirements come from the TI PCM5102A datasheet, pin functions and 3-wire clocking.
S31 signal in this example |
PCM5102A signal |
Clock/data role |
|---|---|---|
GPIO42 |
BCK |
S31 output, DAC input |
GPIO43 |
LRCK/WS |
S31 output, DAC input |
GPIO44 |
DIN |
S31 playback data output |
Ground |
Ground |
Common reference |
No external MCLK connection is used. At 48 kHz, two 32-bit slots give a
3.072 MHz BCLK; mclk-fs = <256> requests 12.288 MHz for the S31’s internal
MCLK. These are configured/derived values, not measured frequencies.
Add the card and build it
On the build host, add these options to
linux-esp32-s31/arch/riscv/configs/esp32s31_defconfig and use the full board configuration.
The parent build recreates its generated .config from this source defconfig,
so editing only out/linux/.config is insufficient:
CONFIG_SND_SIMPLE_CARD=y
CONFIG_SND_SOC_PCM5102A=y
Create
linux-esp32-s31/arch/riscv/boot/dts/espressif/esp32s31-overlay-i2s0-pcm5102a.dtso
with this complete overlay:
/dts-v1/;
/plugin/;
#include <dt-bindings/pinctrl/esp32s31-pinmux.h>
/ {
espressif,overlay-name = "i2s0-pcm5102a";
espressif,resource-claims = "i2s0";
};
&ahb_gdma {
status = "okay";
};
&gpio {
pcm5102a_pins: pcm5102a-pins {
i2s-pins {
pinmux = <ESP32S31_MATRIX_OUT(42, 25, 0)>,
<ESP32S31_MATRIX_OUT(43, 27, 0)>,
<ESP32S31_MATRIX_OUT(44, 28, 0)>;
drive-strength = <20>;
};
};
};
&i2s0 {
#sound-dai-cells = <0>;
espressif,external-card;
pinctrl-names = "default";
pinctrl-0 = <&pcm5102a_pins>;
status = "okay";
};
&{/} {
pcm5102a: audio-codec {
compatible = "ti,pcm5102a";
#sound-dai-cells = <0>;
};
sound-pcm5102a {
compatible = "simple-audio-card";
simple-audio-card,name = "S31-PCM5102A";
simple-audio-card,format = "i2s";
simple-audio-card,bitclock-master = <&pcm5102a_cpu>;
simple-audio-card,frame-master = <&pcm5102a_cpu>;
simple-audio-card,mclk-fs = <256>;
pcm5102a_cpu: simple-audio-card,cpu {
sound-dai = <&i2s0>;
dai-tdm-slot-num = <2>;
dai-tdm-slot-width = <32>;
system-clock-direction-out;
};
simple-audio-card,codec {
sound-dai = <&pcm5102a>;
};
};
};
Add this line to the Makefile in the same DTS directory:
dtb-$(CONFIG_ARCH_ESPRESSIF) += esp32s31-overlay-i2s0-pcm5102a.dtbo
The CPU endpoint’s system-clock-direction-out matters: the S31 DAI accepts
set_sysclk() only with ID 0 and SND_SOC_CLOCK_OUT. The card’s master
references make the CPU produce both BCLK and WS. The default pinctrl state
routes both clocks as outputs. espressif,external-card prevents the built-in
dummy card from claiming the DAI. Sources:
S31 format and clock interface,
simple-card clock direction,
PCM5102A driver.
On the build host, rebuild and flash the matching kernel, radio bundle and rootfs. Follow the flash guide to select the port and prepare the serial connection:
make image
make flash-existing-all PORT=/dev/ttyUSB0
The rootfs packaging copies esp32s31-overlay-*.dtbo into its overlay
directory. On the S31, with any existing i2s0 overlay and conflicting pin
users removed, enable the custom card:
s31-overlay apply i2s0-pcm5102a --volatile
aplay -l
Use the card/device numbers reported by aplay -l. The following assumes
hw:0,0 and an existing raw interleaved stereo S16_LE, 48 kHz file at
/mnt/media/audio.raw; replace both with your actual values. In this format,
a 1008-frame period is 4032 bytes, and 8064 frames gives eight periods:
aplay -D hw:0,0 -t raw -f S16_LE -c 2 -r 48000 \
--period-size=1008 --buffer-size=8064 /mnt/media/audio.raw
s31-overlay remove i2s0-pcm5102a --volatile
Expected observations are a PCM card listing, successful playback without
ALSA errors, and the supplied audio at the DAC’s line output. If the card is
missing, check kernel options, overlay application, and probe errors in
dmesg. If playback runs but is silent, check the output clock routes,
FMT/XSMT/SCK straps, supplies, and amplifier path. Measure BCLK/WS to verify
the physical clock configuration. Stop all ALSA users before removing the card.
The stock external-clock card can also be used for capture with a suitable
clock/data source. For stereo 48 kHz S16_LE, the stock card uses two
16-bit slots: supply 48 kHz WS and 1.536 MHz BCLK with the matching I2S data.
Apply the stock i2s0 overlay and record two seconds:
s31-overlay apply i2s0 --volatile
arecord -l
arecord -D hw:0,0 -t raw -f S16_LE -c 2 -r 48000 -d 2 \
--period-size=1008 --buffer-size=8064 /tmp/capture.raw
s31-overlay remove i2s0 --volatile
Again, replace hw:0,0 with the actual device. The PCM5102A is a playback
DAC and cannot supply this capture stream. Duplex use on one S31 DAI requires
the same sample rate and compatible shared MCLK settings.