Skip to content

Add support for Anbernic H700-based devices - #807

Open
pvaibhav wants to merge 76 commits into
LoveRetro:mainfrom
pvaibhav:h700
Open

Add support for Anbernic H700-based devices#807
pvaibhav wants to merge 76 commits into
LoveRetro:mainfrom
pvaibhav:h700

Conversation

@pvaibhav

@pvaibhav pvaibhav commented Aug 15, 2026

Copy link
Copy Markdown

This huge changeset adds support for Anbernic's RG XX lineup for devices based on the Allwinner H700 SoC.

Supported are all SKUs of this family:

  • RG28XX
  • RG34XX
  • RG34XX SP
  • RG35XX+ and RG35 XX 2024
  • RG35XX Pro
  • RG35XX SP
  • RG35XX H
  • RG40XX H
  • RG40XX V
  • RG Cube XX
  • RG SP

The same platform h700 supports all these devices. Third party paks and tools can use the DEVICE env var to figure out the exact model they're running on.

It's been tested thoroughly be several users. The main development was done on RG40xxV and RG34xxSP by me.

There are some small known bugs notably with LED control on RG Cube XX and RG40xxH. These will be fixed post merge. For the most part, all features work, including features that NextUI doesn't support yet, like HDMI output. The power drain and sleep performance was optimised to be near-identical to stock OS.

You can check my testing gate here, and the roadmap + milestone status here. I also tested some cores that I play myself, the core and game testing status is here.

Note: third party paks, the Pak Store and the Updater all need upstream changes.

I've tried to keep my hands off platform-agnostic code unless absolutely necessary.

Furthermore, default display calibrations for some models (RG40xxV, RG34xxSP, and RG28xx calibrated by me, RG SP calibrated by @frysee) are included. The rest I don't have so they're left uncalibrated.

Boot logo pak was also updated to bundle 4 variants of each boot logo.

Good luck with the review! ;-P

pvaibhav added 30 commits August 9, 2026 00:50
The upstream code changed enough that our existing patch can't
find the hunk. I updated it to it builds now.
Document the RG XX H700 port strategy, including verified device facts, build/toolchain choices, boot hijack layout, platform-layer hardware paths, media stack, power behavior, wireless strategy, and rollout validation criteria.

This captures the implementation plan before the port work begins so the subsequent build and platform changes can be reviewed against a concrete hardware-backed baseline.
Add the initial Allwinner H700 platform for Anbernic RG XX devices, using the existing tg5040 toolchain image while forcing in-container builds to run as PLATFORM=h700.

This wires the new platform into the build and package skeleton, adds the dmenu.bin boot shim and TF2 installer/update flow, builds and bundles the Mali fbdev SDL2 runtime, ports the platform layer to H700 sysfs and input paths, adds H700 libmsettings/keymon/rfkill support, and exposes H700 displaycal, WiFi, and Bluetooth settings.

The runtime skeleton owns /mnt/sdcard with /mnt/SDCARD compatibility, stops conflicting stock services, starts H700 WiFi/BT helpers, supports mem suspend with radio bounce on resume, and keeps RG34XXSP/RG28XX detection guarded for later hardware validation.
Disable PicoDrive's inherited LTO flags for the H700 target and filter the remaining linker plugin flags after the upstream makefile is included. The tg5040 toolchain otherwise drops the libretro glue objects during the final shared-object link, leaving VFS and logging symbols unresolved. This keeps the H700 core bundle buildable without changing the shared core list.
Include h700 in the existing tg5040-class minarch build filters so the platform gets the RA, CHD, SRM, samplerate, and compression support libraries that the H700 package already stages. Without this, the package target could copy the H700 cores but failed before creating a usable MinUI payload because minarch.elf was never produced.
Prefer the stock OS unzip binary for the RG40XXV boot shim so the TF2 update payload can be extracted without relying on the embedded tarball helper. Keep the embedded helper as a fallback with explicit logging, and add exFAT/auto mount fallback for TF2 cards that are not already mounted by stock.
Package the H700 runtime libraries required by the Mali SDL stack and H700 platform helpers, including libUMP, libtinyalsa, and libasound. Add a debug keep-network flag and launch logging so failed RG40XXV bring-up attempts remain reachable over SSH for diagnosis.
Incorporate the H700 branch review by tightening the RG40XXV boot and update path, delegating fresh MinUI.zip installs through the platform updater, repairing TF2 mount failures when possible, and improving TF1/TF2 compatibility mounts and dmenu self-heal behavior.

