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 <drm/display/drm_dp.h> 30 31 #include <subdev/bios.h> 32 #include <subdev/bios/init.h> 33 #include <subdev/gpio.h> 34 #include <subdev/i2c.h> 35 36 #include <nvif/event.h> 37 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 44 static int 45 nvkm_dp_mst_id_put(struct nvkm_outp *outp, u32 id) 46 { 47 return 0; 48 } 49 50 static int 51 nvkm_dp_mst_id_get(struct nvkm_outp *outp, u32 *pid) 52 { 53 *pid = BIT(outp->index); 54 return 0; 55 } 56 57 static int 58 nvkm_dp_aux_xfer(struct nvkm_outp *outp, u8 type, u32 addr, u8 *data, u8 *size) 59 { 60 int ret = nvkm_i2c_aux_acquire(outp->dp.aux); 61 62 if (ret) 63 return ret; 64 65 ret = nvkm_i2c_aux_xfer(outp->dp.aux, false, type, addr, data, size); 66 nvkm_i2c_aux_release(outp->dp.aux); 67 return ret; 68 } 69 70 static int 71 nvkm_dp_aux_pwr(struct nvkm_outp *outp, bool pu) 72 { 73 outp->dp.enabled = pu; 74 nvkm_dp_enable(outp, outp->dp.enabled); 75 return 0; 76 } 77 78 struct lt_state { 79 struct nvkm_outp *outp; 80 81 int repeaters; 82 int repeater; 83 84 u8 stat[6]; 85 u8 conf[4]; 86 bool pc2; 87 u8 pc2stat; 88 u8 pc2conf[2]; 89 }; 90 91 static int 92 nvkm_dp_train_sense(struct lt_state *lt, bool pc, u32 delay) 93 { 94 struct nvkm_outp *outp = lt->outp; 95 u32 addr; 96 int ret; 97 98 usleep_range(delay, delay * 2); 99 100 if (lt->repeater) 101 addr = DPCD_LTTPR_LANE0_1_STATUS(lt->repeater); 102 else 103 addr = DPCD_LS02; 104 105 ret = nvkm_rdaux(outp->dp.aux, addr, <->stat[0], 3); 106 if (ret) 107 return ret; 108 109 if (lt->repeater) 110 addr = DPCD_LTTPR_LANE0_1_ADJUST(lt->repeater); 111 else 112 addr = DPCD_LS06; 113 114 ret = nvkm_rdaux(outp->dp.aux, addr, <->stat[4], 2); 115 if (ret) 116 return ret; 117 118 if (pc) { 119 ret = nvkm_rdaux(outp->dp.aux, DPCD_LS0C, <->pc2stat, 1); 120 if (ret) 121 lt->pc2stat = 0x00; 122 123 OUTP_TRACE(outp, "status %6ph pc2 %02x", lt->stat, lt->pc2stat); 124 } else { 125 OUTP_TRACE(outp, "status %6ph", lt->stat); 126 } 127 128 return 0; 129 } 130 131 static int 132 nvkm_dp_train_drive(struct lt_state *lt, bool pc) 133 { 134 struct nvkm_outp *outp = lt->outp; 135 struct nvkm_ior *ior = outp->ior; 136 struct nvkm_bios *bios = ior->disp->engine.subdev.device->bios; 137 struct nvbios_dpout info; 138 struct nvbios_dpcfg ocfg; 139 u8 ver, hdr, cnt, len; 140 u32 addr; 141 u32 data; 142 int ret, i; 143 144 for (i = 0; i < ior->dp.nr; i++) { 145 u8 lane = (lt->stat[4 + (i >> 1)] >> ((i & 1) * 4)) & 0xf; 146 u8 lpc2 = (lt->pc2stat >> (i * 2)) & 0x3; 147 u8 lpre = (lane & 0x0c) >> 2; 148 u8 lvsw = (lane & 0x03) >> 0; 149 u8 hivs = 3 - lpre; 150 u8 hipe = 3; 151 u8 hipc = 3; 152 153 if (lpc2 >= hipc) 154 lpc2 = hipc | DPCD_LC0F_LANE0_MAX_POST_CURSOR2_REACHED; 155 if (lpre >= hipe) { 156 lpre = hipe | DPCD_LC03_MAX_SWING_REACHED; /* yes. */ 157 lvsw = hivs = 3 - (lpre & 3); 158 } else 159 if (lvsw >= hivs) { 160 lvsw = hivs | DPCD_LC03_MAX_SWING_REACHED; 161 } 162 163 lt->conf[i] = (lpre << 3) | lvsw; 164 lt->pc2conf[i >> 1] |= lpc2 << ((i & 1) * 4); 165 166 OUTP_TRACE(outp, "config lane %d %02x %02x", i, lt->conf[i], lpc2); 167 168 if (lt->repeater != lt->repeaters) 169 continue; 170 171 data = nvbios_dpout_match(bios, outp->info.hasht, outp->info.hashm, 172 &ver, &hdr, &cnt, &len, &info); 173 if (!data) 174 continue; 175 176 data = nvbios_dpcfg_match(bios, data, lpc2 & 3, lvsw & 3, lpre & 3, 177 &ver, &hdr, &cnt, &len, &ocfg); 178 if (!data) 179 continue; 180 181 ior->func->dp->drive(ior, i, ocfg.pc, ocfg.dc, ocfg.pe, ocfg.tx_pu); 182 } 183 184 if (lt->repeater) 185 addr = DPCD_LTTPR_LANE0_SET(lt->repeater); 186 else 187 addr = DPCD_LC03(0); 188 189 ret = nvkm_wraux(outp->dp.aux, addr, lt->conf, 4); 190 if (ret) 191 return ret; 192 193 if (pc) { 194 ret = nvkm_wraux(outp->dp.aux, DPCD_LC0F, lt->pc2conf, 2); 195 if (ret) 196 return ret; 197 } 198 199 return 0; 200 } 201 202 static void 203 nvkm_dp_train_pattern(struct lt_state *lt, u8 pattern) 204 { 205 struct nvkm_outp *outp = lt->outp; 206 u32 addr; 207 u8 sink_tp; 208 209 OUTP_TRACE(outp, "training pattern %d", pattern); 210 outp->ior->func->dp->pattern(outp->ior, pattern); 211 212 if (lt->repeater) 213 addr = DPCD_LTTPR_PATTERN_SET(lt->repeater); 214 else 215 addr = DPCD_LC02; 216 217 nvkm_rdaux(outp->dp.aux, addr, &sink_tp, 1); 218 sink_tp &= ~DPCD_LC02_TRAINING_PATTERN_SET; 219 sink_tp |= (pattern != 4) ? pattern : 7; 220 221 if (pattern != 0) 222 sink_tp |= DPCD_LC02_SCRAMBLING_DISABLE; 223 else 224 sink_tp &= ~DPCD_LC02_SCRAMBLING_DISABLE; 225 nvkm_wraux(outp->dp.aux, addr, &sink_tp, 1); 226 } 227 228 static int 229 nvkm_dp_train_eq(struct lt_state *lt) 230 { 231 struct nvkm_i2c_aux *aux = lt->outp->dp.aux; 232 bool eq_done = false, cr_done = true; 233 int tries = 0, usec = 0, i; 234 u8 data; 235 236 if (lt->repeater) { 237 if (!nvkm_rdaux(aux, DPCD_LTTPR_AUX_RD_INTERVAL(lt->repeater), &data, sizeof(data))) 238 usec = (data & DPCD_RC0E_AUX_RD_INTERVAL) * 4000; 239 240 nvkm_dp_train_pattern(lt, 4); 241 } else { 242 if (lt->outp->dp.dpcd[DPCD_RC00_DPCD_REV] >= 0x14 && 243 lt->outp->dp.dpcd[DPCD_RC03] & DPCD_RC03_TPS4_SUPPORTED) 244 nvkm_dp_train_pattern(lt, 4); 245 else 246 if (lt->outp->dp.dpcd[DPCD_RC00_DPCD_REV] >= 0x12 && 247 lt->outp->dp.dpcd[DPCD_RC02] & DPCD_RC02_TPS3_SUPPORTED) 248 nvkm_dp_train_pattern(lt, 3); 249 else 250 nvkm_dp_train_pattern(lt, 2); 251 252 usec = (lt->outp->dp.dpcd[DPCD_RC0E] & DPCD_RC0E_AUX_RD_INTERVAL) * 4000; 253 } 254 255 do { 256 if ((tries && 257 nvkm_dp_train_drive(lt, lt->pc2)) || 258 nvkm_dp_train_sense(lt, lt->pc2, usec ? usec : 400)) 259 break; 260 261 eq_done = !!(lt->stat[2] & DPCD_LS04_INTERLANE_ALIGN_DONE); 262 for (i = 0; i < lt->outp->ior->dp.nr && eq_done; i++) { 263 u8 lane = (lt->stat[i >> 1] >> ((i & 1) * 4)) & 0xf; 264 if (!(lane & DPCD_LS02_LANE0_CR_DONE)) 265 cr_done = false; 266 if (!(lane & DPCD_LS02_LANE0_CHANNEL_EQ_DONE) || 267 !(lane & DPCD_LS02_LANE0_SYMBOL_LOCKED)) 268 eq_done = false; 269 } 270 } while (!eq_done && cr_done && ++tries <= 5); 271 272 return eq_done ? 0 : -1; 273 } 274 275 static int 276 nvkm_dp_train_cr(struct lt_state *lt) 277 { 278 bool cr_done = false, abort = false; 279 int voltage = lt->conf[0] & DPCD_LC03_VOLTAGE_SWING_SET; 280 int tries = 0, usec = 0, i; 281 282 nvkm_dp_train_pattern(lt, 1); 283 284 if (lt->outp->dp.dpcd[DPCD_RC00_DPCD_REV] < 0x14 && !lt->repeater) 285 usec = (lt->outp->dp.dpcd[DPCD_RC0E] & DPCD_RC0E_AUX_RD_INTERVAL) * 4000; 286 287 do { 288 if (nvkm_dp_train_drive(lt, false) || 289 nvkm_dp_train_sense(lt, false, usec ? usec : 100)) 290 break; 291 292 cr_done = true; 293 for (i = 0; i < lt->outp->ior->dp.nr; i++) { 294 u8 lane = (lt->stat[i >> 1] >> ((i & 1) * 4)) & 0xf; 295 if (!(lane & DPCD_LS02_LANE0_CR_DONE)) { 296 cr_done = false; 297 if (lt->conf[i] & DPCD_LC03_MAX_SWING_REACHED) 298 abort = true; 299 break; 300 } 301 } 302 303 if ((lt->conf[0] & DPCD_LC03_VOLTAGE_SWING_SET) != voltage) { 304 voltage = lt->conf[0] & DPCD_LC03_VOLTAGE_SWING_SET; 305 tries = 0; 306 } 307 } while (!cr_done && !abort && ++tries < 5); 308 309 return cr_done ? 0 : -1; 310 } 311 312 static int 313 nvkm_dp_train_link(struct nvkm_outp *outp, int rate) 314 { 315 struct nvkm_ior *ior = outp->ior; 316 struct lt_state lt = { 317 .outp = outp, 318 .pc2 = outp->dp.dpcd[DPCD_RC02] & DPCD_RC02_TPS3_SUPPORTED, 319 .repeaters = outp->dp.lttprs, 320 }; 321 u8 sink[2]; 322 int ret; 323 324 OUTP_DBG(outp, "training %dx%02x", ior->dp.nr, ior->dp.bw); 325 326 /* Set desired link configuration on the sink. */ 327 sink[0] = (outp->dp.rate[rate].dpcd < 0) ? ior->dp.bw : 0; 328 sink[1] = ior->dp.nr; 329 if (ior->dp.ef) 330 sink[1] |= DPCD_LC01_ENHANCED_FRAME_EN; 331 if (outp->dp.lt.post_adj) 332 sink[1] |= 0x20; 333 334 ret = nvkm_wraux(outp->dp.aux, DPCD_LC00_LINK_BW_SET, sink, 2); 335 if (ret) 336 return ret; 337 338 if (outp->dp.rate[rate].dpcd >= 0) { 339 ret = nvkm_rdaux(outp->dp.aux, DPCD_LC15_LINK_RATE_SET, &sink[0], sizeof(sink[0])); 340 if (ret) 341 return ret; 342 343 sink[0] &= ~DPCD_LC15_LINK_RATE_SET_MASK; 344 sink[0] |= outp->dp.rate[rate].dpcd; 345 346 ret = nvkm_wraux(outp->dp.aux, DPCD_LC15_LINK_RATE_SET, &sink[0], sizeof(sink[0])); 347 if (ret) 348 return ret; 349 } 350 351 /* Attempt to train the link in this configuration. */ 352 for (lt.repeater = lt.repeaters; lt.repeater >= 0; lt.repeater--) { 353 if (lt.repeater) 354 OUTP_DBG(outp, "training LTTPR%d", lt.repeater); 355 else 356 OUTP_DBG(outp, "training sink"); 357 358 memset(lt.stat, 0x00, sizeof(lt.stat)); 359 ret = nvkm_dp_train_cr(<); 360 if (ret == 0) 361 ret = nvkm_dp_train_eq(<); 362 nvkm_dp_train_pattern(<, 0); 363 } 364 365 return ret; 366 } 367 368 static int 369 nvkm_dp_train_links(struct nvkm_outp *outp, int rate) 370 { 371 struct nvkm_ior *ior = outp->ior; 372 struct nvkm_disp *disp = outp->disp; 373 struct nvkm_subdev *subdev = &disp->engine.subdev; 374 struct nvkm_bios *bios = subdev->device->bios; 375 u32 lnkcmp; 376 int ret; 377 378 OUTP_DBG(outp, "programming link for %dx%02x", ior->dp.nr, ior->dp.bw); 379 380 /* Intersect misc. capabilities of the OR and sink. */ 381 if (disp->engine.subdev.device->chipset < 0x110) 382 outp->dp.dpcd[DPCD_RC03] &= ~DPCD_RC03_TPS4_SUPPORTED; 383 if (disp->engine.subdev.device->chipset < 0xd0) 384 outp->dp.dpcd[DPCD_RC02] &= ~DPCD_RC02_TPS3_SUPPORTED; 385 386 if (AMPERE_IED_HACK(disp) && (lnkcmp = outp->dp.info.script[0])) { 387 /* Execute BeforeLinkTraining script from DP Info table. */ 388 while (ior->dp.bw < nvbios_rd08(bios, lnkcmp)) 389 lnkcmp += 3; 390 lnkcmp = nvbios_rd16(bios, lnkcmp + 1); 391 392 nvbios_init(&outp->disp->engine.subdev, lnkcmp, 393 init.outp = &outp->info; 394 init.or = ior->id; 395 init.link = ior->asy.link; 396 ); 397 } 398 399 /* Set desired link configuration on the source. */ 400 if ((lnkcmp = outp->dp.info.lnkcmp)) { 401 if (outp->dp.version < 0x30) { 402 while ((ior->dp.bw * 2700) < nvbios_rd16(bios, lnkcmp)) 403 lnkcmp += 4; 404 lnkcmp = nvbios_rd16(bios, lnkcmp + 2); 405 } else { 406 while (ior->dp.bw < nvbios_rd08(bios, lnkcmp)) 407 lnkcmp += 3; 408 lnkcmp = nvbios_rd16(bios, lnkcmp + 1); 409 } 410 411 nvbios_init(subdev, lnkcmp, 412 init.outp = &outp->info; 413 init.or = ior->id; 414 init.link = ior->asy.link; 415 ); 416 } 417 418 ret = ior->func->dp->links(ior, outp->dp.aux); 419 if (ret) { 420 if (ret < 0) { 421 OUTP_ERR(outp, "train failed with %d", ret); 422 return ret; 423 } 424 return 0; 425 } 426 427 ior->func->dp->power(ior, ior->dp.nr); 428 429 /* Attempt to train the link in this configuration. */ 430 return nvkm_dp_train_link(outp, rate); 431 } 432 433 static void 434 nvkm_dp_train_fini(struct nvkm_outp *outp) 435 { 436 /* Execute AfterLinkTraining script from DP Info table. */ 437 nvbios_init(&outp->disp->engine.subdev, outp->dp.info.script[1], 438 init.outp = &outp->info; 439 init.or = outp->ior->id; 440 init.link = outp->ior->asy.link; 441 ); 442 } 443 444 static void 445 nvkm_dp_train_init(struct nvkm_outp *outp) 446 { 447 /* Execute EnableSpread/DisableSpread script from DP Info table. */ 448 if (outp->dp.dpcd[DPCD_RC03] & DPCD_RC03_MAX_DOWNSPREAD) { 449 nvbios_init(&outp->disp->engine.subdev, outp->dp.info.script[2], 450 init.outp = &outp->info; 451 init.or = outp->ior->id; 452 init.link = outp->ior->asy.link; 453 ); 454 } else { 455 nvbios_init(&outp->disp->engine.subdev, outp->dp.info.script[3], 456 init.outp = &outp->info; 457 init.or = outp->ior->id; 458 init.link = outp->ior->asy.link; 459 ); 460 } 461 462 if (!AMPERE_IED_HACK(outp->disp)) { 463 /* Execute BeforeLinkTraining script from DP Info table. */ 464 nvbios_init(&outp->disp->engine.subdev, outp->dp.info.script[0], 465 init.outp = &outp->info; 466 init.or = outp->ior->id; 467 init.link = outp->ior->asy.link; 468 ); 469 } 470 } 471 472 static int 473 nvkm_dp_drive(struct nvkm_outp *outp, u8 lanes, u8 pe[4], u8 vs[4]) 474 { 475 struct lt_state lt = { 476 .outp = outp, 477 .stat[4] = (pe[0] << 2) | (vs[0] << 0) | 478 (pe[1] << 6) | (vs[1] << 4), 479 .stat[5] = (pe[2] << 2) | (vs[2] << 0) | 480 (pe[3] << 6) | (vs[3] << 4), 481 }; 482 483 return nvkm_dp_train_drive(<, false); 484 } 485 486 static int 487 nvkm_dp_train(struct nvkm_outp *outp, bool retrain) 488 { 489 struct nvkm_ior *ior = outp->ior; 490 int ret, rate; 491 492 for (rate = 0; rate < outp->dp.rates; rate++) { 493 if (outp->dp.rate[rate].rate == (retrain ? ior->dp.bw : outp->dp.lt.bw) * 27000) 494 break; 495 } 496 497 if (WARN_ON(rate == outp->dp.rates)) 498 return -EINVAL; 499 500 /* Retraining link? Skip source configuration, it can mess up the active modeset. */ 501 if (retrain) { 502 mutex_lock(&outp->dp.mutex); 503 ret = nvkm_dp_train_link(outp, rate); 504 mutex_unlock(&outp->dp.mutex); 505 return ret; 506 } 507 508 mutex_lock(&outp->dp.mutex); 509 OUTP_DBG(outp, "training"); 510 511 ior->dp.mst = outp->dp.lt.mst; 512 ior->dp.ef = outp->dp.dpcd[DPCD_RC02] & DPCD_RC02_ENHANCED_FRAME_CAP; 513 ior->dp.bw = outp->dp.lt.bw; 514 ior->dp.nr = outp->dp.lt.nr; 515 516 nvkm_dp_train_init(outp); 517 ret = nvkm_dp_train_links(outp, rate); 518 nvkm_dp_train_fini(outp); 519 if (ret < 0) 520 OUTP_ERR(outp, "training failed"); 521 else 522 OUTP_DBG(outp, "training done"); 523 524 mutex_unlock(&outp->dp.mutex); 525 return ret; 526 } 527 528 void 529 nvkm_dp_disable(struct nvkm_outp *outp, struct nvkm_ior *ior) 530 { 531 /* Execute DisableLT script from DP Info Table. */ 532 nvbios_init(&ior->disp->engine.subdev, outp->dp.info.script[4], 533 init.outp = &outp->info; 534 init.or = ior->id; 535 init.link = ior->arm.link; 536 ); 537 } 538 539 static void 540 nvkm_dp_release(struct nvkm_outp *outp) 541 { 542 outp->ior->dp.nr = 0; 543 nvkm_dp_disable(outp, outp->ior); 544 545 nvkm_outp_release(outp); 546 } 547 548 void 549 nvkm_dp_enable(struct nvkm_outp *outp, bool auxpwr) 550 { 551 struct nvkm_gpio *gpio = outp->disp->engine.subdev.device->gpio; 552 struct nvkm_i2c_aux *aux = outp->dp.aux; 553 554 if (auxpwr && !outp->dp.aux_pwr) { 555 /* eDP panels need powering on by us (if the VBIOS doesn't default it 556 * to on) before doing any AUX channel transactions. LVDS panel power 557 * is handled by the SOR itself, and not required for LVDS DDC. 558 */ 559 if (outp->conn->info.type == DCB_CONNECTOR_eDP) { 560 int power = nvkm_gpio_get(gpio, 0, DCB_GPIO_PANEL_POWER, 0xff); 561 if (power == 0) { 562 nvkm_gpio_set(gpio, 0, DCB_GPIO_PANEL_POWER, 0xff, 1); 563 outp->dp.aux_pwr_pu = true; 564 } 565 566 /* We delay here unconditionally, even if already powered, 567 * because some laptop panels having a significant resume 568 * delay before the panel begins responding. 569 * 570 * This is likely a bit of a hack, but no better idea for 571 * handling this at the moment. 572 */ 573 msleep(300); 574 } 575 576 OUTP_DBG(outp, "aux power -> always"); 577 nvkm_i2c_aux_monitor(aux, true); 578 outp->dp.aux_pwr = true; 579 } else 580 if (!auxpwr && outp->dp.aux_pwr) { 581 OUTP_DBG(outp, "aux power -> demand"); 582 nvkm_i2c_aux_monitor(aux, false); 583 outp->dp.aux_pwr = false; 584 585 /* Restore eDP panel GPIO to its prior state if we changed it, as 586 * it could potentially interfere with other outputs. 587 */ 588 if (outp->conn->info.type == DCB_CONNECTOR_eDP) { 589 if (outp->dp.aux_pwr_pu) { 590 nvkm_gpio_set(gpio, 0, DCB_GPIO_PANEL_POWER, 0xff, 0); 591 outp->dp.aux_pwr_pu = false; 592 } 593 } 594 } 595 } 596 597 static void 598 nvkm_dp_fini(struct nvkm_outp *outp) 599 { 600 nvkm_dp_enable(outp, false); 601 } 602 603 static void 604 nvkm_dp_init(struct nvkm_outp *outp) 605 { 606 nvkm_outp_init(outp); 607 nvkm_dp_enable(outp, outp->dp.enabled); 608 } 609 610 static void * 611 nvkm_dp_dtor(struct nvkm_outp *outp) 612 { 613 return outp; 614 } 615 616 static const struct nvkm_outp_func 617 nvkm_dp_func = { 618 .dtor = nvkm_dp_dtor, 619 .init = nvkm_dp_init, 620 .fini = nvkm_dp_fini, 621 .detect = nvkm_outp_detect, 622 .inherit = nvkm_outp_inherit, 623 .acquire = nvkm_outp_acquire, 624 .release = nvkm_dp_release, 625 .bl.get = nvkm_outp_bl_get, 626 .bl.set = nvkm_outp_bl_set, 627 .dp.aux_pwr = nvkm_dp_aux_pwr, 628 .dp.aux_xfer = nvkm_dp_aux_xfer, 629 .dp.train = nvkm_dp_train, 630 .dp.drive = nvkm_dp_drive, 631 .dp.mst_id_get = nvkm_dp_mst_id_get, 632 .dp.mst_id_put = nvkm_dp_mst_id_put, 633 }; 634 635 int 636 nvkm_dp_new(struct nvkm_disp *disp, int index, struct dcb_output *dcbE, struct nvkm_outp **poutp) 637 { 638 struct nvkm_device *device = disp->engine.subdev.device; 639 struct nvkm_bios *bios = device->bios; 640 struct nvkm_i2c *i2c = device->i2c; 641 struct nvkm_outp *outp; 642 u8 ver, hdr, cnt, len; 643 u32 data; 644 int ret; 645 646 ret = nvkm_outp_new_(&nvkm_dp_func, disp, index, dcbE, poutp); 647 outp = *poutp; 648 if (ret) 649 return ret; 650 651 if (dcbE->location == 0) 652 outp->dp.aux = nvkm_i2c_aux_find(i2c, NVKM_I2C_AUX_CCB(dcbE->i2c_index)); 653 else 654 outp->dp.aux = nvkm_i2c_aux_find(i2c, NVKM_I2C_AUX_EXT(dcbE->extdev)); 655 if (!outp->dp.aux) { 656 OUTP_ERR(outp, "no aux"); 657 return -EINVAL; 658 } 659 660 /* bios data is not optional */ 661 data = nvbios_dpout_match(bios, outp->info.hasht, outp->info.hashm, 662 &outp->dp.version, &hdr, &cnt, &len, &outp->dp.info); 663 if (!data) { 664 OUTP_ERR(outp, "no bios dp data"); 665 return -EINVAL; 666 } 667 668 OUTP_DBG(outp, "bios dp %02x %02x %02x %02x", outp->dp.version, hdr, cnt, len); 669 670 data = nvbios_dp_table(bios, &ver, &hdr, &cnt, &len); 671 outp->dp.mst = data && ver >= 0x40 && (nvbios_rd08(bios, data + 0x08) & 0x04); 672 673 mutex_init(&outp->dp.mutex); 674 return 0; 675 } 676