This is an automated email from the ASF dual-hosted git repository.

acassis pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/incubator-nuttx.git

commit 3b5ab27893a4073b29d01627a82ed1ef8c7fdd99
Author: Tiago Medicci Serrano <tiago.medi...@espressif.com>
AuthorDate: Thu Nov 10 16:40:42 2022 -0300

    esp32/i2s: implement I2S receiver module
    
    - Add ioctl method to enable allocating the apb buffer.
    - Add RX methods to set data width, sample rate, channels and
    for receiving data from the I2S peripheral.
    - Update the i2schar defconfig to enable the I2S receiver.
    - Add nxlooper defconfig to enable testing the RX interface.
    - Add specific bindings on ESP32 bringup to enable nxlooper
    to work without the need of any specific codec.
---
 arch/xtensa/src/esp32/Kconfig                      |   17 +-
 arch/xtensa/src/esp32/esp32_i2s.c                  | 1603 +++++++++++++++++---
 arch/xtensa/src/esp32/esp32_i2s.h                  |    9 +-
 .../xtensa/esp32/common/src/esp32_board_i2sdev.c   |   89 +-
 .../esp32/esp32-devkitc/configs/i2schar/defconfig  |    8 +-
 .../esp32/esp32-devkitc/configs/nxlooper/defconfig |  129 ++
 .../xtensa/esp32/esp32-devkitc/src/esp32-devkitc.h |    6 +-
 .../xtensa/esp32/esp32-devkitc/src/esp32_bringup.c |   37 +-
 8 files changed, 1603 insertions(+), 295 deletions(-)

diff --git a/arch/xtensa/src/esp32/Kconfig b/arch/xtensa/src/esp32/Kconfig
index 913f1b8318..8c3ac980d3 100644
--- a/arch/xtensa/src/esp32/Kconfig
+++ b/arch/xtensa/src/esp32/Kconfig
@@ -369,9 +369,9 @@ config ESP32_I2S0_WSPIN
        range 0 39 if ESP32_I2S0_ROLE_SLAVE
 
 config ESP32_I2S0_DINPIN
-       int "I2S0 DOUT pin"
+       int "I2S0 DIN pin"
        depends on ESP32_I2S0_RX
-       default 12
+       default 19
        range 0 39
 
 config ESP32_I2S0_DOUTPIN
@@ -471,7 +471,6 @@ endchoice
 
 config ESP32_I2S1_DATA_BIT_WIDTH
        int
-       default 16
        default 8 if ESP32_I2S1_DATA_BIT_WIDTH_8BIT
        default 16 if ESP32_I2S1_DATA_BIT_WIDTH_16BIT
        default 24 if ESP32_I2S1_DATA_BIT_WIDTH_24BIT
@@ -488,26 +487,26 @@ config ESP32_I2S1_SAMPLE_RATE
 
 config ESP32_I2S1_BCLKPIN
        int "I2S1 BCLK pin"
-       default 19
+       default 22
        range 0 33 if ESP32_I2S1_ROLE_MASTER
        range 0 39 if ESP32_I2S1_ROLE_SLAVE
 
 config ESP32_I2S1_WSPIN
        int "I2S1 WS pin"
-       default 18
+       default 23
        range 0 33 if ESP32_I2S1_ROLE_MASTER
        range 0 39 if ESP32_I2S1_ROLE_SLAVE
 
-config ESP32_I2S1_DOUTPIN
-       int "I2S1 DOUT pin"
+config ESP32_I2S1_DINPIN
+       int "I2S1 DIN pin"
        depends on ESP32_I2S1_RX
-       default 17
+       default 26
        range 0 39
 
 config ESP32_I2S1_DOUTPIN
        int "I2S1 DOUT pin"
        depends on ESP32_I2S1_TX
-       default 16
+       default 25
        range 0 33
 
 config ESP32_I2S1_MCLK
diff --git a/arch/xtensa/src/esp32/esp32_i2s.c 
b/arch/xtensa/src/esp32/esp32_i2s.c
index 96bc7b8503..064f9b31dd 100644
--- a/arch/xtensa/src/esp32/esp32_i2s.c
+++ b/arch/xtensa/src/esp32/esp32_i2s.c
@@ -90,6 +90,7 @@
 
 #ifdef CONFIG_ESP32_I2S0_RX
 #  define I2S0_RX_ENABLED 1
+#  define I2S_HAVE_RX 1
 #else
 #  define I2S0_RX_ENABLED 0
 #endif
@@ -103,6 +104,7 @@
 
 #ifdef CONFIG_ESP32_I2S1_RX
 #  define I2S1_RX_ENABLED 1
+#  define I2S_HAVE_RX 1
 #else
 #  define I2S1_RX_ENABLED 0
 #endif
@@ -172,7 +174,6 @@ struct esp32_i2s_config_s
   uint32_t total_slot;        /* Total slot number */
 
   bool is_apll;               /* Select APLL as the source clock */
-  uint32_t mclk_multiple;     /* The multiple of mclk to the sample rate */
 
   bool tx_en;                 /* Is TX enabled? */
   bool rx_en;                 /* Is RX enabled? */
@@ -219,9 +220,9 @@ struct esp32_buffer_s
 {
   struct esp32_buffer_s *flink; /* Supports a singly linked list */
 
-  /* The associated DMA outlink */
+  /* The associated DMA in/outlink */
 
-  struct esp32_dmadesc_s dma_outlink[I2S_DMADESC_NUM];
+  struct esp32_dmadesc_s dma_link[I2S_DMADESC_NUM];
 
   i2s_callback_t callback;      /* DMA completion callback */
   uint32_t timeout;             /* Timeout value of the DMA transfers */
@@ -232,7 +233,7 @@ struct esp32_buffer_s
   int result;                   /* The result of the transfer */
 };
 
-/* Internal buffer must be aligned to the sample_size. Sometimes,
+/* 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.
@@ -274,10 +275,11 @@ struct esp32_i2s_s
 
   const struct esp32_i2s_config_s *config;
 
-  uint32_t          mclk_freq;  /* I2S actual master clock */
-  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    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 */
 
 #ifdef I2S_HAVE_TX
   struct esp32_transport_s tx;  /* TX transport state */
@@ -285,6 +287,12 @@ struct esp32_i2s_s
   bool tx_started;              /* TX channel started */
 #endif /* I2S_HAVE_TX */
 
+#ifdef I2S_HAVE_RX
+  struct esp32_transport_s rx;  /* RX transport state */
+
+  bool rx_started;              /* RX channel started */
+#endif /* I2S_HAVE_RX */
+
   /* Pre-allocated pool of buffer containers */
 
   sem_t bufsem;                       /* Buffer wait semaphore */
@@ -322,16 +330,28 @@ static void           i2s_tx_schedule(struct esp32_i2s_s 
*priv,
                                       struct esp32_dmadesc_s *outlink);
 #endif /* I2S_HAVE_TX */
 
+#ifdef I2S_HAVE_RX
+static IRAM_ATTR int  i2s_rxdma_setup(struct esp32_i2s_s *priv,
+                                      struct esp32_buffer_s *bfcontainer);
+static void           i2s_rx_worker(void *arg);
+static void           i2s_rx_schedule(struct esp32_i2s_s *priv,
+                                      struct esp32_dmadesc_s *outlink);
+#endif /* I2S_HAVE_RX */
+
 /* I2S methods (and close friends) */
 
 static uint32_t i2s_set_datawidth(struct esp32_i2s_s *priv);
 static uint32_t i2s_set_clock(struct esp32_i2s_s *priv);
+static uint32_t esp32_i2s_mclkfrequency(struct i2s_dev_s *dev,
+                                        uint32_t frequency);
+static int      esp32_i2s_ioctl(struct i2s_dev_s *dev, int cmd,
+                                unsigned long arg);
+
+#ifdef I2S_HAVE_TX
 static void     i2s_tx_channel_start(struct esp32_i2s_s *priv);
 static void     i2s_tx_channel_stop(struct esp32_i2s_s *priv);
 static int      esp32_i2s_txchannels(struct i2s_dev_s *dev,
                                      uint8_t channels);
-static uint32_t esp32_i2s_mclkfrequency(struct i2s_dev_s *dev,
-                                        uint32_t frequency);
 static uint32_t esp32_i2s_txsamplerate(struct i2s_dev_s *dev,
                                        uint32_t rate);
 static uint32_t esp32_i2s_txdatawidth(struct i2s_dev_s *dev, int bits);
@@ -339,6 +359,21 @@ static int      esp32_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 esp32_i2s_s *priv);
+static void     i2s_rx_channel_stop(struct esp32_i2s_s *priv);
+static int      esp32_i2s_rxchannels(struct i2s_dev_s *dev,
+                                     uint8_t channels);
+static uint32_t esp32_i2s_rxsamplerate(struct i2s_dev_s *dev,
+                                       uint32_t rate);
+static uint32_t esp32_i2s_rxdatawidth(struct i2s_dev_s *dev, int bits);
+static int      esp32_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
@@ -346,10 +381,21 @@ static int      esp32_i2s_send(struct i2s_dev_s *dev,
 
 static const struct i2s_ops_s g_i2sops =
 {
+#ifdef I2S_HAVE_TX
   .i2s_txchannels     = esp32_i2s_txchannels,
   .i2s_txsamplerate   = esp32_i2s_txsamplerate,
   .i2s_txdatawidth    = esp32_i2s_txdatawidth,
   .i2s_send           = esp32_i2s_send,
+#endif /* I2S_HAVE_TX */
+
+#ifdef I2S_HAVE_RX
+  .i2s_rxchannels     = esp32_i2s_rxchannels,
+  .i2s_rxsamplerate   = esp32_i2s_rxsamplerate,
+  .i2s_rxdatawidth    = esp32_i2s_rxdatawidth,
+  .i2s_receive        = esp32_i2s_receive,
+#endif /* I2S_HAVE_RX */
+
+  .i2s_ioctl          = esp32_i2s_ioctl,
   .i2s_mclkfrequency  = esp32_i2s_mclkfrequency,
 };
 
@@ -365,7 +411,6 @@ static const struct esp32_i2s_config_s esp32_i2s0_config =
   .data_width       = CONFIG_ESP32_I2S0_DATA_BIT_WIDTH,
   .rate             = CONFIG_ESP32_I2S0_SAMPLE_RATE,
   .total_slot       = 2,
-  .mclk_multiple    = I2S_MCLK_MULTIPLE_256,
   .tx_en            = I2S0_TX_ENABLED,
   .rx_en            = I2S0_RX_ENABLED,
 #ifdef CONFIG_ESP32_I2S0_MCLK
@@ -426,7 +471,6 @@ static const struct esp32_i2s_config_s esp32_i2s1_config =
   .data_width       = CONFIG_ESP32_I2S1_DATA_BIT_WIDTH,
   .rate             = CONFIG_ESP32_I2S1_SAMPLE_RATE,
   .total_slot       = 2,
-  .mclk_multiple    = I2S_MCLK_MULTIPLE_256,
   .tx_en            = I2S1_TX_ENABLED,
   .rx_en            = I2S1_RX_ENABLED,
 #ifdef CONFIG_ESP32_I2S1_MCLK
@@ -487,7 +531,7 @@ static struct esp32_i2s_s esp32_i2s1_priv =
  *   free list
  *
  * Input Parameters:
- *   priv - I2S state instance
+ *   priv - Initialized I2S device structure.
  *
  * Returned Value:
  *   A non-NULL pointer to the allocate buffer container on success; NULL if
@@ -535,7 +579,7 @@ static struct esp32_buffer_s *i2s_buf_allocate(struct 
esp32_i2s_s *priv)
  *   Free buffer container by adding it to the head of the free list
  *
  * Input Parameters:
- *   priv - I2S state instance
+ *   priv - Initialized I2S device structure.
  *   bfcontainer - The buffer container to be freed
  *
  * Returned Value:
@@ -576,7 +620,7 @@ static void i2s_buf_free(struct esp32_i2s_s *priv,
  *   pre-allocated buffer containers to the free list
  *
  * Input Parameters:
- *   priv - I2S state instance
+ *   priv - Initialized I2S device structure.
  *
  * Returned Value:
  *   OK on success; A negated errno value on failure.
@@ -589,8 +633,10 @@ static void i2s_buf_free(struct esp32_i2s_s *priv,
 
 static int i2s_buf_initialize(struct esp32_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_ESP32_I2S_MAXINFLIGHT; i++)
@@ -606,10 +652,10 @@ static int i2s_buf_initialize(struct esp32_i2s_s *priv)
  *
  * Description:
  *   Initiate the next TX DMA transfer. The DMA outlink was previously bound
- *   so it is safe to start the next DMA transfer at interruption level.
+ *   so it is safe to start the next DMA transfer at interrupt level.
  *
  * Input Parameters:
- *   priv - I2S state instance
+ *   priv - Initialized I2S device structure.
  *
  * Returned Value:
  *   OK on success; a negated errno value on failure
@@ -648,7 +694,7 @@ static int i2s_txdma_start(struct esp32_i2s_s *priv)
 
   modifyreg32(I2S_OUT_LINK_REG(priv->config->port), I2S_OUTLINK_ADDR_M,
               FIELD_TO_VALUE(I2S_OUTLINK_ADDR,
-              (uintptr_t) bfcontainer->dma_outlink));
+              (uintptr_t) bfcontainer->dma_link));
 
   modifyreg32(I2S_OUT_LINK_REG(priv->config->port), I2S_OUTLINK_STOP,
               I2S_OUTLINK_START);
@@ -661,6 +707,70 @@ static int i2s_txdma_start(struct esp32_i2s_s *priv)
 }
 #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 esp32_i2s_s *priv)
