Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
7 changes: 4 additions & 3 deletions arch/arm64/boot/dts/apple/t6031-dcp.dtsi
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,10 @@
* interrupts and DART windows are those of the J516C ADT.
*
* Every node stays status = "disabled". The display gate enables the DCP,
* its mailbox, the two DARTs and the display subsystem only when
* apple_t6031_display.enable=1 is on the kernel command line and the boot
* loader has set apple,t6031-handoff = <1>. Compatibles are t6031-specific,
* its mailbox, the two DARTs and the display subsystem only when the boot
* loader has set apple,t6031-handoff = <1> and the native display is
* requested: by default on the J516C, with apple_t6031_display.enable=1 on
* the other boards, never with apple_t6031_display.enable=0. Compatibles are t6031-specific,

Check warning on line 15 in arch/arm64/boot/dts/apple/t6031-dcp.dtsi

View workflow job for this annotation

GitHub Actions / ci / device trees

dt-check style

[line-length] line exceeds 80 columns (93)
* so a kernel that does not know them leaves the nodes unbound.
*
* Only the T6031 board files include this file: t6034.dtsi includes
Expand Down
71 changes: 50 additions & 21 deletions drivers/gpu/drm/apple/apple_drv.c
Original file line number Diff line number Diff line change
Expand Up @@ -57,28 +57,44 @@ struct apple_drm_private {
};

/* Component readiness is later than platform probe/bind. */
static DEFINE_MUTEX(apple_t6030_display_lock);
static DEFINE_MUTEX(apple_g15_display_lock);
static struct device *apple_t6030_display;
static struct device *apple_t6031_display;

static int apple_t6030_display_publish(struct device *dev)
/* The publication slot of a G15 SoC's internal display subsystem, or NULL. */
static struct device **apple_g15_display_slot(struct device *dev)
{
if (!of_device_is_compatible(dev->of_node, "apple,t6030-display-subsystem"))
if (of_device_is_compatible(dev->of_node, "apple,t6030-display-subsystem"))
return &apple_t6030_display;
if (of_device_is_compatible(dev->of_node, "apple,t6031-display-subsystem"))
return &apple_t6031_display;
return NULL;
}

static int apple_g15_display_publish(struct device *dev)
{
struct device **slot = apple_g15_display_slot(dev);

if (!slot)
return 0;
guard(mutex)(&apple_t6030_display_lock);
if (apple_t6030_display)
return apple_t6030_display == dev ? 0 : -EBUSY;
apple_t6030_display = get_device(dev);
guard(mutex)(&apple_g15_display_lock);
if (*slot)
return *slot == dev ? 0 : -EBUSY;
*slot = get_device(dev);
return 0;
}

