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/07 05:46:50 UTC

[incubator-nuttx] branch master updated (60933f5a94 -> 998b726c4b)

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 60933f5a94 stm32_eth: Fix in assertion parameters.
     new d98df37f35 xtensa/esp32s2: add i2s_mclkfrequency to set master clock on I2S
     new 6317f6d597 esp32s2/i2s: use internal buffer to handle multiple audio formats
     new 998b726c4b documentation: update esp32[-s2] documentation about I2S/audio support

The 3 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:
 Documentation/introduction/detailed_support.rst    |   8 +-
 .../xtensa/esp32/boards/esp32-devkitc/index.rst    |  19 +-
 .../esp32s2/boards/esp32s2-saola-1/index.rst       |  16 +-
 arch/xtensa/src/esp32/esp32_i2s.c                  |  16 +-
 arch/xtensa/src/esp32s2/esp32s2_i2s.c              | 623 +++++++++++++++------
 5 files changed, 473 insertions(+), 209 deletions(-)


[incubator-nuttx] 01/03: xtensa/esp32s2: add i2s_mclkfrequency to set master clock on I2S

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 d98df37f3595b4c585a2685b7f39021c2b62bd01
Author: Tiago Medicci Serrano <ti...@espressif.com>
AuthorDate: Wed Oct 26 13:20:34 2022 -0300

    xtensa/esp32s2: add i2s_mclkfrequency to set master clock on I2S
---
 arch/xtensa/src/esp32s2/esp32s2_i2s.c | 54 +++++++++++++++++++++++++++++++----
 1 file changed, 49 insertions(+), 5 deletions(-)

diff --git a/arch/xtensa/src/esp32s2/esp32s2_i2s.c b/arch/xtensa/src/esp32s2/esp32s2_i2s.c
index 50ea4c13a0..24e8753b95 100644
--- a/arch/xtensa/src/esp32s2/esp32s2_i2s.c
+++ b/arch/xtensa/src/esp32s2/esp32s2_i2s.c
@@ -229,6 +229,7 @@ struct esp32s2_i2s_s
   mutex_t           lock;       /* 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 */
 
@@ -288,6 +289,8 @@ static void     i2s_tx_schedule(struct esp32s2_i2s_s *priv,
 
 /* I2S methods (and close friends) */
 
+static uint32_t esp32s2_i2s_mclkfrequency(struct i2s_dev_s *dev,
+                                          uint32_t frequency);
 static uint32_t esp32s2_i2s_txsamplerate(struct i2s_dev_s *dev,
                                          uint32_t rate);
 static uint32_t esp32s2_i2s_txdatawidth(struct i2s_dev_s *dev, int bits);
@@ -302,9 +305,10 @@ static int      esp32s2_i2s_send(struct i2s_dev_s *dev,
 
 static const struct i2s_ops_s g_i2sops =
 {
-  .i2s_txsamplerate = esp32s2_i2s_txsamplerate,
-  .i2s_txdatawidth  = esp32s2_i2s_txdatawidth,
-  .i2s_send         = esp32s2_i2s_send,
+  .i2s_txsamplerate   = esp32s2_i2s_txsamplerate,
+  .i2s_txdatawidth    = esp32s2_i2s_txdatawidth,
+  .i2s_send           = esp32s2_i2s_send,
+  .i2s_mclkfrequency  = esp32s2_i2s_mclkfrequency,
 };
 
 #ifdef CONFIG_ESP32S2_I2S
@@ -319,7 +323,7 @@ static const struct esp32s2_i2s_config_s esp32s2_i2s0_config =
   .data_width       = CONFIG_ESP32S2_I2S_DATA_BIT_WIDTH,
   .rate             = CONFIG_ESP32S2_I2S_SAMPLE_RATE,
   .total_slot       = 2,
-  .mclk_multiple    = I2S_MCLK_MULTIPLE_384,
+  .mclk_multiple    = I2S_MCLK_MULTIPLE_256,
   .tx_en            = I2S_TX_ENABLED,
   .rx_en            = I2S_RX_ENABLED,
 #ifdef CONFIG_ESP32S2_I2S_MCLK
@@ -1016,6 +1020,10 @@ static void i2s_configure(struct esp32s2_i2s_s *priv)
 
       modifyreg32(I2S_FIFO_CONF_REG, 0, I2S_TX_FIFO_MOD_FORCE_EN);
 
+      esp32s2_i2s_mclkfrequency((struct i2s_dev_s *)priv,
+                                (priv->config->rate *
+                                priv->config->mclk_multiple));
+
       esp32s2_i2s_txsamplerate((struct i2s_dev_s *)priv, priv->config->rate);
     }
 
@@ -1107,6 +1115,42 @@ static int esp32s2_i2s_interrupt(int irq, void *context, void *arg)
   return 0;
 }
 
+/****************************************************************************
+ * Name: esp32s2_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 esp32s2_i2s_mclkfrequency(struct i2s_dev_s *dev,
+                                          uint32_t frequency)
+{
+  struct esp32s2_i2s_s *priv = (struct esp32s2_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: esp32s2_i2s_txsamplerate
  *
@@ -1157,7 +1201,7 @@ static uint32_t esp32s2_i2s_txsamplerate(struct i2s_dev_s *dev,
   if (priv->config->role == I2S_ROLE_MASTER)
     {
       bclk = rate * priv->config->total_slot * priv->data_width;
-      mclk = rate * priv->config->mclk_multiple;
+      mclk = priv->mclk_freq;
       bclk_div = mclk / bclk;
     }
   else


[incubator-nuttx] 02/03: esp32s2/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 6317f6d59766795ce218e3da936f7832f65db780
Author: Tiago Medicci Serrano <ti...@espressif.com>
AuthorDate: Thu Nov 3 11:22:27 2022 -0300

    esp32s2/i2s: use internal buffer to handle multiple audio formats
---
 arch/xtensa/src/esp32/esp32_i2s.c     |  16 +-
 arch/xtensa/src/esp32s2/esp32s2_i2s.c | 651 +++++++++++++++++++++++-----------
 2 files changed, 449 insertions(+), 218 deletions(-)

diff --git a/arch/xtensa/src/esp32/esp32_i2s.c b/arch/xtensa/src/esp32/esp32_i2s.c
index 7923e295b9..abb34f0439 100644
--- a/arch/xtensa/src/esp32/esp32_i2s.c
+++ b/arch/xtensa/src/esp32/esp32_i2s.c
@@ -213,10 +213,6 @@ 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
@@ -714,8 +710,8 @@ static int i2s_txdma_start(struct esp32_i2s_s *priv)
  ****************************************************************************/
 
 #ifdef I2S_HAVE_TX
