From 5d844e57edf9a77389d9f5fee7c37ec0b9daacad Mon Sep 17 00:00:00 2001 From: morris Date: Mon, 13 May 2024 14:52:01 +0800 Subject: [PATCH] change(rgb_lcd): set DMA transfer burst size --- components/esp_lcd/rgb/esp_lcd_panel_rgb.c | 104 ++++++++++-------- .../esp_lcd/rgb/include/esp_lcd_panel_rgb.h | 7 +- .../test_apps/rgb_lcd/main/test_rgb_panel.c | 2 +- .../rgb_lcd/main/test_yuv_rgb_conv.c | 2 +- .../api-reference/peripherals/lcd/rgb_lcd.rst | 2 +- .../lcd/rgb_panel/main/rgb_lcd_example_main.c | 2 +- 6 files changed, 70 insertions(+), 49 deletions(-) diff --git a/components/esp_lcd/rgb/esp_lcd_panel_rgb.c b/components/esp_lcd/rgb/esp_lcd_panel_rgb.c index 03161e1a96..a12f244229 100644 --- a/components/esp_lcd/rgb/esp_lcd_panel_rgb.c +++ b/components/esp_lcd/rgb/esp_lcd_panel_rgb.c @@ -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. diff --git a/components/esp_lcd/rgb/include/esp_lcd_panel_rgb.h b/components/esp_lcd/rgb/include/esp_lcd_panel_rgb.h index 7cd4c690b7..2e9e5a95a6 100644 --- a/components/esp_lcd/rgb/include/esp_lcd_panel_rgb.h +++ b/components/esp_lcd/rgb/include/esp_lcd_panel_rgb.h @@ -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 */ 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 4e1eb3edd3..8c39c32121 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 @@ -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, diff --git a/components/esp_lcd/test_apps/rgb_lcd/main/test_yuv_rgb_conv.c b/components/esp_lcd/test_apps/rgb_lcd/main/test_yuv_rgb_conv.c index 4bcf65dbdc..73ab6dede7 100644 --- a/components/esp_lcd/test_apps/rgb_lcd/main/test_yuv_rgb_conv.c +++ b/components/esp_lcd/test_apps/rgb_lcd/main/test_yuv_rgb_conv.c @@ -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, diff --git a/docs/en/api-reference/peripherals/lcd/rgb_lcd.rst b/docs/en/api-reference/peripherals/lcd/rgb_lcd.rst index 882dfb7406..1ecb0a0646 100644 --- a/docs/en/api-reference/peripherals/lcd/rgb_lcd.rst +++ b/docs/en/api-reference/peripherals/lcd/rgb_lcd.rst @@ -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. diff --git a/examples/peripherals/lcd/rgb_panel/main/rgb_lcd_example_main.c b/examples/peripherals/lcd/rgb_panel/main/rgb_lcd_example_main.c index 8c8599a3d3..f388294a41 100644 --- a/examples/peripherals/lcd/rgb_panel/main/rgb_lcd_example_main.c +++ b/examples/peripherals/lcd/rgb_panel/main/rgb_lcd_example_main.c @@ -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,