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