Commit | Line | Data |
---|---|---|
2a7909c0 BS |
1 | /* |
2 | * Copyright 2012 Red Hat Inc. | |
3 | * | |
4 | * Permission is hereby granted, free of charge, to any person obtaining a | |
5 | * copy of this software and associated documentation files (the "Software"), | |
6 | * to deal in the Software without restriction, including without limitation | |
7 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, | |
8 | * and/or sell copies of the Software, and to permit persons to whom the | |
9 | * Software is furnished to do so, subject to the following conditions: | |
10 | * | |
11 | * The above copyright notice and this permission notice shall be included in | |
12 | * all copies or substantial portions of the Software. | |
13 | * | |
14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | |
17 | * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR | |
18 | * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, | |
19 | * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR | |
20 | * OTHER DEALINGS IN THE SOFTWARE. | |
21 | * | |
22 | * Authors: Ben Skeggs | |
23 | */ | |
92fba5d3 | 24 | #include "priv.h" |
acbe9ecf BS |
25 | #include "chan.h" |
26 | #include "hdmi.h" | |
a1c93078 | 27 | #include "head.h" |
78f1ad6f | 28 | #include "ior.h" |
92fba5d3 | 29 | #include "outp.h" |
2a7909c0 | 30 | |
bb3b0a42 BS |
31 | #include <core/ramht.h> |
32 | #include <subdev/timer.h> | |
33 | ||
168c0299 BS |
34 | #include <nvif/class.h> |
35 | ||
7bcf89ee | 36 | static void |
acbe9ecf BS |
37 | gf119_sor_hda_device_entry(struct nvkm_ior *ior, int head) |
38 | { | |
39 | struct nvkm_device *device = ior->disp->engine.subdev.device; | |
40 | const u32 hoff = 0x800 * head; | |
41 | ||
42 | nvkm_mask(device, 0x616548 + hoff, 0x00000070, head << 4); | |
43 | } | |
44 | ||
45 | void | |
46 | gf119_sor_hda_eld(struct nvkm_ior *ior, int head, u8 *data, u8 size) | |
47 | { | |
48 | struct nvkm_device *device = ior->disp->engine.subdev.device; | |
49 | const u32 soff = 0x030 * ior->id + (head * 0x04); | |
50 | int i; | |
51 | ||
52 | for (i = 0; i < size; i++) | |
53 | nvkm_wr32(device, 0x10ec00 + soff, (i << 8) | data[i]); | |
54 | for (; i < 0x60; i++) | |
55 | nvkm_wr32(device, 0x10ec00 + soff, (i << 8)); | |
56 | nvkm_mask(device, 0x10ec10 + soff, 0x80000002, 0x80000002); | |
57 | } | |
58 | ||
59 | void | |
60 | gf119_sor_hda_hpd(struct nvkm_ior *ior, int head, bool present) | |
61 | { | |
62 | struct nvkm_device *device = ior->disp->engine.subdev.device; | |
63 | const u32 soff = 0x030 * ior->id + (head * 0x04); | |
64 | u32 data = 0x80000000; | |
65 | u32 mask = 0x80000001; | |
66 | ||
67 | if (present) { | |
7bcf89ee | 68 | ior->func->hda->device_entry(ior, head); |
acbe9ecf BS |
69 | data |= 0x00000001; |
70 | } else { | |
71 | mask |= 0x00000002; | |
72 | } | |
73 | ||
74 | nvkm_mask(device, 0x10ec10 + soff, mask, data); | |
75 | } | |
76 | ||
7bcf89ee BS |
77 | const struct nvkm_ior_func_hda |
78 | gf119_sor_hda = { | |
79 | .hpd = gf119_sor_hda_hpd, | |
80 | .eld = gf119_sor_hda_eld, | |
81 | .device_entry = gf119_sor_hda_device_entry, | |
82 | }; | |
83 | ||
acbe9ecf BS |
84 | void |
85 | gf119_sor_dp_watermark(struct nvkm_ior *sor, int head, u8 watermark) | |
86 | { | |
87 | struct nvkm_device *device = sor->disp->engine.subdev.device; | |
88 | const u32 hoff = head * 0x800; | |
89 | ||
90 | nvkm_mask(device, 0x616610 + hoff, 0x0800003f, 0x08000000 | watermark); | |
91 | } | |
92 | ||
93 | void | |
94 | gf119_sor_dp_audio_sym(struct nvkm_ior *sor, int head, u16 h, u32 v) | |
95 | { | |
96 | struct nvkm_device *device = sor->disp->engine.subdev.device; | |
97 | const u32 hoff = head * 0x800; | |
98 | ||
99 | nvkm_mask(device, 0x616620 + hoff, 0x0000ffff, h); | |
100 | nvkm_mask(device, 0x616624 + hoff, 0x00ffffff, v); | |
101 | } | |
102 | ||
103 | void | |
104 | gf119_sor_dp_audio(struct nvkm_ior *sor, int head, bool enable) | |
105 | { | |
106 | struct nvkm_device *device = sor->disp->engine.subdev.device; | |
107 | const u32 hoff = 0x800 * head; | |
108 | const u32 data = 0x80000000 | (0x00000001 * enable); | |
109 | const u32 mask = 0x8000000d; | |
110 | ||
111 | nvkm_mask(device, 0x616618 + hoff, mask, data); | |
112 | nvkm_msec(device, 2000, | |
113 | if (!(nvkm_rd32(device, 0x616618 + hoff) & 0x80000000)) | |
114 | break; | |
115 | ); | |
116 | } | |
117 | ||
118 | void | |
119 | gf119_sor_dp_vcpi(struct nvkm_ior *sor, int head, u8 slot, u8 slot_nr, u16 pbn, u16 aligned) | |
120 | { | |
121 | struct nvkm_device *device = sor->disp->engine.subdev.device; | |
122 | const u32 hoff = head * 0x800; | |
123 | ||
124 | nvkm_mask(device, 0x616588 + hoff, 0x00003f3f, (slot_nr << 8) | slot); | |
125 | nvkm_mask(device, 0x61658c + hoff, 0xffffffff, (aligned << 16) | pbn); | |
126 | } | |
127 | ||
128 | void | |
129 | gf119_sor_dp_drive(struct nvkm_ior *sor, int ln, int pc, int dc, int pe, int pu) | |
130 | { | |
131 | struct nvkm_device *device = sor->disp->engine.subdev.device; | |
132 | const u32 loff = nv50_sor_link(sor); | |
9a4514fb | 133 | const u32 shift = sor->func->dp->lanes[ln] * 8; |
acbe9ecf BS |
134 | u32 data[4]; |
135 | ||
136 | data[0] = nvkm_rd32(device, 0x61c118 + loff) & ~(0x000000ff << shift); | |
137 | data[1] = nvkm_rd32(device, 0x61c120 + loff) & ~(0x000000ff << shift); | |
138 | data[2] = nvkm_rd32(device, 0x61c130 + loff); | |
139 | if ((data[2] & 0x0000ff00) < (pu << 8) || ln == 0) | |
140 | data[2] = (data[2] & ~0x0000ff00) | (pu << 8); | |
141 | ||
142 | nvkm_wr32(device, 0x61c118 + loff, data[0] | (dc << shift)); | |
143 | nvkm_wr32(device, 0x61c120 + loff, data[1] | (pe << shift)); | |
144 | nvkm_wr32(device, 0x61c130 + loff, data[2]); | |
145 | ||
146 | data[3] = nvkm_rd32(device, 0x61c13c + loff) & ~(0x000000ff << shift); | |
147 | nvkm_wr32(device, 0x61c13c + loff, data[3] | (pc << shift)); | |
148 | } | |
149 | ||
9a4514fb | 150 | static void |
acbe9ecf BS |
151 | gf119_sor_dp_pattern(struct nvkm_ior *sor, int pattern) |
152 | { | |
153 | struct nvkm_device *device = sor->disp->engine.subdev.device; | |
154 | const u32 soff = nv50_ior_base(sor); | |
155 | u32 data; | |
156 | ||
157 | switch (pattern) { | |
158 | case 0: data = 0x10101010; break; | |
159 | case 1: data = 0x01010101; break; | |
160 | case 2: data = 0x02020202; break; | |
161 | case 3: data = 0x03030303; break; | |
162 | default: | |
163 | WARN_ON(1); | |
164 | return; | |
165 | } | |
166 | ||
167 | nvkm_mask(device, 0x61c110 + soff, 0x1f1f1f1f, data); | |
168 | } | |
169 | ||
170 | int | |
171 | gf119_sor_dp_links(struct nvkm_ior *sor, struct nvkm_i2c_aux *aux) | |
172 | { | |
173 | struct nvkm_device *device = sor->disp->engine.subdev.device; | |
174 | const u32 soff = nv50_ior_base(sor); | |
175 | const u32 loff = nv50_sor_link(sor); | |
176 | u32 dpctrl = 0x00000000; | |
177 | u32 clksor = 0x00000000; | |
178 | ||
179 | clksor |= sor->dp.bw << 18; | |
180 | dpctrl |= ((1 << sor->dp.nr) - 1) << 16; | |
181 | if (sor->dp.mst) | |
182 | dpctrl |= 0x40000000; | |
183 | if (sor->dp.ef) | |
184 | dpctrl |= 0x00004000; | |
185 | ||
186 | nvkm_mask(device, 0x612300 + soff, 0x007c0000, clksor); | |
187 | nvkm_mask(device, 0x61c10c + loff, 0x401f4000, dpctrl); | |
188 | return 0; | |
189 | } | |
190 | ||
9a4514fb BS |
191 | const struct nvkm_ior_func_dp |
192 | gf119_sor_dp = { | |
193 | .lanes = { 2, 1, 0, 3 }, | |
194 | .links = gf119_sor_dp_links, | |
195 | .power = g94_sor_dp_power, | |
196 | .pattern = gf119_sor_dp_pattern, | |
197 | .drive = gf119_sor_dp_drive, | |
198 | .vcpi = gf119_sor_dp_vcpi, | |
199 | .audio = gf119_sor_dp_audio, | |
200 | .audio_sym = gf119_sor_dp_audio_sym, | |
201 | .watermark = gf119_sor_dp_watermark, | |
202 | }; | |
203 | ||
acbe9ecf | 204 | static void |
f530bc60 BS |
205 | gf119_sor_hdmi_infoframe_vsi(struct nvkm_ior *ior, int head, void *data, u32 size) |
206 | { | |
207 | struct nvkm_device *device = ior->disp->engine.subdev.device; | |
208 | struct packed_hdmi_infoframe vsi; | |
209 | const u32 hoff = head * 0x800; | |
210 | ||
211 | pack_hdmi_infoframe(&vsi, data, size); | |
212 | ||
213 | nvkm_mask(device, 0x616730 + hoff, 0x00010001, 0x00010000); | |
214 | if (!size) | |
215 | return; | |
216 | ||
217 | /* | |
218 | * These appear to be the audio infoframe registers, | |
219 | * but no other set of infoframe registers has yet | |
220 | * been found. | |
221 | */ | |
222 | nvkm_wr32(device, 0x616738 + hoff, vsi.header); | |
223 | nvkm_wr32(device, 0x61673c + hoff, vsi.subpack0_low); | |
224 | nvkm_wr32(device, 0x616740 + hoff, vsi.subpack0_high); | |
225 | /* Is there a second (or further?) set of subpack registers here? */ | |
226 | ||
227 | nvkm_mask(device, 0x616730 + hoff, 0x00000001, 0x00000001); | |
228 | } | |
229 | ||
230 | static void | |
231 | gf119_sor_hdmi_infoframe_avi(struct nvkm_ior *ior, int head, void *data, u32 size) | |
232 | { | |
233 | struct nvkm_device *device = ior->disp->engine.subdev.device; | |
234 | struct packed_hdmi_infoframe avi; | |
235 | const u32 hoff = head * 0x800; | |
236 | ||
237 | pack_hdmi_infoframe(&avi, data, size); | |
238 | ||
239 | nvkm_mask(device, 0x616714 + hoff, 0x00000001, 0x00000000); | |
240 | if (!size) | |
241 | return; | |
242 | ||
243 | nvkm_wr32(device, 0x61671c + hoff, avi.header); | |
244 | nvkm_wr32(device, 0x616720 + hoff, avi.subpack0_low); | |
245 | nvkm_wr32(device, 0x616724 + hoff, avi.subpack0_high); | |
246 | nvkm_wr32(device, 0x616728 + hoff, avi.subpack1_low); | |
247 | nvkm_wr32(device, 0x61672c + hoff, avi.subpack1_high); | |
248 | ||
249 | nvkm_mask(device, 0x616714 + hoff, 0x00000001, 0x00000001); | |
250 | } | |
251 | ||
252 | static void | |
253 | gf119_sor_hdmi_ctrl(struct nvkm_ior *ior, int head, bool enable, u8 max_ac_packet, u8 rekey) | |
acbe9ecf BS |
254 | { |
255 | struct nvkm_device *device = ior->disp->engine.subdev.device; | |
256 | const u32 ctrl = 0x40000000 * enable | | |
257 | max_ac_packet << 16 | | |
258 | rekey; | |
259 | const u32 hoff = head * 0x800; | |
acbe9ecf BS |
260 | |
261 | if (!(ctrl & 0x40000000)) { | |
262 | nvkm_mask(device, 0x616798 + hoff, 0x40000000, 0x00000000); | |
263 | nvkm_mask(device, 0x616730 + hoff, 0x00000001, 0x00000000); | |
264 | nvkm_mask(device, 0x6167a4 + hoff, 0x00000001, 0x00000000); | |
265 | nvkm_mask(device, 0x616714 + hoff, 0x00000001, 0x00000000); | |
266 | return; | |
267 | } | |
268 | ||
acbe9ecf BS |
269 | /* ??? InfoFrame? */ |
270 | nvkm_mask(device, 0x6167a4 + hoff, 0x00000001, 0x00000000); | |
271 | nvkm_wr32(device, 0x6167ac + hoff, 0x00000010); | |
272 | nvkm_mask(device, 0x6167a4 + hoff, 0x00000001, 0x00000001); | |
273 | ||
274 | /* HDMI_CTRL */ | |
275 | nvkm_mask(device, 0x616798 + hoff, 0x401f007f, ctrl); | |
276 | } | |
277 | ||
f530bc60 BS |
278 | static const struct nvkm_ior_func_hdmi |
279 | gf119_sor_hdmi = { | |
280 | .ctrl = gf119_sor_hdmi_ctrl, | |
281 | .infoframe_avi = gf119_sor_hdmi_infoframe_avi, | |
282 | .infoframe_vsi = gf119_sor_hdmi_infoframe_vsi, | |
283 | }; | |
284 | ||
acbe9ecf BS |
285 | void |
286 | gf119_sor_clock(struct nvkm_ior *sor) | |
287 | { | |
288 | struct nvkm_device *device = sor->disp->engine.subdev.device; | |
289 | const u32 soff = nv50_ior_base(sor); | |
290 | u32 div1 = sor->asy.link == 3; | |
291 | u32 div2 = sor->asy.link == 3; | |
292 | ||
293 | if (sor->asy.proto == TMDS) { | |
294 | const u32 speed = sor->tmds.high_speed ? 0x14 : 0x0a; | |
295 | nvkm_mask(device, 0x612300 + soff, 0x007c0000, speed << 18); | |
296 | if (sor->tmds.high_speed) | |
297 | div2 = 1; | |
298 | } | |
299 | ||
300 | nvkm_mask(device, 0x612300 + soff, 0x00000707, (div2 << 8) | div1); | |
301 | } | |
302 | ||
303 | void | |
304 | gf119_sor_state(struct nvkm_ior *sor, struct nvkm_ior_state *state) | |
305 | { | |
306 | struct nvkm_device *device = sor->disp->engine.subdev.device; | |
307 | const u32 coff = (state == &sor->asy) * 0x20000 + sor->id * 0x20; | |
308 | u32 ctrl = nvkm_rd32(device, 0x640200 + coff); | |
309 | ||
310 | state->proto_evo = (ctrl & 0x00000f00) >> 8; | |
311 | switch (state->proto_evo) { | |
312 | case 0: state->proto = LVDS; state->link = 1; break; | |
313 | case 1: state->proto = TMDS; state->link = 1; break; | |
314 | case 2: state->proto = TMDS; state->link = 2; break; | |
315 | case 5: state->proto = TMDS; state->link = 3; break; | |
316 | case 8: state->proto = DP; state->link = 1; break; | |
317 | case 9: state->proto = DP; state->link = 2; break; | |
318 | default: | |
319 | state->proto = UNKNOWN; | |
320 | break; | |
321 | } | |
322 | ||
323 | state->head = ctrl & 0x0000000f; | |
324 | } | |
325 | ||
326 | static const struct nvkm_ior_func | |
327 | gf119_sor = { | |
328 | .state = gf119_sor_state, | |
329 | .power = nv50_sor_power, | |
330 | .clock = gf119_sor_clock, | |
2274ce7e | 331 | .bl = >215_sor_bl, |
f530bc60 | 332 | .hdmi = &gf119_sor_hdmi, |
9a4514fb | 333 | .dp = &gf119_sor_dp, |
7bcf89ee | 334 | .hda = &gf119_sor_hda, |
acbe9ecf BS |
335 | }; |
336 | ||
337 | static int | |
338 | gf119_sor_new(struct nvkm_disp *disp, int id) | |
339 | { | |
79c453af | 340 | return nvkm_ior_new_(&gf119_sor, disp, SOR, id, true); |
acbe9ecf BS |
341 | } |
342 | ||
343 | int | |
344 | gf119_sor_cnt(struct nvkm_disp *disp, unsigned long *pmask) | |
345 | { | |
346 | struct nvkm_device *device = disp->engine.subdev.device; | |
347 | *pmask = (nvkm_rd32(device, 0x612004) & 0x0000ff00) >> 8; | |
348 | return 8; | |
349 | } | |
350 | ||
351 | static void | |
352 | gf119_dac_clock(struct nvkm_ior *dac) | |
353 | { | |
354 | struct nvkm_device *device = dac->disp->engine.subdev.device; | |
355 | const u32 doff = nv50_ior_base(dac); | |
356 | nvkm_mask(device, 0x612280 + doff, 0x07070707, 0x00000000); | |
357 | } | |
358 | ||
359 | static void | |
360 | gf119_dac_state(struct nvkm_ior *dac, struct nvkm_ior_state *state) | |
361 | { | |
362 | struct nvkm_device *device = dac->disp->engine.subdev.device; | |
363 | const u32 coff = (state == &dac->asy) * 0x20000 + dac->id * 0x20; | |
364 | u32 ctrl = nvkm_rd32(device, 0x640180 + coff); | |
365 | ||
366 | state->proto_evo = (ctrl & 0x00000f00) >> 8; | |
367 | switch (state->proto_evo) { | |
368 | case 0: state->proto = CRT; break; | |
369 | default: | |
370 | state->proto = UNKNOWN; | |
371 | break; | |
372 | } | |
373 | ||
374 | state->head = ctrl & 0x0000000f; | |
375 | } | |
376 | ||
377 | static const struct nvkm_ior_func | |
378 | gf119_dac = { | |
379 | .state = gf119_dac_state, | |
380 | .power = nv50_dac_power, | |
381 | .sense = nv50_dac_sense, | |
382 | .clock = gf119_dac_clock, | |
383 | }; | |
384 | ||
385 | int | |
386 | gf119_dac_new(struct nvkm_disp *disp, int id) | |
387 | { | |
79c453af | 388 | return nvkm_ior_new_(&gf119_dac, disp, DAC, id, false); |
acbe9ecf BS |
389 | } |
390 | ||
391 | int | |
392 | gf119_dac_cnt(struct nvkm_disp *disp, unsigned long *pmask) | |
393 | { | |
394 | struct nvkm_device *device = disp->engine.subdev.device; | |
395 | *pmask = (nvkm_rd32(device, 0x612004) & 0x000000f0) >> 4; | |
396 | return 4; | |
397 | } | |
398 | ||
399 | static void | |
400 | gf119_head_vblank_put(struct nvkm_head *head) | |
401 | { | |
402 | struct nvkm_device *device = head->disp->engine.subdev.device; | |
403 | const u32 hoff = head->id * 0x800; | |
404 | nvkm_mask(device, 0x6100c0 + hoff, 0x00000001, 0x00000000); | |
405 | } | |
406 | ||
407 | static void | |
408 | gf119_head_vblank_get(struct nvkm_head *head) | |
409 | { | |
410 | struct nvkm_device *device = head->disp->engine.subdev.device; | |
411 | const u32 hoff = head->id * 0x800; | |
412 | nvkm_mask(device, 0x6100c0 + hoff, 0x00000001, 0x00000001); | |
413 | } | |
414 | ||
415 | void | |
416 | gf119_head_rgclk(struct nvkm_head *head, int div) | |
417 | { | |
418 | struct nvkm_device *device = head->disp->engine.subdev.device; | |
419 | nvkm_mask(device, 0x612200 + (head->id * 0x800), 0x0000000f, div); | |
420 | } | |
421 | ||
422 | static void | |
423 | gf119_head_state(struct nvkm_head *head, struct nvkm_head_state *state) | |
424 | { | |
425 | struct nvkm_device *device = head->disp->engine.subdev.device; | |
426 | const u32 hoff = (state == &head->asy) * 0x20000 + head->id * 0x300; | |
427 | u32 data; | |
428 | ||
429 | data = nvkm_rd32(device, 0x640414 + hoff); | |
430 | state->vtotal = (data & 0xffff0000) >> 16; | |
431 | state->htotal = (data & 0x0000ffff); | |
432 | data = nvkm_rd32(device, 0x640418 + hoff); | |
433 | state->vsynce = (data & 0xffff0000) >> 16; | |
434 | state->hsynce = (data & 0x0000ffff); | |
435 | data = nvkm_rd32(device, 0x64041c + hoff); | |
436 | state->vblanke = (data & 0xffff0000) >> 16; | |
437 | state->hblanke = (data & 0x0000ffff); | |
438 | data = nvkm_rd32(device, 0x640420 + hoff); | |
439 | state->vblanks = (data & 0xffff0000) >> 16; | |
440 | state->hblanks = (data & 0x0000ffff); | |
441 | state->hz = nvkm_rd32(device, 0x640450 + hoff); | |
442 | ||
443 | data = nvkm_rd32(device, 0x640404 + hoff); | |
444 | switch ((data & 0x000003c0) >> 6) { | |
445 | case 6: state->or.depth = 30; break; | |
446 | case 5: state->or.depth = 24; break; | |
447 | case 2: state->or.depth = 18; break; | |
448 | case 0: state->or.depth = 18; break; /*XXX: "default" */ | |
449 | default: | |
450 | state->or.depth = 18; | |
451 | WARN_ON(1); | |
452 | break; | |
453 | } | |
454 | } | |
455 | ||
456 | static const struct nvkm_head_func | |
457 | gf119_head = { | |
458 | .state = gf119_head_state, | |
459 | .rgpos = nv50_head_rgpos, | |
460 | .rgclk = gf119_head_rgclk, | |
461 | .vblank_get = gf119_head_vblank_get, | |
462 | .vblank_put = gf119_head_vblank_put, | |
463 | }; | |
464 | ||
465 | int | |
466 | gf119_head_new(struct nvkm_disp *disp, int id) | |
467 | { | |
468 | return nvkm_head_new_(&gf119_head, disp, id); | |
469 | } | |
470 | ||
471 | int | |
472 | gf119_head_cnt(struct nvkm_disp *disp, unsigned long *pmask) | |
473 | { | |
474 | struct nvkm_device *device = disp->engine.subdev.device; | |
475 | *pmask = nvkm_rd32(device, 0x612004) & 0x0000000f; | |
476 | return nvkm_rd32(device, 0x022448); | |
477 | } | |
478 | ||
479 | static void | |
480 | gf119_disp_chan_uevent_fini(struct nvkm_event *event, int type, int index) | |
481 | { | |
482 | struct nvkm_disp *disp = container_of(event, typeof(*disp), uevent); | |
483 | struct nvkm_device *device = disp->engine.subdev.device; | |
484 | nvkm_mask(device, 0x610090, 0x00000001 << index, 0x00000000 << index); | |
485 | nvkm_wr32(device, 0x61008c, 0x00000001 << index); | |
486 | } | |
487 | ||
488 | static void | |
489 | gf119_disp_chan_uevent_init(struct nvkm_event *event, int types, int index) | |
490 | { | |
491 | struct nvkm_disp *disp = container_of(event, typeof(*disp), uevent); | |
492 | struct nvkm_device *device = disp->engine.subdev.device; | |
493 | nvkm_wr32(device, 0x61008c, 0x00000001 << index); | |
494 | nvkm_mask(device, 0x610090, 0x00000001 << index, 0x00000001 << index); | |
495 | } | |
496 | ||
497 | const struct nvkm_event_func | |
498 | gf119_disp_chan_uevent = { | |
acbe9ecf BS |
499 | .init = gf119_disp_chan_uevent_init, |
500 | .fini = gf119_disp_chan_uevent_fini, | |
501 | }; | |
502 | ||
503 | void | |
504 | gf119_disp_chan_intr(struct nvkm_disp_chan *chan, bool en) | |
505 | { | |
506 | struct nvkm_device *device = chan->disp->engine.subdev.device; | |
507 | const u32 mask = 0x00000001 << chan->chid.user; | |
508 | if (!en) { | |
509 | nvkm_mask(device, 0x610090, mask, 0x00000000); | |
510 | nvkm_mask(device, 0x6100a0, mask, 0x00000000); | |
511 | } else { | |
512 | nvkm_mask(device, 0x6100a0, mask, mask); | |
513 | } | |
514 | } | |
515 | ||
516 | static void | |
517 | gf119_disp_pioc_fini(struct nvkm_disp_chan *chan) | |
518 | { | |
519 | struct nvkm_disp *disp = chan->disp; | |
520 | struct nvkm_subdev *subdev = &disp->engine.subdev; | |
521 | struct nvkm_device *device = subdev->device; | |
522 | int ctrl = chan->chid.ctrl; | |
523 | int user = chan->chid.user; | |
524 | ||
525 | nvkm_mask(device, 0x610490 + (ctrl * 0x10), 0x00000001, 0x00000000); | |
526 | if (nvkm_msec(device, 2000, | |
527 | if (!(nvkm_rd32(device, 0x610490 + (ctrl * 0x10)) & 0x00030000)) | |
528 | break; | |
529 | ) < 0) { | |
530 | nvkm_error(subdev, "ch %d fini: %08x\n", user, | |
531 | nvkm_rd32(device, 0x610490 + (ctrl * 0x10))); | |
532 | } | |
533 | } | |
534 | ||
535 | static int | |
536 | gf119_disp_pioc_init(struct nvkm_disp_chan *chan) | |
537 | { | |
538 | struct nvkm_disp *disp = chan->disp; | |
539 | struct nvkm_subdev *subdev = &disp->engine.subdev; | |
540 | struct nvkm_device *device = subdev->device; | |
541 | int ctrl = chan->chid.ctrl; | |
542 | int user = chan->chid.user; | |
543 | ||
544 | /* activate channel */ | |
545 | nvkm_wr32(device, 0x610490 + (ctrl * 0x10), 0x00000001); | |
546 | if (nvkm_msec(device, 2000, | |
547 | u32 tmp = nvkm_rd32(device, 0x610490 + (ctrl * 0x10)); | |
548 | if ((tmp & 0x00030000) == 0x00010000) | |
549 | break; | |
550 | ) < 0) { | |
551 | nvkm_error(subdev, "ch %d init: %08x\n", user, | |
552 | nvkm_rd32(device, 0x610490 + (ctrl * 0x10))); | |
553 | return -EBUSY; | |
554 | } | |
555 | ||
556 | return 0; | |
557 | } | |
558 | ||
559 | const struct nvkm_disp_chan_func | |
560 | gf119_disp_pioc_func = { | |
561 | .init = gf119_disp_pioc_init, | |
562 | .fini = gf119_disp_pioc_fini, | |
563 | .intr = gf119_disp_chan_intr, | |
564 | .user = nv50_disp_chan_user, | |
565 | }; | |
566 | ||
567 | int | |
568 | gf119_disp_dmac_bind(struct nvkm_disp_chan *chan, struct nvkm_object *object, u32 handle) | |
569 | { | |
570 | return nvkm_ramht_insert(chan->disp->ramht, object, chan->chid.user, -9, handle, | |
571 | chan->chid.user << 27 | 0x00000001); | |
572 | } | |
573 | ||
574 | void | |
575 | gf119_disp_dmac_fini(struct nvkm_disp_chan *chan) | |
576 | { | |
577 | struct nvkm_subdev *subdev = &chan->disp->engine.subdev; | |
578 | struct nvkm_device *device = subdev->device; | |
579 | int ctrl = chan->chid.ctrl; | |
580 | int user = chan->chid.user; | |
581 | ||
582 | /* deactivate channel */ | |
583 | nvkm_mask(device, 0x610490 + (ctrl * 0x0010), 0x00001010, 0x00001000); | |
584 | nvkm_mask(device, 0x610490 + (ctrl * 0x0010), 0x00000003, 0x00000000); | |
585 | if (nvkm_msec(device, 2000, | |
586 | if (!(nvkm_rd32(device, 0x610490 + (ctrl * 0x10)) & 0x001e0000)) | |
587 | break; | |
588 | ) < 0) { | |
589 | nvkm_error(subdev, "ch %d fini: %08x\n", user, | |
590 | nvkm_rd32(device, 0x610490 + (ctrl * 0x10))); | |
591 | } | |
592 | ||
593 | chan->suspend_put = nvkm_rd32(device, 0x640000 + (ctrl * 0x1000)); | |
594 | } | |
595 | ||
596 | static int | |
597 | gf119_disp_dmac_init(struct nvkm_disp_chan *chan) | |
598 | { | |
599 | struct nvkm_subdev *subdev = &chan->disp->engine.subdev; | |
600 | struct nvkm_device *device = subdev->device; | |
601 | int ctrl = chan->chid.ctrl; | |
602 | int user = chan->chid.user; | |
603 | ||
604 | /* initialise channel for dma command submission */ | |
605 | nvkm_wr32(device, 0x610494 + (ctrl * 0x0010), chan->push); | |
606 | nvkm_wr32(device, 0x610498 + (ctrl * 0x0010), 0x00010000); | |
607 | nvkm_wr32(device, 0x61049c + (ctrl * 0x0010), 0x00000001); | |
608 | nvkm_mask(device, 0x610490 + (ctrl * 0x0010), 0x00000010, 0x00000010); | |
609 | nvkm_wr32(device, 0x640000 + (ctrl * 0x1000), chan->suspend_put); | |
610 | nvkm_wr32(device, 0x610490 + (ctrl * 0x0010), 0x00000013); | |
611 | ||
612 | /* wait for it to go inactive */ | |
613 | if (nvkm_msec(device, 2000, | |
614 | if (!(nvkm_rd32(device, 0x610490 + (ctrl * 0x10)) & 0x80000000)) | |
615 | break; | |
616 | ) < 0) { | |
617 | nvkm_error(subdev, "ch %d init: %08x\n", user, | |
618 | nvkm_rd32(device, 0x610490 + (ctrl * 0x10))); | |
619 | return -EBUSY; | |
620 | } | |
621 | ||
622 | return 0; | |
623 | } | |
624 | ||
625 | const struct nvkm_disp_chan_func | |
626 | gf119_disp_dmac_func = { | |
889fcbe9 | 627 | .push = nv50_disp_dmac_push, |
acbe9ecf BS |
628 | .init = gf119_disp_dmac_init, |
629 | .fini = gf119_disp_dmac_fini, | |
630 | .intr = gf119_disp_chan_intr, | |
631 | .user = nv50_disp_chan_user, | |
632 | .bind = gf119_disp_dmac_bind, | |
633 | }; | |
634 | ||
889fcbe9 BS |
635 | const struct nvkm_disp_chan_user |
636 | gf119_disp_curs = { | |
637 | .func = &gf119_disp_pioc_func, | |
638 | .ctrl = 13, | |
639 | .user = 13, | |
640 | }; | |
acbe9ecf | 641 | |
889fcbe9 BS |
642 | const struct nvkm_disp_chan_user |
643 | gf119_disp_oimm = { | |
644 | .func = &gf119_disp_pioc_func, | |
645 | .ctrl = 9, | |
646 | .user = 9, | |
647 | }; | |
acbe9ecf BS |
648 | |
649 | static const struct nvkm_disp_mthd_list | |
650 | gf119_disp_ovly_mthd_base = { | |
651 | .mthd = 0x0000, | |
652 | .data = { | |
653 | { 0x0080, 0x665080 }, | |
654 | { 0x0084, 0x665084 }, | |
655 | { 0x0088, 0x665088 }, | |
656 | { 0x008c, 0x66508c }, | |
657 | { 0x0090, 0x665090 }, | |
658 | { 0x0094, 0x665094 }, | |
659 | { 0x00a0, 0x6650a0 }, | |
660 | { 0x00a4, 0x6650a4 }, | |
661 | { 0x00b0, 0x6650b0 }, | |
662 | { 0x00b4, 0x6650b4 }, | |
663 | { 0x00b8, 0x6650b8 }, | |
664 | { 0x00c0, 0x6650c0 }, | |
665 | { 0x00e0, 0x6650e0 }, | |
666 | { 0x00e4, 0x6650e4 }, | |
667 | { 0x00e8, 0x6650e8 }, | |
668 | { 0x0100, 0x665100 }, | |
669 | { 0x0104, 0x665104 }, | |
670 | { 0x0108, 0x665108 }, | |
671 | { 0x010c, 0x66510c }, | |
672 | { 0x0110, 0x665110 }, | |
673 | { 0x0118, 0x665118 }, | |
674 | { 0x011c, 0x66511c }, | |
675 | { 0x0120, 0x665120 }, | |
676 | { 0x0124, 0x665124 }, | |
677 | { 0x0130, 0x665130 }, | |
678 | { 0x0134, 0x665134 }, | |
679 | { 0x0138, 0x665138 }, | |
680 | { 0x013c, 0x66513c }, | |
681 | { 0x0140, 0x665140 }, | |
682 | { 0x0144, 0x665144 }, | |
683 | { 0x0148, 0x665148 }, | |
684 | { 0x014c, 0x66514c }, | |
685 | { 0x0150, 0x665150 }, | |
686 | { 0x0154, 0x665154 }, | |
687 | { 0x0158, 0x665158 }, | |
688 | { 0x015c, 0x66515c }, | |
689 | { 0x0160, 0x665160 }, | |
690 | { 0x0164, 0x665164 }, | |
691 | { 0x0168, 0x665168 }, | |
692 | { 0x016c, 0x66516c }, | |
693 | { 0x0400, 0x665400 }, | |
694 | { 0x0408, 0x665408 }, | |
695 | { 0x040c, 0x66540c }, | |
696 | { 0x0410, 0x665410 }, | |
697 | {} | |
698 | } | |
699 | }; | |
700 | ||
701 | static const struct nvkm_disp_chan_mthd | |
702 | gf119_disp_ovly_mthd = { | |
703 | .name = "Overlay", | |
704 | .addr = 0x001000, | |
705 | .prev = -0x020000, | |
706 | .data = { | |
707 | { "Global", 1, &gf119_disp_ovly_mthd_base }, | |
708 | {} | |
709 | } | |
710 | }; | |
711 | ||
889fcbe9 BS |
712 | static const struct nvkm_disp_chan_user |
713 | gf119_disp_ovly = { | |
714 | .func = &gf119_disp_dmac_func, | |
715 | .ctrl = 5, | |
716 | .user = 5, | |
717 | .mthd = &gf119_disp_ovly_mthd, | |
718 | }; | |
acbe9ecf BS |
719 | |
720 | static const struct nvkm_disp_mthd_list | |
721 | gf119_disp_base_mthd_base = { | |
722 | .mthd = 0x0000, | |
723 | .addr = 0x000000, | |
724 | .data = { | |
725 | { 0x0080, 0x661080 }, | |
726 | { 0x0084, 0x661084 }, | |
727 | { 0x0088, 0x661088 }, | |
728 | { 0x008c, 0x66108c }, | |
729 | { 0x0090, 0x661090 }, | |
730 | { 0x0094, 0x661094 }, | |
731 | { 0x00a0, 0x6610a0 }, | |
732 | { 0x00a4, 0x6610a4 }, | |
733 | { 0x00c0, 0x6610c0 }, | |
734 | { 0x00c4, 0x6610c4 }, | |
735 | { 0x00c8, 0x6610c8 }, | |
736 | { 0x00cc, 0x6610cc }, | |
737 | { 0x00e0, 0x6610e0 }, | |
738 | { 0x00e4, 0x6610e4 }, | |
739 | { 0x00e8, 0x6610e8 }, | |
740 | { 0x00ec, 0x6610ec }, | |
741 | { 0x00fc, 0x6610fc }, | |
742 | { 0x0100, 0x661100 }, | |
743 | { 0x0104, 0x661104 }, | |
744 | { 0x0108, 0x661108 }, | |
745 | { 0x010c, 0x66110c }, | |
746 | { 0x0110, 0x661110 }, | |
747 | { 0x0114, 0x661114 }, | |
748 | { 0x0118, 0x661118 }, | |
749 | { 0x011c, 0x66111c }, | |
750 | { 0x0130, 0x661130 }, | |
751 | { 0x0134, 0x661134 }, | |
752 | { 0x0138, 0x661138 }, | |
753 | { 0x013c, 0x66113c }, | |
754 | { 0x0140, 0x661140 }, | |
755 | { 0x0144, 0x661144 }, | |
756 | { 0x0148, 0x661148 }, | |
757 | { 0x014c, 0x66114c }, | |
758 | { 0x0150, 0x661150 }, | |
759 | { 0x0154, 0x661154 }, | |
760 | { 0x0158, 0x661158 }, | |
761 | { 0x015c, 0x66115c }, | |
762 | { 0x0160, 0x661160 }, | |
763 | { 0x0164, 0x661164 }, | |
764 | { 0x0168, 0x661168 }, | |
765 | { 0x016c, 0x66116c }, | |
766 | {} | |
767 | } | |
768 | }; | |
769 | ||
770 | static const struct nvkm_disp_mthd_list | |
771 | gf119_disp_base_mthd_image = { | |
772 | .mthd = 0x0020, | |
773 | .addr = 0x000020, | |
774 | .data = { | |
775 | { 0x0400, 0x661400 }, | |
776 | { 0x0404, 0x661404 }, | |
777 | { 0x0408, 0x661408 }, | |
778 | { 0x040c, 0x66140c }, | |
779 | { 0x0410, 0x661410 }, | |
780 | {} | |
781 | } | |
782 | }; | |
783 | ||
784 | const struct nvkm_disp_chan_mthd | |
785 | gf119_disp_base_mthd = { | |
786 | .name = "Base", | |
787 | .addr = 0x001000, | |
788 | .prev = -0x020000, | |
789 | .data = { | |
790 | { "Global", 1, &gf119_disp_base_mthd_base }, | |
791 | { "Image", 2, &gf119_disp_base_mthd_image }, | |
792 | {} | |
793 | } | |
794 | }; | |
795 | ||
889fcbe9 BS |
796 | const struct nvkm_disp_chan_user |
797 | gf119_disp_base = { | |
798 | .func = &gf119_disp_dmac_func, | |
799 | .ctrl = 1, | |
800 | .user = 1, | |
801 | .mthd = &gf119_disp_base_mthd, | |
802 | }; | |
acbe9ecf BS |
803 | |
804 | const struct nvkm_disp_mthd_list | |
805 | gf119_disp_core_mthd_base = { | |
806 | .mthd = 0x0000, | |
807 | .addr = 0x000000, | |
808 | .data = { | |
809 | { 0x0080, 0x660080 }, | |
810 | { 0x0084, 0x660084 }, | |
811 | { 0x0088, 0x660088 }, | |
812 | { 0x008c, 0x000000 }, | |
813 | {} | |
814 | } | |
815 | }; | |
816 | ||
817 | const struct nvkm_disp_mthd_list | |
818 | gf119_disp_core_mthd_dac = { | |
819 | .mthd = 0x0020, | |
820 | .addr = 0x000020, | |
821 | .data = { | |
822 | { 0x0180, 0x660180 }, | |
823 | { 0x0184, 0x660184 }, | |
824 | { 0x0188, 0x660188 }, | |
825 | { 0x0190, 0x660190 }, | |
826 | {} | |
827 | } | |
828 | }; | |
829 | ||
830 | const struct nvkm_disp_mthd_list | |
831 | gf119_disp_core_mthd_sor = { | |
832 | .mthd = 0x0020, | |
833 | .addr = 0x000020, | |
834 | .data = { | |
835 | { 0x0200, 0x660200 }, | |
836 | { 0x0204, 0x660204 }, | |
837 | { 0x0208, 0x660208 }, | |
838 | { 0x0210, 0x660210 }, | |
839 | {} | |
840 | } | |
841 | }; | |
842 | ||
843 | const struct nvkm_disp_mthd_list | |
844 | gf119_disp_core_mthd_pior = { | |
845 | .mthd = 0x0020, | |
846 | .addr = 0x000020, | |
847 | .data = { | |
848 | { 0x0300, 0x660300 }, | |
849 | { 0x0304, 0x660304 }, | |
850 | { 0x0308, 0x660308 }, | |
851 | { 0x0310, 0x660310 }, | |
852 | {} | |
853 | } | |
854 | }; | |
855 | ||
856 | static const struct nvkm_disp_mthd_list | |
857 | gf119_disp_core_mthd_head = { | |
858 | .mthd = 0x0300, | |
859 | .addr = 0x000300, | |
860 | .data = { | |
861 | { 0x0400, 0x660400 }, | |
862 | { 0x0404, 0x660404 }, | |
863 | { 0x0408, 0x660408 }, | |
864 | { 0x040c, 0x66040c }, | |
865 | { 0x0410, 0x660410 }, | |
866 | { 0x0414, 0x660414 }, | |
867 | { 0x0418, 0x660418 }, | |
868 | { 0x041c, 0x66041c }, | |
869 | { 0x0420, 0x660420 }, | |
870 | { 0x0424, 0x660424 }, | |
871 | { 0x0428, 0x660428 }, | |
872 | { 0x042c, 0x66042c }, | |
873 | { 0x0430, 0x660430 }, | |
874 | { 0x0434, 0x660434 }, | |
875 | { 0x0438, 0x660438 }, | |
876 | { 0x0440, 0x660440 }, | |
877 | { 0x0444, 0x660444 }, | |
878 | { 0x0448, 0x660448 }, | |
879 | { 0x044c, 0x66044c }, | |
880 | { 0x0450, 0x660450 }, | |
881 | { 0x0454, 0x660454 }, | |
882 | { 0x0458, 0x660458 }, | |
883 | { 0x045c, 0x66045c }, | |
884 | { 0x0460, 0x660460 }, | |
885 | { 0x0468, 0x660468 }, | |
886 | { 0x046c, 0x66046c }, | |
887 | { 0x0470, 0x660470 }, | |
888 | { 0x0474, 0x660474 }, | |
889 | { 0x0480, 0x660480 }, | |
890 | { 0x0484, 0x660484 }, | |
891 | { 0x048c, 0x66048c }, | |
892 | { 0x0490, 0x660490 }, | |
893 | { 0x0494, 0x660494 }, | |
894 | { 0x0498, 0x660498 }, | |
895 | { 0x04b0, 0x6604b0 }, | |
896 | { 0x04b8, 0x6604b8 }, | |
897 | { 0x04bc, 0x6604bc }, | |
898 | { 0x04c0, 0x6604c0 }, | |
899 | { 0x04c4, 0x6604c4 }, | |
900 | { 0x04c8, 0x6604c8 }, | |
901 | { 0x04d0, 0x6604d0 }, | |
902 | { 0x04d4, 0x6604d4 }, | |
903 | { 0x04e0, 0x6604e0 }, | |
904 | { 0x04e4, 0x6604e4 }, | |
905 | { 0x04e8, 0x6604e8 }, | |
906 | { 0x04ec, 0x6604ec }, | |
907 | { 0x04f0, 0x6604f0 }, | |
908 | { 0x04f4, 0x6604f4 }, | |
909 | { 0x04f8, 0x6604f8 }, | |
910 | { 0x04fc, 0x6604fc }, | |
911 | { 0x0500, 0x660500 }, | |
912 | { 0x0504, 0x660504 }, | |
913 | { 0x0508, 0x660508 }, | |
914 | { 0x050c, 0x66050c }, | |
915 | { 0x0510, 0x660510 }, | |
916 | { 0x0514, 0x660514 }, | |
917 | { 0x0518, 0x660518 }, | |
918 | { 0x051c, 0x66051c }, | |
919 | { 0x052c, 0x66052c }, | |
920 | { 0x0530, 0x660530 }, | |
921 | { 0x054c, 0x66054c }, | |
922 | { 0x0550, 0x660550 }, | |
923 | { 0x0554, 0x660554 }, | |
924 | { 0x0558, 0x660558 }, | |
925 | { 0x055c, 0x66055c }, | |
926 | {} | |
927 | } | |
928 | }; | |
929 | ||
930 | static const struct nvkm_disp_chan_mthd | |
931 | gf119_disp_core_mthd = { | |
932 | .name = "Core", | |
933 | .addr = 0x000000, | |
934 | .prev = -0x020000, | |
935 | .data = { | |
936 | { "Global", 1, &gf119_disp_core_mthd_base }, | |
937 | { "DAC", 3, &gf119_disp_core_mthd_dac }, | |
938 | { "SOR", 8, &gf119_disp_core_mthd_sor }, | |
939 | { "PIOR", 4, &gf119_disp_core_mthd_pior }, | |
940 | { "HEAD", 4, &gf119_disp_core_mthd_head }, | |
941 | {} | |
942 | } | |
943 | }; | |
944 | ||
945 | void | |
946 | gf119_disp_core_fini(struct nvkm_disp_chan *chan) | |
947 | { | |
948 | struct nvkm_subdev *subdev = &chan->disp->engine.subdev; | |
949 | struct nvkm_device *device = subdev->device; | |
950 | ||
951 | /* deactivate channel */ | |
952 | nvkm_mask(device, 0x610490, 0x00000010, 0x00000000); | |
953 | nvkm_mask(device, 0x610490, 0x00000003, 0x00000000); | |
954 | if (nvkm_msec(device, 2000, | |
955 | if (!(nvkm_rd32(device, 0x610490) & 0x001e0000)) | |
956 | break; | |
957 | ) < 0) { | |
958 | nvkm_error(subdev, "core fini: %08x\n", | |
959 | nvkm_rd32(device, 0x610490)); | |
960 | } | |
961 | ||
962 | chan->suspend_put = nvkm_rd32(device, 0x640000); | |
963 | } | |
964 | ||
965 | static int | |
966 | gf119_disp_core_init(struct nvkm_disp_chan *chan) | |
967 | { | |
968 | struct nvkm_subdev *subdev = &chan->disp->engine.subdev; | |
969 | struct nvkm_device *device = subdev->device; | |
970 | ||
971 | /* initialise channel for dma command submission */ | |
972 | nvkm_wr32(device, 0x610494, chan->push); | |
973 | nvkm_wr32(device, 0x610498, 0x00010000); | |
974 | nvkm_wr32(device, 0x61049c, 0x00000001); | |
975 | nvkm_mask(device, 0x610490, 0x00000010, 0x00000010); | |
976 | nvkm_wr32(device, 0x640000, chan->suspend_put); | |
977 | nvkm_wr32(device, 0x610490, 0x01000013); | |
978 | ||
979 | /* wait for it to go inactive */ | |
980 | if (nvkm_msec(device, 2000, | |
981 | if (!(nvkm_rd32(device, 0x610490) & 0x80000000)) | |
982 | break; | |
983 | ) < 0) { | |
984 | nvkm_error(subdev, "core init: %08x\n", | |
985 | nvkm_rd32(device, 0x610490)); | |
986 | return -EBUSY; | |
987 | } | |
988 | ||
989 | return 0; | |
990 | } | |
991 | ||
992 | const struct nvkm_disp_chan_func | |
993 | gf119_disp_core_func = { | |
889fcbe9 | 994 | .push = nv50_disp_dmac_push, |
acbe9ecf BS |
995 | .init = gf119_disp_core_init, |
996 | .fini = gf119_disp_core_fini, | |
997 | .intr = gf119_disp_chan_intr, | |
998 | .user = nv50_disp_chan_user, | |
999 | .bind = gf119_disp_dmac_bind, | |
1000 | }; | |
1001 | ||
889fcbe9 BS |
1002 | static const struct nvkm_disp_chan_user |
1003 | gf119_disp_core = { | |
1004 | .func = &gf119_disp_core_func, | |
1005 | .ctrl = 0, | |
1006 | .user = 0, | |
1007 | .mthd = &gf119_disp_core_mthd, | |
1008 | }; | |
acbe9ecf | 1009 | |
2a7909c0 | 1010 | void |
af85389c | 1011 | gf119_disp_super(struct work_struct *work) |
2a7909c0 | 1012 | { |
3517e6b6 | 1013 | struct nvkm_disp *disp = container_of(work, struct nvkm_disp, super.work); |
92fba5d3 | 1014 | struct nvkm_subdev *subdev = &disp->engine.subdev; |
2a7909c0 | 1015 | struct nvkm_device *device = subdev->device; |
a1c93078 | 1016 | struct nvkm_head *head; |
2a7909c0 | 1017 | u32 mask[4]; |
2a7909c0 | 1018 | |
3517e6b6 | 1019 | nvkm_debug(subdev, "supervisor %d\n", ffs(disp->super.pending)); |
a6fd8f93 BS |
1020 | mutex_lock(&disp->super.mutex); |
1021 | ||
92fba5d3 | 1022 | list_for_each_entry(head, &disp->heads, head) { |
a1c93078 BS |
1023 | mask[head->id] = nvkm_rd32(device, 0x6101d4 + (head->id * 0x800)); |
1024 | HEAD_DBG(head, "%08x", mask[head->id]); | |
2a7909c0 BS |
1025 | } |
1026 | ||
3517e6b6 | 1027 | if (disp->super.pending & 0x00000001) { |
0ce41e3c | 1028 | nv50_disp_chan_mthd(disp->chan[0], NV_DBG_DEBUG); |
29c0ca73 | 1029 | nv50_disp_super_1(disp); |
92fba5d3 | 1030 | list_for_each_entry(head, &disp->heads, head) { |
a1c93078 | 1031 | if (!(mask[head->id] & 0x00001000)) |
2a7909c0 | 1032 | continue; |
327c5581 | 1033 | nv50_disp_super_1_0(disp, head); |
2a7909c0 BS |
1034 | } |
1035 | } else | |
3517e6b6 | 1036 | if (disp->super.pending & 0x00000002) { |
92fba5d3 | 1037 | list_for_each_entry(head, &disp->heads, head) { |
a1c93078 | 1038 | if (!(mask[head->id] & 0x00001000)) |
2a7909c0 | 1039 | continue; |
d52e948c | 1040 | nv50_disp_super_2_0(disp, head); |
2a7909c0 | 1041 | } |
92fba5d3 | 1042 | list_for_each_entry(head, &disp->heads, head) { |
a1c93078 | 1043 | if (!(mask[head->id] & 0x00010000)) |
2a7909c0 | 1044 | continue; |
1f0c9eaf | 1045 | nv50_disp_super_2_1(disp, head); |
2a7909c0 | 1046 | } |
92fba5d3 | 1047 | list_for_each_entry(head, &disp->heads, head) { |
a1c93078 | 1048 | if (!(mask[head->id] & 0x00001000)) |
2a7909c0 | 1049 | continue; |
8d7ef84d | 1050 | nv50_disp_super_2_2(disp, head); |
2a7909c0 BS |
1051 | } |
1052 | } else | |
3517e6b6 | 1053 | if (disp->super.pending & 0x00000004) { |
92fba5d3 | 1054 | list_for_each_entry(head, &disp->heads, head) { |
a1c93078 | 1055 | if (!(mask[head->id] & 0x00001000)) |
2a7909c0 | 1056 | continue; |
0d93cd92 | 1057 | nv50_disp_super_3_0(disp, head); |
2a7909c0 BS |
1058 | } |
1059 | } | |
1060 | ||
92fba5d3 | 1061 | list_for_each_entry(head, &disp->heads, head) |
a1c93078 | 1062 | nvkm_wr32(device, 0x6101d4 + (head->id * 0x800), 0x00000000); |
a6fd8f93 | 1063 | |
2a7909c0 | 1064 | nvkm_wr32(device, 0x6101d0, 0x80000000); |
a6fd8f93 | 1065 | mutex_unlock(&disp->super.mutex); |
2a7909c0 BS |
1066 | } |
1067 | ||
fd47877f | 1068 | void |
92fba5d3 | 1069 | gf119_disp_intr_error(struct nvkm_disp *disp, int chid) |
2a7909c0 | 1070 | { |
92fba5d3 | 1071 | struct nvkm_subdev *subdev = &disp->engine.subdev; |
2a7909c0 | 1072 | struct nvkm_device *device = subdev->device; |
a8ce8b65 BS |
1073 | u32 stat = nvkm_rd32(device, 0x6101f0 + (chid * 12)); |
1074 | u32 type = (stat & 0x00007000) >> 12; | |
1075 | u32 mthd = (stat & 0x00000ffc); | |
2a7909c0 | 1076 | u32 data = nvkm_rd32(device, 0x6101f4 + (chid * 12)); |
a8ce8b65 BS |
1077 | u32 code = nvkm_rd32(device, 0x6101f8 + (chid * 12)); |
1078 | const struct nvkm_enum *reason = | |
1079 | nvkm_enum_find(nv50_disp_intr_error_type, type); | |
2a7909c0 | 1080 | |
a8ce8b65 BS |
1081 | nvkm_error(subdev, "chid %d stat %08x reason %d [%s] mthd %04x " |
1082 | "data %08x code %08x\n", | |
1083 | chid, stat, type, reason ? reason->name : "", | |
1084 | mthd, data, code); | |
2a7909c0 | 1085 | |
0ce41e3c | 1086 | if (chid < ARRAY_SIZE(disp->chan)) { |
a8ce8b65 | 1087 | switch (mthd) { |
2a7909c0 | 1088 | case 0x0080: |
0ce41e3c | 1089 | nv50_disp_chan_mthd(disp->chan[chid], NV_DBG_ERROR); |
2a7909c0 BS |
1090 | break; |
1091 | default: | |
1092 | break; | |
1093 | } | |
1094 | } | |
1095 | ||
1096 | nvkm_wr32(device, 0x61009c, (1 << chid)); | |
1097 | nvkm_wr32(device, 0x6101f0 + (chid * 12), 0x90000000); | |
1098 | } | |
1099 | ||
1100 | void | |
92fba5d3 | 1101 | gf119_disp_intr(struct nvkm_disp *disp) |
2a7909c0 | 1102 | { |
92fba5d3 | 1103 | struct nvkm_subdev *subdev = &disp->engine.subdev; |
2a7909c0 | 1104 | struct nvkm_device *device = subdev->device; |
a1c93078 | 1105 | struct nvkm_head *head; |
2a7909c0 | 1106 | u32 intr = nvkm_rd32(device, 0x610088); |
2a7909c0 BS |
1107 | |
1108 | if (intr & 0x00000001) { | |
1109 | u32 stat = nvkm_rd32(device, 0x61008c); | |
1110 | while (stat) { | |
1111 | int chid = __ffs(stat); stat &= ~(1 << chid); | |
1112 | nv50_disp_chan_uevent_send(disp, chid); | |
1113 | nvkm_wr32(device, 0x61008c, 1 << chid); | |
1114 | } | |
1115 | intr &= ~0x00000001; | |
1116 | } | |
1117 | ||
1118 | if (intr & 0x00000002) { | |
1119 | u32 stat = nvkm_rd32(device, 0x61009c); | |
1120 | int chid = ffs(stat) - 1; | |
1121 | if (chid >= 0) | |
fd47877f | 1122 | disp->func->intr_error(disp, chid); |
2a7909c0 BS |
1123 | intr &= ~0x00000002; |
1124 | } | |
1125 | ||
1126 | if (intr & 0x00100000) { | |
1127 | u32 stat = nvkm_rd32(device, 0x6100ac); | |
1128 | if (stat & 0x00000007) { | |
3517e6b6 BS |
1129 | disp->super.pending = (stat & 0x00000007); |
1130 | queue_work(disp->super.wq, &disp->super.work); | |
1131 | nvkm_wr32(device, 0x6100ac, disp->super.pending); | |
2a7909c0 BS |
1132 | stat &= ~0x00000007; |
1133 | } | |
1134 | ||
1135 | if (stat) { | |
1136 | nvkm_warn(subdev, "intr24 %08x\n", stat); | |
1137 | nvkm_wr32(device, 0x6100ac, stat); | |
1138 | } | |
1139 | ||
1140 | intr &= ~0x00100000; | |
1141 | } | |
1142 | ||
92fba5d3 | 1143 | list_for_each_entry(head, &disp->heads, head) { |
a1c93078 BS |
1144 | const u32 hoff = head->id * 0x800; |
1145 | u32 mask = 0x01000000 << head->id; | |
2a7909c0 | 1146 | if (mask & intr) { |
a1c93078 | 1147 | u32 stat = nvkm_rd32(device, 0x6100bc + hoff); |
2a7909c0 | 1148 | if (stat & 0x00000001) |
92fba5d3 | 1149 | nvkm_disp_vblank(disp, head->id); |
a1c93078 BS |
1150 | nvkm_mask(device, 0x6100bc + hoff, 0, 0); |
1151 | nvkm_rd32(device, 0x6100c0 + hoff); | |
2a7909c0 BS |
1152 | } |
1153 | } | |
1154 | } | |
1155 | ||
bb3b0a42 | 1156 | void |
8c186c83 | 1157 | gf119_disp_fini(struct nvkm_disp *disp, bool suspend) |
bb3b0a42 | 1158 | { |
92fba5d3 | 1159 | struct nvkm_device *device = disp->engine.subdev.device; |
bb3b0a42 BS |
1160 | /* disable all interrupts */ |
1161 | nvkm_wr32(device, 0x6100b0, 0x00000000); | |
1162 | } | |
1163 | ||
1164 | int | |
92fba5d3 | 1165 | gf119_disp_init(struct nvkm_disp *disp) |
bb3b0a42 | 1166 | { |
92fba5d3 | 1167 | struct nvkm_device *device = disp->engine.subdev.device; |
bb3b0a42 BS |
1168 | struct nvkm_head *head; |
1169 | u32 tmp; | |
1170 | int i; | |
1171 | ||
1172 | /* The below segments of code copying values from one register to | |
1173 | * another appear to inform EVO of the display capabilities or | |
1174 | * something similar. | |
1175 | */ | |
1176 | ||
1177 | /* ... CRTC caps */ | |
92fba5d3 | 1178 | list_for_each_entry(head, &disp->heads, head) { |
bb3b0a42 BS |
1179 | const u32 hoff = head->id * 0x800; |
1180 | tmp = nvkm_rd32(device, 0x616104 + hoff); | |
1181 | nvkm_wr32(device, 0x6101b4 + hoff, tmp); | |
1182 | tmp = nvkm_rd32(device, 0x616108 + hoff); | |
1183 | nvkm_wr32(device, 0x6101b8 + hoff, tmp); | |
1184 | tmp = nvkm_rd32(device, 0x61610c + hoff); | |
1185 | nvkm_wr32(device, 0x6101bc + hoff, tmp); | |
1186 | } | |
1187 | ||
1188 | /* ... DAC caps */ | |
1189 | for (i = 0; i < disp->dac.nr; i++) { | |
1190 | tmp = nvkm_rd32(device, 0x61a000 + (i * 0x800)); | |
1191 | nvkm_wr32(device, 0x6101c0 + (i * 0x800), tmp); | |
1192 | } | |
1193 | ||
1194 | /* ... SOR caps */ | |
1195 | for (i = 0; i < disp->sor.nr; i++) { | |
1196 | tmp = nvkm_rd32(device, 0x61c000 + (i * 0x800)); | |
1197 | nvkm_wr32(device, 0x6301c4 + (i * 0x800), tmp); | |
1198 | } | |
1199 | ||
1200 | /* steal display away from vbios, or something like that */ | |
1201 | if (nvkm_rd32(device, 0x6100ac) & 0x00000100) { | |
1202 | nvkm_wr32(device, 0x6100ac, 0x00000100); | |
1203 | nvkm_mask(device, 0x6194e8, 0x00000001, 0x00000000); | |
1204 | if (nvkm_msec(device, 2000, | |
1205 | if (!(nvkm_rd32(device, 0x6194e8) & 0x00000002)) | |
1206 | break; | |
1207 | ) < 0) | |
1208 | return -EBUSY; | |
1209 | } | |
1210 | ||
1211 | /* point at display engine memory area (hash table, objects) */ | |
1212 | nvkm_wr32(device, 0x610010, (disp->inst->addr >> 8) | 9); | |
1213 | ||
1214 | /* enable supervisor interrupts, disable everything else */ | |
1215 | nvkm_wr32(device, 0x610090, 0x00000000); | |
1216 | nvkm_wr32(device, 0x6100a0, 0x00000000); | |
1217 | nvkm_wr32(device, 0x6100b0, 0x00000307); | |
1218 | ||
1219 | /* disable underflow reporting, preventing an intermittent issue | |
1220 | * on some gk104 boards where the production vbios left this | |
1221 | * setting enabled by default. | |
1222 | * | |
1223 | * ftp://download.nvidia.com/open-gpu-doc/gk104-disable-underflow-reporting/1/gk104-disable-underflow-reporting.txt | |
1224 | */ | |
92fba5d3 | 1225 | list_for_each_entry(head, &disp->heads, head) { |
bb3b0a42 BS |
1226 | const u32 hoff = head->id * 0x800; |
1227 | nvkm_mask(device, 0x616308 + hoff, 0x00000111, 0x00000010); | |
1228 | } | |
1229 | ||
1230 | return 0; | |
1231 | } | |
1232 | ||
0407b33f | 1233 | static const struct nvkm_disp_func |
0ce41e3c | 1234 | gf119_disp = { |
acbe9ecf | 1235 | .oneinit = nv50_disp_oneinit, |
92fba5d3 BS |
1236 | .init = gf119_disp_init, |
1237 | .fini = gf119_disp_fini, | |
1238 | .intr = gf119_disp_intr, | |
fd47877f | 1239 | .intr_error = gf119_disp_intr_error, |
af85389c | 1240 | .super = gf119_disp_super, |
92fba5d3 | 1241 | .uevent = &gf119_disp_chan_uevent, |
f7b2ece3 | 1242 | .head = { .cnt = gf119_head_cnt, .new = gf119_head_new }, |
bf5d1a6b | 1243 | .dac = { .cnt = gf119_dac_cnt, .new = gf119_dac_new }, |
9fe4e177 | 1244 | .sor = { .cnt = gf119_sor_cnt, .new = gf119_sor_new }, |
168c0299 BS |
1245 | .root = { 0,0,GF110_DISP }, |
1246 | .user = { | |
889fcbe9 BS |
1247 | {{0,0,GF110_DISP_CURSOR }, nvkm_disp_chan_new, &gf119_disp_curs }, |
1248 | {{0,0,GF110_DISP_OVERLAY }, nvkm_disp_chan_new, &gf119_disp_oimm }, | |
1249 | {{0,0,GF110_DISP_BASE_CHANNEL_DMA }, nvkm_disp_chan_new, &gf119_disp_base }, | |
1250 | {{0,0,GF110_DISP_CORE_CHANNEL_DMA }, nvkm_disp_core_new, &gf119_disp_core }, | |
1251 | {{0,0,GF110_DISP_OVERLAY_CONTROL_DMA}, nvkm_disp_chan_new, &gf119_disp_ovly }, | |
168c0299 BS |
1252 | {} |
1253 | }, | |
0ce41e3c BS |
1254 | }; |
1255 | ||
70aa8670 | 1256 | int |
a7f000ec BS |
1257 | gf119_disp_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, |
1258 | struct nvkm_disp **pdisp) | |
2a7909c0 | 1259 | { |
1c6aab75 | 1260 | return nvkm_disp_new_(&gf119_disp, device, type, inst, pdisp); |
2a7909c0 | 1261 | } |