1 /* 2 * SPDX-FileCopyrightText: Copyright (c) 2013 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 "nvkms-evo.h" 25 #include "nvkms-types.h" 26 #include "nvkms-attributes.h" 27 #include "nvkms-dpy.h" 28 #include "nvkms-framelock.h" 29 #include "nvkms-vrr.h" 30 #include "nvkms-rm.h" 31 #include "nvkms-rmapi.h" 32 #include "nvos.h" 33 #include "nvkms-stereo.h" 34 #include "nvkms-hdmi.h" 35 36 #include <ctrl/ctrl0073/ctrl0073dp.h> // NV0073_CTRL_CMD_DP_GET_LINK_CONFIG_* 37 38 /*! 39 * Set the current backlight brightness for the given pDpyEvo. 40 * 41 * \param[in] pDpyEvo The display device whose backlight brightness 42 * should be assigned. 43 * \param[in] brightness The backlight brightness value to program 44 * 45 * \return TRUE if backlight brightness is available for this pDpyEvo, 46 * otherwise FALSE. 47 */ 48 static NvBool DpySetBacklightBrightness(NVDpyEvoRec *pDpyEvo, NvS64 brightness) 49 { 50 NV0073_CTRL_SPECIFIC_BACKLIGHT_BRIGHTNESS_PARAMS params = { 0 }; 51 NVDispEvoPtr pDispEvo = pDpyEvo->pDispEvo; 52 NVDevEvoPtr pDevEvo = pDispEvo->pDevEvo; 53 NvU32 ret; 54 55 if (!pDpyEvo->hasBacklightBrightness) { 56 return FALSE; 57 } 58 59 if (brightness > NV0073_CTRL_BACKLIGHT_BRIGHTNESS_MAX_VALUE) { 60 return FALSE; 61 } 62 63 if (brightness < NV0073_CTRL_BACKLIGHT_BRIGHTNESS_MIN_VALUE) { 64 return FALSE; 65 } 66 67 params.subDeviceInstance = pDispEvo->displayOwner; 68 params.displayId = nvDpyEvoGetConnectorId(pDpyEvo); 69 params.brightness = brightness; 70 71 ret = nvRmApiControl( 72 nvEvoGlobal.clientHandle, 73 pDevEvo->displayCommonHandle, 74 NV0073_CTRL_CMD_SPECIFIC_SET_BACKLIGHT_BRIGHTNESS, 75 ¶ms, sizeof(params)); 76 77 return (ret == NVOS_STATUS_SUCCESS); 78 } 79 80 /*! 81 * Query the current backlight brightness for the given pDpyEvo. 82 * 83 * \param[in] pDpyEvo The display device whose backlight brightness 84 * should be queried. 85 * \param[out] pBrightness The backlight brightness value 86 * 87 * \return TRUE if backlight brightness is available for this pDpyEvo, 88 * otherwise FALSE. 89 */ 90 static NvBool DpyGetBacklightBrightness(const NVDpyEvoRec *pDpyEvo, 91 NvS64 *pBrightness) 92 { 93 NV0073_CTRL_SPECIFIC_BACKLIGHT_BRIGHTNESS_PARAMS params = { 0 }; 94 NVDispEvoPtr pDispEvo = pDpyEvo->pDispEvo; 95 NVDevEvoPtr pDevEvo = pDispEvo->pDevEvo; 96 NvU32 ret; 97 98 params.subDeviceInstance = pDispEvo->displayOwner; 99 params.displayId = nvDpyEvoGetConnectorId(pDpyEvo); 100 101 ret = nvRmApiControl( 102 nvEvoGlobal.clientHandle, 103 pDevEvo->displayCommonHandle, 104 NV0073_CTRL_CMD_SPECIFIC_GET_BACKLIGHT_BRIGHTNESS, 105 ¶ms, sizeof(params)); 106 107 if (ret != NVOS_STATUS_SUCCESS) { 108 return FALSE; 109 } 110 111 nvAssert(params.brightness <= NV0073_CTRL_BACKLIGHT_BRIGHTNESS_MAX_VALUE); 112 113 *pBrightness = params.brightness; 114 115 return TRUE; 116 } 117 118 /*! 119 * Populate NvKmsAttributeValidValuesCommonReply for backlight brightness. 120 * 121 * \param[in] pDpyEvo The display device whose backlight brightness 122 * should be queried. 123 * \param[out] pValidValues The ValidValues structure to populate. 124 * 125 * \return TRUE if backlight brightness is available for this pDpy, 126 * otherwise FALSE. 127 */ 128 static NvBool DpyGetBacklightBrightnessValidValues( 129 const NVDpyEvoRec *pDpyEvo, 130 struct NvKmsAttributeValidValuesCommonReply *pValidValues) 131 { 132 if (!pDpyEvo->hasBacklightBrightness) { 133 return FALSE; 134 } 135 136 pValidValues->type = NV_KMS_ATTRIBUTE_TYPE_RANGE; 137 138 pValidValues->u.range.min = NV0073_CTRL_BACKLIGHT_BRIGHTNESS_MIN_VALUE; 139 pValidValues->u.range.max = NV0073_CTRL_BACKLIGHT_BRIGHTNESS_MAX_VALUE; 140 141 return TRUE; 142 } 143 144 /*! 145 * Query RM for the current scanline of the given pDpyEvo. 146 * 147 * \param[in] pDpyEvo The display device whose scanline 148 * should be queried. 149 * \param[out] pScanLine The scanline value. 150 * 151 * \return TRUE if the scanline could be queried for this pDpyEvo, 152 * otherwise FALSE. 153 */ 154 static NvBool GetScanLine(const NVDpyEvoRec *pDpyEvo, NvS64 *pScanLine) 155 { 156 NV0073_CTRL_SYSTEM_GET_SCANLINE_PARAMS params = { 0 }; 157 NVDispEvoPtr pDispEvo = pDpyEvo->pDispEvo; 158 NVDevEvoPtr pDevEvo = pDispEvo->pDevEvo; 159 NvU32 head, ret; 160 161 if (pDpyEvo->apiHead == NV_INVALID_HEAD) { 162 return FALSE; 163 } 164 165 head = nvGetPrimaryHwHead(pDispEvo, pDpyEvo->apiHead); 166 nvAssert(head != NV_INVALID_HEAD); 167 168 params.subDeviceInstance = pDispEvo->displayOwner; 169 params.head = head; 170 171 ret = nvRmApiControl(nvEvoGlobal.clientHandle, 172 pDevEvo->displayCommonHandle, 173 NV0073_CTRL_CMD_SYSTEM_GET_SCANLINE, 174 ¶ms, sizeof(params)); 175 176 if (ret == NVOS_STATUS_SUCCESS) { 177 *pScanLine = params.currentScanline; 178 return TRUE; 179 } 180 181 return FALSE; 182 } 183 184 /*! 185 * Retrieve the current head of the given pDpyEvo. 186 * 187 * \param[in] pDpyEvo The display device whose head 188 * should be queried. 189 * \param[out] pHead The head value. 190 * 191 * \return TRUE. If there is no valid head pHead will 192 * return NV_INVALID_HEAD 193 */ 194 static NvBool GetHead(const NVDpyEvoRec *pDpyEvo, NvS64 *pHead) 195 { 196 *pHead = (NvS64)pDpyEvo->apiHead; 197 return TRUE; 198 } 199 200 static NvBool GetHwHead(const NVDpyEvoRec *pDpyEvo, NvS64 *pHead) 201 { 202 NvU32 primaryHwHead = 203 nvGetPrimaryHwHead(pDpyEvo->pDispEvo, pDpyEvo->apiHead); 204 *pHead = (NvS64)primaryHwHead; 205 return TRUE; 206 } 207 208 static NvBool DitherConfigurationAllowed(const NVDpyEvoRec *pDpyEvo) 209 { 210 NVDispEvoPtr pDispEvo = pDpyEvo->pDispEvo; 211 NVDevEvoPtr pDevEvo = pDispEvo->pDevEvo; 212 213 return pDevEvo->hal->caps.supportedDitheringModes != 0; 214 } 215 216 static void SetDitheringCommon(NVDpyEvoPtr pDpyEvo) 217 { 218 NVEvoUpdateState updateState = { }; 219 const NVConnectorEvoRec *pConnectorEvo = pDpyEvo->pConnectorEvo; 220 NVDispEvoRec *pDispEvo = pConnectorEvo->pDispEvo; 221 NVDispApiHeadStateEvoRec *pApiHeadState; 222 NvU32 head; 223 224 if (pDpyEvo->apiHead == NV_INVALID_HEAD) { 225 return; 226 } 227 pApiHeadState = &pDispEvo->apiHeadState[pDpyEvo->apiHead]; 228 229 nvAssert((pApiHeadState->hwHeadsMask) != 0x0 && 230 (nvDpyIdIsInDpyIdList(pDpyEvo->id, pApiHeadState->activeDpys))); 231 232 nvChooseDitheringEvo(pConnectorEvo, 233 pApiHeadState->attributes.colorBpc, 234 &pDpyEvo->requestedDithering, 235 &pApiHeadState->attributes.dithering); 236 237 FOR_EACH_EVO_HW_HEAD_IN_MASK(pApiHeadState->hwHeadsMask, head) { 238 nvSetDitheringEvo(pDispEvo, 239 head, 240 &pApiHeadState->attributes.dithering, 241 &updateState); 242 } 243 244 nvEvoUpdateAndKickOff(pDpyEvo->pDispEvo, FALSE, &updateState, 245 TRUE /* releaseElv */); 246 } 247 248 /*! 249 * Assigns dithering on all dpys driven by pDpyEvo's head. 250 */ 251 static NvBool SetDithering(NVDpyEvoRec *pDpyEvo, NvS64 dithering) 252 { 253 if (!DitherConfigurationAllowed(pDpyEvo)) { 254 return FALSE; 255 } 256 257 switch (dithering) { 258 case NV_KMS_DPY_ATTRIBUTE_REQUESTED_DITHERING_AUTO: 259 case NV_KMS_DPY_ATTRIBUTE_REQUESTED_DITHERING_ENABLED: 260 case NV_KMS_DPY_ATTRIBUTE_REQUESTED_DITHERING_DISABLED: 261 break; 262 default: 263 return FALSE; 264 } 265 266 pDpyEvo->requestedDithering.state = dithering; 267 268 SetDitheringCommon(pDpyEvo); 269 270 return TRUE; 271 } 272 273 static NvBool GetDithering(const NVDpyEvoRec *pDpyEvo, NvS64 *pDithering) 274 { 275 if (!DitherConfigurationAllowed(pDpyEvo)) { 276 return FALSE; 277 } 278 279 *pDithering = pDpyEvo->requestedDithering.state; 280 281 return TRUE; 282 } 283 284 static NvBool GetDitheringGenericValidValues( 285 const NVDpyEvoRec *pDpyEvo, 286 struct NvKmsAttributeValidValuesCommonReply *pValidValues) 287 { 288 return DitherConfigurationAllowed(pDpyEvo); 289 } 290 291 /*! 292 * Assigns ditheringMode on all dpys driven by pDpyEvo's head. 293 */ 294 static NvBool SetDitheringMode(NVDpyEvoRec *pDpyEvo, NvS64 ditheringMode) 295 { 296 NVDevEvoPtr pDevEvo = pDpyEvo->pDispEvo->pDevEvo; 297 NvU32 mask = (1 << ditheringMode); 298 299 if (!DitherConfigurationAllowed(pDpyEvo)) { 300 return FALSE; 301 } 302 303 if (!(mask & pDevEvo->hal->caps.supportedDitheringModes)) { 304 return FALSE; 305 } 306 307 switch (ditheringMode) { 308 case NV_KMS_DPY_ATTRIBUTE_REQUESTED_DITHERING_MODE_AUTO: 309 case NV_KMS_DPY_ATTRIBUTE_REQUESTED_DITHERING_MODE_DYNAMIC_2X2: 310 case NV_KMS_DPY_ATTRIBUTE_REQUESTED_DITHERING_MODE_STATIC_2X2: 311 case NV_KMS_DPY_ATTRIBUTE_REQUESTED_DITHERING_MODE_TEMPORAL: 312 break; 313 default: 314 return FALSE; 315 } 316 317 pDpyEvo->requestedDithering.mode = ditheringMode; 318 319 SetDitheringCommon(pDpyEvo); 320 321 return TRUE; 322 } 323 324 static NvBool GetDitheringMode(const NVDpyEvoRec *pDpyEvo, 325 NvS64 *pDitheringMode) 326 { 327 if (!DitherConfigurationAllowed(pDpyEvo)) { 328 return FALSE; 329 } 330 331 *pDitheringMode = pDpyEvo->requestedDithering.mode; 332 333 return TRUE; 334 } 335 336 static NvBool GetDitheringModeValidValues( 337 const NVDpyEvoRec *pDpyEvo, 338 struct NvKmsAttributeValidValuesCommonReply *pValidValues) 339 { 340 NVDispEvoPtr pDispEvo = pDpyEvo->pDispEvo; 341 NVDevEvoPtr pDevEvo = pDispEvo->pDevEvo; 342 343 if (!DitherConfigurationAllowed(pDpyEvo)) { 344 return FALSE; 345 } 346 347 nvAssert(pValidValues->type == NV_KMS_ATTRIBUTE_TYPE_INTBITS); 348 349 pValidValues->u.bits.ints = 350 pDevEvo->hal->caps.supportedDitheringModes; 351 352 return TRUE; 353 } 354 355 /*! 356 * Assigns ditheringDepth on all dpys driven by pDpyEvo's head. 357 */ 358 static NvBool SetDitheringDepth(NVDpyEvoRec *pDpyEvo, NvS64 ditheringDepth) 359 { 360 if (!DitherConfigurationAllowed(pDpyEvo)) { 361 return FALSE; 362 } 363 364 switch (ditheringDepth) { 365 case NV_KMS_DPY_ATTRIBUTE_REQUESTED_DITHERING_DEPTH_AUTO: 366 case NV_KMS_DPY_ATTRIBUTE_REQUESTED_DITHERING_DEPTH_6_BITS: 367 case NV_KMS_DPY_ATTRIBUTE_REQUESTED_DITHERING_DEPTH_8_BITS: 368 break; 369 default: 370 return FALSE; 371 } 372 373 pDpyEvo->requestedDithering.depth = ditheringDepth; 374 375 SetDitheringCommon(pDpyEvo); 376 377 return TRUE; 378 } 379 380 static NvBool GetDitheringDepth(const NVDpyEvoRec *pDpyEvo, 381 NvS64 *pDitheringDepth) 382 { 383 if (!DitherConfigurationAllowed(pDpyEvo)) { 384 return FALSE; 385 } 386 387 *pDitheringDepth = pDpyEvo->requestedDithering.depth; 388 389 return TRUE; 390 } 391 392 static NvBool GetCurrentDithering(const NVDpyEvoRec *pDpyEvo, 393 NvS64 *pCurrentDithering) 394 { 395 if (!DitherConfigurationAllowed(pDpyEvo)) { 396 return FALSE; 397 } 398 399 *pCurrentDithering = pDpyEvo->currentAttributes.dithering.enabled; 400 401 return TRUE; 402 } 403 404 static NvBool GetCurrentDitheringMode(const NVDpyEvoRec *pDpyEvo, 405 NvS64 *pCurrentDitheringMode) 406 { 407 if (!DitherConfigurationAllowed(pDpyEvo)) { 408 return FALSE; 409 } 410 411 *pCurrentDitheringMode = 412 pDpyEvo->currentAttributes.dithering.mode; 413 414 return TRUE; 415 } 416 417 static NvBool GetCurrentDitheringDepth(const NVDpyEvoRec *pDpyEvo, 418 NvS64 *pCurrentDitheringDepth) 419 { 420 421 if (!DitherConfigurationAllowed(pDpyEvo)) { 422 return FALSE; 423 } 424 425 *pCurrentDitheringDepth = 426 pDpyEvo->currentAttributes.dithering.depth; 427 428 return TRUE; 429 } 430 431 static NvBool DigitalVibranceAvailable(const NVDpyEvoRec *pDpyEvo) 432 { 433 const NVDispEvoRec *pDispEvo = pDpyEvo->pDispEvo; 434 NVDevEvoPtr pDevEvo = pDispEvo->pDevEvo; 435 436 if (!nvDpyEvoIsActive(pDpyEvo)) { 437 return FALSE; 438 } 439 440 if (!pDevEvo->hal->caps.supportsDigitalVibrance) { 441 return FALSE; 442 } 443 444 return TRUE; 445 } 446 447 /*! 448 * Assigns dvc on all dpys driven by pDpyEvo's head. 449 */ 450 static NvBool SetDigitalVibrance(NVDpyEvoRec *pDpyEvo, NvS64 dvc) 451 { 452 NVEvoUpdateState updateState = { }; 453 NVDispEvoRec *pDispEvo = pDpyEvo->pDispEvo; 454 NVDispApiHeadStateEvoRec *pApiHeadState; 455 NvU32 head; 456 457 if ((pDpyEvo->apiHead == NV_INVALID_HEAD) || 458 !DigitalVibranceAvailable(pDpyEvo)) { 459 return FALSE; 460 } 461 pApiHeadState = &pDispEvo->apiHeadState[pDpyEvo->apiHead]; 462 463 nvAssert((pApiHeadState->hwHeadsMask) != 0x0 && 464 (nvDpyIdIsInDpyIdList(pDpyEvo->id, pApiHeadState->activeDpys))); 465 466 dvc = NV_MAX(dvc, NV_EVO_DVC_MIN); 467 dvc = NV_MIN(dvc, NV_EVO_DVC_MAX); 468 469 FOR_EACH_EVO_HW_HEAD_IN_MASK(pApiHeadState->hwHeadsMask, head) { 470 nvSetDVCEvo(pDispEvo, head, dvc, &updateState); 471 } 472 473 nvEvoUpdateAndKickOff(pDpyEvo->pDispEvo, FALSE, &updateState, 474 TRUE /* releaseElv */); 475 476 pApiHeadState->attributes.dvc = dvc; 477 478 return TRUE; 479 } 480 481 static NvBool GetDigitalVibrance(const NVDpyEvoRec *pDpyEvo, NvS64 *pDvc) 482 { 483 if (!DigitalVibranceAvailable(pDpyEvo)) { 484 return FALSE; 485 } 486 487 *pDvc = pDpyEvo->currentAttributes.dvc; 488 489 return TRUE; 490 } 491 492 static NvBool GetDigitalVibranceValidValues( 493 const NVDpyEvoRec *pDpyEvo, 494 struct NvKmsAttributeValidValuesCommonReply *pValidValues) 495 { 496 if (!DigitalVibranceAvailable(pDpyEvo)) { 497 return FALSE; 498 } 499 500 nvAssert(pValidValues->type == NV_KMS_ATTRIBUTE_TYPE_RANGE); 501 502 pValidValues->u.range.min = NV_EVO_DVC_MIN; 503 pValidValues->u.range.max = NV_EVO_DVC_MAX; 504 505 return TRUE; 506 } 507 508 static NvBool ImageSharpeningAvailable(const NVDpyEvoRec *pDpyEvo) 509 { 510 if (!pDpyEvo->pDispEvo->pDevEvo->hal->caps.supportsImageSharpening) { 511 return FALSE; 512 } 513 514 if (!nvDpyEvoIsActive(pDpyEvo)) { 515 return FALSE; 516 } 517 518 return pDpyEvo->currentAttributes.imageSharpening.available; 519 } 520 521 /*! 522 * Assigns imageSharpening on all dpys driven by pDpyEvo's head. 523 */ 524 static NvBool SetImageSharpening(NVDpyEvoRec *pDpyEvo, NvS64 imageSharpening) 525 { 526 NVEvoUpdateState updateState = { }; 527 NVDispEvoPtr pDispEvo = pDpyEvo->pDispEvo; 528 NVDispApiHeadStateEvoRec *pApiHeadState; 529 NvU32 head; 530 531 if ((pDpyEvo->apiHead == NV_INVALID_HEAD) || 532 !ImageSharpeningAvailable(pDpyEvo)) { 533 return FALSE; 534 } 535 pApiHeadState = &pDispEvo->apiHeadState[pDpyEvo->apiHead]; 536 537 nvAssert((pApiHeadState->hwHeadsMask) != 0x0 && 538 (nvDpyIdIsInDpyIdList(pDpyEvo->id, pApiHeadState->activeDpys))); 539 540 imageSharpening = NV_MAX(imageSharpening, NV_EVO_IMAGE_SHARPENING_MIN); 541 imageSharpening = NV_MIN(imageSharpening, NV_EVO_IMAGE_SHARPENING_MAX); 542 543 FOR_EACH_EVO_HW_HEAD_IN_MASK(pApiHeadState->hwHeadsMask, head) { 544 nvSetImageSharpeningEvo(pDispEvo, head, imageSharpening, &updateState); 545 } 546 547 nvEvoUpdateAndKickOff(pDispEvo, FALSE, &updateState, 548 TRUE /* releaseElv */); 549 550 pApiHeadState->attributes.imageSharpening.value = imageSharpening; 551 552 return TRUE; 553 } 554 555 static NvBool GetImageSharpening(const NVDpyEvoRec *pDpyEvo, 556 NvS64 *pImageSharpening) 557 { 558 if (!ImageSharpeningAvailable(pDpyEvo)) { 559 return FALSE; 560 } 561 562 *pImageSharpening = pDpyEvo->currentAttributes.imageSharpening.value; 563 564 return TRUE; 565 } 566 567 static NvBool GetImageSharpeningValidValues( 568 const NVDpyEvoRec *pDpyEvo, 569 struct NvKmsAttributeValidValuesCommonReply *pValidValues) 570 { 571 if (!ImageSharpeningAvailable(pDpyEvo)) { 572 return FALSE; 573 } 574 575 nvAssert(pValidValues->type == NV_KMS_ATTRIBUTE_TYPE_RANGE); 576 577 pValidValues->u.range.min = NV_EVO_IMAGE_SHARPENING_MIN; 578 pValidValues->u.range.max = NV_EVO_IMAGE_SHARPENING_MAX; 579 580 return TRUE; 581 } 582 583 static NvBool GetImageSharpeningAvailable(const NVDpyEvoRec *pDpyEvo, 584 NvS64 *pImageSharpeningAvailable) 585 { 586 *pImageSharpeningAvailable = ImageSharpeningAvailable(pDpyEvo); 587 588 return TRUE; 589 } 590 591 static NvBool GetImageSharpeningDefault(const NVDpyEvoRec *pDpyEvo, 592 NvS64 *pImageSharpeningDefault) 593 { 594 if (!nvDpyEvoIsActive(pDpyEvo)) { 595 return FALSE; 596 } 597 598 *pImageSharpeningDefault = NV_EVO_IMAGE_SHARPENING_DEFAULT; 599 600 return TRUE; 601 } 602 603 static NvBool ColorSpaceAndRangeAvailable(const NVDpyEvoRec *pDpyEvo) 604 { 605 return ((pDpyEvo->pConnectorEvo->legacyType == 606 NV0073_CTRL_SPECIFIC_DISPLAY_TYPE_DFP) && 607 (pDpyEvo->pConnectorEvo->signalFormat != 608 NVKMS_CONNECTOR_SIGNAL_FORMAT_DSI)); 609 } 610 611 /*! 612 * Send infoFrame with new color{Space,Range}. 613 */ 614 static void DpyPostColorSpaceOrRangeSetEvo(NVDpyEvoPtr pDpyEvo) 615 { 616 enum NvKmsDpyAttributeCurrentColorSpaceValue colorSpace; 617 enum NvKmsDpyAttributeColorBpcValue colorBpc; 618 enum NvKmsDpyAttributeColorRangeValue colorRange; 619 NVEvoUpdateState updateState = { }; 620 NVDispEvoRec *pDispEvo = pDpyEvo->pDispEvo; 621 NVDispApiHeadStateEvoRec *pApiHeadState; 622 enum NvYuv420Mode yuv420Mode; 623 enum NvKmsOutputTf tf; 624 NvU32 head; 625 626 if (pDpyEvo->apiHead == NV_INVALID_HEAD) { 627 return; 628 } 629 pApiHeadState = &pDispEvo->apiHeadState[pDpyEvo->apiHead]; 630 631 nvAssert((pApiHeadState->hwHeadsMask) != 0x0 && 632 (nvDpyIdIsInDpyIdList(pDpyEvo->id, pApiHeadState->activeDpys))); 633 634 head = nvGetPrimaryHwHead(pDispEvo, pDpyEvo->apiHead); 635 yuv420Mode = pDispEvo->headState[head].timings.yuv420Mode; 636 tf = pDispEvo->headState[head].tf; 637 #if defined(DEBUG) 638 FOR_EACH_EVO_HW_HEAD_IN_MASK(pApiHeadState->hwHeadsMask, head) { 639 nvAssert(yuv420Mode == pDispEvo->headState[head].timings.yuv420Mode); 640 nvAssert(tf == pDispEvo->headState[head].tf); 641 } 642 #endif 643 644 /* 645 * Choose current colorSpace and colorRange based on the current mode 646 * timings and the requested color space and range. 647 */ 648 if (!nvChooseCurrentColorSpaceAndRangeEvo(pDpyEvo, 649 yuv420Mode, 650 tf, 651 pDpyEvo->requestedColorSpace, 652 pDpyEvo->requestedColorRange, 653 &colorSpace, 654 &colorBpc, 655 &colorRange)) { 656 nvAssert(!"Failed to choose current color space and color range"); 657 return; 658 } 659 660 /* For DP, neither color space nor bpc can be changed without a modeset */ 661 if (nvConnectorUsesDPLib(pDpyEvo->pConnectorEvo) && 662 ((pApiHeadState->attributes.colorSpace != colorSpace) || 663 (pApiHeadState->attributes.colorBpc != colorBpc))) { 664 return; 665 } 666 667 pApiHeadState->attributes.colorSpace = colorSpace; 668 pApiHeadState->attributes.colorRange = colorRange; 669 pApiHeadState->attributes.colorBpc = colorBpc; 670 671 /* Update hardware's current colorSpace and colorRange */ 672 FOR_EACH_EVO_HW_HEAD_IN_MASK(pApiHeadState->hwHeadsMask, head) { 673 enum nvKmsPixelDepth newPixelDepth = 674 nvEvoColorSpaceBpcToPixelDepth(pApiHeadState->attributes.colorSpace, 675 pApiHeadState->attributes.colorBpc); 676 677 nvUpdateCurrentHardwareColorSpaceAndRangeEvo(pDispEvo, 678 head, 679 pApiHeadState->attributes.colorSpace, 680 pApiHeadState->attributes.colorRange, 681 &updateState); 682 683 if (newPixelDepth != pDispEvo->headState[head].pixelDepth) { 684 pDispEvo->headState[head].pixelDepth = newPixelDepth; 685 nvEvoHeadSetControlOR(pDispEvo, head, &updateState); 686 } 687 } 688 689 /* Update InfoFrames as needed. */ 690 nvUpdateInfoFrames(pDpyEvo); 691 692 // Kick off 693 nvEvoUpdateAndKickOff(pDispEvo, FALSE, &updateState, TRUE /* releaseElv */); 694 695 // XXX DisplayPort sets color format. 696 } 697 698 static NvU32 DpyGetValidColorSpaces(const NVDpyEvoRec *pDpyEvo) 699 { 700 const NVDevEvoRec *pDevEvo = pDpyEvo->pDispEvo->pDevEvo; 701 NvU32 val = (1 << NV_KMS_DPY_ATTRIBUTE_REQUESTED_COLOR_SPACE_RGB); 702 703 if ((nvDpyIsHdmiEvo(pDpyEvo) && 704 (pDevEvo->caps.hdmiYCbCr422MaxBpc != 0)) || 705 (nvConnectorUsesDPLib(pDpyEvo->pConnectorEvo) && 706 (pDevEvo->caps.dpYCbCr422MaxBpc != 0))) { 707 val |= (1 << NV_KMS_DPY_ATTRIBUTE_REQUESTED_COLOR_SPACE_YCbCr422); 708 } 709 710 if (nvDpyIsHdmiEvo(pDpyEvo) || 711 nvConnectorUsesDPLib(pDpyEvo->pConnectorEvo)) { 712 val |= (1 << NV_KMS_DPY_ATTRIBUTE_REQUESTED_COLOR_SPACE_YCbCr444); 713 } 714 715 return val; 716 } 717 718 NvBool nvDpyValidateColorSpace(const NVDpyEvoRec *pDpyEvo, NvS64 value) 719 { 720 NvU32 validMask = DpyGetValidColorSpaces(pDpyEvo); 721 722 if (!ColorSpaceAndRangeAvailable(pDpyEvo) || !(validMask & (1 << value))) { 723 return FALSE; 724 } 725 726 return TRUE; 727 } 728 729 static NvBool SetRequestedColorSpace(NVDpyEvoRec *pDpyEvo, NvS64 value) 730 { 731 if (!nvDpyValidateColorSpace(pDpyEvo, value)) { 732 return FALSE; 733 } 734 735 pDpyEvo->requestedColorSpace = value; 736 737 DpyPostColorSpaceOrRangeSetEvo(pDpyEvo); 738 739 return TRUE; 740 } 741 742 static NvBool GetCurrentColorSpace(const NVDpyEvoRec *pDpyEvo, NvS64 *pValue) 743 { 744 if (!ColorSpaceAndRangeAvailable(pDpyEvo)) { 745 return FALSE; 746 } 747 748 *pValue = pDpyEvo->currentAttributes.colorSpace; 749 750 return TRUE; 751 } 752 753 static NvBool GetRequestedColorSpace(const NVDpyEvoRec *pDpyEvo, NvS64 *pValue) 754 { 755 if (!ColorSpaceAndRangeAvailable(pDpyEvo)) { 756 return FALSE; 757 } 758 759 *pValue = pDpyEvo->requestedColorSpace; 760 761 return TRUE; 762 } 763 764 static NvBool GetCurrentColorSpaceValidValues( 765 const NVDpyEvoRec *pDpyEvo, 766 struct NvKmsAttributeValidValuesCommonReply *pValidValues) 767 { 768 if (!ColorSpaceAndRangeAvailable(pDpyEvo)) { 769 return FALSE; 770 } 771 772 nvAssert(pValidValues->type == NV_KMS_ATTRIBUTE_TYPE_INTBITS); 773 774 pValidValues->u.bits.ints = DpyGetValidColorSpaces(pDpyEvo); 775 776 /* 777 * The current color space may be YUV420 depending on the current mode. 778 * Rather than determine whether this pDpy is capable of driving any 779 * YUV420 modes, just assume this is always a valid current color space. 780 */ 781 pValidValues->u.bits.ints |= 782 (1 << NV_KMS_DPY_ATTRIBUTE_CURRENT_COLOR_SPACE_YCbCr420); 783 784 return TRUE; 785 } 786 787 static NvBool GetRequestedColorSpaceValidValues( 788 const NVDpyEvoRec *pDpyEvo, 789 struct NvKmsAttributeValidValuesCommonReply *pValidValues) 790 { 791 if (!ColorSpaceAndRangeAvailable(pDpyEvo)) { 792 return FALSE; 793 } 794 795 nvAssert(pValidValues->type == NV_KMS_ATTRIBUTE_TYPE_INTBITS); 796 797 pValidValues->u.bits.ints = DpyGetValidColorSpaces(pDpyEvo); 798 799 return TRUE; 800 } 801 802 static NvBool SetRequestedColorRange(NVDpyEvoRec *pDpyEvo, NvS64 value) 803 { 804 if (!ColorSpaceAndRangeAvailable(pDpyEvo)) { 805 return FALSE; 806 } 807 808 pDpyEvo->requestedColorRange = value; 809 810 DpyPostColorSpaceOrRangeSetEvo(pDpyEvo); 811 812 return TRUE; 813 } 814 815 static NvBool GetCurrentColorRange(const NVDpyEvoRec *pDpyEvo, NvS64 *pValue) 816 { 817 if (!ColorSpaceAndRangeAvailable(pDpyEvo)) { 818 return FALSE; 819 } 820 821 *pValue = pDpyEvo->currentAttributes.colorRange; 822 823 return TRUE; 824 } 825 826 static NvBool GetRequestedColorRange(const NVDpyEvoRec *pDpyEvo, NvS64 *pValue) 827 { 828 if (!ColorSpaceAndRangeAvailable(pDpyEvo)) { 829 return FALSE; 830 } 831 832 *pValue = pDpyEvo->requestedColorRange; 833 834 return TRUE; 835 } 836 837 static NvBool GetColorRangeValidValues( 838 const NVDpyEvoRec *pDpyEvo, 839 struct NvKmsAttributeValidValuesCommonReply *pValidValues) 840 { 841 if (!ColorSpaceAndRangeAvailable(pDpyEvo)) { 842 return FALSE; 843 } 844 845 nvAssert(pValidValues->type == NV_KMS_ATTRIBUTE_TYPE_INTBITS); 846 847 /* 848 * The preferred color range may always select between full or limited 849 * range, but the actual resulting color range depends on the current 850 * color space. Both color ranges are always valid values for both 851 * preferred and current color range attributes. 852 */ 853 pValidValues->u.bits.ints = (1 << NV_KMS_DPY_ATTRIBUTE_COLOR_RANGE_FULL) | 854 (1 << NV_KMS_DPY_ATTRIBUTE_COLOR_RANGE_LIMITED); 855 856 return TRUE; 857 } 858 859 static NvBool DigitalSignalAvailable(const NVDpyEvoRec *pDpyEvo) 860 { 861 return pDpyEvo->pConnectorEvo->legacyType == 862 NV0073_CTRL_SPECIFIC_DISPLAY_TYPE_DFP; 863 } 864 865 static NvBool GetDigitalSignal(const NVDpyEvoRec *pDpyEvo, NvS64 *pValue) 866 { 867 if (!DigitalSignalAvailable(pDpyEvo)) { 868 return FALSE; 869 } 870 871 *pValue = pDpyEvo->currentAttributes.digitalSignal; 872 873 return TRUE; 874 } 875 876 static NvBool GetDigitalSignalValidValues( 877 const NVDpyEvoRec *pDpyEvo, 878 struct NvKmsAttributeValidValuesCommonReply *pValidValues) 879 { 880 if (!DigitalSignalAvailable(pDpyEvo)) { 881 return FALSE; 882 } 883 884 nvAssert(pValidValues->type == NV_KMS_ATTRIBUTE_TYPE_INTEGER); 885 886 return TRUE; 887 } 888 889 static NvBool DigitalLinkTypeAvailable(const NVDpyEvoRec *pDpyEvo) 890 { 891 return (nvDpyEvoIsActive(pDpyEvo) && 892 (pDpyEvo->pConnectorEvo->legacyType == 893 NV0073_CTRL_SPECIFIC_DISPLAY_TYPE_DFP)); 894 } 895 896 static NvBool GetDigitalLinkType(const NVDpyEvoRec *pDpyEvo, NvS64 *pValue) 897 { 898 if (!DigitalLinkTypeAvailable(pDpyEvo)) { 899 return FALSE; 900 } 901 902 if (nvConnectorUsesDPLib(pDpyEvo->pConnectorEvo)) { 903 *pValue = nvRMLaneCountToNvKms(pDpyEvo->dp.laneCount); 904 } else { 905 enum nvKmsTimingsProtocol protocol; 906 const NVDispEvoRec *pDispEvo = pDpyEvo->pDispEvo; 907 NvU32 head = nvGetPrimaryHwHead(pDispEvo, pDpyEvo->apiHead); 908 909 nvAssert(head != NV_INVALID_HEAD); 910 protocol = pDispEvo->headState[head].timings.protocol; 911 #if defined(DEBUG) 912 { 913 NvU32 h; 914 FOR_EACH_EVO_HW_HEAD(pDispEvo, pDpyEvo->apiHead, h) { 915 nvAssert(protocol == pDispEvo->headState[h].timings.protocol); 916 } 917 } 918 #endif 919 920 *pValue = (protocol == NVKMS_PROTOCOL_SOR_DUAL_TMDS) ? 921 NV_KMS_DPY_ATTRIBUTE_DIGITAL_LINK_TYPE_DUAL : 922 NV_KMS_DPY_ATTRIBUTE_DIGITAL_LINK_TYPE_SINGLE; 923 } 924 925 return TRUE; 926 } 927 928 static NvBool GetDigitalLinkTypeValidValues( 929 const NVDpyEvoRec *pDpyEvo, 930 struct NvKmsAttributeValidValuesCommonReply *pValidValues) 931 { 932 if (!DigitalLinkTypeAvailable(pDpyEvo)) { 933 return FALSE; 934 } 935 936 nvAssert(pValidValues->type == NV_KMS_ATTRIBUTE_TYPE_INTEGER); 937 938 return TRUE; 939 } 940 941 static NvBool DisplayportLinkRateAvailable(const NVDpyEvoRec *pDpyEvo) 942 { 943 return ((pDpyEvo->pConnectorEvo->legacyType == 944 NV0073_CTRL_SPECIFIC_DISPLAY_TYPE_DFP) && 945 nvConnectorUsesDPLib(pDpyEvo->pConnectorEvo)); 946 } 947 948 static NvBool GetDisplayportLinkRate(const NVDpyEvoRec *pDpyEvo, NvS64 *pValue) 949 { 950 if (!DisplayportLinkRateAvailable(pDpyEvo)) { 951 return FALSE; 952 } 953 954 *pValue = pDpyEvo->dp.linkRate; 955 956 return TRUE; 957 } 958 959 static NvBool GetDisplayportLinkRateValidValues( 960 const NVDpyEvoRec *pDpyEvo, 961 struct NvKmsAttributeValidValuesCommonReply *pValidValues) 962 { 963 if (!DisplayportLinkRateAvailable(pDpyEvo)) { 964 return FALSE; 965 } 966 967 nvAssert(pValidValues->type == NV_KMS_ATTRIBUTE_TYPE_INTEGER); 968 969 return TRUE; 970 } 971 972 static NvBool GetDisplayportConnectorType(const NVDpyEvoRec *pDpyEvo, 973 NvS64 *pValue) 974 { 975 if (!nvConnectorUsesDPLib(pDpyEvo->pConnectorEvo)) { 976 return FALSE; 977 } 978 979 *pValue = pDpyEvo->dp.connectorType; 980 981 return TRUE; 982 } 983 984 static NvBool GetDisplayportConnectorTypeValidValues( 985 const NVDpyEvoRec *pDpyEvo, 986 struct NvKmsAttributeValidValuesCommonReply *pValidValues) 987 { 988 if (!nvConnectorUsesDPLib(pDpyEvo->pConnectorEvo)) { 989 return FALSE; 990 } 991 992 nvAssert(pValidValues->type == NV_KMS_ATTRIBUTE_TYPE_INTEGER); 993 994 return TRUE; 995 } 996 997 static NvBool GetDisplayportIsMultistream(const NVDpyEvoRec *pDpyEvo, 998 NvS64 *pValue) 999 { 1000 if (!nvConnectorUsesDPLib(pDpyEvo->pConnectorEvo)) { 1001 return FALSE; 1002 } 1003 1004 *pValue = nvDpyEvoIsDPMST(pDpyEvo); 1005 1006 return TRUE; 1007 } 1008 1009 static NvBool GetDisplayportIsMultistreamValidValues( 1010 const NVDpyEvoRec *pDpyEvo, 1011 struct NvKmsAttributeValidValuesCommonReply *pValidValues) 1012 { 1013 if (!nvConnectorUsesDPLib(pDpyEvo->pConnectorEvo)) { 1014 return FALSE; 1015 } 1016 1017 nvAssert(pValidValues->type == NV_KMS_ATTRIBUTE_TYPE_BOOLEAN); 1018 1019 return TRUE; 1020 } 1021 1022 static NvBool GetDisplayportSinkIsAudioCapable(const NVDpyEvoRec *pDpyEvo, 1023 NvS64 *pValue) 1024 { 1025 if (!nvConnectorUsesDPLib(pDpyEvo->pConnectorEvo)) { 1026 return FALSE; 1027 } 1028 1029 *pValue = pDpyEvo->dp.sinkIsAudioCapable; 1030 1031 return TRUE; 1032 } 1033 1034 static NvBool GetDisplayportSinkIsAudioCapableValidValues( 1035 const NVDpyEvoRec *pDpyEvo, 1036 struct NvKmsAttributeValidValuesCommonReply *pValidValues) 1037 { 1038 if (!nvConnectorUsesDPLib(pDpyEvo->pConnectorEvo)) { 1039 return FALSE; 1040 } 1041 1042 nvAssert(pValidValues->type == NV_KMS_ATTRIBUTE_TYPE_BOOLEAN); 1043 1044 return TRUE; 1045 } 1046 1047 NvS64 nvRMLaneCountToNvKms(NvU32 rmLaneCount) 1048 { 1049 switch (rmLaneCount) { 1050 case NV0073_CTRL_CMD_DP_GET_LINK_CONFIG_LANE_COUNT_0: 1051 // fallthrough 1052 default: 1053 nvAssert(!"Unexpected DisplayPort lane configuration!"); 1054 // fallthrough 1055 case NV0073_CTRL_CMD_DP_GET_LINK_CONFIG_LANE_COUNT_1: 1056 return NV_KMS_DPY_ATTRIBUTE_DIGITAL_LINK_TYPE_SINGLE; 1057 case NV0073_CTRL_CMD_DP_GET_LINK_CONFIG_LANE_COUNT_2: 1058 return NV_KMS_DPY_ATTRIBUTE_DIGITAL_LINK_TYPE_DUAL; 1059 case NV0073_CTRL_CMD_DP_GET_LINK_CONFIG_LANE_COUNT_4: 1060 return NV_KMS_DPY_ATTRIBUTE_DIGITAL_LINK_TYPE_QUAD; 1061 } 1062 } 1063 1064 static NvBool SetStereoEvo(NVDpyEvoPtr pDpyEvo, NvS64 value) 1065 { 1066 NvBool enable = !!value; 1067 1068 if (pDpyEvo->apiHead == NV_INVALID_HEAD) { 1069 return FALSE; 1070 } 1071 1072 return nvSetStereo(pDpyEvo->pDispEvo, pDpyEvo->apiHead, enable); 1073 } 1074 1075 static NvBool GetStereoEvo(const NVDpyEvoRec *pDpyEvo, NvS64 *pValue) 1076 { 1077 if (pDpyEvo->apiHead == NV_INVALID_HEAD) { 1078 return FALSE; 1079 } 1080 1081 *pValue = !!nvGetStereo(pDpyEvo->pDispEvo, pDpyEvo->apiHead); 1082 1083 return TRUE; 1084 } 1085 1086 static NvBool GetVrrMinRefreshRate(const NVDpyEvoRec *pDpyEvo, NvS64 *pValue) 1087 { 1088 return FALSE; 1089 } 1090 1091 static NvBool GetVrrMinRefreshRateValidValues( 1092 const NVDpyEvoRec *pDpyEvo, 1093 struct NvKmsAttributeValidValuesCommonReply *pValidValues) 1094 { 1095 return FALSE; 1096 } 1097 1098 static const struct { 1099 NvBool (*set)(NVDpyEvoPtr pDpyEvo, NvS64 value); 1100 NvBool (*get)(const NVDpyEvoRec *pDpyEvo, NvS64 *pValue); 1101 NvBool (*getValidValues)( 1102 const NVDpyEvoRec *pDpyEvo, 1103 struct NvKmsAttributeValidValuesCommonReply *pValidValues); 1104 enum NvKmsAttributeType type; 1105 } DpyAttributesDispatchTable[] = { 1106 [NV_KMS_DPY_ATTRIBUTE_BACKLIGHT_BRIGHTNESS] = { 1107 .set = DpySetBacklightBrightness, 1108 .get = DpyGetBacklightBrightness, 1109 .getValidValues = DpyGetBacklightBrightnessValidValues, 1110 .type = NV_KMS_ATTRIBUTE_TYPE_RANGE, 1111 }, 1112 [NV_KMS_DPY_ATTRIBUTE_SCANLINE] = { 1113 .set = NULL, 1114 .get = GetScanLine, 1115 .getValidValues = NULL, 1116 .type = NV_KMS_ATTRIBUTE_TYPE_INTEGER, 1117 }, 1118 [NV_KMS_DPY_ATTRIBUTE_HEAD] = { 1119 .set = NULL, 1120 .get = GetHead, 1121 .getValidValues = NULL, 1122 .type = NV_KMS_ATTRIBUTE_TYPE_INTEGER, 1123 }, 1124 [NV_KMS_DPY_ATTRIBUTE_HW_HEAD] = { 1125 .set = NULL, 1126 .get = GetHwHead, 1127 .getValidValues = NULL, 1128 .type = NV_KMS_ATTRIBUTE_TYPE_INTEGER, 1129 }, 1130 [NV_KMS_DPY_ATTRIBUTE_REQUESTED_DITHERING] = { 1131 .set = SetDithering, 1132 .get = GetDithering, 1133 .getValidValues = GetDitheringGenericValidValues, 1134 .type = NV_KMS_ATTRIBUTE_TYPE_INTEGER, 1135 }, 1136 [NV_KMS_DPY_ATTRIBUTE_REQUESTED_DITHERING_MODE] = { 1137 .set = SetDitheringMode, 1138 .get = GetDitheringMode, 1139 .getValidValues = GetDitheringModeValidValues, 1140 .type = NV_KMS_ATTRIBUTE_TYPE_INTBITS, 1141 }, 1142 [NV_KMS_DPY_ATTRIBUTE_REQUESTED_DITHERING_DEPTH] = { 1143 .set = SetDitheringDepth, 1144 .get = GetDitheringDepth, 1145 .getValidValues = GetDitheringGenericValidValues, 1146 .type = NV_KMS_ATTRIBUTE_TYPE_INTEGER, 1147 }, 1148 [NV_KMS_DPY_ATTRIBUTE_CURRENT_DITHERING] = { 1149 .set = NULL, 1150 .get = GetCurrentDithering, 1151 .getValidValues = GetDitheringGenericValidValues, 1152 .type = NV_KMS_ATTRIBUTE_TYPE_BOOLEAN, 1153 }, 1154 [NV_KMS_DPY_ATTRIBUTE_CURRENT_DITHERING_MODE] = { 1155 .set = NULL, 1156 .get = GetCurrentDitheringMode, 1157 .getValidValues = GetDitheringGenericValidValues, 1158 .type = NV_KMS_ATTRIBUTE_TYPE_INTEGER, 1159 }, 1160 [NV_KMS_DPY_ATTRIBUTE_CURRENT_DITHERING_DEPTH] = { 1161 .set = NULL, 1162 .get = GetCurrentDitheringDepth, 1163 .getValidValues = GetDitheringGenericValidValues, 1164 .type = NV_KMS_ATTRIBUTE_TYPE_INTEGER, 1165 }, 1166 [NV_KMS_DPY_ATTRIBUTE_DIGITAL_VIBRANCE] = { 1167 .set = SetDigitalVibrance, 1168 .get = GetDigitalVibrance, 1169 .getValidValues = GetDigitalVibranceValidValues, 1170 .type = NV_KMS_ATTRIBUTE_TYPE_RANGE, 1171 }, 1172 [NV_KMS_DPY_ATTRIBUTE_IMAGE_SHARPENING] = { 1173 .set = SetImageSharpening, 1174 .get = GetImageSharpening, 1175 .getValidValues = GetImageSharpeningValidValues, 1176 .type = NV_KMS_ATTRIBUTE_TYPE_RANGE, 1177 }, 1178 [NV_KMS_DPY_ATTRIBUTE_IMAGE_SHARPENING_AVAILABLE] = { 1179 .set = NULL, 1180 .get = GetImageSharpeningAvailable, 1181 .getValidValues = NULL, 1182 .type = NV_KMS_ATTRIBUTE_TYPE_BOOLEAN, 1183 }, 1184 [NV_KMS_DPY_ATTRIBUTE_IMAGE_SHARPENING_DEFAULT] = { 1185 .set = NULL, 1186 .get = GetImageSharpeningDefault, 1187 .getValidValues = NULL, 1188 .type = NV_KMS_ATTRIBUTE_TYPE_INTEGER, 1189 }, 1190 [NV_KMS_DPY_ATTRIBUTE_REQUESTED_COLOR_SPACE] = { 1191 .set = SetRequestedColorSpace, 1192 .get = GetRequestedColorSpace, 1193 .getValidValues = GetRequestedColorSpaceValidValues, 1194 .type = NV_KMS_ATTRIBUTE_TYPE_INTBITS, 1195 }, 1196 [NV_KMS_DPY_ATTRIBUTE_CURRENT_COLOR_SPACE] = { 1197 .set = NULL, 1198 .get = GetCurrentColorSpace, 1199 .getValidValues = GetCurrentColorSpaceValidValues, 1200 .type = NV_KMS_ATTRIBUTE_TYPE_INTBITS, 1201 }, 1202 [NV_KMS_DPY_ATTRIBUTE_REQUESTED_COLOR_RANGE] = { 1203 .set = SetRequestedColorRange, 1204 .get = GetRequestedColorRange, 1205 .getValidValues = GetColorRangeValidValues, 1206 .type = NV_KMS_ATTRIBUTE_TYPE_INTBITS, 1207 }, 1208 [NV_KMS_DPY_ATTRIBUTE_CURRENT_COLOR_RANGE] = { 1209 .set = NULL, 1210 .get = GetCurrentColorRange, 1211 .getValidValues = GetColorRangeValidValues, 1212 .type = NV_KMS_ATTRIBUTE_TYPE_INTBITS, 1213 }, 1214 [NV_KMS_DPY_ATTRIBUTE_DIGITAL_SIGNAL] = { 1215 .set = NULL, 1216 .get = GetDigitalSignal, 1217 .getValidValues = GetDigitalSignalValidValues, 1218 .type = NV_KMS_ATTRIBUTE_TYPE_INTEGER, 1219 }, 1220 [NV_KMS_DPY_ATTRIBUTE_DIGITAL_LINK_TYPE] = { 1221 .set = NULL, 1222 .get = GetDigitalLinkType, 1223 .getValidValues = GetDigitalLinkTypeValidValues, 1224 .type = NV_KMS_ATTRIBUTE_TYPE_INTEGER, 1225 }, 1226 [NV_KMS_DPY_ATTRIBUTE_DISPLAYPORT_LINK_RATE] = { 1227 .set = NULL, 1228 .get = GetDisplayportLinkRate, 1229 .getValidValues = GetDisplayportLinkRateValidValues, 1230 .type = NV_KMS_ATTRIBUTE_TYPE_INTEGER, 1231 }, 1232 [NV_KMS_DPY_ATTRIBUTE_DISPLAYPORT_CONNECTOR_TYPE] = { 1233 .set = NULL, 1234 .get = GetDisplayportConnectorType, 1235 .getValidValues = GetDisplayportConnectorTypeValidValues, 1236 .type = NV_KMS_ATTRIBUTE_TYPE_INTEGER, 1237 }, 1238 [NV_KMS_DPY_ATTRIBUTE_DISPLAYPORT_IS_MULTISTREAM] = { 1239 .set = NULL, 1240 .get = GetDisplayportIsMultistream, 1241 .getValidValues = GetDisplayportIsMultistreamValidValues, 1242 .type = NV_KMS_ATTRIBUTE_TYPE_BOOLEAN, 1243 }, 1244 [NV_KMS_DPY_ATTRIBUTE_DISPLAYPORT_SINK_IS_AUDIO_CAPABLE] = { 1245 .set = NULL, 1246 .get = GetDisplayportSinkIsAudioCapable, 1247 .getValidValues = GetDisplayportSinkIsAudioCapableValidValues, 1248 .type = NV_KMS_ATTRIBUTE_TYPE_BOOLEAN, 1249 }, 1250 [NV_KMS_DPY_ATTRIBUTE_FRAMELOCK_DISPLAY_CONFIG] = { 1251 .set = nvSetFrameLockDisplayConfigEvo, 1252 .get = nvGetFrameLockDisplayConfigEvo, 1253 .getValidValues = nvGetFrameLockDisplayConfigValidValuesEvo, 1254 .type = NV_KMS_ATTRIBUTE_TYPE_INTBITS, 1255 }, 1256 [NV_KMS_DPY_ATTRIBUTE_RASTER_LOCK] = { 1257 .set = NULL, 1258 .get = nvQueryRasterLockEvo, 1259 .getValidValues = NULL, 1260 .type = NV_KMS_ATTRIBUTE_TYPE_BOOLEAN, 1261 }, 1262 [NV_KMS_DPY_ATTRIBUTE_UPDATE_FLIPLOCK] = { 1263 .set = nvSetFlipLockEvo, 1264 .get = nvGetFlipLockEvo, 1265 .getValidValues = NULL, 1266 .type = NV_KMS_ATTRIBUTE_TYPE_BOOLEAN, 1267 }, 1268 [NV_KMS_DPY_ATTRIBUTE_UPDATE_STEREO] = { 1269 .set = SetStereoEvo, 1270 .get = GetStereoEvo, 1271 .getValidValues = NULL, 1272 .type = NV_KMS_ATTRIBUTE_TYPE_BOOLEAN, 1273 }, 1274 [NV_KMS_DPY_ATTRIBUTE_DPMS] = { 1275 .set = nvRmSetDpmsEvo, 1276 .get = NULL, 1277 .getValidValues = NULL, 1278 .type = NV_KMS_ATTRIBUTE_TYPE_INTEGER, 1279 }, 1280 [NV_KMS_DPY_ATTRIBUTE_VRR_MIN_REFRESH_RATE] = { 1281 .set = NULL, 1282 .get = GetVrrMinRefreshRate, 1283 .getValidValues = GetVrrMinRefreshRateValidValues, 1284 .type = NV_KMS_ATTRIBUTE_TYPE_RANGE, 1285 }, 1286 }; 1287 1288 /*! 1289 * Set pParams->attribute to pParams->value on the given dpy. 1290 */ 1291 NvBool nvSetDpyAttributeEvo(NVDpyEvoPtr pDpyEvo, 1292 struct NvKmsSetDpyAttributeParams *pParams) 1293 { 1294 NvU32 index = pParams->request.attribute; 1295 1296 if (index >= ARRAY_LEN(DpyAttributesDispatchTable)) { 1297 return FALSE; 1298 } 1299 1300 if (DpyAttributesDispatchTable[index].set == NULL) { 1301 return FALSE; 1302 } 1303 1304 if (!DpyAttributesDispatchTable[index].set(pDpyEvo, 1305 pParams->request.value)) { 1306 return FALSE; 1307 } 1308 1309 if (pDpyEvo->apiHead != NV_INVALID_HEAD) { 1310 NVDispEvoRec *pDispEvo = pDpyEvo->pDispEvo; 1311 NVDispApiHeadStateEvoRec *pApiHeadState = 1312 &pDispEvo->apiHeadState[pDpyEvo->apiHead]; 1313 NVDpyEvoRec *pClonedDpyEvo; 1314 1315 /* 1316 * The current attributes state should be consistent across all cloned 1317 * dpys. 1318 */ 1319 FOR_ALL_EVO_DPYS(pClonedDpyEvo, pApiHeadState->activeDpys, pDispEvo) { 1320 nvDpyUpdateCurrentAttributes(pClonedDpyEvo); 1321 } 1322 } else { 1323 nvDpyUpdateCurrentAttributes(pDpyEvo); 1324 } 1325 1326 return TRUE; 1327 } 1328 1329 /*! 1330 * Get the value of pParams->attribute on the given dpy. 1331 */ 1332 NvBool nvGetDpyAttributeEvo(const NVDpyEvoRec *pDpyEvo, 1333 struct NvKmsGetDpyAttributeParams *pParams) 1334 { 1335 NvU32 index = pParams->request.attribute; 1336 1337 if (index >= ARRAY_LEN(DpyAttributesDispatchTable)) { 1338 return FALSE; 1339 } 1340 1341 if (DpyAttributesDispatchTable[index].get == NULL) { 1342 return FALSE; 1343 } 1344 1345 return DpyAttributesDispatchTable[index].get(pDpyEvo, 1346 &pParams->reply.value); 1347 } 1348 1349 /*! 1350 * Get the valid values of pParams->attribute on the given dpy. 1351 */ 1352 NvBool nvGetDpyAttributeValidValuesEvo( 1353 const NVDpyEvoRec *pDpyEvo, 1354 struct NvKmsGetDpyAttributeValidValuesParams *pParams) 1355 { 1356 NvU32 index = pParams->request.attribute; 1357 struct NvKmsAttributeValidValuesCommonReply *pReply = 1358 &pParams->reply.common; 1359 1360 if (index >= ARRAY_LEN(DpyAttributesDispatchTable)) { 1361 return FALSE; 1362 } 1363 1364 nvkms_memset(pReply, 0, sizeof(*pReply)); 1365 1366 pReply->readable = (DpyAttributesDispatchTable[index].get != NULL); 1367 pReply->writable = (DpyAttributesDispatchTable[index].set != NULL); 1368 1369 pReply->type = DpyAttributesDispatchTable[index].type; 1370 1371 /* 1372 * The getValidValues function provides three important things: 1373 * - If type==Range, then assigns reply::u::range. 1374 * - If type==IntBits, then assigns reply::u:bits::ints. 1375 * - If the attribute is not currently available, returns FALSE. 1376 * If the getValidValues function is NULL, assume the attribute is 1377 * available. The type must not be something that requires assigning 1378 * to reply::u. 1379 */ 1380 if (DpyAttributesDispatchTable[index].getValidValues == NULL) { 1381 nvAssert(pReply->type != NV_KMS_ATTRIBUTE_TYPE_INTBITS); 1382 nvAssert(pReply->type != NV_KMS_ATTRIBUTE_TYPE_RANGE); 1383 return TRUE; 1384 } 1385 1386 return DpyAttributesDispatchTable[index].getValidValues(pDpyEvo, pReply); 1387 } 1388