diff --git a/config/emulators/gp32.conf b/config/emulators/gp32.conf
new file mode 100644
index 0000000000..cc2d731920
--- /dev/null
+++ b/config/emulators/gp32.conf
@@ -0,0 +1,9 @@
+SYSTEM_NAME="gp32"
+SYSTEM_FULLNAME="GP32"
+SYSTEM_MANUFACTURER="GamePark"
+SYSTEM_HARDWARE="portable"
+SYSTEM_PATH="/storage/roms/gp32"
+SYSTEM_EXTENSION=".smc .zip .7z"
+SYSTEM_PLATFORM="gp32"
+SYSTEM_THEME="gp32"
+SYSTEM_WIKI_PATH="gp32"
diff --git a/documentation/PER_DEVICE_DOCUMENTATION/RK3326/SUPPORTED_EMULATORS_AND_CORES.md b/documentation/PER_DEVICE_DOCUMENTATION/RK3326/SUPPORTED_EMULATORS_AND_CORES.md
index c56d945e0e..635db2f466 100644
--- a/documentation/PER_DEVICE_DOCUMENTATION/RK3326/SUPPORTED_EMULATORS_AND_CORES.md
+++ b/documentation/PER_DEVICE_DOCUMENTATION/RK3326/SUPPORTED_EMULATORS_AND_CORES.md
@@ -40,6 +40,7 @@
|Elektronika|Elektronika BK (bk)|1984|`bk`|.bin .img .dsk|**retroarch:** bk (default)
|
|EPOCH/YENO|Super Cassette Vision (scv)|1984|`scv`|.cart .bin .rom .0 .zip|**retroarch:** emuscv (default)
|
|Fairchild|Fairchild Channel F (channelf)|1976|`channelf`|.bin .chf .zip .7z|**retroarch:** freechaf (default)
|
+|GamePark|GP32 (gp32)|1996|`gp32`|.smc .zip .7z|**retroarch:** mame (default)
|
|id Software|Doom (doom)|1993|`doom`|.doom|**gzdoom:** gzdoom-sa (default)
|
|id Software|iDtech (idtech)|1991|`idtech`|.sh|**retroarch:** idtech
|
|Infocom|Z-machine (zmachine)|1979|`zmachine`|.dat .z1 .z2 .z3 .z4 .z5 .z6 .zip|**retroarch:** mojozork (default)
|
diff --git a/projects/ArchR/packages/graphics/gpudriver/autostart/003-gpudriver b/projects/ArchR/packages/graphics/gpudriver/autostart/003-gpudriver
index fdcf196e55..a2e596203c 100755
--- a/projects/ArchR/packages/graphics/gpudriver/autostart/003-gpudriver
+++ b/projects/ArchR/packages/graphics/gpudriver/autostart/003-gpudriver
@@ -2,4 +2,6 @@
# SPDX-License-Identifier: GPL-2.0
# Copyright (C) 2024 ArchR (https://github.com/archr-linux/Arch-R)
-/usr/bin/gpudriver --start
+# Binary lives under /usr/lib/archr/ instead of /usr/bin/ so the ES menu
+# GPU DRIVER selector stays hidden while the panfrost path is unstable.
+/usr/lib/archr/gpudriver --start
diff --git a/projects/ArchR/packages/graphics/gpudriver/package.mk b/projects/ArchR/packages/graphics/gpudriver/package.mk
index 12d9aac41c..0d373702a5 100644
--- a/projects/ArchR/packages/graphics/gpudriver/package.mk
+++ b/projects/ArchR/packages/graphics/gpudriver/package.mk
@@ -11,9 +11,17 @@ PKG_TOOLCHAIN="manual"
PKG_LONGDESC="GPU driver util for switching between panfrost / panthor and libmali / libmali-vulkan"
post_makeinstall_target() {
- mkdir -p "${INSTALL}/usr/bin/"
- cp -v "${PKG_BUILD}/bin/gpudriver" "${INSTALL}/usr/bin/"
-
+ # Install OUTSIDE /usr/bin while the panfrost path is still unreliable
+ # on RK3326 clones. EmulationStation surfaces the GPU DRIVER selector
+ # only when `/usr/bin/gpudriver` exists (see es-app GuiMenu.cpp), so
+ # placing it under /usr/lib/archr/ hides the picker without removing
+ # any boot-time functionality — the autostart hook (003-gpudriver)
+ # calls this binary by full path. Move back to /usr/bin/gpudriver
+ # once panfrost on the Mali-G31 path stops hanging the kernel.
+ mkdir -p "${INSTALL}/usr/lib/archr/"
+ cp -v "${PKG_BUILD}/bin/gpudriver" "${INSTALL}/usr/lib/archr/"
+ GPUDRIVER_BIN="${INSTALL}/usr/lib/archr/gpudriver"
+
# set the correct mesa pan kernel driver module based on device
case ${DEVICE} in
RK3588)
@@ -39,11 +47,11 @@ post_makeinstall_target() {
esac
sed -e "s/@PAN@/${PAN}/g" \
- -i ${INSTALL}/usr/bin/gpudriver
+ -i "${GPUDRIVER_BIN}"
sed -e "s/@DTB_OVERLAY_LOAD@/${DTB_OVERLAY_LOAD}/g" \
- -i ${INSTALL}/usr/bin/gpudriver
+ -i "${GPUDRIVER_BIN}"
sed -e "s/@DTB_OVERLAY_UNLOAD@/${DTB_OVERLAY_UNLOAD}/g" \
- -i ${INSTALL}/usr/bin/gpudriver
+ -i "${GPUDRIVER_BIN}"
}
\ No newline at end of file
diff --git a/projects/ArchR/packages/graphics/gpudriver/sources/bin/gpudriver b/projects/ArchR/packages/graphics/gpudriver/sources/bin/gpudriver
index 975f88e958..cae5d54d82 100755
--- a/projects/ArchR/packages/graphics/gpudriver/sources/bin/gpudriver
+++ b/projects/ArchR/packages/graphics/gpudriver/sources/bin/gpudriver
@@ -25,7 +25,14 @@ log_event() {
local msg="$1"
mkdir -p "$(dirname "${GPUDRIVER_LOG}")" 2>/dev/null
echo "$(date '+%Y-%m-%d %H:%M:%S') gpudriver: ${msg}" >>"${GPUDRIVER_LOG}" 2>/dev/null
- logger -t gpudriver "${msg}" 2>/dev/null
+ # Intentionally do NOT call `logger` here. The parallel autostart Phase B
+ # bursts hundreds of log messages at once; the journald socket fills, and
+ # `logger` (a synchronous binary) blocks waiting for the daemon to drain.
+ # That deadlock pinned gpudriver between "calling load_driver panfrost"
+ # and the next log step on R36S 20260515 builds — the script never
+ # returned, the autostart wait never finished, and the watchdog reset
+ # the board. The on-disk echo above is enough for our post-mortem
+ # diagnostic needs.
}
# Verify the GPU node ended up bound to the requested kernel driver AND
@@ -102,7 +109,13 @@ load_driver() {
case ${DRIVER_TO_LOAD} in
"libmali")
- modprobe -r @PAN@ 2>/dev/null
+ # Symmetric safety: only rmmod @PAN@ if loaded. In-tree panfrost
+ # is safer to unload than mali_kbase but skip anyway — a reboot
+ # gives a cleaner switch and matches the user's expectation that
+ # changing GPU driver requires a restart.
+ if lsmod 2>/dev/null | grep -q "^@PAN@ "; then
+ log_event "libmali: @PAN@ loaded — switch needs reboot to take effect"
+ fi
modprobe mali_kbase
# Bind-mount mali GLES libraries over mesa if not already done.
@@ -121,8 +134,75 @@ load_driver() {
done
;;
"panfrost")
- modprobe -r mali_kbase 2>/dev/null
- modprobe @PAN@
+ # Snapshot ANY kernel messages before we touch the driver. If the
+ # system is already in a degraded state by the time gpudriver
+ # runs (e.g. an earlier autostart script tickled something), we
+ # want to see it in the post-mortem rather than blaming the
+ # subsequent modprobe.
+ log_event "panfrost: step 0a - capturing pre-state dmesg"
+ dmesg -T 2>/dev/null | tail -100 >>"${GPUDRIVER_LOG}.dmesg-pre" 2>/dev/null
+ log_event "panfrost: step 0b - dmesg pre-state captured"
+
+ # mali_kbase is the ARM vendor out-of-tree blob. Its driver-exit
+ # path double-frees the GPU regulator and clock at unbind time
+ # (observed kernel Oops in __clk_put at POISON_LIST_END +
+ # _regulator_put WARN, cascading into further oopses until the
+ # board watchdog resets — confirmed on R36S with kernel 6.12.79).
+ # Removing it in-place is therefore unsafe: skip the rmmod
+ # entirely if the module is loaded and let the user reboot to
+ # land in a fresh kernel where the module never came up. If the
+ # module is NOT loaded we can proceed; this is the normal case
+ # on a fresh boot where gpu.driver=panfrost.
+ if lsmod 2>/dev/null | grep -q "^mali_kbase "; then
+ log_event "panfrost: step 1 - mali_kbase IS LOADED, skipping rmmod (would crash kernel) — reboot required for switch to take effect"
+ else
+ log_event "panfrost: step 1 - mali_kbase not loaded, no rmmod needed"
+ fi
+ log_event "panfrost: step 2 - rmmod check done"
+
+ # Snapshot the last dmesg line so we can quote only what the
+ # panfrost probe added.
+ PRE_DMESG_COUNT=$(dmesg 2>/dev/null | wc -l)
+ log_event "panfrost: step 3 - dmesg snapshot taken (count=${PRE_DMESG_COUNT})"
+
+ # Launch modprobe asynchronously. `timeout` would normally cap
+ # this, but the panfrost probe on certain RK3326 clones blocks
+ # the modprobe process inside an uninterruptible kernel syscall
+ # (D state) — neither SIGTERM from `timeout` nor SIGKILL from a
+ # subsequent kill can release it. The script must never block
+ # waiting for modprobe; instead we poll for the platform bind to
+ # appear, give up after 30s, and let gpu_is_bound conclude with
+ # a failure that triggers the libmali fallback.
+ modprobe @PAN@ 2>/dev/null &
+ MODPROBE_PID=$!
+ log_event "panfrost: step 4 - modprobe launched pid=${MODPROBE_PID}"
+
+ _iter=0
+ for _wait in $(seq 1 30); do
+ _iter=$_wait
+ for g in /sys/bus/platform/drivers/panfrost/[a-f0-9]*.gpu; do
+ [ -e "$g" ] && break 2
+ done
+ kill -0 ${MODPROBE_PID} 2>/dev/null || break
+ sleep 1
+ done
+ log_event "panfrost: step 5 - poll loop exited at iter=${_iter}"
+
+ if ls /sys/bus/platform/drivers/panfrost/[a-f0-9]*.gpu >/dev/null 2>&1; then
+ log_event "panfrost: step 6a - platform bind observed"
+ else
+ if kill -0 ${MODPROBE_PID} 2>/dev/null; then
+ log_event "panfrost: step 6b - modprobe still running, probe stuck"
+ else
+ log_event "panfrost: step 6c - modprobe exited without bind"
+ fi
+ fi
+
+ # Drop the kernel messages emitted during the probe to the log so
+ # the failure mode is recoverable without a serial console.
+ log_event "panfrost: step 7 - capturing dmesg delta..."
+ dmesg 2>/dev/null | tail -n +$((PRE_DMESG_COUNT + 1)) | head -40 >>"${GPUDRIVER_LOG}" 2>/dev/null
+ log_event "panfrost: step 8 - dmesg captured"
# Reverse every bind the libmali path did. Order matters: undo the
# libEGL/libGLESv2/etc. binds from /usr/lib/mali first, then the
@@ -167,7 +247,37 @@ case "$1" in
"--start")
get_current_driver
log_event "boot: selected driver=${CONFDRIVER}"
+
+ # Boot-loop protection. Some hardware variants kernel-panic on
+ # `modprobe panfrost` (observed on RK3326 clones), which the watchdog
+ # then resets — so the system never reaches gpu_is_bound to record
+ # the failure, and the next boot tries panfrost again, ad infinitum.
+ # We drop a sentinel on /storage before the modprobe and only clear
+ # it once we know the driver bound successfully. On the next boot
+ # we look for the sentinel: if present, the previous attempt with
+ # this driver crashed before recovery — silently force libmali and
+ # rewrite the setting so the user can pick again from a working UI.
+ SENTINEL="/storage/.config/gpudriver.attempting"
+ if [ -f "${SENTINEL}" ]; then
+ PREV="$(cat "${SENTINEL}" 2>/dev/null)"
+ log_event "previous boot crashed while loading ${PREV} — forcing libmali"
+ rm -f "${SENTINEL}"
+ if [ "${CONFDRIVER}" = "${PREV}" ] && [ "${PREV}" != "libmali" ]; then
+ CONFDRIVER="libmali"
+ set_setting ${GPU_DRIVER_SETTING_KEY} libmali
+ fi
+ fi
+
+ # Use dd with oflag=sync so the sentinel is on the SD (not in page
+ # cache) BEFORE we touch the GPU driver. A subsequent kernel hang
+ # or watchdog reset would otherwise drop the sentinel and the boot
+ # after that would try panfrost again, looping.
+ echo "${CONFDRIVER}" | dd of="${SENTINEL}" oflag=sync 2>/dev/null
+ log_event "sentinel persisted: ${CONFDRIVER}"
+
+ log_event "calling load_driver ${CONFDRIVER}"
load_driver ${CONFDRIVER}
+ log_event "load_driver returned"
# If the requested driver didn't actually bind, fall back to the
# other one so the user gets a graphical system either way. Mali-G31
# on RK3326 is normally driven by libmali; if mali_kbase decides the
@@ -182,17 +292,35 @@ case "$1" in
load_driver "${ALT}" 2>/dev/null
if gpu_is_bound "${ALT}"; then
log_event "fallback to ${ALT} succeeded"
+ # Persist the working driver as the new default so the user is
+ # not asked to confirm again on next reboot.
+ set_setting ${GPU_DRIVER_SETTING_KEY} "${ALT}"
else
log_event "fallback to ${ALT} ALSO FAILED — system will run software-rendered"
fi
fi
+
+ # Driver bound (or fallback ran). Clear the sentinel so the next boot
+ # does NOT think the current boot crashed.
+ rm -f "${SENTINEL}" 2>/dev/null
+ sync 2>/dev/null
;;
"libmali")
set_setting ${GPU_DRIVER_SETTING_KEY} $1
+ # Force the setting onto disk before we return control to the UI,
+ # which may immediately trigger a reboot.
+ sync
@DTB_OVERLAY_UNLOAD@
;;
"panfrost")
set_setting ${GPU_DRIVER_SETTING_KEY} $1
+ # Force the setting onto disk before we return control to the UI,
+ # which may immediately trigger a reboot ("Reboot now" in the popup
+ # is dispatched synchronously; ext4 might still have the new value
+ # in the page cache, not on the SD). Without this sync the next
+ # boot could still read gpu.driver=libmali and the user perceives
+ # the switch as silently broken.
+ sync
@DTB_OVERLAY_LOAD@
;;
"")
diff --git a/projects/ArchR/packages/graphics/mesa/package.mk b/projects/ArchR/packages/graphics/mesa/package.mk
index 971182e440..099566e9f8 100644
--- a/projects/ArchR/packages/graphics/mesa/package.mk
+++ b/projects/ArchR/packages/graphics/mesa/package.mk
@@ -12,7 +12,7 @@ PKG_DEPENDS_TARGET="toolchain expat libdrm Mako:host pyyaml:host"
PKG_LONGDESC="Mesa is a 3-D graphics library with an API."
PKG_TOOLCHAIN="meson"
PKG_PATCH_DIRS+=" ${DEVICE}"
-PKG_VERSION="26.0.5"
+PKG_VERSION="26.1.1"
PKG_URL="https://gitlab.freedesktop.org/mesa/mesa/-/archive/mesa-${PKG_VERSION}/mesa-mesa-${PKG_VERSION}.tar.gz"
if listcontains "${GRAPHIC_DRIVERS}" "panfrost"; then
diff --git a/projects/ArchR/packages/virtual/emulators/package.mk b/projects/ArchR/packages/virtual/emulators/package.mk
index c5e5a83151..d038df9671 100644
--- a/projects/ArchR/packages/virtual/emulators/package.mk
+++ b/projects/ArchR/packages/virtual/emulators/package.mk
@@ -1384,6 +1384,10 @@ makeinstall_target() {
add_emu_core palm retroarch mu true
add_es_system palm
+ ### GamePark GP32
+ add_emu_core gp32 retroarch mame true
+ add_es_system gp32
+
### PC Ports
case ${TARGET_ARCH} in
aarch64|arm)