Rework the H700 SDL/NextCommander build inputs so the custom Mali SDL is pinned and verified, Files.pak is built with H700 paths/buttons, and host libraries such as libasound/EGL/GLES are no longer bundled over the stock OS copies.

Harden the platform layer and runtime services with corrected joystick constants, persistent wake input handling, display/audio setting restore, WiFi runtime state separation, Bluetooth audio gating, better crash diagnostics, and removal of leftover Brick defaults that should not ship for H700.
Reorder the H700 auxiliary build so the pinned Mali SDL is configured and installed before Files.pak is compiled, and update the carried NextCommander patch to include the SDK SDL_image/SDL_ttf headers and libraries alongside the local SDL2 build.

Add a tiny framebuffer splash helper to the dmenu shim payload so first boot, missing TF2, stock-target warnings, and install/update work can show visible status before SDL/show2 exists.

Gate H700 Bluetooth audio affordances for the beta while keeping controller pairing available, avoid applying Bluetooth audio sample-rate limits to controller-only connections, and set the RG28XX software rotation flag during platform init.
Stop linking H700 binaries through the tg5040 glesv2 pkg-config library list, which pulled in the old libUMP dependency that is not part of the RG40XXV Mali-G31 stock stack. H700 now links GLES directly while still using the custom SDL2 pkg-config data.

Also stop copying UMP into the H700 payload, add early NextUI startup breadcrumbs around settings/video/input init, and make repeated UI crashes start the stock network/SSH services so boot-logo stalls remain diagnosable on-device.
Build the H700 Mali SDL stack without libudev and without dynamically loaded ALSA/libsamplerate helpers so video startup does not trip over the stock RG40XXV runtime's libudev/loadso behavior.

Add persistent installer logging and a debug-wifi.conf path that can bring up NetworkManager and SSH during install or after repeated UI crashes. Also make audiomon relink when platform link flags change, and guard the Bluetooth scan rate item before pushing it into the settings menu.
Replay the H700 raw evdev input restoration from the previous branch so built-in controls are read reliably on stock H700 images after rebasing onto c24b36d.

Keep the debug wifi launch guard from the same fix, but intentionally leave out the shared renderer and PLATFORM_STRAIGHT_ALPHA_UI workaround so this branch stays on the known-good pre-alpha graphics stack.
Picodrive's H700 patch already carried the FLTO filter needed by this platform, but clean rebuilds exposed two patch hygiene issues after the rebase. The hunk indentation was stale, and the parent core builder's PLATFORM=h700 value prevented Picodrive's included Makefile from selecting its libretro object list.

Keep the H700 platform block for cross flags, force Picodrive's internal PLATFORM back to libretro for object selection, and preserve the existing FLTO filter so clean release builds link the core reproducibly.
Normalize the shared asset sheet on H700 after SDL_image loads it and force source surfaces through a save/restore alpha blend path when blitting tinted bitmaps. The branch is gated by the existing PLATFORM string so tg5040 and tg5050 keep the pre-alpha rendering behavior while H700 can work around its SDL2/SDL_image bitmap handling.
This reverts commit 7f229c2dd11a048c5e4add511c86becc66cba250.
The tg5040-toolchain SDL2_image dlopens libpng12.so.0 at runtime. The
H700 stock Ubuntu rootfs only provides libpng16 (aarch64) and a 32-bit
libpng12 under /mnt/vendor/lib, so IMG_Load failed with 'wrong ELF
class: ELFCLASS32' and every asset bitmap silently blitted from a NULL
surface. Ship the toolchain's 64-bit libpng12 alongside the other
bundled SDL runtime libraries.
The emu pak launch scripts (cloned from tg5040) redirected minarch
output with the bashism '&>'. H700 stock OS /bin/sh is dash, which
parses it as 'background the command, then truncate the file' - so
launch.sh exited immediately, the launch loop restarted nextui.elf,
and games bounced straight back to the main screen. Use the POSIX
'> file 2>&1' form instead, like the old rg35xxplus port did.

