]>
Commit | Line | Data |
---|---|---|
2a7909c0 BS |
1 | /* |
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 | #include "nv50.h" | |
25 | #include "rootnv50.h" | |
26 | ||
27 | #include <subdev/bios.h> | |
28 | #include <subdev/bios/disp.h> | |
29 | #include <subdev/bios/init.h> | |
30 | #include <subdev/bios/pll.h> | |
31 | #include <subdev/devinit.h> | |
32 | ||
70aa8670 BS |
33 | void |
34 | gf119_disp_vblank_init(struct nv50_disp *disp, int head) | |
2a7909c0 | 35 | { |
70aa8670 | 36 | struct nvkm_device *device = disp->base.engine.subdev.device; |
2a7909c0 BS |
37 | nvkm_mask(device, 0x6100c0 + (head * 0x800), 0x00000001, 0x00000001); |
38 | } | |
39 | ||
70aa8670 BS |
40 | void |
41 | gf119_disp_vblank_fini(struct nv50_disp *disp, int head) | |
2a7909c0 | 42 | { |
70aa8670 | 43 | struct nvkm_device *device = disp->base.engine.subdev.device; |
2a7909c0 BS |
44 | nvkm_mask(device, 0x6100c0 + (head * 0x800), 0x00000001, 0x00000000); |
45 | } | |
46 | ||
2a7909c0 BS |
47 | static struct nvkm_output * |
48 | exec_lookup(struct nv50_disp *disp, int head, int or, u32 ctrl, | |
49 | u32 *data, u8 *ver, u8 *hdr, u8 *cnt, u8 *len, | |
50 | struct nvbios_outp *info) | |
51 | { | |
52 | struct nvkm_subdev *subdev = &disp->base.engine.subdev; | |
53 | struct nvkm_bios *bios = subdev->device->bios; | |
54 | struct nvkm_output *outp; | |
55 | u16 mask, type; | |
56 | ||
57 | if (or < 4) { | |
58 | type = DCB_OUTPUT_ANALOG; | |
59 | mask = 0; | |
60 | } else { | |
61 | or -= 4; | |
62 | switch (ctrl & 0x00000f00) { | |
63 | case 0x00000000: type = DCB_OUTPUT_LVDS; mask = 1; break; | |
64 | case 0x00000100: type = DCB_OUTPUT_TMDS; mask = 1; break; | |
65 | case 0x00000200: type = DCB_OUTPUT_TMDS; mask = 2; break; | |
66 | case 0x00000500: type = DCB_OUTPUT_TMDS; mask = 3; break; | |
67 | case 0x00000800: type = DCB_OUTPUT_DP; mask = 1; break; | |
68 | case 0x00000900: type = DCB_OUTPUT_DP; mask = 2; break; | |
69 | default: | |
70 | nvkm_error(subdev, "unknown SOR mc %08x\n", ctrl); | |
71 | return NULL; | |
72 | } | |
73 | } | |
74 | ||
75 | mask = 0x00c0 & (mask << 6); | |
76 | mask |= 0x0001 << or; | |
77 | mask |= 0x0100 << head; | |
78 | ||
79 | list_for_each_entry(outp, &disp->base.outp, head) { | |
80 | if ((outp->info.hasht & 0xff) == type && | |
81 | (outp->info.hashm & mask) == mask) { | |
9a2b8131 | 82 | *data = nvbios_outp_match(bios, outp->info.hasht, mask, |
2a7909c0 BS |
83 | ver, hdr, cnt, len, info); |
84 | if (!*data) | |
85 | return NULL; | |
86 | return outp; | |
87 | } | |
88 | } | |
89 | ||
90 | return NULL; | |
91 | } | |
92 | ||
93 | static struct nvkm_output * | |
94 | exec_script(struct nv50_disp *disp, int head, int id) | |
95 | { | |
70aa8670 BS |
96 | struct nvkm_subdev *subdev = &disp->base.engine.subdev; |
97 | struct nvkm_device *device = subdev->device; | |
2a7909c0 BS |
98 | struct nvkm_bios *bios = device->bios; |
99 | struct nvkm_output *outp; | |
100 | struct nvbios_outp info; | |
101 | u8 ver, hdr, cnt, len; | |
102 | u32 data, ctrl = 0; | |
103 | int or; | |
104 | ||
105 | for (or = 0; !(ctrl & (1 << head)) && or < 8; or++) { | |
106 | ctrl = nvkm_rd32(device, 0x640180 + (or * 0x20)); | |
107 | if (ctrl & (1 << head)) | |
108 | break; | |
109 | } | |
110 | ||
111 | if (or == 8) | |
112 | return NULL; | |
113 | ||
114 | outp = exec_lookup(disp, head, or, ctrl, &data, &ver, &hdr, &cnt, &len, &info); | |
115 | if (outp) { | |
116 | struct nvbios_init init = { | |
70aa8670 | 117 | .subdev = subdev, |
2a7909c0 BS |
118 | .bios = bios, |
119 | .offset = info.script[id], | |
120 | .outp = &outp->info, | |
121 | .crtc = head, | |
122 | .execute = 1, | |
123 | }; | |
124 | ||
125 | nvbios_exec(&init); | |
126 | } | |
127 | ||
128 | return outp; | |
129 | } | |
130 | ||
131 | static struct nvkm_output * | |
132 | exec_clkcmp(struct nv50_disp *disp, int head, int id, u32 pclk, u32 *conf) | |
133 | { | |
70aa8670 BS |
134 | struct nvkm_subdev *subdev = &disp->base.engine.subdev; |
135 | struct nvkm_device *device = subdev->device; | |
2a7909c0 BS |
136 | struct nvkm_bios *bios = device->bios; |
137 | struct nvkm_output *outp; | |
138 | struct nvbios_outp info1; | |
139 | struct nvbios_ocfg info2; | |
140 | u8 ver, hdr, cnt, len; | |
141 | u32 data, ctrl = 0; | |
142 | int or; | |
143 | ||
144 | for (or = 0; !(ctrl & (1 << head)) && or < 8; or++) { | |
145 | ctrl = nvkm_rd32(device, 0x660180 + (or * 0x20)); | |
146 | if (ctrl & (1 << head)) | |
147 | break; | |
148 | } | |
149 | ||
150 | if (or == 8) | |
151 | return NULL; | |
152 | ||
153 | outp = exec_lookup(disp, head, or, ctrl, &data, &ver, &hdr, &cnt, &len, &info1); | |
154 | if (!outp) | |
155 | return NULL; | |
156 | ||
bc9139d2 | 157 | *conf = (ctrl & 0x00000f00) >> 8; |
2a7909c0 BS |
158 | switch (outp->info.type) { |
159 | case DCB_OUTPUT_TMDS: | |
16ef53a9 | 160 | if (*conf == 5) |
2a7909c0 BS |
161 | *conf |= 0x0100; |
162 | break; | |
163 | case DCB_OUTPUT_LVDS: | |
bc9139d2 | 164 | *conf |= disp->sor.lvdsconf; |
2a7909c0 | 165 | break; |
2a7909c0 | 166 | default: |
2a7909c0 BS |
167 | break; |
168 | } | |
169 | ||
bc9139d2 BS |
170 | data = nvbios_ocfg_match(bios, data, *conf & 0xff, *conf >> 8, |
171 | &ver, &hdr, &cnt, &len, &info2); | |
2a7909c0 BS |
172 | if (data && id < 0xff) { |
173 | data = nvbios_oclk_match(bios, info2.clkcmp[id], pclk); | |
174 | if (data) { | |
175 | struct nvbios_init init = { | |
70aa8670 | 176 | .subdev = subdev, |
2a7909c0 BS |
177 | .bios = bios, |
178 | .offset = data, | |
179 | .outp = &outp->info, | |
180 | .crtc = head, | |
181 | .execute = 1, | |
182 | }; | |
183 | ||
184 | nvbios_exec(&init); | |
185 | } | |
186 | } | |
187 | ||
188 | return outp; | |
189 | } | |
190 | ||
191 | static void | |
192 | gf119_disp_intr_unk1_0(struct nv50_disp *disp, int head) | |
193 | { | |
194 | exec_script(disp, head, 1); | |
195 | } | |
196 | ||
197 | static void | |
198 | gf119_disp_intr_unk2_0(struct nv50_disp *disp, int head) | |
199 | { | |
46484438 | 200 | struct nvkm_subdev *subdev = &disp->base.engine.subdev; |
2a7909c0 BS |
201 | struct nvkm_output *outp = exec_script(disp, head, 2); |
202 | ||
203 | /* see note in nv50_disp_intr_unk20_0() */ | |
204 | if (outp && outp->info.type == DCB_OUTPUT_DP) { | |
205 | struct nvkm_output_dp *outpdp = nvkm_output_dp(outp); | |
206 | struct nvbios_init init = { | |
46484438 BS |
207 | .subdev = subdev, |
208 | .bios = subdev->device->bios, | |
2a7909c0 BS |
209 | .outp = &outp->info, |
210 | .crtc = head, | |
211 | .offset = outpdp->info.script[4], | |
212 | .execute = 1, | |
213 | }; | |
214 | ||
215 | nvbios_exec(&init); | |
216 | atomic_set(&outpdp->lt.done, 0); | |
217 | } | |
218 | } | |
219 | ||
220 | static void | |
221 | gf119_disp_intr_unk2_1(struct nv50_disp *disp, int head) | |
222 | { | |
223 | struct nvkm_device *device = disp->base.engine.subdev.device; | |
224 | struct nvkm_devinit *devinit = device->devinit; | |
225 | u32 pclk = nvkm_rd32(device, 0x660450 + (head * 0x300)) / 1000; | |
226 | if (pclk) | |
151abd44 | 227 | nvkm_devinit_pll_set(devinit, PLL_VPLL0 + head, pclk); |
2a7909c0 BS |
228 | nvkm_wr32(device, 0x612200 + (head * 0x800), 0x00000000); |
229 | } | |
230 | ||
231 | static void | |
232 | gf119_disp_intr_unk2_2_tu(struct nv50_disp *disp, int head, | |
233 | struct dcb_output *outp) | |
234 | { | |
235 | struct nvkm_device *device = disp->base.engine.subdev.device; | |
236 | const int or = ffs(outp->or) - 1; | |
237 | const u32 ctrl = nvkm_rd32(device, 0x660200 + (or * 0x020)); | |
238 | const u32 conf = nvkm_rd32(device, 0x660404 + (head * 0x300)); | |
239 | const s32 vactive = nvkm_rd32(device, 0x660414 + (head * 0x300)) & 0xffff; | |
240 | const s32 vblanke = nvkm_rd32(device, 0x66041c + (head * 0x300)) & 0xffff; | |
241 | const s32 vblanks = nvkm_rd32(device, 0x660420 + (head * 0x300)) & 0xffff; | |
242 | const u32 pclk = nvkm_rd32(device, 0x660450 + (head * 0x300)) / 1000; | |
243 | const u32 link = ((ctrl & 0xf00) == 0x800) ? 0 : 1; | |
244 | const u32 hoff = (head * 0x800); | |
245 | const u32 soff = ( or * 0x800); | |
246 | const u32 loff = (link * 0x080) + soff; | |
247 | const u32 symbol = 100000; | |
248 | const u32 TU = 64; | |
249 | u32 dpctrl = nvkm_rd32(device, 0x61c10c + loff); | |
250 | u32 clksor = nvkm_rd32(device, 0x612300 + soff); | |
251 | u32 datarate, link_nr, link_bw, bits; | |
252 | u64 ratio, value; | |
253 | ||
254 | link_nr = hweight32(dpctrl & 0x000f0000); | |
255 | link_bw = (clksor & 0x007c0000) >> 18; | |
256 | link_bw *= 27000; | |
257 | ||
258 | /* symbols/hblank - algorithm taken from comments in tegra driver */ | |
259 | value = vblanke + vactive - vblanks - 7; | |
260 | value = value * link_bw; | |
261 | do_div(value, pclk); | |
262 | value = value - (3 * !!(dpctrl & 0x00004000)) - (12 / link_nr); | |
263 | nvkm_mask(device, 0x616620 + hoff, 0x0000ffff, value); | |
264 | ||
265 | /* symbols/vblank - algorithm taken from comments in tegra driver */ | |
266 | value = vblanks - vblanke - 25; | |
267 | value = value * link_bw; | |
268 | do_div(value, pclk); | |
269 | value = value - ((36 / link_nr) + 3) - 1; | |
270 | nvkm_mask(device, 0x616624 + hoff, 0x00ffffff, value); | |
271 | ||
272 | /* watermark */ | |
273 | if ((conf & 0x3c0) == 0x180) bits = 30; | |
274 | else if ((conf & 0x3c0) == 0x140) bits = 24; | |
275 | else bits = 18; | |
276 | datarate = (pclk * bits) / 8; | |
277 | ||
278 | ratio = datarate; | |
279 | ratio *= symbol; | |
280 | do_div(ratio, link_nr * link_bw); | |
281 | ||
282 | value = (symbol - ratio) * TU; | |
283 | value *= ratio; | |
284 | do_div(value, symbol); | |
285 | do_div(value, symbol); | |
286 | ||
287 | value += 5; | |
288 | value |= 0x08000000; | |
289 | ||
290 | nvkm_wr32(device, 0x616610 + hoff, value); | |
291 | } | |
292 | ||
293 | static void | |
294 | gf119_disp_intr_unk2_2(struct nv50_disp *disp, int head) | |
295 | { | |
296 | struct nvkm_device *device = disp->base.engine.subdev.device; | |
297 | struct nvkm_output *outp; | |
298 | u32 pclk = nvkm_rd32(device, 0x660450 + (head * 0x300)) / 1000; | |
299 | u32 conf, addr, data; | |
300 | ||
301 | outp = exec_clkcmp(disp, head, 0xff, pclk, &conf); | |
302 | if (!outp) | |
303 | return; | |
304 | ||
305 | /* see note in nv50_disp_intr_unk20_2() */ | |
306 | if (outp->info.type == DCB_OUTPUT_DP) { | |
307 | u32 sync = nvkm_rd32(device, 0x660404 + (head * 0x300)); | |
308 | switch ((sync & 0x000003c0) >> 6) { | |
309 | case 6: pclk = pclk * 30; break; | |
310 | case 5: pclk = pclk * 24; break; | |
311 | case 2: | |
312 | default: | |
313 | pclk = pclk * 18; | |
314 | break; | |
315 | } | |
316 | ||
317 | if (nvkm_output_dp_train(outp, pclk, true)) | |
318 | OUTP_ERR(outp, "link not trained before attach"); | |
319 | } else { | |
70aa8670 BS |
320 | if (disp->func->sor.magic) |
321 | disp->func->sor.magic(outp); | |
2a7909c0 BS |
322 | } |
323 | ||
324 | exec_clkcmp(disp, head, 0, pclk, &conf); | |
325 | ||
326 | if (outp->info.type == DCB_OUTPUT_ANALOG) { | |
327 | addr = 0x612280 + (ffs(outp->info.or) - 1) * 0x800; | |
328 | data = 0x00000000; | |
329 | } else { | |
330 | addr = 0x612300 + (ffs(outp->info.or) - 1) * 0x800; | |
331 | data = (conf & 0x0100) ? 0x00000101 : 0x00000000; | |
332 | switch (outp->info.type) { | |
333 | case DCB_OUTPUT_TMDS: | |
334 | nvkm_mask(device, addr, 0x007c0000, 0x00280000); | |
335 | break; | |
336 | case DCB_OUTPUT_DP: | |
337 | gf119_disp_intr_unk2_2_tu(disp, head, &outp->info); | |
338 | break; | |
339 | default: | |
340 | break; | |
341 | } | |
342 | } | |
343 | ||
344 | nvkm_mask(device, addr, 0x00000707, data); | |
345 | } | |
346 | ||
347 | static void | |
348 | gf119_disp_intr_unk4_0(struct nv50_disp *disp, int head) | |
349 | { | |
350 | struct nvkm_device *device = disp->base.engine.subdev.device; | |
351 | u32 pclk = nvkm_rd32(device, 0x660450 + (head * 0x300)) / 1000; | |
352 | u32 conf; | |
353 | ||
354 | exec_clkcmp(disp, head, 1, pclk, &conf); | |
355 | } | |
356 | ||
357 | void | |
358 | gf119_disp_intr_supervisor(struct work_struct *work) | |
359 | { | |
360 | struct nv50_disp *disp = | |
361 | container_of(work, struct nv50_disp, supervisor); | |
2a7909c0 BS |
362 | struct nvkm_subdev *subdev = &disp->base.engine.subdev; |
363 | struct nvkm_device *device = subdev->device; | |
364 | u32 mask[4]; | |
365 | int head; | |
366 | ||
367 | nvkm_debug(subdev, "supervisor %d\n", ffs(disp->super)); | |
70aa8670 | 368 | for (head = 0; head < disp->base.head.nr; head++) { |
2a7909c0 BS |
369 | mask[head] = nvkm_rd32(device, 0x6101d4 + (head * 0x800)); |
370 | nvkm_debug(subdev, "head %d: %08x\n", head, mask[head]); | |
371 | } | |
372 | ||
373 | if (disp->super & 0x00000001) { | |
0ce41e3c | 374 | nv50_disp_chan_mthd(disp->chan[0], NV_DBG_DEBUG); |
70aa8670 | 375 | for (head = 0; head < disp->base.head.nr; head++) { |
2a7909c0 BS |
376 | if (!(mask[head] & 0x00001000)) |
377 | continue; | |
378 | nvkm_debug(subdev, "supervisor 1.0 - head %d\n", head); | |
379 | gf119_disp_intr_unk1_0(disp, head); | |
380 | } | |
381 | } else | |
382 | if (disp->super & 0x00000002) { | |
70aa8670 | 383 | for (head = 0; head < disp->base.head.nr; head++) { |
2a7909c0 BS |
384 | if (!(mask[head] & 0x00001000)) |
385 | continue; | |
386 | nvkm_debug(subdev, "supervisor 2.0 - head %d\n", head); | |
387 | gf119_disp_intr_unk2_0(disp, head); | |
388 | } | |
70aa8670 | 389 | for (head = 0; head < disp->base.head.nr; head++) { |
2a7909c0 BS |
390 | if (!(mask[head] & 0x00010000)) |
391 | continue; | |
392 | nvkm_debug(subdev, "supervisor 2.1 - head %d\n", head); | |
393 | gf119_disp_intr_unk2_1(disp, head); | |
394 | } | |
70aa8670 | 395 | for (head = 0; head < disp->base.head.nr; head++) { |
2a7909c0 BS |
396 | if (!(mask[head] & 0x00001000)) |
397 | continue; | |
398 | nvkm_debug(subdev, "supervisor 2.2 - head %d\n", head); | |
399 | gf119_disp_intr_unk2_2(disp, head); | |
400 | } | |
401 | } else | |
402 | if (disp->super & 0x00000004) { | |
70aa8670 | 403 | for (head = 0; head < disp->base.head.nr; head++) { |
2a7909c0 BS |
404 | if (!(mask[head] & 0x00001000)) |
405 | continue; | |
406 | nvkm_debug(subdev, "supervisor 3.0 - head %d\n", head); | |
407 | gf119_disp_intr_unk4_0(disp, head); | |
408 | } | |
409 | } | |
410 | ||
70aa8670 | 411 | for (head = 0; head < disp->base.head.nr; head++) |
2a7909c0 BS |
412 | nvkm_wr32(device, 0x6101d4 + (head * 0x800), 0x00000000); |
413 | nvkm_wr32(device, 0x6101d0, 0x80000000); | |
414 | } | |
415 | ||
416 | static void | |
417 | gf119_disp_intr_error(struct nv50_disp *disp, int chid) | |
418 | { | |
2a7909c0 BS |
419 | struct nvkm_subdev *subdev = &disp->base.engine.subdev; |
420 | struct nvkm_device *device = subdev->device; | |
421 | u32 mthd = nvkm_rd32(device, 0x6101f0 + (chid * 12)); | |
422 | u32 data = nvkm_rd32(device, 0x6101f4 + (chid * 12)); | |
423 | u32 unkn = nvkm_rd32(device, 0x6101f8 + (chid * 12)); | |
424 | ||
425 | nvkm_error(subdev, "chid %d mthd %04x data %08x %08x %08x\n", | |
426 | chid, (mthd & 0x0000ffc), data, mthd, unkn); | |
427 | ||
0ce41e3c | 428 | if (chid < ARRAY_SIZE(disp->chan)) { |
2a7909c0 BS |
429 | switch (mthd & 0xffc) { |
430 | case 0x0080: | |
0ce41e3c | 431 | nv50_disp_chan_mthd(disp->chan[chid], NV_DBG_ERROR); |
2a7909c0 BS |
432 | break; |
433 | default: | |
434 | break; | |
435 | } | |
436 | } | |
437 | ||
438 | nvkm_wr32(device, 0x61009c, (1 << chid)); | |
439 | nvkm_wr32(device, 0x6101f0 + (chid * 12), 0x90000000); | |
440 | } | |
441 | ||
442 | void | |
70aa8670 | 443 | gf119_disp_intr(struct nv50_disp *disp) |
2a7909c0 | 444 | { |
70aa8670 | 445 | struct nvkm_subdev *subdev = &disp->base.engine.subdev; |
2a7909c0 BS |
446 | struct nvkm_device *device = subdev->device; |
447 | u32 intr = nvkm_rd32(device, 0x610088); | |
448 | int i; | |
449 | ||
450 | if (intr & 0x00000001) { | |
451 | u32 stat = nvkm_rd32(device, 0x61008c); | |
452 | while (stat) { | |
453 | int chid = __ffs(stat); stat &= ~(1 << chid); | |
454 | nv50_disp_chan_uevent_send(disp, chid); | |
455 | nvkm_wr32(device, 0x61008c, 1 << chid); | |
456 | } | |
457 | intr &= ~0x00000001; | |
458 | } | |
459 | ||
460 | if (intr & 0x00000002) { | |
461 | u32 stat = nvkm_rd32(device, 0x61009c); | |
462 | int chid = ffs(stat) - 1; | |
463 | if (chid >= 0) | |
464 | gf119_disp_intr_error(disp, chid); | |
465 | intr &= ~0x00000002; | |
466 | } | |
467 | ||
468 | if (intr & 0x00100000) { | |
469 | u32 stat = nvkm_rd32(device, 0x6100ac); | |
470 | if (stat & 0x00000007) { | |
471 | disp->super = (stat & 0x00000007); | |
472 | schedule_work(&disp->supervisor); | |
473 | nvkm_wr32(device, 0x6100ac, disp->super); | |
474 | stat &= ~0x00000007; | |
475 | } | |
476 | ||
477 | if (stat) { | |
478 | nvkm_warn(subdev, "intr24 %08x\n", stat); | |
479 | nvkm_wr32(device, 0x6100ac, stat); | |
480 | } | |
481 | ||
482 | intr &= ~0x00100000; | |
483 | } | |
484 | ||
70aa8670 | 485 | for (i = 0; i < disp->base.head.nr; i++) { |
2a7909c0 BS |
486 | u32 mask = 0x01000000 << i; |
487 | if (mask & intr) { | |
488 | u32 stat = nvkm_rd32(device, 0x6100bc + (i * 0x800)); | |
489 | if (stat & 0x00000001) | |
490 | nvkm_disp_vblank(&disp->base, i); | |
491 | nvkm_mask(device, 0x6100bc + (i * 0x800), 0, 0); | |
492 | nvkm_rd32(device, 0x6100c0 + (i * 0x800)); | |
493 | } | |
494 | } | |
495 | } | |
496 | ||
70aa8670 BS |
497 | int |
498 | gf119_disp_new_(const struct nv50_disp_func *func, struct nvkm_device *device, | |
499 | int index, struct nvkm_disp **pdisp) | |
500 | { | |
501 | u32 heads = nvkm_rd32(device, 0x022448); | |
502 | return nv50_disp_new_(func, device, index, heads, pdisp); | |
503 | } | |
504 | ||
505 | static const struct nv50_disp_func | |
0ce41e3c | 506 | gf119_disp = { |
70aa8670 BS |
507 | .intr = gf119_disp_intr, |
508 | .uevent = &gf119_disp_chan_uevent, | |
509 | .super = gf119_disp_intr_supervisor, | |
0ce41e3c | 510 | .root = &gf119_disp_root_oclass, |
70aa8670 BS |
511 | .head.vblank_init = gf119_disp_vblank_init, |
512 | .head.vblank_fini = gf119_disp_vblank_fini, | |
513 | .head.scanoutpos = gf119_disp_root_scanoutpos, | |
514 | .outp.internal.crt = nv50_dac_output_new, | |
515 | .outp.internal.tmds = nv50_sor_output_new, | |
516 | .outp.internal.lvds = nv50_sor_output_new, | |
517 | .outp.internal.dp = gf119_sor_dp_new, | |
518 | .dac.nr = 3, | |
519 | .dac.power = nv50_dac_power, | |
520 | .dac.sense = nv50_dac_sense, | |
521 | .sor.nr = 4, | |
522 | .sor.power = nv50_sor_power, | |
523 | .sor.hda_eld = gf119_hda_eld, | |
524 | .sor.hdmi = gf119_hdmi_ctrl, | |
0ce41e3c BS |
525 | }; |
526 | ||
70aa8670 BS |
527 | int |
528 | gf119_disp_new(struct nvkm_device *device, int index, struct nvkm_disp **pdisp) | |
2a7909c0 | 529 | { |
70aa8670 | 530 | return gf119_disp_new_(&gf119_disp, device, index, pdisp); |
2a7909c0 | 531 | } |