You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@nuttx.apache.org by xi...@apache.org on 2022/11/04 16:37:07 UTC

[incubator-nuttx] branch master updated (e239cd942e -> e791d73bb2)

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

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


    from e239cd942e mpfs/mpfs_mm_init: Fix the section align mask checks
     new 9ecc345c02 esp32/i2s: use internal buffer to handle multiple audio formats
     new 64abc72052 drivers/audio/cs4344: add txchannels method and fix supported data width
     new 20d8a55181 esp32/i2s: replace nxsem when used as a lock to nxmutex
     new e791d73bb2 drivers/audio/cs4344: fix configure function when setting mclk

The 4 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 arch/xtensa/src/esp32/esp32_i2s.c                  | 746 ++++++++++++++-------
 .../esp32/esp32-devkitc/configs/audio/defconfig    |   3 +-
 drivers/audio/cs4344.c                             |  30 +-
 3 files changed, 530 insertions(+), 249 deletions(-)


[incubator-nuttx] 04/04: drivers/audio/cs4344: fix configure function when setting mclk

Posted by xi...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

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

commit e791d73bb26f2ac4b390387490fc1583c31ba2d4
Author: Tiago Medicci Serrano <ti...@espressif.com>
AuthorDate: Tue Nov 1 11:02:51 2022 -0300

    drivers/audio/cs4344: fix configure function when setting mclk
---
 drivers/audio/cs4344.c | 1 +
 1 file changed, 1 insertion(+)

diff --git a/drivers/audio/cs4344.c b/drivers/audio/cs4344.c
index 516af188dc..ac83e83c33 100644
--- a/drivers/audio/cs4344.c
+++ b/drivers/audio/cs4344.c
@@ -602,6 +602,7 @@ cs4344_configure(FAR struct audio_lowerhalf_s *dev,
               {
                 audwarn("WARNING: MCLK could not be set on lower half\n");
                 priv->mclk_freq = 0;
+                ret = OK;
               }
           }
 


[incubator-nuttx] 03/04: esp32/i2s: replace nxsem when used as a lock to nxmutex

Posted by xi...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

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

commit 20d8a551817260eaa11c9326fbfd763816acb364
Author: Tiago Medicci Serrano <ti...@espressif.com>
AuthorDate: Tue Oct 25 15:49:46 2022 -0300

    esp32/i2s: replace nxsem when used as a lock to nxmutex
---
 arch/xtensa/src/esp32/esp32_i2s.c | 59 ++++++++++++---------------------------
 1 file changed, 18 insertions(+), 41 deletions(-)

diff --git a/arch/xtensa/src/esp32/esp32_i2s.c b/arch/xtensa/src/esp32/esp32_i2s.c
index 59ceea1a6a..7923e295b9 100644
--- a/arch/xtensa/src/esp32/esp32_i2s.c
+++ b/arch/xtensa/src/esp32/esp32_i2s.c
@@ -269,10 +269,10 @@ struct esp32_transport_s
 struct esp32_i2s_s
 {
   struct i2s_dev_s  dev;          /* Externally visible I2S interface */
-  sem_t             exclsem;      /* Assures mutually exclusive access */
+  mutex_t           lock;         /* Ensures mutually exclusive access */
   int               cpuint;       /* I2S interrupt ID */
   uint8_t           cpu;          /* CPU ID */
-  spinlock_t        lock;         /* Device specific lock. */
+  spinlock_t        slock;        /* Device specific lock. */
 
   /* Port configuration */
 
@@ -310,9 +310,6 @@ struct esp32_i2s_s
 
 /* Semaphore helpers */
 
-static int      i2s_exclsem_take(struct esp32_i2s_s *priv);
-#define         i2s_exclsem_give(priv) nxsem_post(&priv->exclsem)
-
 static int      i2s_bufsem_take(struct esp32_i2s_s *priv);
 #define         i2s_bufsem_give(priv) nxsem_post(&priv->bufsem)
 
@@ -487,26 +484,6 @@ static struct esp32_i2s_s esp32_i2s1_priv =
  * Private Functions
  ****************************************************************************/
 
-/****************************************************************************
- * Name: i2s_exclsem_take
- *
- * Description:
- *   Take the exclusive access semaphore handling any exceptional conditions
- *
- * Input Parameters:
- *   priv - A reference to the i2s peripheral state
- *
- * Returned Value:
- *   Normally OK, but may return -ECANCELED in the rare event that the task
- *   has been canceled.
- *
- ****************************************************************************/
-
-static int i2s_exclsem_take(struct esp32_i2s_s *priv)
-{
-  return nxsem_wait_uninterruptible(&priv->exclsem);
-}
-
 /****************************************************************************
  * Name: i2s_bufsem_take
  *
@@ -565,14 +542,14 @@ static struct esp32_buffer_s *i2s_buf_allocate(struct esp32_i2s_s *priv)
 
   /* Get the buffer from the head of the free list */
 
-  flags = spin_lock_irqsave(&priv->lock);
+  flags = spin_lock_irqsave(&priv->slock);
   bfcontainer = priv->bf_freelist;
   DEBUGASSERT(bfcontainer);
 
   /* Unlink the buffer from the freelist */
 
   priv->bf_freelist = bfcontainer->flink;
-  spin_unlock_irqrestore(&priv->lock, flags);
+  spin_unlock_irqrestore(&priv->slock, flags);
   return bfcontainer;
 }
 