static void apple_t6030_display_withdraw(struct device *dev)
static void apple_g15_display_withdraw(struct device *dev)
{
struct device **slot = apple_g15_display_slot(dev);
struct device *old = NULL;

scoped_guard(mutex, &apple_t6030_display_lock) {
if (apple_t6030_display == dev) {
old = apple_t6030_display;
apple_t6030_display = NULL;
if (!slot)
return;
scoped_guard(mutex, &apple_g15_display_lock) {
if (*slot == dev) {
old = *slot;
*slot = NULL;
}
}
put_device(old);
Expand All @@ -89,22 +105,35 @@ static void apple_t6030_display_withdraw(struct device *dev)
* publish separately. This lock protects only publication and a device link;
* no display calls or firmware RPC run while it is held.
*/
int apple_drm_t6030_link_device(struct device *consumer)
static int apple_g15_link_device(struct device *consumer, const char *machine,
const char *gpu, struct device **display)
{
struct device_link *link;

if (!of_machine_is_compatible("apple,t6030") || !consumer->of_node ||
!of_device_is_compatible(consumer->of_node, "apple,agx-t6030"))
if (!of_machine_is_compatible(machine) || !consumer->of_node ||
!of_device_is_compatible(consumer->of_node, gpu))
return -ENODEV;
guard(mutex)(&apple_t6030_display_lock);
if (!apple_t6030_display)
guard(mutex)(&apple_g15_display_lock);
if (!*display)
return -EPROBE_DEFER;
link = device_link_add(consumer, apple_t6030_display,
DL_FLAG_AUTOREMOVE_CONSUMER);
link = device_link_add(consumer, *display, DL_FLAG_AUTOREMOVE_CONSUMER);
return link ? 0 : -EINVAL;
}

int apple_drm_t6030_link_device(struct device *consumer)
{
return apple_g15_link_device(consumer, "apple,t6030", "apple,agx-t6030",
&apple_t6030_display);
}
EXPORT_SYMBOL_GPL(apple_drm_t6030_link_device);

int apple_drm_t6031_link_device(struct device *consumer)
{
return apple_g15_link_device(consumer, "apple,t6031", "apple,agx-t6031",
&apple_t6031_display);
}
EXPORT_SYMBOL_GPL(apple_drm_t6031_link_device);

DEFINE_DRM_GEM_DMA_FOPS(apple_fops);

#define DART_PAGE_SIZE 16384
Expand Down Expand Up @@ -1089,7 +1118,7 @@ static int apple_drm_init(struct device *dev)
drm_client_setup_with_fourcc(&apple->drm, DRM_FORMAT_XRGB8888);

/* A subsequent successful platform probe retries deferred GPU probes. */
ret = apple_t6030_display_publish(dev);
ret = apple_g15_display_publish(dev);
if (ret) {
drm_dev_unregister(&apple->drm);
goto err_unbind;
Expand All @@ -1111,7 +1140,7 @@ static void apple_drm_uninit(struct device *dev)
{
struct apple_drm_private *apple = dev_get_drvdata(dev);

apple_t6030_display_withdraw(dev);
apple_g15_display_withdraw(dev);

apple_drm_quiesce_connectors(&apple->drm);
drm_dev_unplug(&apple->drm);
Expand Down
12 changes: 12 additions & 0 deletions drivers/gpu/drm/asahi/g15/boot.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
// SPDX-License-Identifier: GPL-2.0-only OR MIT

//! Admission and firmware start to READY.
Expand Down Expand Up @@ -72,6 +72,7 @@
fn asahi_g15_ioremap_wc(start: u64, size: usize) -> *mut core::ffi::c_void;
fn asahi_g15_dma_coherent(dev: *mut core::ffi::c_void) -> bool;
pub(crate) fn asahi_g15_pmp_prepare(dev: *mut core::ffi::c_void) -> core::ffi::c_int;
pub(crate) fn asahi_g15_display_prepare(dev: *mut core::ffi::c_void) -> core::ffi::c_int;
pub(crate) fn asahi_g15_pmp_get(dev: *mut core::ffi::c_void) -> core::ffi::c_int;
pub(crate) fn asahi_g15_pmp_put(dev: *mut core::ffi::c_void);
fn asahi_g15_reserved_uat(index: u32, start: u64, size: u64) -> bool;
Expand Down Expand Up @@ -482,6 +483,17 @@
} else if !glue::machine(cfg.machine) {
dev_err!(dev, "G15: not a {} machine\n", cfg.name);
return Err(ENODEV);
} else {
// No PMP vote here, but the first start follows the native display all the same.
// SAFETY: `dev` is the bound GPU device.
let ret = unsafe { glue::asahi_g15_display_prepare(dev.as_raw().cast()) };
if ret != 0 {
let err = Error::from_errno(ret);
if err != EPROBE_DEFER {
dev_err!(dev, "native display not admitted: {:?}\n", err);
}
return Err(err);
}
}
let adt = adt::read(dev, cfg)
.inspect_err(|e| dev_err!(dev, "GPU regions not admitted: {:?}\n", e))?;
Expand Down
28 changes: 28 additions & 0 deletions drivers/gpu/drm/asahi/g15/glue.c
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ int asahi_g15_adt_phram(u64 *start, u64 *size);
void __iomem *asahi_g15_ioremap_wc(u64 start, size_t size);
bool asahi_g15_dma_coherent(struct device *dev);
int asahi_g15_pmp_prepare(struct device *dev);
int asahi_g15_display_prepare(struct device *dev);
int asahi_g15_pmp_get(struct device *dev);
void asahi_g15_pmp_put(struct device *dev);
bool asahi_g15_reserved_uat(u32 index, u64 start, u64 size);
Expand Down Expand Up @@ -141,6 +142,33 @@ int asahi_g15_pmp_prepare(struct device *dev)
return apple_pmp_link_device(dev);
}

/*
* T6031 has no PMP vote, but its first firmware start follows the native
* display as T6030's does: defer until that display is up. The display can
* be off for this boot (no handoff, or not requested), so say once why. An
* enabled display that is not published yet is said once too: if it never
* comes up (its DCP refused or failed to bind), that line is why the GPU
* stays off.
*/
int asahi_g15_display_prepare(struct device *dev)
{
struct device_node *np;
int ret;

if (!dev->of_node || !of_device_is_compatible(dev->of_node, "apple,agx-t6031"))
return -ENODEV;
ret = apple_drm_t6031_link_device(dev);
if (ret != -EPROBE_DEFER)
return ret;
np = of_find_compatible_node(NULL, NULL, "apple,t6031-display-subsystem");
if (!np || !of_device_is_available(np))
dev_info_once(dev, "G15: waiting for the T6031 native display, which this boot did not enable (it needs the boot loader's display handoff, and apple_t6031_display.enable=1 except on the J516C)\n");
else
dev_info_once(dev, "G15: waiting for the T6031 native display to come up: the GPU firmware starts only after the display is published\n");
of_node_put(np);
return ret;
}

int asahi_g15_pmp_get(struct device *dev)
{
if (!asahi_g15_board() || !dev->of_node ||
Expand Down
16 changes: 9 additions & 7 deletions drivers/gpu/drm/asahi/g15/mod.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
// SPDX-License-Identifier: GPL-2.0-only OR MIT

//! G15 GPU support (T6030, M3 Pro: J514S, J516S; firmware RTKit 2419, the macOS 14.7-14.8 stub).
Expand All @@ -14,9 +14,9 @@
//!
//! # Plugging into the shared driver
//!
//! - [`Driver`] is a separate platform driver. T6030 has a module alias; its probe must wait
//! for the native display and PMP supplier before booting firmware. T6031 retains explicit
//! module loading. It calls [`crate::driver::AsahiPlatformDriver::probe_g15`].
//! - [`Driver`] is a separate platform driver with a module alias. Its probe must wait for
//! the native display (T6030 and T6031) and the PMP supplier (T6030) before booting
//! firmware. It calls [`crate::driver::AsahiPlatformDriver::probe_g15`].
//! - [`Gpu`] implements [`crate::gpu::Gpu`]: GET_PARAMS identity, VM creation (`new_vm`),
//! queues (`new_queue`, see [`queue`]) and timestamp buffers.
//! - User VMs are shared-layer [`mmu::Vm`]s on a [`mmu::Uat::new_adopted`] UAT with tracked
Expand Down Expand Up @@ -152,14 +152,16 @@
),
]);

// T6030 probe admission requires the native display and PMP supplier before
// firmware starts. T6031 retains explicit loading until it has an equivalent
// display-order admission gate.
// Probe admission requires the native display before firmware starts (T6030
// also its PMP supplier), so both may be loaded at device creation.
kernel::of_device_table!(
AUTOLOAD_OF_TABLE,
MODULE_OF_TABLE_G15,
(),
[(of::DeviceId::new(c_str!("apple,agx-t6030")), ())]
[
(of::DeviceId::new(c_str!("apple,agx-t6030")), ()),
(of::DeviceId::new(c_str!("apple,agx-t6031")), ()),
]
);

/// The G15 platform adapter: the shared DRM registration probed through
Expand Down
20 changes: 13 additions & 7 deletions drivers/soc/apple/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -228,9 +228,14 @@ config APPLE_T6030_DISPLAY_GATE
nomodeset.

It also serves T6031 (M3 Max) device trees with apple,t6031-handoff,
in the same way, but only for a boot with
apple_t6031_display.enable=1 on the kernel command line, and only
with the PMP values the boot loader passes.
in the same way and only with the PMP values the boot loader passes.
There the native display is on by default on the J516C, unless
apple_t6031_display.enable=0; the other T6031 boards need
apple_t6031_display.enable=1 on the kernel command line. The T6031
GPU driver starts its firmware only after this native display is
up, so apple_t6031_display.enable=0, or a native display that does
not come up, also keeps the GPU off for that boot. It is not a
display-only switch.

Say 'y' if you want the native display on M3 Pro MacBook Pros.

Expand Down Expand Up @@ -258,10 +263,11 @@ config APPLE_DISPLAY_GATE_KUNIT_TEST
default KUNIT_ALL_TESTS
help
Runs the display gate on test device trees with the T6030, T6031
and T8122 display nodes, without Apple hardware. On T6031: off by
default, enabled only by apple_t6031_display.enable=1, only with the
boot loader's handoff marker and this Mac's PMP values, and with
nothing changed when a check fails. On T6030: on by default, as
and T8122 display nodes, without Apple hardware. On T6031: on by
default only on the default board (the J516C), otherwise only with
apple_t6031_display.enable=1, off with apple_t6031_display.enable=0,
only with the boot loader's handoff marker and this Mac's PMP values,
and with nothing changed when a check fails. On T6030: on by default, as
before, whatever the T6031 option says. On T8122: on by default,
and nothing changed when the handoff or the device tree's own PMP
topology does not check out.
Expand Down
Loading
Loading