diff --git a/components/esp_lcd/dsi/esp_lcd_panel_dpi.c b/components/esp_lcd/dsi/esp_lcd_panel_dpi.c index e3d0e8a8b9..e4fcdc7e54 100644 --- a/components/esp_lcd/dsi/esp_lcd_panel_dpi.c +++ b/components/esp_lcd/dsi/esp_lcd_panel_dpi.c @@ -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; diff --git a/components/esp_lcd/dsi/mipi_dsi_priv.h b/components/esp_lcd/dsi/mipi_dsi_priv.h index eebf93e2e8..bb6fd2fe63 100644 --- a/components/esp_lcd/dsi/mipi_dsi_priv.h +++ b/components/esp_lcd/dsi/mipi_dsi_priv.h @@ -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" { diff --git a/components/esp_lcd/test_apps/mipi_dsi_lcd/main/test_mipi_dsi_panel.c b/components/esp_lcd/test_apps/mipi_dsi_lcd/main/test_mipi_dsi_panel.c index eec61a14b7..a31d26e01f 100644 --- a/components/esp_lcd/test_apps/mipi_dsi_lcd/main/test_mipi_dsi_panel.c +++ b/components/esp_lcd/test_apps/mipi_dsi_lcd/main/test_mipi_dsi_panel.c @@ -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)); } diff --git a/components/hal/esp32p4/include/hal/mipi_dsi_brg_ll.h b/components/hal/esp32p4/include/hal/mipi_dsi_brg_ll.h index 7c399550bb..adfdacad70 100644 --- a/components/hal/esp32p4/include/hal/mipi_dsi_brg_ll.h +++ b/components/hal/esp32p4/include/hal/mipi_dsi_brg_ll.h @@ -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: diff --git a/components/soc/esp32p4/include/soc/reg_base.h b/components/soc/esp32p4/include/soc/reg_base.h index 4c2bbc2c7e..9a4b6fc065 100644 --- a/components/soc/esp32p4/include/soc/reg_base.h +++ b/components/soc/esp32p4/include/soc/reg_base.h @@ -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)