feat(mspi): split dma and mspi alignment

This commit is contained in:
Chen Jichang
2026-08-17 16:49:27 +08:00
parent e7a290a870
commit 2b8029c775
54 changed files with 685 additions and 424 deletions
+10 -3
View File
@@ -13,6 +13,7 @@
#include "esp_memory_utils.h"
#include "esp_private/async_memcpy_dma2d.h"
#include "esp_private/dw_gdma.h"
#include "esp_private/dma2d.h"
#include "hal/color_hal.h"
typedef struct esp_lcd_dpi_panel_t esp_lcd_dpi_panel_t;
@@ -223,17 +224,23 @@ esp_err_t esp_lcd_new_panel_dpi(esp_lcd_dsi_bus_handle_t bus, const esp_lcd_dpi_
dpi_panel->bus = bus;
dpi_panel->num_fbs = num_fbs;
// Although the DW-GDMA can handle unaligned data, frame buffer still may be the dst of DMA2D operation.
// Allocate FB with DMA2D alloc alignment so address is usable as a DMA2D destination when enabled later.
// Line-size / window misalignment will fail later in async_color_convert's DMA2D transaction check.
size_t dma2d_align = dma2d_get_alloc_alignment();
// allocate frame buffer from PSRAM
size_t fb_size = panel_config->video_timing.h_size * panel_config->video_timing.v_size * bits_per_pixel / 8;
for (int i = 0; i < num_fbs; i++) {
uint8_t *frame_buffer = heap_caps_calloc(1, fb_size, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT | MALLOC_CAP_DMA);
uint8_t *frame_buffer = heap_caps_aligned_calloc(dma2d_align, 1, fb_size,
MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT | MALLOC_CAP_DMA);
ESP_GOTO_ON_FALSE(frame_buffer, ESP_ERR_NO_MEM, err, TAG, "no memory for frame buffer");
dpi_panel->fbs[i] = frame_buffer;
ESP_LOGD(TAG, "fb[%d] @%p", i, frame_buffer);
// preset the frame buffer with black color
// the frame buffer address alignment is ensured by `heap_caps_calloc`
// the frame buffer address alignment is ensured by `heap_caps_aligned_calloc`
// while the value of the fb_size may not be aligned to the cache line size
// but that's not a problem because the `heap_caps_calloc` internally allocated a buffer whose size is aligned up to the cache line size
// but that's not a problem because the `heap_caps_aligned_calloc` internally allocated a buffer whose size is aligned up to the cache line size
ESP_GOTO_ON_ERROR(esp_cache_msync(frame_buffer, fb_size, ESP_CACHE_MSYNC_FLAG_DIR_C2M | ESP_CACHE_MSYNC_FLAG_UNALIGNED),
err, TAG, "cache write back failed");
}
+6 -12
View File
@@ -543,11 +543,12 @@ static esp_err_t panel_io_i80_tx_param(esp_lcd_panel_io_t *io, int lcd_cmd, cons
trans_desc->data = (param && param_len) ? bus->format_buffer : NULL;
trans_desc->data_length = trans_desc->data ? param_len : 4;
trans_desc->trans_done_cb = NULL; // no callback for parameter transaction
size_t buffer_alignment = (trans_desc->data == NULL || esp_ptr_internal(trans_desc->data)) ? bus->int_mem_align : bus->ext_mem_align;
static uint32_t fake_trigger = 0;
void *mount_buffer = trans_desc->data ? (void *)trans_desc->data : &fake_trigger;
size_t buffer_alignment = gdma_get_buffer_alignment_constraint(bus->dma_chan, mount_buffer);
// mount data to DMA links
gdma_buffer_mount_config_t mount_config = {
.buffer = trans_desc->data ? (void *)trans_desc->data : (&fake_trigger),
.buffer = mount_buffer,
.buffer_alignment = buffer_alignment,
.length = trans_desc->data_length,
.flags = {
@@ -581,15 +582,6 @@ static esp_err_t panel_io_i80_tx_color(esp_lcd_panel_io_t *io, int lcd_cmd, cons
lcd_i80_trans_descriptor_t *trans_desc = NULL;
ESP_RETURN_ON_FALSE(color_size <= bus->max_transfer_bytes, ESP_ERR_INVALID_ARG, TAG,
"color bytes too long, enlarge max_transfer_bytes");
if (esp_ptr_external_ram(color)) {
// check alignment
ESP_RETURN_ON_FALSE(((uint32_t)color & (bus->ext_mem_align - 1)) == 0, ESP_ERR_INVALID_ARG, TAG, "color address not aligned");
ESP_RETURN_ON_FALSE((color_size & (bus->ext_mem_align - 1)) == 0, ESP_ERR_INVALID_ARG, TAG, "color size not aligned");
} else {
// check alignment
ESP_RETURN_ON_FALSE(((uint32_t)color & (bus->int_mem_align - 1)) == 0, ESP_ERR_INVALID_ARG, TAG, "color address not aligned");
ESP_RETURN_ON_FALSE((color_size & (bus->int_mem_align - 1)) == 0, ESP_ERR_INVALID_ARG, TAG, "color size not aligned");
}
if (esp_cache_get_line_size_by_addr(color) > 0) {
// flush data from cache to the physical memory
esp_cache_msync((void *)color, color_size, ESP_CACHE_MSYNC_FLAG_DIR_C2M | ESP_CACHE_MSYNC_FLAG_UNALIGNED);
@@ -705,7 +697,7 @@ static esp_err_t lcd_i80_init_dma_link(esp_lcd_i80_bus_handle_t bus, const esp_l
.access_ext_mem = true, // the LCD can carry pixel buffer from the external memory
};
ESP_RETURN_ON_ERROR(gdma_config_transfer(bus->dma_chan, &trans_cfg), TAG, "config DMA transfer failed");
gdma_get_alignment_constraints(bus->dma_chan, &bus->int_mem_align, &bus->ext_mem_align);
gdma_get_channel_alignment_constraints(bus->dma_chan, &bus->int_mem_align, &bus->ext_mem_align, NULL);
size_t buffer_alignment = MAX(bus->int_mem_align, bus->ext_mem_align);
size_t num_dma_nodes = esp_dma_calculate_node_count(bus->max_transfer_bytes, buffer_alignment, LCD_DMA_DESCRIPTOR_BUFFER_MAX_SIZE);
@@ -902,8 +894,10 @@ IRAM_ATTR static void i80_lcd_default_isr_handler(void *args)
bus->cur_trans = trans_desc;
bus->cur_device = next_device;
// mount data to DMA links
size_t buffer_alignment = gdma_get_buffer_alignment_constraint(bus->dma_chan, trans_desc->data);
gdma_buffer_mount_config_t mount_config = {
.buffer = (void *)trans_desc->data,
.buffer_alignment = buffer_alignment,
.length = trans_desc->data_length,
.flags = {
.mark_eof = true,
+3 -18
View File
@@ -186,10 +186,6 @@ static esp_err_t lcd_rgb_panel_alloc_frame_buffers(esp_rgb_panel_t *rgb_panel, c
{
bool fb_in_psram = rgb_panel->flags.fb_in_psram;
// read the cache line size of internal and external memory, we use this information to check if the allocated memory is behind the cache
uint32_t int_mem_cache_line_size = cache_hal_get_cache_line_size(CACHE_LL_LEVEL_INT_MEM, CACHE_TYPE_DATA);
uint32_t ext_mem_cache_line_size = cache_hal_get_cache_line_size(CACHE_LL_LEVEL_EXT_MEM, CACHE_TYPE_DATA);
// alloc frame buffer
uint8_t user_fb_count = 0;
for (int i = 0; i < rgb_panel->num_fbs; i++) {
@@ -200,16 +196,6 @@ static esp_err_t lcd_rgb_panel_alloc_frame_buffers(esp_rgb_panel_t *rgb_panel, c
if (!esp_ptr_dma_capable(panel_config->user_fbs[i]) && !esp_ptr_dma_ext_capable(panel_config->user_fbs[i])) {
ESP_RETURN_ON_FALSE(false, ESP_ERR_INVALID_ARG, TAG, "frame buffer %d is not DMA accessible", i);
}
// Check if user frame buffer is in PSRAM or internal memory
if (esp_ptr_external_ram(panel_config->user_fbs[i])) {
ESP_RETURN_ON_FALSE(((uintptr_t)panel_config->user_fbs[i] & (rgb_panel->ext_mem_align - 1)) == 0,
ESP_ERR_INVALID_ARG, TAG, "frame buffer %d is not aligned to "PRIu32"", i, rgb_panel->ext_mem_align);
rgb_panel->flags.fb_behind_cache = ext_mem_cache_line_size > 0;
} else {
ESP_RETURN_ON_FALSE(((uintptr_t)panel_config->user_fbs[i] & (rgb_panel->int_mem_align - 1)) == 0,
ESP_ERR_INVALID_ARG, TAG, "frame buffer %d is not aligned to "PRIu32"", i, rgb_panel->int_mem_align);
rgb_panel->flags.fb_behind_cache = int_mem_cache_line_size > 0;
}
rgb_panel->fbs[i] = (uint8_t *)panel_config->user_fbs[i];
user_fb_count++;
} else {
@@ -219,15 +205,14 @@ static esp_err_t lcd_rgb_panel_alloc_frame_buffers(esp_rgb_panel_t *rgb_panel, c
rgb_panel->fbs[i] = heap_caps_aligned_calloc(rgb_panel->ext_mem_align, 1, rgb_panel->fb_size,
MALLOC_CAP_SPIRAM | MALLOC_CAP_DMA | MALLOC_CAP_8BIT);
ESP_RETURN_ON_FALSE(rgb_panel->fbs[i], ESP_ERR_NO_MEM, TAG, "no mem for frame buffer");
rgb_panel->flags.fb_behind_cache = ext_mem_cache_line_size > 0;
} else {
rgb_panel->fbs[i] = heap_caps_aligned_calloc(rgb_panel->int_mem_align, 1, rgb_panel->fb_size,
MALLOC_CAP_INTERNAL | MALLOC_CAP_DMA | MALLOC_CAP_8BIT);
ESP_RETURN_ON_FALSE(rgb_panel->fbs[i], ESP_ERR_NO_MEM, TAG, "no mem for frame buffer");
rgb_panel->flags.fb_behind_cache = int_mem_cache_line_size > 0;
}
}
rgb_panel->flags.fb_behind_cache = esp_cache_get_line_size_by_addr(rgb_panel->fbs[i]) > 0;
// flush data from cache to the physical memory
if (rgb_panel->flags.fb_behind_cache) {
ESP_LOGD(TAG, "frame buffer %d at %p is behind the cache", i, rgb_panel->fbs[i]);
@@ -246,7 +231,7 @@ static esp_err_t lcd_rgb_panel_alloc_frame_buffers(esp_rgb_panel_t *rgb_panel, c
rgb_panel->bounce_buffer[i] = heap_caps_aligned_calloc(rgb_panel->int_mem_align, 1, rgb_panel->bb_size,
MALLOC_CAP_INTERNAL | MALLOC_CAP_DMA | MALLOC_CAP_8BIT);
ESP_RETURN_ON_FALSE(rgb_panel->bounce_buffer[i], ESP_ERR_NO_MEM, TAG, "no mem for bounce buffer");
if (int_mem_cache_line_size > 0) {
if (esp_cache_get_line_size_by_addr(rgb_panel->bounce_buffer[i]) > 0) {
// flush data from cache to the physical memory
esp_cache_msync(rgb_panel->bounce_buffer[i], rgb_panel->bb_size, ESP_CACHE_MSYNC_FLAG_DIR_C2M | ESP_CACHE_MSYNC_FLAG_UNALIGNED);
rgb_panel->flags.bb_behind_cache = true;
@@ -1253,7 +1238,7 @@ static esp_err_t lcd_rgb_create_dma_channel(esp_rgb_panel_t *rgb_panel)
};
ESP_RETURN_ON_ERROR(gdma_config_transfer(rgb_panel->dma_chan, &trans_cfg), TAG, "config DMA transfer failed");
// get the memory alignment required by the DMA
gdma_get_alignment_constraints(rgb_panel->dma_chan, &rgb_panel->int_mem_align, &rgb_panel->ext_mem_align);
gdma_get_channel_alignment_constraints(rgb_panel->dma_chan, &rgb_panel->int_mem_align, &rgb_panel->ext_mem_align, NULL);
// register DMA event callbacks
gdma_tx_event_callbacks_t cbs = {