#include "codec_board.h" #include "esp_lcd_panel_io.h" #include "esp_lcd_panel_ops.h" #include "tca9554.h" #include "esp_log.h" #include "driver/gpio.h" #include "driver/spi_common.h" #include "esp_idf_version.h" #if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 3, 0) #include "esp_lcd_panel_dev.h" #include "esp_lcd_panel_st7789.h" #else #include "esp_lcd_panel_vendor.h" #endif #if CONFIG_IDF_TARGET_ESP32P4 #include "esp_lcd_panel_ops.h" #include "esp_lcd_mipi_dsi.h" #include "esp_lcd_ili9881c.h" #include "esp_ldo_regulator.h" #include "soc/mipi_dsi_bridge_struct.h" #include "esp_lcd_ek79007.h" #endif #include "freertos/FreeRTOS.h" #define TAG "LCD_INIT" #define RETURN_ON_ERR(ret) if (ret != 0) { \ ESP_LOGE(TAG, "Fail to run on %d ret %d", __LINE__, ret); \ return ret; \ } typedef struct { int (*init)(lcd_cfg_t *cfg); int (*set_dir)(int16_t gpio, bool output); int (*set_gpio)(int16_t gpio, bool high); } extend_io_ops_t; static extend_io_ops_t extend_io_ops; static esp_lcd_panel_handle_t panel_handle = NULL; static int tca9554_io_init(lcd_cfg_t *cfg) { return tca9554_init(cfg->io_i2c_port); } static int tca9554_io_set_dir(int16_t gpio, bool output) { gpio = (1 << gpio); tca9554_set_io_config(gpio, output ? TCA9554_IO_OUTPUT : TCA9554_IO_INPUT); return 0; } static int tca9554_io_set(int16_t gpio, bool high) { gpio = (1 << gpio); return tca9554_set_output_state(gpio, high ? TCA9554_IO_HIGH : TCA9554_IO_LOW); } static void register_tca9554(void) { extend_io_ops.init = tca9554_io_init; extend_io_ops.set_dir = tca9554_io_set_dir; extend_io_ops.set_gpio = tca9554_io_set; } static int init_extend_io(lcd_cfg_t *cfg) { if (cfg->io_type == EXTENT_IO_TYPE_NONE) { return 0; } switch (cfg->io_type) { case EXTENT_IO_TYPE_TCA9554: register_tca9554(); break; default: return -1; } return extend_io_ops.init(cfg); } static int set_pin_dir(int16_t pin, bool output) { if (pin & BOARD_EXTEND_IO_START) { pin &= ~BOARD_EXTEND_IO_START; extend_io_ops.set_dir(pin, output); } else { gpio_config_t bk_gpio_config = { .mode = output ? GPIO_MODE_OUTPUT : GPIO_MODE_INPUT, .pin_bit_mask = pin > 0 ? 1ULL << pin : 0ULL, }; gpio_config(&bk_gpio_config); } return 0; } static int set_pin_state(int16_t pin, bool high) { if (pin & BOARD_EXTEND_IO_START) { extend_io_ops.set_gpio(pin, high); } else { gpio_set_level(pin, true); } return 0; } static int16_t get_hw_gpio(int16_t pin) { if (pin == -1) { return pin; } if (pin & BOARD_EXTEND_IO_START) { return -1; } return pin; } static void sleep_ms(int ms) { vTaskDelay(ms / portTICK_PERIOD_MS); } static int _lcd_rest(lcd_cfg_t *cfg) { if (cfg->reset_pin >= 0) { set_pin_state(cfg->reset_pin, false); sleep_ms(100); set_pin_state(cfg->reset_pin, true); } return 0; } static int _init_spi_lcd(lcd_cfg_t *cfg) { int ret = 0; if (cfg->spi_cfg.cs & BOARD_EXTEND_IO_START) { set_pin_dir(cfg->spi_cfg.cs, true); sleep_ms(10); set_pin_dir(cfg->spi_cfg.cs, false); sleep_ms(10); } spi_bus_config_t buscfg = { .sclk_io_num = cfg->spi_cfg.clk, .mosi_io_num = cfg->spi_cfg.mosi, .miso_io_num = -1, .quadwp_io_num = -1, .quadhd_io_num = -1, .max_transfer_sz = cfg->width * cfg->height * 2, }; #if SOC_SPI_SUPPORT_OCT if (cfg->spi_cfg.d[6] >= 0) { buscfg.data1_io_num = cfg->spi_cfg.d[0]; buscfg.data2_io_num = cfg->spi_cfg.d[1]; ; buscfg.data3_io_num = cfg->spi_cfg.d[2]; ; buscfg.data4_io_num = cfg->spi_cfg.d[3]; ; buscfg.data5_io_num = cfg->spi_cfg.d[4]; ; buscfg.data6_io_num = cfg->spi_cfg.d[5]; ; buscfg.data7_io_num = cfg->spi_cfg.d[6]; ; buscfg.flags = SPICOMMON_BUSFLAG_OCTAL; } #endif int bus_id = SPI1_HOST + (cfg->spi_cfg.spi_bus - 1); ret = spi_bus_initialize(bus_id, &buscfg, SPI_DMA_CH_AUTO); ESP_LOGI(TAG, "CLK %d MOSI %d CS:%d DC: %d Bus:%d", cfg->spi_cfg.clk, cfg->spi_cfg.mosi, get_hw_gpio(cfg->spi_cfg.cs), cfg->spi_cfg.dc, bus_id); RETURN_ON_ERR(ret); esp_lcd_panel_io_spi_config_t io_config = { .dc_gpio_num = cfg->spi_cfg.dc, .cs_gpio_num = get_hw_gpio(cfg->spi_cfg.cs), .pclk_hz = cfg->spi_cfg.pclk_clk ? cfg->spi_cfg.pclk_clk : 60 * 1000 * 1000, .spi_mode = 0, .trans_queue_depth = 10, .lcd_cmd_bits = cfg->spi_cfg.cmd_bits ? cfg->spi_cfg.cmd_bits : 8, .lcd_param_bits = cfg->spi_cfg.param_bits ? cfg->spi_cfg.param_bits : 8, .on_color_trans_done = NULL, .user_ctx = NULL, }; #if SOC_SPI_SUPPORT_OCT if (cfg->spi_cfg.d[6] >= 0) { io_config.flags.octal_mode = 1; io_config.spi_mode = 3; } #endif esp_lcd_panel_io_handle_t io_handle; ret = esp_lcd_new_panel_io_spi((esp_lcd_spi_bus_handle_t)bus_id, &io_config, &io_handle); RETURN_ON_ERR(ret); esp_lcd_panel_dev_config_t panel_config = { .reset_gpio_num = get_hw_gpio(cfg->reset_pin), #if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 3, 0) .rgb_ele_order = ESP_LCD_COLOR_SPACE_BGR, #else .rgb_endian = LCD_RGB_ENDIAN_BGR, #endif .bits_per_pixel = 16, }; switch (cfg->controller) { default: return -1; case LCD_CONTROLLER_TYPE_ST7789: ret = esp_lcd_new_panel_st7789(io_handle, &panel_config, &panel_handle); RETURN_ON_ERR(ret); ESP_LOGI(TAG, "Init driver ST7789 finished"); break; } return ret; } #if CONFIG_IDF_TARGET_ESP32P4 static int power_on_dsi(lcd_mipi_cfg_t *mipi_cfg) { esp_ldo_channel_handle_t ldo_mipi_phy = NULL; esp_ldo_channel_config_t ldo_mipi_phy_config = { .chan_id = mipi_cfg->ldo_chan, .voltage_mv = mipi_cfg->ldo_voltage, }; return esp_ldo_acquire_channel(&ldo_mipi_phy_config, &ldo_mipi_phy); } static int _init_mipi_lcd(lcd_cfg_t *cfg) { int ret = 0; lcd_mipi_cfg_t *mipi_cfg = &cfg->mipi_cfg; power_on_dsi(mipi_cfg); // create MIPI DSI bus first, it will initialize the DSI PHY as well esp_lcd_dsi_bus_handle_t mipi_dsi_bus; esp_lcd_dsi_bus_config_t bus_config = { .bus_id = 0, .num_data_lanes = mipi_cfg->lane_num, .phy_clk_src = MIPI_DSI_PHY_CLK_SRC_DEFAULT, .lane_bit_rate_mbps = mipi_cfg->lane_bitrate, }; ret = esp_lcd_new_dsi_bus(&bus_config, &mipi_dsi_bus); RETURN_ON_ERR(ret); ESP_LOGI(TAG, "Install MIPI DSI LCD control panel"); esp_lcd_panel_io_handle_t mipi_dbi_io; // we use DBI interface to send LCD commands and parameters esp_lcd_dbi_io_config_t dbi_config = { .virtual_channel = 0, .lcd_cmd_bits = 8, .lcd_param_bits = 8, }; ret = esp_lcd_new_panel_io_dbi(mipi_dsi_bus, &dbi_config, &mipi_dbi_io); RETURN_ON_ERR(ret); esp_lcd_dpi_panel_config_t dpi_config = { .num_fbs = mipi_cfg->fb_num, .virtual_channel = 0, .dpi_clk_src = MIPI_DSI_DPI_CLK_SRC_DEFAULT, .dpi_clock_freq_mhz = mipi_cfg->dpi_clk, .pixel_format = mipi_cfg->bit_depth == 24 ? LCD_COLOR_PIXEL_FORMAT_RGB888 : LCD_COLOR_PIXEL_FORMAT_RGB565, .video_timing = { .h_size = cfg->width, .v_size = cfg->height, .hsync_back_porch = mipi_cfg->dsi_hbp, .hsync_pulse_width = mipi_cfg->dsi_hsync, .hsync_front_porch = mipi_cfg->dsi_hfp, .vsync_back_porch = mipi_cfg->dsi_vbp, .vsync_pulse_width = mipi_cfg->dsi_vsync, .vsync_front_porch = mipi_cfg->dsi_vfp, }, .flags.use_dma2d = true, }; esp_lcd_panel_dev_config_t panel_config = { .bits_per_pixel = mipi_cfg->bit_depth, .rgb_ele_order = LCD_RGB_ELEMENT_ORDER_RGB, .reset_gpio_num = cfg->reset_pin, }; if (cfg->width == 1024 && cfg->height == 600) { ESP_LOGI(TAG, "Install EK79007 LCD control panel"); esp_lcd_dpi_panel_config_t dpi_config = EK79007_1024_600_PANEL_60HZ_CONFIG(LCD_COLOR_PIXEL_FORMAT_RGB565); ek79007_vendor_config_t vendor_config = { .mipi_config = { .dsi_bus = mipi_dsi_bus, .dpi_config = &dpi_config, }, }; panel_config.vendor_config = &vendor_config; ret = esp_lcd_new_panel_ek79007(mipi_dbi_io, &panel_config, &panel_handle); RETURN_ON_ERR(ret); } else { ili9881c_vendor_config_t vendor_config = { .mipi_config = { .dsi_bus = mipi_dsi_bus, .dpi_config = &dpi_config, .lane_num = mipi_cfg->lane_num, }, }; panel_config.vendor_config = &vendor_config; ret = esp_lcd_new_panel_ili9881c(mipi_dbi_io, &panel_config, &panel_handle); } RETURN_ON_ERR(ret); esp_lcd_panel_reset(panel_handle); ESP_LOGI(TAG, "Install MIPI DSI LCD data panel"); return ret; } #else static int _init_mipi_lcd(lcd_cfg_t *cfg) { return -1; } #endif static int _init_lcd(lcd_cfg_t *cfg) { int ret = 0; if (cfg->io_type != EXTENT_IO_TYPE_NONE) { ret = init_extend_io(cfg); if (ret != 0) { return ret; } } // Config reset and ctrl gpio dir if (cfg->reset_pin >= 0) { set_pin_dir(cfg->reset_pin, true); } if (cfg->ctrl_pin >= 0) { set_pin_dir(cfg->ctrl_pin, true); } if (cfg->bus_type == LCD_BUS_TYPE_SPI) { if (cfg->spi_cfg.cs >= 0) { set_pin_dir(cfg->spi_cfg.cs, true); } } _lcd_rest(cfg); if (cfg->ctrl_pin >= 0) { set_pin_dir(cfg->ctrl_pin, true); } if (cfg->bus_type == LCD_BUS_TYPE_SPI) { ret = _init_spi_lcd(cfg); } else if (cfg->bus_type == LCD_BUS_TYPE_MIPI) { ret = _init_mipi_lcd(cfg); } if (panel_handle) { ret = esp_lcd_panel_init(panel_handle); RETURN_ON_ERR(ret); if (cfg->color_inv) { ret = esp_lcd_panel_invert_color(panel_handle, cfg->color_inv); } // ret = esp_lcd_panel_set_gap(panel_handle, 0, 0); if (cfg->swap_xy) { ret = esp_lcd_panel_swap_xy(panel_handle, cfg->swap_xy); } if (cfg->mirror_x || cfg->mirror_y) { ret = esp_lcd_panel_mirror(panel_handle, cfg->mirror_x, cfg->mirror_y); } ret = esp_lcd_panel_disp_on_off(panel_handle, true); } return ret; } int board_lcd_init(void) { lcd_cfg_t cfg = { 0 }; int ret = get_lcd_cfg(&cfg); if (ret != 0) { return ret; } return _init_lcd(&cfg); } void *board_get_lcd_handle(void) { if (panel_handle) { return panel_handle; } return NULL; }