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 | */ | |
878da15a BS |
24 | #include "nv50.h" |
25 | #include "outp.h" | |
26 | #include "outpdp.h" | |
ebb945a9 | 27 | |
bf0eb898 | 28 | #include <core/client.h> |
a1e88736 | 29 | #include <core/gpuobj.h> |
117e1633 | 30 | #include <core/enum.h> |
878da15a BS |
31 | #include <core/handle.h> |
32 | #include <core/ramht.h> | |
33 | #include <engine/dmaobj.h> | |
186ecad2 BS |
34 | #include <subdev/bios.h> |
35 | #include <subdev/bios/dcb.h> | |
36 | #include <subdev/bios/disp.h> | |
37 | #include <subdev/bios/init.h> | |
38 | #include <subdev/bios/pll.h> | |
88524bc0 | 39 | #include <subdev/devinit.h> |
370c00f9 | 40 | #include <subdev/fb.h> |
878da15a | 41 | #include <subdev/timer.h> |
446b05a0 | 42 | |
878da15a BS |
43 | #include <nvif/class.h> |
44 | #include <nvif/event.h> | |
45 | #include <nvif/unpack.h> | |
70cabe4a BS |
46 | |
47 | /******************************************************************************* | |
370c00f9 | 48 | * EVO channel base class |
70cabe4a BS |
49 | ******************************************************************************/ |
50 | ||
2c04ae01 | 51 | static int |
878da15a BS |
52 | nv50_disp_chan_create_(struct nvkm_object *parent, |
53 | struct nvkm_object *engine, | |
54 | struct nvkm_oclass *oclass, int head, | |
370c00f9 BS |
55 | int length, void **pobject) |
56 | { | |
2c04ae01 | 57 | const struct nv50_disp_chan_impl *impl = (void *)oclass->ofuncs; |
370c00f9 BS |
58 | struct nv50_disp_base *base = (void *)parent; |
59 | struct nv50_disp_chan *chan; | |
2c04ae01 | 60 | int chid = impl->chid + head; |
370c00f9 BS |
61 | int ret; |
62 | ||
63 | if (base->chan & (1 << chid)) | |
64 | return -EBUSY; | |
65 | base->chan |= (1 << chid); | |
66 | ||
878da15a BS |
67 | ret = nvkm_namedb_create_(parent, engine, oclass, 0, NULL, |
68 | (1ULL << NVDEV_ENGINE_DMAOBJ), | |
69 | length, pobject); | |
370c00f9 BS |
70 | chan = *pobject; |
71 | if (ret) | |
72 | return ret; | |
370c00f9 | 73 | chan->chid = chid; |
2c04ae01 BS |
74 | |
75 | nv_parent(chan)->object_attach = impl->attach; | |
76 | nv_parent(chan)->object_detach = impl->detach; | |
370c00f9 BS |
77 | return 0; |
78 | } | |
79 | ||
2c04ae01 | 80 | static void |
370c00f9 BS |
81 | nv50_disp_chan_destroy(struct nv50_disp_chan *chan) |
82 | { | |
83 | struct nv50_disp_base *base = (void *)nv_object(chan)->parent; | |
84 | base->chan &= ~(1 << chan->chid); | |
878da15a | 85 | nvkm_namedb_destroy(&chan->base); |
370c00f9 BS |
86 | } |
87 | ||
b38a2322 BS |
88 | static void |
89 | nv50_disp_chan_uevent_fini(struct nvkm_event *event, int type, int index) | |
90 | { | |
fd166a18 | 91 | struct nv50_disp *disp = container_of(event, typeof(*disp), uevent); |
2fde1f1c BS |
92 | struct nvkm_device *device = disp->base.engine.subdev.device; |
93 | nvkm_mask(device, 0x610028, 0x00000001 << index, 0x00000000 << index); | |
94 | nvkm_wr32(device, 0x610020, 0x00000001 << index); | |
b38a2322 BS |
95 | } |
96 | ||
97 | static void | |
98 | nv50_disp_chan_uevent_init(struct nvkm_event *event, int types, int index) | |
99 | { | |
fd166a18 | 100 | struct nv50_disp *disp = container_of(event, typeof(*disp), uevent); |
2fde1f1c BS |
101 | struct nvkm_device *device = disp->base.engine.subdev.device; |
102 | nvkm_wr32(device, 0x610020, 0x00000001 << index); | |
103 | nvkm_mask(device, 0x610028, 0x00000001 << index, 0x00000001 << index); | |
b38a2322 BS |
104 | } |
105 | ||
106 | void | |
fd166a18 | 107 | nv50_disp_chan_uevent_send(struct nv50_disp *disp, int chid) |
b38a2322 BS |
108 | { |
109 | struct nvif_notify_uevent_rep { | |
110 | } rep; | |
111 | ||
fd166a18 | 112 | nvkm_event_send(&disp->uevent, 1, chid, &rep, sizeof(rep)); |
b38a2322 BS |
113 | } |
114 | ||
115 | int | |
878da15a | 116 | nv50_disp_chan_uevent_ctor(struct nvkm_object *object, void *data, u32 size, |
b38a2322 BS |
117 | struct nvkm_notify *notify) |
118 | { | |
119 | struct nv50_disp_dmac *dmac = (void *)object; | |
120 | union { | |
121 | struct nvif_notify_uevent_req none; | |
122 | } *args = data; | |
123 | int ret; | |
124 | ||
125 | if (nvif_unvers(args->none)) { | |
126 | notify->size = sizeof(struct nvif_notify_uevent_rep); | |
127 | notify->types = 1; | |
128 | notify->index = dmac->base.chid; | |
129 | return 0; | |
130 | } | |
131 | ||
132 | return ret; | |
133 | } | |
134 | ||
135 | const struct nvkm_event_func | |
136 | nv50_disp_chan_uevent = { | |
137 | .ctor = nv50_disp_chan_uevent_ctor, | |
138 | .init = nv50_disp_chan_uevent_init, | |
139 | .fini = nv50_disp_chan_uevent_fini, | |
140 | }; | |
141 | ||
142 | int | |
878da15a | 143 | nv50_disp_chan_ntfy(struct nvkm_object *object, u32 type, |
b38a2322 BS |
144 | struct nvkm_event **pevent) |
145 | { | |
fd166a18 | 146 | struct nv50_disp *disp = (void *)object->engine; |
b38a2322 BS |
147 | switch (type) { |
148 | case NV50_DISP_CORE_CHANNEL_DMA_V0_NTFY_UEVENT: | |
fd166a18 | 149 | *pevent = &disp->uevent; |
b38a2322 BS |
150 | return 0; |
151 | default: | |
152 | break; | |
153 | } | |
154 | return -EINVAL; | |
155 | } | |
156 | ||
b76f1529 | 157 | int |
878da15a | 158 | nv50_disp_chan_map(struct nvkm_object *object, u64 *addr, u32 *size) |
b76f1529 BS |
159 | { |
160 | struct nv50_disp_chan *chan = (void *)object; | |
161 | *addr = nv_device_resource_start(nv_device(object), 0) + | |
162 | 0x640000 + (chan->chid * 0x1000); | |
163 | *size = 0x001000; | |
164 | return 0; | |
165 | } | |
166 | ||
370c00f9 | 167 | u32 |
878da15a | 168 | nv50_disp_chan_rd32(struct nvkm_object *object, u64 addr) |
70cabe4a | 169 | { |
370c00f9 | 170 | struct nv50_disp_chan *chan = (void *)object; |
2fde1f1c BS |
171 | struct nvkm_device *device = object->engine->subdev.device; |
172 | return nvkm_rd32(device, 0x640000 + (chan->chid * 0x1000) + addr); | |
70cabe4a BS |
173 | } |
174 | ||
370c00f9 | 175 | void |
878da15a | 176 | nv50_disp_chan_wr32(struct nvkm_object *object, u64 addr, u32 data) |
70cabe4a | 177 | { |
370c00f9 | 178 | struct nv50_disp_chan *chan = (void *)object; |
2fde1f1c BS |
179 | struct nvkm_device *device = object->engine->subdev.device; |
180 | nvkm_wr32(device, 0x640000 + (chan->chid * 0x1000) + addr, data); | |
370c00f9 BS |
181 | } |
182 | ||
183 | /******************************************************************************* | |
184 | * EVO DMA channel base class | |
185 | ******************************************************************************/ | |
186 | ||
187 | static int | |
878da15a BS |
188 | nv50_disp_dmac_object_attach(struct nvkm_object *parent, |
189 | struct nvkm_object *object, u32 name) | |
370c00f9 BS |
190 | { |
191 | struct nv50_disp_base *base = (void *)parent->parent; | |
192 | struct nv50_disp_chan *chan = (void *)parent; | |
193 | u32 addr = nv_gpuobj(object)->node->offset; | |
194 | u32 chid = chan->chid; | |
195 | u32 data = (chid << 28) | (addr << 10) | chid; | |
878da15a | 196 | return nvkm_ramht_insert(base->ramht, chid, name, data); |
370c00f9 BS |
197 | } |
198 | ||
199 | static void | |
878da15a | 200 | nv50_disp_dmac_object_detach(struct nvkm_object *parent, int cookie) |
370c00f9 BS |
201 | { |
202 | struct nv50_disp_base *base = (void *)parent->parent; | |
878da15a | 203 | nvkm_ramht_remove(base->ramht, cookie); |
370c00f9 BS |
204 | } |
205 | ||
2c04ae01 | 206 | static int |
878da15a BS |
207 | nv50_disp_dmac_create_(struct nvkm_object *parent, |
208 | struct nvkm_object *engine, | |
209 | struct nvkm_oclass *oclass, u32 pushbuf, int head, | |
370c00f9 BS |
210 | int length, void **pobject) |
211 | { | |
212 | struct nv50_disp_dmac *dmac; | |
213 | int ret; | |
214 | ||
2c04ae01 | 215 | ret = nv50_disp_chan_create_(parent, engine, oclass, head, |
370c00f9 BS |
216 | length, pobject); |
217 | dmac = *pobject; | |
218 | if (ret) | |
219 | return ret; | |
220 | ||
878da15a | 221 | dmac->pushdma = (void *)nvkm_handle_ref(parent, pushbuf); |
370c00f9 BS |
222 | if (!dmac->pushdma) |
223 | return -ENOENT; | |
224 | ||
225 | switch (nv_mclass(dmac->pushdma)) { | |
226 | case 0x0002: | |
227 | case 0x003d: | |
228 | if (dmac->pushdma->limit - dmac->pushdma->start != 0xfff) | |
229 | return -EINVAL; | |
230 | ||
231 | switch (dmac->pushdma->target) { | |
232 | case NV_MEM_TARGET_VRAM: | |
963e9650 | 233 | dmac->push = 0x00000001 | dmac->pushdma->start >> 8; |
370c00f9 | 234 | break; |
944234d6 BS |
235 | case NV_MEM_TARGET_PCI_NOSNOOP: |
236 | dmac->push = 0x00000003 | dmac->pushdma->start >> 8; | |
237 | break; | |
370c00f9 BS |
238 | default: |
239 | return -EINVAL; | |
240 | } | |
241 | break; | |
242 | default: | |
243 | return -EINVAL; | |
244 | } | |
245 | ||
246 | return 0; | |
247 | } | |
248 | ||
249 | void | |
878da15a | 250 | nv50_disp_dmac_dtor(struct nvkm_object *object) |
370c00f9 BS |
251 | { |
252 | struct nv50_disp_dmac *dmac = (void *)object; | |
878da15a | 253 | nvkm_object_ref(NULL, (struct nvkm_object **)&dmac->pushdma); |
370c00f9 BS |
254 | nv50_disp_chan_destroy(&dmac->base); |
255 | } | |
256 | ||
257 | static int | |
878da15a | 258 | nv50_disp_dmac_init(struct nvkm_object *object) |
370c00f9 | 259 | { |
fd166a18 | 260 | struct nv50_disp *disp = (void *)object->engine; |
370c00f9 | 261 | struct nv50_disp_dmac *dmac = (void *)object; |
84407824 BS |
262 | struct nvkm_subdev *subdev = &disp->base.engine.subdev; |
263 | struct nvkm_device *device = subdev->device; | |
370c00f9 BS |
264 | int chid = dmac->base.chid; |
265 | int ret; | |
266 | ||
267 | ret = nv50_disp_chan_init(&dmac->base); | |
268 | if (ret) | |
269 | return ret; | |
270 | ||
271 | /* enable error reporting */ | |
2fde1f1c | 272 | nvkm_mask(device, 0x610028, 0x00010000 << chid, 0x00010000 << chid); |
370c00f9 BS |
273 | |
274 | /* initialise channel for dma command submission */ | |
2fde1f1c BS |
275 | nvkm_wr32(device, 0x610204 + (chid * 0x0010), dmac->push); |
276 | nvkm_wr32(device, 0x610208 + (chid * 0x0010), 0x00010000); | |
277 | nvkm_wr32(device, 0x61020c + (chid * 0x0010), chid); | |
278 | nvkm_mask(device, 0x610200 + (chid * 0x0010), 0x00000010, 0x00000010); | |
279 | nvkm_wr32(device, 0x640000 + (chid * 0x1000), 0x00000000); | |
280 | nvkm_wr32(device, 0x610200 + (chid * 0x0010), 0x00000013); | |
370c00f9 BS |
281 | |
282 | /* wait for it to go inactive */ | |
3a020b4d BS |
283 | if (nvkm_msec(device, 2000, |
284 | if (!(nvkm_rd32(device, 0x610200 + (chid * 0x10)) & 0x80000000)) | |
285 | break; | |
286 | ) < 0) { | |
84407824 BS |
287 | nvkm_error(subdev, "ch %d init timeout, %08x\n", chid, |
288 | nvkm_rd32(device, 0x610200 + (chid * 0x10))); | |
370c00f9 BS |
289 | return -EBUSY; |
290 | } | |
291 | ||
292 | return 0; | |
293 | } | |
294 | ||
295 | static int | |
878da15a | 296 | nv50_disp_dmac_fini(struct nvkm_object *object, bool suspend) |
370c00f9 | 297 | { |
fd166a18 | 298 | struct nv50_disp *disp = (void *)object->engine; |
370c00f9 | 299 | struct nv50_disp_dmac *dmac = (void *)object; |
84407824 BS |
300 | struct nvkm_subdev *subdev = &disp->base.engine.subdev; |
301 | struct nvkm_device *device = subdev->device; | |
370c00f9 BS |
302 | int chid = dmac->base.chid; |
303 | ||
304 | /* deactivate channel */ | |
2fde1f1c BS |
305 | nvkm_mask(device, 0x610200 + (chid * 0x0010), 0x00001010, 0x00001000); |
306 | nvkm_mask(device, 0x610200 + (chid * 0x0010), 0x00000003, 0x00000000); | |
3a020b4d BS |
307 | if (nvkm_msec(device, 2000, |
308 | if (!(nvkm_rd32(device, 0x610200 + (chid * 0x10)) & 0x001e0000)) | |
309 | break; | |
310 | ) < 0) { | |
84407824 BS |
311 | nvkm_error(subdev, "ch %d fini timeout, %08x\n", chid, |
312 | nvkm_rd32(device, 0x610200 + (chid * 0x10))); | |
370c00f9 BS |
313 | if (suspend) |
314 | return -EBUSY; | |
315 | } | |
316 | ||
b38a2322 | 317 | /* disable error reporting and completion notifications */ |
2fde1f1c | 318 | nvkm_mask(device, 0x610028, 0x00010001 << chid, 0x00000000 << chid); |
370c00f9 BS |
319 | |
320 | return nv50_disp_chan_fini(&dmac->base, suspend); | |
70cabe4a BS |
321 | } |
322 | ||
323 | /******************************************************************************* | |
324 | * EVO master channel object | |
325 | ******************************************************************************/ | |
326 | ||
d67d92c0 | 327 | static void |
fd166a18 | 328 | nv50_disp_mthd_list(struct nv50_disp *disp, int debug, u32 base, int c, |
d67d92c0 BS |
329 | const struct nv50_disp_mthd_list *list, int inst) |
330 | { | |
84407824 BS |
331 | struct nvkm_subdev *subdev = &disp->base.engine.subdev; |
332 | struct nvkm_device *device = subdev->device; | |
d67d92c0 BS |
333 | int i; |
334 | ||
335 | for (i = 0; list->data[i].mthd; i++) { | |
336 | if (list->data[i].addr) { | |
2fde1f1c BS |
337 | u32 next = nvkm_rd32(device, list->data[i].addr + base + 0); |
338 | u32 prev = nvkm_rd32(device, list->data[i].addr + base + c); | |
d67d92c0 BS |
339 | u32 mthd = list->data[i].mthd + (list->mthd * inst); |
340 | const char *name = list->data[i].name; | |
341 | char mods[16]; | |
342 | ||
343 | if (prev != next) | |
84407824 | 344 | snprintf(mods, sizeof(mods), "-> %08x", next); |
d67d92c0 BS |
345 | else |
346 | snprintf(mods, sizeof(mods), "%13c", ' '); | |
347 | ||
84407824 BS |
348 | nvkm_printk_(subdev, debug, info, |
349 | "\t%04x: %08x %s%s%s\n", | |
350 | mthd, prev, mods, name ? " // " : "", | |
351 | name ? name : ""); | |
d67d92c0 BS |
352 | } |
353 | } | |
354 | } | |
355 | ||
356 | void | |
fd166a18 | 357 | nv50_disp_mthd_chan(struct nv50_disp *disp, int debug, int head, |
d67d92c0 BS |
358 | const struct nv50_disp_mthd_chan *chan) |
359 | { | |
fd166a18 BS |
360 | struct nvkm_object *object = nv_object(disp); |
361 | const struct nv50_disp_impl *impl = (void *)object->oclass; | |
d67d92c0 | 362 | const struct nv50_disp_mthd_list *list; |
84407824 | 363 | struct nvkm_subdev *subdev = &disp->base.engine.subdev; |
d67d92c0 BS |
364 | int i, j; |
365 | ||
fd166a18 | 366 | if (debug > nv_subdev(disp)->debug) |
d67d92c0 BS |
367 | return; |
368 | ||
369 | for (i = 0; (list = chan->data[i].mthd) != NULL; i++) { | |
370 | u32 base = head * chan->addr; | |
371 | for (j = 0; j < chan->data[i].nr; j++, base += list->addr) { | |
372 | const char *cname = chan->name; | |
373 | const char *sname = ""; | |
374 | char cname_[16], sname_[16]; | |
375 | ||
376 | if (chan->addr) { | |
377 | snprintf(cname_, sizeof(cname_), "%s %d", | |
378 | chan->name, head); | |
379 | cname = cname_; | |
380 | } | |
381 | ||
382 | if (chan->data[i].nr > 1) { | |
383 | snprintf(sname_, sizeof(sname_), " - %s %d", | |
384 | chan->data[i].name, j); | |
385 | sname = sname_; | |
386 | } | |
387 | ||
84407824 | 388 | nvkm_printk_(subdev, debug, info, "%s%s:\n", cname, sname); |
fd166a18 | 389 | nv50_disp_mthd_list(disp, debug, base, impl->mthd.prev, |
d67d92c0 BS |
390 | list, j); |
391 | } | |
392 | } | |
393 | } | |
394 | ||
395 | const struct nv50_disp_mthd_list | |
2832271d | 396 | nv50_disp_core_mthd_base = { |
d67d92c0 BS |
397 | .mthd = 0x0000, |
398 | .addr = 0x000000, | |
399 | .data = { | |
400 | { 0x0080, 0x000000 }, | |
401 | { 0x0084, 0x610bb8 }, | |
402 | { 0x0088, 0x610b9c }, | |
403 | { 0x008c, 0x000000 }, | |
404 | {} | |
405 | } | |
406 | }; | |
407 | ||
408 | static const struct nv50_disp_mthd_list | |
2832271d | 409 | nv50_disp_core_mthd_dac = { |
d67d92c0 BS |
410 | .mthd = 0x0080, |
411 | .addr = 0x000008, | |
412 | .data = { | |
413 | { 0x0400, 0x610b58 }, | |
414 | { 0x0404, 0x610bdc }, | |
415 | { 0x0420, 0x610828 }, | |
416 | {} | |
417 | } | |
418 | }; | |
419 | ||
420 | const struct nv50_disp_mthd_list | |
2832271d | 421 | nv50_disp_core_mthd_sor = { |
d67d92c0 BS |
422 | .mthd = 0x0040, |
423 | .addr = 0x000008, | |
424 | .data = { | |
425 | { 0x0600, 0x610b70 }, | |
426 | {} | |
427 | } | |
428 | }; | |
429 | ||
430 | const struct nv50_disp_mthd_list | |
2832271d | 431 | nv50_disp_core_mthd_pior = { |
d67d92c0 BS |
432 | .mthd = 0x0040, |
433 | .addr = 0x000008, | |
434 | .data = { | |
435 | { 0x0700, 0x610b80 }, | |
436 | {} | |
437 | } | |
438 | }; | |
439 | ||
440 | static const struct nv50_disp_mthd_list | |
2832271d | 441 | nv50_disp_core_mthd_head = { |
d67d92c0 BS |
442 | .mthd = 0x0400, |
443 | .addr = 0x000540, | |
444 | .data = { | |
445 | { 0x0800, 0x610ad8 }, | |
446 | { 0x0804, 0x610ad0 }, | |
447 | { 0x0808, 0x610a48 }, | |
448 | { 0x080c, 0x610a78 }, | |
449 | { 0x0810, 0x610ac0 }, | |
450 | { 0x0814, 0x610af8 }, | |
451 | { 0x0818, 0x610b00 }, | |
452 | { 0x081c, 0x610ae8 }, | |
453 | { 0x0820, 0x610af0 }, | |
454 | { 0x0824, 0x610b08 }, | |
455 | { 0x0828, 0x610b10 }, | |
456 | { 0x082c, 0x610a68 }, | |
457 | { 0x0830, 0x610a60 }, | |
458 | { 0x0834, 0x000000 }, | |
459 | { 0x0838, 0x610a40 }, | |
460 | { 0x0840, 0x610a24 }, | |
461 | { 0x0844, 0x610a2c }, | |
462 | { 0x0848, 0x610aa8 }, | |
463 | { 0x084c, 0x610ab0 }, | |
464 | { 0x0860, 0x610a84 }, | |
465 | { 0x0864, 0x610a90 }, | |
466 | { 0x0868, 0x610b18 }, | |
467 | { 0x086c, 0x610b20 }, | |
468 | { 0x0870, 0x610ac8 }, | |
469 | { 0x0874, 0x610a38 }, | |
470 | { 0x0880, 0x610a58 }, | |
471 | { 0x0884, 0x610a9c }, | |
472 | { 0x08a0, 0x610a70 }, | |
473 | { 0x08a4, 0x610a50 }, | |
474 | { 0x08a8, 0x610ae0 }, | |
475 | { 0x08c0, 0x610b28 }, | |
476 | { 0x08c4, 0x610b30 }, | |
477 | { 0x08c8, 0x610b40 }, | |
478 | { 0x08d4, 0x610b38 }, | |
479 | { 0x08d8, 0x610b48 }, | |
480 | { 0x08dc, 0x610b50 }, | |
481 | { 0x0900, 0x610a18 }, | |
482 | { 0x0904, 0x610ab8 }, | |
483 | {} | |
484 | } | |
485 | }; | |
486 | ||
487 | static const struct nv50_disp_mthd_chan | |
2832271d | 488 | nv50_disp_core_mthd_chan = { |
d67d92c0 BS |
489 | .name = "Core", |
490 | .addr = 0x000000, | |
491 | .data = { | |
2832271d BS |
492 | { "Global", 1, &nv50_disp_core_mthd_base }, |
493 | { "DAC", 3, &nv50_disp_core_mthd_dac }, | |
494 | { "SOR", 2, &nv50_disp_core_mthd_sor }, | |
495 | { "PIOR", 3, &nv50_disp_core_mthd_pior }, | |
496 | { "HEAD", 2, &nv50_disp_core_mthd_head }, | |
d67d92c0 BS |
497 | {} |
498 | } | |
499 | }; | |
500 | ||
2c04ae01 | 501 | int |
878da15a BS |
502 | nv50_disp_core_ctor(struct nvkm_object *parent, |
503 | struct nvkm_object *engine, | |
504 | struct nvkm_oclass *oclass, void *data, u32 size, | |
505 | struct nvkm_object **pobject) | |
70cabe4a | 506 | { |
648d4dfd BS |
507 | union { |
508 | struct nv50_disp_core_channel_dma_v0 v0; | |
509 | } *args = data; | |
370c00f9 | 510 | struct nv50_disp_dmac *mast; |
70cabe4a BS |
511 | int ret; |
512 | ||
53003941 | 513 | nvif_ioctl(parent, "create disp core channel dma size %d\n", size); |
648d4dfd | 514 | if (nvif_unpack(args->v0, 0, 0, false)) { |
53003941 BS |
515 | nvif_ioctl(parent, "create disp core channel dma vers %d " |
516 | "pushbuf %08x\n", | |
517 | args->v0.version, args->v0.pushbuf); | |
648d4dfd BS |
518 | } else |
519 | return ret; | |
370c00f9 | 520 | |
648d4dfd | 521 | ret = nv50_disp_dmac_create_(parent, engine, oclass, args->v0.pushbuf, |
370c00f9 BS |
522 | 0, sizeof(*mast), (void **)&mast); |
523 | *pobject = nv_object(mast); | |
70cabe4a BS |
524 | if (ret) |
525 | return ret; | |
526 | ||
527 | return 0; | |
528 | } | |
529 | ||
70cabe4a | 530 | static int |
878da15a | 531 | nv50_disp_core_init(struct nvkm_object *object) |
70cabe4a | 532 | { |
fd166a18 | 533 | struct nv50_disp *disp = (void *)object->engine; |
370c00f9 | 534 | struct nv50_disp_dmac *mast = (void *)object; |
84407824 BS |
535 | struct nvkm_subdev *subdev = &disp->base.engine.subdev; |
536 | struct nvkm_device *device = subdev->device; | |
70cabe4a BS |
537 | int ret; |
538 | ||
370c00f9 | 539 | ret = nv50_disp_chan_init(&mast->base); |
70cabe4a BS |
540 | if (ret) |
541 | return ret; | |
542 | ||
370c00f9 | 543 | /* enable error reporting */ |
2fde1f1c | 544 | nvkm_mask(device, 0x610028, 0x00010000, 0x00010000); |
370c00f9 BS |
545 | |
546 | /* attempt to unstick channel from some unknown state */ | |
2fde1f1c BS |
547 | if ((nvkm_rd32(device, 0x610200) & 0x009f0000) == 0x00020000) |
548 | nvkm_mask(device, 0x610200, 0x00800000, 0x00800000); | |
549 | if ((nvkm_rd32(device, 0x610200) & 0x003f0000) == 0x00030000) | |
550 | nvkm_mask(device, 0x610200, 0x00600000, 0x00600000); | |
370c00f9 BS |
551 | |
552 | /* initialise channel for dma command submission */ | |
2fde1f1c BS |
553 | nvkm_wr32(device, 0x610204, mast->push); |
554 | nvkm_wr32(device, 0x610208, 0x00010000); | |
555 | nvkm_wr32(device, 0x61020c, 0x00000000); | |
556 | nvkm_mask(device, 0x610200, 0x00000010, 0x00000010); | |
557 | nvkm_wr32(device, 0x640000, 0x00000000); | |
558 | nvkm_wr32(device, 0x610200, 0x01000013); | |
370c00f9 BS |
559 | |
560 | /* wait for it to go inactive */ | |
3a020b4d BS |
561 | if (nvkm_msec(device, 2000, |
562 | if (!(nvkm_rd32(device, 0x610200) & 0x80000000)) | |
563 | break; | |
564 | ) < 0) { | |
84407824 BS |
565 | nvkm_error(subdev, "core init: %08x\n", |
566 | nvkm_rd32(device, 0x610200)); | |
370c00f9 BS |
567 | return -EBUSY; |
568 | } | |
569 | ||
70cabe4a BS |
570 | return 0; |
571 | } | |
572 | ||
573 | static int | |
878da15a | 574 | nv50_disp_core_fini(struct nvkm_object *object, bool suspend) |
70cabe4a | 575 | { |
fd166a18 | 576 | struct nv50_disp *disp = (void *)object->engine; |
370c00f9 | 577 | struct nv50_disp_dmac *mast = (void *)object; |
84407824 BS |
578 | struct nvkm_subdev *subdev = &disp->base.engine.subdev; |
579 | struct nvkm_device *device = subdev->device; | |
370c00f9 BS |
580 | |
581 | /* deactivate channel */ | |
2fde1f1c BS |
582 | nvkm_mask(device, 0x610200, 0x00000010, 0x00000000); |
583 | nvkm_mask(device, 0x610200, 0x00000003, 0x00000000); | |
3a020b4d BS |
584 | if (nvkm_msec(device, 2000, |
585 | if (!(nvkm_rd32(device, 0x610200) & 0x001e0000)) | |
586 | break; | |
587 | ) < 0) { | |
84407824 BS |
588 | nvkm_error(subdev, "core fini: %08x\n", |
589 | nvkm_rd32(device, 0x610200)); | |
370c00f9 BS |
590 | if (suspend) |
591 | return -EBUSY; | |
592 | } | |
593 | ||
b38a2322 | 594 | /* disable error reporting and completion notifications */ |
2fde1f1c | 595 | nvkm_mask(device, 0x610028, 0x00010001, 0x00000000); |
370c00f9 BS |
596 | |
597 | return nv50_disp_chan_fini(&mast->base, suspend); | |
70cabe4a BS |
598 | } |
599 | ||
2c04ae01 | 600 | struct nv50_disp_chan_impl |
2832271d BS |
601 | nv50_disp_core_ofuncs = { |
602 | .base.ctor = nv50_disp_core_ctor, | |
2c04ae01 | 603 | .base.dtor = nv50_disp_dmac_dtor, |
2832271d BS |
604 | .base.init = nv50_disp_core_init, |
605 | .base.fini = nv50_disp_core_fini, | |
b76f1529 | 606 | .base.map = nv50_disp_chan_map, |
b38a2322 | 607 | .base.ntfy = nv50_disp_chan_ntfy, |
2c04ae01 BS |
608 | .base.rd32 = nv50_disp_chan_rd32, |
609 | .base.wr32 = nv50_disp_chan_wr32, | |
610 | .chid = 0, | |
611 | .attach = nv50_disp_dmac_object_attach, | |
612 | .detach = nv50_disp_dmac_object_detach, | |
70cabe4a BS |
613 | }; |
614 | ||
615 | /******************************************************************************* | |
370c00f9 | 616 | * EVO sync channel objects |
70cabe4a BS |
617 | ******************************************************************************/ |
618 | ||
d67d92c0 | 619 | static const struct nv50_disp_mthd_list |
2832271d | 620 | nv50_disp_base_mthd_base = { |
d67d92c0 BS |
621 | .mthd = 0x0000, |
622 | .addr = 0x000000, | |
623 | .data = { | |
624 | { 0x0080, 0x000000 }, | |
625 | { 0x0084, 0x0008c4 }, | |
626 | { 0x0088, 0x0008d0 }, | |
627 | { 0x008c, 0x0008dc }, | |
628 | { 0x0090, 0x0008e4 }, | |
629 | { 0x0094, 0x610884 }, | |
630 | { 0x00a0, 0x6108a0 }, | |
631 | { 0x00a4, 0x610878 }, | |
632 | { 0x00c0, 0x61086c }, | |
633 | { 0x00e0, 0x610858 }, | |
634 | { 0x00e4, 0x610860 }, | |
635 | { 0x00e8, 0x6108ac }, | |
636 | { 0x00ec, 0x6108b4 }, | |
637 | { 0x0100, 0x610894 }, | |
638 | { 0x0110, 0x6108bc }, | |
639 | { 0x0114, 0x61088c }, | |
640 | {} | |
641 | } | |
642 | }; | |
643 | ||
644 | const struct nv50_disp_mthd_list | |
2832271d | 645 | nv50_disp_base_mthd_image = { |
d67d92c0 BS |
646 | .mthd = 0x0400, |
647 | .addr = 0x000000, | |
648 | .data = { | |
649 | { 0x0800, 0x6108f0 }, | |
650 | { 0x0804, 0x6108fc }, | |
651 | { 0x0808, 0x61090c }, | |
652 | { 0x080c, 0x610914 }, | |
653 | { 0x0810, 0x610904 }, | |
654 | {} | |
655 | } | |
656 | }; | |
657 | ||
658 | static const struct nv50_disp_mthd_chan | |
2832271d | 659 | nv50_disp_base_mthd_chan = { |
d67d92c0 BS |
660 | .name = "Base", |
661 | .addr = 0x000540, | |
662 | .data = { | |
2832271d BS |
663 | { "Global", 1, &nv50_disp_base_mthd_base }, |
664 | { "Image", 2, &nv50_disp_base_mthd_image }, | |
d67d92c0 BS |
665 | {} |
666 | } | |
667 | }; | |
668 | ||
2c04ae01 | 669 | int |
878da15a BS |
670 | nv50_disp_base_ctor(struct nvkm_object *parent, |
671 | struct nvkm_object *engine, | |
672 | struct nvkm_oclass *oclass, void *data, u32 size, | |
673 | struct nvkm_object **pobject) | |
70cabe4a | 674 | { |
648d4dfd BS |
675 | union { |
676 | struct nv50_disp_base_channel_dma_v0 v0; | |
677 | } *args = data; | |
fd166a18 | 678 | struct nv50_disp *disp = (void *)engine; |
370c00f9 | 679 | struct nv50_disp_dmac *dmac; |
70cabe4a BS |
680 | int ret; |
681 | ||
53003941 | 682 | nvif_ioctl(parent, "create disp base channel dma size %d\n", size); |
648d4dfd | 683 | if (nvif_unpack(args->v0, 0, 0, false)) { |
53003941 BS |
684 | nvif_ioctl(parent, "create disp base channel dma vers %d " |
685 | "pushbuf %08x head %d\n", | |
686 | args->v0.version, args->v0.pushbuf, args->v0.head); | |
fd166a18 | 687 | if (args->v0.head > disp->head.nr) |
648d4dfd BS |
688 | return -EINVAL; |
689 | } else | |
690 | return ret; | |
370c00f9 | 691 | |
648d4dfd BS |
692 | ret = nv50_disp_dmac_create_(parent, engine, oclass, args->v0.pushbuf, |
693 | args->v0.head, sizeof(*dmac), | |
694 | (void **)&dmac); | |
370c00f9 | 695 | *pobject = nv_object(dmac); |
70cabe4a BS |
696 | if (ret) |
697 | return ret; | |
698 | ||
699 | return 0; | |
700 | } | |
701 | ||
2c04ae01 | 702 | struct nv50_disp_chan_impl |
2832271d BS |
703 | nv50_disp_base_ofuncs = { |
704 | .base.ctor = nv50_disp_base_ctor, | |
2c04ae01 BS |
705 | .base.dtor = nv50_disp_dmac_dtor, |
706 | .base.init = nv50_disp_dmac_init, | |
707 | .base.fini = nv50_disp_dmac_fini, | |
b38a2322 | 708 | .base.ntfy = nv50_disp_chan_ntfy, |
b76f1529 | 709 | .base.map = nv50_disp_chan_map, |
2c04ae01 BS |
710 | .base.rd32 = nv50_disp_chan_rd32, |
711 | .base.wr32 = nv50_disp_chan_wr32, | |
712 | .chid = 1, | |
713 | .attach = nv50_disp_dmac_object_attach, | |
714 | .detach = nv50_disp_dmac_object_detach, | |
370c00f9 BS |
715 | }; |
716 | ||
717 | /******************************************************************************* | |
718 | * EVO overlay channel objects | |
719 | ******************************************************************************/ | |
70cabe4a | 720 | |
d67d92c0 BS |
721 | const struct nv50_disp_mthd_list |
722 | nv50_disp_ovly_mthd_base = { | |
723 | .mthd = 0x0000, | |
724 | .addr = 0x000000, | |
725 | .data = { | |
726 | { 0x0080, 0x000000 }, | |
727 | { 0x0084, 0x0009a0 }, | |
728 | { 0x0088, 0x0009c0 }, | |
729 | { 0x008c, 0x0009c8 }, | |
730 | { 0x0090, 0x6109b4 }, | |
731 | { 0x0094, 0x610970 }, | |
732 | { 0x00a0, 0x610998 }, | |
733 | { 0x00a4, 0x610964 }, | |
734 | { 0x00c0, 0x610958 }, | |
735 | { 0x00e0, 0x6109a8 }, | |
736 | { 0x00e4, 0x6109d0 }, | |
737 | { 0x00e8, 0x6109d8 }, | |
738 | { 0x0100, 0x61094c }, | |
739 | { 0x0104, 0x610984 }, | |
740 | { 0x0108, 0x61098c }, | |
741 | { 0x0800, 0x6109f8 }, | |
742 | { 0x0808, 0x610a08 }, | |
743 | { 0x080c, 0x610a10 }, | |
744 | { 0x0810, 0x610a00 }, | |
745 | {} | |
746 | } | |
747 | }; | |
748 | ||
749 | static const struct nv50_disp_mthd_chan | |
750 | nv50_disp_ovly_mthd_chan = { | |
751 | .name = "Overlay", | |
752 | .addr = 0x000540, | |
753 | .data = { | |
754 | { "Global", 1, &nv50_disp_ovly_mthd_base }, | |
755 | {} | |
756 | } | |
757 | }; | |
758 | ||
2c04ae01 | 759 | int |
878da15a BS |
760 | nv50_disp_ovly_ctor(struct nvkm_object *parent, |
761 | struct nvkm_object *engine, | |
762 | struct nvkm_oclass *oclass, void *data, u32 size, | |
763 | struct nvkm_object **pobject) | |
70cabe4a | 764 | { |
648d4dfd BS |
765 | union { |
766 | struct nv50_disp_overlay_channel_dma_v0 v0; | |
767 | } *args = data; | |
fd166a18 | 768 | struct nv50_disp *disp = (void *)engine; |
370c00f9 | 769 | struct nv50_disp_dmac *dmac; |
70cabe4a BS |
770 | int ret; |
771 | ||
53003941 | 772 | nvif_ioctl(parent, "create disp overlay channel dma size %d\n", size); |
648d4dfd | 773 | if (nvif_unpack(args->v0, 0, 0, false)) { |
53003941 BS |
774 | nvif_ioctl(parent, "create disp overlay channel dma vers %d " |
775 | "pushbuf %08x head %d\n", | |
776 | args->v0.version, args->v0.pushbuf, args->v0.head); | |
fd166a18 | 777 | if (args->v0.head > disp->head.nr) |
648d4dfd BS |
778 | return -EINVAL; |
779 | } else | |
780 | return ret; | |
370c00f9 | 781 | |
648d4dfd BS |
782 | ret = nv50_disp_dmac_create_(parent, engine, oclass, args->v0.pushbuf, |
783 | args->v0.head, sizeof(*dmac), | |
784 | (void **)&dmac); | |
370c00f9 | 785 | *pobject = nv_object(dmac); |
70cabe4a BS |
786 | if (ret) |
787 | return ret; | |
788 | ||
789 | return 0; | |
790 | } | |
791 | ||
2c04ae01 | 792 | struct nv50_disp_chan_impl |
370c00f9 | 793 | nv50_disp_ovly_ofuncs = { |
2c04ae01 BS |
794 | .base.ctor = nv50_disp_ovly_ctor, |
795 | .base.dtor = nv50_disp_dmac_dtor, | |
796 | .base.init = nv50_disp_dmac_init, | |
797 | .base.fini = nv50_disp_dmac_fini, | |
b38a2322 | 798 | .base.ntfy = nv50_disp_chan_ntfy, |
b76f1529 | 799 | .base.map = nv50_disp_chan_map, |
2c04ae01 BS |
800 | .base.rd32 = nv50_disp_chan_rd32, |
801 | .base.wr32 = nv50_disp_chan_wr32, | |
802 | .chid = 3, | |
803 | .attach = nv50_disp_dmac_object_attach, | |
804 | .detach = nv50_disp_dmac_object_detach, | |
70cabe4a BS |
805 | }; |
806 | ||
807 | /******************************************************************************* | |
370c00f9 | 808 | * EVO PIO channel base class |
70cabe4a BS |
809 | ******************************************************************************/ |
810 | ||
811 | static int | |
878da15a BS |
812 | nv50_disp_pioc_create_(struct nvkm_object *parent, |
813 | struct nvkm_object *engine, | |
814 | struct nvkm_oclass *oclass, int head, | |
370c00f9 | 815 | int length, void **pobject) |
70cabe4a | 816 | { |
2c04ae01 | 817 | return nv50_disp_chan_create_(parent, engine, oclass, head, |
370c00f9 BS |
818 | length, pobject); |
819 | } | |
820 | ||
2c04ae01 | 821 | void |
878da15a | 822 | nv50_disp_pioc_dtor(struct nvkm_object *object) |
370c00f9 BS |
823 | { |
824 | struct nv50_disp_pioc *pioc = (void *)object; | |
825 | nv50_disp_chan_destroy(&pioc->base); | |
826 | } | |
827 | ||
828 | static int | |
878da15a | 829 | nv50_disp_pioc_init(struct nvkm_object *object) |
370c00f9 | 830 | { |
fd166a18 | 831 | struct nv50_disp *disp = (void *)object->engine; |
370c00f9 | 832 | struct nv50_disp_pioc *pioc = (void *)object; |
84407824 BS |
833 | struct nvkm_subdev *subdev = &disp->base.engine.subdev; |
834 | struct nvkm_device *device = subdev->device; | |
370c00f9 | 835 | int chid = pioc->base.chid; |
70cabe4a BS |
836 | int ret; |
837 | ||
370c00f9 | 838 | ret = nv50_disp_chan_init(&pioc->base); |
70cabe4a BS |
839 | if (ret) |
840 | return ret; | |
841 | ||
2fde1f1c | 842 | nvkm_wr32(device, 0x610200 + (chid * 0x10), 0x00002000); |
3a020b4d BS |
843 | if (nvkm_msec(device, 2000, |
844 | if (!(nvkm_rd32(device, 0x610200 + (chid * 0x10)) & 0x00030000)) | |
845 | break; | |
846 | ) < 0) { | |
84407824 BS |
847 | nvkm_error(subdev, "ch %d timeout0: %08x\n", chid, |
848 | nvkm_rd32(device, 0x610200 + (chid * 0x10))); | |
370c00f9 BS |
849 | return -EBUSY; |
850 | } | |
851 | ||
2fde1f1c | 852 | nvkm_wr32(device, 0x610200 + (chid * 0x10), 0x00000001); |
3a020b4d BS |
853 | if (nvkm_msec(device, 2000, |
854 | u32 tmp = nvkm_rd32(device, 0x610200 + (chid * 0x10)); | |
855 | if ((tmp & 0x00030000) == 0x00010000) | |
856 | break; | |
857 | ) < 0) { | |
84407824 BS |
858 | nvkm_error(subdev, "ch %d timeout1: %08x\n", chid, |
859 | nvkm_rd32(device, 0x610200 + (chid * 0x10))); | |
370c00f9 BS |
860 | return -EBUSY; |
861 | } | |
862 | ||
70cabe4a BS |
863 | return 0; |
864 | } | |
865 | ||
370c00f9 | 866 | static int |
878da15a | 867 | nv50_disp_pioc_fini(struct nvkm_object *object, bool suspend) |
70cabe4a | 868 | { |
fd166a18 | 869 | struct nv50_disp *disp = (void *)object->engine; |
370c00f9 | 870 | struct nv50_disp_pioc *pioc = (void *)object; |
84407824 BS |
871 | struct nvkm_subdev *subdev = &disp->base.engine.subdev; |
872 | struct nvkm_device *device = subdev->device; | |
370c00f9 BS |
873 | int chid = pioc->base.chid; |
874 | ||
2fde1f1c | 875 | nvkm_mask(device, 0x610200 + (chid * 0x10), 0x00000001, 0x00000000); |
3a020b4d BS |
876 | if (nvkm_msec(device, 2000, |
877 | if (!(nvkm_rd32(device, 0x610200 + (chid * 0x10)) & 0x00030000)) | |
878 | break; | |
879 | ) < 0) { | |
84407824 BS |
880 | nvkm_error(subdev, "ch %d timeout: %08x\n", chid, |
881 | nvkm_rd32(device, 0x610200 + (chid * 0x10))); | |
370c00f9 BS |
882 | if (suspend) |
883 | return -EBUSY; | |
884 | } | |
885 | ||
886 | return nv50_disp_chan_fini(&pioc->base, suspend); | |
70cabe4a BS |
887 | } |
888 | ||
370c00f9 BS |
889 | /******************************************************************************* |
890 | * EVO immediate overlay channel objects | |
891 | ******************************************************************************/ | |
892 | ||
2c04ae01 | 893 | int |
878da15a BS |
894 | nv50_disp_oimm_ctor(struct nvkm_object *parent, |
895 | struct nvkm_object *engine, | |
896 | struct nvkm_oclass *oclass, void *data, u32 size, | |
897 | struct nvkm_object **pobject) | |
70cabe4a | 898 | { |
648d4dfd BS |
899 | union { |
900 | struct nv50_disp_overlay_v0 v0; | |
901 | } *args = data; | |
fd166a18 | 902 | struct nv50_disp *disp = (void *)engine; |
370c00f9 | 903 | struct nv50_disp_pioc *pioc; |
70cabe4a BS |
904 | int ret; |
905 | ||
53003941 | 906 | nvif_ioctl(parent, "create disp overlay size %d\n", size); |
648d4dfd | 907 | if (nvif_unpack(args->v0, 0, 0, false)) { |
53003941 BS |
908 | nvif_ioctl(parent, "create disp overlay vers %d head %d\n", |
909 | args->v0.version, args->v0.head); | |
fd166a18 | 910 | if (args->v0.head > disp->head.nr) |
648d4dfd BS |
911 | return -EINVAL; |
912 | } else | |
913 | return ret; | |
370c00f9 | 914 | |
648d4dfd | 915 | ret = nv50_disp_pioc_create_(parent, engine, oclass, args->v0.head, |
370c00f9 BS |
916 | sizeof(*pioc), (void **)&pioc); |
917 | *pobject = nv_object(pioc); | |
70cabe4a BS |
918 | if (ret) |
919 | return ret; | |
920 | ||
921 | return 0; | |
922 | } | |
923 | ||
2c04ae01 | 924 | struct nv50_disp_chan_impl |
370c00f9 | 925 | nv50_disp_oimm_ofuncs = { |
2c04ae01 BS |
926 | .base.ctor = nv50_disp_oimm_ctor, |
927 | .base.dtor = nv50_disp_pioc_dtor, | |
928 | .base.init = nv50_disp_pioc_init, | |
929 | .base.fini = nv50_disp_pioc_fini, | |
b38a2322 | 930 | .base.ntfy = nv50_disp_chan_ntfy, |
b76f1529 | 931 | .base.map = nv50_disp_chan_map, |
2c04ae01 BS |
932 | .base.rd32 = nv50_disp_chan_rd32, |
933 | .base.wr32 = nv50_disp_chan_wr32, | |
934 | .chid = 5, | |
370c00f9 BS |
935 | }; |
936 | ||
937 | /******************************************************************************* | |
938 | * EVO cursor channel objects | |
939 | ******************************************************************************/ | |
940 | ||
2c04ae01 | 941 | int |
878da15a BS |
942 | nv50_disp_curs_ctor(struct nvkm_object *parent, |
943 | struct nvkm_object *engine, | |
944 | struct nvkm_oclass *oclass, void *data, u32 size, | |
945 | struct nvkm_object **pobject) | |
70cabe4a | 946 | { |
648d4dfd BS |
947 | union { |
948 | struct nv50_disp_cursor_v0 v0; | |
949 | } *args = data; | |
fd166a18 | 950 | struct nv50_disp *disp = (void *)engine; |
370c00f9 BS |
951 | struct nv50_disp_pioc *pioc; |
952 | int ret; | |
953 | ||
53003941 | 954 | nvif_ioctl(parent, "create disp cursor size %d\n", size); |
648d4dfd | 955 | if (nvif_unpack(args->v0, 0, 0, false)) { |
53003941 BS |
956 | nvif_ioctl(parent, "create disp cursor vers %d head %d\n", |
957 | args->v0.version, args->v0.head); | |
fd166a18 | 958 | if (args->v0.head > disp->head.nr) |
648d4dfd BS |
959 | return -EINVAL; |
960 | } else | |
961 | return ret; | |
370c00f9 | 962 | |
648d4dfd | 963 | ret = nv50_disp_pioc_create_(parent, engine, oclass, args->v0.head, |
370c00f9 BS |
964 | sizeof(*pioc), (void **)&pioc); |
965 | *pobject = nv_object(pioc); | |
966 | if (ret) | |
967 | return ret; | |
968 | ||
969 | return 0; | |
70cabe4a BS |
970 | } |
971 | ||
2c04ae01 | 972 | struct nv50_disp_chan_impl |
370c00f9 | 973 | nv50_disp_curs_ofuncs = { |
2c04ae01 BS |
974 | .base.ctor = nv50_disp_curs_ctor, |
975 | .base.dtor = nv50_disp_pioc_dtor, | |
976 | .base.init = nv50_disp_pioc_init, | |
977 | .base.fini = nv50_disp_pioc_fini, | |
b38a2322 | 978 | .base.ntfy = nv50_disp_chan_ntfy, |
b76f1529 | 979 | .base.map = nv50_disp_chan_map, |
2c04ae01 BS |
980 | .base.rd32 = nv50_disp_chan_rd32, |
981 | .base.wr32 = nv50_disp_chan_wr32, | |
982 | .chid = 7, | |
70cabe4a BS |
983 | }; |
984 | ||
985 | /******************************************************************************* | |
986 | * Base display object | |
987 | ******************************************************************************/ | |
988 | ||
d2fa7d32 | 989 | int |
2832271d | 990 | nv50_disp_main_scanoutpos(NV50_DISP_MTHD_V0) |
d2fa7d32 | 991 | { |
2fde1f1c BS |
992 | struct nvkm_device *device = disp->base.engine.subdev.device; |
993 | const u32 blanke = nvkm_rd32(device, 0x610aec + (head * 0x540)); | |
994 | const u32 blanks = nvkm_rd32(device, 0x610af4 + (head * 0x540)); | |
995 | const u32 total = nvkm_rd32(device, 0x610afc + (head * 0x540)); | |
4952b4d3 BS |
996 | union { |
997 | struct nv04_disp_scanoutpos_v0 v0; | |
998 | } *args = data; | |
999 | int ret; | |
1000 | ||
53003941 | 1001 | nvif_ioctl(object, "disp scanoutpos size %d\n", size); |
4952b4d3 | 1002 | if (nvif_unpack(args->v0, 0, 0, false)) { |
53003941 BS |
1003 | nvif_ioctl(object, "disp scanoutpos vers %d\n", |
1004 | args->v0.version); | |
4952b4d3 BS |
1005 | args->v0.vblanke = (blanke & 0xffff0000) >> 16; |
1006 | args->v0.hblanke = (blanke & 0x0000ffff); | |
1007 | args->v0.vblanks = (blanks & 0xffff0000) >> 16; | |
1008 | args->v0.hblanks = (blanks & 0x0000ffff); | |
1009 | args->v0.vtotal = ( total & 0xffff0000) >> 16; | |
1010 | args->v0.htotal = ( total & 0x0000ffff); | |
1011 | args->v0.time[0] = ktime_to_ns(ktime_get()); | |
1012 | args->v0.vline = /* vline read locks hline */ | |
2fde1f1c | 1013 | nvkm_rd32(device, 0x616340 + (head * 0x800)) & 0xffff; |
4952b4d3 BS |
1014 | args->v0.time[1] = ktime_to_ns(ktime_get()); |
1015 | args->v0.hline = | |
2fde1f1c | 1016 | nvkm_rd32(device, 0x616344 + (head * 0x800)) & 0xffff; |
4952b4d3 BS |
1017 | } else |
1018 | return ret; | |
d2fa7d32 | 1019 | |
d2fa7d32 BS |
1020 | return 0; |
1021 | } | |
1022 | ||
bf0eb898 | 1023 | int |
878da15a | 1024 | nv50_disp_main_mthd(struct nvkm_object *object, u32 mthd, void *data, u32 size) |
bf0eb898 | 1025 | { |
4952b4d3 | 1026 | const struct nv50_disp_impl *impl = (void *)nv_oclass(object->engine); |
bf0eb898 BS |
1027 | union { |
1028 | struct nv50_disp_mthd_v0 v0; | |
1029 | struct nv50_disp_mthd_v1 v1; | |
1030 | } *args = data; | |
fd166a18 | 1031 | struct nv50_disp *disp = (void *)object->engine; |
bf0eb898 BS |
1032 | struct nvkm_output *outp = NULL; |
1033 | struct nvkm_output *temp; | |
1034 | u16 type, mask = 0; | |
1035 | int head, ret; | |
1036 | ||
1037 | if (mthd != NV50_DISP_MTHD) | |
1038 | return -EINVAL; | |
1039 | ||
53003941 | 1040 | nvif_ioctl(object, "disp mthd size %d\n", size); |
bf0eb898 | 1041 | if (nvif_unpack(args->v0, 0, 0, true)) { |
53003941 BS |
1042 | nvif_ioctl(object, "disp mthd vers %d mthd %02x head %d\n", |
1043 | args->v0.version, args->v0.method, args->v0.head); | |
bf0eb898 BS |
1044 | mthd = args->v0.method; |
1045 | head = args->v0.head; | |
1046 | } else | |
1047 | if (nvif_unpack(args->v1, 1, 1, true)) { | |
53003941 BS |
1048 | nvif_ioctl(object, "disp mthd vers %d mthd %02x " |
1049 | "type %04x mask %04x\n", | |
1050 | args->v1.version, args->v1.method, | |
1051 | args->v1.hasht, args->v1.hashm); | |
bf0eb898 BS |
1052 | mthd = args->v1.method; |
1053 | type = args->v1.hasht; | |
1054 | mask = args->v1.hashm; | |
1055 | head = ffs((mask >> 8) & 0x0f) - 1; | |
1056 | } else | |
1057 | return ret; | |
1058 | ||
fd166a18 | 1059 | if (head < 0 || head >= disp->head.nr) |
bf0eb898 BS |
1060 | return -ENXIO; |
1061 | ||
1062 | if (mask) { | |
fd166a18 | 1063 | list_for_each_entry(temp, &disp->base.outp, head) { |
bf0eb898 BS |
1064 | if ((temp->info.hasht == type) && |
1065 | (temp->info.hashm & mask) == mask) { | |
1066 | outp = temp; | |
1067 | break; | |
1068 | } | |
1069 | } | |
1070 | if (outp == NULL) | |
1071 | return -ENXIO; | |
1072 | } | |
1073 | ||
1074 | switch (mthd) { | |
4952b4d3 | 1075 | case NV50_DISP_SCANOUTPOS: |
fd166a18 | 1076 | return impl->head.scanoutpos(object, disp, data, size, head); |
bf0eb898 BS |
1077 | default: |
1078 | break; | |
1079 | } | |
1080 | ||
1081 | switch (mthd * !!outp) { | |
1082 | case NV50_DISP_MTHD_V1_DAC_PWR: | |
fd166a18 | 1083 | return disp->dac.power(object, disp, data, size, head, outp); |
c4abd317 | 1084 | case NV50_DISP_MTHD_V1_DAC_LOAD: |
fd166a18 | 1085 | return disp->dac.sense(object, disp, data, size, head, outp); |
d55b4af9 | 1086 | case NV50_DISP_MTHD_V1_SOR_PWR: |
fd166a18 | 1087 | return disp->sor.power(object, disp, data, size, head, outp); |
120b0c39 | 1088 | case NV50_DISP_MTHD_V1_SOR_HDA_ELD: |
fd166a18 | 1089 | if (!disp->sor.hda_eld) |
120b0c39 | 1090 | return -ENODEV; |
fd166a18 | 1091 | return disp->sor.hda_eld(object, disp, data, size, head, outp); |
e00f2235 | 1092 | case NV50_DISP_MTHD_V1_SOR_HDMI_PWR: |
fd166a18 | 1093 | if (!disp->sor.hdmi) |
e00f2235 | 1094 | return -ENODEV; |
fd166a18 | 1095 | return disp->sor.hdmi(object, disp, data, size, head, outp); |
a3761fa2 BS |
1096 | case NV50_DISP_MTHD_V1_SOR_LVDS_SCRIPT: { |
1097 | union { | |
1098 | struct nv50_disp_sor_lvds_script_v0 v0; | |
1099 | } *args = data; | |
53003941 | 1100 | nvif_ioctl(object, "disp sor lvds script size %d\n", size); |
a3761fa2 | 1101 | if (nvif_unpack(args->v0, 0, 0, false)) { |
53003941 BS |
1102 | nvif_ioctl(object, "disp sor lvds script " |
1103 | "vers %d name %04x\n", | |
1104 | args->v0.version, args->v0.script); | |
fd166a18 | 1105 | disp->sor.lvdsconf = args->v0.script; |
a3761fa2 BS |
1106 | return 0; |
1107 | } else | |
1108 | return ret; | |
1109 | } | |
1110 | break; | |
c02ed2bf | 1111 | case NV50_DISP_MTHD_V1_SOR_DP_PWR: { |
f2c906fc | 1112 | struct nvkm_output_dp *outpdp = nvkm_output_dp(outp); |
c02ed2bf BS |
1113 | union { |
1114 | struct nv50_disp_sor_dp_pwr_v0 v0; | |
1115 | } *args = data; | |
53003941 | 1116 | nvif_ioctl(object, "disp sor dp pwr size %d\n", size); |
c02ed2bf | 1117 | if (nvif_unpack(args->v0, 0, 0, false)) { |
53003941 BS |
1118 | nvif_ioctl(object, "disp sor dp pwr vers %d state %d\n", |
1119 | args->v0.version, args->v0.state); | |
c02ed2bf BS |
1120 | if (args->v0.state == 0) { |
1121 | nvkm_notify_put(&outpdp->irq); | |
f2c906fc | 1122 | outpdp->func->lnk_pwr(outpdp, 0); |
c02ed2bf BS |
1123 | atomic_set(&outpdp->lt.done, 0); |
1124 | return 0; | |
1125 | } else | |
1126 | if (args->v0.state != 0) { | |
1127 | nvkm_output_dp_train(&outpdp->base, 0, true); | |
1128 | return 0; | |
1129 | } | |
1130 | } else | |
1131 | return ret; | |
1132 | } | |
1133 | break; | |
67cb49c4 | 1134 | case NV50_DISP_MTHD_V1_PIOR_PWR: |
fd166a18 | 1135 | if (!disp->pior.power) |
67cb49c4 | 1136 | return -ENODEV; |
fd166a18 | 1137 | return disp->pior.power(object, disp, data, size, head, outp); |
bf0eb898 BS |
1138 | default: |
1139 | break; | |
1140 | } | |
1141 | ||
1142 | return -EINVAL; | |
1143 | } | |
1144 | ||
79ca2770 | 1145 | int |
878da15a BS |
1146 | nv50_disp_main_ctor(struct nvkm_object *parent, |
1147 | struct nvkm_object *engine, | |
1148 | struct nvkm_oclass *oclass, void *data, u32 size, | |
1149 | struct nvkm_object **pobject) | |
70cabe4a | 1150 | { |
fd166a18 | 1151 | struct nv50_disp *disp = (void *)engine; |
70cabe4a BS |
1152 | struct nv50_disp_base *base; |
1153 | int ret; | |
1154 | ||
878da15a | 1155 | ret = nvkm_parent_create(parent, engine, oclass, 0, |
fd166a18 | 1156 | disp->sclass, 0, &base); |
70cabe4a BS |
1157 | *pobject = nv_object(base); |
1158 | if (ret) | |
1159 | return ret; | |
1160 | ||
878da15a BS |
1161 | return nvkm_ramht_new(nv_object(base), nv_object(base), 0x1000, 0, |
1162 | &base->ramht); | |
70cabe4a BS |
1163 | } |
1164 | ||
79ca2770 | 1165 | void |
878da15a | 1166 | nv50_disp_main_dtor(struct nvkm_object *object) |
70cabe4a BS |
1167 | { |
1168 | struct nv50_disp_base *base = (void *)object; | |
878da15a BS |
1169 | nvkm_ramht_ref(NULL, &base->ramht); |
1170 | nvkm_parent_destroy(&base->base); | |
70cabe4a BS |
1171 | } |
1172 | ||
1173 | static int | |
878da15a | 1174 | nv50_disp_main_init(struct nvkm_object *object) |
70cabe4a | 1175 | { |
fd166a18 | 1176 | struct nv50_disp *disp = (void *)object->engine; |
70cabe4a | 1177 | struct nv50_disp_base *base = (void *)object; |
2fde1f1c | 1178 | struct nvkm_device *device = disp->base.engine.subdev.device; |
ab77214a BS |
1179 | int ret, i; |
1180 | u32 tmp; | |
70cabe4a | 1181 | |
878da15a | 1182 | ret = nvkm_parent_init(&base->base); |
70cabe4a BS |
1183 | if (ret) |
1184 | return ret; | |
1185 | ||
ab77214a BS |
1186 | /* The below segments of code copying values from one register to |
1187 | * another appear to inform EVO of the display capabilities or | |
1188 | * something similar. NFI what the 0x614004 caps are for.. | |
1189 | */ | |
2fde1f1c BS |
1190 | tmp = nvkm_rd32(device, 0x614004); |
1191 | nvkm_wr32(device, 0x610184, tmp); | |
ab77214a BS |
1192 | |
1193 | /* ... CRTC caps */ | |
fd166a18 | 1194 | for (i = 0; i < disp->head.nr; i++) { |
2fde1f1c BS |
1195 | tmp = nvkm_rd32(device, 0x616100 + (i * 0x800)); |
1196 | nvkm_wr32(device, 0x610190 + (i * 0x10), tmp); | |
1197 | tmp = nvkm_rd32(device, 0x616104 + (i * 0x800)); | |
1198 | nvkm_wr32(device, 0x610194 + (i * 0x10), tmp); | |
1199 | tmp = nvkm_rd32(device, 0x616108 + (i * 0x800)); | |
1200 | nvkm_wr32(device, 0x610198 + (i * 0x10), tmp); | |
1201 | tmp = nvkm_rd32(device, 0x61610c + (i * 0x800)); | |
1202 | nvkm_wr32(device, 0x61019c + (i * 0x10), tmp); | |
ab77214a BS |
1203 | } |
1204 | ||
1205 | /* ... DAC caps */ | |
fd166a18 | 1206 | for (i = 0; i < disp->dac.nr; i++) { |
2fde1f1c BS |
1207 | tmp = nvkm_rd32(device, 0x61a000 + (i * 0x800)); |
1208 | nvkm_wr32(device, 0x6101d0 + (i * 0x04), tmp); | |
ab77214a BS |
1209 | } |
1210 | ||
1211 | /* ... SOR caps */ | |
fd166a18 | 1212 | for (i = 0; i < disp->sor.nr; i++) { |
2fde1f1c BS |
1213 | tmp = nvkm_rd32(device, 0x61c000 + (i * 0x800)); |
1214 | nvkm_wr32(device, 0x6101e0 + (i * 0x04), tmp); | |
ab77214a BS |
1215 | } |
1216 | ||
476e84e1 | 1217 | /* ... PIOR caps */ |
fd166a18 | 1218 | for (i = 0; i < disp->pior.nr; i++) { |
2fde1f1c BS |
1219 | tmp = nvkm_rd32(device, 0x61e000 + (i * 0x800)); |
1220 | nvkm_wr32(device, 0x6101f0 + (i * 0x04), tmp); | |
ab77214a BS |
1221 | } |
1222 | ||
446b05a0 | 1223 | /* steal display away from vbios, or something like that */ |
2fde1f1c BS |
1224 | if (nvkm_rd32(device, 0x610024) & 0x00000100) { |
1225 | nvkm_wr32(device, 0x610024, 0x00000100); | |
1226 | nvkm_mask(device, 0x6194e8, 0x00000001, 0x00000000); | |
3a020b4d BS |
1227 | if (nvkm_msec(device, 2000, |
1228 | if (!(nvkm_rd32(device, 0x6194e8) & 0x00000002)) | |
1229 | break; | |
1230 | ) < 0) | |
446b05a0 | 1231 | return -EBUSY; |
446b05a0 BS |
1232 | } |
1233 | ||
1234 | /* point at display engine memory area (hash table, objects) */ | |
2fde1f1c | 1235 | nvkm_wr32(device, 0x610010, (nv_gpuobj(base->ramht)->addr >> 8) | 9); |
446b05a0 BS |
1236 | |
1237 | /* enable supervisor interrupts, disable everything else */ | |
2fde1f1c BS |
1238 | nvkm_wr32(device, 0x61002c, 0x00000370); |
1239 | nvkm_wr32(device, 0x610028, 0x00000000); | |
70cabe4a BS |
1240 | return 0; |
1241 | } | |
1242 | ||
1243 | static int | |
878da15a | 1244 | nv50_disp_main_fini(struct nvkm_object *object, bool suspend) |
70cabe4a | 1245 | { |
fd166a18 | 1246 | struct nv50_disp *disp = (void *)object->engine; |
70cabe4a | 1247 | struct nv50_disp_base *base = (void *)object; |
2fde1f1c | 1248 | struct nvkm_device *device = disp->base.engine.subdev.device; |
446b05a0 BS |
1249 | |
1250 | /* disable all interrupts */ | |
2fde1f1c BS |
1251 | nvkm_wr32(device, 0x610024, 0x00000000); |
1252 | nvkm_wr32(device, 0x610020, 0x00000000); | |
446b05a0 | 1253 | |
878da15a | 1254 | return nvkm_parent_fini(&base->base, suspend); |
70cabe4a BS |
1255 | } |
1256 | ||
878da15a | 1257 | struct nvkm_ofuncs |
2832271d BS |
1258 | nv50_disp_main_ofuncs = { |
1259 | .ctor = nv50_disp_main_ctor, | |
1260 | .dtor = nv50_disp_main_dtor, | |
1261 | .init = nv50_disp_main_init, | |
1262 | .fini = nv50_disp_main_fini, | |
1263 | .mthd = nv50_disp_main_mthd, | |
878da15a | 1264 | .ntfy = nvkm_disp_ntfy, |
70cabe4a BS |
1265 | }; |
1266 | ||
878da15a | 1267 | static struct nvkm_oclass |
2832271d BS |
1268 | nv50_disp_main_oclass[] = { |
1269 | { NV50_DISP, &nv50_disp_main_ofuncs }, | |
370c00f9 | 1270 | {} |
ebb945a9 BS |
1271 | }; |
1272 | ||
878da15a | 1273 | static struct nvkm_oclass |
ebb945a9 | 1274 | nv50_disp_sclass[] = { |
2832271d BS |
1275 | { NV50_DISP_CORE_CHANNEL_DMA, &nv50_disp_core_ofuncs.base }, |
1276 | { NV50_DISP_BASE_CHANNEL_DMA, &nv50_disp_base_ofuncs.base }, | |
648d4dfd BS |
1277 | { NV50_DISP_OVERLAY_CHANNEL_DMA, &nv50_disp_ovly_ofuncs.base }, |
1278 | { NV50_DISP_OVERLAY, &nv50_disp_oimm_ofuncs.base }, | |
1279 | { NV50_DISP_CURSOR, &nv50_disp_curs_ofuncs.base }, | |
70cabe4a | 1280 | {} |
ebb945a9 BS |
1281 | }; |
1282 | ||
70cabe4a BS |
1283 | /******************************************************************************* |
1284 | * Display context, tracks instmem allocation and prevents more than one | |
1285 | * client using the display hardware at any time. | |
1286 | ******************************************************************************/ | |
1287 | ||
1288 | static int | |
878da15a BS |
1289 | nv50_disp_data_ctor(struct nvkm_object *parent, |
1290 | struct nvkm_object *engine, | |
1291 | struct nvkm_oclass *oclass, void *data, u32 size, | |
1292 | struct nvkm_object **pobject) | |
70cabe4a | 1293 | { |
fd166a18 | 1294 | struct nv50_disp *disp = (void *)engine; |
a1e88736 BS |
1295 | struct nvkm_gpuobj *gpuobj; |
1296 | int ret; | |
70cabe4a | 1297 | |
370c00f9 | 1298 | /* no context needed for channel objects... */ |
586491e6 | 1299 | if (nv_mclass(parent) != NV_DEVICE) { |
370c00f9 BS |
1300 | atomic_inc(&parent->refcount); |
1301 | *pobject = parent; | |
43e6e51c | 1302 | return 1; |
370c00f9 | 1303 | } |
70cabe4a | 1304 | |
370c00f9 | 1305 | /* allocate display hardware to client */ |
a1e88736 BS |
1306 | ret = nvkm_gpuobj_create(parent, engine, oclass, 0, NULL, |
1307 | 0x10000, 0x10000, NVOBJ_FLAG_HEAP, | |
1308 | &gpuobj); | |
1309 | *pobject = nv_object(gpuobj); | |
fd166a18 | 1310 | mutex_lock(&nv_subdev(disp)->mutex); |
a1e88736 BS |
1311 | if (!list_empty(&nv_engine(disp)->contexts)) |
1312 | ret = -EBUSY; | |
fd166a18 | 1313 | mutex_unlock(&nv_subdev(disp)->mutex); |
370c00f9 | 1314 | return ret; |
70cabe4a BS |
1315 | } |
1316 | ||
878da15a | 1317 | struct nvkm_oclass |
70cabe4a | 1318 | nv50_disp_cclass = { |
878da15a | 1319 | .ofuncs = &(struct nvkm_ofuncs) { |
70cabe4a | 1320 | .ctor = nv50_disp_data_ctor, |
a1e88736 BS |
1321 | .dtor = _nvkm_gpuobj_dtor, |
1322 | .init = _nvkm_gpuobj_init, | |
1323 | .fini = _nvkm_gpuobj_fini, | |
1324 | .rd32 = _nvkm_gpuobj_rd32, | |
1325 | .wr32 = _nvkm_gpuobj_wr32, | |
70cabe4a BS |
1326 | }, |
1327 | }; | |
1328 | ||
1329 | /******************************************************************************* | |
1330 | * Display engine implementation | |
1331 | ******************************************************************************/ | |
1332 | ||
79ca2770 BS |
1333 | static void |
1334 | nv50_disp_vblank_fini(struct nvkm_event *event, int type, int head) | |
1335 | { | |
878da15a | 1336 | struct nvkm_disp *disp = container_of(event, typeof(*disp), vblank); |
2fde1f1c BS |
1337 | struct nvkm_device *device = disp->engine.subdev.device; |
1338 | nvkm_mask(device, 0x61002c, (4 << head), 0); | |
79ca2770 BS |
1339 | } |
1340 | ||
1341 | static void | |
1342 | nv50_disp_vblank_init(struct nvkm_event *event, int type, int head) | |
1343 | { | |
878da15a | 1344 | struct nvkm_disp *disp = container_of(event, typeof(*disp), vblank); |
2fde1f1c BS |
1345 | struct nvkm_device *device = disp->engine.subdev.device; |
1346 | nvkm_mask(device, 0x61002c, (4 << head), (4 << head)); | |
79ca2770 BS |
1347 | } |
1348 | ||
1349 | const struct nvkm_event_func | |
1350 | nv50_disp_vblank_func = { | |
878da15a | 1351 | .ctor = nvkm_disp_vblank_ctor, |
79ca2770 BS |
1352 | .init = nv50_disp_vblank_init, |
1353 | .fini = nv50_disp_vblank_fini, | |
1354 | }; | |
1355 | ||
878da15a | 1356 | static const struct nvkm_enum |
117e1633 BS |
1357 | nv50_disp_intr_error_type[] = { |
1358 | { 3, "ILLEGAL_MTHD" }, | |
1359 | { 4, "INVALID_VALUE" }, | |
1360 | { 5, "INVALID_STATE" }, | |
1361 | { 7, "INVALID_HANDLE" }, | |
1362 | {} | |
1363 | }; | |
186ecad2 | 1364 | |
878da15a | 1365 | static const struct nvkm_enum |
117e1633 BS |
1366 | nv50_disp_intr_error_code[] = { |
1367 | { 0x00, "" }, | |
1368 | {} | |
1369 | }; | |
186ecad2 | 1370 | |
117e1633 | 1371 | static void |
fd166a18 | 1372 | nv50_disp_intr_error(struct nv50_disp *disp, int chid) |
117e1633 | 1373 | { |
fd166a18 | 1374 | struct nv50_disp_impl *impl = (void *)nv_object(disp)->oclass; |
84407824 BS |
1375 | struct nvkm_subdev *subdev = &disp->base.engine.subdev; |
1376 | struct nvkm_device *device = subdev->device; | |
2fde1f1c BS |
1377 | u32 data = nvkm_rd32(device, 0x610084 + (chid * 0x08)); |
1378 | u32 addr = nvkm_rd32(device, 0x610080 + (chid * 0x08)); | |
117e1633 BS |
1379 | u32 code = (addr & 0x00ff0000) >> 16; |
1380 | u32 type = (addr & 0x00007000) >> 12; | |
1381 | u32 mthd = (addr & 0x00000ffc); | |
878da15a | 1382 | const struct nvkm_enum *ec, *et; |
117e1633 | 1383 | |
878da15a | 1384 | et = nvkm_enum_find(nv50_disp_intr_error_type, type); |
878da15a | 1385 | ec = nvkm_enum_find(nv50_disp_intr_error_code, code); |
117e1633 | 1386 | |
84407824 BS |
1387 | nvkm_error(subdev, |
1388 | "ERROR %d [%s] %02x [%s] chid %d mthd %04x data %08x\n", | |
1389 | type, et ? et->name : "", code, ec ? ec->name : "", | |
1390 | chid, mthd, data); | |
117e1633 | 1391 | |
9cf6ba20 BS |
1392 | if (chid == 0) { |
1393 | switch (mthd) { | |
1394 | case 0x0080: | |
fd166a18 | 1395 | nv50_disp_mthd_chan(disp, NV_DBG_ERROR, chid - 0, |
9cf6ba20 BS |
1396 | impl->mthd.core); |
1397 | break; | |
1398 | default: | |
1399 | break; | |
1400 | } | |
1401 | } else | |
1402 | if (chid <= 2) { | |
1403 | switch (mthd) { | |
1404 | case 0x0080: | |
fd166a18 | 1405 | nv50_disp_mthd_chan(disp, NV_DBG_ERROR, chid - 1, |
9cf6ba20 BS |
1406 | impl->mthd.base); |
1407 | break; | |
1408 | default: | |
1409 | break; | |
1410 | } | |
1411 | } else | |
1412 | if (chid <= 4) { | |
1413 | switch (mthd) { | |
1414 | case 0x0080: | |
fd166a18 | 1415 | nv50_disp_mthd_chan(disp, NV_DBG_ERROR, chid - 3, |
9cf6ba20 BS |
1416 | impl->mthd.ovly); |
1417 | break; | |
1418 | default: | |
1419 | break; | |
1420 | } | |
1421 | } | |
1422 | ||
2fde1f1c BS |
1423 | nvkm_wr32(device, 0x610020, 0x00010000 << chid); |
1424 | nvkm_wr32(device, 0x610080 + (chid * 0x08), 0x90000000); | |
186ecad2 BS |
1425 | } |
1426 | ||
415f12ef | 1427 | static struct nvkm_output * |
fd166a18 | 1428 | exec_lookup(struct nv50_disp *disp, int head, int or, u32 ctrl, |
415f12ef | 1429 | u32 *data, u8 *ver, u8 *hdr, u8 *cnt, u8 *len, |
186ecad2 BS |
1430 | struct nvbios_outp *info) |
1431 | { | |
84407824 BS |
1432 | struct nvkm_subdev *subdev = &disp->base.engine.subdev; |
1433 | struct nvkm_bios *bios = subdev->device->bios; | |
415f12ef BS |
1434 | struct nvkm_output *outp; |
1435 | u16 mask, type; | |
186ecad2 | 1436 | |
415f12ef | 1437 | if (or < 4) { |
186ecad2 BS |
1438 | type = DCB_OUTPUT_ANALOG; |
1439 | mask = 0; | |
476e84e1 | 1440 | } else |
415f12ef | 1441 | if (or < 8) { |
186ecad2 BS |
1442 | switch (ctrl & 0x00000f00) { |
1443 | case 0x00000000: type = DCB_OUTPUT_LVDS; mask = 1; break; | |
1444 | case 0x00000100: type = DCB_OUTPUT_TMDS; mask = 1; break; | |
1445 | case 0x00000200: type = DCB_OUTPUT_TMDS; mask = 2; break; | |
1446 | case 0x00000500: type = DCB_OUTPUT_TMDS; mask = 3; break; | |
1447 | case 0x00000800: type = DCB_OUTPUT_DP; mask = 1; break; | |
1448 | case 0x00000900: type = DCB_OUTPUT_DP; mask = 2; break; | |
1449 | default: | |
84407824 | 1450 | nvkm_error(subdev, "unknown SOR mc %08x\n", ctrl); |
415f12ef | 1451 | return NULL; |
186ecad2 | 1452 | } |
415f12ef | 1453 | or -= 4; |
476e84e1 | 1454 | } else { |
415f12ef | 1455 | or = or - 8; |
476e84e1 BS |
1456 | type = 0x0010; |
1457 | mask = 0; | |
1458 | switch (ctrl & 0x00000f00) { | |
fd166a18 | 1459 | case 0x00000000: type |= disp->pior.type[or]; break; |
476e84e1 | 1460 | default: |
84407824 | 1461 | nvkm_error(subdev, "unknown PIOR mc %08x\n", ctrl); |
415f12ef | 1462 | return NULL; |
476e84e1 | 1463 | } |
186ecad2 BS |
1464 | } |
1465 | ||
1466 | mask = 0x00c0 & (mask << 6); | |
415f12ef | 1467 | mask |= 0x0001 << or; |
186ecad2 BS |
1468 | mask |= 0x0100 << head; |
1469 | ||
fd166a18 | 1470 | list_for_each_entry(outp, &disp->base.outp, head) { |
415f12ef BS |
1471 | if ((outp->info.hasht & 0xff) == type && |
1472 | (outp->info.hashm & mask) == mask) { | |
1473 | *data = nvbios_outp_match(bios, outp->info.hasht, | |
1474 | outp->info.hashm, | |
1475 | ver, hdr, cnt, len, info); | |
1476 | if (!*data) | |
1477 | return NULL; | |
1478 | return outp; | |
1479 | } | |
1480 | } | |
476e84e1 | 1481 | |
415f12ef | 1482 | return NULL; |
186ecad2 BS |
1483 | } |
1484 | ||
1ae5a62b | 1485 | static struct nvkm_output * |
fd166a18 | 1486 | exec_script(struct nv50_disp *disp, int head, int id) |
186ecad2 | 1487 | { |
2fde1f1c BS |
1488 | struct nvkm_device *device = disp->base.engine.subdev.device; |
1489 | struct nvkm_bios *bios = device->bios; | |
415f12ef | 1490 | struct nvkm_output *outp; |
186ecad2 | 1491 | struct nvbios_outp info; |
186ecad2 | 1492 | u8 ver, hdr, cnt, len; |
415f12ef | 1493 | u32 data, ctrl = 0; |
b969fa52 | 1494 | u32 reg; |
186ecad2 BS |
1495 | int i; |
1496 | ||
476e84e1 | 1497 | /* DAC */ |
fd166a18 | 1498 | for (i = 0; !(ctrl & (1 << head)) && i < disp->dac.nr; i++) |
2fde1f1c | 1499 | ctrl = nvkm_rd32(device, 0x610b5c + (i * 8)); |
186ecad2 | 1500 | |
476e84e1 | 1501 | /* SOR */ |
c684cef7 | 1502 | if (!(ctrl & (1 << head))) { |
fd166a18 BS |
1503 | if (nv_device(disp)->chipset < 0x90 || |
1504 | nv_device(disp)->chipset == 0x92 || | |
1505 | nv_device(disp)->chipset == 0xa0) { | |
b969fa52 | 1506 | reg = 0x610b74; |
c684cef7 | 1507 | } else { |
b969fa52 | 1508 | reg = 0x610798; |
c684cef7 | 1509 | } |
fd166a18 | 1510 | for (i = 0; !(ctrl & (1 << head)) && i < disp->sor.nr; i++) |
2fde1f1c | 1511 | ctrl = nvkm_rd32(device, reg + (i * 8)); |
b969fa52 | 1512 | i += 4; |
186ecad2 BS |
1513 | } |
1514 | ||
476e84e1 BS |
1515 | /* PIOR */ |
1516 | if (!(ctrl & (1 << head))) { | |
fd166a18 | 1517 | for (i = 0; !(ctrl & (1 << head)) && i < disp->pior.nr; i++) |
2fde1f1c | 1518 | ctrl = nvkm_rd32(device, 0x610b84 + (i * 8)); |
476e84e1 BS |
1519 | i += 8; |
1520 | } | |
1521 | ||
186ecad2 | 1522 | if (!(ctrl & (1 << head))) |
1ae5a62b | 1523 | return NULL; |
c684cef7 | 1524 | i--; |
186ecad2 | 1525 | |
fd166a18 | 1526 | outp = exec_lookup(disp, head, i, ctrl, &data, &ver, &hdr, &cnt, &len, &info); |
415f12ef | 1527 | if (outp) { |
186ecad2 | 1528 | struct nvbios_init init = { |
fd166a18 | 1529 | .subdev = nv_subdev(disp), |
186ecad2 BS |
1530 | .bios = bios, |
1531 | .offset = info.script[id], | |
415f12ef | 1532 | .outp = &outp->info, |
186ecad2 BS |
1533 | .crtc = head, |
1534 | .execute = 1, | |
1535 | }; | |
1536 | ||
1ae5a62b | 1537 | nvbios_exec(&init); |
186ecad2 BS |
1538 | } |
1539 | ||
1ae5a62b | 1540 | return outp; |
186ecad2 BS |
1541 | } |
1542 | ||
415f12ef | 1543 | static struct nvkm_output * |
fd166a18 | 1544 | exec_clkcmp(struct nv50_disp *disp, int head, int id, u32 pclk, u32 *conf) |
186ecad2 | 1545 | { |
2fde1f1c BS |
1546 | struct nvkm_device *device = disp->base.engine.subdev.device; |
1547 | struct nvkm_bios *bios = device->bios; | |
415f12ef | 1548 | struct nvkm_output *outp; |
186ecad2 BS |
1549 | struct nvbios_outp info1; |
1550 | struct nvbios_ocfg info2; | |
1551 | u8 ver, hdr, cnt, len; | |
415f12ef | 1552 | u32 data, ctrl = 0; |
b969fa52 | 1553 | u32 reg; |
186ecad2 BS |
1554 | int i; |
1555 | ||
476e84e1 | 1556 | /* DAC */ |
fd166a18 | 1557 | for (i = 0; !(ctrl & (1 << head)) && i < disp->dac.nr; i++) |
2fde1f1c | 1558 | ctrl = nvkm_rd32(device, 0x610b58 + (i * 8)); |
186ecad2 | 1559 | |
476e84e1 | 1560 | /* SOR */ |
c684cef7 | 1561 | if (!(ctrl & (1 << head))) { |
fd166a18 BS |
1562 | if (nv_device(disp)->chipset < 0x90 || |
1563 | nv_device(disp)->chipset == 0x92 || | |
1564 | nv_device(disp)->chipset == 0xa0) { | |
b969fa52 | 1565 | reg = 0x610b70; |
c684cef7 | 1566 | } else { |
b969fa52 | 1567 | reg = 0x610794; |
c684cef7 | 1568 | } |
fd166a18 | 1569 | for (i = 0; !(ctrl & (1 << head)) && i < disp->sor.nr; i++) |
2fde1f1c | 1570 | ctrl = nvkm_rd32(device, reg + (i * 8)); |
b969fa52 | 1571 | i += 4; |
186ecad2 BS |
1572 | } |
1573 | ||
476e84e1 BS |
1574 | /* PIOR */ |
1575 | if (!(ctrl & (1 << head))) { | |
fd166a18 | 1576 | for (i = 0; !(ctrl & (1 << head)) && i < disp->pior.nr; i++) |
2fde1f1c | 1577 | ctrl = nvkm_rd32(device, 0x610b80 + (i * 8)); |
476e84e1 BS |
1578 | i += 8; |
1579 | } | |
1580 | ||
186ecad2 | 1581 | if (!(ctrl & (1 << head))) |
415f12ef | 1582 | return NULL; |
c684cef7 | 1583 | i--; |
186ecad2 | 1584 | |
fd166a18 | 1585 | outp = exec_lookup(disp, head, i, ctrl, &data, &ver, &hdr, &cnt, &len, &info1); |
ba5e01b0 | 1586 | if (!outp) |
415f12ef | 1587 | return NULL; |
186ecad2 | 1588 | |
415f12ef BS |
1589 | if (outp->info.location == 0) { |
1590 | switch (outp->info.type) { | |
476e84e1 | 1591 | case DCB_OUTPUT_TMDS: |
415f12ef | 1592 | *conf = (ctrl & 0x00000f00) >> 8; |
476e84e1 | 1593 | if (pclk >= 165000) |
415f12ef | 1594 | *conf |= 0x0100; |
476e84e1 BS |
1595 | break; |
1596 | case DCB_OUTPUT_LVDS: | |
fd166a18 | 1597 | *conf = disp->sor.lvdsconf; |
476e84e1 BS |
1598 | break; |
1599 | case DCB_OUTPUT_DP: | |
415f12ef | 1600 | *conf = (ctrl & 0x00000f00) >> 8; |
476e84e1 BS |
1601 | break; |
1602 | case DCB_OUTPUT_ANALOG: | |
1603 | default: | |
415f12ef | 1604 | *conf = 0x00ff; |
476e84e1 BS |
1605 | break; |
1606 | } | |
1607 | } else { | |
415f12ef | 1608 | *conf = (ctrl & 0x00000f00) >> 8; |
476e84e1 | 1609 | pclk = pclk / 2; |
186ecad2 BS |
1610 | } |
1611 | ||
415f12ef | 1612 | data = nvbios_ocfg_match(bios, data, *conf, &ver, &hdr, &cnt, &len, &info2); |
0a0afd28 | 1613 | if (data && id < 0xff) { |
186ecad2 BS |
1614 | data = nvbios_oclk_match(bios, info2.clkcmp[id], pclk); |
1615 | if (data) { | |
1616 | struct nvbios_init init = { | |
fd166a18 | 1617 | .subdev = nv_subdev(disp), |
186ecad2 BS |
1618 | .bios = bios, |
1619 | .offset = data, | |
415f12ef | 1620 | .outp = &outp->info, |
186ecad2 BS |
1621 | .crtc = head, |
1622 | .execute = 1, | |
1623 | }; | |
1624 | ||
46c13c13 | 1625 | nvbios_exec(&init); |
186ecad2 BS |
1626 | } |
1627 | } | |
1628 | ||
415f12ef | 1629 | return outp; |
186ecad2 BS |
1630 | } |
1631 | ||
1632 | static void | |
fd166a18 | 1633 | nv50_disp_intr_unk10_0(struct nv50_disp *disp, int head) |
186ecad2 | 1634 | { |
fd166a18 | 1635 | exec_script(disp, head, 1); |
16d4c031 | 1636 | } |
186ecad2 | 1637 | |
16d4c031 | 1638 | static void |
fd166a18 | 1639 | nv50_disp_intr_unk20_0(struct nv50_disp *disp, int head) |
16d4c031 | 1640 | { |
fd166a18 | 1641 | struct nvkm_output *outp = exec_script(disp, head, 2); |
1ae5a62b BS |
1642 | |
1643 | /* the binary driver does this outside of the supervisor handling | |
1644 | * (after the third supervisor from a detach). we (currently?) | |
1645 | * allow both detach/attach to happen in the same set of | |
1646 | * supervisor interrupts, so it would make sense to execute this | |
1647 | * (full power down?) script after all the detach phases of the | |
1648 | * supervisor handling. like with training if needed from the | |
1649 | * second supervisor, nvidia doesn't do this, so who knows if it's | |
1650 | * entirely safe, but it does appear to work.. | |
1651 | * | |
1652 | * without this script being run, on some configurations i've | |
1653 | * seen, switching from DP to TMDS on a DP connector may result | |
1654 | * in a blank screen (SOR_PWR off/on can restore it) | |
1655 | */ | |
1656 | if (outp && outp->info.type == DCB_OUTPUT_DP) { | |
f2c906fc | 1657 | struct nvkm_output_dp *outpdp = nvkm_output_dp(outp); |
1ae5a62b | 1658 | struct nvbios_init init = { |
fd166a18 BS |
1659 | .subdev = nv_subdev(disp), |
1660 | .bios = nvkm_bios(disp), | |
1ae5a62b BS |
1661 | .outp = &outp->info, |
1662 | .crtc = head, | |
1663 | .offset = outpdp->info.script[4], | |
1664 | .execute = 1, | |
1665 | }; | |
1666 | ||
1667 | nvbios_exec(&init); | |
1668 | atomic_set(&outpdp->lt.done, 0); | |
1669 | } | |
186ecad2 BS |
1670 | } |
1671 | ||
1672 | static void | |
fd166a18 | 1673 | nv50_disp_intr_unk20_1(struct nv50_disp *disp, int head) |
16d4c031 | 1674 | { |
2fde1f1c BS |
1675 | struct nvkm_device *device = disp->base.engine.subdev.device; |
1676 | struct nvkm_devinit *devinit = device->devinit; | |
1677 | u32 pclk = nvkm_rd32(device, 0x610ad0 + (head * 0x540)) & 0x3fffff; | |
16d4c031 | 1678 | if (pclk) |
88524bc0 | 1679 | devinit->pll_set(devinit, PLL_VPLL0 + head, pclk); |
16d4c031 BS |
1680 | } |
1681 | ||
1682 | static void | |
fd166a18 | 1683 | nv50_disp_intr_unk20_2_dp(struct nv50_disp *disp, int head, |
16d4c031 | 1684 | struct dcb_output *outp, u32 pclk) |
186ecad2 | 1685 | { |
84407824 BS |
1686 | struct nvkm_subdev *subdev = &disp->base.engine.subdev; |
1687 | struct nvkm_device *device = subdev->device; | |
186ecad2 BS |
1688 | const int link = !(outp->sorconf.link & 1); |
1689 | const int or = ffs(outp->or) - 1; | |
1690 | const u32 soff = ( or * 0x800); | |
1691 | const u32 loff = (link * 0x080) + soff; | |
2fde1f1c | 1692 | const u32 ctrl = nvkm_rd32(device, 0x610794 + (or * 8)); |
186ecad2 | 1693 | const u32 symbol = 100000; |
2fde1f1c BS |
1694 | const s32 vactive = nvkm_rd32(device, 0x610af8 + (head * 0x540)) & 0xffff; |
1695 | const s32 vblanke = nvkm_rd32(device, 0x610ae8 + (head * 0x540)) & 0xffff; | |
1696 | const s32 vblanks = nvkm_rd32(device, 0x610af0 + (head * 0x540)) & 0xffff; | |
1697 | u32 dpctrl = nvkm_rd32(device, 0x61c10c + loff); | |
1698 | u32 clksor = nvkm_rd32(device, 0x614300 + soff); | |
186ecad2 BS |
1699 | int bestTU = 0, bestVTUi = 0, bestVTUf = 0, bestVTUa = 0; |
1700 | int TU, VTUi, VTUf, VTUa; | |
1701 | u64 link_data_rate, link_ratio, unk; | |
1702 | u32 best_diff = 64 * symbol; | |
c354080d | 1703 | u32 link_nr, link_bw, bits; |
9506140f BS |
1704 | u64 value; |
1705 | ||
1706 | link_bw = (clksor & 0x000c0000) ? 270000 : 162000; | |
1707 | link_nr = hweight32(dpctrl & 0x000f0000); | |
1708 | ||
1709 | /* symbols/hblank - algorithm taken from comments in tegra driver */ | |
1710 | value = vblanke + vactive - vblanks - 7; | |
1711 | value = value * link_bw; | |
1712 | do_div(value, pclk); | |
1713 | value = value - (3 * !!(dpctrl & 0x00004000)) - (12 / link_nr); | |
2fde1f1c | 1714 | nvkm_mask(device, 0x61c1e8 + soff, 0x0000ffff, value); |
9506140f BS |
1715 | |
1716 | /* symbols/vblank - algorithm taken from comments in tegra driver */ | |
1717 | value = vblanks - vblanke - 25; | |
1718 | value = value * link_bw; | |
1719 | do_div(value, pclk); | |
1720 | value = value - ((36 / link_nr) + 3) - 1; | |
2fde1f1c | 1721 | nvkm_mask(device, 0x61c1ec + soff, 0x00ffffff, value); |
9506140f BS |
1722 | |
1723 | /* watermark / activesym */ | |
bf2c886a BS |
1724 | if ((ctrl & 0xf0000) == 0x60000) bits = 30; |
1725 | else if ((ctrl & 0xf0000) == 0x50000) bits = 24; | |
1726 | else bits = 18; | |
1727 | ||
186ecad2 BS |
1728 | link_data_rate = (pclk * bits / 8) / link_nr; |
1729 | ||
1730 | /* calculate ratio of packed data rate to link symbol rate */ | |
1731 | link_ratio = link_data_rate * symbol; | |
c354080d | 1732 | do_div(link_ratio, link_bw); |
186ecad2 BS |
1733 | |
1734 | for (TU = 64; TU >= 32; TU--) { | |
1735 | /* calculate average number of valid symbols in each TU */ | |
1736 | u32 tu_valid = link_ratio * TU; | |
1737 | u32 calc, diff; | |
1738 | ||
1739 | /* find a hw representation for the fraction.. */ | |
1740 | VTUi = tu_valid / symbol; | |
1741 | calc = VTUi * symbol; | |
1742 | diff = tu_valid - calc; | |
1743 | if (diff) { | |
1744 | if (diff >= (symbol / 2)) { | |
1745 | VTUf = symbol / (symbol - diff); | |
1746 | if (symbol - (VTUf * diff)) | |
1747 | VTUf++; | |
1748 | ||
1749 | if (VTUf <= 15) { | |
1750 | VTUa = 1; | |
1751 | calc += symbol - (symbol / VTUf); | |
1752 | } else { | |
1753 | VTUa = 0; | |
1754 | VTUf = 1; | |
1755 | calc += symbol; | |
1756 | } | |
1757 | } else { | |
1758 | VTUa = 0; | |
1759 | VTUf = min((int)(symbol / diff), 15); | |
1760 | calc += symbol / VTUf; | |
1761 | } | |
1762 | ||
1763 | diff = calc - tu_valid; | |
1764 | } else { | |
1765 | /* no remainder, but the hw doesn't like the fractional | |
1766 | * part to be zero. decrement the integer part and | |
1767 | * have the fraction add a whole symbol back | |
1768 | */ | |
1769 | VTUa = 0; | |
1770 | VTUf = 1; | |
1771 | VTUi--; | |
1772 | } | |
1773 | ||
1774 | if (diff < best_diff) { | |
1775 | best_diff = diff; | |
1776 | bestTU = TU; | |
1777 | bestVTUa = VTUa; | |
1778 | bestVTUf = VTUf; | |
1779 | bestVTUi = VTUi; | |
1780 | if (diff == 0) | |
1781 | break; | |
1782 | } | |
1783 | } | |
1784 | ||
1785 | if (!bestTU) { | |
84407824 | 1786 | nvkm_error(subdev, "unable to find suitable dp config\n"); |
186ecad2 BS |
1787 | return; |
1788 | } | |
1789 | ||
1790 | /* XXX close to vbios numbers, but not right */ | |
1791 | unk = (symbol - link_ratio) * bestTU; | |
1792 | unk *= link_ratio; | |
c354080d BS |
1793 | do_div(unk, symbol); |
1794 | do_div(unk, symbol); | |
186ecad2 BS |
1795 | unk += 6; |
1796 | ||
2fde1f1c BS |
1797 | nvkm_mask(device, 0x61c10c + loff, 0x000001fc, bestTU << 2); |
1798 | nvkm_mask(device, 0x61c128 + loff, 0x010f7f3f, bestVTUa << 24 | | |
186ecad2 BS |
1799 | bestVTUf << 16 | |
1800 | bestVTUi << 8 | unk); | |
1801 | } | |
1802 | ||
1803 | static void | |
fd166a18 | 1804 | nv50_disp_intr_unk20_2(struct nv50_disp *disp, int head) |
186ecad2 | 1805 | { |
2fde1f1c | 1806 | struct nvkm_device *device = disp->base.engine.subdev.device; |
415f12ef | 1807 | struct nvkm_output *outp; |
2fde1f1c | 1808 | u32 pclk = nvkm_rd32(device, 0x610ad0 + (head * 0x540)) & 0x3fffff; |
16d4c031 BS |
1809 | u32 hval, hreg = 0x614200 + (head * 0x800); |
1810 | u32 oval, oreg; | |
415f12ef | 1811 | u32 mask, conf; |
0a0afd28 | 1812 | |
fd166a18 | 1813 | outp = exec_clkcmp(disp, head, 0xff, pclk, &conf); |
415f12ef BS |
1814 | if (!outp) |
1815 | return; | |
186ecad2 | 1816 | |
55f083c3 BS |
1817 | /* we allow both encoder attach and detach operations to occur |
1818 | * within a single supervisor (ie. modeset) sequence. the | |
1819 | * encoder detach scripts quite often switch off power to the | |
1820 | * lanes, which requires the link to be re-trained. | |
1821 | * | |
1822 | * this is not generally an issue as the sink "must" (heh) | |
1823 | * signal an irq when it's lost sync so the driver can | |
1824 | * re-train. | |
1825 | * | |
1826 | * however, on some boards, if one does not configure at least | |
1827 | * the gpu side of the link *before* attaching, then various | |
1828 | * things can go horribly wrong (PDISP disappearing from mmio, | |
1829 | * third supervisor never happens, etc). | |
1830 | * | |
1831 | * the solution is simply to retrain here, if necessary. last | |
1832 | * i checked, the binary driver userspace does not appear to | |
1833 | * trigger this situation (it forces an UPDATE between steps). | |
1834 | */ | |
b17932c0 | 1835 | if (outp->info.type == DCB_OUTPUT_DP) { |
415f12ef | 1836 | u32 soff = (ffs(outp->info.or) - 1) * 0x08; |
b17932c0 BS |
1837 | u32 ctrl, datarate; |
1838 | ||
1839 | if (outp->info.location == 0) { | |
2fde1f1c | 1840 | ctrl = nvkm_rd32(device, 0x610794 + soff); |
b17932c0 BS |
1841 | soff = 1; |
1842 | } else { | |
2fde1f1c | 1843 | ctrl = nvkm_rd32(device, 0x610b80 + soff); |
b17932c0 BS |
1844 | soff = 2; |
1845 | } | |
415f12ef BS |
1846 | |
1847 | switch ((ctrl & 0x000f0000) >> 16) { | |
0713b451 BS |
1848 | case 6: datarate = pclk * 30; break; |
1849 | case 5: datarate = pclk * 24; break; | |
415f12ef BS |
1850 | case 2: |
1851 | default: | |
0713b451 | 1852 | datarate = pclk * 18; |
415f12ef | 1853 | break; |
186ecad2 | 1854 | } |
186ecad2 | 1855 | |
55f083c3 | 1856 | if (nvkm_output_dp_train(outp, datarate / soff, true)) |
f2c906fc | 1857 | OUTP_ERR(outp, "link not trained before attach"); |
415f12ef BS |
1858 | } |
1859 | ||
fd166a18 | 1860 | exec_clkcmp(disp, head, 0, pclk, &conf); |
415f12ef BS |
1861 | |
1862 | if (!outp->info.location && outp->info.type == DCB_OUTPUT_ANALOG) { | |
1863 | oreg = 0x614280 + (ffs(outp->info.or) - 1) * 0x800; | |
1864 | oval = 0x00000000; | |
1865 | hval = 0x00000000; | |
1866 | mask = 0xffffffff; | |
1867 | } else | |
1868 | if (!outp->info.location) { | |
1869 | if (outp->info.type == DCB_OUTPUT_DP) | |
fd166a18 | 1870 | nv50_disp_intr_unk20_2_dp(disp, head, &outp->info, pclk); |
415f12ef BS |
1871 | oreg = 0x614300 + (ffs(outp->info.or) - 1) * 0x800; |
1872 | oval = (conf & 0x0100) ? 0x00000101 : 0x00000000; | |
1873 | hval = 0x00000000; | |
1874 | mask = 0x00000707; | |
1875 | } else { | |
1876 | oreg = 0x614380 + (ffs(outp->info.or) - 1) * 0x800; | |
1877 | oval = 0x00000001; | |
1878 | hval = 0x00000001; | |
1879 | mask = 0x00000707; | |
16d4c031 | 1880 | } |
415f12ef | 1881 | |
2fde1f1c BS |
1882 | nvkm_mask(device, hreg, 0x0000000f, hval); |
1883 | nvkm_mask(device, oreg, mask, oval); | |
186ecad2 BS |
1884 | } |
1885 | ||
1886 | /* If programming a TMDS output on a SOR that can also be configured for | |
1887 | * DisplayPort, make sure NV50_SOR_DP_CTRL_ENABLE is forced off. | |
1888 | * | |
1889 | * It looks like the VBIOS TMDS scripts make an attempt at this, however, | |
1890 | * the VBIOS scripts on at least one board I have only switch it off on | |
1891 | * link 0, causing a blank display if the output has previously been | |
1892 | * programmed for DisplayPort. | |
1893 | */ | |
1894 | static void | |
fd166a18 | 1895 | nv50_disp_intr_unk40_0_tmds(struct nv50_disp *disp, |
878da15a | 1896 | struct dcb_output *outp) |
186ecad2 | 1897 | { |
2fde1f1c BS |
1898 | struct nvkm_device *device = disp->base.engine.subdev.device; |
1899 | struct nvkm_bios *bios = device->bios; | |
186ecad2 BS |
1900 | const int link = !(outp->sorconf.link & 1); |
1901 | const int or = ffs(outp->or) - 1; | |
1902 | const u32 loff = (or * 0x800) + (link * 0x80); | |
1903 | const u16 mask = (outp->sorconf.link << 6) | outp->or; | |
5838ae61 | 1904 | struct dcb_output match; |
186ecad2 BS |
1905 | u8 ver, hdr; |
1906 | ||
5838ae61 | 1907 | if (dcb_outp_match(bios, DCB_OUTPUT_DP, mask, &ver, &hdr, &match)) |
2fde1f1c | 1908 | nvkm_mask(device, 0x61c10c + loff, 0x00000001, 0x00000000); |
186ecad2 BS |
1909 | } |
1910 | ||
1911 | static void | |
fd166a18 | 1912 | nv50_disp_intr_unk40_0(struct nv50_disp *disp, int head) |
186ecad2 | 1913 | { |
2fde1f1c | 1914 | struct nvkm_device *device = disp->base.engine.subdev.device; |
415f12ef | 1915 | struct nvkm_output *outp; |
2fde1f1c | 1916 | u32 pclk = nvkm_rd32(device, 0x610ad0 + (head * 0x540)) & 0x3fffff; |
415f12ef | 1917 | u32 conf; |
16d4c031 | 1918 | |
fd166a18 | 1919 | outp = exec_clkcmp(disp, head, 1, pclk, &conf); |
415f12ef BS |
1920 | if (!outp) |
1921 | return; | |
1922 | ||
1923 | if (outp->info.location == 0 && outp->info.type == DCB_OUTPUT_TMDS) | |
fd166a18 | 1924 | nv50_disp_intr_unk40_0_tmds(disp, &outp->info); |
186ecad2 BS |
1925 | } |
1926 | ||
5cc027f6 BS |
1927 | void |
1928 | nv50_disp_intr_supervisor(struct work_struct *work) | |
186ecad2 | 1929 | { |
fd166a18 BS |
1930 | struct nv50_disp *disp = |
1931 | container_of(work, struct nv50_disp, supervisor); | |
1932 | struct nv50_disp_impl *impl = (void *)nv_object(disp)->oclass; | |
84407824 BS |
1933 | struct nvkm_subdev *subdev = &disp->base.engine.subdev; |
1934 | struct nvkm_device *device = subdev->device; | |
2fde1f1c | 1935 | u32 super = nvkm_rd32(device, 0x610030); |
16d4c031 | 1936 | int head; |
186ecad2 | 1937 | |
84407824 | 1938 | nvkm_debug(subdev, "supervisor %08x %08x\n", disp->super, super); |
186ecad2 | 1939 | |
fd166a18 BS |
1940 | if (disp->super & 0x00000010) { |
1941 | nv50_disp_mthd_chan(disp, NV_DBG_DEBUG, 0, impl->mthd.core); | |
1942 | for (head = 0; head < disp->head.nr; head++) { | |
16d4c031 BS |
1943 | if (!(super & (0x00000020 << head))) |
1944 | continue; | |
1945 | if (!(super & (0x00000080 << head))) | |
1946 | continue; | |
fd166a18 | 1947 | nv50_disp_intr_unk10_0(disp, head); |
16d4c031 BS |
1948 | } |
1949 | } else | |
fd166a18 BS |
1950 | if (disp->super & 0x00000020) { |
1951 | for (head = 0; head < disp->head.nr; head++) { | |
16d4c031 BS |
1952 | if (!(super & (0x00000080 << head))) |
1953 | continue; | |
fd166a18 | 1954 | nv50_disp_intr_unk20_0(disp, head); |
16d4c031 | 1955 | } |
fd166a18 | 1956 | for (head = 0; head < disp->head.nr; head++) { |
16d4c031 BS |
1957 | if (!(super & (0x00000200 << head))) |
1958 | continue; | |
fd166a18 | 1959 | nv50_disp_intr_unk20_1(disp, head); |
16d4c031 | 1960 | } |
fd166a18 | 1961 | for (head = 0; head < disp->head.nr; head++) { |
16d4c031 BS |
1962 | if (!(super & (0x00000080 << head))) |
1963 | continue; | |
fd166a18 | 1964 | nv50_disp_intr_unk20_2(disp, head); |
16d4c031 BS |
1965 | } |
1966 | } else | |
fd166a18 BS |
1967 | if (disp->super & 0x00000040) { |
1968 | for (head = 0; head < disp->head.nr; head++) { | |
16d4c031 BS |
1969 | if (!(super & (0x00000080 << head))) |
1970 | continue; | |
fd166a18 | 1971 | nv50_disp_intr_unk40_0(disp, head); |
16d4c031 BS |
1972 | } |
1973 | } | |
1974 | ||
2fde1f1c | 1975 | nvkm_wr32(device, 0x610030, 0x80000000); |
186ecad2 BS |
1976 | } |
1977 | ||
70cabe4a | 1978 | void |
878da15a | 1979 | nv50_disp_intr(struct nvkm_subdev *subdev) |
ebb945a9 | 1980 | { |
fd166a18 | 1981 | struct nv50_disp *disp = (void *)subdev; |
2fde1f1c BS |
1982 | struct nvkm_device *device = disp->base.engine.subdev.device; |
1983 | u32 intr0 = nvkm_rd32(device, 0x610020); | |
1984 | u32 intr1 = nvkm_rd32(device, 0x610024); | |
ebb945a9 | 1985 | |
117e1633 BS |
1986 | while (intr0 & 0x001f0000) { |
1987 | u32 chid = __ffs(intr0 & 0x001f0000) - 16; | |
fd166a18 | 1988 | nv50_disp_intr_error(disp, chid); |
117e1633 | 1989 | intr0 &= ~(0x00010000 << chid); |
186ecad2 BS |
1990 | } |
1991 | ||
b38a2322 BS |
1992 | while (intr0 & 0x0000001f) { |
1993 | u32 chid = __ffs(intr0 & 0x0000001f); | |
fd166a18 | 1994 | nv50_disp_chan_uevent_send(disp, chid); |
b38a2322 BS |
1995 | intr0 &= ~(0x00000001 << chid); |
1996 | } | |
1997 | ||
186ecad2 | 1998 | if (intr1 & 0x00000004) { |
fd166a18 | 1999 | nvkm_disp_vblank(&disp->base, 0); |
2fde1f1c | 2000 | nvkm_wr32(device, 0x610024, 0x00000004); |
ebb945a9 BS |
2001 | } |
2002 | ||
186ecad2 | 2003 | if (intr1 & 0x00000008) { |
fd166a18 | 2004 | nvkm_disp_vblank(&disp->base, 1); |
2fde1f1c | 2005 | nvkm_wr32(device, 0x610024, 0x00000008); |
ebb945a9 BS |
2006 | } |
2007 | ||
186ecad2 | 2008 | if (intr1 & 0x00000070) { |
fd166a18 BS |
2009 | disp->super = (intr1 & 0x00000070); |
2010 | schedule_work(&disp->supervisor); | |
2fde1f1c | 2011 | nvkm_wr32(device, 0x610024, disp->super); |
186ecad2 | 2012 | } |
ebb945a9 BS |
2013 | } |
2014 | ||
2015 | static int | |
878da15a BS |
2016 | nv50_disp_ctor(struct nvkm_object *parent, struct nvkm_object *engine, |
2017 | struct nvkm_oclass *oclass, void *data, u32 size, | |
2018 | struct nvkm_object **pobject) | |
ebb945a9 | 2019 | { |
fd166a18 | 2020 | struct nv50_disp *disp; |
ebb945a9 BS |
2021 | int ret; |
2022 | ||
878da15a | 2023 | ret = nvkm_disp_create(parent, engine, oclass, 2, "PDISP", |
fd166a18 BS |
2024 | "display", &disp); |
2025 | *pobject = nv_object(disp); | |
ebb945a9 BS |
2026 | if (ret) |
2027 | return ret; | |
2028 | ||
fd166a18 | 2029 | ret = nvkm_event_init(&nv50_disp_chan_uevent, 1, 9, &disp->uevent); |
b38a2322 BS |
2030 | if (ret) |
2031 | return ret; | |
2032 | ||
fd166a18 BS |
2033 | nv_engine(disp)->sclass = nv50_disp_main_oclass; |
2034 | nv_engine(disp)->cclass = &nv50_disp_cclass; | |
2035 | nv_subdev(disp)->intr = nv50_disp_intr; | |
2036 | INIT_WORK(&disp->supervisor, nv50_disp_intr_supervisor); | |
2037 | disp->sclass = nv50_disp_sclass; | |
2038 | disp->head.nr = 2; | |
2039 | disp->dac.nr = 3; | |
2040 | disp->sor.nr = 2; | |
2041 | disp->pior.nr = 3; | |
2042 | disp->dac.power = nv50_dac_power; | |
2043 | disp->dac.sense = nv50_dac_sense; | |
2044 | disp->sor.power = nv50_sor_power; | |
2045 | disp->pior.power = nv50_pior_power; | |
ebb945a9 BS |
2046 | return 0; |
2047 | } | |
2048 | ||
878da15a | 2049 | struct nvkm_oclass * |
a8f8b489 BS |
2050 | nv50_disp_oclass = &(struct nv50_disp_impl) { |
2051 | .base.base.handle = NV_ENGINE(DISP, 0x50), | |
878da15a | 2052 | .base.base.ofuncs = &(struct nvkm_ofuncs) { |
ebb945a9 | 2053 | .ctor = nv50_disp_ctor, |
878da15a BS |
2054 | .dtor = _nvkm_disp_dtor, |
2055 | .init = _nvkm_disp_init, | |
2056 | .fini = _nvkm_disp_fini, | |
ebb945a9 | 2057 | }, |
f2c906fc BS |
2058 | .base.outp.internal.crt = nv50_dac_output_new, |
2059 | .base.outp.internal.tmds = nv50_sor_output_new, | |
2060 | .base.outp.internal.lvds = nv50_sor_output_new, | |
2061 | .base.outp.external.tmds = nv50_pior_output_new, | |
2062 | .base.outp.external.dp = nv50_pior_dp_new, | |
79ca2770 | 2063 | .base.vblank = &nv50_disp_vblank_func, |
2832271d BS |
2064 | .mthd.core = &nv50_disp_core_mthd_chan, |
2065 | .mthd.base = &nv50_disp_base_mthd_chan, | |
d67d92c0 BS |
2066 | .mthd.ovly = &nv50_disp_ovly_mthd_chan, |
2067 | .mthd.prev = 0x000004, | |
2832271d | 2068 | .head.scanoutpos = nv50_disp_main_scanoutpos, |
a8f8b489 | 2069 | }.base.base; |