mirror of
https://github.com/espressif/esp-idf.git
synced 2024-10-05 20:47:46 -04:00
feat(lcd): pre-support rgb and i80 lcd driver on esp32p4
added LL functions for LCD_CAM module, only the LCD part
This commit is contained in:
parent
4bb8f5c577
commit
e86acbe556
@ -13,6 +13,8 @@
|
||||
#include "hal/i2c_types.h"
|
||||
#include "driver/i2c_types.h"
|
||||
|
||||
#define ESP_LCD_I80_BUS_WIDTH_MAX 16 /*!< Maximum width of I80 bus */
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
@ -231,7 +233,7 @@ typedef struct {
|
||||
int dc_gpio_num; /*!< GPIO used for D/C line */
|
||||
int wr_gpio_num; /*!< GPIO used for WR line */
|
||||
lcd_clock_source_t clk_src; /*!< Clock source for the I80 LCD peripheral */
|
||||
int data_gpio_nums[SOC_LCD_I80_BUS_WIDTH]; /*!< GPIOs used for data lines */
|
||||
int data_gpio_nums[ESP_LCD_I80_BUS_WIDTH_MAX]; /*!< GPIOs used for data lines */
|
||||
size_t bus_width; /*!< Number of data lines, 8 or 16 */
|
||||
size_t max_transfer_bytes; /*!< Maximum transfer size, this determines the length of internal DMA link */
|
||||
size_t psram_trans_align; /*!< DMA transfer alignment for data allocated from PSRAM */
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2021-2023 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2021-2023 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
@ -24,6 +24,12 @@ extern "C" {
|
||||
|
||||
#define LCD_PERIPH_CLOCK_PRE_SCALE (2) // This is the minimum divider that can be applied to LCD peripheral
|
||||
|
||||
#if SOC_PERIPH_CLK_CTRL_SHARED
|
||||
#define LCD_CLOCK_SRC_ATOMIC() PERIPH_RCC_ATOMIC()
|
||||
#else
|
||||
#define LCD_CLOCK_SRC_ATOMIC()
|
||||
#endif
|
||||
|
||||
#if SOC_LCDCAM_SUPPORTED
|
||||
|
||||
typedef enum {
|
||||
|
@ -128,7 +128,7 @@ esp_err_t esp_lcd_new_i80_bus(const esp_lcd_i80_bus_config_t *bus_config, esp_lc
|
||||
esp_err_t ret = ESP_OK;
|
||||
esp_lcd_i80_bus_t *bus = NULL;
|
||||
ESP_GOTO_ON_FALSE(bus_config && ret_bus, ESP_ERR_INVALID_ARG, err, TAG, "invalid argument");
|
||||
// although I2S bus supports up to 24 parallel data lines, we restrict users to only use 8 or 16 bit width, due to limited GPIO numbers
|
||||
// although I2S bus supports up to 24 parallel data lines, we restrict users to only use 8 or 16 bit width
|
||||
ESP_GOTO_ON_FALSE(bus_config->bus_width == 8 || bus_config->bus_width == 16, ESP_ERR_INVALID_ARG, err,
|
||||
TAG, "invalid bus width:%d", bus_config->bus_width);
|
||||
size_t max_transfer_bytes = (bus_config->max_transfer_bytes + 3) & ~0x03; // align up to 4 bytes
|
||||
|
@ -131,7 +131,10 @@ esp_err_t esp_lcd_new_i80_bus(const esp_lcd_i80_bus_config_t *bus_config, esp_lc
|
||||
#endif
|
||||
esp_err_t ret = ESP_OK;
|
||||
esp_lcd_i80_bus_t *bus = NULL;
|
||||
ESP_GOTO_ON_FALSE(bus_config && ret_bus, ESP_ERR_INVALID_ARG, err, TAG, "invalid argument");
|
||||
ESP_RETURN_ON_FALSE(bus_config && ret_bus, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
|
||||
// although LCD_CAM can support up to 24 data lines, we restrict users to only use 8 or 16 bit width
|
||||
ESP_RETURN_ON_FALSE(bus_config->bus_width == 8 || bus_config->bus_width == 16, ESP_ERR_INVALID_ARG,
|
||||
TAG, "invalid bus width:%d", bus_config->bus_width);
|
||||
size_t num_dma_nodes = bus_config->max_transfer_bytes / DMA_DESCRIPTOR_BUFFER_MAX_SIZE + 1;
|
||||
// DMA descriptors must be placed in internal SRAM
|
||||
bus = heap_caps_calloc(1, sizeof(esp_lcd_i80_bus_t) + num_dma_nodes * sizeof(dma_descriptor_t), MALLOC_CAP_INTERNAL | MALLOC_CAP_DMA);
|
||||
@ -153,13 +156,15 @@ esp_err_t esp_lcd_new_i80_bus(const esp_lcd_i80_bus_config_t *bus_config, esp_lc
|
||||
}
|
||||
// initialize HAL layer, so we can call LL APIs later
|
||||
lcd_hal_init(&bus->hal, bus_id);
|
||||
// reset peripheral and FIFO
|
||||
lcd_ll_reset(bus->hal.dev);
|
||||
lcd_ll_fifo_reset(bus->hal.dev);
|
||||
LCD_CLOCK_SRC_ATOMIC() {
|
||||
lcd_ll_enable_clock(bus->hal.dev, true);
|
||||
}
|
||||
// set peripheral clock resolution
|
||||
ret = lcd_i80_select_periph_clock(bus, bus_config->clk_src);
|
||||
ESP_GOTO_ON_ERROR(ret, err, TAG, "select periph clock %d failed", bus_config->clk_src);
|
||||
// reset peripheral and FIFO after we select a correct clock source
|
||||
lcd_ll_reset(bus->hal.dev);
|
||||
lcd_ll_fifo_reset(bus->hal.dev);
|
||||
// install interrupt service, (LCD peripheral shares the same interrupt source with Camera peripheral with different mask)
|
||||
// interrupt is disabled by default
|
||||
int isr_flags = LCD_I80_INTR_ALLOC_FLAGS | ESP_INTR_FLAG_SHARED | ESP_INTR_FLAG_LOWMED;
|
||||
@ -172,12 +177,17 @@ esp_err_t esp_lcd_new_i80_bus(const esp_lcd_i80_bus_config_t *bus_config, esp_lc
|
||||
// install DMA service
|
||||
bus->psram_trans_align = bus_config->psram_trans_align;
|
||||
bus->sram_trans_align = bus_config->sram_trans_align;
|
||||
bus->bus_width = bus_config->bus_width;
|
||||
ret = lcd_i80_init_dma_link(bus);
|
||||
ESP_GOTO_ON_ERROR(ret, err, TAG, "install DMA failed");
|
||||
// enable 8080 mode and set bus width
|
||||
// disable RGB-LCD mode
|
||||
lcd_ll_enable_rgb_mode(bus->hal.dev, false);
|
||||
lcd_ll_set_data_width(bus->hal.dev, bus_config->bus_width);
|
||||
bus->bus_width = bus_config->bus_width;
|
||||
// disable YUV-RGB converter
|
||||
lcd_ll_enable_rgb_yuv_convert(bus->hal.dev, false);
|
||||
// set how much data to read from DMA each time
|
||||
lcd_ll_set_dma_read_stride(bus->hal.dev, bus->bus_width);
|
||||
// sometime, we need to change the output data order: ABAB->BABA
|
||||
lcd_ll_set_swizzle_mode(bus->hal.dev, LCD_LL_SWIZZLE_AB2BA);
|
||||
// number of data cycles is controlled by DMA buffer size
|
||||
lcd_ll_enable_output_always_on(bus->hal.dev, true);
|
||||
// enable trans done interrupt
|
||||
@ -435,8 +445,9 @@ static esp_err_t panel_io_i80_tx_param(esp_lcd_panel_io_t *io, int lcd_cmd, cons
|
||||
// switch devices if necessary
|
||||
lcd_i80_switch_devices(cur_device, next_device);
|
||||
// set data format
|
||||
lcd_ll_reverse_bit_order(bus->hal.dev, false);
|
||||
lcd_ll_swap_byte_order(bus->hal.dev, bus->bus_width, next_device->lcd_param_bits > bus->bus_width);
|
||||
lcd_ll_reverse_dma_data_bit_order(bus->hal.dev, false);
|
||||
// whether to swap the adjacent data bytes
|
||||
lcd_ll_enable_swizzle(bus->hal.dev, next_device->lcd_param_bits > bus->bus_width);
|
||||
bus->cur_trans = NULL;
|
||||
bus->cur_device = next_device;
|
||||
// package a transaction
|
||||
@ -514,9 +525,11 @@ static esp_err_t lcd_i80_select_periph_clock(esp_lcd_i80_bus_handle_t bus, lcd_c
|
||||
ESP_RETURN_ON_ERROR(esp_clk_tree_src_get_freq_hz((soc_module_clk_t)clk_src, ESP_CLK_TREE_SRC_FREQ_PRECISION_CACHED, &src_clk_hz),
|
||||
TAG, "get clock source frequency failed");
|
||||
|
||||
// force to use integer division, as fractional division might lead to clock jitter
|
||||
LCD_CLOCK_SRC_ATOMIC() {
|
||||
lcd_ll_select_clk_src(bus->hal.dev, clk_src);
|
||||
// force to use integer division, as fractional division might lead to clock jitter
|
||||
lcd_ll_set_group_clock_coeff(bus->hal.dev, LCD_PERIPH_CLOCK_PRE_SCALE, 0, 0);
|
||||
}
|
||||
|
||||
// save the resolution of the i80 bus
|
||||
bus->resolution_hz = src_clk_hz / LCD_PERIPH_CLOCK_PRE_SCALE;
|
||||
@ -578,6 +591,8 @@ static esp_err_t lcd_i80_bus_configure_gpio(esp_lcd_i80_bus_handle_t bus, const
|
||||
if (!valid_gpio) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
// Set the number of output data lines
|
||||
lcd_ll_set_data_wire_width(bus->hal.dev, bus_config->bus_width);
|
||||
// connect peripheral signals via GPIO matrix
|
||||
for (size_t i = 0; i < bus_config->bus_width; i++) {
|
||||
gpio_set_direction(bus_config->data_gpio_nums[i], GPIO_MODE_OUTPUT);
|
||||
@ -700,8 +715,8 @@ IRAM_ATTR static void lcd_default_isr_handler(void *args)
|
||||
// switch devices if necessary
|
||||
lcd_i80_switch_devices(cur_device, next_device);
|
||||
// only reverse data bit/bytes for color data
|
||||
lcd_ll_reverse_bit_order(bus->hal.dev, next_device->flags.reverse_color_bits);
|
||||
lcd_ll_swap_byte_order(bus->hal.dev, bus->bus_width, next_device->flags.swap_color_bytes);
|
||||
lcd_ll_reverse_dma_data_bit_order(bus->hal.dev, next_device->flags.reverse_color_bits);
|
||||
lcd_ll_enable_swizzle(bus->hal.dev, next_device->flags.swap_color_bytes);
|
||||
bus->cur_trans = trans_desc;
|
||||
bus->cur_device = next_device;
|
||||
// mount data to DMA links
|
||||
|
@ -35,6 +35,7 @@
|
||||
#include "esp_private/periph_ctrl.h"
|
||||
#include "esp_psram.h"
|
||||
#include "esp_lcd_common.h"
|
||||
#include "esp_memory_utils.h"
|
||||
#include "soc/lcd_periph.h"
|
||||
#include "hal/lcd_hal.h"
|
||||
#include "hal/lcd_ll.h"
|
||||
@ -176,7 +177,9 @@ static esp_err_t lcd_rgb_panel_alloc_frame_buffers(const esp_lcd_rgb_panel_confi
|
||||
|
||||
static esp_err_t lcd_rgb_panel_destory(esp_rgb_panel_t *rgb_panel)
|
||||
{
|
||||
LCD_CLOCK_SRC_ATOMIC() {
|
||||
lcd_ll_enable_clock(rgb_panel->hal.dev, false);
|
||||
}
|
||||
if (rgb_panel->panel_id >= 0) {
|
||||
PERIPH_RCC_RELEASE_ATOMIC(lcd_periph_signals.panels[rgb_panel->panel_id].module, ref_count) {
|
||||
if (ref_count == 0) {
|
||||
@ -218,19 +221,20 @@ esp_err_t esp_lcd_new_rgb_panel(const esp_lcd_rgb_panel_config_t *rgb_panel_conf
|
||||
#endif
|
||||
esp_err_t ret = ESP_OK;
|
||||
esp_rgb_panel_t *rgb_panel = NULL;
|
||||
ESP_GOTO_ON_FALSE(rgb_panel_config && ret_panel, ESP_ERR_INVALID_ARG, err, TAG, "invalid parameter");
|
||||
ESP_GOTO_ON_FALSE(rgb_panel_config->data_width == 16 || rgb_panel_config->data_width == 8,
|
||||
ESP_ERR_NOT_SUPPORTED, err, TAG, "unsupported data width %d", rgb_panel_config->data_width);
|
||||
ESP_GOTO_ON_FALSE(!(rgb_panel_config->flags.double_fb && rgb_panel_config->flags.no_fb),
|
||||
ESP_ERR_INVALID_ARG, err, TAG, "double_fb conflicts with no_fb");
|
||||
ESP_GOTO_ON_FALSE(!(rgb_panel_config->num_fbs > 0 && rgb_panel_config->num_fbs != 2 && rgb_panel_config->flags.double_fb),
|
||||
ESP_ERR_INVALID_ARG, err, TAG, "num_fbs conflicts with double_fb");
|
||||
ESP_GOTO_ON_FALSE(!(rgb_panel_config->num_fbs > 0 && rgb_panel_config->flags.no_fb),
|
||||
ESP_ERR_INVALID_ARG, err, TAG, "num_fbs conflicts with no_fb");
|
||||
ESP_GOTO_ON_FALSE(!(rgb_panel_config->flags.no_fb && rgb_panel_config->bounce_buffer_size_px == 0),
|
||||
ESP_ERR_INVALID_ARG, err, TAG, "must set bounce buffer if there's no frame buffer");
|
||||
ESP_GOTO_ON_FALSE(!(rgb_panel_config->flags.refresh_on_demand && rgb_panel_config->bounce_buffer_size_px),
|
||||
ESP_ERR_INVALID_ARG, err, TAG, "refresh on demand is not supported under bounce buffer mode");
|
||||
ESP_RETURN_ON_FALSE(rgb_panel_config && ret_panel, ESP_ERR_INVALID_ARG, TAG, "invalid parameter");
|
||||
size_t data_width = rgb_panel_config->data_width;
|
||||
ESP_RETURN_ON_FALSE((data_width >= 8) && (data_width <= SOC_LCD_RGB_DATA_WIDTH) && ((data_width & (data_width - 1)) == 0), ESP_ERR_INVALID_ARG,
|
||||
TAG, "unsupported data width %d", data_width);
|
||||
ESP_RETURN_ON_FALSE(!(rgb_panel_config->flags.double_fb && rgb_panel_config->flags.no_fb),
|
||||
ESP_ERR_INVALID_ARG, TAG, "double_fb conflicts with no_fb");
|
||||
ESP_RETURN_ON_FALSE(!(rgb_panel_config->num_fbs > 0 && rgb_panel_config->num_fbs != 2 && rgb_panel_config->flags.double_fb),
|
||||
ESP_ERR_INVALID_ARG, TAG, "num_fbs conflicts with double_fb");
|
||||
ESP_RETURN_ON_FALSE(!(rgb_panel_config->num_fbs > 0 && rgb_panel_config->flags.no_fb),
|
||||
ESP_ERR_INVALID_ARG, TAG, "num_fbs conflicts with no_fb");
|
||||
ESP_RETURN_ON_FALSE(!(rgb_panel_config->flags.no_fb && rgb_panel_config->bounce_buffer_size_px == 0),
|
||||
ESP_ERR_INVALID_ARG, TAG, "must set bounce buffer if there's no frame buffer");
|
||||
ESP_RETURN_ON_FALSE(!(rgb_panel_config->flags.refresh_on_demand && rgb_panel_config->bounce_buffer_size_px),
|
||||
ESP_ERR_INVALID_ARG, TAG, "refresh on demand is not supported under bounce buffer mode");
|
||||
|
||||
// determine number of framebuffers
|
||||
size_t num_fbs = 1;
|
||||
@ -241,11 +245,11 @@ esp_err_t esp_lcd_new_rgb_panel(const esp_lcd_rgb_panel_config_t *rgb_panel_conf
|
||||
} else if (rgb_panel_config->num_fbs > 0) {
|
||||
num_fbs = rgb_panel_config->num_fbs;
|
||||
}
|
||||
ESP_GOTO_ON_FALSE(num_fbs <= RGB_LCD_PANEL_MAX_FB_NUM, ESP_ERR_INVALID_ARG, err, TAG, "too many frame buffers");
|
||||
ESP_RETURN_ON_FALSE(num_fbs <= RGB_LCD_PANEL_MAX_FB_NUM, ESP_ERR_INVALID_ARG, TAG, "too many frame buffers");
|
||||
|
||||
// bpp defaults to the number of data lines, but for serial RGB interface, they're not equal
|
||||
// e.g. for serial RGB 8-bit interface, data lines are 8, whereas the bpp is 24 (RGB888)
|
||||
size_t fb_bits_per_pixel = rgb_panel_config->data_width;
|
||||
size_t fb_bits_per_pixel = data_width;
|
||||
if (rgb_panel_config->bits_per_pixel) { // override bpp if it's set
|
||||
fb_bits_per_pixel = rgb_panel_config->bits_per_pixel;
|
||||
}
|
||||
@ -255,7 +259,7 @@ esp_err_t esp_lcd_new_rgb_panel(const esp_lcd_rgb_panel_config_t *rgb_panel_conf
|
||||
size_t expect_bb_eof_count = 0;
|
||||
if (bb_size) {
|
||||
// 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_RETURN_ON_FALSE(fb_size % (2 * bb_size) == 0, ESP_ERR_INVALID_ARG, TAG,
|
||||
"fb size must be even multiple of bounce buffer size");
|
||||
expect_bb_eof_count = fb_size / bb_size;
|
||||
}
|
||||
@ -299,11 +303,16 @@ esp_err_t esp_lcd_new_rgb_panel(const esp_lcd_rgb_panel_config_t *rgb_panel_conf
|
||||
|
||||
// initialize HAL layer, so we can call LL APIs later
|
||||
lcd_hal_init(&rgb_panel->hal, panel_id);
|
||||
// enable clock gating
|
||||
// enable clock
|
||||
LCD_CLOCK_SRC_ATOMIC() {
|
||||
lcd_ll_enable_clock(rgb_panel->hal.dev, true);
|
||||
}
|
||||
// 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");
|
||||
// reset peripheral and FIFO after we select a correct clock source
|
||||
lcd_ll_fifo_reset(rgb_panel->hal.dev);
|
||||
lcd_ll_reset(rgb_panel->hal.dev);
|
||||
// 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)) {
|
||||
@ -512,13 +521,17 @@ 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->lcd_clk_flags);
|
||||
hal_utils_clk_div_t lcd_clk_div = {};
|
||||
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, &lcd_clk_div);
|
||||
LCD_CLOCK_SRC_ATOMIC() {
|
||||
lcd_ll_set_group_clock_coeff(rgb_panel->hal.dev, lcd_clk_div.integer, lcd_clk_div.denominator, lcd_clk_div.numerator);
|
||||
}
|
||||
// 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);
|
||||
// enable RGB mode and set data width
|
||||
lcd_ll_enable_rgb_mode(rgb_panel->hal.dev, true);
|
||||
lcd_ll_set_data_width(rgb_panel->hal.dev, rgb_panel->data_width);
|
||||
lcd_ll_set_dma_read_stride(rgb_panel->hal.dev, rgb_panel->data_width);
|
||||
lcd_ll_set_phase_cycles(rgb_panel->hal.dev, 0, 0, 1); // enable data phase only
|
||||
// number of data cycles is controlled by DMA buffer size
|
||||
lcd_ll_enable_output_always_on(rgb_panel->hal.dev, true);
|
||||
@ -829,6 +842,8 @@ static esp_err_t lcd_rgb_panel_configure_gpio(esp_rgb_panel_t *panel, const esp_
|
||||
if (!valid_gpio) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
// Set the number of output data lines
|
||||
lcd_ll_set_data_wire_width(panel->hal.dev, panel_config->data_width);
|
||||
// connect peripheral signals via GPIO matrix
|
||||
for (size_t i = 0; i < panel_config->data_width; i++) {
|
||||
gpio_hal_iomux_func_sel(GPIO_PIN_MUX_REG[panel_config->data_gpio_nums[i]], PIN_FUNC_GPIO);
|
||||
@ -878,7 +893,9 @@ static esp_err_t lcd_rgb_panel_select_clock_src(esp_rgb_panel_t *panel, lcd_cloc
|
||||
ESP_RETURN_ON_ERROR(esp_clk_tree_src_get_freq_hz((soc_module_clk_t)clk_src, ESP_CLK_TREE_SRC_FREQ_PRECISION_CACHED, &src_clk_hz),
|
||||
TAG, "get clock source frequency failed");
|
||||
panel->src_clk_hz = src_clk_hz;
|
||||
LCD_CLOCK_SRC_ATOMIC() {
|
||||
lcd_ll_select_clk_src(panel->hal.dev, clk_src);
|
||||
}
|
||||
|
||||
// create pm lock based on different clock source
|
||||
// clock sources like PLL and XTAL will be turned off in light sleep
|
||||
@ -1099,10 +1116,14 @@ static void lcd_rgb_panel_start_transmission(esp_rgb_panel_t *rgb_panel)
|
||||
|
||||
IRAM_ATTR static void lcd_rgb_panel_try_update_pclk(esp_rgb_panel_t *rgb_panel)
|
||||
{
|
||||
hal_utils_clk_div_t lcd_clk_div = {};
|
||||
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->lcd_clk_flags);
|
||||
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, &lcd_clk_div);
|
||||
LCD_CLOCK_SRC_ATOMIC() {
|
||||
lcd_ll_set_group_clock_coeff(rgb_panel->hal.dev, lcd_clk_div.integer, lcd_clk_div.denominator, lcd_clk_div.numerator);
|
||||
}
|
||||
}
|
||||
portEXIT_CRITICAL_ISR(&rgb_panel->spinlock);
|
||||
}
|
||||
|
@ -6,5 +6,6 @@ CONFIG_GDMA_ISR_IRAM_SAFE=y
|
||||
CONFIG_COMPILER_OPTIMIZATION_NONE=y
|
||||
# silent the error check, as the error string are stored in rodata, causing RTL check failure
|
||||
CONFIG_COMPILER_OPTIMIZATION_CHECKS_SILENT=y
|
||||
CONFIG_COMPILER_OPTIMIZATION_ASSERTIONS_SILENT=y
|
||||
# place non-ISR FreeRTOS functions in Flash
|
||||
CONFIG_FREERTOS_PLACE_FUNCTIONS_INTO_FLASH=y
|
||||
|
@ -32,16 +32,8 @@ static inline uint32_t periph_ll_get_clk_en_mask(periph_module_t periph)
|
||||
// IDF-6500
|
||||
case PERIPH_MIPI_CSI_MODULE:
|
||||
return 0;
|
||||
case PERIPH_I2C0_MODULE:
|
||||
return HP_SYS_CLKRST_REG_I2C0_APB_CLK_EN;
|
||||
case PERIPH_I2C1_MODULE:
|
||||
return HP_SYS_CLKRST_REG_I2C1_APB_CLK_EN;
|
||||
case PERIPH_LCD_MODULE:
|
||||
return HP_SYS_CLKRST_REG_LCD_CLK_EN;
|
||||
case PERIPH_I3C_MODULE:
|
||||
return HP_SYS_CLKRST_REG_I3C_MST_CLK_EN;
|
||||
case PERIPH_CAM_MODULE:
|
||||
return HP_SYS_CLKRST_REG_CAM_CLK_EN;
|
||||
case PERIPH_SYSTIMER_MODULE:
|
||||
return HP_SYS_CLKRST_REG_SYSTIMER_CLK_EN;
|
||||
case PERIPH_SARADC_MODULE:
|
||||
@ -99,12 +91,6 @@ static inline uint32_t periph_ll_get_rst_en_mask(periph_module_t periph, bool en
|
||||
return HP_SYS_CLKRST_REG_RST_EN_UHCI;
|
||||
case PERIPH_I3C_MODULE:
|
||||
return HP_SYS_CLKRST_REG_RST_EN_I3CMST | HP_SYS_CLKRST_REG_RST_EN_I3CSLV;
|
||||
case PERIPH_I2C0_MODULE:
|
||||
return HP_SYS_CLKRST_REG_RST_EN_I2C0;
|
||||
case PERIPH_I2C1_MODULE:
|
||||
return HP_SYS_CLKRST_REG_RST_EN_I2C1;
|
||||
case PERIPH_LCD_MODULE:
|
||||
return HP_SYS_CLKRST_REG_RST_EN_LCDCAM;
|
||||
case PERIPH_SARADC_MODULE:
|
||||
return HP_SYS_CLKRST_REG_RST_EN_ADC;
|
||||
case PERIPH_AES_MODULE:
|
||||
@ -157,15 +143,7 @@ static inline uint32_t periph_ll_get_clk_en_reg(periph_module_t periph)
|
||||
return HP_SYS_CLKRST_PERI_CLK_CTRL00_REG;
|
||||
case PERIPH_MIPI_DSI_MODULE:
|
||||
return HP_SYS_CLKRST_PERI_CLK_CTRL03_REG;
|
||||
case PERIPH_I2C0_MODULE:
|
||||
case PERIPH_I2C1_MODULE:
|
||||
return HP_SYS_CLKRST_SOC_CLK_CTRL2_REG;
|
||||
case PERIPH_LCD_MODULE:
|
||||
return HP_SYS_CLKRST_PERI_CLK_CTRL110_REG;
|
||||
return HP_SYS_CLKRST_PERI_CLK_CTRL116_REG;
|
||||
case PERIPH_I3C_MODULE:
|
||||
case PERIPH_CAM_MODULE:
|
||||
return HP_SYS_CLKRST_PERI_CLK_CTRL119_REG;
|
||||
case PERIPH_SYSTIMER_MODULE:
|
||||
case PERIPH_SARADC_MODULE:
|
||||
return HP_SYS_CLKRST_PERI_CLK_CTRL22_REG;
|
||||
@ -203,10 +181,6 @@ static inline uint32_t periph_ll_get_rst_en_reg(periph_module_t periph)
|
||||
case PERIPH_SYSTIMER_MODULE:
|
||||
case PERIPH_UHCI_MODULE:
|
||||
case PERIPH_I3C_MODULE:
|
||||
case PERIPH_I2C0_MODULE:
|
||||
case PERIPH_I2C1_MODULE:
|
||||
return HP_SYS_CLKRST_HP_RST_EN1_REG;
|
||||
case PERIPH_CAM_MODULE:
|
||||
case PERIPH_SARADC_MODULE:
|
||||
case PERIPH_AES_MODULE:
|
||||
case PERIPH_DS_MODULE:
|
||||
|
766
components/hal/esp32p4/include/hal/lcd_ll.h
Normal file
766
components/hal/esp32p4/include/hal/lcd_ll.h
Normal file
@ -0,0 +1,766 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <stddef.h> /* For NULL declaration */
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include "hal/misc.h"
|
||||
#include "soc/lcd_cam_reg.h"
|
||||
#include "soc/lcd_cam_struct.h"
|
||||
#include "hal/assert.h"
|
||||
#include "hal/lcd_types.h"
|
||||
#include "soc/hp_sys_clkrst_struct.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define LCD_LL_GET_HW(id) (((id) == 0) ? (&LCD_CAM) : NULL)
|
||||
|
||||
// Interrupt event, bit mask
|
||||
#define LCD_LL_EVENT_VSYNC_END (1 << 0)
|
||||
#define LCD_LL_EVENT_TRANS_DONE (1 << 1)
|
||||
|
||||
#define LCD_LL_CLK_FRAC_DIV_N_MAX 256 // LCD_CLK = LCD_CLK_S / (N + b/a), the N register is 8 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
|
||||
|
||||
/**
|
||||
* @brief LCD data byte swizzle mode
|
||||
*/
|
||||
typedef enum {
|
||||
LCD_LL_SWIZZLE_AB2BA, /*!< AB -> BA */
|
||||
LCD_LL_SWIZZLE_ABC2ACB, /*!< ABC -> ACB */
|
||||
LCD_LL_SWIZZLE_ABC2BAC, /*!< ABC -> BAC */
|
||||
LCD_LL_SWIZZLE_ABC2BCA, /*!< ABC -> BCA */
|
||||
LCD_LL_SWIZZLE_ABC2CAB, /*!< ABC -> CAB */
|
||||
LCD_LL_SWIZZLE_ABC2CBA, /*!< ABC -> CBA */
|
||||
} lcd_ll_swizzle_mode_t;
|
||||
|
||||
/**
|
||||
* @brief Enable or disable the bus clock for the LCD module
|
||||
*
|
||||
* @param set_bit True to set bit, false to clear bit
|
||||
*/
|
||||
static inline void lcd_ll_enable_bus_clock(int group_id, bool enable)
|
||||
{
|
||||
(void)group_id;
|
||||
HP_SYS_CLKRST.soc_clk_ctrl3.reg_lcdcam_apb_clk_en = enable;
|
||||
}
|
||||
|
||||
/// use a macro to wrap the function, force the caller to use it in a critical section
|
||||
/// the critical section needs to declare the __DECLARE_RCC_RC_ATOMIC_ENV variable in advance
|
||||
#define lcd_ll_enable_bus_clock(...) (void)__DECLARE_RCC_RC_ATOMIC_ENV; lcd_ll_enable_bus_clock(__VA_ARGS__)
|
||||
|
||||
/**
|
||||
* @brief Reset the LCD module
|
||||
*/
|
||||
static inline void lcd_ll_reset_register(int group_id)
|
||||
{
|
||||
(void)group_id;
|
||||
HP_SYS_CLKRST.hp_rst_en2.reg_rst_en_lcdcam = 1;
|
||||
HP_SYS_CLKRST.hp_rst_en2.reg_rst_en_lcdcam = 0;
|
||||
}
|
||||
|
||||
/// use a macro to wrap the function, force the caller to use it in a critical section
|
||||
/// the critical section needs to declare the __DECLARE_RCC_RC_ATOMIC_ENV variable in advance
|
||||
#define lcd_ll_reset_register(...) (void)__DECLARE_RCC_RC_ATOMIC_ENV; lcd_ll_reset_register(__VA_ARGS__)
|
||||
|
||||
/**
|
||||
* @brief Enable clock gating
|
||||
*
|
||||
* @param dev LCD register base address
|
||||
* @param en True to enable, False to disable
|
||||
*/
|
||||
static inline void lcd_ll_enable_clock(lcd_cam_dev_t *dev, bool en)
|
||||
{
|
||||
HP_SYS_CLKRST.peri_clk_ctrl19.reg_lcd_clk_en = en;
|
||||
}
|
||||
|
||||
/// use a macro to wrap the function, force the caller to use it in a critical section
|
||||
/// the critical section needs to declare the __DECLARE_RCC_ATOMIC_ENV variable in advance
|
||||
#define lcd_ll_enable_clock(...) (void)__DECLARE_RCC_ATOMIC_ENV; lcd_ll_enable_clock(__VA_ARGS__)
|
||||
|
||||
/**
|
||||
* @brief Select clock source for LCD peripheral
|
||||
*
|
||||
* @param dev LCD register base address
|
||||
* @param src Clock source
|
||||
*/
|
||||
static inline void lcd_ll_select_clk_src(lcd_cam_dev_t *dev, lcd_clock_source_t src)
|
||||
{
|
||||
switch (src) {
|
||||
case LCD_CLK_SRC_XTAL:
|
||||
HP_SYS_CLKRST.peri_clk_ctrl19.reg_lcd_clk_src_sel = 0;
|
||||
break;
|
||||
case LCD_CLK_SRC_PLL160M:
|
||||
HP_SYS_CLKRST.peri_clk_ctrl19.reg_lcd_clk_src_sel = 1;
|
||||
break;
|
||||
case LCD_CLK_SRC_APLL:
|
||||
HP_SYS_CLKRST.peri_clk_ctrl19.reg_lcd_clk_src_sel = 2;
|
||||
break;
|
||||
default:
|
||||
// disable the clock
|
||||
HP_SYS_CLKRST.peri_clk_ctrl19.reg_lcd_clk_src_sel = 3;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/// use a macro to wrap the function, force the caller to use it in a critical section
|
||||
/// the critical section needs to declare the __DECLARE_RCC_ATOMIC_ENV variable in advance
|
||||
#define lcd_ll_select_clk_src(...) (void)__DECLARE_RCC_ATOMIC_ENV; lcd_ll_select_clk_src(__VA_ARGS__)
|
||||
|
||||
/**
|
||||
* @brief Set clock coefficient of LCD peripheral
|
||||
*
|
||||
* @param dev LCD register base address
|
||||
* @param div_num Integer part of the divider
|
||||
* @param div_a denominator of the divider
|
||||
* @param div_b numerator of the divider
|
||||
*/
|
||||
__attribute__((always_inline))
|
||||
static inline void lcd_ll_set_group_clock_coeff(lcd_cam_dev_t *dev, int div_num, int div_a, int div_b)
|
||||
{
|
||||
// lcd_clk = module_clock_src / (div_num + div_b / div_a)
|
||||
HAL_ASSERT(div_num >= 2 && div_num <= LCD_LL_CLK_FRAC_DIV_N_MAX);
|
||||
// dic_num == 0 means LCD_LL_CLK_FRAC_DIV_N_MAX divider in hardware
|
||||
if (div_num >= LCD_LL_CLK_FRAC_DIV_N_MAX) {
|
||||
div_num = 0;
|
||||
}
|
||||
HAL_FORCE_MODIFY_U32_REG_FIELD(HP_SYS_CLKRST.peri_clk_ctrl110, reg_lcd_clk_div_num, div_num);
|
||||
HAL_FORCE_MODIFY_U32_REG_FIELD(HP_SYS_CLKRST.peri_clk_ctrl110, reg_lcd_clk_div_denominator, div_a);
|
||||
HAL_FORCE_MODIFY_U32_REG_FIELD(HP_SYS_CLKRST.peri_clk_ctrl110, reg_lcd_clk_div_numerator, div_b);
|
||||
}
|
||||
|
||||
/// use a macro to wrap the function, force the caller to use it in a critical section
|
||||
/// the critical section needs to declare the __DECLARE_RCC_ATOMIC_ENV variable in advance
|
||||
#define lcd_ll_set_group_clock_coeff(...) (void)__DECLARE_RCC_ATOMIC_ENV; lcd_ll_set_group_clock_coeff(__VA_ARGS__)
|
||||
|
||||
/**
|
||||
* @brief Set the PCLK clock level state when there's no transaction undergoing
|
||||
*
|
||||
* @param dev LCD register base address
|
||||
* @param level 1 is high level, 0 is low level
|
||||
*/
|
||||
__attribute__((always_inline))
|
||||
static inline void lcd_ll_set_clock_idle_level(lcd_cam_dev_t *dev, bool level)
|
||||
{
|
||||
dev->lcd_clock.lcd_ck_idle_edge = level;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set the PCLK sample edge
|
||||
*
|
||||
* @param dev LCD register base address
|
||||
* @param active_on_neg True: sample on negedge, False: sample on posedge
|
||||
*/
|
||||
__attribute__((always_inline))
|
||||
static inline void lcd_ll_set_pixel_clock_edge(lcd_cam_dev_t *dev, bool active_on_neg)
|
||||
{
|
||||
dev->lcd_clock.lcd_ck_out_edge = active_on_neg;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set PCLK prescale
|
||||
*
|
||||
* @param dev LCD register base address
|
||||
* @param prescale Prescale value, PCLK = LCD_CLK / prescale
|
||||
*/
|
||||
__attribute__((always_inline))
|
||||
static inline void lcd_ll_set_pixel_clock_prescale(lcd_cam_dev_t *dev, uint32_t prescale)
|
||||
{
|
||||
HAL_ASSERT(prescale <= LCD_LL_PCLK_DIV_MAX);
|
||||
// Formula: pixel_clk = lcd_clk / (1 + clkcnt_n)
|
||||
// clkcnt_n can't be zero
|
||||
uint32_t scale = 1;
|
||||
if (prescale == 1) {
|
||||
dev->lcd_clock.lcd_clk_equ_sysclk = 1;
|
||||
} else {
|
||||
dev->lcd_clock.lcd_clk_equ_sysclk = 0;
|
||||
scale = prescale - 1;
|
||||
}
|
||||
dev->lcd_clock.lcd_clkcnt_n = scale;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Enable YUV-RGB converter
|
||||
*
|
||||
* @param dev LCD register base address
|
||||
* @param en True to enable converter, False to disable converter
|
||||
*/
|
||||
static inline void lcd_ll_enable_rgb_yuv_convert(lcd_cam_dev_t *dev, bool en)
|
||||
{
|
||||
dev->lcd_rgb_yuv.lcd_conv_enable = en;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set convert data line width
|
||||
*
|
||||
* @param dev LCD register base address
|
||||
* @param width data line width
|
||||
*/
|
||||
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)
|
||||
{
|
||||
if (range == LCD_COLOR_RANGE_LIMIT) {
|
||||
dev->lcd_rgb_yuv.lcd_conv_data_in_mode = 0;
|
||||
} else if (range == LCD_COLOR_RANGE_FULL) {
|
||||
dev->lcd_rgb_yuv.lcd_conv_data_in_mode = 1;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @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)
|
||||
{
|
||||
if (range == LCD_COLOR_RANGE_LIMIT) {
|
||||
dev->lcd_rgb_yuv.lcd_conv_data_out_mode = 0;
|
||||
} else if (range == LCD_COLOR_RANGE_FULL) {
|
||||
dev->lcd_rgb_yuv.lcd_conv_data_out_mode = 1;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @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)
|
||||
{
|
||||
if (std == LCD_YUV_CONV_STD_BT601) {
|
||||
dev->lcd_rgb_yuv.lcd_conv_protocol_mode = 0;
|
||||
} else if (std == LCD_YUV_CONV_STD_BT709) {
|
||||
dev->lcd_rgb_yuv.lcd_conv_protocol_mode = 1;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @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_yuv2yuv_mode = 3;
|
||||
switch (yuv_sample) {
|
||||
case LCD_YUV_SAMPLE_422:
|
||||
dev->lcd_rgb_yuv.lcd_conv_yuv_mode = 0;
|
||||
break;
|
||||
case LCD_YUV_SAMPLE_420:
|
||||
dev->lcd_rgb_yuv.lcd_conv_yuv_mode = 1;
|
||||
break;
|
||||
case LCD_YUV_SAMPLE_411:
|
||||
dev->lcd_rgb_yuv.lcd_conv_yuv_mode = 2;
|
||||
break;
|
||||
default:
|
||||
abort();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @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_yuv2yuv_mode = 3;
|
||||
switch (yuv_sample) {
|
||||
case LCD_YUV_SAMPLE_422:
|
||||
dev->lcd_rgb_yuv.lcd_conv_yuv_mode = 0;
|
||||
break;
|
||||
case LCD_YUV_SAMPLE_420:
|
||||
dev->lcd_rgb_yuv.lcd_conv_yuv_mode = 1;
|
||||
break;
|
||||
case LCD_YUV_SAMPLE_411:
|
||||
dev->lcd_rgb_yuv.lcd_conv_yuv_mode = 2;
|
||||
break;
|
||||
default:
|
||||
abort();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @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;
|
||||
switch (src_sample) {
|
||||
case LCD_YUV_SAMPLE_422:
|
||||
dev->lcd_rgb_yuv.lcd_conv_yuv_mode = 0;
|
||||
break;
|
||||
case LCD_YUV_SAMPLE_420:
|
||||
dev->lcd_rgb_yuv.lcd_conv_yuv_mode = 1;
|
||||
break;
|
||||
case LCD_YUV_SAMPLE_411:
|
||||
dev->lcd_rgb_yuv.lcd_conv_yuv_mode = 2;
|
||||
break;
|
||||
default:
|
||||
abort();
|
||||
}
|
||||
switch (dst_sample) {
|
||||
case LCD_YUV_SAMPLE_422:
|
||||
dev->lcd_rgb_yuv.lcd_conv_yuv2yuv_mode = 0;
|
||||
break;
|
||||
case LCD_YUV_SAMPLE_420:
|
||||
dev->lcd_rgb_yuv.lcd_conv_yuv2yuv_mode = 1;
|
||||
break;
|
||||
case LCD_YUV_SAMPLE_411:
|
||||
dev->lcd_rgb_yuv.lcd_conv_yuv2yuv_mode = 2;
|
||||
break;
|
||||
default:
|
||||
abort();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set clock cycles of each transaction phases
|
||||
*
|
||||
* @param dev LCD register base address
|
||||
* @param cmd_cycles Clock cycles of CMD phase
|
||||
* @param dummy_cycles Clock cycles of DUMMY phase
|
||||
* @param data_cycles Clock cycles of DATA phase
|
||||
*/
|
||||
__attribute__((always_inline))
|
||||
static inline void lcd_ll_set_phase_cycles(lcd_cam_dev_t *dev, uint32_t cmd_cycles, uint32_t dummy_cycles, uint32_t data_cycles)
|
||||
{
|
||||
HAL_ASSERT(cmd_cycles <= 2);
|
||||
dev->lcd_user.lcd_cmd = (cmd_cycles > 0);
|
||||
dev->lcd_user.lcd_dummy = (dummy_cycles > 0);
|
||||
dev->lcd_user.lcd_dout = (data_cycles > 0);
|
||||
dev->lcd_user.lcd_cmd_2_cycle_en = cmd_cycles > 1;
|
||||
dev->lcd_user.lcd_dummy_cyclelen = dummy_cycles - 1;
|
||||
dev->lcd_user.lcd_dout_cyclelen = data_cycles - 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set clock cycles of blank phases
|
||||
*
|
||||
* @param dev LCD register base address
|
||||
* @param fk_cycles Clock cycles of front blank
|
||||
* @param bk_cycles Clock cycles of back blank
|
||||
*/
|
||||
static inline void lcd_ll_set_blank_cycles(lcd_cam_dev_t *dev, uint32_t fk_cycles, uint32_t bk_cycles)
|
||||
{
|
||||
dev->lcd_misc.lcd_bk_en = (fk_cycles || bk_cycles);
|
||||
dev->lcd_misc.lcd_vfk_cyclelen = fk_cycles - 1;
|
||||
dev->lcd_misc.lcd_vbk_cyclelen = bk_cycles - 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set data read stride, i.e., number of bytes the LCD reads from the DMA in each step
|
||||
*
|
||||
* @param dev LCD register base address
|
||||
* @param stride data stride size, in bits
|
||||
*/
|
||||
static inline void lcd_ll_set_dma_read_stride(lcd_cam_dev_t *dev, uint32_t stride)
|
||||
{
|
||||
switch (stride) {
|
||||
case 8:
|
||||
dev->lcd_user.lcd_byte_mode = 0;
|
||||
break;
|
||||
case 16:
|
||||
dev->lcd_user.lcd_byte_mode = 1;
|
||||
break;
|
||||
case 24:
|
||||
dev->lcd_user.lcd_byte_mode = 2;
|
||||
break;
|
||||
case 32:
|
||||
dev->lcd_user.lcd_byte_mode = 3;
|
||||
break;
|
||||
default:
|
||||
abort();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set the wire width of LCD output
|
||||
*
|
||||
* @param dev LCD register base address
|
||||
* @param width LCD output wire width
|
||||
*/
|
||||
static inline void lcd_ll_set_data_wire_width(lcd_cam_dev_t *dev, uint32_t width)
|
||||
{
|
||||
switch (width) {
|
||||
case 8:
|
||||
dev->lcd_misc.lcd_wire_mode = 0;
|
||||
break;
|
||||
case 16:
|
||||
dev->lcd_misc.lcd_wire_mode = 1;
|
||||
break;
|
||||
case 24:
|
||||
dev->lcd_misc.lcd_wire_mode = 2;
|
||||
break;
|
||||
default:
|
||||
abort();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Whether to continue the data phase when the DMA has content to send
|
||||
*
|
||||
* @param dev LCD register base address
|
||||
* @param en True: The number of data cycles will be controller by DMA buffer size, instead of lcd_dout_cyclelen
|
||||
* False: The number of data cycles will be controlled by lcd_dout_cyclelen
|
||||
*/
|
||||
static inline void lcd_ll_enable_output_always_on(lcd_cam_dev_t *dev, bool en)
|
||||
{
|
||||
dev->lcd_user.lcd_always_out_en = en;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Start the LCD transaction
|
||||
*
|
||||
* @param dev LCD register base address
|
||||
*/
|
||||
__attribute__((always_inline))
|
||||
static inline void lcd_ll_start(lcd_cam_dev_t *dev)
|
||||
{
|
||||
dev->lcd_user.lcd_update_reg = 1; // update parameters before start transaction
|
||||
dev->lcd_user.lcd_start = 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Stop the LCD transaction
|
||||
*
|
||||
* @param dev LCD register base address
|
||||
*/
|
||||
__attribute__((always_inline))
|
||||
static inline void lcd_ll_stop(lcd_cam_dev_t *dev)
|
||||
{
|
||||
dev->lcd_user.lcd_start = 0;
|
||||
dev->lcd_user.lcd_update_reg = 1; // self clear
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Reset LCD TX controller and RGB/YUV converter
|
||||
*
|
||||
* @param dev LCD register base address
|
||||
*/
|
||||
static inline void lcd_ll_reset(lcd_cam_dev_t *dev)
|
||||
{
|
||||
dev->lcd_user.lcd_reset = 1; // self clear
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Whether to reverse the data bit order
|
||||
*
|
||||
* @note It acts before the YUV-RGB converter
|
||||
*
|
||||
* @param dev LCD register base address
|
||||
* @param en True to reverse, False to not reverse
|
||||
*/
|
||||
__attribute__((always_inline))
|
||||
static inline void lcd_ll_reverse_dma_data_bit_order(lcd_cam_dev_t *dev, bool en)
|
||||
{
|
||||
dev->lcd_user.lcd_bit_order = en;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Whether to reverse the output data bit order
|
||||
*
|
||||
* @note It acts after the YUV-RGB converter
|
||||
*
|
||||
* @param dev LCD register base address
|
||||
* @param en True to reverse, False to not reverse
|
||||
*/
|
||||
static inline void lcd_ll_reverse_wire_bit_order(lcd_cam_dev_t *dev, bool en)
|
||||
{
|
||||
dev->lcd_user.lcd_dout_bit_order = en;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Whether to swap adjacent two bytes
|
||||
*
|
||||
* @note This acts before the YUV-RGB converter, mainly to change the data endian.
|
||||
* {B1,B0},{B3,B2} => {B0,B1}{B2,B3}
|
||||
*
|
||||
* @param dev LCD register base address
|
||||
* @param en True to swap the byte order, False to not swap
|
||||
*/
|
||||
__attribute__((always_inline))
|
||||
static inline void lcd_ll_swap_dma_data_byte_order(lcd_cam_dev_t *dev, bool en)
|
||||
{
|
||||
dev->lcd_user.lcd_byte_order = en;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Enable the byte swizzle
|
||||
*
|
||||
* @note The swizzle module acts after the YUV-RGB converter, used to reorder the data bytes before the data output line
|
||||
*
|
||||
* @param dev LCD register base address
|
||||
* @param en True to enable, False to disable
|
||||
*/
|
||||
__attribute__((always_inline))
|
||||
static inline void lcd_ll_enable_swizzle(lcd_cam_dev_t *dev, bool en)
|
||||
{
|
||||
dev->lcd_user.lcd_dout_byte_swizzle_enable = en;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set data byte swizzle mode
|
||||
*
|
||||
* @param dev LCD register base address
|
||||
* @param mode Swizzle mode
|
||||
*/
|
||||
static inline void lcd_ll_set_swizzle_mode(lcd_cam_dev_t *dev, lcd_ll_swizzle_mode_t mode)
|
||||
{
|
||||
dev->lcd_user.lcd_dout_byte_swizzle_mode = mode;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Reset Async TX FIFO
|
||||
*
|
||||
* @param dev LCD register base address
|
||||
*/
|
||||
__attribute__((always_inline))
|
||||
static inline void lcd_ll_fifo_reset(lcd_cam_dev_t *dev)
|
||||
{
|
||||
dev->lcd_misc.lcd_afifo_reset = 1; // self clear
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set the level state of DC line, on different transaction phases
|
||||
*
|
||||
* @param dev LCD register base address
|
||||
* @param idle_phase Level state of DC line on IDLE phase
|
||||
* @param cmd_phase Level state of DC line on CMD phase
|
||||
* @param dummy_phase Level state of DC line on DUMMY phase
|
||||
* @param data_phase Level state of DC line on DATA phase
|
||||
*/
|
||||
__attribute__((always_inline))
|
||||
static inline void lcd_ll_set_dc_level(lcd_cam_dev_t *dev, bool idle_phase, bool cmd_phase, bool dummy_phase, bool data_phase)
|
||||
{
|
||||
dev->lcd_misc.lcd_cd_idle_edge = idle_phase;
|
||||
dev->lcd_misc.lcd_cd_cmd_set = (cmd_phase != idle_phase);
|
||||
dev->lcd_misc.lcd_cd_dummy_set = (dummy_phase != idle_phase);
|
||||
dev->lcd_misc.lcd_cd_data_set = (data_phase != idle_phase);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set cycle of delay for DC line
|
||||
*
|
||||
* @param dev LCD register base address
|
||||
* @param delay Ticks of delay
|
||||
*/
|
||||
static inline void lcd_ll_set_dc_delay_ticks(lcd_cam_dev_t *dev, uint32_t delay)
|
||||
{
|
||||
dev->lcd_dly_mode_cfg1.lcd_cd_mode = delay;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set the LCD command (the data at CMD phase)
|
||||
*
|
||||
* @param dev LCD register base address
|
||||
* @param data_width Data line width
|
||||
* @param command command value
|
||||
*/
|
||||
__attribute__((always_inline))
|
||||
static inline void lcd_ll_set_command(lcd_cam_dev_t *dev, uint32_t data_width, uint32_t command)
|
||||
{
|
||||
// i80 interface only supports 8-bit or 16-bit data width
|
||||
HAL_ASSERT(data_width == 8 || data_width == 16);
|
||||
// if command phase has two cycles, in the first cycle we use lcd_first_cmd_val
|
||||
// in the second cycle, we use lcd_latter_cmd_val
|
||||
if (data_width == 8) {
|
||||
dev->lcd_first_cmd_val.val = command & 0xFF;
|
||||
dev->lcd_latter_cmd_val.val = (command >> 8) & 0xFF;
|
||||
} else if (data_width == 16) {
|
||||
dev->lcd_first_cmd_val.val = command;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Wether to enable RGB interface
|
||||
*
|
||||
* @param dev LCD register base address
|
||||
* @param en True to enable RGB interface, False to disable RGB interface
|
||||
*/
|
||||
static inline void lcd_ll_enable_rgb_mode(lcd_cam_dev_t *dev, bool en)
|
||||
{
|
||||
dev->lcd_ctrl.lcd_rgb_mode_en = en;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Whether to send the next frame automatically
|
||||
*
|
||||
* @param dev LCD register base address
|
||||
* @param en True to enable, False to disable
|
||||
*/
|
||||
static inline void lcd_ll_enable_auto_next_frame(lcd_cam_dev_t *dev, bool en)
|
||||
{
|
||||
// in RGB mode, enabling "next frame" means LCD controller keeps sending frame data
|
||||
dev->lcd_misc.lcd_next_frame_en = en;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Wether to output HSYNC signal in porch resion
|
||||
*
|
||||
* @param dev LCD register base address
|
||||
* @param en True to enable, False to disable
|
||||
*/
|
||||
static inline void lcd_ll_enable_output_hsync_in_porch_region(lcd_cam_dev_t *dev, bool en)
|
||||
{
|
||||
dev->lcd_ctrl2.lcd_hs_blank_en = en;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set HSYNC signal offset in the line
|
||||
*
|
||||
* @param dev LCD register base address
|
||||
* @param offset_in_line Offset value
|
||||
*/
|
||||
static inline void lcd_ll_set_hsync_position(lcd_cam_dev_t *dev, uint32_t offset_in_line)
|
||||
{
|
||||
HAL_FORCE_MODIFY_U32_REG_FIELD(dev->lcd_ctrl2, lcd_hsync_position, offset_in_line);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set RGB LCD horizontal timing
|
||||
*
|
||||
* @param dev LCD register base address
|
||||
* @param hsw Horizontal sync width
|
||||
* @param hbp Horizontal back porch
|
||||
* @param active_width Horizontal active width
|
||||
* @param hfp Horizontal front porch
|
||||
*/
|
||||
static inline void lcd_ll_set_horizontal_timing(lcd_cam_dev_t *dev, uint32_t hsw, uint32_t hbp, uint32_t active_width, uint32_t hfp)
|
||||
{
|
||||
dev->lcd_ctrl2.lcd_hsync_width = hsw - 1;
|
||||
dev->lcd_ctrl.lcd_hb_front = hbp + hsw - 1;
|
||||
dev->lcd_ctrl1.lcd_ha_width = active_width - 1;
|
||||
dev->lcd_ctrl1.lcd_ht_width = hsw + hbp + active_width + hfp - 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set RGB vertical timing
|
||||
*
|
||||
* @param dev LCD register base address
|
||||
* @param vsw Vertical sync width
|
||||
* @param vbp Vertical back porch
|
||||
* @param active_height Vertical active height
|
||||
* @param vfp Vertical front porch
|
||||
*/
|
||||
static inline void lcd_ll_set_vertical_timing(lcd_cam_dev_t *dev, uint32_t vsw, uint32_t vbp, uint32_t active_height, uint32_t vfp)
|
||||
{
|
||||
dev->lcd_ctrl2.lcd_vsync_width = vsw - 1;
|
||||
HAL_FORCE_MODIFY_U32_REG_FIELD(dev->lcd_ctrl1, lcd_vb_front, vbp + vsw - 1);
|
||||
dev->lcd_ctrl.lcd_va_height = active_height - 1;
|
||||
dev->lcd_ctrl.lcd_vt_height = vsw + vbp + active_height + vfp - 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set level state for hsync, vsync, de at IDLE phase
|
||||
*
|
||||
* @param dev LCD register base address
|
||||
* @param hsync_idle_level HSYNC level on IDLE phase
|
||||
* @param vsync_idle_level VSYNC level on IDLE phase
|
||||
* @param de_idle_level DE level on IDLE phase
|
||||
*/
|
||||
static inline void lcd_ll_set_idle_level(lcd_cam_dev_t *dev, bool hsync_idle_level, bool vsync_idle_level, bool de_idle_level)
|
||||
{
|
||||
dev->lcd_ctrl2.lcd_hsync_idle_pol = hsync_idle_level;
|
||||
dev->lcd_ctrl2.lcd_vsync_idle_pol = vsync_idle_level;
|
||||
dev->lcd_ctrl2.lcd_de_idle_pol = de_idle_level;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set extra delay for HSYNC, VSYNC, and DE signals
|
||||
*
|
||||
* @param dev LCD register base address
|
||||
* @param hsync_delay HSYNC delay
|
||||
* @param vsync_delay VSYNC delay
|
||||
* @param de_delay DE delay
|
||||
*/
|
||||
static inline void lcd_ll_set_delay_ticks(lcd_cam_dev_t *dev, uint32_t hsync_delay, uint32_t vsync_delay, uint32_t de_delay)
|
||||
{
|
||||
dev->lcd_dly_mode_cfg1.lcd_hsync_mode = hsync_delay;
|
||||
dev->lcd_dly_mode_cfg1.lcd_vsync_mode = vsync_delay;
|
||||
dev->lcd_dly_mode_cfg1.lcd_de_mode = de_delay;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Enable/disable interrupt by mask
|
||||
*
|
||||
* @param dev LCD register base address
|
||||
* @param mask Interrupt mask
|
||||
* @param en True to enable interrupt, False to disable interrupt
|
||||
*/
|
||||
static inline void lcd_ll_enable_interrupt(lcd_cam_dev_t *dev, uint32_t mask, bool en)
|
||||
{
|
||||
if (en) {
|
||||
dev->lc_dma_int_ena.val |= mask & 0x03;
|
||||
} else {
|
||||
dev->lc_dma_int_ena.val &= ~(mask & 0x03);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get interrupt status value
|
||||
*
|
||||
* @param dev LCD register base address
|
||||
* @return Interrupt status value
|
||||
*/
|
||||
__attribute__((always_inline))
|
||||
static inline uint32_t lcd_ll_get_interrupt_status(lcd_cam_dev_t *dev)
|
||||
{
|
||||
return dev->lc_dma_int_st.val & 0x03;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Clear interrupt status by mask
|
||||
*
|
||||
* @param dev LCD register base address
|
||||
* @param mask Interupt status mask
|
||||
*/
|
||||
__attribute__((always_inline))
|
||||
static inline void lcd_ll_clear_interrupt_status(lcd_cam_dev_t *dev, uint32_t mask)
|
||||
{
|
||||
dev->lc_dma_int_clr.val = mask & 0x03;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get address of interrupt status register address
|
||||
*
|
||||
* @param dev LCD register base address
|
||||
* @return Interrupt status register address
|
||||
*/
|
||||
static inline volatile void *lcd_ll_get_interrupt_status_reg(lcd_cam_dev_t *dev)
|
||||
{
|
||||
return &dev->lc_dma_int_st;
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2021-2023 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
@ -29,6 +29,42 @@ 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_PCLK_DIV_MAX 64 // LCD_PCLK = LCD_CLK / MO, the MO register is 6 bit-width
|
||||
|
||||
/**
|
||||
* @brief LCD data byte swizzle mode
|
||||
*/
|
||||
typedef enum {
|
||||
LCD_LL_SWIZZLE_AB2BA, /*!< AB -> BA */
|
||||
} lcd_ll_swizzle_mode_t;
|
||||
|
||||
/**
|
||||
* @brief Enable or disable the bus clock for the LCD module
|
||||
*
|
||||
* @param set_bit True to set bit, false to clear bit
|
||||
*/
|
||||
static inline void lcd_ll_enable_bus_clock(int group_id, bool enable)
|
||||
{
|
||||
(void)group_id;
|
||||
SYSTEM.perip_clk_en1.lcd_cam_clk_en = enable;
|
||||
}
|
||||
|
||||
/// use a macro to wrap the function, force the caller to use it in a critical section
|
||||
/// the critical section needs to declare the __DECLARE_RCC_RC_ATOMIC_ENV variable in advance
|
||||
#define lcd_ll_enable_bus_clock(...) (void)__DECLARE_RCC_RC_ATOMIC_ENV; lcd_ll_enable_bus_clock(__VA_ARGS__)
|
||||
|
||||
/**
|
||||
* @brief Reset the LCD module
|
||||
*/
|
||||
static inline void lcd_ll_reset_register(int group_id)
|
||||
{
|
||||
(void)group_id;
|
||||
SYSTEM.perip_rst_en1.lcd_cam_rst = 0x01;
|
||||
SYSTEM.perip_rst_en1.lcd_cam_rst = 0x00;
|
||||
}
|
||||
|
||||
/// use a macro to wrap the function, force the caller to use it in a critical section
|
||||
/// the critical section needs to declare the __DECLARE_RCC_RC_ATOMIC_ENV variable in advance
|
||||
#define lcd_ll_reset_register(...) (void)__DECLARE_RCC_RC_ATOMIC_ENV; lcd_ll_reset_register(__VA_ARGS__)
|
||||
|
||||
/**
|
||||
* @brief Enable clock gating
|
||||
*
|
||||
@ -326,15 +362,35 @@ static inline void lcd_ll_set_blank_cycles(lcd_cam_dev_t *dev, uint32_t fk_cycle
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set data line width
|
||||
* @brief Set data read stride, i.e., number of bytes the LCD reads from the DMA in each step
|
||||
*
|
||||
* @param dev LCD register base address
|
||||
* @param width data line width (8 or 16)
|
||||
* @param stride data stride size
|
||||
*/
|
||||
static inline void lcd_ll_set_data_width(lcd_cam_dev_t *dev, uint32_t width)
|
||||
static inline void lcd_ll_set_dma_read_stride(lcd_cam_dev_t *dev, uint32_t stride)
|
||||
{
|
||||
HAL_ASSERT(width == 8 || width == 16);
|
||||
dev->lcd_user.lcd_2byte_en = (width == 16);
|
||||
switch (stride) {
|
||||
case 8:
|
||||
dev->lcd_user.lcd_2byte_en = 0;
|
||||
break;
|
||||
case 16:
|
||||
dev->lcd_user.lcd_2byte_en = 1;
|
||||
break;
|
||||
default:
|
||||
abort();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set the wire width of LCD output
|
||||
*
|
||||
* @param dev LCD register base address
|
||||
* @param width LCD output wire width
|
||||
*/
|
||||
static inline void lcd_ll_set_data_wire_width(lcd_cam_dev_t *dev, uint32_t width)
|
||||
{
|
||||
// data line width is same as data stride that set in `lcd_ll_set_dma_read_stride`
|
||||
}
|
||||
|
||||
/**
|
||||
@ -386,36 +442,69 @@ static inline void lcd_ll_reset(lcd_cam_dev_t *dev)
|
||||
/**
|
||||
* @brief Whether to reverse the data bit order
|
||||
*
|
||||
* @note It acts before the YUV-RGB converter
|
||||
*
|
||||
* @param dev LCD register base address
|
||||
* @param en True to reverse, False to not reverse
|
||||
*/
|
||||
__attribute__((always_inline))
|
||||
static inline void lcd_ll_reverse_bit_order(lcd_cam_dev_t *dev, bool en)
|
||||
static inline void lcd_ll_reverse_dma_data_bit_order(lcd_cam_dev_t *dev, bool en)
|
||||
{
|
||||
// whether to change LCD_DATA_out[N:0] to LCD_DATA_out[0:N]
|
||||
dev->lcd_user.lcd_bit_order = en;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Whether to reverse the output data bit order
|
||||
*
|
||||
* @note ESP32S3 doesn't support to reverse the data bit after the YUV-RGB converter
|
||||
*
|
||||
* @param dev LCD register base address
|
||||
* @param en True to reverse, False to not reverse
|
||||
*/
|
||||
static inline void lcd_ll_reverse_wire_bit_order(lcd_cam_dev_t *dev, bool en)
|
||||
{
|
||||
(void)dev;
|
||||
(void)en;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Whether to swap adjacent two bytes
|
||||
*
|
||||
* @note This acts before the YUV-RGB converter, mainly to change the data endian.
|
||||
* {B1,B0},{B3,B2} => {B0,B1}{B2,B3}
|
||||
*
|
||||
* @param dev LCD register base address
|
||||
* @param width Bus width
|
||||
* @param en True to swap the byte order, False to not swap
|
||||
*/
|
||||
__attribute__((always_inline))
|
||||
static inline void lcd_ll_swap_byte_order(lcd_cam_dev_t *dev, uint32_t width, bool en)
|
||||
static inline void lcd_ll_swap_dma_data_byte_order(lcd_cam_dev_t *dev, bool en)
|
||||
{
|
||||
HAL_ASSERT(width == 8 || width == 16);
|
||||
if (width == 8) {
|
||||
// {B0}{B1}{B2}{B3} => {B1}{B0}{B3}{B2}
|
||||
dev->lcd_user.lcd_8bits_order = en;
|
||||
dev->lcd_user.lcd_byte_order = 0;
|
||||
} else if (width == 16) {
|
||||
// {B1,B0},{B3,B2} => {B0,B1}{B2,B3}
|
||||
dev->lcd_user.lcd_byte_order = en;
|
||||
dev->lcd_user.lcd_8bits_order = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Enable the byte swizzle
|
||||
*
|
||||
* @note The swizzle module acts after the YUV-RGB converter, used to reorder the data bytes before the data wire
|
||||
*
|
||||
* @param dev LCD register base address
|
||||
* @param en True to enable, False to disable
|
||||
*/
|
||||
__attribute__((always_inline))
|
||||
static inline void lcd_ll_enable_swizzle(lcd_cam_dev_t *dev, bool en)
|
||||
{
|
||||
dev->lcd_user.lcd_8bits_order = en;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set data byte swizzle mode
|
||||
*
|
||||
* @param dev LCD register base address
|
||||
* @param mode Swizzle mode
|
||||
*/
|
||||
static inline void lcd_ll_set_swizzle_mode(lcd_cam_dev_t *dev, lcd_ll_swizzle_mode_t mode)
|
||||
{
|
||||
HAL_ASSERT(mode == LCD_LL_SWIZZLE_AB2BA);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -652,35 +741,6 @@ static inline volatile void *lcd_ll_get_interrupt_status_reg(lcd_cam_dev_t *dev)
|
||||
return &dev->lc_dma_int_st;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Enable or disable the bus clock for the LCD module
|
||||
*
|
||||
* @param set_bit True to set bit, false to clear bit
|
||||
*/
|
||||
static inline void lcd_ll_enable_bus_clock(int group_id, bool enable)
|
||||
{
|
||||
(void)group_id;
|
||||
SYSTEM.perip_clk_en1.lcd_cam_clk_en = enable;
|
||||
}
|
||||
|
||||
/// use a macro to wrap the function, force the caller to use it in a critical section
|
||||
/// the critical section needs to declare the __DECLARE_RCC_RC_ATOMIC_ENV variable in advance
|
||||
#define lcd_ll_enable_bus_clock(...) (void)__DECLARE_RCC_RC_ATOMIC_ENV; lcd_ll_enable_bus_clock(__VA_ARGS__)
|
||||
|
||||
/**
|
||||
* @brief Reset the LCD module
|
||||
*/
|
||||
static inline void lcd_ll_reset_register(int group_id)
|
||||
{
|
||||
(void)group_id;
|
||||
SYSTEM.perip_rst_en1.lcd_cam_rst = 0x01;
|
||||
SYSTEM.perip_rst_en1.lcd_cam_rst = 0x00;
|
||||
}
|
||||
|
||||
/// use a macro to wrap the function, force the caller to use it in a critical section
|
||||
/// the critical section needs to declare the __DECLARE_RCC_RC_ATOMIC_ENV variable in advance
|
||||
#define lcd_ll_reset_register(...) (void)__DECLARE_RCC_RC_ATOMIC_ENV; lcd_ll_reset_register(__VA_ARGS__)
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
@ -8,6 +8,7 @@
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include "hal/hal_utils.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
@ -43,9 +44,10 @@ void lcd_hal_init(lcd_hal_context_t *hal, int id);
|
||||
* @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_
|
||||
* @param lcd_clk_div Returned LCD clock divider parameter
|
||||
* @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, int lcd_clk_flags);
|
||||
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, hal_utils_clk_div_t* lcd_clk_div);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
@ -19,7 +19,9 @@ extern "C" {
|
||||
* @brief LCD clock source
|
||||
*/
|
||||
typedef soc_periph_lcd_clk_src_t lcd_clock_source_t;
|
||||
#endif
|
||||
#else
|
||||
typedef int lcd_clock_source_t;
|
||||
#endif // SOC_LCD_I80_SUPPORTED || SOC_LCD_RGB_SUPPORTED
|
||||
|
||||
/**
|
||||
* @brief RGB data endian
|
||||
|
@ -7,14 +7,13 @@
|
||||
#include "hal/lcd_hal.h"
|
||||
#include "hal/lcd_ll.h"
|
||||
#include "hal/log.h"
|
||||
#include "hal/hal_utils.h"
|
||||
|
||||
void lcd_hal_init(lcd_hal_context_t *hal, int id)
|
||||
{
|
||||
hal->dev = LCD_LL_GET_HW(id);
|
||||
}
|
||||
|
||||
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)
|
||||
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, hal_utils_clk_div_t* lcd_clk_div)
|
||||
{
|
||||
// lcd_clk = module_clock_src / (n + b / a)
|
||||
// pixel_clk = lcd_clk / mo
|
||||
@ -29,12 +28,9 @@ uint32_t lcd_hal_cal_pclk_freq(lcd_hal_context_t *hal, uint32_t src_freq_hz, uin
|
||||
.min_integ = 2,
|
||||
.max_fract = LCD_LL_CLK_FRAC_DIV_AB_MAX,
|
||||
};
|
||||
hal_utils_clk_div_t lcd_clk_div = {};
|
||||
uint32_t real_freq = hal_utils_calc_clk_div_frac_fast(&lcd_clk_info, &lcd_clk_div);
|
||||
HAL_EARLY_LOGD("lcd_hal", "n=%"PRIu32",a=%"PRIu32",b=%"PRIu32",mo=%"PRIu32, lcd_clk_div.integer, lcd_clk_div.denominator, lcd_clk_div.numerator, mo);
|
||||
uint32_t real_freq = hal_utils_calc_clk_div_frac_fast(&lcd_clk_info, lcd_clk_div);
|
||||
HAL_EARLY_LOGD("lcd_hal", "n=%"PRIu32",a=%"PRIu32",b=%"PRIu32",mo=%"PRIu32, lcd_clk_div->integer, lcd_clk_div->denominator, lcd_clk_div->numerator, mo);
|
||||
|
||||
lcd_ll_set_group_clock_coeff(hal->dev, lcd_clk_div.integer, lcd_clk_div.denominator, lcd_clk_div.numerator);
|
||||
lcd_ll_set_pixel_clock_prescale(hal->dev, mo);
|
||||
|
||||
return real_freq / mo;
|
||||
}
|
||||
|
@ -727,6 +727,26 @@ config SOC_RMT_SUPPORT_DMA
|
||||
bool
|
||||
default y
|
||||
|
||||
config SOC_LCD_I80_BUSES
|
||||
int
|
||||
default 1
|
||||
|
||||
config SOC_LCD_RGB_PANELS
|
||||
int
|
||||
default 1
|
||||
|
||||
config SOC_LCD_I80_BUS_WIDTH
|
||||
int
|
||||
default 24
|
||||
|
||||
config SOC_LCD_RGB_DATA_WIDTH
|
||||
int
|
||||
default 24
|
||||
|
||||
config SOC_LCD_SUPPORT_RGB_YUV_CONV
|
||||
bool
|
||||
default y
|
||||
|
||||
config SOC_MCPWM_GROUPS
|
||||
int
|
||||
default 2
|
||||
|
@ -372,6 +372,23 @@ typedef enum {
|
||||
I2S_CLK_SRC_EXTERNAL = -1, /*!< Select external clock as source clock */
|
||||
} soc_periph_i2s_clk_src_t;
|
||||
|
||||
//////////////////////////////////////////////////LCD///////////////////////////////////////////////////////////////////
|
||||
|
||||
/**
|
||||
* @brief Array initializer for all supported clock sources of LCD
|
||||
*/
|
||||
#define SOC_LCD_CLKS {SOC_MOD_CLK_PLL_F160M, SOC_MOD_CLK_XTAL, SOC_MOD_CLK_APLL}
|
||||
|
||||
/**
|
||||
* @brief Type of LCD clock source
|
||||
*/
|
||||
typedef enum {
|
||||
LCD_CLK_SRC_PLL160M = SOC_MOD_CLK_PLL_F160M, /*!< Select PLL_F160M as the source clock */
|
||||
LCD_CLK_SRC_XTAL = SOC_MOD_CLK_XTAL, /*!< Select XTAL as the source clock */
|
||||
LCD_CLK_SRC_APLL = SOC_MOD_CLK_APLL, /*!< Select APLL as the source clock */
|
||||
LCD_CLK_SRC_DEFAULT = SOC_MOD_CLK_PLL_F160M, /*!< Select PLL_F160M as the default choice */
|
||||
} soc_periph_lcd_clk_src_t;
|
||||
|
||||
/////////////////////////////////////////////////I2C////////////////////////////////////////////////////////////////////
|
||||
|
||||
/**
|
||||
@ -492,7 +509,7 @@ typedef enum {
|
||||
typedef enum {
|
||||
TWAI_CLK_SRC_XTAL = SOC_MOD_CLK_XTAL, /*!< Select XTAL as the source clock */
|
||||
#if SOC_CLK_TREE_SUPPORTED
|
||||
TWAI_CLK_SRC_RC_FAST = SOC_MOD_CLK_RC_FAST /*!< Select RC_FAST as the source clock */
|
||||
TWAI_CLK_SRC_RC_FAST = SOC_MOD_CLK_RC_FAST, /*!< Select RC_FAST as the source clock */
|
||||
#endif
|
||||
TWAI_CLK_SRC_DEFAULT = SOC_MOD_CLK_XTAL, /*!< Select XTAL as the default clock choice */
|
||||
} soc_periph_twai_clk_src_t;
|
||||
|
@ -820,7 +820,7 @@ typedef union {
|
||||
} lcdcam_lc_reg_date_reg_t;
|
||||
|
||||
|
||||
typedef struct lcdcam_dev_t {
|
||||
typedef struct lcd_cam_dev_t {
|
||||
volatile lcdcam_lcd_clock_reg_t lcd_clock;
|
||||
volatile lcdcam_cam_ctrl_reg_t cam_ctrl;
|
||||
volatile lcdcam_cam_ctrl1_reg_t cam_ctrl1;
|
||||
@ -843,13 +843,15 @@ typedef struct lcdcam_dev_t {
|
||||
volatile lcdcam_lc_dma_int_clr_reg_t lc_dma_int_clr;
|
||||
uint32_t reserved_074[34];
|
||||
volatile lcdcam_lc_reg_date_reg_t lc_reg_date;
|
||||
} lcdcam_dev_t;
|
||||
} lcd_cam_dev_t;
|
||||
|
||||
|
||||
#ifndef __cplusplus
|
||||
_Static_assert(sizeof(lcdcam_dev_t) == 0x100, "Invalid size of lcdcam_dev_t structure");
|
||||
_Static_assert(sizeof(lcd_cam_dev_t) == 0x100, "Invalid size of lcdcam_dev_t structure");
|
||||
#endif
|
||||
|
||||
extern lcd_cam_dev_t LCD_CAM;
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
@ -24,7 +24,7 @@ typedef enum {
|
||||
PERIPH_I2S1_MODULE,
|
||||
PERIPH_I2S2_MODULE,
|
||||
|
||||
PERIPH_LCD_MODULE = 10,
|
||||
PERIPH_LCD_CAM_MODULE = 10,
|
||||
PERIPH_UART0_MODULE,
|
||||
PERIPH_UART1_MODULE,
|
||||
PERIPH_UART2_MODULE,
|
||||
@ -39,7 +39,6 @@ typedef enum {
|
||||
PERIPH_GPSPI3_MODULE,
|
||||
PERIPH_PARLIO_MODULE,
|
||||
PERIPH_I3C_MODULE,
|
||||
PERIPH_CAM_MODULE,
|
||||
PERIPH_MCPWM0_MODULE,
|
||||
PERIPH_MCPWM1_MODULE,
|
||||
PERIPH_TIMG0_MODULE,
|
||||
|
@ -34,6 +34,7 @@
|
||||
#define SOC_AXI_GDMA_SUPPORTED 1
|
||||
#define SOC_GPTIMER_SUPPORTED 1
|
||||
#define SOC_PCNT_SUPPORTED 1
|
||||
// #define SOC_LCDCAM_SUPPORTED 1 // TODO: IDF-7465
|
||||
#define SOC_MCPWM_SUPPORTED 1
|
||||
#define SOC_TWAI_SUPPORTED 1
|
||||
#define SOC_ETM_SUPPORTED 1
|
||||
@ -326,6 +327,16 @@
|
||||
// #define SOC_RMT_SUPPORT_RC_FAST 1 /*!< Support set RC_FAST clock as the RMT clock source */
|
||||
#define SOC_RMT_SUPPORT_DMA 1 /*!< RMT peripheral can connect to DMA channel */
|
||||
|
||||
/*-------------------------- LCD CAPS ----------------------------------------*/
|
||||
/* I80 bus and RGB timing generator can't work at the same time */
|
||||
// #define SOC_LCD_I80_SUPPORTED (1) /*!< Intel 8080 LCD is supported */ // TODO: IDF-7465
|
||||
// #define SOC_LCD_RGB_SUPPORTED (1) /*!< RGB LCD is supported */ // TODO: IDF-7465
|
||||
#define SOC_LCD_I80_BUSES (1U) /*!< Has one LCD Intel 8080 bus */
|
||||
#define SOC_LCD_RGB_PANELS (1U) /*!< Support one RGB LCD panel */
|
||||
#define SOC_LCD_I80_BUS_WIDTH (24) /*!< Intel 8080 bus width */
|
||||
#define SOC_LCD_RGB_DATA_WIDTH (24) /*!< Number of LCD data lines */
|
||||
#define SOC_LCD_SUPPORT_RGB_YUV_CONV (1) /*!< Support color format conversion between RGB and YUV */
|
||||
|
||||
/*-------------------------- MCPWM CAPS --------------------------------------*/
|
||||
#define SOC_MCPWM_GROUPS (2U) ///< 2 MCPWM groups on the chip (i.e., the number of independent MCPWM peripherals)
|
||||
#define SOC_MCPWM_TIMERS_PER_GROUP (3) ///< The number of timers that each group has
|
||||
|
82
components/soc/esp32p4/lcd_periph.c
Normal file
82
components/soc/esp32p4/lcd_periph.c
Normal file
@ -0,0 +1,82 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include "soc/lcd_periph.h"
|
||||
#include "soc/gpio_sig_map.h"
|
||||
|
||||
const lcd_signal_conn_t lcd_periph_signals = {
|
||||
.buses = {
|
||||
[0] = {
|
||||
.module = PERIPH_LCD_CAM_MODULE,
|
||||
.irq_id = ETS_LCD_CAM_INTR_SOURCE,
|
||||
.data_sigs = {
|
||||
LCD_DATA_OUT_PAD_OUT0_IDX,
|
||||
LCD_DATA_OUT_PAD_OUT1_IDX,
|
||||
LCD_DATA_OUT_PAD_OUT2_IDX,
|
||||
LCD_DATA_OUT_PAD_OUT3_IDX,
|
||||
LCD_DATA_OUT_PAD_OUT4_IDX,
|
||||
LCD_DATA_OUT_PAD_OUT5_IDX,
|
||||
LCD_DATA_OUT_PAD_OUT6_IDX,
|
||||
LCD_DATA_OUT_PAD_OUT7_IDX,
|
||||
LCD_DATA_OUT_PAD_OUT8_IDX,
|
||||
LCD_DATA_OUT_PAD_OUT9_IDX,
|
||||
LCD_DATA_OUT_PAD_OUT10_IDX,
|
||||
LCD_DATA_OUT_PAD_OUT11_IDX,
|
||||
LCD_DATA_OUT_PAD_OUT12_IDX,
|
||||
LCD_DATA_OUT_PAD_OUT13_IDX,
|
||||
LCD_DATA_OUT_PAD_OUT14_IDX,
|
||||
LCD_DATA_OUT_PAD_OUT15_IDX,
|
||||
LCD_DATA_OUT_PAD_OUT16_IDX,
|
||||
LCD_DATA_OUT_PAD_OUT17_IDX,
|
||||
LCD_DATA_OUT_PAD_OUT18_IDX,
|
||||
LCD_DATA_OUT_PAD_OUT19_IDX,
|
||||
LCD_DATA_OUT_PAD_OUT20_IDX,
|
||||
LCD_DATA_OUT_PAD_OUT21_IDX,
|
||||
LCD_DATA_OUT_PAD_OUT22_IDX,
|
||||
LCD_DATA_OUT_PAD_OUT23_IDX,
|
||||
},
|
||||
.cs_sig = LCD_CS_PAD_OUT_IDX,
|
||||
.dc_sig = LCD_DC_PAD_OUT_IDX,
|
||||
.wr_sig = LCD_PCLK_PAD_OUT_IDX
|
||||
}
|
||||
},
|
||||
.panels = {
|
||||
[0] = {
|
||||
.module = PERIPH_LCD_CAM_MODULE,
|
||||
.irq_id = ETS_LCD_CAM_INTR_SOURCE,
|
||||
.data_sigs = {
|
||||
LCD_DATA_OUT_PAD_OUT0_IDX,
|
||||
LCD_DATA_OUT_PAD_OUT1_IDX,
|
||||
LCD_DATA_OUT_PAD_OUT2_IDX,
|
||||
LCD_DATA_OUT_PAD_OUT3_IDX,
|
||||
LCD_DATA_OUT_PAD_OUT4_IDX,
|
||||
LCD_DATA_OUT_PAD_OUT5_IDX,
|
||||
LCD_DATA_OUT_PAD_OUT6_IDX,
|
||||
LCD_DATA_OUT_PAD_OUT7_IDX,
|
||||
LCD_DATA_OUT_PAD_OUT8_IDX,
|
||||
LCD_DATA_OUT_PAD_OUT9_IDX,
|
||||
LCD_DATA_OUT_PAD_OUT10_IDX,
|
||||
LCD_DATA_OUT_PAD_OUT11_IDX,
|
||||
LCD_DATA_OUT_PAD_OUT12_IDX,
|
||||
LCD_DATA_OUT_PAD_OUT13_IDX,
|
||||
LCD_DATA_OUT_PAD_OUT14_IDX,
|
||||
LCD_DATA_OUT_PAD_OUT15_IDX,
|
||||
LCD_DATA_OUT_PAD_OUT16_IDX,
|
||||
LCD_DATA_OUT_PAD_OUT17_IDX,
|
||||
LCD_DATA_OUT_PAD_OUT18_IDX,
|
||||
LCD_DATA_OUT_PAD_OUT19_IDX,
|
||||
LCD_DATA_OUT_PAD_OUT20_IDX,
|
||||
LCD_DATA_OUT_PAD_OUT21_IDX,
|
||||
LCD_DATA_OUT_PAD_OUT22_IDX,
|
||||
LCD_DATA_OUT_PAD_OUT23_IDX,
|
||||
},
|
||||
.hsync_sig = LCD_H_SYNC_PAD_OUT_IDX,
|
||||
.vsync_sig = LCD_V_SYNC_PAD_OUT_IDX,
|
||||
.pclk_sig = LCD_PCLK_PAD_OUT_IDX,
|
||||
.de_sig = LCD_H_ENABLE_PAD_OUT_IDX,
|
||||
}
|
||||
}
|
||||
};
|
Loading…
Reference in New Issue
Block a user