With output captured, minarch still segfaulted in SND_init:
PLAT_pickSampleRate called GetAudioSink(), but minarch deliberately
runs SND_init before InitSettings(), so the msettings shared memory
is not mapped yet (NULL->audiosink at offset 0x70). Match tg5040 and
only consult PLAT_bluetoothConnected() there.

Also fix the same '&>' bashism in a system() amixer call in
msettings.c, which dash would background as well.
SDL2 was configured with --disable-alsa-shared, direct-linking the
tg5040 toolchain's libasound, which is built without symbol
versioning. The resulting unversioned references resolve against the
H700 stock Ubuntu's versioned libasound to the legacy ALSA_0.9
hw_params symbols (value semantics, chosen value as return) instead
of the new pointer API. snd_pcm_hw_params_set_rate_near then read
SDL's pointer argument as the requested rate and clamped it to the
hardware max: the codec ran at 192 kHz while SDL believed 32.768 kHz,
so only ~17% of every buffer was real audio and games sounded like
short slivers with silence gaps.

Configure with --enable-alsa-shared instead: SDL dlopens libasound at
runtime and dlsym always resolves the default (new-API) symbols. The
codec now honors the negotiated rate (e.g. req 32768 -> rec 32000)
and minarch's resampler bridges the difference. Bump the SDL2 stamp
to force a reconfigure.
The implementation is complete (working beta on RG40XXV), so the plan
docs are rewritten as-implemented: deviations recorded with reasoning,
branch-review files verified against the code and folded into the
topical docs, testing matrix filled with actual results, and a
prioritized roadmap added for the remaining work.
- Alpha/tinted-bitmap question resolved: glitches were the missing 64-bit
  libpng bundle, not main's alpha-blending work; plan is to rebase the
  h700 branch onto main including that work.
- 480p UI verdict from use: OK for alpha; Battery pak hint pills overlap.
- New roadmap item: audit Brick-era hardware assumptions in shared UI
  (Input tester layout, Fn switch, dead enhance display controls —
  only backlight/colortemp/white-point/RGB tuning work on RG XX).
Document that the RG34XXSP generally behaves like the RG40XXV on the H700 port while lid sleep/wake remains broken. Replace local test fleet IP/login details with public-safe evidence notes so the bring-up docs can be committed without leaking LAN-specific data.
The H700 firmware emits a compound sequence for a short MENU tap:
312 (BTN_TL2) down/up tracking the physical button, then a synthetic
354 (KEY_GOTO) pulse starting the instant 312 releases and lasting
~190ms (on a long hold, 354 never fires). Mapping both codes to
BTN_MENU stretched every tap past the 250ms MENU_DELAY threshold, so
a quick tap at the home screen flipped the shortcuts overlay into
brightness-adjust mode, and the Input tester showed MENU held after
release.

Drop the CODE_MENU_ALT (354) -> BTN_MENU mapping in the platform
layer and keymon; tap-vs-hold is derived from the clean 312 timing.
The SDL joystick path is unaffected (JOY_MENU_ALT = JOY_NA).

Verified via evtest and on-device testing on RG34XXSP; the quirk is
firmware-common across the RG XX H700 line.
Record that volume control is working correctly on both RG40XXV and RG34XXSP, including the UI and audible levels from mute through 100%. Retire the roadmap note that treated the digital-volume inversion as an unresolved documentation task.
…ume freeze

Four stacked bugs, all verified on RG34XXSP hardware:

- bin/suspend died on 'set -o pipefail' (unsupported by stock /bin/sh),
  exiting 2 before writing /sys/power/state; PWR_deepSleep treated that
  as suspend failure and powered off. Use 'set -u' only.
- The persistent wake_fd buffered the power-key release that triggered
  sleep, so every 2nd+ sleep of a process woke instantly. Drain the fd
  each PLAT_pollInput.
