1 // SPDX-License-Identifier: GPL-2.0+
3 * Copyright (c) 2016 Google, Inc
11 #include <video_console.h>
13 /* Functions needed by stb_truetype.h */
14 static int tt_floor(double val)
17 return (int)(val - 0.999);
22 static int tt_ceil(double val)
27 return (int)(val + 0.999);
30 static double frac(double val)
32 return val - tt_floor(val);
35 static double tt_fabs(double x)
37 return x < 0 ? -x : x;
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.
48 static double tt_sqrt(double value)
53 while (hi - lo > 0.00001) {
54 double mid = lo + (hi - lo) / 2;
56 if (mid * mid - value > 0.00001)
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
76 #define STB_TRUETYPE_IMPLEMENTATION
77 #include "stb_truetype.h"
80 * struct pos_info - Records a cursor position
82 * @xpos_frac: Fractional X position in pixels (multiplied by VID_FRAC_DIV)
83 * @ypos: Y position (pixels from the top)
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.
94 #define POS_HISTORY_SIZE (CONFIG_SYS_CBSIZE * 11 / 10)
97 * struct console_tt_priv - Private data for this driver
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
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.
116 struct console_tt_priv {
120 struct pos_info pos[POS_HISTORY_SIZE];
126 static int console_truetype_set_row(struct udevice *dev, uint row, int clr)
128 struct video_priv *vid_priv = dev_get_uclass_priv(dev->parent);
129 struct console_tt_priv *priv = dev_get_priv(dev);
131 int pixels = priv->font_size * vid_priv->line_length;
134 line = vid_priv->fb + row * priv->font_size * vid_priv->line_length;
135 switch (vid_priv->bpix) {
136 #ifdef CONFIG_VIDEO_BPP8
140 for (i = 0; i < pixels; i++)
146 #ifdef CONFIG_VIDEO_BPP16
148 uint16_t *dst = line;
150 for (i = 0; i < pixels; i++)
156 #ifdef CONFIG_VIDEO_BPP32
158 uint32_t *dst = line;
160 for (i = 0; i < pixels; i++)
169 ret = vidconsole_sync_copy(dev, line, end);
176 static int console_truetype_move_rows(struct udevice *dev, uint rowdst,
177 uint rowsrc, uint count)
179 struct video_priv *vid_priv = dev_get_uclass_priv(dev->parent);
180 struct console_tt_priv *priv = dev_get_priv(dev);
185 dst = vid_priv->fb + rowdst * priv->font_size * vid_priv->line_length;
186 src = vid_priv->fb + rowsrc * priv->font_size * vid_priv->line_length;
187 ret = vidconsole_memmove(dev, dst, src, priv->font_size *
188 vid_priv->line_length * count);
192 /* Scroll up our position history */
193 diff = (rowsrc - rowdst) * priv->font_size;
194 for (i = 0; i < priv->pos_ptr; i++)
195 priv->pos[i].ypos -= diff;
200 static int console_truetype_putc_xy(struct udevice *dev, uint x, uint y,
203 struct vidconsole_priv *vc_priv = dev_get_uclass_priv(dev);
204 struct udevice *vid = dev->parent;
205 struct video_priv *vid_priv = dev_get_uclass_priv(vid);
206 struct console_tt_priv *priv = dev_get_priv(dev);
207 stbtt_fontinfo *font = &priv->font;
208 int width, height, xoff, yoff;
209 double xpos, x_shift;
211 int width_frac, linenum;
212 struct pos_info *pos;
215 void *start, *end, *line;
218 /* First get some basic metrics about this character */
219 stbtt_GetCodepointHMetrics(font, ch, &advance, &lsb);
222 * First out our current X position in fractional pixels. If we wrote
223 * a character previously, using kerning to fine-tune the position of
225 xpos = frac(VID_TO_PIXEL((double)x));
226 if (vc_priv->last_ch) {
227 xpos += priv->scale * stbtt_GetCodepointKernAdvance(font,
228 vc_priv->last_ch, ch);
232 * Figure out where the cursor will move to after this character, and
233 * abort if we are out of space on this line. Also calculate the
234 * effective width of this character, which will be our return value:
235 * it dictates how much the cursor will move forward on the line.
237 x_shift = xpos - (double)tt_floor(xpos);
238 xpos += advance * priv->scale;
239 width_frac = (int)VID_TO_POS(xpos);
240 if (x + width_frac >= vc_priv->xsize_frac)
243 /* Write the current cursor position into history */
244 if (priv->pos_ptr < POS_HISTORY_SIZE) {
245 pos = &priv->pos[priv->pos_ptr];
246 pos->xpos_frac = vc_priv->xcur_frac;
247 pos->ypos = vc_priv->ycur;
252 * Figure out how much past the start of a pixel we are, and pass this
253 * information into the render, which will return a 8-bit-per-pixel
254 * image of the character. For empty characters, like ' ', data will
257 data = stbtt_GetCodepointBitmapSubpixel(font, priv->scale, priv->scale,
258 x_shift, 0, ch, &width, &height,
263 /* Figure out where to write the character in the frame buffer */
265 start = vid_priv->fb + y * vid_priv->line_length +
266 VID_TO_PIXEL(x) * VNBYTES(vid_priv->bpix);
267 linenum = priv->baseline + yoff;
269 start += linenum * vid_priv->line_length;
273 * Write a row at a time, converting the 8bpp image into the colour
274 * depth of the display. We only expect white-on-black or the reverse
275 * so the code only handles this simple case.
277 for (row = 0; row < height; row++) {
278 switch (vid_priv->bpix) {
279 #ifdef CONFIG_VIDEO_BPP16
281 uint16_t *dst = (uint16_t *)line + xoff;
284 for (i = 0; i < width; i++) {
288 if (vid_priv->colour_bg)
293 if (vid_priv->colour_fg)
303 #ifdef CONFIG_VIDEO_BPP32
305 u32 *dst = (u32 *)line + xoff;
308 for (i = 0; i < width; i++) {
312 if (vid_priv->colour_bg)
314 out = val | val << 8 | val << 16;
315 if (vid_priv->colour_fg)
330 line += vid_priv->line_length;
332 ret = vidconsole_sync_copy(dev, start, line);
341 * console_truetype_erase() - Erase a character
343 * This is used for backspace. We erase a square of the display within the
346 * @dev: Device to update
347 * @xstart: X start position in pixels from the left
348 * @ystart: Y start position in pixels from the top
349 * @xend: X end position in pixels from the left
350 * @yend: Y end position in pixels from the top
351 * @clr: Value to write
352 * @return 0 if OK, -ENOSYS if the display depth is not supported
354 static int console_truetype_erase(struct udevice *dev, int xstart, int ystart,
355 int xend, int yend, int clr)
357 struct video_priv *vid_priv = dev_get_uclass_priv(dev->parent);
359 int pixels = xend - xstart;
362 start = vid_priv->fb + ystart * vid_priv->line_length;
363 start += xstart * VNBYTES(vid_priv->bpix);
365 for (row = ystart; row < yend; row++) {
366 switch (vid_priv->bpix) {
367 #ifdef CONFIG_VIDEO_BPP8
371 for (i = 0; i < pixels; i++)
376 #ifdef CONFIG_VIDEO_BPP16
378 uint16_t *dst = line;
380 for (i = 0; i < pixels; i++)
385 #ifdef CONFIG_VIDEO_BPP32
387 uint32_t *dst = line;
389 for (i = 0; i < pixels; i++)
397 line += vid_priv->line_length;
399 ret = vidconsole_sync_copy(dev, start, line);
407 * console_truetype_backspace() - Handle a backspace operation
409 * This clears the previous character so that the console looks as if it had
412 * @dev: Device to update
413 * @return 0 if OK, -ENOSYS if not supported
415 static int console_truetype_backspace(struct udevice *dev)
417 struct vidconsole_priv *vc_priv = dev_get_uclass_priv(dev);
418 struct console_tt_priv *priv = dev_get_priv(dev);
419 struct udevice *vid_dev = dev->parent;
420 struct video_priv *vid_priv = dev_get_uclass_priv(vid_dev);
421 struct pos_info *pos;
425 * This indicates a very strange error higher in the stack. The caller
426 * has sent out n character and n + 1 backspaces.
431 /* Pop the last cursor position off the stack */
432 pos = &priv->pos[--priv->pos_ptr];
435 * Figure out the end position for clearing. Normlly it is the current
436 * cursor position, but if we are clearing a character on the previous
437 * line, we clear from the end of the line.
439 if (pos->ypos == vc_priv->ycur)
440 xend = VID_TO_PIXEL(vc_priv->xcur_frac);
442 xend = vid_priv->xsize;
444 console_truetype_erase(dev, VID_TO_PIXEL(pos->xpos_frac), pos->ypos,
445 xend, pos->ypos + vc_priv->y_charsize,
446 vid_priv->colour_bg);
448 /* Move the cursor back to where it was when we pushed this record */
449 vc_priv->xcur_frac = pos->xpos_frac;
450 vc_priv->ycur = pos->ypos;
455 static int console_truetype_entry_start(struct udevice *dev)
457 struct console_tt_priv *priv = dev_get_priv(dev);
459 /* A new input line has start, so clear our history */
466 * Provides a list of fonts which can be obtained at run-time in U-Boot. These
467 * are compiled in by the Makefile.
469 * At present there is no mechanism to select a particular font - the first
470 * one found is the one that is used. But the build system and the code here
471 * supports multiple fonts, which may be useful for certain firmware screens.
479 #define FONT_DECL(_name) \
480 extern u8 __ttf_ ## _name ## _begin[]; \
481 extern u8 __ttf_ ## _name ## _end[];
483 #define FONT_ENTRY(_name) { \
485 .begin = __ttf_ ## _name ## _begin, \
486 .end = __ttf_ ## _name ## _end, \
489 FONT_DECL(nimbus_sans_l_regular);
490 FONT_DECL(ankacoder_c75_r);
491 FONT_DECL(rufscript010);
492 FONT_DECL(cantoraone_regular);
494 static struct font_info font_table[] = {
495 #ifdef CONFIG_CONSOLE_TRUETYPE_NIMBUS
496 FONT_ENTRY(nimbus_sans_l_regular),
498 #ifdef CONFIG_CONSOLE_TRUETYPE_ANKACODER
499 FONT_ENTRY(ankacoder_c75_r),
501 #ifdef CONFIG_CONSOLE_TRUETYPE_RUFSCRIPT
502 FONT_ENTRY(rufscript010),
504 #ifdef CONFIG_CONSOLE_TRUETYPE_CANTORAONE
505 FONT_ENTRY(cantoraone_regular),
510 #define FONT_BEGIN(name) __ttf_ ## name ## _begin
511 #define FONT_END(name) __ttf_ ## name ## _end
512 #define FONT_IS_VALID(name) (abs(FONT_END(name) - FONT_BEGIN) > 4)
515 * console_truetype_find_font() - Find a suitable font
517 * This searched for the first available font.
519 * @return pointer to the font, or NULL if none is found
521 static u8 *console_truetype_find_font(void)
523 struct font_info *tab;
525 for (tab = font_table; tab->begin; tab++) {
526 if (abs(tab->begin - tab->end) > 4) {
527 debug("%s: Font '%s', at %p, size %lx\n", __func__,
528 tab->name, tab->begin,
529 (ulong)(tab->end - tab->begin));
537 static int console_truetype_probe(struct udevice *dev)
539 struct vidconsole_priv *vc_priv = dev_get_uclass_priv(dev);
540 struct console_tt_priv *priv = dev_get_priv(dev);
541 struct udevice *vid_dev = dev->parent;
542 struct video_priv *vid_priv = dev_get_uclass_priv(vid_dev);
543 stbtt_fontinfo *font = &priv->font;
546 debug("%s: start\n", __func__);
547 if (vid_priv->font_size)
548 priv->font_size = vid_priv->font_size;
550 priv->font_size = CONFIG_CONSOLE_TRUETYPE_SIZE;
551 priv->font_data = console_truetype_find_font();
552 if (!priv->font_data) {
553 debug("%s: Could not find any fonts\n", __func__);
557 vc_priv->x_charsize = priv->font_size;
558 vc_priv->y_charsize = priv->font_size;
559 vc_priv->xstart_frac = VID_TO_POS(2);
560 vc_priv->cols = vid_priv->xsize / priv->font_size;
561 vc_priv->rows = vid_priv->ysize / priv->font_size;
562 vc_priv->tab_width_frac = VID_TO_POS(priv->font_size) * 8 / 2;
564 if (!stbtt_InitFont(font, priv->font_data, 0)) {
565 debug("%s: Font init failed\n", __func__);
569 /* Pre-calculate some things we will need regularly */
570 priv->scale = stbtt_ScaleForPixelHeight(font, priv->font_size);
571 stbtt_GetFontVMetrics(font, &ascent, 0, 0);
572 priv->baseline = (int)(ascent * priv->scale);
573 debug("%s: ready\n", __func__);
578 struct vidconsole_ops console_truetype_ops = {
579 .putc_xy = console_truetype_putc_xy,
580 .move_rows = console_truetype_move_rows,
581 .set_row = console_truetype_set_row,
582 .backspace = console_truetype_backspace,
583 .entry_start = console_truetype_entry_start,
586 U_BOOT_DRIVER(vidconsole_truetype) = {
587 .name = "vidconsole_tt",
588 .id = UCLASS_VIDEO_CONSOLE,
589 .ops = &console_truetype_ops,
590 .probe = console_truetype_probe,
591 .priv_auto_alloc_size = sizeof(struct console_tt_priv),