[PATCH v3 13/13] Documentation/gpu: document the Vino driver
From: Mike Lothian
Date: Wed Aug 26 2026 - 12:46:22 EST
Describe how a dock is identified and placed by family rather than by
product ID, what the driver implements in-kernel in place of EVDI and
DisplayLinkManager, and the module parameters an unfamiliar dock or a
converter with broken DDC may need.
Assisted-by: Claude:claude-opus-5
Signed-off-by: Mike Lothian <mike@xxxxxxxxxxxxxx>
---
Documentation/gpu/drivers.rst | 1 +
Documentation/gpu/vino.rst | 254 ++++++++++++++++++++++++++++++++++
MAINTAINERS | 1 +
3 files changed, 256 insertions(+)
create mode 100644 Documentation/gpu/vino.rst
diff --git a/Documentation/gpu/drivers.rst b/Documentation/gpu/drivers.rst
index 20d2c454aa1d..4b524d6980c4 100644
--- a/Documentation/gpu/drivers.rst
+++ b/Documentation/gpu/drivers.rst
@@ -17,6 +17,7 @@ GPU Driver Documentation
tve200
v3d
vc4
+ vino
vkms
bridge/dw-hdmi
xen-front
diff --git a/Documentation/gpu/vino.rst b/Documentation/gpu/vino.rst
new file mode 100644
index 000000000000..98e82297a82a
--- /dev/null
+++ b/Documentation/gpu/vino.rst
@@ -0,0 +1,254 @@
+.. SPDX-License-Identifier: GPL-2.0-only
+
+==========================
+Vino DisplayLink DL3 driver
+==========================
+
+Vino is a Rust DRM/KMS driver for DisplayLink DL3 USB display devices. Three
+hardware families are supported: Ella (DL-3x00 silicon, e.g. the HP 3005pr),
+Ridge (DL-6xxx silicon, e.g. the Dell Universal Dock D6000) and Navarro
+(DL-7000 silicon, e.g. the DL-7400 quad-display docks). Each family has a
+profile carrying the endpoints, strip geometry, connector count, link limits
+and pacing of its hardware; the rest of the driver reads those values rather
+than branching on the model.
+
+Device identification
+=====================
+
+The driver binds to a DisplayLink *function*, not to a list of product IDs: any
+device with vendor ``17e9`` exposing an interface of class ``0xff``, subclass
+``0``, protocol ``0x03`` (a DL3 display function; ``0x00`` is the older ``udl``
+hardware), plus that device's USB DFU interface. This matches what the vendor's
+own udev rules key on, and means a dock nobody has tested is offered to the
+driver rather than ignored.
+
+Which family a device belongs to is then read from the device itself. Every
+DisplayLink dock carries a sixteen-byte vendor descriptor, type ``0x40``, in
+its ordinary configuration descriptor, holding the running firmware version and
+an eight-character platform name -- ``NavaDock``, ``RidgeDoc`` and so on. It is
+read with a standard ``GET_DESCRIPTOR``, needing no session and no crypto, so
+identification happens at probe.
+
+A device whose identity names a family the driver cannot drive is declined by
+name, so its owner gets a log line and something worth reporting instead of a
+driver guessing at an unknown wire format. A device whose identity cannot be
+*read* falls back to a small product-ID quirk table, so a transient descriptor
+failure does not cost a known dock its displays.
+
+The number of connectors comes from how many of the family's video endpoints
+the device actually exposes, bounded by the profile. A dock in a known family
+with fewer outputs is therefore driven with the outputs it has.
+
+The driver owns the USB device and implements the dock's initialization,
+HDCP 2.2 authentication, encrypted control protocol, downstream monitor
+management, mode programming, cursor updates, video compression, and USB
+submission in the kernel. It does not use EVDI or a userspace display daemon.
+
+Configuration
+=============
+
+The driver is selected by ``CONFIG_DRM_VINO``. It requires Rust, USB, DRM,
+MMU support, the Rust DRM shmem helper, and the kernel crypto primitives used
+by HDCP 2.2.
+
+``CONFIG_DRM_VINO=m`` builds ``vino.ko``.
+
+Verbose protocol and scanout diagnostics are disabled by default. Pass
+``debug=1`` when loading the module to enable them::
+
+ modprobe vino debug=1
+
+Errors, connection changes, and session state remain visible without this
+parameter.
+
+The remaining module parameters are for recovery and diagnosis:
+
+``edid_override``
+ Bitmask of connectors whose EDID the dock cannot read -- typically a monitor
+ behind a DP-to-HDMI converter that mangles DDC -- and which are described by
+ DRM's own EDID override instead.
+
+``force_flash``
+ Write the packaged dock firmware even when the dock already runs that version
+ or newer. See `Firmware updates`_.
+
+``rtc_utc_offset_minutes``
+ Local offset from UTC, in minutes east, used when synchronizing a Navarro
+ dock's real-time clock.
+
+``trace_crypto``
+ Discloses the ephemeral control and video keys of one session so that a
+ ``usbmon`` capture can be decrypted. For protocol work only.
+
+The optional ``CONFIG_DRM_VINO_KUNIT_TEST`` setting builds the driver's KUnit
+suite. It is intended for development kernels and is disabled by default.
+
+KMS model
+=========
+
+A dock exposes independent display connectors: two on the D6000, four on the
+DL-7400, where they are multiplexed over two video endpoints. Each has:
+
+* one primary plane using ``DRM_FORMAT_XRGB8888``, and ``DRM_FORMAT_XRGB2101010``
+ as well where the dock's link carries ten bits per channel;
+* one cursor plane using ``DRM_FORMAT_ARGB8888``;
+* one CRTC, encoder, and connector;
+* a downstream EDID channel; and
+* a bulk-OUT video endpoint, which two connectors may share.
+
+Atomic commits record the latest desired state and wake an ordered,
+device-owned control queue. Blocking USB transactions are never issued from
+the atomic callback. A transient control failure retains the desired
+generation and retries it; a newer atomic state always supersedes an older
+retry.
+
+The primary plane supports the four DRM rotations and both reflection axes in
+all valid combinations. Rotated and reflected frames are conservatively sent
+as full updates. Their independent codec strips are still encoded in parallel;
+identity scanout additionally supports damage updates and encoded-strip reuse.
+
+Mode validation
+===============
+
+A mode reaches the dock as a timing plus two control words describing its sync
+polarity and its CTA video identification code. Those words are taken verbatim
+from decrypted vendor captures for the timings a capture covers -- 1920x1080 at
+60 and 120 Hz, and 2560x1440 CVT-RB at 60 and 120 Hz -- and derived from the
+mode's own sync flags and VIC otherwise, so a monitor's native timing is driven
+rather than approximated.
+
+A mode is refused when it exceeds the ceilings its dock's profile names: the
+highest pixel clock a single connector may carry, an optional refresh-rate cap
+where the vendor driver is known to clamp, and the dock-wide pixel budget. The
+budget is shared, so the atomic check enforces the combined rate of the
+connectors a commit leaves enabled, while mode validation refuses only a single
+mode too large for the whole dock.
+
+Framebuffer ownership and damage
+================================
+
+The dock cannot scan out a GEM object directly. Vino therefore copies changed
+strips from the shmem framebuffer into driver-owned snapshots before the
+atomic commit completes. The compositor may reuse its source buffer after
+that snapshot without racing the encoder.
+
+Each head retains at most four validated, owned shmem mappings, matching a
+typical compositor swapchain. Repeated flips reuse those prepared mappings;
+round-robin eviction and DPMS teardown keep pinned memory bounded. This is the
+USB-display equivalent of preparing buffers before submission and requires no
+driver-specific userspace API.
+
+Encoding and USB submission run asynchronously on per-head workers. Damage is
+tracked against the last frame successfully submitted to the dock, not merely
+against the previous atomic commit. This preserves changes when commits are
+coalesced or a transfer fails.
+
+A strip whose content changes is charged one transmission for each buffer the
+dock rotates through, plus one, and remains selected until that debt is paid.
+One presentation reaches exactly one of those buffers, so a strip delivered to
+only some of them would leave the panel alternating between old and new
+content. A surface with no change and no debt outstanding sends nothing.
+
+The video path keeps a bounded, persistent USB request ring. The first
+presentation after a mode change carries the decoder arm sequence and the
+opening frame in one USB request, as required by the receiver.
+
+Control and authentication
+==========================
+
+One per-device session owns the HDCP and encrypted-control counters, keys,
+nonces, EP02 submissions, and EP84 replies. The control queue serializes
+transactions so a KMS update cannot interleave with a heartbeat or monitor
+operation.
+
+Initial transport, authentication, and encrypted-control setup is retried after
+transient failures. Once authentication has succeeded, a timeout while
+discovering one downstream monitor does not discard the live session. That
+head remains disconnected until the bounded runtime re-engagement path
+obtains a valid EDID.
+
+HDCP message identifiers and HDMI mode matching use the DRM display helpers.
+AES, AES-CMAC, SHA-256, HMAC-SHA256, and RSA operations use kernel crypto
+interfaces. Session material is stored per device and is not exposed through
+a driver-specific userspace API.
+
+Colour management
+=================
+
+The CRTC advertises ``CTM`` and a 256-entry ``GAMMA_LUT``, and both are applied
+in software during encoding. A dock has no colour hardware to program, so a
+compositor correcting through those properties -- as GNOME's Night Light and
+KDE's Night Colour do -- would otherwise have nowhere to put the correction on
+this output while native outputs are corrected normally.
+
+Colour depth and HDR
+====================
+
+On a dock whose silicon carries ten bits per channel, each connector also
+advertises ``max bpc``, ``Colorspace`` and ``HDR_OUTPUT_METADATA``. The wire
+format is one codec parameterised by sample depth rather than two: an HDR
+frame differs from an SDR one only in how deep its samples are, in the escape
+ceilings the entropy coder is allowed to reach, and in the transfer function
+and colorimetry stated in the mode set.
+
+The link's depth is taken from ``max bpc`` together with the PQ transfer
+function the compositor attached, not from the committed framebuffer's format:
+driving a ten-bit link from an eight-bit surface is ordinary, and a sample is
+widened into the deeper link after it is decoded. A ten-bit connector costs the
+dock a third more bandwidth per pixel, so the shared budget is priced at the
+deepest connector in use, and where a pair of modes does not fit at ten bits
+the depth gives way rather than the mode -- a compositor handed ``EINVAL``
+disables the output instead of asking for a shallower link.
+
+Firmware updates
+================
+
+A dock reports the firmware it is running in a vendor descriptor, and the
+vendor's ``*-release.spkg`` packages state the version they carry. If the
+kernel firmware loader can supply a package that is newer than what the dock
+is running, vino writes it over USB DFU before opening a control session.
+
+Two deliberate paths exist alongside that. ``force_flash=1`` writes the
+packaged image even when the dock already runs that version or a newer one,
+which is how a dock left in a bad state is recovered. The
+``/sys/class/firmware/vino-<dock>/`` upload interface takes an image from
+userspace and writes whatever version it is, which is the only way to re-flash
+the running version or to go back to an older one. Both refuse an image that is
+not a DisplayLink package or is for another dock family: the DFU interface
+supports no upload, so the running image cannot be read back and there is
+nothing to restore from.
+
+Monitor handling
+================
+
+EDID reads are tunneled through the dock's encrypted control protocol. An
+unsolicited downstream event schedules a fresh presence probe. Removal,
+reattachment, and a connector powered down by userspace are tracked separately
+so a deliberate power transition is not reported as a physical unplug.
+
+The initial driver does not register a virtual I2C adapter for DDC/CI monitor
+controls.
+
+Disconnect
+==========
+
+USB I/O is guarded by a revocable I/O window. Disconnect closes that window
+before workers and request queues are drained, preventing new transfers from
+starting during teardown. The owned DRM registration, timers, work queues,
+frame snapshots, and USB queues are then released by their Rust owners.
+
+Testing and validation
+======================
+
+With ``CONFIG_DRM_VINO_KUNIT_TEST=y``, the protocol and codec have KUnit
+coverage for cryptographic known-answer tests, captured control-message
+vectors, mode profiles, decoder-arm framing, codec boundaries, damage
+selection, USB record construction, and serial-versus-parallel output for all
+rotation and reflection combinations.
+
+A focused compile check is::
+
+ make LLVM=1 rust/kernel.o drivers/gpu/drm/vino/vino.o
+
+External-module ``modpost`` also needs a completed kernel build with a
+matching ``Module.symvers``.
diff --git a/MAINTAINERS b/MAINTAINERS
index 79ccf112fabd..1b0d496897ea 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -8541,6 +8541,7 @@ DRM DRIVER FOR VINO DISPLAYLINK DL3 DEVICES
M: Mike Lothian <mike@xxxxxxxxxxxxxx>
L: dri-devel@xxxxxxxxxxxxxxxxxxxxx
S: Maintained
+F: Documentation/gpu/vino.rst
F: drivers/gpu/drm/vino/
DRM DRIVER FOR VIRTUAL KERNEL MODESETTING (VKMS)