- Lid close injected a BTN_SLEEP release, which can never satisfy the
  manual-sleep condition (requires a power-key press). Add
  PWR_requestSleep() and call it on lid close; stop firing on lid open.
- Wake froze UI/input 5-10s: an ALSA PCM held open across suspend takes
  SDL ~9s to close. Close audio in PWR_enterSleep (reopened on wake;
  reinit now 150-200ms), and split the suspend script's resume work into
  after_sync (alsactl/syncsettings) and backgrounded after_async (wifi/bt).

Update h700-port docs: postmortem in 06, test matrix, roadmap P0 #1 done.
- Persist white point correction / RGB gains across reboots: displaycal
  defaults are now seeded only by the shm host and only when the settings
  file is missing or predates v11, instead of clobbering user values on
  every InitSettings()
- Hide Contrast/Saturation/Exposure on H700 (disp driver lacks the
  enhance_* sysfs nodes), including their mute-toggle variants
- Add per-model displaycal preset plumbing for the full H700 family
  (RG28XX..RGCubeXX), all neutral/disabled until each panel is measured;
  detection via RGXX_MODEL with DEVICE fallback
- Map RG35xx* to DEVICE=rg35xx in launch.sh instead of lumping the
  RG35xx family into rg40xx
- Raise default brightness to 4 on H700 devices (Brick keeps 2)

Verified on RG40XXV: gains survive reboot, fresh defaults are
brightness 4 with neutral displaycal; tg5040 builds unaffected.
- 04: displaycal reboot-persistence fix, per-model preset plumbing,
  RGXX_MODEL detection notes, hidden enhance controls, brightness 4
- 08: add verified rows for reboot persistence, fresh defaults, gating
- 09: mark display settings item done at 0e60efd, note remaining
  syncsettings nit, add per-panel calibration to the later list
… first frame

The Files app (NextCommander) appeared to freeze on startup: it ran fine but
never received input. The bundled SDL fork's Batocera patches removed the evdev
joystick heuristic from SDL_EVDEV_GuessDeviceClass() (Batocera classifies via
udev), so our no-udev build never enumerated the built-in pad and
SDL_NumJoysticks() returned 0. New patches/sdl2-h700.patch restores
classification via BTN_GAMEPAD/BTN_JOYSTICK (the pad has no ABS_X/ABS_Y, so the
original upstream check would not have matched either), applied with a stamp
like the NextCommander patch.

NextCommander fixes (patches/NextCommander-h700.patch):
- Button indices remapped: the fork assigns SDL button indices in ascending
  keycode order, so ESC/VOL-/VOL+ (1/114/115) occupy 0-2 and the gamepad
  cluster starts at 3 (A=3, B=4, Y=5, X=6, L1=7, R1=8, SELECT=9, START=10,
  MENU=11). Volume keys no longer trigger navigation.
- SDL_CreateWindow was passed height for both dimensions (square window that
  could not fill 640x480), and sized the window from disp_width*ppu, which
  exceeds what mali-fbdev can allocate at higher PPU; with autoscale the
  window now uses the display's current mode.
- Render the first 8 frames instead of one: a single startup flip could land
  on a stale page during the launcher handoff, leaving a blank screen until
  the first button press.

PPU stays 2 (user-preferred after comparing 2/3/4/6 on RG40XXV) with
disp_autoscale_dpi off since SDL_GetDisplayDPI is unsupported here.
The sdl2-h700.patch made the built-in gpio-keys pad enumerable by SDL (needed
by the Files app), but NextUI's platform layer interprets every SDL joystick
with the Bluetooth-pad JOY_* layout. The built-in pad's presses were therefore
processed twice: correctly via raw evdev, and again through the BT mapping
with the fork's ascending-keycode indices (B->L1, Y->R1, VOL- -> back, MENU's
354 keycode -> volume UI), scrambling controls in Settings and every other
NextUI app.

