mirror of
https://github.com/espressif/esp-idf.git
synced 2026-10-02 03:00:34 +03:00
feat(mspi): split dma and mspi alignment
This commit is contained in:
@@ -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");
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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 = {
|
||||
|
||||
Reference in New Issue
Block a user