de8b86bbacc79b9e64ae1a659ab1496248caeb77
[platform/kernel/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 <log.h>
9 #include <malloc.h>
10 #include <video.h>
11 #include <video_console.h>
12
13 /* Functions needed by stb_truetype.h */
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
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
30 static double frac(double val)
31 {
32         return val - tt_floor(val);
33 }
34
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   */
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
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
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
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                 case VIDEO_BPP8:
278                         if (IS_ENABLED(CONFIG_VIDEO_BPP8)) {
279                                 u8 *dst = 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;
289                                         if (vid_priv->colour_fg)
290                                                 *dst++ |= out;
291                                         else
292                                                 *dst++ &= out;
293                                         bits++;
294                                 }
295                                 end = dst;
296                         }
297                         break;
298 #ifdef CONFIG_VIDEO_BPP16
299                 case VIDEO_BPP16: {
300                         uint16_t *dst = (uint16_t *)line + xoff;
301                         int i;
302
303                         for (i = 0; i < width; i++) {
304                                 int val = *bits;
305                                 int out;
306
307                                 if (vid_priv->colour_bg)
308                                         val = 255 - val;
309                                 out = val >> 3 |
310                                         (val >> 2) << 5 |
311                                         (val >> 3) << 11;
312                                 if (vid_priv->colour_fg)
313                                         *dst++ |= out;
314                                 else
315                                         *dst++ &= out;
316                                 bits++;
317                         }
318                         end = dst;
319                         break;
320                 }
321 #endif
322 #ifdef CONFIG_VIDEO_BPP32
323                 case VIDEO_BPP32: {
324                         u32 *dst = (u32 *)line + xoff;
325                         int i;
326
327                         for (i = 0; i < width; i++) {
328                                 int val = *bits;
329                                 int out;
330
331                                 if (vid_priv->colour_bg)
332                                         val = 255 - val;
333                                 out = val | val << 8 | val << 16;
334                                 if (vid_priv->colour_fg)
335                                         *dst++ |= out;
336                                 else
337                                         *dst++ &= out;
338                                 bits++;
339                         }
340                         end = dst;
341                         break;
342                 }
343 #endif
344                 default:
345                         free(data);
346                         return -ENOSYS;
347                 }
348
349                 line += vid_priv->line_length;
350         }
351         ret = vidconsole_sync_copy(dev, start, line);
352         if (ret)
353                 return ret;
354         free(data);
355
356         return width_frac;
357 }
358
359 /**
360  * console_truetype_erase() - Erase a character
361  *
362  * This is used for backspace. We erase a square of the display within the
363  * given bounds.
364  *
365  * @dev:        Device to update
366  * @xstart:     X start position in pixels from the left
367  * @ystart:     Y start position in pixels from the top
368  * @xend:       X end position in pixels from the left
369  * @yend:       Y end position  in pixels from the top
370  * @clr:        Value to write
371  * @return 0 if OK, -ENOSYS if the display depth is not supported
372  */
373 static int console_truetype_erase(struct udevice *dev, int xstart, int ystart,
374                                   int xend, int yend, int clr)
375 {
376         struct video_priv *vid_priv = dev_get_uclass_priv(dev->parent);
377         void *start, *line;
378         int pixels = xend - xstart;
379         int row, i, ret;
380
381         start = vid_priv->fb + ystart * vid_priv->line_length;
382         start += xstart * VNBYTES(vid_priv->bpix);
383         line = start;
384         for (row = ystart; row < yend; row++) {
385                 switch (vid_priv->bpix) {
386 #ifdef CONFIG_VIDEO_BPP8
387                 case VIDEO_BPP8: {
388                         uint8_t *dst = line;
389
390                         for (i = 0; i < pixels; i++)
391                                 *dst++ = clr;
392                         break;
393                 }
394 #endif
395 #ifdef CONFIG_VIDEO_BPP16
396                 case VIDEO_BPP16: {
397                         uint16_t *dst = line;
398
399                         for (i = 0; i < pixels; i++)
400                                 *dst++ = clr;
401                         break;
402                 }
403 #endif
404 #ifdef CONFIG_VIDEO_BPP32
405                 case VIDEO_BPP32: {
406                         uint32_t *dst = line;
407
408                         for (i = 0; i < pixels; i++)
409                                 *dst++ = clr;
410                         break;
411                 }
412 #endif
413                 default:
414                         return -ENOSYS;
415                 }
416                 line += vid_priv->line_length;
417         }
418         ret = vidconsole_sync_copy(dev, start, line);
419         if (ret)
420                 return ret;
421
422         return 0;
423 }
424
425 /**
426  * console_truetype_backspace() - Handle a backspace operation
427  *
428  * This clears the previous character so that the console looks as if it had
429  * not been entered.
430  *
431  * @dev:        Device to update
432  * @return 0 if OK, -ENOSYS if not supported
433  */
434 static int console_truetype_backspace(struct udevice *dev)
435 {
436         struct vidconsole_priv *vc_priv = dev_get_uclass_priv(dev);
437         struct console_tt_priv *priv = dev_get_priv(dev);
438         struct udevice *vid_dev = dev->parent;
439         struct video_priv *vid_priv = dev_get_uclass_priv(vid_dev);
440         struct pos_info *pos;
441         int xend;
442
443         /*
444          * This indicates a very strange error higher in the stack. The caller
445          * has sent out n character and n + 1 backspaces.
446          */
447         if (!priv->pos_ptr)
448                 return -ENOSYS;
449
450         /* Pop the last cursor position off the stack */
451         pos = &priv->pos[--priv->pos_ptr];
452
453         /*
454          * Figure out the end position for clearing. Normally it is the current
455          * cursor position, but if we are clearing a character on the previous
456          * line, we clear from the end of the line.
457          */
458         if (pos->ypos == vc_priv->ycur)
459                 xend = VID_TO_PIXEL(vc_priv->xcur_frac);
460         else
461                 xend = vid_priv->xsize;
462
463         console_truetype_erase(dev, VID_TO_PIXEL(pos->xpos_frac), pos->ypos,
464                                xend, pos->ypos + vc_priv->y_charsize,
465                                vid_priv->colour_bg);
466
467         /* Move the cursor back to where it was when we pushed this record */
468         vc_priv->xcur_frac = pos->xpos_frac;
469         vc_priv->ycur = pos->ypos;
470
471         return 0;
472 }
473
474 static int console_truetype_entry_start(struct udevice *dev)
475 {
476         struct console_tt_priv *priv = dev_get_priv(dev);
477
478         /* A new input line has start, so clear our history */
479         priv->pos_ptr = 0;
480
481         return 0;
482 }
483
484 /*
485  * Provides a list of fonts which can be obtained at run-time in U-Boot. These
486  * are compiled in by the Makefile.
487  *
488  * At present there is no mechanism to select a particular font - the first
489  * one found is the one that is used. But the build system and the code here
490  * supports multiple fonts, which may be useful for certain firmware screens.
491  */
492 struct font_info {
493         char *name;
494         u8 *begin;
495         u8 *end;
496 };
497
498 #define FONT_DECL(_name) \
499         extern u8 __ttf_ ## _name ## _begin[]; \
500         extern u8 __ttf_ ## _name ## _end[];
501
502 #define FONT_ENTRY(_name)               { \
503         .name = #_name, \
504         .begin = __ttf_ ## _name ## _begin, \
505         .end = __ttf_ ## _name ## _end, \
506         }
507
508 FONT_DECL(nimbus_sans_l_regular);
509 FONT_DECL(ankacoder_c75_r);
510 FONT_DECL(rufscript010);
511 FONT_DECL(cantoraone_regular);
512
513 static struct font_info font_table[] = {
514 #ifdef CONFIG_CONSOLE_TRUETYPE_NIMBUS
515         FONT_ENTRY(nimbus_sans_l_regular),
516 #endif
517 #ifdef CONFIG_CONSOLE_TRUETYPE_ANKACODER
518         FONT_ENTRY(ankacoder_c75_r),
519 #endif
520 #ifdef CONFIG_CONSOLE_TRUETYPE_RUFSCRIPT
521         FONT_ENTRY(rufscript010),
522 #endif
523 #ifdef CONFIG_CONSOLE_TRUETYPE_CANTORAONE
524         FONT_ENTRY(cantoraone_regular),
525 #endif
526         {} /* sentinel */
527 };
528
529 #define FONT_BEGIN(name)        __ttf_ ## name ## _begin
530 #define FONT_END(name)          __ttf_ ## name ## _end
531 #define FONT_IS_VALID(name)     (abs(FONT_END(name) - FONT_BEGIN) > 4)
532
533 /**
534  * console_truetype_find_font() - Find a suitable font
535  *
536  * This searched for the first available font.
537  *
538  * @return pointer to the font, or NULL if none is found
539  */
540 static u8 *console_truetype_find_font(void)
541 {
542         struct font_info *tab;
543
544         for (tab = font_table; tab->begin; tab++) {
545                 if (abs(tab->begin - tab->end) > 4) {
546                         debug("%s: Font '%s', at %p, size %lx\n", __func__,
547                               tab->name, tab->begin,
548                               (ulong)(tab->end - tab->begin));
549                         return tab->begin;
550                 }
551         }
552
553         return NULL;
554 }
555
556 static int console_truetype_probe(struct udevice *dev)
557 {
558         struct vidconsole_priv *vc_priv = dev_get_uclass_priv(dev);
559         struct console_tt_priv *priv = dev_get_priv(dev);
560         struct udevice *vid_dev = dev->parent;
561         struct video_priv *vid_priv = dev_get_uclass_priv(vid_dev);
562         stbtt_fontinfo *font = &priv->font;
563         int ascent;
564
565         debug("%s: start\n", __func__);
566         if (vid_priv->font_size)
567                 priv->font_size = vid_priv->font_size;
568         else
569                 priv->font_size = CONFIG_CONSOLE_TRUETYPE_SIZE;
570         priv->font_data = console_truetype_find_font();
571         if (!priv->font_data) {
572                 debug("%s: Could not find any fonts\n", __func__);
573                 return -EBFONT;
574         }
575
576         vc_priv->x_charsize = priv->font_size;
577         vc_priv->y_charsize = priv->font_size;
578         vc_priv->xstart_frac = VID_TO_POS(2);
579         vc_priv->cols = vid_priv->xsize / priv->font_size;
580         vc_priv->rows = vid_priv->ysize / priv->font_size;
581         vc_priv->tab_width_frac = VID_TO_POS(priv->font_size) * 8 / 2;
582
583         if (!stbtt_InitFont(font, priv->font_data, 0)) {
584                 debug("%s: Font init failed\n", __func__);
585                 return -EPERM;
586         }
587
588         /* Pre-calculate some things we will need regularly */
589         priv->scale = stbtt_ScaleForPixelHeight(font, priv->font_size);
590         stbtt_GetFontVMetrics(font, &ascent, 0, 0);
591         priv->baseline = (int)(ascent * priv->scale);
592         debug("%s: ready\n", __func__);
593
594         return 0;
595 }
596
597 struct vidconsole_ops console_truetype_ops = {
598         .putc_xy        = console_truetype_putc_xy,
599         .move_rows      = console_truetype_move_rows,
600         .set_row        = console_truetype_set_row,
601         .backspace      = console_truetype_backspace,
602         .entry_start    = console_truetype_entry_start,
603 };
604
605 U_BOOT_DRIVER(vidconsole_truetype) = {
606         .name   = "vidconsole_tt",
607         .id     = UCLASS_VIDEO_CONSOLE,
608         .ops    = &console_truetype_ops,
609         .probe  = console_truetype_probe,
610         .priv_auto      = sizeof(struct console_tt_priv),
611 };