Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
7e8b229
drm/apple: fix the merged firmware-27 table's A428 method and try it …
sithglan Nov 7, 2026
121d6e9
drm/apple: T8142: run the macOS 27.0 DCP firmware as firmware 27
sithglan Nov 7, 2026
43f8980
drm/apple: T8142 on firmware 27: offer only alpha formats on the plane
sithglan Nov 9, 2026
d89d347
drm/apple: say why a present is dropped at the backlight step
sithglan Nov 9, 2026
db0c05a
drm/asahi: T8142 on 26A428: probe the G17 path and log the GPU identity
sithglan Nov 7, 2026
482acb1
drm/asahi: g17: M5: the J813 (T8142) on the 26A428 graph (bring-up)
sithglan Nov 7, 2026
ea2f309
drm/asahi: g17: M5: J813 InitData order, GFX1 first (bring-up)
sithglan Nov 7, 2026
4b4cfaf
drm/asahi: g17: M5: the J813 on the common 26A428 InitData layout (br…
sithglan Nov 7, 2026
1b45e21
drm/asahi: g17: M5: fill the J813 InitData kick-queue seed and entry …
sithglan Nov 7, 2026
e5bdd20
drm/asahi: g17: M5: follow macOS's order for the gating and the init ack
sithglan Nov 8, 2026
42499a1
drm/asahi: g17: M5: the J813's own register map, from macOS 27.0's bu…
sithglan Nov 8, 2026
e4b2dba
drm/asahi: g17: M5: publish the J813's rings where macOS 27.0 does
sithglan Nov 8, 2026
6c71c9c
drm/asahi: g17: M5: hand the J813's coprocessors their log buffers
sithglan Nov 8, 2026
65967c8
drm/asahi: g17: M5: the firmware's own offsets in the descriptor tail
sithglan Nov 8, 2026
3719811
drm/asahi: g17: M5: keep the J813's always-on block where macOS 27.0 …
sithglan Nov 8, 2026
17d8a6e
drm/asahi: g17: M5: the J813's perf tables as macOS 27.0 has them
sithglan Nov 8, 2026
2499e26
drm/asahi: g17: M5: read back the firmware's log and hardware data
sithglan Nov 8, 2026
16e5246
drm/asahi: g17: M5: hold the J813's GFX power domain for the session
sithglan Nov 8, 2026
12f0fbf
drm/asahi: g17: say what the firmware did with a render that timed out
sithglan Nov 8, 2026
e5bf560
drm/asahi: g17: name the command a refused submission was refused for
sithglan Nov 8, 2026
c52c64a
drm/asahi: g17: name the step a submission was refused at
sithglan Nov 8, 2026
e6bb758
drm/asahi: g17: M5: the J813's render commands as its own kext writes…
sithglan Nov 8, 2026
4b06516
drm/asahi: g17: M5: the J813's compute kicks as macOS 27.0 writes them
sithglan Nov 9, 2026
b31f941
drm/asahi: g17: M5: the J813's TA and fragment programs as macOS 27.0…
sithglan Nov 9, 2026
838bbe8
drm/asahi: g17: start the work-state words at generation 1
sithglan Nov 9, 2026
752b3e4
drm/asahi: g17: M5: submit to the J813 in the common 26A428 forms
sithglan Nov 9, 2026
0baa7d3
drm/asahi: g17: M5: the J813's pools in the G17P form
sithglan Nov 9, 2026
ab6729b
drm/asahi: g17: M5: map the J813's render metrics in context 0
sithglan Nov 9, 2026
3e0b58b
drm/asahi: g17: grow a parameter buffer only as far as it holds
sithglan Nov 9, 2026
7595d75
drm/asahi: g17: M5: on the J813, a tiling kick waits for the last fra…
sithglan Nov 9, 2026
2100ac7
Merge integration/aurora into thomas/j813-gpu-27-on-integration
DjDeveloperr Nov 11, 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
28 changes: 27 additions & 1 deletion Documentation/devicetree/bindings/display/apple,dcp.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -514,7 +514,13 @@ allOf:
- const: 27
- const: 0
- const: 0
else:
- if:
not:
properties:
compatible:
contains:
enum: ['apple,t6050-dcp', 'apple,t6050-dcpext', 'apple,t8142-dcp']
then:
properties:
clocks:
maxItems: 2
Expand All @@ -524,6 +530,26 @@ allOf:
- const: clock-194
apple,clock-ids: false
apple,firmware-physical-regs: false
# T8142 runs both 26.x and 27.0 DCP firmware. The 26.x paths use the first
# clock; firmware 27 asks for the provider clocks of apple,clock-ids by
# index, as firmware-0 and firmware-1.
- if:
properties:
compatible:
contains:
const: apple,t8142-dcp
then:
properties:
clocks:
minItems: 1
maxItems: 3
clock-names:
minItems: 1
items:
- const: clock-154
- const: firmware-0
- const: firmware-1
apple,firmware-physical-regs: false
- if:
not:
properties:
Expand Down
34 changes: 33 additions & 1 deletion arch/arm64/boot/dts/apple/t8142-j813-display.dtsi
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,14 @@
apple,bw-doorbell = <&pmgr_dcp 1 6>;
/* Scan out the primary plane on IOMFB surface 2. */
apple,iomfb-surfaces = <0 0 1 0>;
clocks = <&clkref>;
/*
* clkref is the clock the 26.x firmware paths use. Firmware 27
* (DCP_FIRMWARE_V_27_0) asks for its provider clocks by index into
* /arm-io/disp0 clock-ids, in this order: firmware-0 and firmware-1.
*/
apple,clock-ids = <585 431>;
clocks = <&clkref>, <&dcp_fw_clk585>, <&dcp_fw_clk431>;
clock-names = "clock-154", "firmware-0", "firmware-1";
/* generate phandle explicitly for use in loader */
phandle = <&dcp>;
status = "disabled";
Expand All @@ -106,3 +113,28 @@
};
};
};

