162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Syncpoint dma_fence implementation 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (c) 2020, NVIDIA Corporation. 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#include <linux/dma-fence.h> 962306a36Sopenharmony_ci#include <linux/file.h> 1062306a36Sopenharmony_ci#include <linux/fs.h> 1162306a36Sopenharmony_ci#include <linux/slab.h> 1262306a36Sopenharmony_ci#include <linux/sync_file.h> 1362306a36Sopenharmony_ci 1462306a36Sopenharmony_ci#include "fence.h" 1562306a36Sopenharmony_ci#include "intr.h" 1662306a36Sopenharmony_ci#include "syncpt.h" 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_cistatic const char *host1x_syncpt_fence_get_driver_name(struct dma_fence *f) 1962306a36Sopenharmony_ci{ 2062306a36Sopenharmony_ci return "host1x"; 2162306a36Sopenharmony_ci} 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_cistatic const char *host1x_syncpt_fence_get_timeline_name(struct dma_fence *f) 2462306a36Sopenharmony_ci{ 2562306a36Sopenharmony_ci return "syncpoint"; 2662306a36Sopenharmony_ci} 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_cistatic struct host1x_syncpt_fence *to_host1x_fence(struct dma_fence *f) 2962306a36Sopenharmony_ci{ 3062306a36Sopenharmony_ci return container_of(f, struct host1x_syncpt_fence, base); 3162306a36Sopenharmony_ci} 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_cistatic bool host1x_syncpt_fence_enable_signaling(struct dma_fence *f) 3462306a36Sopenharmony_ci{ 3562306a36Sopenharmony_ci struct host1x_syncpt_fence *sf = to_host1x_fence(f); 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ci if (host1x_syncpt_is_expired(sf->sp, sf->threshold)) 3862306a36Sopenharmony_ci return false; 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_ci /* Reference for interrupt path. */ 4162306a36Sopenharmony_ci dma_fence_get(f); 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_ci /* 4462306a36Sopenharmony_ci * The dma_fence framework requires the fence driver to keep a 4562306a36Sopenharmony_ci * reference to any fences for which 'enable_signaling' has been 4662306a36Sopenharmony_ci * called (and that have not been signalled). 4762306a36Sopenharmony_ci * 4862306a36Sopenharmony_ci * We cannot currently always guarantee that all fences get signalled 4962306a36Sopenharmony_ci * or cancelled. As such, for such situations, set up a timeout, so 5062306a36Sopenharmony_ci * that long-lasting fences will get reaped eventually. 5162306a36Sopenharmony_ci */ 5262306a36Sopenharmony_ci if (sf->timeout) { 5362306a36Sopenharmony_ci /* Reference for timeout path. */ 5462306a36Sopenharmony_ci dma_fence_get(f); 5562306a36Sopenharmony_ci schedule_delayed_work(&sf->timeout_work, msecs_to_jiffies(30000)); 5662306a36Sopenharmony_ci } 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci host1x_intr_add_fence_locked(sf->sp->host, sf); 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_ci /* 6162306a36Sopenharmony_ci * The fence may get signalled at any time after the above call, 6262306a36Sopenharmony_ci * so we need to initialize all state used by signalling 6362306a36Sopenharmony_ci * before it. 6462306a36Sopenharmony_ci */ 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_ci return true; 6762306a36Sopenharmony_ci} 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_cistatic const struct dma_fence_ops host1x_syncpt_fence_ops = { 7062306a36Sopenharmony_ci .get_driver_name = host1x_syncpt_fence_get_driver_name, 7162306a36Sopenharmony_ci .get_timeline_name = host1x_syncpt_fence_get_timeline_name, 7262306a36Sopenharmony_ci .enable_signaling = host1x_syncpt_fence_enable_signaling, 7362306a36Sopenharmony_ci}; 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_civoid host1x_fence_signal(struct host1x_syncpt_fence *f) 7662306a36Sopenharmony_ci{ 7762306a36Sopenharmony_ci if (atomic_xchg(&f->signaling, 1)) { 7862306a36Sopenharmony_ci /* 7962306a36Sopenharmony_ci * Already on timeout path, but we removed the fence before 8062306a36Sopenharmony_ci * timeout path could, so drop interrupt path reference. 8162306a36Sopenharmony_ci */ 8262306a36Sopenharmony_ci dma_fence_put(&f->base); 8362306a36Sopenharmony_ci return; 8462306a36Sopenharmony_ci } 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci if (f->timeout && cancel_delayed_work(&f->timeout_work)) { 8762306a36Sopenharmony_ci /* 8862306a36Sopenharmony_ci * We know that the timeout path will not be entered. 8962306a36Sopenharmony_ci * Safe to drop the timeout path's reference now. 9062306a36Sopenharmony_ci */ 9162306a36Sopenharmony_ci dma_fence_put(&f->base); 9262306a36Sopenharmony_ci } 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ci dma_fence_signal_locked(&f->base); 9562306a36Sopenharmony_ci dma_fence_put(&f->base); 9662306a36Sopenharmony_ci} 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_cistatic void do_fence_timeout(struct work_struct *work) 9962306a36Sopenharmony_ci{ 10062306a36Sopenharmony_ci struct delayed_work *dwork = (struct delayed_work *)work; 10162306a36Sopenharmony_ci struct host1x_syncpt_fence *f = 10262306a36Sopenharmony_ci container_of(dwork, struct host1x_syncpt_fence, timeout_work); 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ci if (atomic_xchg(&f->signaling, 1)) { 10562306a36Sopenharmony_ci /* Already on interrupt path, drop timeout path reference if any. */ 10662306a36Sopenharmony_ci if (f->timeout) 10762306a36Sopenharmony_ci dma_fence_put(&f->base); 10862306a36Sopenharmony_ci return; 10962306a36Sopenharmony_ci } 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_ci if (host1x_intr_remove_fence(f->sp->host, f)) { 11262306a36Sopenharmony_ci /* 11362306a36Sopenharmony_ci * Managed to remove fence from queue, so it's safe to drop 11462306a36Sopenharmony_ci * the interrupt path's reference. 11562306a36Sopenharmony_ci */ 11662306a36Sopenharmony_ci dma_fence_put(&f->base); 11762306a36Sopenharmony_ci } 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_ci dma_fence_set_error(&f->base, -ETIMEDOUT); 12062306a36Sopenharmony_ci dma_fence_signal(&f->base); 12162306a36Sopenharmony_ci if (f->timeout) 12262306a36Sopenharmony_ci dma_fence_put(&f->base); 12362306a36Sopenharmony_ci} 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_cistruct dma_fence *host1x_fence_create(struct host1x_syncpt *sp, u32 threshold, 12662306a36Sopenharmony_ci bool timeout) 12762306a36Sopenharmony_ci{ 12862306a36Sopenharmony_ci struct host1x_syncpt_fence *fence; 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ci fence = kzalloc(sizeof(*fence), GFP_KERNEL); 13162306a36Sopenharmony_ci if (!fence) 13262306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_ci fence->sp = sp; 13562306a36Sopenharmony_ci fence->threshold = threshold; 13662306a36Sopenharmony_ci fence->timeout = timeout; 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_ci dma_fence_init(&fence->base, &host1x_syncpt_fence_ops, &sp->fences.lock, 13962306a36Sopenharmony_ci dma_fence_context_alloc(1), 0); 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ci INIT_DELAYED_WORK(&fence->timeout_work, do_fence_timeout); 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_ci return &fence->base; 14462306a36Sopenharmony_ci} 14562306a36Sopenharmony_ciEXPORT_SYMBOL(host1x_fence_create); 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_civoid host1x_fence_cancel(struct dma_fence *f) 14862306a36Sopenharmony_ci{ 14962306a36Sopenharmony_ci struct host1x_syncpt_fence *sf = to_host1x_fence(f); 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ci schedule_delayed_work(&sf->timeout_work, 0); 15262306a36Sopenharmony_ci flush_delayed_work(&sf->timeout_work); 15362306a36Sopenharmony_ci} 15462306a36Sopenharmony_ciEXPORT_SYMBOL(host1x_fence_cancel); 155