This is an automated email from the ASF dual-hosted git repository. xiaoxiang pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/nuttx.git
commit 873a6319bbf0b479f3a362d6d3aa69cab4501af6 Author: Eren Terzioglu <eren.terzio...@espressif.com> AuthorDate: Wed Mar 5 16:17:36 2025 +0100 arch/esp32[s2|s3]: Add common I2S arch layer support Add common I2S arch layer support for Xtensa based Espressif devices Signed-off-by: Eren Terzioglu <eren.terzio...@espressif.com> --- arch/xtensa/src/common/espressif/Kconfig | 269 +++ arch/xtensa/src/common/espressif/Make.defs | 4 + arch/xtensa/src/common/espressif/esp_i2s.c | 3248 ++++++++++++++++++++++++++++ arch/xtensa/src/common/espressif/esp_i2s.h | 87 + arch/xtensa/src/esp32/Kconfig | 2 +- arch/xtensa/src/esp32/hal.mk | 2 + arch/xtensa/src/esp32s2/Kconfig | 2 +- arch/xtensa/src/esp32s2/hal.mk | 2 + arch/xtensa/src/esp32s3/Kconfig | 2 +- arch/xtensa/src/esp32s3/hal.mk | 3 + 10 files changed, 3618 insertions(+), 3 deletions(-) diff --git a/arch/xtensa/src/common/espressif/Kconfig b/arch/xtensa/src/common/espressif/Kconfig index b84dee3518..fdf26ae15c 100644 --- a/arch/xtensa/src/common/espressif/Kconfig +++ b/arch/xtensa/src/common/espressif/Kconfig @@ -28,6 +28,35 @@ config ESPRESSIF_TEMP ---help--- A built-in sensor used to measure the chip's internal temperature. +config ESPRESSIF_I2S + bool + default n + +config ESPRESSIF_I2S0 + bool "I2S 0" + default n + depends on !ESP32_I2S && !ESP32S2_I2S && !ESP32S3_I2S + select ESPRESSIF_I2S + select I2S + select ESP32S3_DMA if ARCH_CHIP_ESP32S3 + select ESP32S3_GPIO_IRQ if ARCH_CHIP_ESP32S3 + select ESP32S2_GPIO_IRQ if ARCH_CHIP_ESP32S2 + select ESP32_GPIO_IRQ if ARCH_CHIP_ESP32 + select SCHED_HPWORK + select ARCH_DMA + +config ESPRESSIF_I2S1 + bool "I2S 1" + depends on !ARCH_CHIP_ESP32S2 + default n + select ESPRESSIF_I2S + select I2S + select ESP32S3_DMA if ARCH_CHIP_ESP32S3 + select ESP32S3_GPIO_IRQ if ARCH_CHIP_ESP32S3 + select ESP32_GPIO_IRQ if ARCH_CHIP_ESP32 + select SCHED_HPWORK + select ARCH_DMA + config ESPRESSIF_I2C_PERIPH_MASTER_MODE bool depends on (ESP32S3_I2C_PERIPH_MASTER_MODE || ESP32S2_I2C_PERIPH_MASTER_MODE || ESP32_I2C_PERIPH_MASTER_MODE) @@ -345,6 +374,246 @@ config ESPRESSIF_TEMP_THREAD_STACKSIZE endmenu # ESPRESSIF_TEMP +menu "I2S Configuration" + depends on ESPRESSIF_I2S + +config ESPRESSIF_I2S_MAXINFLIGHT + int "I2S queue size" + default 4 + ---help--- + This is the total number of transfers, both RX and TX, that can be + enqueued before the caller is required to wait. This setting + determines the number certain queue data structures that will be + pre-allocated. + +if ESPRESSIF_I2S0 + +config ESPRESSIF_I2S0_RX + bool "Enable I2S receiver" + default y + ---help--- + Enable I2S receiver (port 0) + +config ESPRESSIF_I2S0_TX + bool "Enable I2S transmitter" + default y + ---help--- + Enable I2S transmitter (port 0) + +choice ESPRESSIF_I2S0_ROLE + prompt "I2S0 role" + default ESPRESSIF_I2S0_ROLE_MASTER + ---help--- + Selects the operation role of the I2S0. + +config ESPRESSIF_I2S0_ROLE_MASTER + bool "Master" + +config ESPRESSIF_I2S0_ROLE_SLAVE + bool "Slave" + +endchoice # I2S0 role + +choice ESPRESSIF_I2S0_DATA_BIT_WIDTH + prompt "Bit width" + default ESPRESSIF_I2S0_DATA_BIT_WIDTH_16BIT + ---help--- + Selects the valid data bits per sample. + Note that this option may be overwritten by the audio + according to the bit width of the file being played + +config ESPRESSIF_I2S0_DATA_BIT_WIDTH_8BIT + bool "8 bits" + +config ESPRESSIF_I2S0_DATA_BIT_WIDTH_16BIT + bool "16 bits" + +config ESPRESSIF_I2S0_DATA_BIT_WIDTH_24BIT + bool "24 bits" + +config ESPRESSIF_I2S0_DATA_BIT_WIDTH_32BIT + bool "32 bits" + +endchoice # Bit width + +config ESPRESSIF_I2S0_SAMPLE_RATE + int "I2S0 sample rate" + default 44100 + range 8000 48000 + ---help--- + Selects the sample rate. + Note that this option may be overwritten by the audio + according to the bit width of the file being played + +config ESPRESSIF_I2S0_BCLKPIN + int "I2S0 BCLK pin" + default 4 if ARCH_CHIP_ESP32S3 || ARCH_CHIP_ESP32 + default 35 if ARCH_CHIP_ESP32S2 + range 0 45 if (ARCH_CHIP_ESP32S2 && ESPRESSIF_I2S0_ROLE_MASTER) + range 0 46 if (ARCH_CHIP_ESP32S2 && ESPRESSIF_I2S0_ROLE_SLAVE) + range 0 33 if (ARCH_CHIP_ESP32 && ESPRESSIF_I2S0_ROLE_MASTER) + range 0 39 if (ARCH_CHIP_ESP32 && ESPRESSIF_I2S0_ROLE_SLAVE) + range 0 48 if ARCH_CHIP_ESP32S3 + +config ESPRESSIF_I2S0_WSPIN + int "I2S0 WS pin" + default 5 if ARCH_CHIP_ESP32S3 || ARCH_CHIP_ESP32 + default 34 if ARCH_CHIP_ESP32S2 + range 0 45 if (ARCH_CHIP_ESP32S2 && ESPRESSIF_I2S0_ROLE_MASTER) + range 0 46 if (ARCH_CHIP_ESP32S2 && ESPRESSIF_I2S0_ROLE_SLAVE) + range 0 33 if (ARCH_CHIP_ESP32 && ESPRESSIF_I2S0_ROLE_MASTER) + range 0 39 if (ARCH_CHIP_ESP32 && ESPRESSIF_I2S0_ROLE_SLAVE) + range 0 48 if ARCH_CHIP_ESP32S3 + +config ESPRESSIF_I2S0_DINPIN + int "I2S0 DIN pin" + depends on ESPRESSIF_I2S0_RX + default 19 if ARCH_CHIP_ESP32S3 || ARCH_CHIP_ESP32 + default 37 if ARCH_CHIP_ESP32S2 + range 0 46 if ARCH_CHIP_ESP32S2 + range 0 48 if ARCH_CHIP_ESP32S3 + range 0 39 if ARCH_CHIP_ESP32 + +config ESPRESSIF_I2S0_DOUTPIN + int "I2S0 DOUT pin" + depends on ESPRESSIF_I2S0_TX + default 18 if ARCH_CHIP_ESP32S3 || ARCH_CHIP_ESP32 + default 36 if ARCH_CHIP_ESP32S2 + range 0 46 if ARCH_CHIP_ESP32S2 + range 0 48 if ARCH_CHIP_ESP32S3 + range 0 33 if ARCH_CHIP_ESP32 + +config ESPRESSIF_I2S0_MCLK + bool "Enable I2S Master Clock" + depends on ESPRESSIF_I2S0_ROLE_MASTER + default n + ---help--- + Enable I2S master clock + +config ESPRESSIF_I2S0_MCLKPIN + int "I2S MCLK pin" + depends on ESPRESSIF_I2S0_MCLK + default 0 if ARCH_CHIP_ESP32S3 || ARCH_CHIP_ESP32 + default 33 if ARCH_CHIP_ESP32S2 + range 0 45 if ARCH_CHIP_ESP32S2 + range 0 48 if ARCH_CHIP_ESP32S3 + range 0 3 if ARCH_CHIP_ESP32 + +endif # ESPRESSIF_I2S0 + +if ESPRESSIF_I2S1 + +config ESPRESSIF_I2S1_RX + bool "Enable I2S receiver" + default y + ---help--- + Enable I2S receiver (port 1) + +config ESPRESSIF_I2S1_TX + bool "Enable I2S transmitter" + default y + ---help--- + Enable I2S transmitter (port 1) + +choice ESPRESSIF_I2S1_ROLE + prompt "I2S1 role" + default ESPRESSIF_I2S1_ROLE_MASTER + ---help--- + Selects the operation role of the I2S0. + +config ESPRESSIF_I2S1_ROLE_MASTER + bool "Master" + +config ESPRESSIF_I2S1_ROLE_SLAVE + bool "Slave" + +endchoice # I2S1 role + +choice ESPRESSIF_I2S1_DATA_BIT_WIDTH + prompt "Bit width" + default ESPRESSIF_I2S1_DATA_BIT_WIDTH_16BIT + ---help--- + Selects the valid data bits per sample. + Note that this option may be overwritten by the audio + according to the bit width of the file being played + +config ESPRESSIF_I2S1_DATA_BIT_WIDTH_8BIT + bool "8 bits" + +config ESPRESSIF_I2S1_DATA_BIT_WIDTH_16BIT + bool "16 bits" + +config ESPRESSIF_I2S1_DATA_BIT_WIDTH_24BIT + bool "24 bits" + +config ESPRESSIF_I2S1_DATA_BIT_WIDTH_32BIT + bool "32 bits" + +endchoice # Bit width + +config ESPRESSIF_I2S1_SAMPLE_RATE + int "I2S1 sample rate" + default 44100 + range 8000 48000 + ---help--- + Selects the sample rate. + Note that this option may be overwritten by the audio + according to the bit width of the file being played + +config ESPRESSIF_I2S1_BCLKPIN + int "I2S1 BCLK pin" + default 22 + range 0 33 if (ARCH_CHIP_ESP32 && ESPRESSIF_I2S0_ROLE_MASTER) + range 0 39 if (ARCH_CHIP_ESP32 && ESPRESSIF_I2S0_ROLE_SLAVE) + range 0 48 if ARCH_CHIP_ESP32S3 + +config ESPRESSIF_I2S1_WSPIN + int "I2S1 WS pin" + default 23 + range 0 33 if (ARCH_CHIP_ESP32 && ESPRESSIF_I2S0_ROLE_MASTER) + range 0 39 if (ARCH_CHIP_ESP32 && ESPRESSIF_I2S0_ROLE_SLAVE) + range 0 48 if ARCH_CHIP_ESP32S3 + +config ESPRESSIF_I2S1_DINPIN + int "I2S1 DIN pin" + depends on ESPRESSIF_I2S1_RX + default 26 + range 0 39 if ARCH_CHIP_ESP32 + range 0 48 if ARCH_CHIP_ESP32S3 + +config ESPRESSIF_I2S1_DOUTPIN + int "I2S1 DOUT pin" + depends on ESPRESSIF_I2S1_TX + default 25 + range 0 39 if ARCH_CHIP_ESP32 + range 0 48 if ARCH_CHIP_ESP32S3 + +config ESPRESSIF_I2S1_MCLK + bool "Enable I2S Master Clock" + depends on ESPRESSIF_I2S1_ROLE_MASTER + default n + ---help--- + Enable I2S master clock + +config ESPRESSIF_I2S1_MCLKPIN + int "I2S1 MCLK pin" + depends on ESPRESSIF_I2S1_MCLK + default 1 if ARCH_CHIP_ESP32S3 + default 0 if ARCH_CHIP_ESP32 + range 0 48 if ARCH_CHIP_ESP32S3 + range 0 3 if ARCH_CHIP_ESP32 + +endif # ESPRESSIF_I2S1 + +config I2S_DMADESC_NUM + int "I2S DMA maximum number of descriptors" + default 2 + ---help--- + Configure the maximum number of out-link/in-link descriptors to + be chained for a I2S DMA transfer. + +endmenu # I2S configuration + menu "I2C bitbang configuration" depends on ESPRESSIF_I2C_BITBANG diff --git a/arch/xtensa/src/common/espressif/Make.defs b/arch/xtensa/src/common/espressif/Make.defs index 0dc9db513e..e59cee5e71 100644 --- a/arch/xtensa/src/common/espressif/Make.defs +++ b/arch/xtensa/src/common/espressif/Make.defs @@ -42,6 +42,10 @@ ifeq ($(CONFIG_ESPRESSIF_TEMP),y) CHIP_CSRCS += esp_temperature_sensor.c endif +ifeq ($(CONFIG_ESPRESSIF_I2S),y) +CHIP_CSRCS += esp_i2s.c +endif + ifeq ($(CONFIG_ESPRESSIF_I2C_BITBANG),y) CHIP_CSRCS += esp_i2c_bitbang.c endif diff --git a/arch/xtensa/src/common/espressif/esp_i2s.c b/arch/xtensa/src/common/espressif/esp_i2s.c new file mode 100644 index 0000000000..9a2f567998 --- /dev/null +++ b/arch/xtensa/src/common/espressif/esp_i2s.c @@ -0,0 +1,3248 @@ +/**************************************************************************** + * arch/xtensa/src/common/espressif/esp_i2s.c + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include <nuttx/config.h> + +#include <assert.h> +#include <debug.h> +#include <errno.h> +#include <stdbool.h> +#include <stdint.h> +#include <math.h> + +#include <nuttx/irq.h> +#include <nuttx/arch.h> +#include <nuttx/spinlock.h> +#include <arch/irq.h> + +#include "xtensa.h" +#include "esp_i2s.h" +#if defined(CONFIG_ARCH_CHIP_ESP32S3) +#include "esp32s3_gpio.h" +#include "hardware/esp32s3_gpio_sigmap.h" +#include "esp32s3_dma.h" +#include "hardware/esp32s3_dma.h" +#include "esp32s3_irq.h" +#elif defined(CONFIG_ARCH_CHIP_ESP32S2) +#include "esp32s2_gpio.h" +#include "hardware/esp32s2_gpio_sigmap.h" +#include "esp32s2_dma.h" +#include "esp32s2_irq.h" +#else +#include "esp32_gpio.h" +#include "hardware/esp32_gpio_sigmap.h" +#include "esp32_dma.h" +#include "esp32_irq.h" +#endif + +#include "hal/i2s_hal.h" +#include "hal/i2s_ll.h" +#include "soc/i2s_periph.h" +#include "soc/i2s_reg.h" +#include "hal/i2s_types.h" +#include "soc/gpio_sig_map.h" +#include "periph_ctrl.h" +#if defined(CONFIG_ARCH_CHIP_ESP32S3) +# include "soc/gdma_reg.h" +# include "soc/gdma_periph.h" +# include "hal/gdma_ll.h" +#endif + +#include "esp_attr.h" +#include "esp_bit_defs.h" +#include "esp_cpu.h" +#include "esp_rom_sys.h" +#include "soc/soc.h" +#include "hal/dma_types.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#if defined(CONFIG_ARCH_CHIP_ESP32S3) +#define esp_setup_irq esp32s3_setup_irq +#define esp_teardown_irq esp32s3_teardown_irq +#define esp_configgpio esp32s3_configgpio +#define esp_gpio_matrix_in esp32s3_gpio_matrix_in +#define esp_gpio_matrix_out esp32s3_gpio_matrix_out +#define esp_gpiowrite esp32s3_gpiowrite +#define esp_dma_init esp32s3_dma_init +#define esp_dma_request esp32s3_dma_request +#define esp_dma_setup esp32s3_dma_setup +#define esp_dma_load esp32s3_dma_load +#define esp_dma_enable esp32s3_dma_enable +#define ESP_IRQ_PRIORITY_DEFAULT ESP32S3_INT_PRIO_DEF +#define ESP_IRQ_TRIGGER_LEVEL ESP32S3_CPUINT_LEVEL +#define ESPRESSIF_DMA_BUFLEN_MAX ESP32S3_DMA_BUFLEN_MAX +#define ESPRESSIF_DMA_PERIPH_I2S ESP32S3_DMA_PERIPH_I2S1 +#define ESP_SOURCE2IRQ ESP32S3_PERIPH2IRQ +#define esp_dmadesc_s esp32s3_dmadesc_s +#elif defined(CONFIG_ARCH_CHIP_ESP32S2) +#define esp_setup_irq esp32s2_setup_irq +#define esp_teardown_irq esp32s2_teardown_irq +#define esp_configgpio esp32s2_configgpio +#define esp_gpio_matrix_in esp32s2_gpio_matrix_in +#define esp_gpio_matrix_out esp32s2_gpio_matrix_out +#define esp_gpiowrite esp32s2_gpiowrite +#define esp_dma_setup esp32s2_dma_init +#define ESP_SOURCE2IRQ ESP32S2_PERIPH2IRQ +#define esp_dmadesc_s esp32s2_dmadesc_s +#define ESPRESSIF_DMA_BUFLEN_MAX ESP32S2_DMA_DATALEN_MAX +#define ESP_IRQ_PRIORITY_DEFAULT ESP32S2_INT_PRIO_DEF +#define ESP_IRQ_TRIGGER_LEVEL ESP32S2_CPUINT_LEVEL +#define I2S0O_SD_OUT_IDX I2S0O_DATA_OUT23_IDX +#define I2S0I_SD_IN_IDX I2S0I_DATA_IN15_IDX +#define I2S0_MCLK_OUT_IDX CLK_I2S_MUX_IDX +#elif defined(CONFIG_ARCH_CHIP_ESP32) +#define esp_setup_irq esp32_setup_irq +#define esp_teardown_irq esp32_teardown_irq +#define esp_configgpio esp32_configgpio +#define esp_gpio_matrix_in esp32_gpio_matrix_in +#define esp_gpio_matrix_out esp32_gpio_matrix_out +#define esp_gpiowrite esp32_gpiowrite +#define esp_dma_setup esp32_dma_init +#define esp_dmadesc_s esp32_dmadesc_s +#define ESP_SOURCE2IRQ ESP32_PERIPH2IRQ +#define ESPRESSIF_DMA_BUFLEN_MAX ESP32_DMA_DATALEN_MAX +#define I2S0O_SD_OUT_IDX I2S0O_DATA_OUT23_IDX +#define I2S0I_SD_IN_IDX I2S0I_DATA_IN15_IDX +#define I2S0_MCLK_OUT_IDX -1 +#define I2S1O_SD_OUT_IDX I2S1O_DATA_OUT23_IDX +#define I2S1I_SD_IN_IDX I2S1I_DATA_IN15_IDX +#define I2S1_MCLK_OUT_IDX -1 +#define ESP_IRQ_PRIORITY_DEFAULT 1 +#define ESP_IRQ_TRIGGER_LEVEL ESP32_CPUINT_LEVEL +#endif + +#ifdef CONFIG_ESPRESSIF_I2S0_DATA_BIT_WIDTH_8BIT +# define ESPRESSIF_I2S0_DATA_BIT_WIDTH 8 +#elif CONFIG_ESPRESSIF_I2S0_DATA_BIT_WIDTH_16BIT +# define ESPRESSIF_I2S0_DATA_BIT_WIDTH 16 +#elif CONFIG_ESPRESSIF_I2S0_DATA_BIT_WIDTH_24BIT +# define ESPRESSIF_I2S0_DATA_BIT_WIDTH 24 +#elif CONFIG_ESPRESSIF_I2S0_DATA_BIT_WIDTH_32BIT +# define ESPRESSIF_I2S0_DATA_BIT_WIDTH 32 +#endif + +#ifdef CONFIG_ESPRESSIF_I2S1_DATA_BIT_WIDTH_8BIT +# define ESPRESSIF_I2S1_DATA_BIT_WIDTH 8 +#elif CONFIG_ESPRESSIF_I2S1_DATA_BIT_WIDTH_16BIT +# define ESPRESSIF_I2S1_DATA_BIT_WIDTH 16 +#elif CONFIG_ESPRESSIF_I2S1_DATA_BIT_WIDTH_24BIT +# define ESPRESSIF_I2S1_DATA_BIT_WIDTH 24 +#elif CONFIG_ESPRESSIF_I2S1_DATA_BIT_WIDTH_32BIT +# define ESPRESSIF_I2S1_DATA_BIT_WIDTH 32 +#endif + +/* I2S DMA RX/TX description number */ + +#define I2S_DMADESC_NUM (CONFIG_I2S_DMADESC_NUM) + +/* I2S DMA channel number */ + +#define I2S_DMA_CHANNEL_MAX (2) + +#ifdef CONFIG_ESPRESSIF_I2S0_TX +# define I2S0_TX_ENABLED 1 +# define I2S_HAVE_TX 1 +#else +# define I2S0_TX_ENABLED 0 +#endif + +#ifdef CONFIG_ESPRESSIF_I2S0_RX +# define I2S0_RX_ENABLED 1 +# define I2S_HAVE_RX 1 +#else +# define I2S0_RX_ENABLED 0 +#endif + +#ifdef CONFIG_ESPRESSIF_I2S1_TX +# define I2S1_TX_ENABLED 1 +# define I2S_HAVE_TX 1 +#else +# define I2S1_TX_ENABLED 0 +#endif + +#ifdef CONFIG_ESPRESSIF_I2S1_RX +# define I2S1_RX_ENABLED 1 +# define I2S_HAVE_RX 1 +#else +# define I2S1_RX_ENABLED 0 +#endif + +/* Debug ********************************************************************/ + +#ifdef CONFIG_DEBUG_I2S_INFO +# define CONFIG_ESPRESSIF_I2S_DUMPBUFFERS +#else +# undef CONFIG_ESPRESSIF_I2S_DUMPBUFFERS +#endif + +#define I2S_GPIO_UNUSED -1 /* For signals which are not used */ + +#if CONFIG_ARCH_CHIP_ESP32 +# define I2S_STD_PHILIPS_SLOT_DEFAULT_CONFIG(cfg, bits_per_sample, mask) \ + cfg.slot_mask = mask, \ + cfg.ws_width = bits_per_sample, \ + cfg.ws_pol = false, \ + cfg.bit_shift = true, \ + cfg.msb_right = true \ + +# define I2S_STD_PCM_SLOT_DEFAULT_CONFIG(cfg, bits_per_sample, mask) \ + cfg.slot_mask = mask, \ + cfg.ws_width = 1, \ + cfg.ws_pol = true, \ + cfg.bit_shift = false, \ + cfg.msb_right = true \ + +# define I2S_STD_MSB_SLOT_DEFAULT_CONFIG(cfg, bits_per_sample, mask) \ + cfg.slot_mask = mask, \ + cfg.ws_width = bits_per_sample, \ + cfg.ws_pol = false, \ + cfg.bit_shift = false, \ + cfg.msb_right = true \ + +#endif /* CONFIG_ARCH_CHIP_ESP32 */ + +#ifdef CONFIG_ARCH_CHIP_ESP32S2 +#define I2S_STD_PHILIPS_SLOT_DEFAULT_CONFIG(cfg, bits_per_sample, mask) \ + cfg.slot_mask = (mask), \ + cfg.ws_width = bits_per_sample, \ + cfg.ws_pol = false, \ + cfg.bit_shift = true, \ + cfg.msb_right = true \ + +#define I2S_STD_PCM_SLOT_DEFAULT_CONFIG(cfg, bits_per_sample, mask) \ + cfg.slot_mask = (mask), \ + cfg.ws_width = 1, \ + cfg.ws_pol = true, \ + cfg.bit_shift = true, \ + cfg.msb_right = true \ + +# define I2S_STD_MSB_SLOT_DEFAULT_CONFIG(cfg, bits_per_sample, mask) \ + cfg.slot_mask = (mask), \ + cfg.ws_width = bits_per_sample, \ + cfg.ws_pol = false, \ + cfg.bit_shift = false, \ + cfg.msb_right = true \ + +#endif /* CONFIG_ARCH_CHIP_ESP32S2 */ + +#ifdef CONFIG_ARCH_CHIP_ESP32S3 +# define I2S_STD_PHILIPS_SLOT_DEFAULT_CONFIG(cfg, bits_per_sample, mask) \ + cfg.slot_mask = I2S_STD_SLOT_BOTH, \ + cfg.ws_width = bits_per_sample, \ + cfg.ws_pol = false, \ + cfg.bit_shift = true, \ + cfg.left_align = true, \ + cfg.big_endian = false, \ + cfg.bit_order_lsb = false \ + +# define I2S_STD_MSB_SLOT_DEFAULT_CONFIG(cfg, bits_per_sample, mask) \ + cfg.slot_mask = I2S_STD_SLOT_BOTH, \ + cfg.ws_width = bits_per_sample, \ + cfg.ws_pol = false, \ + cfg.bit_shift = false, \ + cfg.left_align = true, \ + cfg.big_endian = false, \ + cfg.bit_order_lsb = false \ + +# define I2S_STD_PCM_SLOT_DEFAULT_CONFIG(cfg, bits_per_sample, mask) \ + cfg.slot_mask = I2S_STD_SLOT_BOTH, \ + cfg.ws_width = 1, \ + cfg.ws_pol = true, \ + cfg.bit_shift = true, \ + cfg.left_align = true, \ + cfg.big_endian = false, \ + cfg.bit_order_lsb = false \ + +# define I2S_PDM_TX_SLOT_DEFAULT_CONFIG(cfg) \ + cfg.sd_prescale = 0, \ + cfg.sd_scale = I2S_PDM_SIG_SCALING_MUL_1, \ + cfg.hp_scale = I2S_PDM_SIG_SCALING_DIV_2, \ + cfg.lp_scale = I2S_PDM_SIG_SCALING_MUL_1, \ + cfg.sinc_scale = I2S_PDM_SIG_SCALING_MUL_1, \ + cfg.line_mode = I2S_PDM_TX_ONE_LINE_CODEC, \ + cfg.hp_en = true, \ + cfg.hp_cut_off_freq_hz = 35.5, \ + cfg.sd_dither = 0, \ + cfg.sd_dither2 = 1 \ + +#endif /* CONFIG_ARCH_CHIP_ESP32S3 */ + +#define MIN(x,y) ((x)<(y)?(x):(y)) + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +/* Multiplier of MCLK to sample rate */ + +typedef enum +{ + I2S_MCLK_MULTIPLE_128 = 128, /* mclk = sample_rate * 128 */ + I2S_MCLK_MULTIPLE_256 = 256, /* mclk = sample_rate * 256 */ + I2S_MCLK_MULTIPLE_384 = 384, /* mclk = sample_rate * 384 */ + I2S_MCLK_MULTIPLE_512 = 512, /* mclk = sample_rate * 512 */ +} i2s_mclk_multiple_t; + +/* I2S Audio Standard Mode */ + +typedef enum +{ + I2S_STD_PHILIPS = 0, + I2S_STD_MSB, + I2S_STD_PCM, +#ifdef CONFIG_ARCH_CHIP_ESP32S3 + I2S_PDM, +#endif +} i2s_audio_mode_t; + +/* I2S Device hardware configuration */ + +struct esp_i2s_config_s +{ + uint32_t port; /* I2S port */ + uint32_t role; /* I2S port role (master or slave) */ + uint8_t data_width; /* I2S sample data width */ + uint32_t rate; /* I2S sample-rate */ + uint32_t total_slot; /* Total slot number */ + + bool tx_en; /* Is TX enabled? */ + bool rx_en; /* Is RX enabled? */ + int8_t mclk_pin; /* MCLK pin, output */ + + int tx_clk_src; /* Select the I2S TX source clock */ + int rx_clk_src; /* Select the I2S TX source clock */ + int clk_hz; /* Clock speed in hz */ + + /* BCLK pin, input in slave role, output in master role */ + + int8_t bclk_pin; + + /* WS pin, input in slave role, output in master role */ + + int8_t ws_pin; + + int8_t dout_pin; /* DATA pin, output */ + int8_t din_pin; /* DATA pin, input */ + + uint32_t bclk_in_insig; /* RX channel BCK signal (slave mode) index */ + uint32_t bclk_in_outsig; /* RX channel BCK signal (master mode) index */ + uint32_t bclk_out_insig; /* TX channel BCK signal (slave mode) index */ + uint32_t bclk_out_outsig; /* TX channel BCK signal (master mode) index */ + uint32_t ws_in_insig; /* RX channel WS signal (slave mode) index */ + uint32_t ws_in_outsig; /* RX channel WS signal (master mode) index */ + uint32_t ws_out_insig; /* TX channel WS signal (slave mode) index */ + uint32_t ws_out_outsig; /* TX channel WS signal (master mode) index */ + uint32_t din_insig; /* RX channel Data Input signal index */ + uint32_t dout_outsig; /* TX channel Data Output signal index */ + uint32_t mclk_out_sig; /* Master clock output index */ + + uint8_t audio_std_mode; /* Select audio standard (i2s_audio_mode_t) */ + + /* WS signal polarity, set true to enable high level first */ + + bool ws_pol; + + i2s_hal_context_t *ctx; /* Common layer struct */ + i2s_hal_clock_info_t *clk_info; /* Common layer clock info struct */ +}; + +struct esp_buffer_s +{ + struct esp_buffer_s *flink; /* Supports a singly linked list */ + + /* The associated DMA in/outlink */ + + struct esp_dmadesc_s dma_link[I2S_DMADESC_NUM]; + + i2s_callback_t callback; /* DMA completion callback */ + uint32_t timeout; /* Timeout value of the DMA transfers */ + void *arg; /* Callback's argument */ + struct ap_buffer_s *apb; /* The audio buffer */ + uint8_t *buf; /* The DMA's descriptor buffer */ + uint32_t nbytes; /* The DMA's descriptor buffer size */ + int result; /* The result of the transfer */ +}; + +/* Internal buffer must be aligned to the bytes_per_sample. Sometimes, + * however, the audio buffer is not aligned and additional bytes must + * be copied to be inserted on the next buffer. This structure keeps + * track of the bytes that were not written to the internal buffer yet. + */ + +struct esp_buffer_carry_s +{ + uint32_t value; + size_t bytes; +}; + +/* This structure describes the state of one receiver or transmitter + * transport. + */ + +struct esp_transport_s +{ + sq_queue_t pend; /* A queue of pending transfers */ + sq_queue_t act; /* A queue of active transfers */ + sq_queue_t done; /* A queue of completed transfers */ + struct work_s work; /* Supports worker thread operations */ + + /* Bytes to be written at the beginning of the next DMA buffer */ + + struct esp_buffer_carry_s carry; +}; + +/* The state of the one I2S peripheral */ + +struct esp_i2s_s +{ + struct i2s_dev_s dev; /* Externally visible I2S interface */ + mutex_t lock; /* Ensures mutually exclusive access */ + uint8_t cpu; /* CPU ID */ + spinlock_t slock; /* Device specific lock. */ + + /* Port configuration */ + + const struct esp_i2s_config_s *config; + + uint32_t mclk_freq; /* I2S actual master clock */ + uint32_t mclk_multiple; /* The multiple of mclk to the sample rate */ + uint32_t channels; /* Audio channels (1:mono or 2:stereo) */ + uint32_t rate; /* I2S actual configured sample-rate */ + uint32_t data_width; /* I2S actual configured data_width */ + uint32_t dma_channel; /* I2S DMA channel being used */ + +#ifdef I2S_HAVE_TX + struct esp_transport_s tx; /* TX transport state */ + + int tx_irq; /* TX IRQ */ + bool tx_started; /* TX channel started */ +#endif /* I2S_HAVE_TX */ + +#ifdef I2S_HAVE_RX + struct esp_transport_s rx; /* RX transport state */ + + int rx_irq; /* RX IRQ */ + bool rx_started; /* RX channel started */ +#endif /* I2S_HAVE_RX */ + + bool streaming; /* Is I2S peripheral active? */ + + /* Pre-allocated pool of buffer containers */ + + sem_t bufsem; /* Buffer wait semaphore */ + struct esp_buffer_s *bf_freelist; /* A list a free buffer containers */ + struct esp_buffer_s containers[CONFIG_ESPRESSIF_I2S_MAXINFLIGHT]; +}; + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +/* Register helpers */ + +#ifdef CONFIG_ESPRESSIF_I2S_DUMPBUFFERS +# define i2s_dump_buffer(m,b,s) lib_dumpbuffer(m,b,s) +#else +# define i2s_dump_buffer(m,b,s) +#endif + +/* Buffer container helpers */ + +static struct esp_buffer_s * + i2s_buf_allocate(struct esp_i2s_s *priv); +static void i2s_buf_free(struct esp_i2s_s *priv, + struct esp_buffer_s *bfcontainer); +static int i2s_buf_initialize(struct esp_i2s_s *priv); + +/* DMA support */ + +#ifdef I2S_HAVE_TX +static IRAM_ATTR int i2s_txdma_setup(struct esp_i2s_s *priv, + struct esp_buffer_s *bfcontainer); +static void i2s_tx_worker(void *arg); +static void i2s_tx_schedule(struct esp_i2s_s *priv, + struct esp_dmadesc_s *outlink); +#endif /* I2S_HAVE_TX */ + +#ifdef I2S_HAVE_RX +static IRAM_ATTR int i2s_rxdma_setup(struct esp_i2s_s *priv, + struct esp_buffer_s *bfcontainer); +static void i2s_rx_worker(void *arg); +static void i2s_rx_schedule(struct esp_i2s_s *priv, + struct esp_dmadesc_s *outlink); +#endif /* I2S_HAVE_RX */ + +/* I2S methods (and close friends) */ + +static int32_t i2s_check_mclkfrequency(struct esp_i2s_s *priv); +static uint32_t i2s_set_datawidth(struct esp_i2s_s *priv); +static void i2s_set_clock(struct esp_i2s_s *priv); +static uint32_t i2s_getmclkfrequency(struct i2s_dev_s *dev); +static uint32_t i2s_setmclkfrequency(struct i2s_dev_s *dev, + uint32_t frequency); +static int i2s_ioctl(struct i2s_dev_s *dev, int cmd, unsigned long arg); + +#ifdef I2S_HAVE_TX +static void i2s_tx_channel_start(struct esp_i2s_s *priv); +static void i2s_tx_channel_stop(struct esp_i2s_s *priv); +static int i2s_txchannels(struct i2s_dev_s *dev, uint8_t channels); +static uint32_t i2s_txsamplerate(struct i2s_dev_s *dev, uint32_t rate); +static uint32_t i2s_txdatawidth(struct i2s_dev_s *dev, int bits); +static int i2s_send(struct i2s_dev_s *dev, struct ap_buffer_s *apb, + i2s_callback_t callback, void *arg, + uint32_t timeout); +#endif /* I2S_HAVE_TX */ + +#ifdef I2S_HAVE_RX +static void i2s_rx_channel_start(struct esp_i2s_s *priv); +static void i2s_rx_channel_stop(struct esp_i2s_s *priv); +static int i2s_rxchannels(struct i2s_dev_s *dev, uint8_t channels); +static uint32_t i2s_rxsamplerate(struct i2s_dev_s *dev, uint32_t rate); +static uint32_t i2s_rxdatawidth(struct i2s_dev_s *dev, int bits); +static int i2s_receive(struct i2s_dev_s *dev, struct ap_buffer_s *apb, + i2s_callback_t callback, void *arg, + uint32_t timeout); +#endif /* I2S_HAVE_RX */ + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static const struct i2s_ops_s g_i2sops = +{ +#ifdef I2S_HAVE_TX + .i2s_txchannels = i2s_txchannels, + .i2s_txsamplerate = i2s_txsamplerate, + .i2s_txdatawidth = i2s_txdatawidth, + .i2s_send = i2s_send, +#endif /* I2S_HAVE_TX */ + +#ifdef I2S_HAVE_RX + .i2s_rxchannels = i2s_rxchannels, + .i2s_rxsamplerate = i2s_rxsamplerate, + .i2s_rxdatawidth = i2s_rxdatawidth, + .i2s_receive = i2s_receive, +#endif /* I2S_HAVE_RX */ + + .i2s_ioctl = i2s_ioctl, + .i2s_getmclkfrequency = i2s_getmclkfrequency, + .i2s_setmclkfrequency = i2s_setmclkfrequency, +}; + +#ifdef CONFIG_ESPRESSIF_I2S0 + +i2s_hal_context_t ctx_i2s0 = +{ + 0 +}; + +i2s_hal_clock_info_t clk_info_i2s0 = +{ + 0 +}; + +static const struct esp_i2s_config_s esp_i2s0_config = +{ + .port = 0, +#ifdef CONFIG_ESPRESSIF_I2S0_ROLE_MASTER + .role = I2S_ROLE_MASTER, +#else + .role = I2S_ROLE_SLAVE, +#endif /* CONFIG_ESPRESSIF_I2S0_ROLE_MASTER */ + .data_width = ESPRESSIF_I2S0_DATA_BIT_WIDTH, + .rate = CONFIG_ESPRESSIF_I2S0_SAMPLE_RATE, + .total_slot = 2, + .tx_en = I2S0_TX_ENABLED, + .rx_en = I2S0_RX_ENABLED, + .tx_clk_src = I2S_CLK_SRC_PLL_160M, + .rx_clk_src = I2S_CLK_SRC_PLL_160M, + .clk_hz = (2 * APB_CLK_FREQ), +#ifdef CONFIG_ESPRESSIF_I2S0_MCLK + .mclk_pin = CONFIG_ESPRESSIF_I2S0_MCLKPIN, +#else + .mclk_pin = I2S_GPIO_UNUSED, +#endif /* CONFIG_ESPRESSIF_I2S0_MCLK */ + .bclk_pin = CONFIG_ESPRESSIF_I2S0_BCLKPIN, + .ws_pin = CONFIG_ESPRESSIF_I2S0_WSPIN, +#ifdef CONFIG_ESPRESSIF_I2S0_DOUTPIN + .dout_pin = CONFIG_ESPRESSIF_I2S0_DOUTPIN, +#else + .dout_pin = I2S_GPIO_UNUSED, +#endif /* CONFIG_ESPRESSIF_I2S0_DOUTPIN */ +#ifdef CONFIG_ESPRESSIF_I2S0_DINPIN + .din_pin = CONFIG_ESPRESSIF_I2S0_DINPIN, +#else + .din_pin = I2S_GPIO_UNUSED, +#endif /* CONFIG_ESPRESSIF_I2S0_DINPIN */ + .bclk_in_insig = I2S0I_BCK_IN_IDX, + .bclk_in_outsig = I2S0I_BCK_OUT_IDX, + .bclk_out_insig = I2S0O_BCK_IN_IDX, + .bclk_out_outsig = I2S0O_BCK_OUT_IDX, + .ws_in_insig = I2S0I_WS_IN_IDX, + .ws_in_outsig = I2S0I_WS_OUT_IDX, + .ws_out_insig = I2S0O_WS_IN_IDX, + .ws_out_outsig = I2S0O_WS_OUT_IDX, + .din_insig = I2S0I_SD_IN_IDX, + .dout_outsig = I2S0O_SD_OUT_IDX, + .mclk_out_sig = I2S0_MCLK_OUT_IDX, + .audio_std_mode = I2S_STD_PHILIPS, + .ctx = &ctx_i2s0, + .clk_info = &clk_info_i2s0, +}; + +static struct esp_i2s_s esp_i2s0_priv = +{ + .dev = + { + .ops = &g_i2sops, + }, + .lock = NXMUTEX_INITIALIZER, + .config = &esp_i2s0_config, + .bufsem = SEM_INITIALIZER(0), +}; +#endif /* CONFIG_ESPRESSIF_I2S0 */ + +#ifdef CONFIG_ESPRESSIF_I2S1 + +i2s_hal_context_t ctx_i2s1 = +{ + 0 +}; + +i2s_hal_clock_info_t clk_info_i2s1 = +{ + 0 +}; + +static const struct esp_i2s_config_s esp_i2s1_config = +{ + .port = 1, +#ifdef CONFIG_ESPRESSIF_I2S1_ROLE_MASTER + .role = I2S_ROLE_MASTER, +#else + .role = I2S_ROLE_SLAVE, +#endif /* CONFIG_ESPRESSIF_I2S1_ROLE_MASTER */ + .data_width = ESPRESSIF_I2S1_DATA_BIT_WIDTH, + .rate = CONFIG_ESPRESSIF_I2S1_SAMPLE_RATE, + .total_slot = 2, + .tx_en = I2S1_TX_ENABLED, + .rx_en = I2S1_RX_ENABLED, + .tx_clk_src = I2S_CLK_SRC_PLL_160M, + .rx_clk_src = I2S_CLK_SRC_PLL_160M, + .clk_hz = (2 * APB_CLK_FREQ), +#ifdef CONFIG_ESPRESSIF_I2S1_MCLK + .mclk_pin = CONFIG_ESPRESSIF_I2S1_MCLKPIN, +#else + .mclk_pin = I2S_GPIO_UNUSED, +#endif /* CONFIG_ESPRESSIF_I2S1_MCLK */ + .bclk_pin = CONFIG_ESPRESSIF_I2S1_BCLKPIN, + .ws_pin = CONFIG_ESPRESSIF_I2S1_WSPIN, +#ifdef CONFIG_ESPRESSIF_I2S1_DOUTPIN + .dout_pin = CONFIG_ESPRESSIF_I2S1_DOUTPIN, +#else + .dout_pin = I2S_GPIO_UNUSED, +#endif /* CONFIG_ESPRESSIF_I2S1_DOUTPIN */ +#ifdef CONFIG_ESPRESSIF_I2S1_DINPIN + .din_pin = CONFIG_ESPRESSIF_I2S1_DINPIN, +#else + .din_pin = I2S_GPIO_UNUSED, +#endif /* CONFIG_ESPRESSIF_I2S1_DINPIN */ + .bclk_in_insig = I2S1I_BCK_IN_IDX, + .bclk_in_outsig = I2S1I_BCK_OUT_IDX, + .bclk_out_insig = I2S1O_BCK_IN_IDX, + .bclk_out_outsig = I2S1O_BCK_OUT_IDX, + .ws_in_insig = I2S1I_WS_IN_IDX, + .ws_in_outsig = I2S1I_WS_OUT_IDX, + .ws_out_insig = I2S1O_WS_IN_IDX, + .ws_out_outsig = I2S1O_WS_OUT_IDX, + .din_insig = I2S1I_SD_IN_IDX, + .dout_outsig = I2S1O_SD_OUT_IDX, + .mclk_out_sig = I2S1_MCLK_OUT_IDX, + .audio_std_mode = I2S_STD_PHILIPS, + .ctx = &ctx_i2s1, + .clk_info = &clk_info_i2s1, +}; + +static struct esp_i2s_s esp_i2s1_priv = +{ + .dev = + { + .ops = &g_i2sops, + }, + .lock = NXMUTEX_INITIALIZER, + .config = &esp_i2s1_config, + .bufsem = SEM_INITIALIZER(0), +}; +#endif /* CONFIG_ESPRESSIF_I2S1 */ + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: i2s_buf_allocate + * + * Description: + * Allocate a buffer container by removing the one at the head of the + * free list + * + * Input Parameters: + * priv - Initialized I2S device structure. + * + * Returned Value: + * A non-NULL pointer to the allocate buffer container on success; NULL if + * there are no available buffer containers. + * + * Assumptions: + * The caller does NOT have exclusive access to the I2S state structure. + * That would result in a deadlock! + * + ****************************************************************************/ + +static struct esp_buffer_s *i2s_buf_allocate(struct esp_i2s_s *priv) +{ + struct esp_buffer_s *bfcontainer; + irqstate_t flags; + int ret; + + /* Set aside a buffer container. By doing this, we guarantee that we will + * have at least one free buffer container. + */ + + ret = nxsem_wait_uninterruptible(&priv->bufsem); + if (ret < 0) + { + return NULL; + } + + /* Get the buffer from the head of the free list */ + + flags = spin_lock_irqsave(&priv->slock); + bfcontainer = priv->bf_freelist; + DEBUGASSERT(bfcontainer); + + /* Unlink the buffer from the freelist */ + + priv->bf_freelist = bfcontainer->flink; + spin_unlock_irqrestore(&priv->slock, flags); + return bfcontainer; +} + +/**************************************************************************** + * Name: i2s_buf_free + * + * Description: + * Free buffer container by adding it to the head of the free list + * + * Input Parameters: + * priv - Initialized I2S device structure. + * bfcontainer - The buffer container to be freed + * + * Returned Value: + * None + * + * Assumptions: + * The caller has exclusive access to the I2S state structure + * + ****************************************************************************/ + +static void i2s_buf_free(struct esp_i2s_s *priv, + struct esp_buffer_s *bfcontainer) +{ + irqstate_t flags; + + /* Put the buffer container back on the free list (circbuf) */ + + flags = spin_lock_irqsave(&priv->slock); + + bfcontainer->apb = NULL; + bfcontainer->buf = NULL; + bfcontainer->nbytes = 0; + bfcontainer->flink = priv->bf_freelist; + priv->bf_freelist = bfcontainer; + + spin_unlock_irqrestore(&priv->slock, flags); + + /* Wake up any threads waiting for a buffer container */ + + nxsem_post(&priv->bufsem); +} + +/**************************************************************************** + * Name: i2s_buf_initialize + * + * Description: + * Initialize the buffer container allocator by adding all of the + * pre-allocated buffer containers to the free list + * + * Input Parameters: + * priv - Initialized I2S device structure. + * + * Returned Value: + * OK on success; A negated errno value on failure. + * + * Assumptions: + * Called early in I2S initialization so that there are no issues with + * concurrency. + * + ****************************************************************************/ + +static int i2s_buf_initialize(struct esp_i2s_s *priv) +{ +#ifdef I2S_HAVE_TX + priv->tx.carry.bytes = 0; + priv->tx.carry.value = 0; +#endif /* I2S_HAVE_TX */ + + priv->bf_freelist = NULL; + for (int i = 0; i < CONFIG_ESPRESSIF_I2S_MAXINFLIGHT; i++) + { + i2s_buf_free(priv, &priv->containers[i]); + } + + return OK; +} + +/**************************************************************************** + * Name: i2s_txdma_start + * + * Description: + * Initiate the next TX DMA transfer. The DMA outlink was previously bound + * so it is safe to start the next DMA transfer at interrupt level. + * + * Input Parameters: + * priv - Initialized I2S device structure. + * + * Returned Value: + * OK on success; a negated errno value on failure + * + * Assumptions: + * Interrupts are disabled + * + ****************************************************************************/ + +#ifdef I2S_HAVE_TX +static int IRAM_ATTR i2s_txdma_start(struct esp_i2s_s *priv) +{ + struct esp_buffer_s *bfcontainer; + + /* If there is already an active transmission in progress, then bail + * returning success. + */ + + if (!sq_empty(&priv->tx.act)) + { + return OK; + } + + /* If there are no pending transfer, then bail returning success */ + + if (sq_empty(&priv->tx.pend)) + { + return OK; + } + + i2s_hal_tx_reset(priv->config->ctx); + i2s_hal_tx_reset_fifo(priv->config->ctx); + + /* Start transmission if no data is already being transmitted */ + + bfcontainer = (struct esp_buffer_s *)sq_remfirst(&priv->tx.pend); + +#ifdef CONFIG_ARCH_CHIP_ESP32S3 + esp_dma_load(bfcontainer->dma_link, priv->dma_channel, I2S_DIR_TX); + esp_dma_enable(priv->dma_channel, I2S_DIR_TX); +#else + i2s_hal_tx_enable_dma(priv->config->ctx); + i2s_hal_tx_enable_intr(priv->config->ctx); + i2s_hal_tx_start_link(priv->config->ctx, (uint32_t) bfcontainer->dma_link); +#endif + + i2s_hal_tx_start(priv->config->ctx); + + sq_addlast((sq_entry_t *)bfcontainer, &priv->tx.act); + + return OK; +} +#endif /* I2S_HAVE_TX */ + +/**************************************************************************** + * Name: i2s_rxdma_start + * + * Description: + * Initiate the next RX DMA transfer. Assuming the DMA inlink is already + * bound, it's safe to start the next DMA transfer in an interrupt context. + * + * Input Parameters: + * priv - Initialized I2S device structure. + * + * Returned Value: + * OK on success; a negated errno value on failure + * + * Assumptions: + * Interrupts are disabled + * + ****************************************************************************/ + +#ifdef I2S_HAVE_RX +static int i2s_rxdma_start(struct esp_i2s_s *priv) +{ + struct esp_buffer_s *bfcontainer; + size_t eof_nbytes; + + /* If there is already an active transmission in progress, then bail + * returning success. + */ + + if (!sq_empty(&priv->rx.act)) + { + return OK; + } + + /* If there are no pending transfer, then bail returning success */ + + if (sq_empty(&priv->rx.pend)) + { + return OK; + } + + i2s_hal_rx_reset(priv->config->ctx); + i2s_hal_rx_reset_fifo(priv->config->ctx); + + bfcontainer = (struct esp_buffer_s *)sq_remfirst(&priv->rx.pend); + + /* If there isn't already an active transmission in progress, + * then start it. + */ + + eof_nbytes = MIN(bfcontainer->nbytes, ESPRESSIF_DMA_BUFLEN_MAX); + + i2s_ll_rx_set_eof_num(priv->config->ctx->dev, eof_nbytes); + +#ifdef CONFIG_ARCH_CHIP_ESP32S3 + esp_dma_load(bfcontainer->dma_link, priv->dma_channel, I2S_DIR_RX); + esp_dma_enable(priv->dma_channel, I2S_DIR_RX); +#else + i2s_hal_rx_enable_dma(priv->config->ctx); + i2s_hal_rx_enable_intr(priv->config->ctx); + i2s_hal_rx_start_link(priv->config->ctx, (uint32_t) bfcontainer->dma_link); + i2s_ll_rx_set_eof_num(priv->config->ctx->dev, bfcontainer->nbytes); +#endif + + i2s_hal_rx_start(priv->config->ctx); + + sq_addlast((sq_entry_t *)bfcontainer, &priv->rx.act); + + return OK; +} +#endif /* I2S_HAVE_RX */ + +/**************************************************************************** + * Name: i2s_txdma_setup + * + * Description: + * Setup the next TX DMA transfer + * + * Input Parameters: + * priv - Initialized I2S device structure. + * bfcontainer - The buffer container to be set up + * + * Returned Value: + * OK on success; a negated errno value on failure + * + * Assumptions: + * Interrupts are disabled + * + ****************************************************************************/ + +#ifdef I2S_HAVE_TX +static IRAM_ATTR int i2s_txdma_setup(struct esp_i2s_s *priv, + struct esp_buffer_s *bfcontainer) +{ + int ret = OK; + size_t carry_size; + uint32_t bytes_queued; + uint32_t data_copied; + struct ap_buffer_s *apb; + struct esp_dmadesc_s *outlink; + apb_samp_t samp_size; + irqstate_t flags; + uint8_t *buf; + uint8_t padding; + uint8_t *samp; + + DEBUGASSERT(bfcontainer && bfcontainer->apb); + + apb = bfcontainer->apb; + outlink = bfcontainer->dma_link; + + /* Get the transfer information, accounting for any data offset */ + + const apb_samp_t bytes_per_sample = priv->data_width / 8; + samp = &apb->samp[apb->curbyte]; + samp_size = (apb->nbytes - apb->curbyte) + priv->tx.carry.bytes; + carry_size = samp_size % bytes_per_sample; + + /* Allocate the current audio buffer considering the remaining bytes + * carried from the last upper half audio buffer. + */ + + bfcontainer->buf = calloc(bfcontainer->nbytes, 1); + if (bfcontainer->buf == NULL) + { + i2serr("Failed to allocate the DMA internal buffer " + "[%" PRIu32 " bytes]", bfcontainer->nbytes); + return -ENOMEM; + } + + data_copied = 0; + buf = bfcontainer->buf; + + /* Copy the remaining bytes from the last audio buffer to the current + * audio buffer. The remaining bytes are part of a sample that was split + * between the last and the current audio buffer. Also, copy the bytes + * from that split sample that are on the current buffer to the internal + * buffer. + */ + + if (priv->tx.carry.bytes) + { + memcpy(buf, &priv->tx.carry.value, priv->tx.carry.bytes); + buf += priv->tx.carry.bytes; + data_copied += priv->tx.carry.bytes; + memcpy(buf, samp, (bytes_per_sample - priv->tx.carry.bytes)); + buf += (bytes_per_sample - priv->tx.carry.bytes); + samp += (bytes_per_sample - priv->tx.carry.bytes); + data_copied += (bytes_per_sample - priv->tx.carry.bytes); + } + + /* Copy the upper half buffer to the internal buffer considering that + * the current upper half buffer may not contain a complete sample at + * the end of the buffer (and those bytes needs to be carried to the + * next audio buffer). + */ + + memcpy(buf, samp, samp_size - (data_copied + carry_size)); + buf += samp_size - (data_copied + carry_size); + samp += samp_size - (data_copied + carry_size); + data_copied += samp_size - (data_copied + carry_size); + + /* If the audio buffer's size is not a multiple of the sample size, + * it's necessary to carry the remaining bytes that are part of what + * would be the last sample on this buffer. These bytes will then be + * saved and inserted at the beginning of the next DMA buffer to + * rebuild the sample correctly. + */ + + priv->tx.carry.bytes = carry_size; + if (priv->tx.carry.bytes) + { + memcpy((uint8_t *)&priv->tx.carry.value, samp, priv->tx.carry.bytes); + } + + /* Release our reference on the audio buffer. This may very likely + * cause the audio buffer to be freed. + */ + + apb_free(bfcontainer->apb); + + /* Configure DMA stream */ + +#ifdef CONFIG_ARCH_CHIP_ESP32S3 + bytes_queued = esp_dma_setup((struct esp_dmadesc_s *)outlink, + I2S_DMADESC_NUM, + (uint8_t *) bfcontainer->buf, + bfcontainer->nbytes, + true, + priv->dma_channel); +#else + bytes_queued = esp_dma_setup((struct esp_dmadesc_s *)outlink, + I2S_DMADESC_NUM, + (uint8_t *) bfcontainer->buf, + bfcontainer->nbytes); +#endif + + if (bytes_queued != bfcontainer->nbytes) + { + i2serr("Failed to enqueue I2S buffer " + "(%" PRIu32 " bytes of %" PRIu32 ")\n", + bytes_queued, bfcontainer->nbytes); + return -bytes_queued; + } + + flags = spin_lock_irqsave(&priv->slock); + + /* Add the buffer container to the end of the TX pending queue */ + + sq_addlast((sq_entry_t *)bfcontainer, &priv->tx.pend); + + /* Trigger DMA transfer if no transmission is in progress */ + + ret = i2s_txdma_start(priv); + + spin_unlock_irqrestore(&priv->slock, flags); + + return ret; +} +#endif /* I2S_HAVE_TX */ + +/**************************************************************************** + * Name: i2s_rxdma_setup + * + * Description: + * Setup the next RX DMA transfer + * + * Input Parameters: + * priv - Initialized I2S device structure. + * bfcontainer - The buffer container to be set up + * + * Returned Value: + * OK on success; a negated errno value on failure + * + * Assumptions: + * Interrupts are disabled + * + ****************************************************************************/ + +#ifdef I2S_HAVE_RX +static int i2s_rxdma_setup(struct esp_i2s_s *priv, + struct esp_buffer_s *bfcontainer) +{ + int ret = OK; + struct esp_dmadesc_s *inlink; + uint32_t bytes_queued; + irqstate_t flags; + + DEBUGASSERT(bfcontainer && bfcontainer->apb); + + inlink = bfcontainer->dma_link; + + /* Configure DMA stream */ + +#ifdef CONFIG_ARCH_CHIP_ESP32S3 + bytes_queued = esp_dma_setup((struct esp_dmadesc_s *)inlink, + I2S_DMADESC_NUM, + (uint8_t *) bfcontainer->apb->samp, + bfcontainer->nbytes, + false, + priv->dma_channel); +#else + bytes_queued = esp_dma_setup((struct esp_dmadesc_s *)inlink, + I2S_DMADESC_NUM, + (uint8_t *) bfcontainer->apb->samp, + bfcontainer->nbytes); +#endif + + if (bytes_queued != bfcontainer->nbytes) + { + i2serr("Failed to enqueue I2S buffer " + "(%" PRIu32 " bytes of %" PRIu32 ")\n", + bytes_queued, bfcontainer->nbytes); + return -bytes_queued; + } + + flags = spin_lock_irqsave(&priv->slock); + + /* Add the buffer container to the end of the RX pending queue */ + + sq_addlast((sq_entry_t *)bfcontainer, &priv->rx.pend); + + /* Trigger DMA transfer if no transmission is in progress */ + + ret = i2s_rxdma_start(priv); + + spin_unlock_irqrestore(&priv->slock, flags); + + return ret; +} +#endif /* I2S_HAVE_RX */ + +/**************************************************************************** + * Name: i2s_tx_schedule + * + * Description: + * An TX DMA completion has occurred. Schedule processing on + * the working thread. + * + * Input Parameters: + * priv - Initialized I2S device structure. + * outlink - DMA outlink descriptor that triggered the interrupt. + * + * Returned Value: + * None + * + * Assumptions: + * - Interrupts are disabled + * + ****************************************************************************/ + +#ifdef I2S_HAVE_TX +static void IRAM_ATTR i2s_tx_schedule(struct esp_i2s_s *priv, + struct esp_dmadesc_s *outlink) +{ + struct esp_buffer_s *bfcontainer; + struct esp_dmadesc_s *bfdesc; + dma_descriptor_t *bfdesc_ctrl; + int ret; + + /* Upon entry, the transfer(s) that just completed are the ones in the + * priv->tx.act queue. + */ + + /* Move all entries from the tx.act queue to the tx.done queue */ + + if (!sq_empty(&priv->tx.act)) + { + /* Remove the next buffer container from the tx.act list */ + + bfcontainer = (struct esp_buffer_s *)sq_peek(&priv->tx.act); + + /* Check if the DMA descriptor that generated an EOF interrupt is the + * last descriptor of the current buffer container's DMA outlink. + * REVISIT: what to do if we miss syncronization and the descriptor + * that generated the interrupt is different from the expected (the + * oldest of the list containing active transmissions)? + */ + + /* Find the last descriptor of the current buffer container */ + + bfdesc = bfcontainer->dma_link; + bfdesc_ctrl = (dma_descriptor_t *)bfdesc; + while (!(bfdesc_ctrl->dw0.suc_eof)) + { + DEBUGASSERT(bfdesc->next); + bfdesc = bfdesc->next; + } + + if (bfdesc == outlink) + { + sq_remfirst(&priv->tx.act); + + /* Report the result of the transfer */ + + bfcontainer->result = OK; + + /* Add the completed buffer container to the tail of the tx.done + * queue + */ + + sq_addlast((sq_entry_t *)bfcontainer, &priv->tx.done); + + /* Check if the DMA is IDLE */ + + if (sq_empty(&priv->tx.act)) + { + /* Then start the next DMA. */ + + i2s_txdma_start(priv); + } + } + + /* If the worker has completed running, then reschedule the working + * thread. + */ + + if (work_available(&priv->tx.work)) + { + /* Schedule the TX DMA done processing to occur on the worker + * thread. + */ + + ret = work_queue(HPWORK, &priv->tx.work, i2s_tx_worker, priv, 0); + if (ret != 0) + { + i2serr("ERROR: Failed to queue TX work: %d\n", ret); + } + } + } +} +#endif /* I2S_HAVE_TX */ + +/**************************************************************************** + * Name: i2s_rx_schedule + * + * Description: + * An RX DMA completion has occurred. Schedule processing on + * the working thread. + * + * Input Parameters: + * priv - Initialized I2S device structure. + * inlink - DMA inlink descriptor that triggered the interrupt. + * + * Returned Value: + * None + * + * Assumptions: + * - Interrupts are disabled + * + ****************************************************************************/ + +#ifdef I2S_HAVE_RX +static void i2s_rx_schedule(struct esp_i2s_s *priv, + struct esp_dmadesc_s *inlink) +{ + struct esp_buffer_s *bfcontainer; + struct esp_dmadesc_s *bfdesc; + dma_descriptor_t *bfdesc_ctrl; + int ret; + + /* Upon entry, the transfer(s) that just completed are the ones in the + * priv->rx.act queue. + */ + + /* Move all entries from the rx.act queue to the rx.done queue */ + + if (!sq_empty(&priv->rx.act)) + { + /* Remove the next buffer container from the rx.act list */ + + bfcontainer = (struct esp_buffer_s *)sq_peek(&priv->rx.act); + + /* Find the last descriptor of the current buffer container */ + + bfdesc = bfcontainer->dma_link; + bfdesc_ctrl = (dma_descriptor_t *)bfdesc; + + while (bfdesc->next != NULL && + (bfdesc_ctrl->dw0.suc_eof)) + { + bfdesc = bfdesc->next; + } + + if (bfdesc == inlink) + { + sq_remfirst(&priv->rx.act); + + /* Report the result of the transfer */ + + bfcontainer->result = OK; + + /* Add the completed buffer container to the tail of the rx.done + * queue + */ + + sq_addlast((sq_entry_t *)bfcontainer, &priv->rx.done); + + /* Check if the DMA is IDLE */ + + if (sq_empty(&priv->rx.act)) + { + /* Then start the next DMA. */ + + i2s_rxdma_start(priv); + } + } + + /* If the worker has completed running, then reschedule the working + * thread. + */ + + if (work_available(&priv->rx.work)) + { + /* Schedule the RX DMA done processing to occur on the worker + * thread. + */ + + ret = work_queue(HPWORK, &priv->rx.work, i2s_rx_worker, priv, 0); + if (ret != 0) + { + i2serr("ERROR: Failed to queue RX work: %d\n", ret); + } + } + } +} +#endif /* I2S_HAVE_RX */ + +/**************************************************************************** + * Name: i2s_tx_worker + * + * Description: + * TX transfer done worker + * + * Input Parameters: + * arg - the I2S device instance cast to void* + * + * Returned Value: + * None + * + ****************************************************************************/ + +#ifdef I2S_HAVE_TX +static void i2s_tx_worker(void *arg) +{ + struct esp_i2s_s *priv = (struct esp_i2s_s *)arg; + struct esp_buffer_s *bfcontainer; + irqstate_t flags; + + DEBUGASSERT(priv); + + /* When the transfer was started, the active buffer containers were removed + * from the tx.pend queue and saved in the tx.act queue. We get here when + * the DMA is finished. + * + * In any case, the buffer containers in tx.act will be moved to the end + * of the tx.done queue and tx.act will be emptied before this worker is + * started. + * + */ + + i2sinfo("tx.act.head=%p tx.done.head=%p\n", + priv->tx.act.head, priv->tx.done.head); + + /* Process each buffer in the tx.done queue */ + + while (sq_peek(&priv->tx.done) != NULL) + { + /* Remove the buffer container from the tx.done queue. NOTE that + * interrupts must be disabled to do this because the tx.done queue is + * also modified from the interrupt level. + */ + + flags = spin_lock_irqsave(&priv->slock); + bfcontainer = (struct esp_buffer_s *)sq_remfirst(&priv->tx.done); + spin_unlock_irqrestore(&priv->slock, flags); + + /* Perform the TX transfer done callback */ + + DEBUGASSERT(bfcontainer && bfcontainer->callback); + bfcontainer->callback(&priv->dev, bfcontainer->apb, + bfcontainer->arg, bfcontainer->result); + + /* Release the internal buffer used by the DMA outlink */ + + free(bfcontainer->buf); + + /* And release the buffer container */ + + i2s_buf_free(priv, bfcontainer); + } +} +#endif /* I2S_HAVE_TX */ + +/**************************************************************************** + * Name: i2s_rx_worker + * + * Description: + * RX transfer done worker + * + * Input Parameters: + * arg - the I2S device instance cast to void* + * + * Returned Value: + * None + * + ****************************************************************************/ + +#ifdef I2S_HAVE_RX +static void i2s_rx_worker(void *arg) +{ + struct esp_i2s_s *priv = (struct esp_i2s_s *)arg; + struct esp_buffer_s *bfcontainer; + struct esp_dmadesc_s *dmadesc; + dma_descriptor_t *dmadesc_ctrl; + irqstate_t flags; + + DEBUGASSERT(priv); + + /* When the transfer was started, the active buffer containers were removed + * from the rx.pend queue and saved in the rx.act queue. We get here when + * the DMA is finished. + * + * In any case, the buffer containers in rx.act will be moved to the end + * of the rx.done queue and rx.act will be emptied before this worker is + * started. + * + */ + + i2sinfo("rx.act.head=%p rx.done.head=%p\n", + priv->rx.act.head, priv->rx.done.head); + + /* Process each buffer in the rx.done queue */ + + while (sq_peek(&priv->rx.done) != NULL) + { + /* Remove the buffer container from the rx.done queue. NOTE that + * interrupts must be disabled to do this because the rx.done queue is + * also modified from the interrupt level. + */ + + flags = spin_lock_irqsave(&priv->slock); + bfcontainer = (struct esp_buffer_s *)sq_remfirst(&priv->rx.done); + spin_unlock_irqrestore(&priv->slock, flags); + + dmadesc = bfcontainer->dma_link; + dmadesc_ctrl = (dma_descriptor_t *)dmadesc; + + bfcontainer->apb->nbytes = 0; + + while (dmadesc != NULL && (dmadesc_ctrl->dw0.suc_eof)) + { + bfcontainer->apb->nbytes += dmadesc_ctrl->dw0.length; + dmadesc = dmadesc->next; + } + + /* Perform the RX transfer done callback */ + + DEBUGASSERT(bfcontainer && bfcontainer->callback); + + if (priv->streaming == false) + { + bfcontainer->apb->flags |= AUDIO_APB_FINAL; + } + + bfcontainer->callback(&priv->dev, bfcontainer->apb, + bfcontainer->arg, bfcontainer->result); + + /* Release our reference on the audio buffer. This may very likely + * cause the audio buffer to be freed. + */ + + apb_free(bfcontainer->apb); + + /* And release the buffer container */ + + i2s_buf_free(priv, bfcontainer); + } +} +#endif /* I2S_HAVE_RX */ + +/**************************************************************************** + * Name: i2s_configure + * + * Description: + * Configure I2S + * + * Input Parameters: + * priv - Partially initialized I2S device structure. This function + * will complete the I2S specific portions of the initialization + * + * Returned Value: + * None + * + ****************************************************************************/ + +static void i2s_configure(struct esp_i2s_s *priv) +{ + uint32_t tx_conf = 0; + uint32_t rx_conf = 0; + bool loopback = false; + i2s_hal_slot_config_t tx_slot_cfg = + { + 0 + }; + + i2s_hal_slot_config_t rx_slot_cfg = + { + 0 + }; + + /* Set peripheral clock and clear reset */ + + periph_module_enable(i2s_periph_signal[priv->config->port].module); + + i2s_hal_init(priv->config->ctx, priv->config->port); + i2s_ll_enable_clock(priv->config->ctx->dev); + + /* Configure multiplexed pins as connected on the board */ + + /* Enable TX channel */ + + if (priv->config->dout_pin != I2S_GPIO_UNUSED) + { + /* If TX channel is used, enable the clock source */ + + esp_gpiowrite(priv->config->dout_pin, 1); + esp_configgpio(priv->config->dout_pin, OUTPUT_FUNCTION_2); + esp_gpio_matrix_out(priv->config->dout_pin, + priv->config->dout_outsig, 0, 0); + } + + /* Enable RX channel */ + + if (priv->config->din_pin != I2S_GPIO_UNUSED) + { + /* If RX channel is used, enable the clock source */ + + /* Check for loopback mode */ + + if (priv->config->dout_pin != I2S_GPIO_UNUSED && + priv->config->din_pin == priv->config->dout_pin) + { + esp_configgpio(priv->config->din_pin, + INPUT_FUNCTION_2 | OUTPUT_FUNCTION_2); + esp_gpio_matrix_in(priv->config->din_pin, + priv->config->din_insig, 0); + esp_gpio_matrix_out(priv->config->din_pin, + priv->config->dout_outsig, 0, 0); + } + else + { + esp_configgpio(priv->config->din_pin, INPUT_FUNCTION_2); + esp_gpio_matrix_in(priv->config->din_pin, + priv->config->din_insig, 0); + } + } + + if (priv->config->role == I2S_ROLE_SLAVE) + { + if (priv->config->tx_en && !priv->config->rx_en) + { + /* For "tx + slave" mode, select TX signal index for ws and bck */ + + esp_gpiowrite(priv->config->ws_pin, 1); + esp_configgpio(priv->config->ws_pin, INPUT_FUNCTION_2); + esp_gpio_matrix_in(priv->config->ws_pin, + priv->config->ws_out_insig, 0); + + esp_gpiowrite(priv->config->bclk_pin, 1); + esp_configgpio(priv->config->bclk_pin, INPUT_FUNCTION_2); + esp_gpio_matrix_in(priv->config->bclk_pin, + priv->config->bclk_out_insig, 0); + } + else + { + /* For "tx + rx + slave" or "rx + slave" mode, select RX signal + * index for ws and bck. + */ + + esp_gpiowrite(priv->config->ws_pin, 1); + esp_configgpio(priv->config->ws_pin, INPUT_FUNCTION_2); + esp_gpio_matrix_in(priv->config->ws_pin, + priv->config->ws_in_insig, 0); + + esp_gpiowrite(priv->config->bclk_pin, 1); + esp_configgpio(priv->config->bclk_pin, INPUT_FUNCTION_2); + esp_gpio_matrix_in(priv->config->bclk_pin, + priv->config->bclk_in_insig, 0); + } + } + else + { + /* Considering master role for the I2S port */ + + /* Set MCLK pin */ + + if (priv->config->mclk_pin != I2S_GPIO_UNUSED) + { + i2sinfo("Configuring GPIO%" PRIu8 " to output master clock\n", + priv->config->mclk_pin); + + esp_gpiowrite(priv->config->mclk_pin, 1); + esp_configgpio(priv->config->mclk_pin, OUTPUT_FUNCTION_2); + esp_gpio_matrix_out(priv->config->mclk_pin, + priv->config->mclk_out_sig, 0, 0); + } + + if (priv->config->rx_en && !priv->config->tx_en) + { + /* For "rx + master" mode, select RX signal index for ws and bck */ + + esp_gpiowrite(priv->config->ws_pin, 1); + esp_configgpio(priv->config->ws_pin, OUTPUT_FUNCTION_2); + esp_gpio_matrix_out(priv->config->ws_pin, + priv->config->ws_in_outsig, 0, 0); + + esp_gpiowrite(priv->config->bclk_pin, 1); + esp_configgpio(priv->config->bclk_pin, OUTPUT_FUNCTION_2); + esp_gpio_matrix_out(priv->config->bclk_pin, + priv->config->bclk_in_outsig, 0, 0); + } + else + { + /* For "tx + rx + master" or "tx + master" mode, select TX signal + * index for ws and bck. + */ + + esp_gpiowrite(priv->config->ws_pin, 1); + esp_configgpio(priv->config->ws_pin, OUTPUT_FUNCTION_2); + esp_gpio_matrix_out(priv->config->ws_pin, + priv->config->ws_out_outsig, 0, 0); + + esp_gpiowrite(priv->config->bclk_pin, 1); + esp_configgpio(priv->config->bclk_pin, OUTPUT_FUNCTION_2); + esp_gpio_matrix_out(priv->config->bclk_pin, + priv->config->bclk_out_outsig, 0, 0); + } + } + + /* Share BCLK and WS if in full-duplex mode */ + + if (priv->config->tx_en && priv->config->rx_en) + { + loopback = true; + } + + i2s_ll_share_bck_ws(priv->config->ctx->dev, loopback); + + /* Configure the TX module */ + + if (priv->config->tx_en) + { + if (priv->channels == 1) + { + tx_slot_cfg.slot_mode = I2S_SLOT_MODE_MONO; + } + else + { + tx_slot_cfg.slot_mode = I2S_SLOT_MODE_STEREO; + } + + tx_slot_cfg.data_bit_width = priv->config->data_width; + tx_slot_cfg.slot_bit_width = I2S_SLOT_BIT_WIDTH_AUTO; + priv->data_width = priv->config->data_width; + + if (priv->config->audio_std_mode <= I2S_STD_PCM) + { + i2s_ll_tx_enable_std(priv->config->ctx->dev); + + if (priv->config->audio_std_mode == I2S_STD_PHILIPS) + { + I2S_STD_PHILIPS_SLOT_DEFAULT_CONFIG(tx_slot_cfg.std, + priv->data_width, + I2S_STD_SLOT_BOTH); + } + else if (priv->config->audio_std_mode == I2S_STD_MSB) + { + I2S_STD_MSB_SLOT_DEFAULT_CONFIG(tx_slot_cfg.std, + priv->data_width, + I2S_STD_SLOT_BOTH); + } + else + { + I2S_STD_PCM_SLOT_DEFAULT_CONFIG(tx_slot_cfg.std, + priv->data_width, + I2S_STD_SLOT_BOTH); + } + + i2s_hal_std_set_tx_slot(priv->config->ctx, + priv->config->role == I2S_ROLE_SLAVE, + &tx_slot_cfg); + } +#ifdef CONFIG_ARCH_CHIP_ESP32S3 + else + { + i2s_ll_tx_enable_pdm(priv->config->ctx->dev); + I2S_PDM_TX_SLOT_DEFAULT_CONFIG(tx_slot_cfg.pdm_tx); + i2s_hal_pdm_set_tx_slot(priv->config->ctx, + priv->config->role == I2S_ROLE_SLAVE, + &tx_slot_cfg); + } +#endif + + /* The default value for the master clock frequency (MCLK frequency) + * can be set from the sample rate multiplied by a fixed value, known + * as MCLK multiplier. This multiplier, however, should be divisible + * by the number of bytes from a sample, i.e, for 24 bits, the + * multiplier should be divisible by 3. NOTE: the MCLK frequency can + * be adjusted on runtime, so this value remains valid only if the + * upper half does not implement the `i2s_setmclkfrequency` method. + */ + + if (priv->config->data_width == I2S_DATA_BIT_WIDTH_24BIT) + { + priv->mclk_multiple = I2S_MCLK_MULTIPLE_384; + } + else + { + priv->mclk_multiple = I2S_MCLK_MULTIPLE_256; + } + + i2s_setmclkfrequency((struct i2s_dev_s *)priv, (priv->config->rate * + priv->mclk_multiple)); + + priv->rate = priv->config->rate; + i2s_set_clock(priv); + } + + /* Configure the RX module */ + + if (priv->config->rx_en) + { + if (priv->channels == 1) + { + rx_slot_cfg.slot_mode = I2S_SLOT_MODE_MONO; + } + else + { + rx_slot_cfg.slot_mode = I2S_SLOT_MODE_STEREO; + } + + rx_slot_cfg.data_bit_width = priv->config->data_width; + rx_slot_cfg.slot_bit_width = I2S_SLOT_BIT_WIDTH_AUTO; + + if (priv->config->audio_std_mode <= I2S_STD_PCM) + { + i2s_ll_rx_enable_std(priv->config->ctx->dev); + + if (priv->config->audio_std_mode == I2S_STD_PHILIPS) + { + I2S_STD_PHILIPS_SLOT_DEFAULT_CONFIG(rx_slot_cfg.std, + priv->data_width, + I2S_STD_SLOT_BOTH); + } + else if (priv->config->audio_std_mode == I2S_STD_MSB) + { + I2S_STD_MSB_SLOT_DEFAULT_CONFIG(rx_slot_cfg.std, + priv->data_width, + I2S_STD_SLOT_BOTH); + } + else + { + I2S_STD_PCM_SLOT_DEFAULT_CONFIG(tx_slot_cfg.std, + priv->data_width, + I2S_STD_SLOT_BOTH); + } + + i2s_hal_std_set_rx_slot(priv->config->ctx, + priv->config->role == I2S_ROLE_SLAVE, + &rx_slot_cfg); + } +#ifdef CONFIG_ARCH_CHIP_ESP32S3 + else + { + i2serr("Due to the lack of `PDM to PCM` module, \ + PDM RX is not available\n"); + } +#endif + + /* The default value for the master clock frequency (MCLK frequency) + * can be set from the sample rate multiplied by a fixed value, known + * as MCLK multiplier. This multiplier, however, should be divisible + * by the number of bytes from a sample, i.e, for 24 bits, the + * multiplier should be divisible by 3. NOTE: the MCLK frequency can + * be adjusted on runtime, so this value remains valid only if the + * upper half does not implement the `i2s_setmclkfrequency` method. + */ + + if (priv->config->data_width == I2S_DATA_BIT_WIDTH_24BIT) + { + priv->mclk_multiple = I2S_MCLK_MULTIPLE_384; + } + else + { + priv->mclk_multiple = I2S_MCLK_MULTIPLE_256; + } + + i2s_setmclkfrequency((struct i2s_dev_s *)priv, (priv->config->rate * + priv->mclk_multiple)); + + priv->rate = priv->config->rate; + i2s_set_clock(priv); + } +} + +/**************************************************************************** + * Name: i2s_check_mclkfrequency + * + * Description: + * Check if MCLK frequency is compatible with the current data width and + * bits/sample set. Master clock should be multiple of the sample rate and + * bclk at the same time. + * + * Input Parameters: + * priv - Initialized I2S device structure. + * + * Returned Value: + * Returns the current master clock or a negated errno value on failure. + * + ****************************************************************************/ + +static int32_t i2s_check_mclkfrequency(struct esp_i2s_s *priv) +{ + uint32_t mclk_freq; + uint32_t mclk_multiple = priv->mclk_multiple; + uint32_t bclk = priv->rate * priv->config->total_slot * priv->data_width; + int i; + + /* If the master clock is divisible by both the sample rate and the bit + * clock, everything is as expected and we can return the current master + * clock frequency. + */ + + if (priv->mclk_freq % priv->rate == 0 && priv->mclk_freq % bclk == 0) + { + priv->mclk_multiple = priv->mclk_freq / priv->rate; + return priv->mclk_freq; + } + + /* Select the lowest multiplier for setting the master clock */ + + for (mclk_multiple = I2S_MCLK_MULTIPLE_128; + mclk_multiple <= I2S_MCLK_MULTIPLE_512; + mclk_multiple += I2S_MCLK_MULTIPLE_128) + { + mclk_freq = priv->rate * mclk_multiple; + if (mclk_freq % priv->rate == 0 && mclk_freq % bclk == 0) + { + priv->mclk_multiple = mclk_multiple; + i2s_setmclkfrequency((struct i2s_dev_s *)priv, mclk_freq); + return priv->mclk_freq; + } + } + + return -EINVAL; +} + +/**************************************************************************** + * Name: i2s_set_datawidth + * + * Description: + * Set the I2S TX/RX data width. + * + * Input Parameters: + * priv - Initialized I2S device structure. + * + * Returned Value: + * Returns the resulting data width + * + ****************************************************************************/ + +static uint32_t i2s_set_datawidth(struct esp_i2s_s *priv) +{ + int width; +#ifdef I2S_HAVE_TX + if (priv->config->tx_en) + { + i2s_ll_tx_set_sample_bit(priv->config->ctx->dev, + priv->data_width, priv->data_width); +#ifdef CONFIG_ARCH_CHIP_ESP32S3 + i2s_ll_tx_set_half_sample_bit(priv->config->ctx->dev, + priv->data_width); +#else + i2s_ll_tx_enable_msb_right(priv->config->ctx->dev, true); +#endif + + if (priv->config->audio_std_mode != I2S_STD_PCM) + { + width = priv->data_width; + } + else + { + width = 1; + } + + i2s_ll_tx_set_ws_width(priv->config->ctx->dev, width); + } +#endif /* I2S_HAVE_TX */ + +#ifdef I2S_HAVE_RX + if (priv->config->rx_en) + { + i2s_ll_rx_set_sample_bit(priv->config->ctx->dev, + priv->data_width, priv->data_width); +#ifdef CONFIG_ARCH_CHIP_ESP32S3 + i2s_ll_rx_set_half_sample_bit(priv->config->ctx->dev, + priv->data_width); +#else + i2s_ll_tx_enable_msb_right(priv->config->ctx->dev, true); +#endif + + if (priv->config->audio_std_mode != I2S_STD_PCM) + { + width = priv->data_width; + } + else + { + width = 1; + } + + i2s_ll_rx_set_ws_width(priv->config->ctx->dev, width); + } +#endif /* I2S_HAVE_RX */ + + return priv->data_width; +} + +/**************************************************************************** + * Name: i2s_set_clock + * + * Description: + * Set the I2S TX sample rate by adjusting I2S clock. + * + * Input Parameters: + * priv - Initialized I2S device structure. + * + * Returned Value: + * None + * + ****************************************************************************/ + +static void i2s_set_clock(struct esp_i2s_s *priv) +{ + uint32_t bclk; + uint32_t mclk; + uint32_t sclk; + uint32_t mclk_div; + uint16_t bclk_div; + + sclk = priv->config->clk_hz; + + /* fmclk = bck_div * fbclk = fsclk / (mclk_div + b / a) + * mclk_div is the I2S clock divider's integral value + * b is the fraction clock divider's numerator value + * a is the fraction clock divider's denominator value + */ + + if (priv->config->role == I2S_ROLE_MASTER) + { + bclk = priv->rate * priv->config->total_slot * + priv->config->data_width; + mclk = priv->mclk_freq; + bclk_div = mclk / bclk; + } + else + { + /* For slave mode, mclk >= bclk * 8, so fix bclk_div to 2 first */ + + bclk_div = 8; + bclk = priv->rate * priv->config->total_slot * + priv->config->data_width; + mclk = bclk * bclk_div; + } + + /* Calculate the nearest integer value of the I2S clock divider */ + + mclk_div = sclk / mclk; + + i2sinfo("Clock division info: [sclk]%" PRIu32 " Hz [mdiv] %d " + "[mclk] %" PRIu32 " Hz [bdiv] %d [bclk] %" PRIu32 " Hz\n", + sclk, mclk_div, mclk, bclk_div, bclk); + + priv->config->clk_info->bclk = bclk; + priv->config->clk_info->bclk_div = bclk_div; + priv->config->clk_info->mclk = mclk; + priv->config->clk_info->mclk_div = mclk_div; + priv->config->clk_info->sclk = sclk; + +#ifdef I2S_HAVE_TX + i2s_hal_set_tx_clock(priv->config->ctx, + priv->config->clk_info, + priv->config->tx_clk_src); +#endif /* I2S_HAVE_TX */ + +#ifdef I2S_HAVE_RX + i2s_hal_set_rx_clock(priv->config->ctx, + priv->config->clk_info, + priv->config->rx_clk_src); +#endif /* I2S_HAVE_RX */ +} + +/**************************************************************************** + * Name: i2s_tx_channel_start + * + * Description: + * Start TX channel for the I2S port + * + * Input Parameters: + * priv - Initialized I2S device structure. + * + * Returned Value: + * None + * + ****************************************************************************/ + +#ifdef I2S_HAVE_TX +static void i2s_tx_channel_start(struct esp_i2s_s *priv) +{ + if (priv->config->tx_en) + { + if (priv->tx_started) + { + i2swarn("TX channel of port %d was previously started\n", + priv->config->port); + return; + } + + /* Reset the DMA operation */ + +#ifdef CONFIG_ARCH_CHIP_ESP32S3 + esp32s3_dma_reset_channel(priv->dma_channel, true); +#else + i2s_hal_tx_reset_dma(priv->config->ctx); +#endif + /* Reset the TX channel */ + + /* Reset TX FIFO */ + + i2s_hal_tx_reset(priv->config->ctx); + i2s_hal_tx_reset_fifo(priv->config->ctx); + + /* Set I2S_RX_UPDATE bit to update the configs. + * This bit is automatically cleared. + */ + + i2s_hal_tx_start(priv->config->ctx); + + /* Enable DMA interrupt */ + + up_enable_irq(priv->tx_irq); + +#ifdef CONFIG_ARCH_CHIP_ESP32S3 + esp32s3_dma_enable_interrupt(priv->dma_channel, true, + GDMA_LL_EVENT_TX_TOTAL_EOF | + GDMA_LL_EVENT_TX_DESC_ERROR, + true); +#else + i2s_hal_tx_enable_intr(priv->config->ctx); + i2s_hal_tx_enable_dma(priv->config->ctx); + i2s_hal_tx_start_link(priv->config->ctx, 0); +#endif + + priv->tx_started = true; + + i2sinfo("Started TX channel of port %d\n", priv->config->port); + } +} +#endif /* I2S_HAVE_TX */ + +/**************************************************************************** + * Name: i2s_rx_channel_start + * + * Description: + * Start RX channel for the I2S port + * + * Input Parameters: + * priv - Initialized I2S device structure. + * + * Returned Value: + * None + * + ****************************************************************************/ + +#ifdef I2S_HAVE_RX +static void i2s_rx_channel_start(struct esp_i2s_s *priv) +{ + if (priv->config->rx_en) + { + if (priv->rx_started) + { + i2swarn("RX channel of port %d was previously started\n", + priv->config->port); + return; + } + + /* Reset the DMA operation */ + +#ifdef CONFIG_ARCH_CHIP_ESP32S3 + esp32s3_dma_reset_channel(priv->dma_channel, false); +#else + i2s_hal_rx_reset_dma(priv->config->ctx); +#endif + + /* Reset the RX channel */ + + /* Reset RX FIFO */ + + i2s_hal_rx_reset(priv->config->ctx); + i2s_hal_rx_reset_fifo(priv->config->ctx); + + /* Set I2S_RX_UPDATE bit to update the configs. + * This bit is automatically cleared. + */ + + i2s_hal_rx_start(priv->config->ctx); + + /* Enable DMA interrupt */ + + up_enable_irq(priv->rx_irq); + +#ifdef CONFIG_ARCH_CHIP_ESP32S3 + esp32s3_dma_enable_interrupt(priv->dma_channel, false, + GDMA_LL_EVENT_RX_SUC_EOF, + true); +#else + i2s_hal_rx_enable_intr(priv->config->ctx); + i2s_hal_rx_enable_dma(priv->config->ctx); + i2s_hal_rx_start_link(priv->config->ctx, 0); +#endif + + priv->rx_started = true; + + i2sinfo("Started RX channel of port %d\n", priv->config->port); + } +} +#endif /* I2S_HAVE_RX */ + +/**************************************************************************** + * Name: i2s_tx_channel_stop + * + * Description: + * Stop TX channel for the I2S port + * + * Input Parameters: + * priv - Initialized I2S device structure. + * + * Returned Value: + * None + * + ****************************************************************************/ + +#ifdef I2S_HAVE_TX +static void i2s_tx_channel_stop(struct esp_i2s_s *priv) +{ + if (priv->config->tx_en) + { + if (!priv->tx_started) + { + i2swarn("TX channel of port %d was previously stopped\n", + priv->config->port); + return; + } + + /* Stop TX channel */ + + i2s_hal_tx_stop(priv->config->ctx); + + /* Stop outlink */ + + /* Disable DMA interrupt */ + +#ifdef CONFIG_ARCH_CHIP_ESP32S3 + esp32s3_dma_disable(priv->dma_channel, true); + esp32s3_dma_enable_interrupt(priv->dma_channel, true, + GDMA_LL_EVENT_TX_EOF, false); +#else + i2s_hal_tx_stop_link(priv->config->ctx); + i2s_hal_tx_disable_intr(priv->config->ctx); + i2s_hal_tx_disable_dma(priv->config->ctx); +#endif + + /* Disable DMA operation mode */ + + up_disable_irq(priv->tx_irq); + + priv->tx_started = false; + + i2sinfo("Stopped TX channel of port %d\n", priv->config->port); + } +} +#endif /* I2S_HAVE_TX */ + +/**************************************************************************** + * Name: i2s_rx_channel_stop + * + * Description: + * Stop RX channel for the I2S port + * + * Input Parameters: + * priv - Initialized I2S device structure. + * + * Returned Value: + * None + * + ****************************************************************************/ + +#ifdef I2S_HAVE_RX +static void i2s_rx_channel_stop(struct esp_i2s_s *priv) +{ + if (priv->config->rx_en) + { + if (!priv->rx_started) + { + i2swarn("RX channel of port %d was previously stopped\n", + priv->config->port); + return; + } + + /* Stop RX channel */ + + i2s_hal_rx_stop(priv->config->ctx); + + /* Stop outlink */ + + /* Disable DMA interrupt */ + +#ifdef CONFIG_ARCH_CHIP_ESP32S3 + esp32s3_dma_disable(priv->dma_channel, false); + esp32s3_dma_enable_interrupt(priv->dma_channel, false, + GDMA_LL_EVENT_RX_SUC_EOF, false); +#else + i2s_hal_rx_stop_link(priv->config->ctx); + i2s_hal_rx_disable_intr(priv->config->ctx); + i2s_hal_rx_disable_dma(priv->config->ctx); +#endif + + /* Disable DMA operation mode */ + + up_disable_irq(priv->rx_irq); + + priv->rx_started = false; + + i2sinfo("Stopped RX channel of port %d\n", priv->config->port); + } +} +#endif /* I2S_HAVE_RX */ + +/**************************************************************************** + * Name: i2s_interrupt + * + * Description: + * Common I2S interrupt handler + * + * Input Parameters: + * irq - Number of the IRQ that generated the interrupt + * context - Interrupt register state save info + * arg - I2S controller private data + * + * Returned Value: + * Standard interrupt return value. + * + ****************************************************************************/ + +static int i2s_interrupt(int irq, void *context, void *arg) +{ + struct esp_i2s_s *priv = (struct esp_i2s_s *)arg; + uint32_t status = 0; + +#ifdef CONFIG_ARCH_CHIP_ESP32S3 + struct esp_dmadesc_s *cur = NULL; +# ifdef I2S_HAVE_RX + status = esp32s3_dma_get_interrupt(priv->dma_channel, false); + esp32s3_dma_clear_interrupt(priv->dma_channel, false, status); + if (priv->config->rx_en) + { + if (status & GDMA_LL_EVENT_RX_SUC_EOF) + { + cur = (struct esp_dmadesc_s *) + esp32s3_dma_get_desc_addr(priv->dma_channel, true); + + /* Schedule completion of the transfer on the worker thread */ + + i2s_rx_schedule(priv, cur); + } + } +# endif + +# ifdef I2S_HAVE_TX + status = esp32s3_dma_get_interrupt(priv->dma_channel, true); + esp32s3_dma_clear_interrupt(priv->dma_channel, true, status); + if (priv->config->tx_en) + { + if (status & GDMA_LL_EVENT_TX_TOTAL_EOF) + { + cur = (struct esp_dmadesc_s *) + esp32s3_dma_get_desc_addr(priv->dma_channel, true); + + /* Schedule completion of the transfer on the worker thread */ + + i2s_tx_schedule(priv, cur); + } + } +# endif +#else + struct esp_dmadesc_s cur; + + status = i2s_hal_get_intr_status(priv->config->ctx); + i2s_hal_clear_intr_status(priv->config->ctx, UINT32_MAX); + +# ifdef I2S_HAVE_RX + if (priv->config->rx_en) + { + if (status & I2S_LL_EVENT_RX_EOF) + { + i2s_hal_get_out_eof_des_addr(priv->config->ctx, (uint32_t *) &cur); + + /* Schedule completion of the transfer on the worker thread */ + + i2s_rx_schedule(priv, &cur); + } + } +# endif + +# ifdef I2S_HAVE_TX + if (priv->config->tx_en) + { + if (status & I2S_LL_EVENT_TX_EOF) + { + i2s_hal_get_out_eof_des_addr(priv->config->ctx, (uint32_t *) &cur); + + /* Schedule completion of the transfer on the worker thread */ + + i2s_tx_schedule(priv, &cur); + } + } +# endif +#endif + + return 0; +} + +/**************************************************************************** + * Name: i2s_getmclkfrequency + * + * Description: + * Get the current master clock frequency. + * + * Input Parameters: + * dev - Device-specific state data + * + * Returned Value: + * Returns the current master clock. + * + ****************************************************************************/ + +static uint32_t i2s_getmclkfrequency(struct i2s_dev_s *dev) +{ + struct esp_i2s_s *priv = (struct esp_i2s_s *)dev; + + return priv->mclk_freq; +} + +/**************************************************************************** + * Name: i2s_setmclkfrequency + * + * Description: + * Set the master clock frequency. Usually, the MCLK is a multiple of the + * sample rate. Most of the audio codecs require setting specific MCLK + * frequency according to the sample rate. + * + * Input Parameters: + * dev - Device-specific state data + * frequency - The I2S master clock's frequency + * + * Returned Value: + * Returns the resulting master clock or a negated errno value on failure. + * + ****************************************************************************/ + +static uint32_t i2s_setmclkfrequency(struct i2s_dev_s *dev, + uint32_t frequency) +{ + struct esp_i2s_s *priv = (struct esp_i2s_s *)dev; + + /* Check if the master clock frequency is beyond the highest possible + * value and return an error. + */ + + if (frequency >= (priv->config->clk_hz / 2)) + { + return -EINVAL; + } + + priv->mclk_freq = frequency; + + return frequency; +} + +/**************************************************************************** + * Name: i2s_txchannels + * + * Description: + * Set the I2S TX number of channels. + * + * Input Parameters: + * dev - Device-specific state data + * channels - The I2S numbers of channels + * + * Returned Value: + * OK on success; a negated errno value on failure. + * + ****************************************************************************/ + +#ifdef I2S_HAVE_TX +static int i2s_txchannels(struct i2s_dev_s *dev, uint8_t channels) +{ + struct esp_i2s_s *priv = (struct esp_i2s_s *)dev; + bool is_mono = true; + + if (priv->config->tx_en) + { + if (channels != 1 && channels != 2) + { + return -EINVAL; + } + + i2s_tx_channel_stop(priv); + + priv->channels = channels; + + if (priv->channels > 1) + { + is_mono = false; + } + + i2s_ll_tx_enable_mono_mode(priv->config->ctx->dev, + is_mono); + + /* Set I2S_TX_UPDATE bit to update the configs. + * This bit is automatically cleared. + */ + + i2s_tx_channel_start(priv); + + return OK; + } + + return -ENOTTY; +} +#endif /* I2S_HAVE_TX */ + +/**************************************************************************** + * Name: i2s_rxchannels + * + * Description: + * Set the I2S RX number of channels. + * + * Input Parameters: + * dev - Device-specific state data + * channels - The I2S numbers of channels + * + * Returned Value: + * OK on success; a negated errno value on failure. + * + ****************************************************************************/ + +#ifdef I2S_HAVE_RX +static int i2s_rxchannels(struct i2s_dev_s *dev, uint8_t channels) +{ + struct esp_i2s_s *priv = (struct esp_i2s_s *)dev; + bool is_mono = true; + + if (priv->config->rx_en) + { + if (channels != 1 && channels != 2) + { + return -EINVAL; + } + + i2s_rx_channel_stop(priv); + + priv->channels = channels; + + if (priv->channels > 1) + { + is_mono = false; + } + + i2s_ll_tx_enable_mono_mode(priv->config->ctx->dev, + is_mono); + + i2s_rx_channel_start(priv); + + return OK; + } + + return -ENOTTY; +} +#endif /* I2S_HAVE_RX */ + +/**************************************************************************** + * Name: i2s_txsamplerate + * + * Description: + * Set the I2S TX sample rate. NOTE: This will have no effect if (1) the + * driver does not support an I2S transmitter or if (2) the sample rate is + * driven by the I2S frame clock. This may also have unexpected side- + * effects of the TX sample is coupled with the RX sample rate. + * + * Input Parameters: + * dev - Device-specific state data + * rate - The I2S sample rate in samples (not bits) per second + * + * Returned Value: + * OK on success, ERROR on fail + * + ****************************************************************************/ + +#ifdef I2S_HAVE_TX +static uint32_t i2s_txsamplerate(struct i2s_dev_s *dev, uint32_t rate) +{ + struct esp_i2s_s *priv = (struct esp_i2s_s *)dev; + + if (priv->config->tx_en) + { + i2s_tx_channel_stop(priv); + + priv->rate = rate; + + if (i2s_check_mclkfrequency(priv) < OK) + { + return ERROR; + } + + i2s_set_clock(priv); + + i2s_tx_channel_start(priv); + + return OK; + } + + return ERROR; +} +#endif /* I2S_HAVE_TX */ + +/**************************************************************************** + * Name: i2s_rxsamplerate + * + * Description: + * Set the I2S RX sample rate. + * + * Input Parameters: + * dev - Device-specific state data + * rate - The I2S sample rate in samples (not bits) per second + * + * Returned Value: + * OK on success, ERROR on fail + * + ****************************************************************************/ + +#ifdef I2S_HAVE_RX +static uint32_t i2s_rxsamplerate(struct i2s_dev_s *dev, uint32_t rate) +{ + struct esp_i2s_s *priv = (struct esp_i2s_s *)dev; + + if (priv->config->rx_en) + { + i2s_rx_channel_stop(priv); + + priv->rate = rate; + + if (i2s_check_mclkfrequency(priv) < OK) + { + return ERROR; + } + + i2s_set_clock(priv); + + i2s_rx_channel_start(priv); + + return OK; + } + + return ERROR; +} +#endif /* I2S_HAVE_RX */ + +/**************************************************************************** + * Name: i2s_txdatawidth + * + * Description: + * Set the I2S TX data width. The TX bitrate is determined by + * sample_rate * data_width. + * + * Input Parameters: + * dev - Device-specific state data + * width - The I2S data with in bits. + * + * Returned Value: + * Returns the resulting data width + * + ****************************************************************************/ + +#ifdef I2S_HAVE_TX +static uint32_t i2s_txdatawidth(struct i2s_dev_s *dev, int bits) +{ + struct esp_i2s_s *priv = (struct esp_i2s_s *)dev; + + if (priv->config->tx_en) + { + i2s_tx_channel_stop(priv); + + priv->data_width = bits; + + i2s_set_datawidth(priv); + + i2s_tx_channel_start(priv); + + return bits; + } + + return 0; +} +#endif /* I2S_HAVE_TX */ + +/**************************************************************************** + * Name: i2s_rxdatawidth + * + * Description: + * Set the I2S RX data width. The RX bitrate is determined by + * sample_rate * data_width. + * + * Input Parameters: + * dev - Device-specific state data + * width - The I2S data with in bits. + * + * Returned Value: + * Returns the resulting data width + * + ****************************************************************************/ + +#ifdef I2S_HAVE_RX +static uint32_t i2s_rxdatawidth(struct i2s_dev_s *dev, int bits) +{ + struct esp_i2s_s *priv = (struct esp_i2s_s *)dev; + + if (priv->config->rx_en) + { + i2s_rx_channel_stop(priv); + + priv->data_width = bits; + + i2s_set_datawidth(priv); + + i2s_rx_channel_start(priv); + + return bits; + } + + return 0; +} +#endif /* I2S_HAVE_RX */ + +/**************************************************************************** + * Name: i2s_send + * + * Description: + * Send a block of data on I2S. + * + * Input Parameters: + * dev - Device-specific state data + * apb - A pointer to the audio buffer from which to send data + * callback - A user provided callback function that will be called at + * the completion of the transfer. + * arg - An opaque argument that will be provided to the callback + * when the transfer complete + * timeout - The timeout value to use. The transfer will be cancelled + * and an ETIMEDOUT error will be reported if this timeout + * elapsed without completion of the DMA transfer. Units + * are system clock ticks. Zero means no timeout. + * + * Returned Value: + * OK on success; a negated errno value on failure. + * + ****************************************************************************/ + +#ifdef I2S_HAVE_TX +static int i2s_send(struct i2s_dev_s *dev, struct ap_buffer_s *apb, + i2s_callback_t callback, void *arg, uint32_t timeout) +{ + struct esp_i2s_s *priv = (struct esp_i2s_s *)dev; + + if (priv->config->tx_en) + { + struct esp_buffer_s *bfcontainer; + int ret = OK; + uint32_t nbytes; + uint32_t nsamp; + + /* Check audio buffer data size from the upper half. If the buffer + * size is not a multiple of the data width, the remaining bytes + * must be sent along with the next audio buffer. + */ + + nbytes = (apb->nbytes - apb->curbyte) + priv->tx.carry.bytes; + + nbytes -= (nbytes % (priv->data_width / 8)); + + if (nbytes > (ESPRESSIF_DMA_BUFLEN_MAX * I2S_DMADESC_NUM)) + { + i2serr("Required buffer size can't fit into DMA outlink " + "(exceeds in %" PRIu32 " bytes). Try to increase the " + "number of the DMA descriptors (CONFIG_I2S_DMADESC_NUM).", + nbytes - (ESPRESSIF_DMA_BUFLEN_MAX * I2S_DMADESC_NUM)); + return -EFBIG; + } + + /* Allocate a buffer container in advance */ + + bfcontainer = i2s_buf_allocate(priv); + if (bfcontainer == NULL) + { + i2serr("Failed to allocate the buffer container"); + return -ENOMEM; + } + + /* Get exclusive access to the I2S driver data */ + + ret = nxmutex_lock(&priv->lock); + if (ret < 0) + { + goto errout_with_buf; + } + + /* Add a reference to the audio buffer */ + + apb_reference(apb); + + /* Initialize the buffer container structure */ + + bfcontainer->callback = callback; + bfcontainer->timeout = timeout; + bfcontainer->arg = arg; + bfcontainer->apb = apb; + bfcontainer->nbytes = nbytes; + bfcontainer->result = -EBUSY; + + ret = i2s_txdma_setup(priv, bfcontainer); + + if (ret != OK) + { + goto errout_with_buf; + } + + i2sinfo("Queued %d bytes into DMA buffers\n", apb->nbytes); + i2s_dump_buffer("Audio pipeline buffer:", &apb->samp[apb->curbyte], + apb->nbytes - apb->curbyte); + + nxmutex_unlock(&priv->lock); + + return OK; + +errout_with_buf: + nxmutex_unlock(&priv->lock); + i2s_buf_free(priv, bfcontainer); + return ret; + } + + return -ENOTTY; +} +#endif /* I2S_HAVE_TX */ + +/**************************************************************************** + * Name: i2s_receive + * + * Description: + * Receive a block of data on I2S. + * + * Input Parameters: + * dev - Device-specific state data + * apb - A pointer to the audio buffer in which to receive data + * callback - A user provided callback function that will be called at + * the completion of the transfer. + * arg - An opaque argument that will be provided to the callback + * when the transfer complete + * timeout - The timeout value to use. The transfer will be cancelled + * and an ETIMEDOUT error will be reported if this timeout + * elapsed without completion of the DMA transfer. Units + * are system clock ticks. Zero means no timeout. + * + * Returned Value: + * OK on success; a negated errno value on failure. + * + ****************************************************************************/ + +#ifdef I2S_HAVE_RX +static int i2s_receive(struct i2s_dev_s *dev, struct ap_buffer_s *apb, + i2s_callback_t callback, void *arg, uint32_t timeout) +{ + struct esp_i2s_s *priv = (struct esp_i2s_s *)dev; + + if (priv->config->rx_en) + { + struct esp_buffer_s *bfcontainer; + int ret = OK; + uint32_t nbytes; + uint32_t nsamp; + + /* Check max audio buffer data size from the upper half and align the + * receiving buffer according to the data width. + */ + + nbytes = apb->nmaxbytes; + + nbytes -= (nbytes % (priv->data_width / 8)); + + nbytes = MIN(nbytes, ESPRESSIF_DMA_BUFLEN_MAX); + + /* Allocate a buffer container in advance */ + + bfcontainer = i2s_buf_allocate(priv); + if (bfcontainer == NULL) + { + i2serr("Failed to allocate the buffer container"); + return -ENOMEM; + } + + /* Get exclusive access to the I2S driver data */ + + ret = nxmutex_lock(&priv->lock); + if (ret < 0) + { + goto errout_with_buf; + } + + /* Add a reference to the audio buffer */ + + apb_reference(apb); + + /* Initialize the buffer container structure */ + + bfcontainer->callback = callback; + bfcontainer->timeout = timeout; + bfcontainer->arg = arg; + bfcontainer->apb = apb; + bfcontainer->nbytes = nbytes; + bfcontainer->result = -EBUSY; + + ret = i2s_rxdma_setup(priv, bfcontainer); + + if (ret != OK) + { + goto errout_with_buf; + } + + i2sinfo("Prepared %d bytes to receive DMA buffers\n", apb->nmaxbytes); + i2s_dump_buffer("Recieved Audio pipeline buffer:", + &apb->samp[apb->curbyte], + apb->nbytes - apb->curbyte); + + nxmutex_unlock(&priv->lock); + + return OK; + +errout_with_buf: + nxmutex_unlock(&priv->lock); + i2s_buf_free(priv, bfcontainer); + return ret; + } + + return -ENOTTY; +} +#endif /* I2S_HAVE_RX */ + +/**************************************************************************** + * Name: i2s_ioctl + * + * Description: + * Implement the lower-half logic ioctl commands + * + * Input parameters: + * dev - A reference to the lower-half I2S driver device + * cmd - The ioctl command + * arg - The argument accompanying the ioctl command + * + * Returned Value: + * OK on success; A negated errno value on failure. + * + ****************************************************************************/ + +static int i2s_ioctl(struct i2s_dev_s *dev, int cmd, unsigned long arg) +{ + struct esp_i2s_s *priv = (struct esp_i2s_s *)dev; + struct audio_buf_desc_s *bufdesc; + int ret = -ENOTTY; + + switch (cmd) + { + /* AUDIOIOC_START - Start the audio stream. + * + * ioctl argument: Audio session + */ + + case AUDIOIOC_START: + { + i2sinfo("AUDIOIOC_START\n"); + + priv->streaming = true; + + ret = OK; + } + break; + + /* AUDIOIOC_STOP - Stop the audio stream. + * + * ioctl argument: Audio session + */ + +#ifndef CONFIG_AUDIO_EXCLUDE_STOP + case AUDIOIOC_STOP: + { + i2sinfo("AUDIOIOC_STOP\n"); + + priv->streaming = false; + + ret = OK; + } + break; +#endif /* CONFIG_AUDIO_EXCLUDE_STOP */ + + /* AUDIOIOC_ALLOCBUFFER - Allocate an audio buffer + * + * ioctl argument: pointer to an audio_buf_desc_s structure + */ + + case AUDIOIOC_ALLOCBUFFER: + { + i2sinfo("AUDIOIOC_ALLOCBUFFER\n"); + + bufdesc = (struct audio_buf_desc_s *) arg; + ret = apb_alloc(bufdesc); + } + break; + + /* AUDIOIOC_FREEBUFFER - Free an audio buffer + * + * ioctl argument: pointer to an audio_buf_desc_s structure + */ + + case AUDIOIOC_FREEBUFFER: + { + i2sinfo("AUDIOIOC_FREEBUFFER\n"); + + bufdesc = (struct audio_buf_desc_s *) arg; + DEBUGASSERT(bufdesc->u.buffer != NULL); + apb_free(bufdesc->u.buffer); + ret = sizeof(struct audio_buf_desc_s); + } + break; + + default: + break; + } + + return ret; +} + +/**************************************************************************** + * Name: i2s_dma_setup + * + * Description: + * Configure the DMA for the I2S peripheral + * + * Input Parameters: + * priv - Partially initialized I2S device structure. This function + * will complete the I2S specific portions of the initialization + * regarding the DMA operation. + * + * Returned Value: + * OK on success; A negated errno value on failure. + * + ****************************************************************************/ + +static int i2s_dma_setup(struct esp_i2s_s *priv) +{ + int ret; + int periph; +#ifdef CONFIG_ARCH_CHIP_ESP32S3 + int i2s_dma_dev; + + i2s_dma_dev = ESPRESSIF_DMA_PERIPH_I2S; + + /* Request a GDMA channel for the I2S peripheral */ + + esp_dma_init(); + priv->dma_channel = esp_dma_request(i2s_dma_dev, 1, 1, false); + if (priv->dma_channel < 0) + { + i2serr("Failed to allocate GDMA channel\n"); + return ERROR; + } + + /* Set up to receive GDMA interrupts on the current CPU. Each TX/RX channel + * will be assigned to a different CPU interrupt. + */ + + priv->cpu = this_cpu(); + +# ifdef I2S_HAVE_TX + if (priv->config->tx_en) + { + periph = + gdma_periph_signals.groups[0].pairs[priv->dma_channel].tx_irq_id; + int cpuint = esp_setup_irq(priv->cpu, + periph, 1, + ESP_IRQ_TRIGGER_LEVEL); + if (cpuint < 0) + { + i2serr("Failed to allocate a CPU interrupt.\n"); + return ERROR; + } + + priv->tx_irq = ESP_SOURCE2IRQ(periph); + ret = irq_attach(priv->tx_irq, i2s_interrupt, priv); + if (ret != OK) + { + i2serr("Couldn't attach IRQ to handler.\n"); + esp_teardown_irq(priv->cpu, + periph, + cpuint); + return ret; + } + } +# endif /* I2S_HAVE_TX */ + +# ifdef I2S_HAVE_RX + if (priv->config->rx_en) + { + periph = + gdma_periph_signals.groups[0].pairs[priv->dma_channel].rx_irq_id; + int cpuint = esp_setup_irq(priv->cpu, + periph, 1, + ESP_IRQ_TRIGGER_LEVEL); + if (cpuint < 0) + { + i2serr("Failed to allocate a CPU interrupt.\n"); + return ERROR; + } + + priv->rx_irq = ESP_SOURCE2IRQ(periph); + ret = irq_attach(priv->rx_irq, i2s_interrupt, priv); + if (ret != OK) + { + i2serr("Couldn't attach IRQ to handler.\n"); + esp_teardown_irq(priv->cpu, + periph, + cpuint); + return ret; + } + } +# endif /* I2S_HAVE_RX */ +#else + + /* Clear the interrupts */ + + i2s_hal_clear_intr_status(priv->config->ctx, UINT32_MAX); + + /* Set up to receive peripheral interrupts on the current CPU */ + + priv->cpu = this_cpu(); + periph = i2s_periph_signal[priv->config->port].irq; + + int cpuint = esp_setup_irq( +# ifndef CONFIG_ARCH_CHIP_ESP32S2 + priv->cpu, +# endif + periph, 1, + ESP_IRQ_TRIGGER_LEVEL); + if (cpuint < 0) + { + i2serr("Failed to allocate a CPU interrupt.\n"); + return ERROR; + } + + int irq = ESP_SOURCE2IRQ(periph); + ret = irq_attach(irq, i2s_interrupt, priv); + if (ret != OK) + { + i2serr("Couldn't attach IRQ to handler.\n"); + esp_teardown_irq( +#ifndef CONFIG_ARCH_CHIP_ESP32S2 + priv->cpu, +#endif + periph, + cpuint); + return ret; + } +#endif + + return OK; +} + +/**************************************************************************** + * Name: esp_i2sbus_initialize + * + * Description: + * Initialize the selected I2S port + * + * Input Parameters: + * port - Port number (for hardware that has multiple I2S interfaces) + * + * Returned Value: + * Valid I2S device structure reference on success; a NULL on failure + * + ****************************************************************************/ + +struct i2s_dev_s *esp_i2sbus_initialize(int port) +{ + int ret; + struct esp_i2s_s *priv = NULL; + irqstate_t flags; + + i2sinfo("port: %d\n", port); + + /* Statically allocated I2S' device strucuture */ + + switch (port) + { +#ifdef CONFIG_ESPRESSIF_I2S0 + case ESPRESSIF_I2S0: + priv = &esp_i2s0_priv; + break; +#endif +#ifdef CONFIG_ESPRESSIF_I2S1 + case ESPRESSIF_I2S1: + priv = &esp_i2s1_priv; + break; +#endif + default: + return NULL; + } + + /* Allocate buffer containers */ + + ret = i2s_buf_initialize(priv); + if (ret < 0) + { + goto err; + } + + flags = spin_lock_irqsave(&priv->slock); + + i2s_configure(priv); + + ret = i2s_dma_setup(priv); + if (ret < 0) + { + goto err; + } + +#ifdef I2S_HAVE_TX + /* Start TX channel */ + + if (priv->config->tx_en) + { + priv->tx_started = false; + i2s_tx_channel_start(priv); + } +#endif /* I2S_HAVE_TX */ + +#ifdef I2S_HAVE_RX + /* Start RX channel */ + + if (priv->config->rx_en) + { + priv->rx_started = false; + i2s_rx_channel_start(priv); + } +#endif /* I2S_HAVE_RX */ + + spin_unlock_irqrestore(&priv->slock, flags); + + /* Success exit */ + + i2sinfo("I2S%d was successfully initialized\n", priv->config->port); + + return &priv->dev; + + /* Failure exit */ + +err: + spin_unlock_irqrestore(&priv->slock, flags); + return NULL; +} diff --git a/arch/xtensa/src/common/espressif/esp_i2s.h b/arch/xtensa/src/common/espressif/esp_i2s.h new file mode 100644 index 0000000000..042ee82f43 --- /dev/null +++ b/arch/xtensa/src/common/espressif/esp_i2s.h @@ -0,0 +1,87 @@ +/**************************************************************************** + * arch/xtensa/src/common/espressif/esp_i2s.h + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +#ifndef __ARCH_XTENSA_SRC_COMMON_ESPRESSIF_ESP_I2S_H +#define __ARCH_XTENSA_SRC_COMMON_ESPRESSIF_ESP_I2S_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include <nuttx/config.h> +#include <nuttx/audio/i2s.h> + +#ifndef __ASSEMBLY__ + +#undef EXTERN +#if defined(__cplusplus) +#define EXTERN extern "C" +extern "C" +{ +#else +#define EXTERN extern +#endif + +#ifdef CONFIG_ESPRESSIF_I2S + +#define ESPRESSIF_I2S0 0 +#define ESPRESSIF_I2S1 1 + +#if defined(CONFIG_ARCH_CHIP_ESP32S3) +#define ESP32S3_I2S0 0 +#define ESP32S3_I2S1 1 +#elif defined(CONFIG_ARCH_CHIP_ESP32S2) +#define ESP32S2_I2S0 0 +#elif defined(CONFIG_ARCH_CHIP_ESP32) +#define ESP32_I2S0 0 +#define ESP32_I2S1 1 +#endif + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +/**************************************************************************** + * Name: esp_i2sbus_initialize + * + * Description: + * Initialize the selected I2S port + * + * Input Parameters: + * port - Port number (for hardware that has multiple I2S interfaces) + * + * Returned Value: + * Valid I2S device structure reference on success; a NULL on failure + * + ****************************************************************************/ + +struct i2s_dev_s *esp_i2sbus_initialize(int port); + +#endif /* CONFIG_ESPRESSIF_I2S */ + +#undef EXTERN +#ifdef __cplusplus +} +#endif + +#endif /* __ASSEMBLY__ */ +#endif /* __ARCH_XTENSA_SRC_COMMON_ESPRESSIF_ESP_I2S_H */ diff --git a/arch/xtensa/src/esp32/Kconfig b/arch/xtensa/src/esp32/Kconfig index 246bfc6278..7d793d96c3 100644 --- a/arch/xtensa/src/esp32/Kconfig +++ b/arch/xtensa/src/esp32/Kconfig @@ -311,7 +311,7 @@ config ESP32_SDMMC No yet implemented config ESP32_I2S - bool "I2S" + bool "I2S (legacy implementation)" select I2S ---help--- See the Board Selection menu to configure the pins used by I2S. diff --git a/arch/xtensa/src/esp32/hal.mk b/arch/xtensa/src/esp32/hal.mk index ef96969c02..11cfbfe943 100644 --- a/arch/xtensa/src/esp32/hal.mk +++ b/arch/xtensa/src/esp32/hal.mk @@ -110,6 +110,7 @@ CHIP_CSRCS += chip$(DELIM)$(ESP_HAL_3RDPARTY_REPO)$(DELIM)components$(DELIM)hal$ CHIP_CSRCS += chip$(DELIM)$(ESP_HAL_3RDPARTY_REPO)$(DELIM)components$(DELIM)hal$(DELIM)ledc_hal.c CHIP_CSRCS += chip$(DELIM)$(ESP_HAL_3RDPARTY_REPO)$(DELIM)components$(DELIM)hal$(DELIM)pcnt_hal.c CHIP_CSRCS += chip$(DELIM)$(ESP_HAL_3RDPARTY_REPO)$(DELIM)components$(DELIM)hal$(DELIM)rmt_hal.c +CHIP_CSRCS += chip$(DELIM)$(ESP_HAL_3RDPARTY_REPO)$(DELIM)components$(DELIM)hal$(DELIM)i2s_hal.c CHIP_CSRCS += chip$(DELIM)$(ESP_HAL_3RDPARTY_REPO)$(DELIM)components$(DELIM)hal$(DELIM)mcpwm_hal.c CHIP_CSRCS += chip$(DELIM)$(ESP_HAL_3RDPARTY_REPO)$(DELIM)components$(DELIM)hal$(DELIM)timer_hal.c CHIP_CSRCS += chip$(DELIM)$(ESP_HAL_3RDPARTY_REPO)$(DELIM)components$(DELIM)hal$(DELIM)uart_hal_iram.c @@ -123,6 +124,7 @@ CHIP_CSRCS += chip$(DELIM)$(ESP_HAL_3RDPARTY_REPO)$(DELIM)components$(DELIM)soc$ CHIP_CSRCS += chip$(DELIM)$(ESP_HAL_3RDPARTY_REPO)$(DELIM)components$(DELIM)soc$(DELIM)$(CHIP_SERIES)$(DELIM)pcnt_periph.c CHIP_CSRCS += chip$(DELIM)$(ESP_HAL_3RDPARTY_REPO)$(DELIM)components$(DELIM)soc$(DELIM)$(CHIP_SERIES)$(DELIM)rmt_periph.c CHIP_CSRCS += chip$(DELIM)$(ESP_HAL_3RDPARTY_REPO)$(DELIM)components$(DELIM)soc$(DELIM)$(CHIP_SERIES)$(DELIM)i2c_periph.c +CHIP_CSRCS += chip$(DELIM)$(ESP_HAL_3RDPARTY_REPO)$(DELIM)components$(DELIM)soc$(DELIM)$(CHIP_SERIES)$(DELIM)i2s_periph.c CHIP_CSRCS += chip$(DELIM)$(ESP_HAL_3RDPARTY_REPO)$(DELIM)components$(DELIM)soc$(DELIM)$(CHIP_SERIES)$(DELIM)mcpwm_periph.c ifeq ($(CONFIG_ESPRESSIF_SIMPLE_BOOT),y) diff --git a/arch/xtensa/src/esp32s2/Kconfig b/arch/xtensa/src/esp32s2/Kconfig index 86ce1c3537..592e464b50 100644 --- a/arch/xtensa/src/esp32s2/Kconfig +++ b/arch/xtensa/src/esp32s2/Kconfig @@ -229,7 +229,7 @@ config ESP32S2_RNG ESP32-S2 supports a RNG that passed on Dieharder test suite. config ESP32S2_I2S - bool "I2S" + bool "I2S (legacy implementation)" default n select I2S select ARCH_DMA diff --git a/arch/xtensa/src/esp32s2/hal.mk b/arch/xtensa/src/esp32s2/hal.mk index d04648c8dd..4d0f4524e1 100644 --- a/arch/xtensa/src/esp32s2/hal.mk +++ b/arch/xtensa/src/esp32s2/hal.mk @@ -108,6 +108,7 @@ CHIP_CSRCS += chip$(DELIM)$(ESP_HAL_3RDPARTY_REPO)$(DELIM)components$(DELIM)hal$ CHIP_CSRCS += chip$(DELIM)$(ESP_HAL_3RDPARTY_REPO)$(DELIM)components$(DELIM)hal$(DELIM)ledc_hal.c CHIP_CSRCS += chip$(DELIM)$(ESP_HAL_3RDPARTY_REPO)$(DELIM)components$(DELIM)hal$(DELIM)pcnt_hal.c CHIP_CSRCS += chip$(DELIM)$(ESP_HAL_3RDPARTY_REPO)$(DELIM)components$(DELIM)hal$(DELIM)rmt_hal.c +CHIP_CSRCS += chip$(DELIM)$(ESP_HAL_3RDPARTY_REPO)$(DELIM)components$(DELIM)hal$(DELIM)i2s_hal.c CHIP_CSRCS += chip$(DELIM)$(ESP_HAL_3RDPARTY_REPO)$(DELIM)components$(DELIM)hal$(DELIM)timer_hal.c CHIP_CSRCS += chip$(DELIM)$(ESP_HAL_3RDPARTY_REPO)$(DELIM)components$(DELIM)hal$(DELIM)uart_hal_iram.c CHIP_CSRCS += chip$(DELIM)$(ESP_HAL_3RDPARTY_REPO)$(DELIM)components$(DELIM)hal$(DELIM)uart_hal.c @@ -118,6 +119,7 @@ CHIP_CSRCS += chip$(DELIM)$(ESP_HAL_3RDPARTY_REPO)$(DELIM)components$(DELIM)soc$ CHIP_CSRCS += chip$(DELIM)$(ESP_HAL_3RDPARTY_REPO)$(DELIM)components$(DELIM)soc$(DELIM)$(CHIP_SERIES)$(DELIM)pcnt_periph.c CHIP_CSRCS += chip$(DELIM)$(ESP_HAL_3RDPARTY_REPO)$(DELIM)components$(DELIM)soc$(DELIM)$(CHIP_SERIES)$(DELIM)rmt_periph.c CHIP_CSRCS += chip$(DELIM)$(ESP_HAL_3RDPARTY_REPO)$(DELIM)components$(DELIM)soc$(DELIM)$(CHIP_SERIES)$(DELIM)i2c_periph.c +CHIP_CSRCS += chip$(DELIM)$(ESP_HAL_3RDPARTY_REPO)$(DELIM)components$(DELIM)soc$(DELIM)$(CHIP_SERIES)$(DELIM)i2s_periph.c CHIP_CSRCS += chip$(DELIM)$(ESP_HAL_3RDPARTY_REPO)$(DELIM)components$(DELIM)soc$(DELIM)$(CHIP_SERIES)$(DELIM)temperature_sensor_periph.c CHIP_CSRCS += chip$(DELIM)$(ESP_HAL_3RDPARTY_REPO)$(DELIM)components$(DELIM)spi_flash$(DELIM)flash_ops.c diff --git a/arch/xtensa/src/esp32s3/Kconfig b/arch/xtensa/src/esp32s3/Kconfig index 3e51869676..10b490d741 100644 --- a/arch/xtensa/src/esp32s3/Kconfig +++ b/arch/xtensa/src/esp32s3/Kconfig @@ -365,7 +365,7 @@ config ESP32S3_I2C default n config ESP32S3_I2S - bool "I2S" + bool "I2S (legacy implementation)" select I2S ---help--- See the Board Selection menu to configure the pins used by I2S. diff --git a/arch/xtensa/src/esp32s3/hal.mk b/arch/xtensa/src/esp32s3/hal.mk index 8bd319f4c9..9d7642a989 100644 --- a/arch/xtensa/src/esp32s3/hal.mk +++ b/arch/xtensa/src/esp32s3/hal.mk @@ -116,6 +116,7 @@ CHIP_CSRCS += chip$(DELIM)$(ESP_HAL_3RDPARTY_REPO)$(DELIM)components$(DELIM)hal$ CHIP_CSRCS += chip$(DELIM)$(ESP_HAL_3RDPARTY_REPO)$(DELIM)components$(DELIM)hal$(DELIM)ledc_hal.c CHIP_CSRCS += chip$(DELIM)$(ESP_HAL_3RDPARTY_REPO)$(DELIM)components$(DELIM)hal$(DELIM)pcnt_hal.c CHIP_CSRCS += chip$(DELIM)$(ESP_HAL_3RDPARTY_REPO)$(DELIM)components$(DELIM)hal$(DELIM)rmt_hal.c +CHIP_CSRCS += chip$(DELIM)$(ESP_HAL_3RDPARTY_REPO)$(DELIM)components$(DELIM)hal$(DELIM)i2s_hal.c CHIP_CSRCS += chip$(DELIM)$(ESP_HAL_3RDPARTY_REPO)$(DELIM)components$(DELIM)hal$(DELIM)mcpwm_hal.c CHIP_CSRCS += chip$(DELIM)$(ESP_HAL_3RDPARTY_REPO)$(DELIM)components$(DELIM)hal$(DELIM)timer_hal.c CHIP_CSRCS += chip$(DELIM)$(ESP_HAL_3RDPARTY_REPO)$(DELIM)components$(DELIM)hal$(DELIM)cache_hal.c @@ -126,11 +127,13 @@ CHIP_CSRCS += chip$(DELIM)$(ESP_HAL_3RDPARTY_REPO)$(DELIM)components$(DELIM)hal$ CHIP_CSRCS += chip$(DELIM)$(ESP_HAL_3RDPARTY_REPO)$(DELIM)components$(DELIM)hal$(DELIM)uart_hal.c CHIP_CSRCS += chip$(DELIM)$(ESP_HAL_3RDPARTY_REPO)$(DELIM)components$(DELIM)log$(DELIM)log_noos.c CHIP_CSRCS += chip$(DELIM)$(ESP_HAL_3RDPARTY_REPO)$(DELIM)components$(DELIM)log$(DELIM)log.c +CHIP_CSRCS += chip$(DELIM)$(ESP_HAL_3RDPARTY_REPO)$(DELIM)components$(DELIM)soc$(DELIM)$(CHIP_SERIES)$(DELIM)gdma_periph.c CHIP_CSRCS += chip$(DELIM)$(ESP_HAL_3RDPARTY_REPO)$(DELIM)components$(DELIM)soc$(DELIM)$(CHIP_SERIES)$(DELIM)gpio_periph.c CHIP_CSRCS += chip$(DELIM)$(ESP_HAL_3RDPARTY_REPO)$(DELIM)components$(DELIM)soc$(DELIM)$(CHIP_SERIES)$(DELIM)ledc_periph.c CHIP_CSRCS += chip$(DELIM)$(ESP_HAL_3RDPARTY_REPO)$(DELIM)components$(DELIM)soc$(DELIM)$(CHIP_SERIES)$(DELIM)pcnt_periph.c CHIP_CSRCS += chip$(DELIM)$(ESP_HAL_3RDPARTY_REPO)$(DELIM)components$(DELIM)soc$(DELIM)$(CHIP_SERIES)$(DELIM)rmt_periph.c CHIP_CSRCS += chip$(DELIM)$(ESP_HAL_3RDPARTY_REPO)$(DELIM)components$(DELIM)soc$(DELIM)$(CHIP_SERIES)$(DELIM)i2c_periph.c +CHIP_CSRCS += chip$(DELIM)$(ESP_HAL_3RDPARTY_REPO)$(DELIM)components$(DELIM)soc$(DELIM)$(CHIP_SERIES)$(DELIM)i2s_periph.c CHIP_CSRCS += chip$(DELIM)$(ESP_HAL_3RDPARTY_REPO)$(DELIM)components$(DELIM)soc$(DELIM)$(CHIP_SERIES)$(DELIM)mcpwm_periph.c CHIP_CSRCS += chip$(DELIM)$(ESP_HAL_3RDPARTY_REPO)$(DELIM)components$(DELIM)soc$(DELIM)$(CHIP_SERIES)$(DELIM)temperature_sensor_periph.c CHIP_CSRCS += chip$(DELIM)$(ESP_HAL_3RDPARTY_REPO)$(DELIM)components$(DELIM)spi_flash$(DELIM)spi_flash_wrap.c