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