feat(mipi_dsi): update low level functions to include underrun interrupt

This commit is contained in:
morris 2024-07-01 17:11:33 +08:00
parent e91c5e33ca
commit 28a3227b2f
5 changed files with 78 additions and 23 deletions

View File

@ -32,12 +32,12 @@ struct esp_lcd_dpi_panel_t {
esp_lcd_panel_t base; // Base class of generic lcd panel
esp_lcd_dsi_bus_handle_t bus; // DSI bus handle
uint8_t virtual_channel; // Virtual channel ID, index from 0
uint8_t cur_fb_index; // Current frame buffer index
uint8_t num_fbs; // Number of frame buffers
uint8_t cur_fb_index; // Current frame buffer index
uint8_t num_fbs; // Number of frame buffers
uint8_t *fbs[DPI_PANEL_MAX_FB_NUM]; // Frame buffers
uint32_t h_pixels; // Horizontal pixels
uint32_t v_pixels; // Vertical pixels
size_t frame_buffer_size; // Frame buffer size
size_t fb_size; // Frame buffer size, in bytes
size_t bits_per_pixel; // Bits per pixel
lcd_color_rgb_pixel_format_t pixel_format; // RGB Pixel format
dw_gdma_channel_handle_t dma_chan; // DMA channel
@ -126,7 +126,7 @@ static esp_err_t dpi_panel_create_dma_link(esp_lcd_dpi_panel_t *dpi_panel)
// create DMA link lists
dw_gdma_link_list_config_t link_list_config = {
.num_items = DPI_PANEL_LLI_PER_FRAME,
.num_items = DPI_PANEL_MIN_DMA_NODES_PER_LINK,
.link_type = DW_GDMA_LINKED_LIST_TYPE_SINGLY,
};
for (int i = 0; i < dpi_panel->num_fbs; i++) {
@ -191,21 +191,21 @@ esp_err_t esp_lcd_new_panel_dpi(esp_lcd_dsi_bus_handle_t bus, const esp_lcd_dpi_
uint32_t cache_line_size = cache_hal_get_cache_line_size(CACHE_LL_LEVEL_EXT_MEM, CACHE_TYPE_DATA);
// DMA doesn't have requirement on the buffer alignment, but the cache does
uint32_t alignment = cache_line_size;
size_t frame_buffer_size = panel_config->video_timing.h_size * panel_config->video_timing.v_size * bits_per_pixel / 8;
size_t fb_size = panel_config->video_timing.h_size * panel_config->video_timing.v_size * bits_per_pixel / 8;
uint8_t *frame_buffer = NULL;
for (int i = 0; i < num_fbs; i++) {
frame_buffer = heap_caps_aligned_calloc(alignment, 1, frame_buffer_size, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT);
frame_buffer = heap_caps_aligned_calloc(alignment, 1, fb_size, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT);
ESP_GOTO_ON_FALSE(frame_buffer, ESP_ERR_NO_MEM, err, TAG, "no memory for frame buffer");
dpi_panel->fbs[i] = frame_buffer;
ESP_LOGD(TAG, "fb[%d] @%p", i, frame_buffer);
// preset the frame buffer with black color
// the frame buffer address alignment is ensured by `heap_caps_aligned_calloc`
// while the value of the frame_buffer_size may not be aligned to the cache line size
// while the value of the fb_size may not be aligned to the cache line size
// but that's not a problem because the `heap_caps_aligned_calloc` internally allocated a buffer whose size is aligned up to the cache line size
ESP_GOTO_ON_ERROR(esp_cache_msync(frame_buffer, frame_buffer_size, ESP_CACHE_MSYNC_FLAG_DIR_C2M | ESP_CACHE_MSYNC_FLAG_UNALIGNED),
ESP_GOTO_ON_ERROR(esp_cache_msync(frame_buffer, fb_size, ESP_CACHE_MSYNC_FLAG_DIR_C2M | ESP_CACHE_MSYNC_FLAG_UNALIGNED),
err, TAG, "cache write back failed");
}
dpi_panel->frame_buffer_size = frame_buffer_size;
dpi_panel->fb_size = fb_size;
dpi_panel->bits_per_pixel = bits_per_pixel;
dpi_panel->h_pixels = panel_config->video_timing.h_size;
dpi_panel->v_pixels = panel_config->video_timing.v_size;
@ -282,6 +282,7 @@ esp_err_t esp_lcd_new_panel_dpi(esp_lcd_dsi_bus_handle_t bus, const esp_lcd_dpi_
mipi_dsi_brg_ll_set_underrun_discard_count(hal->bridge, panel_config->video_timing.h_size);
// use the DW_GDMA as the flow controller
mipi_dsi_brg_ll_set_flow_controller(hal->bridge, MIPI_DSI_LL_FLOW_CONTROLLER_DMA);
mipi_dsi_brg_ll_set_multi_block_number(hal->bridge, DPI_PANEL_MIN_DMA_NODES_PER_LINK);
mipi_dsi_brg_ll_set_burst_len(hal->bridge, 256);
mipi_dsi_brg_ll_set_empty_threshold(hal->bridge, 1024 - 256);
// enable DSI bridge
@ -381,7 +382,7 @@ static esp_err_t dpi_panel_init(esp_lcd_panel_t *panel)
.burst_len = 16,
.width = DW_GDMA_TRANS_WIDTH_64,
},
.size = dpi_panel->frame_buffer_size * 8 / 64,
.size = dpi_panel->fb_size * 8 / 64,
};
for (int i = 0; i < dpi_panel->num_fbs; i++) {
link_list = dpi_panel->link_lists[i];
@ -419,7 +420,7 @@ static esp_err_t dpi_panel_draw_bitmap(esp_lcd_panel_t *panel, int x_start, int
uint8_t cur_fb_index = dpi_panel->cur_fb_index;
uint8_t *frame_buffer = dpi_panel->fbs[cur_fb_index];
uint8_t *draw_buffer = (uint8_t *)color_data;
size_t frame_buffer_size = dpi_panel->frame_buffer_size;
size_t fb_size = dpi_panel->fb_size;
size_t bits_per_pixel = dpi_panel->bits_per_pixel;
// clip to boundaries
@ -434,11 +435,11 @@ static esp_err_t dpi_panel_draw_bitmap(esp_lcd_panel_t *panel, int x_start, int
uint8_t draw_buf_fb_index = 0;
// check if the user draw buffer resides in any frame buffer's memory range
// if so, we don't need to copy the data, just do cache write back
if (draw_buffer >= dpi_panel->fbs[0] && draw_buffer < dpi_panel->fbs[0] + frame_buffer_size) {
if (draw_buffer >= dpi_panel->fbs[0] && draw_buffer < dpi_panel->fbs[0] + fb_size) {
draw_buf_fb_index = 0;
} else if (draw_buffer >= dpi_panel->fbs[1] && draw_buffer < dpi_panel->fbs[1] + frame_buffer_size) {
} else if (draw_buffer >= dpi_panel->fbs[1] && draw_buffer < dpi_panel->fbs[1] + fb_size) {
draw_buf_fb_index = 1;
} else if (draw_buffer >= dpi_panel->fbs[2] && draw_buffer < dpi_panel->fbs[2] + frame_buffer_size) {
} else if (draw_buffer >= dpi_panel->fbs[2] && draw_buffer < dpi_panel->fbs[2] + fb_size) {
draw_buf_fb_index = 2;
} else {
do_copy = true;

View File

@ -29,9 +29,8 @@
#define DSI_MEM_ALLOC_CAPS MALLOC_CAP_DEFAULT
#endif
#define DPI_PANEL_MAX_FB_NUM 3 // maximum number of supported frame buffers for DPI panel
#define DPI_PANEL_LLI_PER_FRAME 1 // NOTE: we assume ONE DMA link item can carry the WHOLE image (1920*1080)
#define DPI_PANEL_MAX_FB_NUM 3 // maximum number of frame buffers that can be maintained by the driver
#define DPI_PANEL_MIN_DMA_NODES_PER_LINK 1 // NOTE: we assume 1 DMA link item can carry the WHOLE image
#ifdef __cplusplus
extern "C" {

View File

@ -222,16 +222,16 @@ TEST_CASE("MIPI DSI with multiple frame buffers (ILI9881C)", "[mipi_dsi]")
uint16_t *fbs[3];
TEST_ESP_OK(esp_lcd_dpi_panel_get_frame_buffer(mipi_dpi_panel, 3, (void **)&fbs[0], (void **)&fbs[1], (void **)&fbs[2]));
for (int i = 0; i < 3; i++) {
for (int i = 0; i < 9; i++) {
uint16_t color_byte = rand() & 0xFFFF;
int x_start = rand() % (MIPI_DSI_LCD_H_RES - 100);
int y_start = rand() % (MIPI_DSI_LCD_V_RES - 100);
for (int j = y_start; j < y_start + 100; j++) {
for (int k = x_start; k < x_start + 100; k++) {
fbs[i][j * MIPI_DSI_LCD_H_RES + k] = color_byte;
fbs[i % 3][j * MIPI_DSI_LCD_H_RES + k] = color_byte;
}
}
esp_lcd_panel_draw_bitmap(mipi_dpi_panel, x_start, y_start, x_start + 100, y_start + 100, fbs[i]);
esp_lcd_panel_draw_bitmap(mipi_dpi_panel, x_start, y_start, x_start + 100, y_start + 100, fbs[i % 3]);
vTaskDelay(pdMS_TO_TICKS(1000));
}

View File

@ -14,6 +14,7 @@
#include "hal/lcd_types.h"
#define MIPI_DSI_LL_GET_BRG(bus_id) (bus_id == 0 ? &MIPI_DSI_BRIDGE : NULL)
#define MIPI_DSI_LL_EVENT_UNDERRUN (1 << 0)
#ifdef __cplusplus
extern "C" {
@ -35,6 +36,46 @@ static inline void mipi_dsi_brg_ll_enable(dsi_brg_dev_t *dev, bool en)
dev->en.dsi_en = en;
}
/**
* @brief Enable DSI bridge interrupt for specific event mask
*
* @param dev Pointer to the DSI bridge controller register base address
* @param mask Event mask
* @param enable True to enable, False to disable
*/
static inline void mipi_dsi_brg_ll_enable_interrupt(dsi_brg_dev_t *dev, uint32_t mask, bool enable)
{
if (enable) {
dev->int_ena.val |= mask;
} else {
dev->int_ena.val &= ~mask;
}
}
/**
* @brief Clear DSI bridge interrupt for specific event mask
*
* @param dev Pointer to the DSI bridge controller register base address
* @param mask Event mask
*/
__attribute__((always_inline))
static inline void mipi_dsi_brg_ll_clear_interrupt_status(dsi_brg_dev_t *dev, uint32_t mask)
{
dev->int_clr.val = mask;
}
/**
* @brief Get interrupt status for DSI bridge
*
* @param dev Pointer to the DSI bridge controller register base address
* @return Interrupt status
*/
__attribute__((always_inline))
static inline uint32_t mipi_dsi_brg_ll_get_interrupt_status(dsi_brg_dev_t *dev)
{
return dev->int_st.val;
}
/**
* @brief Set the number of 64-bit words in one dma burst transfer
*
@ -242,7 +283,7 @@ static inline void mipi_dsi_brg_ll_enable_ref_clock(dsi_brg_dev_t *dev, bool en)
* @param dev Pointer to the DSI bridge controller register base address
* @param controller Flow controller
*/
static inline void mipi_dsi_brg_ll_set_flow_controller(dsi_brg_dev_t* dev, mipi_dsi_ll_flow_controller_t controller)
static inline void mipi_dsi_brg_ll_set_flow_controller(dsi_brg_dev_t *dev, mipi_dsi_ll_flow_controller_t controller)
{
dev->dma_flow_ctrl.dsi_dma_flow_controller = controller;
}
@ -255,9 +296,21 @@ static inline void mipi_dsi_brg_ll_set_flow_controller(dsi_brg_dev_t* dev, mipi_
* @param dev Pointer to the DSI bridge controller register base address
* @param number Number of blocks
*/
static inline void mipi_dsi_brg_ll_set_multi_block_number(dsi_brg_dev_t* dev, uint32_t number)
static inline void mipi_dsi_brg_ll_set_multi_block_number(dsi_brg_dev_t *dev, uint32_t number)
{
dev->dma_flow_ctrl.dma_flow_multiblk_num = number;
dev->dma_frame_interval.dma_multiblk_en = number > 1;
}
/**
* @brief Get the FIFO depth of the DSI bridge
*
* @param dev Pointer to the DSI bridge controller register base address
* @return FIFO depth
*/
static inline uint32_t mipi_dsi_brg_ll_get_fifo_depth(dsi_brg_dev_t *dev)
{
return dev->fifo_flow_status.raw_buf_depth;
}
/**
@ -266,7 +319,7 @@ static inline void mipi_dsi_brg_ll_set_multi_block_number(dsi_brg_dev_t* dev, ui
* @param dev Pointer to the DSI bridge controller register base address
* @param std YUV-RGB conversion standard
*/
static inline void mipi_dsi_brg_ll_set_yuv_convert_std(dsi_brg_dev_t* dev, lcd_yuv_conv_std_t std)
static inline void mipi_dsi_brg_ll_set_yuv_convert_std(dsi_brg_dev_t *dev, lcd_yuv_conv_std_t std)
{
switch (std) {
case LCD_YUV_CONV_STD_BT601:

View File

@ -59,7 +59,9 @@
#define DR_REG_DDRPHY_BASE (DR_REG_HPPERIPH0_BASE + 0x9D000)
#define DR_REG_PVT_BASE (DR_REG_HPPERIPH0_BASE + 0x9E000)
#define DR_REG_CSI_HOST_BASE (DR_REG_HPPERIPH0_BASE + 0x9F000)
#define DR_REG_CSI_BRG_BASE (DR_REG_HPPERIPH0_BASE + 0x9F800)
#define DR_REG_DSI_HOST_BASE (DR_REG_HPPERIPH0_BASE + 0xA0000)
#define DR_REG_DSI_BRG_BASE (DR_REG_HPPERIPH0_BASE + 0xA0800)
#define DR_REG_ISP_BASE (DR_REG_HPPERIPH0_BASE + 0xA1000)
#define DR_REG_RMT_BASE (DR_REG_HPPERIPH0_BASE + 0xA2000)
#define DR_REG_BITSCRAM_BASE (DR_REG_HPPERIPH0_BASE + 0xA3000)