Skip to content

plymouth: hand the display over cleanly, survive sysupgrade, suit small panels - #170

Open
dangowrt wants to merge 3 commits into
openwrt:masterfrom
dangowrt:plymouth-various-fixes
Open

dangowrt wants to merge 3 commits into
openwrt:masterfrom
dangowrt:plymouth-various-fixes

Conversation

@dangowrt

@dangowrt dangowrt commented Oct 7, 2026

Copy link
Copy Markdown
Member

The splash now gives the display up and takes it back without help from the application that follows it. In boot-up mode the ucode plugin releases the display on the first key, button or touch press, with input devices announced through /etc/hotplug.d/input since procd replaces /dev after preinit. It publishes a plymouth ubus object reporting whether it holds the display and hands procd a plymouth.release event when it lets go, so services can wait for it with a trigger. For shutdown and sysupgrade, the new plymouth-evict stops whatever holds the KMS device, through procd where the holder is a service, and the splash comes back in shutdown, reboot or system-upgrade mode; for sysupgrade it is restarted from the ramfs. Only the DRM renderer is shipped, as libudev-zero cannot tie an fbdev emulation to its DRM card and plymouth drew on both. The plugin also survives mode changes and ubusd going away, and holds its frame rate under load.

The demo theme is reworked for small hi-DPI panels: the backdrop is drawn at device resolution, pictograms have a minimum size, an uninitialised panel's backlight fades in, and frames are much cheaper to paint. Pictograms are shown in boot-up mode only, upgrades turn a spinner instead of suggesting progress, and shutdown takes the graph down a level every 250 ms so it finishes within the few seconds a router takes to power off.

A deactivated splash kept flushing its displays from a timer. When the
shutdown sequence reactivated the daemon waiting since boot, whatever
had taken the display over still held DRM master, so the splash never
appeared, and the last frame gave way to the console during the final
kill. libudev-zero cannot tie a frame buffer device to its DRM card, so
every fbdev emulation of a DRM driver was driven as a second display.

Carry a patch that stops the frame timer while the splash is
deactivated. Add plymouth-evict, which stops every process holding a
KMS device other than plymouthd and waits until they are gone. A
process that belongs to a procd service is stopped through procd,
which applies the service's own term_timeout and does not respawn it,
and the wait ends on procd's instance.stop notifications; anything
else, and everything once ubus is gone as in sysupgrade's stage2, gets
SIGTERM directly. The shutdown action runs it, ignores rcS cancelling
the K script, and switches to shutdown mode before reactivating, so
the first frame back is already the shutdown splash.

Install plymouthd-fd-escrow, which keeps the last frame on the display
through the final kill, and install the DRM renderer alone, as the
splash is meant for devices with a KMS driver.

Fixes: 1a39b47 ("plymouth: add boot splash for KMS devices")
Signed-off-by: Daniel Golle <[email protected]>
A splash shown by the plugin held the display until something told
plymouthd to let go, gave a waiting display client no way to know when
that happened, and crashed or lost its ubus link on changes of mode.

In boot-up mode, open the input event nodes present when the splash
starts and those procd announces later, and on the first key, button
or touch press send plymouthd the deactivate request over its own
socket. procd replaces /dev with a fresh tmpfs after preinit, where the
splash starts, so the nodes it creates cannot be watched for; its
hotplug.d scripts run for every input device once the node exists,
coldplugged ones included, and the one shipped here hands the node to
the plugin as a plymouth status update.

Publish a plymouth object whose status method reports whether the
splash holds the display, and hand procd a plymouth.release event
whenever it lets go, without waiting for the reply, so a service can
wait for it with a trigger; procd runs triggers for events it is
handed, not for broadcasts. Stop the frame timer on the way to idle
and start it again when plymouth redraws the splash after
reactivating, and drop the unused progress callback.

Clear the ucode VM after freeing it, as uc_vm_init() leaves
vm->registry pointing at the freed registry. Remove the object before
freeing the connection. Drop a connection whose ubusd went away from a
disconnect handler, since plymouth's event loop frees the watch
itself, keep the inotify wait on the ubus socket directory armed so a
restarted ubusd is picked up, and clear the ids libubus caches from
the previous ubusd before registering again.

Keep the splash up through sysupgrade: in stage2, copy plymouth's
files, plugins and ucode modules into the ramfs, take the display back
with plymouth-evict and switch to system-upgrade mode; in do_stage2,
start plymouthd from the ramfs. Schedule frames against a deadline
advancing by one interval, so a theme keeps its frame rate under load.

Fixes: 9ef83aa ("plymouth-plugin-ucode: add a scripted splash plugin for ucode")
Signed-off-by: Daniel Golle <[email protected]>
The theme was laid out and drawn with a 1080p screen in mind, and on a
small panel with a device scale above one it falls short.

The backdrop holding the logo, links and nodes is now allocated in
device pixels and drawn through a canvas scaled by the device scale, so
the static scene is as sharp as what is drawn on top of it. At scale 1
the texture matrix is the identity and plutovg keeps its plain blit.

The pictograms get a floor of 12 logical pixels and shrink only as far
as needed for the row to fit the screen width. The row is reserved for
that size and the logo and graph are laid out in the height left above
it; where the floor does not apply, the layout is unchanged.

In boot-up mode the backlight that the DRM panel references through its
backlight phandle is taken to zero before the first frame and faded in
over 1.5 s to 80% of its maximum, as full brightness lifts the black
background to grey. A panel carrying the bootloader-initialized
property already shows the bootloader's picture and is left alone. On
a backlight whose scale is not perceptual the ramp follows the CIE 1931
lightness curve pwm_bl uses for its own tables.

Nothing reports how far a system upgrade has got, so the upgrade modes
keep the whole graph and turn a spinner around the router node instead
of dismantling the graph on a timer, which would read as progress.
The pictograms stand for services coming up, so only boot-up mode
shows them. A router shuts down within a few seconds, so shutdown and
reboot take the graph down one level every 250 ms, leaves first, and
only the logo is left after three steps.

paint() walked every packet and pulse once per damaged rectangle, and
every packet and pulse damaged a rectangle of its own, so the cost of a
frame grew with the square of the packet count and was spent in script
loops rather than in pixels. A splash connecting to a running bus
received one packet per existing ubus object, without the cap
flow_spawn() observes, and dropped far below its frame rate. Damage is
now collected into one box per frame, paint() draws each packet once
with colours built when the packet is created, pulse rings are filled
as annuli where a stroke cost considerably more, and FLOW_CAP, lowered
to 8 as a handful of packets conveys activity as well as two dozen,
applies to packets launched by ubus objects too.

Fixes: 6aca04f ("plymouth-theme-openwrt-demo: add a demo theme for the ucode engine")
Signed-off-by: Daniel Golle <[email protected]>
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