Skip "ANBERNIC-keys" in PLAT_initInput and the SDL_JOYDEVICEADDED hotplug
path: raw evdev stays the sole input path for built-in controls, SDL joystick
remains for external Bluetooth pads only.
pvaibhav added 30 commits August 9, 2026 11:55
Upstream FN action (LoveRetro#788) and USB keep-awake (LoveRetro#783) APIs require
platform definitions that were only added for other platforms.
Provide H700 FN stubs (no assignable buttons) and a zeroing
PLAT_isUSBConnected so the post-rebase build links again.

Document that FN1/FN2/HOME pak actions and USB keep-awake are
not supported on H700.
No need to add development process info for end users. Also added
button shortcuts info like the other devices.
Record hardware validation through 2026-07-20 and scope beta1 vs RC: FBNeo
missing-BIOS hard-lock as beta1 known issue / RC must-fix; remove clean
uninstall from the matrix; defer SIGUSR1 and dirty-card recovery to RC polish;
close accepted power policies and other closed test items.
Export the H700 platform and model during package installation. Have the tg5040 BlueZ post-install exit immediately when invoked from H700, before probing TrimUI state or modifying the root filesystem.

Leave the existing tg5040 and tg5050 installer behavior unchanged.
Make the testing status the canonical evidence ledger with stable test IDs and device-specific results, and make the roadmap the owner of Alpha, Beta, RC, and Release gates.

Convert the subsystem chapters into maintainer knowledge-base references, document representative-family testing, record HDMI and Bluetooth audio results, and reconcile implementation claims against the current build, input, display, audio, power, and connectivity code.

Mark the unexercised crash-loop cutoff as RC qualification work and align the platform input comment with the current SDL classification and evdev design.
Let schedutil use the greatest advertised H700 frequency instead of capping Auto one step below the in-spec 1.5 GHz ceiling. Keep the powersave midpoint cap and performance behavior unchanged.

Add pending PERF-05 hardware validation and make it a high-priority Beta gate. Record tested per-model shader, emulator, and frontend defaults as planned Beta work, and classify overlays for the three logical H700 resolutions as optional Beta or post-release content.
Start the system D-Bus before audiomon so it can observe A2DP connections and switch ALSA away from the internal speaker.

Use BlueALSA's default volume mode because BaseOS cannot initialize the AirPods transport volume in native mode. Address the remaining connection race by querying the BlueALSA mixer explicitly and retrying until its A2DP control appears, ensuring the saved NextUI volume reaches the headset.
Stop remapping PLATFORM=h700 to tg5040-toolchain. Pull and clone
h700-toolchain instead, and update the port docs to match.
Stop cloning and building mali-fbdev SDL2 in the platform early target.
The latest h700-toolchain prebakes this into the container image, plus
also no need to compile bluez since on H700 we use stock bluez.
Use PREFIX_LOCAL from the toolchain image and update the port docs.
SDL2 is built in h700-toolchain; remove the duplicate patch and point
docs at support/sdl2-h700.patch there.
Avoid invoking bluetoothctl when BlueZ is absent, which can block
the init lock and prevent later activation on on-demand stacks.
Time-limit shutdown requests and clear stale discovery clients while
preserving normal stock and stockmod teardown behavior.
The RGcubexx main list stopped 68% down the 720x720 panel, leaving a
black gap above the button hints (issue #18). Rows run from
SCALE1(PADDING + j*PILL_SIZE) to the footer at h - SCALE1(PADDING +
PILL_SIZE), so the panel fits (360 - 10 - 30) / 30 = 10 rows, not the 8
that were hardcoded. Same height and scale as the HDMI path and tg5050,
which already use 10.

Untested on cube hardware.
Cores that share picoarch's build system set `PLATFORM = libretro` in their
own makefile. d4fc5b5 changed the container invocations to
`make PLATFORM=$(PLATFORM)`, and a command-line variable is recorded in
MAKEOVERRIDES, so it overrode that assignment in every recursive make.
picodrive and pcsx_rearmed then built without their libretro frontend
objects and, because the link uses -flto plus a version script that keeps
only retro_* global plus --gc-sections, nothing was reachable: instead of
failing, the link emitted a ~10KB stub with no entry points that segfaults
as soon as minarch resolves retro_init. That stub shipped as the PS1 core
for h700, tg5040 and tg5050 in the 20260720 and 20260724 builds (issue #19).

This is branch-local. Upstream runs a bare `make` in the container and lets
UNION_PLATFORM arrive through the environment, where a makefile assignment
still wins; the host's MAKEFLAGS never crosses the docker boundary. Drop the
argument from build/build-cores/build-core so the guest matches upstream,
and revert 04a1d62's per-core picodrive workaround, which was treating this
same cause. workspace/all/cores/makefile is byte-identical to main again.

Verified: clean rebuilds of pcsx_rearmed and picodrive with no per-core
override both export the libretro entry points, picodrive matches its last
good release size, a full `make build PLATFORM=h700` completes, and on an
RG40XXV the rebuilt PS1 core reports api_version=1, PCSX-ReARMed r26, and
survives retro_init/retro_deinit.
…ernel

Power-off was intermittently leaving the device in limbo: screen dark, rails
still up. PLAT_powerOff kills the backlight and blanks the framebuffer before
it writes the /tmp/poweroff sentinel, so everything downstream of that point is
invisible and any failure presents as "it turned off but it didn't".

Root cause is the same PMIC latch the Brick had. reboot(LINUX_REBOOT_CMD_POWER_OFF)
lands in the kernel's generic axp20x_power_off, which writes AXP20X_OFF_CTRL at
0x32. AXP2202 (same die as AXP717) moved the on/off control group the AXP2101
keeps at 0x10 out to 0x27, so that write is a no-op: the CPU halts and the rails
stay up. Compounding it, the AXP's interrupts are still armed at that point --
0x40-0x44 read non-zero on device, with the power-key edge IRQs (PONP/PONN in
IRQ_EN1) enabled -- and a pending unmasked IRQ holds the IRQ pin low, which
powers the PMU straight back on after >16ms. The press that asked for power-off
is itself the most likely thing to undo it.

03-platform-layer.md listed poweroff_next/reboot_next as deliberately dropped
because "systemd poweroff/reboot work fine". That was wrong twice over: this is
a PMIC latch no init system can fix, and the base OS runs BusyBox init with no
systemd at all.

Port both tools from tg5040 (itself vendored from Helaas's
nextui-brick-poweroff-hook). Register sequence against the AXP2202 at 0x34:
mask 0x40-0x44, clear 0x48-0x4C (write-1-to-clear), 0x22 <- 0x0A (PWROFF_EN:
button event powers off rather than restarts), 0x27 <- 0x01 (SOFT_PWROFF).
The 0x27 write is a plain store, not read-modify-write, so it also clears bit3
(PWROK pulled low restarts the system), observed set as 0x27 = 0x08 on stock
H700 firmware.

Note this is not a battery disconnect, despite how the Brick fix is usually
described; that is 0x12 bit 3 (BATFET_CTRL), which the sequence never touches.
The "soft-disconnect the battery" framing traces to an earlier brute-force
experiment using different registers with misattributed names. 0x12 is
eFuse-defaulted and left alone.

Four deliberate divergences from the tg5040 tool:

- The I2C bus is auto-detected by scanning /sys/bus/i2c/devices/*/name for an
  axp* at 0x34, falling back to /dev/i2c-5. tg5040 hardcodes /dev/i2c-6; H700
  has the PMIC on twi5.
- The card path is resolved with realpath. SDCARD_PATH is /mnt/SDCARD, which
  launch.sh makes a symlink or bind mount onto /mnt/sdcard, and both
  /proc/mounts and /proc/*/fd name the resolved path -- the tg5040 string
  compares against /mnt/SDCARD would never match here.
- No global process kill. tg5040 SIGTERM/SIGKILLs every pid first; the base OS
  has ::respawn:/sbin/nextui-session in inittab, so killing launch.sh starts a
  new frontend racing the shutdown, which is the failure being fixed. The PMIC
  cut is instantaneous, so nothing needs reaping; CFG_getPowerOffProtection now
  gates only sync + swapoff + detach-unmount.
- reboot_next does no PMIC writes at all -- a restart is the SoC's job. It
  exists for the deterministic sync and signal blocking. (tg5040's launch.sh
  calls reboot_next but never builds or ships it, so on the Brick that line is
  a command-not-found followed by exit 0.)

finalize() returns only when every shutdown path has failed, so main returns
EXIT_FAILURE there; otherwise the `poweroff_next || poweroff` fallback in
launch.sh could never fire. Logging goes to /tmp rather than the card: this
path unmounts the card, and the only case where the log matters is the one
where we failed to power off, in which case the device is still running.

--dry-run reports the detected bus, resolved card path and planned writes while
touching nothing; safe on a live device.

Verified on RG40XXV: cross-compiles clean, bus detection and path resolution
confirmed on hardware via --dry-run for both binaries. The power-off itself is
NOT yet hardware-verified.
After the disp0 output switch both the TV and the panel stayed black.

fb_var_screeninfo's pan offset survives a FBIOPUT_VSCREENINFO resize:
coming off the 640x480 panel yoffset is still 480, which is not a page
boundary of the new 1280x720x2 buffer. Measured on RG40XXV, that wedges
the mali fbdev winsys — it never pans again and every frame lands in page
0 — while commitLayerGeometry() had cropped the scanout layer to page 1.
Page 0 full of pixels, page 1 all zeros, crop pointing at page 1.

Reset both offsets when changing geometry and commit crop.y as page 0 to
match, so the layer and the buffer agree from the first scanout and the
blob's flips take over from a sane origin. crop.y joins the read-back
verification too, since the attach path's asynchronous restore can revive
the old page offset after our commit lands.

Only reachable on an OS that provides /sys/kernel/debug/dispdbg; on
BaseOS the switch itself was a no-op until it started mounting debugfs,
which is what kept this hidden.

Validated on RG40XXV: nine hotplug transitions in both directions, all
exiting cleanly with no SetHDMI errors.
These three models do have RGB LEDs; they hang off an MCU on /dev/ttyS5
(115200 8N1, gated by axp2202 mcu_pwr), not /sys/class/leds, which is why
an earlier probe concluded the hardware didn't exist. The MCU animates in
firmware, so effect ids are translated onto its native modes rather than
animated in userspace -- same division of labour as led_anim on tg50x0.

LedControl is now platform-driven (light count, names, effect list), so
h700 offers only the four effects the firmware has and RGB-less models get
a message instead of dividing by zero. Ambient Mode narrows to Off/On.

Also fixes shared code that predates this: LEDS_updateLeds derived its LED
count from $DEVICE and ignored MAX_LIGHTS, and GFX_setAmbientColor wrote
lightsAmbient[3] unconditionally. tg5040/tg5050 now report their real
per-model count -- returning MAX_LIGHTS there would walk a zeroed slot and
write brightness 0 to the global max_scale, killing every LED.

Verified on RG40XXV (colour, brightness, effects, speed, ambient) and
TrimUI Brick (per-LED mapping). RG40XX H and CubeXX are untested: their
second bank and its ordering are assumptions, flagged as LED-01b.
RG SP model name was not known until now, so NextUI was falling through
to the default rg40xx profile, and setting screen width as 640px isntead
of 720px.

After inspecting the stock OS firmware for RG SP, the model name was
found ("RGSP"). All detection paths were updated to allow for this to be
its own device, although it is functionally equivalent to rg34xx/sp,
just with some DTB changes (DRAM timing was tweaked, analog sticks were
deleted, and interestingly, battery got bumped from 3300 mAh -> 3500
mAh).

Also, displaycal will treat it as a different model with its own default
values, even though the screen is the exact same as the RG34xxSP. The
stock OS also has a 640x480 boot logo but this does not concern us.

Untested on hardware (don't have it yet) but sent to a couple of people
who've got the device. Hope it works all OK.
This change enables the "super standby" mode for flip devices. On
non-flip devices this is already enabled by default and cannot be
turned off.

However, on the flip devices for whatever reason it is not enabled.

The super standby writes value 16 to `os_sleep` sysfs node, which
signals to various other parts of the kernel what to do during a
later suspend-to-ram command. The value 16 is not consequential. A
kernel disassembly shows any non-zero value will have the same
effect. To be specific this disables the lid wake interrupt and
also turns off the USB controllers. Without it, the USB controllers
are powered on during sleep which seems to increasese the sleep
battery drain 3x.

On RG34XXSP the sleep drain was 0.8%/hr while on an RG40XXV where
this is already enabled the drain was 0.3%/hr.

This "trick" was found by checking what the stock and stock mod OS
do for super standby. I still have to test it on RG34XXSP overnight
to confirm that this is better than before.
The simple_ondemand governor for the GPU doesn't perform well. It
consistently keeps the GPU at the lowest or intermediate freqs
when it should be throttling up.

It's (probably) because our rendering is vsync'd, and our perception
of the performance is based on whether or not frames are being
missed. But the governor only uses GPU busy time (%) over a 100ms
interval. It doesn't care if one or more frames took maybe 1ms
longer to compute than 16ms, and thus missed the vsync. So spiky
GPU loads will not throttle the GPU clock up.

After a lot of tests (including extra power consumption test), I
decided to pin the GPU to its max frequency unless powersave mode
is selected.

This costs between 9 to 26 mW extra power consumption depending on
GPU usage (no shader vs. heavy shader that uses almost all GPU).
In context, the RG40XXV running at full brightness uses 2000 mW
total while running an NES game with the xbrz-freescale-multipass
shader which has a spiky load that can take up all 16 ms of a
frame (but not every frame). Overall this change is going to cost
users about 0.5% of battery over a 6 hr run or about 2 min less
battery life. There is no effect on thermals that I can measure.

One issue is that the 648 MHz top GPU speed also needs a higher
voltage (0.96V vs 0.90V for all other speeds) and since the power
consumption increases proportional to f*V^2 going from 600 -> 648
MHz increase the power requirement by 23% instead of the more
modest 8% going from 552 -> 600 MHz. In any case my estimate of
the overall effect is still under 2 min of play time over the
battery life of 6hr, totally acceptable and we're only paying for
what we actually want: stutter free games.

Also, minarch debug HUD was causing the GPU speed to be read for
every frame but failing twice before hitting the last working
path. This was causing *massive* cpu time drain 30-40%/frame! I
changed it with help of Claude to the actual working path (1st
option) and a few other potentials that other devices I don't have
may be using - not proven but doesn't hurt.
Expose the L3 and R3 bindings in the H700 PS1 pak so DualShock
games can use the physical stick clicks.

Detect sticks conservatively by model, including the single-stick
RG40XXV and dual-stick H, Pro, SP, and Cube variants.
Like on tg5040/5050, the fceumm core's patch needed an update to
apply cleanly. This changeset adds that update to h700 too.
Enable calibrated DisplayCal defaults for the RG SP, RG28XX,
RG40XXV, and RG34XXSP.

Keep the remaining H700 models disabled with neutral gains until
they are calibrated.
Use the standard 10-unit padding on 480p H700 displays so six list
rows have the same spacing above the bottom button hints. Keep the
existing compact padding for Cube and HDMI layouts.
Stock Anbernic firmware does not provide curl, so RetroAchievements
only worked when NextUI ran on BaseOS.

Build pinned curl, OpenSSL, and musl sources into a self-contained
aarch64 binary and ship a private CA bundle under the NextUI system
tree. This keeps the stock card unchanged and avoids glibc/NSS runtime
dependencies that break DNS resolution.
Catch the docs up with what the code actually does now, mark
h700-rc8 as the golden master for the upstream PR, and record
where the remaining known issues stand.
Replace family DEVICE buckets with exact tokens so sticks,
LEDs, panel geometry, and displaycal select by SKU. RGXX_MODEL
stays scrape/display only.

Until now DEVICE was tacked on and on during development into
strings and grouped buckets that had no rhyme or reason. Since
we are about to get merged upstream, now is the time to settle on
a stable naming convention.

NOTE: this update causes existing minarch config and some other
stuff to become orphan. Users' preferences for devices that got
split by this change (e.g. rg40xx -> rg40xxv / rg40xxh) will get
reset. Better to accept this pain now and stabilise the contract
for Pak authors than to run into problems later.
Update PAKS.md and auto.sh path guidance; trim redundant contract wording
now that h700-device.sh owns the map.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant