diff --git a/drivers/gpu/drm/asahi/Kconfig b/drivers/gpu/drm/asahi/Kconfig index 0e989189206bad..a32095f36ca982 100644 --- a/drivers/gpu/drm/asahi/Kconfig +++ b/drivers/gpu/drm/asahi/Kconfig @@ -67,6 +67,7 @@ config DRM_ASAHI_G15 depends on DRM_ASAHI depends on DRM_APPLE=y || (DRM_APPLE=m && DRM_ASAHI=m) select APPLE_T6030_GPU_PMP_LINK + select APPLE_T6031_GPU_PMP_REPORT select CRYPTO_LIB_SHA256 select DRM_ASAHI_UAT_ADOPTION default n diff --git a/drivers/gpu/drm/asahi/g15/boot.rs b/drivers/gpu/drm/asahi/g15/boot.rs index 45e244531323d7..8ea95d88e90023 100644 --- a/drivers/gpu/drm/asahi/g15/boot.rs +++ b/drivers/gpu/drm/asahi/g15/boot.rs @@ -75,6 +75,7 @@ pub(crate) mod glue { 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_ready() -> core::ffi::c_int; + pub(crate) fn asahi_g15_pmp_report_t6031(enable: bool) -> 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; } diff --git a/drivers/gpu/drm/asahi/g15/glue.c b/drivers/gpu/drm/asahi/g15/glue.c index 385b9fde390538..8b09a012a4c7c5 100644 --- a/drivers/gpu/drm/asahi/g15/glue.c +++ b/drivers/gpu/drm/asahi/g15/glue.c @@ -19,6 +19,7 @@ #include #include #include +#include #include void asahi_g15_sha256(const u8 *data, size_t len, u8 *out); @@ -37,6 +38,7 @@ int asahi_g15_pmp_prepare(struct device *dev); int asahi_g15_display_prepare(struct device *dev); int asahi_g15_pmp_get(struct device *dev); int asahi_g15_pmp_ready(void); +int asahi_g15_pmp_report_t6031(bool enable); void asahi_g15_pmp_put(struct device *dev); bool asahi_g15_reserved_uat(u32 index, u64 start, u64 size); @@ -184,6 +186,12 @@ int asahi_g15_pmp_ready(void) return apple_pmp_t6030_gpu_ready(); } +/* T6031: the AGX request of the PMP report, after the firmware is running. */ +int asahi_g15_pmp_report_t6031(bool enable) +{ + return apple_pmp_report_t6031_gfx(enable); +} + /* * Give back the vote of asahi_g15_pmp_get() after a start that failed before * the firmware was touched. The full M3 stack's J514S bridge has no release: diff --git a/drivers/gpu/drm/asahi/g15/mod.rs b/drivers/gpu/drm/asahi/g15/mod.rs index 8bfa34a5892fde..f9ff5ec7f1cc54 100644 --- a/drivers/gpu/drm/asahi/g15/mod.rs +++ b/drivers/gpu/drm/asahi/g15/mod.rs @@ -566,6 +566,21 @@ impl Firmware { } = KBox::into_inner(firmware); return Err(orphan(session, objects, channels, init, events, e)); } + if cfg.platform == hw::Platform::DeviceTree { + // T6031: nothing else sets the PMP's AGX request, and without it memory stays at its + // idle floor. Set it after setup, as T6030 does; a failure keeps the GPU as before. + // SAFETY: The session is retained until reboot, and the request with it. + let ret = unsafe { boot::glue::asahi_g15_pmp_report_t6031(true) }; + if ret == 0 { + dev_info!(dev.as_ref(), "G15: PMP AGX request set (memory bandwidth scaling)\n"); + } else if ret != ENODEV.to_errno() { + dev_warn!( + dev.as_ref(), + "G15: PMP AGX request failed ({}); memory stays at its idle floor\n", + ret + ); + } + } Ok(firmware) } } diff --git a/drivers/pmdomain/apple/pmp-report.c b/drivers/pmdomain/apple/pmp-report.c index 98d96d08f8a434..0b639821843c02 100644 --- a/drivers/pmdomain/apple/pmp-report.c +++ b/drivers/pmdomain/apple/pmp-report.c @@ -1348,6 +1348,58 @@ int apple_pmp_report_set_t6030_gfx(struct device *supplier, bool enable) } EXPORT_SYMBOL_GPL(apple_pmp_report_set_t6030_gfx); +/* + * T6031 (M3 Max): the GPU is powered by its PMGR domain alone, with no RTKit + * vote, and nothing sets its PTD request, so PMP ignores the GPU's bandwidth + * demand and memory stays at its idle floor. AGX is the acknowledged + * soc-device ID 14 (record 13) of the J516C table. As on T6030, a request + * made before the firmware runs is acknowledged without enabling bandwidth + * scaling: enabling clears and sets it again. On a failed enable the request + * is cleared, which is how the GPU ran before. + */ +int apple_pmp_report_t6031_gfx(bool enable) +{ + struct apple_pmp_report *rep, *found = NULL; + struct device_node *pmp = NULL; + const u8 *table = NULL; + int len = 0, ret = -ENODEV; + + if (!IS_ENABLED(CONFIG_APPLE_T6031_GPU_PMP_REPORT) || + !of_machine_is_compatible("apple,t6031")) + return -ENODEV; + + rcu_read_lock(); + list_for_each_entry_rcu(rep, &apple_pmp_reports, list) { + if (rep->offsets != &apple_pmp_offsets_t6031) + continue; + if (found) + goto out; + found = rep; + } + if (!found) + goto out; + pmp = of_parse_phandle(found->dev->of_node, "apple,pmp", 0); + if (pmp && of_device_is_compatible(pmp, "apple,t6031-pmp-v2")) + table = of_get_property(pmp, "apple,tunable-soc-device", &len); + if (!apple_pmp_devices_valid(table, len > 0 ? len : 0, BIT_ULL(13), BIT_ULL(13), true) || + memcmp(table + 13 * PMP_SOC_DEVICE_SIZE + 116, "AGX\0\0\0\0\0", 8)) { + ret = -EINVAL; + } else if (readq(found->base + found->offsets->status) != PMP_REPORT_READY) { + ret = -EAGAIN; + } else { + ret = apple_pmp_report_set_state(found, 13, false, false); + if (!ret && enable) + ret = apple_pmp_report_set_state(found, 13, true, false); + if (ret && enable) + apple_pmp_report_set_state(found, 13, false, false); + } + of_node_put(pmp); +out: + rcu_read_unlock(); + return ret; +} +EXPORT_SYMBOL_GPL(apple_pmp_report_t6031_gfx); + static int apple_pmp_report_entry_set_state(struct generic_pm_domain *genpd, bool enable) { struct apple_pmp_report_entry *ent = genpd_to_apple_pmp_report_entry(genpd); diff --git a/drivers/soc/apple/Kconfig b/drivers/soc/apple/Kconfig index ba57db91f8860b..fd64eae1b7280c 100644 --- a/drivers/soc/apple/Kconfig +++ b/drivers/soc/apple/Kconfig @@ -257,6 +257,16 @@ config APPLE_T6030_GPU_PMP_LINK GPU power commands wait for that supplier's acknowledged display request through the existing PMP report startup and removal rules. +config APPLE_T6031_GPU_PMP_REPORT + bool + depends on APPLE_PMP_REPORT + default n + help + Set the AGX request of the T6031 (M3 Max) PMP report once the G15 + firmware runs. The T6031 GPU is powered by its PMGR domain alone, so + nothing else sets that request, and without it the PMP ignores the + GPU's bandwidth demand: memory stays at its idle floor. + config APPLE_DISPLAY_GATE_KUNIT_TEST bool "KUnit tests for the Apple display gate" if !KUNIT_ALL_TESTS depends on KUNIT=y && APPLE_T6030_DISPLAY_GATE diff --git a/include/linux/soc/apple/pmp-report.h b/include/linux/soc/apple/pmp-report.h index 1d0689f31c2039..53098fc67edb01 100644 --- a/include/linux/soc/apple/pmp-report.h +++ b/include/linux/soc/apple/pmp-report.h @@ -18,6 +18,8 @@ int apple_pmp_report_wait_supplier_ready(struct device *supplier, unsigned int t /* Caller holds the PMP owner's vote lock and keeps the supplier bound. */ int apple_pmp_report_check_t6030_gfx(struct device *supplier); int apple_pmp_report_set_t6030_gfx(struct device *supplier, bool enable); +/* After the T6031 G15 firmware is running; the request stays until reboot. */ +int apple_pmp_report_t6031_gfx(bool enable); int apple_pmp_report_wait_ready(struct device_node *entry, unsigned long timeout); int apple_pmp_report_fast_die_effort(const struct device_node *np, unsigned int lane, u8 *effort); @@ -38,6 +40,11 @@ static inline int apple_pmp_report_set_t6030_gfx(struct device *supplier, bool e return -ENODEV; } +static inline int apple_pmp_report_t6031_gfx(bool enable) +{ + return -ENODEV; +} + static inline int apple_pmp_report_wait_ready(struct device_node *entry, unsigned long timeout) {