[PATCH 4/4] gpu: host1x: Allow limiting usable channel and syncpoint ranges
From: Mikko Perttunen
Date: Mon Jun 22 2026 - 04:29:15 EST
Allow specifying usable channel, and allocatable syncpoint ranges
through the device tree. This is necessary when Host1x resources
are shared between multiple operating systems or independent hardware
units and Linux has limited access to them.
Signed-off-by: Mikko Perttunen <mperttunen@xxxxxxxxxx>
---
drivers/gpu/host1x/channel.c | 6 +++---
drivers/gpu/host1x/dev.c | 37 +++++++++++++++++++++++++++++++++++++
drivers/gpu/host1x/dev.h | 3 +++
drivers/gpu/host1x/syncpt.c | 21 +++++++++++++--------
4 files changed, 56 insertions(+), 11 deletions(-)
diff --git a/drivers/gpu/host1x/channel.c b/drivers/gpu/host1x/channel.c
index ca5d0f51cf7d..38fcdda8388a 100644
--- a/drivers/gpu/host1x/channel.c
+++ b/drivers/gpu/host1x/channel.c
@@ -121,13 +121,13 @@ EXPORT_SYMBOL(host1x_channel_put);
static struct host1x_channel *acquire_unused_channel(struct host1x *host)
{
struct host1x_channel_list *chlist = &host->channel_list;
- unsigned int max_channels = host->info->nb_channels;
unsigned int index;
mutex_lock(&chlist->lock);
- index = find_first_zero_bit(chlist->allocated_channels, max_channels);
- if (index >= max_channels) {
+ index = find_next_zero_bit(chlist->allocated_channels,
+ host->channel_end, host->channel_base);
+ if (index >= host->channel_end) {
mutex_unlock(&chlist->lock);
dev_err(host->dev, "failed to find free channel\n");
return NULL;
diff --git a/drivers/gpu/host1x/dev.c b/drivers/gpu/host1x/dev.c
index 545fa2e3f180..7103f018cb1b 100644
--- a/drivers/gpu/host1x/dev.c
+++ b/drivers/gpu/host1x/dev.c
@@ -331,6 +331,39 @@ static void host1x_setup_virtualization_tables(struct host1x *host)
}
}
+static int host1x_get_assigned_resources(struct host1x *host)
+{
+ struct device_node *np = host->dev->of_node;
+ u32 vals[2];
+ int err;
+
+ err = of_property_read_u32_array(np, "nvidia,channels", vals, 2);
+ if (err == 0) {
+ host->channel_base = vals[0];
+ host->channel_end = vals[0] + vals[1];
+ } else if (err == -EINVAL) {
+ host->channel_base = 0;
+ host->channel_end = host->info->nb_channels;
+ } else {
+ dev_err(host->dev, "invalid nvidia,channels property: %d\n", err);
+ return err;
+ }
+
+ err = of_property_read_u32_array(np, "nvidia,syncpoints", vals, 2);
+ if (err == 0) {
+ host->syncpt_base = vals[0];
+ host->syncpt_end = vals[0] + vals[1];
+ } else if (err == -EINVAL) {
+ host->syncpt_base = 0;
+ host->syncpt_end = host->info->nb_pts;
+ } else {
+ dev_err(host->dev, "invalid nvidia,syncpoints property: %d\n", err);
+ return err;
+ }
+
+ return 0;
+}
+
static bool host1x_wants_iommu(struct host1x *host1x)
{
/* Our IOMMU usage policy doesn't currently play well with GART */
@@ -602,6 +635,10 @@ static int host1x_probe(struct platform_device *pdev)
if (IS_ERR(host->clk))
return dev_err_probe(&pdev->dev, PTR_ERR(host->clk), "failed to get clock\n");
+ err = host1x_get_assigned_resources(host);
+ if (err)
+ return err;
+
err = host1x_get_resets(host);
if (err)
return err;
diff --git a/drivers/gpu/host1x/dev.h b/drivers/gpu/host1x/dev.h
index ef44618ed88a..89f1fc838a1c 100644
--- a/drivers/gpu/host1x/dev.h
+++ b/drivers/gpu/host1x/dev.h
@@ -141,6 +141,9 @@ struct host1x {
struct reset_control_bulk_data resets[2];
unsigned int nresets;
+ unsigned int syncpt_base, syncpt_end;
+ unsigned int channel_base, channel_end;
+
struct iommu_group *group;
struct iommu_domain *domain;
struct iova_domain iova;
diff --git a/drivers/gpu/host1x/syncpt.c b/drivers/gpu/host1x/syncpt.c
index acc7d82e0585..fe27a386cc0c 100644
--- a/drivers/gpu/host1x/syncpt.c
+++ b/drivers/gpu/host1x/syncpt.c
@@ -59,7 +59,7 @@ struct host1x_syncpt *host1x_syncpt_alloc(struct host1x *host,
unsigned long flags,
const char *name)
{
- struct host1x_syncpt *sp = host->syncpt;
+ struct host1x_syncpt *sp = host->syncpt + host->syncpt_base;
char *full_name;
unsigned int i;
@@ -68,10 +68,10 @@ struct host1x_syncpt *host1x_syncpt_alloc(struct host1x *host,
mutex_lock(&host->syncpt_mutex);
- for (i = 0; i < host->info->nb_pts && kref_read(&sp->ref); i++, sp++)
+ for (i = host->syncpt_base; i < host->syncpt_end && kref_read(&sp->ref); i++, sp++)
;
- if (i >= host->info->nb_pts)
+ if (i >= host->syncpt_end)
goto unlock;
if (flags & HOST1X_SYNCPT_HAS_BASE) {
@@ -138,7 +138,7 @@ void host1x_syncpt_restore(struct host1x *host)
struct host1x_syncpt *sp_base = host->syncpt;
unsigned int i;
- for (i = 0; i < host1x_syncpt_nb_pts(host); i++) {
+ for (i = host->syncpt_base; i < host->syncpt_end; i++) {
/*
* Unassign syncpt from channels for purposes of Tegra186
* syncpoint protection. This prevents any channel from
@@ -296,6 +296,9 @@ int host1x_syncpt_init(struct host1x *host)
for (i = 0; i < host->info->nb_pts; i++) {
syncpt[i].id = i;
syncpt[i].host = host;
+
+ /* Default to client managed for syncpoints not owned by us */
+ syncpt[i].client_managed = true;
}
for (i = 0; i < host->info->nb_bases; i++)
@@ -305,10 +308,12 @@ int host1x_syncpt_init(struct host1x *host)
host->syncpt = syncpt;
host->bases = bases;
- /* Allocate sync point to use for clearing waits for expired fences */
- host->nop_sp = host1x_syncpt_alloc(host, 0, "reserved-nop");
- if (!host->nop_sp)
- return -ENOMEM;
+ /* Prevent syncpoint 0 from being allocated by users */
+ if (host->syncpt_base == 0) {
+ host->nop_sp = host1x_syncpt_alloc(host, 0, "reserved-nop");
+ if (!host->nop_sp)
+ return -ENOMEM;
+ }
if (host->info->reserve_vblank_syncpts) {
kref_init(&host->syncpt[26].ref);
--
2.53.0