feat(parlio_tx): support loop transmission

This commit is contained in:
Chen Jichang
2025-03-18 16:23:34 +08:00
parent 6474fd67ae
commit 65074afc0d
17 changed files with 292 additions and 66 deletions

View File

@@ -157,6 +157,7 @@ typedef struct {
uint32_t idle_value; /*!< The value on the data line when the parallel IO is in idle state */
struct {
uint32_t queue_nonblocking : 1; /*!< If set, when the transaction queue is full, driver will not block the thread but return directly */
uint32_t loop_transmission : 1; /*!< If set, the transmission will be repeated continuously, until the tx_unit is disabled by `parlio_tx_unit_disable` */
} flags; /*!< Transmit specific config flags */
} parlio_transmit_config_t;

View File

@@ -4,6 +4,7 @@ entries:
if PARLIO_TX_ISR_HANDLER_IN_IRAM = y:
parlio_tx: parlio_tx_default_isr (noflash)
parlio_tx: parlio_tx_do_transaction (noflash)
parlio_tx: parlio_mount_buffer (noflash)
if PARLIO_RX_ISR_HANDLER_IN_IRAM = y:
parlio_rx: parlio_rx_default_eof_callback (noflash)
parlio_rx: parlio_rx_default_desc_done_callback (noflash)
@@ -15,6 +16,7 @@ archive: libesp_hw_support.a
entries:
if PARLIO_TX_ISR_HANDLER_IN_IRAM = y:
gdma_link: gdma_link_mount_buffers (noflash)
gdma_link: gdma_link_concat (noflash)
gdma_link: gdma_link_get_head_addr (noflash)
gdma: gdma_start (noflash)
if PARLIO_RX_ISR_HANDLER_IN_IRAM = y:

View File

@@ -61,6 +61,9 @@ typedef dma_descriptor_align8_t parlio_dma_desc_t;
#endif
#endif // defined(SOC_GDMA_TRIG_PERIPH_PARLIO0_BUS)
// loop transmission requires ping-pong link to prevent data tearing.
#define PARLIO_DMA_LINK_NUM 2
#if SOC_PERIPH_CLK_CTRL_SHARED
#define PARLIO_CLOCK_SRC_ATOMIC() PERIPH_RCC_ATOMIC()
#else

View File

