1153 lines
29 KiB
C
1153 lines
29 KiB
C
/*
|
|
* Copyright (c) 2018 STMicroelectronics
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
#include <string.h>
|
|
#include <dma.h>
|
|
#include <i2s.h>
|
|
#include <soc.h>
|
|
#include <clock_control/stm32_clock_control.h>
|
|
#include <clock_control.h>
|
|
|
|
#include "i2s_ll_stm32.h"
|
|
#include <logging/log.h>
|
|
LOG_MODULE_REGISTER(i2s_ll_stm32);
|
|
|
|
/* FIXME change to
|
|
* #if __DCACHE_PRESENT == 1
|
|
* when cache support is added
|
|
*/
|
|
#if 0
|
|
#define DCACHE_INVALIDATE(addr, size) \
|
|
SCB_InvalidateDCache_by_Addr((u32_t *)addr, size)
|
|
#define DCACHE_CLEAN(addr, size) \
|
|
SCB_CleanDCache_by_Addr((u32_t *)addr, size)
|
|
#else
|
|
#define DCACHE_INVALIDATE(addr, size) {; }
|
|
#define DCACHE_CLEAN(addr, size) {; }
|
|
#endif
|
|
|
|
#define MODULO_INC(val, max) { val = (++val < max) ? val : 0; }
|
|
|
|
static unsigned int div_round_closest(u32_t dividend, u32_t divisor)
|
|
{
|
|
return (dividend + (divisor / 2)) / divisor;
|
|
}
|
|
|
|
/*
|
|
* Get data from the queue
|
|
*/
|
|
static int queue_get(struct ring_buf *rb, void **mem_block, size_t *size)
|
|
{
|
|
unsigned int key;
|
|
|
|
key = irq_lock();
|
|
|
|
if (rb->tail == rb->head) {
|
|
/* Ring buffer is empty */
|
|
irq_unlock(key);
|
|
return -ENOMEM;
|
|
}
|
|
|
|
*mem_block = rb->buf[rb->tail].mem_block;
|
|
*size = rb->buf[rb->tail].size;
|
|
MODULO_INC(rb->tail, rb->len);
|
|
|
|
irq_unlock(key);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Put data in the queue
|
|
*/
|
|
static int queue_put(struct ring_buf *rb, void *mem_block, size_t size)
|
|
{
|
|
u16_t head_next;
|
|
unsigned int key;
|
|
|
|
key = irq_lock();
|
|
|
|
head_next = rb->head;
|
|
MODULO_INC(head_next, rb->len);
|
|
|
|
if (head_next == rb->tail) {
|
|
/* Ring buffer is full */
|
|
irq_unlock(key);
|
|
return -ENOMEM;
|
|
}
|
|
|
|
rb->buf[rb->head].mem_block = mem_block;
|
|
rb->buf[rb->head].size = size;
|
|
rb->head = head_next;
|
|
|
|
irq_unlock(key);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int i2s_stm32_enable_clock(struct device *dev)
|
|
{
|
|
const struct i2s_stm32_cfg *cfg = DEV_CFG(dev);
|
|
struct device *clk;
|
|
int ret;
|
|
|
|
clk = device_get_binding(STM32_CLOCK_CONTROL_NAME);
|
|
__ASSERT_NO_MSG(clk);
|
|
|
|
ret = clock_control_on(clk, (clock_control_subsys_t *) &cfg->pclken);
|
|
if (ret != 0) {
|
|
LOG_ERR("Could not enable I2S clock");
|
|
return -EIO;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
#ifdef CONFIG_I2S_STM32_USE_PLLI2S_ENABLE
|
|
#define PLLI2S_MAX_MS_TIME 1 /* PLLI2S lock time is 300us max */
|
|
static u16_t plli2s_ms_count;
|
|
|
|
#define _pllr(v) LL_RCC_PLLI2SR_DIV_ ## v
|
|
#define pllr(v) _pllr(v)
|
|
#endif
|
|
|
|
static int i2s_stm32_set_clock(struct device *dev, u32_t bit_clk_freq)
|
|
{
|
|
const struct i2s_stm32_cfg *cfg = DEV_CFG(dev);
|
|
u32_t pll_src = LL_RCC_PLL_GetMainSource();
|
|
int freq_in;
|
|
u8_t i2s_div, i2s_odd;
|
|
|
|
freq_in = (pll_src == LL_RCC_PLLSOURCE_HSI) ?
|
|
HSI_VALUE : CONFIG_CLOCK_STM32_HSE_CLOCK;
|
|
|
|
#ifdef CONFIG_I2S_STM32_USE_PLLI2S_ENABLE
|
|
/* Set PLLI2S */
|
|
LL_RCC_PLLI2S_Disable();
|
|
LL_RCC_PLLI2S_ConfigDomain_I2S(pll_src,
|
|
CONFIG_I2S_STM32_PLLI2S_PLLM,
|
|
CONFIG_I2S_STM32_PLLI2S_PLLN,
|
|
pllr(CONFIG_I2S_STM32_PLLI2S_PLLR));
|
|
LL_RCC_PLLI2S_Enable();
|
|
|
|
/* wait until PLLI2S gets locked */
|
|
while (!LL_RCC_PLLI2S_IsReady()) {
|
|
if (plli2s_ms_count++ > PLLI2S_MAX_MS_TIME) {
|
|
return -EIO;
|
|
}
|
|
|
|
/* wait 1 ms */
|
|
k_sleep(1);
|
|
}
|
|
LOG_DBG("PLLI2S is locked");
|
|
|
|
/* Adjust freq_in according to PLLM, PLLN, PLLR */
|
|
float freq_tmp;
|
|
|
|
freq_tmp = freq_in / CONFIG_I2S_STM32_PLLI2S_PLLM;
|
|
freq_tmp *= CONFIG_I2S_STM32_PLLI2S_PLLN;
|
|
freq_tmp /= CONFIG_I2S_STM32_PLLI2S_PLLR;
|
|
freq_in = (int) freq_tmp;
|
|
#endif /* CONFIG_I2S_STM32_USE_PLLI2S_ENABLE */
|
|
|
|
/* Select clock source */
|
|
LL_RCC_SetI2SClockSource(cfg->i2s_clk_sel);
|
|
|
|
/*
|
|
* The ratio between input clock (I2SxClk) and output
|
|
* clock on the pad (I2S_CK) is obtained using the
|
|
* following formula:
|
|
* (i2s_div * 2) + i2s_odd
|
|
*/
|
|
i2s_div = div_round_closest(freq_in, bit_clk_freq);
|
|
i2s_odd = (i2s_div & 0x1) ? 1 : 0;
|
|
i2s_div >>= 1;
|
|
|
|
LOG_DBG("i2s_div: %d - i2s_odd: %d", i2s_div, i2s_odd);
|
|
|
|
LL_I2S_SetPrescalerLinear(cfg->i2s, i2s_div);
|
|
LL_I2S_SetPrescalerParity(cfg->i2s, i2s_odd);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int i2s_stm32_configure(struct device *dev, enum i2s_dir dir,
|
|
struct i2s_config *i2s_cfg)
|
|
{
|
|
const struct i2s_stm32_cfg *const cfg = DEV_CFG(dev);
|
|
struct i2s_stm32_data *const dev_data = DEV_DATA(dev);
|
|
struct stream *stream;
|
|
u32_t bit_clk_freq;
|
|
int ret;
|
|
|
|
if (dir == I2S_DIR_RX) {
|
|
stream = &dev_data->rx;
|
|
} else if (dir == I2S_DIR_TX) {
|
|
stream = &dev_data->tx;
|
|
} else {
|
|
LOG_ERR("Either RX or TX direction must be selected");
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (stream->state != I2S_STATE_NOT_READY &&
|
|
stream->state != I2S_STATE_READY) {
|
|
LOG_ERR("invalid state");
|
|
return -EINVAL;
|
|
}
|
|
|
|
stream->master = true;
|
|
if (i2s_cfg->options & I2S_OPT_FRAME_CLK_SLAVE ||
|
|
i2s_cfg->options & I2S_OPT_BIT_CLK_SLAVE) {
|
|
stream->master = false;
|
|
}
|
|
|
|
if (i2s_cfg->frame_clk_freq == 0) {
|
|
stream->queue_drop(stream);
|
|
memset(&stream->cfg, 0, sizeof(struct i2s_config));
|
|
stream->state = I2S_STATE_NOT_READY;
|
|
return 0;
|
|
}
|
|
|
|
memcpy(&stream->cfg, i2s_cfg, sizeof(struct i2s_config));
|
|
|
|
/* set I2S bitclock */
|
|
bit_clk_freq = i2s_cfg->frame_clk_freq *
|
|
i2s_cfg->word_size * i2s_cfg->channels;
|
|
|
|
ret = i2s_stm32_set_clock(dev, bit_clk_freq);
|
|
if (ret < 0) {
|
|
return ret;
|
|
}
|
|
|
|
/* set I2S Data Format */
|
|
if (i2s_cfg->word_size == 16) {
|
|
LL_I2S_SetDataFormat(cfg->i2s, LL_I2S_DATAFORMAT_16B);
|
|
} else if (i2s_cfg->word_size == 24) {
|
|
LL_I2S_SetDataFormat(cfg->i2s, LL_I2S_DATAFORMAT_24B);
|
|
} else if (i2s_cfg->word_size == 32) {
|
|
LL_I2S_SetDataFormat(cfg->i2s, LL_I2S_DATAFORMAT_32B);
|
|
} else {
|
|
LOG_ERR("invalid word size");
|
|
return -EINVAL;
|
|
}
|
|
|
|
/* set I2S Standard */
|
|
switch (i2s_cfg->format & I2S_FMT_DATA_FORMAT_MASK) {
|
|
case I2S_FMT_DATA_FORMAT_I2S:
|
|
LL_I2S_SetStandard(cfg->i2s, LL_I2S_STANDARD_PHILIPS);
|
|
break;
|
|
|
|
case I2S_FMT_DATA_FORMAT_PCM_SHORT:
|
|
LL_I2S_SetStandard(cfg->i2s, LL_I2S_STANDARD_PCM_SHORT);
|
|
break;
|
|
|
|
case I2S_FMT_DATA_FORMAT_PCM_LONG:
|
|
LL_I2S_SetStandard(cfg->i2s, LL_I2S_STANDARD_PCM_LONG);
|
|
break;
|
|
|
|
case I2S_FMT_DATA_FORMAT_LEFT_JUSTIFIED:
|
|
LL_I2S_SetStandard(cfg->i2s, LL_I2S_STANDARD_MSB);
|
|
break;
|
|
|
|
case I2S_FMT_DATA_FORMAT_RIGHT_JUSTIFIED:
|
|
LL_I2S_SetStandard(cfg->i2s, LL_I2S_STANDARD_LSB);
|
|
break;
|
|
|
|
default:
|
|
LOG_ERR("Unsupported I2S data format");
|
|
return -EINVAL;
|
|
}
|
|
|
|
/* set I2S clock polarity */
|
|
if ((i2s_cfg->format & I2S_FMT_CLK_FORMAT_MASK) == I2S_FMT_BIT_CLK_INV)
|
|
LL_I2S_SetClockPolarity(cfg->i2s, LL_I2S_POLARITY_HIGH);
|
|
else
|
|
LL_I2S_SetClockPolarity(cfg->i2s, LL_I2S_POLARITY_LOW);
|
|
|
|
stream->state = I2S_STATE_READY;
|
|
return 0;
|
|
}
|
|
|
|
static int i2s_stm32_trigger(struct device *dev, enum i2s_dir dir,
|
|
enum i2s_trigger_cmd cmd)
|
|
{
|
|
struct i2s_stm32_data *const dev_data = DEV_DATA(dev);
|
|
struct stream *stream;
|
|
unsigned int key;
|
|
int ret;
|
|
|
|
if (dir == I2S_DIR_RX) {
|
|
stream = &dev_data->rx;
|
|
} else if (dir == I2S_DIR_TX) {
|
|
stream = &dev_data->tx;
|
|
} else {
|
|
LOG_ERR("Either RX or TX direction must be selected");
|
|
return -EINVAL;
|
|
}
|
|
|
|
switch (cmd) {
|
|
case I2S_TRIGGER_START:
|
|
if (stream->state != I2S_STATE_READY) {
|
|
LOG_ERR("START trigger: invalid state %d",
|
|
stream->state);
|
|
return -EIO;
|
|
}
|
|
|
|
__ASSERT_NO_MSG(stream->mem_block == NULL);
|
|
|
|
ret = stream->stream_start(stream, dev);
|
|
if (ret < 0) {
|
|
LOG_ERR("START trigger failed %d", ret);
|
|
return ret;
|
|
}
|
|
|
|
stream->state = I2S_STATE_RUNNING;
|
|
stream->last_block = false;
|
|
break;
|
|
|
|
case I2S_TRIGGER_STOP:
|
|
key = irq_lock();
|
|
if (stream->state != I2S_STATE_RUNNING) {
|
|
irq_unlock(key);
|
|
LOG_ERR("STOP trigger: invalid state");
|
|
return -EIO;
|
|
}
|
|
irq_unlock(key);
|
|
stream->stream_disable(stream, dev);
|
|
stream->queue_drop(stream);
|
|
stream->state = I2S_STATE_READY;
|
|
stream->last_block = true;
|
|
break;
|
|
|
|
case I2S_TRIGGER_DRAIN:
|
|
key = irq_lock();
|
|
if (stream->state != I2S_STATE_RUNNING) {
|
|
irq_unlock(key);
|
|
LOG_ERR("DRAIN trigger: invalid state");
|
|
return -EIO;
|
|
}
|
|
stream->stream_disable(stream, dev);
|
|
stream->queue_drop(stream);
|
|
stream->state = I2S_STATE_READY;
|
|
irq_unlock(key);
|
|
break;
|
|
|
|
case I2S_TRIGGER_DROP:
|
|
if (stream->state == I2S_STATE_NOT_READY) {
|
|
LOG_ERR("DROP trigger: invalid state");
|
|
return -EIO;
|
|
}
|
|
stream->stream_disable(stream, dev);
|
|
stream->queue_drop(stream);
|
|
stream->state = I2S_STATE_READY;
|
|
break;
|
|
|
|
case I2S_TRIGGER_PREPARE:
|
|
if (stream->state != I2S_STATE_ERROR) {
|
|
LOG_ERR("PREPARE trigger: invalid state");
|
|
return -EIO;
|
|
}
|
|
stream->state = I2S_STATE_READY;
|
|
stream->queue_drop(stream);
|
|
break;
|
|
|
|
default:
|
|
LOG_ERR("Unsupported trigger command");
|
|
return -EINVAL;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int i2s_stm32_read(struct device *dev, void **mem_block, size_t *size)
|
|
{
|
|
struct i2s_stm32_data *const dev_data = DEV_DATA(dev);
|
|
int ret;
|
|
|
|
if (dev_data->rx.state == I2S_STATE_NOT_READY) {
|
|
LOG_DBG("invalid state");
|
|
return -EIO;
|
|
}
|
|
|
|
if (dev_data->rx.state != I2S_STATE_ERROR) {
|
|
ret = k_sem_take(&dev_data->rx.sem, dev_data->rx.cfg.timeout);
|
|
if (ret < 0) {
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
/* Get data from the beginning of RX queue */
|
|
ret = queue_get(&dev_data->rx.mem_block_queue, mem_block, size);
|
|
if (ret < 0) {
|
|
return -EIO;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int i2s_stm32_write(struct device *dev, void *mem_block, size_t size)
|
|
{
|
|
struct i2s_stm32_data *const dev_data = DEV_DATA(dev);
|
|
int ret;
|
|
|
|
if (dev_data->tx.state != I2S_STATE_RUNNING &&
|
|
dev_data->tx.state != I2S_STATE_READY) {
|
|
LOG_DBG("invalid state");
|
|
return -EIO;
|
|
}
|
|
|
|
ret = k_sem_take(&dev_data->tx.sem, dev_data->tx.cfg.timeout);
|
|
if (ret < 0) {
|
|
return ret;
|
|
}
|
|
|
|
/* Add data to the end of the TX queue */
|
|
queue_put(&dev_data->tx.mem_block_queue, mem_block, size);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static const struct i2s_driver_api i2s_stm32_driver_api = {
|
|
.configure = i2s_stm32_configure,
|
|
.read = i2s_stm32_read,
|
|
.write = i2s_stm32_write,
|
|
.trigger = i2s_stm32_trigger,
|
|
};
|
|
|
|
#define STM32_DMA_NUM_CHANNELS 8
|
|
static struct device *active_dma_rx_channel[STM32_DMA_NUM_CHANNELS];
|
|
static struct device *active_dma_tx_channel[STM32_DMA_NUM_CHANNELS];
|
|
|
|
static int start_dma(struct device *dev_dma, u32_t channel,
|
|
struct dma_config *dcfg, void *src, void *dst,
|
|
u32_t blk_size)
|
|
{
|
|
struct dma_block_config blk_cfg;
|
|
int ret;
|
|
|
|
memset(&blk_cfg, 0, sizeof(blk_cfg));
|
|
blk_cfg.block_size = blk_size / sizeof(u16_t);
|
|
blk_cfg.source_address = (u32_t)src;
|
|
blk_cfg.dest_address = (u32_t)dst;
|
|
|
|
dcfg->head_block = &blk_cfg;
|
|
|
|
ret = dma_config(dev_dma, channel, dcfg);
|
|
if (ret < 0) {
|
|
return ret;
|
|
}
|
|
|
|
ret = dma_start(dev_dma, channel);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static struct device *get_dev_from_rx_dma_channel(u32_t dma_channel);
|
|
static struct device *get_dev_from_tx_dma_channel(u32_t dma_channel);
|
|
static void rx_stream_disable(struct stream *stream, struct device *dev);
|
|
static void tx_stream_disable(struct stream *stream, struct device *dev);
|
|
|
|
/* This function is executed in the interrupt context */
|
|
static void dma_rx_callback(void *arg, u32_t channel, int status)
|
|
{
|
|
struct device *dev = get_dev_from_rx_dma_channel(channel);
|
|
const struct i2s_stm32_cfg *cfg = DEV_CFG(dev);
|
|
struct i2s_stm32_data *const dev_data = DEV_DATA(dev);
|
|
struct stream *stream = &dev_data->rx;
|
|
void *mblk_tmp;
|
|
int ret;
|
|
|
|
if (status != 0) {
|
|
ret = -EIO;
|
|
stream->state = I2S_STATE_ERROR;
|
|
goto rx_disable;
|
|
}
|
|
|
|
__ASSERT_NO_MSG(stream->mem_block != NULL);
|
|
|
|
/* Stop reception if there was an error */
|
|
if (stream->state == I2S_STATE_ERROR) {
|
|
goto rx_disable;
|
|
}
|
|
|
|
mblk_tmp = stream->mem_block;
|
|
|
|
/* Prepare to receive the next data block */
|
|
ret = k_mem_slab_alloc(stream->cfg.mem_slab, &stream->mem_block,
|
|
K_NO_WAIT);
|
|
if (ret < 0) {
|
|
stream->state = I2S_STATE_ERROR;
|
|
goto rx_disable;
|
|
}
|
|
|
|
ret = start_dma(dev_data->dev_dma, stream->dma_channel,
|
|
&stream->dma_cfg,
|
|
(void *)LL_SPI_DMA_GetRegAddr(cfg->i2s),
|
|
stream->mem_block,
|
|
stream->cfg.block_size);
|
|
if (ret < 0) {
|
|
LOG_DBG("Failed to start RX DMA transfer: %d", ret);
|
|
goto rx_disable;
|
|
}
|
|
|
|
/* Assure cache coherency after DMA write operation */
|
|
DCACHE_INVALIDATE(mblk_tmp, stream->cfg.block_size);
|
|
|
|
/* All block data received */
|
|
ret = queue_put(&stream->mem_block_queue, mblk_tmp,
|
|
stream->cfg.block_size);
|
|
if (ret < 0) {
|
|
stream->state = I2S_STATE_ERROR;
|
|
goto rx_disable;
|
|
}
|
|
k_sem_give(&stream->sem);
|
|
|
|
/* Stop reception if we were requested */
|
|
if (stream->state == I2S_STATE_STOPPING) {
|
|
stream->state = I2S_STATE_READY;
|
|
goto rx_disable;
|
|
}
|
|
|
|
return;
|
|
|
|
rx_disable:
|
|
rx_stream_disable(stream, dev);
|
|
}
|
|
|
|
static void dma_tx_callback(void *arg, u32_t channel, int status)
|
|
{
|
|
struct device *dev = get_dev_from_tx_dma_channel(channel);
|
|
const struct i2s_stm32_cfg *cfg = DEV_CFG(dev);
|
|
struct i2s_stm32_data *const dev_data = DEV_DATA(dev);
|
|
struct stream *stream = &dev_data->tx;
|
|
size_t mem_block_size;
|
|
int ret;
|
|
|
|
if (status != 0) {
|
|
ret = -EIO;
|
|
stream->state = I2S_STATE_ERROR;
|
|
goto tx_disable;
|
|
}
|
|
|
|
__ASSERT_NO_MSG(stream->mem_block != NULL);
|
|
|
|
/* All block data sent */
|
|
k_mem_slab_free(stream->cfg.mem_slab, &stream->mem_block);
|
|
stream->mem_block = NULL;
|
|
|
|
/* Stop transmission if there was an error */
|
|
if (stream->state == I2S_STATE_ERROR) {
|
|
LOG_ERR("TX error detected");
|
|
goto tx_disable;
|
|
}
|
|
|
|
/* Stop transmission if we were requested */
|
|
if (stream->last_block) {
|
|
stream->state = I2S_STATE_READY;
|
|
goto tx_disable;
|
|
}
|
|
|
|
/* Prepare to send the next data block */
|
|
ret = queue_get(&stream->mem_block_queue, &stream->mem_block,
|
|
&mem_block_size);
|
|
if (ret < 0) {
|
|
if (stream->state == I2S_STATE_STOPPING) {
|
|
stream->state = I2S_STATE_READY;
|
|
} else {
|
|
stream->state = I2S_STATE_ERROR;
|
|
}
|
|
goto tx_disable;
|
|
}
|
|
k_sem_give(&stream->sem);
|
|
|
|
/* Assure cache coherency before DMA read operation */
|
|
DCACHE_CLEAN(stream->mem_block, mem_block_size);
|
|
|
|
ret = start_dma(dev_data->dev_dma, stream->dma_channel,
|
|
&stream->dma_cfg,
|
|
stream->mem_block,
|
|
(void *)LL_SPI_DMA_GetRegAddr(cfg->i2s),
|
|
stream->cfg.block_size);
|
|
if (ret < 0) {
|
|
LOG_DBG("Failed to start TX DMA transfer: %d", ret);
|
|
goto tx_disable;
|
|
}
|
|
|
|
return;
|
|
|
|
tx_disable:
|
|
tx_stream_disable(stream, dev);
|
|
}
|
|
|
|
static u32_t i2s_stm32_irq_count;
|
|
static u32_t i2s_stm32_irq_ovr_count;
|
|
|
|
static void i2s_stm32_isr(void *arg)
|
|
{
|
|
struct device *const dev = (struct device *) arg;
|
|
const struct i2s_stm32_cfg *cfg = DEV_CFG(dev);
|
|
struct i2s_stm32_data *const dev_data = DEV_DATA(dev);
|
|
struct stream *stream = &dev_data->rx;
|
|
|
|
LOG_ERR("%s: err=%d", __func__, LL_I2S_ReadReg(cfg->i2s, SR));
|
|
stream->state = I2S_STATE_ERROR;
|
|
|
|
/* OVR error must be explicitly cleared */
|
|
if (LL_I2S_IsActiveFlag_OVR(cfg->i2s)) {
|
|
i2s_stm32_irq_ovr_count++;
|
|
LL_I2S_ClearFlag_OVR(cfg->i2s);
|
|
}
|
|
|
|
i2s_stm32_irq_count++;
|
|
}
|
|
|
|
static int i2s_stm32_initialize(struct device *dev)
|
|
{
|
|
const struct i2s_stm32_cfg *cfg = DEV_CFG(dev);
|
|
struct i2s_stm32_data *const dev_data = DEV_DATA(dev);
|
|
int ret, i;
|
|
|
|
/* Enable I2S clock propagation */
|
|
ret = i2s_stm32_enable_clock(dev);
|
|
if (ret < 0) {
|
|
LOG_ERR("%s: clock enabling failed: %d", __func__, ret);
|
|
return -EIO;
|
|
}
|
|
|
|
cfg->irq_config(dev);
|
|
|
|
k_sem_init(&dev_data->rx.sem, 0, CONFIG_I2S_STM32_RX_BLOCK_COUNT);
|
|
k_sem_init(&dev_data->tx.sem, CONFIG_I2S_STM32_TX_BLOCK_COUNT,
|
|
CONFIG_I2S_STM32_TX_BLOCK_COUNT);
|
|
|
|
for (i = 0; i < STM32_DMA_NUM_CHANNELS; i++) {
|
|
active_dma_rx_channel[i] = NULL;
|
|
active_dma_tx_channel[i] = NULL;
|
|
}
|
|
|
|
/* Get the binding to the DMA device */
|
|
dev_data->dev_dma = device_get_binding(dev_data->dma_name);
|
|
if (!dev_data->dev_dma) {
|
|
LOG_ERR("%s device not found", dev_data->dma_name);
|
|
return -ENODEV;
|
|
}
|
|
|
|
LOG_INF("%s inited", dev->config->name);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int rx_stream_start(struct stream *stream, struct device *dev)
|
|
{
|
|
const struct i2s_stm32_cfg *cfg = DEV_CFG(dev);
|
|
struct i2s_stm32_data *const dev_data = DEV_DATA(dev);
|
|
int ret;
|
|
|
|
ret = k_mem_slab_alloc(stream->cfg.mem_slab, &stream->mem_block,
|
|
K_NO_WAIT);
|
|
if (ret < 0) {
|
|
return ret;
|
|
}
|
|
|
|
if (stream->master) {
|
|
LL_I2S_SetTransferMode(cfg->i2s, LL_I2S_MODE_MASTER_RX);
|
|
} else {
|
|
LL_I2S_SetTransferMode(cfg->i2s, LL_I2S_MODE_SLAVE_RX);
|
|
}
|
|
|
|
/* remember active RX DMA channel (used in callback) */
|
|
active_dma_rx_channel[stream->dma_channel] = dev;
|
|
|
|
ret = start_dma(dev_data->dev_dma, stream->dma_channel,
|
|
&stream->dma_cfg,
|
|
(void *)LL_SPI_DMA_GetRegAddr(cfg->i2s),
|
|
stream->mem_block,
|
|
stream->cfg.block_size);
|
|
if (ret < 0) {
|
|
LOG_ERR("Failed to start RX DMA transfer: %d", ret);
|
|
return ret;
|
|
}
|
|
|
|
LL_I2S_EnableDMAReq_RX(cfg->i2s);
|
|
|
|
LL_I2S_EnableIT_ERR(cfg->i2s);
|
|
LL_I2S_Enable(cfg->i2s);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int tx_stream_start(struct stream *stream, struct device *dev)
|
|
{
|
|
const struct i2s_stm32_cfg *cfg = DEV_CFG(dev);
|
|
struct i2s_stm32_data *const dev_data = DEV_DATA(dev);
|
|
size_t mem_block_size;
|
|
int ret;
|
|
|
|
ret = queue_get(&stream->mem_block_queue, &stream->mem_block,
|
|
&mem_block_size);
|
|
if (ret < 0) {
|
|
return ret;
|
|
}
|
|
k_sem_give(&stream->sem);
|
|
|
|
/* Assure cache coherency before DMA read operation */
|
|
DCACHE_CLEAN(stream->mem_block, mem_block_size);
|
|
|
|
if (stream->master) {
|
|
LL_I2S_SetTransferMode(cfg->i2s, LL_I2S_MODE_MASTER_TX);
|
|
} else {
|
|
LL_I2S_SetTransferMode(cfg->i2s, LL_I2S_MODE_SLAVE_TX);
|
|
}
|
|
|
|
/* remember active TX DMA channel (used in callback) */
|
|
active_dma_tx_channel[stream->dma_channel] = dev;
|
|
|
|
ret = start_dma(dev_data->dev_dma, stream->dma_channel,
|
|
&stream->dma_cfg,
|
|
stream->mem_block,
|
|
(void *)LL_SPI_DMA_GetRegAddr(cfg->i2s),
|
|
stream->cfg.block_size);
|
|
if (ret < 0) {
|
|
LOG_ERR("Failed to start TX DMA transfer: %d", ret);
|
|
return ret;
|
|
}
|
|
|
|
LL_I2S_EnableDMAReq_TX(cfg->i2s);
|
|
|
|
LL_I2S_EnableIT_ERR(cfg->i2s);
|
|
LL_I2S_Enable(cfg->i2s);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void rx_stream_disable(struct stream *stream, struct device *dev)
|
|
{
|
|
const struct i2s_stm32_cfg *cfg = DEV_CFG(dev);
|
|
struct i2s_stm32_data *const dev_data = DEV_DATA(dev);
|
|
struct device *dev_dma = dev_data->dev_dma;
|
|
|
|
LL_I2S_DisableDMAReq_RX(cfg->i2s);
|
|
LL_I2S_DisableIT_ERR(cfg->i2s);
|
|
|
|
dma_stop(dev_dma, stream->dma_channel);
|
|
if (stream->mem_block != NULL) {
|
|
k_mem_slab_free(stream->cfg.mem_slab, &stream->mem_block);
|
|
stream->mem_block = NULL;
|
|
}
|
|
|
|
LL_I2S_Disable(cfg->i2s);
|
|
|
|
active_dma_rx_channel[stream->dma_channel] = NULL;
|
|
}
|
|
|
|
static void tx_stream_disable(struct stream *stream, struct device *dev)
|
|
{
|
|
const struct i2s_stm32_cfg *cfg = DEV_CFG(dev);
|
|
struct i2s_stm32_data *const dev_data = DEV_DATA(dev);
|
|
struct device *dev_dma = dev_data->dev_dma;
|
|
|
|
LL_I2S_DisableDMAReq_TX(cfg->i2s);
|
|
LL_I2S_DisableIT_ERR(cfg->i2s);
|
|
|
|
dma_stop(dev_dma, stream->dma_channel);
|
|
if (stream->mem_block != NULL) {
|
|
k_mem_slab_free(stream->cfg.mem_slab, &stream->mem_block);
|
|
stream->mem_block = NULL;
|
|
}
|
|
|
|
LL_I2S_Disable(cfg->i2s);
|
|
|
|
active_dma_tx_channel[stream->dma_channel] = NULL;
|
|
}
|
|
|
|
static void rx_queue_drop(struct stream *stream)
|
|
{
|
|
size_t size;
|
|
void *mem_block;
|
|
|
|
while (queue_get(&stream->mem_block_queue, &mem_block, &size) == 0) {
|
|
k_mem_slab_free(stream->cfg.mem_slab, &mem_block);
|
|
}
|
|
|
|
k_sem_reset(&stream->sem);
|
|
}
|
|
|
|
static void tx_queue_drop(struct stream *stream)
|
|
{
|
|
size_t size;
|
|
void *mem_block;
|
|
unsigned int n = 0U;
|
|
|
|
while (queue_get(&stream->mem_block_queue, &mem_block, &size) == 0) {
|
|
k_mem_slab_free(stream->cfg.mem_slab, &mem_block);
|
|
n++;
|
|
}
|
|
|
|
for (; n > 0; n--) {
|
|
k_sem_give(&stream->sem);
|
|
}
|
|
}
|
|
|
|
static struct device *get_dev_from_rx_dma_channel(u32_t dma_channel)
|
|
{
|
|
return active_dma_rx_channel[dma_channel];
|
|
}
|
|
|
|
static struct device *get_dev_from_tx_dma_channel(u32_t dma_channel)
|
|
{
|
|
return active_dma_tx_channel[dma_channel];
|
|
}
|
|
|
|
#ifdef CONFIG_I2S_1
|
|
static struct device DEVICE_NAME_GET(i2s_stm32_1);
|
|
|
|
static void i2s_stm32_irq_config_func_1(struct device *dev);
|
|
|
|
static const struct i2s_stm32_cfg i2s_stm32_config_1 = {
|
|
.i2s = (SPI_TypeDef *) DT_I2S_1_BASE_ADDRESS,
|
|
.pclken = {
|
|
.enr = DT_I2S_1_CLOCK_BITS,
|
|
.bus = DT_I2S_1_CLOCK_BUS,
|
|
},
|
|
.i2s_clk_sel = CLK_SEL_2,
|
|
.irq_config = i2s_stm32_irq_config_func_1,
|
|
};
|
|
|
|
struct queue_item rx_1_ring_buf[CONFIG_I2S_STM32_RX_BLOCK_COUNT + 1];
|
|
struct queue_item tx_1_ring_buf[CONFIG_I2S_STM32_TX_BLOCK_COUNT + 1];
|
|
|
|
static struct i2s_stm32_data i2s_stm32_data_1 = {
|
|
.dma_name = I2S1_DMA_NAME,
|
|
.rx = {
|
|
.dma_channel = I2S1_DMA_CHAN_RX,
|
|
.dma_cfg = {
|
|
.block_count = 1,
|
|
.dma_slot = I2S1_DMA_SLOT_RX,
|
|
.channel_direction = PERIPHERAL_TO_MEMORY,
|
|
.source_data_size = 1, /* 16bit default */
|
|
.dest_data_size = 1, /* 16bit default */
|
|
.source_burst_length = 0, /* SINGLE transfer */
|
|
.dest_burst_length = 1,
|
|
.dma_callback = dma_rx_callback,
|
|
},
|
|
.stream_start = rx_stream_start,
|
|
.stream_disable = rx_stream_disable,
|
|
.queue_drop = rx_queue_drop,
|
|
.mem_block_queue.buf = rx_1_ring_buf,
|
|
.mem_block_queue.len = ARRAY_SIZE(rx_1_ring_buf),
|
|
},
|
|
.tx = {
|
|
.dma_channel = I2S1_DMA_CHAN_TX,
|
|
.dma_cfg = {
|
|
.block_count = 1,
|
|
.dma_slot = I2S1_DMA_SLOT_TX,
|
|
.channel_direction = MEMORY_TO_PERIPHERAL,
|
|
.source_data_size = 1, /* 16bit default */
|
|
.dest_data_size = 1, /* 16bit default */
|
|
.source_burst_length = 1,
|
|
.dest_burst_length = 0, /* SINGLE transfer */
|
|
.dma_callback = dma_tx_callback,
|
|
},
|
|
.stream_start = tx_stream_start,
|
|
.stream_disable = tx_stream_disable,
|
|
.queue_drop = tx_queue_drop,
|
|
.mem_block_queue.buf = tx_1_ring_buf,
|
|
.mem_block_queue.len = ARRAY_SIZE(tx_1_ring_buf),
|
|
},
|
|
};
|
|
DEVICE_AND_API_INIT(i2s_stm32_1, DT_I2S_1_NAME, &i2s_stm32_initialize,
|
|
&i2s_stm32_data_1, &i2s_stm32_config_1, POST_KERNEL,
|
|
CONFIG_I2S_INIT_PRIORITY, &i2s_stm32_driver_api);
|
|
|
|
static void i2s_stm32_irq_config_func_1(struct device *dev)
|
|
{
|
|
IRQ_CONNECT(DT_I2S_1_IRQ, DT_I2S_1_IRQ_PRI, i2s_stm32_isr,
|
|
DEVICE_GET(i2s_stm32_1), 0);
|
|
irq_enable(DT_I2S_1_IRQ);
|
|
}
|
|
|
|
#endif /* CONFIG_I2S_1 */
|
|
|
|
#ifdef CONFIG_I2S_2
|
|
static struct device DEVICE_NAME_GET(i2s_stm32_2);
|
|
|
|
static void i2s_stm32_irq_config_func_2(struct device *dev);
|
|
|
|
static const struct i2s_stm32_cfg i2s_stm32_config_2 = {
|
|
.i2s = (SPI_TypeDef *) DT_I2S_2_BASE_ADDRESS,
|
|
.pclken = {
|
|
.enr = DT_I2S_2_CLOCK_BITS,
|
|
.bus = DT_I2S_2_CLOCK_BUS,
|
|
},
|
|
.i2s_clk_sel = CLK_SEL_1,
|
|
.irq_config = i2s_stm32_irq_config_func_2,
|
|
};
|
|
|
|
struct queue_item rx_2_ring_buf[CONFIG_I2S_STM32_RX_BLOCK_COUNT + 1];
|
|
struct queue_item tx_2_ring_buf[CONFIG_I2S_STM32_TX_BLOCK_COUNT + 1];
|
|
|
|
static struct i2s_stm32_data i2s_stm32_data_2 = {
|
|
.dma_name = I2S2_DMA_NAME,
|
|
.rx = {
|
|
.dma_channel = I2S2_DMA_CHAN_RX,
|
|
.dma_cfg = {
|
|
.block_count = 1,
|
|
.dma_slot = I2S2_DMA_SLOT_RX,
|
|
.channel_direction = PERIPHERAL_TO_MEMORY,
|
|
.source_data_size = 1, /* 16bit default */
|
|
.dest_data_size = 1, /* 16bit default */
|
|
.source_burst_length = 0, /* SINGLE transfer */
|
|
.dest_burst_length = 1,
|
|
.dma_callback = dma_rx_callback,
|
|
},
|
|
.stream_start = rx_stream_start,
|
|
.stream_disable = rx_stream_disable,
|
|
.queue_drop = rx_queue_drop,
|
|
.mem_block_queue.buf = rx_2_ring_buf,
|
|
.mem_block_queue.len = ARRAY_SIZE(rx_2_ring_buf),
|
|
},
|
|
.tx = {
|
|
.dma_channel = I2S2_DMA_CHAN_TX,
|
|
.dma_cfg = {
|
|
.block_count = 1,
|
|
.dma_slot = I2S2_DMA_SLOT_TX,
|
|
.channel_direction = MEMORY_TO_PERIPHERAL,
|
|
.source_data_size = 1, /* 16bit default */
|
|
.dest_data_size = 1, /* 16bit default */
|
|
.source_burst_length = 1,
|
|
.dest_burst_length = 0, /* SINGLE transfer */
|
|
.dma_callback = dma_tx_callback,
|
|
},
|
|
.stream_start = tx_stream_start,
|
|
.stream_disable = tx_stream_disable,
|
|
.queue_drop = tx_queue_drop,
|
|
.mem_block_queue.buf = tx_2_ring_buf,
|
|
.mem_block_queue.len = ARRAY_SIZE(tx_2_ring_buf),
|
|
},
|
|
};
|
|
DEVICE_AND_API_INIT(i2s_stm32_2, DT_I2S_2_NAME, &i2s_stm32_initialize,
|
|
&i2s_stm32_data_2, &i2s_stm32_config_2, POST_KERNEL,
|
|
CONFIG_I2S_INIT_PRIORITY, &i2s_stm32_driver_api);
|
|
|
|
static void i2s_stm32_irq_config_func_2(struct device *dev)
|
|
{
|
|
IRQ_CONNECT(DT_I2S_2_IRQ, DT_I2S_2_IRQ_PRI, i2s_stm32_isr,
|
|
DEVICE_GET(i2s_stm32_2), 0);
|
|
irq_enable(DT_I2S_2_IRQ);
|
|
}
|
|
|
|
#endif /* CONFIG_I2S_2 */
|
|
|
|
#ifdef CONFIG_I2S_3
|
|
static struct device DEVICE_NAME_GET(i2s_stm32_3);
|
|
|
|
static void i2s_stm32_irq_config_func_3(struct device *dev);
|
|
|
|
static const struct i2s_stm32_cfg i2s_stm32_config_3 = {
|
|
.i2s = (SPI_TypeDef *) DT_I2S_3_BASE_ADDRESS,
|
|
.pclken = {
|
|
.enr = DT_I2S_3_CLOCK_BITS,
|
|
.bus = DT_I2S_3_CLOCK_BUS,
|
|
},
|
|
.i2s_clk_sel = CLK_SEL_1,
|
|
.irq_config = i2s_stm32_irq_config_func_3,
|
|
};
|
|
|
|
struct queue_item rx_3_ring_buf[CONFIG_I2S_STM32_RX_BLOCK_COUNT + 1];
|
|
struct queue_item tx_3_ring_buf[CONFIG_I2S_STM32_TX_BLOCK_COUNT + 1];
|
|
|
|
static struct i2s_stm32_data i2s_stm32_data_3 = {
|
|
.dma_name = I2S3_DMA_NAME,
|
|
.rx = {
|
|
.dma_channel = I2S3_DMA_CHAN_RX,
|
|
.dma_cfg = {
|
|
.block_count = 1,
|
|
.dma_slot = I2S3_DMA_SLOT_RX,
|
|
.channel_direction = PERIPHERAL_TO_MEMORY,
|
|
.source_data_size = 1, /* 16bit default */
|
|
.dest_data_size = 1, /* 16bit default */
|
|
.source_burst_length = 0, /* SINGLE transfer */
|
|
.dest_burst_length = 1,
|
|
.dma_callback = dma_rx_callback,
|
|
},
|
|
.stream_start = rx_stream_start,
|
|
.stream_disable = rx_stream_disable,
|
|
.queue_drop = rx_queue_drop,
|
|
.mem_block_queue.buf = rx_3_ring_buf,
|
|
.mem_block_queue.len = ARRAY_SIZE(rx_3_ring_buf),
|
|
},
|
|
.tx = {
|
|
.dma_channel = I2S3_DMA_CHAN_TX,
|
|
.dma_cfg = {
|
|
.block_count = 1,
|
|
.dma_slot = I2S3_DMA_SLOT_TX,
|
|
.channel_direction = MEMORY_TO_PERIPHERAL,
|
|
.source_data_size = 1, /* 16bit default */
|
|
.dest_data_size = 1, /* 16bit default */
|
|
.source_burst_length = 1,
|
|
.dest_burst_length = 0, /* SINGLE transfer */
|
|
.dma_callback = dma_tx_callback,
|
|
},
|
|
.stream_start = tx_stream_start,
|
|
.stream_disable = tx_stream_disable,
|
|
.queue_drop = tx_queue_drop,
|
|
.mem_block_queue.buf = tx_3_ring_buf,
|
|
.mem_block_queue.len = ARRAY_SIZE(tx_3_ring_buf),
|
|
},
|
|
};
|
|
DEVICE_AND_API_INIT(i2s_stm32_3, DT_I2S_3_NAME, &i2s_stm32_initialize,
|
|
&i2s_stm32_data_3, &i2s_stm32_config_3, POST_KERNEL,
|
|
CONFIG_I2S_INIT_PRIORITY, &i2s_stm32_driver_api);
|
|
|
|
static void i2s_stm32_irq_config_func_3(struct device *dev)
|
|
{
|
|
IRQ_CONNECT(DT_I2S_3_IRQ, DT_I2S_3_IRQ_PRI, i2s_stm32_isr,
|
|
DEVICE_GET(i2s_stm32_3), 0);
|
|
irq_enable(DT_I2S_3_IRQ);
|
|
}
|
|
|
|
#endif /* CONFIG_I2S_3 */
|
|
|
|
#ifdef CONFIG_I2S_4
|
|
static struct device DEVICE_NAME_GET(i2s_stm32_4);
|
|
|
|
static void i2s_stm32_irq_config_func_4(struct device *dev);
|
|
|
|
static const struct i2s_stm32_cfg i2s_stm32_config_4 = {
|
|
.i2s = (SPI_TypeDef *) DT_I2S_4_BASE_ADDRESS,
|
|
.pclken = {
|
|
.enr = DT_I2S_4_CLOCK_BITS,
|
|
.bus = DT_I2S_4_CLOCK_BUS,
|
|
},
|
|
.i2s_clk_sel = CLK_SEL_2,
|
|
.irq_config = i2s_stm32_irq_config_func_4,
|
|
};
|
|
|
|
struct queue_item rx_4_ring_buf[CONFIG_I2S_STM32_RX_BLOCK_COUNT + 1];
|
|
struct queue_item tx_4_ring_buf[CONFIG_I2S_STM32_TX_BLOCK_COUNT + 1];
|
|
|
|
static struct i2s_stm32_data i2s_stm32_data_4 = {
|
|
.dma_name = I2S4_DMA_NAME,
|
|
.rx = {
|
|
.dma_channel = I2S4_DMA_CHAN_RX,
|
|
.dma_cfg = {
|
|
.block_count = 1,
|
|
.dma_slot = I2S4_DMA_SLOT_RX,
|
|
.channel_direction = PERIPHERAL_TO_MEMORY,
|
|
.source_data_size = 1, /* 16bit default */
|
|
.dest_data_size = 1, /* 16bit default */
|
|
.source_burst_length = 0, /* SINGLE transfer */
|
|
.dest_burst_length = 1,
|
|
.dma_callback = dma_rx_callback,
|
|
},
|
|
.stream_start = rx_stream_start,
|
|
.stream_disable = rx_stream_disable,
|
|
.queue_drop = rx_queue_drop,
|
|
.mem_block_queue.buf = rx_4_ring_buf,
|
|
.mem_block_queue.len = ARRAY_SIZE(rx_4_ring_buf),
|
|
},
|
|
.tx = {
|
|
.dma_channel = I2S4_DMA_CHAN_TX,
|
|
.dma_cfg = {
|
|
.block_count = 1,
|
|
.dma_slot = I2S4_DMA_SLOT_TX,
|
|
.channel_direction = MEMORY_TO_PERIPHERAL,
|
|
.source_data_size = 1, /* 16bit default */
|
|
.dest_data_size = 1, /* 16bit default */
|
|
.source_burst_length = 1,
|
|
.dest_burst_length = 0, /* SINGLE transfer */
|
|
.dma_callback = dma_tx_callback,
|
|
},
|
|
.stream_start = tx_stream_start,
|
|
.stream_disable = tx_stream_disable,
|
|
.queue_drop = tx_queue_drop,
|
|
.mem_block_queue.buf = tx_4_ring_buf,
|
|
.mem_block_queue.len = ARRAY_SIZE(tx_4_ring_buf),
|
|
},
|
|
};
|
|
DEVICE_AND_API_INIT(i2s_stm32_4, DT_I2S_4_NAME, &i2s_stm32_initialize,
|
|
&i2s_stm32_data_4, &i2s_stm32_config_4, POST_KERNEL,
|
|
CONFIG_I2S_INIT_PRIORITY, &i2s_stm32_driver_api);
|
|
|
|
static void i2s_stm32_irq_config_func_4(struct device *dev)
|
|
{
|
|
IRQ_CONNECT(DT_I2S_4_IRQ, DT_I2S_4_IRQ_PRI, i2s_stm32_isr,
|
|
DEVICE_GET(i2s_stm32_4), 0);
|
|
irq_enable(DT_I2S_4_IRQ);
|
|
}
|
|
|
|
#endif /* CONFIG_I2S_4 */
|
|
|
|
#ifdef CONFIG_I2S_5
|
|
static struct device DEVICE_NAME_GET(i2s_stm32_5);
|
|
|
|
static void i2s_stm32_irq_config_func_5(struct device *dev);
|
|
|
|
static const struct i2s_stm32_cfg i2s_stm32_config_5 = {
|
|
.i2s = (SPI_TypeDef *) DT_I2S_5_BASE_ADDRESS,
|
|
.pclken = {
|
|
.enr = DT_I2S_5_CLOCK_BITS,
|
|
.bus = DT_I2S_5_CLOCK_BUS,
|
|
},
|
|
.i2s_clk_sel = CLK_SEL_2,
|
|
.irq_config = i2s_stm32_irq_config_func_5,
|
|
};
|
|
|
|
struct queue_item rx_5_ring_buf[CONFIG_I2S_STM32_RX_BLOCK_COUNT + 1];
|
|
struct queue_item tx_5_ring_buf[CONFIG_I2S_STM32_TX_BLOCK_COUNT + 1];
|
|
|
|
static struct i2s_stm32_data i2s_stm32_data_5 = {
|
|
.dma_name = I2S5_DMA_NAME,
|
|
.rx = {
|
|
.dma_channel = I2S5_DMA_CHAN_RX,
|
|
.dma_cfg = {
|
|
.block_count = 1,
|
|
.dma_slot = I2S5_DMA_SLOT_RX,
|
|
.channel_direction = PERIPHERAL_TO_MEMORY,
|
|
.source_data_size = 1, /* 16bit default */
|
|
.dest_data_size = 1, /* 16bit default */
|
|
.source_burst_length = 0, /* SINGLE transfer */
|
|
.dest_burst_length = 1,
|
|
.dma_callback = dma_rx_callback,
|
|
},
|
|
.stream_start = rx_stream_start,
|
|
.stream_disable = rx_stream_disable,
|
|
.queue_drop = rx_queue_drop,
|
|
.mem_block_queue.buf = rx_5_ring_buf,
|
|
.mem_block_queue.len = ARRAY_SIZE(rx_5_ring_buf),
|
|
},
|
|
.tx = {
|
|
.dma_channel = I2S5_DMA_CHAN_TX,
|
|
.dma_cfg = {
|
|
.block_count = 1,
|
|
.dma_slot = I2S5_DMA_SLOT_TX,
|
|
.channel_direction = MEMORY_TO_PERIPHERAL,
|
|
.source_data_size = 1, /* 16bit default */
|
|
.dest_data_size = 1, /* 16bit default */
|
|
.source_burst_length = 1,
|
|
.dest_burst_length = 0, /* SINGLE transfer */
|
|
.dma_callback = dma_tx_callback,
|
|
},
|
|
.stream_start = tx_stream_start,
|
|
.stream_disable = tx_stream_disable,
|
|
.queue_drop = tx_queue_drop,
|
|
.mem_block_queue.buf = tx_5_ring_buf,
|
|
.mem_block_queue.len = ARRAY_SIZE(tx_5_ring_buf),
|
|
},
|
|
};
|
|
DEVICE_AND_API_INIT(i2s_stm32_5, DT_I2S_5_NAME, &i2s_stm32_initialize,
|
|
&i2s_stm32_data_5, &i2s_stm32_config_5, POST_KERNEL,
|
|
CONFIG_I2S_INIT_PRIORITY, &i2s_stm32_driver_api);
|
|
|
|
static void i2s_stm32_irq_config_func_5(struct device *dev)
|
|
{
|
|
IRQ_CONNECT(DT_I2S_5_IRQ, DT_I2S_5_IRQ_PRI, i2s_stm32_isr,
|
|
DEVICE_GET(i2s_stm32_5), 0);
|
|
irq_enable(DT_I2S_5_IRQ);
|
|
}
|
|
|
|
#endif /* CONFIG_I2S_5 */
|