mirror of
https://github.com/espressif/esp-idf.git
synced 2026-10-02 03:00:34 +03:00
feat(parlio_tx): apply the gdma link switch event to parlio_tx
This commit is contained in:
@@ -7,7 +7,7 @@ entries:
|
|||||||
parlio_tx: parlio_mount_buffer (noflash)
|
parlio_tx: parlio_mount_buffer (noflash)
|
||||||
|
|
||||||
if SOC_PARLIO_TX_SUPPORT_LOOP_TRANSMISSION = y:
|
if SOC_PARLIO_TX_SUPPORT_LOOP_TRANSMISSION = y:
|
||||||
parlio_tx: parlio_tx_gdma_eof_callback (noflash)
|
parlio_tx: parlio_tx_gdma_buffer_switched_callback (noflash)
|
||||||
|
|
||||||
if PARLIO_RX_ISR_HANDLER_IN_IRAM = y:
|
if PARLIO_RX_ISR_HANDLER_IN_IRAM = y:
|
||||||
parlio_rx: parlio_rx_default_eof_callback (noflash)
|
parlio_rx: parlio_rx_default_eof_callback (noflash)
|
||||||
|
|||||||
@@ -50,6 +50,13 @@
|
|||||||
#include "esp_private/gdma_link.h"
|
#include "esp_private/gdma_link.h"
|
||||||
#include "esp_private/esp_dma_utils.h"
|
#include "esp_private/esp_dma_utils.h"
|
||||||
|
|
||||||
|
#if defined(SOC_GDMA_TRIG_PERIPH_PARLIO0_BUS) && defined(SOC_GDMA_BUS_AXI) && (SOC_GDMA_TRIG_PERIPH_PARLIO0_BUS == SOC_GDMA_BUS_AXI)
|
||||||
|
#include "hal/axi_dma_ll.h"
|
||||||
|
#if AXI_DMA_LL_SUPPORT(TX_LINK_SWITCH)
|
||||||
|
#define PARLIO_USE_GDMA_LINK_SWITCH_EVENT 1
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
#if CONFIG_PARLIO_OBJ_CACHE_SAFE
|
#if CONFIG_PARLIO_OBJ_CACHE_SAFE
|
||||||
#define PARLIO_MEM_ALLOC_CAPS (MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT)
|
#define PARLIO_MEM_ALLOC_CAPS (MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT)
|
||||||
#else
|
#else
|
||||||
|
|||||||
@@ -10,8 +10,14 @@
|
|||||||
#include "driver/parlio_tx.h"
|
#include "driver/parlio_tx.h"
|
||||||
#include "parlio_priv.h"
|
#include "parlio_priv.h"
|
||||||
|
|
||||||
|
#define PARLIO_ALIGN_DOWN(num, align) ((num) & ~((align) - 1))
|
||||||
|
#define PARLIO_DMA_BUFFER_MAX_SIZE(alignment) \
|
||||||
|
((alignment) > 1 ? PARLIO_ALIGN_DOWN(PARLIO_DMA_DESCRIPTOR_BUFFER_MAX_SIZE, (alignment)) : PARLIO_DMA_DESCRIPTOR_BUFFER_MAX_SIZE)
|
||||||
|
#define PARLIO_DMA_BUFFER_SPLIT_SIZE(size, alignment) \
|
||||||
|
((alignment) > 1 ? PARLIO_ALIGN_DOWN((size) / 2, (alignment)) : (size) / 2)
|
||||||
|
|
||||||
#if SOC_PARLIO_TX_SUPPORT_LOOP_TRANSMISSION
|
#if SOC_PARLIO_TX_SUPPORT_LOOP_TRANSMISSION
|
||||||
static bool parlio_tx_gdma_eof_callback(gdma_channel_handle_t dma_chan, gdma_event_data_t *event_data, void *user_data);
|
static bool parlio_tx_gdma_buffer_switched_callback(gdma_channel_handle_t dma_chan, gdma_event_data_t *event_data, void *user_data);
|
||||||
#endif
|
#endif
|
||||||
static void parlio_tx_default_isr(void *args);
|
static void parlio_tx_default_isr(void *args);
|
||||||
|
|
||||||
@@ -162,6 +168,8 @@ static esp_err_t parlio_tx_unit_init_dma(parlio_tx_unit_t *tx_unit, const parlio
|
|||||||
// create DMA link list
|
// create DMA link list
|
||||||
size_t buffer_alignment = MAX(tx_unit->int_mem_align, tx_unit->ext_mem_align);
|
size_t buffer_alignment = MAX(tx_unit->int_mem_align, tx_unit->ext_mem_align);
|
||||||
size_t num_dma_nodes = esp_dma_calculate_node_count(config->max_transfer_size, buffer_alignment, PARLIO_DMA_DESCRIPTOR_BUFFER_MAX_SIZE);
|
size_t num_dma_nodes = esp_dma_calculate_node_count(config->max_transfer_size, buffer_alignment, PARLIO_DMA_DESCRIPTOR_BUFFER_MAX_SIZE);
|
||||||
|
// link switch event requires at least 2 nodes
|
||||||
|
num_dma_nodes = MAX(num_dma_nodes, 2);
|
||||||
gdma_link_list_config_t dma_link_config = {
|
gdma_link_list_config_t dma_link_config = {
|
||||||
.item_alignment = PARLIO_DMA_DESC_ALIGNMENT,
|
.item_alignment = PARLIO_DMA_DESC_ALIGNMENT,
|
||||||
.num_items = num_dma_nodes,
|
.num_items = num_dma_nodes,
|
||||||
@@ -430,16 +438,21 @@ esp_err_t parlio_tx_unit_register_event_callbacks(parlio_tx_unit_handle_t tx_uni
|
|||||||
|
|
||||||
if (cbs->on_buffer_switched) {
|
if (cbs->on_buffer_switched) {
|
||||||
#if SOC_PARLIO_TX_SUPPORT_LOOP_TRANSMISSION
|
#if SOC_PARLIO_TX_SUPPORT_LOOP_TRANSMISSION
|
||||||
|
#if !PARLIO_USE_GDMA_LINK_SWITCH_EVENT
|
||||||
// workaround for DIG-559
|
// workaround for DIG-559
|
||||||
ESP_RETURN_ON_FALSE(tx_unit->data_width > 1, ESP_ERR_NOT_SUPPORTED, TAG, "on_buffer_switched callback is not supported for 1-bit data width");
|
ESP_RETURN_ON_FALSE(tx_unit->data_width > 1, ESP_ERR_NOT_SUPPORTED, TAG, "on_buffer_switched callback is not supported for 1-bit data width");
|
||||||
|
|
||||||
gdma_tx_event_callbacks_t gdma_cbs = {
|
gdma_tx_event_callbacks_t gdma_cbs = {
|
||||||
.on_trans_eof = parlio_tx_gdma_eof_callback,
|
.on_trans_eof = parlio_tx_gdma_buffer_switched_callback,
|
||||||
};
|
};
|
||||||
|
#else // !PARLIO_USE_GDMA_LINK_SWITCH_EVENT
|
||||||
|
gdma_tx_event_callbacks_t gdma_cbs = {
|
||||||
|
.on_link_switch = parlio_tx_gdma_buffer_switched_callback,
|
||||||
|
};
|
||||||
|
#endif // !PARLIO_USE_GDMA_LINK_SWITCH_EVENT
|
||||||
ESP_RETURN_ON_ERROR(gdma_register_tx_event_callbacks(tx_unit->dma_chan, &gdma_cbs, tx_unit), TAG, "install DMA callback failed");
|
ESP_RETURN_ON_ERROR(gdma_register_tx_event_callbacks(tx_unit->dma_chan, &gdma_cbs, tx_unit), TAG, "install DMA callback failed");
|
||||||
#else
|
#else // SOC_PARLIO_TX_SUPPORT_LOOP_TRANSMISSION
|
||||||
ESP_RETURN_ON_FALSE(false, ESP_ERR_NOT_SUPPORTED, TAG, "on_buffer_switched callback is not supported");
|
ESP_RETURN_ON_FALSE(false, ESP_ERR_NOT_SUPPORTED, TAG, "on_buffer_switched callback is not supported");
|
||||||
#endif
|
#endif // SOC_PARLIO_TX_SUPPORT_LOOP_TRANSMISSION
|
||||||
}
|
}
|
||||||
|
|
||||||
tx_unit->on_trans_done = cbs->on_trans_done;
|
tx_unit->on_trans_done = cbs->on_trans_done;
|
||||||
@@ -452,10 +465,11 @@ static void parlio_mount_buffer(parlio_tx_unit_t *tx_unit, parlio_tx_trans_desc_
|
|||||||
{
|
{
|
||||||
size_t buffer_alignment = esp_ptr_internal(t->payload) ? tx_unit->int_mem_align : tx_unit->ext_mem_align;
|
size_t buffer_alignment = esp_ptr_internal(t->payload) ? tx_unit->int_mem_align : tx_unit->ext_mem_align;
|
||||||
// DMA transfer data based on bytes not bits, so convert the bit length to bytes, round up
|
// DMA transfer data based on bytes not bits, so convert the bit length to bytes, round up
|
||||||
|
size_t payload_bytes = (t->payload_bits + 7) / 8;
|
||||||
gdma_buffer_mount_config_t mount_config = {
|
gdma_buffer_mount_config_t mount_config = {
|
||||||
.buffer = (void *)t->payload,
|
.buffer = (void *)t->payload,
|
||||||
.buffer_alignment = buffer_alignment,
|
.buffer_alignment = buffer_alignment,
|
||||||
.length = (t->payload_bits + 7) / 8,
|
.length = payload_bytes,
|
||||||
.flags = {
|
.flags = {
|
||||||
// if transmission is loop, we don't need to generate the EOF for 1-bit data width, DIG-559
|
// if transmission is loop, we don't need to generate the EOF for 1-bit data width, DIG-559
|
||||||
.mark_eof = tx_unit->data_width == 1 ? !t->flags.loop_transmission : true,
|
.mark_eof = tx_unit->data_width == 1 ? !t->flags.loop_transmission : true,
|
||||||
@@ -464,7 +478,30 @@ static void parlio_mount_buffer(parlio_tx_unit_t *tx_unit, parlio_tx_trans_desc_
|
|||||||
};
|
};
|
||||||
|
|
||||||
int next_link_idx = t->flags.loop_transmission ? 1 - t->dma_link_idx : t->dma_link_idx;
|
int next_link_idx = t->flags.loop_transmission ? 1 - t->dma_link_idx : t->dma_link_idx;
|
||||||
gdma_link_mount_buffers(tx_unit->dma_link[next_link_idx], 0, &mount_config, 1, NULL);
|
size_t max_mount_size = PARLIO_DMA_BUFFER_MAX_SIZE(buffer_alignment);
|
||||||
|
if (t->flags.loop_transmission && payload_bytes <= max_mount_size) {
|
||||||
|
// split the payload into two DMA descriptors to trigger the buffer switch event
|
||||||
|
size_t split_bytes = PARLIO_DMA_BUFFER_SPLIT_SIZE(payload_bytes, buffer_alignment);
|
||||||
|
gdma_buffer_mount_config_t mount_configs[2] = {
|
||||||
|
{
|
||||||
|
.buffer = (void *)t->payload,
|
||||||
|
.buffer_alignment = buffer_alignment,
|
||||||
|
.length = split_bytes,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.buffer = (void *)((uint8_t *)t->payload + split_bytes),
|
||||||
|
.buffer_alignment = buffer_alignment,
|
||||||
|
.length = payload_bytes - split_bytes,
|
||||||
|
.flags = {
|
||||||
|
.mark_eof = tx_unit->data_width == 1 ? false : true,
|
||||||
|
.mark_final = GDMA_FINAL_LINK_TO_START,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
gdma_link_mount_buffers(tx_unit->dma_link[next_link_idx], 0, mount_configs, 2, NULL);
|
||||||
|
} else {
|
||||||
|
gdma_link_mount_buffers(tx_unit->dma_link[next_link_idx], 0, &mount_config, 1, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
if (t->flags.loop_transmission) {
|
if (t->flags.loop_transmission) {
|
||||||
// concatenate the DMA linked list of the next frame transmission with the DMA linked list of the current frame to realize the reuse of the current transmission transaction
|
// concatenate the DMA linked list of the next frame transmission with the DMA linked list of the current frame to realize the reuse of the current transmission transaction
|
||||||
@@ -668,10 +705,17 @@ esp_err_t parlio_tx_unit_transmit(parlio_tx_unit_handle_t tx_unit, const void *p
|
|||||||
// check alignment
|
// check alignment
|
||||||
ESP_RETURN_ON_FALSE(((uint32_t)payload & (alignment - 1)) == 0, ESP_ERR_INVALID_ARG, TAG, "payload address %p not aligned to %d", payload, alignment);
|
ESP_RETURN_ON_FALSE(((uint32_t)payload & (alignment - 1)) == 0, ESP_ERR_INVALID_ARG, TAG, "payload address %p not aligned to %d", payload, alignment);
|
||||||
ESP_RETURN_ON_FALSE((payload_bits & (alignment - 1)) == 0, ESP_ERR_INVALID_ARG, TAG, "payload size %d not aligned to %d", payload_bits, alignment);
|
ESP_RETURN_ON_FALSE((payload_bits & (alignment - 1)) == 0, ESP_ERR_INVALID_ARG, TAG, "payload size %d not aligned to %d", payload_bits, alignment);
|
||||||
|
size_t payload_bytes = (payload_bits + 7) / 8;
|
||||||
|
|
||||||
|
// check if the payload size is too small to split into two DMA descriptors
|
||||||
|
if (config->flags.loop_transmission && payload_bytes <= PARLIO_DMA_BUFFER_MAX_SIZE(alignment)) {
|
||||||
|
size_t split_bytes = PARLIO_DMA_BUFFER_SPLIT_SIZE(payload_bytes, alignment);
|
||||||
|
ESP_RETURN_ON_FALSE(split_bytes > 0 && split_bytes < payload_bytes, ESP_ERR_INVALID_ARG, TAG, "loop payload too small");
|
||||||
|
}
|
||||||
|
|
||||||
if (esp_cache_get_line_size_by_addr(payload) > 0) {
|
if (esp_cache_get_line_size_by_addr(payload) > 0) {
|
||||||
// Write back to cache to synchronize the cache before DMA start
|
// Write back to cache to synchronize the cache before DMA start
|
||||||
ESP_RETURN_ON_ERROR(esp_cache_msync((void *)payload, (payload_bits + 7) / 8,
|
ESP_RETURN_ON_ERROR(esp_cache_msync((void *)payload, payload_bytes,
|
||||||
ESP_CACHE_MSYNC_FLAG_DIR_C2M | ESP_CACHE_MSYNC_FLAG_UNALIGNED), TAG, "cache sync failed");
|
ESP_CACHE_MSYNC_FLAG_DIR_C2M | ESP_CACHE_MSYNC_FLAG_UNALIGNED), TAG, "cache sync failed");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -682,6 +726,11 @@ esp_err_t parlio_tx_unit_transmit(parlio_tx_unit_handle_t tx_unit, const void *p
|
|||||||
tx_unit->cur_trans->payload_bits = payload_bits;
|
tx_unit->cur_trans->payload_bits = payload_bits;
|
||||||
parlio_mount_buffer(tx_unit, tx_unit->cur_trans);
|
parlio_mount_buffer(tx_unit, tx_unit->cur_trans);
|
||||||
atomic_store(&tx_unit->buffer_need_switch, true);
|
atomic_store(&tx_unit->buffer_need_switch, true);
|
||||||
|
#if PARLIO_USE_GDMA_LINK_SWITCH_EVENT
|
||||||
|
if (tx_unit->on_buffer_switched) {
|
||||||
|
ESP_RETURN_ON_ERROR(gdma_request_link_switch_event(tx_unit->dma_chan), TAG, "request link switch event failed");
|
||||||
|
}
|
||||||
|
#endif
|
||||||
} else {
|
} else {
|
||||||
TickType_t queue_wait_ticks = portMAX_DELAY;
|
TickType_t queue_wait_ticks = portMAX_DELAY;
|
||||||
if (config->flags.queue_nonblocking) {
|
if (config->flags.queue_nonblocking) {
|
||||||
@@ -731,7 +780,7 @@ esp_err_t parlio_tx_unit_transmit(parlio_tx_unit_handle_t tx_unit, const void *p
|
|||||||
}
|
}
|
||||||
|
|
||||||
#if SOC_PARLIO_TX_SUPPORT_LOOP_TRANSMISSION
|
#if SOC_PARLIO_TX_SUPPORT_LOOP_TRANSMISSION
|
||||||
static bool parlio_tx_gdma_eof_callback(gdma_channel_handle_t dma_chan, gdma_event_data_t *event_data, void *user_data)
|
static bool parlio_tx_gdma_buffer_switched_callback(gdma_channel_handle_t dma_chan, gdma_event_data_t *event_data, void *user_data)
|
||||||
{
|
{
|
||||||
parlio_tx_unit_t *tx_unit = (parlio_tx_unit_t *) user_data;
|
parlio_tx_unit_t *tx_unit = (parlio_tx_unit_t *) user_data;
|
||||||
bool need_yield = false;
|
bool need_yield = false;
|
||||||
|
|||||||
Reference in New Issue
Block a user