feat(lcd): support buffer switch interrupt

This commit is contained in:
Chen Jichang
2026-06-02 19:35:19 +08:00
parent 33589325c7
commit e1d48975fb
26 changed files with 337 additions and 104 deletions
+48 -2
View File
@@ -465,6 +465,12 @@ esp_err_t gdma_register_tx_event_callbacks(gdma_channel_handle_t dma_chan, gdma_
gdma_group_t *group = pair->group;
gdma_hal_context_t *hal = &group->hal;
gdma_tx_channel_t *tx_chan = __containerof(dma_chan, gdma_tx_channel_t, base);
bool link_switch_event_supported = gdma_hal_is_tx_link_switch_event_supported(hal);
if (cbs->on_link_switch) {
ESP_RETURN_ON_FALSE(link_switch_event_supported,
ESP_ERR_NOT_SUPPORTED, TAG, "on_link_switch not supported");
}
if (dma_chan->flags.isr_cache_safe) {
if (cbs->on_trans_eof) {
@@ -475,6 +481,10 @@ esp_err_t gdma_register_tx_event_callbacks(gdma_channel_handle_t dma_chan, gdma_
ESP_RETURN_ON_FALSE(esp_ptr_in_iram(cbs->on_descr_err), ESP_ERR_INVALID_ARG,
TAG, "on_descr_err not in IRAM");
}
if (cbs->on_link_switch) {
ESP_RETURN_ON_FALSE(esp_ptr_in_iram(cbs->on_link_switch), ESP_ERR_INVALID_ARG,
TAG, "on_link_switch not in IRAM");
}
if (user_data) {
ESP_RETURN_ON_FALSE(esp_ptr_internal(user_data), ESP_ERR_INVALID_ARG,
TAG, "user context not in internal RAM");
@@ -488,6 +498,12 @@ esp_err_t gdma_register_tx_event_callbacks(gdma_channel_handle_t dma_chan, gdma_
portENTER_CRITICAL(&pair->spinlock);
gdma_hal_enable_intr(hal, pair->pair_id, GDMA_CHANNEL_DIRECTION_TX, GDMA_LL_EVENT_TX_EOF, cbs->on_trans_eof != NULL);
gdma_hal_enable_intr(hal, pair->pair_id, GDMA_CHANNEL_DIRECTION_TX, GDMA_LL_EVENT_TX_DESC_ERROR, cbs->on_descr_err != NULL);
#if GDMA_LL_EVENT_TX_LINK_SWITCH
if (link_switch_event_supported) {
gdma_hal_enable_intr(hal, pair->pair_id, GDMA_CHANNEL_DIRECTION_TX, GDMA_LL_EVENT_TX_LINK_SWITCH,
cbs->on_link_switch != NULL);
}
#endif // GDMA_LL_EVENT_TX_LINK_SWITCH
portEXIT_CRITICAL(&pair->spinlock);
memcpy(&tx_chan->cbs, cbs, sizeof(gdma_tx_event_callbacks_t));
@@ -601,6 +617,30 @@ esp_err_t gdma_reset(gdma_channel_handle_t dma_chan)
return ESP_OK;
}
esp_err_t gdma_request_link_switch_event(gdma_channel_handle_t dma_tx_chan)
{
if (!dma_tx_chan || dma_tx_chan->direction != GDMA_CHANNEL_DIRECTION_TX) {
return ESP_ERR_INVALID_ARG;
}
gdma_pair_t *pair = dma_tx_chan->pair;
gdma_group_t *group = pair->group;
gdma_hal_context_t *hal = &group->hal;
gdma_tx_channel_t *tx_chan = __containerof(dma_tx_chan, gdma_tx_channel_t, base);
if (!gdma_hal_is_tx_link_switch_event_supported(hal)) {
return ESP_ERR_NOT_SUPPORTED;
}
if (!tx_chan->cbs.on_link_switch) {
return ESP_ERR_INVALID_STATE;
}
portENTER_CRITICAL_SAFE(&dma_tx_chan->spinlock);
gdma_hal_request_link_switch_event(hal, pair->pair_id, dma_tx_chan->direction);
portEXIT_CRITICAL_SAFE(&dma_tx_chan->spinlock);
return ESP_OK;
}
static void gdma_release_group_handle(gdma_group_t *group)
{
int group_id = group->group_id;
@@ -868,6 +908,12 @@ void gdma_default_tx_isr(void *args)
if ((intr_status & GDMA_LL_EVENT_TX_DESC_ERROR) && tx_chan->cbs.on_descr_err) {
need_yield |= tx_chan->cbs.on_descr_err(&tx_chan->base, NULL, tx_chan->user_data);
}
#if GDMA_LL_EVENT_TX_LINK_SWITCH
if (gdma_hal_is_tx_link_switch_event_supported(hal) &&
(intr_status & GDMA_LL_EVENT_TX_LINK_SWITCH) && tx_chan->cbs.on_link_switch) {
need_yield |= tx_chan->cbs.on_link_switch(&tx_chan->base, NULL, tx_chan->user_data);
}
#endif // GDMA_LL_EVENT_TX_LINK_SWITCH
if (need_yield) {
portYIELD_FROM_ISR();
}
@@ -890,7 +936,7 @@ static esp_err_t gdma_install_rx_interrupt(gdma_rx_channel_t *rx_chan)
#endif
intr_handle_t intr = NULL;
ret = esp_intr_alloc_intrstatus(gdma_periph_signals.groups[group->group_id].pairs[pair_id].rx_irq_id, isr_flags,
gdma_hal_get_intr_status_reg(hal, pair_id, GDMA_CHANNEL_DIRECTION_RX), GDMA_LL_RX_EVENT_MASK,
gdma_hal_get_intr_status_reg(hal, pair_id, GDMA_CHANNEL_DIRECTION_RX), hal->priv_data->rx_event_mask,
gdma_default_rx_isr, rx_chan, &intr);
ESP_GOTO_ON_ERROR(ret, err, TAG, "alloc interrupt failed");
rx_chan->base.intr = intr;
@@ -922,7 +968,7 @@ static esp_err_t gdma_install_tx_interrupt(gdma_tx_channel_t *tx_chan)
#endif
intr_handle_t intr = NULL;
ret = esp_intr_alloc_intrstatus(gdma_periph_signals.groups[group->group_id].pairs[pair_id].tx_irq_id, isr_flags,
gdma_hal_get_intr_status_reg(hal, pair_id, GDMA_CHANNEL_DIRECTION_TX), GDMA_LL_TX_EVENT_MASK,
gdma_hal_get_intr_status_reg(hal, pair_id, GDMA_CHANNEL_DIRECTION_TX), hal->priv_data->tx_event_mask,
gdma_default_tx_isr, tx_chan, &intr);
ESP_GOTO_ON_ERROR(ret, err, TAG, "alloc interrupt failed");
tx_chan->base.intr = intr;
@@ -68,6 +68,7 @@ typedef bool (*gdma_event_callback_t)(gdma_channel_handle_t dma_chan, gdma_event
typedef struct {
gdma_event_callback_t on_trans_eof; /*!< Invoked when TX engine meets EOF descriptor */
gdma_event_callback_t on_descr_err; /*!< Invoked when DMA encounters a descriptor error */
gdma_event_callback_t on_link_switch; /*!< Invoked when TX link list switches to a new descriptor chain */
} gdma_tx_event_callbacks_t;
/**
@@ -281,6 +282,26 @@ esp_err_t gdma_get_group_channel_id(gdma_channel_handle_t dma_chan, int *group_i
*/
esp_err_t gdma_register_tx_event_callbacks(gdma_channel_handle_t dma_chan, gdma_tx_event_callbacks_t *cbs, void *user_data);
/**
* @brief Request an interrupt when the TX channel switches to a new descriptor chain
*
* @note This API is only available on targets that support TX link switch interrupt.
* @note Register the `on_link_switch` callback by `gdma_register_tx_event_callbacks()` before calling this API.
* @note The TX EOF event indicates that GDMA has reached an EOF descriptor. By contrast, the TX link switch
* event indicates that GDMA has started using the next descriptor chain after a link update.
* GDMA can prefetch descriptors from the updated link before the EOF event, so EOF doesn't always mean
* the previous descriptor chain is no longer referenced by GDMA. The link switch event is needed when
* the caller must know that the previous descriptor chain can be reused safely.
*
* @param[in] dma_tx_chan GDMA TX channel handle
* @return
* - ESP_OK: Request link switch event successfully
* - ESP_ERR_INVALID_ARG: Invalid argument
* - ESP_ERR_NOT_SUPPORTED: Link switch event is not supported
* - ESP_ERR_INVALID_STATE: Link switch callback is not registered
*/
esp_err_t gdma_request_link_switch_event(gdma_channel_handle_t dma_tx_chan);
/**
* @brief Set GDMA event callbacks for RX channel
* @note This API will install GDMA interrupt service for the channel internally
+4
View File
@@ -20,24 +20,28 @@ entries:
gdma_hal_top: gdma_hal_clear_intr (noflash)
gdma_hal_top: gdma_hal_read_intr_status (noflash)
gdma_hal_top: gdma_hal_get_eof_desc_addr (noflash)
gdma_hal_top: gdma_hal_is_tx_link_switch_event_supported (noflash)
# GDMA implementation layer for AHB-DMA version 1
if SOC_AHB_GDMA_VERSION = 1:
gdma_hal_ahb_v1: gdma_ahb_hal_clear_intr (noflash)
gdma_hal_ahb_v1: gdma_ahb_hal_read_intr_status (noflash)
gdma_hal_ahb_v1: gdma_ahb_hal_get_eof_desc_addr (noflash)
gdma_hal_ahb_v1: gdma_ahb_hal_is_tx_link_switch_event_supported (noflash)
# GDMA implementation layer for AHB-DMA version 2
if SOC_AHB_GDMA_VERSION = 2:
gdma_hal_ahb_v2: gdma_ahb_hal_clear_intr (noflash)
gdma_hal_ahb_v2: gdma_ahb_hal_read_intr_status (noflash)
gdma_hal_ahb_v2: gdma_ahb_hal_get_eof_desc_addr (noflash)
gdma_hal_ahb_v2: gdma_ahb_hal_is_tx_link_switch_event_supported (noflash)
# GDMA implementation layer for AXI-DMA
if SOC_AXI_GDMA_SUPPORTED = y:
gdma_hal_axi: gdma_axi_hal_clear_intr (noflash)
gdma_hal_axi: gdma_axi_hal_read_intr_status (noflash)
gdma_hal_axi: gdma_axi_hal_get_eof_desc_addr (noflash)
gdma_hal_axi: gdma_axi_hal_is_tx_link_switch_event_supported (noflash)
# put GDMA control HAL functions in IRAM
if GDMA_CTRL_FUNC_IN_IRAM = y: