You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@nuttx.apache.org by ac...@apache.org on 2022/11/15 20:01:55 UTC
[incubator-nuttx] branch master updated (97930ab110 -> 9b55168462)
This is an automated email from the ASF dual-hosted git repository.
acassis pushed a change to branch master
in repository https://gitbox.apache.org/repos/asf/incubator-nuttx.git
from 97930ab110 imxrt:Fix Case ENET_MMFR_OP_RdNOTMII->ENET_MMFR_OP_RDNOTMII
new 7ae4152f47 esp32[-s2]/i2s: fix gpio setting when slave mode is selected
new 3b5ab27893 esp32/i2s: implement I2S receiver module
new aa208bd52c esp32/i2s: remove "esp32_" prefix from private functions
new 9b55168462 documentation: update ESP32's documentation about the I2S RX mode
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:
.../xtensa/esp32/boards/esp32-devkitc/index.rst | 105 +-
arch/xtensa/src/esp32/Kconfig | 17 +-
arch/xtensa/src/esp32/esp32_i2s.c | 1663 ++++++++++++++++----
arch/xtensa/src/esp32/esp32_i2s.h | 9 +-
arch/xtensa/src/esp32s2/esp32s2_i2s.c | 18 +-
.../xtensa/esp32/common/src/esp32_board_i2sdev.c | 89 +-
.../esp32/esp32-devkitc/configs/i2schar/defconfig | 8 +-
.../configs/{audio => nxlooper}/defconfig | 16 +-
.../xtensa/esp32/esp32-devkitc/src/esp32-devkitc.h | 6 +-
.../xtensa/esp32/esp32-devkitc/src/esp32_bringup.c | 37 +-
10 files changed, 1611 insertions(+), 357 deletions(-)
copy boards/xtensa/esp32/esp32-devkitc/configs/{audio => nxlooper}/defconfig (91%)
[incubator-nuttx] 04/04: documentation: update ESP32's documentation about the I2S RX mode
Posted by ac...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
acassis pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/incubator-nuttx.git
commit 9b5516846282d2f11dce65c12d22473255668fff
Author: Tiago Medicci Serrano <ti...@espressif.com>
AuthorDate: Fri Nov 11 14:21:04 2022 -0300
documentation: update ESP32's documentation about the I2S RX mode
---
.../xtensa/esp32/boards/esp32-devkitc/index.rst | 105 +++++++++++++++++++--
1 file changed, 95 insertions(+), 10 deletions(-)
diff --git a/Documentation/platforms/xtensa/esp32/boards/esp32-devkitc/index.rst b/Documentation/platforms/xtensa/esp32/boards/esp32-devkitc/index.rst
index 0016fbe37c..5a186f8650 100644
--- a/Documentation/platforms/xtensa/esp32/boards/esp32-devkitc/index.rst
+++ b/Documentation/platforms/xtensa/esp32/boards/esp32-devkitc/index.rst
@@ -110,8 +110,10 @@ I2S
ESP32 has two I2S peripherals accessible using either the generic I2S audio
driver or a specific audio codec driver
(`CS4344 <https://www.cirrus.com/products/cs4344-45-48/>`__ bindings are
-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.
+available at the moment). The generic I2S audio driver enables using both
+the receiver module (RX) and the transmitter module (TX) without using any
+specific codec. Also, it's possible to use the I2S character device driver
+to bypass the audio subsystem 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.
@@ -134,6 +136,11 @@ driver to bypass audio systems and write directly to the I2S peripheral.
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.
+The following configurations use the I2S peripheral::
+ * :ref:`platforms/xtensa/esp32/boards/esp32-devkitc/index:audio`
+ * :ref:`platforms/xtensa/esp32/boards/esp32-devkitc/index:i2schar`
+ * :ref:`platforms/xtensa/esp32/boards/esp32-devkitc/index:nxlooper`
+
Pin Mapping
===========
@@ -189,15 +196,15 @@ browser:
After successfully built and flashed, connect the board to the Wi-Fi network::
- $ nsh> wapi psk wlan0 mypasswd 1
- $ nsh> wapi essid wlan0 myssid 1
- $ nsh> renew wlan0
+ nsh> wapi psk wlan0 mypasswd 1
+ nsh> wapi essid wlan0 myssid 1
+ nsh> renew wlan0
Once connected, open NuttX's player and play the file according to its file
name and the IP address of the HTTP server::
- $ nsh> nxplayer
- $ nxplayer> play http://192.168.1.239:8000/tones.wav
+ nsh> nxplayer
+ nxplayer> play http://192.168.1.239:8000/tones.wav
efuse
-----
@@ -208,12 +215,35 @@ i2schar
-------
This configuration enables the I2S character device and the i2schar example
-app, which provides an easy-to-use way of testing the I2S peripheral (I2S0
-on this example).
+app, which provides an easy-to-use way of testing the I2S peripherals (I2S0
+and I2S1), enabling both the TX and the RX for those peripherals.
+
+**I2S0 pinout**
+
+========== ========== =========================================
+ESP32 Pin Signal Pin Description
+========== ========== =========================================
+0 MCLK Master Clock
+4 BCLK Bit Clock (SCLK)
+5 WS Word Select (LRCLK)
+18 DOUT Data Out
+19 DIN Data IN
+========== ========== =========================================
+
+**I2S1 pinout**
+
+========== ========== =========================================
+ESP32 Pin Signal Pin Description
+========== ========== =========================================
+22 BCLK Bit Clock (SCLK)
+23 WS Word Select (LRCLK)
+25 DOUT Data Out
+26 DIN Data IN
+========== ========== =========================================
After successfully built and flashed, run on the boards's terminal::
- $ i2schar
+ i2schar -p /dev/i2schar[0-1]
The corresponding output should show related debug informations.
@@ -319,6 +349,61 @@ nsh
Basic NuttShell configuration (console enabled in UART0, exposed via
USB connection by means of CP2102 converter, at 115200 bps).
+nxlooper
+--------
+
+This configuration uses the I2S1 peripheral as an I2S receiver and the I2S0
+peripheral as an I2S transmitter. The idea is to capture an I2S data frame
+using an I2S peripheral and reproduce the captured data on the other.
+
+**Receiving data on I2S1**
+
+The I2S1 will act as a receiver (master mode), capturing data from DIN, which
+needs to be connected to an external source as follows:
+
+========== ========== =========================================
+ESP32 Pin Signal Pin Description
+========== ========== =========================================
+22 BCLK Bit Clock (SCLK)
+23 WS Word Select (LRCLK)
+26 DIN Data IN
+========== ========== =========================================
+
+**Transmitting data on I2S0**
+
+The I2S0 will act as a transmitter (master mode), replicating the data
+captured on I2S1. The pinout for the transmitter is as follows:
+
+========== ========== =========================================
+ESP32 Pin Signal Pin Description
+========== ========== =========================================
+0 MCLK Master Clock
+4 BCLK Bit Clock (SCLK)
+5 WS Word Select (LRCLK)
+18 DOUT Data Out
+========== ========== =========================================
+
+.. note:: The audio codec CS4344 can be connected to the transmitter pins
+ to reproduce the captured data if the receiver's source is an audio data.
+
+**nxlooper**
+
+The `nxlooper` application captures data from the audio device with receiving
+capabilities (the I2S1 on this example) and forwards the audio data frame to
+the audio device with transmitting capabilities (the I2S0 on this example).
+
+After successfully built and flashed, run on the boards's terminal::
+
+ nsh> nxlooper
+ nxlooper> loopback
+
+.. note:: `loopback` command default arguments for the channel configuration,
+ the data width and the sample rate are, respectively, 2 channels,
+ 16 bits/sample and 48KHz. These arguments can be supplied to select
+ different audio formats, for instance::
+
+ nxlooper> loopback 2 8 44100
+
ostest
------
[incubator-nuttx] 03/04: esp32/i2s: remove "esp32_" prefix from private functions
Posted by ac...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
acassis pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/incubator-nuttx.git
commit aa208bd52c772e0ce8f99cd443e5dba6878db707
Author: Tiago Medicci Serrano <ti...@espressif.com>
AuthorDate: Fri Nov 11 10:20:39 2022 -0300
esp32/i2s: remove "esp32_" prefix from private functions
---
arch/xtensa/src/esp32/esp32_i2s.c | 118 +++++++++++++++++---------------------
1 file changed, 53 insertions(+), 65 deletions(-)
diff --git a/arch/xtensa/src/esp32/esp32_i2s.c b/arch/xtensa/src/esp32/esp32_i2s.c
index 064f9b31dd..16169ce408 100644
--- a/arch/xtensa/src/esp32/esp32_i2s.c
+++ b/arch/xtensa/src/esp32/esp32_i2s.c
@@ -342,37 +342,29 @@ static void i2s_rx_schedule(struct esp32_i2s_s *priv,
static uint32_t i2s_set_datawidth(struct esp32_i2s_s *priv);
static uint32_t i2s_set_clock(struct esp32_i2s_s *priv);
-static uint32_t esp32_i2s_mclkfrequency(struct i2s_dev_s *dev,
- uint32_t frequency);
-static int esp32_i2s_ioctl(struct i2s_dev_s *dev, int cmd,
- unsigned long arg);
+static uint32_t i2s_mclkfrequency(struct i2s_dev_s *dev, uint32_t frequency);
+static int i2s_ioctl(struct i2s_dev_s *dev, int cmd, unsigned long arg);
#ifdef I2S_HAVE_TX
static void i2s_tx_channel_start(struct 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_txsamplerate(struct i2s_dev_s *dev,
- uint32_t rate);
-static uint32_t esp32_i2s_txdatawidth(struct i2s_dev_s *dev, int bits);
-static int esp32_i2s_send(struct i2s_dev_s *dev,
- struct ap_buffer_s *apb,
- i2s_callback_t callback, void *arg,
- uint32_t timeout);
+static int i2s_txchannels(struct i2s_dev_s *dev, uint8_t channels);
+static uint32_t i2s_txsamplerate(struct i2s_dev_s *dev, uint32_t rate);
+static uint32_t i2s_txdatawidth(struct i2s_dev_s *dev, int bits);
+static int i2s_send(struct i2s_dev_s *dev, struct ap_buffer_s *apb,
+ i2s_callback_t callback, void *arg,
+ uint32_t timeout);
#endif /* I2S_HAVE_TX */
#ifdef I2S_HAVE_RX
static void i2s_rx_channel_start(struct esp32_i2s_s *priv);
static void i2s_rx_channel_stop(struct esp32_i2s_s *priv);
-static int esp32_i2s_rxchannels(struct i2s_dev_s *dev,
- uint8_t channels);
-static uint32_t esp32_i2s_rxsamplerate(struct i2s_dev_s *dev,
- uint32_t rate);
-static uint32_t esp32_i2s_rxdatawidth(struct i2s_dev_s *dev, int bits);
-static int esp32_i2s_receive(struct i2s_dev_s *dev,
- struct ap_buffer_s *apb,
- i2s_callback_t callback, void *arg,
- uint32_t timeout);
+static int i2s_rxchannels(struct i2s_dev_s *dev, uint8_t channels);
+static uint32_t i2s_rxsamplerate(struct i2s_dev_s *dev, uint32_t rate);
+static uint32_t i2s_rxdatawidth(struct i2s_dev_s *dev, int bits);
+static int i2s_receive(struct i2s_dev_s *dev, struct ap_buffer_s *apb,
+ i2s_callback_t callback, void *arg,
+ uint32_t timeout);
#endif /* I2S_HAVE_RX */
/****************************************************************************
@@ -382,21 +374,21 @@ static int esp32_i2s_receive(struct i2s_dev_s *dev,
static const struct i2s_ops_s g_i2sops =
{
#ifdef I2S_HAVE_TX
- .i2s_txchannels = esp32_i2s_txchannels,
- .i2s_txsamplerate = esp32_i2s_txsamplerate,
- .i2s_txdatawidth = esp32_i2s_txdatawidth,
- .i2s_send = esp32_i2s_send,
+ .i2s_txchannels = i2s_txchannels,
+ .i2s_txsamplerate = i2s_txsamplerate,
+ .i2s_txdatawidth = i2s_txdatawidth,
+ .i2s_send = i2s_send,
#endif /* I2S_HAVE_TX */
#ifdef I2S_HAVE_RX
- .i2s_rxchannels = esp32_i2s_rxchannels,
- .i2s_rxsamplerate = esp32_i2s_rxsamplerate,
- .i2s_rxdatawidth = esp32_i2s_rxdatawidth,
- .i2s_receive = esp32_i2s_receive,
+ .i2s_rxchannels = i2s_rxchannels,
+ .i2s_rxsamplerate = i2s_rxsamplerate,
+ .i2s_rxdatawidth = i2s_rxdatawidth,
+ .i2s_receive = i2s_receive,
#endif /* I2S_HAVE_RX */
- .i2s_ioctl = esp32_i2s_ioctl,
- .i2s_mclkfrequency = esp32_i2s_mclkfrequency,
+ .i2s_ioctl = i2s_ioctl,
+ .i2s_mclkfrequency = i2s_mclkfrequency,
};
#ifdef CONFIG_ESP32_I2S0
@@ -1677,8 +1669,8 @@ static void i2s_configure(struct esp32_i2s_s *priv)
priv->mclk_multiple = I2S_MCLK_MULTIPLE_256;
}
- esp32_i2s_mclkfrequency((struct i2s_dev_s *)priv, (priv->config->rate *
- priv->mclk_multiple));
+ i2s_mclkfrequency((struct i2s_dev_s *)priv, (priv->config->rate *
+ priv->mclk_multiple));
priv->rate = priv->config->rate;
i2s_set_clock(priv);
@@ -1797,8 +1789,8 @@ static void i2s_configure(struct esp32_i2s_s *priv)
priv->mclk_multiple = I2S_MCLK_MULTIPLE_256;
}
- esp32_i2s_mclkfrequency((struct i2s_dev_s *)priv, (priv->config->rate *
- priv->mclk_multiple));
+ i2s_mclkfrequency((struct i2s_dev_s *)priv, (priv->config->rate *
+ priv->mclk_multiple));
priv->rate = priv->config->rate;
i2s_set_clock(priv);
@@ -2258,7 +2250,7 @@ static void i2s_rx_channel_stop(struct esp32_i2s_s *priv)
#endif /* I2S_HAVE_RX */
/****************************************************************************
- * Name: esp32_i2s_interrupt
+ * Name: i2s_interrupt
*
* Description:
* Common I2S DMA interrupt handler
@@ -2273,7 +2265,7 @@ static void i2s_rx_channel_stop(struct esp32_i2s_s *priv)
*
****************************************************************************/
-static int esp32_i2s_interrupt(int irq, void *context, void *arg)
+static int i2s_interrupt(int irq, void *context, void *arg)
{
struct esp32_i2s_s *priv = (struct esp32_i2s_s *)arg;
struct esp32_dmadesc_s *cur = NULL;
@@ -2316,7 +2308,7 @@ static int esp32_i2s_interrupt(int irq, void *context, void *arg)
}
/****************************************************************************
- * Name: esp32_i2s_mclkfrequency
+ * Name: i2s_mclkfrequency
*
* Description:
* Set the master clock frequency. Usually, the MCLK is a multiple of the
@@ -2332,8 +2324,7 @@ static int esp32_i2s_interrupt(int irq, void *context, void *arg)
*
****************************************************************************/
-static uint32_t esp32_i2s_mclkfrequency(struct i2s_dev_s *dev,
- uint32_t frequency)
+static uint32_t i2s_mclkfrequency(struct i2s_dev_s *dev, uint32_t frequency)
{
struct esp32_i2s_s *priv = (struct esp32_i2s_s *)dev;
@@ -2352,7 +2343,7 @@ static uint32_t esp32_i2s_mclkfrequency(struct i2s_dev_s *dev,
}
/****************************************************************************
- * Name: esp32_i2s_txchannels
+ * Name: i2s_txchannels
*
* Description:
* Set the I2S TX number of channels.
@@ -2367,7 +2358,7 @@ static uint32_t esp32_i2s_mclkfrequency(struct i2s_dev_s *dev,
****************************************************************************/
#ifdef I2S_HAVE_TX
-static int esp32_i2s_txchannels(struct i2s_dev_s *dev, uint8_t channels)
+static int i2s_txchannels(struct i2s_dev_s *dev, uint8_t channels)
{
struct esp32_i2s_s *priv = (struct esp32_i2s_s *)dev;
@@ -2387,7 +2378,7 @@ static int esp32_i2s_txchannels(struct i2s_dev_s *dev, uint8_t channels)
#endif /* I2S_HAVE_TX */
/****************************************************************************
- * Name: esp32_i2s_rxchannels
+ * Name: i2s_rxchannels
*
* Description:
* Set the I2S RX number of channels.
@@ -2402,7 +2393,7 @@ static int esp32_i2s_txchannels(struct i2s_dev_s *dev, uint8_t channels)
****************************************************************************/
#ifdef I2S_HAVE_RX
-static int esp32_i2s_rxchannels(struct i2s_dev_s *dev, uint8_t channels)
+static int i2s_rxchannels(struct i2s_dev_s *dev, uint8_t channels)
{
struct esp32_i2s_s *priv = (struct esp32_i2s_s *)dev;
@@ -2422,7 +2413,7 @@ static int esp32_i2s_rxchannels(struct i2s_dev_s *dev, uint8_t channels)
#endif /* I2S_HAVE_RX */
/****************************************************************************
- * Name: esp32_i2s_txsamplerate
+ * Name: i2s_txsamplerate
*
* Description:
* Set the I2S TX sample rate. NOTE: This will have no effect if (1) the
@@ -2440,7 +2431,7 @@ static int esp32_i2s_rxchannels(struct i2s_dev_s *dev, uint8_t channels)
****************************************************************************/
#ifdef I2S_HAVE_TX
-static uint32_t esp32_i2s_txsamplerate(struct i2s_dev_s *dev, uint32_t rate)
+static uint32_t i2s_txsamplerate(struct i2s_dev_s *dev, uint32_t rate)
{
struct esp32_i2s_s *priv = (struct esp32_i2s_s *)dev;
@@ -2462,7 +2453,7 @@ static uint32_t esp32_i2s_txsamplerate(struct i2s_dev_s *dev, uint32_t rate)
#endif /* I2S_HAVE_TX */
/****************************************************************************
- * Name: esp32_i2s_rxsamplerate
+ * Name: i2s_rxsamplerate
*
* Description:
* Set the I2S RX sample rate.
@@ -2477,7 +2468,7 @@ static uint32_t esp32_i2s_txsamplerate(struct i2s_dev_s *dev, uint32_t rate)
****************************************************************************/
#ifdef I2S_HAVE_RX
-static uint32_t esp32_i2s_rxsamplerate(struct i2s_dev_s *dev, uint32_t rate)
+static uint32_t i2s_rxsamplerate(struct i2s_dev_s *dev, uint32_t rate)
{
struct esp32_i2s_s *priv = (struct esp32_i2s_s *)dev;
@@ -2499,7 +2490,7 @@ static uint32_t esp32_i2s_rxsamplerate(struct i2s_dev_s *dev, uint32_t rate)
#endif /* I2S_HAVE_RX */
/****************************************************************************
- * Name: esp32_i2s_txdatawidth
+ * Name: i2s_txdatawidth
*
* Description:
* Set the I2S TX data width. The TX bitrate is determined by
@@ -2515,7 +2506,7 @@ static uint32_t esp32_i2s_rxsamplerate(struct i2s_dev_s *dev, uint32_t rate)
****************************************************************************/
#ifdef I2S_HAVE_TX
-static uint32_t esp32_i2s_txdatawidth(struct i2s_dev_s *dev, int bits)
+static uint32_t i2s_txdatawidth(struct i2s_dev_s *dev, int bits)
{
struct esp32_i2s_s *priv = (struct esp32_i2s_s *)dev;
@@ -2537,7 +2528,7 @@ static uint32_t esp32_i2s_txdatawidth(struct i2s_dev_s *dev, int bits)
#endif /* I2S_HAVE_TX */
/****************************************************************************
- * Name: esp32_i2s_rxdatawidth
+ * Name: i2s_rxdatawidth
*
* Description:
* Set the I2S RX data width. The RX bitrate is determined by
@@ -2553,7 +2544,7 @@ static uint32_t esp32_i2s_txdatawidth(struct i2s_dev_s *dev, int bits)
****************************************************************************/
#ifdef I2S_HAVE_RX
-static uint32_t esp32_i2s_rxdatawidth(struct i2s_dev_s *dev, int bits)
+static uint32_t i2s_rxdatawidth(struct i2s_dev_s *dev, int bits)
{
struct esp32_i2s_s *priv = (struct esp32_i2s_s *)dev;
@@ -2575,7 +2566,7 @@ static uint32_t esp32_i2s_rxdatawidth(struct i2s_dev_s *dev, int bits)
#endif /* I2S_HAVE_RX */
/****************************************************************************
- * Name: esp32_i2s_send
+ * Name: i2s_send
*
* Description:
* Send a block of data on I2S.
@@ -2598,9 +2589,8 @@ static uint32_t esp32_i2s_rxdatawidth(struct i2s_dev_s *dev, int bits)
****************************************************************************/
#ifdef I2S_HAVE_TX
-static int esp32_i2s_send(struct i2s_dev_s *dev, struct ap_buffer_s *apb,
- i2s_callback_t callback, void *arg,
- uint32_t timeout)
+static int i2s_send(struct i2s_dev_s *dev, struct ap_buffer_s *apb,
+ i2s_callback_t callback, void *arg, uint32_t timeout)
{
struct esp32_i2s_s *priv = (struct esp32_i2s_s *)dev;
@@ -2695,7 +2685,7 @@ errout_with_buf:
#endif /* I2S_HAVE_TX */
/****************************************************************************
- * Name: esp32_i2s_receive
+ * Name: i2s_receive
*
* Description:
* Receive a block of data on I2S.
@@ -2718,9 +2708,8 @@ errout_with_buf:
****************************************************************************/
#ifdef I2S_HAVE_RX
-static int esp32_i2s_receive(struct i2s_dev_s *dev, struct ap_buffer_s *apb,
- i2s_callback_t callback, void *arg,
- uint32_t timeout)
+static int i2s_receive(struct i2s_dev_s *dev, struct ap_buffer_s *apb,
+ i2s_callback_t callback, void *arg, uint32_t timeout)
{
struct esp32_i2s_s *priv = (struct esp32_i2s_s *)dev;
@@ -2805,15 +2794,14 @@ errout_with_buf:
#endif /* I2S_HAVE_RX */
/****************************************************************************
- * Name: esp32_i2s_ioctl
+ * Name: i2s_ioctl
*
* Description:
* Perform a device ioctl
*
****************************************************************************/
-static int esp32_i2s_ioctl(struct i2s_dev_s *dev, int cmd,
- unsigned long arg)
+static int i2s_ioctl(struct i2s_dev_s *dev, int cmd, unsigned long arg)
{
struct audio_buf_desc_s *bufdesc;
int ret = -ENOTTY;
@@ -2905,7 +2893,7 @@ static int i2s_dma_setup(struct esp32_i2s_s *priv)
return priv->cpuint;
}
- ret = irq_attach(priv->config->irq, esp32_i2s_interrupt, priv);
+ ret = irq_attach(priv->config->irq, i2s_interrupt, priv);
if (ret != OK)
{
i2serr("Couldn't attach IRQ to handler.\n");
[incubator-nuttx] 02/04: esp32/i2s: implement I2S receiver module
Posted by ac...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
acassis pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/incubator-nuttx.git
commit 3b5ab27893a4073b29d01627a82ed1ef8c7fdd99
Author: Tiago Medicci Serrano <ti...@espressif.com>
AuthorDate: Thu Nov 10 16:40:42 2022 -0300
esp32/i2s: implement I2S receiver module
- Add ioctl method to enable allocating the apb buffer.
- Add RX methods to set data width, sample rate, channels and
for receiving data from the I2S peripheral.
- Update the i2schar defconfig to enable the I2S receiver.
- Add nxlooper defconfig to enable testing the RX interface.
- Add specific bindings on ESP32 bringup to enable nxlooper
to work without the need of any specific codec.
---
arch/xtensa/src/esp32/Kconfig | 17 +-
arch/xtensa/src/esp32/esp32_i2s.c | 1603 +++++++++++++++++---
arch/xtensa/src/esp32/esp32_i2s.h | 9 +-
.../xtensa/esp32/common/src/esp32_board_i2sdev.c | 89 +-
.../esp32/esp32-devkitc/configs/i2schar/defconfig | 8 +-
.../esp32/esp32-devkitc/configs/nxlooper/defconfig | 129 ++
.../xtensa/esp32/esp32-devkitc/src/esp32-devkitc.h | 6 +-
.../xtensa/esp32/esp32-devkitc/src/esp32_bringup.c | 37 +-
8 files changed, 1603 insertions(+), 295 deletions(-)
diff --git a/arch/xtensa/src/esp32/Kconfig b/arch/xtensa/src/esp32/Kconfig
index 913f1b8318..8c3ac980d3 100644
--- a/arch/xtensa/src/esp32/Kconfig
+++ b/arch/xtensa/src/esp32/Kconfig
@@ -369,9 +369,9 @@ config ESP32_I2S0_WSPIN
range 0 39 if ESP32_I2S0_ROLE_SLAVE
config ESP32_I2S0_DINPIN
- int "I2S0 DOUT pin"
+ int "I2S0 DIN pin"
depends on ESP32_I2S0_RX
- default 12
+ default 19
range 0 39
config ESP32_I2S0_DOUTPIN
@@ -471,7 +471,6 @@ endchoice
config ESP32_I2S1_DATA_BIT_WIDTH
int
- default 16
default 8 if ESP32_I2S1_DATA_BIT_WIDTH_8BIT
default 16 if ESP32_I2S1_DATA_BIT_WIDTH_16BIT
default 24 if ESP32_I2S1_DATA_BIT_WIDTH_24BIT
@@ -488,26 +487,26 @@ config ESP32_I2S1_SAMPLE_RATE
config ESP32_I2S1_BCLKPIN
int "I2S1 BCLK pin"
- default 19
+ default 22
range 0 33 if ESP32_I2S1_ROLE_MASTER
range 0 39 if ESP32_I2S1_ROLE_SLAVE
config ESP32_I2S1_WSPIN
int "I2S1 WS pin"
- default 18
+ default 23
range 0 33 if ESP32_I2S1_ROLE_MASTER
range 0 39 if ESP32_I2S1_ROLE_SLAVE
-config ESP32_I2S1_DOUTPIN
- int "I2S1 DOUT pin"
+config ESP32_I2S1_DINPIN
+ int "I2S1 DIN pin"
depends on ESP32_I2S1_RX
- default 17
+ default 26
range 0 39
config ESP32_I2S1_DOUTPIN
int "I2S1 DOUT pin"
depends on ESP32_I2S1_TX
- default 16
+ default 25
range 0 33
config ESP32_I2S1_MCLK
diff --git a/arch/xtensa/src/esp32/esp32_i2s.c b/arch/xtensa/src/esp32/esp32_i2s.c
index 96bc7b8503..064f9b31dd 100644
--- a/arch/xtensa/src/esp32/esp32_i2s.c
+++ b/arch/xtensa/src/esp32/esp32_i2s.c
@@ -90,6 +90,7 @@
#ifdef CONFIG_ESP32_I2S0_RX
# define I2S0_RX_ENABLED 1
+# define I2S_HAVE_RX 1
#else
# define I2S0_RX_ENABLED 0
#endif
@@ -103,6 +104,7 @@
#ifdef CONFIG_ESP32_I2S1_RX
# define I2S1_RX_ENABLED 1
+# define I2S_HAVE_RX 1
#else
# define I2S1_RX_ENABLED 0
#endif
@@ -172,7 +174,6 @@ struct esp32_i2s_config_s
uint32_t total_slot; /* Total slot number */
bool is_apll; /* Select APLL as the source clock */
- uint32_t mclk_multiple; /* The multiple of mclk to the sample rate */
bool tx_en; /* Is TX enabled? */
bool rx_en; /* Is RX enabled? */
@@ -219,9 +220,9 @@ struct esp32_buffer_s
{
struct esp32_buffer_s *flink; /* Supports a singly linked list */
- /* The associated DMA outlink */
+ /* The associated DMA in/outlink */
- struct esp32_dmadesc_s dma_outlink[I2S_DMADESC_NUM];
+ struct esp32_dmadesc_s dma_link[I2S_DMADESC_NUM];
i2s_callback_t callback; /* DMA completion callback */
uint32_t timeout; /* Timeout value of the DMA transfers */
@@ -232,7 +233,7 @@ struct esp32_buffer_s
int result; /* The result of the transfer */
};
-/* Internal buffer must be aligned to the sample_size. Sometimes,
+/* Internal buffer must be aligned to the bytes_per_sample. Sometimes,
* however, the audio buffer is not aligned and additional bytes must
* be copied to be inserted on the next buffer. This structure keeps
* track of the bytes that were not written to the internal buffer yet.
@@ -274,10 +275,11 @@ struct esp32_i2s_s
const struct esp32_i2s_config_s *config;
- uint32_t mclk_freq; /* I2S actual master clock */
- uint32_t channels; /* Audio channels (1:mono or 2:stereo) */
- uint32_t rate; /* I2S actual configured sample-rate */
- uint32_t data_width; /* I2S actual configured data_width */
+ uint32_t mclk_freq; /* I2S actual master clock */
+ uint32_t mclk_multiple; /* The multiple of mclk to the sample rate */
+ uint32_t channels; /* Audio channels (1:mono or 2:stereo) */
+ uint32_t rate; /* I2S actual configured sample-rate */
+ uint32_t data_width; /* I2S actual configured data_width */
#ifdef I2S_HAVE_TX
struct esp32_transport_s tx; /* TX transport state */
@@ -285,6 +287,12 @@ struct esp32_i2s_s
bool tx_started; /* TX channel started */
#endif /* I2S_HAVE_TX */
+#ifdef I2S_HAVE_RX
+ struct esp32_transport_s rx; /* RX transport state */
+
+ bool rx_started; /* RX channel started */
+#endif /* I2S_HAVE_RX */
+
/* Pre-allocated pool of buffer containers */
sem_t bufsem; /* Buffer wait semaphore */
@@ -322,16 +330,28 @@ static void i2s_tx_schedule(struct esp32_i2s_s *priv,
struct esp32_dmadesc_s *outlink);
#endif /* I2S_HAVE_TX */
+#ifdef I2S_HAVE_RX
+static IRAM_ATTR int i2s_rxdma_setup(struct esp32_i2s_s *priv,
+ struct esp32_buffer_s *bfcontainer);
+static void i2s_rx_worker(void *arg);
+static void i2s_rx_schedule(struct esp32_i2s_s *priv,
+ struct esp32_dmadesc_s *outlink);
+#endif /* I2S_HAVE_RX */
+
/* I2S methods (and close friends) */
static uint32_t i2s_set_datawidth(struct esp32_i2s_s *priv);
static uint32_t i2s_set_clock(struct esp32_i2s_s *priv);
+static uint32_t esp32_i2s_mclkfrequency(struct i2s_dev_s *dev,
+ uint32_t frequency);
+static int esp32_i2s_ioctl(struct i2s_dev_s *dev, int cmd,
+ unsigned long arg);
+
+#ifdef I2S_HAVE_TX
static void i2s_tx_channel_start(struct esp32_i2s_s *priv);
static void i2s_tx_channel_stop(struct esp32_i2s_s *priv);
static int esp32_i2s_txchannels(struct i2s_dev_s *dev,
uint8_t channels);
-static uint32_t esp32_i2s_mclkfrequency(struct i2s_dev_s *dev,
- uint32_t frequency);
static uint32_t esp32_i2s_txsamplerate(struct i2s_dev_s *dev,
uint32_t rate);
static uint32_t esp32_i2s_txdatawidth(struct i2s_dev_s *dev, int bits);
@@ -339,6 +359,21 @@ static int esp32_i2s_send(struct i2s_dev_s *dev,
struct ap_buffer_s *apb,
i2s_callback_t callback, void *arg,
uint32_t timeout);
+#endif /* I2S_HAVE_TX */
+
+#ifdef I2S_HAVE_RX
+static void i2s_rx_channel_start(struct esp32_i2s_s *priv);
+static void i2s_rx_channel_stop(struct esp32_i2s_s *priv);
+static int esp32_i2s_rxchannels(struct i2s_dev_s *dev,
+ uint8_t channels);
+static uint32_t esp32_i2s_rxsamplerate(struct i2s_dev_s *dev,
+ uint32_t rate);
+static uint32_t esp32_i2s_rxdatawidth(struct i2s_dev_s *dev, int bits);
+static int esp32_i2s_receive(struct i2s_dev_s *dev,
+ struct ap_buffer_s *apb,
+ i2s_callback_t callback, void *arg,
+ uint32_t timeout);
+#endif /* I2S_HAVE_RX */
/****************************************************************************
* Private Data
@@ -346,10 +381,21 @@ static int esp32_i2s_send(struct i2s_dev_s *dev,
static const struct i2s_ops_s g_i2sops =
{
+#ifdef I2S_HAVE_TX
.i2s_txchannels = esp32_i2s_txchannels,
.i2s_txsamplerate = esp32_i2s_txsamplerate,
.i2s_txdatawidth = esp32_i2s_txdatawidth,
.i2s_send = esp32_i2s_send,
+#endif /* I2S_HAVE_TX */
+
+#ifdef I2S_HAVE_RX
+ .i2s_rxchannels = esp32_i2s_rxchannels,
+ .i2s_rxsamplerate = esp32_i2s_rxsamplerate,
+ .i2s_rxdatawidth = esp32_i2s_rxdatawidth,
+ .i2s_receive = esp32_i2s_receive,
+#endif /* I2S_HAVE_RX */
+
+ .i2s_ioctl = esp32_i2s_ioctl,
.i2s_mclkfrequency = esp32_i2s_mclkfrequency,
};
@@ -365,7 +411,6 @@ static const struct esp32_i2s_config_s esp32_i2s0_config =
.data_width = CONFIG_ESP32_I2S0_DATA_BIT_WIDTH,
.rate = CONFIG_ESP32_I2S0_SAMPLE_RATE,
.total_slot = 2,
- .mclk_multiple = I2S_MCLK_MULTIPLE_256,
.tx_en = I2S0_TX_ENABLED,
.rx_en = I2S0_RX_ENABLED,
#ifdef CONFIG_ESP32_I2S0_MCLK
@@ -426,7 +471,6 @@ static const struct esp32_i2s_config_s esp32_i2s1_config =
.data_width = CONFIG_ESP32_I2S1_DATA_BIT_WIDTH,
.rate = CONFIG_ESP32_I2S1_SAMPLE_RATE,
.total_slot = 2,
- .mclk_multiple = I2S_MCLK_MULTIPLE_256,
.tx_en = I2S1_TX_ENABLED,
.rx_en = I2S1_RX_ENABLED,
#ifdef CONFIG_ESP32_I2S1_MCLK
@@ -487,7 +531,7 @@ static struct esp32_i2s_s esp32_i2s1_priv =
* free list
*
* Input Parameters:
- * priv - I2S state instance
+ * priv - Initialized I2S device structure.
*
* Returned Value:
* A non-NULL pointer to the allocate buffer container on success; NULL if
@@ -535,7 +579,7 @@ static struct esp32_buffer_s *i2s_buf_allocate(struct esp32_i2s_s *priv)
* Free buffer container by adding it to the head of the free list
*
* Input Parameters:
- * priv - I2S state instance
+ * priv - Initialized I2S device structure.
* bfcontainer - The buffer container to be freed
*
* Returned Value:
@@ -576,7 +620,7 @@ static void i2s_buf_free(struct esp32_i2s_s *priv,
* pre-allocated buffer containers to the free list
*
* Input Parameters:
- * priv - I2S state instance
+ * priv - Initialized I2S device structure.
*
* Returned Value:
* OK on success; A negated errno value on failure.
@@ -589,8 +633,10 @@ static void i2s_buf_free(struct esp32_i2s_s *priv,
static int i2s_buf_initialize(struct esp32_i2s_s *priv)
{
+#ifdef I2S_HAVE_TX
priv->tx.carry.bytes = 0;
priv->tx.carry.value = 0;
+#endif /* I2S_HAVE_TX */
priv->bf_freelist = NULL;
for (int i = 0; i < CONFIG_ESP32_I2S_MAXINFLIGHT; i++)
@@ -606,10 +652,10 @@ static int i2s_buf_initialize(struct esp32_i2s_s *priv)
*
* Description:
* Initiate the next TX DMA transfer. The DMA outlink was previously bound
- * so it is safe to start the next DMA transfer at interruption level.
+ * so it is safe to start the next DMA transfer at interrupt level.
*
* Input Parameters:
- * priv - I2S state instance
+ * priv - Initialized I2S device structure.
*
* Returned Value:
* OK on success; a negated errno value on failure
@@ -648,7 +694,7 @@ static int i2s_txdma_start(struct esp32_i2s_s *priv)
modifyreg32(I2S_OUT_LINK_REG(priv->config->port), I2S_OUTLINK_ADDR_M,
FIELD_TO_VALUE(I2S_OUTLINK_ADDR,
- (uintptr_t) bfcontainer->dma_outlink));
+ (uintptr_t) bfcontainer->dma_link));
modifyreg32(I2S_OUT_LINK_REG(priv->config->port), I2S_OUTLINK_STOP,
I2S_OUTLINK_START);
@@ -661,6 +707,70 @@ static int i2s_txdma_start(struct esp32_i2s_s *priv)
}
#endif /* I2S_HAVE_TX */
+/****************************************************************************
+ * Name: i2s_rxdma_start
+ *
+ * Description:
+ * Initiate the next RX DMA transfer. Assuming the DMA inlink is already
+ * bound, it's safe to start the next DMA transfer in an interrupt context.
+ *
+ * Input Parameters:
+ * priv - Initialized I2S device structure.
+ *
+ * Returned Value:
+ * OK on success; a negated errno value on failure
+ *
+ * Assumptions:
+ * Interrupts are disabled
+ *
+ ****************************************************************************/
+
+#ifdef I2S_HAVE_RX
+static int i2s_rxdma_start(struct esp32_i2s_s *priv)
+{
+ struct esp32_buffer_s *bfcontainer;
+
+ /* If there is already an active transmission in progress, then bail
+ * returning success.
+ */
+
+ if (!sq_empty(&priv->rx.act))
+ {
+ return OK;
+ }
+
+ /* If there are no pending transfer, then bail returning success */
+
+ if (sq_empty(&priv->rx.pend))
+ {
+ return OK;
+ }
+
+ bfcontainer = (struct esp32_buffer_s *)sq_remfirst(&priv->rx.pend);
+
+ /* If there isn't already an active transmission in progress,
+ * then start it.
+ */
+
+ modifyreg32(I2S_RXEOF_NUM_REG(priv->config->port), I2S_RX_EOF_NUM_M,
+ FIELD_TO_VALUE(I2S_RX_EOF_NUM,
+ (bfcontainer->nbytes / 4)));
+
+ modifyreg32(I2S_IN_LINK_REG(priv->config->port), I2S_INLINK_ADDR_M,
+ FIELD_TO_VALUE(I2S_INLINK_ADDR,
+ (uintptr_t) bfcontainer->dma_link));
+
+ modifyreg32(I2S_IN_LINK_REG(priv->config->port), I2S_INLINK_STOP,
+ I2S_INLINK_START);
+
+ modifyreg32(I2S_CONF_REG(priv->config->port), 0, I2S_RX_START);
+
+ sq_addlast((sq_entry_t *)bfcontainer, &priv->rx.act);
+
+ return OK;
+}
+#endif /* I2S_HAVE_RX */
+
/****************************************************************************
* Name: i2s_txdma_setup
*
@@ -668,7 +778,8 @@ static int i2s_txdma_start(struct esp32_i2s_s *priv)
* Setup the next TX DMA transfer
*
* Input Parameters:
- * priv - I2S state instance
+ * priv - Initialized I2S device structure.
+ * bfcontainer - The buffer container to be set up
*
* Returned Value:
* OK on success; a negated errno value on failure
@@ -682,34 +793,34 @@ static int i2s_txdma_start(struct esp32_i2s_s *priv)
static IRAM_ATTR int i2s_txdma_setup(struct esp32_i2s_s *priv,
struct esp32_buffer_s *bfcontainer)
{
- struct ap_buffer_s *apb;
- struct esp32_dmadesc_s *outlink;
- uint8_t *samp;
- apb_samp_t samp_size;
+ int ret = OK;
size_t carry_size;
uint32_t bytes_queued;
uint32_t data_copied;
+ struct ap_buffer_s *apb;
+ struct esp32_dmadesc_s *outlink;
+ apb_samp_t samp_size;
+ irqstate_t flags;
uint8_t *buf;
uint8_t padding;
- irqstate_t flags;
- int ret = OK;
+ uint8_t *samp;
DEBUGASSERT(bfcontainer && bfcontainer->apb);
apb = bfcontainer->apb;
- outlink = bfcontainer->dma_outlink;
+ outlink = bfcontainer->dma_link;
/* Get the transfer information, accounting for any data offset */
- const apb_samp_t sample_size = priv->data_width / 8;
+ const apb_samp_t bytes_per_sample = priv->data_width / 8;
samp = &apb->samp[apb->curbyte];
samp_size = (apb->nbytes - apb->curbyte) + priv->tx.carry.bytes;
- carry_size = samp_size % sample_size;
+ carry_size = samp_size % bytes_per_sample;
/* Padding contains the size (in bytes) to be skipped on the
* internal buffer do provide 1) the ability to handle mono
* audio files and 2) to correctly fill the buffer to 16 or 32-bits
- * aligned positions
+ * aligned positions.
*/
padding = priv->channels == 1 ?
@@ -721,7 +832,13 @@ static IRAM_ATTR int i2s_txdma_setup(struct esp32_i2s_s *priv,
/* Copy audio data into internal buffer */
- bfcontainer->buf = (uint8_t *)calloc(bfcontainer->nbytes, 1);
+ bfcontainer->buf = calloc(bfcontainer->nbytes, 1);
+ if (bfcontainer->buf == NULL)
+ {
+ i2serr("Failed to allocate the DMA internal buffer "
+ "[%" PRIu32 " bytes]", bfcontainer->nbytes);
+ return -ENOMEM;
+ }
data_copied = 0;
buf = bfcontainer->buf + padding;
@@ -731,25 +848,25 @@ static IRAM_ATTR int i2s_txdma_setup(struct esp32_i2s_s *priv,
memcpy(buf, &priv->tx.carry.value, priv->tx.carry.bytes);
buf += priv->tx.carry.bytes;
data_copied += priv->tx.carry.bytes;
- memcpy(buf, samp, (sample_size - priv->tx.carry.bytes));
- buf += (sample_size - priv->tx.carry.bytes + padding);
- samp += (sample_size - priv->tx.carry.bytes);
- data_copied += (sample_size - priv->tx.carry.bytes);
+ memcpy(buf, samp, (bytes_per_sample - priv->tx.carry.bytes));
+ buf += (bytes_per_sample - priv->tx.carry.bytes + padding);
+ samp += (bytes_per_sample - priv->tx.carry.bytes);
+ data_copied += (bytes_per_sample - priv->tx.carry.bytes);
}
/* If there is no need to add padding bytes, the memcpy may be done at
* once. Otherwise, the operation must add the padding bytes to each
- * sample in the internal buffer
+ * sample in the internal buffer.
*/
if (padding)
{
while (data_copied < (samp_size - carry_size))
{
- memcpy(buf, samp, sample_size);
- buf += (sample_size + padding);
- samp += sample_size;
- data_copied += sample_size;
+ memcpy(buf, samp, bytes_per_sample);
+ buf += (bytes_per_sample + padding);
+ samp += bytes_per_sample;
+ data_copied += bytes_per_sample;
}
}
else
@@ -787,8 +904,9 @@ static IRAM_ATTR int i2s_txdma_setup(struct esp32_i2s_s *priv,
if (bytes_queued != bfcontainer->nbytes)
{
- i2serr("Failed to enqueue I2S buffer (%d bytes of %d)\n",
- bytes_queued, (uint32_t)bfcontainer->nbytes);
+ i2serr("Failed to enqueue I2S buffer "
+ "(%" PRIu32 " bytes of %" PRIu32 ")\n",
+ bytes_queued, bfcontainer->nbytes);
return bytes_queued;
}
@@ -808,6 +926,77 @@ static IRAM_ATTR int i2s_txdma_setup(struct esp32_i2s_s *priv,
}
#endif /* I2S_HAVE_TX */
+/****************************************************************************
+ * Name: i2s_rxdma_setup
+ *
+ * Description:
+ * Setup the next RX DMA transfer
+ *
+ * Input Parameters:
+ * priv - Initialized I2S device structure.
+ * bfcontainer - The buffer container to be set up
+ *
+ * Returned Value:
+ * OK on success; a negated errno value on failure
+ *
+ * Assumptions:
+ * Interrupts are disabled
+ *
+ ****************************************************************************/
+
+#ifdef I2S_HAVE_RX
+static int i2s_rxdma_setup(struct esp32_i2s_s *priv,
+ struct esp32_buffer_s *bfcontainer)
+{
+ int ret = OK;
+ struct esp32_dmadesc_s *inlink;
+ uint32_t bytes_queued;
+ irqstate_t flags;
+
+ DEBUGASSERT(bfcontainer && bfcontainer->apb);
+
+ inlink = bfcontainer->dma_link;
+
+ /* Allocate the internal buffer for RX */
+
+ bfcontainer->buf = calloc(bfcontainer->nbytes, 1);
+ if (bfcontainer->buf == NULL)
+ {
+ i2serr("Failed to allocate the DMA internal buffer "
+ "[%" PRIu32 " bytes]", bfcontainer->nbytes);
+ return -ENOMEM;
+ }
+
+ /* Configure DMA stream */
+
+ bytes_queued = esp32_dma_init(inlink, I2S_DMADESC_NUM,
+ bfcontainer->buf,
+ bfcontainer->nbytes);
+
+ if (bytes_queued != bfcontainer->nbytes)
+ {
+ i2serr("Failed to enqueue I2S buffer "
+ "(%" PRIu32 " bytes of %" PRIu32 ")\n",
+ bytes_queued, bfcontainer->nbytes);
+ return bytes_queued;
+ }
+
+ flags = spin_lock_irqsave(&priv->slock);
+
+ /* Add the buffer container to the end of the RX pending queue */
+
+ sq_addlast((sq_entry_t *)bfcontainer, &priv->rx.pend);
+
+ /* Trigger DMA transfer if no transmission is in progress */
+
+ ret = i2s_rxdma_start(priv);
+
+ spin_unlock_irqrestore(&priv->slock, flags);
+
+ return ret;
+}
+#endif /* I2S_HAVE_RX */
+
/****************************************************************************
* Name: i2s_tx_schedule
*
@@ -816,16 +1005,14 @@ static IRAM_ATTR int i2s_txdma_setup(struct esp32_i2s_s *priv,
* the working thread.
*
* Input Parameters:
- * handle - The DMA handler
- * arg - A pointer to the chip select struction
- * result - The result of the DMA transfer
+ * priv - Initialized I2S device structure.
+ * outlink - DMA outlink descriptor that triggered the interrupt.
*
* Returned Value:
* None
*
* Assumptions:
* - Interrupts are disabled
- * - The TX timeout has been canceled.
*
****************************************************************************/
@@ -858,7 +1045,7 @@ static void i2s_tx_schedule(struct esp32_i2s_s *priv,
/* Find the last descriptor of the current buffer container */
- bfdesc = bfcontainer->dma_outlink;
+ bfdesc = bfcontainer->dma_link;
while (!(bfdesc->ctrl & DMA_CTRL_EOF))
{
DEBUGASSERT(bfdesc->next);
@@ -909,6 +1096,105 @@ static void i2s_tx_schedule(struct esp32_i2s_s *priv,
}
#endif /* I2S_HAVE_TX */
+/****************************************************************************
+ * Name: i2s_rx_schedule
+ *
+ * Description:
+ * An RX DMA completion has occurred. Schedule processing on
+ * the working thread.
+ *
+ * Input Parameters:
+ * priv - Initialized I2S device structure.
+ * inlink - DMA inlink descriptor that triggered the interrupt.
+ *
+ * Returned Value:
+ * None
+ *
+ * Assumptions:
+ * - Interrupts are disabled
+ *
+ ****************************************************************************/
+
+#ifdef I2S_HAVE_RX
+static void i2s_rx_schedule(struct esp32_i2s_s *priv,
+ struct esp32_dmadesc_s *inlink)
+{
+ struct esp32_buffer_s *bfcontainer;
+ struct esp32_dmadesc_s *bfdesc;
+ int ret;
+
+ /* Upon entry, the transfer(s) that just completed are the ones in the
+ * priv->rx.act queue.
+ */
+
+ /* Move all entries from the rx.act queue to the rx.done queue */
+
+ if (!sq_empty(&priv->rx.act))
+ {
+ /* Remove the next buffer container from the rx.act list */
+
+ bfcontainer = (struct esp32_buffer_s *)sq_peek(&priv->rx.act);
+
+ /* Check if the DMA descriptor that generated an EOF interrupt is the
+ * last descriptor of the current buffer container's DMA inlink.
+ * REVISIT: what to do if we miss syncronization and the descriptor
+ * that generated the interrupt is different from the expected (the
+ * oldest of the list containing active transmissions)?
+ */
+
+ /* Find the last descriptor of the current buffer container */
+
+ bfdesc = bfcontainer->dma_link;
+ while (!(bfdesc->ctrl & DMA_CTRL_EOF))
+ {
+ DEBUGASSERT(bfdesc->next);
+ bfdesc = bfdesc->next;
+ }
+
+ if (bfdesc == inlink)
+ {
+ sq_remfirst(&priv->rx.act);
+
+ /* Report the result of the transfer */
+
+ bfcontainer->result = OK;
+
+ /* Add the completed buffer container to the tail of the rx.done
+ * queue
+ */
+
+ sq_addlast((sq_entry_t *)bfcontainer, &priv->rx.done);
+
+ /* Check if the DMA is IDLE */
+
+ if (sq_empty(&priv->rx.act))
+ {
+ /* Then start the next DMA. */
+
+ i2s_rxdma_start(priv);
+ }
+ }
+
+ /* If the worker has completed running, then reschedule the working
+ * thread.
+ */
+
+ if (work_available(&priv->rx.work))
+ {
+ /* Schedule the RX DMA done processing to occur on the worker
+ * thread.
+ */
+
+ ret = work_queue(HPWORK, &priv->rx.work, i2s_rx_worker, priv, 0);
+ if (ret != 0)
+ {
+ i2serr("ERROR: Failed to queue RX work: %d\n", ret);
+ }
+ }
+ }
+}
+#endif /* I2S_HAVE_RX */
+
/****************************************************************************
* Name: i2s_tx_worker
*
@@ -976,62 +1262,219 @@ static void i2s_tx_worker(void *arg)
#endif /* I2S_HAVE_TX */
/****************************************************************************
- * Name: i2s_configure
+ * Name: i2s_rx_worker
*
* Description:
- * Configure I2S
+ * RX transfer done worker
*
* Input Parameters:
- * priv - Partially initialized I2S device structure. This function
- * will complete the I2S specific portions of the initialization
+ * arg - the I2S device instance cast to void*
*
* Returned Value:
* None
*
****************************************************************************/
-static void i2s_configure(struct esp32_i2s_s *priv)
+#ifdef I2S_HAVE_RX
+static void i2s_rx_worker(void *arg)
{
- /* Set peripheral clock and clear reset */
+ struct esp32_i2s_s *priv = (struct esp32_i2s_s *)arg;
+ struct esp32_buffer_s *bfcontainer;
+ irqstate_t flags;
- modifyreg32(DPORT_PERIP_CLK_EN_REG, 0, DPORT_I2S0_CLK_EN);
- modifyreg32(DPORT_PERIP_RST_EN_REG, 0, DPORT_I2S0_RST);
- modifyreg32(DPORT_PERIP_RST_EN_REG, DPORT_I2S0_RST, 0);
+ DEBUGASSERT(priv);
- /* I2S module general init, enable I2S clock */
+ /* When the transfer was started, the active buffer containers were removed
+ * from the rx.pend queue and saved in the rx.act queue. We get here when
+ * the DMA is finished.
+ *
+ * In any case, the buffer containers in rx.act will be moved to the end
+ * of the rx.done queue and rx.act will be emptied before this worker is
+ * started.
+ *
+ */
- if (!(getreg32(I2S_CLKM_CONF_REG(priv->config->port)) & I2S_CLK_ENA))
+ i2sinfo("rx.act.head=%p rx.done.head=%p\n",
+ priv->rx.act.head, priv->rx.done.head);
+
+ /* Process each buffer in the rx.done queue */
+
+ while (sq_peek(&priv->rx.done) != NULL)
{
- i2sinfo("Enabling I2S port clock...\n");
- modifyreg32(I2S_CLKM_CONF_REG(priv->config->port), 0, I2S_CLK_ENA);
- putreg32(0, I2S_CONF2_REG(priv->config->port));
- }
+ /* Remove the buffer container from the rx.done queue. NOTE that
+ * interrupts must be disabled to do this because the rx.done queue is
+ * also modified from the interrupt level.
+ */
- /* Configure multiplexed pins as connected on the board */
+ flags = spin_lock_irqsave(&priv->slock);
+ bfcontainer = (struct esp32_buffer_s *)sq_remfirst(&priv->rx.done);
+ spin_unlock_irqrestore(&priv->slock, flags);
- /* TODO: check for loopback mode */
+ apb_samp_t samp_size;
+ uint32_t data_copied;
+ uint8_t padding;
+ uint8_t *buf;
+ uint8_t *samp;
- /* Enable TX channel */
+ /* Padding contains the size (in bytes) to be skipped on the internal
+ * buffer - which store data aligned with 2 or 4 bytes - to correctly
+ * copy and fill the audio buffer according to the actual data width.
+ */
- if (priv->config->dout_pin != I2S_GPIO_UNUSED)
- {
- esp32_gpiowrite(priv->config->dout_pin, 1);
- esp32_configgpio(priv->config->dout_pin, OUTPUT_FUNCTION_3);
- esp32_gpio_matrix_out(priv->config->dout_pin,
- priv->config->dout_outsig, 0, 0);
- }
+ padding = priv->data_width != I2S_DATA_BIT_WIDTH_16BIT ?
+ (priv->data_width != I2S_DATA_BIT_WIDTH_32BIT ? 1 : 0) : 0;
- /* TODO: repeat above function for RX channel */
+ /* Get the transfer information, accounting for any data offset */
- if (priv->config->role == I2S_ROLE_SLAVE)
- {
- if (priv->config->tx_en && !priv->config->rx_en)
- {
- /* For "tx + slave" mode, select TX signal index for ws and bck */
+ const apb_samp_t bytes_per_sample = priv->data_width / 8;
+ samp = &bfcontainer->apb->samp[bfcontainer->apb->curbyte];
+ samp_size = (bfcontainer->apb->nbytes - bfcontainer->apb->curbyte);
- esp32_gpiowrite(priv->config->ws_pin, 1);
- esp32_configgpio(priv->config->ws_pin, INPUT_FUNCTION_3);
- esp32_gpio_matrix_in(priv->config->ws_pin,
+ /* If there is no need to add padding bytes, the memcpy may be done at
+ * once. Otherwise, the operation must add the padding bytes to each
+ * sample in the internal buffer.
+ */
+
+ data_copied = 0;
+ buf = bfcontainer->buf + padding;
+
+ if (padding)
+ {
+ while (data_copied < samp_size)
+ {
+ memcpy(samp, buf, bytes_per_sample);
+ buf += (bytes_per_sample + padding);
+ samp += bytes_per_sample;
+ data_copied += bytes_per_sample;
+ }
+ }
+ else
+ {
+ memcpy(samp, buf, samp_size - data_copied);
+ buf += samp_size - data_copied;
+ samp += samp_size - data_copied;
+ data_copied += samp_size - data_copied;
+ }
+
+ /* Calculate the audio buffer size from the internal buffer size.
+ * The internal I2S buffer size is calculated from I2S_RXEOF_NUM_REG,
+ * which returns the size in number of words.
+ */
+
+ bfcontainer->apb->nbytes =
+ (getreg32(I2S_RXEOF_NUM_REG(priv->config->port)) * 4);
+
+ /* The internal buffer size must be divided by either 16 or 32 bits
+ * as the I2S peripheral aligns each sample to one of these boundaries.
+ * The result represents the number of samples on the internal buffer.
+ */
+
+ bfcontainer->apb->nbytes /=
+ ((priv->data_width <= I2S_DATA_BIT_WIDTH_16BIT ?
+ I2S_DATA_BIT_WIDTH_16BIT : I2S_DATA_BIT_WIDTH_32BIT) / 8);
+
+ /* Finally, calculate the number of bytes on the audio buffer */
+
+ bfcontainer->apb->nbytes *= bytes_per_sample;
+
+ DEBUGASSERT(bfcontainer->apb->nbytes == data_copied);
+
+ /* Perform the RX transfer done callback */
+
+ DEBUGASSERT(bfcontainer && bfcontainer->callback);
+ bfcontainer->callback(&priv->dev, bfcontainer->apb,
+ bfcontainer->arg, bfcontainer->result);
+
+ /* Release the internal buffer used by the DMA inlink */
+
+ free(bfcontainer->buf);
+
+ /* Release our reference on the audio buffer. This may very likely
+ * cause the audio buffer to be freed.
+ */
+
+ apb_free(bfcontainer->apb);
+
+ /* And release the buffer container */
+
+ i2s_buf_free(priv, bfcontainer);
+ }
+}
+#endif /* I2S_HAVE_RX */
+
+/****************************************************************************
+ * Name: i2s_configure
+ *
+ * Description:
+ * Configure I2S
+ *
+ * Input Parameters:
+ * priv - Partially initialized I2S device structure. This function
+ * will complete the I2S specific portions of the initialization
+ *
+ * Returned Value:
+ * None
+ *
+ ****************************************************************************/
+
+static void i2s_configure(struct esp32_i2s_s *priv)
+{
+ /* Set peripheral clock and clear reset */
+
+ if (priv->config->port == ESP32_I2S0)
+ {
+ modifyreg32(DPORT_PERIP_CLK_EN_REG, 0, DPORT_I2S0_CLK_EN);
+ modifyreg32(DPORT_PERIP_RST_EN_REG, 0, DPORT_I2S0_RST);
+ modifyreg32(DPORT_PERIP_RST_EN_REG, DPORT_I2S0_RST, 0);
+ }
+ else
+ {
+ modifyreg32(DPORT_PERIP_CLK_EN_REG, 0, DPORT_I2S1_CLK_EN);
+ modifyreg32(DPORT_PERIP_RST_EN_REG, 0, DPORT_I2S1_RST);
+ modifyreg32(DPORT_PERIP_RST_EN_REG, DPORT_I2S1_RST, 0);
+ }
+
+ /* I2S module general init, enable I2S clock */
+
+ if (!(getreg32(I2S_CLKM_CONF_REG(priv->config->port)) & I2S_CLK_ENA))
+ {
+ i2sinfo("Enabling I2S port clock...\n");
+ modifyreg32(I2S_CLKM_CONF_REG(priv->config->port), 0, I2S_CLK_ENA);
+ putreg32(0, I2S_CONF2_REG(priv->config->port));
+ }
+
+ /* Configure multiplexed pins as connected on the board */
+
+ /* TODO: check for loopback mode */
+
+ /* Enable TX channel */
+
+ if (priv->config->dout_pin != I2S_GPIO_UNUSED)
+ {
+ esp32_gpiowrite(priv->config->dout_pin, 1);
+ esp32_configgpio(priv->config->dout_pin, OUTPUT_FUNCTION_3);
+ esp32_gpio_matrix_out(priv->config->dout_pin,
+ priv->config->dout_outsig, 0, 0);
+ }
+
+ /* Enable RX channel */
+
+ if (priv->config->din_pin != I2S_GPIO_UNUSED)
+ {
+ esp32_configgpio(priv->config->din_pin, INPUT_FUNCTION_3);
+ esp32_gpio_matrix_in(priv->config->din_pin,
+ priv->config->din_insig, 0);
+ }
+
+ if (priv->config->role == I2S_ROLE_SLAVE)
+ {
+ if (priv->config->tx_en && !priv->config->rx_en)
+ {
+ /* For "tx + slave" mode, select TX signal index for ws and bck */
+
+ esp32_gpiowrite(priv->config->ws_pin, 1);
+ esp32_configgpio(priv->config->ws_pin, INPUT_FUNCTION_3);
+ esp32_gpio_matrix_in(priv->config->ws_pin,
priv->config->ws_out_insig, 0);
esp32_gpiowrite(priv->config->bclk_pin, 1);
@@ -1122,9 +1565,18 @@ static void i2s_configure(struct esp32_i2s_s *priv)
}
}
- /* TODO: share BCLK and WS if in full-duplex mode */
+ /* Share BCLK and WS if in full-duplex mode */
+
+ if (priv->config->tx_en && priv->config->rx_en)
+ {
+ modifyreg32(I2S_CONF_REG(priv->config->port), 0, I2S_SIG_LOOPBACK);
+ }
+ else
+ {
+ modifyreg32(I2S_CONF_REG(priv->config->port), I2S_SIG_LOOPBACK, 0);
+ }
- /* Configure the hardware to apply STD format */
+ /* Configure the TX module */
if (priv->config->tx_en)
{
@@ -1146,8 +1598,8 @@ static void i2s_configure(struct esp32_i2s_s *priv)
modifyreg32(I2S_CONF_REG(priv->config->port), I2S_TX_SLAVE_MOD, 0);
}
- /* Congfigure TX chan bit, audio data bit and mono mode.
- * On ESP32, sample_bit should equals to data_bit
+ /* Configure TX chan bit, audio data bit and mono mode.
+ * On ESP32, sample_bit should equals to data_bit.
*/
/* Set TX data width */
@@ -1207,22 +1659,157 @@ static void i2s_configure(struct esp32_i2s_s *priv)
modifyreg32(I2S_FIFO_CONF_REG(priv->config->port), 0,
I2S_TX_FIFO_MOD_FORCE_EN);
- esp32_i2s_mclkfrequency((struct i2s_dev_s *)priv,
- (priv->config->rate *
- priv->config->mclk_multiple));
+ /* The default value for the master clock frequency (MCLK frequency)
+ * can be set from the sample rate multiplied by a fixed value, known
+ * as MCLK multiplier. This multiplier, however, should be divisible
+ * by the number of bytes from a sample, i.e, for 24 bits, the
+ * multiplier should be divisible by 3. NOTE: the MCLK frequency can
+ * be adjusted on runtime, so this value remains valid only if the
+ * upper half does not implement the `i2s_mclkfrequency` method.
+ */
+
+ if (priv->config->data_width == I2S_DATA_BIT_WIDTH_24BIT)
+ {
+ priv->mclk_multiple = I2S_MCLK_MULTIPLE_384;
+ }
+ else
+ {
+ priv->mclk_multiple = I2S_MCLK_MULTIPLE_256;
+ }
+
+ esp32_i2s_mclkfrequency((struct i2s_dev_s *)priv, (priv->config->rate *
+ priv->mclk_multiple));
priv->rate = priv->config->rate;
i2s_set_clock(priv);
}
- /* TODO: check for rx enabled flag */
+ /* Configure the RX module */
+
+ if (priv->config->rx_en)
+ {
+ /* Reset I2S RX module */
+
+ modifyreg32(I2S_CONF_REG(priv->config->port), 0, I2S_RX_RESET);
+ modifyreg32(I2S_CONF_REG(priv->config->port), I2S_RX_RESET, 0);
+ modifyreg32(I2S_LC_CONF_REG(priv->config->port), 0, I2S_IN_RST);
+ modifyreg32(I2S_LC_CONF_REG(priv->config->port), I2S_IN_RST, 0);
+
+ /* Enable/disable I2S RX slave mode */
+
+ if (priv->config->role == I2S_ROLE_SLAVE)
+ {
+ modifyreg32(I2S_CONF_REG(priv->config->port), 0, I2S_RX_SLAVE_MOD);
+ }
+ else
+ {
+ /* Since BCLK and WS are shared, only TX or RX can be master. In
+ * this case, force RX as slave to avoid conflict of clock signal.
+ */
+
+ if (priv->config->tx_en)
+ {
+ modifyreg32(I2S_CONF_REG(priv->config->port), 0,
+ I2S_RX_SLAVE_MOD);
+ }
+ else
+ {
+ modifyreg32(I2S_CONF_REG(priv->config->port),
+ I2S_RX_SLAVE_MOD, 0);
+ }
+ }
+
+ /* Congfigure RX chan bit, audio data bit and mono mode.
+ * On ESP32, sample_bit should equals to data_bit.
+ */
+
+ /* Set RX data width */
+
+ priv->data_width = priv->config->data_width;
+ i2s_set_datawidth(priv);
+
+ /* Set I2S RX chan mode */
+
+ modifyreg32(I2S_CONF_CHAN_REG(priv->config->port),
+ I2S_RX_CHAN_MOD_M, 0);
+
+ /* Enable/disable RX MSB shift, the data will be read at the first
+ * BCK clock.
+ */
+
+ if (priv->config->bit_shift)
+ {
+ modifyreg32(I2S_CONF_REG(priv->config->port), 0, I2S_RX_MSB_SHIFT);
+ }
+ else
+ {
+ modifyreg32(I2S_CONF_REG(priv->config->port), I2S_RX_MSB_SHIFT, 0);
+ }
+
+ /* Configure RX WS signal width. Set to to enable receiver in PCM
+ * standard mode.
+ */
+
+ if (priv->config->ws_width == 1)
+ {
+ modifyreg32(I2S_CONF_REG(priv->config->port), 0,
+ I2S_RX_SHORT_SYNC);
+ }
+ else
+ {
+ modifyreg32(I2S_CONF_REG(priv->config->port),
+ I2S_RX_SHORT_SYNC, 0);
+ }
+
+ /* Set I2S RX right channel first */
+
+ if (priv->config->ws_pol == 1)
+ {
+ modifyreg32(I2S_CONF_REG(priv->config->port), 0,
+ I2S_RX_RIGHT_FIRST);
+ }
+ else
+ {
+ modifyreg32(I2S_CONF_REG(priv->config->port),
+ I2S_RX_RIGHT_FIRST, 0);
+ }
+
+ /* I2S RX fifo module force enable */
+
+ modifyreg32(I2S_FIFO_CONF_REG(priv->config->port), 0,
+ I2S_RX_FIFO_MOD_FORCE_EN);
+
+ /* The default value for the master clock frequency (MCLK frequency)
+ * can be set from the sample rate multiplied by a fixed value, known
+ * as MCLK multiplier. This multiplier, however, should be divisible
+ * by the number of bytes from a sample, i.e, for 24 bits, the
+ * multiplier should be divisible by 3. NOTE: the MCLK frequency can
+ * be adjusted on runtime, so this value remains valid only if the
+ * upper half does not implement the `i2s_mclkfrequency` method.
+ */
+
+ if (priv->config->data_width == I2S_DATA_BIT_WIDTH_24BIT)
+ {
+ priv->mclk_multiple = I2S_MCLK_MULTIPLE_384;
+ }
+ else
+ {
+ priv->mclk_multiple = I2S_MCLK_MULTIPLE_256;
+ }
+
+ esp32_i2s_mclkfrequency((struct i2s_dev_s *)priv, (priv->config->rate *
+ priv->mclk_multiple));
+
+ priv->rate = priv->config->rate;
+ i2s_set_clock(priv);
+ }
}
/****************************************************************************
* Name: i2s_set_datawidth
*
* Description:
- * Set the I2S TX data width.
+ * Set the I2S TX/RX data width.
*
* Input Parameters:
* priv - Initialized I2S device structure.
@@ -1234,27 +1821,63 @@ static void i2s_configure(struct esp32_i2s_s *priv)
static uint32_t i2s_set_datawidth(struct esp32_i2s_s *priv)
{
- modifyreg32(I2S_SAMPLE_RATE_CONF_REG(priv->config->port),
- I2S_TX_BITS_MOD_M, FIELD_TO_VALUE(I2S_TX_BITS_MOD,
- priv->data_width));
+#ifdef I2S_HAVE_TX
+ if (priv->config->tx_en)
+ {
+ modifyreg32(I2S_SAMPLE_RATE_CONF_REG(priv->config->port),
+ I2S_TX_BITS_MOD_M, FIELD_TO_VALUE(I2S_TX_BITS_MOD,
+ priv->data_width));
- /* Set TX FIFO operation mode */
+ /* Set TX FIFO operation mode */
- modifyreg32(I2S_FIFO_CONF_REG(priv->config->port), I2S_TX_FIFO_MOD_M,
- priv->data_width <= I2S_DATA_BIT_WIDTH_16BIT ?
- FIELD_TO_VALUE(I2S_TX_FIFO_MOD, 0 + priv->config->mono_en) :
- FIELD_TO_VALUE(I2S_TX_FIFO_MOD, 2 + priv->config->mono_en));
+ modifyreg32(I2S_FIFO_CONF_REG(priv->config->port), I2S_TX_FIFO_MOD_M,
+ priv->data_width <= I2S_DATA_BIT_WIDTH_16BIT ?
+ FIELD_TO_VALUE(I2S_TX_FIFO_MOD,
+ 0 + priv->config->mono_en) :
+ FIELD_TO_VALUE(I2S_TX_FIFO_MOD,
+ 2 + priv->config->mono_en));
- /* I2S TX MSB right enable */
+ /* I2S TX MSB right enable */
- if (priv->data_width <= I2S_DATA_BIT_WIDTH_16BIT)
- {
- modifyreg32(I2S_CONF_REG(priv->config->port), 0, I2S_TX_MSB_RIGHT);
+ if (priv->data_width <= I2S_DATA_BIT_WIDTH_16BIT)
+ {
+ modifyreg32(I2S_CONF_REG(priv->config->port), 0, I2S_TX_MSB_RIGHT);
+ }
+ else
+ {
+ modifyreg32(I2S_CONF_REG(priv->config->port), I2S_TX_MSB_RIGHT, 0);
+ }
}
- else
+#endif /* I2S_HAVE_TX */
+
+#ifdef I2S_HAVE_RX
+ if (priv->config->rx_en)
{
- modifyreg32(I2S_CONF_REG(priv->config->port), I2S_TX_MSB_RIGHT, 0);
+ modifyreg32(I2S_SAMPLE_RATE_CONF_REG(priv->config->port),
+ I2S_RX_BITS_MOD_M, FIELD_TO_VALUE(I2S_RX_BITS_MOD,
+ priv->data_width));
+
+ /* Set RX FIFO operation mode */
+
+ modifyreg32(I2S_FIFO_CONF_REG(priv->config->port), I2S_RX_FIFO_MOD_M,
+ priv->data_width <= I2S_DATA_BIT_WIDTH_16BIT ?
+ FIELD_TO_VALUE(I2S_RX_FIFO_MOD,
+ 0 + priv->config->mono_en) :
+ FIELD_TO_VALUE(I2S_RX_FIFO_MOD,
+ 2 + priv->config->mono_en));
+
+ /* I2S RX MSB right enable */
+
+ if (priv->data_width <= I2S_DATA_BIT_WIDTH_16BIT)
+ {
+ modifyreg32(I2S_CONF_REG(priv->config->port), 0, I2S_RX_MSB_RIGHT);
+ }
+ else
+ {
+ modifyreg32(I2S_CONF_REG(priv->config->port), I2S_RX_MSB_RIGHT, 0);
+ }
}
+#endif /* I2S_HAVE_RX */
return priv->data_width;
}
@@ -1278,13 +1901,13 @@ static uint32_t i2s_set_clock(struct esp32_i2s_s *priv)
uint32_t rate;
uint32_t bclk;
uint32_t mclk;
- uint16_t bclk_div;
uint32_t sclk;
uint32_t mclk_div;
int denominator;
int numerator;
uint32_t regval;
uint32_t freq_diff;
+ uint16_t bclk_div;
/* TODO: provide APLL clock support */
@@ -1320,8 +1943,8 @@ static uint32_t i2s_set_clock(struct esp32_i2s_s *priv)
mclk_div = sclk / mclk;
- i2sinfo("Clock division info: [sclk]%"PRIu32" Hz [mdiv] %d "
- "[mclk] %"PRIu32" Hz [bdiv] %d [bclk] %"PRIu32" Hz\n",
+ i2sinfo("Clock division info: [sclk]%" PRIu32 " Hz [mdiv] %d "
+ "[mclk] %" PRIu32 " Hz [bdiv] %d [bclk] %" PRIu32 " Hz\n",
sclk, mclk_div, mclk, bclk_div, bclk);
freq_diff = abs((int)sclk - (int)(mclk * mclk_div));
@@ -1343,7 +1966,7 @@ static uint32_t i2s_set_clock(struct esp32_i2s_s *priv)
}
else
{
- uint32_t min = ~0;
+ uint32_t min = UINT32_MAX;
for (int a = 2; a <= I2S_LL_MCLK_DIVIDER_MAX; a++)
{
@@ -1367,7 +1990,7 @@ static uint32_t i2s_set_clock(struct esp32_i2s_s *priv)
}
}
- i2sinfo("Clock register: [mclk] %"PRIu32" Hz [numerator] %d "
+ i2sinfo("Clock register: [mclk] %" PRIu32 " Hz [numerator] %d "
"[denominator] %d\n", mclk, numerator, denominator);
regval = getreg32(I2S_CLKM_CONF_REG(priv->config->port));
@@ -1379,11 +2002,19 @@ static uint32_t i2s_set_clock(struct esp32_i2s_s *priv)
regval |= FIELD_TO_VALUE(I2S_CLKM_DIV_A, denominator);
putreg32(regval, I2S_CLKM_CONF_REG(priv->config->port));
- /* Set I2S tx bck div num */
+ /* Set I2S TX bck div num */
+#ifdef I2S_HAVE_TX
modifyreg32(I2S_SAMPLE_RATE_CONF_REG(priv->config->port),
I2S_TX_BCK_DIV_NUM_M,
FIELD_TO_VALUE(I2S_TX_BCK_DIV_NUM, bclk_div));
+#endif /* I2S_HAVE_TX */
+
+#ifdef I2S_HAVE_RX
+ modifyreg32(I2S_SAMPLE_RATE_CONF_REG(priv->config->port),
+ I2S_RX_BCK_DIV_NUM_M,
+ FIELD_TO_VALUE(I2S_RX_BCK_DIV_NUM, bclk_div));
+#endif /* I2S_HAVE_RX */
/* Returns the actual sample rate */
@@ -1411,54 +2042,57 @@ static uint32_t i2s_set_clock(struct esp32_i2s_s *priv)
#ifdef I2S_HAVE_TX
static void i2s_tx_channel_start(struct esp32_i2s_s *priv)
{
- if (priv->tx_started)
+ if (priv->config->tx_en)
{
- i2swarn("TX channel of port %d was previously started\n",
- priv->config->port);
- return;
- }
+ if (priv->tx_started)
+ {
+ i2swarn("TX channel of port %d was previously started\n",
+ priv->config->port);
+ return;
+ }
- /* Reset the TX channel */
+ /* Reset the TX channel */
- modifyreg32(I2S_CONF_REG(priv->config->port), 0, I2S_TX_RESET);
- modifyreg32(I2S_CONF_REG(priv->config->port), I2S_TX_RESET, 0);
+ modifyreg32(I2S_CONF_REG(priv->config->port), 0, I2S_TX_RESET);
+ modifyreg32(I2S_CONF_REG(priv->config->port), I2S_TX_RESET, 0);
- /* Reset the DMA operation */
+ /* Reset the DMA operation */
- modifyreg32(I2S_LC_CONF_REG(priv->config->port), 0, I2S_OUT_RST);
- modifyreg32(I2S_LC_CONF_REG(priv->config->port), I2S_OUT_RST, 0);
+ modifyreg32(I2S_LC_CONF_REG(priv->config->port), 0, I2S_OUT_RST);
+ modifyreg32(I2S_LC_CONF_REG(priv->config->port), I2S_OUT_RST, 0);
- /* Reset TX FIFO */
+ /* Reset TX FIFO */
- modifyreg32(I2S_CONF_REG(priv->config->port), 0, I2S_TX_FIFO_RESET);
- modifyreg32(I2S_CONF_REG(priv->config->port), I2S_TX_FIFO_RESET, 0);
+ modifyreg32(I2S_CONF_REG(priv->config->port), 0, I2S_TX_FIFO_RESET);
+ modifyreg32(I2S_CONF_REG(priv->config->port), I2S_TX_FIFO_RESET, 0);
- /* Enable DMA interrupt */
+ /* Enable DMA interrupt */
- up_enable_irq(priv->config->irq);
+ up_enable_irq(priv->config->irq);
- modifyreg32(I2S_INT_ENA_REG(priv->config->port), UINT32_MAX,
- I2S_OUT_EOF_INT_ENA);
+ modifyreg32(I2S_INT_ENA_REG(priv->config->port), 0,
+ I2S_OUT_EOF_INT_ENA);
- /* Enable DMA operation mode */
+ /* Enable DMA operation mode */
- modifyreg32(I2S_FIFO_CONF_REG(priv->config->port), 0, I2S_DSCR_EN);
+ modifyreg32(I2S_FIFO_CONF_REG(priv->config->port), 0, I2S_DSCR_EN);
- /* Unset the DMA outlink */
+ /* Unset the DMA outlink */
- putreg32(0, I2S_OUT_LINK_REG(priv->config->port));
+ putreg32(0, I2S_OUT_LINK_REG(priv->config->port));
- priv->tx_started = true;
+ priv->tx_started = true;
- i2sinfo("Started TX channel of port %d\n", priv->config->port);
+ i2sinfo("Started TX channel of port %d\n", priv->config->port);
+ }
}
#endif /* I2S_HAVE_TX */
/****************************************************************************
- * Name: i2s_tx_channel_stop
+ * Name: i2s_rx_channel_start
*
* Description:
- * Stop TX channel for the I2S port
+ * Start RX channel for the I2S port
*
* Input Parameters:
* priv - Initialized I2S device structure.
@@ -1468,75 +2102,217 @@ static void i2s_tx_channel_start(struct esp32_i2s_s *priv)
*
****************************************************************************/
-#ifdef I2S_HAVE_TX
-static void i2s_tx_channel_stop(struct esp32_i2s_s *priv)
+#ifdef I2S_HAVE_RX
+static void i2s_rx_channel_start(struct esp32_i2s_s *priv)
{
- if (!priv->tx_started)
+ if (priv->config->rx_en)
{
- i2swarn("TX channel of port %d was previously stopped\n",
- priv->config->port);
- return;
- }
+ if (priv->rx_started)
+ {
+ i2swarn("RX channel of port %d was previously started\n",
+ priv->config->port);
+ return;
+ }
+
+ /* Reset the RX channel */
+
+ modifyreg32(I2S_CONF_REG(priv->config->port), 0, I2S_RX_RESET);
+ modifyreg32(I2S_CONF_REG(priv->config->port), I2S_RX_RESET, 0);
+
+ /* Reset the DMA operation */
- /* Stop TX channel */
+ modifyreg32(I2S_LC_CONF_REG(priv->config->port), 0, I2S_IN_RST);
+ modifyreg32(I2S_LC_CONF_REG(priv->config->port), I2S_IN_RST, 0);
- modifyreg32(I2S_CONF_REG(priv->config->port), I2S_TX_START, 0);
+ /* Reset RX FIFO */
- /* Stop outlink */
+ modifyreg32(I2S_CONF_REG(priv->config->port), 0, I2S_RX_FIFO_RESET);
+ modifyreg32(I2S_CONF_REG(priv->config->port), I2S_RX_FIFO_RESET, 0);
- modifyreg32(I2S_OUT_LINK_REG(priv->config->port), I2S_OUTLINK_START,
- I2S_OUTLINK_STOP);
+ /* Enable DMA interrupt */
- /* Disable DMA interrupt */
+ up_enable_irq(priv->config->irq);
- modifyreg32(I2S_INT_ENA_REG(priv->config->port), UINT32_MAX, 0);
+ modifyreg32(I2S_INT_ENA_REG(priv->config->port), 0,
+ I2S_IN_SUC_EOF_INT_ENA);
- /* Disable DMA operation mode */
+ /* Enable DMA operation mode */
- modifyreg32(I2S_FIFO_CONF_REG(priv->config->port), I2S_DSCR_EN, 0);
+ modifyreg32(I2S_FIFO_CONF_REG(priv->config->port), 0, I2S_DSCR_EN);
- up_disable_irq(priv->config->irq);
+ /* Unset the DMA outlink */
- priv->tx_started = false;
+ putreg32(0, I2S_IN_LINK_REG(priv->config->port));
- i2sinfo("Stopped TX channel of port %d\n", priv->config->port);
+ priv->rx_started = true;
+
+ i2sinfo("Started RX channel of port %d\n", priv->config->port);
+ }
}
-#endif /* I2S_HAVE_TX */
+#endif /* I2S_HAVE_RX */
/****************************************************************************
- * Name: esp32_i2s_interrupt
+ * Name: i2s_tx_channel_stop
*
* Description:
- * Common I2S DMA interrupt handler
+ * Stop TX channel for the I2S port
*
* Input Parameters:
- * arg - i2s controller private data
+ * priv - Initialized I2S device structure.
*
* Returned Value:
- * Standard interrupt return value.
+ * None
*
****************************************************************************/
-static int esp32_i2s_interrupt(int irq, void *context, void *arg)
+#ifdef I2S_HAVE_TX
+static void i2s_tx_channel_stop(struct esp32_i2s_s *priv)
{
- struct esp32_i2s_s *priv = (struct esp32_i2s_s *)arg;
- struct esp32_dmadesc_s *cur = NULL;
+ if (priv->config->tx_en)
+ {
+ if (!priv->tx_started)
+ {
+ i2swarn("TX channel of port %d was previously stopped\n",
+ priv->config->port);
+ return;
+ }
- uint32_t status = getreg32(I2S_INT_ST_REG(priv->config->port));
+ /* Stop TX channel */
- putreg32(UINT32_MAX, I2S_INT_CLR_REG(priv->config->port));
+ modifyreg32(I2S_CONF_REG(priv->config->port), I2S_TX_START, 0);
- if (status & I2S_OUT_EOF_INT_ST)
- {
- cur = (struct esp32_dmadesc_s *)
- getreg32(I2S_OUT_EOF_DES_ADDR_REG(priv->config->port));
+ /* Stop outlink */
- /* Schedule completion of the transfer to occur on the worker thread */
+ modifyreg32(I2S_OUT_LINK_REG(priv->config->port), I2S_OUTLINK_START,
+ I2S_OUTLINK_STOP);
- i2s_tx_schedule(priv, cur);
- }
+ /* Disable DMA interrupt */
- return 0;
+ modifyreg32(I2S_INT_ENA_REG(priv->config->port),
+ I2S_OUT_EOF_INT_ENA, 0);
+
+ /* Disable DMA operation mode */
+
+ modifyreg32(I2S_FIFO_CONF_REG(priv->config->port), I2S_DSCR_EN, 0);
+
+ up_disable_irq(priv->config->irq);
+
+ priv->tx_started = false;
+
+ i2sinfo("Stopped TX channel of port %d\n", priv->config->port);
+ }
+}
+#endif /* I2S_HAVE_TX */
+
+/****************************************************************************
+ * Name: i2s_rx_channel_stop
+ *
+ * Description:
+ * Stop RX channel for the I2S port
+ *
+ * Input Parameters:
+ * priv - Initialized I2S device structure.
+ *
+ * Returned Value:
+ * None
+ *
+ ****************************************************************************/
+
+#ifdef I2S_HAVE_RX
+static void i2s_rx_channel_stop(struct esp32_i2s_s *priv)
+{
+ if (priv->config->rx_en)
+ {
+ if (!priv->rx_started)
+ {
+ i2swarn("RX channel of port %d was previously stopped\n",
+ priv->config->port);
+ return;
+ }
+
+ /* Stop RX channel */
+
+ modifyreg32(I2S_CONF_REG(priv->config->port), I2S_RX_START, 0);
+
+ /* Stop outlink */
+
+ modifyreg32(I2S_IN_LINK_REG(priv->config->port), I2S_INLINK_START,
+ I2S_INLINK_STOP);
+
+ /* Disable DMA interrupt */
+
+ modifyreg32(I2S_INT_ENA_REG(priv->config->port),
+ I2S_IN_SUC_EOF_INT_ENA, 0);
+
+ /* Disable DMA operation mode */
+
+ modifyreg32(I2S_FIFO_CONF_REG(priv->config->port), I2S_DSCR_EN, 0);
+
+ up_disable_irq(priv->config->irq);
+
+ priv->rx_started = false;
+
+ i2sinfo("Stopped RX channel of port %d\n", priv->config->port);
+ }
+}
+#endif /* I2S_HAVE_RX */
+
+/****************************************************************************
+ * Name: esp32_i2s_interrupt
+ *
+ * Description:
+ * Common I2S DMA interrupt handler
+ *
+ * Input Parameters:
+ * irq - Number of the IRQ that generated the interrupt
+ * context - Interrupt register state save info
+ * arg - I2S controller private data
+ *
+ * Returned Value:
+ * Standard interrupt return value.
+ *
+ ****************************************************************************/
+
+static int esp32_i2s_interrupt(int irq, void *context, void *arg)
+{
+ struct esp32_i2s_s *priv = (struct esp32_i2s_s *)arg;
+ struct esp32_dmadesc_s *cur = NULL;
+
+ uint32_t status = getreg32(I2S_INT_ST_REG(priv->config->port));
+
+ putreg32(UINT32_MAX, I2S_INT_CLR_REG(priv->config->port));
+
+#ifdef I2S_HAVE_TX
+ if (priv->config->tx_en)
+ {
+ if (status & I2S_OUT_EOF_INT_ST)
+ {
+ cur = (struct esp32_dmadesc_s *)
+ getreg32(I2S_OUT_EOF_DES_ADDR_REG(priv->config->port));
+
+ /* Schedule completion of the transfer on the worker thread */
+
+ i2s_tx_schedule(priv, cur);
+ }
+ }
+#endif /* I2S_HAVE_TX */
+
+#ifdef I2S_HAVE_RX
+ if (priv->config->rx_en)
+ {
+ if (status & I2S_IN_SUC_EOF_INT_ST)
+ {
+ cur = (struct esp32_dmadesc_s *)
+ getreg32(I2S_IN_EOF_DES_ADDR_REG(priv->config->port));
+
+ /* Schedule completion of the transfer on the worker thread */
+
+ i2s_rx_schedule(priv, cur);
+ }
+ }
+#endif /* I2S_HAVE_RX */
+
+ return 0;
}
/****************************************************************************
@@ -1590,18 +2366,60 @@ static uint32_t esp32_i2s_mclkfrequency(struct i2s_dev_s *dev,
*
****************************************************************************/
+#ifdef I2S_HAVE_TX
static int esp32_i2s_txchannels(struct i2s_dev_s *dev, uint8_t channels)
{
struct esp32_i2s_s *priv = (struct esp32_i2s_s *)dev;
- if (channels != 1 && channels != 2)
+ if (priv->config->tx_en)
{
- return -EINVAL;
+ if (channels != 1 && channels != 2)
+ {
+ return -EINVAL;
+ }
+
+ priv->channels = channels;
+ return OK;
}
- priv->channels = channels;
- return OK;
+ return -ENOTTY;
+}
+#endif /* I2S_HAVE_TX */
+
+/****************************************************************************
+ * Name: esp32_i2s_rxchannels
+ *
+ * Description:
+ * Set the I2S RX number of channels.
+ *
+ * Input Parameters:
+ * dev - Device-specific state data
+ * channels - The I2S numbers of channels
+ *
+ * Returned Value:
+ * OK on success; a negated errno value on failure.
+ *
+ ****************************************************************************/
+
+#ifdef I2S_HAVE_RX
+static int esp32_i2s_rxchannels(struct i2s_dev_s *dev, uint8_t channels)
+{
+ struct esp32_i2s_s *priv = (struct esp32_i2s_s *)dev;
+
+ if (priv->config->rx_en)
+ {
+ if (channels != 1 && channels != 2)
+ {
+ return -EINVAL;
+ }
+
+ priv->channels = channels;
+ return OK;
+ }
+
+ return -ENOTTY;
}
+#endif /* I2S_HAVE_RX */
/****************************************************************************
* Name: esp32_i2s_txsamplerate
@@ -1621,20 +2439,64 @@ static int esp32_i2s_txchannels(struct i2s_dev_s *dev, uint8_t channels)
*
****************************************************************************/
+#ifdef I2S_HAVE_TX
static uint32_t esp32_i2s_txsamplerate(struct i2s_dev_s *dev, uint32_t rate)
{
struct esp32_i2s_s *priv = (struct esp32_i2s_s *)dev;
- i2s_tx_channel_stop(priv);
+ if (priv->config->tx_en)
+ {
+ i2s_tx_channel_stop(priv);
+
+ priv->rate = rate;
- priv->rate = rate;
+ rate = i2s_set_clock(priv);
- rate = i2s_set_clock(priv);
+ i2s_tx_channel_start(priv);
- i2s_tx_channel_start(priv);
+ return rate;
+ }
- return rate;
+ return 0;
}
+#endif /* I2S_HAVE_TX */
+
+/****************************************************************************
+ * Name: esp32_i2s_rxsamplerate
+ *
+ * Description:
+ * Set the I2S RX sample rate.
+ *
+ * Input Parameters:
+ * dev - Device-specific state data
+ * rate - The I2S sample rate in samples (not bits) per second
+ *
+ * Returned Value:
+ * Returns the resulting bitrate
+ *
+ ****************************************************************************/
+
+#ifdef I2S_HAVE_RX
+static uint32_t esp32_i2s_rxsamplerate(struct i2s_dev_s *dev, uint32_t rate)
+{
+ struct esp32_i2s_s *priv = (struct esp32_i2s_s *)dev;
+
+ if (priv->config->rx_en)
+ {
+ i2s_rx_channel_stop(priv);
+
+ priv->rate = rate;
+
+ rate = i2s_set_clock(priv);
+
+ i2s_rx_channel_start(priv);
+
+ return rate;
+ }
+
+ return 0;
+}
+#endif /* I2S_HAVE_RX */
/****************************************************************************
* Name: esp32_i2s_txdatawidth
@@ -1652,20 +2514,65 @@ static uint32_t esp32_i2s_txsamplerate(struct i2s_dev_s *dev, uint32_t rate)
*
****************************************************************************/
+#ifdef I2S_HAVE_TX
static uint32_t esp32_i2s_txdatawidth(struct i2s_dev_s *dev, int bits)
{
struct esp32_i2s_s *priv = (struct esp32_i2s_s *)dev;
- i2s_tx_channel_stop(priv);
+ if (priv->config->tx_en)
+ {
+ i2s_tx_channel_stop(priv);
+
+ priv->data_width = bits;
+
+ i2s_set_datawidth(priv);
+
+ i2s_tx_channel_start(priv);
+
+ return bits;
+ }
+
+ return 0;
+}
+#endif /* I2S_HAVE_TX */
+
+/****************************************************************************
+ * Name: esp32_i2s_rxdatawidth
+ *
+ * Description:
+ * Set the I2S RX data width. The RX bitrate is determined by
+ * sample_rate * data_width.
+ *
+ * Input Parameters:
+ * dev - Device-specific state data
+ * width - The I2S data with in bits.
+ *
+ * Returned Value:
+ * Returns the resulting data width
+ *
+ ****************************************************************************/
+
+#ifdef I2S_HAVE_RX
+static uint32_t esp32_i2s_rxdatawidth(struct i2s_dev_s *dev, int bits)
+{
+ struct esp32_i2s_s *priv = (struct esp32_i2s_s *)dev;
+
+ if (priv->config->rx_en)
+ {
+ i2s_rx_channel_stop(priv);
+
+ priv->data_width = bits;
- priv->data_width = bits;
+ i2s_set_datawidth(priv);
- i2s_set_datawidth(priv);
+ i2s_rx_channel_start(priv);
- i2s_tx_channel_start(priv);
+ return bits;
+ }
- return bits;
+ return 0;
}
+#endif /* I2S_HAVE_RX */
/****************************************************************************
* Name: esp32_i2s_send
@@ -1690,95 +2597,281 @@ static uint32_t esp32_i2s_txdatawidth(struct i2s_dev_s *dev, int bits)
*
****************************************************************************/
+#ifdef I2S_HAVE_TX
static int esp32_i2s_send(struct i2s_dev_s *dev, struct ap_buffer_s *apb,
+ i2s_callback_t callback, void *arg,
+ uint32_t timeout)
+{
+ struct esp32_i2s_s *priv = (struct esp32_i2s_s *)dev;
+
+ if (priv->config->tx_en)
+ {
+ struct esp32_buffer_s *bfcontainer;
+ int ret = OK;
+ uint32_t nbytes;
+ uint32_t nsamp;
+
+ /* Check audio buffer data size */
+
+ nbytes = (apb->nbytes - apb->curbyte) + priv->tx.carry.bytes;
+
+ /* If data width is 8, it is necessary to use a word of 16 bits;
+ * If data width is 24, it is necessary to use a word of 32 bits;
+ */
+
+ nsamp = nbytes / (priv->data_width / 8);
+ nbytes = nsamp *
+ ((priv->data_width <= I2S_DATA_BIT_WIDTH_16BIT ?
+ I2S_DATA_BIT_WIDTH_16BIT : I2S_DATA_BIT_WIDTH_32BIT) / 8);
+
+ /* ESP32's I2S peripheral always consider two channels. If the source
+ * is mono, it is necessary to copy zero-ed data between each sample.
+ */
+
+ nbytes *= priv->channels == 1 ? 2 : 1;
+
+ /* Ensure nbytes is 4-byte aligned */
+
+ nbytes = ALIGN_UP(nbytes, sizeof(uintptr_t));
+
+ if (nbytes > (ESP32_DMA_DATALEN_MAX * I2S_DMADESC_NUM))
+ {
+ i2serr("Required buffer size can't fit into DMA outlink "
+ "(exceeds in %" PRIu32 " bytes). Try to increase the "
+ "number of the DMA descriptors (CONFIG_I2S_DMADESC_NUM).",
+ nbytes - (ESP32_DMA_DATALEN_MAX * I2S_DMADESC_NUM));
+ return -EFBIG;
+ }
+
+ /* Allocate a buffer container in advance */
+
+ bfcontainer = i2s_buf_allocate(priv);
+ DEBUGASSERT(bfcontainer);
+
+ /* Get exclusive access to the I2S driver data */
+
+ ret = nxmutex_lock(&priv->lock);
+ if (ret < 0)
+ {
+ goto errout_with_buf;
+ }
+
+ /* Add a reference to the audio buffer */
+
+ apb_reference(apb);
+
+ /* Initialize the buffer container structure */
+
+ bfcontainer->callback = callback;
+ bfcontainer->timeout = timeout;
+ bfcontainer->arg = arg;
+ bfcontainer->apb = apb;
+ bfcontainer->nbytes = nbytes;
+ bfcontainer->result = -EBUSY;
+
+ ret = i2s_txdma_setup(priv, bfcontainer);
+
+ if (ret != OK)
+ {
+ goto errout_with_buf;
+ }
+
+ i2sinfo("Queued %d bytes into DMA buffers\n", apb->nbytes);
+ i2s_dump_buffer("Audio pipeline buffer:", &apb->samp[apb->curbyte],
+ apb->nbytes - apb->curbyte);
+
+ nxmutex_unlock(&priv->lock);
+
+ return OK;
+
+errout_with_buf:
+ nxmutex_unlock(&priv->lock);
+ i2s_buf_free(priv, bfcontainer);
+ return ret;
+ }
+
+ return -ENOTTY;
+}
+#endif /* I2S_HAVE_TX */
+
+/****************************************************************************
+ * Name: esp32_i2s_receive
+ *
+ * Description:
+ * Receive a block of data on I2S.
+ *
+ * Input Parameters:
+ * dev - Device-specific state data
+ * apb - A pointer to the audio buffer in which to receive data
+ * callback - A user provided callback function that will be called at
+ * the completion of the transfer.
+ * arg - An opaque argument that will be provided to the callback
+ * when the transfer complete
+ * timeout - The timeout value to use. The transfer will be cancelled
+ * and an ETIMEDOUT error will be reported if this timeout
+ * elapsed without completion of the DMA transfer. Units
+ * are system clock ticks. Zero means no timeout.
+ *
+ * Returned Value:
+ * OK on success; a negated errno value on failure.
+ *
+ ****************************************************************************/
+
+#ifdef I2S_HAVE_RX
+static int esp32_i2s_receive(struct i2s_dev_s *dev, struct ap_buffer_s *apb,
i2s_callback_t callback, void *arg,
uint32_t timeout)
{
struct esp32_i2s_s *priv = (struct esp32_i2s_s *)dev;
- struct esp32_buffer_s *bfcontainer;
- int ret = OK;
- uint32_t nbytes;
- uint32_t nsamp;
- /* Check audio buffer data size */
+ if (priv->config->rx_en)
+ {
+ struct esp32_buffer_s *bfcontainer;
+ int ret = OK;
+ uint32_t nbytes;
+ uint32_t nsamp;
- nbytes = (apb->nbytes - apb->curbyte) + priv->tx.carry.bytes;
+ /* Check max audio buffer data size */
- /* If data width is 8, it is necessary to use a word of 16 bits;
- * If data width is 24, it is necessary to a word of 32 bits;
- */
+ nbytes = apb->nmaxbytes;
+
+ /* If data width is 8, it is necessary to use a word of 16 bits;
+ * If data width is 24, it is necessary to use a word of 32 bits;
+ */
- nsamp = nbytes / (priv->data_width / 8);
- nbytes = nsamp *
- ((priv->data_width <= I2S_DATA_BIT_WIDTH_16BIT ?
- I2S_DATA_BIT_WIDTH_16BIT : I2S_DATA_BIT_WIDTH_32BIT) / 8);
+ nsamp = nbytes / (priv->data_width / 8);
+ nbytes = nsamp *
+ ((priv->data_width <= I2S_DATA_BIT_WIDTH_16BIT ?
+ I2S_DATA_BIT_WIDTH_16BIT : I2S_DATA_BIT_WIDTH_32BIT) / 8);
- /* ESP32's I2S peripheral always consider two channels. If the source is
- * mono, it is necessary to copy zero-ed data between each sample.
- */
+ if (nbytes > (ESP32_DMA_DATALEN_MAX * I2S_DMADESC_NUM))
+ {
+ i2serr("Required buffer size can't fit into DMA inlink "
+ "(exceeds in %" PRIu32 " bytes). Try to increase the "
+ "number of the DMA descriptors (CONFIG_I2S_DMADESC_NUM).",
+ nbytes - (ESP32_DMA_DATALEN_MAX * I2S_DMADESC_NUM));
+ return -EFBIG;
+ }
- nbytes *= priv->channels == 1 ? 2 : 1;
+ /* Allocate a buffer container in advance */
- /* Ensure nbytes is 4-byte aligned */
+ bfcontainer = i2s_buf_allocate(priv);
+ DEBUGASSERT(bfcontainer);
- nbytes = ALIGN_UP(nbytes, sizeof(uintptr_t));
+ /* Get exclusive access to the I2S driver data */
- if (nbytes > (ESP32_DMA_DATALEN_MAX * I2S_DMADESC_NUM))
- {
- i2serr("Required buffer size can not be fitted into DMA outlink "
- "(exceeds in %" PRIu32 " bytes). Try to increase the "
- "number of the DMA descriptors (CONFIG_I2S_DMADESC_NUM).",
- nbytes - (ESP32_DMA_DATALEN_MAX * I2S_DMADESC_NUM));
- return -EFBIG;
+ ret = nxmutex_lock(&priv->lock);
+ if (ret < 0)
+ {
+ goto errout_with_buf;
+ }
+
+ /* Add a reference to the audio buffer */
+
+ apb_reference(apb);
+
+ /* Initialize the buffer container structure */
+
+ bfcontainer->callback = callback;
+ bfcontainer->timeout = timeout;
+ bfcontainer->arg = arg;
+ bfcontainer->apb = apb;
+ bfcontainer->nbytes = nbytes;
+ bfcontainer->result = -EBUSY;
+
+ ret = i2s_rxdma_setup(priv, bfcontainer);
+
+ if (ret != OK)
+ {
+ goto errout_with_buf;
+ }
+
+ i2sinfo("Prepared %d bytes to receive DMA buffers\n", apb->nmaxbytes);
+ i2s_dump_buffer("Audio pipeline buffer:", &apb->samp[apb->curbyte],
+ apb->nbytes - apb->curbyte);
+
+ nxmutex_unlock(&priv->lock);
+
+ return OK;
+
+errout_with_buf:
+ nxmutex_unlock(&priv->lock);
+ i2s_buf_free(priv, bfcontainer);
+ return ret;
}
- /* Allocate a buffer container in advance */
+ return -ENOTTY;
+}
+#endif /* I2S_HAVE_RX */
- bfcontainer = i2s_buf_allocate(priv);
- DEBUGASSERT(bfcontainer);
+/****************************************************************************
+ * Name: esp32_i2s_ioctl
+ *
+ * Description:
+ * Perform a device ioctl
+ *
+ ****************************************************************************/
- /* Get exclusive access to the I2S driver data */
+static int esp32_i2s_ioctl(struct i2s_dev_s *dev, int cmd,
+ unsigned long arg)
+{
+ struct audio_buf_desc_s *bufdesc;
+ int ret = -ENOTTY;
- ret = nxmutex_lock(&priv->lock);
- if (ret < 0)
+ switch (cmd)
{
- goto errout_with_buf;
- }
+ /* AUDIOIOC_START - Start the audio stream.
+ *
+ * ioctl argument: Audio session
+ */
- /* Add a reference to the audio buffer */
+ case AUDIOIOC_START:
+ {
+ i2sinfo("AUDIOIOC_START\n");
- apb_reference(apb);
+ ret = OK;
+ }
+ break;
- /* Initialize the buffer container structure */
+ /* AUDIOIOC_ALLOCBUFFER - Allocate an audio buffer
+ *
+ * ioctl argument: pointer to an audio_buf_desc_s structure
+ */
- bfcontainer->callback = callback;
- bfcontainer->timeout = timeout;
- bfcontainer->arg = arg;
- bfcontainer->apb = apb;
- bfcontainer->nbytes = nbytes;
- bfcontainer->result = -EBUSY;
+ case AUDIOIOC_ALLOCBUFFER:
+ {
+ i2sinfo("AUDIOIOC_ALLOCBUFFER\n");
- ret = i2s_txdma_setup(priv, bfcontainer);
- if (ret != OK)
- {
- goto errout_with_buf;
- }
+ bufdesc = (struct audio_buf_desc_s *) arg;
+ ret = apb_alloc(bufdesc);
+ }
+ break;
- i2sinfo("Queued %d bytes into DMA buffers\n", apb->nbytes);
- i2s_dump_buffer("Audio pipeline buffer:", &apb->samp[apb->curbyte],
- apb->nbytes - apb->curbyte);
+ /* AUDIOIOC_FREEBUFFER - Free an audio buffer
+ *
+ * ioctl argument: pointer to an audio_buf_desc_s structure
+ */
- nxmutex_unlock(&priv->lock);
- return OK;
+ case AUDIOIOC_FREEBUFFER:
+ {
+ i2sinfo("AUDIOIOC_FREEBUFFER\n");
+
+ bufdesc = (struct audio_buf_desc_s *) arg;
+ DEBUGASSERT(bufdesc->u.buffer != NULL);
+ apb_free(bufdesc->u.buffer);
+ ret = sizeof(struct audio_buf_desc_s);
+ }
+ break;
+
+ default:
+ break;
+ }
-errout_with_buf:
- nxmutex_unlock(&priv->lock);
- i2s_buf_free(priv, bfcontainer);
return ret;
}
/****************************************************************************
- * Name: esp32_i2sdma_setup
+ * Name: i2s_dma_setup
*
* Description:
* Configure the DMA for the I2S peripheral
@@ -1793,7 +2886,7 @@ errout_with_buf:
*
****************************************************************************/
-static int esp32_i2sdma_setup(struct esp32_i2s_s *priv)
+static int i2s_dma_setup(struct esp32_i2s_s *priv)
{
int ret;
@@ -1865,8 +2958,6 @@ struct i2s_dev_s *esp32_i2sbus_initialize(int port)
return NULL;
}
- priv->tx_started = false;
-
flags = spin_lock_irqsave(&priv->slock);
i2s_configure(priv);
@@ -1879,7 +2970,7 @@ struct i2s_dev_s *esp32_i2sbus_initialize(int port)
goto err;
}
- ret = esp32_i2sdma_setup(priv);
+ ret = i2s_dma_setup(priv);
if (ret < 0)
{
goto err;
@@ -1888,9 +2979,23 @@ struct i2s_dev_s *esp32_i2sbus_initialize(int port)
#ifdef I2S_HAVE_TX
/* Start TX channel */
- i2s_tx_channel_start(priv);
+ if (priv->config->tx_en)
+ {
+ priv->tx_started = false;
+ i2s_tx_channel_start(priv);
+ }
#endif /* I2S_HAVE_TX */
+#ifdef I2S_HAVE_RX
+ /* Start RX channel */
+
+ if (priv->config->rx_en)
+ {
+ priv->rx_started = false;
+ i2s_rx_channel_start(priv);
+ }
+#endif /* I2S_HAVE_RX */
+
spin_unlock_irqrestore(&priv->slock, flags);
/* Success exit */
@@ -1902,7 +3007,7 @@ struct i2s_dev_s *esp32_i2sbus_initialize(int port)
/* Failure exit */
err:
- spin_unlock_irqrestore(&priv->lock, flags);
+ spin_unlock_irqrestore(&priv->slock, flags);
return NULL;
}
diff --git a/arch/xtensa/src/esp32/esp32_i2s.h b/arch/xtensa/src/esp32/esp32_i2s.h
index 6ee13bb279..ab6235f059 100644
--- a/arch/xtensa/src/esp32/esp32_i2s.h
+++ b/arch/xtensa/src/esp32/esp32_i2s.h
@@ -41,13 +41,8 @@ extern "C"
#ifdef CONFIG_ESP32_I2S
-#ifdef CONFIG_ESP32_I2S0
- #define ESP32_I2S0 0
-#endif /* CONFIG_ESP32_I2S0 */
-
-#ifdef CONFIG_ESP32_I2S1
- #define ESP32_I2S1 1
-#endif /* CONFIG_ESP32_I2S1 */
+#define ESP32_I2S0 0
+#define ESP32_I2S1 1
/****************************************************************************
* Public Function Prototypes
diff --git a/boards/xtensa/esp32/common/src/esp32_board_i2sdev.c b/boards/xtensa/esp32/common/src/esp32_board_i2sdev.c
index ad5c634f31..c436f950db 100644
--- a/boards/xtensa/esp32/common/src/esp32_board_i2sdev.c
+++ b/boards/xtensa/esp32/common/src/esp32_board_i2sdev.c
@@ -37,8 +37,8 @@
#include "esp32_i2s.h"
-#if defined(CONFIG_ESP32_I2S0) && !defined(CONFIG_AUDIO_CS4344) && \
-!defined(CONFIG_AUDIO_ES8388) || defined(CONFIG_ESP32_I2S1)
+#if defined(CONFIG_ESP32_I2S0) && !defined(CONFIG_AUDIO_CS4344) || \
+ defined(CONFIG_ESP32_I2S1)
/****************************************************************************
* Public Functions
@@ -54,7 +54,9 @@
* number.
*
* Input Parameters:
- * port - The I2S port used for the device
+ * port - The I2S port used for the device
+ * enable_tx - Register device as TX if true
+ * enable_rx - Register device as RX if true
*
* Returned Value:
* Zero is returned on success. Otherwise, a negated errno value is
@@ -62,12 +64,11 @@
*
****************************************************************************/
-int board_i2sdev_initialize(int port)
+int board_i2sdev_initialize(int port, bool enable_tx, bool enable_rx)
{
struct audio_lowerhalf_s *audio_i2s;
- struct audio_lowerhalf_s *pcm;
struct i2s_dev_s *i2s;
- char devname[12];
+ char devname[8];
int ret;
ainfo("Initializing I2S\n");
@@ -75,7 +76,7 @@ int board_i2sdev_initialize(int port)
i2s = esp32_i2sbus_initialize(port);
#ifdef CONFIG_AUDIO_I2SCHAR
- ret = i2schar_register(i2s, 0);
+ ret = i2schar_register(i2s, port);
if (ret < 0)
{
aerr("ERROR: i2schar_register failed: %d\n", ret);
@@ -83,29 +84,71 @@ int board_i2sdev_initialize(int port)
}
#endif
- audio_i2s = audio_i2s_initialize(i2s, true);
-
- if (!audio_i2s)
+ if (enable_tx)
{
- auderr("ERROR: Failed to initialize I2S\n");
- return -ENODEV;
- }
+ /* Initialize audio output */
- pcm = pcm_decode_initialize(audio_i2s);
+ audio_i2s = audio_i2s_initialize(i2s, true);
- if (!pcm)
- {
- auderr("ERROR: Failed create the PCM decoder\n");
- return -ENODEV;
- }
+ if (audio_i2s == NULL)
+ {
+ auderr("ERROR: Failed to initialize I2S audio output\n");
+ return -ENODEV;
+ }
- snprintf(devname, 12, "pcm%d", port);
+ snprintf(devname, sizeof(devname), "pcm%d", port);
- ret = audio_register(devname, pcm);
+ /* If nxlooper is selected, the playback buffer is not rendered as
+ * a WAV file. Therefore, PCM decode will fail while processing such
+ * output buffer. In such a case, we bypass the PCM decode.
+ */
- if (ret < 0)
+#ifdef CONFIG_SYSTEM_NXLOOPER
+ ret = audio_register(devname, audio_i2s);
+#else
+ struct audio_lowerhalf_s *pcm;
+
+ pcm = pcm_decode_initialize(audio_i2s);
+
+ if (pcm == NULL)
+ {
+ auderr("ERROR: Failed create the PCM decoder\n");
+ return -ENODEV;
+ }
+
+ ret = audio_register(devname, pcm);
+#endif /* CONFIG_SYSTEM_NXLOOPER */
+
+ if (ret < 0)
+ {
+ auderr("ERROR: Failed to register /dev/%s device: %d\n",
+ devname, ret);
+ return ret;
+ }
+ }
+
+ if (enable_rx)
{
- auderr("ERROR: Failed to register /dev/%s device: %d\n", devname, ret);
+ /* Initialize audio input */
+
+ audio_i2s = audio_i2s_initialize(i2s, false);
+
+ if (audio_i2s == NULL)
+ {
+ auderr("ERROR: Failed to initialize I2S audio input\n");
+ return -ENODEV;
+ }
+
+ snprintf(devname, sizeof(devname), "pcm_in%d", port);
+
+ ret = audio_register(devname, audio_i2s);
+
+ if (ret < 0)
+ {
+ auderr("ERROR: Failed to register /dev/%s device: %d\n",
+ devname, ret);
+ return ret;
+ }
}
return ret;
diff --git a/boards/xtensa/esp32/esp32-devkitc/configs/i2schar/defconfig b/boards/xtensa/esp32/esp32-devkitc/configs/i2schar/defconfig
index dcf9c709fa..a4aec245ef 100644
--- a/boards/xtensa/esp32/esp32-devkitc/configs/i2schar/defconfig
+++ b/boards/xtensa/esp32/esp32-devkitc/configs/i2schar/defconfig
@@ -6,7 +6,6 @@
# modifications.
#
# CONFIG_ARCH_LEDS is not set
-# CONFIG_ESP32_I2S0_RX is not set
# CONFIG_NDEBUG is not set
# CONFIG_NSH_ARGCAT is not set
# CONFIG_NSH_CMDOPT_HEXDUMP is not set
@@ -25,13 +24,18 @@ CONFIG_AUDIO_I2S=y
CONFIG_AUDIO_I2SCHAR=y
CONFIG_BOARD_LOOPSPERMSEC=16717
CONFIG_BUILTIN=y
-CONFIG_DEBUG_SYMBOLS=y
CONFIG_DRIVERS_AUDIO=y
CONFIG_ESP32_I2S0=y
+CONFIG_ESP32_I2S0_DATA_BIT_WIDTH_16BIT=y
CONFIG_ESP32_I2S0_MCLK=y
+CONFIG_ESP32_I2S1=y
CONFIG_ESP32_I2S=y
CONFIG_ESP32_UART0=y
CONFIG_EXAMPLES_I2SCHAR=y
+CONFIG_EXAMPLES_I2SCHAR_BUFSIZE=1024
+CONFIG_EXAMPLES_I2SCHAR_RX=y
+CONFIG_EXAMPLES_I2SCHAR_RXBUFFERS=2
+CONFIG_EXAMPLES_I2SCHAR_RXSTACKSIZE=4096
CONFIG_EXAMPLES_I2SCHAR_TX=y
CONFIG_EXAMPLES_I2SCHAR_TXBUFFERS=2
CONFIG_EXAMPLES_I2SCHAR_TXSTACKSIZE=4096
diff --git a/boards/xtensa/esp32/esp32-devkitc/configs/nxlooper/defconfig b/boards/xtensa/esp32/esp32-devkitc/configs/nxlooper/defconfig
new file mode 100644
index 0000000000..e5e60cd57e
--- /dev/null
+++ b/boards/xtensa/esp32/esp32-devkitc/configs/nxlooper/defconfig
@@ -0,0 +1,129 @@
+#
+# This file is autogenerated: PLEASE DO NOT EDIT IT.
+#
+# You can use "make menuconfig" to make any modifications to the installed .config file.
+# You can then do "make savedefconfig" to generate a new defconfig file that includes your
+# modifications.
+#
+# CONFIG_ARCH_LEDS is not set
+# CONFIG_ESP32_I2S0_RX is not set
+# CONFIG_ESP32_I2S1_TX is not set
+# CONFIG_NDEBUG is not set
+# CONFIG_NSH_ARGCAT is not set
+# CONFIG_NSH_CMDOPT_HEXDUMP is not set
+# CONFIG_NSH_CMDPARMS is not set
+CONFIG_ALLOW_BSD_COMPONENTS=y
+CONFIG_ARCH="xtensa"
+CONFIG_ARCH_BOARD="esp32-devkitc"
+CONFIG_ARCH_BOARD_COMMON=y
+CONFIG_ARCH_BOARD_ESP32_DEVKITC=y
+CONFIG_ARCH_CHIP="esp32"
+CONFIG_ARCH_CHIP_ESP32=y
+CONFIG_ARCH_CHIP_ESP32WROVER=y
+CONFIG_ARCH_INTERRUPTSTACK=4096
+CONFIG_ARCH_STACKDUMP=y
+CONFIG_ARCH_XTENSA=y
+CONFIG_AUDIO=y
+CONFIG_AUDIOUTILS_MMLPARSER_LIB=y
+CONFIG_AUDIO_BUFFER_NUMBYTES=4092
+CONFIG_AUDIO_EXCLUDE_BALANCE=y
+CONFIG_AUDIO_EXCLUDE_FFORWARD=y
+CONFIG_AUDIO_EXCLUDE_TONE=y
+CONFIG_AUDIO_EXCLUDE_VOLUME=y
+CONFIG_AUDIO_I2S=y
+CONFIG_AUDIO_I2SCHAR=y
+CONFIG_AUDIO_NUM_BUFFERS=4
+CONFIG_BOARDCTL_ROMDISK=y
+CONFIG_BOARD_LOOPSPERMSEC=16717
+CONFIG_BUILTIN=y
+CONFIG_DEFAULT_TASK_STACKSIZE=4096
+CONFIG_DEV_URANDOM=y
+CONFIG_DRIVERS_AUDIO=y
+CONFIG_DRIVERS_IEEE80211=y
+CONFIG_DRIVERS_WIRELESS=y
+CONFIG_ESP32_I2S0=y
+CONFIG_ESP32_I2S0_DATA_BIT_WIDTH_16BIT=y
+CONFIG_ESP32_I2S0_MCLK=y
+CONFIG_ESP32_I2S1=y
+CONFIG_ESP32_I2S1_DATA_BIT_WIDTH_16BIT=y
+CONFIG_ESP32_I2S=y
+CONFIG_ESP32_SPIFLASH=y
+CONFIG_ESP32_SPIFLASH_SPIFFS=y
+CONFIG_ESP32_STORAGE_MTD_SIZE=0x80000
+CONFIG_ESP32_UART0=y
+CONFIG_ESP32_WIFI=y
+CONFIG_ESP32_WIFI_SAVE_PARAM=y
+CONFIG_EXAMPLES_I2SCHAR=y
+CONFIG_EXAMPLES_I2SCHAR_BUFSIZE=960
+CONFIG_EXAMPLES_I2SCHAR_RX=y
+CONFIG_EXAMPLES_I2SCHAR_RXBUFFERS=2
+CONFIG_EXAMPLES_I2SCHAR_RXSTACKSIZE=2048
+CONFIG_EXAMPLES_I2SCHAR_TX=y
+CONFIG_EXAMPLES_I2SCHAR_TXBUFFERS=2
+CONFIG_EXAMPLES_I2SCHAR_TXSTACKSIZE=2048
+CONFIG_EXAMPLES_ROMFS=y
+CONFIG_FS_PROCFS=y
+CONFIG_FS_ROMFS=y
+CONFIG_HAVE_CXX=y
+CONFIG_HAVE_CXXINITIALIZE=y
+CONFIG_I2S_DMADESC_NUM=4
+CONFIG_IDLETHREAD_STACKSIZE=3072
+CONFIG_INIT_ENTRYPOINT="nsh_main"
+CONFIG_INTELHEX_BINARY=y
+CONFIG_IOB_NBUFFERS=24
+CONFIG_IOB_THROTTLE=0
+CONFIG_MM_REGIONS=3
+CONFIG_NAME_MAX=48
+CONFIG_NETDB_DNSCLIENT=y
+CONFIG_NETDB_DNSCLIENT_NAMESIZE=64
+CONFIG_NETDEV_LATEINIT=y
+CONFIG_NETDEV_PHY_IOCTL=y
+CONFIG_NETDEV_WIRELESS_IOCTL=y
+CONFIG_NETINIT_WAPI_ALG=1
+CONFIG_NETUTILS_IPERF=y
+CONFIG_NETUTILS_TELNETD=y
+CONFIG_NET_BROADCAST=y
+CONFIG_NET_ETH_PKTSIZE=1518
+CONFIG_NET_ICMP=y
+CONFIG_NET_ICMP_SOCKET=y
+CONFIG_NET_NACTIVESOCKETS=32
+CONFIG_NET_STATISTICS=y
+CONFIG_NET_TCP=y
+CONFIG_NET_TCP_DELAYED_ACK=y
+CONFIG_NET_TCP_WRITE_BUFFERS=y
+CONFIG_NET_UDP=y
+CONFIG_NSH_ARCHINIT=y
+CONFIG_NSH_BUILTIN_APPS=y
+CONFIG_NSH_FILEIOSIZE=512
+CONFIG_NSH_LINELEN=300
+CONFIG_NSH_READLINE=y
+CONFIG_NXPLAYER_HTTP_STREAMING_SUPPORT=y
+CONFIG_NXPLAYER_PLAYTHREAD_STACKSIZE=4096
+CONFIG_POSIX_SPAWN_DEFAULT_STACKSIZE=2048
+CONFIG_PREALLOC_TIMERS=4
+CONFIG_PTHREAD_MUTEX_TYPES=y
+CONFIG_PTHREAD_STACK_DEFAULT=2048
+CONFIG_RAM_SIZE=114688
+CONFIG_RAM_START=0x20000000
+CONFIG_RR_INTERVAL=200
+CONFIG_SCHED_HPWORK=y
+CONFIG_SCHED_HPWORKSTACKSIZE=2048
+CONFIG_SCHED_LPWORK=y
+CONFIG_SCHED_WAITPID=y
+CONFIG_SIG_DEFAULT=y
+CONFIG_SPI=y
+CONFIG_SPIFFS_NAME_MAX=48
+CONFIG_START_DAY=6
+CONFIG_START_MONTH=12
+CONFIG_START_YEAR=2011
+CONFIG_SYSTEM_DHCPC_RENEW=y
+CONFIG_SYSTEM_NSH=y
+CONFIG_SYSTEM_NXLOOPER=y
+CONFIG_SYSTEM_NXPLAYER=y
+CONFIG_SYSTEM_PING=y
+CONFIG_TELNET_CHARACTER_MODE=y
+CONFIG_TLS_TASK_NELEM=4
+CONFIG_UART0_SERIAL_CONSOLE=y
+CONFIG_WIRELESS=y
+CONFIG_WIRELESS_WAPI=y
+CONFIG_WIRELESS_WAPI_CMDTOOL=y
diff --git a/boards/xtensa/esp32/esp32-devkitc/src/esp32-devkitc.h b/boards/xtensa/esp32/esp32-devkitc/src/esp32-devkitc.h
index 3360d9ca95..8e5e9d9d08 100644
--- a/boards/xtensa/esp32/esp32-devkitc/src/esp32-devkitc.h
+++ b/boards/xtensa/esp32/esp32-devkitc/src/esp32-devkitc.h
@@ -165,7 +165,9 @@ int esp32_twai_setup(void);
* number.
*
* Input Parameters:
- * port - The I2S port used for the device
+ * port - The I2S port used for the device
+ * enable_tx - Register device as TX if true
+ * enable_rx - Register device as RX if true
*
* Returned Value:
* Zero is returned on success. Otherwise, a negated errno value is
@@ -175,7 +177,7 @@ int esp32_twai_setup(void);
#if defined(CONFIG_ESP32_I2S0) && !defined(CONFIG_AUDIO_CS4344) || \
defined(CONFIG_ESP32_I2S1)
-int board_i2sdev_initialize(int port);
+int board_i2sdev_initialize(int port, bool enable_tx, bool enable_rx);
#endif
/****************************************************************************
diff --git a/boards/xtensa/esp32/esp32-devkitc/src/esp32_bringup.c b/boards/xtensa/esp32/esp32-devkitc/src/esp32_bringup.c
index cc3eca312c..cef7858b14 100644
--- a/boards/xtensa/esp32/esp32-devkitc/src/esp32_bringup.c
+++ b/boards/xtensa/esp32/esp32-devkitc/src/esp32_bringup.c
@@ -488,6 +488,12 @@ int esp32_bringup(void)
#ifdef CONFIG_ESP32_I2S
+#if defined(CONFIG_ESP32_I2S0) && !defined(CONFIG_AUDIO_CS4344) || \
+ defined(CONFIG_ESP32_I2S1)
+ bool i2s_enable_tx;
+ bool i2s_enable_rx;
+#endif
+
#ifdef CONFIG_ESP32_I2S0
/* Configure I2S0 */
@@ -503,27 +509,52 @@ int esp32_bringup(void)
}
#else
+#ifdef CONFIG_ESP32_I2S0_TX
+ i2s_enable_tx = true;
+#else
+ i2s_enable_tx = false;
+#endif /* CONFIG_ESP32_I2S0_TX */
+
+#ifdef CONFIG_ESP32_I2S0_RX
+ i2s_enable_rx = true;
+#else
+ i2s_enable_rx = false;
+#endif /* CONFIG_ESP32_I2S0_RX */
+
/* Configure I2S generic audio on I2S0 */
- ret = board_i2sdev_initialize(ESP32_I2S0);
+ ret = board_i2sdev_initialize(ESP32_I2S0, i2s_enable_tx, i2s_enable_rx);
if (ret < 0)
{
syslog(LOG_ERR, "Failed to initialize I2S%d driver: %d\n",
CONFIG_ESP32_I2S0, ret);
}
+
#endif /* CONFIG_AUDIO_CS4344 */
#endif /* CONFIG_ESP32_I2S0 */
#ifdef CONFIG_ESP32_I2S1
+#ifdef CONFIG_ESP32_I2S1_TX
+ i2s_enable_tx = true;
+#else
+ i2s_enable_tx = false;
+#endif /* CONFIG_ESP32_I2S1_TX */
+
+#ifdef CONFIG_ESP32_I2S1_RX
+ i2s_enable_rx = true;
+#else
+ i2s_enable_rx = false;
+#endif /* CONFIG_ESP32_I2S1_RX */
+
/* Configure I2S generic audio on I2S1 */
- ret = board_i2sdev_initialize(ESP32_I2S1);
+ ret = board_i2sdev_initialize(ESP32_I2S1, i2s_enable_tx, i2s_enable_rx);
if (ret < 0)
{
syslog(LOG_ERR, "Failed to initialize I2S%d driver: %d\n",
- CONFIG_ESP32_I2S0, ret);
+ CONFIG_ESP32_I2S1, ret);
}
#endif /* CONFIG_ESP32_I2S1 */
[incubator-nuttx] 01/04: esp32[-s2]/i2s: fix gpio setting when slave mode is selected
Posted by ac...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
acassis pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/incubator-nuttx.git
commit 7ae4152f47d3564b102fe6473dfd40c4a08269c6
Author: Tiago Medicci Serrano <ti...@espressif.com>
AuthorDate: Thu Nov 10 10:32:48 2022 -0300
esp32[-s2]/i2s: fix gpio setting when slave mode is selected
---
arch/xtensa/src/esp32/esp32_i2s.c | 16 ++++++++--------
arch/xtensa/src/esp32s2/esp32s2_i2s.c | 18 +++++++++---------
2 files changed, 17 insertions(+), 17 deletions(-)
diff --git a/arch/xtensa/src/esp32/esp32_i2s.c b/arch/xtensa/src/esp32/esp32_i2s.c
index 0fcfb0b16d..96bc7b8503 100644
--- a/arch/xtensa/src/esp32/esp32_i2s.c
+++ b/arch/xtensa/src/esp32/esp32_i2s.c
@@ -1031,13 +1031,13 @@ 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);
+ esp32_gpio_matrix_in(priv->config->ws_pin,
+ priv->config->ws_out_insig, 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_out_insig, 0, 0);
+ esp32_gpio_matrix_in(priv->config->bclk_pin,
+ priv->config->bclk_out_insig, 0);
}
else
{
@@ -1047,13 +1047,13 @@ 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);
+ esp32_gpio_matrix_in(priv->config->ws_pin,
+ priv->config->ws_in_insig, 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);
+ esp32_gpio_matrix_in(priv->config->bclk_pin,
+ priv->config->bclk_in_insig, 0);
}
}
else
diff --git a/arch/xtensa/src/esp32s2/esp32s2_i2s.c b/arch/xtensa/src/esp32s2/esp32s2_i2s.c
index 47f969626d..e42103538d 100644
--- a/arch/xtensa/src/esp32s2/esp32s2_i2s.c
+++ b/arch/xtensa/src/esp32s2/esp32s2_i2s.c
@@ -347,7 +347,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_256,
+ .mclk_multiple = I2S_MCLK_MULTIPLE_384,
.tx_en = I2S_TX_ENABLED,
.rx_en = I2S_RX_ENABLED,
#ifdef CONFIG_ESP32S2_I2S_MCLK
@@ -939,13 +939,13 @@ static void i2s_configure(struct esp32s2_i2s_s *priv)
esp32s2_gpiowrite(priv->config->ws_pin, 1);
esp32s2_configgpio(priv->config->ws_pin, INPUT_FUNCTION_2);
- esp32s2_gpio_matrix_out(priv->config->ws_pin,
- priv->config->ws_out_insig, 0, 0);
+ esp32s2_gpio_matrix_in(priv->config->ws_pin,
+ priv->config->ws_out_insig, 0);
esp32s2_gpiowrite(priv->config->bclk_pin, 1);
esp32s2_configgpio(priv->config->bclk_pin, INPUT_FUNCTION_2);
- esp32s2_gpio_matrix_out(priv->config->bclk_pin,
- priv->config->bclk_out_insig, 0, 0);
+ esp32s2_gpio_matrix_in(priv->config->bclk_pin,
+ priv->config->bclk_out_insig, 0);
}
else
{
@@ -955,13 +955,13 @@ static void i2s_configure(struct esp32s2_i2s_s *priv)
esp32s2_gpiowrite(priv->config->ws_pin, 1);
esp32s2_configgpio(priv->config->ws_pin, INPUT_FUNCTION_2);
- esp32s2_gpio_matrix_out(priv->config->ws_pin,
- priv->config->ws_in_insig, 0, 0);
+ esp32s2_gpio_matrix_in(priv->config->ws_pin,
+ priv->config->ws_in_insig, 0);
esp32s2_gpiowrite(priv->config->bclk_pin, 1);
esp32s2_configgpio(priv->config->bclk_pin, INPUT_FUNCTION_2);
- esp32s2_gpio_matrix_out(priv->config->bclk_pin,
- priv->config->bclk_in_insig, 0, 0);
+ esp32s2_gpio_matrix_in(priv->config->bclk_pin,
+ priv->config->bclk_in_insig, 0);
}
}
else