~ubuntu-branches/ubuntu/precise/linux-lowlatency/precise

« back to all changes in this revision

Viewing changes to drivers/gpu/drm/nouveau/nouveau_ramht.c

  • Committer: Package Import Robot
  • Author(s): Alessio Igor Bogani
  • Date: 2011-10-26 11:13:05 UTC
  • Revision ID: package-import@ubuntu.com-20111026111305-tz023xykf0i6eosh
Tags: upstream-3.2.0
ImportĀ upstreamĀ versionĀ 3.2.0

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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
 
 
25
#include "drmP.h"
 
26
 
 
27
#include "nouveau_drv.h"
 
28
#include "nouveau_ramht.h"
 
29
 
 
30
static u32
 
31
nouveau_ramht_hash_handle(struct nouveau_channel *chan, u32 handle)
 
32
{
 
33
        struct drm_device *dev = chan->dev;
 
34
        struct drm_nouveau_private *dev_priv = dev->dev_private;
 
35
        struct nouveau_ramht *ramht = chan->ramht;
 
36
        u32 hash = 0;
 
37
        int i;
 
38
 
 
39
        NV_DEBUG(dev, "ch%d handle=0x%08x\n", chan->id, handle);
 
40
 
 
41
        for (i = 32; i > 0; i -= ramht->bits) {
 
42
                hash ^= (handle & ((1 << ramht->bits) - 1));
 
43
                handle >>= ramht->bits;
 
44
        }
 
45
 
 
46
        if (dev_priv->card_type < NV_50)
 
47
                hash ^= chan->id << (ramht->bits - 4);
 
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,
 
56
                          u32 offset)
 
57
{
 
58
        struct drm_nouveau_private *dev_priv = dev->dev_private;
 
59
        u32 ctx = nv_ro32(ramht, offset + 4);
 
60
 
 
61
        if (dev_priv->card_type < NV_40)
 
62
                return ((ctx & NV_RAMHT_CONTEXT_VALID) != 0);
 
63
        return (ctx != 0);
 
64
}
 
65
 
 
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
 
 
83
int
 
84
nouveau_ramht_insert(struct nouveau_channel *chan, u32 handle,
 
85
                     struct nouveau_gpuobj *gpuobj)
 
86
{
 
87
        struct drm_device *dev = chan->dev;
 
88
        struct drm_nouveau_private *dev_priv = dev->dev_private;
 
89
        struct nouveau_instmem_engine *instmem = &dev_priv->engine.instmem;
 
90
        struct nouveau_ramht_entry *entry;
 
91
        struct nouveau_gpuobj *ramht = chan->ramht->gpuobj;
 
92
        unsigned long flags;
 
93
        u32 ctx, co, ho;
 
94
 
 
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;
 
104
        nouveau_gpuobj_ref(gpuobj, &entry->gpuobj);
 
105
 
 
106
        if (dev_priv->card_type < NV_40) {
 
107
                ctx = NV_RAMHT_CONTEXT_VALID | (gpuobj->pinst >> 4) |
 
108
                      (chan->id << NV_RAMHT_CONTEXT_CHANNEL_SHIFT) |
 
109
                      (gpuobj->engine << NV_RAMHT_CONTEXT_ENGINE_SHIFT);
 
110
        } else
 
111
        if (dev_priv->card_type < NV_50) {
 
112
                ctx = (gpuobj->pinst >> 4) |
 
113
                      (chan->id << NV40_RAMHT_CONTEXT_CHANNEL_SHIFT) |
 
114
                      (gpuobj->engine << NV40_RAMHT_CONTEXT_ENGINE_SHIFT);
 
115
        } else {
 
116
                if (gpuobj->engine == NVOBJ_ENGINE_DISPLAY) {
 
117
                        ctx = (gpuobj->cinst << 10) |
 
118
                              (chan->id << 28) |
 
119
                              chan->id; /* HASH_TAG */
 
120
                } else {
 
121
                        ctx = (gpuobj->cinst >> 4) |
 
122
                              ((gpuobj->engine <<
 
123
                                NV40_RAMHT_CONTEXT_ENGINE_SHIFT));
 
124
                }
 
125
        }
 
126
 
 
127
        spin_lock_irqsave(&chan->ramht->lock, flags);
 
128
        list_add(&entry->head, &chan->ramht->entries);
 
129
 
 
130
        co = ho = nouveau_ramht_hash_handle(chan, handle);
 
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",
 
135
                                 chan->id, co, handle, ctx);
 
136
                        nv_wo32(ramht, co + 0, handle);
 
137
                        nv_wo32(ramht, co + 4, ctx);
 
138
 
 
139
                        spin_unlock_irqrestore(&chan->ramht->lock, flags);
 
140
                        instmem->flush(dev);
 
141
                        return 0;
 
142
                }
 
143
                NV_DEBUG(dev, "collision ch%d 0x%08x: h=0x%08x\n",
 
144
                         chan->id, co, nv_ro32(ramht, co));
 
145
 
 
146
                co += 8;
 
