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