Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
  • lyudess/linux
1 result
Show changes
Commits on Source (44)
  • Asahi Lina's avatar
    rust: helpers: Add bindings/wrappers for dma_resv · 48495b3c
    Asahi Lina authored and Lyude Paul's avatar Lyude Paul committed
    
    This is just for basic usage in the DRM shmem abstractions for implied
    locking, not intended as a full DMA Reservation abstraction yet.
    
    Signed-off-by: default avatarAsahi Lina <lina@asahilina.net>
    48495b3c
  • Asahi Lina's avatar
    drm/gem-shmem: Export VM ops functions · 07869664
    Asahi Lina authored and Lyude Paul's avatar Lyude Paul committed
    
    There doesn't seem to be a way for the Rust bindings to get a
    compile-time constant reference to drm_gem_shmem_vm_ops, so we need to
    duplicate that structure in Rust... this isn't nice...
    
    Signed-off-by: default avatarAsahi Lina <lina@asahilina.net>
    07869664
  • Asahi Lina's avatar
    rust: drm: gem: shmem: Add DRM shmem helper abstraction · 0808eb6c
    Asahi Lina authored and Lyude Paul's avatar Lyude Paul committed
    
    The DRM shmem helper includes common code useful for drivers which
    allocate GEM objects as anonymous shmem. Add a Rust abstraction for
    this. Drivers can choose the raw GEM implementation or the shmem layer,
    depending on their needs.
    
    Lyude changelog:
    * Rebase
    * Squash "rust: drm: device: Convert Device to AlwaysRefCounted" as this
      commit doesn't compile without it anyway
    
    Signed-off-by: default avatarAsahi Lina <lina@asahilina.net>
    0808eb6c
  • Maíra Canal's avatar
    rust: make platform device implement the Send and Sync traits · c07ac449
    Maíra Canal authored and Lyude Paul's avatar Lyude Paul committed
    Currently, platform device holds only a pointer, which is safe to be
    used from any thread. This is useful for devices that are not probed,
    such as virtual devices.
    
    [Lyude: cherry-picked from 9a6a07a9828ea840194c8428d45cd54d5c386265
    https://github.com/mairacanal/linux/tree/vgem/wip-dma
    
     ]
    
    Signed-off-by: default avatarMaíra Canal <mcanal@igalia.com>
    c07ac449
  • Maíra Canal's avatar
    rust: device: add Device Resource Management abstraction · 141318cf
    Maíra Canal authored and Lyude Paul's avatar Lyude Paul committed
    Create an abstraction to allow Rust drivers to open a new devres group for
    the device with a defined id. Moreover, implement the related Drop trait
    to release the devres group.
    
    [Lyude: cherry picked from 8e57c7d478ddeabe61c53b6dd01fd326e3256221 from
    https://github.com/mairacanal/linux/pull/11
    
     ]
    
    Signed-off-by: default avatarMaíra Canal <mcanal@igalia.com>
    [Rebased]
    Signed-off-by: Lyude Paul's avatarLyude Paul <lyude@redhat.com>
    141318cf
  • Lyude Paul's avatar
    a7b54175
  • Lyude Paul's avatar
    rust: lock: Expose Guard::new() and add Guard::new_with() · ca180e8f
    Lyude Paul authored
    
    Since we have the ability to retrieve Lock structures from raw C structures
    now, we also want the ability to do so for guards - as this can be useful
    in the context of C callbacks where we know that a lock is already held,
    and may need to take advantage of that to perform operations under said
    lock.
    
    So, let's make Guard::new() a public function. Additionally, let's also add
    a Guard::new_with() function to match the lock() and lock_with() pattern
    that we currently have with locks. This way users get the additional
    protection of associating the lifetime of the lock context with the Guard
    even in unsafe contexts.
    
    Signed-off-by: Lyude Paul's avatarLyude Paul <lyude@redhat.com>
    ca180e8f
  • Lyude Paul's avatar
    WIP delete me: Fix bug in from_raw() · 6a7f920d
    Lyude Paul authored
    This doesnt' really matter though, since we already decided from_raw() is a
    bad idea when we can just from_raw the actual lock instead
    6a7f920d
  • Lyude Paul's avatar
    WIP: rust: lock: Add B::is_locked() assertion to Guard::new() · f75561b7
    Lyude Paul authored
    TODO: turn this into its own commit in preparation for making Guard::new()
    public
    f75561b7
  • Lyude Paul's avatar
    WIP: rust/drm: Add fourcc bindings · 169d51e4
    Lyude Paul authored
    
    This adds some very basic rust bindings for fourcc. We only have a single
    format code added for the moment, but this is enough to get a driver
    registered.
    
    TODO:
    * Write up something to automatically generate constants from the fourcc
      headers
    
    Signed-off-by: Lyude Paul's avatarLyude Paul <lyude@redhat.com>
    169d51e4
  • Lyude Paul's avatar
    WIP: rust: drm: Add traits for registering KMS devices · 1bd59d0b
    Lyude Paul authored
    
    This commit adds some traits for registering DRM devices with KMS support,
    implemented through the kernel::drm::kms::Kms trait. Devices which don't
    have KMS support can simply use PhantomData<Self>.
    
    Signed-off-by: Lyude Paul's avatarLyude Paul <lyude@redhat.com>
    
    ---
    
    TODO:
    * Generate feature flags automatically, these shouldn't need to be
      specified by the user
    1bd59d0b
  • Lyude Paul's avatar
    rust: drm/kms/fbdev: Add FbdevShmem · bf255ed9
    Lyude Paul authored
    
    For drivers which use the shmem based GEM helpers, they'll want to use the
    relevant drm_fbdev_shmem_setup() functions instead of the
    drm_fbdev_dma_setup() functions. To allow for this, introduce another
    FbdevImpl that such drivers can use instead of FbdevDma.
    
    Signed-off-by: Lyude Paul's avatarLyude Paul <lyude@redhat.com>
    bf255ed9
  • Lyude Paul's avatar
    rust: drm/kms: Introduce the main ModeConfigObject traits · 70a0ee27
    Lyude Paul authored
    
    The KMS API has a very consistent idea of a "mode config object", which
    includes any object with a drm_mode_object struct embedded in it. These
    objects have their own object IDs which DRM exposes to userspace, and we
    introduce the ModeConfigObject trait to represent any object matching these
    characteristics.
    
    One slightly less consistent trait of these objects however: some mode
    objects have a reference count, while others don't. Since rust requires
    that we are able to define the lifetime of an object up-front, we introduce
    two other super-traits of ModeConfigObject for this:
    
    * StaticModeObject - this trait represents any mode object which does not
      have a reference count of its own. Such objects can be considered to
      share the lifetime of their parent KMS device
    * RcModeObject - this trait represents any mode object which does have its
      own reference count. Objects implementing this trait get a free blanket
      implementation of AlwaysRefCounted, and as such can be used with the ARef
      container without us having to implement AlwaysRefCounted for each
      individual mode object.
    
    This will be able to handle most lifetimes we'll need with one exception:
    it's entirely possible a driver may want to hold a "owned" reference to a
    static mode object. We allow for this by introducing the KmsRef container,
    which grabs an owned refcount to the parent KMS device of a
    StaticModeObject and holds a pointer to said object - essentially allowing
    it to act identically to an owned refcount by preventing the device's
    lifetime from ending until the KmsRef is dropped. I choose not to use
    AlwaysRefCounted for this as holding a refcount to the device has its own
    set of implications since if you forget to drop the KmsRef the device will
    never be destroyed.
    
    Signed-off-by: Lyude Paul's avatarLyude Paul <lyude@redhat.com>
    70a0ee27
  • Lyude Paul's avatar
    rust: drm/kms: Add bindings for drm_connector · 18f745d4
    Lyude Paul authored
    
    We start off by introducing wrappers for the first important type of mode
    object: a DRM display connector. This introduces Connector<T:
    DriverConnector> and ConnectorState<T: DriverConnectorState>. Both
    DriverConnector and DriverConnectorState must be implemented by KMS
    drivers, and a driver may have as many implementations of these two traits
    as it needs. This also introduces the general data pattern we'll be using
    for all of the core mode objects that can be used in atomic commits.
    
    It's important to note that both Connector<T> and ConnectorState<T> are
    intended to be "subclassable". To explain what this means, we need to look
    at how a DRM driver normally uses objects like DRM connectors.
    
    Typically, a driver in C will define its connectors like so:
    
    struct foo_connector {
      struct drm_connector base;
      int bar;
    }
    
    Note that we have a drm_connector struct embedded in foo_connector, but we
    have data which comes after it which is defined by the driver. This is
    important for a number of reasons: connectors can have their own mutexes
    and various other hardware-specific information that a driver may want
    access to at any time. The same goes for drm_connector_state, where drivers
    will subclass this struct in the same way. It's worth noting as well that
    it isn't uncommon for a driver to have multiple types of connectors, but
    we'll handle in a later commit.
    
    As a result, we've designed Connector<T> and ConnectorState<T> so that for
    both types: a DRM driver can add custom data into the T. As well, there's
    some basic limitations on how this data may be accessed:
    
    * Data within the `DriverConnector` struct is pinned in order to allow
      mutexes and other structs which need pinning to be stored within it. As
      well, it is impossible to get a direct mutable reference to the data
      within DriverConnector - as there's no locks for doing so which would
      cause a race condition.
    * Data within the `DriverConnectorState` struct is currently not pinned.
      While it's not unheard of for a driver to put something like a mutex in
      its atomic states, (VKMS actually does this in some spots) this quickly
      complicates things especially with nonblocking modesets - and doesn't
      really fit into the philosophy of an atomic state anyway. We may add
      support for this in the future later if this does end up being needed,
      but for now we hold back in order to make it much easier for drivers to
      access private data within the atomic state.
      As well, the functions we provide for converting to/from raw connector
      state pointers are notably different from many other rust types in the
      kernel. Instead of converting raw state pointers to raw ConnectorState<T>
      pointers, we allow for direct immutable and mutable references. The
      reason for this is that it makes accessing private driver data in the
      state much easier, and unlike Connector<T> - we can actually uphold
      all of the required data aliasing rules thanks to states only being
      mutable by a single thread before they've been swapped in.
      Note that currently, we don't provide a way to access said private data
      for ConnectorState<T> since allowing direct access to a &mut
      ConnectorState<T> could allow a caller to modify portions of
      drm_connector_state which are meant to be invariant throughout the
      lifetime of the connector state. We'll address this in the next few
      commits when we introduce the global atomic state type.
    
    And finally - we introduce the following internal traits for the crate side
    of things:
    
      * AsRawConnector - any type which can spit out a *mut
        bindings::drm_connector or be recovered from one
      * AsRawConnectorState - any type which can return a reference to a
        bindings::drm_connector_state
      * private::AsRawConnectorState - just methods for AsRawConnectorState
        that we don't want to be accessible to our users (since they could be
        used to introduce UB)
      * FromRawConnectorState - any type which can be recovered from a raw
        pointer to a bindings::drm_connector_state
    
    The reason for having AsRawConnectorState and FromRawConnectorState as
    separate traits unlike AsRawConnector is due to the fact that we'll
    introduce objects later on which can be used as DRM connector states, but
    cannot be directly derived from a *mut bindings::drm_connector_state
    because they hold additional state or have additional side-effects.
    
    Likewise, we'll also have other objects which can be used as raw DRM
    connectors - hence AsRawConnector.
    
    Signed-off-by: Lyude Paul's avatarLyude Paul <lyude@redhat.com>
    18f745d4
  • Lyude Paul's avatar
    rust: drm/kms: Add drm_plane bindings · 71b21d74
    Lyude Paul authored
    
    The next step is adding a set of basic bindings to create a plane, which
    has to happen before we can create a CRTC (since we need to be able to at
    least specify a primary plane for a CRTC upon creation). This mostly
    follows the same general pattern as connectors (AsRawPlane,
    AsRawPlaneState, etc.).
    
    There is one major difference with planes vs. other types of atomic mode
    objects: drm_plane_state isn't the only base plane struct used in DRM
    drivers, as some drivers will use helpers like drm_shadow_plane_state which
    have a drm_plane_state embedded within them.
    
    Since we'll eventually be adding bindings for shadow planes, we introduce a
    PlaneStateHelper trait - which represents any data type which can be used
    as the main wrapping structure around a drm_plane_state - and we implement
    this trait for PlaneState<T>. This trait can be used in our C callbacks to
    allow for drivers to use different wrapping structures without needing to
    implement a separate set of FFI callbacks for each type. Currently planes
    are the only type I'm aware of which do this.
    
    Signed-off-by: Lyude Paul's avatarLyude Paul <lyude@redhat.com>
    
    ---
    
    V2:
    * Start using Gerry Guo's updated #[vtable] function so that our driver
      operations table has a static location in memory
    71b21d74
  • Lyude Paul's avatar
    WIP: rust: drm/kms: Add drm_crtc bindings · 14b4f03a
    Lyude Paul authored
    
    This introduces basic bindings for DRM CRTCs which follow the same general
    pattern as connectors and planes (e.g. AsRawCrtc, AsRawCrtcState, etc.).
    There is one big difference though - drm_crtc_state appears to be the one
    atomic state that actually has data which can be mutated from outside of
    the atomic commit phase - which means we can't keep rust referencs to it,
    and instead need to use the Opaque type and implement things through
    pointers instead.
    
    This should be the last mode object we're introducing for the time being
    with its own atomic state. Note that we've not added bindings for private
    modesetting objects yet, but I don't think those will be needed for rvkms -
    and the same general patterns we're using here should work for adding
    private modesetting objects.
    
    Signed-off-by: Lyude Paul's avatarLyude Paul <lyude@redhat.com>
    
    ---
    
    TODO:
    * Add commit data in the future
    14b4f03a
  • Lyude Paul's avatar
    rust: drm/kms: Add bindings for drm_encoder · d143e843
    Lyude Paul authored
    
    The last thing we need to be able to register a KMS driver is the ability
    to create DRM encoders, so let's add bindings for that. Again, these
    bindings follow the same general pattern as CRTCs, planes, and connector
    with one difference: encoders don't have an atomic state.
    
    Note that not having an atomic state doesn't mean there aren't plenty of
    valid usecases for a driver to stick private data within a DRM encoder,
    hence why we reuse the aforementioned pattern.
    
    Signed-off-by: Lyude Paul's avatarLyude Paul <lyude@redhat.com>
    d143e843
  • Lyude Paul's avatar
    WIP: rust: drm/kms: Add Connector.attach_encoder() · 47e67048
    Lyude Paul authored
    
    This adds a simple binding for completing the last step of creating a DRM
    connector - attaching its encoder. This function should only be called
    before the connector is registered, and DRM should enforce this itself by
    returning an error if a driver tries to add an encoder to an
    already-registered DRM connector.
    
    Note that unlike most of the methods we'll be adding to DRM mode objects,
    this is directly implemented on the Connector<T> type since I don't really
    think it would make sense for us to allow this operation on an
    OpaqueConnector (a DRM connector without a known DriverConnector
    implementation, something we'll be adding in the next few commits).
    
    Signed-off-by: Lyude Paul's avatarLyude Paul <lyude@redhat.com>
    
    ---
    
    TODO:
    * Figure out a solution for making sure that this can only be called when a
      Connector is unregistered, probably via an UnregisteredConnector type.
    47e67048
  • Lyude Paul's avatar
    rust: drm/kms: Add DriverConnector::get_mode callback · 380657fa
    Lyude Paul authored
    
    Next up is filling out some of the basic connector hotplugging callbacks -
    which we'll need for setting up the fbdev helpers for KMS devices. Note
    that connector hotplugging in DRM follows a BFL scheme: pretty much all
    probing is protected under the mighty drm_device->mode_config.lock, which
    of course is a bit counter-intuitive to rust's locking schemes where data
    is always associated with its lock.
    
    Since that lock is embedded in an FFI type and not a rust type, we need to
    introduce our own wrapper type that acts as a lock acquisition for this.
    This brings us to introducing a few new types:
    
    * ModeConfigGuard - the most basic lock guard, as long as this object is
      alive we are guaranteed to be holding drm_device->mode_config.lock. This
      object doesn't do much else on its own currently.
    * ConnectorGuard - an object which corresponds to a specific typed DRM
      connector. This can only be acquired with a ModeConfigGuard, and will be
      used to allow calling methods that are only safe to call with
      drm_device->mode_config.lock held. Since it implements
      Deref<Target=Connector<T>> as well, it can also be used for any other
      operations that would normally be available on a DRM connector.
    
    And finally, we add the DriverConnector::get_modes() trait method which
    drivers can use to implement the drm_connector_helper_funcs.get_modes
    callback. Note that while we make this trait method mandatory, we only do
    so for the time being since VKMS doesn't do very much with DRM connectors -
    and as such we have no need yet to implement alternative connector probing
    schemes outside of get_modes().
    
    Signed-off-by: Lyude Paul's avatarLyude Paul <lyude@redhat.com>
    380657fa
  • Lyude Paul's avatar
    rust: drm/kms: Add ConnectorGuard::add_modes_noedid() · 7ec49144
    Lyude Paul authored
    
    A simple binding for drm_add_modes_noedid() using the ConnectorGuard type
    we just added.
    
    Signed-off-by: Lyude Paul's avatarLyude Paul <lyude@redhat.com>
    7ec49144
  • Lyude Paul's avatar
    rust: drm/kms: Add ConnectorGuard::set_preferred_mode · cf4eeef4
    Lyude Paul authored
    
    Add a wrapper for `drm_set_preferred_mode()` for our new
    `ConnectorGuard` type so we can set the preferred mode for RVKMS
    connectors.
    
    Signed-off-by: Lyude Paul's avatarLyude Paul <lyude@redhat.com>
    cf4eeef4
  • Lyude Paul's avatar
    WIP: rust: drm/kms: Add OpaqueConnector and OpaqueConnectorState · 3f20068c
    Lyude Paul authored
    
    Since we allow drivers to have multiple implementations of DriverConnector
    and DriverConnectorState (in C, the equivalent of this is having multiple
    structs which embed drm_connector) - there are some situations we will run
    into where it's not possible for us to know the corresponding
    DriverConnector or DriverConnectorState for a given connector. The most
    obvious one is iterating through all connectors on a KMS device.
    
    So, take advantage of the various connector traits we added to introduce
    OpaqueConnector<> and OpaqueConnectorState<> which both can be used as a
    DRM connector and connector state respectively without needing to know the
    corresponding traits.
    
    Signed-off-by: Lyude Paul's avatarLyude Paul <lyude@redhat.com>
    
    ---
    
    TODO:
    * Add upcast functions for these types
    3f20068c
  • Lyude Paul's avatar
    WIP: rust: drm/kms: Add OpaqueCrtc and OpaqueCrtcState · 2d60db93
    Lyude Paul authored
    
    This is the same thing as OpaqueConnector and OpaqueConnectorState, but for
    CRTCs now.
    
    Signed-off-by: Lyude Paul's avatarLyude Paul <lyude@redhat.com>
    
    ---
    
    TODO:
    * Add upcast functions
    2d60db93
  • Lyude Paul's avatar
    WIP: rust: drm/kms: Add OpaquePlane and OpaquePlaneState · 936228d8
    Lyude Paul authored
    
    Same thing as OpaqueCrtc and OpaqueCrtcState, but for plane states now.
    
    Signed-off-by: Lyude Paul's avatarLyude Paul <lyude@redhat.com>
    
    ---
    
    TODO:
    * Finish adding upcast functions.
    936228d8
  • Lyude Paul's avatar
    rust: drm/kms: Add RawConnector and RawConnectorState · fab5ee81
    Lyude Paul authored
    
    Now that we have more then one way to refer to connectors, we also want to
    ensure that any methods which are common to any kind of connector type can
    be used on all connector representations. This is where RawConnector and
    RawConnectorState come in: we implement these traits for any type which
    implements AsRawConnector or AsRawConnectorState respectively.
    
    Signed-off-by: Lyude Paul's avatarLyude Paul <lyude@redhat.com>
    fab5ee81
  • Lyude Paul's avatar
    rust: drm/kms: Add RawCrtc and RawCrtcState · 1a422fff
    Lyude Paul authored
    
    Same thing as RawConnector and RawConnectorState, just for CRTCs now.
    
    Signed-off-by: Lyude Paul's avatarLyude Paul <lyude@redhat.com>
    1a422fff
  • Lyude Paul's avatar
    rust: drm/kms: Add RawPlane and RawPlaneState · 6c64611f
    Lyude Paul authored
    
    Same thing as RawCrtc and RawCrtcState, but for DRM planes now
    
    Signed-off-by: Lyude Paul's avatarLyude Paul <lyude@redhat.com>
    6c64611f
  • Lyude Paul's avatar
    WIP: rust: drm/kms: Add OpaqueEncoder · 0ce1fccf
    Lyude Paul authored
    
    Same thing as OpaquePlane, but for encoders now.
    
    Signed-off-by: Lyude Paul's avatarLyude Paul <lyude@redhat.com>
    
    ---
    
    TODO:
    * Add upcast functions for this
    0ce1fccf
  • Lyude Paul's avatar
    WIP: rust: drm/kms: Add drm_atomic_state bindings · ea59337a
    Lyude Paul authored
    
    Next up is introducing bindings that we can use to represent the global DRM
    atomic state, along with all of the various object states contained within.
    We do this by introducing a few new concepts: borrowed states, atomic state
    mutators, and atomic state composers.
    
    To understand these, we need to quickly touch upon the general life of an
    atomic commit. Assuming a driver does its own internal atomic commit, the
    procedure looks something like this:
    
    * Allocate a new atomic state
    * Duplicate the atomic state of each mode object we want to mutate, and add
      the duplicated state to the new atomic state
    * Check (possibly more then once) the atomic state, possibly modifying it
      along the way
    * Commit the atomic state to software (we'll call this commit time). At
      this point no new objects can be added to the state
    * Finish committing the atomic state to hardware asynchronously
    
    With this in mind, we introduce AtomicStateMutator and AtomicStateComposer
    (along with leaky variants intended for uses in FFI calls). An
    AtomicStateMutator allows mutating an atomic state but does not allow for
    adding new objects to the state. Subsequently, an AtomicStateComposer
    allows for both mutating an atomic state and adding new mode objects. We
    control when we expose each of these types in order to implement the
    limitations required by the aforementioned example.
    
    Note as well that AtomicStateComposer is intended to eventually be usable
    directly by drivers. In this scenario, a driver will be able to create an
    AtomicStateComposer (the equivalent of allocating an atomic state in C) and
    then commit it by passing it to our DRM bindings by-value, insuring that
    once the commit process begins it is impossible to keep using the
    AtomicStateComposer.
    
    The next part of this is allowing users to modify the atomic states of all
    of the objects contained within an atomic state. Since it's an extremely
    common usecase for objects to mutate the atomic state of multiple objects
    at once in an unpredictable order, we need a mechanism that will allow us
    to hand out &mut references to each state while ensuring at runtime that we
    do not break rust's data aliasing rules (which disallow us from ever having
    more then one &mut reference to the same piece of data).
    
    We do this by introducing the concept of a "borrowed" state. This is a very
    similar concept to RefCell, where it is ensured during runtime that when a
    &mut reference is taken out another one cannot be created until the
    corresponding Ref object has been dropped. Our equivalent Ref types are
    BorrowedConnectorState, BorrowedCrtcState, and BorrowedPlaneState.
    
    Each one of these types can be used in the same manner as a Ref - no
    additional borrows for an atomic state may be taken until the existing one
    has been dropped. Subsequently, all of these types implement their
    respective AsRaw* and FromRaw* counter-parts - and allow dereferencing to
    each driver-private data structure for fully qualified borrows (like
    BorrowedCrtcState<'a, CrtcState<T>>. This allows a pretty clean way of
    mutating multiple states at once without ever breaking rust's mutability
    rules.
    
    We'll use all of these types over the next few commits to begin introducing
    various atomic modeset callbacks to each mode object type.
    
    Signed-off-by: Lyude Paul's avatarLyude Paul <lyude@redhat.com>
    
    ---
    
    TODO:
    * Finish adding state iterators
      We only have one iterator for planes right now, but the plan is to have
      iterators for all types and have two different kind of iterators:
      * State object iterators
        Basically, these just iterate through all of the mode objects of a
        specific type present in an atomic state. Currently this is what our
        plane iterator does.
      * State mutator iterators
        With the existence of AtomicStateMutator and friends, it makes sense to
        have a type of iterator that:
        * Only iterates through unborrowed atomic states, removing the need to
          deal with the Option<> that get_new_*_state() functions return
        * Returns each (object, old_state, new_state) triplet as a dedicated
          type (PlaneUpdate, CrtcUpdate, ConnectorUpdate) that can be upcasted
          from an Opaque type using a single call. This is desirable, as it
          would make iterating through objects with a specific Driver*
          implementation as easy as just adding a .filter_map() call to the
          iterator.
      * Upcast functions for the Borrowed* types
    ea59337a
  • Lyude Paul's avatar
    rust: drm/kms: Introduce DriverCrtc::atomic_check() · 15610ea3
    Lyude Paul authored
    
    An optional trait method for implementing a CRTC's atomic state check.
    
    Signed-off-by: Lyude Paul's avatarLyude Paul <lyude@redhat.com>
    15610ea3
  • Lyude Paul's avatar
    rust: drm/kms: Add DriverPlane::atomic_update() · a2174ae6
    Lyude Paul authored
    
    A mandatory trait method used for implementing DRM's atomic plane update
    callback.
    
    Signed-off-by: Lyude Paul's avatarLyude Paul <lyude@redhat.com>
    a2174ae6
  • Lyude Paul's avatar
    rust: drm/kms: Add DriverPlane::atomic_check() · e5f4d3fb
    Lyude Paul authored
    
    Optional trait method for implementing a plane's atomic_check().
    
    Signed-off-by: Lyude Paul's avatarLyude Paul <lyude@redhat.com>
    e5f4d3fb
  • Lyude Paul's avatar
    rust: drm/kms: Add RawCrtcState::active() · 258500d9
    Lyude Paul authored
    
    A binding for checking drm_crtc_state.active.
    
    Signed-off-by: Lyude Paul's avatarLyude Paul <lyude@redhat.com>
    258500d9
  • Lyude Paul's avatar
    rust: drm/kms: Add RawPlaneState::crtc() · ad61f71e
    Lyude Paul authored
    
    Add a binding for checking drm_plane_state.crtc. Note that we don't have a
    way of knowing what DriverCrtc implementation would be used here (and want
    to make this function also available on OpaquePlaneState types), so we
    return an OpaqueCrtc.
    
    Signed-off-by: Lyude Paul's avatarLyude Paul <lyude@redhat.com>
    ad61f71e
  • Lyude Paul's avatar
    WIP: rust: drm/kms: Add RawPlaneState::atomic_helper_check() · a6d9ce69
    Lyude Paul authored
    
    Add a binding for drm_atomic_helper_check_plane_state(). Since we want to
    make sure that the user is passing in the new state for a Crtc instead of
    an old state, we explicitly ask for a reference to a BorrowedCrtcState.
    
    Signed-off-by: Lyude Paul's avatarLyude Paul <lyude@redhat.com>
    
    ---
    
    TODO:
    * Add support for scaling options
    a6d9ce69
  • Lyude Paul's avatar
    rust: drm/kms: Add drm_framebuffer bindings · 93cb2ac8
    Lyude Paul authored
    
    This adds some very simple bindings for drm_framebuffer. We don't use them
    much yet, but we'll eventually be using them when rvkms eventually gets CRC
    and writeback support. Just like Connector objects, these use RcModeObject.
    
    Signed-off-by: Lyude Paul's avatarLyude Paul <lyude@redhat.com>
    93cb2ac8
  • Lyude Paul's avatar
    rust: drm/kms: Add RawPlane::framebuffer() · 5c086d84
    Lyude Paul authored
    
    Returns the Framebuffer currently assigned in an atomic plane state.
    
    Signed-off-by: Lyude Paul's avatarLyude Paul <lyude@redhat.com>
    5c086d84
  • Lyude Paul's avatar
    rust: drm/kms: Add DriverCrtc::atomic_begin() and atomic_flush() · 43e348c4
    Lyude Paul authored
    
    Optional trait methods for implementing the atomic_begin and atomic_flush
    callbacks for a CRTC.
    
    Signed-off-by: Lyude Paul's avatarLyude Paul <lyude@redhat.com>
    43e348c4
  • Lyude Paul's avatar
    rust: drm/kms: Add DriverCrtc::atomic_enable() and atomic_disable() · 0643ddba
    Lyude Paul authored
    
    Optional trait methods for implementing the atomic_enable and
    atomic_disable callbacks of a CRTC.
    
    Signed-off-by: Lyude Paul's avatarLyude Paul <lyude@redhat.com>
    0643ddba
  • Lyude Paul's avatar
    rust: drm: Add Device::event_lock() · f4020258
    Lyude Paul authored
    
    This is just a crate-private helper to use Lock::from_raw() to provide an
    immutable reference to the DRM event_lock, so that it can be used like a
    normal rust spinlock. We'll need this for adding vblank related bindings.
    
    Signed-off-by: Lyude Paul's avatarLyude Paul <lyude@redhat.com>
    f4020258
  • Lyude Paul's avatar
    rust: drm/kms: Add Device::num_crtcs() · 782741a9
    Lyude Paul authored
    
    A binding for checking drm_device.num_crtcs. We'll need this in a moment
    for vblank support, since setting it up requires knowing the number of
    CRTCs that a driver has initialized.
    
    Signed-off-by: Lyude Paul's avatarLyude Paul <lyude@redhat.com>
    782741a9
  • Lyude Paul's avatar
    WIP: rust: drm/kms: Add VblankSupport · 3e275b69
    Lyude Paul authored
    
    This commit adds bindings for implementing vblank support for a driver's
    CRTCs. These bindings are optional, to account for the fact that not all
    drivers have dedicated hardware vblanks.
    
    In order to accomplish this, we introduce the VblankSupport trait which can
    be implemented on DriverCrtc by drivers which support vblanks. This works
    in the same way as the main Kms trait - drivers which don't support
    hardware vblanks can simply pass PhantomData<Self> to the associated type
    on DriverCrtc. If a driver chooses to implement VblankSupport, VblankImpl
    will be implemented by DRM automatically - and can be passed to the
    VblankImpl associated type on DriverCrtc.
    
    Additionally, we gate methods which only apply to vblank-supporting drivers
    by introducing a VblankDriverCrtc trait that is automatically implemented
    by DRM for CRTC drivers implementing VblankSupport. This works basically in
    the same way as Kms and KmsDriver, but for CRTCs.
    
    Signed-off-by: Lyude Paul's avatarLyude Paul <lyude@redhat.com>
    
    ---
    
    Notes:
    
    * One thing to keep in mind: this trait is implemented on the CRTC as
      opposed to the KMS driver due to the possibility that a driver may have
      multiple different types of CRTCs. As a result, it's not impossible that
      there could potentially be differences in each type's vblank hardware
      implementation. In theory this could lead to a driver mistakenly only
      implementing VblankSupport for some CRTCs and not others, which isn't
      really defined behavior in DRM. As such, one of the dependencies in the
      branch for this patch series preview is a fix to ensure that DRM
      disallows registering drivers that make this mistake.
    
    TODO:
    
    * Technically this patch introduces a soundness issue. We currently allow
      access to a kernel::drm::Device (through UnregisteredKmsDevice's Deref
      implementation) within the kernel::drm::kms::Kms::create_objects trait
      method. A caller could potentially access an uninitialized mutex by
      calling Crtc::vblank_lock() within this context.
      My solution for this is likely going to be adding unregistered variants
      of Crtc and other mode-objects that don't have access to the full set of
      methods on mode objects.
    3e275b69
  • Lyude Paul's avatar
    WIP: rust: drm/kms: Add Kms::atomic_commit_tail · 49a66dd8
    Lyude Paul authored
    
    A quick note: this is one of my favorite bindings so far :). It sounds way
    overly complicated, but so far actually writing implementations of this in
    rust has been a breeze.
    
    Anyway: RVKMS has a slightly different atomic_commit_tail than normal,
    which means we need to write up some bindings for atomic_commit_tail. This
    is a lot more interesting then it might seem on the surface as implementing
    atomic_commit_tail incorrectly could result in UB. And in general, DRM has
    up until now relied entirely on the programmer to do this correctly through
    implicit ordering requirements.
    
    In the universe of rust though, we want no UB at all! To ensure this, we
    need to make sure that all atomic commit callbacks follow all of these
    requirements:
    
    * Disable/enable modeset commits must happen exactly once
    * A disable modeset must be committed for a resource before an enable
      modeset may be committed for a resource
    * Plane updates must happen exactly once
    * drm_atomic_commit_hw_done() must be called exactly once, and only after
      all commits have been completed.
    * The state may not be mutated after drm_atomic_commit_hw_done() is called
    * Access to the prior atomic states are revoked after
      drm_atomic_commit_hw_done() is called (and our "new" states become "old"
      states)
    
    To handle this, we introduce a number of new objects and types:
    tokens:
    
    * AtomicCommitTail
      Main object for controlling the commit_tail process
      * ModesetsReadyToken
        A single use token indicating that no modesets have been committed with
        the AtomicCommitTail yet
      * commit_modeset_disables() -> DisablesCommittedToken
        This function consumes the ModesetsReadyToken, commits modeset
        disables, and then returns a DisablesCommittedToken
      * commit_modeset_enables() -> EnablesCommittedToken
        This function consumes a DisablesCommittedToken, commits modeset
        enables, and then returns a EnablesCommittedToken
        EnablesCommittedToken - enforcing the disables -> enables order.
      * commit_planes() -> PlaneUpdatesCommittedToken
        Consumes a PlaneUpdatesReadyToken and returns a
        PlaneUpdatesCommittedToken.
      * commit_hw_done() -> CommittedAtomicState
        Revokes access to the AtomicCommitTailObject, and consumes both the
        EnablesCommittedToken and PlaneUpdatesCommitted tokens. This ensures
        that all modesets and plane updates have occurred exactly once.
    * CommittedAtomicState - main object for controlling the atomic_commit_tail
      after the state has been swapped in. This must be returned from the
      atomic_commit_tail function to prove that all of the required commits
      have occurred.
    
    Signed-off-by: Lyude Paul's avatarLyude Paul <lyude@redhat.com>
    
    ---
    
    TODO:
    
    * Currently this solution wouldn't be sufficient for drivers that need
      precise control over the order of each individual modeset or plane
      update. However, this should be very easy to add.
    * Figure out something better for enforcing the plane cleanup then what we
      have right now (e.g. cleaning up planes in the destructor for
      CommittedAtomicState).
    * Add iterator functions that take mutable references to the atomic state
      objects here. This will prevent functions like commit_modeset_disables()
      from being called while a state borrow is taken out, while still allowing
      easy access to the contents of the atomic state at any portion of the
      atomic commit tail.
    * Actually add some macros for generating bitmasks like we do with
      PlaneCommitFlags - right now we just do this by hand.
    49a66dd8
  • Lyude Paul's avatar
    WIP: drm: Introduce RVKMS! · c470fec9
    Lyude Paul authored
    
    Now that we've added all of the bits that we need for the KMS API, it's
    time to introduce rvkms! This is a port of the VKMS driver to rust, with
    the intent of acting as an example usecase of the KMS bindings that we've
    come up with so far in preparation for writing a display driver for nova.
    
    Currently RVKMS is an extremely bear bones driver - it only registers a
    device and emulates vblanking, but it exercises a good portion of the API
    that we've introduced so far! Eventually I hope to introduce CRC generation
    and maybe writeback connectors like.
    
    Signed-off-by: Lyude Paul's avatarLyude Paul <lyude@redhat.com>
    c470fec9
Showing
with 830 additions and 3 deletions
......@@ -323,6 +323,8 @@ source "drivers/gpu/drm/amd/amdgpu/Kconfig"
source "drivers/gpu/drm/nouveau/Kconfig"
source "drivers/gpu/drm/rvkms/Kconfig"
source "drivers/gpu/drm/i915/Kconfig"
source "drivers/gpu/drm/xe/Kconfig"
......
......@@ -172,6 +172,7 @@ obj-$(CONFIG_DRM_VMWGFX)+= vmwgfx/
obj-$(CONFIG_DRM_VGEM) += vgem/
obj-$(CONFIG_DRM_VKMS) += vkms/
obj-$(CONFIG_DRM_NOUVEAU) +=nouveau/
obj-$(CONFIG_DRM_RVKMS) += rvkms/
obj-$(CONFIG_DRM_EXYNOS) +=exynos/
obj-$(CONFIG_DRM_ROCKCHIP) +=rockchip/
obj-$(CONFIG_DRM_GMA500) += gma500/
......
......@@ -509,7 +509,7 @@ int drm_gem_shmem_dumb_create(struct drm_file *file, struct drm_device *dev,
}
EXPORT_SYMBOL_GPL(drm_gem_shmem_dumb_create);
static vm_fault_t drm_gem_shmem_fault(struct vm_fault *vmf)
vm_fault_t drm_gem_shmem_fault(struct vm_fault *vmf)
{
struct vm_area_struct *vma = vmf->vma;
struct drm_gem_object *obj = vma->vm_private_data;
......@@ -538,8 +538,9 @@ static vm_fault_t drm_gem_shmem_fault(struct vm_fault *vmf)
return ret;
}
EXPORT_SYMBOL_GPL(drm_gem_shmem_fault);
static void drm_gem_shmem_vm_open(struct vm_area_struct *vma)
void drm_gem_shmem_vm_open(struct vm_area_struct *vma)
{
struct drm_gem_object *obj = vma->vm_private_data;
struct drm_gem_shmem_object *shmem = to_drm_gem_shmem_obj(obj);
......@@ -560,8 +561,9 @@ static void drm_gem_shmem_vm_open(struct vm_area_struct *vma)
drm_gem_vm_open(vma);
}
EXPORT_SYMBOL_GPL(drm_gem_shmem_vm_open);
static void drm_gem_shmem_vm_close(struct vm_area_struct *vma)
void drm_gem_shmem_vm_close(struct vm_area_struct *vma)
{
struct drm_gem_object *obj = vma->vm_private_data;
struct drm_gem_shmem_object *shmem = to_drm_gem_shmem_obj(obj);
......@@ -572,6 +574,7 @@ static void drm_gem_shmem_vm_close(struct vm_area_struct *vma)
drm_gem_vm_close(vma);
}
EXPORT_SYMBOL_GPL(drm_gem_shmem_vm_close);
const struct vm_operations_struct drm_gem_shmem_vm_ops = {
.fault = drm_gem_shmem_fault,
......
config DRM_RVKMS
tristate "Rust VKMS PoC driver (EXPERIMENTAL)"
depends on RUST && DRM && DRM_GEM_SHMEM_HELPER=y
obj-$(CONFIG_DRM_RVKMS) += rvkms.o
// SPDX-License-Identifier: GPL-2.0
use super::{Rvkms, RvkmsDevice, MAX_RES, DEFAULT_RES};
use kernel::{
prelude::*,
drm::{
device::Device,
kms::{
connector::{self, ConnectorGuard, DriverConnectorOps},
ModeConfigGuard
}
},
prelude::*
};
use core::marker::PhantomPinned;
#[pin_data]
pub(crate) struct DriverConnector {
#[pin]
_p: PhantomPinned
}
pub(crate) type Connector = connector::Connector<DriverConnector>;
#[vtable]
impl connector::DriverConnector for DriverConnector {
#[unique]
const OPS: &'static DriverConnectorOps;
type State = ConnectorState;
type Driver = Rvkms;
type Args = ();
fn new(dev: &Device<Self::Driver>, args: Self::Args) -> impl PinInit<Self, Error> {
try_pin_init!(Self { _p: PhantomPinned })
}
fn get_modes(
connector: ConnectorGuard<'_, Self>,
_guard: &ModeConfigGuard<'_, Self::Driver>
) -> i32 {
let count = connector.add_modes_noedid(MAX_RES);
connector.set_preferred_mode(DEFAULT_RES);
count
}
}
#[derive(Clone, Default)]
pub(crate) struct ConnectorState;
impl connector::DriverConnectorState for ConnectorState {
type Connector = DriverConnector;
}
// TODO: License and stuff
// Contain's rvkms's drm_crtc implementation
use core::marker::*;
use super::{Rvkms, plane::*};
use kernel::{
prelude::*,
drm::{
device::Device,
kms::{
atomic::*,
crtc::{self, RawCrtcState, DriverCrtcOps},
ModeObject,
KmsRef,
vblank::*,
}
},
sync::{
lock::Guard,
SpinLockIrq,
LockedBy,
},
hrtimer::*,
time::*,
irq::*,
sync::Arc,
new_spinlock_irq,
impl_has_timer
};
pub(crate) type Crtc = crtc::Crtc<RvkmsCrtc>;
pub(crate) type CrtcState = crtc::CrtcState<RvkmsCrtcState>;
#[derive(Default)]
pub(crate) struct VblankState {
/// A reference to the current VblankTimer
timer: Option<Arc<VblankTimer>>,
/// A reference to a handle for the current VblankTimer
handle: Option<ArcTimerHandle<VblankTimer>>,
/// The current frame duration in ns
///
/// Stored separately here so it can be read safely without the vblank lock
period_ns: i32,
}
#[pin_data]
pub(crate) struct RvkmsCrtc {
/// The current vblank emulation state
///
/// This is uninitalized when the CRTC is disabled to prevent circular references
#[pin]
vblank_state: SpinLockIrq<VblankState>
}
#[vtable]
impl crtc::DriverCrtc for RvkmsCrtc {
#[unique]
const OPS: &'static DriverCrtcOps;
type Args = ();
type State = RvkmsCrtcState;
type Driver = Rvkms;
type VblankImpl = Self;
fn new(device: &Device<Self::Driver>, args: &Self::Args) -> impl PinInit<Self, Error> {
try_pin_init!(Self {
vblank_state <- new_spinlock_irq!(VblankState::default(), "vblank_handle_lock")
})
}
fn atomic_check(
crtc: &Crtc,
old_state: &CrtcState,
mut new_state: crtc::BorrowedCrtcState<'_, CrtcState>,
state: &AtomicStateComposer<Self::Driver>
) -> Result {
state.add_affected_planes(crtc)?;
// Create a vblank timer when enabling a CRTC, and destroy said timer when disabling to
// resolve the circular reference to CRTC it creates
if old_state.active() != new_state.active() {
new_state.vblank_timer = if new_state.active() {
Some(VblankTimer::new(crtc)?)
} else {
None
};
}
Ok(())
}
fn atomic_flush(
crtc: &Crtc,
_old_state: &CrtcState,
mut new_state: crtc::BorrowedCrtcState<'_, CrtcState>,
_state: &AtomicStateMutator<Self::Driver>
) {
if let Some(event) = new_state.get_pending_vblank_event() {
if let Ok(vbl_ref) = crtc.vblank_get() {
event.arm(vbl_ref);
} else {
event.send();
}
}
}
fn atomic_enable(
crtc: &Crtc,
old_state: &CrtcState,
new_state: crtc::BorrowedCrtcState<'_, CrtcState>,
_state: &AtomicStateMutator<Self::Driver>
) {
with_irqs_disabled(|irq| {
// Store a reference to the newly created vblank timer for this CRTC
crtc.vblank_state.lock_with(irq).timer = new_state.vblank_timer.clone()
});
crtc.vblank_on();
}
fn atomic_disable(
crtc: &Crtc,
_old_state: &CrtcState,
_new_state: crtc::BorrowedCrtcState<'_, CrtcState>,
_state: &AtomicStateMutator<Self::Driver>
) {
crtc.vblank_off();
// Since we just explicitly disabled vblanks, destroy the vblank state to resolve circular
// reference to this CRTC that it holds. Note that dropping the handle will cause us to wait
// for the timer to finish, so we return it from with_irqs_disabled so that it is only
// dropped once the vblank_state lock has been released
drop(with_irqs_disabled(|irq| {
let mut state = crtc.vblank_state.lock_with(irq);
(state.timer.take(), state.handle.take())
}));
}
}
impl VblankSupport for RvkmsCrtc {
type Crtc = Self;
fn enable_vblank(
crtc: &Crtc,
vblank: &VblankGuard<'_, Self::Crtc>,
irq: IrqDisabled<'_>,
) -> Result {
let period_ns = vblank.frame_duration();
let mut vbl_state = crtc.vblank_state.lock_with(irq);
if let Some(timer) = vbl_state.timer.clone() {
vbl_state.period_ns = period_ns;
vbl_state.handle = Some(timer.schedule(period_ns as _));
}
Ok(())
}
fn disable_vblank(crtc: &Crtc, _vbl_guard: &VblankGuard<'_, Self::Crtc>, irq: IrqDisabled<'_>) {
let handle = crtc.vblank_state.lock_with(irq).handle.take();
// Now that we're outside of the vblank lock, we can safely drop the handle
drop(handle);
}
fn get_vblank_timestamp(crtc: &Crtc, _handling_vblank_irq: bool) -> Option<VblankTimestamp> {
let time = with_irqs_disabled(|irq| {
let vbl_state = crtc.vblank_state.lock_with(irq);
// Return the expiration of our vblank timer if we have one (if not, vblanks are
// disabled)
vbl_state.timer.as_ref().map(|t| {
// To prevent races, we roll the hrtimer forward before we do any interrupt
// processing - this is how real hw works (the interrupt is only generated after all
// the vblank registers are updated) and what the vblank core expects. Therefore we
// need to always correct the timestamps by one frame.
t.timer.expires() - Ktime::from_ns(vbl_state.period_ns)
})
});
Some(VblankTimestamp {
// …otherwise, just use the current time
time: time.unwrap_or_else(|| Ktime::ktime_get()),
max_error: 0
})
}
}
#[derive(Clone, Default)]
pub(crate) struct RvkmsCrtcState {
vblank_timer: Option<Arc<VblankTimer>>
}
impl crtc::DriverCrtcState for RvkmsCrtcState {
type Crtc = RvkmsCrtc;
}
/// The main hrtimer structure for emulating vblanks.
#[pin_data]
pub(crate) struct VblankTimer {
/// The actual hrtimer used for sending out vblanks
#[pin]
timer: Timer<Self>,
/// An owned reference to the CRTC that this [`VblankTimer`] belongs to
crtc: KmsRef<Crtc>,
}
impl_has_timer! {
impl HasTimer<Self> for VblankTimer { self.timer }
}
impl VblankTimer {
pub(crate) fn new(crtc: &Crtc) -> Result<Arc<Self>> {
Arc::pin_init(
pin_init!(Self {
timer <- Timer::<Self>::new::<Arc<Self>>(),
crtc: crtc.into(),
}),
GFP_KERNEL
)
}
}
impl TimerCallback for VblankTimer {
type CallbackTarget<'a> = Arc<Self>;
fn run(
this: Self::CallbackTarget<'_>,
context: TimerCallbackContext<'_, Self>
) -> TimerRestart
where
Self: Sized
{
with_irqs_disabled(|irq| {
let period_ns = this.crtc.vblank_state.lock_with(irq).period_ns;
let overrun = context.forward_now(Ktime::from_ns(period_ns));
if overrun != 1 {
dev_warn!(
this.crtc.drm_dev().as_ref(),
"vblank timer overrun (expected 1, got {overrun})\n"
);
}
this.crtc.handle_vblank();
});
TimerRestart::Restart
}
}
// SPDX-License-Identifier: GPL-2.0
use core::marker::PhantomPinned;
use kernel::{
drm::{device::Device, kms::encoder},
prelude::*,
types::ARef,
};
use crate::{Rvkms, connector::Connector};
#[pin_data]
pub(crate) struct DriverEncoder {
connector: ARef<Connector>,
#[pin]
_p: PhantomPinned,
}
pub(crate) type Encoder = encoder::Encoder<DriverEncoder>;
#[vtable]
impl encoder::DriverEncoder for DriverEncoder {
#[unique]
const OPS: &'static encoder::DriverEncoderOps;
type Driver = Rvkms;
type Args = ARef<Connector>;
fn new(device: &Device<Self::Driver>, args: Self::Args) -> impl PinInit<Self, Error> {
try_pin_init!(Self {
connector: args,
_p: PhantomPinned
})
}
}
use super::Rvkms;
use kernel::{
drm::{
self,
device::Device as DrmDevice
},
prelude::*,
};
use core::option::*;
pub(crate) struct File;
impl drm::file::DriverFile for File {
type Driver = Rvkms;
fn open(device: &DrmDevice<Self::Driver>) -> Result<Pin<Box<Self>>> {
pr_info!("Someone opened a file! But I do not yet know which one...\n");
Box::pin_init(init!(File { }), GFP_KERNEL)
}
}
use crate::{Rvkms, RvkmsDevice};
use core::sync::atomic::{AtomicU64, Ordering};
use kernel::{
drm::{self, gem},
prelude::*,
};
static GEM_ID: AtomicU64 = AtomicU64::new(0);
/// GEM Object implementation
#[pin_data]
pub(crate) struct DriverObject {
/// ID for debugging
id: u64,
}
pub(crate) type Object = gem::shmem::Object<DriverObject>;
impl gem::BaseDriverObject<Object> for DriverObject {
fn new(dev: &RvkmsDevice, size: usize) -> impl PinInit<Self, Error> {
let id = GEM_ID.fetch_add(1, Ordering::Relaxed);
pr_debug!("DriverObject::new id={id}\n");
DriverObject { id }
}
}
impl gem::shmem::DriverObject for DriverObject {
type Driver = Rvkms;
}
// SPDX-License-Identifier: GPL-2.0
use crate::{
crtc::Crtc,
plane::Plane,
connector::Connector,
encoder::Encoder,
RvkmsDevice,
Rvkms
};
use kernel::{
drm::{
fourcc::*,
kms::{
connector::DRM_MODE_CONNECTOR_VIRTUAL,
encoder::DRM_MODE_ENCODER_VIRTUAL,
plane::{self, PlaneType},
framebuffer::*,
UnregisteredKmsDevice,
},
},
sync::Arc,
prelude::*,
types::ARef,
};
const FORMATS: FormatList<1> = FormatList::new([XRGB888]);
pub(crate) fn create_output(dev: &UnregisteredKmsDevice<'_, Rvkms>, index: u8) -> Result {
let possible_crtcs = 1 << index;
let primary = Plane::new(
dev,
possible_crtcs,
&FORMATS,
Option::<&ModifierList<0>>::None,
PlaneType::PRIMARY,
None,
()
)?;
let crtc = Crtc::new(dev, primary, Option::<&Plane>::None, None, ())?;
let connector = Connector::new(dev, DRM_MODE_CONNECTOR_VIRTUAL, ())?;
let encoder = Encoder::new(
dev,
DRM_MODE_ENCODER_VIRTUAL,
possible_crtcs,
0,
None,
connector.clone()
)?;
connector.attach_encoder(encoder)
}
// SPDX-License-Identifier: GPL-2.0
use core::marker::PhantomPinned;
use super::{Rvkms, crtc::{RvkmsCrtc, Crtc}};
use kernel::{
prelude::*,
drm::{
device::Device,
kms::{
atomic::*,
plane::{
self,
AsRawPlaneState,
FromRawPlaneState,
DriverPlaneState,
RawPlane,
RawPlaneState,
BorrowedPlaneState,
DriverPlaneOps,
},
ModeObject
}
},
};
#[pin_data]
pub(crate) struct RvkmsPlane {
#[pin]
_p: PhantomPinned,
}
pub(crate) type Plane = plane::Plane<RvkmsPlane>;
pub(crate) type PlaneState = plane::PlaneState<RvkmsPlaneState>;
#[vtable]
impl plane::DriverPlane for RvkmsPlane {
#[unique]
const OPS: &'static DriverPlaneOps;
type State = RvkmsPlaneState;
type Driver = Rvkms;
type Args = ();
fn new(device: &Device<Self::Driver>, args: Self::Args) -> impl PinInit<Self, Error> {
try_pin_init!(Self { _p: PhantomPinned })
}
fn atomic_check(
plane: &Plane,
mut new_state: BorrowedPlaneState<'_, PlaneState>,
_old_state: &PlaneState,
state: &AtomicStateComposer<Self::Driver>
) -> Result {
if new_state.framebuffer().is_none() {
return Ok(());
}
if let Some(crtc) = new_state.crtc() {
let crtc_state = state.add_crtc_state(crtc)?;
new_state.atomic_helper_check(&crtc_state, true, true)
} else {
// TODO: We should be printing a warning here if we have no CRTC but do have an fb
return Ok(());
}
}
fn atomic_update(
_plane: &Plane,
_new_state: BorrowedPlaneState<'_, PlaneState>,
_old_state: &PlaneState,
_state: &AtomicStateMutator<Self::Driver>,
) {
// TODO, no-op for now
}
}
#[derive(Clone, Default)]
pub(crate) struct RvkmsPlaneState;
impl DriverPlaneState for RvkmsPlaneState {
type Plane = RvkmsPlane;
}
// SPDX-License-Identifier: GPL-2.0
mod connector;
mod crtc;
mod file;
mod gem;
mod plane;
mod output;
mod encoder;
use alloc::boxed::Box;
use core::{option::*, marker::*};
use kernel::{
c_str,
str::CStr,
device,
driver,
drm::{
self,
drv,
kms::{
Kms,
ModeConfigInfo,
UnregisteredKmsDevice,
atomic::*,
fbdev::*,
},
},
platform,
prelude::*,
sync::Arc,
};
/// Convienence type alias for the DRM device type for this driver
pub(crate) type RvkmsDevice = drm::device::Device<Rvkms>;
/// The name of the driver
const NAME: &'static CStr = c_str!("rvkms");
/// Driver metadata
const INFO: drv::DriverInfo = drv::DriverInfo {
major: 0,
minor: 0,
patchlevel: 0,
name: &NAME,
desc: c_str!("Rust VKMS PoC"),
date: c_str!("20240115"),
};
/// The minimum supported resolution
const MIN_RES: (i32, i32) = (10, 10);
/// The maximum supported resolution
const MAX_RES: (i32, i32) = (8192, 8192);
/// The "preferred" resolution
const DEFAULT_RES: (i32, i32) = (1024, 768);
pub(crate) struct Data {
}
/// DRM Driver implementation for `RvkmsDriver`
#[vtable]
impl drv::Driver for Rvkms {
type Data = Arc<Data>;
type Object = gem::Object;
type File = file::File;
type Kms = Self;
const INFO: drv::DriverInfo = INFO;
const FEATURES:u32 = drv::FEAT_GEM | drv::FEAT_MODESET | drv::FEAT_ATOMIC;
kernel::declare_drm_ioctls! {}
}
#[vtable]
impl Kms for Rvkms {
type Driver = Self;
type Fbdev = FbdevShmem<Self>;
fn mode_config_info(
_dev: &device::Device,
_drm_data: <<Self::Driver as drv::Driver>::Data as kernel::types::ForeignOwnable>::Borrowed<'_>,
) -> Result<ModeConfigInfo> {
Ok(MODE_CONFIG_INFO)
}
fn create_objects(drm: &UnregisteredKmsDevice<'_, Self::Driver>) -> Result {
output::create_output(drm, 0)
}
fn atomic_commit_tail<'a>(
mut state: AtomicCommitTail<'a, Self::Driver>,
modeset_token: ModesetsReadyToken<'_>,
plane_update_token: PlaneUpdatesReadyToken<'_>,
) -> CommittedAtomicState<'a, Self::Driver> {
let modeset_token = state.commit_modeset_disables(modeset_token);
let plane_update_token = state.commit_planes(plane_update_token, Default::default());
let modeset_token = state.commit_modeset_enables(modeset_token);
state.fake_vblank();
let state = state.commit_hw_done(modeset_token, plane_update_token);
state.wait_for_flip_done();
state
}
}
impl platform::Driver for Rvkms {
type Data = Arc<Data>;
type IdInfo = ();
fn probe(pdev: &mut platform::Device, id_info: Option<&Self::IdInfo>) -> Result<Self::Data> {
// XXX: do not fret, the mutable reference here is temporary (poke dakr if it isn't)
let dev: &device::Device = pdev.as_ref();
dev.pr_info(format_args!("RVKMS probing\n"));
let data = Arc::new(Data { }, GFP_KERNEL)?;
let drm = drv::Registration::<Rvkms>::new_foreign_owned(dev, data.clone(), 0)?;
Ok(data)
}
}
pub(crate) struct Rvkms {
drv_reg: Pin<Box<platform::Registration<Self>>>,
pdev: platform::Device,
}
const MODE_CONFIG_INFO: ModeConfigInfo = ModeConfigInfo {
min_resolution: MIN_RES,
max_resolution: MAX_RES,
max_cursor: (512, 512),
preferred_depth: 0,
};
impl kernel::Module for Rvkms {
fn init(name: &'static CStr, module: &'static ThisModule) -> kernel::error::Result<Self> {
pr_info!("RVKMS module loaded\n");
// Register the driver (FIXME: this should be static
let drv_reg = Box::try_pin_init(
platform::Registration::<Self>::new(name, module),
GFP_KERNEL
)?;
let pdev = platform::Device::create_simple(&NAME, 0)?;
let dev: &device::Device = pdev.as_ref();
Ok(Self {
drv_reg,
pdev,
})
}
}
module! {
type: Rvkms,
name: "rvkms",
author: "Lyude Paul",
description: "Rust VKMS Proof of Concept driver",
license: "GPL v2",
}
......@@ -129,6 +129,9 @@ void drm_gem_shmem_print_info(const struct drm_gem_shmem_object *shmem,
struct drm_printer *p, unsigned int indent);
extern const struct vm_operations_struct drm_gem_shmem_vm_ops;
vm_fault_t drm_gem_shmem_fault(struct vm_fault *vmf);
void drm_gem_shmem_vm_open(struct vm_area_struct *vma);
void drm_gem_shmem_vm_close(struct vm_area_struct *vma);
/*
* GEM object functions
......
......@@ -6,19 +6,33 @@
* Sorted alphabetically.
*/
#include <drm/drm_atomic.h>
#include <drm/drm_atomic_helper.h>
#include <drm/drm_device.h>
#include <drm/drm_drv.h>
#include <drm/drm_edid.h>
#include <drm/drm_file.h>
#include <drm/drm_fourcc.h>
#include <drm/drm_fbdev_dma.h>
#include <drm/drm_fbdev_shmem.h>
#include <drm/drm_framebuffer.h>
#include <drm/drm_gem.h>
#include <drm/drm_gem_framebuffer_helper.h>
#include <drm/drm_gem_shmem_helper.h>
#include <drm/drm_mode_object.h>
#include <drm/drm_ioctl.h>
#include <drm/drm_probe_helper.h>
#include <drm/drm_vblank.h>
#include <kunit/test.h>
#include <linux/blk-mq.h>
#include <linux/blk_types.h>
#include <linux/blkdev.h>
#include <linux/dma-resv.h>
#include <linux/errname.h>
#include <linux/ethtool.h>
#include <linux/firmware.h>
#include <linux/jiffies.h>
#include <linux/iosys-map.h>
#include <linux/mdio.h>
#include <linux/pci.h>
#include <linux/phy.h>
......
// SPDX-License-Identifier: GPL-2.0
#include <linux/dma-resv.h>
int rust_helper_dma_resv_lock(struct dma_resv *obj, struct ww_acquire_ctx *ctx)
{
return dma_resv_lock(obj, ctx);
}
void rust_helper_dma_resv_unlock(struct dma_resv *obj)
{
dma_resv_unlock(obj);
}
// SPDX-License-Identifier: GPL-2.0
#include <drm/drm_atomic.h>
void rust_helper_drm_atomic_state_get(struct drm_atomic_state *state)
{
drm_atomic_state_get(state);
}
void rust_helper_drm_atomic_state_put(struct drm_atomic_state *state)
{
drm_atomic_state_put(state);
}
// Macros for generating one repetitive atomic state accessors (like drm_atomic_get_new_plane_state)
#define STATE_FUNC(type, tense) \
struct drm_ ## type ## _state *rust_helper_drm_atomic_get_ ## tense ## _ ## type ## _state( \
const struct drm_atomic_state *state, \
struct drm_ ## type *type \
) { \
return drm_atomic_get_## tense ## _ ## type ## _state(state, type); \
}
#define STATE_FUNCS(type) \
STATE_FUNC(type, new); \
STATE_FUNC(type, old);
STATE_FUNCS(plane);
STATE_FUNCS(crtc);
STATE_FUNCS(connector);
#undef STATE_FUNCS
#undef STATE_FUNC
// SPDX-License-Identifier: GPL-2.0
#ifdef CONFIG_DRM_KMS_HELPER
#include "atomic.c"
#include "vblank.c"
#endif
#include "gem.c"
#ifdef CONFIG_DRM_GEM_SHMEM_HELPER
#include "gem_shmem_helper.c"
#endif
#include "vma_manager.c"
#include <drm/drm_gem_shmem_helper.h>
void rust_helper_drm_gem_shmem_object_free(struct drm_gem_object *obj)
{
return drm_gem_shmem_object_free(obj);
}
void rust_helper_drm_gem_shmem_object_print_info(struct drm_printer *p, unsigned int indent,
const struct drm_gem_object *obj)
{
drm_gem_shmem_object_print_info(p, indent, obj);
}
int rust_helper_drm_gem_shmem_object_pin(struct drm_gem_object *obj)
{
return drm_gem_shmem_object_pin(obj);
}
void rust_helper_drm_gem_shmem_object_unpin(struct drm_gem_object *obj)
{
drm_gem_shmem_object_unpin(obj);
}
struct sg_table *rust_helper_drm_gem_shmem_object_get_sg_table(struct drm_gem_object *obj)
{
return drm_gem_shmem_object_get_sg_table(obj);
}
int rust_helper_drm_gem_shmem_object_vmap(struct drm_gem_object *obj,
struct iosys_map *map)
{
return drm_gem_shmem_object_vmap(obj, map);
}
void rust_helper_drm_gem_shmem_object_vunmap(struct drm_gem_object *obj,
struct iosys_map *map)
{
drm_gem_shmem_object_vunmap(obj, map);
}
int rust_helper_drm_gem_shmem_object_mmap(struct drm_gem_object *obj, struct vm_area_struct *vma)
{
return drm_gem_shmem_object_mmap(obj, vma);
}
// SPDX-License-Identifier: GPL-2.0
#include <drm/drm_vblank.h>
struct drm_vblank_crtc *rust_helper_drm_crtc_vblank_crtc(struct drm_crtc *crtc)
{
return drm_crtc_vblank_crtc(crtc);
}