diff --git a/projects/ArchR/packages/graphics/gpudriver/package.mk b/projects/ArchR/packages/graphics/gpudriver/package.mk index ef47793f54..12d9aac41c 100644 --- a/projects/ArchR/packages/graphics/gpudriver/package.mk +++ b/projects/ArchR/packages/graphics/gpudriver/package.mk @@ -27,16 +27,17 @@ post_makeinstall_target() { DTB_OVERLAY_UNLOAD="\/usr\/bin\/dtb_overlay set driver-gpu None" ;; *) - # No DTB overlay shipped for this device. The gpudriver script - # detects the empty placeholder and (a) hides panfrost from the - # UI, (b) refuses runtime switches, and (c) on boot falls back to - # libmali if a stale setting still points at panfrost. + # No DTB overlay needed: kernel base DTS already has the GPU node + # with 'arm,mali-bifrost' compatible, so panfrost binds directly + # via modprobe. Switching between libmali and panfrost just + # requires loading/unloading the kernel module and swapping the + # userspace GL library binds. PAN="panfrost" DTB_OVERLAY_LOAD="" DTB_OVERLAY_UNLOAD="" ;; esac - + sed -e "s/@PAN@/${PAN}/g" \ -i ${INSTALL}/usr/bin/gpudriver diff --git a/projects/ArchR/packages/graphics/gpudriver/sources/bin/gpudriver b/projects/ArchR/packages/graphics/gpudriver/sources/bin/gpudriver index c9e0b46875..975f88e958 100755 --- a/projects/ArchR/packages/graphics/gpudriver/sources/bin/gpudriver +++ b/projects/ArchR/packages/graphics/gpudriver/sources/bin/gpudriver @@ -15,14 +15,82 @@ get_current_driver() { } -# Verify the GPU node ended up bound to a kernel driver. A successful -# modprobe doesn't always mean the driver actually attached to the -# hardware (mismatched compatible string, regulator/clock failure, etc). -# `/sys/class/drm/card0` only exists when *some* DRM driver is bound. +GPUDRIVER_LOG="/storage/.config/gpudriver.log" + +# Append a one-line tagged entry to the persistent log. We keep this on +# /storage so it survives across reboots — the most common diagnostic +# need is "I picked panfrost and the system came up in libmali", which +# is invisible at boot otherwise. +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 +} + +# Verify the GPU node ended up bound to the requested kernel driver AND +# that the display chain is alive. A successful modprobe doesn't always +# mean the driver attached to the hardware (mismatched compatible, +# regulator fail), and even a bound driver doesn't always mean the +# panel came up (panfrost can bind cleanly while the DSI bridge fails +# on a clone variant we haven't characterized, leaving a black screen). +# We treat both cases as a failure so the alternate driver gets a +# chance. +# +# When the check fails we drop the failure reason + the relevant dmesg +# tail to /storage/.config/gpudriver.log so users reporting "panfrost +# doesn't boot" can hand us actionable data instead of a black screen. gpu_is_bound() { - for c in /sys/class/drm/card[0-9]*; do - [ -e "$c" ] && return 0 + local driver="$1" + local connector status + local have_card=0 have_display=0 have_bind=0 + + # Give the kernel a beat to attach. Without this we frequently raced + # the bind on slow microSD boots and triggered a false fallback. + for _wait in 1 2 3 4 5; do + for c in /sys/class/drm/card[0-9]*; do + [ -e "$c" ] && have_card=1 + done + [ $have_card -eq 1 ] && break + sleep 0.2 done + + # The driver-specific sysfs node tells us the kernel actually attached + # the requested driver to the GPU platform device — not just that + # *some* DRM card exists. rockchip-vop creates card0 even without any + # GPU driver, so checking only /sys/class/drm is not enough. + case "${driver}" in + panfrost) + for g in /sys/bus/platform/drivers/panfrost/[a-f0-9]*.gpu; do + [ -e "$g" ] && have_bind=1 + done + ;; + libmali) + # mali_kbase exposes /dev/mali0 once bound. + [ -e /dev/mali0 ] && have_bind=1 + ;; + esac + + # At least one DRM connector must report "connected". DSI panels on + # rk3326 report status via /sys/class/drm/cardN-DSI-1/status. If no + # connector ever becomes connected, the panel chain is dead and the + # user is looking at a black screen no matter what we do here, so + # signal failure and let the caller try the other driver. + for connector in /sys/class/drm/card[0-9]*-*/status; do + [ -e "$connector" ] || continue + read -r status <"$connector" 2>/dev/null + [ "$status" = "connected" ] && have_display=1 && break + done + + if [ $have_card -eq 1 ] && [ $have_bind -eq 1 ] && [ $have_display -eq 1 ]; then + log_event "${driver} bound OK (card=1 bind=1 display=1)" + return 0 + fi + + log_event "${driver} bind check FAILED (card=${have_card} bind=${have_bind} display=${have_display})" + # Last lines mentioning the driver, so the failure mode is recoverable + # from /storage without needing a serial console. + dmesg 2>/dev/null | grep -iE "${driver}|mali|gpu@ff400000|panfrost" | tail -20 >>"${GPUDRIVER_LOG}" 2>/dev/null return 1 } @@ -56,14 +124,34 @@ load_driver() { modprobe -r mali_kbase 2>/dev/null modprobe @PAN@ + # Reverse every bind the libmali path did. Order matters: undo the + # libEGL/libGLESv2/etc. binds from /usr/lib/mali first, then the + # /dev/null binds on libGL.so (mesa needs that path to exist), and + # finally the panfrost vulkan ICD masks. Missing any of these + # leaves apps opening /dev/null where libGL.so should be, which is + # what was making the system "not boot" for users picking panfrost + # from the UI: kernel binds cleanly, but every Mesa GL app crashes + # on dlopen because libGL.so is shadowed. mount | grep -q "on /usr/lib/libEGL.so" && find /usr/lib/mali -type f -exec bash -c 'lib={}; umount ${lib/\/mali\//\/}' ';' if [ -d /usr/lib/glesonly ]; then mount | grep -q "on /usr/lib/libSDL2" && find /usr/lib/glesonly -type f -exec bash -c 'lib={}; umount ${lib/\/glesonly\//\/}' ';' fi - # Mask mali Vulkan ICD so the loader only finds panfrost + # Restore real libGL.so on both archs. These are bound to /dev/null + # by the libmali path so vanilla GL apps cannot pick the wrong + # library; on panfrost we WANT Mesa's libGL to be visible. + mount | grep -q "on /usr/lib/libGL.so" && umount /usr/lib/libGL.so 2>/dev/null + mount | grep -q "on /usr/lib32/libGL.so" && umount /usr/lib32/libGL.so 2>/dev/null + + # Unmask panfrost Vulkan ICD entries that the libmali path masked, + # then mask the mali ICD so the loader picks up panfrost only. + for icd in /usr/share/vulkan/icd.d/panfrost_icd.*.json; do + [ -f "${icd}" ] || continue + mount | grep -q "on ${icd}" && umount "${icd}" 2>/dev/null + done [ -f /usr/share/vulkan/icd.d/mali.json ] && \ - mount --bind /dev/null /usr/share/vulkan/icd.d/mali.json + ( mount | grep -q "on /usr/share/vulkan/icd.d/mali.json" || \ + mount --bind /dev/null /usr/share/vulkan/icd.d/mali.json ) ;; *) exit 3 @@ -78,6 +166,7 @@ case "$1" in ;; "--start") get_current_driver + log_event "boot: selected driver=${CONFDRIVER}" load_driver ${CONFDRIVER} # 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 @@ -86,11 +175,16 @@ case "$1" in # composited system with software rasterization that crawls. Try the # alternate driver instead of letting the user think their device is # just slow. - if ! gpu_is_bound; then + if ! gpu_is_bound "${CONFDRIVER}"; then ALT="libmali" [ "${CONFDRIVER}" = "libmali" ] && ALT="panfrost" - logger -t gpudriver "${CONFDRIVER} did not bind to GPU; trying ${ALT}" + log_event "${CONFDRIVER} did not bind to GPU; falling back to ${ALT}" load_driver "${ALT}" 2>/dev/null + if gpu_is_bound "${ALT}"; then + log_event "fallback to ${ALT} succeeded" + else + log_event "fallback to ${ALT} ALSO FAILED — system will run software-rendered" + fi fi ;; "libmali") diff --git a/projects/ArchR/packages/linux-drivers/archr-joypad/patches/0002-accept-micro-gamepad-compatible.patch b/projects/ArchR/packages/linux-drivers/archr-joypad/patches/0002-accept-micro-gamepad-compatible.patch new file mode 100644 index 0000000000..689635f2a5 --- /dev/null +++ b/projects/ArchR/packages/linux-drivers/archr-joypad/patches/0002-accept-micro-gamepad-compatible.patch @@ -0,0 +1,36 @@ +From: ArchR +Subject: [PATCH] archr-joypad: bind to "micro,gamepad" compatible + +Some R36S clone variants (notably a V12-silkscreen / G80CA-1.3-sticker +hybrid surfacing in Discord reports) ship a vendor DTB with +'compatible = "micro,gamepad"' on the joypad node, instead of +'odroidgo3-joypad'. The wire format is multi-ADC (4 separate saradc +channels) plus a flat key-gpios array, similar enough to our +archr-joypad multi-ADC path that adding the compat string lets the +driver bind and start reading io-channels. + +Caveats observed in the wild: + +* io-channel-names on these boards use "button0".."button3" instead of + our usual "key-LX/LY/RX/RY". The kernel's iio framework looks up the + channel by index in io-channel-names, so the bind itself succeeds; + what may need flipping per board is the axis mapping (see the + 'abs-left-first' property and the mapping_r_l / mapping_l_r arrays + in joypad_adc_setup). +* The vendor DTB encodes GPIO buttons as a single flat 'key-gpios' + array (18 entries on the boards we have seen) rather than the + individual swN { gpios = ... } child nodes our driver parses today. + The analog sticks should come up with this patch; the GPIO buttons + still need either a runtime translation in an overlay or a driver + extension. That is tracked separately; this patch closes the bind + failure so users at least have the sticks working. + +--- a/archr-joypad.c ++++ b/archr-joypad.c +@@ -642,6 +642,7 @@ + { .compatible = "archr-joypad", }, + { .compatible = "odroidgo3-joypad", }, + { .compatible = "rocknix-joypad", }, ++ { .compatible = "micro,gamepad", }, + {}, + };