From 07d65333c3d85d19ce8e0b811668b1da0670365d Mon Sep 17 00:00:00 2001 From: Liu Zhongwei Date: Mon, 12 Dec 2022 10:47:09 +0800 Subject: [PATCH 1/2] rgb_lcd: support multi-fb with bounce buffer --- components/esp_lcd/src/esp_lcd_panel_rgb.c | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/components/esp_lcd/src/esp_lcd_panel_rgb.c b/components/esp_lcd/src/esp_lcd_panel_rgb.c index 3ad7bcb917..94ca1df346 100644 --- a/components/esp_lcd/src/esp_lcd_panel_rgb.c +++ b/components/esp_lcd/src/esp_lcd_panel_rgb.c @@ -97,7 +97,8 @@ struct esp_rgb_panel_t { esp_pm_lock_handle_t pm_lock; // Power management lock size_t num_dma_nodes; // Number of DMA descriptors that used to carry the frame buffer uint8_t *fbs[RGB_LCD_PANEL_MAX_FB_NUM]; // Frame buffers - uint8_t cur_fb_index; // Current frame buffer index (0 or 1) + uint8_t cur_fb_index; // Current frame buffer index + uint8_t bb_fb_index; // Current frame buffer index which used by bounce buffer size_t fb_size; // Size of frame buffer int data_gpio_nums[SOC_LCD_RGB_DATA_WIDTH]; // GPIOs used for data lines, we keep these GPIOs for action like "invert_color" uint32_t src_clk_hz; // Peripheral source clock resolution @@ -165,6 +166,7 @@ static esp_err_t lcd_rgb_panel_alloc_frame_buffers(const esp_lcd_rgb_panel_confi } } rgb_panel->cur_fb_index = 0; + rgb_panel->bb_fb_index = 0; rgb_panel->flags.fb_in_psram = fb_in_psram; return ESP_OK; @@ -962,25 +964,26 @@ static IRAM_ATTR bool lcd_rgb_panel_fill_bounce_buffer(esp_rgb_panel_t *panel, u } else { // We do have frame buffer; copy from there. // Note: if the cache is diabled, and accessing the PSRAM by DCACHE will crash. - memcpy(buffer, &panel->fbs[panel->cur_fb_index][panel->bounce_pos_px * bytes_per_pixel], panel->bb_size); + memcpy(buffer, &panel->fbs[panel->bb_fb_index][panel->bounce_pos_px * bytes_per_pixel], panel->bb_size); if (panel->flags.bb_invalidate_cache) { // We don't need the bytes we copied from the psram anymore // Make sure that if anything happened to have changed (because the line already was in cache) we write the data back. - Cache_WriteBack_Addr((uint32_t)&panel->fbs[panel->cur_fb_index][panel->bounce_pos_px * bytes_per_pixel], panel->bb_size); + Cache_WriteBack_Addr((uint32_t)&panel->fbs[panel->bb_fb_index][panel->bounce_pos_px * bytes_per_pixel], panel->bb_size); // Invalidate the data. // Note: possible race: perhaps something on the other core can squeeze a write between this and the writeback, // in which case that data gets discarded. - Cache_Invalidate_Addr((uint32_t)&panel->fbs[panel->cur_fb_index][panel->bounce_pos_px * bytes_per_pixel], panel->bb_size); + Cache_Invalidate_Addr((uint32_t)&panel->fbs[panel->bb_fb_index][panel->bounce_pos_px * bytes_per_pixel], panel->bb_size); } } panel->bounce_pos_px += panel->bb_size / bytes_per_pixel; // If the bounce pos is larger than the frame buffer size, wrap around so the next isr starts pre-loading the next frame. if (panel->bounce_pos_px >= panel->fb_size / bytes_per_pixel) { panel->bounce_pos_px = 0; + panel->bb_fb_index = panel->cur_fb_index; } if (panel->num_fbs > 0) { // Preload the next bit of buffer from psram - Cache_Start_DCache_Preload((uint32_t)&panel->fbs[panel->cur_fb_index][panel->bounce_pos_px * bytes_per_pixel], + Cache_Start_DCache_Preload((uint32_t)&panel->fbs[panel->bb_fb_index][panel->bounce_pos_px * bytes_per_pixel], panel->bb_size, 0); } return need_yield; From 6d229594616d6f4036b1c09caa122c4d99d4f8e2 Mon Sep 17 00:00:00 2001 From: morris Date: Thu, 15 Dec 2022 15:04:37 +0800 Subject: [PATCH 2/2] doc: recommend turn on psram xip feature for bounbe buffer mode --- components/esp_lcd/src/esp_lcd_panel_rgb.c | 4 ---- components/esp_lcd/test_apps/rgb_lcd/main/test_rgb_panel.c | 3 --- components/esp_lcd/test_apps/rgb_lcd/sdkconfig.ci.iram_safe | 2 ++ .../esp_lcd/test_apps/rgb_lcd/sdkconfig.defaults.esp32s3 | 4 ++++ docs/en/api-reference/peripherals/lcd.rst | 6 +++++- 5 files changed, 11 insertions(+), 8 deletions(-) diff --git a/components/esp_lcd/src/esp_lcd_panel_rgb.c b/components/esp_lcd/src/esp_lcd_panel_rgb.c index 94ca1df346..c8dd1d9fea 100644 --- a/components/esp_lcd/src/esp_lcd_panel_rgb.c +++ b/components/esp_lcd/src/esp_lcd_panel_rgb.c @@ -225,10 +225,6 @@ esp_err_t esp_lcd_new_rgb_panel(const esp_lcd_rgb_panel_config_t *rgb_panel_conf ESP_ERR_INVALID_ARG, err, TAG, "must set bounce buffer if there's no frame buffer"); ESP_GOTO_ON_FALSE(!(rgb_panel_config->flags.refresh_on_demand && rgb_panel_config->bounce_buffer_size_px), ESP_ERR_INVALID_ARG, err, TAG, "refresh on demand is not supported under bounce buffer mode"); -#if CONFIG_LCD_RGB_ISR_IRAM_SAFE - ESP_GOTO_ON_FALSE(rgb_panel_config->bounce_buffer_size_px == 0, - ESP_ERR_INVALID_ARG, err, TAG, "bounce buffer mode is not IRAM Safe"); -#endif // determine number of framebuffers size_t num_fbs = 1; diff --git a/components/esp_lcd/test_apps/rgb_lcd/main/test_rgb_panel.c b/components/esp_lcd/test_apps/rgb_lcd/main/test_rgb_panel.c index a10d2e215c..7fb829a886 100644 --- a/components/esp_lcd/test_apps/rgb_lcd/main/test_rgb_panel.c +++ b/components/esp_lcd/test_apps/rgb_lcd/main/test_rgb_panel.c @@ -159,8 +159,6 @@ TEST_CASE("lcd_rgb_panel_refresh_on_demand", "[lcd]") free(img); } -#if !CONFIG_LCD_RGB_ISR_IRAM_SAFE -// bounce buffer mode is not IRAM safe, so we don't test it TEST_CASE("lcd_rgb_panel_bounce_buffer", "[lcd]") { uint8_t *img = malloc(TEST_IMG_SIZE); @@ -184,7 +182,6 @@ TEST_CASE("lcd_rgb_panel_bounce_buffer", "[lcd]") TEST_ESP_OK(esp_lcd_panel_del(panel_handle)); free(img); } -#endif TEST_CASE("lcd_rgb_panel_update_pclk", "[lcd]") { diff --git a/components/esp_lcd/test_apps/rgb_lcd/sdkconfig.ci.iram_safe b/components/esp_lcd/test_apps/rgb_lcd/sdkconfig.ci.iram_safe index d37073719a..0aaa1281df 100644 --- a/components/esp_lcd/test_apps/rgb_lcd/sdkconfig.ci.iram_safe +++ b/components/esp_lcd/test_apps/rgb_lcd/sdkconfig.ci.iram_safe @@ -1,6 +1,8 @@ CONFIG_COMPILER_DUMP_RTL_FILES=y CONFIG_LCD_RGB_ISR_IRAM_SAFE=y CONFIG_GDMA_CTRL_FUNC_IN_IRAM=y +# bounce buffer mode relies on GDMA EOF interrupt to be service-able +CONFIG_GDMA_ISR_IRAM_SAFE=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 diff --git a/components/esp_lcd/test_apps/rgb_lcd/sdkconfig.defaults.esp32s3 b/components/esp_lcd/test_apps/rgb_lcd/sdkconfig.defaults.esp32s3 index 655c2aef85..36a4a647ea 100644 --- a/components/esp_lcd/test_apps/rgb_lcd/sdkconfig.defaults.esp32s3 +++ b/components/esp_lcd/test_apps/rgb_lcd/sdkconfig.defaults.esp32s3 @@ -1,3 +1,7 @@ CONFIG_SPIRAM=y CONFIG_SPIRAM_MODE_OCT=y CONFIG_SPIRAM_SPEED_80M=y + +# Enable the XIP-PSRAM feature, so the ext-mem cache won't be disabled when SPI1 is operating the main flash +CONFIG_SPIRAM_FETCH_INSTRUCTIONS=y +CONFIG_SPIRAM_RODATA=y diff --git a/docs/en/api-reference/peripherals/lcd.rst b/docs/en/api-reference/peripherals/lcd.rst index c47f31217f..7c7d3da229 100644 --- a/docs/en/api-reference/peripherals/lcd.rst +++ b/docs/en/api-reference/peripherals/lcd.rst @@ -376,7 +376,11 @@ More LCD panel drivers and touch drivers are available in `IDF Component Registr Bounce Buffer with Single PSRAM Frame Buffer ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - This mode allocates two so-called ``bounce buffers`` from the internal memory, and a main frame buffer that is still in PSRAM. This mode is selected by setting the :cpp:member:`esp_lcd_rgb_panel_config_t::fb_in_psram` flag and additionally specifying a non-zero :cpp:member:`esp_lcd_rgb_panel_config_t::bounce_buffer_size_px` value. The bounce buffers only need to be large enough to hold a few lines of display data, which is significantly less than the main frame buffer. The LCD peripheral will use DMA to read data from one of the bounce buffers, and meanwhile an interrupt routine will use the CPU DCache to copy data from the main PSRAM frame buffer into the other bounce buffer. Once the LCD peripheral has finished reading the bounce buffer, the two buffers change place and the CPU can fill the others. The advantage of this mode is that, you can achieve higher pixel clock frequency. As the bounce buffers are larger than the FIFOs in the EDMA path, this method is also more robust against short bandwidth spikes. The downside is a major increase in CPU use and the LCD **CAN'T** work if the cache is disabled by flash operations, e.g. OTA or NVS write. + This mode allocates two so-called ``bounce buffers`` from the internal memory, and a main frame buffer that is still in PSRAM. This mode is selected by setting the :cpp:member:`esp_lcd_rgb_panel_config_t::fb_in_psram` flag and additionally specifying a non-zero :cpp:member:`esp_lcd_rgb_panel_config_t::bounce_buffer_size_px` value. The bounce buffers only need to be large enough to hold a few lines of display data, which is significantly less than the main frame buffer. The LCD peripheral will use DMA to read data from one of the bounce buffers, and meanwhile an interrupt routine will use the CPU DCache to copy data from the main PSRAM frame buffer into the other bounce buffer. Once the LCD peripheral has finished reading the bounce buffer, the two buffers change place and the CPU can fill the others. The advantage of this mode is that, you can achieve higher pixel clock frequency. As the bounce buffers are larger than the FIFOs in the EDMA path, this method is also more robust against short bandwidth spikes. The downside is a major increase in CPU use and the LCD **CAN'T** work if we disable the cache of the external memory, via e.g. OTA or NVS write to the main flash. + + .. note:: + + It's highly recommended to turn on the "PSRAM XIP (Execute In Place)" feature in this mode by enabling the Kconfig options: :ref:`CONFIG_SPIRAM_FETCH_INSTRUCTIONS` and :ref:`CONFIG_SPIRAM_RODATA`, which allows the CPU to fetch instructions and readonly data from the PSRAM instead of the main flash. What's more, the external memory cache won't be disabled even if you attempt to write to the main flash through SPI1. This makes it possible to display an OTA progress bar for your application. .. code:: c