1 /*
2  * Copyright (c) 2019 Jan Van Winkel <jan.van_winkel@dxplore.eu>
3  *
4  * Based on ST7789V sample:
5  * Copyright (c) 2019 Marc Reilly
6  *
7  * SPDX-License-Identifier: Apache-2.0
8  */
9 
10 #include <zephyr/logging/log.h>
11 LOG_MODULE_REGISTER(sample, LOG_LEVEL_INF);
12 
13 #include <zephyr/kernel.h>
14 #include <zephyr/device.h>
15 #include <zephyr/drivers/display.h>
16 
17 #ifdef CONFIG_ARCH_POSIX
18 #include "posix_board_if.h"
19 #endif
20 
21 enum corner {
22 	TOP_LEFT,
23 	TOP_RIGHT,
24 	BOTTOM_RIGHT,
25 	BOTTOM_LEFT
26 };
27 
28 typedef void (*fill_buffer)(enum corner corner, uint8_t grey, uint8_t *buf,
29 			    size_t buf_size);
30 
31 
32 #ifdef CONFIG_ARCH_POSIX
posix_exit_main(int exit_code)33 static void posix_exit_main(int exit_code)
34 {
35 #if CONFIG_TEST
36 	if (exit_code == 0) {
37 		LOG_INF("PROJECT EXECUTION SUCCESSFUL");
38 	} else {
39 		LOG_INF("PROJECT EXECUTION FAILED");
40 	}
41 #endif
42 	posix_exit(exit_code);
43 }
44 #endif
45 
fill_buffer_argb8888(enum corner corner,uint8_t grey,uint8_t * buf,size_t buf_size)46 static void fill_buffer_argb8888(enum corner corner, uint8_t grey, uint8_t *buf,
47 				 size_t buf_size)
48 {
49 	uint32_t color = 0;
50 
51 	switch (corner) {
52 	case TOP_LEFT:
53 		color = 0xFFFF0000u;
54 		break;
55 	case TOP_RIGHT:
56 		color = 0xFF00FF00u;
57 		break;
58 	case BOTTOM_RIGHT:
59 		color = 0xFF0000FFu;
60 		break;
61 	case BOTTOM_LEFT:
62 		color = 0xFF000000u | grey << 16 | grey << 8 | grey;
63 		break;
64 	}
65 
66 	for (size_t idx = 0; idx < buf_size; idx += 4) {
67 		*((uint32_t *)(buf + idx)) = color;
68 	}
69 }
70 
fill_buffer_rgb888(enum corner corner,uint8_t grey,uint8_t * buf,size_t buf_size)71 static void fill_buffer_rgb888(enum corner corner, uint8_t grey, uint8_t *buf,
72 			       size_t buf_size)
73 {
74 	uint32_t color = 0;
75 
76 	switch (corner) {
77 	case TOP_LEFT:
78 		color = 0x00FF0000u;
79 		break;
80 	case TOP_RIGHT:
81 		color = 0x0000FF00u;
82 		break;
83 	case BOTTOM_RIGHT:
84 		color = 0x000000FFu;
85 		break;
86 	case BOTTOM_LEFT:
87 		color = grey << 16 | grey << 8 | grey;
88 		break;
89 	}
90 
91 	for (size_t idx = 0; idx < buf_size; idx += 3) {
92 		*(buf + idx + 0) = color >> 16;
93 		*(buf + idx + 1) = color >> 8;
94 		*(buf + idx + 2) = color >> 0;
95 	}
96 }
97 
get_rgb565_color(enum corner corner,uint8_t grey)98 static uint16_t get_rgb565_color(enum corner corner, uint8_t grey)
99 {
100 	uint16_t color = 0;
101 	uint16_t grey_5bit;
102 
103 	switch (corner) {
104 	case TOP_LEFT:
105 		color = 0xF800u;
106 		break;
107 	case TOP_RIGHT:
108 		color = 0x07E0u;
109 		break;
110 	case BOTTOM_RIGHT:
111 		color = 0x001Fu;
112 		break;
113 	case BOTTOM_LEFT:
114 		grey_5bit = grey & 0x1Fu;
115 		/* shift the green an extra bit, it has 6 bits */
116 		color = grey_5bit << 11 | grey_5bit << (5 + 1) | grey_5bit;
117 		break;
118 	}
119 	return color;
120 }
121 
fill_buffer_bgr565(enum corner corner,uint8_t grey,uint8_t * buf,size_t buf_size)122 static void fill_buffer_bgr565(enum corner corner, uint8_t grey, uint8_t *buf,
123 			       size_t buf_size)
124 {
125 	uint16_t color = get_rgb565_color(corner, grey);
126 
127 	for (size_t idx = 0; idx < buf_size; idx += 2) {
128 		*(buf + idx + 0) = (color >> 8) & 0xFFu;
129 		*(buf + idx + 1) = (color >> 0) & 0xFFu;
130 	}
131 }
132 
fill_buffer_rgb565(enum corner corner,uint8_t grey,uint8_t * buf,size_t buf_size)133 static void fill_buffer_rgb565(enum corner corner, uint8_t grey, uint8_t *buf,
134 			       size_t buf_size)
135 {
136 	uint16_t color = get_rgb565_color(corner, grey);
137 
138 	for (size_t idx = 0; idx < buf_size; idx += 2) {
139 		*(uint16_t *)(buf + idx) = color;
140 	}
141 }
142 
fill_buffer_mono(enum corner corner,uint8_t grey,uint8_t black,uint8_t white,uint8_t * buf,size_t buf_size)143 static void fill_buffer_mono(enum corner corner, uint8_t grey,
144 			     uint8_t black, uint8_t white,
145 			     uint8_t *buf, size_t buf_size)
146 {
147 	uint16_t color;
148 
149 	switch (corner) {
150 	case BOTTOM_LEFT:
151 		color = (grey & 0x01u) ? white : black;
152 		break;
153 	default:
154 		color = black;
155 		break;
156 	}
157 
158 	memset(buf, color, buf_size);
159 }
160 
fill_buffer_l_8(enum corner corner,uint8_t grey,uint8_t * buf,size_t buf_size)161 static inline void fill_buffer_l_8(enum corner corner, uint8_t grey, uint8_t *buf, size_t buf_size)
162 {
163 	for (size_t idx = 0; idx < buf_size; idx += 1) {
164 		*(uint8_t *)(buf + idx) = grey;
165 	}
166 }
167 
fill_buffer_al_88(enum corner corner,uint8_t grey,uint8_t * buf,size_t buf_size)168 static void fill_buffer_al_88(enum corner corner, uint8_t grey, uint8_t *buf,
169 				 size_t buf_size)
170 {
171 	uint16_t color;
172 
173 	switch (corner) {
174 	case TOP_LEFT:
175 		color = 0xFF00u;
176 		break;
177 	case TOP_RIGHT:
178 		color = 0xFFFFu;
179 		break;
180 	case BOTTOM_RIGHT:
181 		color = 0xFF88u;
182 		break;
183 	case BOTTOM_LEFT:
184 		color = 0xFF00u | grey;
185 		break;
186 	default:
187 		color = 0;
188 		break;
189 	}
190 
191 	for (size_t idx = 0; idx < buf_size; idx += 2) {
192 		*((uint16_t *)(buf + idx)) = color;
193 	}
194 }
195 
fill_buffer_mono01(enum corner corner,uint8_t grey,uint8_t * buf,size_t buf_size)196 static inline void fill_buffer_mono01(enum corner corner, uint8_t grey,
197 				      uint8_t *buf, size_t buf_size)
198 {
199 	fill_buffer_mono(corner, grey, 0x00u, 0xFFu, buf, buf_size);
200 }
201 
fill_buffer_mono10(enum corner corner,uint8_t grey,uint8_t * buf,size_t buf_size)202 static inline void fill_buffer_mono10(enum corner corner, uint8_t grey,
203 				      uint8_t *buf, size_t buf_size)
204 {
205 	fill_buffer_mono(corner, grey, 0xFFu, 0x00u, buf, buf_size);
206 }
207 
main(void)208 int main(void)
209 {
210 	size_t x;
211 	size_t y;
212 	size_t rect_w;
213 	size_t rect_h;
214 	size_t h_step;
215 	size_t scale;
216 	size_t grey_count;
217 	uint8_t bg_color;
218 	uint8_t *buf;
219 	int32_t grey_scale_sleep;
220 	const struct device *display_dev;
221 	struct display_capabilities capabilities;
222 	struct display_buffer_descriptor buf_desc;
223 	size_t buf_size = 0;
224 	fill_buffer fill_buffer_fnc = NULL;
225 	int ret;
226 
227 	display_dev = DEVICE_DT_GET(DT_CHOSEN(zephyr_display));
228 	if (!device_is_ready(display_dev)) {
229 		LOG_ERR("Device %s not found. Aborting sample.",
230 			display_dev->name);
231 #ifdef CONFIG_ARCH_POSIX
232 		posix_exit_main(1);
233 #else
234 		return 0;
235 #endif
236 	}
237 
238 	LOG_INF("Display sample for %s", display_dev->name);
239 	display_get_capabilities(display_dev, &capabilities);
240 
241 	if (capabilities.screen_info & SCREEN_INFO_MONO_VTILED) {
242 		rect_w = 16;
243 		rect_h = 8;
244 	} else {
245 		rect_w = 2;
246 		rect_h = 1;
247 	}
248 
249 	if ((capabilities.x_resolution < 3 * rect_w) ||
250 	    (capabilities.y_resolution < 3 * rect_h) ||
251 	    (capabilities.x_resolution < 8 * rect_h)) {
252 		rect_w = capabilities.x_resolution * 40 / 100;
253 		rect_h = capabilities.y_resolution * 40 / 100;
254 		h_step = capabilities.y_resolution * 20 / 100;
255 		scale = 1;
256 	} else {
257 		h_step = rect_h;
258 		scale = (capabilities.x_resolution / 8) / rect_h;
259 	}
260 
261 	rect_w *= scale;
262 	rect_h *= scale;
263 
264 	if (capabilities.screen_info & SCREEN_INFO_EPD) {
265 		grey_scale_sleep = 10000;
266 	} else {
267 		grey_scale_sleep = 100;
268 	}
269 
270 	if (capabilities.screen_info & SCREEN_INFO_X_ALIGNMENT_WIDTH) {
271 		rect_w = capabilities.x_resolution;
272 	}
273 
274 	rect_w = ROUND_UP(rect_w, CONFIG_SAMPLE_PITCH_ALIGN);
275 
276 	buf_size = rect_w * rect_h;
277 
278 	if (buf_size < (capabilities.x_resolution * h_step)) {
279 		buf_size = capabilities.x_resolution * h_step;
280 	}
281 
282 	switch (capabilities.current_pixel_format) {
283 	case PIXEL_FORMAT_ARGB_8888:
284 		bg_color = 0x00u;
285 		fill_buffer_fnc = fill_buffer_argb8888;
286 		buf_size *= 4;
287 		break;
288 	case PIXEL_FORMAT_RGB_888:
289 		bg_color = 0xFFu;
290 		fill_buffer_fnc = fill_buffer_rgb888;
291 		buf_size *= 3;
292 		break;
293 	case PIXEL_FORMAT_RGB_565:
294 		bg_color = 0xFFu;
295 		fill_buffer_fnc = fill_buffer_rgb565;
296 		buf_size *= 2;
297 		break;
298 	case PIXEL_FORMAT_BGR_565:
299 		bg_color = 0xFFu;
300 		fill_buffer_fnc = fill_buffer_bgr565;
301 		buf_size *= 2;
302 		break;
303 	case PIXEL_FORMAT_L_8:
304 		bg_color = 0xFFu;
305 		fill_buffer_fnc = fill_buffer_l_8;
306 		break;
307 	case PIXEL_FORMAT_AL_88:
308 		bg_color = 0x00u;
309 		fill_buffer_fnc = fill_buffer_al_88;
310 		buf_size *= 2;
311 		break;
312 	case PIXEL_FORMAT_MONO01:
313 		bg_color = 0xFFu;
314 		fill_buffer_fnc = fill_buffer_mono01;
315 		buf_size = DIV_ROUND_UP(DIV_ROUND_UP(
316 			buf_size, NUM_BITS(uint8_t)), sizeof(uint8_t));
317 		break;
318 	case PIXEL_FORMAT_MONO10:
319 		bg_color = 0x00u;
320 		fill_buffer_fnc = fill_buffer_mono10;
321 		buf_size = DIV_ROUND_UP(DIV_ROUND_UP(
322 			buf_size, NUM_BITS(uint8_t)), sizeof(uint8_t));
323 		break;
324 	default:
325 		LOG_ERR("Unsupported pixel format. Aborting sample.");
326 #ifdef CONFIG_ARCH_POSIX
327 		posix_exit_main(1);
328 #else
329 		return 0;
330 #endif
331 	}
332 
333 	buf = k_aligned_alloc(CONFIG_SAMPLE_BUFFER_ADDR_ALIGN, buf_size);
334 
335 	if (buf == NULL) {
336 		LOG_ERR("Could not allocate memory. Aborting sample.");
337 #ifdef CONFIG_ARCH_POSIX
338 		posix_exit_main(1);
339 #else
340 		return 0;
341 #endif
342 	}
343 
344 	(void)memset(buf, bg_color, buf_size);
345 
346 	buf_desc.buf_size = buf_size;
347 	buf_desc.pitch = ROUND_UP(capabilities.x_resolution, CONFIG_SAMPLE_PITCH_ALIGN);
348 	buf_desc.width = capabilities.x_resolution;
349 	buf_desc.height = h_step;
350 
351 	/*
352 	 * The following writes will only render parts of the image,
353 	 * so turn this option on.
354 	 * This allows double-buffered displays to hold the pixels
355 	 * back until the image is complete.
356 	 */
357 	buf_desc.frame_incomplete = true;
358 
359 	for (int idx = 0; idx < capabilities.y_resolution; idx += h_step) {
360 		/*
361 		 * Tweaking the height value not to draw outside of the display.
362 		 * It is required when using a monochrome display whose vertical
363 		 * resolution can not be divided by 8.
364 		 */
365 		if ((capabilities.y_resolution - idx) < h_step) {
366 			buf_desc.height = (capabilities.y_resolution - idx);
367 		}
368 		ret = display_write(display_dev, 0, idx, &buf_desc, buf);
369 		if (ret < 0) {
370 			LOG_ERR("Failed to write to display (error %d)", ret);
371 #ifdef CONFIG_ARCH_POSIX
372 			posix_exit_main(1);
373 #else
374 			return 0;
375 #endif
376 		}
377 	}
378 
379 	buf_desc.pitch = ROUND_UP(rect_w, CONFIG_SAMPLE_PITCH_ALIGN);
380 	buf_desc.width = rect_w;
381 	buf_desc.height = rect_h;
382 
383 	fill_buffer_fnc(TOP_LEFT, 0, buf, buf_size);
384 	x = 0;
385 	y = 0;
386 	ret = display_write(display_dev, x, y, &buf_desc, buf);
387 	if (ret < 0) {
388 		LOG_ERR("Failed to write to display (error %d)", ret);
389 #ifdef CONFIG_ARCH_POSIX
390 		posix_exit_main(1);
391 #else
392 		return 0;
393 #endif
394 	}
395 
396 	fill_buffer_fnc(TOP_RIGHT, 0, buf, buf_size);
397 	x = capabilities.x_resolution - rect_w;
398 	y = 0;
399 	ret = display_write(display_dev, x, y, &buf_desc, buf);
400 	if (ret < 0) {
401 		LOG_ERR("Failed to write to display (error %d)", ret);
402 #ifdef CONFIG_ARCH_POSIX
403 		posix_exit_main(1);
404 #else
405 		return 0;
406 #endif
407 	}
408 
409 	/*
410 	 * This is the last write of the frame, so turn this off.
411 	 * Double-buffered displays will now present the new image
412 	 * to the user.
413 	 */
414 	buf_desc.frame_incomplete = false;
415 
416 	fill_buffer_fnc(BOTTOM_RIGHT, 0, buf, buf_size);
417 	x = capabilities.x_resolution - rect_w;
418 	y = capabilities.y_resolution - rect_h;
419 	ret = display_write(display_dev, x, y, &buf_desc, buf);
420 	if (ret < 0) {
421 		LOG_ERR("Failed to write to display (error %d)", ret);
422 #ifdef CONFIG_ARCH_POSIX
423 		posix_exit_main(1);
424 #else
425 		return 0;
426 #endif
427 	}
428 
429 	ret = display_blanking_off(display_dev);
430 	if (ret < 0 && ret != -ENOSYS) {
431 		LOG_ERR("Failed to turn blanking off (error %d)", ret);
432 #ifdef CONFIG_ARCH_POSIX
433 		posix_exit_main(1);
434 #else
435 		return 0;
436 #endif
437 	}
438 
439 	grey_count = 0;
440 	x = 0;
441 	y = capabilities.y_resolution - rect_h;
442 
443 	LOG_INF("Display starts");
444 	while (1) {
445 		fill_buffer_fnc(BOTTOM_LEFT, grey_count, buf, buf_size);
446 		ret = display_write(display_dev, x, y, &buf_desc, buf);
447 		if (ret < 0) {
448 			LOG_ERR("Failed to write to display (error %d)", ret);
449 #ifdef CONFIG_ARCH_POSIX
450 			posix_exit_main(1);
451 #else
452 			return 0;
453 #endif
454 		}
455 
456 		++grey_count;
457 		k_msleep(grey_scale_sleep);
458 #if CONFIG_TEST
459 		if (grey_count >= 30) {
460 			LOG_INF("Display sample test mode done %s", display_dev->name);
461 			break;
462 		}
463 #endif
464 	}
465 
466 #ifdef CONFIG_ARCH_POSIX
467 	posix_exit_main(0);
468 #endif
469 	return 0;
470 }
471