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