7 Commits
Author SHA1 Message Date
vhaudiquet c78ba55fd4 video: console: fix scrolling on rotated consoles
For odd rotations (rot=1 and rot=3) the console rows advance along the
physical X axis by the font height (priv->y_charsize): console_move_rows()
and console_set_row() treat a row as fontdata->height physical columns.

But the scroll condition in vidconsole_newline() compared the cursor
position against the font *width* (priv->x_charsize):

    priv->ycur + priv->x_charsize > vid_priv->xsize

The condition is only met at the bottom boundary when
(xsize % font_height) < font_width. With CONFIG_VIDEO_FONT_16X32 on a
1200x1920 panel used with rot=3, xsize % 32 == 16 == font_width, so the
condition is never true at the boundary: the console fills the visible
rows and then keeps drawing past the right screen edge, corrupting the
following scanout line (and past the framebuffer allocation on the last
scanout line), with no visible scrolling.

Use priv->y_charsize in the odd-rotation branch, matching the even-
rotation branch which already correctly compares against the character
height. With this the cursor wraps back to row 0 and scrolling advances
by one row per newline as expected.

Signed-off-by: vhaudiquet <vhaudiquet@localhost>
2026-08-31 12:51:29 +02:00
vhaudiquetandvhaudiquet ac15f4dcbc krane: rotate the console 270 degrees clockwise for landscape use
The BOE TV101WUM-NL6 panel is mounted rotated 270 degrees in the
krane chassis (rotation property on the panel node in the upstream
DT). With the keyboard attached the tablet is used in landscape, so
set video rot=1 and enable CONFIG_CONSOLE_ROTATION: the uclass then
binds the rotated vidconsole1 driver, which writes glyphs rotated by
90 degrees clockwise into the native 1200x1920 portrait scanout and
uses the swapped console geometry (240 columns x 75 rows).

The framebuffer geometry and the depthcharge scanout path are
unchanged; only the console text placement rotates.
2026-08-31 00:19:46 +02:00
vhaudiquetandvhaudiquet 6f79f12858 krane: bind the mtu3 SSUSB container so its xhci child is scanned
DM scans only the immediate subnodes of a bound node; recursion into
children happens when a bound bus driver runs dm_scan_fdt_dev. Nothing
in U-Boot binds the "mediatek,mtu3" ssusb@11201000 node, so its xhci
child (usb@11200000) was never bound, no UCLASS_USB device existed and
usb start() reported "No USB controllers found".

Add "simple-mfd" to the ssusb compatible in the -u-boot.dtsi overlay
so the generic simple-bus driver binds the node and scans its children.
2026-08-31 00:19:46 +02:00
vhaudiquetandvhaudiquet ae757417c8 krane: enable USB xHCI, T-PHY and USB keyboard for the pogo keyboard
The pogo keyboard is a USB HID device behind the GL610 hub on
usb@11200000. Enable the USB stack (USB, DM_USB, USB_XHCI_HCD,
USB_XHCI_MTK, USB_STORAGE, CMD_USB), the MTK T-PHY (PHY_MTK_TPHY),
USB_KEYBOARD and CONFIG_USE_PREBOOT (preboot defaults to "usb start"
with USB_KEYBOARD) so the bus is enumerated before the prompt.

The upstream mt8183.dtsi models the SSUSB block for the Linux mtu3
dual-role driver: the IPPC register space and the phys live on the
parent mtu3 node while the xhci child has only its "mac" register.
U-Boot binds the "mediatek,mtk-xhci" child directly and expects
mac+ippc and a phys list on that node, so add a board
-u-boot.dtsi overlay (auto-included after the upstream dts) that
supplies them. No upstream DT files are modified.

