1 /*
2 * Copyright (c) 2018 PHYTEC Messtechnik GmbH
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #include <zephyr/kernel.h>
8 #include <string.h>
9 #include <zephyr/display/cfb.h>
10
11 #define LOG_LEVEL CONFIG_CFB_LOG_LEVEL
12 #include <zephyr/logging/log.h>
13 LOG_MODULE_REGISTER(cfb);
14
15 STRUCT_SECTION_START_EXTERN(cfb_font);
16 STRUCT_SECTION_END_EXTERN(cfb_font);
17
byte_reverse(uint8_t b)18 static inline uint8_t byte_reverse(uint8_t b)
19 {
20 b = (b & 0xf0) >> 4 | (b & 0x0f) << 4;
21 b = (b & 0xcc) >> 2 | (b & 0x33) << 2;
22 b = (b & 0xaa) >> 1 | (b & 0x55) << 1;
23 return b;
24 }
25
26 struct char_framebuffer {
27 /** Pointer to a buffer in RAM */
28 uint8_t *buf;
29
30 /** Size of the framebuffer */
31 uint32_t size;
32
33 /** Pointer to the font entry array */
34 const struct cfb_font *fonts;
35
36 /** Display pixel format */
37 enum display_pixel_format pixel_format;
38
39 /** Display screen info */
40 enum display_screen_info screen_info;
41
42 /** Resolution of a framebuffer in pixels in X direction */
43 uint16_t x_res;
44
45 /** Resolution of a framebuffer in pixels in Y direction */
46 uint16_t y_res;
47
48 /** Number of pixels per tile, typically 8 */
49 uint8_t ppt;
50
51 /** Number of available fonts */
52 uint8_t numof_fonts;
53
54 /** Current font index */
55 uint8_t font_idx;
56
57 /** Font kerning */
58 int8_t kerning;
59
60 /** Inverted */
61 bool inverted;
62 };
63
64 static struct char_framebuffer char_fb;
65
get_glyph_ptr(const struct cfb_font * fptr,char c)66 static inline uint8_t *get_glyph_ptr(const struct cfb_font *fptr, char c)
67 {
68 return (uint8_t *)fptr->data +
69 (c - fptr->first_char) *
70 (fptr->width * fptr->height / 8U);
71 }
72
get_glyph_byte(uint8_t * glyph_ptr,const struct cfb_font * fptr,uint8_t x,uint8_t y)73 static inline uint8_t get_glyph_byte(uint8_t *glyph_ptr, const struct cfb_font *fptr,
74 uint8_t x, uint8_t y)
75 {
76 if (fptr->caps & CFB_FONT_MONO_VPACKED) {
77 return glyph_ptr[x * (fptr->height / 8U) + y];
78 } else if (fptr->caps & CFB_FONT_MONO_HPACKED) {
79 return glyph_ptr[y * (fptr->width) + x];
80 }
81
82 LOG_WRN("Unknown font type");
83 return 0;
84 }
85
86 /*
87 * Draw the monochrome character in the monochrome tiled framebuffer,
88 * a byte is interpreted as 8 pixels ordered vertically among each other.
89 */
draw_char_vtmono(const struct char_framebuffer * fb,char c,uint16_t x,uint16_t y,bool draw_bg)90 static uint8_t draw_char_vtmono(const struct char_framebuffer *fb,
91 char c, uint16_t x, uint16_t y,
92 bool draw_bg)
93 {
94 const struct cfb_font *fptr = &(fb->fonts[fb->font_idx]);
95 const bool need_reverse = (((fb->screen_info & SCREEN_INFO_MONO_MSB_FIRST) != 0)
96 != ((fptr->caps & CFB_FONT_MSB_FIRST) != 0));
97 uint8_t *glyph_ptr;
98
99 if (c < fptr->first_char || c > fptr->last_char) {
100 c = ' ';
101 }
102
103 glyph_ptr = get_glyph_ptr(fptr, c);
104 if (!glyph_ptr) {
105 return 0;
106 }
107
108 for (size_t g_x = 0; g_x < fptr->width; g_x++) {
109 const int16_t fb_x = x + g_x;
110
111 for (size_t g_y = 0; g_y < fptr->height; g_y++) {
112 /*
113 * Process glyph rendering in the y direction
114 * by separating per 8-line boundaries.
115 */
116
117 const int16_t fb_y = y + g_y;
118 const size_t fb_index = (fb_y / 8U) * fb->x_res + fb_x;
119 const size_t offset = y % 8;
120 uint8_t bg_mask;
121 uint8_t byte;
122
123 if (fb_x < 0 || fb->x_res <= fb_x || fb_y < 0 || fb->y_res <= fb_y) {
124 continue;
125 }
126
127 byte = get_glyph_byte(glyph_ptr, fptr, g_x, g_y / 8);
128
129 if (offset == 0) {
130 /*
131 * The start row is on an 8-line boundary.
132 * Therefore, it can set the value directly.
133 */
134 bg_mask = 0;
135 g_y += 7;
136 } else if (g_y == 0) {
137 /*
138 * If the starting row is not on the 8-line boundary,
139 * shift the glyph to the starting line, and create a mask
140 * from the 8-line boundary to the starting line.
141 */
142 byte = byte << offset;
143 bg_mask = BIT_MASK(offset);
144 g_y += (7 - offset);
145 } else {
146 /*
147 * After the starting row, read and concatenate the next 8-rows
148 * glyph and clip to the 8-line boundary and write 8-lines
149 * at the time.
150 * Create a mask to prevent overwriting the drawing contents
151 * after the end row.
152 */
153 const size_t lines = ((fptr->height - g_y) < 8) ? offset : 8;
154
155 if (lines == 8) {
156 uint16_t byte2 = byte;
157
158 byte2 |= (get_glyph_byte(glyph_ptr, fptr, g_x,
159 (g_y + 8) / 8))
160 << 8;
161 byte = (byte2 >> (8 - offset)) & BIT_MASK(lines);
162 } else {
163 byte = (byte >> (8 - offset)) & BIT_MASK(lines);
164 }
165
166 bg_mask = (BIT_MASK(8 - lines) << (lines)) & 0xFF;
167 g_y += (lines - 1);
168 }
169
170 if (need_reverse) {
171 byte = byte_reverse(byte);
172 bg_mask = byte_reverse(bg_mask);
173 }
174
175 if (draw_bg) {
176 fb->buf[fb_index] &= bg_mask;
177 }
178
179 fb->buf[fb_index] |= byte;
180 }
181 }
182
183 return fptr->width;
184 }
185
draw_point(struct char_framebuffer * fb,int16_t x,int16_t y)186 static inline void draw_point(struct char_framebuffer *fb, int16_t x, int16_t y)
187 {
188 const bool need_reverse = ((fb->screen_info & SCREEN_INFO_MONO_MSB_FIRST) != 0);
189 const size_t index = ((y / 8) * fb->x_res);
190 uint8_t m = BIT(y % 8);
191
192 if (x < 0 || x >= fb->x_res) {
193 return;
194 }
195
196 if (y < 0 || y >= fb->y_res) {
197 return;
198 }
199
200 if (need_reverse) {
201 m = byte_reverse(m);
202 }
203
204 fb->buf[index + x] |= m;
205 }
206
draw_line(struct char_framebuffer * fb,int16_t x0,int16_t y0,int16_t x1,int16_t y1)207 static void draw_line(struct char_framebuffer *fb, int16_t x0, int16_t y0, int16_t x1, int16_t y1)
208 {
209 int16_t sx = (x0 < x1) ? 1 : -1;
210 int16_t sy = (y0 < y1) ? 1 : -1;
211 int16_t dx = (sx > 0) ? (x1 - x0) : (x0 - x1);
212 int16_t dy = (sy > 0) ? (y0 - y1) : (y1 - y0);
213 int16_t err = dx + dy;
214 int16_t e2;
215
216 while (true) {
217 draw_point(fb, x0, y0);
218
219 if (x0 == x1 && y0 == y1) {
220 break;
221 }
222
223 e2 = 2 * err;
224
225 if (e2 >= dy) {
226 err += dy;
227 x0 += sx;
228 }
229
230 if (e2 <= dx) {
231 err += dx;
232 y0 += sy;
233 }
234 }
235 }
236
draw_text(const struct device * dev,const char * const str,int16_t x,int16_t y,bool wrap)237 static int draw_text(const struct device *dev, const char *const str, int16_t x, int16_t y,
238 bool wrap)
239 {
240 const struct char_framebuffer *fb = &char_fb;
241 const struct cfb_font *fptr;
242
243 if (!fb->fonts || !fb->buf) {
244 return -ENODEV;
245 }
246
247 fptr = &(fb->fonts[fb->font_idx]);
248
249 if (fptr->height % 8) {
250 LOG_ERR("Wrong font size");
251 return -EINVAL;
252 }
253
254 if ((fb->screen_info & SCREEN_INFO_MONO_VTILED)) {
255 for (size_t i = 0; i < strlen(str); i++) {
256 if ((x + fptr->width > fb->x_res) && wrap) {
257 x = 0U;
258 y += fptr->height;
259 }
260 x += fb->kerning + draw_char_vtmono(fb, str[i], x, y, wrap);
261 }
262 return 0;
263 }
264
265 LOG_ERR("Unsupported framebuffer configuration");
266 return -EINVAL;
267 }
268
cfb_draw_point(const struct device * dev,const struct cfb_position * pos)269 int cfb_draw_point(const struct device *dev, const struct cfb_position *pos)
270 {
271 struct char_framebuffer *fb = &char_fb;
272
273 draw_point(fb, pos->x, pos->y);
274
275 return 0;
276 }
277
cfb_draw_line(const struct device * dev,const struct cfb_position * start,const struct cfb_position * end)278 int cfb_draw_line(const struct device *dev, const struct cfb_position *start,
279 const struct cfb_position *end)
280 {
281 struct char_framebuffer *fb = &char_fb;
282
283 draw_line(fb, start->x, start->y, end->x, end->y);
284
285 return 0;
286 }
287
cfb_draw_rect(const struct device * dev,const struct cfb_position * start,const struct cfb_position * end)288 int cfb_draw_rect(const struct device *dev, const struct cfb_position *start,
289 const struct cfb_position *end)
290 {
291 struct char_framebuffer *fb = &char_fb;
292
293 draw_line(fb, start->x, start->y, end->x, start->y);
294 draw_line(fb, end->x, start->y, end->x, end->y);
295 draw_line(fb, end->x, end->y, start->x, end->y);
296 draw_line(fb, start->x, end->y, start->x, start->y);
297
298 return 0;
299 }
300
cfb_draw_text(const struct device * dev,const char * const str,int16_t x,int16_t y)301 int cfb_draw_text(const struct device *dev, const char *const str, int16_t x, int16_t y)
302 {
303 return draw_text(dev, str, x, y, false);
304 }
305
cfb_print(const struct device * dev,const char * const str,uint16_t x,uint16_t y)306 int cfb_print(const struct device *dev, const char *const str, uint16_t x, uint16_t y)
307 {
308 return draw_text(dev, str, x, y, true);
309 }
310
cfb_invert_area(const struct device * dev,uint16_t x,uint16_t y,uint16_t width,uint16_t height)311 int cfb_invert_area(const struct device *dev, uint16_t x, uint16_t y,
312 uint16_t width, uint16_t height)
313 {
314 const struct char_framebuffer *fb = &char_fb;
315 const bool need_reverse = ((fb->screen_info & SCREEN_INFO_MONO_MSB_FIRST) != 0);
316
317 if (x >= fb->x_res || y >= fb->y_res) {
318 LOG_ERR("Coordinates outside of framebuffer");
319
320 return -EINVAL;
321 }
322
323 if ((fb->screen_info & SCREEN_INFO_MONO_VTILED)) {
324 if (x > fb->x_res) {
325 x = fb->x_res;
326 }
327
328 if (y > fb->y_res) {
329 y = fb->y_res;
330 }
331
332 if (x + width > fb->x_res) {
333 width = fb->x_res - x;
334 }
335
336 if (y + height > fb->y_res) {
337 height = fb->y_res - y;
338 }
339
340 for (size_t i = x; i < x + width; i++) {
341 for (size_t j = y; j < (y + height); j++) {
342 /*
343 * Process inversion in the y direction
344 * by separating per 8-line boundaries.
345 */
346
347 const size_t index = ((j / 8) * fb->x_res) + i;
348 const uint8_t remains = y + height - j;
349
350 /*
351 * Make mask to prevent overwriting the drawing contents that on
352 * between the start line or end line and the 8-line boundary.
353 */
354 if ((j % 8) > 0) {
355 uint8_t m = BIT_MASK((j % 8));
356 uint8_t b = fb->buf[index];
357
358 /*
359 * Generate mask for remaining lines in case of
360 * drawing within 8 lines from the start line
361 */
362 if (remains < 8) {
363 m |= BIT_MASK((8 - (j % 8) + remains))
364 << ((j % 8) + remains);
365 }
366
367 if (need_reverse) {
368 m = byte_reverse(m);
369 }
370
371 fb->buf[index] = (b ^ (~m));
372 j += 7 - (j % 8);
373 } else if (remains >= 8) {
374 /* No mask required if no start or end line is included */
375 fb->buf[index] = ~fb->buf[index];
376 j += 7;
377 } else {
378 uint8_t m = BIT_MASK(8 - remains) << (remains);
379 uint8_t b = fb->buf[index];
380
381 if (need_reverse) {
382 m = byte_reverse(m);
383 }
384
385 fb->buf[index] = (b ^ (~m));
386 j += (remains - 1);
387 }
388 }
389 }
390
391 return 0;
392 }
393
394 LOG_ERR("Unsupported framebuffer configuration");
395 return -EINVAL;
396 }
397
cfb_invert(const struct char_framebuffer * fb)398 static int cfb_invert(const struct char_framebuffer *fb)
399 {
400 for (size_t i = 0; i < fb->x_res * fb->y_res / 8U; i++) {
401 fb->buf[i] = ~fb->buf[i];
402 }
403
404 return 0;
405 }
406
cfb_framebuffer_clear(const struct device * dev,bool clear_display)407 int cfb_framebuffer_clear(const struct device *dev, bool clear_display)
408 {
409 const struct char_framebuffer *fb = &char_fb;
410 struct display_buffer_descriptor desc;
411
412 if (!fb || !fb->buf) {
413 return -ENODEV;
414 }
415
416 desc.buf_size = fb->size;
417 desc.width = fb->x_res;
418 desc.height = fb->y_res;
419 desc.pitch = fb->x_res;
420 memset(fb->buf, 0, fb->size);
421
422 if (clear_display) {
423 cfb_framebuffer_finalize(dev);
424 }
425
426 return 0;
427 }
428
429
cfb_framebuffer_invert(const struct device * dev)430 int cfb_framebuffer_invert(const struct device *dev)
431 {
432 struct char_framebuffer *fb = &char_fb;
433
434 if (!fb || !fb->buf) {
435 return -ENODEV;
436 }
437
438 fb->inverted = !fb->inverted;
439
440 return 0;
441 }
442
cfb_framebuffer_finalize(const struct device * dev)443 int cfb_framebuffer_finalize(const struct device *dev)
444 {
445 const struct display_driver_api *api = dev->api;
446 const struct char_framebuffer *fb = &char_fb;
447 struct display_buffer_descriptor desc;
448 int err;
449
450 if (!fb || !fb->buf) {
451 return -ENODEV;
452 }
453
454 desc.buf_size = fb->size;
455 desc.width = fb->x_res;
456 desc.height = fb->y_res;
457 desc.pitch = fb->x_res;
458
459 if (!(fb->pixel_format & PIXEL_FORMAT_MONO10) != !(fb->inverted)) {
460 cfb_invert(fb);
461 err = api->write(dev, 0, 0, &desc, fb->buf);
462 cfb_invert(fb);
463 return err;
464 }
465
466 return api->write(dev, 0, 0, &desc, fb->buf);
467 }
468
cfb_get_display_parameter(const struct device * dev,enum cfb_display_param param)469 int cfb_get_display_parameter(const struct device *dev,
470 enum cfb_display_param param)
471 {
472 const struct char_framebuffer *fb = &char_fb;
473
474 switch (param) {
475 case CFB_DISPLAY_HEIGH:
476 return fb->y_res;
477 case CFB_DISPLAY_WIDTH:
478 return fb->x_res;
479 case CFB_DISPLAY_PPT:
480 return fb->ppt;
481 case CFB_DISPLAY_ROWS:
482 if (fb->screen_info & SCREEN_INFO_MONO_VTILED) {
483 return fb->y_res / fb->ppt;
484 }
485 return fb->y_res;
486 case CFB_DISPLAY_COLS:
487 if (fb->screen_info & SCREEN_INFO_MONO_VTILED) {
488 return fb->x_res;
489 }
490 return fb->x_res / fb->ppt;
491 }
492 return 0;
493 }
494
cfb_framebuffer_set_font(const struct device * dev,uint8_t idx)495 int cfb_framebuffer_set_font(const struct device *dev, uint8_t idx)
496 {
497 struct char_framebuffer *fb = &char_fb;
498
499 if (idx >= fb->numof_fonts) {
500 return -EINVAL;
501 }
502
503 fb->font_idx = idx;
504
505 return 0;
506 }
507
cfb_get_font_size(const struct device * dev,uint8_t idx,uint8_t * width,uint8_t * height)508 int cfb_get_font_size(const struct device *dev, uint8_t idx, uint8_t *width,
509 uint8_t *height)
510 {
511 const struct char_framebuffer *fb = &char_fb;
512
513 if (idx >= fb->numof_fonts) {
514 return -EINVAL;
515 }
516
517 if (width) {
518 *width = TYPE_SECTION_START(cfb_font)[idx].width;
519 }
520
521 if (height) {
522 *height = TYPE_SECTION_START(cfb_font)[idx].height;
523 }
524
525 return 0;
526 }
527
cfb_set_kerning(const struct device * dev,int8_t kerning)528 int cfb_set_kerning(const struct device *dev, int8_t kerning)
529 {
530 char_fb.kerning = kerning;
531
532 return 0;
533 }
534
cfb_get_numof_fonts(const struct device * dev)535 int cfb_get_numof_fonts(const struct device *dev)
536 {
537 const struct char_framebuffer *fb = &char_fb;
538
539 return fb->numof_fonts;
540 }
541
cfb_framebuffer_init(const struct device * dev)542 int cfb_framebuffer_init(const struct device *dev)
543 {
544 const struct display_driver_api *api = dev->api;
545 struct char_framebuffer *fb = &char_fb;
546 struct display_capabilities cfg;
547
548 api->get_capabilities(dev, &cfg);
549
550 STRUCT_SECTION_COUNT(cfb_font, &fb->numof_fonts);
551
552 LOG_DBG("number of fonts %d", fb->numof_fonts);
553 if (!fb->numof_fonts) {
554 return -ENODEV;
555 }
556
557 fb->x_res = cfg.x_resolution;
558 fb->y_res = cfg.y_resolution;
559 fb->ppt = 8U;
560 fb->pixel_format = cfg.current_pixel_format;
561 fb->screen_info = cfg.screen_info;
562 fb->buf = NULL;
563 fb->kerning = 0;
564 fb->inverted = false;
565
566 fb->fonts = TYPE_SECTION_START(cfb_font);
567 fb->font_idx = 0U;
568
569 fb->size = fb->x_res * fb->y_res / fb->ppt;
570 fb->buf = k_malloc(fb->size);
571 if (!fb->buf) {
572 return -ENOMEM;
573 }
574
575 memset(fb->buf, 0, fb->size);
576
577 return 0;
578 }
579