Skip to content

Commit c4768ea

Browse files
committed
Merge branch 'feat/support_cs_signal_in_parlio_tx' into 'master'
feat(parlio_tx): support cs signal on esp32c5 v1.0 Closes IDF-12836 and IDF-12633 See merge request espressif/esp-idf!38646
2 parents ac0f4b5 + 6edf48d commit c4768ea

File tree

19 files changed

+221
-32
lines changed

19 files changed

+221
-32
lines changed

components/esp_driver_parlio/include/driver/parlio_tx.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@ typedef struct {
3030
gpio_num_t clk_out_gpio_num; /*!< GPIO number of the output clock signal, the clock is synced with TX data */
3131
gpio_num_t valid_gpio_num; /*!< GPIO number of the valid signal, which stays high when transferring data.
3232
Note that, the valid signal will always occupy the MSB data bit */
33+
uint16_t valid_start_delay; /*!< The clock cycles that the valid signal becomes active before data start */
34+
uint16_t valid_stop_delay; /*!< The clock cycles that the valid signal keeps active after data end */
3335
size_t trans_queue_depth; /*!< Depth of internal transaction queue */
3436
size_t max_transfer_size; /*!< Maximum transfer size in one transaction, in bytes. This decides the number of DMA nodes will be used for each transaction */
3537
size_t dma_burst_size; /*!< DMA burst size, in bytes */

components/esp_driver_parlio/src/parlio_tx.c

Lines changed: 29 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -130,19 +130,28 @@ static esp_err_t parlio_tx_unit_configure_gpio(parlio_tx_unit_t *tx_unit, const
130130
parlio_periph_signals.groups[group_id].tx_units[unit_id].data_sigs[i], false, false);
131131
}
132132
}
133-
// Note: the valid signal will override TXD[PARLIO_LL_TX_DATA_LINE_AS_VALID_SIG]
133+
134134
if (config->valid_gpio_num >= 0) {
135135
gpio_func_sel(config->valid_gpio_num, PIN_FUNC_GPIO);
136136

137137
// deprecated, to be removed in in esp-idf v6.0
138138
if (config->flags.io_loop_back) {
139139
gpio_input_enable(config->valid_gpio_num);
140140
}
141-
141+
#if !PARLIO_LL_TX_DATA_LINE_AS_VALID_SIG
142+
// Configure CS signal if supported
143+
// Note: the default value of CS signal is low, so we need to invert the CS to keep compatible with the default value
144+
// connect the signal to the GPIO by matrix, it will also enable the output path properly
145+
esp_rom_gpio_connect_out_signal(config->valid_gpio_num,
146+
parlio_periph_signals.groups[group_id].tx_units[unit_id].cs_sig,
147+
!config->flags.invert_valid_out, false);
148+
#else
142149
// connect the signal to the GPIO by matrix, it will also enable the output path properly
150+
// Note: the valid signal will override TXD[PARLIO_LL_TX_DATA_LINE_AS_VALID_SIG]
143151
esp_rom_gpio_connect_out_signal(config->valid_gpio_num,
144152
parlio_periph_signals.groups[group_id].tx_units[unit_id].data_sigs[PARLIO_LL_TX_DATA_LINE_AS_VALID_SIG],
145153
config->flags.invert_valid_out, false);
154+
#endif // !PARLIO_LL_TX_DATA_LINE_AS_VALID_SIG
146155
}
147156
if (config->clk_out_gpio_num >= 0) {
148157
gpio_func_sel(config->clk_out_gpio_num, PIN_FUNC_GPIO);
@@ -295,11 +304,16 @@ esp_err_t parlio_new_tx_unit(const parlio_tx_unit_config_t *config, parlio_tx_un
295304
// data_width must be power of 2 and less than or equal to SOC_PARLIO_TX_UNIT_MAX_DATA_WIDTH
296305
ESP_RETURN_ON_FALSE(data_width && (data_width <= SOC_PARLIO_TX_UNIT_MAX_DATA_WIDTH) && ((data_width & (data_width - 1)) == 0),
297306
ESP_ERR_INVALID_ARG, TAG, "invalid data width");
307+
308+
// No need to check data width conflict with valid signal when CS is supported
309+
#if PARLIO_LL_TX_DATA_LINE_AS_VALID_SIG
298310
// data_width must not conflict with the valid signal
299311
ESP_RETURN_ON_FALSE(!(config->valid_gpio_num >= 0 && data_width > PARLIO_LL_TX_DATA_LINE_AS_VALID_SIG),
300312
ESP_ERR_INVALID_ARG, TAG, "valid signal conflicts with data signal");
313+
#endif // PARLIO_LL_TX_DATA_LINE_AS_VALID_SIG
314+
301315
#if SOC_PARLIO_TX_CLK_SUPPORT_GATING
302-
// clock gating is controlled by either the MSB bit of data bus or the valid signal
316+
// clock gating is controlled by either the MSB bit of data bus or the valid signal(or CS signal when supported)
303317
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),
304318
ESP_ERR_INVALID_ARG, TAG, "no gpio can control the clock gating");
305319
#else
@@ -351,13 +365,25 @@ esp_err_t parlio_new_tx_unit(const parlio_tx_unit_config_t *config, parlio_tx_un
351365
// set data width
352366
parlio_ll_tx_set_bus_width(hal->regs, data_width);
353367
unit->idle_value_mask = (1 << data_width) - 1;
368+
// set valid delay
369+
ESP_GOTO_ON_FALSE(parlio_ll_tx_set_valid_delay(hal->regs, config->valid_start_delay, config->valid_stop_delay), ESP_ERR_INVALID_ARG, err, TAG, "invalid valid delay");
354370
// whether to use the valid signal
371+
#if !PARLIO_LL_TX_DATA_LINE_AS_VALID_SIG
372+
// the clock gating source is actual selectable, it doesn't rely on the available of valid GPIO.
373+
// but there is no use case that valid signal is used, but clocking gating is still controlled by data.
374+
if (config->valid_gpio_num >= 0) {
375+
parlio_ll_tx_clock_gating_from_valid(hal->regs, true);
376+
} else {
377+
parlio_ll_tx_clock_gating_from_valid(hal->regs, false);
378+
}
379+
#else
355380
if (config->valid_gpio_num >= 0) {
356381
parlio_ll_tx_treat_msb_as_valid(hal->regs, true);
357382
unit->idle_value_mask &= ~(1 << PARLIO_LL_TX_DATA_LINE_AS_VALID_SIG);
358383
} else {
359384
parlio_ll_tx_treat_msb_as_valid(hal->regs, false);
360385
}
386+
#endif // !PARLIO_LL_TX_DATA_LINE_AS_VALID_SIG
361387
// set data byte packing order
362388
if (data_width < 8) {
363389
parlio_ll_tx_set_bit_pack_order(hal->regs, config->bit_pack_order);

components/esp_driver_parlio/test_apps/parlio/main/test_parlio_tx.c

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,8 +50,10 @@ TEST_CASE("parallel_tx_unit_install_uninstall", "[parlio_tx]")
5050
config.input_clk_src_freq_hz = 1000000;
5151

5252
config.valid_gpio_num = 0;
53+
#if PARLIO_LL_TX_DATA_LINE_AS_VALID_SIG
5354
// failed because of data line conflict with valid signal
5455
TEST_ESP_ERR(ESP_ERR_INVALID_ARG, parlio_new_tx_unit(&config, &units[0]));
56+
#endif // !PARLIO_LL_TX_DATA_LINE_AS_VALID_SIG
5557

5658
config.data_width = 4;
5759
TEST_ESP_OK(parlio_new_tx_unit(&config, &units[0]));
@@ -288,6 +290,73 @@ TEST_CASE("parallel_tx_clock_gating", "[paralio_tx]")
288290
TEST_ESP_OK(parlio_del_tx_unit(tx_unit));
289291
TEST_ESP_OK(gpio_reset_pin(TEST_CLK_GPIO));
290292
}
293+
294+
#if !PARLIO_LL_TX_DATA_LINE_AS_VALID_SIG
295+
TEST_CASE("parallel_tx_clock_gating_and_msb_coexist", "[paralio_tx]")
296+
{
297+
printf("init a gpio to read parlio_tx clk output\r\n");
298+
gpio_config_t test_gpio_conf = {
299+
.mode = GPIO_MODE_INPUT,
300+
.pin_bit_mask = BIT64(TEST_CLK_GPIO) | BIT64(TEST_DATA7_GPIO),
301+
};
302+
TEST_ESP_OK(gpio_config(&test_gpio_conf));
303+
304+
printf("install parlio tx unit\r\n");
305+
parlio_tx_unit_handle_t tx_unit = NULL;
306+
parlio_tx_unit_config_t config = {
307+
.clk_src = PARLIO_CLK_SRC_DEFAULT,
308+
.data_width = 8,
309+
.clk_in_gpio_num = -1, // use internal clock source
310+
.valid_gpio_num = TEST_VALID_GPIO, // generate the valid signal
311+
.clk_out_gpio_num = TEST_CLK_GPIO,
312+
.data_gpio_nums = {
313+
TEST_DATA0_GPIO,
314+
TEST_DATA1_GPIO,
315+
TEST_DATA2_GPIO,
316+
TEST_DATA3_GPIO,
317+
TEST_DATA4_GPIO,
318+
TEST_DATA5_GPIO,
319+
TEST_DATA6_GPIO,
320+
TEST_DATA7_GPIO,
321+
},
322+
.output_clk_freq_hz = 1 * 1000 * 1000,
323+
.trans_queue_depth = 4,
324+
.max_transfer_size = 256,
325+
.bit_pack_order = PARLIO_BIT_PACK_ORDER_MSB,
326+
.sample_edge = PARLIO_SAMPLE_EDGE_POS,
327+
.valid_start_delay = 5,
328+
.valid_stop_delay = 5,
329+
.flags.clk_gate_en = true, // enable clock gating, controlled by the CS signal
330+
};
331+
TEST_ESP_OK(parlio_new_tx_unit(&config, &tx_unit));
332+
TEST_ESP_OK(parlio_tx_unit_enable(tx_unit));
333+
334+
printf("send packets and see if the clock is gated when there's no transaction on line\r\n");
335+
parlio_transmit_config_t transmit_config = {
336+
// set the idle value to 0x80, so that the MSB is high when there's no transaction
337+
.idle_value = 0x80,
338+
};
339+
uint32_t size = 256;
340+
__attribute__((aligned(64))) uint8_t payload[size];
341+
for (int i = 0; i < size; i++) {
342+
payload[i] = i;
343+
}
344+
TEST_ESP_OK(parlio_tx_unit_transmit(tx_unit, payload, size * sizeof(uint8_t) * 8, &transmit_config));
345+
TEST_ESP_OK(parlio_tx_unit_wait_all_done(tx_unit, -1));
346+
// check if the level on the clock line is low
347+
TEST_ASSERT_EQUAL(0, gpio_get_level(TEST_CLK_GPIO));
348+
TEST_ASSERT_EQUAL(1, gpio_get_level(TEST_DATA7_GPIO));
349+
TEST_ESP_OK(parlio_tx_unit_transmit(tx_unit, payload, size * sizeof(uint8_t) * 8, &transmit_config));
350+
TEST_ESP_OK(parlio_tx_unit_wait_all_done(tx_unit, -1));
351+
TEST_ASSERT_EQUAL(0, gpio_get_level(TEST_CLK_GPIO));
352+
TEST_ASSERT_EQUAL(0, gpio_get_level(TEST_CLK_GPIO));
353+
TEST_ASSERT_EQUAL(1, gpio_get_level(TEST_DATA7_GPIO));
354+
355+
TEST_ESP_OK(parlio_tx_unit_disable(tx_unit));
356+
TEST_ESP_OK(parlio_del_tx_unit(tx_unit));
357+
TEST_ESP_OK(gpio_reset_pin(TEST_CLK_GPIO));
358+
}
359+
#endif // !PARLIO_LL_TX_DATA_LINE_AS_VALID_SIG
291360
#endif // SOC_PARLIO_TX_CLK_SUPPORT_GATING
292361

293362
#if SOC_PSRAM_DMA_CAPABLE

components/esp_lcd/test_apps/parlio_lcd/main/test_parlio_board.h

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
2+
* SPDX-FileCopyrightText: 2024-2025 Espressif Systems (Shanghai) CO LTD
33
*
44
* SPDX-License-Identifier: Apache-2.0
55
*/
@@ -34,12 +34,20 @@ extern "C" {
3434
#define TEST_LCD_PCLK_GPIO (5)
3535
#define TEST_LCD_DATA0_GPIO (4)
3636
#elif CONFIG_IDF_TARGET_ESP32C5
37-
#define TEST_LCD_BK_LIGHT_GPIO (1)
38-
#define TEST_LCD_RST_GPIO (7)
39-
#define TEST_LCD_CS_GPIO (27)
40-
#define TEST_LCD_DC_GPIO (6)
41-
#define TEST_LCD_PCLK_GPIO (25)
42-
#define TEST_LCD_DATA0_GPIO (26)
37+
#define TEST_LCD_BK_LIGHT_GPIO (5)
38+
#define TEST_LCD_RST_GPIO (2)
39+
#define TEST_LCD_CS_GPIO (0)
40+
#define TEST_LCD_DC_GPIO (3)
41+
#define TEST_LCD_PCLK_GPIO (1)
42+
#define TEST_LCD_DATA0_GPIO (4)
43+
#define TEST_LCD_DATA1_GPIO (9)
44+
#define TEST_LCD_DATA2_GPIO (28)
45+
#define TEST_LCD_DATA3_GPIO (24)
46+
#define TEST_LCD_DATA4_GPIO (14)
47+
#define TEST_LCD_DATA5_GPIO (23)
48+
#define TEST_LCD_DATA6_GPIO (13)
49+
#define TEST_LCD_DATA7_GPIO (27)
50+
4351
#endif
4452

4553
#define TEST_LCD_PIXEL_CLOCK_HZ (10 * 1000 * 1000)

components/hal/esp32c5/include/hal/parlio_ll.h

Lines changed: 22 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -32,11 +32,8 @@
3232
#define PARLIO_LL_EVENT_TX_MASK (PARLIO_LL_EVENT_TX_FIFO_EMPTY | PARLIO_LL_EVENT_TX_EOF)
3333
#define PARLIO_LL_EVENT_RX_MASK (PARLIO_LL_EVENT_RX_FIFO_FULL)
3434

35-
#define PARLIO_LL_TX_DATA_LINE_AS_VALID_SIG 7 // TXD[7] can be used a valid signal
3635
#define PARLIO_LL_TX_DATA_LINE_AS_CLK_GATE 7 // TXD[7] can be used as clock gate signal
37-
38-
#define PARLIO_LL_CLK_DIVIDER_MAX (0) // Not support fractional divider
39-
36+
#define PARLIO_LL_TX_VALID_MAX_DELAY 32767
4037
#ifdef __cplusplus
4138
extern "C" {
4239
#endif
@@ -526,18 +523,34 @@ static inline void parlio_ll_tx_start(parl_io_dev_t *dev, bool en)
526523
}
527524

528525
/**
529-
* @brief Whether to treat the MSB of TXD as the valid signal
530-
*
531-
* @note If enabled, TXD[7] will work as valid signal, which stay high during data transmission.
526+
* @brief Set the clock gating from the valid signal
532527
*
533528
* @param dev Parallel IO register base address
534-
* @param en True to enable, False to disable
529+
* @param en If set to true, the clock is gated by the valid signal, otherwise it is gated by the MSB of the data line.
535530
*/
536-
static inline void parlio_ll_tx_treat_msb_as_valid(parl_io_dev_t *dev, bool en)
531+
static inline void parlio_ll_tx_clock_gating_from_valid(parl_io_dev_t *dev, bool en)
537532
{
538533
dev->tx_genrl_cfg.tx_valid_output_en = en;
539534
}
540535

536+
/**
537+
* @brief Set TX valid signal delay
538+
*
539+
* @param dev Parallel IO register base address
540+
* @param start_delay Number of clock cycles to delay
541+
* @param stop_delay Number of clock cycles to delay
542+
* @return true: success, false: valid delay is not supported
543+
*/
544+
static inline bool parlio_ll_tx_set_valid_delay(parl_io_dev_t *dev, uint32_t start_delay, uint32_t stop_delay)
545+
{
546+
if (start_delay > PARLIO_LL_TX_VALID_MAX_DELAY || stop_delay > PARLIO_LL_TX_VALID_MAX_DELAY) {
547+
return false;
548+
}
549+
HAL_FORCE_MODIFY_U32_REG_FIELD(dev->tx_cs_cfg, tx_cs_start_delay, start_delay);
550+
HAL_FORCE_MODIFY_U32_REG_FIELD(dev->tx_cs_cfg, tx_cs_stop_delay, stop_delay);
551+
return true;
552+
}
553+
541554
/**
542555
* @brief Set the sample clock edge
543556
*

components/hal/esp32c6/include/hal/parlio_ll.h

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -509,6 +509,23 @@ static inline void parlio_ll_tx_treat_msb_as_valid(parl_io_dev_t *dev, bool en)
509509
dev->tx_cfg0.tx_hw_valid_en = en;
510510
}
511511

512+
/**
513+
* @brief Set TX valid signal delay
514+
*
515+
* @param dev Parallel IO register base address
516+
* @param start_delay Number of clock cycles to delay
517+
* @param stop_delay Number of clock cycles to delay
518+
* @return true: success, false: valid delay is not supported
519+
*/
520+
static inline bool parlio_ll_tx_set_valid_delay(parl_io_dev_t *dev, uint32_t start_delay, uint32_t stop_delay)
521+
{
522+
(void)dev;
523+
if (start_delay == 0 && stop_delay == 0) {
524+
return true;
525+
}
526+
return false;
527+
}
528+
512529
/**
513530
* @brief Set the sample clock edge
514531
*

components/hal/esp32h2/include/hal/parlio_ll.h

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,6 @@
3737
#define PARLIO_LL_TX_DATA_LINE_AS_VALID_SIG 7 // TXD[7] can be used a valid signal
3838
#define PARLIO_LL_TX_DATA_LINE_AS_CLK_GATE 7 // TXD[7] can be used as clock gate signal
3939

40-
#define PARLIO_LL_CLK_DIVIDER_MAX (0) // Not support fractional divider
41-
4240
#ifdef __cplusplus
4341
extern "C" {
4442
#endif
@@ -534,7 +532,7 @@ static inline void parlio_ll_tx_start(parl_io_dev_t *dev, bool en)
534532
/**
535533
* @brief Whether to treat the MSB of TXD as the valid signal
536534
*
537-
* @note If enabled, TXD[15] will work as valid signal, which stay high during data transmission.
535+
* @note If enabled, TXD[7] will work as valid signal, which stay high during data transmission.
538536
*
539537
* @param dev Parallel IO register base address
540538
* @param en True to enable, False to disable
@@ -544,6 +542,23 @@ static inline void parlio_ll_tx_treat_msb_as_valid(parl_io_dev_t *dev, bool en)
544542
dev->tx_genrl_cfg.tx_valid_output_en = en;
545543
}
546544

545+
/**
546+
* @brief Set TX valid signal delay
547+
*
548+
* @param dev Parallel IO register base address
549+
* @param start_delay Number of clock cycles to delay
550+
* @param stop_delay Number of clock cycles to delay
551+
* @return true: success, false: valid delay is not supported
552+
*/
553+
static inline bool parlio_ll_tx_set_valid_delay(parl_io_dev_t *dev, uint32_t start_delay, uint32_t stop_delay)
554+
{
555+
(void)dev;
556+
if (start_delay == 0 && stop_delay == 0) {
557+
return true;
558+
}
559+
return false;
560+
}
561+
547562
/**
548563
* @brief Set the sample clock edge
549564
*

components/hal/esp32p4/include/hal/parlio_ll.h

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -523,6 +523,23 @@ static inline void parlio_ll_tx_set_trans_bit_len(parl_io_dev_t *dev, uint32_t b
523523
dev->tx_data_cfg.tx_bitlen = bitlen;
524524
}
525525

526+
/**
527+
* @brief Set TX valid signal delay
528+
*
529+
* @param dev Parallel IO register base address
530+
* @param start_delay Number of clock cycles to delay
531+
* @param stop_delay Number of clock cycles to delay
532+
* @return true: success, false: valid delay is not supported
533+
*/
534+
static inline bool parlio_ll_tx_set_valid_delay(parl_io_dev_t *dev, uint32_t start_delay, uint32_t stop_delay)
535+
{
536+
(void)dev;
537+
if (start_delay == 0 && stop_delay == 0) {
538+
return true;
539+
}
540+
return false;
541+
}
542+
526543
/**
527544
* @brief Check if tx size can be determined by DMA
528545
*

components/soc/esp32c5/include/soc/Kconfig.soc_caps.in

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -991,6 +991,10 @@ config SOC_PARLIO_SUPPORT_SPI_LCD
991991
bool
992992
default y
993993

994+
config SOC_PARLIO_SUPPORT_I80_LCD
995+
bool
996+
default y
997+
994998
config SOC_MPI_MEM_BLOCKS_NUM
995999
int
9961000
default 4

components/soc/esp32c5/include/soc/soc_caps.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -389,6 +389,7 @@
389389
#define SOC_PARLIO_TX_SUPPORT_LOOP_TRANSMISSION 1 /*!< Support loop transmission */
390390
#define SOC_PARLIO_SUPPORT_SLEEP_RETENTION 1 /*!< Support back up registers before sleep */
391391
#define SOC_PARLIO_SUPPORT_SPI_LCD 1 /*!< Support to drive SPI interfaced LCD */
392+
#define SOC_PARLIO_SUPPORT_I80_LCD 1 /*!< Support to drive I80 interfaced LCD */
392393

393394
/*--------------------------- MPI CAPS ---------------------------------------*/
394395
#define SOC_MPI_MEM_BLOCKS_NUM (4)

0 commit comments

Comments
 (0)