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