1 | /* $NetBSD: nouveau_engine_disp_nvd0.c,v 1.3 2016/04/23 14:59:01 riastradh Exp $ */ |
2 | |
3 | /* |
4 | * Copyright 2012 Red Hat Inc. |
5 | * |
6 | * Permission is hereby granted, free of charge, to any person obtaining a |
7 | * copy of this software and associated documentation files (the "Software"), |
8 | * to deal in the Software without restriction, including without limitation |
9 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, |
10 | * and/or sell copies of the Software, and to permit persons to whom the |
11 | * Software is furnished to do so, subject to the following conditions: |
12 | * |
13 | * The above copyright notice and this permission notice shall be included in |
14 | * all copies or substantial portions of the Software. |
15 | * |
16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL |
19 | * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR |
20 | * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, |
21 | * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR |
22 | * OTHER DEALINGS IN THE SOFTWARE. |
23 | * |
24 | * Authors: Ben Skeggs |
25 | */ |
26 | |
27 | #include <sys/cdefs.h> |
28 | __KERNEL_RCSID(0, "$NetBSD: nouveau_engine_disp_nvd0.c,v 1.3 2016/04/23 14:59:01 riastradh Exp $" ); |
29 | |
30 | #include <core/object.h> |
31 | #include <core/parent.h> |
32 | #include <core/handle.h> |
33 | #include <core/class.h> |
34 | |
35 | #include <engine/disp.h> |
36 | |
37 | #include <subdev/bios.h> |
38 | #include <subdev/bios/dcb.h> |
39 | #include <subdev/bios/disp.h> |
40 | #include <subdev/bios/init.h> |
41 | #include <subdev/bios/pll.h> |
42 | #include <subdev/devinit.h> |
43 | #include <subdev/fb.h> |
44 | #include <subdev/timer.h> |
45 | |
46 | #include <asm/div64.h> /* XXX */ |
47 | #include <linux/ktime.h> /* XXX */ |
48 | |
49 | #include "nv50.h" |
50 | |
51 | /******************************************************************************* |
52 | * EVO DMA channel base class |
53 | ******************************************************************************/ |
54 | |
55 | static int |
56 | nvd0_disp_dmac_object_attach(struct nouveau_object *parent, |
57 | struct nouveau_object *object, u32 name) |
58 | { |
59 | struct nv50_disp_base *base = (void *)parent->parent; |
60 | struct nv50_disp_chan *chan = (void *)parent; |
61 | u32 addr = nv_gpuobj(object)->node->offset; |
62 | u32 data = (chan->chid << 27) | (addr << 9) | 0x00000001; |
63 | return nouveau_ramht_insert(base->ramht, chan->chid, name, data); |
64 | } |
65 | |
66 | static void |
67 | nvd0_disp_dmac_object_detach(struct nouveau_object *parent, int cookie) |
68 | { |
69 | struct nv50_disp_base *base = (void *)parent->parent; |
70 | nouveau_ramht_remove(base->ramht, cookie); |
71 | } |
72 | |
73 | static int |
74 | nvd0_disp_dmac_init(struct nouveau_object *object) |
75 | { |
76 | struct nv50_disp_priv *priv = (void *)object->engine; |
77 | struct nv50_disp_dmac *dmac = (void *)object; |
78 | int chid = dmac->base.chid; |
79 | int ret; |
80 | |
81 | ret = nv50_disp_chan_init(&dmac->base); |
82 | if (ret) |
83 | return ret; |
84 | |
85 | /* enable error reporting */ |
86 | nv_mask(priv, 0x610090, 0x00000001 << chid, 0x00000001 << chid); |
87 | nv_mask(priv, 0x6100a0, 0x00000001 << chid, 0x00000001 << chid); |
88 | |
89 | /* initialise channel for dma command submission */ |
90 | nv_wr32(priv, 0x610494 + (chid * 0x0010), dmac->push); |
91 | nv_wr32(priv, 0x610498 + (chid * 0x0010), 0x00010000); |
92 | nv_wr32(priv, 0x61049c + (chid * 0x0010), 0x00000001); |
93 | nv_mask(priv, 0x610490 + (chid * 0x0010), 0x00000010, 0x00000010); |
94 | nv_wr32(priv, 0x640000 + (chid * 0x1000), 0x00000000); |
95 | nv_wr32(priv, 0x610490 + (chid * 0x0010), 0x00000013); |
96 | |
97 | /* wait for it to go inactive */ |
98 | if (!nv_wait(priv, 0x610490 + (chid * 0x10), 0x80000000, 0x00000000)) { |
99 | nv_error(dmac, "init: 0x%08x\n" , |
100 | nv_rd32(priv, 0x610490 + (chid * 0x10))); |
101 | return -EBUSY; |
102 | } |
103 | |
104 | return 0; |
105 | } |
106 | |
107 | static int |
108 | nvd0_disp_dmac_fini(struct nouveau_object *object, bool suspend) |
109 | { |
110 | struct nv50_disp_priv *priv = (void *)object->engine; |
111 | struct nv50_disp_dmac *dmac = (void *)object; |
112 | int chid = dmac->base.chid; |
113 | |
114 | /* deactivate channel */ |
115 | nv_mask(priv, 0x610490 + (chid * 0x0010), 0x00001010, 0x00001000); |
116 | nv_mask(priv, 0x610490 + (chid * 0x0010), 0x00000003, 0x00000000); |
117 | if (!nv_wait(priv, 0x610490 + (chid * 0x10), 0x001e0000, 0x00000000)) { |
118 | nv_error(dmac, "fini: 0x%08x\n" , |
119 | nv_rd32(priv, 0x610490 + (chid * 0x10))); |
120 | if (suspend) |
121 | return -EBUSY; |
122 | } |
123 | |
124 | /* disable error reporting */ |
125 | nv_mask(priv, 0x610090, 0x00000001 << chid, 0x00000000); |
126 | nv_mask(priv, 0x6100a0, 0x00000001 << chid, 0x00000000); |
127 | |
128 | return nv50_disp_chan_fini(&dmac->base, suspend); |
129 | } |
130 | |
131 | /******************************************************************************* |
132 | * EVO master channel object |
133 | ******************************************************************************/ |
134 | |
135 | const struct nv50_disp_mthd_list |
136 | nvd0_disp_mast_mthd_base = { |
137 | .mthd = 0x0000, |
138 | .addr = 0x000000, |
139 | .data = { |
140 | { 0x0080, 0x660080 }, |
141 | { 0x0084, 0x660084 }, |
142 | { 0x0088, 0x660088 }, |
143 | { 0x008c, 0x000000 }, |
144 | {} |
145 | } |
146 | }; |
147 | |
148 | const struct nv50_disp_mthd_list |
149 | nvd0_disp_mast_mthd_dac = { |
150 | .mthd = 0x0020, |
151 | .addr = 0x000020, |
152 | .data = { |
153 | { 0x0180, 0x660180 }, |
154 | { 0x0184, 0x660184 }, |
155 | { 0x0188, 0x660188 }, |
156 | { 0x0190, 0x660190 }, |
157 | {} |
158 | } |
159 | }; |
160 | |
161 | const struct nv50_disp_mthd_list |
162 | nvd0_disp_mast_mthd_sor = { |
163 | .mthd = 0x0020, |
164 | .addr = 0x000020, |
165 | .data = { |
166 | { 0x0200, 0x660200 }, |
167 | { 0x0204, 0x660204 }, |
168 | { 0x0208, 0x660208 }, |
169 | { 0x0210, 0x660210 }, |
170 | {} |
171 | } |
172 | }; |
173 | |
174 | const struct nv50_disp_mthd_list |
175 | nvd0_disp_mast_mthd_pior = { |
176 | .mthd = 0x0020, |
177 | .addr = 0x000020, |
178 | .data = { |
179 | { 0x0300, 0x660300 }, |
180 | { 0x0304, 0x660304 }, |
181 | { 0x0308, 0x660308 }, |
182 | { 0x0310, 0x660310 }, |
183 | {} |
184 | } |
185 | }; |
186 | |
187 | static const struct nv50_disp_mthd_list |
188 | nvd0_disp_mast_mthd_head = { |
189 | .mthd = 0x0300, |
190 | .addr = 0x000300, |
191 | .data = { |
192 | { 0x0400, 0x660400 }, |
193 | { 0x0404, 0x660404 }, |
194 | { 0x0408, 0x660408 }, |
195 | { 0x040c, 0x66040c }, |
196 | { 0x0410, 0x660410 }, |
197 | { 0x0414, 0x660414 }, |
198 | { 0x0418, 0x660418 }, |
199 | { 0x041c, 0x66041c }, |
200 | { 0x0420, 0x660420 }, |
201 | { 0x0424, 0x660424 }, |
202 | { 0x0428, 0x660428 }, |
203 | { 0x042c, 0x66042c }, |
204 | { 0x0430, 0x660430 }, |
205 | { 0x0434, 0x660434 }, |
206 | { 0x0438, 0x660438 }, |
207 | { 0x0440, 0x660440 }, |
208 | { 0x0444, 0x660444 }, |
209 | { 0x0448, 0x660448 }, |
210 | { 0x044c, 0x66044c }, |
211 | { 0x0450, 0x660450 }, |
212 | { 0x0454, 0x660454 }, |
213 | { 0x0458, 0x660458 }, |
214 | { 0x045c, 0x66045c }, |
215 | { 0x0460, 0x660460 }, |
216 | { 0x0468, 0x660468 }, |
217 | { 0x046c, 0x66046c }, |
218 | { 0x0470, 0x660470 }, |
219 | { 0x0474, 0x660474 }, |
220 | { 0x0480, 0x660480 }, |
221 | { 0x0484, 0x660484 }, |
222 | { 0x048c, 0x66048c }, |
223 | { 0x0490, 0x660490 }, |
224 | { 0x0494, 0x660494 }, |
225 | { 0x0498, 0x660498 }, |
226 | { 0x04b0, 0x6604b0 }, |
227 | { 0x04b8, 0x6604b8 }, |
228 | { 0x04bc, 0x6604bc }, |
229 | { 0x04c0, 0x6604c0 }, |
230 | { 0x04c4, 0x6604c4 }, |
231 | { 0x04c8, 0x6604c8 }, |
232 | { 0x04d0, 0x6604d0 }, |
233 | { 0x04d4, 0x6604d4 }, |
234 | { 0x04e0, 0x6604e0 }, |
235 | { 0x04e4, 0x6604e4 }, |
236 | { 0x04e8, 0x6604e8 }, |
237 | { 0x04ec, 0x6604ec }, |
238 | { 0x04f0, 0x6604f0 }, |
239 | { 0x04f4, 0x6604f4 }, |
240 | { 0x04f8, 0x6604f8 }, |
241 | { 0x04fc, 0x6604fc }, |
242 | { 0x0500, 0x660500 }, |
243 | { 0x0504, 0x660504 }, |
244 | { 0x0508, 0x660508 }, |
245 | { 0x050c, 0x66050c }, |
246 | { 0x0510, 0x660510 }, |
247 | { 0x0514, 0x660514 }, |
248 | { 0x0518, 0x660518 }, |
249 | { 0x051c, 0x66051c }, |
250 | { 0x052c, 0x66052c }, |
251 | { 0x0530, 0x660530 }, |
252 | { 0x054c, 0x66054c }, |
253 | { 0x0550, 0x660550 }, |
254 | { 0x0554, 0x660554 }, |
255 | { 0x0558, 0x660558 }, |
256 | { 0x055c, 0x66055c }, |
257 | {} |
258 | } |
259 | }; |
260 | |
261 | static const struct nv50_disp_mthd_chan |
262 | nvd0_disp_mast_mthd_chan = { |
263 | .name = "Core" , |
264 | .addr = 0x000000, |
265 | .data = { |
266 | { "Global" , 1, &nvd0_disp_mast_mthd_base }, |
267 | { "DAC" , 3, &nvd0_disp_mast_mthd_dac }, |
268 | { "SOR" , 8, &nvd0_disp_mast_mthd_sor }, |
269 | { "PIOR" , 4, &nvd0_disp_mast_mthd_pior }, |
270 | { "HEAD" , 4, &nvd0_disp_mast_mthd_head }, |
271 | {} |
272 | } |
273 | }; |
274 | |
275 | static int |
276 | nvd0_disp_mast_ctor(struct nouveau_object *parent, |
277 | struct nouveau_object *engine, |
278 | struct nouveau_oclass *oclass, void *data, u32 size, |
279 | struct nouveau_object **pobject) |
280 | { |
281 | struct nv50_display_mast_class *args = data; |
282 | struct nv50_disp_dmac *mast; |
283 | int ret; |
284 | |
285 | if (size < sizeof(*args)) |
286 | return -EINVAL; |
287 | |
288 | ret = nv50_disp_dmac_create_(parent, engine, oclass, args->pushbuf, |
289 | 0, sizeof(*mast), (void **)&mast); |
290 | *pobject = nv_object(mast); |
291 | if (ret) |
292 | return ret; |
293 | |
294 | nv_parent(mast)->object_attach = nvd0_disp_dmac_object_attach; |
295 | nv_parent(mast)->object_detach = nvd0_disp_dmac_object_detach; |
296 | return 0; |
297 | } |
298 | |
299 | static int |
300 | nvd0_disp_mast_init(struct nouveau_object *object) |
301 | { |
302 | struct nv50_disp_priv *priv = (void *)object->engine; |
303 | struct nv50_disp_dmac *mast = (void *)object; |
304 | int ret; |
305 | |
306 | ret = nv50_disp_chan_init(&mast->base); |
307 | if (ret) |
308 | return ret; |
309 | |
310 | /* enable error reporting */ |
311 | nv_mask(priv, 0x610090, 0x00000001, 0x00000001); |
312 | nv_mask(priv, 0x6100a0, 0x00000001, 0x00000001); |
313 | |
314 | /* initialise channel for dma command submission */ |
315 | nv_wr32(priv, 0x610494, mast->push); |
316 | nv_wr32(priv, 0x610498, 0x00010000); |
317 | nv_wr32(priv, 0x61049c, 0x00000001); |
318 | nv_mask(priv, 0x610490, 0x00000010, 0x00000010); |
319 | nv_wr32(priv, 0x640000, 0x00000000); |
320 | nv_wr32(priv, 0x610490, 0x01000013); |
321 | |
322 | /* wait for it to go inactive */ |
323 | if (!nv_wait(priv, 0x610490, 0x80000000, 0x00000000)) { |
324 | nv_error(mast, "init: 0x%08x\n" , nv_rd32(priv, 0x610490)); |
325 | return -EBUSY; |
326 | } |
327 | |
328 | return 0; |
329 | } |
330 | |
331 | static int |
332 | nvd0_disp_mast_fini(struct nouveau_object *object, bool suspend) |
333 | { |
334 | struct nv50_disp_priv *priv = (void *)object->engine; |
335 | struct nv50_disp_dmac *mast = (void *)object; |
336 | |
337 | /* deactivate channel */ |
338 | nv_mask(priv, 0x610490, 0x00000010, 0x00000000); |
339 | nv_mask(priv, 0x610490, 0x00000003, 0x00000000); |
340 | if (!nv_wait(priv, 0x610490, 0x001e0000, 0x00000000)) { |
341 | nv_error(mast, "fini: 0x%08x\n" , nv_rd32(priv, 0x610490)); |
342 | if (suspend) |
343 | return -EBUSY; |
344 | } |
345 | |
346 | /* disable error reporting */ |
347 | nv_mask(priv, 0x610090, 0x00000001, 0x00000000); |
348 | nv_mask(priv, 0x6100a0, 0x00000001, 0x00000000); |
349 | |
350 | return nv50_disp_chan_fini(&mast->base, suspend); |
351 | } |
352 | |
353 | struct nouveau_ofuncs |
354 | nvd0_disp_mast_ofuncs = { |
355 | .ctor = nvd0_disp_mast_ctor, |
356 | .dtor = nv50_disp_dmac_dtor, |
357 | .init = nvd0_disp_mast_init, |
358 | .fini = nvd0_disp_mast_fini, |
359 | .rd32 = nv50_disp_chan_rd32, |
360 | .wr32 = nv50_disp_chan_wr32, |
361 | }; |
362 | |
363 | /******************************************************************************* |
364 | * EVO sync channel objects |
365 | ******************************************************************************/ |
366 | |
367 | static const struct nv50_disp_mthd_list |
368 | nvd0_disp_sync_mthd_base = { |
369 | .mthd = 0x0000, |
370 | .addr = 0x000000, |
371 | .data = { |
372 | { 0x0080, 0x661080 }, |
373 | { 0x0084, 0x661084 }, |
374 | { 0x0088, 0x661088 }, |
375 | { 0x008c, 0x66108c }, |
376 | { 0x0090, 0x661090 }, |
377 | { 0x0094, 0x661094 }, |
378 | { 0x00a0, 0x6610a0 }, |
379 | { 0x00a4, 0x6610a4 }, |
380 | { 0x00c0, 0x6610c0 }, |
381 | { 0x00c4, 0x6610c4 }, |
382 | { 0x00c8, 0x6610c8 }, |
383 | { 0x00cc, 0x6610cc }, |
384 | { 0x00e0, 0x6610e0 }, |
385 | { 0x00e4, 0x6610e4 }, |
386 | { 0x00e8, 0x6610e8 }, |
387 | { 0x00ec, 0x6610ec }, |
388 | { 0x00fc, 0x6610fc }, |
389 | { 0x0100, 0x661100 }, |
390 | { 0x0104, 0x661104 }, |
391 | { 0x0108, 0x661108 }, |
392 | { 0x010c, 0x66110c }, |
393 | { 0x0110, 0x661110 }, |
394 | { 0x0114, 0x661114 }, |
395 | { 0x0118, 0x661118 }, |
396 | { 0x011c, 0x66111c }, |
397 | { 0x0130, 0x661130 }, |
398 | { 0x0134, 0x661134 }, |
399 | { 0x0138, 0x661138 }, |
400 | { 0x013c, 0x66113c }, |
401 | { 0x0140, 0x661140 }, |
402 | { 0x0144, 0x661144 }, |
403 | { 0x0148, 0x661148 }, |
404 | { 0x014c, 0x66114c }, |
405 | { 0x0150, 0x661150 }, |
406 | { 0x0154, 0x661154 }, |
407 | { 0x0158, 0x661158 }, |
408 | { 0x015c, 0x66115c }, |
409 | { 0x0160, 0x661160 }, |
410 | { 0x0164, 0x661164 }, |
411 | { 0x0168, 0x661168 }, |
412 | { 0x016c, 0x66116c }, |
413 | {} |
414 | } |
415 | }; |
416 | |
417 | static const struct nv50_disp_mthd_list |
418 | nvd0_disp_sync_mthd_image = { |
419 | .mthd = 0x0400, |
420 | .addr = 0x000400, |
421 | .data = { |
422 | { 0x0400, 0x661400 }, |
423 | { 0x0404, 0x661404 }, |
424 | { 0x0408, 0x661408 }, |
425 | { 0x040c, 0x66140c }, |
426 | { 0x0410, 0x661410 }, |
427 | {} |
428 | } |
429 | }; |
430 | |
431 | const struct nv50_disp_mthd_chan |
432 | nvd0_disp_sync_mthd_chan = { |
433 | .name = "Base" , |
434 | .addr = 0x001000, |
435 | .data = { |
436 | { "Global" , 1, &nvd0_disp_sync_mthd_base }, |
437 | { "Image" , 2, &nvd0_disp_sync_mthd_image }, |
438 | {} |
439 | } |
440 | }; |
441 | |
442 | static int |
443 | nvd0_disp_sync_ctor(struct nouveau_object *parent, |
444 | struct nouveau_object *engine, |
445 | struct nouveau_oclass *oclass, void *data, u32 size, |
446 | struct nouveau_object **pobject) |
447 | { |
448 | struct nv50_display_sync_class *args = data; |
449 | struct nv50_disp_priv *priv = (void *)engine; |
450 | struct nv50_disp_dmac *dmac; |
451 | int ret; |
452 | |
453 | if (size < sizeof(*args) || args->head >= priv->head.nr) |
454 | return -EINVAL; |
455 | |
456 | ret = nv50_disp_dmac_create_(parent, engine, oclass, args->pushbuf, |
457 | 1 + args->head, sizeof(*dmac), |
458 | (void **)&dmac); |
459 | *pobject = nv_object(dmac); |
460 | if (ret) |
461 | return ret; |
462 | |
463 | nv_parent(dmac)->object_attach = nvd0_disp_dmac_object_attach; |
464 | nv_parent(dmac)->object_detach = nvd0_disp_dmac_object_detach; |
465 | return 0; |
466 | } |
467 | |
468 | struct nouveau_ofuncs |
469 | nvd0_disp_sync_ofuncs = { |
470 | .ctor = nvd0_disp_sync_ctor, |
471 | .dtor = nv50_disp_dmac_dtor, |
472 | .init = nvd0_disp_dmac_init, |
473 | .fini = nvd0_disp_dmac_fini, |
474 | .rd32 = nv50_disp_chan_rd32, |
475 | .wr32 = nv50_disp_chan_wr32, |
476 | }; |
477 | |
478 | /******************************************************************************* |
479 | * EVO overlay channel objects |
480 | ******************************************************************************/ |
481 | |
482 | static const struct nv50_disp_mthd_list |
483 | nvd0_disp_ovly_mthd_base = { |
484 | .mthd = 0x0000, |
485 | .data = { |
486 | { 0x0080, 0x665080 }, |
487 | { 0x0084, 0x665084 }, |
488 | { 0x0088, 0x665088 }, |
489 | { 0x008c, 0x66508c }, |
490 | { 0x0090, 0x665090 }, |
491 | { 0x0094, 0x665094 }, |
492 | { 0x00a0, 0x6650a0 }, |
493 | { 0x00a4, 0x6650a4 }, |
494 | { 0x00b0, 0x6650b0 }, |
495 | { 0x00b4, 0x6650b4 }, |
496 | { 0x00b8, 0x6650b8 }, |
497 | { 0x00c0, 0x6650c0 }, |
498 | { 0x00e0, 0x6650e0 }, |
499 | { 0x00e4, 0x6650e4 }, |
500 | { 0x00e8, 0x6650e8 }, |
501 | { 0x0100, 0x665100 }, |
502 | { 0x0104, 0x665104 }, |
503 | { 0x0108, 0x665108 }, |
504 | { 0x010c, 0x66510c }, |
505 | { 0x0110, 0x665110 }, |
506 | { 0x0118, 0x665118 }, |
507 | { 0x011c, 0x66511c }, |
508 | { 0x0120, 0x665120 }, |
509 | { 0x0124, 0x665124 }, |
510 | { 0x0130, 0x665130 }, |
511 | { 0x0134, 0x665134 }, |
512 | { 0x0138, 0x665138 }, |
513 | { 0x013c, 0x66513c }, |
514 | { 0x0140, 0x665140 }, |
515 | { 0x0144, 0x665144 }, |
516 | { 0x0148, 0x665148 }, |
517 | { 0x014c, 0x66514c }, |
518 | { 0x0150, 0x665150 }, |
519 | { 0x0154, 0x665154 }, |
520 | { 0x0158, 0x665158 }, |
521 | { 0x015c, 0x66515c }, |
522 | { 0x0160, 0x665160 }, |
523 | { 0x0164, 0x665164 }, |
524 | { 0x0168, 0x665168 }, |
525 | { 0x016c, 0x66516c }, |
526 | { 0x0400, 0x665400 }, |
527 | { 0x0408, 0x665408 }, |
528 | { 0x040c, 0x66540c }, |
529 | { 0x0410, 0x665410 }, |
530 | {} |
531 | } |
532 | }; |
533 | |
534 | static const struct nv50_disp_mthd_chan |
535 | nvd0_disp_ovly_mthd_chan = { |
536 | .name = "Overlay" , |
537 | .addr = 0x001000, |
538 | .data = { |
539 | { "Global" , 1, &nvd0_disp_ovly_mthd_base }, |
540 | {} |
541 | } |
542 | }; |
543 | |
544 | static int |
545 | nvd0_disp_ovly_ctor(struct nouveau_object *parent, |
546 | struct nouveau_object *engine, |
547 | struct nouveau_oclass *oclass, void *data, u32 size, |
548 | struct nouveau_object **pobject) |
549 | { |
550 | struct nv50_display_ovly_class *args = data; |
551 | struct nv50_disp_priv *priv = (void *)engine; |
552 | struct nv50_disp_dmac *dmac; |
553 | int ret; |
554 | |
555 | if (size < sizeof(*args) || args->head >= priv->head.nr) |
556 | return -EINVAL; |
557 | |
558 | ret = nv50_disp_dmac_create_(parent, engine, oclass, args->pushbuf, |
559 | 5 + args->head, sizeof(*dmac), |
560 | (void **)&dmac); |
561 | *pobject = nv_object(dmac); |
562 | if (ret) |
563 | return ret; |
564 | |
565 | nv_parent(dmac)->object_attach = nvd0_disp_dmac_object_attach; |
566 | nv_parent(dmac)->object_detach = nvd0_disp_dmac_object_detach; |
567 | return 0; |
568 | } |
569 | |
570 | struct nouveau_ofuncs |
571 | nvd0_disp_ovly_ofuncs = { |
572 | .ctor = nvd0_disp_ovly_ctor, |
573 | .dtor = nv50_disp_dmac_dtor, |
574 | .init = nvd0_disp_dmac_init, |
575 | .fini = nvd0_disp_dmac_fini, |
576 | .rd32 = nv50_disp_chan_rd32, |
577 | .wr32 = nv50_disp_chan_wr32, |
578 | }; |
579 | |
580 | /******************************************************************************* |
581 | * EVO PIO channel base class |
582 | ******************************************************************************/ |
583 | |
584 | static int |
585 | nvd0_disp_pioc_create_(struct nouveau_object *parent, |
586 | struct nouveau_object *engine, |
587 | struct nouveau_oclass *oclass, int chid, |
588 | int length, void **pobject) |
589 | { |
590 | return nv50_disp_chan_create_(parent, engine, oclass, chid, |
591 | length, pobject); |
592 | } |
593 | |
594 | static void |
595 | nvd0_disp_pioc_dtor(struct nouveau_object *object) |
596 | { |
597 | struct nv50_disp_pioc *pioc = (void *)object; |
598 | nv50_disp_chan_destroy(&pioc->base); |
599 | } |
600 | |
601 | static int |
602 | nvd0_disp_pioc_init(struct nouveau_object *object) |
603 | { |
604 | struct nv50_disp_priv *priv = (void *)object->engine; |
605 | struct nv50_disp_pioc *pioc = (void *)object; |
606 | int chid = pioc->base.chid; |
607 | int ret; |
608 | |
609 | ret = nv50_disp_chan_init(&pioc->base); |
610 | if (ret) |
611 | return ret; |
612 | |
613 | /* enable error reporting */ |
614 | nv_mask(priv, 0x610090, 0x00000001 << chid, 0x00000001 << chid); |
615 | nv_mask(priv, 0x6100a0, 0x00000001 << chid, 0x00000001 << chid); |
616 | |
617 | /* activate channel */ |
618 | nv_wr32(priv, 0x610490 + (chid * 0x10), 0x00000001); |
619 | if (!nv_wait(priv, 0x610490 + (chid * 0x10), 0x00030000, 0x00010000)) { |
620 | nv_error(pioc, "init: 0x%08x\n" , |
621 | nv_rd32(priv, 0x610490 + (chid * 0x10))); |
622 | return -EBUSY; |
623 | } |
624 | |
625 | return 0; |
626 | } |
627 | |
628 | static int |
629 | nvd0_disp_pioc_fini(struct nouveau_object *object, bool suspend) |
630 | { |
631 | struct nv50_disp_priv *priv = (void *)object->engine; |
632 | struct nv50_disp_pioc *pioc = (void *)object; |
633 | int chid = pioc->base.chid; |
634 | |
635 | nv_mask(priv, 0x610490 + (chid * 0x10), 0x00000001, 0x00000000); |
636 | if (!nv_wait(priv, 0x610490 + (chid * 0x10), 0x00030000, 0x00000000)) { |
637 | nv_error(pioc, "timeout: 0x%08x\n" , |
638 | nv_rd32(priv, 0x610490 + (chid * 0x10))); |
639 | if (suspend) |
640 | return -EBUSY; |
641 | } |
642 | |
643 | /* disable error reporting */ |
644 | nv_mask(priv, 0x610090, 0x00000001 << chid, 0x00000000); |
645 | nv_mask(priv, 0x6100a0, 0x00000001 << chid, 0x00000000); |
646 | |
647 | return nv50_disp_chan_fini(&pioc->base, suspend); |
648 | } |
649 | |
650 | /******************************************************************************* |
651 | * EVO immediate overlay channel objects |
652 | ******************************************************************************/ |
653 | |
654 | static int |
655 | nvd0_disp_oimm_ctor(struct nouveau_object *parent, |
656 | struct nouveau_object *engine, |
657 | struct nouveau_oclass *oclass, void *data, u32 size, |
658 | struct nouveau_object **pobject) |
659 | { |
660 | struct nv50_display_oimm_class *args = data; |
661 | struct nv50_disp_priv *priv = (void *)engine; |
662 | struct nv50_disp_pioc *pioc; |
663 | int ret; |
664 | |
665 | if (size < sizeof(*args) || args->head >= priv->head.nr) |
666 | return -EINVAL; |
667 | |
668 | ret = nvd0_disp_pioc_create_(parent, engine, oclass, 9 + args->head, |
669 | sizeof(*pioc), (void **)&pioc); |
670 | *pobject = nv_object(pioc); |
671 | if (ret) |
672 | return ret; |
673 | |
674 | return 0; |
675 | } |
676 | |
677 | struct nouveau_ofuncs |
678 | nvd0_disp_oimm_ofuncs = { |
679 | .ctor = nvd0_disp_oimm_ctor, |
680 | .dtor = nvd0_disp_pioc_dtor, |
681 | .init = nvd0_disp_pioc_init, |
682 | .fini = nvd0_disp_pioc_fini, |
683 | .rd32 = nv50_disp_chan_rd32, |
684 | .wr32 = nv50_disp_chan_wr32, |
685 | }; |
686 | |
687 | /******************************************************************************* |
688 | * EVO cursor channel objects |
689 | ******************************************************************************/ |
690 | |
691 | static int |
692 | nvd0_disp_curs_ctor(struct nouveau_object *parent, |
693 | struct nouveau_object *engine, |
694 | struct nouveau_oclass *oclass, void *data, u32 size, |
695 | struct nouveau_object **pobject) |
696 | { |
697 | struct nv50_display_curs_class *args = data; |
698 | struct nv50_disp_priv *priv = (void *)engine; |
699 | struct nv50_disp_pioc *pioc; |
700 | int ret; |
701 | |
702 | if (size < sizeof(*args) || args->head >= priv->head.nr) |
703 | return -EINVAL; |
704 | |
705 | ret = nvd0_disp_pioc_create_(parent, engine, oclass, 13 + args->head, |
706 | sizeof(*pioc), (void **)&pioc); |
707 | *pobject = nv_object(pioc); |
708 | if (ret) |
709 | return ret; |
710 | |
711 | return 0; |
712 | } |
713 | |
714 | struct nouveau_ofuncs |
715 | nvd0_disp_curs_ofuncs = { |
716 | .ctor = nvd0_disp_curs_ctor, |
717 | .dtor = nvd0_disp_pioc_dtor, |
718 | .init = nvd0_disp_pioc_init, |
719 | .fini = nvd0_disp_pioc_fini, |
720 | .rd32 = nv50_disp_chan_rd32, |
721 | .wr32 = nv50_disp_chan_wr32, |
722 | }; |
723 | |
724 | /******************************************************************************* |
725 | * Base display object |
726 | ******************************************************************************/ |
727 | |
728 | static int |
729 | nvd0_disp_base_scanoutpos(struct nouveau_object *object, u32 mthd, |
730 | void *data, u32 size) |
731 | { |
732 | struct nv50_disp_priv *priv = (void *)object->engine; |
733 | struct nv04_display_scanoutpos *args = data; |
734 | const int head = (mthd & NV50_DISP_MTHD_HEAD); |
735 | u32 blanke, blanks, total; |
736 | |
737 | if (size < sizeof(*args) || head >= priv->head.nr) |
738 | return -EINVAL; |
739 | |
740 | total = nv_rd32(priv, 0x640414 + (head * 0x300)); |
741 | blanke = nv_rd32(priv, 0x64041c + (head * 0x300)); |
742 | blanks = nv_rd32(priv, 0x640420 + (head * 0x300)); |
743 | |
744 | args->vblanke = (blanke & 0xffff0000) >> 16; |
745 | args->hblanke = (blanke & 0x0000ffff); |
746 | args->vblanks = (blanks & 0xffff0000) >> 16; |
747 | args->hblanks = (blanks & 0x0000ffff); |
748 | args->vtotal = ( total & 0xffff0000) >> 16; |
749 | args->htotal = ( total & 0x0000ffff); |
750 | |
751 | args->time[0] = ktime_to_ns(ktime_get()); |
752 | args->vline = nv_rd32(priv, 0x616340 + (head * 0x800)) & 0xffff; |
753 | args->time[1] = ktime_to_ns(ktime_get()); /* vline read locks hline */ |
754 | args->hline = nv_rd32(priv, 0x616344 + (head * 0x800)) & 0xffff; |
755 | return 0; |
756 | } |
757 | |
758 | static void |
759 | nvd0_disp_base_vblank_enable(struct nouveau_event *event, int head) |
760 | { |
761 | nv_mask(event->priv, 0x6100c0 + (head * 0x800), 0x00000001, 0x00000001); |
762 | } |
763 | |
764 | static void |
765 | nvd0_disp_base_vblank_disable(struct nouveau_event *event, int head) |
766 | { |
767 | nv_mask(event->priv, 0x6100c0 + (head * 0x800), 0x00000001, 0x00000000); |
768 | } |
769 | |
770 | static int |
771 | nvd0_disp_base_ctor(struct nouveau_object *parent, |
772 | struct nouveau_object *engine, |
773 | struct nouveau_oclass *oclass, void *data, u32 size, |
774 | struct nouveau_object **pobject) |
775 | { |
776 | struct nv50_disp_priv *priv = (void *)engine; |
777 | struct nv50_disp_base *base; |
778 | int ret; |
779 | |
780 | ret = nouveau_parent_create(parent, engine, oclass, 0, |
781 | priv->sclass, 0, &base); |
782 | *pobject = nv_object(base); |
783 | if (ret) |
784 | return ret; |
785 | |
786 | priv->base.vblank->priv = priv; |
787 | priv->base.vblank->enable = nvd0_disp_base_vblank_enable; |
788 | priv->base.vblank->disable = nvd0_disp_base_vblank_disable; |
789 | |
790 | return nouveau_ramht_new(nv_object(base), nv_object(base), 0x1000, 0, |
791 | &base->ramht); |
792 | } |
793 | |
794 | static void |
795 | nvd0_disp_base_dtor(struct nouveau_object *object) |
796 | { |
797 | struct nv50_disp_base *base = (void *)object; |
798 | nouveau_ramht_ref(NULL, &base->ramht); |
799 | nouveau_parent_destroy(&base->base); |
800 | } |
801 | |
802 | static int |
803 | nvd0_disp_base_init(struct nouveau_object *object) |
804 | { |
805 | struct nv50_disp_priv *priv = (void *)object->engine; |
806 | struct nv50_disp_base *base = (void *)object; |
807 | int ret, i; |
808 | u32 tmp; |
809 | |
810 | ret = nouveau_parent_init(&base->base); |
811 | if (ret) |
812 | return ret; |
813 | |
814 | /* The below segments of code copying values from one register to |
815 | * another appear to inform EVO of the display capabilities or |
816 | * something similar. |
817 | */ |
818 | |
819 | /* ... CRTC caps */ |
820 | for (i = 0; i < priv->head.nr; i++) { |
821 | tmp = nv_rd32(priv, 0x616104 + (i * 0x800)); |
822 | nv_wr32(priv, 0x6101b4 + (i * 0x800), tmp); |
823 | tmp = nv_rd32(priv, 0x616108 + (i * 0x800)); |
824 | nv_wr32(priv, 0x6101b8 + (i * 0x800), tmp); |
825 | tmp = nv_rd32(priv, 0x61610c + (i * 0x800)); |
826 | nv_wr32(priv, 0x6101bc + (i * 0x800), tmp); |
827 | } |
828 | |
829 | /* ... DAC caps */ |
830 | for (i = 0; i < priv->dac.nr; i++) { |
831 | tmp = nv_rd32(priv, 0x61a000 + (i * 0x800)); |
832 | nv_wr32(priv, 0x6101c0 + (i * 0x800), tmp); |
833 | } |
834 | |
835 | /* ... SOR caps */ |
836 | for (i = 0; i < priv->sor.nr; i++) { |
837 | tmp = nv_rd32(priv, 0x61c000 + (i * 0x800)); |
838 | nv_wr32(priv, 0x6301c4 + (i * 0x800), tmp); |
839 | } |
840 | |
841 | /* steal display away from vbios, or something like that */ |
842 | if (nv_rd32(priv, 0x6100ac) & 0x00000100) { |
843 | nv_wr32(priv, 0x6100ac, 0x00000100); |
844 | nv_mask(priv, 0x6194e8, 0x00000001, 0x00000000); |
845 | if (!nv_wait(priv, 0x6194e8, 0x00000002, 0x00000000)) { |
846 | nv_error(priv, "timeout acquiring display\n" ); |
847 | return -EBUSY; |
848 | } |
849 | } |
850 | |
851 | /* point at display engine memory area (hash table, objects) */ |
852 | nv_wr32(priv, 0x610010, (nv_gpuobj(object->parent)->addr >> 8) | 9); |
853 | |
854 | /* enable supervisor interrupts, disable everything else */ |
855 | nv_wr32(priv, 0x610090, 0x00000000); |
856 | nv_wr32(priv, 0x6100a0, 0x00000000); |
857 | nv_wr32(priv, 0x6100b0, 0x00000307); |
858 | |
859 | /* disable underflow reporting, preventing an intermittent issue |
860 | * on some nve4 boards where the production vbios left this |
861 | * setting enabled by default. |
862 | * |
863 | * ftp://download.nvidia.com/open-gpu-doc/gk104-disable-underflow-reporting/1/gk104-disable-underflow-reporting.txt |
864 | */ |
865 | for (i = 0; i < priv->head.nr; i++) |
866 | nv_mask(priv, 0x616308 + (i * 0x800), 0x00000111, 0x00000010); |
867 | |
868 | return 0; |
869 | } |
870 | |
871 | static int |
872 | nvd0_disp_base_fini(struct nouveau_object *object, bool suspend) |
873 | { |
874 | struct nv50_disp_priv *priv = (void *)object->engine; |
875 | struct nv50_disp_base *base = (void *)object; |
876 | |
877 | /* disable all interrupts */ |
878 | nv_wr32(priv, 0x6100b0, 0x00000000); |
879 | |
880 | return nouveau_parent_fini(&base->base, suspend); |
881 | } |
882 | |
883 | struct nouveau_ofuncs |
884 | nvd0_disp_base_ofuncs = { |
885 | .ctor = nvd0_disp_base_ctor, |
886 | .dtor = nvd0_disp_base_dtor, |
887 | .init = nvd0_disp_base_init, |
888 | .fini = nvd0_disp_base_fini, |
889 | }; |
890 | |
891 | struct nouveau_omthds |
892 | nvd0_disp_base_omthds[] = { |
893 | { HEAD_MTHD(NV50_DISP_SCANOUTPOS) , nvd0_disp_base_scanoutpos }, |
894 | { SOR_MTHD(NV50_DISP_SOR_PWR) , nv50_sor_mthd }, |
895 | { SOR_MTHD(NVA3_DISP_SOR_HDA_ELD) , nv50_sor_mthd }, |
896 | { SOR_MTHD(NV84_DISP_SOR_HDMI_PWR) , nv50_sor_mthd }, |
897 | { SOR_MTHD(NV50_DISP_SOR_LVDS_SCRIPT) , nv50_sor_mthd }, |
898 | { DAC_MTHD(NV50_DISP_DAC_PWR) , nv50_dac_mthd }, |
899 | { DAC_MTHD(NV50_DISP_DAC_LOAD) , nv50_dac_mthd }, |
900 | { PIOR_MTHD(NV50_DISP_PIOR_PWR) , nv50_pior_mthd }, |
901 | { PIOR_MTHD(NV50_DISP_PIOR_TMDS_PWR) , nv50_pior_mthd }, |
902 | { PIOR_MTHD(NV50_DISP_PIOR_DP_PWR) , nv50_pior_mthd }, |
903 | {}, |
904 | }; |
905 | |
906 | static struct nouveau_oclass |
907 | nvd0_disp_base_oclass[] = { |
908 | { NVD0_DISP_CLASS, &nvd0_disp_base_ofuncs, nvd0_disp_base_omthds }, |
909 | {} |
910 | }; |
911 | |
912 | static struct nouveau_oclass |
913 | nvd0_disp_sclass[] = { |
914 | { NVD0_DISP_MAST_CLASS, &nvd0_disp_mast_ofuncs }, |
915 | { NVD0_DISP_SYNC_CLASS, &nvd0_disp_sync_ofuncs }, |
916 | { NVD0_DISP_OVLY_CLASS, &nvd0_disp_ovly_ofuncs }, |
917 | { NVD0_DISP_OIMM_CLASS, &nvd0_disp_oimm_ofuncs }, |
918 | { NVD0_DISP_CURS_CLASS, &nvd0_disp_curs_ofuncs }, |
919 | {} |
920 | }; |
921 | |
922 | /******************************************************************************* |
923 | * Display engine implementation |
924 | ******************************************************************************/ |
925 | |
926 | static u16 |
927 | exec_lookup(struct nv50_disp_priv *priv, int head, int outp, u32 ctrl, |
928 | struct dcb_output *dcb, u8 *ver, u8 *hdr, u8 *cnt, u8 *len, |
929 | struct nvbios_outp *info) |
930 | { |
931 | struct nouveau_bios *bios = nouveau_bios(priv); |
932 | u16 mask, type, data; |
933 | |
934 | if (outp < 4) { |
935 | type = DCB_OUTPUT_ANALOG; |
936 | mask = 0; |
937 | } else { |
938 | outp -= 4; |
939 | switch (ctrl & 0x00000f00) { |
940 | case 0x00000000: type = DCB_OUTPUT_LVDS; mask = 1; break; |
941 | case 0x00000100: type = DCB_OUTPUT_TMDS; mask = 1; break; |
942 | case 0x00000200: type = DCB_OUTPUT_TMDS; mask = 2; break; |
943 | case 0x00000500: type = DCB_OUTPUT_TMDS; mask = 3; break; |
944 | case 0x00000800: type = DCB_OUTPUT_DP; mask = 1; break; |
945 | case 0x00000900: type = DCB_OUTPUT_DP; mask = 2; break; |
946 | default: |
947 | nv_error(priv, "unknown SOR mc 0x%08x\n" , ctrl); |
948 | return 0x0000; |
949 | } |
950 | dcb->sorconf.link = mask; |
951 | } |
952 | |
953 | mask = 0x00c0 & (mask << 6); |
954 | mask |= 0x0001 << outp; |
955 | mask |= 0x0100 << head; |
956 | |
957 | data = dcb_outp_match(bios, type, mask, ver, hdr, dcb); |
958 | if (!data) |
959 | return 0x0000; |
960 | |
961 | return nvbios_outp_match(bios, type, mask, ver, hdr, cnt, len, info); |
962 | } |
963 | |
964 | static bool |
965 | exec_script(struct nv50_disp_priv *priv, int head, int id) |
966 | { |
967 | struct nouveau_bios *bios = nouveau_bios(priv); |
968 | struct nvbios_outp info; |
969 | struct dcb_output dcb; |
970 | u8 ver, hdr, cnt, len; |
971 | u32 ctrl = 0x00000000; |
972 | u16 data; |
973 | int outp; |
974 | |
975 | for (outp = 0; !(ctrl & (1 << head)) && outp < 8; outp++) { |
976 | ctrl = nv_rd32(priv, 0x640180 + (outp * 0x20)); |
977 | if (ctrl & (1 << head)) |
978 | break; |
979 | } |
980 | |
981 | if (outp == 8) |
982 | return false; |
983 | |
984 | data = exec_lookup(priv, head, outp, ctrl, &dcb, &ver, &hdr, &cnt, &len, &info); |
985 | if (data) { |
986 | struct nvbios_init init = { |
987 | .subdev = nv_subdev(priv), |
988 | .bios = bios, |
989 | .offset = info.script[id], |
990 | .outp = &dcb, |
991 | .crtc = head, |
992 | .execute = 1, |
993 | }; |
994 | |
995 | return nvbios_exec(&init) == 0; |
996 | } |
997 | |
998 | return false; |
999 | } |
1000 | |
1001 | static u32 |
1002 | exec_clkcmp(struct nv50_disp_priv *priv, int head, int id, |
1003 | u32 pclk, struct dcb_output *dcb) |
1004 | { |
1005 | struct nouveau_bios *bios = nouveau_bios(priv); |
1006 | struct nvbios_outp info1; |
1007 | struct nvbios_ocfg info2; |
1008 | u8 ver, hdr, cnt, len; |
1009 | u32 ctrl = 0x00000000; |
1010 | u32 data, conf = ~0; |
1011 | int outp; |
1012 | |
1013 | for (outp = 0; !(ctrl & (1 << head)) && outp < 8; outp++) { |
1014 | ctrl = nv_rd32(priv, 0x660180 + (outp * 0x20)); |
1015 | if (ctrl & (1 << head)) |
1016 | break; |
1017 | } |
1018 | |
1019 | if (outp == 8) |
1020 | return conf; |
1021 | |
1022 | data = exec_lookup(priv, head, outp, ctrl, dcb, &ver, &hdr, &cnt, &len, &info1); |
1023 | if (data == 0x0000) |
1024 | return conf; |
1025 | |
1026 | switch (dcb->type) { |
1027 | case DCB_OUTPUT_TMDS: |
1028 | conf = (ctrl & 0x00000f00) >> 8; |
1029 | if (pclk >= 165000) |
1030 | conf |= 0x0100; |
1031 | break; |
1032 | case DCB_OUTPUT_LVDS: |
1033 | conf = priv->sor.lvdsconf; |
1034 | break; |
1035 | case DCB_OUTPUT_DP: |
1036 | conf = (ctrl & 0x00000f00) >> 8; |
1037 | break; |
1038 | case DCB_OUTPUT_ANALOG: |
1039 | default: |
1040 | conf = 0x00ff; |
1041 | break; |
1042 | } |
1043 | |
1044 | data = nvbios_ocfg_match(bios, data, conf, &ver, &hdr, &cnt, &len, &info2); |
1045 | CTASSERT(__arraycount(info2.clkcmp) <= 0xff); |
1046 | if (data && id < __arraycount(info2.clkcmp)) { |
1047 | data = nvbios_oclk_match(bios, info2.clkcmp[id], pclk); |
1048 | if (data) { |
1049 | struct nvbios_init init = { |
1050 | .subdev = nv_subdev(priv), |
1051 | .bios = bios, |
1052 | .offset = data, |
1053 | .outp = dcb, |
1054 | .crtc = head, |
1055 | .execute = 1, |
1056 | }; |
1057 | |
1058 | nvbios_exec(&init); |
1059 | } |
1060 | } |
1061 | |
1062 | return conf; |
1063 | } |
1064 | |
1065 | static void |
1066 | nvd0_disp_intr_unk1_0(struct nv50_disp_priv *priv, int head) |
1067 | { |
1068 | exec_script(priv, head, 1); |
1069 | } |
1070 | |
1071 | static void |
1072 | nvd0_disp_intr_unk2_0(struct nv50_disp_priv *priv, int head) |
1073 | { |
1074 | exec_script(priv, head, 2); |
1075 | } |
1076 | |
1077 | static void |
1078 | nvd0_disp_intr_unk2_1(struct nv50_disp_priv *priv, int head) |
1079 | { |
1080 | struct nouveau_devinit *devinit = nouveau_devinit(priv); |
1081 | u32 pclk = nv_rd32(priv, 0x660450 + (head * 0x300)) / 1000; |
1082 | if (pclk) |
1083 | devinit->pll_set(devinit, PLL_VPLL0 + head, pclk); |
1084 | nv_wr32(priv, 0x612200 + (head * 0x800), 0x00000000); |
1085 | } |
1086 | |
1087 | static void |
1088 | nvd0_disp_intr_unk2_2_tu(struct nv50_disp_priv *priv, int head, |
1089 | struct dcb_output *outp) |
1090 | { |
1091 | const int or = ffs(outp->or) - 1; |
1092 | const u32 ctrl = nv_rd32(priv, 0x660200 + (or * 0x020)); |
1093 | const u32 conf = nv_rd32(priv, 0x660404 + (head * 0x300)); |
1094 | const u32 pclk = nv_rd32(priv, 0x660450 + (head * 0x300)) / 1000; |
1095 | const u32 link = ((ctrl & 0xf00) == 0x800) ? 0 : 1; |
1096 | const u32 hoff = (head * 0x800); |
1097 | const u32 soff = ( or * 0x800); |
1098 | const u32 loff = (link * 0x080) + soff; |
1099 | const u32 symbol = 100000; |
1100 | const u32 TU = 64; |
1101 | u32 dpctrl = nv_rd32(priv, 0x61c10c + loff) & 0x000f0000; |
1102 | u32 clksor = nv_rd32(priv, 0x612300 + soff); |
1103 | u32 datarate, link_nr, link_bw, bits; |
1104 | u64 ratio, value; |
1105 | |
1106 | if ((conf & 0x3c0) == 0x180) bits = 30; |
1107 | else if ((conf & 0x3c0) == 0x140) bits = 24; |
1108 | else bits = 18; |
1109 | datarate = (pclk * bits) / 8; |
1110 | |
1111 | if (dpctrl > 0x00030000) link_nr = 4; |
1112 | else if (dpctrl > 0x00010000) link_nr = 2; |
1113 | else link_nr = 1; |
1114 | |
1115 | link_bw = (clksor & 0x007c0000) >> 18; |
1116 | link_bw *= 27000; |
1117 | |
1118 | ratio = datarate; |
1119 | ratio *= symbol; |
1120 | do_div(ratio, link_nr * link_bw); |
1121 | |
1122 | value = (symbol - ratio) * TU; |
1123 | value *= ratio; |
1124 | do_div(value, symbol); |
1125 | do_div(value, symbol); |
1126 | |
1127 | value += 5; |
1128 | value |= 0x08000000; |
1129 | |
1130 | nv_wr32(priv, 0x616610 + hoff, value); |
1131 | } |
1132 | |
1133 | static void |
1134 | nvd0_disp_intr_unk2_2(struct nv50_disp_priv *priv, int head) |
1135 | { |
1136 | struct dcb_output outp; |
1137 | u32 pclk = nv_rd32(priv, 0x660450 + (head * 0x300)) / 1000; |
1138 | u32 conf = exec_clkcmp(priv, head, 0xff, pclk, &outp); |
1139 | if (conf != ~0) { |
1140 | u32 addr, data; |
1141 | |
1142 | if (outp.type == DCB_OUTPUT_DP) { |
1143 | u32 sync = nv_rd32(priv, 0x660404 + (head * 0x300)); |
1144 | switch ((sync & 0x000003c0) >> 6) { |
1145 | case 6: pclk = pclk * 30 / 8; break; |
1146 | case 5: pclk = pclk * 24 / 8; break; |
1147 | case 2: |
1148 | default: |
1149 | pclk = pclk * 18 / 8; |
1150 | break; |
1151 | } |
1152 | |
1153 | nouveau_dp_train(&priv->base, priv->sor.dp, |
1154 | &outp, head, pclk); |
1155 | } |
1156 | |
1157 | exec_clkcmp(priv, head, 0, pclk, &outp); |
1158 | |
1159 | if (outp.type == DCB_OUTPUT_ANALOG) { |
1160 | addr = 0x612280 + (ffs(outp.or) - 1) * 0x800; |
1161 | data = 0x00000000; |
1162 | } else { |
1163 | if (outp.type == DCB_OUTPUT_DP) |
1164 | nvd0_disp_intr_unk2_2_tu(priv, head, &outp); |
1165 | addr = 0x612300 + (ffs(outp.or) - 1) * 0x800; |
1166 | data = (conf & 0x0100) ? 0x00000101 : 0x00000000; |
1167 | } |
1168 | |
1169 | nv_mask(priv, addr, 0x00000707, data); |
1170 | } |
1171 | } |
1172 | |
1173 | static void |
1174 | nvd0_disp_intr_unk4_0(struct nv50_disp_priv *priv, int head) |
1175 | { |
1176 | struct dcb_output outp; |
1177 | u32 pclk = nv_rd32(priv, 0x660450 + (head * 0x300)) / 1000; |
1178 | exec_clkcmp(priv, head, 1, pclk, &outp); |
1179 | } |
1180 | |
1181 | void |
1182 | nvd0_disp_intr_supervisor(struct work_struct *work) |
1183 | { |
1184 | struct nv50_disp_priv *priv = |
1185 | container_of(work, struct nv50_disp_priv, supervisor); |
1186 | struct nv50_disp_impl *impl = (void *)nv_object(priv)->oclass; |
1187 | u32 mask[4]; |
1188 | int head; |
1189 | |
1190 | nv_debug(priv, "supervisor %d\n" , ffs(priv->super)); |
1191 | for (head = 0; head < priv->head.nr; head++) { |
1192 | mask[head] = nv_rd32(priv, 0x6101d4 + (head * 0x800)); |
1193 | nv_debug(priv, "head %d: 0x%08x\n" , head, mask[head]); |
1194 | } |
1195 | |
1196 | if (priv->super & 0x00000001) { |
1197 | nv50_disp_mthd_chan(priv, NV_DBG_DEBUG, 0, impl->mthd.core); |
1198 | for (head = 0; head < priv->head.nr; head++) { |
1199 | if (!(mask[head] & 0x00001000)) |
1200 | continue; |
1201 | nv_debug(priv, "supervisor 1.0 - head %d\n" , head); |
1202 | nvd0_disp_intr_unk1_0(priv, head); |
1203 | } |
1204 | } else |
1205 | if (priv->super & 0x00000002) { |
1206 | for (head = 0; head < priv->head.nr; head++) { |
1207 | if (!(mask[head] & 0x00001000)) |
1208 | continue; |
1209 | nv_debug(priv, "supervisor 2.0 - head %d\n" , head); |
1210 | nvd0_disp_intr_unk2_0(priv, head); |
1211 | } |
1212 | for (head = 0; head < priv->head.nr; head++) { |
1213 | if (!(mask[head] & 0x00010000)) |
1214 | continue; |
1215 | nv_debug(priv, "supervisor 2.1 - head %d\n" , head); |
1216 | nvd0_disp_intr_unk2_1(priv, head); |
1217 | } |
1218 | for (head = 0; head < priv->head.nr; head++) { |
1219 | if (!(mask[head] & 0x00001000)) |
1220 | continue; |
1221 | nv_debug(priv, "supervisor 2.2 - head %d\n" , head); |
1222 | nvd0_disp_intr_unk2_2(priv, head); |
1223 | } |
1224 | } else |
1225 | if (priv->super & 0x00000004) { |
1226 | for (head = 0; head < priv->head.nr; head++) { |
1227 | if (!(mask[head] & 0x00001000)) |
1228 | continue; |
1229 | nv_debug(priv, "supervisor 3.0 - head %d\n" , head); |
1230 | nvd0_disp_intr_unk4_0(priv, head); |
1231 | } |
1232 | } |
1233 | |
1234 | for (head = 0; head < priv->head.nr; head++) |
1235 | nv_wr32(priv, 0x6101d4 + (head * 0x800), 0x00000000); |
1236 | nv_wr32(priv, 0x6101d0, 0x80000000); |
1237 | } |
1238 | |
1239 | static void |
1240 | nvd0_disp_intr_error(struct nv50_disp_priv *priv, int chid) |
1241 | { |
1242 | const struct nv50_disp_impl *impl = (void *)nv_object(priv)->oclass; |
1243 | u32 mthd = nv_rd32(priv, 0x6101f0 + (chid * 12)); |
1244 | u32 data = nv_rd32(priv, 0x6101f4 + (chid * 12)); |
1245 | u32 unkn = nv_rd32(priv, 0x6101f8 + (chid * 12)); |
1246 | |
1247 | nv_error(priv, "chid %d mthd 0x%04x data 0x%08x " |
1248 | "0x%08x 0x%08x\n" , |
1249 | chid, (mthd & 0x0000ffc), data, mthd, unkn); |
1250 | |
1251 | if (chid == 0) { |
1252 | switch (mthd) { |
1253 | case 0x0080: |
1254 | nv50_disp_mthd_chan(priv, NV_DBG_ERROR, chid - 0, |
1255 | impl->mthd.core); |
1256 | break; |
1257 | default: |
1258 | break; |
1259 | } |
1260 | } else |
1261 | if (chid <= 4) { |
1262 | switch (mthd) { |
1263 | case 0x0080: |
1264 | nv50_disp_mthd_chan(priv, NV_DBG_ERROR, chid - 1, |
1265 | impl->mthd.base); |
1266 | break; |
1267 | default: |
1268 | break; |
1269 | } |
1270 | } else |
1271 | if (chid <= 8) { |
1272 | switch (mthd) { |
1273 | case 0x0080: |
1274 | nv50_disp_mthd_chan(priv, NV_DBG_ERROR, chid - 5, |
1275 | impl->mthd.ovly); |
1276 | break; |
1277 | default: |
1278 | break; |
1279 | } |
1280 | } |
1281 | |
1282 | nv_wr32(priv, 0x61009c, (1 << chid)); |
1283 | nv_wr32(priv, 0x6101f0 + (chid * 12), 0x90000000); |
1284 | } |
1285 | |
1286 | void |
1287 | nvd0_disp_intr(struct nouveau_subdev *subdev) |
1288 | { |
1289 | struct nv50_disp_priv *priv = (void *)subdev; |
1290 | u32 intr = nv_rd32(priv, 0x610088); |
1291 | int i; |
1292 | |
1293 | if (intr & 0x00000001) { |
1294 | u32 stat = nv_rd32(priv, 0x61008c); |
1295 | nv_wr32(priv, 0x61008c, stat); |
1296 | intr &= ~0x00000001; |
1297 | } |
1298 | |
1299 | if (intr & 0x00000002) { |
1300 | u32 stat = nv_rd32(priv, 0x61009c); |
1301 | int chid = ffs(stat) - 1; |
1302 | if (chid >= 0) |
1303 | nvd0_disp_intr_error(priv, chid); |
1304 | intr &= ~0x00000002; |
1305 | } |
1306 | |
1307 | if (intr & 0x00100000) { |
1308 | u32 stat = nv_rd32(priv, 0x6100ac); |
1309 | if (stat & 0x00000007) { |
1310 | priv->super = (stat & 0x00000007); |
1311 | schedule_work(&priv->supervisor); |
1312 | nv_wr32(priv, 0x6100ac, priv->super); |
1313 | stat &= ~0x00000007; |
1314 | } |
1315 | |
1316 | if (stat) { |
1317 | nv_info(priv, "unknown intr24 0x%08x\n" , stat); |
1318 | nv_wr32(priv, 0x6100ac, stat); |
1319 | } |
1320 | |
1321 | intr &= ~0x00100000; |
1322 | } |
1323 | |
1324 | for (i = 0; i < priv->head.nr; i++) { |
1325 | u32 mask = 0x01000000 << i; |
1326 | if (mask & intr) { |
1327 | u32 stat = nv_rd32(priv, 0x6100bc + (i * 0x800)); |
1328 | if (stat & 0x00000001) |
1329 | nouveau_event_trigger(priv->base.vblank, i); |
1330 | nv_mask(priv, 0x6100bc + (i * 0x800), 0, 0); |
1331 | nv_rd32(priv, 0x6100c0 + (i * 0x800)); |
1332 | } |
1333 | } |
1334 | } |
1335 | |
1336 | static int |
1337 | nvd0_disp_ctor(struct nouveau_object *parent, struct nouveau_object *engine, |
1338 | struct nouveau_oclass *oclass, void *data, u32 size, |
1339 | struct nouveau_object **pobject) |
1340 | { |
1341 | struct nv50_disp_priv *priv; |
1342 | int heads = nv_rd32(parent, 0x022448); |
1343 | int ret; |
1344 | |
1345 | ret = nouveau_disp_create(parent, engine, oclass, heads, |
1346 | "PDISP" , "display" , &priv); |
1347 | *pobject = nv_object(priv); |
1348 | if (ret) |
1349 | return ret; |
1350 | |
1351 | nv_engine(priv)->sclass = nvd0_disp_base_oclass; |
1352 | nv_engine(priv)->cclass = &nv50_disp_cclass; |
1353 | nv_subdev(priv)->intr = nvd0_disp_intr; |
1354 | INIT_WORK(&priv->supervisor, nvd0_disp_intr_supervisor); |
1355 | priv->sclass = nvd0_disp_sclass; |
1356 | priv->head.nr = heads; |
1357 | priv->dac.nr = 3; |
1358 | priv->sor.nr = 4; |
1359 | priv->dac.power = nv50_dac_power; |
1360 | priv->dac.sense = nv50_dac_sense; |
1361 | priv->sor.power = nv50_sor_power; |
1362 | priv->sor.hda_eld = nvd0_hda_eld; |
1363 | priv->sor.hdmi = nvd0_hdmi_ctrl; |
1364 | priv->sor.dp = &nvd0_sor_dp_func; |
1365 | return 0; |
1366 | } |
1367 | |
1368 | struct nouveau_oclass * |
1369 | nvd0_disp_oclass = &(struct nv50_disp_impl) { |
1370 | .base.base.handle = NV_ENGINE(DISP, 0x90), |
1371 | .base.base.ofuncs = &(struct nouveau_ofuncs) { |
1372 | .ctor = nvd0_disp_ctor, |
1373 | .dtor = _nouveau_disp_dtor, |
1374 | .init = _nouveau_disp_init, |
1375 | .fini = _nouveau_disp_fini, |
1376 | }, |
1377 | .mthd.core = &nvd0_disp_mast_mthd_chan, |
1378 | .mthd.base = &nvd0_disp_sync_mthd_chan, |
1379 | .mthd.ovly = &nvd0_disp_ovly_mthd_chan, |
1380 | .mthd.prev = -0x020000, |
1381 | }.base.base; |
1382 | |