From 7dc9c7aa0514c6bc0e95fe204985c5202ea46668 Mon Sep 17 00:00:00 2001 From: morris Date: Tue, 24 Aug 2021 20:37:58 +0800 Subject: [PATCH] lcd: allow execlusive use of i80 bus --- .gitlab/ci/target-test.yml | 2 +- components/esp_lcd/include/esp_lcd_panel_io.h | 2 +- components/esp_lcd/src/esp_lcd_panel_io_i2s.c | 29 +++++++++++---- components/esp_lcd/src/esp_lcd_panel_io_i80.c | 31 ++++++++++++---- components/esp_lcd/test/test_i80_lcd_panel.c | 35 +++++++++++++++++++ 5 files changed, 84 insertions(+), 15 deletions(-) diff --git a/.gitlab/ci/target-test.yml b/.gitlab/ci/target-test.yml index 14301c1d87..9cbf83488e 100644 --- a/.gitlab/ci/target-test.yml +++ b/.gitlab/ci/target-test.yml @@ -633,7 +633,7 @@ UT_046: UT_047: extends: .unit_test_esp32s2_template - parallel: 4 + parallel: 5 tags: - ESP32S2_IDF - UT_T1_1 diff --git a/components/esp_lcd/include/esp_lcd_panel_io.h b/components/esp_lcd/include/esp_lcd_panel_io.h index bdeb94c499..eebcabf42b 100644 --- a/components/esp_lcd/include/esp_lcd_panel_io.h +++ b/components/esp_lcd/include/esp_lcd_panel_io.h @@ -165,7 +165,7 @@ esp_err_t esp_lcd_del_i80_bus(esp_lcd_i80_bus_handle_t bus); * @brief Panel IO configuration structure, for intel 8080 interface */ typedef struct { - int cs_gpio_num; /*!< GPIO used for CS line */ + int cs_gpio_num; /*!< GPIO used for CS line, set to -1 will declaim exclusively use of I80 bus */ unsigned int pclk_hz; /*!< Frequency of pixel clock */ size_t trans_queue_depth; /*!< Transaction queue size, larger queue, higher throughput */ bool (*on_color_trans_done)(esp_lcd_panel_io_handle_t panel_io, void *user_data, void *event_data); /*!< Callback, invoked when color data was tranferred done */ 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 cca9f49025..cea58f71fa 100644 --- a/components/esp_lcd/src/esp_lcd_panel_io_i2s.c +++ b/components/esp_lcd/src/esp_lcd_panel_io_i2s.c @@ -71,6 +71,9 @@ struct esp_lcd_i80_bus_t { lcd_i80_trans_descriptor_t *cur_trans; // Current transaction 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 + struct { + unsigned int exclusive: 1; // Indicate whether the I80 bus is owned by one device (whose CS GPIO is not assigned) exclusively + } flags; dma_descriptor_t dma_nodes[]; // DMA descriptor pool, the descriptors are shared by all i80 devices }; @@ -233,7 +236,17 @@ esp_err_t esp_lcd_new_panel_io_i80(esp_lcd_i80_bus_handle_t bus, const esp_lcd_p { esp_err_t ret = ESP_OK; lcd_panel_io_i80_t *i80_device = NULL; + bool bus_exclusive = false; ESP_GOTO_ON_FALSE(bus && io_config && ret_io, ESP_ERR_INVALID_ARG, err, TAG, "invalid argument"); + // check if the bus has been configured as exclusive + portENTER_CRITICAL(&bus->spinlock); + if (!bus->flags.exclusive) { + bus->flags.exclusive = io_config->cs_gpio_num < 0; + } else { + bus_exclusive = true; + } + portEXIT_CRITICAL(&bus->spinlock); + ESP_GOTO_ON_FALSE(!bus_exclusive, ESP_ERR_INVALID_STATE, err, TAG, "bus has been exclusively owned by device"); // because we set the I2S's left channel data same to right channel, so f_pclk = f_i2s/pclk_div/2 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, @@ -269,10 +282,12 @@ esp_err_t esp_lcd_new_panel_io_i80(esp_lcd_i80_bus_handle_t bus, const esp_lcd_p i80_device->base.del = panel_io_i80_del; i80_device->base.tx_param = panel_io_i80_tx_param; i80_device->base.tx_color = panel_io_i80_tx_color; - // CS signal is controlled by software - gpio_set_level(io_config->cs_gpio_num, !io_config->flags.cs_active_high); // de-assert by default - 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); + if (io_config->cs_gpio_num >= 0) { + // CS signal is controlled by software + gpio_set_level(io_config->cs_gpio_num, !io_config->flags.cs_active_high); // de-assert by default + 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); + } *ret_io = &(i80_device->base); ESP_LOGD(TAG, "new i80 lcd panel io @%p on bus(%d), pclk=%uHz", i80_device, bus->bus_id, i80_device->pclk_hz); return ESP_OK; @@ -646,10 +661,12 @@ static void lcd_i80_switch_devices(lcd_panel_io_i80_t *cur_device, lcd_panel_io_ if (next_device != cur_device) { // reconfigure PCLK for the new device i2s_ll_tx_set_bck_div_num(bus->hal.dev, next_device->clock_prescale); - if (cur_device) { // de-assert current device + if (cur_device && cur_device->cs_gpio_num >= 0) { // de-assert current device gpio_set_level(cur_device->cs_gpio_num, !cur_device->flags.cs_active_high); } - gpio_set_level(next_device->cs_gpio_num, next_device->flags.cs_active_high); // select the next device + if (next_device->cs_gpio_num >= 0) { + gpio_set_level(next_device->cs_gpio_num, next_device->flags.cs_active_high); // select the next device + } // 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 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); 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 bcf2804cbd..58138fa768 100644 --- a/components/esp_lcd/src/esp_lcd_panel_io_i80.c +++ b/components/esp_lcd/src/esp_lcd_panel_io_i80.c @@ -65,6 +65,9 @@ struct esp_lcd_i80_bus_t { lcd_i80_trans_descriptor_t *cur_trans; // Current transaction 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 + struct { + unsigned int exclusive: 1; // Indicate whether the I80 bus is owned by one device (whose CS GPIO is not assigned) exclusively + } flags; dma_descriptor_t dma_nodes[]; // DMA descriptor pool, the descriptors are shared by all i80 devices }; @@ -219,7 +222,17 @@ esp_err_t esp_lcd_new_panel_io_i80(esp_lcd_i80_bus_handle_t bus, const esp_lcd_p { esp_err_t ret = ESP_OK; lcd_panel_io_i80_t *i80_device = NULL; + bool bus_exclusive = false; ESP_GOTO_ON_FALSE(bus && io_config && ret_io, ESP_ERR_INVALID_ARG, err, TAG, "invalid argument"); + // check if the bus has been configured as exclusive + portENTER_CRITICAL(&bus->spinlock); + if (!bus->flags.exclusive) { + bus->flags.exclusive = io_config->cs_gpio_num < 0; + } else { + bus_exclusive = true; + } + portEXIT_CRITICAL(&bus->spinlock); + ESP_GOTO_ON_FALSE(!bus_exclusive, ESP_ERR_INVALID_STATE, err, TAG, "bus has been exclusively owned by device"); // check if pixel clock setting is valid 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, @@ -261,9 +274,11 @@ esp_err_t esp_lcd_new_panel_io_i80(esp_lcd_i80_bus_handle_t bus, const esp_lcd_p i80_device->base.tx_color = panel_io_i80_tx_color; // we only configure the CS GPIO as output, don't connect to the peripheral signal at the moment // we will connect the CS GPIO to peripheral signal when switching devices in lcd_i80_switch_devices() - 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_hal_iomux_func_sel(GPIO_PIN_MUX_REG[io_config->cs_gpio_num], PIN_FUNC_GPIO); + if (io_config->cs_gpio_num >= 0) { + 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_hal_iomux_func_sel(GPIO_PIN_MUX_REG[io_config->cs_gpio_num], PIN_FUNC_GPIO); + } *ret_io = &(i80_device->base); ESP_LOGD(TAG, "new i80 lcd panel io @%p on bus(%d)", i80_device, bus->bus_id); return ESP_OK; @@ -541,13 +556,15 @@ static void lcd_i80_switch_devices(lcd_panel_io_i80_t *cur_device, lcd_panel_io_ // configure DC line level for the new device lcd_ll_set_dc_level(bus->hal.dev, next_device->dc_levels.dc_idle_level, next_device->dc_levels.dc_cmd_level, next_device->dc_levels.dc_dummy_level, next_device->dc_levels.dc_data_level); - if (cur_device) { + if (cur_device && cur_device->cs_gpio_num >= 0) { // disconnect current CS GPIO from peripheral signal esp_rom_gpio_connect_out_signal(cur_device->cs_gpio_num, SIG_GPIO_OUT_IDX, false, false); } - // 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, - next_device->flags.cs_active_high, false); + if (next_device->cs_gpio_num >= 0) { + // 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, + next_device->flags.cs_active_high, false); + } } } diff --git a/components/esp_lcd/test/test_i80_lcd_panel.c b/components/esp_lcd/test/test_i80_lcd_panel.c index fc07add0c2..4135e4de65 100644 --- a/components/esp_lcd/test/test_i80_lcd_panel.c +++ b/components/esp_lcd/test/test_i80_lcd_panel.c @@ -176,6 +176,41 @@ TEST_CASE("lcd i80 bus and device allocation", "[lcd]") } } +TEST_CASE("lcd i80 bus exclusively owned by one device", "[lcd]") +{ + esp_lcd_i80_bus_handle_t i80_bus_handle = NULL; + esp_lcd_i80_bus_config_t bus_config = { + .dc_gpio_num = TEST_LCD_DC_GPIO, + .wr_gpio_num = TEST_LCD_PCLK_GPIO, + .data_gpio_nums = { + TEST_LCD_DATA0_GPIO, + TEST_LCD_DATA1_GPIO, + TEST_LCD_DATA2_GPIO, + TEST_LCD_DATA3_GPIO, + TEST_LCD_DATA4_GPIO, + TEST_LCD_DATA5_GPIO, + TEST_LCD_DATA6_GPIO, + TEST_LCD_DATA7_GPIO, + }, + .bus_width = 8, + .max_transfer_bytes = TEST_LCD_H_RES * 40 * sizeof(uint16_t) + }; + TEST_ESP_OK(esp_lcd_new_i80_bus(&bus_config, &i80_bus_handle)); + esp_lcd_panel_io_handle_t io_handle = NULL; + esp_lcd_panel_io_i80_config_t io_config = { + .cs_gpio_num = -1, // own the bus exclusively + .pclk_hz = 5000000, + .trans_queue_depth = 4, + .lcd_cmd_bits = 8, + .lcd_param_bits = 8, + }; + TEST_ESP_OK(esp_lcd_new_panel_io_i80(i80_bus_handle, &io_config, &io_handle)); + io_config.cs_gpio_num = 0; + TEST_ASSERT_EQUAL(ESP_ERR_INVALID_STATE, esp_lcd_new_panel_io_i80(i80_bus_handle, &io_config, &io_handle)); + TEST_ESP_OK(esp_lcd_panel_io_del(io_handle)); + TEST_ESP_OK(esp_lcd_del_i80_bus(i80_bus_handle)); +} + TEST_CASE("lcd panel i80 io test", "[lcd]") { esp_lcd_i80_bus_handle_t i80_bus = NULL;