Rust for Linux List
 help / color / mirror / Atom feed
* [PATCH 0/2] drm/tyr: add Job IRQ handling and GPU event wait support
@ 2026-07-21 15:14 Laura Nao
  2026-07-21 15:14 ` [PATCH 1/2] drm/tyr: add Wait type for GPU events Laura Nao
  2026-07-21 15:14 ` [PATCH 2/2] drm/tyr: add Job IRQ handling Laura Nao
  0 siblings, 2 replies; 4+ messages in thread
From: Laura Nao @ 2026-07-21 15:14 UTC (permalink / raw)
  To: daniel.almeida, aliceryhl, dakr, airlied, simona, ojeda
  Cc: boqun, gary, bjorn3_gh, a.hindborg, tmgross, tamird, acourbot,
	work, deborah.brouwer, linux-kernel, dri-devel, rust-for-linux,
	kernel, Laura Nao

This series follows up to [1], which adds support for firmware loading
and MCU booting to the Tyr driver. The changes included here were
originally introduced in v4[2], then dropped to reduce the scope of the
series, and have been adjusted to work with the HRT (Higher-Ranked
Lifetime Types) driver architecture recently introduced.

The first commit adds a new Wait type, wrapping a CondVar and a Mutex,
for sleeping until a condition is met or a timeout expires. This can be
used to wait for GPU-driven events.

The second commit adds a threaded IRQ wrapper for the firmware Job
interrupts, used to signal events from the global CSF (GLB) and Command
Stream Group (CSG) interfaces.

These changes will be later used to wait for global CSF interface
readiness after firmware boot, as part of the CSF firmware interfaces
support that will be submitted as a separate series.

This series is based on drm-rust-next and depends on:
- [PATCH v7 0/7] drm/tyr: firmware loading and MCU boot support[1]
- [PATCH v2] rust: irq: make Registration compatible with lifetime-bound drivers[2]

[1] https://lore.kernel.org/rust-for-linux/20260713-fw-boot-b4-v7-0-afd8dc1494dd@collabora.com/#t
[2] https://lore.kernel.org/rust-for-linux/20260719153631.559341-1-dakr@kernel.org/

Deborah Brouwer (1):
  drm/tyr: add Wait type for GPU events

Laura Nao (1):
  drm/tyr: add Job IRQ handling

 drivers/gpu/drm/tyr/driver.rs |  75 ++++++++++++++++++++
 drivers/gpu/drm/tyr/fw.rs     |   4 ++
 drivers/gpu/drm/tyr/fw/irq.rs | 104 ++++++++++++++++++++++++++++
 drivers/gpu/drm/tyr/tyr.rs    |   1 +
 drivers/gpu/drm/tyr/wait.rs   | 126 ++++++++++++++++++++++++++++++++++
 5 files changed, 310 insertions(+)
 create mode 100644 drivers/gpu/drm/tyr/fw/irq.rs
 create mode 100644 drivers/gpu/drm/tyr/wait.rs

-- 
2.39.5


^ permalink raw reply	[flat|nested] 4+ messages in thread

* [PATCH 1/2] drm/tyr: add Wait type for GPU events
  2026-07-21 15:14 [PATCH 0/2] drm/tyr: add Job IRQ handling and GPU event wait support Laura Nao
