feat(lcd): add MIPI-to-HDMI example (lt9611)

This commit is contained in:
Chen Jichang
2026-09-16 14:21:25 +08:00
parent 71474016ec
commit 345d707b21
20 changed files with 637 additions and 32 deletions
@@ -9,6 +9,7 @@
#include <stdbool.h>
#include <stdint.h>
#include "hal/assert.h"
#include "soc/mipi_dsi_bridge_reg.h"
#include "soc/mipi_dsi_bridge_struct.h"
#include "hal/mipi_dsi_types.h"
#include "hal/lcd_types.h"
@@ -16,6 +17,8 @@
#define MIPI_DSI_LL_GET_BRG(bus_id) (bus_id == 0 ? &MIPI_DSI_BRIDGE : NULL)
#define MIPI_DSI_BRG_HTOTAL_MAX DSI_BRG_HTOTAL_V
#define MIPI_DSI_BRG_LL_EVENT_UNDERRUN (1 << 0)
#if HAL_CONFIG(CHIP_SUPPORT_MIN_REV) >= 300
#define MIPI_DSI_BRG_LL_EVENT_VSYNC (1 << 1)
@@ -212,7 +215,7 @@ static inline void mipi_dsi_brg_ll_set_horizontal_timing(dsi_brg_dev_t *dev, uin
* @brief Set the under run discard count for the bridge controller
*
* @param dev Pointer to the DSI bridge controller register base address
* @param under_run_discard_count Under run discard count
* @param under_run_discard_count Trigger underrun interrupt when underrun occurs and line_cnt is less than this field.
*/
static inline void mipi_dsi_brg_ll_set_underrun_discard_count(dsi_brg_dev_t *dev, uint32_t under_run_discard_count)
{
@@ -51,15 +51,6 @@ typedef enum {
MIPI_DSI_LL_COLOR_CODE_24BIT = 5, // 24-bit
} mipi_dsi_ll_color_coding_t;
/**
* @brief MIPI DSI Video mode burst type
*/
typedef enum {
MIPI_DSI_LL_VIDEO_NON_BURST_WITH_SYNC_PULSES, // Non-burst mode with sync pulses
MIPI_DSI_LL_VIDEO_NON_BURST_WITH_SYNC_EVENTS, // Non-burst mode with sync events
MIPI_DSI_LL_VIDEO_BURST_WITH_SYNC_PULSES, // Burst mode with sync pulses
} mipi_dsi_ll_video_burst_type_t;
/**
* @brief Set the DSI Host controller power state
*
@@ -284,11 +275,25 @@ static inline void mipi_dsi_host_ll_dpi_enable_lp_command(dsi_host_dev_t *dev, b
* @brief Set MIPI DSI video burst type
*
* @param dev Pointer to the DSI Host controller register base address
* @param mode Video mode type
* @param type Video burst type
*/
static inline void mipi_dsi_host_ll_dpi_set_video_burst_type(dsi_host_dev_t *dev, mipi_dsi_ll_video_burst_type_t type)
static inline void mipi_dsi_host_ll_dpi_set_video_burst_type(dsi_host_dev_t *dev, mipi_dsi_video_burst_type_t type)
{
dev->vid_mode_cfg.vid_mode_type = type;
// Public enum order is not the DSI Host VID_MODE_TYPE encoding.
switch (type) {
case MIPI_DSI_VIDEO_NON_BURST_WITH_SYNC_PULSES:
dev->vid_mode_cfg.vid_mode_type = 0;
break;
case MIPI_DSI_VIDEO_NON_BURST_WITH_SYNC_EVENTS:
dev->vid_mode_cfg.vid_mode_type = 1;
break;
case MIPI_DSI_VIDEO_BURST_WITH_SYNC_PULSES:
dev->vid_mode_cfg.vid_mode_type = 2;
break;
default:
HAL_ASSERT(false);
break;
}
}
/**
@@ -154,8 +154,9 @@ bool mipi_dsi_hal_host_gen_read_short_packet(mipi_dsi_hal_context_t *hal, uint8_
* @param hbp Horizontal Back Porch
* @param active_width Active Width
* @param hfp Horizontal Front Porch
* @return True if the compensated timing is valid, otherwise false
*/
void mipi_dsi_hal_host_dpi_set_horizontal_timing(mipi_dsi_hal_context_t *hal, uint32_t hsw, uint32_t hbp, uint32_t active_width, uint32_t hfp);
bool mipi_dsi_hal_host_dpi_set_horizontal_timing(mipi_dsi_hal_context_t *hal, uint32_t hsw, uint32_t hbp, uint32_t active_width, uint32_t hfp);
/**
* @brief Set vertical timing parameters for DPI
@@ -58,6 +58,19 @@ typedef enum {
MIPI_DSI_PATTERN_BER_VERTICAL, /*!< Vertical Bit Error Rate(BER) pattern */
} mipi_dsi_pattern_type_t;
/**
* @brief MIPI DSI DPI video burst type
*
* Selects how pixel packets are scheduled in DPI video mode:
* burst vs non-burst, and sync pulses vs sync events.
*/
typedef enum {
MIPI_DSI_VIDEO_BURST_WITH_SYNC_PULSES, /*!< Burst mode with sync pulses */
MIPI_DSI_VIDEO_NON_BURST_WITH_SYNC_PULSES, /*!< Non-burst mode with sync pulses */
MIPI_DSI_VIDEO_NON_BURST_WITH_SYNC_EVENTS, /*!< Non-burst mode with sync events */
MIPI_DSI_VIDEO_BURST_TYPE_MAX, /*!< Number of burst types, used for parameter check */
} mipi_dsi_video_burst_type_t;
#if SOC_MIPI_DSI_SUPPORTED
/**
+20 -7
View File
@@ -256,26 +256,39 @@ bool mipi_dsi_hal_host_gen_read_dcs_command(mipi_dsi_hal_context_t *hal, uint8_t
return mipi_dsi_hal_host_gen_read_short_packet(hal, vc, MIPI_DSI_DT_DCS_READ_0, header_data, ret_param, param_buf_size);
}
void mipi_dsi_hal_host_dpi_set_horizontal_timing(mipi_dsi_hal_context_t *hal, uint32_t hsw, uint32_t hbp, uint32_t active_width, uint32_t hfp)
bool mipi_dsi_hal_host_dpi_set_horizontal_timing(mipi_dsi_hal_context_t *hal, uint32_t hsw, uint32_t hbp, uint32_t active_width, uint32_t hfp)
{
// set the horizontal timing based on user's expectation
uint32_t htotal = hsw + hbp + active_width + hfp;
int compensation = (int)roundf(hal->real_dpi_clock_freq_mhz / hal->expect_dpi_clock_freq_mhz * htotal) - (int)htotal;
int compensated_hfp = (int)hfp + compensation;
int compensated_htotal = (int)htotal + compensation;
if (compensated_hfp <= 0 || compensated_htotal > MIPI_DSI_BRG_HTOTAL_MAX) {
HAL_LOGE(TAG, "invalid compensated timing: hfp=%d, htotal=%d", compensated_hfp, compensated_htotal);
return false;
}
if (fabsf(hal->real_dpi_clock_freq_mhz - hal->expect_dpi_clock_freq_mhz) > 0.01f) {
HAL_LOGW(TAG, "DPI clock adjusted from %.2f MHz to %.2f MHz, compensated hfp=%d, compensated htotal=%d",
hal->expect_dpi_clock_freq_mhz, hal->real_dpi_clock_freq_mhz,
compensated_hfp, compensated_htotal);
}
float dpi2lane_clk_ratio = hal->lane_bit_rate_mbps / hal->expect_dpi_clock_freq_mhz / 8.0f;
uint32_t host_hsw = (uint32_t)roundf(hsw * dpi2lane_clk_ratio);
uint32_t host_hbp = (uint32_t)roundf(hbp * dpi2lane_clk_ratio);
uint32_t host_act = (uint32_t)roundf(active_width * dpi2lane_clk_ratio);
uint32_t host_hfp = (uint32_t)roundf(hfp * dpi2lane_clk_ratio);
uint32_t host_htotal = (uint32_t)roundf(htotal * dpi2lane_clk_ratio);
int compensation = (int)host_htotal - (int)(host_hsw + host_hbp + host_act + host_hfp);
mipi_dsi_host_ll_dpi_set_horizontal_timing(hal->host, host_hsw, host_hbp, host_act + compensation, host_hfp);
int host_compensation = (int)host_htotal - (int)(host_hsw + host_hbp + host_act + host_hfp);
mipi_dsi_host_ll_dpi_set_horizontal_timing(hal->host, host_hsw, host_hbp, host_act + host_compensation, host_hfp);
HAL_LOGD(TAG, "host video timing: hsw=%" PRIu32 ", hbp=%" PRIu32 ", act=%" PRIu32 ", hfp=%" PRIu32,
host_hsw, host_hbp, host_act + compensation, host_hfp);
host_hsw, host_hbp, host_act + host_compensation, host_hfp);
// compensate the video timing to achieve the same refresh rate as expected
compensation = (int)roundf(hal->real_dpi_clock_freq_mhz / hal->expect_dpi_clock_freq_mhz * htotal) - (int)htotal;
mipi_dsi_brg_ll_set_horizontal_timing(hal->bridge, hsw, hbp, active_width, hfp + compensation);
mipi_dsi_brg_ll_set_horizontal_timing(hal->bridge, hsw, hbp, active_width, compensated_hfp);
HAL_LOGD(TAG, "brg video timing: hsw=%" PRIu32 ", hbp=%" PRIu32 ", act=%" PRIu32 ", hfp=%" PRIu32,
hsw, hbp, active_width, hfp + compensation);
hsw, hbp, active_width, (uint32_t)compensated_hfp);
return true;
}
void mipi_dsi_hal_host_dpi_set_vertical_timing(mipi_dsi_hal_context_t *hal, uint32_t vsw, uint32_t vbp, uint32_t active_height, uint32_t vfp)
+16 -10
View File
@@ -187,6 +187,7 @@ esp_err_t esp_lcd_new_panel_dpi(esp_lcd_dsi_bus_handle_t bus, const esp_lcd_dpi_
ESP_RETURN_ON_FALSE(bus && panel_config && ret_panel, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
ESP_RETURN_ON_FALSE(panel_config->virtual_channel < 4, ESP_ERR_INVALID_ARG, TAG, "invalid virtual channel %d", panel_config->virtual_channel);
ESP_RETURN_ON_FALSE(panel_config->dpi_clock_freq_mhz > 0, ESP_ERR_INVALID_ARG, TAG, "invalid DPI clock frequency %.2f", panel_config->dpi_clock_freq_mhz);
ESP_RETURN_ON_FALSE(panel_config->video_burst_type < MIPI_DSI_VIDEO_BURST_TYPE_MAX, ESP_ERR_INVALID_ARG, TAG, "invalid DPI video burst type");
size_t num_fbs = panel_config->num_fbs;
// if the user doesn't specify the number of frame buffers, then fallback to use one frame buffer
@@ -298,27 +299,32 @@ esp_err_t esp_lcd_new_panel_dpi(esp_lcd_dsi_bus_handle_t bus, const esp_lcd_dpi_
// commands are transmitted in low-power mode
mipi_dsi_host_ll_dpi_enable_lp_command(hal->host, true);
}
// after sending a frame, the DSI device should return an ack
mipi_dsi_host_ll_dpi_enable_frame_ack(hal->host, true);
// using the burst mode because it's energy-efficient
mipi_dsi_host_ll_dpi_set_video_burst_type(hal->host, MIPI_DSI_LL_VIDEO_BURST_WITH_SYNC_PULSES);
// configure the size of the active lin period, measured in pixels
// Some bridge devices don't return a frame acknowledgement.
mipi_dsi_host_ll_dpi_enable_frame_ack(hal->host, !panel_config->flags.disable_frame_ack);
mipi_dsi_host_ll_dpi_set_video_burst_type(hal->host, panel_config->video_burst_type);
// configure the size of the active line period, measured in pixels
mipi_dsi_host_ll_dpi_set_video_packet_pixel_num(hal->host, panel_config->video_timing.h_size);
// disable multi-packets
mipi_dsi_host_ll_dpi_set_trunks_num(hal->host, 0);
// disable "null packets"
mipi_dsi_host_ll_dpi_set_null_packet_size(hal->host, 0);
// set horizontal and vertical timing configuration
mipi_dsi_hal_host_dpi_set_horizontal_timing(hal, panel_config->video_timing.hsync_pulse_width,
panel_config->video_timing.hsync_back_porch,
panel_config->video_timing.h_size,
panel_config->video_timing.hsync_front_porch);
ESP_GOTO_ON_FALSE(mipi_dsi_hal_host_dpi_set_horizontal_timing(hal, panel_config->video_timing.hsync_pulse_width,
panel_config->video_timing.hsync_back_porch,
panel_config->video_timing.h_size,
panel_config->video_timing.hsync_front_porch),
ESP_ERR_INVALID_ARG, err, TAG, "invalid compensated horizontal timing");
mipi_dsi_hal_host_dpi_set_vertical_timing(hal, panel_config->video_timing.vsync_pulse_width,
panel_config->video_timing.vsync_back_porch,
panel_config->video_timing.v_size,
panel_config->video_timing.vsync_front_porch);
mipi_dsi_brg_ll_set_num_pixel_bits(hal->bridge, panel_config->video_timing.h_size * panel_config->video_timing.v_size * bits_per_pixel);
mipi_dsi_brg_ll_set_underrun_discard_count(hal->bridge, panel_config->video_timing.h_size);
// Report underruns for the whole frame: IRQ when underrun occurs and line_cnt < vtotal.
mipi_dsi_brg_ll_set_underrun_discard_count(hal->bridge,
panel_config->video_timing.vsync_pulse_width +
panel_config->video_timing.vsync_back_porch +
panel_config->video_timing.v_size +
panel_config->video_timing.vsync_front_porch);
// set the in/out color formats in the DSI bridge
mipi_dsi_brg_ll_set_input_color_format(hal->bridge, in_color_format);
mipi_dsi_brg_ll_set_output_color_format(hal->bridge, out_color_format, 0);
@@ -94,10 +94,12 @@ typedef struct {
which is the format that the panel can accept */
uint8_t num_fbs; /*!< Number of screen-sized frame buffers that allocated by the driver
By default (set to either 0 or 1) only one frame buffer will be created */
mipi_dsi_video_burst_type_t video_burst_type; /*!< DPI video burst type */
esp_lcd_video_timing_t video_timing; /*!< Video timing */
/// Extra configuration flags for MIPI DSI DPI panel
struct extra_dpi_panel_flags {
uint32_t disable_lp: 1;/*!< Disable low-power for DPI */
uint32_t disable_lp: 1; /*!< Disable low-power for DPI */
uint32_t disable_frame_ack: 1; /*!< Disable the frame acknowledgement request */
} flags; /*!< Extra configuration flags */
} esp_lcd_dpi_panel_config_t;