1 /* 2 * SPDX-FileCopyrightText: Copyright (c) 2011 NVIDIA CORPORATION & AFFILIATES. All rights reserved. 3 * SPDX-License-Identifier: MIT 4 * 5 * Permission is hereby granted, free of charge, to any person obtaining a 6 * copy of this software and associated documentation files (the "Software"), 7 * to deal in the Software without restriction, including without limitation 8 * the rights to use, copy, modify, merge, publish, distribute, sublicense, 9 * and/or sell copies of the Software, and to permit persons to whom the 10 * Software is furnished to do so, subject to the following conditions: 11 * 12 * The above copyright notice and this permission notice shall be included in 13 * all copies or substantial portions of the Software. 14 * 15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 18 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 20 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 21 * DEALINGS IN THE SOFTWARE. 22 */ 23 24 #include "dp/nvdp-connector.h" 25 #include "nvdp-timer.hpp" 26 #include "nvdp-connector-event-sink.hpp" 27 #include "dp/nvdp-connector-event-sink.h" 28 #include "dp/nvdp-timer.h" 29 30 #include "nvkms-evo.h" 31 #include "nvkms-types.h" 32 #include "nvkms-modeset.h" 33 #include "nvkms-utils.h" 34 #include "nvkms-rmapi.h" 35 36 #include <dp_connector.h> 37 38 // Loop over all display devices attached to a connector. 39 // Connector::enumDevices(NULL) returns the first device, and then 40 // enumDevices(previous) returns each subsequent device. 41 #define for_each_device(connector, dev) \ 42 for (DisplayPort::Device *(dev) = NULL; ((dev) = (connector)->enumDevices(dev)); ) 43 44 NVDPLibConnectorPtr nvDPCreateConnector(NVConnectorEvoPtr pConnectorEvo) 45 { 46 NVDevEvoPtr pDevEvo = pConnectorEvo->pDispEvo->pDevEvo; 47 DisplayPort::Timer *pTimer = &pDevEvo->dpTimer->timer; 48 NVDPLibConnectorPtr pNVDpLibConnector = 49 (NVDPLibConnectorPtr) nvCalloc(1, sizeof(*pNVDpLibConnector)); 50 51 if (!pNVDpLibConnector) { 52 return NULL; 53 } 54 55 pNVDpLibConnector->pConnectorEvo = pConnectorEvo; 56 57 // Create the EVO interface object. 58 pNVDpLibConnector->evoInterface = 59 new nvkmsDisplayPort::EvoInterface(pConnectorEvo); 60 if (!pNVDpLibConnector->evoInterface) { 61 goto fail; 62 } 63 64 // Create the event sink object. 65 pNVDpLibConnector->evtSink = 66 new nvkmsDisplayPort::ConnectorEventSink(pConnectorEvo); 67 if (!pNVDpLibConnector->evtSink) { 68 goto fail; 69 } 70 71 // Create the MainLink object. 72 pNVDpLibConnector->mainLink = 73 DisplayPort::MakeEvoMainLink(pNVDpLibConnector->evoInterface, pTimer); 74 if (!pNVDpLibConnector->mainLink) { 75 goto fail; 76 } 77 78 // Create the AuxBus object. 79 pNVDpLibConnector->auxBus = 80 DisplayPort::MakeEvoAuxBus(pNVDpLibConnector->evoInterface, pTimer); 81 if (!pNVDpLibConnector->auxBus) { 82 goto fail; 83 } 84 85 pNVDpLibConnector->connector = 86 DisplayPort::createConnector(pNVDpLibConnector->mainLink, 87 pNVDpLibConnector->auxBus, 88 pTimer, 89 pNVDpLibConnector->evtSink); 90 if (!pNVDpLibConnector->connector) { 91 goto fail; 92 } 93 94 pNVDpLibConnector->connector->setPolicyAssessLinkSafely(TRUE); 95 96 return pNVDpLibConnector; 97 98 fail: 99 nvDPDestroyConnector(pNVDpLibConnector); 100 return NULL; 101 } 102 103 void nvDPNotifyLongPulse(NVConnectorEvoPtr pConnectorEvo, 104 NvBool connected) 105 { 106 NVDPLibConnectorPtr pNVDpLibConnector = pConnectorEvo->pDpLibConnector; 107 DisplayPort::Connector *c = pNVDpLibConnector->connector; 108 109 pNVDpLibConnector->plugged = connected; 110 111 if (connected && !nvAssignSOREvo(pConnectorEvo->pDispEvo, 112 nvDpyIdToNvU32(pConnectorEvo->displayId), 113 FALSE /* b2Heads1Or */, 114 0 /* sorExcludeMask */)) { 115 // DPLib takes care of skipping LT on unassigned SOR Display. 116 } 117 118 c->notifyLongPulse(connected); 119 120 } 121 122 void nvDPNotifyShortPulse(NVDPLibConnectorPtr pNVDpLibConnector) 123 { 124 DisplayPort::Connector *c = pNVDpLibConnector->connector; 125 126 c->notifyShortPulse(); 127 } 128 129 void nvDPDestroyConnector(NVDPLibConnectorPtr pNVDpLibConnector) 130 { 131 if (!pNVDpLibConnector) return; 132 133 if (pNVDpLibConnector->connector) { 134 pNVDpLibConnector->connector->destroy(); 135 } 136 if (pNVDpLibConnector->auxBus) { 137 delete pNVDpLibConnector->auxBus; 138 } 139 if (pNVDpLibConnector->mainLink) { 140 delete pNVDpLibConnector->mainLink; 141 } 142 if (pNVDpLibConnector->evoInterface) { 143 delete pNVDpLibConnector->evoInterface; 144 } 145 if (pNVDpLibConnector->evtSink) { 146 delete pNVDpLibConnector->evtSink; 147 } 148 149 nvFree(pNVDpLibConnector); 150 } 151 152 NvBool nvDPIsLinkAwaitingTransition(NVConnectorEvoPtr pConnectorEvo) 153 { 154 if (nvConnectorUsesDPLib(pConnectorEvo)) { 155 DisplayPort::Connector *c = pConnectorEvo->pDpLibConnector->connector; 156 return c->isLinkAwaitingTransition(); 157 } 158 159 return FALSE; 160 } 161 162 /* 163 * Start DisplayPort mode validation on all connectors on a disp. 164 */ 165 void nvDPBeginValidation(NVDispEvoPtr pDispEvo) 166 { 167 NVConnectorEvoPtr pConnectorEvo; 168 169 FOR_ALL_EVO_CONNECTORS(pConnectorEvo, pDispEvo) { 170 if (nvConnectorUsesDPLib(pConnectorEvo)) { 171 pConnectorEvo->pDpLibConnector->connector->beginCompoundQuery(); 172 } 173 } 174 } 175 176 /*! 177 * Create a new DisplayPort group and populate it with the devices specified by 178 * dpyIdList. For MST groups, this allocates a dynamic RM display ID. 179 * Otherwise, it uses the connector's display ID. 180 */ 181 static DisplayPort::Group* CreateGroup( 182 const NVDPLibConnectorRec *pDpLibConnector, 183 const NVDpyIdList dpyIdList) 184 { 185 NVDpyEvoPtr pDpyEvo; 186 DisplayPort::Group *pGroup = NULL; 187 188 pGroup = pDpLibConnector->connector->newGroup(); 189 if (pGroup == NULL) { 190 return NULL; 191 } 192 193 // Populate the group 194 FOR_ALL_EVO_DPYS(pDpyEvo, 195 dpyIdList, pDpLibConnector->pConnectorEvo->pDispEvo) { 196 if (pDpyEvo->dp.pDpLibDevice) { 197 pGroup->insert(pDpyEvo->dp.pDpLibDevice->device); 198 } 199 } 200 201 return pGroup; 202 } 203 204 static NvU32 GetColorDepth( 205 const enum NvKmsDpyAttributeCurrentColorSpaceValue colorSpace, 206 const enum NvKmsDpyAttributeColorBpcValue colorBpc) 207 { 208 switch (colorSpace) { 209 case NV_KMS_DPY_ATTRIBUTE_CURRENT_COLOR_SPACE_YCbCr420: 210 /* 211 * In YUV420, HW is programmed with RGB color space and full color 212 * range. The color space conversion and color range compression 213 * happen in a headSurface composite shader. 214 */ 215 case NV_KMS_DPY_ATTRIBUTE_CURRENT_COLOR_SPACE_YCbCr444: 216 case NV_KMS_DPY_ATTRIBUTE_CURRENT_COLOR_SPACE_RGB: 217 /* 218 * For RGB/YCbCr444, each pixel is always 3 components. For 219 * YCbCr/YUV420, we currently always scan out from the headSurface 220 * as RGB. 221 */ 222 return colorBpc * 3; 223 case NV_KMS_DPY_ATTRIBUTE_CURRENT_COLOR_SPACE_YCbCr422: 224 return colorBpc * 2; 225 } 226 227 return 0; 228 } 229 230 static void SetDPMSATiming(const NVDispEvoRec *pDispEvo, 231 const NvU32 displayId, 232 NV0073_CTRL_CMD_DP_SET_MSA_PROPERTIES_PARAMS *msaParams, 233 const NVHwModeTimingsEvo *pTimings) 234 { 235 NV0073_CTRL_DP_MSA_PROPERTIES_MASK *featureMask = &msaParams->featureMask; 236 NV0073_CTRL_DP_MSA_PROPERTIES_VALUES *featureValues = 237 &msaParams->featureValues; 238 239 nvkms_memset(msaParams, 0, sizeof(*msaParams)); 240 241 /* 242 * Fill in displayId and subDeviceInstance unconditionally. 243 * From CL#27980662, dplib started passing the client provided displayId 244 * to RM for setting MSA properties. 245 * Default value of displayId is 0, leading to RMControl failure in 246 * the displayport library. 247 */ 248 msaParams->subDeviceInstance = pDispEvo->displayOwner; 249 msaParams->displayId = displayId; 250 251 if ((displayId == 0x0) || 252 ((pTimings->yuv420Mode != NV_YUV420_MODE_SW) && 253 !nvIsAdaptiveSyncDpyVrrType(pTimings->vrr.type))) { 254 return; 255 } 256 257 msaParams->bEnableMSA = 1; 258 msaParams->bCacheMsaOverrideForNextModeset = 1; 259 260 if (pTimings->yuv420Mode == NV_YUV420_MODE_SW) { 261 featureMask->bRasterTotalHorizontal = true; 262 featureMask->bActiveStartHorizontal = true; 263 featureMask->bSurfaceTotalHorizontal = true; 264 featureMask->bSyncWidthHorizontal = true; 265 featureValues->rasterTotalHorizontal = 2 * pTimings->rasterSize.x; 266 featureValues->activeStartHorizontal = 2 * (pTimings->rasterBlankEnd.x + 1); 267 featureValues->surfaceTotalHorizontal = 2 * nvEvoVisibleWidth(pTimings); 268 featureValues->syncWidthHorizontal = 2 * (pTimings->rasterSyncEnd.x + 1); 269 } 270 271 /* 272 * In case of Adaptive-Sync VRR, override VTotal field of MSA (Main Stream 273 * Attributes) to workaround bug 4164132. 274 */ 275 if (nvIsAdaptiveSyncDpyVrrType(pTimings->vrr.type)) { 276 featureMask->bRasterTotalVertical = true; 277 featureValues->rasterTotalVertical = pTimings->rasterSize.y; 278 } 279 } 280 281 static void InitDpModesetParams( 282 const NVDispEvoRec *pDispEvo, 283 const NvU32 head, 284 const NvU32 displayId, 285 const NVHwModeTimingsEvo *pTimings, 286 const enum NvKmsDpyAttributeCurrentColorSpaceValue colorSpace, 287 const enum NvKmsDpyAttributeColorBpcValue colorBpc, 288 DisplayPort::DpModesetParams *pParams) 289 { 290 pParams->modesetInfo.pixelClockHz = pTimings->pixelClock * 1000; 291 pParams->modesetInfo.rasterWidth = pTimings->rasterSize.x; 292 pParams->modesetInfo.rasterHeight = pTimings->rasterSize.y; 293 pParams->modesetInfo.rasterBlankStartX = pTimings->rasterBlankStart.x; 294 pParams->modesetInfo.rasterBlankEndX = pTimings->rasterBlankEnd.x; 295 pParams->modesetInfo.surfaceWidth = nvEvoVisibleWidth(pTimings); 296 pParams->modesetInfo.surfaceHeight = nvEvoVisibleHeight(pTimings); 297 298 pParams->modesetInfo.depth = 299 GetColorDepth(colorSpace, colorBpc); 300 pParams->modesetInfo.bitsPerComponent = colorBpc; 301 302 pParams->colorFormat = dpColorFormat_Unknown; 303 switch (colorSpace) { 304 case NV_KMS_DPY_ATTRIBUTE_CURRENT_COLOR_SPACE_YCbCr420: 305 /* HW YUV420 mode is only supported for HDMI, not DP */ 306 nvAssert(pTimings->yuv420Mode == NV_YUV420_MODE_SW); 307 pParams->modesetInfo.pixelClockHz *= 2; 308 pParams->colorFormat = dpColorFormat_YCbCr420; 309 break; 310 case NV_KMS_DPY_ATTRIBUTE_CURRENT_COLOR_SPACE_YCbCr444: 311 pParams->colorFormat = dpColorFormat_YCbCr444; 312 break; 313 case NV_KMS_DPY_ATTRIBUTE_CURRENT_COLOR_SPACE_YCbCr422: 314 pParams->colorFormat = dpColorFormat_YCbCr422; 315 break; 316 case NV_KMS_DPY_ATTRIBUTE_CURRENT_COLOR_SPACE_RGB: 317 pParams->colorFormat = dpColorFormat_RGB; 318 break; 319 } 320 321 pParams->headIndex = head; 322 323 SetDPMSATiming(pDispEvo, displayId, &pParams->msaparams, pTimings); 324 } 325 326 NVDPLibModesetStatePtr nvDPLibCreateModesetState( 327 const NVDispEvoRec *pDispEvo, 328 const NvU32 head, 329 const NvU32 displayId, 330 const NVDpyIdList dpyIdList, 331 const enum NvKmsDpyAttributeCurrentColorSpaceValue colorSpace, 332 const enum NvKmsDpyAttributeColorBpcValue colorBpc, 333 const NVHwModeTimingsEvo *pTimings, 334 const NVDscInfoEvoRec *pDscInfo) 335 { 336 bool found = false; 337 const NVDPLibConnectorRec *pDpLibConnector = NULL; 338 const NVDpyEvoRec *pDpyEvo; 339 NVDPLibModesetStatePtr pDpLibModesetState = NULL; 340 341 FOR_ALL_EVO_DPYS(pDpyEvo, dpyIdList, pDispEvo) { 342 if (!found) { 343 pDpLibConnector = pDpyEvo->pConnectorEvo->pDpLibConnector; 344 found = true; 345 } else if (pDpLibConnector != pDpyEvo->pConnectorEvo->pDpLibConnector) { 346 /* All Dpys must belongs to same DP connector */ 347 return NULL; 348 } 349 } 350 351 /* Do nothing if any of the display is not DP */ 352 if (pDpLibConnector == NULL) { 353 return NULL; 354 } 355 356 pDpLibModesetState = 357 (NVDPLibModesetStatePtr) nvCalloc(1, sizeof(*pDpLibModesetState)); 358 if (pDpLibModesetState == NULL) { 359 return NULL; 360 } 361 362 InitDpModesetParams(pDispEvo, 363 head, 364 displayId, 365 pTimings, 366 colorSpace, 367 colorBpc, 368 &pDpLibModesetState->modesetParams); 369 if (pDscInfo->type == NV_DSC_INFO_EVO_TYPE_DP) { 370 pDpLibModesetState->modesetParams.modesetInfo.bEnableDsc = true; 371 372 /* 373 * If DSC is enabled then override normal pixel depth with 374 * target bpp rate of DSC encoder, the rate at which it is going to 375 * output compressed stream. 376 */ 377 pDpLibModesetState->modesetParams.modesetInfo.depth = 378 pDscInfo->dp.bitsPerPixelX16; 379 380 switch (pDscInfo->dp.dscMode) { 381 case NV_DSC_EVO_MODE_SINGLE: 382 pDpLibModesetState->modesetParams.modesetInfo.mode = 383 DSC_SINGLE; 384 break; 385 case NV_DSC_EVO_MODE_DUAL: 386 pDpLibModesetState->modesetParams.modesetInfo.mode = 387 DSC_DUAL; 388 break; 389 } 390 } else { 391 nvAssert(pDscInfo->type == NV_DSC_INFO_EVO_TYPE_DISABLED); 392 } 393 394 pDpLibModesetState->dpyIdList = dpyIdList; 395 396 return pDpLibModesetState; 397 } 398 399 void nvDPLibFreeModesetState(NVDPLibModesetStatePtr pDpLibModesetState) 400 { 401 nvFree(pDpLibModesetState); 402 } 403 404 /* 405 * Validate the mode for a given NVHwModeTimingsEvo + dpyIdList. This 406 * function should be called for each head, and must be called between 407 * nvDPBeginValidation and nvDPEndValidation. 408 * 409 * If validation fails, this function returns FALSE. You must still call 410 * nvDPEndValidation even if an individual head fails. 411 * 412 * If validation succeeds, the DSC fields within pTimings are updated with what 413 * is returned by compoundQueryAttach(). 414 */ 415 NvBool nvDPLibValidateTimings( 416 const NVDispEvoRec *pDispEvo, 417 const NvU32 head, 418 const NvU32 displayId, 419 const NVDpyIdList dpyIdList, 420 const enum NvKmsDpyAttributeCurrentColorSpaceValue colorSpace, 421 const enum NvKmsDpyAttributeColorBpcValue colorBpc, 422 const struct NvKmsModeValidationParams *pModeValidationParams, 423 const NVHwModeTimingsEvo *pTimings, 424 const NvBool b2Heads1Or, 425 NVDscInfoEvoRec *pDscInfo) 426 { 427 const NVDpyEvoRec *pDpyEvo; 428 const NVDPLibConnectorRec *pDpLibConnector = NULL; 429 bool found = false; 430 431 DisplayPort::Group *pGroup = NULL; 432 DisplayPort::DscOutParams *pDscOutParams = NULL; 433 DisplayPort::DpModesetParams *pModesetParams = NULL; 434 DisplayPort::DscParams dpDscParams; 435 NvBool ret = FALSE; 436 437 FOR_ALL_EVO_DPYS(pDpyEvo, dpyIdList, pDispEvo) { 438 if (!found) { 439 pDpLibConnector = pDpyEvo->pConnectorEvo->pDpLibConnector; 440 found = true; 441 } else if (pDpLibConnector != pDpyEvo->pConnectorEvo->pDpLibConnector) { 442 /* All Dpys must belongs to same DP connector */ 443 return FALSE; 444 } 445 } 446 447 /* Do nothing if any of the display is not DP */ 448 if (pDpLibConnector == NULL) { 449 return TRUE; 450 } 451 452 pGroup = CreateGroup(pDpLibConnector, dpyIdList); 453 if (pGroup == NULL) { 454 nvEvoLogDisp(pDispEvo, EVO_LOG_ERROR, 455 "Failed to create a DisplayPort group"); 456 goto done; 457 } 458 459 pDscOutParams = 460 (DisplayPort::DscOutParams*) nvCalloc(1, sizeof(*pDscOutParams)); 461 if (pDscOutParams == NULL) { 462 goto done; 463 } 464 465 466 pModesetParams = 467 (DisplayPort::DpModesetParams*) nvCalloc(1, sizeof(*pModesetParams)); 468 if (pModesetParams == NULL) { 469 goto done; 470 } 471 472 InitDpModesetParams(pDispEvo, 473 head, 474 displayId, 475 pTimings, 476 colorSpace, 477 colorBpc, 478 pModesetParams); 479 480 if (b2Heads1Or) { 481 pModesetParams->modesetInfo.mode = DSC_DUAL; 482 } 483 484 dpDscParams.bCheckWithDsc = true; 485 dpDscParams.forceDsc = pModeValidationParams->forceDsc ? 486 DisplayPort::DSC_FORCE_ENABLE : 487 DisplayPort::DSC_DEFAULT; 488 /* 489 * 2Heads1Or requires either YUV420 or DSC; if b2Heads1Or is enabled 490 * but YUV420 is not, force DSC. 491 */ 492 if (b2Heads1Or && (pTimings->yuv420Mode != NV_YUV420_MODE_HW)) { 493 dpDscParams.forceDsc = DisplayPort::DSC_FORCE_ENABLE; 494 } 495 dpDscParams.bitsPerPixelX16 = 496 pModeValidationParams->dscOverrideBitsPerPixelX16; 497 dpDscParams.pDscOutParams = pDscOutParams; 498 499 ret = pDpLibConnector->connector->compoundQueryAttach( 500 pGroup, *pModesetParams, 501 &dpDscParams); 502 503 if (ret && b2Heads1Or) { 504 /* 505 * 2Heads1OR requires either YUV420 or DSC; 506 * dpDscParams.bEnableDsc is assigned by compoundQueryAttach(). 507 */ 508 nvAssert(dpDscParams.bEnableDsc || 509 (pTimings->yuv420Mode == NV_YUV420_MODE_HW)); 510 } 511 512 if (ret && (pDscInfo != NULL)) { 513 nvkms_memset(pDscInfo, 0, sizeof(*pDscInfo)); 514 515 if (dpDscParams.bEnableDsc) { 516 pDscInfo->type = NV_DSC_INFO_EVO_TYPE_DP; 517 518 pDscInfo->dp.dscMode = b2Heads1Or ? 519 NV_DSC_EVO_MODE_DUAL : NV_DSC_EVO_MODE_SINGLE; 520 pDscInfo->dp.bitsPerPixelX16 = dpDscParams.bitsPerPixelX16; 521 ct_assert(sizeof(pDscInfo->dp.pps) == sizeof(pDscOutParams->PPS)); 522 nvkms_memcpy(pDscInfo->dp.pps, pDscOutParams->PPS, 523 sizeof(pDscInfo->dp.pps)); 524 } else { 525 pDscInfo->type = NV_DSC_INFO_EVO_TYPE_DISABLED; 526 } 527 } 528 529 done: 530 nvFree(pDscOutParams); 531 nvFree(pModesetParams); 532 if (pGroup != NULL) { 533 pGroup->destroy(); 534 } 535 return ret; 536 } 537 538 /* 539 * Finishes DisplayPort mode validation. Returns TRUE if the complete 540 * configuration is possible, and FALSE if it can't be achieved. 541 */ 542 NvBool nvDPEndValidation(NVDispEvoPtr pDispEvo) 543 { 544 NvBool ret = TRUE; 545 NVConnectorEvoPtr pConnectorEvo; 546 547 FOR_ALL_EVO_CONNECTORS(pConnectorEvo, pDispEvo) { 548 if (nvConnectorUsesDPLib(pConnectorEvo)) { 549 DisplayPort::Connector *connector = 550 pConnectorEvo->pDpLibConnector->connector; 551 552 /* endCompoundQuery() must be called for all dp connectors */ 553 ret = connector->endCompoundQuery() && ret; 554 } 555 } 556 557 return ret; 558 } 559 560 NvBool nvDPValidateModeForDpyEvo( 561 const NVDpyEvoRec *pDpyEvo, 562 const enum NvKmsDpyAttributeCurrentColorSpaceValue colorSpace, 563 const enum NvKmsDpyAttributeColorBpcValue colorBpc, 564 const struct NvKmsModeValidationParams *pModeValidationParams, 565 const NVHwModeTimingsEvo *pTimings, 566 const NvBool b2Heads1Or, 567 NVDscInfoEvoRec *pDscInfo) 568 { 569 const NVConnectorEvoRec *pConnectorEvo = pDpyEvo->pConnectorEvo; 570 571 nvAssert(nvConnectorUsesDPLib(pConnectorEvo)); 572 573 DisplayPort::Connector *connector = 574 pConnectorEvo->pDpLibConnector->connector; 575 576 connector->beginCompoundQuery(); 577 NvBool ret = nvDPLibValidateTimings(pDpyEvo->pDispEvo, 578 0 /* head */, 579 0 /* displayId */, 580 nvAddDpyIdToEmptyDpyIdList(pDpyEvo->id), 581 colorSpace, 582 colorBpc, 583 pModeValidationParams, 584 pTimings, 585 b2Heads1Or, 586 pDscInfo); 587 connector->endCompoundQuery(); 588 589 return ret; 590 } 591 592 /* 593 * Notify the DisplayPort library that a given mode is about to be set on a 594 * given head. The configuration for this head must have previously been 595 * validated by a call to nvDPLibValidateTimings. 596 */ 597 static 598 void NotifyAttachBegin(NVDPLibConnectorPtr pDpLibConnector, 599 const NvU32 head, 600 const NVDPLibModesetStateRec *pDpLibModesetState) 601 { 602 const NVConnectorEvoRec *pConnectorEvo = pDpLibConnector->pConnectorEvo; 603 const NVDispEvoRec *pDispEvo = pConnectorEvo->pDispEvo; 604 const DisplayPort::DpModesetParams *pParams = 605 &pDpLibModesetState->modesetParams; 606 const NVDpyEvoRec *pDpyEvo = NULL; 607 608 /* Insert active dpys into group */ 609 pDpLibConnector->dpyIdList[head] = pDpLibModesetState->dpyIdList; 610 FOR_ALL_EVO_DPYS(pDpyEvo, pDpLibConnector->dpyIdList[head], pDispEvo) { 611 if (pDpyEvo->dp.pDpLibDevice) { 612 pDpLibConnector->pGroup[head]->insert( 613 pDpyEvo->dp.pDpLibDevice->device); 614 } 615 } 616 617 pDpLibConnector->connector->notifyAttachBegin( 618 pDpLibConnector->pGroup[head], 619 *pParams); 620 } 621 622 /* 623 * Notify the DisplayPort library that a modeset on a head begun by 624 * nvDPNotifyAttachBegin is finished. 625 */ 626 static void NotifyAttachEnd(NVDPLibConnectorPtr pDpLibConnector, NvU32 head) 627 { 628 pDpLibConnector->connector->notifyAttachEnd(false); 629 pDpLibConnector->headMask |= NVBIT(head); 630 } 631 632 /* 633 * Notify the DisplayPort library that the given head driving displays on this 634 * connector is about to be shut down. 635 */ 636 static void NotifyDetachBegin(NVDPLibConnectorPtr pDpLibConnector, const NvU32 head) 637 { 638 /* 639 * The firmware group is the VBIOS monitor group the DP Library manages 640 * internally. In notifyDetachBegin(NULL), the NULL defaults to firmware 641 * group. 642 */ 643 pDpLibConnector->connector->notifyDetachBegin( 644 pDpLibConnector->headInFirmware ? 645 NULL : pDpLibConnector->pGroup[head]); 646 } 647 648 /* 649 * Notify the DisplayPort library that the driver has finished shutting down a 650 * head that was previously driving this connector. 651 */ 652 static void NotifyDetachEnd(NVDPLibConnectorPtr pDpLibConnector, const NvU32 head) 653 { 654 pDpLibConnector->connector->notifyDetachEnd(); 655 656 if (!pDpLibConnector->headInFirmware) { 657 const NVConnectorEvoRec *pConnectorEvo = 658 pDpLibConnector->pConnectorEvo; 659 const NVDispEvoRec *pDispEvo = pConnectorEvo->pDispEvo; 660 const NVDpyEvoRec *pDpyEvo; 661 662 663 /* Empty inactive group */ 664 FOR_ALL_EVO_DPYS(pDpyEvo, pDpLibConnector->dpyIdList[head], pDispEvo) { 665 if (pDpyEvo->dp.pDpLibDevice) { 666 pDpLibConnector->pGroup[head]->remove( 667 pDpyEvo->dp.pDpLibDevice->device); 668 } 669 } 670 pDpLibConnector->dpyIdList[head] = nvEmptyDpyIdList(); 671 } else { 672 nvAssert(pDpLibConnector->pGroup[head]->enumDevices(0) == NULL); 673 pDpLibConnector->headInFirmware = false; 674 } 675 676 pDpLibConnector->headMask &= ~NVBIT(head); 677 } 678 679 /* 680 * Handles DP stream programming requires to be done before committing MODESET 681 * update. The function should be called for each of affected(change in 682 * head-connector attachment) DpLib connectors, before commit. 683 */ 684 void nvDPPreSetMode(NVDPLibConnectorPtr pDpLibConnector, 685 const NVEvoModesetUpdateState *pModesetUpdateState) 686 { 687 const NVConnectorEvoRec *pConnectorEvo = 688 pDpLibConnector->pConnectorEvo; 689 NVDispEvoRec *pDispEvo = pConnectorEvo->pDispEvo; 690 const NvU32 oldHeadMask = pDpLibConnector->headMask; 691 const NvU32 newHeadMask = 692 nvConnectorGetAttachedHeadMaskEvo(pConnectorEvo); 693 694 for (NvU32 head = 0; head < pDispEvo->pDevEvo->numHeads; head++) { 695 696 if ((newHeadMask & NVBIT(head)) != 0x0 && 697 (oldHeadMask & NVBIT(head)) == 0x0) { 698 699 if (pModesetUpdateState->pDpLibModesetState[head] != NULL) { 700 NotifyAttachBegin(pDpLibConnector, 701 head, 702 pModesetUpdateState->pDpLibModesetState[head]); 703 } 704 } else if ((newHeadMask & NVBIT(head)) == 0x0 && 705 (oldHeadMask & NVBIT(head)) != 0x0) { 706 707 NotifyDetachBegin(pDpLibConnector, head); 708 709 } 710 } 711 } 712 713 /* 714 * Handles DP stream programming requires to be done before committing MODESET 715 * update. The function should be called for each of affected(change in 716 * head-connector attachment) DpLib connectors, before commit. 717 */ 718 void nvDPPostSetMode(NVDPLibConnectorPtr pDpLibConnector, 719 const NVEvoModesetUpdateState *pModesetUpdateState) 720 { 721 const NVConnectorEvoRec *pConnectorEvo = 722 pDpLibConnector->pConnectorEvo; 723 const NVDispEvoRec *pDispEvo = pConnectorEvo->pDispEvo; 724 const NvU32 oldHeadMask = pDpLibConnector->headMask; 725 const NvU32 newHeadMask = 726 nvConnectorGetAttachedHeadMaskEvo(pConnectorEvo); 727 728 for (NvU32 head = 0; head < pDispEvo->pDevEvo->numHeads; head++) { 729 730 if ((newHeadMask & NVBIT(head)) != 0x0 && 731 (oldHeadMask & NVBIT(head)) == 0x0) { 732 733 if (pModesetUpdateState->pDpLibModesetState[head] != NULL) { 734 NotifyAttachEnd(pDpLibConnector, head); 735 } 736 } else if ((newHeadMask & NVBIT(head)) == 0x0 && 737 (oldHeadMask & NVBIT(head)) != 0x0) { 738 739 NotifyDetachEnd(pDpLibConnector, head); 740 741 } 742 } 743 744 /* 745 * Update DisplayPort link information for all displays on DpLib connector 746 */ 747 if (newHeadMask != oldHeadMask) { 748 NVDpyEvoPtr pDpyEvo; 749 750 FOR_ALL_EVO_DPYS(pDpyEvo, pDispEvo->validDisplays, pDispEvo) { 751 if (pDpyEvo->pConnectorEvo->pDpLibConnector == pDpLibConnector) { 752 nvDPLibUpdateDpyLinkConfiguration(pDpyEvo); 753 } 754 } 755 } 756 } 757 758 void nvDPPause(NVDPLibConnectorPtr pNVDpLibConnector) 759 { 760 DisplayPort::Connector *connector = pNVDpLibConnector->connector; 761 const NVConnectorEvoRec *pConnectorEvo = pNVDpLibConnector->pConnectorEvo; 762 const NVDispEvoRec *pDispEvo = pConnectorEvo->pDispEvo; 763 const NVDevEvoRec *pDevEvo = pDispEvo->pDevEvo; 764 765 if (!pNVDpLibConnector->isActive) { 766 return; 767 } 768 769 if (pDevEvo->skipConsoleRestore && pNVDpLibConnector->headMask != 0) { 770 /* Clear vbios DisplayPort RAD scratch registers, see bug 200471345 */ 771 772 nvAssert(nvPopCount32(pNVDpLibConnector->headMask) == 1); 773 nvAssert(connector->isDp11ProtocolForced()); 774 775 NV0073_CTRL_CMD_DP_CONFIG_RAD_SCRATCH_REG_PARAMS params = {0}; 776 777 params.subDeviceInstance = pDispEvo->displayOwner; 778 params.displayId = nvDpyIdToNvU32(pConnectorEvo->displayId); 779 780 nvAssert(pConnectorEvo->or.protocol == 781 NV0073_CTRL_SPECIFIC_OR_PROTOCOL_SOR_DP_A || 782 pConnectorEvo->or.protocol == 783 NV0073_CTRL_SPECIFIC_OR_PROTOCOL_SOR_DP_B); 784 785 params.dpLink = pConnectorEvo->or.protocol == 786 NV0073_CTRL_SPECIFIC_OR_PROTOCOL_SOR_DP_A ? 0 : 1; 787 params.sorIndex = pConnectorEvo->or.primary; 788 789 NvU32 ret = nvRmApiControl( 790 nvEvoGlobal.clientHandle, 791 pDevEvo->displayCommonHandle, 792 NV0073_CTRL_CMD_DP_CONFIG_RAD_SCRATCH_REG, 793 ¶ms, 794 sizeof(params)); 795 796 if (ret != NVOS_STATUS_SUCCESS) { 797 nvEvoLogDispDebug( 798 pDispEvo, 799 EVO_LOG_ERROR, 800 "NV0073_CTRL_CMD_DP_CONFIG_RAD_SCRATCH_REG " 801 "failed, error code 0x%x", 802 ret); 803 } 804 } 805 806 /* Before pausing DpLib, destroy group and clear head bitmask */ 807 for (NvU32 head = 0; head < ARRAY_LEN(pNVDpLibConnector->pGroup); head++) { 808 pNVDpLibConnector->pGroup[head]->destroy(); 809 } 810 pNVDpLibConnector->headMask = 0x0; 811 812 connector->pause(); 813 814 pNVDpLibConnector->isActive = false; 815 } 816 817 /*! 818 * Determine which head, if any, is driving this connector. 819 */ 820 static NvU32 GetFirmwareHead(NVConnectorEvoPtr pConnectorEvo) 821 { 822 NvU32 orIndex = pConnectorEvo->or.primary; 823 824 if (orIndex == NV_INVALID_OR || 825 pConnectorEvo->or.ownerHeadMask[orIndex] == 0) { 826 return NV_INVALID_HEAD; 827 } 828 829 return BIT_IDX_32(pConnectorEvo->or.ownerHeadMask[orIndex]); 830 } 831 832 /*! 833 * Determine whether an active connector shares an OR with this connector. 834 */ 835 static bool ConnectorIsSharedWithActiveOR(NVConnectorEvoPtr pConnectorEvo) 836 { 837 NVDispEvoPtr pDispEvo = pConnectorEvo->pDispEvo; 838 NVConnectorEvoPtr pOtherConnectorEvo; 839 840 FOR_ALL_EVO_CONNECTORS(pOtherConnectorEvo, pDispEvo) { 841 if (pOtherConnectorEvo != pConnectorEvo && 842 nvIsConnectorActiveEvo(pOtherConnectorEvo) && 843 (pOtherConnectorEvo->or.primary == pConnectorEvo->or.primary)) { 844 nvAssert(pOtherConnectorEvo->or.primary != NV_INVALID_OR); 845 return true; 846 } 847 } 848 849 return false; 850 } 851 852 NvBool nvDPResume(NVDPLibConnectorPtr pNVDpLibConnector, NvBool plugged) 853 { 854 NVConnectorEvoRec *pConnectorEvo = 855 pNVDpLibConnector->pConnectorEvo; 856 NVDispEvoPtr pDispEvo = pConnectorEvo->pDispEvo; 857 DisplayPort::Connector *c = pNVDpLibConnector->connector; 858 const unsigned int firmwareHead = GetFirmwareHead(pConnectorEvo); 859 const bool firmwareLinkHandsOff = ConnectorIsSharedWithActiveOR(pConnectorEvo); 860 bool dpyIdIsDynamic = false; 861 /* By default allow MST */ 862 bool allowMST = true; 863 864 if (firmwareHead != NV_INVALID_HEAD) { 865 NVDpyId firmwareDpyId = nvInvalidDpyId(); 866 867 pNVDpLibConnector->headInFirmware = true; 868 pNVDpLibConnector->headMask = NVBIT(firmwareHead); 869 870 // Use the first displayId in the boot display list. 871 // 872 // TODO: What should we do if more than one dpy ID is listed for a boot 873 // display? 874 nvAssert(nvCountDpyIdsInDpyIdList(pDispEvo->vbiosDpyConfig[firmwareHead]) == 1); 875 firmwareDpyId = 876 nvNextDpyIdInDpyIdListUnsorted(nvInvalidDpyId(), 877 pDispEvo->vbiosDpyConfig[firmwareHead]); 878 879 dpyIdIsDynamic = !nvDpyIdsAreEqual(firmwareDpyId, 880 pConnectorEvo->displayId); 881 882 /* Do not allow MST if firmware driving DP connector in SST mode */ 883 if (!dpyIdIsDynamic) { 884 allowMST = false; 885 } 886 } 887 888 pConnectorEvo->detectComplete = FALSE; 889 890 pNVDpLibConnector->plugged = plugged; 891 if (plugged && !pNVDpLibConnector->headInFirmware) { 892 NvBool ret = nvAssignSOREvo(pDispEvo, 893 nvDpyIdToNvU32(pConnectorEvo->displayId), 894 FALSE /* b2Heads1Or */, 895 0 /* sorExcludeMask */); 896 897 nvAssert(ret); 898 if (!ret) { 899 // DP lib skips LT for unassigned SOR. 900 } 901 } 902 903 c->resume(firmwareLinkHandsOff, 904 pNVDpLibConnector->headInFirmware, 905 plugged, 906 false /* isUefiSystem */, 907 firmwareHead, 908 dpyIdIsDynamic /* bFirmwareLinkUseMultistream */, 909 true /* bDisableVbiosScratchRegisterUpdate, bug 200471345 */, 910 allowMST); 911 912 for (NvU32 head = 0; head < ARRAY_LEN(pNVDpLibConnector->pGroup); head++) { 913 pNVDpLibConnector->pGroup[head] = 914 pNVDpLibConnector->connector->newGroup(); 915 916 if (pNVDpLibConnector->pGroup[head] == NULL) { 917 for (NvU32 i = 0; i < head; i++) { 918 pNVDpLibConnector->pGroup[i]->destroy(); 919 } 920 goto failed; 921 } 922 } 923 924 pNVDpLibConnector->isActive = true; 925 return TRUE; 926 927 failed: 928 pNVDpLibConnector->connector->pause(); 929 return FALSE; 930 } 931 932 void nvDPSetAllowMultiStreamingOneConnector( 933 NVDPLibConnectorPtr pDpLibConnector, 934 NvBool allowMST) 935 { 936 NVConnectorEvoRec *pConnectorEvo = 937 pDpLibConnector->pConnectorEvo; 938 939 if (pDpLibConnector->connector->getAllowMultiStreaming() == allowMST) { 940 return; 941 } 942 943 /* 944 * If there is change in MST capability and DPlib re-runs device detection 945 * routine for plugged sink. Reset 'pConnectorEvo->detectComplete' only for 946 * MST capable sinks, in order to track completion of that fresh detection 947 * routine. 948 */ 949 if (pDpLibConnector->plugged && 950 pDpLibConnector->connector->getSinkMultiStreamCap()) { 951 pConnectorEvo->detectComplete = FALSE; 952 } 953 pDpLibConnector->connector->setAllowMultiStreaming(allowMST); 954 } 955 956 static NvBool IsDpSinkMstCapableForceSst(const NVDispEvoRec *pDispEvo, 957 const NvU32 apiHead) 958 { 959 const NVDispApiHeadStateEvoRec *pApiHeadState = 960 &pDispEvo->apiHeadState[apiHead]; 961 const NVDpyEvoRec *pDpyEvo = 962 nvGetOneArbitraryDpyEvo(pApiHeadState->activeDpys, pDispEvo); 963 const NVConnectorEvoRec *pConnectorEvo = (pDpyEvo != NULL) ? 964 pDpyEvo->pConnectorEvo : NULL; 965 966 if ((pConnectorEvo == NULL) || 967 (pConnectorEvo->pDpLibConnector == NULL)) { 968 return FALSE; 969 } 970 971 DisplayPort::Connector *c = 972 pConnectorEvo->pDpLibConnector->connector; 973 974 return (c->getSinkMultiStreamCap() && !c->getAllowMultiStreaming()); 975 } 976 977 static NvBool IsDpLinkTransitionWaitingForHeadShutDown( 978 const NVDispEvoRec *pDispEvo, 979 const NvU32 apiHead) 980 { 981 const NVDispApiHeadStateEvoRec *pApiHeadState = 982 &pDispEvo->apiHeadState[apiHead]; 983 const NVDpyEvoRec *pDpyEvo = 984 nvGetOneArbitraryDpyEvo(pApiHeadState->activeDpys, pDispEvo); 985 986 return (pDpyEvo != NULL) && 987 nvDPIsLinkAwaitingTransition(pDpyEvo->pConnectorEvo); 988 } 989 990 void nvDPSetAllowMultiStreaming(NVDevEvoPtr pDevEvo, NvBool allowMST) 991 { 992 NvBool needUpdate = FALSE; 993 NVDispEvoPtr pDispEvo; 994 NvU32 dispIndex; 995 996 FOR_ALL_EVO_DISPLAYS(pDispEvo, dispIndex, pDevEvo) { 997 NVConnectorEvoPtr pConnectorEvo; 998 999 FOR_ALL_EVO_CONNECTORS(pConnectorEvo, pDispEvo) { 1000 NVDPLibConnectorPtr pDpLibConnector = 1001 pConnectorEvo->pDpLibConnector; 1002 if (pDpLibConnector && 1003 pDpLibConnector->connector->getAllowMultiStreaming() 1004 != allowMST) { 1005 needUpdate = TRUE; 1006 } 1007 } 1008 } 1009 1010 if (!needUpdate) { 1011 return; 1012 } 1013 1014 nvShutDownApiHeads(pDevEvo, IsDpSinkMstCapableForceSst); 1015 1016 /* 1017 * Heads driving MST capable sinks in force SST mode, are shut down. Now you 1018 * can allow MST on all DisplayPort Connector, safely in compliance 1019 * of DP 1.2 specification. 1020 * 1021 * The section 5.4 and table 2-75 (of section 2.9.3.1) of DisplayPort 1.2 1022 * specification, does not allow to enable/disable MST mode of sink while 1023 * transmitting active stream (see description of CL#25551338). 1024 */ 1025 FOR_ALL_EVO_DISPLAYS(pDispEvo, dispIndex, pDevEvo) { 1026 NVConnectorEvoPtr pConnectorEvo; 1027 1028 FOR_ALL_EVO_CONNECTORS(pConnectorEvo, pDispEvo) { 1029 if (!pConnectorEvo->pDpLibConnector) { 1030 continue; 1031 } 1032 nvDPSetAllowMultiStreamingOneConnector( 1033 pConnectorEvo->pDpLibConnector, 1034 allowMST); 1035 } 1036 } 1037 1038 /* Shut down all DisplayPort heads that need to transition to/from SST. */ 1039 nvShutDownApiHeads(pDevEvo, 1040 IsDpLinkTransitionWaitingForHeadShutDown); 1041 1042 /* 1043 * Handle any pending timers the DP library scheduled to notify us 1044 * about changes in the connected device list. 1045 */ 1046 nvDPFireExpiredTimers(pDevEvo); 1047 } 1048 1049 enum NVDpLinkMode nvDPGetActiveLinkMode(NVDPLibConnectorPtr pDpLibConnector) 1050 { 1051 DisplayPort::LinkConfiguration linkConfig = 1052 pDpLibConnector->connector->getActiveLinkConfig(); 1053 if (linkConfig.lanes == 0) { 1054 return NV_DP_LINK_MODE_OFF; 1055 } 1056 return linkConfig.multistream ? NV_DP_LINK_MODE_MST : 1057 NV_DP_LINK_MODE_SST; 1058 } 1059