@@ -601,7 +578,7 @@ static void i2s_buf_free(struct esp32_i2s_s *priv,
 
   /* Put the buffer container back on the free list (circbuf) */
 
-  flags = spin_lock_irqsave(&priv->lock);
+  flags = spin_lock_irqsave(&priv->slock);
 
   bfcontainer->apb = NULL;
   bfcontainer->buf = NULL;
@@ -609,7 +586,7 @@ static void i2s_buf_free(struct esp32_i2s_s *priv,
   bfcontainer->flink  = priv->bf_freelist;
   priv->bf_freelist = bfcontainer;
 
-  spin_unlock_irqrestore(&priv->lock, flags);
+  spin_unlock_irqrestore(&priv->slock, flags);
 
   /* Wake up any threads waiting for a buffer container */
 
@@ -850,7 +827,7 @@ static int i2s_txdma_setup(struct esp32_i2s_s *priv,
       return bytes_queued;
     }
 
-  flags = spin_lock_irqsave(&priv->lock);
+  flags = spin_lock_irqsave(&priv->slock);
 
   /* Add the buffer container to the end of the TX pending queue */
 
@@ -860,7 +837,7 @@ static int i2s_txdma_setup(struct esp32_i2s_s *priv,
 
   ret = i2s_txdma_start(priv);
 
-  spin_unlock_irqrestore(&priv->lock, flags);
+  spin_unlock_irqrestore(&priv->slock, flags);
 
   return ret;
 }
@@ -1012,9 +989,9 @@ static void i2s_tx_worker(void *arg)
        * also modified from the interrupt level.
        */
 
-      flags = spin_lock_irqsave(&priv->lock);
+      flags = spin_lock_irqsave(&priv->slock);
       bfcontainer = (struct esp32_buffer_s *)sq_remfirst(&priv->tx.done);
-      spin_unlock_irqrestore(&priv->lock, flags);
+      spin_unlock_irqrestore(&priv->slock, flags);
 
       /* Perform the TX transfer done callback */
 
@@ -1801,7 +1778,7 @@ static int esp32_i2s_send(struct i2s_dev_s *dev, struct ap_buffer_s *apb,
 
   /* Get exclusive access to the I2S driver data */
 
-  ret = i2s_exclsem_take(priv);
+  ret = nxmutex_lock(&priv->lock);
   if (ret < 0)
     {
       goto errout_with_buf;
@@ -1831,12 +1808,12 @@ static int esp32_i2s_send(struct i2s_dev_s *dev, struct ap_buffer_s *apb,
   i2s_dump_buffer("Audio pipeline buffer:", &apb->samp[apb->curbyte],
                   apb->nbytes - apb->curbyte);
 
-  i2s_exclsem_give(priv);
+  nxmutex_unlock(&priv->lock);
 
   return OK;
 
 errout_with_buf:
-  i2s_exclsem_give(priv);
+  nxmutex_unlock(&priv->lock);
   i2s_buf_free(priv, bfcontainer);
   return ret;
 }
@@ -1931,9 +1908,9 @@ struct i2s_dev_s *esp32_i2sbus_initialize(int port)
 
   priv->tx_started = false;
 
-  flags = spin_lock_irqsave(&priv->lock);
+  flags = spin_lock_irqsave(&priv->slock);
 
-  nxsem_init(&priv->exclsem, 0, 1);
+  nxmutex_init(&priv->lock);
 
   i2s_configure(priv);
 
@@ -1957,7 +1934,7 @@ struct i2s_dev_s *esp32_i2sbus_initialize(int port)
   i2s_tx_channel_start(priv);
 #endif /* I2S_HAVE_TX */
 
-  spin_unlock_irqrestore(&priv->lock, flags);
+  spin_unlock_irqrestore(&priv->slock, flags);
 
   /* Success exit */
 
@@ -1968,8 +1945,8 @@ struct i2s_dev_s *esp32_i2sbus_initialize(int port)
   /* Failure exit */
 
 err:
-  spin_unlock_irqrestore(&priv->lock, flags);
-  nxsem_destroy(&priv->exclsem);
+  spin_unlock_irqrestore(&priv->slock, flags);
+  nxmutex_destroy(&priv->lock);
   return NULL;
 }
 


[incubator-nuttx] 02/04: drivers/audio/cs4344: add txchannels method and fix supported data width

Posted by xi...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

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

commit 64abc7205259cd5badfeb114041ade0be3cbdfa4
Author: Tiago Medicci Serrano <ti...@espressif.com>
AuthorDate: Tue Oct 25 14:34:35 2022 -0300

    drivers/audio/cs4344: add txchannels method and fix supported data width
---
 drivers/audio/cs4344.c | 29 +++++++++++++++++++++++------
 1 file changed, 23 insertions(+), 6 deletions(-)

diff --git a/drivers/audio/cs4344.c b/drivers/audio/cs4344.c
index 57278239c8..516af188dc 100644
--- a/drivers/audio/cs4344.c
+++ b/drivers/audio/cs4344.c
@@ -55,6 +55,7 @@
  ****************************************************************************/
 
 static int  cs4344_setmclkfrequency(FAR struct cs4344_dev_s *priv);
+static void cs4344_settxchannels(FAR struct cs4344_dev_s *priv);
 static void cs4344_setdatawidth(FAR struct cs4344_dev_s *priv);
 static void cs4344_setbitrate(FAR struct cs4344_dev_s *priv);
 
@@ -287,11 +288,26 @@ static int cs4344_setmclkfrequency(FAR struct cs4344_dev_s *priv)
   return ret > 0 ? OK : ret;
 }
 
+/****************************************************************************
+ * Name: cs4344_settxchannels
+ *
+ * Description:
+ *   Set the number of channels
+ *
+ ****************************************************************************/
+
+static void cs4344_settxchannels(FAR struct cs4344_dev_s *priv)
+{
+  DEBUGASSERT(priv);
+
+  I2S_TXCHANNELS(priv->i2s, priv->nchannels);
+}
+
 /****************************************************************************
  * Name: cs4344_setdatawidth
  *
  * Description:
- *   Set the 8- or 16-bit data modes
+ *   Set the 16 or 24-bit data modes
  *
  ****************************************************************************/
 
@@ -301,13 +317,13 @@ static void cs4344_setdatawidth(FAR struct cs4344_dev_s *priv)
     {
       /* Reset default default setting */
 
-      priv->i2s->ops->i2s_txdatawidth(priv->i2s, 16);
+      I2S_TXDATAWIDTH(priv->i2s, 16);
     }
   else
     {
-      /* This should select 8-bit with no companding */
+      /* This should select 24-bit with no companding */
 
-      priv->i2s->ops->i2s_txdatawidth(priv->i2s, 8);
+      I2S_TXDATAWIDTH(priv->i2s, 24);
     }
 }
 
@@ -320,7 +336,7 @@ static void cs4344_setbitrate(FAR struct cs4344_dev_s *priv)
 {
   DEBUGASSERT(priv);
 
-  priv->i2s->ops->i2s_txsamplerate(priv->i2s, priv->samprate);
+  I2S_TXSAMPLERATE(priv->i2s, priv->samprate);
 
   audinfo("sample rate=%u nchannels=%u bpsamp=%u\n",
           priv->samprate, priv->nchannels, priv->bpsamp);
@@ -553,7 +569,7 @@ cs4344_configure(FAR struct audio_lowerhalf_s *dev,
             break;
           }
 
-        if (caps->ac_controls.b[2] != 8 && caps->ac_controls.b[2] != 16)
+        if (caps->ac_controls.b[2] != 16 && caps->ac_controls.b[2] != 24)
           {
             auderr("ERROR: Unsupported bits per sample: %d\n",
                    caps->ac_controls.b[2]);
@@ -589,6 +605,7 @@ cs4344_configure(FAR struct audio_lowerhalf_s *dev,
               }
           }
 
+        cs4344_settxchannels(priv);
         cs4344_setdatawidth(priv);
         cs4344_setbitrate(priv);
       }


[incubator-nuttx] 01/04: esp32/i2s: use internal buffer to handle multiple audio formats

Posted by xi...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

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

commit 9ecc345c02599d1ea8eff65eaf8b7df7c2336dd9
Author: Tiago Medicci Serrano <ti...@espressif.com>
AuthorDate: Tue Oct 25 14:25:15 2022 -0300

    esp32/i2s: use internal buffer to handle multiple audio formats
---
 arch/xtensa/src/esp32/esp32_i2s.c                  | 695 +++++++++++++++------
 .../esp32/esp32-devkitc/configs/audio/defconfig    |   3 +-
 2 files changed, 492 insertions(+), 206 deletions(-)

diff --git a/arch/xtensa/src/esp32/esp32_i2s.c b/arch/xtensa/src/esp32/esp32_i2s.c
index 830b25b728..59ceea1a6a 100644
--- a/arch/xtensa/src/esp32/esp32_i2s.c
+++ b/arch/xtensa/src/esp32/esp32_i2s.c
@@ -82,29 +82,37 @@
 #define I2S_DMA_CHANNEL_MAX (2)
 
 #ifdef CONFIG_ESP32_I2S0_TX
-  #define I2S0_TX_ENABLED 1
-  #define I2S_HAVE_TX 1
+#  define I2S0_TX_ENABLED 1
+#  define I2S_HAVE_TX 1
 #else
-  #define I2S0_TX_ENABLED 0
+#  define I2S0_TX_ENABLED 0
 #endif
 
 #ifdef CONFIG_ESP32_I2S0_RX
-  #define I2S0_RX_ENABLED 1
+#  define I2S0_RX_ENABLED 1
 #else
-  #define I2S0_RX_ENABLED 0
+#  define I2S0_RX_ENABLED 0
 #endif
 
 #ifdef CONFIG_ESP32_I2S1_TX
-  #define I2S1_TX_ENABLED 1
-  #define I2S_HAVE_TX 1
+#  define I2S1_TX_ENABLED 1
+#  define I2S_HAVE_TX 1
 #else
-  #define I2S1_TX_ENABLED 0
+#  define I2S1_TX_ENABLED 0
 #endif
 
 #ifdef CONFIG_ESP32_I2S1_RX
-  #define I2S1_RX_ENABLED 1
+#  define I2S1_RX_ENABLED 1
 #else
-  #define I2S1_RX_ENABLED 0
+#  define I2S1_RX_ENABLED 0
+#endif
+
+#ifndef MIN
+#  define MIN(a,b) ((a) < (b) ? (a) : (b))
+#endif
+
+#ifndef ALIGN_UP
+#  define ALIGN_UP(num, align) (((num) + ((align) - 1)) & ~((align) - 1))
 #endif
 
 /* Debug ********************************************************************/
@@ -205,6 +213,10 @@ struct esp32_i2s_config_s
   /* WS signal polarity, set true to enable high lever first */
 
   bool ws_pol;
+
+  /* The associated DMA outlink */
+
+  struct esp32_dmadesc_s dma_outlink[I2S_DMADESC_NUM];
 };
 
 struct esp32_buffer_s
