162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright (C) 2013-2016 Red Hat
462306a36Sopenharmony_ci * Author: Rob Clark <robdclark@gmail.com>
562306a36Sopenharmony_ci */
662306a36Sopenharmony_ci
762306a36Sopenharmony_ci#include <linux/dma-fence.h>
862306a36Sopenharmony_ci
962306a36Sopenharmony_ci#include "msm_drv.h"
1062306a36Sopenharmony_ci#include "msm_fence.h"
1162306a36Sopenharmony_ci#include "msm_gpu.h"
1262306a36Sopenharmony_ci
1362306a36Sopenharmony_cistatic struct msm_gpu *fctx2gpu(struct msm_fence_context *fctx)
1462306a36Sopenharmony_ci{
1562306a36Sopenharmony_ci	struct msm_drm_private *priv = fctx->dev->dev_private;
1662306a36Sopenharmony_ci	return priv->gpu;
1762306a36Sopenharmony_ci}
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_cistatic enum hrtimer_restart deadline_timer(struct hrtimer *t)
2062306a36Sopenharmony_ci{
2162306a36Sopenharmony_ci	struct msm_fence_context *fctx = container_of(t,
2262306a36Sopenharmony_ci			struct msm_fence_context, deadline_timer);
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_ci	kthread_queue_work(fctx2gpu(fctx)->worker, &fctx->deadline_work);
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_ci	return HRTIMER_NORESTART;
2762306a36Sopenharmony_ci}
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_cistatic void deadline_work(struct kthread_work *work)
3062306a36Sopenharmony_ci{
3162306a36Sopenharmony_ci	struct msm_fence_context *fctx = container_of(work,
3262306a36Sopenharmony_ci			struct msm_fence_context, deadline_work);
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_ci	/* If deadline fence has already passed, nothing to do: */
3562306a36Sopenharmony_ci	if (msm_fence_completed(fctx, fctx->next_deadline_fence))
3662306a36Sopenharmony_ci		return;
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_ci	msm_devfreq_boost(fctx2gpu(fctx), 2);
3962306a36Sopenharmony_ci}
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_cistruct msm_fence_context *
4362306a36Sopenharmony_cimsm_fence_context_alloc(struct drm_device *dev, volatile uint32_t *fenceptr,
4462306a36Sopenharmony_ci		const char *name)
4562306a36Sopenharmony_ci{
4662306a36Sopenharmony_ci	struct msm_fence_context *fctx;
4762306a36Sopenharmony_ci	static int index = 0;
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_ci	fctx = kzalloc(sizeof(*fctx), GFP_KERNEL);
5062306a36Sopenharmony_ci	if (!fctx)
5162306a36Sopenharmony_ci		return ERR_PTR(-ENOMEM);
5262306a36Sopenharmony_ci
5362306a36Sopenharmony_ci	fctx->dev = dev;
5462306a36Sopenharmony_ci	strscpy(fctx->name, name, sizeof(fctx->name));
5562306a36Sopenharmony_ci	fctx->context = dma_fence_context_alloc(1);
5662306a36Sopenharmony_ci	fctx->index = index++;
5762306a36Sopenharmony_ci	fctx->fenceptr = fenceptr;
5862306a36Sopenharmony_ci	spin_lock_init(&fctx->spinlock);
5962306a36Sopenharmony_ci
6062306a36Sopenharmony_ci	/*
6162306a36Sopenharmony_ci	 * Start out close to the 32b fence rollover point, so we can
6262306a36Sopenharmony_ci	 * catch bugs with fence comparisons.
6362306a36Sopenharmony_ci	 */
6462306a36Sopenharmony_ci	fctx->last_fence = 0xffffff00;
6562306a36Sopenharmony_ci	fctx->completed_fence = fctx->last_fence;
6662306a36Sopenharmony_ci	*fctx->fenceptr = fctx->last_fence;
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_ci	hrtimer_init(&fctx->deadline_timer, CLOCK_MONOTONIC, HRTIMER_MODE_ABS);
6962306a36Sopenharmony_ci	fctx->deadline_timer.function = deadline_timer;
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_ci	kthread_init_work(&fctx->deadline_work, deadline_work);
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_ci	fctx->next_deadline = ktime_get();
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_ci	return fctx;
7662306a36Sopenharmony_ci}
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_civoid msm_fence_context_free(struct msm_fence_context *fctx)
7962306a36Sopenharmony_ci{
8062306a36Sopenharmony_ci	kfree(fctx);
8162306a36Sopenharmony_ci}
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_cibool msm_fence_completed(struct msm_fence_context *fctx, uint32_t fence)
8462306a36Sopenharmony_ci{
8562306a36Sopenharmony_ci	/*
8662306a36Sopenharmony_ci	 * Note: Check completed_fence first, as fenceptr is in a write-combine
8762306a36Sopenharmony_ci	 * mapping, so it will be more expensive to read.
8862306a36Sopenharmony_ci	 */
8962306a36Sopenharmony_ci	return (int32_t)(fctx->completed_fence - fence) >= 0 ||
9062306a36Sopenharmony_ci		(int32_t)(*fctx->fenceptr - fence) >= 0;
9162306a36Sopenharmony_ci}
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_ci/* called from irq handler and workqueue (in recover path) */
9462306a36Sopenharmony_civoid msm_update_fence(struct msm_fence_context *fctx, uint32_t fence)
9562306a36Sopenharmony_ci{
9662306a36Sopenharmony_ci	unsigned long flags;
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_ci	spin_lock_irqsave(&fctx->spinlock, flags);
9962306a36Sopenharmony_ci	if (fence_after(fence, fctx->completed_fence))
10062306a36Sopenharmony_ci		fctx->completed_fence = fence;
10162306a36Sopenharmony_ci	if (msm_fence_completed(fctx, fctx->next_deadline_fence))
10262306a36Sopenharmony_ci		hrtimer_cancel(&fctx->deadline_timer);
10362306a36Sopenharmony_ci	spin_unlock_irqrestore(&fctx->spinlock, flags);
10462306a36Sopenharmony_ci}
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_cistruct msm_fence {
10762306a36Sopenharmony_ci	struct dma_fence base;
10862306a36Sopenharmony_ci	struct msm_fence_context *fctx;
10962306a36Sopenharmony_ci};
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_cistatic inline struct msm_fence *to_msm_fence(struct dma_fence *fence)
11262306a36Sopenharmony_ci{
11362306a36Sopenharmony_ci	return container_of(fence, struct msm_fence, base);
11462306a36Sopenharmony_ci}
11562306a36Sopenharmony_ci
11662306a36Sopenharmony_cistatic const char *msm_fence_get_driver_name(struct dma_fence *fence)
11762306a36Sopenharmony_ci{
11862306a36Sopenharmony_ci	return "msm";
11962306a36Sopenharmony_ci}
12062306a36Sopenharmony_ci
12162306a36Sopenharmony_cistatic const char *msm_fence_get_timeline_name(struct dma_fence *fence)
12262306a36Sopenharmony_ci{
12362306a36Sopenharmony_ci	struct msm_fence *f = to_msm_fence(fence);
12462306a36Sopenharmony_ci	return f->fctx->name;
12562306a36Sopenharmony_ci}
12662306a36Sopenharmony_ci
12762306a36Sopenharmony_cistatic bool msm_fence_signaled(struct dma_fence *fence)
12862306a36Sopenharmony_ci{
12962306a36Sopenharmony_ci	struct msm_fence *f = to_msm_fence(fence);
13062306a36Sopenharmony_ci	return msm_fence_completed(f->fctx, f->base.seqno);
13162306a36Sopenharmony_ci}
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_cistatic void msm_fence_set_deadline(struct dma_fence *fence, ktime_t deadline)
13462306a36Sopenharmony_ci{
13562306a36Sopenharmony_ci	struct msm_fence *f = to_msm_fence(fence);
13662306a36Sopenharmony_ci	struct msm_fence_context *fctx = f->fctx;
13762306a36Sopenharmony_ci	unsigned long flags;
13862306a36Sopenharmony_ci	ktime_t now;
13962306a36Sopenharmony_ci
14062306a36Sopenharmony_ci	spin_lock_irqsave(&fctx->spinlock, flags);
14162306a36Sopenharmony_ci	now = ktime_get();
14262306a36Sopenharmony_ci
14362306a36Sopenharmony_ci	if (ktime_after(now, fctx->next_deadline) ||
14462306a36Sopenharmony_ci			ktime_before(deadline, fctx->next_deadline)) {
14562306a36Sopenharmony_ci		fctx->next_deadline = deadline;
14662306a36Sopenharmony_ci		fctx->next_deadline_fence =
14762306a36Sopenharmony_ci			max(fctx->next_deadline_fence, (uint32_t)fence->seqno);
14862306a36Sopenharmony_ci
14962306a36Sopenharmony_ci		/*
15062306a36Sopenharmony_ci		 * Set timer to trigger boost 3ms before deadline, or
15162306a36Sopenharmony_ci		 * if we are already less than 3ms before the deadline
15262306a36Sopenharmony_ci		 * schedule boost work immediately.
15362306a36Sopenharmony_ci		 */
15462306a36Sopenharmony_ci		deadline = ktime_sub(deadline, ms_to_ktime(3));
15562306a36Sopenharmony_ci
15662306a36Sopenharmony_ci		if (ktime_after(now, deadline)) {
15762306a36Sopenharmony_ci			kthread_queue_work(fctx2gpu(fctx)->worker,
15862306a36Sopenharmony_ci					&fctx->deadline_work);
15962306a36Sopenharmony_ci		} else {
16062306a36Sopenharmony_ci			hrtimer_start(&fctx->deadline_timer, deadline,
16162306a36Sopenharmony_ci					HRTIMER_MODE_ABS);
16262306a36Sopenharmony_ci		}
16362306a36Sopenharmony_ci	}
16462306a36Sopenharmony_ci
16562306a36Sopenharmony_ci	spin_unlock_irqrestore(&fctx->spinlock, flags);
16662306a36Sopenharmony_ci}
16762306a36Sopenharmony_ci
16862306a36Sopenharmony_cistatic const struct dma_fence_ops msm_fence_ops = {
16962306a36Sopenharmony_ci	.get_driver_name = msm_fence_get_driver_name,
17062306a36Sopenharmony_ci	.get_timeline_name = msm_fence_get_timeline_name,
17162306a36Sopenharmony_ci	.signaled = msm_fence_signaled,
17262306a36Sopenharmony_ci	.set_deadline = msm_fence_set_deadline,
17362306a36Sopenharmony_ci};
17462306a36Sopenharmony_ci
17562306a36Sopenharmony_cistruct dma_fence *
17662306a36Sopenharmony_cimsm_fence_alloc(void)
17762306a36Sopenharmony_ci{
17862306a36Sopenharmony_ci	struct msm_fence *f;
17962306a36Sopenharmony_ci
18062306a36Sopenharmony_ci	f = kzalloc(sizeof(*f), GFP_KERNEL);
18162306a36Sopenharmony_ci	if (!f)
18262306a36Sopenharmony_ci		return ERR_PTR(-ENOMEM);
18362306a36Sopenharmony_ci
18462306a36Sopenharmony_ci	return &f->base;
18562306a36Sopenharmony_ci}
18662306a36Sopenharmony_ci
18762306a36Sopenharmony_civoid
18862306a36Sopenharmony_cimsm_fence_init(struct dma_fence *fence, struct msm_fence_context *fctx)
18962306a36Sopenharmony_ci{
19062306a36Sopenharmony_ci	struct msm_fence *f = to_msm_fence(fence);
19162306a36Sopenharmony_ci
19262306a36Sopenharmony_ci	f->fctx = fctx;
19362306a36Sopenharmony_ci
19462306a36Sopenharmony_ci	/*
19562306a36Sopenharmony_ci	 * Until this point, the fence was just some pre-allocated memory,
19662306a36Sopenharmony_ci	 * no-one should have taken a reference to it yet.
19762306a36Sopenharmony_ci	 */
19862306a36Sopenharmony_ci	WARN_ON(kref_read(&fence->refcount));
19962306a36Sopenharmony_ci
20062306a36Sopenharmony_ci	dma_fence_init(&f->base, &msm_fence_ops, &fctx->spinlock,
20162306a36Sopenharmony_ci		       fctx->context, ++fctx->last_fence);
20262306a36Sopenharmony_ci}
203