@ 2026-07-21 15:14 ` Laura Nao
  2026-07-21 15:32   ` Danilo Krummrich
  2026-07-21 15:14 ` [PATCH 2/2] drm/tyr: add Job IRQ handling Laura Nao
  1 sibling, 1 reply; 4+ messages in thread
From: Laura Nao @ 2026-07-21 15:14 UTC (permalink / raw)
  To: daniel.almeida, aliceryhl, dakr, airlied, simona, ojeda
  Cc: boqun, gary, bjorn3_gh, a.hindborg, tmgross, tamird, acourbot,
	work, deborah.brouwer, linux-kernel, dri-devel, rust-for-linux,
	kernel, Beata Michalska

From: Deborah Brouwer <deborah.brouwer@collabora.com>

Add a Wait convenience type wrapping a CondVar and Mutex for sleeping
until a condition is met or a timeout expires.

The helper centralizes a common wait pattern: check the completion
predicate before sleeping, wait interruptibly with a timeout, retry on
spurious or unrelated wakeups, and perform a final predicate check before
returning ETIMEDOUT.

This will be used for CSF firmware responses and other GPU-driven events.

Also add a new_wait! macro so each Wait instance gets a call-site-specific
lockdep class key for its internal mutex.

Co-developed-by: Daniel Almeida <daniel.almeida@collabora.com>
Signed-off-by: Daniel Almeida <daniel.almeida@collabora.com>
Co-developed-by: Beata Michalska <beata.michalska@arm.com>
Signed-off-by: Beata Michalska <beata.michalska@arm.com>
Signed-off-by: Deborah Brouwer <deborah.brouwer@collabora.com>
---
 drivers/gpu/drm/tyr/tyr.rs  |   1 +
 drivers/gpu/drm/tyr/wait.rs | 126 ++++++++++++++++++++++++++++++++++++
 2 files changed, 127 insertions(+)
 create mode 100644 drivers/gpu/drm/tyr/wait.rs

diff --git a/drivers/gpu/drm/tyr/tyr.rs b/drivers/gpu/drm/tyr/tyr.rs
index e7ec450bdc9c..3f6fe5fbeb0f 100644
--- a/drivers/gpu/drm/tyr/tyr.rs
+++ b/drivers/gpu/drm/tyr/tyr.rs
@@ -16,6 +16,7 @@
 mod regs;
 mod slot;
 mod vm;
+mod wait;
 
 kernel::module_platform_driver! {
     type: TyrPlatformDriver,
diff --git a/drivers/gpu/drm/tyr/wait.rs b/drivers/gpu/drm/tyr/wait.rs
new file mode 100644
index 000000000000..2a4d691c443c
--- /dev/null
+++ b/drivers/gpu/drm/tyr/wait.rs
@@ -0,0 +1,126 @@
+// SPDX-License-Identifier: GPL-2.0 or MIT
+
+//! Code to wait on GPU events.
+#![allow(dead_code)]
+
+use kernel::{
+    new_condvar,
+    prelude::*,
+    sync::{
+        lock::{
+            mutex::MutexBackend,
+            Lock, //
+        },
+        Arc,
+        CondVar,
+        CondVarTimeoutResult,
+        Mutex, //
+    },
+    time::msecs_to_jiffies, //
+};
+
+/// Creates a new [`Wait`] instance with a call-site-specific lockdep class key.
+///
+/// Always prefer this macro over [`Wait::new_with_lock`] when the [`Wait`] instance has
+/// unique locking behaviour that could otherwise trigger false-positive lockdep
+/// warnings.
+#[macro_export]
+macro_rules! new_wait {
+    () => {{
+        let lock = new_mutex!(());
+        $crate::wait::Wait::new_with_lock(lock)
+    }};
+}
+
+/// A convenience type to wait for GPU events.
+///
+/// Wraps a [`CondVar`] and [`Mutex`] pair. The mutex synchronizes predicate checks
+/// with wait/wake operations; the condvar provides the sleep/wake mechanism.
+#[pin_data]
+pub(crate) struct Wait {
+    /// The actual wait/signal mechanism.
+    #[pin]
+    cond: CondVar,
+    /// Synchronizes waiters with notifications.
+    #[pin]
+    lock: Mutex<()>,
+}
+
+impl Wait {
+    /// Creates a new [`Wait`] with a caller-supplied lock instance.
+    ///
+    /// Use [`new_wait!`] instead of calling this directly; the macro ensures a
+    /// per-call-site lockdep class key is registered.
+    pub(crate) fn new_with_lock(lock: impl PinInit<Lock<(), MutexBackend>>) -> Result<Arc<Self>> {
+        Arc::pin_init(
+            pin_init!(Self {
+                cond <- new_condvar!(),
+                lock <- lock,
+            }),
+            GFP_KERNEL,
+        )
+    }
+
+    /// Waits until a GPU event condition is met or the timeout elapses.
+    ///
+    /// Calls `on_woken` before sleeping and after each wakeup. If `on_woken`
+    /// returns [`WaitResult::Retry`], the wait continues; [`WaitResult::Done`]
+    /// returns success.
+    ///
+    /// `on_woken` is called while the internal wait lock is held, so it must be
+    /// cheap and must not call back into code that can notify this wait object.
+    ///
+    /// Returns [`ETIMEDOUT`] if the deadline is reached without the condition
+    /// becoming true, or [`ERESTARTSYS`] if interrupted by a signal.
+    pub(crate) fn wait_interruptible_timeout<F>(&self, timeout_ms: u32, mut on_woken: F) -> Result
+    where
+        F: FnMut() -> Result<WaitResult>,
+    {
+        let mut guard = self.lock.lock();
+        let mut remaining_time = msecs_to_jiffies(timeout_ms);
+
+        loop {
+            // Check the condition before sleeping to avoid missing a wakeup
+            // that arrived between the caller's last check and acquiring the
+            // lock here.
+            if let WaitResult::Done = on_woken()? {
+                return Ok(());
+            }
+
+            match self
+                .cond
+                .wait_interruptible_timeout(&mut guard, remaining_time)
+            {
+                CondVarTimeoutResult::Woken { jiffies } => match on_woken()? {
+                    WaitResult::Done => return Ok(()),
+                    WaitResult::Retry => remaining_time = jiffies,
+                },
+                CondVarTimeoutResult::Timeout => {
+                    // One final check before giving up.
+                    if let WaitResult::Done = on_woken()? {
+                        return Ok(());
+                    }
+                    return Err(ETIMEDOUT);
+                }
+                CondVarTimeoutResult::Signal { .. } => return Err(ERESTARTSYS),
+            }
+        }
+    }
+
+    /// Wakes all waiters.
+    ///
+    /// Takes the internal lock so notifications are serialized against waiters
+    /// checking the condition and entering the sleep state.
+    pub(crate) fn notify_all(&self) {
+        let _guard = self.lock.lock();
+        self.cond.notify_all();
+    }
+}
+
+/// The result of a wait operation.
+pub(crate) enum WaitResult {
+    /// The condition was met.
+    Done,
+    /// The wakeup was spurious or for an unrelated event; retry.
+    Retry,
+}
-- 
2.39.5


^ permalink raw reply related	[flat|nested] 4+ messages in thread

* [PATCH 2/2] drm/tyr: add Job IRQ handling
  2026-07-21 15:14 [PATCH 0/2] drm/tyr: add Job IRQ handling and GPU event wait support Laura Nao
  2026-07-21 15:14 ` [PATCH 1/2] drm/tyr: add Wait type for GPU events Laura Nao