/ {
/*
* The DCP firmware's clocks 585 and 431 (/arm-io/disp0 clock-ids). The
* J813 ADT's clock-frequencies (ids 0x100..0x1ad) has no entry for
* either. Firmware 27 reads only 585, as its minimum pixel clock
* (getClockFrequency(0)); below its 138.521 MHz video clock it gives up
* on frequencies. 712 MHz is clock-frequencies entry 0x49, the one 585's
* low byte names; with it macOS 27.0's DCP firmware offered a display
* under aura. The firmware never asks for 431, which has no rate here.
*/
dcp_fw_clk585: clock-dcp-firmware-585 {
compatible = "fixed-clock";
#clock-cells = <0>;
clock-frequency = <712000000>;
clock-output-names = "dcp_firmware_585";
};

dcp_fw_clk431: clock-dcp-firmware-431 {
compatible = "fixed-clock";
#clock-cells = <0>;
clock-frequency = <0>;
clock-output-names = "dcp_firmware_431";
};
};
16 changes: 14 additions & 2 deletions arch/arm64/boot/dts/apple/t8142-j813-gpu.dtsi
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,17 @@
reg = <0 0 0 0>;
};

/*
* macOS 27.0 (26A428): both roles' __DATA and __DATA_SHARED_RO, contiguous
* (ADT gfx-asc and gfx1-asc segment-ranges), where the firmware places its crash
* logs. Fixed at the addresses iBoot has used on every 27.0 boot so far, until the
* bootloader fills it in.
*/
/* Mapped like gfx-data: the driver reads the crash logs through the linear map. */
gpu_fw_data: fw-data {
reg = <0x100 0x02444000 0x0 0x184000>;
};

uat_handoff: uat-handoff {
reg = <0 0 0 0>;
};
Expand Down Expand Up @@ -93,9 +104,10 @@
mboxes = <&gfx_mbox>, <&gfx1_mbox>;
power-domains = <&ps_gfx>;
memory-region = <&uat_ttbs>, <&uat_pagetables>, <&uat_handoff>,
<&uat_l2_pagetables>, <&gfx_data>, <&gfx1_data>;
<&uat_l2_pagetables>, <&gfx_data>, <&gfx1_data>,
<&gpu_fw_data>;
memory-region-names = "ttbs", "pagetables", "handoff",
"shared-l2", "gfx-data", "gfx1-data";
"shared-l2", "gfx-data", "gfx1-data", "fw-data";
operating-points-v2 = <&gpu_opp>;
/* Holds the DCS and fabric floor while GPU work is in flight. */
apple,clpc = <&clpc>;
Expand Down
16 changes: 14 additions & 2 deletions drivers/gpu/drm/apple/apple_drv.c
Original file line number Diff line number Diff line change
Expand Up @@ -1135,11 +1135,23 @@ static int apple_probe_per_dcp(struct device *dev,
interchange = supports_interchange &&
(zpos <= 1 || surf != top_surf);

/*
* The T8142 runs macOS 27.0's DCP firmware as firmware
* 27, outside the H17G profile, and blends XRGB's X
* byte as alpha all the same (GPU-rendered alacritty
* showed wrong colours on the panel).
*/
bool xrgb_is_alpha =
dcp_data->hw.iomfb_method_profile ==
DCP_IOMFB_METHODS_H17G ||
(dcp_data->fw_compat == DCP_FIRMWARE_V_27_0 &&
of_device_is_compatible(dcp_data->dev->of_node,
"apple,t8142-dcp"));

planes[zpos] = apple_plane_init(drm, 1U << num, surf,
supports_l10r,
supports_xrgb2101010,
dcp_data->hw.iomfb_method_profile ==
DCP_IOMFB_METHODS_H17G,
xrgb_is_alpha,
plane_type, interchange,
supports_premultiplied);
}
Expand Down
43 changes: 43 additions & 0 deletions drivers/gpu/drm/apple/dcp.c
Original file line number Diff line number Diff line change
Expand Up @@ -5013,6 +5013,18 @@ static enum dcp_firmware_version dcp_check_firmware_version(struct device *dev)
char fw_str[DCP_FW_VERSION_STR_LEN];
int ret;

