blob: 7f16697cc96ce6438896414735b5fa4d12a84a34 [file] [log] [blame]
Ben Skeggs479dcae2010-09-01 15:24:28 +10001/*
2 * Copyright 2010 Red Hat Inc.
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a
5 * copy of this software and associated documentation files (the "Software"),
6 * to deal in the Software without restriction, including without limitation
7 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 * and/or sell copies of the Software, and to permit persons to whom the
9 * Software is furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice shall be included in
12 * all copies or substantial portions of the Software.
13 *
14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
17 * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
18 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
19 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
20 * OTHER DEALINGS IN THE SOFTWARE.
21 *
22 * Authors: Ben Skeggs
23 */
24
25#include "drmP.h"
26
27#include "nouveau_drv.h"
28#include "nouveau_ramht.h"
29
Ben Skeggsdac79002010-09-01 15:24:36 +100030static u32
31nouveau_ramht_hash_handle(struct nouveau_channel *chan, u32 handle)
Ben Skeggs479dcae2010-09-01 15:24:28 +100032{
Ben Skeggse05c5a32010-09-01 15:24:35 +100033 struct drm_device *dev = chan->dev;
Ben Skeggs479dcae2010-09-01 15:24:28 +100034 struct drm_nouveau_private *dev_priv = dev->dev_private;
Ben Skeggse05c5a32010-09-01 15:24:35 +100035 struct nouveau_ramht *ramht = chan->ramht;
Ben Skeggsdac79002010-09-01 15:24:36 +100036 u32 hash = 0;
Ben Skeggs479dcae2010-09-01 15:24:28 +100037 int i;
38
Ben Skeggse05c5a32010-09-01 15:24:35 +100039 NV_DEBUG(dev, "ch%d handle=0x%08x\n", chan->id, handle);
Ben Skeggs479dcae2010-09-01 15:24:28 +100040
Ben Skeggse05c5a32010-09-01 15:24:35 +100041 for (i = 32; i > 0; i -= ramht->bits) {
42 hash ^= (handle & ((1 << ramht->bits) - 1));
43 handle >>= ramht->bits;
Ben Skeggs479dcae2010-09-01 15:24:28 +100044 }
45
46 if (dev_priv->card_type < NV_50)
Ben Skeggse05c5a32010-09-01 15:24:35 +100047 hash ^= chan->id << (ramht->bits - 4);
Ben Skeggs479dcae2010-09-01 15:24:28 +100048 hash <<= 3;
49
50 NV_DEBUG(dev, "hash=0x%08x\n", hash);
51 return hash;
52}
53
54static int
55nouveau_ramht_entry_valid(struct drm_device *dev, struct nouveau_gpuobj *ramht,
Ben Skeggsdac79002010-09-01 15:24:36 +100056 u32 offset)
Ben Skeggs479dcae2010-09-01 15:24:28 +100057{
58 struct drm_nouveau_private *dev_priv = dev->dev_private;
Ben Skeggsdac79002010-09-01 15:24:36 +100059 u32 ctx = nv_ro32(ramht, offset + 4);
Ben Skeggs479dcae2010-09-01 15:24:28 +100060
61 if (dev_priv->card_type < NV_40)
62 return ((ctx & NV_RAMHT_CONTEXT_VALID) != 0);
63 return (ctx != 0);
64}
65
Francisco Jerez3bc14b42010-09-05 06:03:07 +020066static int
67nouveau_ramht_entry_same_channel(struct nouveau_channel *chan,
68 struct nouveau_gpuobj *ramht, u32 offset)
69{
70 struct drm_nouveau_private *dev_priv = chan->dev->dev_private;
71 u32 ctx = nv_ro32(ramht, offset + 4);
72
73 if (dev_priv->card_type >= NV_50)
74 return true;
75 else if (dev_priv->card_type >= NV_40)
76 return chan->id ==
77 ((ctx >> NV40_RAMHT_CONTEXT_CHANNEL_SHIFT) & 0x1f);
78 else
79 return chan->id ==
80 ((ctx >> NV_RAMHT_CONTEXT_CHANNEL_SHIFT) & 0x1f);
81}
82
Ben Skeggs479dcae2010-09-01 15:24:28 +100083int
Ben Skeggsa8eaebc2010-09-01 15:24:31 +100084nouveau_ramht_insert(struct nouveau_channel *chan, u32 handle,
85 struct nouveau_gpuobj *gpuobj)
Ben Skeggs479dcae2010-09-01 15:24:28 +100086{
Ben Skeggsa8eaebc2010-09-01 15:24:31 +100087 struct drm_device *dev = chan->dev;
Ben Skeggs479dcae2010-09-01 15:24:28 +100088 struct drm_nouveau_private *dev_priv = dev->dev_private;
89 struct nouveau_instmem_engine *instmem = &dev_priv->engine.instmem;
Ben Skeggsa8eaebc2010-09-01 15:24:31 +100090 struct nouveau_ramht_entry *entry;
91 struct nouveau_gpuobj *ramht = chan->ramht->gpuobj;
Ben Skeggsdac79002010-09-01 15:24:36 +100092 unsigned long flags;
93 u32 ctx, co, ho;
Ben Skeggs479dcae2010-09-01 15:24:28 +100094
Ben Skeggsa8eaebc2010-09-01 15:24:31 +100095 if (nouveau_ramht_find(chan, handle))
96 return -EEXIST;
97
98 entry = kmalloc(sizeof(*entry), GFP_KERNEL);
99 if (!entry)
100 return -ENOMEM;
101 entry->channel = chan;
102 entry->gpuobj = NULL;
103 entry->handle = handle;
Ben Skeggsa8eaebc2010-09-01 15:24:31 +1000104 nouveau_gpuobj_ref(gpuobj, &entry->gpuobj);
Ben Skeggs479dcae2010-09-01 15:24:28 +1000105
106 if (dev_priv->card_type < NV_40) {
Ben Skeggsa8eaebc2010-09-01 15:24:31 +1000107 ctx = NV_RAMHT_CONTEXT_VALID | (gpuobj->cinst >> 4) |
Ben Skeggs479dcae2010-09-01 15:24:28 +1000108 (chan->id << NV_RAMHT_CONTEXT_CHANNEL_SHIFT) |
Ben Skeggsa8eaebc2010-09-01 15:24:31 +1000109 (gpuobj->engine << NV_RAMHT_CONTEXT_ENGINE_SHIFT);
Ben Skeggs479dcae2010-09-01 15:24:28 +1000110 } else
111 if (dev_priv->card_type < NV_50) {
Ben Skeggsa8eaebc2010-09-01 15:24:31 +1000112 ctx = (gpuobj->cinst >> 4) |
Ben Skeggs479dcae2010-09-01 15:24:28 +1000113 (chan->id << NV40_RAMHT_CONTEXT_CHANNEL_SHIFT) |
Ben Skeggsa8eaebc2010-09-01 15:24:31 +1000114 (gpuobj->engine << NV40_RAMHT_CONTEXT_ENGINE_SHIFT);
Ben Skeggs479dcae2010-09-01 15:24:28 +1000115 } else {
Ben Skeggsa8eaebc2010-09-01 15:24:31 +1000116 if (gpuobj->engine == NVOBJ_ENGINE_DISPLAY) {
117 ctx = (gpuobj->cinst << 10) | 2;
Ben Skeggs479dcae2010-09-01 15:24:28 +1000118 } else {
Ben Skeggsa8eaebc2010-09-01 15:24:31 +1000119 ctx = (gpuobj->cinst >> 4) |
120 ((gpuobj->engine <<
Ben Skeggs479dcae2010-09-01 15:24:28 +1000121 NV40_RAMHT_CONTEXT_ENGINE_SHIFT));
122 }
123 }
124
Ben Skeggsdac79002010-09-01 15:24:36 +1000125 spin_lock_irqsave(&chan->ramht->lock, flags);
126 list_add(&entry->head, &chan->ramht->entries);
127
Ben Skeggse05c5a32010-09-01 15:24:35 +1000128 co = ho = nouveau_ramht_hash_handle(chan, handle);
Ben Skeggs479dcae2010-09-01 15:24:28 +1000129 do {
130 if (!nouveau_ramht_entry_valid(dev, ramht, co)) {
131 NV_DEBUG(dev,
132 "insert ch%d 0x%08x: h=0x%08x, c=0x%08x\n",
Ben Skeggsa8eaebc2010-09-01 15:24:31 +1000133 chan->id, co, handle, ctx);
134 nv_wo32(ramht, co + 0, handle);
Ben Skeggsb3beb162010-09-01 15:24:29 +1000135 nv_wo32(ramht, co + 4, ctx);
Ben Skeggs479dcae2010-09-01 15:24:28 +1000136
Ben Skeggsdac79002010-09-01 15:24:36 +1000137 spin_unlock_irqrestore(&chan->ramht->lock, flags);
Ben Skeggs479dcae2010-09-01 15:24:28 +1000138 instmem->flush(dev);
139 return 0;
140 }
141 NV_DEBUG(dev, "collision ch%d 0x%08x: h=0x%08x\n",
Ben Skeggsb3beb162010-09-01 15:24:29 +1000142 chan->id, co, nv_ro32(ramht, co));
Ben Skeggs479dcae2010-09-01 15:24:28 +1000143
144 co += 8;
Ben Skeggse05c5a32010-09-01 15:24:35 +1000145 if (co >= ramht->size)
Ben Skeggs479dcae2010-09-01 15:24:28 +1000146 co = 0;
147 } while (co != ho);
148
149 NV_ERROR(dev, "RAMHT space exhausted. ch=%d\n", chan->id);
Ben Skeggsa8eaebc2010-09-01 15:24:31 +1000150 list_del(&entry->head);
Ben Skeggsdac79002010-09-01 15:24:36 +1000151 spin_unlock_irqrestore(&chan->ramht->lock, flags);
Ben Skeggsa8eaebc2010-09-01 15:24:31 +1000152 kfree(entry);
Ben Skeggs479dcae2010-09-01 15:24:28 +1000153 return -ENOMEM;
154}
155
Ben Skeggsdac79002010-09-01 15:24:36 +1000156static void
157nouveau_ramht_remove_locked(struct nouveau_channel *chan, u32 handle)
Ben Skeggs479dcae2010-09-01 15:24:28 +1000158{
Ben Skeggsa8eaebc2010-09-01 15:24:31 +1000159 struct drm_device *dev = chan->dev;
Ben Skeggs479dcae2010-09-01 15:24:28 +1000160 struct drm_nouveau_private *dev_priv = dev->dev_private;
161 struct nouveau_instmem_engine *instmem = &dev_priv->engine.instmem;
Ben Skeggsa8eaebc2010-09-01 15:24:31 +1000162 struct nouveau_gpuobj *ramht = chan->ramht->gpuobj;
163 struct nouveau_ramht_entry *entry, *tmp;
164 u32 co, ho;
Ben Skeggs479dcae2010-09-01 15:24:28 +1000165
Ben Skeggsa8eaebc2010-09-01 15:24:31 +1000166 list_for_each_entry_safe(entry, tmp, &chan->ramht->entries, head) {
167 if (entry->channel != chan || entry->handle != handle)
168 continue;
169
170 nouveau_gpuobj_ref(NULL, &entry->gpuobj);
171 list_del(&entry->head);
172 kfree(entry);
173 break;
Ben Skeggs479dcae2010-09-01 15:24:28 +1000174 }
175
Ben Skeggse05c5a32010-09-01 15:24:35 +1000176 co = ho = nouveau_ramht_hash_handle(chan, handle);
Ben Skeggs479dcae2010-09-01 15:24:28 +1000177 do {
178 if (nouveau_ramht_entry_valid(dev, ramht, co) &&
Francisco Jerez3bc14b42010-09-05 06:03:07 +0200179 nouveau_ramht_entry_same_channel(chan, ramht, co) &&
Ben Skeggsa8eaebc2010-09-01 15:24:31 +1000180 (handle == nv_ro32(ramht, co))) {
Ben Skeggs479dcae2010-09-01 15:24:28 +1000181 NV_DEBUG(dev,
182 "remove ch%d 0x%08x: h=0x%08x, c=0x%08x\n",
Ben Skeggsa8eaebc2010-09-01 15:24:31 +1000183 chan->id, co, handle, nv_ro32(ramht, co + 4));
Ben Skeggsb3beb162010-09-01 15:24:29 +1000184 nv_wo32(ramht, co + 0, 0x00000000);
185 nv_wo32(ramht, co + 4, 0x00000000);
Ben Skeggs479dcae2010-09-01 15:24:28 +1000186 instmem->flush(dev);
187 return;
188 }
189
190 co += 8;
Ben Skeggse05c5a32010-09-01 15:24:35 +1000191 if (co >= ramht->size)
Ben Skeggs479dcae2010-09-01 15:24:28 +1000192 co = 0;
193 } while (co != ho);
Ben Skeggs479dcae2010-09-01 15:24:28 +1000194
195 NV_ERROR(dev, "RAMHT entry not found. ch=%d, handle=0x%08x\n",
Ben Skeggsa8eaebc2010-09-01 15:24:31 +1000196 chan->id, handle);
197}
198
Ben Skeggsdac79002010-09-01 15:24:36 +1000199void
200nouveau_ramht_remove(struct nouveau_channel *chan, u32 handle)
201{
202 struct nouveau_ramht *ramht = chan->ramht;
203 unsigned long flags;
204
205 spin_lock_irqsave(&ramht->lock, flags);
206 nouveau_ramht_remove_locked(chan, handle);
207 spin_unlock_irqrestore(&ramht->lock, flags);
208}
209
Ben Skeggsa8eaebc2010-09-01 15:24:31 +1000210struct nouveau_gpuobj *
211nouveau_ramht_find(struct nouveau_channel *chan, u32 handle)
212{
Ben Skeggsdac79002010-09-01 15:24:36 +1000213 struct nouveau_ramht *ramht = chan->ramht;
Ben Skeggsa8eaebc2010-09-01 15:24:31 +1000214 struct nouveau_ramht_entry *entry;
Ben Skeggsdac79002010-09-01 15:24:36 +1000215 struct nouveau_gpuobj *gpuobj = NULL;
216 unsigned long flags;
Ben Skeggsa8eaebc2010-09-01 15:24:31 +1000217
Ben Skeggs29414822010-09-03 10:25:02 +1000218 if (unlikely(!chan->ramht))
219 return NULL;
220
Ben Skeggsdac79002010-09-01 15:24:36 +1000221 spin_lock_irqsave(&ramht->lock, flags);
Ben Skeggsa8eaebc2010-09-01 15:24:31 +1000222 list_for_each_entry(entry, &chan->ramht->entries, head) {
Ben Skeggsdac79002010-09-01 15:24:36 +1000223 if (entry->channel == chan && entry->handle == handle) {
224 gpuobj = entry->gpuobj;
225 break;
226 }
Ben Skeggsa8eaebc2010-09-01 15:24:31 +1000227 }
Ben Skeggsdac79002010-09-01 15:24:36 +1000228 spin_unlock_irqrestore(&ramht->lock, flags);
Ben Skeggsa8eaebc2010-09-01 15:24:31 +1000229
Ben Skeggsdac79002010-09-01 15:24:36 +1000230 return gpuobj;
Ben Skeggsa8eaebc2010-09-01 15:24:31 +1000231}
232
233int
234nouveau_ramht_new(struct drm_device *dev, struct nouveau_gpuobj *gpuobj,
235 struct nouveau_ramht **pramht)
236{
237 struct nouveau_ramht *ramht;
238
239 ramht = kzalloc(sizeof(*ramht), GFP_KERNEL);
240 if (!ramht)
241 return -ENOMEM;
242
243 ramht->dev = dev;
Ben Skeggsdac79002010-09-01 15:24:36 +1000244 kref_init(&ramht->refcount);
Ben Skeggse05c5a32010-09-01 15:24:35 +1000245 ramht->bits = drm_order(gpuobj->size / 8);
Ben Skeggsa8eaebc2010-09-01 15:24:31 +1000246 INIT_LIST_HEAD(&ramht->entries);
Ben Skeggsdac79002010-09-01 15:24:36 +1000247 spin_lock_init(&ramht->lock);
Ben Skeggsa8eaebc2010-09-01 15:24:31 +1000248 nouveau_gpuobj_ref(gpuobj, &ramht->gpuobj);
249
250 *pramht = ramht;
251 return 0;
252}
253
Ben Skeggsdac79002010-09-01 15:24:36 +1000254static void
255nouveau_ramht_del(struct kref *ref)
256{
257 struct nouveau_ramht *ramht =
258 container_of(ref, struct nouveau_ramht, refcount);
259
260 nouveau_gpuobj_ref(NULL, &ramht->gpuobj);
261 kfree(ramht);
262}
263
Ben Skeggsa8eaebc2010-09-01 15:24:31 +1000264void
265nouveau_ramht_ref(struct nouveau_ramht *ref, struct nouveau_ramht **ptr,
266 struct nouveau_channel *chan)
267{
268 struct nouveau_ramht_entry *entry, *tmp;
269 struct nouveau_ramht *ramht;
Ben Skeggsdac79002010-09-01 15:24:36 +1000270 unsigned long flags;
Ben Skeggsa8eaebc2010-09-01 15:24:31 +1000271
272 if (ref)
Ben Skeggsdac79002010-09-01 15:24:36 +1000273 kref_get(&ref->refcount);
Ben Skeggsa8eaebc2010-09-01 15:24:31 +1000274
275 ramht = *ptr;
276 if (ramht) {
Ben Skeggsdac79002010-09-01 15:24:36 +1000277 spin_lock_irqsave(&ramht->lock, flags);
Ben Skeggsa8eaebc2010-09-01 15:24:31 +1000278 list_for_each_entry_safe(entry, tmp, &ramht->entries, head) {
Ben Skeggsdac79002010-09-01 15:24:36 +1000279 if (entry->channel != chan)
280 continue;
Ben Skeggsa8eaebc2010-09-01 15:24:31 +1000281
Ben Skeggsdac79002010-09-01 15:24:36 +1000282 nouveau_ramht_remove_locked(chan, entry->handle);
Ben Skeggsa8eaebc2010-09-01 15:24:31 +1000283 }
Ben Skeggsdac79002010-09-01 15:24:36 +1000284 spin_unlock_irqrestore(&ramht->lock, flags);
285
286 kref_put(&ramht->refcount, nouveau_ramht_del);
Ben Skeggsa8eaebc2010-09-01 15:24:31 +1000287 }
288 *ptr = ref;
Ben Skeggs479dcae2010-09-01 15:24:28 +1000289}