From 9a4ae67f7bf4ae3a8424efa252747a84497f9099 Mon Sep 17 00:00:00 2001 From: Fabian Blatz Date: Fri, 5 Apr 2024 16:40:28 +0200 Subject: [PATCH] modules: lvgl: Add zephyr OSAL implementation This patch adds the OSAL implementation of dynamic thread creation, mutex and thread syncronization primitive (semaphore) enabling the use of the parallel rendering architecture provided with LVGL 9.0. To use it, set `CONFIG_LV_Z_USE_OSAL` and your preferred dynamic thread stack allocation method (pool or heap). Signed-off-by: Fabian Blatz --- modules/lvgl/CMakeLists.txt | 2 + modules/lvgl/Kconfig | 7 ++ modules/lvgl/include/lv_conf.h | 5 + modules/lvgl/include/lvgl_zephyr_osal.h | 30 +++++ modules/lvgl/lvgl_zephyr_osal.c | 151 ++++++++++++++++++++++++ 5 files changed, 195 insertions(+) create mode 100644 modules/lvgl/include/lvgl_zephyr_osal.h create mode 100644 modules/lvgl/lvgl_zephyr_osal.c diff --git a/modules/lvgl/CMakeLists.txt b/modules/lvgl/CMakeLists.txt index 2f9ce0925ce..5ef568e6b89 100644 --- a/modules/lvgl/CMakeLists.txt +++ b/modules/lvgl/CMakeLists.txt @@ -329,6 +329,8 @@ zephyr_library_sources_ifdef(CONFIG_LV_Z_BUTTON_INPUT input/lvgl_button_input.c) zephyr_library_sources_ifdef(CONFIG_LV_Z_ENCODER_INPUT input/lvgl_encoder_input.c) zephyr_library_sources_ifdef(CONFIG_LV_Z_KEYPAD_INPUT input/lvgl_keypad_input.c) +zephyr_library_sources_ifdef(CONFIG_LV_Z_USE_OSAL lvgl_zephyr_osal.c) + zephyr_library_link_libraries(LVGL) target_link_libraries(LVGL INTERFACE zephyr_interface) diff --git a/modules/lvgl/Kconfig b/modules/lvgl/Kconfig index 9f66720eb91..2c4506095fc 100644 --- a/modules/lvgl/Kconfig +++ b/modules/lvgl/Kconfig @@ -155,6 +155,13 @@ config LV_DRAW_DMA2D_HAL_INCLUDE Must be defined to include path of CMSIS header of target processor e.g. "stm32f769xx.h" or "stm32f429xx.h" +config LV_Z_USE_OSAL + bool "Use OSAL enabling parallel rendering" + depends on DYNAMIC_THREAD + help + Use the Zephyr LVGL OSAL to enable parallel rendering + pipelines. + rsource "Kconfig.memory" rsource "Kconfig.input" rsource "Kconfig.shell" diff --git a/modules/lvgl/include/lv_conf.h b/modules/lvgl/include/lv_conf.h index 2206c8f40a5..8f73f5e87b6 100644 --- a/modules/lvgl/include/lv_conf.h +++ b/modules/lvgl/include/lv_conf.h @@ -39,6 +39,11 @@ #define LV_COLOR_16_SWAP 1 #endif /* CONFIG_LV_COLOR_16_SWAP */ +#ifdef CONFIG_LV_Z_USE_OSAL +#define LV_USE_OS LV_OS_CUSTOM +#define LV_OS_CUSTOM_INCLUDE "lvgl_zephyr_osal.h" +#endif /* CONFIG_LV_Z_USE_OSAL */ + /* * Needed because of a workaround for a GCC bug, * see https://github.com/lvgl/lvgl/issues/3078 diff --git a/modules/lvgl/include/lvgl_zephyr_osal.h b/modules/lvgl/include/lvgl_zephyr_osal.h new file mode 100644 index 00000000000..07877741bd8 --- /dev/null +++ b/modules/lvgl/include/lvgl_zephyr_osal.h @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2024 Fabian Blatz + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef ZEPHYR_MODULES_LVGL_ZEPHYR_OSAL_H_ +#define ZEPHYR_MODULES_LVGL_ZEPHYR_OSAL_H_ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct { + k_tid_t tid; + k_thread_stack_t *stack; + struct k_thread thread; +} lv_thread_t; + +typedef struct k_mutex lv_mutex_t; + +typedef struct k_sem lv_thread_sync_t; + +#ifdef __cplusplus +} +#endif + +#endif /* ZEPHYR_MODULES_LVGL_ZEPHYR_OSAL_H_ */ diff --git a/modules/lvgl/lvgl_zephyr_osal.c b/modules/lvgl/lvgl_zephyr_osal.c new file mode 100644 index 00000000000..84c118f0020 --- /dev/null +++ b/modules/lvgl/lvgl_zephyr_osal.c @@ -0,0 +1,151 @@ +/* + * Copyright (c) 2024 Fabian Blatz + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "lvgl_zephyr_osal.h" +#include + +#include +LOG_MODULE_DECLARE(lvgl, CONFIG_LV_Z_LOG_LEVEL); + +typedef void (*lv_thread_entry)(void *); +static void thread_entry(void *thread, void *cb, void *user_data); + +lv_result_t lv_thread_init(lv_thread_t *thread, lv_thread_prio_t prio, void (*callback)(void *), + size_t stack_size, void *user_data) +{ + int thread_priority; + + thread->stack = k_thread_stack_alloc(stack_size, 0); + if (thread->stack == NULL) { + return LV_RESULT_INVALID; + } + + thread_priority = (CONFIG_NUM_PREEMPT_PRIORITIES - 1) - + ((prio * (CONFIG_NUM_PREEMPT_PRIORITIES - 1)) / LV_THREAD_PRIO_HIGHEST); + + thread->tid = k_thread_create(&thread->thread, thread->stack, stack_size, thread_entry, + thread, callback, user_data, thread_priority, 0, K_NO_WAIT); + + return LV_RESULT_OK; +} + +lv_result_t lv_thread_delete(lv_thread_t *thread) +{ + int ret; + + k_thread_abort(thread->tid); + ret = k_thread_stack_free(thread->stack); + if (ret < 0) { + LOG_ERR("Failled to delete thread: %d", ret); + return LV_RESULT_INVALID; + } + + return LV_RESULT_OK; +} + +lv_result_t lv_mutex_init(lv_mutex_t *mutex) +{ + k_mutex_init(mutex); + return LV_RESULT_OK; +} + +lv_result_t lv_mutex_lock(lv_mutex_t *mutex) +{ + int ret; + + ret = k_mutex_lock(mutex, K_FOREVER); + if (ret != 0) { + LOG_ERR("Failed to lock mutex: %d", ret); + return LV_RESULT_INVALID; + } + + return LV_RESULT_OK; +} + +lv_result_t lv_mutex_lock_isr(lv_mutex_t *mutex) +{ + int ret; + + ret = k_mutex_lock(mutex, K_NO_WAIT); + if (ret != 0) { + LOG_ERR("Failed to lock mutex: %d", ret); + return LV_RESULT_INVALID; + } + + return LV_RESULT_OK; +} + +lv_result_t lv_mutex_unlock(lv_mutex_t *mutex) +{ + int ret; + + ret = k_mutex_unlock(mutex); + if (ret != 0) { + LOG_ERR("Failed to unlock mutex: %d", ret); + return LV_RESULT_INVALID; + } + + return LV_RESULT_OK; +} + +lv_result_t lv_mutex_delete(lv_mutex_t *mutex) +{ + ARG_UNUSED(mutex); + return LV_RESULT_OK; +} + +lv_result_t lv_thread_sync_init(lv_thread_sync_t *sync) +{ + int ret; + + ret = k_sem_init(sync, 0, 1); + if (ret != 0) { + LOG_ERR("Failed to init thread sync: %d", ret); + return LV_RESULT_INVALID; + } + + return LV_RESULT_OK; +} + +lv_result_t lv_thread_sync_wait(lv_thread_sync_t *sync) +{ + int ret; + + ret = k_sem_take(sync, K_FOREVER); + if (ret < 0) { + LOG_ERR("Error waiting on thread sync: %d", ret); + return LV_RESULT_INVALID; + } + + return LV_RESULT_OK; +} + +lv_result_t lv_thread_sync_signal(lv_thread_sync_t *sync) +{ + k_sem_give(sync); + return LV_RESULT_OK; +} + +lv_result_t lv_thread_sync_signal_isr(lv_thread_sync_t *sync) +{ + k_sem_give(sync); + return LV_RESULT_OK; +} + +lv_result_t lv_thread_sync_delete(lv_thread_sync_t *sync) +{ + ARG_UNUSED(sync); + return LV_RESULT_OK; +} + +void thread_entry(void *thread, void *cb, void *user_data) +{ + __ASSERT_NO_MSG(cb != NULL); + lv_thread_entry entry_cb = (lv_thread_entry)cb; + + entry_cb(user_data); + lv_thread_delete((lv_thread_t *)thread); +}