mirror of
https://github.com/espressif/esp-idf.git
synced 2024-10-05 20:47:46 -04:00
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:
commit
36cd038526
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
Loading…
Reference in New Issue
Block a user