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