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