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