-static int i2s_txdma_setup(struct esp32_i2s_s *priv,
-                           struct esp32_buffer_s *bfcontainer)
+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;
@@ -1316,8 +1312,6 @@ static uint32_t i2s_set_clock(struct esp32_i2s_s *priv)
   uint16_t bclk_div;
   uint32_t sclk;
   uint32_t mclk_div;
-  int ma;
-  int mb;
   int denominator;
   int numerator;
   uint32_t regval;
@@ -1363,8 +1357,6 @@ static uint32_t i2s_set_clock(struct esp32_i2s_s *priv)
 
   freq_diff = abs((int)sclk - (int)(mclk * mclk_div));
 
-  ma = 0;
-  mb = 0;
   denominator = 1;
   numerator = 0;
 
@@ -1387,8 +1379,8 @@ static uint32_t i2s_set_clock(struct esp32_i2s_s *priv)
           for (int a = 2; a <= I2S_LL_MCLK_DIVIDER_MAX; a++)
             {
               int b = (int)(a * (freq_diff / (double)mclk) + 0.5);
-              ma = freq_diff * a;
-              mb = mclk * b;
+              int ma = freq_diff * a;
+              int mb = mclk * b;
               if (ma == mb)
                 {
                   denominator = a;
diff --git a/arch/xtensa/src/esp32s2/esp32s2_i2s.c b/arch/xtensa/src/esp32s2/esp32s2_i2s.c
index 24e8753b95..6260e912e3 100644
--- a/arch/xtensa/src/esp32s2/esp32s2_i2s.c
+++ b/arch/xtensa/src/esp32s2/esp32s2_i2s.c
@@ -93,6 +93,10 @@
 #  define I2S_RX_ENABLED 0
 #endif
 
+#ifndef ALIGN_UP
+#  define ALIGN_UP(num, align) (((num) + ((align) - 1)) & ~((align) - 1))
+#endif
+
 /* Debug ********************************************************************/
 
 #ifdef CONFIG_DEBUG_I2S_INFO
@@ -206,9 +210,23 @@ struct esp32s2_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 bytes_per_sample. Sometimes,
+ * however, the audio buffer is not aligned and additional bytes must
+ * be copied to be inserted on the next buffer. This structure keeps
+ * track of the bytes that were not written to the internal buffer yet.
+ */
+
+struct esp32s2_buffer_carry_s
+{
+  uint32_t value;
+  size_t bytes;
+};
+
 /* This structure describes the state of one receiver or transmitter
  * transport.
  */
@@ -219,6 +237,10 @@ struct esp32s2_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 esp32s2_buffer_carry_s carry;
 };
 
 /* The state of the one I2S peripheral */
@@ -226,10 +248,7 @@ struct esp32s2_transport_s
 struct esp32s2_i2s_s
 {
   struct i2s_dev_s  dev;        /* Externally visible I2S interface */
-  mutex_t           lock;       /* 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 */
+  mutex_t           lock;       /* Ensures mutually exclusive access */
   int               cpuint;     /* I2S interrupt ID */
   uint8_t           cpu;        /* CPU ID */
 
@@ -237,12 +256,15 @@ struct esp32s2_i2s_s
 
   const struct esp32s2_i2s_config_s *config;
 
-#ifdef I2S_HAVE_TX
-  struct esp32s2_transport_s tx;        /* TX transport state */
+  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 */
 
-  /* Stuff var to fill DMA buffer if not word-aligned */
+#ifdef I2S_HAVE_TX
+  struct esp32s2_transport_s tx;  /* TX transport state */
 
-  uint32_t stuff;
+  bool tx_started;              /* TX channel started */
 #endif /* I2S_HAVE_TX */
 
   /* Pre-allocated pool of buffer containers */
@@ -280,24 +302,30 @@ static int      i2s_buf_initialize(struct esp32s2_i2s_s *priv);
 /* DMA support */
 
 #ifdef I2S_HAVE_TX
-static int      i2s_txdma_setup(struct esp32s2_i2s_s *priv,
-                                struct esp32s2_buffer_s *bfcontainer);
-static void     i2s_tx_worker(void *arg);
-static void     i2s_tx_schedule(struct esp32s2_i2s_s *priv,
-                                struct esp32s2_dmadesc_s *outlink);
+static int  i2s_txdma_setup(struct esp32s2_i2s_s *priv,
+                            struct esp32s2_buffer_s *bfcontainer);
+static void i2s_tx_worker(void *arg);
+static void i2s_tx_schedule(struct esp32s2_i2s_s *priv,
+                            struct esp32s2_dmadesc_s *outlink);
 #endif /* I2S_HAVE_TX */
 
 /* I2S methods (and close friends) */
 
-static uint32_t esp32s2_i2s_mclkfrequency(struct i2s_dev_s *dev,
-                                          uint32_t frequency);
-static uint32_t esp32s2_i2s_txsamplerate(struct i2s_dev_s *dev,
-                                         uint32_t rate);
-static uint32_t esp32s2_i2s_txdatawidth(struct i2s_dev_s *dev, int bits);
-static int      esp32s2_i2s_send(struct i2s_dev_s *dev,
-                                 struct ap_buffer_s *apb,
-                                 i2s_callback_t callback, void *arg,
-                                 uint32_t timeout);
+static uint32_t i2s_set_datawidth(struct esp32s2_i2s_s *priv);
+static uint32_t i2s_set_clock(struct esp32s2_i2s_s *priv);
+static void     i2s_tx_channel_start(struct esp32s2_i2s_s *priv);
+static void     i2s_tx_channel_stop(struct esp32s2_i2s_s *priv);
+static int      i2s_txchannels(struct i2s_dev_s *dev,
+                               uint8_t channels);
+static uint32_t i2s_mclkfrequency(struct i2s_dev_s *dev,
+                                  uint32_t frequency);
+static uint32_t i2s_txsamplerate(struct i2s_dev_s *dev,
+                                 uint32_t rate);
+static uint32_t i2s_txdatawidth(struct i2s_dev_s *dev, int bits);
+static int      i2s_send(struct i2s_dev_s *dev,
+                         struct ap_buffer_s *apb,
+                         i2s_callback_t callback, void *arg,
+                         uint32_t timeout);
 
 /****************************************************************************
  * Private Data
@@ -305,10 +333,11 @@ static int      esp32s2_i2s_send(struct i2s_dev_s *dev,
 
 static const struct i2s_ops_s g_i2sops =
 {
-  .i2s_txsamplerate   = esp32s2_i2s_txsamplerate,
-  .i2s_txdatawidth    = esp32s2_i2s_txdatawidth,
-  .i2s_send           = esp32s2_i2s_send,
-  .i2s_mclkfrequency  = esp32s2_i2s_mclkfrequency,
+  .i2s_txchannels     = i2s_txchannels,
+  .i2s_txsamplerate   = i2s_txsamplerate,
+  .i2s_txdatawidth    = i2s_txdatawidth,
+  .i2s_send           = i2s_send,
+  .i2s_mclkfrequency  = i2s_mclkfrequency,
 };
 
 #ifdef CONFIG_ESP32S2_I2S
@@ -472,6 +501,8 @@ static void i2s_buf_free(struct esp32s2_i2s_s *priv,
   flags = enter_critical_section();
 
   bfcontainer->apb = NULL;
+  bfcontainer->buf = NULL;
+  bfcontainer->nbytes = 0;
   bfcontainer->flink  = priv->bf_freelist;
   priv->bf_freelist = bfcontainer;
 
@@ -505,6 +536,9 @@ static int i2s_buf_initialize(struct esp32s2_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);
 
@@ -604,9 +638,14 @@ static int i2s_txdma_setup(struct esp32s2_i2s_s *priv,
 {
   struct ap_buffer_s *apb;
   struct esp32s2_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;
+  irqstate_t flags;
+  int ret = OK;
 
   DEBUGASSERT(bfcontainer && bfcontainer->apb);
 
@@ -615,27 +654,89 @@ static int i2s_txdma_setup(struct esp32s2_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 bytes_per_sample = priv->data_width / 8;
+  samp = &apb->samp[apb->curbyte];
+  samp_size = (apb->nbytes - apb->curbyte) + priv->tx.carry.bytes;
+  carry_size = samp_size % bytes_per_sample;
+
+  /* Allocate the current audio buffer considering the remaining bytes
+   * carried from the last upper half audio buffer.
+   */
+
+  bfcontainer->buf = (uint8_t *)calloc(bfcontainer->nbytes, 1);
+
+  data_copied = 0;
+  buf = bfcontainer->buf;
+
+  /* Copy the remaining bytes from the last audio buffer to the current
+   * audio buffer. The remaining bytes are part of a sample that was split
+   * between the last and the current audio buffer. Also, copy the bytes
+   * from that split sample that are on the current buffer to the internal
+   * buffer.
+   */
+
+  if (priv->tx.carry.bytes)
+    {
+      memcpy(buf, &priv->tx.carry.value, priv->tx.carry.bytes);
+      buf += priv->tx.carry.bytes;
+      data_copied += priv->tx.carry.bytes;
+      memcpy(buf, samp, (bytes_per_sample - priv->tx.carry.bytes));
+      buf += (bytes_per_sample - priv->tx.carry.bytes);
+      samp += (bytes_per_sample - priv->tx.carry.bytes);
+      data_copied += (bytes_per_sample - priv->tx.carry.bytes);
+    }
+
+  /* Copy the upper half buffer to the internal buffer considering that
+   * the current upper half buffer may not contain a complete sample at
+   * the end of the buffer (and those bytes needs to be carried to the
+   * next audio buffer).
+   */
+
+  memcpy(buf, samp, samp_size - (data_copied + carry_size));
+  buf += samp_size - (data_copied + carry_size);
+  samp += samp_size - (data_copied + carry_size);
+  data_copied += samp_size - (data_copied + carry_size);
+
+  /* If the audio buffer's size is not a multiple of the sample size,
+   * it's necessary to carry the remaining bytes that are part of what
+   * would be the last sample on this buffer. These bytes will then be
+   * saved and inserted at the beginning of the next DMA buffer to
+   * rebuild the sample correctly.
+   */
+
+  priv->tx.carry.bytes = carry_size;
+
+  if (priv->tx.carry.bytes)
+    {
+      memcpy(&priv->tx.carry.value, samp, priv->tx.carry.bytes);
+    }
 
   /* Configure DMA stream */
 
-  bytes_queued = esp32s2_dma_init_with_padding(outlink, I2S_DMADESC_NUM,
-                                               (uint8_t *)samp, nbytes,
-                                               &priv->stuff);
+  bytes_queued = esp32s2_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);
+      i2serr("Failed to enqueue I2S buffer "
+             "(%" PRIu32 " bytes of %" PRIu32 ")\n",
+             bytes_queued, bfcontainer->nbytes);
       return bytes_queued;
     }
 
