]>
Commit | Line | Data |
---|---|---|
1 | /* | |
2 | * Copyright 2012 Red Hat Inc. | |
3 | * | |
4 | * Permission is hereby granted, free of charge, to any person obtaining a | |
5 | * copy of this software and associated documentation files (the "Software"), | |
6 | * to deal in the Software without restriction, including without limitation | |
7 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, | |
8 | * and/or sell copies of the Software, and to permit persons to whom the | |
9 | * Software is furnished to do so, subject to the following conditions: | |
10 | * | |
11 | * The above copyright notice and this permission notice shall be included in | |
12 | * all copies or substantial portions of the Software. | |
13 | * | |
14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | |
17 | * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR | |
18 | * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, | |
19 | * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR | |
20 | * OTHER DEALINGS IN THE SOFTWARE. | |
21 | * | |
22 | * Authors: Ben Skeggs | |
23 | */ | |
24 | #include "rootnv50.h" | |
25 | #include "dmacnv50.h" | |
26 | ||
27 | #include <core/client.h> | |
28 | #include <core/ramht.h> | |
29 | #include <subdev/timer.h> | |
30 | ||
31 | #include <nvif/class.h> | |
32 | #include <nvif/unpack.h> | |
33 | ||
34 | int | |
35 | nv50_disp_root_scanoutpos(NV50_DISP_MTHD_V0) | |
36 | { | |
37 | struct nvkm_device *device = disp->base.engine.subdev.device; | |
38 | const u32 blanke = nvkm_rd32(device, 0x610aec + (head * 0x540)); | |
39 | const u32 blanks = nvkm_rd32(device, 0x610af4 + (head * 0x540)); | |
40 | const u32 total = nvkm_rd32(device, 0x610afc + (head * 0x540)); | |
41 | union { | |
42 | struct nv04_disp_scanoutpos_v0 v0; | |
43 | } *args = data; | |
44 | int ret; | |
45 | ||
46 | nvif_ioctl(object, "disp scanoutpos size %d\n", size); | |
47 | if (nvif_unpack(args->v0, 0, 0, false)) { | |
48 | nvif_ioctl(object, "disp scanoutpos vers %d\n", | |
49 | args->v0.version); | |
50 | args->v0.vblanke = (blanke & 0xffff0000) >> 16; | |
51 | args->v0.hblanke = (blanke & 0x0000ffff); | |
52 | args->v0.vblanks = (blanks & 0xffff0000) >> 16; | |
53 | args->v0.hblanks = (blanks & 0x0000ffff); | |
54 | args->v0.vtotal = ( total & 0xffff0000) >> 16; | |
55 | args->v0.htotal = ( total & 0x0000ffff); | |
56 | args->v0.time[0] = ktime_to_ns(ktime_get()); | |
57 | args->v0.vline = /* vline read locks hline */ | |
58 | nvkm_rd32(device, 0x616340 + (head * 0x800)) & 0xffff; | |
59 | args->v0.time[1] = ktime_to_ns(ktime_get()); | |
60 | args->v0.hline = | |
61 | nvkm_rd32(device, 0x616344 + (head * 0x800)) & 0xffff; | |
62 | } else | |
63 | return ret; | |
64 | ||
65 | return 0; | |
66 | } | |
67 | ||
68 | int | |
69 | nv50_disp_root_mthd_(struct nvkm_object *object, u32 mthd, void *data, u32 size) | |
70 | { | |
71 | union { | |
72 | struct nv50_disp_mthd_v0 v0; | |
73 | struct nv50_disp_mthd_v1 v1; | |
74 | } *args = data; | |
75 | struct nv50_disp_root *root = nv50_disp_root(object); | |
76 | struct nv50_disp *disp = root->disp; | |
77 | const struct nv50_disp_impl *impl = (void *)nv_oclass(object->engine); | |
78 | struct nvkm_output *outp = NULL; | |
79 | struct nvkm_output *temp; | |
80 | u16 type, mask = 0; | |
81 | int head, ret; | |
82 | ||
83 | if (mthd != NV50_DISP_MTHD) | |
84 | return -EINVAL; | |
85 | ||
86 | nvif_ioctl(object, "disp mthd size %d\n", size); | |
87 | if (nvif_unpack(args->v0, 0, 0, true)) { | |
88 | nvif_ioctl(object, "disp mthd vers %d mthd %02x head %d\n", | |
89 | args->v0.version, args->v0.method, args->v0.head); | |
90 | mthd = args->v0.method; | |
91 | head = args->v0.head; | |
92 | } else | |
93 | if (nvif_unpack(args->v1, 1, 1, true)) { | |
94 | nvif_ioctl(object, "disp mthd vers %d mthd %02x " | |
95 | "type %04x mask %04x\n", | |
96 | args->v1.version, args->v1.method, | |
97 | args->v1.hasht, args->v1.hashm); | |
98 | mthd = args->v1.method; | |
99 | type = args->v1.hasht; | |
100 | mask = args->v1.hashm; | |
101 | head = ffs((mask >> 8) & 0x0f) - 1; | |
102 | } else | |
103 | return ret; | |
104 | ||
105 | if (head < 0 || head >= disp->head.nr) | |
106 | return -ENXIO; | |
107 | ||
108 | if (mask) { | |
109 | list_for_each_entry(temp, &disp->base.outp, head) { | |
110 | if ((temp->info.hasht == type) && | |
111 | (temp->info.hashm & mask) == mask) { | |
112 | outp = temp; | |
113 | break; | |
114 | } | |
115 | } | |
116 | if (outp == NULL) | |
117 | return -ENXIO; | |
118 | } | |
119 | ||
120 | switch (mthd) { | |
121 | case NV50_DISP_SCANOUTPOS: | |
122 | return impl->head.scanoutpos(object, disp, data, size, head); | |
123 | default: | |
124 | break; | |
125 | } | |
126 | ||
127 | switch (mthd * !!outp) { | |
128 | case NV50_DISP_MTHD_V1_DAC_PWR: | |
129 | return disp->dac.power(object, disp, data, size, head, outp); | |
130 | case NV50_DISP_MTHD_V1_DAC_LOAD: | |
131 | return disp->dac.sense(object, disp, data, size, head, outp); | |
132 | case NV50_DISP_MTHD_V1_SOR_PWR: | |
133 | return disp->sor.power(object, disp, data, size, head, outp); | |
134 | case NV50_DISP_MTHD_V1_SOR_HDA_ELD: | |
135 | if (!disp->sor.hda_eld) | |
136 | return -ENODEV; | |
137 | return disp->sor.hda_eld(object, disp, data, size, head, outp); | |
138 | case NV50_DISP_MTHD_V1_SOR_HDMI_PWR: | |
139 | if (!disp->sor.hdmi) | |
140 | return -ENODEV; | |
141 | return disp->sor.hdmi(object, disp, data, size, head, outp); | |
142 | case NV50_DISP_MTHD_V1_SOR_LVDS_SCRIPT: { | |
143 | union { | |
144 | struct nv50_disp_sor_lvds_script_v0 v0; | |
145 | } *args = data; | |
146 | nvif_ioctl(object, "disp sor lvds script size %d\n", size); | |
147 | if (nvif_unpack(args->v0, 0, 0, false)) { | |
148 | nvif_ioctl(object, "disp sor lvds script " | |
149 | "vers %d name %04x\n", | |
150 | args->v0.version, args->v0.script); | |
151 | disp->sor.lvdsconf = args->v0.script; | |
152 | return 0; | |
153 | } else | |
154 | return ret; | |
155 | } | |
156 | break; | |
157 | case NV50_DISP_MTHD_V1_SOR_DP_PWR: { | |
158 | struct nvkm_output_dp *outpdp = nvkm_output_dp(outp); | |
159 | union { | |
160 | struct nv50_disp_sor_dp_pwr_v0 v0; | |
161 | } *args = data; | |
162 | nvif_ioctl(object, "disp sor dp pwr size %d\n", size); | |
163 | if (nvif_unpack(args->v0, 0, 0, false)) { | |
164 | nvif_ioctl(object, "disp sor dp pwr vers %d state %d\n", | |
165 | args->v0.version, args->v0.state); | |
166 | if (args->v0.state == 0) { | |
167 | nvkm_notify_put(&outpdp->irq); | |
168 | outpdp->func->lnk_pwr(outpdp, 0); | |
169 | atomic_set(&outpdp->lt.done, 0); | |
170 | return 0; | |
171 | } else | |
172 | if (args->v0.state != 0) { | |
173 | nvkm_output_dp_train(&outpdp->base, 0, true); | |
174 | return 0; | |
175 | } | |
176 | } else | |
177 | return ret; | |
178 | } | |
179 | break; | |
180 | case NV50_DISP_MTHD_V1_PIOR_PWR: | |
181 | if (!disp->pior.power) | |
182 | return -ENODEV; | |
183 | return disp->pior.power(object, disp, data, size, head, outp); | |
184 | default: | |
185 | break; | |
186 | } | |
187 | ||
188 | return -EINVAL; | |
189 | } | |
190 | ||
191 | static int | |
192 | nv50_disp_root_dmac_new_(const struct nvkm_oclass *oclass, | |
193 | void *data, u32 size, struct nvkm_object **pobject) | |
194 | { | |
195 | const struct nv50_disp_dmac_oclass *sclass = oclass->priv; | |
196 | struct nv50_disp_root *root = nv50_disp_root(oclass->parent); | |
197 | return sclass->ctor(sclass->func, sclass->mthd, root, sclass->chid, | |
198 | oclass, data, size, pobject); | |
199 | } | |
200 | ||
201 | static int | |
202 | nv50_disp_root_pioc_new_(const struct nvkm_oclass *oclass, | |
203 | void *data, u32 size, struct nvkm_object **pobject) | |
204 | { | |
205 | const struct nv50_disp_pioc_oclass *sclass = oclass->priv; | |
206 | struct nv50_disp_root *root = nv50_disp_root(oclass->parent); | |
207 | return sclass->ctor(sclass->func, sclass->mthd, root, sclass->chid, | |
208 | oclass, data, size, pobject); | |
209 | } | |
210 | ||
211 | static int | |
212 | nv50_disp_root_child_get_(struct nvkm_object *object, int index, | |
213 | struct nvkm_oclass *sclass) | |
214 | { | |
215 | struct nv50_disp_root *root = nv50_disp_root(object); | |
216 | ||
217 | if (index < ARRAY_SIZE(root->func->dmac)) { | |
218 | sclass->base = root->func->dmac[index]->base; | |
219 | sclass->priv = root->func->dmac[index]; | |
220 | sclass->ctor = nv50_disp_root_dmac_new_; | |
221 | return 0; | |
222 | } | |
223 | ||
224 | index -= ARRAY_SIZE(root->func->dmac); | |
225 | ||
226 | if (index < ARRAY_SIZE(root->func->pioc)) { | |
227 | sclass->base = root->func->pioc[index]->base; | |
228 | sclass->priv = root->func->pioc[index]; | |
229 | sclass->ctor = nv50_disp_root_pioc_new_; | |
230 | return 0; | |
231 | } | |
232 | ||
233 | return -EINVAL; | |
234 | } | |
235 | ||
236 | static int | |
237 | nv50_disp_root_fini_(struct nvkm_object *object, bool suspend) | |
238 | { | |
239 | struct nv50_disp_root *root = nv50_disp_root(object); | |
240 | root->func->fini(root); | |
241 | return 0; | |
242 | } | |
243 | ||
244 | static int | |
245 | nv50_disp_root_init_(struct nvkm_object *object) | |
246 | { | |
247 | struct nv50_disp_root *root = nv50_disp_root(object); | |
248 | return root->func->init(root); | |
249 | } | |
250 | ||
251 | static void * | |
252 | nv50_disp_root_dtor_(struct nvkm_object *object) | |
253 | { | |
254 | struct nv50_disp_root *root = nv50_disp_root(object); | |
255 | nvkm_ramht_del(&root->ramht); | |
256 | nvkm_gpuobj_del(&root->instmem); | |
257 | return root; | |
258 | } | |
259 | ||
260 | static const struct nvkm_object_func | |
261 | nv50_disp_root_ = { | |
262 | .dtor = nv50_disp_root_dtor_, | |
263 | .init = nv50_disp_root_init_, | |
264 | .fini = nv50_disp_root_fini_, | |
265 | .mthd = nv50_disp_root_mthd_, | |
266 | .ntfy = nvkm_disp_ntfy, | |
267 | .sclass = nv50_disp_root_child_get_, | |
268 | }; | |
269 | ||
270 | int | |
271 | nv50_disp_root_new_(const struct nv50_disp_root_func *func, | |
272 | struct nvkm_disp *base, const struct nvkm_oclass *oclass, | |
273 | void *data, u32 size, struct nvkm_object **pobject) | |
274 | { | |
275 | struct nv50_disp *disp = nv50_disp(base); | |
276 | struct nv50_disp_root *root; | |
277 | struct nvkm_device *device = disp->base.engine.subdev.device; | |
278 | int ret; | |
279 | ||
280 | if (!(root = kzalloc(sizeof(*root), GFP_KERNEL))) | |
281 | return -ENOMEM; | |
282 | *pobject = &root->object; | |
283 | ||
284 | nvkm_object_ctor(&nv50_disp_root_, oclass, &root->object); | |
285 | root->func = func; | |
286 | root->disp = disp; | |
287 | ||
288 | ret = nvkm_gpuobj_new(disp->base.engine.subdev.device, 0x10000, 0x10000, | |
289 | false, NULL, &root->instmem); | |
290 | if (ret) | |
291 | return ret; | |
292 | ||
293 | return nvkm_ramht_new(device, 0x1000, 0, root->instmem, &root->ramht); | |
294 | } | |
295 | ||
296 | void | |
297 | nv50_disp_root_fini(struct nv50_disp_root *root) | |
298 | { | |
299 | struct nvkm_device *device = root->disp->base.engine.subdev.device; | |
300 | /* disable all interrupts */ | |
301 | nvkm_wr32(device, 0x610024, 0x00000000); | |
302 | nvkm_wr32(device, 0x610020, 0x00000000); | |
303 | } | |
304 | ||
305 | int | |
306 | nv50_disp_root_init(struct nv50_disp_root *root) | |
307 | { | |
308 | struct nv50_disp *disp = root->disp; | |
309 | struct nvkm_device *device = disp->base.engine.subdev.device; | |
310 | u32 tmp; | |
311 | int i; | |
312 | ||
313 | /* The below segments of code copying values from one register to | |
314 | * another appear to inform EVO of the display capabilities or | |
315 | * something similar. NFI what the 0x614004 caps are for.. | |
316 | */ | |
317 | tmp = nvkm_rd32(device, 0x614004); | |
318 | nvkm_wr32(device, 0x610184, tmp); | |
319 | ||
320 | /* ... CRTC caps */ | |
321 | for (i = 0; i < disp->head.nr; i++) { | |
322 | tmp = nvkm_rd32(device, 0x616100 + (i * 0x800)); | |
323 | nvkm_wr32(device, 0x610190 + (i * 0x10), tmp); | |
324 | tmp = nvkm_rd32(device, 0x616104 + (i * 0x800)); | |
325 | nvkm_wr32(device, 0x610194 + (i * 0x10), tmp); | |
326 | tmp = nvkm_rd32(device, 0x616108 + (i * 0x800)); | |
327 | nvkm_wr32(device, 0x610198 + (i * 0x10), tmp); | |
328 | tmp = nvkm_rd32(device, 0x61610c + (i * 0x800)); | |
329 | nvkm_wr32(device, 0x61019c + (i * 0x10), tmp); | |
330 | } | |
331 | ||
332 | /* ... DAC caps */ | |
333 | for (i = 0; i < disp->dac.nr; i++) { | |
334 | tmp = nvkm_rd32(device, 0x61a000 + (i * 0x800)); | |
335 | nvkm_wr32(device, 0x6101d0 + (i * 0x04), tmp); | |
336 | } | |
337 | ||
338 | /* ... SOR caps */ | |
339 | for (i = 0; i < disp->sor.nr; i++) { | |
340 | tmp = nvkm_rd32(device, 0x61c000 + (i * 0x800)); | |
341 | nvkm_wr32(device, 0x6101e0 + (i * 0x04), tmp); | |
342 | } | |
343 | ||
344 | /* ... PIOR caps */ | |
345 | for (i = 0; i < disp->pior.nr; i++) { | |
346 | tmp = nvkm_rd32(device, 0x61e000 + (i * 0x800)); | |
347 | nvkm_wr32(device, 0x6101f0 + (i * 0x04), tmp); | |
348 | } | |
349 | ||
350 | /* steal display away from vbios, or something like that */ | |
351 | if (nvkm_rd32(device, 0x610024) & 0x00000100) { | |
352 | nvkm_wr32(device, 0x610024, 0x00000100); | |
353 | nvkm_mask(device, 0x6194e8, 0x00000001, 0x00000000); | |
354 | if (nvkm_msec(device, 2000, | |
355 | if (!(nvkm_rd32(device, 0x6194e8) & 0x00000002)) | |
356 | break; | |
357 | ) < 0) | |
358 | return -EBUSY; | |
359 | } | |
360 | ||
361 | /* point at display engine memory area (hash table, objects) */ | |
362 | nvkm_wr32(device, 0x610010, (root->instmem->addr >> 8) | 9); | |
363 | ||
364 | /* enable supervisor interrupts, disable everything else */ | |
365 | nvkm_wr32(device, 0x61002c, 0x00000370); | |
366 | nvkm_wr32(device, 0x610028, 0x00000000); | |
367 | return 0; | |
368 | } | |
369 | ||
370 | static const struct nv50_disp_root_func | |
371 | nv50_disp_root = { | |
372 | .init = nv50_disp_root_init, | |
373 | .fini = nv50_disp_root_fini, | |
374 | .dmac = { | |
375 | &nv50_disp_core_oclass, | |
376 | &nv50_disp_base_oclass, | |
377 | &nv50_disp_ovly_oclass, | |
378 | }, | |
379 | .pioc = { | |
380 | &nv50_disp_oimm_oclass, | |
381 | &nv50_disp_curs_oclass, | |
382 | }, | |
383 | }; | |
384 | ||
385 | static int | |
386 | nv50_disp_root_new(struct nvkm_disp *disp, const struct nvkm_oclass *oclass, | |
387 | void *data, u32 size, struct nvkm_object **pobject) | |
388 | { | |
389 | return nv50_disp_root_new_(&nv50_disp_root, disp, oclass, | |
390 | data, size, pobject); | |
391 | } | |
392 | ||
393 | const struct nvkm_disp_oclass | |
394 | nv50_disp_root_oclass = { | |
395 | .base.oclass = NV50_DISP, | |
396 | .base.minver = -1, | |
397 | .base.maxver = -1, | |
398 | .ctor = nv50_disp_root_new, | |
399 | }; |