[PATCH v1 19/27] drm/panthor: Add AW assignment tracking
From: Karunika Choo
Date: Tue Sep 22 2026 - 17:11:38 EST
Track the scheduler assignment for each access window using the default
access-window masks parsed from device properties.
Use the mapping to route request, idle, stopped, grant, stop, and close
events through the scheduler associated with the access window, and keep
schedulers disabled while they have no assigned access windows.
Signed-off-by: Karunika Choo <karunika.choo@xxxxxxx>
---
.../panthor/arbitration/panthor_arbitration.h | 15 ++
.../arbitration/panthor_arbitration_drv.c | 201 ++++++++++++++++--
.../arbitration/panthor_arbitration_sched.c | 2 +
.../arbitration/panthor_arbitration_sched.h | 3 +
4 files changed, 209 insertions(+), 12 deletions(-)
diff --git a/drivers/gpu/drm/panthor/arbitration/panthor_arbitration.h b/drivers/gpu/drm/panthor/arbitration/panthor_arbitration.h
index 149221333eb1e..e1d569158ac6c 100644
--- a/drivers/gpu/drm/panthor/arbitration/panthor_arbitration.h
+++ b/drivers/gpu/drm/panthor/arbitration/panthor_arbitration.h
@@ -7,6 +7,7 @@
#include <linux/ioport.h>
#include <linux/fwnode.h>
#include <linux/types.h>
+#include <linux/mutex.h>
struct device;
struct panthor_arbitration_sched;
@@ -26,6 +27,11 @@ struct panthor_arbitration_resource {
u32 id;
};
+struct panthor_arbitration_dt_info {
+ /** @default_aw_masks: Default per-partition AW assignments */
+ u32 default_aw_masks[AM_ARB_MAX_PC_COUNT];
+};
+
/**
* struct panthor_arbitration - Arbitration device
*/
@@ -44,6 +50,15 @@ struct panthor_arbitration {
/** @sched: Pointer to scheduler data. 1 sched per partition */
struct panthor_arbitration_sched *sched[AM_ARB_MAX_PC_COUNT];
+
+ /** @dt_info: Properties parsed from the device tree */
+ struct panthor_arbitration_dt_info dt_info;
+
+ /** @assignment_lock: Protects AW and slice assignment */
+ struct mutex assignment_lock;
+
+ /** @aw_sched_map: Tracks assignment of AW to sched instance */
+ s8 aw_sched_map[AM_ARB_MAX_AW_COUNT];
};
int panthor_arbitration_parse_resource(struct device *dev,
diff --git a/drivers/gpu/drm/panthor/arbitration/panthor_arbitration_drv.c b/drivers/gpu/drm/panthor/arbitration/panthor_arbitration_drv.c
index 723cf605e16a3..f44cd45ea342e 100644
--- a/drivers/gpu/drm/panthor/arbitration/panthor_arbitration_drv.c
+++ b/drivers/gpu/drm/panthor/arbitration/panthor_arbitration_drv.c
@@ -1,11 +1,13 @@
// SPDX-License-Identifier: GPL-2.0 or MIT
/* Copyright 2026 ARM Limited. All rights reserved. */
+#include <linux/bits.h>
#include <linux/module.h>
#include <linux/of_device.h>
#include <linux/of_platform.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
+#include <linux/property.h>
#include <linux/types.h>
#include <linux/of_address.h>
#include <linux/of.h>
@@ -17,8 +19,27 @@
#define PANTHOR_PM_AUTOSUSPEND_DELAY_MS 100
+#define DT_ASSIGNED_AW "arm,access-window-mask"
#define DT_ID "arm,id"
+static int to_assigned_sched_idx(struct panthor_arbitration *adev, u8 aw_id)
+{
+ int idx = -1;
+
+ if (aw_id >= AM_ARB_MAX_AW_COUNT) {
+ dev_err(adev->dev, "Invalid AW ID (%u)", aw_id);
+ return -EINVAL;
+ }
+
+ idx = READ_ONCE(adev->aw_sched_map[aw_id]);
+ if (idx < 0 || idx >= AM_ARB_MAX_PC_COUNT || !adev->sched[idx]) {
+ dev_warn(adev->dev, "AW%u not assigned to any sched", aw_id);
+ return -ENOENT;
+ }
+
+ return idx;
+};
+
int panthor_arbitration_parse_resource(struct device *dev,
const struct fwnode_handle *child,
struct panthor_arbitration_resource *res)
@@ -95,42 +116,186 @@ static int panthor_arbitration_runtime_resume(struct device *dev)
return 0;
}
+static u32 arbitration_get_sched_aw_mask(struct panthor_arbitration *adev,
+ u8 sched_id)
+{
+ u32 aw_mask = 0;
+
+ lockdep_assert_held(&adev->assignment_lock);
+
+ for (int i = 0; i < AM_ARB_MAX_AW_COUNT; i++) {
+ if (adev->aw_sched_map[i] == sched_id)
+ aw_mask |= BIT(i);
+ }
+
+ return aw_mask;
+}
+
+static int arbitration_dt_parse_assigned_aw(struct panthor_arbitration *adev)
+{
+ struct panthor_arbitration_dt_info *dt_info = &adev->dt_info;
+ struct device *dev = adev->dev;
+ u32 seen_mask = 0;
+ u8 seen_partitions = 0;
+ int ret;
+
+ device_for_each_named_child_node_scoped(dev, child, "partition") {
+ struct panthor_arbitration_resource res;
+
+ if (panthor_arbitration_parse_resource(dev, child, &res))
+ continue;
+
+ if (res.id >= AM_ARB_MAX_PC_COUNT) {
+ dev_warn(dev,
+ "partition@%llx: id (%u) >= %u not supported",
+ res.res.start, res.id, AM_ARB_MAX_PC_COUNT);
+ continue;
+ }
+
+ if (BIT(res.id) & seen_partitions) {
+ dev_warn(dev, "partition@%llx: id (%u) already present",
+ res.res.start, res.id);
+ continue;
+ }
+
+ seen_partitions |= BIT(res.id);
+
+ if (!fwnode_property_present(child, DT_ASSIGNED_AW))
+ continue;
+
+ ret = fwnode_property_read_u32(child, DT_ASSIGNED_AW,
+ &dt_info->default_aw_masks[res.id]);
+ if (ret)
+ continue;
+ }
+
+ for (int i = 0; i < AM_ARB_MAX_PC_COUNT; i++) {
+ u32 aw_mask = dt_info->default_aw_masks[i];
+
+ if (aw_mask & ~GENMASK(AM_ARB_MAX_AW_COUNT - 1, 0))
+ return dev_err_probe(dev, -EINVAL, "%s[%d] entry exceeds max AW count",
+ DT_ASSIGNED_AW, i);
+
+ if (aw_mask & seen_mask)
+ return dev_err_probe(dev, -EINVAL, "%s entries overlap",
+ DT_ASSIGNED_AW);
+
+ seen_mask |= aw_mask;
+ }
+
+ return 0;
+}
+
+static int arbitration_dt_parse_properties(struct panthor_arbitration *adev)
+{
+ return arbitration_dt_parse_assigned_aw(adev);
+}
+
+static int arbitration_sched_try_start(struct panthor_arbitration *adev, u8 sched_id)
+{
+ struct panthor_arbitration_sched *sched;
+ u32 aw_mask;
+
+ if (!adev->sched[sched_id])
+ return -EINVAL;
+
+ sched = adev->sched[sched_id];
+
+ scoped_guard(mutex, &adev->assignment_lock)
+ aw_mask = arbitration_get_sched_aw_mask(adev, sched_id);
+
+ if (aw_mask)
+ panthor_arbitration_sched_disable_clear(sched, ARB_SCHED_DISABLE_RESOURCE);
+ else
+ panthor_arbitration_sched_disable_set(sched, ARB_SCHED_DISABLE_RESOURCE);
+
+ return panthor_arbitration_sched_start(sched);
+}
+
+static int arbitration_resource_init(struct panthor_arbitration *adev)
+{
+ struct panthor_arbitration_dt_info *dt_info = &adev->dt_info;
+
+ for (int i = 0; i < AM_ARB_MAX_PC_COUNT; i++) {
+ unsigned long pc_aw_mask = dt_info->default_aw_masks[i];
+ u8 aw_id;
+ int ret;
+
+ if (!adev->pc[i] || !adev->sched[i])
+ continue;
+
+ scoped_guard(mutex, &adev->assignment_lock) {
+ for_each_set_bit(aw_id, &pc_aw_mask, AM_ARB_MAX_AW_COUNT)
+ WRITE_ONCE(adev->aw_sched_map[aw_id], i);
+ }
+
+ ret = arbitration_sched_try_start(adev, i);
+ if (ret)
+ return ret;
+ }
+ return 0;
+}
+
/* AW to Arbiter events */
int panthor_arbitration_on_request(struct panthor_arbitration *adev, u8 aw_id)
{
- /* TODO: AW to PC assignment */
- return panthor_arbitration_sched_on_request(adev->sched[0], aw_id);
+ int idx = to_assigned_sched_idx(adev, aw_id);
+
+ if (idx < 0)
+ return idx;
+
+ return panthor_arbitration_sched_on_request(adev->sched[idx], aw_id);
}
int panthor_arbitration_on_idle(struct panthor_arbitration *adev, u8 aw_id)
{
- /* TODO: AW to PC assignment */
- return panthor_arbitration_sched_on_idle(adev->sched[0], aw_id);
+ int idx = to_assigned_sched_idx(adev, aw_id);
+
+ if (idx < 0)
+ return idx;
+
+ return panthor_arbitration_sched_on_idle(adev->sched[idx], aw_id);
}
int panthor_arbitration_on_stopped(struct panthor_arbitration *adev, u8 aw_id)
{
- /* TODO: AW to PC assignment */
- return panthor_arbitration_sched_on_stopped(adev->sched[0], aw_id);
+ int idx = to_assigned_sched_idx(adev, aw_id);
+
+ if (idx < 0)
+ return idx;
+
+ return panthor_arbitration_sched_on_stopped(adev->sched[idx], aw_id);
}
/* Arbiter to AW events */
int panthor_arbitration_on_grant(struct panthor_arbitration *adev, u8 aw_id)
{
- /* TODO: AW to PC assignment */
- return panthor_partition_control_open_window(adev->pc[0], aw_id);
+ int idx = to_assigned_sched_idx(adev, aw_id);
+
+ if (idx < 0)
+ return idx;
+
+ return panthor_partition_control_open_window(adev->pc[idx], aw_id);
}
int panthor_arbitration_on_stop(struct panthor_arbitration *adev, u8 aw_id)
{
- /* TODO: AW to PC assignment */
- return panthor_partition_control_yield_now(adev->pc[0]);
+ int idx = to_assigned_sched_idx(adev, aw_id);
+
+ if (idx < 0)
+ return idx;
+
+ return panthor_partition_control_yield_now(adev->pc[idx]);
}
int panthor_arbitration_on_close(struct panthor_arbitration *adev, u8 aw_id)
{
- /* TODO: AW to PC assignment */
- return panthor_partition_control_close_window(adev->pc[0]);
+ int idx = to_assigned_sched_idx(adev, aw_id);
+
+ if (idx < 0)
+ return idx;
+
+ return panthor_partition_control_close_window(adev->pc[idx]);
}
static int panthor_arbitration_probe(struct platform_device *pdev)
@@ -148,8 +313,16 @@ static int panthor_arbitration_probe(struct platform_device *pdev)
adev->dev = dev;
+ mutex_init(&adev->assignment_lock);
+ for (int i = 0; i < AM_ARB_MAX_AW_COUNT; i++)
+ adev->aw_sched_map[i] = -1;
+
dev_set_drvdata(dev, adev);
+ ret = arbitration_dt_parse_properties(adev);
+ if (ret)
+ return ret;
+
ret = devm_pm_runtime_enable(dev);
if (ret)
return ret;
@@ -170,6 +343,10 @@ static int panthor_arbitration_probe(struct platform_device *pdev)
if (ret)
goto err_term_sched;
+ ret = arbitration_resource_init(adev);
+ if (ret)
+ goto err_term_sched;
+
ret = of_platform_populate(dev->of_node, NULL, NULL, dev);
if (ret)
goto err_depopulate;
diff --git a/drivers/gpu/drm/panthor/arbitration/panthor_arbitration_sched.c b/drivers/gpu/drm/panthor/arbitration/panthor_arbitration_sched.c
index 41f06af92ce0c..9e26b8f92b69a 100644
--- a/drivers/gpu/drm/panthor/arbitration/panthor_arbitration_sched.c
+++ b/drivers/gpu/drm/panthor/arbitration/panthor_arbitration_sched.c
@@ -585,6 +585,8 @@ static int arbitration_sched_init(struct panthor_arbitration *adev, int i)
INIT_KFIFO(sched->queue);
sched->active_aw_id = -1;
sched->phase = ARB_SCHED_PHASE_IDLE;
+ /* Scheduler is disabled until its resources are configured */
+ sched->disabled = BIT(ARB_SCHED_DISABLE_RESOURCE);
hrtimer_setup(&sched->grant_timer.timer,
panthor_arbitration_sched_on_grant_timeout,
diff --git a/drivers/gpu/drm/panthor/arbitration/panthor_arbitration_sched.h b/drivers/gpu/drm/panthor/arbitration/panthor_arbitration_sched.h
index f1f50316a2fff..803a405964a4e 100644
--- a/drivers/gpu/drm/panthor/arbitration/panthor_arbitration_sched.h
+++ b/drivers/gpu/drm/panthor/arbitration/panthor_arbitration_sched.h
@@ -15,6 +15,9 @@ enum panthor_arb_sched_disable_reason {
/** @ARB_SCHED_DISABLE_TERM: scheduler disabled for termination */
ARB_SCHED_DISABLE_TERM,
+
+ /** @ARB_SCHED_DISABLE_RESOURCE: No resource assigned. scheduler disabled. */
+ ARB_SCHED_DISABLE_RESOURCE,
};
int panthor_arbitration_sched_init(struct panthor_arbitration *adev);
--
2.43.0