147
                if (co >= ramht->size)
 
148
                        co = 0;
 
149
        } while (co != ho);
 
150
 
 
151
        NV_ERROR(dev, "RAMHT space exhausted. ch=%d\n", chan->id);
 
152
        list_del(&entry->head);
 
153
        spin_unlock_irqrestore(&chan->ramht->lock, flags);
 
154
        kfree(entry);
 
155
        return -ENOMEM;
 
156
}
 
157
 
 
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
 
 
183
static void
 
184
nouveau_ramht_remove_hash(struct nouveau_channel *chan, u32 handle)
 
185
{
 
186
        struct drm_device *dev = chan->dev;
 
187
        struct drm_nouveau_private *dev_priv = dev->dev_private;
 
188
        struct nouveau_instmem_engine *instmem = &dev_priv->engine.instmem;
 
189
        struct nouveau_gpuobj *ramht = chan->ramht->gpuobj;
 
190
        unsigned long flags;
 
191
        u32 co, ho;
 
192
 
 
193
        spin_lock_irqsave(&chan->ramht->lock, flags);
 
194
        co = ho = nouveau_ramht_hash_handle(chan, handle);
 
195
        do {
 
196
                if (nouveau_ramht_entry_valid(dev, ramht, co) &&
 
197
                    nouveau_ramht_entry_same_channel(chan, ramht, co) &&
 
198
                    (handle == nv_ro32(ramht, co))) {
 
199
                        NV_DEBUG(dev,
 
200
                                 "remove ch%d 0x%08x: h=0x%08x, c=0x%08x\n",
 
201
                                 chan->id, co, handle, nv_ro32(ramht, co + 4));
 
202
                        nv_wo32(ramht, co + 0, 0x00000000);
 
203
                        nv_wo32(ramht, co + 4, 0x00000000);
 
204
                        instmem->flush(dev);
 
205
                        goto out;
 
206
                }
 
207
 
 
208
                co += 8;
 
209
                if (co >= ramht->size)
 
210
                        co = 0;
 
211
        } while (co != ho);
 
212
 
 
213
        NV_ERROR(dev, "RAMHT entry not found. ch=%d, handle=0x%08x\n",
 
214
                 chan->id, handle);
 
215
out:
 
216
        spin_unlock_irqrestore(&chan->ramht->lock, flags);
 
217
}
 
218
 
 
219
int
 
220
nouveau_ramht_remove(struct nouveau_channel *chan, u32 handle)
 
221
{
 
222
        struct nouveau_ramht_entry *entry;
 
223
 
 
224
        entry = nouveau_ramht_remove_entry(chan, handle);
 
225
        if (!entry)
 
226
                return -ENOENT;
 
227
 
 
228
        nouveau_ramht_remove_hash(chan, entry->handle);
 
229
        nouveau_gpuobj_ref(NULL, &entry->gpuobj);
 
230
        kfree(entry);
 
231
        return 0;
 
232
}
 
233
 
 
234
struct nouveau_gpuobj *
 
235
nouveau_ramht_find(struct nouveau_channel *chan, u32 handle)
 
236
{
 
237
        struct nouveau_ramht *ramht = chan->ramht;
 
238
        struct nouveau_ramht_entry *entry;
 
239
        struct nouveau_gpuobj *gpuobj = NULL;
 
240
        unsigned long flags;
 
241
 
 
242
        if (unlikely(!chan->ramht))
 
243
                return NULL;
 
244
 
 
245
        spin_lock_irqsave(&ramht->lock, flags);
 
246
        list_for_each_entry(entry, &chan->ramht->entries, head) {
 
247
                if (entry->channel == chan && entry->handle == handle) {
 
248
                        gpuobj = entry->gpuobj;
 
249
                        break;
 
250
                }
 
251
        }
 
252
        spin_unlock_irqrestore(&ramht->lock, flags);
 
253
 
 
254
        return gpuobj;
 
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;
 
268
        kref_init(&ramht->refcount);
 
269
        ramht->bits = drm_order(gpuobj->size / 8);
 
270
        INIT_LIST_HEAD(&ramht->entries);
 
271
        spin_lock_init(&ramht->lock);
 
272
        nouveau_gpuobj_ref(gpuobj, &ramht->gpuobj);
 
273
 
 
274
        *pramht = ramht;
 
275
        return 0;
 
276
}
 
277
 
 
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
 
 
288
void
 
289
nouveau_ramht_ref(struct nouveau_ramht *ref, struct nouveau_ramht **ptr,
 
290
                  struct nouveau_channel *chan)
 
291
{
 
292
        struct nouveau_ramht_entry *entry;
 
293
        struct nouveau_ramht *ramht;
 
294
 
 
295
        if (ref)
 
296
                kref_get(&ref->refcount);
 
297
 
 
298
        ramht = *ptr;
 
299
        if (ramht) {
 
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);
 
304
                }
 
305
 
 
306
                kref_put(&ramht->refcount, nouveau_ramht_del);
 
307
        }
 
308
        *ptr = ref;
 
309
}