From 00b1d66c6e74750b445e1f328aa6d50b8148dd94 Mon Sep 17 00:00:00 2001 From: Chen Jichang Date: Tue, 6 May 2025 15:28:19 +0800 Subject: [PATCH] feat(parlio_tx): support to use dma eof on esp32c5 --- components/esp_driver_parlio/src/parlio_tx.c | 17 +++++-- .../test_apps/parlio/main/CMakeLists.txt | 1 + .../test_apps/parlio/main/test_parlio_tx.c | 48 +++++++++++++++++++ .../esp32c5/include/soc/Kconfig.soc_caps.in | 4 ++ components/soc/esp32c5/include/soc/soc_caps.h | 1 + .../peripherals/parlio/parlio_tx.rst | 6 +++ .../peripherals/parlio/parlio_tx.rst | 6 +++ .../main/parlio_simulate_example_main.c | 2 +- 8 files changed, 80 insertions(+), 5 deletions(-) diff --git a/components/esp_driver_parlio/src/parlio_tx.c b/components/esp_driver_parlio/src/parlio_tx.c index 724a7ef057..3e168beb02 100644 --- a/components/esp_driver_parlio/src/parlio_tx.c +++ b/components/esp_driver_parlio/src/parlio_tx.c @@ -342,8 +342,13 @@ 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_SUPPORT_EOF_FROM_DMA + // always use DMA EOF as the Parlio TX EOF if supported + parlio_ll_tx_set_eof_condition(hal->regs, PARLIO_LL_TX_EOF_COND_DMA_EOF); +#else // 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_SUPPORT_EOF_FROM_DMA // clear any pending interrupt parlio_ll_clear_interrupt_status(hal->regs, PARLIO_LL_EVENT_TX_MASK); @@ -460,6 +465,7 @@ static void parlio_tx_do_transaction(parlio_tx_unit_t *tx_unit, parlio_tx_trans_ if (t->flags.loop_transmission) { // Once a loop transmission is started, it cannot be stopped until it is disabled + // If SOC_PARLIO_TX_SUPPORT_EOF_FROM_DMA is supported, setting the eof condition to PARLIO_LL_TX_EOF_COND_DMA_EOF again is harmless parlio_ll_tx_set_eof_condition(hal->regs, PARLIO_LL_TX_EOF_COND_DMA_EOF); } @@ -586,9 +592,11 @@ 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); +#if !SOC_PARLIO_TX_SUPPORT_EOF_FROM_DMA // 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 + // change the EOF condition to be the data length, so the EOF will be triggered normally in the following transaction parlio_ll_tx_set_eof_condition(hal->regs, PARLIO_LL_TX_EOF_COND_DATA_LEN); +#endif // !SOC_PARLIO_TX_SUPPORT_EOF_FROM_DMA #if CONFIG_PM_ENABLE // release power management lock @@ -620,12 +628,13 @@ esp_err_t parlio_tx_unit_transmit(parlio_tx_unit_handle_t tx_unit, const void *p 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 !SOC_PARLIO_TX_SUPPORT_EOF_FROM_DMA + // check the max payload size if it's not a loop transmission and the DMA EOF is not supported 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"); + ESP_ERR_INVALID_ARG, TAG, "invalid transfer size, max transfer size should be less than %d", PARLIO_LL_TX_MAX_BITS_PER_FRAME / 8); } +#endif // !SOC_PARLIO_TX_SUPPORT_EOF_FROM_DMA size_t cache_line_size = 0; size_t alignment = 0; diff --git a/components/esp_driver_parlio/test_apps/parlio/main/CMakeLists.txt b/components/esp_driver_parlio/test_apps/parlio/main/CMakeLists.txt index f5c7730b24..070c011970 100644 --- a/components/esp_driver_parlio/test_apps/parlio/main/CMakeLists.txt +++ b/components/esp_driver_parlio/test_apps/parlio/main/CMakeLists.txt @@ -8,6 +8,7 @@ endif() if(CONFIG_SOC_BITSCRAMBLER_SUPPORTED) list(APPEND srcs "test_parlio_bitscrambler.c") +endif() if(CONFIG_PARLIO_TX_ISR_CACHE_SAFE) list(APPEND srcs "test_parlio_tx_cache_safe.c") 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 e32472f4ec..8389c93a29 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 @@ -598,3 +598,51 @@ TEST_CASE("parlio_tx_loop_transmission", "[parlio_tx]") TEST_ESP_OK(parlio_del_tx_unit(tx_unit)); } #endif // SOC_PARLIO_TX_SUPPORT_LOOP_TRANSMISSION + +#if SOC_PARLIO_TX_SUPPORT_EOF_FROM_DMA +TEST_CASE("parlio_tx can transmit buffer larger than max_size decided by datalen_eof", "[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 = 4, + .clk_in_gpio_num = -1, // use internal clock source + .valid_gpio_num = TEST_VALID_GPIO, // generate the valid signal + .clk_out_gpio_num = TEST_CLK_GPIO, + .data_gpio_nums = { + TEST_DATA0_GPIO, + TEST_DATA1_GPIO, + TEST_DATA2_GPIO, + TEST_DATA3_GPIO, + }, + .output_clk_freq_hz = 10 * 1000 * 1000, + .trans_queue_depth = 1, + .max_transfer_size = 100 * 1024, + .bit_pack_order = PARLIO_BIT_PACK_ORDER_LSB, + .sample_edge = PARLIO_SAMPLE_EDGE_POS, + .flags.clk_gate_en = true, + }; + + TEST_ESP_OK(parlio_new_tx_unit(&config, &tx_unit)); + TEST_ESP_OK(parlio_tx_unit_enable(tx_unit)); + + const size_t buffer_size = 100 * 1024; // 100KB, larger than the 65535 bytes limit + uint8_t *buffer = heap_caps_malloc(buffer_size, MALLOC_CAP_8BIT | MALLOC_CAP_DMA); + TEST_ASSERT_NOT_NULL(buffer); + for (int i = 0; i < buffer_size; i++) { + buffer[i] = i; + } + + parlio_transmit_config_t transmit_config = { + .idle_value = 0x00, + }; + + TEST_ESP_OK(parlio_tx_unit_transmit(tx_unit, buffer, buffer_size * 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)); + free(buffer); +} +#endif // SOC_PARLIO_TX_SUPPORT_EOF_FROM_DMA diff --git a/components/soc/esp32c5/include/soc/Kconfig.soc_caps.in b/components/soc/esp32c5/include/soc/Kconfig.soc_caps.in index ed97f5a826..4e7114dbaf 100644 --- a/components/soc/esp32c5/include/soc/Kconfig.soc_caps.in +++ b/components/soc/esp32c5/include/soc/Kconfig.soc_caps.in @@ -1019,6 +1019,10 @@ config SOC_PARLIO_TX_SUPPORT_LOOP_TRANSMISSION bool default y +config SOC_PARLIO_TX_SUPPORT_EOF_FROM_DMA + bool + default y + config SOC_PARLIO_SUPPORT_SLEEP_RETENTION bool default y diff --git a/components/soc/esp32c5/include/soc/soc_caps.h b/components/soc/esp32c5/include/soc/soc_caps.h index 4060195390..3ccac15471 100644 --- a/components/soc/esp32c5/include/soc/soc_caps.h +++ b/components/soc/esp32c5/include/soc/soc_caps.h @@ -392,6 +392,7 @@ #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 */ +#define SOC_PARLIO_TX_SUPPORT_EOF_FROM_DMA 1 /*!< Support to treat DMA EOF as TX unit EOF */ #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 */ diff --git a/docs/en/api-reference/peripherals/parlio/parlio_tx.rst b/docs/en/api-reference/peripherals/parlio/parlio_tx.rst index 5d469be742..ccd68e8701 100644 --- a/docs/en/api-reference/peripherals/parlio/parlio_tx.rst +++ b/docs/en/api-reference/peripherals/parlio/parlio_tx.rst @@ -320,6 +320,12 @@ The waveform of the external clock input is shown below: After writing the BitScrambler program, we can enable it by calling :cpp:func:`parlio_tx_unit_decorate_bitscrambler`. And configure the :cpp:member:`parlio_transmit_config_t::bitscrambler_program` to point to the binary file of the BitScrambler program. Different transmission transactions can use different BitScrambler programs. The binary file must conform to the BitScrambler assembly language specification, and will be loaded into the BitScrambler's instruction memory at runtime. For details on how to write and compile the BitScrambler program, please refer to :doc:`BitScrambler Programming Guide `. + .. only:: not SOC_PARLIO_TX_SUPPORT_EOF_FROM_DMA + + .. note:: + + Due to hardware limitations, the bitstream generated by the BitScrambler cannot change the length compared to the original bitstream, otherwise transmission blocking or data loss may occur. + :cpp:func:`parlio_tx_unit_decorate_bitscrambler` and :cpp:func:`parlio_tx_unit_undecorate_bitscrambler` need to be used in pairs. When deleting the TX unit, you need to call :cpp:func:`parlio_tx_unit_undecorate_bitscrambler` first to remove the BitScrambler. Power Management diff --git a/docs/zh_CN/api-reference/peripherals/parlio/parlio_tx.rst b/docs/zh_CN/api-reference/peripherals/parlio/parlio_tx.rst index ad0fd0ee58..fdd3c8fa01 100644 --- a/docs/zh_CN/api-reference/peripherals/parlio/parlio_tx.rst +++ b/docs/zh_CN/api-reference/peripherals/parlio/parlio_tx.rst @@ -320,6 +320,12 @@ TX 单元可以选择各种不同的时钟源,其中外部时钟源较为特 编写好比特调节器程序后,通过调用 :cpp:func:`parlio_tx_unit_decorate_bitscrambler` 启用比特调节器。并在 :cpp:member:`parlio_transmit_config_t::bitscrambler_program` 配置本次传输使用比特调节器程序的二进制文件。不同的传输事务可以使用不同的比特调节器程序。该二进制文件必须符合比特调节器的汇编语言规范,并且在运行时会被加载到比特调节器的指令存储器中。如何编写并编译比特调节器程序请参考 :doc:`比特调节器编程指南 `。 + .. only:: not SOC_PARLIO_TX_SUPPORT_EOF_FROM_DMA + + .. note:: + + 由于硬件限制,使用比特调节器生成的比特流与原本比特流相比,长度不能发生变化,否则可能会发生传输阻塞或数据丢失。 + :cpp:func:`parlio_tx_unit_decorate_bitscrambler` 和 :cpp:func:`parlio_tx_unit_undecorate_bitscrambler` 需要成对使用。在删除 TX 单元时,需要先调用 :cpp:func:`parlio_tx_unit_undecorate_bitscrambler` 移除比特调节器。 电源管理 diff --git a/examples/peripherals/lcd/parlio_simulate/main/parlio_simulate_example_main.c b/examples/peripherals/lcd/parlio_simulate/main/parlio_simulate_example_main.c index cb4f3f90c1..53e3763248 100644 --- a/examples/peripherals/lcd/parlio_simulate/main/parlio_simulate_example_main.c +++ b/examples/peripherals/lcd/parlio_simulate/main/parlio_simulate_example_main.c @@ -126,7 +126,7 @@ void example_init_parlio_panel(esp_lcd_panel_io_handle_t *io_handle) #endif }, .data_width = CONFIG_EXAMPLE_LCD_PARLIO_DATA_WIDTH, - .max_transfer_bytes = EXAMPLE_LCD_H_RES * 100 * sizeof(uint16_t), + .max_transfer_bytes = EXAMPLE_LCD_H_RES * EXAMPLE_LVGL_DRAW_BUF_LINES * sizeof(uint16_t), .dma_burst_size = EXAMPLE_DMA_BURST_SIZE, .cs_gpio_num = EXAMPLE_PIN_NUM_CS, .pclk_hz = EXAMPLE_LCD_PIXEL_CLOCK_HZ,