lcd: support putting RGB frame buffer in PSRAM

pull/7365/head
morris 2021-07-23 11:04:35 +08:00
rodzic 21067a0455
commit dc1d14a37f
3 zmienionych plików z 69 dodań i 39 usunięć

Wyświetl plik

@ -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;

Wyświetl plik

@ -10,6 +10,7 @@
#include <sys/cdefs.h>
#include <sys/param.h>
#include <string.h>
#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
// 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);

Wyświetl plik

@ -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