1 /*
2  * SPDX-FileCopyrightText: Copyright (c) 2005-2023 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-device.h"
25 #include "dp/nvdp-connector-event-sink.h"
26 
27 #include "nvkms-evo.h"
28 #include "nvkms-dpy.h"
29 #include "nvkms-dpy-override.h"
30 #include "nvkms-hdmi.h"
31 #include "nvkms-rm.h"
32 #include "nvkms-rmapi.h"
33 #include "nvkms-types.h"
34 #include "nvkms-attributes.h"
35 #include "nvkms-utils.h"
36 #include "nvkms-3dvision.h"
37 
38 #include "nv_mode_timings_utils.h"
39 
40 #include "nvkms-api.h"
41 #include "nvkms-private.h"
42 
43 #include "nvos.h"
44 #include "timing/dpsdp.h"
45 
46 #include "displayport/displayport.h"
47 
48 #include <ctrl/ctrl0073/ctrl0073dfp.h> // NV0073_CTRL_DFP_FLAGS_*
49 #include <ctrl/ctrl0073/ctrl0073dp.h> // NV0073_CTRL_CMD_DP_GET_LINK_CONFIG_*
50 
51 #define TMDS_SINGLE_LINK_PCLK_MAX 165000
52 #define TMDS_DUAL_LINK_PCLK_MAX 330000
53 
54 static void DpyGetDynamicDfpProperties(
55     NVDpyEvoPtr pDpyEvo,
56     const NvBool disableACPIBrightnessHotkeys);
57 
58 static void
59 CreateParsedEdidFromNVT_TIMING(NVT_TIMING *pTimings,
60                                NvU8 bpc,
61                                NVParsedEdidEvoPtr pParsedEdid);
62 
63 static NvBool ReadEdidFromDP              (const NVDpyEvoRec *pDpyEvo,
64                                            NVEdidPtr pEdid);
65 static NvBool ReadEdidFromResman          (const NVDpyEvoRec *pDpyEvo,
66                                            NVEdidPtr pEdid,
67                                            NvKmsEdidReadMode readMode);
68 static NvBool ValidateEdid                (const NVDpyEvoRec *pDpyEvo,
69                                            NVEdidPtr pEdid,
70                                            NVEvoInfoStringPtr pInfoString,
71                                            const NvBool ignoreEdidChecksum);
72 static void LogEdid                       (NVDpyEvoPtr pDpyEvo,
73                                            NVEvoInfoStringPtr pInfoString);
74 static void ClearEdid                     (NVDpyEvoPtr pDpyEvo);
75 static void ClearCustomEdid               (const NVDpyEvoRec *pDpyEvo);
76 static void WriteEdidToResman             (const NVDpyEvoRec *pDpyEvo,
77                                            const NVEdidRec *pEdid);
78 static void PatchAndParseEdid             (const NVDpyEvoRec *pDpyEvo,
79                                            NVEdidPtr pEdid,
80                                            NVParsedEdidEvoPtr,
81                                            NVEvoInfoStringPtr pInfoString);
82 static void ReadAndApplyEdidEvo           (NVDpyEvoPtr pDpyEvo,
83                                            struct NvKmsQueryDpyDynamicDataParams *pParams);
84 static NvBool GetFixedModeTimings         (NVDpyEvoPtr pDpyEvo, struct NvKmsSuperframeInfo *pSuperframeInfo);
85 static NvBool ReadDSITimingsFromResman    (const NVDpyEvoRec *pDpyEvo,
86                                            NVT_TIMING *pTimings,
87                                            NvU8 *pBpc);
88 static void AssignDpyEvoName              (NVDpyEvoPtr pDpyEvo);
89 
90 static NvBool IsConnectorTMDS             (NVConnectorEvoPtr);
91 
92 
93 static void DpyDisconnectEvo(NVDpyEvoPtr pDpyEvo)
94 {
95     NVDispEvoPtr pDispEvo = pDpyEvo->pDispEvo;
96 
97     pDispEvo->connectedDisplays =
98         nvDpyIdListMinusDpyId(pDispEvo->connectedDisplays, pDpyEvo->id);
99 
100     ClearEdid(pDpyEvo);
101 }
102 
103 static NvBool DpyConnectEvo(
104     NVDpyEvoPtr pDpyEvo,
105     struct NvKmsQueryDpyDynamicDataParams *pParams)
106 {
107     NVDispEvoPtr pDispEvo = pDpyEvo->pDispEvo;
108 
109     pDispEvo->connectedDisplays =
110         nvAddDpyIdToDpyIdList(pDpyEvo->id, pDispEvo->connectedDisplays);
111 
112     DpyGetDynamicDfpProperties(pDpyEvo, pParams->request.disableACPIBrightnessHotkeys);
113     nvDPGetDpyGUID(pDpyEvo);
114 
115     if ((pDpyEvo->pConnectorEvo->signalFormat == NVKMS_CONNECTOR_SIGNAL_FORMAT_DSI) ||
116         nvConnectorIsDPSerializer(pDpyEvo->pConnectorEvo)) {
117         if (!GetFixedModeTimings(pDpyEvo, &pParams->reply.superframeInfo)) {
118             return FALSE;
119         }
120     } else {
121         ReadAndApplyEdidEvo(pDpyEvo, pParams);
122     }
123 
124     nvUpdateInfoFrames(pDpyEvo);
125 
126     return TRUE;
127 }
128 
129 /*
130  * DpyAssignColorSpaceCaps() - parse both the CEA-861 extension block and
131  * the EDID 1.4 block to determine YCbCr422/444 capability.
132  */
133 static void DpyAssignColorSpaceCaps(NVDpyEvoPtr pDpyEvo,
134                                     NVEvoInfoStringPtr pInfoString)
135 {
136     NvBool ycbr422_cap = FALSE;
137     NvBool ycbr444_cap = FALSE;
138     const NVParsedEdidEvoRec *pParsedEdid = &pDpyEvo->parsedEdid;
139 
140     /* check for edid YCbCr422/YCbCr444 capability */
141     if (pParsedEdid->valid) {
142         NvBool haveCEA861Block =
143             (pParsedEdid->info.ext861.revision != NVT_CEA861_REV_NONE);
144         if (haveCEA861Block) {
145             ycbr422_cap = !!(pParsedEdid->info.ext861.basic_caps &
146                              NVT_CEA861_CAP_YCbCr_422);
147             ycbr444_cap = !!(pParsedEdid->info.ext861.basic_caps &
148                              NVT_CEA861_CAP_YCbCr_444);
149         }
150         /* check EDID 1.4 base block */
151         if (pParsedEdid->info.version == 0x104 &&
152             pParsedEdid->info.input.isDigital) {
153             NvBool edid14_ycbr422 =
154                 pParsedEdid->info.u.feature_ver_1_4_digital.support_ycrcb_422;
155             NvBool edid14_ycbr444 =
156                 pParsedEdid->info.u.feature_ver_1_4_digital.support_ycrcb_444;
157             if (haveCEA861Block && ycbr422_cap != edid14_ycbr422) {
158                 nvEvoLogInfoString(pInfoString,
159                        "%s EDID inconsistency: the EDID 1.4 base block %s "
160                        "YCbCr 4:2:2 support, but the CEA-861 extension block "
161                        "%s. Assuming YCbCr 4:2:2 is supported.",
162                        pDpyEvo->name,
163                        edid14_ycbr422 ? "indicates" : "does not indicate",
164                        ycbr422_cap ? "does" : "does not");
165             }
166             if (edid14_ycbr422) {
167                 ycbr422_cap = TRUE;
168             }
169             if (haveCEA861Block && ycbr444_cap != edid14_ycbr444) {
170                 nvEvoLogInfoString(pInfoString,
171                        "%s EDID inconsistency: the EDID 1.4 base block %s "
172                        "YCbCr 4:4:4 support, but the CEA-861 extension block "
173                        "%s. Assuming YCbCr 4:4:4 is supported.",
174                        pDpyEvo->name,
175                        edid14_ycbr444 ? "indicates" : "does not indicate",
176                        ycbr444_cap ? "does" : "does not");
177             }
178             if (edid14_ycbr444) {
179                 ycbr444_cap = TRUE;
180             }
181         }
182     }
183     pDpyEvo->colorSpaceCaps.ycbcr422Capable = ycbr422_cap;
184     pDpyEvo->colorSpaceCaps.ycbcr444Capable = ycbr444_cap;
185 }
186 
187 
188 
189 static NvBool GetEdidOverride(
190     const struct NvKmsQueryDpyDynamicDataRequest *pRequest,
191     NVEdidRec *pEdid)
192 {
193     if ((pRequest == NULL) ||
194         !pRequest->overrideEdid ||
195         (pRequest->edid.bufferSize == 0) ||
196         (pRequest->edid.bufferSize > sizeof(pRequest->edid.buffer))) {
197         return FALSE;
198     }
199 
200     pEdid->buffer = nvAlloc(pRequest->edid.bufferSize);
201 
202     if (pEdid->buffer == NULL) {
203         return FALSE;
204     }
205 
206     nvkms_memcpy(pEdid->buffer, pRequest->edid.buffer, pRequest->edid.bufferSize);
207 
208     pEdid->length = pRequest->edid.bufferSize;
209 
210     return TRUE;
211 }
212 
213 /*!
214  * Query resman for the EDID for the pDpyEvo, then parse the EDID into usable
215  * data.  Do not modify the pDpyEvoRec.
216  */
217 
218 NvBool nvDpyReadAndParseEdidEvo(
219     const NVDpyEvoRec *pDpyEvo,
220     const struct NvKmsQueryDpyDynamicDataRequest *pRequest,
221     NvKmsEdidReadMode readMode,
222     NVEdidRec *pEdid,
223     NVParsedEdidEvoPtr *ppParsedEdid,
224     NVEvoInfoStringPtr pInfoString)
225 {
226     NvBool ignoreEdid = FALSE;
227     NvBool ignoreEdidChecksum = FALSE;
228 
229     if (pRequest != NULL) {
230         ignoreEdid = pRequest->ignoreEdid;
231         ignoreEdidChecksum = pRequest->ignoreEdidChecksum;
232     }
233 
234     nvkms_memset(pEdid, 0, sizeof(*pEdid));
235 
236     /* Just return an empty EDID if requested. */
237     if (ignoreEdid) {
238         return TRUE;
239     }
240 
241     /* Load any custom EDID, (or see if DP lib has EDID) */
242     ClearCustomEdid(pDpyEvo);
243 
244     if ((pRequest && GetEdidOverride(pRequest, pEdid)) ||
245         ReadEdidFromDP(pDpyEvo, pEdid)) {
246         /* XXX [VSM] Write, clear and re-read the EDID to/from RM here to make
247          * sure RM and X agree on the final EDID bits.  Once RM no longer
248          * parses the EDID, we can avoid doing this for DP devices.
249          *
250          * If it's a DisplayPort 1.2 multistream device then don't bother trying
251          * to ping-pong the EDID through RM.
252          */
253         if (nvDpyEvoIsDPMST(pDpyEvo)) {
254             goto validateEdid;
255         }
256 
257         WriteEdidToResman(pDpyEvo, pEdid);
258 
259         nvFree(pEdid->buffer);
260         pEdid->buffer = NULL;
261         pEdid->length = 0;
262     }
263 
264     if (!ReadEdidFromResman(pDpyEvo, pEdid, readMode)) {
265         goto fail;
266     }
267 
268 validateEdid:
269     /* Validate the EDID */
270     if (!ValidateEdid(pDpyEvo, pEdid, pInfoString, ignoreEdidChecksum)) {
271         goto fail;
272     }
273 
274     *ppParsedEdid = nvCalloc(1, sizeof(**ppParsedEdid));
275     if (*ppParsedEdid == NULL) {
276         goto fail;
277     }
278     /* Parse the EDID.  Note this may *change* the EDID bytes. */
279     PatchAndParseEdid(pDpyEvo, pEdid, *ppParsedEdid, pInfoString);
280 
281     return TRUE;
282 
283 fail:
284 
285     /* We failed to read a valid EDID.  Free any EDID buffer allocated above. */
286     nvFree(pEdid->buffer);
287     pEdid->buffer = NULL;
288     pEdid->length = 0;
289 
290     return FALSE;
291 }
292 
293 static void AssignIsVrHmd(NVDpyEvoRec *pDpyEvo)
294 {
295     NV0073_CTRL_SPECIFIC_IS_DIRECTMODE_DISPLAY_PARAMS params = { };
296     NVDispEvoPtr pDispEvo = pDpyEvo->pDispEvo;
297     NVDevEvoPtr pDevEvo = pDispEvo->pDevEvo;
298     NvU32 ret;
299 
300     pDpyEvo->isVrHmd = FALSE;
301 
302     if (!pDpyEvo->parsedEdid.valid) {
303         return;
304     }
305 
306     params.manufacturerID = pDpyEvo->parsedEdid.info.manuf_id;
307     params.productID = pDpyEvo->parsedEdid.info.product_id;
308 
309     ret = nvRmApiControl(nvEvoGlobal.clientHandle,
310                          pDevEvo->displayCommonHandle,
311                          NV0073_CTRL_CMD_SPECIFIC_IS_DIRECTMODE_DISPLAY,
312                          &params, sizeof(params));
313 
314     if (ret != NVOS_STATUS_SUCCESS) {
315         nvEvoLogDisp(pDispEvo, EVO_LOG_ERROR,
316                      "Failed to query VR headset for %s", pDpyEvo->name);
317         return;
318     }
319 
320     /*
321      * bIsDirectmode indicates any monitor that by default shouldn't be part of
322      * a desktop (VR headset, touch panel, etc).  But, close enough for our
323      * usage of isVrHmd.
324      */
325     pDpyEvo->isVrHmd = params.bIsDirectmode;
326 }
327 
328 static NvBool EdidHasChanged(
329     const NVDpyEvoRec *pDpyEvo,
330     const NVEdidRec *pEdid,
331     const NVParsedEdidEvoRec *pParsedEdid)
332 {
333     /* Compare EDID bytes */
334     if (pEdid->length != pDpyEvo->edid.length ||
335         nvkms_memcmp(pEdid->buffer, pDpyEvo->edid.buffer, pEdid->length) != 0) {
336         return TRUE;
337     }
338 
339     /* Compare parsed data */
340     if (pParsedEdid != NULL) {
341         if (nvkms_memcmp(pParsedEdid, &pDpyEvo->parsedEdid,
342                          sizeof(*pParsedEdid)) != 0) {
343             return TRUE;
344         }
345     } else if (pDpyEvo->parsedEdid.valid) {
346         return TRUE;
347     }
348 
349     return FALSE;
350 }
351 
352 static void ApplyNewEdid(
353     NVDpyEvoPtr pDpyEvo,
354     const NVEdidRec *pEdid,
355     const NVParsedEdidEvoRec *pParsedEdid,
356     NVEvoInfoStringPtr pInfoString)
357 {
358     if (pDpyEvo->edid.buffer != NULL) {
359         nvFree(pDpyEvo->edid.buffer);
360     }
361     pDpyEvo->edid.buffer = pEdid->buffer;
362     pDpyEvo->edid.length = pEdid->length;
363 
364     if (pParsedEdid != NULL) {
365         nvkms_memcpy(&pDpyEvo->parsedEdid, pParsedEdid,
366                      sizeof(pDpyEvo->parsedEdid));
367     } else {
368         nvkms_memset(&pDpyEvo->parsedEdid, 0, sizeof(pDpyEvo->parsedEdid));
369     }
370 
371     /*
372      * Regenerate the dpy's name, because the parsed EDID monitorName
373      * may have changed.
374      */
375     AssignDpyEvoName(pDpyEvo);
376 
377     /* Write information about the parsed EDID to the infoString. */
378     LogEdid(pDpyEvo, pInfoString);
379 
380     if (pDpyEvo->parsedEdid.valid) {
381         /*
382          * check 3D Vision capability
383          */
384         nvDpyCheck3DVisionCapsEvo(pDpyEvo);
385 
386         /*
387          * Check HDMI VRR capability
388          */
389         nvDpyUpdateHdmiVRRCaps(pDpyEvo);
390     }
391 
392     if (pDpyEvo->pConnectorEvo->legacyType ==
393         NV0073_CTRL_SPECIFIC_DISPLAY_TYPE_DFP) {
394         DpyAssignColorSpaceCaps(pDpyEvo, pInfoString);
395     }
396 
397     nvUpdateHdmiCaps(pDpyEvo);
398 
399     nvDpyProbeMaxPixelClock(pDpyEvo);
400 
401     AssignIsVrHmd(pDpyEvo);
402 }
403 
404 /*
405  * ReadDSITimingsFromResman() - Obtains modetimings for a DSI connector,
406  *                              passing it into pTimings
407  */
408 static NvBool ReadDSITimingsFromResman(
409     const NVDpyEvoRec *pDpyEvo,
410     NVT_TIMING *pTimings,
411     NvU8 *pBpc)
412 {
413     NvU32 ret;
414     NV0073_CTRL_CMD_DFP_GET_DSI_MODE_TIMING_PARAMS dsiModeTimingParams = { 0 };
415 
416     NVDispEvoPtr pDispEvo = pDpyEvo->pDispEvo;
417     NVDevEvoPtr pDevEvo = pDispEvo->pDevEvo;
418     dsiModeTimingParams.subDeviceInstance = pDispEvo->displayOwner;
419 
420     /*
421      * Currently displayId must be hardcoded to 0 to receive timings from RM.
422      * Once the corresponding DCB support is added for DSI, this hack will be
423      * removed and NVKMS will use the actual displayId instead.
424      */
425     dsiModeTimingParams.displayId = 0;
426 
427     ret = nvRmApiControl(nvEvoGlobal.clientHandle,
428                          pDevEvo->displayCommonHandle,
429                          NV0073_CTRL_CMD_DFP_GET_DSI_MODE_TIMING,
430                          &dsiModeTimingParams, sizeof(dsiModeTimingParams));
431 
432     if (ret != NVOS_STATUS_SUCCESS) {
433         nvEvoLogDisp(pDispEvo, EVO_LOG_WARN,
434                      "Unable to read DSI mode timings for display device %s",
435                      pDpyEvo->name);
436         return FALSE;
437     }
438 
439     // Converts refresh (Hz) into appropriate units for rr1k (units of 0.001Hz)
440     pTimings->etc.rrx1k = dsiModeTimingParams.refresh * 1000;
441     pTimings->HVisible = dsiModeTimingParams.hActive;
442     pTimings->HFrontPorch = dsiModeTimingParams.hFrontPorch;
443     pTimings->HSyncWidth = dsiModeTimingParams.hSyncWidth;
444     pTimings->HTotal = dsiModeTimingParams.hActive +
445                        dsiModeTimingParams.hFrontPorch +
446                        dsiModeTimingParams.hSyncWidth +
447                        dsiModeTimingParams.hBackPorch;
448 
449     pTimings->VVisible = dsiModeTimingParams.vActive;
450     pTimings->VFrontPorch = dsiModeTimingParams.vFrontPorch;
451     pTimings->VSyncWidth = dsiModeTimingParams.vSyncWidth;
452     pTimings->VTotal = dsiModeTimingParams.vActive +
453                        dsiModeTimingParams.vFrontPorch +
454                        dsiModeTimingParams.vSyncWidth +
455                        dsiModeTimingParams.vBackPorch;
456 
457     pTimings->pclk = HzToKHz(dsiModeTimingParams.pclkHz) / 10;
458 
459     // DSI only supports RGB444
460     *pBpc = dsiModeTimingParams.bpp / 3;
461 
462     return TRUE;
463 }
464 
465 static NvBool ParseSuperframeInfo(
466     NVDpyEvoRec *pDpyEvo,
467     const NV0073_CTRL_DFP_GET_FIXED_MODE_TIMING_PARAMS *pParams,
468     struct NvKmsSuperframeInfo *pSuperframeInfo)
469 {
470     NvU8 i;
471 
472     if (pParams->superframeInfo.numViews == 0) {
473         return TRUE;
474     }
475 
476     // Currently, we support only dual view superframe.
477     if (pParams->superframeInfo.numViews != 2) {
478         nvEvoLog(EVO_LOG_ERROR, "Invalid number of superframe views");
479         return FALSE;
480     }
481 
482     // Currently, we support only packed symmetrical side-by-side superframe.
483     if ((pParams->superframeInfo.view[0].width * pParams->superframeInfo.numViews) !=
484         pParams->hActive) {
485         nvEvoLog(EVO_LOG_ERROR, "The width of Superframe view[0] is invalid");
486         return FALSE;
487     }
488 
489     if (pParams->superframeInfo.view[0].height != pParams->vActive) {
490         nvEvoLog(EVO_LOG_ERROR, "The height of Superframe view[0] is invalid");
491         return FALSE;
492     }
493 
494     pSuperframeInfo->numViews = 0;
495 
496     for (i = 0; i < pParams->superframeInfo.numViews; i++) {
497         // All superframe views must not have horizontal spacing in between them.
498         if ((pParams->superframeInfo.view[0].width * i) !=
499             pParams->superframeInfo.view[i].x) {
500             nvEvoLog(EVO_LOG_ERROR, "The x offset of Superframe view[%u] is invalid", i);
501             goto fail;
502         }
503 
504         // All superframe views must have y offset as 0.
505         if (pParams->superframeInfo.view[i].y != 0) {
506             nvEvoLog(EVO_LOG_ERROR, "The y offset of Superframe view[%u] is invalid", i);
507             goto fail;
508         }
509 
510         // All superframe views must have the same width.
511         if (pParams->superframeInfo.view[0].width !=
512             pParams->superframeInfo.view[i].width) {
513             nvEvoLog(EVO_LOG_ERROR, "The width of Superframe view[%u] is invalid", i);
514             goto fail;
515         }
516 
517         // All superframe views must have the same height.
518         if (pParams->superframeInfo.view[0].height !=
519             pParams->superframeInfo.view[i].height) {
520             nvEvoLog(EVO_LOG_ERROR, "The height of Superframe view[%u] is invalid", i);
521             goto fail;
522         }
523 
524         pSuperframeInfo->view[i].x = pParams->superframeInfo.view[i].x;
525         pSuperframeInfo->view[i].width = pParams->superframeInfo.view[i].width;
526         pSuperframeInfo->view[i].y = pParams->superframeInfo.view[i].y;
527         pSuperframeInfo->view[i].height = pParams->superframeInfo.view[i].height;
528         pSuperframeInfo->numViews++;
529     }
530 
531     return TRUE;
532 
533 fail:
534     nvkms_memset(pSuperframeInfo, 0, sizeof(*pSuperframeInfo));
535     return FALSE;
536 }
537 
538 static NvBool ReadDPSerializerTimings(
539     NVDpyEvoRec *pDpyEvo,
540     NVT_TIMING *pTimings,
541     NvU8 *pBpc,
542     struct NvKmsSuperframeInfo *pSuperframeInfo)
543 {
544     NV0073_CTRL_DFP_GET_FIXED_MODE_TIMING_PARAMS timingParams = { };
545     NVDispEvoPtr pDispEvo = pDpyEvo->pDispEvo;
546     NVDevEvoPtr pDevEvo = pDispEvo->pDevEvo;
547     NvU32 ret;
548 
549     timingParams.subDeviceInstance = pDispEvo->displayOwner;
550     timingParams.displayId = nvDpyIdToNvU32(pDpyEvo->pConnectorEvo->displayId);
551     timingParams.stream = pDpyEvo->dp.serializerStreamIndex;
552 
553     ret = nvRmApiControl(nvEvoGlobal.clientHandle,
554                          pDevEvo->displayCommonHandle,
555                          NV0073_CTRL_CMD_DFP_GET_FIXED_MODE_TIMING,
556                          &timingParams, sizeof(timingParams));
557     if (ret != NVOS_STATUS_SUCCESS) {
558         nvEvoLogDisp(pDispEvo, EVO_LOG_WARN,
559                      "Unable to read fixed mode timings for display device %s",
560                      pDpyEvo->name);
561         return FALSE;
562     }
563 
564     if (!timingParams.valid) {
565         nvEvoLogDisp(pDispEvo, EVO_LOG_WARN,
566                      "Fixed mode timings are invalid for display device %s",
567                      pDpyEvo->name);
568         return FALSE;
569     }
570 
571     if (!ParseSuperframeInfo(pDpyEvo, &timingParams, pSuperframeInfo)) {
572         return FALSE;
573     }
574 
575     nvkms_memset(pTimings, 0, sizeof(NVT_TIMING));
576 
577     pTimings->HVisible = timingParams.hActive;
578     pTimings->HFrontPorch = timingParams.hFrontPorch;
579     pTimings->HSyncWidth = timingParams.hSyncWidth;
580     pTimings->HTotal = timingParams.hActive + timingParams.hFrontPorch +
581                        timingParams.hSyncWidth + timingParams.hBackPorch;
582 
583     pTimings->VVisible = timingParams.vActive;
584     pTimings->VFrontPorch = timingParams.vFrontPorch;
585     pTimings->VSyncWidth = timingParams.vSyncWidth;
586     pTimings->VTotal = timingParams.vActive + timingParams.vFrontPorch +
587                        timingParams.vSyncWidth + timingParams.vBackPorch;
588 
589     pTimings->pclk = timingParams.pclkKHz / 10;
590     pTimings->etc.rrx1k = timingParams.rrx1k;
591 
592     *pBpc = 0;
593 
594     return TRUE;
595 }
596 
597 static NvBool GetFixedModeTimings(
598     NVDpyEvoPtr pDpyEvo,
599     struct NvKmsSuperframeInfo *pSuperframeInfo)
600 {
601     NVT_TIMING timings = { };
602     NvBool ret = FALSE;
603     NvU8 bpc;
604 
605     if (pDpyEvo->pConnectorEvo->signalFormat == NVKMS_CONNECTOR_SIGNAL_FORMAT_DSI) {
606         ret = ReadDSITimingsFromResman(pDpyEvo, &timings, &bpc);
607     } else if (nvConnectorIsDPSerializer(pDpyEvo->pConnectorEvo)) {
608         ret = ReadDPSerializerTimings(pDpyEvo, &timings, &bpc,
609                                       pSuperframeInfo);
610     }
611 
612     if (!ret) {
613         return ret;
614     }
615 
616     CreateParsedEdidFromNVT_TIMING(&timings, bpc, &pDpyEvo->parsedEdid);
617 
618     AssignDpyEvoName(pDpyEvo);
619     nvDpyProbeMaxPixelClock(pDpyEvo);
620 
621     return TRUE;
622 }
623 
624 static void ReadAndApplyEdidEvo(
625     NVDpyEvoPtr pDpyEvo,
626     struct NvKmsQueryDpyDynamicDataParams *pParams)
627 {
628     const struct NvKmsQueryDpyDynamicDataRequest *pRequest = NULL;
629     NVEdidRec edid = {NULL, 0};
630     NVParsedEdidEvoPtr pParsedEdid = NULL;
631     NVEvoInfoStringRec infoString;
632     NvBool readSuccess;
633 
634     if (pParams != NULL) {
635         nvInitInfoString(&infoString, pParams->reply.edid.infoString,
636                          sizeof(pParams->reply.edid.infoString));
637         pRequest = &pParams->request;
638     } else {
639         nvInitInfoString(&infoString, NULL, 0);
640     }
641 
642     readSuccess = nvDpyReadAndParseEdidEvo(pDpyEvo, pRequest,
643                                            NVKMS_EDID_READ_MODE_DEFAULT,
644                                            &edid, &pParsedEdid, &infoString);
645 
646     if (pParams != NULL) {
647         pParams->reply.edid.valid = readSuccess;
648     }
649 
650     if (EdidHasChanged(pDpyEvo, &edid, pParsedEdid)) {
651         /*
652          * Do not plumb pRequest into ApplyNewEdid().  This helps ensure that
653          * its operation is purely a function of the EDID and parsed EDID data,
654          * which means that if we get into this function again with the same
655          * EDID and parsed EDID data, we can safely skip ApplyNewEdid() without
656          * worrying that this request has different parameters (like CustomEdid
657          * or mode validation overrides).
658          */
659         ApplyNewEdid(pDpyEvo, &edid, pParsedEdid, &infoString);
660     } else {
661         nvFree(edid.buffer);
662     }
663     nvFree(pParsedEdid);
664 }
665 
666 
667 /*!
668  * Get the maximum allowed pixel clock for pDpyEvo.
669  *
670  * This depends on the following conditions:
671  *
672  * - The RM's returned value is sufficient for non-TMDS connectors
673  * - For HDMI, the SOR capabilities exceed the RM's returned value to allow
674  *   for HDMI 1.4 modes that exceed 165MHz on a single link, or
675  *   for HDMI 2.1 modes if the source and sink is capable of FRL
676  * - For DVI, the user is allowed to set an option to exceed the 165MHz
677  *   per-TMDS limit if the SOR capabilities allow it
678  * - Contrary to the above, passive DP->DVI and DP->HDMI dongles have their
679  *   own limits
680  */
681 void nvDpyProbeMaxPixelClock(NVDpyEvoPtr pDpyEvo)
682 {
683     NvU32 ret;
684     NVDispEvoPtr pDispEvo = pDpyEvo->pDispEvo;
685     NVDevEvoPtr pDevEvo = pDispEvo->pDevEvo;
686     NVConnectorEvoPtr pConnectorEvo = pDpyEvo->pConnectorEvo;
687     NvU32 displayOwner = pDispEvo->displayOwner;
688     NVEvoPassiveDpDongleType passiveDpDongleType;
689     NV0073_CTRL_SPECIFIC_GET_PCLK_LIMIT_PARAMS params = { 0 };
690     NvU32 passiveDpDongleMaxPclkKHz;
691 
692     /* First, get the RM-reported value. */
693 
694     params.displayId = nvDpyIdToNvU32(pDpyEvo->pConnectorEvo->displayId);
695     params.subDeviceInstance = pDispEvo->displayOwner;
696 
697     ret = nvRmApiControl(nvEvoGlobal.clientHandle,
698                          pDevEvo->displayCommonHandle,
699                          NV0073_CTRL_CMD_SPECIFIC_GET_PCLK_LIMIT,
700                          &params, sizeof(params));
701 
702     if (ret != NVOS_STATUS_SUCCESS) {
703         nvEvoLogDisp(pDispEvo, EVO_LOG_ERROR,
704                      "Failure reading maximum pixel clock value "
705                      "for display device %s.", pDpyEvo->name);
706         pDpyEvo->maxPixelClockKHz = 100000;
707         pDpyEvo->maxSingleLinkPixelClockKHz = pDpyEvo->maxPixelClockKHz;
708         return;
709     }
710 
711     pDpyEvo->maxPixelClockKHz = params.orPclkLimit;
712     pDpyEvo->maxSingleLinkPixelClockKHz = pDpyEvo->maxPixelClockKHz;
713 
714     /*
715      * The RM's returned max pclk value is sufficient for non-TMDS
716      * connectors
717      */
718     if (!IsConnectorTMDS(pConnectorEvo)) {
719         return;
720     }
721 
722     /*
723      * The RM returns a 165MHz max pclk for single link TMDS and 330MHz
724      * max pclk for dual link TMDS.  We can exceed that in the
725      * following cases:
726      *
727      * - HDMI 1.4a 4Kx2K and 1080p60hz frame packed stereo modes
728      *   require a 297MHz single TMDS link pixel clock, and HDMI 2.0
729      *   allows an even higher pixel clock.
730      * - While the DVI spec mandates a limit of 165MHz per TMDS link,
731      *   since certain GPUs and certain displays support DVI
732      *   connections at higher pixel clocks, we allow users to
733      *   override this limit to allow validation of higher maximum
734      *   pixel clocks over DVI.
735      */
736     if (pDevEvo->gpus != NULL) {
737 
738         NVEvoSorCaps *sorCaps = pDevEvo->gpus[displayOwner].capabilities.sor;
739         NvU32 orIndex = pConnectorEvo->or.primary;
740 
741         if (NV0073_CTRL_SYSTEM_GET_CAP(pDevEvo->commonCapsBits,
742                 NV0073_CTRL_SYSTEM_CAPS_CROSS_BAR_SUPPORTED)) {
743             /*
744              * With the SOR crossbar, pConnectorEvo->or.mask is unknown,
745              * and may change at modeset time.  Use the caps of SOR 0
746              * for validation.
747              */
748             orIndex = 0;
749         }
750 
751         if (nvDpyIsHdmiEvo(pDpyEvo)) {
752             pDpyEvo->maxPixelClockKHz =
753                 pDpyEvo->maxSingleLinkPixelClockKHz =
754                 sorCaps[orIndex].maxTMDSClkKHz;
755 
756             nvkms_memset(&pDpyEvo->hdmi.srcCaps, 0, sizeof(pDpyEvo->hdmi.srcCaps));
757             nvkms_memset(&pDpyEvo->hdmi.sinkCaps, 0, sizeof(pDpyEvo->hdmi.sinkCaps));
758 
759             if (nvHdmiDpySupportsFrl(pDpyEvo)) {
760                 /*
761                  * An SOR needs to be assigned temporarily to do FRL training.
762                  *
763                  * Since the only other SORs in use at the moment (if any) are
764                  * those driving heads, we don't need to exclude RM from
765                  * selecting any SOR, so an sorExcludeMask of 0 is appropriate.
766                  */
767                 if (nvAssignSOREvo(pDispEvo,
768                                    nvDpyIdToNvU32(pConnectorEvo->displayId),
769                                    FALSE /* b2Heads1Or */,
770                                    0 /* sorExcludeMask */) &&
771                     nvHdmiFrlAssessLink(pDpyEvo)) {
772                     /*
773                      * Note that although we "assessed" the link above, the
774                      * maximum pixel clock set here doesn't take that into
775                      * account -- it's the maximum the GPU hardware is capable
776                      * of on the most capable link, mostly for reporting
777                      * purposes.
778                      *
779                      * The calculation for if a given mode can fit in the
780                      * assessed FRL configuration is complex and depends on
781                      * things like the amount of blanking, rather than a simple
782                      * pclk cutoff.  So, we query the hdmi library when
783                      * validating each individual mode, when we know actual
784                      * timings.
785                      */
786 
787                     /*
788                      * This comes from the Windows display driver: (4 lanes *
789                      * 12Gb per lane * FRL encoding i.e 16/18) / 1K
790                      */
791                     pDpyEvo->maxPixelClockKHz =
792                         ((4 * 12 * 1000 * 1000 * 16) / 18);
793                 }
794             }
795         } else {
796             /*
797              * Connector and SOR both must be capable to drive dual-TMDS
798              * resolutions.
799              */
800             NvBool bDualTMDS = sorCaps[orIndex].dualTMDS &&
801                                FLD_TEST_DRF(0073, _CTRL_DFP_FLAGS, _LINK, _DUAL,
802                                pDpyEvo->pConnectorEvo->dfpInfo);
803 
804             pDpyEvo->maxPixelClockKHz = (bDualTMDS ?
805                                          TMDS_DUAL_LINK_PCLK_MAX :
806                                          TMDS_SINGLE_LINK_PCLK_MAX);
807 
808             pDpyEvo->maxSingleLinkPixelClockKHz = TMDS_SINGLE_LINK_PCLK_MAX;
809 
810             if (pDpyEvo->allowDVISpecPClkOverride) {
811                 pDpyEvo->maxPixelClockKHz = sorCaps[orIndex].maxTMDSClkKHz *
812                     (bDualTMDS ? 2 : 1);
813                 pDpyEvo->maxSingleLinkPixelClockKHz =
814                     sorCaps[orIndex].maxTMDSClkKHz;
815             }
816         }
817     }
818 
819     /*
820      * Passive DP->DVI and DP->HDMI dongles may have a limit more
821      * restrictive than the one described above.  Check whether one of
822      * these dongles is in use, and override the limit accordingly.
823      */
824     passiveDpDongleType =
825         nvDpyGetPassiveDpDongleType(pDpyEvo, &passiveDpDongleMaxPclkKHz);
826 
827     if (passiveDpDongleType != NV_EVO_PASSIVE_DP_DONGLE_UNUSED) {
828         pDpyEvo->maxPixelClockKHz = NV_MIN(passiveDpDongleMaxPclkKHz,
829                                            pDpyEvo->maxPixelClockKHz);
830         pDpyEvo->maxSingleLinkPixelClockKHz = pDpyEvo->maxPixelClockKHz;
831     }
832 }
833 
834 static void DpyGetDynamicDfpProperties(
835     NVDpyEvoPtr pDpyEvo,
836     const NvBool disableACPIBrightnessHotkeys)
837 {
838     if (disableACPIBrightnessHotkeys) {
839         return;
840     }
841     if (!disableACPIBrightnessHotkeys) {
842         struct NvKmsGetDpyAttributeParams params;
843         nvkms_memset(&params, 0, sizeof(params));
844         params.request.attribute = NV_KMS_DPY_ATTRIBUTE_BACKLIGHT_BRIGHTNESS;
845 
846         pDpyEvo->hasBacklightBrightness =
847             nvGetDpyAttributeEvo(pDpyEvo, &params);
848     }
849 }
850 /*
851  * DpyGetDfpProperties() - get DFP properties: reduced blanking flags
852  * and general DFP flags
853  */
854 
855 static void DpyGetStaticDfpProperties(NVDpyEvoPtr pDpyEvo)
856 {
857     NVConnectorEvoPtr pConnectorEvo = pDpyEvo->pConnectorEvo;
858 
859     if (pConnectorEvo->legacyType != NV0073_CTRL_SPECIFIC_DISPLAY_TYPE_DFP) {
860         return;
861     }
862 
863     if (nvDpyEvoIsDPMST(pDpyEvo)) {
864         // None of this stuff can be queried directly for dynamic DP MST
865         // displays.
866         // XXX DP MST: Should we fill in these fields somehow anyway?
867         return;
868     }
869 
870     pDpyEvo->internal = FALSE;
871     pDpyEvo->hdmiCapable = FALSE;
872 
873     if (pConnectorEvo->dfpInfo == 0x0) {
874         return;
875     }
876     /* Check if the connected DFP is HDMI capable */
877 
878     if (FLD_TEST_DRF(0073, _CTRL_DFP_FLAGS, _HDMI_CAPABLE, _TRUE,
879                      pConnectorEvo->dfpInfo)) {
880         pDpyEvo->hdmiCapable = TRUE;
881     }
882 
883     pDpyEvo->internal = nvConnectorIsInternal(pDpyEvo->pConnectorEvo);
884 }
885 
886 /*!
887  * Return true if the connector is single or dual link TMDS (not CRT, not DP).
888  */
889 static NvBool IsConnectorTMDS(NVConnectorEvoPtr pConnectorEvo)
890 {
891     NvU32 protocol = pConnectorEvo->or.protocol;
892     return ((pConnectorEvo->or.type == NV0073_CTRL_SPECIFIC_OR_TYPE_SOR) &&
893             ((protocol == NV0073_CTRL_SPECIFIC_OR_PROTOCOL_SOR_SINGLE_TMDS_A) ||
894              (protocol == NV0073_CTRL_SPECIFIC_OR_PROTOCOL_SOR_SINGLE_TMDS_B) ||
895              (protocol == NV0073_CTRL_SPECIFIC_OR_PROTOCOL_SOR_DUAL_TMDS)));
896 }
897 
898 /*!
899  * Query RM for the passive Displayport dongle type; this can influence
900  * the maximum pixel clock allowed on that display.
901  */
902 NVEvoPassiveDpDongleType
903 nvDpyGetPassiveDpDongleType(const NVDpyEvoRec *pDpyEvo,
904                             NvU32 *passiveDpDongleMaxPclkKHz)
905 {
906     NV0073_CTRL_DFP_GET_DISPLAYPORT_DONGLE_INFO_PARAMS params = { 0 };
907     NvU32 ret;
908     NVConnectorEvoPtr pConnectorEvo = pDpyEvo->pConnectorEvo;
909     NVDispEvoPtr pDispEvo = pDpyEvo->pDispEvo;
910     NVDevEvoPtr pDevEvo = pDispEvo->pDevEvo;
911 
912     NVEvoPassiveDpDongleType passiveDpDongleType =
913         NV_EVO_PASSIVE_DP_DONGLE_UNUSED;
914 
915     // The rmcontrol below fails if we try querying the dongle info on
916     // non-TMDS connectors.
917     if (!IsConnectorTMDS(pConnectorEvo)) {
918         return passiveDpDongleType;
919     }
920 
921     params.displayId = nvDpyIdToNvU32(pConnectorEvo->displayId);
922     params.subDeviceInstance = pDispEvo->displayOwner;
923     params.flags = 0;
924 
925     ret = nvRmApiControl(nvEvoGlobal.clientHandle,
926                          pDevEvo->displayCommonHandle,
927                          NV0073_CTRL_CMD_DFP_GET_DISPLAYPORT_DONGLE_INFO,
928                          &params, sizeof(params));
929 
930     if (ret != NVOS_STATUS_SUCCESS) {
931         nvEvoLogDisp(pDispEvo, EVO_LOG_ERROR,
932                      "Failure reading DP dongle info "
933                      "for display device %s.", pDpyEvo->name);
934         return passiveDpDongleType;
935     }
936 
937     if (FLD_TEST_DRF(0073_CTRL_DFP,
938                      _GET_DISPLAYPORT_DONGLE_INFO_FLAGS,
939                      _ATTACHED, _TRUE, params.flags))
940     {
941         if (FLD_TEST_DRF(0073_CTRL_DFP,
942                          _GET_DISPLAYPORT_DONGLE_INFO_FLAGS, _TYPE, _DP2DVI,
943                          params.flags)) {
944 
945             passiveDpDongleType = NV_EVO_PASSIVE_DP_DONGLE_DP2DVI;
946 
947             if (passiveDpDongleMaxPclkKHz) {
948                 *passiveDpDongleMaxPclkKHz = TMDS_SINGLE_LINK_PCLK_MAX;
949             }
950         } else if (FLD_TEST_DRF(0073_CTRL_DFP,
951                                 _GET_DISPLAYPORT_DONGLE_INFO_FLAGS, _TYPE, _DP2HDMI,
952                                 params.flags)) {
953             if (FLD_TEST_DRF(0073_CTRL_DFP,
954                              _GET_DISPLAYPORT_DONGLE_INFO_FLAGS_DP2TMDS_DONGLE, _TYPE, _1,
955                              params.flags)) {
956 
957                 passiveDpDongleType = NV_EVO_PASSIVE_DP_DONGLE_DP2HDMI_TYPE_1;
958 
959                 if (passiveDpDongleMaxPclkKHz) {
960                     *passiveDpDongleMaxPclkKHz = params.maxTmdsClkRateHz / 1000;
961                 }
962             } else if (FLD_TEST_DRF(0073_CTRL_DFP,
963                                     _GET_DISPLAYPORT_DONGLE_INFO_FLAGS_DP2TMDS_DONGLE, _TYPE, _2,
964                                     params.flags)) {
965 
966                 passiveDpDongleType = NV_EVO_PASSIVE_DP_DONGLE_DP2HDMI_TYPE_2;
967 
968                 if (passiveDpDongleMaxPclkKHz) {
969                     *passiveDpDongleMaxPclkKHz = params.maxTmdsClkRateHz / 1000;
970                 }
971             }
972             // For other dongle types: LFH_DVI (DMS59-DVI) and LFH_VGA (DMS59-VGA) breakout dongles,
973             // We consider them as native connection, hence we don't track passiveDpDongleType here
974         }
975     }
976 
977     return passiveDpDongleType;
978 }
979 
980 
981 /*!
982  * Validate an NVKMS client-specified NvKmsModeValidationFrequencyRanges.
983  */
984 static NvBool ValidateFrequencyRanges(
985     const struct NvKmsModeValidationFrequencyRanges *pRanges)
986 {
987     NvU32 i;
988 
989     if (pRanges->numRanges >= ARRAY_LEN(pRanges->range)) {
990         return FALSE;
991     }
992 
993     for (i = 0; i < pRanges->numRanges; i++) {
994         if (pRanges->range[i].high < pRanges->range[i].low) {
995             return FALSE;
996         }
997         if (pRanges->range[i].high == 0) {
998             return FALSE;
999         }
1000     }
1001 
1002     return TRUE;
1003 }
1004 
1005 
1006 static void DpySetValidSyncsHelper(
1007     struct NvKmsModeValidationFrequencyRanges *pRanges,
1008     const NVParsedEdidEvoRec *pParsedEdid,
1009     NvBool isHorizSync, NvBool ignoreEdidSource)
1010 {
1011     NvBool found = FALSE;
1012     NvU32 edidMin = 0, edidMax = 0;
1013 
1014     if (pParsedEdid->valid) {
1015         if (isHorizSync) {
1016             edidMin = pParsedEdid->limits.min_h_rate_hz;
1017             edidMax = pParsedEdid->limits.max_h_rate_hz;
1018         } else {
1019             edidMin = pParsedEdid->limits.min_v_rate_hzx1k;
1020             edidMax = pParsedEdid->limits.max_v_rate_hzx1k;
1021         }
1022     }
1023 
1024     /* If the client-specified ranges are invalid, clear them. */
1025 
1026     if ((pRanges->source ==
1027          NVKMS_MODE_VALIDATION_FREQUENCY_RANGE_SOURCE_CLIENT_BEFORE_EDID) ||
1028         (pRanges->source ==
1029          NVKMS_MODE_VALIDATION_FREQUENCY_RANGE_SOURCE_CLIENT_AFTER_EDID)) {
1030 
1031         if (!ValidateFrequencyRanges(pRanges)) {
1032             nvkms_memset(pRanges, 0, sizeof(*pRanges));
1033             pRanges->source = NVKMS_MODE_VALIDATION_FREQUENCY_RANGE_SOURCE_NONE;
1034         }
1035     }
1036 
1037     /* Use CLIENT_BEFORE_EDID, if provided. */
1038 
1039     if (pRanges->source ==
1040         NVKMS_MODE_VALIDATION_FREQUENCY_RANGE_SOURCE_CLIENT_BEFORE_EDID) {
1041         found = TRUE;
1042     }
1043 
1044     /*
1045      * Otherwise, if EDID-reported sync ranges are available, use
1046      * those.
1047      */
1048     if (!found &&
1049         !ignoreEdidSource &&
1050         (edidMin != 0) && (edidMax != 0)) {
1051 
1052         pRanges->numRanges = 1;
1053         pRanges->range[0].low = edidMin;
1054         pRanges->range[0].high = edidMax;
1055         pRanges->source = NVKMS_MODE_VALIDATION_FREQUENCY_RANGE_SOURCE_EDID;
1056         found = TRUE;
1057     }
1058 
1059     /*
1060      * Otherwise, use CLIENT_AFTER_EDID, if available.
1061      */
1062     if (!found &&
1063         (pRanges->source ==
1064          NVKMS_MODE_VALIDATION_FREQUENCY_RANGE_SOURCE_CLIENT_AFTER_EDID)) {
1065         found = TRUE;
1066     }
1067 
1068     /*
1069      * Finally, fall back to conservative defaults if we could not
1070      * find anything else; this will validate 1024x768 @ 60Hz.
1071      */
1072     if (!found) {
1073 
1074         pRanges->numRanges = 1;
1075 
1076         if (isHorizSync) {
1077             pRanges->range[0].low = 28000;
1078             pRanges->range[0].high = 55000;
1079         } else {
1080             pRanges->range[0].low = 43000;
1081             pRanges->range[0].high = 72000;
1082         }
1083 
1084         pRanges->source =
1085             NVKMS_MODE_VALIDATION_FREQUENCY_RANGE_SOURCE_CONSERVATIVE_DEFAULTS;
1086     }
1087 }
1088 
1089 
1090 /*!
1091  * Assign NvKmsModeValidationValidSyncs
1092  *
1093  * Assign the HorizSync and VertRefresh ranges in
1094  * NvKmsModeValidationValidSyncs.  The priority order is:
1095  *
1096  * (1) Any HorizSync and VertRefresh provided by the client that
1097  *     overrides the EDID (CLIENT_BEFORE_EDID).
1098  * (2) Valid range information from the EDID.
1099  * (3) Any HorizSync and VertRefresh specified by the client as a
1100  *     fallback for the EDID (CLIENT_AFTER_EDID).
1101  * (4) Conservative builtin defaults.
1102  *
1103  * HorizSync and VertRefresh can come from different sources.  (1) and
1104  * (3) are provided through pValidSyncs.  (2) and (4) get written to
1105  * pValidSyncs.
1106  *
1107  * \param[in]      pDpy         The dpy whose EDID will be used.
1108  * \param[in,out]  pValidSyncs  This is initialized by the client, and
1109  *                              will be updated based on the frequency
1110  *                              range priority described above.
1111  */
1112 void nvDpySetValidSyncsEvo(const NVDpyEvoRec *pDpyEvo,
1113                            struct NvKmsModeValidationValidSyncs *pValidSyncs)
1114 {
1115     const NVParsedEdidEvoRec *pParsedEdid = &pDpyEvo->parsedEdid;
1116 
1117     DpySetValidSyncsHelper(&pValidSyncs->horizSyncHz,
1118                            pParsedEdid,
1119                            TRUE, /* isHorizSync */
1120                            pValidSyncs->ignoreEdidSource);
1121 
1122     DpySetValidSyncsHelper(&pValidSyncs->vertRefreshHz1k,
1123                            pParsedEdid,
1124                            FALSE, /* isHorizSync */
1125                            pValidSyncs->ignoreEdidSource);
1126 }
1127 
1128 
1129 /*
1130  * ReadEdidFromDP() - query the EDID for the specified display device from the
1131  * DP lib.
1132  */
1133 
1134 static NvBool ReadEdidFromDP(const NVDpyEvoRec *pDpyEvo, NVEdidPtr pEdid)
1135 {
1136     NVDispEvoPtr pDispEvo = pDpyEvo->pDispEvo;
1137     NvU8 *pNewEdid = NULL;
1138     int newEdidLength;
1139 
1140     if (!nvDpyUsesDPLib(pDpyEvo)) {
1141         return FALSE;
1142     }
1143 
1144     /* get size and allocate space for the EDID data */
1145     newEdidLength = nvDPGetEDIDSize(pDpyEvo);
1146     if (newEdidLength == 0) {
1147         goto fail;
1148     }
1149 
1150     pNewEdid = nvCalloc(newEdidLength, 1);
1151 
1152     if (pNewEdid == NULL) {
1153         goto fail;
1154     }
1155 
1156     if (!nvDPGetEDID(pDpyEvo, pNewEdid, newEdidLength)) {
1157         goto fail;
1158     }
1159 
1160     pEdid->buffer = pNewEdid;
1161     pEdid->length = newEdidLength;
1162 
1163     return TRUE;
1164 
1165  fail:
1166 
1167     nvFree(pNewEdid);
1168 
1169     nvEvoLogDisp(pDispEvo, EVO_LOG_WARN,
1170                  "Unable to read EDID for display device %s",
1171                  pDpyEvo->name);
1172     return FALSE;
1173 
1174 } // ReadEdidFromDP()
1175 
1176 
1177 
1178 /*
1179  * ReadEdidFromResman() - query the EDID for the specified display device
1180  */
1181 
1182 static NvBool ReadEdidFromResman(const NVDpyEvoRec *pDpyEvo, NVEdidPtr pEdid,
1183                                  NvKmsEdidReadMode readMode)
1184 {
1185     NvU32 ret;
1186     NVDispEvoPtr pDispEvo = pDpyEvo->pDispEvo;
1187     NVDevEvoPtr pDevEvo = pDispEvo->pDevEvo;
1188     NV0073_CTRL_SPECIFIC_GET_EDID_V2_PARAMS *getEdidParams;
1189     int retryEdidReadCount = 0;
1190     NvBool success = FALSE;
1191 
1192     if (nvDpyEvoIsDPMST(pDpyEvo)) {
1193         // RM doesn't track this device, so leave the EDID alone.
1194         return TRUE;
1195     }
1196 
1197     getEdidParams = nvCalloc(sizeof(*getEdidParams), 1);
1198     if (getEdidParams == NULL) {
1199         goto done;
1200     }
1201 
1202  query_edid:
1203 
1204     getEdidParams->subDeviceInstance = pDispEvo->displayOwner;
1205     getEdidParams->displayId = nvDpyEvoGetConnectorId(pDpyEvo);
1206     getEdidParams->flags = NV0073_CTRL_SPECIFIC_GET_EDID_FLAGS_COPY_CACHE_NO;
1207 
1208     if (readMode == NVKMS_EDID_READ_MODE_ACPI) {
1209         getEdidParams->flags |= DRF_DEF(0073_CTRL_SPECIFIC, _GET_EDID_FLAGS,
1210             _DISPMUX_READ_MODE, _ACPI);
1211     }
1212 
1213     ret = nvRmApiControl(nvEvoGlobal.clientHandle,
1214                          pDevEvo->displayCommonHandle,
1215                          NV0073_CTRL_CMD_SPECIFIC_GET_EDID_V2,
1216                          getEdidParams, sizeof(*getEdidParams));
1217 
1218     if ((ret != NVOS_STATUS_SUCCESS) || (getEdidParams->bufferSize <= 0)) {
1219         /* WAR for Bug 777646: retry reading the EDID on error for DP
1220          * devices to avoid possible TDR assertion in the RM.
1221          *
1222          * XXX This should be moved to the DP library.
1223          */
1224         if (nvConnectorUsesDPLib(pDpyEvo->pConnectorEvo) &&
1225             (retryEdidReadCount < NV_DP_READ_EDID_RETRIES)) {
1226             retryEdidReadCount++;
1227 
1228             nvkms_usleep(NV_DP_REREAD_EDID_DELAY_USEC);
1229 
1230             goto query_edid;
1231         }
1232         goto done;
1233     }
1234 
1235     pEdid->buffer = nvCalloc(getEdidParams->bufferSize, 1);
1236 
1237     if (pEdid->buffer == NULL) {
1238         goto done;
1239     }
1240 
1241     nvkms_memcpy(pEdid->buffer, &getEdidParams->edidBuffer,
1242                  getEdidParams->bufferSize);
1243     pEdid->length = getEdidParams->bufferSize;
1244 
1245     success = TRUE;
1246 
1247  done:
1248 
1249     nvFree(getEdidParams);
1250 
1251     if (!success) {
1252         nvEvoLogDisp(pDispEvo, EVO_LOG_WARN,
1253                      "Unable to read EDID for display device %s",
1254                      pDpyEvo->name);
1255     }
1256 
1257     return success;
1258 } // ReadEdidFromResman()
1259 
1260 
1261 /*
1262  * Check if the EDID meets basic validation criteria.
1263  */
1264 static NvBool ValidateEdid(const NVDpyEvoRec *pDpyEvo, NVEdidPtr pEdid,
1265                            NVEvoInfoStringPtr pInfoString,
1266                            const NvBool ignoreEdidChecksum)
1267 {
1268     NvU32 status, tmpStatus;
1269 
1270     status = NvTiming_EDIDValidationMask(pEdid->buffer, pEdid->length, TRUE);
1271     tmpStatus = status;
1272 
1273     if (status == 0) {
1274         return TRUE;
1275     }
1276 
1277     nvEvoLogInfoString(pInfoString,
1278                        "The EDID read for display device %s is invalid:",
1279                        pDpyEvo->name);
1280 
1281     /*
1282      * Warn about every error we know about, masking it out of tmpStatus, then
1283      * warn about an unknown error if there are still any bits remaining in
1284      * tmpStatus.
1285      */
1286     if (status &
1287         NVT_EDID_VALIDATION_ERR_MASK(NVT_EDID_VALIDATION_ERR_VERSION)) {
1288         nvEvoLogInfoString(pInfoString,
1289                      "- The EDID has an unrecognized version.");
1290         tmpStatus &= ~NVT_EDID_VALIDATION_ERR_MASK(NVT_EDID_VALIDATION_ERR_VERSION);
1291     }
1292 
1293     if (status &
1294         NVT_EDID_VALIDATION_ERR_MASK(NVT_EDID_VALIDATION_ERR_SIZE)) {
1295         nvEvoLogInfoString(pInfoString,
1296                      "- The EDID is too short.");
1297         tmpStatus &= ~NVT_EDID_VALIDATION_ERR_MASK(NVT_EDID_VALIDATION_ERR_SIZE);
1298     }
1299 
1300     if (status &
1301         NVT_EDID_VALIDATION_ERR_MASK(NVT_EDID_VALIDATION_ERR_CHECKSUM)) {
1302         /*
1303          * XXX NVKMS TODO: massage wording to not reference X
1304          * configuration option.
1305          */
1306         nvEvoLogInfoString(pInfoString,
1307                      "- The EDID has a bad checksum. %s",
1308                      ignoreEdidChecksum ? "This error will be ignored. Note "
1309                      "that an EDID with a bad checksum could indicate a "
1310                      "corrupt EDID. A corrupt EDID may have mode timings "
1311                      "beyond the capabilities of your display, and could "
1312                      "damage your hardware. Please use with care." :
1313                      "The \"IgnoreEDIDChecksum\" X configuration option may "
1314                      "be used to attempt using mode timings in this EDID in "
1315                      "spite of this error. A corrupt EDID may have mode "
1316                      "timings beyond the capabilities of your display, and "
1317                      "could damage your hardware. Please use with care.");
1318         tmpStatus &= ~NVT_EDID_VALIDATION_ERR_MASK(NVT_EDID_VALIDATION_ERR_CHECKSUM);
1319     }
1320 
1321     if (status &
1322         NVT_EDID_VALIDATION_ERR_MASK(NVT_EDID_VALIDATION_ERR_RANGE_LIMIT)) {
1323         nvEvoLogInfoString(pInfoString,
1324                      "- The EDID has a bad range limit.");
1325         tmpStatus &= ~NVT_EDID_VALIDATION_ERR_MASK(NVT_EDID_VALIDATION_ERR_RANGE_LIMIT);
1326     }
1327 
1328     if (status &
1329         NVT_EDID_VALIDATION_ERR_MASK(NVT_EDID_VALIDATION_ERR_DTD)) {
1330         nvEvoLogInfoString(pInfoString,
1331                      "- The EDID has a bad detailed timing descriptor.");
1332         tmpStatus &= ~NVT_EDID_VALIDATION_ERR_MASK(NVT_EDID_VALIDATION_ERR_DTD);
1333     }
1334 
1335     if (status &
1336         NVT_EDID_VALIDATION_ERR_MASK(NVT_EDID_VALIDATION_ERR_EXT_DTD)) {
1337         nvEvoLogInfoString(pInfoString,
1338                      "- The EDID has an extension block with a bad detailed "
1339                      "timing descriptor.");
1340         tmpStatus &= ~NVT_EDID_VALIDATION_ERR_MASK(NVT_EDID_VALIDATION_ERR_EXT_DTD);
1341     }
1342 
1343     if (status &
1344         NVT_EDID_VALIDATION_ERR_MASK(NVT_EDID_VALIDATION_ERR_EXT)) {
1345         nvEvoLogInfoString(pInfoString,
1346                      "- The EDID extension block is invalid.");
1347         tmpStatus &= ~NVT_EDID_VALIDATION_ERR_MASK(NVT_EDID_VALIDATION_ERR_EXT);
1348     }
1349 
1350     if (tmpStatus) {
1351         nvEvoLogInfoString(pInfoString,
1352                      "- The EDID has an unrecognized error.");
1353     }
1354 
1355     /*
1356      * Unset the bits for errors we don't care about (invalid DTDs in the
1357      * extension block, or checksum errors if ignoreEdidChecksum is in use)
1358      * then return true if there are any remaining errors we do care about.
1359      */
1360     if (ignoreEdidChecksum) {
1361         status &= ~(NVT_EDID_VALIDATION_ERR_MASK(NVT_EDID_VALIDATION_ERR_CHECKSUM));
1362     }
1363 
1364     if (status ==
1365         NVT_EDID_VALIDATION_ERR_MASK(NVT_EDID_VALIDATION_ERR_EXT_DTD)) {
1366         /*
1367          * If the only problem with the EDID is invalid DTDs in the extension
1368          * block, don't reject the EDID; those timings can be safely skipped in
1369          * NvTiming_ParseEDIDInfo()/parse861ExtDetailedTiming()
1370          */
1371         nvEvoLogInfoString(pInfoString,
1372                            "This bad detailed timing descriptor will be ignored.");
1373     }
1374 
1375     status &= ~(NVT_EDID_VALIDATION_ERR_MASK(NVT_EDID_VALIDATION_ERR_EXT_DTD));
1376 
1377     return (status == 0);
1378 }
1379 
1380 static const char *GetColorDepthBpc(NVT_COLORDEPTH colorDepth)
1381 {
1382     static char buffer[32];
1383     NVEvoInfoStringRec infoString;
1384     NvBool first = TRUE;
1385     int i;
1386 
1387     struct {
1388         NvBool val;
1389         int bpc;
1390     } table[] = {
1391         { colorDepth.bpc.bpc6,  6  },
1392         { colorDepth.bpc.bpc8,  8  },
1393         { colorDepth.bpc.bpc10, 10 },
1394         { colorDepth.bpc.bpc12, 12 },
1395         { colorDepth.bpc.bpc14, 14 },
1396         { colorDepth.bpc.bpc16, 16 },
1397     };
1398 
1399     nvInitInfoString(&infoString, buffer, sizeof(buffer));
1400 
1401     buffer[0] = '\0';
1402 
1403     for (i = 0; i < ARRAY_LEN(table); i++) {
1404         if (table[i].val) {
1405             nvEvoLogInfoStringRaw(&infoString, "%s%d",
1406                                   first ? "" : ", ",
1407                                   table[i].bpc);
1408             first = FALSE;
1409         }
1410     }
1411 
1412     return buffer;
1413 }
1414 
1415 
1416 /*
1417  * Log information about the EDID.
1418  */
1419 
1420 static void LogEdid(NVDpyEvoPtr pDpyEvo, NVEvoInfoStringPtr pInfoString)
1421 {
1422     int k;
1423     NVParsedEdidEvoPtr pParsedEdid;
1424 
1425     static const struct {
1426         NVT_TIMING_TYPE type;
1427         const char *name;
1428     } mode_type_table[] = {
1429         { NVT_TYPE_DMT,              "Display Monitor Timings" },
1430         { NVT_TYPE_GTF,              "Generalized Timing Formula Timings" },
1431         { NVT_TYPE_ASPR,             "ASPR Timings"},
1432         { NVT_TYPE_NTSC_TV,          "NTSC Timings" },
1433         { NVT_TYPE_PAL_TV,           "PAL Timings" },
1434         { NVT_TYPE_CVT,              "Coordinated Video Timings"},
1435         { NVT_TYPE_CVT_RB,      "Reduced Blanking Coordinated Video Timings" },
1436         { NVT_TYPE_CUST,             "Customized Timings" },
1437         { NVT_TYPE_EDID_STD,         "Standard Timings" },
1438         { NVT_TYPE_EDID_DTD,         "Detailed Timings" },
1439         { NVT_TYPE_EDID_CVT,         "Coordinated Video Timings" },
1440         { NVT_TYPE_EDID_EST,         "Established Timings"},
1441         { NVT_TYPE_EDID_861ST,       "CEA-861B Timings" },
1442         { NVT_TYPE_NV_PREDEFINED,    "Predefined Timings" },
1443         { NVT_TYPE_DMT_RB,        "Reduced Blanking Display Monitor Timings" },
1444         { NVT_TYPE_EDID_EXT_DTD,     "Extension Block Detailed Timings" },
1445         { NVT_TYPE_SDTV,             "SDTV Timings "},
1446         { NVT_TYPE_HDTV,             "HDTV Timings" },
1447         { NVT_TYPE_SMPTE,            "SMPTE Timings" },
1448         { NVT_TYPE_EDID_VTB_EXT,     "VTB Extension Timings" },
1449         { NVT_TYPE_EDID_VTB_EXT_STD, "VTB Extension Detailed Timings" },
1450         { NVT_TYPE_EDID_VTB_EXT_DTD, "VTB Extension Standard Timings" },
1451         { NVT_TYPE_EDID_VTB_EXT_CVT, "VTB Extension CVT Timings" },
1452         { NVT_TYPE_HDMI_STEREO,      "HDMI Stereo Timings" },
1453         { NVT_TYPE_DISPLAYID_1,      "DisplayID Type 1 Timings" },
1454         { NVT_TYPE_DISPLAYID_2,      "DisplayID Type 2 Timings" },
1455         { NVT_TYPE_HDMI_EXT,         "HDMI Extended Resolution Timings" },
1456         { NVT_TYPE_CUST_AUTO,        "Customized Auto Timings" },
1457         { NVT_TYPE_CUST_MANUAL,      "Customized Manual Timings" },
1458         { NVT_TYPE_CVT_RB_2,"Reduced Blanking Coordinated Video Timings, v2" },
1459         { NVT_TYPE_DMT_RB_2,         "Display Monitor Timings, V2" },
1460         { NVT_TYPE_DISPLAYID_7,      "DisplayID Type 7 Timings" },
1461         { NVT_TYPE_DISPLAYID_8,      "DisplayID Type 8 Timings" },
1462         { NVT_TYPE_DISPLAYID_9,      "DisplayID Type 9 Timings" },
1463         { NVT_TYPE_DISPLAYID_10,     "DisplayID Type 10 Timings" },
1464         { NVT_TYPE_CVT_RB_3,         "Reduced Blanking Coordinated Video Timings, v3" },
1465     };
1466 
1467     /*
1468      * Trigger a warning if new NVT_TIMING_TYPE values are added
1469      * without updating this function.
1470      *
1471      * If a warning is produced about unhandled enum in the below
1472      * switch statement, please update both the switch statement and
1473      * mode_type_table[], or contact the sw-nvkms email alias.
1474      */
1475     if (pDpyEvo->parsedEdid.valid) {
1476         for (k = 0; k < pDpyEvo->parsedEdid.info.total_timings; k++) {
1477             NvU32 status = pDpyEvo->parsedEdid.info.timing[k].etc.status;
1478             NVT_TIMING_TYPE type = NVT_GET_TIMING_STATUS_TYPE(status);
1479 
1480             switch (type) {
1481                 case NVT_TYPE_DMT:
1482                 case NVT_TYPE_GTF:
1483                 case NVT_TYPE_ASPR:
1484                 case NVT_TYPE_NTSC_TV:
1485                 case NVT_TYPE_PAL_TV:
1486                 case NVT_TYPE_CVT:
1487                 case NVT_TYPE_CVT_RB:
1488                 case NVT_TYPE_CUST:
1489                 case NVT_TYPE_EDID_DTD:
1490                 case NVT_TYPE_EDID_STD:
1491                 case NVT_TYPE_EDID_EST:
1492                 case NVT_TYPE_EDID_CVT:
1493                 case NVT_TYPE_EDID_861ST:
1494                 case NVT_TYPE_NV_PREDEFINED:
1495                 case NVT_TYPE_DMT_RB:
1496                 case NVT_TYPE_EDID_EXT_DTD:
1497                 case NVT_TYPE_SDTV:
1498                 case NVT_TYPE_HDTV:
1499                 case NVT_TYPE_SMPTE:
1500                 case NVT_TYPE_EDID_VTB_EXT:
1501                 case NVT_TYPE_EDID_VTB_EXT_STD:
1502                 case NVT_TYPE_EDID_VTB_EXT_DTD:
1503                 case NVT_TYPE_EDID_VTB_EXT_CVT:
1504                 case NVT_TYPE_HDMI_STEREO:
1505                 case NVT_TYPE_DISPLAYID_1:
1506                 case NVT_TYPE_DISPLAYID_2:
1507                 case NVT_TYPE_HDMI_EXT:
1508                 case NVT_TYPE_CUST_AUTO:
1509                 case NVT_TYPE_CUST_MANUAL:
1510                 case NVT_TYPE_CVT_RB_2:
1511                 case NVT_TYPE_DMT_RB_2:
1512                 case NVT_TYPE_DISPLAYID_7:
1513                 case NVT_TYPE_DISPLAYID_8:
1514                 case NVT_TYPE_DISPLAYID_9:
1515                 case NVT_TYPE_DISPLAYID_10:
1516                 case NVT_TYPE_CVT_RB_3:
1517                     /*
1518                      * XXX temporarily disable the warning so that additional
1519                      * NVT_TYPEs_ can be added to nvtiming.h.  Bug 3849339.
1520                      */
1521                 default:
1522                     break;
1523             }
1524             break;
1525         }
1526     }
1527 
1528     nvEvoLogInfoString(pInfoString, "");
1529     nvEvoLogInfoString(pInfoString,
1530                        "--- EDID for %s ---", pDpyEvo->name);
1531 
1532     if (!pDpyEvo->parsedEdid.valid) {
1533         nvEvoLogInfoString(pInfoString, "");
1534         nvEvoLogInfoString(pInfoString, "No EDID Available.");
1535         nvEvoLogInfoString(pInfoString, "");
1536         goto done;
1537     }
1538 
1539     pParsedEdid = &pDpyEvo->parsedEdid;
1540 
1541     nvEvoLogInfoString(pInfoString,
1542                        "EDID Version                 : %d.%d",
1543                        pParsedEdid->info.version >> 8,
1544                        pParsedEdid->info.version & 0xff);
1545 
1546     nvEvoLogInfoString(pInfoString,
1547                        "Manufacturer                 : %s",
1548                        pParsedEdid->info.manuf_name);
1549 
1550     nvEvoLogInfoString(pInfoString,
1551                        "Monitor Name                 : %s",
1552                        pParsedEdid->monitorName);
1553 
1554     nvEvoLogInfoString(pInfoString,
1555                        "Product ID                   : 0x%04x",
1556                        pParsedEdid->info.product_id);
1557 
1558     nvEvoLogInfoString(pInfoString,
1559                        "32-bit Serial Number         : 0x%08x",
1560                        pParsedEdid->info.serial_number);
1561 
1562     nvEvoLogInfoString(pInfoString,
1563                        "Serial Number String         : %s",
1564                        pParsedEdid->serialNumberString);
1565 
1566     nvEvoLogInfoString(pInfoString,
1567                        "Manufacture Date             : %d, week %d",
1568                        pParsedEdid->info.year,
1569                        pParsedEdid->info.week);
1570 
1571     /*
1572      * despite the name feature_ver_1_3, the below features are
1573      * reported on all EDID versions
1574      */
1575     nvEvoLogInfoString(pInfoString,
1576                        "DPMS Capabilities            :%s%s%s",
1577                        pParsedEdid->info.u.feature_ver_1_3.support_standby ?
1578                        " Standby" : "",
1579                        pParsedEdid->info.u.feature_ver_1_3.support_suspend ?
1580                        " Suspend" : "",
1581                        pParsedEdid->info.u.feature_ver_1_3.support_active_off ?
1582                        " Active Off" : "");
1583 
1584     nvEvoLogInfoString(pInfoString,
1585                        "Input Type                   : %s",
1586                        pParsedEdid->info.input.isDigital ?
1587                        "Digital" : "Analog");
1588 
1589     nvEvoLogInfoString(pInfoString,
1590                        "Prefer first detailed timing : %s",
1591                        pParsedEdid->info.u.feature_ver_1_3.preferred_timing_is_native ?
1592                        "Yes" : "No");
1593 
1594     if (pParsedEdid->info.version == NVT_EDID_VER_1_3) {
1595         nvEvoLogInfoString(pInfoString,
1596                            "Supports GTF                 : %s",
1597                            pParsedEdid->info.u.feature_ver_1_3.support_gtf ?
1598                            "Yes" : "No");
1599     }
1600 
1601     if (pParsedEdid->info.version >= NVT_EDID_VER_1_4) {
1602         NvBool continuousFrequency = FALSE;
1603         if (pParsedEdid->info.input.isDigital) {
1604             continuousFrequency =
1605                 pParsedEdid->info.u.feature_ver_1_4_digital.continuous_frequency;
1606         } else {
1607             continuousFrequency =
1608                 pParsedEdid->info.u.feature_ver_1_4_analog.continuous_frequency;
1609         }
1610 
1611         nvEvoLogInfoString(pInfoString,
1612                            "Supports Continuous Frequency: %s",
1613                            continuousFrequency ? "Yes" : "No");
1614 
1615         nvEvoLogInfoString(pInfoString,
1616                            "EDID 1.4 YCbCr 422 support   : %s",
1617                            pParsedEdid->info.u.feature_ver_1_4_digital.support_ycrcb_422
1618                            ? "Yes" : "No");
1619 
1620         nvEvoLogInfoString(pInfoString,
1621                            "EDID 1.4 YCbCr 444 support   : %s",
1622                            pParsedEdid->info.u.feature_ver_1_4_digital.support_ycrcb_444
1623                            ? "Yes" : "No");
1624     }
1625 
1626     nvEvoLogInfoString(pInfoString,
1627                        "Maximum Image Size           : %d mm x %d mm",
1628                        pParsedEdid->info.screen_size_x * 10, /* screen_size_* is in cm */
1629                        pParsedEdid->info.screen_size_y * 10);
1630 
1631     nvEvoLogInfoString(pInfoString,
1632                        "Valid HSync Range            : "
1633                        NV_FMT_DIV_1000_POINT_1
1634                        " kHz - " NV_FMT_DIV_1000_POINT_1 " kHz",
1635                        NV_VA_DIV_1000_POINT_1(pParsedEdid->limits.min_h_rate_hz),
1636                        NV_VA_DIV_1000_POINT_1(pParsedEdid->limits.max_h_rate_hz));
1637 
1638     nvEvoLogInfoString(pInfoString,
1639                        "Valid VRefresh Range         : "
1640                        NV_FMT_DIV_1000_POINT_1 " Hz - "
1641                        NV_FMT_DIV_1000_POINT_1 " Hz",
1642                        NV_VA_DIV_1000_POINT_1(pParsedEdid->limits.min_v_rate_hzx1k),
1643                        NV_VA_DIV_1000_POINT_1(pParsedEdid->limits.max_v_rate_hzx1k));
1644 
1645     nvEvoLogInfoString(pInfoString,
1646                        "EDID maximum pixel clock     : "
1647                        NV_FMT_DIV_1000_POINT_1 " MHz",
1648                        NV_VA_DIV_1000_POINT_1(pParsedEdid->limits.max_pclk_10khz * 10));
1649 
1650     if (pParsedEdid->info.nvdaVsdbInfo.valid) {
1651         nvEvoLogInfoString(pInfoString,
1652                            "G-Sync capable               : %s",
1653                            pParsedEdid->info.nvdaVsdbInfo.vrrData.v1.supportsVrr
1654                            ? "Yes" : "No");
1655         nvEvoLogInfoString(pInfoString,
1656                            "G-Sync minimum refresh rate  : %d Hz",
1657                            pParsedEdid->info.nvdaVsdbInfo.vrrData.v1.minRefreshRate);
1658     }
1659 
1660     nvLogEdidCea861InfoEvo(pDpyEvo, pInfoString);
1661 
1662     if (pParsedEdid->info.input.isDigital &&
1663         pParsedEdid->info.version >= NVT_EDID_VER_1_4) {
1664         nvEvoLogInfoString(pInfoString,
1665                            "EDID bits per component      : %d",
1666                            pParsedEdid->info.input.u.digital.bpc);
1667     }
1668 
1669     /* print the tiled display extension block, if present */
1670     if (pParsedEdid->info.ext_displayid.tile_topology_id.vendor_id) {
1671         const NVT_DISPLAYID_INFO *tile = &pParsedEdid->info.ext_displayid;
1672         const char *tmp;
1673 
1674         nvEvoLogInfoString(pInfoString,
1675                            "Tiled display information    :");
1676         nvEvoLogInfoString(pInfoString,
1677                            "  Revision                   : %d",
1678                            tile->tiled_display_revision);
1679         nvEvoLogInfoString(pInfoString,
1680                            "  Single Enclosure           : %s",
1681                            tile->tile_capability.bSingleEnclosure ?
1682                            "Yes" : "No");
1683 
1684         tmp = "Unknown";
1685         switch (tile->tile_capability.multi_tile_behavior) {
1686             case NVT_MULTI_TILE_BEHAVIOR_OTHER:
1687                 tmp = "Other";
1688                 break;
1689             case NVT_MULTI_TILE_BEHAVIOR_SOURCE_DRIVEN:
1690                 tmp = "Source-driven";
1691                 break;
1692         }
1693         nvEvoLogInfoString(pInfoString,
1694                            "  Multi-tile Behavior        : %s", tmp);
1695 
1696         tmp = "Unknown";
1697         switch (tile->tile_capability.single_tile_behavior) {
1698             case NVT_SINGLE_TILE_BEHAVIOR_OTHER:
1699                 tmp = "Other";
1700                 break;
1701             case NVT_SINGLE_TILE_BEHAVIOR_SOURCE_DRIVEN:
1702                 tmp = "Source-driven";
1703                 break;
1704             case NVT_SINGLE_TILE_BEHAVIOR_SCALE:
1705                 tmp = "Scale";
1706                 break;
1707             case NVT_SINGLE_TILE_BEHAVIOR_CLONE:
1708                 tmp = "Clone";
1709                 break;
1710         }
1711         nvEvoLogInfoString(pInfoString,
1712                            "  Single-tile Behavior       : %s", tmp);
1713         nvEvoLogInfoString(pInfoString,
1714                            "  Topology                   : %d row%s, %d column%s",
1715                            tile->tile_topology.row,
1716                            (tile->tile_topology.row == 1) ? "" : "s",
1717                            tile->tile_topology.col,
1718                            (tile->tile_topology.col == 1) ? "" : "s");
1719         nvEvoLogInfoString(pInfoString,
1720                            "  Location                   : (%d,%d)",
1721                            tile->tile_location.x, tile->tile_location.y);
1722         nvEvoLogInfoString(pInfoString,
1723                            "  Native Resolution          : %dx%d",
1724                            tile->native_resolution.width,
1725                            tile->native_resolution.height);
1726         if (tile->tile_capability.bHasBezelInfo) {
1727             nvEvoLogInfoString(pInfoString,
1728                                "  Bezel Information          :");
1729             nvEvoLogInfoString(pInfoString,
1730                                "    Pixel Density            : %d",
1731                                tile->bezel_info.pixel_density);
1732             nvEvoLogInfoString(pInfoString,
1733                                "    Top                      : %d",
1734                                tile->bezel_info.top);
1735             nvEvoLogInfoString(pInfoString,
1736                                "    Bottom                   : %d",
1737                                tile->bezel_info.bottom);
1738             nvEvoLogInfoString(pInfoString,
1739                                "    Left                     : %d",
1740                                tile->bezel_info.right);
1741             nvEvoLogInfoString(pInfoString,
1742                                "    Right                    : %d",
1743                                tile->bezel_info.left);
1744         }
1745         nvEvoLogInfoString(pInfoString,
1746                            "  Vendor ID                  : 0x%x",
1747                            tile->tile_topology_id.vendor_id);
1748         nvEvoLogInfoString(pInfoString,
1749                            "  Product ID                 : 0x%x",
1750                            tile->tile_topology_id.product_id);
1751         nvEvoLogInfoString(pInfoString,
1752                            "  Serial Number              : 0x%x",
1753                            tile->tile_topology_id.serial_number);
1754     }
1755 
1756     for (k = 0; k < ARRAY_LEN(mode_type_table); k++) {
1757 
1758         int i;
1759 
1760         /* scan through the ModeList to find a mode of the current type */
1761 
1762         for (i = 0; i < pParsedEdid->info.total_timings; i++) {
1763             NVT_TIMING *pTiming = &pParsedEdid->info.timing[i];
1764             if (mode_type_table[k].type ==
1765                 NVT_GET_TIMING_STATUS_TYPE(pTiming->etc.status)) {
1766                 break;
1767             }
1768         }
1769 
1770         /* if there are none of this type, skip to the next mode type */
1771 
1772         if (i == pParsedEdid->info.total_timings) {
1773             continue;
1774         }
1775 
1776         nvEvoLogInfoString(pInfoString, "");
1777         nvEvoLogInfoString(pInfoString, "%s:", mode_type_table[k].name);
1778 
1779         for (i = 0; i < pParsedEdid->info.total_timings; i++) {
1780 
1781             NVT_TIMING *pTiming = &pParsedEdid->info.timing[i];
1782             NVT_TIMING_TYPE type =
1783                 NVT_GET_TIMING_STATUS_TYPE(pTiming->etc.status);
1784             int vScale = 1;
1785 
1786             if (mode_type_table[k].type != type) {
1787                 continue;
1788             }
1789 
1790             if ((type == NVT_TYPE_EDID_EST) ||
1791                 (type == NVT_TYPE_EDID_STD)) {
1792 
1793                 nvEvoLogInfoString(pInfoString,
1794                                    "  %-4d x %-4d @ %d Hz",
1795                                    NV_NVT_TIMING_HVISIBLE(pTiming),
1796                                    NV_NVT_TIMING_VVISIBLE(pTiming),
1797                                    pTiming->etc.rr);
1798                 continue;
1799             }
1800 
1801             if (pTiming->interlaced) {
1802                 vScale = 2;
1803             }
1804 
1805             nvEvoLogInfoString(pInfoString,
1806                                "  %-4d x %-4d @ %d Hz",
1807                                NV_NVT_TIMING_HVISIBLE(pTiming),
1808                                NV_NVT_TIMING_VVISIBLE(pTiming),
1809                                pTiming->etc.rr);
1810 
1811             nvEvoLogInfoString(pInfoString,
1812                                "    Pixel Clock      : "
1813                                NV_FMT_DIV_1000_POINT_2 " MHz",
1814                                NV_VA_DIV_1000_POINT_2(pTiming->pclk
1815                                                               * 10));
1816 
1817             nvEvoLogInfoString(pInfoString,
1818                                "    HRes, HSyncStart : %d, %d",
1819                                pTiming->HVisible,
1820                                pTiming->HVisible +
1821                                pTiming->HFrontPorch);
1822 
1823             nvEvoLogInfoString(pInfoString,
1824                                "    HSyncEnd, HTotal : %d, %d",
1825                                pTiming->HVisible +
1826                                pTiming->HFrontPorch +
1827                                pTiming->HSyncWidth,
1828                                pTiming->HTotal);
1829 
1830             nvEvoLogInfoString(pInfoString,
1831                                "    VRes, VSyncStart : %d, %d",
1832                                pTiming->VVisible * vScale,
1833                                (pTiming->VVisible +
1834                                 pTiming->VFrontPorch) * vScale);
1835 
1836             nvEvoLogInfoString(pInfoString,
1837                                "    VSyncEnd, VTotal : %d, %d",
1838                                (pTiming->VVisible +
1839                                 pTiming->VFrontPorch +
1840                                 pTiming->VSyncWidth) * vScale,
1841                                pTiming->VTotal * vScale);
1842 
1843             nvEvoLogInfoString(pInfoString,
1844                                "    H/V Polarity     : %s/%s",
1845                                (pTiming->HSyncPol == NVT_H_SYNC_NEGATIVE) ?
1846                                "-" : "+",
1847                                (pTiming->VSyncPol == NVT_V_SYNC_NEGATIVE) ?
1848                                "-" : "+");
1849 
1850             if (pTiming->interlaced) {
1851                 nvEvoLogInfoString(pInfoString,
1852                                    "    Interlaced       : yes");
1853             }
1854             if (pTiming->etc.flag & NVT_FLAG_NV_DOUBLE_SCAN_TIMING) {
1855                 nvEvoLogInfoString(pInfoString,
1856                                    "    Double Scanned   : yes");
1857             }
1858 
1859             if (type == NVT_TYPE_EDID_861ST) {
1860                 nvEvoLogInfoString(pInfoString,
1861                                    "    CEA Format       : %d",
1862                                    NVT_GET_CEA_FORMAT(pTiming->etc.status));
1863             }
1864 
1865             if (NV_NVT_TIMING_HAS_ASPECT_RATIO(pTiming)) {
1866                 nvEvoLogInfoString(pInfoString,
1867                                    "    Aspect Ratio     : %d:%d",
1868                                    NV_NVT_TIMING_IMAGE_SIZE_WIDTH(pTiming),
1869                                    NV_NVT_TIMING_IMAGE_SIZE_HEIGHT(pTiming));
1870             }
1871 
1872             if (NV_NVT_TIMING_HAS_IMAGE_SIZE(pTiming)) {
1873                 nvEvoLogInfoString(pInfoString,
1874                                    "    Image Size       : %d mm x %d mm",
1875                                    NV_NVT_TIMING_IMAGE_SIZE_WIDTH(pTiming),
1876                                    NV_NVT_TIMING_IMAGE_SIZE_HEIGHT(pTiming));
1877             }
1878 
1879             if (IS_BPC_SUPPORTED_COLORFORMAT(pTiming->etc.rgb444.bpcs)) {
1880                 nvEvoLogInfoString(pInfoString,
1881                                    "    RGB 444 bpcs     : %s",
1882                                    GetColorDepthBpc(pTiming->etc.rgb444));
1883             }
1884 
1885             if (IS_BPC_SUPPORTED_COLORFORMAT(pTiming->etc.yuv444.bpcs)) {
1886                 nvEvoLogInfoString(pInfoString,
1887                                    "    YUV 444 bpcs     : %s",
1888                                    GetColorDepthBpc(pTiming->etc.yuv444));
1889             }
1890 
1891             if (IS_BPC_SUPPORTED_COLORFORMAT(pTiming->etc.yuv422.bpcs)) {
1892                 nvEvoLogInfoString(pInfoString,
1893                                    "    YUV 422 bpcs     : %s",
1894                                    GetColorDepthBpc(pTiming->etc.yuv422));
1895             }
1896 
1897             if (IS_BPC_SUPPORTED_COLORFORMAT(pTiming->etc.yuv420.bpcs)) {
1898                 nvEvoLogInfoString(pInfoString,
1899                                    "    YUV 420 bpcs     : %s",
1900                                    GetColorDepthBpc(pTiming->etc.yuv420));
1901             }
1902         } // i
1903     } // k
1904 
1905     nvEvoLogInfoString(pInfoString, "");
1906 
1907  done:
1908     nvEvoLogInfoString(pInfoString,
1909                        "--- End of EDID for %s ---", pDpyEvo->name);
1910     nvEvoLogInfoString(pInfoString, "");
1911 }
1912 
1913 
1914 
1915 /*
1916  * Clear the EDID and related fields in the display device data
1917  * structure.
1918  */
1919 
1920 static void ClearEdid(NVDpyEvoPtr pDpyEvo)
1921 {
1922     NVEdidRec edid = { };
1923     NVEvoInfoStringRec infoString;
1924     nvInitInfoString(&infoString, NULL, 0);
1925 
1926     if (EdidHasChanged(pDpyEvo, &edid, NULL)) {
1927         ApplyNewEdid(pDpyEvo, &edid, NULL, &infoString);
1928     }
1929 }
1930 
1931 
1932 
1933 /*
1934  * ClearCustomEdid() - send an empty custom EDID to RM; this is to
1935  * clear out any stale state in RM about custom EDIDs that we may have
1936  * told RM about previous runs of X.
1937  */
1938 
1939 static void ClearCustomEdid(const NVDpyEvoRec *pDpyEvo)
1940 {
1941     NVDispEvoPtr pDispEvo = pDpyEvo->pDispEvo;
1942     NVDevEvoPtr pDevEvo = pDispEvo->pDevEvo;
1943     NV0073_CTRL_SPECIFIC_SET_EDID_V2_PARAMS *setEdidParams;
1944 
1945     if (nvDpyEvoIsDPMST(pDpyEvo)) {
1946         // RM doesn't track this device, so leave the EDID alone.
1947         return;
1948     }
1949 
1950     setEdidParams = nvCalloc(sizeof(*setEdidParams), 1);
1951     if (setEdidParams == NULL) {
1952         nvEvoLogDisp(pDispEvo, EVO_LOG_WARN,
1953                      "Unable to clear custom EDID for display device %s",
1954                      pDpyEvo->name);
1955         return;
1956     }
1957 
1958     setEdidParams->subDeviceInstance = pDispEvo->displayOwner;
1959     setEdidParams->displayId = nvDpyEvoGetConnectorId(pDpyEvo);
1960     setEdidParams->bufferSize = 0;
1961 
1962     /* ignore the NvRmControl() return value */
1963 
1964     (void) nvRmApiControl(nvEvoGlobal.clientHandle,
1965                           pDevEvo->displayCommonHandle,
1966                           NV0073_CTRL_CMD_SPECIFIC_SET_EDID_V2,
1967                           setEdidParams, sizeof(*setEdidParams));
1968 
1969     nvFree(setEdidParams);
1970 } // ClearCustomEdid()
1971 
1972 
1973 
1974 /*
1975  * WriteEdidToResman() - send a custom EDID to RM.
1976  */
1977 
1978 static void WriteEdidToResman(const NVDpyEvoRec *pDpyEvo,
1979                               const NVEdidRec *pEdid)
1980 {
1981     NVDispEvoPtr pDispEvo = pDpyEvo->pDispEvo;
1982     NVDevEvoPtr pDevEvo = pDispEvo->pDevEvo;
1983     NV0073_CTRL_SPECIFIC_SET_EDID_V2_PARAMS *setEdidParams = NULL;
1984     NvU32 status = NVOS_STATUS_ERROR_OPERATING_SYSTEM;
1985 
1986     if (pEdid->length > sizeof(setEdidParams->edidBuffer)) {
1987         nvEvoLogDisp(pDispEvo, EVO_LOG_ERROR,
1988             "EDID for display device %s is too long for NV0073_CTRL_CMD_SPECIFIC_SET_EDID_V2",
1989             pDpyEvo->name);
1990         goto done;
1991     }
1992 
1993     setEdidParams = nvCalloc(sizeof(*setEdidParams), 1);
1994     if (setEdidParams == NULL) {
1995         goto done;
1996     }
1997 
1998     setEdidParams->subDeviceInstance = pDispEvo->displayOwner;
1999     setEdidParams->displayId = nvDpyEvoGetConnectorId(pDpyEvo);
2000     nvkms_memcpy(&setEdidParams->edidBuffer, pEdid->buffer, pEdid->length);
2001     setEdidParams->bufferSize = pEdid->length;
2002 
2003     status = nvRmApiControl(nvEvoGlobal.clientHandle,
2004                             pDevEvo->displayCommonHandle,
2005                             NV0073_CTRL_CMD_SPECIFIC_SET_EDID_V2,
2006                             setEdidParams, sizeof(*setEdidParams));
2007 
2008 done:
2009     if (status != NVOS_STATUS_SUCCESS) {
2010         nvEvoLogDisp(pDispEvo, EVO_LOG_WARN,
2011                      "Failure processing EDID for display device "
2012                      "%s.", pDpyEvo->name);
2013     }
2014 
2015     nvFree(setEdidParams);
2016 } // WriteEdidToResman()
2017 
2018 
2019 /*
2020  * NvTiming_ParseEDIDInfo() will ignore some modes that are blatantly
2021  * wrong, so we need to apply any patching to the EDID bytes before
2022  * parsing the EDID.
2023  */
2024 static void PrePatchEdid(const NVDpyEvoRec *pDpyEvo, NVEdidPtr pEdid,
2025                          NVEvoInfoStringPtr pInfoString)
2026 {
2027     NvU8 *pEdidData = pEdid->buffer;
2028 
2029     if (pEdid->buffer == NULL || pEdid->length < 128) {
2030         return;
2031     }
2032 
2033     /*
2034      * Work around bug 628240: some AUO flat panels have invalid
2035      * native modes where HSyncEnd is larger than HTotal, putting the
2036      * end of the sync pulse several columns into the active region of
2037      * the next frame.  AUO confirmed these corrected timings:
2038      *
2039      *     "1366x768" 69.30 1366 1398 1422 1432 768 771 775 806 -hsync -vsync
2040      */
2041     if (pDpyEvo->pConnectorEvo->legacyType ==
2042         NV0073_CTRL_SPECIFIC_DISPLAY_TYPE_DFP &&
2043         pEdidData[0x36] == 0x26 &&
2044         pEdidData[0x37] == 0x1b &&
2045         pEdidData[0x38] == 0x56 &&
2046         pEdidData[0x39] == 0x47 &&
2047         pEdidData[0x3a] == 0x50 &&
2048         pEdidData[0x3b] == 0x00 &&
2049         pEdidData[0x3c] == 0x26 &&
2050         pEdidData[0x3d] == 0x30 &&
2051         pEdidData[0x3e] == 0x30 &&
2052         pEdidData[0x3f] == 0x20 &&
2053         pEdidData[0x40] == 0x34 &&
2054         pEdidData[0x41] == 0x00 &&
2055         pEdidData[0x42] == 0x58 &&
2056         pEdidData[0x43] == 0xc1 &&
2057         pEdidData[0x44] == 0x10 &&
2058         pEdidData[0x45] == 0x00 &&
2059         pEdidData[0x46] == 0x00 &&
2060         pEdidData[0x47] == 0x18 &&
2061         pEdidData[0x7f] == 0x2e) {
2062 
2063         pEdidData[0x36] = 0x12;
2064         pEdidData[0x37] = 0x1b;
2065         pEdidData[0x38] = 0x56;
2066         pEdidData[0x39] = 0x42;
2067         pEdidData[0x3a] = 0x50;
2068         pEdidData[0x3b] = 0x00;
2069         pEdidData[0x3c] = 0x26;
2070         pEdidData[0x3d] = 0x30;
2071         pEdidData[0x3e] = 0x20;
2072         pEdidData[0x3f] = 0x18;
2073         pEdidData[0x40] = 0x34;
2074         pEdidData[0x41] = 0x00;
2075         pEdidData[0x42] = 0x58;
2076         pEdidData[0x43] = 0xc1;
2077         pEdidData[0x44] = 0x10;
2078         pEdidData[0x45] = 0x00;
2079         pEdidData[0x46] = 0x00;
2080         pEdidData[0x47] = 0x18;
2081         pEdidData[0x7f] = 0x5f;
2082 
2083         nvEvoLogInfoString(pInfoString, "Fixed invalid mode for 1366x768");
2084     }
2085 }
2086 
2087 /*
2088  * CreateParsedEdidFromNVT_TIMING() - Puts modetiming data from RM into an EDID format
2089  */
2090 static void CreateParsedEdidFromNVT_TIMING(
2091     NVT_TIMING *pTimings,
2092     NvU8 bpc,
2093     NVParsedEdidEvoPtr pParsedEdid)
2094 {
2095     nvkms_memset(pParsedEdid, 0, sizeof(*pParsedEdid));
2096     pParsedEdid->info.total_timings = 1;
2097     nvkms_memcpy(&pParsedEdid->info.timing[0], pTimings, sizeof(*pTimings));
2098     pParsedEdid->info.timing[0].etc.status = NVT_STATUS_CUST;
2099     pParsedEdid->info.u.feature_ver_1_4_digital.continuous_frequency = FALSE;
2100     pParsedEdid->info.version = NVT_EDID_VER_1_4;
2101     pParsedEdid->info.input.isDigital = TRUE;
2102     pParsedEdid->info.input.u.digital.bpc = bpc;
2103     pParsedEdid->limits.min_h_rate_hz = 1;
2104     pParsedEdid->limits.min_v_rate_hzx1k = 1;
2105     pParsedEdid->limits.max_h_rate_hz = NV_U32_MAX;
2106     pParsedEdid->limits.max_v_rate_hzx1k = NV_U32_MAX;
2107     pParsedEdid->valid = TRUE;
2108 }
2109 
2110 /*
2111  * PatchAndParseEdid() - use the nvtiming library to parse the EDID data.  The
2112  * EDID data provided in the 'pEdid' argument may be patched or modified.
2113  */
2114 
2115 static void PatchAndParseEdid(
2116     const NVDpyEvoRec *pDpyEvo,
2117     NVEdidPtr pEdid,
2118     NVParsedEdidEvoPtr pParsedEdid,
2119     NVEvoInfoStringPtr pInfoString)
2120 {
2121     int i;
2122     NVT_STATUS status;
2123     NvU32 edidSize;
2124 
2125     if (pEdid->buffer == NULL || pEdid->length == 0) {
2126         return;
2127     }
2128 
2129     nvkms_memset(pParsedEdid, 0, sizeof(*pParsedEdid));
2130 
2131     PrePatchEdid(pDpyEvo, pEdid, pInfoString);
2132 
2133     /* parse the majority of information from the EDID */
2134 
2135     status = NvTiming_ParseEDIDInfo(pEdid->buffer, pEdid->length,
2136                                     &pParsedEdid->info);
2137 
2138     if (status != NVT_STATUS_SUCCESS) {
2139         return;
2140     }
2141 
2142     /* interpret the frequency range limits from the EDID */
2143 
2144     NvTiming_CalculateEDIDLimits(&pParsedEdid->info, &pParsedEdid->limits);
2145 
2146     /* get the user-friendly monitor name */
2147 
2148     NvTiming_GetMonitorName(&pParsedEdid->info,
2149                             (NvU8 *) &pParsedEdid->monitorName);
2150     nvAssert(pParsedEdid->monitorName[0] != '\0');
2151 
2152     /* find the serial number string */
2153 
2154     pParsedEdid->serialNumberString[0] = '\0';
2155 
2156     for (i = 0; i < NVT_EDID_MAX_LONG_DISPLAY_DESCRIPTOR; i++) {
2157         if (pParsedEdid->info.ldd[i].tag == NVT_EDID_DISPLAY_DESCRIPTOR_DPSN) {
2158             nvkms_strncpy(
2159                 pParsedEdid->serialNumberString,
2160                 (const char *)pParsedEdid->info.ldd[i].u.serial_number.str,
2161                 sizeof(pParsedEdid->serialNumberString));
2162             pParsedEdid->serialNumberString[
2163                 sizeof(pParsedEdid->serialNumberString) - 1] = '\0';
2164             break;
2165         }
2166     }
2167 
2168 
2169     for (i = 0; i < pParsedEdid->info.total_timings; i++) {
2170         NVT_TIMING *pTiming = &pParsedEdid->info.timing[i];
2171 
2172         /* patch up RRx1k for 640x480@60Hz */
2173 
2174         if (IsEdid640x480_60_NVT_TIMING(pTiming)) {
2175             pTiming->etc.rrx1k = 59940;
2176         }
2177 
2178         /*
2179          * Invalidate modes that require pixel repetition (i.e., modes
2180          * that don't support Pixel Repetition 0).  See bug 1459376.
2181          */
2182 
2183         nvAssert(pTiming->etc.rep != 0);
2184 
2185         if ((pTiming->etc.rep & NVBIT(0)) == 0) {
2186             pTiming->etc.status = 0;
2187         }
2188     }
2189 
2190     pParsedEdid->valid = TRUE;
2191 
2192     /* resize the EDID buffer, if necessary */
2193 
2194     edidSize = NVT_EDID_ACTUAL_SIZE(&pParsedEdid->info);
2195 
2196     if (edidSize < pEdid->length) {
2197         NvU8 *pEdidData = nvAlloc(edidSize);
2198 
2199         if (pEdidData != NULL) {
2200             nvkms_memcpy(pEdidData, pEdid->buffer, edidSize);
2201 
2202             nvFree(pEdid->buffer);
2203 
2204             pEdid->buffer = pEdidData;
2205             pEdid->length = edidSize;
2206         }
2207     }
2208 }
2209 
2210 
2211 /*!
2212  * Assign NVDpyEvoRec::name.
2213  *
2214  * The name has the form:
2215  *
2216  *  "edidName (typeName-N.dpAddress)"
2217  *
2218  * If edidName is unavailable, then it, and the parentheses are omitted:
2219  *
2220  *  "typeName-N.dpAddress"
2221  *  "typeName-N"
2222  *
2223  * if dpAddress is unavailable, then the ".dpAddress" is omitted:
2224  *
2225  *  "edidName (typeName-N)"
2226  *  "typeName-N"
2227  */
2228 static void AssignDpyEvoName(NVDpyEvoPtr pDpyEvo)
2229 {
2230     const NVConnectorEvoRec *pConnectorEvo = pDpyEvo->pConnectorEvo;
2231     const char *edidName = "";
2232     const char *openParen = "";
2233     const char *closeParen = "";
2234     const char *dpAddress = "";
2235     const char *dpAddressSeparator = "";
2236 
2237     if (pDpyEvo->parsedEdid.valid &&
2238         pDpyEvo->parsedEdid.monitorName[0] != '\0') {
2239         edidName = pDpyEvo->parsedEdid.monitorName;
2240         openParen = " (";
2241         closeParen = ")";
2242     }
2243 
2244     if (pDpyEvo->dp.addressString != NULL) {
2245         dpAddress = pDpyEvo->dp.addressString;
2246         dpAddressSeparator = ".";
2247     }
2248 
2249     nvkms_snprintf(pDpyEvo->name, sizeof(pDpyEvo->name),
2250                    "%s%s%s%s%s%s",
2251                    edidName,
2252                    openParen,
2253                    pConnectorEvo->name,
2254                    dpAddressSeparator,
2255                    dpAddress,
2256                    closeParen);
2257 
2258     pDpyEvo->name[sizeof(pDpyEvo->name) - 1] = '\0';
2259 }
2260 
2261 enum NvKmsDpyAttributeDigitalSignalValue
2262 nvGetDefaultDpyAttributeDigitalSignalValue(const NVConnectorEvoRec *pConnectorEvo)
2263 {
2264     enum NvKmsDpyAttributeDigitalSignalValue signal =
2265         NV_KMS_DPY_ATTRIBUTE_DIGITAL_SIGNAL_LVDS;
2266 
2267     if (pConnectorEvo->legacyType == NV0073_CTRL_SPECIFIC_DISPLAY_TYPE_DFP) {
2268         if (nvConnectorUsesDPLib(pConnectorEvo)) {
2269             signal = NV_KMS_DPY_ATTRIBUTE_DIGITAL_SIGNAL_DISPLAYPORT;
2270         } else {
2271             nvAssert((pConnectorEvo->or.type == NV0073_CTRL_SPECIFIC_OR_TYPE_SOR) ||
2272                      (pConnectorEvo->or.type == NV0073_CTRL_SPECIFIC_OR_TYPE_DSI));
2273 
2274             if (pConnectorEvo->or.protocol ==
2275                 NV0073_CTRL_SPECIFIC_OR_PROTOCOL_SOR_LVDS_CUSTOM) {
2276                 signal = NV_KMS_DPY_ATTRIBUTE_DIGITAL_SIGNAL_LVDS;
2277             } else if (pConnectorEvo->or.protocol ==
2278                        NV0073_CTRL_SPECIFIC_OR_PROTOCOL_DSI) {
2279                 signal = NV_KMS_DPY_ATTRIBUTE_DIGITAL_SIGNAL_DSI;
2280             } else {
2281                 // May be later changed to HDMI_FRL at modeset time.
2282                 signal = NV_KMS_DPY_ATTRIBUTE_DIGITAL_SIGNAL_TMDS;
2283             }
2284         }
2285     }
2286 
2287     return signal;
2288 }
2289 
2290 NVDpyEvoPtr nvAllocDpyEvo(NVDispEvoPtr pDispEvo,
2291                           NVConnectorEvoPtr pConnectorEvo,
2292                           NVDpyId dpyId, const char *dpAddress)
2293 {
2294     NVDpyEvoPtr pDpyEvo;
2295 
2296     pDpyEvo = nvCalloc(1, sizeof(*pDpyEvo));
2297 
2298     if (pDpyEvo == NULL) {
2299         return NULL;
2300     }
2301 
2302     pDpyEvo->pDispEvo = pDispEvo;
2303     pDpyEvo->pConnectorEvo = pConnectorEvo;
2304     pDpyEvo->apiHead = NV_INVALID_HEAD;
2305     pDpyEvo->id = dpyId;
2306 
2307     nvListAdd(&pDpyEvo->dpyListEntry, &pDispEvo->dpyList);
2308 
2309     if (dpAddress) {
2310         pDpyEvo->dp.addressString = nvStrDup(dpAddress);
2311         pDispEvo->displayPortMSTIds =
2312             nvAddDpyIdToDpyIdList(dpyId, pDispEvo->displayPortMSTIds);
2313 
2314         if (!nvConnectorIsDPSerializer(pConnectorEvo)) {
2315             pDispEvo->dynamicDpyIds =
2316                 nvAddDpyIdToDpyIdList(dpyId, pDispEvo->dynamicDpyIds);
2317 	}
2318     }
2319 
2320     AssignDpyEvoName(pDpyEvo);
2321 
2322     nvDpyProbeMaxPixelClock(pDpyEvo);
2323 
2324     pDpyEvo->requestedDithering.state =
2325         NV_KMS_DPY_ATTRIBUTE_REQUESTED_DITHERING_AUTO;
2326     pDpyEvo->requestedDithering.mode =
2327         NV_KMS_DPY_ATTRIBUTE_REQUESTED_DITHERING_MODE_AUTO;
2328     pDpyEvo->requestedDithering.depth =
2329         NV_KMS_DPY_ATTRIBUTE_REQUESTED_DITHERING_DEPTH_AUTO;
2330 
2331     // Initialize DP link rate and lane count to sane values.
2332     // This is normally done in nvDPLibUpdateDpyLinkConfiguration,
2333     // but do it here as well in case we query flat panel properties for
2334     // screenless DP devices.
2335     if (nvConnectorUsesDPLib(pConnectorEvo)) {
2336         pDpyEvo->dp.linkRate = 0;
2337         pDpyEvo->dp.laneCount = NV0073_CTRL_CMD_DP_GET_LINK_CONFIG_LANE_COUNT_1;
2338         pDpyEvo->dp.connectorType = NV_KMS_DPY_ATTRIBUTE_DISPLAYPORT_CONNECTOR_TYPE_UNKNOWN;
2339         pDpyEvo->dp.sinkIsAudioCapable = FALSE;
2340     }
2341 
2342     pDpyEvo->requestedColorSpace =
2343         NV_KMS_DPY_ATTRIBUTE_REQUESTED_COLOR_SPACE_RGB;
2344     pDpyEvo->requestedColorRange =
2345         NV_KMS_DPY_ATTRIBUTE_COLOR_RANGE_FULL;
2346 
2347     pDpyEvo->currentAttributes = NV_EVO_DEFAULT_ATTRIBUTES_SET;
2348     pDpyEvo->currentAttributes.digitalSignal =
2349         nvGetDefaultDpyAttributeDigitalSignalValue(pConnectorEvo);
2350 
2351     DpyGetStaticDfpProperties(pDpyEvo);
2352 
2353     return pDpyEvo;
2354 }
2355 
2356 
2357 void nvFreeDpyEvo(NVDispEvoPtr pDispEvo, NVDpyEvoPtr pDpyEvo)
2358 {
2359     nvCancelSDRTransitionTimer(pDpyEvo);
2360 
2361     DpyDisconnectEvo(pDpyEvo);
2362 
2363     // Let the DP library host implementation handle deleting a pDpy as if the
2364     // library had notified it of a lost device.
2365     nvDPDpyFree(pDpyEvo);
2366     nvAssert(!pDpyEvo->dp.pDpLibDevice);
2367 
2368     pDispEvo->validDisplays =
2369         nvDpyIdListMinusDpyId(pDispEvo->validDisplays, pDpyEvo->id);
2370 
2371     pDispEvo->displayPortMSTIds =
2372         nvDpyIdListMinusDpyId(pDispEvo->displayPortMSTIds, pDpyEvo->id);
2373     pDispEvo->dynamicDpyIds =
2374         nvDpyIdListMinusDpyId(pDispEvo->dynamicDpyIds, pDpyEvo->id);
2375 
2376     nvListDel(&pDpyEvo->dpyListEntry);
2377 
2378     nvFree(pDpyEvo->dp.addressString);
2379     nvFree(pDpyEvo);
2380 }
2381 
2382 
2383 /*!
2384  * Return the pConnectorEvo associated with the given (static) display ID.
2385  *
2386  * XXX[DP] not valid for DP monitors, the connector will be known before
2387  *         initialization so this will not be needed.
2388  *
2389  * \param[in]  pDisp    The pDisp on which to search for the pConnector.
2390  * \param[in]  dpyId    The ID of the connector to search for.
2391  *
2392  * \return  The pConnectorEvo from pDisp that matches the ID, or NULL if
2393  *          no connector is found.
2394  */
2395 NVConnectorEvoPtr nvGetConnectorFromDisp(NVDispEvoPtr pDispEvo, NVDpyId dpyId)
2396 {
2397     NVConnectorEvoPtr pConnectorEvo;
2398 
2399     nvAssert(nvDpyIdIsInDpyIdList(dpyId, pDispEvo->connectorIds));
2400 
2401     FOR_ALL_EVO_CONNECTORS(pConnectorEvo, pDispEvo) {
2402         if (nvDpyIdsAreEqual(dpyId, pConnectorEvo->displayId)) {
2403             return pConnectorEvo;
2404         }
2405     }
2406 
2407     nvAssert(!"Failed to find pDpy's connector!");
2408     return NULL;
2409 }
2410 
2411 void nvDpyAssignSDRInfoFramePayload(NVT_HDR_INFOFRAME_PAYLOAD *pPayload)
2412 {
2413     nvkms_memset(pPayload, 0, sizeof(*pPayload));
2414     pPayload->eotf = NVT_CEA861_HDR_INFOFRAME_EOTF_SDR_GAMMA;
2415     pPayload->static_metadata_desc_id = NVT_CEA861_STATIC_METADATA_SM0;
2416 }
2417 
2418 static void UpdateDpHDRInfoFrame(const NVDispEvoRec *pDispEvo, const NvU32 head)
2419 {
2420     NvU32 ret;
2421     const NVDispHeadStateEvoRec *pHeadState =
2422                                 &pDispEvo->headState[head];
2423     NVDevEvoPtr pDevEvo = pDispEvo->pDevEvo;
2424 
2425     NV0073_CTRL_SPECIFIC_SET_OD_PACKET_PARAMS params = {
2426         .subDeviceInstance = pDispEvo->displayOwner,
2427         .displayId = pHeadState->activeRmId
2428     };
2429 
2430     // DPSDP_DESCRIPTOR has a (dataSize, hb, db) layout, while
2431     // NV0073_CTRL_SPECIFIC_SET_OD_PACKET_PARAMS.aPacket needs to contain
2432     // (hb, db) without dataSize, so this makes sdp->hb align with aPacket.
2433     DPSDP_DESCRIPTOR *sdp =
2434         (DPSDP_DESCRIPTOR *)(params.aPacket -
2435         offsetof(DPSDP_DESCRIPTOR, hb));
2436 
2437     nvAssert((void *)&sdp->hb == (void *)params.aPacket);
2438 
2439     sdp->hb.hb0 = 0;
2440     sdp->hb.hb1 = dp_pktType_DynamicRangeMasteringInfoFrame;
2441     sdp->hb.hb2 = DP_INFOFRAME_SDP_V1_3_NON_AUDIO_SIZE - 1;
2442     sdp->hb.hb3 = DP_INFOFRAME_SDP_V1_3_VERSION <<
2443                       DP_INFOFRAME_SDP_V1_3_HB3_VERSION_SHIFT;
2444 
2445     sdp->db.db0 = NVT_VIDEO_INFOFRAME_VERSION_1;
2446     sdp->db.db1 = sizeof(NVT_HDR_INFOFRAME) - sizeof(NVT_INFOFRAME_HEADER);
2447 
2448     nvAssert((sizeof(sdp->db) - 2) >= sizeof(NVT_HDR_INFOFRAME_PAYLOAD));
2449 
2450     if (pHeadState->hdrInfoFrame.state == NVKMS_HDR_INFOFRAME_STATE_ENABLED) {
2451         NVT_HDR_INFOFRAME_PAYLOAD *payload =
2452             (NVT_HDR_INFOFRAME_PAYLOAD *) &sdp->db.db2;
2453 
2454         payload->eotf = pHeadState->hdrInfoFrame.eotf;
2455 
2456         payload->static_metadata_desc_id = NVT_CEA861_STATIC_METADATA_SM0;
2457 
2458         // payload->type1 = static metadata
2459         nvAssert(sizeof(NVT_HDR_INFOFRAME_MASTERING_DATA) ==
2460                  (sizeof(struct NvKmsHDRStaticMetadata)));
2461         nvkms_memcpy(&payload->type1,
2462                      &pHeadState->hdrInfoFrame.staticMetadata,
2463                      sizeof(NVT_HDR_INFOFRAME_MASTERING_DATA));
2464 
2465         params.transmitControl =
2466             DRF_DEF(0073_CTRL_SPECIFIC, _SET_OD_PACKET_TRANSMIT_CONTROL, _SINGLE_FRAME, _DISABLE);
2467     } else if (pHeadState->hdrInfoFrame.state ==
2468                NVKMS_HDR_INFOFRAME_STATE_TRANSITIONING) {
2469         nvDpyAssignSDRInfoFramePayload((NVT_HDR_INFOFRAME_PAYLOAD *) &sdp->db.db2);
2470 
2471         params.transmitControl =
2472             DRF_DEF(0073_CTRL_SPECIFIC, _SET_OD_PACKET_TRANSMIT_CONTROL, _SINGLE_FRAME, _DISABLE);
2473     } else {
2474         nvAssert(pHeadState->hdrInfoFrame.state == NVKMS_HDR_INFOFRAME_STATE_DISABLED);
2475 
2476         nvDpyAssignSDRInfoFramePayload((NVT_HDR_INFOFRAME_PAYLOAD *) &sdp->db.db2);
2477 
2478         params.transmitControl =
2479             DRF_DEF(0073_CTRL_SPECIFIC, _SET_OD_PACKET_TRANSMIT_CONTROL, _SINGLE_FRAME, _ENABLE);
2480     }
2481 
2482     params.transmitControl |=
2483         DRF_DEF(0073_CTRL_SPECIFIC, _SET_OD_PACKET_TRANSMIT_CONTROL, _OTHER_FRAME,  _DISABLE) |
2484         DRF_DEF(0073_CTRL_SPECIFIC, _SET_OD_PACKET_TRANSMIT_CONTROL, _ENABLE,       _YES)     |
2485         DRF_DEF(0073_CTRL_SPECIFIC, _SET_OD_PACKET_TRANSMIT_CONTROL, _GEN_INFOFRAME_MODE, _INFOFRAME1) |
2486         DRF_DEF(0073_CTRL_SPECIFIC, _SET_OD_PACKET_TRANSMIT_CONTROL, _ON_HBLANK,    _DISABLE);
2487 
2488     params.packetSize = sizeof(sdp->hb) + sizeof(NVT_HDR_INFOFRAME);
2489 
2490     ret = nvRmApiControl(nvEvoGlobal.clientHandle,
2491                          pDevEvo->displayCommonHandle,
2492                          NV0073_CTRL_CMD_SPECIFIC_SET_OD_PACKET,
2493                          &params,
2494                          sizeof(params));
2495 
2496     if (ret != NVOS_STATUS_SUCCESS) {
2497         nvAssert(!"NV0073_CTRL_CMD_SPECIFIC_SET_OD_PACKET failed");
2498     }
2499 }
2500 
2501 /*
2502  * Construct the DP 1.3 YUV420 infoframe, and toggle it on or off based on
2503  * whether or not YUV420 mode is in use.
2504  */
2505 static void UpdateDpYUV420InfoFrame(const NVDispEvoRec *pDispEvo,
2506                                     const NvU32 head,
2507                                     const NVAttributesSetEvoRec *pAttributesSet)
2508 {
2509     const NVDispHeadStateEvoRec *pHeadState =
2510                                 &pDispEvo->headState[head];
2511     NV0073_CTRL_SPECIFIC_SET_OD_PACKET_PARAMS params = { 0 };
2512     NVDevEvoPtr pDevEvo = pDispEvo->pDevEvo;
2513     NvU32 ret;
2514 
2515     params.subDeviceInstance = pDispEvo->displayOwner;
2516     params.displayId = pHeadState->activeRmId;
2517 
2518     if (pAttributesSet->colorSpace ==
2519         NV_KMS_DPY_ATTRIBUTE_CURRENT_COLOR_SPACE_YCbCr420) {
2520 
2521         // DPSDP_DP_VSC_SDP_DESCRIPTOR has a (dataSize, hb, db) layout, while
2522         // NV0073_CTRL_SPECIFIC_SET_OD_PACKET_PARAMS.aPacket needs to contain
2523         // (hb, db) without dataSize, so this makes sdp->hb align with aPacket.
2524         DPSDP_DP_VSC_SDP_DESCRIPTOR *sdp =
2525             (DPSDP_DP_VSC_SDP_DESCRIPTOR *)(params.aPacket -
2526             offsetof(DPSDP_DP_VSC_SDP_DESCRIPTOR, hb));
2527 
2528         nvAssert((void *)&sdp->hb == (void *)params.aPacket);
2529 
2530         // Header
2531         // Per DP1.3 spec
2532         sdp->hb.hb0 = 0;
2533         sdp->hb.hb1 = SDP_PACKET_TYPE_VSC;
2534         sdp->hb.revisionNumber = SDP_VSC_REVNUM_STEREO_PSR2_COLOR;
2535         sdp->hb.numValidDataBytes = SDP_VSC_VALID_DATA_BYTES_PSR2_COLOR;
2536 
2537         sdp->db.stereoInterface = 0;
2538         sdp->db.psrState = 0;
2539         sdp->db.contentType = SDP_VSC_CONTENT_TYPE_GRAPHICS;
2540         sdp->db.pixEncoding = SDP_VSC_PIX_ENC_YCBCR420;
2541         sdp->db.colorimetryFormat = SDP_VSC_COLOR_FMT_YCBCR_COLORIMETRY_ITU_R_BT709;
2542 
2543         switch (pAttributesSet->colorBpc) {
2544             case NV_KMS_DPY_ATTRIBUTE_CURRENT_COLOR_BPC_10:
2545                 sdp->db.bitDepth = SDP_VSC_BIT_DEPTH_YCBCR_10BPC;
2546                 break;
2547             case NV_KMS_DPY_ATTRIBUTE_CURRENT_COLOR_BPC_8:
2548                 sdp->db.bitDepth = SDP_VSC_BIT_DEPTH_YCBCR_8BPC;
2549                 break;
2550             default:
2551                 nvAssert(!"Invalid pixelDepth value");
2552                 break;
2553         }
2554 
2555         switch (pAttributesSet->colorRange) {
2556             case NV_KMS_DPY_ATTRIBUTE_COLOR_RANGE_FULL:
2557                 sdp->db.dynamicRange = SDP_VSC_DYNAMIC_RANGE_VESA;
2558                 break;
2559             case NV_KMS_DPY_ATTRIBUTE_COLOR_RANGE_LIMITED:
2560                 sdp->db.dynamicRange = SDP_VSC_DYNAMIC_RANGE_CEA;
2561                 break;
2562             default:
2563                 nvAssert(!"Invalid colorRange value");
2564                 break;
2565         }
2566 
2567         params.packetSize = sizeof(sdp->hb) + sdp->hb.numValidDataBytes;
2568 
2569         params.transmitControl =
2570             DRF_DEF(0073_CTRL_SPECIFIC, _SET_OD_PACKET_TRANSMIT_CONTROL, _ENABLE, _YES) |
2571             DRF_DEF(0073_CTRL_SPECIFIC, _SET_OD_PACKET_TRANSMIT_CONTROL, _OTHER_FRAME, _DISABLE) |
2572             DRF_DEF(0073_CTRL_SPECIFIC, _SET_OD_PACKET_TRANSMIT_CONTROL, _SINGLE_FRAME, _DISABLE) |
2573             DRF_DEF(0073_CTRL_SPECIFIC, _SET_OD_PACKET_TRANSMIT_CONTROL, _ON_HBLANK, _DISABLE);
2574     } else {
2575         params.transmitControl =
2576             DRF_DEF(0073_CTRL_SPECIFIC, _SET_OD_PACKET_TRANSMIT_CONTROL, _ENABLE, _NO);
2577     }
2578 
2579     ret = nvRmApiControl(nvEvoGlobal.clientHandle,
2580                          pDevEvo->displayCommonHandle,
2581                          NV0073_CTRL_CMD_SPECIFIC_SET_OD_PACKET,
2582                          &params,
2583                          sizeof(params));
2584 
2585     if (ret != NVOS_STATUS_SUCCESS) {
2586         nvAssert(!"NV0073_CTRL_CMD_SPECIFIC_SET_OD_PACKET failed");
2587     }
2588 }
2589 
2590 static void UpdateDpInfoFrames(const NVDispEvoRec *pDispEvo,
2591                                const NvU32 head,
2592                                const NVAttributesSetEvoRec *pAttributesSet)
2593 {
2594     UpdateDpHDRInfoFrame(pDispEvo, head);
2595 
2596     UpdateDpYUV420InfoFrame(pDispEvo, head, pAttributesSet);
2597 }
2598 
2599 void nvCancelSDRTransitionTimer(NVDpyEvoRec *pDpyEvo)
2600 {
2601     nvkms_free_timer(pDpyEvo->hdrToSdrTransitionTimer);
2602     pDpyEvo->hdrToSdrTransitionTimer = NULL;
2603 }
2604 
2605 static void SDRTransition(void *dataPtr, NvU32 dataU32)
2606 {
2607     NvU32 head;
2608     NVDpyEvoRec *pDpyEvo = dataPtr;
2609     NVDispEvoRec *pDispEvo = pDpyEvo->pDispEvo;
2610     NVDispApiHeadStateEvoRec *pApiHeadState =
2611         &pDispEvo->apiHeadState[pDpyEvo->apiHead];
2612 
2613     nvCancelSDRTransitionTimer(pDpyEvo);
2614 
2615     nvAssert(pApiHeadState->hwHeadsMask != 0);
2616 
2617     FOR_EACH_EVO_HW_HEAD_IN_MASK(pApiHeadState->hwHeadsMask, head) {
2618         NVDispHeadStateEvoRec *pHeadState =
2619             &pDispEvo->headState[head];
2620         nvAssert(pHeadState->hdrInfoFrame.state ==
2621                  NVKMS_HDR_INFOFRAME_STATE_TRANSITIONING);
2622         pHeadState->hdrInfoFrame.state = NVKMS_HDR_INFOFRAME_STATE_DISABLED;
2623     }
2624 
2625     nvUpdateInfoFrames(pDpyEvo);
2626 }
2627 
2628 static
2629 void ScheduleSDRTransitionTimer(NVDpyEvoRec *pDpyEvo)
2630 {
2631     if (pDpyEvo->hdrToSdrTransitionTimer) {
2632         return;
2633     }
2634 
2635     pDpyEvo->hdrToSdrTransitionTimer =
2636         nvkms_alloc_timer(SDRTransition,
2637                           pDpyEvo,
2638                           0,
2639                           2000000 /* 2 seconds */);
2640 }
2641 
2642 void nvUpdateInfoFrames(NVDpyEvoRec *pDpyEvo)
2643 {
2644     NVDispEvoRec *pDispEvo = pDpyEvo->pDispEvo;
2645     const NVDispApiHeadStateEvoRec *pApiHeadState;
2646     const NVDispHeadStateEvoRec *pHeadState;
2647     NvU32 head;
2648 
2649     if (pDpyEvo->apiHead == NV_INVALID_HEAD) {
2650         return;
2651     }
2652     pApiHeadState = &pDispEvo->apiHeadState[pDpyEvo->apiHead];
2653 
2654     nvAssert((pApiHeadState->hwHeadsMask) != 0x0 &&
2655              (nvDpyIdIsInDpyIdList(pDpyEvo->id, pApiHeadState->activeDpys)));
2656 
2657     head = nvGetPrimaryHwHead(pDispEvo, pDpyEvo->apiHead);
2658 
2659     nvAssert(head != NV_INVALID_HEAD);
2660 
2661     pHeadState = &pDispEvo->headState[head];
2662 
2663     if (nvConnectorUsesDPLib(pDpyEvo->pConnectorEvo)) {
2664         UpdateDpInfoFrames(pDispEvo, head, &pApiHeadState->attributes);
2665     } else {
2666         nvUpdateHdmiInfoFrames(pDispEvo,
2667                                head,
2668                                &pApiHeadState->attributes,
2669                                &pApiHeadState->infoFrame,
2670                                pDpyEvo);
2671     }
2672 
2673     if (pHeadState->hdrInfoFrame.state == NVKMS_HDR_INFOFRAME_STATE_ENABLED) {
2674         nvCancelSDRTransitionTimer(pDpyEvo);
2675     } else if (pHeadState->hdrInfoFrame.state ==
2676                NVKMS_HDR_INFOFRAME_STATE_TRANSITIONING) {
2677         ScheduleSDRTransitionTimer(pDpyEvo);
2678     }
2679 }
2680 
2681 /*!
2682  * nvDpyRequiresDualLinkEvo() - Returns whether or not the given mode exceeds
2683  * the maximum single TMDS link pixel clock.
2684  *
2685  * \param[in] pDpyEvo display to check the maximum single link pixel clock
2686  *
2687  * \param[in] pTimings mode timings to check pixel clock
2688  *
2689  * \return TRUE if pixel clock exceeds display's maximum single link pixel
2690  * clock
2691  */
2692 NvBool nvDpyRequiresDualLinkEvo(const NVDpyEvoRec *pDpyEvo,
2693                                 const NVHwModeTimingsEvo *pTimings)
2694 {
2695     const NvU32 pixelClock = (pTimings->yuv420Mode == NV_YUV420_MODE_HW) ?
2696         (pTimings->pixelClock / 2) : pTimings->pixelClock;
2697 
2698     // Dual link HDMI is not possible.
2699     nvAssert(!(nvDpyIsHdmiEvo(pDpyEvo) &&
2700                (pixelClock > pDpyEvo->maxSingleLinkPixelClockKHz)));
2701     return (pixelClock > pDpyEvo->maxSingleLinkPixelClockKHz);
2702 }
2703 
2704 /*!
2705  * Return the NVDpyEvoPtr that corresponds to the given dpyId, on the
2706  * given NVDispEvoPtr, or NULL if no matching NVDpyEvoPtr can be
2707  * found.
2708  */
2709 NVDpyEvoPtr nvGetDpyEvoFromDispEvo(const NVDispEvoRec *pDispEvo, NVDpyId dpyId)
2710 {
2711     NVDpyEvoPtr pDpyEvo;
2712 
2713     FOR_ALL_EVO_DPYS(pDpyEvo, nvAddDpyIdToEmptyDpyIdList(dpyId), pDispEvo) {
2714         return pDpyEvo;
2715     }
2716 
2717     return NULL;
2718 }
2719 
2720 /*
2721  * Find or create a pDpy with a given root connector and topology path.
2722  */
2723 NVDpyEvoPtr nvGetDPMSTDpyEvo(NVConnectorEvoPtr pConnectorEvo,
2724                              const char *address, NvBool *pDynamicDpyCreated)
2725 {
2726     NVDispEvoPtr pDispEvo = pConnectorEvo->pDispEvo;
2727     NVDpyEvoPtr pDpyEvo = NULL, pTmpDpyEvo;
2728     NVDpyId dpyId;
2729 
2730     // Look for a pDpyEvo on pConnectorEvo whose dp address matches.
2731     FOR_ALL_EVO_DPYS(pTmpDpyEvo, pDispEvo->validDisplays, pDispEvo) {
2732         if (pTmpDpyEvo->pConnectorEvo != pConnectorEvo) {
2733             continue;
2734         }
2735         if (pTmpDpyEvo->dp.addressString == NULL) {
2736             continue;
2737         }
2738         if (nvkms_strcmp(pTmpDpyEvo->dp.addressString, address) == 0) {
2739             pDpyEvo = pTmpDpyEvo;
2740             goto done;
2741         }
2742     }
2743 
2744     // Find a display ID that is not used on this GPU.
2745     dpyId = nvNewDpyId(pDispEvo->validDisplays);
2746     if (nvDpyIdIsInvalid(dpyId)) {
2747         nvEvoLogDisp(pDispEvo, EVO_LOG_ERROR,
2748                      "Failed to allocate a display ID for device %s.%s",
2749                      pConnectorEvo->name,
2750                      address);
2751         goto done;
2752     }
2753 
2754     // Create a new pDpy for this address.
2755     pDpyEvo = nvAllocDpyEvo(pDispEvo, pConnectorEvo, dpyId, address);
2756     if (pDpyEvo == NULL) {
2757         nvEvoLogDisp(pDispEvo, EVO_LOG_ERROR,
2758                      "Failed to create a display device object for %s-%u.%s",
2759                      NvKmsConnectorTypeString(pConnectorEvo->type),
2760                      pConnectorEvo->typeIndex,
2761                      address);
2762         goto done;
2763     }
2764 
2765     pDispEvo->validDisplays =
2766         nvAddDpyIdToDpyIdList(dpyId, pDispEvo->validDisplays);
2767 
2768     *pDynamicDpyCreated = TRUE;
2769 
2770 done:
2771     return pDpyEvo;
2772 }
2773 
2774 /*!
2775  * Return a string with a comma-separated list of dpy names, for all
2776  * dpys in dpyIdList.
2777  *
2778  * If there are no dpys in the dpyIdList, return "none".
2779  *
2780  * The string is dynamically allocated and should be freed by the caller.
2781  *
2782  * Return NULL if an allocation failure occurs.
2783  */
2784 char *nvGetDpyIdListStringEvo(NVDispEvoPtr pDispEvo,
2785                               const NVDpyIdList dpyIdList)
2786 {
2787     NVDpyEvoPtr pDpyEvo;
2788     char *listString = NULL;
2789     NvU32 lengths[NV_DPY_ID_MAX_DPYS_IN_LIST];
2790     NvU32 totalLength = 0;
2791     NvU32 currentOffset;
2792     NvU32 index;
2793 
2794     index = 0;
2795     FOR_ALL_EVO_DPYS(pDpyEvo, dpyIdList, pDispEvo) {
2796 
2797         nvAssert(index < ARRAY_LEN(lengths));
2798 
2799         lengths[index] = nvkms_strlen(pDpyEvo->name);
2800 
2801         totalLength += lengths[index];
2802 
2803         if (index != 0) {
2804             totalLength += 2; /* nvkms_strlen(", ") */
2805         }
2806 
2807         index++;
2808     }
2809 
2810     totalLength += 1; /* for nul terminator */
2811 
2812     if (index == 0) {
2813         return nvStrDup("none");
2814     }
2815 
2816     listString = nvAlloc(totalLength);
2817 
2818     if (listString == NULL) {
2819         return NULL;
2820     }
2821 
2822     index = 0;
2823     currentOffset = 0;
2824 
2825     FOR_ALL_EVO_DPYS(pDpyEvo, dpyIdList, pDispEvo) {
2826 
2827         if (index != 0) {
2828             listString[currentOffset] = ',';
2829             listString[currentOffset+1] = ' ';
2830             currentOffset += 2;
2831         }
2832 
2833         nvkms_memcpy(listString + currentOffset, pDpyEvo->name, lengths[index]);
2834 
2835         currentOffset += lengths[index];
2836 
2837         index++;
2838     }
2839 
2840     listString[currentOffset] = '\0';
2841     currentOffset += 1;
2842 
2843     nvAssert(currentOffset == totalLength);
2844 
2845     return listString;
2846 }
2847 
2848 NvBool nvDpyGetDynamicData(
2849     NVDpyEvoPtr pDpyEvo,
2850     struct NvKmsQueryDpyDynamicDataParams *pParams)
2851 {
2852     NVDispEvoPtr pDispEvo = pDpyEvo->pDispEvo;
2853     struct NvKmsQueryDpyDynamicDataRequest *pRequest = &pParams->request;
2854     struct NvKmsQueryDpyDynamicDataReply *pReply = &pParams->reply;
2855     NVConnectorEvoPtr pConnectorEvo = pDpyEvo->pConnectorEvo;
2856     NVDpyIdList connectedList;
2857     NVDpyIdList oneDpyIdList = nvAddDpyIdToEmptyDpyIdList(pDpyEvo->id);
2858     NVDpyOverridePtr pDpyOverride = nvDpyEvoGetOverride(pDpyEvo);
2859 
2860     nvkms_memset(pReply, 0, sizeof(*pReply));
2861 
2862     if (pDpyOverride != NULL) {
2863         if (pDpyOverride->connected && !pRequest->forceDisconnected) {
2864             /*
2865              * If display is overridden as connected, treat the request as if it
2866              * had both forceConnected and overrideEdid set, unless the request
2867              * had forceDisconnected set.
2868              *
2869              * If the request already had an EDID override, honor that EDID instead
2870              * of the display override EDID.
2871              */
2872             NvBool old = pRequest->forceConnected;
2873             pRequest->forceConnected = TRUE;
2874 
2875             if (!pRequest->overrideEdid) {
2876                 size_t len = nvReadDpyOverrideEdid(pDpyOverride,
2877                                                    pRequest->edid.buffer,
2878                                                    ARRAY_LEN(pRequest->edid.buffer));
2879 
2880                 if (len != 0) {
2881                     pRequest->overrideEdid = TRUE;
2882                     pRequest->edid.bufferSize = len;
2883                 } else {
2884                     pRequest->forceConnected = old;
2885                 }
2886             }
2887         } else if (!pDpyOverride->connected && !pRequest->forceConnected) {
2888             /*
2889              * If display is overriden as disconnected, treat the request as if it
2890              * had forceDisconnected set, unless the request had forceConnected set.
2891              */
2892             pRequest->forceDisconnected = TRUE;
2893         }
2894     }
2895 
2896     /*
2897      * Check for the connection state of the dpy.
2898      *
2899      * For DP MST, we need to honor the current DPlib state; if a DP
2900      * MST monitor is physically connected but forceDisconnected, its
2901      * hotplug events won't get serviced and DPlib will complain
2902      * loudly. This doesn't apply to DP serializer (which is not managed
2903      * by DPLib) since we don't need to do any topology/branch detection,
2904      * and we can honor force{Connected,Disconnected} in MST & SST mode.
2905      *
2906      * Otherwise, allow the client to override detection.
2907      *
2908      * Otherwise, honor the current DPlib state.
2909      *
2910      * If we're using a DP serializer connector in MST mode, don't expose any
2911      * SST displays as connected. In all other cases, assume that everything
2912      * is connected since the serializer connector has a fixed topology.
2913      *
2914      * Lastly, call RM to check if the dpy is connected.
2915      */
2916 
2917     if (nvDpyEvoIsDPMST(pDpyEvo) &&
2918         nvConnectorUsesDPLib(pConnectorEvo)) {
2919         /* honor DP MST connectedness */
2920         connectedList = nvDPLibDpyIsConnected(pDpyEvo) ?
2921             oneDpyIdList : nvEmptyDpyIdList();
2922     } else if (pRequest->forceConnected) {
2923         connectedList = oneDpyIdList;
2924     } else if (pRequest->forceDisconnected) {
2925         connectedList = nvEmptyDpyIdList();
2926     } else if (nvConnectorUsesDPLib(pConnectorEvo)) {
2927         connectedList = nvDPLibDpyIsConnected(pDpyEvo) ?
2928             oneDpyIdList : nvEmptyDpyIdList();
2929     } else if (nvConnectorIsDPSerializer(pConnectorEvo)) {
2930         if (pConnectorEvo->dpSerializerCaps.supportsMST &&
2931             !nvDpyEvoIsDPMST(pDpyEvo)) {
2932             connectedList = nvEmptyDpyIdList();
2933         } else {
2934             connectedList = oneDpyIdList;
2935         }
2936     } else {
2937         connectedList = nvRmGetConnectedDpys(pDispEvo, oneDpyIdList);
2938     }
2939 
2940     pDpyEvo->dp.inbandStereoSignaling = pRequest->dpInbandStereoSignaling;
2941 
2942     /*
2943      * XXX NVKMS TODO: once NVKMS is in the kernel and
2944      * nvAllocCoreChannelEvo() is guaranteed to happen before
2945      * nvDpyGetDynamicData(), pass allowDVISpecPClkOverride through to
2946      * nvDpyProbeMaxPixelClock() rather than cache it.
2947      */
2948     pDpyEvo->allowDVISpecPClkOverride = pRequest->allowDVISpecPClkOverride;
2949 
2950     if (nvDpyIdIsInDpyIdList(pDpyEvo->id, connectedList)) {
2951         if (!DpyConnectEvo(pDpyEvo, pParams)) {
2952             return FALSE;
2953         }
2954     } else {
2955         DpyDisconnectEvo(pDpyEvo);
2956     }
2957 
2958     if (nvConnectorUsesDPLib(pConnectorEvo)) {
2959         nvDPLibUpdateDpyLinkConfiguration(pDpyEvo);
2960     }
2961 
2962     ct_assert(sizeof(pDpyEvo->name) == sizeof(pReply->name));
2963 
2964     nvkms_memcpy(pReply->name, pDpyEvo->name, sizeof(pDpyEvo->name));
2965 
2966     if (pDpyEvo->parsedEdid.valid) {
2967         pReply->physicalDimensions.heightInCM =
2968             pDpyEvo->parsedEdid.info.screen_size_y;
2969         pReply->physicalDimensions.widthInCM =
2970             pDpyEvo->parsedEdid.info.screen_size_x;
2971     }
2972 
2973     /*
2974      * XXX NVKMS until NVKMS is in the kernel and
2975      * nvAllocCoreChannelEvo() is guaranteed to happen before
2976      * nvDpyGetDynamicData(), pDpyEvo->maxPixelClockKHz could change
2977      * later after the assignment here.
2978      */
2979     pReply->maxPixelClockKHz = pDpyEvo->maxPixelClockKHz;
2980 
2981     pReply->connected =
2982         nvDpyIdIsInDpyIdList(pDpyEvo->id, pDispEvo->connectedDisplays);
2983 
2984     pReply->isVirtualRealityHeadMountedDisplay = pDpyEvo->isVrHmd;
2985 
2986     pReply->vrrType = pDpyEvo->vrr.type;
2987 
2988     pReply->stereo3DVision.supported = pDpyEvo->stereo3DVision.supported;
2989     pReply->stereo3DVision.isDLP = pDpyEvo->stereo3DVision.isDLP;
2990     pReply->stereo3DVision.isAegis = pDpyEvo->stereo3DVision.isAegis;
2991     pReply->stereo3DVision.subType = pDpyEvo->stereo3DVision.subType;
2992 
2993     pReply->dp.guid.valid = pDpyEvo->dp.guid.valid;
2994 
2995     ct_assert(sizeof(pReply->dp.guid.buffer) ==
2996               sizeof(pDpyEvo->dp.guid.buffer));
2997     nvkms_memcpy(pReply->dp.guid.buffer, pDpyEvo->dp.guid.buffer,
2998                  sizeof(pDpyEvo->dp.guid.buffer));
2999 
3000     ct_assert(sizeof(pReply->dp.guid.str) == sizeof(pDpyEvo->dp.guid.str));
3001     nvkms_memcpy(pReply->dp.guid.str, pDpyEvo->dp.guid.str,
3002                  sizeof(pDpyEvo->dp.guid.str));
3003 
3004     if (pDpyEvo->edid.length > sizeof(pReply->edid.buffer)) {
3005         nvAssert(!"EDID larger than can be returned in NVKMS API");
3006         return FALSE;
3007     }
3008 
3009     if (pDpyEvo->edid.length > 0) {
3010         pReply->edid.bufferSize = pDpyEvo->edid.length;
3011         nvkms_memcpy(pReply->edid.buffer, pDpyEvo->edid.buffer, pDpyEvo->edid.length);
3012     }
3013 
3014     return TRUE;
3015 }
3016 
3017 void nvDpyUpdateCurrentAttributes(NVDpyEvoRec *pDpyEvo)
3018 {
3019     NVAttributesSetEvoRec newAttributes = pDpyEvo->currentAttributes;
3020 
3021     if (pDpyEvo->apiHead != NV_INVALID_HEAD) {
3022         newAttributes =
3023             pDpyEvo->pDispEvo->apiHeadState[pDpyEvo->apiHead].attributes;
3024     } else {
3025         newAttributes.dithering.enabled = FALSE;
3026         newAttributes.dithering.depth   = NV_KMS_DPY_ATTRIBUTE_CURRENT_DITHERING_DEPTH_NONE;
3027         newAttributes.dithering.mode    = NV_KMS_DPY_ATTRIBUTE_CURRENT_DITHERING_MODE_NONE;
3028         newAttributes.digitalSignal =
3029             nvGetDefaultDpyAttributeDigitalSignalValue(pDpyEvo->pConnectorEvo);
3030         newAttributes.numberOfHardwareHeadsUsed = 0;
3031     }
3032 
3033     if (newAttributes.colorSpace !=
3034         pDpyEvo->currentAttributes.colorSpace) {
3035         nvSendDpyAttributeChangedEventEvo(
3036             pDpyEvo,
3037             NV_KMS_DPY_ATTRIBUTE_CURRENT_COLOR_SPACE,
3038             newAttributes.colorSpace);
3039     }
3040 
3041     if (newAttributes.colorRange !=
3042         pDpyEvo->currentAttributes.colorRange) {
3043         nvSendDpyAttributeChangedEventEvo(
3044             pDpyEvo,
3045             NV_KMS_DPY_ATTRIBUTE_CURRENT_COLOR_RANGE,
3046             newAttributes.colorRange);
3047     }
3048 
3049     if (newAttributes.dithering.enabled !=
3050         pDpyEvo->currentAttributes.dithering.enabled) {
3051         nvSendDpyAttributeChangedEventEvo(
3052             pDpyEvo,
3053             NV_KMS_DPY_ATTRIBUTE_CURRENT_DITHERING,
3054             newAttributes.dithering.enabled);
3055     }
3056 
3057     if (newAttributes.dithering.depth !=
3058         pDpyEvo->currentAttributes.dithering.depth) {
3059         nvSendDpyAttributeChangedEventEvo(
3060             pDpyEvo,
3061             NV_KMS_DPY_ATTRIBUTE_CURRENT_DITHERING_DEPTH,
3062             newAttributes.dithering.depth);
3063     }
3064 
3065     if (newAttributes.dithering.mode !=
3066         pDpyEvo->currentAttributes.dithering.mode) {
3067         nvSendDpyAttributeChangedEventEvo(
3068             pDpyEvo,
3069             NV_KMS_DPY_ATTRIBUTE_CURRENT_DITHERING_MODE,
3070             newAttributes.dithering.mode);
3071     }
3072 
3073     if (newAttributes.imageSharpening.available !=
3074         pDpyEvo->currentAttributes.imageSharpening.available) {
3075         nvSendDpyAttributeChangedEventEvo(
3076             pDpyEvo,
3077             NV_KMS_DPY_ATTRIBUTE_IMAGE_SHARPENING_AVAILABLE,
3078             newAttributes.imageSharpening.available);
3079     }
3080 
3081     if (newAttributes.digitalSignal !=
3082             pDpyEvo->currentAttributes.digitalSignal) {
3083         nvSendDpyAttributeChangedEventEvo(
3084             pDpyEvo,
3085             NV_KMS_DPY_ATTRIBUTE_DIGITAL_SIGNAL,
3086             newAttributes.digitalSignal);
3087     }
3088 
3089     if (newAttributes.numberOfHardwareHeadsUsed !=
3090         pDpyEvo->currentAttributes.numberOfHardwareHeadsUsed) {
3091         nvSendDpyAttributeChangedEventEvo(
3092             pDpyEvo,
3093             NV_KMS_DPY_ATTRIBUTE_NUMBER_OF_HARDWARE_HEADS_USED,
3094             newAttributes.numberOfHardwareHeadsUsed);
3095     }
3096 
3097     pDpyEvo->currentAttributes = newAttributes;
3098 }
3099 
3100 // Returns TRUE if this display is capable of Adaptive-Sync
3101 NvBool nvDpyIsAdaptiveSync(const NVDpyEvoRec *pDpyEvo)
3102 {
3103     return ((pDpyEvo->vrr.type ==
3104              NVKMS_DPY_VRR_TYPE_ADAPTIVE_SYNC_DEFAULTLISTED) ||
3105             (pDpyEvo->vrr.type ==
3106              NVKMS_DPY_VRR_TYPE_ADAPTIVE_SYNC_NON_DEFAULTLISTED));
3107 }
3108 
3109 // Returns TRUE if this display is in the Adaptive-Sync defaultlist
3110 NvBool nvDpyIsAdaptiveSyncDefaultlisted(const NVDpyEvoRec *pDpyEvo)
3111 {
3112     NV0073_CTRL_SPECIFIC_DEFAULT_ADAPTIVESYNC_DISPLAY_PARAMS params = { };
3113     NVDispEvoPtr pDispEvo = pDpyEvo->pDispEvo;
3114     NVDevEvoPtr pDevEvo = pDispEvo->pDevEvo;
3115     NvU32 ret;
3116 
3117     if (!pDpyEvo->parsedEdid.valid) {
3118         return FALSE;
3119     }
3120 
3121     params.manufacturerID = pDpyEvo->parsedEdid.info.manuf_id;
3122     params.productID = pDpyEvo->parsedEdid.info.product_id;
3123 
3124     ret = nvRmApiControl(nvEvoGlobal.clientHandle,
3125                          pDevEvo->displayCommonHandle,
3126                          NV0073_CTRL_CMD_SPECIFIC_DEFAULT_ADAPTIVESYNC_DISPLAY,
3127                          &params, sizeof(params));
3128 
3129     if (ret != NVOS_STATUS_SUCCESS) {
3130         nvEvoLogDisp(pDispEvo, EVO_LOG_ERROR,
3131                      "Failed to query default adaptivesync listing for %s", pDpyEvo->name);
3132         return FALSE;
3133     }
3134 
3135     return params.bDefaultAdaptivesync;
3136 }
3137 
3138 static enum NvKmsDpyAttributeColorBpcValue GetYuv422MaxBpc(
3139     const NVDpyEvoRec *pDpyEvo)
3140 {
3141     const NVT_EDID_CEA861_INFO *p861Info =
3142         &pDpyEvo->parsedEdid.info.ext861;
3143 
3144     nvAssert(nvDpyIsHdmiEvo(pDpyEvo) ||
3145              nvConnectorUsesDPLib(pDpyEvo->pConnectorEvo));
3146 
3147     if (!pDpyEvo->parsedEdid.valid ||
3148         !pDpyEvo->parsedEdid.info.input.isDigital) {
3149         return NV_KMS_DPY_ATTRIBUTE_CURRENT_COLOR_BPC_UNKNOWN;
3150     }
3151 
3152     if (pDpyEvo->parsedEdid.info.version >= NVT_EDID_VER_1_4) {
3153         if (pDpyEvo->parsedEdid.info.u.feature_ver_1_4_digital.support_ycrcb_422) {
3154             if (pDpyEvo->parsedEdid.info.input.u.digital.bpc >= 10) {
3155                 return NV_KMS_DPY_ATTRIBUTE_CURRENT_COLOR_BPC_10;
3156             } else if (pDpyEvo->parsedEdid.info.input.u.digital.bpc >= 8) {
3157                 return NV_KMS_DPY_ATTRIBUTE_CURRENT_COLOR_BPC_8;
3158             }
3159         }
3160     } else {
3161         nvAssert(!nvConnectorUsesDPLib(pDpyEvo->pConnectorEvo));
3162 
3163         if (p861Info->revision >= NVT_CEA861_REV_A &&
3164                 !!(p861Info->basic_caps & NVT_CEA861_CAP_YCbCr_422)) {
3165             return NV_KMS_DPY_ATTRIBUTE_CURRENT_COLOR_BPC_10;
3166         }
3167     }
3168 
3169     return NV_KMS_DPY_ATTRIBUTE_CURRENT_COLOR_BPC_UNKNOWN;
3170 }
3171 
3172 NVColorFormatInfoRec nvGetColorFormatInfo(const NVDpyEvoRec *pDpyEvo)
3173 {
3174     const NVConnectorEvoRec *pConnectorEvo =
3175             pDpyEvo->pConnectorEvo;
3176     NVColorFormatInfoRec colorFormatsInfo = { };
3177 
3178     if (pConnectorEvo->legacyType ==
3179             NV0073_CTRL_SPECIFIC_DISPLAY_TYPE_CRT) {
3180 
3181         colorFormatsInfo.rgb444.maxBpc =
3182             NV_KMS_DPY_ATTRIBUTE_CURRENT_COLOR_BPC_10;
3183     } else if (pConnectorEvo->legacyType ==
3184                    NV0073_CTRL_SPECIFIC_DISPLAY_TYPE_DFP) {
3185 
3186         if (pConnectorEvo->signalFormat ==
3187                 NVKMS_CONNECTOR_SIGNAL_FORMAT_DSI) {
3188 
3189             if (pDpyEvo->parsedEdid.valid) {
3190                 switch (pDpyEvo->parsedEdid.info.input.u.digital.bpc) {
3191                     case 10:
3192                         colorFormatsInfo.rgb444.maxBpc =
3193                             NV_KMS_DPY_ATTRIBUTE_CURRENT_COLOR_BPC_10;
3194                         break;
3195                     case 6:
3196                         colorFormatsInfo.rgb444.maxBpc =
3197                             NV_KMS_DPY_ATTRIBUTE_CURRENT_COLOR_BPC_6;
3198                         break;
3199                     default:
3200                         nvAssert(!"Unsupported bpc for DSI");
3201                         // fall through
3202                     case 8:
3203                         colorFormatsInfo.rgb444.maxBpc =
3204                             NV_KMS_DPY_ATTRIBUTE_CURRENT_COLOR_BPC_8;
3205                         break;
3206                 }
3207             } else {
3208                 colorFormatsInfo.rgb444.maxBpc =
3209                     NV_KMS_DPY_ATTRIBUTE_CURRENT_COLOR_BPC_8;
3210             }
3211         } else if (nvConnectorUsesDPLib(pDpyEvo->pConnectorEvo)) {
3212 
3213             if (pDpyEvo->parsedEdid.valid &&
3214                 pDpyEvo->parsedEdid.info.input.isDigital &&
3215                 pDpyEvo->parsedEdid.info.version >= NVT_EDID_VER_1_4) {
3216                 if (pDpyEvo->parsedEdid.info.input.u.digital.bpc >= 10) {
3217                     colorFormatsInfo.rgb444.maxBpc =
3218                         NV_KMS_DPY_ATTRIBUTE_CURRENT_COLOR_BPC_10;
3219                 } else if (pDpyEvo->parsedEdid.info.input.u.digital.bpc < 8) {
3220                     colorFormatsInfo.rgb444.maxBpc =
3221                         NV_KMS_DPY_ATTRIBUTE_CURRENT_COLOR_BPC_6;
3222                 } else {
3223                     colorFormatsInfo.rgb444.maxBpc =
3224                         NV_KMS_DPY_ATTRIBUTE_CURRENT_COLOR_BPC_8;
3225                     colorFormatsInfo.yuv444.maxBpc =
3226                         NV_KMS_DPY_ATTRIBUTE_CURRENT_COLOR_BPC_8;
3227                 }
3228 
3229                 colorFormatsInfo.yuv422.maxBpc = GetYuv422MaxBpc(pDpyEvo);
3230             } else {
3231                 colorFormatsInfo.rgb444.maxBpc =
3232                     NV_KMS_DPY_ATTRIBUTE_CURRENT_COLOR_BPC_8;
3233             }
3234         } else {
3235             colorFormatsInfo.rgb444.maxBpc =
3236                 nvDpyIsHdmiDepth30Evo(pDpyEvo) ?
3237                     NV_KMS_DPY_ATTRIBUTE_CURRENT_COLOR_BPC_10 :
3238                     NV_KMS_DPY_ATTRIBUTE_CURRENT_COLOR_BPC_8;
3239 
3240             if (nvDpyIsHdmiEvo(pDpyEvo)) {
3241                 // TODO: Handle depth 30 YUV
3242                 colorFormatsInfo.yuv444.maxBpc =
3243                     NV_KMS_DPY_ATTRIBUTE_CURRENT_COLOR_BPC_8;
3244                 colorFormatsInfo.yuv422.maxBpc =
3245                     GetYuv422MaxBpc(pDpyEvo);
3246             }
3247         }
3248     }
3249 
3250     return colorFormatsInfo;
3251 }
3252 
3253 NvU32 nvDpyGetPossibleApiHeadsMask(const NVDpyEvoRec *pDpyEvo)
3254 {
3255     NvU32 possibleApiHeadMask = 0x0;
3256     NvU32 possibleNumLayers = NVKMS_MAX_LAYERS_PER_HEAD;
3257     const NVDevEvoRec *pDevEvo = pDpyEvo->pDispEvo->pDevEvo;
3258 
3259     /*
3260      * DSI supports only the hardware head-0 assigment, and the
3261      * dp-serializer dpys are bound to the specific hardware head;
3262      * the modeset client can be allowed to choose only those
3263      * api-heads to drive these dpys which has the number of layers
3264      * less than or equal to the number of layers supported by the
3265      * bound hardware heads.
3266      */
3267     if (pDpyEvo->pConnectorEvo->signalFormat ==
3268             NVKMS_CONNECTOR_SIGNAL_FORMAT_DSI) {
3269         possibleNumLayers = pDevEvo->head[0].numLayers;
3270     } else if (nvConnectorIsDPSerializer(pDpyEvo->pConnectorEvo)) {
3271         const NvU32 boundHead = pDpyEvo->dp.serializerStreamIndex;
3272         possibleNumLayers = pDevEvo->head[boundHead].numLayers;
3273     }
3274 
3275     for (NvU32 apiHead = 0; apiHead < pDevEvo->numApiHeads; apiHead++) {
3276         if (pDevEvo->apiHead[apiHead].numLayers <= possibleNumLayers) {
3277             possibleApiHeadMask |= NVBIT(apiHead);
3278         }
3279     }
3280 
3281     return possibleApiHeadMask;
3282 }
3283 
3284 NvBool nvDpyIsHDRCapable(const NVDpyEvoRec *pDpyEvo)
3285 {
3286     const NVDispEvoRec *pDispEvo = pDpyEvo->pDispEvo;
3287     const NVDevEvoRec *pDevEvo = pDispEvo->pDevEvo;
3288 
3289     const NVT_EDID_INFO *pInfo = &pDpyEvo->parsedEdid.info;
3290     const NVT_HDR_STATIC_METADATA *pHdrInfo =
3291         &pInfo->hdr_static_metadata_info;
3292 
3293     // Only supported on DP 1.3+ or HDMI
3294     if (nvDpyUsesDPLib(pDpyEvo)) {
3295         unsigned int major;
3296         unsigned int minor;
3297 
3298         if(!pDevEvo->caps.supportsDP13) {
3299             return FALSE;
3300         }
3301 
3302         if (!nvDPDpyGetDpcdRevision(pDpyEvo, &major, &minor)) {
3303             return FALSE;
3304         }
3305 
3306         if ((major < 1) || (minor < 3)) {
3307             return FALSE;
3308         }
3309     } else if (!nvDpyIsHdmiEvo(pDpyEvo)) {
3310         return FALSE;
3311     }
3312 
3313     if (!pDpyEvo->parsedEdid.valid) {
3314         return FALSE;
3315     }
3316 
3317     /*
3318      * XXX HDR is not supported with HDMI 3D due to both using VSI
3319      * infoframes.
3320      */
3321     if (pInfo->HDMI3DSupported) {
3322         return FALSE;
3323     }
3324 
3325     // Sink should support ST2084 EOTF.
3326     if (!pHdrInfo->supported_eotf.smpte_st_2084_eotf) {
3327         return FALSE;
3328     }
3329 
3330     /*
3331      * Sink should support static metadata type1. Nvtiming sets
3332      * static_metadata_type to 1 if the sink supports static metadata type1.
3333      */
3334     if (pHdrInfo->static_metadata_type != 1) {
3335         return FALSE;
3336     }
3337 
3338     return TRUE;
3339 }
3340