blob: fa1b9709f1cfe8ab2d212481ce27929623065adc [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 Skeggs46654062012-08-28 14:10:39 +100025#include <core/object.h>
26#include <core/parent.h>
27#include <core/handle.h>
28#include <core/class.h>
Ben Skeggsebb945a2012-07-20 08:17:34 +100029
30#include <engine/software.h>
31#include <engine/disp.h>
32
Ben Skeggs46654062012-08-28 14:10:39 +100033#include <subdev/timer.h>
34#include <subdev/fb.h>
35#include <subdev/bar.h>
36
37#include "nv50.h"
38
39/*******************************************************************************
40 * EVO DMA channel base class
41 ******************************************************************************/
42
43static int
44nvd0_disp_dmac_object_attach(struct nouveau_object *parent,
45 struct nouveau_object *object, u32 name)
46{
47 struct nv50_disp_base *base = (void *)parent->parent;
48 struct nv50_disp_chan *chan = (void *)parent;
49 u32 addr = nv_gpuobj(object)->node->offset;
50 u32 data = (chan->chid << 27) | (addr << 9) | 0x00000001;
51 return nouveau_ramht_insert(base->ramht, chan->chid, name, data);
52}
53
54static void
55nvd0_disp_dmac_object_detach(struct nouveau_object *parent, int cookie)
56{
57 struct nv50_disp_base *base = (void *)parent->parent;
58 nouveau_ramht_remove(base->ramht, cookie);
59}
60
61static int
62nvd0_disp_dmac_init(struct nouveau_object *object)
63{
64 struct nv50_disp_priv *priv = (void *)object->engine;
65 struct nv50_disp_dmac *dmac = (void *)object;
66 int chid = dmac->base.chid;
67 int ret;
68
69 ret = nv50_disp_chan_init(&dmac->base);
70 if (ret)
71 return ret;
72
73 /* enable error reporting */
74 nv_mask(priv, 0x610090, 0x00000001 << chid, 0x00000001 << chid);
75 nv_mask(priv, 0x6100a0, 0x00000001 << chid, 0x00000001 << chid);
76
77 /* initialise channel for dma command submission */
78 nv_wr32(priv, 0x610494 + (chid * 0x0010), dmac->push);
79 nv_wr32(priv, 0x610498 + (chid * 0x0010), 0x00010000);
80 nv_wr32(priv, 0x61049c + (chid * 0x0010), 0x00000001);
81 nv_mask(priv, 0x610490 + (chid * 0x0010), 0x00000010, 0x00000010);
82 nv_wr32(priv, 0x640000 + (chid * 0x1000), 0x00000000);
83 nv_wr32(priv, 0x610490 + (chid * 0x0010), 0x00000013);
84
85 /* wait for it to go inactive */
86 if (!nv_wait(priv, 0x610490 + (chid * 0x10), 0x80000000, 0x00000000)) {
87 nv_error(dmac, "init: 0x%08x\n",
88 nv_rd32(priv, 0x610490 + (chid * 0x10)));
89 return -EBUSY;
90 }
91
92 return 0;
93}
94
95static int
96nvd0_disp_dmac_fini(struct nouveau_object *object, bool suspend)
97{
98 struct nv50_disp_priv *priv = (void *)object->engine;
99 struct nv50_disp_dmac *dmac = (void *)object;
100 int chid = dmac->base.chid;
101
102 /* deactivate channel */
103 nv_mask(priv, 0x610490 + (chid * 0x0010), 0x00001010, 0x00001000);
104 nv_mask(priv, 0x610490 + (chid * 0x0010), 0x00000003, 0x00000000);
105 if (!nv_wait(priv, 0x610490 + (chid * 0x10), 0x001e0000, 0x00000000)) {
106 nv_error(dmac, "fini: 0x%08x\n",
107 nv_rd32(priv, 0x610490 + (chid * 0x10)));
108 if (suspend)
109 return -EBUSY;
110 }
111
112 /* disable error reporting */
113 nv_mask(priv, 0x610090, 0x00000001 << chid, 0x00000000);
114 nv_mask(priv, 0x6100a0, 0x00000001 << chid, 0x00000000);
115
116 return nv50_disp_chan_fini(&dmac->base, suspend);
117}
118
119/*******************************************************************************
120 * EVO master channel object
121 ******************************************************************************/
122
123static int
124nvd0_disp_mast_ctor(struct nouveau_object *parent,
125 struct nouveau_object *engine,
126 struct nouveau_oclass *oclass, void *data, u32 size,
127 struct nouveau_object **pobject)
128{
129 struct nv50_display_mast_class *args = data;
130 struct nv50_disp_dmac *mast;
131 int ret;
132
133 if (size < sizeof(*args))
134 return -EINVAL;
135
136 ret = nv50_disp_dmac_create_(parent, engine, oclass, args->pushbuf,
137 0, sizeof(*mast), (void **)&mast);
138 *pobject = nv_object(mast);
139 if (ret)
140 return ret;
141
142 nv_parent(mast)->object_attach = nvd0_disp_dmac_object_attach;
143 nv_parent(mast)->object_detach = nvd0_disp_dmac_object_detach;
144 return 0;
145}
146
147static int
148nvd0_disp_mast_init(struct nouveau_object *object)
149{
150 struct nv50_disp_priv *priv = (void *)object->engine;
151 struct nv50_disp_dmac *mast = (void *)object;
152 int ret;
153
154 ret = nv50_disp_chan_init(&mast->base);
155 if (ret)
156 return ret;
157
158 /* enable error reporting */
159 nv_mask(priv, 0x610090, 0x00000001, 0x00000001);
160 nv_mask(priv, 0x6100a0, 0x00000001, 0x00000001);
161
162 /* initialise channel for dma command submission */
163 nv_wr32(priv, 0x610494, mast->push);
164 nv_wr32(priv, 0x610498, 0x00010000);
165 nv_wr32(priv, 0x61049c, 0x00000001);
166 nv_mask(priv, 0x610490, 0x00000010, 0x00000010);
167 nv_wr32(priv, 0x640000, 0x00000000);
168 nv_wr32(priv, 0x610490, 0x01000013);
169
170 /* wait for it to go inactive */
171 if (!nv_wait(priv, 0x610490, 0x80000000, 0x00000000)) {
172 nv_error(mast, "init: 0x%08x\n", nv_rd32(priv, 0x610490));
173 return -EBUSY;
174 }
175
176 return 0;
177}
178
179static int
180nvd0_disp_mast_fini(struct nouveau_object *object, bool suspend)
181{
182 struct nv50_disp_priv *priv = (void *)object->engine;
183 struct nv50_disp_dmac *mast = (void *)object;
184
185 /* deactivate channel */
186 nv_mask(priv, 0x610490, 0x00000010, 0x00000000);
187 nv_mask(priv, 0x610490, 0x00000003, 0x00000000);
188 if (!nv_wait(priv, 0x610490, 0x001e0000, 0x00000000)) {
189 nv_error(mast, "fini: 0x%08x\n", nv_rd32(priv, 0x610490));
190 if (suspend)
191 return -EBUSY;
192 }
193
194 /* disable error reporting */
195 nv_mask(priv, 0x610090, 0x00000001, 0x00000000);
196 nv_mask(priv, 0x6100a0, 0x00000001, 0x00000000);
197
198 return nv50_disp_chan_fini(&mast->base, suspend);
199}
200
201struct nouveau_ofuncs
202nvd0_disp_mast_ofuncs = {
203 .ctor = nvd0_disp_mast_ctor,
204 .dtor = nv50_disp_dmac_dtor,
205 .init = nvd0_disp_mast_init,
206 .fini = nvd0_disp_mast_fini,
207 .rd32 = nv50_disp_chan_rd32,
208 .wr32 = nv50_disp_chan_wr32,
209};
210
211/*******************************************************************************
212 * EVO sync channel objects
213 ******************************************************************************/
214
215static int
216nvd0_disp_sync_ctor(struct nouveau_object *parent,
217 struct nouveau_object *engine,
218 struct nouveau_oclass *oclass, void *data, u32 size,
219 struct nouveau_object **pobject)
220{
221 struct nv50_display_sync_class *args = data;
222 struct nv50_disp_priv *priv = (void *)engine;
223 struct nv50_disp_dmac *dmac;
224 int ret;
225
226 if (size < sizeof(*data) || args->head >= priv->head.nr)
227 return -EINVAL;
228
229 ret = nv50_disp_dmac_create_(parent, engine, oclass, args->pushbuf,
230 1 + args->head, sizeof(*dmac),
231 (void **)&dmac);
232 *pobject = nv_object(dmac);
233 if (ret)
234 return ret;
235
236 nv_parent(dmac)->object_attach = nvd0_disp_dmac_object_attach;
237 nv_parent(dmac)->object_detach = nvd0_disp_dmac_object_detach;
238 return 0;
239}
240
241struct nouveau_ofuncs
242nvd0_disp_sync_ofuncs = {
243 .ctor = nvd0_disp_sync_ctor,
244 .dtor = nv50_disp_dmac_dtor,
245 .init = nvd0_disp_dmac_init,
246 .fini = nvd0_disp_dmac_fini,
247 .rd32 = nv50_disp_chan_rd32,
248 .wr32 = nv50_disp_chan_wr32,
249};
250
251/*******************************************************************************
252 * EVO overlay channel objects
253 ******************************************************************************/
254
255static int
256nvd0_disp_ovly_ctor(struct nouveau_object *parent,
257 struct nouveau_object *engine,
258 struct nouveau_oclass *oclass, void *data, u32 size,
259 struct nouveau_object **pobject)
260{
261 struct nv50_display_ovly_class *args = data;
262 struct nv50_disp_priv *priv = (void *)engine;
263 struct nv50_disp_dmac *dmac;
264 int ret;
265
266 if (size < sizeof(*data) || args->head >= priv->head.nr)
267 return -EINVAL;
268
269 ret = nv50_disp_dmac_create_(parent, engine, oclass, args->pushbuf,
270 5 + args->head, sizeof(*dmac),
271 (void **)&dmac);
272 *pobject = nv_object(dmac);
273 if (ret)
274 return ret;
275
276 nv_parent(dmac)->object_attach = nvd0_disp_dmac_object_attach;
277 nv_parent(dmac)->object_detach = nvd0_disp_dmac_object_detach;
278 return 0;
279}
280
281struct nouveau_ofuncs
282nvd0_disp_ovly_ofuncs = {
283 .ctor = nvd0_disp_ovly_ctor,
284 .dtor = nv50_disp_dmac_dtor,
285 .init = nvd0_disp_dmac_init,
286 .fini = nvd0_disp_dmac_fini,
287 .rd32 = nv50_disp_chan_rd32,
288 .wr32 = nv50_disp_chan_wr32,
289};
290
291/*******************************************************************************
292 * EVO PIO channel base class
293 ******************************************************************************/
294
295static int
296nvd0_disp_pioc_create_(struct nouveau_object *parent,
297 struct nouveau_object *engine,
298 struct nouveau_oclass *oclass, int chid,
299 int length, void **pobject)
300{
301 return nv50_disp_chan_create_(parent, engine, oclass, chid,
302 length, pobject);
303}
304
305static void
306nvd0_disp_pioc_dtor(struct nouveau_object *object)
307{
308 struct nv50_disp_pioc *pioc = (void *)object;
309 nv50_disp_chan_destroy(&pioc->base);
310}
311
312static int
313nvd0_disp_pioc_init(struct nouveau_object *object)
314{
315 struct nv50_disp_priv *priv = (void *)object->engine;
316 struct nv50_disp_pioc *pioc = (void *)object;
317 int chid = pioc->base.chid;
318 int ret;
319
320 ret = nv50_disp_chan_init(&pioc->base);
321 if (ret)
322 return ret;
323
324 /* enable error reporting */
325 nv_mask(priv, 0x610090, 0x00000001 << chid, 0x00000001 << chid);
326 nv_mask(priv, 0x6100a0, 0x00000001 << chid, 0x00000001 << chid);
327
328 /* activate channel */
329 nv_wr32(priv, 0x610490 + (chid * 0x10), 0x00000001);
330 if (!nv_wait(priv, 0x610490 + (chid * 0x10), 0x00030000, 0x00010000)) {
331 nv_error(pioc, "init: 0x%08x\n",
332 nv_rd32(priv, 0x610490 + (chid * 0x10)));
333 return -EBUSY;
334 }
335
336 return 0;
337}
338
339static int
340nvd0_disp_pioc_fini(struct nouveau_object *object, bool suspend)
341{
342 struct nv50_disp_priv *priv = (void *)object->engine;
343 struct nv50_disp_pioc *pioc = (void *)object;
344 int chid = pioc->base.chid;
345
346 nv_mask(priv, 0x610490 + (chid * 0x10), 0x00000001, 0x00000000);
347 if (!nv_wait(priv, 0x610490 + (chid * 0x10), 0x00030000, 0x00000000)) {
348 nv_error(pioc, "timeout: 0x%08x\n",
349 nv_rd32(priv, 0x610490 + (chid * 0x10)));
350 if (suspend)
351 return -EBUSY;
352 }
353
354 /* disable error reporting */
355 nv_mask(priv, 0x610090, 0x00000001 << chid, 0x00000000);
356 nv_mask(priv, 0x6100a0, 0x00000001 << chid, 0x00000000);
357
358 return nv50_disp_chan_fini(&pioc->base, suspend);
359}
360
361/*******************************************************************************
362 * EVO immediate overlay channel objects
363 ******************************************************************************/
364
365static int
366nvd0_disp_oimm_ctor(struct nouveau_object *parent,
367 struct nouveau_object *engine,
368 struct nouveau_oclass *oclass, void *data, u32 size,
369 struct nouveau_object **pobject)
370{
371 struct nv50_display_oimm_class *args = data;
372 struct nv50_disp_priv *priv = (void *)engine;
373 struct nv50_disp_pioc *pioc;
374 int ret;
375
376 if (size < sizeof(*args) || args->head >= priv->head.nr)
377 return -EINVAL;
378
379 ret = nvd0_disp_pioc_create_(parent, engine, oclass, 9 + args->head,
380 sizeof(*pioc), (void **)&pioc);
381 *pobject = nv_object(pioc);
382 if (ret)
383 return ret;
384
385 return 0;
386}
387
388struct nouveau_ofuncs
389nvd0_disp_oimm_ofuncs = {
390 .ctor = nvd0_disp_oimm_ctor,
391 .dtor = nvd0_disp_pioc_dtor,
392 .init = nvd0_disp_pioc_init,
393 .fini = nvd0_disp_pioc_fini,
394 .rd32 = nv50_disp_chan_rd32,
395 .wr32 = nv50_disp_chan_wr32,
396};
397
398/*******************************************************************************
399 * EVO cursor channel objects
400 ******************************************************************************/
401
402static int
403nvd0_disp_curs_ctor(struct nouveau_object *parent,
404 struct nouveau_object *engine,
405 struct nouveau_oclass *oclass, void *data, u32 size,
406 struct nouveau_object **pobject)
407{
408 struct nv50_display_curs_class *args = data;
409 struct nv50_disp_priv *priv = (void *)engine;
410 struct nv50_disp_pioc *pioc;
411 int ret;
412
413 if (size < sizeof(*args) || args->head >= priv->head.nr)
414 return -EINVAL;
415
416 ret = nvd0_disp_pioc_create_(parent, engine, oclass, 13 + args->head,
417 sizeof(*pioc), (void **)&pioc);
418 *pobject = nv_object(pioc);
419 if (ret)
420 return ret;
421
422 return 0;
423}
424
425struct nouveau_ofuncs
426nvd0_disp_curs_ofuncs = {
427 .ctor = nvd0_disp_curs_ctor,
428 .dtor = nvd0_disp_pioc_dtor,
429 .init = nvd0_disp_pioc_init,
430 .fini = nvd0_disp_pioc_fini,
431 .rd32 = nv50_disp_chan_rd32,
432 .wr32 = nv50_disp_chan_wr32,
433};
434
435/*******************************************************************************
436 * Base display object
437 ******************************************************************************/
438
439static int
440nvd0_disp_base_ctor(struct nouveau_object *parent,
441 struct nouveau_object *engine,
442 struct nouveau_oclass *oclass, void *data, u32 size,
443 struct nouveau_object **pobject)
444{
445 struct nv50_disp_priv *priv = (void *)engine;
446 struct nv50_disp_base *base;
447 int ret;
448
449 ret = nouveau_parent_create(parent, engine, oclass, 0,
450 priv->sclass, 0, &base);
451 *pobject = nv_object(base);
452 if (ret)
453 return ret;
454
455 return nouveau_ramht_new(parent, parent, 0x1000, 0, &base->ramht);
456}
457
458static void
459nvd0_disp_base_dtor(struct nouveau_object *object)
460{
461 struct nv50_disp_base *base = (void *)object;
462 nouveau_ramht_ref(NULL, &base->ramht);
463 nouveau_parent_destroy(&base->base);
464}
465
466static int
467nvd0_disp_base_init(struct nouveau_object *object)
468{
469 struct nv50_disp_priv *priv = (void *)object->engine;
470 struct nv50_disp_base *base = (void *)object;
471 int ret, i;
472 u32 tmp;
473
474 ret = nouveau_parent_init(&base->base);
475 if (ret)
476 return ret;
477
478 /* The below segments of code copying values from one register to
479 * another appear to inform EVO of the display capabilities or
480 * something similar.
481 */
482
483 /* ... CRTC caps */
484 for (i = 0; i < priv->head.nr; i++) {
485 tmp = nv_rd32(priv, 0x616104 + (i * 0x800));
486 nv_wr32(priv, 0x6101b4 + (i * 0x800), tmp);
487 tmp = nv_rd32(priv, 0x616108 + (i * 0x800));
488 nv_wr32(priv, 0x6101b8 + (i * 0x800), tmp);
489 tmp = nv_rd32(priv, 0x61610c + (i * 0x800));
490 nv_wr32(priv, 0x6101bc + (i * 0x800), tmp);
491 }
492
493 /* ... DAC caps */
494 for (i = 0; i < priv->dac.nr; i++) {
495 tmp = nv_rd32(priv, 0x61a000 + (i * 0x800));
496 nv_wr32(priv, 0x6101c0 + (i * 0x800), tmp);
497 }
498
499 /* ... SOR caps */
500 for (i = 0; i < priv->sor.nr; i++) {
501 tmp = nv_rd32(priv, 0x61c000 + (i * 0x800));
502 nv_wr32(priv, 0x6301c4 + (i * 0x800), tmp);
503 }
504
505 /* steal display away from vbios, or something like that */
506 if (nv_rd32(priv, 0x6100ac) & 0x00000100) {
507 nv_wr32(priv, 0x6100ac, 0x00000100);
508 nv_mask(priv, 0x6194e8, 0x00000001, 0x00000000);
509 if (!nv_wait(priv, 0x6194e8, 0x00000002, 0x00000000)) {
510 nv_error(priv, "timeout acquiring display\n");
511 return -EBUSY;
512 }
513 }
514
515 /* point at display engine memory area (hash table, objects) */
516 nv_wr32(priv, 0x610010, (nv_gpuobj(object->parent)->addr >> 8) | 9);
517
518 /* enable supervisor interrupts, disable everything else */
519 nv_wr32(priv, 0x610090, 0x00000000);
520 nv_wr32(priv, 0x6100a0, 0x00000000);
521 nv_wr32(priv, 0x6100b0, 0x00000307);
522
523 return 0;
524}
525
526static int
527nvd0_disp_base_fini(struct nouveau_object *object, bool suspend)
528{
529 struct nv50_disp_priv *priv = (void *)object->engine;
530 struct nv50_disp_base *base = (void *)object;
531
532 /* disable all interrupts */
533 nv_wr32(priv, 0x6100b0, 0x00000000);
534
535 return nouveau_parent_fini(&base->base, suspend);
536}
537
538struct nouveau_ofuncs
539nvd0_disp_base_ofuncs = {
540 .ctor = nvd0_disp_base_ctor,
541 .dtor = nvd0_disp_base_dtor,
542 .init = nvd0_disp_base_init,
543 .fini = nvd0_disp_base_fini,
544};
545
546static struct nouveau_oclass
547nvd0_disp_base_oclass[] = {
548 { NVD0_DISP_CLASS, &nvd0_disp_base_ofuncs },
549 {}
Ben Skeggsebb945a2012-07-20 08:17:34 +1000550};
551
552static struct nouveau_oclass
553nvd0_disp_sclass[] = {
Ben Skeggs46654062012-08-28 14:10:39 +1000554 { NVD0_DISP_MAST_CLASS, &nvd0_disp_mast_ofuncs },
555 { NVD0_DISP_SYNC_CLASS, &nvd0_disp_sync_ofuncs },
556 { NVD0_DISP_OVLY_CLASS, &nvd0_disp_ovly_ofuncs },
557 { NVD0_DISP_OIMM_CLASS, &nvd0_disp_oimm_ofuncs },
558 { NVD0_DISP_CURS_CLASS, &nvd0_disp_curs_ofuncs },
559 {}
Ben Skeggsebb945a2012-07-20 08:17:34 +1000560};
561
Ben Skeggs46654062012-08-28 14:10:39 +1000562/*******************************************************************************
563 * Display engine implementation
564 ******************************************************************************/
565
Ben Skeggsebb945a2012-07-20 08:17:34 +1000566static void
Ben Skeggs46654062012-08-28 14:10:39 +1000567nvd0_disp_intr_vblank(struct nv50_disp_priv *priv, int crtc)
Ben Skeggsebb945a2012-07-20 08:17:34 +1000568{
569 struct nouveau_bar *bar = nouveau_bar(priv);
570 struct nouveau_disp *disp = &priv->base;
571 struct nouveau_software_chan *chan, *temp;
572 unsigned long flags;
573
574 spin_lock_irqsave(&disp->vblank.lock, flags);
575 list_for_each_entry_safe(chan, temp, &disp->vblank.list, vblank.head) {
576 if (chan->vblank.crtc != crtc)
577 continue;
578
579 nv_wr32(priv, 0x001718, 0x80000000 | chan->vblank.channel);
580 bar->flush(bar);
581 nv_wr32(priv, 0x06000c, upper_32_bits(chan->vblank.offset));
582 nv_wr32(priv, 0x060010, lower_32_bits(chan->vblank.offset));
583 nv_wr32(priv, 0x060014, chan->vblank.value);
584
585 list_del(&chan->vblank.head);
586 if (disp->vblank.put)
587 disp->vblank.put(disp->vblank.data, crtc);
588 }
589 spin_unlock_irqrestore(&disp->vblank.lock, flags);
590
591 if (disp->vblank.notify)
592 disp->vblank.notify(disp->vblank.data, crtc);
593}
594
Ben Skeggs46654062012-08-28 14:10:39 +1000595void
Ben Skeggsebb945a2012-07-20 08:17:34 +1000596nvd0_disp_intr(struct nouveau_subdev *subdev)
597{
Ben Skeggs46654062012-08-28 14:10:39 +1000598 struct nv50_disp_priv *priv = (void *)subdev;
Ben Skeggsebb945a2012-07-20 08:17:34 +1000599 u32 intr = nv_rd32(priv, 0x610088);
600 int i;
601
602 for (i = 0; i < 4; i++) {
603 u32 mask = 0x01000000 << i;
604 if (mask & intr) {
605 u32 stat = nv_rd32(priv, 0x6100bc + (i * 0x800));
606 if (stat & 0x00000001)
607 nvd0_disp_intr_vblank(priv, i);
608 nv_mask(priv, 0x6100bc + (i * 0x800), 0, 0);
609 nv_rd32(priv, 0x6100c0 + (i * 0x800));
610 }
611 }
612}
613
614static int
615nvd0_disp_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
Ben Skeggs46654062012-08-28 14:10:39 +1000616 struct nouveau_oclass *oclass, void *data, u32 size,
617 struct nouveau_object **pobject)
Ben Skeggsebb945a2012-07-20 08:17:34 +1000618{
Ben Skeggs46654062012-08-28 14:10:39 +1000619 struct nv50_disp_priv *priv;
Ben Skeggsebb945a2012-07-20 08:17:34 +1000620 int ret;
621
622 ret = nouveau_disp_create(parent, engine, oclass, "PDISP",
623 "display", &priv);
624 *pobject = nv_object(priv);
625 if (ret)
626 return ret;
627
Ben Skeggs46654062012-08-28 14:10:39 +1000628 nv_engine(priv)->sclass = nvd0_disp_base_oclass;
629 nv_engine(priv)->cclass = &nv50_disp_cclass;
Ben Skeggsebb945a2012-07-20 08:17:34 +1000630 nv_subdev(priv)->intr = nvd0_disp_intr;
Ben Skeggs46654062012-08-28 14:10:39 +1000631 priv->sclass = nvd0_disp_sclass;
632 priv->head.nr = nv_rd32(priv, 0x022448);
633 priv->dac.nr = 3;
634 priv->sor.nr = 4;
Ben Skeggsebb945a2012-07-20 08:17:34 +1000635
636 INIT_LIST_HEAD(&priv->base.vblank.list);
637 spin_lock_init(&priv->base.vblank.lock);
638 return 0;
639}
640
641struct nouveau_oclass
642nvd0_disp_oclass = {
Ben Skeggs46654062012-08-28 14:10:39 +1000643 .handle = NV_ENGINE(DISP, 0x90),
Ben Skeggsebb945a2012-07-20 08:17:34 +1000644 .ofuncs = &(struct nouveau_ofuncs) {
645 .ctor = nvd0_disp_ctor,
646 .dtor = _nouveau_disp_dtor,
647 .init = _nouveau_disp_init,
648 .fini = _nouveau_disp_fini,
649 },
650};