Commit | Line | Data |
---|---|---|
6ee73861 BS |
1 | /* |
2 | * Copyright 2005-2006 Stephane Marchesin | |
3 | * All Rights Reserved. | |
4 | * | |
5 | * Permission is hereby granted, free of charge, to any person obtaining a | |
6 | * copy of this software and associated documentation files (the "Software"), | |
7 | * to deal in the Software without restriction, including without limitation | |
8 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, | |
9 | * and/or sell copies of the Software, and to permit persons to whom the | |
10 | * Software is furnished to do so, subject to the following conditions: | |
11 | * | |
12 | * The above copyright notice and this permission notice (including the next | |
13 | * paragraph) shall be included in all copies or substantial portions of the | |
14 | * Software. | |
15 | * | |
16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | |
19 | * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR | |
20 | * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, | |
21 | * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER | |
22 | * DEALINGS IN THE SOFTWARE. | |
23 | */ | |
24 | ||
25 | #include "drmP.h" | |
26 | #include "drm.h" | |
27 | #include "nouveau_drv.h" | |
94580299 | 28 | #include <nouveau_drm.h> |
6ee73861 | 29 | #include "nouveau_dma.h" |
02a841d4 BS |
30 | #include <engine/fifo.h> |
31 | #include <core/ramht.h> | |
d375e7d5 | 32 | #include "nouveau_fence.h" |
20abd163 | 33 | #include "nouveau_software.h" |
6ee73861 | 34 | |
8be21a64 BS |
35 | MODULE_PARM_DESC(vram_pushbuf, "Force DMA push buffers to be in VRAM"); |
36 | int nouveau_vram_pushbuf; | |
37 | module_param_named(vram_pushbuf, nouveau_vram_pushbuf, int, 0400); | |
38 | ||
6ee73861 | 39 | static int |
dd6a46cc | 40 | nouveau_channel_pushbuf_init(struct nouveau_channel *chan) |
6ee73861 | 41 | { |
dd6a46cc | 42 | u32 mem = nouveau_vram_pushbuf ? TTM_PL_FLAG_VRAM : TTM_PL_FLAG_TT; |
6ee73861 BS |
43 | struct drm_device *dev = chan->dev; |
44 | struct drm_nouveau_private *dev_priv = dev->dev_private; | |
dd6a46cc BS |
45 | int ret; |
46 | ||
47 | /* allocate buffer object */ | |
22b33e8e | 48 | ret = nouveau_bo_new(dev, 65536, 0, mem, 0, 0, NULL, &chan->pushbuf_bo); |
dd6a46cc BS |
49 | if (ret) |
50 | goto out; | |
51 | ||
52 | ret = nouveau_bo_pin(chan->pushbuf_bo, mem); | |
53 | if (ret) | |
54 | goto out; | |
6ee73861 | 55 | |
dd6a46cc BS |
56 | ret = nouveau_bo_map(chan->pushbuf_bo); |
57 | if (ret) | |
58 | goto out; | |
59 | ||
60 | /* create DMA object covering the entire memtype where the push | |
61 | * buffer resides, userspace can submit its own push buffers from | |
62 | * anywhere within the same memtype. | |
63 | */ | |
180cc306 | 64 | chan->pushbuf_base = chan->pushbuf_bo->bo.offset; |
d87897d4 | 65 | if (dev_priv->card_type >= NV_50) { |
ce163f69 BS |
66 | ret = nouveau_bo_vma_add(chan->pushbuf_bo, chan->vm, |
67 | &chan->pushbuf_vma); | |
68 | if (ret) | |
69 | goto out; | |
70 | ||
96545299 BS |
71 | if (dev_priv->card_type < NV_C0) { |
72 | ret = nouveau_gpuobj_dma_new(chan, | |
73 | NV_CLASS_DMA_IN_MEMORY, 0, | |
74 | (1ULL << 40), | |
75 | NV_MEM_ACCESS_RO, | |
76 | NV_MEM_TARGET_VM, | |
dd6a46cc | 77 | &chan->pushbuf); |
96545299 | 78 | } |
ce163f69 | 79 | chan->pushbuf_base = chan->pushbuf_vma.offset; |
d87897d4 | 80 | } else |
dd6a46cc | 81 | if (chan->pushbuf_bo->bo.mem.mem_type == TTM_PL_TT) { |
7f4a195f BS |
82 | ret = nouveau_gpuobj_dma_new(chan, NV_CLASS_DMA_IN_MEMORY, 0, |
83 | dev_priv->gart_info.aper_size, | |
84 | NV_MEM_ACCESS_RO, | |
dd6a46cc BS |
85 | NV_MEM_TARGET_GART, |
86 | &chan->pushbuf); | |
6ee73861 BS |
87 | } else |
88 | if (dev_priv->card_type != NV_04) { | |
89 | ret = nouveau_gpuobj_dma_new(chan, NV_CLASS_DMA_IN_MEMORY, 0, | |
90 | dev_priv->fb_available_size, | |
7f4a195f | 91 | NV_MEM_ACCESS_RO, |
dd6a46cc BS |
92 | NV_MEM_TARGET_VRAM, |
93 | &chan->pushbuf); | |
6ee73861 BS |
94 | } else { |
95 | /* NV04 cmdbuf hack, from original ddx.. not sure of it's | |
96 | * exact reason for existing :) PCI access to cmdbuf in | |
97 | * VRAM. | |
98 | */ | |
99 | ret = nouveau_gpuobj_dma_new(chan, NV_CLASS_DMA_IN_MEMORY, | |
7f4a195f | 100 | pci_resource_start(dev->pdev, 1), |
6ee73861 | 101 | dev_priv->fb_available_size, |
7f4a195f | 102 | NV_MEM_ACCESS_RO, |
dd6a46cc BS |
103 | NV_MEM_TARGET_PCI, |
104 | &chan->pushbuf); | |
6ee73861 BS |
105 | } |
106 | ||
dd6a46cc | 107 | out: |
6ee73861 | 108 | if (ret) { |
dd6a46cc | 109 | NV_ERROR(dev, "error initialising pushbuf: %d\n", ret); |
ce163f69 | 110 | nouveau_bo_vma_del(chan->pushbuf_bo, &chan->pushbuf_vma); |
dd6a46cc BS |
111 | nouveau_gpuobj_ref(NULL, &chan->pushbuf); |
112 | if (chan->pushbuf_bo) { | |
113 | nouveau_bo_unmap(chan->pushbuf_bo); | |
114 | nouveau_bo_ref(NULL, &chan->pushbuf_bo); | |
115 | } | |
96545299 BS |
116 | } |
117 | ||
dd6a46cc | 118 | return 0; |
6ee73861 BS |
119 | } |
120 | ||
121 | /* allocates and initializes a fifo for user space consumption */ | |
122 | int | |
123 | nouveau_channel_alloc(struct drm_device *dev, struct nouveau_channel **chan_ret, | |
124 | struct drm_file *file_priv, | |
cff5c133 | 125 | uint32_t vram_handle, uint32_t gart_handle) |
6ee73861 BS |
126 | { |
127 | struct drm_nouveau_private *dev_priv = dev->dev_private; | |
e193b1d4 BS |
128 | struct nouveau_fifo_priv *pfifo = nv_engine(dev, NVOBJ_ENGINE_FIFO); |
129 | struct nouveau_fence_priv *fence = dev_priv->fence.func; | |
e8a863c1 | 130 | struct nouveau_fpriv *fpriv = nouveau_fpriv(file_priv); |
6ee73861 | 131 | struct nouveau_channel *chan; |
cff5c133 | 132 | unsigned long flags; |
48aca13f | 133 | int ret, i; |
6ee73861 | 134 | |
cff5c133 BS |
135 | /* allocate and lock channel structure */ |
136 | chan = kzalloc(sizeof(*chan), GFP_KERNEL); | |
137 | if (!chan) | |
6ee73861 | 138 | return -ENOMEM; |
6ee73861 | 139 | chan->dev = dev; |
6ee73861 BS |
140 | chan->file_priv = file_priv; |
141 | chan->vram_handle = vram_handle; | |
cff5c133 BS |
142 | chan->gart_handle = gart_handle; |
143 | ||
f091a3d4 FJ |
144 | kref_init(&chan->ref); |
145 | atomic_set(&chan->users, 1); | |
6a6b73f2 | 146 | mutex_init(&chan->mutex); |
cff5c133 | 147 | mutex_lock(&chan->mutex); |
6ee73861 | 148 | |
cff5c133 BS |
149 | /* allocate hw channel id */ |
150 | spin_lock_irqsave(&dev_priv->channels.lock, flags); | |
151 | for (chan->id = 0; chan->id < pfifo->channels; chan->id++) { | |
3863c9bc BS |
152 | if ( dev_priv->card_type == NV_50 && chan->id == 0) |
153 | continue; | |
154 | ||
cff5c133 | 155 | if (!dev_priv->channels.ptr[chan->id]) { |
f091a3d4 | 156 | nouveau_channel_ref(chan, &dev_priv->channels.ptr[chan->id]); |
cff5c133 BS |
157 | break; |
158 | } | |
159 | } | |
160 | spin_unlock_irqrestore(&dev_priv->channels.lock, flags); | |
161 | ||
162 | if (chan->id == pfifo->channels) { | |
163 | mutex_unlock(&chan->mutex); | |
164 | kfree(chan); | |
165 | return -ENODEV; | |
166 | } | |
167 | ||
168 | NV_DEBUG(dev, "initialising channel %d\n", chan->id); | |
6ee73861 | 169 | |
dd6a46cc BS |
170 | /* setup channel's memory and vm */ |
171 | ret = nouveau_gpuobj_channel_init(chan, vram_handle, gart_handle); | |
172 | if (ret) { | |
173 | NV_ERROR(dev, "gpuobj %d\n", ret); | |
cff5c133 | 174 | nouveau_channel_put(&chan); |
6ee73861 BS |
175 | return ret; |
176 | } | |
177 | ||
6ee73861 BS |
178 | /* Allocate space for per-channel fixed notifier memory */ |
179 | ret = nouveau_notifier_init_channel(chan); | |
180 | if (ret) { | |
181 | NV_ERROR(dev, "ntfy %d\n", ret); | |
cff5c133 | 182 | nouveau_channel_put(&chan); |
6ee73861 BS |
183 | return ret; |
184 | } | |
185 | ||
dd6a46cc BS |
186 | /* Allocate DMA push buffer */ |
187 | ret = nouveau_channel_pushbuf_init(chan); | |
6ee73861 | 188 | if (ret) { |
dd6a46cc | 189 | NV_ERROR(dev, "pushbuf %d\n", ret); |
cff5c133 | 190 | nouveau_channel_put(&chan); |
6ee73861 BS |
191 | return ret; |
192 | } | |
193 | ||
48aca13f | 194 | nouveau_dma_init(chan); |
dd6a46cc BS |
195 | chan->user_put = 0x40; |
196 | chan->user_get = 0x44; | |
4e03b4af | 197 | if (dev_priv->card_type >= NV_50) |
5e120f6e | 198 | chan->user_get_hi = 0x60; |
6ee73861 | 199 | |
c420b2dc BS |
200 | /* create fifo context */ |
201 | ret = pfifo->base.context_new(chan, NVOBJ_ENGINE_FIFO); | |
6ee73861 | 202 | if (ret) { |
cff5c133 | 203 | nouveau_channel_put(&chan); |
6ee73861 BS |
204 | return ret; |
205 | } | |
206 | ||
48aca13f BS |
207 | /* Insert NOPs for NOUVEAU_DMA_SKIPS */ |
208 | ret = RING_SPACE(chan, NOUVEAU_DMA_SKIPS); | |
209 | if (ret) { | |
210 | nouveau_channel_put(&chan); | |
211 | return ret; | |
212 | } | |
213 | ||
214 | for (i = 0; i < NOUVEAU_DMA_SKIPS; i++) | |
215 | OUT_RING (chan, 0x00000000); | |
48aca13f | 216 | |
20abd163 BS |
217 | ret = nouveau_gpuobj_gr_new(chan, NvSw, nouveau_software_class(dev)); |
218 | if (ret) { | |
219 | nouveau_channel_put(&chan); | |
220 | return ret; | |
221 | } | |
222 | ||
5e120f6e BS |
223 | if (dev_priv->card_type < NV_C0) { |
224 | ret = RING_SPACE(chan, 2); | |
225 | if (ret) { | |
226 | nouveau_channel_put(&chan); | |
227 | return ret; | |
228 | } | |
229 | ||
230 | BEGIN_NV04(chan, NvSubSw, NV01_SUBCHAN_OBJECT, 1); | |
231 | OUT_RING (chan, NvSw); | |
232 | FIRE_RING (chan); | |
233 | } | |
234 | ||
235 | FIRE_RING(chan); | |
236 | ||
e193b1d4 | 237 | ret = fence->context_new(chan); |
6ee73861 | 238 | if (ret) { |
cff5c133 | 239 | nouveau_channel_put(&chan); |
6ee73861 BS |
240 | return ret; |
241 | } | |
242 | ||
243 | nouveau_debugfs_channel_init(chan); | |
244 | ||
cff5c133 | 245 | NV_DEBUG(dev, "channel %d initialised\n", chan->id); |
e8a863c1 BS |
246 | if (fpriv) { |
247 | spin_lock(&fpriv->lock); | |
248 | list_add(&chan->list, &fpriv->channels); | |
249 | spin_unlock(&fpriv->lock); | |
250 | } | |
6ee73861 BS |
251 | *chan_ret = chan; |
252 | return 0; | |
253 | } | |
254 | ||
feeb0aec FJ |
255 | struct nouveau_channel * |
256 | nouveau_channel_get_unlocked(struct nouveau_channel *ref) | |
257 | { | |
f091a3d4 | 258 | struct nouveau_channel *chan = NULL; |
feeb0aec | 259 | |
f091a3d4 FJ |
260 | if (likely(ref && atomic_inc_not_zero(&ref->users))) |
261 | nouveau_channel_ref(ref, &chan); | |
262 | ||
263 | return chan; | |
feeb0aec FJ |
264 | } |
265 | ||
cff5c133 | 266 | struct nouveau_channel * |
e8a863c1 | 267 | nouveau_channel_get(struct drm_file *file_priv, int id) |
cff5c133 | 268 | { |
e8a863c1 | 269 | struct nouveau_fpriv *fpriv = nouveau_fpriv(file_priv); |
feeb0aec | 270 | struct nouveau_channel *chan; |
cff5c133 | 271 | |
e8a863c1 BS |
272 | spin_lock(&fpriv->lock); |
273 | list_for_each_entry(chan, &fpriv->channels, list) { | |
274 | if (chan->id == id) { | |
275 | chan = nouveau_channel_get_unlocked(chan); | |
276 | spin_unlock(&fpriv->lock); | |
277 | mutex_lock(&chan->mutex); | |
278 | return chan; | |
279 | } | |
cff5c133 | 280 | } |
e8a863c1 | 281 | spin_unlock(&fpriv->lock); |
cff5c133 | 282 | |
e8a863c1 | 283 | return ERR_PTR(-EINVAL); |
cff5c133 BS |
284 | } |
285 | ||
6ee73861 | 286 | void |
feeb0aec | 287 | nouveau_channel_put_unlocked(struct nouveau_channel **pchan) |
6ee73861 | 288 | { |
cff5c133 | 289 | struct nouveau_channel *chan = *pchan; |
6ee73861 BS |
290 | struct drm_device *dev = chan->dev; |
291 | struct drm_nouveau_private *dev_priv = dev->dev_private; | |
e193b1d4 | 292 | struct nouveau_fence_priv *fence = dev_priv->fence.func; |
6ee73861 | 293 | unsigned long flags; |
6dfdd7a6 | 294 | int i; |
6ee73861 | 295 | |
cff5c133 | 296 | /* decrement the refcount, and we're done if there's still refs */ |
f091a3d4 FJ |
297 | if (likely(!atomic_dec_and_test(&chan->users))) { |
298 | nouveau_channel_ref(NULL, pchan); | |
cff5c133 BS |
299 | return; |
300 | } | |
6ee73861 | 301 | |
25985edc | 302 | /* no one wants the channel anymore */ |
cff5c133 | 303 | NV_DEBUG(dev, "freeing channel %d\n", chan->id); |
6ee73861 BS |
304 | nouveau_debugfs_channel_fini(chan); |
305 | ||
cff5c133 | 306 | /* give it chance to idle */ |
6dccd311 | 307 | nouveau_channel_idle(chan); |
6ee73861 | 308 | |
3945e475 | 309 | /* destroy the engine specific contexts */ |
f51ee65c | 310 | for (i = NVOBJ_ENGINE_NR - 1; i >= 0; i--) { |
6dfdd7a6 BS |
311 | if (chan->engctx[i]) |
312 | dev_priv->eng[i]->context_del(chan, i); | |
313 | } | |
6ee73861 | 314 | |
e193b1d4 BS |
315 | if (chan->fence) |
316 | fence->context_del(chan); | |
317 | ||
cff5c133 BS |
318 | /* aside from its resources, the channel should now be dead, |
319 | * remove it from the channel list | |
320 | */ | |
321 | spin_lock_irqsave(&dev_priv->channels.lock, flags); | |
f091a3d4 | 322 | nouveau_channel_ref(NULL, &dev_priv->channels.ptr[chan->id]); |
cff5c133 BS |
323 | spin_unlock_irqrestore(&dev_priv->channels.lock, flags); |
324 | ||
325 | /* destroy any resources the channel owned */ | |
a8eaebc6 | 326 | nouveau_gpuobj_ref(NULL, &chan->pushbuf); |
6ee73861 | 327 | if (chan->pushbuf_bo) { |
ce163f69 | 328 | nouveau_bo_vma_del(chan->pushbuf_bo, &chan->pushbuf_vma); |
9d59e8a1 | 329 | nouveau_bo_unmap(chan->pushbuf_bo); |
6ee73861 BS |
330 | nouveau_bo_unpin(chan->pushbuf_bo); |
331 | nouveau_bo_ref(NULL, &chan->pushbuf_bo); | |
332 | } | |
b7cb6c01 | 333 | nouveau_ramht_ref(NULL, &chan->ramht, chan); |
6ee73861 | 334 | nouveau_notifier_takedown_channel(chan); |
b7cb6c01 | 335 | nouveau_gpuobj_channel_takedown(chan); |
6ee73861 | 336 | |
f091a3d4 | 337 | nouveau_channel_ref(NULL, pchan); |
6ee73861 BS |
338 | } |
339 | ||
feeb0aec FJ |
340 | void |
341 | nouveau_channel_put(struct nouveau_channel **pchan) | |
342 | { | |
343 | mutex_unlock(&(*pchan)->mutex); | |
344 | nouveau_channel_put_unlocked(pchan); | |
345 | } | |
346 | ||
f091a3d4 FJ |
347 | static void |
348 | nouveau_channel_del(struct kref *ref) | |
349 | { | |
350 | struct nouveau_channel *chan = | |
351 | container_of(ref, struct nouveau_channel, ref); | |
352 | ||
f091a3d4 FJ |
353 | kfree(chan); |
354 | } | |
355 | ||
356 | void | |
357 | nouveau_channel_ref(struct nouveau_channel *chan, | |
358 | struct nouveau_channel **pchan) | |
359 | { | |
360 | if (chan) | |
361 | kref_get(&chan->ref); | |
362 | ||
363 | if (*pchan) | |
364 | kref_put(&(*pchan)->ref, nouveau_channel_del); | |
365 | ||
366 | *pchan = chan; | |
367 | } | |
368 | ||
d1b167e1 | 369 | int |
6dccd311 FJ |
370 | nouveau_channel_idle(struct nouveau_channel *chan) |
371 | { | |
372 | struct drm_device *dev = chan->dev; | |
373 | struct nouveau_fence *fence = NULL; | |
374 | int ret; | |
375 | ||
5e120f6e BS |
376 | ret = nouveau_fence_new(chan, &fence); |
377 | if (!ret) { | |
378 | ret = nouveau_fence_wait(fence, false, false); | |
379 | nouveau_fence_unref(&fence); | |
6dccd311 | 380 | } |
5e120f6e BS |
381 | |
382 | if (ret) | |
383 | NV_ERROR(dev, "Failed to idle channel %d.\n", chan->id); | |
d1b167e1 | 384 | return ret; |
6dccd311 FJ |
385 | } |
386 | ||
6ee73861 BS |
387 | /* cleans up all the fifos from file_priv */ |
388 | void | |
389 | nouveau_channel_cleanup(struct drm_device *dev, struct drm_file *file_priv) | |
390 | { | |
c420b2dc | 391 | struct nouveau_fifo_priv *pfifo = nv_engine(dev, NVOBJ_ENGINE_FIFO); |
cff5c133 | 392 | struct nouveau_channel *chan; |
6ee73861 BS |
393 | int i; |
394 | ||
c420b2dc BS |
395 | if (!pfifo) |
396 | return; | |
397 | ||
6ee73861 | 398 | NV_DEBUG(dev, "clearing FIFO enables from file_priv\n"); |
c420b2dc | 399 | for (i = 0; i < pfifo->channels; i++) { |
e8a863c1 | 400 | chan = nouveau_channel_get(file_priv, i); |
cff5c133 BS |
401 | if (IS_ERR(chan)) |
402 | continue; | |
6ee73861 | 403 | |
e8a863c1 | 404 | list_del(&chan->list); |
f091a3d4 | 405 | atomic_dec(&chan->users); |
cff5c133 | 406 | nouveau_channel_put(&chan); |
6ee73861 BS |
407 | } |
408 | } |