From b2bb8fd3c477794e3e06fe6f57510a58bb28b4f4 Mon Sep 17 00:00:00 2001 From: morris Date: Wed, 1 Jun 2022 11:00:00 +0800 Subject: [PATCH] rgb_lcd: support update pclk at runtime --- .../esp_lcd/include/esp_lcd_panel_rgb.h | 20 +++++++++++++++- components/esp_lcd/src/esp_lcd_rgb_panel.c | 24 +++++++++++++++++++ components/hal/CMakeLists.txt | 5 +++- components/hal/linker.lf | 2 ++ 4 files changed, 49 insertions(+), 2 deletions(-) diff --git a/components/esp_lcd/include/esp_lcd_panel_rgb.h b/components/esp_lcd/include/esp_lcd_panel_rgb.h index 73d8c0d65f..ed856379b7 100644 --- a/components/esp_lcd/include/esp_lcd_panel_rgb.h +++ b/components/esp_lcd/include/esp_lcd_panel_rgb.h @@ -78,7 +78,7 @@ typedef struct { /** * @brief Declare the prototype of the function that will be invoked when panel IO finishes transferring color data * - * @param[in] panel LCD panel handle, returned from `esp_lcd_new_rgb_panel` + * @param[in] panel LCD panel handle, returned from `esp_lcd_new_rgb_panel()` * @param[in] edata Panel event data, fed by driver * @param[in] user_ctx User data, passed from `esp_lcd_rgb_panel_config_t` * @return Whether a high priority task has been waken up by this function @@ -122,6 +122,24 @@ typedef struct { */ esp_err_t esp_lcd_new_rgb_panel(const esp_lcd_rgb_panel_config_t *rgb_panel_config, esp_lcd_panel_handle_t *ret_panel); +/** + * @brief Set frequency of PCLK for RGB LCD panel + * + * @note The PCLK frequency is set in the `esp_lcd_rgb_timing_t` and gets configured during LCD panel initialization. + * Usually you don't need to call this function to set the PCLK again, but in some cases, you may need to change the PCLK frequency. + * e.g. to slow down the PCLK frequency to reduce power consumption or to reduce the memory throughput. + * @note This function doesn't cause the hardware to update the PCLK immediately but to record the new frequency and set a flag internally. + * Next time when start a new transaction, the driver will update the PCLK automatically. + * + * @param panel LCD panel handle, returned from `esp_lcd_new_rgb_panel()` + * @param freq_hz Frequency of pixel clock, in Hz + * @return + * - ESP_ERR_NOT_SUPPORTED if frequency is unreachable + * - ESP_ERR_INVALID_ARG if parameter panel is invalid + * - ESP_OK on success + */ +esp_err_t esp_rgb_panel_set_pclk(esp_lcd_panel_handle_t panel, uint32_t freq_hz); + #endif // SOC_LCD_RGB_SUPPORTED #ifdef __cplusplus diff --git a/components/esp_lcd/src/esp_lcd_rgb_panel.c b/components/esp_lcd/src/esp_lcd_rgb_panel.c index 44687dd19f..5aa7720f40 100644 --- a/components/esp_lcd/src/esp_lcd_rgb_panel.c +++ b/components/esp_lcd/src/esp_lcd_rgb_panel.c @@ -88,10 +88,12 @@ struct esp_rgb_panel_t { void *user_ctx; // Reserved user's data of callback functions 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) struct { unsigned int disp_en_level: 1; // The level which can turn on the screen by `disp_gpio_num` unsigned int stream_mode: 1; // If set, the LCD transfers data continuously, otherwise, it stops refreshing the LCD when transaction done unsigned int fb_in_psram: 1; // Whether the frame buffer is in PSRAM + unsigned int need_update_pclk: 1; // Whether to update the PCLK before start a new transaction } flags; dma_descriptor_t dma_nodes[]; // DMA descriptor pool of size `num_dma_nodes` }; @@ -187,6 +189,7 @@ esp_err_t esp_lcd_new_rgb_panel(const esp_lcd_rgb_panel_config_t *rgb_panel_conf rgb_panel->flags.disp_en_level = !rgb_panel_config->flags.disp_active_low; rgb_panel->on_frame_trans_done = rgb_panel_config->on_frame_trans_done; rgb_panel->user_ctx = rgb_panel_config->user_ctx; + rgb_panel->spinlock = (portMUX_TYPE)portMUX_INITIALIZER_UNLOCKED; // fill function table rgb_panel->base.del = rgb_panel_del; rgb_panel->base.reset = rgb_panel_reset; @@ -227,6 +230,18 @@ err: return ret; } +esp_err_t esp_rgb_panel_set_pclk(esp_lcd_panel_handle_t panel, uint32_t freq_hz) +{ + ESP_RETURN_ON_FALSE(panel, ESP_ERR_INVALID_ARG, TAG, "invalid argument"); + esp_rgb_panel_t *rgb_panel = __containerof(panel, esp_rgb_panel_t, base); + // the pclk frequency will be updated in `lcd_rgb_panel_start_transmission()` + portENTER_CRITICAL(&rgb_panel->spinlock); + rgb_panel->flags.need_update_pclk = true; + rgb_panel->timings.pclk_hz = freq_hz; + portEXIT_CRITICAL(&rgb_panel->spinlock); + return ESP_OK; +} + static esp_err_t rgb_panel_del(esp_lcd_panel_t *panel) { esp_rgb_panel_t *rgb_panel = __containerof(panel, esp_rgb_panel_t, base); @@ -499,6 +514,15 @@ static void lcd_rgb_panel_start_transmission(esp_rgb_panel_t *rgb_panel) // reset FIFO of DMA and LCD, incase there remains old frame data gdma_reset(rgb_panel->dma_chan); lcd_ll_stop(rgb_panel->hal.dev); + + // check whether to update the PCLK frequency + portENTER_CRITICAL_SAFE(&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); + } + portEXIT_CRITICAL_SAFE(&rgb_panel->spinlock); + lcd_ll_fifo_reset(rgb_panel->hal.dev); gdma_start(rgb_panel->dma_chan, (intptr_t)rgb_panel->dma_nodes); // delay 1us is sufficient for DMA to pass data to LCD FIFO diff --git a/components/hal/CMakeLists.txt b/components/hal/CMakeLists.txt index bb6ef9238a..f47c67b937 100644 --- a/components/hal/CMakeLists.txt +++ b/components/hal/CMakeLists.txt @@ -85,6 +85,10 @@ if(NOT BOOTLOADER_BUILD) list(APPEND srcs "emac_hal.c") endif() + if(CONFIG_SOC_LCDCAM_SUPPORTED) + list(APPEND srcs "lcd_hal.c") + endif() + if(${target} STREQUAL "esp32") list(APPEND srcs "dac_hal.c" @@ -119,7 +123,6 @@ if(NOT BOOTLOADER_BUILD) if(${target} STREQUAL "esp32s3") list(APPEND srcs "ds_hal.c" - "lcd_hal.c" "spi_flash_hal_gpspi.c" "spi_slave_hd_hal.c" "touch_sensor_hal.c" diff --git a/components/hal/linker.lf b/components/hal/linker.lf index a2fa83ba21..51d054b5e4 100644 --- a/components/hal/linker.lf +++ b/components/hal/linker.lf @@ -28,3 +28,5 @@ entries: timer_hal_iram (noflash) if GPIO_CTRL_FUNC_IN_IRAM = y: gpio_hal: gpio_hal_intr_disable (noflash) + if LCD_RGB_ISR_IRAM_SAFE = y: + lcd_hal: lcd_hal_cal_pclk_freq (noflash)