From 00f21c37fe0e78b342bdc7cd6d8f217d5d7edf55 Mon Sep 17 00:00:00 2001 From: morris Date: Fri, 20 Dec 2024 16:46:10 +0800 Subject: [PATCH] feat(lcd): increase the upper limit of pclk frequency for RGB LCD --- components/esp_lcd/rgb/esp_lcd_panel_rgb.c | 10 ++-------- components/hal/esp32p4/include/hal/lcd_ll.h | 10 +++------- components/hal/esp32s3/include/hal/lcd_ll.h | 10 +++++----- components/hal/include/hal/lcd_hal.h | 7 ++----- components/hal/lcd_hal.c | 21 ++++++++++++++------- 5 files changed, 26 insertions(+), 32 deletions(-) diff --git a/components/esp_lcd/rgb/esp_lcd_panel_rgb.c b/components/esp_lcd/rgb/esp_lcd_panel_rgb.c index 25cb52db28..800b13d6b0 100644 --- a/components/esp_lcd/rgb/esp_lcd_panel_rgb.c +++ b/components/esp_lcd/rgb/esp_lcd_panel_rgb.c @@ -133,7 +133,6 @@ 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 int rotate_mask; // panel rotate_mask mask, Or'ed of `panel_rotate_mask_t` struct { uint32_t disp_en_level: 1; // The level which can turn on the screen by `disp_gpio_num` @@ -326,11 +325,6 @@ esp_err_t esp_lcd_new_rgb_panel(const esp_lcd_rgb_panel_config_t *rgb_panel_conf // reset peripheral and FIFO after we select a correct clock source lcd_ll_fifo_reset(rgb_panel->hal.dev); lcd_ll_reset(rgb_panel->hal.dev); - // 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 | ESP_INTR_FLAG_LOWMED; ret = esp_intr_alloc_intrstatus(lcd_periph_rgb_signals.panels[panel_id].irq_id, isr_flags, @@ -542,7 +536,7 @@ static esp_err_t rgb_panel_init(esp_lcd_panel_t *panel) esp_rgb_panel_t *rgb_panel = __containerof(panel, esp_rgb_panel_t, base); // set pixel clock frequency hal_utils_clk_div_t lcd_clk_div = {}; - 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, &lcd_clk_div); + rgb_panel->timings.pclk_hz = lcd_hal_cal_pclk_freq(&rgb_panel->hal, rgb_panel->src_clk_hz, rgb_panel->timings.pclk_hz, &lcd_clk_div); LCD_CLOCK_SRC_ATOMIC() { lcd_ll_set_group_clock_coeff(rgb_panel->hal.dev, lcd_clk_div.integer, lcd_clk_div.denominator, lcd_clk_div.numerator); } @@ -1112,7 +1106,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->lcd_clk_flags, &lcd_clk_div); + rgb_panel->timings.pclk_hz = lcd_hal_cal_pclk_freq(&rgb_panel->hal, rgb_panel->src_clk_hz, rgb_panel->timings.pclk_hz, &lcd_clk_div); LCD_CLOCK_SRC_ATOMIC() { lcd_ll_set_group_clock_coeff(rgb_panel->hal.dev, lcd_clk_div.integer, lcd_clk_div.denominator, lcd_clk_div.numerator); } diff --git a/components/hal/esp32p4/include/hal/lcd_ll.h b/components/hal/esp32p4/include/hal/lcd_ll.h index 99e41cfc68..3bad577486 100644 --- a/components/hal/esp32p4/include/hal/lcd_ll.h +++ b/components/hal/esp32p4/include/hal/lcd_ll.h @@ -130,12 +130,8 @@ __attribute__((always_inline)) static inline void lcd_ll_set_group_clock_coeff(lcd_cam_dev_t *dev, int div_num, int div_a, int div_b) { // lcd_clk = module_clock_src / (div_num + div_b / div_a) - HAL_ASSERT(div_num >= 2 && div_num <= LCD_LL_CLK_FRAC_DIV_N_MAX); - // dic_num == 0 means LCD_LL_CLK_FRAC_DIV_N_MAX divider in hardware - if (div_num >= LCD_LL_CLK_FRAC_DIV_N_MAX) { - div_num = 0; - } - HAL_FORCE_MODIFY_U32_REG_FIELD(HP_SYS_CLKRST.peri_clk_ctrl110, reg_lcd_clk_div_num, div_num); + HAL_ASSERT(div_num > 0 && div_num <= LCD_LL_CLK_FRAC_DIV_N_MAX); + HAL_FORCE_MODIFY_U32_REG_FIELD(HP_SYS_CLKRST.peri_clk_ctrl110, reg_lcd_clk_div_num, div_num - 1); HAL_FORCE_MODIFY_U32_REG_FIELD(HP_SYS_CLKRST.peri_clk_ctrl110, reg_lcd_clk_div_denominator, div_a); HAL_FORCE_MODIFY_U32_REG_FIELD(HP_SYS_CLKRST.peri_clk_ctrl110, reg_lcd_clk_div_numerator, div_b); } @@ -177,7 +173,7 @@ static inline void lcd_ll_set_pixel_clock_edge(lcd_cam_dev_t *dev, bool active_o __attribute__((always_inline)) static inline void lcd_ll_set_pixel_clock_prescale(lcd_cam_dev_t *dev, uint32_t prescale) { - HAL_ASSERT(prescale <= LCD_LL_PCLK_DIV_MAX); + HAL_ASSERT(prescale > 0 && prescale <= LCD_LL_PCLK_DIV_MAX); // Formula: pixel_clk = lcd_clk / (1 + clkcnt_n) // clkcnt_n can't be zero uint32_t scale = 1; diff --git a/components/hal/esp32s3/include/hal/lcd_ll.h b/components/hal/esp32s3/include/hal/lcd_ll.h index 7e46542a37..8ef0a32626 100644 --- a/components/hal/esp32s3/include/hal/lcd_ll.h +++ b/components/hal/esp32s3/include/hal/lcd_ll.h @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2021-2023 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2021-2024 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -161,7 +161,7 @@ static inline void lcd_ll_set_pixel_clock_edge(lcd_cam_dev_t *dev, bool active_o __attribute__((always_inline)) static inline void lcd_ll_set_pixel_clock_prescale(lcd_cam_dev_t *dev, uint32_t prescale) { - HAL_ASSERT(prescale <= LCD_LL_PCLK_DIV_MAX); + HAL_ASSERT(prescale > 0 && prescale <= LCD_LL_PCLK_DIV_MAX); // Formula: pixel_clk = lcd_clk / (1 + clkcnt_n) // clkcnt_n can't be zero uint32_t scale = 1; @@ -571,7 +571,7 @@ static inline void lcd_ll_set_command(lcd_cam_dev_t *dev, uint32_t data_width, u } /** - * @brief Wether to enable RGB interface + * @brief Whether to enable RGB interface * * @param dev LCD register base address * @param en True to enable RGB interface, False to disable RGB interface @@ -594,7 +594,7 @@ static inline void lcd_ll_enable_auto_next_frame(lcd_cam_dev_t *dev, bool en) } /** - * @brief Wether to output HSYNC signal in porch resion + * @brief Whether to output HSYNC signal in porch resion * * @param dev LCD register base address * @param en True to enable, False to disable @@ -726,7 +726,7 @@ static inline uint32_t lcd_ll_get_interrupt_status(lcd_cam_dev_t *dev) * @brief Clear interrupt status by mask * * @param dev LCD register base address - * @param mask Interupt status mask + * @param mask Interrupt status mask */ __attribute__((always_inline)) static inline void lcd_ll_clear_interrupt_status(lcd_cam_dev_t *dev, uint32_t mask) diff --git a/components/hal/include/hal/lcd_hal.h b/components/hal/include/hal/lcd_hal.h index 00b7549376..a59ca3165b 100644 --- a/components/hal/include/hal/lcd_hal.h +++ b/components/hal/include/hal/lcd_hal.h @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2021-2024 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -34,8 +34,6 @@ 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 @@ -43,11 +41,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_ * @param lcd_clk_div Returned LCD clock divider parameter * @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, int lcd_clk_flags, hal_utils_clk_div_t* lcd_clk_div); +uint32_t lcd_hal_cal_pclk_freq(lcd_hal_context_t *hal, uint32_t src_freq_hz, uint32_t expect_pclk_freq_hz, hal_utils_clk_div_t* lcd_clk_div); #ifdef __cplusplus } diff --git a/components/hal/lcd_hal.c b/components/hal/lcd_hal.c index 22572e8b09..e28752d796 100644 --- a/components/hal/lcd_hal.c +++ b/components/hal/lcd_hal.c @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2021-2023 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2021-2024 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -13,14 +13,14 @@ void lcd_hal_init(lcd_hal_context_t *hal, int id) hal->dev = LCD_LL_GET_HW(id); } -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, hal_utils_clk_div_t* lcd_clk_div) +uint32_t lcd_hal_cal_pclk_freq(lcd_hal_context_t *hal, uint32_t src_freq_hz, uint32_t expect_pclk_freq_hz, hal_utils_clk_div_t* lcd_clk_div) { + // formula: // 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; - } + + // due to some unstable hardware issue, we prefer to start with mo=2 first + uint32_t mo = 2; hal_utils_clk_info_t lcd_clk_info = { .src_freq_hz = src_freq_hz, .exp_freq_hz = expect_pclk_freq_hz * mo, @@ -29,8 +29,15 @@ uint32_t lcd_hal_cal_pclk_freq(lcd_hal_context_t *hal, uint32_t src_freq_hz, uin .max_fract = LCD_LL_CLK_FRAC_DIV_AB_MAX, }; uint32_t real_freq = hal_utils_calc_clk_div_frac_fast(&lcd_clk_info, lcd_clk_div); - HAL_EARLY_LOGD("lcd_hal", "n=%"PRIu32",a=%"PRIu32",b=%"PRIu32",mo=%"PRIu32, lcd_clk_div->integer, lcd_clk_div->denominator, lcd_clk_div->numerator, mo); + if (!real_freq) { + // if mo=2 can't achieve the target frequency, try others + mo = src_freq_hz / expect_pclk_freq_hz / LCD_LL_CLK_FRAC_DIV_N_MAX + 1; + lcd_clk_info.exp_freq_hz = expect_pclk_freq_hz * mo; + real_freq = hal_utils_calc_clk_div_frac_fast(&lcd_clk_info, lcd_clk_div); + } + + HAL_EARLY_LOGD("lcd_hal", "n=%"PRIu32",a=%"PRIu32",b=%"PRIu32",mo=%"PRIu32, lcd_clk_div->integer, lcd_clk_div->denominator, lcd_clk_div->numerator, mo); lcd_ll_set_pixel_clock_prescale(hal->dev, mo); return real_freq / mo; }