blob: 531175da6bb39baa2ea3c132ed547a7c09950067 [file] [log] [blame]
Ben Skeggsebb945a2012-07-20 08:17:34 +10001/*
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
Ben Skeggs370c00f2012-08-14 14:11:49 +100025#include <core/object.h>
26#include <core/parent.h>
27#include <core/handle.h>
28#include <core/class.h>
29
Ben Skeggsebb945a2012-07-20 08:17:34 +100030#include <engine/software.h>
31#include <engine/disp.h>
32
Ben Skeggs446b05a2012-08-14 12:50:14 +100033#include <subdev/timer.h>
Ben Skeggs370c00f2012-08-14 14:11:49 +100034#include <subdev/fb.h>
35#include <subdev/bar.h>
Ben Skeggs446b05a2012-08-14 12:50:14 +100036
Ben Skeggs70cabe42012-08-14 10:04:04 +100037#include "nv50.h"
38
39/*******************************************************************************
Ben Skeggs370c00f2012-08-14 14:11:49 +100040 * EVO channel base class
Ben Skeggs70cabe42012-08-14 10:04:04 +100041 ******************************************************************************/
42
Ben Skeggs370c00f2012-08-14 14:11:49 +100043int
44nv50_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
68void
69nv50_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
76u32
Ben Skeggs70cabe42012-08-14 10:04:04 +100077nv50_disp_chan_rd32(struct nouveau_object *object, u64 addr)
78{
Ben Skeggs370c00f2012-08-14 14:11:49 +100079 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);
82}
83
84void
85nv50_disp_chan_wr32(struct nouveau_object *object, u64 addr, u32 data)
86{
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
96static int
97nv50_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);
Ben Skeggs70cabe42012-08-14 10:04:04 +1000106}
107
108static void
Ben Skeggs370c00f2012-08-14 14:11:49 +1000109nv50_disp_dmac_object_detach(struct nouveau_object *parent, int cookie)
Ben Skeggs70cabe42012-08-14 10:04:04 +1000110{
Ben Skeggs370c00f2012-08-14 14:11:49 +1000111 struct nv50_disp_base *base = (void *)parent->parent;
112 nouveau_ramht_remove(base->ramht, cookie);
113}
114
115int
116nv50_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;
144 default:
145 return -EINVAL;
146 }
147 break;
148 default:
149 return -EINVAL;
150 }
151
152 return 0;
153}
154
155void
156nv50_disp_dmac_dtor(struct nouveau_object *object)
157{
158 struct nv50_disp_dmac *dmac = (void *)object;
159 nouveau_object_ref(NULL, (struct nouveau_object **)&dmac->pushdma);
160 nv50_disp_chan_destroy(&dmac->base);
161}
162
163static int
164nv50_disp_dmac_init(struct nouveau_object *object)
165{
166 struct nv50_disp_priv *priv = (void *)object->engine;
167 struct nv50_disp_dmac *dmac = (void *)object;
168 int chid = dmac->base.chid;
169 int ret;
170
171 ret = nv50_disp_chan_init(&dmac->base);
172 if (ret)
173 return ret;
174
175 /* enable error reporting */
176 nv_mask(priv, 0x610028, 0x00010001 << chid, 0x00010001 << chid);
177
178 /* initialise channel for dma command submission */
179 nv_wr32(priv, 0x610204 + (chid * 0x0010), dmac->push);
180 nv_wr32(priv, 0x610208 + (chid * 0x0010), 0x00010000);
181 nv_wr32(priv, 0x61020c + (chid * 0x0010), chid);
182 nv_mask(priv, 0x610200 + (chid * 0x0010), 0x00000010, 0x00000010);
183 nv_wr32(priv, 0x640000 + (chid * 0x1000), 0x00000000);
184 nv_wr32(priv, 0x610200 + (chid * 0x0010), 0x00000013);
185
186 /* wait for it to go inactive */
187 if (!nv_wait(priv, 0x610200 + (chid * 0x10), 0x80000000, 0x00000000)) {
188 nv_error(dmac, "init timeout, 0x%08x\n",
189 nv_rd32(priv, 0x610200 + (chid * 0x10)));
190 return -EBUSY;
191 }
192
193 return 0;
194}
195
196static int
197nv50_disp_dmac_fini(struct nouveau_object *object, bool suspend)
198{
199 struct nv50_disp_priv *priv = (void *)object->engine;
200 struct nv50_disp_dmac *dmac = (void *)object;
201 int chid = dmac->base.chid;
202
203 /* deactivate channel */
204 nv_mask(priv, 0x610200 + (chid * 0x0010), 0x00001010, 0x00001000);
205 nv_mask(priv, 0x610200 + (chid * 0x0010), 0x00000003, 0x00000000);
206 if (!nv_wait(priv, 0x610200 + (chid * 0x10), 0x001e0000, 0x00000000)) {
207 nv_error(dmac, "fini timeout, 0x%08x\n",
208 nv_rd32(priv, 0x610200 + (chid * 0x10)));
209 if (suspend)
210 return -EBUSY;
211 }
212
213 /* disable error reporting */
214 nv_mask(priv, 0x610028, 0x00010001 << chid, 0x00000000 << chid);
215
216 return nv50_disp_chan_fini(&dmac->base, suspend);
Ben Skeggs70cabe42012-08-14 10:04:04 +1000217}
218
219/*******************************************************************************
220 * EVO master channel object
221 ******************************************************************************/
222
223static int
224nv50_disp_mast_ctor(struct nouveau_object *parent,
225 struct nouveau_object *engine,
226 struct nouveau_oclass *oclass, void *data, u32 size,
227 struct nouveau_object **pobject)
228{
Ben Skeggs370c00f2012-08-14 14:11:49 +1000229 struct nv50_display_mast_class *args = data;
230 struct nv50_disp_dmac *mast;
Ben Skeggs70cabe42012-08-14 10:04:04 +1000231 int ret;
232
Ben Skeggs370c00f2012-08-14 14:11:49 +1000233 if (size < sizeof(*args))
234 return -EINVAL;
235
236 ret = nv50_disp_dmac_create_(parent, engine, oclass, args->pushbuf,
237 0, sizeof(*mast), (void **)&mast);
238 *pobject = nv_object(mast);
Ben Skeggs70cabe42012-08-14 10:04:04 +1000239 if (ret)
240 return ret;
241
Ben Skeggs370c00f2012-08-14 14:11:49 +1000242 nv_parent(mast)->object_attach = nv50_disp_dmac_object_attach;
243 nv_parent(mast)->object_detach = nv50_disp_dmac_object_detach;
Ben Skeggs70cabe42012-08-14 10:04:04 +1000244 return 0;
245}
246
Ben Skeggs70cabe42012-08-14 10:04:04 +1000247static int
248nv50_disp_mast_init(struct nouveau_object *object)
249{
Ben Skeggs370c00f2012-08-14 14:11:49 +1000250 struct nv50_disp_priv *priv = (void *)object->engine;
251 struct nv50_disp_dmac *mast = (void *)object;
Ben Skeggs70cabe42012-08-14 10:04:04 +1000252 int ret;
253
Ben Skeggs370c00f2012-08-14 14:11:49 +1000254 ret = nv50_disp_chan_init(&mast->base);
Ben Skeggs70cabe42012-08-14 10:04:04 +1000255 if (ret)
256 return ret;
257
Ben Skeggs370c00f2012-08-14 14:11:49 +1000258 /* enable error reporting */
259 nv_mask(priv, 0x610028, 0x00010001, 0x00010001);
260
261 /* attempt to unstick channel from some unknown state */
262 if ((nv_rd32(priv, 0x610200) & 0x009f0000) == 0x00020000)
263 nv_mask(priv, 0x610200, 0x00800000, 0x00800000);
264 if ((nv_rd32(priv, 0x610200) & 0x003f0000) == 0x00030000)
265 nv_mask(priv, 0x610200, 0x00600000, 0x00600000);
266
267 /* initialise channel for dma command submission */
268 nv_wr32(priv, 0x610204, mast->push);
269 nv_wr32(priv, 0x610208, 0x00010000);
270 nv_wr32(priv, 0x61020c, 0x00000000);
271 nv_mask(priv, 0x610200, 0x00000010, 0x00000010);
272 nv_wr32(priv, 0x640000, 0x00000000);
273 nv_wr32(priv, 0x610200, 0x01000013);
274
275 /* wait for it to go inactive */
276 if (!nv_wait(priv, 0x610200, 0x80000000, 0x00000000)) {
277 nv_error(mast, "init: 0x%08x\n", nv_rd32(priv, 0x610200));
278 return -EBUSY;
279 }
280
Ben Skeggs70cabe42012-08-14 10:04:04 +1000281 return 0;
282}
283
284static int
285nv50_disp_mast_fini(struct nouveau_object *object, bool suspend)
286{
Ben Skeggs370c00f2012-08-14 14:11:49 +1000287 struct nv50_disp_priv *priv = (void *)object->engine;
288 struct nv50_disp_dmac *mast = (void *)object;
289
290 /* deactivate channel */
291 nv_mask(priv, 0x610200, 0x00000010, 0x00000000);
292 nv_mask(priv, 0x610200, 0x00000003, 0x00000000);
293 if (!nv_wait(priv, 0x610200, 0x001e0000, 0x00000000)) {
294 nv_error(mast, "fini: 0x%08x\n", nv_rd32(priv, 0x610200));
295 if (suspend)
296 return -EBUSY;
297 }
298
299 /* disable error reporting */
300 nv_mask(priv, 0x610028, 0x00010001, 0x00000000);
301
302 return nv50_disp_chan_fini(&mast->base, suspend);
Ben Skeggs70cabe42012-08-14 10:04:04 +1000303}
304
305struct nouveau_ofuncs
306nv50_disp_mast_ofuncs = {
307 .ctor = nv50_disp_mast_ctor,
Ben Skeggs370c00f2012-08-14 14:11:49 +1000308 .dtor = nv50_disp_dmac_dtor,
Ben Skeggs70cabe42012-08-14 10:04:04 +1000309 .init = nv50_disp_mast_init,
310 .fini = nv50_disp_mast_fini,
311 .rd32 = nv50_disp_chan_rd32,
312 .wr32 = nv50_disp_chan_wr32,
313};
314
315/*******************************************************************************
Ben Skeggs370c00f2012-08-14 14:11:49 +1000316 * EVO sync channel objects
Ben Skeggs70cabe42012-08-14 10:04:04 +1000317 ******************************************************************************/
318
319static int
Ben Skeggs370c00f2012-08-14 14:11:49 +1000320nv50_disp_sync_ctor(struct nouveau_object *parent,
Ben Skeggs70cabe42012-08-14 10:04:04 +1000321 struct nouveau_object *engine,
322 struct nouveau_oclass *oclass, void *data, u32 size,
323 struct nouveau_object **pobject)
324{
Ben Skeggs370c00f2012-08-14 14:11:49 +1000325 struct nv50_display_sync_class *args = data;
326 struct nv50_disp_dmac *dmac;
Ben Skeggs70cabe42012-08-14 10:04:04 +1000327 int ret;
328
Ben Skeggs370c00f2012-08-14 14:11:49 +1000329 if (size < sizeof(*data) || args->head > 1)
330 return -EINVAL;
331
332 ret = nv50_disp_dmac_create_(parent, engine, oclass, args->pushbuf,
333 1 + args->head, sizeof(*dmac),
334 (void **)&dmac);
335 *pobject = nv_object(dmac);
Ben Skeggs70cabe42012-08-14 10:04:04 +1000336 if (ret)
337 return ret;
338
Ben Skeggs370c00f2012-08-14 14:11:49 +1000339 nv_parent(dmac)->object_attach = nv50_disp_dmac_object_attach;
340 nv_parent(dmac)->object_detach = nv50_disp_dmac_object_detach;
Ben Skeggs70cabe42012-08-14 10:04:04 +1000341 return 0;
342}
343
Ben Skeggs70cabe42012-08-14 10:04:04 +1000344struct nouveau_ofuncs
Ben Skeggs370c00f2012-08-14 14:11:49 +1000345nv50_disp_sync_ofuncs = {
346 .ctor = nv50_disp_sync_ctor,
Ben Skeggs70cabe42012-08-14 10:04:04 +1000347 .dtor = nv50_disp_dmac_dtor,
348 .init = nv50_disp_dmac_init,
349 .fini = nv50_disp_dmac_fini,
350 .rd32 = nv50_disp_chan_rd32,
351 .wr32 = nv50_disp_chan_wr32,
352};
353
354/*******************************************************************************
Ben Skeggs370c00f2012-08-14 14:11:49 +1000355 * EVO overlay channel objects
Ben Skeggs70cabe42012-08-14 10:04:04 +1000356 ******************************************************************************/
357
358static int
Ben Skeggs370c00f2012-08-14 14:11:49 +1000359nv50_disp_ovly_ctor(struct nouveau_object *parent,
Ben Skeggs70cabe42012-08-14 10:04:04 +1000360 struct nouveau_object *engine,
361 struct nouveau_oclass *oclass, void *data, u32 size,
362 struct nouveau_object **pobject)
363{
Ben Skeggs370c00f2012-08-14 14:11:49 +1000364 struct nv50_display_ovly_class *args = data;
365 struct nv50_disp_dmac *dmac;
Ben Skeggs70cabe42012-08-14 10:04:04 +1000366 int ret;
367
Ben Skeggs370c00f2012-08-14 14:11:49 +1000368 if (size < sizeof(*data) || args->head > 1)
369 return -EINVAL;
370
371 ret = nv50_disp_dmac_create_(parent, engine, oclass, args->pushbuf,
372 3 + args->head, sizeof(*dmac),
373 (void **)&dmac);
374 *pobject = nv_object(dmac);
Ben Skeggs70cabe42012-08-14 10:04:04 +1000375 if (ret)
376 return ret;
377
Ben Skeggs370c00f2012-08-14 14:11:49 +1000378 nv_parent(dmac)->object_attach = nv50_disp_dmac_object_attach;
379 nv_parent(dmac)->object_detach = nv50_disp_dmac_object_detach;
Ben Skeggs70cabe42012-08-14 10:04:04 +1000380 return 0;
381}
382
Ben Skeggs370c00f2012-08-14 14:11:49 +1000383struct nouveau_ofuncs
384nv50_disp_ovly_ofuncs = {
385 .ctor = nv50_disp_ovly_ctor,
386 .dtor = nv50_disp_dmac_dtor,
387 .init = nv50_disp_dmac_init,
388 .fini = nv50_disp_dmac_fini,
389 .rd32 = nv50_disp_chan_rd32,
390 .wr32 = nv50_disp_chan_wr32,
391};
392
393/*******************************************************************************
394 * EVO PIO channel base class
395 ******************************************************************************/
396
397static int
398nv50_disp_pioc_create_(struct nouveau_object *parent,
399 struct nouveau_object *engine,
400 struct nouveau_oclass *oclass, int chid,
401 int length, void **pobject)
402{
403 return nv50_disp_chan_create_(parent, engine, oclass, chid,
404 length, pobject);
405}
406
Ben Skeggs70cabe42012-08-14 10:04:04 +1000407static void
408nv50_disp_pioc_dtor(struct nouveau_object *object)
409{
Ben Skeggs370c00f2012-08-14 14:11:49 +1000410 struct nv50_disp_pioc *pioc = (void *)object;
411 nv50_disp_chan_destroy(&pioc->base);
Ben Skeggs70cabe42012-08-14 10:04:04 +1000412}
413
414static int
415nv50_disp_pioc_init(struct nouveau_object *object)
416{
Ben Skeggs370c00f2012-08-14 14:11:49 +1000417 struct nv50_disp_priv *priv = (void *)object->engine;
418 struct nv50_disp_pioc *pioc = (void *)object;
419 int chid = pioc->base.chid;
Ben Skeggs70cabe42012-08-14 10:04:04 +1000420 int ret;
421
Ben Skeggs370c00f2012-08-14 14:11:49 +1000422 ret = nv50_disp_chan_init(&pioc->base);
Ben Skeggs70cabe42012-08-14 10:04:04 +1000423 if (ret)
424 return ret;
425
Ben Skeggs370c00f2012-08-14 14:11:49 +1000426 nv_wr32(priv, 0x610200 + (chid * 0x10), 0x00002000);
427 if (!nv_wait(priv, 0x610200 + (chid * 0x10), 0x00000000, 0x00000000)) {
428 nv_error(pioc, "timeout0: 0x%08x\n",
429 nv_rd32(priv, 0x610200 + (chid * 0x10)));
430 return -EBUSY;
431 }
432
433 nv_wr32(priv, 0x610200 + (chid * 0x10), 0x00000001);
434 if (!nv_wait(priv, 0x610200 + (chid * 0x10), 0x00030000, 0x00010000)) {
435 nv_error(pioc, "timeout1: 0x%08x\n",
436 nv_rd32(priv, 0x610200 + (chid * 0x10)));
437 return -EBUSY;
438 }
439
Ben Skeggs70cabe42012-08-14 10:04:04 +1000440 return 0;
441}
442
443static int
444nv50_disp_pioc_fini(struct nouveau_object *object, bool suspend)
445{
Ben Skeggs370c00f2012-08-14 14:11:49 +1000446 struct nv50_disp_priv *priv = (void *)object->engine;
447 struct nv50_disp_pioc *pioc = (void *)object;
448 int chid = pioc->base.chid;
449
450 nv_mask(priv, 0x610200 + (chid * 0x10), 0x00000001, 0x00000000);
451 if (!nv_wait(priv, 0x610200 + (chid * 0x10), 0x00030000, 0x00000000)) {
452 nv_error(pioc, "timeout: 0x%08x\n",
453 nv_rd32(priv, 0x610200 + (chid * 0x10)));
454 if (suspend)
455 return -EBUSY;
456 }
457
458 return nv50_disp_chan_fini(&pioc->base, suspend);
459}
460
461/*******************************************************************************
462 * EVO immediate overlay channel objects
463 ******************************************************************************/
464
465static int
466nv50_disp_oimm_ctor(struct nouveau_object *parent,
467 struct nouveau_object *engine,
468 struct nouveau_oclass *oclass, void *data, u32 size,
469 struct nouveau_object **pobject)
470{
471 struct nv50_display_oimm_class *args = data;
472 struct nv50_disp_pioc *pioc;
473 int ret;
474
475 if (size < sizeof(*args) || args->head > 1)
476 return -EINVAL;
477
478 ret = nv50_disp_pioc_create_(parent, engine, oclass, 5 + args->head,
479 sizeof(*pioc), (void **)&pioc);
480 *pobject = nv_object(pioc);
481 if (ret)
482 return ret;
483
484 return 0;
Ben Skeggs70cabe42012-08-14 10:04:04 +1000485}
486
487struct nouveau_ofuncs
Ben Skeggs370c00f2012-08-14 14:11:49 +1000488nv50_disp_oimm_ofuncs = {
489 .ctor = nv50_disp_oimm_ctor,
490 .dtor = nv50_disp_pioc_dtor,
491 .init = nv50_disp_pioc_init,
492 .fini = nv50_disp_pioc_fini,
493 .rd32 = nv50_disp_chan_rd32,
494 .wr32 = nv50_disp_chan_wr32,
495};
496
497/*******************************************************************************
498 * EVO cursor channel objects
499 ******************************************************************************/
500
501static int
502nv50_disp_curs_ctor(struct nouveau_object *parent,
503 struct nouveau_object *engine,
504 struct nouveau_oclass *oclass, void *data, u32 size,
505 struct nouveau_object **pobject)
506{
507 struct nv50_display_curs_class *args = data;
508 struct nv50_disp_pioc *pioc;
509 int ret;
510
511 if (size < sizeof(*args) || args->head > 1)
512 return -EINVAL;
513
514 ret = nv50_disp_pioc_create_(parent, engine, oclass, 7 + args->head,
515 sizeof(*pioc), (void **)&pioc);
516 *pobject = nv_object(pioc);
517 if (ret)
518 return ret;
519
520 return 0;
521}
522
523struct nouveau_ofuncs
524nv50_disp_curs_ofuncs = {
525 .ctor = nv50_disp_curs_ctor,
Ben Skeggs70cabe42012-08-14 10:04:04 +1000526 .dtor = nv50_disp_pioc_dtor,
527 .init = nv50_disp_pioc_init,
528 .fini = nv50_disp_pioc_fini,
529 .rd32 = nv50_disp_chan_rd32,
530 .wr32 = nv50_disp_chan_wr32,
531};
532
533/*******************************************************************************
534 * Base display object
535 ******************************************************************************/
536
537static int
538nv50_disp_base_ctor(struct nouveau_object *parent,
539 struct nouveau_object *engine,
540 struct nouveau_oclass *oclass, void *data, u32 size,
541 struct nouveau_object **pobject)
542{
543 struct nv50_disp_priv *priv = (void *)engine;
544 struct nv50_disp_base *base;
545 int ret;
546
547 ret = nouveau_parent_create(parent, engine, oclass, 0,
548 priv->sclass, 0, &base);
549 *pobject = nv_object(base);
550 if (ret)
551 return ret;
552
Ben Skeggs370c00f2012-08-14 14:11:49 +1000553 return nouveau_ramht_new(parent, parent, 0x1000, 0, &base->ramht);
Ben Skeggs70cabe42012-08-14 10:04:04 +1000554}
555
556static void
557nv50_disp_base_dtor(struct nouveau_object *object)
558{
559 struct nv50_disp_base *base = (void *)object;
Ben Skeggs370c00f2012-08-14 14:11:49 +1000560 nouveau_ramht_ref(NULL, &base->ramht);
Ben Skeggs70cabe42012-08-14 10:04:04 +1000561 nouveau_parent_destroy(&base->base);
562}
563
564static int
565nv50_disp_base_init(struct nouveau_object *object)
566{
Ben Skeggsab772142012-08-14 11:29:57 +1000567 struct nv50_disp_priv *priv = (void *)object->engine;
Ben Skeggs70cabe42012-08-14 10:04:04 +1000568 struct nv50_disp_base *base = (void *)object;
Ben Skeggsab772142012-08-14 11:29:57 +1000569 int ret, i;
570 u32 tmp;
Ben Skeggs70cabe42012-08-14 10:04:04 +1000571
572 ret = nouveau_parent_init(&base->base);
573 if (ret)
574 return ret;
575
Ben Skeggsab772142012-08-14 11:29:57 +1000576 /* The below segments of code copying values from one register to
577 * another appear to inform EVO of the display capabilities or
578 * something similar. NFI what the 0x614004 caps are for..
579 */
580 tmp = nv_rd32(priv, 0x614004);
581 nv_wr32(priv, 0x610184, tmp);
582
583 /* ... CRTC caps */
584 for (i = 0; i < priv->head.nr; i++) {
585 tmp = nv_rd32(priv, 0x616100 + (i * 0x800));
586 nv_wr32(priv, 0x610190 + (i * 0x10), tmp);
587 tmp = nv_rd32(priv, 0x616104 + (i * 0x800));
588 nv_wr32(priv, 0x610194 + (i * 0x10), tmp);
589 tmp = nv_rd32(priv, 0x616108 + (i * 0x800));
590 nv_wr32(priv, 0x610198 + (i * 0x10), tmp);
591 tmp = nv_rd32(priv, 0x61610c + (i * 0x800));
592 nv_wr32(priv, 0x61019c + (i * 0x10), tmp);
593 }
594
595 /* ... DAC caps */
596 for (i = 0; i < priv->dac.nr; i++) {
597 tmp = nv_rd32(priv, 0x61a000 + (i * 0x800));
598 nv_wr32(priv, 0x6101d0 + (i * 0x04), tmp);
599 }
600
601 /* ... SOR caps */
602 for (i = 0; i < priv->sor.nr; i++) {
603 tmp = nv_rd32(priv, 0x61c000 + (i * 0x800));
604 nv_wr32(priv, 0x6101e0 + (i * 0x04), tmp);
605 }
606
607 /* ... EXT caps */
608 for (i = 0; i < 3; i++) {
609 tmp = nv_rd32(priv, 0x61e000 + (i * 0x800));
610 nv_wr32(priv, 0x6101f0 + (i * 0x04), tmp);
611 }
612
Ben Skeggs446b05a2012-08-14 12:50:14 +1000613 /* steal display away from vbios, or something like that */
614 if (nv_rd32(priv, 0x610024) & 0x00000100) {
615 nv_wr32(priv, 0x610024, 0x00000100);
616 nv_mask(priv, 0x6194e8, 0x00000001, 0x00000000);
617 if (!nv_wait(priv, 0x6194e8, 0x00000002, 0x00000000)) {
618 nv_error(priv, "timeout acquiring display\n");
619 return -EBUSY;
620 }
621 }
622
623 /* point at display engine memory area (hash table, objects) */
Ben Skeggs370c00f2012-08-14 14:11:49 +1000624 nv_wr32(priv, 0x610010, (nv_gpuobj(base->ramht)->addr >> 8) | 9);
Ben Skeggs446b05a2012-08-14 12:50:14 +1000625
626 /* enable supervisor interrupts, disable everything else */
Ben Skeggs370c00f2012-08-14 14:11:49 +1000627 nv_wr32(priv, 0x61002c, 0x00000370);
628 nv_wr32(priv, 0x610028, 0x00000000);
Ben Skeggs70cabe42012-08-14 10:04:04 +1000629 return 0;
630}
631
632static int
633nv50_disp_base_fini(struct nouveau_object *object, bool suspend)
634{
Ben Skeggs446b05a2012-08-14 12:50:14 +1000635 struct nv50_disp_priv *priv = (void *)object->engine;
Ben Skeggs70cabe42012-08-14 10:04:04 +1000636 struct nv50_disp_base *base = (void *)object;
Ben Skeggs446b05a2012-08-14 12:50:14 +1000637
638 /* disable all interrupts */
639 nv_wr32(priv, 0x610024, 0x00000000);
640 nv_wr32(priv, 0x610020, 0x00000000);
641
Ben Skeggs70cabe42012-08-14 10:04:04 +1000642 return nouveau_parent_fini(&base->base, suspend);
643}
644
645struct nouveau_ofuncs
646nv50_disp_base_ofuncs = {
647 .ctor = nv50_disp_base_ctor,
648 .dtor = nv50_disp_base_dtor,
649 .init = nv50_disp_base_init,
650 .fini = nv50_disp_base_fini,
651};
652
653static struct nouveau_oclass
654nv50_disp_base_oclass[] = {
Ben Skeggs370c00f2012-08-14 14:11:49 +1000655 { NV50_DISP_CLASS, &nv50_disp_base_ofuncs },
656 {}
Ben Skeggsebb945a2012-07-20 08:17:34 +1000657};
658
659static struct nouveau_oclass
660nv50_disp_sclass[] = {
Ben Skeggs370c00f2012-08-14 14:11:49 +1000661 { NV50_DISP_MAST_CLASS, &nv50_disp_mast_ofuncs },
662 { NV50_DISP_SYNC_CLASS, &nv50_disp_sync_ofuncs },
663 { NV50_DISP_OVLY_CLASS, &nv50_disp_ovly_ofuncs },
664 { NV50_DISP_OIMM_CLASS, &nv50_disp_oimm_ofuncs },
665 { NV50_DISP_CURS_CLASS, &nv50_disp_curs_ofuncs },
Ben Skeggs70cabe42012-08-14 10:04:04 +1000666 {}
Ben Skeggsebb945a2012-07-20 08:17:34 +1000667};
668
Ben Skeggs70cabe42012-08-14 10:04:04 +1000669/*******************************************************************************
670 * Display context, tracks instmem allocation and prevents more than one
671 * client using the display hardware at any time.
672 ******************************************************************************/
673
674static int
675nv50_disp_data_ctor(struct nouveau_object *parent,
676 struct nouveau_object *engine,
677 struct nouveau_oclass *oclass, void *data, u32 size,
678 struct nouveau_object **pobject)
679{
Ben Skeggs370c00f2012-08-14 14:11:49 +1000680 struct nv50_disp_priv *priv = (void *)engine;
Ben Skeggs70cabe42012-08-14 10:04:04 +1000681 struct nouveau_engctx *ectx;
Ben Skeggs370c00f2012-08-14 14:11:49 +1000682 int ret = -EBUSY;
Ben Skeggs70cabe42012-08-14 10:04:04 +1000683
Ben Skeggs370c00f2012-08-14 14:11:49 +1000684 /* no context needed for channel objects... */
685 if (nv_mclass(parent) != NV_DEVICE_CLASS) {
686 atomic_inc(&parent->refcount);
687 *pobject = parent;
688 return 0;
689 }
Ben Skeggs70cabe42012-08-14 10:04:04 +1000690
Ben Skeggs370c00f2012-08-14 14:11:49 +1000691 /* allocate display hardware to client */
692 mutex_lock(&nv_subdev(priv)->mutex);
693 if (list_empty(&nv_engine(priv)->contexts)) {
694 ret = nouveau_engctx_create(parent, engine, oclass, NULL,
695 0x10000, 0x10000,
696 NVOBJ_FLAG_HEAP, &ectx);
697 *pobject = nv_object(ectx);
698 }
699 mutex_unlock(&nv_subdev(priv)->mutex);
700 return ret;
Ben Skeggs70cabe42012-08-14 10:04:04 +1000701}
702
703struct nouveau_oclass
704nv50_disp_cclass = {
705 .handle = NV_ENGCTX(DISP, 0x50),
706 .ofuncs = &(struct nouveau_ofuncs) {
707 .ctor = nv50_disp_data_ctor,
708 .dtor = _nouveau_engctx_dtor,
709 .init = _nouveau_engctx_init,
710 .fini = _nouveau_engctx_fini,
711 .rd32 = _nouveau_engctx_rd32,
712 .wr32 = _nouveau_engctx_wr32,
713 },
714};
715
716/*******************************************************************************
717 * Display engine implementation
718 ******************************************************************************/
719
Ben Skeggsebb945a2012-07-20 08:17:34 +1000720static void
721nv50_disp_intr_vblank(struct nv50_disp_priv *priv, int crtc)
722{
723 struct nouveau_disp *disp = &priv->base;
724 struct nouveau_software_chan *chan, *temp;
725 unsigned long flags;
726
727 spin_lock_irqsave(&disp->vblank.lock, flags);
728 list_for_each_entry_safe(chan, temp, &disp->vblank.list, vblank.head) {
729 if (chan->vblank.crtc != crtc)
730 continue;
731
732 nv_wr32(priv, 0x001704, chan->vblank.channel);
733 nv_wr32(priv, 0x001710, 0x80000000 | chan->vblank.ctxdma);
734
735 if (nv_device(priv)->chipset == 0x50) {
736 nv_wr32(priv, 0x001570, chan->vblank.offset);
737 nv_wr32(priv, 0x001574, chan->vblank.value);
738 } else {
739 if (nv_device(priv)->chipset >= 0xc0) {
740 nv_wr32(priv, 0x06000c,
741 upper_32_bits(chan->vblank.offset));
742 }
743 nv_wr32(priv, 0x060010, chan->vblank.offset);
744 nv_wr32(priv, 0x060014, chan->vblank.value);
745 }
746
747 list_del(&chan->vblank.head);
748 if (disp->vblank.put)
749 disp->vblank.put(disp->vblank.data, crtc);
750 }
751 spin_unlock_irqrestore(&disp->vblank.lock, flags);
752
753 if (disp->vblank.notify)
754 disp->vblank.notify(disp->vblank.data, crtc);
755}
756
Ben Skeggs70cabe42012-08-14 10:04:04 +1000757void
Ben Skeggsebb945a2012-07-20 08:17:34 +1000758nv50_disp_intr(struct nouveau_subdev *subdev)
759{
760 struct nv50_disp_priv *priv = (void *)subdev;
761 u32 stat1 = nv_rd32(priv, 0x610024);
762
763 if (stat1 & 0x00000004) {
764 nv50_disp_intr_vblank(priv, 0);
765 nv_wr32(priv, 0x610024, 0x00000004);
766 stat1 &= ~0x00000004;
767 }
768
769 if (stat1 & 0x00000008) {
770 nv50_disp_intr_vblank(priv, 1);
771 nv_wr32(priv, 0x610024, 0x00000008);
772 stat1 &= ~0x00000008;
773 }
774
775}
776
777static int
778nv50_disp_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
Ben Skeggs370c00f2012-08-14 14:11:49 +1000779 struct nouveau_oclass *oclass, void *data, u32 size,
780 struct nouveau_object **pobject)
Ben Skeggsebb945a2012-07-20 08:17:34 +1000781{
782 struct nv50_disp_priv *priv;
783 int ret;
784
785 ret = nouveau_disp_create(parent, engine, oclass, "PDISP",
786 "display", &priv);
787 *pobject = nv_object(priv);
788 if (ret)
789 return ret;
790
Ben Skeggs70cabe42012-08-14 10:04:04 +1000791 nv_engine(priv)->sclass = nv50_disp_base_oclass;
792 nv_engine(priv)->cclass = &nv50_disp_cclass;
Ben Skeggsebb945a2012-07-20 08:17:34 +1000793 nv_subdev(priv)->intr = nv50_disp_intr;
Ben Skeggs70cabe42012-08-14 10:04:04 +1000794 priv->sclass = nv50_disp_sclass;
795 priv->head.nr = 2;
796 priv->dac.nr = 3;
797 priv->sor.nr = 2;
Ben Skeggsebb945a2012-07-20 08:17:34 +1000798
799 INIT_LIST_HEAD(&priv->base.vblank.list);
800 spin_lock_init(&priv->base.vblank.lock);
801 return 0;
802}
803
804struct nouveau_oclass
805nv50_disp_oclass = {
806 .handle = NV_ENGINE(DISP, 0x50),
807 .ofuncs = &(struct nouveau_ofuncs) {
808 .ctor = nv50_disp_ctor,
809 .dtor = _nouveau_disp_dtor,
810 .init = _nouveau_disp_init,
811 .fini = _nouveau_disp_fini,
812 },
813};