diff --git a/components/esp_lcd/src/esp_lcd_common.h b/components/esp_lcd/src/esp_lcd_common.h index 6c534bc9f9..f036bc5fa8 100644 --- a/components/esp_lcd/src/esp_lcd_common.h +++ b/components/esp_lcd/src/esp_lcd_common.h @@ -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 diff --git a/components/esp_lcd/src/esp_lcd_panel_io_i2s.c b/components/esp_lcd/src/esp_lcd_panel_io_i2s.c index d1fac6fd01..c8ccdf308f 100644 --- a/components/esp_lcd/src/esp_lcd_panel_io_i2s.c +++ b/components/esp_lcd/src/esp_lcd_panel_io_i2s.c @@ -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 *)); diff --git a/components/esp_lcd/src/esp_lcd_panel_io_i80.c b/components/esp_lcd/src/esp_lcd_panel_io_i80.c index da834bce08..64920d5cb0 100644 --- a/components/esp_lcd/src/esp_lcd_panel_io_i80.c +++ b/components/esp_lcd/src/esp_lcd_panel_io_i80.c @@ -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 diff --git a/components/esp_lcd/test_apps/i2c_lcd/main/CMakeLists.txt b/components/esp_lcd/test_apps/i2c_lcd/main/CMakeLists.txt index 687518660d..1e685eb807 100644 --- a/components/esp_lcd/test_apps/i2c_lcd/main/CMakeLists.txt +++ b/components/esp_lcd/test_apps/i2c_lcd/main/CMakeLists.txt @@ -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") diff --git a/components/esp_lcd/test_apps/i80_lcd/main/CMakeLists.txt b/components/esp_lcd/test_apps/i80_lcd/main/CMakeLists.txt index ef0741a205..eef50b7e7c 100644 --- a/components/esp_lcd/test_apps/i80_lcd/main/CMakeLists.txt +++ b/components/esp_lcd/test_apps/i80_lcd/main/CMakeLists.txt @@ -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") diff --git a/components/esp_lcd/test_apps/spi_lcd/main/CMakeLists.txt b/components/esp_lcd/test_apps/spi_lcd/main/CMakeLists.txt index 8fce5f1489..aacecc1821 100644 --- a/components/esp_lcd/test_apps/spi_lcd/main/CMakeLists.txt +++ b/components/esp_lcd/test_apps/spi_lcd/main/CMakeLists.txt @@ -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") diff --git a/components/hal/esp32s3/include/hal/lcd_ll.h b/components/hal/esp32s3/include/hal/lcd_ll.h index 548c897c8e..89b7674f7e 100644 --- a/components/hal/esp32s3/include/hal/lcd_ll.h +++ b/components/hal/esp32s3/include/hal/lcd_ll.h @@ -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 diff --git a/components/soc/esp32/include/soc/Kconfig.soc_caps.in b/components/soc/esp32/include/soc/Kconfig.soc_caps.in index 489a4cb2ba..e13b9238ac 100644 --- a/components/soc/esp32/include/soc/Kconfig.soc_caps.in +++ b/components/soc/esp32/include/soc/Kconfig.soc_caps.in @@ -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 diff --git a/components/soc/esp32/include/soc/soc_caps.h b/components/soc/esp32/include/soc/soc_caps.h index 79127e4911..8a9c32ec3a 100644 --- a/components/soc/esp32/include/soc/soc_caps.h +++ b/components/soc/esp32/include/soc/soc_caps.h @@ -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 ---------------------------------------*/ diff --git a/components/soc/esp32/lcd_periph.c b/components/soc/esp32/lcd_periph.c index a3bc25a335..148a1527aa 100644 --- a/components/soc/esp32/lcd_periph.c +++ b/components/soc/esp32/lcd_periph.c @@ -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, } } }; diff --git a/components/soc/esp32s2/include/soc/Kconfig.soc_caps.in b/components/soc/esp32s2/include/soc/Kconfig.soc_caps.in index d18fecaccc..95c4e03538 100644 --- a/components/soc/esp32s2/include/soc/Kconfig.soc_caps.in +++ b/components/soc/esp32s2/include/soc/Kconfig.soc_caps.in @@ -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 diff --git a/components/soc/esp32s2/include/soc/soc_caps.h b/components/soc/esp32s2/include/soc/soc_caps.h index 8e6fd779a3..e62053c214 100644 --- a/components/soc/esp32s2/include/soc/soc_caps.h +++ b/components/soc/esp32s2/include/soc/soc_caps.h @@ -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 ---------------------------------------*/ diff --git a/components/soc/esp32s3/include/soc/Kconfig.soc_caps.in b/components/soc/esp32s3/include/soc/Kconfig.soc_caps.in index be6eb1a2ed..8b8a667e33 100644 --- a/components/soc/esp32s3/include/soc/Kconfig.soc_caps.in +++ b/components/soc/esp32s3/include/soc/Kconfig.soc_caps.in @@ -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 diff --git a/components/soc/esp32s3/include/soc/soc_caps.h b/components/soc/esp32s3/include/soc/soc_caps.h index 88d6e7cfc7..6502f72046 100644 --- a/components/soc/esp32s3/include/soc/soc_caps.h +++ b/components/soc/esp32s3/include/soc/soc_caps.h @@ -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 */