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 BS |
28 | |
29 | #include <subdev/bios.h> | |
30 | #include <subdev/bios/init.h> | |
f6d52b21 | 31 | #include <subdev/gpio.h> |
af85389c BS |
32 | #include <subdev/i2c.h> |
33 | ||
34 | #include <nvif/event.h> | |
35 | ||
36 | struct lt_state { | |
f3e70d29 | 37 | struct nvkm_dp *dp; |
af85389c BS |
38 | u8 stat[6]; |
39 | u8 conf[4]; | |
40 | bool pc2; | |
41 | u8 pc2stat; | |
42 | u8 pc2conf[2]; | |
43 | }; | |
44 | ||
45 | static int | |
46 | nvkm_dp_train_sense(struct lt_state *lt, bool pc, u32 delay) | |
47 | { | |
f3e70d29 | 48 | struct nvkm_dp *dp = lt->dp; |
af85389c BS |
49 | int ret; |
50 | ||
f3e70d29 BS |
51 | if (dp->dpcd[DPCD_RC0E_AUX_RD_INTERVAL]) |
52 | mdelay(dp->dpcd[DPCD_RC0E_AUX_RD_INTERVAL] * 4); | |
af85389c BS |
53 | else |
54 | udelay(delay); | |
55 | ||
f3e70d29 | 56 | ret = nvkm_rdaux(dp->aux, DPCD_LS02, lt->stat, 6); |
af85389c BS |
57 | if (ret) |
58 | return ret; | |
59 | ||
60 | if (pc) { | |
f3e70d29 | 61 | ret = nvkm_rdaux(dp->aux, DPCD_LS0C, <->pc2stat, 1); |
af85389c BS |
62 | if (ret) |
63 | lt->pc2stat = 0x00; | |
f3e70d29 BS |
64 | OUTP_TRACE(&dp->outp, "status %6ph pc2 %02x", |
65 | lt->stat, lt->pc2stat); | |
af85389c | 66 | } else { |
f3e70d29 | 67 | OUTP_TRACE(&dp->outp, "status %6ph", lt->stat); |
af85389c BS |
68 | } |
69 | ||
70 | return 0; | |
71 | } | |
72 | ||
73 | static int | |
74 | nvkm_dp_train_drive(struct lt_state *lt, bool pc) | |
75 | { | |
f3e70d29 | 76 | struct nvkm_dp *dp = lt->dp; |
7d1fede0 BS |
77 | struct nvkm_ior *ior = dp->outp.ior; |
78 | struct nvkm_bios *bios = ior->disp->engine.subdev.device->bios; | |
79 | struct nvbios_dpout info; | |
80 | struct nvbios_dpcfg ocfg; | |
81 | u8 ver, hdr, cnt, len; | |
82 | u32 data; | |
af85389c BS |
83 | int ret, i; |
84 | ||
7d1fede0 | 85 | for (i = 0; i < ior->dp.nr; i++) { |
af85389c BS |
86 | u8 lane = (lt->stat[4 + (i >> 1)] >> ((i & 1) * 4)) & 0xf; |
87 | u8 lpc2 = (lt->pc2stat >> (i * 2)) & 0x3; | |
88 | u8 lpre = (lane & 0x0c) >> 2; | |
89 | u8 lvsw = (lane & 0x03) >> 0; | |
90 | u8 hivs = 3 - lpre; | |
91 | u8 hipe = 3; | |
92 | u8 hipc = 3; | |
93 | ||
94 | if (lpc2 >= hipc) | |
95 | lpc2 = hipc | DPCD_LC0F_LANE0_MAX_POST_CURSOR2_REACHED; | |
96 | if (lpre >= hipe) { | |
97 | lpre = hipe | DPCD_LC03_MAX_SWING_REACHED; /* yes. */ | |
98 | lvsw = hivs = 3 - (lpre & 3); | |
99 | } else | |
100 | if (lvsw >= hivs) { | |
101 | lvsw = hivs | DPCD_LC03_MAX_SWING_REACHED; | |
102 | } | |
103 | ||
104 | lt->conf[i] = (lpre << 3) | lvsw; | |
105 | lt->pc2conf[i >> 1] |= lpc2 << ((i & 1) * 4); | |
106 | ||
f3e70d29 BS |
107 | OUTP_TRACE(&dp->outp, "config lane %d %02x %02x", |
108 | i, lt->conf[i], lpc2); | |
7d1fede0 BS |
109 | |
110 | data = nvbios_dpout_match(bios, dp->outp.info.hasht, | |
111 | dp->outp.info.hashm, | |
112 | &ver, &hdr, &cnt, &len, &info); | |
113 | if (!data) | |
114 | continue; | |
115 | ||
116 | data = nvbios_dpcfg_match(bios, data, lpc2 & 3, lvsw & 3, | |
117 | lpre & 3, &ver, &hdr, &cnt, &len, | |
118 | &ocfg); | |
119 | if (!data) | |
120 | continue; | |
121 | ||
122 | ior->func->dp.drive(ior, i, ocfg.pc, ocfg.dc, | |
123 | ocfg.pe, ocfg.tx_pu); | |
af85389c BS |
124 | } |
125 | ||
f3e70d29 | 126 | ret = nvkm_wraux(dp->aux, DPCD_LC03(0), lt->conf, 4); |
af85389c BS |
127 | if (ret) |
128 | return ret; | |
129 | ||
130 | if (pc) { | |
f3e70d29 | 131 | ret = nvkm_wraux(dp->aux, DPCD_LC0F, lt->pc2conf, 2); |
af85389c BS |
132 | if (ret) |
133 | return ret; | |
134 | } | |
135 | ||
136 | return 0; | |
137 | } | |
138 | ||
139 | static void | |
140 | nvkm_dp_train_pattern(struct lt_state *lt, u8 pattern) | |
141 | { | |
f3e70d29 | 142 | struct nvkm_dp *dp = lt->dp; |
af85389c BS |
143 | u8 sink_tp; |
144 | ||
f3e70d29 | 145 | OUTP_TRACE(&dp->outp, "training pattern %d", pattern); |
a1de2b52 | 146 | dp->outp.ior->func->dp.pattern(dp->outp.ior, pattern); |
af85389c | 147 | |
f3e70d29 | 148 | nvkm_rdaux(dp->aux, DPCD_LC02, &sink_tp, 1); |
af85389c BS |
149 | sink_tp &= ~DPCD_LC02_TRAINING_PATTERN_SET; |
150 | sink_tp |= pattern; | |
f3e70d29 | 151 | nvkm_wraux(dp->aux, DPCD_LC02, &sink_tp, 1); |
af85389c BS |
152 | } |
153 | ||
154 | static int | |
155 | nvkm_dp_train_eq(struct lt_state *lt) | |
156 | { | |
af85389c BS |
157 | bool eq_done = false, cr_done = true; |
158 | int tries = 0, i; | |
159 | ||
75eefe95 | 160 | if (lt->dp->dpcd[DPCD_RC02] & DPCD_RC02_TPS3_SUPPORTED) |
af85389c BS |
161 | nvkm_dp_train_pattern(lt, 3); |
162 | else | |
163 | nvkm_dp_train_pattern(lt, 2); | |
164 | ||
165 | do { | |
166 | if ((tries && | |
167 | nvkm_dp_train_drive(lt, lt->pc2)) || | |
168 | nvkm_dp_train_sense(lt, lt->pc2, 400)) | |
169 | break; | |
170 | ||
171 | eq_done = !!(lt->stat[2] & DPCD_LS04_INTERLANE_ALIGN_DONE); | |
75eefe95 | 172 | for (i = 0; i < lt->dp->outp.ior->dp.nr && eq_done; i++) { |
af85389c BS |
173 | u8 lane = (lt->stat[i >> 1] >> ((i & 1) * 4)) & 0xf; |
174 | if (!(lane & DPCD_LS02_LANE0_CR_DONE)) | |
175 | cr_done = false; | |
176 | if (!(lane & DPCD_LS02_LANE0_CHANNEL_EQ_DONE) || | |
177 | !(lane & DPCD_LS02_LANE0_SYMBOL_LOCKED)) | |
178 | eq_done = false; | |
179 | } | |
180 | } while (!eq_done && cr_done && ++tries <= 5); | |
181 | ||
182 | return eq_done ? 0 : -1; | |
183 | } | |
184 | ||
185 | static int | |
186 | nvkm_dp_train_cr(struct lt_state *lt) | |
187 | { | |
188 | bool cr_done = false, abort = false; | |
189 | int voltage = lt->conf[0] & DPCD_LC03_VOLTAGE_SWING_SET; | |
190 | int tries = 0, i; | |
191 | ||
192 | nvkm_dp_train_pattern(lt, 1); | |
193 | ||
194 | do { | |
195 | if (nvkm_dp_train_drive(lt, false) || | |
196 | nvkm_dp_train_sense(lt, false, 100)) | |
197 | break; | |
198 | ||
199 | cr_done = true; | |
75eefe95 | 200 | for (i = 0; i < lt->dp->outp.ior->dp.nr; i++) { |
af85389c BS |
201 | u8 lane = (lt->stat[i >> 1] >> ((i & 1) * 4)) & 0xf; |
202 | if (!(lane & DPCD_LS02_LANE0_CR_DONE)) { | |
203 | cr_done = false; | |
204 | if (lt->conf[i] & DPCD_LC03_MAX_SWING_REACHED) | |
205 | abort = true; | |
206 | break; | |
207 | } | |
208 | } | |
209 | ||
210 | if ((lt->conf[0] & DPCD_LC03_VOLTAGE_SWING_SET) != voltage) { | |
211 | voltage = lt->conf[0] & DPCD_LC03_VOLTAGE_SWING_SET; | |
212 | tries = 0; | |
213 | } | |
214 | } while (!cr_done && !abort && ++tries < 5); | |
215 | ||
216 | return cr_done ? 0 : -1; | |
217 | } | |
218 | ||
219 | static int | |
4423c743 | 220 | nvkm_dp_train_links(struct nvkm_dp *dp) |
af85389c | 221 | { |
75eefe95 | 222 | struct nvkm_ior *ior = dp->outp.ior; |
f3e70d29 | 223 | struct nvkm_disp *disp = dp->outp.disp; |
af85389c BS |
224 | struct nvkm_subdev *subdev = &disp->engine.subdev; |
225 | struct nvkm_bios *bios = subdev->device->bios; | |
4423c743 BS |
226 | struct lt_state lt = { |
227 | .dp = dp, | |
228 | }; | |
af85389c BS |
229 | u32 lnkcmp; |
230 | u8 sink[2]; | |
231 | int ret; | |
232 | ||
75eefe95 BS |
233 | OUTP_DBG(&dp->outp, "training %d x %d MB/s", |
234 | ior->dp.nr, ior->dp.bw * 27); | |
af85389c BS |
235 | |
236 | /* Intersect misc. capabilities of the OR and sink. */ | |
237 | if (disp->engine.subdev.device->chipset < 0xd0) | |
4423c743 BS |
238 | dp->dpcd[DPCD_RC02] &= ~DPCD_RC02_TPS3_SUPPORTED; |
239 | lt.pc2 = dp->dpcd[DPCD_RC02] & DPCD_RC02_TPS3_SUPPORTED; | |
af85389c BS |
240 | |
241 | /* Set desired link configuration on the source. */ | |
4423c743 | 242 | if ((lnkcmp = lt.dp->info.lnkcmp)) { |
f3e70d29 | 243 | if (dp->version < 0x30) { |
75eefe95 | 244 | while ((ior->dp.bw * 2700) < nvbios_rd16(bios, lnkcmp)) |
af85389c | 245 | lnkcmp += 4; |
32a232c5 | 246 | lnkcmp = nvbios_rd16(bios, lnkcmp + 2); |
af85389c | 247 | } else { |
75eefe95 | 248 | while (ior->dp.bw < nvbios_rd08(bios, lnkcmp)) |
af85389c | 249 | lnkcmp += 3; |
32a232c5 | 250 | lnkcmp = nvbios_rd16(bios, lnkcmp + 1); |
af85389c BS |
251 | } |
252 | ||
32a232c5 BS |
253 | nvbios_init(subdev, lnkcmp, |
254 | init.outp = &dp->outp.info; | |
255 | init.or = ior->id; | |
256 | init.link = ior->asy.link; | |
257 | ); | |
af85389c BS |
258 | } |
259 | ||
7dc0bac4 | 260 | ret = ior->func->dp.links(ior, dp->aux); |
af85389c | 261 | if (ret) { |
7dc0bac4 BS |
262 | if (ret < 0) { |
263 | OUTP_ERR(&dp->outp, "train failed with %d", ret); | |
264 | return ret; | |
265 | } | |
266 | return 0; | |
af85389c BS |
267 | } |
268 | ||
a3e81117 | 269 | ior->func->dp.power(ior, ior->dp.nr); |
af85389c BS |
270 | |
271 | /* Set desired link configuration on the sink. */ | |
75eefe95 BS |
272 | sink[0] = ior->dp.bw; |
273 | sink[1] = ior->dp.nr; | |
274 | if (ior->dp.ef) | |
af85389c BS |
275 | sink[1] |= DPCD_LC01_ENHANCED_FRAME_EN; |
276 | ||
4423c743 BS |
277 | ret = nvkm_wraux(dp->aux, DPCD_LC00_LINK_BW_SET, sink, 2); |
278 | if (ret) | |
279 | return ret; | |
280 | ||
281 | /* Attempt to train the link in this configuration. */ | |
282 | memset(lt.stat, 0x00, sizeof(lt.stat)); | |
283 | ret = nvkm_dp_train_cr(<); | |
284 | if (ret == 0) | |
285 | ret = nvkm_dp_train_eq(<); | |
286 | nvkm_dp_train_pattern(<, 0); | |
287 | return ret; | |
af85389c BS |
288 | } |
289 | ||
290 | static void | |
4423c743 | 291 | nvkm_dp_train_fini(struct nvkm_dp *dp) |
af85389c | 292 | { |
af85389c | 293 | /* Execute AfterLinkTraining script from DP Info table. */ |
32a232c5 BS |
294 | nvbios_init(&dp->outp.disp->engine.subdev, dp->info.script[1], |
295 | init.outp = &dp->outp.info; | |
296 | init.or = dp->outp.ior->id; | |
297 | init.link = dp->outp.ior->asy.link; | |
298 | ); | |
af85389c BS |
299 | } |
300 | ||
301 | static void | |
4423c743 | 302 | nvkm_dp_train_init(struct nvkm_dp *dp) |
af85389c | 303 | { |
af85389c | 304 | /* Execute EnableSpread/DisableSpread script from DP Info table. */ |
32a232c5 BS |
305 | if (dp->dpcd[DPCD_RC03] & DPCD_RC03_MAX_DOWNSPREAD) { |
306 | nvbios_init(&dp->outp.disp->engine.subdev, dp->info.script[2], | |
307 | init.outp = &dp->outp.info; | |
308 | init.or = dp->outp.ior->id; | |
309 | init.link = dp->outp.ior->asy.link; | |
310 | ); | |
311 | } else { | |
312 | nvbios_init(&dp->outp.disp->engine.subdev, dp->info.script[3], | |
313 | init.outp = &dp->outp.info; | |
314 | init.or = dp->outp.ior->id; | |
315 | init.link = dp->outp.ior->asy.link; | |
316 | ); | |
317 | } | |
af85389c | 318 | |
f3e70d29 | 319 | /* Execute BeforeLinkTraining script from DP Info table. */ |
32a232c5 BS |
320 | nvbios_init(&dp->outp.disp->engine.subdev, dp->info.script[0], |
321 | init.outp = &dp->outp.info; | |
322 | init.or = dp->outp.ior->id; | |
323 | init.link = dp->outp.ior->asy.link; | |
324 | ); | |
af85389c BS |
325 | } |
326 | ||
327 | static const struct dp_rates { | |
328 | u32 rate; | |
329 | u8 bw; | |
330 | u8 nr; | |
331 | } nvkm_dp_rates[] = { | |
332 | { 2160000, 0x14, 4 }, | |
333 | { 1080000, 0x0a, 4 }, | |
334 | { 1080000, 0x14, 2 }, | |
335 | { 648000, 0x06, 4 }, | |
336 | { 540000, 0x0a, 2 }, | |
337 | { 540000, 0x14, 1 }, | |
338 | { 324000, 0x06, 2 }, | |
339 | { 270000, 0x0a, 1 }, | |
340 | { 162000, 0x06, 1 }, | |
341 | {} | |
342 | }; | |
343 | ||
fafa8b5c | 344 | static int |
49f2b376 | 345 | nvkm_dp_train(struct nvkm_dp *dp, u32 dataKBps) |
af85389c | 346 | { |
75eefe95 BS |
347 | struct nvkm_ior *ior = dp->outp.ior; |
348 | const u8 sink_nr = dp->dpcd[DPCD_RC02] & DPCD_RC02_MAX_LANE_COUNT; | |
349 | const u8 sink_bw = dp->dpcd[DPCD_RC01_MAX_LINK_RATE]; | |
350 | const u8 outp_nr = dp->outp.info.dpconf.link_nr; | |
351 | const u8 outp_bw = dp->outp.info.dpconf.link_bw; | |
49f2b376 BS |
352 | const struct dp_rates *failsafe = NULL, *cfg; |
353 | int ret = -EINVAL; | |
af85389c | 354 | u8 pwr; |
af85389c | 355 | |
49f2b376 BS |
356 | /* Find the lowest configuration of the OR that can support |
357 | * the required link rate. | |
358 | * | |
359 | * We will refuse to program the OR to lower rates, even if | |
360 | * link training fails at higher rates (or even if the sink | |
361 | * can't support the rate at all, though the DD is supposed | |
362 | * to prevent such situations from happening). | |
363 | * | |
364 | * Attempting to do so can cause the entire display to hang, | |
365 | * and it's better to have a failed modeset than that. | |
366 | */ | |
367 | for (cfg = nvkm_dp_rates; cfg->rate; cfg++) { | |
368 | if (cfg->nr <= outp_nr && cfg->nr <= outp_bw) | |
369 | failsafe = cfg; | |
370 | if (failsafe && cfg[1].rate < dataKBps) | |
371 | break; | |
372 | } | |
373 | ||
374 | if (WARN_ON(!failsafe)) | |
375 | return ret; | |
376 | ||
af85389c | 377 | /* Ensure sink is not in a low-power state. */ |
f3e70d29 | 378 | if (!nvkm_rdaux(dp->aux, DPCD_SC00, &pwr, 1)) { |
af85389c BS |
379 | if ((pwr & DPCD_SC00_SET_POWER) != DPCD_SC00_SET_POWER_D0) { |
380 | pwr &= ~DPCD_SC00_SET_POWER; | |
381 | pwr |= DPCD_SC00_SET_POWER_D0; | |
f3e70d29 | 382 | nvkm_wraux(dp->aux, DPCD_SC00, &pwr, 1); |
af85389c BS |
383 | } |
384 | } | |
385 | ||
386 | /* Link training. */ | |
49f2b376 BS |
387 | OUTP_DBG(&dp->outp, "training (min: %d x %d MB/s)", |
388 | failsafe->nr, failsafe->bw * 27); | |
4423c743 | 389 | nvkm_dp_train_init(dp); |
49f2b376 | 390 | for (cfg = nvkm_dp_rates; ret < 0 && cfg <= failsafe; cfg++) { |
af85389c | 391 | /* Skip configurations not supported by both OR and sink. */ |
49f2b376 BS |
392 | if ((cfg->nr > outp_nr || cfg->bw > outp_bw || |
393 | cfg->nr > sink_nr || cfg->bw > sink_bw)) { | |
394 | if (cfg != failsafe) | |
395 | continue; | |
396 | OUTP_ERR(&dp->outp, "link rate unsupported by sink"); | |
397 | } | |
75eefe95 BS |
398 | ior->dp.mst = dp->lt.mst; |
399 | ior->dp.ef = dp->dpcd[DPCD_RC02] & DPCD_RC02_ENHANCED_FRAME_CAP; | |
400 | ior->dp.bw = cfg->bw; | |
401 | ior->dp.nr = cfg->nr; | |
af85389c BS |
402 | |
403 | /* Program selected link configuration. */ | |
4423c743 | 404 | ret = nvkm_dp_train_links(dp); |
af85389c | 405 | } |
4423c743 | 406 | nvkm_dp_train_fini(dp); |
af85389c | 407 | if (ret < 0) |
f3e70d29 | 408 | OUTP_ERR(&dp->outp, "training failed"); |
fafa8b5c BS |
409 | else |
410 | OUTP_DBG(&dp->outp, "training done"); | |
f3e70d29 | 411 | atomic_set(&dp->lt.done, 1); |
fafa8b5c | 412 | return ret; |
af85389c BS |
413 | } |
414 | ||
d52e948c | 415 | static void |
e04cfdc9 | 416 | nvkm_dp_disable(struct nvkm_outp *outp, struct nvkm_ior *ior) |
d52e948c BS |
417 | { |
418 | struct nvkm_dp *dp = nvkm_dp(outp); | |
419 | ||
d52e948c BS |
420 | /* Execute DisableLT script from DP Info Table. */ |
421 | nvbios_init(&ior->disp->engine.subdev, dp->info.script[4], | |
422 | init.outp = &dp->outp.info; | |
423 | init.or = ior->id; | |
424 | init.link = ior->arm.link; | |
425 | ); | |
426 | } | |
427 | ||
e04cfdc9 BS |
428 | static void |
429 | nvkm_dp_release(struct nvkm_outp *outp) | |
430 | { | |
431 | struct nvkm_dp *dp = nvkm_dp(outp); | |
432 | ||
433 | /* Prevent link from being retrained if sink sends an IRQ. */ | |
434 | atomic_set(&dp->lt.done, 0); | |
435 | dp->outp.ior->dp.nr = 0; | |
436 | } | |
437 | ||
8d7ef84d BS |
438 | static int |
439 | nvkm_dp_acquire(struct nvkm_outp *outp) | |
af85389c | 440 | { |
f3e70d29 | 441 | struct nvkm_dp *dp = nvkm_dp(outp); |
fafa8b5c | 442 | struct nvkm_ior *ior = dp->outp.ior; |
9648da5a | 443 | struct nvkm_head *head; |
af85389c | 444 | bool retrain = true; |
9648da5a | 445 | u32 datakbps = 0; |
fafa8b5c | 446 | u32 dataKBps; |
9648da5a | 447 | u32 linkKBps; |
fafa8b5c | 448 | u8 stat[3]; |
af85389c BS |
449 | int ret, i; |
450 | ||
f3e70d29 | 451 | mutex_lock(&dp->mutex); |
af85389c | 452 | |
fafa8b5c | 453 | /* Check that link configuration meets current requirements. */ |
9648da5a BS |
454 | list_for_each_entry(head, &outp->disp->head, head) { |
455 | if (ior->asy.head & (1 << head->id)) { | |
456 | u32 khz = (head->asy.hz >> ior->asy.rgdiv) / 1000; | |
457 | datakbps += khz * head->asy.or.depth; | |
458 | } | |
459 | } | |
460 | ||
fafa8b5c BS |
461 | linkKBps = ior->dp.bw * 27000 * ior->dp.nr; |
462 | dataKBps = DIV_ROUND_UP(datakbps, 8); | |
7d0a01a6 BS |
463 | OUTP_DBG(&dp->outp, "data %d KB/s link %d KB/s mst %d->%d", |
464 | dataKBps, linkKBps, ior->dp.mst, dp->lt.mst); | |
465 | if (linkKBps < dataKBps || ior->dp.mst != dp->lt.mst) { | |
fafa8b5c | 466 | OUTP_DBG(&dp->outp, "link requirements changed"); |
af85389c BS |
467 | goto done; |
468 | } | |
469 | ||
fafa8b5c | 470 | /* Check that link is still trained. */ |
f3e70d29 | 471 | ret = nvkm_rdaux(dp->aux, DPCD_LS02, stat, 3); |
af85389c | 472 | if (ret) { |
f3e70d29 | 473 | OUTP_DBG(&dp->outp, |
af85389c BS |
474 | "failed to read link status, assuming no sink"); |
475 | goto done; | |
476 | } | |
477 | ||
478 | if (stat[2] & DPCD_LS04_INTERLANE_ALIGN_DONE) { | |
fafa8b5c | 479 | for (i = 0; i < ior->dp.nr; i++) { |
af85389c BS |
480 | u8 lane = (stat[i >> 1] >> ((i & 1) * 4)) & 0x0f; |
481 | if (!(lane & DPCD_LS02_LANE0_CR_DONE) || | |
482 | !(lane & DPCD_LS02_LANE0_CHANNEL_EQ_DONE) || | |
483 | !(lane & DPCD_LS02_LANE0_SYMBOL_LOCKED)) { | |
f3e70d29 | 484 | OUTP_DBG(&dp->outp, |
af85389c BS |
485 | "lane %d not equalised", lane); |
486 | goto done; | |
487 | } | |
488 | } | |
489 | retrain = false; | |
490 | } else { | |
f3e70d29 | 491 | OUTP_DBG(&dp->outp, "no inter-lane alignment"); |
af85389c BS |
492 | } |
493 | ||
494 | done: | |
49f2b376 BS |
495 | if (retrain || !atomic_read(&dp->lt.done)) |
496 | ret = nvkm_dp_train(dp, dataKBps); | |
f3e70d29 | 497 | mutex_unlock(&dp->mutex); |
af85389c BS |
498 | return ret; |
499 | } | |
500 | ||
f6d52b21 | 501 | static bool |
f3e70d29 | 502 | nvkm_dp_enable(struct nvkm_dp *dp, bool enable) |
af85389c | 503 | { |
f3e70d29 | 504 | struct nvkm_i2c_aux *aux = dp->aux; |
af85389c BS |
505 | |
506 | if (enable) { | |
f3e70d29 BS |
507 | if (!dp->present) { |
508 | OUTP_DBG(&dp->outp, "aux power -> always"); | |
af85389c | 509 | nvkm_i2c_aux_monitor(aux, true); |
f3e70d29 | 510 | dp->present = true; |
af85389c BS |
511 | } |
512 | ||
f3e70d29 | 513 | if (!nvkm_rdaux(aux, DPCD_RC00_DPCD_REV, dp->dpcd, |
7d0a01a6 | 514 | sizeof(dp->dpcd))) |
f6d52b21 | 515 | return true; |
af85389c BS |
516 | } |
517 | ||
f3e70d29 BS |
518 | if (dp->present) { |
519 | OUTP_DBG(&dp->outp, "aux power -> demand"); | |
af85389c | 520 | nvkm_i2c_aux_monitor(aux, false); |
f3e70d29 | 521 | dp->present = false; |
af85389c BS |
522 | } |
523 | ||
f3e70d29 | 524 | atomic_set(&dp->lt.done, 0); |
f6d52b21 | 525 | return false; |
af85389c BS |
526 | } |
527 | ||
528 | static int | |
f3e70d29 | 529 | nvkm_dp_hpd(struct nvkm_notify *notify) |
af85389c BS |
530 | { |
531 | const struct nvkm_i2c_ntfy_rep *line = notify->data; | |
f3e70d29 | 532 | struct nvkm_dp *dp = container_of(notify, typeof(*dp), hpd); |
981a8162 | 533 | struct nvkm_conn *conn = dp->outp.conn; |
f3e70d29 | 534 | struct nvkm_disp *disp = dp->outp.disp; |
af85389c BS |
535 | struct nvif_notify_conn_rep_v0 rep = {}; |
536 | ||
f3e70d29 | 537 | OUTP_DBG(&dp->outp, "HPD: %d", line->mask); |
22e008f9 BS |
538 | if (line->mask & NVKM_I2C_IRQ) { |
539 | if (atomic_read(&dp->lt.done)) | |
8d7ef84d | 540 | dp->outp.func->acquire(&dp->outp); |
22e008f9 BS |
541 | rep.mask |= NVIF_NOTIFY_CONN_V0_IRQ; |
542 | } else { | |
543 | nvkm_dp_enable(dp, true); | |
544 | } | |
af85389c BS |
545 | |
546 | if (line->mask & NVKM_I2C_UNPLUG) | |
547 | rep.mask |= NVIF_NOTIFY_CONN_V0_UNPLUG; | |
548 | if (line->mask & NVKM_I2C_PLUG) | |
549 | rep.mask |= NVIF_NOTIFY_CONN_V0_PLUG; | |
550 | ||
551 | nvkm_event_send(&disp->hpd, rep.mask, conn->index, &rep, sizeof(rep)); | |
552 | return NVKM_NOTIFY_KEEP; | |
553 | } | |
554 | ||
af85389c | 555 | static void |
f3e70d29 | 556 | nvkm_dp_fini(struct nvkm_outp *outp) |
af85389c | 557 | { |
f3e70d29 BS |
558 | struct nvkm_dp *dp = nvkm_dp(outp); |
559 | nvkm_notify_put(&dp->hpd); | |
f3e70d29 | 560 | nvkm_dp_enable(dp, false); |
af85389c BS |
561 | } |
562 | ||
563 | static void | |
f3e70d29 | 564 | nvkm_dp_init(struct nvkm_outp *outp) |
af85389c | 565 | { |
f6d52b21 | 566 | struct nvkm_gpio *gpio = outp->disp->engine.subdev.device->gpio; |
f3e70d29 | 567 | struct nvkm_dp *dp = nvkm_dp(outp); |
f6d52b21 | 568 | |
f3e70d29 | 569 | nvkm_notify_put(&dp->outp.conn->hpd); |
f6d52b21 BS |
570 | |
571 | /* eDP panels need powering on by us (if the VBIOS doesn't default it | |
572 | * to on) before doing any AUX channel transactions. LVDS panel power | |
573 | * is handled by the SOR itself, and not required for LVDS DDC. | |
574 | */ | |
575 | if (dp->outp.conn->info.type == DCB_CONNECTOR_eDP) { | |
576 | int power = nvkm_gpio_get(gpio, 0, DCB_GPIO_PANEL_POWER, 0xff); | |
577 | if (power == 0) | |
578 | nvkm_gpio_set(gpio, 0, DCB_GPIO_PANEL_POWER, 0xff, 1); | |
579 | ||
580 | /* We delay here unconditionally, even if already powered, | |
581 | * because some laptop panels having a significant resume | |
582 | * delay before the panel begins responding. | |
583 | * | |
584 | * This is likely a bit of a hack, but no better idea for | |
585 | * handling this at the moment. | |
586 | */ | |
587 | msleep(300); | |
588 | ||
589 | /* If the eDP panel can't be detected, we need to restore | |
590 | * the panel power GPIO to avoid breaking another output. | |
591 | */ | |
592 | if (!nvkm_dp_enable(dp, true) && power == 0) | |
593 | nvkm_gpio_set(gpio, 0, DCB_GPIO_PANEL_POWER, 0xff, 0); | |
594 | } else { | |
595 | nvkm_dp_enable(dp, true); | |
596 | } | |
597 | ||
f3e70d29 | 598 | nvkm_notify_get(&dp->hpd); |
af85389c BS |
599 | } |
600 | ||
601 | static void * | |
f3e70d29 | 602 | nvkm_dp_dtor(struct nvkm_outp *outp) |
af85389c | 603 | { |
f3e70d29 BS |
604 | struct nvkm_dp *dp = nvkm_dp(outp); |
605 | nvkm_notify_fini(&dp->hpd); | |
f3e70d29 | 606 | return dp; |
af85389c BS |
607 | } |
608 | ||
f3e70d29 BS |
609 | static const struct nvkm_outp_func |
610 | nvkm_dp_func = { | |
611 | .dtor = nvkm_dp_dtor, | |
612 | .init = nvkm_dp_init, | |
613 | .fini = nvkm_dp_fini, | |
8d7ef84d | 614 | .acquire = nvkm_dp_acquire, |
d52e948c | 615 | .release = nvkm_dp_release, |
e04cfdc9 | 616 | .disable = nvkm_dp_disable, |
af85389c BS |
617 | }; |
618 | ||
f3e70d29 BS |
619 | static int |
620 | nvkm_dp_ctor(struct nvkm_disp *disp, int index, struct dcb_output *dcbE, | |
621 | struct nvkm_i2c_aux *aux, struct nvkm_dp *dp) | |
af85389c BS |
622 | { |
623 | struct nvkm_device *device = disp->engine.subdev.device; | |
624 | struct nvkm_bios *bios = device->bios; | |
625 | struct nvkm_i2c *i2c = device->i2c; | |
626 | u8 hdr, cnt, len; | |
627 | u32 data; | |
628 | int ret; | |
629 | ||
01a97637 BS |
630 | ret = nvkm_outp_ctor(&nvkm_dp_func, disp, index, dcbE, &dp->outp); |
631 | if (ret) | |
632 | return ret; | |
633 | ||
f3e70d29 BS |
634 | dp->aux = aux; |
635 | if (!dp->aux) { | |
636 | OUTP_ERR(&dp->outp, "no aux"); | |
3c66c87d | 637 | return -EINVAL; |
af85389c BS |
638 | } |
639 | ||
640 | /* bios data is not optional */ | |
f3e70d29 BS |
641 | data = nvbios_dpout_match(bios, dp->outp.info.hasht, |
642 | dp->outp.info.hashm, &dp->version, | |
643 | &hdr, &cnt, &len, &dp->info); | |
af85389c | 644 | if (!data) { |
f3e70d29 | 645 | OUTP_ERR(&dp->outp, "no bios dp data"); |
3c66c87d | 646 | return -EINVAL; |
af85389c BS |
647 | } |
648 | ||
f3e70d29 BS |
649 | OUTP_DBG(&dp->outp, "bios dp %02x %02x %02x %02x", |
650 | dp->version, hdr, cnt, len); | |
af85389c | 651 | |
af85389c | 652 | /* hotplug detect, replaces gpio-based mechanism with aux events */ |
f3e70d29 | 653 | ret = nvkm_notify_init(NULL, &i2c->event, nvkm_dp_hpd, true, |
af85389c | 654 | &(struct nvkm_i2c_ntfy_req) { |
22e008f9 BS |
655 | .mask = NVKM_I2C_PLUG | NVKM_I2C_UNPLUG | |
656 | NVKM_I2C_IRQ, | |
f3e70d29 | 657 | .port = dp->aux->id, |
af85389c BS |
658 | }, |
659 | sizeof(struct nvkm_i2c_ntfy_req), | |
660 | sizeof(struct nvkm_i2c_ntfy_rep), | |
f3e70d29 | 661 | &dp->hpd); |
af85389c | 662 | if (ret) { |
f3e70d29 | 663 | OUTP_ERR(&dp->outp, "error monitoring aux hpd: %d", ret); |
af85389c BS |
664 | return ret; |
665 | } | |
666 | ||
22e008f9 BS |
667 | mutex_init(&dp->mutex); |
668 | atomic_set(&dp->lt.done, 0); | |
af85389c BS |
669 | return 0; |
670 | } | |
671 | ||
672 | int | |
3c66c87d BS |
673 | nvkm_dp_new(struct nvkm_disp *disp, int index, struct dcb_output *dcbE, |
674 | struct nvkm_outp **poutp) | |
af85389c BS |
675 | { |
676 | struct nvkm_i2c *i2c = disp->engine.subdev.device->i2c; | |
f3e70d29 BS |
677 | struct nvkm_i2c_aux *aux; |
678 | struct nvkm_dp *dp; | |
679 | ||
680 | if (dcbE->location == 0) | |
681 | aux = nvkm_i2c_aux_find(i2c, NVKM_I2C_AUX_CCB(dcbE->i2c_index)); | |
682 | else | |
683 | aux = nvkm_i2c_aux_find(i2c, NVKM_I2C_AUX_EXT(dcbE->extdev)); | |
af85389c | 684 | |
f3e70d29 | 685 | if (!(dp = kzalloc(sizeof(*dp), GFP_KERNEL))) |
af85389c | 686 | return -ENOMEM; |
f3e70d29 | 687 | *poutp = &dp->outp; |
af85389c | 688 | |
f3e70d29 | 689 | return nvkm_dp_ctor(disp, index, dcbE, aux, dp); |
af85389c | 690 | } |