From dc1d14a37f39fad9673d98a6833a4f372bff887d Mon Sep 17 00:00:00 2001 From: morris Date: Fri, 23 Jul 2021 11:04:35 +0800 Subject: [PATCH] lcd: support putting RGB frame buffer in PSRAM --- .../esp_lcd/include/esp_lcd_panel_rgb.h | 1 + components/esp_lcd/src/esp_lcd_rgb_panel.c | 53 ++++++++++++++---- components/esp_lcd/test/test_rgb_panel.c | 54 +++++++++---------- 3 files changed, 69 insertions(+), 39 deletions(-) diff --git a/components/esp_lcd/include/esp_lcd_panel_rgb.h b/components/esp_lcd/include/esp_lcd_panel_rgb.h index 0847e84bd2..6493292460 100644 --- a/components/esp_lcd/include/esp_lcd_panel_rgb.h +++ b/components/esp_lcd/include/esp_lcd_panel_rgb.h @@ -54,6 +54,7 @@ typedef struct { struct { unsigned int disp_active_low: 1; /*!< If this flag is enabled, a low level of display control signal can turn the screen on; vice versa */ unsigned int relax_on_idle: 1; /*!< If this flag is enabled, the host won't refresh the LCD if nothing changed in host's frame buffer (this is usefull for LCD with built-in GRAM) */ + unsigned int fb_in_psram: 1; /*!< If this flag is enabled, the frame buffer will be allocated from PSRAM preferentially */ } flags; } esp_lcd_rgb_panel_config_t; diff --git a/components/esp_lcd/src/esp_lcd_rgb_panel.c b/components/esp_lcd/src/esp_lcd_rgb_panel.c index a9fe06bdb3..b62daf9481 100644 --- a/components/esp_lcd/src/esp_lcd_rgb_panel.c +++ b/components/esp_lcd/src/esp_lcd_rgb_panel.c @@ -10,6 +10,7 @@ #include #include #include +#include "sdkconfig.h" #include "freertos/FreeRTOS.h" #include "freertos/task.h" #include "freertos/semphr.h" @@ -27,6 +28,9 @@ #include "esp_private/gdma.h" #include "driver/gpio.h" #include "driver/periph_ctrl.h" +#if CONFIG_SPIRAM +#include "spiram.h" +#endif #if SOC_LCDCAM_SUPPORTED #include "esp_lcd_common.h" #include "soc/lcd_periph.h" @@ -37,6 +41,9 @@ static const char *TAG = "lcd_panel.rgb"; typedef struct esp_rgb_panel_t esp_rgb_panel_t; +// This function is located in ROM (also see esp_rom/${target}/ld/${target}.rom.ld) +extern int Cache_WriteBack_Addr(uint32_t addr, uint32_t size); + static esp_err_t rgb_panel_del(esp_lcd_panel_t *panel); static esp_err_t rgb_panel_reset(esp_lcd_panel_t *panel); static esp_err_t rgb_panel_init(esp_lcd_panel_t *panel); @@ -72,11 +79,12 @@ struct esp_rgb_panel_t { int x_gap; // Extra gap in x coordinate, it's used when calculate the flush window int y_gap; // Extra gap in y coordinate, it's used when calculate the flush window struct { - int disp_en_level: 1; // The level which can turn on the screen by `disp_gpio_num` - int stream_mode: 1; // If set, the LCD transfers data continuously, otherwise, it stops refreshing the LCD when transaction done - int new_frame: 1; // Whether the frame we're going to flush is a new one + unsigned int disp_en_level: 1; // The level which can turn on the screen by `disp_gpio_num` + unsigned int stream_mode: 1; // If set, the LCD transfers data continuously, otherwise, it stops refreshing the LCD when transaction done + unsigned int new_frame: 1; // Whether the frame we're going to flush is a new one + unsigned int fb_in_psram: 1; // Whether the frame buffer is in PSRAM } flags; - dma_descriptor_t dma_nodes[0]; // DMA descriptor pool of size `num_dma_nodes` + dma_descriptor_t dma_nodes[]; // DMA descriptor pool of size `num_dma_nodes` }; esp_err_t esp_lcd_new_rgb_panel(const esp_lcd_rgb_panel_config_t *rgb_panel_config, esp_lcd_panel_handle_t *ret_panel) @@ -96,10 +104,24 @@ esp_err_t esp_lcd_new_rgb_panel(const esp_lcd_rgb_panel_config_t *rgb_panel_conf rgb_panel = heap_caps_calloc(1, sizeof(esp_rgb_panel_t) + num_dma_nodes * sizeof(dma_descriptor_t), MALLOC_CAP_DMA); ESP_GOTO_ON_FALSE(rgb_panel, ESP_ERR_NO_MEM, no_mem_panel, TAG, "no mem for rgb panel"); rgb_panel->num_dma_nodes = num_dma_nodes; - // alloc frame buffer, currently we have to put the frame buffer in SRAM - rgb_panel->fb = heap_caps_calloc(1, fb_size, MALLOC_CAP_INTERNAL); + // alloc frame buffer + bool alloc_from_psram = false; + // fb_in_psram is only an option, if there's no PSRAM on board, we still alloc from SRAM + if (rgb_panel_config->flags.fb_in_psram) { +#if CONFIG_SPIRAM_USE_MALLOC || CONFIG_SPIRAM_USE_CAPS_ALLOC + if (esp_spiram_is_initialized()) { + alloc_from_psram = true; + } +#endif + } + if (alloc_from_psram) { + rgb_panel->fb = heap_caps_calloc(1, fb_size, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT); + } else { + rgb_panel->fb = heap_caps_calloc(1, fb_size, MALLOC_CAP_INTERNAL); + } ESP_GOTO_ON_FALSE(rgb_panel->fb, ESP_ERR_NO_MEM, no_mem_fb, TAG, "no mem for frame buffer"); rgb_panel->fb_size = fb_size; + rgb_panel->flags.fb_in_psram = alloc_from_psram; // semaphore indicates new frame trans done rgb_panel->done_sem = xSemaphoreCreateBinary(); ESP_GOTO_ON_FALSE(rgb_panel->done_sem, ESP_ERR_NO_MEM, no_mem_sem, TAG, "create done sem failed"); @@ -113,7 +135,7 @@ esp_err_t esp_lcd_new_rgb_panel(const esp_lcd_rgb_panel_config_t *rgb_panel_conf // initialize HAL layer, so we can call LL APIs later lcd_hal_init(&rgb_panel->hal, panel_id); // install interrupt service, (LCD peripheral shares the interrupt source with Camera by different mask) - int isr_flags = 0; + int isr_flags = ESP_INTR_FLAG_SHARED; ret = esp_intr_alloc_intrstatus(lcd_periph_signals.panels[panel_id].irq_id, isr_flags, (uint32_t)lcd_ll_get_interrupt_status_reg(rgb_panel->hal.dev), LCD_LL_EVENT_VSYNC_END, lcd_default_isr_handler, rgb_panel, &rgb_panel->intr); @@ -257,18 +279,22 @@ static esp_err_t rgb_panel_draw_bitmap(esp_lcd_panel_t *panel, int x_start, int y_end = MIN(y_end, rgb_panel->timings.v_res); xSemaphoreTake(rgb_panel->done_sem, portMAX_DELAY); // wait for last transaction done // convert the frame buffer to 3D array - int bytes_pre_pixel = rgb_panel->data_width / 8; - int pixels_pre_line = rgb_panel->timings.h_res; + int bytes_per_pixel = rgb_panel->data_width / 8; + int pixels_per_line = rgb_panel->timings.h_res; const uint8_t *from = (const uint8_t *)color_data; - uint8_t (*to)[pixels_pre_line][bytes_pre_pixel] = (uint8_t (*)[pixels_pre_line][bytes_pre_pixel])rgb_panel->fb; + uint8_t (*to)[pixels_per_line][bytes_per_pixel] = (uint8_t (*)[pixels_per_line][bytes_per_pixel])rgb_panel->fb; // manipulate the frame buffer for (int j = y_start; j < y_end; j++) { for (int i = x_start; i < x_end; i++) { - for (int k = 0; k < bytes_pre_pixel; k++) { + for (int k = 0; k < bytes_per_pixel; k++) { to[j][i][k] = *from++; } } } + if (rgb_panel->flags.fb_in_psram) { + // CPU writes data to PSRAM through DCache, data in PSRAM might not get updated, so write back + Cache_WriteBack_Addr((uint32_t)&to[y_start][0][0], (y_end - y_start) * rgb_panel->timings.h_res * bytes_per_pixel); + } // we don't care the exact frame ID, as long as it's different from the previous one rgb_panel->new_frame_id++; if (!rgb_panel->flags.stream_mode) { @@ -400,6 +426,11 @@ static esp_err_t lcd_rgb_panel_create_trans_link(esp_rgb_panel_t *panel) ret = gdma_new_channel(&dma_chan_config, &panel->dma_chan); ESP_GOTO_ON_ERROR(ret, err, TAG, "alloc DMA channel failed"); gdma_connect(panel->dma_chan, GDMA_MAKE_TRIGGER(GDMA_TRIG_PERIPH_LCD, 0)); + gdma_transfer_ability_t ability = { + .psram_trans_align = 64, + .sram_trans_align = 4, + }; + gdma_set_transfer_ability(panel->dma_chan, &ability); // the start of DMA should be prior to the start of LCD engine gdma_start(panel->dma_chan, (intptr_t)panel->dma_nodes); diff --git a/components/esp_lcd/test/test_rgb_panel.c b/components/esp_lcd/test/test_rgb_panel.c index bce9d81faf..6aa1da2969 100644 --- a/components/esp_lcd/test/test_rgb_panel.c +++ b/components/esp_lcd/test/test_rgb_panel.c @@ -8,33 +8,31 @@ #define TEST_LCD_H_RES (480) #define TEST_LCD_V_RES (272) -#define TEST_LCD_VSYNC_GPIO (19) -#define TEST_LCD_HSYNC_GPIO (18) +#define TEST_LCD_VSYNC_GPIO (1) +#define TEST_LCD_HSYNC_GPIO (2) #define TEST_LCD_DE_GPIO (-1) -#define TEST_LCD_PCLK_GPIO (17) -#define TEST_LCD_DATA0_GPIO (42) // B0 -#define TEST_LCD_DATA1_GPIO (41) // B1 -#define TEST_LCD_DATA2_GPIO (40) // B2 -#define TEST_LCD_DATA3_GPIO (39) // B3 -#define TEST_LCD_DATA4_GPIO (38) // B4 -#define TEST_LCD_DATA5_GPIO (4) // G0 -#define TEST_LCD_DATA6_GPIO (5) // G1 -#define TEST_LCD_DATA7_GPIO (6) // G2 -#define TEST_LCD_DATA8_GPIO (7) // G3 -#define TEST_LCD_DATA9_GPIO (15) // G4 -#define TEST_LCD_DATA10_GPIO (16) // G5 -#define TEST_LCD_DATA11_GPIO (37) // R0 -#define TEST_LCD_DATA12_GPIO (36) // R1 -#define TEST_LCD_DATA13_GPIO (35) // R2 -#define TEST_LCD_DATA14_GPIO (34) // R3 -#define TEST_LCD_DATA15_GPIO (33) // R4 +#define TEST_LCD_PCLK_GPIO (3) +#define TEST_LCD_DATA0_GPIO (4) // B0 +#define TEST_LCD_DATA1_GPIO (5) // B1 +#define TEST_LCD_DATA2_GPIO (6) // B2 +#define TEST_LCD_DATA3_GPIO (7) // B3 +#define TEST_LCD_DATA4_GPIO (8) // B4 +#define TEST_LCD_DATA5_GPIO (9) // G0 +#define TEST_LCD_DATA6_GPIO (10) // G1 +#define TEST_LCD_DATA7_GPIO (11) // G2 +#define TEST_LCD_DATA8_GPIO (12) // G3 +#define TEST_LCD_DATA9_GPIO (13) // G4 +#define TEST_LCD_DATA10_GPIO (14) // G5 +#define TEST_LCD_DATA11_GPIO (15) // R0 +#define TEST_LCD_DATA12_GPIO (16) // R1 +#define TEST_LCD_DATA13_GPIO (17) // R2 +#define TEST_LCD_DATA14_GPIO (18) // R3 +#define TEST_LCD_DATA15_GPIO (19) // R4 #define TEST_LCD_DISP_EN_GPIO (-1) #if SOC_LCD_RGB_SUPPORTED - -#if !TEMPORARY_DISABLED_FOR_TARGETS(ESP32S3) -/* Not enough memory for framebuffer when running in default_2 config TODO IDF-3565 */ - +// RGB driver consumes a huge memory to save frame buffer, only test it with PSRAM enabled +#if CONFIG_SPIRAM_USE_MALLOC TEST_CASE("lcd rgb lcd panel", "[lcd]") { #define TEST_IMG_SIZE (100 * 100 * sizeof(uint16_t)) @@ -68,7 +66,7 @@ TEST_CASE("lcd rgb lcd panel", "[lcd]") TEST_LCD_DATA15_GPIO, }, .timings = { - .pclk_hz = 20000000, + .pclk_hz = 6000000, .h_res = TEST_LCD_H_RES, .v_res = TEST_LCD_V_RES, .hsync_back_porch = 43, @@ -78,6 +76,7 @@ TEST_CASE("lcd rgb lcd panel", "[lcd]") .vsync_front_porch = 1, .vsync_pulse_width = 1, }, + .flags.fb_in_psram = 1, }; // Test stream mode and one-off mode for (int i = 0; i < 2; i++) { @@ -145,7 +144,7 @@ TEST_CASE("lvgl gui with rgb interface", "[lcd][lvgl][ignore]") TEST_LCD_DATA15_GPIO, }, .timings = { - .pclk_hz = 20000000, + .pclk_hz = 6000000, .h_res = TEST_LCD_H_RES, .v_res = TEST_LCD_V_RES, .hsync_back_porch = 43, @@ -155,6 +154,7 @@ TEST_CASE("lvgl gui with rgb interface", "[lcd][lvgl][ignore]") .vsync_front_porch = 1, .vsync_pulse_width = 1, }, + .flags.fb_in_psram = 1, .on_frame_trans_done = notify_lvgl_ready_to_flush, .user_data = &disp, }; @@ -165,7 +165,5 @@ TEST_CASE("lvgl gui with rgb interface", "[lcd][lvgl][ignore]") test_lvgl_task_loop(panel_handle, TEST_LCD_H_RES, TEST_LCD_V_RES, &disp); } #endif // CONFIG_LV_USE_USER_DATA - -#endif //!TEMPORARY_DISABLED_FOR_TARGETS(ESP32S3) - +#endif // CONFIG_SPIRAM_USE_MALLOC #endif // SOC_LCD_RGB_SUPPORTED