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             &params, 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             &params, 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                          &params, 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