Repository navigation
display-output: the compositor presents to the display driver, and tgl-display answers at vblank (stage 3) - #1134
Conversation
4fb5d22 to
f256164
Compare
Review r1 of #1134 at
|
…l-display answers at vblank (stage 3)
PSR needs to know which frame changed, and where. Without that, PSR1 sees
a CPU write to the firmware's surface only through FBC host tracking of a
graphics-aperture fence, which Vol 12 limits to "projects that support
that". The compositor knows both, so it says them.
`lib/display-output` (`toyos-display-output`) owns the protocol, in the
orchestrator's approved shape:
- the driver's first frame is `Hello{size}`, the output it scans out;
- the compositor sends `Present{sequence, damage}`, one to eight
rectangles each inside that output;
- the driver answers `Presented{sequence, vblank_nanos,
refresh_interval_nanos}`.
One present is outstanding at a time (`Presenting`). Damage composed
meanwhile is merged, past eight rectangles into their bounding box, which is
the one region a PSR2 selective update takes anyway. Every frame is decoded
once, and an answer to no outstanding present is the driver's error.
The compositor serves `display-output` to one driver at a time. After each
blit it hands the composed regions to `Presenting`, and it never waits on
the driver. A refusal, a frame out of protocol, a hello for another size
than the desktop's or a hello later than the handshake timeout drops the
driver by name.
tgl-display offers the firmware's plane (PLANE_SIZE now gives its height)
and answers each present at the vblank after it arrived:
- `interrupt::claim` masks and disables every display interrupt category
but pipe A's, and enables display and primary interrupts.
- `arm` clears pipe A's identity and enables its vblank only while a
present waits.
- `serve` follows Vol 12's service routine and disarms on the vblank. A
pending category this driver enabled nothing in is refused by name.
- The wait has a hang ceiling of FRAME_CEILING_NANOS.
- One connection is served. When it ends, why is said and presents stop,
since a driver the compositor dropped would be dropped again.
Every compositor row serves the port, and tgl-display receives it.
metalcase runs tgl-display beside hda, so its new T14 row
`display_output_presents` shows a present answered, and hda playing with PG3
down, in a boot the other metalcase rows already take.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ugh it Review r1 of #1132 asks for one T14 boot showing `hda` and the panel unharmed by stage 2. This row's metalcase boot already runs tgl-display beside `hda`; what it lacked was order and the panel. - `tone_after_dc6` waits in this boot's log for tgl-display's DC6 allow, then plays `audio_tone`'s muted tone through `hda` to its end, so the analog codec is shown playing with PG3 down. A refusal of DC6 fails it by name. metalcase's test-runner now receives `volume`, which the muted tone needs. - tgl-display logs pipe A's frame counter at its first answered present. The judge holds it past the counter logged with the allow: the timing generator ran on after DC6 was allowed. - The judge requires DC6 allowed on DDI A with DC_STATE_EN 2. It says whether `hda` walked its codecs before or after PG3 went down. - `audio::tone_played` is `tone_on_metal` for any job. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…not its spawn `on_an_output` held soundserver's first output offer to before the tone job's spawn. `tone_after_dc6` spawns at boot and waits for tgl-display's DC6 allow before it plays, so on the T14 (f256164) it was judged to play to the null sink, though its stream opened half a second after `hda`'s output was offered. The claim is about the stream, so the judge reads the first `soundserver: opening stream` after the spawn. For a job that plays at once nothing changes. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…f one carries f566873 marked a tone job's stream by "soundserver: opening stream". The harness's own audio fixture has soundserver say only "client N connected", as every real log also does, so `metal_audio_judges` went red. The judge now takes the first client connection soundserver says after the spawn. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… the interrupt comments true - A driver whose first frame came after the handshake timeout woke the compositor readable and overdue in one turn. Readiness was computed before the sweep dropped it, and the read unwrapped what had gone, so the compositor panicked. The connection's state is now `toyos_display_output::Output`, which refuses a hello past its deadline in the very call that reads it. The session computes readiness after the sweep and unwraps nothing. Host tests cover: - a hello one nanosecond late; - every way out of protocol, a frame carrying handles among them; - a served output's answers. - The driver connects again when a compositor that was answering presents goes away, a restart or a `swap`, at most 8 times a boot, and says each attempt. The queued connection, and the compositor's next present, are the events it waits on; there is no delay. A protocol refusal, or a compositor gone before it took a hello, ends presents by name. Frames carrying handles are refused, since the protocol has none. On the way out the driver disarms everything (`interrupt::release`). - `regs.rs`: each IMR is 8 below its IER, not 4. `interrupt::claim` names what it masks: every category Vol 2c has registers for, with PCH and PICA left as found and refused by `serve` when pending. - Two harness fixtures hold `on_an_output` to the stream's connection: an output offered between a waiting job's spawn and its stream passes, and one offered after the stream fails. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
ebba90d to
4d54cce
Compare
Review r2 of #1134 at
|
The compositor sends a present only once it has taken the hello, so a present arriving proves the connection was served. The driver now connects again after any such connection ends from the compositor's side, so a compositor swapped while the desktop was idle, or before its first present reached a vblank, is reconnected to. Before, the flag was set only when a present was answered. The bound of 8 still contains a loop. `Ended::Gone`'s doc says what EOF can mean: the compositor going away, or it dropping the driver for a fault of the driver's own. `serve`'s doc and the track say exactly what the code does. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…irst present (stage 4)
The engine enters DC5 with the pipe lit only while PSR is active (Vol 12
"Display C5 and C6"), so stage 2's DC6 saves little without it.
The source side follows Vol 2c and Vol 12 throughout. The panel's side
comes from the clean-room brief of VESA facts and is recorded in the PR's
evidence:
- the AUX framing;
- DPCD 00070h-00071h, 00170h, 00600h and 02006h-02009h;
- the setup-time table.
- `aux`: native reads and writes on DDI A's channel, by Vol 12 "AUX
Functional Sequence".
- The data registers are MSbyte first.
- The timeout is the 4 ms maximum.
- The fast-wake count is 7 and the sync count 31, Vol 2c's values.
- DEFER, NACK and short replies are retried, with the reply time-out
between attempts.
- A transaction runs only with PSR off and DC states disallowed, by the
sequence's step 2.
- `psr::prepare`, before DC6:
- refuses PSR already on, a PSR AUX error, a delayed vblank and a
disabled watermark level. The last two need a latency-reporting change
this driver does not make;
- powers AUX A's IO;
- reads the panel's PSR version and capabilities, eDP capability, error
status and status, and refuses a panel without PSR, without SET_POWER,
with a reserved setup time or with errors;
- holds the setup time to vertical blank less a line (Vol 2c SRD_CTL);
- tells the panel the main link stays active, then enables it and sets
D0;
- enables the VSC DIP with hardware's header and data;
- masks max sleep, memory-up and hotplug, and unmasks the exit and
AUX-error identities.
- `Prepared::activate` enables SRD. Context restore is kept, TP1 and TP2
take 2.5 ms, TP4 is skipped, and idle frames are at least 6.
- `Psr::update` flips the surface to itself, a frame update event. `awake`
reads SRD_STATUS's state, `disable` awaits IDLE within Vol 12's bound,
and `fault` disables on a PSR AUX error.
Vol 2c's SRD_CTL says transcoder A's SRD "only operate[s] in standby".
The brief's vendor-code fact says Tiger Lake turns the link off. The PRM
decides, so the panel is told the link stays active, and the boot reads
both SRD_STATUS's state (010b link off, 111b link on) and its link field.
tgl-display enables PSR only at the compositor's first present, and
disables it when the connection ends. Nothing else would end SRD for a
change, and the compositor's first present is now the whole output as it
stands when it reads the hello. A present is answered at the first vblank
out of SRD. The driver says each of the first 16 presents' latency and SRD_STATUS,
then windows of 16 split by whether SRD had to end first, with
SRD_PERF_CNT. The T14 row `psr_exit_latency` rides
display_output_presents' boot.
The panel is read apart from `psr::prepare` (`psr::read_sink`), with DPCD
00000h-0000Fh, the receiver capabilities: its DPCD revision is never 0
from a receiver that answered, and a zero is refused by name
(`NoReceiver`). Every byte read is said before anything decides on it.
The T14's first boot (8230661) refused PSR on a zero at 00070h and said
nothing else of the panel.
Rebased onto #1134's review fixes:
- The whole first present is `toyos_display_output::Output`'s: a taken
hello answers it (`Received::Offered { first }`), and the compositor
sends it.
- A connection that ends from the compositor's side is connected to
again. `Psr` keeps its `Prepared`, so `disable` hands it back with the
panel still configured, and the next connection's first present enables
SRD again with no AUX. A PSR AUX error disables PSR for the boot.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Draft, stacked on #1132 (
display-stage2,166ea4dca).Problem
PSR (stage 4) needs to know which frame changed, and where. Without that, PSR1 sees a CPU write to the firmware's surface only through FBC host tracking of a graphics-aperture fence. Vol 12 "CPU Host Front Buffer Tracking" limits that to "projects that support that". The compositor knows both, so it says them.
What this PR does
Stage 3 of
issues/each-gpu-drives-its-displays-from-a-userland-driver.md, in the shape the orchestrator approved on 2026-11-01.lib/display-output(toyos-display-output) owns the protocol. Every frame is decoded once.Hello{size}is the driver's first frame: the output it scans out.Present{sequence, damage}names one to eight rectangles, each inside that output.Presented{sequence, vblank_nanos, refresh_interval_nanos}is the driver's answer.Presentingkeeps one present outstanding. Damage composed meanwhile is merged, past eight rectangles into their bounding box. That bounding box is the one region a PSR2 selective update takes anyway. An answer to no outstanding present is the driver's error.display-outputto one driver at a time. After each blit it hands the composed regions toPresenting, and it never waits on the driver. A refusal, a frame out of protocol, a hello for another size than the desktop's or a hello later than the handshake timeout drops the driver by name.interrupt::claimmasks and disables every display interrupt category but pipe A's.armclears pipe A's identity and enables vblank only while a present waits, so an idle desktop takes no 60 Hz interrupt.servefollows Vol 12's "Interrupt Service Routine" and disarms on the vblank. A pending category it enabled nothing in is refused by name. The PCH (SDE) category's registers are not in Vol 2c, so they are not zeroed.swap, or the compositor dropping the driver), the driver connects again, at most 8 times a boot. The compositor sends a present only once it has taken the hello. The queued connection and the next present are the events it waits on. A protocol refusal, or a connection that ended before any present arrived on it, ends presents by name, and the driver disarms its interrupts on the way out.metalcasenow runs tgl-display besidehda.Decisions
vblank_nanosis the claim's interrupt record time, a little after the vblank's start.Presentedyet. It still blits the firmware's framebuffer itself until stage 5 moves that into a driver. The answer's fields are the final protocol's.Checks
Review r2, at
e884ee2f6: a connection counts as live once a present arrives on it; host CI exit 0, 107 steps green. The docs and the track say what the code does.Review r1, at
4d54cce89:cargo run -- --ci hostexit 0, 107 steps green.display_output_presentsPASS.toyos_display_output::Outputowns the handshake, with host tests for a hello past the deadline, frames out of protocol and frames with handles.r2, at
f256164ab: host CI exit 0, 107 steps green;--build-onlyexit 0. The run before had a red:suite_splitwanted the new job inRUST_SKIP, which it now is. Evidence: https://github.com/ToyOSOrg/evidence/blob/dc1d7cb98586f2fc299f1f36b9c5a7aa09db503e/r2/README.mdr1, at
4fb5d22f0(the rebase changed none of this code):cargo run -- --ci host: exit 0, 107 steps green.cargo test -p toyos-display-output: 6 pass. They cover:cargo test -p toyos-tgl-display: 34 pass, 3 of them new interrupt tests:The T14 test now pins 1920x1080 and a refresh interval of 16.67 ms.
Negative controls, each a checked mutation restored in the same run, exit 101 each:
Presentingsending while a present is outstanding;armnot clearing identity first.cargo run -- --build-only: exit 0.the_compositor_drives_the_virtio_gpu(QEMU): exit 0, PASS. The compositor starts with the new port and no driver, and its presents go nowhere. It ran before the last edits, which touched only tgl-display's main, metalcase, a metal row and docs, none of which this test reaches.Evidence: https://github.com/ToyOSOrg/evidence/blob/e3e4100f568640262500f27f3abd1aee4e5755bf/r1/README.md
T14 boot (f256164): ran, exit 0; judged PASS
Evidence: https://github.com/ToyOSOrg/evidence/blob/33149bbebcb2b4ceedfe7736dd8a8757ba907120/r3/README.md
display_output_presentsPASS, judged offline on the readback after the tone judge's fix. At its own head it was red, because the judge held hda's offer to before the tone job's spawn, and this job spawns at boot and waits for DC6. f566873 and its follow-up judge at the job's stream.tone_after_dc6played whole after the DC6 line, with no underrun.Risks
metalcaseon the T14 now runs the display driver, so its other rows boot with tuned watermarks and DC6 allowed.🤖 Generated with Claude Code