concordance
HDMI 2.1 mode negotiation — policy layer of the display connection stack.
Given sink, source, and cable capabilities, concordance answers: "what modes can I drive on this display, in what priority order, using what color format and bit depth?"
The library takes three caller-supplied structs — SinkCapabilities, SourceCapabilities,
and CableCapabilities — and runs them through a three-stage pipeline: enumerate all
candidate configurations, check each against HDMI specification constraints, rank the
accepted candidates according to a policy. The output is a ranked Vec<NegotiatedConfig>,
each entry carrying the resolved mode, color format, FRL tier, and any non-fatal warnings.
A ReasoningTrace on each entry records why a configuration was accepted or rejected, so
every output is fully explainable.
For embedded and firmware targets, the constraint engine is also exposed as a standalone
no_std-compatible probe:
use ;
use ;
use ;
// For standard CTA modes, use vic_to_mode — it carries the exact pixel clock from
// the CEA-861 timing table, so bandwidth ceiling checks are precise.
// VIC 97 = 3840×2160 @ 60 Hz (594 MHz). Other common VICs: 16 = 1080p@60, 97 = 4K@60.
let mode = vic_to_mode.unwrap;
let config = new;
match is_config_viable
For the full ranked pipeline (alloc/std):
use NegotiatorBuilder;
let configs = default
.negotiate;
for cfg in &configs
Custom constraint rules can be injected without replacing the default engine:
use NegotiatorBuilder;
let configs = default
.with_extra_rule
.negotiate;
Why concordance
Ranked iterator, not a verdict. There is no single right answer. The library enumerates all valid configurations in a defined, documented priority order and lets the caller pick. No mode is silently discarded — rejections appear in the reasoning trace.
Every output is explainable. Each NegotiatedConfig carries a ReasoningTrace
recording the decisions made during negotiation. A driver or diagnostic tool can explain
exactly why a given mode was selected or excluded.
Extensible without forking. The three pipeline components — ConstraintEngine,
CandidateEnumerator, ConfigRanker — are traits with default implementations. Any
component can be replaced or wrapped via NegotiatorBuilder without touching crate source.
For the common case of adding a rule on top of the default checks, with_extra_rule injects
a single ConstraintRule without reimplementing everything else.
Custom violation and warning types. ConstraintEngine and ConfigRanker declare
associated Warning and Violation types bounded by Diagnostic. A custom component emits
its own variants with full type fidelity — no wrapping, no loss of structured information.
Cable is a first-class constraint. Link capability is determined by source, sink, and
cable. A cable that cannot carry FRL or whose TMDS clock ceiling is too low produces a
Violation like any other constraint failure. CableCapabilities::unconstrained() is
provided for callers without cable information.
no_std at all resource levels. Three build tiers are explicitly supported:
- Bare
no_std(no allocator):is_config_viableborrows all inputs, no heap use. no_std + alloc: the ranked pipeline andReasoningTraceare available.std: full feature set, additive on top ofalloc.
Serde on all public types. Every public type derives Serialize/Deserialize behind
the serde feature flag, covering inputs, outputs, and policy types.
Feature flags
| Flag | Default | Description |
|---|---|---|
std |
yes | Enables the ranked pipeline and ReasoningTrace; implies alloc |
alloc |
no | Enables the ranked pipeline without std |
serde |
no | Derives Serialize/Deserialize on all public types |
Without alloc or std, only is_config_viable is available. The three input structs and
CandidateConfig are available at all tiers.
Stack position
concordance is the policy layer. It consumes the typed model; it does not produce it.
flowchart LR
piaf["piaf\n(EDID parser)"]
dc["DisplayCapabilities"]
sink["SinkCapabilities"]
nb["NegotiatorBuilder"]
out["Vec<NegotiatedConfig>"]
integ["integration layer\n(GPU / cable discovery)"]
piaf --> dc
dc -->|"sink_capabilities_from_display"| sink
sink --> nb
integ -->|"SourceCapabilities\nCableCapabilities"| nb
nb --> out
All types in SinkCapabilities — VideoMode, HdmiForumSinkCap, HdmiVsdb,
ColorCapabilities, etc. — are from
display-types, the shared vocabulary crate.
sink_capabilities_from_display in concordance bridges a parsed DisplayCapabilities
directly into SinkCapabilities; the integration layer only needs to supply source and
cable capabilities.
Out of scope
- EDID parsing — that's piaf.
- Source capability discovery — querying DRM/KMS or VBIOS for GPU limits.
- Cable capability discovery — reading the HDMI cable type marker or accepting a user override.
- Link training — determining whether a negotiated FRL tier is achievable on real hardware.
- InfoFrame encoding — signaling the negotiated configuration to the sink.
- HDCP — out of scope for the entire stack.
Documentation
Extended documentation lives under doc/.
Understanding the library
doc/architecture.md— pipeline structure, constraint rules, ranking algorithm, and design principlesdoc/enumerator.md— candidate enumeration, Cartesian product dimensions, and pre-filtering
Contributing
doc/setup.md— build, test, and coverage commandsdoc/testing.md— testing strategy, fixture corpus, and CI expectationsdoc/roadmap.md— planned features and future work