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