1 /*
2 * Copyright (c) 2018-2019 Jan Van Winkel <jan.van_winkel@dxplore.eu>
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #include <zephyr/init.h>
8 #include <zephyr/kernel.h>
9 #include <lvgl.h>
10 #include "lvgl_display.h"
11 #ifdef CONFIG_LV_Z_USE_FILESYSTEM
12 #include "lvgl_fs.h"
13 #endif
14 #ifdef CONFIG_LV_Z_POINTER_KSCAN
15 #include <zephyr/drivers/kscan.h>
16 #endif
17 #include LV_MEM_CUSTOM_INCLUDE
18
19 #define LOG_LEVEL CONFIG_LV_LOG_LEVEL
20 #include <zephyr/logging/log.h>
21 LOG_MODULE_REGISTER(lvgl);
22
23 static lv_disp_drv_t disp_drv;
24 struct lvgl_disp_data disp_data = {
25 .blanking_on = false,
26 };
27 #ifdef CONFIG_LV_Z_POINTER_KSCAN
28 static lv_indev_drv_t indev_drv;
29 #endif /* CONFIG_LV_Z_POINTER_KSCAN */
30
31 #define DISPLAY_NODE DT_CHOSEN(zephyr_display)
32 #define KSCAN_NODE DT_CHOSEN(zephyr_keyboard_scan)
33
34 #ifdef CONFIG_LV_Z_BUFFER_ALLOC_STATIC
35
36 static lv_disp_draw_buf_t disp_buf;
37
38 #define DISPLAY_WIDTH DT_PROP(DISPLAY_NODE, width)
39 #define DISPLAY_HEIGHT DT_PROP(DISPLAY_NODE, height)
40
41 #define BUFFER_SIZE (CONFIG_LV_Z_BITS_PER_PIXEL * ((CONFIG_LV_Z_VDB_SIZE * \
42 DISPLAY_WIDTH * DISPLAY_HEIGHT) / 100) / 8)
43
44 #define NBR_PIXELS_IN_BUFFER (BUFFER_SIZE * 8 / CONFIG_LV_Z_BITS_PER_PIXEL)
45
46 /* NOTE: depending on chosen color depth buffer may be accessed using uint8_t *,
47 * uint16_t * or uint32_t *, therefore buffer needs to be aligned accordingly to
48 * prevent unaligned memory accesses.
49 */
50 static uint8_t buf0[BUFFER_SIZE]
51 #ifdef CONFIG_LV_Z_VBD_CUSTOM_SECTION
52 Z_GENERIC_SECTION(.lvgl_buf)
53 #endif
54 __aligned(CONFIG_LV_Z_VDB_ALIGN);
55
56 #ifdef CONFIG_LV_Z_DOUBLE_VDB
57 static uint8_t buf1[BUFFER_SIZE]
58 #ifdef CONFIG_LV_Z_VBD_CUSTOM_SECTION
59 Z_GENERIC_SECTION(.lvgl_buf)
60 #endif
61 __aligned(CONFIG_LV_Z_VDB_ALIGN);
62 #endif /* CONFIG_LV_Z_DOUBLE_VDB */
63
64 #endif /* CONFIG_LV_Z_BUFFER_ALLOC_STATIC */
65
66 #if CONFIG_LV_LOG_LEVEL != 0
67 /*
68 * In LVGLv8 the signature of the logging callback has changes and it no longer
69 * takes the log level as an integer argument. Instead, the log level is now
70 * already part of the buffer passed to the logging callback. It's not optimal
71 * but we need to live with it and parse the buffer manually to determine the
72 * level and then truncate the string we actually pass to the logging framework.
73 */
lvgl_log(const char * buf)74 static void lvgl_log(const char *buf)
75 {
76 /*
77 * This is ugly and should be done in a loop or something but as it
78 * turned out, Z_LOG()'s first argument (that specifies the log level)
79 * cannot be an l-value...
80 *
81 * We also assume lvgl is sane and always supplies the level string.
82 */
83 switch (buf[1]) {
84 case 'E':
85 LOG_ERR("%s", buf + strlen("[Error] "));
86 break;
87 case 'W':
88 LOG_WRN("%s", buf + strlen("Warn] "));
89 break;
90 case 'I':
91 LOG_INF("%s", buf + strlen("[Info] "));
92 break;
93 case 'T':
94 LOG_DBG("%s", buf + strlen("[Trace] "));
95 break;
96 }
97 }
98 #endif
99
100 #ifdef CONFIG_LV_Z_BUFFER_ALLOC_STATIC
101
lvgl_allocate_rendering_buffers(lv_disp_drv_t * disp_drv)102 static int lvgl_allocate_rendering_buffers(lv_disp_drv_t *disp_drv)
103 {
104 struct lvgl_disp_data *data =
105 (struct lvgl_disp_data *)disp_drv->user_data;
106 int err = 0;
107
108 if (data->cap.x_resolution <= DISPLAY_WIDTH) {
109 disp_drv->hor_res = data->cap.x_resolution;
110 } else {
111 LOG_ERR("Horizontal resolution is larger than maximum");
112 err = -ENOTSUP;
113 }
114
115 if (data->cap.y_resolution <= DISPLAY_HEIGHT) {
116 disp_drv->ver_res = data->cap.y_resolution;
117 } else {
118 LOG_ERR("Vertical resolution is larger than maximum");
119 err = -ENOTSUP;
120 }
121
122 disp_drv->draw_buf = &disp_buf;
123 #ifdef CONFIG_LV_Z_DOUBLE_VDB
124 lv_disp_draw_buf_init(disp_drv->draw_buf, &buf0, &buf1, NBR_PIXELS_IN_BUFFER);
125 #else
126 lv_disp_draw_buf_init(disp_drv->draw_buf, &buf0, NULL, NBR_PIXELS_IN_BUFFER);
127 #endif /* CONFIG_LV_Z_DOUBLE_VDB */
128
129 return err;
130 }
131
132 #else
133
lvgl_allocate_rendering_buffers(lv_disp_drv_t * disp_drv)134 static int lvgl_allocate_rendering_buffers(lv_disp_drv_t *disp_drv)
135 {
136 void *buf0 = NULL;
137 void *buf1 = NULL;
138 uint16_t buf_nbr_pixels;
139 uint32_t buf_size;
140 struct lvgl_disp_data *data =
141 (struct lvgl_disp_data *)disp_drv->user_data;
142
143 disp_drv->hor_res = data->cap.x_resolution;
144 disp_drv->ver_res = data->cap.y_resolution;
145
146 buf_nbr_pixels = (CONFIG_LV_Z_VDB_SIZE * disp_drv->hor_res *
147 disp_drv->ver_res) / 100;
148 /* one horizontal line is the minimum buffer requirement for lvgl */
149 if (buf_nbr_pixels < disp_drv->hor_res) {
150 buf_nbr_pixels = disp_drv->hor_res;
151 }
152
153 switch (data->cap.current_pixel_format) {
154 case PIXEL_FORMAT_ARGB_8888:
155 buf_size = 4 * buf_nbr_pixels;
156 break;
157 case PIXEL_FORMAT_RGB_888:
158 buf_size = 3 * buf_nbr_pixels;
159 break;
160 case PIXEL_FORMAT_RGB_565:
161 buf_size = 2 * buf_nbr_pixels;
162 break;
163 case PIXEL_FORMAT_MONO01:
164 case PIXEL_FORMAT_MONO10:
165 buf_size = buf_nbr_pixels / 8;
166 buf_size += (buf_nbr_pixels % 8) == 0 ? 0 : 1;
167 break;
168 default:
169 return -ENOTSUP;
170 }
171
172 buf0 = LV_MEM_CUSTOM_ALLOC(buf_size);
173 if (buf0 == NULL) {
174 LOG_ERR("Failed to allocate memory for rendering buffer");
175 return -ENOMEM;
176 }
177
178 #ifdef CONFIG_LV_Z_DOUBLE_VDB
179 buf1 = LV_MEM_CUSTOM_ALLOC(buf_size);
180 if (buf1 == NULL) {
181 LV_MEM_CUSTOM_FREE(buf0);
182 LOG_ERR("Failed to allocate memory for rendering buffer");
183 return -ENOMEM;
184 }
185 #endif
186
187 disp_drv->draw_buf = LV_MEM_CUSTOM_ALLOC(sizeof(lv_disp_draw_buf_t));
188 if (disp_drv->draw_buf == NULL) {
189 LV_MEM_CUSTOM_FREE(buf0);
190 LV_MEM_CUSTOM_FREE(buf1);
191 LOG_ERR("Failed to allocate memory to store rendering buffers");
192 return -ENOMEM;
193 }
194
195 lv_disp_draw_buf_init(disp_drv->draw_buf, buf0, buf1, buf_nbr_pixels);
196 return 0;
197 }
198 #endif /* CONFIG_LV_Z_BUFFER_ALLOC_STATIC */
199
200 #ifdef CONFIG_LV_Z_POINTER_KSCAN
201 K_MSGQ_DEFINE(kscan_msgq, sizeof(lv_indev_data_t),
202 CONFIG_LV_Z_POINTER_KSCAN_MSGQ_COUNT, 4);
203
lvgl_pointer_kscan_callback(const struct device * dev,uint32_t row,uint32_t col,bool pressed)204 static void lvgl_pointer_kscan_callback(const struct device *dev,
205 uint32_t row,
206 uint32_t col, bool pressed)
207 {
208 lv_indev_data_t data = {
209 .point.x = col,
210 .point.y = row,
211 .state = pressed ? LV_INDEV_STATE_PR : LV_INDEV_STATE_REL,
212 };
213
214 if (k_msgq_put(&kscan_msgq, &data, K_NO_WAIT) != 0) {
215 LOG_DBG("Could not put input data into queue");
216 }
217 }
218
lvgl_pointer_kscan_read(lv_indev_drv_t * drv,lv_indev_data_t * data)219 static void lvgl_pointer_kscan_read(lv_indev_drv_t *drv, lv_indev_data_t *data)
220 {
221 lv_disp_t *disp;
222 struct lvgl_disp_data *disp_data;
223 struct display_capabilities *cap;
224 lv_indev_data_t curr;
225
226 static lv_indev_data_t prev = {
227 .point.x = 0,
228 .point.y = 0,
229 .state = LV_INDEV_STATE_REL,
230 };
231
232 if (k_msgq_get(&kscan_msgq, &curr, K_NO_WAIT) != 0) {
233 goto set_and_release;
234 }
235
236 prev = curr;
237
238 disp = lv_disp_get_default();
239 disp_data = disp->driver->user_data;
240 cap = &disp_data->cap;
241
242 /* adjust kscan coordinates */
243 if (IS_ENABLED(CONFIG_LV_Z_POINTER_KSCAN_SWAP_XY)) {
244 lv_coord_t x;
245
246 x = prev.point.x;
247 prev.point.x = prev.point.y;
248 prev.point.y = x;
249 }
250
251 if (IS_ENABLED(CONFIG_LV_Z_POINTER_KSCAN_INVERT_X)) {
252 if (cap->current_orientation == DISPLAY_ORIENTATION_NORMAL ||
253 cap->current_orientation == DISPLAY_ORIENTATION_ROTATED_180) {
254 prev.point.x = cap->x_resolution - prev.point.x;
255 } else {
256 prev.point.x = cap->y_resolution - prev.point.x;
257 }
258 }
259
260 if (IS_ENABLED(CONFIG_LV_Z_POINTER_KSCAN_INVERT_Y)) {
261 if (cap->current_orientation == DISPLAY_ORIENTATION_NORMAL ||
262 cap->current_orientation == DISPLAY_ORIENTATION_ROTATED_180) {
263 prev.point.y = cap->y_resolution - prev.point.y;
264 } else {
265 prev.point.y = cap->x_resolution - prev.point.y;
266 }
267 }
268
269 /* rotate touch point to match display rotation */
270 if (cap->current_orientation == DISPLAY_ORIENTATION_ROTATED_90) {
271 lv_coord_t x;
272
273 x = prev.point.x;
274 prev.point.x = prev.point.y;
275 prev.point.y = cap->y_resolution - x;
276 } else if (cap->current_orientation == DISPLAY_ORIENTATION_ROTATED_180) {
277 prev.point.x = cap->x_resolution - prev.point.x;
278 prev.point.y = cap->y_resolution - prev.point.y;
279 } else if (cap->current_orientation == DISPLAY_ORIENTATION_ROTATED_270) {
280 lv_coord_t x;
281
282 x = prev.point.x;
283 prev.point.x = cap->x_resolution - prev.point.y;
284 prev.point.y = x;
285 }
286
287 /* filter readings within display */
288 if (prev.point.x <= 0) {
289 prev.point.x = 0;
290 }
291 else if (prev.point.x >= cap->x_resolution) {
292 prev.point.x = cap->x_resolution - 1;
293 }
294
295 if (prev.point.y <= 0) {
296 prev.point.y = 0;
297 }
298 else if (prev.point.y >= cap->y_resolution) {
299 prev.point.y = cap->y_resolution - 1;
300 }
301
302 set_and_release:
303 *data = prev;
304
305 data->continue_reading = k_msgq_num_used_get(&kscan_msgq) > 0;
306 }
307
lvgl_pointer_kscan_init(void)308 static int lvgl_pointer_kscan_init(void)
309 {
310 const struct device *kscan_dev = DEVICE_DT_GET(KSCAN_NODE);
311
312 if (!device_is_ready(kscan_dev)) {
313 LOG_ERR("Keyboard scan device not ready.");
314 return -ENODEV;
315 }
316
317 if (kscan_config(kscan_dev, lvgl_pointer_kscan_callback) < 0) {
318 LOG_ERR("Could not configure keyboard scan device.");
319 return -ENODEV;
320 }
321
322 lv_indev_drv_init(&indev_drv);
323 indev_drv.type = LV_INDEV_TYPE_POINTER;
324 indev_drv.read_cb = lvgl_pointer_kscan_read;
325
326 if (lv_indev_drv_register(&indev_drv) == NULL) {
327 LOG_ERR("Failed to register input device.");
328 return -EPERM;
329 }
330
331 kscan_enable_callback(kscan_dev);
332
333 return 0;
334 }
335 #endif /* CONFIG_LV_Z_POINTER_KSCAN */
336
lvgl_init(void)337 static int lvgl_init(void)
338 {
339
340 const struct device *display_dev = DEVICE_DT_GET(DISPLAY_NODE);
341
342 int err = 0;
343
344 if (!device_is_ready(display_dev)) {
345 LOG_ERR("Display device not ready.");
346 return -ENODEV;
347 }
348
349 #if CONFIG_LV_LOG_LEVEL != 0
350 lv_log_register_print_cb(lvgl_log);
351 #endif
352
353 lv_init();
354
355 #ifdef CONFIG_LV_Z_USE_FILESYSTEM
356 lvgl_fs_init();
357 #endif
358
359 disp_data.display_dev = display_dev;
360 display_get_capabilities(display_dev, &disp_data.cap);
361
362 lv_disp_drv_init(&disp_drv);
363 disp_drv.user_data = (void *)&disp_data;
364
365 #ifdef CONFIG_LV_Z_FULL_REFRESH
366 disp_drv.full_refresh = 1;
367 #endif
368
369 err = lvgl_allocate_rendering_buffers(&disp_drv);
370 if (err != 0) {
371 return err;
372 }
373
374 if (set_lvgl_rendering_cb(&disp_drv) != 0) {
375 LOG_ERR("Display not supported.");
376 return -ENOTSUP;
377 }
378
379 if (lv_disp_drv_register(&disp_drv) == NULL) {
380 LOG_ERR("Failed to register display device.");
381 return -EPERM;
382 }
383
384 #ifdef CONFIG_LV_Z_POINTER_KSCAN
385 lvgl_pointer_kscan_init();
386 #endif /* CONFIG_LV_Z_POINTER_KSCAN */
387
388 return 0;
389 }
390
391 SYS_INIT(lvgl_init, APPLICATION, CONFIG_APPLICATION_INIT_PRIORITY);
392