@@ -219,9 +231,23 @@ struct esp32_buffer_s
   uint32_t timeout;             /* Timeout value of the DMA transfers */
   void *arg;                    /* Callback's argument */
   struct ap_buffer_s *apb;      /* The audio buffer */
+  uint8_t *buf;                 /* The DMA's descriptor buffer */
+  uint32_t nbytes;              /* The DMA's descriptor buffer size */
   int result;                   /* The result of the transfer */
 };
 
+/* Internal buffer must be aligned to the sample_size. Sometimes,
+ * however, the audio buffer is not aligned and additional bytes must
+ * be copied to be inserted on the next buffer. This structure keeps
+ * track of the bytes that were not written to the internal buffer yet.
+ */
+
+struct esp32_buffer_carry_s
+{
+  uint32_t value;
+  size_t bytes;
+};
+
 /* This structure describes the state of one receiver or transmitter
  * transport.
  */
@@ -232,31 +258,35 @@ struct esp32_transport_s
   sq_queue_t act;               /* A queue of active transfers */
   sq_queue_t done;              /* A queue of completed transfers */
   struct work_s work;           /* Supports worker thread operations */
+
+  /* Bytes to be written at the beginning of the next DMA buffer */
+
+  struct esp32_buffer_carry_s carry;
 };
 
 /* The state of the one I2S peripheral */
 
 struct esp32_i2s_s
 {
-  struct i2s_dev_s  dev;        /* Externally visible I2S interface */
-  sem_t             exclsem;    /* Assures mutually exclusive access */
-  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 */
-  int               cpuint;     /* I2S interrupt ID */
-  uint8_t           cpu;        /* CPU ID */
-  spinlock_t        lock;       /* Device specific lock. */
+  struct i2s_dev_s  dev;          /* Externally visible I2S interface */
+  sem_t             exclsem;      /* Assures mutually exclusive access */
+  int               cpuint;       /* I2S interrupt ID */
+  uint8_t           cpu;          /* CPU ID */
+  spinlock_t        lock;         /* Device specific lock. */
 
   /* Port configuration */
 
   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 */
+
 #ifdef I2S_HAVE_TX
   struct esp32_transport_s tx;  /* TX transport state */
 
-  /* Stuff var to fill DMA buffer if not word-aligned */
-
-  uint32_t stuff;
+  bool tx_started;              /* TX channel started */
 #endif /* I2S_HAVE_TX */
 
   /* Pre-allocated pool of buffer containers */
@@ -291,21 +321,27 @@ static int      i2s_bufsem_take(struct esp32_i2s_s *priv);
 static struct esp32_buffer_s *
                 i2s_buf_allocate(struct esp32_i2s_s *priv);
 static void     i2s_buf_free(struct esp32_i2s_s *priv,
-                  struct esp32_buffer_s *bfcontainer);
+                             struct esp32_buffer_s *bfcontainer);
 static int      i2s_buf_initialize(struct esp32_i2s_s *priv);
 
 /* DMA support */
 
 #ifdef I2S_HAVE_TX
