From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.133.124]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 461C63D5C26 for ; Tue, 7 Jul 2026 22:21:49 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=170.10.133.124 ARC-Seal:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1783462920; cv=none; b=H5P+3fwcgriDWZZyVe8ppZhEYs3WysJKCOnojODHi9IXRuIM2tQN+TQOAyxlzdw/vREdS/sXViPs5pJ1beW3wUs2SSMFQOAqsdAbESoEcoCnKazirELmvAkWjgk+HL7tAxXjCdjWZolwGZ2Rv56mygTniQfvwfZSorgEFyDo2CM= ARC-Message-Signature:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1783462920; c=relaxed/simple; bh=QnCTfUpwcvxhQWLD9xGx8fS05UpIWbZMJfavBT8iftU=; h=Message-ID:Subject:From:To:Cc:Date:In-Reply-To:References: MIME-Version:Content-Type; b=it694tvQgMO1oahW0Q+eD69KmX/iGekcIFcCt8ALM+FFokEh4/NbdKI7peg+1gQheJcS3n+5Sf6rEDh4K3uughNJu+CrghXh4v94wl+qu6g7p7bC8ev5MYruHOpD5WNmn7YmBdn9tXR6Y6nDQxyqGh8/KonbM4/yfzbiCvOIyiM= ARC-Authentication-Results:i=1; smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=redhat.com; spf=pass smtp.mailfrom=redhat.com; dkim=pass (1024-bit key) header.d=redhat.com header.i=@redhat.com header.b=XKKDs9rM; arc=none smtp.client-ip=170.10.133.124 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=redhat.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=redhat.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=redhat.com header.i=@redhat.com header.b="XKKDs9rM" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1783462908; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=flgZZUgXhkTFGySNpaQuFvJ1hawAqHuGykw7LrBB0QI=; b=XKKDs9rMx4jPKFCOtu48HcxijfToABLWzmWcU+FUljCjzwyc8GvMtH6x+XtCrAGSoRK5FL XSFVnNxUHjzZ4gaTyttlPh2XDvSs0pUYT3sqdP7sTAeIrvP6P5vjgzIhQpXL6tSM7TXjeq AttYAjpLzuQEPBEkRQzeDyI75164aD0= Received: from mail-qv1-f70.google.com (mail-qv1-f70.google.com [209.85.219.70]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-516-rALvkdEdMnqvCcHYK8nQ5w-1; Tue, 07 Jul 2026 18:21:46 -0400 X-MC-Unique: rALvkdEdMnqvCcHYK8nQ5w-1 X-Mimecast-MFC-AGG-ID: rALvkdEdMnqvCcHYK8nQ5w_1783462906 Received: by mail-qv1-f70.google.com with SMTP id 6a1803df08f44-8eeba1d9e47so1278156d6.2 for ; Tue, 07 Jul 2026 15:21:46 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1783462906; x=1784067706; h=mime-version:user-agent:content-transfer-encoding:references :in-reply-to:date:cc:to:from:subject:message-id:x-gm-gg :x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=flgZZUgXhkTFGySNpaQuFvJ1hawAqHuGykw7LrBB0QI=; b=kYn9spyBrQuCt2t2CbOq0NImYMHrVSp+i92USBCCbEt8379X4S+cvXX8lyDfQauXdQ WufSasGDuv4kL4Un5rHS5yeWkBNLJVbIeebRxAVFLGtGQu4BE2F6pY9bkKru5f7hCH+4 fiT4aHV+kAqb3Iw/PcFG2q7hbmXMD6B3Qqznp1eGRT6Xc3zfkZLD8Gmn3N9UJKfvGBFR gaRNDpRQktCWWJ0YnforE/vlxVg0nTyAwP5a2N9tLLfpuD+bWPymKcf3/Vc5mdY4rM6a goP9jXud5m+K7rdT9dHfB93Ypir5xZvrf+PeLodMUrC4B7a48XoUI6XRF+ruMHAUxQG0 wMRw== X-Forwarded-Encrypted: i=1; AHgh+RruUi/IQxH1rXtlyeil/4CCb7rdjQxYaQ2AaPgNNlGRGGSwcVhgMFSXMrj6OJ2zY587Vi0lgt4rmAHyV1Ehjw==@vger.kernel.org X-Gm-Message-State: AOJu0YzrT1gt3c9MNErs0Cmp8iINTADXGFksUmD+uIvyWYV9ak4qTqS1 st04GkOh1pXIUjC4fe+88w20oIKzA54qZ0YTmy1aQvNlbAHNRz18cjFg5etNFGMs+vxxb3offso 6eQcETdu0AAG23i4uZ6WJutSF4fVpmQldoLtjRd0tK5olzTqDJPOQy0qacQ+NOJ1JbWEQ X-Gm-Gg: AfdE7ck+COLOFfZYgKjSJaDk8FcvyANGhRulfGx50QVRbuTaYdLUocjKbC3Lc+t0Byx 3w+Mf4rE3BZz+VsjlEqdmcp6WkT4iqin6M3/VBRJC5zBVsiKHK2cPPTrOWX18RhbM3Z72GGa4i2 c8WS5lENwlx7k52RL5H94q3IEJdkJaNSnEAIU8Mk1WkzMZGYGeWOCWKCEBrtvI9BJ+3uEf5U7KG O+d3YryYY2dOI2vKSYwlzNGEbbu3O7PPiWRFQJOSOTH0iSS6tRpHTBAL+HyQeys804BCSHkFo6z kDLliCEFXXdLsKe2tl3SXSOy4yTQ3xTjRs+58Rg6tJ1vyQkB/ijnvEM+4yXBeRP+K3qdG7P/8AJ 2U6lgqYc= X-Received: by 2002:a05:622a:1e12:b0:51b:efe9:27c0 with SMTP id d75a77b69052e-51c8b3fa4e3mr599131cf.71.1783462901970; Tue, 07 Jul 2026 15:21:41 -0700 (PDT) X-Received: by 2002:a05:622a:1e12:b0:51b:efe9:27c0 with SMTP id d75a77b69052e-51c8b3fa4e3mr597401cf.71.1783462898929; Tue, 07 Jul 2026 15:21:38 -0700 (PDT) Received: from [192.168.8.4] ([100.0.180.93]) by smtp.gmail.com with ESMTPSA id 6a1803df08f44-8f4718141e6sm170582566d6.24.2026.07.07.15.21.37 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 07 Jul 2026 15:21:38 -0700 (PDT) Message-ID: Subject: Re: [RFC PATCH v2 01/18] rust: drm: kms: forward-port the safe mode-object layer onto the typestate device From: lyude@redhat.com To: Mike Lothian , rust-for-linux@vger.kernel.org Cc: dri-devel@lists.freedesktop.org, David Airlie , Simona Vetter , Danilo Krummrich , Miguel Ojeda , Boqun Feng , Gary Guo , =?ISO-8859-1?Q?Bj=F6rn?= Roy Baron , Benno Lossin , Andreas Hindborg , Alice Ryhl , Trevor Gross , linux-kernel@vger.kernel.org Date: Tue, 07 Jul 2026 18:21:37 -0400 In-Reply-To: <20260703030123.2814-2-mike@fireburn.co.uk> References: <20260617150232.2210-1-mike@fireburn.co.uk> <20260703030123.2814-1-mike@fireburn.co.uk> <20260703030123.2814-2-mike@fireburn.co.uk> User-Agent: Evolution 3.58.3 (3.58.3-1.fc43) Precedence: bulk X-Mailing-List: rust-for-linux@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 X-Mimecast-Spam-Score: 0 X-Mimecast-MFC-PROC-ID: y3oRYJWp-UAg6Nph4JO4M5RoD0pF9VBgL3DZHwsWKUA_1783462906 X-Mimecast-Originator: redhat.com Content-Type: text/plain; charset="UTF-8" Content-Transfer-Encoding: quoted-printable NAK on the whole series. The first patch starts with taking all of my work, changing the authorship, adding a claude tag on the thing I spent 2 years of my life trying to get upstream, on top of the dozens of other issues with this patch series: * You, again, changed/removed my authorship on work that I wrote. In this case, you copied my entire git branch, re-authored it under yourself and added a claude assisted-by tag, and have tried submitting it to the kernel mailing list. That is absolutely not OK by any stretch, even for an RFC. The next time I see this, I am giving an immediate NAK and just pointing back at this email. * Rebasing means actually going through the entire patch series and modifying the patches so they apply. It doesn't mean changing the authorship on patches, squashing them together, and then adding a rebase patch at the end. If you need to have a hacked-together branch to make things work, that can't be part of the actual series you submitting. * Multiple import items on a single line. * I get that this is a RFC patch, but there are dozens of patches here that seem to do things that are entirely out of scope of KMS: - Changing the build system to workaround issues without any description as to the actual issues that were hit. It took me multiple tries to understand what you were even trying to fix, and regardless: it shouldn't be part of this patch series. - Blobs of text that appear to be copied straight from AI output, especially in the git commit message. Maintainers aren't here to clean this up for you, people read this and use this to understand what is going on. - More reimplementations of things that exist elsewhere, dozens of comments across patches that clearly didn't get looked at by a person, etc. etc. I've left comments, but there are so many to leave here and it is very obvious that a lot of this code was generated and not actually looked over by a person. I get it, RFCs can be messy, but not like this. It's much different when you ask a machine to generate a large amount of code, don't actually look over it, and then ask for others to go through and vet it. I'm going to ask: if you're not willing to write the code here yourself (as in, without claude), please simply don't submit another version. I've received AI generated patches, they're usually fine, but that's because they're generally used as large search replaces with limited scope such that it's obvious the person who made the patch has understood it. I don't see evidence that the vast majority of this was even read before it was submitted. It's not a maintainer's job to take slop and turn it into something legible. That's not the purpose of an RFC either. On Fri, 2026-07-03 at 04:00 +0100, Mike Lothian wrote: > Port Lyude Paul's rvkms-slim safe mode-object layer > (CRTC/plane/connector/ > encoder/vblank/atomic state) onto her newer rust-stuck typestate > Device > design (ParentDevice/DeviceContext/RegistrationData), which the > preceding > cherry-picked commits bring in. Source: lyude/rvkms-slim + > lyude/rust/rust-stuck. >=20 > Same module split as rvkms-slim (kms/{atomic,connector,crtc,encoder, > framebuffer,modes,plane,vblank}.rs), same trait shape, adjusted to > the > typestate device's generic Ctx/State parameters. This is a mechanical > type-level port, not new design -- diff against lyude/rvkms-slim to > see > exactly what moved. >=20 > Does not build yet. The port's mode-object traits are mutually > recursive in a way the current trait solver can't evaluate; the next > few commits get it to a clean build. >=20 > Signed-off-by: Mike Lothian > Assisted-by: Claude:claude-sonnet-5 [Claude-Code] > --- > =C2=A0rust/kernel/drm/kms.rs=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2= =A0=C2=A0=C2=A0=C2=A0=C2=A0 |=C2=A0 395 +++++++++- > =C2=A0rust/kernel/drm/kms/atomic.rs=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 |=C2=A0= 864 ++++++++++++++++++++++ > =C2=A0rust/kernel/drm/kms/connector.rs=C2=A0=C2=A0 |=C2=A0 997 ++++++++++= +++++++++++++++ > =C2=A0rust/kernel/drm/kms/crtc.rs=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2= =A0 | 1110 > ++++++++++++++++++++++++++++ > =C2=A0rust/kernel/drm/kms/encoder.rs=C2=A0=C2=A0=C2=A0=C2=A0 |=C2=A0 409 = ++++++++++ > =C2=A0rust/kernel/drm/kms/framebuffer.rs |=C2=A0=C2=A0 70 ++ > =C2=A0rust/kernel/drm/kms/modes.rs=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 |= =C2=A0=C2=A0 76 ++ > =C2=A0rust/kernel/drm/kms/plane.rs=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 | = 1095 > +++++++++++++++++++++++++++ > =C2=A0rust/kernel/drm/kms/vblank.rs=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 |=C2=A0= 461 ++++++++++++ > =C2=A09 files changed, 5469 insertions(+), 8 deletions(-) > =C2=A0create mode 100644 rust/kernel/drm/kms/atomic.rs > =C2=A0create mode 100644 rust/kernel/drm/kms/connector.rs > =C2=A0create mode 100644 rust/kernel/drm/kms/crtc.rs > =C2=A0create mode 100644 rust/kernel/drm/kms/encoder.rs > =C2=A0create mode 100644 rust/kernel/drm/kms/framebuffer.rs > =C2=A0create mode 100644 rust/kernel/drm/kms/modes.rs > =C2=A0create mode 100644 rust/kernel/drm/kms/plane.rs > =C2=A0create mode 100644 rust/kernel/drm/kms/vblank.rs >=20 > diff --git a/rust/kernel/drm/kms.rs b/rust/kernel/drm/kms.rs > index 084ed0aebd0e..11b09d2175db 100644 > --- a/rust/kernel/drm/kms.rs > +++ b/rust/kernel/drm/kms.rs > @@ -3,17 +3,37 @@ > =C2=A0//! KMS driver abstractions for rust. > =C2=A0 > =C2=A0use crate::{ > +=C2=A0=C2=A0=C2=A0 container_of, > =C2=A0=C2=A0=C2=A0=C2=A0 drm::{ > -=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 device::Device, > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 device::{Device, DeviceContex= t}, > =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 driver::Driver, > =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 private::Sealed, > =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 Uninit, // > =C2=A0=C2=A0=C2=A0=C2=A0 }, > =C2=A0=C2=A0=C2=A0=C2=A0 error::to_result, > -=C2=A0=C2=A0=C2=A0 prelude::*, // > +=C2=A0=C2=A0=C2=A0 prelude::*, > +=C2=A0=C2=A0=C2=A0 sync::{Mutex, MutexGuard}, > +=C2=A0=C2=A0=C2=A0 types::*, > =C2=A0}; > =C2=A0use bindings; > -use core::{marker::PhantomData, ops::Deref}; > +use core::{ > +=C2=A0=C2=A0=C2=A0 cell::Cell, > +=C2=A0=C2=A0=C2=A0 marker::PhantomData, > +=C2=A0=C2=A0=C2=A0 ops::Deref, > +=C2=A0=C2=A0=C2=A0 ptr::{self, addr_of_mut, NonNull}, > +}; > + > +// Forward-ported mode-object layer (see > Documentation/gpu/rust/vino-kms-forward-port.md). > +// Modules are declared here as each is grafted onto the rust-stuck > typestate design. > +// `modes` is self-contained and ported as-is; the rest follow per > the plan's order. > +pub mod atomic; > +pub mod connector; > +pub mod crtc; > +pub mod encoder; > +pub mod framebuffer; > +pub mod modes; > +pub mod plane; > +pub mod vblank; > =C2=A0 > =C2=A0/// The C vtable for a [`Device`]. > =C2=A0/// > @@ -77,13 +97,21 @@ impl KmsContext for Probing {} > =C2=A0/// This type is guaranteed to represent a [`Device`] that has not > been registered with userspace, > =C2=A0/// and is in the process of setting up KMS support. It carries a > [`KmsDeviceContext`] to indicate > =C2=A0/// which stage of the KMS setup process this [`Device`] is > currently in. > -pub struct NewKmsDevice<'a, T: Driver, C: KmsContext>(&'a Device Uninit>, PhantomData); > +pub struct NewKmsDevice<'a, T: Driver, C: KmsContext> { > +=C2=A0=C2=A0=C2=A0 drm: &'a Device, > +=C2=A0=C2=A0=C2=A0 /// Tracks whether any CRTC created during probe requ= ested > vblank support, so that > +=C2=A0=C2=A0=C2=A0 /// [`drm_vblank_init()`] can be called afterwards. S= et by > [`crtc::Crtc::new()`]. > +=C2=A0=C2=A0=C2=A0 /// > +=C2=A0=C2=A0=C2=A0 /// [`drm_vblank_init()`]: srctree/include/drm/drm_vb= lank.h > +=C2=A0=C2=A0=C2=A0 pub(crate) has_vblanks: Cell, > +=C2=A0=C2=A0=C2=A0 _ctx: PhantomData, > +} > =C2=A0 > =C2=A0impl<'a, T: Driver, C: KmsContext> Deref for NewKmsDevice<'a, T, C> > { > =C2=A0=C2=A0=C2=A0=C2=A0 type Target =3D Device; > =C2=A0 > =C2=A0=C2=A0=C2=A0=C2=A0 fn deref(&self) -> &Self::Target { > -=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 self.0 > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 self.drm > =C2=A0=C2=A0=C2=A0=C2=A0 } > =C2=A0} > =C2=A0 > @@ -95,15 +123,56 @@ fn deref(&self) -> &Self::Target { > =C2=A0/// [`PhantomData`]: PhantomData > =C2=A0#[vtable] > =C2=A0pub trait KmsDriver: Driver { > +=C2=A0=C2=A0=C2=A0 /// The driver's [`DriverConnector`](connector::Drive= rConnector) > implementation. > +=C2=A0=C2=A0=C2=A0 /// > +=C2=A0=C2=A0=C2=A0 /// TODO: This will be unneeded once we support multi= ple > `DriverConnector` implementations. > +=C2=A0=C2=A0=C2=A0 type Connector: connector::DriverConnector; > + > +=C2=A0=C2=A0=C2=A0 /// The driver's [`DriverPlane`](plane::DriverPlane) > implementation. > +=C2=A0=C2=A0=C2=A0 /// > +=C2=A0=C2=A0=C2=A0 /// TODO: This will be unneeded once we support multi= ple > `DriverPlane` implementations. > +=C2=A0=C2=A0=C2=A0 type Plane: plane::DriverPlane; > + > +=C2=A0=C2=A0=C2=A0 /// The driver's [`DriverCrtc`](crtc::DriverCrtc) > implementation. > +=C2=A0=C2=A0=C2=A0 /// > +=C2=A0=C2=A0=C2=A0 /// TODO: This will be unneeded once we support multi= ple > `DriverCrtc` implementations. > +=C2=A0=C2=A0=C2=A0 type Crtc: crtc::DriverCrtc; > + > +=C2=A0=C2=A0=C2=A0 /// The driver's [`DriverEncoder`](encoder::DriverEnc= oder) > implementation. > +=C2=A0=C2=A0=C2=A0 /// > +=C2=A0=C2=A0=C2=A0 /// TODO: This will be unneeded once we support multi= ple > `DriverEncoder` implementations. > +=C2=A0=C2=A0=C2=A0 type Encoder: encoder::DriverEncoder; > + > =C2=A0=C2=A0=C2=A0=C2=A0 /// Return a [`ModeConfigInfo`] structure for th= is > [`device::Device`]. > =C2=A0=C2=A0=C2=A0=C2=A0 fn mode_config_info(dev: &Device) = -> > Result > =C2=A0=C2=A0=C2=A0=C2=A0 where > =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 Self: Sized; > =C2=A0 > =C2=A0=C2=A0=C2=A0=C2=A0 /// Create mode objects like [`crtc::Crtc`], [`p= lane::Plane`], > etc. for this device > -=C2=A0=C2=A0=C2=A0 fn probe(drm: NewKmsDevice<'_, Self, Probing>) -> Res= ult > +=C2=A0=C2=A0=C2=A0 fn probe(drm: &NewKmsDevice<'_, Self, Probing>) -> Re= sult > =C2=A0=C2=A0=C2=A0=C2=A0 where > =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 Self: Sized; > + > +=C2=A0=C2=A0=C2=A0 /// The optional [`atomic_commit_tail`] callback for = this > [`Device`]. > +=C2=A0=C2=A0=C2=A0 /// > +=C2=A0=C2=A0=C2=A0 /// It must return a > [`CommittedAtomicState`](atomic::CommittedAtomicState) to prove that > it has > +=C2=A0=C2=A0=C2=A0 /// signaled completion of the hw commit phase. Drive= rs may use > this function to customize the > +=C2=A0=C2=A0=C2=A0 /// order in which commits are performed during the a= tomic > commit phase. > +=C2=A0=C2=A0=C2=A0 /// > +=C2=A0=C2=A0=C2=A0 /// If not provided, DRM will use its own default ato= mic commit > tail helper > +=C2=A0=C2=A0=C2=A0 /// `drm_atomic_helper_commit_tail`. > +=C2=A0=C2=A0=C2=A0 /// > +=C2=A0=C2=A0=C2=A0 /// [`atomic_commit_tail`]: > srctree/include/drm/drm_modeset_helper_vtables.h > +=C2=A0=C2=A0=C2=A0 fn atomic_commit_tail<'a>( > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 _state: atomic::AtomicCommitT= ail<'a, Self>, > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 _modeset_token: atomic::Modes= etsReadyToken<'_>, > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 _plane_update_token: atomic::= PlaneUpdatesReadyToken<'_>, > +=C2=A0=C2=A0=C2=A0 ) -> atomic::CommittedAtomicState<'a, Self> > +=C2=A0=C2=A0=C2=A0 where > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 Self: Sized, > +=C2=A0=C2=A0=C2=A0 { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 build_error::build_error("Thi= s function should not be > reachable") > +=C2=A0=C2=A0=C2=A0 } > =C2=A0} > =C2=A0 > =C2=A0impl private::KmsImpl for T { > @@ -123,7 +192,11 @@ impl private::KmsImpl for T { > =C2=A0 > =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 kms_helper_vtable: bindi= ngs::drm_mode_config_helper_funcs { > =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 = atomic_commit_setup: None, > -=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 atomi= c_commit_tail: None, > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 atomi= c_commit_tail: if T::HAS_ATOMIC_COMMIT_TAIL { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0= =C2=A0=C2=A0=C2=A0 Some(atomic::commit_tail_callback::) > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 } els= e { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0= =C2=A0=C2=A0=C2=A0 None > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 }, > =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 }, > =C2=A0=C2=A0=C2=A0=C2=A0 }); > =C2=A0 > @@ -156,7 +229,19 @@ unsafe fn probe_kms(drm: &Device Uninit>) -> Result =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // SAFETY: We just setup= all of the info required to call > this function above. > =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 to_result(unsafe { > bindings::drmm_mode_config_init(drm.as_raw()) })?; > =C2=A0 > -=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 T::probe(NewKmsDevice(&drm, P= hantomData))?; > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 let new_kms =3D NewKmsDevice = { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 drm: = &drm, > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 has_v= blanks: Cell::new(false), > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 _ctx:= PhantomData, > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 }; > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 T::probe(&new_kms)?; > + > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 if new_kms.has_vblanks.get() = { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // SA= FETY: `has_vblanks` is only set when CRTCs with > vblank support were created during > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // pr= obe (the only place static mode objects are > created), so the vblank state is ready > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // to= be initialized. > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 to_re= sult(unsafe { > bindings::drm_vblank_init(drm.as_raw(), drm.num_crtcs()) })?; > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 } > =C2=A0 > =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // TODO: In the future, = we should add a hook here for > initializing each state via hardware > =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // state readback. > @@ -192,3 +277,297 @@ pub struct ModeConfigInfo { > =C2=A0=C2=A0=C2=A0=C2=A0 /// An optional default fourcc format code to be= preferred for > clients. > =C2=A0=C2=A0=C2=A0=C2=A0 pub preferred_fourcc: Option, > =C2=A0} > + > +// ----------------------------------------------------------------- > ---------- > +// Mode-object framework (forward-ported from lyude/rvkms-slim's > kms.rs). > +// > +// Adapted to rust-stuck's typestate device: `Device` defaults > `C` to > +// `Registered`, so most `Device` references port unchanged; only > creation > +// paths (during probe) use the `Uninit` context, reached via > `NewKmsDevice`. > +// ----------------------------------------------------------------- > ---------- > + > +/// Implement the repetitive from_opaque/try_from_opaque methods for > all mode object and state > +/// types. > +/// > +/// Because there are so many different ways of accessing mode > objects, their states, etc. we need a > +/// macro that we can use for consistently implementing > try_from_opaque()/from_opaque() functions to > +/// convert from Opaque mode objects to fully typed mode objects. > This macro handles that, and can > +/// generate said functions for any kind of type which the original > mode object driver trait can be > +/// derived from. > +macro_rules! impl_from_opaque_mode_obj { > +=C2=A0=C2=A0=C2=A0 ( > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 fn < > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 $( $l= ifetime:lifetime, )? > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 $( $d= ecl_bound_id:ident ),* > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 > ($opaque:ty) -> $inner_ret_= ty:ty > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 $( > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 where > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0= =C2=A0=C2=A0=C2=A0 $( $extra_bound_id:ident : $extra_trait:ident<$( > $extra_assoc:ident =3D $extra_param_match:ident ),+> ),+ > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 )? ; > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 use > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 $obj_= trait_param:ident as $obj_trait:ident, > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 $drv_= trait_param:ident as > KmsDriver<$drv_assoc_trait:ident =3D ...> > +=C2=A0=C2=A0=C2=A0 ) =3D> { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 #[doc =3D "Try to convert `op= aque` into a fully qualified > `Self`."] > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 #[doc =3D ""] > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 #[doc =3D concat!("This will = try to convert `opaque` into > `Self` if it shares the same [`", > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0= =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 stringif= y!($obj_trait), "`] implementation > as `Self`.")] > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 pub fn try_from_opaque<$( $li= fetime, )? $( $decl_bound_id > ),* >( > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 opaqu= e: $opaque > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 ) -> Result<$inner_ret_ty, $o= paque> > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 where > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 $drv_= trait_param: KmsDriver<$drv_assoc_trait =3D > $obj_trait_param>, > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 $obj_= trait_param: $obj_trait > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 $( , = $( $extra_bound_id: $extra_trait<$( $extra_assoc =3D > $extra_param_match ),+> ),+ )? > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // FI= XME: What we really want to be doing here is > comparing vtable pointers, but this is > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // cu= rrently blocked on getting unique vtable macros to > ensure that each vtable has a > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // co= nsistent memory pointer. > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // Fo= r the time being, we simply restrict things to one > object type per driver and do a > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // tr= ansmutation based on that assumption holding true. > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // SA= FETY: We currently only allow one object type per- > driver, so this transmute is > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // al= ways safe. > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 Ok(un= safe { core::mem::transmute(opaque) }) > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 } > + > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 #[doc =3D "Convert `opaque` i= nto a fully qualified `Self`."] > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 #[doc =3D ""] > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 #[doc =3D concat!("This is an= infallible version of > [`Self::try_from_opaque`]. This ", > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0= =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 "functio= n is mainly useful for drivers where > only a single [`", > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0= =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 stringif= y!($obj_trait), "`] implementation > exists.")] > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 #[doc =3D ""] > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 #[doc =3D "# Panics"] > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 #[doc =3D ""] > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 #[doc =3D concat!("This funct= ion will panic if `opaque` > belongs to a different [`", > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0= =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 stringif= y!($obj_trait), "`] > implementation.")] > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 pub fn from_opaque<$( $lifeti= me, )? $( $decl_bound_id ),* >( > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 opaqu= e: $opaque > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 ) -> $inner_ret_ty > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 where > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 $drv_= trait_param: KmsDriver<$drv_assoc_trait =3D > $obj_trait_param>, > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 $obj_= trait_param: $obj_trait > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 $( , = $( $extra_bound_id: $extra_trait<$( $extra_assoc =3D > $extra_param_match ),+> ),+ )? > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 Self:= :try_from_opaque(opaque) > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0= =C2=A0=C2=A0=C2=A0 .map_or(None, |o| Some(o)) > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0= =C2=A0=C2=A0=C2=A0 .expect(concat!("Passed ", stringify!($opaque), " > does not share this ", > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0= =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2= =A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 stringify!($obj_trait), " > implementation.")) > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 } > +=C2=A0=C2=A0=C2=A0 }; > +} > + > +pub(crate) use impl_from_opaque_mode_obj; > + > +impl Device { > +=C2=A0=C2=A0=C2=A0 /// Retrieve a pointer to the mode_config mutex > +=C2=A0=C2=A0=C2=A0 #[inline] > +=C2=A0=C2=A0=C2=A0 pub(crate) fn mode_config_mutex(&self) -> &Mutex<()> = { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // SAFETY: This lock is initi= alized for as long as > `Device` is exposed to users > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 unsafe { > Mutex::from_raw(addr_of_mut!((*self.as_raw()).mode_config.mutex)) } > +=C2=A0=C2=A0=C2=A0 } > + > +=C2=A0=C2=A0=C2=A0 /// Return the number of registered [`Crtc`](crtc::Cr= tc) objects > on this [`Device`]. > +=C2=A0=C2=A0=C2=A0 #[inline] > +=C2=A0=C2=A0=C2=A0 pub fn num_crtcs(&self) -> u32 { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // SAFETY: > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // * This can only be modifie= d during the single-threaded > context before registration, so > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 //=C2=A0=C2=A0 this is safe > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // * num_crtc is always >=3D = 0, so casting to u32 is fine > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 unsafe { (*self.as_raw()).mod= e_config.num_crtc as u32 } > +=C2=A0=C2=A0=C2=A0 } > +} > + > +impl Device { > +=C2=A0=C2=A0=C2=A0 /// Acquire the [`mode_config.mutex`] for this [`Devi= ce`]. > +=C2=A0=C2=A0=C2=A0 #[inline] > +=C2=A0=C2=A0=C2=A0 pub fn mode_config_lock(&self) -> ModeConfigGuard<'_,= T> { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // INVARIANT: We're locking m= ode_config.mutex, fulfilling > our invariant that this lock is > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // held throughout ModeConfig= Guard's lifetime. > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 ModeConfigGuard(self.mode_con= fig_mutex().lock(), > PhantomData) > +=C2=A0=C2=A0=C2=A0 } > +} > + > +/// A modesetting object in DRM. > +/// > +/// This is any type of object where the underlying C object > contains a [`struct drm_mode_object`]. > +/// This type requires [`Send`] + [`Sync`] as all modesetting > objects in DRM are able to be sent > +/// between threads. > +/// > +/// This type is only implemented by the DRM crate itself. > +/// > +/// # Safety > +/// > +/// [`raw_mode_obj()`] must always return a valid pointer to an > initialized > +/// [`struct drm_mode_object`]. > +/// > +/// [`struct drm_mode_object`]: > srctree/include/drm/drm_mode_object.h > +/// [`raw_mode_obj()`]: ModeObject::raw_mode_obj() > +pub unsafe trait ModeObject: Sealed + Send + Sync { > +=C2=A0=C2=A0=C2=A0 /// The parent driver for this [`ModeObject`]. > +=C2=A0=C2=A0=C2=A0 type Driver: KmsDriver; > + > +=C2=A0=C2=A0=C2=A0 /// Return the [`Device`] for this [`ModeObject`]. > +=C2=A0=C2=A0=C2=A0 fn drm_dev(&self) -> &Device; > + > +=C2=A0=C2=A0=C2=A0 /// Return a pointer to the [`struct drm_mode_object`= ] for this > [`ModeObject`]. > +=C2=A0=C2=A0=C2=A0 /// > +=C2=A0=C2=A0=C2=A0 /// [`struct drm_mode_object`]: > (srctree/include/drm/drm_mode_object.h) > +=C2=A0=C2=A0=C2=A0 fn raw_mode_obj(&self) -> *mut bindings::drm_mode_obj= ect; > +} > + > +/// A trait for modesetting objects which don't come with their own > reference-counting. > +/// > +/// Some [`ModeObject`] types in DRM do not have a reference count. > These types are considered > +/// "static" and share the lifetime of their parent [`Device`]. To > retrieve an owned reference to > +/// such types, see [`KmsRef`]. > +/// > +/// # Safety > +/// > +/// This trait must only be implemented for modesetting objects > which do not have a refcount within > +/// their [`struct drm_mode_object`], otherwise [`KmsRef`] can't > guarantee the object will stay > +/// alive. > +/// > +/// [`struct drm_mode_object`]: > (srctree/include/drm/drm_mode_object.h) > +pub unsafe trait StaticModeObject: ModeObject {} > + > +/// An owned reference to a [`StaticModeObject`]. > +/// > +/// Note that since [`StaticModeObject`] types share the lifetime of > their parent [`Device`], the > +/// parent [`Device`] will stay alive as long as this type exists. > Thus, users should be aware that > +/// storing a [`KmsRef`] within a [`ModeObject`] is a circular > reference. > +/// > +/// # Invariants > +/// > +/// `self.0` points to a valid instance of `T` throughout the > lifetime of this type. > +pub struct KmsRef(NonNull); > + > +// SAFETY: Owned references to DRM device are thread-safe. > +unsafe impl Send for KmsRef {} > +// SAFETY: Owned references to DRM device are thread-safe. > +unsafe impl Sync for KmsRef {} > + > +impl From<&T> for KmsRef { > +=C2=A0=C2=A0=C2=A0 fn from(value: &T) -> Self { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // INVARIANT: Because the lif= etime of the StaticModeObject > is the same as the lifetime of > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // its parent device, we can = ensure that `value` remains > alive by incrementing the device's > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // reference count. The devic= e will only disappear once we > drop this reference in `Drop`. > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 value.drm_dev().inc_ref(); > + > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 Self(value.into()) > +=C2=A0=C2=A0=C2=A0 } > +} > + > +impl Drop for KmsRef { > +=C2=A0=C2=A0=C2=A0 fn drop(&mut self) { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // SAFETY: We're reclaiming t= he reference we leaked in > From<&T> > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 drop(unsafe { ARef::from_raw(= self.drm_dev().into()) }) > +=C2=A0=C2=A0=C2=A0 } > +} > + > +impl Deref for KmsRef { > +=C2=A0=C2=A0=C2=A0 type Target =3D T; > + > +=C2=A0=C2=A0=C2=A0 fn deref(&self) -> &Self::Target { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // SAFETY: We're guaranteed o= bject will point to a valid > object as long as we hold dev > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 unsafe { self.0.as_ref() } > +=C2=A0=C2=A0=C2=A0 } > +} > + > +impl Clone for KmsRef { > +=C2=A0=C2=A0=C2=A0 fn clone(&self) -> Self { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // INVARIANT: See `From<&T>`;= we keep the parent device > alive for this new reference. > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 self.drm_dev().inc_ref(); > + > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 Self(self.0) > +=C2=A0=C2=A0=C2=A0 } > +} > + > +macro_rules! impl_aref_for_mode_object { > +=C2=A0=C2=A0=C2=A0 (impl $( < $( $param:ident: $bound:ident ),+ > )? for= $type:ty) > =3D> { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // SAFETY: drm_mode_object_ge= t()/put() ensure the type is > ref-counted according to the > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // safety contract > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 unsafe impl $( < $( $param: $= bound ),+ > )? > kernel::types::AlwaysRefCounted for $type { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 #[inl= ine] > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 fn in= c_ref(&self) { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0= =C2=A0=C2=A0=C2=A0 // SAFETY: We're guaranteed by the safety contract > of `ModeObject` that > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0= =C2=A0=C2=A0=C2=A0 // `raw_mode_obj()` always returns a pointer to an > initialized `drm_mode_object`. > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0= =C2=A0=C2=A0=C2=A0 unsafe { > kernel::bindings::drm_mode_object_get(self.raw_mode_obj()) } > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 } > + > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 #[inl= ine] > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 unsaf= e fn dec_ref(obj: core::ptr::NonNull) { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0= =C2=A0=C2=A0=C2=A0 // SAFETY: We're guaranteed by the safety contract > of `ModeObject` that > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0= =C2=A0=C2=A0=C2=A0 // `raw_mode_obj()` always returns a pointer to an > initialized `drm_mode_object`. > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0= =C2=A0=C2=A0=C2=A0 unsafe { > kernel::bindings::drm_mode_object_put(obj.as_ref().raw_mode_obj()) } > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 } > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 } > +=C2=A0=C2=A0=C2=A0 }; > +} > + > +pub(super) use impl_aref_for_mode_object; > + > +/// A trait for any object related to a [`ModeObject`] that can > return its vtable. > +/// > +/// This reference will be used for checking whether an opaque > representation of a mode object uses a > +/// specific driver trait implementation. > +/// > +/// # Safety > +/// > +/// `ModeObjectVtable::vtable()` must always return a valid pointer > to the relevant mode object's > +/// vtable. > +pub(crate) unsafe trait ModeObjectVtable { > +=C2=A0=C2=A0=C2=A0 /// The type for the auto-generated vtable. > +=C2=A0=C2=A0=C2=A0 type Vtable; > + > +=C2=A0=C2=A0=C2=A0 /// Return a static reference to the auto-generated v= table for > the relevant mode object. > +=C2=A0=C2=A0=C2=A0 fn vtable(&self) -> *const Self::Vtable; > +} > + > +/// A mode config guard. > +/// > +/// This is an exclusive primitive that represents when > [`drm_device.mode_config.mutex`] is held - as > +/// some modesetting operations (particularly ones related to > [`connectors`](connector)) are still > +/// protected under this single lock. The lock will be dropped once > this object is dropped. > +/// > +/// # Invariants > +/// > +/// - `self.0` is contained within a [`struct drm_mode_config`], > which is contained within a > +///=C2=A0=C2=A0 [`struct drm_device`]. > +/// - The [`KmsDriver`] implementation of that [`struct drm_device`] > is always `T`. > +/// - This type proves that [`drm_device.mode_config.mutex`] is > acquired. > +/// > +/// [`struct drm_mode_config`]: (srctree/include/drm/drm_device.h) > +/// [`drm_device.mode_config.mutex`]: > (srctree/include/drm/drm_device.h) > +/// [`struct drm_device`]: (srctree/include/drm/drm_device.h) > +pub struct ModeConfigGuard<'a, T: KmsDriver>(MutexGuard<'a, ()>, > PhantomData); > + > +impl<'a, T: KmsDriver> ModeConfigGuard<'a, T> { > +=C2=A0=C2=A0=C2=A0 /// Return the [`Device`] that this [`ModeConfigGuard= `] belongs > to. > +=C2=A0=C2=A0=C2=A0 pub fn drm_dev(&self) -> &'a Device { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 let lock: *mut bindings::mute= x =3D > ptr::from_ref(self.0.lock_ref()).cast_mut().cast(); > + > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // SAFETY: > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // - `self` is embedded withi= n a `drm_mode_config` via our > type invariants > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // - `self.0.lock` has an equ= ivalent data type to `mutex` > via its type invariants. > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 let mode_config =3D unsafe { = container_of!(lock, > bindings::drm_mode_config, mutex) }; > + > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // SAFETY: And that `drm_mode= _config` lives in a > `drm_device` via type invariants. > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 unsafe { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 Devic= e::from_raw(container_of!( > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0= =C2=A0=C2=A0=C2=A0 mode_config, > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0= =C2=A0=C2=A0=C2=A0 bindings::drm_device, > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0= =C2=A0=C2=A0=C2=A0 mode_config > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 )) > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 } > +=C2=A0=C2=A0=C2=A0 } > + > +=C2=A0=C2=A0=C2=A0 /// Assert that the given device is the owner of this= mode > config guard. > +=C2=A0=C2=A0=C2=A0 /// > +=C2=A0=C2=A0=C2=A0 /// # Panics > +=C2=A0=C2=A0=C2=A0 /// > +=C2=A0=C2=A0=C2=A0 /// Panics if `dev` is different from the owning devi= ce for this > mode config guard. > +=C2=A0=C2=A0=C2=A0 #[inline] > +=C2=A0=C2=A0=C2=A0 pub(crate) fn assert_owner(&self, dev: &Device) { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 assert!(ptr::eq(self.drm_dev(= ), dev)); > +=C2=A0=C2=A0=C2=A0 } > +} > diff --git a/rust/kernel/drm/kms/atomic.rs > b/rust/kernel/drm/kms/atomic.rs > new file mode 100644 > index 000000000000..cc14bff47abd > --- /dev/null > +++ b/rust/kernel/drm/kms/atomic.rs > @@ -0,0 +1,864 @@ > +// SPDX-License-Identifier: GPL-2.0 OR MIT > + > +//! [`struct drm_atomic_state`] related bindings for rust. > +//! > +//! [`struct drm_atomic_state`]: srctree/include/drm/drm_atomic.h > +use super::{connector::*, crtc::*, plane::*, KmsDriver, ModeObject}; > +use crate::{ > +=C2=A0=C2=A0=C2=A0 bindings, > +=C2=A0=C2=A0=C2=A0 drm::device::Device, > +=C2=A0=C2=A0=C2=A0 error::{from_err_ptr, to_result}, > +=C2=A0=C2=A0=C2=A0 prelude::*, > +=C2=A0=C2=A0=C2=A0 types::*, > +}; > +use core::{cell::Cell, marker::*, mem::ManuallyDrop, ops::*, > ptr::NonNull}; > + > +/// The main wrapper around [`struct drm_atomic_state`]. > +/// > +/// This type is usually embedded within another interface such as > an [`AtomicStateMutator`]. > +/// > +/// # Invariants > +/// > +/// - The data layout of this type is identical to [`struct > drm_atomic_state`]. > +/// - `state` is initialized for as long as this type is exposed to > users. > +/// > +/// [`struct drm_atomic_state`]: srctree/include/drm/drm_atomic.h > +#[repr(transparent)] > +pub struct AtomicState { > +=C2=A0=C2=A0=C2=A0 pub(super) state: Opaque, > +=C2=A0=C2=A0=C2=A0 _p: PhantomData, > +} > + > +impl AtomicState { > +=C2=A0=C2=A0=C2=A0 /// Reconstruct an immutable reference to an atomic s= tate from > the given pointer > +=C2=A0=C2=A0=C2=A0 /// > +=C2=A0=C2=A0=C2=A0 /// # Safety > +=C2=A0=C2=A0=C2=A0 /// > +=C2=A0=C2=A0=C2=A0 /// `ptr` must point to a valid initialized instance = of [`struct > drm_atomic_state`]. > +=C2=A0=C2=A0=C2=A0 /// > +=C2=A0=C2=A0=C2=A0 /// [`struct drm_atomic_state`]: > srctree/include/drm/drm_atomic.h > +=C2=A0=C2=A0=C2=A0 #[allow(dead_code)] > +=C2=A0=C2=A0=C2=A0 pub(super) unsafe fn from_raw<'a>(ptr: *const > bindings::drm_atomic_state) -> &'a Self { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // SAFETY: Our data layout is= identical > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // INVARIANT: Our safety cont= ract upholds the guarantee that > `state` is initialized for as > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // long as this type is expos= ed to users. > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 unsafe { &*ptr.cast() } > +=C2=A0=C2=A0=C2=A0 } > + > +=C2=A0=C2=A0=C2=A0 pub(crate) fn as_raw(&self) -> *mut bindings::drm_ato= mic_state { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 self.state.get() > +=C2=A0=C2=A0=C2=A0 } > + > +=C2=A0=C2=A0=C2=A0 /// Return the [`Device`] associated with this [`Atom= icState`]. > +=C2=A0=C2=A0=C2=A0 pub fn drm_dev(&self) -> &Device { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // SAFETY: > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // - `state` is initialized v= ia our type invariants. > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // - `dev` is invariant throu= ghout the lifetime of > `AtomicState` > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 unsafe { Device::from_raw((*s= elf.state.get()).dev) } > +=C2=A0=C2=A0=C2=A0 } > + > +=C2=A0=C2=A0=C2=A0 /// Return the old atomic state for `crtc`, if it is = present > within this [`AtomicState`]. > +=C2=A0=C2=A0=C2=A0 pub fn get_old_crtc_state(&self, crtc: &C) -> > Option<&C::State> > +=C2=A0=C2=A0=C2=A0 where > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 C: ModesettableCrtc + ModeObj= ect, > +=C2=A0=C2=A0=C2=A0 { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // SAFETY: This function eith= er returns NULL or a valid > pointer to a `drm_crtc_state` > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 unsafe { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 bindi= ngs::drm_atomic_get_old_crtc_state(self.as_raw(), > crtc.as_raw()) > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0= =C2=A0=C2=A0=C2=A0 .as_ref() > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0= =C2=A0=C2=A0=C2=A0 .map(|p| C::State::from_raw(p)) > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 } > +=C2=A0=C2=A0=C2=A0 } > + > +=C2=A0=C2=A0=C2=A0 /// Return the old atomic state for `plane`, if it is= present > within this [`AtomicState`]. > +=C2=A0=C2=A0=C2=A0 pub fn get_old_plane_state

(&self, plane: &P) -> > Option<&P::State> > +=C2=A0=C2=A0=C2=A0 where > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 P: ModesettablePlane + ModeOb= ject, > +=C2=A0=C2=A0=C2=A0 { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // SAFETY: This function eith= er returns NULL or a valid > pointer to a `drm_plane_state` > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 unsafe { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 bindi= ngs::drm_atomic_get_old_plane_state(self.as_raw(), > plane.as_raw()) > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0= =C2=A0=C2=A0=C2=A0 .as_ref() > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0= =C2=A0=C2=A0=C2=A0 .map(|p| P::State::from_raw(p)) > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 } > +=C2=A0=C2=A0=C2=A0 } > + > +=C2=A0=C2=A0=C2=A0 /// Return the old atomic state for `connector` if it= is present > within this [`AtomicState`]. > +=C2=A0=C2=A0=C2=A0 pub fn get_old_connector_state(&self, connector: &= C) -> > Option<&C::State> > +=C2=A0=C2=A0=C2=A0 where > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 C: ModesettableConnector + Mo= deObject, > +=C2=A0=C2=A0=C2=A0 { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // SAFETY: This function eith= er returns NULL or a valid > pointer to a `drm_connector_state`. > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 unsafe { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 > bindings::drm_atomic_get_old_connector_state(self.as_raw(), > connector.as_raw()) > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0= =C2=A0=C2=A0=C2=A0 .as_ref() > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0= =C2=A0=C2=A0=C2=A0 .map(|p| C::State::from_raw(p)) > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 } > +=C2=A0=C2=A0=C2=A0 } > +} > + > +// SAFETY: DRM atomic state objects are always reference counted and > the get/put functions satisfy > +// the requirements. > +unsafe impl AlwaysRefCounted for AtomicState { > +=C2=A0=C2=A0=C2=A0 fn inc_ref(&self) { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // SAFETY: `state` is initial= ized for as long as this type > is exposed to users > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 unsafe { bindings::drm_atomic= _state_get(self.state.get()) } > +=C2=A0=C2=A0=C2=A0 } > + > +=C2=A0=C2=A0=C2=A0 unsafe fn dec_ref(obj: NonNull) { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // SAFETY: `obj` contains a v= alid non-null pointer to an > initialized `Self`. > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 unsafe { bindings::drm_atomic= _state_put(obj.as_ptr().cast()) > } > +=C2=A0=C2=A0=C2=A0 } > +} > + > +/// A smart-pointer for modifying the contents of an atomic state. > +/// > +/// As it's not unreasonable for a modesetting driver to want to > have references to the state of > +/// multiple modesetting objects at once, along with mutating > multiple states for unique modesetting > +/// objects at once, this type provides a mechanism for safely doing > both of these things. > +/// > +/// To honor Rust's aliasing rules regarding mutable references, > this structure ensures only one > +/// mutable reference to a mode object's atomic state may exist at a > time - and refuses to provide > +/// another if one has already been taken out using runtime checks. > +pub struct AtomicStateMutator { > +=C2=A0=C2=A0=C2=A0 /// The state being mutated. Note that the use of `Ma= nuallyDrop` > here is because mutators are > +=C2=A0=C2=A0=C2=A0 /// only constructed in FFI callbacks and thus borrow= their > references to the atomic state from > +=C2=A0=C2=A0=C2=A0 /// DRM. Composers, which make use of mutators intern= ally, can > potentially be owned by rust code > +=C2=A0=C2=A0=C2=A0 /// if a driver is performing an atomic commit intern= ally - and > thus will call the drop > +=C2=A0=C2=A0=C2=A0 /// implementation here. > +=C2=A0=C2=A0=C2=A0 state: ManuallyDrop>>, > + > +=C2=A0=C2=A0=C2=A0 /// Bitmask of borrowed CRTC state objects > +=C2=A0=C2=A0=C2=A0 pub(super) borrowed_crtcs: Cell, > +=C2=A0=C2=A0=C2=A0 /// Bitmask of borrowed plane state objects > +=C2=A0=C2=A0=C2=A0 pub(super) borrowed_planes: Cell, > +=C2=A0=C2=A0=C2=A0 /// Bitmask of borrowed connector state objects > +=C2=A0=C2=A0=C2=A0 pub(super) borrowed_connectors: Cell, > +} > + > +impl AtomicStateMutator { > +=C2=A0=C2=A0=C2=A0 /// Construct a new [`AtomicStateMutator`] > +=C2=A0=C2=A0=C2=A0 /// > +=C2=A0=C2=A0=C2=A0 /// # Safety > +=C2=A0=C2=A0=C2=A0 /// > +=C2=A0=C2=A0=C2=A0 /// `ptr` must point to a valid `drm_atomic_state` > +=C2=A0=C2=A0=C2=A0 #[allow(dead_code)] > +=C2=A0=C2=A0=C2=A0 pub(super) unsafe fn new(ptr: > NonNull) -> Self { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 Self { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // SA= FETY: The data layout of `AtomicState` is > identical to drm_atomic_state > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // We= use `ManuallyDrop` because `AtomicStateMutator` is > only ever provided to users in > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // th= e context of KMS callbacks. As such, skipping ref > inc/dec for the atomic state is > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // co= nvienent for our bindings. > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 state= : ManuallyDrop::new(unsafe { > ARef::from_raw(ptr.cast()) }), > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 borro= wed_planes: Cell::default(), > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 borro= wed_crtcs: Cell::default(), > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 borro= wed_connectors: Cell::default(), > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 } > +=C2=A0=C2=A0=C2=A0 } > + > +=C2=A0=C2=A0=C2=A0 pub(crate) fn as_raw(&self) -> *mut bindings::drm_ato= mic_state { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 self.state.as_raw() > +=C2=A0=C2=A0=C2=A0 } > + > +=C2=A0=C2=A0=C2=A0 /// Return the [`Device`] for this [`AtomicStateMutat= or`]. > +=C2=A0=C2=A0=C2=A0 pub fn drm_dev(&self) -> &Device { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 self.state.drm_dev() > +=C2=A0=C2=A0=C2=A0 } > + > +=C2=A0=C2=A0=C2=A0 /// Retrieve the last committed atomic state for `crt= c` if > `crtc` has already been added to the > +=C2=A0=C2=A0=C2=A0 /// atomic state being composed. > +=C2=A0=C2=A0=C2=A0 /// > +=C2=A0=C2=A0=C2=A0 /// Returns `None` otherwise. > +=C2=A0=C2=A0=C2=A0 pub fn get_old_crtc_state(&self, crtc: &C) -> > Option<&C::State> > +=C2=A0=C2=A0=C2=A0 where > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 C: ModesettableCrtc + ModeObj= ect, > +=C2=A0=C2=A0=C2=A0 { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 self.state.get_old_crtc_state= (crtc) > +=C2=A0=C2=A0=C2=A0 } > + > +=C2=A0=C2=A0=C2=A0 /// Retrieve the last committed atomic state for `con= nector` if > `connector` has already been > +=C2=A0=C2=A0=C2=A0 /// added to the atomic state being composed. > +=C2=A0=C2=A0=C2=A0 /// > +=C2=A0=C2=A0=C2=A0 /// Returns `None` otherwise. > +=C2=A0=C2=A0=C2=A0 pub fn get_old_connector_state(&self, connector: &= C) -> > Option<&C::State> > +=C2=A0=C2=A0=C2=A0 where > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 C: ModesettableConnector + Mo= deObject, > +=C2=A0=C2=A0=C2=A0 { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 self.state.get_old_connector_= state(connector) > +=C2=A0=C2=A0=C2=A0 } > + > +=C2=A0=C2=A0=C2=A0 /// Retrieve the last committed atomic state for `pla= ne` if > `plane` has already been added to > +=C2=A0=C2=A0=C2=A0 /// the atomic state being composed. > +=C2=A0=C2=A0=C2=A0 /// > +=C2=A0=C2=A0=C2=A0 /// Returns `None` otherwise. > +=C2=A0=C2=A0=C2=A0 pub fn get_old_plane_state

(&self, plane: &P) -> > Option<&P::State> > +=C2=A0=C2=A0=C2=A0 where > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 P: ModesettablePlane + ModeOb= ject, > +=C2=A0=C2=A0=C2=A0 { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 self.state.get_old_plane_stat= e(plane) > +=C2=A0=C2=A0=C2=A0 } > + > +=C2=A0=C2=A0=C2=A0 /// Return a composer for `plane`s new atomic state i= f it was > previously added to the atomic > +=C2=A0=C2=A0=C2=A0 /// state being composed. > +=C2=A0=C2=A0=C2=A0 /// > +=C2=A0=C2=A0=C2=A0 /// Returns `None` otherwise, or if another mutator s= till exists > for this state. > +=C2=A0=C2=A0=C2=A0 pub fn get_new_crtc_state(&self, crtc: &C) -> > Option> > +=C2=A0=C2=A0=C2=A0 where > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 C: ModesettableCrtc + ModeObj= ect, > +=C2=A0=C2=A0=C2=A0 { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // SAFETY: DRM either returns= NULL or a valid pointer to a > `drm_crtc_state` > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 let state =3D > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 unsaf= e { > bindings::drm_atomic_get_new_crtc_state(self.as_raw(), crtc.as_raw()) > }; > + > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 CrtcStateMutator:::= :new(self, > NonNull::new(state)?) > +=C2=A0=C2=A0=C2=A0 } > + > +=C2=A0=C2=A0=C2=A0 /// Return a composer for `plane`s new atomic state i= f it was > previously added to the atomic > +=C2=A0=C2=A0=C2=A0 /// state being composed. > +=C2=A0=C2=A0=C2=A0 /// > +=C2=A0=C2=A0=C2=A0 /// Returns `None` otherwise, or if another mutator s= till exists > for this state. > +=C2=A0=C2=A0=C2=A0 pub fn get_new_plane_state

(&self, plane: &P) -> > Option> > +=C2=A0=C2=A0=C2=A0 where > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 P: ModesettablePlane + ModeOb= ject, > +=C2=A0=C2=A0=C2=A0 { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // SAFETY: DRM either returns= NULL or a valid pointer to a > `drm_plane_state`. > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 let state =3D > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 unsaf= e { > bindings::drm_atomic_get_new_plane_state(self.as_raw(), > plane.as_raw()) }; > + > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 PlaneStateMutator::= ::new(self, > NonNull::new(state)?) > +=C2=A0=C2=A0=C2=A0 } > + > +=C2=A0=C2=A0=C2=A0 /// Return a composer for `crtc`s new atomic state if= it was > previously added to the atomic > +=C2=A0=C2=A0=C2=A0 /// state being composed. > +=C2=A0=C2=A0=C2=A0 /// > +=C2=A0=C2=A0=C2=A0 /// Returns `None` otherwise, or if another mutator s= till exists > for this state. > +=C2=A0=C2=A0=C2=A0 pub fn get_new_connector_state( > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 &self, > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 connector: &C, > +=C2=A0=C2=A0=C2=A0 ) -> Option> > +=C2=A0=C2=A0=C2=A0 where > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 C: ModesettableConnector + Mo= deObject, > +=C2=A0=C2=A0=C2=A0 { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // SAFETY: DRM either returns= NULL or a valid pointer to a > `drm_connector_state` > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 let state =3D unsafe { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 > bindings::drm_atomic_get_new_connector_state(self.as_raw(), > connector.as_raw()) > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 }; > + > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 ConnectorStateMutator::::new(self, > NonNull::new(state)?) > +=C2=A0=C2=A0=C2=A0 } > +} > + > +/// An [`AtomicStateMutator`] wrapper which is not yet part of any > commit operation. > +/// > +/// Since it's not yet part of a commit operation, new mode objects > may be added to the state. It > +/// also holds a reference to the underlying [`AtomicState`] that > will be released when this object > +/// is dropped. > +pub struct AtomicStateComposer(AtomicStateMutator); > + > +impl Deref for AtomicStateComposer { > +=C2=A0=C2=A0=C2=A0 type Target =3D AtomicStateMutator; > + > +=C2=A0=C2=A0=C2=A0 fn deref(&self) -> &Self::Target { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 &self.0 > +=C2=A0=C2=A0=C2=A0 } > +} > + > +impl Drop for AtomicStateComposer { > +=C2=A0=C2=A0=C2=A0 fn drop(&mut self) { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // SAFETY: We're in drop, so = this is guaranteed to be the > last possible reference > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 unsafe { ManuallyDrop::drop(&= mut self.0.state) } > +=C2=A0=C2=A0=C2=A0 } > +} > + > +impl AtomicStateComposer { > +=C2=A0=C2=A0=C2=A0 /// # Safety > +=C2=A0=C2=A0=C2=A0 /// > +=C2=A0=C2=A0=C2=A0 /// The caller guarantees that `ptr` points to a vali= d instance > of `drm_atomic_state`. > +=C2=A0=C2=A0=C2=A0 pub(crate) unsafe fn new(ptr: > NonNull) -> Self { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // SAFETY: see `AtomicStateMu= tator::from_raw()` > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 Self(unsafe { AtomicStateMuta= tor::new(ptr) }) > +=C2=A0=C2=A0=C2=A0 } > + > +=C2=A0=C2=A0=C2=A0 /// Attempt to add the state for `crtc` to the atomic= state for > this composer if it hasn't > +=C2=A0=C2=A0=C2=A0 /// already been added, and create a mutator for it. > +=C2=A0=C2=A0=C2=A0 /// > +=C2=A0=C2=A0=C2=A0 /// If a composer already exists for this `crtc`, thi= s function > returns `Error(EBUSY)`. If > +=C2=A0=C2=A0=C2=A0 /// attempting to add the state fails, another error = code will > be returned. > +=C2=A0=C2=A0=C2=A0 pub fn add_crtc_state(&self, crtc: &C) -> > Result> > +=C2=A0=C2=A0=C2=A0 where > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 C: ModesettableCrtc + ModeObj= ect, > +=C2=A0=C2=A0=C2=A0 { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // SAFETY: DRM will only retu= rn a valid pointer to a > `drm_crtc_state` - or an error. > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 let state =3D unsafe { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 from_= err_ptr(bindings::drm_atomic_get_crtc_state( > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0= =C2=A0=C2=A0=C2=A0 self.as_raw(), > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0= =C2=A0=C2=A0=C2=A0 crtc.as_raw(), > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 )) > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 .map(= |c| NonNull::new_unchecked(c)) > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 }?; > + > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 CrtcStateMutator:::= :new(self, state).ok_or(EBUSY) > +=C2=A0=C2=A0=C2=A0 } > + > +=C2=A0=C2=A0=C2=A0 /// Attempt to add the state for `plane` to the atomi= c state for > this composer if it hasn't > +=C2=A0=C2=A0=C2=A0 /// already been added, and create a mutator for it. > +=C2=A0=C2=A0=C2=A0 /// > +=C2=A0=C2=A0=C2=A0 /// If a composer already exists for this `plane`, th= is function > returns `Error(EBUSY)`. If > +=C2=A0=C2=A0=C2=A0 /// attempting to add the state fails, another error = code will > be returned. > +=C2=A0=C2=A0=C2=A0 pub fn add_plane_state

(&self, plane: &P) -> > Result> > +=C2=A0=C2=A0=C2=A0 where > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 P: ModesettablePlane + ModeOb= ject, > +=C2=A0=C2=A0=C2=A0 { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // SAFETY: DRM will only retu= rn a valid pointer to a > `drm_plane_state` - or an error. > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 let state =3D unsafe { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 from_= err_ptr(bindings::drm_atomic_get_plane_state( > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0= =C2=A0=C2=A0=C2=A0 self.as_raw(), > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0= =C2=A0=C2=A0=C2=A0 plane.as_raw(), > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 )) > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 .map(= |p| NonNull::new_unchecked(p)) > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 }?; > + > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 PlaneStateMutator::= ::new(self, state).ok_or(EBUSY) > +=C2=A0=C2=A0=C2=A0 } > + > +=C2=A0=C2=A0=C2=A0 /// Attempt to add the state for `connector` to the a= tomic state > for this composer if it hasn't > +=C2=A0=C2=A0=C2=A0 /// already been added, and create a mutator for it. > +=C2=A0=C2=A0=C2=A0 /// > +=C2=A0=C2=A0=C2=A0 /// If a composer already exists for this `connector`= , this > function returns `Error(EBUSY)`. If > +=C2=A0=C2=A0=C2=A0 /// attempting to add the state fails, another error = code will > be returned. > +=C2=A0=C2=A0=C2=A0 pub fn add_connector_state( > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 &self, > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 connector: &C, > +=C2=A0=C2=A0=C2=A0 ) -> Result> > +=C2=A0=C2=A0=C2=A0 where > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 C: ModesettableConnector + Mo= deObject, > +=C2=A0=C2=A0=C2=A0 { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // SAFETY: DRM will only retu= rn a valid pointer to a > `drm_plane_state` - or an error. > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 let state =3D unsafe { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 from_= err_ptr(bindings::drm_atomic_get_connector_state( > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0= =C2=A0=C2=A0=C2=A0 self.as_raw(), > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0= =C2=A0=C2=A0=C2=A0 connector.as_raw(), > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 )) > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 .map(= |c| NonNull::new_unchecked(c)) > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 }?; > + > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 ConnectorStateMutator::::new(self, > state).ok_or(EBUSY) > +=C2=A0=C2=A0=C2=A0 } > + > +=C2=A0=C2=A0=C2=A0 /// Attempt to add any planes affected by changes on = `crtc` to > this [`AtomicStateComposer`]. > +=C2=A0=C2=A0=C2=A0 /// > +=C2=A0=C2=A0=C2=A0 /// Will return an [`Error`] if this fails. > +=C2=A0=C2=A0=C2=A0 pub fn add_affected_planes(&self, crtc: &C) -> Res= ult > +=C2=A0=C2=A0=C2=A0 where > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 C: ModesettableCrtc + ModeObj= ect, > +=C2=A0=C2=A0=C2=A0 { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // SAFETY: Both .as_raw() val= ues are guaranteed to return a > valid pointer > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 to_result(unsafe { > bindings::drm_atomic_add_affected_planes(self.as_raw(), > crtc.as_raw()) }) > +=C2=A0=C2=A0=C2=A0 } > +} > + > +/// A macro for declaring the repetitive take_all(), take_state(), > etc. methods for atomic state > +/// token types. > +/// > +/// It is assumed that $token_name refers to a struct that contains > two members: > +/// > +/// - `state`: This should be the atomic state type to use > +/// - The object in question. The name of this member is generated > by converting $obj to lowercase. > +/// > +/// The struct should have one lifetime ($lifetime_a) declared, and > one meta-variable ($meta) which > +/// should be bound to the Driver* trait for the given mode object. > +macro_rules! impl_atomic_state_token_ops { > +=C2=A0=C2=A0=C2=A0 ( > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 $token_name:ident, > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 $state:ident, > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 $obj:ident, > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 use <$lifetime_a:lifetime, $m= eta:ident> > +=C2=A0=C2=A0=C2=A0 ) =3D> { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 kernel::macros::paste! { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 /// C= reate a new token. > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 /// > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 /// #= Safety > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 /// > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 /// T= o use this function it must be known in the current > context that: > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 /// > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 /// -= The object has had its atomic states added to > `state`. > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 /// -= No state mutator can possibly be taken out for the > objects new state. > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 pub(c= rate) unsafe fn new( > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0= =C2=A0=C2=A0=C2=A0 [<$obj:lower>]: &$lifetime_a $obj<$meta>, > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0= =C2=A0=C2=A0=C2=A0 state: &$lifetime_a $state<$meta::Driver>, > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 ) -> = Self { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0= =C2=A0=C2=A0=C2=A0 Self { [<$obj:lower>], state } > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 } > + > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 #[doc= =3D concat!("Get the [`", stringify!($obj), "`] > associated with this", > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0= =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2= =A0=C2=A0=C2=A0 " [`", stringify!($token_name), "`].")] > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 pub f= n [<$obj:lower>](&self) -> &$lifetime_a $obj<$meta> > { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0= =C2=A0=C2=A0=C2=A0 self.[<$obj:lower>] > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 } > + > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 /// E= xchange this token for a (atomic_state, old_state, > new_state) tuple. > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 pub f= n take_all(self) -> ( > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0= =C2=A0=C2=A0=C2=A0 &$lifetime_a $state<$meta::Driver>, > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0= =C2=A0=C2=A0=C2=A0 &$lifetime_a [<$obj State>]<$meta::State>, > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0= =C2=A0=C2=A0=C2=A0 [<$obj StateMutator>]<$lifetime_a, [<$obj > State>]<$meta::State>>, > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 ) { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0= =C2=A0=C2=A0=C2=A0 let (old_state, new_state) =3D ( > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0= =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 self.state.[ _state>](self.[<$obj:lower>]), > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0= =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 self.state.[ _state>](self.[<$obj:lower>]), > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0= =C2=A0=C2=A0=C2=A0 ); > + > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0= =C2=A0=C2=A0=C2=A0 // SAFETY: > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0= =C2=A0=C2=A0=C2=A0 // - Both the old and new object state are present > in `state` via our type > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0= =C2=A0=C2=A0=C2=A0 //=C2=A0=C2=A0 invariants. > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0= =C2=A0=C2=A0=C2=A0 // - The new state is guaranteed to have no mutators > taken out via our type > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0= =C2=A0=C2=A0=C2=A0 //=C2=A0=C2=A0 invariants. > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0= =C2=A0=C2=A0=C2=A0 let (old_state, new_state) =3D unsafe { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0= =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 (old_state.unwrap_unchecked(), > new_state.unwrap_unchecked()) > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0= =C2=A0=C2=A0=C2=A0 }; > + > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0= =C2=A0=C2=A0=C2=A0 (self.state, old_state, new_state) > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 } > + > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 #[doc= =3D concat!("Exchange this token for the old [`", > stringify!($obj), "State`].")] > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 pub f= n take_old_state(self) -> &$lifetime_a [<$obj > State>]<$meta::State> { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0= =C2=A0=C2=A0=C2=A0 let old =3D self.state.[ _state>](self.[<$obj:lower>]); > + > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0= =C2=A0=C2=A0=C2=A0 // SAFETY: The old state is guaranteed to be present > in `state` via our type > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0= =C2=A0=C2=A0=C2=A0 // invariants. > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0= =C2=A0=C2=A0=C2=A0 unsafe { old.unwrap_unchecked() } > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 } > + > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 #[doc= =3D concat!("Exchange this token for the new [`", > stringify!($obj), "State`].")] > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 pub f= n take_new_state( > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0= =C2=A0=C2=A0=C2=A0 self > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 ) -> = [<$obj StateMutator>]<$lifetime_a, [<$obj > State>]<$meta::State>> { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0= =C2=A0=C2=A0=C2=A0 let new =3D self.state.[ _state>](self.[<$obj:lower>]); > + > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0= =C2=A0=C2=A0=C2=A0 // SAFETY: > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0= =C2=A0=C2=A0=C2=A0 // - The new state is guaranteed to be present in > our `state` via our type > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0= =C2=A0=C2=A0=C2=A0 //=C2=A0=C2=A0 invariants. > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0= =C2=A0=C2=A0=C2=A0 // - The new state is guaranteed not to have any > mutators taken out for it via our > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0= =C2=A0=C2=A0=C2=A0 //=C2=A0=C2=A0 type invariants. > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0= =C2=A0=C2=A0=C2=A0 unsafe { new.unwrap_unchecked() } > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 } > + > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 #[doc= =3D concat!("Exchange this token for both the old > and new [`", > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0= =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2= =A0=C2=A0=C2=A0 stringify!($obj), "State`].")] > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 pub f= n take_old_new_state(self) -> ( > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0= =C2=A0=C2=A0=C2=A0 &$lifetime_a [<$obj State>]<$meta::State>, > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0= =C2=A0=C2=A0=C2=A0 [<$obj StateMutator>]<$lifetime_a, [<$obj > State>]<$meta::State>>, > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 ) { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0= =C2=A0=C2=A0=C2=A0 let (old_state, new_state) =3D ( > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0= =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 self.state.[ _state>](self.[<$obj:lower>]), > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0= =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 self.state.[ _state>](self.[<$obj:lower>]), > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0= =C2=A0=C2=A0=C2=A0 ); > + > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0= =C2=A0=C2=A0=C2=A0 // SAFETY: > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0= =C2=A0=C2=A0=C2=A0 // - Both the old and new object state are present > in `state` via our type > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0= =C2=A0=C2=A0=C2=A0 //=C2=A0=C2=A0 invariants. > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0= =C2=A0=C2=A0=C2=A0 // - The new state is guaranteed to have no mutators > taken out via our type > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0= =C2=A0=C2=A0=C2=A0 //=C2=A0=C2=A0 invariants. > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0= =C2=A0=C2=A0=C2=A0 let (old_state, new_state) =3D unsafe { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0= =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 (old_state.unwrap_unchecked(), > new_state.unwrap_unchecked()) > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0= =C2=A0=C2=A0=C2=A0 }; > + > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0= =C2=A0=C2=A0=C2=A0 (old_state, new_state) > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 } > + > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 #[doc= =3D concat!("Exchange this token for both the [`", > stringify!($state), > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0= =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2= =A0=C2=A0=C2=A0 "`] and the old [`", stringify!($obj), > "State`].")] > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 pub f= n take_state_old_state(self) -> ( > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0= =C2=A0=C2=A0=C2=A0 &$lifetime_a $state<$meta::Driver>, > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0= =C2=A0=C2=A0=C2=A0 &$lifetime_a [<$obj State>]<$meta::State>, > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 ) { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0= =C2=A0=C2=A0=C2=A0 let old =3D self.state.[ _state>](self.[<$obj:lower>]); > + > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0= =C2=A0=C2=A0=C2=A0 // SAFETY: The old state is guaranteed to be present > in `state` via our type > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0= =C2=A0=C2=A0=C2=A0 // invariants. > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0= =C2=A0=C2=A0=C2=A0 (self.state, unsafe { old.unwrap_unchecked() }) > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 } > + > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 #[doc= =3D concat!("Exchange this token for both the [`", > stringify!($state), > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0= =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2= =A0=C2=A0=C2=A0 "`] and the new [`", stringify!($obj), > "State`].")] > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 pub f= n take_state_new_state(self) -> ( > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0= =C2=A0=C2=A0=C2=A0 &$lifetime_a $state<$meta::Driver>, > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0= =C2=A0=C2=A0=C2=A0 [<$obj StateMutator>]<$lifetime_a, [<$obj > State>]<$meta::State>>, > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 ) { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0= =C2=A0=C2=A0=C2=A0 let new =3D self.state.[ _state>](self.[<$obj:lower>]); > + > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0= =C2=A0=C2=A0=C2=A0 // SAFETY: > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0= =C2=A0=C2=A0=C2=A0 // - The new state is guaranteed to be present in > `state` via our type > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0= =C2=A0=C2=A0=C2=A0 //=C2=A0=C2=A0 invariants. > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0= =C2=A0=C2=A0=C2=A0 // - The new state is guaranteed to have no mutators > taken out via our type > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0= =C2=A0=C2=A0=C2=A0 //=C2=A0=C2=A0 invariants. > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0= =C2=A0=C2=A0=C2=A0 (self.state, unsafe { new.unwrap_unchecked() }) > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 } > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 } > + > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 #[doc =3D concat!("Exchange t= his token for the [`", > stringify!($state), "`].")] > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 pub fn take_state(self) -> &$= lifetime_a > $state<$meta::Driver> { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 self.= state > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 } > +=C2=A0=C2=A0=C2=A0 }; > +} > + > +pub(crate) use impl_atomic_state_token_ops; > + > +/// A token proving that no modesets for a commit have completed. > +/// > +/// This token is proof that no commits have yet completed, and is > provided as an argument to > +/// [`KmsDriver::atomic_commit_tail`]. This may be used with > +/// [`AtomicCommitTail::commit_modeset_disables`]. > +pub struct ModesetsReadyToken<'a>(PhantomData<&'a ()>); > + > +/// A token proving that modeset disables for a commit have > completed. > +/// > +/// This token is proof that an implementor's > [`KmsDriver::atomic_commit_tail`] phase has finished > +/// committing any operations which disable mode objects. It is > returned by > +/// [`AtomicCommitTail::commit_modeset_disables`], and can be used > with > +/// [`AtomicCommitTail::commit_modeset_enables`] to acquire a > [`EnablesCommittedToken`]. > +pub struct DisablesCommittedToken<'a>(PhantomData<&'a ()>); > + > +/// A token proving that modeset enables for a commit have > completed. > +/// > +/// This token is proof that an implementor's > [`KmsDriver::atomic_commit_tail`] phase has finished > +/// committing any operations which enable mode objects. It is > returned by > +/// [`AtomicCommitTail::commit_modeset_enables`]. > +pub struct EnablesCommittedToken<'a>(PhantomData<&'a ()>); > + > +/// A token proving that no plane updates for a commit have > completed. > +/// > +/// This token is proof that no plane updates have yet been > completed within an implementor's > +/// [`KmsDriver::atomic_commit_tail`] implementation, and that we > are ready to begin updating planes. It > +/// is provided as an argument to [`KmsDriver::atomic_commit_tail`]. > +pub struct PlaneUpdatesReadyToken<'a>(PhantomData<&'a ()>); > + > +/// A token proving that all plane updates for a commit have > completed. > +/// > +/// This token is proof that all plane updates within an > implementor's [`KmsDriver::atomic_commit_tail`] > +/// implementation have completed. It is returned by > [`AtomicCommitTail::commit_planes`]. > +pub struct PlaneUpdatesCommittedToken<'a>(PhantomData<&'a ()>); > + > +/// An [`AtomicState`] interface that allows a driver to control the > [`atomic_commit_tail`] > +/// callback. > +/// > +/// This object is provided as an argument to > [`KmsDriver::atomic_commit_tail`], and represents an atomic > +/// state within the commit tail phase which is still in the process > of being committed to hardware. > +/// It may be used to control the order in which the commit process > happens. > +/// > +/// # Invariants > +/// > +/// Same as [`AtomicState`]. > +/// > +/// [`atomic_commit_tail`]: > srctree/include/drm/drm_modeset_helper_vtables.h > +pub struct AtomicCommitTail<'a, T: KmsDriver>(&'a AtomicState); > + > +impl<'a, T: KmsDriver> AtomicCommitTail<'a, T> { > +=C2=A0=C2=A0=C2=A0 /// Commit modesets which would disable outputs. > +=C2=A0=C2=A0=C2=A0 /// > +=C2=A0=C2=A0=C2=A0 /// This function commits any modesets which would sh= ut down > outputs, along with preparing them > +=C2=A0=C2=A0=C2=A0 /// for a new mode (if needed). > +=C2=A0=C2=A0=C2=A0 /// > +=C2=A0=C2=A0=C2=A0 /// Since it is physically impossible to disable an o= utput > multiple times, and since it is > +=C2=A0=C2=A0=C2=A0 /// logically unsound to disable an output within an = atomic > commit after the output was enabled > +=C2=A0=C2=A0=C2=A0 /// in the same commit - this function requires a > [`ModesetsReadyToken`] to consume and returns > +=C2=A0=C2=A0=C2=A0 /// a [`DisablesCommittedToken`]. > +=C2=A0=C2=A0=C2=A0 /// > +=C2=A0=C2=A0=C2=A0 /// If compatibility with legacy CRTC helpers is desi= red, this > +=C2=A0=C2=A0=C2=A0 /// should be called before [`commit_planes`] which i= s what the > default commit function does. > +=C2=A0=C2=A0=C2=A0 /// But drivers with different needs can group the mo= deset > commits tgether and do the plane > +=C2=A0=C2=A0=C2=A0 /// commits at the end. This is useful for drivers do= ing runtime > PM since then plane updates > +=C2=A0=C2=A0=C2=A0 /// only happen when the CRTC is actually enabled. > +=C2=A0=C2=A0=C2=A0 /// > +=C2=A0=C2=A0=C2=A0 /// [`commit_planes`]: AtomicCommitTail::commit_plane= s > +=C2=A0=C2=A0=C2=A0 #[inline] > +=C2=A0=C2=A0=C2=A0 #[must_use] > +=C2=A0=C2=A0=C2=A0 pub fn commit_modeset_disables<'b>( > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 &mut self, > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 _token: ModesetsReadyToken<'_= >, > +=C2=A0=C2=A0=C2=A0 ) -> DisablesCommittedToken<'b> { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // SAFETY: Both `as_raw()` ca= lls are guaranteed to return > valid pointers > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 unsafe { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 bindi= ngs::drm_atomic_helper_commit_modeset_disables( > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0= =C2=A0=C2=A0=C2=A0 self.0.drm_dev().as_raw(), > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0= =C2=A0=C2=A0=C2=A0 self.0.as_raw(), > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 ) > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 } > + > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 DisablesCommittedToken(Phanto= mData) > +=C2=A0=C2=A0=C2=A0 } > + > +=C2=A0=C2=A0=C2=A0 /// Commit all plane updates. > +=C2=A0=C2=A0=C2=A0 /// > +=C2=A0=C2=A0=C2=A0 /// This function performs all plane updates for the = given > [`AtomicCommitTail`]. Since it is > +=C2=A0=C2=A0=C2=A0 /// logically unsound to perform the same plane updat= e more then > once in a given atomic commit, > +=C2=A0=C2=A0=C2=A0 /// this function requires a [`PlaneUpdatesReadyToken= `] to > consume and returns a > +=C2=A0=C2=A0=C2=A0 /// [`PlaneUpdatesCommittedToken`] to prove that plan= e updates > for the state have completed. > +=C2=A0=C2=A0=C2=A0 #[inline] > +=C2=A0=C2=A0=C2=A0 #[must_use] > +=C2=A0=C2=A0=C2=A0 pub fn commit_planes<'b>( > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 &mut self, > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 _token: PlaneUpdatesReadyToke= n<'_>, > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 flags: PlaneCommitFlags, > +=C2=A0=C2=A0=C2=A0 ) -> PlaneUpdatesCommittedToken<'b> { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // SAFETY: Both `as_raw()` ca= lls are guaranteed to return > valid pointers > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 unsafe { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 bindi= ngs::drm_atomic_helper_commit_planes( > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0= =C2=A0=C2=A0=C2=A0 self.0.drm_dev().as_raw(), > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0= =C2=A0=C2=A0=C2=A0 self.0.as_raw(), > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0= =C2=A0=C2=A0=C2=A0 flags.into(), > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 ) > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 } > + > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 PlaneUpdatesCommittedToken(Ph= antomData) > +=C2=A0=C2=A0=C2=A0 } > + > +=C2=A0=C2=A0=C2=A0 /// Commit modesets which would enable outputs. > +=C2=A0=C2=A0=C2=A0 /// > +=C2=A0=C2=A0=C2=A0 /// This function commits any modesets in the given > [`AtomicCommitTail`] which would enable > +=C2=A0=C2=A0=C2=A0 /// outputs, along with preparing them for their new = modes (if > needed). > +=C2=A0=C2=A0=C2=A0 /// > +=C2=A0=C2=A0=C2=A0 /// Since it is logically unsound to enable an output= before any > disabling modesets within the > +=C2=A0=C2=A0=C2=A0 /// same atomic commit have been performed, and physi= cally > impossible to enable the same output > +=C2=A0=C2=A0=C2=A0 /// multiple times - this function requires a > [`DisablesCommittedToken`] to consume and returns > +=C2=A0=C2=A0=C2=A0 /// a [`EnablesCommittedToken`] which may be used as = proof that > all modesets in the state have > +=C2=A0=C2=A0=C2=A0 /// been completed. > +=C2=A0=C2=A0=C2=A0 #[inline] > +=C2=A0=C2=A0=C2=A0 #[must_use] > +=C2=A0=C2=A0=C2=A0 pub fn commit_modeset_enables<'b>( > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 &mut self, > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 _token: DisablesCommittedToke= n<'_>, > +=C2=A0=C2=A0=C2=A0 ) -> EnablesCommittedToken<'b> { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // SAFETY: Both `as_raw()` ca= lls are guaranteed to return > valid pointers > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 unsafe { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 bindi= ngs::drm_atomic_helper_commit_modeset_enables( > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0= =C2=A0=C2=A0=C2=A0 self.0.drm_dev().as_raw(), > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0= =C2=A0=C2=A0=C2=A0 self.0.as_raw(), > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 ) > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 } > + > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 EnablesCommittedToken(Phantom= Data) > +=C2=A0=C2=A0=C2=A0 } > + > +=C2=A0=C2=A0=C2=A0 /// Fake vblank events if needed. > +=C2=A0=C2=A0=C2=A0 /// > +=C2=A0=C2=A0=C2=A0 /// Note that this is still relevant to drivers which= don't > implement [`VblankSupport`] for any > +=C2=A0=C2=A0=C2=A0 /// of their CRTCs. > +=C2=A0=C2=A0=C2=A0 /// > +=C2=A0=C2=A0=C2=A0 /// TODO: more doc > +=C2=A0=C2=A0=C2=A0 /// > +=C2=A0=C2=A0=C2=A0 /// [`VblankSupport`]: super::vblank::VblankSupport > +=C2=A0=C2=A0=C2=A0 pub fn fake_vblank(&mut self) { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // SAFETY: `as_raw()` is guar= anteed to always return a valid > pointer > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 unsafe { > bindings::drm_atomic_helper_fake_vblank(self.0.as_raw()) } > +=C2=A0=C2=A0=C2=A0 } > + > +=C2=A0=C2=A0=C2=A0 /// Signal completion of the hardware commit step. > +=C2=A0=C2=A0=C2=A0 /// > +=C2=A0=C2=A0=C2=A0 /// This swaps the atomic state into the relevant ato= mic state > pointers and marks the hardware > +=C2=A0=C2=A0=C2=A0 /// commit step as completed. Since this step can onl= y happen > after all plane updates and > +=C2=A0=C2=A0=C2=A0 /// modesets within an [`AtomicCommitTail`] have been= completed, > it requires both a > +=C2=A0=C2=A0=C2=A0 /// [`EnablesCommittedToken`] and a > [`PlaneUpdatesCommittedToken`] to consume. After this > +=C2=A0=C2=A0=C2=A0 /// function is called, the caller no longer has excl= usive > access to the underlying atomic > +=C2=A0=C2=A0=C2=A0 /// state. As such, this function consumes the > [`AtomicCommitTail`] object and returns a > +=C2=A0=C2=A0=C2=A0 /// [`CommittedAtomicState`] accessor for performing = post-hw > commit tasks. > +=C2=A0=C2=A0=C2=A0 pub fn commit_hw_done<'b>( > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 self, > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 _modeset_token: EnablesCommit= tedToken<'_>, > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 _plane_updates_token: PlaneUp= datesCommittedToken<'_>, > +=C2=A0=C2=A0=C2=A0 ) -> CommittedAtomicState<'b, T> > +=C2=A0=C2=A0=C2=A0 where > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 'a: 'b, > +=C2=A0=C2=A0=C2=A0 { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // SAFETY: we consume the `At= omicCommitTail` object, making > it impossible for the user to > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // mutate the state after thi= s function has been called - > which upholds the safety > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // requirements of the C API = allowing us to safely call this > function > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 unsafe { > bindings::drm_atomic_helper_commit_hw_done(self.0.as_raw()) }; > + > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 CommittedAtomicState(self.0) > +=C2=A0=C2=A0=C2=A0 } > +} > + > +// The actual raw C callback for custom atomic commit tail > implementations > +pub(crate) unsafe extern "C" fn commit_tail_callback( > +=C2=A0=C2=A0=C2=A0 state: *mut bindings::drm_atomic_state, > +) { > +=C2=A0=C2=A0=C2=A0 // SAFETY: > +=C2=A0=C2=A0=C2=A0 // - We're guaranteed by DRM that `state` always poin= ts to a > valid instance of > +=C2=A0=C2=A0=C2=A0 //=C2=A0=C2=A0 `bindings::drm_atomic_state` > +=C2=A0=C2=A0=C2=A0 // - This conversion is safe via the type invariants > +=C2=A0=C2=A0=C2=A0 let state =3D unsafe { AtomicState::from_raw(state.ca= st_const()) > }; > + > +=C2=A0=C2=A0=C2=A0 T::atomic_commit_tail( > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 AtomicCommitTail(state), > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 ModesetsReadyToken(PhantomDat= a), > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 PlaneUpdatesReadyToken(Phanto= mData), > +=C2=A0=C2=A0=C2=A0 ); > +} > + > +/// An [`AtomicState`] which was just committed with > [`AtomicCommitTail::commit_hw_done`]. > +/// > +/// This object represents an [`AtomicState`] which has been fully > committed to hardware, and as > +/// such may no longer be mutated as it is visible to userspace. It > may be used to control what > +/// happens immediately after an atomic commit finishes within the > [`atomic_commit_tail`] callback. > +/// > +/// Since acquiring this object means that all modesetting locks > have been dropped, a non-blocking > +/// commit could happen at the same time an [`atomic_commit_tail`] > implementer has access to this > +/// object. Thus, it cannot be assumed that this object represents > the current hardware state - and > +/// instead only represents the final result of the > [`AtomicCommitTail`] that was just committed. > +/// > +/// # Invariants > +/// > +/// It may be assumed that [`drm_atomic_helper_commit_hw_done`] has > been called as long as this type > +/// exists. > +/// > +/// [`atomic_commit_tail`]: KmsDriver::atomic_commit_tail > +/// [`drm_atomic_helper_commit_hw_done`]: > srctree/include/drm/drm_atomic_helper.h > +pub struct CommittedAtomicState<'a, T: KmsDriver>(&'a > AtomicState); > + > +impl<'a, T: KmsDriver> CommittedAtomicState<'a, T> { > +=C2=A0=C2=A0=C2=A0 /// Wait for page flips on this state to complete > +=C2=A0=C2=A0=C2=A0 pub fn wait_for_flip_done(&self) { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // SAFETY: `drm_atomic_helper= _commit_hw_done` has been > called via our invariants > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 unsafe { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 bindi= ngs::drm_atomic_helper_wait_for_flip_done( > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0= =C2=A0=C2=A0=C2=A0 self.0.drm_dev().as_raw(), > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0= =C2=A0=C2=A0=C2=A0 self.0.as_raw(), > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 ) > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 } > +=C2=A0=C2=A0=C2=A0 } > +} > + > +impl<'a, T: KmsDriver> Drop for CommittedAtomicState<'a, T> { > +=C2=A0=C2=A0=C2=A0 fn drop(&mut self) { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // SAFETY: > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // * This interface represent= s the last atomic state > accessor which could be affected as a > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 //=C2=A0=C2=A0 result of reso= urces from an atomic commit being cleaned > up. > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 unsafe { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 > bindings::drm_atomic_helper_cleanup_planes(self.0.drm_dev().as_raw(), > self.0.as_raw()) > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 } > +=C2=A0=C2=A0=C2=A0 } > +} > + > +/// An enumator representing a single flag in [`PlaneCommitFlags`]. > +/// > +/// This is a non-exhaustive list, as the C side could add more > later. > +#[derive(Copy, Clone, PartialEq, Eq)] > +#[repr(u32)] > +#[non_exhaustive] > +pub enum PlaneCommitFlag { > +=C2=A0=C2=A0=C2=A0 /// Don't notify applications of plane updates for ne= wly- > disabled planes. Drivers are encouraged > +=C2=A0=C2=A0=C2=A0 /// to set this flag by default, as otherwise they ne= ed to > ignore plane updates for disabled > +=C2=A0=C2=A0=C2=A0 /// planes by hand. > +=C2=A0=C2=A0=C2=A0 ActiveOnly =3D (1 << 0), > +=C2=A0=C2=A0=C2=A0 /// Tell the DRM core that the display hardware requi= res that a > [`Crtc`]'s planes must be > +=C2=A0=C2=A0=C2=A0 /// disabled when the [`Crtc`] is disabled. When not = specified, > +=C2=A0=C2=A0=C2=A0 /// [`AtomicCommitTail::commit_planes`] will skip the= atomic > disable callbacks for a plane if > +=C2=A0=C2=A0=C2=A0 /// the [`Crtc`] in the old [`PlaneState`] needs a mo= desetting > operation. It is still up to the > +=C2=A0=C2=A0=C2=A0 /// driver to disable said planes in their > [`DriverCrtc::atomic_disable`] callback. > +=C2=A0=C2=A0=C2=A0 NoDisableAfterModeset =3D (1 << 1), > +} > + > +impl BitOr for PlaneCommitFlag { > +=C2=A0=C2=A0=C2=A0 type Output =3D PlaneCommitFlags; > + > +=C2=A0=C2=A0=C2=A0 fn bitor(self, rhs: Self) -> Self::Output { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 PlaneCommitFlags(self as u32 = | rhs as u32) > +=C2=A0=C2=A0=C2=A0 } > +} > + > +impl BitOr for PlaneCommitFlag { > +=C2=A0=C2=A0=C2=A0 type Output =3D PlaneCommitFlags; > + > +=C2=A0=C2=A0=C2=A0 fn bitor(self, rhs: PlaneCommitFlags) -> Self::Output= { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 PlaneCommitFlags(self as u32 = | rhs.0) > +=C2=A0=C2=A0=C2=A0 } > +} > + > +/// A bitmask for controlling the behavior of > [`AtomicCommitTail::commit_planes`]. > +/// > +/// This corresponds to the `DRM_PLANE_COMMIT_*` flags on the C > side. Note that this bitmask does > +/// not discard unknown values in order to ensure that adding new > flags on the C side of things does > +/// not break anything in the future. > +#[derive(Copy, Clone, Default, PartialEq, Eq)] > +pub struct PlaneCommitFlags(u32); > + > +impl From for PlaneCommitFlags { > +=C2=A0=C2=A0=C2=A0 fn from(value: PlaneCommitFlag) -> Self { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 Self(value as u32) > +=C2=A0=C2=A0=C2=A0 } > +} > + > +impl From for u32 { > +=C2=A0=C2=A0=C2=A0 fn from(value: PlaneCommitFlags) -> Self { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 value.0 > +=C2=A0=C2=A0=C2=A0 } > +} > + > +impl BitOr for PlaneCommitFlags { > +=C2=A0=C2=A0=C2=A0 type Output =3D Self; > + > +=C2=A0=C2=A0=C2=A0 fn bitor(self, rhs: Self) -> Self::Output { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 Self(self.0 | rhs.0) > +=C2=A0=C2=A0=C2=A0 } > +} > + > +impl BitOrAssign for PlaneCommitFlags { > +=C2=A0=C2=A0=C2=A0 fn bitor_assign(&mut self, rhs: Self) { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 *self =3D *self | rhs > +=C2=A0=C2=A0=C2=A0 } > +} > + > +impl BitAnd for PlaneCommitFlags { > +=C2=A0=C2=A0=C2=A0 type Output =3D PlaneCommitFlags; > + > +=C2=A0=C2=A0=C2=A0 fn bitand(self, rhs: Self) -> Self::Output { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 Self(self.0 & rhs.0) > +=C2=A0=C2=A0=C2=A0 } > +} > + > +impl BitAndAssign for PlaneCommitFlags { > +=C2=A0=C2=A0=C2=A0 fn bitand_assign(&mut self, rhs: Self) { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 *self =3D *self & rhs > +=C2=A0=C2=A0=C2=A0 } > +} > + > +impl BitOr for PlaneCommitFlags { > +=C2=A0=C2=A0=C2=A0 type Output =3D Self; > + > +=C2=A0=C2=A0=C2=A0 fn bitor(self, rhs: PlaneCommitFlag) -> Self::Output = { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 self | Self::from(rhs) > +=C2=A0=C2=A0=C2=A0 } > +} > + > +impl BitOrAssign for PlaneCommitFlags { > +=C2=A0=C2=A0=C2=A0 fn bitor_assign(&mut self, rhs: PlaneCommitFlag) { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 *self =3D *self | rhs > +=C2=A0=C2=A0=C2=A0 } > +} > + > +impl BitAnd for PlaneCommitFlags { > +=C2=A0=C2=A0=C2=A0 type Output =3D PlaneCommitFlags; > + > +=C2=A0=C2=A0=C2=A0 fn bitand(self, rhs: PlaneCommitFlag) -> Self::Output= { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 self & Self::from(rhs) > +=C2=A0=C2=A0=C2=A0 } > +} > + > +impl BitAndAssign for PlaneCommitFlags { > +=C2=A0=C2=A0=C2=A0 fn bitand_assign(&mut self, rhs: PlaneCommitFlag) { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 *self =3D *self & rhs > +=C2=A0=C2=A0=C2=A0 } > +} > + > +impl PlaneCommitFlags { > +=C2=A0=C2=A0=C2=A0 /// Create a new bitmask. > +=C2=A0=C2=A0=C2=A0 pub fn new() -> Self { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 Self::default() > +=C2=A0=C2=A0=C2=A0 } > + > +=C2=A0=C2=A0=C2=A0 /// Check if the bitmask has the given commit flag se= t. > +=C2=A0=C2=A0=C2=A0 pub fn has(&self, flag: PlaneCommitFlag) -> bool { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 *self & flag =3D=3D flag.into= () > +=C2=A0=C2=A0=C2=A0 } > +} > diff --git a/rust/kernel/drm/kms/connector.rs > b/rust/kernel/drm/kms/connector.rs > new file mode 100644 > index 000000000000..05ec64cf6fa2 > --- /dev/null > +++ b/rust/kernel/drm/kms/connector.rs > @@ -0,0 +1,997 @@ > +// SPDX-License-Identifier: GPL-2.0 OR MIT > + > +//! DRM display connectors. > +//! > +//! C header: > [`include/drm/drm_connector.h`](srctree/include/drm/drm_connector.h) > + > +use super::{ > +=C2=A0=C2=A0=C2=A0 atomic::*, encoder::*, KmsDriver, ModeConfigGuard, Mo= deObject, > ModeObjectVtable, Sealed > +}; > +use crate::{ > +=C2=A0=C2=A0=C2=A0 alloc::KBox, > +=C2=A0=C2=A0=C2=A0 bindings, > +=C2=A0=C2=A0=C2=A0 drm::{device::Device, kms::{NewKmsDevice, Probing}}, > +=C2=A0=C2=A0=C2=A0 error::to_result, > +=C2=A0=C2=A0=C2=A0 prelude::*, > +=C2=A0=C2=A0=C2=A0 types::{NotThreadSafe, Opaque}, > +}; > +use core::{ > +=C2=A0=C2=A0=C2=A0 cell::Cell, > +=C2=A0=C2=A0=C2=A0 marker::*, > +=C2=A0=C2=A0=C2=A0 mem::{self, ManuallyDrop}, > +=C2=A0=C2=A0=C2=A0 ops::*, > +=C2=A0=C2=A0=C2=A0 ptr::{null_mut, NonNull}, > +=C2=A0=C2=A0=C2=A0 stringify, > +}; > +use macros::paste; > + > +/// A macro for generating our type ID enumerator. > +macro_rules! declare_conn_types { > +=C2=A0=C2=A0=C2=A0 ($( $oldname:ident as $newname:ident ),+) =3D> { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 /// An enumerator for all pos= sible [`Connector`] type IDs. > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 #[repr(i32)] > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 #[non_exhaustive] > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 #[derive(Copy, Clone, Partial= Eq, Eq)] > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 pub enum Type { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // No= te: bindgen defaults the macro values to u32 and > not i32, but DRM takes them as an > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // i3= 2 - so just do the conversion here > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 $( > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0= =C2=A0=C2=A0=C2=A0 #[doc =3D concat!("The connector type ID for a ", > stringify!($newname), " connector.")] > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0= =C2=A0=C2=A0=C2=A0 $newname =3D > paste!(crate::bindings::[]) as i32 > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 ),+, > + > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // 9P= inDIN is special because of the 9, making it an > invalid ident. Just define it here > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // ma= nually since it's the only one > + > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 /// T= he connector type ID for a 9PinDIN connector. > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 _9Pin= Din =3D crate::bindings::DRM_MODE_CONNECTOR_9PinDIN > as i32 > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 } > +=C2=A0=C2=A0=C2=A0 }; > +} > + > +declare_conn_types! { > +=C2=A0=C2=A0=C2=A0 Unknown=C2=A0=C2=A0=C2=A0=C2=A0 as Unknown, > +=C2=A0=C2=A0=C2=A0 Composite=C2=A0=C2=A0 as Composite, > +=C2=A0=C2=A0=C2=A0 Component=C2=A0=C2=A0 as Component, > +=C2=A0=C2=A0=C2=A0 DisplayPort as DisplayPort, > +=C2=A0=C2=A0=C2=A0 VGA=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 a= s Vga, > +=C2=A0=C2=A0=C2=A0 DVII=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 as Dvi= I, > +=C2=A0=C2=A0=C2=A0 DVID=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 as Dvi= D, > +=C2=A0=C2=A0=C2=A0 DVIA=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 as Dvi= A, > +=C2=A0=C2=A0=C2=A0 SVIDEO=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 as SVideo, > +=C2=A0=C2=A0=C2=A0 LVDS=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 as Lvd= s, > +=C2=A0=C2=A0=C2=A0 HDMIA=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 as HdmiA, > +=C2=A0=C2=A0=C2=A0 HDMIB=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 as HdmiB, > +=C2=A0=C2=A0=C2=A0 TV=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2= =A0 as Tv, > +=C2=A0=C2=A0=C2=A0 eDP=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 a= s Edp, > +=C2=A0=C2=A0=C2=A0 VIRTUAL=C2=A0=C2=A0=C2=A0=C2=A0 as Virtual, > +=C2=A0=C2=A0=C2=A0 DSI=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 a= s Dsi, > +=C2=A0=C2=A0=C2=A0 DPI=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 a= s Dpi, > +=C2=A0=C2=A0=C2=A0 WRITEBACK=C2=A0=C2=A0 as Writeback, > +=C2=A0=C2=A0=C2=A0 SPI=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 a= s Spi, > +=C2=A0=C2=A0=C2=A0 USB=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 a= s Usb > +} > + > +/// 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 > +/// [`Connector`] objects. Additionally, a driver may store driver- > private data within the type that > +/// implements [`DriverConnector`] - and it will be made available > when using a fully typed > +/// [`Connector`] object. > +/// > +/// # Invariants > +/// > +/// - Any C FFI callbacks generated using this trait are guaranteed > that passed-in > +///=C2=A0=C2=A0 [`struct drm_connector`] pointers are contained within a > [`Connector`]. > +/// - Any C FFI callbacks generated using this trait are guaranteed > that passed-in > +///=C2=A0=C2=A0 [`struct drm_connector_state`] pointers are contained wi= thin a > +///=C2=A0=C2=A0 [`ConnectorState`]. > +/// > +/// [`struct drm_connector`]: srctree/include/drm/drm_connector.h > +/// [`struct drm_connector_state`]: > srctree/include/drm/drm_connector.h > +#[vtable] > +pub trait DriverConnector: Send + Sync + Sized { > +=C2=A0=C2=A0=C2=A0 /// The generated C vtable for this [`DriverConnector= `] > implementation > +=C2=A0=C2=A0=C2=A0 const OPS: &'static DriverConnectorOps =3D &DriverCon= nectorOps { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 funcs: bindings::drm_connecto= r_funcs { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 dpms:= None, > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 atomi= c_get_property: None, > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 atomi= c_set_property: None, > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 early= _unregister: None, > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 late_= register: None, > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 set_p= roperty: None, > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 reset= : Some(connector_reset_callback::), > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 atomi= c_print_state: None, > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 atomi= c_destroy_state: > Some(atomic_destroy_state_callback::), > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 destr= oy: Some(connector_destroy_callback::), > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 force= : None, > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 detec= t: None, > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 fill_= modes: > Some(bindings::drm_helper_probe_single_connector_modes), > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 debug= fs_init: None, > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 oob_h= otplug_event: None, > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 atomi= c_duplicate_state: > Some(atomic_duplicate_state_callback::), > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 }, > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 helper_funcs: bindings::drm_c= onnector_helper_funcs { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 mode_= valid: None, > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 atomi= c_check: None, > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 get_m= odes: Some(get_modes_callback::), > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 detec= t_ctx: None, > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 enabl= e_hpd: None, > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 disab= le_hpd: None, > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 best_= encoder: None, > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 atomi= c_commit: None, > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 mode_= valid_ctx: None, > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 atomi= c_best_encoder: None, > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 prepa= re_writeback_job: None, > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 clean= up_writeback_job: None, > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 }, > +=C2=A0=C2=A0=C2=A0 }; > + > +=C2=A0=C2=A0=C2=A0 /// The type to pass to the `args` field of > [`UnregisteredConnector::new`]. > +=C2=A0=C2=A0=C2=A0 /// > +=C2=A0=C2=A0=C2=A0 /// This type will be made available in in the `args`= argument > of [`Self::new`]. Drivers which > +=C2=A0=C2=A0=C2=A0 /// don't need this can simply pass [`()`] here. > +=C2=A0=C2=A0=C2=A0 type Args; > + > +=C2=A0=C2=A0=C2=A0 /// The parent [`KmsDriver`] implementation. > +=C2=A0=C2=A0=C2=A0 type Driver: KmsDriver; > + > +=C2=A0=C2=A0=C2=A0 /// The [`DriverConnectorState`] implementation for t= his > [`DriverConnector`]. > +=C2=A0=C2=A0=C2=A0 /// > +=C2=A0=C2=A0=C2=A0 /// See [`DriverConnectorState`] for more info. > +=C2=A0=C2=A0=C2=A0 type State: DriverConnectorState; > + > +=C2=A0=C2=A0=C2=A0 /// The constructor for creating a [`Connector`] usin= g this > [`DriverConnector`] implementation. > +=C2=A0=C2=A0=C2=A0 /// > +=C2=A0=C2=A0=C2=A0 /// Drivers may use this to instantiate their > [`DriverConnector`] object. > +=C2=A0=C2=A0=C2=A0 fn new(device: &Device, args: Self::Arg= s) -> impl > PinInit; > + > +=C2=A0=C2=A0=C2=A0 /// Retrieve a list of available display modes for th= is > [`Connector`]. > +=C2=A0=C2=A0=C2=A0 fn get_modes<'a>( > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 connector: ConnectorGuard<'a,= Self>, > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 guard: &ModeConfigGuard<'a, S= elf::Driver>, > +=C2=A0=C2=A0=C2=A0 ) -> i32; > +} > + > +/// The generated C vtable for a [`DriverConnector`]. > +/// > +/// This type is created internally by DRM. > +pub struct DriverConnectorOps { > +=C2=A0=C2=A0=C2=A0 funcs: bindings::drm_connector_funcs, > +=C2=A0=C2=A0=C2=A0 helper_funcs: bindings::drm_connector_helper_funcs, > +} > + > +/// The main interface for a [`struct drm_connector`]. > +/// > +/// This type is the main interface for dealing with DRM connectors. > In addition, it also allows > +/// immutable access to whatever private data is contained within an > implementor's > +/// [`DriverConnector`] type. > +/// > +/// # Invariants > +/// > +/// - The DRM C API and our interface guarantees that only the user > has mutable access to `state`, > +///=C2=A0=C2=A0 up until [`drm_atomic_helper_commit_hw_done`] is called. > Therefore, `connector` follows rust's > +///=C2=A0=C2=A0 data aliasing rules and does not need to be behind an > [`Opaque`] type. > +/// - `connector` and `inner` are initialized for as long as this > object is made available to users. > +/// - The data layout of this structure begins with [`struct > drm_connector`]. > +/// - The atomic state for this type can always be assumed to be of > type > +///=C2=A0=C2=A0 [`ConnectorState`]. > +/// > +/// [`struct drm_connector`]: srctree/include/drm/drm_connector.h > +/// [`drm_atomic_helper_commit_hw_done`]: > srctree/include/drm/drm_atomic_helper.h > +#[repr(C)] > +#[pin_data] > +pub struct Connector { > +=C2=A0=C2=A0=C2=A0 connector: Opaque, > +=C2=A0=C2=A0=C2=A0 #[pin] > +=C2=A0=C2=A0=C2=A0 inner: T, > +=C2=A0=C2=A0=C2=A0 #[pin] > +=C2=A0=C2=A0=C2=A0 _p: PhantomPinned, > +} > + > +impl Sealed for Connector {} > + > +impl Deref for Connector { > +=C2=A0=C2=A0=C2=A0 type Target =3D T; > + > +=C2=A0=C2=A0=C2=A0 fn deref(&self) -> &Self::Target { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 &self.inner > +=C2=A0=C2=A0=C2=A0 } > +} > + > +impl Connector { > +=C2=A0=C2=A0=C2=A0 super::impl_from_opaque_mode_obj! { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 fn <'a, D>(&'a OpaqueConnecto= r) -> &'a Self; > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 use > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 T as = DriverConnector, > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 D as = KmsDriver > +=C2=A0=C2=A0=C2=A0 } > + > +=C2=A0=C2=A0=C2=A0 /// Acquire a [`ConnectorGuard`] for this connector f= rom a > [`ModeConfigGuard`]. > +=C2=A0=C2=A0=C2=A0 /// > +=C2=A0=C2=A0=C2=A0 /// This verifies using the provided reference that t= he given > guard is actually for the same > +=C2=A0=C2=A0=C2=A0 /// device as this connector's parent. > +=C2=A0=C2=A0=C2=A0 /// > +=C2=A0=C2=A0=C2=A0 /// # Panics > +=C2=A0=C2=A0=C2=A0 /// > +=C2=A0=C2=A0=C2=A0 /// Panics if `guard` is not a [`ModeConfigGuard`] fo= r this > connector's parent [`Device`]. > +=C2=A0=C2=A0=C2=A0 pub fn guard<'a>(&'a self, guard: &ModeConfigGuard<'a= , > T::Driver>) -> ConnectorGuard<'a, T> { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 guard.assert_owner(self.drm_d= ev()); > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 ConnectorGuard(self) > +=C2=A0=C2=A0=C2=A0 } > +} > + > +/// A trait implemented by any type that acts as a [`struct > drm_connector`] interface. > +/// > +/// This is implemented internally by DRM. > +/// > +/// # Safety > +/// > +/// [`as_raw()`] must always return a pointer to a valid initialized > [`struct drm_connector`]. > +/// > +/// [`as_raw()`]: AsRawConnector::as_raw() > +/// [`struct drm_connector`]: srctree/include/drm/drm_connector.h > +pub unsafe trait AsRawConnector { > +=C2=A0=C2=A0=C2=A0 /// Return the raw [`struct drm_connector`] for this = DRM > connector. > +=C2=A0=C2=A0=C2=A0 /// > +=C2=A0=C2=A0=C2=A0 /// Drivers should never use this directly > +=C2=A0=C2=A0=C2=A0 /// > +=C2=A0=C2=A0=C2=A0 /// [`struct drm_Connector`]: > srctree/include/drm/drm_connector.h > +=C2=A0=C2=A0=C2=A0 fn as_raw(&self) -> *mut bindings::drm_connector; > + > +=C2=A0=C2=A0=C2=A0 /// Convert a raw `bindings::drm_connector` pointer i= nto an > object of this type. > +=C2=A0=C2=A0=C2=A0 /// > +=C2=A0=C2=A0=C2=A0 /// # Safety > +=C2=A0=C2=A0=C2=A0 /// > +=C2=A0=C2=A0=C2=A0 /// Callers promise that `ptr` points to a valid inst= ance of > this type. > +=C2=A0=C2=A0=C2=A0 unsafe fn from_raw<'a>(ptr: *mut bindings::drm_connec= tor) -> &'a > Self; > +} > + > +/// A supertrait of [`AsRawConnector`] for [`struct drm_connector`] > interfaces that can perform > +/// modesets. > +/// > +/// This is implemented internally by DRM. > +/// > +/// # Safety > +/// > +/// Any object implementing this trait must only be made directly > available to the user after > +/// [`create_objects`] has completed. > +/// > +/// [`struct drm_connector`]: srctree/include/drm/drm_connector.h > +/// [`create_objects`]: KmsDriver::create_objects > +pub unsafe trait ModesettableConnector: AsRawConnector { > +=C2=A0=C2=A0=C2=A0 /// The type that should be returned for a plane stat= e acquired > using this plane interface > +=C2=A0=C2=A0=C2=A0 type State: FromRawConnectorState; > +} > + > +// SAFETY: Our connector interfaces are guaranteed to be thread-safe > +unsafe impl Send for Connector {} > + > +// SAFETY: Our connector interfaces are guaranteed to be thread-safe > +unsafe impl Sync for Connector {} > + > +// SAFETY: We don't expose Connector to users before `base` is > initialized in ::new(), so > +// `raw_mode_obj` always returns a valid pointer to a > bindings::drm_mode_object. > +unsafe impl ModeObject for Connector { > +=C2=A0=C2=A0=C2=A0 type Driver =3D T::Driver; > + > +=C2=A0=C2=A0=C2=A0 fn drm_dev(&self) -> &Device { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // SAFETY: The parent device = for a DRM connector will never > outlive the connector, and this > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // pointer is invariant throu= gh the lifetime of the > connector > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 unsafe { Device::from_raw((*s= elf.as_raw()).dev) } > +=C2=A0=C2=A0=C2=A0 } > + > +=C2=A0=C2=A0=C2=A0 fn raw_mode_obj(&self) -> *mut bindings::drm_mode_obj= ect { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // SAFETY: We don't expose DR= M connectors to users before > `base` is initialized > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 unsafe { &raw mut (*self.as_r= aw()).base } > +=C2=A0=C2=A0=C2=A0 } > +} > + > +// Connectors are refcounted objects. > +super::impl_aref_for_mode_object! { > +=C2=A0=C2=A0=C2=A0 impl for Connector > +} > + > +// SAFETY: `funcs` is initialized by DRM when the connector is > allocated > +unsafe impl ModeObjectVtable for Connector { > +=C2=A0=C2=A0=C2=A0 type Vtable =3D bindings::drm_connector_funcs; > + > +=C2=A0=C2=A0=C2=A0 fn vtable(&self) -> *const Self::Vtable { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // SAFETY: `funcs` is initial= ized by DRM when the connector > is allocated > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 unsafe { *self.as_raw() }.fun= cs > +=C2=A0=C2=A0=C2=A0 } > +} > + > +// SAFETY: > +// * Via our type variants our data layout starts with > `drm_connector` > +// * Since we don't expose `Connector` to users before it has been > initialized, this and our data > +//=C2=A0=C2=A0 layout ensure that `as_raw()` always returns a valid poin= ter to > a `drm_connector`. > +unsafe impl AsRawConnector for Connector { > +=C2=A0=C2=A0=C2=A0 fn as_raw(&self) -> *mut bindings::drm_connector { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 self.connector.get() > +=C2=A0=C2=A0=C2=A0 } > + > +=C2=A0=C2=A0=C2=A0 unsafe fn from_raw<'a>(ptr: *mut bindings::drm_connec= tor) -> &'a > Self { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // SAFETY: Our data layout st= arts with > `bindings::drm_connector` > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 unsafe { &*ptr.cast() } > +=C2=A0=C2=A0=C2=A0 } > +} > + > +// SAFETY: We only expose this object to users directly after > KmsDriver::create_objects has been > +// called. > +unsafe impl ModesettableConnector for > Connector { > +=C2=A0=C2=A0=C2=A0 type State =3D ConnectorState; > +} > + > +/// A [`Connector`] that has not yet been registered with userspace. > +/// > +/// KMS registration is single-threaded, so this object is not > thread-safe. > +/// > +/// # Invariants > +/// > +/// - This object can only exist before its respective KMS device > has been registered. > +/// - Otherwise, it inherits all invariants of [`Connector`] and has > an identical data layout. > +pub struct UnregisteredConnector(Connector, > NotThreadSafe); > + > +// SAFETY: We share the invariants of `Connector` > +unsafe impl AsRawConnector for > UnregisteredConnector { > +=C2=A0=C2=A0=C2=A0 fn as_raw(&self) -> *mut bindings::drm_connector { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 self.0.as_raw() > +=C2=A0=C2=A0=C2=A0 } > + > +=C2=A0=C2=A0=C2=A0 unsafe fn from_raw<'a>(ptr: *mut bindings::drm_connec= tor) -> &'a > Self { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // SAFETY: This is another fr= om_raw() call, so this function > shares the same safety contract > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 let connector =3D unsafe { Co= nnector::::from_raw(ptr) }; > + > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // SAFETY: Our data layout is= identical via our type > invariants. > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 unsafe { mem::transmute(conne= ctor) } > +=C2=A0=C2=A0=C2=A0 } > +} > + > +impl Deref for UnregisteredConnector { > +=C2=A0=C2=A0=C2=A0 type Target =3D T; > + > +=C2=A0=C2=A0=C2=A0 fn deref(&self) -> &Self::Target { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 &self.0.inner > +=C2=A0=C2=A0=C2=A0 } > +} > + > +impl UnregisteredConnector { > +=C2=A0=C2=A0=C2=A0 /// Construct a new [`UnregisteredConnector`]. > +=C2=A0=C2=A0=C2=A0 /// > +=C2=A0=C2=A0=C2=A0 /// A driver may use this to create new > [`UnregisteredConnector`] objects. > +=C2=A0=C2=A0=C2=A0 /// > +=C2=A0=C2=A0=C2=A0 /// [`KmsDriver::create_objects`]: > kernel::drm::kms::KmsDriver::create_objects > +=C2=A0=C2=A0=C2=A0 pub fn new<'a>( > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 dev: &'a NewKmsDevice<'a, T::= Driver, Probing>, > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 type_: Type, > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 args: T::Args, > +=C2=A0=C2=A0=C2=A0 ) -> Result<&'a Self> { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 let new: Pin>> =3D KBox::try_pin_init( > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 try_p= in_init!(Connector { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0= =C2=A0=C2=A0=C2=A0 connector: Opaque::new(bindings::drm_connector { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0= =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 helper_private: &T::OPS.helper_f= uncs, > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0= =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 ..Default::default() > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0= =C2=A0=C2=A0=C2=A0 }), > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0= =C2=A0=C2=A0=C2=A0 inner <- T::new(dev, args), > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0= =C2=A0=C2=A0=C2=A0 _p: PhantomPinned, > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 }), > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 GFP_K= ERNEL, > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 )?; > + > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // SAFETY: > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // - `dev` will hold a refere= nce to the new connector, and > thus outlives us. > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // - We just allocated `new` = above > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // - `new` starts with `drm_c= onnector` via its type > invariants. > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 to_result(unsafe { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 bindi= ngs::drm_connector_init(dev.as_raw(), new.as_raw(), > &T::OPS.funcs, type_ as i32) > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 })?; > + > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // SAFETY: We don't move anyt= hing > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 let this =3D unsafe { Pin::in= to_inner_unchecked(new) }; > + > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // We'll re-assemble the box = in connector_destroy_callback() > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 let this =3D KBox::into_raw(t= his); > + > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // UnregisteredConnector has = an equivalent data layout > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 let this: *mut Self =3D this.= cast(); > + > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // SAFETY: We just allocated = the connector above, so this > pointer must be valid > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 Ok(unsafe { &*this }) > +=C2=A0=C2=A0=C2=A0 } > + > +=C2=A0=C2=A0=C2=A0 /// Attach an encoder to this [`Connector`]. > +=C2=A0=C2=A0=C2=A0 #[must_use] > +=C2=A0=C2=A0=C2=A0 pub fn attach_encoder(&self, encoder: &impl AsRawEnco= der) -> > Result { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // SAFETY: > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // - Both as_raw() calls are = guaranteed to return a valid > pointer > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // - We're guaranteed this co= nnector is not registered via > our type invariants, thus this > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 //=C2=A0=C2=A0 function is sa= fe to call > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 to_result(unsafe { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 bindi= ngs::drm_connector_attach_encoder(self.as_raw(), > encoder.as_raw()) > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 }) > +=C2=A0=C2=A0=C2=A0 } > +} > + > +/// Common methods available on any type which implements > [`AsRawConnector`]. > +/// > +/// This is implemented internally by DRM, and provides many of the > basic methods for working with > +/// connectors. > +pub trait RawConnector: AsRawConnector { > +=C2=A0=C2=A0=C2=A0 /// Return the index of this DRM connector > +=C2=A0=C2=A0=C2=A0 #[inline] > +=C2=A0=C2=A0=C2=A0 fn index(&self) -> u32 { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // SAFETY: The index is initi= alized by the time we expose > DRM connector objects to users, > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // and is invariant throughou= t the lifetime of the connector > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 unsafe { (*self.as_raw()).ind= ex } > +=C2=A0=C2=A0=C2=A0 } > + > +=C2=A0=C2=A0=C2=A0 /// Return the bitmask derived from this DRM connecto= r's index > +=C2=A0=C2=A0=C2=A0 #[inline] > +=C2=A0=C2=A0=C2=A0 fn mask(&self) -> u32 { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 1 << self.index() > +=C2=A0=C2=A0=C2=A0 } > +} > +impl RawConnector for T {} > + > +unsafe extern "C" fn connector_destroy_callback( > +=C2=A0=C2=A0=C2=A0 connector: *mut bindings::drm_connector, > +) { > +=C2=A0=C2=A0=C2=A0 // SAFETY: DRM guarantees that `connector` points to = a valid > initialized `drm_connector`. > +=C2=A0=C2=A0=C2=A0 unsafe { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 bindings::drm_connector_unreg= ister(connector); > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 bindings::drm_connector_clean= up(connector); > +=C2=A0=C2=A0=C2=A0 }; > + > +=C2=A0=C2=A0=C2=A0 // SAFETY: > +=C2=A0=C2=A0=C2=A0 // - We originally created the connector in a `Box` > +=C2=A0=C2=A0=C2=A0 // - We are guaranteed to hold the last remaining ref= erence to > this connector > +=C2=A0=C2=A0=C2=A0 // - This cast is safe via `DriverConnector`s type in= variants. > +=C2=A0=C2=A0=C2=A0 drop(unsafe { KBox::from_raw(connector as *mut Connec= tor) }); > +} > + > +unsafe extern "C" fn get_modes_callback( > +=C2=A0=C2=A0=C2=A0 connector: *mut bindings::drm_connector, > +) -> core::ffi::c_int { > +=C2=A0=C2=A0=C2=A0 // SAFETY: This is safe via `DriverConnector`s type i= nvariants. > +=C2=A0=C2=A0=C2=A0 let connector =3D unsafe { Connector::::from_raw(c= onnector) }; > + > +=C2=A0=C2=A0=C2=A0 // SAFETY: This FFI callback is only called while > `mode_config.lock` is held > +=C2=A0=C2=A0=C2=A0 // We use ManuallyDrop here to prevent the lock from = being > released after the callback > +=C2=A0=C2=A0=C2=A0 // completes, as that should be handled by DRM. > +=C2=A0=C2=A0=C2=A0 let guard =3D ManuallyDrop::new(unsafe { > ModeConfigGuard::new(connector.drm_dev()) }); > + > +=C2=A0=C2=A0=C2=A0 T::get_modes(connector.guard(&guard), &guard) > +} > + > +/// A [`struct drm_connector`] without a known [`DriverConnector`] > implementation. > +/// > +/// This is mainly for situations where our bindings can't infer the > [`DriverConnector`] > +/// implementation for a [`struct drm_connector`] automatically. It > is identical to [`Connector`], > +/// except that it does not provide access to the driver's private > data. > +/// > +/// # Invariants > +/// > +/// - `connector` is initialized for as long as this object is > exposed to users. > +/// - The data layout of this type is equivalent to [`struct > drm_connector`]. > +/// > +/// [`struct drm_connector`]: srctree/include/drm/drm_connector.h > +#[repr(transparent)] > +pub struct OpaqueConnector { > +=C2=A0=C2=A0=C2=A0 connector: Opaque, > +=C2=A0=C2=A0=C2=A0 _p: PhantomData, > +} > + > +impl Sealed for OpaqueConnector {} > + > +// SAFETY: > +// - Via our type variants our data layout starts is identical to > `drm_connector` > +// - Since we don't expose `OpaqueConnector` to users before it has > been initialized, this and our > +//=C2=A0=C2=A0 data layout ensure that `as_raw()` always returns a valid > pointer to a `drm_connector`. > +unsafe impl AsRawConnector for OpaqueConnector { > +=C2=A0=C2=A0=C2=A0 fn as_raw(&self) -> *mut bindings::drm_connector { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 self.connector.get() > +=C2=A0=C2=A0=C2=A0 } > + > +=C2=A0=C2=A0=C2=A0 unsafe fn from_raw<'a>(ptr: *mut bindings::drm_connec= tor) -> &'a > Self { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // SAFETY: Our data layout is= identical to > `bindings::drm_connector` > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 unsafe { &*ptr.cast() } > +=C2=A0=C2=A0=C2=A0 } > +} > + > +// SAFETY: We only expose this object to users directly after > KmsDriver::create_objects has been > +// called. > +unsafe impl ModesettableConnector for > OpaqueConnector { > +=C2=A0=C2=A0=C2=A0 type State =3D OpaqueConnectorState; > +} > + > +// SAFETY: We don't expose OpaqueConnector to users before `base` > is initialized in > +// Connector::new(), so `raw_mode_obj` always returns a valid > pointer to a bindings::drm_mode_object. > +unsafe impl ModeObject for OpaqueConnector { > +=C2=A0=C2=A0=C2=A0 type Driver =3D T; > + > +=C2=A0=C2=A0=C2=A0 fn drm_dev(&self) -> &Device { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // SAFETY: The parent device = for a DRM connector will never > outlive the connector, and this > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // pointer is invariant throu= gh the lifetime of the > connector > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 unsafe { Device::from_raw((*s= elf.as_raw()).dev) } > +=C2=A0=C2=A0=C2=A0 } > + > +=C2=A0=C2=A0=C2=A0 fn raw_mode_obj(&self) -> *mut bindings::drm_mode_obj= ect { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // SAFETY: We don't expose DR= M connectors to users before > `base` is initialized > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 unsafe { &mut (*self.as_raw()= ).base } > +=C2=A0=C2=A0=C2=A0 } > +} > + > +super::impl_aref_for_mode_object! { > +=C2=A0=C2=A0=C2=A0 impl for OpaqueConnector > +} > + > +// SAFETY: `funcs` is initialized by DRM when the connector is > allocated > +unsafe impl ModeObjectVtable for OpaqueConnector { > +=C2=A0=C2=A0=C2=A0 type Vtable =3D bindings::drm_connector_funcs; > + > +=C2=A0=C2=A0=C2=A0 fn vtable(&self) -> *const Self::Vtable { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // SAFETY: `funcs` is initial= ized by DRM when the connector > is allocated > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 unsafe { *self.as_raw() }.fun= cs > +=C2=A0=C2=A0=C2=A0 } > +} > + > +// SAFETY: Our connector interfaces are guaranteed to be thread-safe > +unsafe impl Send for OpaqueConnector {} > +unsafe impl Sync for OpaqueConnector {} > + > +/// A privileged [`Connector`] obtained while holding a > [`ModeConfigGuard`]. > +/// > +/// This provides access to various methods for [`Connector`] that > must happen under lock, such as > +/// setting resolution preferences and adding display modes. > +/// > +/// # Invariants > +/// > +/// Shares the invariants of [`ModeConfigGuard`]. > +#[derive(Copy, Clone)] > +pub struct ConnectorGuard<'a, T: DriverConnector>(&'a Connector); > + > +impl Deref for ConnectorGuard<'_, T> { > +=C2=A0=C2=A0=C2=A0 type Target =3D Connector; > + > +=C2=A0=C2=A0=C2=A0 fn deref(&self) -> &Self::Target { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 self.0 > +=C2=A0=C2=A0=C2=A0 } > +} > + > +impl<'a, T: DriverConnector> ConnectorGuard<'a, T> { > +=C2=A0=C2=A0=C2=A0 /// Add modes for a [`ConnectorGuard`] without an EDI= D. > +=C2=A0=C2=A0=C2=A0 /// > +=C2=A0=C2=A0=C2=A0 /// Add the specified modes to the connector's mode l= ist up to > the given maximum resultion. > +=C2=A0=C2=A0=C2=A0 /// Returns how many modes were added. > +=C2=A0=C2=A0=C2=A0 pub fn add_modes_noedid(&self, (max_h, max_v): (u32, = u32)) -> > i32 { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // SAFETY: We hold the locks = required to call this via our > type invariants. > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 unsafe { bindings::drm_add_mo= des_noedid(self.as_raw(), > max_h, max_v) } > +=C2=A0=C2=A0=C2=A0 } > + > +=C2=A0=C2=A0=C2=A0 /// Set the preferred display mode for the underlying > [`Connector`]. > +=C2=A0=C2=A0=C2=A0 pub fn set_preferred_mode(&self, (h_pref, w_pref): (u= 32, u32)) { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // SAFETY: We hold the locks = required to call this via our > type invariants. > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 unsafe { bindings::drm_set_pr= eferred_mode(self.as_raw(), > h_pref, w_pref) } > +=C2=A0=C2=A0=C2=A0 } > +} > + > +/// A trait implemented by any type which can produce a reference to > a > +/// [`struct drm_connector_state`]. > +/// > +/// This is implemented internally by DRM. > +/// > +/// [`struct drm_connector_state`]: > srctree/include/drm/drm_connector.h > +pub trait AsRawConnectorState: private::AsRawConnectorState { > +=C2=A0=C2=A0=C2=A0 /// The type that represents this connector state's D= RM > connector. > +=C2=A0=C2=A0=C2=A0 type Connector: AsRawConnector; > +} > + > +pub(super) mod private { > +=C2=A0=C2=A0=C2=A0 use super::*; > + > +=C2=A0=C2=A0=C2=A0 /// Trait for retrieving references to the base conne= ctor state > contained within any connector > +=C2=A0=C2=A0=C2=A0 /// state compatible type > +=C2=A0=C2=A0=C2=A0 #[allow(unreachable_pub)] > +=C2=A0=C2=A0=C2=A0 pub trait AsRawConnectorState { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 /// Return an immutable refer= ence to the raw connector > state. > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 fn as_raw(&self) -> &bindings= ::drm_connector_state; > + > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 /// Get a mutable reference t= o the raw [`struct > drm_connector_state`] contained within this > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 /// type. > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 /// > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 /// > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 /// # Safety > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 /// > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 /// The caller promises this = mutable reference will not be > used to modify any contents of > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 /// [`struct drm_connector_st= ate`] which DRM would consider > to be static - like the > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 /// backpointer to the DRM co= nnector that owns this state. > This also means the mutable > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 /// reference should never be= exposed outside of this crate. > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 /// > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 /// [`struct drm_connector_st= ate`]: > srctree/include/drm/drm_connector.h > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 unsafe fn as_raw_mut(&mut sel= f) -> &mut > bindings::drm_connector_state; > +=C2=A0=C2=A0=C2=A0 } > +} > + > +pub(super) use private::AsRawConnectorState as > AsRawConnectorStatePrivate; > + > +/// A trait implemented for any type which can be constructed > directly from a > +/// [`struct drm_connector_state`] pointer. > +/// > +/// This is implemented internally by DRM. > +/// > +/// [`struct drm_connector_state`]: > srctree/include/drm/drm_connector.h > +pub trait FromRawConnectorState: AsRawConnectorState { > +=C2=A0=C2=A0=C2=A0 /// Get an immutable reference to this type from the = given raw > [`struct drm_connector_state`] > +=C2=A0=C2=A0=C2=A0 /// pointer. > +=C2=A0=C2=A0=C2=A0 /// > +=C2=A0=C2=A0=C2=A0 /// # Safety > +=C2=A0=C2=A0=C2=A0 /// > +=C2=A0=C2=A0=C2=A0 /// - The caller guarantees `ptr` is contained within= a valid > instance of `Self`. > +=C2=A0=C2=A0=C2=A0 /// - The caller guarantees that `ptr` cannot not be = modified > for the lifetime of `'a`. > +=C2=A0=C2=A0=C2=A0 /// > +=C2=A0=C2=A0=C2=A0 /// [`struct drm_connector_state`]: > srctree/include/drm/drm_connector.h > +=C2=A0=C2=A0=C2=A0 unsafe fn from_raw<'a>(ptr: *const > bindings::drm_connector_state) -> &'a Self; > + > +=C2=A0=C2=A0=C2=A0 /// Get a mutable reference to this type from the giv= en raw > [`struct drm_connector_state`] > +=C2=A0=C2=A0=C2=A0 /// pointer. > +=C2=A0=C2=A0=C2=A0 /// > +=C2=A0=C2=A0=C2=A0 /// # Safety > +=C2=A0=C2=A0=C2=A0 /// > +=C2=A0=C2=A0=C2=A0 /// - The caller guarantees that `ptr` is contained w= ithin a > valid instance of `Self`. > +=C2=A0=C2=A0=C2=A0 /// - The caller guarantees that `ptr` cannot have an= y other > references taken out for the > +=C2=A0=C2=A0=C2=A0 ///=C2=A0=C2=A0 lifetime of `'a`. > +=C2=A0=C2=A0=C2=A0 /// > +=C2=A0=C2=A0=C2=A0 /// [`struct drm_connector_state`]: > srctree/include/drm/drm_connector.h > +=C2=A0=C2=A0=C2=A0 unsafe fn from_raw_mut<'a>(ptr: *mut > bindings::drm_connector_state) -> &'a mut Self; > +} > + > +/// Common methods available on any type which implements > [`AsRawConnectorState`]. > +/// > +/// This is implemented internally by DRM, and provides many of the > basic methods for working with > +/// the atomic state of [`Connector`]s. > +pub trait RawConnectorState: AsRawConnectorState { > +=C2=A0=C2=A0=C2=A0 /// Return the connector that this atomic state belon= gs to. > +=C2=A0=C2=A0=C2=A0 fn connector(&self) -> &Self::Connector { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // SAFETY: This is guaranteed= safe by type invariance, and > we're guaranteed by DRM that > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // `self.state.connector` poi= nts to a valid instance of a > `Connector` > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 unsafe { > Self::Connector::from_raw((*self.as_raw()).connector) } > +=C2=A0=C2=A0=C2=A0 } > +} > +impl RawConnectorState for T {} > + > +/// The main interface for a [`struct drm_connector_state`]. > +/// > +/// This type is the main interface for dealing with the atomic > state of DRM connectors. In > +/// addition, it allows access to whatever private data is contained > within an implementor's > +/// [`DriverConnectorState`] type. > +/// > +/// # Invariants > +/// > +/// - The DRM C API and our interface guarantees that only the user > has mutable access to `state`, > +///=C2=A0=C2=A0 up until [`drm_atomic_helper_commit_hw_done`] is called. > Therefore, `connector` follows rust's > +///=C2=A0=C2=A0 data aliasing rules and does not need to be behind an > [`Opaque`] type. > +/// - `state` and `inner` initialized for as long as this object is > exposed to users. > +/// - The data layout of this structure begins with [`struct > drm_connector_state`]. > +/// - The connector for this atomic state can always be assumed to > be of type > +///=C2=A0=C2=A0 [`Connector`]. > +/// > +/// [`struct drm_connector_state`]: > srctree/include/drm/drm_connector.h > +/// [`drm_atomic_helper_commit_hw_done`]: > srctree/include/drm/drm_atomic_helper.h > +#[derive(Default)] > +#[repr(C)] > +pub struct ConnectorState { > +=C2=A0=C2=A0=C2=A0 state: bindings::drm_connector_state, > +=C2=A0=C2=A0=C2=A0 inner: T, > +} > + > +/// The main trait for implementing the [`struct > drm_connector_state`] API for a [`Connector`]. > +/// > +/// A driver may store driver-private data within the implementor's > type, which will be available > +/// when using a full typed [`ConnectorState`] object. > +/// > +/// # Invariants > +/// > +/// - Any C FFI callbacks generated using this trait are guaranteed > that passed-in > +///=C2=A0=C2=A0 [`struct drm_connector`] pointers are contained within a > [`Connector`]. > +/// - Any C FFI callbacks generated using this trait are guaranteed > that passed-in > +///=C2=A0=C2=A0 [`struct drm_connector_state`] pointers are contained wi= thin a > [`ConnectorState`]. > +/// > +/// [`struct drm_connector`]: srctree/include/drm_connector.h > +/// [`struct drm_connector_state`]: srctree/include/drm_connector.h > +pub trait DriverConnectorState: Clone + Default + Sized { > +=C2=A0=C2=A0=C2=A0 /// The parent [`DriverConnector`]. > +=C2=A0=C2=A0=C2=A0 type Connector: DriverConnector; > +} > + > +impl Sealed for ConnectorState {} > + > +impl AsRawConnectorState for > ConnectorState { > +=C2=A0=C2=A0=C2=A0 type Connector =3D Connector; > +} > + > +impl private::AsRawConnectorState for > ConnectorState { > +=C2=A0=C2=A0=C2=A0 fn as_raw(&self) -> &bindings::drm_connector_state { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 &self.state > +=C2=A0=C2=A0=C2=A0 } > + > +=C2=A0=C2=A0=C2=A0 unsafe fn as_raw_mut(&mut self) -> &mut > bindings::drm_connector_state { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 &mut self.state > +=C2=A0=C2=A0=C2=A0 } > +} > + > +impl FromRawConnectorState for > ConnectorState { > +=C2=A0=C2=A0=C2=A0 unsafe fn from_raw<'a>(ptr: *const > bindings::drm_connector_state) -> &'a Self { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // Our data layout starts wit= h > `bindings::drm_connector_state`. > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 let ptr: *const Self =3D ptr.= cast(); > + > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // SAFETY: > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // - Our safety contract requ= ires that `ptr` be contained > within `Self`. > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // - Our safety contract requ= ires the caller ensure that it > is safe for us to take an > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 //=C2=A0=C2=A0 immutable refe= rence. > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 unsafe { &*ptr } > +=C2=A0=C2=A0=C2=A0 } > + > +=C2=A0=C2=A0=C2=A0 unsafe fn from_raw_mut<'a>(ptr: *mut > bindings::drm_connector_state) -> &'a mut Self { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // Our data layout starts wit= h > `bindings::drm_connector_state`. > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 let ptr: *mut Self =3D ptr.ca= st(); > + > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // SAFETY: > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // - Our safety contract requ= ires that `ptr` be contained > within `Self`. > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // - Our safety contract requ= ires the caller ensure it is > safe for us to take a mutable > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 //=C2=A0=C2=A0 reference. > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 unsafe { &mut *ptr } > +=C2=A0=C2=A0=C2=A0 } > +} > + > +// SAFETY: `funcs` is initialized by DRM when the connector is > allocated > +unsafe impl ModeObjectVtable for > ConnectorState { > +=C2=A0=C2=A0=C2=A0 type Vtable =3D bindings::drm_connector_funcs; > + > +=C2=A0=C2=A0=C2=A0 fn vtable(&self) -> *const Self::Vtable { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 self.connector().vtable() > +=C2=A0=C2=A0=C2=A0 } > +} > + > +impl ConnectorState { > +=C2=A0=C2=A0=C2=A0 super::impl_from_opaque_mode_obj! { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 fn <'a, D, C>(&'a OpaqueConne= ctorState) -> &'a Self > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 where > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 T: Dr= iverConnectorState; > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 use > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 C as = DriverConnector, > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 D as = KmsDriver > +=C2=A0=C2=A0=C2=A0 } > +} > + > +/// A [`struct drm_connector_state`] without a known > [`DriverConnectorState`] implementation. > +/// > +/// This is mainly for situations where our bindings can't infer the > [`DriverConnectorState`] > +/// implementation for a [`struct drm_connector_state`] > automatically. It is identical to > +/// [`Connector`], except that it does not provide access to the > driver's private data. > +/// > +/// # Invariants > +/// > +/// - `state` is initialized for as long as this object is exposed > to users. > +/// - The data layout of this type is identical to [`struct > drm_connector_state`]. > +/// - The DRM C API and our interface guarantees that only the user > has mutable access to `state`, > +///=C2=A0=C2=A0 up until [`drm_atomic_helper_commit_hw_done`] is called. > Therefore, `connector` follows rust's > +///=C2=A0=C2=A0 data aliasing rules and does not need to be behind an > [`Opaque`] type. > +/// > +/// [`struct drm_connector_state`]: > srctree/include/drm/drm_connector.h > +/// [`drm_atomic_helper_commit_hw_done`]: > srctree/include/drm/drm_atomic_helper.h > +#[repr(transparent)] > +pub struct OpaqueConnectorState { > +=C2=A0=C2=A0=C2=A0 state: bindings::drm_connector_state, > +=C2=A0=C2=A0=C2=A0 _p: PhantomData, > +} > + > +impl AsRawConnectorState for OpaqueConnectorState { > +=C2=A0=C2=A0=C2=A0 type Connector =3D OpaqueConnector; > +} > + > +impl private::AsRawConnectorState for > OpaqueConnectorState { > +=C2=A0=C2=A0=C2=A0 fn as_raw(&self) -> &bindings::drm_connector_state { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 &self.state > +=C2=A0=C2=A0=C2=A0 } > + > +=C2=A0=C2=A0=C2=A0 unsafe fn as_raw_mut(&mut self) -> &mut > bindings::drm_connector_state { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 &mut self.state > +=C2=A0=C2=A0=C2=A0 } > +} > + > +impl FromRawConnectorState for OpaqueConnectorState > { > +=C2=A0=C2=A0=C2=A0 unsafe fn from_raw<'a>(ptr: *const > bindings::drm_connector_state) -> &'a Self { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // SAFETY: Our data layout is= identical to > `bindings::drm_connector_state` > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 unsafe { &*ptr.cast() } > +=C2=A0=C2=A0=C2=A0 } > + > +=C2=A0=C2=A0=C2=A0 unsafe fn from_raw_mut<'a>(ptr: *mut > bindings::drm_connector_state) -> &'a mut Self { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // SAFETY: Our data layout is= identical to > `bindings::drm_connector_state` > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 unsafe { &mut *ptr.cast() } > +=C2=A0=C2=A0=C2=A0 } > +} > + > +// SAFETY: See OpaqueConnector's ModeObjectVtable implementation > +unsafe impl ModeObjectVtable for > OpaqueConnectorState { > +=C2=A0=C2=A0=C2=A0 type Vtable =3D bindings::drm_connector_funcs; > + > +=C2=A0=C2=A0=C2=A0 fn vtable(&self) -> *const Self::Vtable { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 self.connector().vtable() > +=C2=A0=C2=A0=C2=A0 } > +} > + > +/// An interface for mutating a [`Connector`]s atomic state. > +/// > +/// This type is typically returned by an [`AtomicStateMutator`] > within contexts where it is > +/// possible to safely mutate a connector's state. In order to > uphold rust's data-aliasing rules, > +/// only [`ConnectorStateMutator`] may exist at a time. > +pub struct ConnectorStateMutator<'a, T: FromRawConnectorState> { > +=C2=A0=C2=A0=C2=A0 state: &'a mut T, > +=C2=A0=C2=A0=C2=A0 mask: &'a Cell, > +} > + > +impl<'a, T: FromRawConnectorState> ConnectorStateMutator<'a, T> { > +=C2=A0=C2=A0=C2=A0 pub(super) fn new( > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 mutator: &'a AtomicStateMutat= or, > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 state: NonNull, > +=C2=A0=C2=A0=C2=A0 ) -> Option { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // SAFETY: > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // - `connector` is invariant= throughout the lifetime of the > atomic state. > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // - `state` is initialized b= y the time it is passed to this > function. > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // - We're guaranteed that `s= tate` is compatible with > `drm_connector` by type invariants. > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 let connector =3D unsafe { > T::Connector::from_raw((*state.as_ptr()).connector) }; > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 let conn_mask =3D connector.m= ask(); > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 let borrowed_mask =3D mutator= .borrowed_connectors.get(); > + > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 if borrowed_mask & conn_mask = =3D=3D 0 { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 mutat= or.borrowed_connectors.set(borrowed_mask | > conn_mask); > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 Some(= Self { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0= =C2=A0=C2=A0=C2=A0 mask: &mutator.borrowed_connectors, > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0= =C2=A0=C2=A0=C2=A0 // SAFETY: We're guaranteed `state` is of `T` by > type invariance, and we just > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0= =C2=A0=C2=A0=C2=A0 // confirmed by checking `borrowed_connectors` that > no other mutable borrows have > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0= =C2=A0=C2=A0=C2=A0 // been taken out for `state` > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0= =C2=A0=C2=A0=C2=A0 state: unsafe { T::from_raw_mut(state.as_ptr()) }, > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 }) > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 } else { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 None > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 } > +=C2=A0=C2=A0=C2=A0 } > +} > + > +impl<'a, T: DriverConnectorState> Deref for > ConnectorStateMutator<'a, ConnectorState> { > +=C2=A0=C2=A0=C2=A0 type Target =3D T; > + > +=C2=A0=C2=A0=C2=A0 fn deref(&self) -> &Self::Target { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 &self.state.inner > +=C2=A0=C2=A0=C2=A0 } > +} > + > +impl<'a, T: DriverConnectorState> DerefMut for > ConnectorStateMutator<'a, ConnectorState> { > +=C2=A0=C2=A0=C2=A0 fn deref_mut(&mut self) -> &mut Self::Target { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 &mut self.state.inner > +=C2=A0=C2=A0=C2=A0 } > +} > + > +impl<'a, T: FromRawConnectorState> Drop for > ConnectorStateMutator<'a, T> { > +=C2=A0=C2=A0=C2=A0 fn drop(&mut self) { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 let mask =3D self.state.conne= ctor().mask(); > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 self.mask.set(self.mask.get()= & !mask); > +=C2=A0=C2=A0=C2=A0 } > +} > + > +impl<'a, T: FromRawConnectorState> AsRawConnectorState for > ConnectorStateMutator<'a, T> { > +=C2=A0=C2=A0=C2=A0 type Connector =3D T::Connector; > +} > + > +impl<'a, T: FromRawConnectorState> private::AsRawConnectorState for > ConnectorStateMutator<'a, T> { > +=C2=A0=C2=A0=C2=A0 fn as_raw(&self) -> &bindings::drm_connector_state { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 self.state.as_raw() > +=C2=A0=C2=A0=C2=A0 } > + > +=C2=A0=C2=A0=C2=A0 unsafe fn as_raw_mut(&mut self) -> &mut > bindings::drm_connector_state { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // SAFETY: We're bound by the= same safety contract as this > function > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 unsafe { self.state.as_raw_mu= t() } > +=C2=A0=C2=A0=C2=A0 } > +} > + > +// SAFETY: we inherit the safety guarantees of `T` > +unsafe impl<'a, T> ModeObjectVtable for ConnectorStateMutator<'a, T> > +where > +=C2=A0=C2=A0=C2=A0 T: FromRawConnectorState + ModeObjectVtable, > +{ > +=C2=A0=C2=A0=C2=A0 type Vtable =3D T::Vtable; > + > +=C2=A0=C2=A0=C2=A0 fn vtable(&self) -> *const Self::Vtable { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 self.state.vtable() > +=C2=A0=C2=A0=C2=A0 } > +} > + > +impl<'a, T: DriverConnectorState> ConnectorStateMutator<'a, > ConnectorState> { > +=C2=A0=C2=A0=C2=A0 super::impl_from_opaque_mode_obj! { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 fn (ConnectorStateMutat= or<'a, > OpaqueConnectorState>) -> Self > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 where > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 T: Dr= iverConnectorState; > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 use > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 C as = DriverConnector, > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 D as = KmsDriver > +=C2=A0=C2=A0=C2=A0 } > +} > + > +unsafe extern "C" fn atomic_duplicate_state_callback DriverConnectorState>( > +=C2=A0=C2=A0=C2=A0 connector: *mut bindings::drm_connector, > +) -> *mut bindings::drm_connector_state { > +=C2=A0=C2=A0=C2=A0 // SAFETY: DRM guarantees that `connector` points to = a valid > initialized `drm_connector`. > +=C2=A0=C2=A0=C2=A0 let state =3D unsafe { (*connector).state }; > +=C2=A0=C2=A0=C2=A0 if state.is_null() { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 return null_mut(); > +=C2=A0=C2=A0=C2=A0 } > + > +=C2=A0=C2=A0=C2=A0 // SAFETY: > +=C2=A0=C2=A0=C2=A0 // - We just verified that `state` is non-null > +=C2=A0=C2=A0=C2=A0 // - This cast is guaranteed to be safe via our type = invariants. > +=C2=A0=C2=A0=C2=A0 let state =3D unsafe { ConnectorState::::from_raw(= state) }; > + > +=C2=A0=C2=A0=C2=A0 let new: Result> =3D KBox::init( > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 init!(ConnectorState:: { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 inner= : state.inner.clone(), > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 state= : bindings::drm_connector_state { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0= =C2=A0=C2=A0=C2=A0 ..Default::default() > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 }, > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 }), > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 GFP_KERNEL, > +=C2=A0=C2=A0=C2=A0 ); > + > +=C2=A0=C2=A0=C2=A0 if let Ok(mut new) =3D new { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // SAFETY: > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // - `new` provides a valid p= ointer to a newly allocated > `drm_plane_state` via type > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 //=C2=A0=C2=A0 invariants > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // - This initializes `new` v= ia memcpy() > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 unsafe { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 > bindings::__drm_atomic_helper_connector_duplicate_state(connector, > new.as_raw_mut()) > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 }; > + > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 KBox::into_raw(new).cast() > +=C2=A0=C2=A0=C2=A0 } else { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 null_mut() > +=C2=A0=C2=A0=C2=A0 } > +} > + > +unsafe extern "C" fn atomic_destroy_state_callback DriverConnectorState>( > +=C2=A0=C2=A0=C2=A0 _connector: *mut bindings::drm_connector, > +=C2=A0=C2=A0=C2=A0 connector_state: *mut bindings::drm_connector_state, > +) { > +=C2=A0=C2=A0=C2=A0 // SAFETY: DRM guarantees that `state` points to a va= lid > instance of `drm_connector_state` > +=C2=A0=C2=A0=C2=A0 unsafe { > bindings::__drm_atomic_helper_connector_destroy_state(connector_state > ) }; > + > +=C2=A0=C2=A0=C2=A0 // SAFETY: > +=C2=A0=C2=A0=C2=A0 // - DRM guarantees we are the only one with access t= o this > `drm_connector_state` > +=C2=A0=C2=A0=C2=A0 // - This cast is safe via our type invariants. > +=C2=A0=C2=A0=C2=A0 drop(unsafe { > KBox::from_raw(connector_state.cast::>()) }); > +} > + > +unsafe extern "C" fn connector_reset_callback DriverConnectorState>( > +=C2=A0=C2=A0=C2=A0 connector: *mut bindings::drm_connector, > +) { > +=C2=A0=C2=A0=C2=A0 // SAFETY: DRM guarantees that `state` points to a va= lid > instance of `drm_connector_state` > +=C2=A0=C2=A0=C2=A0 let state =3D unsafe { (*connector).state }; > +=C2=A0=C2=A0=C2=A0 if !state.is_null() { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // SAFETY: > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // - We're guaranteed `connec= tor` is `Connector` via type > invariants > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // - We're guaranteed `state`= is `ConnectorState` via > type invariants. > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 unsafe { atomic_destroy_state= _callback::(connector, > state) } > + > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // SAFETY: No special require= ments here, DRM expects this to > be NULL > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 unsafe { (*connector).state = =3D null_mut() }; > +=C2=A0=C2=A0=C2=A0 } > + > +=C2=A0=C2=A0=C2=A0 // Unfortunately, this is the best we can do at the m= oment as > this FFI callback was mistakenly > +=C2=A0=C2=A0=C2=A0 // presumed to be infallible :( > +=C2=A0=C2=A0=C2=A0 let new =3D KBox::new(ConnectorState::::default(), > GFP_KERNEL).expect("Blame the API, sorry!"); > + > +=C2=A0=C2=A0=C2=A0 // DRM takes ownership of the state from here, resets= it, and > then assigns it to the connector > +=C2=A0=C2=A0=C2=A0 // SAFETY: > +=C2=A0=C2=A0=C2=A0 // - DRM guarantees that `connector` points to a vali= d instance > of `drm_connector`. > +=C2=A0=C2=A0=C2=A0 // - The cast to `drm_connector_state` is safe via > `ConnectorState`s type invariants. > +=C2=A0=C2=A0=C2=A0 unsafe { > bindings::__drm_atomic_helper_connector_reset(connector, > Box::into_raw(new).cast()) }; > +} > diff --git a/rust/kernel/drm/kms/crtc.rs > b/rust/kernel/drm/kms/crtc.rs > new file mode 100644 > index 000000000000..b9d095854ba6 > --- /dev/null > +++ b/rust/kernel/drm/kms/crtc.rs > @@ -0,0 +1,1110 @@ > +// SPDX-License-Identifier: GPL-2.0 OR MIT > + > +//! DRM CRTCs. > +//! > +//! C header: > [`include/drm/drm_crtc.h`](srctree/include/drm/drm_crtc.h) > + > +use super::{ > +=C2=A0=C2=A0=C2=A0 atomic::*, plane::*, vblank::*, KmsDriver, ModeObject= , > ModeObjectVtable, StaticModeObject, > +=C2=A0=C2=A0=C2=A0 NewKmsDevice, Probing, Sealed, > +}; > +use crate::{ > +=C2=A0=C2=A0=C2=A0 alloc::KBox, > +=C2=A0=C2=A0=C2=A0 bindings, > +=C2=A0=C2=A0=C2=A0 drm::device::Device, > +=C2=A0=C2=A0=C2=A0 error::{from_result, to_result}, > +=C2=A0=C2=A0=C2=A0 prelude::*, > +=C2=A0=C2=A0=C2=A0 types::{NotThreadSafe, Opaque}, > +}; > +use core::{ > +=C2=A0=C2=A0=C2=A0 cell::{Cell, UnsafeCell}, > +=C2=A0=C2=A0=C2=A0 marker::*, > +=C2=A0=C2=A0=C2=A0 mem::{self, ManuallyDrop}, > +=C2=A0=C2=A0=C2=A0 ops::{Deref, DerefMut}, > +=C2=A0=C2=A0=C2=A0 ptr::{null, null_mut, NonNull}, > +}; > +use macros::vtable; > + > +/// The main trait for implementing the [`struct drm_crtc`] API for > [`Crtc`]. > +/// > +/// Any KMS driver should have at least one implementation of this > type, which allows them to create > +/// [`Crtc`] objects. Additionally, a driver may store driver- > private data within the type that > +/// implements [`DriverCrtc`] - and it will be made available when > using a fully typed [`Crtc`] > +/// object. > +/// > +/// # Invariants > +/// > +/// - Any C FFI callbacks generated using this trait are guaranteed > that passed-in > +///=C2=A0=C2=A0 [`struct drm_crtc`] pointers are contained within a > [`Crtc`]. > +/// - Any C FFI callbacks generated using this trait are guaranteed > that passed-in > +///=C2=A0=C2=A0 [`struct drm_crtc_state`] pointers are contained within = a > [`CrtcState`]. > +/// > +/// [`struct drm_crtc`]: srctree/include/drm/drm_crtc.h > +/// [`struct drm_crtc_state`]: srctree/include/drm/drm_crtc.h > +#[vtable] > +pub trait DriverCrtc: Send + Sync + Sized { > +=C2=A0=C2=A0=C2=A0 /// The generated C vtable for this [`DriverCrtc`] > implementation. > +=C2=A0=C2=A0=C2=A0 const OPS: &'static DriverCrtcOps =3D &DriverCrtcOps = { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 funcs: bindings::drm_crtc_fun= cs { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 atomi= c_destroy_state: > Some(atomic_destroy_state_callback::), > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 atomi= c_duplicate_state: > Some(atomic_duplicate_state_callback::), > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 atomi= c_get_property: None, > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 atomi= c_print_state: None, > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 atomi= c_set_property: None, > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 curso= r_move: None, > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 curso= r_set2: None, > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 curso= r_set: None, > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 destr= oy: Some(crtc_destroy_callback::), > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 disab= le_vblank: VblankImpl>::VBLANK_OPS.disable_vblank, > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 early= _unregister: None, > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 enabl= e_vblank: VblankImpl>::VBLANK_OPS.enable_vblank, > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 gamma= _set: None, > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 get_c= rc_sources: None, > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 get_v= blank_counter: None, > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 get_v= blank_timestamp: VblankImpl>::VBLANK_OPS.get_vblank_timestamp, > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 late_= register: None, > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 page_= flip: Some(bindings::drm_atomic_helper_page_flip), > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 page_= flip_target: None, > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 reset= : Some(crtc_reset_callback::), > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 set_c= onfig: > Some(bindings::drm_atomic_helper_set_config), > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 set_c= rc_source: None, > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 set_p= roperty: None, > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 verif= y_crc_source: None, > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 }, > + > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 helper_funcs: bindings::drm_c= rtc_helper_funcs { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 atomi= c_disable: if Self::HAS_ATOMIC_DISABLE { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0= =C2=A0=C2=A0=C2=A0 Some(atomic_disable_callback::) > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 } els= e { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0= =C2=A0=C2=A0=C2=A0 None > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 }, > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 atomi= c_enable: if Self::HAS_ATOMIC_ENABLE { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0= =C2=A0=C2=A0=C2=A0 Some(atomic_enable_callback::) > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 } els= e { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0= =C2=A0=C2=A0=C2=A0 None > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 }, > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 atomi= c_check: if Self::HAS_ATOMIC_CHECK { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0= =C2=A0=C2=A0=C2=A0 Some(atomic_check_callback::) > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 } els= e { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0= =C2=A0=C2=A0=C2=A0 None > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 }, > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 dpms:= None, > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 commi= t: None, > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 prepa= re: None, > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 disab= le: None, > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 mode_= set: None, > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 mode_= valid: None, > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 mode_= fixup: None, > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 atomi= c_begin: if Self::HAS_ATOMIC_BEGIN { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0= =C2=A0=C2=A0=C2=A0 Some(atomic_begin_callback::) > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 } els= e { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0= =C2=A0=C2=A0=C2=A0 None > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 }, > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 atomi= c_flush: if Self::HAS_ATOMIC_FLUSH { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0= =C2=A0=C2=A0=C2=A0 Some(atomic_flush_callback::) > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 } els= e { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0= =C2=A0=C2=A0=C2=A0 None > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 }, > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 mode_= set_nofb: None, > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 mode_= set_base: None, > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 mode_= set_base_atomic: None, > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 get_s= canout_position: None, > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 }, > +=C2=A0=C2=A0=C2=A0 }; > + > +=C2=A0=C2=A0=C2=A0 /// The type to pass to the `args` field of > [`UnregisteredCrtc::new`]. > +=C2=A0=C2=A0=C2=A0 /// > +=C2=A0=C2=A0=C2=A0 /// This type will be made available in in the `args`= argument > of [`Self::new`]. Drivers which > +=C2=A0=C2=A0=C2=A0 /// don't need this can simply pass [`()`] here. > +=C2=A0=C2=A0=C2=A0 type Args; > + > +=C2=A0=C2=A0=C2=A0 /// The parent [`KmsDriver`] implementation. > +=C2=A0=C2=A0=C2=A0 type Driver: KmsDriver; > + > +=C2=A0=C2=A0=C2=A0 /// The [`DriverCrtcState`] implementation for this > [`DriverCrtc`]. > +=C2=A0=C2=A0=C2=A0 /// > +=C2=A0=C2=A0=C2=A0 /// See [`DriverCrtcState`] for more info. > +=C2=A0=C2=A0=C2=A0 type State: DriverCrtcState; > + > +=C2=A0=C2=A0=C2=A0 /// The driver's optional hardware vblank implementat= ion > +=C2=A0=C2=A0=C2=A0 /// > +=C2=A0=C2=A0=C2=A0 /// See [`VblankSupport`] for more info. Drivers that= don't care > about this can just pass > +=C2=A0=C2=A0=C2=A0 /// [`PhantomData`]. > +=C2=A0=C2=A0=C2=A0 type VblankImpl: VblankImpl; > + > +=C2=A0=C2=A0=C2=A0 /// The constructor for creating a [`Crtc`] using thi= s > [`DriverCrtc`] implementation. > +=C2=A0=C2=A0=C2=A0 /// > +=C2=A0=C2=A0=C2=A0 /// Drivers may use this to instantiate their [`Drive= rCrtc`] > object. > +=C2=A0=C2=A0=C2=A0 fn new(device: &Device, args: &Self::Ar= gs) -> impl > PinInit; > + > +=C2=A0=C2=A0=C2=A0 /// The optional [`drm_crtc_helper_funcs.atomic_check= `] hook for > this crtc. > +=C2=A0=C2=A0=C2=A0 /// > +=C2=A0=C2=A0=C2=A0 /// Drivers may use this to customize the atomic chec= k phase of > their [`Crtc`] objects. The > +=C2=A0=C2=A0=C2=A0 /// result of this function determines whether the at= omic check > passed or failed. > +=C2=A0=C2=A0=C2=A0 /// > +=C2=A0=C2=A0=C2=A0 /// [`drm_crtc_helper_funcs.atomic_check`]: > srctree/include/drm/drm_modeset_helper_vtables.h > +=C2=A0=C2=A0=C2=A0 fn atomic_check(_check: CrtcAtomicCheck<'_, Self>) ->= Result { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 build_error::build_error("Thi= s should not be reachable") > +=C2=A0=C2=A0=C2=A0 } > + > +=C2=A0=C2=A0=C2=A0 /// The optional [`drm_crtc_helper_funcs.atomic_begin= `] hook. > +=C2=A0=C2=A0=C2=A0 /// > +=C2=A0=C2=A0=C2=A0 /// This hook will be called before a set of [`Plane`= ] updates > are performed for the given > +=C2=A0=C2=A0=C2=A0 /// [`Crtc`]. > +=C2=A0=C2=A0=C2=A0 /// > +=C2=A0=C2=A0=C2=A0 /// [`drm_crtc_helper_funcs.atomic_begin`]: > srctree/include/drm/drm_modeset_helper_vtables.h > +=C2=A0=C2=A0=C2=A0 fn atomic_begin(_commit: CrtcAtomicCommit<'_, Self>) = { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 build_error::build_error("Thi= s should not be reachable") > +=C2=A0=C2=A0=C2=A0 } > + > +=C2=A0=C2=A0=C2=A0 /// The optional [`drm_crtc_helper_funcs.atomic_flush= `] hook. > +=C2=A0=C2=A0=C2=A0 /// > +=C2=A0=C2=A0=C2=A0 /// This hook will be called after a set of [`Plane`]= updates > are performed for the given > +=C2=A0=C2=A0=C2=A0 /// [`Crtc`]. > +=C2=A0=C2=A0=C2=A0 /// > +=C2=A0=C2=A0=C2=A0 /// [`drm_crtc_helper_funcs.atomic_flush`]: > srctree/include/drm/drm_modeset_helper_vtables.h > +=C2=A0=C2=A0=C2=A0 fn atomic_flush(_commit: CrtcAtomicCommit<'_, Self>) = { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 build_error::build_error("Thi= s should never be reachable") > +=C2=A0=C2=A0=C2=A0 } > + > +=C2=A0=C2=A0=C2=A0 /// The optional [`drm_crtc_helper_funcs.atomic_enabl= e`] hook. > +=C2=A0=C2=A0=C2=A0 /// > +=C2=A0=C2=A0=C2=A0 /// This hook will be called before enabling a [`Crtc= `] in an > atomic commit. > +=C2=A0=C2=A0=C2=A0 /// > +=C2=A0=C2=A0=C2=A0 /// [`drm_crtc_helper_funcs.atomic_enable`]: > srctree/include/drm/drm_modeset_helper_vtables.h > +=C2=A0=C2=A0=C2=A0 fn atomic_enable(_commit: CrtcAtomicCommit<'_, Self>)= { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 build_error::build_error("Thi= s should never be reachable") > +=C2=A0=C2=A0=C2=A0 } > + > +=C2=A0=C2=A0=C2=A0 /// The optional [`drm_crtc_helper_funcs.atomic_disab= le`] hook. > +=C2=A0=C2=A0=C2=A0 /// > +=C2=A0=C2=A0=C2=A0 /// This hook will be called before disabling a [`Crt= c`] in an > atomic commit. > +=C2=A0=C2=A0=C2=A0 /// > +=C2=A0=C2=A0=C2=A0 /// [`drm_crtc_helper_funcs.atomic_disable`]: > srctree/include/drm/drm_modeset_helper_vtables.h > +=C2=A0=C2=A0=C2=A0 fn atomic_disable(_commit: CrtcAtomicCommit<'_, Self>= ) { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 build_error::build_error("Thi= s should never be reachable") > +=C2=A0=C2=A0=C2=A0 } > +} > + > +/// The generated C vtable for a [`DriverCrtc`]. > +/// > +/// This type is created internally by DRM. > +pub struct DriverCrtcOps { > +=C2=A0=C2=A0=C2=A0 funcs: bindings::drm_crtc_funcs, > +=C2=A0=C2=A0=C2=A0 helper_funcs: bindings::drm_crtc_helper_funcs, > +} > + > +/// The main interface for a [`struct drm_crtc`]. > +/// > +/// This type is the main interface for dealing with DRM CRTCs. In > addition, it also allows > +/// immutable access to whatever private data is contained within an > implementor's [`DriverCrtc`] > +/// type. > +/// > +/// # Invariants > +/// > +/// - `crtc` and `inner` are initialized for as long as this object > is made available to users. > +/// - The data layout of this structure begins with [`struct > drm_crtc`]. > +/// - The atomic state for this type can always be assumed to be of > type [`CrtcState`]. > +/// > +/// [`struct drm_crtc`]: srctree/include/drm/drm_crtc.h > +#[repr(C)] > +#[pin_data] > +pub struct Crtc { > +=C2=A0=C2=A0=C2=A0 // The FFI drm_crtc object > +=C2=A0=C2=A0=C2=A0 crtc: Opaque, > +=C2=A0=C2=A0=C2=A0 /// The driver's private inner data > +=C2=A0=C2=A0=C2=A0 #[pin] > +=C2=A0=C2=A0=C2=A0 inner: T, > +=C2=A0=C2=A0=C2=A0 #[pin] > +=C2=A0=C2=A0=C2=A0 _p: PhantomPinned, > +} > + > +impl Sealed for Crtc {} > + > +// SAFETY: Our CRTC interfaces are guaranteed to be thread-safe > +unsafe impl Send for Crtc {} > + > +// SAFETY: Our CRTC interfaces are guaranteed to be thread-safe > +unsafe impl Sync for Crtc {} > + > +impl Deref for Crtc { > +=C2=A0=C2=A0=C2=A0 type Target =3D T; > + > +=C2=A0=C2=A0=C2=A0 fn deref(&self) -> &Self::Target { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 &self.inner > +=C2=A0=C2=A0=C2=A0 } > +} > + > +// SAFETY: We don't expose Crtc to users before `base` is > initialized in ::new(), so > +// `raw_mode_obj` always returns a valid pointer to a > bindings::drm_mode_object. > +unsafe impl ModeObject for Crtc { > +=C2=A0=C2=A0=C2=A0 type Driver =3D T::Driver; > + > +=C2=A0=C2=A0=C2=A0 fn drm_dev(&self) -> &Device { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // SAFETY: DRM connectors exi= st for as long as the device > does, so this pointer is always > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // valid > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 unsafe { Device::from_raw((*s= elf.as_raw()).dev) } > +=C2=A0=C2=A0=C2=A0 } > + > +=C2=A0=C2=A0=C2=A0 fn raw_mode_obj(&self) -> *mut bindings::drm_mode_obj= ect { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // SAFETY: We don't expose Cr= tc to users before it's > initialized, so `base` is always > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // initialized > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 unsafe { &raw mut (*self.as_r= aw()).base } > +=C2=A0=C2=A0=C2=A0 } > +} > + > +// SAFETY: CRTCs are non-refcounted modesetting objects > +unsafe impl StaticModeObject for Crtc {} > + > +// SAFETY: `funcs` is initialized when the crtc is allocated > +unsafe impl ModeObjectVtable for Crtc { > +=C2=A0=C2=A0=C2=A0 type Vtable =3D bindings::drm_crtc_funcs; > + > +=C2=A0=C2=A0=C2=A0 fn vtable(&self) -> *const Self::Vtable { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // SAFETY: `as_raw()` always = returns a valid pointer to a > CRTC > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 unsafe { *self.as_raw() }.fun= cs > +=C2=A0=C2=A0=C2=A0 } > +} > + > +impl Crtc { > +=C2=A0=C2=A0=C2=A0 super::impl_from_opaque_mode_obj! { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 fn <'a, D>(&'a OpaqueCrtc)= -> &'a Self; > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 use > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 T as = DriverCrtc, > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 D as = KmsDriver > +=C2=A0=C2=A0=C2=A0 } > + > +=C2=A0=C2=A0=C2=A0 pub(crate) fn get_vblank_ptr(&self) -> *mut > bindings::drm_vblank_crtc { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // SAFETY: FFI Call with no s= pecial requirements > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 unsafe { bindings::drm_crtc_v= blank_crtc(self.as_raw()) } > +=C2=A0=C2=A0=C2=A0 } > + > +=C2=A0=C2=A0=C2=A0 pub(crate) const fn has_vblank() -> bool { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 T::OPS.funcs.enable_vblank.is= _some() > +=C2=A0=C2=A0=C2=A0 } > +} > + > +/// A [`Crtc`] that has not yet been registered with userspace. > +/// > +/// KMS registration is single-threaded, so this object is not > thread-safe. > +/// > +/// # Invariants > +/// > +/// - This object can only exist before its respective KMS device > has been registered. > +/// - Otherwise, it inherits all invariants of [`Crtc`] and has an > identical data layout. > +pub struct UnregisteredCrtc(Crtc, NotThreadSafe); > + > +impl UnregisteredCrtc { > +=C2=A0=C2=A0=C2=A0 /// Construct a new [`UnregisteredCrtc`]. > +=C2=A0=C2=A0=C2=A0 /// > +=C2=A0=C2=A0=C2=A0 /// A driver may use this from their > [`KmsDriver::create_objects`] callback in order to > +=C2=A0=C2=A0=C2=A0 /// construct new [`UnregisteredCrtc`] objects. > +=C2=A0=C2=A0=C2=A0 /// > +=C2=A0=C2=A0=C2=A0 /// [`KmsDriver::create_objects`]: > kernel::drm::kms::KmsDriver::create_objects > +=C2=A0=C2=A0=C2=A0 pub fn new<'a, 'b: 'a, PrimaryData, CursorData>( > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 dev: &'a NewKmsDevice<'a, T::= Driver, Probing>, > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 primary: &'a UnregisteredPlan= e, > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 cursor: Option<&'a Unregister= edPlane>, > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 name: Option<&CStr>, > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 args: T::Args, > +=C2=A0=C2=A0=C2=A0 ) -> Result<&'a Self> > +=C2=A0=C2=A0=C2=A0 where > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 PrimaryData: DriverPlane, > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 CursorData: DriverPlane, > +=C2=A0=C2=A0=C2=A0 { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 if Crtc::::has_vblank() { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 dev.h= as_vblanks.set(true) > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 } > + > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 let this: Pin>> = =3D KBox::try_pin_init( > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 try_p= in_init!(Crtc { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0= =C2=A0=C2=A0=C2=A0 crtc: Opaque::new(bindings::drm_crtc { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0= =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 helper_private: &T::OPS.helper_f= uncs, > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0= =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 ..Default::default() > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0= =C2=A0=C2=A0=C2=A0 }), > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0= =C2=A0=C2=A0=C2=A0 inner <- T::new(dev, &args), > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0= =C2=A0=C2=A0=C2=A0 _p: PhantomPinned, > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 }), > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 GFP_K= ERNEL, > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 )?; > + > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // SAFETY: > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // - `dev` handles destroying= the CRTC and thus will outlive > us. > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // - We just allocated `this`= , and we won't move it since > it's pinned > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // - `primary` and `cursor` s= hare the lifetime 'a with `dev` > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // - This function will memcp= y the contents of `name` into > its own storage. > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 to_result(unsafe { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 bindi= ngs::drm_crtc_init_with_planes( > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0= =C2=A0=C2=A0=C2=A0 dev.as_raw(), > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0= =C2=A0=C2=A0=C2=A0 this.as_raw(), > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0= =C2=A0=C2=A0=C2=A0 primary.as_raw(), > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0= =C2=A0=C2=A0=C2=A0 cursor.map_or(null_mut(), |c| c.as_raw()), > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0= =C2=A0=C2=A0=C2=A0 &T::OPS.funcs, > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0= =C2=A0=C2=A0=C2=A0 name.map_or(null(), |n| n.as_char_ptr()), > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 ) > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 })?; > + > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // SAFETY: We don't move anyt= hing > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 let this =3D unsafe { Pin::in= to_inner_unchecked(this) }; > + > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // We'll re-assemble the box = in crtc_destroy_callback() > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 let this =3D KBox::into_raw(t= his); > + > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // UnregisteredCrtc has an eq= uivalent data layout > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 let this: *mut Self =3D this.= cast(); > + > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // SAFETY: We just allocated = the crtc above, so this pointer > must be valid > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 Ok(unsafe { &*this }) > +=C2=A0=C2=A0=C2=A0 } > +} > + > +// SAFETY: We inherit all relevant invariants of `Crtc` > +unsafe impl AsRawCrtc for UnregisteredCrtc { > +=C2=A0=C2=A0=C2=A0 fn as_raw(&self) -> *mut bindings::drm_crtc { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 self.0.as_raw() > +=C2=A0=C2=A0=C2=A0 } > + > +=C2=A0=C2=A0=C2=A0 unsafe fn from_raw<'a>(ptr: *mut bindings::drm_crtc) = -> &'a Self > { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // SAFETY: This is another fr= om_raw() call, so this function > shares the same safety contract > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 let crtc =3D unsafe { Crtc::<= T>::from_raw(ptr) }; > + > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // SAFETY: Our data layout is= identical via our type > invariants. > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 unsafe { mem::transmute(crtc)= } > +=C2=A0=C2=A0=C2=A0 } > +} > + > +impl Deref for UnregisteredCrtc { > +=C2=A0=C2=A0=C2=A0 type Target =3D T; > + > +=C2=A0=C2=A0=C2=A0 fn deref(&self) -> &Self::Target { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 &self.0.inner > +=C2=A0=C2=A0=C2=A0 } > +} > + > +/// A trait implemented by any type that acts as a [`struct > drm_crtc`] interface. > +/// > +/// This is implemented internally by DRM. > +/// > +/// # Safety > +/// > +/// [`as_raw()`] must always return a valid pointer to a [`struct > drm_crtc`]. > +/// > +/// [`struct drm_crtc`]: srctree/include/drm/drm_crtc.h > +/// [`as_raw()`]: AsRawCrtc::as_raw() > +pub unsafe trait AsRawCrtc { > +=C2=A0=C2=A0=C2=A0 /// Return a raw pointer to the `bindings::drm_crtc` = for this > object > +=C2=A0=C2=A0=C2=A0 fn as_raw(&self) -> *mut bindings::drm_crtc; > + > +=C2=A0=C2=A0=C2=A0 /// Convert a raw [`struct drm_crtc`] pointer into an= object of > this type. > +=C2=A0=C2=A0=C2=A0 /// > +=C2=A0=C2=A0=C2=A0 /// # Safety > +=C2=A0=C2=A0=C2=A0 /// > +=C2=A0=C2=A0=C2=A0 /// Callers promise that `ptr` points to a valid inst= ance of > this type > +=C2=A0=C2=A0=C2=A0 /// > +=C2=A0=C2=A0=C2=A0 /// [`struct drm_crtc`]: srctree/include/drm/drm_crtc= .h > +=C2=A0=C2=A0=C2=A0 unsafe fn from_raw<'a>(ptr: *mut bindings::drm_crtc) = -> &'a > Self; > +} > + > +// SAFETY: > +// - Via our type variants our data layout starts with `drm_crtc` > +// - Since we don't expose `crtc` to users before it has been > initialized, this and our data > +//=C2=A0=C2=A0 layout ensure that `as_raw()` always returns a valid poin= ter to > a `drm_crtc`. > +unsafe impl AsRawCrtc for Crtc { > +=C2=A0=C2=A0=C2=A0 fn as_raw(&self) -> *mut bindings::drm_crtc { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 self.crtc.get() > +=C2=A0=C2=A0=C2=A0 } > + > +=C2=A0=C2=A0=C2=A0 unsafe fn from_raw<'a>(ptr: *mut bindings::drm_crtc) = -> &'a Self > { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // Our data layout start with= `bindings::drm_crtc`. > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 let ptr: *mut Self =3D ptr.ca= st(); > + > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // SAFETY: Our safety contrac= t requires that `ptr` point to > a valid intance of `Self`. > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 unsafe { &*ptr } > +=C2=A0=C2=A0=C2=A0 } > +} > + > +// SAFETY: We only expose this object to users directly after > KmsDriver::create_objects has been > +// called. > +unsafe impl ModesettableCrtc for Crtc { > +=C2=A0=C2=A0=C2=A0 type State =3D CrtcState; > +} > + > +/// A supertrait of [`AsRawCrtc`] for [`struct drm_crtc`] interfaces > that can perform modesets. > +/// > +/// This is implemented internally by DRM. > +/// > +/// # Safety > +/// > +/// Any object implementing this trait must only be made directly > available to the user after > +/// [`create_objects`] has completed. > +/// > +/// [`struct drm_crtc`]: srctree/include/drm/drm_crtc.h > +/// [`create_objects`]: KmsDriver::create_objects > +pub unsafe trait ModesettableCrtc: AsRawCrtc { > +=C2=A0=C2=A0=C2=A0 /// The type that should be returned for a CRTC state= acquired > using this CRTC interface > +=C2=A0=C2=A0=C2=A0 type State: FromRawCrtcState; > +} > + > +/// Common methods available on any type which implements > [`AsRawCrtc`]. > +/// > +/// This is implemented internally by DRM, and provides many of the > basic methods for working with > +/// CRTCs. > +pub trait RawCrtc: AsRawCrtc { > +=C2=A0=C2=A0=C2=A0 /// Return the index of this CRTC. > +=C2=A0=C2=A0=C2=A0 fn index(&self) -> u32 { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // SAFETY: The index is initi= alized by the time we expose > Crtc objects to users, and is > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // invariant throughout the l= ifetime of the Crtc > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 unsafe { (*self.as_raw()).ind= ex } > +=C2=A0=C2=A0=C2=A0 } > + > +=C2=A0=C2=A0=C2=A0 /// Return the index of this DRM CRTC in the form of = a bitmask. > +=C2=A0=C2=A0=C2=A0 fn mask(&self) -> u32 { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 1 << self.index() > +=C2=A0=C2=A0=C2=A0 } > +} > +impl RawCrtc for T {} > + > +/// A [`struct drm_crtc`] without a known [`DriverCrtc`] > implementation. > +/// > +/// This is mainly for situations where our bindings can't infer the > [`DriverCrtc`] implementation > +/// for a [`struct drm_crtc`] automatically. It is identical to > [`Crtc`], except that it does not > +/// provide access to the driver's private data. > +/// > +/// It may be upcasted to a full [`Crtc`] using > [`Crtc::from_opaque`] or > +/// [`Crtc::try_from_opaque`]. > +/// > +/// # Invariants > +/// > +/// - `crtc` is initialized for as long as this object is made > available to users. > +/// - The data layout of this structure is equivalent to [`struct > drm_crtc`]. > +/// > +/// [`struct drm_crtc`]: srctree/include/drm/drm_crtc.h > +#[repr(transparent)] > +pub struct OpaqueCrtc { > +=C2=A0=C2=A0=C2=A0 crtc: Opaque, > +=C2=A0=C2=A0=C2=A0 _p: PhantomData, > +} > + > +impl Sealed for OpaqueCrtc {} > + > +// SAFETY: > +// - Via our type variants our data layout is identical to > `drm_crtc` > +// - Since we don't expose `OpaqueCrtc` to users before it has been > initialized, this and our data > +//=C2=A0=C2=A0 layout ensure that `as_raw()` always returns a valid poin= ter to > a `drm_crtc`. > +unsafe impl AsRawCrtc for OpaqueCrtc { > +=C2=A0=C2=A0=C2=A0 fn as_raw(&self) -> *mut bindings::drm_crtc { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 self.crtc.get() > +=C2=A0=C2=A0=C2=A0 } > + > +=C2=A0=C2=A0=C2=A0 unsafe fn from_raw<'a>(ptr: *mut bindings::drm_crtc) = -> &'a Self > { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // SAFETY: Our data layout st= arts with `bindings::drm_crtc` > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 unsafe { &*ptr.cast() } > +=C2=A0=C2=A0=C2=A0 } > +} > + > +// SAFETY: We only expose this object to users directly after > KmsDriver::create_objects has been > +// called. > +unsafe impl ModesettableCrtc for OpaqueCrtc { > +=C2=A0=C2=A0=C2=A0 type State =3D OpaqueCrtcState; > +} > + > +// SAFETY: We don't expose OpaqueCrtc to users before `base` is > initialized in Crtc::::new(), > +// so `raw_mode_obj` always returns a valid pointer to a > bindings::drm_mode_object. > +unsafe impl ModeObject for OpaqueCrtc { > +=C2=A0=C2=A0=C2=A0 type Driver =3D T; > + > +=C2=A0=C2=A0=C2=A0 fn drm_dev(&self) -> &Device { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // SAFETY: The parent device = for a DRM connector will never > outlive the connector, and this > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // pointer is invariant throu= gh the lifetime of the > connector > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 unsafe { Device::from_raw((*s= elf.as_raw()).dev) } > +=C2=A0=C2=A0=C2=A0 } > + > +=C2=A0=C2=A0=C2=A0 fn raw_mode_obj(&self) -> *mut bindings::drm_mode_obj= ect { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // SAFETY: We don't expose DR= M connectors to users before > `base` is initialized > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 unsafe { &raw mut (*self.as_r= aw()).base } > +=C2=A0=C2=A0=C2=A0 } > +} > + > +// SAFETY: CRTCs are non-refcounted modesetting objects > +unsafe impl StaticModeObject for OpaqueCrtc {} > + > +// SAFETY: Our CRTC interface is guaranteed to be thread-safe > +unsafe impl Send for OpaqueCrtc {} > + > +// SAFETY: Our CRTC interface is guaranteed to be thread-safe > +unsafe impl Sync for OpaqueCrtc {} > + > +// SAFETY: `funcs` is initialized when the CRTC is allocated > +unsafe impl ModeObjectVtable for OpaqueCrtc { > +=C2=A0=C2=A0=C2=A0 type Vtable =3D bindings::drm_crtc_funcs; > + > +=C2=A0=C2=A0=C2=A0 fn vtable(&self) -> *const Self::Vtable { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // SAFETY: `as_raw()` always = returns a valid pointer to a > crtc > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 unsafe { (*self.as_raw()).fun= cs } > +=C2=A0=C2=A0=C2=A0 } > +} > + > +impl Sealed for CrtcState {} > + > +/// The main trait for implementing the [`struct drm_crtc_state`] > API for a [`Crtc`]. > +/// > +/// A driver may store driver-private data within the implementor's > type, which will be available > +/// when using a full typed [`CrtcState`] object. > +/// > +/// # Invariants > +/// > +/// - Any C FFI callbacks generated using this trait are guaranteed > that passed-in > +///=C2=A0=C2=A0 [`struct drm_crtc`] pointers are contained within a > [`Crtc`]. > +/// - Any C FFI callbacks generated using this trait are guaranteed > that passed-in > +///=C2=A0=C2=A0 [`struct drm_crtc_state`] pointers are contained within = a > [`CrtcState`]. > +/// > +/// [`struct drm_crtc`]: srctree/include/drm_crtc.h > +/// [`struct drm_crtc_state`]: srctree/include/drm_crtc.h > +pub trait DriverCrtcState: Clone + Default + Unpin { > +=C2=A0=C2=A0=C2=A0 /// The parent CRTC driver for this CRTC state > +=C2=A0=C2=A0=C2=A0 type Crtc: DriverCrtc > +=C2=A0=C2=A0=C2=A0 where > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 Self: Sized; > +} > + > +/// The main interface for a [`struct drm_crtc_state`]. > +/// > +/// This type is the main interface for dealing with the atomic > state of DRM crtcs. In addition, it > +/// allows access to whatever private data is contained within an > implementor's [`DriverCrtcState`] > +/// type. > +/// > +/// # Invariants > +/// > +/// - `state` and `inner` initialized for as long as this object is > exposed to users. > +/// - The data layout of this structure begins with [`struct > drm_crtc_state`]. > +/// - The CRTC for this type can always be assumed to be of type > [`Crtc`]. > +/// > +/// [`struct drm_crtc_state`]: srctree/include/drm/drm_crtc.h > +#[repr(C)] > +pub struct CrtcState { > +=C2=A0=C2=A0=C2=A0 // It should be noted that CrtcState is a bit of an o= ddball - > it's the only atomic state > +=C2=A0=C2=A0=C2=A0 // structure that can be modified after it has been s= wapped in, > which is why we need to have > +=C2=A0=C2=A0=C2=A0 // `state` within an `Opaque<>`=E2=80=A6 > +=C2=A0=C2=A0=C2=A0 state: Opaque, > + > +=C2=A0=C2=A0=C2=A0 // =E2=80=A6it is also one of the few atomic states t= hat some drivers > will embed work structures into, > +=C2=A0=C2=A0=C2=A0 // which means there's a good chance in the future we= may have > pinned data here - making it > +=C2=A0=C2=A0=C2=A0 // impossible for us to hold a mutable or immutable r= eference to > the CrtcState. In preparation > +=C2=A0=C2=A0=C2=A0 // for that possibility, we keep `T` in an UnsafeCell= . > +=C2=A0=C2=A0=C2=A0 inner: UnsafeCell, > +} > + > +impl Deref for CrtcState { > +=C2=A0=C2=A0=C2=A0 type Target =3D T; > + > +=C2=A0=C2=A0=C2=A0 fn deref(&self) -> &Self::Target { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // SAFETY: Our interface ensu= res that `inner` will not be > modified unless only a single > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // mutable reference exists t= o `inner`, so this is safe > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 unsafe { &*self.inner.get() } > +=C2=A0=C2=A0=C2=A0 } > +} > + > +impl DerefMut for CrtcState { > +=C2=A0=C2=A0=C2=A0 fn deref_mut(&mut self) -> &mut Self::Target { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 self.inner.get_mut() > +=C2=A0=C2=A0=C2=A0 } > +} > + > +// SAFETY: Shares the safety guarantee of Crtc's ModeObjectVtable > impl > +unsafe impl ModeObjectVtable for CrtcState { > +=C2=A0=C2=A0=C2=A0 type Vtable =3D bindings::drm_crtc_funcs; > + > +=C2=A0=C2=A0=C2=A0 fn vtable(&self) -> *const Self::Vtable { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 self.crtc().vtable() > +=C2=A0=C2=A0=C2=A0 } > +} > + > +impl CrtcState { > +=C2=A0=C2=A0=C2=A0 super::impl_from_opaque_mode_obj! { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 fn <'a, D, C>(&'a OpaqueCrtcS= tate) -> &'a Self > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 where > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 T: Dr= iverCrtcState; > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 use > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 C as = DriverCrtc, > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 D as = KmsDriver > +=C2=A0=C2=A0=C2=A0 } > +} > + > +/// A trait implemented by any type which can produce a reference to > a [`struct drm_crtc_state`]. > +/// > +/// This is implemented internally by DRM. > +/// > +/// [`struct drm_crtc_state`]: srctree/include/drm/drm_crtc.h > +pub trait AsRawCrtcState: private::AsRawCrtcState { > +=C2=A0=C2=A0=C2=A0 /// The type that this CRTC state interface returns t= o represent > the parent CRTC > +=C2=A0=C2=A0=C2=A0 type Crtc: ModesettableCrtc; > +} > + > +pub(crate) mod private { > +=C2=A0=C2=A0=C2=A0 use super::*; > + > +=C2=A0=C2=A0=C2=A0 #[allow(unreachable_pub)] > +=C2=A0=C2=A0=C2=A0 pub trait AsRawCrtcState { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 /// Return a raw pointer to t= he DRM CRTC state > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 /// > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 /// Note that CRTC states are= the only atomic state in KMS > which don't nicely follow rust's > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 /// data aliasing rules alrea= dy. > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 fn as_raw(&self) -> *mut bind= ings::drm_crtc_state; > +=C2=A0=C2=A0=C2=A0 } > +} > + > +pub(super) use private::AsRawCrtcState as AsRawCrtcStatePrivate; > + > +/// Common methods available on any type which implements > [`AsRawCrtcState`]. > +/// > +/// This is implemented internally by DRM, and provides many of the > basic methods for working with > +/// the atomic state of [`Crtc`]s. > +pub trait RawCrtcState: AsRawCrtcState { > +=C2=A0=C2=A0=C2=A0 /// Return the CRTC that owns this state. > +=C2=A0=C2=A0=C2=A0 fn crtc(&self) -> &Self::Crtc { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // SAFETY: > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // - This type conversion is = guaranteed by type invariance > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // - Our interface ensures th= at this access follows rust's > data-aliasing rules > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // - `crtc` is guaranteed to = never be NULL and is invariant > throughout the lifetime of the > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 //=C2=A0=C2=A0 state > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 unsafe { AsRawCrtc>::from_raw((*self.as_raw()).crtc) } > +=C2=A0=C2=A0=C2=A0 } > + > +=C2=A0=C2=A0=C2=A0 /// Returns whether or not the CRTC is active in this= atomic > state. > +=C2=A0=C2=A0=C2=A0 fn active(&self) -> bool { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // SAFETY: `active` and the r= est of its containing bitfield > can only be modified from the > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // atomic check context, and = are invariant beyond that point > - so our interface can ensure > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // this access is serialized > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 unsafe { (*self.as_raw()).act= ive } > +=C2=A0=C2=A0=C2=A0 } > +} > +impl RawCrtcState for T {} > + > +/// A trait implemented for any type which can be constructed > directly from a > +/// [`struct drm_crtc_state`] pointer. > +/// > +/// This is implemented internally by DRM. > +/// > +/// [`struct drm_crtc_state`]: srctree/include/drm/drm_crtc.h > +pub trait FromRawCrtcState: AsRawCrtcState { > +=C2=A0=C2=A0=C2=A0 /// Obtain a reference back to this type from a raw D= RM crtc > state pointer > +=C2=A0=C2=A0=C2=A0 /// > +=C2=A0=C2=A0=C2=A0 /// # Safety > +=C2=A0=C2=A0=C2=A0 /// > +=C2=A0=C2=A0=C2=A0 /// Callers must ensure that ptr contains a valid ins= tance of > this type. > +=C2=A0=C2=A0=C2=A0 unsafe fn from_raw<'a>(ptr: *const bindings::drm_crtc= _state) -> > &'a Self; > +} > + > +impl private::AsRawCrtcState for CrtcState { > +=C2=A0=C2=A0=C2=A0 #[inline] > +=C2=A0=C2=A0=C2=A0 fn as_raw(&self) -> *mut bindings::drm_crtc_state { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 self.state.get() > +=C2=A0=C2=A0=C2=A0 } > +} > + > +impl AsRawCrtcState for CrtcState { > +=C2=A0=C2=A0=C2=A0 type Crtc =3D Crtc; > +} > + > +impl FromRawCrtcState for CrtcState { > +=C2=A0=C2=A0=C2=A0 unsafe fn from_raw<'a>(ptr: *const bindings::drm_crtc= _state) -> > &'a Self { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // SAFETY: Our data layout st= arts with > `bindings::drm_crtc_state` > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 unsafe { &*(ptr.cast()) } > +=C2=A0=C2=A0=C2=A0 } > +} > + > +/// A [`struct drm_crtc_state`] without a known [`DriverCrtcState`] > implementation. > +/// > +/// This is mainly for situations where our bindings can't infer the > [`DriverCrtcState`] > +/// implementation for a [`struct drm_crtc_state`] automatically. It > is identical to [`Crtc`], > +/// except that it does not provide access to the driver's private > data. > +/// > +/// # Invariants > +/// > +/// - `state` is initialized for as long as this object is exposed > to users. > +/// - The data layout of this type is identical to [`struct > drm_crtc_state`]. > +/// > +/// [`struct drm_crtc_state`]: srctree/include/drm/drm_crtc.h > +#[repr(transparent)] > +pub struct OpaqueCrtcState { > +=C2=A0=C2=A0=C2=A0 state: Opaque, > +=C2=A0=C2=A0=C2=A0 _p: PhantomData, > +} > + > +impl AsRawCrtcState for OpaqueCrtcState { > +=C2=A0=C2=A0=C2=A0 type Crtc =3D OpaqueCrtc; > +} > + > +impl private::AsRawCrtcState for OpaqueCrtcState { > +=C2=A0=C2=A0=C2=A0 fn as_raw(&self) -> *mut bindings::drm_crtc_state { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 self.state.get() > +=C2=A0=C2=A0=C2=A0 } > +} > + > +impl FromRawCrtcState for OpaqueCrtcState { > +=C2=A0=C2=A0=C2=A0 unsafe fn from_raw<'a>(ptr: *const bindings::drm_crtc= _state) -> > &'a Self { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // SAFETY: Our data layout is= identical to > `bindings::drm_crtc_state` > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 unsafe { &*(ptr.cast()) } > +=C2=A0=C2=A0=C2=A0 } > +} > + > +// SAFETY: Shares the safety guarantees of OpaqueCrtc's > ModeObjectVtable impl > +unsafe impl ModeObjectVtable for OpaqueCrtcState { > +=C2=A0=C2=A0=C2=A0 type Vtable =3D bindings::drm_crtc_funcs; > + > +=C2=A0=C2=A0=C2=A0 fn vtable(&self) -> *const Self::Vtable { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 self.crtc().vtable() > +=C2=A0=C2=A0=C2=A0 } > +} > + > +/// An interface for mutating a [`Crtc`]s atomic state. > +/// > +/// This type is typically returned by an [`AtomicStateMutator`] > within contexts where it is > +/// possible to safely mutate a plane's state. In order to uphold > rust's data-aliasing rules, only > +/// [`CrtcStateMutator`] may exist at a time. > +/// > +/// # Invariants > +/// > +/// `self.state` always points to a valid instance of a > [`FromRawCrtcState`] object. > +pub struct CrtcStateMutator<'a, T: FromRawCrtcState> { > +=C2=A0=C2=A0=C2=A0 state: NonNull, > +=C2=A0=C2=A0=C2=A0 mask: &'a Cell, > +} > + > +impl<'a, T: FromRawCrtcState> CrtcStateMutator<'a, T> { > +=C2=A0=C2=A0=C2=A0 pub(super) fn new( > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 mutator: &'a AtomicStateMutat= or, > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 state: NonNull, > +=C2=A0=C2=A0=C2=A0 ) -> Option { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // SAFETY: `crtc` is invarian= t throughout the lifetime of > the atomic state, and always > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // points to a valid `Crtc` > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 let crtc =3D unsafe { > T::Crtc::from_raw((*state.as_ptr()).crtc) }; > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 let crtc_mask =3D crtc.mask()= ; > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 let borrowed_mask =3D mutator= .borrowed_crtcs.get(); > + > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 if borrowed_mask & crtc_mask = =3D=3D 0 { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 mutat= or.borrowed_crtcs.set(borrowed_mask | crtc_mask); > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 Some(= Self { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0= =C2=A0=C2=A0=C2=A0 mask: &mutator.borrowed_crtcs, > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0= =C2=A0=C2=A0=C2=A0 state: state.cast(), > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 }) > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 } else { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 None > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 } > +=C2=A0=C2=A0=C2=A0 } > +} > + > +impl<'a, T: DriverCrtcState> CrtcStateMutator<'a, CrtcState> { > +=C2=A0=C2=A0=C2=A0 super::impl_from_opaque_mode_obj! { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 fn (CrtcStateMutator<'a= , OpaqueCrtcState>) -> Self > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 where > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 T: Dr= iverCrtcState; > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 use > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 T as = DriverCrtc, > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 D as = KmsDriver > +=C2=A0=C2=A0=C2=A0 } > +} > + > +impl<'a, T: FromRawCrtcState> Drop for CrtcStateMutator<'a, T> { > +=C2=A0=C2=A0=C2=A0 fn drop(&mut self) { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // SAFETY: Our interface is p= roof that we are the only ones > with a reference to this data > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 let mask =3D unsafe { self.st= ate.as_ref() }.crtc().mask(); > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 self.mask.set(self.mask.get()= & !mask); > +=C2=A0=C2=A0=C2=A0 } > +} > + > +impl<'a, T: DriverCrtcState> Deref for CrtcStateMutator<'a, > CrtcState> { > +=C2=A0=C2=A0=C2=A0 type Target =3D T; > + > +=C2=A0=C2=A0=C2=A0 fn deref(&self) -> &Self::Target { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // SAFETY: Our interface ensu= res that `self.state.inner` > follows rust's data-aliasing rules, > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // so this is safe > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 unsafe { &*(*self.state.as_pt= r()).inner.get() } > +=C2=A0=C2=A0=C2=A0 } > +} > + > +impl<'a, T: DriverCrtcState> DerefMut for CrtcStateMutator<'a, > CrtcState> { > +=C2=A0=C2=A0=C2=A0 fn deref_mut(&mut self) -> &mut Self::Target { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // SAFETY: Our interface ensu= res that `self.state.inner` > follows rust's data-aliasing rules, > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // so this is safe > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 unsafe { (*self.state.as_ptr(= )).inner.get_mut() } > +=C2=A0=C2=A0=C2=A0 } > +} > + > +impl<'a, T: FromRawCrtcState> AsRawCrtcState for > CrtcStateMutator<'a, T> { > +=C2=A0=C2=A0=C2=A0 type Crtc =3D T::Crtc; > +} > + > +impl<'a, T: FromRawCrtcState> private::AsRawCrtcState for > CrtcStateMutator<'a, T> { > +=C2=A0=C2=A0=C2=A0 fn as_raw(&self) -> *mut bindings::drm_crtc_state { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 self.state.as_ptr().cast() > +=C2=A0=C2=A0=C2=A0 } > +} > + > +// SAFETY: Shares the safety guarantees of T::Crtc's > ModeObjectVtable impl > +unsafe impl<'a, T: FromRawCrtcState> ModeObjectVtable for > CrtcStateMutator<'a, T> > +where > +=C2=A0=C2=A0=C2=A0 T::Crtc: ModeObjectVtable, > +{ > +=C2=A0=C2=A0=C2=A0 type Vtable =3D ::Vtable= ; > + > +=C2=A0=C2=A0=C2=A0 fn vtable(&self) -> *const Self::Vtable { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 self.crtc().vtable() > +=C2=A0=C2=A0=C2=A0 } > +} > + > +/// A token provided during [`atomic_check`] callbacks for accessing > the crtc, atomic state, and new > +/// and old states of the crtc. > +/// > +/// # Invariants > +/// > +/// This token is proof that the old and new atomic state of `crtc` > are present in `state` and do > +/// not have any mutators taken out. > +/// > +/// [`atomic_check`]: DriverCrtc::atomic_check > +pub struct CrtcAtomicCheck<'a, T: DriverCrtc> { > +=C2=A0=C2=A0=C2=A0 state: &'a AtomicStateComposer, > +=C2=A0=C2=A0=C2=A0 crtc: &'a Crtc, > +} > + > +impl<'a, T: DriverCrtc> CrtcAtomicCheck<'a, T> { > +=C2=A0=C2=A0=C2=A0 impl_atomic_state_token_ops!( > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 CrtcAtomicCheck, > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 AtomicStateComposer, > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 Crtc, > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 use <'a, T> > +=C2=A0=C2=A0=C2=A0 ); > +} > + > +/// A token provided to [`DriverCrtc`] callbacks during the atomic > commit phase for accessing the > +/// crtc, atomic state, new and old states of the crtc. > +/// > +/// # Invariants > +/// > +/// This token is proof that the old and new atomic state of `crtc` > are present in `state` and do > +/// not have any mutators taken out. > +pub struct CrtcAtomicCommit<'a, T: DriverCrtc> { > +=C2=A0=C2=A0=C2=A0 state: &'a AtomicStateMutator, > +=C2=A0=C2=A0=C2=A0 crtc: &'a Crtc, > +} > + > +impl<'a, T: DriverCrtc> CrtcAtomicCommit<'a, T> { > +=C2=A0=C2=A0=C2=A0 impl_atomic_state_token_ops!( > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 CrtcAtomicCommit, > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 AtomicStateMutator, > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 Crtc, > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 use <'a, T> > +=C2=A0=C2=A0=C2=A0 ); > +} > + > +unsafe extern "C" fn crtc_destroy_callback(crtc: *mut > bindings::drm_crtc) { > +=C2=A0=C2=A0=C2=A0 // SAFETY: DRM guarantees that `crtc` points to a val= id > initialized `drm_crtc`. > +=C2=A0=C2=A0=C2=A0 unsafe { bindings::drm_crtc_cleanup(crtc) }; > + > +=C2=A0=C2=A0=C2=A0 // SAFETY: > +=C2=A0=C2=A0=C2=A0 // - DRM guarantees we are now the only one with acce= ss to this > [`drm_crtc`]. > +=C2=A0=C2=A0=C2=A0 // - This cast is safe via `DriverCrtc`s type invaria= nts. > +=C2=A0=C2=A0=C2=A0 // - We created this as a pinned type originally > +=C2=A0=C2=A0=C2=A0 drop(unsafe { Pin::new_unchecked(KBox::from_raw(crtc = as *mut > Crtc)) }); > +} > + > +unsafe extern "C" fn atomic_duplicate_state_callback DriverCrtcState>( > +=C2=A0=C2=A0=C2=A0 crtc: *mut bindings::drm_crtc, > +) -> *mut bindings::drm_crtc_state { > +=C2=A0=C2=A0=C2=A0 // SAFETY: DRM guarantees that `crtc` points to a val= id > initialized `drm_crtc`. > +=C2=A0=C2=A0=C2=A0 let state =3D unsafe { (*crtc).state }; > +=C2=A0=C2=A0=C2=A0 if state.is_null() { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 return null_mut(); > +=C2=A0=C2=A0=C2=A0 } > + > +=C2=A0=C2=A0=C2=A0 // SAFETY: This cast is safe via `DriverCrtcState`s t= ype > invariants. > +=C2=A0=C2=A0=C2=A0 let crtc =3D unsafe { Crtc::::from_raw(crtc)= }; > + > +=C2=A0=C2=A0=C2=A0 // SAFETY: This cast is safe via `DriverCrtcState`s t= ype > invariants. > +=C2=A0=C2=A0=C2=A0 let state =3D unsafe { CrtcState::::from_raw(state= ) }; > + > +=C2=A0=C2=A0=C2=A0 let new: Result> =3D KBox::try_init( > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 try_init!(CrtcState { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 inner= : UnsafeCell::new((*state).clone()), > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 state= : Opaque::new(Default::default()), > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 }), > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 GFP_KERNEL, > +=C2=A0=C2=A0=C2=A0 ); > + > +=C2=A0=C2=A0=C2=A0 if let Ok(new) =3D new { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 let new =3D KBox::into_raw(ne= w).cast(); > + > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // SAFETY: > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // - `new` provides a valid p= ointer to a newly allocated > `drm_crtc_state` via type > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // invariants > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // - This initializes `new` v= ia memcpy() > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 unsafe { > bindings::__drm_atomic_helper_crtc_duplicate_state(crtc.as_raw(), > new) } > + > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 new > +=C2=A0=C2=A0=C2=A0 } else { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 null_mut() > +=C2=A0=C2=A0=C2=A0 } > +} > + > +unsafe extern "C" fn atomic_destroy_state_callback DriverCrtcState>( > +=C2=A0=C2=A0=C2=A0 _crtc: *mut bindings::drm_crtc, > +=C2=A0=C2=A0=C2=A0 crtc_state: *mut bindings::drm_crtc_state, > +) { > +=C2=A0=C2=A0=C2=A0 // SAFETY: DRM guarantees that `state` points to a va= lid > instance of `drm_crtc_state` > +=C2=A0=C2=A0=C2=A0 unsafe { > bindings::__drm_atomic_helper_crtc_destroy_state(crtc_state) }; > + > +=C2=A0=C2=A0=C2=A0 // SAFETY: > +=C2=A0=C2=A0=C2=A0 // - DRM guarantees we are the only one with access t= o this > `drm_crtc_state` > +=C2=A0=C2=A0=C2=A0 // - This cast is safe via our type invariants. > +=C2=A0=C2=A0=C2=A0 // - All data in `CrtcState` is either Unpin, or pinn= ed > +=C2=A0=C2=A0=C2=A0 drop(unsafe { KBox::from_raw(crtc_state as *mut CrtcS= tate) > }); > +} > + > +unsafe extern "C" fn crtc_reset_callback(crtc: > *mut bindings::drm_crtc) { > +=C2=A0=C2=A0=C2=A0 // SAFETY: DRM guarantees that `state` points to a va= lid > instance of `drm_crtc_state` > +=C2=A0=C2=A0=C2=A0 let state =3D unsafe { (*crtc).state }; > +=C2=A0=C2=A0=C2=A0 if !state.is_null() { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // SAFETY: > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // - We're guaranteed `crtc` = is `Crtc` via type > invariants > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // - We're guaranteed `state`= is `CrtcState` via type > invariants. > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 unsafe { atomic_destroy_state= _callback::(crtc, state) } > + > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // SAFETY: No special require= ments here, DRM expects this to > be NULL > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 unsafe { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 (*crt= c).state =3D null_mut(); > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 } > +=C2=A0=C2=A0=C2=A0 } > + > +=C2=A0=C2=A0=C2=A0 // SAFETY: `crtc` is guaranteed to be of type `Crtc` by > type invariance > +=C2=A0=C2=A0=C2=A0 let crtc =3D unsafe { Crtc::::from_raw(crtc)= }; > + > +=C2=A0=C2=A0=C2=A0 // Unfortunately, this is the best we can do at the m= oment as > this FFI callback was mistakenly > +=C2=A0=C2=A0=C2=A0 // presumed to be infallible :( > +=C2=A0=C2=A0=C2=A0 let new: KBox> =3D KBox::try_init( > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 try_init!(CrtcState { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 state= : Opaque::new(Default::default()), > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 inner= : UnsafeCell::new(Default::default()), > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 }), > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 GFP_KERNEL, > +=C2=A0=C2=A0=C2=A0 ) > +=C2=A0=C2=A0=C2=A0 .expect("Unfortunately, this API was presumed infalli= ble"); > + > +=C2=A0=C2=A0=C2=A0 // SAFETY: DRM takes ownership of the state from here= , and will > never move it > +=C2=A0=C2=A0=C2=A0 unsafe { bindings::__drm_atomic_helper_crtc_reset(crt= c.as_raw(), > KBox::into_raw(new).cast()) }; > +} > + > +unsafe extern "C" fn atomic_check_callback( > +=C2=A0=C2=A0=C2=A0 crtc: *mut bindings::drm_crtc, > +=C2=A0=C2=A0=C2=A0 state: *mut bindings::drm_atomic_state, > +) -> i32 { > +=C2=A0=C2=A0=C2=A0 // SAFETY: > +=C2=A0=C2=A0=C2=A0 // - We're guaranteed `crtc` is of type `Crtc` via= type > invariants. > +=C2=A0=C2=A0=C2=A0 // - We're guaranteed by DRM that `crtc` is pointing = to a valid > initialized state. > +=C2=A0=C2=A0=C2=A0 let crtc =3D unsafe { Crtc::from_raw(crtc) }; > + > +=C2=A0=C2=A0=C2=A0 // SAFETY: DRM guarantees `state` points to a valid > `drm_atomic_state` > +=C2=A0=C2=A0=C2=A0 // We use a ManuallyDrop here to avoid AtomicStateCom= poser > dropping an owned reference we never > +=C2=A0=C2=A0=C2=A0 // acquired. > +=C2=A0=C2=A0=C2=A0 let state =3D > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 unsafe { > ManuallyDrop::new(AtomicStateComposer::new(NonNull::new_unchecked(sta > te))) }; > + > +=C2=A0=C2=A0=C2=A0 // SAFETY: > +=C2=A0=C2=A0=C2=A0 // - Since we're in the atomic check callback, we're = guaranteed > by DRM that both the old and > +=C2=A0=C2=A0=C2=A0 //=C2=A0=C2=A0 new crtc state are present in this ato= mic state > +=C2=A0=C2=A0=C2=A0 // - We just created the state composer above, so oth= er > composers cannot be taken out on the > +=C2=A0=C2=A0=C2=A0 //=C2=A0=C2=A0 crtc state yet. > +=C2=A0=C2=A0=C2=A0 let check =3D unsafe { CrtcAtomicCheck::new(crtc, &st= ate) }; > + > +=C2=A0=C2=A0=C2=A0 from_result(|| { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 T::atomic_check(check)?; > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 Ok(0) > +=C2=A0=C2=A0=C2=A0 }) > +} > + > +unsafe extern "C" fn atomic_begin_callback( > +=C2=A0=C2=A0=C2=A0 crtc: *mut bindings::drm_crtc, > +=C2=A0=C2=A0=C2=A0 state: *mut bindings::drm_atomic_state, > +) { > +=C2=A0=C2=A0=C2=A0 // SAFETY: > +=C2=A0=C2=A0=C2=A0 // * We're guaranteed `crtc` is of type `Crtc` via= type > invariants. > +=C2=A0=C2=A0=C2=A0 // * We're guaranteed by DRM that `crtc` is pointing = to a valid > initialized state. > +=C2=A0=C2=A0=C2=A0 let crtc =3D unsafe { Crtc::from_raw(crtc) }; > + > +=C2=A0=C2=A0=C2=A0 // SAFETY: We're guaranteed by DRM that `state` point= s to a > valid instance of `drm_atomic_state` > +=C2=A0=C2=A0=C2=A0 let state =3D unsafe { > AtomicStateMutator::new(NonNull::new_unchecked(state)) }; > + > +=C2=A0=C2=A0=C2=A0 // SAFETY: > +=C2=A0=C2=A0=C2=A0 // - Since we're in the atomic_begin callback, we're = guaranteed > by DRM that both the old and new > +=C2=A0=C2=A0=C2=A0 //=C2=A0=C2=A0 crtc state are resent in this atomic s= tate. > +=C2=A0=C2=A0=C2=A0 // - We just created the state mutator above, so othe= r mutators > cannot be taken out on the crtc > +=C2=A0=C2=A0=C2=A0 //=C2=A0=C2=A0 state yet. > +=C2=A0=C2=A0=C2=A0 let commit =3D unsafe { CrtcAtomicCommit::new(crtc, &= state) }; > + > +=C2=A0=C2=A0=C2=A0 T::atomic_begin(commit); > +} > + > +unsafe extern "C" fn atomic_flush_callback( > +=C2=A0=C2=A0=C2=A0 crtc: *mut bindings::drm_crtc, > +=C2=A0=C2=A0=C2=A0 state: *mut bindings::drm_atomic_state, > +) { > +=C2=A0=C2=A0=C2=A0 // SAFETY: > +=C2=A0=C2=A0=C2=A0 // - We're guaranteed `crtc` is of type `Crtc` via= type > invariants. > +=C2=A0=C2=A0=C2=A0 // - We're guaranteed by DRM that `crtc` is pointing = to a valid > initialized state. > +=C2=A0=C2=A0=C2=A0 let crtc =3D unsafe { Crtc::from_raw(crtc) }; > + > +=C2=A0=C2=A0=C2=A0 // SAFETY: We're guaranteed by DRM that `state` point= s to a > valid instance of `drm_atomic_state` > +=C2=A0=C2=A0=C2=A0 let state =3D unsafe { > AtomicStateMutator::new(NonNull::new_unchecked(state)) }; > + > +=C2=A0=C2=A0=C2=A0 // SAFETY: > +=C2=A0=C2=A0=C2=A0 // - Since we're in the atomic_flush callback, we're = guaranteed > by DRM that both the old and new > +=C2=A0=C2=A0=C2=A0 //=C2=A0=C2=A0 crtc state are resent in this atomic s= tate. > +=C2=A0=C2=A0=C2=A0 // - We just created the state mutator above, so othe= r mutators > cannot be taken out on the crtc > +=C2=A0=C2=A0=C2=A0 //=C2=A0=C2=A0 state yet. > +=C2=A0=C2=A0=C2=A0 let commit =3D unsafe { CrtcAtomicCommit::new(crtc, &= state) }; > + > +=C2=A0=C2=A0=C2=A0 T::atomic_flush(commit); > +} > + > +unsafe extern "C" fn atomic_enable_callback( > +=C2=A0=C2=A0=C2=A0 crtc: *mut bindings::drm_crtc, > +=C2=A0=C2=A0=C2=A0 state: *mut bindings::drm_atomic_state, > +) { > +=C2=A0=C2=A0=C2=A0 // SAFETY: > +=C2=A0=C2=A0=C2=A0 // - We're guaranteed `crtc` is of type `Crtc` via= type > invariants. > +=C2=A0=C2=A0=C2=A0 // - We're guaranteed by DRM that `crtc` is pointing = to a valid > initialized state. > +=C2=A0=C2=A0=C2=A0 let crtc =3D unsafe { Crtc::from_raw(crtc) }; > + > +=C2=A0=C2=A0=C2=A0 // SAFETY: DRM never passes an invalid ptr for `state= ` > +=C2=A0=C2=A0=C2=A0 let state =3D unsafe { > AtomicStateMutator::new(NonNull::new_unchecked(state)) }; > + > +=C2=A0=C2=A0=C2=A0 // SAFETY: > +=C2=A0=C2=A0=C2=A0 // - Since we're in the atomic_enable callback, we're= guaranteed > by DRM that both the old and > +=C2=A0=C2=A0=C2=A0 //=C2=A0=C2=A0 new crtc state are present in this ato= mic state. > +=C2=A0=C2=A0=C2=A0 // - We just created the state mutator above, so othe= r mutators > cannot be taken out on the crtc > +=C2=A0=C2=A0=C2=A0 //=C2=A0=C2=A0 state yet. > +=C2=A0=C2=A0=C2=A0 let commit =3D unsafe { CrtcAtomicCommit::new(crtc, &= state) }; > + > +=C2=A0=C2=A0=C2=A0 T::atomic_enable(commit); > +} > + > +unsafe extern "C" fn atomic_disable_callback( > +=C2=A0=C2=A0=C2=A0 crtc: *mut bindings::drm_crtc, > +=C2=A0=C2=A0=C2=A0 state: *mut bindings::drm_atomic_state, > +) { > +=C2=A0=C2=A0=C2=A0 // SAFETY: > +=C2=A0=C2=A0=C2=A0 // - We're guaranteed `crtc` points to a valid instan= ce of > `drm_crtc` > +=C2=A0=C2=A0=C2=A0 // - We're guaranteed that `crtc` is of type `Plane` by > `DriverPlane`s type invariants. > +=C2=A0=C2=A0=C2=A0 let crtc =3D unsafe { Crtc::from_raw(crtc) }; > + > +=C2=A0=C2=A0=C2=A0 // SAFETY: We're guaranteed that `state` points to a = valid > `drm_crtc_state` by DRM > +=C2=A0=C2=A0=C2=A0 let state =3D unsafe { > AtomicStateMutator::new(NonNull::new_unchecked(state)) }; > + > +=C2=A0=C2=A0=C2=A0 // SAFETY: > +=C2=A0=C2=A0=C2=A0 // - Since we're in the atomic_disable callback, we'r= e > guaranteed by DRM that both the old and > +=C2=A0=C2=A0=C2=A0 //=C2=A0=C2=A0 new crtc state are present in this ato= mic state. > +=C2=A0=C2=A0=C2=A0 // - We just created the state mutator above, so othe= r mutators > cannot be taken out on the crtc > +=C2=A0=C2=A0=C2=A0 //=C2=A0=C2=A0 state yet. > +=C2=A0=C2=A0=C2=A0 let commit =3D unsafe { CrtcAtomicCommit::new(crtc, &= state) }; > + > +=C2=A0=C2=A0=C2=A0 T::atomic_disable(commit); > +} > diff --git a/rust/kernel/drm/kms/encoder.rs > b/rust/kernel/drm/kms/encoder.rs > new file mode 100644 > index 000000000000..5f860faf8b61 > --- /dev/null > +++ b/rust/kernel/drm/kms/encoder.rs > @@ -0,0 +1,409 @@ > +// SPDX-License-Identifier: GPL-2.0 OR MIT > + > +//! DRM encoders. > +//! > +//! C header: > [`include/drm/drm_encoder.h`](srctree/include/drm/drm_encoder.h) > + > +use super::{ > +=C2=A0=C2=A0=C2=A0 KmsDriver, ModeObject, ModeObjectVtable, NewKmsDevice= , Probing, > StaticModeObject, Sealed > +}; > +use crate::{ > +=C2=A0=C2=A0=C2=A0 alloc::KBox, > +=C2=A0=C2=A0=C2=A0 drm::device::Device, > +=C2=A0=C2=A0=C2=A0 error::to_result, > +=C2=A0=C2=A0=C2=A0 prelude::*, > +=C2=A0=C2=A0=C2=A0 types::{NotThreadSafe, Opaque}, > +}; > +use bindings; > +use core::{ > +=C2=A0=C2=A0=C2=A0 marker::*, > +=C2=A0=C2=A0=C2=A0 mem, > +=C2=A0=C2=A0=C2=A0 ops::Deref, > +=C2=A0=C2=A0=C2=A0 ptr::null, > +}; > +use macros::paste; > + > +/// A macro for generating our type ID enumerator. > +macro_rules! declare_encoder_types { > +=C2=A0=C2=A0=C2=A0 ($( $oldname:ident as $newname:ident ),+) =3D> { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 #[repr(i32)] > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 #[non_exhaustive] > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 #[derive(Copy, Clone, Partial= Eq, Eq)] > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 /// An enumerator for all pos= sible [`Encoder`] type IDs. > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 pub enum Type { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // No= te: bindgen defaults the macro values to u32 and > not i32, but DRM takes them as an > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // i3= 2 - so just do the conversion here > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 $( > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0= =C2=A0=C2=A0=C2=A0 #[doc =3D concat!("The encoder type ID for a ", > stringify!($newname), " encoder.")] > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0= =C2=A0=C2=A0=C2=A0 $newname =3D > paste!(crate::bindings::[]) as i32 > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 ),+ > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 } > +=C2=A0=C2=A0=C2=A0 }; > +} > + > +declare_encoder_types! { > +=C2=A0=C2=A0=C2=A0 NONE=C2=A0=C2=A0=C2=A0=C2=A0 as None, > +=C2=A0=C2=A0=C2=A0 DAC=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 as Dac, > +=C2=A0=C2=A0=C2=A0 TMDS=C2=A0=C2=A0=C2=A0=C2=A0 as Tmds, > +=C2=A0=C2=A0=C2=A0 LVDS=C2=A0=C2=A0=C2=A0=C2=A0 as Lvds, > +=C2=A0=C2=A0=C2=A0 VIRTUAL=C2=A0 as Virtual, > +=C2=A0=C2=A0=C2=A0 DSI=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 as Dsi, > +=C2=A0=C2=A0=C2=A0 DPMST=C2=A0=C2=A0=C2=A0 as DpMst, > +=C2=A0=C2=A0=C2=A0 DPI=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 as Dpi > +} > + > +/// The main trait for implementing the [`struct drm_encoder`] API > for [`Encoder`]. > +/// > +/// Any KMS driver should have at least one implementation of this > type, which allows them to create > +/// [`Encoder`] objects. Additionally, a driver may store driver- > private data within the type that > +/// implements [`DriverEncoder`] - and it will be made available > when using a fully typed > +/// [`Encoder`] object. > +/// > +/// # Invariants > +/// > +/// - Any C FFI callbacks generated using this trait are guaranteed > that passed-in > +///=C2=A0=C2=A0 [`struct drm_encoder`] pointers are contained within a > [`Encoder`]. > +/// > +/// [`struct drm_encoder`]: srctree/include/drm/drm_encoder.h > +#[vtable] > +pub trait DriverEncoder: Send + Sync + Sized { > +=C2=A0=C2=A0=C2=A0 /// The generated C vtable for this [`DriverEncoder`] > implementation. > +=C2=A0=C2=A0=C2=A0 const OPS: &'static DriverEncoderOps =3D &DriverEncod= erOps { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 funcs: bindings::drm_encoder_= funcs { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 reset= : None, > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 destr= oy: Some(encoder_destroy_callback::), > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 late_= register: None, > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 early= _unregister: None, > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 debug= fs_init: None, > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 }, > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 helper_funcs: bindings::drm_e= ncoder_helper_funcs { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 dpms:= None, > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 mode_= valid: None, > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 mode_= fixup: None, > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 prepa= re: None, > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 mode_= set: None, > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 commi= t: None, > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 detec= t: None, > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 enabl= e: None, > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 disab= le: None, > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 atomi= c_check: None, > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 atomi= c_enable: None, > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 atomi= c_disable: None, > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 atomi= c_mode_set: None, > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 }, > +=C2=A0=C2=A0=C2=A0 }; > + > +=C2=A0=C2=A0=C2=A0 /// The parent driver for this drm_encoder implementa= tion > +=C2=A0=C2=A0=C2=A0 type Driver: KmsDriver; > + > +=C2=A0=C2=A0=C2=A0 /// The type to pass to the `args` field of > [`UnregisteredEncoder::new`]. > +=C2=A0=C2=A0=C2=A0 /// > +=C2=A0=C2=A0=C2=A0 /// This type will be made available in in the `args`= argument > of [`Self::new`]. Drivers which > +=C2=A0=C2=A0=C2=A0 /// don't need this can simply pass [`()`] here. > +=C2=A0=C2=A0=C2=A0 type Args; > + > +=C2=A0=C2=A0=C2=A0 /// The constructor for creating a [`Encoder`] using = this > [`DriverEncoder`] implementation. > +=C2=A0=C2=A0=C2=A0 /// > +=C2=A0=C2=A0=C2=A0 /// Drivers may use this to instantiate their [`Drive= rEncoder`] > object. > +=C2=A0=C2=A0=C2=A0 fn new(device: &Device, args: Self::Arg= s) -> impl > PinInit; > +} > + > +/// The generated C vtable for a [`DriverEncoder`]. > +/// > +/// This type is created internally by DRM. > +pub struct DriverEncoderOps { > +=C2=A0=C2=A0=C2=A0 funcs: bindings::drm_encoder_funcs, > +=C2=A0=C2=A0=C2=A0 helper_funcs: bindings::drm_encoder_helper_funcs, > +} > + > +/// A trait implemented by any type that acts as a [`struct > drm_encoder`] interface. > +/// > +/// This is implemented internally by DRM. > +/// > +/// # Safety > +/// > +/// [`as_raw()`] must always return a valid pointer to a [`struct > drm_encoder`]. > +/// > +/// [`struct drm_encoder`]: srctree/include/drm/drm_encoder.h > +/// [`as_raw()`]: AsRawEncoder::as_raw() > +pub unsafe trait AsRawEncoder { > +=C2=A0=C2=A0=C2=A0 /// Return the raw `bindings::drm_encoder` for this D= RM encoder. > +=C2=A0=C2=A0=C2=A0 /// > +=C2=A0=C2=A0=C2=A0 /// Drivers should never use this directly > +=C2=A0=C2=A0=C2=A0 fn as_raw(&self) -> *mut bindings::drm_encoder; > + > +=C2=A0=C2=A0=C2=A0 /// Convert a raw `bindings::drm_encoder` pointer int= o an object > of this type. > +=C2=A0=C2=A0=C2=A0 /// > +=C2=A0=C2=A0=C2=A0 /// # Safety > +=C2=A0=C2=A0=C2=A0 /// > +=C2=A0=C2=A0=C2=A0 /// Callers promise that `ptr` points to a valid inst= ance of > this type > +=C2=A0=C2=A0=C2=A0 unsafe fn from_raw<'a>(ptr: *mut bindings::drm_encode= r) -> &'a > Self; > +} > + > +/// The main interface for a [`struct drm_encoder`]. > +/// > +/// This type is the main interface for dealing with DRM encoders. > In addition, it also allows > +/// immutable access to whatever private data is contained within an > implementor's > +/// [`DriverEncoder`] type. > +/// > +/// # Invariants > +/// > +/// - `encoder` and `inner` are initialized for as long as this > object is made available to users. > +/// - The data layout of this structure begins with [`struct > drm_encoder`]. > +/// > +/// [`struct drm_encoder`]: srctree/include/drm/drm_encoder.h > +#[repr(C)] > +#[pin_data] > +pub struct Encoder { > +=C2=A0=C2=A0=C2=A0 /// The FFI drm_encoder object > +=C2=A0=C2=A0=C2=A0 encoder: Opaque, > +=C2=A0=C2=A0=C2=A0 /// The driver's private inner data > +=C2=A0=C2=A0=C2=A0 #[pin] > +=C2=A0=C2=A0=C2=A0 inner: T, > +=C2=A0=C2=A0=C2=A0 #[pin] > +=C2=A0=C2=A0=C2=A0 _p: PhantomPinned, > +} > + > +impl Sealed for Encoder {} > + > +// SAFETY: Our interface is thread-safe. > +unsafe impl Send for Encoder {} > +// SAFETY: Our interface is thread-safe. > +unsafe impl Sync for Encoder {} > + > +// SAFETY: We don't expose Encoder to users before `base` is > initialized in ::new(), so > +// `raw_mode_obj` always returns a valid pointer to a > bindings::drm_mode_object. > +unsafe impl ModeObject for Encoder { > +=C2=A0=C2=A0=C2=A0 type Driver =3D T::Driver; > + > +=C2=A0=C2=A0=C2=A0 fn drm_dev(&self) -> &Device { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // SAFETY: DRM encoders exist= for as long as the device > does, so this pointer is always > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // valid > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 unsafe { Device::from_raw((*s= elf.encoder.get()).dev) } > +=C2=A0=C2=A0=C2=A0 } > + > +=C2=A0=C2=A0=C2=A0 fn raw_mode_obj(&self) -> *mut bindings::drm_mode_obj= ect { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // SAFETY: We don't expose En= coder to users before it's > initialized, so `base` is always > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // initialized > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 unsafe { &raw mut (*self.enco= der.get()).base } > +=C2=A0=C2=A0=C2=A0 } > +} > + > +// SAFETY: Encoders do not have a refcount > +unsafe impl StaticModeObject for Encoder {} > + > +impl Deref for Encoder { > +=C2=A0=C2=A0=C2=A0 type Target =3D T; > + > +=C2=A0=C2=A0=C2=A0 fn deref(&self) -> &Self::Target { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 &self.inner > +=C2=A0=C2=A0=C2=A0 } > +} > + > +// SAFETY: > +// - Via our type invariants our data layout starts with > `drm_encoder`. > +// - Since we don't expose `Encoder` to users befre it has been > initialized, this and our data > +//=C2=A0=C2=A0 layout ensure that `as_raw()` always returns a valid poin= ter to > a `drm_encoder`. > +unsafe impl AsRawEncoder for Encoder { > +=C2=A0=C2=A0=C2=A0 fn as_raw(&self) -> *mut bindings::drm_encoder { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 self.encoder.get() > +=C2=A0=C2=A0=C2=A0 } > + > +=C2=A0=C2=A0=C2=A0 unsafe fn from_raw<'a>(ptr: *mut bindings::drm_encode= r) -> &'a > Self { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // SAFETY: Our data layout is= starts with to > `bindings::drm_encoder` > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 unsafe { &*ptr.cast() } > +=C2=A0=C2=A0=C2=A0 } > +} > + > +// SAFETY: `funcs` is initialized when the encoder is allocated > +unsafe impl ModeObjectVtable for Encoder { > +=C2=A0=C2=A0=C2=A0 type Vtable =3D bindings::drm_encoder_funcs; > + > +=C2=A0=C2=A0=C2=A0 fn vtable(&self) -> *const Self::Vtable { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // SAFETY: `as_raw()` always = returns a valid pointer to an > encoder > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 unsafe { *self.as_raw() }.fun= cs > +=C2=A0=C2=A0=C2=A0 } > +} > + > +impl Encoder { > +=C2=A0=C2=A0=C2=A0 super::impl_from_opaque_mode_obj! { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 fn <'a, D>(&'a OpaqueEncoder<= D>) -> &'a Self; > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 use > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 T as = DriverEncoder, > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 D as = KmsDriver > +=C2=A0=C2=A0=C2=A0 } > +} > + > +/// A [`Encoder`] that has not yet been registered with userspace. > +/// > +/// KMS registration is single-threaded, so this object is not > thread-safe. > +/// > +/// # Invariants > +/// > +/// - This object can only exist before its respective KMS device > has been registered. > +/// - Otherwise, it inherits all invariants of [`Encoder`] and has > an identical data layout. > +pub struct UnregisteredEncoder(Encoder, > NotThreadSafe); > + > +// SAFETY: We inherit all relevant invariants of `Encoder` > +unsafe impl AsRawEncoder for > UnregisteredEncoder { > +=C2=A0=C2=A0=C2=A0 fn as_raw(&self) -> *mut bindings::drm_encoder { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 self.0.as_raw() > +=C2=A0=C2=A0=C2=A0 } > + > +=C2=A0=C2=A0=C2=A0 unsafe fn from_raw<'a>(ptr: *mut bindings::drm_encode= r) -> &'a > Self { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // SAFETY: This is another fr= om_raw() call, so this function > shares the same safety contract > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 let encoder =3D unsafe { Enco= der::::from_raw(ptr) }; > + > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // SAFETY: Our data layout is= identical via our type > invariants. > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 unsafe { mem::transmute(encod= er) } > +=C2=A0=C2=A0=C2=A0 } > +} > + > +impl Deref for UnregisteredEncoder { > +=C2=A0=C2=A0=C2=A0 type Target =3D T; > + > +=C2=A0=C2=A0=C2=A0 fn deref(&self) -> &Self::Target { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 &self.0.inner > +=C2=A0=C2=A0=C2=A0 } > +} > + > +impl UnregisteredEncoder { > +=C2=A0=C2=A0=C2=A0 /// Construct a new [`UnregisteredEncoder`]. > +=C2=A0=C2=A0=C2=A0 /// > +=C2=A0=C2=A0=C2=A0 /// A driver may use this from their > [`KmsDriver::create_objects`] callback in order to > +=C2=A0=C2=A0=C2=A0 /// construct new [`UnregisteredEncoder`] objects. > +=C2=A0=C2=A0=C2=A0 /// > +=C2=A0=C2=A0=C2=A0 /// [`KmsDriver::create_objects`]: > kernel::drm::kms::KmsDriver::create_objects > +=C2=A0=C2=A0=C2=A0 pub fn new<'a, 'b: 'a>( > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 dev: &'a NewKmsDevice<'a, T::= Driver, Probing>, > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 type_: Type, > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 possible_crtcs: u32, > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 possible_clones: u32, > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 name: Option<&CStr>, > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 args: T::Args, > +=C2=A0=C2=A0=C2=A0 ) -> Result<&'b Self> { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 let this: Pin= >> =3D KBox::try_pin_init( > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 try_p= in_init!(Encoder { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0= =C2=A0=C2=A0=C2=A0 encoder: Opaque::new(bindings::drm_encoder { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0= =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 helper_private: &T::OPS.helper_f= uncs, > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0= =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 possible_crtcs, > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0= =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 possible_clones, > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0= =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 ..Default::default() > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0= =C2=A0=C2=A0=C2=A0 }), > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0= =C2=A0=C2=A0=C2=A0 inner <- T::new(dev, args), > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0= =C2=A0=C2=A0=C2=A0 _p: PhantomPinned > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 }), > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 GFP_K= ERNEL, > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 )?; > + > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // SAFETY: > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // - `dev` is responsible for= destroying the encoder and > thus outlives us. > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // - as_raw() returns valid p= ointers for each type here > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // - This initializes `this` > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // - Our type is proof that t= his is being called before KMS > device registration > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // - `name` is optional and w= ill be auto-generated by DRM if > passed as NULL > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 to_result(unsafe { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 bindi= ngs::drm_encoder_init( > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0= =C2=A0=C2=A0=C2=A0 dev.as_raw(), > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0= =C2=A0=C2=A0=C2=A0 this.as_raw(), > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0= =C2=A0=C2=A0=C2=A0 &T::OPS.funcs, > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0= =C2=A0=C2=A0=C2=A0 type_ as _, > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0= =C2=A0=C2=A0=C2=A0 name.map_or(null(), |n| n.as_char_ptr()), > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 ) > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 })?; > + > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // SAFETY: We don't move anyt= hing > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 let this =3D unsafe { Pin::in= to_inner_unchecked(this) }; > + > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // We'll re-assemble the box = in encoder_destroy_callback() > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 let this =3D KBox::into_raw(t= his); > + > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // UnregisteredEncoder has an= equivalent data layout > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 let this: *mut Self =3D this.= cast(); > + > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // SAFETY: We just allocated = the encoder above, so this > pointer must be valid > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 Ok(unsafe { &*this }) > +=C2=A0=C2=A0=C2=A0 } > +} > + > +/// A [`struct drm_encoder`] without a known [`DriverEncoder`] > implementation. > +/// > +/// This is mainly for situations where our bindings can't infer the > [`DriverEncoder`] implementation > +/// for a [`struct drm_encoder`] automatically. It is identical to > [`Encoder`], except that it does not > +/// provide access to the driver's private data. > +/// > +/// # Invariants > +/// > +/// Same as [`Encoder`]. > +#[repr(transparent)] > +pub struct OpaqueEncoder { > +=C2=A0=C2=A0=C2=A0 encoder: Opaque, > +=C2=A0=C2=A0=C2=A0 _p: PhantomData, > +} > + > +impl Sealed for OpaqueEncoder {} > + > +// SAFETY: All of our encoder interfaces are thread-safe > +unsafe impl Send for OpaqueEncoder {} > + > +// SAFETY: All of our encoder interfaces are thread-safe > +unsafe impl Sync for OpaqueEncoder {} > + > +// SAFETY: We don't expose OpaqueEncoder to users before `base` > is initialized in > +// OpaqueEncoder::new(), so `raw_mode_obj` always returns a valid > poiner to a > +// bindings::drm_mode_object. > +unsafe impl ModeObject for OpaqueEncoder { > +=C2=A0=C2=A0=C2=A0 type Driver =3D T; > + > +=C2=A0=C2=A0=C2=A0 fn drm_dev(&self) -> &Device { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // SAFETY: DRM encoders exist= for as long as the device > does, so this pointer is always > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // valid > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 unsafe { Device::from_raw((*s= elf.encoder.get()).dev) } > +=C2=A0=C2=A0=C2=A0 } > + > +=C2=A0=C2=A0=C2=A0 fn raw_mode_obj(&self) -> *mut bindings::drm_mode_obj= ect { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // SAFETY: We don't expose En= coder to users before it's > initialized, so `base` is always > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // initialized > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 unsafe { &raw mut (*self.enco= der.get()).base } > +=C2=A0=C2=A0=C2=A0 } > +} > + > +// SAFETY: Encoders do not have a refcount > +unsafe impl StaticModeObject for OpaqueEncoder {} > + > +// SAFETY: > +// - Via our type variants our data layout is identical to=C2=A0 with > `drm_encoder` > +// - Since we don't expose `Encoder` to users before it has been > initialized, this and our data > +//=C2=A0=C2=A0 layout ensure that `as_raw()` always returns a valid poin= ter to > a `drm_encoder`. > +unsafe impl AsRawEncoder for OpaqueEncoder { > +=C2=A0=C2=A0=C2=A0 fn as_raw(&self) -> *mut bindings::drm_encoder { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 self.encoder.get() > +=C2=A0=C2=A0=C2=A0 } > + > +=C2=A0=C2=A0=C2=A0 unsafe fn from_raw<'a>(ptr: *mut bindings::drm_encode= r) -> &'a > Self { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // SAFETY: Our data layout is= identical to > `bindings::drm_encoder` > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 unsafe { &*ptr.cast() } > +=C2=A0=C2=A0=C2=A0 } > +} > + > +// SAFETY: `funcs` is initialized when the encoder is allocated > +unsafe impl ModeObjectVtable for OpaqueEncoder { > +=C2=A0=C2=A0=C2=A0 type Vtable =3D bindings::drm_encoder_funcs; > + > +=C2=A0=C2=A0=C2=A0 fn vtable(&self) -> *const Self::Vtable { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // SAFETY: `as_raw()` always = returns a valid pointer to an > encoder > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 unsafe { *self.as_raw() }.fun= cs > +=C2=A0=C2=A0=C2=A0 } > +} > + > +unsafe extern "C" fn encoder_destroy_callback( > +=C2=A0=C2=A0=C2=A0 encoder: *mut bindings::drm_encoder, > +) { > +=C2=A0=C2=A0=C2=A0 // SAFETY: DRM guarantees that `encoder` points to a = valid > initialized `drm_encoder`. > +=C2=A0=C2=A0=C2=A0 unsafe { bindings::drm_encoder_cleanup(encoder) }; > + > +=C2=A0=C2=A0=C2=A0 // SAFETY: > +=C2=A0=C2=A0=C2=A0 // - DRM guarantees we are now the only one with acce= ss to this > [`drm_encoder`]. > +=C2=A0=C2=A0=C2=A0 // - This cast is safe via `DriverEncoder`s type inva= riants. > +=C2=A0=C2=A0=C2=A0 unsafe { drop(KBox::from_raw(encoder as *mut Encoder<= T>)) }; > +} > diff --git a/rust/kernel/drm/kms/framebuffer.rs > b/rust/kernel/drm/kms/framebuffer.rs > new file mode 100644 > index 000000000000..54d0391388a9 > --- /dev/null > +++ b/rust/kernel/drm/kms/framebuffer.rs > @@ -0,0 +1,70 @@ > +// SPDX-License-Identifier: GPL-2.0 OR MIT > + > +//! DRM framebuffers. > +//! > +//! C header: > [`include/drm/drm_framebuffer.h`](srctree/include/drm/drm_framebuffer > .h) > + > +use super::{KmsDriver, ModeObject, Sealed}; > +use crate::{drm::device::Device, types::*}; > +use bindings; > +use core::{marker::*, ptr}; > + > +/// The main interface for [`struct drm_framebuffer`]. > +/// > +/// # Invariants > +/// > +/// - `self.0` is initialized for as long as this object is exposed > to users. > +/// - This type has an identical data layout to [`struct > drm_framebuffer`] > +/// > +/// [`struct drm_framebuffer`]: > srctree/include/drm/drm_framebuffer.h > +#[repr(transparent)] > +pub struct Framebuffer KmsDriver>(Opaque, PhantomData); > + > +// SAFETY: > +// - `self.0` is initialized for as long as this object is exposed > to users > +// - `base` is initialized by DRM when `self.0` is initialized, thus > `raw_mode_obj()` always returns > +//=C2=A0=C2=A0 a valid pointer. > +unsafe impl ModeObject for Framebuffer { > +=C2=A0=C2=A0=C2=A0 type Driver =3D T; > + > +=C2=A0=C2=A0=C2=A0 fn drm_dev(&self) -> &Device { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // SAFETY: `dev` points to an= initialized `struct > drm_device` for as long as this type is > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // initialized > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 unsafe { Device::from_raw((*s= elf.0.get()).dev) } > +=C2=A0=C2=A0=C2=A0 } > + > +=C2=A0=C2=A0=C2=A0 fn raw_mode_obj(&self) -> *mut bindings::drm_mode_obj= ect { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // SAFETY: We don't expose Fr= amebuffer to users before > its initialized, so `base` is > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // always initialized > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 unsafe { &raw mut (*self.0.ge= t()).base } > +=C2=A0=C2=A0=C2=A0 } > +} > + > +// SAFETY: References to framebuffers are safe to be accessed from > any thread > +unsafe impl Send for Framebuffer {} > +// SAFETY: References to framebuffers are safe to be accessed from > any thread > +unsafe impl Sync for Framebuffer {} > + > +// For implementing ModeObject > +impl Sealed for Framebuffer {} > + > +impl PartialEq for Framebuffer { > +=C2=A0=C2=A0=C2=A0 fn eq(&self, other: &Self) -> bool { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 ptr::eq(self.0.get(), other.0= .get()) > +=C2=A0=C2=A0=C2=A0 } > +} > +impl Eq for Framebuffer {} > + > +impl Framebuffer { > +=C2=A0=C2=A0=C2=A0 /// Convert a raw pointer to a `struct drm_framebuffe= r` into a > [`Framebuffer`] > +=C2=A0=C2=A0=C2=A0 /// > +=C2=A0=C2=A0=C2=A0 /// # Safety > +=C2=A0=C2=A0=C2=A0 /// > +=C2=A0=C2=A0=C2=A0 /// The caller guarantews that `ptr` points to a init= ialized > `struct drm_framebuffer` for at > +=C2=A0=C2=A0=C2=A0 /// least the entire lifetime of `'a`. > +=C2=A0=C2=A0=C2=A0 #[inline] > +=C2=A0=C2=A0=C2=A0 pub(super) unsafe fn from_raw<'a>(ptr: *const > bindings::drm_framebuffer) -> &'a Self { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // SAFETY: Our data layout is= identical to drm_framebuffer > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 unsafe { &*ptr.cast() } > +=C2=A0=C2=A0=C2=A0 } > +} > diff --git a/rust/kernel/drm/kms/modes.rs > b/rust/kernel/drm/kms/modes.rs > new file mode 100644 > index 000000000000..0e8dc434487d > --- /dev/null > +++ b/rust/kernel/drm/kms/modes.rs > @@ -0,0 +1,76 @@ > +// SPDX-License-Identifier: GPL-2.0 > +use bindings; > + > +use crate::types::Opaque; > + > +/// DRM kernel-internal display mode structure. > +/// > +/// This structure contains various resolution and timing > information for a given display mode in > +/// DRM. > +/// > +/// # Invariants > +/// > +/// - The data layout of this structure is guaranteed to be > equivalent to that of `struct > +///=C2=A0=C2=A0 drm_display_mode`. > +/// - We ensure through our bindings that rust's data aliasing rules > are maintained, ensuring it is > +///=C2=A0=C2=A0 safe to read any fields inside of `self.inner`. > +#[repr(transparent)] > +pub struct DisplayMode { > +=C2=A0=C2=A0=C2=A0 inner: Opaque, > +} > + > +// SAFETY: Our bindings are thread-safe via our type invariants. > +unsafe impl Send for DisplayMode {} > +// SAFETY: Our bindings are thread-safe via our type invariants. > +unsafe impl Sync for DisplayMode {} > + > +impl DisplayMode { > +=C2=A0=C2=A0=C2=A0 /// Convert a raw pointer to a `struct drm_display_mo= de` into an > immutable [`DisplayMode`] ref. > +=C2=A0=C2=A0=C2=A0 /// > +=C2=A0=C2=A0=C2=A0 /// # SAFETY > +=C2=A0=C2=A0=C2=A0 /// > +=C2=A0=C2=A0=C2=A0 /// - The caller guarantees that `self_ptr` points to= a valid > initialized `struct > +=C2=A0=C2=A0=C2=A0 ///=C2=A0=C2=A0 drm_display_mode`. > +=C2=A0=C2=A0=C2=A0 /// - The caller must ensure that rust's data aliasin= g rules > will not be broken for the lifetime > +=C2=A0=C2=A0=C2=A0 ///=C2=A0=C2=A0 of `'a`, e.g. no mutable references m= ay exist while > immutable references exist to Self. > +=C2=A0=C2=A0=C2=A0 #[inline] > +=C2=A0=C2=A0=C2=A0 pub(crate) unsafe fn as_ref<'a>(self_ptr: *const > bindings::drm_display_mode) -> &'a Self { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // SAFETY: The pointer is val= id via our safety contract, and > the data layout of this struct > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // is equivalent to `Self` vi= a our type invariants. > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 unsafe { &*self_ptr.cast() } > +=C2=A0=C2=A0=C2=A0 } > + > +=C2=A0=C2=A0=C2=A0 /// Return a raw pointer to the `struct drm_display_m= ode` > contained within this [`DisplayMode`]. > +=C2=A0=C2=A0=C2=A0 #[inline] > +=C2=A0=C2=A0=C2=A0 pub(crate) fn as_raw(&self) -> *const bindings::drm_d= isplay_mode > { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 self.inner.get().cast_const() > +=C2=A0=C2=A0=C2=A0 } > + > +=C2=A0=C2=A0=C2=A0 /// Retrieve the pixel clock for the adjusted display= mode in > kHz. > +=C2=A0=C2=A0=C2=A0 #[inline] > +=C2=A0=C2=A0=C2=A0 pub fn crtc_clock(&self) -> i32 { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // SAFETY: Reading these fiel= ds is safe via our type > invariants > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 unsafe { (*self.as_raw()).crt= c_clock } > +=C2=A0=C2=A0=C2=A0 } > + > +=C2=A0=C2=A0=C2=A0 /// Retrieve the start of the vertical sync period fo= r the > adjusted display mode. > +=C2=A0=C2=A0=C2=A0 #[inline] > +=C2=A0=C2=A0=C2=A0 pub fn crtc_vblank_start(&self) -> u16 { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 unsafe { (*self.as_raw()).crt= c_vblank_start } > +=C2=A0=C2=A0=C2=A0 } > + > +=C2=A0=C2=A0=C2=A0 /// Retrieve the end of the vertical sync period for = the > adjusted display mode. > +=C2=A0=C2=A0=C2=A0 #[inline] > +=C2=A0=C2=A0=C2=A0 pub fn crtc_vblank_end(&self) -> u16 { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // SAFETY: Reading these fiel= ds is safe via our type > invariants > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 unsafe { (*self.as_raw()).crt= c_vblank_end } > +=C2=A0=C2=A0=C2=A0 } > + > +=C2=A0=C2=A0=C2=A0 /// Retrieve the number of vertical scanlines for a f= ull scanout > frame in this adjusted display > +=C2=A0=C2=A0=C2=A0 /// mode. > +=C2=A0=C2=A0=C2=A0 #[inline] > +=C2=A0=C2=A0=C2=A0 pub fn crtc_vtotal(&self) -> u16 { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // SAFETY: Reading these fiel= ds is safe via our type > invariants > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 unsafe { (*self.as_raw()).crt= c_vtotal } > +=C2=A0=C2=A0=C2=A0 } > +} > diff --git a/rust/kernel/drm/kms/plane.rs > b/rust/kernel/drm/kms/plane.rs > new file mode 100644 > index 000000000000..661d82273099 > --- /dev/null > +++ b/rust/kernel/drm/kms/plane.rs > @@ -0,0 +1,1095 @@ > +// SPDX-License-Identifier: GPL-2.0 OR MIT > + > +//! DRM display planes. > +//! > +//! C header: > [`include/drm/drm_plane.h`](srctree/include/drm/drm_plane.h) > + > +use super::{ > +=C2=A0=C2=A0=C2=A0 atomic::*, crtc::*, framebuffer::*, KmsDriver, ModeOb= ject, > ModeObjectVtable, StaticModeObject, > +=C2=A0=C2=A0=C2=A0 NewKmsDevice, Probing, Sealed > +}; > +use crate::{ > +=C2=A0=C2=A0=C2=A0 alloc::KBox, > +=C2=A0=C2=A0=C2=A0 bindings, > +=C2=A0=C2=A0=C2=A0 drm::{device::Device, fourcc::*}, > +=C2=A0=C2=A0=C2=A0 error::{from_result, to_result, Error}, > +=C2=A0=C2=A0=C2=A0 prelude::*, > +=C2=A0=C2=A0=C2=A0 types::{NotThreadSafe, Opaque}, > +}; > +use core::{ > +=C2=A0=C2=A0=C2=A0 cell::Cell, > +=C2=A0=C2=A0=C2=A0 marker::*, > +=C2=A0=C2=A0=C2=A0 mem::{self, ManuallyDrop}, > +=C2=A0=C2=A0=C2=A0 ops::*, > +=C2=A0=C2=A0=C2=A0 pin::Pin, > +=C2=A0=C2=A0=C2=A0 ptr::{null, null_mut, NonNull}, > +}; > + > +/// The main trait for implementing the [`struct drm_plane`] API for > [`Plane`]. > +/// > +/// Any KMS driver should have at least one implementation of this > type, which allows them to create > +/// [`Plane`] objects. Additionally, a driver may store driver- > private data within the type that > +/// implements [`DriverPlane`] - and it will be made available when > using a fully typed [`Plane`] > +/// object. > +/// > +/// # Invariants > +/// > +/// - Any C FFI callbacks generated using this trait are guaranteed > that passed-in > +///=C2=A0=C2=A0 [`struct drm_plane`] pointers are contained within a > [`Plane`]. > +/// - Any C FFI callbacks generated using this trait are guaranteed > that passed-in > +///=C2=A0=C2=A0 [`struct drm_plane_state`] pointers are contained within= a > [`PlaneState`]. > +/// > +/// [`struct drm_plane`]: srctree/include/drm/drm_plane.h > +/// [`struct drm_plane_state`]: srctree/include/drm/drm_plane.h > +#[vtable] > +pub trait DriverPlane: Send + Sync + Sized { > +=C2=A0=C2=A0=C2=A0 /// The generated C vtable for this [`DriverPlane`] > implementation. > +=C2=A0=C2=A0=C2=A0 const OPS: &'static DriverPlaneOps =3D &DriverPlaneOp= s { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 funcs: bindings::drm_plane_fu= ncs { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 updat= e_plane: > Some(bindings::drm_atomic_helper_update_plane), > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 disab= le_plane: > Some(bindings::drm_atomic_helper_disable_plane), > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 destr= oy: Some(plane_destroy_callback::), > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 reset= : Some(plane_reset_callback::), > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 set_p= roperty: None, > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 atomi= c_duplicate_state: > Some(atomic_duplicate_state_callback::), > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 atomi= c_destroy_state: > Some(atomic_destroy_state_callback::), > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 atomi= c_set_property: None, > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 atomi= c_get_property: None, > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 late_= register: None, > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 early= _unregister: None, > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 atomi= c_print_state: None, > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 forma= t_mod_supported: None, > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 forma= t_mod_supported_async: None, > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 }, > + > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 helper_funcs: bindings::drm_p= lane_helper_funcs { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 prepa= re_fb: None, > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 clean= up_fb: None, > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 begin= _fb_access: None, > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 end_f= b_access: None, > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 atomi= c_check: if Self::HAS_ATOMIC_CHECK { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0= =C2=A0=C2=A0=C2=A0 Some(atomic_check_callback::) > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 } els= e { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0= =C2=A0=C2=A0=C2=A0 None > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 }, > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 atomi= c_update: if Self::HAS_ATOMIC_UPDATE { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0= =C2=A0=C2=A0=C2=A0 Some(atomic_update_callback::) > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 } els= e { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0= =C2=A0=C2=A0=C2=A0 None > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 }, > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 atomi= c_enable: None, > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 atomi= c_disable: None, > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 atomi= c_async_check: None, > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 atomi= c_async_update: None, > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 panic= _flush: None, > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 get_s= canout_buffer: None, > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 }, > +=C2=A0=C2=A0=C2=A0 }; > + > +=C2=A0=C2=A0=C2=A0 /// The type to pass to the `args` field of > [`UnregisteredPlane::new`]. > +=C2=A0=C2=A0=C2=A0 /// > +=C2=A0=C2=A0=C2=A0 /// This type will be made available in in the `args`= argument > of [`Self::new`]. Drivers which > +=C2=A0=C2=A0=C2=A0 /// don't need this can simply pass [`()`] here. > +=C2=A0=C2=A0=C2=A0 type Args; > + > +=C2=A0=C2=A0=C2=A0 /// The parent [`KmsDriver`] implementation. > +=C2=A0=C2=A0=C2=A0 type Driver: KmsDriver; > + > +=C2=A0=C2=A0=C2=A0 /// The [`DriverPlaneState`] implementation for this > [`DriverPlane`]. > +=C2=A0=C2=A0=C2=A0 /// > +=C2=A0=C2=A0=C2=A0 /// See [`DriverPlaneState`] for more info. > +=C2=A0=C2=A0=C2=A0 type State: DriverPlaneState; > + > +=C2=A0=C2=A0=C2=A0 /// The constructor for creating a [`Plane`] using th= is > [`DriverPlane`] implementation. > +=C2=A0=C2=A0=C2=A0 /// > +=C2=A0=C2=A0=C2=A0 /// Drivers may use this to instantiate their [`Drive= rPlane`] > object. > +=C2=A0=C2=A0=C2=A0 fn new(device: &Device, args: Self::Arg= s) -> impl > PinInit; > + > +=C2=A0=C2=A0=C2=A0 /// The optional [`drm_plane_helper_funcs.atomic_upda= te`] hook > for this plane. > +=C2=A0=C2=A0=C2=A0 /// > +=C2=A0=C2=A0=C2=A0 /// Drivers may use this to customize the atomic upda= te phase of > their [`Plane`] objects. If not > +=C2=A0=C2=A0=C2=A0 /// specified, this function is a no-op. > +=C2=A0=C2=A0=C2=A0 /// > +=C2=A0=C2=A0=C2=A0 /// [`drm_plane_helper_funcs.atomic_update`]: > srctree/include/drm/drm_modeset_helper_vtables.h > +=C2=A0=C2=A0=C2=A0 fn atomic_update(_commit: PlaneAtomicCommit<'_, Self>= ) { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 build_error::build_error("Thi= s should not be reachable") > +=C2=A0=C2=A0=C2=A0 } > + > +=C2=A0=C2=A0=C2=A0 /// The optional [`drm_plane_helper_funcs.atomic_chec= k`] hook > for this plane. > +=C2=A0=C2=A0=C2=A0 /// > +=C2=A0=C2=A0=C2=A0 /// Drivers may use this to customize the atomic chec= k phase of > their [`Plane`] objects. The > +=C2=A0=C2=A0=C2=A0 /// result of this function determines whether the at= omic check > passed or failed. > +=C2=A0=C2=A0=C2=A0 /// > +=C2=A0=C2=A0=C2=A0 /// [`drm_plane_helper_funcs.atomic_check`]: > srctree/include/drm/drm_modeset_helper_vtables.h > +=C2=A0=C2=A0=C2=A0 fn atomic_check(_check: PlaneAtomicCheck<'_, Self>) -= > Result { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 build_error::build_error("Thi= s should not be reachable") > +=C2=A0=C2=A0=C2=A0 } > +} > + > +/// The generated C vtable for a [`DriverPlane`]. > +/// > +/// This type is created internally by DRM. > +pub struct DriverPlaneOps { > +=C2=A0=C2=A0=C2=A0 funcs: bindings::drm_plane_funcs, > +=C2=A0=C2=A0=C2=A0 helper_funcs: bindings::drm_plane_helper_funcs, > +} > + > +#[derive(Copy, Clone, Debug, PartialEq, Eq)] > +#[repr(u32)] > +/// An enumerator describing a type of [`Plane`]. > +/// > +/// This is mainly just relevant for DRM legacy drivers. > +/// > +/// # Invariants > +/// > +/// This type is identical to [`enum drm_plane_type`]. > +/// > +/// [`enum drm_plane_type`]: srctree/include/drm/drm_plane.h > +pub enum Type { > +=C2=A0=C2=A0=C2=A0 /// Overlay planes represent all non-primary, non-cur= sor planes. > Some drivers refer to these > +=C2=A0=C2=A0=C2=A0 /// types of planes as "sprites" internally. > +=C2=A0=C2=A0=C2=A0 Overlay =3D bindings::drm_plane_type_DRM_PLANE_TYPE_O= VERLAY, > + > +=C2=A0=C2=A0=C2=A0 /// A primary plane attached to a CRTC that is the mo= st likely > to be able to light up the CRTC > +=C2=A0=C2=A0=C2=A0 /// when no scaling/cropping is used, and the plane c= overs the > whole CRTC. > +=C2=A0=C2=A0=C2=A0 Primary =3D bindings::drm_plane_type_DRM_PLANE_TYPE_P= RIMARY, > + > +=C2=A0=C2=A0=C2=A0 /// A cursor plane attached to a CRTC that is more li= kely to be > enabled when no scaling/cropping > +=C2=A0=C2=A0=C2=A0 /// is used, and the framebuffer has the size indicat= ed by > [`ModeConfigInfo::max_cursor`]. > +=C2=A0=C2=A0=C2=A0 /// > +=C2=A0=C2=A0=C2=A0 /// [`ModeConfigInfo::max_cursor`]: > crate::drm::kms::ModeConfigInfo > +=C2=A0=C2=A0=C2=A0 Cursor =3D bindings::drm_plane_type_DRM_PLANE_TYPE_CU= RSOR, > +} > + > +/// The main interface for a [`struct drm_plane`]. > +/// > +/// This type is the main interface for dealing with DRM planes. In > addition, it also allows > +/// immutable access to whatever private data is contained within an > implementor's [`DriverPlane`] > +/// type. > +/// > +/// # Invariants > +/// > +/// - `plane` and `inner` are initialized for as long as this object > is made available to users. > +/// - The data layout of this structure begins with [`struct > drm_plane`]. > +/// - The atomic state for this type can always be assumed to be of > type [`PlaneState`]. > +/// > +/// [`struct drm_plane`]: srctree/include/drm/drm_plane.h > +#[repr(C)] > +#[pin_data] > +pub struct Plane { > +=C2=A0=C2=A0=C2=A0 /// The FFI drm_plane object > +=C2=A0=C2=A0=C2=A0 plane: Opaque, > +=C2=A0=C2=A0=C2=A0 /// The driver's private inner data > +=C2=A0=C2=A0=C2=A0 #[pin] > +=C2=A0=C2=A0=C2=A0 inner: T, > +=C2=A0=C2=A0=C2=A0 #[pin] > +=C2=A0=C2=A0=C2=A0 _p: PhantomPinned, > +} > + > +impl Sealed for Plane {} > + > +impl Deref for Plane { > +=C2=A0=C2=A0=C2=A0 type Target =3D T; > + > +=C2=A0=C2=A0=C2=A0 fn deref(&self) -> &Self::Target { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 &self.inner > +=C2=A0=C2=A0=C2=A0 } > +} > + > +// SAFETY: `funcs` is initialized when the plane is allocated > +unsafe impl ModeObjectVtable for Plane { > +=C2=A0=C2=A0=C2=A0 type Vtable =3D bindings::drm_plane_funcs; > + > +=C2=A0=C2=A0=C2=A0 fn vtable(&self) -> *const Self::Vtable { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // SAFETY: `as_raw()` always = returns a valid plane pointer > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 unsafe { *self.as_raw() }.fun= cs > +=C2=A0=C2=A0=C2=A0 } > +} > + > +impl Plane { > +=C2=A0=C2=A0=C2=A0 super::impl_from_opaque_mode_obj! { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 fn <'a, D>(&'a OpaquePlane= ) -> &'a Self; > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 use > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 T as = DriverPlane, > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 D as = KmsDriver > +=C2=A0=C2=A0=C2=A0 } > +} > + > +/// A [`Plane`] that has not yet been registered with userspace. > +/// > +/// KMS registration is single-threaded, so this object is not > thread-safe. > +/// > +/// # Invariants > +/// > +/// - This object can only exist before its respective KMS device > has been registered. > +/// - Otherwise, it inherits all invariants of [`Plane`] and has an > identical data layout. > +pub struct UnregisteredPlane(Plane, > NotThreadSafe); > + > +// SAFETY: We share the invariants of `Plane` > +unsafe impl AsRawPlane for UnregisteredPlane { > +=C2=A0=C2=A0=C2=A0 fn as_raw(&self) -> *mut bindings::drm_plane { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 self.0.as_raw() > +=C2=A0=C2=A0=C2=A0 } > + > +=C2=A0=C2=A0=C2=A0 unsafe fn from_raw<'a>(ptr: *mut bindings::drm_plane)= -> &'a > Self { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // SAFETY: This is another fr= om_raw() call, so this function > shares the same safety contract > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 let plane =3D unsafe { Plane:= :::from_raw(ptr) }; > + > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // SAFETY: Our data layout is= identical via our type > invariants. > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 unsafe { mem::transmute(plane= ) } > +=C2=A0=C2=A0=C2=A0 } > +} > + > +impl Deref for UnregisteredPlane { > +=C2=A0=C2=A0=C2=A0 type Target =3D T; > + > +=C2=A0=C2=A0=C2=A0 fn deref(&self) -> &Self::Target { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 &self.0.inner > +=C2=A0=C2=A0=C2=A0 } > +} > + > +impl UnregisteredPlane { > +=C2=A0=C2=A0=C2=A0 /// Construct a new [`UnregisteredPlane`]. > +=C2=A0=C2=A0=C2=A0 /// > +=C2=A0=C2=A0=C2=A0 /// A driver may use this from their > [`KmsDriver::create_objects`] callback in order to > +=C2=A0=C2=A0=C2=A0 /// construct new [`UnregisteredPlane`] objects. > +=C2=A0=C2=A0=C2=A0 /// > +=C2=A0=C2=A0=C2=A0 /// [`KmsDriver::create_objects`]: > kernel::drm::kms::KmsDriver::create_objects > +=C2=A0=C2=A0=C2=A0 pub fn new<'a, 'b: 'a>( > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 dev: &'a NewKmsDevice<'a, T::= Driver, Probing>, > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 possible_crtcs: u32, > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 formats: &[u32], > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 format_modifiers: Option<&[u6= 4]>, > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 type_: Type, > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 name: Option<&CStr>, > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 args: T::Args, > +=C2=A0=C2=A0=C2=A0 ) -> Result<&'b Self> { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 let this: Pin>>= =3D KBox::try_pin_init( > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 try_p= in_init!(Plane { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0= =C2=A0=C2=A0=C2=A0 plane: Opaque::new(bindings::drm_plane { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0= =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 helper_private: &T::OPS.helper_f= uncs, > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0= =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 ..Default::default() > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0= =C2=A0=C2=A0=C2=A0 }), > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0= =C2=A0=C2=A0=C2=A0 inner <- T::new(dev, args), > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0= =C2=A0=C2=A0=C2=A0 _p: PhantomPinned > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 }), > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 GFP_K= ERNEL, > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 )?; > + > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // TODO: Move this over to us= ing collect() someday > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // Create a modifiers array w= ith the sentinel for passing to > DRM > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 let format_modifiers_raw; > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 if let Some(modifiers) =3D fo= rmat_modifiers { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 let m= ut raw =3D KVec::with_capacity(modifiers.len() + 1, > GFP_KERNEL)?; > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 for m= odifier in modifiers { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0= =C2=A0=C2=A0=C2=A0 raw.push(*modifier, GFP_KERNEL)?; > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 } > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 raw.p= ush(FORMAT_MOD_INVALID, GFP_KERNEL)?; > + > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 forma= t_modifiers_raw =3D Some(raw); > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 } else { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 forma= t_modifiers_raw =3D None; > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 } > + > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // SAFETY: > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // - `dev` handles destroying= the plane, and thus will > outlive us and always be valid. > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // - We just allocated `this`= , and we won't move it since > it's pinned > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // - We just allocated the `f= ormat_modifiers_raw` vec and > added the sentinel DRM expects > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 //=C2=A0=C2=A0 above > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // - `drm_universal_plane_ini= t` will memcpy() the following > parameters into its own storage, > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 //=C2=A0=C2=A0 so it's safe f= or them to become inaccessible after this > call returns: > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 //=C2=A0=C2=A0 - `formats` > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 //=C2=A0=C2=A0 - `format_modi= fiers_raw` > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 //=C2=A0=C2=A0 - `name` > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // - `type_` is equivalent to= `drm_plane_type` via its type > invariants. > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 to_result(unsafe { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 bindi= ngs::drm_universal_plane_init( > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0= =C2=A0=C2=A0=C2=A0 dev.as_raw(), > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0= =C2=A0=C2=A0=C2=A0 this.as_raw(), > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0= =C2=A0=C2=A0=C2=A0 possible_crtcs, > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0= =C2=A0=C2=A0=C2=A0 &T::OPS.funcs, > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0= =C2=A0=C2=A0=C2=A0 formats.as_ptr(), > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0= =C2=A0=C2=A0=C2=A0 formats.len() as _, > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0= =C2=A0=C2=A0=C2=A0 format_modifiers_raw.map_or(null(), |f| f.as_ptr()), > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0= =C2=A0=C2=A0=C2=A0 type_ as _, > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0= =C2=A0=C2=A0=C2=A0 name.map_or(null(), |n| n.as_char_ptr()), > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 ) > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 })?; > + > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // SAFETY: We don't move anyt= hing > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 let this =3D unsafe { Pin::in= to_inner_unchecked(this) }; > + > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // We'll re-assemble the box = in plane_destroy_callback() > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 let this =3D KBox::into_raw(t= his); > + > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // UnregisteredPlane has an e= quivalent data layout > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 let this: *mut Self =3D this.= cast(); > + > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // SAFETY: We just allocated = the plane above, so this > pointer must be valid > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 Ok(unsafe { &*this }) > +=C2=A0=C2=A0=C2=A0 } > +} > + > +/// A trait implemented by any type that acts as a [`struct > drm_plane`] interface. > +/// > +/// This is implemented internally by DRM. > +/// > +/// # Safety > +/// > +/// [`as_raw()`] must always return a valid pointer to an > initialized [`struct drm_plane`]. > +/// > +/// [`struct drm_plane`]: srctree/include/drm/drm_plane.h > +/// [`as_raw()`]: AsRawPlane::as_raw() > +pub unsafe trait AsRawPlane { > +=C2=A0=C2=A0=C2=A0 /// Return the raw `bindings::drm_plane` for this DRM= plane. > +=C2=A0=C2=A0=C2=A0 /// > +=C2=A0=C2=A0=C2=A0 /// Drivers should never use this directly. > +=C2=A0=C2=A0=C2=A0 fn as_raw(&self) -> *mut bindings::drm_plane; > + > +=C2=A0=C2=A0=C2=A0 /// Convert a raw `bindings::drm_plane` pointer into = an object > of this type. > +=C2=A0=C2=A0=C2=A0 /// > +=C2=A0=C2=A0=C2=A0 /// # Safety > +=C2=A0=C2=A0=C2=A0 /// > +=C2=A0=C2=A0=C2=A0 /// Callers promise that `ptr` points to a valid inst= ance of > this type > +=C2=A0=C2=A0=C2=A0 unsafe fn from_raw<'a>(ptr: *mut bindings::drm_plane)= -> &'a > Self; > +} > + > +// SAFETY: > +// - Via our type variants our data layout starts with `drm_plane` > +// - Since we don't expose `plane` to users before it has been > initialized, this and our data > +//=C2=A0=C2=A0 layout ensure that `as_raw()` always returns a valid poin= ter to > a `drm_plane`. > +unsafe impl AsRawPlane for Plane { > +=C2=A0=C2=A0=C2=A0 fn as_raw(&self) -> *mut bindings::drm_plane { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 self.plane.get() > +=C2=A0=C2=A0=C2=A0 } > + > +=C2=A0=C2=A0=C2=A0 unsafe fn from_raw<'a>(ptr: *mut bindings::drm_plane)= -> &'a > Self { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // Our data layout start with= `bindings::drm_plane`. > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 let ptr: *mut Self =3D ptr.ca= st(); > + > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // SAFETY: Our safety contrac= t requires that `ptr` point to > a valid intance of `Self`. > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 unsafe { &*ptr } > +=C2=A0=C2=A0=C2=A0 } > +} > + > +// SAFETY: We only expose this object to users directly after > KmsDriver::create_objects has been > +// called. > +unsafe impl ModesettablePlane for Plane { > +=C2=A0=C2=A0=C2=A0 type State =3D PlaneState; > +} > + > +// SAFETY: We don't expose Plane to users before `base` is > initialized in ::new(), so > +// `raw_mode_obj` always returns a valid pointer to a > bindings::drm_mode_object. > +unsafe impl ModeObject for Plane { > +=C2=A0=C2=A0=C2=A0 type Driver =3D T::Driver; > + > +=C2=A0=C2=A0=C2=A0 fn drm_dev(&self) -> &Device { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // SAFETY: DRM planes exist f= or as long as the device does, > so this pointer is always valid > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 unsafe { Device::from_raw((*s= elf.as_raw()).dev) } > +=C2=A0=C2=A0=C2=A0 } > + > +=C2=A0=C2=A0=C2=A0 fn raw_mode_obj(&self) -> *mut bindings::drm_mode_obj= ect { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // SAFETY: We don't expose DR= M planes to users before `base` > is initialized > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 unsafe { &raw mut (*self.as_r= aw()).base } > +=C2=A0=C2=A0=C2=A0 } > +} > + > +// SAFETY: Planes do not have a refcount > +unsafe impl StaticModeObject for Plane {} > + > +// SAFETY: Our interface is thread-safe. > +unsafe impl Send for Plane {} > + > +// SAFETY: Our interface is thread-safe. > +unsafe impl Sync for Plane {} > + > +/// A supertrait of [`AsRawPlane`] for [`struct drm_plane`] > interfaces that can perform modesets. > +/// > +/// This is implemented internally by DRM. > +/// > +/// # Safety > +/// > +/// Any object implementing this trait must only be made directly > available to the user after > +/// [`create_objects`] has completed. > +/// > +/// [`struct drm_plane`]: srctree/include/drm/drm_plane.h > +/// [`create_objects`]: KmsDriver::create_objects > +pub unsafe trait ModesettablePlane: AsRawPlane { > +=C2=A0=C2=A0=C2=A0 /// The type that should be returned for a plane stat= e acquired > using this plane interface > +=C2=A0=C2=A0=C2=A0 type State: FromRawPlaneState; > +} > + > +/// Common methods available on any type which implements > [`AsRawPlane`]. > +/// > +/// This is implemented internally by DRM, and provides many of the > basic methods for working with > +/// planes. > +pub trait RawPlane: AsRawPlane { > +=C2=A0=C2=A0=C2=A0 /// Return the index of this DRM plane > +=C2=A0=C2=A0=C2=A0 #[inline] > +=C2=A0=C2=A0=C2=A0 fn index(&self) -> u32 { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // SAFETY: > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // - The index is initialized= by the time we expose planes > to users, and does not change > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 //=C2=A0=C2=A0 throughout its= lifetime > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // - `.as_raw()` always retur= ns a valid poiinter. > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 unsafe { *self.as_raw() }.ind= ex > +=C2=A0=C2=A0=C2=A0 } > + > +=C2=A0=C2=A0=C2=A0 /// Return the index of this DRM plane in the form of= a bitmask > +=C2=A0=C2=A0=C2=A0 #[inline] > +=C2=A0=C2=A0=C2=A0 fn mask(&self) -> u32 { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 1 << self.index() > +=C2=A0=C2=A0=C2=A0 } > +} > +impl RawPlane for T {} > + > +/// A [`struct drm_plane`] without a known [`DriverPlane`] > implementation. > +/// > +/// This is mainly for situations where our bindings can't infer the > [`DriverPlane`] implementation > +/// for a [`struct drm_plane`] automatically. It is identical to > [`Plane`], except that it does not > +/// provide access to the driver's private data. > +/// > +/// It may be upcasted to a full [`Plane`] using > [`Plane::from_opaque`] or > +/// [`Plane::try_from_opaque`]. > +/// > +/// # Invariants > +/// > +/// - `plane` is initialized for as long as this object is made > available to users. > +/// - The data layout of this structure is equivalent to [`struct > drm_plane`]. > +/// > +/// [`struct drm_plane`]: srctree/include/drm/drm_plane.h > +#[repr(transparent)] > +pub struct OpaquePlane { > +=C2=A0=C2=A0=C2=A0 plane: Opaque, > +=C2=A0=C2=A0=C2=A0 _p: PhantomData, > +} > + > +impl Sealed for OpaquePlane {} > + > +// SAFETY: > +// * Via our type variants our data layout is identical to > `drm_plane` > +// * Since we don't expose `plane` to users before it has been > initialized, this and our data > +//=C2=A0=C2=A0 layout ensure that `as_raw()` always returns a valid poin= ter to > a `drm_plane`. > +unsafe impl AsRawPlane for OpaquePlane { > +=C2=A0=C2=A0=C2=A0 fn as_raw(&self) -> *mut bindings::drm_plane { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 self.plane.get() > +=C2=A0=C2=A0=C2=A0 } > + > +=C2=A0=C2=A0=C2=A0 unsafe fn from_raw<'a>(ptr: *mut bindings::drm_plane)= -> &'a > Self { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // SAFETY: Our data layout is= identical to > `bindings::drm_plane` > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 unsafe { &*ptr.cast() } > +=C2=A0=C2=A0=C2=A0 } > +} > + > +// SAFETY: We only expose this object to users directly after > KmsDriver::create_objects has been > +// called. > +unsafe impl ModesettablePlane for OpaquePlane { > +=C2=A0=C2=A0=C2=A0 type State =3D OpaquePlaneState; > +} > + > +// SAFETY: We don't expose OpaquePlane to users before `base` is > initialized in > +// Plane::::new(), so `raw_mode_obj` always returns a valid > pointer to a > +// bindings::drm_mode_object. > +unsafe impl ModeObject for OpaquePlane { > +=C2=A0=C2=A0=C2=A0 type Driver =3D T; > + > +=C2=A0=C2=A0=C2=A0 fn drm_dev(&self) -> &Device { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // SAFETY: DRM planes exist f= or as long as the device does, > so this pointer is always valid > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 unsafe { Device::from_raw((*s= elf.as_raw()).dev) } > +=C2=A0=C2=A0=C2=A0 } > + > +=C2=A0=C2=A0=C2=A0 fn raw_mode_obj(&self) -> *mut bindings::drm_mode_obj= ect { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // SAFETY: We don't expose DR= M planes to users before `base` > is initialized > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 unsafe { &raw mut (*self.as_r= aw()).base } > +=C2=A0=C2=A0=C2=A0 } > +} > + > +// SAFETY: Planes do not have a refcount > +unsafe impl StaticModeObject for OpaquePlane {} > + > +// SAFETY: `funcs` is initialized when the plane is allocated > +unsafe impl ModeObjectVtable for OpaquePlane { > +=C2=A0=C2=A0=C2=A0 type Vtable =3D bindings::drm_plane_funcs; > + > +=C2=A0=C2=A0=C2=A0 fn vtable(&self) -> *const Self::Vtable { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // SAFETY: `as_raw()` always = returns a valid pointer to a > plane > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 unsafe { *self.as_raw() }.fun= cs > +=C2=A0=C2=A0=C2=A0 } > +} > + > +// SAFETY: Our plane interfaces are guaranteed to be thread-safe > +unsafe impl Send for OpaquePlane {} > +unsafe impl Sync for OpaquePlane {} > + > +/// A trait implemented by any type which can produce a reference to > a [`struct drm_plane_state`]. > +/// > +/// This is implemented internally by DRM. > +/// > +/// [`struct drm_plane_state`]: srctree/include/drm/drm_plane.h > +pub trait AsRawPlaneState: private::AsRawPlaneState { > +=C2=A0=C2=A0=C2=A0 /// The type that this plane state interface returns = to > represent the parent DRM plane > +=C2=A0=C2=A0=C2=A0 type Plane: ModesettablePlane; > +} > + > +pub(crate) mod private { > +=C2=A0=C2=A0=C2=A0 /// Trait for retrieving references to the base plane= state > contained within any plane state > +=C2=A0=C2=A0=C2=A0 /// compatible type > +=C2=A0=C2=A0=C2=A0 #[allow(unreachable_pub)] > +=C2=A0=C2=A0=C2=A0 pub trait AsRawPlaneState { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 /// Return an immutable refer= ence to the raw plane state > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 fn as_raw(&self) -> &bindings= ::drm_plane_state; > + > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 /// Get a mutable reference t= o the raw > `bindings::drm_plane_state` contained within this > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 /// type. > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 /// > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 /// # Safety > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 /// > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 /// The caller promises this = mutable reference will not be > used to modify any contents of > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 /// `bindings::drm_plane_stat= e` which DRM would consider to > be static - like the backpointer > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 /// to the DRM plane that own= s this state. This also means > the mutable reference should > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 /// never be exposed outside = of this crate. > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 unsafe fn as_raw_mut(&mut sel= f) -> &mut > bindings::drm_plane_state; > +=C2=A0=C2=A0=C2=A0 } > +} > + > +pub(crate) use private::AsRawPlaneState as AsRawPlaneStatePrivate; > + > +/// A trait implemented for any type which can be constructed > directly from a > +/// [`struct drm_plane_state`] pointer. > +/// > +/// This is implemented internally by DRM. > +/// > +/// [`struct drm_plane_state`]: srctree/include/drm/drm_plane.h > +pub trait FromRawPlaneState: AsRawPlaneState { > +=C2=A0=C2=A0=C2=A0 /// Get an immutable reference to this type from the = given raw > [`struct drm_plane_state`] > +=C2=A0=C2=A0=C2=A0 /// pointer. > +=C2=A0=C2=A0=C2=A0 /// > +=C2=A0=C2=A0=C2=A0 /// # Safety > +=C2=A0=C2=A0=C2=A0 /// > +=C2=A0=C2=A0=C2=A0 /// - The caller guarantees `ptr` is contained within= a valid > instance of `Self` > +=C2=A0=C2=A0=C2=A0 /// - The caller guarantees that `ptr` cannot not be = modified > for the lifetime of `'a`. > +=C2=A0=C2=A0=C2=A0 /// > +=C2=A0=C2=A0=C2=A0 /// [`struct drm_plane_state`]: srctree/include/drm/d= rm_plane.h > +=C2=A0=C2=A0=C2=A0 unsafe fn from_raw<'a>(ptr: *const bindings::drm_plan= e_state) -> > &'a Self; > + > +=C2=A0=C2=A0=C2=A0 /// Get a mutable reference to this type from the giv= en raw > [`struct drm_plane_state`] pointer. > +=C2=A0=C2=A0=C2=A0 /// > +=C2=A0=C2=A0=C2=A0 /// # Safety > +=C2=A0=C2=A0=C2=A0 /// > +=C2=A0=C2=A0=C2=A0 /// - The caller guarantees that `ptr` is contained w= ithin a > valid instance of `Self` > +=C2=A0=C2=A0=C2=A0 /// - The caller guarantees that `ptr` cannot have an= y other > references taken out for the > +=C2=A0=C2=A0=C2=A0 ///=C2=A0=C2=A0 lifetime of `'a`. > +=C2=A0=C2=A0=C2=A0 /// > +=C2=A0=C2=A0=C2=A0 /// [`struct drm_plane_state`]: srctree/include/drm/d= rm_plane.h > +=C2=A0=C2=A0=C2=A0 unsafe fn from_raw_mut<'a>(ptr: *mut bindings::drm_pl= ane_state) > -> &'a mut Self; > +} > + > +/// Common methods available on any type which implements > [`AsRawPlane`]. > +/// > +/// This is implemented internally by DRM, and provides many of the > basic methods for working with > +/// the atomic state of [`Plane`]s. > +pub trait RawPlaneState: AsRawPlaneState { > +=C2=A0=C2=A0=C2=A0 /// Return the plane that this plane state belongs to= . > +=C2=A0=C2=A0=C2=A0 fn plane(&self) -> &Self::Plane { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // SAFETY: The index is initi= alized by the time we expose > Plane objects to users, and is > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // invariant throughout the l= ifetime of the Plane > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 unsafe { Self::Plane::from_ra= w(self.as_raw().plane) } > +=C2=A0=C2=A0=C2=A0 } > + > +=C2=A0=C2=A0=C2=A0 /// Return the current [`OpaqueCrtc`] assigned to thi= s plane, if > there is one. > +=C2=A0=C2=A0=C2=A0 fn crtc<'a, 'b: 'a, D>(&'a self) -> Option<&'b Opaque= Crtc> > +=C2=A0=C2=A0=C2=A0 where > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 Self::Plane: ModeObject, > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 D: KmsDriver, > +=C2=A0=C2=A0=C2=A0 { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // SAFETY: This cast is guara= nteed safe by `OpaqueCrtc`s > invariants. > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 NonNull::new(self.as_raw().cr= tc).map(|c| unsafe { > OpaqueCrtc::from_raw(c.as_ptr()) }) > +=C2=A0=C2=A0=C2=A0 } > + > +=C2=A0=C2=A0=C2=A0 /// Run the atomic check helper for this plane and th= e given > CRTC state. > +=C2=A0=C2=A0=C2=A0 fn atomic_helper_check( > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 &mut self, > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 crtc_state: &CrtcStateMutator= <'_, S>, > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 can_position: bool, > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 can_update_disabled: bool, > +=C2=A0=C2=A0=C2=A0 ) -> Result > +=C2=A0=C2=A0=C2=A0 where > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 D: KmsDriver, > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 S: FromRawCrtcState, > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 S::Crtc: ModesettableCrtc + M= odeObject, > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 Self::Plane: ModeObject, > +=C2=A0=C2=A0=C2=A0 { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // SAFETY: We're passing the = mutable reference from > `self.as_raw_mut()` directly to DRM, > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // which is safe. > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 to_result(unsafe { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 bindi= ngs::drm_atomic_helper_check_plane_state( > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0= =C2=A0=C2=A0=C2=A0 self.as_raw_mut(), > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0= =C2=A0=C2=A0=C2=A0 crtc_state.as_raw(), > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0= =C2=A0=C2=A0=C2=A0 bindings::DRM_PLANE_NO_SCALING as _, // TODO: add > parameters for scaling > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0= =C2=A0=C2=A0=C2=A0 bindings::DRM_PLANE_NO_SCALING as _, > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0= =C2=A0=C2=A0=C2=A0 can_position, > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0= =C2=A0=C2=A0=C2=A0 can_update_disabled, > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 ) > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 }) > +=C2=A0=C2=A0=C2=A0 } > + > +=C2=A0=C2=A0=C2=A0 /// Return the framebuffer currently set for this pla= ne state > +=C2=A0=C2=A0=C2=A0 #[inline] > +=C2=A0=C2=A0=C2=A0 fn framebuffer(&self) -> Option<&Framebuffer> > +=C2=A0=C2=A0=C2=A0 where > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 Self::Plane: ModeObject, > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 D: KmsDriver, > +=C2=A0=C2=A0=C2=A0 { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // SAFETY: The layout of Fram= ebuffer is identical to `fb` > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 unsafe { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 self.= as_raw() > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0= =C2=A0=C2=A0=C2=A0 .fb > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0= =C2=A0=C2=A0=C2=A0 .as_ref() > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0= =C2=A0=C2=A0=C2=A0 .map(|fb| Framebuffer::from_raw(fb)) > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 } > +=C2=A0=C2=A0=C2=A0 } > +} > +impl RawPlaneState for T {} > + > +/// The main interface for a [`struct drm_plane_state`]. > +/// > +/// This type is the main interface for dealing with the atomic > state of DRM planes. In addition, it > +/// allows access to whatever private data is contained within an > implementor's [`DriverPlaneState`] > +/// type. > +/// > +/// # Invariants > +/// > +/// - The DRM C API and our interface guarantees that only the user > has mutable access to `state`, > +///=C2=A0=C2=A0 up until [`drm_atomic_helper_commit_hw_done`] is called. > Therefore, `plane` follows rust's > +///=C2=A0=C2=A0 data aliasing rules and does not need to be behind an > [`Opaque`] type. > +/// - `state` and `inner` initialized for as long as this object is > exposed to users. > +/// - The data layout of this structure begins with [`struct > drm_plane_state`]. > +/// - The plane for this atomic state can always be assumed to be of > type [`Plane`]. > +/// > +/// [`struct drm_plane_state`]: srctree/include/drm/drm_plane.h > +/// [`drm_atomic_helper_commit_hw_done`]: > srctree/include/drm/drm_atomic_helper.h > +#[derive(Default)] > +#[repr(C)] > +pub struct PlaneState { > +=C2=A0=C2=A0=C2=A0 state: bindings::drm_plane_state, > +=C2=A0=C2=A0=C2=A0 inner: T, > +} > + > +/// The main trait for implementing the [`struct drm_plane_state`] > API for a [`Plane`]. > +/// > +/// A driver may store driver-private data within the implementor's > type, which will be available > +/// when using a full typed [`PlaneState`] object. > +/// > +/// # Invariants > +/// > +/// - Any C FFI callbacks generated using this trait are guaranteed > that passed-in > +///=C2=A0=C2=A0 [`struct drm_plane`] pointers are contained within a > [`Plane`]. > +/// - Any C FFI callbacks generated using this trait are guaranteed > that passed-in > +///=C2=A0=C2=A0 [`struct drm_plane_state`] pointers are contained within= a > [`PlaneState`]. > +/// > +/// [`struct drm_plane`]: srctree/include/drm_plane.h > +/// [`struct drm_plane_state`]: srctree/include/drm_plane.h > +pub trait DriverPlaneState: Clone + Default + Sized { > +=C2=A0=C2=A0=C2=A0 /// The type for this driver's drm_plane implementati= on > +=C2=A0=C2=A0=C2=A0 type Plane: DriverPlane; > +} > + > +impl Sealed for PlaneState {} > + > +impl AsRawPlaneState for PlaneState { > +=C2=A0=C2=A0=C2=A0 type Plane =3D Plane; > +} > + > +impl private::AsRawPlaneState for PlaneState > { > +=C2=A0=C2=A0=C2=A0 fn as_raw(&self) -> &bindings::drm_plane_state { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 &self.state > +=C2=A0=C2=A0=C2=A0 } > + > +=C2=A0=C2=A0=C2=A0 unsafe fn as_raw_mut(&mut self) -> &mut > bindings::drm_plane_state { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 &mut self.state > +=C2=A0=C2=A0=C2=A0 } > +} > + > +impl FromRawPlaneState for PlaneState { > +=C2=A0=C2=A0=C2=A0 unsafe fn from_raw<'a>(ptr: *const bindings::drm_plan= e_state) -> > &'a Self { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // Our data layout starts wit= h `bindings::drm_plane_state`. > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 let ptr: *const Self =3D ptr.= cast(); > + > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // SAFETY: > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // - Our safety contract requ= ires that `ptr` be contained > within `Self`. > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // - Our safety contract requ= ires the caller ensure that it > is safe for us to take an > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 //=C2=A0=C2=A0 immutable refe= rence. > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 unsafe { &*ptr } > +=C2=A0=C2=A0=C2=A0 } > + > +=C2=A0=C2=A0=C2=A0 unsafe fn from_raw_mut<'a>(ptr: *mut bindings::drm_pl= ane_state) > -> &'a mut Self { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // Our data layout starts wit= h `bindings::drm_plane_state`. > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 let ptr: *mut Self =3D ptr.ca= st(); > + > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // SAFETY: > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // - Our safety contract requ= ires that `ptr` be contained > within `Self`. > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // - Our safety contract requ= ires the caller ensure it is > safe for us to take a mutable > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 //=C2=A0=C2=A0 reference. > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 unsafe { &mut *ptr } > +=C2=A0=C2=A0=C2=A0 } > +} > + > +impl Deref for PlaneState { > +=C2=A0=C2=A0=C2=A0 type Target =3D T; > + > +=C2=A0=C2=A0=C2=A0 fn deref(&self) -> &Self::Target { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 &self.inner > +=C2=A0=C2=A0=C2=A0 } > +} > + > +impl DerefMut for PlaneState { > +=C2=A0=C2=A0=C2=A0 fn deref_mut(&mut self) -> &mut Self::Target { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 &mut self.inner > +=C2=A0=C2=A0=C2=A0 } > +} > + > +// SAFETY: Shares the safety guarantee of Plane's vtable impl > +unsafe impl ModeObjectVtable for PlaneState > { > +=C2=A0=C2=A0=C2=A0 type Vtable =3D bindings::drm_plane_funcs; > + > +=C2=A0=C2=A0=C2=A0 fn vtable(&self) -> *const Self::Vtable { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 self.plane().vtable() > +=C2=A0=C2=A0=C2=A0 } > +} > + > +impl PlaneState { > +=C2=A0=C2=A0=C2=A0 super::impl_from_opaque_mode_obj! { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 fn <'a, D, P>(&'a OpaquePlane= State) -> &'a Self > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 where > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 T: Dr= iverPlaneState; > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 use > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 P as = DriverPlane, > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 D as = KmsDriver > +=C2=A0=C2=A0=C2=A0 } > +} > + > +/// A [`struct drm_plane_state`] without a known > [`DriverPlaneState`] implementation. > +/// > +/// This is mainly for situations where our bindings can't infer the > [`DriverPlaneState`] > +/// implementation for a [`struct drm_plane_state`] automatically. > It is identical to [`Plane`], > +/// except that it does not provide access to the driver's private > data. > +/// > +/// # Invariants > +/// > +/// - The DRM C API and our interface guarantees that only the user > has mutable access to `state`, > +///=C2=A0=C2=A0 up until [`drm_atomic_helper_commit_hw_done`] is called. > Therefore, `plane` follows rust's > +///=C2=A0=C2=A0 data aliasing rules and does not need to be behind an > [`Opaque`] type. > +/// - `state` is initialized for as long as this object is exposed > to users. > +/// - The data layout of this structure is identical to [`struct > drm_plane_state`]. > +/// > +/// [`struct drm_plane_state`]: srctree/include/drm/drm_plane.h > +/// [`drm_atomic_helper_commit_hw_done`]: > srctree/include/drm/drm_atomic_helper.h > +#[repr(transparent)] > +pub struct OpaquePlaneState { > +=C2=A0=C2=A0=C2=A0 state: bindings::drm_plane_state, > +=C2=A0=C2=A0=C2=A0 _p: PhantomData, > +} > + > +impl AsRawPlaneState for OpaquePlaneState { > +=C2=A0=C2=A0=C2=A0 type Plane =3D OpaquePlane; > +} > + > +impl private::AsRawPlaneState for OpaquePlaneState > { > +=C2=A0=C2=A0=C2=A0 fn as_raw(&self) -> &bindings::drm_plane_state { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 &self.state > +=C2=A0=C2=A0=C2=A0 } > + > +=C2=A0=C2=A0=C2=A0 unsafe fn as_raw_mut(&mut self) -> &mut > bindings::drm_plane_state { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 &mut self.state > +=C2=A0=C2=A0=C2=A0 } > +} > + > +impl FromRawPlaneState for OpaquePlaneState { > +=C2=A0=C2=A0=C2=A0 unsafe fn from_raw<'a>(ptr: *const bindings::drm_plan= e_state) -> > &'a Self { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // SAFETY: Our data layout is= identical to `ptr` > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 unsafe { &*ptr.cast() } > +=C2=A0=C2=A0=C2=A0 } > + > +=C2=A0=C2=A0=C2=A0 unsafe fn from_raw_mut<'a>(ptr: *mut bindings::drm_pl= ane_state) > -> &'a mut Self { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // SAFETY: Our data layout is= identical to `ptr` > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 unsafe { &mut *ptr.cast() } > +=C2=A0=C2=A0=C2=A0 } > +} > + > +// SAFETY: Shares the safety guarantee of OpaquePlane's vtable > impl > +unsafe impl ModeObjectVtable for OpaquePlaneState { > +=C2=A0=C2=A0=C2=A0 type Vtable =3D bindings::drm_plane_funcs; > + > +=C2=A0=C2=A0=C2=A0 fn vtable(&self) -> *const Self::Vtable { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 self.plane().vtable() > +=C2=A0=C2=A0=C2=A0 } > +} > + > +/// An interface for mutating a [`Plane`]s atomic state. > +/// > +/// This type is typically returned by an [`AtomicStateMutator`] > within contexts where it is > +/// possible to safely mutate a plane's state. In order to uphold > rust's data-aliasing rules, only > +/// [`PlaneStateMutator`] may exist at a time. > +pub struct PlaneStateMutator<'a, T: FromRawPlaneState> { > +=C2=A0=C2=A0=C2=A0 state: &'a mut T, > +=C2=A0=C2=A0=C2=A0 mask: &'a Cell, > +} > + > +impl<'a, T: FromRawPlaneState> PlaneStateMutator<'a, T> { > +=C2=A0=C2=A0=C2=A0 pub(super) fn new( > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 mutator: &'a AtomicStateMutat= or, > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 state: NonNull, > +=C2=A0=C2=A0=C2=A0 ) -> Option { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // SAFETY: `plane` is invaria= nt throughout the lifetime of > the atomic state, is > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // initialized by this point,= and we're guaranteed it is of > type `AsRawPlane` by type > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // invariance > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 let plane =3D unsafe { > T::Plane::from_raw((*state.as_ptr()).plane) }; > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 let plane_mask =3D plane.mask= (); > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 let borrowed_mask =3D mutator= .borrowed_planes.get(); > + > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 if borrowed_mask & plane_mask= =3D=3D 0 { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 mutat= or.borrowed_planes.set(borrowed_mask | plane_mask); > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 Some(= Self { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0= =C2=A0=C2=A0=C2=A0 mask: &mutator.borrowed_planes, > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0= =C2=A0=C2=A0=C2=A0 // SAFETY: We're guaranteed `state` is of > `FromRawPlaneState` by type invariance, > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0= =C2=A0=C2=A0=C2=A0 // and we just confirmed by checking > `borrowed_planes` that no other mutable borrows > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0= =C2=A0=C2=A0=C2=A0 // have been taken out for `state` > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0= =C2=A0=C2=A0=C2=A0 state: unsafe { T::from_raw_mut(state.as_ptr()) }, > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 }) > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 } else { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 None > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 } > +=C2=A0=C2=A0=C2=A0 } > +} > + > +impl<'a, T: FromRawPlaneState> Drop for PlaneStateMutator<'a, T> { > +=C2=A0=C2=A0=C2=A0 fn drop(&mut self) { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 let mask =3D self.state.plane= ().mask(); > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 self.mask.set(self.mask.get()= & !mask); > +=C2=A0=C2=A0=C2=A0 } > +} > + > +impl<'a, T: FromRawPlaneState> AsRawPlaneState for > PlaneStateMutator<'a, T> { > +=C2=A0=C2=A0=C2=A0 type Plane =3D T::Plane; > +} > + > +impl<'a, T: FromRawPlaneState> private::AsRawPlaneState for > PlaneStateMutator<'a, T> { > +=C2=A0=C2=A0=C2=A0 fn as_raw(&self) -> &bindings::drm_plane_state { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 self.state.as_raw() > +=C2=A0=C2=A0=C2=A0 } > + > +=C2=A0=C2=A0=C2=A0 unsafe fn as_raw_mut(&mut self) -> &mut > bindings::drm_plane_state { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // SAFETY: This function is b= ound by the same safety > contract as `self.inner.as_raw_mut()` > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 unsafe { self.state.as_raw_mu= t() } > +=C2=A0=C2=A0=C2=A0 } > +} > + > +impl<'a, T: FromRawPlaneState> Sealed for PlaneStateMutator<'a, T> > {} > + > +impl<'a, T: DriverPlaneState> Deref for PlaneStateMutator<'a, > PlaneState> { > +=C2=A0=C2=A0=C2=A0 type Target =3D T; > + > +=C2=A0=C2=A0=C2=A0 fn deref(&self) -> &Self::Target { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 &self.state.inner > +=C2=A0=C2=A0=C2=A0 } > +} > + > +impl<'a, T: DriverPlaneState> DerefMut for PlaneStateMutator<'a, > PlaneState> { > +=C2=A0=C2=A0=C2=A0 fn deref_mut(&mut self) -> &mut Self::Target { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 &mut self.state.inner > +=C2=A0=C2=A0=C2=A0 } > +} > + > +// SAFETY: Shares the safety guarantees of `T`'s ModeObjectVtable > impl > +unsafe impl<'a, T: FromRawPlaneState> ModeObjectVtable for > PlaneStateMutator<'a, T> > +where > +=C2=A0=C2=A0=C2=A0 T: FromRawPlaneState + ModeObjectVtable, > +{ > +=C2=A0=C2=A0=C2=A0 type Vtable =3D T::Vtable; > + > +=C2=A0=C2=A0=C2=A0 fn vtable(&self) -> *const Self::Vtable { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 self.state.vtable() > +=C2=A0=C2=A0=C2=A0 } > +} > + > +impl<'a, T: DriverPlaneState> PlaneStateMutator<'a, PlaneState> { > +=C2=A0=C2=A0=C2=A0 super::impl_from_opaque_mode_obj! { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 fn (PlaneStateMutator<'= a, OpaquePlaneState>) -> > Self > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 where > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 T: Dr= iverPlaneState; > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 use > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 P as = DriverPlane, > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 D as = KmsDriver > +=C2=A0=C2=A0=C2=A0 } > +} > + > +/// A token provided during [`atomic_check`] callbacks for accessing > the plane, atomic state, and > +/// the old and new states of the plane. > +/// > +/// [`atomic_check`]: DriverPlane::atomic_check > +pub struct PlaneAtomicCheck<'a, T: DriverPlane> { > +=C2=A0=C2=A0=C2=A0 state: &'a AtomicStateComposer, > +=C2=A0=C2=A0=C2=A0 plane: &'a Plane, > +} > + > +impl<'a, T: DriverPlane> PlaneAtomicCheck<'a, T> { > +=C2=A0=C2=A0=C2=A0 impl_atomic_state_token_ops!( > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 PlaneAtomicCheck, > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 AtomicStateComposer, > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 Plane, > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 use <'a, T> > +=C2=A0=C2=A0=C2=A0 ); > +} > + > +/// A token provided to [`DriverPlane`] callbacks during the atomic > commit phase for accessing the > +/// plane, atomic state, new and old states of the plane. > +/// > +/// # Invariants > +/// > +/// This token is proof that the old and new atomic state of `plane` > are present in `state` and do > +/// not have any mutators taken out. > +pub struct PlaneAtomicCommit<'a, T: DriverPlane> { > +=C2=A0=C2=A0=C2=A0 state: &'a AtomicStateMutator, > +=C2=A0=C2=A0=C2=A0 plane: &'a Plane, > +} > + > +impl<'a, T: DriverPlane> PlaneAtomicCommit<'a, T> { > +=C2=A0=C2=A0=C2=A0 impl_atomic_state_token_ops!( > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 PlaneAtomicCommit, > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 AtomicStateMutator, > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 Plane, > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 use <'a, T> > +=C2=A0=C2=A0=C2=A0 ); > +} > + > +unsafe extern "C" fn plane_destroy_callback(plane: > *mut bindings::drm_plane) { > +=C2=A0=C2=A0=C2=A0 // SAFETY: DRM guarantees that `plane` points to a va= lid > initialized `drm_plane`. > +=C2=A0=C2=A0=C2=A0 unsafe { bindings::drm_plane_cleanup(plane) }; > + > +=C2=A0=C2=A0=C2=A0 // SAFETY: > +=C2=A0=C2=A0=C2=A0 // - DRM guarantees we are now the only one with acce= ss to this > [`drm_plane`]. > +=C2=A0=C2=A0=C2=A0 // - This cast is safe via `DriverPlane`s type invari= ants. > +=C2=A0=C2=A0=C2=A0 drop(unsafe { KBox::from_raw(plane as *mut Plane) = }); > +} > + > +unsafe extern "C" fn atomic_duplicate_state_callback DriverPlaneState>( > +=C2=A0=C2=A0=C2=A0 plane: *mut bindings::drm_plane, > +) -> *mut bindings::drm_plane_state { > +=C2=A0=C2=A0=C2=A0 // SAFETY: DRM guarantees that `plane` points to a va= lid > initialized `drm_plane`. > +=C2=A0=C2=A0=C2=A0 let state =3D unsafe { (*plane).state }; > +=C2=A0=C2=A0=C2=A0 if state.is_null() { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 return null_mut(); > +=C2=A0=C2=A0=C2=A0 } > + > +=C2=A0=C2=A0=C2=A0 // SAFETY: This cast is safe via `DriverPlaneState`s = type > invariants. > +=C2=A0=C2=A0=C2=A0 let state =3D unsafe { PlaneState::::from_raw(stat= e) }; > + > +=C2=A0=C2=A0=C2=A0 let new: Result> =3D KBox::try_init( > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 try_init!(PlaneState { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 inner= : state.inner.clone(), > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 state= : bindings::drm_plane_state { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0= =C2=A0=C2=A0=C2=A0 ..Default::default() > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 }, > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 }), > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 GFP_KERNEL, > +=C2=A0=C2=A0=C2=A0 ); > + > +=C2=A0=C2=A0=C2=A0 if let Ok(mut new) =3D new { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // SAFETY: > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // - `new` provides a valid p= ointer to a newly allocated > `drm_plane_state` via type > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 //=C2=A0=C2=A0 invariants > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // - This initializes `new` v= ia memcpy() > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 unsafe { > bindings::__drm_atomic_helper_plane_duplicate_state(plane, > new.as_raw_mut()) }; > + > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 KBox::into_raw(new).cast() > +=C2=A0=C2=A0=C2=A0 } else { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 null_mut() > +=C2=A0=C2=A0=C2=A0 } > +} > + > +unsafe extern "C" fn atomic_destroy_state_callback DriverPlaneState>( > +=C2=A0=C2=A0=C2=A0 _plane: *mut bindings::drm_plane, > +=C2=A0=C2=A0=C2=A0 state: *mut bindings::drm_plane_state, > +) { > +=C2=A0=C2=A0=C2=A0 // SAFETY: DRM guarantees that `state` points to a va= lid > instance of `drm_plane_state` > +=C2=A0=C2=A0=C2=A0 unsafe { > bindings::__drm_atomic_helper_plane_destroy_state(state) }; > + > +=C2=A0=C2=A0=C2=A0 // SAFETY: > +=C2=A0=C2=A0=C2=A0 // * DRM guarantees we are the only one with access t= o this > `drm_plane_state` > +=C2=A0=C2=A0=C2=A0 // * This cast is safe via our type invariants. > +=C2=A0=C2=A0=C2=A0 drop(unsafe { KBox::from_raw(state.cast::>()) }); > +} > + > +unsafe extern "C" fn plane_reset_callback(plane: > *mut bindings::drm_plane) { > +=C2=A0=C2=A0=C2=A0 // SAFETY: DRM guarantees that `state` points to a va= lid > instance of `drm_plane_state` > +=C2=A0=C2=A0=C2=A0 let state =3D unsafe { (*plane).state }; > +=C2=A0=C2=A0=C2=A0 if !state.is_null() { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // SAFETY: > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // - We're guaranteed `plane`= is `Plane` via type > invariants > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // - We're guaranteed `state`= is `PlaneState` via type > invariants. > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 unsafe { atomic_destroy_state= _callback::(plane, > state) } > + > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // SAFETY: No special require= ments here, DRM expects this to > be NULL > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 unsafe { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 (*pla= ne).state =3D null_mut(); > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 } > +=C2=A0=C2=A0=C2=A0 } > + > +=C2=A0=C2=A0=C2=A0 // Unfortunately, this is the best we can do at the m= oment as > this FFI callback was mistakenly > +=C2=A0=C2=A0=C2=A0 // presumed to be infallible :( > +=C2=A0=C2=A0=C2=A0 let new =3D > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 KBox::new(PlaneState::::default(), > GFP_KERNEL).expect("Blame the API, sorry!"); > + > +=C2=A0=C2=A0=C2=A0 // DRM takes ownership of the state from here, resets= it, and > then assigns it to the plane > +=C2=A0=C2=A0=C2=A0 // SAFETY: > +=C2=A0=C2=A0=C2=A0 // - DRM guarantees that `plane` points to a valid in= stance of > `drm_plane`. > +=C2=A0=C2=A0=C2=A0 // - The cast to `drm_plane_state` is safe via `Plane= State`s > type invariants. > +=C2=A0=C2=A0=C2=A0 unsafe { bindings::__drm_atomic_helper_plane_reset(pl= ane, > KBox::into_raw(new).cast()) }; > +} > + > +unsafe extern "C" fn atomic_update_callback( > +=C2=A0=C2=A0=C2=A0 plane: *mut bindings::drm_plane, > +=C2=A0=C2=A0=C2=A0 state: *mut bindings::drm_atomic_state, > +) { > +=C2=A0=C2=A0=C2=A0 // SAFETY: > +=C2=A0=C2=A0=C2=A0 // - We're guaranteed `plane` is of type `Plane` v= ia type > invariants. > +=C2=A0=C2=A0=C2=A0 // - We're guaranteed by DRM that `plane` is pointing= to a valid > initialized state. > +=C2=A0=C2=A0=C2=A0 let plane =3D unsafe { Plane::from_raw(plane) }; > + > +=C2=A0=C2=A0=C2=A0 // SAFETY: DRM guarantees `state` points to a valid > `drm_atomic_state` > +=C2=A0=C2=A0=C2=A0 let state =3D unsafe { > AtomicStateMutator::new(NonNull::new_unchecked(state)) }; > + > +=C2=A0=C2=A0=C2=A0 // SAFETY: > +=C2=A0=C2=A0=C2=A0 // - Since we're in the atomic_update callback, we're= guaranteed > by DRM that both the old and new > +=C2=A0=C2=A0=C2=A0 //=C2=A0=C2=A0 plane state are resent in this atomic = state. > +=C2=A0=C2=A0=C2=A0 // - We just created the state mutator above, so othe= r mutators > cannot be taken out on the plane > +=C2=A0=C2=A0=C2=A0 //=C2=A0=C2=A0 state yet. > +=C2=A0=C2=A0=C2=A0 let commit =3D unsafe { PlaneAtomicCommit::new(plane,= &state) }; > + > +=C2=A0=C2=A0=C2=A0 T::atomic_update(commit); > +} > + > +unsafe extern "C" fn atomic_check_callback( > +=C2=A0=C2=A0=C2=A0 plane: *mut bindings::drm_plane, > +=C2=A0=C2=A0=C2=A0 state: *mut bindings::drm_atomic_state, > +) -> i32 { > +=C2=A0=C2=A0=C2=A0 // SAFETY: > +=C2=A0=C2=A0=C2=A0 // - We're guaranteed `plane` is of type `Plane` v= ia type > invariants. > +=C2=A0=C2=A0=C2=A0 // - We're guaranteed by DRM that `plane` is pointing= to a valid > initialized state. > +=C2=A0=C2=A0=C2=A0 let plane =3D unsafe { Plane::from_raw(plane) }; > + > +=C2=A0=C2=A0=C2=A0 // SAFETY: We're guaranteed by DRM that `state` point= s to a > valid instance of `drm_atomic_state` > +=C2=A0=C2=A0=C2=A0 // We use ManuallyDrop here since AtomicStateComposer= would > otherwise drop a owned reference to > +=C2=A0=C2=A0=C2=A0 // the atomic state upon finishing this callback. > +=C2=A0=C2=A0=C2=A0 let state =3D ManuallyDrop::new(unsafe { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 > AtomicStateComposer::::new(NonNull::new_unchecked(state)) > +=C2=A0=C2=A0=C2=A0 }); > + > +=C2=A0=C2=A0=C2=A0 // SAFETY: > +=C2=A0=C2=A0=C2=A0 // - Since we're in the atomic check callback, we're = guaranteed > by DRM that both the old and > +=C2=A0=C2=A0=C2=A0 //=C2=A0=C2=A0 new plane state are present in this at= omic state > +=C2=A0=C2=A0=C2=A0 // - We just created the state composer above, so oth= er > composers cannot be taken out on the > +=C2=A0=C2=A0=C2=A0 //=C2=A0=C2=A0 plane state yet. > +=C2=A0=C2=A0=C2=A0 let check =3D unsafe { PlaneAtomicCheck::new(plane, &= state) }; > + > +=C2=A0=C2=A0=C2=A0 from_result(|| T::atomic_check(check).map(|_| 0)) > +} > diff --git a/rust/kernel/drm/kms/vblank.rs > b/rust/kernel/drm/kms/vblank.rs > new file mode 100644 > index 000000000000..dc34e02e8ccb > --- /dev/null > +++ b/rust/kernel/drm/kms/vblank.rs > @@ -0,0 +1,461 @@ > +// SPDX-License-Identifier: GPL-2.0 OR MIT > + > +//! DRM KMS vblank support. > +//! > +//! C header: > [`include/drm/drm_vblank.h`](srcfree/include/drm/drm_vblank.h) > + > +use super::{crtc::*, ModeObject, modes::*, Sealed}; > +use bindings; > +use core::{ > +=C2=A0=C2=A0=C2=A0 marker::*, > +=C2=A0=C2=A0=C2=A0 mem::{self, ManuallyDrop}, > +=C2=A0=C2=A0=C2=A0 ops::{Deref, Drop}, > +=C2=A0=C2=A0=C2=A0 ptr::null_mut, > +}; > +use kernel::{ > +=C2=A0=C2=A0=C2=A0 drm::device::Device, > +=C2=A0=C2=A0=C2=A0 error::{from_result, to_result}, > +=C2=A0=C2=A0=C2=A0 interrupt::LocalInterruptDisabled, > +=C2=A0=C2=A0=C2=A0 prelude::*, > +=C2=A0=C2=A0=C2=A0 time::Delta, > +=C2=A0=C2=A0=C2=A0 types::Opaque, > +}; > + > +/// The main trait for a driver to implement hardware vblank support > for a [`Crtc`]. > +/// > +/// # Invariants > +/// > +/// C FFI callbacks generated using this trait can safely assume > that input pointers to > +/// [`struct drm_crtc`] are always contained within a > [`Crtc`]. > +/// > +/// [`struct drm_crtc`]: srctree/include/drm/drm_crtc.h > +pub trait VblankSupport: Sized { > +=C2=A0=C2=A0=C2=A0 /// The parent [`DriverCrtc`]. > +=C2=A0=C2=A0=C2=A0 type Crtc: VblankDriverCrtc; > + > +=C2=A0=C2=A0=C2=A0 /// Enable vblank interrupts for this [`DriverCrtc`]. > +=C2=A0=C2=A0=C2=A0 fn enable_vblank( > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 crtc: &Crtc, > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 vblank_guard: &VblankGuard<'_= , Self::Crtc>, > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 irq: &LocalInterruptDisabled, > +=C2=A0=C2=A0=C2=A0 ) -> Result; > + > +=C2=A0=C2=A0=C2=A0 /// Disable vblank interrupts for this [`DriverCrtc`]= . > +=C2=A0=C2=A0=C2=A0 fn disable_vblank( > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 crtc: &Crtc, > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 vblank_guard: &VblankGuard<'_= , Self::Crtc>, > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 irq: &LocalInterruptDisabled, > +=C2=A0=C2=A0=C2=A0 ); > + > +=C2=A0=C2=A0=C2=A0 /// Retrieve the current vblank timestamp for this [`= Crtc`] > +=C2=A0=C2=A0=C2=A0 /// > +=C2=A0=C2=A0=C2=A0 /// If this function is being called from the driver'= s vblank > interrupt handler, > +=C2=A0=C2=A0=C2=A0 /// `handling_vblank_irq` will be `true`. > +=C2=A0=C2=A0=C2=A0 fn get_vblank_timestamp( > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 crtc: &Crtc, > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 in_vblank_irq: bool, > +=C2=A0=C2=A0=C2=A0 ) -> Option; > +} > + > +/// Trait used for CRTC vblank (or lack there-of) implementations. > Implemented internally. > +/// > +/// Drivers interested in implementing vblank support should refer > to [`VblankSupport`], drivers > +/// that don't have vblank support can use [`PhantomData`]. > +pub trait VblankImpl { > +=C2=A0=C2=A0=C2=A0 /// The parent [`DriverCrtc`]. > +=C2=A0=C2=A0=C2=A0 type Crtc: DriverCrtc; > + > +=C2=A0=C2=A0=C2=A0 /// The generated [`VblankOps`]. > +=C2=A0=C2=A0=C2=A0 const VBLANK_OPS: VblankOps; > +} > + > +/// C FFI callbacks for vblank management. > +/// > +/// Created internally by DRM. > +#[derive(Default)] > +pub struct VblankOps { > +=C2=A0=C2=A0=C2=A0 pub(crate) enable_vblank: Option bindings::drm_crtc) -> i32>, > +=C2=A0=C2=A0=C2=A0 pub(crate) disable_vblank: Option *mut bindings::drm_crtc)>, > +=C2=A0=C2=A0=C2=A0 pub(crate) get_vblank_timestamp: Option< > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 unsafe extern "C" fn( > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 crtc:= *mut bindings::drm_crtc, > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 max_e= rror: *mut i32, > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 vblan= k_time: *mut bindings::ktime_t, > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 in_vb= lank_irq: bool, > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 ) -> bool, > +=C2=A0=C2=A0=C2=A0 >, > +} > + > +impl VblankImpl for T { > +=C2=A0=C2=A0=C2=A0 type Crtc =3D T::Crtc; > + > +=C2=A0=C2=A0=C2=A0 const VBLANK_OPS: VblankOps =3D VblankOps { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 enable_vblank: Some(enable_vb= lank_callback::), > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 disable_vblank: Some(disable_= vblank_callback::), > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 get_vblank_timestamp: > Some(get_vblank_timestamp_callback::), > +=C2=A0=C2=A0=C2=A0 }; > +} > + > +impl VblankImpl for PhantomData > +where > +=C2=A0=C2=A0=C2=A0 T: DriverCrtc>, > +{ > +=C2=A0=C2=A0=C2=A0 type Crtc =3D T; > + > +=C2=A0=C2=A0=C2=A0 const VBLANK_OPS: VblankOps =3D VblankOps { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 enable_vblank: None, > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 disable_vblank: None, > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 get_vblank_timestamp: None, > +=C2=A0=C2=A0=C2=A0 }; > +} > + > +unsafe extern "C" fn enable_vblank_callback( > +=C2=A0=C2=A0=C2=A0 crtc: *mut bindings::drm_crtc, > +) -> i32 { > +=C2=A0=C2=A0=C2=A0 // SAFETY: We're guaranteed that `crtc` is of type > `Crtc` by type invariants. > +=C2=A0=C2=A0=C2=A0 let crtc =3D unsafe { Crtc::::from_raw(crtc)= }; > + > +=C2=A0=C2=A0=C2=A0 // SAFETY: This callback happens with IRQs disabled > +=C2=A0=C2=A0=C2=A0 let irq =3D unsafe { LocalInterruptDisabled::assume_d= isabled() }; > + > +=C2=A0=C2=A0=C2=A0 // SAFETY: This callback happens with `vbl_lock` alre= ady held > +=C2=A0=C2=A0=C2=A0 // We don't want to drop `vbl_lock` when this callbac= k completes > since DRM will do this for us, > +=C2=A0=C2=A0=C2=A0 // so wrap the `VblankGuard` in a `ManuallyDrop` > +=C2=A0=C2=A0=C2=A0 let vblank_guard =3D ManuallyDrop::new(unsafe { > VblankGuard::new(crtc, irq) }); > + > +=C2=A0=C2=A0=C2=A0 from_result(|| T::enable_vblank(crtc, &vblank_guard, > irq).map(|_| 0)) > +} > + > +unsafe extern "C" fn disable_vblank_callback(crtc: > *mut bindings::drm_crtc) { > +=C2=A0=C2=A0=C2=A0 // SAFETY: We're guaranteed that `crtc` is of type > `Crtc` by type invariants. > +=C2=A0=C2=A0=C2=A0 let crtc =3D unsafe { Crtc::::from_raw(crtc)= }; > + > +=C2=A0=C2=A0=C2=A0 // SAFETY: This callback happens with IRQs disabled > +=C2=A0=C2=A0=C2=A0 let irq =3D unsafe { LocalInterruptDisabled::assume_d= isabled() }; > + > +=C2=A0=C2=A0=C2=A0 // SAFETY: This call happens with `vbl_lock` already = held > +=C2=A0=C2=A0=C2=A0 // We don't want to drop `vbl_lock` when this callbac= k completes > since DRM will do this for us, > +=C2=A0=C2=A0=C2=A0 // so wrap the `VblankGuard` in a `ManuallyDrop` > +=C2=A0=C2=A0=C2=A0 let vblank_guard =3D ManuallyDrop::new(unsafe { > VblankGuard::new(crtc, irq) }); > + > +=C2=A0=C2=A0=C2=A0 T::disable_vblank(crtc, &vblank_guard, irq); > +} > + > +unsafe extern "C" fn get_vblank_timestamp_callback VblankSupport>( > +=C2=A0=C2=A0=C2=A0 crtc: *mut bindings::drm_crtc, > +=C2=A0=C2=A0=C2=A0 max_error: *mut i32, > +=C2=A0=C2=A0=C2=A0 vblank_time: *mut bindings::ktime_t, > +=C2=A0=C2=A0=C2=A0 in_vblank_irq: bool, > +) -> bool { > +=C2=A0=C2=A0=C2=A0 // SAFETY: We're guaranteed `crtc` is of type `Crtc` by > type invariance > +=C2=A0=C2=A0=C2=A0 let crtc =3D unsafe { Crtc::::from_raw(crtc)= }; > + > +=C2=A0=C2=A0=C2=A0 if let Some(timestamp) =3D T::get_vblank_timestamp(cr= tc, > in_vblank_irq) { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // SAFETY: Both of these poin= ters are guaranteed by the C > API to be valid > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 unsafe { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 (*max= _error) =3D timestamp.max_error; > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 (*vbl= ank_time) =3D timestamp.time.as_nanos(); > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 }; > + > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 true > +=C2=A0=C2=A0=C2=A0 } else { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 false > +=C2=A0=C2=A0=C2=A0 } > +} > + > +/// A vblank timestamp. > +/// > +/// This type is used by [`VblankSupport::get_vblank_timestamp`] for > the implementor to return the > +/// current vblank timestamp for the hardware. > +#[derive(Copy, Clone)] > +pub struct VblankTimestamp { > +=C2=A0=C2=A0=C2=A0 /// The actual vblank timestamp in nanoseconds, accur= acy to > within [`Self::max_error`] > +=C2=A0=C2=A0=C2=A0 /// nanoseconds. > +=C2=A0=C2=A0=C2=A0 pub time: Delta, > + > +=C2=A0=C2=A0=C2=A0 /// Maximum allowable timestamp error in nanoseconds > +=C2=A0=C2=A0=C2=A0 pub max_error: i32, > +} > + > +/// A trait for [`DriverCrtc`] implementations with hardware vblank > support. > +/// > +/// This trait is implemented internally by DRM for any > [`DriverCrtc`] implementation that > +/// implements [`VblankSupport`]. It is used to expose hardware- > vblank driver exclusive methods and > +/// data to users. > +pub trait VblankDriverCrtc: DriverCrtc {} > + > +impl VblankDriverCrtc for T > +where > +=C2=A0=C2=A0=C2=A0 T: DriverCrtc, > +=C2=A0=C2=A0=C2=A0 V: VblankSupport, > +{ > +} > + > +impl Crtc { > +=C2=A0=C2=A0=C2=A0 /// Retrieve a reference to the [`VblankCrtc`] for th= is > [`Crtc`]. > +=C2=A0=C2=A0=C2=A0 pub(crate) fn vblank_crtc(&self) -> &VblankCrtc { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // SAFETY: > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // - The data layouts of thes= e types are equivalent via > `VblankCrtc`s type invariants > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // - We don't expose any way = of calling `vblank_crtc()` > before `drm_vblank_init()` has been > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 //=C2=A0=C2=A0 called. > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 unsafe { VblankCrtc::from_raw= (self.get_vblank_ptr()) } > +=C2=A0=C2=A0=C2=A0 } > + > +=C2=A0=C2=A0=C2=A0 /// Access vblank related infrastructure for a [`Crtc= `]. > +=C2=A0=C2=A0=C2=A0 /// > +=C2=A0=C2=A0=C2=A0 /// This function explicitly locks the device's vblan= k lock, and > allows access to controlling > +=C2=A0=C2=A0=C2=A0 /// the vblank configuration for this CRTC. The lock = is dropped > once [`VblankGuard`] is > +=C2=A0=C2=A0=C2=A0 /// dropped. > +=C2=A0=C2=A0=C2=A0 pub fn vblank_lock<'a>(&'a self, irq: &'a > LocalInterruptDisabled) -> VblankGuard<'a, T> { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // SAFETY: `vbl_lock` is init= ialized for as long as `Crtc` > is available to users > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // INVARIANT: We just acquire= d `vbl_lock`, fulfilling the > invariants of `VblankGuard` > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 unsafe { bindings::spin_lock(= &raw mut > (*self.drm_dev().as_raw()).vbl_lock) }; > + > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // SAFETY: We just acquired v= bl_lock above > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 unsafe { VblankGuard::new(sel= f, irq) } > +=C2=A0=C2=A0=C2=A0 } > + > +=C2=A0=C2=A0=C2=A0 /// Trigger a vblank event on this [`Crtc`]. > +=C2=A0=C2=A0=C2=A0 /// > +=C2=A0=C2=A0=C2=A0 /// Drivers should use this in their vblank interrupt= handlers > to update the vblank counter and > +=C2=A0=C2=A0=C2=A0 /// send any signals that may be pending. > +=C2=A0=C2=A0=C2=A0 /// > +=C2=A0=C2=A0=C2=A0 /// Returns whether or not the vblank event was handl= ed. > +=C2=A0=C2=A0=C2=A0 #[inline] > +=C2=A0=C2=A0=C2=A0 pub fn handle_vblank(&self) -> bool { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // SAFETY: `as_raw()` always = returns a valid pointer to an > initialized drm_crtc. > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 unsafe { bindings::drm_crtc_h= andle_vblank(self.as_raw()) } > +=C2=A0=C2=A0=C2=A0 } > + > +=C2=A0=C2=A0=C2=A0 /// Forbid vblank events for a [`Crtc`]. > +=C2=A0=C2=A0=C2=A0 /// > +=C2=A0=C2=A0=C2=A0 /// This function disables vblank events for a [`Crtc= `], even if > [`VblankRef`] objects exist. > +=C2=A0=C2=A0=C2=A0 #[inline] > +=C2=A0=C2=A0=C2=A0 pub fn vblank_off(&self) { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // SAFETY: `as_raw()` always = returns a valid pointer to an > initialized drm_crtc. > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 unsafe { bindings::drm_crtc_v= blank_off(self.as_raw()) } > +=C2=A0=C2=A0=C2=A0 } > + > +=C2=A0=C2=A0=C2=A0 /// Allow vblank events for a [`Crtc`]. > +=C2=A0=C2=A0=C2=A0 /// > +=C2=A0=C2=A0=C2=A0 /// This function allows users to enable vblank event= s and > acquire [`VblankRef`] objects again. > +=C2=A0=C2=A0=C2=A0 #[inline] > +=C2=A0=C2=A0=C2=A0 pub fn vblank_on(&self) { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // SAFETY: `as_raw()` always = returns a valid pointer to an > initialized drm_crtc. > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 unsafe { bindings::drm_crtc_v= blank_on(self.as_raw()) } > +=C2=A0=C2=A0=C2=A0 } > + > +=C2=A0=C2=A0=C2=A0 /// Enable vblank events for a [`Crtc`]. > +=C2=A0=C2=A0=C2=A0 /// > +=C2=A0=C2=A0=C2=A0 /// Returns a [`VblankRef`] which will allow vblank e= vents to be > sent until it is dropped. Note > +=C2=A0=C2=A0=C2=A0 /// that vblank events may still be disabled by > [`Self::vblank_off`]. > +=C2=A0=C2=A0=C2=A0 #[must_use =3D "Vblanks are only enabled until the re= sult from > this function is dropped"] > +=C2=A0=C2=A0=C2=A0 pub fn vblank_get(&self) -> Result> = { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 VblankRef::new(self) > +=C2=A0=C2=A0=C2=A0 } > +} > + > +/// Common methods available on any [`CrtcState`] whose [`Crtc`] > implements [`VblankSupport`]. > +/// > +/// This trait is implemented automatically by DRM for any > [`DriverCrtc`] implementation that > +/// implements [`VblankSupport`]. > +pub trait RawVblankCrtcState: AsRawCrtcState { > +=C2=A0=C2=A0=C2=A0 /// Return the [`PendingVblankEvent`] for this CRTC s= tate, if > there is one. > +=C2=A0=C2=A0=C2=A0 fn get_pending_vblank_event(&mut self) -> > Option> > +=C2=A0=C2=A0=C2=A0 where > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 Self: Sized, > +=C2=A0=C2=A0=C2=A0 { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // SAFETY: The driver is the = only one that will ever modify > this data, and since our > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // interface follows rust's d= ata aliasing rules that means > this is safe to read > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 let event_ptr =3D unsafe { *s= elf.as_raw() }.event; > + > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 (!event_ptr.is_null()).then_s= ome(PendingVblankEvent(self)) > +=C2=A0=C2=A0=C2=A0 } > +} > + > +impl RawVblankCrtcState for T > +where > +=C2=A0=C2=A0=C2=A0 T: AsRawCrtcState>, > +=C2=A0=C2=A0=C2=A0 C: VblankDriverCrtc, > +{ > +} > + > +/// A pending vblank event from an atomic state > +pub struct PendingVblankEvent<'a, T: RawVblankCrtcState>(&'a mut T); > + > +impl<'a, T: RawVblankCrtcState> PendingVblankEvent<'a, T> { > +=C2=A0=C2=A0=C2=A0 /// Send this [`PendingVblankEvent`]. > +=C2=A0=C2=A0=C2=A0 /// > +=C2=A0=C2=A0=C2=A0 /// A [`PendingVblankEvent`] can only be sent once, s= o this > function consumes the > +=C2=A0=C2=A0=C2=A0 /// [`PendingVblankEvent`]. > +=C2=A0=C2=A0=C2=A0 pub fn send(self) > +=C2=A0=C2=A0=C2=A0 where > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 T: RawVblankCrtcState>, > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 C: VblankDriverCrtc, > +=C2=A0=C2=A0=C2=A0 { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 let crtc: &Crtc =3D self.0= .crtc(); > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 let event_lock =3D crtc.drm_d= ev().event_lock(); > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 let _guard =3D event_lock.loc= k(); > + > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // SAFETY: > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // - We now hold the appropri= ate lock to call this function > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // - Vblanks are enabled as p= roved by `vbl_ref`, as per the > C api requirements > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // - Our interface is proof t= hat `event` is non-null > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 unsafe { bindings::drm_crtc_s= end_vblank_event(crtc.as_raw(), > (*self.0.as_raw()).event) }; > + > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // SAFETY: The mutable refere= nce in `self.state` is proof > that it is safe to mutate this, > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // and DRM expects us to set = this to NULL once we've sent > the vblank event. > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 unsafe { (*self.0.as_raw()).e= vent =3D null_mut() }; > +=C2=A0=C2=A0=C2=A0 } > + > +=C2=A0=C2=A0=C2=A0 /// Arm this [`PendingVblankEvent`] to be sent later = by the > CRTC's vblank interrupt handler. > +=C2=A0=C2=A0=C2=A0 /// > +=C2=A0=C2=A0=C2=A0 /// A [`PendingVblankEvent`] can only be armed once, = so this > function consumes the > +=C2=A0=C2=A0=C2=A0 /// [`PendingVblankEvent`]. As well, it requires a [`= VblankRef`] > so that vblank interrupts > +=C2=A0=C2=A0=C2=A0 /// remain enabled until the [`PendingVblankEvent`] h= as been > sent out by the driver's vblank > +=C2=A0=C2=A0=C2=A0 /// interrupt handler. > +=C2=A0=C2=A0=C2=A0 pub fn arm(self, vbl_ref: VblankRef<'_, C>) > +=C2=A0=C2=A0=C2=A0 where > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 T: RawVblankCrtcState>, > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 C: VblankDriverCrtc, > +=C2=A0=C2=A0=C2=A0 { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 let crtc: &Crtc =3D self.0= .crtc(); > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 let event_lock =3D crtc.drm_d= ev().event_lock(); > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 let _guard =3D event_lock.loc= k(); > + > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // SAFETY: > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // - We now hold the appropri= ate lock to call this function > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // - Vblanks are enabled as p= roved by `vbl_ref`, as per the > C api requirements > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // - Our interface is proof t= hat `event` is non-null > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 unsafe { bindings::drm_crtc_a= rm_vblank_event(crtc.as_raw(), > (*self.0.as_raw()).event) }; > + > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // SAFETY: The mutable refere= nce in `self.state` is proof > that it is safe to mutate this, > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // and DRM expects us to set = this to NULL once we've armed > the vblank event. > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 unsafe { (*self.0.as_raw()).e= vent =3D null_mut() }; > + > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // DRM took ownership of `vbl= _ref` after we called > `drm_crtc_arm_vblank_event` > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 mem::forget(vbl_ref); > +=C2=A0=C2=A0=C2=A0 } > +} > + > +/// A borrowed vblank reference. > +/// > +/// This object keeps the vblank reference count for a [`Crtc`] > incremented for as long as it > +/// exists, enabling vblank interrupts for said [`Crtc`] until all > references are dropped, or > +/// [`Crtc::vblank_off`] is called - whichever comes first. > +pub struct VblankRef<'a, T: VblankDriverCrtc>(&'a Crtc); > + > +impl Drop for VblankRef<'_, T> { > +=C2=A0=C2=A0=C2=A0 fn drop(&mut self) { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // SAFETY: as_raw() returns a= valid pointer to an > initialized drm_crtc > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 unsafe { bindings::drm_crtc_v= blank_put(self.0.as_raw()) }; > +=C2=A0=C2=A0=C2=A0 } > +} > + > +impl<'a, T: VblankDriverCrtc> VblankRef<'a, T> { > +=C2=A0=C2=A0=C2=A0 fn new(crtc: &'a Crtc) -> Result { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // SAFETY: as_raw() returns a= valid pointer to an > initialized drm_crtc > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 to_result(unsafe { > bindings::drm_crtc_vblank_get(crtc.as_raw()) })?; > + > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 Ok(Self(crtc)) > +=C2=A0=C2=A0=C2=A0 } > +} > + > +/// The base wrapper for [`drm_vblank_crtc`]. > +/// > +/// Users will rarely interact with this object directly, it is a > simple wrapper around > +/// [`drm_vblank_crtc`] which provides access to methods and data > that is not protected by a lock. > +/// > +/// # Invariants > +/// > +/// This type has an identical data layout to [`drm_vblank_crtc`]. > +/// > +/// [`drm_vblank_crtc`]: srctree/include/drm/drm_vblank.h > +#[repr(transparent)] > +pub struct VblankCrtc(Opaque, > PhantomData); > + > +impl VblankCrtc { > +=C2=A0=C2=A0=C2=A0 pub(crate) fn as_raw(&self) -> *mut bindings::drm_vbl= ank_crtc { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 self.0.get() > +=C2=A0=C2=A0=C2=A0 } > + > +=C2=A0=C2=A0=C2=A0 // SAFETY: The caller promises that `ptr` points to a= valid > instance of > +=C2=A0=C2=A0=C2=A0 // `bindings::drm_vblank_crtc`, and that access to th= is > structure has been properly serialized > +=C2=A0=C2=A0=C2=A0 pub(crate) unsafe fn from_raw<'a>(ptr: *mut > bindings::drm_vblank_crtc) -> &'a Self { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // SAFETY: Our data layouts a= re identical via > #[repr(transparent)] > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 unsafe { &*ptr.cast() } > +=C2=A0=C2=A0=C2=A0 } > + > +=C2=A0=C2=A0=C2=A0 /// Returns the [`Device`] for this [`VblankGuard`] > +=C2=A0=C2=A0=C2=A0 pub fn drm_dev(&self) -> &Device { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // SAFETY: `drm` is initializ= ed, invariant and valid > throughout our lifetime > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 unsafe { Device::from_raw((*s= elf.as_raw()).dev) } > +=C2=A0=C2=A0=C2=A0 } > +} > + > +// NOTE: This type does not use a `Guard` because the mutex is not > contained within the same > +// structure as the relevant CRTC > +/// An interface for accessing and controlling vblank related state > for a [`Crtc`]. > +/// > +/// This type may be returned from some [`VblankSupport`] callbacks, > or manually via > +/// [`Crtc::vblank_lock`]. It provides access to methods and data > which require > +/// [`drm_device.vbl_lock`] be held. > +/// > +/// # Invariants > +/// > +/// - [`drm_device.vbl_lock`] is acquired whenever an instance of > this type exists. > +/// - Shares the invariants of [`VblankCrtc`]. > +/// > +/// [`drm_device.vbl_lock`]: srctree/include/drm/drm_device.h > +#[repr(transparent)] > +pub struct VblankGuard<'a, T: VblankDriverCrtc>(&'a VblankCrtc); > + > +impl<'a, T: VblankDriverCrtc> VblankGuard<'a, T> { > +=C2=A0=C2=A0=C2=A0 /// Construct a new [`VblankGuard`] > +=C2=A0=C2=A0=C2=A0 /// > +=C2=A0=C2=A0=C2=A0 /// # Safety > +=C2=A0=C2=A0=C2=A0 /// > +=C2=A0=C2=A0=C2=A0 /// The caller must have already acquired > [`drm_device.vbl_lock`]. > +=C2=A0=C2=A0=C2=A0 /// > +=C2=A0=C2=A0=C2=A0 /// [`drm_device.vbl_lock`]: srctree/include/drm/drm_= device.h > +=C2=A0=C2=A0=C2=A0 pub(crate) unsafe fn new(crtc: &'a Crtc, _irq: &'a > LocalInterruptDisabled) -> Self { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // INVARIANT: The caller prom= ises that we've acquired > `vbl_lock` > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 Self(crtc.vblank_crtc()) > +=C2=A0=C2=A0=C2=A0 } > + > +=C2=A0=C2=A0=C2=A0 /// Returns the duration of a single scanout frame in= ns. > +=C2=A0=C2=A0=C2=A0 pub fn frame_duration(&self) -> i32 { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // SAFETY: We hold the approp= riate lock for this read via > our type invariants. > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 unsafe { *self.as_raw() }.fra= medur_ns > +=C2=A0=C2=A0=C2=A0 } > + > +=C2=A0=C2=A0=C2=A0 /// Return the vblank core's cached copy of the curre= ntly set > display mode. > +=C2=A0=C2=A0=C2=A0 /// > +=C2=A0=C2=A0=C2=A0 /// If the display is disabled, this will return `Non= e`. > +=C2=A0=C2=A0=C2=A0 pub fn hwmode(&self) -> Option<&DisplayMode> { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // SAFETY: We hold the approp= riate lock for this read via > our type invariants. > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 let ptr =3D unsafe { &raw con= st (*self.as_raw()).hwmode }; > + > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // SAFETY: We check here if t= he cached DisplayMode is Null, > which means the only other > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // possibility is that the po= inter points to a valid > initialized drm_display_mode. > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 (!ptr.is_null()).then(|| unsa= fe { DisplayMode::as_ref(ptr) > }) > +=C2=A0=C2=A0=C2=A0 } > +} > + > +impl Deref for VblankGuard<'_, T> { > +=C2=A0=C2=A0=C2=A0 type Target =3D VblankCrtc; > + > +=C2=A0=C2=A0=C2=A0 fn deref(&self) -> &Self::Target { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 &self.0 > +=C2=A0=C2=A0=C2=A0 } > +} > + > +impl Drop for VblankGuard<'_, T> { > +=C2=A0=C2=A0=C2=A0 fn drop(&mut self) { > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // SAFETY: > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // - We acquired this spinloc= k when creating this object > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // - This lock is guaranteed = to be initialized for as long > as our DRM device is exposed to > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 //=C2=A0=C2=A0 users. > +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 unsafe { bindings::spin_unloc= k(&raw mut > (*self.drm_dev().as_raw()).vbl_lock) } > +=C2=A0=C2=A0=C2=A0 } > +}