Path: blob/master/drivers/gpu/drm/nouveau/nouveau_fence.c
15112 views
/*1* Copyright (C) 2007 Ben Skeggs.2* All Rights Reserved.3*4* Permission is hereby granted, free of charge, to any person obtaining5* a copy of this software and associated documentation files (the6* "Software"), to deal in the Software without restriction, including7* without limitation the rights to use, copy, modify, merge, publish,8* distribute, sublicense, and/or sell copies of the Software, and to9* permit persons to whom the Software is furnished to do so, subject to10* the following conditions:11*12* The above copyright notice and this permission notice (including the13* next paragraph) shall be included in all copies or substantial14* portions of the Software.15*16* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,17* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF18* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.19* IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE20* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION21* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION22* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.23*24*/2526#include "drmP.h"27#include "drm.h"2829#include <linux/ktime.h>30#include <linux/hrtimer.h>3132#include "nouveau_drv.h"33#include "nouveau_ramht.h"34#include "nouveau_dma.h"3536#define USE_REFCNT(dev) (nouveau_private(dev)->chipset >= 0x10)37#define USE_SEMA(dev) (nouveau_private(dev)->chipset >= 0x17)3839struct nouveau_fence {40struct nouveau_channel *channel;41struct kref refcount;42struct list_head entry;4344uint32_t sequence;45bool signalled;4647void (*work)(void *priv, bool signalled);48void *priv;49};5051struct nouveau_semaphore {52struct kref ref;53struct drm_device *dev;54struct drm_mm_node *mem;55};5657static inline struct nouveau_fence *58nouveau_fence(void *sync_obj)59{60return (struct nouveau_fence *)sync_obj;61}6263static void64nouveau_fence_del(struct kref *ref)65{66struct nouveau_fence *fence =67container_of(ref, struct nouveau_fence, refcount);6869nouveau_channel_ref(NULL, &fence->channel);70kfree(fence);71}7273void74nouveau_fence_update(struct nouveau_channel *chan)75{76struct drm_device *dev = chan->dev;77struct nouveau_fence *tmp, *fence;78uint32_t sequence;7980spin_lock(&chan->fence.lock);8182/* Fetch the last sequence if the channel is still up and running */83if (likely(!list_empty(&chan->fence.pending))) {84if (USE_REFCNT(dev))85sequence = nvchan_rd32(chan, 0x48);86else87sequence = atomic_read(&chan->fence.last_sequence_irq);8889if (chan->fence.sequence_ack == sequence)90goto out;91chan->fence.sequence_ack = sequence;92}9394list_for_each_entry_safe(fence, tmp, &chan->fence.pending, entry) {95sequence = fence->sequence;96fence->signalled = true;97list_del(&fence->entry);9899if (unlikely(fence->work))100fence->work(fence->priv, true);101102kref_put(&fence->refcount, nouveau_fence_del);103104if (sequence == chan->fence.sequence_ack)105break;106}107out:108spin_unlock(&chan->fence.lock);109}110111int112nouveau_fence_new(struct nouveau_channel *chan, struct nouveau_fence **pfence,113bool emit)114{115struct nouveau_fence *fence;116int ret = 0;117118fence = kzalloc(sizeof(*fence), GFP_KERNEL);119if (!fence)120return -ENOMEM;121kref_init(&fence->refcount);122nouveau_channel_ref(chan, &fence->channel);123124if (emit)125ret = nouveau_fence_emit(fence);126127if (ret)128nouveau_fence_unref(&fence);129*pfence = fence;130return ret;131}132133struct nouveau_channel *134nouveau_fence_channel(struct nouveau_fence *fence)135{136return fence ? nouveau_channel_get_unlocked(fence->channel) : NULL;137}138139int140nouveau_fence_emit(struct nouveau_fence *fence)141{142struct nouveau_channel *chan = fence->channel;143struct drm_device *dev = chan->dev;144struct drm_nouveau_private *dev_priv = dev->dev_private;145int ret;146147ret = RING_SPACE(chan, 2);148if (ret)149return ret;150151if (unlikely(chan->fence.sequence == chan->fence.sequence_ack - 1)) {152nouveau_fence_update(chan);153154BUG_ON(chan->fence.sequence ==155chan->fence.sequence_ack - 1);156}157158fence->sequence = ++chan->fence.sequence;159160kref_get(&fence->refcount);161spin_lock(&chan->fence.lock);162list_add_tail(&fence->entry, &chan->fence.pending);163spin_unlock(&chan->fence.lock);164165if (USE_REFCNT(dev)) {166if (dev_priv->card_type < NV_C0)167BEGIN_RING(chan, NvSubSw, 0x0050, 1);168else169BEGIN_NVC0(chan, 2, NvSubM2MF, 0x0050, 1);170} else {171BEGIN_RING(chan, NvSubSw, 0x0150, 1);172}173OUT_RING (chan, fence->sequence);174FIRE_RING(chan);175176return 0;177}178179void180nouveau_fence_work(struct nouveau_fence *fence,181void (*work)(void *priv, bool signalled),182void *priv)183{184BUG_ON(fence->work);185186spin_lock(&fence->channel->fence.lock);187188if (fence->signalled) {189work(priv, true);190} else {191fence->work = work;192fence->priv = priv;193}194195spin_unlock(&fence->channel->fence.lock);196}197198void199__nouveau_fence_unref(void **sync_obj)200{201struct nouveau_fence *fence = nouveau_fence(*sync_obj);202203if (fence)204kref_put(&fence->refcount, nouveau_fence_del);205*sync_obj = NULL;206}207208void *209__nouveau_fence_ref(void *sync_obj)210{211struct nouveau_fence *fence = nouveau_fence(sync_obj);212213kref_get(&fence->refcount);214return sync_obj;215}216217bool218__nouveau_fence_signalled(void *sync_obj, void *sync_arg)219{220struct nouveau_fence *fence = nouveau_fence(sync_obj);221struct nouveau_channel *chan = fence->channel;222223if (fence->signalled)224return true;225226nouveau_fence_update(chan);227return fence->signalled;228}229230int231__nouveau_fence_wait(void *sync_obj, void *sync_arg, bool lazy, bool intr)232{233unsigned long timeout = jiffies + (3 * DRM_HZ);234unsigned long sleep_time = NSEC_PER_MSEC / 1000;235ktime_t t;236int ret = 0;237238while (1) {239if (__nouveau_fence_signalled(sync_obj, sync_arg))240break;241242if (time_after_eq(jiffies, timeout)) {243ret = -EBUSY;244break;245}246247__set_current_state(intr ? TASK_INTERRUPTIBLE248: TASK_UNINTERRUPTIBLE);249if (lazy) {250t = ktime_set(0, sleep_time);251schedule_hrtimeout(&t, HRTIMER_MODE_REL);252sleep_time *= 2;253if (sleep_time > NSEC_PER_MSEC)254sleep_time = NSEC_PER_MSEC;255}256257if (intr && signal_pending(current)) {258ret = -ERESTARTSYS;259break;260}261}262263__set_current_state(TASK_RUNNING);264265return ret;266}267268static struct nouveau_semaphore *269semaphore_alloc(struct drm_device *dev)270{271struct drm_nouveau_private *dev_priv = dev->dev_private;272struct nouveau_semaphore *sema;273int size = (dev_priv->chipset < 0x84) ? 4 : 16;274int ret, i;275276if (!USE_SEMA(dev))277return NULL;278279sema = kmalloc(sizeof(*sema), GFP_KERNEL);280if (!sema)281goto fail;282283ret = drm_mm_pre_get(&dev_priv->fence.heap);284if (ret)285goto fail;286287spin_lock(&dev_priv->fence.lock);288sema->mem = drm_mm_search_free(&dev_priv->fence.heap, size, 0, 0);289if (sema->mem)290sema->mem = drm_mm_get_block_atomic(sema->mem, size, 0);291spin_unlock(&dev_priv->fence.lock);292293if (!sema->mem)294goto fail;295296kref_init(&sema->ref);297sema->dev = dev;298for (i = sema->mem->start; i < sema->mem->start + size; i += 4)299nouveau_bo_wr32(dev_priv->fence.bo, i / 4, 0);300301return sema;302fail:303kfree(sema);304return NULL;305}306307static void308semaphore_free(struct kref *ref)309{310struct nouveau_semaphore *sema =311container_of(ref, struct nouveau_semaphore, ref);312struct drm_nouveau_private *dev_priv = sema->dev->dev_private;313314spin_lock(&dev_priv->fence.lock);315drm_mm_put_block(sema->mem);316spin_unlock(&dev_priv->fence.lock);317318kfree(sema);319}320321static void322semaphore_work(void *priv, bool signalled)323{324struct nouveau_semaphore *sema = priv;325struct drm_nouveau_private *dev_priv = sema->dev->dev_private;326327if (unlikely(!signalled))328nouveau_bo_wr32(dev_priv->fence.bo, sema->mem->start / 4, 1);329330kref_put(&sema->ref, semaphore_free);331}332333static int334semaphore_acquire(struct nouveau_channel *chan, struct nouveau_semaphore *sema)335{336struct drm_nouveau_private *dev_priv = chan->dev->dev_private;337struct nouveau_fence *fence = NULL;338int ret;339340if (dev_priv->chipset < 0x84) {341ret = RING_SPACE(chan, 4);342if (ret)343return ret;344345BEGIN_RING(chan, NvSubSw, NV_SW_DMA_SEMAPHORE, 3);346OUT_RING (chan, NvSema);347OUT_RING (chan, sema->mem->start);348OUT_RING (chan, 1);349} else350if (dev_priv->chipset < 0xc0) {351struct nouveau_vma *vma = &dev_priv->fence.bo->vma;352u64 offset = vma->offset + sema->mem->start;353354ret = RING_SPACE(chan, 7);355if (ret)356return ret;357358BEGIN_RING(chan, NvSubSw, NV_SW_DMA_SEMAPHORE, 1);359OUT_RING (chan, chan->vram_handle);360BEGIN_RING(chan, NvSubSw, 0x0010, 4);361OUT_RING (chan, upper_32_bits(offset));362OUT_RING (chan, lower_32_bits(offset));363OUT_RING (chan, 1);364OUT_RING (chan, 1); /* ACQUIRE_EQ */365} else {366struct nouveau_vma *vma = &dev_priv->fence.bo->vma;367u64 offset = vma->offset + sema->mem->start;368369ret = RING_SPACE(chan, 5);370if (ret)371return ret;372373BEGIN_NVC0(chan, 2, NvSubM2MF, 0x0010, 4);374OUT_RING (chan, upper_32_bits(offset));375OUT_RING (chan, lower_32_bits(offset));376OUT_RING (chan, 1);377OUT_RING (chan, 0x1001); /* ACQUIRE_EQ */378}379380/* Delay semaphore destruction until its work is done */381ret = nouveau_fence_new(chan, &fence, true);382if (ret)383return ret;384385kref_get(&sema->ref);386nouveau_fence_work(fence, semaphore_work, sema);387nouveau_fence_unref(&fence);388return 0;389}390391static int392semaphore_release(struct nouveau_channel *chan, struct nouveau_semaphore *sema)393{394struct drm_nouveau_private *dev_priv = chan->dev->dev_private;395struct nouveau_fence *fence = NULL;396int ret;397398if (dev_priv->chipset < 0x84) {399ret = RING_SPACE(chan, 5);400if (ret)401return ret;402403BEGIN_RING(chan, NvSubSw, NV_SW_DMA_SEMAPHORE, 2);404OUT_RING (chan, NvSema);405OUT_RING (chan, sema->mem->start);406BEGIN_RING(chan, NvSubSw, NV_SW_SEMAPHORE_RELEASE, 1);407OUT_RING (chan, 1);408} else409if (dev_priv->chipset < 0xc0) {410struct nouveau_vma *vma = &dev_priv->fence.bo->vma;411u64 offset = vma->offset + sema->mem->start;412413ret = RING_SPACE(chan, 7);414if (ret)415return ret;416417BEGIN_RING(chan, NvSubSw, NV_SW_DMA_SEMAPHORE, 1);418OUT_RING (chan, chan->vram_handle);419BEGIN_RING(chan, NvSubSw, 0x0010, 4);420OUT_RING (chan, upper_32_bits(offset));421OUT_RING (chan, lower_32_bits(offset));422OUT_RING (chan, 1);423OUT_RING (chan, 2); /* RELEASE */424} else {425struct nouveau_vma *vma = &dev_priv->fence.bo->vma;426u64 offset = vma->offset + sema->mem->start;427428ret = RING_SPACE(chan, 5);429if (ret)430return ret;431432BEGIN_NVC0(chan, 2, NvSubM2MF, 0x0010, 4);433OUT_RING (chan, upper_32_bits(offset));434OUT_RING (chan, lower_32_bits(offset));435OUT_RING (chan, 1);436OUT_RING (chan, 0x1002); /* RELEASE */437}438439/* Delay semaphore destruction until its work is done */440ret = nouveau_fence_new(chan, &fence, true);441if (ret)442return ret;443444kref_get(&sema->ref);445nouveau_fence_work(fence, semaphore_work, sema);446nouveau_fence_unref(&fence);447return 0;448}449450int451nouveau_fence_sync(struct nouveau_fence *fence,452struct nouveau_channel *wchan)453{454struct nouveau_channel *chan = nouveau_fence_channel(fence);455struct drm_device *dev = wchan->dev;456struct nouveau_semaphore *sema;457int ret = 0;458459if (likely(!chan || chan == wchan ||460nouveau_fence_signalled(fence)))461goto out;462463sema = semaphore_alloc(dev);464if (!sema) {465/* Early card or broken userspace, fall back to466* software sync. */467ret = nouveau_fence_wait(fence, true, false);468goto out;469}470471/* try to take chan's mutex, if we can't take it right away472* we have to fallback to software sync to prevent locking473* order issues474*/475if (!mutex_trylock(&chan->mutex)) {476ret = nouveau_fence_wait(fence, true, false);477goto out_unref;478}479480/* Make wchan wait until it gets signalled */481ret = semaphore_acquire(wchan, sema);482if (ret)483goto out_unlock;484485/* Signal the semaphore from chan */486ret = semaphore_release(chan, sema);487488out_unlock:489mutex_unlock(&chan->mutex);490out_unref:491kref_put(&sema->ref, semaphore_free);492out:493if (chan)494nouveau_channel_put_unlocked(&chan);495return ret;496}497498int499__nouveau_fence_flush(void *sync_obj, void *sync_arg)500{501return 0;502}503504int505nouveau_fence_channel_init(struct nouveau_channel *chan)506{507struct drm_device *dev = chan->dev;508struct drm_nouveau_private *dev_priv = dev->dev_private;509struct nouveau_gpuobj *obj = NULL;510int ret;511512if (dev_priv->card_type < NV_C0) {513/* Create an NV_SW object for various sync purposes */514ret = nouveau_gpuobj_gr_new(chan, NvSw, NV_SW);515if (ret)516return ret;517518ret = RING_SPACE(chan, 2);519if (ret)520return ret;521522BEGIN_RING(chan, NvSubSw, 0, 1);523OUT_RING (chan, NvSw);524FIRE_RING (chan);525}526527/* Setup area of memory shared between all channels for x-chan sync */528if (USE_SEMA(dev) && dev_priv->chipset < 0x84) {529struct ttm_mem_reg *mem = &dev_priv->fence.bo->bo.mem;530531ret = nouveau_gpuobj_dma_new(chan, NV_CLASS_DMA_IN_MEMORY,532mem->start << PAGE_SHIFT,533mem->size, NV_MEM_ACCESS_RW,534NV_MEM_TARGET_VRAM, &obj);535if (ret)536return ret;537538ret = nouveau_ramht_insert(chan, NvSema, obj);539nouveau_gpuobj_ref(NULL, &obj);540if (ret)541return ret;542}543544INIT_LIST_HEAD(&chan->fence.pending);545spin_lock_init(&chan->fence.lock);546atomic_set(&chan->fence.last_sequence_irq, 0);547return 0;548}549550void551nouveau_fence_channel_fini(struct nouveau_channel *chan)552{553struct nouveau_fence *tmp, *fence;554555spin_lock(&chan->fence.lock);556557list_for_each_entry_safe(fence, tmp, &chan->fence.pending, entry) {558fence->signalled = true;559list_del(&fence->entry);560561if (unlikely(fence->work))562fence->work(fence->priv, false);563564kref_put(&fence->refcount, nouveau_fence_del);565}566567spin_unlock(&chan->fence.lock);568}569570int571nouveau_fence_init(struct drm_device *dev)572{573struct drm_nouveau_private *dev_priv = dev->dev_private;574int size = (dev_priv->chipset < 0x84) ? 4096 : 16384;575int ret;576577/* Create a shared VRAM heap for cross-channel sync. */578if (USE_SEMA(dev)) {579ret = nouveau_bo_new(dev, NULL, size, 0, TTM_PL_FLAG_VRAM,5800, 0, &dev_priv->fence.bo);581if (ret)582return ret;583584ret = nouveau_bo_pin(dev_priv->fence.bo, TTM_PL_FLAG_VRAM);585if (ret)586goto fail;587588ret = nouveau_bo_map(dev_priv->fence.bo);589if (ret)590goto fail;591592ret = drm_mm_init(&dev_priv->fence.heap, 0,593dev_priv->fence.bo->bo.mem.size);594if (ret)595goto fail;596597spin_lock_init(&dev_priv->fence.lock);598}599600return 0;601fail:602nouveau_bo_unmap(dev_priv->fence.bo);603nouveau_bo_ref(NULL, &dev_priv->fence.bo);604return ret;605}606607void608nouveau_fence_fini(struct drm_device *dev)609{610struct drm_nouveau_private *dev_priv = dev->dev_private;611612if (USE_SEMA(dev)) {613drm_mm_takedown(&dev_priv->fence.heap);614nouveau_bo_unmap(dev_priv->fence.bo);615nouveau_bo_unpin(dev_priv->fence.bo);616nouveau_bo_ref(NULL, &dev_priv->fence.bo);617}618}619620621