From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from mail-wr1-f44.google.com (mail-wr1-f44.google.com [209.85.221.44]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 798C330CD85 for ; Fri, 3 Jul 2026 03:02:15 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.221.44 ARC-Seal:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1783047737; cv=none; b=u0zeD5YK5TB01I3HayP3fKkgXfqcH0LJ1PZplIOJBgmtOQQftHhDKrRawySr0wN7DpkHl/MR/HjgrDGjk7MbwBkpPJXShj0rsm3M5xaS7JfGuhvPY1RmsswvSRArTfqAEUINBYeT5ATjiFxoFGdTdx8Xl+d+EawjGLyyybkMRbM= ARC-Message-Signature:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1783047737; c=relaxed/simple; bh=fRfRWSbJImzHZruC9DYmRbxAwGAifNOTfVGD9HhsIVk=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=Ud6RXzUAoJmjqM90fJmvS7Em9v96hAl+IX+uh1dyyNrwTaN9V8En6EhbhYsUYJCDAiXCWPE0LrIoLmY5r8U3h77vyWL/bwa2l5Zq5+/1/OtX41sq4SmDaGvK6sCZqFAM7qvXPpj98NucNybM0h00MhEFEeuKV81dXsZkz5/pP90= ARC-Authentication-Results:i=1; smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=fireburn.co.uk; spf=none smtp.mailfrom=fireburn.co.uk; dkim=pass (2048-bit key) header.d=fireburn-co-uk.20251104.gappssmtp.com header.i=@fireburn-co-uk.20251104.gappssmtp.com header.b=srvPcAMv; arc=none smtp.client-ip=209.85.221.44 Authentication-Results: smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=fireburn.co.uk Authentication-Results: smtp.subspace.kernel.org; spf=none smtp.mailfrom=fireburn.co.uk Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=fireburn-co-uk.20251104.gappssmtp.com header.i=@fireburn-co-uk.20251104.gappssmtp.com header.b="srvPcAMv" Received: by mail-wr1-f44.google.com with SMTP id ffacd0b85a97d-475881b9a4bso79216f8f.3 for ; Thu, 02 Jul 2026 20:02:15 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=fireburn-co-uk.20251104.gappssmtp.com; s=20251104; t=1783047734; x=1783652534; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to:content-type; bh=676W53NrP+v1JtRit87SUFwRygfrhSqyRXaV/N96IbY=; b=srvPcAMvJV6eIG2uXRn0NaCQU2kG5pcrL+ZCt95nD1uaDp2DnBmuAfqO4+QAR8qqxr uXPXiGsg819zb/VTTyLt93QQ42IhCBIOkI8IHCI03KtN54gtCaqOftWjG04oeWipqE6A bl9+aHBvy6XbxHCHX5EwLG9PJzDfAW+2ABU6jxUegoswHK38kfNP0TYeyVsiTJicebjO 86VSPt+7TDtXcl/V8b2tB7lWH5wO2XTw2W003w4FgxpPtMXF2F61T9Ru61Tm4q7G5mIa XFrPZhojuyTQ69bj6ayZoyd6oioZZ1Vvz2OUElgaqWq8xxYSPQb2Nbs1s8td/4gekhiC vjRA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1783047734; x=1783652534; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to:content-type; bh=676W53NrP+v1JtRit87SUFwRygfrhSqyRXaV/N96IbY=; b=SR1k3MQR2omtqdk/gdJB1KzqssXILD/ILVQhR4T9B9wDziwuwEUsF4F8Z1Y8FaOwsI 3bfJ3xhsBYd1F00XyZ5wCcHNMC2a29ixk8+MFOsCgcUD4Bv8dTRrENNY1j67obS0qh+/ Yy6X1yb2i1xj6TLrevI8ArQJIp4bCym5s+Yap8Mk9bLDseW2t0fba/UT4/HBG3af+PaR Mm/v4MppT5osBYIaVWmX83IAX+yXx0pTejNrxylkTsW3hvojgyF8ysp9/6SStAPqPZ+U UO7ay/guSJVEFXtM2SLhsZV1GGAOne6KK/8bQDNxH6SivagofUor1M0TjP/+U2K04jk/ aVVw== X-Forwarded-Encrypted: i=1; AHgh+RoXJIaYHMlS8JrcL67HKvDETtidOYM601mg7IcH6RZMqJ8SWvaLndFkKRjzltgUsI+8X9dPvSM4zrtsvno=@vger.kernel.org X-Gm-Message-State: AOJu0YyUm5rLbEJoFwYrMC1fRscMf0YmYHBlwS9OF0NkcPbWiZcGYszV Z6WsA/cJ75c91FMwfZPY1kk8wlt/J60FMWmQSiWQ07byASoEht04JXJJ6YSN58NbsA== X-Gm-Gg: AfdE7cnweY2rbJl6lrcyds7Mm5Wl7+pRPhdKsklC4P3Mb7RSt4dxUm3C3xNCTGDo8pb PBZHkr/XBXJqe51OkamrQNDH0Y0pGtZM8NRjfIBpzesb0hfUfj2d2NCgoFvGFOQ9DEb+Ksy2h7c bpnouI02SsGYUaR8QpWcYyqM/6ArSxZNbcCsaH43i/4t/Izgx909N/LCi7wMg9uzu3HTQCeJJZl 01r0Z+wr5oIwF9A9YU34GYkDDwXvU9ub1YIl8KFzgSKCjPyAAsW59Q1O0P6gZgnAXLNGqNGefFj NO1yMYX2PntGFenIxSOuIdbJmI6vKfI/ZnOrKN2oIa/scf0z3SCqbTr53AXYQu3jokyCg3dXlTw NNtlDfQTFieLLr5ndnXswv8Wqdwf2ngcDjjf7C2VAgszRYPT5vXY9C/LKCKnrGMFf1toosk3JVd T3VeUitq+9vMYFb2PdSDKCRL6qeKJ7dANUg2jEZzg6rTjuK+F5c/tOUK3r X-Received: by 2002:a5d:5588:0:b0:475:f100:35f5 with SMTP id ffacd0b85a97d-477b5c415a8mr9292156f8f.50.1783047733914; Thu, 02 Jul 2026 20:02:13 -0700 (PDT) Received: from axion.fireburn.co.uk ([137.220.119.72]) by smtp.gmail.com with ESMTPSA id ffacd0b85a97d-477db3dbacbsm14466500f8f.4.2026.07.02.20.02.11 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 02 Jul 2026 20:02:13 -0700 (PDT) From: Mike Lothian To: rust-for-linux@vger.kernel.org Cc: dri-devel@lists.freedesktop.org, David Airlie , Simona Vetter , Danilo Krummrich , Lyude Paul , Miguel Ojeda , Boqun Feng , Gary Guo , =?UTF-8?q?Bj=C3=B6rn=20Roy=20Baron?= , Benno Lossin , Andreas Hindborg , Alice Ryhl , Trevor Gross , linux-kernel@vger.kernel.org, Mike Lothian Subject: [RFC PATCH v2 17/18] rust: drm: kms: add connector detect() and mode_valid() hooks Date: Fri, 3 Jul 2026 04:01:04 +0100 Message-ID: <20260703030123.2814-18-mike@fireburn.co.uk> X-Mailer: git-send-email 2.55.0 In-Reply-To: <20260703030123.2814-1-mike@fireburn.co.uk> References: <20260617150232.2210-1-mike@fireburn.co.uk> <20260703030123.2814-1-mike@fireburn.co.uk> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: 8bit The safe connector layer only exposed get_modes(), so a driver couldn't report hotplug state or prune modes it can't drive. Add two optional DriverConnector hooks, gated the same way as the plane/CRTC optionals (registered in the vtable only when the driver overrides them): - detect(&Connector, force) -> Status, wired to drm_connector_funcs.detect, returning a new Status enum (Connected/Disconnected/Unknown mapping to enum drm_connector_status). - mode_valid(&Connector, &DisplayMode) -> ModeStatus, wired to drm_connector_helper_funcs.mode_valid, returning a new ModeStatus enum (Ok/Bad/ClockHigh/ClockLow, a common subset of enum drm_mode_status). Both leave DRM's default behaviour (always connected, all modes valid) unchanged for connectors that don't implement them. Signed-off-by: Mike Lothian Assisted-by: Claude:claude-opus-4-8 [Claude-Code] --- rust/kernel/drm/kms/connector.rs | 98 +++++++++++++++++++++++++++++++- 1 file changed, 95 insertions(+), 3 deletions(-) diff --git a/rust/kernel/drm/kms/connector.rs b/rust/kernel/drm/kms/connector.rs index 591153de7ab4..a66243db8868 100644 --- a/rust/kernel/drm/kms/connector.rs +++ b/rust/kernel/drm/kms/connector.rs @@ -5,7 +5,8 @@ //! C header: [`include/drm/drm_connector.h`](srctree/include/drm/drm_connector.h) use super::{ - atomic::*, encoder::*, KmsDriver, ModeConfigGuard, ModeObject, ModeObjectVtable, Sealed + atomic::*, encoder::*, modes::DisplayMode, KmsDriver, ModeConfigGuard, ModeObject, + ModeObjectVtable, Sealed, }; use crate::{ alloc::KBox, @@ -72,6 +73,43 @@ pub enum Type { USB as Usb } +/// The connection status of a [`Connector`], as returned by [`DriverConnector::detect`]. +/// +/// This is identical to [`enum drm_connector_status`]. +/// +/// [`enum drm_connector_status`]: srctree/include/drm/drm_connector.h +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +#[repr(u32)] +pub enum Status { + /// The connector is connected to a display and a mode list can be retrieved. + Connected = bindings::drm_connector_status_connector_status_connected, + /// The connector has no display attached. + Disconnected = bindings::drm_connector_status_connector_status_disconnected, + /// The connection state could not be determined (treated as connected for probing). + Unknown = bindings::drm_connector_status_connector_status_unknown, +} + +/// The result of validating a display mode against a [`Connector`], as returned by +/// [`DriverConnector::mode_valid`]. +/// +/// This mirrors a small, commonly-used subset of [`enum drm_mode_status`]; use [`ModeStatus::Bad`] +/// for a generic rejection, or the clock-specific variants when a mode is out of the driver's +/// pixel-clock range. +/// +/// [`enum drm_mode_status`]: srctree/include/drm/drm_modes.h +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +#[repr(i32)] +pub enum ModeStatus { + /// The mode is usable. + Ok = bindings::drm_mode_status_MODE_OK, + /// The mode is rejected for an unspecified reason. + Bad = bindings::drm_mode_status_MODE_BAD, + /// The mode's pixel clock is above what the driver can drive. + ClockHigh = bindings::drm_mode_status_MODE_CLOCK_HIGH, + /// The mode's pixel clock is below what the driver can drive. + ClockLow = bindings::drm_mode_status_MODE_CLOCK_LOW, +} + /// The main trait for implementing the [`struct drm_connector`] API for [`Connector`]. /// /// Any KMS driver should have at least one implementation of this type, which allows them to create @@ -105,14 +143,22 @@ pub trait DriverConnector: Send + Sync + Sized { atomic_destroy_state: Some(atomic_destroy_state_callback::), destroy: Some(connector_destroy_callback::), force: None, - detect: None, + detect: if Self::HAS_DETECT { + Some(detect_callback::) + } else { + None + }, fill_modes: Some(bindings::drm_helper_probe_single_connector_modes), debugfs_init: None, oob_hotplug_event: None, atomic_duplicate_state: Some(atomic_duplicate_state_callback::), }, helper_funcs: bindings::drm_connector_helper_funcs { - mode_valid: None, + mode_valid: if Self::HAS_MODE_VALID { + Some(mode_valid_callback::) + } else { + None + }, atomic_check: None, get_modes: Some(get_modes_callback::), detect_ctx: None, @@ -151,6 +197,28 @@ fn get_modes<'a>( connector: ConnectorGuard<'a, Self>, guard: &ModeConfigGuard<'a, Self::Driver>, ) -> i32; + + /// The optional [`drm_connector_funcs.detect`] hook for this connector. + /// + /// Drivers may implement this to report whether a display is currently attached. If not + /// implemented, the connector is always considered connected (DRM's default with no `detect` + /// hook). `force` is set when userspace explicitly requested a forced probe. + /// + /// [`drm_connector_funcs.detect`]: srctree/include/drm/drm_connector.h + fn detect(_connector: &Connector, _force: bool) -> Status { + build_error::build_error("This should not be reachable") + } + + /// The optional [`drm_connector_helper_funcs.mode_valid`] hook for this connector. + /// + /// Drivers may implement this to reject modes they cannot drive (for example, a mode whose + /// pixel clock exceeds the hardware's budget). Returning anything other than [`ModeStatus::Ok`] + /// prunes the mode from the probed list. If not implemented, every mode is accepted. + /// + /// [`drm_connector_helper_funcs.mode_valid`]: srctree/include/drm/drm_modeset_helper_vtables.h + fn mode_valid(_connector: &Connector, _mode: &DisplayMode) -> ModeStatus { + build_error::build_error("This should not be reachable") + } } /// The generated C vtable for a [`DriverConnector`]. @@ -464,6 +532,30 @@ impl RawConnector for T {} T::get_modes(connector.guard(&guard), &guard) } +unsafe extern "C" fn detect_callback( + connector: *mut bindings::drm_connector, + force: bool, +) -> bindings::drm_connector_status { + // SAFETY: This is safe via `DriverConnector`s type invariants. + let connector = unsafe { Connector::::from_raw(connector) }; + + T::detect(connector, force) as bindings::drm_connector_status +} + +unsafe extern "C" fn mode_valid_callback( + connector: *mut bindings::drm_connector, + mode: *const bindings::drm_display_mode, +) -> bindings::drm_mode_status { + // SAFETY: This is safe via `DriverConnector`s type invariants. + let connector = unsafe { Connector::::from_raw(connector) }; + + // SAFETY: DRM guarantees `mode` points to a valid `drm_display_mode` for the duration of this + // callback, and only passes us shared access to it. + let mode = unsafe { DisplayMode::as_ref(mode) }; + + T::mode_valid(connector, mode) as bindings::drm_mode_status +} + /// A [`struct drm_connector`] without a known [`DriverConnector`] implementation. /// /// This is mainly for situations where our bindings can't infer the [`DriverConnector`] -- 2.55.0