mirror of
https://github.com/espressif/esp-idf.git
synced 2026-10-01 18:50:34 +03:00
feat(lcd): support buffer switch interrupt
This commit is contained in:
@@ -63,6 +63,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;
|
||||
|
||||
/**
|
||||
@@ -305,6 +306,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
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -550,6 +550,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) {
|
||||
@@ -560,6 +566,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");
|
||||
@@ -571,8 +581,16 @@ esp_err_t gdma_register_tx_event_callbacks(gdma_channel_handle_t dma_chan, gdma_
|
||||
|
||||
// enable/disable GDMA interrupt events for TX channel
|
||||
esp_os_enter_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);
|
||||
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
|
||||
esp_os_exit_critical(&pair->spinlock);
|
||||
|
||||
memcpy(&tx_chan->cbs, cbs, sizeof(gdma_tx_event_callbacks_t));
|
||||
@@ -703,6 +721,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;
|
||||
}
|
||||
|
||||
esp_os_enter_critical_safe(&dma_tx_chan->spinlock);
|
||||
gdma_hal_request_link_switch_event(hal, pair->pair_id, dma_tx_chan->direction);
|
||||
esp_os_exit_critical_safe(&dma_tx_chan->spinlock);
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static void gdma_try_free_group_handle(gdma_group_t *group)
|
||||
{
|
||||
int group_id = group->group_id;
|
||||
@@ -1000,6 +1042,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();
|
||||
}
|
||||
@@ -1031,7 +1079,7 @@ static esp_err_t gdma_install_rx_interrupt(gdma_rx_channel_t *rx_chan)
|
||||
.source = pair_signals->rx_irq_id,
|
||||
.flags = isr_flags,
|
||||
.intrstatusreg = gdma_hal_get_intr_status_reg(hal, pair_id, GDMA_CHANNEL_DIRECTION_RX),
|
||||
.intrstatusmask = GDMA_LL_RX_EVENT_MASK,
|
||||
.intrstatusmask = hal->priv_data->rx_event_mask,
|
||||
.handler = gdma_default_rx_isr,
|
||||
.arg = rx_chan,
|
||||
.bind_by.name = tx_rx_share_irq ? pair_signals->name : NULL,
|
||||
@@ -1077,7 +1125,7 @@ static esp_err_t gdma_install_tx_interrupt(gdma_tx_channel_t *tx_chan)
|
||||
.source = pair_signals->tx_irq_id,
|
||||
.flags = isr_flags,
|
||||
.intrstatusreg = gdma_hal_get_intr_status_reg(hal, pair_id, GDMA_CHANNEL_DIRECTION_TX),
|
||||
.intrstatusmask = GDMA_LL_TX_EVENT_MASK,
|
||||
.intrstatusmask = hal->priv_data->tx_event_mask,
|
||||
.handler = gdma_default_tx_isr,
|
||||
.arg = tx_chan,
|
||||
.bind_by.name = tx_rx_share_irq ? pair_signals->name : NULL,
|
||||
|
||||
@@ -24,8 +24,8 @@ extern "C" {
|
||||
#define GDMA_LL_CHANNEL_MAX_PRIORITY 5 // supported priority levels: [0,5]
|
||||
#define GDMA_LL_CHANNEL_MAX_WEIGHT 15 // supported weight levels: [0,15]
|
||||
|
||||
#define GDMA_LL_RX_EVENT_MASK (0x7F)
|
||||
#define GDMA_LL_TX_EVENT_MASK (0x3F)
|
||||
#define AHB_DMA_LL_RX_EVENT_MASK (0x7F)
|
||||
#define AHB_DMA_LL_TX_EVENT_MASK (0x3F)
|
||||
|
||||
// for M2M mode, hardware will automatically assign peri_sel ID depends on the channel number (ch0: 10, ch1: 11, ch2: 12)
|
||||
#define AHB_DMA_LL_M2M_FREE_PERIPH_ID_MASK (0x1C00)
|
||||
|
||||
@@ -24,8 +24,8 @@ extern "C" {
|
||||
#define GDMA_LL_CHANNEL_MAX_PRIORITY 5 // supported priority levels: [0,5]
|
||||
#define GDMA_LL_CHANNEL_MAX_WEIGHT 15 // supported weight levels: [0,15]
|
||||
|
||||
#define GDMA_LL_RX_EVENT_MASK (0x7F)
|
||||
#define GDMA_LL_TX_EVENT_MASK (0x3F)
|
||||
#define AHB_DMA_LL_RX_EVENT_MASK (0x7F)
|
||||
#define AHB_DMA_LL_TX_EVENT_MASK (0x3F)
|
||||
|
||||
// any "dummy" peripheral ID can be used for M2M mode
|
||||
#define AHB_DMA_LL_M2M_FREE_PERIPH_ID_MASK (0xFE75)
|
||||
|
||||
@@ -23,8 +23,8 @@ extern "C" {
|
||||
|
||||
#define GDMA_LL_CHANNEL_MAX_PRIORITY 5 // supported priority levels: [0,5]
|
||||
|
||||
#define GDMA_LL_RX_EVENT_MASK (0x7F)
|
||||
#define GDMA_LL_TX_EVENT_MASK (0x3F)
|
||||
#define AHB_DMA_LL_RX_EVENT_MASK (0x7F)
|
||||
#define AHB_DMA_LL_TX_EVENT_MASK (0x3F)
|
||||
|
||||
// any "dummy" peripheral ID can be used for M2M mode
|
||||
#define AHB_DMA_LL_M2M_FREE_PERIPH_ID_MASK (0xFC00)
|
||||
|
||||
@@ -24,6 +24,8 @@ extern "C" {
|
||||
|
||||
// any "dummy" peripheral ID can be used for M2M mode
|
||||
#define AHB_DMA_LL_M2M_FREE_PERIPH_ID_MASK (0xFAC2)
|
||||
#define AHB_DMA_LL_RX_EVENT_MASK (0x1F)
|
||||
#define AHB_DMA_LL_TX_EVENT_MASK (0x0F)
|
||||
|
||||
///////////////////////////////////// Common /////////////////////////////////////////
|
||||
/**
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
#include "hal/hal_utils.h"
|
||||
#include "hal/gdma_types.h"
|
||||
#include "hal/gdma_ll.h"
|
||||
#include "hal/config.h"
|
||||
#include "soc/axi_dma_struct.h"
|
||||
#include "soc/axi_dma_reg.h"
|
||||
|
||||
@@ -24,6 +25,13 @@ extern "C" {
|
||||
|
||||
// any "dummy" peripheral ID can be used for M2M mode
|
||||
#define AXI_DMA_LL_M2M_FREE_PERIPH_ID_MASK (0xFFC0)
|
||||
#define AXI_DMA_LL_RX_EVENT_MASK (0x1F)
|
||||
#if HAL_CONFIG(CHIP_SUPPORT_MIN_REV) >= 300
|
||||
#define AXI_DMA_LL_SUPPORT_TX_LINK_SWITCH_EVENT 1
|
||||
#define AXI_DMA_LL_TX_EVENT_MASK (0x40F)
|
||||
#else
|
||||
#define AXI_DMA_LL_TX_EVENT_MASK (0x0F)
|
||||
#endif
|
||||
|
||||
///////////////////////////////////// Common /////////////////////////////////////////
|
||||
/**
|
||||
@@ -476,6 +484,42 @@ static inline void axi_dma_ll_tx_restart(axi_dma_dev_t *dev, uint32_t channel)
|
||||
dev->out[channel].conf.out_link1.outlink_restart_chn = 1;
|
||||
}
|
||||
|
||||
#if AXI_DMA_LL_SUPPORT_TX_LINK_SWITCH_EVENT
|
||||
/**
|
||||
* @brief Request link switch done indication for TX channel
|
||||
*/
|
||||
static inline void axi_dma_ll_tx_request_link_switch_event(axi_dma_dev_t *dev, uint32_t channel)
|
||||
{
|
||||
switch (channel) {
|
||||
case 0:
|
||||
dev->link_switch_state.link_switch_state_ch0 = 1;
|
||||
break;
|
||||
case 1:
|
||||
dev->link_switch_state.link_switch_state_ch1 = 1;
|
||||
break;
|
||||
case 2:
|
||||
dev->link_switch_state.link_switch_state_ch2 = 1;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Check if TX link switch done indication is supported
|
||||
*/
|
||||
__attribute__((always_inline))
|
||||
static inline bool axi_dma_ll_tx_is_link_switch_event_supported(axi_dma_dev_t *dev)
|
||||
{
|
||||
(void)dev;
|
||||
#if AXI_DMA_LL_SUPPORT_TX_LINK_SWITCH_EVENT
|
||||
return true;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Check if DMA TX descriptor FSM is in IDLE state
|
||||
*/
|
||||
|
||||
@@ -26,9 +26,10 @@
|
||||
|
||||
#define GDMA_LL_CHANNEL_MAX_PRIORITY 5 // supported priority levels: [0,5]
|
||||
|
||||
#define GDMA_LL_RX_EVENT_MASK (0x1F)
|
||||
#define GDMA_LL_TX_EVENT_MASK (0x0F)
|
||||
|
||||
// the following event bits are only supported by axi-dma
|
||||
#if HAL_CONFIG(CHIP_SUPPORT_MIN_REV) >= 300
|
||||
#define GDMA_LL_EVENT_TX_LINK_SWITCH (1<<10)
|
||||
#endif
|
||||
// the following event bits are identical for ahb-dma and axi-dma
|
||||
#define GDMA_LL_EVENT_TX_TOTAL_EOF (1<<3)
|
||||
#define GDMA_LL_EVENT_TX_DESC_ERROR (1<<2)
|
||||
|
||||
@@ -22,8 +22,12 @@ extern "C" {
|
||||
|
||||
// any "dummy" peripheral ID can be used for M2M mode
|
||||
#define AHB_DMA_LL_M2M_FREE_PERIPH_ID_MASK (0x8200)
|
||||
#define LP_AHB_DMA_LL_M2M_FREE_PERIPH_ID_MASK (0xFFFC)
|
||||
#define AHB_DMA_LL_RX_EVENT_MASK (0x7F)
|
||||
#define AHB_DMA_LL_TX_EVENT_MASK (0x3F)
|
||||
|
||||
#define LP_AHB_DMA_LL_M2M_FREE_PERIPH_ID_MASK (0xFFFC)
|
||||
#define AHB_DMA_LL_RX_EVENT_MASK (0x7F)
|
||||
#define AHB_DMA_LL_TX_EVENT_MASK (0x3F)
|
||||
///////////////////////////////////// Common /////////////////////////////////////////
|
||||
|
||||
/**
|
||||
|
||||
@@ -24,6 +24,9 @@ extern "C" {
|
||||
|
||||
// any "dummy" peripheral ID can be used for M2M mode
|
||||
#define AXI_DMA_LL_M2M_FREE_PERIPH_ID_MASK (0xFFC0)
|
||||
#define AXI_DMA_LL_RX_EVENT_MASK (0x7F)
|
||||
#define AXI_DMA_LL_SUPPORT_TX_LINK_SWITCH_EVENT 1
|
||||
#define AXI_DMA_LL_TX_EVENT_MASK (0x43F)
|
||||
|
||||
///////////////////////////////////// Common /////////////////////////////////////////
|
||||
/**
|
||||
@@ -473,6 +476,36 @@ static inline void axi_dma_ll_tx_restart(axi_dma_dev_t *dev, uint32_t channel)
|
||||
dev->out[channel].conf.out_link1.outlink_restart_chn = 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Request link switch done indication for TX channel
|
||||
*/
|
||||
static inline void axi_dma_ll_tx_request_link_switch_event(axi_dma_dev_t *dev, uint32_t channel)
|
||||
{
|
||||
switch (channel) {
|
||||
case 0:
|
||||
dev->link_switch_state.link_switch_state_ch0 = 1;
|
||||
break;
|
||||
case 1:
|
||||
dev->link_switch_state.link_switch_state_ch1 = 1;
|
||||
break;
|
||||
case 2:
|
||||
dev->link_switch_state.link_switch_state_ch2 = 1;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Check if TX link switch done indication is supported
|
||||
*/
|
||||
__attribute__((always_inline))
|
||||
static inline bool axi_dma_ll_tx_is_link_switch_event_supported(axi_dma_dev_t *dev)
|
||||
{
|
||||
(void)dev;
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Check if DMA TX descriptor FSM is in IDLE state
|
||||
*/
|
||||
|
||||
@@ -20,9 +20,8 @@
|
||||
|
||||
#define GDMA_LL_CHANNEL_MAX_PRIORITY 5 // supported priority levels: [0,5]
|
||||
|
||||
#define GDMA_LL_RX_EVENT_MASK (0x7F)
|
||||
#define GDMA_LL_TX_EVENT_MASK (0x3F)
|
||||
|
||||
// the following event bits are only supported by axi-dma
|
||||
#define GDMA_LL_EVENT_TX_LINK_SWITCH (1<<10)
|
||||
// the following event bits are identical for ahb-dma, axi-dma and lp-ahb-dma
|
||||
#define GDMA_LL_EVENT_TX_FIFO_UDF (1<<5)
|
||||
#define GDMA_LL_EVENT_TX_FIFO_OVF (1<<4)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2022-2024 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2022-2026 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
@@ -11,6 +11,8 @@
|
||||
|
||||
static gdma_hal_priv_data_t gdma_ahb_hal_priv_data = {
|
||||
.m2m_free_periph_mask = GDMA_LL_M2M_FREE_PERIPH_ID_MASK,
|
||||
.tx_event_mask = GDMA_LL_TX_EVENT_MASK,
|
||||
.rx_event_mask = GDMA_LL_RX_EVENT_MASK,
|
||||
};
|
||||
|
||||
void gdma_ahb_hal_start_with_desc(gdma_hal_context_t *hal, int chan_id, gdma_channel_direction_t dir, intptr_t desc_base_addr)
|
||||
@@ -182,6 +184,12 @@ void gdma_ahb_hal_enable_etm_task(gdma_hal_context_t *hal, int chan_id, gdma_cha
|
||||
}
|
||||
#endif // SOC_GDMA_SUPPORT_ETM
|
||||
|
||||
bool gdma_ahb_hal_is_tx_link_switch_event_supported(gdma_hal_context_t *hal)
|
||||
{
|
||||
(void)hal;
|
||||
return false;
|
||||
}
|
||||
|
||||
void gdma_ahb_hal_init(gdma_hal_context_t *hal, const gdma_hal_config_t *config)
|
||||
{
|
||||
hal->dev = GDMA_LL_GET_HW(config->group_id - GDMA_LL_AHB_GROUP_START_ID);
|
||||
@@ -206,5 +214,6 @@ void gdma_ahb_hal_init(gdma_hal_context_t *hal, const gdma_hal_config_t *config)
|
||||
#if GDMA_LL_GET(AHB_BURST_SIZE_ADJUSTABLE)
|
||||
hal->set_burst_size = gdma_ahb_hal_set_burst_size;
|
||||
#endif // GDMA_LL_GET(AHB_BURST_SIZE_ADJUSTABLE)
|
||||
hal->is_tx_link_switch_event_supported = gdma_ahb_hal_is_tx_link_switch_event_supported;
|
||||
hal->priv_data = &gdma_ahb_hal_priv_data;
|
||||
}
|
||||
|
||||
@@ -12,6 +12,8 @@
|
||||
|
||||
static gdma_hal_priv_data_t gdma_ahb_hal_priv_data = {
|
||||
.m2m_free_periph_mask = AHB_DMA_LL_M2M_FREE_PERIPH_ID_MASK,
|
||||
.tx_event_mask = AHB_DMA_LL_TX_EVENT_MASK,
|
||||
.rx_event_mask = AHB_DMA_LL_RX_EVENT_MASK,
|
||||
};
|
||||
|
||||
void gdma_ahb_hal_start_with_desc(gdma_hal_context_t *hal, int chan_id, gdma_channel_direction_t dir, intptr_t desc_base_addr)
|
||||
@@ -250,6 +252,12 @@ void gdma_ahb_hal_set_weight(gdma_hal_context_t *hal, int chan_id, gdma_channel_
|
||||
}
|
||||
#endif // SOC_GDMA_SUPPORT_WEIGHTED_ARBITRATION
|
||||
|
||||
bool gdma_ahb_hal_is_tx_link_switch_event_supported(gdma_hal_context_t *hal)
|
||||
{
|
||||
(void)hal;
|
||||
return false;
|
||||
}
|
||||
|
||||
void gdma_ahb_hal_init(gdma_hal_context_t *hal, const gdma_hal_config_t *config)
|
||||
{
|
||||
hal->ahb_dma_dev = AHB_DMA_LL_GET_HW(config->group_id - GDMA_LL_AHB_GROUP_START_ID);
|
||||
@@ -280,6 +288,7 @@ void gdma_ahb_hal_init(gdma_hal_context_t *hal, const gdma_hal_config_t *config)
|
||||
#if GDMA_LL_GET(AHB_BURST_SIZE_ADJUSTABLE)
|
||||
hal->set_burst_size = gdma_ahb_hal_set_burst_size;
|
||||
#endif // GDMA_LL_GET(AHB_BURST_SIZE_ADJUSTABLE)
|
||||
hal->is_tx_link_switch_event_supported = gdma_ahb_hal_is_tx_link_switch_event_supported;
|
||||
#if SOC_GDMA_SUPPORT_WEIGHTED_ARBITRATION
|
||||
hal->set_weight = gdma_ahb_hal_set_weight;
|
||||
if (config->flags.enable_weighted_arbitration) {
|
||||
@@ -304,6 +313,8 @@ void gdma_ahb_hal_init(gdma_hal_context_t *hal, const gdma_hal_config_t *config)
|
||||
*/
|
||||
static gdma_hal_priv_data_t gdma_lp_ahb_hal_priv_data = {
|
||||
.m2m_free_periph_mask = LP_AHB_DMA_LL_M2M_FREE_PERIPH_ID_MASK,
|
||||
.tx_event_mask = AHB_DMA_LL_TX_EVENT_MASK,
|
||||
.rx_event_mask = AHB_DMA_LL_RX_EVENT_MASK,
|
||||
};
|
||||
|
||||
void gdma_lp_ahb_hal_init(gdma_hal_context_t *hal, const gdma_hal_config_t *config)
|
||||
@@ -333,6 +344,7 @@ void gdma_lp_ahb_hal_init(gdma_hal_context_t *hal, const gdma_hal_config_t *conf
|
||||
#if GDMA_LL_GET(AHB_BURST_SIZE_ADJUSTABLE)
|
||||
hal->set_burst_size = gdma_ahb_hal_set_burst_size;
|
||||
#endif // GDMA_LL_GET(AHB_BURST_SIZE_ADJUSTABLE)
|
||||
hal->is_tx_link_switch_event_supported = gdma_ahb_hal_is_tx_link_switch_event_supported;
|
||||
// LP AHB GDMA has a different memory range
|
||||
lp_ahb_dma_ll_set_default_memory_range(hal->ahb_dma_dev);
|
||||
}
|
||||
|
||||
@@ -12,6 +12,8 @@
|
||||
|
||||
static gdma_hal_priv_data_t gdma_axi_hal_priv_data = {
|
||||
.m2m_free_periph_mask = AXI_DMA_LL_M2M_FREE_PERIPH_ID_MASK,
|
||||
.tx_event_mask = AXI_DMA_LL_TX_EVENT_MASK,
|
||||
.rx_event_mask = AXI_DMA_LL_RX_EVENT_MASK,
|
||||
};
|
||||
|
||||
void gdma_axi_hal_start_with_desc(gdma_hal_context_t *hal, int chan_id, gdma_channel_direction_t dir, intptr_t desc_base_addr)
|
||||
@@ -170,6 +172,15 @@ uint32_t gdma_axi_hal_get_eof_desc_addr(gdma_hal_context_t *hal, int chan_id, gd
|
||||
}
|
||||
}
|
||||
|
||||
#if AXI_DMA_LL_SUPPORT_TX_LINK_SWITCH_EVENT
|
||||
void gdma_axi_hal_request_link_switch_event(gdma_hal_context_t *hal, int chan_id, gdma_channel_direction_t dir)
|
||||
{
|
||||
if (dir == GDMA_CHANNEL_DIRECTION_TX) {
|
||||
axi_dma_ll_tx_request_link_switch_event(hal->axi_dma_dev, chan_id);
|
||||
}
|
||||
}
|
||||
#endif // AXI_DMA_LL_SUPPORT_TX_LINK_SWITCH_EVENT
|
||||
|
||||
#if SOC_GDMA_SUPPORT_CRC
|
||||
void gdma_axi_hal_clear_crc(gdma_hal_context_t *hal, int chan_id, gdma_channel_direction_t dir)
|
||||
{
|
||||
@@ -237,6 +248,11 @@ void gdma_axi_hal_enable_etm_task(gdma_hal_context_t *hal, int chan_id, gdma_cha
|
||||
}
|
||||
#endif // SOC_GDMA_SUPPORT_ETM
|
||||
|
||||
bool gdma_axi_hal_is_tx_link_switch_event_supported(gdma_hal_context_t *hal)
|
||||
{
|
||||
return axi_dma_ll_tx_is_link_switch_event_supported(hal->axi_dma_dev);
|
||||
}
|
||||
|
||||
void gdma_axi_hal_init(gdma_hal_context_t *hal, const gdma_hal_config_t *config)
|
||||
{
|
||||
hal->axi_dma_dev = AXI_DMA_LL_GET_HW(config->group_id - GDMA_LL_AXI_GROUP_START_ID);
|
||||
@@ -265,5 +281,10 @@ void gdma_axi_hal_init(gdma_hal_context_t *hal, const gdma_hal_config_t *config)
|
||||
#if SOC_GDMA_SUPPORT_ETM
|
||||
hal->enable_etm_task = gdma_axi_hal_enable_etm_task;
|
||||
#endif // SOC_GDMA_SUPPORT_ETM
|
||||
|
||||
hal->is_tx_link_switch_event_supported = gdma_axi_hal_is_tx_link_switch_event_supported;
|
||||
#if AXI_DMA_LL_SUPPORT_TX_LINK_SWITCH_EVENT
|
||||
hal->request_link_switch_event = gdma_axi_hal_request_link_switch_event;
|
||||
#endif // AXI_DMA_LL_SUPPORT_TX_LINK_SWITCH_EVENT
|
||||
axi_dma_ll_set_default_memory_range(hal->axi_dma_dev);
|
||||
}
|
||||
|
||||
@@ -125,3 +125,13 @@ void gdma_hal_set_weight(gdma_hal_context_t *hal, int chan_id, gdma_channel_dire
|
||||
hal->set_weight(hal, chan_id, dir, weight);
|
||||
}
|
||||
#endif // SOC_GDMA_SUPPORT_WEIGHTED_ARBITRATION
|
||||
|
||||
void gdma_hal_request_link_switch_event(gdma_hal_context_t *hal, int chan_id, gdma_channel_direction_t dir)
|
||||
{
|
||||
hal->request_link_switch_event(hal, chan_id, dir);
|
||||
}
|
||||
|
||||
bool gdma_hal_is_tx_link_switch_event_supported(gdma_hal_context_t *hal)
|
||||
{
|
||||
return hal->is_tx_link_switch_event_supported(hal);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2020-2025 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2020-2026 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
@@ -52,9 +52,9 @@ typedef struct {
|
||||
typedef struct {
|
||||
// The bitmap of the IDs that can be used by M2M are different between AXI DMA and AHB DMA, so we need to save a copy for each of them
|
||||
uint32_t m2m_free_periph_mask;
|
||||
// TODO: we can add more private data here, e.g. the interrupt event mask of interest
|
||||
// for now, the AXI DMA and AHB DMA are sharing the same interrupt mask, so we don't need to store it here
|
||||
// If one day they become incompatible, we shall save a copy for each of them as a private data
|
||||
// Supported interrupt events can vary across DMA instances (e.g. AHB vs AXI)
|
||||
uint32_t tx_event_mask;
|
||||
uint32_t rx_event_mask;
|
||||
} gdma_hal_priv_data_t;
|
||||
|
||||
/**
|
||||
@@ -102,6 +102,8 @@ struct gdma_hal_context_t {
|
||||
#if SOC_GDMA_SUPPORT_WEIGHTED_ARBITRATION
|
||||
void (*set_weight)(gdma_hal_context_t *hal, int chan_id, gdma_channel_direction_t dir, uint32_t weight); /// Set the channel weight
|
||||
#endif // SOC_GDMA_SUPPORT_WEIGHTED_ARBITRATION
|
||||
bool (*is_tx_link_switch_event_supported)(gdma_hal_context_t *hal); /// Check if TX link-switch interrupt is supported
|
||||
void (*request_link_switch_event)(gdma_hal_context_t *hal, int chan_id, gdma_channel_direction_t dir); /// Raise the interrupt when tx link switch complete
|
||||
};
|
||||
|
||||
void gdma_hal_deinit(gdma_hal_context_t *hal);
|
||||
@@ -161,6 +163,10 @@ void gdma_hal_enable_etm_task(gdma_hal_context_t *hal, int chan_id, gdma_channel
|
||||
void gdma_hal_set_weight(gdma_hal_context_t *hal, int chan_id, gdma_channel_direction_t dir, uint32_t weight);
|
||||
#endif //SOC_GDMA_SUPPORT_WEIGHTED_ARBITRATION
|
||||
|
||||
void gdma_hal_request_link_switch_event(gdma_hal_context_t *hal, int chan_id, gdma_channel_direction_t dir);
|
||||
|
||||
bool gdma_hal_is_tx_link_switch_event_supported(gdma_hal_context_t *hal);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -12,38 +12,6 @@
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
void gdma_ahb_hal_start_with_desc(gdma_hal_context_t *hal, int chan_id, gdma_channel_direction_t dir, intptr_t desc_base_addr);
|
||||
|
||||
void gdma_ahb_hal_stop(gdma_hal_context_t *hal, int chan_id, gdma_channel_direction_t dir);
|
||||
|
||||
void gdma_ahb_hal_append(gdma_hal_context_t *hal, int chan_id, gdma_channel_direction_t dir);
|
||||
|
||||
void gdma_ahb_hal_reset(gdma_hal_context_t *hal, int chan_id, gdma_channel_direction_t dir);
|
||||
|
||||
void gdma_ahb_hal_set_priority(gdma_hal_context_t *hal, int chan_id, gdma_channel_direction_t dir, uint32_t priority);
|
||||
|
||||
void gdma_ahb_hal_connect_peri(gdma_hal_context_t *hal, int chan_id, gdma_channel_direction_t dir, int periph_id);
|
||||
|
||||
void gdma_ahb_hal_connect_mem(gdma_hal_context_t *hal, int chan_id, gdma_channel_direction_t dir, int dummy_id);
|
||||
|
||||
void gdma_ahb_hal_disconnect_all(gdma_hal_context_t *hal, int chan_id, gdma_channel_direction_t dir);
|
||||
|
||||
void gdma_ahb_hal_enable_burst(gdma_hal_context_t *hal, int chan_id, gdma_channel_direction_t dir, bool en_data_burst, bool en_desc_burst);
|
||||
|
||||
void gdma_ahb_hal_set_burst_size(gdma_hal_context_t *hal, int chan_id, gdma_channel_direction_t dir, uint32_t burst_sz);
|
||||
|
||||
void gdma_ahb_hal_set_strategy(gdma_hal_context_t *hal, int chan_id, gdma_channel_direction_t dir, bool en_owner_check, bool en_desc_write_back, bool eof_till_popped);
|
||||
|
||||
void gdma_ahb_hal_enable_intr(gdma_hal_context_t *hal, int chan_id, gdma_channel_direction_t dir, uint32_t intr_event_mask, bool en_or_dis);
|
||||
|
||||
void gdma_ahb_hal_clear_intr(gdma_hal_context_t *hal, int chan_id, gdma_channel_direction_t dir, uint32_t intr_event_mask);
|
||||
|
||||
uint32_t gdma_ahb_hal_read_intr_status(gdma_hal_context_t *hal, int chan_id, gdma_channel_direction_t dir, bool raw);
|
||||
|
||||
uint32_t gdma_ahb_hal_get_intr_status_reg(gdma_hal_context_t *hal, int chan_id, gdma_channel_direction_t dir);
|
||||
|
||||
uint32_t gdma_ahb_hal_get_eof_desc_addr(gdma_hal_context_t *hal, int chan_id, gdma_channel_direction_t dir, bool is_success);
|
||||
|
||||
void gdma_ahb_hal_init(gdma_hal_context_t *hal, const gdma_hal_config_t *config);
|
||||
|
||||
void gdma_lp_ahb_hal_init(gdma_hal_context_t *hal, const gdma_hal_config_t *config);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2020-2023 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2020-2026 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
@@ -12,38 +12,6 @@
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
void gdma_axi_hal_start_with_desc(gdma_hal_context_t *hal, int chan_id, gdma_channel_direction_t dir, intptr_t desc_base_addr);
|
||||
|
||||
void gdma_axi_hal_stop(gdma_hal_context_t *hal, int chan_id, gdma_channel_direction_t dir);
|
||||
|
||||
void gdma_axi_hal_append(gdma_hal_context_t *hal, int chan_id, gdma_channel_direction_t dir);
|
||||
|
||||
void gdma_axi_hal_reset(gdma_hal_context_t *hal, int chan_id, gdma_channel_direction_t dir);
|
||||
|
||||
void gdma_axi_hal_set_priority(gdma_hal_context_t *hal, int chan_id, gdma_channel_direction_t dir, uint32_t priority);
|
||||
|
||||
void gdma_axi_hal_connect_peri(gdma_hal_context_t *hal, int chan_id, gdma_channel_direction_t dir, int periph_id);
|
||||
|
||||
void gdma_axi_hal_connect_mem(gdma_hal_context_t *hal, int chan_id, gdma_channel_direction_t dir, int dummy_id);
|
||||
|
||||
void gdma_axi_hal_disconnect_all(gdma_hal_context_t *hal, int chan_id, gdma_channel_direction_t dir);
|
||||
|
||||
void gdma_axi_hal_enable_burst(gdma_hal_context_t *hal, int chan_id, gdma_channel_direction_t dir, bool en_data_burst, bool en_desc_burst);
|
||||
|
||||
void gdma_axi_hal_set_burst_size(gdma_hal_context_t *hal, int chan_id, gdma_channel_direction_t dir, uint32_t burst_sz);
|
||||
|
||||
void gdma_axi_hal_set_strategy(gdma_hal_context_t *hal, int chan_id, gdma_channel_direction_t dir, bool en_owner_check, bool en_desc_write_back, bool eof_till_popped);
|
||||
|
||||
void gdma_axi_hal_enable_intr(gdma_hal_context_t *hal, int chan_id, gdma_channel_direction_t dir, uint32_t intr_event_mask, bool en_or_dis);
|
||||
|
||||
void gdma_axi_hal_clear_intr(gdma_hal_context_t *hal, int chan_id, gdma_channel_direction_t dir, uint32_t intr_event_mask);
|
||||
|
||||
uint32_t gdma_axi_hal_read_intr_status(gdma_hal_context_t *hal, int chan_id, gdma_channel_direction_t dir, bool raw);
|
||||
|
||||
uint32_t gdma_axi_hal_get_intr_status_reg(gdma_hal_context_t *hal, int chan_id, gdma_channel_direction_t dir);
|
||||
|
||||
uint32_t gdma_axi_hal_get_eof_desc_addr(gdma_hal_context_t *hal, int chan_id, gdma_channel_direction_t dir, bool is_success);
|
||||
|
||||
void gdma_axi_hal_init(gdma_hal_context_t *hal, const gdma_hal_config_t *config);
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
||||
@@ -52,7 +52,8 @@ struct esp_lcd_dpi_panel_t {
|
||||
esp_pm_lock_handle_t pm_lock; // Power management lock
|
||||
#endif
|
||||
esp_lcd_dpi_panel_color_trans_done_cb_t on_color_trans_done; // Callback invoked when color data transfer has finished
|
||||
esp_lcd_dpi_panel_refresh_done_cb_t on_refresh_done; // Callback invoked when one refresh operation finished (kinda like a vsync end)
|
||||
esp_lcd_dpi_panel_frame_buf_complete_cb_t on_frame_buf_complete; // Callback invoked when the frame buffer can be reused safely
|
||||
esp_lcd_dpi_panel_vsync_cb_t on_vsync; // VSYNC event callback
|
||||
void *user_ctx; // User context for the callback
|
||||
};
|
||||
|
||||
@@ -102,10 +103,16 @@ bool mipi_dsi_dma_trans_done_cb(dw_gdma_channel_handle_t chan, const dw_gdma_tra
|
||||
dw_gdma_channel_use_link_list(chan, link_list);
|
||||
dw_gdma_channel_enable_ctrl(chan, true);
|
||||
|
||||
if (dpi_panel->on_frame_buf_complete) {
|
||||
if (dpi_panel->on_frame_buf_complete(&dpi_panel->base, NULL, dpi_panel->user_ctx)) {
|
||||
yield_needed = true;
|
||||
}
|
||||
}
|
||||
|
||||
#if !MIPI_DSI_BRG_LL_EVENT_VSYNC
|
||||
// the DMA descriptor is large enough to carry a whole frame buffer, so this event can also be treated as a fake "vsync end"
|
||||
if (dpi_panel->on_refresh_done) {
|
||||
if (dpi_panel->on_refresh_done(&dpi_panel->base, NULL, dpi_panel->user_ctx)) {
|
||||
if (dpi_panel->on_vsync) {
|
||||
if (dpi_panel->on_vsync(&dpi_panel->base, NULL, dpi_panel->user_ctx)) {
|
||||
yield_needed = true;
|
||||
}
|
||||
}
|
||||
@@ -128,8 +135,8 @@ void mipi_dsi_bridge_isr_handler(void *args)
|
||||
ESP_DRAM_LOGE(TAG, "can't fetch data from external memory fast enough, underrun happens");
|
||||
}
|
||||
if (intr_status & MIPI_DSI_BRG_LL_EVENT_VSYNC) {
|
||||
if (dpi_panel->on_refresh_done) {
|
||||
if (dpi_panel->on_refresh_done(&dpi_panel->base, NULL, dpi_panel->user_ctx)) {
|
||||
if (dpi_panel->on_vsync) {
|
||||
if (dpi_panel->on_vsync(&dpi_panel->base, NULL, dpi_panel->user_ctx)) {
|
||||
portYIELD_FROM_ISR();
|
||||
}
|
||||
}
|
||||
@@ -733,19 +740,23 @@ esp_err_t esp_lcd_dpi_panel_register_event_callbacks(esp_lcd_panel_handle_t pane
|
||||
if (cbs->on_color_trans_done) {
|
||||
ESP_RETURN_ON_FALSE(esp_ptr_in_iram(cbs->on_color_trans_done), ESP_ERR_INVALID_ARG, TAG, "on_color_trans_done callback not in IRAM");
|
||||
}
|
||||
if (cbs->on_refresh_done) {
|
||||
ESP_RETURN_ON_FALSE(esp_ptr_in_iram(cbs->on_refresh_done), ESP_ERR_INVALID_ARG, TAG, "on_refresh_done callback not in IRAM");
|
||||
if (cbs->on_vsync) {
|
||||
ESP_RETURN_ON_FALSE(esp_ptr_in_iram(cbs->on_vsync), ESP_ERR_INVALID_ARG, TAG, "on_vsync callback not in IRAM");
|
||||
}
|
||||
if (cbs->on_frame_buf_complete) {
|
||||
ESP_RETURN_ON_FALSE(esp_ptr_in_iram(cbs->on_frame_buf_complete), ESP_ERR_INVALID_ARG, TAG, "on_frame_buf_complete callback not in IRAM");
|
||||
}
|
||||
if (user_ctx) {
|
||||
ESP_RETURN_ON_FALSE(esp_ptr_internal(user_ctx), ESP_ERR_INVALID_ARG, TAG, "user context not in internal RAM");
|
||||
}
|
||||
#endif // CONFIG_LCD_DSI_ISR_CACHE_SAFE
|
||||
dpi_panel->on_color_trans_done = cbs->on_color_trans_done;
|
||||
dpi_panel->on_refresh_done = cbs->on_refresh_done;
|
||||
dpi_panel->on_vsync = cbs->on_vsync;
|
||||
dpi_panel->on_frame_buf_complete = cbs->on_frame_buf_complete;
|
||||
dpi_panel->user_ctx = user_ctx;
|
||||
|
||||
// enable the vsync interrupt if the callback is provided
|
||||
mipi_dsi_brg_ll_enable_interrupt(dpi_panel->bus->hal.bridge, MIPI_DSI_BRG_LL_EVENT_VSYNC, cbs->on_refresh_done != NULL);
|
||||
mipi_dsi_brg_ll_enable_interrupt(dpi_panel->bus->hal.bridge, MIPI_DSI_BRG_LL_EVENT_VSYNC, cbs->on_vsync != NULL);
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2023-2025 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2023-2026 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
@@ -177,18 +177,36 @@ typedef esp_lcd_dpi_panel_general_cb_t esp_lcd_dpi_panel_color_trans_done_cb_t;
|
||||
|
||||
/**
|
||||
* @brief Declare the prototype of the function that will be invoked
|
||||
* when driver finishes refreshing the frame buffer to the screen
|
||||
* when the frame buffer can be reused safely
|
||||
*
|
||||
* @deprecated Use esp_lcd_dpi_panel_frame_buf_complete_cb_t instead.
|
||||
*/
|
||||
typedef esp_lcd_dpi_panel_general_cb_t esp_lcd_dpi_panel_refresh_done_cb_t;
|
||||
|
||||
/**
|
||||
* @brief Declare the prototype of the function that will be invoked when the LCD controller sends the VSYNC signal.
|
||||
*/
|
||||
typedef esp_lcd_dpi_panel_general_cb_t esp_lcd_dpi_panel_vsync_cb_t;
|
||||
|
||||
/**
|
||||
* @brief Declare the prototype of the function that will be invoked
|
||||
* when the frame buffer can be reused safely
|
||||
*/
|
||||
typedef esp_lcd_dpi_panel_general_cb_t esp_lcd_dpi_panel_frame_buf_complete_cb_t;
|
||||
|
||||
/**
|
||||
* @brief Type of LCD DPI panel callbacks
|
||||
*/
|
||||
typedef struct {
|
||||
esp_lcd_dpi_panel_color_trans_done_cb_t on_color_trans_done; /*!< Invoked when user's color buffer copied to the internal frame buffer.
|
||||
esp_lcd_dpi_panel_color_trans_done_cb_t on_color_trans_done; /*!< Invoked when user's draw buffer copied to the frame buffer.
|
||||
This is an indicator that the draw buffer can be recycled safely.
|
||||
But doesn't mean the draw buffer finishes the refreshing to the screen. */
|
||||
esp_lcd_dpi_panel_refresh_done_cb_t on_refresh_done; /*!< Invoked when the internal frame buffer finishes refreshing to the screen */
|
||||
union {
|
||||
esp_lcd_dpi_panel_refresh_done_cb_t on_refresh_done __attribute__((deprecated("Deprecated, use on_frame_buf_complete instead"))); /*!< Deprecated, use on_frame_buf_complete instead */
|
||||
esp_lcd_dpi_panel_frame_buf_complete_cb_t on_frame_buf_complete; /*!< Invoked when the frame buffer can be reused safely
|
||||
when the frame buffer is the draw buffer. */
|
||||
};
|
||||
esp_lcd_dpi_panel_vsync_cb_t on_vsync; /*!< VSYNC event callback */
|
||||
} esp_lcd_dpi_panel_event_callbacks_t;
|
||||
|
||||
/**
|
||||
|
||||
@@ -49,6 +49,14 @@
|
||||
#include "rgb_lcd_rotation_sw.h"
|
||||
#include "esp_private/sleep_retention.h"
|
||||
|
||||
#if SOC_HAS(AXI_DMA)
|
||||
#include "hal/axi_dma_ll.h"
|
||||
#endif
|
||||
|
||||
#if AXI_DMA_LL_SUPPORT_TX_LINK_SWITCH_EVENT
|
||||
#define RGB_LCD_USE_GDMA_LINK_SWITCH_EVENT 1
|
||||
#endif
|
||||
|
||||
// hardware issue workaround
|
||||
#if CONFIG_IDF_TARGET_ESP32S3
|
||||
#define RGB_LCD_NEEDS_SEPARATE_RESTART_LINK 1
|
||||
@@ -99,6 +107,9 @@ static esp_err_t lcd_rgb_panel_configure_gpio_matrix(const esp_lcd_rgb_panel_con
|
||||
static bool lcd_rgb_panel_can_use_iomux(const esp_lcd_rgb_panel_config_t *panel_config, const soc_lcd_rgb_iomux_desc_t *iomux_desc);
|
||||
static esp_err_t lcd_rgb_panel_configure_iomux(const esp_lcd_rgb_panel_config_t *panel_config, const soc_lcd_rgb_iomux_desc_t *iomux_desc, uint64_t *gpio_reserve_mask);
|
||||
#endif // LCD_LL_SUPPORT(IOMUX)
|
||||
#if RGB_LCD_USE_GDMA_LINK_SWITCH_EVENT
|
||||
static bool lcd_rgb_panel_link_switch_handler(gdma_channel_handle_t dma_chan, gdma_event_data_t *event_data, void *user_data);
|
||||
#endif // RGB_LCD_USE_GDMA_LINK_SWITCH_EVENT
|
||||
|
||||
struct esp_rgb_panel_t {
|
||||
esp_lcd_panel_t base; // Base class of generic lcd panel
|
||||
@@ -143,7 +154,7 @@ struct esp_rgb_panel_t {
|
||||
size_t bb_eof_count; // record the number we received the DMA EOF event, compare with `expect_eof_count` in the VSYNC_END ISR
|
||||
size_t expect_eof_count; // record the number of DMA EOF event we expected to receive
|
||||
esp_lcd_rgb_panel_draw_buf_complete_cb_t on_color_trans_done; // draw buffer completes
|
||||
esp_lcd_rgb_panel_frame_buf_complete_cb_t on_frame_buf_complete; // callback used to notify when the bounce buffer finish copying the entire frame
|
||||
esp_lcd_rgb_panel_frame_buf_complete_cb_t on_frame_buf_complete; // callback used to notify when the buffer can be reused safely
|
||||
esp_lcd_rgb_panel_vsync_cb_t on_vsync; // VSYNC event callback
|
||||
esp_lcd_rgb_panel_bounce_buf_fill_cb_t on_bounce_empty; // callback used to fill a bounce buffer rather than copying from the frame buffer
|
||||
void *user_ctx; // Reserved user's data of callback functions
|
||||
@@ -760,7 +771,9 @@ static esp_err_t rgb_panel_draw_bitmap(esp_lcd_panel_t *panel, int x_start, int
|
||||
// it's hard to know the time when the new frame buffer starts
|
||||
gdma_link_concat(rgb_panel->dma_fb_links[i], -1, rgb_panel->dma_fb_links[rgb_panel->cur_fb_index], 0);
|
||||
}
|
||||
|
||||
#if RGB_LCD_USE_GDMA_LINK_SWITCH_EVENT
|
||||
ESP_RETURN_ON_ERROR(gdma_request_link_switch_event(rgb_panel->dma_chan), TAG, "request link switch event failed");
|
||||
#endif // RGB_LCD_USE_GDMA_LINK_SWITCH_EVENT
|
||||
}
|
||||
}
|
||||
return ESP_OK;
|
||||
@@ -1061,7 +1074,7 @@ static IRAM_ATTR bool lcd_rgb_panel_eof_handler(gdma_channel_handle_t dma_chan,
|
||||
portEXIT_CRITICAL_ISR(&rgb_panel->spinlock);
|
||||
need_yield = lcd_rgb_panel_fill_bounce_buffer(rgb_panel, rgb_panel->bounce_buffer[bb]);
|
||||
} else {
|
||||
// if not bounce buffer, the DMA EOF event means the end of a frame has been sent out to the LCD controller
|
||||
// Once the preload has already done, the buffer complete callback is not reliable.
|
||||
if (rgb_panel->on_frame_buf_complete) {
|
||||
if (rgb_panel->on_frame_buf_complete(&rgb_panel->base, NULL, rgb_panel->user_ctx)) {
|
||||
need_yield = true;
|
||||
@@ -1071,6 +1084,24 @@ static IRAM_ATTR bool lcd_rgb_panel_eof_handler(gdma_channel_handle_t dma_chan,
|
||||
return need_yield;
|
||||
}
|
||||
|
||||
#if RGB_LCD_USE_GDMA_LINK_SWITCH_EVENT
|
||||
static IRAM_ATTR bool lcd_rgb_panel_link_switch_handler(gdma_channel_handle_t dma_chan, gdma_event_data_t *event_data, void *user_data)
|
||||
{
|
||||
(void)dma_chan;
|
||||
(void)event_data;
|
||||
bool need_yield = false;
|
||||
esp_rgb_panel_t *rgb_panel = (esp_rgb_panel_t *)user_data;
|
||||
|
||||
if (rgb_panel->on_frame_buf_complete) {
|
||||
if (rgb_panel->on_frame_buf_complete(&rgb_panel->base, NULL, rgb_panel->user_ctx)) {
|
||||
need_yield = true;
|
||||
}
|
||||
}
|
||||
|
||||
return need_yield;
|
||||
}
|
||||
#endif // RGB_LCD_USE_GDMA_LINK_SWITCH_EVENT
|
||||
|
||||
static esp_err_t lcd_rgb_create_dma_channel(esp_rgb_panel_t *rgb_panel)
|
||||
{
|
||||
// alloc DMA channel and connect to LCD peripheral
|
||||
@@ -1097,11 +1128,19 @@ static esp_err_t lcd_rgb_create_dma_channel(esp_rgb_panel_t *rgb_panel)
|
||||
// get the memory alignment required by the DMA
|
||||
gdma_get_alignment_constraints(rgb_panel->dma_chan, &rgb_panel->int_mem_align, &rgb_panel->ext_mem_align);
|
||||
|
||||
// register DMA EOF callback
|
||||
// register DMA event callbacks
|
||||
gdma_tx_event_callbacks_t cbs = {
|
||||
#if RGB_LCD_USE_GDMA_LINK_SWITCH_EVENT
|
||||
// if no bounce buffer, the DMA EOF event means the end of a frame has been sent out to the LCD controller.
|
||||
// But the dma link may have preloaded the next frame with the current buffer.
|
||||
// So we need to wait for the GDMA link switch event to invoke the on_frame_buf_complete callback.
|
||||
.on_trans_eof = (rgb_panel->flags.stream_mode && !rgb_panel->bb_size) ? NULL : lcd_rgb_panel_eof_handler,
|
||||
.on_link_switch = lcd_rgb_panel_link_switch_handler,
|
||||
#else
|
||||
.on_trans_eof = lcd_rgb_panel_eof_handler,
|
||||
#endif // RGB_LCD_USE_GDMA_LINK_SWITCH_EVENT
|
||||
};
|
||||
ESP_RETURN_ON_ERROR(gdma_register_tx_event_callbacks(rgb_panel->dma_chan, &cbs, rgb_panel), TAG, "register DMA EOF callback failed");
|
||||
ESP_RETURN_ON_ERROR(gdma_register_tx_event_callbacks(rgb_panel->dma_chan, &cbs, rgb_panel), TAG, "register DMA event callbacks failed");
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
@@ -97,7 +97,7 @@ typedef bool (*esp_lcd_rgb_panel_general_cb_t)(esp_lcd_panel_handle_t panel, con
|
||||
typedef esp_lcd_rgb_panel_general_cb_t esp_lcd_rgb_panel_draw_buf_complete_cb_t;
|
||||
|
||||
/**
|
||||
* @brief Declare the prototype of the function that will be invoked when a whole frame buffer is sent to the LCD DMA.
|
||||
* @brief Declare the prototype of the function that will be invoked when a whole frame buffer can be reused safely.
|
||||
* The LCD hardware may still need some blank time to finish the refresh.
|
||||
*/
|
||||
typedef esp_lcd_rgb_panel_general_cb_t esp_lcd_rgb_panel_frame_buf_complete_cb_t;
|
||||
@@ -132,7 +132,7 @@ typedef struct {
|
||||
But doesn't mean the draw buffer finishes the refreshing to the screen. */
|
||||
esp_lcd_rgb_panel_vsync_cb_t on_vsync; /*!< VSYNC event callback */
|
||||
esp_lcd_rgb_panel_bounce_buf_fill_cb_t on_bounce_empty; /*!< Bounce buffer empty callback. */
|
||||
esp_lcd_rgb_panel_frame_buf_complete_cb_t on_frame_buf_complete; /*!< A whole frame buffer was just sent to the LCD DMA */
|
||||
esp_lcd_rgb_panel_frame_buf_complete_cb_t on_frame_buf_complete; /*!< Invoked when the frame buffer can be reused safely */
|
||||
} esp_lcd_rgb_panel_event_callbacks_t;
|
||||
|
||||
/**
|
||||
|
||||
@@ -91,7 +91,7 @@ TEST_CASE("MIPI DSI draw bitmap (EK79007) IRAM Safe", "[mipi_dsi]")
|
||||
|
||||
uint32_t callback_calls = 0;
|
||||
esp_lcd_dpi_panel_event_callbacks_t cbs = {
|
||||
.on_refresh_done = test_dpi_panel_count_in_callback,
|
||||
.on_frame_buf_complete = test_dpi_panel_count_in_callback,
|
||||
};
|
||||
TEST_ESP_OK(esp_lcd_dpi_panel_register_event_callbacks(mipi_dpi_panel, &cbs, &callback_calls));
|
||||
|
||||
|
||||
@@ -648,7 +648,12 @@ typedef union {
|
||||
* underflow.
|
||||
*/
|
||||
uint32_t outfifo_l3_udf_chn_int_raw: 1;
|
||||
uint32_t reserved_10: 22;
|
||||
/** out_link_switch_chn_int_raw : R/WTC/SS; bitpos: [10]; default: 0;
|
||||
* The raw interrupt bit turns to high level when the dma switch to new link for Tx
|
||||
* channel0.
|
||||
*/
|
||||
uint32_t out_link_switch_chn_int_raw: 1;
|
||||
uint32_t reserved_11: 21;
|
||||
};
|
||||
uint32_t val;
|
||||
} axi_dma_out_int_raw_chn_reg_t;
|
||||
@@ -698,7 +703,11 @@ typedef union {
|
||||
* The raw interrupt status bit for the OUTFIFO_UDF_L3_CH_INT interrupt.
|
||||
*/
|
||||
uint32_t outfifo_l3_udf_chn_int_st: 1;
|
||||
uint32_t reserved_10: 22;
|
||||
/** out_link_switch_chn_int_st : RO; bitpos: [10]; default: 0;
|
||||
* The raw interrupt status bit for the OUT_LINK_SWITCH_CH_INT interrupt.
|
||||
*/
|
||||
uint32_t out_link_switch_chn_int_st: 1;
|
||||
uint32_t reserved_11: 21;
|
||||
};
|
||||
uint32_t val;
|
||||
} axi_dma_out_int_st_chn_reg_t;
|
||||
@@ -748,7 +757,11 @@ typedef union {
|
||||
* The interrupt enable bit for the OUTFIFO_UDF_L3_CH_INT interrupt.
|
||||
*/
|
||||
uint32_t outfifo_l3_udf_chn_int_ena: 1;
|
||||
uint32_t reserved_10: 22;
|
||||
/** out_link_switch_chn_int_ena : R/W; bitpos: [10]; default: 0;
|
||||
* The interrupt enable bit for the OUT_LINK_SWITCH_CH_INT interrupt.
|
||||
*/
|
||||
uint32_t out_link_switch_chn_int_ena: 1;
|
||||
uint32_t reserved_11: 21;
|
||||
};
|
||||
uint32_t val;
|
||||
} axi_dma_out_int_ena_chn_reg_t;
|
||||
@@ -798,7 +811,11 @@ typedef union {
|
||||
* Set this bit to clear the OUTFIFO_UDF_L3_CH_INT interrupt.
|
||||
*/
|
||||
uint32_t outfifo_l3_udf_chn_int_clr: 1;
|
||||
uint32_t reserved_10: 22;
|
||||
/** out_link_switch_chn_int_clr : WT; bitpos: [10]; default: 0;
|
||||
* Set this bit to clear the OUT_LINK_SWITCH_CH_INT interrupt.
|
||||
*/
|
||||
uint32_t out_link_switch_chn_int_clr: 1;
|
||||
uint32_t reserved_11: 21;
|
||||
};
|
||||
uint32_t val;
|
||||
} axi_dma_out_int_clr_chn_reg_t;
|
||||
|
||||
Reference in New Issue
Block a user