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