Преглед изворни кода

rgb_lcd: support update pclk at runtime

morris пре 3 година
родитељ
комит
b2bb8fd3c4

+ 19 - 1
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

+ 24 - 0
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

+ 4 - 1
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"

+ 2 - 0
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)