--- /dev/null
+/*
+ * Copyright (c) 2016 Google, Inc
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+
+#include <common.h>
+#include <dm.h>
+#include <video.h>
+#include <video_console.h>
+
+/* Functions needed by stb_truetype.h */
+static int tt_floor(double val)
+{
+ if (val < 0)
+ return (int)(val - 0.999);
+
+ return (int)val;
+}
+
+static int tt_ceil(double val)
+{
+ if (val < 0)
+ return (int)val;
+
+ return (int)(val + 0.999);
+}
+
+static double frac(double val)
+{
+ return val - tt_floor(val);
+}
+
+static double tt_fabs(double x)
+{
+ return x < 0 ? -x : x;
+}
+
+ /*
+ * Simple square root algorithm. This is from:
+ * http://stackoverflow.com/questions/1623375/writing-your-own-square-root-function
+ * Written by Chihung Yu
+ * Creative Commons license
+ * http://creativecommons.org/licenses/by-sa/3.0/legalcode
+ * It has been modified to compile correctly, and for U-Boot style.
+ */
+static double tt_sqrt(double value)
+{
+ double lo = 1.0;
+ double hi = value;
+
+ while (hi - lo > 0.00001) {
+ double mid = lo + (hi - lo) / 2;
+
+ if (mid * mid - value > 0.00001)
+ hi = mid;
+ else
+ lo = mid;
+ }
+
+ return lo;
+}
+
+#define STBTT_ifloor tt_floor
+#define STBTT_iceil tt_ceil
+#define STBTT_fabs tt_fabs
+#define STBTT_sqrt tt_sqrt
+#define STBTT_malloc(size, u) ((void)(u), malloc(size))
+#define STBTT_free(size, u) ((void)(u), free(size))
+#define STBTT_assert(x)
+#define STBTT_strlen(x) strlen(x)
+#define STBTT_memcpy memcpy
+#define STBTT_memset memset
+
+#define STB_TRUETYPE_IMPLEMENTATION
+#include "stb_truetype.h"
+
+/**
+ * struct pos_info - Records a cursor position
+ *
+ * @xpos_frac: Fractional X position in pixels (multiplied by VID_FRAC_DIV)
+ * @ypos: Y position (pixels from the top)
+ */
+struct pos_info {
+ int xpos_frac;
+ int ypos;
+};
+
+/*
+ * Allow one for each character on the command line plus one for each newline.
+ * This is just an estimate, but it should not be exceeded.
+ */
+#define POS_HISTORY_SIZE (CONFIG_SYS_CBSIZE * 11 / 10)
+
+/**
+ * struct console_tt_priv - Private data for this driver
+ *
+ * @font_size: Vertical font size in pixels
+ * @font_data: Pointer to TrueType font file contents
+ * @font: TrueType font information for the current font
+ * @pos: List of cursor positions for each character written. This is
+ * used to handle backspace. We clear the frame buffer between
+ * the last position and the current position, thus erasing the
+ * last character. We record enough characters to go back to the
+ * start of the current command line.
+ * @pos_ptr: Current position in the position history
+ * @baseline: Pixel offset of the font's baseline from the cursor position.
+ * This is the 'ascent' of the font, scaled to pixel coordinates.
+ * It measures the distance from the baseline to the top of the
+ * font.
+ * @scale: Scale of the font. This is calculated from the pixel height
+ * of the font. It is used by the STB library to generate images
+ * of the correct size.
+ */
+struct console_tt_priv {
+ int font_size;
+ u8 *font_data;
+ stbtt_fontinfo font;
+ struct pos_info pos[POS_HISTORY_SIZE];
+ int pos_ptr;
+ int baseline;
+ double scale;
+};
+
+static int console_truetype_set_row(struct udevice *dev, uint row, int clr)
+{
+ struct video_priv *vid_priv = dev_get_uclass_priv(dev->parent);
+ struct console_tt_priv *priv = dev_get_priv(dev);
+ void *line;
+ int pixels = priv->font_size * vid_priv->line_length;
+ int i;
+
+ line = vid_priv->fb + row * priv->font_size * vid_priv->line_length;
+ switch (vid_priv->bpix) {
+#ifdef CONFIG_VIDEO_BPP8
+ case VIDEO_BPP8: {
+ uint8_t *dst = line;
+
+ for (i = 0; i < pixels; i++)
+ *dst++ = clr;
+ break;
+ }
+#endif
+#ifdef CONFIG_VIDEO_BPP16
+ case VIDEO_BPP16: {
+ uint16_t *dst = line;
+
+ for (i = 0; i < pixels; i++)
+ *dst++ = clr;
+ break;
+ }
+#endif
+#ifdef CONFIG_VIDEO_BPP32
+ case VIDEO_BPP32: {
+ uint32_t *dst = line;
+
+ for (i = 0; i < pixels; i++)
+ *dst++ = clr;
+ break;
+ }
+#endif
+ default:
+ return -ENOSYS;
+ }
+
+ return 0;
+}
+
+static int console_truetype_move_rows(struct udevice *dev, uint rowdst,
+ uint rowsrc, uint count)
+{
+ struct video_priv *vid_priv = dev_get_uclass_priv(dev->parent);
+ struct console_tt_priv *priv = dev_get_priv(dev);
+ void *dst;
+ void *src;
+ int i, diff;
+
+ dst = vid_priv->fb + rowdst * priv->font_size * vid_priv->line_length;
+ src = vid_priv->fb + rowsrc * priv->font_size * vid_priv->line_length;
+ memmove(dst, src, priv->font_size * vid_priv->line_length * count);
+
+ /* Scroll up our position history */
+ diff = (rowsrc - rowdst) * priv->font_size;
+ for (i = 0; i < priv->pos_ptr; i++)
+ priv->pos[i].ypos -= diff;
+
+ return 0;
+}
+
+static int console_truetype_putc_xy(struct udevice *dev, uint x, uint y,
+ char ch)
+{
+ struct vidconsole_priv *vc_priv = dev_get_uclass_priv(dev);
+ struct udevice *vid = dev->parent;
+ struct video_priv *vid_priv = dev_get_uclass_priv(vid);
+ struct console_tt_priv *priv = dev_get_priv(dev);
+ stbtt_fontinfo *font = &priv->font;
+ int width, height, xoff, yoff;
+ double xpos, x_shift;
+ int lsb;
+ int width_frac, linenum;
+ struct pos_info *pos;
+ u8 *bits, *data;
+ int advance;
+ void *line;
+ int row;
+
+ /* First get some basic metrics about this character */
+ stbtt_GetCodepointHMetrics(font, ch, &advance, &lsb);
+
+ /*
+ * First out our current X position in fractional pixels. If we wrote
+ * a character previously, using kerning to fine-tune the position of
+ * this character */
+ xpos = frac(VID_TO_PIXEL((double)x));
+ if (vc_priv->last_ch) {
+ xpos += priv->scale * stbtt_GetCodepointKernAdvance(font,
+ vc_priv->last_ch, ch);
+ }
+
+ /*
+ * Figure out where the cursor will move to after this character, and
+ * abort if we are out of space on this line. Also calculate the
+ * effective width of this character, which will be our return value:
+ * it dictates how much the cursor will move forward on the line.
+ */
+ x_shift = xpos - (double)tt_floor(xpos);
+ xpos += advance * priv->scale;
+ width_frac = (int)VID_TO_POS(xpos);
+ if (x + width_frac >= vc_priv->xsize_frac)
+ return -EAGAIN;
+
+ /* Write the current cursor position into history */
+ if (priv->pos_ptr < POS_HISTORY_SIZE) {
+ pos = &priv->pos[priv->pos_ptr];
+ pos->xpos_frac = vc_priv->xcur_frac;
+ pos->ypos = vc_priv->ycur;
+ priv->pos_ptr++;
+ }
+
+ /*
+ * Figure out how much past the start of a pixel we are, and pass this
+ * information into the render, which will return a 8-bit-per-pixel
+ * image of the character. For empty characters, like ' ', data will
+ * return NULL;
+ */
+ data = stbtt_GetCodepointBitmapSubpixel(font, priv->scale, priv->scale,
+ x_shift, 0, ch, &width, &height,
+ &xoff, &yoff);
+ if (!data)
+ return width_frac;
+
+ /* Figure out where to write the character in the frame buffer */
+ bits = data;
+ line = vid_priv->fb + y * vid_priv->line_length +
+ VID_TO_PIXEL(x) * VNBYTES(vid_priv->bpix);
+ linenum = priv->baseline + yoff;
+ if (linenum > 0)
+ line += linenum * vid_priv->line_length;
+
+ /*
+ * Write a row at a time, converting the 8bpp image into the colour
+ * depth of the display. We only expect white-on-black or the reverse
+ * so the code only handles this simple case.
+ */
+ for (row = 0; row < height; row++) {
+ switch (vid_priv->bpix) {
+#ifdef CONFIG_VIDEO_BPP16
+ case VIDEO_BPP16: {
+ uint16_t *dst = (uint16_t *)line + xoff;
+ int i;
+
+ for (i = 0; i < width; i++) {
+ int val = *bits;
+ int out;
+
+ if (vid_priv->colour_bg)
+ val = 255 - val;
+ out = val >> 3 |
+ (val >> 2) << 5 |
+ (val >> 3) << 11;
+ if (vid_priv->colour_fg)
+ *dst++ |= out;
+ else
+ *dst++ &= out;
+ bits++;
+ }
+ break;
+ }
+#endif
+ default:
+ return -ENOSYS;
+ }
+
+ line += vid_priv->line_length;
+ }
+ free(data);
+
+ return width_frac;
+}
+
+/**
+ * console_truetype_erase() - Erase a character
+ *
+ * This is used for backspace. We erase a square of the display within the
+ * given bounds.
+ *
+ * @dev: Device to update
+ * @xstart: X start position in pixels from the left
+ * @ystart: Y start position in pixels from the top
+ * @xend: X end position in pixels from the left
+ * @yend: Y end position in pixels from the top
+ * @clr: Value to write
+ * @return 0 if OK, -ENOSYS if the display depth is not supported
+ */
+static int console_truetype_erase(struct udevice *dev, int xstart, int ystart,
+ int xend, int yend, int clr)
+{
+ struct video_priv *vid_priv = dev_get_uclass_priv(dev->parent);
+ void *line;
+ int pixels = xend - xstart;
+ int row, i;
+
+ line = vid_priv->fb + ystart * vid_priv->line_length;
+ line += xstart * VNBYTES(vid_priv->bpix);
+ for (row = ystart; row < yend; row++) {
+ switch (vid_priv->bpix) {
+#ifdef CONFIG_VIDEO_BPP8
+ case VIDEO_BPP8: {
+ uint8_t *dst = line;
+
+ for (i = 0; i < pixels; i++)
+ *dst++ = clr;
+ break;
+ }
+#endif
+#ifdef CONFIG_VIDEO_BPP16
+ case VIDEO_BPP16: {
+ uint16_t *dst = line;
+
+ for (i = 0; i < pixels; i++)
+ *dst++ = clr;
+ break;
+ }
+#endif
+#ifdef CONFIG_VIDEO_BPP32
+ case VIDEO_BPP32: {
+ uint32_t *dst = line;
+
+ for (i = 0; i < pixels; i++)
+ *dst++ = clr;
+ break;
+ }
+#endif
+ default:
+ return -ENOSYS;
+ }
+ line += vid_priv->line_length;
+ }
+
+ return 0;
+}
+
+/**
+ * console_truetype_backspace() - Handle a backspace operation
+ *
+ * This clears the previous character so that the console looks as if it had
+ * not been entered.
+ *
+ * @dev: Device to update
+ * @return 0 if OK, -ENOSYS if not supported
+ */
+static int console_truetype_backspace(struct udevice *dev)
+{
+ struct vidconsole_priv *vc_priv = dev_get_uclass_priv(dev);
+ struct console_tt_priv *priv = dev_get_priv(dev);
+ struct udevice *vid_dev = dev->parent;
+ struct video_priv *vid_priv = dev_get_uclass_priv(vid_dev);
+ struct pos_info *pos;
+ int xend;
+
+ /*
+ * This indicates a very strange error higher in the stack. The caller
+ * has sent out n character and n + 1 backspaces.
+ */
+ if (!priv->pos_ptr)
+ return -ENOSYS;
+
+ /* Pop the last cursor position off the stack */
+ pos = &priv->pos[--priv->pos_ptr];
+
+ /*
+ * Figure out the end position for clearing. Normlly it is the current
+ * cursor position, but if we are clearing a character on the previous
+ * line, we clear from the end of the line.
+ */
+ if (pos->ypos == vc_priv->ycur)
+ xend = VID_TO_PIXEL(vc_priv->xcur_frac);
+ else
+ xend = vid_priv->xsize;
+
+ console_truetype_erase(dev, VID_TO_PIXEL(pos->xpos_frac), pos->ypos,
+ xend, pos->ypos + vc_priv->y_charsize,
+ vid_priv->colour_bg);
+
+ /* Move the cursor back to where it was when we pushed this record */
+ vc_priv->xcur_frac = pos->xpos_frac;
+ vc_priv->ycur = pos->ypos;
+
+ return 0;
+}
+
+static int console_truetype_entry_start(struct udevice *dev)
+{
+ struct console_tt_priv *priv = dev_get_priv(dev);
+
+ /* A new input line has start, so clear our history */
+ priv->pos_ptr = 0;
+
+ return 0;
+}
+
+/*
+ * Provides a list of fonts which can be obtained at run-time in U-Boot. These
+ * are compiled in by the Makefile.
+ *
+ * At present there is no mechanism to select a particular font - the first
+ * one found is the one that is used. But the build system and the code here
+ * supports multiple fonts, which may be useful for certain firmware screens.
+ */
+struct font_info {
+ char *name;
+ u8 *begin;
+ u8 *end;
+};
+
+#define FONT_DECL(_name) \
+ extern u8 __ttf_ ## _name ## _begin[]; \
+ extern u8 __ttf_ ## _name ## _end[];
+
+#define FONT_ENTRY(_name) { \
+ .name = #_name, \
+ .begin = __ttf_ ## _name ## _begin, \
+ .end = __ttf_ ## _name ## _end, \
+ }
+
+static struct font_info font_table[] = {
+ {} /* sentinel */
+};
+
+#define FONT_BEGIN(name) __ttf_ ## name ## _begin
+#define FONT_END(name) __ttf_ ## name ## _end
+#define FONT_IS_VALID(name) (abs(FONT_END(name) - FONT_BEGIN) > 4)
+
+/**
+ * console_truetype_find_font() - Find a suitable font
+ *
+ * This searched for the first available font.
+ *
+ * @return pointer to the font, or NULL if none is found
+ */
+static u8 *console_truetype_find_font(void)
+{
+ struct font_info *tab;
+
+ for (tab = font_table; tab->begin; tab++) {
+ if (abs(tab->begin - tab->end) > 4) {
+ debug("%s: Font '%s', at %p, size %lx\n", __func__,
+ tab->name, tab->begin,
+ (ulong)(tab->end - tab->begin));
+ return tab->begin;
+ }
+ }
+
+ return NULL;
+}
+
+static int console_truetype_probe(struct udevice *dev)
+{
+ struct vidconsole_priv *vc_priv = dev_get_uclass_priv(dev);
+ struct console_tt_priv *priv = dev_get_priv(dev);
+ struct udevice *vid_dev = dev->parent;
+ struct video_priv *vid_priv = dev_get_uclass_priv(vid_dev);
+ stbtt_fontinfo *font = &priv->font;
+ int ascent;
+
+ debug("%s: start\n", __func__);
+ if (vid_priv->font_size)
+ priv->font_size = vid_priv->font_size;
+ else
+ priv->font_size = CONFIG_CONSOLE_TRUETYPE_SIZE;
+ priv->font_data = console_truetype_find_font();
+ if (!priv->font_data) {
+ debug("%s: Could not find any fonts\n", __func__);
+ return -EBFONT;
+ }
+
+ vc_priv->x_charsize = priv->font_size;
+ vc_priv->y_charsize = priv->font_size;
+ vc_priv->xstart_frac = VID_TO_POS(2);
+ vc_priv->cols = vid_priv->xsize / priv->font_size;
+ vc_priv->rows = vid_priv->ysize / priv->font_size;
+ vc_priv->tab_width_frac = VID_TO_POS(priv->font_size) * 8 / 2;
+
+ if (!stbtt_InitFont(font, priv->font_data, 0)) {
+ debug("%s: Font init failed\n", __func__);
+ return -EPERM;
+ }
+
+ /* Pre-calculate some things we will need regularly */
+ priv->scale = stbtt_ScaleForPixelHeight(font, priv->font_size);
+ stbtt_GetFontVMetrics(font, &ascent, 0, 0);
+ priv->baseline = (int)(ascent * priv->scale);
+ debug("%s: ready\n", __func__);
+
+ return 0;
+}
+
+struct vidconsole_ops console_truetype_ops = {
+ .putc_xy = console_truetype_putc_xy,
+ .move_rows = console_truetype_move_rows,
+ .set_row = console_truetype_set_row,
+ .backspace = console_truetype_backspace,
+ .entry_start = console_truetype_entry_start,
+};
+
+U_BOOT_DRIVER(vidconsole_truetype) = {
+ .name = "vidconsole_tt",
+ .id = UCLASS_VIDEO_CONSOLE,
+ .ops = &console_truetype_ops,
+ .probe = console_truetype_probe,
+ .priv_auto_alloc_size = sizeof(struct console_tt_priv),
+};