// SPDX-License-Identifier: Apache-2.0 #include #include #ifdef ESP_PLATFORM #include #endif #if defined(ESP_PLATFORM) && SOC_PPA_SUPPORTED #include #include #include #define TAG "lvgl_ppa" // PPA output buffer alignment requirement (address AND size - see lvgl_ppa_get_or_create()): // cache line size is hardware-specific, but 64 bytes covers every current PPA-capable target's // L1/L2 cache line size, so a fixed constant avoids needing per-SoC Kconfig lookups here. #define LVGL_PPA_ALIGN 64 #define LVGL_PPA_ALIGN_UP(n) (((n) + (LVGL_PPA_ALIGN - 1)) & ~(size_t)(LVGL_PPA_ALIGN - 1)) struct LvglPpa { ppa_client_handle_t client; uint8_t* out_buffer; size_t out_buffer_size; bool logged_first_rotate; }; bool lvgl_ppa_is_supported(void) { return true; } bool lvgl_ppa_supports_color_format(lv_color_format_t color_format) { return color_format == LV_COLOR_FORMAT_RGB565 || color_format == LV_COLOR_FORMAT_RGB888; } static ppa_srm_color_mode_t lvgl_ppa_color_mode(lv_color_format_t color_format) { return color_format == LV_COLOR_FORMAT_RGB888 ? PPA_SRM_COLOR_MODE_RGB888 : PPA_SRM_COLOR_MODE_RGB565; } void* lvgl_ppa_get_or_create(size_t out_buffer_size_bytes) { struct LvglPpa* ppa = (struct LvglPpa*)calloc(1, sizeof(struct LvglPpa)); if (ppa == NULL) { return NULL; } // PPA requires the output buffer's size, not just its address, to be cache-line aligned (see // ppa_out_pic_blk_config_t's buffer_size doc) - round up before allocating so both match. size_t aligned_size = LVGL_PPA_ALIGN_UP(out_buffer_size_bytes); ppa->out_buffer = (uint8_t*)heap_caps_aligned_alloc(LVGL_PPA_ALIGN, aligned_size, MALLOC_CAP_DMA | MALLOC_CAP_8BIT); if (ppa->out_buffer == NULL) { ppa->out_buffer = (uint8_t*)heap_caps_aligned_alloc(LVGL_PPA_ALIGN, aligned_size, MALLOC_CAP_DEFAULT); } if (ppa->out_buffer == NULL) { LOG_E(TAG, "Failed to allocate PPA output buffer (%u bytes)", (unsigned)aligned_size); free(ppa); return NULL; } ppa->out_buffer_size = aligned_size; ppa_client_config_t client_config = { .oper_type = PPA_OPERATION_SRM, }; if (ppa_register_client(&client_config, &ppa->client) != ESP_OK) { LOG_E(TAG, "Failed to register PPA client"); heap_caps_free(ppa->out_buffer); free(ppa); return NULL; } LOG_I(TAG, "PPA client created (out buffer: %u bytes)", (unsigned)aligned_size); return ppa; } void lvgl_ppa_delete(void* ppa_handle) { if (ppa_handle == NULL) { return; } struct LvglPpa* ppa = (struct LvglPpa*)ppa_handle; ppa_unregister_client(ppa->client); heap_caps_free(ppa->out_buffer); free(ppa); } void* lvgl_ppa_rotate( void* ppa_handle, const uint8_t* in_buff, int32_t w, int32_t h, lv_display_rotation_t rotation, lv_color_format_t color_format, bool swap_bytes ) { struct LvglPpa* ppa = (struct LvglPpa*)ppa_handle; ppa_srm_color_mode_t color_mode = lvgl_ppa_color_mode(color_format); bool swapped_wh = rotation == LV_DISPLAY_ROTATION_90 || rotation == LV_DISPLAY_ROTATION_270; int32_t out_w = swapped_wh ? h : w; int32_t out_h = swapped_wh ? w : h; const ppa_srm_oper_config_t oper_config = { .in = { .buffer = in_buff, .pic_w = (uint32_t)w, .pic_h = (uint32_t)h, .block_w = (uint32_t)w, .block_h = (uint32_t)h, .block_offset_x = 0, .block_offset_y = 0, .srm_cm = color_mode, }, .out = { .buffer = ppa->out_buffer, .buffer_size = ppa->out_buffer_size, .pic_w = (uint32_t)out_w, .pic_h = (uint32_t)out_h, .block_offset_x = 0, .block_offset_y = 0, .srm_cm = color_mode, }, // LVGL rotation is CCW, same convention as ppa_srm_rotation_angle_t - see lvgl_ppa.h. .rotation_angle = (ppa_srm_rotation_angle_t)rotation, .scale_x = 1.0f, .scale_y = 1.0f, .byte_swap = swap_bytes, .mode = PPA_TRANS_MODE_BLOCKING, }; if (ppa_do_scale_rotate_mirror(ppa->client, &oper_config) != ESP_OK) { LOG_E(TAG, "PPA rotate failed (%" PRId32 "x%" PRId32 " -> %" PRId32 "x%" PRId32 ")", w, h, out_w, out_h); return NULL; } if (!ppa->logged_first_rotate) { LOG_I(TAG, "PPA rotate in use (%" PRId32 "x%" PRId32 " -> %" PRId32 "x%" PRId32 ", angle=%d)", w, h, out_w, out_h, (int)rotation); ppa->logged_first_rotate = true; } return ppa->out_buffer; } #else // !(ESP_PLATFORM && SOC_PPA_SUPPORTED) bool lvgl_ppa_is_supported(void) { return false; } bool lvgl_ppa_supports_color_format(lv_color_format_t color_format) { (void)color_format; return false; } void* lvgl_ppa_get_or_create(size_t out_buffer_size_bytes) { (void)out_buffer_size_bytes; return NULL; } void lvgl_ppa_delete(void* ppa_handle) { (void)ppa_handle; } void* lvgl_ppa_rotate( void* ppa_handle, const uint8_t* in_buff, int32_t w, int32_t h, lv_display_rotation_t rotation, lv_color_format_t color_format, bool swap_bytes ) { (void)ppa_handle; (void)in_buff; (void)w; (void)h; (void)rotation; (void)color_format; (void)swap_bytes; return NULL; } #endif