mirror of
https://github.com/espressif/esp-idf.git
synced 2025-08-09 15:44:34 +02:00
Merge branch 'bugfix/rgb_pclk_polarity_bug_workaround' into 'master'
rgb_lcd: workaround pclk polarity bug Closes IDF-5611 See merge request espressif/esp-idf!19068
This commit is contained in:
@@ -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 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
|
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)
|
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 {
|
struct {
|
||||||
uint32_t disp_en_level: 1; // The level which can turn on the screen by `disp_gpio_num`
|
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
|
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
|
// set clock source
|
||||||
ret = lcd_rgb_panel_select_clock_src(rgb_panel, rgb_panel_config->clk_src);
|
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");
|
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)
|
// 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;
|
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,
|
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_err_t ret = ESP_OK;
|
||||||
esp_rgb_panel_t *rgb_panel = __containerof(panel, esp_rgb_panel_t, base);
|
esp_rgb_panel_t *rgb_panel = __containerof(panel, esp_rgb_panel_t, base);
|
||||||
// set pixel clock frequency
|
// 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
|
// 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_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);
|
lcd_ll_set_pixel_clock_edge(rgb_panel->hal.dev, rgb_panel->timings.flags.pclk_active_neg);
|
||||||
@@ -595,15 +601,11 @@ static esp_err_t lcd_rgb_panel_select_clock_src(esp_rgb_panel_t *panel, lcd_cloc
|
|||||||
{
|
{
|
||||||
esp_err_t ret = ESP_OK;
|
esp_err_t ret = ESP_OK;
|
||||||
switch (clk_src) {
|
switch (clk_src) {
|
||||||
|
case LCD_CLK_SRC_PLL240M:
|
||||||
|
panel->src_clk_hz = 240000000;
|
||||||
|
break;
|
||||||
case LCD_CLK_SRC_PLL160M:
|
case LCD_CLK_SRC_PLL160M:
|
||||||
panel->src_clk_hz = 160000000;
|
panel->src_clk_hz = 160000000;
|
||||||
#if CONFIG_PM_ENABLE
|
|
||||||
ret = esp_pm_lock_create(ESP_PM_APB_FREQ_MAX, 0, "rgb_panel", &panel->pm_lock);
|
|
||||||
ESP_RETURN_ON_ERROR(ret, TAG, "create ESP_PM_APB_FREQ_MAX lock failed");
|
|
||||||
// hold the lock during the whole lifecycle of RGB panel
|
|
||||||
esp_pm_lock_acquire(panel->pm_lock);
|
|
||||||
ESP_LOGD(TAG, "installed ESP_PM_APB_FREQ_MAX lock and hold the lock during the whole panel lifecycle");
|
|
||||||
#endif
|
|
||||||
break;
|
break;
|
||||||
case LCD_CLK_SRC_XTAL:
|
case LCD_CLK_SRC_XTAL:
|
||||||
panel->src_clk_hz = esp_clk_xtal_freq();
|
panel->src_clk_hz = esp_clk_xtal_freq();
|
||||||
@@ -613,6 +615,16 @@ static esp_err_t lcd_rgb_panel_select_clock_src(esp_rgb_panel_t *panel, lcd_cloc
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
lcd_ll_select_clk_src(panel->hal.dev, clk_src);
|
lcd_ll_select_clk_src(panel->hal.dev, clk_src);
|
||||||
|
|
||||||
|
if (clk_src == LCD_CLK_SRC_PLL240M || clk_src == LCD_CLK_SRC_PLL160M) {
|
||||||
|
#if CONFIG_PM_ENABLE
|
||||||
|
ret = esp_pm_lock_create(ESP_PM_APB_FREQ_MAX, 0, "rgb_panel", &panel->pm_lock);
|
||||||
|
ESP_RETURN_ON_ERROR(ret, TAG, "create ESP_PM_APB_FREQ_MAX lock failed");
|
||||||
|
// hold the lock during the whole lifecycle of RGB panel
|
||||||
|
esp_pm_lock_acquire(panel->pm_lock);
|
||||||
|
ESP_LOGD(TAG, "installed ESP_PM_APB_FREQ_MAX lock and hold the lock during the whole panel lifecycle");
|
||||||
|
#endif
|
||||||
|
}
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -789,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);
|
portENTER_CRITICAL_ISR(&rgb_panel->spinlock);
|
||||||
if (unlikely(rgb_panel->flags.need_update_pclk)) {
|
if (unlikely(rgb_panel->flags.need_update_pclk)) {
|
||||||
rgb_panel->flags.need_update_pclk = false;
|
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);
|
portEXIT_CRITICAL_ISR(&rgb_panel->spinlock);
|
||||||
}
|
}
|
||||||
|
@@ -7,6 +7,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
extern "C" {
|
extern "C" {
|
||||||
@@ -32,6 +33,8 @@ typedef struct {
|
|||||||
*/
|
*/
|
||||||
void lcd_hal_init(lcd_hal_context_t *hal, int id);
|
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
|
* @brief LCD PCLK clock calculation
|
||||||
* @note Currently this function is only used by RGB LCD driver, I80 driver still uses a fixed clock division
|
* @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 hal LCD HAL layer context
|
||||||
* @param src_freq_hz LCD source clock frequency in Hz
|
* @param src_freq_hz LCD source clock frequency in Hz
|
||||||
* @param expect_pclk_freq_hz Expected LCD PCLK 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
|
* @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
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
|
@@ -20,6 +20,7 @@ void lcd_hal_init(lcd_hal_context_t *hal, int id)
|
|||||||
* @param b smaller value
|
* @param b smaller value
|
||||||
* @return result of gcd(a, b)
|
* @return result of gcd(a, b)
|
||||||
*/
|
*/
|
||||||
|
__attribute__((always_inline))
|
||||||
static inline uint32_t _gcd(uint32_t a, uint32_t b)
|
static inline uint32_t _gcd(uint32_t a, uint32_t b)
|
||||||
{
|
{
|
||||||
uint32_t c = a % b;
|
uint32_t c = a % b;
|
||||||
@@ -31,16 +32,19 @@ static inline uint32_t _gcd(uint32_t a, uint32_t b)
|
|||||||
return 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)
|
// lcd_clk = module_clock_src / (n + b / a)
|
||||||
// pixel_clk = lcd_clk / mo
|
// pixel_clk = lcd_clk / mo
|
||||||
uint32_t mo = src_freq_hz / expect_pclk_freq_hz / LCD_LL_CLK_FRAC_DIV_N_MAX + 1;
|
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 n = src_freq_hz / expect_pclk_freq_hz / mo;
|
||||||
uint32_t a = 0;
|
uint32_t a = 0;
|
||||||
uint32_t b = 0;
|
uint32_t b = 0;
|
||||||
// delta_hz / expect_pclk_freq_hz <==> b / a
|
// 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
|
// fractional divider
|
||||||
if (delta_hz) {
|
if (delta_hz) {
|
||||||
uint32_t gcd = _gcd(expect_pclk_freq_hz, 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;
|
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_group_clock_coeff(hal->dev, n, a, b);
|
||||||
lcd_ll_set_pixel_clock_prescale(hal->dev, mo);
|
lcd_ll_set_pixel_clock_prescale(hal->dev, mo);
|
||||||
|
Reference in New Issue
Block a user