From 2f1393ad534a74850d82b865b42e1b0ebdffd97f Mon Sep 17 00:00:00 2001 From: morris Date: Thu, 7 Apr 2022 15:31:49 +0800 Subject: [PATCH 1/2] gdma: add spin lock for gdma channel ... because we allow several control functions to be runable under ISR context --- components/driver/gdma.c | 18 ++++++++++++++---- components/driver/include/esp_private/gdma.h | 12 ++++++++++++ 2 files changed, 26 insertions(+), 4 deletions(-) diff --git a/components/driver/gdma.c b/components/driver/gdma.c index 974bb0c48c..003e8209b3 100644 --- a/components/driver/gdma.c +++ b/components/driver/gdma.c @@ -87,6 +87,7 @@ struct gdma_pair_t { struct gdma_channel_t { gdma_pair_t *pair; // which pair the channel belongs to intr_handle_t intr; // per-channel interrupt handle + portMUX_TYPE spinlock; // channel level spinlock gdma_channel_direction_t direction; // channel direction int periph_id; // Peripheral instance ID, indicates which peripheral is connected to this GDMA channel size_t sram_alignment; // alignment for memory in SRAM @@ -200,6 +201,7 @@ search_done: *ret_chan = &alloc_rx_channel->base; // return the installed channel } + (*ret_chan)->spinlock = (portMUX_TYPE)portMUX_INITIALIZER_UNLOCKED; ESP_LOGD(TAG, "new %s channel (%d,%d) at %p", (config->direction == GDMA_CHANNEL_DIRECTION_TX) ? "tx" : "rx", group->group_id, pair->pair_id, *ret_chan); return ESP_OK; @@ -451,10 +453,11 @@ esp_err_t gdma_start(gdma_channel_handle_t dma_chan, intptr_t desc_base_addr) esp_err_t ret = ESP_OK; gdma_pair_t *pair = NULL; gdma_group_t *group = NULL; - ESP_GOTO_ON_FALSE(dma_chan, ESP_ERR_INVALID_ARG, err, TAG, "invalid argument"); + ESP_GOTO_ON_FALSE_ISR(dma_chan, ESP_ERR_INVALID_ARG, err, TAG, "invalid argument"); pair = dma_chan->pair; group = pair->group; + portENTER_CRITICAL_SAFE(&dma_chan->spinlock); if (dma_chan->direction == GDMA_CHANNEL_DIRECTION_RX) { gdma_ll_rx_set_desc_addr(group->hal.dev, pair->pair_id, desc_base_addr); gdma_ll_rx_start(group->hal.dev, pair->pair_id); @@ -462,6 +465,7 @@ esp_err_t gdma_start(gdma_channel_handle_t dma_chan, intptr_t desc_base_addr) gdma_ll_tx_set_desc_addr(group->hal.dev, pair->pair_id, desc_base_addr); gdma_ll_tx_start(group->hal.dev, pair->pair_id); } + portEXIT_CRITICAL_SAFE(&dma_chan->spinlock); err: return ret; @@ -472,15 +476,17 @@ esp_err_t gdma_stop(gdma_channel_handle_t dma_chan) esp_err_t ret = ESP_OK; gdma_pair_t *pair = NULL; gdma_group_t *group = NULL; - ESP_GOTO_ON_FALSE(dma_chan, ESP_ERR_INVALID_ARG, err, TAG, "invalid argument"); + ESP_GOTO_ON_FALSE_ISR(dma_chan, ESP_ERR_INVALID_ARG, err, TAG, "invalid argument"); pair = dma_chan->pair; group = pair->group; + portENTER_CRITICAL_SAFE(&dma_chan->spinlock); if (dma_chan->direction == GDMA_CHANNEL_DIRECTION_RX) { gdma_ll_rx_stop(group->hal.dev, pair->pair_id); } else { gdma_ll_tx_stop(group->hal.dev, pair->pair_id); } + portEXIT_CRITICAL_SAFE(&dma_chan->spinlock); err: return ret; @@ -491,15 +497,17 @@ esp_err_t gdma_append(gdma_channel_handle_t dma_chan) esp_err_t ret = ESP_OK; gdma_pair_t *pair = NULL; gdma_group_t *group = NULL; - ESP_GOTO_ON_FALSE(dma_chan, ESP_ERR_INVALID_ARG, err, TAG, "invalid argument"); + ESP_GOTO_ON_FALSE_ISR(dma_chan, ESP_ERR_INVALID_ARG, err, TAG, "invalid argument"); pair = dma_chan->pair; group = pair->group; + portENTER_CRITICAL_SAFE(&dma_chan->spinlock); if (dma_chan->direction == GDMA_CHANNEL_DIRECTION_RX) { gdma_ll_rx_restart(group->hal.dev, pair->pair_id); } else { gdma_ll_tx_restart(group->hal.dev, pair->pair_id); } + portEXIT_CRITICAL_SAFE(&dma_chan->spinlock); err: return ret; @@ -510,15 +518,17 @@ esp_err_t gdma_reset(gdma_channel_handle_t dma_chan) esp_err_t ret = ESP_OK; gdma_pair_t *pair = NULL; gdma_group_t *group = NULL; - ESP_GOTO_ON_FALSE(dma_chan, ESP_ERR_INVALID_ARG, err, TAG, "invalid argument"); + ESP_GOTO_ON_FALSE_ISR(dma_chan, ESP_ERR_INVALID_ARG, err, TAG, "invalid argument"); pair = dma_chan->pair; group = pair->group; + portENTER_CRITICAL_SAFE(&dma_chan->spinlock); if (dma_chan->direction == GDMA_CHANNEL_DIRECTION_RX) { gdma_ll_rx_reset_channel(group->hal.dev, pair->pair_id); } else { gdma_ll_tx_reset_channel(group->hal.dev, pair->pair_id); } + portEXIT_CRITICAL_SAFE(&dma_chan->spinlock); err: return ret; diff --git a/components/driver/include/esp_private/gdma.h b/components/driver/include/esp_private/gdma.h index 88a45b56cf..f888cd5bc9 100644 --- a/components/driver/include/esp_private/gdma.h +++ b/components/driver/include/esp_private/gdma.h @@ -269,6 +269,9 @@ esp_err_t gdma_register_rx_event_callbacks(gdma_channel_handle_t dma_chan, gdma_ /** * @brief Set DMA descriptor address and start engine * + * @note This function is allowed to run within ISR context + * @note This function is also allowed to run when Cache is disabled, if `CONFIG_GDMA_CTRL_FUNC_IN_IRAM` is enabled + * * @param[in] dma_chan GDMA channel handle, allocated by `gdma_new_channel` * @param[in] desc_base_addr Base address of descriptors (usually the descriptors are chained into a link or ring) * @return @@ -281,6 +284,9 @@ esp_err_t gdma_start(gdma_channel_handle_t dma_chan, intptr_t desc_base_addr); /** * @brief Stop DMA engine * + * @note This function is allowed to run within ISR context + * @note This function is also allowed to run when Cache is disabled, if `CONFIG_GDMA_CTRL_FUNC_IN_IRAM` is enabled + * * @param[in] dma_chan GDMA channel handle, allocated by `gdma_new_channel` * @return * - ESP_OK: Stop DMA engine successfully @@ -291,6 +297,9 @@ esp_err_t gdma_stop(gdma_channel_handle_t dma_chan); /** * @brief Make the appended descriptors be aware to the DMA engine + * + * @note This function is allowed to run within ISR context + * @note This function is also allowed to run when Cache is disabled, if `CONFIG_GDMA_CTRL_FUNC_IN_IRAM` is enabled * @note This API could also resume a paused DMA engine, make sure new descriptors have been appended to the descriptor chain before calling it. * * @param[in] dma_chan GDMA channel handle, allocated by `gdma_new_channel` @@ -303,6 +312,9 @@ esp_err_t gdma_append(gdma_channel_handle_t dma_chan); /** * @brief Reset DMA channel FIFO and internal finite state machine + * + * @note This function is allowed to run within ISR context + * @note This function is also allowed to run when Cache is disabled, if `CONFIG_GDMA_CTRL_FUNC_IN_IRAM` is enabled * @note Resetting a DMA channel won't break the connection with the target peripheral * * @param[in] dma_chan GDMA channel handle, allocated by `gdma_new_channel` From 5e49369a2c696531b223c23cf48a5e72115fc5be Mon Sep 17 00:00:00 2001 From: morris Date: Mon, 18 Apr 2022 18:30:47 +0800 Subject: [PATCH 2/2] lcd: spi lcd support transmit lsb first Closes https://github.com/espressif/esp-idf/issues/8790 --- components/esp_lcd/include/esp_lcd_panel_io.h | 3 ++- components/esp_lcd/src/esp_lcd_panel_io_spi.c | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/components/esp_lcd/include/esp_lcd_panel_io.h b/components/esp_lcd/include/esp_lcd_panel_io.h index 75daae34df..c35cb18cfb 100644 --- a/components/esp_lcd/include/esp_lcd_panel_io.h +++ b/components/esp_lcd/include/esp_lcd_panel_io.h @@ -98,7 +98,8 @@ typedef struct { unsigned int dc_as_cmd_phase: 1; /*!< D/C line value is encoded into SPI transaction command phase */ unsigned int dc_low_on_data: 1; /*!< If this flag is enabled, DC line = 0 means transfer data, DC line = 1 means transfer command; vice versa */ unsigned int octal_mode: 1; /*!< transmit with octal mode (8 data lines), this mode is used to simulate Intel 8080 timing */ - } flags; + unsigned int lsb_first: 1; /*!< transmit LSB bit first */ + } flags; /*!< Extra flags to fine-tune the SPI device */ } esp_lcd_panel_io_spi_config_t; /** diff --git a/components/esp_lcd/src/esp_lcd_panel_io_spi.c b/components/esp_lcd/src/esp_lcd_panel_io_spi.c index 64f8db0730..71a2b6b86b 100644 --- a/components/esp_lcd/src/esp_lcd_panel_io_spi.c +++ b/components/esp_lcd/src/esp_lcd_panel_io_spi.c @@ -65,7 +65,8 @@ esp_err_t esp_lcd_new_panel_io_spi(esp_lcd_spi_bus_handle_t bus, const esp_lcd_p ESP_GOTO_ON_FALSE(spi_panel_io, ESP_ERR_NO_MEM, err, TAG, "no mem for spi panel io"); spi_device_interface_config_t devcfg = { - .flags = SPI_DEVICE_HALFDUPLEX, // only use TX path, so half duplex is enough + // currently the driver only supports TX path, so half duplex is enough + .flags = SPI_DEVICE_HALFDUPLEX | (io_config->flags.lsb_first ? SPI_DEVICE_TXBIT_LSBFIRST : 0), .clock_speed_hz = io_config->pclk_hz, .mode = io_config->spi_mode, .spics_io_num = io_config->cs_gpio_num,