-static int      i2s_txdma_setup(struct esp32_i2s_s *priv,
-                                struct esp32_buffer_s *bfcontainer);
-static void     i2s_tx_worker(void *arg);
-static void     i2s_tx_schedule(struct esp32_i2s_s *priv,
-                                struct esp32_dmadesc_s *outlink);
+static IRAM_ATTR int  i2s_txdma_setup(struct esp32_i2s_s *priv,
+                                      struct esp32_buffer_s *bfcontainer);
+static void           i2s_tx_worker(void *arg);
+static void           i2s_tx_schedule(struct esp32_i2s_s *priv,
+                                      struct esp32_dmadesc_s *outlink);
 #endif /* I2S_HAVE_TX */
 
 /* 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 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,
@@ -322,6 +358,7 @@ static int      esp32_i2s_send(struct i2s_dev_s *dev,
 
 static const struct i2s_ops_s g_i2sops =
 {
+  .i2s_txchannels     = esp32_i2s_txchannels,
   .i2s_txsamplerate   = esp32_i2s_txsamplerate,
   .i2s_txdatawidth    = esp32_i2s_txdatawidth,
   .i2s_send           = esp32_i2s_send,
@@ -567,6 +604,8 @@ static void i2s_buf_free(struct esp32_i2s_s *priv,
   flags = spin_lock_irqsave(&priv->lock);
 
   bfcontainer->apb = NULL;
+  bfcontainer->buf = NULL;
+  bfcontainer->nbytes = 0;
   bfcontainer->flink  = priv->bf_freelist;
   priv->bf_freelist = bfcontainer;
 
@@ -600,6 +639,9 @@ static int i2s_buf_initialize(struct esp32_i2s_s *priv)
 {
   int ret;
 
+  priv->tx.carry.bytes = 0;
+  priv->tx.carry.value = 0;
+
   priv->bf_freelist = NULL;
   ret = nxsem_init(&priv->bufsem, 0, 0);
 
@@ -700,9 +742,15 @@ static int i2s_txdma_setup(struct esp32_i2s_s *priv,
 {
   struct ap_buffer_s *apb;
   struct esp32_dmadesc_s *outlink;
-  uintptr_t samp;
-  apb_samp_t nbytes;
+  uint8_t *samp;
+  apb_samp_t samp_size;
+  size_t carry_size;
   uint32_t bytes_queued;
+  uint32_t data_copied;
+  uint8_t *buf;
+  uint8_t padding;
+  irqstate_t flags;
+  int ret = OK;
 
   DEBUGASSERT(bfcontainer && bfcontainer->apb);
 
@@ -711,27 +759,110 @@ static int i2s_txdma_setup(struct esp32_i2s_s *priv,
 
   /* Get the transfer information, accounting for any data offset */
 
-  samp   = (uintptr_t)&apb->samp[apb->curbyte];
-  nbytes = apb->nbytes - apb->curbyte;
+  const apb_samp_t sample_size = 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;
+
+  /* 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
+   */
+
+  padding = priv->channels == 1 ?
+            ((priv->data_width <= I2S_DATA_BIT_WIDTH_16BIT ?
+              I2S_DATA_BIT_WIDTH_16BIT : I2S_DATA_BIT_WIDTH_32BIT) / 8) : 0;
+
+  padding += priv->data_width != I2S_DATA_BIT_WIDTH_16BIT ?
+             (priv->data_width != I2S_DATA_BIT_WIDTH_32BIT ? 1 : 0) : 0;
+
+  /* Copy audio data into internal buffer */
+
+  bfcontainer->buf = (uint8_t *)calloc(bfcontainer->nbytes, 1);
+
+  data_copied = 0;
+  buf = bfcontainer->buf + padding;
+
+  if (priv->tx.carry.bytes)
+    {
+      memcpy(buf, &priv->tx.carry.value, priv->tx.carry.bytes);
+      buf += priv->tx.carry.bytes;
+      data_copied += priv->tx.carry.bytes;
+      memcpy(buf, samp, (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);
+    }
+
+  /* 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
+   */
+
+  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;
+        }
+    }
+  else
+    {
+      memcpy(buf, samp, samp_size - (data_copied + carry_size));
+      buf += samp_size - (data_copied + carry_size);
+      samp += samp_size - (data_copied + carry_size);
+      data_copied += samp_size - (data_copied + carry_size);
+    }
+
+  /* If the audio buffer's size is not a multiple of the sample size,
+   * it's necessary to carry the remaining bytes that are part of what
+   * would be the last sample on this buffer. These bytes will then be
+   * saved and inserted at the beginning of the next DMA buffer to
+   * rebuild the sample correctly.
+   */
+
+  priv->tx.carry.bytes = carry_size;
+
+  if (priv->tx.carry.bytes)
+    {
+      memcpy((uint8_t *)&priv->tx.carry.value, samp, priv->tx.carry.bytes);
+    }
+
+  /* Release our reference on the audio buffer. This may very likely
+   * cause the audio buffer to be freed.
+   */
+
+  apb_free(bfcontainer->apb);
 
   /* Configure DMA stream */
 
