mirror of
https://github.com/espressif/esp-idf.git
synced 2024-10-05 20:47:46 -04:00
feat(esp_hw_support/dma2d): Add 2D-DMA support on ESP32P4
This commit is contained in:
parent
a4268b88c5
commit
08eada7301
@ -97,6 +97,10 @@ if(NOT BOOTLOADER_BUILD)
|
||||
list(APPEND srcs "dma/dw_gdma.c")
|
||||
endif()
|
||||
|
||||
if(CONFIG_SOC_DMA2D_SUPPORTED)
|
||||
list(APPEND srcs "dma/dma2d.c")
|
||||
endif()
|
||||
|
||||
if(CONFIG_SOC_SYSTIMER_SUPPORTED)
|
||||
list(APPEND srcs "port/${target}/systimer.c")
|
||||
endif()
|
||||
|
@ -348,7 +348,6 @@ menu "Hardware Settings"
|
||||
clock support isn't done yet. So with this option,
|
||||
we use xtal on FPGA as the clock source.
|
||||
|
||||
# Invisible bringup bypass options for esp_hw_support component
|
||||
config ESP_BRINGUP_BYPASS_RANDOM_SETTING
|
||||
bool
|
||||
default y if !SOC_RNG_SUPPORTED
|
||||
|
@ -55,3 +55,23 @@ menu "DW_GDMA Configurations"
|
||||
Wether to enable the debug log message for DW_GDMA driver.
|
||||
Note that, this option only controls the DW_GDMA driver log, won't affect other drivers.
|
||||
endmenu # DW_GDMA Configurations
|
||||
|
||||
menu "2D-DMA Configurations"
|
||||
depends on SOC_DMA2D_SUPPORTED
|
||||
|
||||
config DMA2D_OPERATION_FUNC_IN_IRAM
|
||||
bool "Place 2D-DMA operation functions into IRAM"
|
||||
default n
|
||||
help
|
||||
Place 2D-DMA all operation functions, including control functions (e.g. start/stop/append/reset) and setter
|
||||
functions (e.g. connect/strategy/callback registration) into IRAM, so that these functions can be IRAM-safe
|
||||
and able to be called in the other IRAM interrupt context. It also helps optimizing the performance.
|
||||
|
||||
config DMA2D_ISR_IRAM_SAFE
|
||||
bool "2D-DMA ISR IRAM-Safe"
|
||||
default n
|
||||
help
|
||||
This will ensure the 2D-DMA interrupt handler is IRAM-Safe, allow to avoid flash
|
||||
cache misses, and also be able to run whilst the cache is disabled.
|
||||
(e.g. SPI Flash write).
|
||||
endmenu # 2D-DMA Configurations
|
||||
|
979
components/esp_hw_support/dma/dma2d.c
Normal file
979
components/esp_hw_support/dma/dma2d.c
Normal file
@ -0,0 +1,979 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <sys/queue.h>
|
||||
#include <sys/lock.h>
|
||||
#include "esp_check.h"
|
||||
#include "freertos/portmacro.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "esp_intr_alloc.h"
|
||||
#include "esp_heap_caps.h"
|
||||
#include "esp_memory_utils.h"
|
||||
#include "esp_attr.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_private/periph_ctrl.h"
|
||||
#include "dma2d_priv.h"
|
||||
#include "esp_private/dma2d.h"
|
||||
#include "hal/dma2d_hal.h"
|
||||
#include "hal/dma2d_ll.h"
|
||||
#include "soc/dma2d_channel.h"
|
||||
#include "soc/dma2d_periph.h"
|
||||
#include "soc/soc_caps.h"
|
||||
#include "esp_bit_defs.h"
|
||||
|
||||
/**
|
||||
* The 2D-DMA driver is designed with a pool & client model + queue design pattern.
|
||||
*
|
||||
* Pools represents the groups of 2D-DMA module, which contain the limited resource, channels.
|
||||
* Clients represents the upper modules which are the consumers of the 2D-DMA channels, such as JPEG and PPA.
|
||||
*
|
||||
* Each pool has a queue to store the 2D-DMA transactions that are waiting to be processed.
|
||||
*
|
||||
* The upper modules should register themselves as the clients to a 2D-DMA pool. And then they should push the
|
||||
* 2D-DMA transactions into the pool queue. The driver will continuously look for the desired resources from the pool to
|
||||
* complete the transactions.
|
||||
*/
|
||||
|
||||
static const char *TAG = "dma2d";
|
||||
|
||||
typedef struct dma2d_platform_t {
|
||||
_lock_t mutex; // platform level mutex lock to protect the dma2d_acquire_pool/dma2d_release_pool process
|
||||
dma2d_group_t *groups[SOC_DMA2D_GROUPS]; // array of 2D-DMA group instances
|
||||
int group_ref_counts[SOC_DMA2D_GROUPS]; // reference count used to protect group install/uninstall
|
||||
} dma2d_platform_t;
|
||||
|
||||
// 2D-DMA driver platform
|
||||
static dma2d_platform_t s_platform = {
|
||||
.groups = {},
|
||||
};
|
||||
|
||||
// extern 2D-DMA channel reserved mask variables to be ORed in the constructors
|
||||
uint32_t dma2d_tx_channel_reserved_mask[SOC_DMA2D_GROUPS] = { [0 ... SOC_DMA2D_GROUPS - 1] = 0 };
|
||||
uint32_t dma2d_rx_channel_reserved_mask[SOC_DMA2D_GROUPS] = { [0 ... SOC_DMA2D_GROUPS - 1] = 0 };
|
||||
|
||||
// The most number of channels required for a 2D-DMA transaction (a PPA Blend operation requires 2 TX + 1 RX)
|
||||
#define DMA2D_MAX_CHANNEL_NUM_PER_TRANSACTION 3
|
||||
|
||||
|
||||
/* This static function is not thread-safe, group's spinlock protection should be added in its caller */
|
||||
static bool acquire_free_channels_for_trans(dma2d_group_t *dma2d_group, const dma2d_trans_config_t *trans_desc, dma2d_trans_channel_info_t *channel_handle_array)
|
||||
{
|
||||
bool found = true;
|
||||
uint32_t idx = 0;
|
||||
uint32_t bundled_tx_channel_mask = 0;
|
||||
|
||||
if (trans_desc->tx_channel_num > 0) {
|
||||
uint32_t tx_free_channel_mask;
|
||||
if (!trans_desc->specified_tx_channel_mask) {
|
||||
tx_free_channel_mask = dma2d_group->tx_channel_free_mask;
|
||||
tx_free_channel_mask &= (((trans_desc->channel_flags & DMA2D_CHANNEL_FUNCTION_FLAG_TX_REORDER) ? DMA2D_LL_TX_CHANNEL_SUPPORT_RO_MASK : UINT32_MAX) &
|
||||
((trans_desc->channel_flags & DMA2D_CHANNEL_FUNCTION_FLAG_TX_CSC) ? DMA2D_LL_TX_CHANNEL_SUPPORT_CSC_MASK : UINT32_MAX));
|
||||
tx_free_channel_mask &= ~dma2d_group->tx_channel_reserved_mask;
|
||||
if (trans_desc->channel_flags & DMA2D_CHANNEL_FUNCTION_FLAG_SIBLING) {
|
||||
uint32_t rx_channel_candidate = dma2d_group->rx_channel_free_mask &
|
||||
((trans_desc->channel_flags & DMA2D_CHANNEL_FUNCTION_FLAG_RX_REORDER) ? DMA2D_LL_RX_CHANNEL_SUPPORT_RO_MASK : UINT32_MAX) &
|
||||
((trans_desc->channel_flags & DMA2D_CHANNEL_FUNCTION_FLAG_RX_CSC) ? DMA2D_LL_RX_CHANNEL_SUPPORT_CSC_MASK : UINT32_MAX) &
|
||||
~dma2d_group->rx_channel_reserved_mask;
|
||||
tx_free_channel_mask &= rx_channel_candidate;
|
||||
}
|
||||
// As long as __builtin_popcount(tx_free_channel_mask) >= trans_desc->tx_channel_num, it can meet the criteria of "found"
|
||||
} else {
|
||||
tx_free_channel_mask = trans_desc->specified_tx_channel_mask & dma2d_group->tx_channel_free_mask;
|
||||
// tx_free_channel_mask need to be exactly equal to trans_desc->specified_tx_channel_mask to meet the criteria of "found"
|
||||
}
|
||||
|
||||
for (int i = 0; i < trans_desc->tx_channel_num; i++) {
|
||||
if (tx_free_channel_mask) {
|
||||
int channel_id = 31 - __builtin_clz(tx_free_channel_mask); // channel 0 has the most features, acquire other channels first if possible
|
||||
tx_free_channel_mask &= ~(1 << channel_id);
|
||||
dma2d_group->tx_channel_free_mask &= ~(1 << channel_id);
|
||||
bundled_tx_channel_mask |= (1 << channel_id);
|
||||
// Record channel status
|
||||
memset(&dma2d_group->tx_chans[channel_id]->base.status, 0, sizeof(dma2d_group->tx_chans[channel_id]->base.status));
|
||||
dma2d_group->tx_chans[channel_id]->base.status.periph_sel_id = -1;
|
||||
if (trans_desc->channel_flags & DMA2D_CHANNEL_FUNCTION_FLAG_TX_REORDER) {
|
||||
dma2d_group->tx_chans[channel_id]->base.status.reorder_en = true;
|
||||
} else if (trans_desc->channel_flags & DMA2D_CHANNEL_FUNCTION_FLAG_SIBLING) {
|
||||
dma2d_group->tx_chans[channel_id]->base.status.has_sibling = true;
|
||||
}
|
||||
channel_handle_array[idx].chan = &dma2d_group->tx_chans[channel_id]->base;
|
||||
channel_handle_array[idx].dir = DMA2D_CHANNEL_DIRECTION_TX;
|
||||
idx++;
|
||||
} else {
|
||||
found = false;
|
||||
goto revert;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (trans_desc->rx_channel_num > 0) {
|
||||
uint32_t rx_free_channel_mask;
|
||||
if (trans_desc->specified_rx_channel_mask) {
|
||||
rx_free_channel_mask = trans_desc->specified_rx_channel_mask & dma2d_group->rx_channel_free_mask;
|
||||
// rx_free_channel_mask need to be exactly equal to trans_desc->specified_rx_channel_mask to meet the criteria of "found"
|
||||
} else if (trans_desc->channel_flags & DMA2D_CHANNEL_FUNCTION_FLAG_SIBLING) {
|
||||
// rx channel has already been determined
|
||||
rx_free_channel_mask = bundled_tx_channel_mask;
|
||||
} else {
|
||||
rx_free_channel_mask = dma2d_group->rx_channel_free_mask;
|
||||
rx_free_channel_mask &= (((trans_desc->channel_flags & DMA2D_CHANNEL_FUNCTION_FLAG_RX_REORDER) ? DMA2D_LL_RX_CHANNEL_SUPPORT_RO_MASK : UINT32_MAX) &
|
||||
((trans_desc->channel_flags & DMA2D_CHANNEL_FUNCTION_FLAG_RX_CSC) ? DMA2D_LL_RX_CHANNEL_SUPPORT_CSC_MASK : UINT32_MAX));
|
||||
rx_free_channel_mask &= ~dma2d_group->rx_channel_reserved_mask;
|
||||
// As long as __builtin_popcount(rx_free_channel_mask) >= trans_desc->rx_channel_num, it can meet the criteria of "found"
|
||||
}
|
||||
|
||||
// Requires one RX channel at most, no need a for loop
|
||||
if (rx_free_channel_mask) {
|
||||
int channel_id = 31 - __builtin_clz(rx_free_channel_mask); // channel 0 has full features, acquire other channels first if possible
|
||||
rx_free_channel_mask &= ~(1 << channel_id);
|
||||
dma2d_group->rx_channel_free_mask &= ~(1 << channel_id);
|
||||
// Record channel status
|
||||
memset(&dma2d_group->rx_chans[channel_id]->base.status, 0, sizeof(dma2d_group->rx_chans[channel_id]->base.status));
|
||||
dma2d_group->rx_chans[channel_id]->base.status.periph_sel_id = -1;
|
||||
if (trans_desc->channel_flags & DMA2D_CHANNEL_FUNCTION_FLAG_RX_REORDER) {
|
||||
dma2d_group->rx_chans[channel_id]->base.status.reorder_en = true;
|
||||
} else if (trans_desc->channel_flags & DMA2D_CHANNEL_FUNCTION_FLAG_SIBLING) {
|
||||
dma2d_group->rx_chans[channel_id]->base.status.has_sibling = true;
|
||||
}
|
||||
channel_handle_array[idx].chan = &dma2d_group->rx_chans[channel_id]->base;
|
||||
channel_handle_array[idx].dir = DMA2D_CHANNEL_DIRECTION_RX;
|
||||
idx++;
|
||||
|
||||
// Record its bundled TX channels, to be freed in the isr
|
||||
dma2d_rx_channel_t *rx_chan = dma2d_group->rx_chans[channel_id];
|
||||
portENTER_CRITICAL_SAFE(&rx_chan->base.spinlock);
|
||||
rx_chan->bundled_tx_channel_mask = bundled_tx_channel_mask;
|
||||
portEXIT_CRITICAL_SAFE(&rx_chan->base.spinlock);
|
||||
} else {
|
||||
found = false;
|
||||
goto revert;
|
||||
}
|
||||
}
|
||||
|
||||
revert:
|
||||
if (!found) {
|
||||
for (int i = 0; i < idx; i++) {
|
||||
int free_channel_mask = (1 << channel_handle_array[i].chan->channel_id);
|
||||
if (channel_handle_array[i].dir == DMA2D_CHANNEL_DIRECTION_TX) {
|
||||
dma2d_group->tx_channel_free_mask |= free_channel_mask;
|
||||
} else {
|
||||
dma2d_group->rx_channel_free_mask |= free_channel_mask;
|
||||
}
|
||||
}
|
||||
}
|
||||
return found;
|
||||
}
|
||||
|
||||
/* This function will free up the RX channel and its bundled TX channels, then check for whether there is next transaction to be picked up */
|
||||
static bool free_up_channels(dma2d_group_t *group, dma2d_rx_channel_t *rx_chan)
|
||||
{
|
||||
bool need_yield = false;
|
||||
uint32_t channel_id = rx_chan->base.channel_id;
|
||||
// 1. Clean up channels
|
||||
uint32_t bundled_tx_channel_mask = rx_chan->bundled_tx_channel_mask;
|
||||
// Disable RX channel interrupt
|
||||
portENTER_CRITICAL_SAFE(&rx_chan->base.spinlock);
|
||||
dma2d_ll_rx_enable_interrupt(group->hal.dev, channel_id, UINT32_MAX, false);
|
||||
// Reset RX channel event related pointers and flags
|
||||
rx_chan->on_recv_eof = NULL;
|
||||
rx_chan->on_desc_done = NULL;
|
||||
// Disconnect RX channel from the peripheral
|
||||
dma2d_ll_rx_disconnect_from_periph(group->hal.dev, channel_id);
|
||||
// Clear the pointer that points to the finished transaction
|
||||
rx_chan->base.status.transaction = NULL;
|
||||
portEXIT_CRITICAL_SAFE(&rx_chan->base.spinlock);
|
||||
// For every bundled TX channels:
|
||||
while (rx_chan->bundled_tx_channel_mask) {
|
||||
uint32_t nbit = __builtin_ffs(rx_chan->bundled_tx_channel_mask) - 1;
|
||||
rx_chan->bundled_tx_channel_mask &= ~(1 << nbit);
|
||||
dma2d_tx_channel_t *tx_chan = group->tx_chans[nbit];
|
||||
// Disable TX channel interrupt
|
||||
portENTER_CRITICAL_SAFE(&tx_chan->base.spinlock);
|
||||
dma2d_ll_tx_enable_interrupt(group->hal.dev, nbit, UINT32_MAX, false);
|
||||
// Reset TX channel event related pointers
|
||||
tx_chan->on_desc_done = NULL;
|
||||
// Disconnect TX channel from the peripheral
|
||||
dma2d_ll_tx_disconnect_from_periph(group->hal.dev, nbit);
|
||||
// Clear the pointer that points to the finished transaction
|
||||
tx_chan->base.status.transaction = NULL;
|
||||
portEXIT_CRITICAL_SAFE(&tx_chan->base.spinlock);
|
||||
}
|
||||
// Channel functionality flags will be reset and assigned new values inside `acquire_free_channels_for_trans`
|
||||
// Channel reset will always be done at `dma2d_connect` (i.e. when the channel is selected for a new transaction)
|
||||
|
||||
// 2. Check if next pending transaction in the tailq can start
|
||||
bool channels_found = false;
|
||||
const dma2d_trans_config_t *next_trans = NULL;
|
||||
dma2d_trans_channel_info_t channel_handle_array[DMA2D_MAX_CHANNEL_NUM_PER_TRANSACTION];
|
||||
|
||||
portENTER_CRITICAL_SAFE(&group->spinlock);
|
||||
group->tx_channel_free_mask |= bundled_tx_channel_mask;
|
||||
group->rx_channel_free_mask |= (1 << channel_id);
|
||||
int rx_periph_sel_id = group->rx_chans[channel_id]->base.status.periph_sel_id;
|
||||
if (rx_periph_sel_id != -1 && ((1 << rx_periph_sel_id) & DMA2D_LL_CHANNEL_PERIPH_M2M_FREE_ID_MASK)) {
|
||||
group->periph_m2m_free_id_mask |= (1 << rx_periph_sel_id); // release m2m periph_sel_id
|
||||
}
|
||||
|
||||
dma2d_trans_t *next_trans_elm = TAILQ_FIRST(&group->pending_trans_tailq);
|
||||
if (next_trans_elm) {
|
||||
// There is a pending transaction
|
||||
next_trans = next_trans_elm->desc;
|
||||
channels_found = acquire_free_channels_for_trans(group, next_trans, channel_handle_array);
|
||||
}
|
||||
|
||||
if (channels_found) {
|
||||
TAILQ_REMOVE(&group->pending_trans_tailq, next_trans_elm, entry);
|
||||
}
|
||||
portEXIT_CRITICAL_SAFE(&group->spinlock);
|
||||
|
||||
if (channels_found) {
|
||||
// If the transaction can be processed, let consumer handle the transaction
|
||||
uint32_t total_channel_num = next_trans->tx_channel_num + next_trans->rx_channel_num;
|
||||
// Store the acquired rx_chan into trans_elm (dma2d_trans_t) in case upper driver later need it to call `dma2d_force_end`
|
||||
// Upper driver controls the life cycle of trans_elm
|
||||
for (int i = 0; i < total_channel_num; i++) {
|
||||
if (channel_handle_array[i].dir == DMA2D_CHANNEL_DIRECTION_RX) {
|
||||
next_trans_elm->rx_chan = channel_handle_array[i].chan;
|
||||
}
|
||||
// Also save the transaction pointer
|
||||
channel_handle_array[i].chan->status.transaction = next_trans_elm;
|
||||
}
|
||||
need_yield |= next_trans->on_job_picked(total_channel_num, channel_handle_array, next_trans->user_config);
|
||||
}
|
||||
return need_yield;
|
||||
}
|
||||
|
||||
static NOINLINE_ATTR bool _dma2d_default_tx_isr(dma2d_group_t *group, int channel_id)
|
||||
{
|
||||
bool need_yield = false;
|
||||
dma2d_tx_channel_t *tx_chan = group->tx_chans[channel_id];
|
||||
dma2d_event_data_t edata = {
|
||||
.transaction = tx_chan->base.status.transaction,
|
||||
};
|
||||
// Clear pending interrupt event
|
||||
uint32_t intr_status = dma2d_ll_tx_get_interrupt_status(group->hal.dev, channel_id);
|
||||
dma2d_ll_tx_clear_interrupt_status(group->hal.dev, channel_id, intr_status);
|
||||
|
||||
// Handle callback
|
||||
if (intr_status & DMA2D_LL_EVENT_TX_DONE) {
|
||||
if (tx_chan->on_desc_done) {
|
||||
need_yield |= tx_chan->on_desc_done(&tx_chan->base, &edata, tx_chan->user_data);
|
||||
}
|
||||
}
|
||||
return need_yield;
|
||||
}
|
||||
|
||||
static NOINLINE_ATTR bool _dma2d_default_rx_isr(dma2d_group_t *group, int channel_id)
|
||||
{
|
||||
bool need_yield = false;
|
||||
dma2d_rx_channel_t *rx_chan = group->rx_chans[channel_id];
|
||||
dma2d_event_data_t edata = {
|
||||
.transaction = rx_chan->base.status.transaction,
|
||||
};
|
||||
// Clear pending interrupt event
|
||||
uint32_t intr_status = dma2d_ll_rx_get_interrupt_status(group->hal.dev, channel_id);
|
||||
dma2d_ll_rx_clear_interrupt_status(group->hal.dev, channel_id, intr_status);
|
||||
// Save RX channel EOF callback pointers temporarily, could be overwritten by new ones
|
||||
dma2d_event_callback_t on_recv_eof = rx_chan->on_recv_eof;
|
||||
void *user_data = rx_chan->user_data;
|
||||
uint32_t suc_eof_desc_addr = dma2d_ll_rx_get_success_eof_desc_addr(group->hal.dev, channel_id);
|
||||
|
||||
// It is guaranteed in hardware that if SUC_EOF/ERR_EOF interrupt is raised, it will always be raised together with
|
||||
// RX_DONE interrupt at the same time.
|
||||
|
||||
// On RX_DONE triggered, it may be an indication of partially done, call `on_desc_done` callback, allowing 2D-DMA
|
||||
// channel operations on the currently acquired channels. Channel may continue running again.
|
||||
if (intr_status & DMA2D_LL_EVENT_RX_DONE) {
|
||||
if (rx_chan->on_desc_done) {
|
||||
need_yield |= rx_chan->on_desc_done(&rx_chan->base, &edata, user_data);
|
||||
}
|
||||
}
|
||||
|
||||
// If last transcation completes (regardless success or not), free the channels
|
||||
if ((intr_status & DMA2D_LL_EVENT_RX_SUC_EOF) ||
|
||||
(intr_status & DMA2D_LL_EVENT_RX_ERR_EOF) ||
|
||||
(intr_status & DMA2D_LL_EVENT_RX_DESC_ERROR)) {
|
||||
assert(dma2d_ll_rx_is_fsm_idle(group->hal.dev, channel_id));
|
||||
need_yield |= free_up_channels(group, rx_chan);
|
||||
}
|
||||
|
||||
// Handle last transaction's end callbacks (at this point, last transaction's channels are completely freed,
|
||||
// therefore, we don't pass in channel handle to the callbacks anymore)
|
||||
if (intr_status & DMA2D_LL_EVENT_RX_SUC_EOF) {
|
||||
if (on_recv_eof) {
|
||||
edata.rx_eof_desc_addr = suc_eof_desc_addr;
|
||||
need_yield |= on_recv_eof(NULL, &edata, user_data);
|
||||
}
|
||||
}
|
||||
return need_yield;
|
||||
}
|
||||
|
||||
static void dma2d_default_isr(void *args)
|
||||
{
|
||||
dma2d_channel_t *chan = (dma2d_channel_t *)args;
|
||||
dma2d_group_t *group = chan->group;
|
||||
|
||||
bool need_yield = false;
|
||||
|
||||
if (chan->direction == DMA2D_CHANNEL_DIRECTION_TX) {
|
||||
need_yield |= _dma2d_default_tx_isr(group, chan->channel_id);
|
||||
} else {
|
||||
// For RX channel interrupt triggered, we need to check whether there is any interrupt triggered for the
|
||||
// bundled TX channels but hasn't been processed. If so, handle TX interrupt first.
|
||||
uint32_t bundled_tx_channel_mask = group->rx_chans[chan->channel_id]->bundled_tx_channel_mask;
|
||||
while (bundled_tx_channel_mask) {
|
||||
uint32_t chan_id = __builtin_ffs(bundled_tx_channel_mask) - 1;
|
||||
bundled_tx_channel_mask &= ~(1 << chan_id);
|
||||
need_yield |= _dma2d_default_tx_isr(group, chan_id);
|
||||
}
|
||||
|
||||
need_yield |= _dma2d_default_rx_isr(group, chan->channel_id);
|
||||
}
|
||||
|
||||
if (need_yield) {
|
||||
portYIELD_FROM_ISR();
|
||||
}
|
||||
}
|
||||
|
||||
esp_err_t dma2d_acquire_pool(const dma2d_pool_config_t *config, dma2d_pool_handle_t *ret_pool)
|
||||
{
|
||||
esp_err_t ret = ESP_OK;
|
||||
ESP_RETURN_ON_FALSE(config && ret_pool, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
|
||||
ESP_RETURN_ON_FALSE(config->pool_id < SOC_DMA2D_GROUPS, ESP_ERR_INVALID_ARG, TAG, "invalid pool_id");
|
||||
if (config->intr_priority) {
|
||||
ESP_RETURN_ON_FALSE(1 << (config->intr_priority) & ESP_INTR_FLAG_LOWMED, ESP_ERR_INVALID_ARG, TAG,
|
||||
"invalid interrupt priority: %" PRIu32, config->intr_priority);
|
||||
}
|
||||
|
||||
int group_id = config->pool_id; // A pool is referring to a module group in hardware
|
||||
|
||||
_lock_acquire(&s_platform.mutex);
|
||||
if (!s_platform.groups[group_id]) {
|
||||
dma2d_group_t *pre_alloc_group = heap_caps_calloc(1, sizeof(dma2d_group_t), DMA2D_MEM_ALLOC_CAPS);
|
||||
dma2d_tx_channel_t *pre_alloc_tx_channels = heap_caps_calloc(SOC_DMA2D_TX_CHANNELS_PER_GROUP, sizeof(dma2d_tx_channel_t), DMA2D_MEM_ALLOC_CAPS);
|
||||
dma2d_rx_channel_t *pre_alloc_rx_channels = heap_caps_calloc(SOC_DMA2D_RX_CHANNELS_PER_GROUP, sizeof(dma2d_rx_channel_t), DMA2D_MEM_ALLOC_CAPS);
|
||||
if (pre_alloc_group && pre_alloc_tx_channels && pre_alloc_rx_channels) {
|
||||
pre_alloc_group->group_id = group_id;
|
||||
pre_alloc_group->spinlock = (portMUX_TYPE)portMUX_INITIALIZER_UNLOCKED;
|
||||
TAILQ_INIT(&pre_alloc_group->pending_trans_tailq);
|
||||
pre_alloc_group->tx_channel_free_mask = (1 << SOC_DMA2D_TX_CHANNELS_PER_GROUP) - 1;
|
||||
pre_alloc_group->rx_channel_free_mask = (1 << SOC_DMA2D_RX_CHANNELS_PER_GROUP) - 1;
|
||||
pre_alloc_group->tx_channel_reserved_mask = dma2d_tx_channel_reserved_mask[group_id];
|
||||
pre_alloc_group->rx_channel_reserved_mask = dma2d_rx_channel_reserved_mask[group_id];
|
||||
pre_alloc_group->periph_m2m_free_id_mask = DMA2D_LL_CHANNEL_PERIPH_M2M_FREE_ID_MASK;
|
||||
pre_alloc_group->intr_priority = -1;
|
||||
for (int i = 0; i < SOC_DMA2D_TX_CHANNELS_PER_GROUP; i++) {
|
||||
pre_alloc_group->tx_chans[i] = &pre_alloc_tx_channels[i];
|
||||
dma2d_tx_channel_t *tx_chan = pre_alloc_group->tx_chans[i];
|
||||
tx_chan->base.group = pre_alloc_group;
|
||||
tx_chan->base.channel_id = i;
|
||||
tx_chan->base.direction = DMA2D_CHANNEL_DIRECTION_TX;
|
||||
tx_chan->base.spinlock = (portMUX_TYPE)portMUX_INITIALIZER_UNLOCKED;
|
||||
}
|
||||
for (int i = 0; i < SOC_DMA2D_RX_CHANNELS_PER_GROUP; i++) {
|
||||
pre_alloc_group->rx_chans[i] = &pre_alloc_rx_channels[i];
|
||||
dma2d_rx_channel_t *rx_chan = pre_alloc_group->rx_chans[i];
|
||||
rx_chan->base.group = pre_alloc_group;
|
||||
rx_chan->base.channel_id = i;
|
||||
rx_chan->base.direction = DMA2D_CHANNEL_DIRECTION_RX;
|
||||
rx_chan->base.spinlock = (portMUX_TYPE)portMUX_INITIALIZER_UNLOCKED;
|
||||
}
|
||||
s_platform.groups[group_id] = pre_alloc_group; // register to platform
|
||||
|
||||
// Enable bus clock for the 2D-DMA registers
|
||||
PERIPH_RCC_ATOMIC() {
|
||||
dma2d_ll_enable_bus_clock(group_id, true);
|
||||
dma2d_ll_reset_register(group_id);
|
||||
}
|
||||
dma2d_hal_init(&pre_alloc_group->hal, group_id); // initialize HAL context
|
||||
// Enable 2D-DMA module clock
|
||||
dma2d_ll_hw_enable(s_platform.groups[group_id]->hal.dev, true);
|
||||
// Configure 2D-DMA accessible memory range
|
||||
dma2d_ll_set_accessible_mem_range(s_platform.groups[group_id]->hal.dev);
|
||||
} else {
|
||||
ret = ESP_ERR_NO_MEM;
|
||||
free(pre_alloc_tx_channels);
|
||||
free(pre_alloc_rx_channels);
|
||||
free(pre_alloc_group);
|
||||
}
|
||||
}
|
||||
|
||||
// Tracks the number of consumers of 2D-DMA module (clients of the pool)
|
||||
if (s_platform.groups[group_id]) {
|
||||
s_platform.group_ref_counts[group_id]++;
|
||||
}
|
||||
|
||||
// Allocate interrupts
|
||||
// First figure out the interrupt priority
|
||||
bool intr_priority_conflict = false;
|
||||
if (s_platform.groups[group_id]->intr_priority == -1) {
|
||||
s_platform.groups[group_id]->intr_priority = config->intr_priority;
|
||||
} else if (config->intr_priority != 0) {
|
||||
intr_priority_conflict = (s_platform.groups[group_id]->intr_priority != config->intr_priority);
|
||||
}
|
||||
ESP_GOTO_ON_FALSE(!intr_priority_conflict, ESP_ERR_INVALID_ARG, wrap_up, TAG, "intr_priority conflict, already is %d but attempt to %" PRIu32, s_platform.groups[group_id]->intr_priority, config->intr_priority);
|
||||
uint32_t intr_flags = DMA2D_INTR_ALLOC_FLAGS;
|
||||
if (s_platform.groups[group_id]->intr_priority) {
|
||||
intr_flags |= (1 << s_platform.groups[group_id]->intr_priority);
|
||||
} else {
|
||||
intr_flags |= ESP_INTR_FLAG_LOWMED;
|
||||
}
|
||||
|
||||
// Allocate TX and RX interrupts
|
||||
if (s_platform.groups[group_id]) {
|
||||
for (int i = 0; i < SOC_DMA2D_RX_CHANNELS_PER_GROUP; i++) {
|
||||
dma2d_rx_channel_t *rx_chan = s_platform.groups[group_id]->rx_chans[i];
|
||||
if (rx_chan->base.intr == NULL) {
|
||||
ret = esp_intr_alloc_intrstatus(dma2d_periph_signals.groups[group_id].rx_irq_id[i],
|
||||
intr_flags,
|
||||
(uint32_t)dma2d_ll_rx_get_interrupt_status_reg(s_platform.groups[group_id]->hal.dev, i),
|
||||
DMA2D_LL_RX_EVENT_MASK, dma2d_default_isr, &rx_chan->base, &rx_chan->base.intr);
|
||||
if (ret != ESP_OK) {
|
||||
ret = ESP_FAIL;
|
||||
ESP_LOGE(TAG, "alloc interrupt failed on rx channel (%d, %d)", group_id, i);
|
||||
goto wrap_up;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < SOC_DMA2D_TX_CHANNELS_PER_GROUP; i++) {
|
||||
dma2d_tx_channel_t *tx_chan = s_platform.groups[group_id]->tx_chans[i];
|
||||
if (tx_chan->base.intr == NULL) {
|
||||
ret = esp_intr_alloc_intrstatus(dma2d_periph_signals.groups[group_id].tx_irq_id[i],
|
||||
intr_flags,
|
||||
(uint32_t)dma2d_ll_tx_get_interrupt_status_reg(s_platform.groups[group_id]->hal.dev, i),
|
||||
DMA2D_LL_TX_EVENT_MASK, dma2d_default_isr, &tx_chan->base, &tx_chan->base.intr);
|
||||
if (ret != ESP_OK) {
|
||||
ret = ESP_FAIL;
|
||||
ESP_LOGE(TAG, "alloc interrupt failed on tx channel (%d, %d)", group_id, i);
|
||||
goto wrap_up;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
wrap_up:
|
||||
_lock_release(&s_platform.mutex);
|
||||
|
||||
if (ret != ESP_OK && s_platform.groups[group_id]) {
|
||||
dma2d_release_pool(s_platform.groups[group_id]);
|
||||
}
|
||||
|
||||
*ret_pool = s_platform.groups[group_id];
|
||||
return ret;
|
||||
}
|
||||
|
||||
esp_err_t dma2d_release_pool(dma2d_pool_handle_t dma2d_pool)
|
||||
{
|
||||
esp_err_t ret = ESP_OK;
|
||||
ESP_RETURN_ON_FALSE(dma2d_pool, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
|
||||
dma2d_group_t *dma2d_group = dma2d_pool;
|
||||
|
||||
bool do_deinitialize = false;
|
||||
int group_id = dma2d_group->group_id;
|
||||
|
||||
_lock_acquire(&s_platform.mutex);
|
||||
// Remove a client from the 2D-DMA pool
|
||||
s_platform.group_ref_counts[group_id]--;
|
||||
// If the pool has no client, then release pool resources
|
||||
if (s_platform.group_ref_counts[group_id] == 0) {
|
||||
assert(s_platform.groups[group_id]);
|
||||
do_deinitialize = true;
|
||||
// There must be no transaction pending (this should be handled by upper (consumer) driver)
|
||||
// Transaction tailq should be empty at this moment
|
||||
if (!TAILQ_EMPTY(&dma2d_group->pending_trans_tailq)) {
|
||||
ret = ESP_ERR_NOT_ALLOWED;
|
||||
ESP_LOGE(TAG, "Still pending transaction in the pool");
|
||||
s_platform.group_ref_counts[group_id]++;
|
||||
goto err;
|
||||
}
|
||||
s_platform.groups[group_id] = NULL; // deregister from platform
|
||||
// Disable 2D-DMA module clock
|
||||
dma2d_ll_hw_enable(dma2d_group->hal.dev, false);
|
||||
// Disable the bus clock for the 2D-DMA registers
|
||||
PERIPH_RCC_ATOMIC() {
|
||||
dma2d_ll_enable_bus_clock(group_id, false);
|
||||
}
|
||||
}
|
||||
|
||||
if (do_deinitialize) {
|
||||
for (int i = 0; i < SOC_DMA2D_RX_CHANNELS_PER_GROUP; i++) {
|
||||
if (dma2d_group->rx_chans[i]->base.intr) {
|
||||
esp_intr_free(dma2d_group->rx_chans[i]->base.intr);
|
||||
}
|
||||
}
|
||||
for (int i = 0; i < SOC_DMA2D_TX_CHANNELS_PER_GROUP; i++) {
|
||||
if (dma2d_group->tx_chans[i]->base.intr) {
|
||||
esp_intr_free(dma2d_group->tx_chans[i]->base.intr);
|
||||
}
|
||||
}
|
||||
free(*(dma2d_group->tx_chans));
|
||||
free(*(dma2d_group->rx_chans));
|
||||
free(dma2d_group);
|
||||
s_platform.groups[group_id] = NULL;
|
||||
}
|
||||
err:
|
||||
_lock_release(&s_platform.mutex);
|
||||
return ret;
|
||||
}
|
||||
|
||||
esp_err_t dma2d_connect(dma2d_channel_handle_t dma2d_chan, const dma2d_trigger_t *trig_periph)
|
||||
{
|
||||
esp_err_t ret = ESP_OK;
|
||||
ESP_GOTO_ON_FALSE_ISR(dma2d_chan && trig_periph, ESP_ERR_INVALID_ARG, err, TAG, "invalid argument");
|
||||
|
||||
dma2d_group_t *group = dma2d_chan->group;
|
||||
int channel_id = dma2d_chan->channel_id;
|
||||
|
||||
// Find periph_sel_id for the channel
|
||||
int peri_sel_id = trig_periph->periph_sel_id;
|
||||
portENTER_CRITICAL_SAFE(&group->spinlock);
|
||||
if (trig_periph->periph == DMA2D_TRIG_PERIPH_M2M) {
|
||||
// Assign peri_sel_id to one of {4, 5, 6, 7}
|
||||
assert(dma2d_chan->status.has_sibling);
|
||||
// First find out the peri_sel_id of its sibling channel
|
||||
int sibling_periph_sel_id = -1;
|
||||
if (dma2d_chan->direction == DMA2D_CHANNEL_DIRECTION_TX) {
|
||||
sibling_periph_sel_id = group->rx_chans[channel_id]->base.status.periph_sel_id;
|
||||
} else {
|
||||
sibling_periph_sel_id = group->tx_chans[channel_id]->base.status.periph_sel_id;
|
||||
}
|
||||
|
||||
if (peri_sel_id == -1) {
|
||||
// Unspecified periph_sel_id, decide by the driver
|
||||
if (sibling_periph_sel_id != -1 && ((1 << sibling_periph_sel_id) & DMA2D_LL_CHANNEL_PERIPH_M2M_FREE_ID_MASK)) {
|
||||
peri_sel_id = sibling_periph_sel_id;
|
||||
} else {
|
||||
peri_sel_id = __builtin_ctz(group->periph_m2m_free_id_mask);
|
||||
}
|
||||
} else {
|
||||
// Check whether specified periph_sel_id is valid
|
||||
if (sibling_periph_sel_id != -1) {
|
||||
if (sibling_periph_sel_id != peri_sel_id) {
|
||||
peri_sel_id = -1; // Conflict id with its sibling channel
|
||||
}
|
||||
} else {
|
||||
if (!((1 << peri_sel_id) & group->periph_m2m_free_id_mask & DMA2D_LL_CHANNEL_PERIPH_M2M_FREE_ID_MASK)) {
|
||||
peri_sel_id = -1; // Occupied or invalid m2m peri_sel_id
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (peri_sel_id >= 0) {
|
||||
dma2d_chan->status.periph_sel_id = peri_sel_id;
|
||||
group->periph_m2m_free_id_mask &= ~(1 << peri_sel_id); // acquire m2m periph_sel_id
|
||||
}
|
||||
portEXIT_CRITICAL_SAFE(&group->spinlock);
|
||||
ESP_GOTO_ON_FALSE_ISR(peri_sel_id >= 0, ESP_ERR_INVALID_ARG, err, TAG, "invalid periph_sel_id");
|
||||
|
||||
portENTER_CRITICAL_SAFE(&dma2d_chan->spinlock);
|
||||
if (dma2d_chan->direction == DMA2D_CHANNEL_DIRECTION_TX) {
|
||||
dma2d_ll_tx_stop(group->hal.dev, channel_id);
|
||||
dma2d_hal_tx_reset_channel(&group->hal, channel_id);
|
||||
dma2d_ll_tx_connect_to_periph(group->hal.dev, channel_id, trig_periph->periph, peri_sel_id);
|
||||
|
||||
// Configure reorder functionality
|
||||
dma2d_ll_tx_enable_reorder(group->hal.dev, channel_id, dma2d_chan->status.reorder_en);
|
||||
// Assume dscr_port enable or not can be directly derived from trig_periph
|
||||
dma2d_ll_tx_enable_dscr_port(group->hal.dev, channel_id, trig_periph->periph == DMA2D_TRIG_PERIPH_PPA_SR);
|
||||
|
||||
// Reset to certain settings
|
||||
dma2d_ll_tx_enable_owner_check(group->hal.dev, channel_id, false);
|
||||
dma2d_ll_tx_enable_auto_write_back(group->hal.dev, channel_id, false);
|
||||
dma2d_ll_tx_enable_eof_mode(group->hal.dev, channel_id, true);
|
||||
dma2d_ll_tx_enable_descriptor_burst(group->hal.dev, channel_id, false);
|
||||
dma2d_ll_tx_set_data_burst_length(group->hal.dev, channel_id, DMA2D_DATA_BURST_LENGTH_128);
|
||||
dma2d_ll_tx_enable_page_bound_wrap(group->hal.dev, channel_id, true);
|
||||
dma2d_ll_tx_set_macro_block_size(group->hal.dev, channel_id, DMA2D_MACRO_BLOCK_SIZE_NONE);
|
||||
if ((1 << channel_id) & DMA2D_LL_TX_CHANNEL_SUPPORT_CSC_MASK) {
|
||||
dma2d_ll_tx_configure_color_space_conv(group->hal.dev, channel_id, DMA2D_CSC_TX_NONE);
|
||||
}
|
||||
|
||||
// Disable and clear all interrupt events
|
||||
dma2d_ll_tx_enable_interrupt(group->hal.dev, channel_id, UINT32_MAX, false); // disable all interrupt events
|
||||
dma2d_ll_tx_clear_interrupt_status(group->hal.dev, channel_id, UINT32_MAX); // clear all pending events
|
||||
} else {
|
||||
dma2d_ll_rx_stop(group->hal.dev, channel_id);
|
||||
dma2d_hal_rx_reset_channel(&group->hal, channel_id);
|
||||
dma2d_ll_rx_connect_to_periph(group->hal.dev, channel_id, trig_periph->periph, peri_sel_id);
|
||||
|
||||
// Configure reorder functionality
|
||||
dma2d_ll_rx_enable_reorder(group->hal.dev, channel_id, dma2d_chan->status.reorder_en);
|
||||
// Assume dscr_port enable or not can be directly derived from trig_periph
|
||||
dma2d_ll_rx_enable_dscr_port(group->hal.dev, channel_id, trig_periph->periph == DMA2D_TRIG_PERIPH_PPA_SR);
|
||||
|
||||
// Reset to certain settings
|
||||
dma2d_ll_rx_enable_owner_check(group->hal.dev, channel_id, false);
|
||||
dma2d_ll_rx_enable_descriptor_burst(group->hal.dev, channel_id, false);
|
||||
dma2d_ll_rx_set_data_burst_length(group->hal.dev, channel_id, DMA2D_DATA_BURST_LENGTH_128);
|
||||
dma2d_ll_rx_enable_page_bound_wrap(group->hal.dev, channel_id, true);
|
||||
dma2d_ll_rx_set_macro_block_size(group->hal.dev, channel_id, DMA2D_MACRO_BLOCK_SIZE_NONE);
|
||||
if ((1 << channel_id) & DMA2D_LL_RX_CHANNEL_SUPPORT_CSC_MASK) {
|
||||
dma2d_ll_rx_configure_color_space_conv(group->hal.dev, channel_id, DMA2D_CSC_RX_NONE);
|
||||
}
|
||||
|
||||
// Disable and clear all interrupt events
|
||||
dma2d_ll_rx_enable_interrupt(group->hal.dev, channel_id, UINT32_MAX, false); // disable all interrupt events
|
||||
dma2d_ll_rx_clear_interrupt_status(group->hal.dev, channel_id, UINT32_MAX); // clear all pending events
|
||||
}
|
||||
portEXIT_CRITICAL_SAFE(&dma2d_chan->spinlock);
|
||||
|
||||
err:
|
||||
return ret;
|
||||
}
|
||||
|
||||
esp_err_t dma2d_register_tx_event_callbacks(dma2d_channel_handle_t dma2d_chan, dma2d_tx_event_callbacks_t *cbs, void *user_data)
|
||||
{
|
||||
esp_err_t ret = ESP_OK;
|
||||
ESP_GOTO_ON_FALSE_ISR(dma2d_chan && dma2d_chan->direction == DMA2D_CHANNEL_DIRECTION_TX && cbs, ESP_ERR_INVALID_ARG, err, TAG, "invalid argument");
|
||||
ESP_GOTO_ON_FALSE_ISR(dma2d_chan->intr, ESP_ERR_INVALID_STATE, err, TAG, "tx channel intr not allocated");
|
||||
|
||||
dma2d_group_t *group = dma2d_chan->group;
|
||||
dma2d_tx_channel_t *tx_chan = __containerof(dma2d_chan, dma2d_tx_channel_t, base);
|
||||
|
||||
#if CONFIG_DMA2D_ISR_IRAM_SAFE
|
||||
if (cbs->on_desc_done) {
|
||||
ESP_GOTO_ON_FALSE_ISR(esp_ptr_in_iram(cbs->on_desc_done),
|
||||
ESP_ERR_INVALID_ARG, err, TAG, "on_desc_done not in IRAM");
|
||||
}
|
||||
if (user_data) {
|
||||
ESP_GOTO_ON_FALSE_ISR(esp_ptr_internal(user_data),
|
||||
ESP_ERR_INVALID_ARG, err, TAG, "user context not in internal RAM");
|
||||
}
|
||||
#endif
|
||||
|
||||
// Enable/Disable 2D-DMA interrupt events for the TX channel
|
||||
uint32_t mask = 0;
|
||||
portENTER_CRITICAL_SAFE(&tx_chan->base.spinlock);
|
||||
if (cbs->on_desc_done) {
|
||||
tx_chan->on_desc_done = cbs->on_desc_done;
|
||||
mask |= DMA2D_LL_EVENT_TX_DONE;
|
||||
}
|
||||
tx_chan->user_data = user_data;
|
||||
dma2d_ll_tx_enable_interrupt(group->hal.dev, tx_chan->base.channel_id, mask, true);
|
||||
portEXIT_CRITICAL_SAFE(&tx_chan->base.spinlock);
|
||||
|
||||
err:
|
||||
return ret;
|
||||
}
|
||||
|
||||
esp_err_t dma2d_register_rx_event_callbacks(dma2d_channel_handle_t dma2d_chan, dma2d_rx_event_callbacks_t *cbs, void *user_data)
|
||||
{
|
||||
esp_err_t ret = ESP_OK;
|
||||
ESP_GOTO_ON_FALSE_ISR(dma2d_chan && dma2d_chan->direction == DMA2D_CHANNEL_DIRECTION_RX && cbs, ESP_ERR_INVALID_ARG, err, TAG, "invalid argument");
|
||||
|
||||
dma2d_group_t *group = dma2d_chan->group;
|
||||
dma2d_rx_channel_t *rx_chan = __containerof(dma2d_chan, dma2d_rx_channel_t, base);
|
||||
|
||||
#if CONFIG_DMA2D_ISR_IRAM_SAFE
|
||||
if (cbs->on_recv_eof) {
|
||||
ESP_GOTO_ON_FALSE_ISR(esp_ptr_in_iram(cbs->on_recv_eof),
|
||||
ESP_ERR_INVALID_ARG, err, TAG, "on_recv_eof not in IRAM");
|
||||
}
|
||||
if (cbs->on_desc_done) {
|
||||
ESP_GOTO_ON_FALSE_ISR(esp_ptr_in_iram(cbs->on_desc_done),
|
||||
ESP_ERR_INVALID_ARG, err, TAG, "on_desc_done not in IRAM");
|
||||
}
|
||||
if (user_data) {
|
||||
ESP_GOTO_ON_FALSE_ISR(esp_ptr_internal(user_data),
|
||||
ESP_ERR_INVALID_ARG, err, TAG, "user context not in internal RAM");
|
||||
}
|
||||
#endif
|
||||
|
||||
// Enable/Disable 2D-DMA interrupt events for the RX channel
|
||||
uint32_t mask = 0;
|
||||
portENTER_CRITICAL_SAFE(&rx_chan->base.spinlock);
|
||||
if (cbs->on_recv_eof) {
|
||||
rx_chan->on_recv_eof = cbs->on_recv_eof;
|
||||
mask |= DMA2D_LL_EVENT_RX_SUC_EOF;
|
||||
}
|
||||
if (cbs->on_desc_done) {
|
||||
rx_chan->on_desc_done = cbs->on_desc_done;
|
||||
mask |= DMA2D_LL_EVENT_RX_DONE;
|
||||
}
|
||||
rx_chan->user_data = user_data;
|
||||
dma2d_ll_rx_enable_interrupt(group->hal.dev, rx_chan->base.channel_id, mask, true);
|
||||
|
||||
portEXIT_CRITICAL_SAFE(&rx_chan->base.spinlock);
|
||||
|
||||
err:
|
||||
return ret;
|
||||
}
|
||||
|
||||
esp_err_t dma2d_set_desc_addr(dma2d_channel_handle_t dma2d_chan, intptr_t desc_base_addr)
|
||||
{
|
||||
esp_err_t ret = ESP_OK;
|
||||
ESP_GOTO_ON_FALSE_ISR(dma2d_chan && desc_base_addr, ESP_ERR_INVALID_ARG, err, TAG, "invalid argument");
|
||||
// 2D-DMA descriptor addr needs 8-byte alignment and not in TCM (addr not in TCM is IDF restriction)
|
||||
ESP_GOTO_ON_FALSE_ISR((desc_base_addr & 0x7) == 0 && !esp_ptr_in_tcm((void *)desc_base_addr), ESP_ERR_INVALID_ARG, err, TAG, "invalid argument");
|
||||
|
||||
dma2d_group_t *group = dma2d_chan->group;
|
||||
int channel_id = dma2d_chan->channel_id;
|
||||
|
||||
if (dma2d_chan->direction == DMA2D_CHANNEL_DIRECTION_TX) {
|
||||
dma2d_ll_tx_set_desc_addr(group->hal.dev, channel_id, desc_base_addr);
|
||||
} else {
|
||||
dma2d_ll_rx_set_desc_addr(group->hal.dev, channel_id, desc_base_addr);
|
||||
}
|
||||
|
||||
err:
|
||||
return ret;
|
||||
}
|
||||
|
||||
esp_err_t dma2d_start(dma2d_channel_handle_t dma2d_chan)
|
||||
{
|
||||
esp_err_t ret = ESP_OK;
|
||||
ESP_GOTO_ON_FALSE_ISR(dma2d_chan, ESP_ERR_INVALID_ARG, err, TAG, "invalid argument");
|
||||
|
||||
dma2d_group_t *group = dma2d_chan->group;
|
||||
int channel_id = dma2d_chan->channel_id;
|
||||
|
||||
if (dma2d_chan->direction == DMA2D_CHANNEL_DIRECTION_RX) {
|
||||
// dma2d driver relies on going into ISR to free the channels,
|
||||
// so even if callbacks are not necessary in some cases, minimum interrupt events should be enabled to trigger ISR
|
||||
dma2d_ll_rx_enable_interrupt(group->hal.dev, channel_id, DMA2D_RX_DEFAULT_INTR_FLAG, true);
|
||||
}
|
||||
|
||||
if (dma2d_chan->direction == DMA2D_CHANNEL_DIRECTION_TX) {
|
||||
assert(dma2d_ll_tx_is_fsm_idle(group->hal.dev, channel_id));
|
||||
dma2d_ll_tx_start(group->hal.dev, channel_id);
|
||||
} else {
|
||||
assert(dma2d_ll_rx_is_fsm_idle(group->hal.dev, channel_id));
|
||||
dma2d_ll_rx_start(group->hal.dev, channel_id);
|
||||
}
|
||||
|
||||
err:
|
||||
return ret;
|
||||
}
|
||||
|
||||
esp_err_t dma2d_stop(dma2d_channel_handle_t dma2d_chan)
|
||||
{
|
||||
ESP_RETURN_ON_FALSE_ISR(dma2d_chan, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
|
||||
|
||||
dma2d_group_t *group = dma2d_chan->group;
|
||||
int channel_id = dma2d_chan->channel_id;
|
||||
|
||||
if (dma2d_chan->direction == DMA2D_CHANNEL_DIRECTION_TX) {
|
||||
dma2d_ll_tx_stop(group->hal.dev, channel_id);
|
||||
} else {
|
||||
dma2d_ll_rx_stop(group->hal.dev, channel_id);
|
||||
}
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t dma2d_append(dma2d_channel_handle_t dma2d_chan)
|
||||
{
|
||||
ESP_RETURN_ON_FALSE_ISR(dma2d_chan, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
|
||||
|
||||
dma2d_group_t *group = dma2d_chan->group;
|
||||
int channel_id = dma2d_chan->channel_id;
|
||||
|
||||
if (dma2d_chan->direction == DMA2D_CHANNEL_DIRECTION_TX) {
|
||||
dma2d_ll_tx_restart(group->hal.dev, channel_id);
|
||||
} else {
|
||||
dma2d_ll_rx_restart(group->hal.dev, channel_id);
|
||||
}
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t dma2d_reset(dma2d_channel_handle_t dma2d_chan)
|
||||
{
|
||||
ESP_RETURN_ON_FALSE_ISR(dma2d_chan, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
|
||||
|
||||
dma2d_group_t *group = dma2d_chan->group;
|
||||
int channel_id = dma2d_chan->channel_id;
|
||||
|
||||
portENTER_CRITICAL_SAFE(&dma2d_chan->spinlock);
|
||||
if (dma2d_chan->direction == DMA2D_CHANNEL_DIRECTION_TX) {
|
||||
dma2d_hal_tx_reset_channel(&group->hal, channel_id);
|
||||
} else {
|
||||
dma2d_hal_rx_reset_channel(&group->hal, channel_id);
|
||||
}
|
||||
portEXIT_CRITICAL_SAFE(&dma2d_chan->spinlock);
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t dma2d_apply_strategy(dma2d_channel_handle_t dma2d_chan, const dma2d_strategy_config_t *config)
|
||||
{
|
||||
esp_err_t ret = ESP_OK;
|
||||
ESP_GOTO_ON_FALSE_ISR(dma2d_chan && config, ESP_ERR_INVALID_ARG, err, TAG, "invalid argument");
|
||||
|
||||
dma2d_group_t *group = dma2d_chan->group;
|
||||
int channel_id = dma2d_chan->channel_id;
|
||||
|
||||
if (dma2d_chan->direction == DMA2D_CHANNEL_DIRECTION_TX) {
|
||||
dma2d_ll_tx_enable_owner_check(group->hal.dev, channel_id, config->owner_check);
|
||||
dma2d_ll_tx_enable_auto_write_back(group->hal.dev, channel_id, config->auto_update_desc);
|
||||
dma2d_ll_tx_enable_eof_mode(group->hal.dev, channel_id, config->eof_till_data_popped);
|
||||
} else {
|
||||
dma2d_ll_rx_enable_owner_check(group->hal.dev, channel_id, config->owner_check);
|
||||
// RX channels do not have control over auto_write_back (always auto_write_back) and eof_mode
|
||||
}
|
||||
|
||||
err:
|
||||
return ret;
|
||||
}
|
||||
|
||||
esp_err_t dma2d_set_transfer_ability(dma2d_channel_handle_t dma2d_chan, const dma2d_transfer_ability_t *ability)
|
||||
{
|
||||
esp_err_t ret = ESP_OK;
|
||||
ESP_GOTO_ON_FALSE_ISR(dma2d_chan && ability, ESP_ERR_INVALID_ARG, err, TAG, "invalid argument");
|
||||
ESP_GOTO_ON_FALSE_ISR(ability->data_burst_length < DMA2D_DATA_BURST_LENGTH_INVALID, ESP_ERR_INVALID_ARG, err, TAG, "invalid argument");
|
||||
ESP_GOTO_ON_FALSE_ISR(ability->mb_size < DMA2D_MACRO_BLOCK_SIZE_INVALID, ESP_ERR_INVALID_ARG, err, TAG, "invalid argument");
|
||||
|
||||
dma2d_group_t *group = dma2d_chan->group;
|
||||
int channel_id = dma2d_chan->channel_id;
|
||||
|
||||
if (dma2d_chan->direction == DMA2D_CHANNEL_DIRECTION_TX) {
|
||||
dma2d_ll_tx_enable_descriptor_burst(group->hal.dev, channel_id, ability->desc_burst_en);
|
||||
dma2d_ll_tx_set_data_burst_length(group->hal.dev, channel_id, ability->data_burst_length);
|
||||
dma2d_ll_tx_enable_page_bound_wrap(group->hal.dev, channel_id, ability->data_burst_length != 1);
|
||||
dma2d_ll_tx_set_macro_block_size(group->hal.dev, channel_id, ability->mb_size);
|
||||
} else {
|
||||
dma2d_ll_rx_enable_descriptor_burst(group->hal.dev, channel_id, ability->desc_burst_en);
|
||||
dma2d_ll_rx_set_data_burst_length(group->hal.dev, channel_id, ability->data_burst_length);
|
||||
dma2d_ll_rx_enable_page_bound_wrap(group->hal.dev, channel_id, ability->data_burst_length != 1);
|
||||
dma2d_ll_rx_set_macro_block_size(group->hal.dev, channel_id, ability->mb_size);
|
||||
}
|
||||
|
||||
err:
|
||||
return ret;
|
||||
}
|
||||
|
||||
esp_err_t dma2d_configure_color_space_conversion(dma2d_channel_handle_t dma2d_chan, const dma2d_csc_config_t *config)
|
||||
{
|
||||
esp_err_t ret = ESP_OK;
|
||||
ESP_GOTO_ON_FALSE_ISR(dma2d_chan && config, ESP_ERR_INVALID_ARG, err, TAG, "invalid argument");
|
||||
|
||||
dma2d_group_t *group = dma2d_chan->group;
|
||||
int channel_id = dma2d_chan->channel_id;
|
||||
|
||||
if (dma2d_chan->direction == DMA2D_CHANNEL_DIRECTION_TX) {
|
||||
ESP_GOTO_ON_FALSE_ISR((1 << channel_id) & DMA2D_LL_TX_CHANNEL_SUPPORT_CSC_MASK, ESP_ERR_INVALID_ARG, err, TAG, "invalid argument");
|
||||
ESP_GOTO_ON_FALSE_ISR(config->tx_csc_option < DMA2D_CSC_TX_INVALID, ESP_ERR_INVALID_ARG, err, TAG, "invalid argument");
|
||||
ESP_GOTO_ON_FALSE_ISR(config->post_scramble == 0, ESP_ERR_INVALID_ARG, err, TAG, "invalid argument");
|
||||
ESP_GOTO_ON_FALSE_ISR(config->pre_scramble == DMA2D_SCRAMBLE_ORDER_BYTE2_1_0 || (config->pre_scramble != DMA2D_SCRAMBLE_ORDER_BYTE2_1_0 && config->tx_csc_option != DMA2D_CSC_TX_NONE),
|
||||
ESP_ERR_INVALID_ARG, err, TAG, "invalid argument");
|
||||
|
||||
dma2d_ll_tx_configure_color_space_conv(group->hal.dev, channel_id, config->tx_csc_option);
|
||||
dma2d_ll_tx_set_csc_pre_scramble(group->hal.dev, channel_id, config->pre_scramble);
|
||||
} else {
|
||||
ESP_GOTO_ON_FALSE_ISR((1 << channel_id) & DMA2D_LL_RX_CHANNEL_SUPPORT_CSC_MASK, ESP_ERR_INVALID_ARG, err, TAG, "invalid argument");
|
||||
ESP_GOTO_ON_FALSE_ISR(config->rx_csc_option < DMA2D_CSC_RX_INVALID, ESP_ERR_INVALID_ARG, err, TAG, "invalid argument");
|
||||
ESP_GOTO_ON_FALSE_ISR((config->pre_scramble == DMA2D_SCRAMBLE_ORDER_BYTE2_1_0 && config->post_scramble == DMA2D_SCRAMBLE_ORDER_BYTE2_1_0) ||
|
||||
((config->pre_scramble != DMA2D_SCRAMBLE_ORDER_BYTE2_1_0 || config->post_scramble != DMA2D_SCRAMBLE_ORDER_BYTE2_1_0) && config->rx_csc_option != DMA2D_CSC_RX_NONE),
|
||||
ESP_ERR_INVALID_ARG, err, TAG, "invalid argument");
|
||||
|
||||
dma2d_ll_rx_configure_color_space_conv(group->hal.dev, channel_id, config->rx_csc_option);
|
||||
dma2d_ll_rx_set_csc_pre_scramble(group->hal.dev, channel_id, config->pre_scramble);
|
||||
dma2d_ll_rx_set_csc_post_scramble(group->hal.dev, channel_id, config->post_scramble);
|
||||
}
|
||||
|
||||
err:
|
||||
return ret;
|
||||
}
|
||||
|
||||
esp_err_t dma2d_enqueue(dma2d_pool_handle_t dma2d_pool, const dma2d_trans_config_t *trans_desc, dma2d_trans_t *trans_placeholder)
|
||||
{
|
||||
esp_err_t ret = ESP_OK;
|
||||
ESP_GOTO_ON_FALSE_ISR(dma2d_pool && trans_desc && trans_placeholder, ESP_ERR_INVALID_ARG, err, TAG, "invalid argument");
|
||||
ESP_GOTO_ON_FALSE_ISR(trans_desc->rx_channel_num <= 1, ESP_ERR_INVALID_ARG, err, TAG, "one trans at most has one rx channel");
|
||||
uint32_t total_channel_num = trans_desc->tx_channel_num + trans_desc->rx_channel_num;
|
||||
ESP_GOTO_ON_FALSE_ISR(total_channel_num <= DMA2D_MAX_CHANNEL_NUM_PER_TRANSACTION, ESP_ERR_INVALID_ARG, err, TAG, "too many channels acquiring for a trans");
|
||||
dma2d_group_t *dma2d_group = dma2d_pool;
|
||||
if (trans_desc->specified_tx_channel_mask || trans_desc->specified_rx_channel_mask) {
|
||||
ESP_GOTO_ON_FALSE_ISR(
|
||||
(trans_desc->specified_tx_channel_mask ? (trans_desc->specified_tx_channel_mask & dma2d_group->tx_channel_reserved_mask) : 1 ) &&
|
||||
(trans_desc->specified_rx_channel_mask ? (trans_desc->specified_rx_channel_mask & dma2d_group->rx_channel_reserved_mask) : 1 ),
|
||||
ESP_ERR_INVALID_ARG, err, TAG, "specified channel(s) not reserved");
|
||||
ESP_GOTO_ON_FALSE_ISR(
|
||||
(__builtin_popcount(trans_desc->specified_tx_channel_mask) == trans_desc->tx_channel_num) &&
|
||||
(__builtin_popcount(trans_desc->specified_rx_channel_mask) == trans_desc->rx_channel_num) &&
|
||||
(!trans_desc->tx_channel_num ? 1 : (trans_desc->specified_tx_channel_mask & ((trans_desc->channel_flags & DMA2D_CHANNEL_FUNCTION_FLAG_TX_REORDER) ? DMA2D_LL_TX_CHANNEL_SUPPORT_RO_MASK : UINT32_MAX) & ((trans_desc->channel_flags & DMA2D_CHANNEL_FUNCTION_FLAG_TX_CSC) ? DMA2D_LL_TX_CHANNEL_SUPPORT_CSC_MASK : UINT32_MAX))) &&
|
||||
(!trans_desc->rx_channel_num ? 1 : (trans_desc->specified_rx_channel_mask & ((trans_desc->channel_flags & DMA2D_CHANNEL_FUNCTION_FLAG_RX_REORDER) ? DMA2D_LL_RX_CHANNEL_SUPPORT_RO_MASK : UINT32_MAX) & ((trans_desc->channel_flags & DMA2D_CHANNEL_FUNCTION_FLAG_RX_CSC) ? DMA2D_LL_RX_CHANNEL_SUPPORT_CSC_MASK : UINT32_MAX))) &&
|
||||
((trans_desc->channel_flags & DMA2D_CHANNEL_FUNCTION_FLAG_SIBLING) ? (trans_desc->specified_tx_channel_mask == trans_desc->specified_rx_channel_mask) : 1),
|
||||
ESP_ERR_INVALID_ARG, err, TAG, "specified channels cannot meet function requirements");
|
||||
}
|
||||
|
||||
#if CONFIG_DMA2D_ISR_IRAM_SAFE
|
||||
ESP_GOTO_ON_FALSE_ISR(trans_desc->on_job_picked && esp_ptr_in_iram(trans_desc->on_job_picked),
|
||||
ESP_ERR_INVALID_ARG, err, TAG, "on_job_picked not in IRAM");
|
||||
ESP_GOTO_ON_FALSE_ISR(trans_desc->user_config && esp_ptr_internal(trans_desc->user_config),
|
||||
ESP_ERR_INVALID_ARG, err, TAG, "user context not in internal RAM");
|
||||
#endif
|
||||
|
||||
trans_placeholder->desc = trans_desc;
|
||||
dma2d_trans_channel_info_t channel_handle_array[DMA2D_MAX_CHANNEL_NUM_PER_TRANSACTION];
|
||||
|
||||
portENTER_CRITICAL_SAFE(&dma2d_group->spinlock);
|
||||
bool enqueue = !acquire_free_channels_for_trans(dma2d_group, trans_desc, channel_handle_array);
|
||||
if (enqueue) {
|
||||
if (!trans_desc->specified_tx_channel_mask && !trans_desc->specified_rx_channel_mask) {
|
||||
TAILQ_INSERT_TAIL(&dma2d_group->pending_trans_tailq, trans_placeholder, entry);
|
||||
} else {
|
||||
TAILQ_INSERT_HEAD(&dma2d_group->pending_trans_tailq, trans_placeholder, entry);
|
||||
}
|
||||
}
|
||||
portEXIT_CRITICAL_SAFE(&dma2d_group->spinlock);
|
||||
if (!enqueue) {
|
||||
// Free channels available, start transaction immediately
|
||||
// Store the acquired rx_chan into trans_placeholder (dma2d_trans_t) in case upper driver later need it to call `dma2d_force_end`
|
||||
// Upper driver controls the life cycle of trans_placeholder
|
||||
for (int i = 0; i < total_channel_num; i++) {
|
||||
if (channel_handle_array[i].dir == DMA2D_CHANNEL_DIRECTION_RX) {
|
||||
trans_placeholder->rx_chan = channel_handle_array[i].chan;
|
||||
}
|
||||
// Also save the transaction pointer
|
||||
channel_handle_array[i].chan->status.transaction = trans_placeholder;
|
||||
}
|
||||
trans_desc->on_job_picked(total_channel_num, channel_handle_array, trans_desc->user_config);
|
||||
}
|
||||
|
||||
err:
|
||||
return ret;
|
||||
}
|
||||
|
||||
esp_err_t dma2d_force_end(dma2d_trans_t *trans, bool *need_yield)
|
||||
{
|
||||
ESP_RETURN_ON_FALSE_ISR(trans && trans->rx_chan, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
|
||||
assert(trans->rx_chan->direction == DMA2D_CHANNEL_DIRECTION_RX);
|
||||
|
||||
dma2d_group_t *group = trans->rx_chan->group;
|
||||
|
||||
bool in_flight = false;
|
||||
// We judge whether the transaction is in-flight by checking the RX channel it uses is in-use or free
|
||||
portENTER_CRITICAL_SAFE(&group->spinlock);
|
||||
if (!(group->rx_channel_free_mask & (1 << trans->rx_chan->channel_id))) {
|
||||
in_flight = true;
|
||||
dma2d_ll_rx_enable_interrupt(group->hal.dev, trans->rx_chan->channel_id, UINT32_MAX, false);
|
||||
assert(!dma2d_ll_rx_is_fsm_idle(group->hal.dev, trans->rx_chan->channel_id));
|
||||
}
|
||||
portEXIT_CRITICAL_SAFE(&group->spinlock);
|
||||
ESP_RETURN_ON_FALSE_ISR(in_flight, ESP_ERR_INVALID_STATE, TAG, "transaction not in-flight");
|
||||
|
||||
dma2d_rx_channel_t *rx_chan = group->rx_chans[trans->rx_chan->channel_id];
|
||||
// Stop the RX channel and its bundled TX channels first
|
||||
dma2d_stop(&rx_chan->base);
|
||||
uint32_t tx_chans = rx_chan->bundled_tx_channel_mask;
|
||||
for (int i = 0; i < SOC_DMA2D_TX_CHANNELS_PER_GROUP; i++) {
|
||||
if (tx_chans & (1 << i)) {
|
||||
dma2d_stop(&group->tx_chans[i]->base);
|
||||
}
|
||||
}
|
||||
// Then release channels
|
||||
*need_yield = free_up_channels(group, rx_chan);
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
size_t dma2d_get_trans_elm_size(void)
|
||||
{
|
||||
return sizeof(dma2d_trans_t);
|
||||
}
|
94
components/esp_hw_support/dma/dma2d_priv.h
Normal file
94
components/esp_hw_support/dma/dma2d_priv.h
Normal file
@ -0,0 +1,94 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include <sys/queue.h>
|
||||
#include "sdkconfig.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "esp_intr_alloc.h"
|
||||
#include "esp_heap_caps.h"
|
||||
#include "soc/soc_caps.h"
|
||||
#include "hal/dma2d_hal.h"
|
||||
#include "hal/dma2d_ll.h"
|
||||
#include "esp_private/dma2d.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#if CONFIG_DMA2D_OPERATION_FUNC_IN_IRAM || CONFIG_DMA2D_ISR_IRAM_SAFE
|
||||
#define DMA2D_MEM_ALLOC_CAPS (MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT)
|
||||
#else
|
||||
#define DMA2D_MEM_ALLOC_CAPS MALLOC_CAP_DEFAULT
|
||||
#endif
|
||||
|
||||
#if CONFIG_DMA2D_ISR_IRAM_SAFE
|
||||
#define DMA2D_INTR_ALLOC_FLAGS (ESP_INTR_FLAG_SHARED | ESP_INTR_FLAG_IRAM)
|
||||
#else
|
||||
#define DMA2D_INTR_ALLOC_FLAGS ESP_INTR_FLAG_SHARED
|
||||
#endif
|
||||
|
||||
#define DMA2D_RX_DEFAULT_INTR_FLAG (DMA2D_LL_EVENT_RX_SUC_EOF | DMA2D_LL_EVENT_RX_ERR_EOF | DMA2D_LL_EVENT_RX_DESC_ERROR)
|
||||
|
||||
typedef struct dma2d_channel_t dma2d_channel_t;
|
||||
typedef struct dma2d_tx_channel_t dma2d_tx_channel_t;
|
||||
typedef struct dma2d_rx_channel_t dma2d_rx_channel_t;
|
||||
typedef struct dma2d_group_t dma2d_group_t;
|
||||
|
||||
struct dma2d_trans_s {
|
||||
TAILQ_ENTRY(dma2d_trans_s) entry; // Link entry
|
||||
const dma2d_trans_config_t *desc; // Pointer to the structure containing all configuration items of a transaction
|
||||
dma2d_channel_handle_t rx_chan; // Pointer to the RX channel handle that will be used to do the transaction
|
||||
};
|
||||
|
||||
struct dma2d_group_t {
|
||||
int group_id; // Group ID, index from 0
|
||||
dma2d_hal_context_t hal; // HAL instance is at group level
|
||||
portMUX_TYPE spinlock; // Group level spinlock
|
||||
TAILQ_HEAD(pending_trans, dma2d_trans_s) pending_trans_tailq; // Link head of pending 2D-DMA transactions
|
||||
uint8_t tx_channel_free_mask; // Bit mask indicating the free TX channels at the moment
|
||||
uint8_t rx_channel_free_mask; // Bit mask indicating the free RX channels at the moment
|
||||
uint8_t tx_channel_reserved_mask; // Bit mask indicating the being reserved TX channels
|
||||
uint8_t rx_channel_reserved_mask; // Bit mask indicating the being reserved RX channels
|
||||
uint32_t periph_m2m_free_id_mask; // Bit mask indicating the available M2M peripheral selelction IDs at the moment
|
||||
dma2d_tx_channel_t *tx_chans[SOC_DMA2D_TX_CHANNELS_PER_GROUP]; // Handles of 2D-DMA TX channels
|
||||
dma2d_rx_channel_t *rx_chans[SOC_DMA2D_RX_CHANNELS_PER_GROUP]; // Handles of 2D-DMA RX channels
|
||||
int intr_priority; // All channels in the same group should share the same interrupt priority
|
||||
};
|
||||
|
||||
struct dma2d_channel_t {
|
||||
dma2d_group_t *group; // Which group the channel belongs to
|
||||
int channel_id; // Channel ID
|
||||
dma2d_channel_direction_t direction; // Channel direction, TX or RX
|
||||
intr_handle_t intr; // Per-channel interrupt handle
|
||||
portMUX_TYPE spinlock; // Channel level spinlock
|
||||
struct {
|
||||
dma2d_trans_t *transaction; // Pointer to the 2D-DMA transaction context that is currently being processed on the channel
|
||||
uint32_t reorder_en : 1; // This flag indicates the channel will enable reorder functionality
|
||||
uint32_t has_sibling : 1; // This flag indicates its sibling channel is also in-use
|
||||
int periph_sel_id : (DMA2D_LL_CHANNEL_PERIPH_SEL_BIT_WIDTH + 1); // This is used to record the periph_sel_id of each channel
|
||||
} status;
|
||||
};
|
||||
|
||||
struct dma2d_tx_channel_t {
|
||||
dma2d_channel_t base; // 2D-DMA channel base class
|
||||
void *user_data; // User registered DMA event data
|
||||
dma2d_event_callback_t on_desc_done; // TX DONE event callback
|
||||
};
|
||||
|
||||
struct dma2d_rx_channel_t {
|
||||
dma2d_channel_t base; // 2D-DMA channel base class
|
||||
void *user_data; // User registered DMA event data
|
||||
dma2d_event_callback_t on_recv_eof; // RX EOF event callback
|
||||
dma2d_event_callback_t on_desc_done; // RX DONE event callback
|
||||
uint32_t bundled_tx_channel_mask; // Bit mask indicating the TX channels together with the RX channel to do the transaction
|
||||
};
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
419
components/esp_hw_support/dma/include/esp_private/dma2d.h
Normal file
419
components/esp_hw_support/dma/include/esp_private/dma2d.h
Normal file
@ -0,0 +1,419 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
// DO NOT USE THESE APIS IN ANY APPLICATIONS
|
||||
// DMA2D driver is not public for end users, but for ESP-IDF developers.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
#include "esp_err.h"
|
||||
#include "hal/dma2d_types.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Type of 2D-DMA pool handle
|
||||
*/
|
||||
typedef struct dma2d_group_t *dma2d_pool_handle_t;
|
||||
|
||||
/**
|
||||
* @brief Type of 2D-DMA transaction context (queue element)
|
||||
*/
|
||||
typedef struct dma2d_trans_s dma2d_trans_t;
|
||||
|
||||
/**
|
||||
* @brief Helper macro to get the size for struct `dma2d_trans_t`
|
||||
*/
|
||||
#define SIZEOF_DMA2D_TRANS_T dma2d_get_trans_elm_size()
|
||||
|
||||
/**
|
||||
* @brief Get the size for struct `dma2d_trans_t`
|
||||
*
|
||||
* @return size_t Size of struct `dma2d_trans_t`
|
||||
*/
|
||||
size_t dma2d_get_trans_elm_size(void);
|
||||
|
||||
/**
|
||||
* @brief A collection of configuration items that used for allocating a 2D-DMA pool
|
||||
*/
|
||||
typedef struct {
|
||||
uint32_t pool_id; /*!< The ID number of the 2D-DMA pool to allocate */
|
||||
uint32_t intr_priority; /*!< 2D-DMA interrupt priority,
|
||||
if set to 0, the driver will try to allocate an interrupt with a relative low priority (1,2,3) */
|
||||
} dma2d_pool_config_t;
|
||||
|
||||
/**
|
||||
* @brief Acquire a 2D-DMA pool
|
||||
*
|
||||
* @param[in] config Pointer to a collection of configurations for the 2D-DMA pool
|
||||
* @param[out] ret_pool Returned pool handle
|
||||
* @return
|
||||
* - ESP_OK: Acquire the 2D-DMA pool successfully
|
||||
* - ESP_ERR_INVALID_ARG: Acquire the 2D-DMA pool failed because of invalid argument
|
||||
* - ESP_ERR_NO_MEM: Acquire the 2D-DMA pool failed because out of memory
|
||||
* - ESP_FAIL: Acquire the 2D-DMA pool failed because of other error
|
||||
*/
|
||||
esp_err_t dma2d_acquire_pool(const dma2d_pool_config_t *config, dma2d_pool_handle_t *ret_pool);
|
||||
|
||||
/**
|
||||
* @brief Release a 2D-DMA pool
|
||||
*
|
||||
* @warning Upper driver should make sure there is no pending transaction (enqueued by the driver, but haven't be
|
||||
* processed) before calling this function.
|
||||
*
|
||||
* @param[in] dma2d_pool 2D-DMA pool handle, allocated by `dma2d_acquire_pool`
|
||||
* @return
|
||||
* - ESP_OK: Release the 2D-DMA pool successfully
|
||||
* - ESP_ERR_INVALID_ARG: Release the 2D-DMA pool failed because of invalid argument
|
||||
* - ESP_ERR_NOT_ALLOWED: Release the 2D-DMA pool failed because there is pending transactions in the pool,
|
||||
* pool can not be destroyed
|
||||
*/
|
||||
esp_err_t dma2d_release_pool(dma2d_pool_handle_t dma2d_pool);
|
||||
|
||||
/**
|
||||
* @brief Type of 2D-DMA channel handle
|
||||
*/
|
||||
typedef struct dma2d_channel_t *dma2d_channel_handle_t;
|
||||
|
||||
/**
|
||||
* @brief Struct to save the necessary information of a 2D-DMA channel for upper drivers to configure the channels
|
||||
*/
|
||||
typedef struct {
|
||||
dma2d_channel_direction_t dir; /*!< Direction of the DMA channel */
|
||||
dma2d_channel_handle_t chan; /*!< Handle of the DMA channel */
|
||||
} dma2d_trans_channel_info_t;
|
||||
|
||||
/**
|
||||
* @brief Callback function to start a 2D-DMA transaction. This callback is being called when all necessary channels to
|
||||
* do the transaction have been acquired.
|
||||
*
|
||||
* Inside this function, usually configure the DMA channels, and then call `dma2d_start` to start the transaction.
|
||||
*
|
||||
* @note This function could run in current thread, or in other threads, or in ISR context (needs to follow ISR rules!)
|
||||
*
|
||||
* @param[in] num_chans Number of DMA channels acquired for the transaction
|
||||
* @param[in] dma2d_chans List of the channels acquired
|
||||
* @param[in] user_config User registered data (usually dma channel configurations) from `dma2d_trans_config_t::user_config` which is passed into `dma2d_enqueue`
|
||||
*
|
||||
* @return Whether a task switch is needed after the callback function returns,
|
||||
* this is usually due to the callback wakes up some high priority task.
|
||||
*/
|
||||
typedef bool (*dma2d_trans_callback_t)(uint32_t num_chans, const dma2d_trans_channel_info_t *dma2d_chans, void *user_config);
|
||||
|
||||
/**
|
||||
* @brief 2D-DMA channel special function flags
|
||||
*
|
||||
* These flags are supposed to be used to specify the 2D-DMA channel requirements for a transaction.
|
||||
*/
|
||||
#define DMA2D_CHANNEL_FUNCTION_FLAG_TX_REORDER (1 << 0) /*!< TX channel that has reorder functionality */
|
||||
#define DMA2D_CHANNEL_FUNCTION_FLAG_RX_REORDER (1 << 1) /*!< RX channel that has reorder functionality */
|
||||
#define DMA2D_CHANNEL_FUNCTION_FLAG_TX_CSC (1 << 2) /*!< TX channel that has color space conversion functionality */
|
||||
#define DMA2D_CHANNEL_FUNCTION_FLAG_RX_CSC (1 << 3) /*!< RX channel that has color space conversion functionality */
|
||||
#define DMA2D_CHANNEL_FUNCTION_FLAG_SIBLING (1 << 4) /*!< TX and RX channel with same channel ID */
|
||||
|
||||
/**
|
||||
* @brief A collection of configuration items for a 2D-DMA transaction
|
||||
*/
|
||||
typedef struct {
|
||||
uint32_t tx_channel_num; /*!< Number of 2D-DMA TX channels required */
|
||||
uint32_t rx_channel_num; /*!< Number of 2D-DMA RX channels required */
|
||||
|
||||
// Special function requirements for channels
|
||||
uint32_t channel_flags; /*!< Bitwise OR of `DMA2D_CHANNEL_FUNCTION_FLAG_*` flags indicating the required functions on the channels for the transaction */
|
||||
|
||||
// Specified to use reserved channels
|
||||
uint32_t specified_tx_channel_mask; /*!< Bit mask of the specific TX channels to be used, the specified TX channels should have been reserved */
|
||||
uint32_t specified_rx_channel_mask; /*!< Bit mask of the specific RX channels to be used, the specified RX channels should have been reserved */
|
||||
|
||||
dma2d_trans_callback_t on_job_picked; /*!< Callback function to be called when all necessary channels to do the transaction have been acquired */
|
||||
void *user_config; /*!< User registered data to be passed into `on_job_picked` callback */
|
||||
} dma2d_trans_config_t;
|
||||
|
||||
/**
|
||||
* @brief Enqueue a 2D-DMA transaction to be picked up by a certain 2D-DMA pool
|
||||
*
|
||||
* @param[in] dma2d_pool 2D-DMA pool handle, allocated by `dma2d_acquire_pool`
|
||||
* @param[in] trans_desc Pointer to a collection of configurations for a transaction
|
||||
* The context must exist at least until `on_job_picked` callback function is called.
|
||||
* @param[in] trans_placeholder Address to the memory for storing this transaction context
|
||||
* Caller must malloc a placeholder for storing the 2D-DMA transaction, and pass it into the function.
|
||||
* Size of the placeholder can be get from `SIZEOF_DMA2D_TRANS_T` macro.
|
||||
* Freeing the 2D-DMA transaction placeholder should also be taken care by the upper driver.
|
||||
* It can be freed when `on_job_picked` callback function is called or anytime later.
|
||||
* @return
|
||||
* - ESP_OK: Enqueue the 2D-DMA transaction successfully
|
||||
* - ESP_ERR_INVALID_ARG: Enqueue the 2D-DMA transaction failed because of invalid argument
|
||||
*/
|
||||
esp_err_t dma2d_enqueue(dma2d_pool_handle_t dma2d_pool, const dma2d_trans_config_t *trans_desc, dma2d_trans_t *trans_placeholder);
|
||||
|
||||
/**
|
||||
* @brief Force end an in-flight 2D-DMA transaction
|
||||
*
|
||||
* This API is useful when the error was caused by the DMA consumer (such as JPEG). The error can only be detected
|
||||
* by the consumer module, and the error info will only be propagated to the consumer driver. The 2D-DMA channels being
|
||||
* involved to transfer the data has no way to be informed about the error at its upstream, it will keep waiting for
|
||||
* the data.
|
||||
*
|
||||
* Therefore, when the consumer driver is doing the error handling, it is required to call this API to end the on-going
|
||||
* transaction and release the taken TX and RX channels. It will stop and free the TX and RX channels that are bundled
|
||||
* together to process the transaction.
|
||||
*
|
||||
* @param[in] trans Pointer to the 2D-DMA transaction context
|
||||
* @param[out] need_yield Pointer to a status flag to record whether a task switch is needed if this API is being called in an ISR
|
||||
* @return
|
||||
* - ESP_OK: Force end an in-flight transaction successfully
|
||||
* - ESP_ERR_INVALID_ARG: Force end failed because of invalid argument
|
||||
* - ESP_ERR_INVALID_STATE: Force end failed because the transaction is not yet in-flight
|
||||
*/
|
||||
esp_err_t dma2d_force_end(dma2d_trans_t *trans, bool *need_yield);
|
||||
|
||||
|
||||
/*********************************************** DMA CHANNEL OPERATIONS ***********************************************/
|
||||
|
||||
/**
|
||||
* @brief Type of 2D-DMA engine trigger
|
||||
*/
|
||||
typedef struct {
|
||||
dma2d_trigger_peripheral_t periph; /*!< Target peripheral which will trigger DMA operations */
|
||||
int periph_sel_id; /*!< Peripheral selection ID. Supported IDs are listed in `soc/dma2d_channel.h` */
|
||||
} dma2d_trigger_t;
|
||||
|
||||
/**
|
||||
* @brief Connect 2D-DMA channel to trigger peripheral, and configure all other channel settings to a certain state (the channel will be reset first)
|
||||
*
|
||||
* Usually only to be called in `on_job_picked` callback, and is the first step to do inside the callback, since it resets other configurations to a default mode.
|
||||
*
|
||||
* @param[in] dma2d_chan 2D-DMA channel handle, get from the `on_job_picked` callback input argument `dma2d_chans`
|
||||
* @param[in] trig_periph 2D-DMA trigger peripheral
|
||||
* @return
|
||||
* - ESP_OK: Connect 2D-DMA channel successfully
|
||||
* - ESP_ERR_INVALID_ARG: Connect 2D-DMA channel failed because of invalid argument
|
||||
*/
|
||||
esp_err_t dma2d_connect(dma2d_channel_handle_t dma2d_chan, const dma2d_trigger_t *trig_periph);
|
||||
|
||||
/**
|
||||
* @brief A collection of strategy items that each 2D-DMA channel could apply
|
||||
*/
|
||||
typedef struct {
|
||||
bool owner_check; /*!< If set / clear, DMA channel enables / disables checking owner validity */
|
||||
bool auto_update_desc; /*!< If set / clear, DMA channel enables / disables hardware to update descriptor automatically (TX channel only) */
|
||||
bool eof_till_data_popped; /*!< If set, EOF flag is generated until the data needs to read has been popped from the DMA FIFO (TX channel only) */
|
||||
} dma2d_strategy_config_t;
|
||||
|
||||
/**
|
||||
* @brief Apply channel strategy for 2D-DMA channel
|
||||
*
|
||||
* Usually only to be called in `on_job_picked` callback.
|
||||
*
|
||||
* @param[in] dma2d_chan 2D-DMA channel handle, get from the `on_job_picked` callback input argument `dma2d_chans`
|
||||
* @param[in] config Configuration of 2D-DMA channel strategy
|
||||
* @return
|
||||
* - ESP_OK: Apply channel strategy successfully
|
||||
* - ESP_ERR_INVALID_ARG: Apply channel strategy failed because of invalid argument
|
||||
*/
|
||||
esp_err_t dma2d_apply_strategy(dma2d_channel_handle_t dma2d_chan, const dma2d_strategy_config_t *config);
|
||||
|
||||
/**
|
||||
* @brief A collection of transfer ability items that each 2D-DMA channel could apply to improve transfer efficiency
|
||||
*
|
||||
* @note The 2D-DMA driver has no knowledge about the DMA buffer (address and size) used by upper layer.
|
||||
* So it's the responsibility of the **upper layer** to take care of the buffer address and size.
|
||||
* Usually RX buffer at least requires 4-byte alignment to avoid overwriting other data by DMA write PSRAM process
|
||||
* or its data being overwritten.
|
||||
*/
|
||||
typedef struct {
|
||||
bool desc_burst_en; /*!< If set / clear, DMA channel enables / disables burst reading descriptor link */
|
||||
dma2d_data_burst_length_t data_burst_length; /*!< Configure the DMA channel burst reading data length */
|
||||
dma2d_macro_block_size_t mb_size; /*!< Configure the DMA channel macro block size (only useful in DMA2D_DESCRIPTOR_BLOCK_RW_MODE_MULTIPLE mode) */
|
||||
} dma2d_transfer_ability_t;
|
||||
|
||||
/**
|
||||
* @brief Configure 2D-DMA channel transfer ability for transfer efficiency
|
||||
*
|
||||
* Usually only to be called in `on_job_picked` callback.
|
||||
*
|
||||
* @param[in] dma2d_chan 2D-DMA channel handle, get from the `on_job_picked` callback input argument `dma2d_chans`
|
||||
* @param[in] ability Configuration of 2D-DMA channel transfer ability
|
||||
* @return
|
||||
* - ESP_OK: Set channel transfer ability successfully
|
||||
* - ESP_ERR_INVALID_ARG: Set channel transfer ability failed because of invalid argument
|
||||
*/
|
||||
esp_err_t dma2d_set_transfer_ability(dma2d_channel_handle_t dma2d_chan, const dma2d_transfer_ability_t *ability);
|
||||
|
||||
/**
|
||||
* @brief A collection of color space conversion (CSC) items that each 2D-DMA channel could apply
|
||||
*/
|
||||
typedef struct {
|
||||
union {
|
||||
dma2d_csc_tx_option_t tx_csc_option; /*!< TX direction (into DMA) pixel format conversion option */
|
||||
dma2d_csc_rx_option_t rx_csc_option; /*!< RX direction (out from DMA) pixel format conversion option */
|
||||
};
|
||||
dma2d_scramble_order_t pre_scramble; /*!< DMA channel data scramble order before color conversion */
|
||||
dma2d_scramble_order_t post_scramble; /*!< DMA channel data scramble order after color conversion (only available for RX channels) */
|
||||
} dma2d_csc_config_t;
|
||||
|
||||
/**
|
||||
* @brief Configure color space conversion setting for 2D-DMA channel
|
||||
*
|
||||
* Usually only to be called in `on_job_picked` callback.
|
||||
*
|
||||
* @param[in] dma2d_chan 2D-DMA channel handle, get from the `on_job_picked` callback input argument `dma2d_chans`
|
||||
* @param[in] config Configuration of 2D-DMA channel color space conversion
|
||||
* @return
|
||||
* - ESP_OK: Configure DMA color space conversion successfully
|
||||
* - ESP_ERR_INVALID_ARG: Configure DMA color space conversion failed because of invalid argument
|
||||
*/
|
||||
esp_err_t dma2d_configure_color_space_conversion(dma2d_channel_handle_t dma2d_chan, const dma2d_csc_config_t *config);
|
||||
|
||||
/**
|
||||
* @brief Type of 2D-DMA event data
|
||||
*/
|
||||
typedef struct {
|
||||
union {
|
||||
intptr_t rx_eof_desc_addr; /*!< EOF descriptor address of RX channel */
|
||||
intptr_t tx_eof_desc_addr; /*!< EOF descriptor address of TX channel */
|
||||
};
|
||||
dma2d_trans_t *transaction; /*!< Pointer to the transaction context processed */
|
||||
} dma2d_event_data_t;
|
||||
|
||||
/**
|
||||
* @brief Type of 2D-DMA event callback
|
||||
*
|
||||
* @param dma2d_chan 2D-DMA channel handle. Depends on the callback events, sometimes will pass NULL to this parameter.
|
||||
* @param event_data 2D-DMA event data
|
||||
* @param user_data User registered data from `dma2d_register_tx/rx_event_callbacks`
|
||||
*
|
||||
* @return Whether a task switch is needed after the callback function returns,
|
||||
* this is usually due to the callback wakes up some high priority task.
|
||||
*/
|
||||
typedef bool (*dma2d_event_callback_t)(dma2d_channel_handle_t dma2d_chan, dma2d_event_data_t *event_data, void *user_data);
|
||||
|
||||
/**
|
||||
* @brief Group of supported 2D-DMA TX callbacks
|
||||
* @note The callbacks are all running under ISR environment
|
||||
*/
|
||||
typedef struct {
|
||||
dma2d_event_callback_t on_desc_done; /*!< Invoked when TX engine completes processing all data in a descriptor.
|
||||
When TX_DONE interrupt gets triggered but not EOF, it is considered
|
||||
to be still in the middle of a complete transaction (partially done),
|
||||
you are allowed to configure 2D-DMA channel hardware/descriptor in this
|
||||
callback, and let the channels start running again */
|
||||
} dma2d_tx_event_callbacks_t;
|
||||
|
||||
/**
|
||||
* @brief Group of supported 2D-DMA RX callbacks
|
||||
* @note The callbacks are all running under ISR environment
|
||||
*
|
||||
* Users should be clear on the unique responsibility of each callback when writing the callback functions, such as
|
||||
* where to free the transaction memory.
|
||||
*/
|
||||
typedef struct {
|
||||
dma2d_event_callback_t on_recv_eof; /*!< Invoked when RX engine meets EOF descriptor.
|
||||
Note that in this callback, RX channel handle will not be given.
|
||||
This is because at the moment of `on_recv_eof` callback is called,
|
||||
the channels are returned to the pool and may have already been used to start another new transaction */
|
||||
dma2d_event_callback_t on_desc_done; /*!< Invoked when RX engine completes processing all data in a descriptor.
|
||||
When RX_DONE interrupt gets triggered but not EOF, it is considered
|
||||
to be still in the middle of a complete transaction (partially done),
|
||||
you are allowed to configure 2D-DMA channel hardware/descriptor in this
|
||||
callback, and let the channels start running again */
|
||||
} dma2d_rx_event_callbacks_t;
|
||||
|
||||
/**
|
||||
* @brief Set 2D-DMA event callbacks for TX channel
|
||||
*
|
||||
* Usually only to be called in `on_job_picked` callback.
|
||||
*
|
||||
* @param[in] dma2d_chan 2D-DMA TX channel handle, get from the `on_job_picked` callback input argument `dma2d_chans`
|
||||
* @param[in] cbs Group of callback functions
|
||||
* @param[in] user_data User data, which will be passed to callback functions directly
|
||||
* @return
|
||||
* - ESP_OK: Set event callbacks successfully
|
||||
* - ESP_ERR_INVALID_ARG: Set event callbacks failed because of invalid argument
|
||||
*/
|
||||
esp_err_t dma2d_register_tx_event_callbacks(dma2d_channel_handle_t dma2d_chan, dma2d_tx_event_callbacks_t *cbs, void *user_data);
|
||||
|
||||
/**
|
||||
* @brief Set 2D-DMA event callbacks for RX channel
|
||||
*
|
||||
* Usually only to be called in `on_job_picked` callback.
|
||||
*
|
||||
* @param[in] dma2d_chan 2D-DMA RX channel handle, get from the `on_job_picked` callback input argument `dma2d_chans`
|
||||
* @param[in] cbs Group of callback functions
|
||||
* @param[in] user_data User data, which will be passed to callback functions directly
|
||||
* @return
|
||||
* - ESP_OK: Set event callbacks successfully
|
||||
* - ESP_ERR_INVALID_ARG: Set event callbacks failed because of invalid argument
|
||||
*/
|
||||
esp_err_t dma2d_register_rx_event_callbacks(dma2d_channel_handle_t dma2d_chan, dma2d_rx_event_callbacks_t *cbs, void *user_data);
|
||||
|
||||
/**
|
||||
* @brief Set descriptor address for 2D-DMA channel
|
||||
*
|
||||
* Usually only to be called in `on_job_picked` callback.
|
||||
*
|
||||
* @param[in] dma2d_chan 2D-DMA channel handle, get from the `on_job_picked` callback input argument `dma2d_chans`
|
||||
* @param[in] desc_base_addr Base address of descriptors
|
||||
* @return
|
||||
* - ESP_OK: Set 2D-DMA descriptor addr successfully
|
||||
* - ESP_ERR_INVALID_ARG: Set 2D-DMA descriptor addr failed because of invalid argument
|
||||
*/
|
||||
esp_err_t dma2d_set_desc_addr(dma2d_channel_handle_t dma2d_chan, intptr_t desc_base_addr);
|
||||
|
||||
/**
|
||||
* @brief Start engine for 2D-DMA channel
|
||||
*
|
||||
* Usually only to be called in `on_job_picked` callback.
|
||||
*
|
||||
* @param[in] dma2d_chan 2D-DMA channel handle, get from the `on_job_picked` callback input argument `dma2d_chans`
|
||||
* @return
|
||||
* - ESP_OK: Start 2D-DMA engine successfully
|
||||
* - ESP_ERR_INVALID_ARG: Start 2D-DMA engine failed because of invalid argument
|
||||
*/
|
||||
esp_err_t dma2d_start(dma2d_channel_handle_t dma2d_chan);
|
||||
|
||||
/**
|
||||
* @brief Stop engine for 2D-DMA channel
|
||||
*
|
||||
* Usually to be called in ISR context.
|
||||
*
|
||||
* @param[in] dma2d_chan 2D-DMA channel handle
|
||||
* @return
|
||||
* - ESP_OK: Stop 2D-DMA engine successfully
|
||||
* - ESP_ERR_INVALID_ARG: Stop 2D-DMA engine failed because of invalid argument
|
||||
*/
|
||||
esp_err_t dma2d_stop(dma2d_channel_handle_t dma2d_chan);
|
||||
|
||||
/**
|
||||
* @brief Make the appended descriptors be aware to the 2D-DMA engine
|
||||
*
|
||||
* Usually to be called in ISR context.
|
||||
*
|
||||
* @param[in] dma2d_chan 2D-DMA channel handle
|
||||
* @return
|
||||
* - ESP_OK: Send append command to 2D-DMA engine successfully
|
||||
* - ESP_ERR_INVALID_ARG: Send append command to 2D-DMA engine failed because of invalid argument
|
||||
*/
|
||||
esp_err_t dma2d_append(dma2d_channel_handle_t dma2d_chan);
|
||||
|
||||
/**
|
||||
* @brief Reset engine for 2D-DMA channel
|
||||
*
|
||||
* Usually to be called in ISR context.
|
||||
*
|
||||
* @param[in] dma2d_chan 2D-DMA channel handle
|
||||
* @return
|
||||
* - ESP_OK: Reset 2D-DMA engine successfully
|
||||
* - ESP_ERR_INVALID_ARG: Reset 2D-DMA engine failed because of invalid argument
|
||||
*/
|
||||
esp_err_t dma2d_reset(dma2d_channel_handle_t dma2d_chan);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
@ -82,3 +82,37 @@ entries:
|
||||
if DW_GDMA_SETTER_FUNC_IN_IRAM = y:
|
||||
dw_gdma: dw_gdma_channel_set_block_markers (noflash)
|
||||
dw_gdma: dw_gdma_lli_set_block_markers (noflash)
|
||||
|
||||
[mapping:dma2d_driver]
|
||||
archive: libesp_hw_support.a
|
||||
entries:
|
||||
# performance optimization, always put the 2D-DMA default interrupt handler in IRAM
|
||||
if SOC_DMA2D_SUPPORTED = y:
|
||||
dma2d: acquire_free_channels_for_trans (noflash)
|
||||
dma2d: free_up_channels (noflash)
|
||||
dma2d: _dma2d_default_tx_isr (noflash)
|
||||
dma2d: _dma2d_default_rx_isr (noflash)
|
||||
dma2d: dma2d_default_isr (noflash)
|
||||
|
||||
# put 2D-DMA operation functions in IRAM
|
||||
if DMA2D_OPERATION_FUNC_IN_IRAM = y:
|
||||
dma2d: dma2d_connect (noflash)
|
||||
dma2d: dma2d_register_tx_event_callbacks (noflash)
|
||||
dma2d: dma2d_register_rx_event_callbacks (noflash)
|
||||
dma2d: dma2d_set_desc_addr (noflash)
|
||||
dma2d: dma2d_start (noflash)
|
||||
dma2d: dma2d_stop (noflash)
|
||||
dma2d: dma2d_append (noflash)
|
||||
dma2d: dma2d_reset (noflash)
|
||||
dma2d: dma2d_force_end (noflash)
|
||||
dma2d: dma2d_apply_strategy (noflash)
|
||||
dma2d: dma2d_set_transfer_ability (noflash)
|
||||
dma2d: dma2d_configure_color_space_conversion (noflash)
|
||||
dma2d: dma2d_enqueue (noflash)
|
||||
|
||||
[mapping:dma2d_hal]
|
||||
archive: libhal.a
|
||||
entries:
|
||||
if DMA2D_ISR_IRAM_SAFE = y || DMA2D_OPERATION_FUNC_IN_IRAM = y:
|
||||
dma2d_hal: dma2d_hal_tx_reset_channel (noflash)
|
||||
dma2d_hal: dma2d_hal_rx_reset_channel (noflash)
|
||||
|
@ -8,6 +8,12 @@ components/esp_hw_support/test_apps/dma:
|
||||
depends_filepatterns:
|
||||
- components/esp_hw_support/dma/**/*
|
||||
|
||||
components/esp_hw_support/test_apps/dma2d:
|
||||
disable:
|
||||
- if: SOC_DMA2D_SUPPORTED != 1
|
||||
depends_filepatterns:
|
||||
- components/esp_hw_support/dma/**/*
|
||||
|
||||
components/esp_hw_support/test_apps/esp_hw_support_unity_tests:
|
||||
disable:
|
||||
- if: SOC_GPSPI_SUPPORTED != 1
|
||||
|
10
components/esp_hw_support/test_apps/dma2d/CMakeLists.txt
Normal file
10
components/esp_hw_support/test_apps/dma2d/CMakeLists.txt
Normal file
@ -0,0 +1,10 @@
|
||||
# This is the project CMakeLists.txt file for the test subproject
|
||||
cmake_minimum_required(VERSION 3.16)
|
||||
|
||||
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||
|
||||
# "Trim" the build. Include the minimal set of components, main, and anything it depends on. We also depend on esp_psram
|
||||
# as we set CONFIG_SPIRAM_... options.
|
||||
set(COMPONENTS main esp_psram)
|
||||
|
||||
project(dma2d_test)
|
2
components/esp_hw_support/test_apps/dma2d/README.md
Normal file
2
components/esp_hw_support/test_apps/dma2d/README.md
Normal file
@ -0,0 +1,2 @@
|
||||
| Supported Targets | ESP32-P4 |
|
||||
| ----------------- | -------- |
|
@ -0,0 +1,10 @@
|
||||
set(srcs "test_app_main.c"
|
||||
"test_dma2d.c"
|
||||
"dma2d_test_utils.c")
|
||||
|
||||
# In order for the cases defined by `TEST_CASE` to be linked into the final elf,
|
||||
# the component can be registered as WHOLE_ARCHIVE
|
||||
idf_component_register(SRCS ${srcs}
|
||||
INCLUDE_DIRS "."
|
||||
PRIV_REQUIRES unity esp_mm
|
||||
WHOLE_ARCHIVE)
|
@ -0,0 +1,162 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include "unity.h"
|
||||
#include "dma2d_test_utils.h"
|
||||
#include "esp_private/dma2d.h"
|
||||
#include "soc/dma2d_channel.h"
|
||||
#include "esp_heap_caps.h"
|
||||
#include "esp_memory_utils.h"
|
||||
#include "esp_check.h"
|
||||
#include "esp_err.h"
|
||||
#include "esp_attr.h"
|
||||
#include "esp_log.h"
|
||||
|
||||
__attribute__((unused)) static const char *TAG = "dma2d_m2m";
|
||||
|
||||
#if CONFIG_DMA2D_OPERATION_FUNC_IN_IRAM || CONFIG_DMA2D_ISR_IRAM_SAFE
|
||||
#define DMA2D_M2M_MEM_ALLOC_CAPS (MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT)
|
||||
#else
|
||||
#define DMA2D_M2M_MEM_ALLOC_CAPS MALLOC_CAP_DEFAULT
|
||||
#endif
|
||||
|
||||
#if CONFIG_DMA2D_ISR_IRAM_SAFE
|
||||
#define DMA2D_M2M_ATTR IRAM_ATTR
|
||||
#else
|
||||
#define DMA2D_M2M_ATTR
|
||||
#endif
|
||||
|
||||
typedef dma2d_m2m_trans_config_t dma2d_m2m_trans_desc_t;
|
||||
|
||||
typedef struct {
|
||||
dma2d_m2m_trans_desc_t m2m_trans_desc;
|
||||
dma2d_trans_config_t dma_chan_desc;
|
||||
uint32_t dma_trans_placeholder_head; /* Head of the memory for storing the 2D-DMA transaction elm */
|
||||
} dma2d_m2m_transaction_t;
|
||||
|
||||
static dma2d_pool_handle_t dma2d_pool_handle;
|
||||
|
||||
esp_err_t dma2d_m2m_init(void)
|
||||
{
|
||||
dma2d_pool_config_t dma2d_pool_config = {
|
||||
.pool_id = 0,
|
||||
};
|
||||
return dma2d_acquire_pool(&dma2d_pool_config, &dma2d_pool_handle);
|
||||
}
|
||||
|
||||
esp_err_t dma2d_m2m_deinit(void)
|
||||
{
|
||||
return dma2d_release_pool(dma2d_pool_handle);
|
||||
}
|
||||
|
||||
static bool DMA2D_M2M_ATTR dma2d_m2m_transaction_done_cb(dma2d_channel_handle_t dma2d_chan, dma2d_event_data_t *event_data, void *user_data)
|
||||
{
|
||||
bool need_yield = false;
|
||||
dma2d_m2m_transaction_t *trans_config = (dma2d_m2m_transaction_t *)user_data;
|
||||
dma2d_m2m_trans_desc_t *m2m_trans_desc = &trans_config->m2m_trans_desc;
|
||||
|
||||
if (m2m_trans_desc->trans_eof_cb) {
|
||||
need_yield |= m2m_trans_desc->trans_eof_cb(m2m_trans_desc->user_data);
|
||||
}
|
||||
|
||||
free(trans_config);
|
||||
|
||||
return need_yield;
|
||||
}
|
||||
|
||||
static bool DMA2D_M2M_ATTR dma2d_m2m_transaction_on_picked(uint32_t channel_num, const dma2d_trans_channel_info_t *dma2d_chans, void *user_config)
|
||||
{
|
||||
assert(channel_num == 2 && dma2d_chans && user_config);
|
||||
dma2d_m2m_transaction_t *trans_config = (dma2d_m2m_transaction_t *)user_config;
|
||||
dma2d_m2m_trans_desc_t *m2m_trans_desc = &trans_config->m2m_trans_desc;
|
||||
|
||||
// Get the required 2D-DMA channel handles
|
||||
uint32_t dma_tx_chan_idx = 0;
|
||||
uint32_t dma_rx_chan_idx = 1;
|
||||
if (dma2d_chans[0].dir == DMA2D_CHANNEL_DIRECTION_RX) {
|
||||
dma_tx_chan_idx = 1;
|
||||
dma_rx_chan_idx = 0;
|
||||
}
|
||||
dma2d_channel_handle_t dma_tx_chan = dma2d_chans[dma_tx_chan_idx].chan;
|
||||
dma2d_channel_handle_t dma_rx_chan = dma2d_chans[dma_rx_chan_idx].chan;
|
||||
|
||||
dma2d_trigger_t trig_periph = {
|
||||
.periph = DMA2D_TRIG_PERIPH_M2M,
|
||||
.periph_sel_id = SOC_DMA2D_TRIG_PERIPH_M2M_TX,
|
||||
};
|
||||
dma2d_connect(dma_tx_chan, &trig_periph);
|
||||
trig_periph.periph_sel_id = SOC_DMA2D_TRIG_PERIPH_M2M_RX;
|
||||
dma2d_connect(dma_rx_chan, &trig_periph);
|
||||
|
||||
if (m2m_trans_desc->transfer_ability_config) {
|
||||
dma2d_set_transfer_ability(dma_tx_chan, m2m_trans_desc->transfer_ability_config);
|
||||
dma2d_set_transfer_ability(dma_rx_chan, m2m_trans_desc->transfer_ability_config);
|
||||
}
|
||||
|
||||
if (m2m_trans_desc->tx_strategy_config) {
|
||||
dma2d_apply_strategy(dma_tx_chan, m2m_trans_desc->tx_strategy_config);
|
||||
}
|
||||
if (m2m_trans_desc->rx_strategy_config) {
|
||||
dma2d_apply_strategy(dma_rx_chan, m2m_trans_desc->rx_strategy_config);
|
||||
}
|
||||
|
||||
if (m2m_trans_desc->tx_csc_config) {
|
||||
dma2d_configure_color_space_conversion(dma_tx_chan, m2m_trans_desc->tx_csc_config);
|
||||
}
|
||||
if (m2m_trans_desc->rx_csc_config) {
|
||||
dma2d_configure_color_space_conversion(dma_rx_chan, m2m_trans_desc->rx_csc_config);
|
||||
}
|
||||
|
||||
dma2d_rx_event_callbacks_t dma_cbs = {
|
||||
.on_recv_eof = dma2d_m2m_transaction_done_cb,
|
||||
};
|
||||
dma2d_register_rx_event_callbacks(dma_rx_chan, &dma_cbs, (void *)trans_config);
|
||||
|
||||
dma2d_set_desc_addr(dma_tx_chan, m2m_trans_desc->tx_desc_base_addr);
|
||||
dma2d_set_desc_addr(dma_rx_chan, m2m_trans_desc->rx_desc_base_addr);
|
||||
|
||||
dma2d_start(dma_tx_chan);
|
||||
dma2d_start(dma_rx_chan);
|
||||
|
||||
// No need to yield
|
||||
return false;
|
||||
}
|
||||
|
||||
esp_err_t dma2d_m2m(const dma2d_m2m_trans_config_t *trans_config)
|
||||
{
|
||||
#if CONFIG_DMA2D_ISR_IRAM_SAFE
|
||||
if (trans_config->trans_eof_cb) {
|
||||
ESP_RETURN_ON_FALSE(esp_ptr_in_iram(trans_config->trans_eof_cb),
|
||||
ESP_ERR_INVALID_ARG, TAG, "trans_eof_cb not in IRAM");
|
||||
}
|
||||
if (trans_config->user_data) {
|
||||
ESP_RETURN_ON_FALSE(esp_ptr_internal(trans_config->user_data),
|
||||
ESP_ERR_INVALID_ARG, TAG, "user context not in internal RAM");
|
||||
}
|
||||
#endif
|
||||
dma2d_m2m_transaction_t *dma2d_m2m_trans = (dma2d_m2m_transaction_t *)heap_caps_calloc(1, sizeof(dma2d_m2m_transaction_t) + SIZEOF_DMA2D_TRANS_T, DMA2D_M2M_MEM_ALLOC_CAPS);
|
||||
TEST_ASSERT_NOT_NULL(dma2d_m2m_trans);
|
||||
|
||||
dma2d_m2m_trans->dma_chan_desc.tx_channel_num = 1;
|
||||
dma2d_m2m_trans->dma_chan_desc.rx_channel_num = 1;
|
||||
dma2d_m2m_trans->dma_chan_desc.channel_flags = DMA2D_CHANNEL_FUNCTION_FLAG_SIBLING;
|
||||
dma2d_m2m_trans->dma_chan_desc.channel_flags |= (trans_config->tx_csc_config == NULL) ? 0 : DMA2D_CHANNEL_FUNCTION_FLAG_TX_CSC;
|
||||
dma2d_m2m_trans->dma_chan_desc.channel_flags |= (trans_config->rx_csc_config == NULL) ? 0 : DMA2D_CHANNEL_FUNCTION_FLAG_RX_CSC;
|
||||
dma2d_m2m_trans->dma_chan_desc.specified_tx_channel_mask = 0;
|
||||
dma2d_m2m_trans->dma_chan_desc.specified_rx_channel_mask = 0;
|
||||
|
||||
memcpy(&dma2d_m2m_trans->m2m_trans_desc, trans_config, sizeof(dma2d_m2m_trans_config_t));
|
||||
|
||||
dma2d_m2m_trans->dma_chan_desc.user_config = (void *)dma2d_m2m_trans;
|
||||
dma2d_m2m_trans->dma_chan_desc.on_job_picked = dma2d_m2m_transaction_on_picked;
|
||||
|
||||
esp_err_t ret = dma2d_enqueue(dma2d_pool_handle, &dma2d_m2m_trans->dma_chan_desc, (dma2d_trans_t *)&dma2d_m2m_trans->dma_trans_placeholder_head);
|
||||
if (ret != ESP_OK) {
|
||||
free(dma2d_m2m_trans);
|
||||
}
|
||||
return ret;
|
||||
}
|
@ -0,0 +1,63 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "esp_private/dma2d.h"
|
||||
#include "esp_err.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Initialize the 2D-DMA module to perform memcopy operations
|
||||
*/
|
||||
esp_err_t dma2d_m2m_init(void);
|
||||
|
||||
/**
|
||||
* @brief De-initialize the 2D-DMA module
|
||||
*/
|
||||
esp_err_t dma2d_m2m_deinit(void);
|
||||
|
||||
/**
|
||||
* @brief Callback function when a memcopy operation is done
|
||||
*
|
||||
* @param user_data User registered data from `dma2d_m2m_trans_config_t`
|
||||
*
|
||||
* @return Whether a task switch is needed after the callback function returns,
|
||||
* this is usually due to the callback wakes up some high priority task.
|
||||
*/
|
||||
typedef bool (*dma2d_m2m_trans_eof_callback_t)(void *user_data);
|
||||
|
||||
/**
|
||||
* @brief A collection of configuration items for perferming a memcopy operation with 2D-DMA
|
||||
*/
|
||||
typedef struct {
|
||||
intptr_t tx_desc_base_addr; /*!< 2D-DMA TX descriptor address */
|
||||
intptr_t rx_desc_base_addr; /*!< 2D-DMA RX descriptor address */
|
||||
dma2d_m2m_trans_eof_callback_t trans_eof_cb; /*!< Callback function to be called when the memcopy operation is done */
|
||||
void *user_data; /*!< User registered data to be passed into `trans_eof_cb` callback */
|
||||
dma2d_transfer_ability_t *transfer_ability_config; /*!< Pointer to a collection of 2D-DMA transfer ability configuration */
|
||||
dma2d_strategy_config_t *tx_strategy_config; /*!< Pointer to a collection of 2D-DMA TX strategy configuration */
|
||||
dma2d_strategy_config_t *rx_strategy_config; /*!< Pointer to a collection of 2D-DMA RX strategy configuration */
|
||||
dma2d_csc_config_t *tx_csc_config; /*!< Pointer to a collection of 2D-DMA TX color space conversion configuration */
|
||||
dma2d_csc_config_t *rx_csc_config; /*!< Pointer to a collection of 2D-DMA RX color space conversion configuration */
|
||||
} dma2d_m2m_trans_config_t;
|
||||
|
||||
/**
|
||||
* @brief Do a memcopy operation with 2D-DMA module
|
||||
*
|
||||
* @param trans_config Pointer to a collection of configurations for the memcopy operation
|
||||
* @return
|
||||
* - ESP_OK: Enqueue the transaction to 2D-DMA pool successfully
|
||||
* - ESP_ERR_INVALID_ARG: Enqueue the transaction to 2D-DMA pool failed because of invalid argument
|
||||
*/
|
||||
esp_err_t dma2d_m2m(const dma2d_m2m_trans_config_t *trans_config);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
@ -0,0 +1,36 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include "unity.h"
|
||||
#include "unity_test_runner.h"
|
||||
#include "esp_heap_caps.h"
|
||||
#include "unity_test_utils.h"
|
||||
|
||||
// Some resources are lazy allocated in the driver, the threshold is left for that case
|
||||
#define TEST_MEMORY_LEAK_THRESHOLD (200)
|
||||
|
||||
void setUp(void)
|
||||
{
|
||||
unity_utils_record_free_mem();
|
||||
}
|
||||
|
||||
void tearDown(void)
|
||||
{
|
||||
esp_reent_cleanup(); //clean up some of the newlib's lazy allocations
|
||||
unity_utils_evaluate_leaks_direct(TEST_MEMORY_LEAK_THRESHOLD);
|
||||
}
|
||||
|
||||
void app_main(void)
|
||||
{
|
||||
printf(" ______ ___ ___ ______ _____ ______ \n");
|
||||
printf("(______) (___)_(___) (______) _(_____) (______) \n");
|
||||
printf("(_) (_) (_) (_) (_) (_)____(_) (_) _(_) (_) (_) \n");
|
||||
printf("(_) (_)(_) (_) (_) (________) _(_) (_) (_)\n");
|
||||
printf("(_)___(_) (_) (_) (_) (_) (_)___ (_)___(_) \n");
|
||||
printf("(______) (_) (_) (_) (_) (_______) (______) \n");
|
||||
|
||||
unity_run_menu();
|
||||
}
|
677
components/esp_hw_support/test_apps/dma2d/main/test_dma2d.c
Normal file
677
components/esp_hw_support/test_apps/dma2d/main/test_dma2d.c
Normal file
@ -0,0 +1,677 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/param.h>
|
||||
#include "unity.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "freertos/semphr.h"
|
||||
#include "esp_private/dma2d.h"
|
||||
#include "dma2d_test_utils.h"
|
||||
#include "hal/dma2d_types.h"
|
||||
#include "hal/color_types.h"
|
||||
#include "esp_heap_caps.h"
|
||||
#include "esp_cache.h"
|
||||
|
||||
// All test will perform `M2M_TRANS_TIMES` times memcpy transactions, utilizing all available 2D-DMA channels.
|
||||
// This tests the hardware capability of multiple 2D-DMA transactions running together, and the driver capbility of
|
||||
// transactions being send to a queue, and waiting for free channels becoming available, and being picked to start the
|
||||
// real hardware operation.
|
||||
#define M2M_TRANS_TIMES (8)
|
||||
|
||||
// Descriptor and buffer address and size should aligned to 64 bytes (the cacheline size alignment restriction) to be used by CPU
|
||||
|
||||
static dma2d_descriptor_t *tx_dsc[M2M_TRANS_TIMES];
|
||||
static dma2d_descriptor_t *rx_dsc[M2M_TRANS_TIMES];
|
||||
|
||||
static dma2d_m2m_trans_config_t m2m_trans_config[M2M_TRANS_TIMES];
|
||||
|
||||
static void dma2d_link_dscr_init(uint32_t *head, uint32_t *next, void *buf_ptr,
|
||||
uint32_t ha, uint32_t va, uint32_t hb, uint32_t vb,
|
||||
uint32_t eof, uint32_t en_2d, uint32_t pbyte, uint32_t mod,
|
||||
uint32_t bias_x, uint32_t bias_y)
|
||||
{
|
||||
dma2d_descriptor_t *dma2d = (dma2d_descriptor_t *)head;
|
||||
memset(dma2d, 0, sizeof(dma2d_descriptor_t));
|
||||
dma2d->owner = DMA2D_DESCRIPTOR_BUFFER_OWNER_DMA;
|
||||
dma2d->suc_eof = eof;
|
||||
dma2d->dma2d_en = en_2d;
|
||||
dma2d->err_eof = 0;
|
||||
dma2d->hb_length = hb;
|
||||
dma2d->vb_size = vb;
|
||||
dma2d->pbyte = pbyte;
|
||||
dma2d->ha_length = ha;
|
||||
dma2d->va_size = va;
|
||||
dma2d->mode = mod;
|
||||
dma2d->y = bias_y;
|
||||
dma2d->x = bias_x;
|
||||
dma2d->buffer = buf_ptr;
|
||||
dma2d->next = (dma2d_descriptor_t *)next;
|
||||
}
|
||||
|
||||
static bool IRAM_ATTR dma2d_m2m_suc_eof_event_cb(void *user_data)
|
||||
{
|
||||
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
|
||||
|
||||
SemaphoreHandle_t sem = (SemaphoreHandle_t)user_data;
|
||||
xSemaphoreGiveFromISR(sem, &xHigherPriorityTaskWoken);
|
||||
return (xHigherPriorityTaskWoken == pdTRUE);
|
||||
}
|
||||
|
||||
TEST_CASE("DMA2D_M2M_1D_basic", "[DMA2D]")
|
||||
{
|
||||
// Test a 16KB data block pure memcopy
|
||||
const uint32_t data_size = 16 * 1024; // unit: byte
|
||||
|
||||
memset(m2m_trans_config, 0, M2M_TRANS_TIMES * sizeof(dma2d_m2m_trans_config_t));
|
||||
TEST_ESP_OK(dma2d_m2m_init());
|
||||
|
||||
dma2d_descriptor_t *tx_link_buffer = (dma2d_descriptor_t *)heap_caps_aligned_calloc(64, M2M_TRANS_TIMES, 64, MALLOC_CAP_DEFAULT);
|
||||
dma2d_descriptor_t *rx_link_buffer = (dma2d_descriptor_t *)heap_caps_aligned_calloc(64, M2M_TRANS_TIMES, 64, MALLOC_CAP_DEFAULT);
|
||||
TEST_ASSERT_NOT_NULL(tx_link_buffer);
|
||||
TEST_ASSERT_NOT_NULL(rx_link_buffer);
|
||||
for (int i = 0; i < M2M_TRANS_TIMES; i++) {
|
||||
tx_dsc[i] = (dma2d_descriptor_t *)((uint32_t)tx_link_buffer + 64 * i);
|
||||
rx_dsc[i] = (dma2d_descriptor_t *)((uint32_t)rx_link_buffer + 64 * i);
|
||||
}
|
||||
|
||||
uint8_t *prtx;
|
||||
uint8_t *prrx;
|
||||
uint8_t *tx_buf = heap_caps_aligned_calloc(64, data_size * M2M_TRANS_TIMES, sizeof(uint8_t), MALLOC_CAP_SPIRAM);
|
||||
uint8_t *rx_buf = heap_caps_aligned_calloc(64, data_size * M2M_TRANS_TIMES, sizeof(uint8_t), MALLOC_CAP_SPIRAM);
|
||||
TEST_ASSERT_NOT_NULL(tx_buf);
|
||||
TEST_ASSERT_NOT_NULL(rx_buf);
|
||||
|
||||
dma2d_transfer_ability_t transfer_ability_config = {
|
||||
.data_burst_length = DMA2D_DATA_BURST_LENGTH_64,
|
||||
.desc_burst_en = true,
|
||||
.mb_size = DMA2D_MACRO_BLOCK_SIZE_NONE,
|
||||
};
|
||||
|
||||
SemaphoreHandle_t counting_sem = xSemaphoreCreateCounting(M2M_TRANS_TIMES, 0);
|
||||
|
||||
// Preparation
|
||||
for (int i = 0; i < M2M_TRANS_TIMES; i++) {
|
||||
// Buffer data preparation
|
||||
prtx = tx_buf + i * data_size;
|
||||
prrx = rx_buf + i * data_size;
|
||||
for (int idx = 0; idx < data_size; idx++) {
|
||||
prtx[idx] = (i + idx + 0x45) & 0xFF;
|
||||
prrx[idx] = 0;
|
||||
}
|
||||
// Writeback and invalidate the TX and RX buffers
|
||||
esp_cache_msync((void *)prtx, data_size, ESP_CACHE_MSYNC_FLAG_DIR_C2M | ESP_CACHE_MSYNC_FLAG_INVALIDATE);
|
||||
esp_cache_msync((void *)prrx, data_size, ESP_CACHE_MSYNC_FLAG_DIR_C2M | ESP_CACHE_MSYNC_FLAG_INVALIDATE);
|
||||
|
||||
// DMA description preparation
|
||||
dma2d_link_dscr_init((uint32_t *)tx_dsc[i], NULL, (void *)prtx,
|
||||
data_size >> 14, data_size >> 14,
|
||||
data_size & 0x3FFF, data_size & 0x3FFF,
|
||||
1, 0, DMA2D_DESCRIPTOR_PBYTE_1B0_PER_PIXEL,
|
||||
DMA2D_DESCRIPTOR_BLOCK_RW_MODE_SINGLE, 0, 0);
|
||||
dma2d_link_dscr_init((uint32_t *)rx_dsc[i], NULL, (void *)prrx,
|
||||
0, data_size >> 14,
|
||||
0, data_size & 0x3FFF,
|
||||
0, 0, DMA2D_DESCRIPTOR_PBYTE_1B0_PER_PIXEL,
|
||||
DMA2D_DESCRIPTOR_BLOCK_RW_MODE_SINGLE, 0, 0);
|
||||
// Writeback the DMA descriptors
|
||||
esp_cache_msync((void *)tx_dsc[i], 64, ESP_CACHE_MSYNC_FLAG_DIR_C2M);
|
||||
esp_cache_msync((void *)rx_dsc[i], 64, ESP_CACHE_MSYNC_FLAG_DIR_C2M);
|
||||
|
||||
// Construct dma2d_m2m_trans_config_t structure
|
||||
m2m_trans_config[i].tx_desc_base_addr = (intptr_t)tx_dsc[i];
|
||||
m2m_trans_config[i].rx_desc_base_addr = (intptr_t)rx_dsc[i];
|
||||
m2m_trans_config[i].trans_eof_cb = dma2d_m2m_suc_eof_event_cb;
|
||||
m2m_trans_config[i].user_data = (void *)counting_sem;
|
||||
m2m_trans_config[i].transfer_ability_config = &transfer_ability_config;
|
||||
}
|
||||
|
||||
for (int i = 0; i < M2M_TRANS_TIMES; i++) {
|
||||
printf("trans %d\n", i);
|
||||
TEST_ESP_OK(dma2d_m2m(&m2m_trans_config[i]));
|
||||
}
|
||||
|
||||
for (int i = 0; i < M2M_TRANS_TIMES; i++) {
|
||||
xSemaphoreTake(counting_sem, portMAX_DELAY);
|
||||
printf("trans %d done\n", i);
|
||||
}
|
||||
printf("All transactions done!\n");
|
||||
|
||||
// Check result
|
||||
for (int i = 0; i < M2M_TRANS_TIMES; i++) {
|
||||
prtx = tx_buf + i * data_size;
|
||||
prrx = rx_buf + i * data_size;
|
||||
for (int idx = 0; idx < data_size; idx++) {
|
||||
TEST_ASSERT_EQUAL(prtx[idx], prrx[idx]);
|
||||
TEST_ASSERT_EQUAL(prtx[idx], (i + idx + 0x45) & 0xFF);
|
||||
}
|
||||
}
|
||||
|
||||
free(tx_link_buffer);
|
||||
free(rx_link_buffer);
|
||||
free(tx_buf);
|
||||
free(rx_buf);
|
||||
vSemaphoreDelete(counting_sem);
|
||||
|
||||
TEST_ESP_OK(dma2d_m2m_deinit());
|
||||
}
|
||||
|
||||
static void rgb565_to_rgb888(uint16_t rgb565, void *__r, void *__g, void *__b)
|
||||
{
|
||||
uint8_t *r = (uint8_t *)__r;
|
||||
uint8_t *g = (uint8_t *)__g;
|
||||
uint8_t *b = (uint8_t *)__b;
|
||||
|
||||
uint32_t _rgb565 = rgb565;
|
||||
uint8_t _b = (_rgb565>>8) & 0xF8;
|
||||
uint8_t _g = (_rgb565>>3) & 0xFC;
|
||||
uint8_t _r = (_rgb565<<3) & 0xF8;
|
||||
// *r = (_r & 0x08) ? (_r | 0x1) : (_r);
|
||||
// *g = (_g & 0x04) ? (_g | 0x1) : (_g);
|
||||
// *b = (_b & 0x08) ? (_b | 0x1) : (_b);
|
||||
|
||||
*r = _r | ( (_r >>3) & 0x7);
|
||||
*g = _g | ( (_g >>2) & 0x3);
|
||||
*b = _b | ( (_b >>3) & 0x7);
|
||||
}
|
||||
|
||||
static int rgb565_to_rgb888_and_cmp(void *_rgb565, void *__rgb888, int pix)
|
||||
{
|
||||
uint16_t *rgb565 = (uint16_t *)_rgb565;
|
||||
uint8_t *_rgb888 = (uint8_t *)__rgb888;
|
||||
uint8_t _r,_g,_b;
|
||||
for (int i = 0; i < pix; i++) {
|
||||
rgb565_to_rgb888(rgb565[i], &_r, &_g, &_b);
|
||||
if (_r != _rgb888[0] || _g != _rgb888[1] || _b != _rgb888[2]) {
|
||||
printf("idx %d - conv fail, %x:%x:%x, rgb565:%x, _rgb888:%x:%x:%x\r\n",
|
||||
i, _r, _g, _b, rgb565[i], _rgb888[0], _rgb888[1] ,_rgb888[2]);
|
||||
return -1;
|
||||
}
|
||||
_rgb888 += 3;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
TEST_CASE("DMA2D_M2M_1D_RGB565_to_RGB888", "[DMA2D]")
|
||||
{
|
||||
// Test a 4K pixel 1D buffer (original pixel in RGB565 format, convert to RGB888 format)
|
||||
const uint32_t item_size = 1024 * 4;
|
||||
|
||||
memset(m2m_trans_config, 0, M2M_TRANS_TIMES * sizeof(dma2d_m2m_trans_config_t));
|
||||
TEST_ESP_OK(dma2d_m2m_init());
|
||||
|
||||
dma2d_descriptor_t *tx_link_buffer = (dma2d_descriptor_t *)heap_caps_aligned_calloc(64, M2M_TRANS_TIMES, 64, MALLOC_CAP_DEFAULT);
|
||||
dma2d_descriptor_t *rx_link_buffer = (dma2d_descriptor_t *)heap_caps_aligned_calloc(64, M2M_TRANS_TIMES, 64, MALLOC_CAP_DEFAULT);
|
||||
TEST_ASSERT_NOT_NULL(tx_link_buffer);
|
||||
TEST_ASSERT_NOT_NULL(rx_link_buffer);
|
||||
for (int i = 0; i < M2M_TRANS_TIMES; i++) {
|
||||
tx_dsc[i] = (dma2d_descriptor_t *)((uint32_t)tx_link_buffer + 64 * i);
|
||||
rx_dsc[i] = (dma2d_descriptor_t *)((uint32_t)rx_link_buffer + 64 * i);
|
||||
}
|
||||
|
||||
uint8_t *prtx;
|
||||
uint8_t *prrx;
|
||||
uint8_t *tx_buf = heap_caps_aligned_calloc(64, item_size * 2 * M2M_TRANS_TIMES, sizeof(uint8_t), MALLOC_CAP_SPIRAM);
|
||||
uint8_t *rx_buf = heap_caps_aligned_calloc(64, item_size * 3 * M2M_TRANS_TIMES, sizeof(uint8_t), MALLOC_CAP_SPIRAM);
|
||||
TEST_ASSERT_NOT_NULL(tx_buf);
|
||||
TEST_ASSERT_NOT_NULL(rx_buf);
|
||||
|
||||
SemaphoreHandle_t counting_sem = xSemaphoreCreateCounting(M2M_TRANS_TIMES, 0);
|
||||
|
||||
dma2d_transfer_ability_t transfer_ability_config = {
|
||||
.data_burst_length = DMA2D_DATA_BURST_LENGTH_128,
|
||||
.desc_burst_en = true,
|
||||
.mb_size = DMA2D_MACRO_BLOCK_SIZE_NONE,
|
||||
};
|
||||
|
||||
dma2d_csc_config_t m2m_dma2d_tx_csc = {
|
||||
.tx_csc_option = DMA2D_CSC_TX_RGB565_TO_RGB888,
|
||||
.pre_scramble = DMA2D_SCRAMBLE_ORDER_BYTE2_1_0,
|
||||
};
|
||||
|
||||
// Preparation
|
||||
for (int i = 0; i < M2M_TRANS_TIMES; i++) {
|
||||
// Buffer data preparation
|
||||
prtx = tx_buf + i * item_size * 2;
|
||||
prrx = rx_buf + i * item_size * 3;
|
||||
for (int idx = 0; idx < item_size; idx++) {
|
||||
uint32_t r = (idx * 32 / item_size) & 0x1F;
|
||||
uint32_t g = (idx * 64 / item_size) & 0x3F;
|
||||
uint32_t b = (idx * 32 / item_size) & 0x1F;
|
||||
prtx[idx * 2] = (r << 3) || (g >> 3);
|
||||
prtx[idx * 2 + 1] = (g << 5) || b;
|
||||
prrx[idx * 3] = 0;
|
||||
prrx[idx * 3 + 1] = 0;
|
||||
prrx[idx * 3 + 2] = 0;
|
||||
}
|
||||
// Writeback and invalidate the TX and RX buffers
|
||||
esp_cache_msync((void *)prtx, item_size * 2, ESP_CACHE_MSYNC_FLAG_DIR_C2M | ESP_CACHE_MSYNC_FLAG_INVALIDATE);
|
||||
esp_cache_msync((void *)prrx, item_size * 3, ESP_CACHE_MSYNC_FLAG_DIR_C2M | ESP_CACHE_MSYNC_FLAG_INVALIDATE);
|
||||
|
||||
// DMA description preparation
|
||||
dma2d_link_dscr_init((uint32_t *)tx_dsc[i], NULL, (void *)prtx,
|
||||
(item_size * 2) >> 14, (item_size * 2) >> 14,
|
||||
(item_size * 2) & 0x3FFF, (item_size * 2) & 0x3FFF,
|
||||
1, 0, DMA2D_DESCRIPTOR_PBYTE_1B0_PER_PIXEL,
|
||||
DMA2D_DESCRIPTOR_BLOCK_RW_MODE_SINGLE, 0, 0);
|
||||
dma2d_link_dscr_init((uint32_t *)rx_dsc[i], NULL, (void *)prrx,
|
||||
0, (item_size * 3) >> 14,
|
||||
0, (item_size * 3) & 0x3FFF,
|
||||
0, 0, DMA2D_DESCRIPTOR_PBYTE_1B0_PER_PIXEL,
|
||||
DMA2D_DESCRIPTOR_BLOCK_RW_MODE_SINGLE, 0, 0);
|
||||
// Writeback the DMA descriptors
|
||||
esp_cache_msync((void *)tx_dsc[i], 64, ESP_CACHE_MSYNC_FLAG_DIR_C2M);
|
||||
esp_cache_msync((void *)rx_dsc[i], 64, ESP_CACHE_MSYNC_FLAG_DIR_C2M);
|
||||
|
||||
// Construct dma2d_m2m_trans_config_t structure
|
||||
m2m_trans_config[i].tx_desc_base_addr = (intptr_t)tx_dsc[i];
|
||||
m2m_trans_config[i].rx_desc_base_addr = (intptr_t)rx_dsc[i];
|
||||
m2m_trans_config[i].trans_eof_cb = dma2d_m2m_suc_eof_event_cb;
|
||||
m2m_trans_config[i].user_data = (void *)counting_sem;
|
||||
m2m_trans_config[i].transfer_ability_config = &transfer_ability_config;
|
||||
m2m_trans_config[i].tx_csc_config = &m2m_dma2d_tx_csc;
|
||||
}
|
||||
|
||||
for (int i = 0; i < M2M_TRANS_TIMES; i++) {
|
||||
printf("trans %d\n", i);
|
||||
TEST_ESP_OK(dma2d_m2m(&m2m_trans_config[i]));
|
||||
}
|
||||
|
||||
for (int i = 0; i < M2M_TRANS_TIMES; i++) {
|
||||
xSemaphoreTake(counting_sem, portMAX_DELAY);
|
||||
printf("trans %d done\n", i);
|
||||
}
|
||||
printf("All transactions done!\n");
|
||||
|
||||
// Check result
|
||||
for (int i = 0; i < M2M_TRANS_TIMES; i++) {
|
||||
prtx = tx_buf + i * item_size * 2;
|
||||
prrx = rx_buf + i * item_size * 3;
|
||||
TEST_ASSERT_EQUAL(0, rgb565_to_rgb888_and_cmp(prtx, prrx, item_size));
|
||||
}
|
||||
|
||||
free(tx_link_buffer);
|
||||
free(rx_link_buffer);
|
||||
free(tx_buf);
|
||||
free(rx_buf);
|
||||
vSemaphoreDelete(counting_sem);
|
||||
|
||||
TEST_ESP_OK(dma2d_m2m_deinit());
|
||||
}
|
||||
|
||||
TEST_CASE("DMA2D_M2M_2D_basic", "[DMA2D]")
|
||||
{
|
||||
// Test a 128 x 128 pixel data block (one byte per pixel - assume A8)
|
||||
const color_space_pixel_format_t pixel_format = {
|
||||
.color_space = COLOR_SPACE_ALPHA,
|
||||
.pixel_format = COLOR_PIXEL_A8,
|
||||
};
|
||||
const uint32_t stripe_size = 128; // unit: bytes
|
||||
|
||||
memset(m2m_trans_config, 0, M2M_TRANS_TIMES * sizeof(dma2d_m2m_trans_config_t));
|
||||
TEST_ESP_OK(dma2d_m2m_init());
|
||||
|
||||
dma2d_descriptor_t *tx_link_buffer = (dma2d_descriptor_t *)heap_caps_aligned_calloc(64, M2M_TRANS_TIMES, 64, MALLOC_CAP_DEFAULT);
|
||||
dma2d_descriptor_t *rx_link_buffer = (dma2d_descriptor_t *)heap_caps_aligned_calloc(64, M2M_TRANS_TIMES, 64, MALLOC_CAP_DEFAULT);
|
||||
TEST_ASSERT_NOT_NULL(tx_link_buffer);
|
||||
TEST_ASSERT_NOT_NULL(rx_link_buffer);
|
||||
for (int i = 0; i < M2M_TRANS_TIMES; i++) {
|
||||
tx_dsc[i] = (dma2d_descriptor_t *)((uint32_t)tx_link_buffer + 64 * i);
|
||||
rx_dsc[i] = (dma2d_descriptor_t *)((uint32_t)rx_link_buffer + 64 * i);
|
||||
}
|
||||
|
||||
uint8_t *prtx;
|
||||
uint8_t *prrx;
|
||||
uint8_t *tx_buf = heap_caps_aligned_calloc(64, stripe_size * stripe_size * M2M_TRANS_TIMES, sizeof(uint8_t), MALLOC_CAP_SPIRAM);
|
||||
uint8_t *rx_buf = heap_caps_aligned_calloc(64, stripe_size * stripe_size * M2M_TRANS_TIMES, sizeof(uint8_t), MALLOC_CAP_SPIRAM);
|
||||
TEST_ASSERT_NOT_NULL(tx_buf);
|
||||
TEST_ASSERT_NOT_NULL(rx_buf);
|
||||
|
||||
SemaphoreHandle_t counting_sem = xSemaphoreCreateCounting(M2M_TRANS_TIMES, 0);
|
||||
|
||||
dma2d_transfer_ability_t transfer_ability_config = {
|
||||
.data_burst_length = DMA2D_DATA_BURST_LENGTH_128,
|
||||
.desc_burst_en = true,
|
||||
.mb_size = DMA2D_MACRO_BLOCK_SIZE_NONE,
|
||||
};
|
||||
|
||||
// Preparation
|
||||
for (int i = 0; i < M2M_TRANS_TIMES; i++) {
|
||||
// Buffer data preparation
|
||||
prtx = tx_buf + i * stripe_size * stripe_size;
|
||||
prrx = rx_buf + i * stripe_size * stripe_size;
|
||||
for (int idx = 0; idx < stripe_size * stripe_size; idx++) {
|
||||
prtx[idx] = (i + idx + 0x45) & 0xFF;
|
||||
prrx[idx] = 0;
|
||||
}
|
||||
// Writeback and invalidate the TX and RX buffers
|
||||
esp_cache_msync((void *)prtx, stripe_size * stripe_size, ESP_CACHE_MSYNC_FLAG_DIR_C2M | ESP_CACHE_MSYNC_FLAG_INVALIDATE);
|
||||
esp_cache_msync((void *)prrx, stripe_size * stripe_size, ESP_CACHE_MSYNC_FLAG_DIR_C2M | ESP_CACHE_MSYNC_FLAG_INVALIDATE);
|
||||
|
||||
// DMA description preparation
|
||||
dma2d_link_dscr_init((uint32_t *)tx_dsc[i], NULL, (void *)prtx,
|
||||
stripe_size, stripe_size,
|
||||
stripe_size, stripe_size,
|
||||
1, 1, dma2d_desc_pixel_format_to_pbyte_value(pixel_format), // 1 bytes/pixel
|
||||
DMA2D_DESCRIPTOR_BLOCK_RW_MODE_SINGLE, 0, 0);
|
||||
dma2d_link_dscr_init((uint32_t *)rx_dsc[i], NULL, (void *)prrx,
|
||||
stripe_size, stripe_size,
|
||||
stripe_size, stripe_size,
|
||||
0, 1, dma2d_desc_pixel_format_to_pbyte_value(pixel_format), // 1 bytes/pixel
|
||||
DMA2D_DESCRIPTOR_BLOCK_RW_MODE_SINGLE, 0, 0);
|
||||
// Writeback the DMA descriptors
|
||||
esp_cache_msync((void *)tx_dsc[i], 64, ESP_CACHE_MSYNC_FLAG_DIR_C2M);
|
||||
esp_cache_msync((void *)rx_dsc[i], 64, ESP_CACHE_MSYNC_FLAG_DIR_C2M);
|
||||
|
||||
// Construct dma2d_m2m_trans_config_t structure
|
||||
m2m_trans_config[i].tx_desc_base_addr = (intptr_t)tx_dsc[i];
|
||||
m2m_trans_config[i].rx_desc_base_addr = (intptr_t)rx_dsc[i];
|
||||
m2m_trans_config[i].trans_eof_cb = dma2d_m2m_suc_eof_event_cb;
|
||||
m2m_trans_config[i].user_data = (void *)counting_sem;
|
||||
m2m_trans_config[i].transfer_ability_config = &transfer_ability_config;
|
||||
}
|
||||
|
||||
for (int i = 0; i < M2M_TRANS_TIMES; i++) {
|
||||
printf("trans %d\n", i);
|
||||
TEST_ESP_OK(dma2d_m2m(&m2m_trans_config[i]));
|
||||
}
|
||||
|
||||
for (int i = 0; i < M2M_TRANS_TIMES; i++) {
|
||||
xSemaphoreTake(counting_sem, portMAX_DELAY);
|
||||
printf("trans %d done\n", i);
|
||||
}
|
||||
printf("All transactions done!\n");
|
||||
|
||||
// Check result
|
||||
for (int i = 0; i < M2M_TRANS_TIMES; i++) {
|
||||
prtx = tx_buf + i * stripe_size * stripe_size;
|
||||
prrx = rx_buf + i * stripe_size * stripe_size;
|
||||
for (int idx = 0; idx < stripe_size * stripe_size; idx++) {
|
||||
TEST_ASSERT_EQUAL(prtx[idx], prrx[idx]);
|
||||
TEST_ASSERT_EQUAL(prtx[idx], (i + idx + 0x45) & 0xFF);
|
||||
}
|
||||
}
|
||||
|
||||
free(tx_link_buffer);
|
||||
free(rx_link_buffer);
|
||||
free(tx_buf);
|
||||
free(rx_buf);
|
||||
vSemaphoreDelete(counting_sem);
|
||||
|
||||
TEST_ESP_OK(dma2d_m2m_deinit());
|
||||
}
|
||||
|
||||
static void rgb888_to_rgb565(uint8_t r, uint8_t g, uint8_t b, uint16_t *rgb565)
|
||||
{
|
||||
uint16_t _rgb565 = (b >> 3);
|
||||
_rgb565 = (_rgb565 << 6) | (g >>2);
|
||||
_rgb565 = (_rgb565 << 5) | (r >>3);
|
||||
*rgb565 = _rgb565;
|
||||
}
|
||||
|
||||
static int rgb888_to_rgb565_and_cmp(void *__rgb888, void *__rgb565, int pix)
|
||||
{
|
||||
uint8_t *_rgb888 = (uint8_t *)__rgb888;
|
||||
uint16_t *rgb565 = (uint16_t *)__rgb565;
|
||||
|
||||
uint16_t _rgb565 = 0;
|
||||
uint8_t *rgb888 = _rgb888;
|
||||
for (int i = 0; i < pix; i++) {
|
||||
rgb888_to_rgb565(rgb888[0], rgb888[1], rgb888[2], &_rgb565);
|
||||
if (_rgb565 != rgb565[0]) {
|
||||
printf("conv fail, r:%x, g:%x, b:%x, rgb565:%x, _rgb565:%x\r\n",
|
||||
rgb888[0], rgb888[1], rgb888[2], rgb565[0], _rgb565);
|
||||
return -1;
|
||||
}
|
||||
rgb888 += 3;
|
||||
rgb565++;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
TEST_CASE("DMA2D_M2M_2D_RGB888_to_RGB565", "[DMA2D]")
|
||||
{
|
||||
// Test a 64 x 64 pixel data block (original pixel in RGB888 format, convert to RGB565 format)
|
||||
const color_space_pixel_format_t in_pixel_format = {
|
||||
.color_space = COLOR_SPACE_RGB,
|
||||
.pixel_format = COLOR_PIXEL_RGB888,
|
||||
};
|
||||
const color_space_pixel_format_t out_pixel_format = {
|
||||
.color_space = COLOR_SPACE_RGB,
|
||||
.pixel_format = COLOR_PIXEL_RGB565,
|
||||
};
|
||||
const uint32_t stripe_pixel_size = 64; // unit: pixel
|
||||
|
||||
memset(m2m_trans_config, 0, M2M_TRANS_TIMES * sizeof(dma2d_m2m_trans_config_t));
|
||||
TEST_ESP_OK(dma2d_m2m_init());
|
||||
|
||||
dma2d_descriptor_t *tx_link_buffer = (dma2d_descriptor_t *)heap_caps_aligned_calloc(64, M2M_TRANS_TIMES, 64, MALLOC_CAP_DEFAULT);
|
||||
dma2d_descriptor_t *rx_link_buffer = (dma2d_descriptor_t *)heap_caps_aligned_calloc(64, M2M_TRANS_TIMES, 64, MALLOC_CAP_DEFAULT);
|
||||
TEST_ASSERT_NOT_NULL(tx_link_buffer);
|
||||
TEST_ASSERT_NOT_NULL(rx_link_buffer);
|
||||
for (int i = 0; i < M2M_TRANS_TIMES; i++) {
|
||||
tx_dsc[i] = (dma2d_descriptor_t *)((uint32_t)tx_link_buffer + 64 * i);
|
||||
rx_dsc[i] = (dma2d_descriptor_t *)((uint32_t)rx_link_buffer + 64 * i);
|
||||
}
|
||||
|
||||
uint8_t *prtx;
|
||||
uint8_t *prrx;
|
||||
uint8_t *tx_buf = heap_caps_aligned_calloc(64, stripe_pixel_size * stripe_pixel_size * 3 * M2M_TRANS_TIMES, sizeof(uint8_t), MALLOC_CAP_SPIRAM);
|
||||
uint8_t *rx_buf = heap_caps_aligned_calloc(64, stripe_pixel_size * stripe_pixel_size * 2 * M2M_TRANS_TIMES, sizeof(uint8_t), MALLOC_CAP_SPIRAM);
|
||||
TEST_ASSERT_NOT_NULL(tx_buf);
|
||||
TEST_ASSERT_NOT_NULL(rx_buf);
|
||||
|
||||
SemaphoreHandle_t counting_sem = xSemaphoreCreateCounting(M2M_TRANS_TIMES, 0);
|
||||
|
||||
dma2d_transfer_ability_t transfer_ability_config = {
|
||||
.data_burst_length = DMA2D_DATA_BURST_LENGTH_16,
|
||||
.desc_burst_en = true,
|
||||
.mb_size = DMA2D_MACRO_BLOCK_SIZE_NONE,
|
||||
};
|
||||
|
||||
dma2d_csc_config_t m2m_dma2d_tx_csc = {
|
||||
.tx_csc_option = DMA2D_CSC_TX_RGB888_TO_RGB565,
|
||||
.pre_scramble = DMA2D_SCRAMBLE_ORDER_BYTE2_1_0,
|
||||
};
|
||||
|
||||
// Preparation
|
||||
for (int i = 0; i < M2M_TRANS_TIMES; i++) {
|
||||
// Buffer data preparation
|
||||
prtx = tx_buf + i * stripe_pixel_size * stripe_pixel_size * 3;
|
||||
prrx = rx_buf + i * stripe_pixel_size * stripe_pixel_size * 2;
|
||||
for (int idx = 0; idx < stripe_pixel_size * stripe_pixel_size; idx++) {
|
||||
uint32_t r = (i + idx + 0x5A) & 0xFF;
|
||||
uint32_t g = (i + idx + 0x4C) & 0xFF;
|
||||
uint32_t b = (i + idx + 0x9E) & 0xFF;
|
||||
prtx[idx * 3] = r;
|
||||
prtx[idx * 3 + 1] = g;
|
||||
prtx[idx * 3 + 2] = b;
|
||||
prrx[idx * 2] = 0;
|
||||
prrx[idx * 2 + 1] = 0;
|
||||
}
|
||||
// Writeback and invalidate the TX and RX buffers
|
||||
esp_cache_msync((void *)prtx, stripe_pixel_size * stripe_pixel_size * 3, ESP_CACHE_MSYNC_FLAG_DIR_C2M | ESP_CACHE_MSYNC_FLAG_INVALIDATE);
|
||||
esp_cache_msync((void *)prrx, stripe_pixel_size * stripe_pixel_size * 2, ESP_CACHE_MSYNC_FLAG_DIR_C2M | ESP_CACHE_MSYNC_FLAG_INVALIDATE);
|
||||
|
||||
// DMA description preparation
|
||||
dma2d_link_dscr_init((uint32_t *)tx_dsc[i], NULL, (void *)prtx,
|
||||
stripe_pixel_size, stripe_pixel_size,
|
||||
stripe_pixel_size, stripe_pixel_size,
|
||||
1, 1, dma2d_desc_pixel_format_to_pbyte_value(in_pixel_format),
|
||||
DMA2D_DESCRIPTOR_BLOCK_RW_MODE_SINGLE, 0, 0);
|
||||
dma2d_link_dscr_init((uint32_t *)rx_dsc[i], NULL, (void *)prrx,
|
||||
stripe_pixel_size, stripe_pixel_size,
|
||||
stripe_pixel_size, stripe_pixel_size,
|
||||
0, 1, dma2d_desc_pixel_format_to_pbyte_value(out_pixel_format),
|
||||
DMA2D_DESCRIPTOR_BLOCK_RW_MODE_SINGLE, 0, 0);
|
||||
// Writeback the DMA descriptors
|
||||
esp_cache_msync((void *)tx_dsc[i], 64, ESP_CACHE_MSYNC_FLAG_DIR_C2M);
|
||||
esp_cache_msync((void *)rx_dsc[i], 64, ESP_CACHE_MSYNC_FLAG_DIR_C2M);
|
||||
|
||||
// Construct dma2d_m2m_trans_config_t structure
|
||||
m2m_trans_config[i].tx_desc_base_addr = (intptr_t)tx_dsc[i];
|
||||
m2m_trans_config[i].rx_desc_base_addr = (intptr_t)rx_dsc[i];
|
||||
m2m_trans_config[i].trans_eof_cb = dma2d_m2m_suc_eof_event_cb;
|
||||
m2m_trans_config[i].user_data = (void *)counting_sem;
|
||||
m2m_trans_config[i].transfer_ability_config = &transfer_ability_config;
|
||||
m2m_trans_config[i].tx_csc_config = &m2m_dma2d_tx_csc;
|
||||
}
|
||||
|
||||
for (int i = 0; i < M2M_TRANS_TIMES; i++) {
|
||||
printf("trans %d\n", i);
|
||||
TEST_ESP_OK(dma2d_m2m(&m2m_trans_config[i]));
|
||||
}
|
||||
|
||||
for (int i = 0; i < M2M_TRANS_TIMES; i++) {
|
||||
xSemaphoreTake(counting_sem, portMAX_DELAY);
|
||||
printf("trans %d done\n", i);
|
||||
}
|
||||
printf("All transactions done!\n");
|
||||
|
||||
// Check result
|
||||
for (int i = 0; i < M2M_TRANS_TIMES; i++) {
|
||||
prtx = tx_buf + i * stripe_pixel_size * stripe_pixel_size * 3;
|
||||
prrx = rx_buf + i * stripe_pixel_size * stripe_pixel_size * 2;
|
||||
TEST_ASSERT_EQUAL(0, rgb888_to_rgb565_and_cmp(prtx, prrx, stripe_pixel_size * stripe_pixel_size));
|
||||
}
|
||||
|
||||
free(tx_link_buffer);
|
||||
free(rx_link_buffer);
|
||||
free(tx_buf);
|
||||
free(rx_buf);
|
||||
vSemaphoreDelete(counting_sem);
|
||||
|
||||
TEST_ESP_OK(dma2d_m2m_deinit());
|
||||
}
|
||||
|
||||
TEST_CASE("DMA2D_M2M_2D_window", "[DMA2D]")
|
||||
{
|
||||
// Test 2D memcpy to a 2 x 2 block at (2, 4) in a 8 x 8 picture (pixel in RGB565 format)
|
||||
const color_space_pixel_format_t pixel_format = {
|
||||
.color_space = COLOR_SPACE_RGB,
|
||||
.pixel_format = COLOR_PIXEL_RGB565,
|
||||
};
|
||||
const uint32_t va = 8, ha = 8; // Define picture height and width (unit: pixel)
|
||||
const uint32_t vb = 2, hb = 2; // Define block height and width (unit: pixel)
|
||||
const uint32_t x_offset = 2, y_offset = 4; // Define block location in the picture (unit: pixel)
|
||||
|
||||
memset(m2m_trans_config, 0, M2M_TRANS_TIMES * sizeof(dma2d_m2m_trans_config_t));
|
||||
TEST_ESP_OK(dma2d_m2m_init());
|
||||
|
||||
dma2d_descriptor_t *tx_link_buffer = (dma2d_descriptor_t *)heap_caps_aligned_calloc(64, M2M_TRANS_TIMES, 64, MALLOC_CAP_DEFAULT);
|
||||
dma2d_descriptor_t *rx_link_buffer = (dma2d_descriptor_t *)heap_caps_aligned_calloc(64, M2M_TRANS_TIMES, 64, MALLOC_CAP_DEFAULT);
|
||||
TEST_ASSERT_NOT_NULL(tx_link_buffer);
|
||||
TEST_ASSERT_NOT_NULL(rx_link_buffer);
|
||||
for (int i = 0; i < M2M_TRANS_TIMES; i++) {
|
||||
tx_dsc[i] = (dma2d_descriptor_t *)((uint32_t)tx_link_buffer + 64 * i);
|
||||
rx_dsc[i] = (dma2d_descriptor_t *)((uint32_t)rx_link_buffer + 64 * i);
|
||||
}
|
||||
|
||||
uint8_t *prtx;
|
||||
uint8_t *prrx;
|
||||
uint8_t *tx_buf = heap_caps_aligned_calloc(64, 64 * M2M_TRANS_TIMES, sizeof(uint8_t), MALLOC_CAP_INTERNAL);
|
||||
uint8_t *rx_buf = heap_caps_aligned_calloc(64, va * ha * 2 * M2M_TRANS_TIMES, sizeof(uint8_t), MALLOC_CAP_INTERNAL);
|
||||
TEST_ASSERT_NOT_NULL(tx_buf);
|
||||
TEST_ASSERT_NOT_NULL(rx_buf);
|
||||
|
||||
SemaphoreHandle_t counting_sem = xSemaphoreCreateCounting(M2M_TRANS_TIMES, 0);
|
||||
|
||||
dma2d_transfer_ability_t transfer_ability_config = {
|
||||
.data_burst_length = DMA2D_DATA_BURST_LENGTH_128,
|
||||
.desc_burst_en = true,
|
||||
.mb_size = DMA2D_MACRO_BLOCK_SIZE_NONE,
|
||||
};
|
||||
|
||||
// Preparation
|
||||
for (int i = 0; i < M2M_TRANS_TIMES; i++) {
|
||||
// Buffer data preparation
|
||||
prtx = tx_buf + i * 64;
|
||||
prrx = rx_buf + i * va * ha * 2;
|
||||
for (int idx = 0; idx < vb * hb; idx++) {
|
||||
prtx[idx * 2] = 0x55 + idx + i;
|
||||
prtx[idx * 2 + 1] = 0xAA + idx + i;
|
||||
}
|
||||
for (int idx = 0; idx < va * ha; idx++) {
|
||||
prrx[idx * 2] = 0xFF;
|
||||
prrx[idx * 2 + 1] = 0xFF;
|
||||
}
|
||||
|
||||
// Writeback and invalidate the TX and RX buffers
|
||||
esp_cache_msync((void *)prtx, 64, ESP_CACHE_MSYNC_FLAG_DIR_C2M | ESP_CACHE_MSYNC_FLAG_INVALIDATE);
|
||||
esp_cache_msync((void *)prrx, va * ha * 2, ESP_CACHE_MSYNC_FLAG_DIR_C2M | ESP_CACHE_MSYNC_FLAG_INVALIDATE);
|
||||
|
||||
// DMA description preparation
|
||||
dma2d_link_dscr_init((uint32_t *)tx_dsc[i], NULL, (void *)prtx,
|
||||
hb, vb,
|
||||
hb, vb,
|
||||
1, 1, dma2d_desc_pixel_format_to_pbyte_value(pixel_format),
|
||||
DMA2D_DESCRIPTOR_BLOCK_RW_MODE_SINGLE, 0, 0);
|
||||
dma2d_link_dscr_init((uint32_t *)rx_dsc[i], NULL, (void *)prrx,
|
||||
ha, va,
|
||||
hb, vb,
|
||||
0, 1, dma2d_desc_pixel_format_to_pbyte_value(pixel_format),
|
||||
DMA2D_DESCRIPTOR_BLOCK_RW_MODE_SINGLE, x_offset, y_offset);
|
||||
// Writeback the DMA descriptors
|
||||
esp_cache_msync((void *)tx_dsc[i], 64, ESP_CACHE_MSYNC_FLAG_DIR_C2M);
|
||||
esp_cache_msync((void *)rx_dsc[i], 64, ESP_CACHE_MSYNC_FLAG_DIR_C2M);
|
||||
|
||||
// Construct dma2d_m2m_trans_config_t structure
|
||||
m2m_trans_config[i].tx_desc_base_addr = (intptr_t)tx_dsc[i];
|
||||
m2m_trans_config[i].rx_desc_base_addr = (intptr_t)rx_dsc[i];
|
||||
m2m_trans_config[i].trans_eof_cb = dma2d_m2m_suc_eof_event_cb;
|
||||
m2m_trans_config[i].user_data = (void *)counting_sem;
|
||||
m2m_trans_config[i].transfer_ability_config = &transfer_ability_config;
|
||||
}
|
||||
|
||||
for (int i = 0; i < M2M_TRANS_TIMES; i++) {
|
||||
printf("trans %d\n", i);
|
||||
TEST_ESP_OK(dma2d_m2m(&m2m_trans_config[i]));
|
||||
}
|
||||
|
||||
for (int i = 0; i < M2M_TRANS_TIMES; i++) {
|
||||
xSemaphoreTake(counting_sem, portMAX_DELAY);
|
||||
printf("trans %d done\n", i);
|
||||
}
|
||||
printf("All transactions done!\n");
|
||||
|
||||
// Print the picture and check result
|
||||
for (int i = 0; i < M2M_TRANS_TIMES; i++) {
|
||||
prtx = tx_buf + i * 64;
|
||||
prrx = rx_buf + i * va * ha * 2;
|
||||
printf("pic:\n");
|
||||
for (int idx = 0; idx < va * ha; idx++) {
|
||||
printf("%02X%02X ", prrx[idx * 2], prrx[idx * 2 + 1]);
|
||||
if (idx % ha == (ha - 1)) {
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
bool pixel_in_window = false;
|
||||
for (int window_y = 0; window_y < vb; window_y++) {
|
||||
if (idx >= (ha * (y_offset + window_y) + x_offset) && idx < (ha * (y_offset + window_y) + x_offset + hb)) {
|
||||
uint32_t window_x = idx - ha * (y_offset + window_y) - x_offset;
|
||||
TEST_ASSERT_EQUAL(prtx[(window_y * hb + window_x) * 2], prrx[idx * 2]);
|
||||
TEST_ASSERT_EQUAL(prtx[(window_y * hb + window_x) * 2 + 1], prrx[idx * 2 + 1]);
|
||||
pixel_in_window = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!pixel_in_window) {
|
||||
TEST_ASSERT_EQUAL(0XFF, prrx[idx * 2]);
|
||||
TEST_ASSERT_EQUAL(0XFF, prrx[idx * 2 + 1]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
free(tx_link_buffer);
|
||||
free(rx_link_buffer);
|
||||
free(tx_buf);
|
||||
free(rx_buf);
|
||||
vSemaphoreDelete(counting_sem);
|
||||
|
||||
TEST_ESP_OK(dma2d_m2m_deinit());
|
||||
}
|
18
components/esp_hw_support/test_apps/dma2d/pytest_dma2d.py
Normal file
18
components/esp_hw_support/test_apps/dma2d/pytest_dma2d.py
Normal file
@ -0,0 +1,18 @@
|
||||
# SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD
|
||||
# SPDX-License-Identifier: CC0-1.0
|
||||
|
||||
import pytest
|
||||
from pytest_embedded import Dut
|
||||
|
||||
|
||||
@pytest.mark.esp32p4
|
||||
@pytest.mark.generic
|
||||
@pytest.mark.parametrize(
|
||||
'config',
|
||||
[
|
||||
'release',
|
||||
],
|
||||
indirect=True,
|
||||
)
|
||||
def test_dma2d(dut: Dut) -> None:
|
||||
dut.run_all_single_board_cases()
|
@ -0,0 +1,6 @@
|
||||
# set compiler optimization level
|
||||
CONFIG_COMPILER_OPTIMIZATION_SIZE=y
|
||||
CONFIG_BOOTLOADER_COMPILER_OPTIMIZATION_SIZE=y
|
||||
|
||||
# we can silent the assertion to save the binary footprint
|
||||
CONFIG_COMPILER_OPTIMIZATION_ASSERTIONS_SILENT=y
|
@ -0,0 +1,3 @@
|
||||
CONFIG_FREERTOS_HZ=1000
|
||||
CONFIG_ESP_TASK_WDT_EN=n
|
||||
CONFIG_IDF_EXPERIMENTAL_FEATURES=y
|
@ -0,0 +1,3 @@
|
||||
CONFIG_SPIRAM=y
|
||||
CONFIG_SPIRAM_MODE_HEX=y
|
||||
CONFIG_SPIRAM_SPEED_200M=y
|
@ -41,6 +41,7 @@ if(NOT CONFIG_APP_BUILD_TYPE_PURE_RAM_APP)
|
||||
endif()
|
||||
|
||||
if(NOT BOOTLOADER_BUILD)
|
||||
list(APPEND srcs "color_hal.c")
|
||||
|
||||
if(NOT CONFIG_APP_BUILD_TYPE_PURE_RAM_APP)
|
||||
if(CONFIG_SOC_SPI_FLASH_SUPPORTED)
|
||||
@ -127,6 +128,10 @@ if(NOT BOOTLOADER_BUILD)
|
||||
list(APPEND srcs "dw_gdma_hal.c")
|
||||
endif()
|
||||
|
||||
if(CONFIG_SOC_DMA2D_SUPPORTED)
|
||||
list(APPEND srcs "dma2d_hal.c")
|
||||
endif()
|
||||
|
||||
if(CONFIG_SOC_I2S_SUPPORTED)
|
||||
list(APPEND srcs "i2s_hal.c")
|
||||
endif()
|
||||
|
42
components/hal/color_hal.c
Normal file
42
components/hal/color_hal.c
Normal file
@ -0,0 +1,42 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <stdbool.h>
|
||||
#include "hal/color_hal.h"
|
||||
#include "hal/assert.h"
|
||||
|
||||
uint32_t color_hal_pixel_format_get_bit_depth(color_space_pixel_format_t format)
|
||||
{
|
||||
switch (format.color_type_id) {
|
||||
case COLOR_TYPE_ID(COLOR_SPACE_GRAY, COLOR_PIXEL_GRAY4):
|
||||
case COLOR_TYPE_ID(COLOR_SPACE_ALPHA, COLOR_PIXEL_A4):
|
||||
case COLOR_TYPE_ID(COLOR_SPACE_CLUT, COLOR_PIXEL_L4):
|
||||
return 4;
|
||||
case COLOR_TYPE_ID(COLOR_SPACE_RAW, COLOR_PIXEL_RAW8):
|
||||
case COLOR_TYPE_ID(COLOR_SPACE_GRAY, COLOR_PIXEL_GRAY8):
|
||||
case COLOR_TYPE_ID(COLOR_SPACE_ALPHA, COLOR_PIXEL_A8):
|
||||
case COLOR_TYPE_ID(COLOR_SPACE_CLUT, COLOR_PIXEL_L8):
|
||||
return 8;
|
||||
case COLOR_TYPE_ID(COLOR_SPACE_RAW, COLOR_PIXEL_RAW10):
|
||||
return 10;
|
||||
case COLOR_TYPE_ID(COLOR_SPACE_RAW, COLOR_PIXEL_RAW12):
|
||||
case COLOR_TYPE_ID(COLOR_SPACE_YUV, COLOR_PIXEL_YUV420):
|
||||
case COLOR_TYPE_ID(COLOR_SPACE_YUV, COLOR_PIXEL_YUV411):
|
||||
return 12;
|
||||
case COLOR_TYPE_ID(COLOR_SPACE_RGB, COLOR_PIXEL_RGB565):
|
||||
case COLOR_TYPE_ID(COLOR_SPACE_YUV, COLOR_PIXEL_YUV422):
|
||||
return 16;
|
||||
case COLOR_TYPE_ID(COLOR_SPACE_RGB, COLOR_PIXEL_RGB888):
|
||||
case COLOR_TYPE_ID(COLOR_SPACE_YUV, COLOR_PIXEL_YUV444):
|
||||
return 24;
|
||||
case COLOR_TYPE_ID(COLOR_SPACE_ARGB, COLOR_PIXEL_ARGB8888):
|
||||
return 32;
|
||||
default:
|
||||
// Unknown color space pixel format, unknown bit depth
|
||||
HAL_ASSERT(false);
|
||||
return 0;
|
||||
}
|
||||
}
|
29
components/hal/dma2d_hal.c
Normal file
29
components/hal/dma2d_hal.c
Normal file
@ -0,0 +1,29 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include "hal/dma2d_hal.h"
|
||||
#include "hal/dma2d_ll.h"
|
||||
|
||||
void dma2d_hal_init(dma2d_hal_context_t *hal, int group_id)
|
||||
{
|
||||
hal->dev = DMA2D_LL_GET_HW(group_id);
|
||||
}
|
||||
|
||||
void dma2d_hal_tx_reset_channel(dma2d_hal_context_t *hal, uint32_t channel)
|
||||
{
|
||||
dma2d_ll_tx_disable_cmd(hal->dev, channel, true);
|
||||
while (!dma2d_ll_tx_is_reset_avail(hal->dev, channel));
|
||||
dma2d_ll_tx_reset_channel(hal->dev, channel);
|
||||
dma2d_ll_tx_disable_cmd(hal->dev, channel, false);
|
||||
}
|
||||
|
||||
void dma2d_hal_rx_reset_channel(dma2d_hal_context_t *hal, uint32_t channel)
|
||||
{
|
||||
dma2d_ll_rx_disable_cmd(hal->dev, channel, true);
|
||||
while (!dma2d_ll_rx_is_reset_avail(hal->dev, channel));
|
||||
dma2d_ll_rx_reset_channel(hal->dev, channel);
|
||||
dma2d_ll_rx_disable_cmd(hal->dev, channel, false);
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2020-2023 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2020-2024 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
@ -242,9 +242,9 @@ static inline void gdma_ll_rx_enable_auto_return(gdma_dev_t *dev, uint32_t chann
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Check if DMA RX FSM is in IDLE state
|
||||
* @brief Check if DMA RX descriptor FSM is in IDLE state
|
||||
*/
|
||||
static inline bool gdma_ll_rx_is_fsm_idle(gdma_dev_t *dev, uint32_t channel)
|
||||
static inline bool gdma_ll_rx_is_desc_fsm_idle(gdma_dev_t *dev, uint32_t channel)
|
||||
{
|
||||
return dev->channel[channel].in.in_link.park;
|
||||
}
|
||||
@ -468,9 +468,9 @@ static inline void gdma_ll_tx_restart(gdma_dev_t *dev, uint32_t channel)
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Check if DMA TX FSM is in IDLE state
|
||||
* @brief Check if DMA TX descriptor FSM is in IDLE state
|
||||
*/
|
||||
static inline bool gdma_ll_tx_is_fsm_idle(gdma_dev_t *dev, uint32_t channel)
|
||||
static inline bool gdma_ll_tx_is_desc_fsm_idle(gdma_dev_t *dev, uint32_t channel)
|
||||
{
|
||||
return dev->channel[channel].out.out_link.park;
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2020-2023 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2020-2024 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
@ -242,9 +242,9 @@ static inline void gdma_ll_rx_enable_auto_return(gdma_dev_t *dev, uint32_t chann
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Check if DMA RX FSM is in IDLE state
|
||||
* @brief Check if DMA RX descriptor FSM is in IDLE state
|
||||
*/
|
||||
static inline bool gdma_ll_rx_is_fsm_idle(gdma_dev_t *dev, uint32_t channel)
|
||||
static inline bool gdma_ll_rx_is_desc_fsm_idle(gdma_dev_t *dev, uint32_t channel)
|
||||
{
|
||||
return dev->channel[channel].in.in_link.park;
|
||||
}
|
||||
@ -468,9 +468,9 @@ static inline void gdma_ll_tx_restart(gdma_dev_t *dev, uint32_t channel)
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Check if DMA TX FSM is in IDLE state
|
||||
* @brief Check if DMA TX descriptor FSM is in IDLE state
|
||||
*/
|
||||
static inline bool gdma_ll_tx_is_fsm_idle(gdma_dev_t *dev, uint32_t channel)
|
||||
static inline bool gdma_ll_tx_is_desc_fsm_idle(gdma_dev_t *dev, uint32_t channel)
|
||||
{
|
||||
return dev->channel[channel].out.out_link.park;
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2022-2024 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
@ -278,9 +278,9 @@ static inline void gdma_ll_rx_enable_auto_return(gdma_dev_t *dev, uint32_t chann
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Check if DMA RX FSM is in IDLE state
|
||||
* @brief Check if DMA RX descriptor FSM is in IDLE state
|
||||
*/
|
||||
static inline bool gdma_ll_rx_is_fsm_idle(gdma_dev_t *dev, uint32_t channel)
|
||||
static inline bool gdma_ll_rx_is_desc_fsm_idle(gdma_dev_t *dev, uint32_t channel)
|
||||
{
|
||||
return dev->channel[channel].in.in_link.inlink_park;
|
||||
}
|
||||
@ -514,9 +514,9 @@ static inline void gdma_ll_tx_restart(gdma_dev_t *dev, uint32_t channel)
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Check if DMA TX FSM is in IDLE state
|
||||
* @brief Check if DMA TX descriptor FSM is in IDLE state
|
||||
*/
|
||||
static inline bool gdma_ll_tx_is_fsm_idle(gdma_dev_t *dev, uint32_t channel)
|
||||
static inline bool gdma_ll_tx_is_desc_fsm_idle(gdma_dev_t *dev, uint32_t channel)
|
||||
{
|
||||
return dev->channel[channel].out.out_link.outlink_park;
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2022-2024 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
@ -278,9 +278,9 @@ static inline void gdma_ll_rx_enable_auto_return(gdma_dev_t *dev, uint32_t chann
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Check if DMA RX FSM is in IDLE state
|
||||
* @brief Check if DMA RX descriptor FSM is in IDLE state
|
||||
*/
|
||||
static inline bool gdma_ll_rx_is_fsm_idle(gdma_dev_t *dev, uint32_t channel)
|
||||
static inline bool gdma_ll_rx_is_desc_fsm_idle(gdma_dev_t *dev, uint32_t channel)
|
||||
{
|
||||
return dev->channel[channel].in.in_link.inlink_park;
|
||||
}
|
||||
@ -514,9 +514,9 @@ static inline void gdma_ll_tx_restart(gdma_dev_t *dev, uint32_t channel)
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Check if DMA TX FSM is in IDLE state
|
||||
* @brief Check if DMA TX descriptor FSM is in IDLE state
|
||||
*/
|
||||
static inline bool gdma_ll_tx_is_fsm_idle(gdma_dev_t *dev, uint32_t channel)
|
||||
static inline bool gdma_ll_tx_is_desc_fsm_idle(gdma_dev_t *dev, uint32_t channel)
|
||||
{
|
||||
return dev->channel[channel].out.out_link.outlink_park;
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2022-2024 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
@ -215,9 +215,9 @@ static inline void ahb_dma_ll_rx_enable_auto_return(ahb_dma_dev_t *dev, uint32_t
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Check if DMA RX FSM is in IDLE state
|
||||
* @brief Check if DMA RX descriptor FSM is in IDLE state
|
||||
*/
|
||||
static inline bool ahb_dma_ll_rx_is_fsm_idle(ahb_dma_dev_t *dev, uint32_t channel)
|
||||
static inline bool ahb_dma_ll_rx_is_desc_fsm_idle(ahb_dma_dev_t *dev, uint32_t channel)
|
||||
{
|
||||
return dev->channel[channel].in.in_link.inlink_park_chn;
|
||||
}
|
||||
@ -451,9 +451,9 @@ static inline void ahb_dma_ll_tx_restart(ahb_dma_dev_t *dev, uint32_t channel)
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Check if DMA TX FSM is in IDLE state
|
||||
* @brief Check if DMA TX descriptor FSM is in IDLE state
|
||||
*/
|
||||
static inline bool ahb_dma_ll_tx_is_fsm_idle(ahb_dma_dev_t *dev, uint32_t channel)
|
||||
static inline bool ahb_dma_ll_tx_is_desc_fsm_idle(ahb_dma_dev_t *dev, uint32_t channel)
|
||||
{
|
||||
return dev->channel[channel].out.out_link.outlink_park_chn;
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2022-2024 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
@ -189,9 +189,9 @@ static inline void axi_dma_ll_rx_enable_auto_return(axi_dma_dev_t *dev, uint32_t
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Check if DMA RX FSM is in IDLE state
|
||||
* @brief Check if DMA RX descriptor FSM is in IDLE state
|
||||
*/
|
||||
static inline bool axi_dma_ll_rx_is_fsm_idle(axi_dma_dev_t *dev, uint32_t channel)
|
||||
static inline bool axi_dma_ll_rx_is_desc_fsm_idle(axi_dma_dev_t *dev, uint32_t channel)
|
||||
{
|
||||
return dev->in[channel].conf.in_link1.inlink_park_chn;
|
||||
}
|
||||
@ -397,9 +397,9 @@ static inline void axi_dma_ll_tx_restart(axi_dma_dev_t *dev, uint32_t channel)
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Check if DMA TX FSM is in IDLE state
|
||||
* @brief Check if DMA TX descriptor FSM is in IDLE state
|
||||
*/
|
||||
static inline bool axi_dma_ll_tx_is_fsm_idle(axi_dma_dev_t *dev, uint32_t channel)
|
||||
static inline bool axi_dma_ll_tx_is_desc_fsm_idle(axi_dma_dev_t *dev, uint32_t channel)
|
||||
{
|
||||
return dev->out[channel].conf.out_link1.outlink_park_chn;
|
||||
}
|
||||
|
@ -60,8 +60,6 @@ static inline uint32_t periph_ll_get_rst_en_mask(periph_module_t periph, bool en
|
||||
return HP_SYS_CLKRST_REG_RST_EN_PVT_TOP;
|
||||
case PERIPH_ISP_MODULE:
|
||||
return HP_SYS_CLKRST_REG_RST_EN_ISP;
|
||||
case PERIPH_DMA2D_MODULE:
|
||||
return HP_SYS_CLKRST_REG_RST_EN_DMA2D;
|
||||
case PERIPH_UHCI_MODULE:
|
||||
return HP_SYS_CLKRST_REG_RST_EN_UHCI;
|
||||
case PERIPH_I3C_MODULE:
|
||||
@ -141,7 +139,6 @@ static inline uint32_t periph_ll_get_rst_en_reg(periph_module_t periph)
|
||||
switch (periph) {
|
||||
case PERIPH_PVT_MODULE:
|
||||
case PERIPH_ISP_MODULE:
|
||||
case PERIPH_DMA2D_MODULE:
|
||||
return HP_SYS_CLKRST_HP_RST_EN0_REG;
|
||||
case PERIPH_UHCI_MODULE:
|
||||
case PERIPH_I3C_MODULE:
|
||||
|
1166
components/hal/esp32p4/include/hal/dma2d_ll.h
Normal file
1166
components/hal/esp32p4/include/hal/dma2d_ll.h
Normal file
File diff suppressed because it is too large
Load Diff
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2020-2023 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2020-2024 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
@ -297,9 +297,9 @@ static inline void gdma_ll_rx_enable_auto_return(gdma_dev_t *dev, uint32_t chann
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Check if DMA RX FSM is in IDLE state
|
||||
* @brief Check if DMA RX descriptor FSM is in IDLE state
|
||||
*/
|
||||
static inline bool gdma_ll_rx_is_fsm_idle(gdma_dev_t *dev, uint32_t channel)
|
||||
static inline bool gdma_ll_rx_is_desc_fsm_idle(gdma_dev_t *dev, uint32_t channel)
|
||||
{
|
||||
return dev->channel[channel].in.link.park;
|
||||
}
|
||||
@ -565,9 +565,9 @@ static inline void gdma_ll_tx_restart(gdma_dev_t *dev, uint32_t channel)
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Check if DMA TX FSM is in IDLE state
|
||||
* @brief Check if DMA TX descriptor FSM is in IDLE state
|
||||
*/
|
||||
static inline bool gdma_ll_tx_is_fsm_idle(gdma_dev_t *dev, uint32_t channel)
|
||||
static inline bool gdma_ll_tx_is_desc_fsm_idle(gdma_dev_t *dev, uint32_t channel)
|
||||
{
|
||||
return dev->channel[channel].out.link.park;
|
||||
}
|
||||
|
27
components/hal/include/hal/color_hal.h
Normal file
27
components/hal/include/hal/color_hal.h
Normal file
@ -0,0 +1,27 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include "hal/color_types.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Get the bit depth (bits/pixel) of each color space pixel format
|
||||
*
|
||||
* @param color_type_id Value constructed in color_space_pixel_format_t struct
|
||||
*
|
||||
* @return Number of bits per pixel
|
||||
*/
|
||||
uint32_t color_hal_pixel_format_get_bit_depth(color_space_pixel_format_t color_type_id);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
56
components/hal/include/hal/dma2d_hal.h
Normal file
56
components/hal/include/hal/dma2d_hal.h
Normal file
@ -0,0 +1,56 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
/*******************************************************************************
|
||||
* NOTICE
|
||||
* The HAL is not public api, don't use in application code.
|
||||
* See readme.md in soc/README.md
|
||||
******************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef struct dma2d_dev_t *dma2d_soc_handle_t; // 2D-DMA SOC layer handle
|
||||
|
||||
/**
|
||||
* Context that should be maintained by both the driver and the HAL
|
||||
*/
|
||||
typedef struct {
|
||||
dma2d_soc_handle_t dev; // 2D-DMA SOC layer handle (i.e. register base address)
|
||||
} dma2d_hal_context_t;
|
||||
|
||||
/**
|
||||
* @brief Init the 2D-DMA hal. This function should be called first before other hal layer function is called
|
||||
*
|
||||
* @param hal Context of the HAL layer
|
||||
* @param group_id The 2D-DMA group number
|
||||
*/
|
||||
void dma2d_hal_init(dma2d_hal_context_t *hal, int group_id);
|
||||
|
||||
/**
|
||||
* @brief Reset 2D-DMA TX channel
|
||||
*
|
||||
* @param hal Context of the HAL layer
|
||||
* @param channel TX channel ID
|
||||
*/
|
||||
void dma2d_hal_tx_reset_channel(dma2d_hal_context_t *hal, uint32_t channel);
|
||||
|
||||
/**
|
||||
* @brief Reset 2D-DMA RX channel
|
||||
*
|
||||
* @param hal Context of the HAL layer
|
||||
* @param channel RX channel ID
|
||||
*/
|
||||
void dma2d_hal_rx_reset_channel(dma2d_hal_context_t *hal, uint32_t channel);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
252
components/hal/include/hal/dma2d_types.h
Normal file
252
components/hal/include/hal/dma2d_types.h
Normal file
@ -0,0 +1,252 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include "esp_assert.h"
|
||||
#include "hal/assert.h"
|
||||
#include "hal/color_hal.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Type of 2D-DMA descriptor
|
||||
*/
|
||||
typedef struct dma2d_descriptor_align8_s dma2d_descriptor_align8_t;
|
||||
struct dma2d_descriptor_align8_s {
|
||||
struct {
|
||||
uint32_t vb_size : 14; /*!< Vertical height of the block, unit: pixel
|
||||
When dma2d_en field is 0, this field is the lower 14-bit of the buffer memory space size, unit: byte */
|
||||
uint32_t hb_length : 14; /*!< Horizontal width of the block, unit: pixel
|
||||
When dma2d_en field is 0, this field is the lower 14-bit of the buffer length, unit: byte
|
||||
For data receive, this field is filled by DMA after transfer finishes */
|
||||
uint32_t err_eof : 1; /*!< Whether the received buffer contains error
|
||||
For data transfer, this bit is fixed to 0 */
|
||||
uint32_t dma2d_en : 1; /*!< Whether to enable 2D functionality */
|
||||
uint32_t suc_eof : 1; /*!< Whether the descriptor is the last one in the link */
|
||||
uint32_t owner : 1; /*!< Who is allowed to access the buffer that this descriptor points to, select DMA2D_DESCRIPTOR_BUFFER_OWNER_CPU or DMA2D_DESCRIPTOR_BUFFER_OWNER_DMA
|
||||
When owner is chosen to be DMA, after DMA finishs with the descriptor, it will clear this bit
|
||||
For data transfer, the bit won't be cleared unless DMA2D_OUT_AUTO_WRBACK is enabled */
|
||||
}; /*!< Descriptor Word 0 */
|
||||
struct {
|
||||
uint32_t va_size : 14; /*!< Vertical height of the picture, unit: pixel
|
||||
When dma2d_en field is 0, this field is the higher 14-bit of the buffer memory space size, unit: byte */
|
||||
uint32_t ha_length : 14; /*!< Horizontal width of the picture, unit: pixel
|
||||
When dma2d_en field is 0, this field is the higher 14-bit of the buffer length, unit: byte
|
||||
For data receive, this field is filled by DMA after transfer finishes */
|
||||
uint32_t pbyte : 4; /*!< Number of bytes per pixel (make use of dma2d_desc_pixel_format_to_pbyte_value to get the value)
|
||||
When dma2d_en field is 0, this field has no use */
|
||||
}; /*!< Descriptor Word 1 */
|
||||
struct {
|
||||
uint32_t y : 14; /*!< The y-coordinate value of the hb * vb block, unit: pixel
|
||||
When dma2d_en field is 0, this field has no use */
|
||||
uint32_t x : 14; /*!< The x-coordinate value of the hb * vb block, unit: pixel
|
||||
When dma2d_en field is 0, this field has no use */
|
||||
uint32_t mode : 1; /*!< Data block read/write mode, select DMA2D_DESCRIPTOR_BLOCK_RW_MODE_SINGLE or DMA2D_DESCRIPTOR_BLOCK_RW_MODE_MULTIPLE
|
||||
When dma2d_en field is 0, this field must be 0 (i.e. DMA2D_DESCRIPTOR_BLOCK_RW_MODE_SINGLE mode) */
|
||||
uint32_t reserved29 : 3;
|
||||
}; /*!< Descriptor Word 2 */
|
||||
void *buffer; /*!< Pointer to the buffer (of ha * va picture)
|
||||
For RX buffer, there is 4-byte alignment addr and size requirement (spiram and axi bus restriction, otherwises, could overwrite other data or be overwritten) */
|
||||
dma2d_descriptor_align8_t *next; /*!< Pointer to the next descriptor (set to NULL if the descriptor is the last one, e.g. suc_eof=1) */
|
||||
} __attribute__((aligned(8)));
|
||||
|
||||
ESP_STATIC_ASSERT(sizeof(dma2d_descriptor_align8_t) == 24, "dma2d_descriptor_align8_t should occupy 24 bytes in memory");
|
||||
|
||||
// 2D-DMA descriptor requires 8-byte alignment
|
||||
typedef dma2d_descriptor_align8_t dma2d_descriptor_t;
|
||||
|
||||
#define DMA2D_DESCRIPTOR_BUFFER_OWNER_CPU (0) /*!< 2D-DMA buffer is allowed to be accessed by CPU */
|
||||
#define DMA2D_DESCRIPTOR_BUFFER_OWNER_DMA (1) /*!< 2D-DMA buffer is allowed to be accessed by 2D-DMA engine */
|
||||
|
||||
#define DMA2D_DESCRIPTOR_BLOCK_RW_MODE_SINGLE (0) /*!< 2D-DMA data block single R/W mode, only r/w one hb * vb block */
|
||||
#define DMA2D_DESCRIPTOR_BLOCK_RW_MODE_MULTIPLE (1) /*!< 2D-DMA data block multiple R/W mode, continues r/w hb * vb blocks until the ha * va picture is done */
|
||||
|
||||
#define DMA2D_DESCRIPTOR_PBYTE_0B5_PER_PIXEL (0) /*!< 2D-DMA descriptor pbyte value when 0.5 bytes/pixel */
|
||||
#define DMA2D_DESCRIPTOR_PBYTE_1B0_PER_PIXEL (1) /*!< 2D-DMA descriptor pbyte value when 1 bytes/pixel */
|
||||
#define DMA2D_DESCRIPTOR_PBYTE_1B5_PER_PIXEL (2) /*!< 2D-DMA descriptor pbyte value when 1.5 bytes/pixel */
|
||||
#define DMA2D_DESCRIPTOR_PBYTE_2B0_PER_PIXEL (3) /*!< 2D-DMA descriptor pbyte value when 2 bytes/pixel */
|
||||
#define DMA2D_DESCRIPTOR_PBYTE_3B0_PER_PIXEL (4) /*!< 2D-DMA descriptor pbyte value when 3 bytes/pixel */
|
||||
#define DMA2D_DESCRIPTOR_PBYTE_4B0_PER_PIXEL (5) /*!< 2D-DMA descriptor pbyte value when 4 bytes/pixel */
|
||||
|
||||
// Helper function to convert pixel format to 2D-DMA descriptor pbyte value
|
||||
static inline uint32_t dma2d_desc_pixel_format_to_pbyte_value(color_space_pixel_format_t pixel_format)
|
||||
{
|
||||
switch (color_hal_pixel_format_get_bit_depth(pixel_format)) {
|
||||
case 4:
|
||||
return DMA2D_DESCRIPTOR_PBYTE_0B5_PER_PIXEL;
|
||||
case 8:
|
||||
return DMA2D_DESCRIPTOR_PBYTE_1B0_PER_PIXEL;
|
||||
case 12:
|
||||
return DMA2D_DESCRIPTOR_PBYTE_1B5_PER_PIXEL;
|
||||
case 16:
|
||||
return DMA2D_DESCRIPTOR_PBYTE_2B0_PER_PIXEL;
|
||||
case 24:
|
||||
return DMA2D_DESCRIPTOR_PBYTE_3B0_PER_PIXEL;
|
||||
case 32:
|
||||
return DMA2D_DESCRIPTOR_PBYTE_4B0_PER_PIXEL;
|
||||
default:
|
||||
// Unsupported bit depth
|
||||
abort();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Enumeration of peripherals which have the 2D-DMA capability
|
||||
*/
|
||||
typedef enum {
|
||||
DMA2D_TRIG_PERIPH_M2M, /*!< 2D-DMA trigger peripheral: M2M */
|
||||
DMA2D_TRIG_PERIPH_JPEG_ENCODER, /*!< 2D-DMA trigger peripheral: JPEG Encoder */
|
||||
DMA2D_TRIG_PERIPH_JPEG_DECODER, /*!< 2D-DMA trigger peripheral: JPEG Decoder */
|
||||
DMA2D_TRIG_PERIPH_PPA_SR, /*!< 2D-DMA trigger peripheral: PPA SR engine */
|
||||
DMA2D_TRIG_PERIPH_PPA_BLEND, /*!< 2D-DMA trigger peripheral: PPA Blending engine */
|
||||
} dma2d_trigger_peripheral_t;
|
||||
|
||||
/**
|
||||
* @brief Enumeration of 2D-DMA channel direction
|
||||
*/
|
||||
typedef enum {
|
||||
DMA2D_CHANNEL_DIRECTION_TX, /*!< 2D-DMA channel direction: TX */
|
||||
DMA2D_CHANNEL_DIRECTION_RX, /*!< 2D-DMA channel direction: RX */
|
||||
} dma2d_channel_direction_t;
|
||||
|
||||
/**
|
||||
* @brief Enumeration of 2D-DMA data burst length options
|
||||
*/
|
||||
typedef enum {
|
||||
DMA2D_DATA_BURST_LENGTH_1, /*!< 2D-DMA block size: single byte */
|
||||
DMA2D_DATA_BURST_LENGTH_16, /*!< 2D-DMA block size: 16 bytes */
|
||||
DMA2D_DATA_BURST_LENGTH_32, /*!< 2D-DMA block size: 32 bytes */
|
||||
DMA2D_DATA_BURST_LENGTH_64, /*!< 2D-DMA block size: 64 bytes */
|
||||
DMA2D_DATA_BURST_LENGTH_128, /*!< 2D-DMA block size: 128 bytes */
|
||||
DMA2D_DATA_BURST_LENGTH_INVALID, /*!< Invalid 2D-DMA block size */
|
||||
} dma2d_data_burst_length_t;
|
||||
|
||||
/**
|
||||
* @brief Enumeration of 2D-DMA macro block size options
|
||||
* Only useful in DMA2D_DESCRIPTOR_BLOCK_RW_MODE_MULTIPLE mode (dma2d_en = 1, mode = 1)
|
||||
* Descriptor vb and hb fields has to be multiples of macro block sizes
|
||||
*/
|
||||
typedef enum {
|
||||
DMA2D_MACRO_BLOCK_SIZE_NONE, /*!< 2D-DMA no macro block */
|
||||
DMA2D_MACRO_BLOCK_SIZE_8_8, /*!< 2D-DMA 8 pixel x 8 pixel macro block */
|
||||
DMA2D_MACRO_BLOCK_SIZE_8_16, /*!< 2D-DMA 8 pixel x 16 pixel macro block */
|
||||
DMA2D_MACRO_BLOCK_SIZE_16_16, /*!< 2D-DMA 16 pixel x 16 pixel macro block */
|
||||
DMA2D_MACRO_BLOCK_SIZE_INVALID, /*!< Invalid 2D-DMA macro block size */
|
||||
} dma2d_macro_block_size_t;
|
||||
|
||||
/**
|
||||
* @brief Enumeration of 2D-DMA pixel bytes scamble order in color space conversion
|
||||
*
|
||||
* Assuming the original pixel byte order (from MSB to LSB) is 2-1-0 .
|
||||
*/
|
||||
typedef enum {
|
||||
DMA2D_SCRAMBLE_ORDER_BYTE2_1_0, /*!< 2D-DMA pixel data scrambled as BYTE2-1-0 (no scramble) */
|
||||
DMA2D_SCRAMBLE_ORDER_BYTE2_0_1, /*!< 2D-DMA pixel data scrambled as BYTE2-0-1 */
|
||||
DMA2D_SCRAMBLE_ORDER_BYTE1_0_2, /*!< 2D-DMA pixel data scrambled as BYTE1-0-2 */
|
||||
DMA2D_SCRAMBLE_ORDER_BYTE1_2_0, /*!< 2D-DMA pixel data scrambled as BYTE1-2-0 */
|
||||
DMA2D_SCRAMBLE_ORDER_BYTE0_2_1, /*!< 2D-DMA pixel data scrambled as BYTE0-2-1 */
|
||||
DMA2D_SCRAMBLE_ORDER_BYTE0_1_2, /*!< 2D-DMA pixel data scrambled as BYTE0-1-2 */
|
||||
DMA2D_SCRAMBLE_ORDER_INVALID, /*!< Invalid 2D-DMA pixel data scramble order */
|
||||
} dma2d_scramble_order_t;
|
||||
|
||||
//*********************BT601***********************************//
|
||||
// Y = 16 + 0.257 * R + 0.504 * g + 0.098 * b //
|
||||
// Cb = 128 - 0.148 * R - 0.291 * g + 0.439 * b //
|
||||
// Cr = 128 + 0.439 * R - 0.368 * g - 0.071 * b //
|
||||
// R = 1.164 *(Y - 16) + 1.596 *(Cr - 128) //
|
||||
// G = 1.164 *(Y - 16) - 0.392 *(Cb - 128) - 0.812 *(Cr - 128)//
|
||||
// B = 1.164 *(Y - 16) + 2.016 *(Cb - 128) //
|
||||
//*********************BT601***********************************//
|
||||
|
||||
//*********************BT709***********************************//
|
||||
// Y = 16 + 0.183 * R + 0.614 * g + 0.062 * b //
|
||||
// Cb = 128 - 0.101 * R - 0.339 * g + 0.439 * b //
|
||||
// Cr = 128 + 0.439 * R - 0.399 * g - 0.040 * b //
|
||||
// R = 1.164 *(Y - 16) + 1.792 *(Cr - 128) //
|
||||
// G = 1.164 *(Y - 16) - 0.213 *(Cb - 128) - 0.534 *(Cr - 128)//
|
||||
// B = 1.164 *(Y - 16) + 2.114 *(Cb - 128) //
|
||||
//*********************BT709***********************************//
|
||||
|
||||
// 256 * Q = A[9:0] * x + B[10:0] * y + C[9:0] * z + D[17:0]
|
||||
|
||||
#define DMA2D_COLOR_SPACE_CONV_PARAM_RGB2YUV_BT601 \
|
||||
{ \
|
||||
{ 66, 129, 25, 4096}, \
|
||||
{ -38, -74, 112, 32768}, \
|
||||
{ 112, -94, -18, 32768}, \
|
||||
}
|
||||
|
||||
#define DMA2D_COLOR_SPACE_CONV_PARAM_RGB2YUV_BT709 \
|
||||
{ \
|
||||
{ 47, 157, 16, 4096}, \
|
||||
{ -26, -86, 112, 32768}, \
|
||||
{ 112, -102, -10, 32768}, \
|
||||
}
|
||||
|
||||
#define DMA2D_COLOR_SPACE_CONV_PARAM_YUV2RGB_BT601 \
|
||||
{ \
|
||||
{ 298, 0, 409, -56906}, \
|
||||
{ 298, -100, -208, 34707}, \
|
||||
{ 298, 516, 0, -70836}, \
|
||||
}
|
||||
|
||||
#define DMA2D_COLOR_SPACE_CONV_PARAM_YUV2RGB_BT709 \
|
||||
{ \
|
||||
{ 298, 0, 459, -63367}, \
|
||||
{ 298, -55, -136, 19681}, \
|
||||
{ 298, 541, 0, -73918}, \
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Enumeration of 2D-DMA TX color space conversion (CSC) option
|
||||
*/
|
||||
typedef enum {
|
||||
DMA2D_CSC_TX_NONE, /*!< 2D-DMA TX perform no CSC */
|
||||
DMA2D_CSC_TX_SCRAMBLE, /*!< 2D-DMA TX perform only data scramble */
|
||||
DMA2D_CSC_TX_RGB888_TO_RGB565, /*!< 2D-DMA TX perform RGB888 to RGB565 conversion */
|
||||
DMA2D_CSC_TX_RGB565_TO_RGB888, /*!< 2D-DMA TX perform RGB565 to RGB888 conversion */
|
||||
DMA2D_CSC_TX_RGB888_TO_YUV444_601, /*!< 2D-DMA TX perform RGB888 to YUV444 conversion (follow BT601 standard) */
|
||||
DMA2D_CSC_TX_RGB888_TO_YUV444_709, /*!< 2D-DMA TX perform RGB888 to YUV444 conversion (follow BT709 standard) */
|
||||
DMA2D_CSC_TX_RGB888_TO_YUV422_601, /*!< 2D-DMA TX perform RGB888 to YUV422-MIPI conversion (follow BT601 standard) */
|
||||
DMA2D_CSC_TX_RGB888_TO_YUV422_709, /*!< 2D-DMA TX perform RGB888 to YUV422-MIPI conversion (follow BT709 standard) */
|
||||
DMA2D_CSC_TX_YUV444_TO_RGB888_601, /*!< 2D-DMA TX perform YUV444 to RGB888 conversion (follow BT601 standard) */
|
||||
DMA2D_CSC_TX_YUV444_TO_RGB888_709, /*!< 2D-DMA TX perform YUV444 to RGB888 conversion (follow BT709 standard) */
|
||||
DMA2D_CSC_TX_YUV422_TO_RGB888_601, /*!< 2D-DMA TX perform YUV422-MIPI to RGB888 conversion (follow BT601 standard) */
|
||||
DMA2D_CSC_TX_YUV422_TO_RGB888_709, /*!< 2D-DMA TX perform YUV422-MIPI to RGB888 conversion (follow BT709 standard) */
|
||||
DMA2D_CSC_TX_INVALID, /*!< Invalid 2D-DMA TX color space conversion */
|
||||
} dma2d_csc_tx_option_t;
|
||||
|
||||
/**
|
||||
* @brief Enumeration of 2D-DMA RX color space conversion (CSC) option
|
||||
* RX side only JPEG requires CSC
|
||||
*/
|
||||
typedef enum {
|
||||
DMA2D_CSC_RX_NONE, /*!< 2D-DMA RX perform no CSC */
|
||||
DMA2D_CSC_RX_SCRAMBLE, /*!< 2D-DMA RX perform only data scramble */
|
||||
DMA2D_CSC_RX_YUV422_TO_YUV444, /*!< 2D-DMA RX perform YUV422 to YUV444 conversion */
|
||||
DMA2D_CSC_RX_YUV420_TO_YUV444, /*!< 2D-DMA RX perform YUV420 to YUV444 conversion */
|
||||
DMA2D_CSC_RX_YUV420_TO_RGB888_601, /*!< 2D-DMA RX perform YUV420 to RGB888 conversion (follow BT601 standard) */
|
||||
DMA2D_CSC_RX_YUV420_TO_RGB565_601, /*!< 2D-DMA RX perform YUV420 to RGB565 conversion (follow BT601 standard) */
|
||||
DMA2D_CSC_RX_YUV420_TO_RGB888_709, /*!< 2D-DMA RX perform YUV420 to RGB888 conversion (follow BT709 standard) */
|
||||
DMA2D_CSC_RX_YUV420_TO_RGB565_709, /*!< 2D-DMA RX perform YUV420 to RGB565 conversion (follow BT709 standard) */
|
||||
DMA2D_CSC_RX_YUV422_TO_RGB888_601, /*!< 2D-DMA RX perform YUV422 to RGB888 conversion (follow BT601 standard) */
|
||||
DMA2D_CSC_RX_YUV422_TO_RGB565_601, /*!< 2D-DMA RX perform YUV422 to RGB565 conversion (follow BT601 standard) */
|
||||
DMA2D_CSC_RX_YUV422_TO_RGB888_709, /*!< 2D-DMA RX perform YUV422 to RGB888 conversion (follow BT709 standard) */
|
||||
DMA2D_CSC_RX_YUV422_TO_RGB565_709, /*!< 2D-DMA RX perform YUV422 to RGB565 conversion (follow BT709 standard) */
|
||||
DMA2D_CSC_RX_YUV444_TO_RGB888_601, /*!< 2D-DMA RX perform YUV444 to RGB888 conversion (follow BT601 standard) */
|
||||
DMA2D_CSC_RX_YUV444_TO_RGB565_601, /*!< 2D-DMA RX perform YUV444 to RGB565 conversion (follow BT601 standard) */
|
||||
DMA2D_CSC_RX_YUV444_TO_RGB888_709, /*!< 2D-DMA RX perform YUV444 to RGB888 conversion (follow BT709 standard) */
|
||||
DMA2D_CSC_RX_YUV444_TO_RGB565_709, /*!< 2D-DMA RX perform YUV444 to RGB565 conversion (follow BT709 standard) */
|
||||
DMA2D_CSC_RX_INVALID, /*!< Invalid 2D-DMA RX color space conversion */
|
||||
} dma2d_csc_rx_option_t;
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
@ -35,6 +35,10 @@ if(CONFIG_SOC_GDMA_SUPPORTED)
|
||||
list(APPEND srcs "${target}/gdma_periph.c")
|
||||
endif()
|
||||
|
||||
if(CONFIG_SOC_DMA2D_SUPPORTED)
|
||||
list(APPEND srcs "${target}/dma2d_periph.c")
|
||||
endif()
|
||||
|
||||
if(CONFIG_SOC_GPSPI_SUPPORTED)
|
||||
list(APPEND srcs "${target}/spi_periph.c")
|
||||
endif()
|
||||
|
24
components/soc/esp32p4/dma2d_periph.c
Normal file
24
components/soc/esp32p4/dma2d_periph.c
Normal file
@ -0,0 +1,24 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include "soc/dma2d_periph.h"
|
||||
#include "soc/interrupts.h"
|
||||
|
||||
const dma2d_signal_conn_t dma2d_periph_signals = {
|
||||
.groups = {
|
||||
[0] = {
|
||||
.tx_irq_id = {
|
||||
[0] = ETS_DMA2D_OUT_CH0_INTR_SOURCE,
|
||||
[1] = ETS_DMA2D_OUT_CH1_INTR_SOURCE,
|
||||
[2] = ETS_DMA2D_OUT_CH2_INTR_SOURCE,
|
||||
},
|
||||
.rx_irq_id = {
|
||||
[0] = ETS_DMA2D_IN_CH0_INTR_SOURCE,
|
||||
[1] = ETS_DMA2D_IN_CH1_INTR_SOURCE,
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
@ -27,6 +27,10 @@ config SOC_DW_GDMA_SUPPORTED
|
||||
bool
|
||||
default y
|
||||
|
||||
config SOC_DMA2D_SUPPORTED
|
||||
bool
|
||||
default y
|
||||
|
||||
config SOC_GPTIMER_SUPPORTED
|
||||
bool
|
||||
default y
|
||||
@ -403,6 +407,18 @@ config SOC_GDMA_SUPPORT_ETM
|
||||
bool
|
||||
default y
|
||||
|
||||
config SOC_DMA2D_GROUPS
|
||||
int
|
||||
default 1
|
||||
|
||||
config SOC_DMA2D_TX_CHANNELS_PER_GROUP
|
||||
int
|
||||
default 3
|
||||
|
||||
config SOC_DMA2D_RX_CHANNELS_PER_GROUP
|
||||
int
|
||||
default 2
|
||||
|
||||
config SOC_ETM_GROUPS
|
||||
int
|
||||
default 1
|
||||
|
19
components/soc/esp32p4/include/soc/dma2d_channel.h
Normal file
19
components/soc/esp32p4/include/soc/dma2d_channel.h
Normal file
@ -0,0 +1,19 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
// The following macros are matched with the 2D-DMA peri_sel field peripheral selection ID
|
||||
#define SOC_DMA2D_TRIG_PERIPH_JPEG_RX (0)
|
||||
#define SOC_DMA2D_TRIG_PERIPH_PPA_SR_RX (1)
|
||||
#define SOC_DMA2D_TRIG_PERIPH_PPA_BLEND_RX (2)
|
||||
#define SOC_DMA2D_TRIG_PERIPH_M2M_RX (-1) // Any value of 3 ~ 7, but TX and RX needs the same ID for M2M
|
||||
|
||||
#define SOC_DMA2D_TRIG_PERIPH_JPEG_TX (0)
|
||||
#define SOC_DMA2D_TRIG_PERIPH_PPA_SR_TX (1)
|
||||
#define SOC_DMA2D_TRIG_PERIPH_PPA_BLEND_FG_TX (2)
|
||||
#define SOC_DMA2D_TRIG_PERIPH_PPA_BLEND_BG_TX (3)
|
||||
#define SOC_DMA2D_TRIG_PERIPH_M2M_TX (-1) // Any value of 4 ~ 7, but TX and RX needs the same ID for M2M
|
@ -1,5 +1,5 @@
|
||||
/**
|
||||
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
@ -690,90 +690,6 @@ typedef union {
|
||||
uint32_t val;
|
||||
} dma2d_out_scramble_chn_reg_t;
|
||||
|
||||
/** Type of out_color_param0_chn register
|
||||
* Configures the tx color convert parameter of channel 0
|
||||
*/
|
||||
typedef union {
|
||||
struct {
|
||||
/** out_color_param_h0_chn : R/W; bitpos: [20:0]; default: 298;
|
||||
* Set first 2 parameter of most significant byte of pending 3 bytes
|
||||
*/
|
||||
uint32_t out_color_param_h0_chn:21;
|
||||
uint32_t reserved_21:11;
|
||||
};
|
||||
uint32_t val;
|
||||
} dma2d_out_color_param0_chn_reg_t;
|
||||
|
||||
/** Type of out_color_param1_chn register
|
||||
* Configures the tx color convert parameter of channel 0
|
||||
*/
|
||||
typedef union {
|
||||
struct {
|
||||
/** out_color_param_h1_chn : R/W; bitpos: [27:0]; default: 210164121;
|
||||
* Set last 2 parameter of most significant byte of pending 3 bytes
|
||||
*/
|
||||
uint32_t out_color_param_h1_chn:28;
|
||||
uint32_t reserved_28:4;
|
||||
};
|
||||
uint32_t val;
|
||||
} dma2d_out_color_param1_chn_reg_t;
|
||||
|
||||
/** Type of out_color_param2_chn register
|
||||
* Configures the tx color convert parameter of channel 0
|
||||
*/
|
||||
typedef union {
|
||||
struct {
|
||||
/** out_color_param_m0_chn : R/W; bitpos: [20:0]; default: 1995050;
|
||||
* Set first 2 parameter of midium significant byte of pending 3 bytes
|
||||
*/
|
||||
uint32_t out_color_param_m0_chn:21;
|
||||
uint32_t reserved_21:11;
|
||||
};
|
||||
uint32_t val;
|
||||
} dma2d_out_color_param2_chn_reg_t;
|
||||
|
||||
/** Type of out_color_param3_chn register
|
||||
* Configures the tx color convert parameter of channel 0
|
||||
*/
|
||||
typedef union {
|
||||
struct {
|
||||
/** out_color_param_m1_chn : R/W; bitpos: [27:0]; default: 35540784;
|
||||
* Set last 2 parameter of midium significant byte of pending 3 bytes
|
||||
*/
|
||||
uint32_t out_color_param_m1_chn:28;
|
||||
uint32_t reserved_28:4;
|
||||
};
|
||||
uint32_t val;
|
||||
} dma2d_out_color_param3_chn_reg_t;
|
||||
|
||||
/** Type of out_color_param4_chn register
|
||||
* Configures the tx color convert parameter of channel 0
|
||||
*/
|
||||
typedef union {
|
||||
struct {
|
||||
/** out_color_param_l0_chn : R/W; bitpos: [20:0]; default: 528682;
|
||||
* Set first 2 parameter of least significant byte of pending 3 bytes
|
||||
*/
|
||||
uint32_t out_color_param_l0_chn:21;
|
||||
uint32_t reserved_21:11;
|
||||
};
|
||||
uint32_t val;
|
||||
} dma2d_out_color_param4_chn_reg_t;
|
||||
|
||||
/** Type of out_color_param5_chn register
|
||||
* Configures the tx color convert parameter of channel 0
|
||||
*/
|
||||
typedef union {
|
||||
struct {
|
||||
/** out_color_param_l1_chn : R/W; bitpos: [27:0]; default: 195899392;
|
||||
* Set last 2 parameter of least significant byte of pending 3 bytes
|
||||
*/
|
||||
uint32_t out_color_param_l1_chn:28;
|
||||
uint32_t reserved_28:4;
|
||||
};
|
||||
uint32_t val;
|
||||
} dma2d_out_color_param5_chn_reg_t;
|
||||
|
||||
/** Type of out_etm_conf_chn register
|
||||
* Configures the tx etm of channel 0
|
||||
*/
|
||||
@ -1529,90 +1445,6 @@ typedef union {
|
||||
uint32_t val;
|
||||
} dma2d_in_scramble_chn_reg_t;
|
||||
|
||||
/** Type of in_color_param0_chn register
|
||||
* Configures the rx color convert parameter of channel 0
|
||||
*/
|
||||
typedef union {
|
||||
struct {
|
||||
/** in_color_param_h0_chn : R/W; bitpos: [20:0]; default: 298;
|
||||
* Set first 2 parameter of most significant byte of pending 3 bytes
|
||||
*/
|
||||
uint32_t in_color_param_h0_chn:21;
|
||||
uint32_t reserved_21:11;
|
||||
};
|
||||
uint32_t val;
|
||||
} dma2d_in_color_param0_chn_reg_t;
|
||||
|
||||
/** Type of in_color_param1_chn register
|
||||
* Configures the rx color convert parameter of channel 0
|
||||
*/
|
||||
typedef union {
|
||||
struct {
|
||||
/** in_color_param_h1_chn : R/W; bitpos: [27:0]; default: 210164121;
|
||||
* Set last 2 parameter of most significant byte of pending 3 bytes
|
||||
*/
|
||||
uint32_t in_color_param_h1_chn:28;
|
||||
uint32_t reserved_28:4;
|
||||
};
|
||||
uint32_t val;
|
||||
} dma2d_in_color_param1_chn_reg_t;
|
||||
|
||||
/** Type of in_color_param2_chn register
|
||||
* Configures the rx color convert parameter of channel 0
|
||||
*/
|
||||
typedef union {
|
||||
struct {
|
||||
/** in_color_param_m0_chn : R/W; bitpos: [20:0]; default: 1995050;
|
||||
* Set first 2 parameter of midium significant byte of pending 3 bytes
|
||||
*/
|
||||
uint32_t in_color_param_m0_chn:21;
|
||||
uint32_t reserved_21:11;
|
||||
};
|
||||
uint32_t val;
|
||||
} dma2d_in_color_param2_chn_reg_t;
|
||||
|
||||
/** Type of in_color_param3_chn register
|
||||
* Configures the rx color convert parameter of channel 0
|
||||
*/
|
||||
typedef union {
|
||||
struct {
|
||||
/** in_color_param_m1_chn : R/W; bitpos: [27:0]; default: 35540784;
|
||||
* Set last 2 parameter of midium significant byte of pending 3 bytes
|
||||
*/
|
||||
uint32_t in_color_param_m1_chn:28;
|
||||
uint32_t reserved_28:4;
|
||||
};
|
||||
uint32_t val;
|
||||
} dma2d_in_color_param3_chn_reg_t;
|
||||
|
||||
/** Type of in_color_param4_chn register
|
||||
* Configures the rx color convert parameter of channel 0
|
||||
*/
|
||||
typedef union {
|
||||
struct {
|
||||
/** in_color_param_l0_chn : R/W; bitpos: [20:0]; default: 528682;
|
||||
* Set first 2 parameter of least significant byte of pending 3 bytes
|
||||
*/
|
||||
uint32_t in_color_param_l0_chn:21;
|
||||
uint32_t reserved_21:11;
|
||||
};
|
||||
uint32_t val;
|
||||
} dma2d_in_color_param4_chn_reg_t;
|
||||
|
||||
/** Type of in_color_param5_chn register
|
||||
* Configures the rx color convert parameter of channel 0
|
||||
*/
|
||||
typedef union {
|
||||
struct {
|
||||
/** in_color_param_l1_chn : R/W; bitpos: [27:0]; default: 195899392;
|
||||
* Set last 2 parameter of least significant byte of pending 3 bytes
|
||||
*/
|
||||
uint32_t in_color_param_l1_chn:28;
|
||||
uint32_t reserved_28:4;
|
||||
};
|
||||
uint32_t val;
|
||||
} dma2d_in_color_param5_chn_reg_t;
|
||||
|
||||
/** Type of in_etm_conf_chn register
|
||||
* Configures the rx etm of channel 0
|
||||
*/
|
||||
@ -1718,7 +1550,7 @@ typedef union {
|
||||
*/
|
||||
typedef union {
|
||||
struct {
|
||||
/** access_intr_mem_start_addr : R/W; bitpos: [31:0]; default: 806354944;
|
||||
/** access_intr_mem_start_addr : R/W; bitpos: [31:0]; default: 806354944 (0x30100000);
|
||||
* The start address of accessible address space.
|
||||
*/
|
||||
uint32_t access_intr_mem_start_addr:32;
|
||||
@ -1731,7 +1563,7 @@ typedef union {
|
||||
*/
|
||||
typedef union {
|
||||
struct {
|
||||
/** access_intr_mem_end_addr : R/W; bitpos: [31:0]; default: 2415919103;
|
||||
/** access_intr_mem_end_addr : R/W; bitpos: [31:0]; default: 2415919103 (0x8FFFFFFF);
|
||||
* The end address of accessible address space. The access address beyond this range
|
||||
* would lead to descriptor error.
|
||||
*/
|
||||
@ -1745,7 +1577,7 @@ typedef union {
|
||||
*/
|
||||
typedef union {
|
||||
struct {
|
||||
/** access_extr_mem_start_addr : R/W; bitpos: [31:0]; default: 806354944;
|
||||
/** access_extr_mem_start_addr : R/W; bitpos: [31:0]; default: 806354944 (0x30100000);
|
||||
* The start address of accessible address space.
|
||||
*/
|
||||
uint32_t access_extr_mem_start_addr:32;
|
||||
@ -1758,7 +1590,7 @@ typedef union {
|
||||
*/
|
||||
typedef union {
|
||||
struct {
|
||||
/** access_extr_mem_end_addr : R/W; bitpos: [31:0]; default: 2415919103;
|
||||
/** access_extr_mem_end_addr : R/W; bitpos: [31:0]; default: 2415919103 (0x8FFFFFFF);
|
||||
* The end address of accessible address space. The access address beyond this range
|
||||
* would lead to descriptor error.
|
||||
*/
|
||||
@ -1847,36 +1679,69 @@ typedef union {
|
||||
uint32_t val;
|
||||
} dma2d_rdn_eco_low_reg_t;
|
||||
|
||||
|
||||
/** Type of in/out_color_param_h/m/l_chn register
|
||||
* Configures the rx/tx color convert parameter of channel n
|
||||
*/
|
||||
typedef union {
|
||||
struct {
|
||||
struct {
|
||||
/** a: R/W; bitpos: [9:0]; default: h:298, m:298, l:298
|
||||
* Set the first parameter of the most/medium/least significant byte of pending 3 bytes
|
||||
*/
|
||||
uint32_t a : 10;
|
||||
/** b: R/W; bitpos: [20:10]; default: h:0, in_m:1948, l:516
|
||||
* Set the second parameter of the most/medium/least significant byte of pending 3 bytes
|
||||
*/
|
||||
uint32_t b : 11;
|
||||
uint32_t reserved21 : 11;
|
||||
};
|
||||
struct {
|
||||
/** c: R/W; bitpos: [41:32]; default: h:409, m:816, l:0
|
||||
* Set the third parameter of the most/medium/least significant byte of pending 3 bytes
|
||||
*/
|
||||
uint32_t c : 10;
|
||||
/** d: R/W; bitpos: [59:42]; default: h:205238, m:34707, l:191308
|
||||
* Set the fourth parameter of the most/medium/least significant byte of pending 3 bytes
|
||||
*/
|
||||
uint32_t d : 18;
|
||||
uint32_t reserved60 : 4;
|
||||
};
|
||||
};
|
||||
uint32_t val[2];
|
||||
} dma2d_color_param_reg_t;
|
||||
|
||||
typedef struct {
|
||||
volatile dma2d_out_conf0_chn_reg_t out_conf0_ch0;
|
||||
volatile dma2d_out_int_raw_chn_reg_t out_int_raw_ch0;
|
||||
volatile dma2d_out_int_ena_chn_reg_t out_int_ena_ch0;
|
||||
volatile dma2d_out_int_st_chn_reg_t out_int_st_ch0;
|
||||
volatile dma2d_out_int_clr_chn_reg_t out_int_clr_ch0;
|
||||
volatile dma2d_outfifo_status_chn_reg_t outfifo_status_ch0;
|
||||
volatile dma2d_out_push_chn_reg_t out_push_ch0;
|
||||
volatile dma2d_out_link_conf_chn_reg_t out_link_conf_ch0;
|
||||
volatile dma2d_out_link_addr_chn_reg_t out_link_addr_ch0;
|
||||
volatile dma2d_out_state_chn_reg_t out_state_ch0;
|
||||
volatile dma2d_out_eof_des_addr_chn_reg_t out_eof_des_addr_ch0;
|
||||
volatile dma2d_out_dscr_chn_reg_t out_dscr_ch0;
|
||||
volatile dma2d_out_dscr_bf0_chn_reg_t out_dscr_bf0_ch0;
|
||||
volatile dma2d_out_dscr_bf1_chn_reg_t out_dscr_bf1_ch0;
|
||||
volatile dma2d_out_peri_sel_chn_reg_t out_peri_sel_ch0;
|
||||
volatile dma2d_out_arb_chn_reg_t out_arb_ch0;
|
||||
volatile dma2d_out_ro_status_chn_reg_t out_ro_status_ch0;
|
||||
volatile dma2d_out_ro_pd_conf_chn_reg_t out_ro_pd_conf_ch0; //only chn0
|
||||
volatile dma2d_out_color_convert_chn_reg_t out_color_convert_ch0;
|
||||
volatile dma2d_out_scramble_chn_reg_t out_scramble_ch0;
|
||||
volatile dma2d_out_color_param0_chn_reg_t out_color_param0_ch0;
|
||||
volatile dma2d_out_color_param1_chn_reg_t out_color_param1_ch0;
|
||||
volatile dma2d_out_color_param2_chn_reg_t out_color_param2_ch0;
|
||||
volatile dma2d_out_color_param3_chn_reg_t out_color_param3_ch0;
|
||||
volatile dma2d_out_color_param4_chn_reg_t out_color_param4_ch0;
|
||||
volatile dma2d_out_color_param5_chn_reg_t out_color_param5_ch0;
|
||||
volatile dma2d_out_etm_conf_chn_reg_t out_etm_conf_ch0;
|
||||
volatile dma2d_out_dscr_port_blk_chn_reg_t out_dscr_port_blk_ch0;
|
||||
uint32_t reserved[36];
|
||||
volatile dma2d_color_param_reg_t param_h;
|
||||
volatile dma2d_color_param_reg_t param_m;
|
||||
volatile dma2d_color_param_reg_t param_l;
|
||||
} dma2d_color_param_group_chn_reg_t;
|
||||
|
||||
typedef struct {
|
||||
volatile dma2d_out_conf0_chn_reg_t out_conf0;
|
||||
volatile dma2d_out_int_raw_chn_reg_t out_int_raw;
|
||||
volatile dma2d_out_int_ena_chn_reg_t out_int_ena;
|
||||
volatile dma2d_out_int_st_chn_reg_t out_int_st;
|
||||
volatile dma2d_out_int_clr_chn_reg_t out_int_clr;
|
||||
volatile dma2d_outfifo_status_chn_reg_t outfifo_status;
|
||||
volatile dma2d_out_push_chn_reg_t out_push;
|
||||
volatile dma2d_out_link_conf_chn_reg_t out_link_conf;
|
||||
volatile dma2d_out_link_addr_chn_reg_t out_link_addr;
|
||||
volatile dma2d_out_state_chn_reg_t out_state;
|
||||
volatile dma2d_out_eof_des_addr_chn_reg_t out_eof_des_addr;
|
||||
volatile dma2d_out_dscr_chn_reg_t out_dscr;
|
||||
volatile dma2d_out_dscr_bf0_chn_reg_t out_dscr_bf0;
|
||||
volatile dma2d_out_dscr_bf1_chn_reg_t out_dscr_bf1;
|
||||
volatile dma2d_out_peri_sel_chn_reg_t out_peri_sel;
|
||||
volatile dma2d_out_arb_chn_reg_t out_arb;
|
||||
volatile dma2d_out_ro_status_chn_reg_t out_ro_status;
|
||||
volatile dma2d_out_ro_pd_conf_chn_reg_t out_ro_pd_conf; /* only exist on channel0 */
|
||||
volatile dma2d_out_color_convert_chn_reg_t out_color_convert;
|
||||
volatile dma2d_out_scramble_chn_reg_t out_scramble;
|
||||
volatile dma2d_color_param_group_chn_reg_t out_color_param_group;
|
||||
volatile dma2d_out_etm_conf_chn_reg_t out_etm_conf;
|
||||
volatile dma2d_out_dscr_port_blk_chn_reg_t out_dscr_port_blk;
|
||||
uint32_t reserved_out[36];
|
||||
} dma2d_out_chn_reg_t;
|
||||
|
||||
typedef struct {
|
||||
@ -1898,15 +1763,10 @@ typedef struct {
|
||||
volatile dma2d_in_peri_sel_chn_reg_t in_peri_sel;
|
||||
volatile dma2d_in_arb_chn_reg_t in_arb;
|
||||
volatile dma2d_in_ro_status_chn_reg_t in_ro_status;
|
||||
volatile dma2d_in_ro_pd_conf_chn_reg_t in_ro_pd_conf; //only ch0
|
||||
volatile dma2d_in_color_convert_chn_reg_t in_color_convert; //only ch0
|
||||
volatile dma2d_in_scramble_chn_reg_t in_scramble; //only ch0
|
||||
volatile dma2d_in_color_param0_chn_reg_t in_color_param0; //only ch0
|
||||
volatile dma2d_in_color_param1_chn_reg_t in_color_param1; //only ch0
|
||||
volatile dma2d_in_color_param2_chn_reg_t in_color_param2; //only ch0
|
||||
volatile dma2d_in_color_param3_chn_reg_t in_color_param3; //only ch0
|
||||
volatile dma2d_in_color_param4_chn_reg_t in_color_param4; //only ch0
|
||||
volatile dma2d_in_color_param5_chn_reg_t in_color_param5; //only ch0
|
||||
volatile dma2d_in_ro_pd_conf_chn_reg_t in_ro_pd_conf;
|
||||
volatile dma2d_in_color_convert_chn_reg_t in_color_convert;
|
||||
volatile dma2d_in_scramble_chn_reg_t in_scramble;
|
||||
volatile dma2d_color_param_group_chn_reg_t in_color_param_group;
|
||||
volatile dma2d_in_etm_conf_chn_reg_t in_etm_conf;
|
||||
uint32_t reserved_570[36];
|
||||
} dma2d_in_ch0_reg_t;
|
||||
@ -1931,14 +1791,15 @@ typedef struct {
|
||||
volatile dma2d_in_arb_chn_reg_t in_arb;
|
||||
volatile dma2d_in_ro_status_chn_reg_t in_ro_status;
|
||||
volatile dma2d_in_etm_conf_chn_reg_t in_etm_conf;
|
||||
uint32_t reserved_64c[45];
|
||||
} dma2d_in_ch1_reg_t;
|
||||
|
||||
typedef struct {
|
||||
typedef struct dma2d_dev_t {
|
||||
volatile dma2d_out_chn_reg_t out_channel[3];
|
||||
uint32_t reserved_300[128];
|
||||
volatile dma2d_in_ch0_reg_t in_channel0;
|
||||
volatile dma2d_in_ch1_reg_t in_channel1;
|
||||
uint32_t reserved_6dc[237];
|
||||
uint32_t reserved_700[192];
|
||||
volatile dma2d_axi_err_reg_t axi_err;
|
||||
volatile dma2d_rst_conf_reg_t rst_conf;
|
||||
volatile dma2d_intr_mem_start_addr_reg_t intr_mem_start_addr;
|
||||
|
@ -25,6 +25,7 @@
|
||||
#define SOC_AHB_GDMA_SUPPORTED 1
|
||||
#define SOC_AXI_GDMA_SUPPORTED 1
|
||||
#define SOC_DW_GDMA_SUPPORTED 1
|
||||
#define SOC_DMA2D_SUPPORTED 1
|
||||
#define SOC_GPTIMER_SUPPORTED 1
|
||||
#define SOC_PCNT_SUPPORTED 1
|
||||
// #define SOC_LCDCAM_SUPPORTED 1 // TODO: IDF-7465
|
||||
@ -180,6 +181,12 @@
|
||||
#define SOC_AXI_GDMA_SUPPORT_PSRAM 1
|
||||
#define SOC_GDMA_SUPPORT_ETM 1
|
||||
|
||||
/*-------------------------- 2D-DMA CAPS -------------------------------------*/
|
||||
#define SOC_DMA2D_GROUPS (1U) // Number of 2D-DMA groups
|
||||
#define SOC_DMA2D_TX_CHANNELS_PER_GROUP (3) // Number of 2D-DMA TX (OUT) channels in each group
|
||||
#define SOC_DMA2D_RX_CHANNELS_PER_GROUP (2) // Number of 2D-DMA RX (IN) channels in each group
|
||||
// #define SOC_DMA2D_SUPPORT_ETM (1) // Support ETM submodule
|
||||
|
||||
/*-------------------------- ETM CAPS --------------------------------------*/
|
||||
#define SOC_ETM_GROUPS 1U // Number of ETM groups
|
||||
#define SOC_ETM_CHANNELS_PER_GROUP 50 // Number of ETM channels in the group
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2022-2024 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
@ -100,9 +100,9 @@ PROVIDE ( I3C_MST = 0x500DA000 );
|
||||
PROVIDE ( I3C_MST_MEM = 0x500DA000 );
|
||||
PROVIDE ( I3C_SLV = 0x500DB000 );
|
||||
|
||||
PROVIDE ( JPEG = 0x50086000 );
|
||||
PROVIDE ( PPA = 0x50087000 );
|
||||
PROVIDE ( DMA2D = 0x50088000 );
|
||||
PROVIDE ( JPEG = 0x50086000 );
|
||||
|
||||
PROVIDE ( USB_WRAP = 0x50080000 );
|
||||
PROVIDE ( USB_DWC_HS = 0x50000000 );
|
||||
|
29
components/soc/include/soc/dma2d_periph.h
Normal file
29
components/soc/include/soc/dma2d_periph.h
Normal file
@ -0,0 +1,29 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include "soc/soc_caps.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#if SOC_DMA2D_SUPPORTED
|
||||
typedef struct {
|
||||
struct {
|
||||
const int tx_irq_id[SOC_DMA2D_TX_CHANNELS_PER_GROUP];
|
||||
const int rx_irq_id[SOC_DMA2D_RX_CHANNELS_PER_GROUP];
|
||||
} groups[SOC_DMA2D_GROUPS];
|
||||
} dma2d_signal_conn_t;
|
||||
|
||||
extern const dma2d_signal_conn_t dma2d_periph_signals;
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
Loading…
Reference in New Issue
Block a user