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_rgb565(enum corner corner,uint8_t grey,uint8_t * buf,size_t buf_size)122 static void fill_buffer_rgb565(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_bgr565(enum corner corner,uint8_t grey,uint8_t * buf,size_t buf_size)133 static void fill_buffer_bgr565(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_mono01(enum corner corner,uint8_t grey,uint8_t * buf,size_t buf_size)168 static inline void fill_buffer_mono01(enum corner corner, uint8_t grey,
169 uint8_t *buf, size_t buf_size)
170 {
171 fill_buffer_mono(corner, grey, 0x00u, 0xFFu, buf, buf_size);
172 }
173
fill_buffer_mono10(enum corner corner,uint8_t grey,uint8_t * buf,size_t buf_size)174 static inline void fill_buffer_mono10(enum corner corner, uint8_t grey,
175 uint8_t *buf, size_t buf_size)
176 {
177 fill_buffer_mono(corner, grey, 0xFFu, 0x00u, buf, buf_size);
178 }
179
main(void)180 int main(void)
181 {
182 size_t x;
183 size_t y;
184 size_t rect_w;
185 size_t rect_h;
186 size_t h_step;
187 size_t scale;
188 size_t grey_count;
189 uint8_t bg_color;
190 uint8_t *buf;
191 int32_t grey_scale_sleep;
192 const struct device *display_dev;
193 struct display_capabilities capabilities;
194 struct display_buffer_descriptor buf_desc;
195 size_t buf_size = 0;
196 fill_buffer fill_buffer_fnc = NULL;
197
198 display_dev = DEVICE_DT_GET(DT_CHOSEN(zephyr_display));
199 if (!device_is_ready(display_dev)) {
200 LOG_ERR("Device %s not found. Aborting sample.",
201 display_dev->name);
202 #ifdef CONFIG_ARCH_POSIX
203 posix_exit_main(1);
204 #else
205 return 0;
206 #endif
207 }
208
209 LOG_INF("Display sample for %s", display_dev->name);
210 display_get_capabilities(display_dev, &capabilities);
211
212 if (capabilities.screen_info & SCREEN_INFO_MONO_VTILED) {
213 rect_w = 16;
214 rect_h = 8;
215 } else {
216 rect_w = 2;
217 rect_h = 1;
218 }
219
220 if ((capabilities.x_resolution < 3 * rect_w) ||
221 (capabilities.y_resolution < 3 * rect_h) ||
222 (capabilities.x_resolution < 8 * rect_h)) {
223 rect_w = capabilities.x_resolution * 40 / 100;
224 rect_h = capabilities.y_resolution * 40 / 100;
225 h_step = capabilities.y_resolution * 20 / 100;
226 scale = 1;
227 } else {
228 h_step = rect_h;
229 scale = (capabilities.x_resolution / 8) / rect_h;
230 }
231
232 rect_w *= scale;
233 rect_h *= scale;
234
235 if (capabilities.screen_info & SCREEN_INFO_EPD) {
236 grey_scale_sleep = 10000;
237 } else {
238 grey_scale_sleep = 100;
239 }
240
241 if (capabilities.screen_info & SCREEN_INFO_X_ALIGNMENT_WIDTH) {
242 rect_w = capabilities.x_resolution;
243 }
244
245 buf_size = rect_w * rect_h;
246
247 if (buf_size < (capabilities.x_resolution * h_step)) {
248 buf_size = capabilities.x_resolution * h_step;
249 }
250
251 switch (capabilities.current_pixel_format) {
252 case PIXEL_FORMAT_ARGB_8888:
253 bg_color = 0x00u;
254 fill_buffer_fnc = fill_buffer_argb8888;
255 buf_size *= 4;
256 break;
257 case PIXEL_FORMAT_RGB_888:
258 bg_color = 0xFFu;
259 fill_buffer_fnc = fill_buffer_rgb888;
260 buf_size *= 3;
261 break;
262 case PIXEL_FORMAT_RGB_565:
263 bg_color = 0xFFu;
264 fill_buffer_fnc = fill_buffer_rgb565;
265 buf_size *= 2;
266 break;
267 case PIXEL_FORMAT_BGR_565:
268 bg_color = 0xFFu;
269 fill_buffer_fnc = fill_buffer_bgr565;
270 buf_size *= 2;
271 break;
272 case PIXEL_FORMAT_L_8:
273 bg_color = 0xFFu;
274 fill_buffer_fnc = fill_buffer_l_8;
275 break;
276 case PIXEL_FORMAT_MONO01:
277 bg_color = 0xFFu;
278 fill_buffer_fnc = fill_buffer_mono01;
279 buf_size = DIV_ROUND_UP(DIV_ROUND_UP(
280 buf_size, NUM_BITS(uint8_t)), sizeof(uint8_t));
281 break;
282 case PIXEL_FORMAT_MONO10:
283 bg_color = 0x00u;
284 fill_buffer_fnc = fill_buffer_mono10;
285 buf_size = DIV_ROUND_UP(DIV_ROUND_UP(
286 buf_size, NUM_BITS(uint8_t)), sizeof(uint8_t));
287 break;
288 default:
289 LOG_ERR("Unsupported pixel format. Aborting sample.");
290 #ifdef CONFIG_ARCH_POSIX
291 posix_exit_main(1);
292 #else
293 return 0;
294 #endif
295 }
296
297 buf = k_malloc(buf_size);
298
299 if (buf == NULL) {
300 LOG_ERR("Could not allocate memory. Aborting sample.");
301 #ifdef CONFIG_ARCH_POSIX
302 posix_exit_main(1);
303 #else
304 return 0;
305 #endif
306 }
307
308 (void)memset(buf, bg_color, buf_size);
309
310 buf_desc.buf_size = buf_size;
311 buf_desc.pitch = capabilities.x_resolution;
312 buf_desc.width = capabilities.x_resolution;
313 buf_desc.height = h_step;
314
315 /*
316 * The following writes will only render parts of the image,
317 * so turn this option on.
318 * This allows double-buffered displays to hold the pixels
319 * back until the image is complete.
320 */
321 buf_desc.frame_incomplete = true;
322
323 for (int idx = 0; idx < capabilities.y_resolution; idx += h_step) {
324 /*
325 * Tweaking the height value not to draw outside of the display.
326 * It is required when using a monochrome display whose vertical
327 * resolution can not be divided by 8.
328 */
329 if ((capabilities.y_resolution - idx) < h_step) {
330 buf_desc.height = (capabilities.y_resolution - idx);
331 }
332 display_write(display_dev, 0, idx, &buf_desc, buf);
333 }
334
335 buf_desc.pitch = rect_w;
336 buf_desc.width = rect_w;
337 buf_desc.height = rect_h;
338
339 fill_buffer_fnc(TOP_LEFT, 0, buf, buf_size);
340 x = 0;
341 y = 0;
342 display_write(display_dev, x, y, &buf_desc, buf);
343
344 fill_buffer_fnc(TOP_RIGHT, 0, buf, buf_size);
345 x = capabilities.x_resolution - rect_w;
346 y = 0;
347 display_write(display_dev, x, y, &buf_desc, buf);
348
349 /*
350 * This is the last write of the frame, so turn this off.
351 * Double-buffered displays will now present the new image
352 * to the user.
353 */
354 buf_desc.frame_incomplete = false;
355
356 fill_buffer_fnc(BOTTOM_RIGHT, 0, buf, buf_size);
357 x = capabilities.x_resolution - rect_w;
358 y = capabilities.y_resolution - rect_h;
359 display_write(display_dev, x, y, &buf_desc, buf);
360
361 display_blanking_off(display_dev);
362
363 grey_count = 0;
364 x = 0;
365 y = capabilities.y_resolution - rect_h;
366
367 LOG_INF("Display starts");
368 while (1) {
369 fill_buffer_fnc(BOTTOM_LEFT, grey_count, buf, buf_size);
370 display_write(display_dev, x, y, &buf_desc, buf);
371 ++grey_count;
372 k_msleep(grey_scale_sleep);
373 #if CONFIG_TEST
374 if (grey_count >= 30) {
375 LOG_INF("Display sample test mode done %s", display_dev->name);
376 break;
377 }
378 #endif
379 }
380
381 #ifdef CONFIG_ARCH_POSIX
382 posix_exit_main(0);
383 #endif
384 return 0;
385 }
386