Commit | Line | Data |
---|---|---|
6ee73861 | 1 | /* |
ebb945a9 | 2 | * Copyright 2012 Red Hat Inc. |
6ee73861 | 3 | * |
ebb945a9 BS |
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: | |
6ee73861 | 10 | * |
ebb945a9 BS |
11 | * The above copyright notice and this permission notice shall be included in |
12 | * all copies or substantial portions of the Software. | |
6ee73861 | 13 | * |
ebb945a9 BS |
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. | |
6ee73861 | 21 | * |
ebb945a9 | 22 | * Authors: Ben Skeggs |
6ee73861 BS |
23 | */ |
24 | ||
ebb945a9 BS |
25 | #include <core/client.h> |
26 | #include <core/engctx.h> | |
02a841d4 | 27 | #include <core/ramht.h> |
ebb945a9 BS |
28 | #include <core/class.h> |
29 | #include <core/math.h> | |
6ee73861 | 30 | |
ebb945a9 BS |
31 | #include <subdev/timer.h> |
32 | #include <subdev/bar.h> | |
c420b2dc | 33 | |
ebb945a9 BS |
34 | #include <engine/dmaobj.h> |
35 | #include <engine/fifo.h> | |
36 | ||
37 | #include "nv50.h" | |
38 | ||
39 | /******************************************************************************* | |
40 | * FIFO channel objects | |
41 | ******************************************************************************/ | |
c420b2dc BS |
42 | |
43 | void | |
ebb945a9 | 44 | nv50_fifo_playlist_update(struct nv50_fifo_priv *priv) |
6ee73861 | 45 | { |
ebb945a9 | 46 | struct nouveau_bar *bar = nouveau_bar(priv); |
a8eaebc6 | 47 | struct nouveau_gpuobj *cur; |
694931d2 | 48 | int i, p; |
6ee73861 | 49 | |
c420b2dc BS |
50 | cur = priv->playlist[priv->cur_playlist]; |
51 | priv->cur_playlist = !priv->cur_playlist; | |
6ee73861 | 52 | |
ebb945a9 BS |
53 | for (i = priv->base.min, p = 0; i < priv->base.max; i++) { |
54 | if (nv_rd32(priv, 0x002600 + (i * 4)) & 0x80000000) | |
694931d2 | 55 | nv_wo32(cur, p++ * 4, i); |
6ee73861 | 56 | } |
694931d2 | 57 | |
ebb945a9 | 58 | bar->flush(bar); |
6ee73861 | 59 | |
ebb945a9 BS |
60 | nv_wr32(priv, 0x0032f4, cur->addr >> 12); |
61 | nv_wr32(priv, 0x0032ec, p); | |
62 | nv_wr32(priv, 0x002500, 0x00000101); | |
6ee73861 BS |
63 | } |
64 | ||
c420b2dc | 65 | static int |
ebb945a9 BS |
66 | nv50_fifo_context_attach(struct nouveau_object *parent, |
67 | struct nouveau_object *object) | |
6ee73861 | 68 | { |
ebb945a9 BS |
69 | struct nouveau_bar *bar = nouveau_bar(parent); |
70 | struct nv50_fifo_base *base = (void *)parent->parent; | |
71 | struct nouveau_gpuobj *ectx = (void *)object; | |
72 | u64 limit = ectx->addr + ectx->size - 1; | |
73 | u64 start = ectx->addr; | |
74 | u32 addr; | |
75 | ||
76 | switch (nv_engidx(object->engine)) { | |
77 | case NVDEV_ENGINE_SW : return 0; | |
78 | case NVDEV_ENGINE_GR : addr = 0x0000; break; | |
79 | case NVDEV_ENGINE_MPEG : addr = 0x0060; break; | |
80 | default: | |
81 | return -EINVAL; | |
6ee73861 BS |
82 | } |
83 | ||
ebb945a9 BS |
84 | nv_wo32(base->eng, addr + 0x00, 0x00190000); |
85 | nv_wo32(base->eng, addr + 0x04, lower_32_bits(limit)); | |
86 | nv_wo32(base->eng, addr + 0x08, lower_32_bits(start)); | |
87 | nv_wo32(base->eng, addr + 0x0c, upper_32_bits(limit) << 24 | | |
88 | upper_32_bits(start)); | |
89 | nv_wo32(base->eng, addr + 0x10, 0x00000000); | |
90 | nv_wo32(base->eng, addr + 0x14, 0x00000000); | |
91 | bar->flush(bar); | |
92 | return 0; | |
6ee73861 BS |
93 | } |
94 | ||
ebb945a9 BS |
95 | static int |
96 | nv50_fifo_context_detach(struct nouveau_object *parent, bool suspend, | |
97 | struct nouveau_object *object) | |
03bd6efa | 98 | { |
ebb945a9 BS |
99 | struct nouveau_bar *bar = nouveau_bar(parent); |
100 | struct nv50_fifo_priv *priv = (void *)parent->engine; | |
101 | struct nv50_fifo_base *base = (void *)parent->parent; | |
102 | struct nv50_fifo_chan *chan = (void *)parent; | |
103 | u32 addr, me; | |
104 | int ret = 0; | |
105 | ||
106 | switch (nv_engidx(object->engine)) { | |
107 | case NVDEV_ENGINE_SW : return 0; | |
108 | case NVDEV_ENGINE_GR : addr = 0x0000; break; | |
109 | case NVDEV_ENGINE_MPEG : addr = 0x0060; break; | |
110 | default: | |
111 | return -EINVAL; | |
112 | } | |
113 | ||
114 | nv_wo32(base->eng, addr + 0x00, 0x00000000); | |
115 | nv_wo32(base->eng, addr + 0x04, 0x00000000); | |
116 | nv_wo32(base->eng, addr + 0x08, 0x00000000); | |
117 | nv_wo32(base->eng, addr + 0x0c, 0x00000000); | |
118 | nv_wo32(base->eng, addr + 0x10, 0x00000000); | |
119 | nv_wo32(base->eng, addr + 0x14, 0x00000000); | |
120 | bar->flush(bar); | |
c420b2dc BS |
121 | |
122 | /* HW bug workaround: | |
123 | * | |
124 | * PFIFO will hang forever if the connected engines don't report | |
125 | * that they've processed the context switch request. | |
126 | * | |
127 | * In order for the kickoff to work, we need to ensure all the | |
128 | * connected engines are in a state where they can answer. | |
129 | * | |
130 | * Newer chipsets don't seem to suffer from this issue, and well, | |
131 | * there's also a "ignore these engines" bitmask reg we can use | |
132 | * if we hit the issue there.. | |
133 | */ | |
ebb945a9 | 134 | me = nv_mask(priv, 0x00b860, 0x00000001, 0x00000001); |
c420b2dc BS |
135 | |
136 | /* do the kickoff... */ | |
ebb945a9 BS |
137 | nv_wr32(priv, 0x0032fc, nv_gpuobj(base)->addr >> 12); |
138 | if (!nv_wait_ne(priv, 0x0032fc, 0xffffffff, 0xffffffff)) { | |
139 | nv_error(priv, "channel %d unload timeout\n", chan->base.chid); | |
140 | if (suspend) | |
141 | ret = -EBUSY; | |
03bd6efa BS |
142 | } |
143 | ||
ebb945a9 BS |
144 | nv_wr32(priv, 0x00b860, me); |
145 | return ret; | |
03bd6efa BS |
146 | } |
147 | ||
ebb945a9 BS |
148 | static int |
149 | nv50_fifo_object_attach(struct nouveau_object *parent, | |
150 | struct nouveau_object *object, u32 handle) | |
6ee73861 | 151 | { |
ebb945a9 BS |
152 | struct nv50_fifo_chan *chan = (void *)parent; |
153 | u32 context; | |
154 | ||
155 | if (nv_iclass(object, NV_GPUOBJ_CLASS)) | |
156 | context = nv_gpuobj(object)->node->offset >> 4; | |
157 | else | |
158 | context = 0x00000004; /* just non-zero */ | |
159 | ||
160 | switch (nv_engidx(object->engine)) { | |
161 | case NVDEV_ENGINE_DMAOBJ: | |
162 | case NVDEV_ENGINE_SW : context |= 0x00000000; break; | |
163 | case NVDEV_ENGINE_GR : context |= 0x00100000; break; | |
164 | case NVDEV_ENGINE_MPEG : context |= 0x00200000; break; | |
165 | default: | |
166 | return -EINVAL; | |
d908175c | 167 | } |
03bd6efa | 168 | |
ebb945a9 BS |
169 | return nouveau_ramht_insert(chan->ramht, 0, handle, context); |
170 | } | |
171 | ||
172 | void | |
173 | nv50_fifo_object_detach(struct nouveau_object *parent, int cookie) | |
174 | { | |
175 | struct nv50_fifo_chan *chan = (void *)parent; | |
176 | nouveau_ramht_remove(chan->ramht, cookie); | |
6ee73861 BS |
177 | } |
178 | ||
c420b2dc | 179 | static int |
ebb945a9 BS |
180 | nv50_fifo_chan_ctor(struct nouveau_object *parent, |
181 | struct nouveau_object *engine, | |
182 | struct nouveau_oclass *oclass, void *data, u32 size, | |
183 | struct nouveau_object **pobject) | |
6ee73861 | 184 | { |
ebb945a9 BS |
185 | struct nv_channel_ind_class *args = data; |
186 | struct nouveau_bar *bar = nouveau_bar(parent); | |
187 | struct nv50_fifo_base *base = (void *)parent; | |
188 | struct nv50_fifo_chan *chan; | |
189 | u64 ioffset, ilength; | |
190 | int ret; | |
191 | ||
192 | if (size < sizeof(*args)) | |
193 | return -EINVAL; | |
194 | ||
195 | ret = nouveau_fifo_channel_create(parent, engine, oclass, 0, 0xc00000, | |
196 | 0x2000, args->pushbuf, | |
197 | (1 << NVDEV_ENGINE_DMAOBJ) | | |
198 | (1 << NVDEV_ENGINE_SW) | | |
199 | (1 << NVDEV_ENGINE_GR) | | |
200 | (1 << NVDEV_ENGINE_MPEG), &chan); | |
201 | *pobject = nv_object(chan); | |
202 | if (ret) | |
203 | return ret; | |
c420b2dc | 204 | |
ebb945a9 BS |
205 | nv_parent(chan)->context_attach = nv50_fifo_context_attach; |
206 | nv_parent(chan)->context_detach = nv50_fifo_context_detach; | |
207 | nv_parent(chan)->object_attach = nv50_fifo_object_attach; | |
208 | nv_parent(chan)->object_detach = nv50_fifo_object_detach; | |
c420b2dc | 209 | |
ebb945a9 BS |
210 | ret = nouveau_ramht_new(parent, parent, 0x8000, 16, &chan->ramht); |
211 | if (ret) | |
212 | return ret; | |
213 | ||
214 | ioffset = args->ioffset; | |
215 | ilength = log2i(args->ilength / 8); | |
216 | ||
217 | nv_wo32(base->ramfc, 0x3c, 0x403f6078); | |
218 | nv_wo32(base->ramfc, 0x44, 0x01003fff); | |
219 | nv_wo32(base->ramfc, 0x48, chan->base.pushgpu->node->offset >> 4); | |
220 | nv_wo32(base->ramfc, 0x50, lower_32_bits(ioffset)); | |
221 | nv_wo32(base->ramfc, 0x54, upper_32_bits(ioffset) | (ilength << 16)); | |
222 | nv_wo32(base->ramfc, 0x60, 0x7fffffff); | |
223 | nv_wo32(base->ramfc, 0x78, 0x00000000); | |
224 | nv_wo32(base->ramfc, 0x7c, 0x30000001); | |
225 | nv_wo32(base->ramfc, 0x80, ((chan->ramht->bits - 9) << 27) | | |
226 | (4 << 24) /* SEARCH_FULL */ | | |
227 | (chan->ramht->base.node->offset >> 4)); | |
228 | bar->flush(bar); | |
6ee73861 BS |
229 | return 0; |
230 | } | |
231 | ||
ebb945a9 BS |
232 | void |
233 | nv50_fifo_chan_dtor(struct nouveau_object *object) | |
234 | { | |
235 | struct nv50_fifo_chan *chan = (void *)object; | |
236 | nouveau_ramht_ref(NULL, &chan->ramht); | |
237 | nouveau_fifo_channel_destroy(&chan->base); | |
238 | } | |
239 | ||
c420b2dc | 240 | static int |
ebb945a9 | 241 | nv50_fifo_chan_init(struct nouveau_object *object) |
6ee73861 | 242 | { |
ebb945a9 BS |
243 | struct nv50_fifo_priv *priv = (void *)object->engine; |
244 | struct nv50_fifo_base *base = (void *)object->parent; | |
245 | struct nv50_fifo_chan *chan = (void *)object; | |
246 | struct nouveau_gpuobj *ramfc = base->ramfc; | |
247 | u32 chid = chan->base.chid; | |
248 | int ret; | |
6ee73861 | 249 | |
ebb945a9 BS |
250 | ret = nouveau_fifo_channel_init(&chan->base); |
251 | if (ret) | |
252 | return ret; | |
253 | ||
254 | nv_wr32(priv, 0x002600 + (chid * 4), 0x80000000 | ramfc->addr >> 12); | |
255 | nv50_fifo_playlist_update(priv); | |
6ee73861 BS |
256 | return 0; |
257 | } | |
258 | ||
ebb945a9 BS |
259 | int |
260 | nv50_fifo_chan_fini(struct nouveau_object *object, bool suspend) | |
56ac7475 | 261 | { |
ebb945a9 BS |
262 | struct nv50_fifo_priv *priv = (void *)object->engine; |
263 | struct nv50_fifo_chan *chan = (void *)object; | |
264 | u32 chid = chan->base.chid; | |
265 | ||
266 | /* remove channel from playlist, fifo will unload context */ | |
267 | nv_mask(priv, 0x002600 + (chid * 4), 0x80000000, 0x00000000); | |
268 | nv50_fifo_playlist_update(priv); | |
269 | nv_wr32(priv, 0x002600 + (chid * 4), 0x00000000); | |
270 | ||
271 | return nouveau_fifo_channel_fini(&chan->base, suspend); | |
56ac7475 | 272 | } |
c420b2dc | 273 | |
ebb945a9 BS |
274 | static struct nouveau_ofuncs |
275 | nv50_fifo_ofuncs = { | |
276 | .ctor = nv50_fifo_chan_ctor, | |
277 | .dtor = nv50_fifo_chan_dtor, | |
278 | .init = nv50_fifo_chan_init, | |
279 | .fini = nv50_fifo_chan_fini, | |
280 | .rd32 = _nouveau_fifo_channel_rd32, | |
281 | .wr32 = _nouveau_fifo_channel_wr32, | |
282 | }; | |
283 | ||
284 | static struct nouveau_oclass | |
285 | nv50_fifo_sclass[] = { | |
286 | { 0x506f, &nv50_fifo_ofuncs }, | |
287 | {} | |
288 | }; | |
289 | ||
290 | /******************************************************************************* | |
291 | * FIFO context - basically just the instmem reserved for the channel | |
292 | ******************************************************************************/ | |
293 | ||
294 | static int | |
295 | nv50_fifo_context_ctor(struct nouveau_object *parent, | |
296 | struct nouveau_object *engine, | |
297 | struct nouveau_oclass *oclass, void *data, u32 size, | |
298 | struct nouveau_object **pobject) | |
c420b2dc | 299 | { |
ebb945a9 BS |
300 | struct nv50_fifo_base *base; |
301 | int ret; | |
c420b2dc | 302 | |
ebb945a9 BS |
303 | ret = nouveau_fifo_context_create(parent, engine, oclass, NULL, 0x10000, |
304 | 0x1000, NVOBJ_FLAG_HEAP, &base); | |
305 | *pobject = nv_object(base); | |
306 | if (ret) | |
307 | return ret; | |
c420b2dc | 308 | |
ebb945a9 BS |
309 | ret = nouveau_gpuobj_new(parent, nv_object(base), 0x0200, 0x1000, |
310 | NVOBJ_FLAG_ZERO_ALLOC, &base->ramfc); | |
311 | if (ret) | |
312 | return ret; | |
313 | ||
314 | ret = nouveau_gpuobj_new(parent, nv_object(base), 0x1200, 0, | |
315 | NVOBJ_FLAG_ZERO_ALLOC, &base->eng); | |
316 | if (ret) | |
317 | return ret; | |
318 | ||
319 | ret = nouveau_gpuobj_new(parent, nv_object(base), 0x4000, 0, 0, | |
320 | &base->pgd); | |
321 | if (ret) | |
322 | return ret; | |
323 | ||
324 | ret = nouveau_vm_ref(nouveau_client(parent)->vm, &base->vm, base->pgd); | |
325 | if (ret) | |
326 | return ret; | |
c420b2dc | 327 | |
ebb945a9 | 328 | return 0; |
c420b2dc BS |
329 | } |
330 | ||
ebb945a9 BS |
331 | void |
332 | nv50_fifo_context_dtor(struct nouveau_object *object) | |
333 | { | |
334 | struct nv50_fifo_base *base = (void *)object; | |
335 | nouveau_vm_ref(NULL, &base->vm, base->pgd); | |
336 | nouveau_gpuobj_ref(NULL, &base->pgd); | |
337 | nouveau_gpuobj_ref(NULL, &base->eng); | |
338 | nouveau_gpuobj_ref(NULL, &base->ramfc); | |
339 | nouveau_gpuobj_ref(NULL, &base->cache); | |
340 | nouveau_fifo_context_destroy(&base->base); | |
341 | } | |
342 | ||
343 | static struct nouveau_oclass | |
344 | nv50_fifo_cclass = { | |
345 | .handle = NV_ENGCTX(FIFO, 0x50), | |
346 | .ofuncs = &(struct nouveau_ofuncs) { | |
347 | .ctor = nv50_fifo_context_ctor, | |
348 | .dtor = nv50_fifo_context_dtor, | |
349 | .init = _nouveau_fifo_context_init, | |
350 | .fini = _nouveau_fifo_context_fini, | |
351 | .rd32 = _nouveau_fifo_context_rd32, | |
352 | .wr32 = _nouveau_fifo_context_wr32, | |
353 | }, | |
354 | }; | |
355 | ||
356 | /******************************************************************************* | |
357 | * PFIFO engine | |
358 | ******************************************************************************/ | |
359 | ||
360 | static int | |
361 | nv50_fifo_ctor(struct nouveau_object *parent, struct nouveau_object *engine, | |
362 | struct nouveau_oclass *oclass, void *data, u32 size, | |
363 | struct nouveau_object **pobject) | |
c420b2dc | 364 | { |
c420b2dc BS |
365 | struct nv50_fifo_priv *priv; |
366 | int ret; | |
367 | ||
ebb945a9 BS |
368 | ret = nouveau_fifo_create(parent, engine, oclass, 1, 127, &priv); |
369 | *pobject = nv_object(priv); | |
c420b2dc | 370 | if (ret) |
ebb945a9 | 371 | return ret; |
c420b2dc | 372 | |
ebb945a9 BS |
373 | ret = nouveau_gpuobj_new(parent, NULL, 128 * 4, 0x1000, 0, |
374 | &priv->playlist[0]); | |
c420b2dc | 375 | if (ret) |
ebb945a9 | 376 | return ret; |
c420b2dc | 377 | |
ebb945a9 BS |
378 | ret = nouveau_gpuobj_new(parent, NULL, 128 * 4, 0x1000, 0, |
379 | &priv->playlist[1]); | |
c420b2dc | 380 | if (ret) |
ebb945a9 BS |
381 | return ret; |
382 | ||
383 | nv_subdev(priv)->unit = 0x00000100; | |
384 | nv_subdev(priv)->intr = nv04_fifo_intr; | |
385 | nv_engine(priv)->cclass = &nv50_fifo_cclass; | |
386 | nv_engine(priv)->sclass = nv50_fifo_sclass; | |
387 | return 0; | |
388 | } | |
389 | ||
390 | void | |
391 | nv50_fifo_dtor(struct nouveau_object *object) | |
392 | { | |
393 | struct nv50_fifo_priv *priv = (void *)object; | |
394 | ||
395 | nouveau_gpuobj_ref(NULL, &priv->playlist[1]); | |
396 | nouveau_gpuobj_ref(NULL, &priv->playlist[0]); | |
397 | ||
398 | nouveau_fifo_destroy(&priv->base); | |
c420b2dc | 399 | } |
ebb945a9 BS |
400 | |
401 | int | |
402 | nv50_fifo_init(struct nouveau_object *object) | |
403 | { | |
404 | struct nv50_fifo_priv *priv = (void *)object; | |
405 | int ret, i; | |
406 | ||
407 | ret = nouveau_fifo_init(&priv->base); | |
408 | if (ret) | |
409 | return ret; | |
410 | ||
411 | nv_mask(priv, 0x000200, 0x00000100, 0x00000000); | |
412 | nv_mask(priv, 0x000200, 0x00000100, 0x00000100); | |
413 | nv_wr32(priv, 0x00250c, 0x6f3cfc34); | |
414 | nv_wr32(priv, 0x002044, 0x01003fff); | |
415 | ||
416 | nv_wr32(priv, 0x002100, 0xffffffff); | |
417 | nv_wr32(priv, 0x002140, 0xffffffff); | |
418 | ||
419 | for (i = 0; i < 128; i++) | |
420 | nv_wr32(priv, 0x002600 + (i * 4), 0x00000000); | |
421 | nv50_fifo_playlist_update(priv); | |
422 | ||
423 | nv_wr32(priv, 0x003200, 0x00000001); | |
424 | nv_wr32(priv, 0x003250, 0x00000001); | |
425 | nv_wr32(priv, 0x002500, 0x00000001); | |
426 | return 0; | |
427 | } | |
428 | ||
429 | struct nouveau_oclass | |
430 | nv50_fifo_oclass = { | |
431 | .handle = NV_ENGINE(FIFO, 0x50), | |
432 | .ofuncs = &(struct nouveau_ofuncs) { | |
433 | .ctor = nv50_fifo_ctor, | |
434 | .dtor = nv50_fifo_dtor, | |
435 | .init = nv50_fifo_init, | |
436 | .fini = _nouveau_fifo_fini, | |
437 | }, | |
438 | }; |