feat(lcd): support i80 lcd driver on esp32p4

This commit is contained in:
morris 2023-11-13 17:50:29 +08:00
parent 884caed878
commit 23c41fc772
27 changed files with 272 additions and 148 deletions

View File

@ -26,13 +26,17 @@ if(CONFIG_SOC_GPSPI_SUPPORTED)
list(APPEND srcs "spi/esp_lcd_panel_io_spi.c") list(APPEND srcs "spi/esp_lcd_panel_io_spi.c")
endif() endif()
if(CONFIG_SOC_I2S_LCD_I80_VARIANT) if(CONFIG_SOC_I2S_SUPPORTS_LCD_CAMERA)
list(APPEND srcs "i80/esp_lcd_panel_io_i2s.c") list(APPEND srcs "i80/esp_lcd_panel_io_i2s.c")
endif() endif()
if(CONFIG_SOC_LCDCAM_SUPPORTED) if(CONFIG_SOC_LCDCAM_I80_LCD_SUPPORTED)
list(APPEND srcs "i80/esp_lcd_panel_io_i80.c")
endif()
if(CONFIG_SOC_LCDCAM_RGB_LCD_SUPPORTED)
list(APPEND includes "rgb/include") list(APPEND includes "rgb/include")
list(APPEND srcs "i80/esp_lcd_panel_io_i80.c" "rgb/esp_lcd_panel_rgb.c") list(APPEND srcs "rgb/esp_lcd_panel_rgb.c")
endif() endif()
if(CONFIG_SOC_MIPI_DSI_SUPPORTED) if(CONFIG_SOC_MIPI_DSI_SUPPORTED)

View File

@ -2,14 +2,6 @@ menu "LCD and Touch Panel"
comment "LCD Touch Drivers are maintained in the ESP Component Registry" comment "LCD Touch Drivers are maintained in the ESP Component Registry"
menu "LCD Peripheral Configuration" menu "LCD Peripheral Configuration"
config LCD_PANEL_IO_FORMAT_BUF_SIZE
depends on SOC_LCD_I80_SUPPORTED
int "LCD panel io format buffer size"
default 32
help
LCD driver allocates an internal buffer to transform the data into a proper format, because of
the endian order mismatch. This option is to set the size of the buffer, in bytes.
config LCD_ENABLE_DEBUG_LOG config LCD_ENABLE_DEBUG_LOG
bool "Enable debug log" bool "Enable debug log"
default n default n

View File

@ -161,7 +161,7 @@ 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, max_transfer_bytes, MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT | MALLOC_CAP_DMA); bus->format_buffer = heap_caps_calloc(1, max_transfer_bytes, MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT | MALLOC_CAP_DMA);
#else #else
// only transform format for LCD parameters, buffer size depends on specific LCD, set at compile time // only transform format for LCD parameters, buffer size depends on specific LCD, set at compile time
bus->format_buffer = heap_caps_calloc(1, CONFIG_LCD_PANEL_IO_FORMAT_BUF_SIZE, MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT | MALLOC_CAP_DMA); bus->format_buffer = heap_caps_calloc(1, LCD_I80_IO_FORMAT_BUF_SIZE, MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT | MALLOC_CAP_DMA);
#endif // SOC_I2S_TRANS_SIZE_ALIGN_WORD #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"); ESP_GOTO_ON_FALSE(bus->format_buffer, ESP_ERR_NO_MEM, err, TAG, "no mem for format buffer");
// LCD mode can't work with other modes at the same time, we need to register the driver object to the I2S platform // LCD mode can't work with other modes at the same time, we need to register the driver object to the I2S platform
@ -186,7 +186,7 @@ esp_err_t esp_lcd_new_i80_bus(const esp_lcd_i80_bus_config_t *bus_config, esp_lc
// install interrupt service, (I2S LCD mode only uses the "TX Unit", which leaves "RX Unit" for other purpose) // 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 // So the interrupt should also be able to share with other functionality
int isr_flags = LCD_I80_INTR_ALLOC_FLAGS | ESP_INTR_FLAG_SHARED | ESP_INTR_FLAG_LOWMED; int isr_flags = LCD_I80_INTR_ALLOC_FLAGS | ESP_INTR_FLAG_SHARED | ESP_INTR_FLAG_LOWMED;
ret = esp_intr_alloc_intrstatus(lcd_periph_signals.buses[bus->bus_id].irq_id, isr_flags, ret = esp_intr_alloc_intrstatus(lcd_periph_i2s_signals.buses[bus->bus_id].irq_id, isr_flags,
(uint32_t)i2s_ll_get_intr_status_reg(bus->hal.dev), (uint32_t)i2s_ll_get_intr_status_reg(bus->hal.dev),
I2S_LL_EVENT_TX_EOF, lcd_default_isr_handler, bus, &bus->intr); I2S_LL_EVENT_TX_EOF, lcd_default_isr_handler, bus, &bus->intr);
ESP_GOTO_ON_ERROR(ret, err, TAG, "install interrupt failed"); ESP_GOTO_ON_ERROR(ret, err, TAG, "install interrupt failed");
@ -514,7 +514,7 @@ static esp_err_t panel_io_i80_tx_param(esp_lcd_panel_io_t *io, int lcd_cmd, cons
lcd_panel_io_i80_t *cur_device = bus->cur_device; lcd_panel_io_i80_t *cur_device = bus->cur_device;
lcd_i80_trans_descriptor_t *trans_desc = NULL; lcd_i80_trans_descriptor_t *trans_desc = NULL;
assert(param_size <= (bus->num_dma_nodes * LCD_DMA_DESCRIPTOR_BUFFER_MAX_SIZE) && "parameter bytes too long, enlarge max_transfer_bytes"); assert(param_size <= (bus->num_dma_nodes * LCD_DMA_DESCRIPTOR_BUFFER_MAX_SIZE) && "parameter bytes too long, enlarge max_transfer_bytes");
assert(param_size <= CONFIG_LCD_PANEL_IO_FORMAT_BUF_SIZE && "format buffer too small, increase CONFIG_LCD_PANEL_IO_FORMAT_BUF_SIZE"); assert(param_size <= LCD_I80_IO_FORMAT_BUF_SIZE && "format buffer too small, increase LCD_I80_IO_FORMAT_BUF_SIZE");
size_t num_trans_inflight = next_device->num_trans_inflight; size_t num_trans_inflight = next_device->num_trans_inflight;
// before issue a polling transaction, need to wait queued transactions finished // before issue a polling transaction, need to wait queued transactions finished
for (size_t i = 0; i < num_trans_inflight; i++) { for (size_t i = 0; i < num_trans_inflight; i++) {
@ -700,15 +700,15 @@ static esp_err_t i2s_lcd_configure_gpio(esp_lcd_i80_bus_handle_t bus, const esp_
for (size_t i = 0; i < bus_config->bus_width; i++) { for (size_t i = 0; i < bus_config->bus_width; i++) {
gpio_set_direction(bus_config->data_gpio_nums[i], GPIO_MODE_OUTPUT); gpio_set_direction(bus_config->data_gpio_nums[i], GPIO_MODE_OUTPUT);
#if SOC_I2S_TRANS_SIZE_ALIGN_WORD #if SOC_I2S_TRANS_SIZE_ALIGN_WORD
esp_rom_gpio_connect_out_signal(bus_config->data_gpio_nums[i], lcd_periph_signals.buses[bus_id].data_sigs[i + 8], false, false); esp_rom_gpio_connect_out_signal(bus_config->data_gpio_nums[i], lcd_periph_i2s_signals.buses[bus_id].data_sigs[i + 8], false, false);
#else #else
esp_rom_gpio_connect_out_signal(bus_config->data_gpio_nums[i], lcd_periph_signals.buses[bus_id].data_sigs[i + SOC_LCD_I80_BUS_WIDTH - bus_config->bus_width], false, false); esp_rom_gpio_connect_out_signal(bus_config->data_gpio_nums[i], lcd_periph_i2s_signals.buses[bus_id].data_sigs[i + SOC_I2S_MAX_DATA_WIDTH - bus_config->bus_width], false, false);
#endif #endif
gpio_hal_iomux_func_sel(GPIO_PIN_MUX_REG[bus_config->data_gpio_nums[i]], PIN_FUNC_GPIO); gpio_hal_iomux_func_sel(GPIO_PIN_MUX_REG[bus_config->data_gpio_nums[i]], PIN_FUNC_GPIO);
} }
// WR signal (pclk) // WR signal (pclk)
gpio_set_direction(bus_config->wr_gpio_num, GPIO_MODE_OUTPUT); gpio_set_direction(bus_config->wr_gpio_num, GPIO_MODE_OUTPUT);
esp_rom_gpio_connect_out_signal(bus_config->wr_gpio_num, lcd_periph_signals.buses[bus_id].wr_sig, true, false); esp_rom_gpio_connect_out_signal(bus_config->wr_gpio_num, lcd_periph_i2s_signals.buses[bus_id].wr_sig, true, false);
gpio_hal_iomux_func_sel(GPIO_PIN_MUX_REG[bus_config->wr_gpio_num], PIN_FUNC_GPIO); gpio_hal_iomux_func_sel(GPIO_PIN_MUX_REG[bus_config->wr_gpio_num], PIN_FUNC_GPIO);
// DC signal is controlled by software, set as general purpose IO // DC signal is controlled by software, set as general purpose IO
gpio_set_direction(bus_config->dc_gpio_num, GPIO_MODE_OUTPUT); gpio_set_direction(bus_config->dc_gpio_num, GPIO_MODE_OUTPUT);
@ -751,7 +751,7 @@ static void lcd_i80_switch_devices(lcd_panel_io_i80_t *cur_device, lcd_panel_io_
} }
// the WR signal (a.k.a the PCLK) generated by I2S is low level in idle stage // the WR signal (a.k.a the PCLK) generated by I2S is low level in idle stage
// but most of 8080 LCDs require the WR line to be in high level during idle stage // but most of 8080 LCDs require the WR line to be in high level during idle stage
esp_rom_gpio_connect_out_signal(bus->wr_gpio_num, lcd_periph_signals.buses[bus->bus_id].wr_sig, !next_device->flags.pclk_idle_low, false); esp_rom_gpio_connect_out_signal(bus->wr_gpio_num, lcd_periph_i2s_signals.buses[bus->bus_id].wr_sig, !next_device->flags.pclk_idle_low, false);
} }
bus->cur_device = next_device; bus->cur_device = next_device;
} }

View File

