2 * Copyright 2012 Red Hat Inc.
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:
11 * The above copyright notice and this permission notice shall be included in
12 * all copies or substantial portions of the Software.
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.
30 #include <core/client.h>
31 #include <core/ramht.h>
32 #include <subdev/bios.h>
33 #include <subdev/bios/disp.h>
34 #include <subdev/bios/init.h>
35 #include <subdev/bios/pll.h>
36 #include <subdev/devinit.h>
37 #include <subdev/timer.h>
39 static const struct nvkm_disp_oclass
*
40 nv50_disp_root_(struct nvkm_disp
*base
)
42 return nv50_disp(base
)->func
->root
;
46 nv50_disp_intr_(struct nvkm_disp
*base
)
48 struct nv50_disp
*disp
= nv50_disp(base
);
49 disp
->func
->intr(disp
);
53 nv50_disp_fini_(struct nvkm_disp
*base
)
55 struct nv50_disp
*disp
= nv50_disp(base
);
56 disp
->func
->fini(disp
);
60 nv50_disp_init_(struct nvkm_disp
*base
)
62 struct nv50_disp
*disp
= nv50_disp(base
);
63 return disp
->func
->init(disp
);
67 nv50_disp_dtor_(struct nvkm_disp
*base
)
69 struct nv50_disp
*disp
= nv50_disp(base
);
71 nvkm_ramht_del(&disp
->ramht
);
72 nvkm_gpuobj_del(&disp
->inst
);
74 nvkm_event_fini(&disp
->uevent
);
76 destroy_workqueue(disp
->wq
);
82 nv50_disp_oneinit_(struct nvkm_disp
*base
)
84 struct nv50_disp
*disp
= nv50_disp(base
);
85 const struct nv50_disp_func
*func
= disp
->func
;
86 struct nvkm_subdev
*subdev
= &disp
->base
.engine
.subdev
;
87 struct nvkm_device
*device
= subdev
->device
;
91 disp
->wndw
.nr
= func
->wndw
.cnt(&disp
->base
, &disp
->wndw
.mask
);
92 nvkm_debug(subdev
, "Window(s): %d (%08lx)\n",
93 disp
->wndw
.nr
, disp
->wndw
.mask
);
96 disp
->head
.nr
= func
->head
.cnt(&disp
->base
, &disp
->head
.mask
);
97 nvkm_debug(subdev
, " Head(s): %d (%02lx)\n",
98 disp
->head
.nr
, disp
->head
.mask
);
99 for_each_set_bit(i
, &disp
->head
.mask
, disp
->head
.nr
) {
100 ret
= func
->head
.new(&disp
->base
, i
);
106 disp
->dac
.nr
= func
->dac
.cnt(&disp
->base
, &disp
->dac
.mask
);
107 nvkm_debug(subdev
, " DAC(s): %d (%02lx)\n",
108 disp
->dac
.nr
, disp
->dac
.mask
);
109 for_each_set_bit(i
, &disp
->dac
.mask
, disp
->dac
.nr
) {
110 ret
= func
->dac
.new(&disp
->base
, i
);
116 if (func
->pior
.cnt
) {
117 disp
->pior
.nr
= func
->pior
.cnt(&disp
->base
, &disp
->pior
.mask
);
118 nvkm_debug(subdev
, " PIOR(s): %d (%02lx)\n",
119 disp
->pior
.nr
, disp
->pior
.mask
);
120 for_each_set_bit(i
, &disp
->pior
.mask
, disp
->pior
.nr
) {
121 ret
= func
->pior
.new(&disp
->base
, i
);
127 disp
->sor
.nr
= func
->sor
.cnt(&disp
->base
, &disp
->sor
.mask
);
128 nvkm_debug(subdev
, " SOR(s): %d (%02lx)\n",
129 disp
->sor
.nr
, disp
->sor
.mask
);
130 for_each_set_bit(i
, &disp
->sor
.mask
, disp
->sor
.nr
) {
131 ret
= func
->sor
.new(&disp
->base
, i
);
136 ret
= nvkm_gpuobj_new(device
, 0x10000, 0x10000, false, NULL
,
141 return nvkm_ramht_new(device
, func
->ramht_size
? func
->ramht_size
:
142 0x1000, 0, disp
->inst
, &disp
->ramht
);
145 static const struct nvkm_disp_func
147 .dtor
= nv50_disp_dtor_
,
148 .oneinit
= nv50_disp_oneinit_
,
149 .init
= nv50_disp_init_
,
150 .fini
= nv50_disp_fini_
,
151 .intr
= nv50_disp_intr_
,
152 .root
= nv50_disp_root_
,
156 nv50_disp_new_(const struct nv50_disp_func
*func
, struct nvkm_device
*device
,
157 int index
, struct nvkm_disp
**pdisp
)
159 struct nv50_disp
*disp
;
162 if (!(disp
= kzalloc(sizeof(*disp
), GFP_KERNEL
)))
165 *pdisp
= &disp
->base
;
167 ret
= nvkm_disp_ctor(&nv50_disp_
, device
, index
, &disp
->base
);
171 disp
->wq
= create_singlethread_workqueue("nvkm-disp");
175 INIT_WORK(&disp
->supervisor
, func
->super
);
177 return nvkm_event_init(func
->uevent
, 1, ARRAY_SIZE(disp
->chan
),
182 nv50_disp_super_iedt(struct nvkm_head
*head
, struct nvkm_outp
*outp
,
183 u8
*ver
, u8
*hdr
, u8
*cnt
, u8
*len
,
184 struct nvbios_outp
*iedt
)
186 struct nvkm_bios
*bios
= head
->disp
->engine
.subdev
.device
->bios
;
187 const u8 l
= ffs(outp
->info
.link
);
188 const u16 t
= outp
->info
.hasht
;
189 const u16 m
= (0x0100 << head
->id
) | (l
<< 6) | outp
->info
.or;
190 u32 data
= nvbios_outp_match(bios
, t
, m
, ver
, hdr
, cnt
, len
, iedt
);
192 OUTP_DBG(outp
, "missing IEDT for %04x:%04x", t
, m
);
197 nv50_disp_super_ied_on(struct nvkm_head
*head
,
198 struct nvkm_ior
*ior
, int id
, u32 khz
)
200 struct nvkm_subdev
*subdev
= &head
->disp
->engine
.subdev
;
201 struct nvkm_bios
*bios
= subdev
->device
->bios
;
202 struct nvkm_outp
*outp
= ior
->asy
.outp
;
203 struct nvbios_ocfg iedtrs
;
204 struct nvbios_outp iedt
;
205 u8 ver
, hdr
, cnt
, len
, flags
= 0x00;
209 IOR_DBG(ior
, "nothing to attach");
213 /* Lookup IED table for the device. */
214 data
= nv50_disp_super_iedt(head
, outp
, &ver
, &hdr
, &cnt
, &len
, &iedt
);
218 /* Lookup IEDT runtime settings for the current configuration. */
219 if (ior
->type
== SOR
) {
220 if (ior
->asy
.proto
== LVDS
) {
221 if (head
->asy
.or.depth
== 24)
224 if (ior
->asy
.link
== 3)
228 data
= nvbios_ocfg_match(bios
, data
, ior
->asy
.proto_evo
, flags
,
229 &ver
, &hdr
, &cnt
, &len
, &iedtrs
);
231 OUTP_DBG(outp
, "missing IEDT RS for %02x:%02x",
232 ior
->asy
.proto_evo
, flags
);
236 /* Execute the OnInt[23] script for the current frequency. */
237 data
= nvbios_oclk_match(bios
, iedtrs
.clkcmp
[id
], khz
);
239 OUTP_DBG(outp
, "missing IEDT RSS %d for %02x:%02x %d khz",
240 id
, ior
->asy
.proto_evo
, flags
, khz
);
244 nvbios_init(subdev
, data
,
245 init
.outp
= &outp
->info
;
247 init
.link
= ior
->asy
.link
;
248 init
.head
= head
->id
;
253 nv50_disp_super_ied_off(struct nvkm_head
*head
, struct nvkm_ior
*ior
, int id
)
255 struct nvkm_outp
*outp
= ior
->arm
.outp
;
256 struct nvbios_outp iedt
;
257 u8 ver
, hdr
, cnt
, len
;
261 IOR_DBG(ior
, "nothing attached");
265 data
= nv50_disp_super_iedt(head
, outp
, &ver
, &hdr
, &cnt
, &len
, &iedt
);
269 nvbios_init(&head
->disp
->engine
.subdev
, iedt
.script
[id
],
270 init
.outp
= &outp
->info
;
272 init
.link
= ior
->arm
.link
;
273 init
.head
= head
->id
;
277 static struct nvkm_ior
*
278 nv50_disp_super_ior_asy(struct nvkm_head
*head
)
280 struct nvkm_ior
*ior
;
281 list_for_each_entry(ior
, &head
->disp
->ior
, head
) {
282 if (ior
->asy
.head
& (1 << head
->id
)) {
283 HEAD_DBG(head
, "to %s", ior
->name
);
287 HEAD_DBG(head
, "nothing to attach");
291 static struct nvkm_ior
*
292 nv50_disp_super_ior_arm(struct nvkm_head
*head
)
294 struct nvkm_ior
*ior
;
295 list_for_each_entry(ior
, &head
->disp
->ior
, head
) {
296 if (ior
->arm
.head
& (1 << head
->id
)) {
297 HEAD_DBG(head
, "on %s", ior
->name
);
301 HEAD_DBG(head
, "nothing attached");
306 nv50_disp_super_3_0(struct nv50_disp
*disp
, struct nvkm_head
*head
)
308 struct nvkm_ior
*ior
;
310 /* Determine which OR, if any, we're attaching to the head. */
311 HEAD_DBG(head
, "supervisor 3.0");
312 ior
= nv50_disp_super_ior_asy(head
);
316 /* Execute OnInt3 IED script. */
317 nv50_disp_super_ied_on(head
, ior
, 1, head
->asy
.hz
/ 1000);
319 /* OR-specific handling. */
320 if (ior
->func
->war_3
)
321 ior
->func
->war_3(ior
);
325 nv50_disp_super_2_2_dp(struct nvkm_head
*head
, struct nvkm_ior
*ior
)
327 struct nvkm_subdev
*subdev
= &head
->disp
->engine
.subdev
;
328 const u32 khz
= head
->asy
.hz
/ 1000;
329 const u32 linkKBps
= ior
->dp
.bw
* 27000;
330 const u32 symbol
= 100000;
331 int bestTU
= 0, bestVTUi
= 0, bestVTUf
= 0, bestVTUa
= 0;
332 int TU
, VTUi
, VTUf
, VTUa
;
333 u64 link_data_rate
, link_ratio
, unk
;
334 u32 best_diff
= 64 * symbol
;
337 /* symbols/hblank - algorithm taken from comments in tegra driver */
338 h
= head
->asy
.hblanke
+ head
->asy
.htotal
- head
->asy
.hblanks
- 7;
341 h
= h
- (3 * ior
->dp
.ef
) - (12 / ior
->dp
.nr
);
343 /* symbols/vblank - algorithm taken from comments in tegra driver */
344 v
= head
->asy
.vblanks
- head
->asy
.vblanke
- 25;
347 v
= v
- ((36 / ior
->dp
.nr
) + 3) - 1;
349 ior
->func
->dp
.audio_sym(ior
, head
->id
, h
, v
);
351 /* watermark / activesym */
352 link_data_rate
= (khz
* head
->asy
.or.depth
/ 8) / ior
->dp
.nr
;
354 /* calculate ratio of packed data rate to link symbol rate */
355 link_ratio
= link_data_rate
* symbol
;
356 do_div(link_ratio
, linkKBps
);
358 for (TU
= 64; ior
->func
->dp
.activesym
&& TU
>= 32; TU
--) {
359 /* calculate average number of valid symbols in each TU */
360 u32 tu_valid
= link_ratio
* TU
;
363 /* find a hw representation for the fraction.. */
364 VTUi
= tu_valid
/ symbol
;
365 calc
= VTUi
* symbol
;
366 diff
= tu_valid
- calc
;
368 if (diff
>= (symbol
/ 2)) {
369 VTUf
= symbol
/ (symbol
- diff
);
370 if (symbol
- (VTUf
* diff
))
375 calc
+= symbol
- (symbol
/ VTUf
);
383 VTUf
= min((int)(symbol
/ diff
), 15);
384 calc
+= symbol
/ VTUf
;
387 diff
= calc
- tu_valid
;
389 /* no remainder, but the hw doesn't like the fractional
390 * part to be zero. decrement the integer part and
391 * have the fraction add a whole symbol back
398 if (diff
< best_diff
) {
409 if (ior
->func
->dp
.activesym
) {
411 nvkm_error(subdev
, "unable to determine dp config\n");
414 ior
->func
->dp
.activesym(ior
, head
->id
, bestTU
,
415 bestVTUa
, bestVTUf
, bestVTUi
);
420 /* XXX close to vbios numbers, but not right */
421 unk
= (symbol
- link_ratio
) * bestTU
;
427 ior
->func
->dp
.watermark(ior
, head
->id
, unk
);
431 nv50_disp_super_2_2(struct nv50_disp
*disp
, struct nvkm_head
*head
)
433 const u32 khz
= head
->asy
.hz
/ 1000;
434 struct nvkm_outp
*outp
;
435 struct nvkm_ior
*ior
;
437 /* Determine which OR, if any, we're attaching from the head. */
438 HEAD_DBG(head
, "supervisor 2.2");
439 ior
= nv50_disp_super_ior_asy(head
);
443 /* For some reason, NVIDIA decided not to:
445 * A) Give dual-link LVDS a separate EVO protocol, like for TMDS.
447 * B) Use SetControlOutputResource.PixelDepth on LVDS.
449 * Override the values we usually read from HW with the same
450 * data we pass though an ioctl instead.
452 if (ior
->type
== SOR
&& ior
->asy
.proto
== LVDS
) {
453 head
->asy
.or.depth
= (disp
->sor
.lvdsconf
& 0x0200) ? 24 : 18;
454 ior
->asy
.link
= (disp
->sor
.lvdsconf
& 0x0100) ? 3 : 1;
457 /* Handle any link training, etc. */
458 if ((outp
= ior
->asy
.outp
) && outp
->func
->acquire
)
459 outp
->func
->acquire(outp
);
461 /* Execute OnInt2 IED script. */
462 nv50_disp_super_ied_on(head
, ior
, 0, khz
);
464 /* Program RG clock divider. */
465 head
->func
->rgclk(head
, ior
->asy
.rgdiv
);
467 /* Mode-specific internal DP configuration. */
468 if (ior
->type
== SOR
&& ior
->asy
.proto
== DP
)
469 nv50_disp_super_2_2_dp(head
, ior
);
471 /* OR-specific handling. */
472 ior
->func
->clock(ior
);
473 if (ior
->func
->war_2
)
474 ior
->func
->war_2(ior
);
478 nv50_disp_super_2_1(struct nv50_disp
*disp
, struct nvkm_head
*head
)
480 struct nvkm_devinit
*devinit
= disp
->base
.engine
.subdev
.device
->devinit
;
481 const u32 khz
= head
->asy
.hz
/ 1000;
482 HEAD_DBG(head
, "supervisor 2.1 - %d khz", khz
);
484 nvkm_devinit_pll_set(devinit
, PLL_VPLL0
+ head
->id
, khz
);
488 nv50_disp_super_2_0(struct nv50_disp
*disp
, struct nvkm_head
*head
)
490 struct nvkm_outp
*outp
;
491 struct nvkm_ior
*ior
;
493 /* Determine which OR, if any, we're detaching from the head. */
494 HEAD_DBG(head
, "supervisor 2.0");
495 ior
= nv50_disp_super_ior_arm(head
);
499 /* Execute OffInt2 IED script. */
500 nv50_disp_super_ied_off(head
, ior
, 2);
502 /* If we're shutting down the OR's only active head, execute
503 * the output path's disable function.
505 if (ior
->arm
.head
== (1 << head
->id
)) {
506 if ((outp
= ior
->arm
.outp
) && outp
->func
->disable
)
507 outp
->func
->disable(outp
, ior
);
512 nv50_disp_super_1_0(struct nv50_disp
*disp
, struct nvkm_head
*head
)
514 struct nvkm_ior
*ior
;
516 /* Determine which OR, if any, we're detaching from the head. */
517 HEAD_DBG(head
, "supervisor 1.0");
518 ior
= nv50_disp_super_ior_arm(head
);
522 /* Execute OffInt1 IED script. */
523 nv50_disp_super_ied_off(head
, ior
, 1);
527 nv50_disp_super_1(struct nv50_disp
*disp
)
529 struct nvkm_head
*head
;
530 struct nvkm_ior
*ior
;
532 list_for_each_entry(head
, &disp
->base
.head
, head
) {
533 head
->func
->state(head
, &head
->arm
);
534 head
->func
->state(head
, &head
->asy
);
537 list_for_each_entry(ior
, &disp
->base
.ior
, head
) {
538 ior
->func
->state(ior
, &ior
->arm
);
539 ior
->func
->state(ior
, &ior
->asy
);
544 nv50_disp_super(struct work_struct
*work
)
546 struct nv50_disp
*disp
=
547 container_of(work
, struct nv50_disp
, supervisor
);
548 struct nvkm_subdev
*subdev
= &disp
->base
.engine
.subdev
;
549 struct nvkm_device
*device
= subdev
->device
;
550 struct nvkm_head
*head
;
551 u32 super
= nvkm_rd32(device
, 0x610030);
553 nvkm_debug(subdev
, "supervisor %08x %08x\n", disp
->super
, super
);
555 if (disp
->super
& 0x00000010) {
556 nv50_disp_chan_mthd(disp
->chan
[0], NV_DBG_DEBUG
);
557 nv50_disp_super_1(disp
);
558 list_for_each_entry(head
, &disp
->base
.head
, head
) {
559 if (!(super
& (0x00000020 << head
->id
)))
561 if (!(super
& (0x00000080 << head
->id
)))
563 nv50_disp_super_1_0(disp
, head
);
566 if (disp
->super
& 0x00000020) {
567 list_for_each_entry(head
, &disp
->base
.head
, head
) {
568 if (!(super
& (0x00000080 << head
->id
)))
570 nv50_disp_super_2_0(disp
, head
);
572 nvkm_outp_route(&disp
->base
);
573 list_for_each_entry(head
, &disp
->base
.head
, head
) {
574 if (!(super
& (0x00000200 << head
->id
)))
576 nv50_disp_super_2_1(disp
, head
);
578 list_for_each_entry(head
, &disp
->base
.head
, head
) {
579 if (!(super
& (0x00000080 << head
->id
)))
581 nv50_disp_super_2_2(disp
, head
);
584 if (disp
->super
& 0x00000040) {
585 list_for_each_entry(head
, &disp
->base
.head
, head
) {
586 if (!(super
& (0x00000080 << head
->id
)))
588 nv50_disp_super_3_0(disp
, head
);
592 nvkm_wr32(device
, 0x610030, 0x80000000);
595 const struct nvkm_enum
596 nv50_disp_intr_error_type
[] = {
598 { 1, "PUSHBUFFER_ERR" },
600 { 3, "RESERVED_METHOD" },
601 { 4, "INVALID_ARG" },
602 { 5, "INVALID_STATE" },
603 { 7, "UNRESOLVABLE_HANDLE" },
607 static const struct nvkm_enum
608 nv50_disp_intr_error_code
[] = {
614 nv50_disp_intr_error(struct nv50_disp
*disp
, int chid
)
616 struct nvkm_subdev
*subdev
= &disp
->base
.engine
.subdev
;
617 struct nvkm_device
*device
= subdev
->device
;
618 u32 data
= nvkm_rd32(device
, 0x610084 + (chid
* 0x08));
619 u32 addr
= nvkm_rd32(device
, 0x610080 + (chid
* 0x08));
620 u32 code
= (addr
& 0x00ff0000) >> 16;
621 u32 type
= (addr
& 0x00007000) >> 12;
622 u32 mthd
= (addr
& 0x00000ffc);
623 const struct nvkm_enum
*ec
, *et
;
625 et
= nvkm_enum_find(nv50_disp_intr_error_type
, type
);
626 ec
= nvkm_enum_find(nv50_disp_intr_error_code
, code
);
629 "ERROR %d [%s] %02x [%s] chid %d mthd %04x data %08x\n",
630 type
, et
? et
->name
: "", code
, ec
? ec
->name
: "",
633 if (chid
< ARRAY_SIZE(disp
->chan
)) {
636 nv50_disp_chan_mthd(disp
->chan
[chid
], NV_DBG_ERROR
);
643 nvkm_wr32(device
, 0x610020, 0x00010000 << chid
);
644 nvkm_wr32(device
, 0x610080 + (chid
* 0x08), 0x90000000);
648 nv50_disp_intr(struct nv50_disp
*disp
)
650 struct nvkm_device
*device
= disp
->base
.engine
.subdev
.device
;
651 u32 intr0
= nvkm_rd32(device
, 0x610020);
652 u32 intr1
= nvkm_rd32(device
, 0x610024);
654 while (intr0
& 0x001f0000) {
655 u32 chid
= __ffs(intr0
& 0x001f0000) - 16;
656 nv50_disp_intr_error(disp
, chid
);
657 intr0
&= ~(0x00010000 << chid
);
660 while (intr0
& 0x0000001f) {
661 u32 chid
= __ffs(intr0
& 0x0000001f);
662 nv50_disp_chan_uevent_send(disp
, chid
);
663 intr0
&= ~(0x00000001 << chid
);
666 if (intr1
& 0x00000004) {
667 nvkm_disp_vblank(&disp
->base
, 0);
668 nvkm_wr32(device
, 0x610024, 0x00000004);
671 if (intr1
& 0x00000008) {
672 nvkm_disp_vblank(&disp
->base
, 1);
673 nvkm_wr32(device
, 0x610024, 0x00000008);
676 if (intr1
& 0x00000070) {
677 disp
->super
= (intr1
& 0x00000070);
678 queue_work(disp
->wq
, &disp
->supervisor
);
679 nvkm_wr32(device
, 0x610024, disp
->super
);
684 nv50_disp_fini(struct nv50_disp
*disp
)
686 struct nvkm_device
*device
= disp
->base
.engine
.subdev
.device
;
687 /* disable all interrupts */
688 nvkm_wr32(device
, 0x610024, 0x00000000);
689 nvkm_wr32(device
, 0x610020, 0x00000000);
693 nv50_disp_init(struct nv50_disp
*disp
)
695 struct nvkm_device
*device
= disp
->base
.engine
.subdev
.device
;
696 struct nvkm_head
*head
;
700 /* The below segments of code copying values from one register to
701 * another appear to inform EVO of the display capabilities or
702 * something similar. NFI what the 0x614004 caps are for..
704 tmp
= nvkm_rd32(device
, 0x614004);
705 nvkm_wr32(device
, 0x610184, tmp
);
708 list_for_each_entry(head
, &disp
->base
.head
, head
) {
709 tmp
= nvkm_rd32(device
, 0x616100 + (head
->id
* 0x800));
710 nvkm_wr32(device
, 0x610190 + (head
->id
* 0x10), tmp
);
711 tmp
= nvkm_rd32(device
, 0x616104 + (head
->id
* 0x800));
712 nvkm_wr32(device
, 0x610194 + (head
->id
* 0x10), tmp
);
713 tmp
= nvkm_rd32(device
, 0x616108 + (head
->id
* 0x800));
714 nvkm_wr32(device
, 0x610198 + (head
->id
* 0x10), tmp
);
715 tmp
= nvkm_rd32(device
, 0x61610c + (head
->id
* 0x800));
716 nvkm_wr32(device
, 0x61019c + (head
->id
* 0x10), tmp
);
720 for (i
= 0; i
< disp
->dac
.nr
; i
++) {
721 tmp
= nvkm_rd32(device
, 0x61a000 + (i
* 0x800));
722 nvkm_wr32(device
, 0x6101d0 + (i
* 0x04), tmp
);
726 for (i
= 0; i
< disp
->sor
.nr
; i
++) {
727 tmp
= nvkm_rd32(device
, 0x61c000 + (i
* 0x800));
728 nvkm_wr32(device
, 0x6101e0 + (i
* 0x04), tmp
);
732 for (i
= 0; i
< disp
->pior
.nr
; i
++) {
733 tmp
= nvkm_rd32(device
, 0x61e000 + (i
* 0x800));
734 nvkm_wr32(device
, 0x6101f0 + (i
* 0x04), tmp
);
737 /* steal display away from vbios, or something like that */
738 if (nvkm_rd32(device
, 0x610024) & 0x00000100) {
739 nvkm_wr32(device
, 0x610024, 0x00000100);
740 nvkm_mask(device
, 0x6194e8, 0x00000001, 0x00000000);
741 if (nvkm_msec(device
, 2000,
742 if (!(nvkm_rd32(device
, 0x6194e8) & 0x00000002))
748 /* point at display engine memory area (hash table, objects) */
749 nvkm_wr32(device
, 0x610010, (disp
->inst
->addr
>> 8) | 9);
751 /* enable supervisor interrupts, disable everything else */
752 nvkm_wr32(device
, 0x61002c, 0x00000370);
753 nvkm_wr32(device
, 0x610028, 0x00000000);
757 static const struct nv50_disp_func
759 .init
= nv50_disp_init
,
760 .fini
= nv50_disp_fini
,
761 .intr
= nv50_disp_intr
,
762 .uevent
= &nv50_disp_chan_uevent
,
763 .super
= nv50_disp_super
,
764 .root
= &nv50_disp_root_oclass
,
765 .head
= { .cnt
= nv50_head_cnt
, .new = nv50_head_new
},
766 .dac
= { .cnt
= nv50_dac_cnt
, .new = nv50_dac_new
},
767 .sor
= { .cnt
= nv50_sor_cnt
, .new = nv50_sor_new
},
768 .pior
= { .cnt
= nv50_pior_cnt
, .new = nv50_pior_new
},
772 nv50_disp_new(struct nvkm_device
*device
, int index
, struct nvkm_disp
**pdisp
)
774 return nv50_disp_new_(&nv50_disp
, device
, index
, pdisp
);