mirror of
https://github.com/espressif/esp-idf.git
synced 2026-04-27 19:13:21 +00:00
feat(dsi_lcd): pixel clock frequency can be a float number
This commit is contained in:
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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),
|
||||
|
||||
Reference in New Issue
Block a user