@ -28,18 +28,37 @@
#include "esp_clk_tree.h" #include "esp_clk_tree.h"
#include "esp_memory_utils.h" #include "esp_memory_utils.h"
#include "esp_cache.h" #include "esp_cache.h"
#include "hal/dma_types.h"
#include "hal/gpio_hal.h"
#include "esp_private/gdma.h"
#include "driver/gpio.h" #include "driver/gpio.h"
#include "esp_private/gpio.h"
#include "esp_private/gdma.h"
#include "esp_private/gdma_link.h"
#include "esp_private/periph_ctrl.h" #include "esp_private/periph_ctrl.h"
#include "esp_lcd_common.h" #include "esp_lcd_common.h"
#include "soc/lcd_periph.h" #include "soc/lcd_periph.h"
#include "soc/io_mux_reg.h"
#include "soc/gpio_sig_map.h"
#include "hal/lcd_ll.h" #include "hal/lcd_ll.h"
#include "hal/lcd_hal.h" #include "hal/lcd_hal.h"
#include "hal/cache_ll.h"
#include "hal/cache_hal.h"
#define ALIGN_UP(size, align) (((size) + (align) - 1) & ~((align) - 1)) #define LCD_DMA_DESCRIPTOR_BUFFER_MAX_SIZE 4095
#define ALIGN_DOWN(size, align) ((size) & ~((align) - 1))
#if defined(SOC_GDMA_TRIG_PERIPH_LCD0_BUS) && (SOC_GDMA_TRIG_PERIPH_LCD0_BUS == SOC_GDMA_BUS_AHB)
#define LCD_GDMA_NEW_CHANNEL gdma_new_ahb_channel
#define LCD_GDMA_DESCRIPTOR_ALIGN 4
#elif defined(SOC_GDMA_TRIG_PERIPH_LCD0_BUS) && (SOC_GDMA_TRIG_PERIPH_LCD0_BUS == SOC_GDMA_BUS_AXI)
#define LCD_GDMA_NEW_CHANNEL gdma_new_axi_channel
#define LCD_GDMA_DESCRIPTOR_ALIGN 8
#else
#error "Unsupported GDMA bus type for LCD i80"
#endif
#if SOC_NON_CACHEABLE_OFFSET
#define LCD_CACHE_ADDR_TO_NON_CACHE_ADDR(addr) ((addr) + SOC_NON_CACHEABLE_OFFSET)
#else
#define LCD_CACHE_ADDR_TO_NON_CACHE_ADDR(addr) (addr)
#endif
static const char *TAG = "lcd_panel.io.i80"; static const char *TAG = "lcd_panel.io.i80";
@ -66,19 +85,20 @@ struct esp_lcd_i80_bus_t {
size_t bus_width; // Number of data lines size_t bus_width; // Number of data lines
intr_handle_t intr; // LCD peripheral interrupt handle intr_handle_t intr; // LCD peripheral interrupt handle
esp_pm_lock_handle_t pm_lock; // Power management lock esp_pm_lock_handle_t pm_lock; // Power management lock
size_t num_dma_nodes; // Number of DMA descriptors
uint8_t *format_buffer; // The driver allocates an internal buffer for DMA to do data format transformer uint8_t *format_buffer; // The driver allocates an internal buffer for DMA to do data format transformer
uint8_t *format_buffer_nc; // Non-cacheable version of format buffer
size_t resolution_hz; // LCD_CLK resolution, determined by selected clock source size_t resolution_hz; // LCD_CLK resolution, determined by selected clock source
size_t num_dma_nodes; // Number of DMA nodes (descriptors)
gdma_channel_handle_t dma_chan; // DMA channel handle gdma_channel_handle_t dma_chan; // DMA channel handle
gdma_link_list_handle_t dma_link; // DMA link list handle
size_t int_mem_align; // Alignment for internal memory size_t int_mem_align; // Alignment for internal memory
size_t ext_mem_align; // Alignment for external memory size_t ext_mem_align; // Alignment for external memory
lcd_i80_trans_descriptor_t *cur_trans; // Current transaction lcd_i80_trans_descriptor_t *cur_trans; // Current transaction
lcd_panel_io_i80_t *cur_device; // Current working device lcd_panel_io_i80_t *cur_device; // Current working device
LIST_HEAD(i80_device_list, lcd_panel_io_i80_t) device_list; // Head of i80 device list LIST_HEAD(i80_device_list, lcd_panel_io_i80_t) device_list; // Head of i80 device list
struct { struct {
unsigned int exclusive: 1; // Indicate whether the I80 bus is owned by one device (whose CS GPIO is not assigned) exclusively uint32_t exclusive: 1; // Indicate whether the I80 bus is owned by one device (whose CS GPIO is not assigned) exclusively
} flags; } flags;
dma_descriptor_t dma_nodes[]; // DMA descriptor pool, the descriptors are shared by all i80 devices
}; };
struct lcd_i80_trans_descriptor_t { struct lcd_i80_trans_descriptor_t {
@ -133,20 +153,39 @@ esp_err_t esp_lcd_new_i80_bus(const esp_lcd_i80_bus_config_t *bus_config, esp_lc
// although LCD_CAM can support up to 24 data lines, we restrict users to only use 8 or 16 bit width // 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, 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); 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; // allocate i80 bus memory
// DMA descriptors must be placed in internal SRAM bus = heap_caps_calloc(1, sizeof(esp_lcd_i80_bus_t), LCD_I80_MEM_ALLOC_CAPS);
bus = heap_caps_calloc(1, sizeof(esp_lcd_i80_bus_t) + num_dma_nodes * sizeof(dma_descriptor_t), MALLOC_CAP_INTERNAL | MALLOC_CAP_DMA);
ESP_GOTO_ON_FALSE(bus, ESP_ERR_NO_MEM, err, TAG, "no mem for i80 bus"); ESP_GOTO_ON_FALSE(bus, ESP_ERR_NO_MEM, err, TAG, "no mem for i80 bus");
size_t num_dma_nodes = bus_config->max_transfer_bytes / LCD_DMA_DESCRIPTOR_BUFFER_MAX_SIZE + 1;
// create DMA link list
gdma_link_list_config_t dma_link_config = {
.buffer_alignment = 1, // no special buffer alignment for LCD TX buffer
.item_alignment = LCD_GDMA_DESCRIPTOR_ALIGN,
.num_items = num_dma_nodes,
.flags = {
.check_owner = true,
},
};
ESP_GOTO_ON_ERROR(gdma_new_link_list(&dma_link_config, &bus->dma_link), err, TAG, "create DMA link list failed");
bus->num_dma_nodes = num_dma_nodes; bus->num_dma_nodes = num_dma_nodes;
bus->bus_width = bus_config->bus_width;
bus->bus_id = -1; bus->bus_id = -1;
bus->format_buffer = heap_caps_calloc(1, CONFIG_LCD_PANEL_IO_FORMAT_BUF_SIZE, MALLOC_CAP_INTERNAL | MALLOC_CAP_DMA); // allocate the format buffer from internal memory, with DMA capability
bus->format_buffer = heap_caps_calloc(1, LCD_I80_IO_FORMAT_BUF_SIZE,
MALLOC_CAP_8BIT | MALLOC_CAP_INTERNAL | MALLOC_CAP_DMA);
ESP_GOTO_ON_FALSE(bus->format_buffer, ESP_ERR_NO_MEM, err, TAG, "no mem for format buffer"); ESP_GOTO_ON_FALSE(bus->format_buffer, ESP_ERR_NO_MEM, err, TAG, "no mem for format buffer");
// if the buffer is behind the cache, write it back to the underlying memory
if (cache_hal_get_cache_line_size(CACHE_LL_LEVEL_INT_MEM, CACHE_TYPE_DATA) > 0) {
esp_cache_msync(bus->format_buffer, LCD_I80_IO_FORMAT_BUF_SIZE,
ESP_CACHE_MSYNC_FLAG_DIR_C2M | ESP_CACHE_MSYNC_FLAG_UNALIGNED);
}
bus->format_buffer_nc = LCD_CACHE_ADDR_TO_NON_CACHE_ADDR(bus->format_buffer);
// register to platform // register to platform
int bus_id = lcd_com_register_device(LCD_COM_DEVICE_TYPE_I80, bus); int bus_id = lcd_com_register_device(LCD_COM_DEVICE_TYPE_I80, bus);
ESP_GOTO_ON_FALSE(bus_id >= 0, ESP_ERR_NOT_FOUND, err, TAG, "no free i80 bus slot"); ESP_GOTO_ON_FALSE(bus_id >= 0, ESP_ERR_NOT_FOUND, err, TAG, "no free i80 bus slot");
bus->bus_id = bus_id; bus->bus_id = bus_id;
// enable APB to access LCD registers // enable APB to access LCD registers
PERIPH_RCC_ACQUIRE_ATOMIC(lcd_periph_signals.panels[bus_id].module, ref_count) { PERIPH_RCC_ACQUIRE_ATOMIC(lcd_periph_i80_signals.buses[bus_id].module, ref_count) {
if (ref_count == 0) { if (ref_count == 0) {
lcd_ll_enable_bus_clock(bus_id, true); lcd_ll_enable_bus_clock(bus_id, true);
lcd_ll_reset_register(bus_id); lcd_ll_reset_register(bus_id);
@ -166,14 +205,13 @@ esp_err_t esp_lcd_new_i80_bus(const esp_lcd_i80_bus_config_t *bus_config, esp_lc
// install interrupt service, (LCD peripheral shares the same interrupt source with Camera peripheral with different mask) // install interrupt service, (LCD peripheral shares the same interrupt source with Camera peripheral with different mask)
// interrupt is disabled by default // interrupt is disabled by default
int isr_flags = LCD_I80_INTR_ALLOC_FLAGS | ESP_INTR_FLAG_SHARED | ESP_INTR_FLAG_LOWMED; int isr_flags = LCD_I80_INTR_ALLOC_FLAGS | ESP_INTR_FLAG_SHARED | ESP_INTR_FLAG_LOWMED;
ret = esp_intr_alloc_intrstatus(lcd_periph_signals.buses[bus_id].irq_id, isr_flags, ret = esp_intr_alloc_intrstatus(lcd_periph_i80_signals.buses[bus_id].irq_id, isr_flags,
(uint32_t)lcd_ll_get_interrupt_status_reg(bus->hal.dev), (uint32_t)lcd_ll_get_interrupt_status_reg(bus->hal.dev),
LCD_LL_EVENT_TRANS_DONE, lcd_default_isr_handler, bus, &bus->intr); LCD_LL_EVENT_TRANS_DONE, lcd_default_isr_handler, bus, &bus->intr);
ESP_GOTO_ON_ERROR(ret, err, TAG, "install interrupt failed"); ESP_GOTO_ON_ERROR(ret, err, TAG, "install interrupt failed");
lcd_ll_enable_interrupt(bus->hal.dev, LCD_LL_EVENT_TRANS_DONE, false); // disable all interrupts lcd_ll_enable_interrupt(bus->hal.dev, LCD_LL_EVENT_TRANS_DONE, false); // disable all interrupts
lcd_ll_clear_interrupt_status(bus->hal.dev, UINT32_MAX); // clear pending interrupt lcd_ll_clear_interrupt_status(bus->hal.dev, UINT32_MAX); // clear pending interrupt
// install DMA service // install DMA service
bus->bus_width = bus_config->bus_width;
ret = lcd_i80_init_dma_link(bus, bus_config); ret = lcd_i80_init_dma_link(bus, bus_config);
ESP_GOTO_ON_ERROR(ret, err, TAG, "install DMA failed"); ESP_GOTO_ON_ERROR(ret, err, TAG, "install DMA failed");
// disable RGB-LCD mode // disable RGB-LCD mode
@ -210,8 +248,11 @@ err:
gdma_disconnect(bus->dma_chan); gdma_disconnect(bus->dma_chan);
gdma_del_channel(bus->dma_chan); gdma_del_channel(bus->dma_chan);
} }
if (bus->dma_link) {
gdma_del_link_list(bus->dma_link);
}
if (bus->bus_id >= 0) { if (bus->bus_id >= 0) {
PERIPH_RCC_RELEASE_ATOMIC(lcd_periph_signals.panels[bus->bus_id].module, ref_count) { PERIPH_RCC_RELEASE_ATOMIC(lcd_periph_i80_signals.buses[bus->bus_id].module, ref_count) {
if (ref_count == 0) { if (ref_count == 0) {
lcd_ll_enable_bus_clock(bus->bus_id, false); lcd_ll_enable_bus_clock(bus->bus_id, false);
} }
@ -236,7 +277,7 @@ esp_err_t esp_lcd_del_i80_bus(esp_lcd_i80_bus_handle_t bus)
ESP_GOTO_ON_FALSE(LIST_EMPTY(&bus->device_list), ESP_ERR_INVALID_STATE, err, TAG, "device list not empty"); ESP_GOTO_ON_FALSE(LIST_EMPTY(&bus->device_list), ESP_ERR_INVALID_STATE, err, TAG, "device list not empty");
int bus_id = bus->bus_id; int bus_id = bus->bus_id;
lcd_com_remove_device(LCD_COM_DEVICE_TYPE_I80, bus_id); lcd_com_remove_device(LCD_COM_DEVICE_TYPE_I80, bus_id);
PERIPH_RCC_RELEASE_ATOMIC(lcd_periph_signals.panels[bus_id].module, ref_count) { PERIPH_RCC_RELEASE_ATOMIC(lcd_periph_i80_signals.buses[bus_id].module, ref_count) {
if (ref_count == 0) { if (ref_count == 0) {
lcd_ll_enable_bus_clock(bus_id, false); lcd_ll_enable_bus_clock(bus_id, false);
} }
@ -248,6 +289,7 @@ esp_err_t esp_lcd_del_i80_bus(esp_lcd_i80_bus_handle_t bus)
if (bus->pm_lock) { if (bus->pm_lock) {
esp_pm_lock_delete(bus->pm_lock); esp_pm_lock_delete(bus->pm_lock);
} }
gdma_del_link_list(bus->dma_link);
free(bus); free(bus);
ESP_LOGD(TAG, "del i80 bus(%d)", bus_id); ESP_LOGD(TAG, "del i80 bus(%d)", bus_id);
err: err:
@ -314,7 +356,7 @@ esp_err_t esp_lcd_new_panel_io_i80(esp_lcd_i80_bus_handle_t bus, const esp_lcd_p
if (io_config->cs_gpio_num >= 0) { if (io_config->cs_gpio_num >= 0) {
gpio_set_level(io_config->cs_gpio_num, !io_config->flags.cs_active_high); gpio_set_level(io_config->cs_gpio_num, !io_config->flags.cs_active_high);
gpio_set_direction(io_config->cs_gpio_num, GPIO_MODE_OUTPUT); gpio_set_direction(io_config->cs_gpio_num, GPIO_MODE_OUTPUT);
gpio_hal_iomux_func_sel(GPIO_PIN_MUX_REG[io_config->cs_gpio_num], PIN_FUNC_GPIO); gpio_func_sel(io_config->cs_gpio_num, PIN_FUNC_GPIO);
} }
*ret_io = &(i80_device->base); *ret_io = &(i80_device->base);
ESP_LOGD(TAG, "new i80 lcd panel io @%p on bus(%d)", i80_device, bus->bus_id); ESP_LOGD(TAG, "new i80 lcd panel io @%p on bus(%d)", i80_device, bus->bus_id);
@ -393,7 +435,8 @@ static uint32_t i80_lcd_prepare_param_buffer(esp_lcd_i80_bus_t *bus, lcd_panel_i
int param_per_size = i80_device->lcd_param_bits / 8; int param_per_size = i80_device->lcd_param_bits / 8;
int param_num = param_size / param_per_size; int param_num = param_size / param_per_size;
const uint8_t *from = (const uint8_t *)lcd_param; const uint8_t *from = (const uint8_t *)lcd_param;
uint8_t *to = bus->format_buffer; // we use non-cacheable address to write to the format buffer, for simplicity
uint8_t *to = bus->format_buffer_nc;
uint8_t step = bus->bus_width / 8; uint8_t step = bus->bus_width / 8;
int param_cycle = i80_device->lcd_param_bits / bus->bus_width; int param_cycle = i80_device->lcd_param_bits / bus->bus_width;
// in case bus_width=16 and param_bits=8, we still need 1 param_cycle // in case bus_width=16 and param_bits=8, we still need 1 param_cycle
@ -410,7 +453,7 @@ static uint32_t i80_lcd_prepare_param_buffer(esp_lcd_i80_bus_t *bus, lcd_panel_i
} }
to += step; to += step;
} }
return to - bus->format_buffer; return to - bus->format_buffer_nc;
} }
static esp_err_t panel_io_i80_tx_param(esp_lcd_panel_io_t *io, int lcd_cmd, const void *param, size_t param_size) static esp_err_t panel_io_i80_tx_param(esp_lcd_panel_io_t *io, int lcd_cmd, const void *param, size_t param_size)
@ -419,8 +462,8 @@ static esp_err_t panel_io_i80_tx_param(esp_lcd_panel_io_t *io, int lcd_cmd, cons
esp_lcd_i80_bus_t *bus = next_device->bus; esp_lcd_i80_bus_t *bus = next_device->bus;
lcd_panel_io_i80_t *cur_device = bus->cur_device; lcd_panel_io_i80_t *cur_device = bus->cur_device;
lcd_i80_trans_descriptor_t *trans_desc = NULL; lcd_i80_trans_descriptor_t *trans_desc = NULL;
assert(param_size <= (bus->num_dma_nodes * DMA_DESCRIPTOR_BUFFER_MAX_SIZE) && "parameter bytes too long, enlarge max_transfer_bytes"); assert(param_size <= (bus->num_dma_nodes * LCD_DMA_DESCRIPTOR_BUFFER_MAX_SIZE) && "parameter bytes too long, enlarge max_transfer_bytes");
assert(param_size <= CONFIG_LCD_PANEL_IO_FORMAT_BUF_SIZE && "format buffer too small, increase CONFIG_LCD_PANEL_IO_FORMAT_BUF_SIZE"); assert(param_size <= LCD_I80_IO_FORMAT_BUF_SIZE && "format buffer too small, increase LCD_I80_IO_FORMAT_BUF_SIZE");
uint32_t cmd_cycles = next_device->lcd_cmd_bits / bus->bus_width; uint32_t cmd_cycles = next_device->lcd_cmd_bits / bus->bus_width;
// in case bus_width=16 and cmd_bits=8, we still need 1 cmd_cycle // in case bus_width=16 and cmd_bits=8, we still need 1 cmd_cycle
if (cmd_cycles * bus->bus_width < next_device->lcd_cmd_bits) { if (cmd_cycles * bus->bus_width < next_device->lcd_cmd_bits) {
@ -456,7 +499,15 @@ static esp_err_t panel_io_i80_tx_param(esp_lcd_panel_io_t *io, int lcd_cmd, cons
trans_desc->data_length = trans_desc->data ? param_len : 0; trans_desc->data_length = trans_desc->data ? param_len : 0;
trans_desc->trans_done_cb = NULL; // no callback for parameter transaction trans_desc->trans_done_cb = NULL; // no callback for parameter transaction
// mount data to DMA links // mount data to DMA links
lcd_com_mount_dma_data(bus->dma_nodes, trans_desc->data, trans_desc->data_length); gdma_buffer_mount_config_t mount_config = {
.buffer = (void *)trans_desc->data,
.length = trans_desc->data_length,
.flags = {
.mark_eof = true,
.mark_final = true, // singly link list, mark final descriptor
}
};
gdma_link_mount_buffers(bus->dma_link, 0, &mount_config, 1, NULL);
// increase the pm lock reference count before starting a new transaction // increase the pm lock reference count before starting a new transaction
if (bus->pm_lock) { if (bus->pm_lock) {
esp_pm_lock_acquire(bus->pm_lock); esp_pm_lock_acquire(bus->pm_lock);
@ -476,17 +527,22 @@ static esp_err_t panel_io_i80_tx_color(esp_lcd_panel_io_t *io, int lcd_cmd, cons
lcd_panel_io_i80_t *i80_device = __containerof(io, lcd_panel_io_i80_t, base); lcd_panel_io_i80_t *i80_device = __containerof(io, lcd_panel_io_i80_t, base);
esp_lcd_i80_bus_t *bus = i80_device->bus; esp_lcd_i80_bus_t *bus = i80_device->bus;
lcd_i80_trans_descriptor_t *trans_desc = NULL; lcd_i80_trans_descriptor_t *trans_desc = NULL;
assert(color_size <= (bus->num_dma_nodes * DMA_DESCRIPTOR_BUFFER_MAX_SIZE) && "color bytes too long, enlarge max_transfer_bytes"); assert(color_size <= (bus->num_dma_nodes * LCD_DMA_DESCRIPTOR_BUFFER_MAX_SIZE) && "color bytes too long, enlarge max_transfer_bytes");
uint32_t cache_line_size = 0;
if (esp_ptr_external_ram(color)) { if (esp_ptr_external_ram(color)) {
// check alignment // check alignment
ESP_RETURN_ON_FALSE(((uint32_t)color & (bus->ext_mem_align - 1)) == 0, ESP_ERR_INVALID_ARG, TAG, "color address not aligned"); ESP_RETURN_ON_FALSE(((uint32_t)color & (bus->ext_mem_align - 1)) == 0, ESP_ERR_INVALID_ARG, TAG, "color address not aligned");
ESP_RETURN_ON_FALSE((color_size & (bus->ext_mem_align - 1)) == 0, ESP_ERR_INVALID_ARG, TAG, "color size not aligned"); ESP_RETURN_ON_FALSE((color_size & (bus->ext_mem_align - 1)) == 0, ESP_ERR_INVALID_ARG, TAG, "color size not aligned");
// flush frame buffer from cache to the physical PSRAM cache_line_size = cache_hal_get_cache_line_size(CACHE_LL_LEVEL_EXT_MEM, CACHE_TYPE_DATA);
esp_cache_msync((void *)color, color_size, ESP_CACHE_MSYNC_FLAG_DIR_C2M | ESP_CACHE_MSYNC_FLAG_UNALIGNED);
} else { } else {
// check alignment // check alignment
ESP_RETURN_ON_FALSE(((uint32_t)color & (bus->int_mem_align - 1)) == 0, ESP_ERR_INVALID_ARG, TAG, "color address not aligned"); ESP_RETURN_ON_FALSE(((uint32_t)color & (bus->int_mem_align - 1)) == 0, ESP_ERR_INVALID_ARG, TAG, "color address not aligned");
ESP_RETURN_ON_FALSE((color_size & (bus->int_mem_align - 1)) == 0, ESP_ERR_INVALID_ARG, TAG, "color size not aligned"); ESP_RETURN_ON_FALSE((color_size & (bus->int_mem_align - 1)) == 0, ESP_ERR_INVALID_ARG, TAG, "color size not aligned");
cache_line_size = cache_hal_get_cache_line_size(CACHE_LL_LEVEL_INT_MEM, CACHE_TYPE_DATA);
}
if (cache_line_size > 0) {
// flush data from cache to the physical memory
esp_cache_msync((void *)color, color_size, ESP_CACHE_MSYNC_FLAG_DIR_C2M | ESP_CACHE_MSYNC_FLAG_UNALIGNED);
} }
// in case bus_width=16 and cmd_bits=8, we still need 1 cmd_cycle // in case bus_width=16 and cmd_bits=8, we still need 1 cmd_cycle
@ -546,21 +602,11 @@ static esp_err_t lcd_i80_select_periph_clock(esp_lcd_i80_bus_handle_t bus, lcd_c
static esp_err_t lcd_i80_init_dma_link(esp_lcd_i80_bus_handle_t bus, const esp_lcd_i80_bus_config_t *bus_config) static esp_err_t lcd_i80_init_dma_link(esp_lcd_i80_bus_handle_t bus, const esp_lcd_i80_bus_config_t *bus_config)
{ {
esp_err_t ret = ESP_OK; esp_err_t ret = ESP_OK;
// chain DMA descriptors
for (int i = 0; i < bus->num_dma_nodes; i++) {
bus->dma_nodes[i].dw0.owner = DMA_DESCRIPTOR_BUFFER_OWNER_CPU;
bus->dma_nodes[i].next = &bus->dma_nodes[i + 1];
}
bus->dma_nodes[bus->num_dma_nodes - 1].next = NULL; // one-off DMA chain
// alloc DMA channel and connect to LCD peripheral // alloc DMA channel and connect to LCD peripheral
gdma_channel_alloc_config_t dma_chan_config = { gdma_channel_alloc_config_t dma_chan_config = {
.direction = GDMA_CHANNEL_DIRECTION_TX, .direction = GDMA_CHANNEL_DIRECTION_TX,
}; };
#if SOC_GDMA_TRIG_PERIPH_LCD0_BUS == SOC_GDMA_BUS_AHB ret = LCD_GDMA_NEW_CHANNEL(&dma_chan_config, &bus->dma_chan);
ret = gdma_new_ahb_channel(&dma_chan_config, &bus->dma_chan);
#elif SOC_GDMA_TRIG_PERIPH_LCD0_BUS == SOC_GDMA_BUS_AXI
ret = gdma_new_axi_channel(&dma_chan_config, &bus->dma_chan);
#endif
ESP_GOTO_ON_ERROR(ret, err, TAG, "alloc DMA channel failed"); ESP_GOTO_ON_ERROR(ret, err, TAG, "alloc DMA channel failed");
gdma_connect(bus->dma_chan, GDMA_MAKE_TRIGGER(GDMA_TRIG_PERIPH_LCD, 0)); gdma_connect(bus->dma_chan, GDMA_MAKE_TRIGGER(GDMA_TRIG_PERIPH_LCD, 0));
gdma_strategy_config_t strategy_config = { gdma_strategy_config_t strategy_config = {
@ -614,15 +660,15 @@ static esp_err_t lcd_i80_bus_configure_gpio(esp_lcd_i80_bus_handle_t bus, const
// connect peripheral signals via GPIO matrix // connect peripheral signals via GPIO matrix
for (size_t i = 0; i < bus_config->bus_width; i++) { for (size_t i = 0; i < bus_config->bus_width; i++) {
gpio_set_direction(bus_config->data_gpio_nums[i], GPIO_MODE_OUTPUT); gpio_set_direction(bus_config->data_gpio_nums[i], GPIO_MODE_OUTPUT);
esp_rom_gpio_connect_out_signal(bus_config->data_gpio_nums[i], lcd_periph_signals.buses[bus_id].data_sigs[i], false, false); esp_rom_gpio_connect_out_signal(bus_config->data_gpio_nums[i], lcd_periph_i80_signals.buses[bus_id].data_sigs[i], false, false);
gpio_hal_iomux_func_sel(GPIO_PIN_MUX_REG[bus_config->data_gpio_nums[i]], PIN_FUNC_GPIO); gpio_func_sel(bus_config->data_gpio_nums[i], PIN_FUNC_GPIO);
} }
gpio_set_direction(bus_config->dc_gpio_num, GPIO_MODE_OUTPUT); gpio_set_direction(bus_config->dc_gpio_num, GPIO_MODE_OUTPUT);
esp_rom_gpio_connect_out_signal(bus_config->dc_gpio_num, lcd_periph_signals.buses[bus_id].dc_sig, false, false); esp_rom_gpio_connect_out_signal(bus_config->dc_gpio_num, lcd_periph_i80_signals.buses[bus_id].dc_sig, false, false);
gpio_hal_iomux_func_sel(GPIO_PIN_MUX_REG[bus_config->dc_gpio_num], PIN_FUNC_GPIO); gpio_func_sel(bus_config->dc_gpio_num, PIN_FUNC_GPIO);
gpio_set_direction(bus_config->wr_gpio_num, GPIO_MODE_OUTPUT); gpio_set_direction(bus_config->wr_gpio_num, GPIO_MODE_OUTPUT);
esp_rom_gpio_connect_out_signal(bus_config->wr_gpio_num, lcd_periph_signals.buses[bus_id].wr_sig, false, false); esp_rom_gpio_connect_out_signal(bus_config->wr_gpio_num, lcd_periph_i80_signals.buses[bus_id].wr_sig, false, false);
gpio_hal_iomux_func_sel(GPIO_PIN_MUX_REG[bus_config->wr_gpio_num], PIN_FUNC_GPIO); gpio_func_sel(bus_config->wr_gpio_num, PIN_FUNC_GPIO);
return ESP_OK; return ESP_OK;
} }
@ -649,8 +695,11 @@ static void lcd_start_transaction(esp_lcd_i80_bus_t *bus, lcd_i80_trans_descript
lcd_ll_set_phase_cycles(bus->hal.dev, cmd_cycles, dummy_cycles, data_cycles); lcd_ll_set_phase_cycles(bus->hal.dev, cmd_cycles, dummy_cycles, data_cycles);
lcd_ll_set_blank_cycles(bus->hal.dev, 1, 1); lcd_ll_set_blank_cycles(bus->hal.dev, 1, 1);
// reset FIFO before starting a new transaction
lcd_ll_fifo_reset(bus->hal.dev);
if (trans_desc->data) { // some specific LCD commands can have no parameters if (trans_desc->data) { // some specific LCD commands can have no parameters
gdma_start(bus->dma_chan, (intptr_t)(bus->dma_nodes)); gdma_start(bus->dma_chan, gdma_link_get_head_addr(bus->dma_link));
// delay 1us is sufficient for DMA to pass data to LCD FIFO // delay 1us is sufficient for DMA to pass data to LCD FIFO
// in fact, this is only needed when LCD pixel clock is set too high // in fact, this is only needed when LCD pixel clock is set too high
esp_rom_delay_us(1); esp_rom_delay_us(1);
@ -676,7 +725,7 @@ static void lcd_i80_switch_devices(lcd_panel_io_i80_t *cur_device, lcd_panel_io_
} }
if (next_device->cs_gpio_num >= 0) { if (next_device->cs_gpio_num >= 0) {
// connect CS signal to the new device // connect CS signal to the new device
esp_rom_gpio_connect_out_signal(next_device->cs_gpio_num, lcd_periph_signals.buses[bus->bus_id].cs_sig, esp_rom_gpio_connect_out_signal(next_device->cs_gpio_num, lcd_periph_i80_signals.buses[bus->bus_id].cs_sig,
next_device->flags.cs_active_high, false); next_device->flags.cs_active_high, false);
} }
} }
@ -738,7 +787,15 @@ IRAM_ATTR static void lcd_default_isr_handler(void *args)
bus->cur_trans = trans_desc; bus->cur_trans = trans_desc;
bus->cur_device = next_device; bus->cur_device = next_device;
// mount data to DMA links // mount data to DMA links
lcd_com_mount_dma_data(bus->dma_nodes, trans_desc->data, trans_desc->data_length); gdma_buffer_mount_config_t mount_config = {
.buffer = (void *)trans_desc->data,
.length = trans_desc->data_length,
.flags = {
.mark_eof = true,
.mark_final = true, // singly link list, mark final descriptor
}
};
gdma_link_mount_buffers(bus->dma_link, 0, &mount_config, 1, NULL);
// enable interrupt again, because the new transaction can trigger new trans done event // enable interrupt again, because the new transaction can trigger new trans done event
esp_intr_enable(bus->intr); esp_intr_enable(bus->intr);
// increase the pm lock reference count before starting a new transaction // increase the pm lock reference count before starting a new transaction

View File

@ -19,6 +19,9 @@
extern "C" { extern "C" {
#endif #endif
// size of the internal buffer to transform the data into a proper format (e.g. data endian)
#define LCD_I80_IO_FORMAT_BUF_SIZE 32
#define LCD_I80_INTR_ALLOC_FLAGS ESP_INTR_FLAG_INTRDISABLED #define LCD_I80_INTR_ALLOC_FLAGS ESP_INTR_FLAG_INTRDISABLED
#define LCD_I80_MEM_ALLOC_CAPS MALLOC_CAP_DEFAULT #define LCD_I80_MEM_ALLOC_CAPS MALLOC_CAP_DEFAULT

View File

@ -196,7 +196,7 @@ static esp_err_t lcd_rgb_panel_destroy(esp_rgb_panel_t *rgb_panel)
lcd_ll_enable_clock(rgb_panel->hal.dev, false); lcd_ll_enable_clock(rgb_panel->hal.dev, false);
} }
if (rgb_panel->panel_id >= 0) { if (rgb_panel->panel_id >= 0) {
PERIPH_RCC_RELEASE_ATOMIC(lcd_periph_signals.panels[rgb_panel->panel_id].module, ref_count) { PERIPH_RCC_RELEASE_ATOMIC(lcd_periph_rgb_signals.panels[rgb_panel->panel_id].module, ref_count) {
if (ref_count == 0) { if (ref_count == 0) {
lcd_ll_enable_bus_clock(rgb_panel->panel_id, false); lcd_ll_enable_bus_clock(rgb_panel->panel_id, false);
} }
@ -306,7 +306,7 @@ esp_err_t esp_lcd_new_rgb_panel(const esp_lcd_rgb_panel_config_t *rgb_panel_conf
rgb_panel->panel_id = panel_id; rgb_panel->panel_id = panel_id;
// enable APB to access LCD registers // enable APB to access LCD registers
PERIPH_RCC_ACQUIRE_ATOMIC(lcd_periph_signals.panels[panel_id].module, ref_count) { PERIPH_RCC_ACQUIRE_ATOMIC(lcd_periph_rgb_signals.panels[panel_id].module, ref_count) {
if (ref_count == 0) { if (ref_count == 0) {
lcd_ll_enable_bus_clock(panel_id, true); lcd_ll_enable_bus_clock(panel_id, true);
lcd_ll_reset_register(panel_id); lcd_ll_reset_register(panel_id);
@ -332,7 +332,7 @@ esp_err_t esp_lcd_new_rgb_panel(const esp_lcd_rgb_panel_config_t *rgb_panel_conf
} }
// install interrupt service, (LCD peripheral shares the interrupt source with Camera by different mask) // install interrupt service, (LCD peripheral shares the interrupt source with Camera by different mask)
int isr_flags = LCD_RGB_INTR_ALLOC_FLAGS | ESP_INTR_FLAG_SHARED | ESP_INTR_FLAG_LOWMED; int isr_flags = LCD_RGB_INTR_ALLOC_FLAGS | ESP_INTR_FLAG_SHARED | ESP_INTR_FLAG_LOWMED;
ret = esp_intr_alloc_intrstatus(lcd_periph_signals.panels[panel_id].irq_id, isr_flags, ret = esp_intr_alloc_intrstatus(lcd_periph_rgb_signals.panels[panel_id].irq_id, isr_flags,
(uint32_t)lcd_ll_get_interrupt_status_reg(rgb_panel->hal.dev), (uint32_t)lcd_ll_get_interrupt_status_reg(rgb_panel->hal.dev),
LCD_LL_EVENT_VSYNC_END, lcd_default_isr_handler, rgb_panel, &rgb_panel->intr); LCD_LL_EVENT_VSYNC_END, lcd_default_isr_handler, rgb_panel, &rgb_panel->intr);
ESP_GOTO_ON_ERROR(ret, err, TAG, "install interrupt failed"); ESP_GOTO_ON_ERROR(ret, err, TAG, "install interrupt failed");
@ -801,7 +801,7 @@ static esp_err_t rgb_panel_invert_color(esp_lcd_panel_t *panel, bool invert_colo
// inverting the data line by GPIO matrix // inverting the data line by GPIO matrix
for (int i = 0; i < rgb_panel->data_width; i++) { for (int i = 0; i < rgb_panel->data_width; i++) {
if (rgb_panel->data_gpio_nums[i] >= 0) { if (rgb_panel->data_gpio_nums[i] >= 0) {
esp_rom_gpio_connect_out_signal(rgb_panel->data_gpio_nums[i], lcd_periph_signals.panels[panel_id].data_sigs[i], esp_rom_gpio_connect_out_signal(rgb_panel->data_gpio_nums[i], lcd_periph_rgb_signals.panels[panel_id].data_sigs[i],
invert_color_data, false); invert_color_data, false);
} }
} }
@ -857,34 +857,34 @@ static esp_err_t lcd_rgb_panel_configure_gpio(esp_rgb_panel_t *panel, const esp_
gpio_hal_iomux_func_sel(GPIO_PIN_MUX_REG[panel_config->data_gpio_nums[i]], PIN_FUNC_GPIO); gpio_hal_iomux_func_sel(GPIO_PIN_MUX_REG[panel_config->data_gpio_nums[i]], PIN_FUNC_GPIO);
gpio_set_direction(panel_config->data_gpio_nums[i], GPIO_MODE_OUTPUT); gpio_set_direction(panel_config->data_gpio_nums[i], GPIO_MODE_OUTPUT);
esp_rom_gpio_connect_out_signal(panel_config->data_gpio_nums[i], esp_rom_gpio_connect_out_signal(panel_config->data_gpio_nums[i],
lcd_periph_signals.panels[panel_id].data_sigs[i], false, false); lcd_periph_rgb_signals.panels[panel_id].data_sigs[i], false, false);
} }
} }
if (panel_config->hsync_gpio_num >= 0) { if (panel_config->hsync_gpio_num >= 0) {
gpio_hal_iomux_func_sel(GPIO_PIN_MUX_REG[panel_config->hsync_gpio_num], PIN_FUNC_GPIO); gpio_hal_iomux_func_sel(GPIO_PIN_MUX_REG[panel_config->hsync_gpio_num], PIN_FUNC_GPIO);
gpio_set_direction(panel_config->hsync_gpio_num, GPIO_MODE_OUTPUT); gpio_set_direction(panel_config->hsync_gpio_num, GPIO_MODE_OUTPUT);
esp_rom_gpio_connect_out_signal(panel_config->hsync_gpio_num, esp_rom_gpio_connect_out_signal(panel_config->hsync_gpio_num,
lcd_periph_signals.panels[panel_id].hsync_sig, false, false); lcd_periph_rgb_signals.panels[panel_id].hsync_sig, false, false);
} }
if (panel_config->vsync_gpio_num >= 0) { if (panel_config->vsync_gpio_num >= 0) {
gpio_hal_iomux_func_sel(GPIO_PIN_MUX_REG[panel_config->vsync_gpio_num], PIN_FUNC_GPIO); gpio_hal_iomux_func_sel(GPIO_PIN_MUX_REG[panel_config->vsync_gpio_num], PIN_FUNC_GPIO);
gpio_set_direction(panel_config->vsync_gpio_num, GPIO_MODE_OUTPUT); gpio_set_direction(panel_config->vsync_gpio_num, GPIO_MODE_OUTPUT);
esp_rom_gpio_connect_out_signal(panel_config->vsync_gpio_num, esp_rom_gpio_connect_out_signal(panel_config->vsync_gpio_num,
lcd_periph_signals.panels[panel_id].vsync_sig, false, false); lcd_periph_rgb_signals.panels[panel_id].vsync_sig, false, false);
} }
// PCLK may not be necessary in some cases (i.e. VGA output) // PCLK may not be necessary in some cases (i.e. VGA output)
if (panel_config->pclk_gpio_num >= 0) { if (panel_config->pclk_gpio_num >= 0) {
gpio_hal_iomux_func_sel(GPIO_PIN_MUX_REG[panel_config->pclk_gpio_num], PIN_FUNC_GPIO); gpio_hal_iomux_func_sel(GPIO_PIN_MUX_REG[panel_config->pclk_gpio_num], PIN_FUNC_GPIO);
gpio_set_direction(panel_config->pclk_gpio_num, GPIO_MODE_OUTPUT); gpio_set_direction(panel_config->pclk_gpio_num, GPIO_MODE_OUTPUT);
esp_rom_gpio_connect_out_signal(panel_config->pclk_gpio_num, esp_rom_gpio_connect_out_signal(panel_config->pclk_gpio_num,
lcd_periph_signals.panels[panel_id].pclk_sig, false, false); lcd_periph_rgb_signals.panels[panel_id].pclk_sig, false, false);
} }
// DE signal might not be necessary for some RGB LCD // DE signal might not be necessary for some RGB LCD
if (panel_config->de_gpio_num >= 0) { if (panel_config->de_gpio_num >= 0) {
gpio_hal_iomux_func_sel(GPIO_PIN_MUX_REG[panel_config->de_gpio_num], PIN_FUNC_GPIO); gpio_hal_iomux_func_sel(GPIO_PIN_MUX_REG[panel_config->de_gpio_num], PIN_FUNC_GPIO);
gpio_set_direction(panel_config->de_gpio_num, GPIO_MODE_OUTPUT); gpio_set_direction(panel_config->de_gpio_num, GPIO_MODE_OUTPUT);
esp_rom_gpio_connect_out_signal(panel_config->de_gpio_num, esp_rom_gpio_connect_out_signal(panel_config->de_gpio_num,
lcd_periph_signals.panels[panel_id].de_sig, false, false); lcd_periph_rgb_signals.panels[panel_id].de_sig, false, false);
} }
// disp enable GPIO is optional // disp enable GPIO is optional
if (panel_config->disp_gpio_num >= 0) { if (panel_config->disp_gpio_num >= 0) {

View File

@ -1,5 +1,5 @@
/* /*
* SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD * SPDX-FileCopyrightText: 2021-2024 Espressif Systems (Shanghai) CO LTD
* *
* SPDX-License-Identifier: Apache-2.0 * SPDX-License-Identifier: Apache-2.0
*/ */
@ -14,8 +14,8 @@
typedef struct esp_lcd_platform_t { typedef struct esp_lcd_platform_t {
portMUX_TYPE spinlock; // spinlock used to protect platform level resources portMUX_TYPE spinlock; // spinlock used to protect platform level resources
union { union {
void *panels[SOC_LCD_RGB_PANELS]; // array of RGB LCD panel instances void *panels[SOC_LCDCAM_RGB_NUM_PANELS]; // array of RGB LCD panel instances
void *buses[SOC_LCD_I80_BUSES]; // array of i80 bus instances void *buses[SOC_LCDCAM_I80_NUM_BUSES]; // array of i80 bus instances
}; // LCD peripheral can only work under either RGB mode or intel 8080 mode }; // LCD peripheral can only work under either RGB mode or intel 8080 mode
} esp_lcd_platform_t; } esp_lcd_platform_t;
@ -30,7 +30,7 @@ int lcd_com_register_device(lcd_com_device_type_t device_type, void *device_obj)
switch (device_type) { switch (device_type) {
case LCD_COM_DEVICE_TYPE_I80: case LCD_COM_DEVICE_TYPE_I80:
// search for a bus slot then register to platform // search for a bus slot then register to platform
for (int i = 0; (i < SOC_LCD_I80_BUSES) && (member_id == -1); i++) { for (int i = 0; (i < SOC_LCDCAM_I80_NUM_BUSES) && (member_id == -1); i++) {
portENTER_CRITICAL(&s_lcd_platform.spinlock); portENTER_CRITICAL(&s_lcd_platform.spinlock);
if (!s_lcd_platform.buses[i]) { if (!s_lcd_platform.buses[i]) {
s_lcd_platform.buses[i] = device_obj; s_lcd_platform.buses[i] = device_obj;
@ -41,7 +41,7 @@ int lcd_com_register_device(lcd_com_device_type_t device_type, void *device_obj)
break; break;
case LCD_COM_DEVICE_TYPE_RGB: case LCD_COM_DEVICE_TYPE_RGB:
// search for a panel slot then register to platform // search for a panel slot then register to platform
for (int i = 0; (i < SOC_LCD_RGB_PANELS) && (member_id == -1); i++) { for (int i = 0; (i < SOC_LCDCAM_RGB_NUM_PANELS) && (member_id == -1); i++) {
portENTER_CRITICAL(&s_lcd_platform.spinlock); portENTER_CRITICAL(&s_lcd_platform.spinlock);
if (!s_lcd_platform.panels[i]) { if (!s_lcd_platform.panels[i]) {
s_lcd_platform.panels[i] = device_obj; s_lcd_platform.panels[i] = device_obj;

View File

@ -1,4 +1,4 @@
| Supported Targets | ESP32 | ESP32-S2 | ESP32-S3 | | Supported Targets | ESP32 | ESP32-P4 | ESP32-S2 | ESP32-S3 |
| ----------------- | ----- | -------- | -------- | | ----------------- | ----- | -------- | -------- | -------- |
This test app is used to test LCDs with intel 8080 interface. This test app is used to test LCDs with intel 8080 interface.

View File

@ -1,5 +1,5 @@
/* /*
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD * SPDX-FileCopyrightText: 2022-2024 Espressif Systems (Shanghai) CO LTD
* *
* SPDX-License-Identifier: Apache-2.0 * SPDX-License-Identifier: Apache-2.0
*/ */
@ -12,7 +12,7 @@ extern "C" {
#define TEST_LCD_H_RES (240) #define TEST_LCD_H_RES (240)
#define TEST_LCD_V_RES (280) #define TEST_LCD_V_RES (280)
#if CONFIG_IDF_TARGET_ESP32S3 #if CONFIG_IDF_TARGET_ESP32S3 || CONFIG_IDF_TARGET_ESP32P4
#define TEST_LCD_BK_LIGHT_GPIO (1) #define TEST_LCD_BK_LIGHT_GPIO (1)
#define TEST_LCD_RST_GPIO (2) #define TEST_LCD_RST_GPIO (2)
#define TEST_LCD_CS_GPIO (3) #define TEST_LCD_CS_GPIO (3)

View File

@ -1,5 +1,5 @@
/* /*
* SPDX-FileCopyrightText: 2015-2023 Espressif Systems (Shanghai) CO LTD * SPDX-FileCopyrightText: 2015-2024 Espressif Systems (Shanghai) CO LTD
* *
* SPDX-License-Identifier: CC0-1.0 * SPDX-License-Identifier: CC0-1.0
*/ */
@ -18,7 +18,7 @@
#include "driver/gpio.h" #include "driver/gpio.h"
#include "test_i80_board.h" #include "test_i80_board.h"
#if SOC_I2S_LCD_I80_VARIANT #if SOC_I2S_SUPPORTS_LCD_CAMERA
#include "driver/i2s_std.h" #include "driver/i2s_std.h"
TEST_CASE("i80_and_i2s_driver_co-existence", "[lcd][i2s]") TEST_CASE("i80_and_i2s_driver_co-existence", "[lcd][i2s]")
@ -49,9 +49,9 @@ TEST_CASE("i80_and_i2s_driver_co-existence", "[lcd][i2s]")
TEST_ASSERT_EQUAL(ESP_ERR_NOT_FOUND, i2s_new_channel(&chan_cfg, &tx_handle, NULL)); TEST_ASSERT_EQUAL(ESP_ERR_NOT_FOUND, i2s_new_channel(&chan_cfg, &tx_handle, NULL));
TEST_ESP_OK(esp_lcd_del_i80_bus(i80_bus)); TEST_ESP_OK(esp_lcd_del_i80_bus(i80_bus));
} }
#endif // SOC_I2S_LCD_I80_VARIANT #endif // SOC_I2S_SUPPORTS_LCD_CAMERA
#if SOC_LCDCAM_SUPPORTED #if SOC_LCDCAM_I80_LCD_SUPPORTED
TEST_CASE("lcd_i80_device_swap_color_bytes", "[lcd]") TEST_CASE("lcd_i80_device_swap_color_bytes", "[lcd]")
{ {
esp_lcd_i80_bus_handle_t i80_bus = NULL; esp_lcd_i80_bus_handle_t i80_bus = NULL;
@ -173,11 +173,16 @@ TEST_CASE("lcd_i80_device_clock_mode", "[lcd]")
} }
TEST_ESP_OK(esp_lcd_del_i80_bus(i80_bus)); TEST_ESP_OK(esp_lcd_del_i80_bus(i80_bus));
} }
#endif // SOC_LCDCAM_SUPPORTED #endif // SOC_LCDCAM_I80_LCD_SUPPORTED
TEST_CASE("lcd_i80_bus_and_device_allocation", "[lcd]") TEST_CASE("lcd_i80_bus_and_device_allocation", "[lcd]")
{ {
esp_lcd_i80_bus_handle_t i80_buses[SOC_LCD_I80_BUSES] = {}; #if SOC_I2S_SUPPORTS_LCD_CAMERA
#define TEST_NUM_LCD_I80_BUSES SOC_LCD_I80_BUSES
#elif SOC_LCDCAM_I80_LCD_SUPPORTED
#define TEST_NUM_LCD_I80_BUSES SOC_LCDCAM_I80_NUM_BUSES
#endif
esp_lcd_i80_bus_handle_t i80_buses[TEST_NUM_LCD_I80_BUSES] = {};
esp_lcd_i80_bus_config_t bus_config = { esp_lcd_i80_bus_config_t bus_config = {
.dc_gpio_num = TEST_LCD_DC_GPIO, .dc_gpio_num = TEST_LCD_DC_GPIO,
.wr_gpio_num = TEST_LCD_PCLK_GPIO, .wr_gpio_num = TEST_LCD_PCLK_GPIO,
@ -195,7 +200,7 @@ TEST_CASE("lcd_i80_bus_and_device_allocation", "[lcd]")
.bus_width = 8, .bus_width = 8,
.max_transfer_bytes = TEST_LCD_H_RES * 40 * sizeof(uint16_t) .max_transfer_bytes = TEST_LCD_H_RES * 40 * sizeof(uint16_t)
}; };
for (int i = 0; i < SOC_LCD_I80_BUSES; i++) { for (int i = 0; i < TEST_NUM_LCD_I80_BUSES; i++) {
TEST_ESP_OK(esp_lcd_new_i80_bus(&bus_config, &i80_buses[i])); TEST_ESP_OK(esp_lcd_new_i80_bus(&bus_config, &i80_buses[i]));
} }
TEST_ASSERT_EQUAL(ESP_ERR_NOT_FOUND, esp_lcd_new_i80_bus(&bus_config, &i80_buses[0])); TEST_ASSERT_EQUAL(ESP_ERR_NOT_FOUND, esp_lcd_new_i80_bus(&bus_config, &i80_buses[0]));
@ -215,7 +220,7 @@ TEST_CASE("lcd_i80_bus_and_device_allocation", "[lcd]")
for (int i = 0; i < 10; i++) { for (int i = 0; i < 10; i++) {
TEST_ESP_OK(esp_lcd_panel_io_del(io_handles[i])); TEST_ESP_OK(esp_lcd_panel_io_del(io_handles[i]));
} }
for (int i = 0; i < SOC_LCD_I80_BUSES; i++) { for (int i = 0; i < TEST_NUM_LCD_I80_BUSES; i++) {
TEST_ESP_OK(esp_lcd_del_i80_bus(i80_buses[i])); TEST_ESP_OK(esp_lcd_del_i80_bus(i80_buses[i]));
} }
} }
@ -455,7 +460,7 @@ TEST_CASE("lcd_panel_with_i80_interface_(st7789, 8bits)", "[lcd]")
} }
// TODO: support the test on I2S LCD (IDF-7202) // TODO: support the test on I2S LCD (IDF-7202)
#if !SOC_I2S_LCD_I80_VARIANT #if !SOC_I2S_SUPPORTS_LCD_CAMERA
TEST_CASE("i80_lcd_send_colors_to_fixed_region", "[lcd]") TEST_CASE("i80_lcd_send_colors_to_fixed_region", "[lcd]")
{ {
int x_start = 100; int x_start = 100;

View File

@ -1,6 +1,5 @@
# SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD # SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD
# SPDX-License-Identifier: CC0-1.0 # SPDX-License-Identifier: CC0-1.0
import pytest import pytest
from pytest_embedded import Dut from pytest_embedded import Dut
@ -8,6 +7,7 @@ from pytest_embedded import Dut
@pytest.mark.esp32 @pytest.mark.esp32
@pytest.mark.esp32s2 @pytest.mark.esp32s2
@pytest.mark.esp32s3 @pytest.mark.esp32s3
@pytest.mark.esp32p4
@pytest.mark.generic @pytest.mark.generic
@pytest.mark.parametrize( @pytest.mark.parametrize(
'config', 'config',

View File

@ -1,5 +1,5 @@
/* /*
* SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD * SPDX-FileCopyrightText: 2022-2024 Espressif Systems (Shanghai) CO LTD
* *
* SPDX-License-Identifier: Apache-2.0 * SPDX-License-Identifier: Apache-2.0
*/ */
@ -507,7 +507,8 @@ static inline void lcd_ll_reverse_wire_bit_order(lcd_cam_dev_t *dev, bool en)
* @brief Whether to swap adjacent two bytes * @brief Whether to swap adjacent two bytes
* *
* @note This acts before the YUV-RGB converter, mainly to change the data endian. * @note This acts before the YUV-RGB converter, mainly to change the data endian.
* {B1,B0},{B3,B2} => {B0,B1}{B2,B3} * e.g. {B1,B0},{B3,B2} => {B0,B1}{B2,B3}.
* Only valid when `lcd_ll_set_dma_read_stride` set the DMA read stride >= 16 bits
* *
* @param dev LCD register base address * @param dev LCD register base address
* @param en True to swap the byte order, False to not swap * @param en True to swap the byte order, False to not swap
@ -606,7 +607,7 @@ static inline void lcd_ll_set_command(lcd_cam_dev_t *dev, uint32_t data_width, u
} }
/** /**
* @brief Wether to enable RGB interface * @brief Whether to enable RGB interface
* *
* @param dev LCD register base address * @param dev LCD register base address
* @param en True to enable RGB interface, False to disable RGB interface * @param en True to enable RGB interface, False to disable RGB interface
@ -629,7 +630,7 @@ static inline void lcd_ll_enable_auto_next_frame(lcd_cam_dev_t *dev, bool en)
} }
/** /**
* @brief Wether to output HSYNC signal in porch resion * @brief Whether to output HSYNC signal in porch resion
* *
* @param dev LCD register base address * @param dev LCD register base address
* @param en True to enable, False to disable * @param en True to enable, False to disable
@ -746,7 +747,7 @@ static inline uint32_t lcd_ll_get_interrupt_status(lcd_cam_dev_t *dev)
* @brief Clear interrupt status by mask * @brief Clear interrupt status by mask
* *
* @param dev LCD register base address * @param dev LCD register base address
* @param mask Interupt status mask * @param mask Interrupt status mask
*/ */
__attribute__((always_inline)) __attribute__((always_inline))
static inline void lcd_ll_clear_interrupt_status(lcd_cam_dev_t *dev, uint32_t mask) static inline void lcd_ll_clear_interrupt_status(lcd_cam_dev_t *dev, uint32_t mask)

View File

@ -14,14 +14,14 @@
extern "C" { extern "C" {
#endif #endif
#if SOC_LCD_I80_SUPPORTED || SOC_LCD_RGB_SUPPORTED #if SOC_I2S_SUPPORTS_LCD_CAMERA || SOC_LCDCAM_SUPPORTED
/** /**
* @brief LCD clock source * @brief LCD clock source
*/ */
typedef soc_periph_lcd_clk_src_t lcd_clock_source_t; typedef soc_periph_lcd_clk_src_t lcd_clock_source_t;
#else #else
typedef int lcd_clock_source_t; typedef int lcd_clock_source_t;
#endif // SOC_LCD_I80_SUPPORTED || SOC_LCD_RGB_SUPPORTED #endif // SOC_I2S_SUPPORTS_LCD_CAMERA || SOC_LCDCAM_SUPPORTED
/** /**
* @brief RGB data endian * @brief RGB data endian

View File

@ -89,7 +89,7 @@ if(CONFIG_SOC_GPTIMER_SUPPORTED)
list(APPEND srcs "${target_folder}/timer_periph.c") list(APPEND srcs "${target_folder}/timer_periph.c")
endif() endif()
if(CONFIG_SOC_LCDCAM_SUPPORTED OR CONFIG_SOC_LCD_I80_SUPPORTED) if(CONFIG_SOC_LCDCAM_SUPPORTED OR CONFIG_SOC_I2S_SUPPORTS_LCD_CAMERA)
list(APPEND srcs "${target_folder}/lcd_periph.c") list(APPEND srcs "${target_folder}/lcd_periph.c")
endif() endif()

View File

@ -443,6 +443,10 @@ config SOC_I2S_SUPPORTS_LCD_CAMERA
bool bool
default y default y
config SOC_I2S_MAX_DATA_WIDTH
int
default 24
config SOC_I2S_TRANS_SIZE_ALIGN_WORD config SOC_I2S_TRANS_SIZE_ALIGN_WORD
bool bool
default y default y

View File

@ -225,6 +225,7 @@
#define SOC_I2S_SUPPORTS_ADC (1) #define SOC_I2S_SUPPORTS_ADC (1)
#define SOC_I2S_SUPPORTS_DAC (1) #define SOC_I2S_SUPPORTS_DAC (1)
#define SOC_I2S_SUPPORTS_LCD_CAMERA (1) #define SOC_I2S_SUPPORTS_LCD_CAMERA (1)
#define SOC_I2S_MAX_DATA_WIDTH (24)
#define SOC_I2S_TRANS_SIZE_ALIGN_WORD (1) // I2S DMA transfer size must be aligned to word #define SOC_I2S_TRANS_SIZE_ALIGN_WORD (1) // I2S DMA transfer size must be aligned to word
#define SOC_I2S_LCD_I80_VARIANT (1) // I2S has a special LCD mode that can generate Intel 8080 TX timing #define SOC_I2S_LCD_I80_VARIANT (1) // I2S has a special LCD mode that can generate Intel 8080 TX timing

View File

@ -8,7 +8,7 @@
#include "soc/lcd_periph.h" #include "soc/lcd_periph.h"
#include "soc/gpio_sig_map.h" #include "soc/gpio_sig_map.h"
const lcd_signal_conn_t lcd_periph_signals = { const lcd_i2s_signal_conn_t lcd_periph_i2s_signals = {
.buses = { .buses = {
[0] = { [0] = {
.module = PERIPH_I2S0_MODULE, .module = PERIPH_I2S0_MODULE,

View File

@ -47,10 +47,18 @@ config SOC_PCNT_SUPPORTED
bool bool
default y default y
config SOC_LCDCAM_SUPPORTED
bool
default y
config SOC_LCDCAM_CAM_SUPPORTED config SOC_LCDCAM_CAM_SUPPORTED
bool bool
default y default y
config SOC_LCDCAM_I80_LCD_SUPPORTED
bool
default y
config SOC_MIPI_CSI_SUPPORTED config SOC_MIPI_CSI_SUPPORTED
bool bool
default y default y
@ -1071,26 +1079,22 @@ config SOC_RMT_SUPPORT_SLEEP_RETENTION
bool bool
default y default y
config SOC_LCD_I80_BUSES config SOC_LCD_I80_SUPPORTED
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 bool
default y default y
config SOC_LCDCAM_I80_NUM_BUSES
int
default 1
config SOC_LCDCAM_I80_BUS_WIDTH
int
default 24
config SOC_LCDCAM_RGB_NUM_PANELS
int
default 1
config SOC_MCPWM_GROUPS config SOC_MCPWM_GROUPS
int int
default 2 default 2

View File

@ -28,8 +28,10 @@
#define SOC_DMA2D_SUPPORTED 1 #define SOC_DMA2D_SUPPORTED 1
#define SOC_GPTIMER_SUPPORTED 1 #define SOC_GPTIMER_SUPPORTED 1
#define SOC_PCNT_SUPPORTED 1 #define SOC_PCNT_SUPPORTED 1
// #define SOC_LCDCAM_SUPPORTED 1 // TODO: IDF-7465 #define SOC_LCDCAM_SUPPORTED 1
#define SOC_LCDCAM_CAM_SUPPORTED 1 #define SOC_LCDCAM_CAM_SUPPORTED 1
#define SOC_LCDCAM_I80_LCD_SUPPORTED 1 // support the Intel 8080 bus driver based on the LCD_CAM peripheral
// #define SOC_LCDCAM_RGB_LCD_SUPPORTED 1 // TODO: IDF-7465
#define SOC_MIPI_CSI_SUPPORTED 1 #define SOC_MIPI_CSI_SUPPORTED 1
#define SOC_MIPI_DSI_SUPPORTED 1 #define SOC_MIPI_DSI_SUPPORTED 1
#define SOC_MCPWM_SUPPORTED 1 #define SOC_MCPWM_SUPPORTED 1
@ -404,14 +406,13 @@
#define SOC_RMT_SUPPORT_SLEEP_RETENTION 1 /*!< The sleep retention feature can help back up RMT registers before sleep */ #define SOC_RMT_SUPPORT_SLEEP_RETENTION 1 /*!< The sleep retention feature can help back up RMT registers before sleep */
/*-------------------------- LCD CAPS ----------------------------------------*/ /*-------------------------- LCD CAPS ----------------------------------------*/
/* I80 bus and RGB timing generator can't work at the same time */ /* I80 bus and RGB timing generator can't work at the same time in the LCD_CAM peripheral */
// #define SOC_LCD_I80_SUPPORTED (1) /*!< Intel 8080 LCD is supported */ // TODO: IDF-7465 #define SOC_LCD_I80_SUPPORTED 1 /*!< support intel 8080 driver */
// #define SOC_LCD_RGB_SUPPORTED (1) /*!< RGB LCD is supported */ // TODO: IDF-7465 #define SOC_LCDCAM_I80_NUM_BUSES 1U /*!< LCD_CAM peripheral provides one LCD Intel 8080 bus */
#define SOC_LCD_I80_BUSES (1U) /*!< Has one LCD Intel 8080 bus */ #define SOC_LCDCAM_I80_BUS_WIDTH 24 /*!< Intel 8080 bus max data width */
#define SOC_LCD_RGB_PANELS (1U) /*!< Support one RGB LCD panel */ #define SOC_LCDCAM_RGB_NUM_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_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 */
#define SOC_LCD_SUPPORT_RGB_YUV_CONV (1) /*!< Support color format conversion between RGB and YUV */
/*-------------------------- MCPWM CAPS --------------------------------------*/ /*-------------------------- MCPWM CAPS --------------------------------------*/
#define SOC_MCPWM_GROUPS (2U) ///< 2 MCPWM groups on the chip (i.e., the number of independent MCPWM peripherals) #define SOC_MCPWM_GROUPS (2U) ///< 2 MCPWM groups on the chip (i.e., the number of independent MCPWM peripherals)

View File

@ -1,5 +1,5 @@
/* /*
* SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD * SPDX-FileCopyrightText: 2022-2024 Espressif Systems (Shanghai) CO LTD
* *
* SPDX-License-Identifier: Apache-2.0 * SPDX-License-Identifier: Apache-2.0
*/ */
@ -7,7 +7,8 @@
#include "soc/lcd_periph.h" #include "soc/lcd_periph.h"
#include "soc/gpio_sig_map.h" #include "soc/gpio_sig_map.h"
const lcd_signal_conn_t lcd_periph_signals = { #if SOC_LCDCAM_I80_LCD_SUPPORTED
const lcd_i80_signal_conn_t lcd_periph_i80_signals = {
.buses = { .buses = {
[0] = { [0] = {
.module = PERIPH_LCD_CAM_MODULE, .module = PERIPH_LCD_CAM_MODULE,
@ -42,7 +43,12 @@ const lcd_signal_conn_t lcd_periph_signals = {
.dc_sig = LCD_DC_PAD_OUT_IDX, .dc_sig = LCD_DC_PAD_OUT_IDX,
.wr_sig = LCD_PCLK_PAD_OUT_IDX .wr_sig = LCD_PCLK_PAD_OUT_IDX
} }
}, }
};
#endif // SOC_LCDCAM_I80_LCD_SUPPORTED
#if SOC_LCDCAM_RGB_LCD_SUPPORTED
const lcd_rgb_signal_conn_t lcd_periph_rgb_signals = {
.panels = { .panels = {
[0] = { [0] = {
.module = PERIPH_LCD_CAM_MODULE, .module = PERIPH_LCD_CAM_MODULE,
@ -80,3 +86,4 @@ const lcd_signal_conn_t lcd_periph_signals = {
} }
} }
}; };
#endif // SOC_LCDCAM_RGB_LCD_SUPPORTED

View File

@ -487,6 +487,10 @@ config SOC_I2S_SUPPORTS_LCD_CAMERA
bool bool
default y default y
config SOC_I2S_MAX_DATA_WIDTH
int
default 24
config SOC_I2S_APLL_MIN_FREQ config SOC_I2S_APLL_MIN_FREQ
int int
default 250000000 default 250000000

View File

@ -216,6 +216,7 @@
#define SOC_I2S_SUPPORTS_PLL_F160M (1) #define SOC_I2S_SUPPORTS_PLL_F160M (1)
#define SOC_I2S_SUPPORTS_DMA_EQUAL (1) #define SOC_I2S_SUPPORTS_DMA_EQUAL (1)
#define SOC_I2S_SUPPORTS_LCD_CAMERA (1) #define SOC_I2S_SUPPORTS_LCD_CAMERA (1)
#define SOC_I2S_MAX_DATA_WIDTH (24)
#define SOC_I2S_APLL_MIN_FREQ (250000000) #define SOC_I2S_APLL_MIN_FREQ (250000000)
#define SOC_I2S_APLL_MAX_FREQ (500000000) #define SOC_I2S_APLL_MAX_FREQ (500000000)
#define SOC_I2S_APLL_MIN_RATE (10675) //in Hz, I2S Clock rate limited by hardware #define SOC_I2S_APLL_MIN_RATE (10675) //in Hz, I2S Clock rate limited by hardware

View File

@ -7,7 +7,7 @@
#include "soc/lcd_periph.h" #include "soc/lcd_periph.h"
#include "soc/gpio_sig_map.h" #include "soc/gpio_sig_map.h"
const lcd_signal_conn_t lcd_periph_signals = { const lcd_i2s_signal_conn_t lcd_periph_i2s_signals = {
.buses = { .buses = {
[0] = { [0] = {
.module = PERIPH_I2S0_MODULE, .module = PERIPH_I2S0_MODULE,

View File

@ -63,6 +63,14 @@ config SOC_LCDCAM_SUPPORTED
bool bool
default y default y
config SOC_LCDCAM_I80_LCD_SUPPORTED
bool
default y
config SOC_LCDCAM_RGB_LCD_SUPPORTED
bool
default y
config SOC_MCPWM_SUPPORTED config SOC_MCPWM_SUPPORTED
bool bool
default y default y
@ -767,6 +775,22 @@ config SOC_LCD_SUPPORT_RGB_YUV_CONV
bool bool
default y default y
config SOC_LCDCAM_I80_NUM_BUSES
int
default 1
config SOC_LCDCAM_I80_BUS_WIDTH
int
default 16
config SOC_LCDCAM_RGB_NUM_PANELS
int
default 1
config SOC_LCDCAM_RGB_DATA_WIDTH
int
default 16
config SOC_RTC_CNTL_CPU_PD_DMA_BUS_WIDTH config SOC_RTC_CNTL_CPU_PD_DMA_BUS_WIDTH
int int
default 128 default 128

View File

@ -32,6 +32,8 @@
#define SOC_AHB_GDMA_SUPPORTED 1 #define SOC_AHB_GDMA_SUPPORTED 1
#define SOC_GPTIMER_SUPPORTED 1 #define SOC_GPTIMER_SUPPORTED 1
#define SOC_LCDCAM_SUPPORTED 1 #define SOC_LCDCAM_SUPPORTED 1
#define SOC_LCDCAM_I80_LCD_SUPPORTED 1
#define SOC_LCDCAM_RGB_LCD_SUPPORTED 1
#define SOC_MCPWM_SUPPORTED 1 #define SOC_MCPWM_SUPPORTED 1
#define SOC_DEDICATED_GPIO_SUPPORTED 1 #define SOC_DEDICATED_GPIO_SUPPORTED 1
#define SOC_CACHE_SUPPORT_WRAP 1 #define SOC_CACHE_SUPPORT_WRAP 1
@ -291,6 +293,10 @@
#define SOC_LCD_I80_BUS_WIDTH (16) /*!< Intel 8080 bus width */ #define SOC_LCD_I80_BUS_WIDTH (16) /*!< Intel 8080 bus width */
#define SOC_LCD_RGB_DATA_WIDTH (16) /*!< Number of LCD data lines */ #define SOC_LCD_RGB_DATA_WIDTH (16) /*!< Number of LCD data lines */
#define SOC_LCD_SUPPORT_RGB_YUV_CONV (1) /*!< Support color format conversion between RGB and YUV */ #define SOC_LCD_SUPPORT_RGB_YUV_CONV (1) /*!< Support color format conversion between RGB and YUV */
#define SOC_LCDCAM_I80_NUM_BUSES (1U) /*!< LCD_CAM peripheral provides one LCD Intel 8080 bus */
#define SOC_LCDCAM_I80_BUS_WIDTH (16) /*!< Intel 8080 bus max data width */
#define SOC_LCDCAM_RGB_NUM_PANELS (1U) /*!< LCD_CAM peripheral provides one RGB panel */
#define SOC_LCDCAM_RGB_DATA_WIDTH (16) /*!< RGB panel max data width */
/*-------------------------- RTC CAPS --------------------------------------*/ /*-------------------------- RTC CAPS --------------------------------------*/
#define SOC_RTC_CNTL_CPU_PD_DMA_BUS_WIDTH (128) #define SOC_RTC_CNTL_CPU_PD_DMA_BUS_WIDTH (128)

View File

@ -1,5 +1,5 @@
/* /*
* SPDX-FileCopyrightText: 2021 Espressif Systems (Shanghai) CO LTD * SPDX-FileCopyrightText: 2021-2024 Espressif Systems (Shanghai) CO LTD
* *
* SPDX-License-Identifier: Apache-2.0 * SPDX-License-Identifier: Apache-2.0
*/ */
@ -7,7 +7,7 @@
#include "soc/lcd_periph.h" #include "soc/lcd_periph.h"
#include "soc/gpio_sig_map.h" #include "soc/gpio_sig_map.h"
const lcd_signal_conn_t lcd_periph_signals = { const lcd_i80_signal_conn_t lcd_periph_i80_signals = {
.buses = { .buses = {
[0] = { [0] = {
.module = PERIPH_LCD_CAM_MODULE, .module = PERIPH_LCD_CAM_MODULE,
@ -34,7 +34,10 @@ const lcd_signal_conn_t lcd_periph_signals = {
.dc_sig = LCD_DC_IDX, .dc_sig = LCD_DC_IDX,
.wr_sig = LCD_PCLK_IDX .wr_sig = LCD_PCLK_IDX
} }
}, }
};
const lcd_rgb_signal_conn_t lcd_periph_rgb_signals = {
.panels = { .panels = {
[0] = { [0] = {
.module = PERIPH_LCD_CAM_MODULE, .module = PERIPH_LCD_CAM_MODULE,

View File

@ -1,5 +1,5 @@
/* /*
* SPDX-FileCopyrightText: 2021 Espressif Systems (Shanghai) CO LTD * SPDX-FileCopyrightText: 2021-2024 Espressif Systems (Shanghai) CO LTD
* *
* SPDX-License-Identifier: Apache-2.0 * SPDX-License-Identifier: Apache-2.0
*/ */
@ -13,42 +13,49 @@
extern "C" { extern "C" {
#endif #endif
#if SOC_LCDCAM_SUPPORTED #if SOC_LCDCAM_I80_LCD_SUPPORTED
typedef struct { typedef struct {
struct { struct {
const periph_module_t module; const periph_module_t module;
const int irq_id; const int irq_id;
const int data_sigs[SOC_LCD_I80_BUS_WIDTH]; const int data_sigs[SOC_LCDCAM_I80_BUS_WIDTH];
const int cs_sig; const int cs_sig;
const int dc_sig; const int dc_sig;
const int wr_sig; const int wr_sig;
} buses[SOC_LCD_I80_BUSES]; } buses[SOC_LCDCAM_I80_NUM_BUSES];
} lcd_i80_signal_conn_t;
extern const lcd_i80_signal_conn_t lcd_periph_i80_signals;
#endif // SOC_LCDCAM_I80_LCD_SUPPORTED
#if SOC_LCDCAM_RGB_LCD_SUPPORTED
typedef struct {
struct { struct {
const periph_module_t module; const periph_module_t module;
const int irq_id; const int irq_id;
const int data_sigs[SOC_LCD_RGB_DATA_WIDTH]; const int data_sigs[SOC_LCDCAM_RGB_DATA_WIDTH];
const int hsync_sig; const int hsync_sig;
const int vsync_sig; const int vsync_sig;
const int pclk_sig; const int pclk_sig;
const int de_sig; const int de_sig;
} panels[SOC_LCD_RGB_PANELS]; } panels[SOC_LCDCAM_RGB_NUM_PANELS];
} lcd_signal_conn_t; } lcd_rgb_signal_conn_t;
extern const lcd_signal_conn_t lcd_periph_signals; extern const lcd_rgb_signal_conn_t lcd_periph_rgb_signals;
#endif // SOC_LCDCAM_SUPPORTED #endif // SOC_LCDCAM_RGB_LCD_SUPPORTED
#if SOC_I2S_LCD_I80_VARIANT #if SOC_I2S_SUPPORTS_LCD_CAMERA
typedef struct { typedef struct {
struct { struct {
const periph_module_t module; const periph_module_t module;
const int irq_id; const int irq_id;
const int data_sigs[SOC_LCD_I80_BUS_WIDTH]; const int data_sigs[SOC_I2S_MAX_DATA_WIDTH];
const int wr_sig; const int wr_sig;
} buses[SOC_LCD_I80_BUSES]; } buses[SOC_LCD_I80_BUSES];
} lcd_signal_conn_t; } lcd_i2s_signal_conn_t;
extern const lcd_signal_conn_t lcd_periph_signals; extern const lcd_i2s_signal_conn_t lcd_periph_i2s_signals;
#endif // SOC_I2S_LCD_I80_VARIANT #endif // SOC_I2S_SUPPORTS_LCD_CAMERA
#ifdef __cplusplus #ifdef __cplusplus
} }