stdin=serial,usbkbd routes the prompt through the USB keyboard once
the usb_kbd stdio device is registered.
2026-08-31 00:19:44 +02:00
vhaudiquetandvhaudiquet 2830892520 krane: override get_page_table_size for dynamic scanout mappings 2026-08-30 23:33:12 +02:00
vhaudiquetandvhaudiquet ed1d014b7b krane: default env routes stdout through vidconsole; env nowhere for payload stage 2026-08-30 23:28:40 +02:00
vhaudiquetandvhaudiquet f7faca4f11 krane: CONFIG_TEXT_BASE=0x4C001000 (8-aligned, keeps _start at image base) 2026-08-30 23:28:38 +02:00
8 changed files with 11 additions and 339 deletions
-6
View File
@@ -194,12 +194,6 @@ void do_bad_error(struct pt_regs *pt_regs)
*/
void do_sync(struct pt_regs *pt_regs)
{
extern void krane_fb_log_abort(unsigned long esr, unsigned long elr);
/* TEMPORARY bring-up diagnostics (krane Round 30) — do not
* upstream: draw the abort registers on the panel. */
krane_fb_log_abort(pt_regs->esr, pt_regs->elr);
if (CONFIG_IS_ENABLED(SEMIHOSTING_FALLBACK) &&
smh_emulate_trap(pt_regs))
return;
+2 -76
View File
@@ -3,80 +3,9 @@
* Copyright (C) 2020 BayLibre SAS
* Author: Fabien Parent <fparent@baylibre.com>
*/
#include <dm.h>
#include <net.h>
/*
* TEMPORARY bring-up diagnostics (krane Round 15) — do not upstream.
* Paint colored bands into the live depthcharge scanout as early-init
* checkpoints: magenta full screen = wrapper (before U-Boot), orange
* top band = board_early_init_f, blue bottom band = dram_init_banksize,
* red top band = board_early_init_r, yellow band = misc_init_r (after
* the banner stage). The last one reached localizes the crash; a
* successful video probe clears the screen for the banner.
*/
#include <cpu_func.h>
#include <fdtdec.h>
#include <asm/io.h>
#include <asm/system.h>
#define DIAG_OVL_L0_ADDR 0x14008f40
#define DIAG_FB_WIDTH 1200
#define DIAG_FB_BPL 4800
#define DIAG_FB_SIZE 0x8ca000
static void diag_paint(u32 y0, u32 rows, u32 color)
{
u32 base = readl((void __iomem *)DIAG_OVL_L0_ADDR);
u32 *p, n = rows * DIAG_FB_WIDTH, i;
if (base < 0x40000000)
return;
if (dcache_status())
mmu_map_region(base, DIAG_FB_SIZE, false);
p = (u32 *)(uintptr_t)(base + (u64)y0 * DIAG_FB_BPL);
for (i = 0; i < n; i++)
p[i] = color;
if (dcache_status())
flush_dcache_range((uintptr_t)p,
(uintptr_t)(p + n));
}
int board_early_init_f(void)
{
diag_paint(0, 100, 0x00ff8000); /* orange */
return 0;
}
void dram_init_banksize(void)
{
fdtdec_setup_memory_banksize();
diag_paint(1820, 100, 0x000000ff); /* blue */
}
int board_early_init_r(void)
{
diag_paint(0, 100, 0x00ff0000); /* red */
return 0;
}
int misc_init_r(void)
{
diag_paint(300, 60, 0x00ffff00); /* yellow */
/* re-assert the backlight in case a probed driver cleared it */
writel(0x800, (void __iomem *)0x10005114);
writel(0x10000, (void __iomem *)0x10005154);
return 0;
}
int board_late_init(void)
{
diag_paint(860, 40, 0x00ff80ff); /* pink: board_late_init done */
return 0;
}
/* end TEMPORARY diagnostics */
/*
* The krane scanout driver maps the coreboot table and the live
* framebuffer dynamically after relocation (mmu_map_region), which the
@@ -88,9 +17,6 @@ u64 get_page_table_size(void)
return 0x40000;
}
#include <dm.h>
#include <net.h>
int board_init(void)
{
struct udevice *dev;
-20
View File
@@ -1074,26 +1074,6 @@ void board_init_f(ulong boot_flags)
{
struct board_f boardf;
/* TEMPORARY bring-up diagnostics (krane Round 22) — do not
* upstream: green band = board_init_f entered (start.S/crt0 and
* the PIE fixup all completed). */
{
u32 base = readl((void __iomem *)0x14008f40);
if (base >= 0x40000000) {
u32 *p = (u32 *)(uintptr_t)(base + 400ULL * 4800);
int i;
if (dcache_status())
mmu_map_region(base, 0x8ca000, false);
for (i = 0; i < 60 * 1200; i++)
p[i] = 0x0000ff00;
if (dcache_status())
flush_dcache_range((uintptr_t)p,
(uintptr_t)(p + 60 * 1200));
}
}
/* end TEMPORARY diagnostics */
gd->flags = boot_flags;
gd->flags &= ~GD_FLG_HAVE_CONSOLE;
gd->boardf = &boardf;
+1 -22
View File
@@ -5,8 +5,7 @@
*/
/* #define DEBUG */
#include <asm/io.h>
#include <asm/system.h>
#include <autoboot.h>
#include <button.h>
#include <bootstage.h>
@@ -45,26 +44,6 @@ void main_loop(void)
{
const char *s;
/* TEMPORARY bring-up diagnostics (krane Round 26) — do not upstream:
* light-gray band = main_loop entered; U-Boot init fully done. */
{
u32 base = readl((void __iomem *)0x14008f40);
if (base >= 0x40000000) {
u32 *p = (u32 *)(uintptr_t)(base + 900ULL * 4800);
int i;
if (dcache_status())
mmu_map_region(base, 0x8ca000, false);
for (i = 0; i < 40 * 1200; i++)
p[i] = 0x00c0c0c0;
if (dcache_status())
flush_dcache_range((uintptr_t)p,
(uintptr_t)(p + 40 * 1200));
}
}
/* end TEMPORARY diagnostics */
bootstage_mark_name(BOOTSTAGE_ID_MAIN_LOOP, "main_loop");
if (IS_ENABLED(CONFIG_VERSION_VARIABLE))
-7
View File
@@ -35,12 +35,6 @@ CONFIG_HUSH_PARSER=y
# CONFIG_CMD_GO is not set
# CONFIG_CMD_IMI is not set
CONFIG_BOOTDELAY=-1
CONFIG_USE_PREBOOT=y
CONFIG_BOARD_EARLY_INIT_F=y
CONFIG_BOARD_EARLY_INIT_R=y
CONFIG_MISC_INIT_R=y
CONFIG_BOARD_LATE_INIT=y
CONFIG_PANIC_HANG=y
# CONFIG_CMD_XIMG is not set
# CONFIG_CMD_EXPORTENV is not set
# CONFIG_CMD_IMPORTENV is not set
@@ -87,7 +81,6 @@ CONFIG_DM_SERIAL=y
CONFIG_DEBUG_UART_ANNOUNCE=y
CONFIG_MTK_SERIAL=y
CONFIG_WDT=y
# CONFIG_WATCHDOG_AUTOSTART is not set
CONFIG_WDT_MTK=y
# CONFIG_RANDOM_UUID is not set
# CONFIG_REGEX is not set
+6 -147
View File
@@ -21,7 +21,6 @@
#include <dm.h>
#include <video.h>
#include <video_font.h> /* Get font data, width and height */
#include <asm/io.h>
#include <asm/system.h>
#include <asm/unaligned.h>
@@ -34,137 +33,18 @@
* src/drivers/video/mtk_ddp.c (ChromeOS R93, the generation shipped on
* kukui); both agree on 0x000c for OVL_EN. The 2L sub-engine enable
* (DISP_REG_OVL0_2L_EN) lives in the same register block on MT8183.
* The layer-0 source address register (DISP_REG_OVL_L0_ADDR) is not reset
* by depthcharge's stop() and holds the live scanout address.
*/
#define DISP_REG_OVL_L0_ADDR 0x0f40
#define DISP_REG_OVL_EN 0x000c
#define DISP_REG_OVL0_2L_EN 0x100c
/* TEMPORARY bring-up diagnostics (krane Round 24) — do not upstream:
* paint a band into the live depthcharge scanout. */
/* TEMPORARY bring-up diagnostics (krane Round 30) — do not upstream:
* minimal framebuffer text logger using the built-in 8x16 font, so
* abort dumps and probe logs are readable on the panel even before
* the console exists. */
static void krane_fb_log_char(const struct video_fontdata *f,
u32 base, u32 x0, u32 y0, char c, u32 color)
{
const u8 *glyph = &f->video_fontdata[(u8)c * f->height];
u32 r, b;
if (c < 32 || c > 126)
c = '?';
glyph = &f->video_fontdata[(u8)c * f->height];
for (r = 0; r < (u32)f->height; r++) {
u32 *row = (u32 *)(uintptr_t)(base +
(u64)(y0 + r) * 4800 + (u64)x0 * 4);
u8 bits = glyph[r];
for (b = 0; b < (u32)f->width; b++)
if (bits & (0x80 >> b))
row[b] = color;
}
}
static void krane_fb_log(const char *s)
{
const struct video_fontdata *f = &fonts[0];
static u32 line, col;
u32 base = readl((void __iomem *)0x14008f40);
if (base < 0x40000000)
return;
if (dcache_status())
mmu_map_region(base, 0x8ca000, false);
for (; *s; s++) {
if (*s == '\n' || col + f->width > 1200) {
line++;
col = 0;
if (*s == '\n')
continue;
}
krane_fb_log_char(f, base, col, 1700 + line * f->height,
*s, 0x00ffffff);
col += f->width;
}
if (dcache_status())
flush_dcache_range((uintptr_t)(base + 1690ULL * 4800),
(uintptr_t)(base + 1920ULL * 4800));
}
static void krane_fb_log_hex(const char *prefix, unsigned long v)
{
char buf[32], *p = buf;
int i;
const char hexdigits[] = "0123456789abcdef";
for (i = 0; prefix[i] && i < 15; i++)
*p++ = prefix[i];
*p++ = '0';
*p++ = 'x';
for (i = (sizeof(v) * 8) - 4; i >= 0; i -= 4)
*p++ = hexdigits[(v >> i) & 0xf];
*p = 0;
krane_fb_log(buf);
krane_fb_log("\n");
}
/* Called from the arm64 abort handlers (temporary wiring in
* arch/arm/lib/interrupts_64.c): draw ESR/ELR/FAR on the panel. */
void krane_fb_log_abort(unsigned long esr, unsigned long elr)
{
unsigned long far, cvel;
asm volatile("mrs %0, CurrentEL" : "=r"(cvel));
switch (cvel >> 2) {
case 1:
asm volatile("mrs %0, far_el1" : "=r"(far));
break;
case 3:
asm volatile("mrs %0, far_el3" : "=r"(far));
break;
default:
asm volatile("mrs %0, far_el2" : "=r"(far));
}
krane_fb_log("\nABORT!\n");
krane_fb_log_hex("ESR=", esr);
krane_fb_log_hex("ELR=", elr);
krane_fb_log_hex("FAR=", far);
}
/*
* Backlight GPIOs. The MT8183 GPIO controller is at 0x10005000; the dout
* block starts at +0x100 with 16 bytes per 32-pin group and set@+4 (layout
* of GpioRegs/GpioValRegs in device-era depthcharge src/drivers/gpio/
* mt8183.h). On kukui the backlight is driven by two dedicated GPIOs:
*
* TEMPORARY bring-up diagnostics (krane Round 24) — do not upstream:
* paint a band into the live depthcharge scanout. */
static void krane_diag_band(u32 y0, u32 rows, u32 color)
{
u32 base = readl((void __iomem *)0x14008f40);
u32 *p, n = rows * 1200, i;
if (base < 0x40000000)
return;
if (dcache_status())
mmu_map_region(base, 0x8ca000, false);
p = (u32 *)(uintptr_t)(base + (u64)y0 * 4800);
for (i = 0; i < n; i++)
p[i] = color;
if (dcache_status())
flush_dcache_range((uintptr_t)p, (uintptr_t)(p + n));
}
/*
* DISP_PWM (pin 43) and EN_LCD_BL (PERIPHERAL_EN13, pin 176); depthcharge's
* kukui_backlight_update() drives both high to turn the backlight on.
*/
@@ -267,14 +147,9 @@ static int mt8183_scanout_probe(struct udevice *dev)
u64 addr;
int ret;
krane_diag_band(500, 40, 0x00ff00ff); /* magenta: probe entered */
krane_fb_log("PROBE");
ovl = dev_read_addr(dev);
if (ovl == FDT_ADDR_T_NONE) {
krane_diag_band(780, 40, 0x00ff0000); /* red: ovl read failed */
if (ovl == FDT_ADDR_T_NONE)
return log_msg_ret("ovl", -EINVAL);
}
/*
* Revive the pipeline: undo depthcharge's display_cleanup() by
@@ -288,33 +163,21 @@ static int mt8183_scanout_probe(struct udevice *dev)
(void __iomem *)MTK_GPIO_DOUT_SET(PAD_DISP_PWM));
writel(MTK_GPIO_DOUT_BIT(PAD_EN_LCD_BL),
(void __iomem *)MTK_GPIO_DOUT_SET(PAD_EN_LCD_BL));
krane_diag_band(520, 20, 0x0000ff80); /* mint: revival writes done */
krane_fb_log(" REVIVE");
/*
* The coreboot table sits above the DRAM window described by the
* control DTB; map it before parsing.
*/
mmu_map_region(COREBOOT_TABLE_ADDR, SZ_4K, false);
krane_diag_band(540, 20, 0x008000ff); /* violet: table mapped */
krane_fb_log(" MAPTBL");
ret = find_framebuffer(COREBOOT_TABLE_ADDR, &fb);
if (ret) {
krane_diag_band(800, 40, 0x00ff0000); /* red: lbio failed */
if (ret)
return log_msg_ret("lbio", ret);
}
krane_diag_band(560, 20, 0x00ffff80); /* pale yellow: LBIO found */
krane_fb_log(" LBIO");
if (fb.bits_per_pixel != 32 || fb.red_pos != 16 || fb.red_size != 8 ||
fb.green_pos != 8 || fb.green_size != 8 ||
fb.blue_pos != 0 || fb.blue_size != 8) {
krane_diag_band(820, 40, 0x00ff0000); /* red: fmt mismatch */
fb.blue_pos != 0 || fb.blue_size != 8)
return log_msg_ret("fmt", -ENOTSUPP);
}
krane_diag_band(580, 20, 0x0080ffff); /* pale cyan: fmt OK */
krane_fb_log(" FMT");
/*
* Use the address from the coreboot table when it is a plausible
@@ -326,12 +189,8 @@ static int mt8183_scanout_probe(struct udevice *dev)
if (addr < SZ_1G)
addr = readl(ovl + DISP_REG_OVL_L0_ADDR);
if (addr < SZ_1G) {
krane_diag_band(840, 40, 0x00ff0000); /* red: no scanout addr */
if (addr < SZ_1G)
return log_msg_ret("scanout", -ENODEV);
}
krane_diag_band(620, 40, 0x00aaaaaa); /* gray: geometry resolved */
krane_fb_log(" GEO");
plat->base = addr;
plat->size = fb.bytes_per_line * fb.y_resolution;
+1 -1
View File
@@ -95,7 +95,7 @@ static void vidconsole_newline(struct udevice *dev)
/* Check if we need to scroll the terminal */
if (vid_priv->rot % 2 ?
priv->ycur + priv->x_charsize > vid_priv->xsize :
priv->ycur + priv->y_charsize > vid_priv->xsize :
priv->ycur + priv->y_charsize > vid_priv->ysize) {
vidconsole_move_rows(dev, 0, rows, priv->rows - rows);
for (i = 0; i < rows; i++)
+1 -60
View File
@@ -18,8 +18,6 @@
#include <video.h>
#include <video_console.h>
#include <asm/cache.h>
#include <asm/io.h>
#include <asm/system.h>
#include <asm/global_data.h>
#include <dm/lists.h>
#include <dm/device_compat.h>
@@ -632,35 +630,12 @@ void video_set_white_on_black(struct udevice *dev, bool white_on_black)
}
}
/* TEMPORARY bring-up diagnostics (krane Round 20) — do not upstream:
* paint a band into the live depthcharge scanout. */
static void krane_diag_band(u32 y0, u32 rows, u32 color)
{
u32 base = readl((void __iomem *)0x14008f40);
u32 *p, n = rows * 1200, i;
if (base < 0x40000000)
return;
if (dcache_status())
mmu_map_region(base, 0x8ca000, false);
p = (u32 *)(uintptr_t)(base + (u64)y0 * 4800);
for (i = 0; i < n; i++)
p[i] = color;
if (dcache_status())
flush_dcache_range((uintptr_t)p, (uintptr_t)(p + n));
}
/* end TEMPORARY diagnostics */
/* Set up the display ready for use */
static int video_post_probe(struct udevice *dev)
{
struct video_uc_plat *plat = dev_get_uclass_plat(dev);
struct video_uc_priv *uc_priv = uclass_get_priv(dev->uclass);
struct video_priv *priv = dev_get_uclass_priv(dev);
char name[30], drv[15], *str;
const char *drv_name = drv;
struct udevice *cons;
@@ -699,14 +674,7 @@ static int video_post_probe(struct udevice *dev)
priv->white_on_black = CONFIG_IS_ENABLED(SYS_WHITE_ON_BLACK);
if (!CONFIG_IS_ENABLED(NO_FB_CLEAR))
video_clear(dev);
/* TEMPORARY bring-up diagnostics (krane Round 20) — do not upstream:
* cyan band right after the clear: if visible, the clear and the
* fb mapping work and death happened later in this function. */
krane_diag_band(260, 60, 0x00ffff00);
/* end TEMPORARY diagnostics */
/* Set up colors */
video_set_default_colors(dev, false);
if (!CONFIG_IS_ENABLED(NO_FB_CLEAR))
@@ -770,39 +738,12 @@ static int video_post_probe(struct udevice *dev)
uc_priv->cyc_active = true;
}
/* TEMPORARY bring-up diagnostics (krane Round 20) — do not upstream:
* paint a white band once the vidconsole is up, so a death between
* the framebuffer clear and the banner can be localized. */
krane_diag_band(200, 60, 0x00ffffff);
/* end TEMPORARY diagnostics */
return 0;
};
/* Post-relocation, allocate memory for the frame buffer */
static int video_post_bind(struct udevice *dev)
{
/* TEMPORARY bring-up diagnostics (krane Round 24) — do not upstream:
* purple band = a video device got bound (driver matched the DT
* node); runs before any probe. */
{
u32 base = readl((void __iomem *)0x14008f40);
if (base >= 0x40000000) {
u32 *p = (u32 *)(uintptr_t)(base + 470ULL * 4800);
int i;
if (dcache_status())
mmu_map_region(base, 0x8ca000, false);
for (i = 0; i < 30 * 1200; i++)
p[i] = 0x008000ff;
if (dcache_status())
flush_dcache_range((uintptr_t)p,
(uintptr_t)(p + 30 * 1200));
}
}
/* end TEMPORARY diagnostics */
struct video_uc_priv *uc_priv;
ulong addr;
ulong size;