Atmosic I2S Driver
Overview
The Atmosic I2S driver (atmosic,atm-i2s) implements the standard
Zephyr I2S API
on ATM33, ATM34 and ATM5. Applications use the standard calls
(i2s_configure(), i2s_write(), i2s_trigger(), i2s_config_get()).
This page covers only the Atmosic-specific behavior: how the frame clock is
selected and the related configuration.
Key Features
Standard Zephyr I2S API: No Atmosic-specific calls are needed for normal use
Controller or Target: Drives SCK/WS, or follows them with
I2S_OPT_FRAME_CLK_TARGETData Formats: I2S, PCM short/long, left-justified and right-justified
Word Size: 16-bit or 32-bit WS slot
DMA-Driven TX: Audio blocks are streamed to the I2S FIFO by DMA
Build-Time Rate Selection: The frame clock is fixed by
CONFIG_I2S_LUT_IDX
Supported API
The driver differs from the generic Zephyr I2S API in the following points:
Only
I2S_DIR_TXis supported.i2s_read()returns-ENOTSUP.i2s_configure()can be called once. The driver keeps a pointer to thestruct i2s_config, so it must remain valid (for example,static).frame_clk_freqandword_sizeinstruct i2s_configmust match the rate table entry selected byCONFIG_I2S_LUT_IDX; otherwisei2s_configure()returns-EINVAL(and asserts when asserts are enabled).Samples are always 16-bit in memory. With a 32-bit WS slot the hardware pads the remaining bit clocks with zeros.
Clock Architecture
The I2S source clock is one of 16 MHz, 32 MHz, or the PLL divided by 1 to 8.
The driver never reprograms clocks: it only selects a source that the system
clock configuration already provides. The system clock is chosen at build
time with INIT_BP_FREQ (see How to set BP Clock Frequency).
CONFIG_I2S_CLK_FREQ_MAX (default 32 MHz) is the highest PLL-derived source
the driver uses. Raise it to use the 64, 128 or 192 MHz entries.
The source is selected by I2S_SEL in CLK_AUD_CTRL: 0 is 16 MHz, 1 is
32 MHz, and 2 to 9 is the I2S PLL input divided by N, where I2S_SEL is
N + 1. Below, each PLL source is written as the I2S PLL input, the divider /N
and the resulting I2S_SEL, followed by the INIT_BP_FREQ that provides it.
ATM33
16 MHz:
I2S_SEL0, always available.32 MHz: PLL 64 MHz, /2,
I2S_SEL3 (INIT_BP_FREQ64000000).64, 128 and 192 MHz: not available.
ATM33 cannot use the PLL undivided (I2S_SEL 2 selects 16 MHz), so the
driver starts PLL dividers at /2 (CONFIG_I2S_PLL_DIV_MIN). PLL 32 MHz /1 at
INIT_BP_FREQ 32000000 is therefore not a usable 32 MHz source.
ATM34
16 MHz:
I2S_SEL0, always available.32 MHz, any of:
32 MHz doubler,
I2S_SEL1 (INIT_BP_FREQ32000000)PLL 64 MHz, /2,
I2S_SEL3 (INIT_BP_FREQ64000000)PLL 128 MHz, /4,
I2S_SEL5 (INIT_BP_FREQ64000001)PLL 192 MHz, /6,
I2S_SEL7 (INIT_BP_FREQ48000001)
64 MHz: PLL 64 MHz, /1,
I2S_SEL2 (INIT_BP_FREQ64000000),CONFIG_I2S_CLK_FREQ_MAX>= 64000000.128 MHz: PLL 128 MHz, /1,
I2S_SEL2 (INIT_BP_FREQ64000001),CONFIG_I2S_CLK_FREQ_MAX>= 128000000.192 MHz: PLL 192 MHz, /1,
I2S_SEL2 (INIT_BP_FREQ48000001),CONFIG_I2S_CLK_FREQ_MAX>= 192000000.
ATM5
16 MHz:
I2S_SEL0, always available.32 MHz, either of:
PLL 64 MHz /
DIV_BP_VAL2 = 32 MHz, /1,I2S_SEL2 (INIT_BP_FREQ32000000)PLL 64 MHz, /2,
I2S_SEL3 (INIT_BP_FREQ64000000)
64 MHz: PLL 64 MHz, /1,
I2S_SEL2 (INIT_BP_FREQ64000000),CONFIG_I2S_CLK_FREQ_MAX>= 64000000.128 and 192 MHz: not available.
ATM5 runs the backplane from PLL 64 MHz (INIT_BP_FREQ 32000000:
DIV_BP_VAL 2; INIT_BP_FREQ 64000000: DIV_BP_VAL 1). When
DIV_BP_VAL is greater than 1, the I2S PLL input is taken after the
backplane divider, so it is PLL 64 MHz / DIV_BP_VAL instead of 64 MHz.
Rate Lookup Table
Each supported frame clock is an entry of i2s_rate_lut[] in
openair/drivers/i2s/i2s_atm.c:
struct i2s_rate_cfg {
uint32_t src_freq; /* I2S source clock (Hz) */
uint32_t frame_clk_freq; /* Requested frame clock (Hz) */
uint16_t ck2sck; /* Source clock to SCK divider */
uint8_t sck2ws_rt; /* SCK cycles per WS slot; 32 = 32-bit slot */
};
The resulting frame clock is
src_freq / (ck2sck x sck2ws_rt x 2). Entries with sck2ws_rt 32 are used
with word_size 32; all other entries are used with word_size 16. Some
rates (for example 44.1 kHz) cannot be produced exactly; the comment of each
entry gives the actual rate and its error.
Examples:
Index |
Entry |
Frame clock |
Requirements |
|---|---|---|---|
18 (default) |
|
8000 Hz, 16-bit |
32 MHz source |
3 |
|
48000 Hz, 16-bit |
ATM34, |
Custom Entry
A rate that is not in the table can be added without changing the driver.
CONFIG_I2S_LUT_CUSTOM appends one entry built from four options as index 30
and makes it the default CONFIG_I2S_LUT_IDX:
CONFIG_I2S_LUT_CUSTOM=y
CONFIG_I2S_LUT_CUSTOM_SRC_FREQ=16000000
CONFIG_I2S_LUT_CUSTOM_FRAME_CLK_FREQ=10000
CONFIG_I2S_LUT_CUSTOM_CK2SCK=50
CONFIG_I2S_LUT_CUSTOM_SCK2WS_RT=16
This example gives exactly 16000000 / (50 x 16 x 2) = 10000 Hz with 16-bit
words. CK2SCK is 1-65535 and SCK2WS_RT is 16-32 (32 selects a 32-bit
slot). The build fails if the four values produce a frame clock more than 5%
away from FRAME_CLK_FREQ. The source clock must already be available, as
for any other entry.
Configuration
The I2S driver is enabled with CONFIG_I2S when the i2s node is enabled
in the device tree:
CONFIG_I2S=y
CONFIG_I2S_LUT_IDX=18
Option |
Default |
Description |
|---|---|---|
|
18 (30 with |
Index of the |
|
n |
Append a custom entry; see Custom Entry |
|
32000000 |
Highest PLL-derived I2S source clock the driver may use |
|
2 (ATM33), 1 (others) |
Smallest PLL divider used for the I2S source clock; not user-settable |
|
20 |
Number of blocks in the driver TX memory slab |
|
0 |
Forced word select count; 0 means not forced |
CONFIG_ATM_FIFO_TX_ISR enables a low-latency TX mode with additional
Atmosic-specific calls. Contact Atmosic support before using it.
Device Tree
The i2s node is defined in the SoC device tree. Enable it and assign the
pins in a board overlay. The binding (atmosic,atm-i2s) provides
sck-out-pin, ws-out-pin and sd-out-pin for output, and
sck-in-pin, ws-in-pin and sd-in-pin for input:
&i2s {
sck-out-pin = <18>;
ws-out-pin = <19>;
sd-out-pin = <20>;
status = "okay";
};
/ {
aliases {
i2s-tx = &i2s;
};
};
Usage Examples
Basic TX
The configuration must match the selected entry, here index 18 (8000 Hz, 16-bit):
#include <zephyr/drivers/i2s.h>
#define BLOCK_SIZE 128
#define BLOCK_NUM 4
K_MEM_SLAB_DEFINE_STATIC(tx_slab, BLOCK_SIZE, BLOCK_NUM, 4);
int i2s_tx_example(void)
{
const struct device *dev = DEVICE_DT_GET(DT_ALIAS(i2s_tx));
static const struct i2s_config cfg = {
.word_size = 16,
.channels = 2,
.format = I2S_FMT_DATA_FORMAT_I2S,
.options = I2S_OPT_BIT_CLK_CONTROLLER | I2S_OPT_FRAME_CLK_CONTROLLER,
.frame_clk_freq = 8000,
.mem_slab = &tx_slab,
.block_size = BLOCK_SIZE,
.timeout = 1000,
};
void *block;
int ret;
ret = i2s_configure(dev, I2S_DIR_TX, &cfg);
if (ret < 0) {
return ret;
}
/* Queue the first block, then start */
k_mem_slab_alloc(&tx_slab, &block, K_FOREVER);
/* fill block with audio samples */
i2s_write(dev, block, BLOCK_SIZE);
i2s_trigger(dev, I2S_DIR_TX, I2S_TRIGGER_START);
/* keep calling i2s_write() with new blocks */
return i2s_trigger(dev, I2S_DIR_TX, I2S_TRIGGER_DRAIN);
}
Selecting Another Rate
To output 48 kHz with 16-bit words on ATM34, select index 3 and make the 192 MHz source available:
west build -p always -b <BOARD> <APP> --sysbuild -- \
-DCONFIG_I2S_LUT_IDX=3 -DCONFIG_I2S_CLK_FREQ_MAX=192000000 \
-DSB_ATM_DTS_EXTRA_CPPFLAGS="-DINIT_BP_FREQ=48000001;"
Then set .frame_clk_freq = 48000 and .word_size = 16 in
struct i2s_config.
Limitations
TX only
One frame clock per build, selected by
CONFIG_I2S_LUT_IDXSome frame clocks are approximate (see the entry comments in
i2s_rate_lut[])64, 128 and 192 MHz sources depend on the platform (see Clock Architecture)
Troubleshooting
“does not match i2s_rate_lut[N]”: frame_clk_freq or word_size
differs from the selected entry. Change CONFIG_I2S_LUT_IDX or the
configuration so that they match.
“source … Hz is not available”: The source clock of the selected entry is
not provided by the current clock configuration. Use an INIT_BP_FREQ from
Clock Architecture, and raise CONFIG_I2S_CLK_FREQ_MAX for sources above
32 MHz.
Debug logging: CONFIG_I2S_LOG_LEVEL_DBG=y prints the selected source
clock, dividers and I2S_SEL value.
References
openair/samples/drivers/i2s/output: I2S output sample