Răsfoiți Sursa

Merge branch 'feature/rgb_lcd_yuv_converter' into 'master'

RGB-YUV converter

Closes IDF-4598

See merge request espressif/esp-idf!19094
morris 3 ani în urmă
părinte
comite
e2634b5223

+ 2 - 1
.pre-commit-config.yaml

@@ -21,7 +21,8 @@ repos:
             .+test_idf_monitor\/tests\/.+|
             .+test_idf_monitor\/tests\/.+|
             .*_pb2.py|
             .*_pb2.py|
             .*.pb-c.h|
             .*.pb-c.h|
-            .*.pb-c.c
+            .*.pb-c.c|
+            .*.yuv
           )$
           )$
       - id: end-of-file-fixer
       - id: end-of-file-fixer
         exclude: *whitespace_excludes
         exclude: *whitespace_excludes

+ 34 - 1
components/esp_lcd/include/esp_lcd_panel_rgb.h

@@ -115,7 +115,7 @@ typedef struct {
     lcd_clock_source_t clk_src;   /*!< Clock source for the RGB LCD peripheral */
     lcd_clock_source_t clk_src;   /*!< Clock source for the RGB LCD peripheral */
     esp_lcd_rgb_timing_t timings; /*!< RGB timing parameters, including the screen resolution */
     esp_lcd_rgb_timing_t timings; /*!< RGB timing parameters, including the screen resolution */
     size_t data_width;            /*!< Number of data lines */
     size_t data_width;            /*!< Number of data lines */
-    size_t bits_per_pixel;        /*!< Color depth, in bpp, specially, if set to zero, it will default to `data_width`.
+    size_t bits_per_pixel;        /*!< Frame buffer color depth, in bpp, specially, if set to zero, it will default to `data_width`.
                                        When using a Serial RGB interface, this value could be different from `data_width` */
                                        When using a Serial RGB interface, this value could be different from `data_width` */
     size_t bounce_buffer_size_px; /*!< If it's non-zero, the driver allocates two DRAM bounce buffers for DMA use.
     size_t bounce_buffer_size_px; /*!< If it's non-zero, the driver allocates two DRAM bounce buffers for DMA use.
                                        DMA fetching from DRAM bounce buffer is much faster than PSRAM frame buffer. */
                                        DMA fetching from DRAM bounce buffer is much faster than PSRAM frame buffer. */
@@ -209,6 +209,39 @@ esp_err_t esp_lcd_rgb_panel_get_frame_buffer(esp_lcd_panel_handle_t panel, uint3
  */
  */
 esp_err_t esp_lcd_rgb_panel_refresh(esp_lcd_panel_handle_t panel);
 esp_err_t esp_lcd_rgb_panel_refresh(esp_lcd_panel_handle_t panel);
 
 
+/**
+ * @brief LCD color conversion profile
+ */
+typedef struct {
+    lcd_color_space_t color_space; /*!< Color space of the image */
+    lcd_color_range_t color_range; /*!< Color range of the image */
+    lcd_yuv_sample_t yuv_sample;   /*!< YUV sample format of the image */
+} esp_lcd_color_conv_profile_t;
+
+/**
+ * @brief Configuration of YUG-RGB conversion
+ */
+typedef struct {
+    lcd_yuv_conv_std_t std;           /*!< YUV conversion standard: BT601, BT709 */
+    esp_lcd_color_conv_profile_t src; /*!< Color conversion profile of the input image */
+    esp_lcd_color_conv_profile_t dst; /*!< Color conversion profile of the output image */
+} esp_lcd_yuv_conv_config_t;
+
+/**
+ * @brief Configure how to convert the color format between RGB and YUV
+ *
+ * @note Pass in `config` as NULL will disable the RGB-YUV converter.
+ * @note The hardware converter can only parse a "packed" storage format, while "planar" and "semi-planar" format is not supported.
+ *
+ * @param[in] panel LCD panel handle, returned from `esp_lcd_new_rgb_panel`
+ * @param[in] config Configuration of RGB-YUV conversion
+ * @return
+ *      - ESP_ERR_INVALID_ARG: Configure RGB-YUV conversion failed because of invalid argument
+ *      - ESP_ERR_NOT_SUPPORTED: Configure RGB-YUV conversion failed because the conversion mode is not supported by the hardware
+ *      - ESP_OK: Configure RGB-YUV conversion successfully
+ */
+esp_err_t esp_lcd_rgb_panel_set_yuv_conversion(esp_lcd_panel_handle_t panel, const esp_lcd_yuv_conv_config_t *config);
+
 #endif // SOC_LCD_RGB_SUPPORTED
 #endif // SOC_LCD_RGB_SUPPORTED
 
 
 #ifdef __cplusplus
 #ifdef __cplusplus

+ 62 - 9
components/esp_lcd/src/esp_lcd_rgb_panel.c

@@ -83,7 +83,8 @@ struct esp_rgb_panel_t {
     int panel_id;          // LCD panel ID
     int panel_id;          // LCD panel ID
     lcd_hal_context_t hal; // Hal layer object
     lcd_hal_context_t hal; // Hal layer object
     size_t data_width;     // Number of data lines
     size_t data_width;     // Number of data lines
-    size_t bits_per_pixel; // Color depth, in bpp
+    size_t fb_bits_per_pixel; // Frame buffer color depth, in bpp
+    size_t output_bits_per_pixel; // Color depth seen from the output data line. Default to fb_bits_per_pixel, but can be changed by YUV-RGB conversion
     size_t sram_trans_align;  // Alignment for framebuffer that allocated in SRAM
     size_t sram_trans_align;  // Alignment for framebuffer that allocated in SRAM
     size_t psram_trans_align; // Alignment for framebuffer that allocated in PSRAM
     size_t psram_trans_align; // Alignment for framebuffer that allocated in PSRAM
     int disp_gpio_num;     // Display control GPIO, which is used to perform action like "disp_off"
     int disp_gpio_num;     // Display control GPIO, which is used to perform action like "disp_off"
@@ -220,13 +221,13 @@ esp_err_t esp_lcd_new_rgb_panel(const esp_lcd_rgb_panel_config_t *rgb_panel_conf
 #endif
 #endif
 
 
     // bpp defaults to the number of data lines, but for serial RGB interface, they're not equal
     // bpp defaults to the number of data lines, but for serial RGB interface, they're not equal
-    size_t bits_per_pixel = rgb_panel_config->data_width;
+    size_t fb_bits_per_pixel = rgb_panel_config->data_width;
     if (rgb_panel_config->bits_per_pixel) { // override bpp if it's set
     if (rgb_panel_config->bits_per_pixel) { // override bpp if it's set
-        bits_per_pixel = rgb_panel_config->bits_per_pixel;
+        fb_bits_per_pixel = rgb_panel_config->bits_per_pixel;
     }
     }
     // calculate buffer size
     // calculate buffer size
-    size_t fb_size = rgb_panel_config->timings.h_res * rgb_panel_config->timings.v_res * bits_per_pixel / 8;
-    size_t bb_size = rgb_panel_config->bounce_buffer_size_px * bits_per_pixel / 8;
+    size_t fb_size = rgb_panel_config->timings.h_res * rgb_panel_config->timings.v_res * fb_bits_per_pixel / 8;
+    size_t bb_size = rgb_panel_config->bounce_buffer_size_px * fb_bits_per_pixel / 8;
     if (bb_size) {
     if (bb_size) {
         // we want the bounce can always end in the second buffer
         // we want the bounce can always end in the second buffer
         ESP_GOTO_ON_FALSE(fb_size % (2 * bb_size) == 0, ESP_ERR_INVALID_ARG, err, TAG,
         ESP_GOTO_ON_FALSE(fb_size % (2 * bb_size) == 0, ESP_ERR_INVALID_ARG, err, TAG,
@@ -297,7 +298,8 @@ esp_err_t esp_lcd_new_rgb_panel(const esp_lcd_rgb_panel_config_t *rgb_panel_conf
     memcpy(rgb_panel->data_gpio_nums, rgb_panel_config->data_gpio_nums, SOC_LCD_RGB_DATA_WIDTH);
     memcpy(rgb_panel->data_gpio_nums, rgb_panel_config->data_gpio_nums, SOC_LCD_RGB_DATA_WIDTH);
     rgb_panel->timings = rgb_panel_config->timings;
     rgb_panel->timings = rgb_panel_config->timings;
     rgb_panel->data_width = rgb_panel_config->data_width;
     rgb_panel->data_width = rgb_panel_config->data_width;
-    rgb_panel->bits_per_pixel = bits_per_pixel;
+    rgb_panel->fb_bits_per_pixel = fb_bits_per_pixel;
+    rgb_panel->output_bits_per_pixel = fb_bits_per_pixel; // by default, the output bpp is the same as the frame buffer bpp
     rgb_panel->disp_gpio_num = rgb_panel_config->disp_gpio_num;
     rgb_panel->disp_gpio_num = rgb_panel_config->disp_gpio_num;
     rgb_panel->flags.disp_en_level = !rgb_panel_config->flags.disp_active_low;
     rgb_panel->flags.disp_en_level = !rgb_panel_config->flags.disp_active_low;
     rgb_panel->flags.no_fb = rgb_panel_config->flags.no_fb;
     rgb_panel->flags.no_fb = rgb_panel_config->flags.no_fb;
@@ -387,6 +389,57 @@ esp_err_t esp_lcd_rgb_panel_refresh(esp_lcd_panel_handle_t panel)
     return ESP_OK;
     return ESP_OK;
 }
 }
 
 
+esp_err_t esp_lcd_rgb_panel_set_yuv_conversion(esp_lcd_panel_handle_t panel, const esp_lcd_yuv_conv_config_t *config)
+{
+    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);
+    lcd_hal_context_t *hal = &rgb_panel->hal;
+    bool en_conversion = config != NULL;
+
+    // bits per pixel for different YUV sample
+    const uint8_t bpp_yuv[] = {
+        [LCD_YUV_SAMPLE_422] = 16,
+        [LCD_YUV_SAMPLE_420] = 12,
+        [LCD_YUV_SAMPLE_411] = 12,
+    };
+
+    if (en_conversion) {
+        if (memcmp(&config->src, &config->dst, sizeof(config->src)) == 0) {
+            ESP_RETURN_ON_FALSE(false, ESP_ERR_INVALID_ARG, TAG, "conversion source and destination are the same");
+        }
+
+        if (config->src.color_space == LCD_COLOR_SPACE_YUV && config->dst.color_space == LCD_COLOR_SPACE_RGB) { // YUV->RGB
+            lcd_ll_set_convert_mode_yuv_to_rgb(hal->dev, config->src.yuv_sample);
+            // Note, the RGB->YUV conversion only support RGB565
+            rgb_panel->output_bits_per_pixel = 16;
+        } else if (config->src.color_space == LCD_COLOR_SPACE_RGB && config->dst.color_space == LCD_COLOR_SPACE_YUV) { // RGB->YUV
+            lcd_ll_set_convert_mode_rgb_to_yuv(hal->dev, config->dst.yuv_sample);
+            rgb_panel->output_bits_per_pixel = bpp_yuv[config->dst.yuv_sample];
+        } else if (config->src.color_space == LCD_COLOR_SPACE_YUV && config->dst.color_space == LCD_COLOR_SPACE_YUV) { // YUV->YUV
+            lcd_ll_set_convert_mode_yuv_to_yuv(hal->dev, config->src.yuv_sample, config->dst.yuv_sample);
+            rgb_panel->output_bits_per_pixel = bpp_yuv[config->dst.yuv_sample];
+        } else {
+            ESP_RETURN_ON_FALSE(false, ESP_ERR_NOT_SUPPORTED, TAG, "unsupported conversion mode");
+        }
+
+        // set conversion standard
+        lcd_ll_set_yuv_convert_std(hal->dev, config->std);
+        // set conversion data width
+        lcd_ll_set_convert_data_width(hal->dev, rgb_panel->data_width);
+        // set color range
+        lcd_ll_set_input_color_range(hal->dev, config->src.color_range);
+        lcd_ll_set_output_color_range(hal->dev, config->dst.color_range);
+    } else {
+        // output bpp equals to frame buffer bpp
+        rgb_panel->output_bits_per_pixel = rgb_panel->fb_bits_per_pixel;
+    }
+
+    // enable or disable RGB-YUV conversion
+    lcd_ll_enable_rgb_yuv_convert(hal->dev, en_conversion);
+
+    return ESP_OK;
+}
+
 static esp_err_t rgb_panel_del(esp_lcd_panel_t *panel)
 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);
     esp_rgb_panel_t *rgb_panel = __containerof(panel, esp_rgb_panel_t, base);
@@ -425,7 +478,7 @@ static esp_err_t rgb_panel_init(esp_lcd_panel_t *panel)
     // configure blank region timing
     // configure blank region timing
     lcd_ll_set_blank_cycles(rgb_panel->hal.dev, 1, 1); // RGB panel always has a front and back blank (porch region)
     lcd_ll_set_blank_cycles(rgb_panel->hal.dev, 1, 1); // RGB panel always has a front and back blank (porch region)
     lcd_ll_set_horizontal_timing(rgb_panel->hal.dev, rgb_panel->timings.hsync_pulse_width,
     lcd_ll_set_horizontal_timing(rgb_panel->hal.dev, rgb_panel->timings.hsync_pulse_width,
-                                 rgb_panel->timings.hsync_back_porch, rgb_panel->timings.h_res * rgb_panel->bits_per_pixel / rgb_panel->data_width,
+                                 rgb_panel->timings.hsync_back_porch, rgb_panel->timings.h_res * rgb_panel->output_bits_per_pixel / rgb_panel->data_width,
                                  rgb_panel->timings.hsync_front_porch);
                                  rgb_panel->timings.hsync_front_porch);
     lcd_ll_set_vertical_timing(rgb_panel->hal.dev, rgb_panel->timings.vsync_pulse_width,
     lcd_ll_set_vertical_timing(rgb_panel->hal.dev, rgb_panel->timings.vsync_pulse_width,
                                rgb_panel->timings.vsync_back_porch, rgb_panel->timings.v_res,
                                rgb_panel->timings.vsync_back_porch, rgb_panel->timings.v_res,
@@ -500,7 +553,7 @@ static esp_err_t rgb_panel_draw_bitmap(esp_lcd_panel_t *panel, int x_start, int
         y_end = MIN(y_end, v_res);
         y_end = MIN(y_end, v_res);
     }
     }
 
 
-    int bytes_per_pixel = rgb_panel->bits_per_pixel / 8;
+    int bytes_per_pixel = rgb_panel->fb_bits_per_pixel / 8;
     int pixels_per_line = rgb_panel->timings.h_res;
     int pixels_per_line = rgb_panel->timings.h_res;
     uint32_t bytes_per_line = bytes_per_pixel * pixels_per_line;
     uint32_t bytes_per_line = bytes_per_pixel * pixels_per_line;
     uint8_t *fb = rgb_panel->fbs[rgb_panel->cur_fb_index];
     uint8_t *fb = rgb_panel->fbs[rgb_panel->cur_fb_index];
@@ -862,7 +915,7 @@ static esp_err_t lcd_rgb_panel_select_clock_src(esp_rgb_panel_t *panel, lcd_cloc
 static IRAM_ATTR bool lcd_rgb_panel_fill_bounce_buffer(esp_rgb_panel_t *panel, uint8_t *buffer)
 static IRAM_ATTR bool lcd_rgb_panel_fill_bounce_buffer(esp_rgb_panel_t *panel, uint8_t *buffer)
 {
 {
     bool need_yield = false;
     bool need_yield = false;
-    int bytes_per_pixel = panel->bits_per_pixel / 8;
+    int bytes_per_pixel = panel->fb_bits_per_pixel / 8;
     if (panel->flags.no_fb) {
     if (panel->flags.no_fb) {
         if (panel->on_bounce_empty) {
         if (panel->on_bounce_empty) {
             // We don't have a frame buffer here; we need to call a callback to refill the bounce buffer
             // We don't have a frame buffer here; we need to call a callback to refill the bounce buffer

+ 3 - 0
components/esp_lcd/test_apps/rgb_lcd/CMakeLists.txt

@@ -4,6 +4,9 @@ cmake_minimum_required(VERSION 3.16)
 include($ENV{IDF_PATH}/tools/cmake/project.cmake)
 include($ENV{IDF_PATH}/tools/cmake/project.cmake)
 project(rgb_lcd_panel_test)
 project(rgb_lcd_panel_test)
 
 
+target_add_binary_data(rgb_lcd_panel_test.elf "resources/pictures/hello.yuv" BINARY)
+target_add_binary_data(rgb_lcd_panel_test.elf "resources/pictures/world.yuv" BINARY)
+
 if(CONFIG_COMPILER_DUMP_RTL_FILES)
 if(CONFIG_COMPILER_DUMP_RTL_FILES)
     add_custom_target(check_test_app_sections ALL
     add_custom_target(check_test_app_sections ALL
                       COMMAND ${PYTHON} $ENV{IDF_PATH}/tools/ci/check_callgraph.py
                       COMMAND ${PYTHON} $ENV{IDF_PATH}/tools/ci/check_callgraph.py

+ 4 - 0
components/esp_lcd/test_apps/rgb_lcd/main/CMakeLists.txt

@@ -1,6 +1,10 @@
 set(srcs "test_app_main.c"
 set(srcs "test_app_main.c"
          "test_rgb_panel.c")
          "test_rgb_panel.c")
 
 
+if(CONFIG_SOC_LCD_SUPPORT_RGB_YUV_CONV)
+    list(APPEND srcs "test_yuv_rgb_conv.c")
+endif()
+
 # In order for the cases defined by `TEST_CASE` to be linked into the final elf,
 # In order for the cases defined by `TEST_CASE` to be linked into the final elf,
 # the component can be registered as WHOLE_ARCHIVE
 # the component can be registered as WHOLE_ARCHIVE
 idf_component_register(SRCS ${srcs}
 idf_component_register(SRCS ${srcs}

+ 105 - 0
components/esp_lcd/test_apps/rgb_lcd/main/test_yuv_rgb_conv.c

@@ -0,0 +1,105 @@
+/*
+ * SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+#include <stdio.h>
+#include <string.h>
+#include "freertos/FreeRTOS.h"
+#include "freertos/task.h"
+#include "unity.h"
+#include "esp_lcd_panel_rgb.h"
+#include "esp_lcd_panel_ops.h"
+#include "esp_random.h"
+#include "esp_timer.h"
+#include "esp_attr.h"
+#include "spi_flash_mmap.h"
+#include "test_rgb_board.h"
+
+#define TEST_IMG_SIZE (320 * 320 * sizeof(uint16_t))
+
+// YUV images are embedded in the firmware binary
+extern const uint8_t image_hello_yuv_start[] asm("_binary_hello_yuv_start");
+extern const uint8_t image_hello_yuv_end[]   asm("_binary_hello_yuv_end");
+extern const uint8_t image_world_yuv_start[] asm("_binary_world_yuv_start");
+extern const uint8_t image_world_yuv_end[]   asm("_binary_world_yuv_end");
+
+TEST_CASE("lcd_rgb_panel_yuv422_conversion", "[lcd]")
+{
+    esp_lcd_panel_handle_t panel_handle = NULL;
+    esp_lcd_rgb_panel_config_t panel_config = {
+        .data_width = 16,
+        .psram_trans_align = 64,
+        .bits_per_pixel = 16, // YUV422: 16bits per pixel
+        .clk_src = LCD_CLK_SRC_DEFAULT,
+        .disp_gpio_num = TEST_LCD_DISP_EN_GPIO,
+        .pclk_gpio_num = TEST_LCD_PCLK_GPIO,
+        .vsync_gpio_num = TEST_LCD_VSYNC_GPIO,
+        .hsync_gpio_num = TEST_LCD_HSYNC_GPIO,
+        .de_gpio_num = TEST_LCD_DE_GPIO,
+        .data_gpio_nums = {
+            TEST_LCD_DATA0_GPIO,
+            TEST_LCD_DATA1_GPIO,
+            TEST_LCD_DATA2_GPIO,
+            TEST_LCD_DATA3_GPIO,
+            TEST_LCD_DATA4_GPIO,
+            TEST_LCD_DATA5_GPIO,
+            TEST_LCD_DATA6_GPIO,
+            TEST_LCD_DATA7_GPIO,
+            TEST_LCD_DATA8_GPIO,
+            TEST_LCD_DATA9_GPIO,
+            TEST_LCD_DATA10_GPIO,
+            TEST_LCD_DATA11_GPIO,
+            TEST_LCD_DATA12_GPIO,
+            TEST_LCD_DATA13_GPIO,
+            TEST_LCD_DATA14_GPIO,
+            TEST_LCD_DATA15_GPIO,
+        },
+        .timings = {
+            .pclk_hz = TEST_LCD_PIXEL_CLOCK_HZ,
+            .h_res = TEST_LCD_H_RES,
+            .v_res = TEST_LCD_V_RES,
+            .hsync_back_porch = 68,
+            .hsync_front_porch = 20,
+            .hsync_pulse_width = 5,
+            .vsync_back_porch = 18,
+            .vsync_front_porch = 4,
+            .vsync_pulse_width = 1,
+        },
+        .flags.fb_in_psram = 1, // allocate frame buffer in PSRAM
+    };
+
+    printf("Create RGB LCD panel\r\n");
+    TEST_ESP_OK(esp_lcd_new_rgb_panel(&panel_config, &panel_handle));
+    TEST_ESP_OK(esp_lcd_panel_reset(panel_handle));
+
+    printf("Set YUV-RGB conversion profile\r\n");
+    esp_lcd_yuv_conv_config_t conv_config = {
+        .std = LCD_YUV_CONV_STD_BT601,
+        .src = {
+            .color_range = LCD_COLOR_RANGE_FULL,
+            .color_space = LCD_COLOR_SPACE_RGB,
+        },
+        .dst = {
+            .color_range = LCD_COLOR_RANGE_FULL,
+            .color_space = LCD_COLOR_SPACE_RGB,
+        },
+    };
+    TEST_ESP_ERR(ESP_ERR_INVALID_ARG, esp_lcd_rgb_panel_set_yuv_conversion(panel_handle, &conv_config));
+
+    conv_config.src.color_space = LCD_COLOR_SPACE_YUV;
+    conv_config.src.yuv_sample = LCD_YUV_SAMPLE_422;
+    TEST_ESP_OK(esp_lcd_rgb_panel_set_yuv_conversion(panel_handle, &conv_config));
+
+    TEST_ESP_OK(esp_lcd_panel_init(panel_handle));
+
+    printf("Draw YUV images\r\n");
+    for (int i = 0; i < 4; i++) {
+        TEST_ESP_OK(esp_lcd_panel_draw_bitmap(panel_handle, 0, 0, 320, 320, image_hello_yuv_start));
+        vTaskDelay(pdMS_TO_TICKS(1000));
+        TEST_ESP_OK(esp_lcd_panel_draw_bitmap(panel_handle, 0, 0, 320, 320, image_world_yuv_start));
+        vTaskDelay(pdMS_TO_TICKS(1000));
+    }
+
+    TEST_ESP_OK(esp_lcd_panel_del(panel_handle));
+}

+ 5 - 0
components/esp_lcd/test_apps/rgb_lcd/resources/README.md

@@ -0,0 +1,5 @@
+# How to generate the YUV image from the PNG image
+
+```bash
+ffmpeg -i hello.png -pix_fmt uyvy422 hello.yuv
+```

BIN
components/esp_lcd/test_apps/rgb_lcd/resources/pictures/hello.png


Fișier diff suprimat deoarece este prea mare
+ 0 - 0
components/esp_lcd/test_apps/rgb_lcd/resources/pictures/hello.yuv


BIN
components/esp_lcd/test_apps/rgb_lcd/resources/pictures/world.png


Fișier diff suprimat deoarece este prea mare
+ 0 - 0
components/esp_lcd/test_apps/rgb_lcd/resources/pictures/world.yuv


+ 90 - 0
components/hal/esp32s3/include/hal/lcd_ll.h

@@ -28,6 +28,10 @@ extern "C" {
 #define LCD_LL_CLK_FRAC_DIV_AB_MAX 64  // LCD_CLK = LCD_CLK_S / (N + b/a), the a/b register is 6 bit-width
 #define LCD_LL_CLK_FRAC_DIV_AB_MAX 64  // LCD_CLK = LCD_CLK_S / (N + b/a), the a/b register is 6 bit-width
 #define LCD_LL_PCLK_DIV_MAX        64  // LCD_PCLK = LCD_CLK / MO, the MO register is 6 bit-width
 #define LCD_LL_PCLK_DIV_MAX        64  // LCD_PCLK = LCD_CLK / MO, the MO register is 6 bit-width
 
 
+#define LCD_LL_COLOR_RANGE_TO_REG(range) (uint8_t[]){0,1}[(range)]
+#define LCD_LL_CONV_STD_TO_REG(std)      (uint8_t[]){0,1}[(std)]
+#define LCD_LL_YUV_SAMPLE_TO_REG(sample) (uint8_t[]){0,1,2}[(sample)]
+
 /**
 /**
  * @brief Enable clock gating
  * @brief Enable clock gating
  *
  *
@@ -144,6 +148,92 @@ static inline void lcd_ll_enable_rgb_yuv_convert(lcd_cam_dev_t *dev, bool en)
     dev->lcd_rgb_yuv.lcd_conv_bypass = en;
     dev->lcd_rgb_yuv.lcd_conv_bypass = en;
 }
 }
 
 
+/**
+ * @brief Set convert data line width
+ *
+ * @param dev LCD register base address
+ * @param width data line width (8 or 16)
+ */
+static inline void lcd_ll_set_convert_data_width(lcd_cam_dev_t *dev, uint32_t width)
+{
+    HAL_ASSERT(width == 8 || width == 16);
+    dev->lcd_rgb_yuv.lcd_conv_mode_8bits_on = (width == 8) ? 1 : 0;
+}
+
+/**
+ * @brief Set the color range of input data
+ *
+ * @param dev LCD register base address
+ * @param range Color range
+ */
+static inline void lcd_ll_set_input_color_range(lcd_cam_dev_t *dev, lcd_color_range_t range)
+{
+    dev->lcd_rgb_yuv.lcd_conv_data_in_mode = LCD_LL_COLOR_RANGE_TO_REG(range);
+}
+
+/**
+ * @brief Set the color range of output data
+ *
+ * @param dev LCD register base address
+ * @param range Color range
+ */
+static inline void lcd_ll_set_output_color_range(lcd_cam_dev_t *dev, lcd_color_range_t range)
+{
+    dev->lcd_rgb_yuv.lcd_conv_data_out_mode = LCD_LL_COLOR_RANGE_TO_REG(range);
+}
+
+/**
+ * @brief Set YUV conversion standard
+ *
+ * @param dev LCD register base address
+ * @param std YUV conversion standard
+ */
+static inline void lcd_ll_set_yuv_convert_std(lcd_cam_dev_t *dev, lcd_yuv_conv_std_t std)
+{
+    dev->lcd_rgb_yuv.lcd_conv_protocol_mode = LCD_LL_CONV_STD_TO_REG(std);
+}
+
+/**
+ * @brief Set the converter mode: RGB565 to YUV
+ *
+ * @param dev LCD register base address
+ * @param yuv_sample YUV sample mode
+ */
+static inline void lcd_ll_set_convert_mode_rgb_to_yuv(lcd_cam_dev_t *dev, lcd_yuv_sample_t yuv_sample)
+{
+    dev->lcd_rgb_yuv.lcd_conv_trans_mode = 1;
+    dev->lcd_rgb_yuv.lcd_conv_yuv_mode = LCD_LL_YUV_SAMPLE_TO_REG(yuv_sample);
+    dev->lcd_rgb_yuv.lcd_conv_yuv2yuv_mode = 3;
+}
+
+/**
+ * @brief Set the converter mode: YUV to RGB565
+ *
+ * @param dev LCD register base address
+ * @param yuv_sample YUV sample mode
+ */
+static inline void lcd_ll_set_convert_mode_yuv_to_rgb(lcd_cam_dev_t *dev, lcd_yuv_sample_t yuv_sample)
+{
+    dev->lcd_rgb_yuv.lcd_conv_trans_mode = 0;
+    dev->lcd_rgb_yuv.lcd_conv_yuv_mode = LCD_LL_YUV_SAMPLE_TO_REG(yuv_sample);
+    dev->lcd_rgb_yuv.lcd_conv_yuv2yuv_mode = 3;
+}
+
+/**
+ * @brief Set the converter mode: YUV to YUV
+ *
+ * @param dev LCD register base address
+ * @param src_sample Source YUV sample mode
+ * @param dst_sample Destination YUV sample mode
+ */
+static inline void lcd_ll_set_convert_mode_yuv_to_yuv(lcd_cam_dev_t *dev, lcd_yuv_sample_t src_sample, lcd_yuv_sample_t dst_sample)
+{
+    HAL_ASSERT(src_sample != dst_sample);
+    dev->lcd_rgb_yuv.lcd_conv_trans_mode = 1;
+    dev->lcd_rgb_yuv.lcd_conv_yuv_mode = LCD_LL_YUV_SAMPLE_TO_REG(src_sample);
+    dev->lcd_rgb_yuv.lcd_conv_yuv2yuv_mode = LCD_LL_YUV_SAMPLE_TO_REG(dst_sample);
+}
+
 /**
 /**
  * @brief Set clock cycles of each transaction phases
  * @brief Set clock cycles of each transaction phases
  *
  *

+ 33 - 0
components/hal/include/hal/lcd_types.h

@@ -28,6 +28,39 @@ typedef enum {
     LCD_RGB_ENDIAN_BGR, /*!< RGB data endian: BGR */
     LCD_RGB_ENDIAN_BGR, /*!< RGB data endian: BGR */
 } lcd_color_rgb_endian_t;
 } lcd_color_rgb_endian_t;
 
 
+/**
+ * @brief LCD color space
+ */
+typedef enum {
+    LCD_COLOR_SPACE_RGB, /*!< Color space: RGB */
+    LCD_COLOR_SPACE_YUV, /*!< Color space: YUV */
+} lcd_color_space_t;
+
+/**
+ * @brief LCD color range
+ */
+typedef enum {
+    LCD_COLOR_RANGE_LIMIT, /*!< Limited color range */
+    LCD_COLOR_RANGE_FULL,  /*!< Full color range */
+} lcd_color_range_t;
+
+/**
+ * @brief YUV sampling method
+ */
+typedef enum {
+    LCD_YUV_SAMPLE_422, /*!< YUV 4:2:2 sampling */
+    LCD_YUV_SAMPLE_420, /*!< YUV 4:2:0 sampling */
+    LCD_YUV_SAMPLE_411, /*!< YUV 4:1:1 sampling */
+} lcd_yuv_sample_t;
+
+/**
+ * @brief The standard used for conversion between RGB and YUV
+ */
+typedef enum {
+    LCD_YUV_CONV_STD_BT601, /*!< YUV<->RGB conversion standard: BT.601 */
+    LCD_YUV_CONV_STD_BT709, /*!< YUV<->RGB conversion standard: BT.709 */
+} lcd_yuv_conv_std_t;
+
 #ifdef __cplusplus
 #ifdef __cplusplus
 }
 }
 #endif
 #endif

+ 4 - 0
components/soc/esp32s3/include/soc/Kconfig.soc_caps.in

@@ -603,6 +603,10 @@ config SOC_LCD_RGB_DATA_WIDTH
     int
     int
     default 16
     default 16
 
 
+config SOC_LCD_SUPPORT_RGB_YUV_CONV
+    bool
+    default y
+
 config SOC_RTC_CNTL_CPU_PD_DMA_BUS_WIDTH
 config SOC_RTC_CNTL_CPU_PD_DMA_BUS_WIDTH
     int
     int
     default 128
     default 128

+ 1 - 0
components/soc/esp32s3/include/soc/soc_caps.h

@@ -240,6 +240,7 @@
 #define SOC_LCD_RGB_PANELS              (1U) /*!< Support one RGB LCD panel */
 #define SOC_LCD_RGB_PANELS              (1U) /*!< Support one RGB LCD panel */
 #define SOC_LCD_I80_BUS_WIDTH           (16) /*!< Intel 8080 bus width */
 #define SOC_LCD_I80_BUS_WIDTH           (16) /*!< Intel 8080 bus width */
 #define SOC_LCD_RGB_DATA_WIDTH          (16) /*!< Number of LCD data lines */
 #define SOC_LCD_RGB_DATA_WIDTH          (16) /*!< Number of LCD data lines */
+#define SOC_LCD_SUPPORT_RGB_YUV_CONV    (1)  /*!< Support color format conversion between RGB and YUV */
 
 
 /*-------------------------- RTC CAPS --------------------------------------*/
 /*-------------------------- RTC CAPS --------------------------------------*/
 #define SOC_RTC_CNTL_CPU_PD_DMA_BUS_WIDTH       (128)
 #define SOC_RTC_CNTL_CPU_PD_DMA_BUS_WIDTH       (128)

Unele fișiere nu au fost afișate deoarece prea multe fișiere au fost modificate în acest diff