1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3 * Copyright (c) 2016 Google, Inc
4 */
5
6 #include <common.h>
7 #include <dm.h>
8 #include <log.h>
9 #include <malloc.h>
10 #include <video.h>
11 #include <video_console.h>
12
13 /* Functions needed by stb_truetype.h */
tt_floor(double val)14 static int tt_floor(double val)
15 {
16 if (val < 0)
17 return (int)(val - 0.999);
18
19 return (int)val;
20 }
21
tt_ceil(double val)22 static int tt_ceil(double val)
23 {
24 if (val < 0)
25 return (int)val;
26
27 return (int)(val + 0.999);
28 }
29
frac(double val)30 static double frac(double val)
31 {
32 return val - tt_floor(val);
33 }
34
tt_fabs(double x)35 static double tt_fabs(double x)
36 {
37 return x < 0 ? -x : x;
38 }
39
40 /*
41 * Simple square root algorithm. This is from:
42 * http://stackoverflow.com/questions/1623375/writing-your-own-square-root-function
43 * Written by Chihung Yu
44 * Creative Commons license
45 * http://creativecommons.org/licenses/by-sa/3.0/legalcode
46 * It has been modified to compile correctly, and for U-Boot style.
47 */
tt_sqrt(double value)48 static double tt_sqrt(double value)
49 {
50 double lo = 1.0;
51 double hi = value;
52
53 while (hi - lo > 0.00001) {
54 double mid = lo + (hi - lo) / 2;
55
56 if (mid * mid - value > 0.00001)
57 hi = mid;
58 else
59 lo = mid;
60 }
61
62 return lo;
63 }
64
65 #define STBTT_ifloor tt_floor
66 #define STBTT_iceil tt_ceil
67 #define STBTT_fabs tt_fabs
68 #define STBTT_sqrt tt_sqrt
69 #define STBTT_malloc(size, u) ((void)(u), malloc(size))
70 #define STBTT_free(size, u) ((void)(u), free(size))
71 #define STBTT_assert(x)
72 #define STBTT_strlen(x) strlen(x)
73 #define STBTT_memcpy memcpy
74 #define STBTT_memset memset
75
76 #define STB_TRUETYPE_IMPLEMENTATION
77 #include "stb_truetype.h"
78
79 /**
80 * struct pos_info - Records a cursor position
81 *
82 * @xpos_frac: Fractional X position in pixels (multiplied by VID_FRAC_DIV)
83 * @ypos: Y position (pixels from the top)
84 */
85 struct pos_info {
86 int xpos_frac;
87 int ypos;
88 };
89
90 /*
91 * Allow one for each character on the command line plus one for each newline.
92 * This is just an estimate, but it should not be exceeded.
93 */
94 #define POS_HISTORY_SIZE (CONFIG_SYS_CBSIZE * 11 / 10)
95
96 /**
97 * struct console_tt_priv - Private data for this driver
98 *
99 * @font_size: Vertical font size in pixels
100 * @font_data: Pointer to TrueType font file contents
101 * @font: TrueType font information for the current font
102 * @pos: List of cursor positions for each character written. This is
103 * used to handle backspace. We clear the frame buffer between
104 * the last position and the current position, thus erasing the
105 * last character. We record enough characters to go back to the
106 * start of the current command line.
107 * @pos_ptr: Current position in the position history
108 * @baseline: Pixel offset of the font's baseline from the cursor position.
109 * This is the 'ascent' of the font, scaled to pixel coordinates.
110 * It measures the distance from the baseline to the top of the
111 * font.
112 * @scale: Scale of the font. This is calculated from the pixel height
113 * of the font. It is used by the STB library to generate images
114 * of the correct size.
115 */
116 struct console_tt_priv {
117 int font_size;
118 u8 *font_data;
119 stbtt_fontinfo font;
120 struct pos_info pos[POS_HISTORY_SIZE];
121 int pos_ptr;
122 int baseline;
123 double scale;
124 };
125
console_truetype_set_row(struct udevice * dev,uint row,int clr)126 static int console_truetype_set_row(struct udevice *dev, uint row, int clr)
127 {
128 struct video_priv *vid_priv = dev_get_uclass_priv(dev->parent);
129 struct console_tt_priv *priv = dev_get_priv(dev);
130 void *end, *line;
131 int ret;
132
133 line = vid_priv->fb + row * priv->font_size * vid_priv->line_length;
134 end = line + priv->font_size * vid_priv->line_length;
135
136 switch (vid_priv->bpix) {
137 #ifdef CONFIG_VIDEO_BPP8
138 case VIDEO_BPP8: {
139 u8 *dst;
140
141 for (dst = line; dst < (u8 *)end; ++dst)
142 *dst = clr;
143 break;
144 }
145 #endif
146 #ifdef CONFIG_VIDEO_BPP16
147 case VIDEO_BPP16: {
148 u16 *dst = line;
149
150 for (dst = line; dst < (u16 *)end; ++dst)
151 *dst = clr;
152 break;
153 }
154 #endif
155 #ifdef CONFIG_VIDEO_BPP32
156 case VIDEO_BPP32: {
157 u32 *dst = line;
158
159 for (dst = line; dst < (u32 *)end; ++dst)
160 *dst = clr;
161 break;
162 }
163 #endif
164 default:
165 return -ENOSYS;
166 }
167 ret = vidconsole_sync_copy(dev, line, end);
168 if (ret)
169 return ret;
170
171 return 0;
172 }
173
console_truetype_move_rows(struct udevice * dev,uint rowdst,uint rowsrc,uint count)174 static int console_truetype_move_rows(struct udevice *dev, uint rowdst,
175 uint rowsrc, uint count)
176 {
177 struct video_priv *vid_priv = dev_get_uclass_priv(dev->parent);
178 struct console_tt_priv *priv = dev_get_priv(dev);
179 void *dst;
180 void *src;
181 int i, diff, ret;
182
183 dst = vid_priv->fb + rowdst * priv->font_size * vid_priv->line_length;
184 src = vid_priv->fb + rowsrc * priv->font_size * vid_priv->line_length;
185 ret = vidconsole_memmove(dev, dst, src, priv->font_size *
186 vid_priv->line_length * count);
187 if (ret)
188 return ret;
189
190 /* Scroll up our position history */
191 diff = (rowsrc - rowdst) * priv->font_size;
192 for (i = 0; i < priv->pos_ptr; i++)
193 priv->pos[i].ypos -= diff;
194
195 return 0;
196 }
197
console_truetype_putc_xy(struct udevice * dev,uint x,uint y,char ch)198 static int console_truetype_putc_xy(struct udevice *dev, uint x, uint y,
199 char ch)
200 {
201 struct vidconsole_priv *vc_priv = dev_get_uclass_priv(dev);
202 struct udevice *vid = dev->parent;
203 struct video_priv *vid_priv = dev_get_uclass_priv(vid);
204 struct console_tt_priv *priv = dev_get_priv(dev);
205 stbtt_fontinfo *font = &priv->font;
206 int width, height, xoff, yoff;
207 double xpos, x_shift;
208 int lsb;
209 int width_frac, linenum;
210 struct pos_info *pos;
211 u8 *bits, *data;
212 int advance;
213 void *start, *end, *line;
214 int row, ret;
215
216 /* First get some basic metrics about this character */
217 stbtt_GetCodepointHMetrics(font, ch, &advance, &lsb);
218
219 /*
220 * First out our current X position in fractional pixels. If we wrote
221 * a character previously, using kerning to fine-tune the position of
222 * this character */
223 xpos = frac(VID_TO_PIXEL((double)x));
224 if (vc_priv->last_ch) {
225 xpos += priv->scale * stbtt_GetCodepointKernAdvance(font,
226 vc_priv->last_ch, ch);
227 }
228
229 /*
230 * Figure out where the cursor will move to after this character, and
231 * abort if we are out of space on this line. Also calculate the
232 * effective width of this character, which will be our return value:
233 * it dictates how much the cursor will move forward on the line.
234 */
235 x_shift = xpos - (double)tt_floor(xpos);
236 xpos += advance * priv->scale;
237 width_frac = (int)VID_TO_POS(xpos);
238 if (x + width_frac >= vc_priv->xsize_frac)
239 return -EAGAIN;
240
241 /* Write the current cursor position into history */
242 if (priv->pos_ptr < POS_HISTORY_SIZE) {
243 pos = &priv->pos[priv->pos_ptr];
244 pos->xpos_frac = vc_priv->xcur_frac;
245 pos->ypos = vc_priv->ycur;
246 priv->pos_ptr++;
247 }
248
249 /*
250 * Figure out how much past the start of a pixel we are, and pass this
251 * information into the render, which will return a 8-bit-per-pixel
252 * image of the character. For empty characters, like ' ', data will
253 * return NULL;
254 */
255 data = stbtt_GetCodepointBitmapSubpixel(font, priv->scale, priv->scale,
256 x_shift, 0, ch, &width, &height,
257 &xoff, &yoff);
258 if (!data)
259 return width_frac;
260
261 /* Figure out where to write the character in the frame buffer */
262 bits = data;
263 start = vid_priv->fb + y * vid_priv->line_length +
264 VID_TO_PIXEL(x) * VNBYTES(vid_priv->bpix);
265 linenum = priv->baseline + yoff;
266 if (linenum > 0)
267 start += linenum * vid_priv->line_length;
268 line = start;
269
270 /*
271 * Write a row at a time, converting the 8bpp image into the colour
272 * depth of the display. We only expect white-on-black or the reverse
273 * so the code only handles this simple case.
274 */
275 for (row = 0; row < height; row++) {
276 switch (vid_priv->bpix) {
277 #ifdef CONFIG_VIDEO_BPP16
278 case VIDEO_BPP16: {
279 uint16_t *dst = (uint16_t *)line + xoff;
280 int i;
281
282 for (i = 0; i < width; i++) {
283 int val = *bits;
284 int out;
285
286 if (vid_priv->colour_bg)
287 val = 255 - val;
288 out = val >> 3 |
289 (val >> 2) << 5 |
290 (val >> 3) << 11;
291 if (vid_priv->colour_fg)
292 *dst++ |= out;
293 else
294 *dst++ &= out;
295 bits++;
296 }
297 end = dst;
298 break;
299 }
300 #endif
301 #ifdef CONFIG_VIDEO_BPP32
302 case VIDEO_BPP32: {
303 u32 *dst = (u32 *)line + xoff;
304 int i;
305
306 for (i = 0; i < width; i++) {
307 int val = *bits;
308 int out;
309
310 if (vid_priv->colour_bg)
311 val = 255 - val;
312 out = val | val << 8 | val << 16;
313 if (vid_priv->colour_fg)
314 *dst++ |= out;
315 else
316 *dst++ &= out;
317 bits++;
318 }
319 end = dst;
320 break;
321 }
322 #endif
323 default:
324 free(data);
325 return -ENOSYS;
326 }
327
328 line += vid_priv->line_length;
329 }
330 ret = vidconsole_sync_copy(dev, start, line);
331 if (ret)
332 return ret;
333 free(data);
334
335 return width_frac;
336 }
337
338 /**
339 * console_truetype_erase() - Erase a character
340 *
341 * This is used for backspace. We erase a square of the display within the
342 * given bounds.
343 *
344 * @dev: Device to update
345 * @xstart: X start position in pixels from the left
346 * @ystart: Y start position in pixels from the top
347 * @xend: X end position in pixels from the left
348 * @yend: Y end position in pixels from the top
349 * @clr: Value to write
350 * @return 0 if OK, -ENOSYS if the display depth is not supported
351 */
console_truetype_erase(struct udevice * dev,int xstart,int ystart,int xend,int yend,int clr)352 static int console_truetype_erase(struct udevice *dev, int xstart, int ystart,
353 int xend, int yend, int clr)
354 {
355 struct video_priv *vid_priv = dev_get_uclass_priv(dev->parent);
356 void *start, *line;
357 int pixels = xend - xstart;
358 int row, i, ret;
359
360 start = vid_priv->fb + ystart * vid_priv->line_length;
361 start += xstart * VNBYTES(vid_priv->bpix);
362 line = start;
363 for (row = ystart; row < yend; row++) {
364 switch (vid_priv->bpix) {
365 #ifdef CONFIG_VIDEO_BPP8
366 case VIDEO_BPP8: {
367 uint8_t *dst = line;
368
369 for (i = 0; i < pixels; i++)
370 *dst++ = clr;
371 break;
372 }
373 #endif
374 #ifdef CONFIG_VIDEO_BPP16
375 case VIDEO_BPP16: {
376 uint16_t *dst = line;
377
378 for (i = 0; i < pixels; i++)
379 *dst++ = clr;
380 break;
381 }
382 #endif
383 #ifdef CONFIG_VIDEO_BPP32
384 case VIDEO_BPP32: {
385 uint32_t *dst = line;
386
387 for (i = 0; i < pixels; i++)
388 *dst++ = clr;
389 break;
390 }
391 #endif
392 default:
393 return -ENOSYS;
394 }
395 line += vid_priv->line_length;
396 }
397 ret = vidconsole_sync_copy(dev, start, line);
398 if (ret)
399 return ret;
400
401 return 0;
402 }
403
404 /**
405 * console_truetype_backspace() - Handle a backspace operation
406 *
407 * This clears the previous character so that the console looks as if it had
408 * not been entered.
409 *
410 * @dev: Device to update
411 * @return 0 if OK, -ENOSYS if not supported
412 */
console_truetype_backspace(struct udevice * dev)413 static int console_truetype_backspace(struct udevice *dev)
414 {
415 struct vidconsole_priv *vc_priv = dev_get_uclass_priv(dev);
416 struct console_tt_priv *priv = dev_get_priv(dev);
417 struct udevice *vid_dev = dev->parent;
418 struct video_priv *vid_priv = dev_get_uclass_priv(vid_dev);
419 struct pos_info *pos;
420 int xend;
421
422 /*
423 * This indicates a very strange error higher in the stack. The caller
424 * has sent out n character and n + 1 backspaces.
425 */
426 if (!priv->pos_ptr)
427 return -ENOSYS;
428
429 /* Pop the last cursor position off the stack */
430 pos = &priv->pos[--priv->pos_ptr];
431
432 /*
433 * Figure out the end position for clearing. Normally it is the current
434 * cursor position, but if we are clearing a character on the previous
435 * line, we clear from the end of the line.
436 */
437 if (pos->ypos == vc_priv->ycur)
438 xend = VID_TO_PIXEL(vc_priv->xcur_frac);
439 else
440 xend = vid_priv->xsize;
441
442 console_truetype_erase(dev, VID_TO_PIXEL(pos->xpos_frac), pos->ypos,
443 xend, pos->ypos + vc_priv->y_charsize,
444 vid_priv->colour_bg);
445
446 /* Move the cursor back to where it was when we pushed this record */
447 vc_priv->xcur_frac = pos->xpos_frac;
448 vc_priv->ycur = pos->ypos;
449
450 return 0;
451 }
452
console_truetype_entry_start(struct udevice * dev)453 static int console_truetype_entry_start(struct udevice *dev)
454 {
455 struct console_tt_priv *priv = dev_get_priv(dev);
456
457 /* A new input line has start, so clear our history */
458 priv->pos_ptr = 0;
459
460 return 0;
461 }
462
463 /*
464 * Provides a list of fonts which can be obtained at run-time in U-Boot. These
465 * are compiled in by the Makefile.
466 *
467 * At present there is no mechanism to select a particular font - the first
468 * one found is the one that is used. But the build system and the code here
469 * supports multiple fonts, which may be useful for certain firmware screens.
470 */
471 struct font_info {
472 char *name;
473 u8 *begin;
474 u8 *end;
475 };
476
477 #define FONT_DECL(_name) \
478 extern u8 __ttf_ ## _name ## _begin[]; \
479 extern u8 __ttf_ ## _name ## _end[];
480
481 #define FONT_ENTRY(_name) { \
482 .name = #_name, \
483 .begin = __ttf_ ## _name ## _begin, \
484 .end = __ttf_ ## _name ## _end, \
485 }
486
487 FONT_DECL(nimbus_sans_l_regular);
488 FONT_DECL(ankacoder_c75_r);
489 FONT_DECL(rufscript010);
490 FONT_DECL(cantoraone_regular);
491
492 static struct font_info font_table[] = {
493 #ifdef CONFIG_CONSOLE_TRUETYPE_NIMBUS
494 FONT_ENTRY(nimbus_sans_l_regular),
495 #endif
496 #ifdef CONFIG_CONSOLE_TRUETYPE_ANKACODER
497 FONT_ENTRY(ankacoder_c75_r),
498 #endif
499 #ifdef CONFIG_CONSOLE_TRUETYPE_RUFSCRIPT
500 FONT_ENTRY(rufscript010),
501 #endif
502 #ifdef CONFIG_CONSOLE_TRUETYPE_CANTORAONE
503 FONT_ENTRY(cantoraone_regular),
504 #endif
505 {} /* sentinel */
506 };
507
508 #define FONT_BEGIN(name) __ttf_ ## name ## _begin
509 #define FONT_END(name) __ttf_ ## name ## _end
510 #define FONT_IS_VALID(name) (abs(FONT_END(name) - FONT_BEGIN) > 4)
511
512 /**
513 * console_truetype_find_font() - Find a suitable font
514 *
515 * This searched for the first available font.
516 *
517 * @return pointer to the font, or NULL if none is found
518 */
console_truetype_find_font(void)519 static u8 *console_truetype_find_font(void)
520 {
521 struct font_info *tab;
522
523 for (tab = font_table; tab->begin; tab++) {
524 if (abs(tab->begin - tab->end) > 4) {
525 debug("%s: Font '%s', at %p, size %lx\n", __func__,
526 tab->name, tab->begin,
527 (ulong)(tab->end - tab->begin));
528 return tab->begin;
529 }
530 }
531
532 return NULL;
533 }
534
console_truetype_probe(struct udevice * dev)535 static int console_truetype_probe(struct udevice *dev)
536 {
537 struct vidconsole_priv *vc_priv = dev_get_uclass_priv(dev);
538 struct console_tt_priv *priv = dev_get_priv(dev);
539 struct udevice *vid_dev = dev->parent;
540 struct video_priv *vid_priv = dev_get_uclass_priv(vid_dev);
541 stbtt_fontinfo *font = &priv->font;
542 int ascent;
543
544 debug("%s: start\n", __func__);
545 if (vid_priv->font_size)
546 priv->font_size = vid_priv->font_size;
547 else
548 priv->font_size = CONFIG_CONSOLE_TRUETYPE_SIZE;
549 priv->font_data = console_truetype_find_font();
550 if (!priv->font_data) {
551 debug("%s: Could not find any fonts\n", __func__);
552 return -EBFONT;
553 }
554
555 vc_priv->x_charsize = priv->font_size;
556 vc_priv->y_charsize = priv->font_size;
557 vc_priv->xstart_frac = VID_TO_POS(2);
558 vc_priv->cols = vid_priv->xsize / priv->font_size;
559 vc_priv->rows = vid_priv->ysize / priv->font_size;
560 vc_priv->tab_width_frac = VID_TO_POS(priv->font_size) * 8 / 2;
561
562 if (!stbtt_InitFont(font, priv->font_data, 0)) {
563 debug("%s: Font init failed\n", __func__);
564 return -EPERM;
565 }
566
567 /* Pre-calculate some things we will need regularly */
568 priv->scale = stbtt_ScaleForPixelHeight(font, priv->font_size);
569 stbtt_GetFontVMetrics(font, &ascent, 0, 0);
570 priv->baseline = (int)(ascent * priv->scale);
571 debug("%s: ready\n", __func__);
572
573 return 0;
574 }
575
576 struct vidconsole_ops console_truetype_ops = {
577 .putc_xy = console_truetype_putc_xy,
578 .move_rows = console_truetype_move_rows,
579 .set_row = console_truetype_set_row,
580 .backspace = console_truetype_backspace,
581 .entry_start = console_truetype_entry_start,
582 };
583
584 U_BOOT_DRIVER(vidconsole_truetype) = {
585 .name = "vidconsole_tt",
586 .id = UCLASS_VIDEO_CONSOLE,
587 .ops = &console_truetype_ops,
588 .probe = console_truetype_probe,
589 .priv_auto = sizeof(struct console_tt_priv),
590 };
591