-  bytes_queued = esp32_dma_init_with_padding(outlink, I2S_DMADESC_NUM,
-                                              (uint8_t *)samp, nbytes,
-                                              &priv->stuff);
+  bytes_queued = esp32_dma_init(outlink, I2S_DMADESC_NUM,
+                                bfcontainer->buf, bfcontainer->nbytes);
 
-  if (bytes_queued != nbytes)
+  if (bytes_queued != bfcontainer->nbytes)
     {
       i2serr("Failed to enqueue I2S buffer (%d bytes of %d)\n",
-              bytes_queued, (uint32_t)nbytes);
+             bytes_queued, (uint32_t)bfcontainer->nbytes);
       return bytes_queued;
     }
 
+  flags = spin_lock_irqsave(&priv->lock);
+
   /* Add the buffer container to the end of the TX pending queue */
 
   sq_addlast((sq_entry_t *)bfcontainer, &priv->tx.pend);
 
-  return OK;
+  /* Trigger DMA transfer if no transmission is in progress */
+
+  ret = i2s_txdma_start(priv);
+
+  spin_unlock_irqrestore(&priv->lock, flags);
+
+  return ret;
 }
 #endif /* I2S_HAVE_TX */
 
@@ -891,11 +1022,9 @@ static void i2s_tx_worker(void *arg)
       bfcontainer->callback(&priv->dev, bfcontainer->apb,
                             bfcontainer->arg, bfcontainer->result);
 
-      /* Release our reference on the audio buffer.  This may very likely
-       * cause the audio buffer to be freed.
-       */
+      /* Release the internal buffer used by the DMA outlink */
 
-      apb_free(bfcontainer->apb);
+      free(bfcontainer->buf);
 
       /* And release the buffer container */
 
@@ -947,7 +1076,7 @@ static void i2s_configure(struct esp32_i2s_s *priv)
       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);
+                            priv->config->dout_outsig, 0, 0);
     }
 
   /* TODO: repeat above function for RX channel */
@@ -961,7 +1090,7 @@ static void i2s_configure(struct esp32_i2s_s *priv)
           esp32_gpiowrite(priv->config->ws_pin, 1);
           esp32_configgpio(priv->config->ws_pin, INPUT_FUNCTION_3);
           esp32_gpio_matrix_out(priv->config->ws_pin,
-                                  priv->config->ws_out_insig, 0, 0);
+                                priv->config->ws_out_insig, 0, 0);
 
           esp32_gpiowrite(priv->config->bclk_pin, 1);
           esp32_configgpio(priv->config->bclk_pin, INPUT_FUNCTION_3);
@@ -977,12 +1106,12 @@ static void i2s_configure(struct esp32_i2s_s *priv)
           esp32_gpiowrite(priv->config->ws_pin, 1);
           esp32_configgpio(priv->config->ws_pin, INPUT_FUNCTION_3);
           esp32_gpio_matrix_out(priv->config->ws_pin,
-                                  priv->config->ws_in_insig, 0, 0);
+                                priv->config->ws_in_insig, 0, 0);
 
           esp32_gpiowrite(priv->config->bclk_pin, 1);
           esp32_configgpio(priv->config->bclk_pin, INPUT_FUNCTION_3);
           esp32_gpio_matrix_out(priv->config->bclk_pin,
-                                  priv->config->bclk_in_insig, 0, 0);
+                                priv->config->bclk_in_insig, 0, 0);
         }
     }
   else
@@ -1000,22 +1129,22 @@ static void i2s_configure(struct esp32_i2s_s *priv)
 
           esp32_configgpio(priv->config->mclk_pin, OUTPUT_FUNCTION_2);
           esp32_gpio_matrix_out(priv->config->mclk_pin,
-                                  SIG_GPIO_OUT_IDX, 0, 0);
+                                SIG_GPIO_OUT_IDX, 0, 0);
 
           if (priv->config->mclk_pin == 0)
             {
               putreg32(priv->config->is_apll ?
-                        0xfff6 : (is_i2s0 ? 0xfff0 : 0xffff), PIN_CTRL);
+                       0xfff6 : (is_i2s0 ? 0xfff0 : 0xffff), PIN_CTRL);
             }
           else if (priv->config->mclk_pin == 1)
             {
               putreg32(priv->config->is_apll ?
-                        0xf6f6 : (is_i2s0 ? 0xf0f0 : 0xf0ff), PIN_CTRL);
+                       0xf6f6 : (is_i2s0 ? 0xf0f0 : 0xf0ff), PIN_CTRL);
             }
           else
             {
               putreg32(priv->config->is_apll ?
-                        0xff66 : (is_i2s0 ? 0xff00 : 0xff0f), PIN_CTRL);
+                       0xff66 : (is_i2s0 ? 0xff00 : 0xff0f), PIN_CTRL);
             }
         }
 
@@ -1026,12 +1155,12 @@ static void i2s_configure(struct esp32_i2s_s *priv)
           esp32_gpiowrite(priv->config->ws_pin, 1);
           esp32_configgpio(priv->config->ws_pin, OUTPUT_FUNCTION_3);
           esp32_gpio_matrix_out(priv->config->ws_pin,
-                                  priv->config->ws_in_outsig, 0, 0);
+                                priv->config->ws_in_outsig, 0, 0);
 
           esp32_gpiowrite(priv->config->bclk_pin, 1);
           esp32_configgpio(priv->config->bclk_pin, OUTPUT_FUNCTION_3);
           esp32_gpio_matrix_out(priv->config->bclk_pin,
-                                  priv->config->bclk_in_outsig, 0, 0);
+                                priv->config->bclk_in_outsig, 0, 0);
         }
       else
         {
@@ -1081,8 +1210,8 @@ static void i2s_configure(struct esp32_i2s_s *priv)
 
       /* Set TX data width */
 
-      esp32_i2s_txdatawidth((struct i2s_dev_s *)priv,
-                              priv->config->data_width);
+      priv->data_width = priv->config->data_width;
+      i2s_set_datawidth(priv);
 
       /* Set I2S tx chan mode */
 
@@ -1140,156 +1269,71 @@ static void i2s_configure(struct esp32_i2s_s *priv)
                               (priv->config->rate *
                                priv->config->mclk_multiple));
 