@@ -46,6 +46,10 @@ typedef struct {
uint32_t idle_value; // Parallel IO bus idle value
const void *payload; // payload to be transmitted
size_t payload_bits; // payload size in bits
int dma_link_idx; // index of DMA link list
struct {
uint32_t loop_transmission : 1; // whether the transmission is in loop mode
} flags; // Extra configuration flags
} parlio_tx_trans_desc_t;
typedef struct parlio_tx_unit_t {
@@ -54,7 +58,7 @@ typedef struct parlio_tx_unit_t {
intr_handle_t intr; // allocated interrupt handle
esp_pm_lock_handle_t pm_lock; // power management lock
gdma_channel_handle_t dma_chan; // DMA channel
gdma_link_list_handle_t dma_link; // DMA link list handle
gdma_link_list_handle_t dma_link[PARLIO_DMA_LINK_NUM]; // DMA link list handle
size_t int_mem_align; // Alignment for internal memory
size_t ext_mem_align; // Alignment for external memory
#if CONFIG_PM_ENABLE
@@ -129,8 +133,10 @@ static esp_err_t parlio_destroy_tx_unit(parlio_tx_unit_t *tx_unit)
// de-register from group
parlio_unregister_unit_from_group(&tx_unit->base);
}
if (tx_unit->dma_link) {
ESP_RETURN_ON_ERROR(gdma_del_link_list(tx_unit->dma_link), TAG, "delete dma link list failed");
for (int i = 0; i < PARLIO_DMA_LINK_NUM; i++) {
if (tx_unit->dma_link[i]) {
ESP_RETURN_ON_ERROR(gdma_del_link_list(tx_unit->dma_link[i]), TAG, "delete dma link list failed");
}
}
free(tx_unit);
return ESP_OK;
@@ -204,8 +210,9 @@ static esp_err_t parlio_tx_unit_init_dma(parlio_tx_unit_t *tx_unit, const parlio
ESP_RETURN_ON_ERROR(PARLIO_GDMA_NEW_CHANNEL(&dma_chan_config, &tx_unit->dma_chan), TAG, "allocate TX DMA channel failed");
gdma_connect(tx_unit->dma_chan, GDMA_MAKE_TRIGGER(GDMA_TRIG_PERIPH_PARLIO, 0));
gdma_strategy_config_t gdma_strategy_conf = {
.auto_update_desc = true,
.owner_check = true,
.auto_update_desc = false, // for loop transmission, we have no chance to change the owner
.owner_check = false,
.eof_till_data_popped = true,
};
gdma_apply_strategy(tx_unit->dma_chan, &gdma_strategy_conf);
@@ -227,7 +234,9 @@ static esp_err_t parlio_tx_unit_init_dma(parlio_tx_unit_t *tx_unit, const parlio
};
// throw the error to the caller
ESP_RETURN_ON_ERROR(gdma_new_link_list(&dma_link_config, &tx_unit->dma_link), TAG, "create DMA link list failed");
for (int i = 0; i < PARLIO_DMA_LINK_NUM; i++) {
ESP_RETURN_ON_ERROR(gdma_new_link_list(&dma_link_config, &tx_unit->dma_link[i]), TAG, "create DMA link list failed");
}
return ESP_OK;
}
@@ -316,8 +325,6 @@ esp_err_t parlio_new_tx_unit(const parlio_tx_unit_config_t *config, parlio_tx_un
// data_width must not conflict with the valid signal
ESP_RETURN_ON_FALSE(!(config->valid_gpio_num >= 0 && data_width > PARLIO_LL_TX_DATA_LINE_AS_VALID_SIG),
ESP_ERR_INVALID_ARG, TAG, "valid signal conflicts with data signal");
ESP_RETURN_ON_FALSE(config->max_transfer_size && config->max_transfer_size <= PARLIO_LL_TX_MAX_BITS_PER_FRAME / 8,
ESP_ERR_INVALID_ARG, TAG, "invalid max transfer size");
#if SOC_PARLIO_TX_CLK_SUPPORT_GATING
// clock gating is controlled by either the MSB bit of data bus or the valid signal
ESP_RETURN_ON_FALSE(!(config->flags.clk_gate_en && config->valid_gpio_num < 0 && config->data_width <= PARLIO_LL_TX_DATA_LINE_AS_CLK_GATE),
@@ -386,10 +393,8 @@ esp_err_t parlio_new_tx_unit(const parlio_tx_unit_config_t *config, parlio_tx_un
// set sample clock edge
parlio_ll_tx_set_sample_clock_edge(hal->regs, config->sample_edge);
#if SOC_PARLIO_TX_SIZE_BY_DMA
// Always use DATA LEN EOF as the Parlio TX EOF
// In default, use DATA LEN EOF as the Parlio TX EOF
parlio_ll_tx_set_eof_condition(hal->regs, PARLIO_LL_TX_EOF_COND_DATA_LEN);
#endif // SOC_PARLIO_TX_SIZE_BY_DMA
// clear any pending interrupt
parlio_ll_clear_interrupt_status(hal->regs, PARLIO_LL_EVENT_TX_MASK);
@@ -461,10 +466,38 @@ esp_err_t parlio_tx_unit_register_event_callbacks(parlio_tx_unit_handle_t tx_uni
return ESP_OK;
}
static void parlio_mount_buffer(parlio_tx_unit_t *tx_unit, parlio_tx_trans_desc_t *t)
{
// DMA transfer data based on bytes not bits, so convert the bit length to bytes, round up
gdma_buffer_mount_config_t mount_config = {
.buffer = (void *)t->payload,
.length = (t->payload_bits + 7) / 8,
.flags = {
// if transmission is loop, we don't need to generate the EOF, as well as the final mark
.mark_eof = !t->flags.loop_transmission,
.mark_final = !t->flags.loop_transmission,
}
};
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);
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
gdma_link_concat(tx_unit->dma_link[t->dma_link_idx], -1, tx_unit->dma_link[next_link_idx], 0);
t->dma_link_idx = next_link_idx;
}
}
static void parlio_tx_do_transaction(parlio_tx_unit_t *tx_unit, parlio_tx_trans_desc_t *t)
{
parlio_hal_context_t *hal = &tx_unit->base.group->hal;
if (t->flags.loop_transmission) {
// Once a loop transmission is started, it cannot be stopped until it is disabled
parlio_ll_tx_set_eof_condition(hal->regs, PARLIO_LL_TX_EOF_COND_DMA_EOF);
}
tx_unit->cur_trans = t;
// If the external clock is a non-free-running clock, it needs to be switched to the internal free-running clock first.
@@ -478,21 +511,10 @@ static void parlio_tx_do_transaction(parlio_tx_unit_t *tx_unit, parlio_tx_trans_
PARLIO_RCC_ATOMIC() {
parlio_ll_tx_reset_clock(hal->regs);
}
// DMA transfer data based on bytes not bits, so convert the bit length to bytes, round up
gdma_buffer_mount_config_t mount_config = {
.buffer = (void *)t->payload,
.length = (t->payload_bits + 7) / 8,
.flags = {
.mark_eof = true,
.mark_final = true, // singly link list, mark final descriptor
}
};
// Since the threshold of the clock divider counter is not updated simultaneously with the clock source switching.
// The update of the threshold relies on the moment when the counter reaches the threshold each time.
// We place gdma_link_mount_buffers between reset clock and disable clock to ensure enough time for updating the threshold of the clock divider counter.
gdma_link_mount_buffers(tx_unit->dma_link, 0, &mount_config, 1, NULL);
// We place parlio_mount_buffer between reset clock and disable clock to ensure enough time for updating the threshold of the clock divider counter.
parlio_mount_buffer(tx_unit, t);
if (switch_clk) {
PARLIO_CLOCK_SRC_ATOMIC() {
parlio_ll_tx_set_clock_source(hal->regs, PARLIO_CLK_SRC_EXTERNAL);
@@ -506,7 +528,7 @@ static void parlio_tx_do_transaction(parlio_tx_unit_t *tx_unit, parlio_tx_trans_
parlio_ll_tx_set_idle_data_value(hal->regs, t->idle_value);
parlio_ll_tx_set_trans_bit_len(hal->regs, t->payload_bits);
gdma_start(tx_unit->dma_chan, gdma_link_get_head_addr(tx_unit->dma_link));
gdma_start(tx_unit->dma_chan, gdma_link_get_head_addr(tx_unit->dma_link[t->dma_link_idx]));
// wait until the data goes from the DMA to TX unit's FIFO
while (parlio_ll_tx_is_ready(hal->regs) == false);
// turn on the core clock after we start the TX unit
@@ -592,6 +614,10 @@ esp_err_t parlio_tx_unit_disable(parlio_tx_unit_handle_t tx_unit)
parlio_ll_tx_start(hal->regs, false);
parlio_ll_enable_interrupt(hal->regs, PARLIO_LL_EVENT_TX_MASK, false);
// Once a loop teansmission transaction is started, it can only be stopped in disable function
// change the EOF condition to be the data length, so the EOF will be triggered normally
parlio_ll_tx_set_eof_condition(hal->regs, PARLIO_LL_TX_EOF_COND_DATA_LEN);
// release power management lock
if (tx_unit->pm_lock) {
esp_pm_lock_release(tx_unit->pm_lock);
@@ -612,6 +638,21 @@ esp_err_t parlio_tx_unit_transmit(parlio_tx_unit_handle_t tx_unit, const void *p
ESP_RETURN_ON_FALSE((payload_bits % 8) == 0, ESP_ERR_INVALID_ARG, TAG, "payload bit length must be multiple of 8");
#endif // !SOC_PARLIO_TRANS_BIT_ALIGN
#if SOC_PARLIO_TX_SUPPORT_LOOP_TRANSMISSION
if (config->flags.loop_transmission) {
ESP_RETURN_ON_FALSE(parlio_ll_tx_support_dma_eof(NULL), ESP_ERR_NOT_SUPPORTED, TAG, "loop transmission is not supported by this chip revision");
}
#else
ESP_RETURN_ON_FALSE(config->flags.loop_transmission == false, ESP_ERR_NOT_SUPPORTED, TAG, "loop transmission is not supported on this chip");
#endif
// check the max payload size if it's not a loop transmission
// workaround for EOF limitation, when DMA EOF issue is fixed, we can remove this check
if (!config->flags.loop_transmission) {
ESP_RETURN_ON_FALSE(tx_unit->max_transfer_bits <= PARLIO_LL_TX_MAX_BITS_PER_FRAME,
ESP_ERR_INVALID_ARG, TAG, "invalid transfer size");
}
size_t cache_line_size = 0;
size_t alignment = 0;
uint8_t cache_type = 0;
@@ -627,39 +668,48 @@ esp_err_t parlio_tx_unit_transmit(parlio_tx_unit_handle_t tx_unit, const void *p
ESP_CACHE_MSYNC_FLAG_DIR_C2M | ESP_CACHE_MSYNC_FLAG_UNALIGNED), TAG, "cache sync failed");
}
TickType_t queue_wait_ticks = portMAX_DELAY;
if (config->flags.queue_nonblocking) {
queue_wait_ticks = 0;
}
parlio_tx_trans_desc_t *t = NULL;
// acquire one transaction description from ready queue or complete queue
if (xQueueReceive(tx_unit->trans_queues[PARLIO_TX_QUEUE_READY], &t, 0) != pdTRUE) {
if (xQueueReceive(tx_unit->trans_queues[PARLIO_TX_QUEUE_COMPLETE], &t, queue_wait_ticks) == pdTRUE) {
tx_unit->num_trans_inflight--;
// check if to start a new transaction or update the current loop transaction
bool no_trans_pending_in_queue = uxQueueMessagesWaiting(tx_unit->trans_queues[PARLIO_TX_QUEUE_PROGRESS]) == 0;
if (tx_unit->cur_trans && tx_unit->cur_trans->flags.loop_transmission && config->flags.loop_transmission && no_trans_pending_in_queue) {
tx_unit->cur_trans->payload = payload;
tx_unit->cur_trans->payload_bits = payload_bits;
parlio_mount_buffer(tx_unit, tx_unit->cur_trans);
} else {
TickType_t queue_wait_ticks = portMAX_DELAY;
if (config->flags.queue_nonblocking) {
queue_wait_ticks = 0;
}
}
ESP_RETURN_ON_FALSE(t, ESP_ERR_INVALID_STATE, TAG, "no free transaction descriptor, please consider increasing trans_queue_depth");
parlio_tx_trans_desc_t *t = NULL;
// acquire one transaction description from ready queue or complete queue
if (xQueueReceive(tx_unit->trans_queues[PARLIO_TX_QUEUE_READY], &t, 0) != pdTRUE) {
if (xQueueReceive(tx_unit->trans_queues[PARLIO_TX_QUEUE_COMPLETE], &t, queue_wait_ticks) == pdTRUE) {
tx_unit->num_trans_inflight--;
}
}
ESP_RETURN_ON_FALSE(t, ESP_ERR_INVALID_STATE, TAG, "no free transaction descriptor, please consider increasing trans_queue_depth");
// fill in the transaction descriptor
memset(t, 0, sizeof(parlio_tx_trans_desc_t));
t->payload = payload;
t->payload_bits = payload_bits;
t->idle_value = config->idle_value & tx_unit->idle_value_mask;
// fill in the transaction descriptor
memset(t, 0, sizeof(parlio_tx_trans_desc_t));
t->payload = payload;
t->payload_bits = payload_bits;
t->idle_value = config->idle_value & tx_unit->idle_value_mask;
t->flags.loop_transmission = config->flags.loop_transmission;
// send the transaction descriptor to progress queue
ESP_RETURN_ON_FALSE(xQueueSend(tx_unit->trans_queues[PARLIO_TX_QUEUE_PROGRESS], &t, 0) == pdTRUE,
ESP_ERR_INVALID_STATE, TAG, "failed to send transaction descriptor to progress queue");
tx_unit->num_trans_inflight++;
// send the transaction descriptor to progress queue
ESP_RETURN_ON_FALSE(xQueueSend(tx_unit->trans_queues[PARLIO_TX_QUEUE_PROGRESS], &t, 0) == pdTRUE,
ESP_ERR_INVALID_STATE, TAG, "failed to send transaction descriptor to progress queue");
tx_unit->num_trans_inflight++;
// check if we need to start one pending transaction
parlio_tx_fsm_t expected_fsm = PARLIO_TX_FSM_ENABLE;
if (atomic_compare_exchange_strong(&tx_unit->fsm, &expected_fsm, PARLIO_TX_FSM_RUN_WAIT)) {
// check if we need to start one transaction
if (xQueueReceive(tx_unit->trans_queues[PARLIO_TX_QUEUE_PROGRESS], &t, 0) == pdTRUE) {
atomic_store(&tx_unit->fsm, PARLIO_TX_FSM_RUN);
parlio_tx_do_transaction(tx_unit, t);
} else {
atomic_store(&tx_unit->fsm, PARLIO_TX_FSM_ENABLE);
// check if we need to start one pending transaction
parlio_tx_fsm_t expected_fsm = PARLIO_TX_FSM_ENABLE;
if (atomic_compare_exchange_strong(&tx_unit->fsm, &expected_fsm, PARLIO_TX_FSM_RUN_WAIT)) {
// check if we need to start one transaction
if (xQueueReceive(tx_unit->trans_queues[PARLIO_TX_QUEUE_PROGRESS], &t, 0) == pdTRUE) {
atomic_store(&tx_unit->fsm, PARLIO_TX_FSM_RUN);
parlio_tx_do_transaction(tx_unit, t);
} else {
atomic_store(&tx_unit->fsm, PARLIO_TX_FSM_ENABLE);
}
}
}

View File

@@ -291,7 +291,7 @@ TEST_CASE("parallel_tx_clock_gating", "[paralio_tx]")
#endif // SOC_PARLIO_TX_CLK_SUPPORT_GATING
#if SOC_PSRAM_DMA_CAPABLE
TEST_CASE("parlio can transmit PSRAM buffer", "[parlio_tx]")
TEST_CASE("parlio_tx_can_transmit_PSRAM_buffer", "[parlio_tx]")
{
printf("install parlio tx unit\r\n");
parlio_tx_unit_handle_t tx_unit = NULL;
@@ -447,3 +447,85 @@ TEST_CASE("parallel tx unit use external non-free running clock", "[parlio_tx]")
TEST_ESP_OK(gpio_reset_pin(config.data_gpio_nums[i]));
}
};
#if SOC_PARLIO_TX_SUPPORT_LOOP_TRANSMISSION
TEST_CASE("parlio_tx_loop_transmission", "[parlio_tx]")
{
printf("install parlio tx unit\r\n");
parlio_tx_unit_handle_t tx_unit = NULL;
parlio_tx_unit_config_t config = {
.clk_src = PARLIO_CLK_SRC_DEFAULT,
.data_width = 8,
.clk_in_gpio_num = -1, // use internal clock source
.valid_gpio_num = -1,
.clk_out_gpio_num = TEST_CLK_GPIO,
.data_gpio_nums = {
TEST_DATA0_GPIO,
TEST_DATA1_GPIO,
TEST_DATA2_GPIO,
TEST_DATA3_GPIO,
TEST_DATA4_GPIO,
TEST_DATA5_GPIO,
TEST_DATA6_GPIO,
TEST_DATA7_GPIO,
},
.output_clk_freq_hz = 10 * 1000 * 1000,
.trans_queue_depth = 3,
.max_transfer_size = 256,
.bit_pack_order = PARLIO_BIT_PACK_ORDER_LSB,
.sample_edge = PARLIO_SAMPLE_EDGE_POS,
};
TEST_ESP_OK(parlio_new_tx_unit(&config, &tx_unit));
TEST_ESP_OK(parlio_tx_unit_enable(tx_unit));
printf("send packets and check event is fired\r\n");
parlio_transmit_config_t transmit_config = {
.idle_value = 0x00,
.flags.loop_transmission = true,
};
__attribute__((aligned(64))) uint8_t payload_loop1[256] = {0};
__attribute__((aligned(64))) uint8_t payload_loop2[256] = {0};
__attribute__((aligned(64))) uint8_t payload_oneshot[256] = {0};
for (int i = 0; i < 256; i++) {
payload_loop1[i] = i;
payload_loop2[i] = 255 - i;
payload_oneshot[i] = i * 2 + 1;
}
if (parlio_ll_tx_support_dma_eof(NULL)) { // for some chips, only support in particular ECO version
transmit_config.flags.loop_transmission = true;
int lopp_count = 3;
while (lopp_count--) {
TEST_ESP_OK(parlio_tx_unit_transmit(tx_unit, payload_loop1, 256 * sizeof(uint8_t) * 8, &transmit_config));
vTaskDelay(pdMS_TO_TICKS(10));
// Should be sent after the previous frame has been completely sent
TEST_ESP_OK(parlio_tx_unit_transmit(tx_unit, payload_loop2, 256 * sizeof(uint8_t) * 8, &transmit_config));
vTaskDelay(pdMS_TO_TICKS(10));
}
transmit_config.flags.loop_transmission = false;
// should be pending in queue
TEST_ESP_OK(parlio_tx_unit_transmit(tx_unit, payload_oneshot, 256 * sizeof(uint8_t) * 8, &transmit_config));
transmit_config.flags.loop_transmission = true;
// there is a oneshot trans in queue, should also be pending in queue
TEST_ESP_OK(parlio_tx_unit_transmit(tx_unit, payload_loop1, 256 * sizeof(uint8_t) * 8, &transmit_config));
TEST_ESP_ERR(ESP_ERR_TIMEOUT, parlio_tx_unit_wait_all_done(tx_unit, 50));
// stop infinite loop transmission
parlio_tx_unit_disable(tx_unit);
// We should see 1 oneshot frame and 1 loop transmission (both pending in queue)
parlio_tx_unit_enable(tx_unit);
vTaskDelay(pdMS_TO_TICKS(10));
// stop the second infinite loop transmission
parlio_tx_unit_disable(tx_unit);
parlio_tx_unit_enable(tx_unit);
} else {
TEST_ESP_ERR(ESP_ERR_NOT_SUPPORTED, parlio_tx_unit_transmit(tx_unit, payload_loop1, 256 * sizeof(uint8_t) * 8, &transmit_config));
}
TEST_ESP_OK(parlio_tx_unit_wait_all_done(tx_unit, -1));
TEST_ESP_OK(parlio_tx_unit_disable(tx_unit));
TEST_ESP_OK(parlio_del_tx_unit(tx_unit));
}
#endif // SOC_PARLIO_TX_SUPPORT_LOOP_TRANSMISSION

View File

@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2023-2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@@ -474,12 +474,24 @@ static inline void parlio_ll_tx_set_trans_bit_len(parl_io_dev_t *dev, uint32_t b
dev->tx_data_cfg.tx_bitlen = bitlen;
}
/**
* @brief Check if tx size can be determined by DMA
*
* @param dev Parallel IO register base address (not used)
*/
static inline bool parlio_ll_tx_support_dma_eof(parl_io_dev_t *dev)
{
(void)dev;
return true;
}
/**
* @brief Set the condition to generate the TX EOF event
*
* @param dev Parallel IO register base address
* @param cond TX EOF condition
*/
__attribute__((always_inline))
static inline void parlio_ll_tx_set_eof_condition(parl_io_dev_t *dev, parlio_ll_tx_eof_cond_t cond)
{
dev->tx_genrl_cfg.tx_eof_gen_sel = cond;

View File

@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2023-2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@@ -44,6 +44,11 @@ typedef enum {
PARLIO_LL_RX_EOF_COND_EN_INACTIVE, /*!< RX unit generates EOF event when the external enable signal becomes inactive */
} parlio_ll_rx_eof_cond_t;
typedef enum {
PARLIO_LL_TX_EOF_COND_DATA_LEN, /*!< TX unit generates EOF event when it transmits particular data bit length that specified in `tx_bitlen`. */
PARLIO_LL_TX_EOF_COND_DMA_EOF, /*!< TX unit generates EOF event when the DMA EOF takes place */
} parlio_ll_tx_eof_cond_t;
/**
* @brief Enable or disable the parlio peripheral APB clock
*
@@ -450,6 +455,19 @@ static inline void parlio_ll_tx_set_trans_bit_len(parl_io_dev_t *dev, uint32_t b
HAL_FORCE_MODIFY_U32_REG_FIELD(dev->tx_cfg0, tx_bytelen, bitlen / 8);
}
/**
* @brief Set the condition to generate the TX EOF event (this chip does not support)
*
* @param dev Parallel IO register base address (not used)
* @param cond TX EOF condition (not used)
*/
__attribute__((always_inline))
static inline void parlio_ll_tx_set_eof_condition(parl_io_dev_t *dev, parlio_ll_tx_eof_cond_t cond)
{
(void) dev;
HAL_ASSERT(cond == PARLIO_LL_TX_EOF_COND_DATA_LEN);
}
/**
* @brief Whether to enable the TX clock gating
*

View File

@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2023-2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@@ -48,6 +48,11 @@ typedef enum {
PARLIO_LL_RX_EOF_COND_EN_INACTIVE, /*!< RX unit generates EOF event when the external enable signal becomes inactive */
} parlio_ll_rx_eof_cond_t;
typedef enum {
PARLIO_LL_TX_EOF_COND_DATA_LEN, /*!< TX unit generates EOF event when it transmits particular data bit length that specified in `tx_bitlen`. */
PARLIO_LL_TX_EOF_COND_DMA_EOF, /*!< TX unit generates EOF event when the DMA EOF takes place */
} parlio_ll_tx_eof_cond_t;
/**
* @brief Enable or disable the parlio peripheral APB clock
*
@@ -475,6 +480,29 @@ static inline void parlio_ll_tx_set_trans_bit_len(parl_io_dev_t *dev, uint32_t b
dev->tx_data_cfg.tx_bitlen = bitlen;
}
/**
* @brief Check if tx size can be determined by DMA
*
* @param dev Parallel IO register base address (not used)
*/
static inline bool parlio_ll_tx_support_dma_eof(parl_io_dev_t *dev)
{
(void)dev;
return ESP_CHIP_REV_ABOVE(efuse_hal_chip_revision(), 102);
}
/**
* @brief Set the condition to generate the TX EOF event
*
* @param dev Parallel IO register base address
* @param cond TX EOF condition
*/
__attribute__((always_inline))
static inline void parlio_ll_tx_set_eof_condition(parl_io_dev_t *dev, parlio_ll_tx_eof_cond_t cond)
{
dev->tx_genrl_cfg.tx_eof_gen_sel = cond;
}
/**
* @brief Whether to enable the TX clock gating
*

View File

@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2023-2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@@ -523,12 +523,24 @@ static inline void parlio_ll_tx_set_trans_bit_len(parl_io_dev_t *dev, uint32_t b
dev->tx_data_cfg.tx_bitlen = bitlen;
}
/**
* @brief Check if tx size can be determined by DMA
*
* @param dev Parallel IO register base address (not used)
*/
static inline bool parlio_ll_tx_support_dma_eof(parl_io_dev_t *dev)
{
(void)dev;
return true;
}
/**
* @brief Set the condition to generate the TX EOF event
*
* @param dev Parallel IO register base address
* @param cond TX EOF condition
*/
__attribute__((always_inline))
static inline void parlio_ll_tx_set_eof_condition(parl_io_dev_t *dev, parlio_ll_tx_eof_cond_t cond)
{
dev->tx_genrl_cfg.tx_eof_gen_sel = cond;

View File

@@ -987,7 +987,7 @@ config SOC_PARLIO_TRANS_BIT_ALIGN
bool
default y
config SOC_PARLIO_TX_SIZE_BY_DMA
config SOC_PARLIO_TX_SUPPORT_LOOP_TRANSMISSION
bool
default y

View File

@@ -385,7 +385,7 @@
#define SOC_PARLIO_RX_CLK_SUPPORT_GATING 1 /*!< Support gating RX clock */
#define SOC_PARLIO_RX_CLK_SUPPORT_OUTPUT 1 /*!< Support output RX clock to a GPIO */
#define SOC_PARLIO_TRANS_BIT_ALIGN 1 /*!< Support bit alignment in transaction */
#define SOC_PARLIO_TX_SIZE_BY_DMA 1 /*!< Transaction length is controlled by DMA instead of indicated by register */
#define SOC_PARLIO_TX_SUPPORT_LOOP_TRANSMISSION 1 /*!< Support loop transmission */
#define SOC_PARLIO_SUPPORT_SLEEP_RETENTION 1 /*!< Support back up registers before sleep */
#define SOC_PARLIO_SUPPORT_SPI_LCD 1 /*!< Support to drive SPI interfaced LCD */

View File

@@ -947,6 +947,10 @@ config SOC_PARLIO_TRANS_BIT_ALIGN
bool
default y
config SOC_PARLIO_TX_SUPPORT_LOOP_TRANSMISSION
bool
default y
config SOC_PARLIO_SUPPORT_SLEEP_RETENTION
bool
default y

View File

@@ -374,6 +374,7 @@
#define SOC_PARLIO_RX_CLK_SUPPORT_GATING 1 /*!< Support gating RX clock */
#define SOC_PARLIO_RX_CLK_SUPPORT_OUTPUT 1 /*!< Support output RX clock to a GPIO */
#define SOC_PARLIO_TRANS_BIT_ALIGN 1 /*!< Support bit alignment in transaction */
#define SOC_PARLIO_TX_SUPPORT_LOOP_TRANSMISSION 1 /*!< Support loop transmission. Only avliable in chip version above 1.2 */
#define SOC_PARLIO_SUPPORT_SLEEP_RETENTION 1 /*!< Support back up registers before sleep */
#define SOC_PARLIO_SUPPORT_SPI_LCD 1 /*!< Support to drive SPI interfaced LCD */

View File

@@ -1,5 +1,5 @@
/**
* SPDX-FileCopyrightText: 2022-2024 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2022-2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@@ -186,6 +186,14 @@ extern "C" {
* Parallel TX general configuration register.
*/
#define PARL_IO_TX_GENRL_CFG_REG (DR_REG_PARL_IO_BASE + 0x18)
/** PARL_IO_TX_EOF_GEN_SEL : R/W; bitpos: [13]; default: 0;
* Configures the tx eof generated mechanism. 1'b0: eof generated by data bit length.
* 1'b1: eof generated by DMA eof.
*/
#define PARL_IO_TX_EOF_GEN_SEL (BIT(13))
#define PARL_IO_TX_EOF_GEN_SEL_M (PARL_IO_TX_EOF_GEN_SEL_V << PARL_IO_TX_EOF_GEN_SEL_S)
#define PARL_IO_TX_EOF_GEN_SEL_V 0x00000001U
#define PARL_IO_TX_EOF_GEN_SEL_S 13
/** PARL_IO_TX_IDLE_VALUE : R/W; bitpos: [29:14]; default: 0;
* Configures bus value of transmitter in IDLE state.
*/

View File

@@ -1,5 +1,5 @@
/**
* SPDX-FileCopyrightText: 2022-2024 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2022-2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@@ -179,7 +179,12 @@ typedef union {
*/
typedef union {
struct {
uint32_t reserved_0:14;
uint32_t reserved_0:13;
/** tx_eof_gen_sel : R/W; bitpos: [13]; default: 0;
* Configures the tx eof generated mechanism. 1'b0: eof generated by data bit length.
* 1'b1: eof generated by DMA eof.
*/
uint32_t tx_eof_gen_sel:1;
/** tx_idle_value : R/W; bitpos: [29:14]; default: 0;
* Configures bus value of transmitter in IDLE state.
*/

View File

@@ -1371,7 +1371,7 @@ config SOC_PARLIO_TRANS_BIT_ALIGN
bool
default y
config SOC_PARLIO_TX_SIZE_BY_DMA
config SOC_PARLIO_TX_SUPPORT_LOOP_TRANSMISSION
bool
default y

View File

@@ -488,7 +488,7 @@
#define SOC_PARLIO_RX_CLK_SUPPORT_GATING 1 /*!< Support gating RX clock */
#define SOC_PARLIO_RX_CLK_SUPPORT_OUTPUT 1 /*!< Support output RX clock to a GPIO */
#define SOC_PARLIO_TRANS_BIT_ALIGN 1 /*!< Support bit alignment in transaction */
#define SOC_PARLIO_TX_SIZE_BY_DMA 1 /*!< Transaction length is controlled by DMA instead of indicated by register */
#define SOC_PARLIO_TX_SUPPORT_LOOP_TRANSMISSION 1 /*!< Support loop transmission */
#define SOC_PARLIO_SUPPORT_SLEEP_RETENTION 1 /*!< Support back up registers before sleep */
#define SOC_PARLIO_SUPPORT_SPI_LCD 1 /*!< Support to drive SPI interfaced LCD */
#define SOC_PARLIO_SUPPORT_I80_LCD 1 /*!< Support to drive I80 interfaced LCD */