1 /*
2 * Copyright (c) 2019 Jan Van Winkel <jan.van_winkel@dxplore.eu>
3 * Copyright 2023 NXP
4 *
5 * SPDX-License-Identifier: Apache-2.0
6 */
7
8 #include <zephyr/kernel.h>
9 #include <errno.h>
10
11 #include "lvgl_display.h"
12
13 #ifdef CONFIG_LV_Z_FLUSH_THREAD
14
15 K_SEM_DEFINE(flush_complete, 0, 1);
16 /* Needed because the wait callback might be called even if not flush is pending */
17 K_SEM_DEFINE(flush_required, 0, 1);
18 /* Message queue will only ever need to queue one message */
19 K_MSGQ_DEFINE(flush_queue, sizeof(struct lvgl_display_flush), 1, 1);
20
lvgl_flush_thread_entry(void * arg1,void * arg2,void * arg3)21 void lvgl_flush_thread_entry(void *arg1, void *arg2, void *arg3)
22 {
23 struct lvgl_display_flush flush;
24 struct lvgl_disp_data *data;
25
26 while (1) {
27 k_msgq_get(&flush_queue, &flush, K_FOREVER);
28 data = (struct lvgl_disp_data *)lv_display_get_user_data(flush.display);
29
30 flush.desc.frame_incomplete = !lv_display_flush_is_last(flush.display);
31 display_write(data->display_dev, flush.x, flush.y, &flush.desc, flush.buf);
32
33 k_sem_give(&flush_complete);
34 }
35 }
36
37 K_THREAD_DEFINE(lvgl_flush_thread, CONFIG_LV_Z_FLUSH_THREAD_STACK_SIZE, lvgl_flush_thread_entry,
38 NULL, NULL, NULL, CONFIG_LV_Z_FLUSH_THREAD_PRIORITY, 0, 0);
39
lvgl_wait_cb(lv_display_t * display)40 void lvgl_wait_cb(lv_display_t *display)
41 {
42 if (k_sem_take(&flush_required, K_NO_WAIT) == 0) {
43 k_sem_take(&flush_complete, K_FOREVER);
44 }
45 }
46
47 #endif /* CONFIG_LV_Z_FLUSH_THREAD */
48
49 #ifdef CONFIG_LV_Z_USE_ROUNDER_CB
lvgl_rounder_cb(lv_event_t * e)50 void lvgl_rounder_cb(lv_event_t *e)
51 {
52 lv_area_t *area = lv_event_get_param(e);
53 #if CONFIG_LV_Z_AREA_X_ALIGNMENT_WIDTH != 1
54 __ASSERT(POPCOUNT(CONFIG_LV_Z_AREA_X_ALIGNMENT_WIDTH) == 1, "Invalid X alignment width");
55
56 area->x1 &= ~(CONFIG_LV_Z_AREA_X_ALIGNMENT_WIDTH - 1);
57 area->x2 |= (CONFIG_LV_Z_AREA_X_ALIGNMENT_WIDTH - 1);
58 #endif
59 #if CONFIG_LV_Z_AREA_Y_ALIGNMENT_WIDTH != 1
60 __ASSERT(POPCOUNT(CONFIG_LV_Z_AREA_Y_ALIGNMENT_WIDTH) == 1, "Invalid Y alignment width");
61
62 area->y1 &= ~(CONFIG_LV_Z_AREA_Y_ALIGNMENT_WIDTH - 1);
63 area->y2 |= (CONFIG_LV_Z_AREA_Y_ALIGNMENT_WIDTH - 1);
64 #endif
65 }
66 #else
67 #define lvgl_rounder_cb NULL
68 #endif
69
set_lvgl_rendering_cb(lv_display_t * display)70 int set_lvgl_rendering_cb(lv_display_t *display)
71 {
72 int err = 0;
73 struct lvgl_disp_data *data = (struct lvgl_disp_data *)lv_display_get_user_data(display);
74
75 #ifdef CONFIG_LV_Z_FLUSH_THREAD
76 lv_display_set_flush_wait_cb(display, lvgl_wait_cb);
77 #endif
78
79 switch (data->cap.current_pixel_format) {
80 case PIXEL_FORMAT_ARGB_8888:
81 lv_display_set_color_format(display, LV_COLOR_FORMAT_ARGB8888);
82 lv_display_set_flush_cb(display, lvgl_flush_cb_32bit);
83 lv_display_add_event_cb(display, lvgl_rounder_cb, LV_EVENT_INVALIDATE_AREA,
84 display);
85 break;
86 case PIXEL_FORMAT_RGB_888:
87 lv_display_set_color_format(display, LV_COLOR_FORMAT_RGB888);
88 lv_display_set_flush_cb(display, lvgl_flush_cb_24bit);
89 lv_display_add_event_cb(display, lvgl_rounder_cb, LV_EVENT_INVALIDATE_AREA,
90 display);
91 break;
92 case PIXEL_FORMAT_RGB_565:
93 case PIXEL_FORMAT_BGR_565:
94 lv_display_set_color_format(display, LV_COLOR_FORMAT_RGB565);
95 lv_display_set_flush_cb(display, lvgl_flush_cb_16bit);
96 lv_display_add_event_cb(display, lvgl_rounder_cb, LV_EVENT_INVALIDATE_AREA,
97 display);
98 break;
99 case PIXEL_FORMAT_L_8:
100 lv_display_set_color_format(display, LV_COLOR_FORMAT_L8);
101 lv_display_set_flush_cb(display, lvgl_flush_cb_8bit);
102 lv_display_add_event_cb(display, lvgl_rounder_cb, LV_EVENT_INVALIDATE_AREA,
103 display);
104 break;
105 case PIXEL_FORMAT_MONO01:
106 case PIXEL_FORMAT_MONO10:
107 lv_display_set_color_format(display, LV_COLOR_FORMAT_I1);
108 lv_display_set_flush_cb(display, lvgl_flush_cb_mono);
109 lv_display_add_event_cb(display, lvgl_rounder_cb_mono, LV_EVENT_INVALIDATE_AREA,
110 display);
111 break;
112 default:
113 lv_display_set_flush_cb(display, NULL);
114 lv_display_add_event_cb(display, lvgl_rounder_cb, LV_EVENT_INVALIDATE_AREA,
115 display);
116 err = -ENOTSUP;
117 break;
118 }
119
120 return err;
121 }
122
lvgl_flush_display(struct lvgl_display_flush * request)123 void lvgl_flush_display(struct lvgl_display_flush *request)
124 {
125 #ifdef CONFIG_LV_Z_FLUSH_THREAD
126 /*
127 * LVGL will only start a flush once the previous one is complete,
128 * so we can reset the flush state semaphore here.
129 */
130 k_sem_reset(&flush_complete);
131 k_msgq_put(&flush_queue, request, K_FOREVER);
132 k_sem_give(&flush_required);
133 /* Explicitly yield, in case the calling thread is a cooperative one */
134 k_yield();
135 #else
136 /* Write directly to the display */
137 struct lvgl_disp_data *data =
138 (struct lvgl_disp_data *)lv_display_get_user_data(request->display);
139
140 request->desc.frame_incomplete = !lv_display_flush_is_last(request->display);
141 display_write(data->display_dev, request->x, request->y, &request->desc, request->buf);
142 lv_display_flush_ready(request->display);
143 #endif
144 }
145