Skip to content

Commit

Permalink
i2s: support preload data
Browse files Browse the repository at this point in the history
Closes #8471
  • Loading branch information
L-KAYA committed Feb 22, 2023
1 parent 778aeae commit 7397b3f
Show file tree
Hide file tree
Showing 2 changed files with 81 additions and 3 deletions.
60 changes: 58 additions & 2 deletions components/driver/i2s/i2s_common.c
Original file line number Diff line number Diff line change
Expand Up @@ -285,6 +285,8 @@ static esp_err_t i2s_register_channel(i2s_controller_t *i2s_obj, i2s_dir_t dir,
new_chan->callbacks.on_recv_q_ovf = NULL;
new_chan->callbacks.on_sent = NULL;
new_chan->callbacks.on_send_q_ovf = NULL;
new_chan->dma.rw_pos = 0;
new_chan->dma.curr_ptr = NULL;
new_chan->start = NULL;
new_chan->stop = NULL;

Expand Down Expand Up @@ -1014,8 +1016,6 @@ esp_err_t i2s_channel_enable(i2s_chan_handle_t handle)
#if CONFIG_PM_ENABLE
esp_pm_lock_acquire(handle->pm_lock);
#endif
handle->dma.curr_ptr = NULL;
handle->dma.rw_pos = 0;
handle->start(handle);
handle->state = I2S_CHAN_STATE_RUNNING;
/* Reset queue */
Expand Down Expand Up @@ -1043,6 +1043,8 @@ esp_err_t i2s_channel_disable(i2s_chan_handle_t handle)
handle->state = I2S_CHAN_STATE_READY;
/* Waiting for reading/wrinting operation quit */
xSemaphoreTake(handle->binary, portMAX_DELAY);
handle->dma.curr_ptr = NULL;
handle->dma.rw_pos = 0;
handle->stop(handle);
#if CONFIG_PM_ENABLE
esp_pm_lock_release(handle->pm_lock);
Expand All @@ -1056,6 +1058,60 @@ esp_err_t i2s_channel_disable(i2s_chan_handle_t handle)
return ret;
}

esp_err_t i2s_channel_preload_writing_data(i2s_chan_handle_t handle, const void *src, size_t size, size_t *bytes_loaded)
{
I2S_NULL_POINTER_CHECK(TAG, handle);
ESP_RETURN_ON_FALSE(handle->dir == I2S_DIR_TX, ESP_ERR_INVALID_ARG, TAG, "this channel is not tx channel");
ESP_RETURN_ON_FALSE(handle->state == I2S_CHAN_STATE_READY, ESP_ERR_INVALID_STATE, TAG, "data can only be preloaded when the channel is READY");

uint8_t *data_ptr = (uint8_t *)src;
size_t remain_bytes = size;
size_t total_loaded_bytes = 0;

xSemaphoreTake(handle->mutex, portMAX_DELAY);

/* The pre-load data will be loaded from the first descriptor */
if (handle->dma.curr_ptr == NULL) {
handle->dma.curr_ptr = handle->dma.desc[0];
handle->dma.rw_pos = 0;
}
lldesc_t *desc_ptr = (lldesc_t *)handle->dma.curr_ptr;

/* Loop until no bytes in source buff remain or the descriptors are full */
while (remain_bytes) {
size_t bytes_can_load = remain_bytes > (handle->dma.buf_size - handle->dma.rw_pos) ?
(handle->dma.buf_size - handle->dma.rw_pos) : remain_bytes;
/* When all the descriptors has loaded data, no more bytes can be loaded, break directly */
if (bytes_can_load == 0) {
break;
}
/* Load the data from the last loaded position */
memcpy((uint8_t *)(desc_ptr->buf + handle->dma.rw_pos), data_ptr, bytes_can_load);
data_ptr += bytes_can_load; // Move forward the data pointer
total_loaded_bytes += bytes_can_load; // Add to the total loaded bytes
remain_bytes -= bytes_can_load; // Update the remaining bytes to be loaded
handle->dma.rw_pos += bytes_can_load; // Move forward the dma buffer position
/* When the current position reach the end of the dma buffer */
if (handle->dma.rw_pos == handle->dma.buf_size) {
/* If the next descriptor is not the first descriptor, keep load to the first descriptor
* otherwise all descriptor has been loaded, break directly, the dma buffer position
* will remain at the end of the last dma buffer */
if (desc_ptr->empty != (uint32_t)handle->dma.desc[0]) {
desc_ptr = (lldesc_t *)desc_ptr->empty;
handle->dma.curr_ptr = (void *)desc_ptr;
handle->dma.rw_pos = 0;
} else {
break;
}
}
}
*bytes_loaded = total_loaded_bytes;

xSemaphoreGive(handle->mutex);

return ESP_OK;
}

esp_err_t i2s_channel_write(i2s_chan_handle_t handle, const void *src, size_t size, size_t *bytes_written, uint32_t timeout_ms)
{
I2S_NULL_POINTER_CHECK(TAG, handle);
Expand Down
24 changes: 23 additions & 1 deletion components/driver/i2s/include/driver/i2s_common.h
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,7 @@ esp_err_t i2s_channel_enable(i2s_chan_handle_t handle);

/**
* @brief Disable the i2s channel
* @note Only allowed to be called when the channel state is READY / RUNNING, (i.e., channel has been initialized)
* @note Only allowed to be called when the channel state is RUNNING, (i.e., channel has been started)
* the channel will enter READY state once it is disabled successfully.
* @note Disable the channel can stop the I2S communication on hardware. It will stop bclk and ws signal but not mclk signal
*
Expand All @@ -154,6 +154,28 @@ esp_err_t i2s_channel_enable(i2s_chan_handle_t handle);
*/
esp_err_t i2s_channel_disable(i2s_chan_handle_t handle);

/**
* @brief Preload the data into TX DMA buffer
* @note Only allowed to be called when the channel state is READY, (i.e., channel has been initialized, but not started)
* @note As the initial DMA buffer has no data inside, it will transmit the empty buffer after enabled the channel,
* this function is used to preload the data into the DMA buffer, so that the valid data can be transmit immediately
* when the channel is enabled.
* @note This function can be called multiple times before enabling the channel, the buffer that loaded later will be concatenated
* behind the former loaded buffer. But when all the DMA buffers have been loaded, no more data can be preload then, please
* check the `bytes_loaded` parameter to see how many bytes are loaded successfully, when the `bytes_loaded` is smaller than
* the `size`, it means the DMA buffers are full.
*
* @param[in] handle I2S TX channel handler
* @param[in] src The pointer of the source buffer to be loaded
* @param[in] size The source buffer size
* @param[out] bytes_loaded The bytes that successfully been loaded into the TX DMA buffer
* @return
* - ESP_OK Load data successful
* - ESP_ERR_INVALID_ARG NULL pointer or not TX direction
* - ESP_ERR_INVALID_STATE This channel has not stated
*/
esp_err_t i2s_channel_preload_writing_data(i2s_chan_handle_t handle, const void *src, size_t size, size_t *bytes_loaded);

/**
* @brief I2S write data
* @note Only allowed to be called when the channel state is RUNNING, (i.e., tx channel has been started and is not writing now)
Expand Down

0 comments on commit 7397b3f

Please sign in to comment.