diff --git a/components/esp_lcd/src/esp_lcd_rgb_panel.c b/components/esp_lcd/src/esp_lcd_rgb_panel.c index 1a6f11aa81..267bec453d 100644 --- a/components/esp_lcd/src/esp_lcd_rgb_panel.c +++ b/components/esp_lcd/src/esp_lcd_rgb_panel.c @@ -95,6 +95,7 @@ struct esp_rgb_panel_t { int x_gap; // Extra gap in x coordinate, it's used when calculate the flush window int y_gap; // Extra gap in y coordinate, it's used when calculate the flush window portMUX_TYPE spinlock; // to protect panel specific resource from concurrent access (e.g. between task and ISR) + int lcd_clk_flags; // LCD clock calculation flags struct { uint32_t disp_en_level: 1; // The level which can turn on the screen by `disp_gpio_num` uint32_t stream_mode: 1; // If set, the LCD transfers data continuously, otherwise, it stops refreshing the LCD when transaction done @@ -259,6 +260,11 @@ esp_err_t esp_lcd_new_rgb_panel(const esp_lcd_rgb_panel_config_t *rgb_panel_conf // set clock source ret = lcd_rgb_panel_select_clock_src(rgb_panel, rgb_panel_config->clk_src); ESP_GOTO_ON_ERROR(ret, err, TAG, "set source clock failed"); + // set minimal PCLK divider + // A limitation in the hardware, if the LCD_PCLK == LCD_CLK, then the PCLK polarity can't be adjustable + if (!(rgb_panel_config->timings.flags.pclk_active_neg || rgb_panel_config->timings.flags.pclk_idle_high)) { + rgb_panel->lcd_clk_flags |= LCD_HAL_PCLK_FLAG_ALLOW_EQUAL_SYSCLK; + } // install interrupt service, (LCD peripheral shares the interrupt source with Camera by different mask) int isr_flags = LCD_RGB_INTR_ALLOC_FLAGS | ESP_INTR_FLAG_SHARED; ret = esp_intr_alloc_intrstatus(lcd_periph_signals.panels[panel_id].irq_id, isr_flags, @@ -391,7 +397,7 @@ static esp_err_t rgb_panel_init(esp_lcd_panel_t *panel) esp_err_t ret = ESP_OK; esp_rgb_panel_t *rgb_panel = __containerof(panel, esp_rgb_panel_t, base); // set pixel clock frequency - rgb_panel->timings.pclk_hz = lcd_hal_cal_pclk_freq(&rgb_panel->hal, rgb_panel->src_clk_hz, rgb_panel->timings.pclk_hz); + rgb_panel->timings.pclk_hz = lcd_hal_cal_pclk_freq(&rgb_panel->hal, rgb_panel->src_clk_hz, rgb_panel->timings.pclk_hz, rgb_panel->lcd_clk_flags); // pixel clock phase and polarity lcd_ll_set_clock_idle_level(rgb_panel->hal.dev, rgb_panel->timings.flags.pclk_idle_high); lcd_ll_set_pixel_clock_edge(rgb_panel->hal.dev, rgb_panel->timings.flags.pclk_active_neg); @@ -795,7 +801,7 @@ IRAM_ATTR static void lcd_rgb_panel_try_update_pclk(esp_rgb_panel_t *rgb_panel) portENTER_CRITICAL_ISR(&rgb_panel->spinlock); if (unlikely(rgb_panel->flags.need_update_pclk)) { rgb_panel->flags.need_update_pclk = false; - rgb_panel->timings.pclk_hz = lcd_hal_cal_pclk_freq(&rgb_panel->hal, rgb_panel->src_clk_hz, rgb_panel->timings.pclk_hz); + rgb_panel->timings.pclk_hz = lcd_hal_cal_pclk_freq(&rgb_panel->hal, rgb_panel->src_clk_hz, rgb_panel->timings.pclk_hz, rgb_panel->lcd_clk_flags); } portEXIT_CRITICAL_ISR(&rgb_panel->spinlock); } diff --git a/components/hal/include/hal/lcd_hal.h b/components/hal/include/hal/lcd_hal.h index 0f0018e2e7..1312b5135e 100644 --- a/components/hal/include/hal/lcd_hal.h +++ b/components/hal/include/hal/lcd_hal.h @@ -7,6 +7,7 @@ #pragma once #include +#include #ifdef __cplusplus extern "C" { @@ -32,6 +33,8 @@ typedef struct { */ void lcd_hal_init(lcd_hal_context_t *hal, int id); +#define LCD_HAL_PCLK_FLAG_ALLOW_EQUAL_SYSCLK (1 << 0) + /** * @brief LCD PCLK clock calculation * @note Currently this function is only used by RGB LCD driver, I80 driver still uses a fixed clock division @@ -39,9 +42,10 @@ void lcd_hal_init(lcd_hal_context_t *hal, int id); * @param hal LCD HAL layer context * @param src_freq_hz LCD source clock frequency in Hz * @param expect_pclk_freq_hz Expected LCD PCLK frequency in Hz + * @param lcd_clk_flags Extra flags to control LCD PCLK clock calculation, supported flags are prefixed with LCD_HAL_PCLK_FLAG_ * @return Actual LCD PCLK frequency in Hz */ -uint32_t lcd_hal_cal_pclk_freq(lcd_hal_context_t *hal, uint32_t src_freq_hz, uint32_t expect_pclk_freq_hz); +uint32_t lcd_hal_cal_pclk_freq(lcd_hal_context_t *hal, uint32_t src_freq_hz, uint32_t expect_pclk_freq_hz, int lcd_clk_flags); #ifdef __cplusplus } diff --git a/components/hal/lcd_hal.c b/components/hal/lcd_hal.c index 73a19cff2c..08d332a794 100644 --- a/components/hal/lcd_hal.c +++ b/components/hal/lcd_hal.c @@ -20,6 +20,7 @@ void lcd_hal_init(lcd_hal_context_t *hal, int id) * @param b smaller value * @return result of gcd(a, b) */ +__attribute__((always_inline)) static inline uint32_t _gcd(uint32_t a, uint32_t b) { uint32_t c = a % b; @@ -31,16 +32,19 @@ static inline uint32_t _gcd(uint32_t a, uint32_t b) return b; } -uint32_t lcd_hal_cal_pclk_freq(lcd_hal_context_t *hal, uint32_t src_freq_hz, uint32_t expect_pclk_freq_hz) +uint32_t lcd_hal_cal_pclk_freq(lcd_hal_context_t *hal, uint32_t src_freq_hz, uint32_t expect_pclk_freq_hz, int lcd_clk_flags) { // lcd_clk = module_clock_src / (n + b / a) // pixel_clk = lcd_clk / mo uint32_t mo = src_freq_hz / expect_pclk_freq_hz / LCD_LL_CLK_FRAC_DIV_N_MAX + 1; + if (mo == 1 && !(lcd_clk_flags & LCD_HAL_PCLK_FLAG_ALLOW_EQUAL_SYSCLK)) { + mo = 2; + } uint32_t n = src_freq_hz / expect_pclk_freq_hz / mo; uint32_t a = 0; uint32_t b = 0; // delta_hz / expect_pclk_freq_hz <==> b / a - uint32_t delta_hz = src_freq_hz - expect_pclk_freq_hz * mo * n; + uint32_t delta_hz = src_freq_hz / mo - expect_pclk_freq_hz * n; // fractional divider if (delta_hz) { uint32_t gcd = _gcd(expect_pclk_freq_hz, delta_hz); @@ -52,7 +56,7 @@ uint32_t lcd_hal_cal_pclk_freq(lcd_hal_context_t *hal, uint32_t src_freq_hz, uin b /= d; } - HAL_LOGD("lcd_hal", "n=%d,a=%d,b=%d,mo=%d", n, a, b, mo); + HAL_EARLY_LOGD("lcd_hal", "n=%d,a=%d,b=%d,mo=%d", n, a, b, mo); lcd_ll_set_group_clock_coeff(hal->dev, n, a, b); lcd_ll_set_pixel_clock_prescale(hal->dev, mo);