+  flags = enter_critical_section();
+
   /* 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);
+
+  leave_critical_section(flags);
+
+  return ret;
 }
 #endif /* I2S_HAVE_TX */
 
@@ -795,7 +896,11 @@ 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
+      /* Release the internal buffer used by the DMA outlink */
+
+      free(bfcontainer->buf);
+
+      /* Release our reference on the audio buffer. This may very likely
        * cause the audio buffer to be freed.
        */
 
@@ -836,9 +941,9 @@ static void i2s_configure(struct esp32s2_i2s_s *priv)
   if (!(getreg32(I2S_CLKM_CONF_REG) & I2S_CLK_EN))
     {
       i2sinfo("Enabling I2S port clock...\n");
-      modifyreg32(I2S_CLKM_CONF_REG, 0, I2S_CLK_EN);
       modifyreg32(I2S_CLKM_CONF_REG, I2S_CLK_SEL_M,
                   FIELD_TO_VALUE(I2S_CLK_SEL, 2));
+      modifyreg32(I2S_CLKM_CONF_REG, 0, I2S_CLK_EN);
       putreg32(0, I2S_CONF2_REG);
     }
 
@@ -964,14 +1069,14 @@ static void i2s_configure(struct esp32s2_i2s_s *priv)
           modifyreg32(I2S_CONF_REG, I2S_TX_SLAVE_MOD, 0);
         }
 
