Merge branch 'fix/psram-ecc-mspi-dma-align' into 'master'

fix(mspi): handle PSRAM ECC DMA alignment across MSPI users

Closes IDF-15850, IDF-15910, and IDF-15700

See merge request espressif/esp-idf!49987
This commit is contained in:
morris
2026-08-20 11:51:46 +08:00
60 changed files with 829 additions and 467 deletions
@@ -93,9 +93,12 @@ extern void example_lvgl_demo_ui(lv_display_t *disp);
#if CONFIG_EXAMPLE_USE_DMA2D_COPY_FRAME
void example_rounder_flush_area_cb(lv_event_t * event)
{
// Under flash encryption, DMA2D access to PSRAM must satisfy MSPI encryption
// alignment (typically 16 bytes) for both the buffer address and transfer size.
// Round the LVGL invalidate area so the flush region width meets that requirement.
lv_area_t * area = lv_event_get_invalidated_area(event);
area->x1 = ESP_ALIGN_DOWN(area->x1, 16);
area->x2 = ESP_ALIGN_UP(area->x2, 16) - 1;
area->x2 = ESP_ALIGN_UP(area->x2 + 1, 16) - 1;
}
#endif
@@ -106,7 +109,7 @@ static void example_lvgl_flush_cb(lv_display_t *disp, const lv_area_t *area, uin
int offsetx2 = area->x2;
int offsety1 = area->y1;
int offsety2 = area->y2;
// pass the draw buffer to the driver
// LVGL area coordinates are inclusive; panel draw_bitmap expects [start, end).
esp_lcd_panel_draw_bitmap(panel_handle, offsetx1, offsety1, offsetx2 + 1, offsety2 + 1, px_map);
}
@@ -296,23 +299,14 @@ void app_main(void)
void *buf2 = NULL;
ESP_LOGI(TAG, "Allocate separate LVGL draw buffers");
size_t alignment = 1;
#if CONFIG_EXAMPLE_USE_DMA2D_COPY_FRAME
if (esp_efuse_is_flash_encryption_enabled()) {
alignment = SOC_GDMA_EXT_MEM_ENC_ALIGNMENT;
if (EXAMPLE_MIPI_DSI_LCD_H_RES % alignment != 0) {
ESP_LOGW(TAG, "EXAMPLE_MIPI_DSI_LCD_H_RES is not aligned to %d, may cause MSPI error", alignment);
}
}
#endif
size_t draw_buffer_sz = EXAMPLE_MIPI_DSI_LCD_H_RES * EXAMPLE_LVGL_DRAW_BUF_LINES * sizeof(lv_color_t);
// Note:
// Keep the display buffer in **internal** RAM can speed up the UI because LVGL uses it a lot and it should have a fast access time
// This example allocate the buffer from PSRAM mainly because we want to save the internal RAM
buf1 = heap_caps_aligned_calloc(alignment, 1, draw_buffer_sz, MALLOC_CAP_SPIRAM);
buf1 = heap_caps_aligned_calloc(16, 1, draw_buffer_sz, MALLOC_CAP_SPIRAM);
assert(buf1);
buf2 = heap_caps_aligned_calloc(alignment, 1, draw_buffer_sz, MALLOC_CAP_SPIRAM);
buf2 = heap_caps_aligned_calloc(16, 1, draw_buffer_sz, MALLOC_CAP_SPIRAM);
assert(buf2);
// initialize LVGL draw buffers
lv_display_set_buffers(display, buf1, buf2, draw_buffer_sz, LV_DISPLAY_RENDER_MODE_PARTIAL);
@@ -320,9 +314,13 @@ void app_main(void)
lv_display_set_flush_cb(display, example_lvgl_flush_cb);
#if CONFIG_EXAMPLE_USE_DMA2D_COPY_FRAME
// If flash encryption is enabled, DMA2D requires the flush buffer address and size to be aligned to 16 bytes.
// We need to round the flush area to the multiple of 16.
if (esp_efuse_is_flash_encryption_enabled()) {
// If Flash Encryption/ PSRAM ECC is enabled, DMA2D requires the flush buffer address and size to be aligned.
// Round the LVGL invalidate area accordingly (this is an LVGL integration hook, not a panel API).
bool need_rounder = esp_efuse_is_flash_encryption_enabled();
#if CONFIG_SPIRAM_ECC_ENABLE
need_rounder = true;
#endif
if (need_rounder) {
ESP_LOGI(TAG, "Register event callback for LVGL flush area rounding");
lv_display_add_event_cb(display, example_rounder_flush_area_cb, LV_EVENT_INVALIDATE_AREA, NULL);
}