-      esp32_i2s_txsamplerate((struct i2s_dev_s *)priv, priv->config->rate);
+      priv->rate = priv->config->rate;
+      i2s_set_clock(priv);
     }
 
   /* TODO: check for rx enabled flag */
 }
 
 /****************************************************************************
- * Name: i2s_tx_channel_start
+ * Name: i2s_set_datawidth
  *
  * Description:
- *   Start TX channel for the I2S port
+ *   Set the I2S TX data width.
  *
  * Input Parameters:
  *   priv - Initialized I2S device structure.
  *
  * Returned Value:
- *   None
+ *   Returns the resulting data width
  *
  ****************************************************************************/
 
-#ifdef I2S_HAVE_TX
-static void i2s_tx_channel_start(struct esp32_i2s_s *priv)
+static uint32_t i2s_set_datawidth(struct esp32_i2s_s *priv)
 {
-  /* 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);
-
-  /* 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);
-
-  /* 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);
-
-  /* Enable DMA interruption */
-
-  up_enable_irq(priv->config->irq);
-
-  modifyreg32(I2S_INT_ENA_REG(priv->config->port), UINT32_MAX,
-              I2S_OUT_EOF_INT_ENA);
-
-  /* Enable DMA operation mode */
-
-  modifyreg32(I2S_FIFO_CONF_REG(priv->config->port), 0, I2S_DSCR_EN);
-
-  /* Unset the DMA outlink */
-
-  putreg32(0, I2S_OUT_LINK_REG(priv->config->port));
-
-  i2sinfo("Started TX channel of port %d\n", priv->config->port);
-}
-#endif /* I2S_HAVE_TX */
-
-/****************************************************************************
- * Name: esp32_i2s_interrupt
- *
- * Description:
- *   Common I2S DMA interrupt handler
- *
- * Input Parameters:
- *   arg - i2s controller private data
- *
- * Returned Value:
- *   Standard interrupt return value.
- *
- ****************************************************************************/
+  modifyreg32(I2S_SAMPLE_RATE_CONF_REG(priv->config->port),
+              I2S_TX_BITS_MOD_M, FIELD_TO_VALUE(I2S_TX_BITS_MOD,
+              priv->data_width));
 
-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;
+  /* Set TX FIFO operation mode */
 
-  uint32_t status = getreg32(I2S_INT_ST_REG(priv->config->port));
+  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));
 
-  putreg32(UINT32_MAX, I2S_INT_CLR_REG(priv->config->port));
+  /* I2S TX MSB right enable */
 
-  if (status & I2S_OUT_EOF_INT_ST)
+  if (priv->data_width <= I2S_DATA_BIT_WIDTH_16BIT)
     {
-      cur = (struct esp32_dmadesc_s *)
-            getreg32(I2S_OUT_EOF_DES_ADDR_REG(priv->config->port));
-
-      /* Schedule completion of the transfer to occur on the worker thread */
-
-      i2s_tx_schedule(priv, cur);
+      modifyreg32(I2S_CONF_REG(priv->config->port), 0, I2S_TX_MSB_RIGHT);
     }
-
-  return 0;
-}
-
-/****************************************************************************
- * Name: esp32_i2s_mclkfrequency
- *
- * Description:
- *   Set the master clock frequency. Usually, the MCLK is a multiple of the
- *   sample rate. Most of the audio codecs require setting specific MCLK
- *   frequency according to the sample rate.
- *
- * Input Parameters:
- *   dev        - Device-specific state data
- *   frequency  - The I2S master clock's frequency
- *
- * Returned Value:
- *   Returns the resulting master clock or a negated errno value on failure.
- *
- ****************************************************************************/
-
-static uint32_t esp32_i2s_mclkfrequency(struct i2s_dev_s *dev,
-                                        uint32_t frequency)
-{
-  struct esp32_i2s_s *priv = (struct esp32_i2s_s *)dev;
-
-  /* Check if the master clock frequency is beyond the highest possible
-   * value and return an error.
-   */
-
-  if (frequency >= (I2S_LL_BASE_CLK / 2))
+  else
     {
-      return -EINVAL;
+      modifyreg32(I2S_CONF_REG(priv->config->port), I2S_TX_MSB_RIGHT, 0);
     }
 
-  priv->mclk_freq = frequency;
-
-  return frequency;
+  return priv->data_width;
 }
 
 /****************************************************************************
- * Name: esp32_i2s_txsamplerate
+ * Name: i2s_set_clock
  *
  * Description:
- *   Set the I2S TX sample rate.  NOTE:  This will have no effect if (1) the
- *   driver does not support an I2S transmitter or if (2) the sample rate is
- *   driven by the I2S frame clock.  This may also have unexpected side-
- *   effects of the TX sample is coupled with the RX sample rate.
+ *   Set the I2S TX sample rate by adjusting I2S clock.
  *
  * Input Parameters:
- *   dev  - Device-specific state data
- *   rate - The I2S sample rate in samples (not bits) per second
+ *   priv - Initialized I2S device structure.
  *
  * Returned Value:
  *   Returns the resulting bitrate
  *
  ****************************************************************************/
 
-static uint32_t esp32_i2s_txsamplerate(struct i2s_dev_s *dev, uint32_t rate)
+static uint32_t i2s_set_clock(struct esp32_i2s_s *priv)
 {
-  struct esp32_i2s_s *priv = (struct esp32_i2s_s *)dev;
+  uint32_t rate;
   uint32_t bclk;
   uint32_t mclk;
   uint16_t bclk_div;
@@ -1319,7 +1363,7 @@ static uint32_t esp32_i2s_txsamplerate(struct i2s_dev_s *dev, uint32_t rate)
 
   if (priv->config->role == I2S_ROLE_MASTER)
     {
-      bclk = rate * priv->config->total_slot * priv->data_width;
+      bclk = priv->rate * priv->config->total_slot * priv->data_width;
       mclk = priv->mclk_freq;
       bclk_div = mclk / bclk;
     }
@@ -1328,7 +1372,7 @@ static uint32_t esp32_i2s_txsamplerate(struct i2s_dev_s *dev, uint32_t rate)
       /* For slave mode, mclk >= bclk * 8, so fix bclk_div to 2 first */
 
       bclk_div = 8;
-      bclk = rate * priv->config->total_slot * priv->data_width;
+      bclk = priv->rate * priv->config->total_slot * priv->data_width;
       mclk = bclk * bclk_div;
     }
 