+{
+  struct esp32_buffer_s *bfcontainer;
+
+  /* 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;
+    }
+
+  bfcontainer = (struct esp32_buffer_s *)sq_remfirst(&priv->rx.pend);
+
+  /* If there isn't already an active transmission in progress,
+   * then start it.
+   */
+
+  modifyreg32(I2S_RXEOF_NUM_REG(priv->config->port), I2S_RX_EOF_NUM_M,
+              FIELD_TO_VALUE(I2S_RX_EOF_NUM,
+              (bfcontainer->nbytes / 4)));
+
+  modifyreg32(I2S_IN_LINK_REG(priv->config->port), I2S_INLINK_ADDR_M,
+              FIELD_TO_VALUE(I2S_INLINK_ADDR,
+              (uintptr_t) bfcontainer->dma_link));
+
+  modifyreg32(I2S_IN_LINK_REG(priv->config->port), I2S_INLINK_STOP,
+              I2S_INLINK_START);
+
+  modifyreg32(I2S_CONF_REG(priv->config->port), 0, I2S_RX_START);
+
+  sq_addlast((sq_entry_t *)bfcontainer, &priv->rx.act);
+
+  return OK;
+}
+#endif /* I2S_HAVE_RX */
+
 /****************************************************************************
  * Name: i2s_txdma_setup
  *
@@ -668,7 +778,8 @@ static int i2s_txdma_start(struct esp32_i2s_s *priv)
  *   Setup the next TX DMA transfer
  *
  * Input Parameters:
- *   priv - I2S state instance
+ *   priv - Initialized I2S device structure.
+ *   bfcontainer - The buffer container to be set up
  *
  * Returned Value:
  *   OK on success; a negated errno value on failure
@@ -682,34 +793,34 @@ static int i2s_txdma_start(struct esp32_i2s_s *priv)
 static IRAM_ATTR int i2s_txdma_setup(struct esp32_i2s_s *priv,
                                      struct esp32_buffer_s *bfcontainer)
 {
-  struct ap_buffer_s *apb;
-  struct esp32_dmadesc_s *outlink;
-  uint8_t *samp;
-  apb_samp_t samp_size;
+  int ret = OK;
   size_t carry_size;
   uint32_t bytes_queued;
   uint32_t data_copied;
+  struct ap_buffer_s *apb;
+  struct esp32_dmadesc_s *outlink;
+  apb_samp_t samp_size;
+  irqstate_t flags;
   uint8_t *buf;
   uint8_t padding;
-  irqstate_t flags;
-  int ret = OK;
+  uint8_t *samp;
 
   DEBUGASSERT(bfcontainer && bfcontainer->apb);
 
   apb = bfcontainer->apb;
-  outlink = bfcontainer->dma_outlink;
+  outlink = bfcontainer->dma_link;
 
   /* Get the transfer information, accounting for any data offset */
 
-  const apb_samp_t sample_size = priv->data_width / 8;
+  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 % sample_size;
+  carry_size = samp_size % bytes_per_sample;
 
   /* Padding contains the size (in bytes) to be skipped on the
    * internal buffer do provide 1) the ability to handle mono
    * audio files and 2) to correctly fill the buffer to 16 or 32-bits
-   * aligned positions
+   * aligned positions.
    */
 
   padding = priv->channels == 1 ?
