diff --git a/components/driver/i2s/i2s_common.c b/components/driver/i2s/i2s_common.c index 86cc8993fe..28335b42ee 100644 --- a/components/driver/i2s/i2s_common.c +++ b/components/driver/i2s/i2s_common.c @@ -898,6 +898,7 @@ esp_err_t i2s_del_channel(i2s_chan_handle_t handle) #endif if (handle->dma.dma_chan) { #if SOC_GDMA_SUPPORTED + gdma_disconnect(handle->dma.dma_chan); gdma_del_channel(handle->dma.dma_chan); #else esp_intr_free(handle->dma.dma_chan); diff --git a/components/esp_hw_support/gdma.c b/components/esp_hw_support/gdma.c index 6ffa28fc8b..d8a2d86349 100644 --- a/components/esp_hw_support/gdma.c +++ b/components/esp_hw_support/gdma.c @@ -71,6 +71,8 @@ struct gdma_group_t { int group_id; // Group ID, index from 0 gdma_hal_context_t hal; // HAL instance is at group level portMUX_TYPE spinlock; // group level spinlock + uint32_t tx_periph_in_use_mask; // each bit indicates which peripheral (TX direction) has been occupied + uint32_t rx_periph_in_use_mask; // each bit indicates which peripheral (RX direction) has been occupied gdma_pair_t *pairs[SOC_GDMA_PAIRS_PER_GROUP]; // handles of GDMA pairs int pair_ref_counts[SOC_GDMA_PAIRS_PER_GROUP]; // reference count used to protect pair install/uninstall }; @@ -246,53 +248,97 @@ err: esp_err_t gdma_connect(gdma_channel_handle_t dma_chan, gdma_trigger_t trig_periph) { - 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(dma_chan->periph_id == GDMA_INVALID_PERIPH_TRIG, ESP_ERR_INVALID_STATE, err, TAG, "channel is using by peripheral: %d", dma_chan->periph_id); + ESP_RETURN_ON_FALSE(dma_chan, ESP_ERR_INVALID_ARG, TAG, "invalid argument"); + ESP_RETURN_ON_FALSE(dma_chan->periph_id == GDMA_INVALID_PERIPH_TRIG, ESP_ERR_INVALID_STATE, TAG, "channel is using by peripheral: %d", dma_chan->periph_id); pair = dma_chan->pair; group = pair->group; - - dma_chan->periph_id = trig_periph.instance_id; - // enable/disable m2m mode - gdma_ll_enable_m2m_mode(group->hal.dev, pair->pair_id, trig_periph.periph == GDMA_TRIG_PERIPH_M2M); + bool periph_conflict = false; if (dma_chan->direction == GDMA_CHANNEL_DIRECTION_TX) { - gdma_ll_tx_reset_channel(group->hal.dev, pair->pair_id); // reset channel - if (trig_periph.periph != GDMA_TRIG_PERIPH_M2M) { - gdma_ll_tx_connect_to_periph(group->hal.dev, pair->pair_id, trig_periph.instance_id); + if (trig_periph.instance_id >= 0) { + portENTER_CRITICAL(&group->spinlock); + if (group->tx_periph_in_use_mask & (1 << trig_periph.instance_id)) { + periph_conflict = true; + } else { + group->tx_periph_in_use_mask |= (1 << trig_periph.instance_id); + } + portEXIT_CRITICAL(&group->spinlock); + } + if (!periph_conflict) { + gdma_ll_tx_reset_channel(group->hal.dev, pair->pair_id); // reset channel + gdma_ll_tx_connect_to_periph(group->hal.dev, pair->pair_id, trig_periph.periph, trig_periph.instance_id); } } else { - gdma_ll_rx_reset_channel(group->hal.dev, pair->pair_id); // reset channel - if (trig_periph.periph != GDMA_TRIG_PERIPH_M2M) { - gdma_ll_rx_connect_to_periph(group->hal.dev, pair->pair_id, trig_periph.instance_id); + if (trig_periph.instance_id >= 0) { + portENTER_CRITICAL(&group->spinlock); + if (group->rx_periph_in_use_mask & (1 << trig_periph.instance_id)) { + periph_conflict = true; + } else { + group->rx_periph_in_use_mask |= (1 << trig_periph.instance_id); + } + portEXIT_CRITICAL(&group->spinlock); + } + if (!periph_conflict) { + gdma_ll_rx_reset_channel(group->hal.dev, pair->pair_id); // reset channel + gdma_ll_rx_connect_to_periph(group->hal.dev, pair->pair_id, trig_periph.periph, trig_periph.instance_id); } } -err: - return ret; + ESP_RETURN_ON_FALSE(!periph_conflict, ESP_ERR_INVALID_STATE, TAG, "peripheral %d is already used by another channel", trig_periph.instance_id); + dma_chan->periph_id = trig_periph.instance_id; + return ESP_OK; } esp_err_t gdma_disconnect(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(dma_chan->periph_id != GDMA_INVALID_PERIPH_TRIG, ESP_ERR_INVALID_STATE, err, TAG, "no peripheral is connected to the channel"); + ESP_RETURN_ON_FALSE(dma_chan, ESP_ERR_INVALID_ARG, TAG, "invalid argument"); + ESP_RETURN_ON_FALSE(dma_chan->periph_id != GDMA_INVALID_PERIPH_TRIG, ESP_ERR_INVALID_STATE, TAG, "no peripheral is connected to the channel"); + + pair = dma_chan->pair; + group = pair->group; + int save_periph_id = dma_chan->periph_id; + + if (dma_chan->direction == GDMA_CHANNEL_DIRECTION_TX) { + if (save_periph_id >= 0) { + portENTER_CRITICAL(&group->spinlock); + group->tx_periph_in_use_mask &= ~(1 << save_periph_id); + portEXIT_CRITICAL(&group->spinlock); + } + gdma_ll_tx_disconnect_from_periph(group->hal.dev, pair->pair_id); + } else { + if (save_periph_id >= 0) { + portENTER_CRITICAL(&group->spinlock); + group->rx_periph_in_use_mask &= ~(1 << save_periph_id); + portEXIT_CRITICAL(&group->spinlock); + } + gdma_ll_rx_disconnect_from_periph(group->hal.dev, pair->pair_id); + } + + dma_chan->periph_id = GDMA_INVALID_PERIPH_TRIG; + return ESP_OK; +} + +esp_err_t gdma_get_free_m2m_trig_id_mask(gdma_channel_handle_t dma_chan, uint32_t *mask) +{ + gdma_pair_t *pair = NULL; + gdma_group_t *group = NULL; + ESP_RETURN_ON_FALSE(dma_chan && mask, ESP_ERR_INVALID_ARG, TAG, "invalid argument"); + + uint32_t free_mask = GDMA_LL_M2M_FREE_PERIPH_ID_MASK; pair = dma_chan->pair; group = pair->group; - dma_chan->periph_id = GDMA_INVALID_PERIPH_TRIG; - if (dma_chan->direction == GDMA_CHANNEL_DIRECTION_TX) { - gdma_ll_tx_connect_to_periph(group->hal.dev, pair->pair_id, GDMA_INVALID_PERIPH_TRIG); - } else { - gdma_ll_rx_connect_to_periph(group->hal.dev, pair->pair_id, GDMA_INVALID_PERIPH_TRIG); - } + portENTER_CRITICAL(&group->spinlock); + free_mask &= ~(group->tx_periph_in_use_mask); + free_mask &= ~(group->rx_periph_in_use_mask); + portEXIT_CRITICAL(&group->spinlock); -err: - return ret; + *mask = free_mask; + return ESP_OK; } esp_err_t gdma_set_transfer_ability(gdma_channel_handle_t dma_chan, const gdma_transfer_ability_t *ability) diff --git a/components/esp_hw_support/include/esp_private/gdma.h b/components/esp_hw_support/include/esp_private/gdma.h index f888cd5bc9..eb9cc27ef7 100644 --- a/components/esp_hw_support/include/esp_private/gdma.h +++ b/components/esp_hw_support/include/esp_private/gdma.h @@ -11,6 +11,7 @@ #include #include "soc/gdma_channel.h" +#include "hal/gdma_types.h" #include "esp_err.h" #ifdef __cplusplus @@ -23,34 +24,6 @@ extern "C" { */ typedef struct gdma_channel_t *gdma_channel_handle_t; -/** - * @brief Enumeration of peripherals which have the DMA capability - * @note Some peripheral might not be available on certain chip, please refer to `soc_caps.h` for detail. - * - */ -typedef enum { - GDMA_TRIG_PERIPH_M2M, /*!< GDMA trigger peripheral: M2M */ - GDMA_TRIG_PERIPH_UART, /*!< GDMA trigger peripheral: UART */ - GDMA_TRIG_PERIPH_SPI, /*!< GDMA trigger peripheral: SPI */ - GDMA_TRIG_PERIPH_I2S, /*!< GDMA trigger peripheral: I2S */ - GDMA_TRIG_PERIPH_AES, /*!< GDMA trigger peripheral: AES */ - GDMA_TRIG_PERIPH_SHA, /*!< GDMA trigger peripheral: SHA */ - GDMA_TRIG_PERIPH_ADC, /*!< GDMA trigger peripheral: ADC */ - GDMA_TRIG_PERIPH_DAC, /*!< GDMA trigger peripheral: DAC */ - GDMA_TRIG_PERIPH_LCD, /*!< GDMA trigger peripheral: LCD */ - GDMA_TRIG_PERIPH_CAM, /*!< GDMA trigger peripheral: CAM */ - GDMA_TRIG_PERIPH_RMT, /*!< GDMA trigger peripheral: RMT */ -} gdma_trigger_peripheral_t; - -/** - * @brief Enumeration of GDMA channel direction - * - */ -typedef enum { - GDMA_CHANNEL_DIRECTION_TX, /*!< GDMA channel direction: TX */ - GDMA_CHANNEL_DIRECTION_RX, /*!< GDMA channel direction: RX */ -} gdma_channel_direction_t; - /** * @brief Collection of configuration items that used for allocating GDMA channel * @@ -124,13 +97,13 @@ typedef struct { */ typedef struct { gdma_trigger_peripheral_t periph; /*!< Target peripheral which will trigger DMA operations */ - int instance_id; /*!< Peripheral instance ID. Supported IDs are listed in `soc/gdma_channel.h`, e.g. SOC_GDMA_TRIG_PERIPH_UART0 */ + int instance_id; /*!< Peripheral instance ID. Supported IDs are listed in `soc/gdma_channel.h`, e.g. SOC_GDMA_TRIG_PERIPH_UHCI0 */ } gdma_trigger_t; /** * @brief Helper macro to initialize GDMA trigger * @note value of `peri` must be selected from `gdma_trigger_peripheral_t` enum. - * e.g. GDMA_MAKE_TRIGGER(GDMA_TRIG_PERIPH_UART,0) + * e.g. GDMA_MAKE_TRIGGER(GDMA_TRIG_PERIPH_I2S,0) * */ #define GDMA_MAKE_TRIGGER(peri, id) \ @@ -325,6 +298,22 @@ esp_err_t gdma_append(gdma_channel_handle_t dma_chan); */ esp_err_t gdma_reset(gdma_channel_handle_t dma_chan); +/** + * @brief Get the mask of free M2M trigger IDs + * + * @note On some ESP targets (e.g. ESP32C3/S3), DMA trigger used for memory copy can be any of valid peripheral's trigger ID, + * which can bring conflict if the peripheral is also using the same trigger ID. This function can return the free IDs + * for memory copy, at the runtime. + * + * @param[in] dma_chan GDMA channel handle, allocated by `gdma_new_channel` + * @param[out] mask Returned mask of free M2M trigger IDs + * @return + * - ESP_OK: Get free M2M trigger IDs successfully + * - ESP_ERR_INVALID_ARG: Get free M2M trigger IDs failed because of invalid argument + * - ESP_FAIL: Get free M2M trigger IDs failed because of other error + */ +esp_err_t gdma_get_free_m2m_trig_id_mask(gdma_channel_handle_t dma_chan, uint32_t *mask); + #ifdef __cplusplus } #endif diff --git a/components/esp_hw_support/port/async_memcpy_impl_gdma.c b/components/esp_hw_support/port/async_memcpy_impl_gdma.c index ee56d174a9..a5dea2b2fd 100644 --- a/components/esp_hw_support/port/async_memcpy_impl_gdma.c +++ b/components/esp_hw_support/port/async_memcpy_impl_gdma.c @@ -48,8 +48,13 @@ esp_err_t async_memcpy_impl_init(async_memcpy_impl_t *impl) goto err; } - gdma_connect(impl->rx_channel, GDMA_MAKE_TRIGGER(GDMA_TRIG_PERIPH_M2M, 0)); - gdma_connect(impl->tx_channel, GDMA_MAKE_TRIGGER(GDMA_TRIG_PERIPH_M2M, 0)); + gdma_trigger_t m2m_trigger = GDMA_MAKE_TRIGGER(GDMA_TRIG_PERIPH_M2M, 0); + // get a free DMA trigger ID for memory copy + uint32_t free_m2m_id_mask = 0; + gdma_get_free_m2m_trig_id_mask(impl->tx_channel, &free_m2m_id_mask); + m2m_trigger.instance_id = __builtin_ctz(free_m2m_id_mask); + gdma_connect(impl->rx_channel, m2m_trigger); + gdma_connect(impl->tx_channel, m2m_trigger); gdma_strategy_config_t strategy_config = { .auto_update_desc = true, diff --git a/components/esp_hw_support/test/test_gdma.c b/components/esp_hw_support/test/test_gdma.c index 6dc447ddcb..83ace6a75d 100644 --- a/components/esp_hw_support/test/test_gdma.c +++ b/components/esp_hw_support/test/test_gdma.c @@ -18,31 +18,27 @@ TEST_CASE("GDMA channel allocation", "[gdma]") gdma_tx_event_callbacks_t tx_cbs = {}; gdma_rx_event_callbacks_t rx_cbs = {}; - // install TX channels for different peripherals + // install TX channels for (int i = 0; i < SOC_GDMA_PAIRS_PER_GROUP; i++) { TEST_ESP_OK(gdma_new_channel(&channel_config, &tx_channels[i])); - TEST_ESP_OK(gdma_connect(tx_channels[i], GDMA_MAKE_TRIGGER(GDMA_TRIG_PERIPH_M2M, 0))); TEST_ESP_OK(gdma_register_tx_event_callbacks(tx_channels[i], &tx_cbs, NULL)); }; TEST_ASSERT_EQUAL(ESP_ERR_NOT_FOUND, gdma_new_channel(&channel_config, &tx_channels[0])); // Free interrupts before installing RX interrupts to ensure enough free interrupts for (int i = 0; i < SOC_GDMA_PAIRS_PER_GROUP; i++) { - TEST_ESP_OK(gdma_disconnect(tx_channels[i])); TEST_ESP_OK(gdma_del_channel(tx_channels[i])); } - // install RX channels for different peripherals + // install RX channels channel_config.direction = GDMA_CHANNEL_DIRECTION_RX; for (int i = 0; i < SOC_GDMA_PAIRS_PER_GROUP; i++) { TEST_ESP_OK(gdma_new_channel(&channel_config, &rx_channels[i])); - TEST_ESP_OK(gdma_connect(rx_channels[i], GDMA_MAKE_TRIGGER(GDMA_TRIG_PERIPH_M2M, 0))); TEST_ESP_OK(gdma_register_rx_event_callbacks(rx_channels[i], &rx_cbs, NULL)); } TEST_ASSERT_EQUAL(ESP_ERR_NOT_FOUND, gdma_new_channel(&channel_config, &rx_channels[0])); for (int i = 0; i < SOC_GDMA_PAIRS_PER_GROUP; i++) { - TEST_ESP_OK(gdma_disconnect(rx_channels[i])); TEST_ESP_OK(gdma_del_channel(rx_channels[i])); } @@ -63,7 +59,18 @@ TEST_CASE("GDMA channel allocation", "[gdma]") TEST_ESP_OK(gdma_new_channel(&channel_config, &rx_channels[1])); channel_config.sibling_chan = NULL; TEST_ESP_OK(gdma_new_channel(&channel_config, &rx_channels[0])); + + TEST_ESP_OK(gdma_connect(tx_channels[0], GDMA_MAKE_TRIGGER(GDMA_TRIG_PERIPH_SPI, 2))); + // can't connect multiple channels to the same peripheral + TEST_ESP_ERR(ESP_ERR_INVALID_STATE, gdma_connect(tx_channels[1], GDMA_MAKE_TRIGGER(GDMA_TRIG_PERIPH_SPI, 2))); + TEST_ESP_OK(gdma_connect(tx_channels[1], GDMA_MAKE_TRIGGER(GDMA_TRIG_PERIPH_M2M, 0))); + + TEST_ESP_OK(gdma_connect(rx_channels[0], GDMA_MAKE_TRIGGER(GDMA_TRIG_PERIPH_SPI, 2))); + // but rx and tx can connect to the same peripheral + TEST_ESP_OK(gdma_connect(rx_channels[1], GDMA_MAKE_TRIGGER(GDMA_TRIG_PERIPH_M2M, 0))); for (int i = 0; i < 2; i++) { + TEST_ESP_OK(gdma_disconnect(tx_channels[i])); + TEST_ESP_OK(gdma_disconnect(rx_channels[i])); TEST_ESP_OK(gdma_del_channel(tx_channels[i])); TEST_ESP_OK(gdma_del_channel(rx_channels[i])); } diff --git a/components/hal/esp32c2/include/hal/gdma_ll.h b/components/hal/esp32c2/include/hal/gdma_ll.h index 3db45a5f44..639309f972 100644 --- a/components/hal/esp32c2/include/hal/gdma_ll.h +++ b/components/hal/esp32c2/include/hal/gdma_ll.h @@ -8,6 +8,7 @@ #include /* Required for NULL constant */ #include #include +#include "hal/gdma_types.h" #include "soc/gdma_struct.h" #include "soc/gdma_reg.h" @@ -20,6 +21,10 @@ extern "C" { #define GDMA_LL_RX_EVENT_MASK (0x06A7) #define GDMA_LL_TX_EVENT_MASK (0x1958) +// any "valid" peripheral ID can be used for M2M mode +#define GDMA_LL_M2M_FREE_PERIPH_ID_MASK (0x185) +#define GDMA_LL_INVALID_PERIPH_ID (0x3F) + #define GDMA_LL_EVENT_TX_FIFO_UDF (1<<12) #define GDMA_LL_EVENT_TX_FIFO_OVF (1<<11) #define GDMA_LL_EVENT_RX_FIFO_UDF (1<<10) @@ -35,19 +40,6 @@ extern "C" { #define GDMA_LL_EVENT_RX_DONE (1<<0) ///////////////////////////////////// Common ///////////////////////////////////////// -/** - * @brief Enable DMA channel M2M mode (TX channel n forward data to RX channel n), disabled by default - */ -static inline void gdma_ll_enable_m2m_mode(gdma_dev_t *dev, uint32_t channel, bool enable) -{ - dev->channel[channel].in.in_conf0.mem_trans_en = enable; - if (enable) { - // to enable m2m mode, the tx chan has to be the same to rx chan, and set to a valid value - dev->channel[channel].in.in_peri_sel.sel = 0; - dev->channel[channel].out.out_peri_sel.sel = 0; - } -} - /** * @brief Enable DMA clock gating */ @@ -255,9 +247,19 @@ static inline void gdma_ll_rx_set_priority(gdma_dev_t *dev, uint32_t channel, ui /** * @brief Connect DMA RX channel to a given peripheral */ -static inline void gdma_ll_rx_connect_to_periph(gdma_dev_t *dev, uint32_t channel, int periph_id) +static inline void gdma_ll_rx_connect_to_periph(gdma_dev_t *dev, uint32_t channel, gdma_trigger_peripheral_t periph, int periph_id) { dev->channel[channel].in.in_peri_sel.sel = periph_id; + dev->channel[channel].in.in_conf0.mem_trans_en = (periph == GDMA_TRIG_PERIPH_M2M); +} + +/** + * @brief Disconnect DMA RX channel from peripheral + */ +static inline void gdma_ll_rx_disconnect_from_periph(gdma_dev_t *dev, uint32_t channel) +{ + dev->channel[channel].in.in_peri_sel.sel = GDMA_LL_INVALID_PERIPH_ID; + dev->channel[channel].in.in_conf0.mem_trans_en = false; } ///////////////////////////////////// TX ///////////////////////////////////////// @@ -458,11 +460,20 @@ static inline void gdma_ll_tx_set_priority(gdma_dev_t *dev, uint32_t channel, ui /** * @brief Connect DMA TX channel to a given peripheral */ -static inline void gdma_ll_tx_connect_to_periph(gdma_dev_t *dev, uint32_t channel, int periph_id) +static inline void gdma_ll_tx_connect_to_periph(gdma_dev_t *dev, uint32_t channel, gdma_trigger_peripheral_t periph, int periph_id) { + (void)periph; dev->channel[channel].out.out_peri_sel.sel = periph_id; } +/** + * @brief Disconnect DMA TX channel from peripheral + */ +static inline void gdma_ll_tx_disconnect_from_periph(gdma_dev_t *dev, uint32_t channel) +{ + dev->channel[channel].out.out_peri_sel.sel = GDMA_LL_INVALID_PERIPH_ID; +} + #ifdef __cplusplus } #endif diff --git a/components/hal/esp32c3/include/hal/gdma_ll.h b/components/hal/esp32c3/include/hal/gdma_ll.h index 67aa058e42..617b118f21 100644 --- a/components/hal/esp32c3/include/hal/gdma_ll.h +++ b/components/hal/esp32c3/include/hal/gdma_ll.h @@ -8,6 +8,7 @@ #include /* Required for NULL constant */ #include #include +#include "hal/gdma_types.h" #include "soc/gdma_struct.h" #include "soc/gdma_reg.h" @@ -20,6 +21,10 @@ extern "C" { #define GDMA_LL_RX_EVENT_MASK (0x06A7) #define GDMA_LL_TX_EVENT_MASK (0x1958) +// any "valid" peripheral ID can be used for M2M mode +#define GDMA_LL_M2M_FREE_PERIPH_ID_MASK (0x1CD) +#define GDMA_LL_INVALID_PERIPH_ID (0x3F) + #define GDMA_LL_EVENT_TX_FIFO_UDF (1<<12) #define GDMA_LL_EVENT_TX_FIFO_OVF (1<<11) #define GDMA_LL_EVENT_RX_FIFO_UDF (1<<10) @@ -35,19 +40,6 @@ extern "C" { #define GDMA_LL_EVENT_RX_DONE (1<<0) ///////////////////////////////////// Common ///////////////////////////////////////// -/** - * @brief Enable DMA channel M2M mode (TX channel n forward data to RX channel n), disabled by default - */ -static inline void gdma_ll_enable_m2m_mode(gdma_dev_t *dev, uint32_t channel, bool enable) -{ - dev->channel[channel].in.in_conf0.mem_trans_en = enable; - if (enable) { - // to enable m2m mode, the tx chan has to be the same to rx chan, and set to a valid value - dev->channel[channel].in.in_peri_sel.sel = 0; - dev->channel[channel].out.out_peri_sel.sel = 0; - } -} - /** * @brief Enable DMA clock gating */ @@ -255,9 +247,19 @@ static inline void gdma_ll_rx_set_priority(gdma_dev_t *dev, uint32_t channel, ui /** * @brief Connect DMA RX channel to a given peripheral */ -static inline void gdma_ll_rx_connect_to_periph(gdma_dev_t *dev, uint32_t channel, int periph_id) +static inline void gdma_ll_rx_connect_to_periph(gdma_dev_t *dev, uint32_t channel, gdma_trigger_peripheral_t periph, int periph_id) { dev->channel[channel].in.in_peri_sel.sel = periph_id; + dev->channel[channel].in.in_conf0.mem_trans_en = (periph == GDMA_TRIG_PERIPH_M2M); +} + +/** + * @brief Disconnect DMA RX channel from peripheral + */ +static inline void gdma_ll_rx_disconnect_from_periph(gdma_dev_t *dev, uint32_t channel) +{ + dev->channel[channel].in.in_peri_sel.sel = GDMA_LL_INVALID_PERIPH_ID; + dev->channel[channel].in.in_conf0.mem_trans_en = false; } ///////////////////////////////////// TX ///////////////////////////////////////// @@ -458,11 +460,20 @@ static inline void gdma_ll_tx_set_priority(gdma_dev_t *dev, uint32_t channel, ui /** * @brief Connect DMA TX channel to a given peripheral */ -static inline void gdma_ll_tx_connect_to_periph(gdma_dev_t *dev, uint32_t channel, int periph_id) +static inline void gdma_ll_tx_connect_to_periph(gdma_dev_t *dev, uint32_t channel, gdma_trigger_peripheral_t periph, int periph_id) { + (void)periph; dev->channel[channel].out.out_peri_sel.sel = periph_id; } +/** + * @brief Disconnect DMA TX channel from peripheral + */ +static inline void gdma_ll_tx_disconnect_from_periph(gdma_dev_t *dev, uint32_t channel) +{ + dev->channel[channel].out.out_peri_sel.sel = GDMA_LL_INVALID_PERIPH_ID; +} + #ifdef __cplusplus } #endif diff --git a/components/hal/esp32h2/include/hal/gdma_ll.h b/components/hal/esp32h2/include/hal/gdma_ll.h index 67aa058e42..617b118f21 100644 --- a/components/hal/esp32h2/include/hal/gdma_ll.h +++ b/components/hal/esp32h2/include/hal/gdma_ll.h @@ -8,6 +8,7 @@ #include /* Required for NULL constant */ #include #include +#include "hal/gdma_types.h" #include "soc/gdma_struct.h" #include "soc/gdma_reg.h" @@ -20,6 +21,10 @@ extern "C" { #define GDMA_LL_RX_EVENT_MASK (0x06A7) #define GDMA_LL_TX_EVENT_MASK (0x1958) +// any "valid" peripheral ID can be used for M2M mode +#define GDMA_LL_M2M_FREE_PERIPH_ID_MASK (0x1CD) +#define GDMA_LL_INVALID_PERIPH_ID (0x3F) + #define GDMA_LL_EVENT_TX_FIFO_UDF (1<<12) #define GDMA_LL_EVENT_TX_FIFO_OVF (1<<11) #define GDMA_LL_EVENT_RX_FIFO_UDF (1<<10) @@ -35,19 +40,6 @@ extern "C" { #define GDMA_LL_EVENT_RX_DONE (1<<0) ///////////////////////////////////// Common ///////////////////////////////////////// -/** - * @brief Enable DMA channel M2M mode (TX channel n forward data to RX channel n), disabled by default - */ -static inline void gdma_ll_enable_m2m_mode(gdma_dev_t *dev, uint32_t channel, bool enable) -{ - dev->channel[channel].in.in_conf0.mem_trans_en = enable; - if (enable) { - // to enable m2m mode, the tx chan has to be the same to rx chan, and set to a valid value - dev->channel[channel].in.in_peri_sel.sel = 0; - dev->channel[channel].out.out_peri_sel.sel = 0; - } -} - /** * @brief Enable DMA clock gating */ @@ -255,9 +247,19 @@ static inline void gdma_ll_rx_set_priority(gdma_dev_t *dev, uint32_t channel, ui /** * @brief Connect DMA RX channel to a given peripheral */ -static inline void gdma_ll_rx_connect_to_periph(gdma_dev_t *dev, uint32_t channel, int periph_id) +static inline void gdma_ll_rx_connect_to_periph(gdma_dev_t *dev, uint32_t channel, gdma_trigger_peripheral_t periph, int periph_id) { dev->channel[channel].in.in_peri_sel.sel = periph_id; + dev->channel[channel].in.in_conf0.mem_trans_en = (periph == GDMA_TRIG_PERIPH_M2M); +} + +/** + * @brief Disconnect DMA RX channel from peripheral + */ +static inline void gdma_ll_rx_disconnect_from_periph(gdma_dev_t *dev, uint32_t channel) +{ + dev->channel[channel].in.in_peri_sel.sel = GDMA_LL_INVALID_PERIPH_ID; + dev->channel[channel].in.in_conf0.mem_trans_en = false; } ///////////////////////////////////// TX ///////////////////////////////////////// @@ -458,11 +460,20 @@ static inline void gdma_ll_tx_set_priority(gdma_dev_t *dev, uint32_t channel, ui /** * @brief Connect DMA TX channel to a given peripheral */ -static inline void gdma_ll_tx_connect_to_periph(gdma_dev_t *dev, uint32_t channel, int periph_id) +static inline void gdma_ll_tx_connect_to_periph(gdma_dev_t *dev, uint32_t channel, gdma_trigger_peripheral_t periph, int periph_id) { + (void)periph; dev->channel[channel].out.out_peri_sel.sel = periph_id; } +/** + * @brief Disconnect DMA TX channel from peripheral + */ +static inline void gdma_ll_tx_disconnect_from_periph(gdma_dev_t *dev, uint32_t channel) +{ + dev->channel[channel].out.out_peri_sel.sel = GDMA_LL_INVALID_PERIPH_ID; +} + #ifdef __cplusplus } #endif diff --git a/components/hal/esp32s3/include/hal/gdma_ll.h b/components/hal/esp32s3/include/hal/gdma_ll.h index de8337468d..2fc7029303 100644 --- a/components/hal/esp32s3/include/hal/gdma_ll.h +++ b/components/hal/esp32s3/include/hal/gdma_ll.h @@ -8,7 +8,7 @@ #include /* For NULL declaration */ #include #include -#include "soc/soc_caps.h" +#include "hal/gdma_types.h" #include "soc/gdma_struct.h" #include "soc/gdma_reg.h" @@ -21,6 +21,10 @@ extern "C" { #define GDMA_LL_RX_EVENT_MASK (0x3FF) #define GDMA_LL_TX_EVENT_MASK (0xFF) +// any "valid" peripheral ID can be used for M2M mode +#define GDMA_LL_M2M_FREE_PERIPH_ID_MASK (0x3FF) +#define GDMA_LL_INVALID_PERIPH_ID (0x3F) + #define GDMA_LL_EVENT_TX_L3_FIFO_UDF (1<<7) #define GDMA_LL_EVENT_TX_L3_FIFO_OVF (1<<6) #define GDMA_LL_EVENT_TX_L1_FIFO_UDF (1<<5) @@ -49,19 +53,6 @@ extern "C" { #define GDMA_LL_EXT_MEM_BK_SIZE_64B (2) ///////////////////////////////////// Common ///////////////////////////////////////// -/** - * @brief Enable DMA channel M2M mode (TX channel n forward data to RX channel n), disabled by default - */ -static inline void gdma_ll_enable_m2m_mode(gdma_dev_t *dev, uint32_t channel, bool enable) -{ - dev->channel[channel].in.conf0.mem_trans_en = enable; - if (enable) { - // to enable m2m mode, the tx chan has to be the same to rx chan, and set to a valid value - dev->channel[channel].in.peri_sel.sel = 0; - dev->channel[channel].out.peri_sel.sel = 0; - } -} - /** * @brief Enable DMA clock gating */ @@ -303,9 +294,19 @@ static inline void gdma_ll_rx_set_priority(gdma_dev_t *dev, uint32_t channel, ui /** * @brief Connect DMA RX channel to a given peripheral */ -static inline void gdma_ll_rx_connect_to_periph(gdma_dev_t *dev, uint32_t channel, int periph_id) +static inline void gdma_ll_rx_connect_to_periph(gdma_dev_t *dev, uint32_t channel, gdma_trigger_peripheral_t periph, int periph_id) { dev->channel[channel].in.peri_sel.sel = periph_id; + dev->channel[channel].in.conf0.mem_trans_en = (periph == GDMA_TRIG_PERIPH_M2M); +} + +/** + * @brief Disconnect DMA RX channel from peripheral + */ +static inline void gdma_ll_rx_disconnect_from_periph(gdma_dev_t *dev, uint32_t channel) +{ + dev->channel[channel].in.peri_sel.sel = GDMA_LL_INVALID_PERIPH_ID; + dev->channel[channel].in.conf0.mem_trans_en = false; } ///////////////////////////////////// TX ///////////////////////////////////////// @@ -532,11 +533,20 @@ static inline void gdma_ll_tx_set_priority(gdma_dev_t *dev, uint32_t channel, ui /** * @brief Connect DMA TX channel to a given peripheral */ -static inline void gdma_ll_tx_connect_to_periph(gdma_dev_t *dev, uint32_t channel, int periph_id) +static inline void gdma_ll_tx_connect_to_periph(gdma_dev_t *dev, uint32_t channel, gdma_trigger_peripheral_t periph, int periph_id) { + (void)periph; dev->channel[channel].out.peri_sel.sel = periph_id; } +/** + * @brief Disconnect DMA TX channel from peripheral + */ +static inline void gdma_ll_tx_disconnect_from_periph(gdma_dev_t *dev, uint32_t channel) +{ + dev->channel[channel].out.peri_sel.sel = GDMA_LL_INVALID_PERIPH_ID; +} + #ifdef __cplusplus } #endif diff --git a/components/hal/include/hal/gdma_types.h b/components/hal/include/hal/gdma_types.h new file mode 100644 index 0000000000..eb1447a78f --- /dev/null +++ b/components/hal/include/hal/gdma_types.h @@ -0,0 +1,43 @@ +/* + * SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Enumeration of peripherals which have the DMA capability + * @note Some peripheral might not be available on certain chip, please refer to `soc_caps.h` for detail. + * + */ +typedef enum { + GDMA_TRIG_PERIPH_M2M, /*!< GDMA trigger peripheral: M2M */ + GDMA_TRIG_PERIPH_UHCI, /*!< GDMA trigger peripheral: UHCI */ + GDMA_TRIG_PERIPH_SPI, /*!< GDMA trigger peripheral: SPI */ + GDMA_TRIG_PERIPH_I2S, /*!< GDMA trigger peripheral: I2S */ + GDMA_TRIG_PERIPH_AES, /*!< GDMA trigger peripheral: AES */ + GDMA_TRIG_PERIPH_SHA, /*!< GDMA trigger peripheral: SHA */ + GDMA_TRIG_PERIPH_ADC, /*!< GDMA trigger peripheral: ADC */ + GDMA_TRIG_PERIPH_DAC, /*!< GDMA trigger peripheral: DAC */ + GDMA_TRIG_PERIPH_LCD, /*!< GDMA trigger peripheral: LCD */ + GDMA_TRIG_PERIPH_CAM, /*!< GDMA trigger peripheral: CAM */ + GDMA_TRIG_PERIPH_RMT, /*!< GDMA trigger peripheral: RMT */ +} gdma_trigger_peripheral_t; + +/** + * @brief Enumeration of GDMA channel direction + * + */ +typedef enum { + GDMA_CHANNEL_DIRECTION_TX, /*!< GDMA channel direction: TX */ + GDMA_CHANNEL_DIRECTION_RX, /*!< GDMA channel direction: RX */ +} gdma_channel_direction_t; + +#ifdef __cplusplus +} +#endif diff --git a/components/soc/esp32c2/include/soc/gdma_channel.h b/components/soc/esp32c2/include/soc/gdma_channel.h index 1c84a35249..d45217826b 100644 --- a/components/soc/esp32c2/include/soc/gdma_channel.h +++ b/components/soc/esp32c2/include/soc/gdma_channel.h @@ -9,6 +9,6 @@ // The following macros have a format SOC_[periph][instance_id] to make it work with `GDMA_MAKE_TRIGGER` #define SOC_GDMA_TRIG_PERIPH_M2M0 (-1) #define SOC_GDMA_TRIG_PERIPH_SPI2 (0) -#define SOC_GDMA_TRIG_PERIPH_UART0 (2) +#define SOC_GDMA_TRIG_PERIPH_UHCI0 (2) #define SOC_GDMA_TRIG_PERIPH_SHA0 (7) #define SOC_GDMA_TRIG_PERIPH_ADC0 (8) diff --git a/components/soc/esp32c3/include/soc/gdma_channel.h b/components/soc/esp32c3/include/soc/gdma_channel.h index ca3f0f7951..5f0b818bd2 100644 --- a/components/soc/esp32c3/include/soc/gdma_channel.h +++ b/components/soc/esp32c3/include/soc/gdma_channel.h @@ -9,7 +9,7 @@ // The following macros have a format SOC_[periph][instance_id] to make it work with `GDMA_MAKE_TRIGGER` #define SOC_GDMA_TRIG_PERIPH_M2M0 (-1) #define SOC_GDMA_TRIG_PERIPH_SPI2 (0) -#define SOC_GDMA_TRIG_PERIPH_UART0 (2) +#define SOC_GDMA_TRIG_PERIPH_UHCI0 (2) #define SOC_GDMA_TRIG_PERIPH_I2S0 (3) #define SOC_GDMA_TRIG_PERIPH_AES0 (6) #define SOC_GDMA_TRIG_PERIPH_SHA0 (7) diff --git a/components/soc/esp32h2/include/soc/gdma_channel.h b/components/soc/esp32h2/include/soc/gdma_channel.h index ca3f0f7951..5f0b818bd2 100644 --- a/components/soc/esp32h2/include/soc/gdma_channel.h +++ b/components/soc/esp32h2/include/soc/gdma_channel.h @@ -9,7 +9,7 @@ // The following macros have a format SOC_[periph][instance_id] to make it work with `GDMA_MAKE_TRIGGER` #define SOC_GDMA_TRIG_PERIPH_M2M0 (-1) #define SOC_GDMA_TRIG_PERIPH_SPI2 (0) -#define SOC_GDMA_TRIG_PERIPH_UART0 (2) +#define SOC_GDMA_TRIG_PERIPH_UHCI0 (2) #define SOC_GDMA_TRIG_PERIPH_I2S0 (3) #define SOC_GDMA_TRIG_PERIPH_AES0 (6) #define SOC_GDMA_TRIG_PERIPH_SHA0 (7) diff --git a/components/soc/esp32s3/include/soc/gdma_channel.h b/components/soc/esp32s3/include/soc/gdma_channel.h index 373cf6cabd..6178c1f1ab 100644 --- a/components/soc/esp32s3/include/soc/gdma_channel.h +++ b/components/soc/esp32s3/include/soc/gdma_channel.h @@ -10,7 +10,7 @@ #define SOC_GDMA_TRIG_PERIPH_M2M0 (-1) #define SOC_GDMA_TRIG_PERIPH_SPI2 (0) #define SOC_GDMA_TRIG_PERIPH_SPI3 (1) -#define SOC_GDMA_TRIG_PERIPH_UART0 (2) +#define SOC_GDMA_TRIG_PERIPH_UHCI0 (2) #define SOC_GDMA_TRIG_PERIPH_I2S0 (3) #define SOC_GDMA_TRIG_PERIPH_I2S1 (4) #define SOC_GDMA_TRIG_PERIPH_LCD0 (5) diff --git a/examples/bluetooth/hci/controller_hci_uart_esp32c3_and_esp32s3/main/main.c b/examples/bluetooth/hci/controller_hci_uart_esp32c3_and_esp32s3/main/main.c index 0cacc7b4ca..8770990c8e 100644 --- a/examples/bluetooth/hci/controller_hci_uart_esp32c3_and_esp32s3/main/main.c +++ b/examples/bluetooth/hci/controller_hci_uart_esp32c3_and_esp32s3/main/main.c @@ -221,8 +221,8 @@ void uhci_uart_install(void) }; ESP_ERROR_CHECK(gdma_new_channel(&rx_channel_config, &s_rx_channel)); - gdma_connect(s_tx_channel, GDMA_MAKE_TRIGGER(GDMA_TRIG_PERIPH_UART, 0)); - gdma_connect(s_rx_channel, GDMA_MAKE_TRIGGER(GDMA_TRIG_PERIPH_UART, 0)); + gdma_connect(s_tx_channel, GDMA_MAKE_TRIGGER(GDMA_TRIG_PERIPH_UHCI, 0)); + gdma_connect(s_rx_channel, GDMA_MAKE_TRIGGER(GDMA_TRIG_PERIPH_UHCI, 0)); gdma_strategy_config_t strategy_config = { .auto_update_desc = false,