Skip to content

Commit 07d9e52

Browse files
tmon-nordiccarlescufi
authored andcommitted
drivers: i2s_nrfx: Support less than block size writes
Calling I2S write with less bytes than I2S TX memory block size makes it possible to synchronize the time when next block is used against some other, possibly externally sourced, signal. Example use case includes USB Audio where audio sink and/or source has to be synchronized against USB SOF. In Asynchronous synchronization type the rate matching is achieved by varying the number of samples sent/received by 1 sample (e.g. for 48 kHz audio, 47 or 49 samples are transmitted during frame instead of 48). Signed-off-by: Tomasz Moń <[email protected]>
1 parent 2dee952 commit 07d9e52

File tree

1 file changed

+53
-28
lines changed

1 file changed

+53
-28
lines changed

drivers/i2s/i2s_nrfx.c

Lines changed: 53 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,11 @@ struct stream_cfg {
2020
nrfx_i2s_config_t nrfx_cfg;
2121
};
2222

23+
struct i2s_buf {
24+
void *mem_block;
25+
size_t size;
26+
};
27+
2328
struct i2s_nrfx_drv_data {
2429
struct onoff_manager *clk_mgr;
2530
struct onoff_client clk_cli;
@@ -189,9 +194,14 @@ static void find_suitable_clock(const struct i2s_nrfx_drv_cfg *drv_cfg,
189194
static bool get_next_tx_buffer(struct i2s_nrfx_drv_data *drv_data,
190195
nrfx_i2s_buffers_t *buffers)
191196
{
197+
struct i2s_buf buf;
192198
int ret = k_msgq_get(&drv_data->tx_queue,
193-
&buffers->p_tx_buffer,
199+
&buf,
194200
K_NO_WAIT);
201+
if (ret == 0) {
202+
buffers->p_tx_buffer = buf.mem_block;
203+
buffers->buffer_size = buf.size / sizeof(uint32_t);
204+
}
195205
return (ret == 0);
196206
}
197207

@@ -226,21 +236,22 @@ static void free_rx_buffer(struct i2s_nrfx_drv_data *drv_data, void *buffer)
226236
static bool supply_next_buffers(struct i2s_nrfx_drv_data *drv_data,
227237
nrfx_i2s_buffers_t *next)
228238
{
229-
uint32_t block_size = (drv_data->active_dir == I2S_DIR_TX)
230-
? drv_data->tx.cfg.block_size
231-
: drv_data->rx.cfg.block_size;
232-
233-
drv_data->last_tx_buffer = next->p_tx_buffer;
234-
235239
if (drv_data->active_dir != I2S_DIR_TX) { /* -> RX active */
236240
if (!get_next_rx_buffer(drv_data, next)) {
237241
drv_data->state = I2S_STATE_ERROR;
238242
nrfx_i2s_stop(drv_data->p_i2s);
239243
return false;
240244
}
245+
/* Set buffer size if there is no TX buffer (which effectively
246+
* controls how many bytes will be received).
247+
*/
248+
if (drv_data->active_dir == I2S_DIR_RX) {
249+
next->buffer_size =
250+
drv_data->rx.cfg.block_size / sizeof(uint32_t);
251+
}
241252
}
242253

243-
next->buffer_size = block_size / sizeof(uint32_t);
254+
drv_data->last_tx_buffer = next->p_tx_buffer;
244255

245256
LOG_DBG("Next buffers: %p/%p", next->p_tx_buffer, next->p_rx_buffer);
246257
nrfx_i2s_next_buffers_set(drv_data->p_i2s, next);
@@ -300,8 +311,12 @@ static void data_handler(const struct device *dev,
300311
if (drv_data->discard_rx) {
301312
free_rx_buffer(drv_data, released->p_rx_buffer);
302313
} else {
314+
struct i2s_buf buf = {
315+
.mem_block = released->p_rx_buffer,
316+
.size = released->buffer_size * sizeof(uint32_t)
317+
};
303318
int ret = k_msgq_put(&drv_data->rx_queue,
304-
&released->p_rx_buffer,
319+
&buf,
305320
K_NO_WAIT);
306321
if (ret < 0) {
307322
LOG_ERR("No room in RX queue");
@@ -351,6 +366,7 @@ static void data_handler(const struct device *dev,
351366
* before this buffer would be started again).
352367
*/
353368
next.p_tx_buffer = drv_data->last_tx_buffer;
369+
next.buffer_size = 1;
354370
} else if (get_next_tx_buffer(drv_data, &next)) {
355371
/* Next TX buffer successfully retrieved from
356372
* the queue, nothing more to do here.
@@ -367,6 +383,7 @@ static void data_handler(const struct device *dev,
367383
* will be stopped earlier.
368384
*/
369385
next.p_tx_buffer = drv_data->last_tx_buffer;
386+
next.buffer_size = 1;
370387
} else {
371388
/* Next TX buffer cannot be supplied now.
372389
* Defer it to when the user writes more data.
@@ -383,21 +400,21 @@ static void data_handler(const struct device *dev,
383400
static void purge_queue(const struct device *dev, enum i2s_dir dir)
384401
{
385402
struct i2s_nrfx_drv_data *drv_data = dev->data;
386-
void *mem_block;
403+
struct i2s_buf buf;
387404

388405
if (dir == I2S_DIR_TX || dir == I2S_DIR_BOTH) {
389406
while (k_msgq_get(&drv_data->tx_queue,
390-
&mem_block,
407+
&buf,
391408
K_NO_WAIT) == 0) {
392-
free_tx_buffer(drv_data, mem_block);
409+
free_tx_buffer(drv_data, buf.mem_block);
393410
}
394411
}
395412

396413
if (dir == I2S_DIR_RX || dir == I2S_DIR_BOTH) {
397414
while (k_msgq_get(&drv_data->rx_queue,
398-
&mem_block,
415+
&buf,
399416
K_NO_WAIT) == 0) {
400-
free_rx_buffer(drv_data, mem_block);
417+
free_rx_buffer(drv_data, buf.mem_block);
401418
}
402419
}
403420
}
@@ -560,6 +577,7 @@ static int i2s_nrfx_read(const struct device *dev,
560577
void **mem_block, size_t *size)
561578
{
562579
struct i2s_nrfx_drv_data *drv_data = dev->data;
580+
struct i2s_buf buf;
563581
int ret;
564582

565583
if (!drv_data->rx_configured) {
@@ -568,18 +586,19 @@ static int i2s_nrfx_read(const struct device *dev,
568586
}
569587

570588
ret = k_msgq_get(&drv_data->rx_queue,
571-
mem_block,
589+
&buf,
572590
(drv_data->state == I2S_STATE_ERROR)
573591
? K_NO_WAIT
574592
: SYS_TIMEOUT_MS(drv_data->rx.cfg.timeout));
575593
if (ret == -ENOMSG) {
576594
return -EIO;
577595
}
578596

579-
LOG_DBG("Released RX %p", *mem_block);
597+
LOG_DBG("Released RX %p", buf.mem_block);
580598

581599
if (ret == 0) {
582-
*size = drv_data->rx.cfg.block_size;
600+
*mem_block = buf.mem_block;
601+
*size = buf.size;
583602
}
584603

585604
return ret;
@@ -589,6 +608,7 @@ static int i2s_nrfx_write(const struct device *dev,
589608
void *mem_block, size_t size)
590609
{
591610
struct i2s_nrfx_drv_data *drv_data = dev->data;
611+
struct i2s_buf buf = { .mem_block = mem_block, .size = size };
592612
int ret;
593613

594614
if (!drv_data->tx_configured) {
@@ -602,14 +622,14 @@ static int i2s_nrfx_write(const struct device *dev,
602622
return -EIO;
603623
}
604624

605-
if (size != drv_data->tx.cfg.block_size) {
606-
LOG_ERR("This device can only write blocks of %u bytes",
625+
if (size > drv_data->tx.cfg.block_size || size < sizeof(uint32_t)) {
626+
LOG_ERR("This device can only write blocks up to %u bytes",
607627
drv_data->tx.cfg.block_size);
608628
return -EIO;
609629
}
610630

611631
ret = k_msgq_put(&drv_data->tx_queue,
612-
&mem_block,
632+
&buf,
613633
SYS_TIMEOUT_MS(drv_data->tx.cfg.timeout));
614634
if (ret < 0) {
615635
return ret;
@@ -662,12 +682,17 @@ static int start_transfer(struct i2s_nrfx_drv_data *drv_data)
662682
/* Failed to allocate next RX buffer */
663683
ret = -ENOMEM;
664684
} else {
665-
uint32_t block_size = (drv_data->active_dir == I2S_DIR_TX)
666-
? drv_data->tx.cfg.block_size
667-
: drv_data->rx.cfg.block_size;
668685
nrfx_err_t err;
669686

670-
initial_buffers.buffer_size = block_size / sizeof(uint32_t);
687+
/* It is necessary to set buffer size here only for I2S_DIR_RX,
688+
* because only then the get_next_tx_buffer() call in the if
689+
* condition above gets short-circuited.
690+
*/
691+
if (drv_data->active_dir == I2S_DIR_RX) {
692+
initial_buffers.buffer_size =
693+
drv_data->rx.cfg.block_size / sizeof(uint32_t);
694+
}
695+
671696
drv_data->last_tx_buffer = initial_buffers.p_tx_buffer;
672697

673698
err = nrfx_i2s_start(drv_data->p_i2s, &initial_buffers, 0);
@@ -904,8 +929,8 @@ static const struct i2s_driver_api i2s_nrf_drv_api = {
904929
#define I2S_CLK_SRC(idx) DT_STRING_TOKEN(I2S(idx), clock_source)
905930

906931
#define I2S_NRFX_DEVICE(idx) \
907-
static void *tx_msgs##idx[CONFIG_I2S_NRFX_TX_BLOCK_COUNT]; \
908-
static void *rx_msgs##idx[CONFIG_I2S_NRFX_RX_BLOCK_COUNT]; \
932+
static struct i2s_buf tx_msgs##idx[CONFIG_I2S_NRFX_TX_BLOCK_COUNT]; \
933+
static struct i2s_buf rx_msgs##idx[CONFIG_I2S_NRFX_RX_BLOCK_COUNT]; \
909934
static void data_handler##idx(nrfx_i2s_buffers_t const *p_released, \
910935
uint32_t status) \
911936
{ \
@@ -941,10 +966,10 @@ static const struct i2s_driver_api i2s_nrf_drv_api = {
941966
return err; \
942967
} \
943968
k_msgq_init(&i2s_nrfx_data##idx.tx_queue, \
944-
(char *)tx_msgs##idx, sizeof(void *), \
969+
(char *)tx_msgs##idx, sizeof(struct i2s_buf), \
945970
ARRAY_SIZE(tx_msgs##idx)); \
946971
k_msgq_init(&i2s_nrfx_data##idx.rx_queue, \
947-
(char *)rx_msgs##idx, sizeof(void *), \
972+
(char *)rx_msgs##idx, sizeof(struct i2s_buf), \
948973
ARRAY_SIZE(rx_msgs##idx)); \
949974
init_clock_manager(dev); \
950975
return 0; \

0 commit comments

Comments
 (0)