Skip to content

drm/vc4: kms: Reduce firmware clock requests during atomic commits#7498

Draft
popcornmix wants to merge 1 commit into
raspberrypi:rpi-6.18.yfrom
popcornmix:hvsclock
Draft

drm/vc4: kms: Reduce firmware clock requests during atomic commits#7498
popcornmix wants to merge 1 commit into
raspberrypi:rpi-6.18.yfrom
popcornmix:hvsclock

Conversation

@popcornmix

Copy link
Copy Markdown
Collaborator

On Raspberry Pi the core clock is managed by the firmware, so every clk_set_min_rate() call on it results in at least one blocking mailbox round-trip to the VPU: the clock is registered with CLK_GET_RATE_NOCACHE, so clk_set_rate_range() always queries the current rate via GET_CLOCK_RATE, and issues a SET_CLOCK_RATE on top if the aggregated rate changed. All of this happens under the global clk prepare_lock.

vc4_atomic_commit_tail() currently requests a boost of the core clock to at least 500MHz at the start of every commit and settles back to the load-derived rate at the end, even though the boost is only needed to avoid stalling the pipeline during a full modeset. In addition the final drm_dbg() evaluated clk_get_rate() unconditionally, hiding another firmware query. A compositor page-flipping at 60Hz therefore generated several mailbox transactions per frame, all of them no-ops.

Fix this in two ways:

  • Only apply the 500MHz floor when one of the CRTCs in the commit actually needs a modeset. Plane-only updates still raise the clock to the maximum of the old and new state requirements before the HVS is reprogrammed, since the load can change without a modeset.

  • Cache the last requested minimum rate in struct vc4_hvs and skip requests that wouldn't change it. The core and display clocks are always requested at the same rate, so a single cached value is enough, and the actual-rate debug query now only happens when the request really changes.

With this, steady-state page flips no longer generate any firmware mailbox traffic, while modesets and genuine load changes behave as before.

On Raspberry Pi the core clock is managed by the firmware, so every
clk_set_min_rate() call on it results in at least one blocking
mailbox round-trip to the VPU: the clock is registered with
CLK_GET_RATE_NOCACHE, so clk_set_rate_range() always queries the
current rate via GET_CLOCK_RATE, and issues a SET_CLOCK_RATE on top
if the aggregated rate changed. All of this happens under the global
clk prepare_lock.

vc4_atomic_commit_tail() currently requests a boost of the core clock
to at least 500MHz at the start of *every* commit and settles back to
the load-derived rate at the end, even though the boost is only needed
to avoid stalling the pipeline during a full modeset. In addition the
final drm_dbg() evaluated clk_get_rate() unconditionally, hiding
another firmware query. A compositor page-flipping at 60Hz therefore
generated several mailbox transactions per frame, all of them no-ops.

Fix this in two ways:

  - Only apply the 500MHz floor when one of the CRTCs in the commit
    actually needs a modeset. Plane-only updates still raise the clock
    to the maximum of the old and new state requirements before the
    HVS is reprogrammed, since the load can change without a modeset.

  - Cache the last requested minimum rate in struct vc4_hvs and skip
    requests that wouldn't change it. The core and display clocks are
    always requested at the same rate, so a single cached value is
    enough, and the actual-rate debug query now only happens when the
    request really changes.

With this, steady-state page flips no longer generate any firmware
mailbox traffic, while modesets and genuine load changes behave as
before.

Signed-off-by: Dom Cobley <popcornmix@gmail.com>
@popcornmix

Copy link
Copy Markdown
Collaborator Author

@XECDesign does this help your display latency testing.

@XECDesign

XECDesign commented Jul 14, 2026

Copy link
Copy Markdown
Contributor

The pi no longer reboot reliably, but when it does, the numbers are what you'd expect.

  ┌──────────────────────────┬─────────────────┬────────────────┬────────────────────┐
  │         Measure          │ before          │ after (PR7498) │         Δ          │
  ├──────────────────────────┼─────────────────┼────────────────┼────────────────────┤
  │ t0→flip p50              │ 39.25           │ 36.11          │ −3.14 (−8%)        │
  ├──────────────────────────┼─────────────────┼────────────────┼────────────────────┤
  │ pre-flush                │ 3.16            │ 0.02           │ −3.14              │
  ├──────────────────────────┼─────────────────┼────────────────┼────────────────────┤
  │ commit-tail ramp windows │ 1687/1687       │ 0/1686         │ eliminated         │
  ├──────────────────────────┼─────────────────┼────────────────┼────────────────────┤
  │ mailbox RTTs/key         │ 10 (21.4 ms)    │ 6 (18.3 ms)    │ −4 calls / −3.1 ms │
  ├──────────────────────────┼─────────────────┼────────────────┼────────────────────┤
  │ clk_set_min_rate/key     │ 2               │ 0              │ gone               │
  ├──────────────────────────┼─────────────────┼────────────────┼────────────────────┤
  │ slip                     │ 53.1%           │ 35.9%          │ −17 pts            │
  └──────────────────────────┴─────────────────┴────────────────┴────────────────────┘

I'll do a bit more testing to be 100% sure the patch is the problem, but it may be worth somebody else trying it as well. I'm seeing a boot failure about every 4th time. Black screen, probably not getting past the module load in initramfs.

Edit: I don't think the reboot issue is caused by the patch. Still getting the same problem after reverting it.

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.

2 participants