Add early console support for generic linear framebuffer devices.
This driver supports probing from cmdline early parameters
or from the device-tree using information in simple-framebuffer node.
The EFI functionality should be retained in whole.
The driver was disabled on ARM because of a bug in early_ioremap
implementation on ARM and on IA64 because of lack of early_memremap_prot.
Signed-off-by: Markuss Broks <markuss.broks@gmail.com>
---
.../admin-guide/kernel-parameters.txt | 12 +-
MAINTAINERS | 5 +
drivers/firmware/efi/Kconfig | 7 +-
drivers/firmware/efi/Makefile | 1 -
drivers/firmware/efi/earlycon.c | 246 --------------
drivers/video/console/Kconfig | 11 +
drivers/video/console/Makefile | 1 +
drivers/video/console/earlycon.c | 305 ++++++++++++++++++
8 files changed, 332 insertions(+), 256 deletions(-)
delete mode 100644 drivers/firmware/efi/earlycon.c
create mode 100644 drivers/video/console/earlycon.c
diff --git a/Documentation/admin-guide/kernel-parameters.txt b/Documentation/admin-guide/kernel-parameters.txt
index 8090130b544b0701237a7b657a29c83c000a60f4..bccb1ac8978eb5cf7e2bb20834b1881b27040666 100644
--- a/Documentation/admin-guide/kernel-parameters.txt
+++ b/Documentation/admin-guide/kernel-parameters.txt
@@ -1281,12 +1281,9 @@
specified address. The serial port must already be
setup and configured. Options are not yet supported.
- efifb,[options]
+ efifb
Start an early, unaccelerated console on the EFI
- memory mapped framebuffer (if available). On cache
- coherent non-x86 systems that use system memory for
- the framebuffer, pass the 'ram' option so that it is
- mapped with the correct attributes.
+ memory mapped framebuffer (if available).
linflex,<addr>
Use early console provided by Freescale LINFlexD UART
@@ -1294,6 +1291,11 @@
address must be provided, and the serial port must
already be setup and configured.
+ simplefb,<addr>,<width>x<height>x<bpp>
+ Use early console with simple framebuffer that is
+ pre-initialized by firmware. A valid base address,
+ width, height and pixel size must be provided.
+
earlyprintk= [X86,SH,ARM,M68k,S390]
earlyprintk=vga
earlyprintk=sclp
diff --git a/MAINTAINERS b/MAINTAINERS
index 1fc9ead83d2aa3e60ccc4cfa8ee16df09ef579bf..af8b8e289483b6a264d477145061bd0e0ba34a25 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -7033,6 +7033,11 @@ Q: http://patchwork.linuxtv.org/project/linux-media/list/
T: git git://linuxtv.org/anttip/media_tree.git
F: drivers/media/tuners/e4000*
+EARLY CONSOLE FRAMEBUFFER DRIVER
+M: Markuss Broks <markuss.broks@gmail.com>
+S: Maintained
+F: drivers/video/console/earlycon.c
+
EARTH_PT1 MEDIA DRIVER
M: Akihiro Tsukada <tskd08@gmail.com>
L: linux-media@vger.kernel.org
diff --git a/drivers/firmware/efi/Kconfig b/drivers/firmware/efi/Kconfig
index 7aa4717cdcac46f91dd202f868c463388eb02735..ea76ccfb9bcd8ba44ddca06052eaa442ed6c30f7 100644
--- a/drivers/firmware/efi/Kconfig
+++ b/drivers/firmware/efi/Kconfig
@@ -259,10 +259,9 @@ config EFI_DISABLE_PCI_DMA
may be used to override this option.
config EFI_EARLYCON
- def_bool y
- depends on SERIAL_EARLYCON && !ARM && !IA64
- select FONT_SUPPORT
- select ARCH_USE_MEMREMAP_PROT
+ bool "EFI early console support"
+ select FB_EARLYCON
+ default y
config EFI_CUSTOM_SSDT_OVERLAYS
bool "Load custom ACPI SSDT overlay from an EFI variable"
diff --git a/drivers/firmware/efi/Makefile b/drivers/firmware/efi/Makefile
index c02ff25dd47707090a2ab86ee4f330e467f878f5..64eea61fbb43d76ec2d5416d467dfbb9aa21bda0 100644
--- a/drivers/firmware/efi/Makefile
+++ b/drivers/firmware/efi/Makefile
@@ -44,6 +44,5 @@ obj-$(CONFIG_ARM64) += $(arm-obj-y)
riscv-obj-$(CONFIG_EFI) := efi-init.o riscv-runtime.o
obj-$(CONFIG_RISCV) += $(riscv-obj-y)
obj-$(CONFIG_EFI_CAPSULE_LOADER) += capsule-loader.o
-obj-$(CONFIG_EFI_EARLYCON) += earlycon.o
obj-$(CONFIG_UEFI_CPER_ARM) += cper-arm.o
obj-$(CONFIG_UEFI_CPER_X86) += cper-x86.o
diff --git a/drivers/firmware/efi/earlycon.c b/drivers/firmware/efi/earlycon.c
deleted file mode 100644
index a52236e11e5f73ddea5bb1f42ca2ca7c42425dab..0000000000000000000000000000000000000000
--- a/drivers/firmware/efi/earlycon.c
+++ /dev/null
@@ -1,246 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-/*
- * Copyright (C) 2013 Intel Corporation; author Matt Fleming
- */
-
-#include <linux/console.h>
-#include <linux/efi.h>
-#include <linux/font.h>
-#include <linux/io.h>
-#include <linux/kernel.h>
-#include <linux/serial_core.h>
-#include <linux/screen_info.h>
-
-#include <asm/early_ioremap.h>
-
-static const struct console *earlycon_console __initdata;
-static const struct font_desc *font;
-static u32 efi_x, efi_y;
-static u64 fb_base;
-static bool fb_wb;
-static void *efi_fb;
-
-/*
- * EFI earlycon needs to use early_memremap() to map the framebuffer.
- * But early_memremap() is not usable for 'earlycon=efifb keep_bootcon',
- * memremap() should be used instead. memremap() will be available after
- * paging_init() which is earlier than initcall callbacks. Thus adding this
- * early initcall function early_efi_map_fb() to map the whole EFI framebuffer.
- */
-static int __init efi_earlycon_remap_fb(void)
-{
- /* bail if there is no bootconsole or it has been disabled already */
- if (!earlycon_console || !(earlycon_console->flags & CON_ENABLED))
- return 0;
-
- efi_fb = memremap(fb_base, screen_info.lfb_size,
- fb_wb ? MEMREMAP_WB : MEMREMAP_WC);
-
- return efi_fb ? 0 : -ENOMEM;
-}
-early_initcall(efi_earlycon_remap_fb);
-
-static int __init efi_earlycon_unmap_fb(void)
-{
- /* unmap the bootconsole fb unless keep_bootcon has left it enabled */
- if (efi_fb && !(earlycon_console->flags & CON_ENABLED))
- memunmap(efi_fb);
- return 0;
-}
-late_initcall(efi_earlycon_unmap_fb);
-
-static __ref void *efi_earlycon_map(unsigned long start, unsigned long len)
-{
- pgprot_t fb_prot;
-
- if (efi_fb)
- return efi_fb + start;
-
- fb_prot = fb_wb ? PAGE_KERNEL : pgprot_writecombine(PAGE_KERNEL);
- return early_memremap_prot(fb_base + start, len, pgprot_val(fb_prot));
-}
-
-static __ref void efi_earlycon_unmap(void *addr, unsigned long len)
-{
- if (efi_fb)
- return;
-
- early_memunmap(addr, len);
-}
-
-static void efi_earlycon_clear_scanline(unsigned int y)
-{
- unsigned long *dst;
- u16 len;
-
- len = screen_info.lfb_linelength;
- dst = efi_earlycon_map(y*len, len);
- if (!dst)
- return;
-
- memset(dst, 0, len);
- efi_earlycon_unmap(dst, len);
-}
-
-static void efi_earlycon_scroll_up(void)
-{
- unsigned long *dst, *src;
- u16 len;
- u32 i, height;
-
- len = screen_info.lfb_linelength;
- height = screen_info.lfb_height;
-
- for (i = 0; i < height - font->height; i++) {
- dst = efi_earlycon_map(i*len, len);
- if (!dst)
- return;
-
- src = efi_earlycon_map((i + font->height) * len, len);
- if (!src) {
- efi_earlycon_unmap(dst, len);
- return;
- }
-
- memmove(dst, src, len);
-
- efi_earlycon_unmap(src, len);
- efi_earlycon_unmap(dst, len);
- }
-}
-
-static void efi_earlycon_write_char(u32 *dst, unsigned char c, unsigned int h)
-{
- const u32 color_black = 0x00000000;
- const u32 color_white = 0x00ffffff;
- const u8 *src;
- int m, n, bytes;
- u8 x;
-
- bytes = BITS_TO_BYTES(font->width);
- src = font->data + c * font->height * bytes + h * bytes;
-
- for (m = 0; m < font->width; m++) {
- n = m % 8;
- x = *(src + m / 8);
- if ((x >> (7 - n)) & 1)
- *dst = color_white;
- else
- *dst = color_black;
- dst++;
- }
-}
-
-static void
-efi_earlycon_write(struct console *con, const char *str, unsigned int num)
-{
- struct screen_info *si;
- unsigned int len;
- const char *s;
- void *dst;
-
- si = &screen_info;
- len = si->lfb_linelength;
-
- while (num) {
- unsigned int linemax;
- unsigned int h, count = 0;
-
- for (s = str; *s && *s != '\n'; s++) {
- if (count == num)
- break;
- count++;
- }
-
- linemax = (si->lfb_width - efi_x) / font->width;
- if (count > linemax)
- count = linemax;
-
- for (h = 0; h < font->height; h++) {
- unsigned int n, x;
-
- dst = efi_earlycon_map((efi_y + h) * len, len);
- if (!dst)
- return;
-
- s = str;
- n = count;
- x = efi_x;
-
- while (n-- > 0) {
- efi_earlycon_write_char(dst + x*4, *s, h);
- x += font->width;
- s++;
- }
-
- efi_earlycon_unmap(dst, len);
- }
-
- num -= count;
- efi_x += count * font->width;
- str += count;
-
- if (num > 0 && *s == '\n') {
- efi_x = 0;
- efi_y += font->height;
- str++;
- num--;
- }
-
- if (efi_x + font->width > si->lfb_width) {
- efi_x = 0;
- efi_y += font->height;
- }
-
- if (efi_y + font->height > si->lfb_height) {
- u32 i;
-
- efi_y -= font->height;
- efi_earlycon_scroll_up();
-
- for (i = 0; i < font->height; i++)
- efi_earlycon_clear_scanline(efi_y + i);
- }
- }
-}
-
-static int __init efi_earlycon_setup(struct earlycon_device *device,
- const char *opt)
-{
- struct screen_info *si;
- u16 xres, yres;
- u32 i;
-
- if (screen_info.orig_video_isVGA != VIDEO_TYPE_EFI)
- return -ENODEV;
-
- fb_base = screen_info.lfb_base;
- if (screen_info.capabilities & VIDEO_CAPABILITY_64BIT_BASE)
- fb_base |= (u64)screen_info.ext_lfb_base << 32;
-
- fb_wb = opt && !strcmp(opt, "ram");
-
- si = &screen_info;
- xres = si->lfb_width;
- yres = si->lfb_height;
-
- /*
- * efi_earlycon_write_char() implicitly assumes a framebuffer with
- * 32 bits per pixel.
- */
- if (si->lfb_depth != 32)
- return -ENODEV;
-
- font = get_default_font(xres, yres, -1, -1);
- if (!font)
- return -ENODEV;
-
- efi_y = rounddown(yres, font->height) - font->height;
- for (i = 0; i < (yres - efi_y) / font->height; i++)
- efi_earlycon_scroll_up();
-
- device->con->write = efi_earlycon_write;
- earlycon_console = device->con;
- return 0;
-}
-EARLYCON_DECLARE(efifb, efi_earlycon_setup);
diff --git a/drivers/video/console/Kconfig b/drivers/video/console/Kconfig
index 40c50fa2dd70c33a1549141b15e6cba721352d2d..8052507e058fce37f5a51058e58ae2eb10d9669a 100644
--- a/drivers/video/console/Kconfig
+++ b/drivers/video/console/Kconfig
@@ -69,6 +69,17 @@ config DUMMY_CONSOLE_ROWS
monitor.
Select 25 if you use a 640x480 resolution by default.
+config FB_EARLYCON
+ bool "Generic framebuffer early console"
+ depends on SERIAL_EARLYCON && !ARM && !IA64
+ select FONT_SUPPORT
+ select ARCH_USE_MEMREMAP_PROT
+ help
+ Say Y here if you want early console support for firmware established
+ linear framebuffer. Unless you are using EFI framebuffer, you need to
+ specify framebuffer geometry and address in device-tree or in kernel
+ command line.
+
config FRAMEBUFFER_CONSOLE
bool "Framebuffer Console support"
depends on FB && !UML
diff --git a/drivers/video/console/Makefile b/drivers/video/console/Makefile
index db07b784bd2ccdcbffde933926ed5cee2bbbc7d4..7818faee587fc9c40b429617cfa224c0ccbc557c 100644
--- a/drivers/video/console/Makefile
+++ b/drivers/video/console/Makefile
@@ -9,4 +9,5 @@ obj-$(CONFIG_STI_CONSOLE) += sticon.o sticore.o
obj-$(CONFIG_VGA_CONSOLE) += vgacon.o
obj-$(CONFIG_MDA_CONSOLE) += mdacon.o
+obj-$(CONFIG_FB_EARLYCON) += earlycon.o
obj-$(CONFIG_FB_STI) += sticore.o
diff --git a/drivers/video/console/earlycon.c b/drivers/video/console/earlycon.c
new file mode 100644
index 0000000000000000000000000000000000000000..54436587e3db90034652dcc144669dca91b863d5
--- /dev/null
+++ b/drivers/video/console/earlycon.c
@@ -0,0 +1,305 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2013 Intel Corporation; author Matt Fleming
+ * Copyright (C) 2022 Markuss Broks <markuss.broks@gmail.com>
+ */
+
+#include <asm/early_ioremap.h>
+#include <linux/console.h>
+#include <linux/efi.h>
+#include <linux/font.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/of.h>
+#include <linux/of_fdt.h>
+#include <linux/serial_core.h>
+#include <linux/screen_info.h>
+
+struct fb_earlycon {
+ u32 x, y, curr_x, curr_y, depth, stride;
+ size_t size;
+ phys_addr_t phys_base;
+ void __iomem *virt_base;
+};
+
+static const struct console *earlycon_console __initconst;
+static struct fb_earlycon info;
+static const struct font_desc *font;
+
+static int __init simplefb_earlycon_remap_fb(void)
+{
+ unsigned long mapping;
+ /* bail if there is no bootconsole or it has been disabled already */
+ if (!earlycon_console || !(earlycon_console->flags & CON_ENABLED))
+ return 0;
+
+ if (region_intersects(info.phys_base, info.size,
+ IORESOURCE_SYSTEM_RAM, IORES_DESC_NONE) == REGION_INTERSECTS)
+ mapping = MEMREMAP_WB;
+ else
+ mapping = MEMREMAP_WC;
+
+ info.virt_base = memremap(info.phys_base, info.size, mapping);
+
+ return info.virt_base ? 0 : -ENOMEM;
+}
+early_initcall(simplefb_earlycon_remap_fb);
+
+static int __init simplefb_earlycon_unmap_fb(void)
+{
+ /* unmap the bootconsole fb unless keep_bootcon has left it enabled */
+ if (info.virt_base && !(earlycon_console->flags & CON_ENABLED))
+ memunmap(info.virt_base);
+ return 0;
+}
+late_initcall(simplefb_earlycon_unmap_fb);
+
+static __ref void *simplefb_earlycon_map(unsigned long start, unsigned long len)
+{
+ pgprot_t fb_prot;
+
+ if (info.virt_base)
+ return info.virt_base + start;
+
+ fb_prot = PAGE_KERNEL;
+ return early_memremap_prot(info.phys_base + start, len, pgprot_val(fb_prot));
+}
+
+static __ref void simplefb_earlycon_unmap(void *addr, unsigned long len)
+{
+ if (info.virt_base)
+ return;
+
+ early_memunmap(addr, len);
+}
+
+static void simplefb_earlycon_clear_scanline(unsigned int y)
+{
+ unsigned long *dst;
+ u16 len;
+
+ len = info.stride;
+ dst = simplefb_earlycon_map(y * len, len);
+ if (!dst)
+ return;
+
+ memset(dst, 0, len);
+ simplefb_earlycon_unmap(dst, len);
+}
+
+static void simplefb_earlycon_scroll_up(void)
+{
+ unsigned long *dst, *src;
+ u16 len;
+ u32 i, height;
+
+ len = info.stride;
+ height = info.y;
+
+ for (i = 0; i < height - font->height; i++) {
+ dst = simplefb_earlycon_map(i * len, len);
+ if (!dst)
+ return;
+
+ src = simplefb_earlycon_map((i + font->height) * len, len);
+ if (!src) {
+ simplefb_earlycon_unmap(dst, len);
+ return;
+ }
+
+ memmove(dst, src, len);
+
+ simplefb_earlycon_unmap(src, len);
+ simplefb_earlycon_unmap(dst, len);
+ }
+}
+
+static void simplefb_earlycon_write_char(u8 *dst, unsigned char c, unsigned int h)
+{
+ const u8 *src;
+ int m, n, bytes;
+ u8 x;
+
+ bytes = BITS_TO_BYTES(font->width);
+ src = font->data + c * font->height * bytes + h * bytes;
+
+ for (m = 0; m < font->width; m++) {
+ n = m % 8;
+ x = *(src + m / 8);
+ if ((x >> (7 - n)) & 1)
+ memset(dst, 0xff, (info.depth / 8));
+ else
+ memset(dst, 0, (info.depth / 8));
+ dst += (info.depth / 8);
+ }
+}
+
+static void
+simplefb_earlycon_write(struct console *con, const char *str, unsigned int num)
+{
+ unsigned int len;
+ const char *s;
+ void *dst;
+
+ len = info.stride;
+
+ while (num) {
+ unsigned int linemax;
+ unsigned int h, count = 0;
+
+ for (s = str; *s && *s != '\n'; s++) {
+ if (count == num)
+ break;
+ count++;
+ }
+
+ linemax = (info.x - info.curr_x) / font->width;
+ if (count > linemax)
+ count = linemax;
+
+ for (h = 0; h < font->height; h++) {
+ unsigned int n, x;
+
+ dst = simplefb_earlycon_map((info.curr_y + h) * len, len);
+ if (!dst)
+ return;
+
+ s = str;
+ n = count;
+ x = info.curr_x;
+
+ while (n-- > 0) {
+ simplefb_earlycon_write_char(dst + (x * 4), *s, h);
+ x += font->width;
+ s++;
+ }
+
+ simplefb_earlycon_unmap(dst, len);
+ }
+
+ num -= count;
+ info.curr_x += count * font->width;
+ str += count;
+
+ if (num > 0 && *s == '\n') {
+ info.curr_x = 0;
+ info.curr_y += font->height;
+ str++;
+ num--;
+ }
+
+ if (info.curr_x + font->width > info.x) {
+ info.curr_x = 0;
+ info.curr_y += font->height;
+ }
+
+ if (info.curr_y + font->height > info.y) {
+ u32 i;
+
+ info.curr_y -= font->height;
+ simplefb_earlycon_scroll_up();
+
+ for (i = 0; i < font->height; i++)
+ simplefb_earlycon_clear_scanline(info.curr_y + i);
+ }
+ }
+}
+
+static int __init simplefb_earlycon_setup_common(struct earlycon_device *device,
+ const char *opt)
+{
+ int i;
+
+ info.size = info.x * info.y * (info.depth / 8);
+
+ font = get_default_font(info.x, info.y, -1, -1);
+ if (!font)
+ return -ENODEV;
+
+ info.curr_y = rounddown(info.y, font->height) - font->height;
+ for (i = 0; i < (info.y - info.curr_y) / font->height; i++)
+ simplefb_earlycon_scroll_up();
+
+ device->con->write = simplefb_earlycon_write;
+ earlycon_console = device->con;
+ return 0;
+}
+
+static int __init simplefb_earlycon_setup(struct earlycon_device *device,
+ const char *opt)
+{
+ struct uart_port *port = &device->port;
+ int ret;
+
+ if (!port->mapbase)
+ return -ENODEV;
+
+ info.phys_base = port->mapbase;
+
+ ret = sscanf(device->options, "%ux%ux%u", &info.x, &info.y, &info.depth);
+ if (ret != 3)
+ return -ENODEV;
+
+ info.stride = info.x * (info.depth / 8);
+
+ return simplefb_earlycon_setup_common(device, opt);
+}
+
+EARLYCON_DECLARE(simplefb, simplefb_earlycon_setup);
+
+#ifdef CONFIG_EFI_EARLYCON
+static int __init simplefb_earlycon_setup_efi(struct earlycon_device *device,
+ const char *opt)
+{
+ if (screen_info.orig_video_isVGA != VIDEO_TYPE_EFI)
+ return -ENODEV;
+
+ info.phys_base = screen_info.lfb_base;
+ if (screen_info.capabilities & VIDEO_CAPABILITY_64BIT_BASE)
+ info.phys_base |= (u64)screen_info.ext_lfb_base << 32;
+
+ info.x = screen_info.lfb_width;
+ info.y = screen_info.lfb_height;
+ info.depth = screen_info.lfb_depth;
+ info.stride = screen_info.lfb_linelength;
+
+ return simplefb_earlycon_setup_common(device, opt);
+}
+
+EARLYCON_DECLARE(efifb, simplefb_earlycon_setup_efi);
+#endif
+
+#ifdef CONFIG_OF_EARLY_FLATTREE
+static int __init simplefb_earlycon_setup_of(struct earlycon_device *device,
+ const char *opt)
+{
+ struct uart_port *port = &device->port;
+ const __be32 *val;
+
+ if (!port->mapbase)
+ return -ENODEV;
+
+ info.phys_base = port->mapbase;
+
+ val = of_get_flat_dt_prop(device->offset, "width", NULL);
+ if (!val)
+ return -ENODEV;
+ info.x = be32_to_cpu(*val);
+
+ val = of_get_flat_dt_prop(device->offset, "height", NULL);
+ if (!val)
+ return -ENODEV;
+ info.y = be32_to_cpu(*val);
+
+ val = of_get_flat_dt_prop(device->offset, "stride", NULL);
+ if (!val)
+ return -ENODEV;
+ info.stride = be32_to_cpu(*val);
+ info.depth = (info.stride / info.x) * 8;
+
+ return simplefb_earlycon_setup_common(device, opt);
+}
+
+OF_EARLYCON_DECLARE(simplefb, "simple-framebuffer", simplefb_earlycon_setup_of);
+#endif
--
2.37.0
On Sat, Aug 06, 2022 at 07:32:24PM +0300, Markuss Broks wrote:
> Add early console support for generic linear framebuffer devices.
> This driver supports probing from cmdline early parameters
> or from the device-tree using information in simple-framebuffer node.
> The EFI functionality should be retained in whole.
> The driver was disabled on ARM because of a bug in early_ioremap
> implementation on ARM and on IA64 because of lack of early_memremap_prot.
>
> Signed-off-by: Markuss Broks <markuss.broks@gmail.com>
> ---
> .../admin-guide/kernel-parameters.txt | 12 +-
> MAINTAINERS | 5 +
> drivers/firmware/efi/Kconfig | 7 +-
> drivers/firmware/efi/Makefile | 1 -
> drivers/firmware/efi/earlycon.c | 246 --------------
> drivers/video/console/Kconfig | 11 +
> drivers/video/console/Makefile | 1 +
> drivers/video/console/earlycon.c | 305 ++++++++++++++++++
Ok I have a more fundamental issue with this than the lack of proper patch
splitting I mentioned in the other thread.
This is the wrong place.
drivers/video/console is about the various vt console implementations,
which supply a struct consw to con_register_driver.
This otoh is an (early) kernel/printk console implemented using struct
console. Totally different thing, and really shouldn't end up in
drivers/video/console imo. Somewhere in drivers/firmware might still be
the best place, the sysfb stuff is also there. Maybe
drivers/firmware/sysfb_earlycon.c?
Also patch split is still an issue here, like I and Greg already said.
-Daniel
> 8 files changed, 332 insertions(+), 256 deletions(-)
> delete mode 100644 drivers/firmware/efi/earlycon.c
> create mode 100644 drivers/video/console/earlycon.c
>
> diff --git a/Documentation/admin-guide/kernel-parameters.txt b/Documentation/admin-guide/kernel-parameters.txt
> index 8090130b544b0701237a7b657a29c83c000a60f4..bccb1ac8978eb5cf7e2bb20834b1881b27040666 100644
> --- a/Documentation/admin-guide/kernel-parameters.txt
> +++ b/Documentation/admin-guide/kernel-parameters.txt
> @@ -1281,12 +1281,9 @@
> specified address. The serial port must already be
> setup and configured. Options are not yet supported.
>
> - efifb,[options]
> + efifb
> Start an early, unaccelerated console on the EFI
> - memory mapped framebuffer (if available). On cache
> - coherent non-x86 systems that use system memory for
> - the framebuffer, pass the 'ram' option so that it is
> - mapped with the correct attributes.
> + memory mapped framebuffer (if available).
>
> linflex,<addr>
> Use early console provided by Freescale LINFlexD UART
> @@ -1294,6 +1291,11 @@
> address must be provided, and the serial port must
> already be setup and configured.
>
> + simplefb,<addr>,<width>x<height>x<bpp>
> + Use early console with simple framebuffer that is
> + pre-initialized by firmware. A valid base address,
> + width, height and pixel size must be provided.
> +
> earlyprintk= [X86,SH,ARM,M68k,S390]
> earlyprintk=vga
> earlyprintk=sclp
> diff --git a/MAINTAINERS b/MAINTAINERS
> index 1fc9ead83d2aa3e60ccc4cfa8ee16df09ef579bf..af8b8e289483b6a264d477145061bd0e0ba34a25 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -7033,6 +7033,11 @@ Q: http://patchwork.linuxtv.org/project/linux-media/list/
> T: git git://linuxtv.org/anttip/media_tree.git
> F: drivers/media/tuners/e4000*
>
> +EARLY CONSOLE FRAMEBUFFER DRIVER
> +M: Markuss Broks <markuss.broks@gmail.com>
> +S: Maintained
> +F: drivers/video/console/earlycon.c
> +
> EARTH_PT1 MEDIA DRIVER
> M: Akihiro Tsukada <tskd08@gmail.com>
> L: linux-media@vger.kernel.org
> diff --git a/drivers/firmware/efi/Kconfig b/drivers/firmware/efi/Kconfig
> index 7aa4717cdcac46f91dd202f868c463388eb02735..ea76ccfb9bcd8ba44ddca06052eaa442ed6c30f7 100644
> --- a/drivers/firmware/efi/Kconfig
> +++ b/drivers/firmware/efi/Kconfig
> @@ -259,10 +259,9 @@ config EFI_DISABLE_PCI_DMA
> may be used to override this option.
>
> config EFI_EARLYCON
> - def_bool y
> - depends on SERIAL_EARLYCON && !ARM && !IA64
> - select FONT_SUPPORT
> - select ARCH_USE_MEMREMAP_PROT
> + bool "EFI early console support"
> + select FB_EARLYCON
> + default y
>
> config EFI_CUSTOM_SSDT_OVERLAYS
> bool "Load custom ACPI SSDT overlay from an EFI variable"
> diff --git a/drivers/firmware/efi/Makefile b/drivers/firmware/efi/Makefile
> index c02ff25dd47707090a2ab86ee4f330e467f878f5..64eea61fbb43d76ec2d5416d467dfbb9aa21bda0 100644
> --- a/drivers/firmware/efi/Makefile
> +++ b/drivers/firmware/efi/Makefile
> @@ -44,6 +44,5 @@ obj-$(CONFIG_ARM64) += $(arm-obj-y)
> riscv-obj-$(CONFIG_EFI) := efi-init.o riscv-runtime.o
> obj-$(CONFIG_RISCV) += $(riscv-obj-y)
> obj-$(CONFIG_EFI_CAPSULE_LOADER) += capsule-loader.o
> -obj-$(CONFIG_EFI_EARLYCON) += earlycon.o
> obj-$(CONFIG_UEFI_CPER_ARM) += cper-arm.o
> obj-$(CONFIG_UEFI_CPER_X86) += cper-x86.o
> diff --git a/drivers/firmware/efi/earlycon.c b/drivers/firmware/efi/earlycon.c
> deleted file mode 100644
> index a52236e11e5f73ddea5bb1f42ca2ca7c42425dab..0000000000000000000000000000000000000000
> --- a/drivers/firmware/efi/earlycon.c
> +++ /dev/null
> @@ -1,246 +0,0 @@
> -// SPDX-License-Identifier: GPL-2.0
> -/*
> - * Copyright (C) 2013 Intel Corporation; author Matt Fleming
> - */
> -
> -#include <linux/console.h>
> -#include <linux/efi.h>
> -#include <linux/font.h>
> -#include <linux/io.h>
> -#include <linux/kernel.h>
> -#include <linux/serial_core.h>
> -#include <linux/screen_info.h>
> -
> -#include <asm/early_ioremap.h>
> -
> -static const struct console *earlycon_console __initdata;
> -static const struct font_desc *font;
> -static u32 efi_x, efi_y;
> -static u64 fb_base;
> -static bool fb_wb;
> -static void *efi_fb;
> -
> -/*
> - * EFI earlycon needs to use early_memremap() to map the framebuffer.
> - * But early_memremap() is not usable for 'earlycon=efifb keep_bootcon',
> - * memremap() should be used instead. memremap() will be available after
> - * paging_init() which is earlier than initcall callbacks. Thus adding this
> - * early initcall function early_efi_map_fb() to map the whole EFI framebuffer.
> - */
> -static int __init efi_earlycon_remap_fb(void)
> -{
> - /* bail if there is no bootconsole or it has been disabled already */
> - if (!earlycon_console || !(earlycon_console->flags & CON_ENABLED))
> - return 0;
> -
> - efi_fb = memremap(fb_base, screen_info.lfb_size,
> - fb_wb ? MEMREMAP_WB : MEMREMAP_WC);
> -
> - return efi_fb ? 0 : -ENOMEM;
> -}
> -early_initcall(efi_earlycon_remap_fb);
> -
> -static int __init efi_earlycon_unmap_fb(void)
> -{
> - /* unmap the bootconsole fb unless keep_bootcon has left it enabled */
> - if (efi_fb && !(earlycon_console->flags & CON_ENABLED))
> - memunmap(efi_fb);
> - return 0;
> -}
> -late_initcall(efi_earlycon_unmap_fb);
> -
> -static __ref void *efi_earlycon_map(unsigned long start, unsigned long len)
> -{
> - pgprot_t fb_prot;
> -
> - if (efi_fb)
> - return efi_fb + start;
> -
> - fb_prot = fb_wb ? PAGE_KERNEL : pgprot_writecombine(PAGE_KERNEL);
> - return early_memremap_prot(fb_base + start, len, pgprot_val(fb_prot));
> -}
> -
> -static __ref void efi_earlycon_unmap(void *addr, unsigned long len)
> -{
> - if (efi_fb)
> - return;
> -
> - early_memunmap(addr, len);
> -}
> -
> -static void efi_earlycon_clear_scanline(unsigned int y)
> -{
> - unsigned long *dst;
> - u16 len;
> -
> - len = screen_info.lfb_linelength;
> - dst = efi_earlycon_map(y*len, len);
> - if (!dst)
> - return;
> -
> - memset(dst, 0, len);
> - efi_earlycon_unmap(dst, len);
> -}
> -
> -static void efi_earlycon_scroll_up(void)
> -{
> - unsigned long *dst, *src;
> - u16 len;
> - u32 i, height;
> -
> - len = screen_info.lfb_linelength;
> - height = screen_info.lfb_height;
> -
> - for (i = 0; i < height - font->height; i++) {
> - dst = efi_earlycon_map(i*len, len);
> - if (!dst)
> - return;
> -
> - src = efi_earlycon_map((i + font->height) * len, len);
> - if (!src) {
> - efi_earlycon_unmap(dst, len);
> - return;
> - }
> -
> - memmove(dst, src, len);
> -
> - efi_earlycon_unmap(src, len);
> - efi_earlycon_unmap(dst, len);
> - }
> -}
> -
> -static void efi_earlycon_write_char(u32 *dst, unsigned char c, unsigned int h)
> -{
> - const u32 color_black = 0x00000000;
> - const u32 color_white = 0x00ffffff;
> - const u8 *src;
> - int m, n, bytes;
> - u8 x;
> -
> - bytes = BITS_TO_BYTES(font->width);
> - src = font->data + c * font->height * bytes + h * bytes;
> -
> - for (m = 0; m < font->width; m++) {
> - n = m % 8;
> - x = *(src + m / 8);
> - if ((x >> (7 - n)) & 1)
> - *dst = color_white;
> - else
> - *dst = color_black;
> - dst++;
> - }
> -}
> -
> -static void
> -efi_earlycon_write(struct console *con, const char *str, unsigned int num)
> -{
> - struct screen_info *si;
> - unsigned int len;
> - const char *s;
> - void *dst;
> -
> - si = &screen_info;
> - len = si->lfb_linelength;
> -
> - while (num) {
> - unsigned int linemax;
> - unsigned int h, count = 0;
> -
> - for (s = str; *s && *s != '\n'; s++) {
> - if (count == num)
> - break;
> - count++;
> - }
> -
> - linemax = (si->lfb_width - efi_x) / font->width;
> - if (count > linemax)
> - count = linemax;
> -
> - for (h = 0; h < font->height; h++) {
> - unsigned int n, x;
> -
> - dst = efi_earlycon_map((efi_y + h) * len, len);
> - if (!dst)
> - return;
> -
> - s = str;
> - n = count;
> - x = efi_x;
> -
> - while (n-- > 0) {
> - efi_earlycon_write_char(dst + x*4, *s, h);
> - x += font->width;
> - s++;
> - }
> -
> - efi_earlycon_unmap(dst, len);
> - }
> -
> - num -= count;
> - efi_x += count * font->width;
> - str += count;
> -
> - if (num > 0 && *s == '\n') {
> - efi_x = 0;
> - efi_y += font->height;
> - str++;
> - num--;
> - }
> -
> - if (efi_x + font->width > si->lfb_width) {
> - efi_x = 0;
> - efi_y += font->height;
> - }
> -
> - if (efi_y + font->height > si->lfb_height) {
> - u32 i;
> -
> - efi_y -= font->height;
> - efi_earlycon_scroll_up();
> -
> - for (i = 0; i < font->height; i++)
> - efi_earlycon_clear_scanline(efi_y + i);
> - }
> - }
> -}
> -
> -static int __init efi_earlycon_setup(struct earlycon_device *device,
> - const char *opt)
> -{
> - struct screen_info *si;
> - u16 xres, yres;
> - u32 i;
> -
> - if (screen_info.orig_video_isVGA != VIDEO_TYPE_EFI)
> - return -ENODEV;
> -
> - fb_base = screen_info.lfb_base;
> - if (screen_info.capabilities & VIDEO_CAPABILITY_64BIT_BASE)
> - fb_base |= (u64)screen_info.ext_lfb_base << 32;
> -
> - fb_wb = opt && !strcmp(opt, "ram");
> -
> - si = &screen_info;
> - xres = si->lfb_width;
> - yres = si->lfb_height;
> -
> - /*
> - * efi_earlycon_write_char() implicitly assumes a framebuffer with
> - * 32 bits per pixel.
> - */
> - if (si->lfb_depth != 32)
> - return -ENODEV;
> -
> - font = get_default_font(xres, yres, -1, -1);
> - if (!font)
> - return -ENODEV;
> -
> - efi_y = rounddown(yres, font->height) - font->height;
> - for (i = 0; i < (yres - efi_y) / font->height; i++)
> - efi_earlycon_scroll_up();
> -
> - device->con->write = efi_earlycon_write;
> - earlycon_console = device->con;
> - return 0;
> -}
> -EARLYCON_DECLARE(efifb, efi_earlycon_setup);
> diff --git a/drivers/video/console/Kconfig b/drivers/video/console/Kconfig
> index 40c50fa2dd70c33a1549141b15e6cba721352d2d..8052507e058fce37f5a51058e58ae2eb10d9669a 100644
> --- a/drivers/video/console/Kconfig
> +++ b/drivers/video/console/Kconfig
> @@ -69,6 +69,17 @@ config DUMMY_CONSOLE_ROWS
> monitor.
> Select 25 if you use a 640x480 resolution by default.
>
> +config FB_EARLYCON
> + bool "Generic framebuffer early console"
> + depends on SERIAL_EARLYCON && !ARM && !IA64
> + select FONT_SUPPORT
> + select ARCH_USE_MEMREMAP_PROT
> + help
> + Say Y here if you want early console support for firmware established
> + linear framebuffer. Unless you are using EFI framebuffer, you need to
> + specify framebuffer geometry and address in device-tree or in kernel
> + command line.
> +
> config FRAMEBUFFER_CONSOLE
> bool "Framebuffer Console support"
> depends on FB && !UML
> diff --git a/drivers/video/console/Makefile b/drivers/video/console/Makefile
> index db07b784bd2ccdcbffde933926ed5cee2bbbc7d4..7818faee587fc9c40b429617cfa224c0ccbc557c 100644
> --- a/drivers/video/console/Makefile
> +++ b/drivers/video/console/Makefile
> @@ -9,4 +9,5 @@ obj-$(CONFIG_STI_CONSOLE) += sticon.o sticore.o
> obj-$(CONFIG_VGA_CONSOLE) += vgacon.o
> obj-$(CONFIG_MDA_CONSOLE) += mdacon.o
>
> +obj-$(CONFIG_FB_EARLYCON) += earlycon.o
> obj-$(CONFIG_FB_STI) += sticore.o
> diff --git a/drivers/video/console/earlycon.c b/drivers/video/console/earlycon.c
> new file mode 100644
> index 0000000000000000000000000000000000000000..54436587e3db90034652dcc144669dca91b863d5
> --- /dev/null
> +++ b/drivers/video/console/earlycon.c
> @@ -0,0 +1,305 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright (C) 2013 Intel Corporation; author Matt Fleming
> + * Copyright (C) 2022 Markuss Broks <markuss.broks@gmail.com>
> + */
> +
> +#include <asm/early_ioremap.h>
> +#include <linux/console.h>
> +#include <linux/efi.h>
> +#include <linux/font.h>
> +#include <linux/io.h>
> +#include <linux/kernel.h>
> +#include <linux/mm.h>
> +#include <linux/of.h>
> +#include <linux/of_fdt.h>
> +#include <linux/serial_core.h>
> +#include <linux/screen_info.h>
> +
> +struct fb_earlycon {
> + u32 x, y, curr_x, curr_y, depth, stride;
> + size_t size;
> + phys_addr_t phys_base;
> + void __iomem *virt_base;
> +};
> +
> +static const struct console *earlycon_console __initconst;
> +static struct fb_earlycon info;
> +static const struct font_desc *font;
> +
> +static int __init simplefb_earlycon_remap_fb(void)
> +{
> + unsigned long mapping;
> + /* bail if there is no bootconsole or it has been disabled already */
> + if (!earlycon_console || !(earlycon_console->flags & CON_ENABLED))
> + return 0;
> +
> + if (region_intersects(info.phys_base, info.size,
> + IORESOURCE_SYSTEM_RAM, IORES_DESC_NONE) == REGION_INTERSECTS)
> + mapping = MEMREMAP_WB;
> + else
> + mapping = MEMREMAP_WC;
> +
> + info.virt_base = memremap(info.phys_base, info.size, mapping);
> +
> + return info.virt_base ? 0 : -ENOMEM;
> +}
> +early_initcall(simplefb_earlycon_remap_fb);
> +
> +static int __init simplefb_earlycon_unmap_fb(void)
> +{
> + /* unmap the bootconsole fb unless keep_bootcon has left it enabled */
> + if (info.virt_base && !(earlycon_console->flags & CON_ENABLED))
> + memunmap(info.virt_base);
> + return 0;
> +}
> +late_initcall(simplefb_earlycon_unmap_fb);
> +
> +static __ref void *simplefb_earlycon_map(unsigned long start, unsigned long len)
> +{
> + pgprot_t fb_prot;
> +
> + if (info.virt_base)
> + return info.virt_base + start;
> +
> + fb_prot = PAGE_KERNEL;
> + return early_memremap_prot(info.phys_base + start, len, pgprot_val(fb_prot));
> +}
> +
> +static __ref void simplefb_earlycon_unmap(void *addr, unsigned long len)
> +{
> + if (info.virt_base)
> + return;
> +
> + early_memunmap(addr, len);
> +}
> +
> +static void simplefb_earlycon_clear_scanline(unsigned int y)
> +{
> + unsigned long *dst;
> + u16 len;
> +
> + len = info.stride;
> + dst = simplefb_earlycon_map(y * len, len);
> + if (!dst)
> + return;
> +
> + memset(dst, 0, len);
> + simplefb_earlycon_unmap(dst, len);
> +}
> +
> +static void simplefb_earlycon_scroll_up(void)
> +{
> + unsigned long *dst, *src;
> + u16 len;
> + u32 i, height;
> +
> + len = info.stride;
> + height = info.y;
> +
> + for (i = 0; i < height - font->height; i++) {
> + dst = simplefb_earlycon_map(i * len, len);
> + if (!dst)
> + return;
> +
> + src = simplefb_earlycon_map((i + font->height) * len, len);
> + if (!src) {
> + simplefb_earlycon_unmap(dst, len);
> + return;
> + }
> +
> + memmove(dst, src, len);
> +
> + simplefb_earlycon_unmap(src, len);
> + simplefb_earlycon_unmap(dst, len);
> + }
> +}
> +
> +static void simplefb_earlycon_write_char(u8 *dst, unsigned char c, unsigned int h)
> +{
> + const u8 *src;
> + int m, n, bytes;
> + u8 x;
> +
> + bytes = BITS_TO_BYTES(font->width);
> + src = font->data + c * font->height * bytes + h * bytes;
> +
> + for (m = 0; m < font->width; m++) {
> + n = m % 8;
> + x = *(src + m / 8);
> + if ((x >> (7 - n)) & 1)
> + memset(dst, 0xff, (info.depth / 8));
> + else
> + memset(dst, 0, (info.depth / 8));
> + dst += (info.depth / 8);
> + }
> +}
> +
> +static void
> +simplefb_earlycon_write(struct console *con, const char *str, unsigned int num)
> +{
> + unsigned int len;
> + const char *s;
> + void *dst;
> +
> + len = info.stride;
> +
> + while (num) {
> + unsigned int linemax;
> + unsigned int h, count = 0;
> +
> + for (s = str; *s && *s != '\n'; s++) {
> + if (count == num)
> + break;
> + count++;
> + }
> +
> + linemax = (info.x - info.curr_x) / font->width;
> + if (count > linemax)
> + count = linemax;
> +
> + for (h = 0; h < font->height; h++) {
> + unsigned int n, x;
> +
> + dst = simplefb_earlycon_map((info.curr_y + h) * len, len);
> + if (!dst)
> + return;
> +
> + s = str;
> + n = count;
> + x = info.curr_x;
> +
> + while (n-- > 0) {
> + simplefb_earlycon_write_char(dst + (x * 4), *s, h);
> + x += font->width;
> + s++;
> + }
> +
> + simplefb_earlycon_unmap(dst, len);
> + }
> +
> + num -= count;
> + info.curr_x += count * font->width;
> + str += count;
> +
> + if (num > 0 && *s == '\n') {
> + info.curr_x = 0;
> + info.curr_y += font->height;
> + str++;
> + num--;
> + }
> +
> + if (info.curr_x + font->width > info.x) {
> + info.curr_x = 0;
> + info.curr_y += font->height;
> + }
> +
> + if (info.curr_y + font->height > info.y) {
> + u32 i;
> +
> + info.curr_y -= font->height;
> + simplefb_earlycon_scroll_up();
> +
> + for (i = 0; i < font->height; i++)
> + simplefb_earlycon_clear_scanline(info.curr_y + i);
> + }
> + }
> +}
> +
> +static int __init simplefb_earlycon_setup_common(struct earlycon_device *device,
> + const char *opt)
> +{
> + int i;
> +
> + info.size = info.x * info.y * (info.depth / 8);
> +
> + font = get_default_font(info.x, info.y, -1, -1);
> + if (!font)
> + return -ENODEV;
> +
> + info.curr_y = rounddown(info.y, font->height) - font->height;
> + for (i = 0; i < (info.y - info.curr_y) / font->height; i++)
> + simplefb_earlycon_scroll_up();
> +
> + device->con->write = simplefb_earlycon_write;
> + earlycon_console = device->con;
> + return 0;
> +}
> +
> +static int __init simplefb_earlycon_setup(struct earlycon_device *device,
> + const char *opt)
> +{
> + struct uart_port *port = &device->port;
> + int ret;
> +
> + if (!port->mapbase)
> + return -ENODEV;
> +
> + info.phys_base = port->mapbase;
> +
> + ret = sscanf(device->options, "%ux%ux%u", &info.x, &info.y, &info.depth);
> + if (ret != 3)
> + return -ENODEV;
> +
> + info.stride = info.x * (info.depth / 8);
> +
> + return simplefb_earlycon_setup_common(device, opt);
> +}
> +
> +EARLYCON_DECLARE(simplefb, simplefb_earlycon_setup);
> +
> +#ifdef CONFIG_EFI_EARLYCON
> +static int __init simplefb_earlycon_setup_efi(struct earlycon_device *device,
> + const char *opt)
> +{
> + if (screen_info.orig_video_isVGA != VIDEO_TYPE_EFI)
> + return -ENODEV;
> +
> + info.phys_base = screen_info.lfb_base;
> + if (screen_info.capabilities & VIDEO_CAPABILITY_64BIT_BASE)
> + info.phys_base |= (u64)screen_info.ext_lfb_base << 32;
> +
> + info.x = screen_info.lfb_width;
> + info.y = screen_info.lfb_height;
> + info.depth = screen_info.lfb_depth;
> + info.stride = screen_info.lfb_linelength;
> +
> + return simplefb_earlycon_setup_common(device, opt);
> +}
> +
> +EARLYCON_DECLARE(efifb, simplefb_earlycon_setup_efi);
> +#endif
> +
> +#ifdef CONFIG_OF_EARLY_FLATTREE
> +static int __init simplefb_earlycon_setup_of(struct earlycon_device *device,
> + const char *opt)
> +{
> + struct uart_port *port = &device->port;
> + const __be32 *val;
> +
> + if (!port->mapbase)
> + return -ENODEV;
> +
> + info.phys_base = port->mapbase;
> +
> + val = of_get_flat_dt_prop(device->offset, "width", NULL);
> + if (!val)
> + return -ENODEV;
> + info.x = be32_to_cpu(*val);
> +
> + val = of_get_flat_dt_prop(device->offset, "height", NULL);
> + if (!val)
> + return -ENODEV;
> + info.y = be32_to_cpu(*val);
> +
> + val = of_get_flat_dt_prop(device->offset, "stride", NULL);
> + if (!val)
> + return -ENODEV;
> + info.stride = be32_to_cpu(*val);
> + info.depth = (info.stride / info.x) * 8;
> +
> + return simplefb_earlycon_setup_common(device, opt);
> +}
> +
> +OF_EARLYCON_DECLARE(simplefb, "simple-framebuffer", simplefb_earlycon_setup_of);
> +#endif
> --
> 2.37.0
>
--
Daniel Vetter
Software Engineer, Intel Corporation
http://blog.ffwll.ch
Hi
Am 06.09.22 um 23:52 schrieb Daniel Vetter:
> On Sat, Aug 06, 2022 at 07:32:24PM +0300, Markuss Broks wrote:
>> Add early console support for generic linear framebuffer devices.
>> This driver supports probing from cmdline early parameters
>> or from the device-tree using information in simple-framebuffer node.
>> The EFI functionality should be retained in whole.
>> The driver was disabled on ARM because of a bug in early_ioremap
>> implementation on ARM and on IA64 because of lack of early_memremap_prot.
>>
>> Signed-off-by: Markuss Broks <markuss.broks@gmail.com>
>> ---
>> .../admin-guide/kernel-parameters.txt | 12 +-
>> MAINTAINERS | 5 +
>> drivers/firmware/efi/Kconfig | 7 +-
>> drivers/firmware/efi/Makefile | 1 -
>> drivers/firmware/efi/earlycon.c | 246 --------------
>> drivers/video/console/Kconfig | 11 +
>> drivers/video/console/Makefile | 1 +
>> drivers/video/console/earlycon.c | 305 ++++++++++++++++++
>
> Ok I have a more fundamental issue with this than the lack of proper patch
> splitting I mentioned in the other thread.
>
> This is the wrong place.
>
> drivers/video/console is about the various vt console implementations,
> which supply a struct consw to con_register_driver.
>
> This otoh is an (early) kernel/printk console implemented using struct
> console. Totally different thing, and really shouldn't end up in
> drivers/video/console imo. Somewhere in drivers/firmware might still be
> the best place, the sysfb stuff is also there. Maybe
> drivers/firmware/sysfb_earlycon.c?
Then better use just earlycon.c in that directory. We use the sysfb_
prefix for code of the framebuffer devices that simpledrm and simplefb
run on top. It would not fit well here.
Best regards
Thomas
>
> Also patch split is still an issue here, like I and Greg already said.
> -Daniel
>
>> 8 files changed, 332 insertions(+), 256 deletions(-)
>> delete mode 100644 drivers/firmware/efi/earlycon.c
>> create mode 100644 drivers/video/console/earlycon.c
>>
>> diff --git a/Documentation/admin-guide/kernel-parameters.txt b/Documentation/admin-guide/kernel-parameters.txt
>> index 8090130b544b0701237a7b657a29c83c000a60f4..bccb1ac8978eb5cf7e2bb20834b1881b27040666 100644
>> --- a/Documentation/admin-guide/kernel-parameters.txt
>> +++ b/Documentation/admin-guide/kernel-parameters.txt
>> @@ -1281,12 +1281,9 @@
>> specified address. The serial port must already be
>> setup and configured. Options are not yet supported.
>>
>> - efifb,[options]
>> + efifb
>> Start an early, unaccelerated console on the EFI
>> - memory mapped framebuffer (if available). On cache
>> - coherent non-x86 systems that use system memory for
>> - the framebuffer, pass the 'ram' option so that it is
>> - mapped with the correct attributes.
>> + memory mapped framebuffer (if available).
>>
>> linflex,<addr>
>> Use early console provided by Freescale LINFlexD UART
>> @@ -1294,6 +1291,11 @@
>> address must be provided, and the serial port must
>> already be setup and configured.
>>
>> + simplefb,<addr>,<width>x<height>x<bpp>
>> + Use early console with simple framebuffer that is
>> + pre-initialized by firmware. A valid base address,
>> + width, height and pixel size must be provided.
>> +
>> earlyprintk= [X86,SH,ARM,M68k,S390]
>> earlyprintk=vga
>> earlyprintk=sclp
>> diff --git a/MAINTAINERS b/MAINTAINERS
>> index 1fc9ead83d2aa3e60ccc4cfa8ee16df09ef579bf..af8b8e289483b6a264d477145061bd0e0ba34a25 100644
>> --- a/MAINTAINERS
>> +++ b/MAINTAINERS
>> @@ -7033,6 +7033,11 @@ Q: http://patchwork.linuxtv.org/project/linux-media/list/
>> T: git git://linuxtv.org/anttip/media_tree.git
>> F: drivers/media/tuners/e4000*
>>
>> +EARLY CONSOLE FRAMEBUFFER DRIVER
>> +M: Markuss Broks <markuss.broks@gmail.com>
>> +S: Maintained
>> +F: drivers/video/console/earlycon.c
>> +
>> EARTH_PT1 MEDIA DRIVER
>> M: Akihiro Tsukada <tskd08@gmail.com>
>> L: linux-media@vger.kernel.org
>> diff --git a/drivers/firmware/efi/Kconfig b/drivers/firmware/efi/Kconfig
>> index 7aa4717cdcac46f91dd202f868c463388eb02735..ea76ccfb9bcd8ba44ddca06052eaa442ed6c30f7 100644
>> --- a/drivers/firmware/efi/Kconfig
>> +++ b/drivers/firmware/efi/Kconfig
>> @@ -259,10 +259,9 @@ config EFI_DISABLE_PCI_DMA
>> may be used to override this option.
>>
>> config EFI_EARLYCON
>> - def_bool y
>> - depends on SERIAL_EARLYCON && !ARM && !IA64
>> - select FONT_SUPPORT
>> - select ARCH_USE_MEMREMAP_PROT
>> + bool "EFI early console support"
>> + select FB_EARLYCON
>> + default y
>>
>> config EFI_CUSTOM_SSDT_OVERLAYS
>> bool "Load custom ACPI SSDT overlay from an EFI variable"
>> diff --git a/drivers/firmware/efi/Makefile b/drivers/firmware/efi/Makefile
>> index c02ff25dd47707090a2ab86ee4f330e467f878f5..64eea61fbb43d76ec2d5416d467dfbb9aa21bda0 100644
>> --- a/drivers/firmware/efi/Makefile
>> +++ b/drivers/firmware/efi/Makefile
>> @@ -44,6 +44,5 @@ obj-$(CONFIG_ARM64) += $(arm-obj-y)
>> riscv-obj-$(CONFIG_EFI) := efi-init.o riscv-runtime.o
>> obj-$(CONFIG_RISCV) += $(riscv-obj-y)
>> obj-$(CONFIG_EFI_CAPSULE_LOADER) += capsule-loader.o
>> -obj-$(CONFIG_EFI_EARLYCON) += earlycon.o
>> obj-$(CONFIG_UEFI_CPER_ARM) += cper-arm.o
>> obj-$(CONFIG_UEFI_CPER_X86) += cper-x86.o
>> diff --git a/drivers/firmware/efi/earlycon.c b/drivers/firmware/efi/earlycon.c
>> deleted file mode 100644
>> index a52236e11e5f73ddea5bb1f42ca2ca7c42425dab..0000000000000000000000000000000000000000
>> --- a/drivers/firmware/efi/earlycon.c
>> +++ /dev/null
>> @@ -1,246 +0,0 @@
>> -// SPDX-License-Identifier: GPL-2.0
>> -/*
>> - * Copyright (C) 2013 Intel Corporation; author Matt Fleming
>> - */
>> -
>> -#include <linux/console.h>
>> -#include <linux/efi.h>
>> -#include <linux/font.h>
>> -#include <linux/io.h>
>> -#include <linux/kernel.h>
>> -#include <linux/serial_core.h>
>> -#include <linux/screen_info.h>
>> -
>> -#include <asm/early_ioremap.h>
>> -
>> -static const struct console *earlycon_console __initdata;
>> -static const struct font_desc *font;
>> -static u32 efi_x, efi_y;
>> -static u64 fb_base;
>> -static bool fb_wb;
>> -static void *efi_fb;
>> -
>> -/*
>> - * EFI earlycon needs to use early_memremap() to map the framebuffer.
>> - * But early_memremap() is not usable for 'earlycon=efifb keep_bootcon',
>> - * memremap() should be used instead. memremap() will be available after
>> - * paging_init() which is earlier than initcall callbacks. Thus adding this
>> - * early initcall function early_efi_map_fb() to map the whole EFI framebuffer.
>> - */
>> -static int __init efi_earlycon_remap_fb(void)
>> -{
>> - /* bail if there is no bootconsole or it has been disabled already */
>> - if (!earlycon_console || !(earlycon_console->flags & CON_ENABLED))
>> - return 0;
>> -
>> - efi_fb = memremap(fb_base, screen_info.lfb_size,
>> - fb_wb ? MEMREMAP_WB : MEMREMAP_WC);
>> -
>> - return efi_fb ? 0 : -ENOMEM;
>> -}
>> -early_initcall(efi_earlycon_remap_fb);
>> -
>> -static int __init efi_earlycon_unmap_fb(void)
>> -{
>> - /* unmap the bootconsole fb unless keep_bootcon has left it enabled */
>> - if (efi_fb && !(earlycon_console->flags & CON_ENABLED))
>> - memunmap(efi_fb);
>> - return 0;
>> -}
>> -late_initcall(efi_earlycon_unmap_fb);
>> -
>> -static __ref void *efi_earlycon_map(unsigned long start, unsigned long len)
>> -{
>> - pgprot_t fb_prot;
>> -
>> - if (efi_fb)
>> - return efi_fb + start;
>> -
>> - fb_prot = fb_wb ? PAGE_KERNEL : pgprot_writecombine(PAGE_KERNEL);
>> - return early_memremap_prot(fb_base + start, len, pgprot_val(fb_prot));
>> -}
>> -
>> -static __ref void efi_earlycon_unmap(void *addr, unsigned long len)
>> -{
>> - if (efi_fb)
>> - return;
>> -
>> - early_memunmap(addr, len);
>> -}
>> -
>> -static void efi_earlycon_clear_scanline(unsigned int y)
>> -{
>> - unsigned long *dst;
>> - u16 len;
>> -
>> - len = screen_info.lfb_linelength;
>> - dst = efi_earlycon_map(y*len, len);
>> - if (!dst)
>> - return;
>> -
>> - memset(dst, 0, len);
>> - efi_earlycon_unmap(dst, len);
>> -}
>> -
>> -static void efi_earlycon_scroll_up(void)
>> -{
>> - unsigned long *dst, *src;
>> - u16 len;
>> - u32 i, height;
>> -
>> - len = screen_info.lfb_linelength;
>> - height = screen_info.lfb_height;
>> -
>> - for (i = 0; i < height - font->height; i++) {
>> - dst = efi_earlycon_map(i*len, len);
>> - if (!dst)
>> - return;
>> -
>> - src = efi_earlycon_map((i + font->height) * len, len);
>> - if (!src) {
>> - efi_earlycon_unmap(dst, len);
>> - return;
>> - }
>> -
>> - memmove(dst, src, len);
>> -
>> - efi_earlycon_unmap(src, len);
>> - efi_earlycon_unmap(dst, len);
>> - }
>> -}
>> -
>> -static void efi_earlycon_write_char(u32 *dst, unsigned char c, unsigned int h)
>> -{
>> - const u32 color_black = 0x00000000;
>> - const u32 color_white = 0x00ffffff;
>> - const u8 *src;
>> - int m, n, bytes;
>> - u8 x;
>> -
>> - bytes = BITS_TO_BYTES(font->width);
>> - src = font->data + c * font->height * bytes + h * bytes;
>> -
>> - for (m = 0; m < font->width; m++) {
>> - n = m % 8;
>> - x = *(src + m / 8);
>> - if ((x >> (7 - n)) & 1)
>> - *dst = color_white;
>> - else
>> - *dst = color_black;
>> - dst++;
>> - }
>> -}
>> -
>> -static void
>> -efi_earlycon_write(struct console *con, const char *str, unsigned int num)
>> -{
>> - struct screen_info *si;
>> - unsigned int len;
>> - const char *s;
>> - void *dst;
>> -
>> - si = &screen_info;
>> - len = si->lfb_linelength;
>> -
>> - while (num) {
>> - unsigned int linemax;
>> - unsigned int h, count = 0;
>> -
>> - for (s = str; *s && *s != '\n'; s++) {
>> - if (count == num)
>> - break;
>> - count++;
>> - }
>> -
>> - linemax = (si->lfb_width - efi_x) / font->width;
>> - if (count > linemax)
>> - count = linemax;
>> -
>> - for (h = 0; h < font->height; h++) {
>> - unsigned int n, x;
>> -
>> - dst = efi_earlycon_map((efi_y + h) * len, len);
>> - if (!dst)
>> - return;
>> -
>> - s = str;
>> - n = count;
>> - x = efi_x;
>> -
>> - while (n-- > 0) {
>> - efi_earlycon_write_char(dst + x*4, *s, h);
>> - x += font->width;
>> - s++;
>> - }
>> -
>> - efi_earlycon_unmap(dst, len);
>> - }
>> -
>> - num -= count;
>> - efi_x += count * font->width;
>> - str += count;
>> -
>> - if (num > 0 && *s == '\n') {
>> - efi_x = 0;
>> - efi_y += font->height;
>> - str++;
>> - num--;
>> - }
>> -
>> - if (efi_x + font->width > si->lfb_width) {
>> - efi_x = 0;
>> - efi_y += font->height;
>> - }
>> -
>> - if (efi_y + font->height > si->lfb_height) {
>> - u32 i;
>> -
>> - efi_y -= font->height;
>> - efi_earlycon_scroll_up();
>> -
>> - for (i = 0; i < font->height; i++)
>> - efi_earlycon_clear_scanline(efi_y + i);
>> - }
>> - }
>> -}
>> -
>> -static int __init efi_earlycon_setup(struct earlycon_device *device,
>> - const char *opt)
>> -{
>> - struct screen_info *si;
>> - u16 xres, yres;
>> - u32 i;
>> -
>> - if (screen_info.orig_video_isVGA != VIDEO_TYPE_EFI)
>> - return -ENODEV;
>> -
>> - fb_base = screen_info.lfb_base;
>> - if (screen_info.capabilities & VIDEO_CAPABILITY_64BIT_BASE)
>> - fb_base |= (u64)screen_info.ext_lfb_base << 32;
>> -
>> - fb_wb = opt && !strcmp(opt, "ram");
>> -
>> - si = &screen_info;
>> - xres = si->lfb_width;
>> - yres = si->lfb_height;
>> -
>> - /*
>> - * efi_earlycon_write_char() implicitly assumes a framebuffer with
>> - * 32 bits per pixel.
>> - */
>> - if (si->lfb_depth != 32)
>> - return -ENODEV;
>> -
>> - font = get_default_font(xres, yres, -1, -1);
>> - if (!font)
>> - return -ENODEV;
>> -
>> - efi_y = rounddown(yres, font->height) - font->height;
>> - for (i = 0; i < (yres - efi_y) / font->height; i++)
>> - efi_earlycon_scroll_up();
>> -
>> - device->con->write = efi_earlycon_write;
>> - earlycon_console = device->con;
>> - return 0;
>> -}
>> -EARLYCON_DECLARE(efifb, efi_earlycon_setup);
>> diff --git a/drivers/video/console/Kconfig b/drivers/video/console/Kconfig
>> index 40c50fa2dd70c33a1549141b15e6cba721352d2d..8052507e058fce37f5a51058e58ae2eb10d9669a 100644
>> --- a/drivers/video/console/Kconfig
>> +++ b/drivers/video/console/Kconfig
>> @@ -69,6 +69,17 @@ config DUMMY_CONSOLE_ROWS
>> monitor.
>> Select 25 if you use a 640x480 resolution by default.
>>
>> +config FB_EARLYCON
>> + bool "Generic framebuffer early console"
>> + depends on SERIAL_EARLYCON && !ARM && !IA64
>> + select FONT_SUPPORT
>> + select ARCH_USE_MEMREMAP_PROT
>> + help
>> + Say Y here if you want early console support for firmware established
>> + linear framebuffer. Unless you are using EFI framebuffer, you need to
>> + specify framebuffer geometry and address in device-tree or in kernel
>> + command line.
>> +
>> config FRAMEBUFFER_CONSOLE
>> bool "Framebuffer Console support"
>> depends on FB && !UML
>> diff --git a/drivers/video/console/Makefile b/drivers/video/console/Makefile
>> index db07b784bd2ccdcbffde933926ed5cee2bbbc7d4..7818faee587fc9c40b429617cfa224c0ccbc557c 100644
>> --- a/drivers/video/console/Makefile
>> +++ b/drivers/video/console/Makefile
>> @@ -9,4 +9,5 @@ obj-$(CONFIG_STI_CONSOLE) += sticon.o sticore.o
>> obj-$(CONFIG_VGA_CONSOLE) += vgacon.o
>> obj-$(CONFIG_MDA_CONSOLE) += mdacon.o
>>
>> +obj-$(CONFIG_FB_EARLYCON) += earlycon.o
>> obj-$(CONFIG_FB_STI) += sticore.o
>> diff --git a/drivers/video/console/earlycon.c b/drivers/video/console/earlycon.c
>> new file mode 100644
>> index 0000000000000000000000000000000000000000..54436587e3db90034652dcc144669dca91b863d5
>> --- /dev/null
>> +++ b/drivers/video/console/earlycon.c
>> @@ -0,0 +1,305 @@
>> +// SPDX-License-Identifier: GPL-2.0
>> +/*
>> + * Copyright (C) 2013 Intel Corporation; author Matt Fleming
>> + * Copyright (C) 2022 Markuss Broks <markuss.broks@gmail.com>
>> + */
>> +
>> +#include <asm/early_ioremap.h>
>> +#include <linux/console.h>
>> +#include <linux/efi.h>
>> +#include <linux/font.h>
>> +#include <linux/io.h>
>> +#include <linux/kernel.h>
>> +#include <linux/mm.h>
>> +#include <linux/of.h>
>> +#include <linux/of_fdt.h>
>> +#include <linux/serial_core.h>
>> +#include <linux/screen_info.h>
>> +
>> +struct fb_earlycon {
>> + u32 x, y, curr_x, curr_y, depth, stride;
>> + size_t size;
>> + phys_addr_t phys_base;
>> + void __iomem *virt_base;
>> +};
>> +
>> +static const struct console *earlycon_console __initconst;
>> +static struct fb_earlycon info;
>> +static const struct font_desc *font;
>> +
>> +static int __init simplefb_earlycon_remap_fb(void)
>> +{
>> + unsigned long mapping;
>> + /* bail if there is no bootconsole or it has been disabled already */
>> + if (!earlycon_console || !(earlycon_console->flags & CON_ENABLED))
>> + return 0;
>> +
>> + if (region_intersects(info.phys_base, info.size,
>> + IORESOURCE_SYSTEM_RAM, IORES_DESC_NONE) == REGION_INTERSECTS)
>> + mapping = MEMREMAP_WB;
>> + else
>> + mapping = MEMREMAP_WC;
>> +
>> + info.virt_base = memremap(info.phys_base, info.size, mapping);
>> +
>> + return info.virt_base ? 0 : -ENOMEM;
>> +}
>> +early_initcall(simplefb_earlycon_remap_fb);
>> +
>> +static int __init simplefb_earlycon_unmap_fb(void)
>> +{
>> + /* unmap the bootconsole fb unless keep_bootcon has left it enabled */
>> + if (info.virt_base && !(earlycon_console->flags & CON_ENABLED))
>> + memunmap(info.virt_base);
>> + return 0;
>> +}
>> +late_initcall(simplefb_earlycon_unmap_fb);
>> +
>> +static __ref void *simplefb_earlycon_map(unsigned long start, unsigned long len)
>> +{
>> + pgprot_t fb_prot;
>> +
>> + if (info.virt_base)
>> + return info.virt_base + start;
>> +
>> + fb_prot = PAGE_KERNEL;
>> + return early_memremap_prot(info.phys_base + start, len, pgprot_val(fb_prot));
>> +}
>> +
>> +static __ref void simplefb_earlycon_unmap(void *addr, unsigned long len)
>> +{
>> + if (info.virt_base)
>> + return;
>> +
>> + early_memunmap(addr, len);
>> +}
>> +
>> +static void simplefb_earlycon_clear_scanline(unsigned int y)
>> +{
>> + unsigned long *dst;
>> + u16 len;
>> +
>> + len = info.stride;
>> + dst = simplefb_earlycon_map(y * len, len);
>> + if (!dst)
>> + return;
>> +
>> + memset(dst, 0, len);
>> + simplefb_earlycon_unmap(dst, len);
>> +}
>> +
>> +static void simplefb_earlycon_scroll_up(void)
>> +{
>> + unsigned long *dst, *src;
>> + u16 len;
>> + u32 i, height;
>> +
>> + len = info.stride;
>> + height = info.y;
>> +
>> + for (i = 0; i < height - font->height; i++) {
>> + dst = simplefb_earlycon_map(i * len, len);
>> + if (!dst)
>> + return;
>> +
>> + src = simplefb_earlycon_map((i + font->height) * len, len);
>> + if (!src) {
>> + simplefb_earlycon_unmap(dst, len);
>> + return;
>> + }
>> +
>> + memmove(dst, src, len);
>> +
>> + simplefb_earlycon_unmap(src, len);
>> + simplefb_earlycon_unmap(dst, len);
>> + }
>> +}
>> +
>> +static void simplefb_earlycon_write_char(u8 *dst, unsigned char c, unsigned int h)
>> +{
>> + const u8 *src;
>> + int m, n, bytes;
>> + u8 x;
>> +
>> + bytes = BITS_TO_BYTES(font->width);
>> + src = font->data + c * font->height * bytes + h * bytes;
>> +
>> + for (m = 0; m < font->width; m++) {
>> + n = m % 8;
>> + x = *(src + m / 8);
>> + if ((x >> (7 - n)) & 1)
>> + memset(dst, 0xff, (info.depth / 8));
>> + else
>> + memset(dst, 0, (info.depth / 8));
>> + dst += (info.depth / 8);
>> + }
>> +}
>> +
>> +static void
>> +simplefb_earlycon_write(struct console *con, const char *str, unsigned int num)
>> +{
>> + unsigned int len;
>> + const char *s;
>> + void *dst;
>> +
>> + len = info.stride;
>> +
>> + while (num) {
>> + unsigned int linemax;
>> + unsigned int h, count = 0;
>> +
>> + for (s = str; *s && *s != '\n'; s++) {
>> + if (count == num)
>> + break;
>> + count++;
>> + }
>> +
>> + linemax = (info.x - info.curr_x) / font->width;
>> + if (count > linemax)
>> + count = linemax;
>> +
>> + for (h = 0; h < font->height; h++) {
>> + unsigned int n, x;
>> +
>> + dst = simplefb_earlycon_map((info.curr_y + h) * len, len);
>> + if (!dst)
>> + return;
>> +
>> + s = str;
>> + n = count;
>> + x = info.curr_x;
>> +
>> + while (n-- > 0) {
>> + simplefb_earlycon_write_char(dst + (x * 4), *s, h);
>> + x += font->width;
>> + s++;
>> + }
>> +
>> + simplefb_earlycon_unmap(dst, len);
>> + }
>> +
>> + num -= count;
>> + info.curr_x += count * font->width;
>> + str += count;
>> +
>> + if (num > 0 && *s == '\n') {
>> + info.curr_x = 0;
>> + info.curr_y += font->height;
>> + str++;
>> + num--;
>> + }
>> +
>> + if (info.curr_x + font->width > info.x) {
>> + info.curr_x = 0;
>> + info.curr_y += font->height;
>> + }
>> +
>> + if (info.curr_y + font->height > info.y) {
>> + u32 i;
>> +
>> + info.curr_y -= font->height;
>> + simplefb_earlycon_scroll_up();
>> +
>> + for (i = 0; i < font->height; i++)
>> + simplefb_earlycon_clear_scanline(info.curr_y + i);
>> + }
>> + }
>> +}
>> +
>> +static int __init simplefb_earlycon_setup_common(struct earlycon_device *device,
>> + const char *opt)
>> +{
>> + int i;
>> +
>> + info.size = info.x * info.y * (info.depth / 8);
>> +
>> + font = get_default_font(info.x, info.y, -1, -1);
>> + if (!font)
>> + return -ENODEV;
>> +
>> + info.curr_y = rounddown(info.y, font->height) - font->height;
>> + for (i = 0; i < (info.y - info.curr_y) / font->height; i++)
>> + simplefb_earlycon_scroll_up();
>> +
>> + device->con->write = simplefb_earlycon_write;
>> + earlycon_console = device->con;
>> + return 0;
>> +}
>> +
>> +static int __init simplefb_earlycon_setup(struct earlycon_device *device,
>> + const char *opt)
>> +{
>> + struct uart_port *port = &device->port;
>> + int ret;
>> +
>> + if (!port->mapbase)
>> + return -ENODEV;
>> +
>> + info.phys_base = port->mapbase;
>> +
>> + ret = sscanf(device->options, "%ux%ux%u", &info.x, &info.y, &info.depth);
>> + if (ret != 3)
>> + return -ENODEV;
>> +
>> + info.stride = info.x * (info.depth / 8);
>> +
>> + return simplefb_earlycon_setup_common(device, opt);
>> +}
>> +
>> +EARLYCON_DECLARE(simplefb, simplefb_earlycon_setup);
>> +
>> +#ifdef CONFIG_EFI_EARLYCON
>> +static int __init simplefb_earlycon_setup_efi(struct earlycon_device *device,
>> + const char *opt)
>> +{
>> + if (screen_info.orig_video_isVGA != VIDEO_TYPE_EFI)
>> + return -ENODEV;
>> +
>> + info.phys_base = screen_info.lfb_base;
>> + if (screen_info.capabilities & VIDEO_CAPABILITY_64BIT_BASE)
>> + info.phys_base |= (u64)screen_info.ext_lfb_base << 32;
>> +
>> + info.x = screen_info.lfb_width;
>> + info.y = screen_info.lfb_height;
>> + info.depth = screen_info.lfb_depth;
>> + info.stride = screen_info.lfb_linelength;
>> +
>> + return simplefb_earlycon_setup_common(device, opt);
>> +}
>> +
>> +EARLYCON_DECLARE(efifb, simplefb_earlycon_setup_efi);
>> +#endif
>> +
>> +#ifdef CONFIG_OF_EARLY_FLATTREE
>> +static int __init simplefb_earlycon_setup_of(struct earlycon_device *device,
>> + const char *opt)
>> +{
>> + struct uart_port *port = &device->port;
>> + const __be32 *val;
>> +
>> + if (!port->mapbase)
>> + return -ENODEV;
>> +
>> + info.phys_base = port->mapbase;
>> +
>> + val = of_get_flat_dt_prop(device->offset, "width", NULL);
>> + if (!val)
>> + return -ENODEV;
>> + info.x = be32_to_cpu(*val);
>> +
>> + val = of_get_flat_dt_prop(device->offset, "height", NULL);
>> + if (!val)
>> + return -ENODEV;
>> + info.y = be32_to_cpu(*val);
>> +
>> + val = of_get_flat_dt_prop(device->offset, "stride", NULL);
>> + if (!val)
>> + return -ENODEV;
>> + info.stride = be32_to_cpu(*val);
>> + info.depth = (info.stride / info.x) * 8;
>> +
>> + return simplefb_earlycon_setup_common(device, opt);
>> +}
>> +
>> +OF_EARLYCON_DECLARE(simplefb, "simple-framebuffer", simplefb_earlycon_setup_of);
>> +#endif
>> --
>> 2.37.0
>>
>
--
Thomas Zimmermann
Graphics Driver Developer
SUSE Software Solutions Germany GmbH
Maxfeldstr. 5, 90409 Nürnberg, Germany
(HRB 36809, AG Nürnberg)
Geschäftsführer: Ivo Totev
On Tue, Sep 6, 2022, at 11:52 PM, Daniel Vetter wrote:
> On Sat, Aug 06, 2022 at 07:32:24PM +0300, Markuss Broks wrote:
>
> Ok I have a more fundamental issue with this than the lack of proper patch
> splitting I mentioned in the other thread.
>
> This is the wrong place.
>
> drivers/video/console is about the various vt console implementations,
> which supply a struct consw to con_register_driver.
You are right, that was my mistake. The original patch was for
drivers/video/fbdev/, and I suggested moving it out of there because
it does not depend on the fbdev subsystem, but clearly my suggestions
of drivers/video/console was equally wrong.
> This otoh is an (early) kernel/printk console implemented using struct
> console. Totally different thing, and really shouldn't end up in
> drivers/video/console imo. Somewhere in drivers/firmware might still be
> the best place, the sysfb stuff is also there. Maybe
> drivers/firmware/sysfb_earlycon.c?
drivers/firmware/ is better but doesn't sound great to me either,
since one important thing the patch does is to not make it depend
on EFI firmware or BIOS style screen_info any more.
Maybe drivers/tty/earlycon_simplefb.c would work better, keeping
it close to the earlycon base support in drivers/tty/serial/,
the vt console and the old simplefb driver/, without tying to too
closely to fbdev of UEFI.
Arnd
Hi Markuss,
I love your patch! Perhaps something to improve:
[auto build test WARNING on tty/tty-testing]
[also build test WARNING on efi/next staging/staging-testing usb/usb-testing linus/master v5.19 next-20220812]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]
url: https://github.com/intel-lab-lkp/linux/commits/Markuss-Broks/Add-generic-framebuffer-support-to-EFI-earlycon-driver/20220807-003646
base: https://git.kernel.org/pub/scm/linux/kernel/git/gregkh/tty.git tty-testing
config: x86_64-kismet-CONFIG_FB_EARLYCON-CONFIG_EFI_EARLYCON-0-0 (https://download.01.org/0day-ci/archive/20220814/202208141430.Ez8EkEwO-lkp@intel.com/config)
reproduce:
# https://github.com/intel-lab-lkp/linux/commit/97dfc2aa69b065de769a191352afe2099c52fedb
git remote add linux-review https://github.com/intel-lab-lkp/linux
git fetch --no-tags linux-review Markuss-Broks/Add-generic-framebuffer-support-to-EFI-earlycon-driver/20220807-003646
git checkout 97dfc2aa69b065de769a191352afe2099c52fedb
# 1. reproduce by kismet
# install kmax per https://github.com/paulgazz/kmax/blob/master/README.md
kismet --linux-ksrc=linux --selectees CONFIG_FB_EARLYCON --selectors CONFIG_EFI_EARLYCON -a=x86_64
# 2. reproduce by make
# save the config file to linux source tree
cd linux
make ARCH=x86_64 olddefconfig
If you fix the issue, kindly add following tag where applicable
Reported-by: kernel test robot <lkp@intel.com>
kismet warnings: (new ones prefixed by >>)
>> kismet: WARNING: unmet direct dependencies detected for FB_EARLYCON when selected by EFI_EARLYCON
WARNING: unmet direct dependencies detected for FB_EARLYCON
Depends on [n]: VT [=y] && SERIAL_EARLYCON [=n] && !ARM && !IA64
Selected by [y]:
- EFI_EARLYCON [=y] && EFI [=y]
--
0-DAY CI Kernel Test Service
https://01.org/lkp
Hi Markuss,
I love your patch! Perhaps something to improve:
[auto build test WARNING on tty/tty-testing]
[also build test WARNING on efi/next staging/staging-testing usb/usb-testing linus/master v5.19 next-20220812]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]
url: https://github.com/intel-lab-lkp/linux/commits/Markuss-Broks/Add-generic-framebuffer-support-to-EFI-earlycon-driver/20220807-003646
base: https://git.kernel.org/pub/scm/linux/kernel/git/gregkh/tty.git tty-testing
config: loongarch-randconfig-s031-20220807 (https://download.01.org/0day-ci/archive/20220814/202208140705.bU9i1c1t-lkp@intel.com/config)
compiler: loongarch64-linux-gcc (GCC) 12.1.0
reproduce:
wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
chmod +x ~/bin/make.cross
# apt-get install sparse
# sparse version: v0.6.4-39-gce1a6720-dirty
# https://github.com/intel-lab-lkp/linux/commit/97dfc2aa69b065de769a191352afe2099c52fedb
git remote add linux-review https://github.com/intel-lab-lkp/linux
git fetch --no-tags linux-review Markuss-Broks/Add-generic-framebuffer-support-to-EFI-earlycon-driver/20220807-003646
git checkout 97dfc2aa69b065de769a191352afe2099c52fedb
# save the config file
mkdir build_dir && cp config build_dir/.config
COMPILER_INSTALL_PATH=$HOME/0day COMPILER=gcc-12.1.0 make.cross C=1 CF='-fdiagnostic-prefix -D__CHECK_ENDIAN__' O=build_dir ARCH=loongarch SHELL=/bin/bash drivers/video/console/
If you fix the issue, kindly add following tag where applicable
Reported-by: kernel test robot <lkp@intel.com>
sparse warnings: (new ones prefixed by >>)
>> drivers/video/console/earlycon.c:43:24: sparse: sparse: incorrect type in assignment (different address spaces) @@ expected void [noderef] __iomem *static [toplevel] virt_base @@ got void * @@
drivers/video/console/earlycon.c:43:24: sparse: expected void [noderef] __iomem *static [toplevel] virt_base
drivers/video/console/earlycon.c:43:24: sparse: got void *
>> drivers/video/console/earlycon.c:53:30: sparse: sparse: incorrect type in argument 1 (different address spaces) @@ expected void *addr @@ got void [noderef] __iomem *static [toplevel] virt_base @@
drivers/video/console/earlycon.c:53:30: sparse: expected void *addr
drivers/video/console/earlycon.c:53:30: sparse: got void [noderef] __iomem *static [toplevel] virt_base
>> drivers/video/console/earlycon.c:63:39: sparse: sparse: incorrect type in return expression (different address spaces) @@ expected void * @@ got void [noderef] __iomem * @@
drivers/video/console/earlycon.c:63:39: sparse: expected void *
drivers/video/console/earlycon.c:63:39: sparse: got void [noderef] __iomem *
>> drivers/video/console/earlycon.c:74:24: sparse: sparse: incorrect type in argument 1 (different address spaces) @@ expected void [noderef] __iomem *addr @@ got void *addr @@
drivers/video/console/earlycon.c:74:24: sparse: expected void [noderef] __iomem *addr
drivers/video/console/earlycon.c:74:24: sparse: got void *addr
vim +43 drivers/video/console/earlycon.c
29
30 static int __init simplefb_earlycon_remap_fb(void)
31 {
32 unsigned long mapping;
33 /* bail if there is no bootconsole or it has been disabled already */
34 if (!earlycon_console || !(earlycon_console->flags & CON_ENABLED))
35 return 0;
36
37 if (region_intersects(info.phys_base, info.size,
38 IORESOURCE_SYSTEM_RAM, IORES_DESC_NONE) == REGION_INTERSECTS)
39 mapping = MEMREMAP_WB;
40 else
41 mapping = MEMREMAP_WC;
42
> 43 info.virt_base = memremap(info.phys_base, info.size, mapping);
44
45 return info.virt_base ? 0 : -ENOMEM;
46 }
47 early_initcall(simplefb_earlycon_remap_fb);
48
49 static int __init simplefb_earlycon_unmap_fb(void)
50 {
51 /* unmap the bootconsole fb unless keep_bootcon has left it enabled */
52 if (info.virt_base && !(earlycon_console->flags & CON_ENABLED))
> 53 memunmap(info.virt_base);
54 return 0;
55 }
56 late_initcall(simplefb_earlycon_unmap_fb);
57
58 static __ref void *simplefb_earlycon_map(unsigned long start, unsigned long len)
59 {
60 pgprot_t fb_prot;
61
62 if (info.virt_base)
> 63 return info.virt_base + start;
64
65 fb_prot = PAGE_KERNEL;
66 return early_memremap_prot(info.phys_base + start, len, pgprot_val(fb_prot));
67 }
68
69 static __ref void simplefb_earlycon_unmap(void *addr, unsigned long len)
70 {
71 if (info.virt_base)
72 return;
73
> 74 early_memunmap(addr, len);
75 }
76
--
0-DAY CI Kernel Test Service
https://01.org/lkp
Hi Markuss,
I love your patch! Yet something to improve:
[auto build test ERROR on tty/tty-testing]
[also build test ERROR on efi/next staging/staging-testing usb/usb-testing linus/master v5.19 next-20220805]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]
url: https://github.com/intel-lab-lkp/linux/commits/Markuss-Broks/Add-generic-framebuffer-support-to-EFI-earlycon-driver/20220807-003646
base: https://git.kernel.org/pub/scm/linux/kernel/git/gregkh/tty.git tty-testing
config: sh-allmodconfig (https://download.01.org/0day-ci/archive/20220807/202208072145.yWoGkrW1-lkp@intel.com/config)
compiler: sh4-linux-gcc (GCC) 12.1.0
reproduce (this is a W=1 build):
wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
chmod +x ~/bin/make.cross
# https://github.com/intel-lab-lkp/linux/commit/97dfc2aa69b065de769a191352afe2099c52fedb
git remote add linux-review https://github.com/intel-lab-lkp/linux
git fetch --no-tags linux-review Markuss-Broks/Add-generic-framebuffer-support-to-EFI-earlycon-driver/20220807-003646
git checkout 97dfc2aa69b065de769a191352afe2099c52fedb
# save the config file
mkdir build_dir && cp config build_dir/.config
COMPILER_INSTALL_PATH=$HOME/0day COMPILER=gcc-12.1.0 make.cross W=1 O=build_dir ARCH=sh SHELL=/bin/bash
If you fix the issue, kindly add following tag where applicable
Reported-by: kernel test robot <lkp@intel.com>
All errors (new ones prefixed by >>):
>> drivers/video/console/earlycon.c:7:10: fatal error: asm/early_ioremap.h: No such file or directory
7 | #include <asm/early_ioremap.h>
| ^~~~~~~~~~~~~~~~~~~~~
compilation terminated.
vim +7 drivers/video/console/earlycon.c
> 7 #include <asm/early_ioremap.h>
8 #include <linux/console.h>
9 #include <linux/efi.h>
10 #include <linux/font.h>
11 #include <linux/io.h>
12 #include <linux/kernel.h>
13 #include <linux/mm.h>
14 #include <linux/of.h>
15 #include <linux/of_fdt.h>
16 #include <linux/serial_core.h>
17 #include <linux/screen_info.h>
18
--
0-DAY CI Kernel Test Service
https://01.org/lkp
On Sat, Aug 6, 2022 at 6:38 PM Markuss Broks <markuss.broks@gmail.com> wrote:
>
> Add early console support for generic linear framebuffer devices.
> This driver supports probing from cmdline early parameters
> or from the device-tree using information in simple-framebuffer node.
> The EFI functionality should be retained in whole.
> The driver was disabled on ARM because of a bug in early_ioremap
We refer to functions like func().
> implementation on ARM and on IA64 because of lack of early_memremap_prot.
Ditto.
...
> +#include <asm/early_ioremap.h>
Can it be placed after linux/* ones?
> +#include <linux/console.h>
> +#include <linux/efi.h>
> +#include <linux/font.h>
> +#include <linux/io.h>
> +#include <linux/kernel.h>
> +#include <linux/mm.h>
> +#include <linux/of.h>
> +#include <linux/of_fdt.h>
> +#include <linux/serial_core.h>
> +#include <linux/screen_info.h>
...
> +static int __init simplefb_earlycon_remap_fb(void)
> +{
> + unsigned long mapping;
+ Blank line.
> + /* bail if there is no bootconsole or it has been disabled already */
> + if (!earlycon_console || !(earlycon_console->flags & CON_ENABLED))
> + return 0;
> +
> + if (region_intersects(info.phys_base, info.size,
> + IORESOURCE_SYSTEM_RAM, IORES_DESC_NONE) == REGION_INTERSECTS)
> + mapping = MEMREMAP_WB;
> + else
> + mapping = MEMREMAP_WC;
> + info.virt_base = memremap(info.phys_base, info.size, mapping);
> +
> + return info.virt_base ? 0 : -ENOMEM;
Easier to read the standard pattern:
if (!info.virt_base)
return -ENOMEM;
return 0;
> +}
...
> +static void simplefb_earlycon_write_char(u8 *dst, unsigned char c, unsigned int h)
> +{
> + const u8 *src;
> + int m, n, bytes;
> + u8 x;
> +
> + bytes = BITS_TO_BYTES(font->width);
> + src = font->data + c * font->height * bytes + h * bytes;
> +
> + for (m = 0; m < font->width; m++) {
> + n = m % 8;
> + x = *(src + m / 8);
I would write it as
x = src[m / 8];
> + if ((x >> (7 - n)) & 1)
> + memset(dst, 0xff, (info.depth / 8));
Too many parentheses.
> + else
> + memset(dst, 0, (info.depth / 8));
Ditto.
> + dst += (info.depth / 8);
Ditto.
> + }
> +}
--
With Best Regards,
Andy Shevchenko
© 2016 - 2026 Red Hat, Inc.