Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
53 commits
Select commit Hold shift + click to select a range
ec30b22
drm/asahi: enable J613 extended firmware render and retirement
satchlj Oct 31, 2026
efcd402
drm/asahi: admit authorized HIGH queues on Extended T8122
satchlj Nov 3, 2026
c1c0e23
drm/asahi: test the M3 host priority mapping alongside admission
satchlj Nov 3, 2026
ba872b9
ci: register the M3 queue priority admission tests
satchlj Nov 4, 2026
bd92c01
drm/asahi: back off interrupt-assisted M3 completion waits
satchlj Nov 3, 2026
9f3b310
drm/asahi: omit no-op M3 module pins without unload support
satchlj Nov 3, 2026
8f8374b
Merge branch 'satchlj/t8122-api-fixes-20261104' into satchlj/port-347…
satchlj Nov 5, 2026
40c0f57
selftests: asahi: match the 7.3 capability argument type
satchlj Nov 5, 2026
841394c
Merge branch 'satchlj/port-346-20261104' into satchlj/port-347-20261104
satchlj Nov 5, 2026
a64da66
Merge branch 'satchlj/t8122-api-fixes-20261104' into satchlj/port-307…
satchlj Nov 5, 2026
3822ecc
Merge branch 'satchlj/t8122-api-fixes-20261104' into satchlj/port-346…
satchlj Nov 5, 2026
82aaf1b
Merge branch 'satchlj/t8122-api-fixes-20261104' into satchlj/port-347…
satchlj Nov 5, 2026
8363fbb
Merge branch 'satchlj/t8122-api-fixes-20261104' into satchlj/port-307…
satchlj Nov 5, 2026
ae10e3b
Merge branch 'satchlj/t8122-api-fixes-20261104' into satchlj/port-346…
satchlj Nov 5, 2026
a3cf48e
Merge branch 'satchlj/t8122-api-fixes-20261104' into satchlj/port-347…
satchlj Nov 5, 2026
7879118
Merge branch 'satchlj/t8122-api-fixes-20261104' into satchlj/port-307…
satchlj Nov 5, 2026
79d45bd
Merge branch 'satchlj/t8122-api-fixes-20261104' into satchlj/port-346…
satchlj Nov 5, 2026
d7be34e
Merge branch 'satchlj/t8122-api-fixes-20261104' into satchlj/port-347…
satchlj Nov 5, 2026
72b6142
Merge branch 'satchlj/port-306-20261104' into satchlj/port-307-20261104
satchlj Nov 5, 2026
66eb34e
Merge branch 'satchlj/port-307-20261104' into satchlj/port-346-20261104
satchlj Nov 5, 2026
03692e5
Merge branch 'satchlj/port-346-20261104' into satchlj/port-347-20261104
satchlj Nov 5, 2026
34de4e4
Merge branch 'satchlj/t8122-api-fixes-20261104' into satchlj/port-307…
satchlj Nov 5, 2026
119f1ea
Merge branch 'satchlj/t8122-api-fixes-20261104' into satchlj/port-346…
satchlj Nov 5, 2026
020e3f8
Merge branch 'satchlj/t8122-api-fixes-20261104' into satchlj/port-347…
satchlj Nov 5, 2026
2436856
Merge branch 'satchlj/t8122-api-fixes-20261104' into satchlj/port-307…
satchlj Nov 5, 2026
49a678e
Merge branch 'satchlj/t8122-api-fixes-20261104' into satchlj/port-346…
satchlj Nov 5, 2026
523b9eb
Merge branch 'satchlj/t8122-api-fixes-20261104' into satchlj/port-347…
satchlj Nov 5, 2026
ef875a1
Merge branch 'satchlj/port-306-20261104' into satchlj/port-307-20261104
satchlj Nov 5, 2026
44c5989
Merge branch 'satchlj/port-307-20261104' into satchlj/port-346-20261104
satchlj Nov 5, 2026
c89fd6e
Merge branch 'satchlj/port-346-20261104' into satchlj/port-347-20261104
satchlj Nov 5, 2026
57d2141
Merge branch 'satchlj/t8122-api-fixes-20261104' into satchlj/port-307…
satchlj Nov 5, 2026
9a28266
Merge branch 'satchlj/t8122-api-fixes-20261104' into satchlj/port-346…
satchlj Nov 5, 2026
d798e55
Merge branch 'satchlj/t8122-api-fixes-20261104' into satchlj/port-347…
satchlj Nov 5, 2026
ea85d43
Merge branch 'satchlj/port-306-20261104' into satchlj/port-307-20261104
satchlj Nov 5, 2026
33dcc77
Merge branch 'satchlj/port-307-20261104' into satchlj/port-346-20261104
satchlj Nov 5, 2026
1ac0d6b
Merge branch 'satchlj/port-346-20261104' into satchlj/port-347-20261104
satchlj Nov 5, 2026
076b28b
Merge commit '33313096c66f4afdff4ef7060d20fe9bedebc34c' into satchlj/…
satchlj Nov 5, 2026
c34bce8
Merge commit '33313096c66f4afdff4ef7060d20fe9bedebc34c' into satchlj/…
satchlj Nov 5, 2026
bbf8435
Merge commit '33313096c66f4afdff4ef7060d20fe9bedebc34c' into satchlj/…
satchlj Nov 5, 2026
0d31784
Merge branch 'satchlj/port-306-20261104' into satchlj/port-307-20261104
satchlj Nov 5, 2026
f1e8a99
Merge branch 'satchlj/port-307-20261104' into satchlj/port-346-20261104
satchlj Nov 5, 2026
b73d1f9
Merge branch 'satchlj/port-346-20261104' into satchlj/port-347-20261104
satchlj Nov 5, 2026
c81410e
Merge commit 'b50066848322c6799c1433ee3a358aab2e9faabb' into satchlj/…
satchlj Nov 5, 2026
cd9eac9
Merge commit 'b50066848322c6799c1433ee3a358aab2e9faabb' into satchlj/…
satchlj Nov 5, 2026
1f5c951
Merge commit 'b50066848322c6799c1433ee3a358aab2e9faabb' into satchlj/…
satchlj Nov 5, 2026
9b2e8cf
Merge branch 'satchlj/port-306-20261104' into satchlj/port-307-20261104
satchlj Nov 5, 2026
e7936d8
Merge branch 'satchlj/port-307-20261104' into satchlj/port-346-20261104
satchlj Nov 5, 2026
7b88ebb
Merge branch 'satchlj/port-346-20261104' into satchlj/port-347-20261104
satchlj Nov 5, 2026
cc7dc16
Merge satchlj/port-306-20261104 into satchlj/port-307-20261104
DjDeveloperr Nov 7, 2026
b3a9f65
Merge satchlj/port-307-20261104 into satchlj/port-346-20261104
DjDeveloperr Nov 7, 2026
25f9051
Merge satchlj/port-346-20261104 into satchlj/port-347-20261104
DjDeveloperr Nov 7, 2026
9840daa
Merge pull request #977 from aurora-silicon/satchlj/port-347-20261104
RyanTheTide Nov 10, 2026
27d860d
Merge pull request #976 from aurora-silicon/satchlj/port-346-20261104
RyanTheTide Nov 10, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 8 additions & 2 deletions drivers/gpu/drm/asahi/file.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1350,9 +1350,15 @@ impl File {
cls_pr_debug!(Errors, "queue_create: Invalid priority\n");
return Err(EINVAL);
}
// HIGH has only been run on G17; G13 and G14 keep refusing it.
// Keep support tied to a backend and admitted profile. G13/G14 and
// unqualified M3 profiles retain their previous rejection.
let high_supported = reg_data.gpu.as_any().is::<crate::g17::Gpu>();
#[cfg(CONFIG_DRM_ASAHI_M3_T8122)]
let high_supported = high_supported || reg_data.gpu.as_any()
.downcast_ref::<crate::m3::m3_drm::Backend>()
.is_some_and(|gpu| gpu.supports_high_priority());
if data.priority == uapi::drm_asahi_priority_DRM_ASAHI_PRIORITY_HIGH
&& !reg_data.gpu.as_any().is::<crate::g17::Gpu>()
&& !high_supported
{
cls_pr_debug!(
Errors,
Expand Down
2 changes: 1 addition & 1 deletion drivers/gpu/drm/asahi/m3/m3_board.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ use crate::m3::m3_resources::{
use crate::m3::m3_soc::Soc;

/// Whether the root node's compatible list contains `compatible`.
fn board_is(compatible: &[u8]) -> bool {
pub(crate) fn board_is(compatible: &[u8]) -> bool {
let Some(root) = kernel::of::root() else {
return false;
};
Expand Down
26 changes: 11 additions & 15 deletions drivers/gpu/drm/asahi/m3/m3_completion.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,9 @@ impl WorkItem for Completion {
type Pointer = Arc<Self>;
fn run(owner: Arc<Self>) {
use crate::m3::m3_runtime::Service;
// Snapshots after a notification before sleeping for the next one: the completion
// stamps can trail the firmware's event message.
const TRAILING_POLLS: u32 = 4;
let (mut trailing, mut waited_at) = (0, u64::MAX);
// No deliberate spin: fsleep(5) selects udelay, not a scheduler sleep.
// Check immediately on IRQ, with backoff for trailing coherent witnesses.
let mut policy = super::m3_completion_policy::WaitPolicy::new();
loop {
let (outcome, events) = {
let mut guard = owner.shared.lock();
Expand All @@ -37,23 +36,20 @@ impl WorkItem for Completion {
// Preserve the root-only CPU-overlap diagnostic: disabled waits retain
// the runtime lock so VM/timestamp preparation cannot overlap the GPU.
if let Service::Waiting(messages)=outcome {
if !events.cpu_overlap() {events.wait_past(messages);continue;}
if !events.cpu_overlap() {
events.wait_past(messages, policy.next_wait_us(events.wait_ceiling_us()));
continue;
}
}
(outcome,events)
};
match outcome {
Service::Idle => break,
Service::Progress => { trailing = 0; waited_at = u64::MAX; }
Service::Progress => policy.progress(),
Service::Waiting(messages) => {
if messages != waited_at { waited_at = messages; trailing = 0; }
if trailing < TRAILING_POLLS {
trailing += 1;
kernel::time::delay::fsleep(kernel::time::Delta::from_micros(5));
} else {
// Until the next firmware event, or a bounded 100 us resnapshot.
events.wait_past(messages);
trailing = 0;
}
// IRQs and timer expiry alone are not retirement progress.
// Keep backing off until service publishes or retires work.
events.wait_past(messages, policy.next_wait_us(events.wait_ceiling_us()));
}
}
}
Expand Down
77 changes: 77 additions & 0 deletions drivers/gpu/drm/asahi/m3/m3_completion_policy.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
// SPDX-License-Identifier: GPL-2.0-only OR MIT

//! Bounded fallback for witnesses which can become visible after the last IRQ.
//!
//! Notifications request a fresh snapshot, but do not prove witness progress.
//! Only publication or retirement resets this policy. In particular, neither a
//! timer wake nor a stream of early firmware notifications renews a spin budget.

pub(crate) struct WaitPolicy {
next_us: u64,
}

impl WaitPolicy {
pub(crate) const fn new() -> Self {
Self { next_us: 25 }
}

pub(crate) fn progress(&mut self) {
self.next_us = 25;
}

/// No deliberate busy delay: IRQs still wake each sleep immediately.
/// The caller supplies the root-selected ceiling (25..=1000 us).
pub(crate) fn next_wait_us(&mut self, ceiling_us: u64) -> u64 {
let wait = self.next_us.min(ceiling_us.clamp(25, 1000));
self.next_us = (wait * 2).min(1000);
wait
}
}

#[cfg_attr(not(test), kernel::macros::kunit_tests(asahi_m3_completion_policy))]
mod tests {
use super::WaitPolicy;

#[test]
fn no_progress_backs_off_and_stays_bounded() {
let mut policy = WaitPolicy::new();
for expected in [25, 50, 100, 200, 400, 800, 1000, 1000] {
assert_eq!(policy.next_wait_us(1000), expected);
}
for _ in 0..100_000 {
assert_eq!(policy.next_wait_us(1000), 1000);
}
}

#[test]
fn publication_or_retirement_restarts_fast_detection() {
let mut policy = WaitPolicy::new();
for _ in 0..8 { policy.next_wait_us(1000); }
policy.progress();
assert_eq!(policy.next_wait_us(1000), 25);
assert_eq!(policy.next_wait_us(1000), 50);
}

#[test]
fn live_ceiling_changes_apply_to_the_next_wait() {
let mut policy = WaitPolicy::new();
for _ in 0..8 { policy.next_wait_us(1000); }
assert_eq!(policy.next_wait_us(25), 25);
assert_eq!(policy.next_wait_us(1000), 50);
assert_eq!(policy.next_wait_us(100), 100);
assert_eq!(policy.next_wait_us(100), 100);
}

#[test]
fn fallback_keeps_timeout_checks_frequent_without_any_irq() {
let mut policy = WaitPolicy::new();
let mut elapsed_us = 0;
while elapsed_us < 2_000_000 {
let wait = policy.next_wait_us(1000);
assert!(wait <= 1000);
elapsed_us += wait;
}
// This bounds the requested delay, not scheduler dispatch latency.
assert!(elapsed_us < 2_001_000);
}
}
10 changes: 6 additions & 4 deletions drivers/gpu/drm/asahi/m3/m3_compute.rs
Original file line number Diff line number Diff line change
Expand Up @@ -350,10 +350,12 @@ impl Compute {
let barriers=self.barriers.as_mut().ok_or(EINVAL)?;
let offset=self.slot*64;
let mut bytes=[0;crate::m3::m3_sync_layout::BARRIER_SIZE];
crate::m3::m3_sync_layout::Barrier {
let barrier=crate::m3::m3_sync_layout::Barrier {
stamp:dependency.stamp,wait_value:dependency.value,event:dependency.event,
self_value:value,uuid:0xc10000,dependency:crate::m3::m3_sync_layout::Dependency::PreviousPass,
}.encode(&mut bytes).map_err(|_|EINVAL)?;
};
if self.profile==super::m3_profile::Profile::Extended {barrier.encode_extended(&mut bytes)}
else {barrier.encode(&mut bytes)}.map_err(|_|EINVAL)?;
barriers.write(offset,&bytes)?;
let address=barriers.va()+offset as u64;
let (slots,next)=queue::insert_before_last(self.head,queue::QUALIFIED_CAPACITY).map_err(|_|EINVAL)?;
Expand Down Expand Up @@ -382,8 +384,8 @@ impl Compute {
/// Read only after stamps, queues, events and idle state prove retirement.
pub(crate) fn batch_gpu_span(&mut self)->Result<[u64;2]> {
if self.batch_count == 0 {return Err(EINVAL);}
Ok([self.objects[TIMESTAMPS[0]].read_u64(storage::slot_offset_for(self.profile,TIMESTAMPS[0],0).map_err(|_|EINVAL)?)?,
self.objects[TIMESTAMPS[1]].read_u64(storage::slot_offset_for(self.profile,TIMESTAMPS[1],self.batch_count-1).map_err(|_|EINVAL)?)?])
Ok([self.profile.profiler_ticks(self.objects[TIMESTAMPS[0]].read_u64(storage::slot_offset_for(self.profile,TIMESTAMPS[0],0).map_err(|_|EINVAL)?)?),
self.profile.profiler_ticks(self.objects[TIMESTAMPS[1]].read_u64(storage::slot_offset_for(self.profile,TIMESTAMPS[1],self.batch_count-1).map_err(|_|EINVAL)?)?)])
}
pub(crate) fn head(&self) -> u16 { self.head }
pub(crate) fn ordinal(&self)->u64 {self.sequence}
Expand Down
12 changes: 12 additions & 0 deletions drivers/gpu/drm/asahi/m3/m3_config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -246,6 +246,18 @@ impl Config {
self.thermal.busy(now);
Ok(())
}
pub(crate) fn render_stats(&self) -> Result<[init::Region;2]> {
if self.profile==super::m3_profile::Profile::Legacy {
let value=self.stats_region()?;return Ok([value;2]);
}
let region=|index:usize,offset:usize| {
let object=&self.objects[index];
init::Region::new(object.va().checked_add(offset as u64).ok_or(EOVERFLOW)?,
object.size().checked_sub(offset).ok_or(EINVAL)?).map_err(|_|EINVAL)
};
Ok([region(UNKNOWN_C0,4)?,region(UNKNOWN_C1,8)?])
}

pub(crate) fn stats_region(&self) -> Result<init::Region> {
let owner = &self.objects[if self.profile == super::m3_profile::Profile::Extended {
UNKNOWN_C3
Expand Down
17 changes: 12 additions & 5 deletions drivers/gpu/drm/asahi/m3/m3_drm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ struct CompletionView(Arc<crate::m3::m3_rtkit::EventWait>);
impl kernel::debugfs::Writer for CompletionView {
fn write(&self, f: &mut kernel::fmt::Formatter<'_>) -> kernel::fmt::Result {
let (us, irqs, sleeps, timers, preparations) = self.0.snapshot();
writeln!(f, "version=1 wait_us={} notifications={} sleeps={} timer_wakes={} cpu_preparations={}",
writeln!(f, "version=2 policy=adaptive wait_us={} notifications={} sleeps={} timer_wakes={} cpu_preparations={}",
us, irqs, sleeps, timers, preparations)
}
}
Expand Down Expand Up @@ -96,6 +96,11 @@ pub(crate) struct Backend {
}

impl Backend {
/// HIGH promises host publication ordering, not firmware preemption.
pub(crate) fn supports_high_priority(&self) -> bool {
self.soc.chip_id == 0x8122 && self.profile == super::m3_profile::Profile::Extended
}

/// Admits and starts the M3 runtime on `soc` (the T8122), with the DRM device `drm` that the
/// caller registers afterwards.
pub(crate) fn start(
Expand All @@ -113,10 +118,12 @@ impl Backend {
crate::m3::t8122_start::not_admitted(pdev.as_ref(), soc);
})?;
dev_info!(pdev.as_ref(), "M3: resource admission complete\n");
// An armed T8122 start supplies the values its table leaves out. An unarmed T8122
// needs a complete table, which it does not have: it is refused here.
let experiment = crate::m3::t8122_start::arm(pdev.as_ref(), soc)?;
if experiment.is_none() {
// Extended T8122 uses its dedicated initialization graph. Keep the
// legacy guarded-start and completeness checks for older interfaces.
let extended=crate::m3::t8122_start::is_t8122(soc)
&& crate::m3::t8122_admission::loaded_profile(pdev)?==super::m3_profile::Profile::Extended;
let experiment = if extended {None} else {crate::m3::t8122_start::arm(pdev.as_ref(), soc)?};
if experiment.is_none() && !extended {
soc.require_complete(pdev.as_ref())?;
}
let experiment = experiment.as_ref();
Expand Down
10 changes: 7 additions & 3 deletions drivers/gpu/drm/asahi/m3/m3_firmware.rs
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ pub(crate) static KNOWN_IMAGES_T8122: [KnownImage; 1] = [KnownImage {
initdata_magic: Some(G15_V14_8_3_INITDATA),
}];

/// Image record for the extended format. The SoC's runtime selection does not use it yet.
/// Exact normalized image identity for the extended firmware interface.
pub(crate) static T8122_IMAGES_26: [KnownImage; 1] = [KnownImage {
name: "T8122 C0 firmware-compat 26.6.2",
uuid: None,
Expand Down Expand Up @@ -164,7 +164,7 @@ pub(crate) static T8122_LAYOUT: Layout = Layout {
pub(crate) const TEXT_SHA256: [u8; 32] = [
0x11, 0xe4, 0x9f, 0x75, 0xb, 0x67, 0x1a, 0x2b, 0x13, 0xd0, 0x92, 0xdb, 0x1c, 0xbc, 0xa3, 0xa8, 0x77, 0x55, 0x86, 0xd6, 0xb1, 0xe3, 0xaa, 0xe8, 0xa6, 0xa4, 0x64, 0xa7, 0x80, 0x2, 0x93, 0xd5];

/// T8122 C0, firmware-compat 26.6.2. Selection remains separate from runtime admission.
/// T8122 C0, firmware-compat 26.6.2 segment and loader-mutable bounds.
pub(crate) static T8122_LAYOUT_26: Layout = Layout {
text_size: 393216,
data_size: 1228800,
Expand Down Expand Up @@ -313,7 +313,10 @@ pub(crate) fn identify_loaded(
prelude::*,
};

let layout = soc.firmware.ok_or(ENODEV)?;
let extended=crate::m3::t8122_start::is_t8122(soc)
&& crate::m3::t8122_admission::loaded_profile(pdev)?==super::m3_profile::Profile::Extended;
if extended && experiment.is_some() {return Err(EINVAL);}
let layout = if extended {&T8122_LAYOUT_26} else {soc.firmware.ok_or(ENODEV)?};
if !Firmware::matching_layout(layout, &resources) {
dev_err!(pdev.as_ref(), "M3 {}: unsupported firmware segment layout\n", soc.gpu_name);
return Err(ENODEV);
Expand All @@ -337,6 +340,7 @@ pub(crate) fn identify_loaded(
// The T8122 start experiment identifies the same image, with its own InitData version.
let images = match experiment {
Some(_) => &KNOWN_IMAGES_T8122_EXPERIMENT[..],
None if extended => &T8122_IMAGES_26[..],
None => soc.images,
};
let image = crate::m3::m3_board::identify(pdev.as_ref(), bytes, &digest, images).ok_or(ENODEV)?;
Expand Down
Loading