Commit | Line | Data |
---|---|---|
af85389c BS |
1 | /* |
2 | * Copyright 2014 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 "dp.h" | |
25 | #include "conn.h" | |
9648da5a | 26 | #include "head.h" |
75eefe95 | 27 | #include "ior.h" |
af85389c | 28 | |
e4060dad LP |
29 | #include <drm/display/drm_dp.h> |
30 | ||
af85389c BS |
31 | #include <subdev/bios.h> |
32 | #include <subdev/bios/init.h> | |
f6d52b21 | 33 | #include <subdev/gpio.h> |
af85389c BS |
34 | #include <subdev/i2c.h> |
35 | ||
36 | #include <nvif/event.h> | |
37 | ||
8ef23b6f BS |
38 | /* IED scripts are no longer used by UEFI/RM from Ampere, but have been updated for |
39 | * the x86 option ROM. However, the relevant VBIOS table versions weren't modified, | |
40 | * so we're unable to detect this in a nice way. | |
41 | */ | |
42 | #define AMPERE_IED_HACK(disp) ((disp)->engine.subdev.device->card_type >= GA100) | |
43 | ||
af85389c | 44 | struct lt_state { |
7786fb36 | 45 | struct nvkm_outp *outp; |
f21e5fa1 BS |
46 | |
47 | int repeaters; | |
48 | int repeater; | |
49 | ||
af85389c BS |
50 | u8 stat[6]; |
51 | u8 conf[4]; | |
52 | bool pc2; | |
53 | u8 pc2stat; | |
54 | u8 pc2conf[2]; | |
55 | }; | |
56 | ||
57 | static int | |
58 | nvkm_dp_train_sense(struct lt_state *lt, bool pc, u32 delay) | |
59 | { | |
7786fb36 | 60 | struct nvkm_outp *outp = lt->outp; |
f21e5fa1 | 61 | u32 addr; |
af85389c BS |
62 | int ret; |
63 | ||
9543e3c0 | 64 | usleep_range(delay, delay * 2); |
af85389c | 65 | |
f21e5fa1 BS |
66 | if (lt->repeater) |
67 | addr = DPCD_LTTPR_LANE0_1_STATUS(lt->repeater); | |
68 | else | |
69 | addr = DPCD_LS02; | |
70 | ||
7786fb36 | 71 | ret = nvkm_rdaux(outp->dp.aux, addr, <->stat[0], 3); |
f21e5fa1 BS |
72 | if (ret) |
73 | return ret; | |
74 | ||
75 | if (lt->repeater) | |
76 | addr = DPCD_LTTPR_LANE0_1_ADJUST(lt->repeater); | |
77 | else | |
78 | addr = DPCD_LS06; | |
79 | ||
7786fb36 | 80 | ret = nvkm_rdaux(outp->dp.aux, addr, <->stat[4], 2); |
af85389c BS |
81 | if (ret) |
82 | return ret; | |
83 | ||
84 | if (pc) { | |
7786fb36 | 85 | ret = nvkm_rdaux(outp->dp.aux, DPCD_LS0C, <->pc2stat, 1); |
af85389c BS |
86 | if (ret) |
87 | lt->pc2stat = 0x00; | |
7786fb36 BS |
88 | |
89 | OUTP_TRACE(outp, "status %6ph pc2 %02x", lt->stat, lt->pc2stat); | |
af85389c | 90 | } else { |
7786fb36 | 91 | OUTP_TRACE(outp, "status %6ph", lt->stat); |
af85389c BS |
92 | } |
93 | ||
94 | return 0; | |
95 | } | |
96 | ||
97 | static int | |
98 | nvkm_dp_train_drive(struct lt_state *lt, bool pc) | |
99 | { | |
7786fb36 BS |
100 | struct nvkm_outp *outp = lt->outp; |
101 | struct nvkm_ior *ior = outp->ior; | |
7d1fede0 BS |
102 | struct nvkm_bios *bios = ior->disp->engine.subdev.device->bios; |
103 | struct nvbios_dpout info; | |
104 | struct nvbios_dpcfg ocfg; | |
105 | u8 ver, hdr, cnt, len; | |
f21e5fa1 | 106 | u32 addr; |
7d1fede0 | 107 | u32 data; |
af85389c BS |
108 | int ret, i; |
109 | ||
7d1fede0 | 110 | for (i = 0; i < ior->dp.nr; i++) { |
af85389c BS |
111 | u8 lane = (lt->stat[4 + (i >> 1)] >> ((i & 1) * 4)) & 0xf; |
112 | u8 lpc2 = (lt->pc2stat >> (i * 2)) & 0x3; | |
113 | u8 lpre = (lane & 0x0c) >> 2; | |
114 | u8 lvsw = (lane & 0x03) >> 0; | |
115 | u8 hivs = 3 - lpre; | |
116 | u8 hipe = 3; | |
117 | u8 hipc = 3; | |
118 | ||
119 | if (lpc2 >= hipc) | |
120 | lpc2 = hipc | DPCD_LC0F_LANE0_MAX_POST_CURSOR2_REACHED; | |
121 | if (lpre >= hipe) { | |
122 | lpre = hipe | DPCD_LC03_MAX_SWING_REACHED; /* yes. */ | |
123 | lvsw = hivs = 3 - (lpre & 3); | |
124 | } else | |
125 | if (lvsw >= hivs) { | |
126 | lvsw = hivs | DPCD_LC03_MAX_SWING_REACHED; | |
127 | } | |
128 | ||
129 | lt->conf[i] = (lpre << 3) | lvsw; | |
130 | lt->pc2conf[i >> 1] |= lpc2 << ((i & 1) * 4); | |
131 | ||
7786fb36 | 132 | OUTP_TRACE(outp, "config lane %d %02x %02x", i, lt->conf[i], lpc2); |
7d1fede0 | 133 | |
f21e5fa1 BS |
134 | if (lt->repeater != lt->repeaters) |
135 | continue; | |
136 | ||
7786fb36 | 137 | data = nvbios_dpout_match(bios, outp->info.hasht, outp->info.hashm, |
7d1fede0 BS |
138 | &ver, &hdr, &cnt, &len, &info); |
139 | if (!data) | |
140 | continue; | |
141 | ||
7786fb36 BS |
142 | data = nvbios_dpcfg_match(bios, data, lpc2 & 3, lvsw & 3, lpre & 3, |
143 | &ver, &hdr, &cnt, &len, &ocfg); | |
7d1fede0 BS |
144 | if (!data) |
145 | continue; | |
146 | ||
9a4514fb | 147 | ior->func->dp->drive(ior, i, ocfg.pc, ocfg.dc, ocfg.pe, ocfg.tx_pu); |
af85389c BS |
148 | } |
149 | ||
f21e5fa1 BS |
150 | if (lt->repeater) |
151 | addr = DPCD_LTTPR_LANE0_SET(lt->repeater); | |
152 | else | |
153 | addr = DPCD_LC03(0); | |
154 | ||
7786fb36 | 155 | ret = nvkm_wraux(outp->dp.aux, addr, lt->conf, 4); |
af85389c BS |
156 | if (ret) |
157 | return ret; | |
158 | ||
159 | if (pc) { | |
7786fb36 | 160 | ret = nvkm_wraux(outp->dp.aux, DPCD_LC0F, lt->pc2conf, 2); |
af85389c BS |
161 | if (ret) |
162 | return ret; | |
163 | } | |
164 | ||
165 | return 0; | |
166 | } | |
167 | ||
168 | static void | |
169 | nvkm_dp_train_pattern(struct lt_state *lt, u8 pattern) | |
170 | { | |
7786fb36 | 171 | struct nvkm_outp *outp = lt->outp; |
f21e5fa1 | 172 | u32 addr; |
af85389c BS |
173 | u8 sink_tp; |
174 | ||
7786fb36 | 175 | OUTP_TRACE(outp, "training pattern %d", pattern); |
9a4514fb | 176 | outp->ior->func->dp->pattern(outp->ior, pattern); |
af85389c | 177 | |
f21e5fa1 BS |
178 | if (lt->repeater) |
179 | addr = DPCD_LTTPR_PATTERN_SET(lt->repeater); | |
180 | else | |
181 | addr = DPCD_LC02; | |
182 | ||
7786fb36 | 183 | nvkm_rdaux(outp->dp.aux, addr, &sink_tp, 1); |
af85389c | 184 | sink_tp &= ~DPCD_LC02_TRAINING_PATTERN_SET; |
3edcd504 | 185 | sink_tp |= (pattern != 4) ? pattern : 7; |
be5b6985 BS |
186 | |
187 | if (pattern != 0) | |
188 | sink_tp |= DPCD_LC02_SCRAMBLING_DISABLE; | |
189 | else | |
190 | sink_tp &= ~DPCD_LC02_SCRAMBLING_DISABLE; | |
7786fb36 | 191 | nvkm_wraux(outp->dp.aux, addr, &sink_tp, 1); |
af85389c BS |
192 | } |
193 | ||
194 | static int | |
195 | nvkm_dp_train_eq(struct lt_state *lt) | |
196 | { | |
7786fb36 | 197 | struct nvkm_i2c_aux *aux = lt->outp->dp.aux; |
af85389c | 198 | bool eq_done = false, cr_done = true; |
9543e3c0 | 199 | int tries = 0, usec = 0, i; |
f21e5fa1 | 200 | u8 data; |
af85389c | 201 | |
f21e5fa1 BS |
202 | if (lt->repeater) { |
203 | if (!nvkm_rdaux(aux, DPCD_LTTPR_AUX_RD_INTERVAL(lt->repeater), &data, sizeof(data))) | |
204 | usec = (data & DPCD_RC0E_AUX_RD_INTERVAL) * 4000; | |
205 | ||
206 | nvkm_dp_train_pattern(lt, 4); | |
207 | } else { | |
7786fb36 BS |
208 | if (lt->outp->dp.dpcd[DPCD_RC00_DPCD_REV] >= 0x14 && |
209 | lt->outp->dp.dpcd[DPCD_RC03] & DPCD_RC03_TPS4_SUPPORTED) | |
3edcd504 BS |
210 | nvkm_dp_train_pattern(lt, 4); |
211 | else | |
7786fb36 BS |
212 | if (lt->outp->dp.dpcd[DPCD_RC00_DPCD_REV] >= 0x12 && |
213 | lt->outp->dp.dpcd[DPCD_RC02] & DPCD_RC02_TPS3_SUPPORTED) | |
3edcd504 BS |
214 | nvkm_dp_train_pattern(lt, 3); |
215 | else | |
216 | nvkm_dp_train_pattern(lt, 2); | |
9543e3c0 | 217 | |
7786fb36 | 218 | usec = (lt->outp->dp.dpcd[DPCD_RC0E] & DPCD_RC0E_AUX_RD_INTERVAL) * 4000; |
3edcd504 | 219 | } |
af85389c BS |
220 | |
221 | do { | |
222 | if ((tries && | |
223 | nvkm_dp_train_drive(lt, lt->pc2)) || | |
9543e3c0 | 224 | nvkm_dp_train_sense(lt, lt->pc2, usec ? usec : 400)) |
af85389c BS |
225 | break; |
226 | ||
227 | eq_done = !!(lt->stat[2] & DPCD_LS04_INTERLANE_ALIGN_DONE); | |
7786fb36 | 228 | for (i = 0; i < lt->outp->ior->dp.nr && eq_done; i++) { |
af85389c BS |
229 | u8 lane = (lt->stat[i >> 1] >> ((i & 1) * 4)) & 0xf; |
230 | if (!(lane & DPCD_LS02_LANE0_CR_DONE)) | |
231 | cr_done = false; | |
232 | if (!(lane & DPCD_LS02_LANE0_CHANNEL_EQ_DONE) || | |
233 | !(lane & DPCD_LS02_LANE0_SYMBOL_LOCKED)) | |
234 | eq_done = false; | |
235 | } | |
236 | } while (!eq_done && cr_done && ++tries <= 5); | |
237 | ||
238 | return eq_done ? 0 : -1; | |
239 | } | |
240 | ||
241 | static int | |
242 | nvkm_dp_train_cr(struct lt_state *lt) | |
243 | { | |
244 | bool cr_done = false, abort = false; | |
245 | int voltage = lt->conf[0] & DPCD_LC03_VOLTAGE_SWING_SET; | |
9543e3c0 | 246 | int tries = 0, usec = 0, i; |
af85389c BS |
247 | |
248 | nvkm_dp_train_pattern(lt, 1); | |
249 | ||
7786fb36 BS |
250 | if (lt->outp->dp.dpcd[DPCD_RC00_DPCD_REV] < 0x14 && !lt->repeater) |
251 | usec = (lt->outp->dp.dpcd[DPCD_RC0E] & DPCD_RC0E_AUX_RD_INTERVAL) * 4000; | |
9543e3c0 | 252 | |
af85389c BS |
253 | do { |
254 | if (nvkm_dp_train_drive(lt, false) || | |
9543e3c0 | 255 | nvkm_dp_train_sense(lt, false, usec ? usec : 100)) |
af85389c BS |
256 | break; |
257 | ||
258 | cr_done = true; | |
7786fb36 | 259 | for (i = 0; i < lt->outp->ior->dp.nr; i++) { |
af85389c BS |
260 | u8 lane = (lt->stat[i >> 1] >> ((i & 1) * 4)) & 0xf; |
261 | if (!(lane & DPCD_LS02_LANE0_CR_DONE)) { | |
262 | cr_done = false; | |
263 | if (lt->conf[i] & DPCD_LC03_MAX_SWING_REACHED) | |
264 | abort = true; | |
265 | break; | |
266 | } | |
267 | } | |
268 | ||
269 | if ((lt->conf[0] & DPCD_LC03_VOLTAGE_SWING_SET) != voltage) { | |
270 | voltage = lt->conf[0] & DPCD_LC03_VOLTAGE_SWING_SET; | |
271 | tries = 0; | |
272 | } | |
273 | } while (!cr_done && !abort && ++tries < 5); | |
274 | ||
275 | return cr_done ? 0 : -1; | |
276 | } | |
277 | ||
278 | static int | |
8bb30c88 | 279 | nvkm_dp_train_link(struct nvkm_outp *outp, int rate) |
af85389c | 280 | { |
7786fb36 | 281 | struct nvkm_ior *ior = outp->ior; |
4423c743 | 282 | struct lt_state lt = { |
7786fb36 | 283 | .outp = outp, |
8bb30c88 | 284 | .pc2 = outp->dp.dpcd[DPCD_RC02] & DPCD_RC02_TPS3_SUPPORTED, |
4423c743 | 285 | }; |
f21e5fa1 | 286 | u8 sink[2], data; |
af85389c BS |
287 | int ret; |
288 | ||
81344372 | 289 | OUTP_DBG(outp, "training %dx%02x", ior->dp.nr, ior->dp.bw); |
af85389c | 290 | |
f21e5fa1 BS |
291 | /* Select LTTPR non-transparent mode if we have a valid configuration, |
292 | * use transparent mode otherwise. | |
293 | */ | |
7786fb36 | 294 | if (outp->dp.lttpr[0] >= 0x14) { |
f21e5fa1 | 295 | data = DPCD_LTTPR_MODE_TRANSPARENT; |
7786fb36 | 296 | nvkm_wraux(outp->dp.aux, DPCD_LTTPR_MODE, &data, sizeof(data)); |
f21e5fa1 | 297 | |
7786fb36 | 298 | if (outp->dp.lttprs) { |
f21e5fa1 | 299 | data = DPCD_LTTPR_MODE_NON_TRANSPARENT; |
7786fb36 BS |
300 | nvkm_wraux(outp->dp.aux, DPCD_LTTPR_MODE, &data, sizeof(data)); |
301 | lt.repeaters = outp->dp.lttprs; | |
f21e5fa1 BS |
302 | } |
303 | } | |
304 | ||
af85389c | 305 | /* Set desired link configuration on the sink. */ |
7786fb36 | 306 | sink[0] = (outp->dp.rate[rate].dpcd < 0) ? ior->dp.bw : 0; |
75eefe95 BS |
307 | sink[1] = ior->dp.nr; |
308 | if (ior->dp.ef) | |
af85389c BS |
309 | sink[1] |= DPCD_LC01_ENHANCED_FRAME_EN; |
310 | ||
7786fb36 | 311 | ret = nvkm_wraux(outp->dp.aux, DPCD_LC00_LINK_BW_SET, sink, 2); |
4423c743 BS |
312 | if (ret) |
313 | return ret; | |
314 | ||
7786fb36 BS |
315 | if (outp->dp.rate[rate].dpcd >= 0) { |
316 | ret = nvkm_rdaux(outp->dp.aux, DPCD_LC15_LINK_RATE_SET, &sink[0], sizeof(sink[0])); | |
70704fbf BS |
317 | if (ret) |
318 | return ret; | |
319 | ||
320 | sink[0] &= ~DPCD_LC15_LINK_RATE_SET_MASK; | |
7786fb36 | 321 | sink[0] |= outp->dp.rate[rate].dpcd; |
70704fbf | 322 | |
7786fb36 | 323 | ret = nvkm_wraux(outp->dp.aux, DPCD_LC15_LINK_RATE_SET, &sink[0], sizeof(sink[0])); |
70704fbf BS |
324 | if (ret) |
325 | return ret; | |
326 | } | |
327 | ||
4423c743 | 328 | /* Attempt to train the link in this configuration. */ |
f21e5fa1 BS |
329 | for (lt.repeater = lt.repeaters; lt.repeater >= 0; lt.repeater--) { |
330 | if (lt.repeater) | |
7786fb36 | 331 | OUTP_DBG(outp, "training LTTPR%d", lt.repeater); |
f21e5fa1 | 332 | else |
7786fb36 | 333 | OUTP_DBG(outp, "training sink"); |
f21e5fa1 BS |
334 | |
335 | memset(lt.stat, 0x00, sizeof(lt.stat)); | |
336 | ret = nvkm_dp_train_cr(<); | |
337 | if (ret == 0) | |
338 | ret = nvkm_dp_train_eq(<); | |
339 | nvkm_dp_train_pattern(<, 0); | |
340 | } | |
341 | ||
4423c743 | 342 | return ret; |
af85389c BS |
343 | } |
344 | ||
8bb30c88 BS |
345 | static int |
346 | nvkm_dp_train_links(struct nvkm_outp *outp, int rate) | |
347 | { | |
348 | struct nvkm_ior *ior = outp->ior; | |
349 | struct nvkm_disp *disp = outp->disp; | |
350 | struct nvkm_subdev *subdev = &disp->engine.subdev; | |
351 | struct nvkm_bios *bios = subdev->device->bios; | |
352 | u32 lnkcmp; | |
353 | int ret; | |
354 | ||
355 | OUTP_DBG(outp, "programming link for %dx%02x", ior->dp.nr, ior->dp.bw); | |
356 | ||
357 | /* Intersect misc. capabilities of the OR and sink. */ | |
358 | if (disp->engine.subdev.device->chipset < 0x110) | |
359 | outp->dp.dpcd[DPCD_RC03] &= ~DPCD_RC03_TPS4_SUPPORTED; | |
360 | if (disp->engine.subdev.device->chipset < 0xd0) | |
361 | outp->dp.dpcd[DPCD_RC02] &= ~DPCD_RC02_TPS3_SUPPORTED; | |
362 | ||
363 | if (AMPERE_IED_HACK(disp) && (lnkcmp = outp->dp.info.script[0])) { | |
364 | /* Execute BeforeLinkTraining script from DP Info table. */ | |
365 | while (ior->dp.bw < nvbios_rd08(bios, lnkcmp)) | |
366 | lnkcmp += 3; | |
367 | lnkcmp = nvbios_rd16(bios, lnkcmp + 1); | |
368 | ||
369 | nvbios_init(&outp->disp->engine.subdev, lnkcmp, | |
370 | init.outp = &outp->info; | |
371 | init.or = ior->id; | |
372 | init.link = ior->asy.link; | |
373 | ); | |
374 | } | |
375 | ||
376 | /* Set desired link configuration on the source. */ | |
377 | if ((lnkcmp = outp->dp.info.lnkcmp)) { | |
378 | if (outp->dp.version < 0x30) { | |
379 | while ((ior->dp.bw * 2700) < nvbios_rd16(bios, lnkcmp)) | |
380 | lnkcmp += 4; | |
381 | lnkcmp = nvbios_rd16(bios, lnkcmp + 2); | |
382 | } else { | |
383 | while (ior->dp.bw < nvbios_rd08(bios, lnkcmp)) | |
384 | lnkcmp += 3; | |
385 | lnkcmp = nvbios_rd16(bios, lnkcmp + 1); | |
386 | } | |
387 | ||
388 | nvbios_init(subdev, lnkcmp, | |
389 | init.outp = &outp->info; | |
390 | init.or = ior->id; | |
391 | init.link = ior->asy.link; | |
392 | ); | |
393 | } | |
394 | ||
395 | ret = ior->func->dp->links(ior, outp->dp.aux); | |
396 | if (ret) { | |
397 | if (ret < 0) { | |
398 | OUTP_ERR(outp, "train failed with %d", ret); | |
399 | return ret; | |
400 | } | |
401 | return 0; | |
402 | } | |
403 | ||
404 | ior->func->dp->power(ior, ior->dp.nr); | |
405 | ||
406 | /* Attempt to train the link in this configuration. */ | |
407 | return nvkm_dp_train_link(outp, rate); | |
408 | } | |
409 | ||
af85389c | 410 | static void |
7786fb36 | 411 | nvkm_dp_train_fini(struct nvkm_outp *outp) |
af85389c | 412 | { |
af85389c | 413 | /* Execute AfterLinkTraining script from DP Info table. */ |
7786fb36 BS |
414 | nvbios_init(&outp->disp->engine.subdev, outp->dp.info.script[1], |
415 | init.outp = &outp->info; | |
416 | init.or = outp->ior->id; | |
417 | init.link = outp->ior->asy.link; | |
32a232c5 | 418 | ); |
af85389c BS |
419 | } |
420 | ||
421 | static void | |
7786fb36 | 422 | nvkm_dp_train_init(struct nvkm_outp *outp) |
af85389c | 423 | { |
af85389c | 424 | /* Execute EnableSpread/DisableSpread script from DP Info table. */ |
7786fb36 BS |
425 | if (outp->dp.dpcd[DPCD_RC03] & DPCD_RC03_MAX_DOWNSPREAD) { |
426 | nvbios_init(&outp->disp->engine.subdev, outp->dp.info.script[2], | |
427 | init.outp = &outp->info; | |
428 | init.or = outp->ior->id; | |
429 | init.link = outp->ior->asy.link; | |
32a232c5 BS |
430 | ); |
431 | } else { | |
7786fb36 BS |
432 | nvbios_init(&outp->disp->engine.subdev, outp->dp.info.script[3], |
433 | init.outp = &outp->info; | |
434 | init.or = outp->ior->id; | |
435 | init.link = outp->ior->asy.link; | |
32a232c5 BS |
436 | ); |
437 | } | |
af85389c | 438 | |
7786fb36 | 439 | if (!AMPERE_IED_HACK(outp->disp)) { |
8ef23b6f | 440 | /* Execute BeforeLinkTraining script from DP Info table. */ |
7786fb36 BS |
441 | nvbios_init(&outp->disp->engine.subdev, outp->dp.info.script[0], |
442 | init.outp = &outp->info; | |
443 | init.or = outp->ior->id; | |
444 | init.link = outp->ior->asy.link; | |
8ef23b6f BS |
445 | ); |
446 | } | |
af85389c BS |
447 | } |
448 | ||
fafa8b5c | 449 | static int |
7786fb36 | 450 | nvkm_dp_train(struct nvkm_outp *outp, u32 dataKBps) |
af85389c | 451 | { |
7786fb36 | 452 | struct nvkm_ior *ior = outp->ior; |
b96a1d8c | 453 | int ret = -EINVAL, nr, rate; |
af85389c | 454 | u8 pwr; |
af85389c | 455 | |
8bb30c88 BS |
456 | /* Retraining link? Skip source configuration, it can mess up the active modeset. */ |
457 | if (atomic_read(&outp->dp.lt.done)) { | |
458 | for (rate = 0; rate < outp->dp.rates; rate++) { | |
459 | if (outp->dp.rate[rate].rate == ior->dp.bw * 27000) | |
460 | return nvkm_dp_train_link(outp, ret); | |
461 | } | |
462 | WARN_ON(1); | |
463 | return -EINVAL; | |
464 | } | |
465 | ||
af85389c | 466 | /* Ensure sink is not in a low-power state. */ |
7786fb36 | 467 | if (!nvkm_rdaux(outp->dp.aux, DPCD_SC00, &pwr, 1)) { |
af85389c BS |
468 | if ((pwr & DPCD_SC00_SET_POWER) != DPCD_SC00_SET_POWER_D0) { |
469 | pwr &= ~DPCD_SC00_SET_POWER; | |
470 | pwr |= DPCD_SC00_SET_POWER_D0; | |
7786fb36 | 471 | nvkm_wraux(outp->dp.aux, DPCD_SC00, &pwr, 1); |
af85389c BS |
472 | } |
473 | } | |
474 | ||
7786fb36 BS |
475 | ior->dp.mst = outp->dp.lt.mst; |
476 | ior->dp.ef = outp->dp.dpcd[DPCD_RC02] & DPCD_RC02_ENHANCED_FRAME_CAP; | |
b96a1d8c BS |
477 | ior->dp.nr = 0; |
478 | ||
af85389c | 479 | /* Link training. */ |
7786fb36 BS |
480 | OUTP_DBG(outp, "training"); |
481 | nvkm_dp_train_init(outp); | |
81344372 BS |
482 | |
483 | /* Validate and train at configuration requested (if any) on ACQUIRE. */ | |
484 | if (outp->dp.lt.nr) { | |
485 | for (nr = outp->dp.links; ret < 0 && nr; nr >>= 1) { | |
486 | for (rate = 0; nr == outp->dp.lt.nr && rate < outp->dp.rates; rate++) { | |
487 | if (outp->dp.rate[rate].rate / 27000 == outp->dp.lt.bw) { | |
488 | ior->dp.bw = outp->dp.rate[rate].rate / 27000; | |
489 | ior->dp.nr = nr; | |
490 | ret = nvkm_dp_train_links(outp, rate); | |
491 | } | |
492 | } | |
493 | } | |
494 | } | |
495 | ||
496 | /* Otherwise, loop through all valid link configurations that support the data rate. */ | |
7786fb36 BS |
497 | for (nr = outp->dp.links; ret < 0 && nr; nr >>= 1) { |
498 | for (rate = 0; ret < 0 && rate < outp->dp.rates; rate++) { | |
499 | if (outp->dp.rate[rate].rate * nr >= dataKBps || WARN_ON(!ior->dp.nr)) { | |
b96a1d8c | 500 | /* Program selected link configuration. */ |
7786fb36 | 501 | ior->dp.bw = outp->dp.rate[rate].rate / 27000; |
b96a1d8c | 502 | ior->dp.nr = nr; |
7786fb36 | 503 | ret = nvkm_dp_train_links(outp, rate); |
b96a1d8c | 504 | } |
49f2b376 | 505 | } |
af85389c | 506 | } |
81344372 BS |
507 | |
508 | /* Finish up. */ | |
7786fb36 | 509 | nvkm_dp_train_fini(outp); |
af85389c | 510 | if (ret < 0) |
7786fb36 | 511 | OUTP_ERR(outp, "training failed"); |
fafa8b5c | 512 | else |
7786fb36 BS |
513 | OUTP_DBG(outp, "training done"); |
514 | atomic_set(&outp->dp.lt.done, 1); | |
fafa8b5c | 515 | return ret; |
af85389c BS |
516 | } |
517 | ||
6eaa1f3c | 518 | void |
e04cfdc9 | 519 | nvkm_dp_disable(struct nvkm_outp *outp, struct nvkm_ior *ior) |
d52e948c | 520 | { |
d52e948c | 521 | /* Execute DisableLT script from DP Info Table. */ |
7786fb36 BS |
522 | nvbios_init(&ior->disp->engine.subdev, outp->dp.info.script[4], |
523 | init.outp = &outp->info; | |
d52e948c BS |
524 | init.or = ior->id; |
525 | init.link = ior->arm.link; | |
526 | ); | |
527 | } | |
528 | ||
e04cfdc9 BS |
529 | static void |
530 | nvkm_dp_release(struct nvkm_outp *outp) | |
531 | { | |
e04cfdc9 | 532 | /* Prevent link from being retrained if sink sends an IRQ. */ |
7786fb36 BS |
533 | atomic_set(&outp->dp.lt.done, 0); |
534 | outp->ior->dp.nr = 0; | |
e04cfdc9 BS |
535 | } |
536 | ||
8d7ef84d BS |
537 | static int |
538 | nvkm_dp_acquire(struct nvkm_outp *outp) | |
af85389c | 539 | { |
7786fb36 | 540 | struct nvkm_ior *ior = outp->ior; |
9648da5a | 541 | struct nvkm_head *head; |
af85389c | 542 | bool retrain = true; |
9648da5a | 543 | u32 datakbps = 0; |
fafa8b5c | 544 | u32 dataKBps; |
9648da5a | 545 | u32 linkKBps; |
fafa8b5c | 546 | u8 stat[3]; |
af85389c BS |
547 | int ret, i; |
548 | ||
7786fb36 | 549 | mutex_lock(&outp->dp.mutex); |
af85389c | 550 | |
fafa8b5c | 551 | /* Check that link configuration meets current requirements. */ |
92fba5d3 | 552 | list_for_each_entry(head, &outp->disp->heads, head) { |
9648da5a BS |
553 | if (ior->asy.head & (1 << head->id)) { |
554 | u32 khz = (head->asy.hz >> ior->asy.rgdiv) / 1000; | |
555 | datakbps += khz * head->asy.or.depth; | |
556 | } | |
557 | } | |
558 | ||
fafa8b5c BS |
559 | linkKBps = ior->dp.bw * 27000 * ior->dp.nr; |
560 | dataKBps = DIV_ROUND_UP(datakbps, 8); | |
7786fb36 BS |
561 | OUTP_DBG(outp, "data %d KB/s link %d KB/s mst %d->%d", |
562 | dataKBps, linkKBps, ior->dp.mst, outp->dp.lt.mst); | |
563 | if (linkKBps < dataKBps || ior->dp.mst != outp->dp.lt.mst) { | |
564 | OUTP_DBG(outp, "link requirements changed"); | |
af85389c BS |
565 | goto done; |
566 | } | |
567 | ||
fafa8b5c | 568 | /* Check that link is still trained. */ |
7786fb36 | 569 | ret = nvkm_rdaux(outp->dp.aux, DPCD_LS02, stat, 3); |
af85389c | 570 | if (ret) { |
7786fb36 | 571 | OUTP_DBG(outp, "failed to read link status, assuming no sink"); |
af85389c BS |
572 | goto done; |
573 | } | |
574 | ||
575 | if (stat[2] & DPCD_LS04_INTERLANE_ALIGN_DONE) { | |
fafa8b5c | 576 | for (i = 0; i < ior->dp.nr; i++) { |
af85389c BS |
577 | u8 lane = (stat[i >> 1] >> ((i & 1) * 4)) & 0x0f; |
578 | if (!(lane & DPCD_LS02_LANE0_CR_DONE) || | |
579 | !(lane & DPCD_LS02_LANE0_CHANNEL_EQ_DONE) || | |
580 | !(lane & DPCD_LS02_LANE0_SYMBOL_LOCKED)) { | |
7786fb36 | 581 | OUTP_DBG(outp, "lane %d not equalised", lane); |
af85389c BS |
582 | goto done; |
583 | } | |
584 | } | |
585 | retrain = false; | |
586 | } else { | |
7786fb36 | 587 | OUTP_DBG(outp, "no inter-lane alignment"); |
af85389c BS |
588 | } |
589 | ||
590 | done: | |
7786fb36 BS |
591 | if (retrain || !atomic_read(&outp->dp.lt.done)) |
592 | ret = nvkm_dp_train(outp, dataKBps); | |
593 | mutex_unlock(&outp->dp.mutex); | |
af85389c BS |
594 | return ret; |
595 | } | |
596 | ||
70704fbf | 597 | static bool |
7786fb36 | 598 | nvkm_dp_enable_supported_link_rates(struct nvkm_outp *outp) |
70704fbf BS |
599 | { |
600 | u8 sink_rates[DPCD_RC10_SUPPORTED_LINK_RATES__SIZE]; | |
601 | int i, j, k; | |
602 | ||
7786fb36 BS |
603 | if (outp->conn->info.type != DCB_CONNECTOR_eDP || |
604 | outp->dp.dpcd[DPCD_RC00_DPCD_REV] < 0x13 || | |
605 | nvkm_rdaux(outp->dp.aux, DPCD_RC10_SUPPORTED_LINK_RATES(0), | |
606 | sink_rates, sizeof(sink_rates))) | |
70704fbf BS |
607 | return false; |
608 | ||
609 | for (i = 0; i < ARRAY_SIZE(sink_rates); i += 2) { | |
610 | const u32 rate = ((sink_rates[i + 1] << 8) | sink_rates[i]) * 200 / 10; | |
611 | ||
7786fb36 | 612 | if (!rate || WARN_ON(outp->dp.rates == ARRAY_SIZE(outp->dp.rate))) |
70704fbf BS |
613 | break; |
614 | ||
7786fb36 BS |
615 | if (rate > outp->info.dpconf.link_bw * 27000) { |
616 | OUTP_DBG(outp, "rate %d !outp", rate); | |
70704fbf BS |
617 | continue; |
618 | } | |
619 | ||
7786fb36 BS |
620 | for (j = 0; j < outp->dp.rates; j++) { |
621 | if (rate > outp->dp.rate[j].rate) { | |
622 | for (k = outp->dp.rates; k > j; k--) | |
623 | outp->dp.rate[k] = outp->dp.rate[k - 1]; | |
70704fbf BS |
624 | break; |
625 | } | |
626 | } | |
627 | ||
7786fb36 BS |
628 | outp->dp.rate[j].dpcd = i / 2; |
629 | outp->dp.rate[j].rate = rate; | |
630 | outp->dp.rates++; | |
70704fbf BS |
631 | } |
632 | ||
7786fb36 BS |
633 | for (i = 0; i < outp->dp.rates; i++) |
634 | OUTP_DBG(outp, "link_rate[%d] = %d", outp->dp.rate[i].dpcd, outp->dp.rate[i].rate); | |
70704fbf | 635 | |
7786fb36 | 636 | return outp->dp.rates != 0; |
70704fbf BS |
637 | } |
638 | ||
e4060dad LP |
639 | /* XXX: This is a big fat hack, and this is just drm_dp_read_dpcd_caps() |
640 | * converted to work inside nvkm. This is a temporary holdover until we start | |
641 | * passing the drm_dp_aux device through NVKM | |
642 | */ | |
643 | static int | |
644 | nvkm_dp_read_dpcd_caps(struct nvkm_outp *outp) | |
645 | { | |
646 | struct nvkm_i2c_aux *aux = outp->dp.aux; | |
647 | u8 dpcd_ext[DP_RECEIVER_CAP_SIZE]; | |
648 | int ret; | |
649 | ||
650 | ret = nvkm_rdaux(aux, DPCD_RC00_DPCD_REV, outp->dp.dpcd, DP_RECEIVER_CAP_SIZE); | |
651 | if (ret < 0) | |
652 | return ret; | |
653 | ||
654 | /* | |
655 | * Prior to DP1.3 the bit represented by | |
656 | * DP_EXTENDED_RECEIVER_CAP_FIELD_PRESENT was reserved. | |
657 | * If it is set DP_DPCD_REV at 0000h could be at a value less than | |
658 | * the true capability of the panel. The only way to check is to | |
659 | * then compare 0000h and 2200h. | |
660 | */ | |
661 | if (!(outp->dp.dpcd[DP_TRAINING_AUX_RD_INTERVAL] & | |
662 | DP_EXTENDED_RECEIVER_CAP_FIELD_PRESENT)) | |
663 | return 0; | |
664 | ||
665 | ret = nvkm_rdaux(aux, DP_DP13_DPCD_REV, dpcd_ext, sizeof(dpcd_ext)); | |
666 | if (ret < 0) | |
667 | return ret; | |
668 | ||
669 | if (outp->dp.dpcd[DP_DPCD_REV] > dpcd_ext[DP_DPCD_REV]) { | |
670 | OUTP_DBG(outp, "Extended DPCD rev less than base DPCD rev (%d > %d)\n", | |
671 | outp->dp.dpcd[DP_DPCD_REV], dpcd_ext[DP_DPCD_REV]); | |
672 | return 0; | |
673 | } | |
674 | ||
675 | if (!memcmp(outp->dp.dpcd, dpcd_ext, sizeof(dpcd_ext))) | |
676 | return 0; | |
677 | ||
678 | memcpy(outp->dp.dpcd, dpcd_ext, sizeof(dpcd_ext)); | |
679 | ||
680 | return 0; | |
681 | } | |
682 | ||
a62b7493 BS |
683 | void |
684 | nvkm_dp_enable(struct nvkm_outp *outp, bool auxpwr) | |
af85389c | 685 | { |
a62b7493 | 686 | struct nvkm_gpio *gpio = outp->disp->engine.subdev.device->gpio; |
7786fb36 | 687 | struct nvkm_i2c_aux *aux = outp->dp.aux; |
af85389c | 688 | |
a62b7493 BS |
689 | if (auxpwr && !outp->dp.aux_pwr) { |
690 | /* eDP panels need powering on by us (if the VBIOS doesn't default it | |
691 | * to on) before doing any AUX channel transactions. LVDS panel power | |
692 | * is handled by the SOR itself, and not required for LVDS DDC. | |
693 | */ | |
694 | if (outp->conn->info.type == DCB_CONNECTOR_eDP) { | |
695 | int power = nvkm_gpio_get(gpio, 0, DCB_GPIO_PANEL_POWER, 0xff); | |
696 | if (power == 0) { | |
697 | nvkm_gpio_set(gpio, 0, DCB_GPIO_PANEL_POWER, 0xff, 1); | |
698 | outp->dp.aux_pwr_pu = true; | |
699 | } | |
700 | ||
701 | /* We delay here unconditionally, even if already powered, | |
702 | * because some laptop panels having a significant resume | |
703 | * delay before the panel begins responding. | |
704 | * | |
705 | * This is likely a bit of a hack, but no better idea for | |
706 | * handling this at the moment. | |
707 | */ | |
708 | msleep(300); | |
af85389c BS |
709 | } |
710 | ||
a62b7493 BS |
711 | OUTP_DBG(outp, "aux power -> always"); |
712 | nvkm_i2c_aux_monitor(aux, true); | |
713 | outp->dp.aux_pwr = true; | |
714 | ||
f21e5fa1 | 715 | /* Detect any LTTPRs before reading DPCD receiver caps. */ |
7786fb36 BS |
716 | if (!nvkm_rdaux(aux, DPCD_LTTPR_REV, outp->dp.lttpr, sizeof(outp->dp.lttpr)) && |
717 | outp->dp.lttpr[0] >= 0x14 && outp->dp.lttpr[2]) { | |
718 | switch (outp->dp.lttpr[2]) { | |
719 | case 0x80: outp->dp.lttprs = 1; break; | |
720 | case 0x40: outp->dp.lttprs = 2; break; | |
721 | case 0x20: outp->dp.lttprs = 3; break; | |
722 | case 0x10: outp->dp.lttprs = 4; break; | |
723 | case 0x08: outp->dp.lttprs = 5; break; | |
724 | case 0x04: outp->dp.lttprs = 6; break; | |
725 | case 0x02: outp->dp.lttprs = 7; break; | |
726 | case 0x01: outp->dp.lttprs = 8; break; | |
f21e5fa1 BS |
727 | default: |
728 | /* Unknown LTTPR count, we'll switch to transparent mode. */ | |
729 | WARN_ON(1); | |
7786fb36 | 730 | outp->dp.lttprs = 0; |
f21e5fa1 BS |
731 | break; |
732 | } | |
733 | } else { | |
734 | /* No LTTPR support, or zero LTTPR count - don't touch it at all. */ | |
7786fb36 | 735 | memset(outp->dp.lttpr, 0x00, sizeof(outp->dp.lttpr)); |
f21e5fa1 BS |
736 | } |
737 | ||
e4060dad | 738 | if (!nvkm_dp_read_dpcd_caps(outp)) { |
405d5382 | 739 | const u8 rates[] = { 0x1e, 0x14, 0x0a, 0x06, 0 }; |
b96a1d8c BS |
740 | const u8 *rate; |
741 | int rate_max; | |
742 | ||
7786fb36 BS |
743 | outp->dp.rates = 0; |
744 | outp->dp.links = outp->dp.dpcd[DPCD_RC02] & DPCD_RC02_MAX_LANE_COUNT; | |
745 | outp->dp.links = min(outp->dp.links, outp->info.dpconf.link_nr); | |
746 | if (outp->dp.lttprs && outp->dp.lttpr[4]) | |
747 | outp->dp.links = min_t(int, outp->dp.links, outp->dp.lttpr[4]); | |
b96a1d8c | 748 | |
7786fb36 BS |
749 | rate_max = outp->dp.dpcd[DPCD_RC01_MAX_LINK_RATE]; |
750 | rate_max = min(rate_max, outp->info.dpconf.link_bw); | |
751 | if (outp->dp.lttprs && outp->dp.lttpr[1]) | |
752 | rate_max = min_t(int, rate_max, outp->dp.lttpr[1]); | |
b96a1d8c | 753 | |
7786fb36 | 754 | if (!nvkm_dp_enable_supported_link_rates(outp)) { |
b96a1d8c | 755 | for (rate = rates; *rate; rate++) { |
7786fb36 BS |
756 | if (*rate > rate_max) |
757 | continue; | |
758 | ||
759 | if (WARN_ON(outp->dp.rates == ARRAY_SIZE(outp->dp.rate))) | |
760 | break; | |
761 | ||
762 | outp->dp.rate[outp->dp.rates].dpcd = -1; | |
763 | outp->dp.rate[outp->dp.rates].rate = *rate * 27000; | |
764 | outp->dp.rates++; | |
b96a1d8c BS |
765 | } |
766 | } | |
b96a1d8c | 767 | } |
a62b7493 BS |
768 | } else |
769 | if (!auxpwr && outp->dp.aux_pwr) { | |
7786fb36 | 770 | OUTP_DBG(outp, "aux power -> demand"); |
af85389c | 771 | nvkm_i2c_aux_monitor(aux, false); |
a62b7493 BS |
772 | outp->dp.aux_pwr = false; |
773 | atomic_set(&outp->dp.lt.done, 0); | |
af85389c | 774 | |
a62b7493 BS |
775 | /* Restore eDP panel GPIO to its prior state if we changed it, as |
776 | * it could potentially interfere with other outputs. | |
777 | */ | |
778 | if (outp->conn->info.type == DCB_CONNECTOR_eDP) { | |
779 | if (outp->dp.aux_pwr_pu) { | |
780 | nvkm_gpio_set(gpio, 0, DCB_GPIO_PANEL_POWER, 0xff, 0); | |
781 | outp->dp.aux_pwr_pu = false; | |
782 | } | |
783 | } | |
784 | } | |
af85389c BS |
785 | } |
786 | ||
af85389c | 787 | static void |
f3e70d29 | 788 | nvkm_dp_fini(struct nvkm_outp *outp) |
af85389c | 789 | { |
7786fb36 | 790 | nvkm_dp_enable(outp, false); |
af85389c BS |
791 | } |
792 | ||
793 | static void | |
f3e70d29 | 794 | nvkm_dp_init(struct nvkm_outp *outp) |
af85389c | 795 | { |
a62b7493 | 796 | nvkm_dp_enable(outp, outp->dp.enabled); |
af85389c BS |
797 | } |
798 | ||
799 | static void * | |
f3e70d29 | 800 | nvkm_dp_dtor(struct nvkm_outp *outp) |
af85389c | 801 | { |
7786fb36 | 802 | return outp; |
af85389c BS |
803 | } |
804 | ||
f3e70d29 BS |
805 | static const struct nvkm_outp_func |
806 | nvkm_dp_func = { | |
807 | .dtor = nvkm_dp_dtor, | |
808 | .init = nvkm_dp_init, | |
809 | .fini = nvkm_dp_fini, | |
8d7ef84d | 810 | .acquire = nvkm_dp_acquire, |
d52e948c | 811 | .release = nvkm_dp_release, |
e04cfdc9 | 812 | .disable = nvkm_dp_disable, |
af85389c BS |
813 | }; |
814 | ||
412dfcf3 BS |
815 | int |
816 | nvkm_dp_new(struct nvkm_disp *disp, int index, struct dcb_output *dcbE, struct nvkm_outp **poutp) | |
af85389c BS |
817 | { |
818 | struct nvkm_device *device = disp->engine.subdev.device; | |
819 | struct nvkm_bios *bios = device->bios; | |
820 | struct nvkm_i2c *i2c = device->i2c; | |
412dfcf3 | 821 | struct nvkm_outp *outp; |
af85389c BS |
822 | u8 hdr, cnt, len; |
823 | u32 data; | |
824 | int ret; | |
825 | ||
412dfcf3 BS |
826 | ret = nvkm_outp_new_(&nvkm_dp_func, disp, index, dcbE, poutp); |
827 | outp = *poutp; | |
01a97637 BS |
828 | if (ret) |
829 | return ret; | |
830 | ||
412dfcf3 BS |
831 | if (dcbE->location == 0) |
832 | outp->dp.aux = nvkm_i2c_aux_find(i2c, NVKM_I2C_AUX_CCB(dcbE->i2c_index)); | |
833 | else | |
834 | outp->dp.aux = nvkm_i2c_aux_find(i2c, NVKM_I2C_AUX_EXT(dcbE->extdev)); | |
7786fb36 BS |
835 | if (!outp->dp.aux) { |
836 | OUTP_ERR(outp, "no aux"); | |
3c66c87d | 837 | return -EINVAL; |
af85389c BS |
838 | } |
839 | ||
840 | /* bios data is not optional */ | |
7786fb36 BS |
841 | data = nvbios_dpout_match(bios, outp->info.hasht, outp->info.hashm, |
842 | &outp->dp.version, &hdr, &cnt, &len, &outp->dp.info); | |
af85389c | 843 | if (!data) { |
7786fb36 | 844 | OUTP_ERR(outp, "no bios dp data"); |
3c66c87d | 845 | return -EINVAL; |
af85389c BS |
846 | } |
847 | ||
7786fb36 | 848 | OUTP_DBG(outp, "bios dp %02x %02x %02x %02x", outp->dp.version, hdr, cnt, len); |
af85389c | 849 | |
7786fb36 BS |
850 | mutex_init(&outp->dp.mutex); |
851 | atomic_set(&outp->dp.lt.done, 0); | |
af85389c BS |
852 | return 0; |
853 | } |