@@ -721,7 +832,13 @@ static IRAM_ATTR int i2s_txdma_setup(struct esp32_i2s_s 
*priv,
 
   /* Copy audio data into internal buffer */
 
-  bfcontainer->buf = (uint8_t *)calloc(bfcontainer->nbytes, 1);
+  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 + padding;
@@ -731,25 +848,25 @@ static IRAM_ATTR int i2s_txdma_setup(struct esp32_i2s_s 
*priv,
       memcpy(buf, &priv->tx.carry.value, priv->tx.carry.bytes);
       buf += priv->tx.carry.bytes;
       data_copied += priv->tx.carry.bytes;
-      memcpy(buf, samp, (sample_size - priv->tx.carry.bytes));
-      buf += (sample_size - priv->tx.carry.bytes + padding);
-      samp += (sample_size - priv->tx.carry.bytes);
-      data_copied += (sample_size - priv->tx.carry.bytes);
+      memcpy(buf, samp, (bytes_per_sample - priv->tx.carry.bytes));
+      buf += (bytes_per_sample - priv->tx.carry.bytes + padding);
+      samp += (bytes_per_sample - priv->tx.carry.bytes);
+      data_copied += (bytes_per_sample - priv->tx.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
+   * sample in the internal buffer.
    */
 
   if (padding)
     {
       while (data_copied < (samp_size - carry_size))
         {
-          memcpy(buf, samp, sample_size);
-          buf += (sample_size + padding);
-          samp += sample_size;
-          data_copied += sample_size;
+          memcpy(buf, samp, bytes_per_sample);
+          buf += (bytes_per_sample + padding);
+          samp += bytes_per_sample;
+          data_copied += bytes_per_sample;
         }
     }
   else
@@ -787,8 +904,9 @@ static IRAM_ATTR int i2s_txdma_setup(struct esp32_i2s_s 
*priv,
 
   if (bytes_queued != bfcontainer->nbytes)
     {
-      i2serr("Failed to enqueue I2S buffer (%d bytes of %d)\n",
-             bytes_queued, (uint32_t)bfcontainer->nbytes);
+      i2serr("Failed to enqueue I2S buffer "
+             "(%" PRIu32 " bytes of %" PRIu32 ")\n",
+             bytes_queued, bfcontainer->nbytes);
       return bytes_queued;
     }
 
@@ -808,6 +926,77 @@ static IRAM_ATTR int i2s_txdma_setup(struct esp32_i2s_s 
*priv,
 }
 #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 esp32_i2s_s *priv,
+                           struct esp32_buffer_s *bfcontainer)
+{
+  int ret = OK;
+  struct esp32_dmadesc_s *inlink;
+  uint32_t bytes_queued;
+  irqstate_t flags;
+
+  DEBUGASSERT(bfcontainer && bfcontainer->apb);
+
+  inlink = bfcontainer->dma_link;
+
+  /* Allocate the internal buffer for RX */
+
+  bfcontainer->buf = calloc(bfcontainer->nbytes, 1);
+  if (bfcontainer->buf == NULL)
+    {
+      i2serr("Failed to allocate the DMA internal buffer "
+             "[%" PRIu32 " bytes]", bfcontainer->nbytes);
+      return -ENOMEM;
+    }
+
+  /* Configure DMA stream */
+
+  bytes_queued = esp32_dma_init(inlink, I2S_DMADESC_NUM,
+                                bfcontainer->buf,
+                                bfcontainer->nbytes);
+
+  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
  *
@@ -816,16 +1005,14 @@ static IRAM_ATTR int i2s_txdma_setup(struct esp32_i2s_s 
*priv,
  *   the working thread.
  *
  * Input Parameters:
- *   handle - The DMA handler
- *   arg - A pointer to the chip select struction
- *   result - The result of the DMA transfer
+ *   priv - Initialized I2S device structure.
+ *   outlink - DMA outlink descriptor that triggered the interrupt.
  *
  * Returned Value:
  *   None
  *
  * Assumptions:
  *   - Interrupts are disabled
- *   - The TX timeout has been canceled.
  *
  ****************************************************************************/
 
@@ -858,7 +1045,7 @@ static void i2s_tx_schedule(struct esp32_i2s_s *priv,
 
       /* Find the last descriptor of the current buffer container */
 
-      bfdesc = bfcontainer->dma_outlink;
+      bfdesc = bfcontainer->dma_link;
       while (!(bfdesc->ctrl & DMA_CTRL_EOF))
         {
           DEBUGASSERT(bfdesc->next);
@@ -909,6 +1096,105 @@ static void i2s_tx_schedule(struct esp32_i2s_s *priv,
 }
 #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 esp32_i2s_s *priv,
+                            struct esp32_dmadesc_s *inlink)
+{
+  struct esp32_buffer_s *bfcontainer;
+  struct esp32_dmadesc_s *bfdesc;
+  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 esp32_buffer_s *)sq_peek(&priv->rx.act);
+
+      /* Check if the DMA descriptor that generated an EOF interrupt is the
+       * last descriptor of the current buffer container's DMA inlink.
+       * 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;
+      while (!(bfdesc->ctrl & DMA_CTRL_EOF))
+        {
+          DEBUGASSERT(bfdesc->next);
+          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
  *
@@ -976,62 +1262,219 @@ static void i2s_tx_worker(void *arg)
 #endif /* I2S_HAVE_TX */
 
 /****************************************************************************
- * Name: i2s_configure
+ * Name: i2s_rx_worker
  *
  * Description:
- *   Configure I2S
+ *   RX transfer done worker
  *
  * Input Parameters:
- *   priv - Partially initialized I2S device structure.  This function
- *          will complete the I2S specific portions of the initialization
+ *   arg - the I2S device instance cast to void*
  *
  * Returned Value:
  *   None
  *
  ****************************************************************************/
 
-static void i2s_configure(struct esp32_i2s_s *priv)
+#ifdef I2S_HAVE_RX
+static void i2s_rx_worker(void *arg)
 {
-  /* Set peripheral clock and clear reset */
+  struct esp32_i2s_s *priv = (struct esp32_i2s_s *)arg;
+  struct esp32_buffer_s *bfcontainer;
+  irqstate_t flags;
 
-  modifyreg32(DPORT_PERIP_CLK_EN_REG, 0, DPORT_I2S0_CLK_EN);
-  modifyreg32(DPORT_PERIP_RST_EN_REG, 0, DPORT_I2S0_RST);
-  modifyreg32(DPORT_PERIP_RST_EN_REG, DPORT_I2S0_RST, 0);
+  DEBUGASSERT(priv);
 
-  /* I2S module general init, enable I2S clock */
+  /* 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.
+   *
+   */
 
-  if (!(getreg32(I2S_CLKM_CONF_REG(priv->config->port)) & I2S_CLK_ENA))
+  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)
     {
-      i2sinfo("Enabling I2S port clock...\n");
-      modifyreg32(I2S_CLKM_CONF_REG(priv->config->port), 0, I2S_CLK_ENA);
-      putreg32(0, I2S_CONF2_REG(priv->config->port));
-    }
+      /* 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.
+       */
 
-  /* Configure multiplexed pins as connected on the board */
+      flags = spin_lock_irqsave(&priv->slock);
+      bfcontainer = (struct esp32_buffer_s *)sq_remfirst(&priv->rx.done);
+      spin_unlock_irqrestore(&priv->slock, flags);
 
-  /* TODO: check for loopback mode */
+      apb_samp_t samp_size;
+      uint32_t data_copied;
+      uint8_t padding;
+      uint8_t *buf;
+      uint8_t *samp;
 
-  /* Enable TX channel */
+      /* Padding contains the size (in bytes) to be skipped on the internal
+       * buffer - which store data aligned with 2 or 4 bytes - to correctly
+       * copy and fill the audio buffer according to the actual data width.
+       */
 
-  if (priv->config->dout_pin != I2S_GPIO_UNUSED)
-    {
-      esp32_gpiowrite(priv->config->dout_pin, 1);
-      esp32_configgpio(priv->config->dout_pin, OUTPUT_FUNCTION_3);
-      esp32_gpio_matrix_out(priv->config->dout_pin,
-                            priv->config->dout_outsig, 0, 0);
-    }
+      padding = priv->data_width != I2S_DATA_BIT_WIDTH_16BIT ?
+                (priv->data_width != I2S_DATA_BIT_WIDTH_32BIT ? 1 : 0) : 0;
 
-  /* TODO: repeat above function for RX channel */
+      /* Get the transfer information, accounting for any data offset */
 
-  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 */
+      const apb_samp_t bytes_per_sample = priv->data_width / 8;
+      samp = &bfcontainer->apb->samp[bfcontainer->apb->curbyte];
+      samp_size = (bfcontainer->apb->nbytes - bfcontainer->apb->curbyte);
 
-          esp32_gpiowrite(priv->config->ws_pin, 1);
-          esp32_configgpio(priv->config->ws_pin, INPUT_FUNCTION_3);
-          esp32_gpio_matrix_in(priv->config->ws_pin,
+      /* 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;
+
+      if (padding)
+        {
+          while (data_copied < samp_size)
+            {
+              memcpy(samp, buf, bytes_per_sample);
+              buf += (bytes_per_sample + padding);
+              samp += bytes_per_sample;
+              data_copied += bytes_per_sample;
+            }
+        }
+      else
+        {
+          memcpy(samp, buf, samp_size - data_copied);
+          buf += samp_size - data_copied;
+          samp += samp_size - data_copied;
+          data_copied += samp_size - data_copied;
+        }
+
+      /* Calculate the audio buffer size from the internal buffer size.
+       * The internal I2S buffer size is calculated from I2S_RXEOF_NUM_REG,
+       * which returns the size in number of words.
+       */
+
+      bfcontainer->apb->nbytes =
+                  (getreg32(I2S_RXEOF_NUM_REG(priv->config->port)) * 4);
+
+      /* The internal buffer size must be divided by either 16 or 32 bits
+       * as the I2S peripheral aligns each sample to one of these boundaries.
+       * The result represents the number of samples on the internal buffer.
+       */
+
+      bfcontainer->apb->nbytes /=
+                  ((priv->data_width <= I2S_DATA_BIT_WIDTH_16BIT ?
+                   I2S_DATA_BIT_WIDTH_16BIT : I2S_DATA_BIT_WIDTH_32BIT) / 8);
+
+      /* Finally, calculate the number of bytes on the audio buffer */
+
+      bfcontainer->apb->nbytes *= bytes_per_sample;
+
+      DEBUGASSERT(bfcontainer->apb->nbytes == data_copied);
+
+      /* Perform the RX 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 inlink */
+
+      free(bfcontainer->buf);
+
+      /* 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 esp32_i2s_s *priv)
+{
+  /* Set peripheral clock and clear reset */
+
+  if (priv->config->port == ESP32_I2S0)
+    {
+      modifyreg32(DPORT_PERIP_CLK_EN_REG, 0, DPORT_I2S0_CLK_EN);
+      modifyreg32(DPORT_PERIP_RST_EN_REG, 0, DPORT_I2S0_RST);
+      modifyreg32(DPORT_PERIP_RST_EN_REG, DPORT_I2S0_RST, 0);
+    }
+  else
+    {
+      modifyreg32(DPORT_PERIP_CLK_EN_REG, 0, DPORT_I2S1_CLK_EN);
+      modifyreg32(DPORT_PERIP_RST_EN_REG, 0, DPORT_I2S1_RST);
+      modifyreg32(DPORT_PERIP_RST_EN_REG, DPORT_I2S1_RST, 0);
+    }
+
+  /* I2S module general init, enable I2S clock */
+
+  if (!(getreg32(I2S_CLKM_CONF_REG(priv->config->port)) & I2S_CLK_ENA))
+    {
+      i2sinfo("Enabling I2S port clock...\n");
+      modifyreg32(I2S_CLKM_CONF_REG(priv->config->port), 0, I2S_CLK_ENA);
+      putreg32(0, I2S_CONF2_REG(priv->config->port));
+    }
+
+  /* Configure multiplexed pins as connected on the board */
+
+  /* TODO: check for loopback mode */
+
+  /* Enable TX channel */
+
+  if (priv->config->dout_pin != I2S_GPIO_UNUSED)
+    {
+      esp32_gpiowrite(priv->config->dout_pin, 1);
+      esp32_configgpio(priv->config->dout_pin, OUTPUT_FUNCTION_3);
+      esp32_gpio_matrix_out(priv->config->dout_pin,
+                            priv->config->dout_outsig, 0, 0);
+    }
+
+  /* Enable RX channel */
+
+  if (priv->config->din_pin != I2S_GPIO_UNUSED)
+    {
+      esp32_configgpio(priv->config->din_pin, INPUT_FUNCTION_3);
+      esp32_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 */
+
+          esp32_gpiowrite(priv->config->ws_pin, 1);
+          esp32_configgpio(priv->config->ws_pin, INPUT_FUNCTION_3);
+          esp32_gpio_matrix_in(priv->config->ws_pin,
                                priv->config->ws_out_insig, 0);
 
           esp32_gpiowrite(priv->config->bclk_pin, 1);
@@ -1122,9 +1565,18 @@ static void i2s_configure(struct esp32_i2s_s *priv)
         }
     }
 
-  /* TODO: share BCLK and WS if in full-duplex mode */
+  /* Share BCLK and WS if in full-duplex mode */
+
+  if (priv->config->tx_en && priv->config->rx_en)
+    {
+      modifyreg32(I2S_CONF_REG(priv->config->port), 0, I2S_SIG_LOOPBACK);
+    }
+  else
+    {
+      modifyreg32(I2S_CONF_REG(priv->config->port), I2S_SIG_LOOPBACK, 0);
+    }
 
-  /* Configure the hardware to apply STD format */
+  /* Configure the TX module */
 
   if (priv->config->tx_en)
     {
@@ -1146,8 +1598,8 @@ static void i2s_configure(struct esp32_i2s_s *priv)
           modifyreg32(I2S_CONF_REG(priv->config->port), I2S_TX_SLAVE_MOD, 0);
         }
 
-      /* Congfigure TX chan bit, audio data bit and mono mode.
-       * On ESP32, sample_bit should equals to data_bit
+      /* Configure TX chan bit, audio data bit and mono mode.
+       * On ESP32, sample_bit should equals to data_bit.
        */
 
       /* Set TX data width */
@@ -1207,22 +1659,157 @@ static void i2s_configure(struct esp32_i2s_s *priv)
       modifyreg32(I2S_FIFO_CONF_REG(priv->config->port), 0,
                   I2S_TX_FIFO_MOD_FORCE_EN);
 
-      esp32_i2s_mclkfrequency((struct i2s_dev_s *)priv,
-                              (priv->config->rate *
-                               priv->config->mclk_multiple));
+      /* 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_mclkfrequency` 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;
+        }
+
+      esp32_i2s_mclkfrequency((struct i2s_dev_s *)priv, (priv->config->rate *
+                              priv->mclk_multiple));
 
       priv->rate = priv->config->rate;
       i2s_set_clock(priv);
     }
 
-  /* TODO: check for rx enabled flag */
+  /* Configure the RX module */
+
+  if (priv->config->rx_en)
+    {
+      /* Reset I2S RX module */
+
+      modifyreg32(I2S_CONF_REG(priv->config->port), 0, I2S_RX_RESET);
+      modifyreg32(I2S_CONF_REG(priv->config->port), I2S_RX_RESET, 0);
+      modifyreg32(I2S_LC_CONF_REG(priv->config->port), 0, I2S_IN_RST);
+      modifyreg32(I2S_LC_CONF_REG(priv->config->port), I2S_IN_RST, 0);
+
+      /* Enable/disable I2S RX slave mode */
+
+      if (priv->config->role == I2S_ROLE_SLAVE)
+        {
+          modifyreg32(I2S_CONF_REG(priv->config->port), 0, I2S_RX_SLAVE_MOD);
+        }
+      else
+        {
+          /* Since BCLK and WS are shared, only TX or RX can be master. In
+           * this case, force RX as slave to avoid conflict of clock signal.
+           */
+
+          if (priv->config->tx_en)
+            {
+              modifyreg32(I2S_CONF_REG(priv->config->port), 0,
+                          I2S_RX_SLAVE_MOD);
+            }
+          else
+            {
+              modifyreg32(I2S_CONF_REG(priv->config->port),
+                          I2S_RX_SLAVE_MOD, 0);
+            }
+        }
+
+      /* Congfigure RX chan bit, audio data bit and mono mode.
+       * On ESP32, sample_bit should equals to data_bit.
+       */
+
+      /* Set RX data width */
+
+      priv->data_width = priv->config->data_width;
+      i2s_set_datawidth(priv);
+
+      /* Set I2S RX chan mode */
+
+      modifyreg32(I2S_CONF_CHAN_REG(priv->config->port),
+                  I2S_RX_CHAN_MOD_M, 0);
+
+      /* Enable/disable RX MSB shift, the data will be read at the first
+       * BCK clock.
+       */
+
+      if (priv->config->bit_shift)
+        {
+          modifyreg32(I2S_CONF_REG(priv->config->port), 0, I2S_RX_MSB_SHIFT);
+        }
+      else
+        {
+          modifyreg32(I2S_CONF_REG(priv->config->port), I2S_RX_MSB_SHIFT, 0);
+        }
+
+      /* Configure RX WS signal width. Set to to enable receiver in PCM
+       * standard mode.
+       */
+
+      if (priv->config->ws_width == 1)
+        {
+          modifyreg32(I2S_CONF_REG(priv->config->port), 0,
+                      I2S_RX_SHORT_SYNC);
+        }
+      else
+        {
+          modifyreg32(I2S_CONF_REG(priv->config->port),
+                      I2S_RX_SHORT_SYNC, 0);
+        }
+
+      /* Set I2S RX right channel first */
+
+      if (priv->config->ws_pol == 1)
+        {
+          modifyreg32(I2S_CONF_REG(priv->config->port), 0,
+                      I2S_RX_RIGHT_FIRST);
+        }
+      else
+        {
+          modifyreg32(I2S_CONF_REG(priv->config->port),
+                      I2S_RX_RIGHT_FIRST, 0);
+        }
+
+      /* I2S RX fifo module force enable */
+
+      modifyreg32(I2S_FIFO_CONF_REG(priv->config->port), 0,
+                  I2S_RX_FIFO_MOD_FORCE_EN);
+
+      /* 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_mclkfrequency` 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;
+        }
+
+      esp32_i2s_mclkfrequency((struct i2s_dev_s *)priv, (priv->config->rate *
+                              priv->mclk_multiple));
+
+      priv->rate = priv->config->rate;
+      i2s_set_clock(priv);
+    }
 }
 
 /****************************************************************************
  * Name: i2s_set_datawidth
  *
  * Description:
- *   Set the I2S TX data width.
+ *   Set the I2S TX/RX data width.
  *
  * Input Parameters:
  *   priv - Initialized I2S device structure.
@@ -1234,27 +1821,63 @@ static void i2s_configure(struct esp32_i2s_s *priv)
 
 static uint32_t i2s_set_datawidth(struct esp32_i2s_s *priv)
 {
-  modifyreg32(I2S_SAMPLE_RATE_CONF_REG(priv->config->port),
-              I2S_TX_BITS_MOD_M, FIELD_TO_VALUE(I2S_TX_BITS_MOD,
-              priv->data_width));
+#ifdef I2S_HAVE_TX
+  if (priv->config->tx_en)
+    {
+      modifyreg32(I2S_SAMPLE_RATE_CONF_REG(priv->config->port),
+                  I2S_TX_BITS_MOD_M, FIELD_TO_VALUE(I2S_TX_BITS_MOD,
+                  priv->data_width));
 
-  /* Set TX FIFO operation mode */
+      /* Set TX FIFO operation mode */
 
-  modifyreg32(I2S_FIFO_CONF_REG(priv->config->port), I2S_TX_FIFO_MOD_M,
-              priv->data_width <= I2S_DATA_BIT_WIDTH_16BIT ?
-              FIELD_TO_VALUE(I2S_TX_FIFO_MOD, 0 + priv->config->mono_en) :
-              FIELD_TO_VALUE(I2S_TX_FIFO_MOD, 2 + priv->config->mono_en));
+      modifyreg32(I2S_FIFO_CONF_REG(priv->config->port), I2S_TX_FIFO_MOD_M,
+                  priv->data_width <= I2S_DATA_BIT_WIDTH_16BIT ?
+                  FIELD_TO_VALUE(I2S_TX_FIFO_MOD,
+                                 0 + priv->config->mono_en) :
+                  FIELD_TO_VALUE(I2S_TX_FIFO_MOD,
+                                 2 + priv->config->mono_en));
 
-  /* I2S TX MSB right enable */
+      /* I2S TX MSB right enable */
 
-  if (priv->data_width <= I2S_DATA_BIT_WIDTH_16BIT)
-    {
-      modifyreg32(I2S_CONF_REG(priv->config->port), 0, I2S_TX_MSB_RIGHT);
+      if (priv->data_width <= I2S_DATA_BIT_WIDTH_16BIT)
+        {
+          modifyreg32(I2S_CONF_REG(priv->config->port), 0, I2S_TX_MSB_RIGHT);
+        }
+      else
+        {
+          modifyreg32(I2S_CONF_REG(priv->config->port), I2S_TX_MSB_RIGHT, 0);
+        }
     }
-  else
+#endif /* I2S_HAVE_TX */
+
+#ifdef I2S_HAVE_RX
+  if (priv->config->rx_en)
     {
-      modifyreg32(I2S_CONF_REG(priv->config->port), I2S_TX_MSB_RIGHT, 0);
+      modifyreg32(I2S_SAMPLE_RATE_CONF_REG(priv->config->port),
+                  I2S_RX_BITS_MOD_M, FIELD_TO_VALUE(I2S_RX_BITS_MOD,
+                  priv->data_width));
+
+      /* Set RX FIFO operation mode */
+
+      modifyreg32(I2S_FIFO_CONF_REG(priv->config->port), I2S_RX_FIFO_MOD_M,
+                  priv->data_width <= I2S_DATA_BIT_WIDTH_16BIT ?
+                  FIELD_TO_VALUE(I2S_RX_FIFO_MOD,
+                                 0 + priv->config->mono_en) :
+                  FIELD_TO_VALUE(I2S_RX_FIFO_MOD,
+                                 2 + priv->config->mono_en));
+
+      /* I2S RX MSB right enable */
+
+      if (priv->data_width <= I2S_DATA_BIT_WIDTH_16BIT)
+        {
+          modifyreg32(I2S_CONF_REG(priv->config->port), 0, I2S_RX_MSB_RIGHT);
+        }
+      else
+        {
+          modifyreg32(I2S_CONF_REG(priv->config->port), I2S_RX_MSB_RIGHT, 0);
+        }
     }
+#endif /* I2S_HAVE_RX */
 
   return priv->data_width;
 }
@@ -1278,13 +1901,13 @@ static uint32_t i2s_set_clock(struct esp32_i2s_s *priv)
   uint32_t rate;
   uint32_t bclk;
   uint32_t mclk;
-  uint16_t bclk_div;
   uint32_t sclk;
   uint32_t mclk_div;
   int denominator;
   int numerator;
   uint32_t regval;
   uint32_t freq_diff;
+  uint16_t bclk_div;
 
   /* TODO: provide APLL clock support */
 
@@ -1320,8 +1943,8 @@ static uint32_t i2s_set_clock(struct esp32_i2s_s *priv)
 
   mclk_div = sclk / mclk;
 
-  i2sinfo("Clock division info: [sclk]%"PRIu32" Hz [mdiv] %d "
-          "[mclk] %"PRIu32" Hz [bdiv] %d [bclk] %"PRIu32" Hz\n",
+  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);
 
   freq_diff = abs((int)sclk - (int)(mclk * mclk_div));
@@ -1343,7 +1966,7 @@ static uint32_t i2s_set_clock(struct esp32_i2s_s *priv)
         }
       else
         {
-          uint32_t min = ~0;
+          uint32_t min = UINT32_MAX;
 
           for (int a = 2; a <= I2S_LL_MCLK_DIVIDER_MAX; a++)
             {
@@ -1367,7 +1990,7 @@ static uint32_t i2s_set_clock(struct esp32_i2s_s *priv)
         }
     }
 
-  i2sinfo("Clock register: [mclk] %"PRIu32" Hz [numerator] %d "
+  i2sinfo("Clock register: [mclk] %" PRIu32 " Hz [numerator] %d "
           "[denominator] %d\n", mclk, numerator, denominator);
 
   regval = getreg32(I2S_CLKM_CONF_REG(priv->config->port));
@@ -1379,11 +2002,19 @@ static uint32_t i2s_set_clock(struct esp32_i2s_s *priv)
   regval |= FIELD_TO_VALUE(I2S_CLKM_DIV_A, denominator);
   putreg32(regval, I2S_CLKM_CONF_REG(priv->config->port));
 
-  /* Set I2S tx bck div num */
+  /* Set I2S TX bck div num */
 
+#ifdef I2S_HAVE_TX
   modifyreg32(I2S_SAMPLE_RATE_CONF_REG(priv->config->port),
               I2S_TX_BCK_DIV_NUM_M,
               FIELD_TO_VALUE(I2S_TX_BCK_DIV_NUM, bclk_div));
+#endif /* I2S_HAVE_TX */
+
+#ifdef I2S_HAVE_RX
+  modifyreg32(I2S_SAMPLE_RATE_CONF_REG(priv->config->port),
+              I2S_RX_BCK_DIV_NUM_M,
+              FIELD_TO_VALUE(I2S_RX_BCK_DIV_NUM, bclk_div));
+#endif /* I2S_HAVE_RX */
 
   /* Returns the actual sample rate */
 
@@ -1411,54 +2042,57 @@ static uint32_t i2s_set_clock(struct esp32_i2s_s *priv)
 #ifdef I2S_HAVE_TX
 static void i2s_tx_channel_start(struct esp32_i2s_s *priv)
 {
-  if (priv->tx_started)
+  if (priv->config->tx_en)
     {
-      i2swarn("TX channel of port %d was previously started\n",
-              priv->config->port);
-      return;
-    }
+      if (priv->tx_started)
+        {
+          i2swarn("TX channel of port %d was previously started\n",
+                  priv->config->port);
+          return;
+        }
 
-  /* Reset the TX channel */
+      /* Reset the TX channel */
 
-  modifyreg32(I2S_CONF_REG(priv->config->port), 0, I2S_TX_RESET);
-  modifyreg32(I2S_CONF_REG(priv->config->port), I2S_TX_RESET, 0);
+      modifyreg32(I2S_CONF_REG(priv->config->port), 0, I2S_TX_RESET);
+      modifyreg32(I2S_CONF_REG(priv->config->port), I2S_TX_RESET, 0);
 
-  /* Reset the DMA operation */
+      /* Reset the DMA operation */
 
-  modifyreg32(I2S_LC_CONF_REG(priv->config->port), 0, I2S_OUT_RST);
-  modifyreg32(I2S_LC_CONF_REG(priv->config->port), I2S_OUT_RST, 0);
+      modifyreg32(I2S_LC_CONF_REG(priv->config->port), 0, I2S_OUT_RST);
+      modifyreg32(I2S_LC_CONF_REG(priv->config->port), I2S_OUT_RST, 0);
 
-  /* Reset TX FIFO */
+      /* Reset TX FIFO */
 
-  modifyreg32(I2S_CONF_REG(priv->config->port), 0, I2S_TX_FIFO_RESET);
-  modifyreg32(I2S_CONF_REG(priv->config->port), I2S_TX_FIFO_RESET, 0);
+      modifyreg32(I2S_CONF_REG(priv->config->port), 0, I2S_TX_FIFO_RESET);
+      modifyreg32(I2S_CONF_REG(priv->config->port), I2S_TX_FIFO_RESET, 0);
 
-  /* Enable DMA interrupt */
+      /* Enable DMA interrupt */
 
-  up_enable_irq(priv->config->irq);
+      up_enable_irq(priv->config->irq);
 
-  modifyreg32(I2S_INT_ENA_REG(priv->config->port), UINT32_MAX,
-              I2S_OUT_EOF_INT_ENA);
+      modifyreg32(I2S_INT_ENA_REG(priv->config->port), 0,
+                  I2S_OUT_EOF_INT_ENA);
 
-  /* Enable DMA operation mode */
+      /* Enable DMA operation mode */
 
-  modifyreg32(I2S_FIFO_CONF_REG(priv->config->port), 0, I2S_DSCR_EN);
+      modifyreg32(I2S_FIFO_CONF_REG(priv->config->port), 0, I2S_DSCR_EN);
 
-  /* Unset the DMA outlink */
+      /* Unset the DMA outlink */
 
-  putreg32(0, I2S_OUT_LINK_REG(priv->config->port));
+      putreg32(0, I2S_OUT_LINK_REG(priv->config->port));
 
-  priv->tx_started = true;
+      priv->tx_started = true;
 
-  i2sinfo("Started TX channel of port %d\n", priv->config->port);
+      i2sinfo("Started TX channel of port %d\n", priv->config->port);
+    }
 }
 #endif /* I2S_HAVE_TX */
 
 /****************************************************************************
- * Name: i2s_tx_channel_stop
+ * Name: i2s_rx_channel_start
  *
  * Description:
- *   Stop TX channel for the I2S port
+ *   Start RX channel for the I2S port
  *
  * Input Parameters:
  *   priv - Initialized I2S device structure.
@@ -1468,75 +2102,217 @@ static void i2s_tx_channel_start(struct esp32_i2s_s 
*priv)
  *
  ****************************************************************************/
 
-#ifdef I2S_HAVE_TX
-static void i2s_tx_channel_stop(struct esp32_i2s_s *priv)
+#ifdef I2S_HAVE_RX
+static void i2s_rx_channel_start(struct esp32_i2s_s *priv)
 {
-  if (!priv->tx_started)
+  if (priv->config->rx_en)
     {
-      i2swarn("TX channel of port %d was previously stopped\n",
-              priv->config->port);
-      return;
-    }
+      if (priv->rx_started)
+        {
+          i2swarn("RX channel of port %d was previously started\n",
+                  priv->config->port);
+          return;
+        }
+
+      /* Reset the RX channel */
+
+      modifyreg32(I2S_CONF_REG(priv->config->port), 0, I2S_RX_RESET);
+      modifyreg32(I2S_CONF_REG(priv->config->port), I2S_RX_RESET, 0);
+
+      /* Reset the DMA operation */
 
-  /* Stop TX channel */
+      modifyreg32(I2S_LC_CONF_REG(priv->config->port), 0, I2S_IN_RST);
+      modifyreg32(I2S_LC_CONF_REG(priv->config->port), I2S_IN_RST, 0);
 
-  modifyreg32(I2S_CONF_REG(priv->config->port), I2S_TX_START, 0);
+      /* Reset RX FIFO */
 
-  /* Stop outlink */
+      modifyreg32(I2S_CONF_REG(priv->config->port), 0, I2S_RX_FIFO_RESET);
+      modifyreg32(I2S_CONF_REG(priv->config->port), I2S_RX_FIFO_RESET, 0);
 
-  modifyreg32(I2S_OUT_LINK_REG(priv->config->port), I2S_OUTLINK_START,
-              I2S_OUTLINK_STOP);
+      /* Enable DMA interrupt */
 
-  /* Disable DMA interrupt */
+      up_enable_irq(priv->config->irq);
 
-  modifyreg32(I2S_INT_ENA_REG(priv->config->port), UINT32_MAX, 0);
+      modifyreg32(I2S_INT_ENA_REG(priv->config->port), 0,
+                  I2S_IN_SUC_EOF_INT_ENA);
 
-  /* Disable DMA operation mode */
+      /* Enable DMA operation mode */
 
-  modifyreg32(I2S_FIFO_CONF_REG(priv->config->port), I2S_DSCR_EN, 0);
+      modifyreg32(I2S_FIFO_CONF_REG(priv->config->port), 0, I2S_DSCR_EN);
 
-  up_disable_irq(priv->config->irq);
+      /* Unset the DMA outlink */
 
-  priv->tx_started = false;
+      putreg32(0, I2S_IN_LINK_REG(priv->config->port));
 
-  i2sinfo("Stopped TX channel of port %d\n", priv->config->port);
+      priv->rx_started = true;
+
+      i2sinfo("Started RX channel of port %d\n", priv->config->port);
+    }
 }
-#endif /* I2S_HAVE_TX */
+#endif /* I2S_HAVE_RX */
 
 /****************************************************************************
- * Name: esp32_i2s_interrupt
+ * Name: i2s_tx_channel_stop
  *
  * Description:
- *   Common I2S DMA interrupt handler
+ *   Stop TX channel for the I2S port
  *
  * Input Parameters:
- *   arg - i2s controller private data
+ *   priv - Initialized I2S device structure.
  *
  * Returned Value:
- *   Standard interrupt return value.
+ *   None
  *
  ****************************************************************************/
 
-static int esp32_i2s_interrupt(int irq, void *context, void *arg)
+#ifdef I2S_HAVE_TX
+static void i2s_tx_channel_stop(struct esp32_i2s_s *priv)
 {
-  struct esp32_i2s_s *priv = (struct esp32_i2s_s *)arg;
-  struct esp32_dmadesc_s *cur = NULL;
+  if (priv->config->tx_en)
+    {
+      if (!priv->tx_started)
+        {
+          i2swarn("TX channel of port %d was previously stopped\n",
+                  priv->config->port);
+          return;
+        }
 
-  uint32_t status = getreg32(I2S_INT_ST_REG(priv->config->port));
+      /* Stop TX channel */
 
-  putreg32(UINT32_MAX, I2S_INT_CLR_REG(priv->config->port));
+      modifyreg32(I2S_CONF_REG(priv->config->port), I2S_TX_START, 0);
 
-  if (status & I2S_OUT_EOF_INT_ST)
-    {
-      cur = (struct esp32_dmadesc_s *)
-            getreg32(I2S_OUT_EOF_DES_ADDR_REG(priv->config->port));
+      /* Stop outlink */
 
-      /* Schedule completion of the transfer to occur on the worker thread */
+      modifyreg32(I2S_OUT_LINK_REG(priv->config->port), I2S_OUTLINK_START,
+                  I2S_OUTLINK_STOP);
 
-      i2s_tx_schedule(priv, cur);
-    }
+      /* Disable DMA interrupt */
 
-  return 0;
+      modifyreg32(I2S_INT_ENA_REG(priv->config->port),
+                  I2S_OUT_EOF_INT_ENA, 0);
+
+      /* Disable DMA operation mode */
+
+      modifyreg32(I2S_FIFO_CONF_REG(priv->config->port), I2S_DSCR_EN, 0);
+
+      up_disable_irq(priv->config->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 esp32_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 */
+
+      modifyreg32(I2S_CONF_REG(priv->config->port), I2S_RX_START, 0);
+
+      /* Stop outlink */
+
+      modifyreg32(I2S_IN_LINK_REG(priv->config->port), I2S_INLINK_START,
+                  I2S_INLINK_STOP);
+
+      /* Disable DMA interrupt */
+
+      modifyreg32(I2S_INT_ENA_REG(priv->config->port),
+                  I2S_IN_SUC_EOF_INT_ENA, 0);
+
+      /* Disable DMA operation mode */
+
+      modifyreg32(I2S_FIFO_CONF_REG(priv->config->port), I2S_DSCR_EN, 0);
+
+      up_disable_irq(priv->config->irq);
+
+      priv->rx_started = false;
+
+      i2sinfo("Stopped RX channel of port %d\n", priv->config->port);
+    }
+}
+#endif /* I2S_HAVE_RX */
+
+/****************************************************************************
+ * Name: esp32_i2s_interrupt
+ *
+ * Description:
+ *   Common I2S DMA 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 esp32_i2s_interrupt(int irq, void *context, void *arg)
+{
+  struct esp32_i2s_s *priv = (struct esp32_i2s_s *)arg;
+  struct esp32_dmadesc_s *cur = NULL;
+
+  uint32_t status = getreg32(I2S_INT_ST_REG(priv->config->port));
+
+  putreg32(UINT32_MAX, I2S_INT_CLR_REG(priv->config->port));
+
+#ifdef I2S_HAVE_TX
+  if (priv->config->tx_en)
+    {
+      if (status & I2S_OUT_EOF_INT_ST)
+        {
+          cur = (struct esp32_dmadesc_s *)
+                getreg32(I2S_OUT_EOF_DES_ADDR_REG(priv->config->port));
+
+          /* Schedule completion of the transfer on the worker thread */
+
+          i2s_tx_schedule(priv, cur);
+        }
+    }
+#endif /* I2S_HAVE_TX */
+
+#ifdef I2S_HAVE_RX
+  if (priv->config->rx_en)
+    {
+      if (status & I2S_IN_SUC_EOF_INT_ST)
+        {
+          cur = (struct esp32_dmadesc_s *)
+                getreg32(I2S_IN_EOF_DES_ADDR_REG(priv->config->port));
+
+          /* Schedule completion of the transfer on the worker thread */
+
+          i2s_rx_schedule(priv, cur);
+        }
+    }
+#endif /* I2S_HAVE_RX */
+
+  return 0;
 }
 
 /****************************************************************************
@@ -1590,18 +2366,60 @@ static uint32_t esp32_i2s_mclkfrequency(struct 
i2s_dev_s *dev,
  *
  ****************************************************************************/
 
+#ifdef I2S_HAVE_TX
 static int esp32_i2s_txchannels(struct i2s_dev_s *dev, uint8_t channels)
 {
   struct esp32_i2s_s *priv = (struct esp32_i2s_s *)dev;
 
-  if (channels != 1 && channels != 2)
+  if (priv->config->tx_en)
     {
-      return -EINVAL;
+      if (channels != 1 && channels != 2)
+        {
+          return -EINVAL;
+        }
+
+      priv->channels = channels;
+      return OK;
     }
 
-  priv->channels = channels;
-  return OK;
+  return -ENOTTY;
+}
+#endif /* I2S_HAVE_TX */
+
+/****************************************************************************
+ * Name: esp32_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 esp32_i2s_rxchannels(struct i2s_dev_s *dev, uint8_t channels)
+{
+  struct esp32_i2s_s *priv = (struct esp32_i2s_s *)dev;
+
+  if (priv->config->rx_en)
+    {
+      if (channels != 1 && channels != 2)
+        {
+          return -EINVAL;
+        }
+
+      priv->channels = channels;
+      return OK;
+    }
+
+  return -ENOTTY;
 }
+#endif /* I2S_HAVE_RX */
 
 /****************************************************************************
  * Name: esp32_i2s_txsamplerate
@@ -1621,20 +2439,64 @@ static int esp32_i2s_txchannels(struct i2s_dev_s *dev, 
uint8_t channels)
  *
  ****************************************************************************/
 
+#ifdef I2S_HAVE_TX
 static uint32_t esp32_i2s_txsamplerate(struct i2s_dev_s *dev, uint32_t rate)
 {
   struct esp32_i2s_s *priv = (struct esp32_i2s_s *)dev;
 
-  i2s_tx_channel_stop(priv);
+  if (priv->config->tx_en)
+    {
+      i2s_tx_channel_stop(priv);
+
+      priv->rate = rate;
 
-  priv->rate = rate;
+      rate = i2s_set_clock(priv);
 
-  rate = i2s_set_clock(priv);
+      i2s_tx_channel_start(priv);
 
-  i2s_tx_channel_start(priv);
+      return rate;
+    }
 
-  return rate;
+  return 0;
 }
+#endif /* I2S_HAVE_TX */
+
+/****************************************************************************
+ * Name: esp32_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:
+ *   Returns the resulting bitrate
+ *
+ ****************************************************************************/
+
+#ifdef I2S_HAVE_RX
+static uint32_t esp32_i2s_rxsamplerate(struct i2s_dev_s *dev, uint32_t rate)
+{
+  struct esp32_i2s_s *priv = (struct esp32_i2s_s *)dev;
+
+  if (priv->config->rx_en)
+    {
+      i2s_rx_channel_stop(priv);
+
+      priv->rate = rate;
+
+      rate = i2s_set_clock(priv);
+
+      i2s_rx_channel_start(priv);
+
+      return rate;
+    }
+
+  return 0;
+}
+#endif /* I2S_HAVE_RX */
 
 /****************************************************************************
  * Name: esp32_i2s_txdatawidth
@@ -1652,20 +2514,65 @@ static uint32_t esp32_i2s_txsamplerate(struct i2s_dev_s 
*dev, uint32_t rate)
  *
  ****************************************************************************/
 
+#ifdef I2S_HAVE_TX
 static uint32_t esp32_i2s_txdatawidth(struct i2s_dev_s *dev, int bits)
 {
   struct esp32_i2s_s *priv = (struct esp32_i2s_s *)dev;
 
-  i2s_tx_channel_stop(priv);
+  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: esp32_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 esp32_i2s_rxdatawidth(struct i2s_dev_s *dev, int bits)
+{
+  struct esp32_i2s_s *priv = (struct esp32_i2s_s *)dev;
+
+  if (priv->config->rx_en)
+    {
+      i2s_rx_channel_stop(priv);
+
+      priv->data_width = bits;
 
-  priv->data_width = bits;
+      i2s_set_datawidth(priv);
 
-  i2s_set_datawidth(priv);
+      i2s_rx_channel_start(priv);
 
-  i2s_tx_channel_start(priv);
+      return bits;
+    }
 
-  return bits;
+  return 0;
 }
+#endif /* I2S_HAVE_RX */
 
 /****************************************************************************
  * Name: esp32_i2s_send
@@ -1690,95 +2597,281 @@ static uint32_t esp32_i2s_txdatawidth(struct i2s_dev_s 
*dev, int bits)
  *
  ****************************************************************************/
 
+#ifdef I2S_HAVE_TX
 static int esp32_i2s_send(struct i2s_dev_s *dev, struct ap_buffer_s *apb,
+                          i2s_callback_t callback, void *arg,
+                          uint32_t timeout)
+{
+  struct esp32_i2s_s *priv = (struct esp32_i2s_s *)dev;
+
+  if (priv->config->tx_en)
+    {
+      struct esp32_buffer_s *bfcontainer;
+      int ret = OK;
+      uint32_t nbytes;
+      uint32_t nsamp;
+
+      /* Check audio buffer data size */
+
+      nbytes = (apb->nbytes - apb->curbyte) + priv->tx.carry.bytes;
+
+      /* If data width is 8, it is necessary to use a word of 16 bits;
+       * If data width is 24, it is necessary to use a word of 32 bits;
+       */
+
+      nsamp = nbytes / (priv->data_width / 8);
+      nbytes = nsamp *
+              ((priv->data_width <= I2S_DATA_BIT_WIDTH_16BIT ?
+                I2S_DATA_BIT_WIDTH_16BIT : I2S_DATA_BIT_WIDTH_32BIT) / 8);
+
+      /* ESP32's I2S peripheral always consider two channels. If the source
+       * is mono, it is necessary to copy zero-ed data between each sample.
+       */
+
+      nbytes *= priv->channels == 1 ? 2 : 1;
+
+      /* Ensure nbytes is 4-byte aligned */
+
+      nbytes = ALIGN_UP(nbytes, sizeof(uintptr_t));
+
+      if (nbytes > (ESP32_DMA_DATALEN_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 - (ESP32_DMA_DATALEN_MAX * I2S_DMADESC_NUM));
+          return -EFBIG;
+        }
+
+      /* Allocate a buffer container in advance */
+
+      bfcontainer = i2s_buf_allocate(priv);
+      DEBUGASSERT(bfcontainer);
+
+      /* 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: esp32_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 esp32_i2s_receive(struct i2s_dev_s *dev, struct ap_buffer_s *apb,
                              i2s_callback_t callback, void *arg,
                              uint32_t timeout)
 {
   struct esp32_i2s_s *priv = (struct esp32_i2s_s *)dev;
-  struct esp32_buffer_s *bfcontainer;
-  int ret = OK;
-  uint32_t nbytes;
-  uint32_t nsamp;
 
-  /* Check audio buffer data size */
+  if (priv->config->rx_en)
+    {
+      struct esp32_buffer_s *bfcontainer;
+      int ret = OK;
+      uint32_t nbytes;
+      uint32_t nsamp;
 
-  nbytes = (apb->nbytes - apb->curbyte) + priv->tx.carry.bytes;
+      /* Check max audio buffer data size */
 
-  /* If data width is 8, it is necessary to use a word of 16 bits;
-   * If data width is 24, it is necessary to a word of 32 bits;
-   */
+      nbytes = apb->nmaxbytes;
+
+      /* If data width is 8, it is necessary to use a word of 16 bits;
+       * If data width is 24, it is necessary to use a word of 32 bits;
+       */
 
-  nsamp = nbytes / (priv->data_width / 8);
-  nbytes = nsamp *
-           ((priv->data_width <= I2S_DATA_BIT_WIDTH_16BIT ?
-             I2S_DATA_BIT_WIDTH_16BIT : I2S_DATA_BIT_WIDTH_32BIT) / 8);
+      nsamp = nbytes / (priv->data_width / 8);
+      nbytes = nsamp *
+              ((priv->data_width <= I2S_DATA_BIT_WIDTH_16BIT ?
+                I2S_DATA_BIT_WIDTH_16BIT : I2S_DATA_BIT_WIDTH_32BIT) / 8);
 
-  /* ESP32's I2S peripheral always consider two channels. If the source is
-   * mono, it is necessary to copy zero-ed data between each sample.
-   */
+      if (nbytes > (ESP32_DMA_DATALEN_MAX * I2S_DMADESC_NUM))
+        {
+          i2serr("Required buffer size can't fit into DMA inlink "
+                "(exceeds in %" PRIu32 " bytes). Try to increase the "
+                "number of the DMA descriptors (CONFIG_I2S_DMADESC_NUM).",
+                nbytes - (ESP32_DMA_DATALEN_MAX * I2S_DMADESC_NUM));
+          return -EFBIG;
+        }
 
-  nbytes *= priv->channels == 1 ? 2 : 1;
+      /* Allocate a buffer container in advance */
 
-  /* Ensure nbytes is 4-byte aligned */
+      bfcontainer = i2s_buf_allocate(priv);
+      DEBUGASSERT(bfcontainer);
 
-  nbytes = ALIGN_UP(nbytes, sizeof(uintptr_t));
+      /* Get exclusive access to the I2S driver data */
 
-  if (nbytes > (ESP32_DMA_DATALEN_MAX * I2S_DMADESC_NUM))
-    {
-      i2serr("Required buffer size can not be fitted into DMA outlink "
-             "(exceeds in %" PRIu32 " bytes). Try to increase the "
-             "number of the DMA descriptors (CONFIG_I2S_DMADESC_NUM).",
-             nbytes - (ESP32_DMA_DATALEN_MAX * I2S_DMADESC_NUM));
-      return -EFBIG;
+      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("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;
     }
 
-  /* Allocate a buffer container in advance */
+  return -ENOTTY;
+}
+#endif /* I2S_HAVE_RX */
 
-  bfcontainer = i2s_buf_allocate(priv);
-  DEBUGASSERT(bfcontainer);
+/****************************************************************************
+ * Name: esp32_i2s_ioctl
+ *
+ * Description:
+ *   Perform a device ioctl
+ *
+ ****************************************************************************/
 
-  /* Get exclusive access to the I2S driver data */
+static int esp32_i2s_ioctl(struct i2s_dev_s *dev, int cmd,
+                            unsigned long arg)
+{
+  struct audio_buf_desc_s  *bufdesc;
+  int ret = -ENOTTY;
 
-  ret = nxmutex_lock(&priv->lock);
-  if (ret < 0)
+  switch (cmd)
     {
-      goto errout_with_buf;
-    }
+      /* AUDIOIOC_START - Start the audio stream.
+       *
+       *   ioctl argument:  Audio session
+       */
 
-  /* Add a reference to the audio buffer */
+      case AUDIOIOC_START:
+        {
+          i2sinfo("AUDIOIOC_START\n");
 
-  apb_reference(apb);
+          ret = OK;
+        }
+        break;
 
-  /* Initialize the buffer container structure */
+      /* AUDIOIOC_ALLOCBUFFER - Allocate an audio buffer
+       *
+       *   ioctl argument:  pointer to an audio_buf_desc_s structure
+       */
 
-  bfcontainer->callback = callback;
-  bfcontainer->timeout  = timeout;
-  bfcontainer->arg      = arg;
-  bfcontainer->apb      = apb;
-  bfcontainer->nbytes   = nbytes;
-  bfcontainer->result   = -EBUSY;
+      case AUDIOIOC_ALLOCBUFFER:
+        {
+          i2sinfo("AUDIOIOC_ALLOCBUFFER\n");
 
-  ret = i2s_txdma_setup(priv, bfcontainer);
-  if (ret != OK)
-    {
-      goto errout_with_buf;
-    }
+          bufdesc = (struct audio_buf_desc_s *) arg;
+          ret = apb_alloc(bufdesc);
+        }
+        break;
 
-  i2sinfo("Queued %d bytes into DMA buffers\n", apb->nbytes);
-  i2s_dump_buffer("Audio pipeline buffer:", &apb->samp[apb->curbyte],
-                  apb->nbytes - apb->curbyte);
+      /* AUDIOIOC_FREEBUFFER - Free an audio buffer
+       *
+       *   ioctl argument:  pointer to an audio_buf_desc_s structure
+       */
 
-  nxmutex_unlock(&priv->lock);
-  return OK;
+      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;
+    }
 
-errout_with_buf:
-  nxmutex_unlock(&priv->lock);
-  i2s_buf_free(priv, bfcontainer);
   return ret;
 }
 
 /****************************************************************************
- * Name: esp32_i2sdma_setup
+ * Name: i2s_dma_setup
  *
  * Description:
  *   Configure the DMA for the I2S peripheral
@@ -1793,7 +2886,7 @@ errout_with_buf:
  *
  ****************************************************************************/
 
-static int esp32_i2sdma_setup(struct esp32_i2s_s *priv)
+static int i2s_dma_setup(struct esp32_i2s_s *priv)
 {
   int ret;
 
@@ -1865,8 +2958,6 @@ struct i2s_dev_s *esp32_i2sbus_initialize(int port)
         return NULL;
     }
 
-  priv->tx_started = false;
-
   flags = spin_lock_irqsave(&priv->slock);
 
   i2s_configure(priv);
@@ -1879,7 +2970,7 @@ struct i2s_dev_s *esp32_i2sbus_initialize(int port)
       goto err;
     }
 
-  ret = esp32_i2sdma_setup(priv);
+  ret = i2s_dma_setup(priv);
   if (ret < 0)
     {
       goto err;
@@ -1888,9 +2979,23 @@ struct i2s_dev_s *esp32_i2sbus_initialize(int port)
 #ifdef I2S_HAVE_TX
   /* Start TX channel */
 
-  i2s_tx_channel_start(priv);
+  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 */
@@ -1902,7 +3007,7 @@ struct i2s_dev_s *esp32_i2sbus_initialize(int port)
   /* Failure exit */
 
 err:
-  spin_unlock_irqrestore(&priv->lock, flags);
+  spin_unlock_irqrestore(&priv->slock, flags);
   return NULL;
 }
 
diff --git a/arch/xtensa/src/esp32/esp32_i2s.h 
b/arch/xtensa/src/esp32/esp32_i2s.h
index 6ee13bb279..ab6235f059 100644
--- a/arch/xtensa/src/esp32/esp32_i2s.h
+++ b/arch/xtensa/src/esp32/esp32_i2s.h
@@ -41,13 +41,8 @@ extern "C"
 
 #ifdef CONFIG_ESP32_I2S
 
-#ifdef CONFIG_ESP32_I2S0
-  #define ESP32_I2S0 0
-#endif /* CONFIG_ESP32_I2S0 */
-
-#ifdef CONFIG_ESP32_I2S1
-  #define ESP32_I2S1 1
-#endif /* CONFIG_ESP32_I2S1 */
+#define ESP32_I2S0 0
+#define ESP32_I2S1 1
 
 /****************************************************************************
  * Public Function Prototypes
diff --git a/boards/xtensa/esp32/common/src/esp32_board_i2sdev.c 
b/boards/xtensa/esp32/common/src/esp32_board_i2sdev.c
index ad5c634f31..c436f950db 100644
--- a/boards/xtensa/esp32/common/src/esp32_board_i2sdev.c
+++ b/boards/xtensa/esp32/common/src/esp32_board_i2sdev.c
@@ -37,8 +37,8 @@
 
 #include "esp32_i2s.h"
 
-#if defined(CONFIG_ESP32_I2S0) && !defined(CONFIG_AUDIO_CS4344) && \
-!defined(CONFIG_AUDIO_ES8388) || defined(CONFIG_ESP32_I2S1)
+#if defined(CONFIG_ESP32_I2S0) && !defined(CONFIG_AUDIO_CS4344) || \
+    defined(CONFIG_ESP32_I2S1)
 
 /****************************************************************************
  * Public Functions
@@ -54,7 +54,9 @@
  *   number.
  *
  * Input Parameters:
- *   port  - The I2S port used for the device
+ *   port       - The I2S port used for the device
+ *   enable_tx  - Register device as TX if true
+ *   enable_rx  - Register device as RX if true
  *
  * Returned Value:
  *   Zero is returned on success.  Otherwise, a negated errno value is
@@ -62,12 +64,11 @@
  *
  ****************************************************************************/
 
-int board_i2sdev_initialize(int port)
+int board_i2sdev_initialize(int port, bool enable_tx, bool enable_rx)
 {
   struct audio_lowerhalf_s *audio_i2s;
-  struct audio_lowerhalf_s *pcm;
   struct i2s_dev_s *i2s;
-  char devname[12];
+  char devname[8];
   int ret;
 
   ainfo("Initializing I2S\n");
@@ -75,7 +76,7 @@ int board_i2sdev_initialize(int port)
   i2s = esp32_i2sbus_initialize(port);
 
 #ifdef CONFIG_AUDIO_I2SCHAR
-  ret = i2schar_register(i2s, 0);
+  ret = i2schar_register(i2s, port);
   if (ret < 0)
     {
       aerr("ERROR: i2schar_register failed: %d\n", ret);
@@ -83,29 +84,71 @@ int board_i2sdev_initialize(int port)
     }
 #endif
 
-  audio_i2s = audio_i2s_initialize(i2s, true);
-
-  if (!audio_i2s)
+  if (enable_tx)
     {
-      auderr("ERROR: Failed to initialize I2S\n");
-      return -ENODEV;
-    }
+      /* Initialize audio output */
 
-  pcm = pcm_decode_initialize(audio_i2s);
+      audio_i2s = audio_i2s_initialize(i2s, true);
 
-  if (!pcm)
-    {
-      auderr("ERROR: Failed create the PCM decoder\n");
-      return  -ENODEV;
-    }
+      if (audio_i2s == NULL)
+        {
+          auderr("ERROR: Failed to initialize I2S audio output\n");
+          return -ENODEV;
+        }
 
-  snprintf(devname, 12, "pcm%d", port);
+      snprintf(devname, sizeof(devname), "pcm%d", port);
 
-  ret = audio_register(devname, pcm);
+      /* If nxlooper is selected, the playback buffer is not rendered as
+       * a WAV file. Therefore, PCM decode will fail while processing such
+       * output buffer. In such a case, we bypass the PCM decode.
+       */
 
-  if (ret < 0)
+#ifdef CONFIG_SYSTEM_NXLOOPER
+      ret = audio_register(devname, audio_i2s);
+#else
+      struct audio_lowerhalf_s *pcm;
+
+      pcm = pcm_decode_initialize(audio_i2s);
+
+      if (pcm == NULL)
+        {
+          auderr("ERROR: Failed create the PCM decoder\n");
+          return -ENODEV;
+        }
+
+      ret = audio_register(devname, pcm);
+#endif /* CONFIG_SYSTEM_NXLOOPER */
+
+      if (ret < 0)
+        {
+          auderr("ERROR: Failed to register /dev/%s device: %d\n",
+                 devname, ret);
+          return ret;
+        }
+    }
+
+  if (enable_rx)
     {
-      auderr("ERROR: Failed to register /dev/%s device: %d\n", devname, ret);
+      /* Initialize audio input */
+
+      audio_i2s = audio_i2s_initialize(i2s, false);
+
+      if (audio_i2s == NULL)
+        {
+          auderr("ERROR: Failed to initialize I2S audio input\n");
+          return -ENODEV;
+        }
+
+      snprintf(devname, sizeof(devname), "pcm_in%d", port);
+
+      ret = audio_register(devname, audio_i2s);
+
+      if (ret < 0)
+        {
+          auderr("ERROR: Failed to register /dev/%s device: %d\n",
+                 devname, ret);
+          return ret;
+        }
     }
 
   return ret;
diff --git a/boards/xtensa/esp32/esp32-devkitc/configs/i2schar/defconfig 
b/boards/xtensa/esp32/esp32-devkitc/configs/i2schar/defconfig
index dcf9c709fa..a4aec245ef 100644
--- a/boards/xtensa/esp32/esp32-devkitc/configs/i2schar/defconfig
+++ b/boards/xtensa/esp32/esp32-devkitc/configs/i2schar/defconfig
@@ -6,7 +6,6 @@
 # modifications.
 #
 # CONFIG_ARCH_LEDS is not set
-# CONFIG_ESP32_I2S0_RX is not set
 # CONFIG_NDEBUG is not set
 # CONFIG_NSH_ARGCAT is not set
 # CONFIG_NSH_CMDOPT_HEXDUMP is not set
@@ -25,13 +24,18 @@ CONFIG_AUDIO_I2S=y
 CONFIG_AUDIO_I2SCHAR=y
 CONFIG_BOARD_LOOPSPERMSEC=16717
 CONFIG_BUILTIN=y
-CONFIG_DEBUG_SYMBOLS=y
 CONFIG_DRIVERS_AUDIO=y
 CONFIG_ESP32_I2S0=y
+CONFIG_ESP32_I2S0_DATA_BIT_WIDTH_16BIT=y
 CONFIG_ESP32_I2S0_MCLK=y
+CONFIG_ESP32_I2S1=y
 CONFIG_ESP32_I2S=y
 CONFIG_ESP32_UART0=y
 CONFIG_EXAMPLES_I2SCHAR=y
+CONFIG_EXAMPLES_I2SCHAR_BUFSIZE=1024
+CONFIG_EXAMPLES_I2SCHAR_RX=y
+CONFIG_EXAMPLES_I2SCHAR_RXBUFFERS=2
+CONFIG_EXAMPLES_I2SCHAR_RXSTACKSIZE=4096
 CONFIG_EXAMPLES_I2SCHAR_TX=y
 CONFIG_EXAMPLES_I2SCHAR_TXBUFFERS=2
 CONFIG_EXAMPLES_I2SCHAR_TXSTACKSIZE=4096
diff --git a/boards/xtensa/esp32/esp32-devkitc/configs/nxlooper/defconfig 
b/boards/xtensa/esp32/esp32-devkitc/configs/nxlooper/defconfig
new file mode 100644
index 0000000000..e5e60cd57e
--- /dev/null
+++ b/boards/xtensa/esp32/esp32-devkitc/configs/nxlooper/defconfig
@@ -0,0 +1,129 @@
+#
+# This file is autogenerated: PLEASE DO NOT EDIT IT.
+#
+# You can use "make menuconfig" to make any modifications to the installed 
.config file.
+# You can then do "make savedefconfig" to generate a new defconfig file that 
includes your
+# modifications.
+#
+# CONFIG_ARCH_LEDS is not set
+# CONFIG_ESP32_I2S0_RX is not set
+# CONFIG_ESP32_I2S1_TX is not set
+# CONFIG_NDEBUG is not set
+# CONFIG_NSH_ARGCAT is not set
+# CONFIG_NSH_CMDOPT_HEXDUMP is not set
+# CONFIG_NSH_CMDPARMS is not set
+CONFIG_ALLOW_BSD_COMPONENTS=y
+CONFIG_ARCH="xtensa"
+CONFIG_ARCH_BOARD="esp32-devkitc"
+CONFIG_ARCH_BOARD_COMMON=y
+CONFIG_ARCH_BOARD_ESP32_DEVKITC=y
+CONFIG_ARCH_CHIP="esp32"
+CONFIG_ARCH_CHIP_ESP32=y
+CONFIG_ARCH_CHIP_ESP32WROVER=y
+CONFIG_ARCH_INTERRUPTSTACK=4096
+CONFIG_ARCH_STACKDUMP=y
+CONFIG_ARCH_XTENSA=y
+CONFIG_AUDIO=y
+CONFIG_AUDIOUTILS_MMLPARSER_LIB=y
+CONFIG_AUDIO_BUFFER_NUMBYTES=4092
+CONFIG_AUDIO_EXCLUDE_BALANCE=y
+CONFIG_AUDIO_EXCLUDE_FFORWARD=y
+CONFIG_AUDIO_EXCLUDE_TONE=y
+CONFIG_AUDIO_EXCLUDE_VOLUME=y
+CONFIG_AUDIO_I2S=y
+CONFIG_AUDIO_I2SCHAR=y
+CONFIG_AUDIO_NUM_BUFFERS=4
+CONFIG_BOARDCTL_ROMDISK=y
+CONFIG_BOARD_LOOPSPERMSEC=16717
+CONFIG_BUILTIN=y
+CONFIG_DEFAULT_TASK_STACKSIZE=4096
+CONFIG_DEV_URANDOM=y
+CONFIG_DRIVERS_AUDIO=y
+CONFIG_DRIVERS_IEEE80211=y
+CONFIG_DRIVERS_WIRELESS=y
+CONFIG_ESP32_I2S0=y
+CONFIG_ESP32_I2S0_DATA_BIT_WIDTH_16BIT=y
+CONFIG_ESP32_I2S0_MCLK=y
+CONFIG_ESP32_I2S1=y
+CONFIG_ESP32_I2S1_DATA_BIT_WIDTH_16BIT=y
+CONFIG_ESP32_I2S=y
+CONFIG_ESP32_SPIFLASH=y
+CONFIG_ESP32_SPIFLASH_SPIFFS=y
+CONFIG_ESP32_STORAGE_MTD_SIZE=0x80000
+CONFIG_ESP32_UART0=y
+CONFIG_ESP32_WIFI=y
+CONFIG_ESP32_WIFI_SAVE_PARAM=y
+CONFIG_EXAMPLES_I2SCHAR=y
+CONFIG_EXAMPLES_I2SCHAR_BUFSIZE=960
+CONFIG_EXAMPLES_I2SCHAR_RX=y
+CONFIG_EXAMPLES_I2SCHAR_RXBUFFERS=2
+CONFIG_EXAMPLES_I2SCHAR_RXSTACKSIZE=2048
+CONFIG_EXAMPLES_I2SCHAR_TX=y
+CONFIG_EXAMPLES_I2SCHAR_TXBUFFERS=2
+CONFIG_EXAMPLES_I2SCHAR_TXSTACKSIZE=2048
+CONFIG_EXAMPLES_ROMFS=y
+CONFIG_FS_PROCFS=y
+CONFIG_FS_ROMFS=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
+CONFIG_IOB_NBUFFERS=24
+CONFIG_IOB_THROTTLE=0
+CONFIG_MM_REGIONS=3
+CONFIG_NAME_MAX=48
+CONFIG_NETDB_DNSCLIENT=y
+CONFIG_NETDB_DNSCLIENT_NAMESIZE=64
+CONFIG_NETDEV_LATEINIT=y
+CONFIG_NETDEV_PHY_IOCTL=y
+CONFIG_NETDEV_WIRELESS_IOCTL=y
+CONFIG_NETINIT_WAPI_ALG=1
+CONFIG_NETUTILS_IPERF=y
+CONFIG_NETUTILS_TELNETD=y
+CONFIG_NET_BROADCAST=y
+CONFIG_NET_ETH_PKTSIZE=1518
+CONFIG_NET_ICMP=y
+CONFIG_NET_ICMP_SOCKET=y
+CONFIG_NET_NACTIVESOCKETS=32
+CONFIG_NET_STATISTICS=y
+CONFIG_NET_TCP=y
+CONFIG_NET_TCP_DELAYED_ACK=y
+CONFIG_NET_TCP_WRITE_BUFFERS=y
+CONFIG_NET_UDP=y
+CONFIG_NSH_ARCHINIT=y
+CONFIG_NSH_BUILTIN_APPS=y
+CONFIG_NSH_FILEIOSIZE=512
+CONFIG_NSH_LINELEN=300
+CONFIG_NSH_READLINE=y
+CONFIG_NXPLAYER_HTTP_STREAMING_SUPPORT=y
+CONFIG_NXPLAYER_PLAYTHREAD_STACKSIZE=4096
+CONFIG_POSIX_SPAWN_DEFAULT_STACKSIZE=2048
+CONFIG_PREALLOC_TIMERS=4
+CONFIG_PTHREAD_MUTEX_TYPES=y
+CONFIG_PTHREAD_STACK_DEFAULT=2048
+CONFIG_RAM_SIZE=114688
+CONFIG_RAM_START=0x20000000
+CONFIG_RR_INTERVAL=200
+CONFIG_SCHED_HPWORK=y
+CONFIG_SCHED_HPWORKSTACKSIZE=2048
+CONFIG_SCHED_LPWORK=y
+CONFIG_SCHED_WAITPID=y
+CONFIG_SIG_DEFAULT=y
+CONFIG_SPI=y
+CONFIG_SPIFFS_NAME_MAX=48
+CONFIG_START_DAY=6
+CONFIG_START_MONTH=12
+CONFIG_START_YEAR=2011
+CONFIG_SYSTEM_DHCPC_RENEW=y
+CONFIG_SYSTEM_NSH=y
+CONFIG_SYSTEM_NXLOOPER=y
+CONFIG_SYSTEM_NXPLAYER=y
+CONFIG_SYSTEM_PING=y
+CONFIG_TELNET_CHARACTER_MODE=y
+CONFIG_TLS_TASK_NELEM=4
+CONFIG_UART0_SERIAL_CONSOLE=y
+CONFIG_WIRELESS=y
+CONFIG_WIRELESS_WAPI=y
+CONFIG_WIRELESS_WAPI_CMDTOOL=y
diff --git a/boards/xtensa/esp32/esp32-devkitc/src/esp32-devkitc.h 
b/boards/xtensa/esp32/esp32-devkitc/src/esp32-devkitc.h
index 3360d9ca95..8e5e9d9d08 100644
--- a/boards/xtensa/esp32/esp32-devkitc/src/esp32-devkitc.h
+++ b/boards/xtensa/esp32/esp32-devkitc/src/esp32-devkitc.h
@@ -165,7 +165,9 @@ int esp32_twai_setup(void);
  *   number.
  *
  * Input Parameters:
- *   port  - The I2S port used for the device
+ *   port       - The I2S port used for the device
+ *   enable_tx  - Register device as TX if true
+ *   enable_rx  - Register device as RX if true
  *
  * Returned Value:
  *   Zero is returned on success.  Otherwise, a negated errno value is
@@ -175,7 +177,7 @@ int esp32_twai_setup(void);
 
 #if defined(CONFIG_ESP32_I2S0) && !defined(CONFIG_AUDIO_CS4344) || \
     defined(CONFIG_ESP32_I2S1)
-int board_i2sdev_initialize(int port);
+int board_i2sdev_initialize(int port, bool enable_tx, bool enable_rx);
 #endif
 
 /****************************************************************************
diff --git a/boards/xtensa/esp32/esp32-devkitc/src/esp32_bringup.c 
b/boards/xtensa/esp32/esp32-devkitc/src/esp32_bringup.c
index cc3eca312c..cef7858b14 100644
--- a/boards/xtensa/esp32/esp32-devkitc/src/esp32_bringup.c
+++ b/boards/xtensa/esp32/esp32-devkitc/src/esp32_bringup.c
@@ -488,6 +488,12 @@ int esp32_bringup(void)
 
 #ifdef CONFIG_ESP32_I2S
 
+#if defined(CONFIG_ESP32_I2S0) && !defined(CONFIG_AUDIO_CS4344) || \
+    defined(CONFIG_ESP32_I2S1)
+  bool i2s_enable_tx;
+  bool i2s_enable_rx;
+#endif
+
 #ifdef CONFIG_ESP32_I2S0
 
   /* Configure I2S0 */
@@ -503,27 +509,52 @@ int esp32_bringup(void)
     }
 #else
 
+#ifdef CONFIG_ESP32_I2S0_TX
+  i2s_enable_tx = true;
+#else
+  i2s_enable_tx = false;
+#endif /* CONFIG_ESP32_I2S0_TX */
+
+#ifdef CONFIG_ESP32_I2S0_RX
+    i2s_enable_rx = true;
+#else
+    i2s_enable_rx = false;
+#endif /* CONFIG_ESP32_I2S0_RX */
+
   /* Configure I2S generic audio on I2S0 */
 
-  ret = board_i2sdev_initialize(ESP32_I2S0);
+  ret = board_i2sdev_initialize(ESP32_I2S0, i2s_enable_tx, i2s_enable_rx);
   if (ret < 0)
     {
       syslog(LOG_ERR, "Failed to initialize I2S%d driver: %d\n",
              CONFIG_ESP32_I2S0, ret);
     }
+
 #endif /* CONFIG_AUDIO_CS4344 */
 
 #endif  /* CONFIG_ESP32_I2S0 */
 
 #ifdef CONFIG_ESP32_I2S1
 
+#ifdef CONFIG_ESP32_I2S1_TX
+  i2s_enable_tx = true;
+#else
+  i2s_enable_tx = false;
+#endif /* CONFIG_ESP32_I2S1_TX */
+
+#ifdef CONFIG_ESP32_I2S1_RX
+    i2s_enable_rx = true;
+#else
+    i2s_enable_rx = false;
+#endif /* CONFIG_ESP32_I2S1_RX */
+
   /* Configure I2S generic audio on I2S1 */
 
-  ret = board_i2sdev_initialize(ESP32_I2S1);
+  ret = board_i2sdev_initialize(ESP32_I2S1, i2s_enable_tx, i2s_enable_rx);
   if (ret < 0)
     {
       syslog(LOG_ERR, "Failed to initialize I2S%d driver: %d\n",
-             CONFIG_ESP32_I2S0, ret);
+             CONFIG_ESP32_I2S1, ret);
     }
 
 #endif  /* CONFIG_ESP32_I2S1 */

Reply via email to