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