@@ -1355,7 +1399,7 @@ static uint32_t esp32_i2s_txsamplerate(struct i2s_dev_s *dev, uint32_t rate)
        * 1.0 - 1.0 / (63.0 * 2) = 125.0 / 126.0
        */
 
-      if (decimal > 125.0 / 126.0)
+      if (decimal > 125.0f / 126.0f)
         {
           mclk_div++;
         }
@@ -1409,8 +1453,248 @@ static uint32_t esp32_i2s_txsamplerate(struct i2s_dev_s *dev, uint32_t rate)
                         bclk_div);
   rate = bclk / (float)(priv->config->total_slot * priv->data_width);
 
+  return rate;
+}
+
+/****************************************************************************
+ * Name: i2s_tx_channel_start
+ *
+ * Description:
+ *   Start TX channel for the I2S port
+ *
+ * Input Parameters:
+ *   priv - Initialized I2S device structure.
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+#ifdef I2S_HAVE_TX
+static void i2s_tx_channel_start(struct esp32_i2s_s *priv)
+{
+  if (priv->tx_started)
+    {
+      i2swarn("TX channel of port %d was previously started\n",
+              priv->config->port);
+      return;
+    }
+
+  /* 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);
+
+  /* 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);
+
+  /* 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);
+
+  /* Enable DMA interrupt */
+
+  up_enable_irq(priv->config->irq);
+
+  modifyreg32(I2S_INT_ENA_REG(priv->config->port), UINT32_MAX,
+              I2S_OUT_EOF_INT_ENA);
+
+  /* Enable DMA operation mode */
+
+  modifyreg32(I2S_FIFO_CONF_REG(priv->config->port), 0, I2S_DSCR_EN);
+
+  /* Unset the DMA outlink */
+
+  putreg32(0, I2S_OUT_LINK_REG(priv->config->port));
+
+  priv->tx_started = true;
+
+  i2sinfo("Started TX channel of port %d\n", priv->config->port);
+}
+#endif /* I2S_HAVE_TX */
+
+/****************************************************************************
+ * Name: i2s_tx_channel_stop
+ *
+ * Description:
+ *   Stop TX channel for the I2S port
+ *
+ * Input Parameters:
+ *   priv - Initialized I2S device structure.
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+#ifdef I2S_HAVE_TX
+static void i2s_tx_channel_stop(struct esp32_i2s_s *priv)
+{
+  if (!priv->tx_started)
+    {
+      i2swarn("TX channel of port %d was previously stopped\n",
+              priv->config->port);
+      return;
+    }
+
+  /* Stop TX channel */
+
+  modifyreg32(I2S_CONF_REG(priv->config->port), I2S_TX_START, 0);
+
+  /* Stop outlink */
+
+  modifyreg32(I2S_OUT_LINK_REG(priv->config->port), I2S_OUTLINK_START,
+              I2S_OUTLINK_STOP);
+
+  /* Disable DMA interrupt */
+
+  modifyreg32(I2S_INT_ENA_REG(priv->config->port), UINT32_MAX, 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: esp32_i2s_interrupt
+ *
+ * Description:
+ *   Common I2S DMA interrupt handler
+ *
+ * Input Parameters:
+ *   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));
+
+  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 to occur on the worker thread */
+
+      i2s_tx_schedule(priv, cur);
+    }
+
+  return 0;
+}
+
+/****************************************************************************
+ * Name: esp32_i2s_mclkfrequency
+ *
+ * Description:
+ *   Set the master clock frequency. Usually, the MCLK is a multiple of the
+ *   sample rate. Most of the audio codecs require setting specific MCLK
+ *   frequency according to the sample rate.
+ *
+ * Input Parameters:
+ *   dev        - Device-specific state data
+ *   frequency  - The I2S master clock's frequency
+ *
+ * Returned Value:
+ *   Returns the resulting master clock or a negated errno value on failure.
+ *
+ ****************************************************************************/
+
+static uint32_t esp32_i2s_mclkfrequency(struct i2s_dev_s *dev,
+                                        uint32_t frequency)
+{
+  struct esp32_i2s_s *priv = (struct esp32_i2s_s *)dev;
+
+  /* Check if the master clock frequency is beyond the highest possible
+   * value and return an error.
+   */
+
+  if (frequency >= (I2S_LL_BASE_CLK / 2))
+    {
+      return -EINVAL;
+    }
+
+  priv->mclk_freq = frequency;
+
+  return frequency;
+}
+
+/****************************************************************************
+ * Name: esp32_i2s_txchannels
+ *
+ * Description:
+ *   Set the I2S TX number of channels.
+ *
+ * Input Parameters:
+ *   dev  - Device-specific state data
+ *   channels - The I2S numbers of channels
+ *
+ * Returned Value:
+ *   OK on success; a negated errno value on failure.
+ *
+ ****************************************************************************/
+
+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)
+    {
+      return -EINVAL;
+    }
+
+  priv->channels = channels;
+  return OK;
+}
+
+/****************************************************************************
+ * Name: esp32_i2s_txsamplerate
+ *
+ * Description:
+ *   Set the I2S TX sample rate.  NOTE:  This will have no effect if (1) the
+ *   driver does not support an I2S transmitter or if (2) the sample rate is
+ *   driven by the I2S frame clock.  This may also have unexpected side-
+ *   effects of the TX sample is coupled with the RX sample rate.
+ *
+ * Input Parameters:
+ *   dev  - Device-specific state data
+ *   rate - The I2S sample rate in samples (not bits) per second
+ *
+ * Returned Value:
+ *   Returns the resulting bitrate
+ *
+ ****************************************************************************/
+
+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);
+
   priv->rate = rate;
 
