Home | History | Annotate | Download | only in video
      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 		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  */
    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 
    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  */
    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 };
    551