ports/mimxrt: Enable edma memcpy.

This commit is contained in:
Kwabena W. Agyeman 2024-02-03 13:02:16 -08:00
parent 61ec9006bb
commit 63edb776c3
2 changed files with 115 additions and 7 deletions

View File

@ -192,7 +192,12 @@
// Camera interface configuration. // Camera interface configuration.
#define OMV_CSI_BASE (CSI) #define OMV_CSI_BASE (CSI)
#define OMV_CSI_DMA (DMA0)
#define OMV_CSI_DMA_MUX (DMAMUX)
#define OMV_CSI_DMA_CHANNEL_START (4U)
#define OMV_CSI_DMA_CHANNEL_COUNT (2U)
#define OMV_CSI_XCLK_FREQUENCY (12000000) #define OMV_CSI_XCLK_FREQUENCY (12000000)
#define OMV_CSI_DMA_MEMCPY_ENABLE (1)
#define OMV_CSI_HW_SWAP_ENABLE (1) #define OMV_CSI_HW_SWAP_ENABLE (1)
#define OMV_CSI_D0_PIN (&omv_pin_DCMI_D0) #define OMV_CSI_D0_PIN (&omv_pin_DCMI_D0)

View File

@ -24,14 +24,21 @@
#include "framebuffer.h" #include "framebuffer.h"
#include "unaligned_memcpy.h" #include "unaligned_memcpy.h"
#define DMA_LENGTH_ALIGNMENT (8)
#define SENSOR_TIMEOUT_MS (3000)
// Sensor struct. // Sensor struct.
sensor_t sensor = {}; sensor_t sensor = {};
extern uint8_t _line_buf[OMV_LINE_BUF_SIZE]; extern uint8_t _line_buf[OMV_LINE_BUF_SIZE];
#if defined(OMV_CSI_DMA)
static edma_handle_t CSI_EDMA_Handle[OMV_CSI_DMA_CHANNEL_COUNT];
static int src_inc, src_size, dest_inc_size;
#endif
#define CSI_IRQ_FLAGS (CSI_CR1_SOF_INTEN_MASK \ #define CSI_IRQ_FLAGS (CSI_CR1_SOF_INTEN_MASK \
| CSI_CR1_FB2_DMA_DONE_INTEN_MASK \ | CSI_CR1_FB2_DMA_DONE_INTEN_MASK \
| CSI_CR1_FB1_DMA_DONE_INTEN_MASK) | CSI_CR1_FB1_DMA_DONE_INTEN_MASK)
//CSI_CR1_RF_OR_INTEN_MASK
void sensor_init0() { void sensor_init0() {
sensor_abort(true, false); sensor_abort(true, false);
@ -187,6 +194,39 @@ void sensor_sof_callback() {
} }
} }
#if defined(OMV_CSI_DMA)
int sensor_dma_memcpy(void *dma, void *dst, void *src, int bpp, bool transposed) {
// EMDA will not perform burst transfers for anything less than 32-byte chunks made of four 64-bit
// beats. Additionally, the CSI hardware lacks cropping so we cannot align the source address.
// Given this, performance will be lacking on cropped images. So much so that we do not use
// the EDMA for anything less than 4-byte transfers otherwise you get sensor timeout errors.
if (dest_inc_size < 4) {
return -1;
}
edma_handle_t *handle = dma;
edma_transfer_config_t config;
EDMA_PrepareTransferConfig(&config,
src, // srcAddr
src_size, // srcWidth
src_inc, // srcOffset
dst, // destAddr
transposed ? bpp : dest_inc_size, // destWidth
transposed ? (MAIN_FB()->v * bpp) : dest_inc_size, // destOffset
MAIN_FB()->u * bpp, // bytesEachRequest
MAIN_FB()->u * bpp); // transferBytes
// Drop the frame if EDMA is not keeping up as the image will be corrupt.
if (EDMA_SubmitTransfer(handle, &config) != kStatus_Success) {
sensor.drop_frame = true;
return 0;
}
EDMA_TriggerChannelStart(handle->base, handle->channel);
return 0;
}
#endif
void sensor_line_callback(uint32_t addr) { void sensor_line_callback(uint32_t addr) {
// Throttle frames to match the current frame rate. // Throttle frames to match the current frame rate.
sensor_throttle_framerate(); sensor_throttle_framerate();
@ -272,8 +312,14 @@ void sensor_line_callback(uint32_t addr) {
dst += MAIN_FB()->u * bytes_per_pixel * (buffer->offset - MAIN_FB()->y); dst += MAIN_FB()->u * bytes_per_pixel * (buffer->offset - MAIN_FB()->y);
} }
// Copy line to frame buffer. #if defined(OMV_CSI_DMA)
// We're using multiple handles to give each channel the maximum amount of time possible to do the line
// transfer. In most situations only one channel will be running at a time. However, if SDRAM is
// backedup we don't have to disable the channel if it is flushing trailing data to SDRAM.
sensor_copy_line(&CSI_EDMA_Handle[buffer->offset % OMV_CSI_DMA_CHANNEL_COUNT], src, dst);
#else
sensor_copy_line(NULL, src, dst); sensor_copy_line(NULL, src, dst);
#endif
} }
if (++buffer->offset == resolution[sensor.framesize][1]) { if (++buffer->offset == resolution[sensor.framesize][1]) {
@ -286,6 +332,46 @@ void sensor_line_callback(uint32_t addr) {
} }
} }
#if defined(OMV_CSI_DMA)
static void edma_config(sensor_t *sensor, uint32_t bytes_per_pixel) {
uint32_t line_offset_bytes = MAIN_FB()->x * bytes_per_pixel;
uint32_t line_width_bytes = MAIN_FB()->u * bytes_per_pixel;
// YUV422 Source -> Y Destination
if ((sensor->pixformat == PIXFORMAT_GRAYSCALE) && (sensor->hw_flags.gs_bpp == 2)) {
line_width_bytes /= 2;
}
// Destination will be 32-byte aligned. So, we just need to breakup the line width into the largest
// power of 2. Source may have an offset which further limits this to a sub power of 2.
for (int i = 5; i >= 0; i--) {
// 16-byte burst is not supported.
if ((i != 4) && (!(line_width_bytes % (1 << i)))) {
for (int j = i; j >= 0; j--) {
// 16-byte burst is not supported.
if ((j != 4) && (!(line_offset_bytes % (1 << j)))) {
src_inc = src_size = 1 << j;
break;
}
}
dest_inc_size = 1 << i;
break;
}
}
if (sensor->transpose) {
dest_inc_size = bytes_per_pixel;
}
// YUV422 Source -> Y Destination
if ((sensor->pixformat == PIXFORMAT_GRAYSCALE) && (sensor->hw_flags.gs_bpp == 2)) {
src_inc = 2;
src_size = 1;
}
}
#endif
// This is the default snapshot function, which can be replaced in sensor_init functions. // This is the default snapshot function, which can be replaced in sensor_init functions.
int sensor_snapshot(sensor_t *sensor, image_t *image, uint32_t flags) { int sensor_snapshot(sensor_t *sensor, image_t *image, uint32_t flags) {
// Used to restore MAIN_FB's width and height. // Used to restore MAIN_FB's width and height.
@ -315,13 +401,30 @@ int sensor_snapshot(sensor_t *sensor, image_t *image, uint32_t flags) {
if (!(CSI->CR18 & CSI_CR18_CSI_ENABLE_MASK)) { if (!(CSI->CR18 & CSI_CR18_CSI_ENABLE_MASK)) {
framebuffer_setup_buffers(); framebuffer_setup_buffers();
vbuffer_t *buffer = framebuffer_get_tail(FB_PEEK);
if (buffer == NULL) {
return SENSOR_ERROR_FRAMEBUFFER_ERROR;
}
// Re/configure and re/start the CSI. // Re/configure and re/start the CSI.
uint32_t bytes_per_pixel = sensor_get_src_bpp(); uint32_t bytes_per_pixel = sensor_get_src_bpp();
uint32_t dma_line_bytes = resolution[sensor->framesize][0] * bytes_per_pixel; uint32_t dma_line_bytes = resolution[sensor->framesize][0] * bytes_per_pixel;
uint32_t length = dma_line_bytes * h;
// Error out if the transfer size is not compatible with DMA transfer restrictions.
if ((!dma_line_bytes)
|| (dma_line_bytes % sizeof(uint64_t))
|| (dma_line_bytes > (OMV_LINE_BUF_SIZE / 2))
|| (!length)
|| (length % DMA_LENGTH_ALIGNMENT)) {
return SENSOR_ERROR_INVALID_FRAMESIZE;
}
#if defined(OMV_CSI_DMA)
// The code below will enable EDMA data transfer from the line buffer for non-JPEG modes.
if (sensor->pixformat != PIXFORMAT_JPEG) {
edma_config(sensor, bytes_per_pixel);
for (int i = 0; i < OMV_CSI_DMA_CHANNEL_COUNT; i++) {
EDMA_CreateHandle(&CSI_EDMA_Handle[i], OMV_CSI_DMA, OMV_CSI_DMA_CHANNEL_START + i);
EDMA_DisableChannelInterrupts(OMV_CSI_DMA, OMV_CSI_DMA_CHANNEL_START + i, kEDMA_MajorInterruptEnable);
}
}
#endif
if ((sensor->pixformat == PIXFORMAT_RGB565 && sensor->hw_flags.rgb_swap) if ((sensor->pixformat == PIXFORMAT_RGB565 && sensor->hw_flags.rgb_swap)
|| (sensor->pixformat == PIXFORMAT_YUV422 && sensor->hw_flags.yuv_swap)) { || (sensor->pixformat == PIXFORMAT_YUV422 && sensor->hw_flags.yuv_swap)) {
@ -353,7 +456,7 @@ int sensor_snapshot(sensor_t *sensor, image_t *image, uint32_t flags) {
// Wait for the DMA to finish the transfer. // Wait for the DMA to finish the transfer.
for (mp_uint_t ticks = mp_hal_ticks_ms(); buffer == NULL;) { for (mp_uint_t ticks = mp_hal_ticks_ms(); buffer == NULL;) {
MICROPY_EVENT_POLL_HOOK MICROPY_EVENT_POLL_HOOK
if ((mp_hal_ticks_ms() - ticks) > 3000) { if ((mp_hal_ticks_ms() - ticks) > SENSOR_TIMEOUT_MS) {
sensor_abort(true, false); sensor_abort(true, false);
#if defined(OMV_CSI_FSYNC_PIN) #if defined(OMV_CSI_FSYNC_PIN)