-      /* Congfigure TX chan bit, audio data bit and mono mode.
-       * On ESP32S2, sample_bit should equals to data_bit
+      /* Configure TX chan bit, audio data bit and mono mode.
+       * On ESP32-S2, sample_bit should equals to data_bit
        */
 
       /* Set TX data width */
 
-      esp32s2_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 */
 
@@ -1020,16 +1125,179 @@ static void i2s_configure(struct esp32s2_i2s_s *priv)
 
       modifyreg32(I2S_FIFO_CONF_REG, 0, I2S_TX_FIFO_MOD_FORCE_EN);
 
-      esp32s2_i2s_mclkfrequency((struct i2s_dev_s *)priv,
+      i2s_mclkfrequency((struct i2s_dev_s *)priv,
                                 (priv->config->rate *
                                 priv->config->mclk_multiple));
 
-      esp32s2_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_set_datawidth
+ *
+ * Description:
+ *   Set the I2S TX data width.
+ *
+ * Input Parameters:
+ *   priv - Initialized I2S device structure.
+ *
+ * Returned Value:
+ *   Returns the resulting data width
+ *
+ ****************************************************************************/
+
+static uint32_t i2s_set_datawidth(struct esp32s2_i2s_s *priv)
+{
+  modifyreg32(I2S_SAMPLE_RATE_CONF_REG, I2S_TX_BITS_MOD_M,
+              FIELD_TO_VALUE(I2S_TX_BITS_MOD, priv->data_width));
+
+  /* Set TX FIFO operation mode */
+
+  modifyreg32(I2S_FIFO_CONF_REG, 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 */
+
+  modifyreg32(I2S_CONF_REG, 0, I2S_TX_MSB_RIGHT);
+
+  return priv->data_width;
+}
+
+/****************************************************************************
+ * Name: i2s_set_clock
+ *
+ * Description:
+ *   Set the I2S TX sample rate by adjusting I2S clock.
+ *
+ * Input Parameters:
+ *   priv - Initialized I2S device structure.
+ *
+ * Returned Value:
+ *   Returns the resulting bitrate
+ *
+ ****************************************************************************/
+
+static uint32_t i2s_set_clock(struct esp32s2_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;
+
+  /* TODO: provide APLL clock support */
+
+  sclk = I2S_LL_BASE_CLK;
+
+  /* fmclk = bck_div * fbclk = fsclk / (mclk_div + b / a)
+   * mclk_div is the I2S clock divider's integral value
+   * b is the fraction clock divider's numerator value
+   * a is the fraction clock divider's denominator value
+   */
+
+  if (priv->config->role == I2S_ROLE_MASTER)
+    {
+      bclk = priv->rate * priv->config->total_slot * priv->data_width;
+      mclk = priv->mclk_freq;
+      bclk_div = mclk / bclk;
+    }
+  else
+    {
+      /* For slave mode, mclk >= bclk * 8, so fix bclk_div to 2 first */
+
+      bclk_div = 8;
+      bclk = priv->rate * priv->config->total_slot * priv->data_width;
+      mclk = bclk * bclk_div;
+    }
+
+  /* Calculate the nearest integer value of the I2S clock divider */
+
+  mclk_div = sclk / mclk;
+
+  i2sinfo("Clock division info: [sclk]%" PRIu32 " Hz [mdiv] %d "
+          "[mclk] %" PRIu32 " Hz [bdiv] %d [bclk] %" PRIu32 " Hz\n",
+          sclk, mclk_div, mclk, bclk_div, bclk);
+
+  freq_diff = abs((int)sclk - (int)(mclk * mclk_div));
+
+  denominator = 1;
+  numerator = 0;
+
+  if (freq_diff)
+    {
+      float decimal = freq_diff / (float)mclk;
+
+      /* Carry bit if the decimal is greater than
+       * 1.0 - 1.0 / (63.0 * 2) = 125.0 / 126.0
+       */
+
+      if (decimal > 125.0f / 126.0f)
+        {
+          mclk_div++;
+        }
+      else
+        {
+          uint32_t min = UINT32_MAX;
+
+          for (int a = 2; a <= I2S_LL_MCLK_DIVIDER_MAX; a++)
+            {
+              int b = (int)(a * (freq_diff / (double)mclk) + 0.5);
+              int ma = freq_diff * a;
+              int mb = mclk * b;
+              if (ma == mb)
+                {
+                  denominator = a;
+                  numerator = b;
+                  break;
+                }
+
+              if (abs((mb - ma)) < min)
+                {
+                  denominator = a;
+                  numerator = b;
+                  min = abs(mb - ma);
+                }
+            }
+        }
+    }
+
+  i2sinfo("Clock register: [mclk] %" PRIu32 " Hz [numerator] %d "
+          "[denominator] %d\n", mclk, numerator, denominator);
+
+  regval = getreg32(I2S_CLKM_CONF_REG);
+  regval &= ~I2S_CLKM_DIV_NUM_M;
+  regval |= FIELD_TO_VALUE(I2S_CLKM_DIV_NUM, mclk_div);
+  regval &= ~I2S_CLKM_DIV_B_M;
+  regval |= FIELD_TO_VALUE(I2S_CLKM_DIV_B, numerator);
+  regval &= ~I2S_CLKM_DIV_A_M;
+  regval |= FIELD_TO_VALUE(I2S_CLKM_DIV_A, denominator);
+  putreg32(regval, I2S_CLKM_CONF_REG);
+
+  /* Set I2S tx bck div num */
+
+  modifyreg32(I2S_SAMPLE_RATE_CONF_REG, I2S_TX_BCK_DIV_NUM_M,
+              FIELD_TO_VALUE(I2S_TX_BCK_DIV_NUM, bclk_div));
+
+  /* Returns the actual sample rate */
+
+  bclk = sclk / (float)((mclk_div + numerator / (float)denominator) *
+                        bclk_div);
+  rate = bclk / (float)(priv->config->total_slot * priv->data_width);
+
+  return rate;
+}
+
 /****************************************************************************
  * Name: i2s_tx_channel_start
  *
@@ -1047,6 +1315,12 @@ static void i2s_configure(struct esp32s2_i2s_s *priv)
 #ifdef I2S_HAVE_TX
 static void i2s_tx_channel_start(struct esp32s2_i2s_s *priv)
 {
+  if (priv->tx_started)
+    {
+      i2swarn("TX channel was previously started\n");
+      return;
+    }
+
   /* Reset the TX channel */
 
   modifyreg32(I2S_CONF_REG, 0, I2S_TX_RESET);
@@ -1055,14 +1329,18 @@ static void i2s_tx_channel_start(struct esp32s2_i2s_s *priv)
   /* Reset the DMA operation */
 
   modifyreg32(I2S_LC_CONF_REG, 0, I2S_OUT_RST);
+  modifyreg32(I2S_LC_CONF_REG, 0, I2S_AHBM_FIFO_RST);
+  modifyreg32(I2S_LC_CONF_REG, 0, I2S_AHBM_RST);
   modifyreg32(I2S_LC_CONF_REG, I2S_OUT_RST, 0);
+  modifyreg32(I2S_LC_CONF_REG, I2S_AHBM_FIFO_RST, 0);
+  modifyreg32(I2S_LC_CONF_REG, I2S_AHBM_RST, 0);
 
   /* Reset TX FIFO */
 
   modifyreg32(I2S_CONF_REG, 0, I2S_TX_FIFO_RESET);
   modifyreg32(I2S_CONF_REG, I2S_TX_FIFO_RESET, 0);
 
-  /* Enable DMA interruption */
+  /* Enable DMA interrupt */
 
   up_enable_irq(priv->config->irq);
 
@@ -1076,12 +1354,61 @@ static void i2s_tx_channel_start(struct esp32s2_i2s_s *priv)
 
   putreg32(0, I2S_OUT_LINK_REG);
 
+  priv->tx_started = true;
+
   i2sinfo("Started TX channel on I2S0\n");
 }
 #endif /* I2S_HAVE_TX */
 
 /****************************************************************************
- * Name: esp32s2_i2s_interrupt
+ * 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 esp32s2_i2s_s *priv)
+{
+  if (!priv->tx_started)
+    {
+      i2swarn("TX channel was previously stopped\n");
+      return;
+    }
+
+  /* Stop TX channel */
+
+  modifyreg32(I2S_CONF_REG, I2S_TX_START, 0);
+
+  /* Stop outlink */
+
+  modifyreg32(I2S_OUT_LINK_REG, I2S_OUTLINK_START, I2S_OUTLINK_STOP);
+
+  /* Disable DMA interrupt */
+
+  modifyreg32(I2S_INT_ENA_REG, UINT32_MAX, 0);
+
+  /* Disable DMA operation mode */
+
+  modifyreg32(I2S_FIFO_CONF_REG, I2S_DSCR_EN, 0);
+
+  up_disable_irq(priv->config->irq);
+
+  priv->tx_started = false;
+
+  i2sinfo("Stopped TX channel on I2S0\n");
+}
+#endif /* I2S_HAVE_TX */
+
+/****************************************************************************
+ * Name: i2s_interrupt
  *
  * Description:
  *   Common I2S DMA interrupt handler
@@ -1094,7 +1421,7 @@ static void i2s_tx_channel_start(struct esp32s2_i2s_s *priv)
  *
  ****************************************************************************/
 
