From bb6f32d610305d1ea1ad61071ddac52b089c6728 Mon Sep 17 00:00:00 2001 From: simonatoaca Date: Mon, 19 Feb 2024 09:58:10 +0200 Subject: [PATCH] esp32-sparrow-kit: Add I2S support for the board's microphone The board's microphone uses 24-bit i2s and this commit also fixes the segmentation fault caused by the audio buffer overflow. arch/xtensa/src/esp32/esp32_i2s.c: Fix bug regarding 24-bit audio and add AUDIOIOC_STOP to ioctl drivers/audio/audio_i2s.c: Report number of channels on AUDIOIOC_GETCAPS in boards/xtensa/esp32/esp32-sparrow-kit: /configs/nsh/defconfig: Add I2S configs /src/esp32-sparrow-kit.h: Add the signature of esp32_i2sdev_initialize() /src/esp32_bringup.c: Add call to esp32_i2sdev_initialize() Signed-off-by: simonatoaca --- arch/xtensa/src/esp32/esp32_i2s.c | 102 +++++++++++++++++- .../esp32-sparrow-kit/configs/nsh/defconfig | 16 +++ .../esp32-sparrow-kit/src/esp32-sparrow-kit.h | 22 ++++ .../esp32-sparrow-kit/src/esp32_bringup.c | 21 ++++ drivers/audio/audio_i2s.c | 3 + 5 files changed, 161 insertions(+), 3 deletions(-) diff --git a/arch/xtensa/src/esp32/esp32_i2s.c b/arch/xtensa/src/esp32/esp32_i2s.c index 856aafe260..fabf4a0c1a 100644 --- a/arch/xtensa/src/esp32/esp32_i2s.c +++ b/arch/xtensa/src/esp32/esp32_i2s.c @@ -361,6 +361,7 @@ static void i2s_rx_channel_stop(struct esp32_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 void i2s_cleanup_queues(struct esp32_i2s_s *priv); static int i2s_receive(struct i2s_dev_s *dev, struct ap_buffer_s *apb, i2s_callback_t callback, void *arg, uint32_t timeout); @@ -1304,6 +1305,7 @@ static void i2s_rx_worker(void *arg) apb_samp_t samp_size; uint32_t data_copied; + uint8_t carry_bytes; uint8_t padding; uint8_t *buf; uint8_t *samp; @@ -1322,23 +1324,48 @@ static void i2s_rx_worker(void *arg) samp = &bfcontainer->apb->samp[bfcontainer->apb->curbyte]; samp_size = (bfcontainer->apb->nbytes - bfcontainer->apb->curbyte); + data_copied = 0; + buf = bfcontainer->buf; + + /* Copy the remaining bytes from previous transfer and + * complete the sample so that the next memcpy is aligned + * with the sample size. + */ + + if (priv->rx.carry.bytes) + { + memcpy(samp, &priv->rx.carry.value, priv->rx.carry.bytes); + samp += priv->rx.carry.bytes; + data_copied += priv->rx.carry.bytes; + + memcpy(samp, buf, (bytes_per_sample - priv->rx.carry.bytes)); + buf += (bytes_per_sample - priv->rx.carry.bytes); + samp += (bytes_per_sample - priv->rx.carry.bytes); + data_copied += (bytes_per_sample - priv->rx.carry.bytes); + } + /* If there is no need to add padding bytes, the memcpy may be done at * once. Otherwise, the operation must add the padding bytes to each * sample in the internal buffer. */ - data_copied = 0; - buf = bfcontainer->buf + padding; + buf += padding; if (padding) { - while (data_copied < samp_size) + while (data_copied + bytes_per_sample <= samp_size) { memcpy(samp, buf, bytes_per_sample); buf += (bytes_per_sample + padding); samp += bytes_per_sample; data_copied += bytes_per_sample; } + + /* Store the carry bytes, if any */ + + carry_bytes = samp_size - data_copied; + memcpy(&priv->rx.carry.value, buf, carry_bytes); + priv->rx.carry.bytes = carry_bytes; } else { @@ -2815,6 +2842,56 @@ errout_with_buf: } #endif /* I2S_HAVE_RX */ +/**************************************************************************** + * Name: i2s_cleanup_queues + * + * Description: + * Wait for all buffers to be processed and free them after + * + ****************************************************************************/ + +#ifdef I2S_HAVE_RX +static void i2s_cleanup_queues(struct esp32_i2s_s *priv) +{ + irqstate_t flags; + struct esp32_buffer_s *bfcontainer; + + while (sq_peek(&priv->rx.done) != NULL) + { + flags = enter_critical_section(); + bfcontainer = (struct esp32_buffer_s *)sq_remfirst(&priv->rx.done); + leave_critical_section(flags); + bfcontainer->callback(&priv->dev, bfcontainer->apb, + bfcontainer->arg, OK); + apb_free(bfcontainer->apb); + i2s_buf_free(priv, bfcontainer); + } + + while (sq_peek(&priv->rx.act) != NULL) + { + flags = enter_critical_section(); + bfcontainer = (struct esp32_buffer_s *)sq_remfirst(&priv->rx.act); + leave_critical_section(flags); + bfcontainer->callback(&priv->dev, bfcontainer->apb, + bfcontainer->arg, OK); + apb_free(bfcontainer->apb); + i2s_buf_free(priv, bfcontainer); + } + + while (sq_peek(&priv->rx.pend) != NULL) + { + flags = enter_critical_section(); + bfcontainer = (struct esp32_buffer_s *)sq_remfirst(&priv->rx.pend); + leave_critical_section(flags); + bfcontainer->apb->flags |= AUDIO_APB_FINAL; + bfcontainer->callback(&priv->dev, bfcontainer->apb, + bfcontainer->arg, OK); + apb_free(bfcontainer->apb); + i2s_buf_free(priv, bfcontainer); + } +} +#endif /* I2S_HAVE_RX */ + /**************************************************************************** * Name: i2s_ioctl * @@ -2843,6 +2920,25 @@ static int i2s_ioctl(struct i2s_dev_s *dev, int cmd, unsigned long arg) } break; + /* AUDIOIOC_STOP - Stop the audio stream. + * + * ioctl argument: Audio session + */ + + case AUDIOIOC_STOP: + { + i2sinfo("AUDIOIOC_STOP\n"); + +#ifdef I2S_HAVE_RX + struct esp32_i2s_s *priv = (struct esp32_i2s_s *)dev; + + i2s_cleanup_queues(priv); +#endif /* I2S_HAVE_RX */ + + ret = OK; + } + break; + /* AUDIOIOC_ALLOCBUFFER - Allocate an audio buffer * * ioctl argument: pointer to an audio_buf_desc_s structure diff --git a/boards/xtensa/esp32/esp32-sparrow-kit/configs/nsh/defconfig b/boards/xtensa/esp32/esp32-sparrow-kit/configs/nsh/defconfig index 1cec9907f2..25b9017c95 100644 --- a/boards/xtensa/esp32/esp32-sparrow-kit/configs/nsh/defconfig +++ b/boards/xtensa/esp32/esp32-sparrow-kit/configs/nsh/defconfig @@ -22,12 +22,26 @@ CONFIG_ARCH_CHIP_ESP32=y CONFIG_ARCH_CHIP_ESP32WROVER=y CONFIG_ARCH_STACKDUMP=y CONFIG_ARCH_XTENSA=y +CONFIG_AUDIO=y +CONFIG_AUDIO_DMA=y +CONFIG_AUDIO_FORMAT_RAW=y +CONFIG_AUDIO_I2S=y CONFIG_BME680_ENABLE_IIR_FILTER=y CONFIG_BOARD_LOOPSPERMSEC=16717 CONFIG_BUILTIN=y +CONFIG_DMA=y +CONFIG_DMA_LINK=y +CONFIG_DRIVERS_AUDIO=y CONFIG_DRIVERS_VIDEO=y CONFIG_ESP32_I2C0=y CONFIG_ESP32_I2C0_SDAPIN=21 +CONFIG_ESP32_I2S0=y +CONFIG_ESP32_I2S0_BCLKPIN=25 +CONFIG_ESP32_I2S0_DATA_BIT_WIDTH_24BIT=y +CONFIG_ESP32_I2S0_DINPIN=26 +CONFIG_ESP32_I2S0_SAMPLE_RATE=8000 +CONFIG_ESP32_I2S0_WSPIN=27 +CONFIG_ESP32_I2S=y CONFIG_ESP32_LEDC=y CONFIG_ESP32_LEDC_CHANNEL0_PIN=14 CONFIG_ESP32_LEDC_CHANNEL1_PIN=13 @@ -47,6 +61,7 @@ CONFIG_FS_FAT=y CONFIG_FS_PROCFS=y CONFIG_HAVE_CXX=y CONFIG_HAVE_CXXINITIALIZE=y +CONFIG_I2S_DMADESC_NUM=4 CONFIG_IDLETHREAD_STACKSIZE=3072 CONFIG_INIT_ENTRYPOINT="nsh_main" CONFIG_INTELHEX_BINARY=y @@ -75,6 +90,7 @@ CONFIG_RGBLED_INVERT=y CONFIG_RGBLED_LIGHTNESS_CORRECTION=y CONFIG_RGBLED_PWM_FREQ=200 CONFIG_RR_INTERVAL=200 +CONFIG_SCHED_HPWORK=y CONFIG_SCHED_WAITPID=y CONFIG_SENSORS=y CONFIG_SENSORS_BME680=y diff --git a/boards/xtensa/esp32/esp32-sparrow-kit/src/esp32-sparrow-kit.h b/boards/xtensa/esp32/esp32-sparrow-kit/src/esp32-sparrow-kit.h index 787ccf9939..c4b87b22d9 100644 --- a/boards/xtensa/esp32/esp32-sparrow-kit/src/esp32-sparrow-kit.h +++ b/boards/xtensa/esp32/esp32-sparrow-kit/src/esp32-sparrow-kit.h @@ -106,6 +106,28 @@ int esp32_mmcsd_initialize(int minor); int esp32_spiflash_init(void); +/**************************************************************************** + * Name: board_i2sdev_initialize + * + * Description: + * This function is called by platform-specific, setup logic to configure + * and register the generic I2S audio driver. This function will register + * the driver as /dev/audio/pcm[x] where x is determined by the I2S port + * number. + * + * Input Parameters: + * port - The I2S port used for the device + * + * Returned Value: + * Zero is returned on success. Otherwise, a negated errno value is + * returned to indicate the nature of the failure. + * + ****************************************************************************/ + +#if defined CONFIG_ESP32_I2S0 || defined CONFIG_ESP32_I2S1 +int board_i2sdev_initialize(int port); +#endif + /**************************************************************************** * Name: esp32_gpio_init ****************************************************************************/ diff --git a/boards/xtensa/esp32/esp32-sparrow-kit/src/esp32_bringup.c b/boards/xtensa/esp32/esp32-sparrow-kit/src/esp32_bringup.c index 3665696ccb..4b06008116 100644 --- a/boards/xtensa/esp32/esp32-sparrow-kit/src/esp32_bringup.c +++ b/boards/xtensa/esp32/esp32-sparrow-kit/src/esp32_bringup.c @@ -80,6 +80,10 @@ # include "esp32_board_i2c.h" #endif +#ifdef CONFIG_ESP32_I2S +# include "esp32_i2s.h" +#endif + #ifdef CONFIG_SENSORS_BMP180 # include "esp32_bmp180.h" #endif @@ -343,6 +347,23 @@ int esp32_bringup(void) #endif +#ifdef CONFIG_ESP32_I2S + +#ifdef CONFIG_ESP32_I2S0 + + /* Configure I2S generic audio on I2S0 */ + + ret = board_i2sdev_initialize(ESP32_I2S0); + if (ret < 0) + { + syslog(LOG_ERR, "Failed to initialize I2S%d driver: %d\n", + CONFIG_ESP32_I2S0, ret); + } + +#endif /* CONFIG_ESP32_I2S0 */ + +#endif /* CONFIG_ESP32_I2S */ + #ifdef CONFIG_SENSORS_BMP180 /* Try to register BMP180 device in I2C0 */ diff --git a/drivers/audio/audio_i2s.c b/drivers/audio/audio_i2s.c index 536a8d599b..c427b6e5ed 100644 --- a/drivers/audio/audio_i2s.c +++ b/drivers/audio/audio_i2s.c @@ -185,6 +185,9 @@ static int audio_i2s_getcaps(FAR struct audio_lowerhalf_s *dev, int type, /* Report the Sample rates we support */ caps->ac_controls.hw[0] = AUDIO_SAMP_RATE_DEF_ALL; + + caps->ac_channels = 2; + break; }