/*
* T8142 (J813) on macOS 27.0: the H17P interface written for its 26.x
* firmware makes the 27.0 DCP firmware panic right after the first
* hotplug ("ap_link: callback failed with: 0x5"). It runs the T6050's
* firmware-27 generation.
*/
if (of_device_is_compatible(dev->of_node, "apple,t8142-dcp")) {
ret = dcp_read_fw_version(dev, "apple,firmware-compat", compat_str);
if (ret >= 0 && !strcmp(compat_str, "27.0.0"))
return DCP_FIRMWARE_V_27_0;
}

if (hw->firmware_compat == DCP_FIRMWARE_V_27_0) {
ret = dcp_read_fw_version(dev, "apple,firmware-compat", compat_str);
if (ret < 0 || strcmp(compat_str, "27.0.0"))
Expand Down Expand Up @@ -5270,6 +5282,30 @@ static int dcp_firmware_metadata_v27(struct apple_dcp *dcp)
clocks = 0;
if (clocks < 0 || clocks > 64)
return -EINVAL;
/*
* A DCP that also boots earlier firmware (T8142) lists the clock those
* use first and names the provider clocks "firmware-<index>". Take only
* the named ones here.
*/
if (clocks &&
of_property_match_string(dev->of_node, "clock-names", "firmware-0") >= 0) {
dcp->firmware_clocks = devm_kcalloc(dev, clocks,
sizeof(*dcp->firmware_clocks),
GFP_KERNEL);
if (!dcp->firmware_clocks)
return -ENOMEM;
for (index = 0; index < clocks; index++) {
dcp->firmware_clocks[index].id =
devm_kasprintf(dev, GFP_KERNEL, "firmware-%d", index);
if (!dcp->firmware_clocks[index].id)
return -ENOMEM;
}
ret = devm_clk_bulk_get(dev, clocks, dcp->firmware_clocks);
if (ret)
return ret;
dcp->num_firmware_clocks = clocks;
return 0;
}
ret = devm_clk_bulk_get_all(dev, &dcp->firmware_clocks);
if (ret < 0)
return ret;
Expand Down Expand Up @@ -6147,6 +6183,13 @@ static int dcp_platform_probe_pipe(struct platform_device *pdev)
if (ret)
return ret;
dcp->hw = *(struct apple_dcp_hw_data *)of_device_get_match_data(dev);
/*
* T8142 on firmware 27 runs as the T6050's firmware-27 instance, with
* the default method profile: with H17G every swap fails with status 4.
*/
if (fw_compat == DCP_FIRMWARE_V_27_0 &&
of_device_is_compatible(dev->of_node, "apple,t8142-dcp"))
dcp->hw.iomfb_method_profile = DCP_IOMFB_METHODS_DEFAULT;
/* The display such a processor finds at boot is its first sink. */
if (dcp_sink_metadata(dcp))
dcp_desktop_metadata_new_sink(dcp);
Expand Down
12 changes: 10 additions & 2 deletions drivers/gpu/drm/apple/iomfb.c
Original file line number Diff line number Diff line change
Expand Up @@ -3321,13 +3321,21 @@ bool iomfb_apply_backlight_h17p(struct apple_dcp *dcp,
dcp->brightness.maximum, false);
return true;
}
if (!transaction || !dcp->brightness.maximum)
if (!transaction || !dcp->brightness.maximum) {
/* The caller drops this present ("swap failed"): say why. */
dev_warn_ratelimited(dcp->dev,
"present dropped: transaction %d brightness maximum %u\n",
!!transaction, dcp->brightness.maximum);
return false;
}
for (i = 0; i < SWAP_SURFACES; i++)
have_surface |= !request->surf_null[i];
ret = dcp_backlight_prepare(dcp, have_surface, &present);
if (ret && ret != -EALREADY)
if (ret && ret != -EALREADY) {
dev_warn_ratelimited(dcp->dev,
"present dropped: backlight prepare %d\n", ret);
return false;
}
if (!ret) {
transaction->backlight = present;
transaction->backlight_reserved = true;
Expand Down
2 changes: 1 addition & 1 deletion drivers/gpu/drm/asahi/Makefile
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# SPDX-License-Identifier: GPL-2.0

obj-$(CONFIG_DRM_ASAHI) += aurora.o
aurora-y := asahi_core.o fwid.o legacy_sched.o sysfs.o g17/sync_input.o g17/sync_output.o g17/sync_scope.o g17/trace.o g17/clpc.o g17/fans.o
aurora-y := asahi_core.o fwid.o legacy_sched.o sysfs.o g17/sync_input.o g17/sync_output.o g17/sync_scope.o g17/trace.o g17/clpc.o g17/fans.o g17/pmpdash.o
CFLAGS_g17/trace.o := -I$(src)/g17

aurora-$(CONFIG_DRM_ASAHI_G15) += g15/glue.o
Expand Down
11 changes: 11 additions & 0 deletions drivers/gpu/drm/asahi/driver.rs
Original file line number Diff line number Diff line change
Expand Up @@ -547,6 +547,17 @@ impl AsahiPlatformDriver {
fn probe_m5<'bound>(
pdev: &'bound platform::Device<Core<'_>>,
) -> Result<AsahiPlatformDriverData<'bound>> {
// Without aura's 25G76 profile (the macOS 27.0 stub's 26A428 firmware), take the M5
// graph's G17 path (J813 bring-up).
let has_25g76 = of::chosen().is_some_and(|chosen| {
matches!(
chosen.get_opt_property::<KVec<u8>>(c"aurora,m5-gpu-firmware-profile"),
Ok(Some(_))
)
});
if !has_25g76 {
return Self::probe_g17(pdev, regs::G17Resources::new(pdev)?, &hw::t8142::CONFIG);
}
// SAFETY: The M5 UAT has 42 output bits.
unsafe { pdev.dma_set_mask_and_coherent(DmaMask::try_new(42)?)? };
let resources = regs::Resources::new(pdev)?;
Expand Down
20 changes: 18 additions & 2 deletions drivers/gpu/drm/asahi/g17/buffer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -581,8 +581,20 @@ impl MetricsLease {
}
let pitch = self.allocator.entry_pitch;
let offset = self.slot.checked_mul(pitch).ok_or(EOVERFLOW)?;
let client = self.backing.map_range(vm, offset..offset + pitch, BlockGeometry::M5_COMPACT,
mmu::UAT_PGSZ as u64, mmu::PROT_GPU_SHARED_RW, 0)?;
// The J813's firmware touches a render's metrics in context 0, whose user root is the
// kernel lower VM, at the client's address. That VM maps the whole backing at the compact
// window's start (InitData::bind_m5_metrics), so place the entry where its offset puts
// it there.
let fixed = super::J813_FORMS.load(Ordering::Relaxed).then(|| {
let at = BlockGeometry::M5_COMPACT.start + offset as u64;
self.backing.map_range(vm, offset..offset + pitch, at..at + pitch as u64,
mmu::UAT_PGSZ as u64, mmu::PROT_GPU_SHARED_RW, 0).ok()
}).flatten();
let client = match fixed {
Some(client) => client,
None => self.backing.map_range(vm, offset..offset + pitch, BlockGeometry::M5_COMPACT,
mmu::UAT_PGSZ as u64, mmu::PROT_GPU_SHARED_RW, 0)?,
};
self.addresses.client = client.iova();
self.client = Some(client);
Ok(())
Expand Down Expand Up @@ -833,6 +845,10 @@ impl Manager {
pub(crate) fn firmware_target(&self) -> usize {
self.blocks().saturating_add(fw::GROW_BLOCKS).min(self.tvb.geometry.max_blocks())
}
/// The most blocks this buffer's geometry can hold.
pub(crate) fn max_blocks(&self) -> usize {
self.tvb.geometry.max_blocks()
}
/// Read-only diagnostic while firmware is halted. In particular this
/// does not reset the high-water counter as automatic_target() does.
pub(crate) fn report_recovery(&self) {
Expand Down
2 changes: 1 addition & 1 deletion drivers/gpu/drm/asahi/g17/buffer/target.rs
Original file line number Diff line number Diff line change
Expand Up @@ -277,7 +277,7 @@ impl Target {
Ok(crate::g17::fw::m5::render_registers::TaExtra {
gtp1: pointers[1], gtp2: pointers[2], gtp4: pointers[4], gtp5: pointers[5],
gtp6: pointers[6], gtp7: pointers[7], segment_control: control[0], segment_index: control[1],
min_free_tvb_pages: control[2], scs_bracket: 0, matrix_bracket: 0,
min_free_tvb_pages: control[2], scs_bracket: 0, matrix_bracket: 0, j813: false,
})
}

Expand Down
62 changes: 62 additions & 0 deletions drivers/gpu/drm/asahi/g17/command.rs
Original file line number Diff line number Diff line change
Expand Up @@ -199,7 +199,34 @@ impl<'a> Parser<'a> {
}
}

/// A refused submission reaches userspace as EINVAL alone, which names neither the command
/// the parser stopped at nor what it holds. Name both, for the first few of a boot.
pub(crate) fn next(&mut self) -> Result<Option<Command>> {
let head = self.bytes;
let result = self.next_parsed();
if let Err(error) = &result {
static REFUSED: core::sync::atomic::AtomicU32 = core::sync::atomic::AtomicU32::new(0);
if REFUSED.fetch_add(1, core::sync::atomic::Ordering::Relaxed) < 8 {
match Reader::new(head).read::<uapi::drm_asahi_cmd_header>() {
Ok(header) => pr_warn!(
"G17: refused a submission ({:?}) at its command of type {}, {} bytes, \
barriers [{:#x}, {:#x}], with {} of the submission's bytes left\n",
error, header.cmd_type, header.size, header.vdm_barrier,
header.cdm_barrier, head.len()
),
Err(_) => pr_warn!(
"G17: refused a submission ({:?}): its remaining {} bytes are not a \
command header\n",
error,
head.len()
),
}
}
}
result
}

fn next_parsed(&mut self) -> Result<Option<Command>> {
while !self.bytes.is_empty() {
let mut reader = Reader::new(self.bytes);
let header: uapi::drm_asahi_cmd_header = reader.read()?;
Expand Down Expand Up @@ -284,6 +311,13 @@ impl<'a> Parser<'a> {

pub(crate) fn finish(&self) -> Result {
if !self.bytes.is_empty() || self.counts == [0; 2] {
pr_warn!(
"G17: refused a submission: {} bytes past its last command, {} renders and {} \
dispatches\n",
self.bytes.len(),
self.counts[0],
self.counts[1]
);
return Err(EINVAL);
}
Ok(())
Expand Down Expand Up @@ -494,11 +528,39 @@ impl Payload {
}
}

/// A refused submission reaches userspace as EINVAL alone, which names neither the command
/// nor the requirement it missed. Print the fields the requirements below are written in,
/// as userspace sent them, for the first few refusals of a boot.
pub(crate) fn validate(
&self,
window: &UscWindow,
variant: &super::fw::Variant,
space: &impl AddressSpace,
) -> Result<Validated> {
let result = self.validate_checked(window, variant, space);
if let (Err(error), Self::Render(cmd, ..)) = (&result, self) {
static REFUSED: core::sync::atomic::AtomicU32 = core::sync::atomic::AtomicU32::new(0);
if REFUSED.fetch_add(1, core::sync::atomic::Ordering::Relaxed) < 4 {
pr_warn!(
"G17: refused a render ({:?}): {}x{} px, {} layers, {} samples, {} B/sample, \
utile {}x{}, flags {:#x}, vdm {:#x}, scissor {:#x}, dbias {:#x}, oclqry {:#x}, \
usc bg {:#x} eot {:#x} partial bg {:#x} eot {:#x}, samplers {} at {:#x}\n",
error, cmd.width_px, cmd.height_px, cmd.layers, cmd.samples,
cmd.sample_size_B, cmd.utile_width_px, cmd.utile_height_px, cmd.flags,
cmd.vdm_ctrl_stream_base, cmd.isp_scissor_base, cmd.isp_dbias_base,
cmd.isp_oclqry_base, cmd.bg.usc, cmd.eot.usc, cmd.partial_bg.usc,
cmd.partial_eot.usc, cmd.sampler_count, cmd.sampler_heap
);
}
}
result
}

fn validate_checked(
&self,
window: &UscWindow,
variant: &super::fw::Variant,
space: &impl AddressSpace,
) -> Result<Validated> {
// Everything M5 accepts beyond the G17P command set is gated on this.
let m5 = variant.m5_commands;
Expand Down
Loading
Loading