lcd: support I2S1 LCD mode on esp32

This commit is contained in:
morris 2022-03-03 15:35:43 +08:00
parent f35edeb5a3
commit 9422fe077a
14 changed files with 85 additions and 52 deletions

View File

@ -19,8 +19,8 @@
extern "C" {
#endif
#define LCD_INTR_ALLOC_FLAGS ESP_INTR_FLAG_INTRDISABLED
#define LCD_MEM_ALLOC_CAPS MALLOC_CAP_DEFAULT
#define LCD_I80_INTR_ALLOC_FLAGS ESP_INTR_FLAG_INTRDISABLED
#define LCD_I80_MEM_ALLOC_CAPS MALLOC_CAP_DEFAULT
#define LCD_PERIPH_CLOCK_PRE_SCALE (2) // This is the minimum divider that can be applied to LCD peripheral

View File

@ -57,7 +57,7 @@ static esp_err_t i2s_lcd_init_dma_link(esp_lcd_i80_bus_handle_t bus);
static esp_err_t i2s_lcd_configure_gpio(esp_lcd_i80_bus_handle_t bus, const esp_lcd_i80_bus_config_t *bus_config);
static void i2s_lcd_trigger_quick_trans_done_event(esp_lcd_i80_bus_handle_t bus);
static void lcd_i80_switch_devices(lcd_panel_io_i80_t *cur_device, lcd_panel_io_i80_t *next_device);
static IRAM_ATTR void lcd_default_isr_handler(void *args);
static void lcd_default_isr_handler(void *args);
struct esp_lcd_i80_bus_t {
int bus_id; // Bus ID, index from 0
@ -149,9 +149,16 @@ esp_err_t esp_lcd_new_i80_bus(const esp_lcd_i80_bus_config_t *bus_config, esp_lc
bus->format_buffer = heap_caps_calloc(1, CONFIG_LCD_PANEL_IO_FORMAT_BUF_SIZE, MALLOC_CAP_INTERNAL | MALLOC_CAP_DMA);
#endif // SOC_I2S_TRANS_SIZE_ALIGN_WORD
ESP_GOTO_ON_FALSE(bus->format_buffer, ESP_ERR_NO_MEM, err, TAG, "no mem for format buffer");
// I2S0 has the LCD mode, but the LCD mode can't work with other modes at the same time, we need to register the driver object to the I2S platform
ESP_GOTO_ON_ERROR(i2s_priv_register_object(bus, 0), err, TAG, "register to I2S platform failed");
bus->bus_id = 0;
// LCD mode can't work with other modes at the same time, we need to register the driver object to the I2S platform
int bus_id = -1;
for (int i = 0; i < SOC_LCD_I80_BUSES; i++) {
if (i2s_priv_register_object(bus, i) == ESP_OK) {
bus_id = i;
break;
}
}
ESP_GOTO_ON_FALSE(bus_id != -1, ESP_ERR_NOT_FOUND, err, TAG, "no free i80 bus slot");
bus->bus_id = bus_id;
// initialize HAL layer
i2s_hal_init(&bus->hal, bus->bus_id);
// set peripheral clock resolution
@ -163,7 +170,7 @@ esp_err_t esp_lcd_new_i80_bus(const esp_lcd_i80_bus_config_t *bus_config, esp_lc
i2s_ll_tx_reset_fifo(bus->hal.dev);
// install interrupt service, (I2S LCD mode only uses the "TX Unit", which leaves "RX Unit" for other purpose)
// So the interrupt should also be able to share with other functionality
int isr_flags = ESP_INTR_FLAG_INTRDISABLED | ESP_INTR_FLAG_SHARED;
int isr_flags = LCD_I80_INTR_ALLOC_FLAGS | ESP_INTR_FLAG_SHARED;
ret = esp_intr_alloc_intrstatus(lcd_periph_signals.buses[bus->bus_id].irq_id, isr_flags,
(uint32_t)i2s_ll_get_intr_status_reg(bus->hal.dev),
I2S_LL_EVENT_TX_EOF, lcd_default_isr_handler, bus, &bus->intr);
@ -257,7 +264,7 @@ esp_err_t esp_lcd_new_panel_io_i80(esp_lcd_i80_bus_handle_t bus, const esp_lcd_p
uint32_t pclk_prescale = bus->resolution_hz / 2 / io_config->pclk_hz;
ESP_GOTO_ON_FALSE(pclk_prescale > 0 && pclk_prescale <= I2S_LL_BCK_MAX_PRESCALE, ESP_ERR_NOT_SUPPORTED, err, TAG,
"prescaler can't satisfy PCLK clock %u", io_config->pclk_hz);
i80_device = calloc(1, sizeof(lcd_panel_io_i80_t) + io_config->trans_queue_depth * sizeof(lcd_i80_trans_descriptor_t));
i80_device = heap_caps_calloc(1, sizeof(lcd_panel_io_i80_t) + io_config->trans_queue_depth * sizeof(lcd_i80_trans_descriptor_t), LCD_I80_MEM_ALLOC_CAPS);
ESP_GOTO_ON_FALSE(i80_device, ESP_ERR_NO_MEM, err, TAG, "no mem for i80 panel io");
// create two queues for i80 device
i80_device->trans_queue = xQueueCreate(io_config->trans_queue_depth, sizeof(lcd_i80_trans_descriptor_t *));

View File

@ -55,7 +55,7 @@ static esp_err_t lcd_i80_select_periph_clock(esp_lcd_i80_bus_handle_t bus, lcd_c
static esp_err_t lcd_i80_bus_configure_gpio(esp_lcd_i80_bus_handle_t bus, const esp_lcd_i80_bus_config_t *bus_config);
static void lcd_i80_switch_devices(lcd_panel_io_i80_t *cur_device, lcd_panel_io_i80_t *next_device);
static void lcd_start_transaction(esp_lcd_i80_bus_t *bus, lcd_i80_trans_descriptor_t *trans_desc);
static IRAM_ATTR void lcd_default_isr_handler(void *args);
static void lcd_default_isr_handler(void *args);
struct esp_lcd_i80_bus_t {
int bus_id; // Bus ID, index from 0
@ -153,7 +153,7 @@ esp_err_t esp_lcd_new_i80_bus(const esp_lcd_i80_bus_config_t *bus_config, esp_lc
ESP_GOTO_ON_ERROR(ret, err, TAG, "select periph clock %d failed", bus_config->clk_src);
// 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_INTR_ALLOC_FLAGS | ESP_INTR_FLAG_SHARED;
int isr_flags = LCD_I80_INTR_ALLOC_FLAGS | ESP_INTR_FLAG_SHARED;
ret = esp_intr_alloc_intrstatus(lcd_periph_signals.buses[bus_id].irq_id, isr_flags,
(uint32_t)lcd_ll_get_interrupt_status_reg(bus->hal.dev),
LCD_LL_EVENT_TRANS_DONE, lcd_default_isr_handler, bus, &bus->intr);
@ -250,7 +250,7 @@ esp_err_t esp_lcd_new_panel_io_i80(esp_lcd_i80_bus_handle_t bus, const esp_lcd_p
uint32_t pclk_prescale = bus->resolution_hz / io_config->pclk_hz;
ESP_GOTO_ON_FALSE(pclk_prescale > 0 && pclk_prescale <= LCD_LL_CLOCK_PRESCALE_MAX, ESP_ERR_NOT_SUPPORTED, err, TAG,
"prescaler can't satisfy PCLK clock %u", io_config->pclk_hz);
i80_device = heap_caps_calloc(1, sizeof(lcd_panel_io_i80_t) + io_config->trans_queue_depth * sizeof(lcd_i80_trans_descriptor_t), LCD_MEM_ALLOC_CAPS);
i80_device = heap_caps_calloc(1, sizeof(lcd_panel_io_i80_t) + io_config->trans_queue_depth * sizeof(lcd_i80_trans_descriptor_t), LCD_I80_MEM_ALLOC_CAPS);
ESP_GOTO_ON_FALSE(i80_device, ESP_ERR_NO_MEM, err, TAG, "no mem for i80 panel io");
// create two queues for i80 device
i80_device->trans_queue = xQueueCreate(io_config->trans_queue_depth, sizeof(lcd_i80_trans_descriptor_t *));
@ -393,9 +393,8 @@ 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_swap_data_byte_order(bus->hal.dev, false);
lcd_ll_reverse_data_bit_order(bus->hal.dev, false);
lcd_ll_reverse_data_8bits_order(bus->hal.dev, next_device->lcd_param_bits > bus->bus_width);
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);
bus->cur_trans = NULL;
bus->cur_device = next_device;
// package a transaction
@ -643,9 +642,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_data_bit_order(bus->hal.dev, next_device->flags.reverse_color_bits);
lcd_ll_swap_data_byte_order(bus->hal.dev, next_device->flags.swap_color_bytes);
lcd_ll_reverse_data_8bits_order(bus->hal.dev, false);
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);
bus->cur_trans = trans_desc;
bus->cur_device = next_device;
// mount data to DMA links

View File

@ -1,7 +1,6 @@
set(srcs "test_app_main.c"
"test_i2c_lcd_panel.c")
idf_component_register(SRCS ${srcs}
PRIV_REQUIRES esp_lcd unity driver)
idf_component_register(SRCS ${srcs})
target_link_libraries(${COMPONENT_LIB} INTERFACE "-u test_app_include_i2c_lcd")

View File

@ -1,7 +1,6 @@
set(srcs "test_app_main.c"
"test_i80_lcd_panel.c")
idf_component_register(SRCS ${srcs}
PRIV_REQUIRES esp_lcd unity driver)
idf_component_register(SRCS ${srcs})
target_link_libraries(${COMPONENT_LIB} INTERFACE "-u test_app_include_i80_lcd")

View File

@ -1,7 +1,6 @@
set(srcs "test_app_main.c"
"test_spi_lcd_panel.c")
idf_component_register(SRCS ${srcs}
PRIV_REQUIRES esp_lcd unity driver)
idf_component_register(SRCS ${srcs})
target_link_libraries(${COMPONENT_LIB} INTERFACE "-u test_app_include_spi_lcd")

View File

@ -207,6 +207,7 @@ static inline void lcd_ll_start(lcd_cam_dev_t *dev)
*
* @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;
@ -230,34 +231,32 @@ static inline void lcd_ll_reset(lcd_cam_dev_t *dev)
* @param en True to reverse, False to not reverse
*/
__attribute__((always_inline))
static inline void lcd_ll_reverse_data_bit_order(lcd_cam_dev_t *dev, bool en)
static inline void lcd_ll_reverse_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 swap data byte order, i.e. data[15:0] -> data[7:0][15:8]
* @brief Whether to swap adjacent two bytes
*
* @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_data_byte_order(lcd_cam_dev_t *dev, bool en)
static inline void lcd_ll_swap_byte_order(lcd_cam_dev_t *dev, uint32_t width, bool en)
{
dev->lcd_user.lcd_byte_order = en;
}
/**
* @brief Whether to reverse the 8bits order
*
* @param dev LCD register base address
* @param en True to reverse, False to not reverse
*/
__attribute__((always_inline))
static inline void lcd_ll_reverse_data_8bits_order(lcd_cam_dev_t *dev, bool en)
{
dev->lcd_user.lcd_8bits_order = 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;
}
}
/**
@ -265,6 +264,7 @@ static inline void lcd_ll_reverse_data_8bits_order(lcd_cam_dev_t *dev, bool en)
*
* @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

View File

@ -260,8 +260,8 @@ config SOC_LCD_I80_SUPPORTED
default y
config SOC_LCD_I80_BUSES
bool
default y
int
default 2
config SOC_LCD_I80_BUS_WIDTH
int

View File

@ -185,7 +185,7 @@
/*-------------------------- LCD CAPS ----------------------------------------*/
/* Notes: On esp32, LCD intel 8080 timing is generated by I2S peripheral */
#define SOC_LCD_I80_SUPPORTED (1) /*!< Intel 8080 LCD is supported */
#define SOC_LCD_I80_BUSES (1) /*!< Only I2S0 has LCD mode */
#define SOC_LCD_I80_BUSES (2) /*!< Both I2S0/1 have LCD mode */
#define SOC_LCD_I80_BUS_WIDTH (24) /*!< Intel 8080 bus width */
/*-------------------------- LEDC CAPS ---------------------------------------*/

View File

@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2021 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@ -40,6 +40,37 @@ const lcd_signal_conn_t lcd_periph_signals = {
I2S0O_DATA_OUT23_IDX,
},
.wr_sig = I2S0O_WS_OUT_IDX,
},
[1] = {
.module = PERIPH_I2S1_MODULE,
.irq_id = ETS_I2S1_INTR_SOURCE,
.data_sigs = {
I2S1O_DATA_OUT0_IDX,
I2S1O_DATA_OUT1_IDX,
I2S1O_DATA_OUT2_IDX,
I2S1O_DATA_OUT3_IDX,
I2S1O_DATA_OUT4_IDX,
I2S1O_DATA_OUT5_IDX,
I2S1O_DATA_OUT6_IDX,
I2S1O_DATA_OUT7_IDX,
I2S1O_DATA_OUT8_IDX,
I2S1O_DATA_OUT9_IDX,
I2S1O_DATA_OUT10_IDX,
I2S1O_DATA_OUT11_IDX,
I2S1O_DATA_OUT12_IDX,
I2S1O_DATA_OUT13_IDX,
I2S1O_DATA_OUT14_IDX,
I2S1O_DATA_OUT15_IDX,
I2S1O_DATA_OUT16_IDX,
I2S1O_DATA_OUT17_IDX,
I2S1O_DATA_OUT18_IDX,
I2S1O_DATA_OUT19_IDX,
I2S1O_DATA_OUT20_IDX,
I2S1O_DATA_OUT21_IDX,
I2S1O_DATA_OUT22_IDX,
I2S1O_DATA_OUT23_IDX,
},
.wr_sig = I2S1O_WS_OUT_IDX,
}
}
};

View File

@ -328,8 +328,8 @@ config SOC_LCD_I80_SUPPORTED
default y
config SOC_LCD_I80_BUSES
bool
default y
int
default 1
config SOC_LCD_I80_BUS_WIDTH
int

View File

@ -173,7 +173,7 @@
/*-------------------------- LCD CAPS ----------------------------------------*/
/* Notes: On esp32-s2, LCD intel 8080 timing is generated by I2S peripheral */
#define SOC_LCD_I80_SUPPORTED (1) /*!< Intel 8080 LCD is supported */
#define SOC_LCD_I80_BUSES (1) /*!< Only I2S0 has LCD mode */
#define SOC_LCD_I80_BUSES (1U) /*!< Only I2S0 has LCD mode */
#define SOC_LCD_I80_BUS_WIDTH (24) /*!< Intel 8080 bus width */
/*-------------------------- LEDC CAPS ---------------------------------------*/

View File

@ -528,12 +528,12 @@ config SOC_LCD_RGB_SUPPORTED
default y
config SOC_LCD_I80_BUSES
bool
default y
int
default 1
config SOC_LCD_RGB_PANELS
bool
default y
int
default 1
config SOC_LCD_I80_BUS_WIDTH
int

View File

@ -194,8 +194,8 @@
/* Notes: On esp32-s3, I80 bus and RGB timing generator can't work at the same time */
#define SOC_LCD_I80_SUPPORTED (1) /*!< Intel 8080 LCD is supported */
#define SOC_LCD_RGB_SUPPORTED (1) /*!< RGB LCD is supported */
#define SOC_LCD_I80_BUSES (1) /*!< Has one LCD Intel 8080 bus */
#define SOC_LCD_RGB_PANELS (1) /*!< Support one RGB LCD panel */
#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 (16) /*!< Intel 8080 bus width */
#define SOC_LCD_RGB_DATA_WIDTH (16) /*!< Number of LCD data lines */