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