+  rate = i2s_set_clock(priv);
+
+  i2s_tx_channel_start(priv);
+
   return rate;
 }
 
@@ -1426,7 +1710,7 @@ static uint32_t esp32_i2s_txsamplerate(struct i2s_dev_s *dev, uint32_t rate)
  *   width - The I2S data with in bits.
  *
  * Returned Value:
- *   Returns the resulting bitrate
+ *   Returns the resulting data width
  *
  ****************************************************************************/
 
@@ -1434,28 +1718,13 @@ static uint32_t esp32_i2s_txdatawidth(struct i2s_dev_s *dev, int bits)
 {
   struct esp32_i2s_s *priv = (struct esp32_i2s_s *)dev;
 
-  modifyreg32(I2S_SAMPLE_RATE_CONF_REG(priv->config->port),
-              I2S_TX_BITS_MOD_M, FIELD_TO_VALUE(I2S_TX_BITS_MOD, bits));
+  i2s_tx_channel_stop(priv);
 
   priv->data_width = bits;
 
-  /* Set TX FIFO operation mode */
+  i2s_set_datawidth(priv);
 
-  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 */
-
-  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);
-    }
+  i2s_tx_channel_start(priv);
 
   return bits;
 }
@@ -1489,14 +1758,39 @@ static int esp32_i2s_send(struct i2s_dev_s *dev, struct ap_buffer_s *apb,
 {
   struct esp32_i2s_s *priv = (struct esp32_i2s_s *)dev;
   struct esp32_buffer_s *bfcontainer;
-  irqstate_t flags;
   int ret = OK;
+  uint32_t nbytes;
+  uint32_t nsamp;
 
   /* Check audio buffer data size */
 
-  if ((apb->nbytes - apb->curbyte) >
-      (ESP32_DMA_DATALEN_MAX * (I2S_DMADESC_NUM - 1)))
+  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 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 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;
     }
 
@@ -1519,23 +1813,15 @@ static int esp32_i2s_send(struct i2s_dev_s *dev, struct ap_buffer_s *apb,
 
   /* Initialize the buffer container structure */
 
-  bfcontainer->callback = (void *)callback;
+  bfcontainer->callback = callback;
   bfcontainer->timeout  = timeout;
   bfcontainer->arg      = arg;
   bfcontainer->apb      = apb;
+  bfcontainer->nbytes   = nbytes;
   bfcontainer->result   = -EBUSY;
 
-  flags = spin_lock_irqsave(&priv->lock);
-
   ret = i2s_txdma_setup(priv, bfcontainer);
 
-  if (ret != OK)
-    {
-      goto errout_with_buf;
-    }
-
-  ret = i2s_txdma_start(priv);
-
   if (ret != OK)
     {
       goto errout_with_buf;
@@ -1543,11 +1829,8 @@ static int esp32_i2s_send(struct i2s_dev_s *dev, struct ap_buffer_s *apb,
 
   i2sinfo("Queued %d bytes into DMA buffers\n", apb->nbytes);
   i2s_dump_buffer("Audio pipeline buffer:", &apb->samp[apb->curbyte],
-                    apb->nbytes - apb->curbyte);
+                  apb->nbytes - apb->curbyte);
 
-  /* Trigger DMA transfer */
-
-  spin_unlock_irqrestore(&priv->lock, flags);
   i2s_exclsem_give(priv);
 
   return OK;
@@ -1586,7 +1869,7 @@ static int esp32_i2sdma_setup(struct esp32_i2s_s *priv)
 
   priv->cpu = up_cpu_index();
   priv->cpuint = esp32_setup_irq(priv->cpu, priv->config->periph,
-                                  1, ESP32_CPUINT_LEVEL);
+                                 1, ESP32_CPUINT_LEVEL);
   if (priv->cpuint < 0)
     {
       i2serr("Failed to allocate a CPU interrupt.\n");
@@ -1598,8 +1881,8 @@ static int esp32_i2sdma_setup(struct esp32_i2s_s *priv)
     {
       i2serr("Couldn't attach IRQ to handler.\n");
       esp32_teardown_irq(priv->cpu,
-                          priv->config->periph,
-                          priv->cpuint);
+                         priv->config->periph,
+                         priv->cpuint);
       return ret;
     }
 
@@ -1646,6 +1929,8 @@ struct i2s_dev_s *esp32_i2sbus_initialize(int port)
         return NULL;
     }
 
+  priv->tx_started = false;
+
   flags = spin_lock_irqsave(&priv->lock);
 
   nxsem_init(&priv->exclsem, 0, 1);
diff --git a/boards/xtensa/esp32/esp32-devkitc/configs/audio/defconfig b/boards/xtensa/esp32/esp32-devkitc/configs/audio/defconfig
index 0d6fa51d51..d9d634bd01 100644
--- a/boards/xtensa/esp32/esp32-devkitc/configs/audio/defconfig
+++ b/boards/xtensa/esp32/esp32-devkitc/configs/audio/defconfig
@@ -24,6 +24,7 @@ CONFIG_ARCH_STACKDUMP=y
 CONFIG_ARCH_XTENSA=y
 CONFIG_AUDIO=y
 CONFIG_AUDIOUTILS_MMLPARSER_LIB=y
+CONFIG_AUDIO_BUFFER_NUMBYTES=4092
 CONFIG_AUDIO_CS4344=y
 CONFIG_AUDIO_EXCLUDE_BALANCE=y
 CONFIG_AUDIO_EXCLUDE_FFORWARD=y
@@ -31,7 +32,7 @@ CONFIG_AUDIO_EXCLUDE_TONE=y
 CONFIG_AUDIO_EXCLUDE_VOLUME=y
 CONFIG_AUDIO_I2S=y
 CONFIG_AUDIO_I2SCHAR=y
-CONFIG_AUDIO_NUM_BUFFERS=4
+CONFIG_AUDIO_NUM_BUFFERS=6
 CONFIG_BOARDCTL_ROMDISK=y
 CONFIG_BOARD_LOOPSPERMSEC=16717
 CONFIG_BUILTIN=y