diff --git a/components/esp_driver_parlio/include/driver/parlio_tx.h b/components/esp_driver_parlio/include/driver/parlio_tx.h index 24e8359ffd..fa7ea93cdd 100644 --- a/components/esp_driver_parlio/include/driver/parlio_tx.h +++ b/components/esp_driver_parlio/include/driver/parlio_tx.h @@ -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; diff --git a/components/esp_driver_parlio/linker.lf b/components/esp_driver_parlio/linker.lf index ca745a78b9..9765812e26 100644 --- a/components/esp_driver_parlio/linker.lf +++ b/components/esp_driver_parlio/linker.lf @@ -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: diff --git a/components/esp_driver_parlio/src/parlio_priv.h b/components/esp_driver_parlio/src/parlio_priv.h index 0cc9254f50..1c31cf4add 100644 --- a/components/esp_driver_parlio/src/parlio_priv.h +++ b/components/esp_driver_parlio/src/parlio_priv.h @@ -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 diff --git a/components/esp_driver_parlio/src/parlio_tx.c b/components/esp_driver_parlio/src/parlio_tx.c index cade5e9b91..56d3434600 100644 --- a/components/esp_driver_parlio/src/parlio_tx.c +++ b/components/esp_driver_parlio/src/parlio_tx.c @@ -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); + } } } diff --git a/components/esp_driver_parlio/test_apps/parlio/main/test_parlio_tx.c b/components/esp_driver_parlio/test_apps/parlio/main/test_parlio_tx.c index ba22f8e072..cacd77e9dd 100644 --- a/components/esp_driver_parlio/test_apps/parlio/main/test_parlio_tx.c +++ b/components/esp_driver_parlio/test_apps/parlio/main/test_parlio_tx.c @@ -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 diff --git a/components/hal/esp32c5/include/hal/parlio_ll.h b/components/hal/esp32c5/include/hal/parlio_ll.h index 5d4032ad28..c800e952ee 100644 --- a/components/hal/esp32c5/include/hal/parlio_ll.h +++ b/components/hal/esp32c5/include/hal/parlio_ll.h @@ -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; diff --git a/components/hal/esp32c6/include/hal/parlio_ll.h b/components/hal/esp32c6/include/hal/parlio_ll.h index 1679dc35c2..66a0f5e5b0 100644 --- a/components/hal/esp32c6/include/hal/parlio_ll.h +++ b/components/hal/esp32c6/include/hal/parlio_ll.h @@ -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 * diff --git a/components/hal/esp32h2/include/hal/parlio_ll.h b/components/hal/esp32h2/include/hal/parlio_ll.h index e41a13a3e0..42172b4909 100644 --- a/components/hal/esp32h2/include/hal/parlio_ll.h +++ b/components/hal/esp32h2/include/hal/parlio_ll.h @@ -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 * diff --git a/components/hal/esp32p4/include/hal/parlio_ll.h b/components/hal/esp32p4/include/hal/parlio_ll.h index eca2b83689..016ca79b36 100644 --- a/components/hal/esp32p4/include/hal/parlio_ll.h +++ b/components/hal/esp32p4/include/hal/parlio_ll.h @@ -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; diff --git a/components/soc/esp32c5/include/soc/Kconfig.soc_caps.in b/components/soc/esp32c5/include/soc/Kconfig.soc_caps.in index 19328f81bf..48a50d29a6 100644 --- a/components/soc/esp32c5/include/soc/Kconfig.soc_caps.in +++ b/components/soc/esp32c5/include/soc/Kconfig.soc_caps.in @@ -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 diff --git a/components/soc/esp32c5/include/soc/soc_caps.h b/components/soc/esp32c5/include/soc/soc_caps.h index 802d640889..b6335da3de 100644 --- a/components/soc/esp32c5/include/soc/soc_caps.h +++ b/components/soc/esp32c5/include/soc/soc_caps.h @@ -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 */ diff --git a/components/soc/esp32h2/include/soc/Kconfig.soc_caps.in b/components/soc/esp32h2/include/soc/Kconfig.soc_caps.in index 5f00c2bf5a..4140785837 100644 --- a/components/soc/esp32h2/include/soc/Kconfig.soc_caps.in +++ b/components/soc/esp32h2/include/soc/Kconfig.soc_caps.in @@ -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 diff --git a/components/soc/esp32h2/include/soc/soc_caps.h b/components/soc/esp32h2/include/soc/soc_caps.h index af1c0d05d6..8be21f7226 100644 --- a/components/soc/esp32h2/include/soc/soc_caps.h +++ b/components/soc/esp32h2/include/soc/soc_caps.h @@ -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 */ diff --git a/components/soc/esp32h2/register/soc/parl_io_reg.h b/components/soc/esp32h2/register/soc/parl_io_reg.h index 05b4ec3fe3..1a8a711633 100644 --- a/components/soc/esp32h2/register/soc/parl_io_reg.h +++ b/components/soc/esp32h2/register/soc/parl_io_reg.h @@ -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. */ diff --git a/components/soc/esp32h2/register/soc/parl_io_struct.h b/components/soc/esp32h2/register/soc/parl_io_struct.h index d74bef0e25..c31095674d 100644 --- a/components/soc/esp32h2/register/soc/parl_io_struct.h +++ b/components/soc/esp32h2/register/soc/parl_io_struct.h @@ -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. */ diff --git a/components/soc/esp32p4/include/soc/Kconfig.soc_caps.in b/components/soc/esp32p4/include/soc/Kconfig.soc_caps.in index 0c7f21c693..388b455194 100644 --- a/components/soc/esp32p4/include/soc/Kconfig.soc_caps.in +++ b/components/soc/esp32p4/include/soc/Kconfig.soc_caps.in @@ -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 diff --git a/components/soc/esp32p4/include/soc/soc_caps.h b/components/soc/esp32p4/include/soc/soc_caps.h index 0262fd55ac..21752e65ae 100644 --- a/components/soc/esp32p4/include/soc/soc_caps.h +++ b/components/soc/esp32p4/include/soc/soc_caps.h @@ -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 */