[RFC PATCH 15/18] drm/panthor: Add access-window support

From: Karunika Choo

Date: Thu May 28 2026 - 11:20:08 EST


Add the VM-side access-window component for v15 GPUs. The new code
handles AM message handshakes, requests GPU access from the arbiter,
waits for the window-open interrupt, and yields access on suspend.

Split panthor_hw_init into bind device and initialize gpu_info after
panthor_aw has ensured GPU access.

Signed-off-by: Karunika Choo <karunika.choo@xxxxxxx>
---
drivers/gpu/drm/panthor/Makefile | 1 +
drivers/gpu/drm/panthor/panthor_aw.c | 421 +++++++++++++++++++++++
drivers/gpu/drm/panthor/panthor_aw.h | 42 +++
drivers/gpu/drm/panthor/panthor_device.c | 15 +-
drivers/gpu/drm/panthor/panthor_device.h | 4 +
drivers/gpu/drm/panthor/panthor_hw.c | 8 +-
drivers/gpu/drm/panthor/panthor_hw.h | 2 +
7 files changed, 486 insertions(+), 7 deletions(-)
create mode 100644 drivers/gpu/drm/panthor/panthor_aw.c
create mode 100644 drivers/gpu/drm/panthor/panthor_aw.h

diff --git a/drivers/gpu/drm/panthor/Makefile b/drivers/gpu/drm/panthor/Makefile
index 5d4d0ae64952..aa48212653dc 100644
--- a/drivers/gpu/drm/panthor/Makefile
+++ b/drivers/gpu/drm/panthor/Makefile
@@ -1,6 +1,7 @@
# SPDX-License-Identifier: GPL-2.0 or MIT

