Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

drivers: i2s_nrfx: Support less than block size writes #67314

Merged
merged 2 commits into from
Jan 15, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
79 changes: 55 additions & 24 deletions drivers/i2s/i2s_nrfx.c
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,11 @@ struct stream_cfg {
nrfx_i2s_config_t nrfx_cfg;
};

struct i2s_buf {
void *mem_block;
size_t size;
};

struct i2s_nrfx_drv_data {
struct onoff_manager *clk_mgr;
struct onoff_client clk_cli;
Expand Down Expand Up @@ -189,9 +194,14 @@ static void find_suitable_clock(const struct i2s_nrfx_drv_cfg *drv_cfg,
static bool get_next_tx_buffer(struct i2s_nrfx_drv_data *drv_data,
nrfx_i2s_buffers_t *buffers)
{
struct i2s_buf buf;
int ret = k_msgq_get(&drv_data->tx_queue,
&buffers->p_tx_buffer,
&buf,
K_NO_WAIT);
if (ret == 0) {
buffers->p_tx_buffer = buf.mem_block;
buffers->buffer_size = buf.size / sizeof(uint32_t);
}
return (ret == 0);
}

Expand Down Expand Up @@ -226,16 +236,23 @@ static void free_rx_buffer(struct i2s_nrfx_drv_data *drv_data, void *buffer)
static bool supply_next_buffers(struct i2s_nrfx_drv_data *drv_data,
nrfx_i2s_buffers_t *next)
{
drv_data->last_tx_buffer = next->p_tx_buffer;

if (drv_data->active_dir != I2S_DIR_TX) { /* -> RX active */
if (!get_next_rx_buffer(drv_data, next)) {
drv_data->state = I2S_STATE_ERROR;
nrfx_i2s_stop(drv_data->p_i2s);
return false;
}
/* Set buffer size if there is no TX buffer (which effectively
* controls how many bytes will be received).
*/
if (drv_data->active_dir == I2S_DIR_RX) {
next->buffer_size =
drv_data->rx.cfg.block_size / sizeof(uint32_t);
}
}

drv_data->last_tx_buffer = next->p_tx_buffer;

LOG_DBG("Next buffers: %p/%p", next->p_tx_buffer, next->p_rx_buffer);
nrfx_i2s_next_buffers_set(drv_data->p_i2s, next);
return true;
Expand Down Expand Up @@ -294,8 +311,12 @@ static void data_handler(const struct device *dev,
if (drv_data->discard_rx) {
free_rx_buffer(drv_data, released->p_rx_buffer);
} else {
struct i2s_buf buf = {
.mem_block = released->p_rx_buffer,
.size = released->buffer_size * sizeof(uint32_t)
};
int ret = k_msgq_put(&drv_data->rx_queue,
&released->p_rx_buffer,
&buf,
K_NO_WAIT);
if (ret < 0) {
LOG_ERR("No room in RX queue");
Expand Down Expand Up @@ -345,6 +366,7 @@ static void data_handler(const struct device *dev,
* before this buffer would be started again).
*/
next.p_tx_buffer = drv_data->last_tx_buffer;
next.buffer_size = 1;
} else if (get_next_tx_buffer(drv_data, &next)) {
/* Next TX buffer successfully retrieved from
* the queue, nothing more to do here.
Expand All @@ -361,6 +383,7 @@ static void data_handler(const struct device *dev,
* will be stopped earlier.
*/
next.p_tx_buffer = drv_data->last_tx_buffer;
next.buffer_size = 1;
} else {
/* Next TX buffer cannot be supplied now.
* Defer it to when the user writes more data.
Expand All @@ -377,21 +400,21 @@ static void data_handler(const struct device *dev,
static void purge_queue(const struct device *dev, enum i2s_dir dir)
{
struct i2s_nrfx_drv_data *drv_data = dev->data;
void *mem_block;
struct i2s_buf buf;

if (dir == I2S_DIR_TX || dir == I2S_DIR_BOTH) {
while (k_msgq_get(&drv_data->tx_queue,
&mem_block,
&buf,
K_NO_WAIT) == 0) {
free_tx_buffer(drv_data, mem_block);
free_tx_buffer(drv_data, buf.mem_block);
}
}

if (dir == I2S_DIR_RX || dir == I2S_DIR_BOTH) {
while (k_msgq_get(&drv_data->rx_queue,
&mem_block,
&buf,
K_NO_WAIT) == 0) {
free_rx_buffer(drv_data, mem_block);
free_rx_buffer(drv_data, buf.mem_block);
}
}
}
Expand Down Expand Up @@ -554,6 +577,7 @@ static int i2s_nrfx_read(const struct device *dev,
void **mem_block, size_t *size)
{
struct i2s_nrfx_drv_data *drv_data = dev->data;
struct i2s_buf buf;
int ret;

if (!drv_data->rx_configured) {
Expand All @@ -562,18 +586,19 @@ static int i2s_nrfx_read(const struct device *dev,
}

ret = k_msgq_get(&drv_data->rx_queue,
mem_block,
&buf,
(drv_data->state == I2S_STATE_ERROR)
? K_NO_WAIT
: SYS_TIMEOUT_MS(drv_data->rx.cfg.timeout));
if (ret == -ENOMSG) {
return -EIO;
}

LOG_DBG("Released RX %p", *mem_block);
LOG_DBG("Released RX %p", buf.mem_block);

if (ret == 0) {
*size = drv_data->rx.cfg.block_size;
*mem_block = buf.mem_block;
*size = buf.size;
}

return ret;
Expand All @@ -583,6 +608,7 @@ static int i2s_nrfx_write(const struct device *dev,
void *mem_block, size_t size)
{
struct i2s_nrfx_drv_data *drv_data = dev->data;
struct i2s_buf buf = { .mem_block = mem_block, .size = size };
int ret;

if (!drv_data->tx_configured) {
Expand All @@ -596,14 +622,14 @@ static int i2s_nrfx_write(const struct device *dev,
return -EIO;
}

if (size != drv_data->tx.cfg.block_size) {
anangl marked this conversation as resolved.
Show resolved Hide resolved
LOG_ERR("This device can only write blocks of %u bytes",
if (size > drv_data->tx.cfg.block_size || size < sizeof(uint32_t)) {
LOG_ERR("This device can only write blocks up to %u bytes",
drv_data->tx.cfg.block_size);
return -EIO;
}

ret = k_msgq_put(&drv_data->tx_queue,
&mem_block,
&buf,
SYS_TIMEOUT_MS(drv_data->tx.cfg.timeout));
if (ret < 0) {
return ret;
Expand Down Expand Up @@ -656,15 +682,20 @@ static int start_transfer(struct i2s_nrfx_drv_data *drv_data)
/* Failed to allocate next RX buffer */
ret = -ENOMEM;
} else {
uint32_t block_size = (drv_data->active_dir == I2S_DIR_TX)
? drv_data->tx.cfg.block_size
: drv_data->rx.cfg.block_size;
nrfx_err_t err;

/* It is necessary to set buffer size here only for I2S_DIR_RX,
* because only then the get_next_tx_buffer() call in the if
* condition above gets short-circuited.
*/
if (drv_data->active_dir == I2S_DIR_RX) {
initial_buffers.buffer_size =
drv_data->rx.cfg.block_size / sizeof(uint32_t);
}
anangl marked this conversation as resolved.
Show resolved Hide resolved

drv_data->last_tx_buffer = initial_buffers.p_tx_buffer;

err = nrfx_i2s_start(drv_data->p_i2s, &initial_buffers,
block_size / sizeof(uint32_t), 0);
err = nrfx_i2s_start(drv_data->p_i2s, &initial_buffers, 0);
if (err == NRFX_SUCCESS) {
return 0;
}
Expand Down Expand Up @@ -898,8 +929,8 @@ static const struct i2s_driver_api i2s_nrf_drv_api = {
#define I2S_CLK_SRC(idx) DT_STRING_TOKEN(I2S(idx), clock_source)

#define I2S_NRFX_DEVICE(idx) \
static void *tx_msgs##idx[CONFIG_I2S_NRFX_TX_BLOCK_COUNT]; \
static void *rx_msgs##idx[CONFIG_I2S_NRFX_RX_BLOCK_COUNT]; \
static struct i2s_buf tx_msgs##idx[CONFIG_I2S_NRFX_TX_BLOCK_COUNT]; \
static struct i2s_buf rx_msgs##idx[CONFIG_I2S_NRFX_RX_BLOCK_COUNT]; \
static void data_handler##idx(nrfx_i2s_buffers_t const *p_released, \
uint32_t status) \
{ \
Expand Down Expand Up @@ -935,10 +966,10 @@ static const struct i2s_driver_api i2s_nrf_drv_api = {
return err; \
} \
k_msgq_init(&i2s_nrfx_data##idx.tx_queue, \
(char *)tx_msgs##idx, sizeof(void *), \
(char *)tx_msgs##idx, sizeof(struct i2s_buf), \
ARRAY_SIZE(tx_msgs##idx)); \
k_msgq_init(&i2s_nrfx_data##idx.rx_queue, \
(char *)rx_msgs##idx, sizeof(void *), \
(char *)rx_msgs##idx, sizeof(struct i2s_buf), \
ARRAY_SIZE(rx_msgs##idx)); \
init_clock_manager(dev); \
return 0; \
Expand Down
2 changes: 1 addition & 1 deletion modules/hal_nordic/nrfx/nrfx_config_common.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@

/** @brief Symbol specifying minor version of the nrfx API to be used. */
#ifndef NRFX_CONFIG_API_VER_MINOR
#define NRFX_CONFIG_API_VER_MINOR 2
#define NRFX_CONFIG_API_VER_MINOR 3
#endif

/** @brief Symbol specifying micro version of the nrfx API to be used. */
Expand Down
Loading