diff --git a/components/esp_hal_lcd/esp32p4/include/hal/mipi_dsi_host_ll.h b/components/esp_hal_lcd/esp32p4/include/hal/mipi_dsi_host_ll.h index bd30ee1408d..e038cff427c 100644 --- a/components/esp_hal_lcd/esp32p4/include/hal/mipi_dsi_host_ll.h +++ b/components/esp_hal_lcd/esp32p4/include/hal/mipi_dsi_host_ll.h @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2023-2026 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -665,6 +665,17 @@ static inline bool mipi_dsi_host_ll_gen_is_read_cmd_busy(dsi_host_dev_t *dev) return dev->cmd_pkt_status.gen_rd_cmd_busy; } +/** + * @brief Has the low-power reception timed out? + * + * @param dev Pointer to the DSI Host controller register base address + * @return True if low-power reception timed out, False otherwise + */ +static inline bool mipi_dsi_host_ll_is_lp_rx_timeout(dsi_host_dev_t *dev) +{ + return dev->int_st1.to_lp_rx; +} + /** * @brief Is the read payload FIFO of the generic interface full? * diff --git a/components/esp_hal_lcd/include/hal/mipi_dsi_hal.h b/components/esp_hal_lcd/include/hal/mipi_dsi_hal.h index 3c1692383be..6ea714d622c 100644 --- a/components/esp_hal_lcd/include/hal/mipi_dsi_hal.h +++ b/components/esp_hal_lcd/include/hal/mipi_dsi_hal.h @@ -104,8 +104,9 @@ void mipi_dsi_hal_host_gen_write_dcs_command(mipi_dsi_hal_context_t *hal, uint8_ * @param command_bytes Number of bytes of the command * @param ret_param Pointer to the buffer to store the returned parameters * @param param_buf_size Size of the buffer to store the returned parameters + * @return True if the command response is received, False if LP RX times out */ -void mipi_dsi_hal_host_gen_read_dcs_command(mipi_dsi_hal_context_t *hal, uint8_t vc, +bool mipi_dsi_hal_host_gen_read_dcs_command(mipi_dsi_hal_context_t *hal, uint8_t vc, uint32_t command, uint32_t command_bytes, void *ret_param, uint16_t param_buf_size); /** @@ -140,8 +141,9 @@ void mipi_dsi_hal_host_gen_write_short_packet(mipi_dsi_hal_context_t *hal, uint8 * @param header_data Data to be filled into the DSI packet header * @param ret_buffer Pointer to the buffer to store the returned data * @param buffer_size Size of the buffer to store the returned data + * @return True if the packet response is received, False if LP RX times out */ -void mipi_dsi_hal_host_gen_read_short_packet(mipi_dsi_hal_context_t *hal, uint8_t vc, +bool mipi_dsi_hal_host_gen_read_short_packet(mipi_dsi_hal_context_t *hal, uint8_t vc, mipi_dsi_data_type_t dt, uint16_t header_data, void *ret_buffer, uint16_t buffer_size); /** diff --git a/components/esp_hal_lcd/mipi_dsi_hal.c b/components/esp_hal_lcd/mipi_dsi_hal.c index 565cb80c64d..3a82db2294c 100644 --- a/components/esp_hal_lcd/mipi_dsi_hal.c +++ b/components/esp_hal_lcd/mipi_dsi_hal.c @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2023-2025 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2023-2026 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -16,6 +16,18 @@ HAL_LOG_ATTR_TAG(TAG, "dsi_hal"); +static inline bool mipi_dsi_hal_host_wait_timeout(mipi_dsi_hal_context_t *hal, + bool (*condition)(mipi_dsi_host_soc_handle_t host), + bool (*timeout)(mipi_dsi_host_soc_handle_t host)) +{ + while (condition(hal->host)) { + if (timeout(hal->host)) { + return true; + } + } + return timeout(hal->host); +} + void mipi_dsi_hal_init(mipi_dsi_hal_context_t *hal, const mipi_dsi_hal_config_t *config) { hal->host = MIPI_DSI_LL_GET_HOST(config->bus_id); @@ -203,7 +215,7 @@ void mipi_dsi_hal_host_gen_write_long_packet(mipi_dsi_hal_context_t *hal, uint8_ mipi_dsi_host_ll_gen_set_packet_header(hal->host, vc, dt, wc_msb, wc_lsb); } -void mipi_dsi_hal_host_gen_read_short_packet(mipi_dsi_hal_context_t *hal, uint8_t vc, mipi_dsi_data_type_t dt, uint16_t header_data, void *ret_buffer, uint16_t buffer_size) +bool mipi_dsi_hal_host_gen_read_short_packet(mipi_dsi_hal_context_t *hal, uint8_t vc, mipi_dsi_data_type_t dt, uint16_t header_data, void *ret_buffer, uint16_t buffer_size) { uint8_t *receive_buffer = (uint8_t *)ret_buffer; // set the maximum returned data size, it should equal to the parameter size of the read command @@ -215,9 +227,13 @@ void mipi_dsi_hal_host_gen_read_short_packet(mipi_dsi_hal_context_t *hal, uint8_ // listen to the same virtual channel as the one sent to mipi_dsi_host_ll_gen_set_rx_vcid(hal->host, vc); mipi_dsi_hal_host_gen_write_short_packet(hal, vc, dt, header_data); - while (mipi_dsi_host_ll_gen_is_read_cmd_busy(hal->host)); + if (mipi_dsi_hal_host_wait_timeout(hal, mipi_dsi_host_ll_gen_is_read_cmd_busy, mipi_dsi_host_ll_is_lp_rx_timeout)) { + return false; + } // wait data to come into the fifo - while (mipi_dsi_host_ll_gen_is_read_fifo_empty(hal->host)); + if (mipi_dsi_hal_host_wait_timeout(hal, mipi_dsi_host_ll_gen_is_read_fifo_empty, mipi_dsi_host_ll_is_lp_rx_timeout)) { + return false; + } uint32_t temp = 0; uint32_t counter = 0; while (!mipi_dsi_host_ll_gen_is_read_fifo_empty(hal->host)) { @@ -229,12 +245,13 @@ void mipi_dsi_hal_host_gen_read_short_packet(mipi_dsi_hal_context_t *hal, uint8_ } } } + return true; } -void mipi_dsi_hal_host_gen_read_dcs_command(mipi_dsi_hal_context_t *hal, uint8_t vc, uint32_t command, uint32_t command_bytes, void *ret_param, uint16_t param_buf_size) +bool mipi_dsi_hal_host_gen_read_dcs_command(mipi_dsi_hal_context_t *hal, uint8_t vc, uint32_t command, uint32_t command_bytes, void *ret_param, uint16_t param_buf_size) { uint16_t header_data = command & ((1U << (8 * command_bytes)) - 1); - mipi_dsi_hal_host_gen_read_short_packet(hal, vc, MIPI_DSI_DT_DCS_READ_0, header_data, ret_param, param_buf_size); + return mipi_dsi_hal_host_gen_read_short_packet(hal, vc, MIPI_DSI_DT_DCS_READ_0, header_data, ret_param, param_buf_size); } void mipi_dsi_hal_host_dpi_set_horizontal_timing(mipi_dsi_hal_context_t *hal, uint32_t hsw, uint32_t hbp, uint32_t active_width, uint32_t hfp) diff --git a/components/esp_lcd/dsi/esp_lcd_mipi_dsi_bus.c b/components/esp_lcd/dsi/esp_lcd_mipi_dsi_bus.c index d4baedfc39f..26878e892d3 100644 --- a/components/esp_lcd/dsi/esp_lcd_mipi_dsi_bus.c +++ b/components/esp_lcd/dsi/esp_lcd_mipi_dsi_bus.c @@ -11,6 +11,7 @@ #define MIPI_DSI_DEFAULT_TIMEOUT_CLOCK_FREQ_MHZ 10 // TxClkEsc frequency must be configured between 2 and 20 MHz #define MIPI_DSI_DEFAULT_ESCAPE_CLOCK_FREQ_MHZ 18 +#define MIPI_DSI_DEFAULT_HOST_LP_RX_TIMEOUT_COUNT 0x7FFF esp_err_t esp_lcd_new_dsi_bus(const esp_lcd_dsi_bus_config_t *bus_config, esp_lcd_dsi_bus_handle_t *ret_bus) { @@ -118,9 +119,11 @@ esp_err_t esp_lcd_new_dsi_bus(const esp_lcd_dsi_bus_config_t *bus_config, esp_lc mipi_dsi_host_ll_set_timeout_clock_division(hal->host, (uint32_t)roundf(bus_config->lane_bit_rate_mbps / 8.0f / MIPI_DSI_DEFAULT_TIMEOUT_CLOCK_FREQ_MHZ)); // Set the divider to get the TX Escape clock, clock source is the high-speed byte clock mipi_dsi_host_ll_set_escape_clock_division(hal->host, (uint32_t)roundf(bus_config->lane_bit_rate_mbps / 8.0f / MIPI_DSI_DEFAULT_ESCAPE_CLOCK_FREQ_MHZ)); - // set the timeout intervals to zero, means to disable the timeout mechanism - mipi_dsi_host_ll_set_timeout_count(hal->host, 0, 0, 0, 0, 0, 0, 0); - // DSI host will wait indefinitely for a read response from the DSI device + // Enable host timeout detection for command mode transactions. + mipi_dsi_host_ll_set_timeout_count(hal->host, 0, + MIPI_DSI_DEFAULT_HOST_LP_RX_TIMEOUT_COUNT, + 0, 0, 0, 0, 0); + // Set the maximum time required to perform a read command, measured in lane byte clock cycles. mipi_dsi_phy_ll_set_max_read_time(hal->host, 6000); // set how long the DSI host will wait before sending the next transmission mipi_dsi_phy_ll_set_stop_wait_time(hal->host, 0x3F); diff --git a/components/esp_lcd/dsi/esp_lcd_panel_io_dbi.c b/components/esp_lcd/dsi/esp_lcd_panel_io_dbi.c index 9ad7782af57..bf3448bc215 100644 --- a/components/esp_lcd/dsi/esp_lcd_panel_io_dbi.c +++ b/components/esp_lcd/dsi/esp_lcd_panel_io_dbi.c @@ -83,7 +83,8 @@ static esp_err_t panel_io_dbi_rx_param(esp_lcd_panel_io_t *io, int lcd_cmd, void esp_lcd_dsi_bus_handle_t bus = dbi_io->bus; mipi_dsi_hal_context_t *hal = &bus->hal; - mipi_dsi_hal_host_gen_read_dcs_command(hal, dbi_io->virtual_channel, lcd_cmd, dbi_io->lcd_cmd_bits / 8, param, param_size); + ESP_RETURN_ON_FALSE(mipi_dsi_hal_host_gen_read_dcs_command(hal, dbi_io->virtual_channel, lcd_cmd, dbi_io->lcd_cmd_bits / 8, param, param_size), + ESP_ERR_TIMEOUT, TAG, "DSI read command timeout"); return ESP_OK; } diff --git a/components/esp_lcd/test_apps/.build-test-rules.yml b/components/esp_lcd/test_apps/.build-test-rules.yml index b0b8a63c83c..c2a4e29f678 100644 --- a/components/esp_lcd/test_apps/.build-test-rules.yml +++ b/components/esp_lcd/test_apps/.build-test-rules.yml @@ -26,10 +26,6 @@ components/esp_lcd/test_apps/mipi_dsi_lcd: - esp_lcd disable: - if: SOC_MIPI_DSI_SUPPORTED != 1 - disable_test: - - if: IDF_TARGET == "esp32p4" - temporary: true - reason: lack of runners, DSI can't work without an LCD connected components/esp_lcd/test_apps/parlio_lcd: depends_components: diff --git a/components/esp_lcd/test_apps/mipi_dsi_lcd/pytest_mipi_dsi_lcd.py b/components/esp_lcd/test_apps/mipi_dsi_lcd/pytest_mipi_dsi_lcd.py index dab2c388aec..4f1c0a6a89a 100644 --- a/components/esp_lcd/test_apps/mipi_dsi_lcd/pytest_mipi_dsi_lcd.py +++ b/components/esp_lcd/test_apps/mipi_dsi_lcd/pytest_mipi_dsi_lcd.py @@ -15,7 +15,6 @@ from pytest_embedded_idf.utils import idf_parametrize indirect=True, ) @idf_parametrize('target', ['esp32p4'], indirect=['target']) -@pytest.mark.temp_skip_ci(targets=['esp32p4'], reason='no runner') def test_dsi_lcd(dut: Dut) -> None: dut.run_all_single_board_cases()