ODROID-M1 dedicated target: hardkernel BSP kernel + Mali-G52 blob + VU8M overlays - #1
Open
Pyohwan wants to merge 396 commits into
Open
ODROID-M1 dedicated target: hardkernel BSP kernel + Mali-G52 blob + VU8M overlays#1Pyohwan wants to merge 396 commits into
Pyohwan wants to merge 396 commits into
Conversation
Pyohwan
marked this pull request as ready for review
July 25, 2026 09:28
Pyohwan
added a commit
that referenced
this pull request
Jul 25, 2026
…ion order)
The SD card now shows up as "batocera.linux" in the on-board SPI-NOR
Petitboot menu on its own, and booting that entry kexecs into our real
6.1 kernel through to EmulationStation - verified end to end on hardware.
Three things were needed, each of which had been broken by an earlier
commit in this branch that judged them dead from our own boot chain's
point of view only:
- boot.scr had to exist at all. Petitboot's parser handles boot.ini /
boot.scr / kboot.conf / grub.cfg and explicitly not extlinux.conf (per
the ODROID-M1 Petitboot author on the forum), so removing boot.cmd as
"dead code" silently removed the only thing Petitboot could recognise.
It is now generated from boot.cmd at build time with mkimage instead of
being a committed binary, so the two can't drift apart again.
- The load commands must not carry a ":<part>" device suffix. Petitboot
only defers resolution to another device when the resource string
contains a colon (upstream create_devpath_resource()); with
"${devnum}:${partition}" it substituted a device name but got the
partition index wrong and every resource stayed unresolved forever
(pb-discover.log: "resource depends on device /dev/mmcblk1p1" x3, then
"boot option mmcblk1p2#batocera.linux is unresolved"). Without the
colon it resolves against the partition being scanned, which is right.
This matches Armbian's own Petitboot-compatible boot.scr.
- The boot partition has to be #1, so U-Boot no longer takes a GPT table
slot. That GPT entry was added earlier for the vendor wiki's sake and
UART capture had already shown SPL locates the FIT purely by fixed
sector offset, so it bought nothing - and cost the partition ordering
Petitboot effectively depends on. Layout is back to vanilla Batocera's.
Nothing here changes direct SD boot: bootstd still picks extlinux.conf
first (confirmed by UART), so the VU8M DEFAULT-label toggle keeps working
as before. Booting via Petitboot always uses the base DTB (kexec has no
FDTOVERLAYS equivalent), i.e. HDMI-only - documented in boot.cmd.
Pyohwan
added a commit
that referenced
this pull request
Jul 25, 2026
… need SPL needs U-Boot in a GPT entry literally named "uboot" (locates it by label, per Hardkernel's wiki - not "SPL doesn't care about the partition table", which an earlier commit on this branch mistakenly concluded from seeing "Trying fit image at 0x4000 sector" without checking what happens if the named entry is absent). Petitboot's device scan only finds a bootable OS on partition #1 (confirmed live: with the boot partition at #2, entries are parsed correctly - "boot option ... is unresolved" is gone - but Petitboot still lists nothing at all). Both are satisfied simultaneously by declaring the boot partition first in genimage.cfg: genimage numbers GPT entries by declaration order, not by on-disk offset (confirmed by reading genimage's own overlap-checking code, which only rejects actual overlaps). So the boot partition becomes GPT #1 for Petitboot while "uboot" keeps its own named entry (now #3) at its original 8M offset for the SPL. Physical layout on the card is unchanged either way. Verified end to end on hardware in this exact image: SPL finds u-boot.itb fine at GPT #3 ("Trying fit image at 0x4000 sector" -> all atf/fdt checks OK), U-Boot's bootstd sees the boot partition as "part 1" and boots extlinux.conf's "batocera-vu8m" label correctly (VU8M active, correctly rotated), and Petitboot lists and boots "batocera.linux" from the same card. All three requirements hold in the same image at last.
Pyohwan
added a commit
that referenced
this pull request
Jul 26, 2026
…ion order)
The SD card now shows up as "batocera.linux" in the on-board SPI-NOR
Petitboot menu on its own, and booting that entry kexecs into our real
6.1 kernel through to EmulationStation - verified end to end on hardware.
Three things were needed, each of which had been broken by an earlier
commit in this branch that judged them dead from our own boot chain's
point of view only:
- boot.scr had to exist at all. Petitboot's parser handles boot.ini /
boot.scr / kboot.conf / grub.cfg and explicitly not extlinux.conf (per
the ODROID-M1 Petitboot author on the forum), so removing boot.cmd as
"dead code" silently removed the only thing Petitboot could recognise.
It is now generated from boot.cmd at build time with mkimage instead of
being a committed binary, so the two can't drift apart again.
- The load commands must not carry a ":<part>" device suffix. Petitboot
only defers resolution to another device when the resource string
contains a colon (upstream create_devpath_resource()); with
"${devnum}:${partition}" it substituted a device name but got the
partition index wrong and every resource stayed unresolved forever
(pb-discover.log: "resource depends on device /dev/mmcblk1p1" x3, then
"boot option mmcblk1p2#batocera.linux is unresolved"). Without the
colon it resolves against the partition being scanned, which is right.
This matches Armbian's own Petitboot-compatible boot.scr.
- The boot partition has to be #1, so U-Boot no longer takes a GPT table
slot. That GPT entry was added earlier for the vendor wiki's sake and
UART capture had already shown SPL locates the FIT purely by fixed
sector offset, so it bought nothing - and cost the partition ordering
Petitboot effectively depends on. Layout is back to vanilla Batocera's.
Nothing here changes direct SD boot: bootstd still picks extlinux.conf
first (confirmed by UART), so the VU8M DEFAULT-label toggle keeps working
as before. Booting via Petitboot always uses the base DTB (kexec has no
FDTOVERLAYS equivalent), i.e. HDMI-only - documented in boot.cmd.
Pyohwan
added a commit
that referenced
this pull request
Jul 26, 2026
… need SPL needs U-Boot in a GPT entry literally named "uboot" (locates it by label, per Hardkernel's wiki - not "SPL doesn't care about the partition table", which an earlier commit on this branch mistakenly concluded from seeing "Trying fit image at 0x4000 sector" without checking what happens if the named entry is absent). Petitboot's device scan only finds a bootable OS on partition #1 (confirmed live: with the boot partition at #2, entries are parsed correctly - "boot option ... is unresolved" is gone - but Petitboot still lists nothing at all). Both are satisfied simultaneously by declaring the boot partition first in genimage.cfg: genimage numbers GPT entries by declaration order, not by on-disk offset (confirmed by reading genimage's own overlap-checking code, which only rejects actual overlaps). So the boot partition becomes GPT #1 for Petitboot while "uboot" keeps its own named entry (now #3) at its original 8M offset for the SPL. Physical layout on the card is unchanged either way. Verified end to end on hardware in this exact image: SPL finds u-boot.itb fine at GPT #3 ("Trying fit image at 0x4000 sector" -> all atf/fdt checks OK), U-Boot's bootstd sees the boot partition as "part 1" and boots extlinux.conf's "batocera-vu8m" label correctly (VU8M active, correctly rotated), and Petitboot lists and boots "batocera.linux" from the same card. All three requirements hold in the same image at last.
Pyohwan
force-pushed
the
odroid-m1-bsp-kernel
branch
from
July 26, 2026 08:21
af639ea to
e1efa96
Compare
Pyohwan
added a commit
that referenced
this pull request
Jul 27, 2026
…ion order)
The SD card now shows up as "batocera.linux" in the on-board SPI-NOR
Petitboot menu on its own, and booting that entry kexecs into our real
6.1 kernel through to EmulationStation - verified end to end on hardware.
Three things were needed, each of which had been broken by an earlier
commit in this branch that judged them dead from our own boot chain's
point of view only:
- boot.scr had to exist at all. Petitboot's parser handles boot.ini /
boot.scr / kboot.conf / grub.cfg and explicitly not extlinux.conf (per
the ODROID-M1 Petitboot author on the forum), so removing boot.cmd as
"dead code" silently removed the only thing Petitboot could recognise.
It is now generated from boot.cmd at build time with mkimage instead of
being a committed binary, so the two can't drift apart again.
- The load commands must not carry a ":<part>" device suffix. Petitboot
only defers resolution to another device when the resource string
contains a colon (upstream create_devpath_resource()); with
"${devnum}:${partition}" it substituted a device name but got the
partition index wrong and every resource stayed unresolved forever
(pb-discover.log: "resource depends on device /dev/mmcblk1p1" x3, then
"boot option mmcblk1p2#batocera.linux is unresolved"). Without the
colon it resolves against the partition being scanned, which is right.
This matches Armbian's own Petitboot-compatible boot.scr.
- The boot partition has to be #1, so U-Boot no longer takes a GPT table
slot. That GPT entry was added earlier for the vendor wiki's sake and
UART capture had already shown SPL locates the FIT purely by fixed
sector offset, so it bought nothing - and cost the partition ordering
Petitboot effectively depends on. Layout is back to vanilla Batocera's.
Nothing here changes direct SD boot: bootstd still picks extlinux.conf
first (confirmed by UART), so the VU8M DEFAULT-label toggle keeps working
as before. Booting via Petitboot always uses the base DTB (kexec has no
FDTOVERLAYS equivalent), i.e. HDMI-only - documented in boot.cmd.
Pyohwan
added a commit
that referenced
this pull request
Jul 27, 2026
… need SPL needs U-Boot in a GPT entry literally named "uboot" (locates it by label, per Hardkernel's wiki - not "SPL doesn't care about the partition table", which an earlier commit on this branch mistakenly concluded from seeing "Trying fit image at 0x4000 sector" without checking what happens if the named entry is absent). Petitboot's device scan only finds a bootable OS on partition #1 (confirmed live: with the boot partition at #2, entries are parsed correctly - "boot option ... is unresolved" is gone - but Petitboot still lists nothing at all). Both are satisfied simultaneously by declaring the boot partition first in genimage.cfg: genimage numbers GPT entries by declaration order, not by on-disk offset (confirmed by reading genimage's own overlap-checking code, which only rejects actual overlaps). So the boot partition becomes GPT #1 for Petitboot while "uboot" keeps its own named entry (now #3) at its original 8M offset for the SPL. Physical layout on the card is unchanged either way. Verified end to end on hardware in this exact image: SPL finds u-boot.itb fine at GPT #3 ("Trying fit image at 0x4000 sector" -> all atf/fdt checks OK), U-Boot's bootstd sees the boot partition as "part 1" and boots extlinux.conf's "batocera-vu8m" label correctly (VU8M active, correctly rotated), and Petitboot lists and boots "batocera.linux" from the same card. All three requirements hold in the same image at last.
Pyohwan
force-pushed
the
odroid-m1-bsp-kernel
branch
from
July 27, 2026 12:32
1d68de7 to
d9476a7
Compare
…t.cfg Adds a named inputConfig entry for "SHAKS S6b 52e1 Win-Mac" (hotkey=select, matching batocera's own convention) so this pad works out of the box on a fresh reflash instead of requiring the user to redo "CONFIGURE INPUT" every time /userdata gets wiped. Mapping copied verbatim from the live-configured /userdata/system/configs/emulationstation/es_input.cfg on the device.
BIOS lives only on the SD card today (plain directory on mmcblk1p2, never unioned with anything) - every reflash wipes it entirely. This bit us 2026-08-04: a prior reflash's BIOS restore only copied back 2 of 26 files from the manual NVMe backup, silently breaking neogeo (missing neogeo.zip -> FBNeo couldn't find sp-s3.sp1) and leaving several other systems' BIOS missing too (PSX, Saturn, GBA/GB, PC Engine CD, ...). New dedicated init script (kept separate from batocera-storage-manager, which hardcodes a single roms-only pool throughout and would be riskier to extend given BIOS is a launch-time dependency for every emulator) mirrors the same category.search=newest mergerfs union pattern already used for roms: .bios_base (SD, mostly empty after first boot) + /media/ROMs/bios (NVMe, where the actual content now lives) merged onto /userdata/bios. On first run, migrates whatever is currently sitting directly in /userdata/bios onto the NVMe branch (not .bios_base) - the SD-card side needs to end up empty, otherwise the content would just get wiped by the next reflash exactly as before, defeating the point. Live-tested on device: mount succeeds, neogeo.zip (and everything else) readable through the merged path, migration correctly landed content on NVMe (df shows /userdata/bios backed by the 510G pool, not the 53G SD card), idempotent on re-run (no stacked mounts).
mergerfs earns its complexity when two branches both hold real, coexisting content that needs merging (roms' .roms_base factory-demo content alongside the real NVMe library). BIOS has no such split by design - the SD-card side is meant to stay empty and NVMe holds everything - so a FUSE union with category.search/category.create policy was solving a problem that doesn't exist here. A plain `mount --bind` does the same job (NVMe's /media/ROMs/bios transparently appears at /userdata/bios, any future write lands directly on NVMe) with no FUSE process and no policy to get wrong. Live-tested on device: unmounted the previous mergerfs union, redeployed, confirmed `mount` shows a straight bind of nvme0n1p3 rather than a fuse.* entry, neogeo.zip still readable through the bind path, idempotent on re-run (no stacked mounts).
…/fbneo) pcsx_rearmed is CPU-bound (dynarec + GTE) like FBNeo, not GPU-bound like N64 - live tests showed the same "interactive" governor lagging under load: Beatmania ~44fps, Soul Blade mostly 59fps with occasional drops to 44fps. Same fix as n64.powermode/fbneo.powermode: pin CPU to "performance" during gameplay only via the existing powermode_launch_hooks.sh mechanism.
configgen's global default (configgen-defaults.yml) is hud_support: true with no n64-specific override anywhere upstream, so every rk3568 board inherits mangohud wrapping mupen64plus by default. The Mali blob doesn't implement whatever EGL extension function mangohud's inject_egl hooks need, so SDL_CreateWindow fails outright inside mupen64plus - audio plays, video never comes up (confirmed on ODROID-M1 with both g13p0 and g24p0 blobs). This was previously worked around live via SSH (n64.hud_support=0 in /userdata/system/batocera.conf) but never actually persisted anywhere, so it silently regressed on every SD-card reflash since - same class of mistake as S19fbneokorean's missing +x bit. Fixed properly this time in configgen-defaults-rk3568.yml (a real per-architecture defaults file already used for the same kind of fix - see the adjacent psx hud_support:false entry, "duckstation doesn't like it on n2+" - not a live/SSH-only workaround this time), so it applies to any rk3568 board, not just this one. Live-tested via batocera-settings-set n64.hud_support false for immediate verification without waiting on a rebuild.
User-confirmed live via per-game ADVANCED GAME OPTIONS override on New Super Mario Bros. (Korea) - noticeably less stutter with CPU pinned to performance during gameplay. Same underlying cause as n64/fbneo/psx (CPU-bound emulation, "interactive" governor lags behind load), made system-wide since it's not specific to that one game.
…ode menu instead n64/fbneo/psx/nds.powermode=highperformance were all measured and confirmed live, but a system-specific override always wins over ES's own Game Settings > Power Mode (global) - user wants a single global knob instead of needing to touch source for every system that benefits. Measurements kept in Joplin in case per-system defaults are wanted again.
…cera-linux#16176) `gosu` resets `HOME` from the first `/etc/passwd` entry matching the target UID. Ubuntu images include an "ubuntu" user at UID 1000, and `useradd -o` adds another entry rather than replacing it, so explicitly restore `HOME` after `gosu` resolves the target user.
sync BR for pygobject 3.56
Qt defaulted to xcb and aborted (Aborted signal 6) - this board has neither X11 nor a working Wayland-client EGL path (vendor blob bug, see board defconfig). ENABLE_WAYLAND=ON here was wrong since it was written before that bug was documented. eglfs (direct KMS/GBM, confirmed working) is correct, needs libinput for its KMS input backend. Live-tested: window/game boot now works end to end (DRM mode switch to 1920x1080 confirmed, no GPU faults). Does NOT fix performance - melonDS's own 3D renderer still logs "Failed to create OpenGL context, falling back to Software Renderer" and self-persists that fallback to melonDS.toml on every run, because it wants desktop GL like the old libretro-melonds core did and this blob only has GLES. Software-only 3D is too slow to play on this CPU. Whether melonDS ever gets GPU accel here now depends entirely on the planned Panfrost A/B test.
Generator assumed every target has Wayland or X11/XWayland and defaulted to xcb otherwise - boards with neither (headless KMS/DRM direct, like odroid-m1) got Qt aborting immediately with no platform plugin available. Same failure class just fixed for standalone melonDS.
…ate cores Both were excluded by upstream select conditions that don't reflect real technical requirements for this board: - kronos: gated to x86 only, but its own .mk already supports several ARM+GLES boards (odroid-xu4/n2, rockpro64, odroid-c4) via FORCE_GLES=1 - added an RK3568 branch reusing the odroid-c4 mapping. Untested on real hardware, yabasanshiro stays the default fallback. - libretro-melonds-ds: gated behind BR2_PACKAGE_BATOCERA_XWAYLAND, which is off on this board for an unrelated reason (Wayland-client EGL is broken on this Mali blob, see board defconfig comment) - melonds-ds's actual deps (cmake/libpcap/libstdc++) don't need XWayland at all. The already- built libretro-melonds is confirmed dead on this blob (wants desktop GL, board only has GLES); melonds-ds has a GL-free Software render mode worth trying. Neither confirmed working yet - next build should surface whether they even compile for this target, then need real hardware testing.
libretro-kronos unconditionally depends on full mesa3d, which buildroot refuses to combine with mesa3d-headers (our vendor blob's header source). Needs kronos.mk patched before this can be retried - keeping melonds-ds (no such conflict) enabled for tonight's build.
qt6wayland is no longer needed now that ENABLE_WAYLAND=OFF (melonds.mk); libinput is needed for the eglfs platform plugin's KMS backend and was already added to MELONDS_DEPENDENCIES but never select'd here, which buildroot's Kconfig consistency check rejects outright.
…re real system folders for merge prompts Two related fixes, both confirmed live on the 4-partition NVMe drive that exposed them: 1. REQUEST_MERGE sent the *disk's* unique id (get_unique_id, serial/WWN- based) for its 'ignore forever' button, not the partition's. A drive with several partitions - two leftover EmuELEC install partitions alongside the real ROMs partition - meant permanently ignoring either junk partition silently also blocked the real ROMs partition's merge on every subsequent boot, including fresh SD reflashes where the 'already configured, keep it' guard (added earlier for the reboot case) doesn't apply yet since mergerfs.roms hasn't been written at all. Added get_partition_unique_id (filesystem UUID/PARTUUID-based) and send that in the notification instead - no ES-side change needed, Window.cpp just echoes back whatever id it was given via ignoreDevicePermanently(). REQUEST_FORMAT (whole-disk, no partition table yet) is correctly left disk-scoped. 2. The merge-candidate check was just "does a roms/ directory exist", so leftover install partitions with their own unrelated roms/ folder (in this case, EmuELEC's own roms layout) got prompted too. Added is_valid_roms_dir: requires at least one subdirectory matching a real batocera system folder, checked against the on-device skeleton (/usr/share/batocera/datainit/roms/<system>) rather than a hardcoded list. Live-verified on hardware: p1/p2 (EmuELEC leftovers) correctly rejected and stay individually ignored; p3 (real ROMs partition) no longer inherits their ignore status.
… with no compositor/X11, disable mangohud on rk3568 flycast systems
SDL2 auto-probes video (wayland->x11->...) and audio (pipewire needs
XDG_RUNTIME_DIR) drivers with no fallback for compositor-less KMS/DRM-only
boards, so standalone flycast failed outright on ODROID-M1 ("Can't load
EGL/GL library on window creation", audio silently disabled). Same
generic fix pattern as duckstationGenerator's earlier eglfs fallback
(cfc6624), applied to flycast's SDL_VIDEODRIVER/SDL_AUDIODRIVER/
XDG_RUNTIME_DIR instead of Qt's QT_QPA_PLATFORM.
Also hit the same mangohud+Mali-blob EGL crash already fixed for n64
(b1f1989) once the driver detection was fixed - extended that
configgen-defaults-rk3568.yml hud_support:false pattern to dreamcast/
naomi/atomiswave/systemsp (all share the flycast core).
Live-verified on-device via scp'd configgen + batocera-settings-set
before committing: video renders and audio plays with no mangohud crash.
fix pyright issue
- due to boot & i/o failures
move gameforce ace back to the 6.1 kernel
Follow-up to d05f5c6. User feedback: before we actually cut a tagged release, keep batocera.version identical to upstream's own dev-build identifier ("44-dev-<hash> <date>") rather than rebranding every untagged build as "batocera44-odroidm1-dev-<hash>". The missing commit hash that started this whole investigation was purely the git-worktree docker mount bug (fixed in 520d806) - upstream's format was never broken, our build environment just couldn't populate it. Our own release-tag format (batocera44-odroidm1-vX.Y.Z) still kicks in correctly once HEAD is exactly on one of those tags - that part is unchanged, since that's a genuine, deliberate identifier for an actual release, not a rebrand of ordinary dev builds. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01F4zVPhdz6gmjHPNpGGTQei
Live-verified auto core/API selection against the benchmarked table (project_odroid_retro.md) game-by-game. Genesis/FBNeo/Saturn/PSX all matched. N64 didn't: it launched glide64mk2 instead of rice. Root cause: ES's "auto" choice isn't determined by es_systems.yml's core listing order - it's computed by comparing each (emulator, core) pair against this board's configgen-defaults emulator/core keys (see python-src/batocera-es-system's registry.py, get_systems_metadata). The earlier "auto order" fix (mentioned in project_odroid_retro.md) reordered es_systems.yml's n64 core list, which has zero effect on that comparison - the actual default (mupen64plus + glide64mk2) was never overridden here, so it fell through to the shared configgen-defaults.yml value the whole time. Auditing the same mechanism for the other benchmarked standalone-emulator systems found two more real mismatches (not yet live-tested, but the same class of bug, confirmed via source inspection): - gamecube/wii: emulator was "libretro" (copied verbatim from the shared configgen-defaults-rk3568.yml), which picks libretro-dolphin - not the standalone dolphin (emulator: dolphin) this board actually benchmarks and needs for the Wayland-only OpenGL fix. dolphinGenerator.py already defaults to Vulkan unless gfxbackend=OGL is set, so no extra option needed for the API side. - dreamcast: no override at all, falling through to the shared file's libretro-flycast default instead of standalone flycast. Also added flycast_renderer=4 (forces Vulkan) since flycastGenerator.py's own default is OpenGL, unlike dolphin/ppsspp which already default to Vulkan. psp needed no change: ppssppConfig.py already defaults to "3 (VULKAN)" when gfxbackend is unset, matching the benchmark recommendation as-is. Not yet live-verified on hardware (n64 was; gamecube/wii/dreamcast weren't reached yet in the ongoing verification pass) - flagged in project_odroid_retro.md for the next round of testing. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01F4zVPhdz6gmjHPNpGGTQei
Continuation of the auto core/API verification pass (n64/gamecube/wii/ dreamcast fixed earlier). PS2's auto default was set to pcsx2, which is the core we already determined is structurally broken on this board's Mali blob (DATA_INVALID_FAULT GPU faults - see project_odroid_retro.md's "Mali G52 blob 구조적 한계" section). Confirmed live: auto silently launched pcsx2, dmesg showed "GPU fault 0x5b from job slot 0", and the game visibly hung. Switched the default to aethersx2 (the actually-recommended, working choice). pcsx2 stays installed/selectable in ES as a fallback, just no longer the silent default that most users would hit. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01F4zVPhdz6gmjHPNpGGTQei
…doring Follow-up to 4dc8fe5 (PS2 auto default fix). Live-testing that fix surfaced a real launch failure: "libXrandr.so.2: cannot open shared object file" - a leftover gap from AetherSX2's prebuilt-binary NEEDED list that nothing had exercised before, since PS2's auto default had been silently pointing at pcsx2 this whole time. Initially vendored libXrandr.so.2 + its libXext/libXrender/libX11 transitive closure from Debian arm64 packages (same technique already used for the GL/GLX/curl/openssl shims in aethersx2/libs/), but per feedback: unlike those, there's no ABI/missing-feature reason libX11's family can't just be built normally via buildroot's own xlib_libX11/ libXext/libXrender/libXrandr packages - so build them for real instead. Reverted the vendored copies, added a select chain to aethersx2's Config.in instead (same flattening pattern as melonds/Config.in - these xlib_* Config.in entries all live inside "if BR2_PACKAGE_XORG7", so a select landing on one of them doesn't reliably cascade to that symbol's own further selects). Confirmed live: libXrandr/libXext/libXrender/libX11 all built successfully via buildroot and installed into the standard /usr/lib, which aethersx2Generator.py's LD_LIBRARY_PATH already covers - no changes needed there. No performance impact: aethersx2Generator.py forces QT_QPA_PLATFORM= wayland, so the Qt xcb platform plugin path that actually pulls libXrandr in at runtime (display mode/refresh-rate queries) is never taken - same "NEEDED-pass only" story as the GL/GLX shims that already exist in aethersx2/libs/. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01F4zVPhdz6gmjHPNpGGTQei
Convenience fix requested during VU8M setup testing: enabling VU8M (overlays=display_vu8m) already requires editing config.ini, which is the one file on the PC-visible FAT32 boot partition. batocera.conf (the "correct" place for display.rotate) lives on the userdata partition (ext4, not readable from a plain Windows/Mac file browser), and batocera-boot.conf - while technically fine to hand-edit here (nothing regenerates it wholesale; batocera-config only ever amends specific keys like sharedevice/rootshadowpassword via targeted sed, never display.rotate) - is a second file to open for no real reason when config.ini is sitting right there. config.ini is checked first, ahead of the existing batocera-boot.conf path, so setting VU8M up (enable + rotate) is now a single-file edit. Verified live: "Rotation from config.ini: [1]" logged, correct 800x1280 crop and -o 1 fbv rotation applied. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01F4zVPhdz6gmjHPNpGGTQei
…rd README batocera-backglass-window.cpp's font candidate lists (both header and description) only ever contained DejaVu Sans / Liberation Sans - both Latin-only, so any Korean text (game titles, backglass metadata) rendered as missing-glyph boxes on the VU8M backglass screen. This is a narrow gap in an otherwise Korean-font-aware system: ES itself already ships a bundled Korean fallback font (PyeojinGothic-Medium, compiled into the ES binary as a resource), and this board already has Noto Sans KR + NanumGothic installed as real font files (via the vanilla upstream nanum-font package, unrelated to our fork) - backglass's own hardcoded candidate lists just never got updated to include either. Prepended NotoSansKR-VF.ttf (checked first) to both candidate lists - it's a variable font with full Latin+CJK coverage in one file, so one font renders both scripts consistently (SDL_ttf can't font-switch mid-string anyway). Falls back to the existing DejaVu/Liberation chain if Noto isn't present. Compiled and deployed live; full visual confirmation still pending (testing got sidetracked investigating an unrelated HDMI+VU8M simultaneous-connection issue - see below). Also adds README-odroidm1.md: repo-root, Korean, covers what's added over vanilla Batocera, VU8M/HDMI display setup (including the config.ini single-file rotation trick from the commit below), and the full per-system performance/auto-core table. Iterated heavily with the user this session - still being refined, not final. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01F4zVPhdz6gmjHPNpGGTQei
…anism Live testing on-device found that S65values4boot already syncs display.rotate/display.rotate.<connector> from batocera.conf into batocera-boot.conf on every shutdown/reboot (vanilla mechanism) - the config.ini duplication added in 58fcc13 was unnecessary, since the boot splash's existing batocera-boot.conf fallback already picks up the correct value with no extra config needed. Also fixes the real bug behind this session's dual-display report: VU8M rotation is controlled by global.videooutput2/display.rotate2.<connector> in batocera.conf (set via the ES MULTISCREENS menu, or directly with batocera-settings-set), not by anything in batocera-boot.conf or config.ini - those only ever affect the primary output. Verified live: global.videooutput2=DSI-1 + display.rotate2.DSI-1=3 (270 degrees) gives correct portrait orientation on VU8M. README updated to describe the actual mechanism instead of the config.ini workaround.
…README 270 degrees was this particular panel's measured correct orientation, not a general fact about VU8M - actual mounting can vary per unit.
- Remove the standalone "boot partition file editing" section - stale instructions, redundant with each subsection already saying where its own file lives. - Shrink the HDMI+VU8M "not recommended together" caveat down to the one hardware fact that's still true (DSI has no hotplug detection) - today's live testing showed no actual splash-rotation problem once global.videooutput2 is set correctly, so the earlier elaborate warning overstated a non-issue. - Rename "화면 회전" to "화면 회전값 설정" and drop the global.videooutput2 example from it (that's the previous section's concern, not rotation) - keep only rotation-specific keys/commands here.
…ass/rotation section
- Move the SHARE-partition-init caveat to the top of the section (it's a precondition for everything below, not an afterthought). - Split the reboot out of the settings command block with a one-line explanation: EmulationStation only reads/applies videooutput2 and rotation at its own startup, so a plain batocera-settings-set has no effect until the next boot.
Both the primary and backglass screens have an option literally labeled "SCREEN ROTATION", which is confusing without their full menu paths (confirmed against GuiMenu.cpp): SYSTEM SETTINGS > HARDWARE for the main screen, vs. SYSTEM SETTINGS > HARDWARE > MULTISCREENS > BACKGLASS / INFORMATION SCREEN for VU8M.
Same upstream snapshot merged into the C track (odroid-m1-g29p1, commit cbeaa11) on 2026-08-17, applied here to odroid-m1-bsp-kernel so PR #4's diff reflects genuine A-vs-C track differences instead of upstream drift. Merged with --allow-unrelated-histories (upstream's 2026-08-04 history squash has no shared ancestor with this fork). 243 conflicted files. 228 were files this branch never customized - resolved by taking origin's version wholesale (git checkout --theirs). The other 14 (this board's own S02resize/audio-config/power-mode/ Ssplash-mpv/es_input.cfg/emulationstation-standalone/storage-manager/ melonDS/libretro-core-info/libretro-kronos/batocera.conf fixes) were reconciled via git merge-file: base = the file's content immediately before this branch's own customizing commit, ours = this branch's current version, theirs = origin's new version - applies this branch's own diff cleanly on top of upstream's latest instead of blindly preferring either side. All 14 merged with zero remaining conflict markers except melonds.mk, whose 2 conflicting hunks (X11/libinput dependency gate, ENABLE_WAYLAND rationale comment) are pure odroid-m1 board-specific additions absent from both base and origin - resolved by keeping this branch's version. buildroot submodule conflict resolved by keeping this branch's own pin (fork/odroidm1-mpv-gbm-fix) - already only 1 trivial, unrelated commit (webkitgtk build fix) behind origin/master, unlike C's buildroot fork which needed real reconciliation. Also fixed 6 files that lost their executable bit during manual reconciliation (git show | cp does not preserve file mode).
…the ES version string ES's version label is literally "BATOCERA.LINUX ES V" + BATOCERA_SYSTEM_VERSION, so using the git tag (batocera44-odroidm1-vX.Y.Z) verbatim doubled up into "...ES Vbatocera44-odroidm1-v1.0.0". Strip the tag down to "44-odroidm1-X.Y.Z" for the version string while keeping the tag itself unchanged (still reads well in GitHub's release list) - final ES label becomes "BATOCERA.LINUX ES V44-odroidm1-1.0.0 <build date>", matching the shape of upstream's own "V44-dev-<hash>".
…s ambiguous merge-base Both odroid-m1-bsp-kernel and odroid-m1-g29p1 independently merged the same upstream squash commit (e38b00f) with --allow-unrelated-histories, which left git (and GitHub's PR diff) with two candidate merge-bases and no single common ancestor - PR #4's diff stayed at ~390-540 files instead of shrinking to the genuine A-vs-C track difference. This merge changes no C-track content (tree is identical to the previous commit) - it only records odroid-m1-bsp-kernel's current tip as an ancestor of this branch, giving the two branches one unambiguous shared commit going forward so PR #4's diff resolves to the real difference.
- Add the missing fbneo_korean_libretro.info symlink (present in the A track, missing here) - fbneo_korean is the default FBNeo core on this board and README-odroidm1.md advertises it, but without this symlink RetroArch can't read the core's name/extensions/options metadata. - Remove the board fsoverlay copy of batocera-upgrade - confirmed byte-identical to the shared package/batocera/core/batocera-scripts copy (the local-var/$? fix already landed in both), so the fsoverlay copy does nothing now except silently re-shadow the shared file on the next upstream sync. - Revert the erroneous "except ValueError, ImportError: is Python 2 syntax" fix from 52523fc - it's valid PEP 758 syntax (unparenthesized multi-exception except clauses), accepted starting Python 3.14, which this build already targets (pyproject.toml target-version = "py314", buildroot PYTHON3_VERSION 3.14.5). Confirmed via `ruff format --target-version py314`, which actively strips the parentheses back out - the original upstream code was correct. - Remove aethersx2Generator.py's unused imports (Mapping, SystemConfig, Guns, Input, DeviceInfoMapping, Resolution) - the 6 ruff-check failures that were breaking CI on this branch only, since this file never ran through CI before (per 1st/2nd review rounds). Auto-fixed with `ruff check --fix`. All four verified with a local ruff 0.16.5 install (check + format --diff), matching this repo's pyproject.toml configuration.
Zansword was an incorrect name attached to the credit in an earlier session - the correct name/casing is DsNo. Also describe the patch more precisely: it's a CRC-tolerance fix (accepts Korean-translated ROMs that fail the standard CRC32 check), not a text/UI translation.
README-odroidm1.md -> README-odroidm1.ko.md (unchanged content, just renamed), plus a new README-odroidm1.en.md English translation. Both files cross-link to each other at the top. Using ISO 639-1 "ko"/"en" language codes rather than "kr" (that's the ISO 3166-1 country code for South Korea, not a language code).
…ADME's perf table
… v1.0.0 g29p1 Vulkan blob + Wayland for ODROID-M1 (C안) — full system matrix complete, release prep
…parate odroid-m1 branch odroid-m1-bsp-kernel is the actual ongoing release branch (where PR #4 just merged the g29p1/Wayland work) - keeping a separate odroid-m1 branch purely to host the updates/ manifest tree adds an extra branch to remember to update on every release with no real benefit over just committing it here directly. Also copies the current updates/bsp-odroidm1/stable/last/batocera.version manifest (content identical to the released batocera44-odroidm1-v1.0.0 image's own batocera.version) so the new URL resolves immediately - switching the pointer without this would 404 for anyone building from this branch before the next real release. The old odroid-m1 branch is left as-is (not deleted) since the already-shipped v1.0.0 image's batocera.conf still points there - only affects builds from this branch going forward.
odroidm1: point updates.url at odroid-m1-bsp-kernel instead of the separate odroid-m1 branch
… fallback The earlier fix (e8ea822) prepended NotoSansKR-VF.ttf to the font candidate lists, picked once at startup by "first file that exists" - this fixed Korean text but would regress any other script that font covers only partially: NotoSansKR-VF.ttf has just 66 Cyrillic codepoints against DejaVu Sans's full 256, so Cyrillic game titles would start rendering as missing-glyph boxes to fix Korean ones (caught before this ever shipped, via a question about whether the same gap affects other languages). SDL2_ttf has no per-glyph fallback chain (that's an SDL3_ttf-only feature), so a single fixed font picked once renders every string for the rest of the session regardless of what that string contains. Replaced with: keep DejaVu/Liberation as the primary choice exactly as upstream has it, and open NotoSansKR-VF.ttf as a fallback-only font. A new pickFontForText() decodes each string to Unicode codepoints and checks TTF_GlyphIsProvided32() against the primary font; only if the primary is missing a glyph the string actually needs does it fall back to the Korean/CJK font for that string. Checked per string (game title, system name, description), not globally - a Cyrillic title still uses DejaVu (unaffected) while a Korean title uses the fallback. The new decode/coverage-check logic (utf8ToCodepoints + pickFontForText) was verified with a standalone host-side unit test against ASCII, Korean, Cyrillic, and mixed-script strings, all resolving to the expected font. Not yet rebuilt and re-flashed to real hardware - pending before this (or an equivalent patch against current upstream, which has since rewritten this file with GIF-animation/Wayland support) goes to batocera-linux upstream.
…overage-aware fallback odroidm1: replace naive Korean font prepend with glyph-coverage-aware fallback
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds a new dedicated buildroot target
rk3568-odroidm1for ODROID-M1, separate from the sharedrk3568target (mainline-kernel/Panfrost, shared by 11 handheld boards — incompatible with our downstream BSP kernel, left untouched).PR against my own fork, for documentation/review only — not intended for upstream batocera-linux.
Status
Boots end-to-end, EmulationStation renders via GBM/KMSDRM, gameplay verified across multiple systems (NDS, PSP/PPSSPP, Genesis) with noticeably better performance than vanilla Batocera on the same board. Storage pooling, gamepad input, WiFi, and the single-active-display (HDMI ↔ VU8M) toggle are all verified working on real hardware.
What this is
Pyohwan/linux, branchfix-hdmi-dsi-dual-livelock).rockchip/rk3568-odroid-m1;i2c0/i2c1/spi0statically merged.display_vu8mships as a standalone.dtbo, applied at boot time byboot.cmdreading/boot/config.ini'soverlays=key (see "Key decisions" below - notextlinux.conf, which this board doesn't ship at all).mali-g52-odroidm1package wrapping hardkernel's official Mali blob — only its GBM/DRM EGL platform is used (the blob's Wayland-client EGL platform doesn't work, see below).BR2_PACKAGE_BATOCERA_WAYLAND_LABWCoff, EmulationStation renders directly via KMSDRM (same architecture therk3326target uses).u-boot-rockchip.bin(idbloader +u-boot.itbcombined into one image, correct offsets baked in by U-Boot's own build) is a from-source mainline 2026.04 build (odroid-m1-rk3568_defconfig— seepackage/batocera/boot/uboot-odroid-m1/BUILD-NOTES.mdfor exact provenance), replacing a prebuilt blob of unknown origin end to end - there's no separate untouched Hardkernel SPL stage, the whole combined blob is ours.bsp-odroidm1(notrk3568-*, avoids an upstreampost-image-script.shfilename-collision quirk for subtargets whose name starts with the target family name).Key decisions
eglInitialize()fails after a successfuleglGetPlatformDisplayEXT(), confirmed on two blob versions); its GBM/DRM EGL platform works fine, so the compositor is skipped entirely./boot/config.ini'soverlays=key, not DT-baking or DPMS blanking.boot.cmdloadsconfig.iniwith U-Boot'sinicommand (the same mechanism Hardkernel's own official Ubuntu image uses, confirmed by dumping and inspecting its realboot.scr) andfdt applys whatever.dtbonames are listed - empty by default (HDMI-only), set todisplay_vu8mto enable the panel. One line edit plus a reboot, no image rebuild, and it's read identically by every boot path (SPI-NOR Petitboot via kexec,skip_spibootdirect U-Boot, or this project's own U-Boot if its GPT entry is re-added - see the boot-mechanism decision below). This replaced an earlierextlinux.conf-based design (two staticLABELs toggled byDEFAULT) that only covered one of those boot paths;extlinux.confisn't shipped at all anymore; asyslinux.cfgfallback still exists only for Petitboot builds with a syslinux parser instead of the kexec one, kept manually in sync withconfig.ini's default. The unwanted DSI panel is never probed by the kernel at all (rather than probed then hidden), so there's nothing left to fight withbatocera-switch-screen-checker's hotplug detection. Verified both directions on hardware, including VU8M as the sole active display with HDMI physically disconnected.display.rotate.<connector>uses a different connector-identifier format depending on the consumer:emulationstation-standalonecanonicalizes it tobatocera-resolution listOutputs's own form (1 - DSIon this board), while the boot splash usesbatocera-drminfo's raw name (DSI). The sysconfig default previously used the wrong (splash-style) format for the ES-side key, so rotation silently never applied. Fixed to the correct key and verified live (90° is the correct direction).boot.scr, self-boot and Petitboot are mutually exclusive by design (not simultaneous). Petitboot parsesboot.ini/boot.scr/kboot.conf/grub.cfgand explicitly notextlinux.conf, so aboot.scris generated fromboot.cmdat build time (withmkimage, rather than committing a binary that can drift out of sync with its source) and placed at the boot partition's root. Two non-obvious constraints, both found by reading upstream Petitboot's own source and confirmed againstpb-discover.logon hardware: theloadlines must NOT carry a:<part>device suffix (with one, Petitboot mis-resolves the partition index to p1 and every resource stays permanently unresolved), and the boot partition must be GPT entry ODROID-M1 dedicated target: hardkernel BSP kernel + Mali-G52 blob + VU8M overlays #1. The real conflict is with a separate requirement: the SPL locates the main-stage U-Boot by GPT entry name ("uboot") - with that entry present, the boot ROM always runs this card's own bootloader and the SPI-NOR/Petitboot is never reached at all, no matter what's on the card (confirmed live, and matches how the Petitboot author himself describes it on the ODROID forum, t=44346: a storage device carrying its own bootloader outranks Petitboot). Sogenimage.cfgdeliberately leaves the "uboot" GPT entry unregistered (in-partition-table = "no") - this is what makes Petitboot reachable and the boot partition land on GPT entry ODROID-M1 dedicated target: hardkernel BSP kernel + Mali-G52 blob + VU8M overlays #1 by declaration order, at the cost of the card no longer self-booting by default.fw_setenv skip_spiboot truein the Petitboot shell makes the SPI-NOR's own U-Boot distro-boot straight off this card'sboot.scrinstead of showing the menu (falserestores it) - same script either way, so VU8M and everything else behaves identically. Re-adding the "uboot" entry togenimage.cfgis all it takes to go back to direct self-boot, at the cost of Petitboot no longer being reachable. All three paths (Petitboot menu,skip_spiboot, and direct self-boot with the entry re-added) read the sameconfig.ini-driven VU8M toggle identically - verified on hardware.libbrotlidec.so.1runtime dependency kept explicit (curl still needs it, nothing else pulls it in anymore); FUSE module now loaded beforebatocera-storage-managerruns; NVMe multi-partition ROM merge rescoped per-partition instead of per-disk (same fix applied toignore.drives); a startup race between D-Bus and PipeWire/WirePlumber fixed for Bluetooth audio; RK809 DAC volume fixed to account for the driver clamping to 252/255, not 100%.emulationstation-standalone's second-output lookup switched tobatocera-settings-get-master(shared file, applies to every board): the non-master lookup only checks/userdata/system/batocera.confdirectly, so an unset user override read back as empty and auto-picked a second output anyway - confirmed live,DSI-1kept getting auto-selected and DPMS'd back on every boot despite the sysconfig default. Flagged by review as worth noting since it's a shared-file behavior change; any other board with aglobal.videooutput2sysconfig default would see the same fix (a strict improvement over the previous silently-broken lookup, not board-specific).Known issues (root-caused, not yet fixed)
xcbandeglfsQt platforms fail) — NDS testing in this PR used a libretro core instead.Notable fixes bundled in (packaging)
mali-g52-odroidm1: pkgconfig, missing headers, a symbol alias,/etc/ld.so.preloadentry, currentmesa3d-headers, and (2nd review round) the siblingmali-G52package's DMA-BUF/format-modifier feature selects that this variant was missing.buildrootsubmodule bumped (836eafc→95d26b3) for themesa3d-headersversion fix this board'smesa3d-headersdependency needs (26.1.4 → 26.1.5).configs/batocera-rk3568-odroidm1.boardclaiming none of the sharedrk3568/patchesffmpeg/mpv patches apply here - actually 4 of 5 are byte-identical copies kept in sync by hand, only the HEVC one genuinely doesn't apply. Addedpatches-source.txtprovenance files.rocknix-joypad/TRX/moonlight-qt/SDL3_X11: excluded for this target only, no other board affected.xone: extended its existing older-kernel workaround to also coverBR2_KERNEL_HEADERS_6_1.dosbox-x: fixed a duplicate--disable-avcodecflag inDOSBOX_X_CONF_OPTS(the real one already exists unconditionally upstream/on the sharedrk3568target for the ffmpeg 8.1.2 API breakage; this branch had accidentally added a second, redundant copy).Test plan
rtw88) workingu-boot-rockchip.binreplaced with a mainline 2026.04 build from this board's real upstream defconfig; validates and boots the new FIT correctly/boot/config.ini'soverlays=key — verified both directions from a clean reboot, including VU8M as the sole active display with HDMI physically disconnectedconfig.ini(via SSH, no image rebuild) read correctly by U-Boot'sinicommandsystem.cpu.governor=interactivefw_setenv skip_spiboot truein the Petitboot shell distro-boots straight off this card'sboot.scrinstead, same config.ini-driven behavior🤖 Generated with Claude Code
https://claude.ai/code/session_f66fcf92-a0dc-4745-94a0-4fa46afc8b55
https://claude.ai/code/session_011nACLgqFd8ijkjYT67HXrv
https://claude.ai/code/session_66b26045-42fa-4151-b4bb-4a185e068a06