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 		const size_t len = strlen(str);
282 
283 		for (size_t i = 0; i < len; i++) {
284 			if ((x + fptr->width > fb->x_res) && wrap) {
285 				x = 0U;
286 				y += fptr->height;
287 			}
288 			x += fb->kerning + draw_char_vtmono(fb, str[i], x, y, wrap);
289 		}
290 		return 0;
291 	}
292 
293 	LOG_ERR("Unsupported framebuffer configuration");
294 	return -EINVAL;
295 }
296 
cfb_draw_point(const struct device * dev,const struct cfb_position * pos)297 int cfb_draw_point(const struct device *dev, const struct cfb_position *pos)
298 {
299 	struct char_framebuffer *fb = &char_fb;
300 
301 	draw_point(fb, pos->x, pos->y);
302 
303 	return 0;
304 }
305 
cfb_draw_line(const struct device * dev,const struct cfb_position * start,const struct cfb_position * end)306 int cfb_draw_line(const struct device *dev, const struct cfb_position *start,
307 		  const struct cfb_position *end)
308 {
309 	struct char_framebuffer *fb = &char_fb;
310 
311 	draw_line(fb, start->x, start->y, end->x, end->y);
312 
313 	return 0;
314 }
315 
cfb_draw_rect(const struct device * dev,const struct cfb_position * start,const struct cfb_position * end)316 int cfb_draw_rect(const struct device *dev, const struct cfb_position *start,
317 		  const struct cfb_position *end)
318 {
319 	struct char_framebuffer *fb = &char_fb;
320 
321 	draw_line(fb, start->x, start->y, end->x, start->y);
322 	draw_line(fb, end->x, start->y, end->x, end->y);
323 	draw_line(fb, end->x, end->y, start->x, end->y);
324 	draw_line(fb, start->x, end->y, start->x, start->y);
325 
326 	return 0;
327 }
328 
cfb_draw_text(const struct device * dev,const char * const str,int16_t x,int16_t y)329 int cfb_draw_text(const struct device *dev, const char *const str, int16_t x, int16_t y)
330 {
331 	return draw_text(dev, str, x, y, false);
332 }
333 
cfb_print(const struct device * dev,const char * const str,uint16_t x,uint16_t y)334 int cfb_print(const struct device *dev, const char *const str, uint16_t x, uint16_t y)
335 {
336 	return draw_text(dev, str, x, y, true);
337 }
338 
cfb_invert_area(const struct device * dev,uint16_t x,uint16_t y,uint16_t width,uint16_t height)339 int cfb_invert_area(const struct device *dev, uint16_t x, uint16_t y,
340 		    uint16_t width, uint16_t height)
341 {
342 	const struct char_framebuffer *fb = &char_fb;
343 	const bool need_reverse = ((fb->screen_info & SCREEN_INFO_MONO_MSB_FIRST) != 0);
344 
345 	if (x >= fb->x_res || y >= fb->y_res) {
346 		LOG_ERR("Coordinates outside of framebuffer");
347 
348 		return -EINVAL;
349 	}
350 
351 	if ((fb->screen_info & SCREEN_INFO_MONO_VTILED)) {
352 		if (x > fb->x_res) {
353 			x = fb->x_res;
354 		}
355 
356 		if (y > fb->y_res) {
357 			y = fb->y_res;
358 		}
359 
360 		if (x + width > fb->x_res) {
361 			width = fb->x_res - x;
362 		}
363 
364 		if (y + height > fb->y_res) {
365 			height = fb->y_res - y;
366 		}
367 
368 		for (size_t i = x; i < x + width; i++) {
369 			for (size_t j = y; j < (y + height); j++) {
370 				/*
371 				 * Process inversion in the y direction
372 				 * by separating per 8-line boundaries.
373 				 */
374 
375 				const size_t index = ((j / 8) * fb->x_res) + i;
376 				const uint8_t remains = y + height - j;
377 
378 				/*
379 				 * Make mask to prevent overwriting the drawing contents that on
380 				 * between the start line or end line and the 8-line boundary.
381 				 */
382 				if ((j % 8) > 0) {
383 					uint8_t m = BIT_MASK((j % 8));
384 					uint8_t b = fb->buf[index];
385 
386 					/*
387 					 * Generate mask for remaining lines in case of
388 					 * drawing within 8 lines from the start line
389 					 */
390 					if (remains < 8) {
391 						m |= BIT_MASK((8 - (j % 8) + remains))
392 						     << ((j % 8) + remains);
393 					}
394 
395 					if (need_reverse) {
396 						m = byte_reverse(m);
397 					}
398 
399 					fb->buf[index] = (b ^ (~m));
400 					j += 7 - (j % 8);
401 				} else if (remains >= 8) {
402 					/* No mask required if no start or end line is included */
403 					fb->buf[index] = ~fb->buf[index];
404 					j += 7;
405 				} else {
406 					uint8_t m = BIT_MASK(8 - remains) << (remains);
407 					uint8_t b = fb->buf[index];
408 
409 					if (need_reverse) {
410 						m = byte_reverse(m);
411 					}
412 
413 					fb->buf[index] = (b ^ (~m));
414 					j += (remains - 1);
415 				}
416 			}
417 		}
418 
419 		return 0;
420 	}
421 
422 	LOG_ERR("Unsupported framebuffer configuration");
423 	return -EINVAL;
424 }
425 
cfb_invert(const struct char_framebuffer * fb)426 static int cfb_invert(const struct char_framebuffer *fb)
427 {
428 	for (size_t i = 0; i < fb->x_res * fb->y_res / 8U; i++) {
429 		fb->buf[i] = ~fb->buf[i];
430 	}
431 
432 	return 0;
433 }
434 
cfb_framebuffer_clear(const struct device * dev,bool clear_display)435 int cfb_framebuffer_clear(const struct device *dev, bool clear_display)
436 {
437 	const struct char_framebuffer *fb = &char_fb;
438 
439 	if (!fb->buf) {
440 		return -ENODEV;
441 	}
442 
443 	memset(fb->buf, 0, fb->size);
444 
445 	if (clear_display) {
446 		cfb_framebuffer_finalize(dev);
447 	}
448 
449 	return 0;
450 }
451 
cfb_framebuffer_invert(const struct device * dev)452 int cfb_framebuffer_invert(const struct device *dev)
453 {
454 	struct char_framebuffer *fb = &char_fb;
455 
456 	fb->inverted = !fb->inverted;
457 
458 	return 0;
459 }
460 
cfb_framebuffer_finalize(const struct device * dev)461 int cfb_framebuffer_finalize(const struct device *dev)
462 {
463 	const struct display_driver_api *api = dev->api;
464 	const struct char_framebuffer *fb = &char_fb;
465 	int err;
466 
467 	__ASSERT_NO_MSG(DEVICE_API_IS(display, dev));
468 
469 	if (!fb->buf) {
470 		return -ENODEV;
471 	}
472 
473 	struct display_buffer_descriptor desc = {
474 		.buf_size = fb->size,
475 		.width = fb->x_res,
476 		.height = fb->y_res,
477 		.pitch = fb->x_res,
478 	};
479 
480 	if (!(fb->pixel_format & PIXEL_FORMAT_MONO10) != !(fb->inverted)) {
481 		cfb_invert(fb);
482 		err = api->write(dev, 0, 0, &desc, fb->buf);
483 		cfb_invert(fb);
484 		return err;
485 	}
486 
487 	return api->write(dev, 0, 0, &desc, fb->buf);
488 }
489 
cfb_get_display_parameter(const struct device * dev,enum cfb_display_param param)490 int cfb_get_display_parameter(const struct device *dev,
491 			       enum cfb_display_param param)
492 {
493 	const struct char_framebuffer *fb = &char_fb;
494 
495 	switch (param) {
496 	case CFB_DISPLAY_HEIGH:
497 		return fb->y_res;
498 	case CFB_DISPLAY_WIDTH:
499 		return fb->x_res;
500 	case CFB_DISPLAY_PPT:
501 		return fb->ppt;
502 	case CFB_DISPLAY_ROWS:
503 		if (fb->screen_info & SCREEN_INFO_MONO_VTILED) {
504 			return fb->y_res / fb->ppt;
505 		}
506 		return fb->y_res;
507 	case CFB_DISPLAY_COLS:
508 		if (fb->screen_info & SCREEN_INFO_MONO_VTILED) {
509 			return fb->x_res;
510 		}
511 		return fb->x_res / fb->ppt;
512 	}
513 	return 0;
514 }
515 
cfb_framebuffer_set_font(const struct device * dev,uint8_t idx)516 int cfb_framebuffer_set_font(const struct device *dev, uint8_t idx)
517 {
518 	struct char_framebuffer *fb = &char_fb;
519 
520 	if (idx >= fb->numof_fonts) {
521 		return -EINVAL;
522 	}
523 
524 	fb->font_idx = idx;
525 
526 	return 0;
527 }
528 
cfb_get_font_size(const struct device * dev,uint8_t idx,uint8_t * width,uint8_t * height)529 int cfb_get_font_size(const struct device *dev, uint8_t idx, uint8_t *width,
530 		      uint8_t *height)
531 {
532 	const struct char_framebuffer *fb = &char_fb;
533 
534 	if (idx >= fb->numof_fonts) {
535 		return -EINVAL;
536 	}
537 
538 	if (width) {
539 		*width = TYPE_SECTION_START(cfb_font)[idx].width;
540 	}
541 
542 	if (height) {
543 		*height = TYPE_SECTION_START(cfb_font)[idx].height;
544 	}
545 
546 	return 0;
547 }
548 
cfb_set_kerning(const struct device * dev,int8_t kerning)549 int cfb_set_kerning(const struct device *dev, int8_t kerning)
550 {
551 	char_fb.kerning = kerning;
552 
553 	return 0;
554 }
555 
cfb_get_numof_fonts(const struct device * dev)556 int cfb_get_numof_fonts(const struct device *dev)
557 {
558 	const struct char_framebuffer *fb = &char_fb;
559 
560 	return fb->numof_fonts;
561 }
562 
cfb_framebuffer_init(const struct device * dev)563 int cfb_framebuffer_init(const struct device *dev)
564 {
565 	const struct display_driver_api *api = dev->api;
566 	struct char_framebuffer *fb = &char_fb;
567 	struct display_capabilities cfg;
568 
569 	__ASSERT_NO_MSG(DEVICE_API_IS(display, dev));
570 
571 	api->get_capabilities(dev, &cfg);
572 
573 	STRUCT_SECTION_COUNT(cfb_font, &fb->numof_fonts);
574 
575 	LOG_DBG("number of fonts %d", fb->numof_fonts);
576 
577 	fb->x_res = cfg.x_resolution;
578 	fb->y_res = cfg.y_resolution;
579 	fb->ppt = 8U;
580 	fb->pixel_format = cfg.current_pixel_format;
581 	fb->screen_info = cfg.screen_info;
582 	fb->kerning = 0;
583 	fb->inverted = false;
584 
585 	fb->fonts = TYPE_SECTION_START(cfb_font);
586 	fb->font_idx = 0U;
587 
588 	fb->size = fb->x_res * fb->y_res / fb->ppt;
589 	fb->buf = k_malloc(fb->size);
590 	if (!fb->buf) {
591 		return -ENOMEM;
592 	}
593 
594 	memset(fb->buf, 0, fb->size);
595 
596 	return 0;
597 }
598 
cfb_framebuffer_deinit(const struct device * dev)599 void cfb_framebuffer_deinit(const struct device *dev)
600 {
601 	struct char_framebuffer *fb = &char_fb;
602 
603 	if (fb->buf) {
604 		k_free(fb->buf);
605 		fb->buf = NULL;
606 	}
607 
608 }
609