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