mirror of
https://github.com/espressif/esp-idf.git
synced 2024-10-05 20:47:46 -04:00
change(rgb_lcd): set DMA transfer burst size
This commit is contained in:
parent
b2ff20d94c
commit
5d844e57ed
@ -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.
|
||||
|
@ -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 */
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
@ -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.
|
||||
|
@ -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,
|
||||
|
Loading…
Reference in New Issue
Block a user