Browse Source

rgb_lcd: workaround pclk polarity bug by setting mo>=2

morris 3 năm trước cách đây
mục cha
commit
454d658309

+ 8 - 2
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);
 }

+ 5 - 1
components/hal/include/hal/lcd_hal.h

@@ -7,6 +7,7 @@
 #pragma once
 
 #include <stdint.h>
+#include <stdbool.h>
 
 #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
 }

+ 7 - 3
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);