-static int esp32s2_i2s_interrupt(int irq, void *context, void *arg)
+static int i2s_interrupt(int irq, void *context, void *arg)
 {
   struct esp32s2_i2s_s *priv = (struct esp32s2_i2s_s *)arg;
   struct esp32s2_dmadesc_s *cur = NULL;
@@ -1116,7 +1443,7 @@ static int esp32s2_i2s_interrupt(int irq, void *context, void *arg)
 }
 
 /****************************************************************************
- * Name: esp32s2_i2s_mclkfrequency
+ * Name: i2s_mclkfrequency
  *
  * Description:
  *   Set the master clock frequency. Usually, the MCLK is a multiple of the
@@ -1132,8 +1459,7 @@ static int esp32s2_i2s_interrupt(int irq, void *context, void *arg)
  *
  ****************************************************************************/
 
-static uint32_t esp32s2_i2s_mclkfrequency(struct i2s_dev_s *dev,
-                                          uint32_t frequency)
+static uint32_t i2s_mclkfrequency(struct i2s_dev_s *dev, uint32_t frequency)
 {
   struct esp32s2_i2s_s *priv = (struct esp32s2_i2s_s *)dev;
 
@@ -1152,148 +1478,76 @@ static uint32_t esp32s2_i2s_mclkfrequency(struct i2s_dev_s *dev,
 }
 
 /****************************************************************************
- * Name: esp32s2_i2s_txsamplerate
+ * Name: i2s_txchannels
  *
  * 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 number of channels.
  *
  * Input Parameters:
  *   dev  - Device-specific state data
- *   rate - The I2S sample rate in samples (not bits) per second
+ *   channels - The I2S numbers of channels
  *
  * Returned Value:
- *   Returns the resulting bitrate
+ *   OK on success; a negated errno value on failure.
  *
  ****************************************************************************/
 
-static uint32_t esp32s2_i2s_txsamplerate(struct i2s_dev_s *dev,
-                                         uint32_t rate)
+static int i2s_txchannels(struct i2s_dev_s *dev, uint8_t channels)
 {
   struct esp32s2_i2s_s *priv = (struct esp32s2_i2s_s *)dev;
-  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;
-
-  /* TODO: provide APLL clock support */
-
-  /* Disable APLL clock, I2S module will using PLL_D2_CLK(160M) as source
-   * clock.
-   */
-
-  modifyreg32(I2S_CLKM_CONF_REG, I2S_CLK_EN, 0);
-  sclk = I2S_LL_BASE_CLK;
-
-  /* fmclk = bck_div * fbclk = fsclk / (mclk_div + b / a)
-   * mclk_div is the I2S clock divider's integral value
-   * b is the fraction clock divider's numerator value
-   * a is the fraction clock divider's denominator value
-   */
 
-  if (priv->config->role == I2S_ROLE_MASTER)
-    {
-      bclk = rate * priv->config->total_slot * priv->data_width;
-      mclk = priv->mclk_freq;
-      bclk_div = mclk / bclk;
-    }
-  else
+  if (channels != 1 && channels != 2)
     {
-      /* 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;
-      mclk = bclk * bclk_div;
+      return -EINVAL;
     }
 
-  /* Calculate the nearest integer value of the I2S clock divider */
-
-  mclk_div = sclk / mclk;
-
-  i2sinfo("Clock division info: [sclk]%" PRIu32 " Hz [mdiv] %d "
-          "[mclk] %" PRIu32 " Hz [bdiv] %d [bclk] %" PRIu32 " Hz\n",
-          sclk, mclk_div, mclk, bclk_div, bclk);
-
-  freq_diff = abs((int)sclk - (int)(mclk * mclk_div));
-
-  denominator = 1;
-  numerator = 0;
-
-  if (freq_diff)
-    {
-      float decimal = freq_diff / (float)mclk;
-
-      /* Carry bit if the decimal is greater than
-       * 1.0 - 1.0 / (63.0 * 2) = 125.0 / 126.0
-       */
+  i2s_tx_channel_stop(priv);
 
-      if (decimal > 125.0f / 126.0f)
-        {
-          mclk_div++;
-        }
-      else
-        {
-          uint32_t min = UINT32_MAX;
+  priv->channels = channels;
 
-          for (int a = 2; a <= I2S_LL_MCLK_DIVIDER_MAX; a++)
-            {
-              int b = (int)(a * (freq_diff / (double)mclk) + 0.5);
-              int ma = freq_diff * a;
-              int mb = mclk * b;
-              if (ma == mb)
-                {
-                  denominator = a;
-                  numerator = b;
-                  break;
-                }
+  modifyreg32(I2S_CONF_REG, priv->channels == 1 ? 0 : I2S_TX_DMA_EQUAL,
+              priv->channels == 1 ? I2S_TX_DMA_EQUAL : 0);
 
-              if (abs((mb - ma)) < min)
-                {
-                  denominator = a;
-                  numerator = b;
-                  min = abs(mb - ma);
-                }
-            }
-        }
-    }
+  i2s_tx_channel_start(priv);
 
-  i2sinfo("Clock register: [mclk] %" PRIu32 " Hz [numerator] %d "
-          "[denominator] %d\n", mclk, numerator, denominator);
+  return OK;
+}
 
-  regval = getreg32(I2S_CLKM_CONF_REG);
-  regval &= ~I2S_CLKM_DIV_NUM_M;
-  regval |= FIELD_TO_VALUE(I2S_CLKM_DIV_NUM, mclk_div);
-  regval &= ~I2S_CLKM_DIV_B_M;
-  regval |= FIELD_TO_VALUE(I2S_CLKM_DIV_B, numerator);
-  regval &= ~I2S_CLKM_DIV_A_M;
-  regval |= FIELD_TO_VALUE(I2S_CLKM_DIV_A, denominator);
-  putreg32(regval, I2S_CLKM_CONF_REG);
+/****************************************************************************
+ * Name: i2s_txsamplerate
+ *
+ * Description:
+ *   Set the I2S TX sample rate.  NOTE:  This will have no effect if (1) the
+ *   driver does not support an I2S transmitter or if (2) the sample rate is
+ *   driven by the I2S frame clock.  This may also have unexpected side-
+ *   effects of the TX sample is coupled with the RX sample rate.
+ *
+ * Input Parameters:
+ *   dev  - Device-specific state data
+ *   rate - The I2S sample rate in samples (not bits) per second
+ *
+ * Returned Value:
+ *   Returns the resulting bitrate
+ *
+ ****************************************************************************/
 
-  /* Set I2S tx bck div num */
+static uint32_t i2s_txsamplerate(struct i2s_dev_s *dev, uint32_t rate)
+{
+  struct esp32s2_i2s_s *priv = (struct esp32s2_i2s_s *)dev;
 
-  modifyreg32(I2S_SAMPLE_RATE_CONF_REG, I2S_TX_BCK_DIV_NUM_M,
-              FIELD_TO_VALUE(I2S_TX_BCK_DIV_NUM, bclk_div));
+  i2s_tx_channel_stop(priv);
 
-  /* Returns the actual sample rate */
+  priv->rate = rate;
 
-  bclk = sclk / (float)((mclk_div + numerator / (float)denominator) *
-                        bclk_div);
-  rate = bclk / (float)(priv->config->total_slot * priv->data_width);
+  rate = i2s_set_clock(priv);
 
-  priv->rate = rate;
+  i2s_tx_channel_start(priv);
 
   return rate;
 }
 
 /****************************************************************************
- * Name: esp32s2_i2s_txdatawidth
+ * Name: i2s_txdatawidth
  *
  * Description:
  *   Set the I2S TX data width.  The TX bitrate is determined by
@@ -1304,42 +1558,27 @@ static uint32_t esp32s2_i2s_txsamplerate(struct i2s_dev_s *dev,
  *   width - The I2S data with in bits.
  *
  * Returned Value:
- *   Returns the resulting bitrate
+ *   Returns the resulting data width
  *
  ****************************************************************************/
 
-static uint32_t esp32s2_i2s_txdatawidth(struct i2s_dev_s *dev, int bits)
+static uint32_t i2s_txdatawidth(struct i2s_dev_s *dev, int bits)
 {
   struct esp32s2_i2s_s *priv = (struct esp32s2_i2s_s *)dev;
 
-  modifyreg32(I2S_SAMPLE_RATE_CONF_REG, 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 */
-
-  modifyreg32(I2S_FIFO_CONF_REG, 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_set_datawidth(priv);
 
-  if (priv->data_width <= I2S_DATA_BIT_WIDTH_16BIT)
-    {
-      modifyreg32(I2S_CONF_REG, 0, I2S_TX_MSB_RIGHT);
-    }
-  else
-    {
-      modifyreg32(I2S_CONF_REG, I2S_TX_MSB_RIGHT, 0);
-    }
+  i2s_tx_channel_start(priv);
 
   return bits;
 }
 
 /****************************************************************************
- * Name: esp32s2_i2s_send
+ * Name: i2s_send
  *
  * Description:
  *   Send a block of data on I2S.
@@ -1361,20 +1600,29 @@ static uint32_t esp32s2_i2s_txdatawidth(struct i2s_dev_s *dev, int bits)
  *
  ****************************************************************************/
 
-static int esp32s2_i2s_send(struct i2s_dev_s *dev, struct ap_buffer_s *apb,
-                            i2s_callback_t callback, void *arg,
-                            uint32_t timeout)
+static int i2s_send(struct i2s_dev_s *dev, struct ap_buffer_s *apb,
+                    i2s_callback_t callback, void *arg, uint32_t timeout)
 {
   struct esp32s2_i2s_s *priv = (struct esp32s2_i2s_s *)dev;
   struct esp32s2_buffer_s *bfcontainer;
-  irqstate_t flags;
   int ret = OK;
+  uint32_t nbytes;
+
+  /* Check audio buffer data size from the upper half. If the buffer
+   * size is not a multiple of the data width, the remaining bytes
+   * must be sent along with the next audio buffer.
+   */
+
+  nbytes = (apb->nbytes - apb->curbyte) + priv->tx.carry.bytes;
 
-  /* Check audio buffer data size */
+  nbytes -= (nbytes % (priv->data_width / 8));
 
-  if ((apb->nbytes - apb->curbyte) >
-      (ESP32S2_DMA_DATALEN_MAX * (I2S_DMADESC_NUM - 1)))
+  if (nbytes > (ESP32S2_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 - (ESP32S2_DMA_DATALEN_MAX * I2S_DMADESC_NUM));
       return -EFBIG;
     }
 
@@ -1401,19 +1649,11 @@ static int esp32s2_i2s_send(struct i2s_dev_s *dev, struct ap_buffer_s *apb,
   bfcontainer->timeout  = timeout;
   bfcontainer->arg      = arg;
   bfcontainer->apb      = apb;
+  bfcontainer->nbytes   = nbytes;
   bfcontainer->result   = -EBUSY;
 
-  flags = enter_critical_section();
-
   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;
@@ -1423,9 +1663,6 @@ static int esp32s2_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);
 
-  /* Trigger DMA transfer */
-
-  leave_critical_section(flags);
   nxmutex_unlock(&priv->lock);
 
   return OK;
@@ -1437,7 +1674,7 @@ errout_with_buf:
 }
 
 /****************************************************************************
- * Name: esp32s2_i2sdma_setup
+ * Name: i2sdma_setup
  *
  * Description:
  *   Configure the DMA for the I2S peripheral
@@ -1452,7 +1689,7 @@ errout_with_buf:
  *
  ****************************************************************************/
 
-static int esp32s2_i2sdma_setup(struct esp32s2_i2s_s *priv)
+static int i2sdma_setup(struct esp32s2_i2s_s *priv)
 {
   int ret;
 
@@ -1471,7 +1708,7 @@ static int esp32s2_i2sdma_setup(struct esp32s2_i2s_s *priv)
       return priv->cpuint;
     }
 
-  ret = irq_attach(priv->config->irq, esp32s2_i2s_interrupt, priv);
+  ret = irq_attach(priv->config->irq, i2s_interrupt, priv);
   if (ret != OK)
     {
       i2serr("Couldn't attach IRQ to handler.\n");
@@ -1506,6 +1743,8 @@ struct i2s_dev_s *esp32s2_i2sbus_initialize(void)
 
   priv = &esp32s2_i2s0_priv;
 
+  priv->tx_started = false;
+
   flags = enter_critical_section();
 
   nxmutex_init(&priv->lock);
@@ -1520,7 +1759,7 @@ struct i2s_dev_s *esp32s2_i2sbus_initialize(void)
       goto err;
     }
 
-  ret = esp32s2_i2sdma_setup(priv);
+  ret = i2sdma_setup(priv);
   if (ret < 0)
     {
       goto err;


[incubator-nuttx] 03/03: documentation: update esp32[-s2] documentation about I2S/audio support

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 998b726c4b6740381fc692ae21958cd209d6794d
Author: Tiago Medicci Serrano <ti...@espressif.com>
AuthorDate: Sun Nov 6 12:38:04 2022 -0300

    documentation: update esp32[-s2] documentation about I2S/audio support
---
 Documentation/introduction/detailed_support.rst       |  8 ++++----
 .../xtensa/esp32/boards/esp32-devkitc/index.rst       | 19 ++++++++-----------
 .../xtensa/esp32s2/boards/esp32s2-saola-1/index.rst   | 16 ++++------------
 3 files changed, 16 insertions(+), 27 deletions(-)

diff --git a/Documentation/introduction/detailed_support.rst b/Documentation/introduction/detailed_support.rst
index b71dedd64a..53162a744d 100644
--- a/Documentation/introduction/detailed_support.rst
+++ b/Documentation/introduction/detailed_support.rst
@@ -3052,10 +3052,10 @@ Please, refer to the :doc:`ESP32 </platforms/xtensa/esp32/index>` on NuttX for
 further information.
 
 ESP32-S2 (Single Xtensa LX7)
-=======================
+============================
 
 Xtensa LX7 ESP32-S2
-----------------
+-------------------
 
 Initial architectural support for Xtensa LX7 processor for the Espressif
 ESP32-S2 was added in NuttX-10.2.
@@ -3067,10 +3067,10 @@ Please, refer to the :doc:`ESP32-S2 </platforms/xtensa/esp32s2/index>` on NuttX
 further information.
 
 ESP32-S3 (Dual Xtensa LX7)
-=======================
+==========================
 
 Xtensa LX7 ESP32-S3
-----------------
+-------------------
 
 Initial architectural support for dual Xtensa LX7 processors for the Espressif
 ESP32-S3 was added in NuttX-10.3.
diff --git a/Documentation/platforms/xtensa/esp32/boards/esp32-devkitc/index.rst b/Documentation/platforms/xtensa/esp32/boards/esp32-devkitc/index.rst
index ef589cfcb2..0016fbe37c 100644
--- a/Documentation/platforms/xtensa/esp32/boards/esp32-devkitc/index.rst
+++ b/Documentation/platforms/xtensa/esp32/boards/esp32-devkitc/index.rst
@@ -115,16 +115,13 @@ driver to bypass audio systems and write directly to the I2S peripheral.
 
 .. note:: The I2S peripheral is able to work on two functional modes
   internally: 16 and 32-bit width.
-  That limits using the I2S peripheral to play audio files other than 16/32
-  bit-widths as the internal buffer allocated for the audio content does not
-  consider the operation modes of the peripheral. This limitation is planned
-  to be removed soon by copying the buffers internally and making the
-  necessary adjustments.
+  ESP32's I2S driver, however, uses an internal buffer to enable inserting
+  padding bytes and provide the ability to play 8, 16, 24 or 32-bits/sample
+  audio files. Sample rate and data width are automatically set by the upper
+  half audio driver.
 
-.. note:: The above statement is not valid when using the I2S character
-  device driver.
-  It's possible to use 8, 16, 24, and 32-bit-widths writing directly to the
-  I2S character device. Just make sure to set the bit-width::
+.. note:: Also, it's possible to use 8, 16, 24, and 32-bit-widths writing
+  directly to the I2S character device. Just make sure to set the bit-width::
 
     $ make menuconfig
     -> System Type
@@ -173,8 +170,8 @@ ESP32 Pin  CS4344 Pin Description
 
 **Simple HTTP server**
 
-Prepare a PCM-encoded (`.wav`) audio file with 16 bits/sample (sampled at
-8~48kHz). This file must be placed into a folder in a computer that could
+Prepare a PCM-encoded (`.wav`) audio file with 16 or 24 bits/sample (sampled at
+16~48kHz). This file must be placed into a folder in a computer that could
 be accessed on the same Wi-Fi network the ESP32 will be connecting to.
 
 Python provides a simple HTTP server. `cd` to the audio file folder on the
diff --git a/Documentation/platforms/xtensa/esp32s2/boards/esp32s2-saola-1/index.rst b/Documentation/platforms/xtensa/esp32s2/boards/esp32s2-saola-1/index.rst
index b43c4b808c..d9287f538f 100644
--- a/Documentation/platforms/xtensa/esp32s2/boards/esp32s2-saola-1/index.rst
+++ b/Documentation/platforms/xtensa/esp32s2/boards/esp32s2-saola-1/index.rst
@@ -66,13 +66,8 @@ driver or a specific audio codec driver
 available at the moment). Also, it's possible to use the I2S character device
 driver to bypass audio systems and write directly to the I2S peripheral.
 
-.. note:: The I2S peripheral is able to work on two functional modes
-  internally: 16 and 32-bit width.
-  That limits using the I2S peripheral to play audio files other than 16/32
-  bit-widths as the internal buffer allocated for the audio content does not
-  consider the operation modes of the peripheral. This limitation is planned
-  to be removed soon by copying the buffers internally and making the
-  necessary adjustments.
+.. note:: When using the audio system, sample rate and data width are
+  automatically set by the upper half audio driver.
 
 .. note:: The above statement is not valid when using the I2S character
   device driver.
@@ -86,9 +81,6 @@ driver to bypass audio systems and write directly to the I2S peripheral.
                 -> I2S0/1
                     -> Bit Witdh
 
-  And make sure the data stream buffer being written to the I2S peripheral is
-  aligned to the next boundary i.e. 16 bits for the 8 and 16-bit-widths and
-  32 bits for 24 and 32-bit-widths.
 
 Configurations
 ==============
@@ -99,7 +91,7 @@ audio
 This configuration uses the I2S0 peripheral and an externally connected audio
 codec to play an audio file. The easiest way of playing an uncompressed file
 is embedding into the firmware. This configuration selects
-`romfs example <https://github.com/apache/incubator-nuttx-apps/tree/master/examples/romfs>`_`
+`romfs example <https://github.com/apache/incubator-nuttx-apps/tree/master/examples/romfs>`__
 to allow that.
 
 **Audio Codec Setup**
@@ -122,7 +114,7 @@ Prepare and build the `audio` defconfig::
   $ make -j distclean && ./tools/configure.sh esp32s2-saola-1:audio && make
 
 This will create a temporary folder in `apps/examples/romfs/testdir`. Move
-a PCM-encoded (`.wav`) audio file with 16 bits/sample (sampled at 8~48kHz)
+a PCM-encoded (`.wav`) audio file with 16 or 24 bits/sample (sampled at 16~48kHz)
 to this folder.
 
 .. note:: You can use :download:`this 440 Hz sinusoidal tone <tone.wav>`.