diff options
author | Ben Skeggs <bskeggs@redhat.com> | 2012-07-19 18:17:34 -0400 |
---|---|---|
committer | Ben Skeggs <bskeggs@redhat.com> | 2012-10-02 23:12:56 -0400 |
commit | ebb945a94bba2ce8dff7b0942ff2b3f2a52a0a69 (patch) | |
tree | 07cad59be501458e6ae1304b7c0352e322ac3387 /drivers/gpu/drm/nouveau/nouveau_chan.c | |
parent | ac1499d9573f4aadd1d2beac11fe23af8ce90c24 (diff) |
drm/nouveau: port all engines to new engine module format
This is a HUGE commit, but it's not nearly as bad as it looks - any problems
can be isolated to a particular chipset and engine combination. It was
simply too difficult to port each one at a time, the compat layers are
*already* ridiculous.
Most of the changes here are simply to the glue, the process for each of the
engine modules was to start with a standard skeleton and copy+paste the old
code into the appropriate places, fixing up variable names etc as needed.
v2: Marcin Slusarz <marcin.slusarz@gmail.com>
- fix find/replace bug in license header
v3: Ben Skeggs <bskeggs@redhat.com>
- bump indirect pushbuf size to 8KiB, 4KiB barely enough for userspace and
left no space for kernel's requirements during GEM pushbuf submission.
- fix duplicate assignments noticed by clang
v4: Marcin Slusarz <marcin.slusarz@gmail.com>
- add sparse annotations to nv04_fifo_pause/nv04_fifo_start
- use ioread32_native/iowrite32_native for fifo control registers
v5: Ben Skeggs <bskeggs@redhat.com>
- rebase on v3.6-rc4, modified to keep copy engine fix intact
- nv10/fence: unmap fence bo before destroying
- fixed fermi regression when using nvidia gr fuc
- fixed typo in supported dma_mask checking
Signed-off-by: Ben Skeggs <bskeggs@redhat.com>
Diffstat (limited to 'drivers/gpu/drm/nouveau/nouveau_chan.c')
-rw-r--r-- | drivers/gpu/drm/nouveau/nouveau_chan.c | 387 |
1 files changed, 387 insertions, 0 deletions
diff --git a/drivers/gpu/drm/nouveau/nouveau_chan.c b/drivers/gpu/drm/nouveau/nouveau_chan.c new file mode 100644 index 000000000000..3dd5f712b98c --- /dev/null +++ b/drivers/gpu/drm/nouveau/nouveau_chan.c | |||
@@ -0,0 +1,387 @@ | |||
1 | /* | ||
2 | * Copyright 2012 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 <core/object.h> | ||
26 | #include <core/client.h> | ||
27 | #include <core/device.h> | ||
28 | #include <core/class.h> | ||
29 | |||
30 | #include <subdev/fb.h> | ||
31 | #include <subdev/vm.h> | ||
32 | #include <subdev/instmem.h> | ||
33 | |||
34 | #include <engine/software.h> | ||
35 | |||
36 | #include "nouveau_drm.h" | ||
37 | #include "nouveau_dma.h" | ||
38 | #include "nouveau_bo.h" | ||
39 | #include "nouveau_chan.h" | ||
40 | #include "nouveau_fence.h" | ||
41 | #include "nouveau_abi16.h" | ||
42 | |||
43 | MODULE_PARM_DESC(vram_pushbuf, "Create DMA push buffers in VRAM"); | ||
44 | static int nouveau_vram_pushbuf; | ||
45 | module_param_named(vram_pushbuf, nouveau_vram_pushbuf, int, 0400); | ||
46 | |||
47 | int | ||
48 | nouveau_channel_idle(struct nouveau_channel *chan) | ||
49 | { | ||
50 | struct nouveau_drm *drm = chan->drm; | ||
51 | struct nouveau_fence *fence = NULL; | ||
52 | int ret; | ||
53 | |||
54 | ret = nouveau_fence_new(chan, &fence); | ||
55 | if (!ret) { | ||
56 | ret = nouveau_fence_wait(fence, false, false); | ||
57 | nouveau_fence_unref(&fence); | ||
58 | } | ||
59 | |||
60 | if (ret) | ||
61 | NV_ERROR(drm, "failed to idle channel 0x%08x\n", chan->handle); | ||
62 | return ret; | ||
63 | } | ||
64 | |||
65 | void | ||
66 | nouveau_channel_del(struct nouveau_channel **pchan) | ||
67 | { | ||
68 | struct nouveau_channel *chan = *pchan; | ||
69 | if (chan) { | ||
70 | struct nouveau_object *client = nv_object(chan->cli); | ||
71 | if (chan->fence) { | ||
72 | nouveau_channel_idle(chan); | ||
73 | nouveau_fence(chan->drm)->context_del(chan); | ||
74 | } | ||
75 | nouveau_object_del(client, NVDRM_DEVICE, chan->handle); | ||
76 | nouveau_object_del(client, NVDRM_DEVICE, chan->push.handle); | ||
77 | nouveau_bo_vma_del(chan->push.buffer, &chan->push.vma); | ||
78 | nouveau_bo_unmap(chan->push.buffer); | ||
79 | nouveau_bo_ref(NULL, &chan->push.buffer); | ||
80 | kfree(chan); | ||
81 | } | ||
82 | *pchan = NULL; | ||
83 | } | ||
84 | |||
85 | static int | ||
86 | nouveau_channel_prep(struct nouveau_drm *drm, struct nouveau_cli *cli, | ||
87 | u32 parent, u32 handle, u32 size, | ||
88 | struct nouveau_channel **pchan) | ||
89 | { | ||
90 | struct nouveau_device *device = nv_device(drm->device); | ||
91 | struct nouveau_instmem *imem = nouveau_instmem(device); | ||
92 | struct nouveau_vmmgr *vmm = nouveau_vmmgr(device); | ||
93 | struct nouveau_fb *pfb = nouveau_fb(device); | ||
94 | struct nouveau_client *client = &cli->base; | ||
95 | struct nv_dma_class args = {}; | ||
96 | struct nouveau_channel *chan; | ||
97 | struct nouveau_object *push; | ||
98 | u32 target; | ||
99 | int ret; | ||
100 | |||
101 | chan = *pchan = kzalloc(sizeof(*chan), GFP_KERNEL); | ||
102 | if (!chan) | ||
103 | return -ENOMEM; | ||
104 | |||
105 | chan->cli = cli; | ||
106 | chan->drm = drm; | ||
107 | chan->handle = handle; | ||
108 | |||
109 | /* allocate memory for dma push buffer */ | ||
110 | target = TTM_PL_FLAG_TT; | ||
111 | if (nouveau_vram_pushbuf) | ||
112 | target = TTM_PL_FLAG_VRAM; | ||
113 | |||
114 | ret = nouveau_bo_new(drm->dev, size, 0, target, 0, 0, NULL, | ||
115 | &chan->push.buffer); | ||
116 | if (ret == 0) { | ||
117 | ret = nouveau_bo_pin(chan->push.buffer, target); | ||
118 | if (ret == 0) | ||
119 | ret = nouveau_bo_map(chan->push.buffer); | ||
120 | } | ||
121 | |||
122 | if (ret) { | ||
123 | nouveau_channel_del(pchan); | ||
124 | return ret; | ||
125 | } | ||
126 | |||
127 | /* create dma object covering the *entire* memory space that the | ||
128 | * pushbuf lives in, this is because the GEM code requires that | ||
129 | * we be able to call out to other (indirect) push buffers | ||
130 | */ | ||
131 | chan->push.vma.offset = chan->push.buffer->bo.offset; | ||
132 | chan->push.handle = NVDRM_PUSH | (handle & 0xffff); | ||
133 | |||
134 | if (device->card_type >= NV_50) { | ||
135 | ret = nouveau_bo_vma_add(chan->push.buffer, client->vm, | ||
136 | &chan->push.vma); | ||
137 | if (ret) { | ||
138 | nouveau_channel_del(pchan); | ||
139 | return ret; | ||
140 | } | ||
141 | |||
142 | args.flags = NV_DMA_TARGET_VM | NV_DMA_ACCESS_VM; | ||
143 | args.start = 0; | ||
144 | args.limit = client->vm->vmm->limit - 1; | ||
145 | } else | ||
146 | if (chan->push.buffer->bo.mem.mem_type == TTM_PL_VRAM) { | ||
147 | u64 limit = pfb->ram.size - imem->reserved - 1; | ||
148 | if (device->card_type == NV_04) { | ||
149 | /* nv04 vram pushbuf hack, retarget to its location in | ||
150 | * the framebuffer bar rather than direct vram access.. | ||
151 | * nfi why this exists, it came from the -nv ddx. | ||
152 | */ | ||
153 | args.flags = NV_DMA_TARGET_PCI | NV_DMA_ACCESS_RDWR; | ||
154 | args.start = pci_resource_start(device->pdev, 1); | ||
155 | args.limit = args.start + limit; | ||
156 | } else { | ||
157 | args.flags = NV_DMA_TARGET_VRAM | NV_DMA_ACCESS_RDWR; | ||
158 | args.start = 0; | ||
159 | args.limit = limit; | ||
160 | } | ||
161 | } else { | ||
162 | if (chan->drm->agp.stat == ENABLED) { | ||
163 | args.flags = NV_DMA_TARGET_AGP | NV_DMA_ACCESS_RDWR; | ||
164 | args.start = chan->drm->agp.base; | ||
165 | args.limit = chan->drm->agp.base + | ||
166 | chan->drm->agp.size - 1; | ||
167 | } else { | ||
168 | args.flags = NV_DMA_TARGET_VM | NV_DMA_ACCESS_RDWR; | ||
169 | args.start = 0; | ||
170 | args.limit = vmm->limit - 1; | ||
171 | } | ||
172 | } | ||
173 | |||
174 | ret = nouveau_object_new(nv_object(chan->cli), parent, | ||
175 | chan->push.handle, 0x0002, | ||
176 | &args, sizeof(args), &push); | ||
177 | if (ret) { | ||
178 | nouveau_channel_del(pchan); | ||
179 | return ret; | ||
180 | } | ||
181 | |||
182 | return 0; | ||
183 | } | ||
184 | |||
185 | int | ||
186 | nouveau_channel_ind(struct nouveau_drm *drm, struct nouveau_cli *cli, | ||
187 | u32 parent, u32 handle, struct nouveau_channel **pchan) | ||
188 | { | ||
189 | static const u16 oclasses[] = { 0xa06f, 0x906f, 0x826f, 0x506f, 0 }; | ||
190 | const u16 *oclass = oclasses; | ||
191 | struct nv_channel_ind_class args; | ||
192 | struct nouveau_channel *chan; | ||
193 | int ret; | ||
194 | |||
195 | /* allocate dma push buffer */ | ||
196 | ret = nouveau_channel_prep(drm, cli, parent, handle, 0x12000, &chan); | ||
197 | *pchan = chan; | ||
198 | if (ret) | ||
199 | return ret; | ||
200 | |||
201 | /* create channel object */ | ||
202 | args.pushbuf = chan->push.handle; | ||
203 | args.ioffset = 0x10000 + chan->push.vma.offset; | ||
204 | args.ilength = 0x02000; | ||
205 | |||
206 | do { | ||
207 | ret = nouveau_object_new(nv_object(cli), parent, handle, | ||
208 | *oclass++, &args, sizeof(args), | ||
209 | &chan->object); | ||
210 | if (ret == 0) | ||
211 | return ret; | ||
212 | } while (*oclass); | ||
213 | |||
214 | nouveau_channel_del(pchan); | ||
215 | return ret; | ||
216 | } | ||
217 | |||
218 | static int | ||
219 | nouveau_channel_dma(struct nouveau_drm *drm, struct nouveau_cli *cli, | ||
220 | u32 parent, u32 handle, struct nouveau_channel **pchan) | ||
221 | { | ||
222 | static const u16 oclasses[] = { 0x006e, 0 }; | ||
223 | const u16 *oclass = oclasses; | ||
224 | struct nv_channel_dma_class args; | ||
225 | struct nouveau_channel *chan; | ||
226 | int ret; | ||
227 | |||
228 | /* allocate dma push buffer */ | ||
229 | ret = nouveau_channel_prep(drm, cli, parent, handle, 0x10000, &chan); | ||
230 | *pchan = chan; | ||
231 | if (ret) | ||
232 | return ret; | ||
233 | |||
234 | /* create channel object */ | ||
235 | args.pushbuf = chan->push.handle; | ||
236 | args.offset = chan->push.vma.offset; | ||
237 | |||
238 | do { | ||
239 | ret = nouveau_object_new(nv_object(cli), parent, handle, | ||
240 | *oclass++, &args, sizeof(args), | ||
241 | &chan->object); | ||
242 | if (ret == 0) | ||
243 | return ret; | ||
244 | } while (ret && *oclass); | ||
245 | |||
246 | nouveau_channel_del(pchan); | ||
247 | return ret; | ||
248 | } | ||
249 | |||
250 | static int | ||
251 | nouveau_channel_init(struct nouveau_channel *chan, u32 vram, u32 gart) | ||
252 | { | ||
253 | struct nouveau_client *client = nv_client(chan->cli); | ||
254 | struct nouveau_device *device = nv_device(chan->drm->device); | ||
255 | struct nouveau_instmem *imem = nouveau_instmem(device); | ||
256 | struct nouveau_vmmgr *vmm = nouveau_vmmgr(device); | ||
257 | struct nouveau_fb *pfb = nouveau_fb(device); | ||
258 | struct nouveau_software_chan *swch; | ||
259 | struct nouveau_object *object; | ||
260 | struct nv_dma_class args; | ||
261 | int ret, i; | ||
262 | |||
263 | chan->vram = vram; | ||
264 | chan->gart = gart; | ||
265 | |||
266 | /* allocate dma objects to cover all allowed vram, and gart */ | ||
267 | if (device->card_type < NV_C0) { | ||
268 | if (device->card_type >= NV_50) { | ||
269 | args.flags = NV_DMA_TARGET_VM | NV_DMA_ACCESS_VM; | ||
270 | args.start = 0; | ||
271 | args.limit = client->vm->vmm->limit - 1; | ||
272 | } else { | ||
273 | args.flags = NV_DMA_TARGET_VRAM | NV_DMA_ACCESS_RDWR; | ||
274 | args.start = 0; | ||
275 | args.limit = pfb->ram.size - imem->reserved - 1; | ||
276 | } | ||
277 | |||
278 | ret = nouveau_object_new(nv_object(client), chan->handle, vram, | ||
279 | 0x003d, &args, sizeof(args), &object); | ||
280 | if (ret) | ||
281 | return ret; | ||
282 | |||
283 | if (device->card_type >= NV_50) { | ||
284 | args.flags = NV_DMA_TARGET_VM | NV_DMA_ACCESS_VM; | ||
285 | args.start = 0; | ||
286 | args.limit = client->vm->vmm->limit - 1; | ||
287 | } else | ||
288 | if (chan->drm->agp.stat == ENABLED) { | ||
289 | args.flags = NV_DMA_TARGET_AGP | NV_DMA_ACCESS_RDWR; | ||
290 | args.start = chan->drm->agp.base; | ||
291 | args.limit = chan->drm->agp.base + | ||
292 | chan->drm->agp.size - 1; | ||
293 | } else { | ||
294 | args.flags = NV_DMA_TARGET_VM | NV_DMA_ACCESS_RDWR; | ||
295 | args.start = 0; | ||
296 | args.limit = vmm->limit - 1; | ||
297 | } | ||
298 | |||
299 | ret = nouveau_object_new(nv_object(client), chan->handle, gart, | ||
300 | 0x003d, &args, sizeof(args), &object); | ||
301 | if (ret) | ||
302 | return ret; | ||
303 | } | ||
304 | |||
305 | /* initialise dma tracking parameters */ | ||
306 | switch (nv_hclass(chan->object) & 0xffff) { | ||
307 | case 0x006e: | ||
308 | chan->user_put = 0x40; | ||
309 | chan->user_get = 0x44; | ||
310 | chan->dma.max = (0x10000 / 4) - 2; | ||
311 | break; | ||
312 | default: | ||
313 | chan->user_put = 0x40; | ||
314 | chan->user_get = 0x44; | ||
315 | chan->user_get_hi = 0x60; | ||
316 | chan->dma.ib_base = 0x10000 / 4; | ||
317 | chan->dma.ib_max = (0x02000 / 8) - 1; | ||
318 | chan->dma.ib_put = 0; | ||
319 | chan->dma.ib_free = chan->dma.ib_max - chan->dma.ib_put; | ||
320 | chan->dma.max = chan->dma.ib_base; | ||
321 | break; | ||
322 | } | ||
323 | |||
324 | chan->dma.put = 0; | ||
325 | chan->dma.cur = chan->dma.put; | ||
326 | chan->dma.free = chan->dma.max - chan->dma.cur; | ||
327 | |||
328 | ret = RING_SPACE(chan, NOUVEAU_DMA_SKIPS); | ||
329 | if (ret) | ||
330 | return ret; | ||
331 | |||
332 | for (i = 0; i < NOUVEAU_DMA_SKIPS; i++) | ||
333 | OUT_RING(chan, 0x00000000); | ||
334 | |||
335 | /* allocate software object class (used for fences on <= nv05, and | ||
336 | * to signal flip completion), bind it to a subchannel. | ||
337 | */ | ||
338 | ret = nouveau_object_new(nv_object(client), chan->handle, | ||
339 | NvSw, nouveau_abi16_swclass(chan->drm), | ||
340 | NULL, 0, &object); | ||
341 | if (ret) | ||
342 | return ret; | ||
343 | |||
344 | swch = (void *)object->parent; | ||
345 | swch->flip = nouveau_flip_complete; | ||
346 | swch->flip_data = chan; | ||
347 | |||
348 | if (device->card_type < NV_C0) { | ||
349 | ret = RING_SPACE(chan, 2); | ||
350 | if (ret) | ||
351 | return ret; | ||
352 | |||
353 | BEGIN_NV04(chan, NvSubSw, 0x0000, 1); | ||
354 | OUT_RING (chan, NvSw); | ||
355 | FIRE_RING (chan); | ||
356 | } | ||
357 | |||
358 | /* initialise synchronisation */ | ||
359 | return nouveau_fence(chan->drm)->context_new(chan); | ||
360 | } | ||
361 | |||
362 | int | ||
363 | nouveau_channel_new(struct nouveau_drm *drm, struct nouveau_cli *cli, | ||
364 | u32 parent, u32 handle, u32 vram, u32 gart, | ||
365 | struct nouveau_channel **pchan) | ||
366 | { | ||
367 | int ret; | ||
368 | |||
369 | ret = nouveau_channel_ind(drm, cli, parent, handle, pchan); | ||
370 | if (ret) { | ||
371 | NV_DEBUG(drm, "ib channel create, %d\n", ret); | ||
372 | ret = nouveau_channel_dma(drm, cli, parent, handle, pchan); | ||
373 | if (ret) { | ||
374 | NV_DEBUG(drm, "dma channel create, %d\n", ret); | ||
375 | return ret; | ||
376 | } | ||
377 | } | ||
378 | |||
379 | ret = nouveau_channel_init(*pchan, vram, gart); | ||
380 | if (ret) { | ||
381 | NV_ERROR(drm, "channel failed to initialise, %d\n", ret); | ||
382 | nouveau_channel_del(pchan); | ||
383 | return ret; | ||
384 | } | ||
385 | |||
386 | return 0; | ||
387 | } | ||