diff --git a/components/esp_hw_support/dma/Kconfig.dma b/components/esp_hw_support/dma/Kconfig.dma index 48aef8846a..fa43dd6ea8 100644 --- a/components/esp_hw_support/dma/Kconfig.dma +++ b/components/esp_hw_support/dma/Kconfig.dma @@ -19,7 +19,7 @@ menu "GDMA Configurations" bool "Enable debug log" default n help - Wether to enable the debug log message for GDMA driver. + Whether to enable the debug log message for GDMA driver. Note that, this option only controls the GDMA driver log, won't affect other drivers. endmenu # GDMA Configurations @@ -40,6 +40,13 @@ menu "DW_GDMA Configurations" Place DW_GDMA setter functions (e.g. dw_gdma_channel_set_block_markers) into IRAM, so that these functions can be IRAM-safe and able to be called in the other IRAM interrupt context. + config DW_GDMA_GETTER_FUNC_IN_IRAM + bool + default n + help + Place DW_GDMA getter functions (e.g. dw_gdma_link_list_get_item) into IRAM, + so that these functions can be IRAM-safe and able to be called in the other IRAM interrupt context. + config DW_GDMA_ISR_IRAM_SAFE bool default n @@ -52,7 +59,7 @@ menu "DW_GDMA Configurations" bool "Enable debug log" default n help - Wether to enable the debug log message for DW_GDMA driver. + Whether to enable the debug log message for DW_GDMA driver. Note that, this option only controls the DW_GDMA driver log, won't affect other drivers. endmenu # DW_GDMA Configurations diff --git a/components/esp_hw_support/dma/linker.lf b/components/esp_hw_support/dma/linker.lf index c4ace066f1..0acf4b6f21 100644 --- a/components/esp_hw_support/dma/linker.lf +++ b/components/esp_hw_support/dma/linker.lf @@ -78,10 +78,15 @@ entries: # put DW_GDMA control functions in IRAM if DW_GDMA_CTRL_FUNC_IN_IRAM = y: dw_gdma: dw_gdma_channel_continue (noflash) + dw_gdma: dw_gdma_channel_enable_ctrl (noflash) if DW_GDMA_SETTER_FUNC_IN_IRAM = y: dw_gdma: dw_gdma_channel_set_block_markers (noflash) dw_gdma: dw_gdma_lli_set_block_markers (noflash) + dw_gdma: dw_gdma_channel_use_link_list (noflash) + + if DW_GDMA_GETTER_FUNC_IN_IRAM = y: + dw_gdma: dw_gdma_link_list_get_item (noflash) [mapping:dma2d_driver] archive: libesp_hw_support.a diff --git a/components/esp_lcd/Kconfig b/components/esp_lcd/Kconfig index 60cde6973c..1f93e5e036 100644 --- a/components/esp_lcd/Kconfig +++ b/components/esp_lcd/Kconfig @@ -36,5 +36,20 @@ menu "LCD and Touch Panel" Only need to enable it when in your application, the DMA can't deliver data as fast as the LCD consumes it. endif # SOC_LCD_RGB_SUPPORTED + + if SOC_MIPI_DSI_SUPPORTED + config LCD_DSI_ISR_IRAM_SAFE + bool "DSI LCD ISR IRAM-Safe" + default n + select DW_GDMA_ISR_IRAM_SAFE + select DW_GDMA_CTRL_FUNC_IN_IRAM + select DW_GDMA_SETTER_FUNC_IN_IRAM + select DW_GDMA_GETTER_FUNC_IN_IRAM + help + Ensure the LCD interrupt is IRAM-Safe by allowing the interrupt handler to be + executable when the cache is disabled (e.g. SPI Flash write). + If you want the LCD driver to keep flushing the screen even when cache ops disabled, + you can enable this option. Note, this will also increase the IRAM usage. + endif # SOC_MIPI_DSI_SUPPORTED endmenu endmenu diff --git a/components/esp_lcd/dsi/esp_lcd_panel_dpi.c b/components/esp_lcd/dsi/esp_lcd_panel_dpi.c index 3eed7e6b34..71375bbf78 100644 --- a/components/esp_lcd/dsi/esp_lcd_panel_dpi.c +++ b/components/esp_lcd/dsi/esp_lcd_panel_dpi.c @@ -14,6 +14,7 @@ #include "esp_cache.h" #include "mipi_dsi_priv.h" #include "esp_async_fbcpy.h" +#include "esp_memory_utils.h" #include "esp_private/dw_gdma.h" #include "hal/cache_hal.h" #include "hal/cache_ll.h" @@ -528,6 +529,17 @@ esp_err_t esp_lcd_dpi_panel_register_event_callbacks(esp_lcd_panel_handle_t pane { ESP_RETURN_ON_FALSE(panel && cbs, ESP_ERR_INVALID_ARG, TAG, "invalid argument"); esp_lcd_dpi_panel_t *dpi_panel = __containerof(panel, esp_lcd_dpi_panel_t, base); +#if CONFIG_LCD_DSI_ISR_IRAM_SAFE + if (cbs->on_color_trans_done) { + ESP_RETURN_ON_FALSE(esp_ptr_in_iram(cbs->on_color_trans_done), ESP_ERR_INVALID_ARG, TAG, "on_color_trans_done callback not in IRAM"); + } + if (cbs->on_refresh_done) { + ESP_RETURN_ON_FALSE(esp_ptr_in_iram(cbs->on_refresh_done), ESP_ERR_INVALID_ARG, TAG, "on_refresh_done callback not in IRAM"); + } + if (user_ctx) { + ESP_RETURN_ON_FALSE(esp_ptr_internal(user_ctx), ESP_ERR_INVALID_ARG, TAG, "user context not in internal RAM"); + } +#endif // CONFIG_LCD_RGB_ISR_IRAM_SAFE dpi_panel->on_color_trans_done = cbs->on_color_trans_done; dpi_panel->on_refresh_done = cbs->on_refresh_done; dpi_panel->user_ctx = user_ctx; diff --git a/components/esp_lcd/dsi/mipi_dsi_priv.h b/components/esp_lcd/dsi/mipi_dsi_priv.h index 57a7407864..eebf93e2e8 100644 --- a/components/esp_lcd/dsi/mipi_dsi_priv.h +++ b/components/esp_lcd/dsi/mipi_dsi_priv.h @@ -23,7 +23,11 @@ #define DSI_RCC_ATOMIC() #endif -#define DSI_MEM_ALLOC_CAPS MALLOC_CAP_DEFAULT +#if CONFIG_LCD_DSI_ISR_IRAM_SAFE +#define DSI_MEM_ALLOC_CAPS (MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT) +#else +#define DSI_MEM_ALLOC_CAPS MALLOC_CAP_DEFAULT +#endif #define DPI_PANEL_MAX_FB_NUM 3 // maximum number of supported frame buffers for DPI panel diff --git a/components/esp_lcd/test_apps/.build-test-rules.yml b/components/esp_lcd/test_apps/.build-test-rules.yml index a4c2fd4995..998917f0ab 100644 --- a/components/esp_lcd/test_apps/.build-test-rules.yml +++ b/components/esp_lcd/test_apps/.build-test-rules.yml @@ -38,7 +38,7 @@ components/esp_lcd/test_apps/mipi_dsi_lcd: disable_test: - if: IDF_TARGET == "esp32p4" temporary: true - reason: lack of runners + reason: lack of runners, DSI can't work without an LCD connected components/esp_lcd/test_apps/rgb_lcd: depends_components: diff --git a/components/esp_lcd/test_apps/mipi_dsi_lcd/CMakeLists.txt b/components/esp_lcd/test_apps/mipi_dsi_lcd/CMakeLists.txt index b28d8784fe..9a4ba3eaaa 100644 --- a/components/esp_lcd/test_apps/mipi_dsi_lcd/CMakeLists.txt +++ b/components/esp_lcd/test_apps/mipi_dsi_lcd/CMakeLists.txt @@ -6,3 +6,16 @@ set(COMPONENTS main) include($ENV{IDF_PATH}/tools/cmake/project.cmake) project(mipi_dsi_lcd_panel_test) + +if(CONFIG_COMPILER_DUMP_RTL_FILES) + add_custom_target(check_test_app_sections ALL + COMMAND ${PYTHON} $ENV{IDF_PATH}/tools/ci/check_callgraph.py + --rtl-dirs ${CMAKE_BINARY_DIR}/esp-idf/esp_lcd/,${CMAKE_BINARY_DIR}/esp-idf/hal/ + --elf-file ${CMAKE_BINARY_DIR}/mipi_dsi_lcd_panel_test.elf + find-refs + --from-sections=.iram0.text + --to-sections=.flash.text,.flash.rodata + --exit-code + DEPENDS ${elf} + ) +endif() diff --git a/components/esp_lcd/test_apps/mipi_dsi_lcd/main/CMakeLists.txt b/components/esp_lcd/test_apps/mipi_dsi_lcd/main/CMakeLists.txt index ab9796d92d..8ee501adae 100644 --- a/components/esp_lcd/test_apps/mipi_dsi_lcd/main/CMakeLists.txt +++ b/components/esp_lcd/test_apps/mipi_dsi_lcd/main/CMakeLists.txt @@ -2,6 +2,10 @@ set(srcs "test_app_main.c" "test_mipi_dsi_board.c" "test_mipi_dsi_panel.c") +if(CONFIG_LCD_DSI_ISR_IRAM_SAFE) + list(APPEND srcs "test_mipi_dsi_iram.c") +endif() + # In order for the cases defined by `TEST_CASE` to be linked into the final elf, # the component can be registered as WHOLE_ARCHIVE idf_component_register(SRCS ${srcs} diff --git a/components/esp_lcd/test_apps/mipi_dsi_lcd/main/test_mipi_dsi_iram.c b/components/esp_lcd/test_apps/mipi_dsi_lcd/main/test_mipi_dsi_iram.c new file mode 100644 index 0000000000..85cef15288 --- /dev/null +++ b/components/esp_lcd/test_apps/mipi_dsi_lcd/main/test_mipi_dsi_iram.c @@ -0,0 +1,119 @@ +/* + * SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#include +#include +#include +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "unity.h" +#include "unity_test_utils.h" +#include "esp_lcd_mipi_dsi.h" +#include "esp_lcd_panel_ops.h" +#include "esp_lcd_panel_io.h" +#include "esp_random.h" +#include "esp_attr.h" +#include "test_mipi_dsi_board.h" +#include "esp_lcd_ili9881c.h" + +IRAM_ATTR static bool test_rgb_panel_count_in_callback(esp_lcd_panel_handle_t panel, esp_lcd_dpi_panel_event_data_t *edata, void *user_ctx) +{ + uint32_t *count = (uint32_t *)user_ctx; + *count = *count + 1; + return false; +} + +static void IRAM_ATTR test_delay_post_cache_disable(void *args) +{ + esp_rom_delay_us(200000); +} + +#define TEST_IMG_SIZE (100 * 100 * sizeof(uint16_t)) + +TEST_CASE("MIPI DSI draw bitmap (ILI9881C) IRAM Safe", "[mipi_dsi]") +{ + esp_lcd_dsi_bus_handle_t mipi_dsi_bus; + esp_lcd_panel_io_handle_t mipi_dbi_io; + esp_lcd_panel_handle_t mipi_dpi_panel; + esp_lcd_panel_handle_t ili9881c_ctrl_panel; + + test_bsp_enable_dsi_phy_power(); + + uint8_t *img = malloc(TEST_IMG_SIZE); + TEST_ASSERT_NOT_NULL(img); + + esp_lcd_dsi_bus_config_t bus_config = { + .bus_id = 0, + .num_data_lanes = 2, + .phy_clk_src = MIPI_DSI_PHY_CLK_SRC_DEFAULT, + .lane_bit_rate_mbps = 1000, // 1000 Mbps + }; + TEST_ESP_OK(esp_lcd_new_dsi_bus(&bus_config, &mipi_dsi_bus)); + + esp_lcd_dbi_io_config_t dbi_config = { + .virtual_channel = 0, + .lcd_cmd_bits = 8, + .lcd_param_bits = 8, + }; + TEST_ESP_OK(esp_lcd_new_panel_io_dbi(mipi_dsi_bus, &dbi_config, &mipi_dbi_io)); + + esp_lcd_panel_dev_config_t lcd_dev_config = { + .bits_per_pixel = 16, + .rgb_ele_order = LCD_RGB_ELEMENT_ORDER_RGB, + .reset_gpio_num = -1, + }; + TEST_ESP_OK(esp_lcd_new_panel_ili9881c(mipi_dbi_io, &lcd_dev_config, &ili9881c_ctrl_panel)); + TEST_ESP_OK(esp_lcd_panel_reset(ili9881c_ctrl_panel)); + TEST_ESP_OK(esp_lcd_panel_init(ili9881c_ctrl_panel)); + // turn on display + TEST_ESP_OK(esp_lcd_panel_disp_on_off(ili9881c_ctrl_panel, true)); + + esp_lcd_dpi_panel_config_t dpi_config = { + .dpi_clk_src = MIPI_DSI_DPI_CLK_SRC_DEFAULT, + .dpi_clock_freq_mhz = MIPI_DSI_DPI_CLK_MHZ, + .virtual_channel = 0, + .pixel_format = LCD_COLOR_PIXEL_FORMAT_RGB565, + .video_timing = { + .h_size = MIPI_DSI_LCD_H_RES, + .v_size = MIPI_DSI_LCD_V_RES, + .hsync_back_porch = MIPI_DSI_LCD_HBP, + .hsync_pulse_width = MIPI_DSI_LCD_HSYNC, + .hsync_front_porch = MIPI_DSI_LCD_HFP, + .vsync_back_porch = MIPI_DSI_LCD_VBP, + .vsync_pulse_width = MIPI_DSI_LCD_VSYNC, + .vsync_front_porch = MIPI_DSI_LCD_VFP, + }, + }; + TEST_ESP_OK(esp_lcd_new_panel_dpi(mipi_dsi_bus, &dpi_config, &mipi_dpi_panel)); + TEST_ESP_OK(esp_lcd_panel_init(mipi_dpi_panel)); + uint32_t callback_calls = 0; + esp_lcd_dpi_panel_event_callbacks_t cbs = { + .on_refresh_done = test_rgb_panel_count_in_callback, + }; + TEST_ESP_OK(esp_lcd_dpi_panel_register_event_callbacks(mipi_dpi_panel, &cbs, &callback_calls)); + + uint8_t color_byte = rand() & 0xFF; + int x_start = rand() % (MIPI_DSI_LCD_H_RES - 100); + int y_start = rand() % (MIPI_DSI_LCD_V_RES - 100); + memset(img, color_byte, TEST_IMG_SIZE); + esp_lcd_panel_draw_bitmap(mipi_dpi_panel, x_start, y_start, x_start + 100, y_start + 100, img); + vTaskDelay(pdMS_TO_TICKS(100)); + + printf("The LCD driver should keep flushing the color block in the background\r\n"); + + // disable the cache for a while, the LCD driver should stay working + printf("disable the cache for a while\r\n"); + unity_utils_run_cache_disable_stub(test_delay_post_cache_disable, NULL); + printf("callback calls: %"PRIu32"\r\n", callback_calls); + TEST_ASSERT(callback_calls > 2); + + TEST_ESP_OK(esp_lcd_panel_del(mipi_dpi_panel)); + TEST_ESP_OK(esp_lcd_panel_del(ili9881c_ctrl_panel)); + TEST_ESP_OK(esp_lcd_panel_io_del(mipi_dbi_io)); + TEST_ESP_OK(esp_lcd_del_dsi_bus(mipi_dsi_bus)); + free(img); + + test_bsp_disable_dsi_phy_power(); +} 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 eab77ed4c9..7e4dc07e7d 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 @@ -1,11 +1,18 @@ # SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD # SPDX-License-Identifier: CC0-1.0 - import pytest from pytest_embedded import Dut @pytest.mark.esp32p4 @pytest.mark.generic -def test_rgb_lcd(dut: Dut) -> None: +@pytest.mark.parametrize( + 'config', + [ + 'iram_safe', + 'release', + ], + indirect=True, +) +def test_dsi_lcd(dut: Dut) -> None: dut.run_all_single_board_cases() diff --git a/components/esp_lcd/test_apps/mipi_dsi_lcd/sdkconfig.ci.iram_safe b/components/esp_lcd/test_apps/mipi_dsi_lcd/sdkconfig.ci.iram_safe new file mode 100644 index 0000000000..9d1d9f005f --- /dev/null +++ b/components/esp_lcd/test_apps/mipi_dsi_lcd/sdkconfig.ci.iram_safe @@ -0,0 +1,8 @@ +CONFIG_COMPILER_DUMP_RTL_FILES=y +CONFIG_COMPILER_OPTIMIZATION_NONE=y +# silent the error check, as the error string are stored in rodata, causing RTL check failure +CONFIG_COMPILER_OPTIMIZATION_CHECKS_SILENT=y +CONFIG_COMPILER_OPTIMIZATION_ASSERTIONS_SILENT=y +# place non-ISR FreeRTOS functions in Flash +CONFIG_FREERTOS_PLACE_FUNCTIONS_INTO_FLASH=y +CONFIG_LCD_DSI_ISR_IRAM_SAFE=y