@ 2026-07-21 15:14 ` Laura Nao
  1 sibling, 0 replies; 4+ messages in thread
From: Laura Nao @ 2026-07-21 15:14 UTC (permalink / raw)
  To: daniel.almeida, aliceryhl, dakr, airlied, simona, ojeda
  Cc: boqun, gary, bjorn3_gh, a.hindborg, tmgross, tamird, acourbot,
	work, deborah.brouwer, linux-kernel, dri-devel, rust-for-linux,
	kernel, Laura Nao

Add a threaded IRQ wrapper for Tyr interrupt sources and use it to handle
the firmware Job IRQ.

The Job IRQ reports requests from the CSF firmware, including global
interface requests and CSG attention bits. Add a Job IRQ handler that
masks the interrupt in the primary IRQ handler, processes pending raw
status in the threaded handler, clears handled bits, and reenables the
mask before returning.

Co-developed-by: Daniel Almeida <daniel.almeida@collabora.com>
Signed-off-by: Daniel Almeida <daniel.almeida@collabora.com>
Co-developed-by: Deborah Brouwer <deborah.brouwer@collabora.com>
Signed-off-by: Deborah Brouwer <deborah.brouwer@collabora.com>
Signed-off-by: Laura Nao <laura.nao@collabora.com>
---
 drivers/gpu/drm/tyr/driver.rs |  75 ++++++++++++++++++++++++
 drivers/gpu/drm/tyr/fw.rs     |   4 ++
 drivers/gpu/drm/tyr/fw/irq.rs | 104 ++++++++++++++++++++++++++++++++++
 3 files changed, 183 insertions(+)
 create mode 100644 drivers/gpu/drm/tyr/fw/irq.rs

diff --git a/drivers/gpu/drm/tyr/driver.rs b/drivers/gpu/drm/tyr/driver.rs
index 46ba91af9aca..6ee6f5e578b7 100644
--- a/drivers/gpu/drm/tyr/driver.rs
+++ b/drivers/gpu/drm/tyr/driver.rs
@@ -1,5 +1,7 @@
 // SPDX-License-Identifier: GPL-2.0 or MIT
 
+use core::marker::PhantomPinned;
+
 use kernel::{
     clk::{
         Clk,
@@ -21,6 +23,13 @@
         poll,
         Io, //
     },
+    irq::{
+        Flags,
+        IrqReturn,
+        ThreadedHandler,
+        ThreadedIrqReturn,
+        ThreadedRegistration, //
+    },
     new_mutex,
     of,
     platform,
@@ -238,3 +247,69 @@ struct Regulators {
     _mali: Regulator<regulator::Enabled>,
     _sram: Regulator<regulator::Enabled>,
 }
+
+pub(crate) trait TyrIrqTrait: Sync {
+    fn read_status(&self) -> u32;
+    fn clear_mask(&self);
+    fn reenable_mask(&self);
+    fn read_raw_status(&self) -> u32;
+    fn clear_status(&self, status: u32);
+    fn mask(&self) -> u32;
+    fn handle(&self, status: u32);
+}
+
+#[pin_data]
+pub(crate) struct TyrIrq<T: TyrIrqTrait> {
+    irq: T,
+    #[pin]
+    _pin: PhantomPinned,
+}
+
+impl<T: TyrIrqTrait> TyrIrq<T> {
+    pub(crate) fn request<'a>(
+        pdev: &'a platform::Device<Bound>,
+        name: &'static CStr,
+        irq: T,
+    ) -> Result<impl PinInit<ThreadedRegistration<'a, Self>, Error> + 'a>
+    where
+        T: 'a,
+    {
+        let handler = try_pin_init!(Self {
+            irq,
+            _pin: PhantomPinned,
+        });
+
+        // SAFETY: The resulting `PinInit` is not leaked, it is consumed by the caller to
+        // initialize a pinned `ThreadedRegistration`.
+        Ok(unsafe { pdev.request_threaded_irq_by_name(Flags::SHARED, name, name, handler) })
+    }
+}
+
+impl<T: TyrIrqTrait> ThreadedHandler for TyrIrq<T> {
+    fn handle(&self) -> ThreadedIrqReturn {
+        let masked_status = self.irq.read_status();
+
+        if masked_status == 0 {
+            return ThreadedIrqReturn::None;
+        }
+        self.irq.clear_mask();
+        ThreadedIrqReturn::WakeThread
+    }
+
+    fn handle_threaded(&self) -> IrqReturn {
+        let mut ret = IrqReturn::None;
+
+        loop {
+            let raw_status = self.irq.read_raw_status() & self.irq.mask();
+            if raw_status == 0 {
+                break;
+            }
+            self.irq.handle(raw_status);
+            self.irq.clear_status(raw_status);
+            ret = IrqReturn::Handled;
+        }
+
+        self.irq.reenable_mask();
+        ret
+    }
+}
diff --git a/drivers/gpu/drm/tyr/fw.rs b/drivers/gpu/drm/tyr/fw.rs
index 554808a792aa..4188dc4232f5 100644
--- a/drivers/gpu/drm/tyr/fw.rs
+++ b/drivers/gpu/drm/tyr/fw.rs
@@ -66,8 +66,12 @@
     vm::Vm, //
 };
 
+pub(crate) mod irq;
 mod parser;
 
+/// Maximum number of CSG interfaces supported by hardware.
+const MAX_CSG: usize = 16;
+
 impl_flags!(
     #[derive(Debug, Clone, Default, Copy, PartialEq, Eq)]
     pub(super) struct SectionFlags(u32);
diff --git a/drivers/gpu/drm/tyr/fw/irq.rs b/drivers/gpu/drm/tyr/fw/irq.rs
new file mode 100644
index 000000000000..4084904d53f6
--- /dev/null
+++ b/drivers/gpu/drm/tyr/fw/irq.rs
@@ -0,0 +1,104 @@
+// SPDX-License-Identifier: GPL-2.0 or MIT
+
+//! IRQ handling for the Job IRQ.
+//!
+//! The Job IRQ signals events from the MCU, including global interface acknowledgements.
+#![allow(dead_code)]
+
+use core::sync::atomic::{
+    AtomicBool,
+    Ordering, //
+};
+
+use kernel::{
+    c_str,
+    device::Bound, //
+    io::Io,
+    irq::ThreadedRegistration,
+    platform,
+    prelude::*,
+    sync::Arc, //
+};
+
+use crate::{
+    driver::{
+        IoMem,
+        TyrIrq,
+        TyrIrqTrait, //
+    },
+    regs::job_control::{
+        JOB_IRQ_CLEAR,
+        JOB_IRQ_MASK,
+        JOB_IRQ_RAWSTAT,
+        JOB_IRQ_STATUS, //
+    },
+    wait::Wait, //
+};
+
+const CSG_IRQ_MASK: u32 = (1u32 << super::MAX_CSG) - 1;
+
+pub(crate) struct JobIrq<'bound> {
+    iomem: Arc<IoMem<'bound>>,
+    fw_ready: Arc<AtomicBool>,
+    ready_wait: Arc<Wait>,
+}
+
+pub(crate) fn job_irq_init<'bound>(
+    pdev: &'bound platform::Device<Bound>,
+    iomem: Arc<IoMem<'bound>>,
+    fw_ready: Arc<AtomicBool>,
+    ready_wait: Arc<Wait>,
+) -> Result<impl PinInit<ThreadedRegistration<'bound, TyrIrq<JobIrq<'bound>>>, Error> + 'bound> {
+    iomem.write_reg(
+        JOB_IRQ_MASK::zeroed()
+            .with_const_csg::<CSG_IRQ_MASK>()
+            .with_glb(true),
+    );
+    let job_irq = JobIrq {
+        iomem: iomem.clone(),
+        fw_ready,
+        ready_wait,
+    };
+
+    TyrIrq::request(pdev, c_str!("job"), job_irq)
+}
+
+impl TyrIrqTrait for JobIrq<'_> {
+    fn read_status(&self) -> u32 {
+        self.iomem.read(JOB_IRQ_STATUS).into_raw()
+    }
+
+    fn clear_mask(&self) {
+        self.iomem.write_reg(JOB_IRQ_MASK::zeroed());
+    }
+
+    fn reenable_mask(&self) {
+        self.iomem.write_reg(
+            JOB_IRQ_MASK::zeroed()
+                .with_const_csg::<CSG_IRQ_MASK>()
+                .with_glb(true),
+        );
+    }
+
+    fn read_raw_status(&self) -> u32 {
+        self.iomem.read(JOB_IRQ_RAWSTAT).into_raw()
+    }
+
+    fn clear_status(&self, status: u32) {
+        self.iomem.write_reg(JOB_IRQ_CLEAR::from_raw(status));
+    }
+
+    fn mask(&self) -> u32 {
+        JOB_IRQ_MASK::zeroed()
+            .with_const_csg::<CSG_IRQ_MASK>()
+            .with_glb(true)
+            .into_raw()
+    }
+
+    fn handle(&self, status: u32) {
+        if JOB_IRQ_RAWSTAT::from_raw(status).glb() {
+            self.fw_ready.store(true, Ordering::Release);
+            self.ready_wait.notify_all();
+        }
+    }
+}
-- 
2.39.5


^ permalink raw reply related	[flat|nested] 4+ messages in thread

* Re: [PATCH 1/2] drm/tyr: add Wait type for GPU events
  2026-07-21 15:14 ` [PATCH 1/2] drm/tyr: add Wait type for GPU events Laura Nao
