• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 */
tt_floor(double val)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 
tt_ceil(double val)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 
frac(double val)28 static double frac(double val)
29 {
30 	return val - tt_floor(val);
31 }
32 
tt_fabs(double x)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   */
tt_sqrt(double value)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 
console_truetype_set_row(struct udevice * dev,uint row,int clr)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 
console_truetype_move_rows(struct udevice * dev,uint rowdst,uint rowsrc,uint count)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 
console_truetype_putc_xy(struct udevice * dev,uint x,uint y,char ch)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 		default:
291 			free(data);
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  */
console_truetype_erase(struct udevice * dev,int xstart,int ystart,int xend,int yend,int clr)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  */
console_truetype_backspace(struct udevice * dev)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 
console_truetype_entry_start(struct udevice * dev)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 
447 FONT_DECL(nimbus_sans_l_regular);
448 FONT_DECL(ankacoder_c75_r);
449 FONT_DECL(rufscript010);
450 FONT_DECL(cantoraone_regular);
451 
452 static struct font_info font_table[] = {
453 #ifdef CONFIG_CONSOLE_TRUETYPE_NIMBUS
454 	FONT_ENTRY(nimbus_sans_l_regular),
455 #endif
456 #ifdef CONFIG_CONSOLE_TRUETYPE_ANKACODER
457 	FONT_ENTRY(ankacoder_c75_r),
458 #endif
459 #ifdef CONFIG_CONSOLE_TRUETYPE_RUFSCRIPT
460 	FONT_ENTRY(rufscript010),
461 #endif
462 #ifdef CONFIG_CONSOLE_TRUETYPE_CANTORAONE
463 	FONT_ENTRY(cantoraone_regular),
464 #endif
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  */
console_truetype_find_font(void)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 
console_truetype_probe(struct udevice * dev)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 };
551