* arch/xtensa/src/esp32/esp32_i2s.c
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership. The
* ASF licenses this file to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the
* License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
****************************************************************************/
* Included Files
****************************************************************************/
#include <nuttx/config.h>
#ifdef CONFIG_ESP32_I2S
#include <debug.h>
#include <sys/param.h>
#include <sys/types.h>
#include <inttypes.h>
#include <stdint.h>
#include <stdio.h>
#include <fcntl.h>
#include <stdbool.h>
#include <stdlib.h>
#include <time.h>
#include <assert.h>
#include <nuttx/arch.h>
#include <nuttx/irq.h>
#include <nuttx/clock.h>
#include <nuttx/semaphore.h>
#include <nuttx/spinlock.h>
#include <nuttx/mqueue.h>
#include <nuttx/mutex.h>
#include <nuttx/circbuf.h>
#include <nuttx/nuttx.h>
#include <nuttx/audio/audio.h>
#include <nuttx/audio/i2s.h>
#include <arch/board/board.h>
#include "esp32_i2s.h"
#include "esp32_gpio.h"
#include "esp32_irq.h"
#include "esp32_dma.h"
#include "xtensa.h"
#include "hardware/esp32_gpio_sigmap.h"
#include "hardware/esp32_dport.h"
#include "hardware/esp32_i2s.h"
#include "hardware/esp32_soc.h"
#include "hardware/esp32_iomux.h"
#include "hardware/esp32_pinmap.h"
#include "hardware/esp32_dma.h"
* Pre-processor Definitions
****************************************************************************/
#define I2S_DMADESC_NUM (CONFIG_I2S_DMADESC_NUM)
#define I2S_LL_BASE_CLK (2 * APB_CLK_FREQ)
#define I2S_LL_MCLK_DIVIDER_BIT_WIDTH (6)
#define I2S_LL_MCLK_DIVIDER_MAX ((1 << I2S_LL_MCLK_DIVIDER_BIT_WIDTH) - 1)
#define I2S_DMA_CHANNEL_MAX (2)
#ifdef CONFIG_ESP32_I2S0_TX
# define I2S0_TX_ENABLED 1
# define I2S_HAVE_TX 1
#else
# define I2S0_TX_ENABLED 0
#endif
#ifdef CONFIG_ESP32_I2S0_RX
# define I2S0_RX_ENABLED 1
# define I2S_HAVE_RX 1
#else
# define I2S0_RX_ENABLED 0
#endif
#ifdef CONFIG_ESP32_I2S1_TX
# define I2S1_TX_ENABLED 1
# define I2S_HAVE_TX 1
#else
# define I2S1_TX_ENABLED 0
#endif
#ifdef CONFIG_ESP32_I2S1_RX
# define I2S1_RX_ENABLED 1
# define I2S_HAVE_RX 1
#else
# define I2S1_RX_ENABLED 0
#endif
#ifdef CONFIG_DEBUG_I2S_INFO
# define CONFIG_ESP32_I2S_DUMPBUFFERS
#else
# undef CONFIG_ESP32_I2S_DUMPBUFFERS
#endif
#ifndef CONFIG_ESP32_I2S_MAXINFLIGHT
# define CONFIG_ESP32_I2S_MAXINFLIGHT 4
#endif
#define I2S_GPIO_UNUSED -1
* Private Types
****************************************************************************/
typedef enum
{
I2S_ROLE_MASTER,
I2S_ROLE_SLAVE
} i2s_role_t;
typedef enum
{
I2S_DATA_BIT_WIDTH_8BIT = 8,
I2S_DATA_BIT_WIDTH_16BIT = 16,
I2S_DATA_BIT_WIDTH_24BIT = 24,
I2S_DATA_BIT_WIDTH_32BIT = 32,
} i2s_data_bit_width_t;
typedef enum
{
I2S_MCLK_MULTIPLE_128 = 128,
I2S_MCLK_MULTIPLE_256 = 256,
I2S_MCLK_MULTIPLE_384 = 384,
I2S_MCLK_MULTIPLE_512 = 512,
} i2s_mclk_multiple_t;
struct esp32_i2s_config_s
{
uint32_t port;
uint32_t role;
uint8_t data_width;
uint32_t rate;
uint32_t total_slot;
bool is_apll;
bool tx_en;
bool rx_en;
int8_t mclk_pin;
int8_t bclk_pin;
int8_t ws_pin;
int8_t dout_pin;
int8_t din_pin;
uint8_t periph;
uint8_t irq;
uint32_t bclk_in_insig;
uint32_t bclk_in_outsig;
uint32_t bclk_out_insig;
uint32_t bclk_out_outsig;
uint32_t ws_in_insig;
uint32_t ws_in_outsig;
uint32_t ws_out_insig;
uint32_t ws_out_outsig;
uint32_t din_insig;
uint32_t dout_outsig;
bool bit_shift;
bool mono_en;
uint32_t ws_width;
bool ws_pol;
};
struct esp32_buffer_s
{
struct esp32_buffer_s *flink;
struct esp32_dmadesc_s dma_link[I2S_DMADESC_NUM];
i2s_callback_t callback;
uint32_t timeout;
void *arg;
struct ap_buffer_s *apb;
uint8_t *buf;
uint32_t nbytes;
int result;
};
* however, the audio buffer is not aligned and additional bytes must
* be copied to be inserted on the next buffer. This structure keeps
* track of the bytes that were not written to the internal buffer yet.
*/
struct esp32_buffer_carry_s
{
uint32_t value;
size_t bytes;
};
* transport.
*/
struct esp32_transport_s
{
sq_queue_t pend;
sq_queue_t act;
sq_queue_t done;
struct work_s work;
struct esp32_buffer_carry_s carry;
};
struct esp32_i2s_s
{
struct i2s_dev_s dev;
mutex_t lock;
int cpuint;
uint8_t cpu;
spinlock_t slock;
const struct esp32_i2s_config_s *config;
uint32_t mclk_freq;
uint32_t mclk_multiple;
uint32_t channels;
uint32_t rate;
uint32_t data_width;
#ifdef I2S_HAVE_TX
struct esp32_transport_s tx;
bool tx_started;
#endif
#ifdef I2S_HAVE_RX
struct esp32_transport_s rx;
bool rx_started;
#endif
sem_t bufsem;
struct esp32_buffer_s *bf_freelist;
struct esp32_buffer_s containers[CONFIG_ESP32_I2S_MAXINFLIGHT];
};
* Private Function Prototypes
****************************************************************************/
#ifdef CONFIG_ESP32_I2S_DUMPBUFFERS
# define i2s_dump_buffer(m,b,s) lib_dumpbuffer(m,b,s)
#else
# define i2s_dump_buffer(m,b,s)
#endif
static struct esp32_buffer_s *
i2s_buf_allocate(struct esp32_i2s_s *priv);
static void i2s_buf_free(struct esp32_i2s_s *priv,
struct esp32_buffer_s *bfcontainer);
static int i2s_buf_initialize(struct esp32_i2s_s *priv);
#ifdef I2S_HAVE_TX
static IRAM_ATTR int i2s_txdma_setup(struct esp32_i2s_s *priv,
struct esp32_buffer_s *bfcontainer);
static void i2s_tx_worker(void *arg);
static void i2s_tx_schedule(struct esp32_i2s_s *priv,
struct esp32_dmadesc_s *outlink);
#endif
#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
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 i2s_getmclkfrequency(struct i2s_dev_s *dev);
static uint32_t i2s_setmclkfrequency(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 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
#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 i2s_rxchannels(struct i2s_dev_s *dev, uint8_t channels);
static uint32_t i2s_rxsamplerate(struct i2s_dev_s *dev, uint32_t rate);
static uint32_t i2s_rxdatawidth(struct i2s_dev_s *dev, int bits);
static void i2s_cleanup_queues(struct esp32_i2s_s *priv);
static int i2s_receive(struct i2s_dev_s *dev, struct ap_buffer_s *apb,
i2s_callback_t callback, void *arg,
uint32_t timeout);
#endif
* Private Data
****************************************************************************/
static const struct i2s_ops_s g_i2sops =
{
#ifdef I2S_HAVE_TX
.i2s_txchannels = i2s_txchannels,
.i2s_txsamplerate = i2s_txsamplerate,
.i2s_txdatawidth = i2s_txdatawidth,
.i2s_send = i2s_send,
#endif
#ifdef I2S_HAVE_RX
.i2s_rxchannels = i2s_rxchannels,
.i2s_rxsamplerate = i2s_rxsamplerate,
.i2s_rxdatawidth = i2s_rxdatawidth,
.i2s_receive = i2s_receive,
#endif
.i2s_ioctl = i2s_ioctl,
.i2s_getmclkfrequency = i2s_getmclkfrequency,
.i2s_setmclkfrequency = i2s_setmclkfrequency,
};
#ifdef CONFIG_ESP32_I2S0
static const struct esp32_i2s_config_s esp32_i2s0_config =
{
.port = 0,
#ifdef CONFIG_ESP32_I2S0_ROLE_MASTER
.role = I2S_ROLE_MASTER,
#else
.role = I2S_ROLE_SLAVE,
#endif
.data_width = CONFIG_ESP32_I2S0_DATA_BIT_WIDTH,
.rate = CONFIG_ESP32_I2S0_SAMPLE_RATE,
.total_slot = 2,
.tx_en = I2S0_TX_ENABLED,
.rx_en = I2S0_RX_ENABLED,
#ifdef CONFIG_ESP32_I2S0_MCLK
.mclk_pin = CONFIG_ESP32_I2S0_MCLKPIN,
#else
.mclk_pin = I2S_GPIO_UNUSED,
#endif
.bclk_pin = CONFIG_ESP32_I2S0_BCLKPIN,
.ws_pin = CONFIG_ESP32_I2S0_WSPIN,
#ifdef CONFIG_ESP32_I2S0_DOUTPIN
.dout_pin = CONFIG_ESP32_I2S0_DOUTPIN,
#else
.dout_pin = I2S_GPIO_UNUSED,
#endif
#ifdef CONFIG_ESP32_I2S0_DINPIN
.din_pin = CONFIG_ESP32_I2S0_DINPIN,
#else
.din_pin = I2S_GPIO_UNUSED,
#endif
.periph = ESP32_PERIPH_I2S0,
.irq = ESP32_IRQ_I2S0,
.bclk_in_insig = I2S0I_BCK_IN_IDX,
.bclk_in_outsig = I2S0I_BCK_OUT_IDX,
.bclk_out_insig = I2S0O_BCK_IN_IDX,
.bclk_out_outsig = I2S0O_BCK_OUT_IDX,
.ws_in_insig = I2S0I_WS_IN_IDX,
.ws_in_outsig = I2S0I_WS_OUT_IDX,
.ws_out_insig = I2S0O_WS_IN_IDX,
.ws_out_outsig = I2S0O_WS_OUT_IDX,
.din_insig = I2S0I_DATA_IN15_IDX,
.dout_outsig = I2S0O_DATA_OUT23_IDX,
.bit_shift = true,
.mono_en = false,
.ws_width = CONFIG_ESP32_I2S0_DATA_BIT_WIDTH,
};
static struct esp32_i2s_s esp32_i2s0_priv =
{
.dev =
{
.ops = &g_i2sops,
},
.lock = NXMUTEX_INITIALIZER,
.config = &esp32_i2s0_config,
.bufsem = SEM_INITIALIZER(0),
};
#endif
#ifdef CONFIG_ESP32_I2S1
static const struct esp32_i2s_config_s esp32_i2s1_config =
{
.port = 1,
#ifdef CONFIG_ESP32_I2S1_ROLE_MASTER
.role = I2S_ROLE_MASTER,
#else
.role = I2S_ROLE_SLAVE,
#endif
.data_width = CONFIG_ESP32_I2S1_DATA_BIT_WIDTH,
.rate = CONFIG_ESP32_I2S1_SAMPLE_RATE,
.total_slot = 2,
.tx_en = I2S1_TX_ENABLED,
.rx_en = I2S1_RX_ENABLED,
#ifdef CONFIG_ESP32_I2S1_MCLK
.mclk_pin = CONFIG_ESP32_I2S1_MCLKPIN,
#else
.mclk_pin = I2S_GPIO_UNUSED,
#endif
.bclk_pin = CONFIG_ESP32_I2S1_BCLKPIN,
.ws_pin = CONFIG_ESP32_I2S1_WSPIN,
#ifdef CONFIG_ESP32_I2S1_DOUTPIN
.dout_pin = CONFIG_ESP32_I2S1_DOUTPIN,
#else
.dout_pin = I2S_GPIO_UNUSED,
#endif
#ifdef CONFIG_ESP32_I2S1_DINPIN
.din_pin = CONFIG_ESP32_I2S1_DINPIN,
#else
.din_pin = I2S_GPIO_UNUSED,
#endif
.periph = ESP32_PERIPH_I2S1,
.irq = ESP32_IRQ_I2S1,
.bclk_in_insig = I2S1I_BCK_IN_IDX,
.bclk_in_outsig = I2S1I_BCK_OUT_IDX,
.bclk_out_insig = I2S1O_BCK_IN_IDX,
.bclk_out_outsig = I2S1O_BCK_OUT_IDX,
.ws_in_insig = I2S1I_WS_IN_IDX,
.ws_in_outsig = I2S1I_WS_OUT_IDX,
.ws_out_insig = I2S1O_WS_IN_IDX,
.ws_out_outsig = I2S1O_WS_OUT_IDX,
.din_insig = I2S1I_DATA_IN15_IDX,
.dout_outsig = I2S1O_DATA_OUT23_IDX,
.bit_shift = true,
.mono_en = false,
.ws_width = CONFIG_ESP32_I2S1_DATA_BIT_WIDTH,
};
static struct esp32_i2s_s esp32_i2s1_priv =
{
.dev =
{
.ops = &g_i2sops,
},
.lock = NXMUTEX_INITIALIZER,
.config = &esp32_i2s1_config,
.bufsem = SEM_INITIALIZER(0),
};
#endif
* Private Functions
****************************************************************************/
* Name: i2s_buf_allocate
*
* Description:
* Allocate a buffer container by removing the one at the head of the
* free list
*
* Input Parameters:
* priv - Initialized I2S device structure.
*
* Returned Value:
* A non-NULL pointer to the allocate buffer container on success; NULL if
* there are no available buffer containers.
*
* Assumptions:
* The caller does NOT have exclusive access to the I2S state structure.
* That would result in a deadlock!
*
****************************************************************************/
static struct esp32_buffer_s *i2s_buf_allocate(struct esp32_i2s_s *priv)
{
struct esp32_buffer_s *bfcontainer;
irqstate_t flags;
int ret;
* have at least one free buffer container.
*/
ret = nxsem_wait_uninterruptible(&priv->bufsem);
if (ret < 0)
{
return NULL;
}
flags = spin_lock_irqsave(&priv->slock);
bfcontainer = priv->bf_freelist;
DEBUGASSERT(bfcontainer);
priv->bf_freelist = bfcontainer->flink;
spin_unlock_irqrestore(&priv->slock, flags);
return bfcontainer;
}
* Name: i2s_buf_free
*
* Description:
* Free buffer container by adding it to the head of the free list
*
* Input Parameters:
* priv - Initialized I2S device structure.
* bfcontainer - The buffer container to be freed
*
* Returned Value:
* None
*
* Assumptions:
* The caller has exclusive access to the I2S state structure
*
****************************************************************************/
static void i2s_buf_free(struct esp32_i2s_s *priv,
struct esp32_buffer_s *bfcontainer)
{
irqstate_t flags;
flags = spin_lock_irqsave(&priv->slock);
bfcontainer->apb = NULL;
bfcontainer->buf = NULL;
bfcontainer->nbytes = 0;
bfcontainer->flink = priv->bf_freelist;
priv->bf_freelist = bfcontainer;
spin_unlock_irqrestore(&priv->slock, flags);
nxsem_post(&priv->bufsem);
}
* Name: i2s_buf_initialize
*
* Description:
* Initialize the buffer container allocator by adding all of the
* pre-allocated buffer containers to the free list
*
* Input Parameters:
* priv - Initialized I2S device structure.
*
* Returned Value:
* OK on success; A negated errno value on failure.
*
* Assumptions:
* Called early in I2S initialization so that there are no issues with
* concurrency.
*
****************************************************************************/
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
priv->bf_freelist = NULL;
for (int i = 0; i < CONFIG_ESP32_I2S_MAXINFLIGHT; i++)
{
i2s_buf_free(priv, &priv->containers[i]);
}
return OK;
}
* Name: i2s_txdma_start
*
* Description:
* Initiate the next TX DMA transfer. The DMA outlink was previously bound
* so it is safe to start the next DMA transfer at interrupt level.
*
* 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_TX
static int i2s_txdma_start(struct esp32_i2s_s *priv)
{
struct esp32_buffer_s *bfcontainer;
* returning success.
*/
if (!sq_empty(&priv->tx.act))
{
return OK;
}
if (sq_empty(&priv->tx.pend))
{
return OK;
}
bfcontainer = (struct esp32_buffer_s *)sq_remfirst(&priv->tx.pend);
* then start it.
*/
modifyreg32(I2S_OUT_LINK_REG(priv->config->port), I2S_OUTLINK_ADDR_M,
FIELD_TO_VALUE(I2S_OUTLINK_ADDR,
(uintptr_t) bfcontainer->dma_link));
modifyreg32(I2S_OUT_LINK_REG(priv->config->port), I2S_OUTLINK_STOP,
I2S_OUTLINK_START);
modifyreg32(I2S_CONF_REG(priv->config->port), 0, I2S_TX_START);
sq_addlast((sq_entry_t *)bfcontainer, &priv->tx.act);
return OK;
}
#endif
* 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;
* returning success.
*/
if (!sq_empty(&priv->rx.act))
{
return OK;
}
if (sq_empty(&priv->rx.pend))
{
return OK;
}
bfcontainer = (struct esp32_buffer_s *)sq_remfirst(&priv->rx.pend);
* 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
* Name: i2s_txdma_setup
*
* Description:
* Setup the next TX 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_TX
static IRAM_ATTR int i2s_txdma_setup(struct esp32_i2s_s *priv,
struct esp32_buffer_s *bfcontainer)
{
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;
uint8_t *samp;
DEBUGASSERT(bfcontainer && bfcontainer->apb);
apb = bfcontainer->apb;
outlink = bfcontainer->dma_link;
const apb_samp_t bytes_per_sample = priv->data_width / 8;
samp = &apb->samp[apb->curbyte];
samp_size = (apb->nbytes - apb->curbyte) + priv->tx.carry.bytes;
carry_size = samp_size % bytes_per_sample;
* internal buffer do provide 1) the ability to handle mono
* audio files and 2) to correctly fill the buffer to 16 or 32-bits
* aligned positions.
*/
padding = priv->channels == 1 ?
((priv->data_width <= I2S_DATA_BIT_WIDTH_16BIT ?
I2S_DATA_BIT_WIDTH_16BIT : I2S_DATA_BIT_WIDTH_32BIT) / 8) : 0;
padding += priv->data_width != I2S_DATA_BIT_WIDTH_16BIT ?
(priv->data_width != I2S_DATA_BIT_WIDTH_32BIT ? 1 : 0) : 0;
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;
if (priv->tx.carry.bytes)
{
memcpy(buf, &priv->tx.carry.value, priv->tx.carry.bytes);
buf += priv->tx.carry.bytes;
data_copied += priv->tx.carry.bytes;
memcpy(buf, samp, (bytes_per_sample - priv->tx.carry.bytes));
buf += (bytes_per_sample - priv->tx.carry.bytes + padding);
samp += (bytes_per_sample - priv->tx.carry.bytes);
data_copied += (bytes_per_sample - priv->tx.carry.bytes);
}
* once. Otherwise, the operation must add the padding bytes to each
* sample in the internal buffer.
*/
if (padding)
{
while (data_copied < (samp_size - carry_size))
{
memcpy(buf, samp, bytes_per_sample);
buf += (bytes_per_sample + padding);
samp += bytes_per_sample;
data_copied += bytes_per_sample;
}
}
else
{
memcpy(buf, samp, samp_size - (data_copied + carry_size));
buf += samp_size - (data_copied + carry_size);
samp += samp_size - (data_copied + carry_size);
data_copied += samp_size - (data_copied + carry_size);
}
* it's necessary to carry the remaining bytes that are part of what
* would be the last sample on this buffer. These bytes will then be
* saved and inserted at the beginning of the next DMA buffer to
* rebuild the sample correctly.
*/
priv->tx.carry.bytes = carry_size;
if (priv->tx.carry.bytes)
{
memcpy((uint8_t *)&priv->tx.carry.value, samp, priv->tx.carry.bytes);
}
* cause the audio buffer to be freed.
*/
apb_free(bfcontainer->apb);
bytes_queued = esp32_dma_init(outlink, 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);
sq_addlast((sq_entry_t *)bfcontainer, &priv->tx.pend);
ret = i2s_txdma_start(priv);
spin_unlock_irqrestore(&priv->slock, flags);
return ret;
}
#endif
* 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;
bfcontainer->buf = calloc(bfcontainer->nbytes, 1);
if (bfcontainer->buf == NULL)
{
i2serr("Failed to allocate the DMA internal buffer "
"[%" PRIu32 " bytes]", bfcontainer->nbytes);
return -ENOMEM;
}
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);
sq_addlast((sq_entry_t *)bfcontainer, &priv->rx.pend);
ret = i2s_rxdma_start(priv);
spin_unlock_irqrestore(&priv->slock, flags);
return ret;
}
#endif
* Name: i2s_tx_schedule
*
* Description:
* An TX DMA completion has occurred. Schedule processing on
* the working thread.
*
* Input Parameters:
* priv - Initialized I2S device structure.
* outlink - DMA outlink descriptor that triggered the interrupt.
*
* Returned Value:
* None
*
* Assumptions:
* - Interrupts are disabled
*
****************************************************************************/
#ifdef I2S_HAVE_TX
static void i2s_tx_schedule(struct esp32_i2s_s *priv,
struct esp32_dmadesc_s *outlink)
{
struct esp32_buffer_s *bfcontainer;
struct esp32_dmadesc_s *bfdesc;
int ret;
* priv->tx.act queue.
*/
if (!sq_empty(&priv->tx.act))
{
bfcontainer = (struct esp32_buffer_s *)sq_peek(&priv->tx.act);
* last descriptor of the current buffer container's DMA outlink.
* 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)?
*/
bfdesc = bfcontainer->dma_link;
while (!(bfdesc->ctrl & DMA_CTRL_EOF))
{
DEBUGASSERT(bfdesc->next);
bfdesc = bfdesc->next;
}
if (bfdesc == outlink)
{
sq_remfirst(&priv->tx.act);
bfcontainer->result = OK;
* queue
*/
sq_addlast((sq_entry_t *)bfcontainer, &priv->tx.done);
if (sq_empty(&priv->tx.act))
{
i2s_txdma_start(priv);
}
}
* thread.
*/
if (work_available(&priv->tx.work))
{
* thread.
*/
ret = work_queue(HPWORK, &priv->tx.work, i2s_tx_worker, priv, 0);
if (ret != 0)
{
i2serr("ERROR: Failed to queue TX work: %d\n", ret);
}
}
}
}
#endif
* 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;
* priv->rx.act queue.
*/
if (!sq_empty(&priv->rx.act))
{
bfcontainer = (struct esp32_buffer_s *)sq_peek(&priv->rx.act);
* 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)?
*/
bfdesc = bfcontainer->dma_link;
while (!(bfdesc->ctrl & DMA_CTRL_EOF))
{
DEBUGASSERT(bfdesc->next);
bfdesc = bfdesc->next;
}
if (bfdesc == inlink)
{
sq_remfirst(&priv->rx.act);
bfcontainer->result = OK;
* queue
*/
sq_addlast((sq_entry_t *)bfcontainer, &priv->rx.done);
if (sq_empty(&priv->rx.act))
{
i2s_rxdma_start(priv);
}
}
* thread.
*/
if (work_available(&priv->rx.work))
{
* 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
* Name: i2s_tx_worker
*
* Description:
* TX transfer done worker
*
* Input Parameters:
* arg - the I2S device instance cast to void*
*
* Returned Value:
* None
*
****************************************************************************/
#ifdef I2S_HAVE_TX
static void i2s_tx_worker(void *arg)
{
struct esp32_i2s_s *priv = (struct esp32_i2s_s *)arg;
struct esp32_buffer_s *bfcontainer;
irqstate_t flags;
DEBUGASSERT(priv);
* from the tx.pend queue and saved in the tx.act queue. We get here when
* the DMA is finished.
*
* In any case, the buffer containers in tx.act will be moved to the end
* of the tx.done queue and tx.act will be emptied before this worker is
* started.
*
*/
i2sinfo("tx.act.head=%p tx.done.head=%p\n",
priv->tx.act.head, priv->tx.done.head);
while (sq_peek(&priv->tx.done) != NULL)
{
* interrupts must be disabled to do this because the tx.done queue is
* also modified from the interrupt level.
*/
flags = spin_lock_irqsave(&priv->slock);
bfcontainer = (struct esp32_buffer_s *)sq_remfirst(&priv->tx.done);
spin_unlock_irqrestore(&priv->slock, flags);
DEBUGASSERT(bfcontainer && bfcontainer->callback);
bfcontainer->callback(&priv->dev, bfcontainer->apb,
bfcontainer->arg, bfcontainer->result);
free(bfcontainer->buf);
i2s_buf_free(priv, bfcontainer);
}
}
#endif
* Name: i2s_rx_worker
*
* Description:
* RX transfer done worker
*
* Input Parameters:
* arg - the I2S device instance cast to void*
*
* Returned Value:
* None
*
****************************************************************************/
#ifdef I2S_HAVE_RX
static void i2s_rx_worker(void *arg)
{
struct esp32_i2s_s *priv = (struct esp32_i2s_s *)arg;
struct esp32_buffer_s *bfcontainer;
irqstate_t flags;
DEBUGASSERT(priv);
* 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.
*
*/
i2sinfo("rx.act.head=%p rx.done.head=%p\n",
priv->rx.act.head, priv->rx.done.head);
while (sq_peek(&priv->rx.done) != NULL)
{
* interrupts must be disabled to do this because the rx.done queue is
* also modified from the interrupt level.
*/
flags = spin_lock_irqsave(&priv->slock);
bfcontainer = (struct esp32_buffer_s *)sq_remfirst(&priv->rx.done);
spin_unlock_irqrestore(&priv->slock, flags);
apb_samp_t samp_size;
uint32_t data_copied;
uint8_t carry_bytes;
uint8_t padding;
uint8_t *buf;
uint8_t *samp;
* buffer - which store data aligned with 2 or 4 bytes - to correctly
* copy and fill the audio buffer according to the actual data width.
*/
padding = priv->data_width != I2S_DATA_BIT_WIDTH_16BIT ?
(priv->data_width != I2S_DATA_BIT_WIDTH_32BIT ? 1 : 0) : 0;
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);
data_copied = 0;
buf = bfcontainer->buf;
* complete the sample so that the next memcpy is aligned
* with the sample size.
*/
if (priv->rx.carry.bytes)
{
memcpy(samp, &priv->rx.carry.value, priv->rx.carry.bytes);
samp += priv->rx.carry.bytes;
data_copied += priv->rx.carry.bytes;
memcpy(samp, buf, (bytes_per_sample - priv->rx.carry.bytes));
buf += (bytes_per_sample - priv->rx.carry.bytes);
samp += (bytes_per_sample - priv->rx.carry.bytes);
data_copied += (bytes_per_sample - priv->rx.carry.bytes);
}
* once. Otherwise, the operation must add the padding bytes to each
* sample in the internal buffer.
*/
buf += padding;
if (padding)
{
while (data_copied + bytes_per_sample <= samp_size)
{
memcpy(samp, buf, bytes_per_sample);
buf += (bytes_per_sample + padding);
samp += bytes_per_sample;
data_copied += bytes_per_sample;
}
carry_bytes = samp_size - data_copied;
memcpy(&priv->rx.carry.value, buf, carry_bytes);
priv->rx.carry.bytes = carry_bytes;
}
else
{
memcpy(samp, buf, samp_size - data_copied);
buf += samp_size - data_copied;
samp += samp_size - data_copied;
data_copied += samp_size - data_copied;
}
* 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);
* 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);
bfcontainer->apb->nbytes *= bytes_per_sample;
DEBUGASSERT(bfcontainer->apb->nbytes == data_copied);
DEBUGASSERT(bfcontainer && bfcontainer->callback);
bfcontainer->callback(&priv->dev, bfcontainer->apb,
bfcontainer->arg, bfcontainer->result);
free(bfcontainer->buf);
* cause the audio buffer to be freed.
*/
apb_free(bfcontainer->apb);
i2s_buf_free(priv, bfcontainer);
}
}
#endif
* 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)
{
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);
}
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));
}
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);
}
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)
{
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);
esp32_configgpio(priv->config->bclk_pin, INPUT_FUNCTION_3);
esp32_gpio_matrix_in(priv->config->bclk_pin,
priv->config->bclk_out_insig, 0);
}
else
{
* 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_in_insig, 0);
esp32_gpiowrite(priv->config->bclk_pin, 1);
esp32_configgpio(priv->config->bclk_pin, INPUT_FUNCTION_3);
esp32_gpio_matrix_in(priv->config->bclk_pin,
priv->config->bclk_in_insig, 0);
}
}
else
{
if (priv->config->mclk_pin != I2S_GPIO_UNUSED)
{
bool is_i2s0 = priv->config->port == ESP32_I2S0 ? true : false;
i2sinfo("Configuring GPIO%" PRIu8 " to output master clock\n",
priv->config->mclk_pin);
esp32_configgpio(priv->config->mclk_pin, OUTPUT_FUNCTION_2);
esp32_gpio_matrix_out(priv->config->mclk_pin,
SIG_GPIO_OUT_IDX, 0, 0);
if (priv->config->mclk_pin == 0)
{
putreg32(priv->config->is_apll ?
0xfff6 : (is_i2s0 ? 0xfff0 : 0xffff), PIN_CTRL);
}
else if (priv->config->mclk_pin == 1)
{
putreg32(priv->config->is_apll ?
0xf6f6 : (is_i2s0 ? 0xf0f0 : 0xf0ff), PIN_CTRL);
}
else
{
putreg32(priv->config->is_apll ?
0xff66 : (is_i2s0 ? 0xff00 : 0xff0f), PIN_CTRL);
}
}
if (priv->config->rx_en && !priv->config->tx_en)
{
esp32_gpiowrite(priv->config->ws_pin, 1);
esp32_configgpio(priv->config->ws_pin, OUTPUT_FUNCTION_3);
esp32_gpio_matrix_out(priv->config->ws_pin,
priv->config->ws_in_outsig, 0, 0);
esp32_gpiowrite(priv->config->bclk_pin, 1);
esp32_configgpio(priv->config->bclk_pin, OUTPUT_FUNCTION_3);
esp32_gpio_matrix_out(priv->config->bclk_pin,
priv->config->bclk_in_outsig, 0, 0);
}
else
{
* index for ws and bck.
*/
esp32_gpiowrite(priv->config->ws_pin, 1);
esp32_configgpio(priv->config->ws_pin, OUTPUT_FUNCTION_3);
esp32_gpio_matrix_out(priv->config->ws_pin,
priv->config->ws_out_outsig, 0, 0);
esp32_gpiowrite(priv->config->bclk_pin, 1);
esp32_configgpio(priv->config->bclk_pin, OUTPUT_FUNCTION_3);
esp32_gpio_matrix_out(priv->config->bclk_pin,
priv->config->bclk_out_outsig, 0, 0);
}
}
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);
}
if (priv->config->tx_en)
{
modifyreg32(I2S_CONF_REG(priv->config->port), 0, I2S_TX_RESET);
modifyreg32(I2S_CONF_REG(priv->config->port), I2S_TX_RESET, 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);
if (priv->config->role == I2S_ROLE_SLAVE)
{
modifyreg32(I2S_CONF_REG(priv->config->port), 0, I2S_TX_SLAVE_MOD);
}
else
{
modifyreg32(I2S_CONF_REG(priv->config->port), I2S_TX_SLAVE_MOD, 0);
}
* On ESP32, sample_bit should equals to data_bit.
*/
priv->data_width = priv->config->data_width;
i2s_set_datawidth(priv);
modifyreg32(I2S_CONF_CHAN_REG(priv->config->port), I2S_TX_CHAN_MOD_M,
FIELD_TO_VALUE(I2S_TX_CHAN_MOD, priv->config->mono_en ?
4 : 0));
* BCK clock.
*/
if (priv->config->bit_shift)
{
modifyreg32(I2S_CONF_REG(priv->config->port), 0, I2S_TX_MSB_SHIFT);
}
else
{
modifyreg32(I2S_CONF_REG(priv->config->port), I2S_TX_MSB_SHIFT, 0);
}
* standard mode.
*/
if (priv->config->ws_width == 1)
{
modifyreg32(I2S_CONF_REG(priv->config->port), 0,
I2S_TX_SHORT_SYNC);
}
else
{
modifyreg32(I2S_CONF_REG(priv->config->port),
I2S_TX_SHORT_SYNC, 0);
}
if (priv->config->ws_pol == 1)
{
modifyreg32(I2S_CONF_REG(priv->config->port), 0,
I2S_TX_RIGHT_FIRST);
}
else
{
modifyreg32(I2S_CONF_REG(priv->config->port),
I2S_TX_RIGHT_FIRST, 0);
}
modifyreg32(I2S_FIFO_CONF_REG(priv->config->port), 0,
I2S_TX_FIFO_MOD_FORCE_EN);
* 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_setmclkfrequency` 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;
}
i2s_setmclkfrequency((struct i2s_dev_s *)priv, (priv->config->rate *
priv->mclk_multiple));
priv->rate = priv->config->rate;
i2s_set_clock(priv);
}
if (priv->config->rx_en)
{
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);
if (priv->config->role == I2S_ROLE_SLAVE)
{
modifyreg32(I2S_CONF_REG(priv->config->port), 0, I2S_RX_SLAVE_MOD);
}
else
{
* 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);
}
}
* On ESP32, sample_bit should equals to data_bit.
*/
priv->data_width = priv->config->data_width;
i2s_set_datawidth(priv);
modifyreg32(I2S_CONF_CHAN_REG(priv->config->port),
I2S_RX_CHAN_MOD_M, 0);
* 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);
}
* 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);
}
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);
}
modifyreg32(I2S_FIFO_CONF_REG(priv->config->port), 0,
I2S_RX_FIFO_MOD_FORCE_EN);
* 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_setmclkfrequency` 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;
}
i2s_setmclkfrequency((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/RX data width.
*
* Input Parameters:
* priv - Initialized I2S device structure.
*
* Returned Value:
* Returns the resulting data width
*
****************************************************************************/
static uint32_t i2s_set_datawidth(struct esp32_i2s_s *priv)
{
#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));
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));
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);
}
}
#endif
#ifdef I2S_HAVE_RX
if (priv->config->rx_en)
{
modifyreg32(I2S_SAMPLE_RATE_CONF_REG(priv->config->port),
I2S_RX_BITS_MOD_M, FIELD_TO_VALUE(I2S_RX_BITS_MOD,
priv->data_width));
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));
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
return priv->data_width;
}
* Name: i2s_set_clock
*
* Description:
* Set the I2S TX sample rate by adjusting I2S clock.
*
* Input Parameters:
* priv - Initialized I2S device structure.
*
* Returned Value:
* Returns the resulting bitrate
*
****************************************************************************/
static uint32_t i2s_set_clock(struct esp32_i2s_s *priv)
{
uint32_t rate;
uint32_t bclk;
uint32_t mclk;
uint32_t sclk;
uint32_t mclk_div;
int denominator;
int numerator;
uint32_t regval;
uint32_t freq_diff;
uint16_t bclk_div;
* clock.
*/
modifyreg32(I2S_CLKM_CONF_REG(priv->config->port), I2S_CLKA_ENA, 0);
sclk = I2S_LL_BASE_CLK;
* mclk_div is the I2S clock divider's integral value
* b is the fraction clock divider's numerator value
* a is the fraction clock divider's denominator value
*/
if (priv->config->role == I2S_ROLE_MASTER)
{
bclk = priv->rate * priv->config->total_slot * priv->data_width;
mclk = priv->mclk_freq;
bclk_div = mclk / bclk;
}
else
{
bclk_div = 8;
bclk = priv->rate * priv->config->total_slot * priv->data_width;
mclk = bclk * bclk_div;
}
mclk_div = sclk / mclk;
i2sinfo("Clock division info: [sclk]%" PRIu32 " Hz [mdiv] %d "
"[mclk] %" PRIu32 " Hz [bdiv] %d [bclk] %" PRIu32 " Hz\n",
sclk, mclk_div, mclk, bclk_div, bclk);
freq_diff = abs((int)sclk - (int)(mclk * mclk_div));
denominator = 1;
numerator = 0;
if (freq_diff)
{
float decimal = freq_diff / (float)mclk;
* 1.0 - 1.0 / (63.0 * 2) = 125.0 / 126.0
*/
if (decimal > 125.0f / 126.0f)
{
mclk_div++;
}
else
{
uint32_t min = UINT32_MAX;
for (int a = 2; a <= I2S_LL_MCLK_DIVIDER_MAX; a++)
{
int b = (int)(a * (freq_diff / (double)mclk) + 0.5);
int ma = freq_diff * a;
int mb = mclk * b;
if (ma == mb)
{
denominator = a;
numerator = b;
break;
}
if (abs((mb - ma)) < min)
{
denominator = a;
numerator = b;
min = abs(mb - ma);
}
}
}
}
i2sinfo("Clock register: [mclk] %" PRIu32 " Hz [numerator] %d "
"[denominator] %d\n", mclk, numerator, denominator);
regval = getreg32(I2S_CLKM_CONF_REG(priv->config->port));
regval &= ~I2S_CLKM_DIV_NUM_M;
regval |= FIELD_TO_VALUE(I2S_CLKM_DIV_NUM, mclk_div);
regval &= ~I2S_CLKM_DIV_B_M;
regval |= FIELD_TO_VALUE(I2S_CLKM_DIV_B, numerator);
regval &= ~I2S_CLKM_DIV_A_M;
regval |= FIELD_TO_VALUE(I2S_CLKM_DIV_A, denominator);
putreg32(regval, I2S_CLKM_CONF_REG(priv->config->port));
#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
#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
bclk = sclk / (float)((mclk_div + numerator / (float)denominator) *
bclk_div);
rate = bclk / (float)(priv->config->total_slot * priv->data_width);
return rate;
}
* Name: i2s_tx_channel_start
*
* Description:
* Start TX channel for the I2S port
*
* Input Parameters:
* priv - Initialized I2S device structure.
*
* Returned Value:
* None
*
****************************************************************************/
#ifdef I2S_HAVE_TX
static void i2s_tx_channel_start(struct esp32_i2s_s *priv)
{
if (priv->config->tx_en)
{
if (priv->tx_started)
{
i2swarn("TX channel of port %d was previously started\n",
priv->config->port);
return;
}
modifyreg32(I2S_CONF_REG(priv->config->port), 0, I2S_TX_RESET);
modifyreg32(I2S_CONF_REG(priv->config->port), I2S_TX_RESET, 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);
modifyreg32(I2S_CONF_REG(priv->config->port), 0, I2S_TX_FIFO_RESET);
modifyreg32(I2S_CONF_REG(priv->config->port), I2S_TX_FIFO_RESET, 0);
up_enable_irq(priv->config->irq);
modifyreg32(I2S_INT_ENA_REG(priv->config->port), 0,
I2S_OUT_EOF_INT_ENA);
modifyreg32(I2S_FIFO_CONF_REG(priv->config->port), 0, I2S_DSCR_EN);
putreg32(0, I2S_OUT_LINK_REG(priv->config->port));
priv->tx_started = true;
i2sinfo("Started TX channel of port %d\n", priv->config->port);
}
}
#endif
* Name: i2s_rx_channel_start
*
* Description:
* Start 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_start(struct esp32_i2s_s *priv)
{
if (priv->config->rx_en)
{
if (priv->rx_started)
{
i2swarn("RX channel of port %d was previously started\n",
priv->config->port);
return;
}
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);
modifyreg32(I2S_CONF_REG(priv->config->port), 0, I2S_RX_FIFO_RESET);
modifyreg32(I2S_CONF_REG(priv->config->port), I2S_RX_FIFO_RESET, 0);
up_enable_irq(priv->config->irq);
modifyreg32(I2S_INT_ENA_REG(priv->config->port), 0,
I2S_IN_SUC_EOF_INT_ENA);
modifyreg32(I2S_FIFO_CONF_REG(priv->config->port), 0, I2S_DSCR_EN);
putreg32(0, I2S_IN_LINK_REG(priv->config->port));
priv->rx_started = true;
i2sinfo("Started RX channel of port %d\n", priv->config->port);
}
}
#endif
* Name: i2s_tx_channel_stop
*
* Description:
* Stop TX channel for the I2S port
*
* Input Parameters:
* priv - Initialized I2S device structure.
*
* Returned Value:
* None
*
****************************************************************************/
#ifdef I2S_HAVE_TX
static void i2s_tx_channel_stop(struct esp32_i2s_s *priv)
{
if (priv->config->tx_en)
{
if (!priv->tx_started)
{
i2swarn("TX channel of port %d was previously stopped\n",
priv->config->port);
return;
}
modifyreg32(I2S_CONF_REG(priv->config->port), I2S_TX_START, 0);
modifyreg32(I2S_OUT_LINK_REG(priv->config->port), I2S_OUTLINK_START,
I2S_OUTLINK_STOP);
modifyreg32(I2S_INT_ENA_REG(priv->config->port),
I2S_OUT_EOF_INT_ENA, 0);
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
* 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;
}
modifyreg32(I2S_CONF_REG(priv->config->port), I2S_RX_START, 0);
modifyreg32(I2S_IN_LINK_REG(priv->config->port), I2S_INLINK_START,
I2S_INLINK_STOP);
modifyreg32(I2S_INT_ENA_REG(priv->config->port),
I2S_IN_SUC_EOF_INT_ENA, 0);
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
* Name: 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 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));
i2s_tx_schedule(priv, cur);
}
}
#endif
#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));
i2s_rx_schedule(priv, cur);
}
}
#endif
return 0;
}
* Name: i2s_getmclkfrequency
*
* Description:
* Get the current master clock frequency.
*
* Input Parameters:
* dev - Device-specific state data
*
* Returned Value:
* Returns the current master clock.
*
****************************************************************************/
static uint32_t i2s_getmclkfrequency(struct i2s_dev_s *dev)
{
struct esp32_i2s_s *priv = (struct esp32_i2s_s *)dev;
return priv->mclk_freq;
}
* Name: i2s_setmclkfrequency
*
* Description:
* Set the master clock frequency. Usually, the MCLK is a multiple of the
* sample rate. Most of the audio codecs require setting specific MCLK
* frequency according to the sample rate.
*
* Input Parameters:
* dev - Device-specific state data
* frequency - The I2S master clock's frequency
*
* Returned Value:
* Returns the resulting master clock or a negated errno value on failure.
*
****************************************************************************/
static uint32_t i2s_setmclkfrequency(struct i2s_dev_s *dev,
uint32_t frequency)
{
struct esp32_i2s_s *priv = (struct esp32_i2s_s *)dev;
* value and return an error.
*/
if (frequency >= (I2S_LL_BASE_CLK / 2))
{
return -EINVAL;
}
priv->mclk_freq = frequency;
return frequency;
}
* Name: i2s_txchannels
*
* Description:
* Set the I2S TX number of channels.
*
* Input Parameters:
* dev - Device-specific state data
* channels - The I2S numbers of channels
*
* Returned Value:
* OK on success; a negated errno value on failure.
*
****************************************************************************/
#ifdef I2S_HAVE_TX
static int i2s_txchannels(struct i2s_dev_s *dev, uint8_t channels)
{
struct esp32_i2s_s *priv = (struct esp32_i2s_s *)dev;
if (priv->config->tx_en)
{
if (channels != 1 && channels != 2)
{
return -EINVAL;
}
priv->channels = channels;
return OK;
}
return -ENOTTY;
}
#endif
* Name: 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 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
* Name: i2s_txsamplerate
*
* Description:
* Set the I2S TX sample rate. NOTE: This will have no effect if (1) the
* driver does not support an I2S transmitter or if (2) the sample rate is
* driven by the I2S frame clock. This may also have unexpected side-
* effects of the TX sample is coupled with the RX sample rate.
*
* Input Parameters:
* dev - Device-specific state data
* rate - The I2S sample rate in samples (not bits) per second
*
* Returned Value:
* Returns the resulting bitrate
*
****************************************************************************/
#ifdef I2S_HAVE_TX
static uint32_t i2s_txsamplerate(struct i2s_dev_s *dev, uint32_t rate)
{
struct esp32_i2s_s *priv = (struct esp32_i2s_s *)dev;
if (priv->config->tx_en)
{
i2s_tx_channel_stop(priv);
priv->rate = rate;
rate = i2s_set_clock(priv);
i2s_tx_channel_start(priv);
return rate;
}
return 0;
}
#endif
* Name: 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 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
* Name: i2s_txdatawidth
*
* Description:
* Set the I2S TX data width. The TX 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_TX
static uint32_t i2s_txdatawidth(struct i2s_dev_s *dev, int bits)
{
struct esp32_i2s_s *priv = (struct esp32_i2s_s *)dev;
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
* Name: 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 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;
i2s_set_datawidth(priv);
i2s_rx_channel_start(priv);
return bits;
}
return 0;
}
#endif
* Name: i2s_send
*
* Description:
* Send a block of data on I2S.
*
* Input Parameters:
* dev - Device-specific state data
* apb - A pointer to the audio buffer from which to send 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_TX
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;
if (priv->config->tx_en)
{
struct esp32_buffer_s *bfcontainer;
int ret = OK;
uint32_t nbytes;
uint32_t nsamp;
nbytes = (apb->nbytes - apb->curbyte) + priv->tx.carry.bytes;
* 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);
* is mono, it is necessary to copy zero-ed data between each sample.
*/
nbytes *= priv->channels == 1 ? 2 : 1;
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;
}
bfcontainer = i2s_buf_allocate(priv);
DEBUGASSERT(bfcontainer);
ret = nxmutex_lock(&priv->lock);
if (ret < 0)
{
goto errout_with_buf;
}
apb_reference(apb);
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
* Name: 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 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;
if (priv->config->rx_en)
{
struct esp32_buffer_s *bfcontainer;
int ret = OK;
uint32_t nbytes;
uint32_t nsamp;
nbytes = apb->nmaxbytes;
* 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);
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;
}
bfcontainer = i2s_buf_allocate(priv);
DEBUGASSERT(bfcontainer);
ret = nxmutex_lock(&priv->lock);
if (ret < 0)
{
goto errout_with_buf;
}
apb_reference(apb);
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;
}
return -ENOTTY;
}
#endif
* Name: i2s_cleanup_queues
*
* Description:
* Wait for all buffers to be processed and free them after
*
****************************************************************************/
#ifdef I2S_HAVE_RX
static void i2s_cleanup_queues(struct esp32_i2s_s *priv)
{
irqstate_t flags;
struct esp32_buffer_s *bfcontainer;
while (sq_peek(&priv->rx.done) != NULL)
{
flags = enter_critical_section();
bfcontainer = (struct esp32_buffer_s *)sq_remfirst(&priv->rx.done);
leave_critical_section(flags);
bfcontainer->callback(&priv->dev, bfcontainer->apb,
bfcontainer->arg, OK);
apb_free(bfcontainer->apb);
i2s_buf_free(priv, bfcontainer);
}
while (sq_peek(&priv->rx.act) != NULL)
{
flags = enter_critical_section();
bfcontainer = (struct esp32_buffer_s *)sq_remfirst(&priv->rx.act);
leave_critical_section(flags);
bfcontainer->callback(&priv->dev, bfcontainer->apb,
bfcontainer->arg, OK);
apb_free(bfcontainer->apb);
i2s_buf_free(priv, bfcontainer);
}
while (sq_peek(&priv->rx.pend) != NULL)
{
flags = enter_critical_section();
bfcontainer = (struct esp32_buffer_s *)sq_remfirst(&priv->rx.pend);
leave_critical_section(flags);
bfcontainer->apb->flags |= AUDIO_APB_FINAL;
bfcontainer->callback(&priv->dev, bfcontainer->apb,
bfcontainer->arg, OK);
apb_free(bfcontainer->apb);
i2s_buf_free(priv, bfcontainer);
}
}
#endif
* Name: i2s_ioctl
*
* Description:
* Perform a device ioctl
*
****************************************************************************/
static int i2s_ioctl(struct i2s_dev_s *dev, int cmd, unsigned long arg)
{
struct audio_buf_desc_s *bufdesc;
int ret = -ENOTTY;
switch (cmd)
{
*
* ioctl argument: Audio session
*/
case AUDIOIOC_START:
{
i2sinfo("AUDIOIOC_START\n");
ret = OK;
}
break;
*
* ioctl argument: Audio session
*/
case AUDIOIOC_STOP:
{
i2sinfo("AUDIOIOC_STOP\n");
#ifdef I2S_HAVE_RX
struct esp32_i2s_s *priv = (struct esp32_i2s_s *)dev;
i2s_cleanup_queues(priv);
#endif
ret = OK;
}
break;
*
* ioctl argument: pointer to an audio_buf_desc_s structure
*/
case AUDIOIOC_ALLOCBUFFER:
{
i2sinfo("AUDIOIOC_ALLOCBUFFER\n");
bufdesc = (struct audio_buf_desc_s *) arg;
ret = apb_alloc(bufdesc);
}
break;
*
* ioctl argument: pointer to an audio_buf_desc_s structure
*/
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;
}
return ret;
}
* Name: i2s_dma_setup
*
* Description:
* Configure the DMA for the I2S peripheral
*
* Input Parameters:
* priv - Partially initialized I2S device structure. This function
* will complete the I2S specific portions of the initialization
* regarding the DMA operation.
*
* Returned Value:
* OK on success; A negated errno value on failure.
*
****************************************************************************/
static int i2s_dma_setup(struct esp32_i2s_s *priv)
{
int ret;
putreg32(UINT32_MAX, I2S_INT_CLR_REG(priv->config->port));
priv->cpu = this_cpu();
priv->cpuint = esp32_setup_irq(priv->cpu, priv->config->periph,
1, ESP32_CPUINT_LEVEL);
if (priv->cpuint < 0)
{
i2serr("Failed to allocate a CPU interrupt.\n");
return priv->cpuint;
}
ret = irq_attach(priv->config->irq, i2s_interrupt, priv);
if (ret != OK)
{
i2serr("Couldn't attach IRQ to handler.\n");
esp32_teardown_irq(priv->cpu,
priv->config->periph,
priv->cpuint);
return ret;
}
return OK;
}
* Name: esp32_i2sbus_initialize
*
* Description:
* Initialize the selected I2S port
*
* Input Parameters:
* Port number (for hardware that has multiple I2S interfaces)
*
* Returned Value:
* Valid I2S device structure reference on success; a NULL on failure
*
****************************************************************************/
struct i2s_dev_s *esp32_i2sbus_initialize(int port)
{
int ret;
struct esp32_i2s_s *priv = NULL;
irqstate_t flags;
i2sinfo("port: %d\n", port);
switch (port)
{
#ifdef CONFIG_ESP32_I2S0
case ESP32_I2S0:
priv = &esp32_i2s0_priv;
break;
#endif
#ifdef CONFIG_ESP32_I2S1
case ESP32_I2S1:
priv = &esp32_i2s1_priv;
break;
#endif
default:
return NULL;
}
flags = spin_lock_irqsave(&priv->slock);
i2s_configure(priv);
ret = i2s_buf_initialize(priv);
if (ret < 0)
{
goto err;
}
ret = i2s_dma_setup(priv);
if (ret < 0)
{
goto err;
}
#ifdef I2S_HAVE_TX
if (priv->config->tx_en)
{
priv->tx_started = false;
i2s_tx_channel_start(priv);
}
#endif
#ifdef I2S_HAVE_RX
if (priv->config->rx_en)
{
priv->rx_started = false;
i2s_rx_channel_start(priv);
}
#endif
spin_unlock_irqrestore(&priv->slock, flags);
i2sinfo("I2S%d was successfully initialized\n", priv->config->port);
return &priv->dev;
err:
spin_unlock_irqrestore(&priv->slock, flags);
return NULL;
}
#endif