Commit | Line | Data |
---|---|---|
479dcaea BS |
1 | /* |
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 | ||
760285e7 | 25 | #include <drm/drmP.h> |
479dcaea BS |
26 | |
27 | #include "nouveau_drv.h" | |
28 | #include "nouveau_ramht.h" | |
29 | ||
dac79008 BS |
30 | static u32 |
31 | nouveau_ramht_hash_handle(struct nouveau_channel *chan, u32 handle) | |
479dcaea | 32 | { |
e05c5a31 | 33 | struct drm_device *dev = chan->dev; |
479dcaea | 34 | struct drm_nouveau_private *dev_priv = dev->dev_private; |
e05c5a31 | 35 | struct nouveau_ramht *ramht = chan->ramht; |
dac79008 | 36 | u32 hash = 0; |
479dcaea BS |
37 | int i; |
38 | ||
e05c5a31 | 39 | NV_DEBUG(dev, "ch%d handle=0x%08x\n", chan->id, handle); |
479dcaea | 40 | |
e05c5a31 BS |
41 | for (i = 32; i > 0; i -= ramht->bits) { |
42 | hash ^= (handle & ((1 << ramht->bits) - 1)); | |
43 | handle >>= ramht->bits; | |
479dcaea BS |
44 | } |
45 | ||
46 | if (dev_priv->card_type < NV_50) | |
e05c5a31 | 47 | hash ^= chan->id << (ramht->bits - 4); |
479dcaea BS |
48 | hash <<= 3; |
49 | ||
50 | NV_DEBUG(dev, "hash=0x%08x\n", hash); | |
51 | return hash; | |
52 | } | |
53 | ||
54 | static int | |
55 | nouveau_ramht_entry_valid(struct drm_device *dev, struct nouveau_gpuobj *ramht, | |
dac79008 | 56 | u32 offset) |
479dcaea BS |
57 | { |
58 | struct drm_nouveau_private *dev_priv = dev->dev_private; | |
dac79008 | 59 | u32 ctx = nv_ro32(ramht, offset + 4); |
479dcaea BS |
60 | |
61 | if (dev_priv->card_type < NV_40) | |
62 | return ((ctx & NV_RAMHT_CONTEXT_VALID) != 0); | |
63 | return (ctx != 0); | |
64 | } | |
65 | ||
3bc14b4d FJ |
66 | static int |
67 | nouveau_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 | ||
479dcaea | 83 | int |
a8eaebc6 BS |
84 | nouveau_ramht_insert(struct nouveau_channel *chan, u32 handle, |
85 | struct nouveau_gpuobj *gpuobj) | |
479dcaea | 86 | { |
a8eaebc6 | 87 | struct drm_device *dev = chan->dev; |
479dcaea BS |
88 | struct drm_nouveau_private *dev_priv = dev->dev_private; |
89 | struct nouveau_instmem_engine *instmem = &dev_priv->engine.instmem; | |
a8eaebc6 BS |
90 | struct nouveau_ramht_entry *entry; |
91 | struct nouveau_gpuobj *ramht = chan->ramht->gpuobj; | |
dac79008 BS |
92 | unsigned long flags; |
93 | u32 ctx, co, ho; | |
479dcaea | 94 | |
a8eaebc6 BS |
95 | 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; | |
a8eaebc6 | 104 | nouveau_gpuobj_ref(gpuobj, &entry->gpuobj); |
479dcaea BS |
105 | |
106 | if (dev_priv->card_type < NV_40) { | |
ca130c22 | 107 | ctx = NV_RAMHT_CONTEXT_VALID | (gpuobj->pinst >> 4) | |
479dcaea | 108 | (chan->id << NV_RAMHT_CONTEXT_CHANNEL_SHIFT) | |
a8eaebc6 | 109 | (gpuobj->engine << NV_RAMHT_CONTEXT_ENGINE_SHIFT); |
479dcaea BS |
110 | } else |
111 | if (dev_priv->card_type < NV_50) { | |
ca130c22 | 112 | ctx = (gpuobj->pinst >> 4) | |
479dcaea | 113 | (chan->id << NV40_RAMHT_CONTEXT_CHANNEL_SHIFT) | |
a8eaebc6 | 114 | (gpuobj->engine << NV40_RAMHT_CONTEXT_ENGINE_SHIFT); |
479dcaea | 115 | } else { |
a8eaebc6 | 116 | if (gpuobj->engine == NVOBJ_ENGINE_DISPLAY) { |
961b6e68 BS |
117 | ctx = (gpuobj->cinst << 10) | |
118 | (chan->id << 28) | | |
119 | chan->id; /* HASH_TAG */ | |
479dcaea | 120 | } else { |
a8eaebc6 BS |
121 | ctx = (gpuobj->cinst >> 4) | |
122 | ((gpuobj->engine << | |
479dcaea BS |
123 | NV40_RAMHT_CONTEXT_ENGINE_SHIFT)); |
124 | } | |
125 | } | |
126 | ||
dac79008 BS |
127 | spin_lock_irqsave(&chan->ramht->lock, flags); |
128 | list_add(&entry->head, &chan->ramht->entries); | |
129 | ||
e05c5a31 | 130 | co = ho = nouveau_ramht_hash_handle(chan, handle); |
479dcaea BS |
131 | do { |
132 | if (!nouveau_ramht_entry_valid(dev, ramht, co)) { | |
133 | NV_DEBUG(dev, | |
134 | "insert ch%d 0x%08x: h=0x%08x, c=0x%08x\n", | |
a8eaebc6 BS |
135 | chan->id, co, handle, ctx); |
136 | nv_wo32(ramht, co + 0, handle); | |
b3beb167 | 137 | nv_wo32(ramht, co + 4, ctx); |
479dcaea | 138 | |
dac79008 | 139 | spin_unlock_irqrestore(&chan->ramht->lock, flags); |
479dcaea BS |
140 | instmem->flush(dev); |
141 | return 0; | |
142 | } | |
143 | NV_DEBUG(dev, "collision ch%d 0x%08x: h=0x%08x\n", | |
b3beb167 | 144 | chan->id, co, nv_ro32(ramht, co)); |
479dcaea BS |
145 | |
146 | co += 8; | |
e05c5a31 | 147 | if (co >= ramht->size) |
479dcaea BS |
148 | co = 0; |
149 | } while (co != ho); | |
150 | ||
151 | NV_ERROR(dev, "RAMHT space exhausted. ch=%d\n", chan->id); | |
a8eaebc6 | 152 | list_del(&entry->head); |
dac79008 | 153 | spin_unlock_irqrestore(&chan->ramht->lock, flags); |
a8eaebc6 | 154 | kfree(entry); |
479dcaea BS |
155 | return -ENOMEM; |
156 | } | |
157 | ||
7bb94d26 FJ |
158 | static struct nouveau_ramht_entry * |
159 | nouveau_ramht_remove_entry(struct nouveau_channel *chan, u32 handle) | |
160 | { | |
161 | struct nouveau_ramht *ramht = chan ? chan->ramht : NULL; | |
162 | struct nouveau_ramht_entry *entry; | |
163 | unsigned long flags; | |
164 | ||
165 | if (!ramht) | |
166 | return NULL; | |
167 | ||
168 | spin_lock_irqsave(&ramht->lock, flags); | |
169 | list_for_each_entry(entry, &ramht->entries, head) { | |
170 | if (entry->channel == chan && | |
171 | (!handle || entry->handle == handle)) { | |
172 | list_del(&entry->head); | |
173 | spin_unlock_irqrestore(&ramht->lock, flags); | |
174 | ||
175 | return entry; | |
176 | } | |
177 | } | |
178 | spin_unlock_irqrestore(&ramht->lock, flags); | |
179 | ||
180 | return NULL; | |
181 | } | |
182 | ||
dac79008 | 183 | static void |
7bb94d26 | 184 | nouveau_ramht_remove_hash(struct nouveau_channel *chan, u32 handle) |
479dcaea | 185 | { |
a8eaebc6 | 186 | struct drm_device *dev = chan->dev; |
479dcaea BS |
187 | struct drm_nouveau_private *dev_priv = dev->dev_private; |
188 | struct nouveau_instmem_engine *instmem = &dev_priv->engine.instmem; | |
a8eaebc6 | 189 | struct nouveau_gpuobj *ramht = chan->ramht->gpuobj; |
7bb94d26 | 190 | unsigned long flags; |
a8eaebc6 BS |
191 | u32 co, ho; |
192 | ||
7bb94d26 | 193 | spin_lock_irqsave(&chan->ramht->lock, flags); |
e05c5a31 | 194 | co = ho = nouveau_ramht_hash_handle(chan, handle); |
479dcaea BS |
195 | do { |
196 | if (nouveau_ramht_entry_valid(dev, ramht, co) && | |
3bc14b4d | 197 | nouveau_ramht_entry_same_channel(chan, ramht, co) && |
a8eaebc6 | 198 | (handle == nv_ro32(ramht, co))) { |
479dcaea BS |
199 | NV_DEBUG(dev, |
200 | "remove ch%d 0x%08x: h=0x%08x, c=0x%08x\n", | |
a8eaebc6 | 201 | chan->id, co, handle, nv_ro32(ramht, co + 4)); |
b3beb167 BS |
202 | nv_wo32(ramht, co + 0, 0x00000000); |
203 | nv_wo32(ramht, co + 4, 0x00000000); | |
479dcaea | 204 | instmem->flush(dev); |
7bb94d26 | 205 | goto out; |
479dcaea BS |
206 | } |
207 | ||
208 | co += 8; | |
e05c5a31 | 209 | if (co >= ramht->size) |
479dcaea BS |
210 | co = 0; |
211 | } while (co != ho); | |
479dcaea BS |
212 | |
213 | NV_ERROR(dev, "RAMHT entry not found. ch=%d, handle=0x%08x\n", | |
a8eaebc6 | 214 | chan->id, handle); |
7bb94d26 FJ |
215 | out: |
216 | spin_unlock_irqrestore(&chan->ramht->lock, flags); | |
a8eaebc6 BS |
217 | } |
218 | ||
18a16a76 | 219 | int |
dac79008 BS |
220 | nouveau_ramht_remove(struct nouveau_channel *chan, u32 handle) |
221 | { | |
7bb94d26 | 222 | struct nouveau_ramht_entry *entry; |
dac79008 | 223 | |
7bb94d26 FJ |
224 | entry = nouveau_ramht_remove_entry(chan, handle); |
225 | if (!entry) | |
18a16a76 | 226 | return -ENOENT; |
7bb94d26 FJ |
227 | |
228 | nouveau_ramht_remove_hash(chan, entry->handle); | |
229 | nouveau_gpuobj_ref(NULL, &entry->gpuobj); | |
230 | kfree(entry); | |
18a16a76 | 231 | return 0; |
dac79008 BS |
232 | } |
233 | ||
a8eaebc6 BS |
234 | struct nouveau_gpuobj * |
235 | nouveau_ramht_find(struct nouveau_channel *chan, u32 handle) | |
236 | { | |
dac79008 | 237 | struct nouveau_ramht *ramht = chan->ramht; |
a8eaebc6 | 238 | struct nouveau_ramht_entry *entry; |
dac79008 BS |
239 | struct nouveau_gpuobj *gpuobj = NULL; |
240 | unsigned long flags; | |
a8eaebc6 | 241 | |
2941482e BS |
242 | if (unlikely(!chan->ramht)) |
243 | return NULL; | |
244 | ||
dac79008 | 245 | spin_lock_irqsave(&ramht->lock, flags); |
a8eaebc6 | 246 | list_for_each_entry(entry, &chan->ramht->entries, head) { |
dac79008 BS |
247 | if (entry->channel == chan && entry->handle == handle) { |
248 | gpuobj = entry->gpuobj; | |
249 | break; | |
250 | } | |
a8eaebc6 | 251 | } |
dac79008 | 252 | spin_unlock_irqrestore(&ramht->lock, flags); |
a8eaebc6 | 253 | |
dac79008 | 254 | return gpuobj; |
a8eaebc6 BS |
255 | } |
256 | ||
257 | int | |
258 | nouveau_ramht_new(struct drm_device *dev, struct nouveau_gpuobj *gpuobj, | |
259 | struct nouveau_ramht **pramht) | |
260 | { | |
261 | struct nouveau_ramht *ramht; | |
262 | ||
263 | ramht = kzalloc(sizeof(*ramht), GFP_KERNEL); | |
264 | if (!ramht) | |
265 | return -ENOMEM; | |
266 | ||
267 | ramht->dev = dev; | |
dac79008 | 268 | kref_init(&ramht->refcount); |
e05c5a31 | 269 | ramht->bits = drm_order(gpuobj->size / 8); |
a8eaebc6 | 270 | INIT_LIST_HEAD(&ramht->entries); |
dac79008 | 271 | spin_lock_init(&ramht->lock); |
a8eaebc6 BS |
272 | nouveau_gpuobj_ref(gpuobj, &ramht->gpuobj); |
273 | ||
274 | *pramht = ramht; | |
275 | return 0; | |
276 | } | |
277 | ||
dac79008 BS |
278 | static void |
279 | nouveau_ramht_del(struct kref *ref) | |
280 | { | |
281 | struct nouveau_ramht *ramht = | |
282 | container_of(ref, struct nouveau_ramht, refcount); | |
283 | ||
284 | nouveau_gpuobj_ref(NULL, &ramht->gpuobj); | |
285 | kfree(ramht); | |
286 | } | |
287 | ||
a8eaebc6 BS |
288 | void |
289 | nouveau_ramht_ref(struct nouveau_ramht *ref, struct nouveau_ramht **ptr, | |
290 | struct nouveau_channel *chan) | |
291 | { | |
7bb94d26 | 292 | struct nouveau_ramht_entry *entry; |
a8eaebc6 BS |
293 | struct nouveau_ramht *ramht; |
294 | ||
295 | if (ref) | |
dac79008 | 296 | kref_get(&ref->refcount); |
a8eaebc6 BS |
297 | |
298 | ramht = *ptr; | |
299 | if (ramht) { | |
7bb94d26 FJ |
300 | while ((entry = nouveau_ramht_remove_entry(chan, 0))) { |
301 | nouveau_ramht_remove_hash(chan, entry->handle); | |
302 | nouveau_gpuobj_ref(NULL, &entry->gpuobj); | |
303 | kfree(entry); | |
a8eaebc6 | 304 | } |
dac79008 BS |
305 | |
306 | kref_put(&ramht->refcount, nouveau_ramht_del); | |
a8eaebc6 BS |
307 | } |
308 | *ptr = ref; | |
479dcaea | 309 | } |