Path: blob/master/drivers/gpu/drm/nouveau/nouveau_ramht.c
15112 views
/*1* Copyright 2010 Red Hat Inc.2*3* Permission is hereby granted, free of charge, to any person obtaining a4* copy of this software and associated documentation files (the "Software"),5* to deal in the Software without restriction, including without limitation6* the rights to use, copy, modify, merge, publish, distribute, sublicense,7* and/or sell copies of the Software, and to permit persons to whom the8* Software is furnished to do so, subject to the following conditions:9*10* The above copyright notice and this permission notice shall be included in11* all copies or substantial portions of the Software.12*13* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR14* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,15* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL16* THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR17* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,18* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR19* OTHER DEALINGS IN THE SOFTWARE.20*21* Authors: Ben Skeggs22*/2324#include "drmP.h"2526#include "nouveau_drv.h"27#include "nouveau_ramht.h"2829static u3230nouveau_ramht_hash_handle(struct nouveau_channel *chan, u32 handle)31{32struct drm_device *dev = chan->dev;33struct drm_nouveau_private *dev_priv = dev->dev_private;34struct nouveau_ramht *ramht = chan->ramht;35u32 hash = 0;36int i;3738NV_DEBUG(dev, "ch%d handle=0x%08x\n", chan->id, handle);3940for (i = 32; i > 0; i -= ramht->bits) {41hash ^= (handle & ((1 << ramht->bits) - 1));42handle >>= ramht->bits;43}4445if (dev_priv->card_type < NV_50)46hash ^= chan->id << (ramht->bits - 4);47hash <<= 3;4849NV_DEBUG(dev, "hash=0x%08x\n", hash);50return hash;51}5253static int54nouveau_ramht_entry_valid(struct drm_device *dev, struct nouveau_gpuobj *ramht,55u32 offset)56{57struct drm_nouveau_private *dev_priv = dev->dev_private;58u32 ctx = nv_ro32(ramht, offset + 4);5960if (dev_priv->card_type < NV_40)61return ((ctx & NV_RAMHT_CONTEXT_VALID) != 0);62return (ctx != 0);63}6465static int66nouveau_ramht_entry_same_channel(struct nouveau_channel *chan,67struct nouveau_gpuobj *ramht, u32 offset)68{69struct drm_nouveau_private *dev_priv = chan->dev->dev_private;70u32 ctx = nv_ro32(ramht, offset + 4);7172if (dev_priv->card_type >= NV_50)73return true;74else if (dev_priv->card_type >= NV_40)75return chan->id ==76((ctx >> NV40_RAMHT_CONTEXT_CHANNEL_SHIFT) & 0x1f);77else78return chan->id ==79((ctx >> NV_RAMHT_CONTEXT_CHANNEL_SHIFT) & 0x1f);80}8182int83nouveau_ramht_insert(struct nouveau_channel *chan, u32 handle,84struct nouveau_gpuobj *gpuobj)85{86struct drm_device *dev = chan->dev;87struct drm_nouveau_private *dev_priv = dev->dev_private;88struct nouveau_instmem_engine *instmem = &dev_priv->engine.instmem;89struct nouveau_ramht_entry *entry;90struct nouveau_gpuobj *ramht = chan->ramht->gpuobj;91unsigned long flags;92u32 ctx, co, ho;9394if (nouveau_ramht_find(chan, handle))95return -EEXIST;9697entry = kmalloc(sizeof(*entry), GFP_KERNEL);98if (!entry)99return -ENOMEM;100entry->channel = chan;101entry->gpuobj = NULL;102entry->handle = handle;103nouveau_gpuobj_ref(gpuobj, &entry->gpuobj);104105if (dev_priv->card_type < NV_40) {106ctx = NV_RAMHT_CONTEXT_VALID | (gpuobj->pinst >> 4) |107(chan->id << NV_RAMHT_CONTEXT_CHANNEL_SHIFT) |108(gpuobj->engine << NV_RAMHT_CONTEXT_ENGINE_SHIFT);109} else110if (dev_priv->card_type < NV_50) {111ctx = (gpuobj->pinst >> 4) |112(chan->id << NV40_RAMHT_CONTEXT_CHANNEL_SHIFT) |113(gpuobj->engine << NV40_RAMHT_CONTEXT_ENGINE_SHIFT);114} else {115if (gpuobj->engine == NVOBJ_ENGINE_DISPLAY) {116ctx = (gpuobj->cinst << 10) |117(chan->id << 28) |118chan->id; /* HASH_TAG */119} else {120ctx = (gpuobj->cinst >> 4) |121((gpuobj->engine <<122NV40_RAMHT_CONTEXT_ENGINE_SHIFT));123}124}125126spin_lock_irqsave(&chan->ramht->lock, flags);127list_add(&entry->head, &chan->ramht->entries);128129co = ho = nouveau_ramht_hash_handle(chan, handle);130do {131if (!nouveau_ramht_entry_valid(dev, ramht, co)) {132NV_DEBUG(dev,133"insert ch%d 0x%08x: h=0x%08x, c=0x%08x\n",134chan->id, co, handle, ctx);135nv_wo32(ramht, co + 0, handle);136nv_wo32(ramht, co + 4, ctx);137138spin_unlock_irqrestore(&chan->ramht->lock, flags);139instmem->flush(dev);140return 0;141}142NV_DEBUG(dev, "collision ch%d 0x%08x: h=0x%08x\n",143chan->id, co, nv_ro32(ramht, co));144145co += 8;146if (co >= ramht->size)147co = 0;148} while (co != ho);149150NV_ERROR(dev, "RAMHT space exhausted. ch=%d\n", chan->id);151list_del(&entry->head);152spin_unlock_irqrestore(&chan->ramht->lock, flags);153kfree(entry);154return -ENOMEM;155}156157static struct nouveau_ramht_entry *158nouveau_ramht_remove_entry(struct nouveau_channel *chan, u32 handle)159{160struct nouveau_ramht *ramht = chan ? chan->ramht : NULL;161struct nouveau_ramht_entry *entry;162unsigned long flags;163164if (!ramht)165return NULL;166167spin_lock_irqsave(&ramht->lock, flags);168list_for_each_entry(entry, &ramht->entries, head) {169if (entry->channel == chan &&170(!handle || entry->handle == handle)) {171list_del(&entry->head);172spin_unlock_irqrestore(&ramht->lock, flags);173174return entry;175}176}177spin_unlock_irqrestore(&ramht->lock, flags);178179return NULL;180}181182static void183nouveau_ramht_remove_hash(struct nouveau_channel *chan, u32 handle)184{185struct drm_device *dev = chan->dev;186struct drm_nouveau_private *dev_priv = dev->dev_private;187struct nouveau_instmem_engine *instmem = &dev_priv->engine.instmem;188struct nouveau_gpuobj *ramht = chan->ramht->gpuobj;189unsigned long flags;190u32 co, ho;191192spin_lock_irqsave(&chan->ramht->lock, flags);193co = ho = nouveau_ramht_hash_handle(chan, handle);194do {195if (nouveau_ramht_entry_valid(dev, ramht, co) &&196nouveau_ramht_entry_same_channel(chan, ramht, co) &&197(handle == nv_ro32(ramht, co))) {198NV_DEBUG(dev,199"remove ch%d 0x%08x: h=0x%08x, c=0x%08x\n",200chan->id, co, handle, nv_ro32(ramht, co + 4));201nv_wo32(ramht, co + 0, 0x00000000);202nv_wo32(ramht, co + 4, 0x00000000);203instmem->flush(dev);204goto out;205}206207co += 8;208if (co >= ramht->size)209co = 0;210} while (co != ho);211212NV_ERROR(dev, "RAMHT entry not found. ch=%d, handle=0x%08x\n",213chan->id, handle);214out:215spin_unlock_irqrestore(&chan->ramht->lock, flags);216}217218int219nouveau_ramht_remove(struct nouveau_channel *chan, u32 handle)220{221struct nouveau_ramht_entry *entry;222223entry = nouveau_ramht_remove_entry(chan, handle);224if (!entry)225return -ENOENT;226227nouveau_ramht_remove_hash(chan, entry->handle);228nouveau_gpuobj_ref(NULL, &entry->gpuobj);229kfree(entry);230return 0;231}232233struct nouveau_gpuobj *234nouveau_ramht_find(struct nouveau_channel *chan, u32 handle)235{236struct nouveau_ramht *ramht = chan->ramht;237struct nouveau_ramht_entry *entry;238struct nouveau_gpuobj *gpuobj = NULL;239unsigned long flags;240241if (unlikely(!chan->ramht))242return NULL;243244spin_lock_irqsave(&ramht->lock, flags);245list_for_each_entry(entry, &chan->ramht->entries, head) {246if (entry->channel == chan && entry->handle == handle) {247gpuobj = entry->gpuobj;248break;249}250}251spin_unlock_irqrestore(&ramht->lock, flags);252253return gpuobj;254}255256int257nouveau_ramht_new(struct drm_device *dev, struct nouveau_gpuobj *gpuobj,258struct nouveau_ramht **pramht)259{260struct nouveau_ramht *ramht;261262ramht = kzalloc(sizeof(*ramht), GFP_KERNEL);263if (!ramht)264return -ENOMEM;265266ramht->dev = dev;267kref_init(&ramht->refcount);268ramht->bits = drm_order(gpuobj->size / 8);269INIT_LIST_HEAD(&ramht->entries);270spin_lock_init(&ramht->lock);271nouveau_gpuobj_ref(gpuobj, &ramht->gpuobj);272273*pramht = ramht;274return 0;275}276277static void278nouveau_ramht_del(struct kref *ref)279{280struct nouveau_ramht *ramht =281container_of(ref, struct nouveau_ramht, refcount);282283nouveau_gpuobj_ref(NULL, &ramht->gpuobj);284kfree(ramht);285}286287void288nouveau_ramht_ref(struct nouveau_ramht *ref, struct nouveau_ramht **ptr,289struct nouveau_channel *chan)290{291struct nouveau_ramht_entry *entry;292struct nouveau_ramht *ramht;293294if (ref)295kref_get(&ref->refcount);296297ramht = *ptr;298if (ramht) {299while ((entry = nouveau_ramht_remove_entry(chan, 0))) {300nouveau_ramht_remove_hash(chan, entry->handle);301nouveau_gpuobj_ref(NULL, &entry->gpuobj);302kfree(entry);303}304305kref_put(&ramht->refcount, nouveau_ramht_del);306}307*ptr = ref;308}309310311