Commit | Line | Data |
---|---|---|
ebb945a9 BS |
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 | ||
370c00f9 BS |
25 | #include <core/object.h> |
26 | #include <core/parent.h> | |
27 | #include <core/handle.h> | |
28 | #include <core/class.h> | |
29 | ||
ebb945a9 BS |
30 | #include <engine/software.h> |
31 | #include <engine/disp.h> | |
32 | ||
446b05a0 | 33 | #include <subdev/timer.h> |
370c00f9 BS |
34 | #include <subdev/fb.h> |
35 | #include <subdev/bar.h> | |
446b05a0 | 36 | |
70cabe4a BS |
37 | #include "nv50.h" |
38 | ||
39 | /******************************************************************************* | |
370c00f9 | 40 | * EVO channel base class |
70cabe4a BS |
41 | ******************************************************************************/ |
42 | ||
370c00f9 BS |
43 | int |
44 | nv50_disp_chan_create_(struct nouveau_object *parent, | |
45 | struct nouveau_object *engine, | |
46 | struct nouveau_oclass *oclass, int chid, | |
47 | int length, void **pobject) | |
48 | { | |
49 | struct nv50_disp_base *base = (void *)parent; | |
50 | struct nv50_disp_chan *chan; | |
51 | int ret; | |
52 | ||
53 | if (base->chan & (1 << chid)) | |
54 | return -EBUSY; | |
55 | base->chan |= (1 << chid); | |
56 | ||
57 | ret = nouveau_namedb_create_(parent, engine, oclass, 0, NULL, | |
58 | (1ULL << NVDEV_ENGINE_DMAOBJ), | |
59 | length, pobject); | |
60 | chan = *pobject; | |
61 | if (ret) | |
62 | return ret; | |
63 | ||
64 | chan->chid = chid; | |
65 | return 0; | |
66 | } | |
67 | ||
68 | void | |
69 | nv50_disp_chan_destroy(struct nv50_disp_chan *chan) | |
70 | { | |
71 | struct nv50_disp_base *base = (void *)nv_object(chan)->parent; | |
72 | base->chan &= ~(1 << chan->chid); | |
73 | nouveau_namedb_destroy(&chan->base); | |
74 | } | |
75 | ||
76 | u32 | |
70cabe4a BS |
77 | nv50_disp_chan_rd32(struct nouveau_object *object, u64 addr) |
78 | { | |
370c00f9 BS |
79 | struct nv50_disp_priv *priv = (void *)object->engine; |
80 | struct nv50_disp_chan *chan = (void *)object; | |
81 | return nv_rd32(priv, 0x640000 + (chan->chid * 0x1000) + addr); | |
70cabe4a BS |
82 | } |
83 | ||
370c00f9 | 84 | void |
70cabe4a BS |
85 | nv50_disp_chan_wr32(struct nouveau_object *object, u64 addr, u32 data) |
86 | { | |
370c00f9 BS |
87 | struct nv50_disp_priv *priv = (void *)object->engine; |
88 | struct nv50_disp_chan *chan = (void *)object; | |
89 | nv_wr32(priv, 0x640000 + (chan->chid * 0x1000) + addr, data); | |
90 | } | |
91 | ||
92 | /******************************************************************************* | |
93 | * EVO DMA channel base class | |
94 | ******************************************************************************/ | |
95 | ||
96 | static int | |
97 | nv50_disp_dmac_object_attach(struct nouveau_object *parent, | |
98 | struct nouveau_object *object, u32 name) | |
99 | { | |
100 | struct nv50_disp_base *base = (void *)parent->parent; | |
101 | struct nv50_disp_chan *chan = (void *)parent; | |
102 | u32 addr = nv_gpuobj(object)->node->offset; | |
103 | u32 chid = chan->chid; | |
104 | u32 data = (chid << 28) | (addr << 10) | chid; | |
105 | return nouveau_ramht_insert(base->ramht, chid, name, data); | |
106 | } | |
107 | ||
108 | static void | |
109 | nv50_disp_dmac_object_detach(struct nouveau_object *parent, int cookie) | |
110 | { | |
111 | struct nv50_disp_base *base = (void *)parent->parent; | |
112 | nouveau_ramht_remove(base->ramht, cookie); | |
113 | } | |
114 | ||
115 | int | |
116 | nv50_disp_dmac_create_(struct nouveau_object *parent, | |
117 | struct nouveau_object *engine, | |
118 | struct nouveau_oclass *oclass, u32 pushbuf, int chid, | |
119 | int length, void **pobject) | |
120 | { | |
121 | struct nv50_disp_dmac *dmac; | |
122 | int ret; | |
123 | ||
124 | ret = nv50_disp_chan_create_(parent, engine, oclass, chid, | |
125 | length, pobject); | |
126 | dmac = *pobject; | |
127 | if (ret) | |
128 | return ret; | |
129 | ||
130 | dmac->pushdma = (void *)nouveau_handle_ref(parent, pushbuf); | |
131 | if (!dmac->pushdma) | |
132 | return -ENOENT; | |
133 | ||
134 | switch (nv_mclass(dmac->pushdma)) { | |
135 | case 0x0002: | |
136 | case 0x003d: | |
137 | if (dmac->pushdma->limit - dmac->pushdma->start != 0xfff) | |
138 | return -EINVAL; | |
139 | ||
140 | switch (dmac->pushdma->target) { | |
141 | case NV_MEM_TARGET_VRAM: | |
142 | dmac->push = 0x00000000 | dmac->pushdma->start >> 8; | |
143 | break; | |
944234d6 BS |
144 | case NV_MEM_TARGET_PCI_NOSNOOP: |
145 | dmac->push = 0x00000003 | dmac->pushdma->start >> 8; | |
146 | break; | |
370c00f9 BS |
147 | default: |
148 | return -EINVAL; | |
149 | } | |
150 | break; | |
151 | default: | |
152 | return -EINVAL; | |
153 | } | |
154 | ||
155 | return 0; | |
156 | } | |
157 | ||
158 | void | |
159 | nv50_disp_dmac_dtor(struct nouveau_object *object) | |
160 | { | |
161 | struct nv50_disp_dmac *dmac = (void *)object; | |
162 | nouveau_object_ref(NULL, (struct nouveau_object **)&dmac->pushdma); | |
163 | nv50_disp_chan_destroy(&dmac->base); | |
164 | } | |
165 | ||
166 | static int | |
167 | nv50_disp_dmac_init(struct nouveau_object *object) | |
168 | { | |
169 | struct nv50_disp_priv *priv = (void *)object->engine; | |
170 | struct nv50_disp_dmac *dmac = (void *)object; | |
171 | int chid = dmac->base.chid; | |
172 | int ret; | |
173 | ||
174 | ret = nv50_disp_chan_init(&dmac->base); | |
175 | if (ret) | |
176 | return ret; | |
177 | ||
178 | /* enable error reporting */ | |
179 | nv_mask(priv, 0x610028, 0x00010001 << chid, 0x00010001 << chid); | |
180 | ||
181 | /* initialise channel for dma command submission */ | |
182 | nv_wr32(priv, 0x610204 + (chid * 0x0010), dmac->push); | |
183 | nv_wr32(priv, 0x610208 + (chid * 0x0010), 0x00010000); | |
184 | nv_wr32(priv, 0x61020c + (chid * 0x0010), chid); | |
185 | nv_mask(priv, 0x610200 + (chid * 0x0010), 0x00000010, 0x00000010); | |
186 | nv_wr32(priv, 0x640000 + (chid * 0x1000), 0x00000000); | |
187 | nv_wr32(priv, 0x610200 + (chid * 0x0010), 0x00000013); | |
188 | ||
189 | /* wait for it to go inactive */ | |
190 | if (!nv_wait(priv, 0x610200 + (chid * 0x10), 0x80000000, 0x00000000)) { | |
191 | nv_error(dmac, "init timeout, 0x%08x\n", | |
192 | nv_rd32(priv, 0x610200 + (chid * 0x10))); | |
193 | return -EBUSY; | |
194 | } | |
195 | ||
196 | return 0; | |
197 | } | |
198 | ||
199 | static int | |
200 | nv50_disp_dmac_fini(struct nouveau_object *object, bool suspend) | |
201 | { | |
202 | struct nv50_disp_priv *priv = (void *)object->engine; | |
203 | struct nv50_disp_dmac *dmac = (void *)object; | |
204 | int chid = dmac->base.chid; | |
205 | ||
206 | /* deactivate channel */ | |
207 | nv_mask(priv, 0x610200 + (chid * 0x0010), 0x00001010, 0x00001000); | |
208 | nv_mask(priv, 0x610200 + (chid * 0x0010), 0x00000003, 0x00000000); | |
209 | if (!nv_wait(priv, 0x610200 + (chid * 0x10), 0x001e0000, 0x00000000)) { | |
210 | nv_error(dmac, "fini timeout, 0x%08x\n", | |
211 | nv_rd32(priv, 0x610200 + (chid * 0x10))); | |
212 | if (suspend) | |
213 | return -EBUSY; | |
214 | } | |
215 | ||
216 | /* disable error reporting */ | |
217 | nv_mask(priv, 0x610028, 0x00010001 << chid, 0x00000000 << chid); | |
218 | ||
219 | return nv50_disp_chan_fini(&dmac->base, suspend); | |
70cabe4a BS |
220 | } |
221 | ||
222 | /******************************************************************************* | |
223 | * EVO master channel object | |
224 | ******************************************************************************/ | |
225 | ||
226 | static int | |
227 | nv50_disp_mast_ctor(struct nouveau_object *parent, | |
228 | struct nouveau_object *engine, | |
229 | struct nouveau_oclass *oclass, void *data, u32 size, | |
230 | struct nouveau_object **pobject) | |
231 | { | |
370c00f9 BS |
232 | struct nv50_display_mast_class *args = data; |
233 | struct nv50_disp_dmac *mast; | |
70cabe4a BS |
234 | int ret; |
235 | ||
370c00f9 BS |
236 | if (size < sizeof(*args)) |
237 | return -EINVAL; | |
238 | ||
239 | ret = nv50_disp_dmac_create_(parent, engine, oclass, args->pushbuf, | |
240 | 0, sizeof(*mast), (void **)&mast); | |
241 | *pobject = nv_object(mast); | |
70cabe4a BS |
242 | if (ret) |
243 | return ret; | |
244 | ||
370c00f9 BS |
245 | nv_parent(mast)->object_attach = nv50_disp_dmac_object_attach; |
246 | nv_parent(mast)->object_detach = nv50_disp_dmac_object_detach; | |
70cabe4a BS |
247 | return 0; |
248 | } | |
249 | ||
70cabe4a BS |
250 | static int |
251 | nv50_disp_mast_init(struct nouveau_object *object) | |
252 | { | |
370c00f9 BS |
253 | struct nv50_disp_priv *priv = (void *)object->engine; |
254 | struct nv50_disp_dmac *mast = (void *)object; | |
70cabe4a BS |
255 | int ret; |
256 | ||
370c00f9 | 257 | ret = nv50_disp_chan_init(&mast->base); |
70cabe4a BS |
258 | if (ret) |
259 | return ret; | |
260 | ||
370c00f9 BS |
261 | /* enable error reporting */ |
262 | nv_mask(priv, 0x610028, 0x00010001, 0x00010001); | |
263 | ||
264 | /* attempt to unstick channel from some unknown state */ | |
265 | if ((nv_rd32(priv, 0x610200) & 0x009f0000) == 0x00020000) | |
266 | nv_mask(priv, 0x610200, 0x00800000, 0x00800000); | |
267 | if ((nv_rd32(priv, 0x610200) & 0x003f0000) == 0x00030000) | |
268 | nv_mask(priv, 0x610200, 0x00600000, 0x00600000); | |
269 | ||
270 | /* initialise channel for dma command submission */ | |
271 | nv_wr32(priv, 0x610204, mast->push); | |
272 | nv_wr32(priv, 0x610208, 0x00010000); | |
273 | nv_wr32(priv, 0x61020c, 0x00000000); | |
274 | nv_mask(priv, 0x610200, 0x00000010, 0x00000010); | |
275 | nv_wr32(priv, 0x640000, 0x00000000); | |
276 | nv_wr32(priv, 0x610200, 0x01000013); | |
277 | ||
278 | /* wait for it to go inactive */ | |
279 | if (!nv_wait(priv, 0x610200, 0x80000000, 0x00000000)) { | |
280 | nv_error(mast, "init: 0x%08x\n", nv_rd32(priv, 0x610200)); | |
281 | return -EBUSY; | |
282 | } | |
283 | ||
70cabe4a BS |
284 | return 0; |
285 | } | |
286 | ||
287 | static int | |
288 | nv50_disp_mast_fini(struct nouveau_object *object, bool suspend) | |
289 | { | |
370c00f9 BS |
290 | struct nv50_disp_priv *priv = (void *)object->engine; |
291 | struct nv50_disp_dmac *mast = (void *)object; | |
292 | ||
293 | /* deactivate channel */ | |
294 | nv_mask(priv, 0x610200, 0x00000010, 0x00000000); | |
295 | nv_mask(priv, 0x610200, 0x00000003, 0x00000000); | |
296 | if (!nv_wait(priv, 0x610200, 0x001e0000, 0x00000000)) { | |
297 | nv_error(mast, "fini: 0x%08x\n", nv_rd32(priv, 0x610200)); | |
298 | if (suspend) | |
299 | return -EBUSY; | |
300 | } | |
301 | ||
302 | /* disable error reporting */ | |
303 | nv_mask(priv, 0x610028, 0x00010001, 0x00000000); | |
304 | ||
305 | return nv50_disp_chan_fini(&mast->base, suspend); | |
70cabe4a BS |
306 | } |
307 | ||
308 | struct nouveau_ofuncs | |
309 | nv50_disp_mast_ofuncs = { | |
310 | .ctor = nv50_disp_mast_ctor, | |
370c00f9 | 311 | .dtor = nv50_disp_dmac_dtor, |
70cabe4a BS |
312 | .init = nv50_disp_mast_init, |
313 | .fini = nv50_disp_mast_fini, | |
314 | .rd32 = nv50_disp_chan_rd32, | |
315 | .wr32 = nv50_disp_chan_wr32, | |
316 | }; | |
317 | ||
318 | /******************************************************************************* | |
370c00f9 | 319 | * EVO sync channel objects |
70cabe4a BS |
320 | ******************************************************************************/ |
321 | ||
322 | static int | |
370c00f9 | 323 | nv50_disp_sync_ctor(struct nouveau_object *parent, |
70cabe4a BS |
324 | struct nouveau_object *engine, |
325 | struct nouveau_oclass *oclass, void *data, u32 size, | |
326 | struct nouveau_object **pobject) | |
327 | { | |
370c00f9 BS |
328 | struct nv50_display_sync_class *args = data; |
329 | struct nv50_disp_dmac *dmac; | |
70cabe4a BS |
330 | int ret; |
331 | ||
370c00f9 BS |
332 | if (size < sizeof(*data) || args->head > 1) |
333 | return -EINVAL; | |
334 | ||
335 | ret = nv50_disp_dmac_create_(parent, engine, oclass, args->pushbuf, | |
336 | 1 + args->head, sizeof(*dmac), | |
337 | (void **)&dmac); | |
338 | *pobject = nv_object(dmac); | |
70cabe4a BS |
339 | if (ret) |
340 | return ret; | |
341 | ||
370c00f9 BS |
342 | nv_parent(dmac)->object_attach = nv50_disp_dmac_object_attach; |
343 | nv_parent(dmac)->object_detach = nv50_disp_dmac_object_detach; | |
70cabe4a BS |
344 | return 0; |
345 | } | |
346 | ||
370c00f9 BS |
347 | struct nouveau_ofuncs |
348 | nv50_disp_sync_ofuncs = { | |
349 | .ctor = nv50_disp_sync_ctor, | |
350 | .dtor = nv50_disp_dmac_dtor, | |
351 | .init = nv50_disp_dmac_init, | |
352 | .fini = nv50_disp_dmac_fini, | |
353 | .rd32 = nv50_disp_chan_rd32, | |
354 | .wr32 = nv50_disp_chan_wr32, | |
355 | }; | |
356 | ||
357 | /******************************************************************************* | |
358 | * EVO overlay channel objects | |
359 | ******************************************************************************/ | |
70cabe4a BS |
360 | |
361 | static int | |
370c00f9 BS |
362 | nv50_disp_ovly_ctor(struct nouveau_object *parent, |
363 | struct nouveau_object *engine, | |
364 | struct nouveau_oclass *oclass, void *data, u32 size, | |
365 | struct nouveau_object **pobject) | |
70cabe4a | 366 | { |
370c00f9 BS |
367 | struct nv50_display_ovly_class *args = data; |
368 | struct nv50_disp_dmac *dmac; | |
70cabe4a BS |
369 | int ret; |
370 | ||
370c00f9 BS |
371 | if (size < sizeof(*data) || args->head > 1) |
372 | return -EINVAL; | |
373 | ||
374 | ret = nv50_disp_dmac_create_(parent, engine, oclass, args->pushbuf, | |
375 | 3 + args->head, sizeof(*dmac), | |
376 | (void **)&dmac); | |
377 | *pobject = nv_object(dmac); | |
70cabe4a BS |
378 | if (ret) |
379 | return ret; | |
380 | ||
370c00f9 BS |
381 | nv_parent(dmac)->object_attach = nv50_disp_dmac_object_attach; |
382 | nv_parent(dmac)->object_detach = nv50_disp_dmac_object_detach; | |
70cabe4a BS |
383 | return 0; |
384 | } | |
385 | ||
70cabe4a | 386 | struct nouveau_ofuncs |
370c00f9 BS |
387 | nv50_disp_ovly_ofuncs = { |
388 | .ctor = nv50_disp_ovly_ctor, | |
70cabe4a BS |
389 | .dtor = nv50_disp_dmac_dtor, |
390 | .init = nv50_disp_dmac_init, | |
391 | .fini = nv50_disp_dmac_fini, | |
392 | .rd32 = nv50_disp_chan_rd32, | |
393 | .wr32 = nv50_disp_chan_wr32, | |
394 | }; | |
395 | ||
396 | /******************************************************************************* | |
370c00f9 | 397 | * EVO PIO channel base class |
70cabe4a BS |
398 | ******************************************************************************/ |
399 | ||
400 | static int | |
370c00f9 BS |
401 | nv50_disp_pioc_create_(struct nouveau_object *parent, |
402 | struct nouveau_object *engine, | |
403 | struct nouveau_oclass *oclass, int chid, | |
404 | int length, void **pobject) | |
70cabe4a | 405 | { |
370c00f9 BS |
406 | return nv50_disp_chan_create_(parent, engine, oclass, chid, |
407 | length, pobject); | |
408 | } | |
409 | ||
410 | static void | |
411 | nv50_disp_pioc_dtor(struct nouveau_object *object) | |
412 | { | |
413 | struct nv50_disp_pioc *pioc = (void *)object; | |
414 | nv50_disp_chan_destroy(&pioc->base); | |
415 | } | |
416 | ||
417 | static int | |
418 | nv50_disp_pioc_init(struct nouveau_object *object) | |
419 | { | |
420 | struct nv50_disp_priv *priv = (void *)object->engine; | |
421 | struct nv50_disp_pioc *pioc = (void *)object; | |
422 | int chid = pioc->base.chid; | |
70cabe4a BS |
423 | int ret; |
424 | ||
370c00f9 | 425 | ret = nv50_disp_chan_init(&pioc->base); |
70cabe4a BS |
426 | if (ret) |
427 | return ret; | |
428 | ||
370c00f9 BS |
429 | nv_wr32(priv, 0x610200 + (chid * 0x10), 0x00002000); |
430 | if (!nv_wait(priv, 0x610200 + (chid * 0x10), 0x00000000, 0x00000000)) { | |
431 | nv_error(pioc, "timeout0: 0x%08x\n", | |
432 | nv_rd32(priv, 0x610200 + (chid * 0x10))); | |
433 | return -EBUSY; | |
434 | } | |
435 | ||
436 | nv_wr32(priv, 0x610200 + (chid * 0x10), 0x00000001); | |
437 | if (!nv_wait(priv, 0x610200 + (chid * 0x10), 0x00030000, 0x00010000)) { | |
438 | nv_error(pioc, "timeout1: 0x%08x\n", | |
439 | nv_rd32(priv, 0x610200 + (chid * 0x10))); | |
440 | return -EBUSY; | |
441 | } | |
442 | ||
70cabe4a BS |
443 | return 0; |
444 | } | |
445 | ||
370c00f9 BS |
446 | static int |
447 | nv50_disp_pioc_fini(struct nouveau_object *object, bool suspend) | |
70cabe4a | 448 | { |
370c00f9 BS |
449 | struct nv50_disp_priv *priv = (void *)object->engine; |
450 | struct nv50_disp_pioc *pioc = (void *)object; | |
451 | int chid = pioc->base.chid; | |
452 | ||
453 | nv_mask(priv, 0x610200 + (chid * 0x10), 0x00000001, 0x00000000); | |
454 | if (!nv_wait(priv, 0x610200 + (chid * 0x10), 0x00030000, 0x00000000)) { | |
455 | nv_error(pioc, "timeout: 0x%08x\n", | |
456 | nv_rd32(priv, 0x610200 + (chid * 0x10))); | |
457 | if (suspend) | |
458 | return -EBUSY; | |
459 | } | |
460 | ||
461 | return nv50_disp_chan_fini(&pioc->base, suspend); | |
70cabe4a BS |
462 | } |
463 | ||
370c00f9 BS |
464 | /******************************************************************************* |
465 | * EVO immediate overlay channel objects | |
466 | ******************************************************************************/ | |
467 | ||
70cabe4a | 468 | static int |
370c00f9 BS |
469 | nv50_disp_oimm_ctor(struct nouveau_object *parent, |
470 | struct nouveau_object *engine, | |
471 | struct nouveau_oclass *oclass, void *data, u32 size, | |
472 | struct nouveau_object **pobject) | |
70cabe4a | 473 | { |
370c00f9 BS |
474 | struct nv50_display_oimm_class *args = data; |
475 | struct nv50_disp_pioc *pioc; | |
70cabe4a BS |
476 | int ret; |
477 | ||
370c00f9 BS |
478 | if (size < sizeof(*args) || args->head > 1) |
479 | return -EINVAL; | |
480 | ||
481 | ret = nv50_disp_pioc_create_(parent, engine, oclass, 5 + args->head, | |
482 | sizeof(*pioc), (void **)&pioc); | |
483 | *pobject = nv_object(pioc); | |
70cabe4a BS |
484 | if (ret) |
485 | return ret; | |
486 | ||
487 | return 0; | |
488 | } | |
489 | ||
370c00f9 BS |
490 | struct nouveau_ofuncs |
491 | nv50_disp_oimm_ofuncs = { | |
492 | .ctor = nv50_disp_oimm_ctor, | |
493 | .dtor = nv50_disp_pioc_dtor, | |
494 | .init = nv50_disp_pioc_init, | |
495 | .fini = nv50_disp_pioc_fini, | |
496 | .rd32 = nv50_disp_chan_rd32, | |
497 | .wr32 = nv50_disp_chan_wr32, | |
498 | }; | |
499 | ||
500 | /******************************************************************************* | |
501 | * EVO cursor channel objects | |
502 | ******************************************************************************/ | |
503 | ||
70cabe4a | 504 | static int |
370c00f9 BS |
505 | nv50_disp_curs_ctor(struct nouveau_object *parent, |
506 | struct nouveau_object *engine, | |
507 | struct nouveau_oclass *oclass, void *data, u32 size, | |
508 | struct nouveau_object **pobject) | |
70cabe4a | 509 | { |
370c00f9 BS |
510 | struct nv50_display_curs_class *args = data; |
511 | struct nv50_disp_pioc *pioc; | |
512 | int ret; | |
513 | ||
514 | if (size < sizeof(*args) || args->head > 1) | |
515 | return -EINVAL; | |
516 | ||
517 | ret = nv50_disp_pioc_create_(parent, engine, oclass, 7 + args->head, | |
518 | sizeof(*pioc), (void **)&pioc); | |
519 | *pobject = nv_object(pioc); | |
520 | if (ret) | |
521 | return ret; | |
522 | ||
523 | return 0; | |
70cabe4a BS |
524 | } |
525 | ||
526 | struct nouveau_ofuncs | |
370c00f9 BS |
527 | nv50_disp_curs_ofuncs = { |
528 | .ctor = nv50_disp_curs_ctor, | |
70cabe4a BS |
529 | .dtor = nv50_disp_pioc_dtor, |
530 | .init = nv50_disp_pioc_init, | |
531 | .fini = nv50_disp_pioc_fini, | |
532 | .rd32 = nv50_disp_chan_rd32, | |
533 | .wr32 = nv50_disp_chan_wr32, | |
534 | }; | |
535 | ||
536 | /******************************************************************************* | |
537 | * Base display object | |
538 | ******************************************************************************/ | |
539 | ||
540 | static int | |
541 | nv50_disp_base_ctor(struct nouveau_object *parent, | |
542 | struct nouveau_object *engine, | |
543 | struct nouveau_oclass *oclass, void *data, u32 size, | |
544 | struct nouveau_object **pobject) | |
545 | { | |
546 | struct nv50_disp_priv *priv = (void *)engine; | |
547 | struct nv50_disp_base *base; | |
548 | int ret; | |
549 | ||
550 | ret = nouveau_parent_create(parent, engine, oclass, 0, | |
551 | priv->sclass, 0, &base); | |
552 | *pobject = nv_object(base); | |
553 | if (ret) | |
554 | return ret; | |
555 | ||
370c00f9 | 556 | return nouveau_ramht_new(parent, parent, 0x1000, 0, &base->ramht); |
70cabe4a BS |
557 | } |
558 | ||
559 | static void | |
560 | nv50_disp_base_dtor(struct nouveau_object *object) | |
561 | { | |
562 | struct nv50_disp_base *base = (void *)object; | |
370c00f9 | 563 | nouveau_ramht_ref(NULL, &base->ramht); |
70cabe4a BS |
564 | nouveau_parent_destroy(&base->base); |
565 | } | |
566 | ||
567 | static int | |
568 | nv50_disp_base_init(struct nouveau_object *object) | |
569 | { | |
ab77214a | 570 | struct nv50_disp_priv *priv = (void *)object->engine; |
70cabe4a | 571 | struct nv50_disp_base *base = (void *)object; |
ab77214a BS |
572 | int ret, i; |
573 | u32 tmp; | |
70cabe4a BS |
574 | |
575 | ret = nouveau_parent_init(&base->base); | |
576 | if (ret) | |
577 | return ret; | |
578 | ||
ab77214a BS |
579 | /* The below segments of code copying values from one register to |
580 | * another appear to inform EVO of the display capabilities or | |
581 | * something similar. NFI what the 0x614004 caps are for.. | |
582 | */ | |
583 | tmp = nv_rd32(priv, 0x614004); | |
584 | nv_wr32(priv, 0x610184, tmp); | |
585 | ||
586 | /* ... CRTC caps */ | |
587 | for (i = 0; i < priv->head.nr; i++) { | |
588 | tmp = nv_rd32(priv, 0x616100 + (i * 0x800)); | |
589 | nv_wr32(priv, 0x610190 + (i * 0x10), tmp); | |
590 | tmp = nv_rd32(priv, 0x616104 + (i * 0x800)); | |
591 | nv_wr32(priv, 0x610194 + (i * 0x10), tmp); | |
592 | tmp = nv_rd32(priv, 0x616108 + (i * 0x800)); | |
593 | nv_wr32(priv, 0x610198 + (i * 0x10), tmp); | |
594 | tmp = nv_rd32(priv, 0x61610c + (i * 0x800)); | |
595 | nv_wr32(priv, 0x61019c + (i * 0x10), tmp); | |
596 | } | |
597 | ||
598 | /* ... DAC caps */ | |
599 | for (i = 0; i < priv->dac.nr; i++) { | |
600 | tmp = nv_rd32(priv, 0x61a000 + (i * 0x800)); | |
601 | nv_wr32(priv, 0x6101d0 + (i * 0x04), tmp); | |
602 | } | |
603 | ||
604 | /* ... SOR caps */ | |
605 | for (i = 0; i < priv->sor.nr; i++) { | |
606 | tmp = nv_rd32(priv, 0x61c000 + (i * 0x800)); | |
607 | nv_wr32(priv, 0x6101e0 + (i * 0x04), tmp); | |
608 | } | |
609 | ||
610 | /* ... EXT caps */ | |
611 | for (i = 0; i < 3; i++) { | |
612 | tmp = nv_rd32(priv, 0x61e000 + (i * 0x800)); | |
613 | nv_wr32(priv, 0x6101f0 + (i * 0x04), tmp); | |
614 | } | |
615 | ||
446b05a0 BS |
616 | /* steal display away from vbios, or something like that */ |
617 | if (nv_rd32(priv, 0x610024) & 0x00000100) { | |
618 | nv_wr32(priv, 0x610024, 0x00000100); | |
619 | nv_mask(priv, 0x6194e8, 0x00000001, 0x00000000); | |
620 | if (!nv_wait(priv, 0x6194e8, 0x00000002, 0x00000000)) { | |
621 | nv_error(priv, "timeout acquiring display\n"); | |
622 | return -EBUSY; | |
623 | } | |
624 | } | |
625 | ||
626 | /* point at display engine memory area (hash table, objects) */ | |
370c00f9 | 627 | nv_wr32(priv, 0x610010, (nv_gpuobj(base->ramht)->addr >> 8) | 9); |
446b05a0 BS |
628 | |
629 | /* enable supervisor interrupts, disable everything else */ | |
370c00f9 BS |
630 | nv_wr32(priv, 0x61002c, 0x00000370); |
631 | nv_wr32(priv, 0x610028, 0x00000000); | |
70cabe4a BS |
632 | return 0; |
633 | } | |
634 | ||
635 | static int | |
636 | nv50_disp_base_fini(struct nouveau_object *object, bool suspend) | |
637 | { | |
446b05a0 | 638 | struct nv50_disp_priv *priv = (void *)object->engine; |
70cabe4a | 639 | struct nv50_disp_base *base = (void *)object; |
446b05a0 BS |
640 | |
641 | /* disable all interrupts */ | |
642 | nv_wr32(priv, 0x610024, 0x00000000); | |
643 | nv_wr32(priv, 0x610020, 0x00000000); | |
644 | ||
70cabe4a BS |
645 | return nouveau_parent_fini(&base->base, suspend); |
646 | } | |
647 | ||
648 | struct nouveau_ofuncs | |
649 | nv50_disp_base_ofuncs = { | |
650 | .ctor = nv50_disp_base_ctor, | |
651 | .dtor = nv50_disp_base_dtor, | |
652 | .init = nv50_disp_base_init, | |
653 | .fini = nv50_disp_base_fini, | |
654 | }; | |
655 | ||
ef22c8bb BS |
656 | static struct nouveau_omthds |
657 | nv50_disp_base_omthds[] = { | |
658 | { SOR_MTHD(NV50_DISP_SOR_PWR) , nv50_sor_mthd }, | |
4a230fa6 | 659 | { SOR_MTHD(NV50_DISP_SOR_LVDS_SCRIPT) , nv50_sor_mthd }, |
ef22c8bb BS |
660 | { DAC_MTHD(NV50_DISP_DAC_PWR) , nv50_dac_mthd }, |
661 | { DAC_MTHD(NV50_DISP_DAC_LOAD) , nv50_dac_mthd }, | |
662 | {}, | |
663 | }; | |
664 | ||
70cabe4a BS |
665 | static struct nouveau_oclass |
666 | nv50_disp_base_oclass[] = { | |
ef22c8bb | 667 | { NV50_DISP_CLASS, &nv50_disp_base_ofuncs, nv50_disp_base_omthds }, |
370c00f9 | 668 | {} |
ebb945a9 BS |
669 | }; |
670 | ||
671 | static struct nouveau_oclass | |
672 | nv50_disp_sclass[] = { | |
370c00f9 BS |
673 | { NV50_DISP_MAST_CLASS, &nv50_disp_mast_ofuncs }, |
674 | { NV50_DISP_SYNC_CLASS, &nv50_disp_sync_ofuncs }, | |
675 | { NV50_DISP_OVLY_CLASS, &nv50_disp_ovly_ofuncs }, | |
676 | { NV50_DISP_OIMM_CLASS, &nv50_disp_oimm_ofuncs }, | |
677 | { NV50_DISP_CURS_CLASS, &nv50_disp_curs_ofuncs }, | |
70cabe4a | 678 | {} |
ebb945a9 BS |
679 | }; |
680 | ||
70cabe4a BS |
681 | /******************************************************************************* |
682 | * Display context, tracks instmem allocation and prevents more than one | |
683 | * client using the display hardware at any time. | |
684 | ******************************************************************************/ | |
685 | ||
686 | static int | |
687 | nv50_disp_data_ctor(struct nouveau_object *parent, | |
688 | struct nouveau_object *engine, | |
689 | struct nouveau_oclass *oclass, void *data, u32 size, | |
690 | struct nouveau_object **pobject) | |
691 | { | |
370c00f9 | 692 | struct nv50_disp_priv *priv = (void *)engine; |
70cabe4a | 693 | struct nouveau_engctx *ectx; |
370c00f9 | 694 | int ret = -EBUSY; |
70cabe4a | 695 | |
370c00f9 BS |
696 | /* no context needed for channel objects... */ |
697 | if (nv_mclass(parent) != NV_DEVICE_CLASS) { | |
698 | atomic_inc(&parent->refcount); | |
699 | *pobject = parent; | |
700 | return 0; | |
701 | } | |
70cabe4a | 702 | |
370c00f9 BS |
703 | /* allocate display hardware to client */ |
704 | mutex_lock(&nv_subdev(priv)->mutex); | |
705 | if (list_empty(&nv_engine(priv)->contexts)) { | |
706 | ret = nouveau_engctx_create(parent, engine, oclass, NULL, | |
707 | 0x10000, 0x10000, | |
708 | NVOBJ_FLAG_HEAP, &ectx); | |
709 | *pobject = nv_object(ectx); | |
710 | } | |
711 | mutex_unlock(&nv_subdev(priv)->mutex); | |
712 | return ret; | |
70cabe4a BS |
713 | } |
714 | ||
715 | struct nouveau_oclass | |
716 | nv50_disp_cclass = { | |
717 | .handle = NV_ENGCTX(DISP, 0x50), | |
718 | .ofuncs = &(struct nouveau_ofuncs) { | |
719 | .ctor = nv50_disp_data_ctor, | |
720 | .dtor = _nouveau_engctx_dtor, | |
721 | .init = _nouveau_engctx_init, | |
722 | .fini = _nouveau_engctx_fini, | |
723 | .rd32 = _nouveau_engctx_rd32, | |
724 | .wr32 = _nouveau_engctx_wr32, | |
725 | }, | |
726 | }; | |
727 | ||
728 | /******************************************************************************* | |
729 | * Display engine implementation | |
730 | ******************************************************************************/ | |
731 | ||
ebb945a9 BS |
732 | static void |
733 | nv50_disp_intr_vblank(struct nv50_disp_priv *priv, int crtc) | |
734 | { | |
735 | struct nouveau_disp *disp = &priv->base; | |
736 | struct nouveau_software_chan *chan, *temp; | |
737 | unsigned long flags; | |
738 | ||
739 | spin_lock_irqsave(&disp->vblank.lock, flags); | |
740 | list_for_each_entry_safe(chan, temp, &disp->vblank.list, vblank.head) { | |
741 | if (chan->vblank.crtc != crtc) | |
742 | continue; | |
743 | ||
744 | nv_wr32(priv, 0x001704, chan->vblank.channel); | |
745 | nv_wr32(priv, 0x001710, 0x80000000 | chan->vblank.ctxdma); | |
746 | ||
747 | if (nv_device(priv)->chipset == 0x50) { | |
748 | nv_wr32(priv, 0x001570, chan->vblank.offset); | |
749 | nv_wr32(priv, 0x001574, chan->vblank.value); | |
750 | } else { | |
751 | if (nv_device(priv)->chipset >= 0xc0) { | |
752 | nv_wr32(priv, 0x06000c, | |
753 | upper_32_bits(chan->vblank.offset)); | |
754 | } | |
755 | nv_wr32(priv, 0x060010, chan->vblank.offset); | |
756 | nv_wr32(priv, 0x060014, chan->vblank.value); | |
757 | } | |
758 | ||
759 | list_del(&chan->vblank.head); | |
760 | if (disp->vblank.put) | |
761 | disp->vblank.put(disp->vblank.data, crtc); | |
762 | } | |
763 | spin_unlock_irqrestore(&disp->vblank.lock, flags); | |
764 | ||
765 | if (disp->vblank.notify) | |
766 | disp->vblank.notify(disp->vblank.data, crtc); | |
767 | } | |
768 | ||
70cabe4a | 769 | void |
ebb945a9 BS |
770 | nv50_disp_intr(struct nouveau_subdev *subdev) |
771 | { | |
772 | struct nv50_disp_priv *priv = (void *)subdev; | |
773 | u32 stat1 = nv_rd32(priv, 0x610024); | |
774 | ||
775 | if (stat1 & 0x00000004) { | |
776 | nv50_disp_intr_vblank(priv, 0); | |
777 | nv_wr32(priv, 0x610024, 0x00000004); | |
778 | stat1 &= ~0x00000004; | |
779 | } | |
780 | ||
781 | if (stat1 & 0x00000008) { | |
782 | nv50_disp_intr_vblank(priv, 1); | |
783 | nv_wr32(priv, 0x610024, 0x00000008); | |
784 | stat1 &= ~0x00000008; | |
785 | } | |
786 | ||
787 | } | |
788 | ||
789 | static int | |
790 | nv50_disp_ctor(struct nouveau_object *parent, struct nouveau_object *engine, | |
370c00f9 BS |
791 | struct nouveau_oclass *oclass, void *data, u32 size, |
792 | struct nouveau_object **pobject) | |
ebb945a9 BS |
793 | { |
794 | struct nv50_disp_priv *priv; | |
795 | int ret; | |
796 | ||
797 | ret = nouveau_disp_create(parent, engine, oclass, "PDISP", | |
798 | "display", &priv); | |
799 | *pobject = nv_object(priv); | |
800 | if (ret) | |
801 | return ret; | |
802 | ||
70cabe4a BS |
803 | nv_engine(priv)->sclass = nv50_disp_base_oclass; |
804 | nv_engine(priv)->cclass = &nv50_disp_cclass; | |
ebb945a9 | 805 | nv_subdev(priv)->intr = nv50_disp_intr; |
70cabe4a BS |
806 | priv->sclass = nv50_disp_sclass; |
807 | priv->head.nr = 2; | |
808 | priv->dac.nr = 3; | |
809 | priv->sor.nr = 2; | |
ef22c8bb | 810 | priv->dac.power = nv50_dac_power; |
7ebb38b5 | 811 | priv->dac.sense = nv50_dac_sense; |
ef22c8bb | 812 | priv->sor.power = nv50_sor_power; |
ebb945a9 BS |
813 | |
814 | INIT_LIST_HEAD(&priv->base.vblank.list); | |
815 | spin_lock_init(&priv->base.vblank.lock); | |
816 | return 0; | |
817 | } | |
818 | ||
819 | struct nouveau_oclass | |
820 | nv50_disp_oclass = { | |
821 | .handle = NV_ENGINE(DISP, 0x50), | |
822 | .ofuncs = &(struct nouveau_ofuncs) { | |
823 | .ctor = nv50_disp_ctor, | |
824 | .dtor = _nouveau_disp_dtor, | |
825 | .init = _nouveau_disp_init, | |
826 | .fini = _nouveau_disp_fini, | |
827 | }, | |
828 | }; |