panthor-y := \
+ panthor_aw.o \
panthor_devfreq.o \
panthor_device.o \
panthor_drv.o \
diff --git a/drivers/gpu/drm/panthor/panthor_aw.c b/drivers/gpu/drm/panthor/panthor_aw.c
new file mode 100644
index 000000000000..00a52175f3a3
--- /dev/null
+++ b/drivers/gpu/drm/panthor/panthor_aw.c
@@ -0,0 +1,421 @@
+// SPDX-License-Identifier: GPL-2.0 or MIT
+/* Copyright 2026 ARM Limited. All rights reserved. */
+
+#include <linux/platform_device.h>
+
+#include <drm/drm_managed.h>
+#include <drm/drm_print.h>
+#include <drm/drm_drv.h>
+
+#include "panthor_am_msg.h"
+#include "panthor_aw.h"
+#include "panthor_device.h"
+#include "panthor_fw.h"
+#include "panthor_hw.h"
+#include "panthor_sched.h"
+
+#include "panthor_trace.h"
+
+#define WINDOW_CONTROL_BASE 0x0
+#define WINDOW_CONTROL_SIZE 0x1000
+
+#define WINDOW_STATUS 0x43c
+#define WINDOW_STATUS_IRQ_WINDOW_CONTROL BIT(0)
+#define WINDOW_STATUS_IRQ_GPU_CONTROL BIT(1)
+#define WINDOW_STATUS_IRQ_GPU_POWER BIT(2)
+#define WINDOW_STATUS_IRQ_JOB_CONTROL BIT(3)
+#define WINDOW_STATUS_IRQ_MMU_CONTROL BIT(4)
+#define WINDOW_STATUS_WINDOW_OPEN BIT(31)
+
+#define WINDOW_INT_BASE 0x440
+#define WINDOW_IRQ_MESSAGE BIT(0)
+#define WINDOW_IRQ_INVALID_ACCESS BIT(1)
+#define WINDOW_IRQ_WINDOW_OPENING BIT(2)
+#define WINDOW_IRQ_WINDOW_OPENED BIT(3)
+#define WINDOW_IRQ_WINDOW_CLOSED BIT(4)
+
+#define WINDOW_MESSAGE_BASE 0x460
+
+#define WINDOW_IRQ_MASK \
+ (WINDOW_IRQ_MESSAGE | WINDOW_IRQ_WINDOW_OPENED | WINDOW_IRQ_WINDOW_CLOSED)
+
+#define PANTHOR_AW_HANDSHAKE_TIMEOUT_MS (5 * MSEC_PER_SEC)
+#define PANTHOR_AW_GPU_REQUEST_TIMEOUT_MS (5 * MSEC_PER_SEC)
+#define PANTHOR_AW_STATE_TRANSITION_TIMEOUT_MS (5 * MSEC_PER_SEC)
+
+#define panthor_aw_wait_cond(_aw, _cond, _timeout_ms) \
+({ \
+ int __ret = 0; \
+ if (!wait_event_timeout((_aw)->waitqueue, _cond, \
+ msecs_to_jiffies(_timeout_ms))) \
+ if (!(_cond)) \
+ __ret = -ETIMEDOUT; \
+ __ret; \
+})
+
+struct panthor_aw {
+ /** @ptdev: Pointer to Panthor device */
+ struct panthor_device *ptdev;
+
+ /** @iomem: CPU mapping of WINDOW_CONTROL iomem region */
+ void __iomem *iomem;
+
+ /** @irq: IRQ data */
+ struct panthor_irq irq;
+
+ /** @msg: Messaging data */
+ struct panthor_am_msg msg;
+
+ /** @waitqueue: Event wait queue. Not IRQ safe */
+ wait_queue_head_t waitqueue;
+
+ /** @state: Current state of the AW */
+ atomic_t state;
+
+ /** @wq: Scheduler workqueue to dispatch events */
+ struct workqueue_struct *wq;
+
+ /** @msg_retry_work: Work to resend messages */
+ struct work_struct msg_retry_work;
+};
+
+static bool panthor_aw_is_open(struct panthor_aw *aw)
+{
+ return gpu_read(aw->iomem, WINDOW_STATUS) & WINDOW_STATUS_WINDOW_OPEN;
+}
+
+static void panthor_aw_state_set(struct panthor_aw *aw, enum aw_states new)
+{
+ atomic_set(&aw->state, new);
+
+ wake_up_all(&aw->waitqueue);
+}
+
+static bool panthor_aw_state_try_set(struct panthor_aw *aw,
+ enum aw_states expected,
+ enum aw_states new)
+{
+ int old_state = atomic_cmpxchg(&aw->state, expected, new);
+ bool res = (old_state == expected);
+
+ if (res)
+ wake_up_all(&aw->waitqueue);
+
+ return res;
+}
+
+static int panthor_aw_state_wait(struct panthor_aw *aw, enum aw_states target, u32 timeout_ms)
+{
+ return panthor_aw_wait_cond(aw, atomic_read(&aw->state) == target, timeout_ms);
+}
+
+static int panthor_aw_state_wait_transition(struct panthor_aw *aw, u32 timeout_ms)
+{
+ return panthor_aw_wait_cond(
+ aw,
+ (atomic_read(&aw->state) == PANTHOR_AW_STATE_READY ||
+ atomic_read(&aw->state) == PANTHOR_AW_STATE_GPU_GRANTED),
+ timeout_ms);
+}
+
+static void panthor_aw_msg_retry_work(struct work_struct *work)
+{
+ struct panthor_aw *aw =
+ container_of(work, struct panthor_aw, msg_retry_work);
+ struct panthor_am_msg *msg = &aw->msg;
+ int ret;
+
+ ret = panthor_am_msg_retry(msg);
+ if (ret == -EINVAL)
+ drm_warn(&aw->ptdev->base, "Send FIFO unexpectedly empty");
+
+ if (ret == -EBUSY || ret == -EAGAIN)
+ queue_work(aw->wq, &aw->msg_retry_work);
+}
+
+static void panthor_aw_send_msg(struct panthor_aw *aw, u64 message)
+{
+ struct panthor_device *ptdev = aw->ptdev;
+ int ret;
+
+ ret = panthor_am_msg_send(&aw->msg, message);
+ if (ret == -ENOMEM)
+ drm_err(&ptdev->base, "Send FIFO is full");
+
+ if (ret == -EBUSY) {
+ drm_dbg(&ptdev->base, "Pending messages, scheduling retry work");
+ queue_work(aw->wq, &aw->msg_retry_work);
+ }
+}
+
+static void panthor_aw_handshake_handle(struct panthor_aw *aw, u64 message)
+{
+ struct panthor_device *ptdev = aw->ptdev;
+ bool acked = AM_MSG_ACK_GET(message);
+ u8 version = AM_MSG_VERSION_GET(message);
+ int ret;
+
+ ret = panthor_am_msg_version_validate(&aw->msg, version);
+ if (ret == -EOPNOTSUPP)
+ drm_warn(&ptdev->base,
+ "Msg protocol version less than minimum supported (%u < %u)",
+ version, AM_MSG_MIN_SUPPORTED_VERSION);
+
+ if (!acked) {
+ u64 reply = VM_ARB_INIT_MAKE(1, aw->msg.version);
+
+ panthor_aw_send_msg(aw, reply);
+
+ /* TODO: Handle AW restart */
+ }
+
+ panthor_aw_state_try_set(aw, PANTHOR_AW_STATE_INIT, PANTHOR_AW_STATE_READY);
+}
+
+static void panthor_aw_handshake_init(struct panthor_aw *aw)
+{
+ u64 message = VM_ARB_INIT_MAKE(0, AM_MSG_CURRENT_VERSION);
+
+ panthor_aw_send_msg(aw, message);
+}
+
+static void panthor_aw_handle_message(struct panthor_aw *aw)
+{
+ struct panthor_device *ptdev = aw->ptdev;
+ const u64 message = panthor_am_msg_read(&aw->msg);
+ const u8 msg_id = AM_MSG_ID_GET(message);
+
+ /* currently only support ARB_VM_INIT */
+ if (msg_id == ARB_VM_INIT)
+ panthor_aw_handshake_handle(aw, message);
+ else
+ drm_warn(&ptdev->base, "Unsupported msg id (0x%x)", msg_id);
+}
+
+
+static irqreturn_t panthor_aw_irq_raw_hander(int irq, void *data)
+{
+ struct panthor_irq *pirq = data;
+ struct panthor_device *ptdev = pirq->ptdev;
+ struct panthor_aw *aw = ptdev->aw;
+ u32 status;
+
+ scoped_guard(spinlock_irqsave, &pirq->mask_lock) {
+ if (atomic_read(&pirq->state) != PANTHOR_IRQ_STATE_ACTIVE)
+ return IRQ_NONE;
+ }
+
+ status = gpu_read(pirq->iomem, INT_STAT);
+ if (!status)
+ return IRQ_NONE;
+
+ if (status & WINDOW_IRQ_MESSAGE)
+ panthor_aw_handle_message(aw);
+
+ /* TODO: handle WINDOW_CLOSED*/
+
+ if ((status & WINDOW_IRQ_WINDOW_OPENED) && panthor_aw_is_open(aw))
+ panthor_aw_state_try_set(aw, PANTHOR_AW_STATE_GPU_REQUEST,
+ PANTHOR_AW_STATE_GPU_GRANTED);
+
+ gpu_write(pirq->iomem, INT_CLEAR, status);
+
+ return IRQ_HANDLED;
+}
+
+static void panthor_aw_irq_suspend(struct panthor_irq *pirq)
+{
+ scoped_guard(spinlock_irqsave, &pirq->mask_lock) {
+ atomic_set(&pirq->state, PANTHOR_IRQ_STATE_SUSPENDING);
+ gpu_write(pirq->iomem, INT_MASK, 0);
+ }
+ synchronize_irq(pirq->irq);
+ atomic_set(&pirq->state, PANTHOR_IRQ_STATE_SUSPENDED);
+}
+
+static void panthor_aw_irq_resume(struct panthor_irq *pirq)
+{
+ guard(spinlock_irqsave)(&pirq->mask_lock);
+
+ atomic_set(&pirq->state, PANTHOR_IRQ_STATE_ACTIVE);
+ gpu_write(pirq->iomem, INT_MASK, pirq->mask);
+}
+
+static int panthor_request_aw_irq(struct panthor_device *ptdev,
+ struct panthor_irq *pirq, int irq, u32 mask,
+ void __iomem *iomem)
+{
+ int ret;
+
+ pirq->ptdev = ptdev;
+ pirq->irq = irq;
+ pirq->mask = mask;
+ pirq->iomem = iomem;
+ spin_lock_init(&pirq->mask_lock);
+
+ ret = devm_request_irq(ptdev->base.dev, irq, panthor_aw_irq_raw_hander,
+ IRQF_SHARED, "panthor-aw", pirq);
+ if (ret)
+ return ret;
+
+ panthor_aw_irq_resume(pirq);
+
+ return 0;
+}
+
+static int panthor_aw_request(struct panthor_aw *aw)
+{
+ int ret;
+
+again:
+ switch(atomic_read(&aw->state)) {
+ case PANTHOR_AW_STATE_GPU_GRANTED:
+ return 0;
+
+ case PANTHOR_AW_STATE_READY:
+ break;
+
+ case PANTHOR_AW_STATE_INIT:
+ panthor_aw_handshake_init(aw);
+ ret = panthor_aw_state_wait(aw, PANTHOR_AW_STATE_READY,
+ PANTHOR_AW_HANDSHAKE_TIMEOUT_MS);
+ if (ret)
+ return ret;
+ goto again;
+ default:
+ ret = panthor_aw_state_wait_transition(
+ aw, PANTHOR_AW_STATE_TRANSITION_TIMEOUT_MS);
+ if (ret)
+ return ret;
+ goto again;
+ }
+
+ if (!panthor_aw_state_try_set(aw, PANTHOR_AW_STATE_READY,
+ PANTHOR_AW_STATE_GPU_REQUEST))
+ goto again;
+
+ panthor_aw_send_msg(aw, VM_ARB_GPU_REQUEST);
+
+ /* Wait for GPU to be granted */
+ ret = panthor_aw_state_wait(aw, PANTHOR_AW_STATE_GPU_GRANTED,
+ PANTHOR_AW_GPU_REQUEST_TIMEOUT_MS);
+ if (ret) {
+ panthor_aw_state_set(aw, PANTHOR_AW_STATE_READY);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int panthor_aw_yield(struct panthor_aw *aw)
+{
+ panthor_aw_send_msg(aw, VM_ARB_GPU_STOPPED);
+
+ return panthor_aw_state_wait(aw, PANTHOR_AW_STATE_READY,
+ PANTHOR_AW_STATE_TRANSITION_TIMEOUT_MS);
+}
+
+int panthor_aw_init(struct panthor_device *ptdev)
+{
+ struct panthor_aw *aw;
+ int irq;
+ int ret;
+
+ if (!panthor_hw_has_gpu_discover(ptdev))
+ return 0;
+
+ aw = drmm_kzalloc(&ptdev->base, sizeof(*aw), GFP_KERNEL);
+ if (!aw)
+ return -ENOMEM;
+
+ aw->wq = drmm_alloc_ordered_workqueue(&ptdev->base, "panthor-aw-wq", 0);
+ if (!aw->wq)
+ return -ENOMEM;
+
+ aw->ptdev = ptdev;
+ aw->iomem = ptdev->iomem;
+
+ INIT_WORK(&aw->msg_retry_work, panthor_aw_msg_retry_work);
+
+ init_waitqueue_head(&aw->waitqueue);
+ atomic_set(&aw->state, PANTHOR_AW_STATE_INIT);
+
+ panthor_am_msg_init(&aw->msg, aw->iomem + WINDOW_MESSAGE_BASE);
+
+ ptdev->aw = aw;
+
+ irq = platform_get_irq_byname(to_platform_device(ptdev->base.dev), "gpu");
+ if (irq < 0)
+ return irq;
+
+ ret = panthor_request_aw_irq(ptdev, &aw->irq, irq, WINDOW_IRQ_MASK,
+ aw->iomem + WINDOW_INT_BASE);
+ if (ret)
+ return ret;
+
+ ret = panthor_aw_request(aw);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+void panthor_aw_unplug(struct panthor_device *ptdev)
+{
+ struct panthor_aw *aw = ptdev->aw;
+
+ if (!aw)
+ return;
+
+ disable_work_sync(&aw->msg_retry_work);
+
+ panthor_aw_irq_suspend(&aw->irq);
+}
+
+int panthor_aw_resume(struct panthor_device *ptdev)
+{
+ struct panthor_aw *aw = ptdev->aw;
+ int ret;
+
+ if (!aw)
+ return 0;
+
+ panthor_aw_irq_resume(&aw->irq);
+
+ ret = panthor_aw_request(aw);
+ if (ret) {
+ drm_warn(&ptdev->base, "Timedout waiting for GPU to be granted");
+ goto err_out;
+ }
+
+ return 0;
+
+err_out:
+ panthor_aw_irq_suspend(&aw->irq);
+ return ret;
+}
+
+int panthor_aw_suspend(struct panthor_device *ptdev)
+{
+ struct panthor_aw *aw = ptdev->aw;
+ int ret = 0;
+
+ /* suspend hw components directly if AW is not supported */
+ if (!aw)
+ return 0;
+
+ if (atomic_read(&aw->state) == PANTHOR_AW_STATE_READY)
+ goto out_irq_suspend;
+
+ if (panthor_aw_state_try_set(aw, PANTHOR_AW_STATE_GPU_GRANTED,
+ PANTHOR_AW_STATE_GPU_STOPPED))
+ ret = panthor_aw_yield(aw);
+ else
+ ret = panthor_aw_state_wait(
+ aw, PANTHOR_AW_STATE_READY,
+ PANTHOR_AW_STATE_TRANSITION_TIMEOUT_MS);
+
+out_irq_suspend:
+ panthor_aw_irq_suspend(&aw->irq);
+ return ret;
+}
diff --git a/drivers/gpu/drm/panthor/panthor_aw.h b/drivers/gpu/drm/panthor/panthor_aw.h
new file mode 100644
index 000000000000..c2b89caa87c4
--- /dev/null
+++ b/drivers/gpu/drm/panthor/panthor_aw.h
@@ -0,0 +1,42 @@
+/* SPDX-License-Identifier: GPL-2.0 or MIT */
+/* Copyright 2026 ARM Limited. All rights reserved. */
+
+#ifndef __PANTHOR_AW_H__
+#define __PANTHOR_AW_H__
+
+#include <linux/types.h>
+
+struct panthor_device;
+
+/**
+ * enum aw_states - Enumeration of possible Access Window states
+ */
+enum aw_states {
+ /** @PANTHOR_AW_STATE_INIT: Initial state, prior to handshake. */
+ PANTHOR_AW_STATE_INIT = 0,
+
+ /** @PANTHOR_AW_STATE_READY: Handshake with Resource Group completed. */
+ PANTHOR_AW_STATE_READY,
+
+ /** @PANTHOR_AW_STATE_GPU_REQUEST: AW requested for GPU access. */
+ PANTHOR_AW_STATE_GPU_REQUEST,
+
+ /** @PANTHOR_AW_STATE_GPU_GRANTED: AW is granted GPU access. */
+ PANTHOR_AW_STATE_GPU_GRANTED,
+
+ /** @PANTHOR_AW_STATE_GPU_STOP: AW is requested to stop GPU access. */
+ PANTHOR_AW_STATE_GPU_STOP,
+
+ /** @PANTHOR_AW_STATE_GPU_STOPPED: AW has stopped GPU access. */
+ PANTHOR_AW_STATE_GPU_STOPPED,
+};
+
+int panthor_aw_init(struct panthor_device *ptdev);
+
+void panthor_aw_unplug(struct panthor_device *ptdev);
+
+int panthor_aw_resume(struct panthor_device *ptdev);
+
+int panthor_aw_suspend(struct panthor_device *ptdev);
+
+#endif
diff --git a/drivers/gpu/drm/panthor/panthor_device.c b/drivers/gpu/drm/panthor/panthor_device.c
index bd417d6ae8c0..7248e2aa9da2 100644
--- a/drivers/gpu/drm/panthor/panthor_device.c
+++ b/drivers/gpu/drm/panthor/panthor_device.c
@@ -16,6 +16,7 @@
#include <drm/drm_managed.h>
#include <drm/drm_print.h>

+#include "panthor_aw.h"
#include "panthor_devfreq.h"
#include "panthor_device.h"
#include "panthor_fw.h"
@@ -100,6 +101,7 @@ void panthor_device_unplug(struct panthor_device *ptdev)
panthor_gem_shrinker_unplug(ptdev);
panthor_gpu_unplug(ptdev);
panthor_pwr_unplug(ptdev);
+ panthor_aw_unplug(ptdev);

pm_runtime_dont_use_autosuspend(ptdev->base.dev);
pm_runtime_put_sync_suspend(ptdev->base.dev);
@@ -255,10 +257,18 @@ int panthor_device_init(struct panthor_device *ptdev)
if (ret)
goto err_rpm_put;

- ret = panthor_pwr_init(ptdev);
+ ret = panthor_aw_init(ptdev);
if (ret)
goto err_rpm_put;

+ ret = panthor_hw_info_init(ptdev);
+ if (ret)
+ goto err_unplug_aw;
+
+ ret = panthor_pwr_init(ptdev);
+ if (ret)
+ goto err_unplug_aw;
+
ret = panthor_gpu_init(ptdev);
if (ret)
goto err_unplug_pwr;
@@ -315,6 +325,9 @@ int panthor_device_init(struct panthor_device *ptdev)
err_unplug_pwr:
panthor_pwr_unplug(ptdev);

+err_unplug_aw:
+ panthor_aw_unplug(ptdev);
+
err_rpm_put:
pm_runtime_put_sync_suspend(ptdev->base.dev);
return ret;
diff --git a/drivers/gpu/drm/panthor/panthor_device.h b/drivers/gpu/drm/panthor/panthor_device.h
index 8b2a9bb426fc..a1092d02a1fe 100644
--- a/drivers/gpu/drm/panthor/panthor_device.h
+++ b/drivers/gpu/drm/panthor/panthor_device.h
@@ -18,6 +18,7 @@
#include <drm/gpu_scheduler.h>
#include <drm/panthor_drm.h>

+struct panthor_aw;
struct panthor_csf;
struct panthor_csf_ctx;
struct panthor_device;
@@ -196,6 +197,9 @@ struct panthor_device {
/** @hw: GPU-specific data. */
struct panthor_hw *hw;

+ /** @aw: AW-specific data */
+ struct panthor_aw *aw;
+
/** @pwr: Power control management data. */
struct panthor_pwr *pwr;

diff --git a/drivers/gpu/drm/panthor/panthor_hw.c b/drivers/gpu/drm/panthor/panthor_hw.c
index 52271fb9db52..aa77d1b7f21c 100644
--- a/drivers/gpu/drm/panthor/panthor_hw.c
+++ b/drivers/gpu/drm/panthor/panthor_hw.c
@@ -326,7 +326,7 @@ static int panthor_gpu_info_init(struct panthor_device *ptdev)
return overload_shader_present(ptdev);
}

-static int panthor_hw_info_init(struct panthor_device *ptdev)
+int panthor_hw_info_init(struct panthor_device *ptdev)
{
u64 l2_features = ptdev->gpu_info.l2_features;
u32 major, minor, status;
@@ -433,9 +433,5 @@ int panthor_hw_init(struct panthor_device *ptdev)
if (ret)
return ret;

- ret = panthor_hw_bind_device(ptdev);
- if (ret)
- return ret;
-
- return panthor_hw_info_init(ptdev);
+ return panthor_hw_bind_device(ptdev);
}
diff --git a/drivers/gpu/drm/panthor/panthor_hw.h b/drivers/gpu/drm/panthor/panthor_hw.h
index 1b2678ea00db..d60860102b95 100644
--- a/drivers/gpu/drm/panthor/panthor_hw.h
+++ b/drivers/gpu/drm/panthor/panthor_hw.h
@@ -60,6 +60,8 @@ struct panthor_hw {
};

int panthor_hw_init(struct panthor_device *ptdev);
+int panthor_hw_info_init(struct panthor_device *ptdev);
+
int panthor_hw_power_status_register(void);
void panthor_hw_power_status_unregister(void);

--
2.43.0