mirror of
https://github.com/ivoszbg/uniLoader.git
synced 2026-08-15 05:18:32 +00:00
For a while now, both simple-framebuffer and uart were hard-wired to the debug library. You could alternate between each or have both running, but the implementation was really ugly - ifdefs based on whether uart/simplefb are compiled in or not. As uniLoader has advanced enough to feature a sane-ish probing system for drivers, we can start experimenting with things like: - multiple framebuffers / UART devices - support for more complex peripherals (USB, storage) - multiple boot paths Hence registering output sinks as console devices, just like how u-boot and linux do, is a feature we should've had a long while ago. So let's begin working on that by decoupling framebuffer and uart from the "debug" interface in uni and rather allowing the registering of such sinks as "console" devices. With that, make the simple-framebuffer driver handle the debug level colors as well as the message assembling and writing to the framebuffer. That fully detaches it from the debug interface, as all that is required now is to invoke console_register() with the appropriate struct passed as an argument. As the drivers for output sinks are now responsible for the formatting of the messages, their set-up and writing, UART can be implemented as a sole driver as well. However, drivers register late in the boot process (after assembly, board early_initand other drivers with higher priority). With that in mind and all the leftover UART puts/c code in board files for several of the currently supported devices, introduce an early console path dedicated for debugging via UART. Looking at the currently supported devices, the only one requiring set-up before being able to output to UART is lucky7. For ease of use, make early_console_init weak, allowing it to be overrided by board files such as board-lucky7. Lastly, debug -> console, as that more correctly addresses the purpose of the library now.
207 lines
4.9 KiB
C
207 lines
4.9 KiB
C
// SPDX-License-Identifier: GPL-2.0-only
|
|
/*
|
|
* Copyright (c) 2022, Ivaylo Ivanov <ivo.ivanov.ivanov1@gmail.com>
|
|
* Copyright (c) 2022, Markuss Broks <markuss.broks@gmail.com>
|
|
* Copyright (c) 2022, Michael Srba <Michael.Srba@seznam.cz>
|
|
*/
|
|
|
|
#include <string.h>
|
|
#include <util.h>
|
|
#include <drivers/framework.h>
|
|
#include <lib/console.h>
|
|
#include <lib/debug.h>
|
|
#include <lib/video/font.h>
|
|
#include <lib/simplefb.h>
|
|
|
|
struct video_info *fb_info;
|
|
|
|
static void clean_fbmem(void *fb, int width, int height, int stride)
|
|
{
|
|
memset(fb, 0x0, (width * height * stride));
|
|
}
|
|
|
|
static void draw_pixel(volatile char *fb, int x, int y, int width, int stride,
|
|
color c)
|
|
{
|
|
// Check bounds to prevent drawing outside the framebuffer
|
|
if (x < 0 || x >= width || y < 0 || y >= fb_info->height)
|
|
return;
|
|
|
|
long int location = (x * stride) + (y * width * stride);
|
|
switch (fb_info->format) {
|
|
case FB_FORMAT_ARGB8888:
|
|
*(fb + location) = c.b;
|
|
*(fb + location + 1) = c.g;
|
|
*(fb + location + 2) = c.r;
|
|
*(fb + location + 3) = c.a;
|
|
break;
|
|
case FB_FORMAT_ABGR8888:
|
|
*(fb + location) = c.r;
|
|
*(fb + location + 1) = c.g;
|
|
*(fb + location + 2) = c.b;
|
|
*(fb + location + 3) = c.a;
|
|
break;
|
|
case FB_FORMAT_BGRA8888:
|
|
*(fb + location) = c.a;
|
|
*(fb + location + 1) = c.r;
|
|
*(fb + location + 2) = c.g;
|
|
*(fb + location + 3) = c.b;
|
|
break;
|
|
case FB_FORMAT_RGB888:
|
|
*(fb + location) = c.b;
|
|
*(fb + location + 1) = c.g;
|
|
*(fb + location + 2) = c.r;
|
|
break;
|
|
case FB_FORMAT_RGB565:
|
|
*(volatile unsigned short *)(fb + location) = ((c.r >> 3) << 11) |
|
|
((c.g >> 2) << 5) |
|
|
(c.b >> 3);
|
|
break;
|
|
}
|
|
}
|
|
|
|
#define SCALED_FONTW (FONTW * fb_info->scale_f)
|
|
#define SCALED_FONTH (FONTH * fb_info->scale_f)
|
|
|
|
int get_font_scale_factor()
|
|
{
|
|
if (!fb_info)
|
|
return 1;
|
|
|
|
if (fb_info->scale) {
|
|
return fb_info->scale;
|
|
}
|
|
|
|
/* integer sqrt inline */
|
|
int n = fb_info->width * fb_info->width + fb_info->height * fb_info->height;
|
|
int x = n;
|
|
int y = (x + 1) / 2;
|
|
|
|
while (y < x) {
|
|
x = y;
|
|
y = (x + n / x) / 2;
|
|
}
|
|
|
|
/* determine scale factor from pseudo-diagonal */
|
|
int scale = x / 750;
|
|
|
|
/* clamp the result */
|
|
if (scale < 1)
|
|
scale = 1;
|
|
|
|
return scale;
|
|
}
|
|
|
|
void __simplefb_raw_print(const char *text, int text_x, int text_y,
|
|
color text_color)
|
|
{
|
|
if (!fb_info) return;
|
|
|
|
int l = strlen(text);
|
|
int current_x = text_x;
|
|
int current_y = text_y;
|
|
|
|
static int last_y = 0;
|
|
if (last_y == 0) {
|
|
last_y = text_y;
|
|
} else {
|
|
current_y = last_y;
|
|
}
|
|
|
|
int max_x = fb_info->width - SCALED_FONTW;
|
|
|
|
for (int i = 0; i < l; i++) {
|
|
// Special characters handling
|
|
switch (text[i]) {
|
|
case '\n':
|
|
current_x = text_x;
|
|
current_y += SCALED_FONTH;
|
|
break;
|
|
case '\r':
|
|
current_x = text_x;
|
|
break;
|
|
}
|
|
|
|
// Non-printable chars
|
|
if (text[i] < 32)
|
|
continue;
|
|
|
|
if (current_x > max_x) {
|
|
current_x = text_x;
|
|
current_y += SCALED_FONTH;
|
|
}
|
|
|
|
if (current_y >= fb_info->height - SCALED_FONTH) {
|
|
clean_fbmem((char*)fb_info->address, fb_info->width, fb_info->height, fb_info->stride);
|
|
current_x = text_x;
|
|
current_y = FB_TEXT_TOP_PADDING;
|
|
}
|
|
|
|
int ix = font_index(text[i]);
|
|
unsigned char *img = letters[ix];
|
|
|
|
// Draw the character as a scaled bitmap
|
|
for (int y = 0; y < FONTH; y++) {
|
|
unsigned char b = img[y];
|
|
|
|
for (int x = 0; x < FONTW; x++) {
|
|
if (((b << x) & 0b10000000) > 0) {
|
|
for (int dy = 0; dy < fb_info->scale_f; dy++) {
|
|
for (int dx = 0; dx < fb_info->scale_f; dx++) {
|
|
draw_pixel(fb_info->address, current_x + x * fb_info->scale_f + dx,
|
|
current_y + y * fb_info->scale_f + dy,
|
|
fb_info->width, fb_info->stride, text_color);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
current_x += SCALED_FONTW;
|
|
}
|
|
last_y = current_y;
|
|
}
|
|
|
|
static const color level_color[] = {
|
|
[KERN_EMERG] = {255, 0, 0, 255},
|
|
[KERN_ALERT] = {255, 165, 0, 255},
|
|
[KERN_CRIT] = {255, 255, 0, 255},
|
|
[KERN_ERR] = {255, 0, 0, 255},
|
|
[KERN_WARNING] = {255, 255, 0, 255},
|
|
[KERN_NOTICE] = { 0, 255, 0, 255},
|
|
[KERN_INFO] = {255, 192, 0, 255},
|
|
[KERN_DEBUG] = {128, 128, 128, 255},
|
|
};
|
|
static const color body_color = {128, 128, 128, 255};
|
|
|
|
static void simplefb_console_write(int level, const char *prefix, const char *msg)
|
|
{
|
|
color pc = (level >= 0 && level < (int)ARRAY_SIZE(level_color)) ?
|
|
level_color[level] : (color){255, 255, 255, 255};
|
|
int prefix_width = strlen(prefix) * FONTW * get_font_scale_factor();
|
|
|
|
__simplefb_raw_print(prefix, 0, FB_TEXT_TOP_PADDING, pc);
|
|
__simplefb_raw_print(msg, prefix_width, FB_TEXT_TOP_PADDING, body_color);
|
|
}
|
|
|
|
static const struct console simplefb_console = {
|
|
.write = simplefb_console_write,
|
|
};
|
|
|
|
static int simplefb_probe(void *data)
|
|
{
|
|
fb_info = data;
|
|
|
|
clean_fbmem((char*)fb_info->address, fb_info->width, fb_info->height,
|
|
fb_info->stride);
|
|
|
|
fb_info->scale_f = get_font_scale_factor();
|
|
|
|
console_register(&simplefb_console);
|
|
printk(KERN_INFO, "simplefb: ready (%dx%d)\n",
|
|
fb_info->width, fb_info->height);
|
|
|
|
return 0;
|
|
}
|
|
|
|
DRIVER_REGISTER("simplefb", simplefb_probe);
|