diff --git a/drivers/gpu/host1x-emu/Makefile b/drivers/gpu/host1x-emu/Makefile index 351df0db..1f02358e 100644 --- a/drivers/gpu/host1x-emu/Makefile +++ b/drivers/gpu/host1x-emu/Makefile @@ -9,6 +9,9 @@ endif #Enable Tasklet based fence scanning in SyncpointIncr #ccflags-y += -DHOST1X_EMU_SYNC_INC_TASKLET +# Enable HRTimer based fence scanning +ccflags-y += -DHOST1X_EMU_HRTIMER_FENCE_SCAN + ifeq ($(NV_BUILD_CONFIGURATION_EXPOSING_T26X), 1) LINUXINCLUDE += -I$(srctree.nvidia-oot)/drivers/gpu/host1x-emu/include # Enable for verification in VDK. Below allow Emulated Syncpoint driver diff --git a/drivers/gpu/host1x-emu/dev.c b/drivers/gpu/host1x-emu/dev.c index 053a2a82..686da4aa 100644 --- a/drivers/gpu/host1x-emu/dev.c +++ b/drivers/gpu/host1x-emu/dev.c @@ -1,7 +1,5 @@ -/* - * SPDX-FileCopyrightText: Copyright (c) 2024 NVIDIA CORPORATION & AFFILIATES. All rights reserved. - * SPDX-License-Identifier: GPL-2.0-only - */ +// SPDX-License-Identifier: GPL-2.0-only +// SPDX-FileCopyrightText: Copyright (c) 2024-2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved. #include #include #include @@ -93,6 +91,19 @@ static int host1x_get_assigned_resources(struct host1x *host) host->polling_intrval = HOST1X_POOL_MSEC_PERIOD; } +#ifdef HOST1X_EMU_HRTIMER_FENCE_SCAN + err = of_property_read_u32_array(np, "nvidia,hr-polling-interval", vals, 1); + if (err == 0) { + host->hr_polling_intrval = vals[0]; + if (host->hr_polling_intrval < 50) + host->hr_polling_intrval = HRTIMER_TIMEOUT_NSEC; + } else { + host->hr_polling_intrval = HRTIMER_TIMEOUT_NSEC; + } + pr_info("Host1x-EMU: HRTimer Resolution :%unsec\n", MONOTONIC_RES_NSEC); + pr_info("Host1x-EMU: HRTimer Polling Interval :%unsec\n", host->hr_polling_intrval); +#endif + #ifdef HOST1X_EMU_HYPERVISOR err = of_property_read_u32_array(np, "nvidia,syncpoints-mem", vals, 4); if (err == 0) { diff --git a/drivers/gpu/host1x-emu/dev.h b/drivers/gpu/host1x-emu/dev.h index 6b702203..f6ca638c 100644 --- a/drivers/gpu/host1x-emu/dev.h +++ b/drivers/gpu/host1x-emu/dev.h @@ -1,7 +1,5 @@ -/* - * SPDX-FileCopyrightText: Copyright (c) 2024 NVIDIA CORPORATION & AFFILIATES. All rights reserved. - * SPDX-License-Identifier: GPL-2.0-only - */ +// SPDX-License-Identifier: GPL-2.0-only +// SPDX-FileCopyrightText: Copyright (c) 2024-2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved. #ifndef HOST1X_DEV_H #define HOST1X_DEV_H @@ -59,6 +57,9 @@ struct host1x { unsigned int syncpt_base; unsigned int syncpt_count; unsigned int polling_intrval; +#ifdef HOST1X_EMU_HRTIMER_FENCE_SCAN + unsigned int hr_polling_intrval; +#endif #ifdef HOST1X_EMU_HYPERVISOR void __iomem *syncpt_va_apt; /* syncpoint apperture mapped in kernel space */ #else diff --git a/drivers/gpu/host1x-emu/poll.c b/drivers/gpu/host1x-emu/poll.c index 13519cf5..59023cd1 100644 --- a/drivers/gpu/host1x-emu/poll.c +++ b/drivers/gpu/host1x-emu/poll.c @@ -4,46 +4,88 @@ #include "fence.h" #include "poll.h" -static void host1x_poll_add_fence_to_list(struct host1x_fence_list *list, - struct host1x_syncpt_fence *fence) +#ifdef HOST1X_EMU_HRTIMER_FENCE_SCAN + +struct host1x *hr_timer_host; +static struct hrtimer emu_hr_timer; + +//Timer Callback function. This will be called when timer expires +static enum hrtimer_restart timer_callback(struct hrtimer *timer) { - struct host1x_syncpt_fence *fence_in_list; + int id; + unsigned long irqflags; + struct host1x_syncpt *sp; + struct host1x_syncpt *tmp_spt; + struct host1x *host = hr_timer_host; + ktime_t ts = ktime_get(); - list_for_each_entry_reverse(fence_in_list, &list->list, list) { - if ((s32)(fence_in_list->threshold - fence->threshold) <= 0) { - /* Fence in list is before us, we can insert here */ - list_add(&fence->list, &fence_in_list->list); - return; - } - } + for (id = 0; id < host->num_pools + 1; ++id) { + struct host1x_syncpt_pool *pool = &host->pools[id]; - /* Add as first in list */ - list_add(&fence->list, &list->list); + list_for_each_entry_safe(sp, tmp_spt, &pool->syncpt_list.list, list) { + struct host1x_syncpt_fence *fence, *tmp; + unsigned int value; + + value = host1x_syncpt_load(sp); + + spin_lock_irqsave(&sp->fences.lock, irqflags); + list_for_each_entry_safe(fence, tmp, &sp->fences.list, list) { + if (((value - fence->threshold) & 0x80000000U) != 0U) { + /* Fence is not yet expired, we are done */ + break; + } + + list_del_init(&fence->list); + host1x_fence_signal(fence, ts); + } + spin_unlock_irqrestore(&sp->fences.lock, irqflags); + } + } + hrtimer_forward_now(timer, ktime_set(HRTIMER_TIMEOUT_SEC, host->hr_polling_intrval)); + return HRTIMER_RESTART; +} +#endif + +static void host1x_poll_add_fence_to_list(struct host1x_fence_list *list, + struct host1x_syncpt_fence *fence) +{ + struct host1x_syncpt_fence *fence_in_list; + + list_for_each_entry_reverse(fence_in_list, &list->list, list) { + if ((s32)(fence_in_list->threshold - fence->threshold) <= 0) { + /* Fence in list is before us, we can insert here */ + list_add(&fence->list, &fence_in_list->list); + return; + } + } + + /* Add as first in list */ + list_add(&fence->list, &list->list); } void host1x_poll_add_fence_locked(struct host1x *host, struct host1x_syncpt_fence *fence) { - struct host1x_fence_list *fence_list = &fence->sp->fences; + struct host1x_fence_list *fence_list = &fence->sp->fences; - INIT_LIST_HEAD(&fence->list); - host1x_poll_add_fence_to_list(fence_list, fence); + INIT_LIST_HEAD(&fence->list); + host1x_poll_add_fence_to_list(fence_list, fence); } bool host1x_poll_remove_fence(struct host1x *host, struct host1x_syncpt_fence *fence) { - struct host1x_fence_list *fence_list = &fence->sp->fences; - unsigned long irqflags; + struct host1x_fence_list *fence_list = &fence->sp->fences; + unsigned long irqflags; - spin_lock_irqsave(&fence_list->lock, irqflags); + spin_lock_irqsave(&fence_list->lock, irqflags); - if (list_empty(&fence->list)) { - spin_unlock_irqrestore(&fence_list->lock, irqflags); - return false; - } - list_del_init(&fence->list); + if (list_empty(&fence->list)) { + spin_unlock_irqrestore(&fence_list->lock, irqflags); + return false; + } + list_del_init(&fence->list); - spin_unlock_irqrestore(&fence_list->lock, irqflags); - return true; + spin_unlock_irqrestore(&fence_list->lock, irqflags); + return true; } static void host1x_pool_timeout_handler(struct work_struct *work) @@ -68,38 +110,38 @@ static void host1x_pool_timeout_handler(struct work_struct *work) int host1x_poll_init(struct host1x *host) { - unsigned int id; + unsigned int id; - for (id = 0; id < host->num_pools; ++id) { - struct host1x_syncpt_pool *syncpt_pool = &host->pools[id]; + for (id = 0; id < host->num_pools; ++id) { + struct host1x_syncpt_pool *syncpt_pool = &host->pools[id]; - syncpt_pool->host = host; - spin_lock_init(&syncpt_pool->syncpt_list.lock); - INIT_LIST_HEAD(&syncpt_pool->syncpt_list.list); + syncpt_pool->host = host; + spin_lock_init(&syncpt_pool->syncpt_list.lock); + INIT_LIST_HEAD(&syncpt_pool->syncpt_list.list); - INIT_DELAYED_WORK(&syncpt_pool->pool_work, host1x_pool_timeout_handler); - } + INIT_DELAYED_WORK(&syncpt_pool->pool_work, host1x_pool_timeout_handler); + } - /* Initialize RO-Pool*/ - host->pools[host->ro_pool_id].host = host; - spin_lock_init(&host->pools[host->ro_pool_id].syncpt_list.lock); - INIT_LIST_HEAD(&host->pools[host->ro_pool_id].syncpt_list.list); - INIT_DELAYED_WORK(&host->pools[host->ro_pool_id].pool_work, - host1x_pool_timeout_handler); + /* Initialize RO-Pool*/ + host->pools[host->ro_pool_id].host = host; + spin_lock_init(&host->pools[host->ro_pool_id].syncpt_list.lock); + INIT_LIST_HEAD(&host->pools[host->ro_pool_id].syncpt_list.list); + INIT_DELAYED_WORK(&host->pools[host->ro_pool_id].pool_work, + host1x_pool_timeout_handler); - for (id = 0; id < host1x_syncpt_nb_pts(host); ++id) { - struct host1x_syncpt *syncpt = &host->syncpt[id]; - struct host1x_syncpt_pool *pool = syncpt->pool; + for (id = 0; id < host1x_syncpt_nb_pts(host); ++id) { + struct host1x_syncpt *syncpt = &host->syncpt[id]; + struct host1x_syncpt_pool *pool = syncpt->pool; - spin_lock_init(&syncpt->fences.lock); - INIT_LIST_HEAD(&syncpt->fences.list); - INIT_LIST_HEAD(&syncpt->list); + spin_lock_init(&syncpt->fences.lock); + INIT_LIST_HEAD(&syncpt->fences.list); + INIT_LIST_HEAD(&syncpt->list); /* Add syncpoint to pool list*/ list_add_tail(&syncpt->list, &pool->syncpt_list.list); } - return 0; + return 0; } void host1x_poll_irq_check_syncpt_fence(struct host1x_syncpt *sp) @@ -125,29 +167,42 @@ void host1x_poll_irq_check_syncpt_fence(struct host1x_syncpt *sp) void host1x_poll_start(struct host1x *host) { - int id; + int id; +#ifdef HOST1X_EMU_HRTIMER_FENCE_SCAN + ktime_t ktime; - /*Loop till "host->num_pools + 1" to include Ro-Pool*/ - for (id = 0; id < host->num_pools + 1; ++id) { - struct host1x_syncpt_pool *syncpt_pool = &host->pools[id]; + hr_timer_host = host; + ktime = ktime_set(HRTIMER_TIMEOUT_SEC, host->hr_polling_intrval); + hrtimer_init(&emu_hr_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); + emu_hr_timer.function = &timer_callback; + hrtimer_start(&emu_hr_timer, ktime, HRTIMER_MODE_REL); +#endif - schedule_delayed_work(&syncpt_pool->pool_work, msecs_to_jiffies(host->polling_intrval)); - } + /*Loop till "host->num_pools + 1" to include Ro-Pool*/ + for (id = 0; id < host->num_pools + 1; ++id) { + struct host1x_syncpt_pool *syncpt_pool = &host->pools[id]; + + schedule_delayed_work(&syncpt_pool->pool_work, msecs_to_jiffies(host->polling_intrval)); + } } void host1x_poll_stop(struct host1x *host) { - int id; + int id; - /*Loop till "host->num_pools + 1" to include Ro-Pool*/ - for (id = 0; id < host->num_pools + 1; ++id) { - struct host1x_syncpt_pool *syncpt_pool = &host->pools[id]; +#ifdef HOST1X_EMU_HRTIMER_FENCE_SCAN + hrtimer_cancel(&emu_hr_timer); +#endif - //Schedule delayed work immediately - schedule_delayed_work(&syncpt_pool->pool_work, 0); - //Wait for schedule work to complete - flush_delayed_work(&syncpt_pool->pool_work); - //Cancel the work as it reschedule itself - cancel_delayed_work(&syncpt_pool->pool_work); - } + /*Loop till "host->num_pools + 1" to include Ro-Pool*/ + for (id = 0; id < host->num_pools + 1; ++id) { + struct host1x_syncpt_pool *syncpt_pool = &host->pools[id]; + + //Schedule delayed work immediately + schedule_delayed_work(&syncpt_pool->pool_work, 0); + //Wait for schedule work to complete + flush_delayed_work(&syncpt_pool->pool_work); + //Cancel the work as it reschedule itself + cancel_delayed_work(&syncpt_pool->pool_work); + } } diff --git a/drivers/gpu/host1x-emu/poll.h b/drivers/gpu/host1x-emu/poll.h index dfa1c1bd..d1af8a85 100644 --- a/drivers/gpu/host1x-emu/poll.h +++ b/drivers/gpu/host1x-emu/poll.h @@ -7,6 +7,11 @@ #include #include +#ifdef HOST1X_EMU_HRTIMER_FENCE_SCAN +#define HRTIMER_TIMEOUT_NSEC 200000U /*200usec*/ +#define HRTIMER_TIMEOUT_SEC 0U /*0sec*/ +#endif /*HOST1X_EMU_HRTIMER_FENCE_SCAN*/ + struct host1x; struct host1x_syncpt; struct host1x_syncpt_fence;