blob: 788dd34ccb54ae7997586149410808afa125e105 [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 Skeggs46654062012-08-28 14:10:39 +100032#include <subdev/timer.h>
33#include <subdev/fb.h>
Ben Skeggs14464b82012-11-02 11:33:27 +100034#include <subdev/clock.h>
35
36#include <subdev/bios.h>
37#include <subdev/bios/dcb.h>
38#include <subdev/bios/disp.h>
39#include <subdev/bios/init.h>
40#include <subdev/bios/pll.h>
Ben Skeggs46654062012-08-28 14:10:39 +100041
42#include "nv50.h"
43
44/*******************************************************************************
45 * EVO DMA channel base class
46 ******************************************************************************/
47
48static int
49nvd0_disp_dmac_object_attach(struct nouveau_object *parent,
50 struct nouveau_object *object, u32 name)
51{
52 struct nv50_disp_base *base = (void *)parent->parent;
53 struct nv50_disp_chan *chan = (void *)parent;
54 u32 addr = nv_gpuobj(object)->node->offset;
55 u32 data = (chan->chid << 27) | (addr << 9) | 0x00000001;
56 return nouveau_ramht_insert(base->ramht, chan->chid, name, data);
57}
58
59static void
60nvd0_disp_dmac_object_detach(struct nouveau_object *parent, int cookie)
61{
62 struct nv50_disp_base *base = (void *)parent->parent;
63 nouveau_ramht_remove(base->ramht, cookie);
64}
65
66static int
67nvd0_disp_dmac_init(struct nouveau_object *object)
68{
69 struct nv50_disp_priv *priv = (void *)object->engine;
70 struct nv50_disp_dmac *dmac = (void *)object;
71 int chid = dmac->base.chid;
72 int ret;
73
74 ret = nv50_disp_chan_init(&dmac->base);
75 if (ret)
76 return ret;
77
78 /* enable error reporting */
79 nv_mask(priv, 0x610090, 0x00000001 << chid, 0x00000001 << chid);
80 nv_mask(priv, 0x6100a0, 0x00000001 << chid, 0x00000001 << chid);
81
82 /* initialise channel for dma command submission */
83 nv_wr32(priv, 0x610494 + (chid * 0x0010), dmac->push);
84 nv_wr32(priv, 0x610498 + (chid * 0x0010), 0x00010000);
85 nv_wr32(priv, 0x61049c + (chid * 0x0010), 0x00000001);
86 nv_mask(priv, 0x610490 + (chid * 0x0010), 0x00000010, 0x00000010);
87 nv_wr32(priv, 0x640000 + (chid * 0x1000), 0x00000000);
88 nv_wr32(priv, 0x610490 + (chid * 0x0010), 0x00000013);
89
90 /* wait for it to go inactive */
91 if (!nv_wait(priv, 0x610490 + (chid * 0x10), 0x80000000, 0x00000000)) {
92 nv_error(dmac, "init: 0x%08x\n",
93 nv_rd32(priv, 0x610490 + (chid * 0x10)));
94 return -EBUSY;
95 }
96
97 return 0;
98}
99
100static int
101nvd0_disp_dmac_fini(struct nouveau_object *object, bool suspend)
102{
103 struct nv50_disp_priv *priv = (void *)object->engine;
104 struct nv50_disp_dmac *dmac = (void *)object;
105 int chid = dmac->base.chid;
106
107 /* deactivate channel */
108 nv_mask(priv, 0x610490 + (chid * 0x0010), 0x00001010, 0x00001000);
109 nv_mask(priv, 0x610490 + (chid * 0x0010), 0x00000003, 0x00000000);
110 if (!nv_wait(priv, 0x610490 + (chid * 0x10), 0x001e0000, 0x00000000)) {
111 nv_error(dmac, "fini: 0x%08x\n",
112 nv_rd32(priv, 0x610490 + (chid * 0x10)));
113 if (suspend)
114 return -EBUSY;
115 }
116
117 /* disable error reporting */
118 nv_mask(priv, 0x610090, 0x00000001 << chid, 0x00000000);
119 nv_mask(priv, 0x6100a0, 0x00000001 << chid, 0x00000000);
120
121 return nv50_disp_chan_fini(&dmac->base, suspend);
122}
123
124/*******************************************************************************
125 * EVO master channel object
126 ******************************************************************************/
127
128static int
129nvd0_disp_mast_ctor(struct nouveau_object *parent,
130 struct nouveau_object *engine,
131 struct nouveau_oclass *oclass, void *data, u32 size,
132 struct nouveau_object **pobject)
133{
134 struct nv50_display_mast_class *args = data;
135 struct nv50_disp_dmac *mast;
136 int ret;
137
138 if (size < sizeof(*args))
139 return -EINVAL;
140
141 ret = nv50_disp_dmac_create_(parent, engine, oclass, args->pushbuf,
142 0, sizeof(*mast), (void **)&mast);
143 *pobject = nv_object(mast);
144 if (ret)
145 return ret;
146
147 nv_parent(mast)->object_attach = nvd0_disp_dmac_object_attach;
148 nv_parent(mast)->object_detach = nvd0_disp_dmac_object_detach;
149 return 0;
150}
151
152static int
153nvd0_disp_mast_init(struct nouveau_object *object)
154{
155 struct nv50_disp_priv *priv = (void *)object->engine;
156 struct nv50_disp_dmac *mast = (void *)object;
157 int ret;
158
159 ret = nv50_disp_chan_init(&mast->base);
160 if (ret)
161 return ret;
162
163 /* enable error reporting */
164 nv_mask(priv, 0x610090, 0x00000001, 0x00000001);
165 nv_mask(priv, 0x6100a0, 0x00000001, 0x00000001);
166
167 /* initialise channel for dma command submission */
168 nv_wr32(priv, 0x610494, mast->push);
169 nv_wr32(priv, 0x610498, 0x00010000);
170 nv_wr32(priv, 0x61049c, 0x00000001);
171 nv_mask(priv, 0x610490, 0x00000010, 0x00000010);
172 nv_wr32(priv, 0x640000, 0x00000000);
173 nv_wr32(priv, 0x610490, 0x01000013);
174
175 /* wait for it to go inactive */
176 if (!nv_wait(priv, 0x610490, 0x80000000, 0x00000000)) {
177 nv_error(mast, "init: 0x%08x\n", nv_rd32(priv, 0x610490));
178 return -EBUSY;
179 }
180
181 return 0;
182}
183
184static int
185nvd0_disp_mast_fini(struct nouveau_object *object, bool suspend)
186{
187 struct nv50_disp_priv *priv = (void *)object->engine;
188 struct nv50_disp_dmac *mast = (void *)object;
189
190 /* deactivate channel */
191 nv_mask(priv, 0x610490, 0x00000010, 0x00000000);
192 nv_mask(priv, 0x610490, 0x00000003, 0x00000000);
193 if (!nv_wait(priv, 0x610490, 0x001e0000, 0x00000000)) {
194 nv_error(mast, "fini: 0x%08x\n", nv_rd32(priv, 0x610490));
195 if (suspend)
196 return -EBUSY;
197 }
198
199 /* disable error reporting */
200 nv_mask(priv, 0x610090, 0x00000001, 0x00000000);
201 nv_mask(priv, 0x6100a0, 0x00000001, 0x00000000);
202
203 return nv50_disp_chan_fini(&mast->base, suspend);
204}
205
206struct nouveau_ofuncs
207nvd0_disp_mast_ofuncs = {
208 .ctor = nvd0_disp_mast_ctor,
209 .dtor = nv50_disp_dmac_dtor,
210 .init = nvd0_disp_mast_init,
211 .fini = nvd0_disp_mast_fini,
212 .rd32 = nv50_disp_chan_rd32,
213 .wr32 = nv50_disp_chan_wr32,
214};
215
216/*******************************************************************************
217 * EVO sync channel objects
218 ******************************************************************************/
219
220static int
221nvd0_disp_sync_ctor(struct nouveau_object *parent,
222 struct nouveau_object *engine,
223 struct nouveau_oclass *oclass, void *data, u32 size,
224 struct nouveau_object **pobject)
225{
226 struct nv50_display_sync_class *args = data;
227 struct nv50_disp_priv *priv = (void *)engine;
228 struct nv50_disp_dmac *dmac;
229 int ret;
230
Dan Carpenteraf1ac182013-01-23 11:27:56 +0300231 if (size < sizeof(*args) || args->head >= priv->head.nr)
Ben Skeggs46654062012-08-28 14:10:39 +1000232 return -EINVAL;
233
234 ret = nv50_disp_dmac_create_(parent, engine, oclass, args->pushbuf,
235 1 + args->head, sizeof(*dmac),
236 (void **)&dmac);
237 *pobject = nv_object(dmac);
238 if (ret)
239 return ret;
240
241 nv_parent(dmac)->object_attach = nvd0_disp_dmac_object_attach;
242 nv_parent(dmac)->object_detach = nvd0_disp_dmac_object_detach;
243 return 0;
244}
245
246struct nouveau_ofuncs
247nvd0_disp_sync_ofuncs = {
248 .ctor = nvd0_disp_sync_ctor,
249 .dtor = nv50_disp_dmac_dtor,
250 .init = nvd0_disp_dmac_init,
251 .fini = nvd0_disp_dmac_fini,
252 .rd32 = nv50_disp_chan_rd32,
253 .wr32 = nv50_disp_chan_wr32,
254};
255
256/*******************************************************************************
257 * EVO overlay channel objects
258 ******************************************************************************/
259
260static int
261nvd0_disp_ovly_ctor(struct nouveau_object *parent,
262 struct nouveau_object *engine,
263 struct nouveau_oclass *oclass, void *data, u32 size,
264 struct nouveau_object **pobject)
265{
266 struct nv50_display_ovly_class *args = data;
267 struct nv50_disp_priv *priv = (void *)engine;
268 struct nv50_disp_dmac *dmac;
269 int ret;
270
Dan Carpenteraf1ac182013-01-23 11:27:56 +0300271 if (size < sizeof(*args) || args->head >= priv->head.nr)
Ben Skeggs46654062012-08-28 14:10:39 +1000272 return -EINVAL;
273
274 ret = nv50_disp_dmac_create_(parent, engine, oclass, args->pushbuf,
275 5 + args->head, sizeof(*dmac),
276 (void **)&dmac);
277 *pobject = nv_object(dmac);
278 if (ret)
279 return ret;
280
281 nv_parent(dmac)->object_attach = nvd0_disp_dmac_object_attach;
282 nv_parent(dmac)->object_detach = nvd0_disp_dmac_object_detach;
283 return 0;
284}
285
286struct nouveau_ofuncs
287nvd0_disp_ovly_ofuncs = {
288 .ctor = nvd0_disp_ovly_ctor,
289 .dtor = nv50_disp_dmac_dtor,
290 .init = nvd0_disp_dmac_init,
291 .fini = nvd0_disp_dmac_fini,
292 .rd32 = nv50_disp_chan_rd32,
293 .wr32 = nv50_disp_chan_wr32,
294};
295
296/*******************************************************************************
297 * EVO PIO channel base class
298 ******************************************************************************/
299
300static int
301nvd0_disp_pioc_create_(struct nouveau_object *parent,
302 struct nouveau_object *engine,
303 struct nouveau_oclass *oclass, int chid,
304 int length, void **pobject)
305{
306 return nv50_disp_chan_create_(parent, engine, oclass, chid,
307 length, pobject);
308}
309
310static void
311nvd0_disp_pioc_dtor(struct nouveau_object *object)
312{
313 struct nv50_disp_pioc *pioc = (void *)object;
314 nv50_disp_chan_destroy(&pioc->base);
315}
316
317static int
318nvd0_disp_pioc_init(struct nouveau_object *object)
319{
320 struct nv50_disp_priv *priv = (void *)object->engine;
321 struct nv50_disp_pioc *pioc = (void *)object;
322 int chid = pioc->base.chid;
323 int ret;
324
325 ret = nv50_disp_chan_init(&pioc->base);
326 if (ret)
327 return ret;
328
329 /* enable error reporting */
330 nv_mask(priv, 0x610090, 0x00000001 << chid, 0x00000001 << chid);
331 nv_mask(priv, 0x6100a0, 0x00000001 << chid, 0x00000001 << chid);
332
333 /* activate channel */
334 nv_wr32(priv, 0x610490 + (chid * 0x10), 0x00000001);
335 if (!nv_wait(priv, 0x610490 + (chid * 0x10), 0x00030000, 0x00010000)) {
336 nv_error(pioc, "init: 0x%08x\n",
337 nv_rd32(priv, 0x610490 + (chid * 0x10)));
338 return -EBUSY;
339 }
340
341 return 0;
342}
343
344static int
345nvd0_disp_pioc_fini(struct nouveau_object *object, bool suspend)
346{
347 struct nv50_disp_priv *priv = (void *)object->engine;
348 struct nv50_disp_pioc *pioc = (void *)object;
349 int chid = pioc->base.chid;
350
351 nv_mask(priv, 0x610490 + (chid * 0x10), 0x00000001, 0x00000000);
352 if (!nv_wait(priv, 0x610490 + (chid * 0x10), 0x00030000, 0x00000000)) {
353 nv_error(pioc, "timeout: 0x%08x\n",
354 nv_rd32(priv, 0x610490 + (chid * 0x10)));
355 if (suspend)
356 return -EBUSY;
357 }
358
359 /* disable error reporting */
360 nv_mask(priv, 0x610090, 0x00000001 << chid, 0x00000000);
361 nv_mask(priv, 0x6100a0, 0x00000001 << chid, 0x00000000);
362
363 return nv50_disp_chan_fini(&pioc->base, suspend);
364}
365
366/*******************************************************************************
367 * EVO immediate overlay channel objects
368 ******************************************************************************/
369
370static int
371nvd0_disp_oimm_ctor(struct nouveau_object *parent,
372 struct nouveau_object *engine,
373 struct nouveau_oclass *oclass, void *data, u32 size,
374 struct nouveau_object **pobject)
375{
376 struct nv50_display_oimm_class *args = data;
377 struct nv50_disp_priv *priv = (void *)engine;
378 struct nv50_disp_pioc *pioc;
379 int ret;
380
381 if (size < sizeof(*args) || args->head >= priv->head.nr)
382 return -EINVAL;
383
384 ret = nvd0_disp_pioc_create_(parent, engine, oclass, 9 + args->head,
385 sizeof(*pioc), (void **)&pioc);
386 *pobject = nv_object(pioc);
387 if (ret)
388 return ret;
389
390 return 0;
391}
392
393struct nouveau_ofuncs
394nvd0_disp_oimm_ofuncs = {
395 .ctor = nvd0_disp_oimm_ctor,
396 .dtor = nvd0_disp_pioc_dtor,
397 .init = nvd0_disp_pioc_init,
398 .fini = nvd0_disp_pioc_fini,
399 .rd32 = nv50_disp_chan_rd32,
400 .wr32 = nv50_disp_chan_wr32,
401};
402
403/*******************************************************************************
404 * EVO cursor channel objects
405 ******************************************************************************/
406
407static int
408nvd0_disp_curs_ctor(struct nouveau_object *parent,
409 struct nouveau_object *engine,
410 struct nouveau_oclass *oclass, void *data, u32 size,
411 struct nouveau_object **pobject)
412{
413 struct nv50_display_curs_class *args = data;
414 struct nv50_disp_priv *priv = (void *)engine;
415 struct nv50_disp_pioc *pioc;
416 int ret;
417
418 if (size < sizeof(*args) || args->head >= priv->head.nr)
419 return -EINVAL;
420
421 ret = nvd0_disp_pioc_create_(parent, engine, oclass, 13 + args->head,
422 sizeof(*pioc), (void **)&pioc);
423 *pobject = nv_object(pioc);
424 if (ret)
425 return ret;
426
427 return 0;
428}
429
430struct nouveau_ofuncs
431nvd0_disp_curs_ofuncs = {
432 .ctor = nvd0_disp_curs_ctor,
433 .dtor = nvd0_disp_pioc_dtor,
434 .init = nvd0_disp_pioc_init,
435 .fini = nvd0_disp_pioc_fini,
436 .rd32 = nv50_disp_chan_rd32,
437 .wr32 = nv50_disp_chan_wr32,
438};
439
440/*******************************************************************************
441 * Base display object
442 ******************************************************************************/
443
Ben Skeggs1d7c71a2013-01-31 09:23:34 +1000444static void
445nvd0_disp_base_vblank_enable(struct nouveau_event *event, int head)
446{
447 nv_mask(event->priv, 0x6100c0 + (head * 0x800), 0x00000001, 0x00000001);
448}
449
450static void
451nvd0_disp_base_vblank_disable(struct nouveau_event *event, int head)
452{
453 nv_mask(event->priv, 0x6100c0 + (head * 0x800), 0x00000001, 0x00000000);
454}
455
Ben Skeggs46654062012-08-28 14:10:39 +1000456static int
457nvd0_disp_base_ctor(struct nouveau_object *parent,
458 struct nouveau_object *engine,
459 struct nouveau_oclass *oclass, void *data, u32 size,
460 struct nouveau_object **pobject)
461{
462 struct nv50_disp_priv *priv = (void *)engine;
463 struct nv50_disp_base *base;
464 int ret;
465
466 ret = nouveau_parent_create(parent, engine, oclass, 0,
467 priv->sclass, 0, &base);
468 *pobject = nv_object(base);
469 if (ret)
470 return ret;
471
Ben Skeggs1d7c71a2013-01-31 09:23:34 +1000472 priv->base.vblank->priv = priv;
473 priv->base.vblank->enable = nvd0_disp_base_vblank_enable;
474 priv->base.vblank->disable = nvd0_disp_base_vblank_disable;
475
Ben Skeggs46654062012-08-28 14:10:39 +1000476 return nouveau_ramht_new(parent, parent, 0x1000, 0, &base->ramht);
477}
478
479static void
480nvd0_disp_base_dtor(struct nouveau_object *object)
481{
482 struct nv50_disp_base *base = (void *)object;
483 nouveau_ramht_ref(NULL, &base->ramht);
484 nouveau_parent_destroy(&base->base);
485}
486
487static int
488nvd0_disp_base_init(struct nouveau_object *object)
489{
490 struct nv50_disp_priv *priv = (void *)object->engine;
491 struct nv50_disp_base *base = (void *)object;
492 int ret, i;
493 u32 tmp;
494
495 ret = nouveau_parent_init(&base->base);
496 if (ret)
497 return ret;
498
499 /* The below segments of code copying values from one register to
500 * another appear to inform EVO of the display capabilities or
501 * something similar.
502 */
503
504 /* ... CRTC caps */
505 for (i = 0; i < priv->head.nr; i++) {
506 tmp = nv_rd32(priv, 0x616104 + (i * 0x800));
507 nv_wr32(priv, 0x6101b4 + (i * 0x800), tmp);
508 tmp = nv_rd32(priv, 0x616108 + (i * 0x800));
509 nv_wr32(priv, 0x6101b8 + (i * 0x800), tmp);
510 tmp = nv_rd32(priv, 0x61610c + (i * 0x800));
511 nv_wr32(priv, 0x6101bc + (i * 0x800), tmp);
512 }
513
514 /* ... DAC caps */
515 for (i = 0; i < priv->dac.nr; i++) {
516 tmp = nv_rd32(priv, 0x61a000 + (i * 0x800));
517 nv_wr32(priv, 0x6101c0 + (i * 0x800), tmp);
518 }
519
520 /* ... SOR caps */
521 for (i = 0; i < priv->sor.nr; i++) {
522 tmp = nv_rd32(priv, 0x61c000 + (i * 0x800));
523 nv_wr32(priv, 0x6301c4 + (i * 0x800), tmp);
524 }
525
526 /* steal display away from vbios, or something like that */
527 if (nv_rd32(priv, 0x6100ac) & 0x00000100) {
528 nv_wr32(priv, 0x6100ac, 0x00000100);
529 nv_mask(priv, 0x6194e8, 0x00000001, 0x00000000);
530 if (!nv_wait(priv, 0x6194e8, 0x00000002, 0x00000000)) {
531 nv_error(priv, "timeout acquiring display\n");
532 return -EBUSY;
533 }
534 }
535
536 /* point at display engine memory area (hash table, objects) */
537 nv_wr32(priv, 0x610010, (nv_gpuobj(object->parent)->addr >> 8) | 9);
538
539 /* enable supervisor interrupts, disable everything else */
540 nv_wr32(priv, 0x610090, 0x00000000);
541 nv_wr32(priv, 0x6100a0, 0x00000000);
542 nv_wr32(priv, 0x6100b0, 0x00000307);
543
544 return 0;
545}
546
547static int
548nvd0_disp_base_fini(struct nouveau_object *object, bool suspend)
549{
550 struct nv50_disp_priv *priv = (void *)object->engine;
551 struct nv50_disp_base *base = (void *)object;
552
553 /* disable all interrupts */
554 nv_wr32(priv, 0x6100b0, 0x00000000);
555
556 return nouveau_parent_fini(&base->base, suspend);
557}
558
559struct nouveau_ofuncs
560nvd0_disp_base_ofuncs = {
561 .ctor = nvd0_disp_base_ctor,
562 .dtor = nvd0_disp_base_dtor,
563 .init = nvd0_disp_base_init,
564 .fini = nvd0_disp_base_fini,
565};
566
567static struct nouveau_oclass
568nvd0_disp_base_oclass[] = {
Ben Skeggs6c5a0422012-11-07 16:43:00 +1000569 { NVD0_DISP_CLASS, &nvd0_disp_base_ofuncs, nva3_disp_base_omthds },
Ben Skeggs46654062012-08-28 14:10:39 +1000570 {}
Ben Skeggsebb945a2012-07-20 08:17:34 +1000571};
572
573static struct nouveau_oclass
574nvd0_disp_sclass[] = {
Ben Skeggs46654062012-08-28 14:10:39 +1000575 { NVD0_DISP_MAST_CLASS, &nvd0_disp_mast_ofuncs },
576 { NVD0_DISP_SYNC_CLASS, &nvd0_disp_sync_ofuncs },
577 { NVD0_DISP_OVLY_CLASS, &nvd0_disp_ovly_ofuncs },
578 { NVD0_DISP_OIMM_CLASS, &nvd0_disp_oimm_ofuncs },
579 { NVD0_DISP_CURS_CLASS, &nvd0_disp_curs_ofuncs },
580 {}
Ben Skeggsebb945a2012-07-20 08:17:34 +1000581};
582
Ben Skeggs46654062012-08-28 14:10:39 +1000583/*******************************************************************************
584 * Display engine implementation
585 ******************************************************************************/
586
Ben Skeggs14464b82012-11-02 11:33:27 +1000587static u16
588exec_lookup(struct nv50_disp_priv *priv, int head, int outp, u32 ctrl,
589 struct dcb_output *dcb, u8 *ver, u8 *hdr, u8 *cnt, u8 *len,
590 struct nvbios_outp *info)
591{
592 struct nouveau_bios *bios = nouveau_bios(priv);
Ben Skeggs75f86932012-11-08 17:41:06 +1000593 u16 mask, type, data;
Ben Skeggs14464b82012-11-02 11:33:27 +1000594
595 if (outp < 4) {
596 type = DCB_OUTPUT_ANALOG;
597 mask = 0;
598 } else {
599 outp -= 4;
600 switch (ctrl & 0x00000f00) {
601 case 0x00000000: type = DCB_OUTPUT_LVDS; mask = 1; break;
602 case 0x00000100: type = DCB_OUTPUT_TMDS; mask = 1; break;
603 case 0x00000200: type = DCB_OUTPUT_TMDS; mask = 2; break;
604 case 0x00000500: type = DCB_OUTPUT_TMDS; mask = 3; break;
605 case 0x00000800: type = DCB_OUTPUT_DP; mask = 1; break;
606 case 0x00000900: type = DCB_OUTPUT_DP; mask = 2; break;
607 default:
608 nv_error(priv, "unknown SOR mc 0x%08x\n", ctrl);
609 return 0x0000;
610 }
611 dcb->sorconf.link = mask;
612 }
613
614 mask = 0x00c0 & (mask << 6);
615 mask |= 0x0001 << outp;
616 mask |= 0x0100 << head;
617
Ben Skeggs75f86932012-11-08 17:41:06 +1000618 data = dcb_outp_match(bios, type, mask, ver, hdr, dcb);
619 if (!data)
620 return 0x0000;
Ben Skeggs14464b82012-11-02 11:33:27 +1000621
Ben Skeggs75f86932012-11-08 17:41:06 +1000622 return nvbios_outp_match(bios, type, mask, ver, hdr, cnt, len, info);
Ben Skeggs14464b82012-11-02 11:33:27 +1000623}
624
625static bool
Ben Skeggs4ea253a2013-02-20 19:21:08 +1000626exec_script(struct nv50_disp_priv *priv, int head, int id)
Ben Skeggs14464b82012-11-02 11:33:27 +1000627{
628 struct nouveau_bios *bios = nouveau_bios(priv);
629 struct nvbios_outp info;
630 struct dcb_output dcb;
631 u8 ver, hdr, cnt, len;
Ben Skeggs4ea253a2013-02-20 19:21:08 +1000632 u32 ctrl = 0x00000000;
Ben Skeggs14464b82012-11-02 11:33:27 +1000633 u16 data;
Ben Skeggs4ea253a2013-02-20 19:21:08 +1000634 int outp;
635
636 for (outp = 0; !(ctrl & (1 << head)) && outp < 8; outp++) {
637 ctrl = nv_rd32(priv, 0x640180 + (outp * 0x20));
638 if (ctrl & (1 << head))
639 break;
640 }
641
642 if (outp == 8)
643 return false;
Ben Skeggs14464b82012-11-02 11:33:27 +1000644
645 data = exec_lookup(priv, head, outp, ctrl, &dcb, &ver, &hdr, &cnt, &len, &info);
646 if (data) {
647 struct nvbios_init init = {
648 .subdev = nv_subdev(priv),
649 .bios = bios,
650 .offset = info.script[id],
651 .outp = &dcb,
652 .crtc = head,
653 .execute = 1,
654 };
655
656 return nvbios_exec(&init) == 0;
657 }
658
659 return false;
660}
661
Ben Skeggs4a230fa2012-11-09 11:25:37 +1000662static u32
Ben Skeggs4ea253a2013-02-20 19:21:08 +1000663exec_clkcmp(struct nv50_disp_priv *priv, int head, int id,
664 u32 pclk, struct dcb_output *dcb)
Ben Skeggs14464b82012-11-02 11:33:27 +1000665{
666 struct nouveau_bios *bios = nouveau_bios(priv);
667 struct nvbios_outp info1;
668 struct nvbios_ocfg info2;
Ben Skeggs14464b82012-11-02 11:33:27 +1000669 u8 ver, hdr, cnt, len;
Ben Skeggs4ea253a2013-02-20 19:21:08 +1000670 u32 ctrl = 0x00000000;
Ben Skeggs46c13c12013-02-16 13:49:21 +1000671 u32 data, conf = ~0;
Ben Skeggs4ea253a2013-02-20 19:21:08 +1000672 int outp;
673
674 for (outp = 0; !(ctrl & (1 << head)) && outp < 8; outp++) {
675 ctrl = nv_rd32(priv, 0x660180 + (outp * 0x20));
676 if (ctrl & (1 << head))
677 break;
678 }
679
680 if (outp == 8)
681 return false;
Ben Skeggs14464b82012-11-02 11:33:27 +1000682
Ben Skeggs0a0afd22013-02-18 23:17:53 -0500683 data = exec_lookup(priv, head, outp, ctrl, dcb, &ver, &hdr, &cnt, &len, &info1);
Ben Skeggs4a230fa2012-11-09 11:25:37 +1000684 if (data == 0x0000)
Ben Skeggs46c13c12013-02-16 13:49:21 +1000685 return conf;
Ben Skeggs14464b82012-11-02 11:33:27 +1000686
Ben Skeggs0a0afd22013-02-18 23:17:53 -0500687 switch (dcb->type) {
Ben Skeggs4a230fa2012-11-09 11:25:37 +1000688 case DCB_OUTPUT_TMDS:
689 conf = (ctrl & 0x00000f00) >> 8;
690 if (pclk >= 165000)
691 conf |= 0x0100;
692 break;
693 case DCB_OUTPUT_LVDS:
694 conf = priv->sor.lvdsconf;
695 break;
696 case DCB_OUTPUT_DP:
697 conf = (ctrl & 0x00000f00) >> 8;
698 break;
699 case DCB_OUTPUT_ANALOG:
700 default:
701 conf = 0x00ff;
702 break;
703 }
704
705 data = nvbios_ocfg_match(bios, data, conf, &ver, &hdr, &cnt, &len, &info2);
Ben Skeggs0a0afd22013-02-18 23:17:53 -0500706 if (data && id < 0xff) {
Ben Skeggs4a230fa2012-11-09 11:25:37 +1000707 data = nvbios_oclk_match(bios, info2.clkcmp[id], pclk);
708 if (data) {
709 struct nvbios_init init = {
710 .subdev = nv_subdev(priv),
711 .bios = bios,
712 .offset = data,
Ben Skeggs0a0afd22013-02-18 23:17:53 -0500713 .outp = dcb,
Ben Skeggs4a230fa2012-11-09 11:25:37 +1000714 .crtc = head,
715 .execute = 1,
716 };
717
Ben Skeggs46c13c12013-02-16 13:49:21 +1000718 nvbios_exec(&init);
Ben Skeggs14464b82012-11-02 11:33:27 +1000719 }
720 }
721
Ben Skeggs46c13c12013-02-16 13:49:21 +1000722 return conf;
Ben Skeggs14464b82012-11-02 11:33:27 +1000723}
724
725static void
Ben Skeggs4ea253a2013-02-20 19:21:08 +1000726nvd0_disp_intr_unk1_0(struct nv50_disp_priv *priv, int head)
Ben Skeggs14464b82012-11-02 11:33:27 +1000727{
Ben Skeggs4ea253a2013-02-20 19:21:08 +1000728 exec_script(priv, head, 1);
Ben Skeggs14464b82012-11-02 11:33:27 +1000729}
730
731static void
Ben Skeggs4ea253a2013-02-20 19:21:08 +1000732nvd0_disp_intr_unk2_0(struct nv50_disp_priv *priv, int head)
Ben Skeggsed58aee2012-11-08 14:59:26 +1000733{
Ben Skeggs4ea253a2013-02-20 19:21:08 +1000734 exec_script(priv, head, 2);
735}
736
737static void
738nvd0_disp_intr_unk2_1(struct nv50_disp_priv *priv, int head)
739{
740 struct nouveau_clock *clk = nouveau_clock(priv);
741 u32 pclk = nv_rd32(priv, 0x660450 + (head * 0x300)) / 1000;
742 if (pclk)
743 clk->pll_set(clk, PLL_VPLL0 + head, pclk);
744 nv_wr32(priv, 0x612200 + (head * 0x800), 0x00000000);
745}
746
747static void
748nvd0_disp_intr_unk2_2_tu(struct nv50_disp_priv *priv, int head,
749 struct dcb_output *outp)
750{
751 const int or = ffs(outp->or) - 1;
Ben Skeggsed58aee2012-11-08 14:59:26 +1000752 const u32 ctrl = nv_rd32(priv, 0x660200 + (or * 0x020));
753 const u32 conf = nv_rd32(priv, 0x660404 + (head * 0x300));
754 const u32 pclk = nv_rd32(priv, 0x660450 + (head * 0x300)) / 1000;
Ben Skeggsed58aee2012-11-08 14:59:26 +1000755 const u32 link = ((ctrl & 0xf00) == 0x800) ? 0 : 1;
756 const u32 hoff = (head * 0x800);
757 const u32 soff = ( or * 0x800);
758 const u32 loff = (link * 0x080) + soff;
759 const u32 symbol = 100000;
760 const u32 TU = 64;
761 u32 dpctrl = nv_rd32(priv, 0x61c10c + loff) & 0x000f0000;
762 u32 clksor = nv_rd32(priv, 0x612300 + soff);
Ben Skeggsbf2c8862012-11-21 14:49:54 +1000763 u32 datarate, link_nr, link_bw, bits;
Ben Skeggsed58aee2012-11-08 14:59:26 +1000764 u64 ratio, value;
765
Ben Skeggsbf2c8862012-11-21 14:49:54 +1000766 if ((conf & 0x3c0) == 0x180) bits = 30;
767 else if ((conf & 0x3c0) == 0x140) bits = 24;
768 else bits = 18;
769 datarate = (pclk * bits) / 8;
770
Ben Skeggsed58aee2012-11-08 14:59:26 +1000771 if (dpctrl > 0x00030000) link_nr = 4;
772 else if (dpctrl > 0x00010000) link_nr = 2;
773 else link_nr = 1;
774
775 link_bw = (clksor & 0x007c0000) >> 18;
776 link_bw *= 27000;
777
778 ratio = datarate;
779 ratio *= symbol;
780 do_div(ratio, link_nr * link_bw);
781
782 value = (symbol - ratio) * TU;
783 value *= ratio;
784 do_div(value, symbol);
785 do_div(value, symbol);
786
787 value += 5;
788 value |= 0x08000000;
789
790 nv_wr32(priv, 0x616610 + hoff, value);
791}
792
793static void
Ben Skeggs4ea253a2013-02-20 19:21:08 +1000794nvd0_disp_intr_unk2_2(struct nv50_disp_priv *priv, int head)
Ben Skeggs14464b82012-11-02 11:33:27 +1000795{
Ben Skeggs0a0afd22013-02-18 23:17:53 -0500796 struct dcb_output outp;
Ben Skeggs4ea253a2013-02-20 19:21:08 +1000797 u32 pclk = nv_rd32(priv, 0x660450 + (head * 0x300)) / 1000;
798 u32 conf = exec_clkcmp(priv, head, 0xff, pclk, &outp);
799 if (conf != ~0) {
800 u32 addr, data;
Ben Skeggs14464b82012-11-02 11:33:27 +1000801
Ben Skeggs4ea253a2013-02-20 19:21:08 +1000802 if (outp.type == DCB_OUTPUT_DP) {
803 u32 sync = nv_rd32(priv, 0x660404 + (head * 0x300));
804 switch ((sync & 0x000003c0) >> 6) {
805 case 6: pclk = pclk * 30 / 8; break;
806 case 5: pclk = pclk * 24 / 8; break;
807 case 2:
808 default:
809 pclk = pclk * 18 / 8;
810 break;
Ben Skeggs14464b82012-11-02 11:33:27 +1000811 }
Ben Skeggs14464b82012-11-02 11:33:27 +1000812
Ben Skeggs4ea253a2013-02-20 19:21:08 +1000813 nouveau_dp_train(&priv->base, priv->sor.dp,
814 &outp, head, pclk);
815 }
816
817 exec_clkcmp(priv, head, 0, pclk, &outp);
818
819 if (outp.type == DCB_OUTPUT_ANALOG) {
820 addr = 0x612280 + (ffs(outp.or) - 1) * 0x800;
821 data = 0x00000000;
822 } else {
823 if (outp.type == DCB_OUTPUT_DP)
824 nvd0_disp_intr_unk2_2_tu(priv, head, &outp);
825 addr = 0x612300 + (ffs(outp.or) - 1) * 0x800;
826 data = (conf & 0x0100) ? 0x00000101 : 0x00000000;
827 }
828
829 nv_mask(priv, addr, 0x00000707, data);
830 }
Ben Skeggs14464b82012-11-02 11:33:27 +1000831}
832
833static void
Ben Skeggs4ea253a2013-02-20 19:21:08 +1000834nvd0_disp_intr_unk4_0(struct nv50_disp_priv *priv, int head)
Ben Skeggs14464b82012-11-02 11:33:27 +1000835{
Ben Skeggs0a0afd22013-02-18 23:17:53 -0500836 struct dcb_output outp;
Ben Skeggs4ea253a2013-02-20 19:21:08 +1000837 u32 pclk = nv_rd32(priv, 0x660450 + (head * 0x300)) / 1000;
838 exec_clkcmp(priv, head, 1, pclk, &outp);
Ben Skeggs14464b82012-11-02 11:33:27 +1000839}
840
Ben Skeggs46654062012-08-28 14:10:39 +1000841void
Ben Skeggs5cc027f2013-02-18 17:50:51 -0500842nvd0_disp_intr_supervisor(struct work_struct *work)
843{
844 struct nv50_disp_priv *priv =
845 container_of(work, struct nv50_disp_priv, supervisor);
Ben Skeggs4ea253a2013-02-20 19:21:08 +1000846 u32 mask[4];
847 int head;
Ben Skeggs5cc027f2013-02-18 17:50:51 -0500848
Ben Skeggs4ea253a2013-02-20 19:21:08 +1000849 nv_debug(priv, "supervisor %08x\n", priv->super);
850 for (head = 0; head < priv->head.nr; head++) {
851 mask[head] = nv_rd32(priv, 0x6101d4 + (head * 0x800));
852 nv_debug(priv, "head %d: 0x%08x\n", head, mask[head]);
853 }
Ben Skeggs5cc027f2013-02-18 17:50:51 -0500854
Ben Skeggs4ea253a2013-02-20 19:21:08 +1000855 if (priv->super & 0x00000001) {
856 for (head = 0; head < priv->head.nr; head++) {
857 if (!(mask[head] & 0x00001000))
858 continue;
859 nvd0_disp_intr_unk1_0(priv, head);
860 }
861 } else
862 if (priv->super & 0x00000002) {
863 for (head = 0; head < priv->head.nr; head++) {
864 if (!(mask[head] & 0x00001000))
865 continue;
866 nvd0_disp_intr_unk2_0(priv, head);
867 }
868 for (head = 0; head < priv->head.nr; head++) {
869 if (!(mask[head] & 0x00010000))
870 continue;
871 nvd0_disp_intr_unk2_1(priv, head);
872 }
873 for (head = 0; head < priv->head.nr; head++) {
874 if (!(mask[head] & 0x00001000))
875 continue;
876 nvd0_disp_intr_unk2_2(priv, head);
877 }
878 } else
879 if (priv->super & 0x00000004) {
880 for (head = 0; head < priv->head.nr; head++) {
881 if (!(mask[head] & 0x00001000))
882 continue;
883 nvd0_disp_intr_unk4_0(priv, head);
884 }
885 }
Ben Skeggs5cc027f2013-02-18 17:50:51 -0500886
Ben Skeggs4ea253a2013-02-20 19:21:08 +1000887 for (head = 0; head < priv->head.nr; head++)
888 nv_wr32(priv, 0x6101d4 + (head * 0x800), 0x00000000);
889 nv_wr32(priv, 0x6101d0, 0x80000000);
Ben Skeggs5cc027f2013-02-18 17:50:51 -0500890}
891
892void
Ben Skeggsebb945a2012-07-20 08:17:34 +1000893nvd0_disp_intr(struct nouveau_subdev *subdev)
894{
Ben Skeggs46654062012-08-28 14:10:39 +1000895 struct nv50_disp_priv *priv = (void *)subdev;
Ben Skeggsebb945a2012-07-20 08:17:34 +1000896 u32 intr = nv_rd32(priv, 0x610088);
897 int i;
898
Ben Skeggs14464b82012-11-02 11:33:27 +1000899 if (intr & 0x00000001) {
900 u32 stat = nv_rd32(priv, 0x61008c);
901 nv_wr32(priv, 0x61008c, stat);
902 intr &= ~0x00000001;
903 }
904
905 if (intr & 0x00000002) {
906 u32 stat = nv_rd32(priv, 0x61009c);
907 int chid = ffs(stat) - 1;
908 if (chid >= 0) {
909 u32 mthd = nv_rd32(priv, 0x6101f0 + (chid * 12));
910 u32 data = nv_rd32(priv, 0x6101f4 + (chid * 12));
911 u32 unkn = nv_rd32(priv, 0x6101f8 + (chid * 12));
912
913 nv_error(priv, "chid %d mthd 0x%04x data 0x%08x "
914 "0x%08x 0x%08x\n",
915 chid, (mthd & 0x0000ffc), data, mthd, unkn);
916 nv_wr32(priv, 0x61009c, (1 << chid));
917 nv_wr32(priv, 0x6101f0 + (chid * 12), 0x90000000);
918 }
919
920 intr &= ~0x00000002;
921 }
922
923 if (intr & 0x00100000) {
924 u32 stat = nv_rd32(priv, 0x6100ac);
Ben Skeggs5cc027f2013-02-18 17:50:51 -0500925 if (stat & 0x00000007) {
926 priv->super = (stat & 0x00000007);
927 schedule_work(&priv->supervisor);
928 nv_wr32(priv, 0x6100ac, priv->super);
929 stat &= ~0x00000007;
Ben Skeggs14464b82012-11-02 11:33:27 +1000930 }
931
932 if (stat) {
933 nv_info(priv, "unknown intr24 0x%08x\n", stat);
934 nv_wr32(priv, 0x6100ac, stat);
935 }
936
937 intr &= ~0x00100000;
938 }
939
940 for (i = 0; i < priv->head.nr; i++) {
Ben Skeggsebb945a2012-07-20 08:17:34 +1000941 u32 mask = 0x01000000 << i;
942 if (mask & intr) {
943 u32 stat = nv_rd32(priv, 0x6100bc + (i * 0x800));
944 if (stat & 0x00000001)
Ben Skeggs1d7c71a2013-01-31 09:23:34 +1000945 nouveau_event_trigger(priv->base.vblank, i);
Ben Skeggsebb945a2012-07-20 08:17:34 +1000946 nv_mask(priv, 0x6100bc + (i * 0x800), 0, 0);
947 nv_rd32(priv, 0x6100c0 + (i * 0x800));
948 }
949 }
950}
951
952static int
953nvd0_disp_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
Ben Skeggs46654062012-08-28 14:10:39 +1000954 struct nouveau_oclass *oclass, void *data, u32 size,
955 struct nouveau_object **pobject)
Ben Skeggsebb945a2012-07-20 08:17:34 +1000956{
Ben Skeggs46654062012-08-28 14:10:39 +1000957 struct nv50_disp_priv *priv;
Ben Skeggs1d7c71a2013-01-31 09:23:34 +1000958 int heads = nv_rd32(parent, 0x022448);
Ben Skeggsebb945a2012-07-20 08:17:34 +1000959 int ret;
960
Ben Skeggs1d7c71a2013-01-31 09:23:34 +1000961 ret = nouveau_disp_create(parent, engine, oclass, heads,
962 "PDISP", "display", &priv);
Ben Skeggsebb945a2012-07-20 08:17:34 +1000963 *pobject = nv_object(priv);
964 if (ret)
965 return ret;
966
Ben Skeggs46654062012-08-28 14:10:39 +1000967 nv_engine(priv)->sclass = nvd0_disp_base_oclass;
968 nv_engine(priv)->cclass = &nv50_disp_cclass;
Ben Skeggsebb945a2012-07-20 08:17:34 +1000969 nv_subdev(priv)->intr = nvd0_disp_intr;
Ben Skeggs5cc027f2013-02-18 17:50:51 -0500970 INIT_WORK(&priv->supervisor, nvd0_disp_intr_supervisor);
Ben Skeggs46654062012-08-28 14:10:39 +1000971 priv->sclass = nvd0_disp_sclass;
Ben Skeggs1d7c71a2013-01-31 09:23:34 +1000972 priv->head.nr = heads;
Ben Skeggs46654062012-08-28 14:10:39 +1000973 priv->dac.nr = 3;
974 priv->sor.nr = 4;
Ben Skeggs35b21d32012-11-08 12:08:55 +1000975 priv->dac.power = nv50_dac_power;
976 priv->dac.sense = nv50_dac_sense;
Ben Skeggs74b66852012-11-08 12:01:39 +1000977 priv->sor.power = nv50_sor_power;
Ben Skeggs0a9e2b952012-11-08 14:03:56 +1000978 priv->sor.hda_eld = nvd0_hda_eld;
Ben Skeggs1c30cd02012-11-08 14:22:28 +1000979 priv->sor.hdmi = nvd0_hdmi_ctrl;
Ben Skeggs0a0afd22013-02-18 23:17:53 -0500980 priv->sor.dp = &nvd0_sor_dp_func;
Ben Skeggsebb945a2012-07-20 08:17:34 +1000981 return 0;
982}
983
984struct nouveau_oclass
985nvd0_disp_oclass = {
Ben Skeggs46654062012-08-28 14:10:39 +1000986 .handle = NV_ENGINE(DISP, 0x90),
Ben Skeggsebb945a2012-07-20 08:17:34 +1000987 .ofuncs = &(struct nouveau_ofuncs) {
988 .ctor = nvd0_disp_ctor,
989 .dtor = _nouveau_disp_dtor,
990 .init = _nouveau_disp_init,
991 .fini = _nouveau_disp_fini,
992 },
993};