change(rgb_lcd): set DMA transfer burst size

This commit is contained in:
morris 2024-05-13 14:52:01 +08:00
parent b2ff20d94c
commit 5d844e57ed
6 changed files with 70 additions and 49 deletions

View File

@ -39,7 +39,8 @@
#include "soc/lcd_periph.h"
#include "hal/lcd_hal.h"
#include "hal/lcd_ll.h"
#include "hal/gdma_ll.h"
#include "hal/cache_hal.h"
#include "hal/cache_ll.h"
#include "rom/cache.h"
#include "esp_cache.h"
@ -77,7 +78,8 @@ static esp_err_t rgb_panel_swap_xy(esp_lcd_panel_t *panel, bool swap_axes);
static esp_err_t rgb_panel_set_gap(esp_lcd_panel_t *panel, int x_gap, int y_gap);
static esp_err_t rgb_panel_disp_on_off(esp_lcd_panel_t *panel, bool off);
static esp_err_t lcd_rgb_panel_select_clock_src(esp_rgb_panel_t *panel, lcd_clock_source_t clk_src);
static esp_err_t lcd_rgb_panel_create_trans_link(esp_rgb_panel_t *panel);
static esp_err_t lcd_rgb_create_dma_channel(esp_rgb_panel_t *panel);
static void lcd_rgb_panel_init_trans_link(esp_rgb_panel_t *panel);
static esp_err_t lcd_rgb_panel_configure_gpio(esp_rgb_panel_t *panel, const esp_lcd_rgb_panel_config_t *panel_config);
static void lcd_rgb_panel_start_transmission(esp_rgb_panel_t *rgb_panel);
static void lcd_default_isr_handler(void *args);
@ -90,8 +92,7 @@ struct esp_rgb_panel_t {
size_t fb_bits_per_pixel; // Frame buffer color depth, in bpp
size_t num_fbs; // Number of frame buffers
size_t output_bits_per_pixel; // Color depth seen from the output data line. Default to fb_bits_per_pixel, but can be changed by YUV-RGB conversion
size_t sram_trans_align; // Alignment for framebuffer that allocated in SRAM
size_t psram_trans_align; // Alignment for framebuffer that allocated in PSRAM
size_t dma_burst_size; // DMA transfer burst size
int disp_gpio_num; // Display control GPIO, which is used to perform action like "disp_off"
intr_handle_t intr; // LCD peripheral interrupt handle
esp_pm_lock_handle_t pm_lock; // Power management lock
@ -134,10 +135,20 @@ struct esp_rgb_panel_t {
static esp_err_t lcd_rgb_panel_alloc_frame_buffers(const esp_lcd_rgb_panel_config_t *rgb_panel_config, esp_rgb_panel_t *rgb_panel)
{
bool fb_in_psram = false;
size_t psram_trans_align = rgb_panel_config->psram_trans_align ? rgb_panel_config->psram_trans_align : 64;
size_t sram_trans_align = rgb_panel_config->sram_trans_align ? rgb_panel_config->sram_trans_align : 4;
rgb_panel->psram_trans_align = psram_trans_align;
rgb_panel->sram_trans_align = sram_trans_align;
size_t ext_mem_align = 0;
size_t int_mem_align = 0;
gdma_get_alignment_constraints(rgb_panel->dma_chan, &int_mem_align, &ext_mem_align);
// also take the cache line size into account when allocating the frame buffer
uint32_t ext_mem_cache_line_size = cache_hal_get_cache_line_size(CACHE_LL_LEVEL_EXT_MEM, CACHE_TYPE_DATA);
uint32_t int_mem_cache_line_size = cache_hal_get_cache_line_size(CACHE_LL_LEVEL_INT_MEM, CACHE_TYPE_DATA);
// The buffer must be aligned to the cache line size
if (ext_mem_cache_line_size) {
ext_mem_align = MAX(ext_mem_align, ext_mem_cache_line_size);
}
if (int_mem_cache_line_size) {
int_mem_align = MAX(int_mem_align, int_mem_cache_line_size);
}
// alloc frame buffer
if (rgb_panel->num_fbs > 0) {
@ -152,13 +163,13 @@ static esp_err_t lcd_rgb_panel_alloc_frame_buffers(const esp_lcd_rgb_panel_confi
for (int i = 0; i < rgb_panel->num_fbs; i++) {
if (fb_in_psram) {
// the low level malloc function will help check the validation of alignment
rgb_panel->fbs[i] = heap_caps_aligned_calloc(psram_trans_align, 1, rgb_panel->fb_size, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT);
rgb_panel->fbs[i] = heap_caps_aligned_calloc(ext_mem_align, 1, rgb_panel->fb_size, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT);
ESP_RETURN_ON_FALSE(rgb_panel->fbs[i], ESP_ERR_NO_MEM, TAG, "no mem for frame buffer");
// calloc not only allocates but also zero's the buffer. We have to make sure this is
// properly committed to the PSRAM, otherwise all sorts of visual corruption will happen.
ESP_RETURN_ON_ERROR(esp_cache_msync(rgb_panel->fbs[i], rgb_panel->fb_size, ESP_CACHE_MSYNC_FLAG_DIR_C2M), TAG, "cache write back failed");
} else {
rgb_panel->fbs[i] = heap_caps_aligned_calloc(sram_trans_align, 1, rgb_panel->fb_size, MALLOC_CAP_INTERNAL | MALLOC_CAP_DMA);
rgb_panel->fbs[i] = heap_caps_aligned_calloc(int_mem_align, 1, rgb_panel->fb_size, MALLOC_CAP_INTERNAL | MALLOC_CAP_DMA);
ESP_RETURN_ON_FALSE(rgb_panel->fbs[i], ESP_ERR_NO_MEM, TAG, "no mem for frame buffer");
}
}
@ -168,7 +179,7 @@ static esp_err_t lcd_rgb_panel_alloc_frame_buffers(const esp_lcd_rgb_panel_confi
if (rgb_panel->bb_size) {
for (int i = 0; i < RGB_LCD_PANEL_BOUNCE_BUF_NUM; i++) {
// bounce buffer must come from SRAM
rgb_panel->bounce_buffer[i] = heap_caps_aligned_calloc(sram_trans_align, 1, rgb_panel->bb_size, MALLOC_CAP_INTERNAL | MALLOC_CAP_DMA);
rgb_panel->bounce_buffer[i] = heap_caps_aligned_calloc(int_mem_align, 1, rgb_panel->bb_size, MALLOC_CAP_INTERNAL | MALLOC_CAP_DMA);
ESP_RETURN_ON_FALSE(rgb_panel->bounce_buffer[i], ESP_ERR_NO_MEM, TAG, "no mem for bounce buffer");
}
}
@ -302,9 +313,6 @@ esp_err_t esp_lcd_new_rgb_panel(const esp_lcd_rgb_panel_config_t *rgb_panel_conf
}
}
// allocate frame buffers + bounce buffers
ESP_GOTO_ON_ERROR(lcd_rgb_panel_alloc_frame_buffers(rgb_panel_config, rgb_panel), err, TAG, "alloc frame buffers failed");
// initialize HAL layer, so we can call LL APIs later
lcd_hal_init(&rgb_panel->hal, panel_id);
// enable clock
@ -334,8 +342,13 @@ esp_err_t esp_lcd_new_rgb_panel(const esp_lcd_rgb_panel_config_t *rgb_panel_conf
// install DMA service
rgb_panel->flags.stream_mode = !rgb_panel_config->flags.refresh_on_demand;
rgb_panel->fb_bits_per_pixel = fb_bits_per_pixel;
ret = lcd_rgb_panel_create_trans_link(rgb_panel);
ESP_GOTO_ON_ERROR(ret, err, TAG, "install DMA failed");
rgb_panel->dma_burst_size = rgb_panel_config->dma_burst_size ? rgb_panel_config->dma_burst_size : 64;
ESP_GOTO_ON_ERROR(lcd_rgb_create_dma_channel(rgb_panel), err, TAG, "install DMA failed");
// allocate frame buffers + bounce buffers
ESP_GOTO_ON_ERROR(lcd_rgb_panel_alloc_frame_buffers(rgb_panel_config, rgb_panel), err, TAG, "alloc frame buffers failed");
// initialize DMA descriptor link
lcd_rgb_panel_init_trans_link(rgb_panel);
// configure GPIO
ret = lcd_rgb_panel_configure_gpio(rgb_panel, rgb_panel_config);
ESP_GOTO_ON_ERROR(ret, err, TAG, "configure GPIO failed");
@ -959,11 +972,42 @@ static IRAM_ATTR bool lcd_rgb_panel_eof_handler(gdma_channel_handle_t dma_chan,
return lcd_rgb_panel_fill_bounce_buffer(panel, panel->bounce_buffer[bb]);
}
static esp_err_t lcd_rgb_create_dma_channel(esp_rgb_panel_t *panel)
{
// alloc DMA channel and connect to LCD peripheral
gdma_channel_alloc_config_t dma_chan_config = {
.direction = GDMA_CHANNEL_DIRECTION_TX,
};
#if SOC_GDMA_TRIG_PERIPH_LCD0_BUS == SOC_GDMA_BUS_AHB
ESP_RETURN_ON_ERROR(gdma_new_ahb_channel(&dma_chan_config, &panel->dma_chan), TAG, "alloc DMA channel failed");
#elif SOC_GDMA_TRIG_PERIPH_LCD0_BUS == SOC_GDMA_BUS_AXI
ESP_RETURN_ON_ERROR(gdma_new_axi_channel(&dma_chan_config, &panel->dma_chan), TAG, "alloc DMA channel failed");
#endif
gdma_connect(panel->dma_chan, GDMA_MAKE_TRIGGER(GDMA_TRIG_PERIPH_LCD, 0));
// configure DMA transfer parameters
gdma_transfer_config_t trans_cfg = {
.max_data_burst_size = panel->dma_burst_size,
.access_ext_mem = true, // frame buffer was allocated from external memory
};
ESP_RETURN_ON_ERROR(gdma_config_transfer(panel->dma_chan, &trans_cfg), TAG, "config DMA transfer failed");
// we need to refill the bounce buffer in the DMA EOF interrupt, so only register the callback for bounce buffer mode
if (panel->bb_size) {
gdma_tx_event_callbacks_t cbs = {
.on_trans_eof = lcd_rgb_panel_eof_handler,
};
gdma_register_tx_event_callbacks(panel->dma_chan, &cbs, panel);
}
return ESP_OK;
}
// If we restart GDMA, many pixels already have been transferred to the LCD peripheral.
// Looks like that has 16 pixels of FIFO plus one holding register.
#define LCD_FIFO_PRESERVE_SIZE_PX (LCD_LL_FIFO_DEPTH + 1)
static esp_err_t lcd_rgb_panel_create_trans_link(esp_rgb_panel_t *panel)
static void lcd_rgb_panel_init_trans_link(esp_rgb_panel_t *panel)
{
for (int i = 0; i < RGB_LCD_PANEL_DMA_LINKS_REPLICA; i++) {
panel->dma_links[i] = &panel->dma_nodes[panel->num_dma_nodes * i];
@ -1007,32 +1051,6 @@ static esp_err_t lcd_rgb_panel_create_trans_link(esp_rgb_panel_t *panel)
panel->dma_restart_node.buffer = &p[restart_skip_bytes];
panel->dma_restart_node.dw0.length -= restart_skip_bytes;
panel->dma_restart_node.dw0.size -= restart_skip_bytes;
// alloc DMA channel and connect to LCD peripheral
gdma_channel_alloc_config_t dma_chan_config = {
.direction = GDMA_CHANNEL_DIRECTION_TX,
};
#if SOC_GDMA_TRIG_PERIPH_LCD0_BUS == SOC_GDMA_BUS_AHB
ESP_RETURN_ON_ERROR(gdma_new_ahb_channel(&dma_chan_config, &panel->dma_chan), TAG, "alloc DMA channel failed");
#elif SOC_GDMA_TRIG_PERIPH_LCD0_BUS == SOC_GDMA_BUS_AXI
ESP_RETURN_ON_ERROR(gdma_new_axi_channel(&dma_chan_config, &panel->dma_chan), TAG, "alloc DMA channel failed");
#endif
gdma_connect(panel->dma_chan, GDMA_MAKE_TRIGGER(GDMA_TRIG_PERIPH_LCD, 0));
gdma_transfer_ability_t ability = {
.psram_trans_align = panel->psram_trans_align,
.sram_trans_align = panel->sram_trans_align,
};
gdma_set_transfer_ability(panel->dma_chan, &ability);
// we need to refill the bounce buffer in the DMA EOF interrupt, so only register the callback for bounce buffer mode
if (panel->bb_size) {
gdma_tx_event_callbacks_t cbs = {
.on_trans_eof = lcd_rgb_panel_eof_handler,
};
gdma_register_tx_event_callbacks(panel->dma_chan, &cbs, panel);
}
return ESP_OK;
}
// reset the GDMA channel every VBlank to stop permanent desyncs from happening.

View File

@ -132,8 +132,11 @@ typedef struct {
size_t num_fbs; /*!< Number of screen-sized frame buffers that allocated by the driver. By default (set to either 0 or 1) only one frame buffer will be used. Maximum number of buffers are 3 */
size_t bounce_buffer_size_px; /*!< If it's non-zero, the driver allocates two DRAM bounce buffers for DMA use.
DMA fetching from DRAM bounce buffer is much faster than PSRAM frame buffer. */
size_t sram_trans_align; /*!< Alignment of buffers (frame buffer or bounce buffer) that allocated in SRAM */
size_t psram_trans_align; /*!< Alignment of buffers (frame buffer) that allocated in PSRAM */
size_t sram_trans_align __attribute__((deprecated)); /*!< Alignment of buffers (frame buffer or bounce buffer) that allocated in SRAM */
union {
size_t psram_trans_align; /*!< Alignment of buffers (frame buffer) that allocated in PSRAM */
size_t dma_burst_size; /*!< DMA burst size, in bytes */
};
int hsync_gpio_num; /*!< GPIO used for HSYNC signal */
int vsync_gpio_num; /*!< GPIO used for VSYNC signal */
int de_gpio_num; /*!< GPIO used for DE signal, set to -1 if it's not used */

View File

@ -31,7 +31,7 @@ static esp_lcd_panel_handle_t test_rgb_panel_initialization(size_t data_width, s
esp_lcd_panel_handle_t panel_handle = NULL;
esp_lcd_rgb_panel_config_t panel_config = {
.data_width = data_width,
.psram_trans_align = 64,
.dma_burst_size = 64,
.bounce_buffer_size_px = bb_pixels,
.bits_per_pixel = bpp,
.clk_src = LCD_CLK_SRC_DEFAULT,

View File

@ -27,7 +27,7 @@ TEST_CASE("lcd_rgb_panel_yuv422_conversion", "[lcd]")
esp_lcd_panel_handle_t panel_handle = NULL;
esp_lcd_rgb_panel_config_t panel_config = {
.data_width = 16,
.psram_trans_align = 64,
.dma_burst_size = 64,
.bits_per_pixel = 16, // YUV422: 16bits per pixel
.clk_src = LCD_CLK_SRC_DEFAULT,
.disp_gpio_num = TEST_LCD_DISP_EN_GPIO,

View File

@ -7,7 +7,7 @@ RGB LCD panel is allocated in one step: :cpp:func:`esp_lcd_new_rgb_panel`, with
- :cpp:member:`esp_lcd_rgb_panel_config_t::data_width` set number of data lines used by the RGB interface. Currently, the supported value can be 8 or 16.
- :cpp:member:`esp_lcd_rgb_panel_config_t::bits_per_pixel` set the number of bits per pixel. This is different from :cpp:member:`esp_lcd_rgb_panel_config_t::data_width`. By default, if you set this field to 0, the driver will automatically adjust the bpp to the :cpp:member:`esp_lcd_rgb_panel_config_t::data_width`. But in some cases, these two value must be different. For example, a Serial RGB interface LCD only needs ``8`` data lines, but the color width can reach to ``RGB888``, i.e., the :cpp:member:`esp_lcd_rgb_panel_config_t::bits_per_pixel` should be set to ``24``.
- :cpp:member:`esp_lcd_rgb_panel_config_t::hsync_gpio_num`, :cpp:member:`esp_lcd_rgb_panel_config_t::vsync_gpio_num`, :cpp:member:`esp_lcd_rgb_panel_config_t::de_gpio_num`, :cpp:member:`esp_lcd_rgb_panel_config_t::pclk_gpio_num`, :cpp:member:`esp_lcd_rgb_panel_config_t::disp_gpio_num` and :cpp:member:`esp_lcd_rgb_panel_config_t::data_gpio_nums` are the GPIO pins used by the RGB LCD controller. If some of them are not used, please set it to `-1`.
- :cpp:member:`esp_lcd_rgb_panel_config_t::sram_trans_align` and :cpp:member:`esp_lcd_rgb_panel_config_t::psram_trans_align` set the alignment of the allocated frame buffer. Internally, the DMA transfer ability will adjust against these alignment values. A higher alignment value can lead to a bigger DMA burst size. Please note, the alignment value must be a power of 2.
- :cpp:member:`esp_lcd_rgb_panel_config_t::dma_burst_size` set the DMA transfer burst size, the value must be a power of 2.
- :cpp:member:`esp_lcd_rgb_panel_config_t::bounce_buffer_size_px` set the size of bounce buffer. This is only necessary for a so-called "bounce buffer" mode. Please refer to :ref:`bounce_buffer_with_single_psram_frame_buffer` for more information.
- :cpp:member:`esp_lcd_rgb_panel_config_t::timings` sets the LCD panel specific timing parameters. All required parameters are listed in the :cpp:type:`esp_lcd_rgb_timing_t`, including the LCD resolution and blanking porches. Please fill them according to the datasheet of your LCD.
- :cpp:member:`esp_lcd_rgb_panel_config_t::fb_in_psram` sets whether to allocate the frame buffer from PSRAM or not. Please refer to :ref:`single_frame_buffer_in_psram` for more information.

View File

@ -166,7 +166,7 @@ void app_main(void)
esp_lcd_panel_handle_t panel_handle = NULL;
esp_lcd_rgb_panel_config_t panel_config = {
.data_width = 16, // RGB565 in parallel mode, thus 16bit in width
.psram_trans_align = 64,
.dma_burst_size = 64,
.num_fbs = EXAMPLE_LCD_NUM_FB,
#if CONFIG_EXAMPLE_USE_BOUNCE_BUFFER
.bounce_buffer_size_px = 10 * EXAMPLE_LCD_H_RES,