.. _i2s_atm: 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_TARGET`` * **Data 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_TX`` is supported. ``i2s_read()`` returns ``-ENOTSUP``. * ``i2s_configure()`` can be called once. The driver keeps a pointer to the ``struct i2s_config``, so it must remain valid (for example, ``static``). * ``frame_clk_freq`` and ``word_size`` in ``struct i2s_config`` must match the rate table entry selected by ``CONFIG_I2S_LUT_IDX``; otherwise ``i2s_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 :doc:`../faq/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_SEL`` 0, always available. * 32 MHz: PLL 64 MHz, /2, ``I2S_SEL`` 3 (``INIT_BP_FREQ`` 64000000). * 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_SEL`` 0, always available. * 32 MHz, any of: * 32 MHz doubler, ``I2S_SEL`` 1 (``INIT_BP_FREQ`` 32000000) * PLL 64 MHz, /2, ``I2S_SEL`` 3 (``INIT_BP_FREQ`` 64000000) * PLL 128 MHz, /4, ``I2S_SEL`` 5 (``INIT_BP_FREQ`` 64000001) * PLL 192 MHz, /6, ``I2S_SEL`` 7 (``INIT_BP_FREQ`` 48000001) * 64 MHz: PLL 64 MHz, /1, ``I2S_SEL`` 2 (``INIT_BP_FREQ`` 64000000), ``CONFIG_I2S_CLK_FREQ_MAX`` >= 64000000. * 128 MHz: PLL 128 MHz, /1, ``I2S_SEL`` 2 (``INIT_BP_FREQ`` 64000001), ``CONFIG_I2S_CLK_FREQ_MAX`` >= 128000000. * 192 MHz: PLL 192 MHz, /1, ``I2S_SEL`` 2 (``INIT_BP_FREQ`` 48000001), ``CONFIG_I2S_CLK_FREQ_MAX`` >= 192000000. ATM5 ==== * 16 MHz: ``I2S_SEL`` 0, always available. * 32 MHz, either of: * PLL 64 MHz / ``DIV_BP_VAL`` 2 = 32 MHz, /1, ``I2S_SEL`` 2 (``INIT_BP_FREQ`` 32000000) * PLL 64 MHz, /2, ``I2S_SEL`` 3 (``INIT_BP_FREQ`` 64000000) * 64 MHz: PLL 64 MHz, /1, ``I2S_SEL`` 2 (``INIT_BP_FREQ`` 64000000), ``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``: .. code-block:: 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: .. list-table:: :header-rows: 1 * - Index - Entry - Frame clock - Requirements * - 18 (default) - ``{32000000, 8000, 125, 16}`` - 8000 Hz, 16-bit - 32 MHz source * - 3 - ``{192000000, 48000, 125, 16}`` - 48000 Hz, 16-bit - ATM34, ``INIT_BP_FREQ`` 48000001, ``CONFIG_I2S_CLK_FREQ_MAX=192000000`` 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``: .. code-block:: kconfig 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: .. code-block:: kconfig CONFIG_I2S=y CONFIG_I2S_LUT_IDX=18 .. list-table:: :header-rows: 1 * - Option - Default - Description * - ``CONFIG_I2S_LUT_IDX`` - 18 (30 with ``CONFIG_I2S_LUT_CUSTOM``) - Index of the ``i2s_rate_lut[]`` entry to use (0-29, or 0-30 with ``CONFIG_I2S_LUT_CUSTOM``) * - ``CONFIG_I2S_LUT_CUSTOM`` - n - Append a custom entry; see `Custom Entry`_ * - ``CONFIG_I2S_CLK_FREQ_MAX`` - 32000000 - Highest PLL-derived I2S source clock the driver may use * - ``CONFIG_I2S_PLL_DIV_MIN`` - 2 (ATM33), 1 (others) - Smallest PLL divider used for the I2S source clock; not user-settable * - ``CONFIG_I2S_TX_BLOCK_NUM`` - 20 - Number of blocks in the driver TX memory slab * - ``CONFIG_I2S_WSCNT`` - 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: .. code-block:: devicetree &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): .. code-block:: c #include #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: .. code-block:: bash west build -p always -b --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_IDX`` * Some 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