blob: 5052071b0406dbc444f0a5483d7f621f461a6d99 [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
Ben Skeggsebb945a2012-07-20 08:17:34 +100030#include <engine/disp.h>
31
Ben Skeggs14464b82012-11-02 11:33:27 +100032#include <subdev/bios.h>
33#include <subdev/bios/dcb.h>
34#include <subdev/bios/disp.h>
35#include <subdev/bios/init.h>
36#include <subdev/bios/pll.h>
Ben Skeggs88524bc2013-03-05 10:53:54 +100037#include <subdev/devinit.h>
38#include <subdev/fb.h>
39#include <subdev/timer.h>
Ben Skeggs46654062012-08-28 14:10:39 +100040
41#include "nv50.h"
42
43/*******************************************************************************
44 * EVO DMA channel base class
45 ******************************************************************************/
46
47static int
48nvd0_disp_dmac_object_attach(struct nouveau_object *parent,
49 struct nouveau_object *object, u32 name)
50{
51 struct nv50_disp_base *base = (void *)parent->parent;
52 struct nv50_disp_chan *chan = (void *)parent;
53 u32 addr = nv_gpuobj(object)->node->offset;
54 u32 data = (chan->chid << 27) | (addr << 9) | 0x00000001;
55 return nouveau_ramht_insert(base->ramht, chan->chid, name, data);
56}
57
58static void
59nvd0_disp_dmac_object_detach(struct nouveau_object *parent, int cookie)
60{
61 struct nv50_disp_base *base = (void *)parent->parent;
62 nouveau_ramht_remove(base->ramht, cookie);
63}
64
65static int
66nvd0_disp_dmac_init(struct nouveau_object *object)
67{
68 struct nv50_disp_priv *priv = (void *)object->engine;
69 struct nv50_disp_dmac *dmac = (void *)object;
70 int chid = dmac->base.chid;
71 int ret;
72
73 ret = nv50_disp_chan_init(&dmac->base);
74 if (ret)
75 return ret;
76
77 /* enable error reporting */
78 nv_mask(priv, 0x610090, 0x00000001 << chid, 0x00000001 << chid);
79 nv_mask(priv, 0x6100a0, 0x00000001 << chid, 0x00000001 << chid);
80
81 /* initialise channel for dma command submission */
82 nv_wr32(priv, 0x610494 + (chid * 0x0010), dmac->push);
83 nv_wr32(priv, 0x610498 + (chid * 0x0010), 0x00010000);
84 nv_wr32(priv, 0x61049c + (chid * 0x0010), 0x00000001);
85 nv_mask(priv, 0x610490 + (chid * 0x0010), 0x00000010, 0x00000010);
86 nv_wr32(priv, 0x640000 + (chid * 0x1000), 0x00000000);
87 nv_wr32(priv, 0x610490 + (chid * 0x0010), 0x00000013);
88
89 /* wait for it to go inactive */
90 if (!nv_wait(priv, 0x610490 + (chid * 0x10), 0x80000000, 0x00000000)) {
91 nv_error(dmac, "init: 0x%08x\n",
92 nv_rd32(priv, 0x610490 + (chid * 0x10)));
93 return -EBUSY;
94 }
95
96 return 0;
97}
98
99static int
100nvd0_disp_dmac_fini(struct nouveau_object *object, bool suspend)
101{
102 struct nv50_disp_priv *priv = (void *)object->engine;
103 struct nv50_disp_dmac *dmac = (void *)object;
104 int chid = dmac->base.chid;
105
106 /* deactivate channel */
107 nv_mask(priv, 0x610490 + (chid * 0x0010), 0x00001010, 0x00001000);
108 nv_mask(priv, 0x610490 + (chid * 0x0010), 0x00000003, 0x00000000);
109 if (!nv_wait(priv, 0x610490 + (chid * 0x10), 0x001e0000, 0x00000000)) {
110 nv_error(dmac, "fini: 0x%08x\n",
111 nv_rd32(priv, 0x610490 + (chid * 0x10)));
112 if (suspend)
113 return -EBUSY;
114 }
115
116 /* disable error reporting */
117 nv_mask(priv, 0x610090, 0x00000001 << chid, 0x00000000);
118 nv_mask(priv, 0x6100a0, 0x00000001 << chid, 0x00000000);
119
120 return nv50_disp_chan_fini(&dmac->base, suspend);
121}
122
123/*******************************************************************************
124 * EVO master channel object
125 ******************************************************************************/
126
127static int
128nvd0_disp_mast_ctor(struct nouveau_object *parent,
129 struct nouveau_object *engine,
130 struct nouveau_oclass *oclass, void *data, u32 size,
131 struct nouveau_object **pobject)
132{
133 struct nv50_display_mast_class *args = data;
134 struct nv50_disp_dmac *mast;
135 int ret;
136
137 if (size < sizeof(*args))
138 return -EINVAL;
139
140 ret = nv50_disp_dmac_create_(parent, engine, oclass, args->pushbuf,
141 0, sizeof(*mast), (void **)&mast);
142 *pobject = nv_object(mast);
143 if (ret)
144 return ret;
145
146 nv_parent(mast)->object_attach = nvd0_disp_dmac_object_attach;
147 nv_parent(mast)->object_detach = nvd0_disp_dmac_object_detach;
148 return 0;
149}
150
151static int
152nvd0_disp_mast_init(struct nouveau_object *object)
153{
154 struct nv50_disp_priv *priv = (void *)object->engine;
155 struct nv50_disp_dmac *mast = (void *)object;
156 int ret;
157
158 ret = nv50_disp_chan_init(&mast->base);
159 if (ret)
160 return ret;
161
162 /* enable error reporting */
163 nv_mask(priv, 0x610090, 0x00000001, 0x00000001);
164 nv_mask(priv, 0x6100a0, 0x00000001, 0x00000001);
165
166 /* initialise channel for dma command submission */
167 nv_wr32(priv, 0x610494, mast->push);
168 nv_wr32(priv, 0x610498, 0x00010000);
169 nv_wr32(priv, 0x61049c, 0x00000001);
170 nv_mask(priv, 0x610490, 0x00000010, 0x00000010);
171 nv_wr32(priv, 0x640000, 0x00000000);
172 nv_wr32(priv, 0x610490, 0x01000013);
173
174 /* wait for it to go inactive */
175 if (!nv_wait(priv, 0x610490, 0x80000000, 0x00000000)) {
176 nv_error(mast, "init: 0x%08x\n", nv_rd32(priv, 0x610490));
177 return -EBUSY;
178 }
179
180 return 0;
181}
182
183static int
184nvd0_disp_mast_fini(struct nouveau_object *object, bool suspend)
185{
186 struct nv50_disp_priv *priv = (void *)object->engine;
187 struct nv50_disp_dmac *mast = (void *)object;
188
189 /* deactivate channel */
190 nv_mask(priv, 0x610490, 0x00000010, 0x00000000);
191 nv_mask(priv, 0x610490, 0x00000003, 0x00000000);
192 if (!nv_wait(priv, 0x610490, 0x001e0000, 0x00000000)) {
193 nv_error(mast, "fini: 0x%08x\n", nv_rd32(priv, 0x610490));
194 if (suspend)
195 return -EBUSY;
196 }
197
198 /* disable error reporting */
199 nv_mask(priv, 0x610090, 0x00000001, 0x00000000);
200 nv_mask(priv, 0x6100a0, 0x00000001, 0x00000000);
201
202 return nv50_disp_chan_fini(&mast->base, suspend);
203}
204
205struct nouveau_ofuncs
206nvd0_disp_mast_ofuncs = {
207 .ctor = nvd0_disp_mast_ctor,
208 .dtor = nv50_disp_dmac_dtor,
209 .init = nvd0_disp_mast_init,
210 .fini = nvd0_disp_mast_fini,
211 .rd32 = nv50_disp_chan_rd32,
212 .wr32 = nv50_disp_chan_wr32,
213};
214
215/*******************************************************************************
216 * EVO sync channel objects
217 ******************************************************************************/
218
219static int
220nvd0_disp_sync_ctor(struct nouveau_object *parent,
221 struct nouveau_object *engine,
222 struct nouveau_oclass *oclass, void *data, u32 size,
223 struct nouveau_object **pobject)
224{
225 struct nv50_display_sync_class *args = data;
226 struct nv50_disp_priv *priv = (void *)engine;
227 struct nv50_disp_dmac *dmac;
228 int ret;
229
Dan Carpenteraf1ac182013-01-23 11:27:56 +0300230 if (size < sizeof(*args) || args->head >= priv->head.nr)
Ben Skeggs46654062012-08-28 14:10:39 +1000231 return -EINVAL;
232
233 ret = nv50_disp_dmac_create_(parent, engine, oclass, args->pushbuf,
234 1 + args->head, sizeof(*dmac),
235 (void **)&dmac);
236 *pobject = nv_object(dmac);
237 if (ret)
238 return ret;
239
240 nv_parent(dmac)->object_attach = nvd0_disp_dmac_object_attach;
241 nv_parent(dmac)->object_detach = nvd0_disp_dmac_object_detach;
242 return 0;
243}
244
245struct nouveau_ofuncs
246nvd0_disp_sync_ofuncs = {
247 .ctor = nvd0_disp_sync_ctor,
248 .dtor = nv50_disp_dmac_dtor,
249 .init = nvd0_disp_dmac_init,
250 .fini = nvd0_disp_dmac_fini,
251 .rd32 = nv50_disp_chan_rd32,
252 .wr32 = nv50_disp_chan_wr32,
253};
254
255/*******************************************************************************
256 * EVO overlay channel objects
257 ******************************************************************************/
258
259static int
260nvd0_disp_ovly_ctor(struct nouveau_object *parent,
261 struct nouveau_object *engine,
262 struct nouveau_oclass *oclass, void *data, u32 size,
263 struct nouveau_object **pobject)
264{
265 struct nv50_display_ovly_class *args = data;
266 struct nv50_disp_priv *priv = (void *)engine;
267 struct nv50_disp_dmac *dmac;
268 int ret;
269
Dan Carpenteraf1ac182013-01-23 11:27:56 +0300270 if (size < sizeof(*args) || args->head >= priv->head.nr)
Ben Skeggs46654062012-08-28 14:10:39 +1000271 return -EINVAL;
272
273 ret = nv50_disp_dmac_create_(parent, engine, oclass, args->pushbuf,
274 5 + args->head, sizeof(*dmac),
275 (void **)&dmac);
276 *pobject = nv_object(dmac);
277 if (ret)
278 return ret;
279
280 nv_parent(dmac)->object_attach = nvd0_disp_dmac_object_attach;
281 nv_parent(dmac)->object_detach = nvd0_disp_dmac_object_detach;
282 return 0;
283}
284
285struct nouveau_ofuncs
286nvd0_disp_ovly_ofuncs = {
287 .ctor = nvd0_disp_ovly_ctor,
288 .dtor = nv50_disp_dmac_dtor,
289 .init = nvd0_disp_dmac_init,
290 .fini = nvd0_disp_dmac_fini,
291 .rd32 = nv50_disp_chan_rd32,
292 .wr32 = nv50_disp_chan_wr32,
293};
294
295/*******************************************************************************
296 * EVO PIO channel base class
297 ******************************************************************************/
298
299static int
300nvd0_disp_pioc_create_(struct nouveau_object *parent,
301 struct nouveau_object *engine,
302 struct nouveau_oclass *oclass, int chid,
303 int length, void **pobject)
304{
305 return nv50_disp_chan_create_(parent, engine, oclass, chid,
306 length, pobject);
307}
308
309static void
310nvd0_disp_pioc_dtor(struct nouveau_object *object)
311{
312 struct nv50_disp_pioc *pioc = (void *)object;
313 nv50_disp_chan_destroy(&pioc->base);
314}
315
316static int
317nvd0_disp_pioc_init(struct nouveau_object *object)
318{
319 struct nv50_disp_priv *priv = (void *)object->engine;
320 struct nv50_disp_pioc *pioc = (void *)object;
321 int chid = pioc->base.chid;
322 int ret;
323
324 ret = nv50_disp_chan_init(&pioc->base);
325 if (ret)
326 return ret;
327
328 /* enable error reporting */
329 nv_mask(priv, 0x610090, 0x00000001 << chid, 0x00000001 << chid);
330 nv_mask(priv, 0x6100a0, 0x00000001 << chid, 0x00000001 << chid);
331
332 /* activate channel */
333 nv_wr32(priv, 0x610490 + (chid * 0x10), 0x00000001);
334 if (!nv_wait(priv, 0x610490 + (chid * 0x10), 0x00030000, 0x00010000)) {
335 nv_error(pioc, "init: 0x%08x\n",
336 nv_rd32(priv, 0x610490 + (chid * 0x10)));
337 return -EBUSY;
338 }
339
340 return 0;
341}
342
343static int
344nvd0_disp_pioc_fini(struct nouveau_object *object, bool suspend)
345{
346 struct nv50_disp_priv *priv = (void *)object->engine;
347 struct nv50_disp_pioc *pioc = (void *)object;
348 int chid = pioc->base.chid;
349
350 nv_mask(priv, 0x610490 + (chid * 0x10), 0x00000001, 0x00000000);
351 if (!nv_wait(priv, 0x610490 + (chid * 0x10), 0x00030000, 0x00000000)) {
352 nv_error(pioc, "timeout: 0x%08x\n",
353 nv_rd32(priv, 0x610490 + (chid * 0x10)));
354 if (suspend)
355 return -EBUSY;
356 }
357
358 /* disable error reporting */
359 nv_mask(priv, 0x610090, 0x00000001 << chid, 0x00000000);
360 nv_mask(priv, 0x6100a0, 0x00000001 << chid, 0x00000000);
361
362 return nv50_disp_chan_fini(&pioc->base, suspend);
363}
364
365/*******************************************************************************
366 * EVO immediate overlay channel objects
367 ******************************************************************************/
368
369static int
370nvd0_disp_oimm_ctor(struct nouveau_object *parent,
371 struct nouveau_object *engine,
372 struct nouveau_oclass *oclass, void *data, u32 size,
373 struct nouveau_object **pobject)
374{
375 struct nv50_display_oimm_class *args = data;
376 struct nv50_disp_priv *priv = (void *)engine;
377 struct nv50_disp_pioc *pioc;
378 int ret;
379
380 if (size < sizeof(*args) || args->head >= priv->head.nr)
381 return -EINVAL;
382
383 ret = nvd0_disp_pioc_create_(parent, engine, oclass, 9 + args->head,
384 sizeof(*pioc), (void **)&pioc);
385 *pobject = nv_object(pioc);
386 if (ret)
387 return ret;
388
389 return 0;
390}
391
392struct nouveau_ofuncs
393nvd0_disp_oimm_ofuncs = {
394 .ctor = nvd0_disp_oimm_ctor,
395 .dtor = nvd0_disp_pioc_dtor,
396 .init = nvd0_disp_pioc_init,
397 .fini = nvd0_disp_pioc_fini,
398 .rd32 = nv50_disp_chan_rd32,
399 .wr32 = nv50_disp_chan_wr32,
400};
401
402/*******************************************************************************
403 * EVO cursor channel objects
404 ******************************************************************************/
405
406static int
407nvd0_disp_curs_ctor(struct nouveau_object *parent,
408 struct nouveau_object *engine,
409 struct nouveau_oclass *oclass, void *data, u32 size,
410 struct nouveau_object **pobject)
411{
412 struct nv50_display_curs_class *args = data;
413 struct nv50_disp_priv *priv = (void *)engine;
414 struct nv50_disp_pioc *pioc;
415 int ret;
416
417 if (size < sizeof(*args) || args->head >= priv->head.nr)
418 return -EINVAL;
419
420 ret = nvd0_disp_pioc_create_(parent, engine, oclass, 13 + args->head,
421 sizeof(*pioc), (void **)&pioc);
422 *pobject = nv_object(pioc);
423 if (ret)
424 return ret;
425
426 return 0;
427}
428
429struct nouveau_ofuncs
430nvd0_disp_curs_ofuncs = {
431 .ctor = nvd0_disp_curs_ctor,
432 .dtor = nvd0_disp_pioc_dtor,
433 .init = nvd0_disp_pioc_init,
434 .fini = nvd0_disp_pioc_fini,
435 .rd32 = nv50_disp_chan_rd32,
436 .wr32 = nv50_disp_chan_wr32,
437};
438
439/*******************************************************************************
440 * Base display object
441 ******************************************************************************/
442
Ben Skeggsd2fa7d32013-11-14 13:37:48 +1000443static int
444nvd0_disp_base_scanoutpos(struct nouveau_object *object, u32 mthd,
445 void *data, u32 size)
446{
447 struct nv50_disp_priv *priv = (void *)object->engine;
448 struct nv04_display_scanoutpos *args = data;
449 const int head = (mthd & NV50_DISP_MTHD_HEAD);
450 u32 blanke, blanks, total;
451
452 if (size < sizeof(*args) || head >= priv->head.nr)
453 return -EINVAL;
454
455 total = nv_rd32(priv, 0x640414 + (head * 0x300));
456 blanke = nv_rd32(priv, 0x64041c + (head * 0x300));
457 blanks = nv_rd32(priv, 0x640420 + (head * 0x300));
458
459 args->vblanke = (blanke & 0xffff0000) >> 16;
460 args->hblanke = (blanke & 0x0000ffff);
461 args->vblanks = (blanks & 0xffff0000) >> 16;
462 args->hblanks = (blanks & 0x0000ffff);
463 args->vtotal = ( total & 0xffff0000) >> 16;
464 args->htotal = ( total & 0x0000ffff);
465
466 args->time[0] = ktime_to_ns(ktime_get());
467 args->vline = nv_rd32(priv, 0x616340 + (head * 0x800)) & 0xffff;
468 args->time[1] = ktime_to_ns(ktime_get()); /* vline read locks hline */
469 args->hline = nv_rd32(priv, 0x616344 + (head * 0x800)) & 0xffff;
470 return 0;
471}
472
Ben Skeggs1d7c71a2013-01-31 09:23:34 +1000473static void
474nvd0_disp_base_vblank_enable(struct nouveau_event *event, int head)
475{
476 nv_mask(event->priv, 0x6100c0 + (head * 0x800), 0x00000001, 0x00000001);
477}
478
479static void
480nvd0_disp_base_vblank_disable(struct nouveau_event *event, int head)
481{
482 nv_mask(event->priv, 0x6100c0 + (head * 0x800), 0x00000001, 0x00000000);
483}
484
Ben Skeggs46654062012-08-28 14:10:39 +1000485static int
486nvd0_disp_base_ctor(struct nouveau_object *parent,
487 struct nouveau_object *engine,
488 struct nouveau_oclass *oclass, void *data, u32 size,
489 struct nouveau_object **pobject)
490{
491 struct nv50_disp_priv *priv = (void *)engine;
492 struct nv50_disp_base *base;
493 int ret;
494
495 ret = nouveau_parent_create(parent, engine, oclass, 0,
496 priv->sclass, 0, &base);
497 *pobject = nv_object(base);
498 if (ret)
499 return ret;
500
Ben Skeggs1d7c71a2013-01-31 09:23:34 +1000501 priv->base.vblank->priv = priv;
502 priv->base.vblank->enable = nvd0_disp_base_vblank_enable;
503 priv->base.vblank->disable = nvd0_disp_base_vblank_disable;
504
Ben Skeggs2ecda482013-04-24 18:04:22 +1000505 return nouveau_ramht_new(nv_object(base), nv_object(base), 0x1000, 0,
506 &base->ramht);
Ben Skeggs46654062012-08-28 14:10:39 +1000507}
508
509static void
510nvd0_disp_base_dtor(struct nouveau_object *object)
511{
512 struct nv50_disp_base *base = (void *)object;
513 nouveau_ramht_ref(NULL, &base->ramht);
514 nouveau_parent_destroy(&base->base);
515}
516
517static int
518nvd0_disp_base_init(struct nouveau_object *object)
519{
520 struct nv50_disp_priv *priv = (void *)object->engine;
521 struct nv50_disp_base *base = (void *)object;
522 int ret, i;
523 u32 tmp;
524
525 ret = nouveau_parent_init(&base->base);
526 if (ret)
527 return ret;
528
529 /* The below segments of code copying values from one register to
530 * another appear to inform EVO of the display capabilities or
531 * something similar.
532 */
533
534 /* ... CRTC caps */
535 for (i = 0; i < priv->head.nr; i++) {
536 tmp = nv_rd32(priv, 0x616104 + (i * 0x800));
537 nv_wr32(priv, 0x6101b4 + (i * 0x800), tmp);
538 tmp = nv_rd32(priv, 0x616108 + (i * 0x800));
539 nv_wr32(priv, 0x6101b8 + (i * 0x800), tmp);
540 tmp = nv_rd32(priv, 0x61610c + (i * 0x800));
541 nv_wr32(priv, 0x6101bc + (i * 0x800), tmp);
542 }
543
544 /* ... DAC caps */
545 for (i = 0; i < priv->dac.nr; i++) {
546 tmp = nv_rd32(priv, 0x61a000 + (i * 0x800));
547 nv_wr32(priv, 0x6101c0 + (i * 0x800), tmp);
548 }
549
550 /* ... SOR caps */
551 for (i = 0; i < priv->sor.nr; i++) {
552 tmp = nv_rd32(priv, 0x61c000 + (i * 0x800));
553 nv_wr32(priv, 0x6301c4 + (i * 0x800), tmp);
554 }
555
556 /* steal display away from vbios, or something like that */
557 if (nv_rd32(priv, 0x6100ac) & 0x00000100) {
558 nv_wr32(priv, 0x6100ac, 0x00000100);
559 nv_mask(priv, 0x6194e8, 0x00000001, 0x00000000);
560 if (!nv_wait(priv, 0x6194e8, 0x00000002, 0x00000000)) {
561 nv_error(priv, "timeout acquiring display\n");
562 return -EBUSY;
563 }
564 }
565
566 /* point at display engine memory area (hash table, objects) */
567 nv_wr32(priv, 0x610010, (nv_gpuobj(object->parent)->addr >> 8) | 9);
568
569 /* enable supervisor interrupts, disable everything else */
570 nv_wr32(priv, 0x610090, 0x00000000);
571 nv_wr32(priv, 0x6100a0, 0x00000000);
572 nv_wr32(priv, 0x6100b0, 0x00000307);
573
Ben Skeggse8d95b22013-10-25 09:59:14 +1000574 /* disable underflow reporting, preventing an intermittent issue
575 * on some nve4 boards where the production vbios left this
576 * setting enabled by default.
577 *
578 * ftp://download.nvidia.com/open-gpu-doc/gk104-disable-underflow-reporting/1/gk104-disable-underflow-reporting.txt
579 */
580 for (i = 0; i < priv->head.nr; i++)
581 nv_mask(priv, 0x616308 + (i * 0x800), 0x00000111, 0x00000010);
582
Ben Skeggs46654062012-08-28 14:10:39 +1000583 return 0;
584}
585
586static int
587nvd0_disp_base_fini(struct nouveau_object *object, bool suspend)
588{
589 struct nv50_disp_priv *priv = (void *)object->engine;
590 struct nv50_disp_base *base = (void *)object;
591
592 /* disable all interrupts */
593 nv_wr32(priv, 0x6100b0, 0x00000000);
594
595 return nouveau_parent_fini(&base->base, suspend);
596}
597
598struct nouveau_ofuncs
599nvd0_disp_base_ofuncs = {
600 .ctor = nvd0_disp_base_ctor,
601 .dtor = nvd0_disp_base_dtor,
602 .init = nvd0_disp_base_init,
603 .fini = nvd0_disp_base_fini,
604};
605
Ben Skeggsd2fa7d32013-11-14 13:37:48 +1000606struct nouveau_omthds
607nvd0_disp_base_omthds[] = {
608 { HEAD_MTHD(NV50_DISP_SCANOUTPOS) , nvd0_disp_base_scanoutpos },
609 { SOR_MTHD(NV50_DISP_SOR_PWR) , nv50_sor_mthd },
610 { SOR_MTHD(NVA3_DISP_SOR_HDA_ELD) , nv50_sor_mthd },
611 { SOR_MTHD(NV84_DISP_SOR_HDMI_PWR) , nv50_sor_mthd },
612 { SOR_MTHD(NV50_DISP_SOR_LVDS_SCRIPT) , nv50_sor_mthd },
613 { DAC_MTHD(NV50_DISP_DAC_PWR) , nv50_dac_mthd },
614 { DAC_MTHD(NV50_DISP_DAC_LOAD) , nv50_dac_mthd },
615 { PIOR_MTHD(NV50_DISP_PIOR_PWR) , nv50_pior_mthd },
616 { PIOR_MTHD(NV50_DISP_PIOR_TMDS_PWR) , nv50_pior_mthd },
617 { PIOR_MTHD(NV50_DISP_PIOR_DP_PWR) , nv50_pior_mthd },
618 {},
619};
620
Ben Skeggs46654062012-08-28 14:10:39 +1000621static struct nouveau_oclass
622nvd0_disp_base_oclass[] = {
Ben Skeggsd2fa7d32013-11-14 13:37:48 +1000623 { NVD0_DISP_CLASS, &nvd0_disp_base_ofuncs, nvd0_disp_base_omthds },
Ben Skeggs46654062012-08-28 14:10:39 +1000624 {}
Ben Skeggsebb945a2012-07-20 08:17:34 +1000625};
626
627static struct nouveau_oclass
628nvd0_disp_sclass[] = {
Ben Skeggs46654062012-08-28 14:10:39 +1000629 { NVD0_DISP_MAST_CLASS, &nvd0_disp_mast_ofuncs },
630 { NVD0_DISP_SYNC_CLASS, &nvd0_disp_sync_ofuncs },
631 { NVD0_DISP_OVLY_CLASS, &nvd0_disp_ovly_ofuncs },
632 { NVD0_DISP_OIMM_CLASS, &nvd0_disp_oimm_ofuncs },
633 { NVD0_DISP_CURS_CLASS, &nvd0_disp_curs_ofuncs },
634 {}
Ben Skeggsebb945a2012-07-20 08:17:34 +1000635};
636
Ben Skeggs46654062012-08-28 14:10:39 +1000637/*******************************************************************************
638 * Display engine implementation
639 ******************************************************************************/
640
Ben Skeggs14464b82012-11-02 11:33:27 +1000641static u16
642exec_lookup(struct nv50_disp_priv *priv, int head, int outp, u32 ctrl,
643 struct dcb_output *dcb, u8 *ver, u8 *hdr, u8 *cnt, u8 *len,
644 struct nvbios_outp *info)
645{
646 struct nouveau_bios *bios = nouveau_bios(priv);
Ben Skeggs75f86932012-11-08 17:41:06 +1000647 u16 mask, type, data;
Ben Skeggs14464b82012-11-02 11:33:27 +1000648
649 if (outp < 4) {
650 type = DCB_OUTPUT_ANALOG;
651 mask = 0;
652 } else {
653 outp -= 4;
654 switch (ctrl & 0x00000f00) {
655 case 0x00000000: type = DCB_OUTPUT_LVDS; mask = 1; break;
656 case 0x00000100: type = DCB_OUTPUT_TMDS; mask = 1; break;
657 case 0x00000200: type = DCB_OUTPUT_TMDS; mask = 2; break;
658 case 0x00000500: type = DCB_OUTPUT_TMDS; mask = 3; break;
659 case 0x00000800: type = DCB_OUTPUT_DP; mask = 1; break;
660 case 0x00000900: type = DCB_OUTPUT_DP; mask = 2; break;
661 default:
662 nv_error(priv, "unknown SOR mc 0x%08x\n", ctrl);
663 return 0x0000;
664 }
665 dcb->sorconf.link = mask;
666 }
667
668 mask = 0x00c0 & (mask << 6);
669 mask |= 0x0001 << outp;
670 mask |= 0x0100 << head;
671
Ben Skeggs75f86932012-11-08 17:41:06 +1000672 data = dcb_outp_match(bios, type, mask, ver, hdr, dcb);
673 if (!data)
674 return 0x0000;
Ben Skeggs14464b82012-11-02 11:33:27 +1000675
Ben Skeggs75f86932012-11-08 17:41:06 +1000676 return nvbios_outp_match(bios, type, mask, ver, hdr, cnt, len, info);
Ben Skeggs14464b82012-11-02 11:33:27 +1000677}
678
679static bool
Ben Skeggs4ea253a2013-02-20 19:21:08 +1000680exec_script(struct nv50_disp_priv *priv, int head, int id)
Ben Skeggs14464b82012-11-02 11:33:27 +1000681{
682 struct nouveau_bios *bios = nouveau_bios(priv);
683 struct nvbios_outp info;
684 struct dcb_output dcb;
685 u8 ver, hdr, cnt, len;
Ben Skeggs4ea253a2013-02-20 19:21:08 +1000686 u32 ctrl = 0x00000000;
Ben Skeggs14464b82012-11-02 11:33:27 +1000687 u16 data;
Ben Skeggs4ea253a2013-02-20 19:21:08 +1000688 int outp;
689
690 for (outp = 0; !(ctrl & (1 << head)) && outp < 8; outp++) {
691 ctrl = nv_rd32(priv, 0x640180 + (outp * 0x20));
692 if (ctrl & (1 << head))
693 break;
694 }
695
696 if (outp == 8)
697 return false;
Ben Skeggs14464b82012-11-02 11:33:27 +1000698
699 data = exec_lookup(priv, head, outp, ctrl, &dcb, &ver, &hdr, &cnt, &len, &info);
700 if (data) {
701 struct nvbios_init init = {
702 .subdev = nv_subdev(priv),
703 .bios = bios,
704 .offset = info.script[id],
705 .outp = &dcb,
706 .crtc = head,
707 .execute = 1,
708 };
709
710 return nvbios_exec(&init) == 0;
711 }
712
713 return false;
714}
715
Ben Skeggs4a230fa2012-11-09 11:25:37 +1000716static u32
Ben Skeggs4ea253a2013-02-20 19:21:08 +1000717exec_clkcmp(struct nv50_disp_priv *priv, int head, int id,
718 u32 pclk, struct dcb_output *dcb)
Ben Skeggs14464b82012-11-02 11:33:27 +1000719{
720 struct nouveau_bios *bios = nouveau_bios(priv);
721 struct nvbios_outp info1;
722 struct nvbios_ocfg info2;
Ben Skeggs14464b82012-11-02 11:33:27 +1000723 u8 ver, hdr, cnt, len;
Ben Skeggs4ea253a2013-02-20 19:21:08 +1000724 u32 ctrl = 0x00000000;
Ben Skeggs46c13c12013-02-16 13:49:21 +1000725 u32 data, conf = ~0;
Ben Skeggs4ea253a2013-02-20 19:21:08 +1000726 int outp;
727
728 for (outp = 0; !(ctrl & (1 << head)) && outp < 8; outp++) {
729 ctrl = nv_rd32(priv, 0x660180 + (outp * 0x20));
730 if (ctrl & (1 << head))
731 break;
732 }
733
734 if (outp == 8)
735 return false;
Ben Skeggs14464b82012-11-02 11:33:27 +1000736
Ben Skeggs0a0afd22013-02-18 23:17:53 -0500737 data = exec_lookup(priv, head, outp, ctrl, dcb, &ver, &hdr, &cnt, &len, &info1);
Ben Skeggs4a230fa2012-11-09 11:25:37 +1000738 if (data == 0x0000)
Ben Skeggs46c13c12013-02-16 13:49:21 +1000739 return conf;
Ben Skeggs14464b82012-11-02 11:33:27 +1000740
Ben Skeggs0a0afd22013-02-18 23:17:53 -0500741 switch (dcb->type) {
Ben Skeggs4a230fa2012-11-09 11:25:37 +1000742 case DCB_OUTPUT_TMDS:
743 conf = (ctrl & 0x00000f00) >> 8;
744 if (pclk >= 165000)
745 conf |= 0x0100;
746 break;
747 case DCB_OUTPUT_LVDS:
748 conf = priv->sor.lvdsconf;
749 break;
750 case DCB_OUTPUT_DP:
751 conf = (ctrl & 0x00000f00) >> 8;
752 break;
753 case DCB_OUTPUT_ANALOG:
754 default:
755 conf = 0x00ff;
756 break;
757 }
758
759 data = nvbios_ocfg_match(bios, data, conf, &ver, &hdr, &cnt, &len, &info2);
Ben Skeggs0a0afd22013-02-18 23:17:53 -0500760 if (data && id < 0xff) {
Ben Skeggs4a230fa2012-11-09 11:25:37 +1000761 data = nvbios_oclk_match(bios, info2.clkcmp[id], pclk);
762 if (data) {
763 struct nvbios_init init = {
764 .subdev = nv_subdev(priv),
765 .bios = bios,
766 .offset = data,
Ben Skeggs0a0afd22013-02-18 23:17:53 -0500767 .outp = dcb,
Ben Skeggs4a230fa2012-11-09 11:25:37 +1000768 .crtc = head,
769 .execute = 1,
770 };
771
Ben Skeggs46c13c12013-02-16 13:49:21 +1000772 nvbios_exec(&init);
Ben Skeggs14464b82012-11-02 11:33:27 +1000773 }
774 }
775
Ben Skeggs46c13c12013-02-16 13:49:21 +1000776 return conf;
Ben Skeggs14464b82012-11-02 11:33:27 +1000777}
778
779static void
Ben Skeggs4ea253a2013-02-20 19:21:08 +1000780nvd0_disp_intr_unk1_0(struct nv50_disp_priv *priv, int head)
Ben Skeggs14464b82012-11-02 11:33:27 +1000781{
Ben Skeggs4ea253a2013-02-20 19:21:08 +1000782 exec_script(priv, head, 1);
Ben Skeggs14464b82012-11-02 11:33:27 +1000783}
784
785static void
Ben Skeggs4ea253a2013-02-20 19:21:08 +1000786nvd0_disp_intr_unk2_0(struct nv50_disp_priv *priv, int head)
Ben Skeggsed58aee2012-11-08 14:59:26 +1000787{
Ben Skeggs4ea253a2013-02-20 19:21:08 +1000788 exec_script(priv, head, 2);
789}
790
791static void
792nvd0_disp_intr_unk2_1(struct nv50_disp_priv *priv, int head)
793{
Ben Skeggs88524bc2013-03-05 10:53:54 +1000794 struct nouveau_devinit *devinit = nouveau_devinit(priv);
Ben Skeggs4ea253a2013-02-20 19:21:08 +1000795 u32 pclk = nv_rd32(priv, 0x660450 + (head * 0x300)) / 1000;
796 if (pclk)
Ben Skeggs88524bc2013-03-05 10:53:54 +1000797 devinit->pll_set(devinit, PLL_VPLL0 + head, pclk);
Ben Skeggs4ea253a2013-02-20 19:21:08 +1000798 nv_wr32(priv, 0x612200 + (head * 0x800), 0x00000000);
799}
800
801static void
802nvd0_disp_intr_unk2_2_tu(struct nv50_disp_priv *priv, int head,
803 struct dcb_output *outp)
804{
805 const int or = ffs(outp->or) - 1;
Ben Skeggsed58aee2012-11-08 14:59:26 +1000806 const u32 ctrl = nv_rd32(priv, 0x660200 + (or * 0x020));
807 const u32 conf = nv_rd32(priv, 0x660404 + (head * 0x300));
808 const u32 pclk = nv_rd32(priv, 0x660450 + (head * 0x300)) / 1000;
Ben Skeggsed58aee2012-11-08 14:59:26 +1000809 const u32 link = ((ctrl & 0xf00) == 0x800) ? 0 : 1;
810 const u32 hoff = (head * 0x800);
811 const u32 soff = ( or * 0x800);
812 const u32 loff = (link * 0x080) + soff;
813 const u32 symbol = 100000;
814 const u32 TU = 64;
815 u32 dpctrl = nv_rd32(priv, 0x61c10c + loff) & 0x000f0000;
816 u32 clksor = nv_rd32(priv, 0x612300 + soff);
Ben Skeggsbf2c8862012-11-21 14:49:54 +1000817 u32 datarate, link_nr, link_bw, bits;
Ben Skeggsed58aee2012-11-08 14:59:26 +1000818 u64 ratio, value;
819
Ben Skeggsbf2c8862012-11-21 14:49:54 +1000820 if ((conf & 0x3c0) == 0x180) bits = 30;
821 else if ((conf & 0x3c0) == 0x140) bits = 24;
822 else bits = 18;
823 datarate = (pclk * bits) / 8;
824
Ben Skeggsed58aee2012-11-08 14:59:26 +1000825 if (dpctrl > 0x00030000) link_nr = 4;
826 else if (dpctrl > 0x00010000) link_nr = 2;
827 else link_nr = 1;
828
829 link_bw = (clksor & 0x007c0000) >> 18;
830 link_bw *= 27000;
831
832 ratio = datarate;
833 ratio *= symbol;
834 do_div(ratio, link_nr * link_bw);
835
836 value = (symbol - ratio) * TU;
837 value *= ratio;
838 do_div(value, symbol);
839 do_div(value, symbol);
840
841 value += 5;
842 value |= 0x08000000;
843
844 nv_wr32(priv, 0x616610 + hoff, value);
845}
846
847static void
Ben Skeggs4ea253a2013-02-20 19:21:08 +1000848nvd0_disp_intr_unk2_2(struct nv50_disp_priv *priv, int head)
Ben Skeggs14464b82012-11-02 11:33:27 +1000849{
Ben Skeggs0a0afd22013-02-18 23:17:53 -0500850 struct dcb_output outp;
Ben Skeggs4ea253a2013-02-20 19:21:08 +1000851 u32 pclk = nv_rd32(priv, 0x660450 + (head * 0x300)) / 1000;
852 u32 conf = exec_clkcmp(priv, head, 0xff, pclk, &outp);
853 if (conf != ~0) {
854 u32 addr, data;
Ben Skeggs14464b82012-11-02 11:33:27 +1000855
Ben Skeggs4ea253a2013-02-20 19:21:08 +1000856 if (outp.type == DCB_OUTPUT_DP) {
857 u32 sync = nv_rd32(priv, 0x660404 + (head * 0x300));
858 switch ((sync & 0x000003c0) >> 6) {
859 case 6: pclk = pclk * 30 / 8; break;
860 case 5: pclk = pclk * 24 / 8; break;
861 case 2:
862 default:
863 pclk = pclk * 18 / 8;
864 break;
Ben Skeggs14464b82012-11-02 11:33:27 +1000865 }
Ben Skeggs14464b82012-11-02 11:33:27 +1000866
Ben Skeggs4ea253a2013-02-20 19:21:08 +1000867 nouveau_dp_train(&priv->base, priv->sor.dp,
868 &outp, head, pclk);
869 }
870
871 exec_clkcmp(priv, head, 0, pclk, &outp);
872
873 if (outp.type == DCB_OUTPUT_ANALOG) {
874 addr = 0x612280 + (ffs(outp.or) - 1) * 0x800;
875 data = 0x00000000;
876 } else {
877 if (outp.type == DCB_OUTPUT_DP)
878 nvd0_disp_intr_unk2_2_tu(priv, head, &outp);
879 addr = 0x612300 + (ffs(outp.or) - 1) * 0x800;
880 data = (conf & 0x0100) ? 0x00000101 : 0x00000000;
881 }
882
883 nv_mask(priv, addr, 0x00000707, data);
884 }
Ben Skeggs14464b82012-11-02 11:33:27 +1000885}
886
887static void
Ben Skeggs4ea253a2013-02-20 19:21:08 +1000888nvd0_disp_intr_unk4_0(struct nv50_disp_priv *priv, int head)
Ben Skeggs14464b82012-11-02 11:33:27 +1000889{
Ben Skeggs0a0afd22013-02-18 23:17:53 -0500890 struct dcb_output outp;
Ben Skeggs4ea253a2013-02-20 19:21:08 +1000891 u32 pclk = nv_rd32(priv, 0x660450 + (head * 0x300)) / 1000;
892 exec_clkcmp(priv, head, 1, pclk, &outp);
Ben Skeggs14464b82012-11-02 11:33:27 +1000893}
894
Ben Skeggs46654062012-08-28 14:10:39 +1000895void
Ben Skeggs5cc027f2013-02-18 17:50:51 -0500896nvd0_disp_intr_supervisor(struct work_struct *work)
897{
898 struct nv50_disp_priv *priv =
899 container_of(work, struct nv50_disp_priv, supervisor);
Ben Skeggs4ea253a2013-02-20 19:21:08 +1000900 u32 mask[4];
901 int head;
Ben Skeggs5cc027f2013-02-18 17:50:51 -0500902
Ben Skeggs4ea253a2013-02-20 19:21:08 +1000903 nv_debug(priv, "supervisor %08x\n", priv->super);
904 for (head = 0; head < priv->head.nr; head++) {
905 mask[head] = nv_rd32(priv, 0x6101d4 + (head * 0x800));
906 nv_debug(priv, "head %d: 0x%08x\n", head, mask[head]);
907 }
Ben Skeggs5cc027f2013-02-18 17:50:51 -0500908
Ben Skeggs4ea253a2013-02-20 19:21:08 +1000909 if (priv->super & 0x00000001) {
910 for (head = 0; head < priv->head.nr; head++) {
911 if (!(mask[head] & 0x00001000))
912 continue;
913 nvd0_disp_intr_unk1_0(priv, head);
914 }
915 } else
916 if (priv->super & 0x00000002) {
917 for (head = 0; head < priv->head.nr; head++) {
918 if (!(mask[head] & 0x00001000))
919 continue;
920 nvd0_disp_intr_unk2_0(priv, head);
921 }
922 for (head = 0; head < priv->head.nr; head++) {
923 if (!(mask[head] & 0x00010000))
924 continue;
925 nvd0_disp_intr_unk2_1(priv, head);
926 }
927 for (head = 0; head < priv->head.nr; head++) {
928 if (!(mask[head] & 0x00001000))
929 continue;
930 nvd0_disp_intr_unk2_2(priv, head);
931 }
932 } else
933 if (priv->super & 0x00000004) {
934 for (head = 0; head < priv->head.nr; head++) {
935 if (!(mask[head] & 0x00001000))
936 continue;
937 nvd0_disp_intr_unk4_0(priv, head);
938 }
939 }
Ben Skeggs5cc027f2013-02-18 17:50:51 -0500940
Ben Skeggs4ea253a2013-02-20 19:21:08 +1000941 for (head = 0; head < priv->head.nr; head++)
942 nv_wr32(priv, 0x6101d4 + (head * 0x800), 0x00000000);
943 nv_wr32(priv, 0x6101d0, 0x80000000);
Ben Skeggs5cc027f2013-02-18 17:50:51 -0500944}
945
946void
Ben Skeggsebb945a2012-07-20 08:17:34 +1000947nvd0_disp_intr(struct nouveau_subdev *subdev)
948{
Ben Skeggs46654062012-08-28 14:10:39 +1000949 struct nv50_disp_priv *priv = (void *)subdev;
Ben Skeggsebb945a2012-07-20 08:17:34 +1000950 u32 intr = nv_rd32(priv, 0x610088);
951 int i;
952
Ben Skeggs14464b82012-11-02 11:33:27 +1000953 if (intr & 0x00000001) {
954 u32 stat = nv_rd32(priv, 0x61008c);
955 nv_wr32(priv, 0x61008c, stat);
956 intr &= ~0x00000001;
957 }
958
959 if (intr & 0x00000002) {
960 u32 stat = nv_rd32(priv, 0x61009c);
961 int chid = ffs(stat) - 1;
962 if (chid >= 0) {
963 u32 mthd = nv_rd32(priv, 0x6101f0 + (chid * 12));
964 u32 data = nv_rd32(priv, 0x6101f4 + (chid * 12));
965 u32 unkn = nv_rd32(priv, 0x6101f8 + (chid * 12));
966
967 nv_error(priv, "chid %d mthd 0x%04x data 0x%08x "
968 "0x%08x 0x%08x\n",
969 chid, (mthd & 0x0000ffc), data, mthd, unkn);
970 nv_wr32(priv, 0x61009c, (1 << chid));
971 nv_wr32(priv, 0x6101f0 + (chid * 12), 0x90000000);
972 }
973
974 intr &= ~0x00000002;
975 }
976
977 if (intr & 0x00100000) {
978 u32 stat = nv_rd32(priv, 0x6100ac);
Ben Skeggs5cc027f2013-02-18 17:50:51 -0500979 if (stat & 0x00000007) {
980 priv->super = (stat & 0x00000007);
981 schedule_work(&priv->supervisor);
982 nv_wr32(priv, 0x6100ac, priv->super);
983 stat &= ~0x00000007;
Ben Skeggs14464b82012-11-02 11:33:27 +1000984 }
985
986 if (stat) {
987 nv_info(priv, "unknown intr24 0x%08x\n", stat);
988 nv_wr32(priv, 0x6100ac, stat);
989 }
990
991 intr &= ~0x00100000;
992 }
993
994 for (i = 0; i < priv->head.nr; i++) {
Ben Skeggsebb945a2012-07-20 08:17:34 +1000995 u32 mask = 0x01000000 << i;
996 if (mask & intr) {
997 u32 stat = nv_rd32(priv, 0x6100bc + (i * 0x800));
998 if (stat & 0x00000001)
Ben Skeggs1d7c71a2013-01-31 09:23:34 +1000999 nouveau_event_trigger(priv->base.vblank, i);
Ben Skeggsebb945a2012-07-20 08:17:34 +10001000 nv_mask(priv, 0x6100bc + (i * 0x800), 0, 0);
1001 nv_rd32(priv, 0x6100c0 + (i * 0x800));
1002 }
1003 }
1004}
1005
1006static int
1007nvd0_disp_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
Ben Skeggs46654062012-08-28 14:10:39 +10001008 struct nouveau_oclass *oclass, void *data, u32 size,
1009 struct nouveau_object **pobject)
Ben Skeggsebb945a2012-07-20 08:17:34 +10001010{
Ben Skeggs46654062012-08-28 14:10:39 +10001011 struct nv50_disp_priv *priv;
Ben Skeggs1d7c71a2013-01-31 09:23:34 +10001012 int heads = nv_rd32(parent, 0x022448);
Ben Skeggsebb945a2012-07-20 08:17:34 +10001013 int ret;
1014
Ben Skeggs1d7c71a2013-01-31 09:23:34 +10001015 ret = nouveau_disp_create(parent, engine, oclass, heads,
1016 "PDISP", "display", &priv);
Ben Skeggsebb945a2012-07-20 08:17:34 +10001017 *pobject = nv_object(priv);
1018 if (ret)
1019 return ret;
1020
Ben Skeggs46654062012-08-28 14:10:39 +10001021 nv_engine(priv)->sclass = nvd0_disp_base_oclass;
1022 nv_engine(priv)->cclass = &nv50_disp_cclass;
Ben Skeggsebb945a2012-07-20 08:17:34 +10001023 nv_subdev(priv)->intr = nvd0_disp_intr;
Ben Skeggs5cc027f2013-02-18 17:50:51 -05001024 INIT_WORK(&priv->supervisor, nvd0_disp_intr_supervisor);
Ben Skeggs46654062012-08-28 14:10:39 +10001025 priv->sclass = nvd0_disp_sclass;
Ben Skeggs1d7c71a2013-01-31 09:23:34 +10001026 priv->head.nr = heads;
Ben Skeggs46654062012-08-28 14:10:39 +10001027 priv->dac.nr = 3;
1028 priv->sor.nr = 4;
Ben Skeggs35b21d32012-11-08 12:08:55 +10001029 priv->dac.power = nv50_dac_power;
1030 priv->dac.sense = nv50_dac_sense;
Ben Skeggs74b66852012-11-08 12:01:39 +10001031 priv->sor.power = nv50_sor_power;
Ben Skeggs0a9e2b952012-11-08 14:03:56 +10001032 priv->sor.hda_eld = nvd0_hda_eld;
Ben Skeggs1c30cd02012-11-08 14:22:28 +10001033 priv->sor.hdmi = nvd0_hdmi_ctrl;
Ben Skeggs0a0afd22013-02-18 23:17:53 -05001034 priv->sor.dp = &nvd0_sor_dp_func;
Ben Skeggsebb945a2012-07-20 08:17:34 +10001035 return 0;
1036}
1037
Ben Skeggsa8f8b482014-02-20 21:33:34 +10001038struct nouveau_oclass *
1039nvd0_disp_oclass = &(struct nv50_disp_impl) {
1040 .base.base.handle = NV_ENGINE(DISP, 0x90),
1041 .base.base.ofuncs = &(struct nouveau_ofuncs) {
Ben Skeggsebb945a2012-07-20 08:17:34 +10001042 .ctor = nvd0_disp_ctor,
1043 .dtor = _nouveau_disp_dtor,
1044 .init = _nouveau_disp_init,
1045 .fini = _nouveau_disp_fini,
1046 },
Ben Skeggsa8f8b482014-02-20 21:33:34 +10001047}.base.base;