@ 2026-07-21 15:32   ` Danilo Krummrich
  0 siblings, 0 replies; 4+ messages in thread
From: Danilo Krummrich @ 2026-07-21 15:32 UTC (permalink / raw)
  To: Laura Nao
  Cc: daniel.almeida, aliceryhl, airlied, simona, ojeda, boqun, gary,
	bjorn3_gh, a.hindborg, tmgross, tamird, acourbot, work,
	deborah.brouwer, linux-kernel, dri-devel, rust-for-linux, kernel,
	Beata Michalska

On Tue Jul 21, 2026 at 5:14 PM CEST, Laura Nao wrote:
> +/// A convenience type to wait for GPU events.
> +///
> +/// Wraps a [`CondVar`] and [`Mutex`] pair. The mutex synchronizes predicate checks
> +/// with wait/wake operations; the condvar provides the sleep/wake mechanism.
> +#[pin_data]
> +pub(crate) struct Wait {
> +    /// The actual wait/signal mechanism.
> +    #[pin]
> +    cond: CondVar,
> +    /// Synchronizes waiters with notifications.
> +    #[pin]
> +    lock: Mutex<()>,
> +}

This is backwards; if I get this right at a quick glance it is basically abusing
CondVar to implement a WaitQueue abstraction in your driver.

What you actually seem to look for is a proper WaitQueue abstraction, which
could then also be used to simplify the implementation of CondVar.

I'm already working on a WaitQueue abstraction, since I need it elsewhere. I can
probably post something in a few days.

(I didn't check your use-case but you may want to consider completions and
simple waitqueue as well.)

^ permalink raw reply	[flat|nested] 4+ messages in thread

end of thread, other threads:[~2026-07-21 15:32 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-07-21 15:14 [PATCH 0/2] drm/tyr: add Job IRQ handling and GPU event wait support Laura Nao
2026-07-21 15:14 ` [PATCH 1/2] drm/tyr: add Wait type for GPU events Laura Nao
2026-07-21 15:32   ` Danilo Krummrich
2026-07-21 15:14 ` [PATCH 2/2] drm/tyr: add Job IRQ handling Laura Nao

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox