| // SPDX-License-Identifier: GPL-2.0+ | |
| /* | |
| * Copyright (c) 2016 Google, Inc | |
| */ | |
| #include <abuf.h> | |
| #include <dm.h> | |
| #include <log.h> | |
| #include <malloc.h> | |
| #include <spl.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; | |
| } | |
| static double tt_fmod(double x, double y) | |
| { | |
| double rem; | |
| if (y == 0.0) | |
| return 0.0; | |
| rem = x - (x / y) * y; | |
| return rem; | |
| } | |
| /* dummy implementation */ | |
| static double tt_pow(double x, double y) | |
| { | |
| return 0; | |
| } | |
| /* dummy implementation */ | |
| static double tt_cos(double val) | |
| { | |
| return 0; | |
| } | |
| /* dummy implementation */ | |
| static double tt_acos(double val) | |
| { | |
| return 0; | |
| } | |
| #define STBTT_ifloor tt_floor | |
| #define STBTT_iceil tt_ceil | |
| #define STBTT_fabs tt_fabs | |
| #define STBTT_sqrt tt_sqrt | |
| #define STBTT_pow tt_pow | |
| #define STBTT_fmod tt_fmod | |
| #define STBTT_cos tt_cos | |
| #define STBTT_acos tt_acos | |
| #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_metrics - Information about a font / size combination | |
| * | |
| * This caches various font metrics which are expensive to regenerate each time | |
| * the font size changes. There is one of these for each font / size combination | |
| * that is being used | |
| * | |
| * @font_name: Name of the font | |
| * @font_size: Vertical font size in pixels | |
| * @font_data: Pointer to TrueType font file contents | |
| * @font: TrueType font information for the current font | |
| * @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_metrics { | |
| const char *font_name; | |
| int font_size; | |
| const u8 *font_data; | |
| stbtt_fontinfo font; | |
| int baseline; | |
| double scale; | |
| }; | |
| /** | |
| * struct console_tt_priv - Private data for this driver | |
| * | |
| * @cur_met: Current metrics being used | |
| * @metrics: List metrics that can be used | |
| * @num_metrics: Number of available metrics | |
| * @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 | |
| */ | |
| struct console_tt_priv { | |
| struct console_tt_metrics *cur_met; | |
| struct console_tt_metrics metrics[CONFIG_CONSOLE_TRUETYPE_MAX_METRICS]; | |
| int num_metrics; | |
| struct pos_info pos[POS_HISTORY_SIZE]; | |
| int pos_ptr; | |
| }; | |
| /** | |
| * struct console_tt_store - Format used for save/restore of entry information | |
| * | |
| * @priv: Private data | |
| * @cur: Current cursor position | |
| */ | |
| struct console_tt_store { | |
| struct console_tt_priv priv; | |
| struct pos_info cur; | |
| }; | |
| 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); | |
| struct console_tt_metrics *met = priv->cur_met; | |
| void *end, *line; | |
| int ret; | |
| line = vid_priv->fb + row * met->font_size * vid_priv->line_length; | |
| end = line + met->font_size * vid_priv->line_length; | |
| switch (vid_priv->bpix) { | |
| case VIDEO_BPP8: { | |
| u8 *dst; | |
| if (IS_ENABLED(CONFIG_VIDEO_BPP8)) { | |
| for (dst = line; dst < (u8 *)end; ++dst) | |
| *dst = clr; | |
| } | |
| break; | |
| } | |
| case VIDEO_BPP16: { | |
| u16 *dst = line; | |
| if (IS_ENABLED(CONFIG_VIDEO_BPP16)) { | |
| for (dst = line; dst < (u16 *)end; ++dst) | |
| *dst = clr; | |
| } | |
| break; | |
| } | |
| case VIDEO_BPP32: { | |
| u32 *dst = line; | |
| if (IS_ENABLED(CONFIG_VIDEO_BPP32)) { | |
| for (dst = line; dst < (u32 *)end; ++dst) | |
| *dst = clr; | |
| } | |
| break; | |
| } | |
| default: | |
| return -ENOSYS; | |
| } | |
| ret = vidconsole_sync_copy(dev, line, end); | |
| if (ret) | |
| return ret; | |
| 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); | |
| struct console_tt_metrics *met = priv->cur_met; | |
| void *dst; | |
| void *src; | |
| int i, diff, ret; | |
| dst = vid_priv->fb + rowdst * met->font_size * vid_priv->line_length; | |
| src = vid_priv->fb + rowsrc * met->font_size * vid_priv->line_length; | |
| ret = vidconsole_memmove(dev, dst, src, met->font_size * | |
| vid_priv->line_length * count); | |
| if (ret) | |
| return ret; | |
| /* Scroll up our position history */ | |
| diff = (rowsrc - rowdst) * met->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, | |
| int cp) | |
| { | |
| 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); | |
| struct console_tt_metrics *met = priv->cur_met; | |
| stbtt_fontinfo *font = &met->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 *start, *end, *line; | |
| int row, ret; | |
| /* First get some basic metrics about this character */ | |
| stbtt_GetCodepointHMetrics(font, cp, &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 += met->scale * stbtt_GetCodepointKernAdvance(font, | |
| vc_priv->last_ch, cp); | |
| } | |
| /* | |
| * 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 * met->scale; | |
| width_frac = (int)VID_TO_POS(advance * met->scale); | |
| 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, met->scale, met->scale, | |
| x_shift, 0, cp, &width, &height, | |
| &xoff, &yoff); | |
| if (!data) | |
| return width_frac; | |
| /* Figure out where to write the character in the frame buffer */ | |
| bits = data; | |
| start = vid_priv->fb + y * vid_priv->line_length + | |
| VID_TO_PIXEL(x) * VNBYTES(vid_priv->bpix); | |
| linenum = met->baseline + yoff; | |
| if (linenum > 0) | |
| start += linenum * vid_priv->line_length; | |
| line = start; | |
| /* | |
| * 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) { | |
| case VIDEO_BPP8: | |
| if (IS_ENABLED(CONFIG_VIDEO_BPP8)) { | |
| u8 *dst = 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; | |
| if (vid_priv->colour_fg) | |
| *dst++ |= out; | |
| else | |
| *dst++ &= out; | |
| bits++; | |
| } | |
| end = dst; | |
| } | |
| break; | |
| case VIDEO_BPP16: { | |
| uint16_t *dst = (uint16_t *)line + xoff; | |
| int i; | |
| if (IS_ENABLED(CONFIG_VIDEO_BPP16)) { | |
| 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++; | |
| } | |
| end = dst; | |
| } | |
| break; | |
| } | |
| case VIDEO_BPP32: { | |
| u32 *dst = (u32 *)line + xoff; | |
| int i; | |
| if (IS_ENABLED(CONFIG_VIDEO_BPP32)) { | |
| for (i = 0; i < width; i++) { | |
| int val = *bits; | |
| int out; | |
| if (vid_priv->colour_bg) | |
| val = 255 - val; | |
| if (vid_priv->format == VIDEO_X2R10G10B10) | |
| out = val << 2 | val << 12 | val << 22; | |
| else | |
| out = val | val << 8 | val << 16; | |
| if (vid_priv->colour_fg) | |
| *dst++ |= out; | |
| else | |
| *dst++ &= out; | |
| bits++; | |
| } | |
| end = dst; | |
| } | |
| break; | |
| } | |
| default: | |
| free(data); | |
| return -ENOSYS; | |
| } | |
| line += vid_priv->line_length; | |
| } | |
| ret = vidconsole_sync_copy(dev, start, line); | |
| if (ret) | |
| return ret; | |
| free(data); | |
| return width_frac; | |
| } | |
| /** | |
| * 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. Normally 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; | |
| video_fill_part(vid_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, \ | |
| } | |
| FONT_DECL(nimbus_sans_l_regular); | |
| FONT_DECL(ankacoder_c75_r); | |
| FONT_DECL(rufscript010); | |
| FONT_DECL(cantoraone_regular); | |
| static struct font_info font_table[] = { | |
| #ifdef CONFIG_CONSOLE_TRUETYPE_NIMBUS | |
| FONT_ENTRY(nimbus_sans_l_regular), | |
| #endif | |
| #ifdef CONFIG_CONSOLE_TRUETYPE_ANKACODER | |
| FONT_ENTRY(ankacoder_c75_r), | |
| #endif | |
| #ifdef CONFIG_CONSOLE_TRUETYPE_RUFSCRIPT | |
| FONT_ENTRY(rufscript010), | |
| #endif | |
| #ifdef CONFIG_CONSOLE_TRUETYPE_CANTORAONE | |
| FONT_ENTRY(cantoraone_regular), | |
| #endif | |
| {} /* sentinel */ | |
| }; | |
| /** | |
| * font_valid() - Check if a font-table entry is valid | |
| * | |
| * Depending on available files in the build system, fonts may end up being | |
| * empty. | |
| * | |
| * @return true if the entry is valid | |
| */ | |
| static inline bool font_valid(struct font_info *tab) | |
| { | |
| return abs(tab->begin - tab->end) > 4; | |
| } | |
| /** | |
| * console_truetype_find_font() - Find a suitable font | |
| * | |
| * This searches for the first available font. | |
| * | |
| * Return: pointer to the font-table entry, or NULL if none is found | |
| */ | |
| static struct font_info *console_truetype_find_font(void) | |
| { | |
| struct font_info *tab; | |
| for (tab = font_table; tab->begin; tab++) { | |
| if (font_valid(tab)) { | |
| debug("%s: Font '%s', at %p, size %lx\n", __func__, | |
| tab->name, tab->begin, | |
| (ulong)(tab->end - tab->begin)); | |
| return tab; | |
| } | |
| } | |
| return NULL; | |
| } | |
| int console_truetype_get_font(struct udevice *dev, int seq, | |
| struct vidfont_info *info) | |
| { | |
| struct font_info *tab; | |
| int i; | |
| for (i = 0, tab = font_table; tab->begin; tab++, i++) { | |
| if (i == seq && font_valid(tab)) { | |
| info->name = tab->name; | |
| return 0; | |
| } | |
| } | |
| return -ENOENT; | |
| } | |
| /** | |
| * truetype_add_metrics() - Add a new font/size combination | |
| * | |
| * @dev: Video console device to update | |
| * @font_name: Name of font | |
| * @font_size: Size of the font (norminal pixel height) | |
| * @font_data: Pointer to the font data | |
| * @return 0 if OK, -EPERM if stbtt failed, -E2BIG if the the metrics table is | |
| * full | |
| */ | |
| static int truetype_add_metrics(struct udevice *dev, const char *font_name, | |
| uint font_size, const void *font_data) | |
| { | |
| struct console_tt_priv *priv = dev_get_priv(dev); | |
| struct console_tt_metrics *met; | |
| stbtt_fontinfo *font; | |
| int ascent; | |
| if (priv->num_metrics == CONFIG_CONSOLE_TRUETYPE_MAX_METRICS) | |
| return log_msg_ret("num", -E2BIG); | |
| met = &priv->metrics[priv->num_metrics]; | |
| met->font_name = font_name; | |
| met->font_size = font_size; | |
| met->font_data = font_data; | |
| font = &met->font; | |
| if (!stbtt_InitFont(font, font_data, 0)) { | |
| debug("%s: Font init failed\n", __func__); | |
| return -EPERM; | |
| } | |
| /* Pre-calculate some things we will need regularly */ | |
| met->scale = stbtt_ScaleForPixelHeight(font, font_size); | |
| stbtt_GetFontVMetrics(font, &ascent, 0, 0); | |
| met->baseline = (int)(ascent * met->scale); | |
| return priv->num_metrics++; | |
| } | |
| /** | |
| * find_metrics() - Find the metrics for a given font and size | |
| * | |
| * @dev: Video console device to update | |
| * @name: Name of font | |
| * @size: Size of the font (norminal pixel height) | |
| * @return metrics, if found, else NULL | |
| */ | |
| static struct console_tt_metrics *find_metrics(struct udevice *dev, | |
| const char *name, uint size) | |
| { | |
| struct console_tt_priv *priv = dev_get_priv(dev); | |
| int i; | |
| for (i = 0; i < priv->num_metrics; i++) { | |
| struct console_tt_metrics *met = &priv->metrics[i]; | |
| if (!strcmp(name, met->font_name) && met->font_size == size) | |
| return met; | |
| } | |
| return NULL; | |
| } | |
| static void select_metrics(struct udevice *dev, struct console_tt_metrics *met) | |
| { | |
| struct vidconsole_priv *vc_priv = dev_get_uclass_priv(dev); | |
| struct console_tt_priv *priv = dev_get_priv(dev); | |
| struct udevice *vid_dev = dev_get_parent(dev); | |
| struct video_priv *vid_priv = dev_get_uclass_priv(vid_dev); | |
| priv->cur_met = met; | |
| vc_priv->x_charsize = met->font_size; | |
| vc_priv->y_charsize = met->font_size; | |
| vc_priv->xstart_frac = VID_TO_POS(2); | |
| vc_priv->cols = vid_priv->xsize / met->font_size; | |
| vc_priv->rows = vid_priv->ysize / met->font_size; | |
| vc_priv->tab_width_frac = VID_TO_POS(met->font_size) * 8 / 2; | |
| } | |
| static int get_metrics(struct udevice *dev, const char *name, uint size, | |
| struct console_tt_metrics **metp) | |
| { | |
| struct console_tt_priv *priv = dev_get_priv(dev); | |
| struct console_tt_metrics *met; | |
| struct font_info *tab; | |
| if (name || size) { | |
| if (!size) | |
| size = CONFIG_CONSOLE_TRUETYPE_SIZE; | |
| if (!name) | |
| name = font_table->name; | |
| met = find_metrics(dev, name, size); | |
| if (!met) { | |
| for (tab = font_table; tab->begin; tab++) { | |
| if (font_valid(tab) && | |
| !strcmp(name, tab->name)) { | |
| int ret; | |
| ret = truetype_add_metrics(dev, | |
| tab->name, | |
| size, | |
| tab->begin); | |
| if (ret < 0) | |
| return log_msg_ret("add", ret); | |
| met = &priv->metrics[ret]; | |
| break; | |
| } | |
| } | |
| } | |
| if (!met) | |
| return log_msg_ret("find", -ENOENT); | |
| } else { | |
| /* Use the default font */ | |
| met = priv->metrics; | |
| } | |
| *metp = met; | |
| return 0; | |
| } | |
| static int truetype_select_font(struct udevice *dev, const char *name, | |
| uint size) | |
| { | |
| struct console_tt_metrics *met; | |
| int ret; | |
| ret = get_metrics(dev, name, size, &met); | |
| if (ret) | |
| return log_msg_ret("sel", ret); | |
| select_metrics(dev, met); | |
| return 0; | |
| } | |
| static int truetype_measure(struct udevice *dev, const char *name, uint size, | |
| const char *text, struct vidconsole_bbox *bbox) | |
| { | |
| struct console_tt_metrics *met; | |
| stbtt_fontinfo *font; | |
| int lsb, advance; | |
| const char *s; | |
| int width; | |
| int last; | |
| int ret; | |
| ret = get_metrics(dev, name, size, &met); | |
| if (ret) | |
| return log_msg_ret("sel", ret); | |
| bbox->valid = false; | |
| if (!*text) | |
| return 0; | |
| font = &met->font; | |
| width = 0; | |
| for (last = 0, s = text; *s; s++) { | |
| int ch = *s; | |
| /* Used kerning to fine-tune the position of this character */ | |
| if (last) | |
| width += stbtt_GetCodepointKernAdvance(font, last, ch); | |
| /* First get some basic metrics about this character */ | |
| stbtt_GetCodepointHMetrics(font, ch, &advance, &lsb); | |
| width += advance; | |
| last = ch; | |
| } | |
| bbox->valid = true; | |
| bbox->x0 = 0; | |
| bbox->y0 = 0; | |
| bbox->x1 = tt_ceil((double)width * met->scale); | |
| bbox->y1 = met->font_size; | |
| return 0; | |
| } | |
| static int truetype_nominal(struct udevice *dev, const char *name, uint size, | |
| uint num_chars, struct vidconsole_bbox *bbox) | |
| { | |
| struct console_tt_metrics *met; | |
| stbtt_fontinfo *font; | |
| int lsb, advance; | |
| int width; | |
| int ret; | |
| ret = get_metrics(dev, name, size, &met); | |
| if (ret) | |
| return log_msg_ret("sel", ret); | |
| font = &met->font; | |
| width = 0; | |
| /* First get some basic metrics about this character */ | |
| stbtt_GetCodepointHMetrics(font, 'W', &advance, &lsb); | |
| width = advance; | |
| bbox->valid = true; | |
| bbox->x0 = 0; | |
| bbox->y0 = 0; | |
| bbox->x1 = tt_ceil((double)width * num_chars * met->scale); | |
| bbox->y1 = met->font_size; | |
| return 0; | |
| } | |
| static int truetype_entry_save(struct udevice *dev, struct abuf *buf) | |
| { | |
| struct vidconsole_priv *vc_priv = dev_get_uclass_priv(dev); | |
| struct console_tt_priv *priv = dev_get_priv(dev); | |
| struct console_tt_store store; | |
| const uint size = sizeof(store); | |
| if (spl_phase() <= PHASE_SPL) | |
| return -ENOSYS; | |
| /* | |
| * store the whole priv structure as it is simpler that picking out | |
| * what we need | |
| */ | |
| if (!abuf_realloc(buf, size)) | |
| return log_msg_ret("sav", -ENOMEM); | |
| store.priv = *priv; | |
| store.cur.xpos_frac = vc_priv->xcur_frac; | |
| store.cur.ypos = vc_priv->ycur; | |
| memcpy(abuf_data(buf), &store, size); | |
| return 0; | |
| } | |
| static int truetype_entry_restore(struct udevice *dev, struct abuf *buf) | |
| { | |
| struct vidconsole_priv *vc_priv = dev_get_uclass_priv(dev); | |
| struct console_tt_priv *priv = dev_get_priv(dev); | |
| struct console_tt_store store; | |
| if (spl_phase() <= PHASE_SPL) | |
| return -ENOSYS; | |
| memcpy(&store, abuf_data(buf), sizeof(store)); | |
| vc_priv->xcur_frac = store.cur.xpos_frac; | |
| vc_priv->ycur = store.cur.ypos; | |
| priv->pos_ptr = store.priv.pos_ptr; | |
| memcpy(priv->pos, store.priv.pos, | |
| store.priv.pos_ptr * sizeof(struct pos_info)); | |
| return 0; | |
| } | |
| static int truetype_set_cursor_visible(struct udevice *dev, bool visible, | |
| uint x, uint y, uint index) | |
| { | |
| 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); | |
| struct console_tt_metrics *met = priv->cur_met; | |
| uint row, width, height, xoff; | |
| void *start, *line; | |
| uint out, val; | |
| int ret; | |
| if (spl_phase() <= PHASE_SPL) | |
| return -ENOSYS; | |
| if (!visible) | |
| return 0; | |
| /* | |
| * figure out where to place the cursor. This driver ignores the | |
| * passed-in values, since an entry_restore() must have been done before | |
| * calling this function. | |
| */ | |
| if (index < priv->pos_ptr) | |
| x = VID_TO_PIXEL(priv->pos[index].xpos_frac); | |
| else | |
| x = VID_TO_PIXEL(vc_priv->xcur_frac); | |
| y = vc_priv->ycur; | |
| height = met->font_size; | |
| xoff = 0; | |
| val = vid_priv->colour_bg ? 0 : 255; | |
| width = VIDCONSOLE_CURSOR_WIDTH; | |
| /* Figure out where to write the cursor in the frame buffer */ | |
| start = vid_priv->fb + y * vid_priv->line_length + | |
| x * VNBYTES(vid_priv->bpix); | |
| line = start; | |
| /* draw a vertical bar in the correct position */ | |
| for (row = 0; row < height; row++) { | |
| switch (vid_priv->bpix) { | |
| case VIDEO_BPP8: | |
| if (IS_ENABLED(CONFIG_VIDEO_BPP8)) { | |
| u8 *dst = line + xoff; | |
| int i; | |
| out = val; | |
| for (i = 0; i < width; i++) { | |
| if (vid_priv->colour_fg) | |
| *dst++ |= out; | |
| else | |
| *dst++ &= out; | |
| } | |
| } | |
| break; | |
| case VIDEO_BPP16: { | |
| u16 *dst = (u16 *)line + xoff; | |
| int i; | |
| if (IS_ENABLED(CONFIG_VIDEO_BPP16)) { | |
| for (i = 0; i < width; i++) { | |
| out = val >> 3 | | |
| (val >> 2) << 5 | | |
| (val >> 3) << 11; | |
| if (vid_priv->colour_fg) | |
| *dst++ |= out; | |
| else | |
| *dst++ &= out; | |
| } | |
| } | |
| break; | |
| } | |
| case VIDEO_BPP32: { | |
| u32 *dst = (u32 *)line + xoff; | |
| int i; | |
| if (IS_ENABLED(CONFIG_VIDEO_BPP32)) { | |
| for (i = 0; i < width; i++) { | |
| int out; | |
| if (vid_priv->format == VIDEO_X2R10G10B10) | |
| out = val << 2 | val << 12 | val << 22; | |
| else | |
| out = val | val << 8 | val << 16; | |
| if (vid_priv->colour_fg) | |
| *dst++ |= out; | |
| else | |
| *dst++ &= out; | |
| } | |
| } | |
| break; | |
| } | |
| default: | |
| return -ENOSYS; | |
| } | |
| line += vid_priv->line_length; | |
| } | |
| ret = vidconsole_sync_copy(dev, start, line); | |
| if (ret) | |
| return ret; | |
| return video_sync(vid, true); | |
| } | |
| const char *console_truetype_get_font_size(struct udevice *dev, uint *sizep) | |
| { | |
| struct console_tt_priv *priv = dev_get_priv(dev); | |
| struct console_tt_metrics *met = priv->cur_met; | |
| *sizep = met->font_size; | |
| return met->font_name; | |
| } | |
| static int console_truetype_probe(struct udevice *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 font_info *tab; | |
| uint font_size; | |
| int ret; | |
| debug("%s: start\n", __func__); | |
| if (vid_priv->font_size) | |
| font_size = vid_priv->font_size; | |
| else | |
| font_size = CONFIG_CONSOLE_TRUETYPE_SIZE; | |
| tab = console_truetype_find_font(); | |
| if (!tab) { | |
| debug("%s: Could not find any fonts\n", __func__); | |
| return -EBFONT; | |
| } | |
| ret = truetype_add_metrics(dev, tab->name, font_size, tab->begin); | |
| if (ret < 0) | |
| return log_msg_ret("add", ret); | |
| priv->cur_met = &priv->metrics[ret]; | |
| select_metrics(dev, &priv->metrics[ret]); | |
| 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, | |
| .get_font = console_truetype_get_font, | |
| .get_font_size = console_truetype_get_font_size, | |
| .select_font = truetype_select_font, | |
| .measure = truetype_measure, | |
| .nominal = truetype_nominal, | |
| .entry_save = truetype_entry_save, | |
| .entry_restore = truetype_entry_restore, | |
| .set_cursor_visible = truetype_set_cursor_visible | |
| }; | |
| U_BOOT_DRIVER(vidconsole_truetype) = { | |
| .name = "vidconsole_tt", | |
| .id = UCLASS_VIDEO_CONSOLE, | |
| .ops = &console_truetype_ops, | |
| .probe = console_truetype_probe, | |
| .priv_auto = sizeof(struct console_tt_priv), | |
| }; |