feat(dma2d): support RGB24 and BGR24 scramble conversion

Add async color convert coverage for direct RGB24/BGR24 byte-order swaps
and
thread both TX and RX CSC state through DMA2D transactions for future
extensibility.
This commit is contained in:
morris
2026-06-30 12:00:13 +08:00
parent 3160a1a73f
commit 02cf2a8a3d
6 changed files with 344 additions and 79 deletions
@@ -35,7 +35,8 @@ struct async_color_convert_transaction {
dma2d_trans_config_t dma2d_trans_config; // Per-request DMA2D transaction configuration dma2d_trans_config_t dma2d_trans_config; // Per-request DMA2D transaction configuration
async_color_convert_request_t request; // Cached user request used to build DMA2D transaction async_color_convert_request_t request; // Cached user request used to build DMA2D transaction
dma2d_csc_config_t tx_csc; // Cached DMA2D CSC configuration resolved in task context dma2d_csc_config_t tx_csc; // Cached DMA2D TX CSC configuration resolved in task context
dma2d_csc_config_t rx_csc; // Cached DMA2D RX CSC configuration resolved in task context
async_color_convert_isr_cb_t cb_isr; // User ISR callback for this request async_color_convert_isr_cb_t cb_isr; // User ISR callback for this request
void *cb_args; // User callback argument void *cb_args; // User callback argument
async_color_convert_dma2d_context_t *ctx; // Back pointer to parent context async_color_convert_dma2d_context_t *ctx; // Back pointer to parent context
@@ -62,34 +63,65 @@ static esp_err_t async_color_convert_dma2d_convert(async_color_convert_context_t
async_color_convert_isr_cb_t cb_isr, async_color_convert_isr_cb_t cb_isr,
void *cb_args); void *cb_args);
static bool try_convert_request_to_dma2d_csc(const async_color_convert_request_t *request, static bool is_rgb24_or_bgr24_fourcc(esp_color_fourcc_t fourcc)
dma2d_csc_config_t *out_tx_csc) {
return fourcc == ESP_COLOR_FOURCC_BGR24 || fourcc == ESP_COLOR_FOURCC_RGB24;
}
static dma2d_csc_config_t default_tx_csc_config(void)
{
return (dma2d_csc_config_t) {
.tx_csc_option = DMA2D_CSC_TX_NONE,
.pre_scramble = DMA2D_SCRAMBLE_ORDER_BYTE2_1_0,
.post_scramble = DMA2D_SCRAMBLE_ORDER_BYTE2_1_0,
};
}
static dma2d_csc_config_t default_rx_csc_config(void)
{
return (dma2d_csc_config_t) {
.rx_csc_option = DMA2D_CSC_RX_NONE,
.pre_scramble = DMA2D_SCRAMBLE_ORDER_BYTE2_1_0,
.post_scramble = DMA2D_SCRAMBLE_ORDER_BYTE2_1_0,
};
}
static bool resolve_dma2d_csc_configs(const async_color_convert_request_t *request,
dma2d_csc_config_t *out_tx_csc,
dma2d_csc_config_t *out_rx_csc)
{ {
esp_color_fourcc_t src_fourcc = request->src_color_format; esp_color_fourcc_t src_fourcc = request->src_color_format;
esp_color_fourcc_t dst_fourcc = request->dst_color_format; esp_color_fourcc_t dst_fourcc = request->dst_color_format;
bool src_is_rgb24_or_bgr24 = is_rgb24_or_bgr24_fourcc(src_fourcc);
bool dst_is_rgb24_or_bgr24 = is_rgb24_or_bgr24_fourcc(dst_fourcc);
*out_tx_csc = default_tx_csc_config();
*out_rx_csc = default_rx_csc_config();
if (src_fourcc == dst_fourcc) { if (src_fourcc == dst_fourcc) {
out_tx_csc->tx_csc_option = DMA2D_CSC_TX_NONE; return true;
out_tx_csc->pre_scramble = DMA2D_SCRAMBLE_ORDER_BYTE2_1_0; }
out_tx_csc->post_scramble = DMA2D_SCRAMBLE_ORDER_BYTE2_1_0;
if (src_is_rgb24_or_bgr24 && dst_is_rgb24_or_bgr24) {
out_tx_csc->tx_csc_option = DMA2D_CSC_TX_SCRAMBLE; // RGB<->BGR conversion is just a scramble operation
out_tx_csc->pre_scramble = DMA2D_SCRAMBLE_ORDER_BYTE0_1_2;
return true; return true;
} }
if (src_fourcc == ESP_COLOR_FOURCC_RGB16 && dst_fourcc == ESP_COLOR_FOURCC_BGR24) { if (src_fourcc == ESP_COLOR_FOURCC_RGB16 && dst_fourcc == ESP_COLOR_FOURCC_BGR24) {
out_tx_csc->tx_csc_option = DMA2D_CSC_TX_RGB565_TO_RGB888; out_tx_csc->tx_csc_option = DMA2D_CSC_TX_RGB565_TO_RGB888;
out_tx_csc->pre_scramble = DMA2D_SCRAMBLE_ORDER_BYTE2_1_0;
out_tx_csc->post_scramble = DMA2D_SCRAMBLE_ORDER_BYTE2_1_0;
return true; return true;
} }
if (src_fourcc == ESP_COLOR_FOURCC_BGR24 && dst_fourcc == ESP_COLOR_FOURCC_RGB16) { if (src_is_rgb24_or_bgr24 && dst_fourcc == ESP_COLOR_FOURCC_RGB16) {
out_tx_csc->tx_csc_option = DMA2D_CSC_TX_RGB888_TO_RGB565; out_tx_csc->tx_csc_option = DMA2D_CSC_TX_RGB888_TO_RGB565;
out_tx_csc->pre_scramble = DMA2D_SCRAMBLE_ORDER_BYTE2_1_0; if (src_fourcc == ESP_COLOR_FOURCC_RGB24) {
out_tx_csc->post_scramble = DMA2D_SCRAMBLE_ORDER_BYTE2_1_0; out_tx_csc->pre_scramble = DMA2D_SCRAMBLE_ORDER_BYTE0_1_2;
}
return true; return true;
} }
if (src_fourcc == ESP_COLOR_FOURCC_BGR24 && dst_fourcc == ESP_COLOR_FOURCC_UYVY) { if (src_is_rgb24_or_bgr24 && dst_fourcc == ESP_COLOR_FOURCC_UYVY) {
if (request->color_conv_std == COLOR_CONV_STD_RGB_YUV_BT601) { if (request->color_conv_std == COLOR_CONV_STD_RGB_YUV_BT601) {
out_tx_csc->tx_csc_option = DMA2D_CSC_TX_RGB888_TO_YUV422_601; out_tx_csc->tx_csc_option = DMA2D_CSC_TX_RGB888_TO_YUV422_601;
} else if (request->color_conv_std == COLOR_CONV_STD_RGB_YUV_BT709) { } else if (request->color_conv_std == COLOR_CONV_STD_RGB_YUV_BT709) {
@@ -97,8 +129,9 @@ static bool try_convert_request_to_dma2d_csc(const async_color_convert_request_t
} else { } else {
return false; return false;
} }
out_tx_csc->pre_scramble = DMA2D_SCRAMBLE_ORDER_BYTE2_1_0; if (src_fourcc == ESP_COLOR_FOURCC_RGB24) {
out_tx_csc->post_scramble = DMA2D_SCRAMBLE_ORDER_BYTE2_1_0; out_tx_csc->pre_scramble = DMA2D_SCRAMBLE_ORDER_BYTE0_1_2;
}
return true; return true;
} }
@@ -110,8 +143,6 @@ static bool try_convert_request_to_dma2d_csc(const async_color_convert_request_t
} else { } else {
return false; return false;
} }
out_tx_csc->pre_scramble = DMA2D_SCRAMBLE_ORDER_BYTE2_1_0;
out_tx_csc->post_scramble = DMA2D_SCRAMBLE_ORDER_BYTE2_1_0;
return true; return true;
} }
@@ -123,6 +154,11 @@ static inline bool needs_tx_csc(const dma2d_csc_config_t *tx_csc)
return tx_csc->tx_csc_option != DMA2D_CSC_TX_NONE; return tx_csc->tx_csc_option != DMA2D_CSC_TX_NONE;
} }
static inline bool needs_rx_csc(const dma2d_csc_config_t *rx_csc)
{
return rx_csc->rx_csc_option != DMA2D_CSC_RX_NONE;
}
static esp_err_t sync_if_cacheable(void *addr, size_t size, int flags) static esp_err_t sync_if_cacheable(void *addr, size_t size, int flags)
{ {
return esp_cache_get_line_size_by_addr(addr) > 0 ? esp_cache_msync(addr, size, flags) : ESP_OK; return esp_cache_get_line_size_by_addr(addr) > 0 ? esp_cache_msync(addr, size, flags) : ESP_OK;
@@ -239,9 +275,8 @@ static bool async_color_convert_on_job_picked(uint32_t channel_num,
dma2d_set_transfer_ability(tx_chan, &transfer_ability); dma2d_set_transfer_ability(tx_chan, &transfer_ability);
dma2d_set_transfer_ability(rx_chan, &transfer_ability); dma2d_set_transfer_ability(rx_chan, &transfer_ability);
if (needs_tx_csc(&trans->tx_csc)) { dma2d_configure_color_space_conversion(tx_chan, &trans->tx_csc);
dma2d_configure_color_space_conversion(tx_chan, &trans->tx_csc); dma2d_configure_color_space_conversion(rx_chan, &trans->rx_csc);
}
dma2d_rx_event_callbacks_t cbs = { dma2d_rx_event_callbacks_t cbs = {
.on_recv_eof = async_color_convert_done_cb, .on_recv_eof = async_color_convert_done_cb,
@@ -301,16 +336,18 @@ static esp_err_t async_color_convert_dma2d_convert(async_color_convert_context_t
esp_color_fourcc_t src_fourcc = request->src_color_format; esp_color_fourcc_t src_fourcc = request->src_color_format;
esp_color_fourcc_t dst_fourcc = request->dst_color_format; esp_color_fourcc_t dst_fourcc = request->dst_color_format;
trans->tx_csc = (dma2d_csc_config_t) {}; trans->tx_csc = default_tx_csc_config();
ESP_GOTO_ON_FALSE(try_convert_request_to_dma2d_csc(request, &trans->tx_csc), trans->rx_csc = default_rx_csc_config();
ESP_GOTO_ON_FALSE(resolve_dma2d_csc_configs(request, &trans->tx_csc, &trans->rx_csc),
ESP_ERR_INVALID_ARG, recycle_and_out, TAG, "unsupported color conversion mode"); ESP_ERR_INVALID_ARG, recycle_and_out, TAG, "unsupported color conversion mode");
trans->dma2d_trans_config.channel_flags = DMA2D_CHANNEL_FUNCTION_FLAG_SIBLING;
if (needs_tx_csc(&trans->tx_csc)) { if (needs_tx_csc(&trans->tx_csc)) {
trans->dma2d_trans_config.channel_flags = DMA2D_CHANNEL_FUNCTION_FLAG_SIBLING | DMA2D_CHANNEL_FUNCTION_FLAG_TX_CSC; trans->dma2d_trans_config.channel_flags |= DMA2D_CHANNEL_FUNCTION_FLAG_TX_CSC;
} else { }
trans->dma2d_trans_config.channel_flags = DMA2D_CHANNEL_FUNCTION_FLAG_SIBLING; if (needs_rx_csc(&trans->rx_csc)) {
trans->dma2d_trans_config.channel_flags |= DMA2D_CHANNEL_FUNCTION_FLAG_RX_CSC;
} }
setup_desc(trans->tx_desc, setup_desc(trans->tx_desc,
(void *)request->src_buffer, (void *)request->src_buffer,
request->src_stride, request->src_stride,
@@ -342,7 +379,7 @@ static esp_err_t async_color_convert_dma2d_convert(async_color_convert_context_t
recycle_and_out, TAG, "source cache sync failed"); recycle_and_out, TAG, "source cache sync failed");
ESP_GOTO_ON_ERROR(sync_if_cacheable(request->dst_buffer, dst_total_size, ESP_GOTO_ON_ERROR(sync_if_cacheable(request->dst_buffer, dst_total_size,
ESP_CACHE_MSYNC_FLAG_DIR_C2M | ESP_CACHE_MSYNC_FLAG_INVALIDATE), ESP_CACHE_MSYNC_FLAG_DIR_C2M | ESP_CACHE_MSYNC_FLAG_INVALIDATE | ESP_CACHE_MSYNC_FLAG_UNALIGNED),
recycle_and_out, TAG, "destination cache sync failed"); recycle_and_out, TAG, "destination cache sync failed");
ESP_GOTO_ON_ERROR(sync_if_cacheable(trans->tx_desc, color_ctx->desc_alloc_size, ESP_GOTO_ON_ERROR(sync_if_cacheable(trans->tx_desc, color_ctx->desc_alloc_size,
@@ -48,10 +48,10 @@ TEST_CASE("async color convert basic callback", "[async_color_convert]")
uint16_t *src565 = heap_caps_aligned_calloc(64, pixel_num, sizeof(uint16_t), uint16_t *src565 = heap_caps_aligned_calloc(64, pixel_num, sizeof(uint16_t),
MALLOC_CAP_INTERNAL | MALLOC_CAP_DMA | MALLOC_CAP_8BIT); MALLOC_CAP_INTERNAL | MALLOC_CAP_DMA | MALLOC_CAP_8BIT);
uint8_t *dst888 = heap_caps_aligned_calloc(64, pixel_num, 3, uint8_t *dst_bgr24 = heap_caps_aligned_calloc(64, pixel_num, 3,
MALLOC_CAP_INTERNAL | MALLOC_CAP_DMA | MALLOC_CAP_8BIT); MALLOC_CAP_INTERNAL | MALLOC_CAP_DMA | MALLOC_CAP_8BIT);
TEST_ASSERT_NOT_NULL(src565); TEST_ASSERT_NOT_NULL(src565);
TEST_ASSERT_NOT_NULL(dst888); TEST_ASSERT_NOT_NULL(dst_bgr24);
for (uint32_t i = 0; i < pixel_num; i++) { for (uint32_t i = 0; i < pixel_num; i++) {
src565[i] = (uint16_t)((i * 13) ^ 0x5AA5); src565[i] = (uint16_t)((i * 13) ^ 0x5AA5);
@@ -71,7 +71,7 @@ TEST_CASE("async color convert basic callback", "[async_color_convert]")
.src_height = height, .src_height = height,
.src_x = 0, .src_x = 0,
.src_y = 0, .src_y = 0,
.dst_buffer = dst888, .dst_buffer = dst_bgr24,
.dst_stride = width, .dst_stride = width,
.dst_height = height, .dst_height = height,
.dst_x = 0, .dst_x = 0,
@@ -96,10 +96,10 @@ TEST_CASE("async color convert basic callback", "[async_color_convert]")
TEST_ESP_OK(esp_async_color_convert_uninstall(conv_hdl)); TEST_ESP_OK(esp_async_color_convert_uninstall(conv_hdl));
free(src565); free(src565);
free(dst888); free(dst_bgr24);
} }
TEST_CASE("async color convert roundtrip: RGB565<->RGB888", "[async_color_convert]") TEST_CASE("async color convert roundtrip: RGB16<->BGR24", "[async_color_convert]")
{ {
const uint32_t width = 32; const uint32_t width = 32;
const uint32_t height = 20; const uint32_t height = 20;
@@ -107,12 +107,12 @@ TEST_CASE("async color convert roundtrip: RGB565<->RGB888", "[async_color_conver
uint16_t *src565 = heap_caps_aligned_calloc(64, pixel_num, sizeof(uint16_t), uint16_t *src565 = heap_caps_aligned_calloc(64, pixel_num, sizeof(uint16_t),
MALLOC_CAP_INTERNAL | MALLOC_CAP_DMA | MALLOC_CAP_8BIT); MALLOC_CAP_INTERNAL | MALLOC_CAP_DMA | MALLOC_CAP_8BIT);
uint8_t *mid888 = heap_caps_aligned_calloc(64, pixel_num, 3, uint8_t *mid_bgr24 = heap_caps_aligned_calloc(64, pixel_num, 3,
MALLOC_CAP_INTERNAL | MALLOC_CAP_DMA | MALLOC_CAP_8BIT); MALLOC_CAP_INTERNAL | MALLOC_CAP_DMA | MALLOC_CAP_8BIT);
uint16_t *dst565 = heap_caps_aligned_calloc(64, pixel_num, sizeof(uint16_t), uint16_t *dst565 = heap_caps_aligned_calloc(64, pixel_num, sizeof(uint16_t),
MALLOC_CAP_INTERNAL | MALLOC_CAP_DMA | MALLOC_CAP_8BIT); MALLOC_CAP_INTERNAL | MALLOC_CAP_DMA | MALLOC_CAP_8BIT);
TEST_ASSERT_NOT_NULL(src565); TEST_ASSERT_NOT_NULL(src565);
TEST_ASSERT_NOT_NULL(mid888); TEST_ASSERT_NOT_NULL(mid_bgr24);
TEST_ASSERT_NOT_NULL(dst565); TEST_ASSERT_NOT_NULL(dst565);
for (uint32_t i = 0; i < pixel_num; i++) { for (uint32_t i = 0; i < pixel_num; i++) {
@@ -127,13 +127,13 @@ TEST_CASE("async color convert roundtrip: RGB565<->RGB888", "[async_color_conver
async_color_convert_handle_t conv_hdl = NULL; async_color_convert_handle_t conv_hdl = NULL;
TEST_ESP_OK(esp_async_color_convert_install_dma2d(&config, &conv_hdl)); TEST_ESP_OK(esp_async_color_convert_install_dma2d(&config, &conv_hdl));
async_color_convert_request_t req_565_to_888 = { async_color_convert_request_t req_565_to_bgr24 = {
.src_buffer = src565, .src_buffer = src565,
.src_stride = width, .src_stride = width,
.src_height = height, .src_height = height,
.src_x = 0, .src_x = 0,
.src_y = 0, .src_y = 0,
.dst_buffer = mid888, .dst_buffer = mid_bgr24,
.dst_stride = width, .dst_stride = width,
.dst_height = height, .dst_height = height,
.dst_x = 0, .dst_x = 0,
@@ -144,8 +144,8 @@ TEST_CASE("async color convert roundtrip: RGB565<->RGB888", "[async_color_conver
.dst_color_format = ESP_COLOR_FOURCC_BGR24, .dst_color_format = ESP_COLOR_FOURCC_BGR24,
}; };
async_color_convert_request_t req_888_to_565 = { async_color_convert_request_t req_bgr24_to_565 = {
.src_buffer = mid888, .src_buffer = mid_bgr24,
.src_stride = width, .src_stride = width,
.src_height = height, .src_height = height,
.src_x = 0, .src_x = 0,
@@ -161,8 +161,8 @@ TEST_CASE("async color convert roundtrip: RGB565<->RGB888", "[async_color_conver
.dst_color_format = ESP_COLOR_FOURCC_RGB16, .dst_color_format = ESP_COLOR_FOURCC_RGB16,
}; };
TEST_ESP_OK(esp_color_convert_blocking(conv_hdl, &req_565_to_888, -1)); TEST_ESP_OK(esp_color_convert_blocking(conv_hdl, &req_565_to_bgr24, -1));
TEST_ESP_OK(esp_color_convert_blocking(conv_hdl, &req_888_to_565, -1)); TEST_ESP_OK(esp_color_convert_blocking(conv_hdl, &req_bgr24_to_565, -1));
// The final dst565 should be the same as the original src565 after round-trip conversion // The final dst565 should be the same as the original src565 after round-trip conversion
TEST_ASSERT_EQUAL_MEMORY(src565, dst565, pixel_num * sizeof(uint16_t)); TEST_ASSERT_EQUAL_MEMORY(src565, dst565, pixel_num * sizeof(uint16_t));
@@ -170,7 +170,7 @@ TEST_CASE("async color convert roundtrip: RGB565<->RGB888", "[async_color_conver
TEST_ESP_OK(esp_async_color_convert_uninstall(conv_hdl)); TEST_ESP_OK(esp_async_color_convert_uninstall(conv_hdl));
free(src565); free(src565);
free(mid888); free(mid_bgr24);
free(dst565); free(dst565);
} }
@@ -305,7 +305,189 @@ static void uyvy_to_bgr24_reference_image(const uint8_t *src_uyvy, uint8_t *dst_
} }
} }
TEST_CASE("async color convert UYVY->RGB888 matches reference", "[async_color_convert]") TEST_CASE("async color convert swaps RGB24 and BGR24 byte order", "[async_color_convert]")
{
const uint32_t width = 4;
const uint32_t height = 2;
const size_t pixel_count = width * height;
const size_t buf_size = pixel_count * 3;
static const uint8_t src_rgb24[] = {
0x10, 0x20, 0x30, 0x7F, 0x80, 0x81, 0xAA, 0x55, 0xFE, 0x01, 0xC0, 0x99,
0xDE, 0xAD, 0xBE, 0x00, 0x11, 0x22, 0x44, 0x88, 0xCC, 0xF0, 0x0D, 0x42,
};
static const uint8_t src_bgr24[] = {
0x30, 0x20, 0x10, 0x81, 0x80, 0x7F, 0xFE, 0x55, 0xAA, 0x99, 0xC0, 0x01,
0xBE, 0xAD, 0xDE, 0x22, 0x11, 0x00, 0xCC, 0x88, 0x44, 0x42, 0x0D, 0xF0,
};
TEST_ASSERT_EQUAL(sizeof(src_rgb24), buf_size);
TEST_ASSERT_EQUAL(sizeof(src_bgr24), buf_size);
uint8_t *rgb24 = heap_caps_aligned_calloc(64, 1, buf_size,
MALLOC_CAP_INTERNAL | MALLOC_CAP_DMA | MALLOC_CAP_8BIT);
uint8_t *bgr24 = heap_caps_aligned_calloc(64, 1, buf_size,
MALLOC_CAP_INTERNAL | MALLOC_CAP_DMA | MALLOC_CAP_8BIT);
uint8_t *dst_bgr24 = heap_caps_aligned_calloc(64, 1, buf_size,
MALLOC_CAP_INTERNAL | MALLOC_CAP_DMA | MALLOC_CAP_8BIT);
uint8_t *dst_rgb24 = heap_caps_aligned_calloc(64, 1, buf_size,
MALLOC_CAP_INTERNAL | MALLOC_CAP_DMA | MALLOC_CAP_8BIT);
TEST_ASSERT_NOT_NULL(rgb24);
TEST_ASSERT_NOT_NULL(bgr24);
TEST_ASSERT_NOT_NULL(dst_bgr24);
TEST_ASSERT_NOT_NULL(dst_rgb24);
memcpy(rgb24, src_rgb24, buf_size);
memcpy(bgr24, src_bgr24, buf_size);
memset(dst_bgr24, 0xA5, buf_size);
memset(dst_rgb24, 0x5A, buf_size);
async_color_convert_config_t config = {
.backlog = 1,
.intr_priority = 0,
.dma_burst_size = 16,
};
async_color_convert_handle_t conv_hdl = NULL;
TEST_ESP_OK(esp_async_color_convert_install_dma2d(&config, &conv_hdl));
async_color_convert_request_t req_rgb_to_bgr = {
.src_buffer = rgb24,
.src_stride = width,
.src_height = height,
.src_x = 0,
.src_y = 0,
.dst_buffer = dst_bgr24,
.dst_stride = width,
.dst_height = height,
.dst_x = 0,
.dst_y = 0,
.copy_width = width,
.copy_height = height,
.src_color_format = ESP_COLOR_FOURCC_RGB24,
.dst_color_format = ESP_COLOR_FOURCC_BGR24,
};
async_color_convert_request_t req_bgr_to_rgb = {
.src_buffer = bgr24,
.src_stride = width,
.src_height = height,
.src_x = 0,
.src_y = 0,
.dst_buffer = dst_rgb24,
.dst_stride = width,
.dst_height = height,
.dst_x = 0,
.dst_y = 0,
.copy_width = width,
.copy_height = height,
.src_color_format = ESP_COLOR_FOURCC_BGR24,
.dst_color_format = ESP_COLOR_FOURCC_RGB24,
};
TEST_ESP_OK(esp_color_convert_blocking(conv_hdl, &req_rgb_to_bgr, -1));
TEST_ASSERT_EQUAL_MEMORY(src_bgr24, dst_bgr24, buf_size);
TEST_ESP_OK(esp_color_convert_blocking(conv_hdl, &req_bgr_to_rgb, -1));
TEST_ASSERT_EQUAL_MEMORY(src_rgb24, dst_rgb24, buf_size);
TEST_ESP_OK(esp_async_color_convert_uninstall(conv_hdl));
free(rgb24);
free(bgr24);
free(dst_bgr24);
free(dst_rgb24);
}
// Verifies the scramble route and BGR24/RGB24->UYVY conversion compose correctly.
TEST_CASE("async color convert RGB24 and BGR24 inputs produce identical UYVY output", "[async_color_convert]")
{
const uint32_t width = 4;
const uint32_t height = 2;
const size_t pixel_count = width * height;
const size_t rgb_size = pixel_count * 3;
const size_t uyvy_size = pixel_count * 2;
static const uint8_t src_rgb24[] = {
0x10, 0x20, 0x30, 0x7F, 0x80, 0x81, 0xAA, 0x55, 0xFE, 0x01, 0xC0, 0x99,
0xDE, 0xAD, 0xBE, 0x00, 0x11, 0x22, 0x44, 0x88, 0xCC, 0xF0, 0x0D, 0x42,
};
static const uint8_t src_bgr24[] = {
0x30, 0x20, 0x10, 0x81, 0x80, 0x7F, 0xFE, 0x55, 0xAA, 0x99, 0xC0, 0x01,
0xBE, 0xAD, 0xDE, 0x22, 0x11, 0x00, 0xCC, 0x88, 0x44, 0x42, 0x0D, 0xF0,
};
const color_conv_std_rgb_yuv_t conv_std = COLOR_CONV_STD_RGB_YUV_BT601;
TEST_ASSERT_EQUAL(sizeof(src_rgb24), rgb_size);
TEST_ASSERT_EQUAL(sizeof(src_bgr24), rgb_size);
uint8_t *rgb24 = heap_caps_aligned_calloc(64, 1, rgb_size,
MALLOC_CAP_INTERNAL | MALLOC_CAP_DMA | MALLOC_CAP_8BIT);
uint8_t *bgr24 = heap_caps_aligned_calloc(64, 1, rgb_size,
MALLOC_CAP_INTERNAL | MALLOC_CAP_DMA | MALLOC_CAP_8BIT);
uint8_t *dst_from_rgb24 = heap_caps_aligned_calloc(64, 1, uyvy_size,
MALLOC_CAP_INTERNAL | MALLOC_CAP_DMA | MALLOC_CAP_8BIT);
uint8_t *dst_from_bgr24 = heap_caps_aligned_calloc(64, 1, uyvy_size,
MALLOC_CAP_INTERNAL | MALLOC_CAP_DMA | MALLOC_CAP_8BIT);
TEST_ASSERT_NOT_NULL(rgb24);
TEST_ASSERT_NOT_NULL(bgr24);
TEST_ASSERT_NOT_NULL(dst_from_rgb24);
TEST_ASSERT_NOT_NULL(dst_from_bgr24);
memcpy(rgb24, src_rgb24, rgb_size);
memcpy(bgr24, src_bgr24, rgb_size);
async_color_convert_config_t config = {
.backlog = 2,
.intr_priority = 0,
.dma_burst_size = 16,
};
async_color_convert_handle_t conv_hdl = NULL;
TEST_ESP_OK(esp_async_color_convert_install_dma2d(&config, &conv_hdl));
memset(dst_from_rgb24, 0xA5, uyvy_size);
memset(dst_from_bgr24, 0x5A, uyvy_size);
async_color_convert_request_t req_rgb24_to_uyvy = {
.src_buffer = rgb24,
.src_stride = width,
.src_height = height,
.src_x = 0,
.src_y = 0,
.dst_buffer = dst_from_rgb24,
.dst_stride = width,
.dst_height = height,
.dst_x = 0,
.dst_y = 0,
.copy_width = width,
.copy_height = height,
.src_color_format = ESP_COLOR_FOURCC_RGB24,
.dst_color_format = ESP_COLOR_FOURCC_UYVY,
.color_conv_std = conv_std,
};
async_color_convert_request_t req_bgr24_to_uyvy = {
.src_buffer = bgr24,
.src_stride = width,
.src_height = height,
.src_x = 0,
.src_y = 0,
.dst_buffer = dst_from_bgr24,
.dst_stride = width,
.dst_height = height,
.dst_x = 0,
.dst_y = 0,
.copy_width = width,
.copy_height = height,
.src_color_format = ESP_COLOR_FOURCC_BGR24,
.dst_color_format = ESP_COLOR_FOURCC_UYVY,
.color_conv_std = conv_std,
};
TEST_ESP_OK(esp_color_convert_blocking(conv_hdl, &req_rgb24_to_uyvy, -1));
TEST_ESP_OK(esp_color_convert_blocking(conv_hdl, &req_bgr24_to_uyvy, -1));
TEST_ASSERT_EQUAL_MEMORY(dst_from_bgr24, dst_from_rgb24, uyvy_size);
TEST_ESP_OK(esp_async_color_convert_uninstall(conv_hdl));
free(rgb24);
free(bgr24);
free(dst_from_rgb24);
free(dst_from_bgr24);
}
TEST_CASE("async color convert UYVY->BGR24 matches reference", "[async_color_convert]")
{ {
const uint32_t src_stride = 32; const uint32_t src_stride = 32;
const uint32_t dst_stride = 64; const uint32_t dst_stride = 64;
@@ -1009,7 +1009,7 @@ static inline void dma2d_ll_tx_configure_color_space_conv(dma2d_dev_t *dev, uint
input_sel = 7; input_sel = 7;
break; break;
case DMA2D_CSC_TX_SCRAMBLE: case DMA2D_CSC_TX_SCRAMBLE:
input_sel = 2; // Or 3 input_sel = 3; // Other 3-byte/pixel input path
proc_en = false; proc_en = false;
output_sel = 2; output_sel = 2;
break; break;
+3 -1
View File
@@ -519,7 +519,9 @@ esp_err_t esp_lcd_dpi_panel_enable_dma2d(esp_lcd_panel_handle_t panel)
// Initialize the async color convert backend used by the built-in DMA2D copy hook. // Initialize the async color convert backend used by the built-in DMA2D copy hook.
// Use its default backlog to queue multiple frame buffer copy requests. // Use its default backlog to queue multiple frame buffer copy requests.
async_color_convert_config_t fbcpy_config = {}; async_color_convert_config_t fbcpy_config = {
.dma_burst_size = 128, // for better performance
};
ESP_RETURN_ON_ERROR(esp_async_color_convert_install_dma2d(&fbcpy_config, &dpi_panel->fbcpy_handle), TAG, "install async frame buffer copy backend failed"); ESP_RETURN_ON_ERROR(esp_async_color_convert_install_dma2d(&fbcpy_config, &dpi_panel->fbcpy_handle), TAG, "install async frame buffer copy backend failed");
// Register the DMA2D draw bitmap hook // Register the DMA2D draw bitmap hook
@@ -148,7 +148,7 @@ Both the source window and destination window must stay within the bounds of the
Supported Conversions Supported Conversions
--------------------- ---------------------
The following format pairs are supported by this driver: The following format pairs are currently supported by this driver:
.. list-table:: .. list-table::
:header-rows: 1 :header-rows: 1
@@ -156,32 +156,54 @@ The following format pairs are supported by this driver:
* - Source format * - Source format
- Destination format - Destination format
- Conversion standard - Conversion standard
* - same as destination (skip conversion) * - ``ESP_COLOR_FOURCC_RGB16``
- same as source (skip conversion) - ``ESP_COLOR_FOURCC_RGB16``
- N/A - N/A
* - RGB565 * - ``ESP_COLOR_FOURCC_BGR24``
- RGB888 - ``ESP_COLOR_FOURCC_BGR24``
- N/A - N/A
* - RGB888 * - ``ESP_COLOR_FOURCC_RGB24``
- RGB565 - ``ESP_COLOR_FOURCC_RGB24``
- N/A - N/A
* - RGB888 * - ``ESP_COLOR_FOURCC_UYVY``
- UYVY422 - ``ESP_COLOR_FOURCC_UYVY``
- N/A
* - ``ESP_COLOR_FOURCC_BGR24``
- ``ESP_COLOR_FOURCC_RGB24``
- N/A
* - ``ESP_COLOR_FOURCC_RGB24``
- ``ESP_COLOR_FOURCC_BGR24``
- N/A
* - ``ESP_COLOR_FOURCC_RGB16``
- ``ESP_COLOR_FOURCC_BGR24``
- N/A
* - ``ESP_COLOR_FOURCC_BGR24``
- ``ESP_COLOR_FOURCC_RGB16``
- N/A
* - ``ESP_COLOR_FOURCC_RGB24``
- ``ESP_COLOR_FOURCC_RGB16``
- N/A
* - ``ESP_COLOR_FOURCC_BGR24``
- ``ESP_COLOR_FOURCC_UYVY``
- BT.601 - BT.601
* - RGB888 * - ``ESP_COLOR_FOURCC_BGR24``
- UYVY422 - ``ESP_COLOR_FOURCC_UYVY``
- BT.709 - BT.709
* - UYVY422 * - ``ESP_COLOR_FOURCC_RGB24``
- RGB888 - ``ESP_COLOR_FOURCC_UYVY``
- BT.601 - BT.601
* - UYVY422 * - ``ESP_COLOR_FOURCC_RGB24``
- RGB888 - ``ESP_COLOR_FOURCC_UYVY``
- BT.709
* - ``ESP_COLOR_FOURCC_UYVY``
- ``ESP_COLOR_FOURCC_BGR24``
- BT.601
* - ``ESP_COLOR_FOURCC_UYVY``
- ``ESP_COLOR_FOURCC_BGR24``
- BT.709 - BT.709
.. note:: .. note::
In this driver, RGB888 uses ``ESP_COLOR_FOURCC_BGR24`` and UYVY422 uses ``ESP_COLOR_FOURCC_UYVY``.
Always set :cpp:member:`async_color_convert_request_t::src_color_format` and Always set :cpp:member:`async_color_convert_request_t::src_color_format` and
:cpp:member:`async_color_convert_request_t::dst_color_format`. :cpp:member:`async_color_convert_request_t::dst_color_format`.
Set :cpp:member:`async_color_convert_request_t::color_conv_std` when converting between RGB and YUV. Set :cpp:member:`async_color_convert_request_t::color_conv_std` when converting between RGB and YUV.
@@ -148,7 +148,7 @@
支持的转换格式 支持的转换格式
-------------- --------------
本驱动支持以下格式组合: 本驱动当前支持以下格式组合:
.. list-table:: .. list-table::
:header-rows: 1 :header-rows: 1
@@ -156,32 +156,54 @@
* - 源格式 * - 源格式
- 目标格式 - 目标格式
- 转换标准 - 转换标准
* - 与目标格式相同(跳过转换) * - ``ESP_COLOR_FOURCC_RGB16``
- 与源格式相同(跳过转换) - ``ESP_COLOR_FOURCC_RGB16``
- 不适用 - 不适用
* - RGB565 * - ``ESP_COLOR_FOURCC_BGR24``
- RGB888 - ``ESP_COLOR_FOURCC_BGR24``
- 不适用 - 不适用
* - RGB888 * - ``ESP_COLOR_FOURCC_RGB24``
- RGB565 - ``ESP_COLOR_FOURCC_RGB24``
- 不适用 - 不适用
* - RGB888 * - ``ESP_COLOR_FOURCC_UYVY``
- UYVY422 - ``ESP_COLOR_FOURCC_UYVY``
- 不适用
* - ``ESP_COLOR_FOURCC_BGR24``
- ``ESP_COLOR_FOURCC_RGB24``
- 不适用
* - ``ESP_COLOR_FOURCC_RGB24``
- ``ESP_COLOR_FOURCC_BGR24``
- 不适用
* - ``ESP_COLOR_FOURCC_RGB16``
- ``ESP_COLOR_FOURCC_BGR24``
- 不适用
* - ``ESP_COLOR_FOURCC_BGR24``
- ``ESP_COLOR_FOURCC_RGB16``
- 不适用
* - ``ESP_COLOR_FOURCC_RGB24``
- ``ESP_COLOR_FOURCC_RGB16``
- 不适用
* - ``ESP_COLOR_FOURCC_BGR24``
- ``ESP_COLOR_FOURCC_UYVY``
- BT.601 - BT.601
* - RGB888 * - ``ESP_COLOR_FOURCC_BGR24``
- UYVY422 - ``ESP_COLOR_FOURCC_UYVY``
- BT.709 - BT.709
* - UYVY422 * - ``ESP_COLOR_FOURCC_RGB24``
- RGB888 - ``ESP_COLOR_FOURCC_UYVY``
- BT.601 - BT.601
* - UYVY422 * - ``ESP_COLOR_FOURCC_RGB24``
- RGB888 - ``ESP_COLOR_FOURCC_UYVY``
- BT.709
* - ``ESP_COLOR_FOURCC_UYVY``
- ``ESP_COLOR_FOURCC_BGR24``
- BT.601
* - ``ESP_COLOR_FOURCC_UYVY``
- ``ESP_COLOR_FOURCC_BGR24``
- BT.709 - BT.709
.. note:: .. note::
在本驱动中,RGB888 使用 ``ESP_COLOR_FOURCC_BGR24``,UYVY422 使用 ``ESP_COLOR_FOURCC_UYVY``。
所有请求都需要设置 :cpp:member:`async_color_convert_request_t::src_color_format` 和 所有请求都需要设置 :cpp:member:`async_color_convert_request_t::src_color_format` 和
:cpp:member:`async_color_convert_request_t::dst_color_format`。 :cpp:member:`async_color_convert_request_t::dst_color_format`。
当在 RGB 和 YUV 之间转换时,还需要设置 :cpp:member:`async_color_convert_request_t::color_conv_std`。 当在 RGB 和 YUV 之间转换时,还需要设置 :cpp:member:`async_color_convert_request_t::color_conv_std`。