Merge branch 'feature/rgb_lcd_fb_in_psram' into 'master'

RGB_LCD: support frame buffer in PSRAM

Closes IDF-3565

See merge request espressif/esp-idf!14555
This commit is contained in:
morris 2021-07-30 09:09:32 +00:00
commit 36cd038526
13 changed files with 1916 additions and 1136 deletions

View File

@ -2,7 +2,7 @@
#
# sourced into the "SPIRAM config" submenu for ESP32 or ESP32S2
# invisible option selected by ESP32_SPIRAM_SUPPORT || ESP32S2_SPIRAM_SUPPORT
# invisible option selected by ${target}_SPIRAM_SUPPORT
config SPIRAM
bool

View File

@ -1,5 +1,5 @@
COMPONENT_SRCDIRS := . port/$(IDF_TARGET)
COMPONENT_ADD_INCLUDEDIRS := . include port/$(IDF_TARGET)/ include/soc port/$(IDF_TARGET)/private_include
COMPONENT_ADD_INCLUDEDIRS := . include port/$(IDF_TARGET)/ include/soc include/soc/${IDF_TARGET} port/$(IDF_TARGET)/private_include
COMPONENT_ADD_LDFRAGMENTS := linker.lf
ifdef IS_BOOTLOADER_BUILD

View File

@ -91,6 +91,15 @@ void esp_spiram_writeback_cache(void);
*/
esp_err_t esp_spiram_reserve_dma_pool(size_t size);
/**
* @brief If SPI RAM(PSRAM) has been initialized
*
* @return
* - true SPI RAM has been initialized successfully
* - false SPI RAM hasn't been initialized or initialized failed
*/
bool esp_spiram_is_initialized(void);
#if CONFIG_SPIRAM_FETCH_INSTRUCTIONS
extern int _instruction_reserved_start, _instruction_reserved_end;

View File

@ -78,7 +78,14 @@ size_t esp_spiram_get_size(void);
*/
void esp_spiram_writeback_cache(void);
/**
* @brief If SPI RAM(PSRAM) has been initialized
*
* @return
* - true SPI RAM has been initialized successfully
* - false SPI RAM hasn't been initialized or initialized failed
*/
bool esp_spiram_is_initialized(void);
/**
* @brief Reserve a pool of internal memory for specific DMA/internal allocations
@ -91,6 +98,15 @@ void esp_spiram_writeback_cache(void);
*/
esp_err_t esp_spiram_reserve_dma_pool(size_t size);
/**
* @brief If SPI RAM(PSRAM) has been initialized
*
* @return
* - true SPI RAM has been initialized successfully
* - false SPI RAM hasn't been initialized or initialized failed
*/
bool esp_spiram_is_initialized(void);
#if CONFIG_SPIRAM_FETCH_INSTRUCTIONS
extern int _instruction_reserved_start, _instruction_reserved_end;

View File

@ -375,6 +375,15 @@ void IRAM_ATTR esp_spiram_writeback_cache(void)
Cache_WriteBack_All();
}
/**
* @brief If SPI RAM(PSRAM) has been initialized
*
* @return true SPI RAM has been initialized successfully
* @return false SPI RAM hasn't been initialized or initialized failed
*/
bool esp_spiram_is_initialized(void)
{
return spiram_inited;
}
#endif

View File

@ -329,4 +329,15 @@ void IRAM_ATTR esp_spiram_writeback_cache(void)
Cache_WriteBack_All();
}
/**
* @brief If SPI RAM(PSRAM) has been initialized
*
* @return true SPI RAM has been initialized successfully
* @return false SPI RAM hasn't been initialized or initialized failed
*/
bool esp_spiram_is_initialized(void)
{
return s_spiram_inited;
}
#endif

View File

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

View File

@ -128,7 +128,7 @@ esp_err_t esp_lcd_new_i80_bus(const esp_lcd_i80_bus_config_t *bus_config, esp_lc
// interrupt is disabled by default
int isr_flags = ESP_INTR_FLAG_INTRDISABLED;
ret = esp_intr_alloc_intrstatus(lcd_periph_signals.buses[bus_id].irq_id, isr_flags,
lcd_ll_get_interrupt_status_reg(bus->hal.dev),
(uint32_t)lcd_ll_get_interrupt_status_reg(bus->hal.dev),
LCD_LL_EVENT_TRANS_DONE, lcd_default_isr_handler, bus, &bus->intr);
ESP_GOTO_ON_ERROR(ret, no_int, TAG, "install interrupt failed");
lcd_ll_enable_interrupt(bus->hal.dev, LCD_LL_EVENT_TRANS_DONE, false); // disable all interrupts

View File

@ -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
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,9 +135,9 @@ 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,
lcd_ll_get_interrupt_status_reg(rgb_panel->hal.dev),
(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);
ESP_GOTO_ON_ERROR(ret, no_int, TAG, "install interrupt failed");
lcd_ll_enable_interrupt(rgb_panel->hal.dev, LCD_LL_EVENT_VSYNC_END, false); // disable all interrupts
@ -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);

View File

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

View File

@ -165,12 +165,10 @@ static inline void lcd_ll_set_command(lcd_cam_dev_t *dev, uint32_t data_width, u
{
// if command phase has two cycles, in the first cycle, command[15:0] is sent out via lcd_data_out[15:0]
// in the second cycle, command[31:16] is sent out via lcd_data_out[15:0]
// no matter the LCD is in 8bit mode or 16bit mode
// so this is a workaround especially for 8bit mode
if (data_width == 8) {
command = (command & 0xFF) | (command & 0xFF00) << 8;
}
dev->lcd_cmd_val = command;
dev->lcd_cmd_val.lcd_cmd_value = command;
}
static inline void lcd_ll_enable_rgb_mode(lcd_cam_dev_t *dev, bool en)
@ -252,9 +250,9 @@ static inline void lcd_ll_clear_interrupt_status(lcd_cam_dev_t *dev, uint32_t ma
dev->lc_dma_int_clr.val = mask & 0x03;
}
static inline uint32_t lcd_ll_get_interrupt_status_reg(lcd_cam_dev_t *dev)
static inline volatile void *lcd_ll_get_interrupt_status_reg(lcd_cam_dev_t *dev)
{
return (uint32_t)(&dev->lc_dma_int_st);
return &dev->lc_dma_int_st;
}
#ifdef __cplusplus

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff