feat(dsi_lcd): pixel clock frequency can be a float number

This commit is contained in:
suda-morris
2025-12-20 11:27:34 +08:00
parent 760d3baa2f
commit ecef7cdd61
5 changed files with 67 additions and 44 deletions
@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2023-2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@@ -30,8 +30,9 @@ typedef struct dsi_brg_dev_t *mipi_dsi_bridge_soc_handle_t;
typedef struct {
mipi_dsi_host_soc_handle_t host; /*!< Pointer to the host controller registers */
mipi_dsi_bridge_soc_handle_t bridge; /*!< Pointer to the bridge controller registers */
uint32_t lane_bit_rate_mbps; /*!< Lane bit rate in Mbps */
uint32_t dpi_clock_freq_mhz; /*!< DPI clock frequency in MHz */
float lane_bit_rate_mbps; /*!< Lane bit rate in Mbps */
float expect_dpi_clock_freq_mhz; /*!< Expected DPI clock frequency in MHz */
float real_dpi_clock_freq_mhz; /*!< Real DPI clock frequency in MHz */
} mipi_dsi_hal_context_t;
/**
@@ -39,7 +40,7 @@ typedef struct {
*/
typedef struct {
int bus_id; /*!< MIPI DSI bus ID, index from 0 */
uint32_t lane_bit_rate_mbps; /*!< Lane bit rate in Mbps */
float lane_bit_rate_mbps; /*!< Lane bit rate in Mbps */
uint8_t num_data_lanes; /*!< Number of data lanes */
} mipi_dsi_hal_config_t;
@@ -67,7 +68,7 @@ void mipi_dsi_hal_deinit(mipi_dsi_hal_context_t *hal);
* @param phy_clk_src_freq_hz PHY clock source frequency in Hz
* @param lane_bit_rate_mbps Lane bit rate in Mbps
*/
void mipi_dsi_hal_configure_phy_pll(mipi_dsi_hal_context_t *hal, uint32_t phy_clk_src_freq_hz, uint32_t lane_bit_rate_mbps);
void mipi_dsi_hal_configure_phy_pll(mipi_dsi_hal_context_t *hal, uint32_t phy_clk_src_freq_hz, float lane_bit_rate_mbps);
/**
* @brief Write a value to a PHY register via internal bus (so-called test interface)
@@ -175,7 +176,7 @@ void mipi_dsi_hal_host_dpi_set_vertical_timing(mipi_dsi_hal_context_t *hal, uint
* @param expect_dpi_clk_mhz Expected DPI clock frequency in MHz
* @return Divider value
*/
uint32_t mipi_dsi_hal_host_dpi_calculate_divider(mipi_dsi_hal_context_t *hal, uint32_t clk_src_mhz, uint32_t expect_dpi_clk_mhz);
uint32_t mipi_dsi_hal_host_dpi_calculate_divider(mipi_dsi_hal_context_t *hal, float clk_src_mhz, float expect_dpi_clk_mhz);
#ifdef __cplusplus
+51 -31
View File
@@ -1,11 +1,13 @@
/*
* SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2023-2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdlib.h>
#include <string.h>
#include <sys/param.h>
#include <math.h>
#include "hal/mipi_dsi_hal.h"
#include "hal/mipi_dsi_ll.h"
#include "hal/assert.h"
@@ -40,28 +42,28 @@ void mipi_dsi_hal_deinit(mipi_dsi_hal_context_t *hal)
hal->bridge = NULL;
}
void mipi_dsi_hal_configure_phy_pll(mipi_dsi_hal_context_t *hal, uint32_t phy_clk_src_freq_hz, uint32_t lane_bit_rate_mbps)
void mipi_dsi_hal_configure_phy_pll(mipi_dsi_hal_context_t *hal, uint32_t phy_clk_src_freq_hz, float lane_bit_rate_mbps)
{
// Formula: f_vco = M/N * f_ref
// where the M is Feedback Multiplication Ratio, N is Input Frequency Division Ratio
uint32_t ref_freq_mhz = phy_clk_src_freq_hz / 1000 / 1000;
uint32_t vco_freq_mhz = lane_bit_rate_mbps;
float ref_freq_mhz = (float)phy_clk_src_freq_hz / 1000.0f / 1000.0f;
float vco_freq_mhz = lane_bit_rate_mbps;
uint8_t pll_N = 1;
uint16_t pll_M = 0;
// 5MHz <= f_ref/N <= 40MHz
uint8_t min_N = MAX(1, ref_freq_mhz / 40);
uint8_t max_N = ref_freq_mhz / 5;
uint16_t min_delta = UINT16_MAX;
uint8_t min_N = MAX(1, (uint8_t)(ref_freq_mhz / 40.0f));
uint8_t max_N = (uint8_t)(ref_freq_mhz / 5.0f);
float min_delta = INFINITY;
for (uint8_t n = min_N; n <= max_N; n++) {
uint16_t m = vco_freq_mhz * n / ref_freq_mhz;
uint16_t m = (uint16_t)(vco_freq_mhz * n / ref_freq_mhz);
// M must be even number
if ((m & 0x01) == 0) {
uint16_t delta = vco_freq_mhz - ref_freq_mhz * m / n;
float delta = fabsf(vco_freq_mhz - ref_freq_mhz * m / n);
if (delta < min_delta) {
min_delta = delta;
pll_M = m;
pll_N = n;
if (min_delta == 0) {
if (min_delta < 0.01f) {
break;
}
}
@@ -86,9 +88,9 @@ void mipi_dsi_hal_configure_phy_pll(mipi_dsi_hal_context_t *hal, uint32_t phy_cl
mipi_dsi_hal_phy_write_register(hal, 0x18, ((pll_M - 1) & 0x1F));
mipi_dsi_hal_phy_write_register(hal, 0x18, 0x80 | (((pll_M - 1) >> 5) & 0x0F));
// update the real lane bit rate
hal->lane_bit_rate_mbps = ref_freq_mhz * pll_M / pll_N;
HAL_LOGD(TAG, "phy pll: ref=%" PRIu32 "Hz, lane_bit_rate=%" PRIu32 "Mbps, M=%" PRId16 ", N=%" PRId8 ", hsfreqrange=%" PRId8,
phy_clk_src_freq_hz, hal->lane_bit_rate_mbps, pll_M, pll_N, hs_freq_sel);
hal->lane_bit_rate_mbps = ref_freq_mhz * (float)pll_M / (float)pll_N;
HAL_LOGD(TAG, "phy pll: ref=%" PRIu32 "Hz, lane_bit_rate=%.2f Mbps, M=%" PRId16 ", N=%" PRId8 ", hsfreqrange=%" PRId8,
phy_clk_src_freq_hz, (double)hal->lane_bit_rate_mbps, pll_M, pll_N, hs_freq_sel);
}
void mipi_dsi_hal_phy_write_register(mipi_dsi_hal_context_t *hal, uint8_t reg_addr, uint8_t reg_val)
@@ -118,7 +120,7 @@ void mipi_dsi_hal_host_gen_write_dcs_command(mipi_dsi_hal_context_t *hal, uint8_
uint32_t payload_size = command_bytes + param_size;
// merge the command and some bytes of parameters into one 32-bit word
uint32_t temp = command & ((1 << (8 * command_bytes)) - 1);
uint32_t temp = command & ((1U << (8 * command_bytes)) - 1);
uint16_t merged_size = MIN(4 - command_bytes, param_size);
for (int i = 0; i < merged_size; i++) {
temp |= payload[i] << (8 * (i + command_bytes));
@@ -133,15 +135,17 @@ void mipi_dsi_hal_host_gen_write_dcs_command(mipi_dsi_hal_context_t *hal, uint8_
payload += merged_size;
uint32_t remain_size = param_size - merged_size;
while (remain_size >= 4) {
temp = *(uint32_t *)payload;
// use memcpy to avoid unaligned memory access
memcpy(&temp, payload, sizeof(uint32_t));
while (mipi_dsi_host_ll_gen_is_write_fifo_full(hal->host));
mipi_dsi_host_ll_gen_write_payload_fifo(hal->host, temp);
payload += 4;
remain_size -= 4;
}
if (remain_size) {
temp = *(uint32_t *)payload;
temp &= (1 << (8 * remain_size)) - 1;
temp = 0;
// use memcpy to avoid unaligned memory access and buffer over-read
memcpy(&temp, payload, remain_size);
while (mipi_dsi_host_ll_gen_is_write_fifo_full(hal->host));
mipi_dsi_host_ll_gen_write_payload_fifo(hal->host, temp);
}
@@ -178,15 +182,17 @@ void mipi_dsi_hal_host_gen_write_long_packet(mipi_dsi_hal_context_t *hal, uint8_
uint32_t remain_size = buffer_size;
uint32_t temp = 0;
while (remain_size >= 4) {
temp = *(uint32_t *)payload;
// use memcpy to avoid unaligned memory access
memcpy(&temp, payload, sizeof(uint32_t));
while (mipi_dsi_host_ll_gen_is_write_fifo_full(hal->host));
mipi_dsi_host_ll_gen_write_payload_fifo(hal->host, temp);
payload += 4;
remain_size -= 4;
}
if (remain_size) {
temp = *(uint32_t *)payload;
temp &= (1 << (8 * remain_size)) - 1;
temp = 0;
// use memcpy to avoid unaligned memory access and buffer over-read
memcpy(&temp, payload, remain_size);
while (mipi_dsi_host_ll_gen_is_write_fifo_full(hal->host));
mipi_dsi_host_ll_gen_write_payload_fifo(hal->host, temp);
}
@@ -227,19 +233,30 @@ void mipi_dsi_hal_host_gen_read_short_packet(mipi_dsi_hal_context_t *hal, uint8_
void mipi_dsi_hal_host_gen_read_dcs_command(mipi_dsi_hal_context_t *hal, uint8_t vc, uint32_t command, uint32_t command_bytes, void *ret_param, uint16_t param_buf_size)
{
uint16_t header_data = command & ((1 << (8 * command_bytes)) - 1);
uint16_t header_data = command & ((1U << (8 * command_bytes)) - 1);
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)
{
float dpi2lane_clk_ratio = (float)hal->lane_bit_rate_mbps / hal->dpi_clock_freq_mhz / 8;
mipi_dsi_host_ll_dpi_set_horizontal_timing(hal->host,
hsw * dpi2lane_clk_ratio,
hbp * dpi2lane_clk_ratio,
active_width * dpi2lane_clk_ratio,
hfp * dpi2lane_clk_ratio);
mipi_dsi_brg_ll_set_horizontal_timing(hal->bridge, hsw, hbp, active_width, hfp);
// set the horizontal timing based on user's expectation
uint32_t htotal = hsw + hbp + active_width + hfp;
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);
HAL_LOGD(TAG, "host video timing: hsw=%" PRIu32 ", hbp=%" PRIu32 ", act=%" PRIu32 ", hfp=%" PRIu32,
host_hsw, host_hbp, host_act + 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);
HAL_LOGD(TAG, "brg video timing: hsw=%" PRIu32 ", hbp=%" PRIu32 ", act=%" PRIu32 ", hfp=%" PRIu32,
hsw, hbp, active_width, hfp + compensation);
}
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)
@@ -248,9 +265,12 @@ void mipi_dsi_hal_host_dpi_set_vertical_timing(mipi_dsi_hal_context_t *hal, uint
mipi_dsi_brg_ll_set_vertical_timing(hal->bridge, vsw, vbp, active_height, vfp);
}
uint32_t mipi_dsi_hal_host_dpi_calculate_divider(mipi_dsi_hal_context_t *hal, uint32_t clk_src_mhz, uint32_t expect_dpi_clk_mhz)
uint32_t mipi_dsi_hal_host_dpi_calculate_divider(mipi_dsi_hal_context_t *hal, float clk_src_mhz, float expect_dpi_clk_mhz)
{
uint32_t div = clk_src_mhz / expect_dpi_clk_mhz;
hal->dpi_clock_freq_mhz = clk_src_mhz / div;
uint32_t div = (uint32_t)roundf(clk_src_mhz / expect_dpi_clk_mhz);
hal->expect_dpi_clock_freq_mhz = expect_dpi_clk_mhz;
hal->real_dpi_clock_freq_mhz = clk_src_mhz / (float)div;
HAL_LOGD(TAG, "dpi clk: src=%.2f MHz, expect=%.2f MHz, div=%" PRIu32 ", real=%.2f MHz",
clk_src_mhz, expect_dpi_clk_mhz, div, hal->real_dpi_clock_freq_mhz);
return div;
}
@@ -3,6 +3,7 @@
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <math.h>
#include "esp_lcd_mipi_dsi.h"
#include "esp_clk_tree.h"
#include "mipi_dsi_priv.h"
@@ -19,7 +20,7 @@ esp_err_t esp_lcd_new_dsi_bus(const esp_lcd_dsi_bus_config_t *bus_config, esp_lc
ESP_ERR_INVALID_ARG, TAG, "invalid number of data lanes %d", bus_config->num_data_lanes);
ESP_RETURN_ON_FALSE(bus_config->lane_bit_rate_mbps >= MIPI_DSI_LL_MIN_PHY_MBPS &&
bus_config->lane_bit_rate_mbps <= MIPI_DSI_LL_MAX_PHY_MBPS, ESP_ERR_INVALID_ARG, TAG,
"invalid lane bit rate %"PRIu32, bus_config->lane_bit_rate_mbps);
"invalid lane bit rate %.2f", bus_config->lane_bit_rate_mbps);
// we don't use an bus allocator here, because different DSI bus uses different PHY.
// And each PHY has its own associated PINs, which is not changeable.
@@ -114,9 +115,9 @@ esp_err_t esp_lcd_new_dsi_bus(const esp_lcd_dsi_bus_config_t *bus_config, esp_lc
mipi_dsi_host_ll_enable_tx_eotp(hal->host, true, false);
// Set the divider to get the Time Out clock, clock source is the high-speed byte clock
mipi_dsi_host_ll_set_timeout_clock_division(hal->host, bus_config->lane_bit_rate_mbps / 8 / MIPI_DSI_DEFAULT_TIMEOUT_CLOCK_FREQ_MHZ);
mipi_dsi_host_ll_set_timeout_clock_division(hal->host, (uint32_t)roundf(bus_config->lane_bit_rate_mbps / 8.0f / MIPI_DSI_DEFAULT_TIMEOUT_CLOCK_FREQ_MHZ));
// Set the divider to get the TX Escape clock, clock source is the high-speed byte clock
mipi_dsi_host_ll_set_escape_clock_division(hal->host, bus_config->lane_bit_rate_mbps / 8 / MIPI_DSI_DEFAULT_ESCAPE_CLOCK_FREQ_MHZ);
mipi_dsi_host_ll_set_escape_clock_division(hal->host, (uint32_t)roundf(bus_config->lane_bit_rate_mbps / 8.0f / MIPI_DSI_DEFAULT_ESCAPE_CLOCK_FREQ_MHZ));
// set the timeout intervals to zero, means to disable the timeout mechanism
mipi_dsi_host_ll_set_timeout_count(hal->host, 0, 0, 0, 0, 0, 0, 0);
// DSI host will wait indefinitely for a read response from the DSI device
+3 -2
View File
@@ -186,7 +186,7 @@ esp_err_t esp_lcd_new_panel_dpi(esp_lcd_dsi_bus_handle_t bus, const esp_lcd_dpi_
esp_lcd_dpi_panel_t *dpi_panel = NULL;
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, ESP_ERR_INVALID_ARG, TAG, "invalid DPI clock frequency %"PRIu32, panel_config->dpi_clock_freq_mhz);
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);
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
@@ -250,7 +250,8 @@ esp_err_t esp_lcd_new_panel_dpi(esp_lcd_dsi_bus_handle_t bus, const esp_lcd_dpi_
ESP_GOTO_ON_ERROR(esp_clk_tree_src_get_freq_hz(dpi_clk_src, ESP_CLK_TREE_SRC_FREQ_PRECISION_CACHED,
&dpi_clk_src_freq_hz), err, TAG, "get clock source frequency failed");
// divide the source clock to get the final DPI clock
uint32_t dpi_div = mipi_dsi_hal_host_dpi_calculate_divider(hal, dpi_clk_src_freq_hz / 1000 / 1000, panel_config->dpi_clock_freq_mhz);
float dpi_clk_src_freq_mhz = (float)dpi_clk_src_freq_hz / 1000.0f / 1000.0f;
uint32_t dpi_div = mipi_dsi_hal_host_dpi_calculate_divider(hal, dpi_clk_src_freq_mhz, panel_config->dpi_clock_freq_mhz);
ESP_GOTO_ON_ERROR(esp_clk_tree_enable_src((soc_module_clk_t)dpi_clk_src, true), err, TAG, "clock source enable failed");
// set the clock source, set the divider, and enable the dpi clock
PERIPH_RCC_ATOMIC() {
@@ -24,7 +24,7 @@ typedef struct {
int bus_id; /*!< Select which DSI controller, index from 0 */
uint8_t num_data_lanes; /*!< Number of data lanes, if set to 0, the driver will fallback to use maximum number of lanes */
mipi_dsi_phy_pllref_clock_source_t phy_clk_src; /*!< The clock source for the PHY PLL */
uint32_t lane_bit_rate_mbps; /*!< Lane bit rate in Mbps */
float lane_bit_rate_mbps; /*!< Lane bit rate in Mbps */
} esp_lcd_dsi_bus_config_t;
/**
@@ -83,7 +83,7 @@ esp_err_t esp_lcd_new_panel_io_dbi(esp_lcd_dsi_bus_handle_t bus, const esp_lcd_d
typedef struct {
uint8_t virtual_channel; /*!< Virtual channel ID, index from 0 */
mipi_dsi_dpi_clock_source_t dpi_clk_src; /*!< MIPI DSI DPI clock source */
uint32_t dpi_clock_freq_mhz; /*!< DPI clock frequency in MHz */
float dpi_clock_freq_mhz; /*!< Pixel clock frequency in MHz */
lcd_color_format_t in_color_format; /*!< Format of the input data (color space and pixel format),
which is the format stored in the frame buffer */
lcd_color_format_t out_color_format; /*!< Format of the output data (color space and pixel format),