1 /*
2  * SPDX-FileCopyrightText: Copyright (c) 1993-2022 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 "nvrm_registry.h"
25 #include "objtmr.h"
26 #include "gpu/external_device/gsync.h"
27 #include "dev_p2060.h"
28 #include "gpu/external_device/dac_p2060.h"
29 #include "gpu_mgr/gpu_mgr.h"
30 #include "gpu/disp/kern_disp.h"
31 #include "rmapi/rmapi_utils.h"
32 #include "class/cl402c.h" // NV40_I2C
33 #include "kernel/gpu/i2c/i2c_api.h"
34 /*
35  * statics
36  */
37 
38 static NvBool     GpuIsP2060Master   (OBJGPU *, PDACP2060EXTERNALDEVICE);
39 static NvBool     GpuIsP2060Connected(OBJGPU *, PDACP2060EXTERNALDEVICE);
40 static NvBool     GpuIsMosaicTimingSlave(OBJGPU *, PDACP2060EXTERNALDEVICE);
41 static NvBool     GpuIsConnectedToMasterViaBridge(OBJGPU *, PDACP2060EXTERNALDEVICE);
42 
43 static OBJGPU*    GetP2060MasterableGpu (OBJGPU *, PDACP2060EXTERNALDEVICE);
44 static OBJGPU*    GetP2060WatchdogGpu   (OBJGPU *, PDACP2060EXTERNALDEVICE);
45 
46 static NV_STATUS  GetP2060GpuLocation   (OBJGPU *, PDACP2060EXTERNALDEVICE, NvU32*);
47 static NV_STATUS  GetP2060ConnectorIndexFromGpu (OBJGPU *, PDACP2060EXTERNALDEVICE, NvU32*);
48 static NvU32      GetP2060GpuSnapshot   (OBJGPU *, PDACP2060EXTERNALDEVICE);
49 
50 static void       gsyncProgramFramelockEnable_P2060(OBJGPU *, PDACP2060EXTERNALDEVICE, NvU32, NvBool);
51 static NvBool     gsyncIsStereoEnabled_p2060 (OBJGPU *, PDACEXTERNALDEVICE);
52 static NV_STATUS  gsyncProgramExtStereoPolarity_P2060 (OBJGPU *, PDACEXTERNALDEVICE);
53 
54 static NV_STATUS  gsyncProgramSlaves_P2060(OBJGPU *, PDACP2060EXTERNALDEVICE, NvU32);
55 static NvU32      gsyncReadSlaves_P2060(OBJGPU *, PDACP2060EXTERNALDEVICE);
56 static NV_STATUS  gsyncProgramMaster_P2060(OBJGPU *, PDACP2060EXTERNALDEVICE, NvU32, NvBool, NvBool);
57 static NvU32      gsyncReadMaster_P2060(OBJGPU *, PDACP2060EXTERNALDEVICE);
58 
59 static NV_STATUS  gsyncUpdateGsyncStatusSnapshot_P2060(OBJGPU *, PDACEXTERNALDEVICE);
60 
61 static void       gsyncCancelWatchdog_P2060(PDACP2060EXTERNALDEVICE);
62 static NV_STATUS  gsyncDisableFrameLockInterrupt_P2060(PDACEXTERNALDEVICE);
63 static NV_STATUS  gsyncEnableFramelockInterrupt_P2060(PDACEXTERNALDEVICE);
64 static NV_STATUS  gsyncDisableNonFramelockInterrupt_P2060(OBJGPU *, PDACEXTERNALDEVICE);
65 static NV_STATUS  gsyncEnableNonFramelockInterrupt_P2060(OBJGPU *, PDACEXTERNALDEVICE);
66 static void       gsyncResetMosaicData_P2060(NvU32, PDACP2060EXTERNALDEVICE);
67 static NV_STATUS  gsyncUpdateSwapRdyConnectionForGpu_P2060(OBJGPU *, PDACP2060EXTERNALDEVICE, NvBool);
68 
69 static NvBool     gsyncIsFrameLocked_P2060(PDACP2060EXTERNALDEVICE);
70 static NvBool     gsyncIsOnlyFrameLockMaster_P2060(PDACP2060EXTERNALDEVICE);
71 static NvBool     gsyncIsFrameLockMaster_P2060(PDACP2060EXTERNALDEVICE);
72 static NvBool     gsyncIsP2060MasterBoard(OBJGPU *, PDACP2060EXTERNALDEVICE);
73 
74 static NV_STATUS  gsyncSetLsrMinTime(OBJGPU *, PDACEXTERNALDEVICE, NvU32);
75 
76 static NV_STATUS  gsyncUpdateFrameCount_P2060(PDACP2060EXTERNALDEVICE, OBJGPU *);
77 static NvU32      gsyncGetNumberOfGpuFrameCountRollbacks_P2060(NvU32, NvU32, NvU32);
78 static NV_STATUS  gsyncFrameCountTimerService_P2060(OBJGPU *, OBJTMR *, void *);
79 static NV_STATUS  gsyncResetFrameCountData_P2060(OBJGPU *, PDACP2060EXTERNALDEVICE);
80 
81 static NV_STATUS  gsyncGpuStereoHeadSync(OBJGPU *, NvU32, PDACEXTERNALDEVICE, NvU32);
82 static NvBool     needsMasterBarrierWar(PDACEXTERNALDEVICE);
83 static NvBool     isFirmwareRevMismatch(OBJGPU *, DAC_EXTERNAL_DEVICE_REVS);
84 
85 static NvBool     isBoardWithNvlinkQsyncContention(POBJGPU);
86 static void       _extdevService(NvU32 , void *);
87 
88 NvBool
89 extdevGetDevice_P2060
90 (
91     OBJGPU *pGpu,
92     PDACEXTERNALDEVICE pExternalDevice
93 )
94 {
95     NvU8 revId;
96     NvU8 data;
97     DAC_EXTERNAL_DEVICES externalDeviceId;
98     DAC_EXTERNAL_DEVICE_REVS externalDeviceRev;
99     NV_STATUS status;
100 
101     if (!RMCFG_FEATURE_EXTDEV_GSYNC_P2060 ||
102         IS_EMULATION(pGpu) || IS_SIMULATION(pGpu))
103     {
104         return NV_FALSE;
105     }
106 
107     // Read the FPGA revision register
108     status = readregu008_extdevice(pGpu, pExternalDevice, (NvU8)NV_P2060_FPGA, &data);
109     if (status != NV_OK)
110     {
111         return NV_FALSE;
112     }
113     revId = data;
114 
115     // Decode the register value into device ID
116     if (DRF_VAL(_P2060, _FPGA, _ID, data) == NV_P2060_FPGA_ID_5)
117     {
118         externalDeviceId = DAC_EXTERNAL_DEVICE_P2060;
119     }
120     else if (DRF_VAL(_P2061, _FPGA, _ID, data) == NV_P2061_FPGA_ID_4)
121     {
122         externalDeviceId = DAC_EXTERNAL_DEVICE_P2061;
123     }
124     else
125     {
126         return NV_FALSE;
127     }
128 
129     // Decode the register value into device revision (major revision)
130     externalDeviceRev = DRF_VAL(_P2060, _FPGA, _REV, data);
131 
132     // Read device extended revision (minor revision)
133     status = readregu008_extdevice(pGpu, pExternalDevice, (NvU8)NV_P2060_FPGA_EXREV, &data);
134     if (status != NV_OK)
135     {
136         return NV_FALSE;
137     }
138 
139     // Caching revId, device ID, device revision, and device extended revision
140     pExternalDevice->revId = revId;
141     pExternalDevice->deviceId = externalDeviceId;
142     pExternalDevice->deviceRev = externalDeviceRev;
143     pExternalDevice->deviceExRev = data;
144 
145     return NV_TRUE;
146 }
147 
148 /*
149  * Return Extdev with setting of the data structures and
150  * function pointers for P2060.
151  */
152 PDACEXTERNALDEVICE
153 extdevConstruct_P2060
154 (
155     OBJGPU             *pGpu,
156     PDACEXTERNALDEVICE  pExternalDevice
157 )
158 {
159     DACP2060EXTERNALDEVICE *pThis = (PDACP2060EXTERNALDEVICE)pExternalDevice;
160     KernelDisplay          *pKernelDisplay = GPU_GET_KERNEL_DISPLAY(pGpu);
161     NvU32 iface, head, i;
162     NvU32 numHeads = kdispGetNumHeads(pKernelDisplay);
163 
164     if ( !extdevConstruct_Base(pGpu, pExternalDevice) )
165     {
166         return 0;
167     }
168 
169     // Setup interfaces
170     pThis->ExternalDevice.pI->Destroy = extdevDestroy_P2060;
171     pThis->ExternalDevice.pI->Attach  = gsyncAttachExternalDevice_P2060;
172 
173     pThis->ExternalDevice.pI->GetDevice             = extdevGetDevice_P2060;
174     pThis->ExternalDevice.pI->Init                  = extdevInit_P2060;
175 
176     pThis->ExternalDevice.pI->Service               = extdevService_P2060;
177     pThis->ExternalDevice.pI->Watchdog              = extdevWatchdog_P2060;
178     pThis->ExternalDevice.pI->setI2cHandles         = extdevSaveI2cHandles_P2060;
179 
180 
181     // Init data members
182     pThis->ExternalDevice.I2CAddr     = 0x20;
183     pThis->ExternalDevice.I2CPort     = pGpu->i2cPortForExtdev;
184     pThis->ExternalDevice.MaxGpus     = NV_P2060_MAX_IFACES_PER_GSYNC * NV_P2060_MAX_GPUS_PER_IFACE;
185 
186     pThis->gpuAttachMask = 0;
187     pThis->id = 0;
188     pThis->watchdogCountDownValue = 0;
189     pThis->isNonFramelockInterruptEnabled = NV_FALSE;
190     pThis->interruptEnabledInterface = 0;
191     pThis->tSwapRdyHi = 0;
192     pThis->tSwapRdyHiLsrMinTime = 0;
193 
194     //init FrameCountData
195     pThis->FrameCountData.totalFrameCount               = 0;
196     pThis->FrameCountData.currentFrameCount             = 0;
197     pThis->FrameCountData.initialDifference             = 0;
198     pThis->FrameCountData.numberOfRollbacks             = 0;
199     pThis->FrameCountData.previousFrameCount            = 0;
200     pThis->FrameCountData.lastFrameCounterQueryTime     = 0;
201     pThis->FrameCountData.bReCheck                      = 0;
202     pThis->FrameCountData.vActive                       = 0;
203     pThis->FrameCountData.isFrmCmpMatchIntMasterEnabled = NV_FALSE;
204     pThis->FrameCountData.enableFrmCmpMatchIntSlave     = NV_FALSE;
205     pThis->FrameCountData.head                          = NV_P2060_MAX_HEADS_PER_GPU;
206     pThis->FrameCountData.iface                         = NV_P2060_MAX_IFACES_PER_GSYNC;
207 
208     for (iface = 0; iface < NV_P2060_MAX_IFACES_PER_GSYNC; iface++)
209     {
210         pThis->Iface[iface].GpuInfo.gpuId     = NV0000_CTRL_GPU_INVALID_ID;
211         pThis->Iface[iface].GpuInfo.connected = NV_FALSE;
212 
213         pThis->Iface[iface].RasterSyncGpio.saved   = NV_FALSE;
214         pThis->Iface[iface].DsiFliplock.saved      = NV_FALSE;
215 
216         for (head = 0; head < numHeads; head++)
217         {
218             pThis->Iface[iface].Sync.Master[head]     = 0;
219             pThis->Iface[iface].Sync.Slaved[head]     = 0;
220             pThis->Iface[iface].Sync.LocalSlave[head] = 0;
221         }
222 
223         pThis->Iface[iface].lastEventNotified = 0;
224         pThis->Iface[iface].gainedSync        = 0;
225 
226         pThis->i2cHandles[iface].hClient       = 0;
227         pThis->i2cHandles[iface].hDevice       = 0;
228         pThis->i2cHandles[iface].hSubdevice    = 0;
229         pThis->i2cHandles[iface].hSubscription = 0;
230         pThis->i2cHandles[iface].gpuId         = 0;
231     }
232 
233     //init MosaicData
234     for (i = 0; i < NV_P2060_MAX_MOSAIC_GROUPS; i++)
235     {
236         gsyncResetMosaicData_P2060(i, pThis);
237     }
238 
239     return pExternalDevice;
240 }
241 
242 /*
243  * setup device registers
244  */
245 static void
246 _externalDeviceInit_P2060
247 (
248     OBJGPU            *pGpu,
249     NvBool             bExtDevFound
250 )
251 {
252     KernelDisplay *pKernelDisplay = GPU_GET_KERNEL_DISPLAY(pGpu);
253     RM_API   *pRmApi      = GPU_GET_PHYSICAL_RMAPI(pGpu);
254     NvU32     hClient     = pGpu->hInternalClient;
255     NvU32     hSubdevice  = pGpu->hInternalSubdevice;
256     NV_STATUS status      = NV_OK;
257     NV2080_CTRL_INTERNAL_GSYNC_ATTACH_AND_INIT_PARAMS ctrlParams = {0};
258 
259     ctrlParams.bExtDevFound = bExtDevFound;
260 
261     status = pRmApi->Control(pRmApi, hClient, hSubdevice,
262                              NV2080_CTRL_CMD_INTERNAL_GSYNC_ATTACH_AND_INIT,
263                              &ctrlParams, sizeof(ctrlParams));
264 
265     if (status != NV_OK)
266     {
267         NV_PRINTF(LEVEL_ERROR, "Extdev GPIO interrupt enable failed\n");
268     }
269     else
270     {
271         pKernelDisplay->bExtdevIntrSupported = NV_TRUE;
272     }
273 
274     return;
275 }
276 
277 NV_STATUS
278 gsyncFindGpuHandleLocation
279 (
280     DACEXTERNALDEVICE      *pExternalDevice,
281     NvU32                   gpuId,
282     NvU32                  *iface
283 )
284 {
285     DACP2060EXTERNALDEVICE *pThis = (PDACP2060EXTERNALDEVICE)pExternalDevice;
286     NvU32 tempIface;
287     NV_STATUS rmStatus = NV_ERR_GENERIC;
288 
289     for (tempIface = 0; tempIface < NV_P2060_MAX_IFACES_PER_GSYNC; tempIface++)
290     {
291         if (pThis->i2cHandles[tempIface].gpuId == gpuId)
292         {
293             *iface = tempIface;
294             rmStatus = NV_OK;
295         }
296     }
297 
298     return rmStatus;
299 }
300 
301 static NV_STATUS
302 gsyncFindFreeHandleLocation
303 (
304     DACP2060EXTERNALDEVICE *pThis,
305     NvU32                  *iface
306 )
307 {
308     NvU32 tempIface;
309     NV_STATUS rmStatus = NV_ERR_GENERIC;
310 
311     for (tempIface = 0; tempIface < NV_P2060_MAX_IFACES_PER_GSYNC; tempIface++)
312     {
313         if (pThis->i2cHandles[tempIface].gpuId == 0)
314         {
315             *iface = tempIface;
316             rmStatus = NV_OK;
317         }
318     }
319 
320     return rmStatus;
321 }
322 
323 NvBool
324 extdevSaveI2cHandles_P2060
325 (
326     OBJGPU             *pGpu,
327     DACEXTERNALDEVICE  *pExternalDevice
328 )
329 {
330     DACP2060EXTERNALDEVICE *pThis = (PDACP2060EXTERNALDEVICE)pExternalDevice;
331     RM_API                 *pRmApi = rmapiGetInterface(RMAPI_GPU_LOCK_INTERNAL);
332     NvHandle                hClient;
333     NvHandle                hDevice;
334     NvHandle                hSubdevice;
335     NvHandle                hSubscription = NV01_NULL_OBJECT;
336     NvU32                   iface;
337     NV_STATUS               rmStatus;
338 
339     rmStatus = gsyncFindFreeHandleLocation(pThis, &iface);
340     if (rmStatus != NV_OK)
341     {
342         NV_PRINTF(LEVEL_ERROR, "Maximum number of GPUs have been attached\n");
343         return NV_FALSE;
344     }
345 
346     rmStatus = rmapiutilAllocClientAndDeviceHandles(pRmApi,
347                                                     pGpu, &hClient, &hDevice, &hSubdevice);
348     NV_ASSERT_OR_RETURN(rmStatus == NV_OK, NV_FALSE);
349 
350     rmStatus = pRmApi->Alloc(pRmApi, hClient, hSubdevice,
351                             &hSubscription, NV40_I2C, NULL);
352 
353     NV_ASSERT_OR_RETURN(rmStatus == NV_OK, NV_FALSE);
354 
355     pThis->i2cHandles[iface].hClient       = hClient;
356     pThis->i2cHandles[iface].hDevice       = hDevice;
357     pThis->i2cHandles[iface].hSubdevice    = hSubdevice;
358     pThis->i2cHandles[iface].hSubscription = hSubscription;
359     pThis->i2cHandles[iface].gpuId         = pGpu->gpuId;
360 
361     return NV_TRUE;
362 }
363 
364 NV_STATUS
365 i2c_extdeviceHelper
366 (
367     OBJGPU            *pGpu,
368     DACEXTERNALDEVICE *pExternalDevice,
369     NvU32              i2cPort,
370     NvU8               SubAdr,
371     NvU8              *pData,
372     NvBool             write
373 )
374 {
375     RM_API                 *pRmApi  = rmapiGetInterface(RMAPI_GPU_LOCK_INTERNAL);
376     DACP2060EXTERNALDEVICE *pThis   = (PDACP2060EXTERNALDEVICE)pExternalDevice;
377     NV_STATUS               status  = NV_ERR_GENERIC;
378     NvU32                   iface;
379     NV402C_CTRL_I2C_TRANSACTION_PARAMS *pParams;
380 
381     pParams = portMemAllocNonPaged(sizeof(*pParams));
382     if (pParams == NULL)
383     {
384         return NV_ERR_NO_MEMORY;
385     }
386 
387     status = gsyncFindGpuHandleLocation(pExternalDevice, pGpu->gpuId, &iface);
388     if (status != NV_OK)
389     {
390         NV_PRINTF(LEVEL_ERROR, "Couldn't find saved GPU entry, check saved i2chandles. \n");
391         return status;
392     }
393 
394     portMemSet(pParams, 0, sizeof(*pParams));
395 
396     pParams->portId        = (NvU8)i2cPort;
397     pParams->transType     = NV402C_CTRL_I2C_TRANSACTION_TYPE_SMBUS_BYTE_RW;
398     pParams->deviceAddress = (NvU16)pExternalDevice->I2CAddr;
399 
400     pParams->transData.smbusByteData.bWrite = write;
401     pParams->transData.smbusByteData.registerAddress = SubAdr;
402     if (write)
403     {
404         pParams->transData.smbusByteData.message = *pData;
405     }
406 
407     status = pRmApi->Control(pRmApi, pThis->i2cHandles[iface].hClient,
408                              pThis->i2cHandles[iface].hSubscription,
409                              NV402C_CTRL_CMD_I2C_TRANSACTION,
410                              pParams, sizeof(*pParams));
411 
412     if (!write)
413     {
414         *pData = pParams->transData.smbusByteData.message;
415     }
416 
417     portMemFree(pParams);
418 
419     return status;
420 }
421 
422 /*
423  * Initialize P2060 for all GPUs in loop.
424  */
425 NvBool
426 extdevInit_P2060
427 (
428     OBJGPU  *pGpu,
429     PDACEXTERNALDEVICE pExternalDevice
430 )
431 {
432     OBJGPU  *pGpuTemp;
433     OBJSYS  *pSys = SYS_GET_INSTANCE();
434     OBJGSYNC *pGsyncTemp = NULL;
435     NvU32 gpuAttachCnt, gpuAttachMask, gpuInstance;
436     NvU32 data;
437 
438     if (!GpuIsP2060Connected(pGpu, (PDACP2060EXTERNALDEVICE)pExternalDevice))
439     {
440         return NV_FALSE;
441     }
442 
443     if (NV_OK != gsyncProgramExtStereoPolarity_P2060(pGpu, pExternalDevice))
444     {
445         return NV_FALSE;
446     }
447 
448     // Check regkeys
449     if (NV_OK == osReadRegistryDword(pGpu, NV_REG_STR_RM_QSYNC_FW_REV_CHECK, &data))
450     {
451         if (NV_REG_STR_RM_QSYNC_FW_REV_CHECK_DISABLE == data)
452         {
453             pSys->setProperty(pSys, PDB_PROP_SYS_IS_QSYNC_FW_REVISION_CHECK_DISABLED, NV_TRUE);
454         }
455     }
456 
457     // Initialize SyncPolarity to FALLING_EDGE. Refer Bug 1035880
458     if (NV_OK != gsyncSetSyncPolarity_P2060(pGpu, pExternalDevice, gsync_SyncPolarity_FallingEdge))
459     {
460         return NV_FALSE;
461     }
462 
463     // get count of all other gpus in the system
464     gpumgrGetGpuAttachInfo(&gpuAttachCnt, &gpuAttachMask);
465 
466     // loop over
467     gpuInstance = 0;
468     while ((pGpuTemp = gpumgrGetNextGpu(gpuAttachMask, &gpuInstance)) != NULL)
469     {
470         pGsyncTemp = gsyncmgrGetGsync(pGpuTemp);
471 
472         if (!pGsyncTemp || !pGsyncTemp->pExtDev)
473             continue;
474 
475         _externalDeviceInit_P2060(pGpuTemp, (pGpu == pGpuTemp));
476     }
477 
478     return NV_TRUE;
479 }
480 
481 static NV_STATUS
482 gsyncReadBoardId_P2060
483 (
484     OBJGPU             *pGpu,
485     PDACEXTERNALDEVICE  pExternalDevice,
486     NvU32              *uniqueId
487 )
488 {
489     NvU8  i, id = 0;
490     NV_STATUS rmStatus = NV_OK;
491 
492     *uniqueId = 0;
493     for (i = 0; i < 4; i++)
494     {
495         rmStatus = readregu008_extdeviceTargeted(pGpu, pExternalDevice,
496                                   NV_P2060_FPGA_ASGN_ID(i), &id);
497         if (rmStatus != NV_OK)
498         {
499             return rmStatus;
500         }
501         *uniqueId |= id << (i * 8);
502     }
503     return NV_OK;
504 }
505 
506 /*
507  * Attach P2060 to GPU on correct connector index.
508  */
509 NvBool
510 gsyncAttachExternalDevice_P2060
511 (
512     OBJGPU             *pGpu,
513     PDACEXTERNALDEVICE *ppExtdevs
514 )
515 {
516     OBJSYS *pSys = SYS_GET_INSTANCE();
517     OBJGSYNCMGR *pGsyncMgr = SYS_GET_GSYNCMGR(pSys);
518     OBJGSYNC *pGsync = NULL;
519     OBJGPU   *pOtherGpu = NULL;
520     DACP2060EXTERNALDEVICE *pThis, *pExt2060Temp;
521     NvU8  i, id = 0, regCtrl2 = 0;
522     NvU32 iface, connector, uniqueId = 0, pOtherGpuId = 0, bSkipResetForVM = 0, index = 0;
523     NvU32 tempIface;
524     NvBool bExtDevFound = NV_FALSE;
525     NV_STATUS rmStatus = NV_OK;
526     NvU8 ctrl = 0;
527 
528     rmStatus = gsyncReadBoardId_P2060(pGpu, *ppExtdevs, &uniqueId);
529     if (rmStatus != NV_OK)
530     {
531         NV_PRINTF(LEVEL_ERROR, "failed to read P2060 device Id.\n");
532         return NV_FALSE;
533     }
534 
535     if (uniqueId != 0x0)
536     {
537         // HW says another GPU has been here first. Confirm this from SW.
538         for (i = 0; i < NV30F1_MAX_GSYNCS; i++)
539         {
540             if (pGsyncMgr->gsyncTable[i].gpuCount)
541             {
542                 pGsync = &pGsyncMgr->gsyncTable[i];
543                 if (pGsync->pExtDev)
544                 {
545                     pThis  = (PDACP2060EXTERNALDEVICE) pGsync->pExtDev;
546                     if (pThis->id == uniqueId)
547                     {
548                         pOtherGpuId = pGsync->gpus[0].gpuId;
549                         bExtDevFound = NV_TRUE;
550                     }
551                 }
552             }
553 
554             if (bExtDevFound)
555             {
556                 break;
557             }
558         }
559 
560         if (!bExtDevFound)
561         {
562             if ((IS_PASSTHRU(pGpu)))
563             {
564                 // look for master board
565                 rmStatus = readregu008_extdeviceTargeted(pGpu, *ppExtdevs,
566                                                             (NvU8)NV_P2060_CONTROL, &ctrl);
567                 if (rmStatus != NV_OK)
568                 {
569                     NV_PRINTF(LEVEL_ERROR, "Failed to read Ctrl data.\n");
570                     return NV_FALSE;
571                 }
572 
573                 pThis = (PDACP2060EXTERNALDEVICE)*ppExtdevs;
574                 bSkipResetForVM = FLD_TEST_DRF(_P2060, _CONTROL, _I_AM, _MASTER, (NvU32)ctrl);
575                 rmStatus = GetP2060ConnectorIndexFromGpu(pGpu, pThis, &index);
576 
577                 if (rmStatus != NV_OK)
578                 {
579                     NV_PRINTF(LEVEL_ERROR, "Failed to get connector index for Gpu.\n");
580                     return NV_FALSE;
581                 }
582 
583                 // Look for SYNC source gpu.
584                 bSkipResetForVM = bSkipResetForVM && !(DRF_VAL(_P2060, _CONTROL, _SYNC_SRC, (NvU32)ctrl) == index);
585             }
586 
587             if (!bSkipResetForVM)
588             {
589                 // ExtDev is not preset in pGsyncMgr. Issue RESET to P2060 HW.
590                 regCtrl2 = FLD_SET_DRF_NUM(_P2060, _CONTROL2, _RESET, NV_TRUE, regCtrl2);
591                 writeregu008_extdeviceTargeted(pGpu, *ppExtdevs,
592                     NV_P2060_CONTROL2, regCtrl2);
593 
594                 osDelay(5); // Add delay of 5ms before I2C read as board is in reset phase.
595 
596                 rmStatus = gsyncReadBoardId_P2060(pGpu, *ppExtdevs, &uniqueId);
597                 if ((rmStatus != NV_OK) || (uniqueId != 0))
598                 {
599                     NV_PRINTF(LEVEL_ERROR,
600                         "failed to read P2060 device Id after reset.\n");
601                     return NV_FALSE;
602                 }
603             }
604         }
605         else
606         {
607             pOtherGpu = gpumgrGetGpuFromId(pOtherGpuId);
608             pGsync    = gsyncmgrGetGsync(pOtherGpu);
609 
610             if (!pGsync || !pGsync->pExtDev)
611             {
612                 NV_ASSERT(0);
613                 return NV_FALSE;
614             }
615 
616             NV_ASSERT(pGsync->pExtDev != *ppExtdevs);
617 
618             pThis = (PDACP2060EXTERNALDEVICE)*ppExtdevs;
619             pExt2060Temp = (PDACP2060EXTERNALDEVICE)(pGsync->pExtDev);
620 
621             rmStatus = gsyncFindFreeHandleLocation(pExt2060Temp, &iface);
622             if (rmStatus != NV_OK)
623             {
624                 NV_PRINTF(LEVEL_ERROR, "Failed to free index for new GPU entry. \n");
625                 return NV_FALSE;
626             }
627 
628             rmStatus = gsyncFindGpuHandleLocation(*ppExtdevs, pGpu->gpuId, &tempIface);
629             if (rmStatus != NV_OK)
630             {
631                 NV_PRINTF(LEVEL_ERROR, "Couldn't find saved GPU entry, check extdevSaveI2cHandles. \n");
632                 return NV_FALSE;
633             }
634 
635             pExt2060Temp->i2cHandles[iface].gpuId         = pThis->i2cHandles[tempIface].gpuId;
636             pExt2060Temp->i2cHandles[iface].hClient       = pThis->i2cHandles[tempIface].hClient;
637             pExt2060Temp->i2cHandles[iface].hDevice       = pThis->i2cHandles[tempIface].hDevice;
638             pExt2060Temp->i2cHandles[iface].hSubdevice    = pThis->i2cHandles[tempIface].hSubdevice;
639             pExt2060Temp->i2cHandles[iface].hSubscription = pThis->i2cHandles[tempIface].hSubscription;
640 
641             pThis->ExternalDevice.pI->Destroy(pGpu, *ppExtdevs);
642 
643             // Free our current pointer and replace it
644             portMemFree(*ppExtdevs);
645             *ppExtdevs = pGsync->pExtDev;
646         }
647     }
648 
649     pThis = (PDACP2060EXTERNALDEVICE)*ppExtdevs;
650 
651     if (uniqueId == 0x0)
652     {
653         // Use pGpu->gpuId as unique value.
654         uniqueId = pGpu->gpuId;
655 
656         for (i = 0; i < 4; i++)
657         {
658             id = (NvU8)(uniqueId >> (i * 8));
659             rmStatus = writeregu008_extdeviceTargeted(pGpu, *ppExtdevs,
660                                                       NV_P2060_FPGA_ASGN_ID(i),
661                                                       id);
662             if (rmStatus != NV_OK)
663             {
664                 NV_PRINTF(LEVEL_ERROR, "failed to update P2060 device Id.\n");
665                 return NV_FALSE;
666             }
667         }
668         pThis->id = uniqueId;
669     }
670 
671     rmStatus = GetP2060ConnectorIndexFromGpu(pGpu, pThis, &iface);
672     if (rmStatus != NV_OK)
673     {
674         NV_PRINTF(LEVEL_ERROR, "failed to find P2060 connector.\n");
675         return NV_FALSE;
676     }
677 
678     //
679     // Add 1 to index we got from status2 register read beacuse connector
680     // NV30F1_CTRL_GET_GSYNC_GPU_TOPOLOGY_ONE start with value 1.
681     //
682     connector = iface + 1;
683 
684     //
685     // If adding a check before the gsyncAttachGpu call and returning before
686     // that please add the following code:
687     // pThis->gpuAttachMask &= ~NVBIT(pGpu->gpuInstance);
688     //    (*ppExtdevs)->ReferenceCount--;
689     // before returning NV_FALSE so that the caller can destroy the
690     // ext device structure. The destroy funciton only decrements the ref count
691     // if the gpu has already been attached.
692     //
693     (*ppExtdevs)->ReferenceCount++;
694     pThis->gpuAttachMask |= NVBIT(pGpu->gpuInstance);
695 
696     pThis->Iface[iface].GpuInfo.gpuId = pGpu->gpuId;
697     pThis->Iface[iface].GpuInfo.connected = NV_TRUE;
698 
699     rmStatus = gsyncAttachGpu(*ppExtdevs, pGpu, connector, NULL, (*ppExtdevs)->deviceId);
700 
701     if (rmStatus != NV_OK)
702     {
703         NV_PRINTF(LEVEL_ERROR, "failed to attach P2060 gsync to gpu.\n");
704         return NV_FALSE;
705     }
706 
707     if (pThis->ExternalDevice.deviceId == DAC_EXTERNAL_DEVICE_P2061)
708     {
709         pGpu->setProperty(pGpu, PDB_PROP_GPU_QSYNC_II_ATTACHED, NV_TRUE);
710     }
711     else
712     {
713         NV_ASSERT(pThis->ExternalDevice.deviceId == DAC_EXTERNAL_DEVICE_P2060);
714         pGpu->setProperty(pGpu, PDB_PROP_GPU_GSYNC_III_ATTACHED, NV_TRUE);
715     }
716 
717     if (!pThis->isNonFramelockInterruptEnabled)
718     {
719         rmStatus = gsyncEnableNonFramelockInterrupt_P2060(pGpu, *ppExtdevs);
720         if (rmStatus != NV_OK)
721         {
722             NV_PRINTF(LEVEL_ERROR,
723                       "Failed to enable non-framelock interrupts on gsync GPU.\n");
724             return NV_FALSE;
725         }
726         pThis->isNonFramelockInterruptEnabled = NV_TRUE;
727         pThis->interruptEnabledInterface = iface;
728     }
729 
730     return NV_TRUE;
731 }
732 
733 /*
734  * Destroy the device P2060.
735  */
736 void
737 extdevDestroy_P2060
738 (
739     OBJGPU *pGpu,
740     PDACEXTERNALDEVICE pExternalDevice
741 )
742 {
743     DACP2060EXTERNALDEVICE *pThis = (PDACP2060EXTERNALDEVICE)pExternalDevice;
744     RM_API   *pRmApi = rmapiGetInterface(RMAPI_GPU_LOCK_INTERNAL);
745     KernelDisplay *pKernelDisplay = GPU_GET_KERNEL_DISPLAY(pGpu);
746     NvU32 iface, head;
747     NvU32 numHeads = kdispGetNumHeads(pKernelDisplay);
748     NvU8 ctrl2 = 0;
749     NV_STATUS rmStatus;
750 
751     for (iface = 0; iface < NV_P2060_MAX_IFACES_PER_GSYNC; iface++)
752     {
753         if (pThis->Iface[iface].GpuInfo.gpuId == pGpu->gpuId)
754         {
755             pThis->gpuAttachMask &= ~NVBIT(pGpu->gpuInstance);
756             pExternalDevice->ReferenceCount--;
757 
758             if (pThis->Iface[iface].GpuInfo.connected)
759             {
760                 if (pExternalDevice->ReferenceCount == 0)
761                 {
762                     // clear id for this gsync device.
763                     pThis->id = 0;
764 
765                     // reset the gsync hw
766                     ctrl2 = FLD_SET_DRF_NUM(_P2060, _CONTROL2, _RESET, NV_TRUE, ctrl2);
767                     writeregu008_extdeviceTargeted(pGpu, pExternalDevice,
768                                                    NV_P2060_CONTROL2, ctrl2);
769                 }
770             }
771 
772             // Restore saved swap lockout window values that may not have
773             // been restored by disabling swap barriers.
774             if (pThis->Iface[iface].DsiFliplock.saved == NV_TRUE)
775             {
776                 for (head = 0; head < numHeads; head++)
777                 {
778                     kdispRestoreOriginalLsrMinTime_HAL(pGpu, pKernelDisplay, head,
779                     pThis->Iface[iface].DsiFliplock.OrigLsrMinTime[head]);
780                 }
781                 pThis->Iface[iface].DsiFliplock.saved = NV_FALSE;
782             }
783 
784             gsyncRemoveGpu(pGpu);
785 
786             pThis->Iface[iface].GpuInfo.gpuId = NV0000_CTRL_GPU_INVALID_ID;
787             pThis->Iface[iface].GpuInfo.connected = NV_FALSE;
788 
789             pGpu->setProperty(pGpu, PDB_PROP_GPU_GSYNC_III_ATTACHED, NV_FALSE);
790             pGpu->setProperty(pGpu, PDB_PROP_GPU_QSYNC_II_ATTACHED, NV_FALSE);
791 
792             rmStatus = gsyncFindGpuHandleLocation(pExternalDevice, pGpu->gpuId, &iface);
793             if (rmStatus != NV_OK)
794             {
795                 NV_PRINTF(LEVEL_ERROR, "Couldn't find saved GPU entry, check saved i2chandles. \n");
796                 goto cleanup;
797             }
798 
799             rmapiutilFreeClientAndDeviceHandles(pRmApi,
800                                                 &pThis->i2cHandles[iface].hClient,
801                                                 &pThis->i2cHandles[iface].hDevice,
802                                                 &pThis->i2cHandles[iface].hSubdevice);
803 
804             pThis->i2cHandles[iface].hClient   = 0;
805             pThis->i2cHandles[iface].hDevice   = 0;
806             pThis->i2cHandles[iface].hSubdevice    = 0;
807             pThis->i2cHandles[iface].hSubscription = 0;
808             pThis->i2cHandles[iface].gpuId   = 0;
809 
810             break;
811         }
812     }
813 
814 cleanup:
815     if (pExternalDevice->ReferenceCount == 0)
816     {
817        // And continue the chain running.
818        extdevDestroy_Base(pGpu, pExternalDevice);
819     }
820 }
821 
822 /*
823  * Handles the loss/gain of sync and other interrupts.
824  */
825 void
826 extdevService_P2060
827 (
828     OBJGPU            *pGpu,
829     PDACEXTERNALDEVICE pExtDev,
830     NvU8               lossRegStatus,
831     NvU8               gainRegStatus,
832     NvU8               miscRegStatus,
833     NvBool             rmStatus
834 )
835 {
836     OBJOS   *pOS    = GPU_GET_OS(pGpu);
837     EXTDEV_INTR_DATA intrData;
838 
839     if (!rmStatus)
840     {
841         return;
842     }
843 
844     intrData.lossRegStatus = lossRegStatus;
845     intrData.gainRegStatus = gainRegStatus;
846     intrData.miscRegStatus = miscRegStatus;
847     intrData.pExtDevice    = pExtDev;
848 
849     if (IS_GSP_CLIENT(pGpu))
850     {
851         EXTDEV_INTR_DATA *workerThreadData = NULL;
852 
853         workerThreadData = portMemAllocNonPaged(sizeof(EXTDEV_INTR_DATA));
854         if (NULL != workerThreadData)
855         {
856             *workerThreadData = intrData;
857         }
858         else
859         {
860             NV_PRINTF(LEVEL_ERROR, "Memalloc failed\n");
861         }
862 
863         // Attempt to queue a work item.
864         if (NV_OK != pOS->osQueueWorkItemWithFlags(pGpu,
865                                                    _extdevService,
866                                                    (void *)workerThreadData,
867                                                    OS_QUEUE_WORKITEM_FLAGS_LOCK_GPU_GROUP_SUBDEVICE_RW))
868         {
869             portMemFree((void *)workerThreadData);
870         }
871     }
872     else
873     {
874         _extdevService(gpuGetInstance(pGpu), (void *)&intrData);
875     }
876 }
877 
878 static void
879 _extdevService
880 (
881     NvU32 gpuInstance,
882     void *workerThreadData
883 )
884 {
885     OBJGPU *pGpu = gpumgrGetGpu(gpuInstance);
886     NV_STATUS rmStatus;
887 
888     EXTDEV_INTR_DATA intrData = *(EXTDEV_INTR_DATA *)workerThreadData;
889     PDACP2060EXTERNALDEVICE pThis = (PDACP2060EXTERNALDEVICE)intrData.pExtDevice;
890     NvU32 iface, ifaceEvents[NV_P2060_MAX_IFACES_PER_GSYNC];
891 
892     rmStatus = gsyncUpdateGsyncStatusSnapshot_P2060(pGpu, intrData.pExtDevice);
893     if (rmStatus != NV_OK)
894     {
895         NV_PRINTF(LEVEL_ERROR,
896                   "Couldn't raad the register status physical RMs.\n");
897         return;
898     }
899     rmStatus = GetP2060GpuLocation(pGpu, pThis, &iface);
900     if (rmStatus != NV_OK)
901     {
902         NV_PRINTF(LEVEL_ERROR,
903                   "Cannot get P2060 Gpu location for serving interrupt.\n");
904         return;
905     }
906 
907     ifaceEvents[iface] = 0x00;
908 
909     if (intrData.lossRegStatus) //lost signal interrupts
910     {
911         if (DRF_VAL(_P2060, _STATUS4, _SYNC, (NvU32)intrData.lossRegStatus))
912         {
913             ifaceEvents[iface] |= NVBIT(NV30F1_GSYNC_NOTIFIERS_SYNC_LOSS(iface));
914         }
915         if (DRF_VAL(_P2060, _STATUS4, _STEREO, (NvU32)intrData.lossRegStatus))
916         {
917             ifaceEvents[iface] |= NVBIT(NV30F1_GSYNC_NOTIFIERS_STEREO_LOSS(iface));
918         }
919         if (DRF_VAL(_P2060, _STATUS4, _HS, (NvU32)intrData.lossRegStatus))
920         {
921             ifaceEvents[iface] |= NVBIT(NV30F1_GSYNC_NOTIFIERS_HOUSE_LOSS);
922         }
923         if (DRF_VAL(_P2060, _STATUS4, _RJ45, (NvU32)intrData.lossRegStatus))
924         {
925             ifaceEvents[iface] |= NVBIT(NV30F1_GSYNC_NOTIFIERS_RJ45_LOSS);
926         }
927 
928         //
929         // Enable the watchdog again if we got any loss events.
930         // Otherise sync gain tracking and stereo sync won't work as desired.
931         //
932         if (ifaceEvents[iface])
933         {
934             extdevScheduleWatchdog(pGpu, (PDACEXTERNALDEVICE)pThis);
935             if (!gsyncIsOnlyFrameLockMaster_P2060(pThis))
936             {
937                 pThis->watchdogCountDownValue = NV_P2060_WATCHDOG_COUNT_DOWN_VALUE;
938             }
939         }
940 
941         if (ifaceEvents[iface] && (pThis->Iface[iface].lastEventNotified != ifaceEvents[iface]))
942         {
943              gsyncSignalServiceRequested(gsyncGetGsyncInstance(pGpu), ifaceEvents[iface], iface);
944              pThis->Iface[iface].lastEventNotified = ifaceEvents[iface];
945         }
946     }
947 
948     if (intrData.gainRegStatus) //Gain signal interrupts
949     {
950         if (DRF_VAL(_P2060, _STATUS4, _SYNC, (NvU32)intrData.gainRegStatus))
951         {
952             ifaceEvents[iface] |= NVBIT(NV30F1_GSYNC_NOTIFIERS_SYNC_GAIN(iface));
953         }
954         if (DRF_VAL(_P2060, _STATUS4, _STEREO, (NvU32)intrData.gainRegStatus))
955         {
956             ifaceEvents[iface] |= NVBIT(NV30F1_GSYNC_NOTIFIERS_STEREO_GAIN(iface));
957         }
958         if (DRF_VAL(_P2060, _STATUS4, _HS, (NvU32)intrData.gainRegStatus))
959         {
960             ifaceEvents[iface] |= NVBIT(NV30F1_GSYNC_NOTIFIERS_HOUSE_GAIN);
961         }
962         if (DRF_VAL(_P2060, _STATUS4, _RJ45, (NvU32)intrData.gainRegStatus))
963         {
964             ifaceEvents[iface] |= NVBIT(NV30F1_GSYNC_NOTIFIERS_RJ45_GAIN);
965         }
966 
967         if (ifaceEvents[iface] && (pThis->Iface[iface].lastEventNotified != ifaceEvents[iface]))
968         {
969             gsyncSignalServiceRequested(gsyncGetGsyncInstance(pGpu), ifaceEvents[iface], iface);
970             pThis->Iface[iface].lastEventNotified = ifaceEvents[iface];
971         }
972     }
973 
974     if (intrData.miscRegStatus) //Other interrupts
975     {
976         if (FLD_TEST_DRF(_P2060, _STATUS4, _FRM_CNT_MATCH_INT, _PENDING, (NvU32)intrData.miscRegStatus))
977         {
978             //
979             // To enable frameCountTimerService callback to verify the cached difference 1 second
980             // after the test signal is received.
981             //
982             pThis->FrameCountData.bReCheck = 1;
983 
984             //
985             // Reset framecountData.Therefore whenever user queries after frame compare
986             // interrupt, gsync and gpu frame count register are read and difference is cached again.
987             //
988             // This will also disable frame compare match interrupt, which will be then enabled
989             // frame compare match interrupt in the next user query or in FrameCountTimerService.
990             // This is required to clear frmCmpInt bit of status1 register. A read to status1
991             // register should clear it, but it is possible that the read to status1 register by
992             // calling  gsyncUpdateGsyncStatusSnapshot_P2060() function above may happen in the same
993             // frame and this would result interrupt to come back again setting frmCmpint bit.
994             //
995             rmStatus |= gsyncResetFrameCountData_P2060(pGpu, pThis);
996 
997             if (rmStatus == NV_OK)
998             {
999                 //
1000                 // Set enableFrmCmpMatchInt flag NV_TRUE and return. This indicates
1001                 // that the frame compare match interrupt for slave needs to be
1002                 // enabled, which is done in the next user query or in
1003                 // FrameCountTimerService callback.
1004                 //
1005                 pThis->FrameCountData.enableFrmCmpMatchIntSlave = NV_TRUE;
1006             }
1007         }
1008 
1009         if (FLD_TEST_DRF(_P2060, _STATUS4, _ERROR_INT, _PENDING, (NvU32)intrData.miscRegStatus))
1010         {
1011             // Some error condition observed. Update snapshot
1012             rmStatus  = gsyncUpdateGsyncStatusSnapshot_P2060(pGpu, intrData.pExtDevice);
1013             if (rmStatus != NV_OK)
1014             {
1015                 return;
1016             }
1017         }
1018     }
1019 }
1020 
1021 /*
1022  * waits for hardware to (re-)establish sync.
1023  * once sync obtains, the watchdog enables interrupt, de-sechedules
1024  * itself, and waits for an interrupt to go off before running again.
1025  */
1026 NV_STATUS
1027 extdevWatchdog_P2060
1028 (
1029     OBJGPU            *pGpu,
1030     OBJTMR            *pTmr,
1031     PDACEXTERNALDEVICE pExtDev
1032 )
1033 {
1034     KernelDisplay *pKernelDisplay = GPU_GET_KERNEL_DISPLAY(pGpu);
1035     PDACP2060EXTERNALDEVICE pThis = (PDACP2060EXTERNALDEVICE)pExtDev;
1036     NvU32 iface, head;
1037     NvBool bStereoLocked;
1038     NV_STATUS rmStatus = NV_OK;
1039     NvBool bStereoEnabled[NV_P2060_MAX_IFACES_PER_GSYNC];
1040     NvU32 numHeads = kdispGetNumHeads(pKernelDisplay);
1041 
1042     if (pThis->watchdogCountDownValue)
1043         pThis->watchdogCountDownValue--;
1044 
1045     // we now can trust our selection of GPU
1046     pGpu = GetP2060WatchdogGpu(pGpu, pThis);
1047     NV_ASSERT(pGpu);
1048 
1049     // schedule the next callback.  we can cancel, if it's not needed.
1050     extdevScheduleWatchdog(pGpu, (PDACEXTERNALDEVICE)pThis);
1051 
1052     rmStatus = gsyncUpdateGsyncStatusSnapshot_P2060(pGpu, pExtDev);
1053 
1054     if (!gsyncIsFrameLocked_P2060(pThis))
1055     {
1056         gsyncCancelWatchdog_P2060(pThis);
1057         rmStatus = gsyncDisableFrameLockInterrupt_P2060((PDACEXTERNALDEVICE)pThis);
1058         return rmStatus;
1059     }
1060 
1061     for (iface = 0; iface < NV_P2060_MAX_IFACES_PER_GSYNC; iface++)
1062     {
1063         OBJGPU *pTmpGpu = NULL;
1064         if (pThis->Iface[iface].GpuInfo.gpuId == NV0000_CTRL_GPU_INVALID_ID)
1065         {
1066             continue;
1067         }
1068         pTmpGpu = gpumgrGetGpuFromId(pThis->Iface[iface].GpuInfo.gpuId);
1069 
1070         NV_ASSERT(pTmpGpu);
1071 
1072         // figure out if stereo is enabled
1073         bStereoEnabled[iface] = gsyncIsStereoEnabled_p2060(pTmpGpu, pExtDev);
1074 
1075         // loop over the heads of the current gpu on this interface
1076         for ( head = 0; head < numHeads; head++ )
1077         {
1078             if (pThis->Iface[iface].Sync.Slaved[head] ||
1079                 pThis->Iface[iface].Sync.LocalSlave[head])
1080             {
1081                 bStereoLocked  = FLD_TEST_DRF(_P2060, _STATUS, _STEREO, _LOCK,
1082                                              (NvU32)pThis->Snapshot[iface].Status1);
1083 
1084                  // check for sync and locks, noting that all heads share the status
1085                  if ((pThis->Iface[iface].gainedSync) &&
1086                      (!bStereoEnabled[iface] || bStereoLocked))
1087                  {
1088                       break;
1089                  }
1090                  else
1091                  {
1092                      return NV_ERR_GENERIC; // hope things are better, on next watchdog run
1093                  }
1094             }
1095         }
1096     }
1097 
1098     if ( NV_OK == rmStatus && pKernelDisplay->bExtdevIntrSupported
1099          && !pThis->watchdogCountDownValue)
1100     {
1101         NV_PRINTF(LEVEL_INFO, "P2060[%d] extdevCancelWatchdog.\n", iface);
1102 
1103         // disable the watchdog,
1104         extdevCancelWatchdog(pGpu, (PDACEXTERNALDEVICE)pThis);
1105 
1106         // enable the framelock interrupt, if either Master or Slaves are desired
1107         gsyncEnableFramelockInterrupt_P2060((PDACEXTERNALDEVICE)pThis);
1108     }
1109 
1110     return NV_OK;
1111 }
1112 
1113 static NV_STATUS
1114 gsyncApplyStereoPinAlwaysHiWar
1115 (
1116     POBJGPU            pGpu,
1117     PDACEXTERNALDEVICE pExtDev
1118 )
1119 {
1120 
1121     return NV_OK;
1122 
1123 }
1124 
1125 static NV_STATUS
1126 gsyncUnApplyStereoPinAlwaysHiWar
1127 (
1128     POBJGPU pGpu
1129 )
1130 {
1131 
1132     return NV_OK;
1133 
1134 }
1135 
1136 //
1137 // gsyncReadUniversalFrameCount_P2060()
1138 //
1139 // When user queries for the first time or after 10 seconds, Gsync and Gpu
1140 // hardware framecount are read and then the difference between them is cached.
1141 // For rest of the instances whenever user queries for frame count, gpu
1142 // frame count is read, and software framecount is updated accordingly based
1143 // on previous cached values.
1144 //
1145 static NV_STATUS
1146 gsyncReadUniversalFrameCount_P2060
1147 (
1148     OBJGPU *pGpu,
1149     PDACEXTERNALDEVICE pExtDev,
1150     NvU32 *pFrameCount
1151 )
1152 {
1153     OBJGPU   *pTmpGpu = NULL;
1154     KernelDisplay *pKernelDisplay = NULL;
1155     PDACP2060EXTERNALDEVICE pThis = (PDACP2060EXTERNALDEVICE) pExtDev;
1156     NV_STATUS rmStatus = NV_OK;
1157     NvU32 lineCount;
1158     NvU32 frameCount;
1159     NvS32 calculatedDiff;
1160     NvU64 currentTime = 0;
1161     NvU64 queryTimeDiff;
1162     OBJTMR *pTmpTmr = NULL;
1163     OBJTMR *pTmr = GPU_GET_TIMER(pGpu);
1164 
1165     if (!(pThis->FrameCountData.iface == NV_P2060_MAX_IFACES_PER_GSYNC))
1166     {
1167         //
1168         // pThis->FrameCountData.iface exists.
1169         // Thus deriving pTmpGpu from it, and to maintain consistency reading the time from it.
1170         //
1171         pTmpGpu = gpumgrGetGpuFromId(pThis->Iface[pThis->FrameCountData.iface].GpuInfo.gpuId);
1172 
1173         if (pTmpGpu)
1174         {
1175             pTmpTmr = GPU_GET_TIMER(pTmpGpu);
1176             currentTime = tmrGetTime_HAL(pTmpGpu, pTmpTmr);
1177         }
1178     }
1179 
1180     if (currentTime == 0)
1181     {
1182         // pTmpGpu doesn't exists, so getting the time from pGpu.
1183         currentTime = tmrGetTime_HAL(pGpu, pTmr);
1184     }
1185 
1186     if (NV_P2060_STATUS_SYNC_LOSS_TRUE == DRF_VAL(_P2060, _STATUS, _SYNC_LOSS, GetP2060GpuSnapshot(pGpu,pThis)))
1187     {
1188         // don't increment the frame counter in case of sync loss
1189         *pFrameCount = pThis->FrameCountData.totalFrameCount;
1190         pThis->FrameCountData.lastFrameCounterQueryTime = currentTime;
1191 
1192         return NV_OK;
1193     }
1194 
1195     queryTimeDiff = currentTime - pThis->FrameCountData.lastFrameCounterQueryTime;
1196 
1197     //
1198     // If user queries for the first time or after 10 secs then read gsync and
1199     // gpu frame count registers and update software frame count and cached
1200     //difference. Also enable the frmCmpMatchInt if not enabled.
1201     //
1202     if ((!pThis->FrameCountData.lastFrameCounterQueryTime) ||
1203        (queryTimeDiff > 2 * NV_P2060_FRAME_COUNT_TIMER_INTERVAL))
1204     {
1205         //
1206         // P2060 refreshrate is in 0.00001 Hz, so divide by 10000 to get Hz.
1207         // divide 1000000 by refreshRate to get the frame time in us.
1208         //
1209         pThis->FrameCountData.frameTime = 1000000 / (pThis->RefreshRate/10000); //in us
1210 
1211         //
1212         // Enable FrameCountTimerService to verify FrameCountData.initialDifference.
1213         //
1214         pThis->FrameCountData.bReCheck = 1;
1215 
1216         rmStatus = gsyncUpdateFrameCount_P2060(pThis, pGpu);
1217         *pFrameCount = pThis->FrameCountData.totalFrameCount;
1218 
1219         // enable frame count match interrupt if not master
1220         if (!gsyncIsFrameLockMaster_P2060(pThis))
1221         {
1222             NvU8 regCtrl3;
1223 
1224             // set frame count match value 1
1225             rmStatus |= writeregu008_extdeviceTargeted(pGpu, (PDACEXTERNALDEVICE)pThis,
1226                                                       (NvU8)NV_P2060_FRAME_CMPR_LOW,  0x1);
1227             rmStatus |= writeregu008_extdeviceTargeted(pGpu, (PDACEXTERNALDEVICE)pThis,
1228                                                       (NvU8)NV_P2060_FRAME_CMPR_MID,  0x0);
1229             rmStatus |= writeregu008_extdeviceTargeted(pGpu, (PDACEXTERNALDEVICE)pThis,
1230                                                       (NvU8)NV_P2060_FRAME_CMPR_HIGH,  0x0);
1231 
1232             //
1233             // Enable frame count match interrupt for the first time. For rest of the
1234             // instances when, TEST_SIGNAL is received, interrupt is enable in
1235             // gsyncUpdateFrameCount_P2060() based on enableFrmCmpMatchIntSlave bit.
1236             //
1237             rmStatus |= readregu008_extdeviceTargeted(pGpu, (PDACEXTERNALDEVICE)pThis,
1238                                                       NV_P2060_CONTROL3,  &regCtrl3);
1239 
1240             if (rmStatus == NV_OK)
1241             {
1242                 regCtrl3 |= DRF_DEF(_P2060, _CONTROL3, _INTERRUPT, _ON_FRAME_MATCH);
1243                 rmStatus = writeregu008_extdeviceTargeted(pGpu, (PDACEXTERNALDEVICE)pThis,
1244                                                           NV_P2060_CONTROL3, regCtrl3);
1245             }
1246         }
1247 
1248         if (rmStatus != NV_OK)
1249         {
1250             rmStatus |= gsyncResetFrameCountData_P2060(pGpu, pThis);
1251             return rmStatus;
1252         }
1253 
1254         pThis->FrameCountData.lastFrameCounterQueryTime = currentTime;
1255     }
1256 
1257     // Update software framecount throught gpu frame count register.
1258     else
1259     {
1260         //
1261         // To avoid any inconsistency, linecount and framecount should always
1262         // be read from one specific Gpu head. Its value is stored in pThis->FrameCountData.
1263         //
1264         NV_ASSERT_OR_RETURN(pTmpGpu, NV_ERR_INVALID_DEVICE);
1265 
1266         pKernelDisplay = GPU_GET_KERNEL_DISPLAY(pTmpGpu);
1267 
1268         // Read the GPU frame count and line count
1269         rmStatus = kdispReadRgLineCountAndFrameCount_HAL(pTmpGpu, pKernelDisplay,
1270                        pThis->FrameCountData.head, &lineCount, &frameCount);
1271         if (rmStatus != NV_OK)
1272         {
1273             NV_PRINTF(LEVEL_ERROR, "Failed to read RG_DPCA.\n");
1274             return rmStatus;
1275         }
1276 
1277         //
1278         // Check to ensure previousFrameCount != currentGpuFrmCnt. If this is not ensured,
1279         // it is possible to miss rollover condition.
1280         //
1281         if (pThis->FrameCountData.currentFrameCount != frameCount)
1282         {
1283             pThis->FrameCountData.previousFrameCount = pThis->FrameCountData.currentFrameCount;
1284             pThis->FrameCountData.currentFrameCount  = frameCount;
1285 
1286             // If rollback for gpu framecount has occured.
1287             if (pThis->FrameCountData.previousFrameCount > pThis->FrameCountData.currentFrameCount)
1288             {
1289                 pThis->FrameCountData.numberOfRollbacks++;
1290             }
1291         }
1292 
1293         calculatedDiff = pThis->FrameCountData.initialDifference +
1294                          pThis->FrameCountData.numberOfRollbacks * (NV_P2060_MAX_GPU_FRAME_COUNT + 1);
1295 
1296         pThis->FrameCountData.totalFrameCount = pThis->FrameCountData.currentFrameCount +
1297                                                 calculatedDiff;
1298 
1299         //
1300         // To keep sync between the sw framecount and gsync framecount.
1301         //
1302         // Gpu framecount increments after VTotal scanout lines  whereas Gsync
1303         // increments after VActive scanout lines. Thus it is necessary to
1304         // mitigate this difference of 1 when linecount > VActive.
1305         //
1306         if (lineCount > pThis->FrameCountData.vActive)
1307         {
1308             pThis->FrameCountData.totalFrameCount++;
1309         }
1310 
1311         *pFrameCount = pThis->FrameCountData.totalFrameCount;
1312 
1313         pThis->FrameCountData.lastFrameCounterQueryTime = currentTime;
1314     }
1315 
1316     //
1317     // Enable the frame compare match interrupt in master gsync, to detect if
1318     // rollover has occured. So that gsync and gpu frame count can be cached
1319     // again and difference between them is verified.
1320     //
1321     if ((!pThis->FrameCountData.isFrmCmpMatchIntMasterEnabled) &&
1322         (pThis->FrameCountData.totalFrameCount > (NV_P2060_MAX_GSYNC_FRAME_COUNT - 1000)))
1323     {
1324         if (gsyncIsOnlyFrameLockMaster_P2060(pThis))
1325         {
1326             NvU8 regCtrl3;
1327 
1328             // enable frame count match interrupt
1329             rmStatus = readregu008_extdeviceTargeted(pGpu, (PDACEXTERNALDEVICE)pThis, NV_P2060_CONTROL3,  &regCtrl3);
1330 
1331             regCtrl3 |= DRF_DEF(_P2060, _CONTROL3, _INTERRUPT, _ON_FRAME_MATCH);
1332             rmStatus |= writeregu008_extdeviceTargeted(pGpu, (PDACEXTERNALDEVICE)pThis, NV_P2060_CONTROL3, regCtrl3);
1333 
1334             pThis->FrameCountData.isFrmCmpMatchIntMasterEnabled = NV_TRUE;
1335 
1336             if (rmStatus != NV_OK)
1337             {
1338                 rmStatus |= gsyncResetFrameCountData_P2060(pGpu, pThis);
1339             }
1340         }
1341     }
1342     return rmStatus;
1343 }
1344 
1345 /*
1346  * Read Frame Rate register and calculate Framerate.
1347  */
1348 static NV_STATUS
1349 gsyncReadFrameRate_P2060
1350 (
1351     OBJGPU *pGpu,
1352     PDACEXTERNALDEVICE pExtDev,
1353     NvU32 *pFrameRate
1354 )
1355 {
1356     NvU8  FrameCountLow, FrameCountMid, FrameCountHigh;
1357     NvU32 FrameCount;
1358     NV_STATUS rmStatus = NV_OK;
1359 
1360     rmStatus |= readregu008_extdeviceTargeted(pGpu, pExtDev, (NvU8)NV_P2060_FRAMERATE_LOW,  &FrameCountLow);
1361     rmStatus |= readregu008_extdeviceTargeted(pGpu, pExtDev, (NvU8)NV_P2060_FRAMERATE_MID,  &FrameCountMid);
1362     rmStatus |= readregu008_extdeviceTargeted(pGpu, pExtDev, (NvU8)NV_P2060_FRAMERATE_HIGH, &FrameCountHigh);
1363 
1364     if ( NV_OK == rmStatus )
1365     {
1366         NvU32 divisor;
1367 
1368         FrameCount = ( ( (((NvU32)FrameCountHigh) & DRF_MASK(NV_P2060_FRAMERATE_HIGH_VAL)) << 16 )  |
1369                        ( (((NvU32)FrameCountMid)  & DRF_MASK(NV_P2060_FRAMERATE_MID_VAL )) <<  8 )  |
1370                        ( (((NvU32)FrameCountLow)  & DRF_MASK(NV_P2060_FRAMERATE_LOW_VAL ))       )  );
1371 
1372         divisor     = FrameCount + 2; // FPGA divider is 1
1373         *pFrameRate = FrameCount ? OVERFLOW_CAREFUL_MUL_DIV(160000000, 2048, divisor) : 0;
1374         *pFrameRate += 5; // take back one kadam, to honor the Extdev god,
1375         *pFrameRate -= *pFrameRate % 10;  // whose GSync board this is...
1376     }
1377     return rmStatus;
1378 }
1379 
1380 /*
1381  * Read House Sync Frame Rate register and calculate Framerate.
1382  */
1383 static NV_STATUS
1384 gsyncReadHouseSyncFrameRate_P2060
1385 (
1386     OBJGPU *pGpu,
1387     PDACEXTERNALDEVICE pExtDev,
1388     NvU32 *pFrameRate
1389 )
1390 {
1391     NvU8  FrameCountLow, FrameCountMid, FrameCountHigh;
1392     NvU32 FrameCount;
1393     NV_STATUS rmStatus = NV_OK;
1394 
1395     rmStatus |= readregu008_extdeviceTargeted(pGpu, pExtDev, (NvU8)NV_P2060_HS_FRAMERATE_LOW,  &FrameCountLow);
1396     rmStatus |= readregu008_extdeviceTargeted(pGpu, pExtDev, (NvU8)NV_P2060_HS_FRAMERATE_MID,  &FrameCountMid);
1397     rmStatus |= readregu008_extdeviceTargeted(pGpu, pExtDev, (NvU8)NV_P2060_HS_FRAMERATE_HIGH, &FrameCountHigh);
1398 
1399     if ( NV_OK == rmStatus )
1400     {
1401         NvU32 divisor;
1402 
1403         FrameCount = ( ( (((NvU32)FrameCountHigh) & DRF_MASK(NV_P2060_HS_FRAMERATE_HIGH_VAL)) << 16 )  |
1404                        ( (((NvU32)FrameCountMid)  & DRF_MASK(NV_P2060_HS_FRAMERATE_MID_VAL )) <<  8 )  |
1405                        ( (((NvU32)FrameCountLow)  & DRF_MASK(NV_P2060_HS_FRAMERATE_LOW_VAL ))       )  );
1406 
1407         divisor     = FrameCount + 2; // FPGA divider is 1
1408         *pFrameRate = FrameCount ? OVERFLOW_CAREFUL_MUL_DIV(160000000, 2048, divisor) : 0;
1409         *pFrameRate += 5; // take back one kadam, to honor the Extdev god,
1410         *pFrameRate -= *pFrameRate % 10;  // whose GSync board this is...
1411     }
1412     return rmStatus;
1413 }
1414 
1415 NV_STATUS
1416 gsyncOptimizeTimingParameters_P2060
1417 (
1418     OBJGPU            *pGpu,
1419     GSYNCTIMINGPARAMS *pParams
1420 )
1421 {
1422     RM_API   *pRmApi      = GPU_GET_PHYSICAL_RMAPI(pGpu);
1423     NvU32     hClient     = pGpu->hInternalClient;
1424     NvU32     hSubdevice  = pGpu->hInternalSubdevice;
1425     NV_STATUS status      = NV_OK;
1426     NV2080_CTRL_INTERNAL_GSYNC_OPTIMIZE_TIMING_PARAMETERS_PARAMS ctrlParams = {0};
1427 
1428     ctrlParams.timingParameters = *pParams;
1429 
1430     status = pRmApi->Control(pRmApi, hClient, hSubdevice,
1431                              NV2080_CTRL_CMD_INTERNAL_GSYNC_OPTIMIZE_TIMING_PARAMETERS,
1432                              &ctrlParams, sizeof(ctrlParams));
1433 
1434     if (status != NV_OK)
1435     {
1436         NV_PRINTF(LEVEL_ERROR, "OptimizeTimingParameters control call has failed! \n");
1437     }
1438     else
1439     {
1440         *pParams = ctrlParams.timingParameters;
1441     }
1442 
1443     return status;
1444 }
1445 
1446 /*
1447  * Program External Stereo Polarity to High side of master stereo
1448  */
1449 static NV_STATUS
1450 gsyncProgramExtStereoPolarity_P2060
1451 (
1452     OBJGPU  *pGpu,
1453     PDACEXTERNALDEVICE pExternalDevice
1454 )
1455 {
1456     NV_STATUS rmStatus = NV_OK;
1457     NvU8  ctrl4 = 0x00;
1458 
1459     rmStatus = readregu008_extdeviceTargeted(pGpu, pExternalDevice,
1460                                              NV_P2060_CONTROL4, &ctrl4);
1461     if (rmStatus != NV_OK)
1462     {
1463         return rmStatus;
1464     }
1465 
1466     // This bit controls stereo polarity relative to an external sync.
1467     ctrl4 = (NvU8) FLD_SET_DRF(_P2060, _CONTROL4, _EXT_STEREO_SYNC_POL, _HI, (NvU32)ctrl4);
1468 
1469     rmStatus = writeregu008_extdeviceTargeted(pGpu, pExternalDevice,
1470                                              NV_P2060_CONTROL4, ctrl4);
1471     return rmStatus;
1472 }
1473 
1474 NV_STATUS
1475 gsyncSetStereoLockMode_P2060
1476 (
1477     OBJGPU            *pGpu,
1478     PDACEXTERNALDEVICE pExtDev,
1479     NvU32              enable
1480 )
1481 {
1482     NvU8 ctrl4;
1483     NV_STATUS rmStatus;
1484 
1485     rmStatus = readregu008_extdeviceTargeted(pGpu, pExtDev, (NvU8)NV_P2060_CONTROL4, &ctrl4);
1486 
1487     if (rmStatus != NV_OK)
1488     {
1489          return rmStatus;
1490     }
1491 
1492     if (enable)
1493     {
1494         ctrl4 = FLD_SET_DRF(_P2060, _CONTROL4, _STEREO_LOCK_MODE, _ON, ctrl4);
1495     }
1496     else
1497     {
1498         ctrl4 = FLD_SET_DRF(_P2060, _CONTROL4, _STEREO_LOCK_MODE, _OFF, ctrl4);
1499     }
1500 
1501     rmStatus = writeregu008_extdeviceTargeted(pGpu, pExtDev, NV_P2060_CONTROL4, ctrl4);
1502 
1503     return rmStatus;
1504 }
1505 
1506 NV_STATUS
1507 gsyncGetStereoLockMode_P2060
1508 (
1509     OBJGPU            *pGpu,
1510     PDACEXTERNALDEVICE pExtDev,
1511     NvU32              *enable
1512 )
1513 {
1514     NvU8 ctrl4;
1515     NV_STATUS rmStatus;
1516 
1517     rmStatus = readregu008_extdeviceTargeted(pGpu, pExtDev, (NvU8)NV_P2060_CONTROL4, &ctrl4);
1518 
1519     if (NV_OK == rmStatus)
1520     {
1521         *enable = FLD_TEST_DRF(_P2060, _CONTROL4, _STEREO_LOCK_MODE, _ON, ctrl4);
1522     }
1523 
1524     return rmStatus;
1525 }
1526 
1527 NV_STATUS
1528 gsyncSetVideoMode_P2060
1529 (
1530     OBJGPU *pGpu,
1531     PDACEXTERNALDEVICE pExtDev,
1532     GSYNCVIDEOMODE VideoMode
1533 )
1534 {
1535     return NV_OK;
1536 }
1537 
1538 NV_STATUS
1539 gsyncGetVideoMode_P2060
1540 (
1541     OBJGPU *pGpu,
1542     PDACEXTERNALDEVICE pExtDev,
1543     GSYNCVIDEOMODE *pVideoMode
1544 )
1545 {
1546     NvU8 videoMode;
1547     NV_STATUS rmStatus;
1548     PDACP2060EXTERNALDEVICE pThis = (PDACP2060EXTERNALDEVICE)pExtDev;
1549 
1550     NV_ASSERT_OR_RETURN(pGpu, NV_ERR_GENERIC);
1551 
1552     rmStatus = readregu008_extdeviceTargeted(pGpu, pExtDev, (NvU8)NV_P2060_STATUS2, &videoMode);
1553 
1554     if ( NV_OK == rmStatus )
1555     {
1556         *pVideoMode = DRF_VAL(_P2060, _STATUS2, _HS_DETECT, videoMode);
1557         if (*pVideoMode == 0x02)
1558         {
1559             // reported videoMode is composite. Convert it to RMAPI exported value.
1560             *pVideoMode = gsync_VideoMode_COMPOSITE;
1561         }
1562     }
1563 
1564     // update p2060 object
1565     pThis->VideoMode = *pVideoMode;
1566 
1567     return rmStatus;
1568 }
1569 
1570 NV_STATUS
1571 gsyncSetEmitTestSignal_P2060
1572 (
1573     OBJGPU *pGpu,
1574     PDACEXTERNALDEVICE pExtDev,
1575     NvU32 bEmitTestSignal
1576 )
1577 {
1578     NvU8 ctrl;
1579     NV_STATUS rmStatus;
1580     PDACP2060EXTERNALDEVICE pThis = (PDACP2060EXTERNALDEVICE)pExtDev;
1581 
1582     // update p2060 object
1583     pThis->EmitTestSignal = bEmitTestSignal;
1584 
1585     if (!gsyncIsFrameLockMaster_P2060(pThis))
1586     {
1587         return NV_ERR_INVALID_DEVICE;
1588     }
1589 
1590     pGpu = GetP2060MasterableGpu(pGpu, (PDACP2060EXTERNALDEVICE)pExtDev);
1591     NV_ASSERT_OR_RETURN(pGpu, NV_ERR_GENERIC);
1592 
1593     rmStatus = readregu008_extdeviceTargeted(pGpu, pExtDev, (NvU8)NV_P2060_CONTROL, &ctrl);
1594 
1595     if ( bEmitTestSignal )
1596     {
1597         ctrl = FLD_SET_DRF(_P2060, _CONTROL, _TEST_MODE,  _ON, ctrl);
1598     }
1599     else
1600     {
1601         ctrl = FLD_SET_DRF(_P2060, _CONTROL, _TEST_MODE, _OFF, ctrl);
1602 
1603         //
1604         // To enable frameCountTimerService callback which verifies
1605         // the cache difference 1 second after sending the test signal
1606         //
1607         pThis->FrameCountData.bReCheck = 1;
1608 
1609         //
1610         // Reset framecountData.Therefore whenever user queries after TEST_MODE_OFF,
1611         // gsync and gpu frame count register are read and difference is cached again.
1612         //
1613         rmStatus |= gsyncResetFrameCountData_P2060(pGpu, pThis);
1614     }
1615 
1616     rmStatus |= writeregu008_extdeviceTargeted(pGpu, pExtDev, NV_P2060_CONTROL, ctrl);
1617 
1618     //
1619     // Add some OS delay between test mode transitions. This is also
1620     // necessary for proper propogation of test signal to other slaves.
1621     //
1622     {
1623         NvU32 refreshTime = 0;
1624         //
1625         // The test mode detection logic will return the previous value until the
1626         // next frame.  Although only the master will only manipulate test mode,
1627         // we may transition to slave while test mode is still on due to this.
1628         // So delay for a couple of frames to make sure the value transitions
1629         // correctly. Fix for bug #82809, back in the day.
1630         //
1631         // P2060 refreshrate is in 0.00001 Hz, so divide by 10000 to get Hz.
1632         //
1633         if ((pThis->RefreshRate/10000) > 0)
1634         {
1635             refreshTime = 1000 / (pThis->RefreshRate/10000);
1636         }
1637         NV_ASSERT(refreshTime != 0);
1638 
1639         // Only wait a maximum amount of time.
1640         refreshTime = NV_MIN(refreshTime, 35);
1641         osDelay(2*refreshTime /* ms */);
1642     }
1643 
1644     return rmStatus;
1645 }
1646 
1647 NV_STATUS
1648 gsyncGetEmitTestSignal_P2060
1649 (
1650  OBJGPU *pGpu,
1651  PDACEXTERNALDEVICE pExtDev,
1652  NvU32 *pVal
1653 )
1654 {
1655     NvU8 ctrl;
1656     NV_STATUS rmStatus;
1657     PDACP2060EXTERNALDEVICE pThis = (PDACP2060EXTERNALDEVICE)pExtDev;
1658 
1659     rmStatus = readregu008_extdeviceTargeted(pGpu, pExtDev, (NvU8)NV_P2060_CONTROL, &ctrl);
1660 
1661     if ( NV_OK == rmStatus )
1662     {
1663         *pVal = FLD_TEST_DRF(_P2060, _CONTROL, _TEST_MODE, _ON, ctrl);
1664     }
1665 
1666     // update p2060 object
1667     pThis->EmitTestSignal = *pVal;
1668 
1669     return rmStatus;
1670 }
1671 
1672 NV_STATUS
1673 gsyncSetInterlaceMode_P2060
1674 (
1675     OBJGPU *pGpu,
1676     PDACEXTERNALDEVICE pExtDev,
1677     NvU32 InterlaceMode
1678 )
1679 {
1680     NvU8 ctrl;
1681     NV_STATUS rmStatus;
1682     PDACP2060EXTERNALDEVICE pThis = (PDACP2060EXTERNALDEVICE)pExtDev;
1683 
1684     // update p2060 object
1685     pThis->InterlaceMode = InterlaceMode;
1686 
1687     pGpu = GetP2060MasterableGpu(pGpu, (PDACP2060EXTERNALDEVICE)pExtDev);
1688     NV_ASSERT_OR_RETURN(pGpu, NV_ERR_GENERIC);
1689 
1690     rmStatus = readregu008_extdeviceTargeted(pGpu, pExtDev, (NvU8)NV_P2060_CONTROL, &ctrl);
1691 
1692     if ( rmStatus == NV_OK )
1693     {
1694         ctrl = FLD_SET_DRF_NUM(_P2060, _CONTROL, _INTERLACE_MODE, (NvU8)InterlaceMode, ctrl);
1695         rmStatus = writeregu008_extdeviceTargeted(pGpu, pExtDev, NV_P2060_CONTROL, ctrl);
1696     }
1697 
1698     return rmStatus;
1699 }
1700 
1701 NV_STATUS
1702 gsyncGetInterlaceMode_P2060
1703 (
1704     OBJGPU *pGpu,
1705     PDACEXTERNALDEVICE pExtDev,
1706     NvU32 *pVal
1707 )
1708 {
1709     NvU8 ctrl;
1710     NV_STATUS rmStatus;
1711     PDACP2060EXTERNALDEVICE pThis = (PDACP2060EXTERNALDEVICE)pExtDev;
1712 
1713     pGpu = GetP2060MasterableGpu(pGpu, (PDACP2060EXTERNALDEVICE)pExtDev);
1714     NV_ASSERT_OR_RETURN(pGpu, NV_ERR_GENERIC);
1715 
1716     rmStatus = readregu008_extdeviceTargeted(pGpu, pExtDev, (NvU8)NV_P2060_CONTROL, &ctrl);
1717 
1718     if ( NV_OK == rmStatus )
1719     {
1720         *pVal = FLD_TEST_DRF(_P2060, _CONTROL, _INTERLACE_MODE, _TRUE, ctrl);
1721     }
1722 
1723     // update p2060 object
1724     pThis->InterlaceMode = *pVal;
1725 
1726     return rmStatus;
1727 }
1728 
1729 NV_STATUS
1730 gsyncSetUseHouse_P2060
1731 (
1732     OBJGPU *pGpu,
1733     PDACEXTERNALDEVICE pExtDev,
1734     NvU32 UseHouseSync
1735 )
1736 {
1737     NvU8 ctrl;
1738     NV_STATUS rmStatus = NV_OK;
1739     PDACP2060EXTERNALDEVICE pThis = (PDACP2060EXTERNALDEVICE)pExtDev;
1740 
1741     // update p2060 object
1742     pThis->UseHouseSync = UseHouseSync;
1743 
1744     pGpu = GetP2060MasterableGpu(pGpu, (PDACP2060EXTERNALDEVICE)pExtDev);
1745     NV_ASSERT_OR_RETURN(pGpu, NV_ERR_GENERIC);
1746 
1747     rmStatus = readregu008_extdeviceTargeted(pGpu, pExtDev, NV_P2060_CONTROL, &ctrl);
1748 
1749     if (NV_OK == rmStatus)
1750     {
1751         ctrl = FLD_SET_DRF_NUM(_P2060, _CONTROL, _SYNC_SELECT, (NvU8)UseHouseSync, ctrl);
1752         rmStatus = writeregu008_extdeviceTargeted(pGpu, pExtDev, NV_P2060_CONTROL, ctrl);
1753     }
1754 
1755     return rmStatus;
1756 }
1757 
1758 NV_STATUS
1759 gsyncGetUseHouse_P2060
1760 (
1761     OBJGPU *pGpu,
1762     PDACEXTERNALDEVICE pExtDev,
1763     NvU32 *val
1764 )
1765 {
1766     NvU8 ctrl;
1767     NV_STATUS rmStatus = NV_OK;
1768     PDACP2060EXTERNALDEVICE pThis = (PDACP2060EXTERNALDEVICE)pExtDev;
1769 
1770     rmStatus = readregu008_extdeviceTargeted(pGpu, pExtDev, NV_P2060_CONTROL, &ctrl);
1771 
1772     if (NV_OK == rmStatus)
1773     {
1774         *val = FLD_TEST_DRF(_P2060, _CONTROL, _SYNC_SELECT, _HOUSE, ctrl);
1775 
1776         // update p2060 object
1777         pThis->UseHouseSync = *val;
1778     }
1779 
1780     return rmStatus;
1781 }
1782 
1783 NV_STATUS
1784 gsyncSetSyncPolarity_P2060
1785 (
1786     OBJGPU *pGpu,
1787     PDACEXTERNALDEVICE pExtDev,
1788     GSYNCSYNCPOLARITY SyncPolarity
1789 )
1790 {
1791     NvU8 ctrl;
1792     NV_STATUS rmStatus;
1793     PDACP2060EXTERNALDEVICE pThis = (PDACP2060EXTERNALDEVICE)pExtDev;
1794     GSYNCSYNCPOLARITY currentSyncPolarity;
1795 
1796     // update p2060 object
1797     pThis->SyncPolarity = SyncPolarity;
1798 
1799     pGpu = GetP2060MasterableGpu(pGpu, (PDACP2060EXTERNALDEVICE)pExtDev);
1800     NV_ASSERT_OR_RETURN(pGpu, NV_ERR_GENERIC);
1801 
1802     rmStatus = readregu008_extdeviceTargeted(pGpu, pExtDev, (NvU8)NV_P2060_CONTROL, &ctrl);
1803 
1804     if ( NV_OK == rmStatus )
1805     {
1806         currentSyncPolarity = DRF_VAL(_P2060, _CONTROL, _SYNC_POLARITY, ctrl);
1807 
1808         if (currentSyncPolarity != SyncPolarity)
1809         {
1810             NvU32 frameTime = 0;
1811 
1812             ctrl = FLD_SET_DRF_NUM(_P2060, _CONTROL, _SYNC_POLARITY, (NvU8)SyncPolarity, ctrl);
1813             rmStatus = writeregu008_extdeviceTargeted(pGpu, pExtDev, NV_P2060_CONTROL, ctrl);
1814 
1815             if ((pThis->RefreshRate/10000) > 0)
1816             {
1817                 frameTime = 1000 / (pThis->RefreshRate/10000);
1818             }
1819 
1820             //
1821             // Wait for max of 10 frames or 100 ms so that hardware can collect
1822             // the new house sync frame rate.
1823             //
1824             frameTime = NV_MAX(frameTime, 10);
1825             osDelay(10 * frameTime /* ms */);
1826         }
1827     }
1828 
1829     return rmStatus;
1830 }
1831 
1832 NV_STATUS
1833 gsyncGetSyncPolarity_P2060
1834 (
1835     OBJGPU *pGpu,
1836     PDACEXTERNALDEVICE pExtDev,
1837     GSYNCSYNCPOLARITY *pSyncPolarity
1838 )
1839 {
1840     NvU8 ctrl;
1841     NV_STATUS rmStatus;
1842     PDACP2060EXTERNALDEVICE pThis = (PDACP2060EXTERNALDEVICE)pExtDev;
1843 
1844     rmStatus = readregu008_extdeviceTargeted(pGpu, pExtDev, (NvU8)NV_P2060_CONTROL, &ctrl);
1845 
1846     if ( NV_OK == rmStatus )
1847     {
1848         *pSyncPolarity = DRF_VAL(_P2060, _CONTROL, _SYNC_POLARITY, ctrl);
1849     }
1850 
1851     // update p2060 object
1852     pThis->SyncPolarity = *pSyncPolarity;
1853 
1854     return rmStatus;
1855 }
1856 
1857 NV_STATUS
1858 gsyncSetNSync_P2060
1859 (
1860     OBJGPU *pGpu,
1861     PDACEXTERNALDEVICE pExtDev,
1862     NvU32 NSync
1863 )
1864 {
1865     NvU8 regNSync;
1866     NV_STATUS rmStatus;
1867     PDACP2060EXTERNALDEVICE pThis = (PDACP2060EXTERNALDEVICE)pExtDev;
1868 
1869     // update p2060 object
1870     pThis->NSync = NSync;
1871 
1872     rmStatus = readregu008_extdeviceTargeted(pGpu, pExtDev, (NvU8)NV_P2060_NSYNC, &regNSync);
1873 
1874     if ( NV_OK == rmStatus )
1875     {
1876         regNSync = FLD_SET_DRF_NUM(_P2060, _NSYNC, _FL, (NvU8)NSync, regNSync);
1877         rmStatus = writeregu008_extdeviceTargeted(pGpu, pExtDev, NV_P2060_NSYNC, regNSync);
1878     }
1879 
1880     return rmStatus;
1881 }
1882 
1883 NV_STATUS
1884 gsyncGetNSync_P2060
1885 (
1886     OBJGPU *pGpu,
1887     PDACEXTERNALDEVICE pExtDev,
1888     NvU32 *pNSync
1889 )
1890 {
1891     NvU8 regNSync;
1892     NV_STATUS rmStatus;
1893     PDACP2060EXTERNALDEVICE pThis = (PDACP2060EXTERNALDEVICE)pExtDev;
1894 
1895     rmStatus = readregu008_extdeviceTargeted(pGpu, pExtDev, (NvU8)NV_P2060_NSYNC, &regNSync);
1896 
1897     if ( NV_OK == rmStatus )
1898     {
1899         *pNSync = DRF_VAL(_P2060, _NSYNC, _FL, regNSync);
1900     }
1901 
1902     // update p2060 object
1903     pThis->NSync = *pNSync;
1904 
1905     return rmStatus;
1906 }
1907 
1908 NV_STATUS
1909 gsyncSetSyncSkew_P2060
1910 (
1911     OBJGPU *pGpu,
1912     PDACEXTERNALDEVICE pExtDev,
1913     NvU32 SyncSkew
1914 )
1915 {
1916     NvU8 SyncSkewLow, SyncSkewHigh;
1917     NV_STATUS rmStatus = NV_OK;
1918     PDACP2060EXTERNALDEVICE pThis = (PDACP2060EXTERNALDEVICE)pExtDev;
1919 
1920     // update p2060 object
1921     pThis->SyncSkew = SyncSkew;
1922 
1923     if (gsyncSupportsLargeSyncSkew_P2060(pExtDev))
1924     {
1925         SyncSkewLow  = (NvU8)((SyncSkew     ) & DRF_MASK(NV_P2060_SYNC_SKEW_LOW_VAL ));
1926         SyncSkewHigh = (NvU8)((SyncSkew >> 8) & DRF_MASK(NV_P2060_SYNC_SKEW_HIGH_VAL));
1927     }
1928     else
1929     {
1930         if ((SyncSkew != 0) && (SyncSkew != 1))
1931         {
1932             return NV_ERR_NOT_SUPPORTED;
1933         }
1934         else
1935         {
1936             SyncSkewLow  = (NvU8)SyncSkew;
1937             SyncSkewHigh = 0x00;
1938        }
1939     }
1940 
1941     rmStatus |= writeregu008_extdeviceTargeted(pGpu, pExtDev, (NvU8)NV_P2060_SYNC_SKEW_LOW, SyncSkewLow);
1942     rmStatus |= writeregu008_extdeviceTargeted(pGpu, pExtDev, (NvU8)NV_P2060_SYNC_SKEW_HIGH, SyncSkewHigh);
1943 
1944     return rmStatus;
1945 
1946 }
1947 
1948 NV_STATUS
1949 gsyncGetSyncSkew_P2060
1950 (
1951     OBJGPU *pGpu,
1952     PDACEXTERNALDEVICE pExtDev,
1953     NvU32 *pSyncSkew
1954 )
1955 {
1956     NvU8 SyncSkewLow, SyncSkewHigh;
1957     NV_STATUS rmStatus = NV_OK;
1958     PDACP2060EXTERNALDEVICE pThis = (PDACP2060EXTERNALDEVICE)pExtDev;
1959 
1960     rmStatus |= readregu008_extdeviceTargeted(pGpu, pExtDev, (NvU8)NV_P2060_SYNC_SKEW_LOW,  &SyncSkewLow);
1961     rmStatus |= readregu008_extdeviceTargeted(pGpu, pExtDev, (NvU8)NV_P2060_SYNC_SKEW_HIGH, &SyncSkewHigh);
1962 
1963     if ( NV_OK == rmStatus )
1964     {
1965         *pSyncSkew =
1966             ( ((((NvU32)SyncSkewHigh) & DRF_MASK(NV_P2060_SYNC_SKEW_HIGH_VAL)) << 8 ) |
1967               ((((NvU32)SyncSkewLow ) & DRF_MASK(NV_P2060_SYNC_SKEW_LOW_VAL ))      ) ) ;
1968     }
1969 
1970     // update p2060 object
1971     pThis->SyncSkew = *pSyncSkew;
1972 
1973     return rmStatus;
1974 }
1975 
1976 NV_STATUS
1977 gsyncSetSyncStartDelay_P2060
1978 (
1979     OBJGPU *pGpu,
1980     PDACEXTERNALDEVICE pExtDev,
1981     NvU32 StartDelay
1982 )
1983 {
1984     NvU8 StartDelayLow, StartDelayHigh;
1985     NV_STATUS rmStatus = NV_OK;
1986     PDACP2060EXTERNALDEVICE pThis = (PDACP2060EXTERNALDEVICE)pExtDev;
1987 
1988     // update p2060 object
1989     pThis->SyncStartDelay = StartDelay;
1990 
1991     StartDelayLow = (NvU8)((StartDelay     ) & DRF_MASK(NV_P2060_START_DELAY_LOW_VAL ));
1992     StartDelayHigh= (NvU8)((StartDelay >> 8) & DRF_MASK(NV_P2060_START_DELAY_HIGH_VAL));
1993 
1994     rmStatus |= writeregu008_extdeviceTargeted(pGpu, pExtDev, (NvU8)NV_P2060_START_DELAY_LOW, StartDelayLow);
1995     rmStatus |= writeregu008_extdeviceTargeted(pGpu, pExtDev, (NvU8)NV_P2060_START_DELAY_HIGH, StartDelayHigh);
1996 
1997     return rmStatus;
1998 }
1999 
2000 NV_STATUS
2001 gsyncGetSyncStartDelay_P2060
2002 (
2003     OBJGPU *pGpu,
2004     PDACEXTERNALDEVICE pExtDev,
2005     NvU32 *pStartDelay
2006 )
2007 {
2008     NvU8 StartDelayLow, StartDelayHigh;
2009     NV_STATUS rmStatus = NV_OK;
2010     PDACP2060EXTERNALDEVICE pThis = (PDACP2060EXTERNALDEVICE)pExtDev;
2011 
2012     rmStatus |= readregu008_extdeviceTargeted(pGpu, pExtDev, (NvU8)NV_P2060_START_DELAY_LOW, &StartDelayLow);
2013     rmStatus |= readregu008_extdeviceTargeted(pGpu, pExtDev, (NvU8)NV_P2060_START_DELAY_HIGH, &StartDelayHigh);
2014 
2015     if ( NV_OK == rmStatus )
2016     {
2017         *pStartDelay =
2018             ( ((((NvU32)StartDelayHigh) & DRF_MASK(NV_P2060_START_DELAY_HIGH_VAL)) << 8 ) |
2019               ((((NvU32)StartDelayLow ) & DRF_MASK(NV_P2060_START_DELAY_LOW_VAL ))      ) );
2020     }
2021 
2022     // update p2060 object
2023     pThis->SyncStartDelay = *pStartDelay;
2024 
2025     return rmStatus;
2026 }
2027 
2028 /*
2029  * check if housesync is present or not.
2030  */
2031 static NV_STATUS
2032 gsyncReadHouseSignalPresent_P2060
2033 (
2034     OBJGPU *pGpu,
2035     PDACEXTERNALDEVICE pExtDev,
2036     NvBool bTestSyncLoss,
2037     NvU32 *pVal
2038 )
2039 {
2040     PDACP2060EXTERNALDEVICE pThis = (PDACP2060EXTERNALDEVICE)pExtDev;
2041     NvU8 regStatus2;
2042     NvU32 regStatus = GetP2060GpuSnapshot(pGpu,pThis);
2043     NV_STATUS rmStatus = NV_OK;
2044 
2045     NV_ASSERT_OR_RETURN(pGpu, NV_ERR_GENERIC);
2046 
2047     if (bTestSyncLoss && FLD_TEST_DRF(_P2060, _STATUS, _SYNC_LOSS, _TRUE, (NvU32)regStatus))
2048     {
2049         rmStatus |= gsyncUpdateGsyncStatusSnapshot_P2060(pGpu, pExtDev);
2050 
2051         if ( NV_OK != rmStatus )
2052             return rmStatus;
2053 
2054         if (FLD_TEST_DRF(_P2060, _STATUS, _SYNC_LOSS, _TRUE, GetP2060GpuSnapshot(pGpu,pThis)))
2055         {
2056             *pVal = 0; // bTestSyncLoss and (SYNC_LOSS == NV_TRUE)
2057         }
2058     }
2059     else
2060     {
2061         rmStatus = readregu008_extdeviceTargeted(pGpu, (PDACEXTERNALDEVICE)pThis,
2062                                                  (NvU8)NV_P2060_STATUS2, &regStatus2);
2063         if ( NV_OK != rmStatus )
2064             return rmStatus;
2065 
2066         *pVal = !!(DRF_VAL(_P2060, _STATUS2, _HS_DETECT, (NvU32)regStatus2));
2067     }
2068 
2069     return rmStatus;
2070 }
2071 
2072 /*
2073  * This function returns whether there is a sync source present.
2074  *
2075  * SYNC_LOSS bit in STATUS register is true by default, i.e when there
2076  * is no incoming sync source. This bit is unset when a stable sync
2077  * source is detected.
2078  *
2079  * Whenever framelock is enabled, NV_TRUE is returned only when the incoming
2080  * sync source is stable. Therefore it is sent based on the software state.
2081  *
2082  * When framelock is not enabled, return value totally depends on the value
2083  * of the gsync STATUS register.
2084  */
2085 static NV_STATUS
2086 gsyncReadIsSyncDetected_P2060
2087 (
2088     OBJGPU *pGpu,
2089     PDACEXTERNALDEVICE pExtDev,
2090     NvU32 *pVal
2091 )
2092 {
2093     PDACP2060EXTERNALDEVICE pThis = (PDACP2060EXTERNALDEVICE)pExtDev;
2094     NV_STATUS rmStatus = NV_OK;
2095     NvU32 iface;
2096 
2097     NV_ASSERT_OR_RETURN(pGpu, NV_ERR_GENERIC);
2098 
2099     if (!gsyncIsFrameLocked_P2060(pThis))
2100     {
2101         NvU8 regStatus;
2102 
2103         // framelock is not enabled, read the NV_P2060_STATUS register to get the SYNC status
2104         rmStatus = readregu008_extdeviceTargeted(pGpu, (PDACEXTERNALDEVICE)pThis, (NvU8)NV_P2060_STATUS, &regStatus);
2105 
2106         if (rmStatus == NV_OK)
2107         {
2108             *pVal = (NV_P2060_STATUS_SYNC_LOSS_TRUE !=  DRF_VAL(_P2060, _STATUS, _SYNC_LOSS, regStatus));
2109         }
2110     }
2111     else
2112     {
2113         rmStatus = GetP2060GpuLocation(pGpu, pThis, &iface);
2114         if (NV_OK != rmStatus)
2115         {
2116             return rmStatus;
2117         }
2118 
2119         //
2120         // Sync gain should only be derived from our internal tracking variable.
2121         // The gsync status variables are reporting more transient data than desired.
2122         //
2123         *pVal = pThis->Iface[iface].gainedSync;
2124     }
2125 
2126     return rmStatus;
2127 }
2128 
2129 /*
2130  * Check if stereo is locked or not.
2131  *
2132  * stereo is locked means
2133  * - a framelock master signal is available as reference
2134  * - sync to this master signal has been gained
2135  * - master and local gpu both have either stereo enabled or disabled
2136  * - master and local stereo signal is in phase (in case it's enabled)
2137  */
2138 static NV_STATUS
2139 gsyncReadStereoLocked_P2060
2140 (
2141     OBJGPU *pGpu,
2142     PDACEXTERNALDEVICE pExtDev,
2143     NvU32 *pVal
2144 )
2145 {
2146     PDACP2060EXTERNALDEVICE pThis = (PDACP2060EXTERNALDEVICE)pExtDev;
2147     NV_STATUS rmStatus = NV_ERR_GENERIC;
2148 
2149     if (pVal)
2150     {
2151         NvU32 iface;
2152 
2153         // Default return is stereo not locked.
2154         *pVal = 0;
2155 
2156         // which gpu's interface are we talking to?
2157         rmStatus = GetP2060GpuLocation(pGpu, pThis, &iface);
2158 
2159         if (NV_OK == rmStatus)
2160         {
2161             // stereo status reporting only makes sense if we've gained sync.
2162             if (pThis->Iface[iface].gainedSync)
2163             {
2164                 NvU32 regStatus = GetP2060GpuSnapshot(pGpu,pThis);
2165 
2166                 if ((NV_P2060_STATUS_MSTR_STEREO_NOT_ACTIVE ==
2167                     DRF_VAL(_P2060, _STATUS, _MSTR_STEREO, regStatus)) &&
2168                     (NV_P2060_STATUS_GPU_STEREO_NOT_ACTIVE ==
2169                     DRF_VAL(_P2060, _STATUS, _GPU_STEREO, regStatus)))
2170                 {
2171                     //
2172                     // If neither local nor master stereo is enabled
2173                     // stereo is locked.
2174                     //
2175                     *pVal = 1;
2176                 }
2177                 else
2178                 {
2179                     //
2180                     // If local or master stereo signals are present,
2181                     // return back P358s stereo_lock reporting.
2182                     //
2183                     *pVal = (NV_P2060_STATUS_STEREO_LOCK ==
2184                         DRF_VAL(_P2060, _STATUS, _STEREO, regStatus));
2185                 }
2186             }
2187         }
2188     }
2189 
2190     return rmStatus;
2191 }
2192 
2193 /*
2194  * Check if VCXO is locked or not.
2195  */
2196 static NV_STATUS
2197 gsyncReadVCXOLocked_P2060
2198 (
2199     OBJGPU *pGpu,
2200     PDACEXTERNALDEVICE pExtDev,
2201     NvU32 *pVal
2202 )
2203 {
2204     PDACP2060EXTERNALDEVICE pThis = (PDACP2060EXTERNALDEVICE)pExtDev;
2205     NV_STATUS rmStatus = NV_OK;
2206     NvU32 regStatus =  GetP2060GpuSnapshot(pGpu,pThis);
2207 
2208     // Status1 only needs to be read if the shapshot shows recent error or no vcxo lock
2209     if (!FLD_TEST_DRF(_P2060, _STATUS, _VCXO, _LOCK, regStatus))
2210     {
2211         rmStatus = gsyncUpdateGsyncStatusSnapshot_P2060(pGpu, pExtDev);
2212         if ( NV_OK == rmStatus )
2213         {
2214             *pVal = FLD_TEST_DRF(_P2060, _STATUS, _VCXO, _LOCK, GetP2060GpuSnapshot(pGpu,pThis));
2215         }
2216     }
2217     else
2218     {
2219         *pVal = 1; //VCXO is locked.
2220     }
2221 
2222     return rmStatus;
2223 }
2224 
2225 /*
2226  * Check if timing is present or not.
2227  */
2228 static NV_STATUS
2229 gsyncReadIsTiming_P2060
2230 (
2231     OBJGPU *pGpu,
2232     PDACEXTERNALDEVICE pExtDev,
2233     NvU32 *pVal
2234 )
2235 {
2236     PDACP2060EXTERNALDEVICE pThis = (PDACP2060EXTERNALDEVICE)pExtDev;
2237     NV_STATUS rmStatus = NV_OK;
2238     KernelDisplay  *pKernelDisplay = GPU_GET_KERNEL_DISPLAY(pGpu);
2239     NvU32 numHeads = kdispGetNumHeads(pKernelDisplay);
2240     NvU32 bHouse, bLock = 0, bSync;
2241     NvU32 iface, head, tempIface, tempHead;
2242 
2243     rmStatus = GetP2060GpuLocation(pGpu, pThis, &iface);
2244     if (NV_OK != rmStatus)
2245     {
2246          return rmStatus;
2247     }
2248 
2249     // assume no, unless we find something below
2250     *pVal = (NvU32)NV_FALSE;
2251 
2252     for (head = 0; head < numHeads; head++)
2253     {
2254         // check if we're slaved to another master head in the same system
2255         if (pThis->Iface[iface].Sync.LocalSlave[head])
2256         {
2257             for (tempIface = 0; tempIface < NV_P2060_MAX_IFACES_PER_GSYNC; tempIface++)
2258             {
2259                 for (tempHead = 0; tempHead < numHeads; tempHead++)
2260                 {
2261                     if (pThis->Iface[tempIface].Sync.Master[tempHead])
2262                     {
2263                         // assume that if we're slaved to another local head
2264                         // that we're in sync. there's no easy way to verify
2265                         // this, short of trying to compare raster scanlines
2266                         *pVal = (NvU32)NV_TRUE;
2267                     }
2268                 }
2269             }
2270             break;
2271         }
2272 
2273         // check if we have a master, connected to a house sync
2274         if (pThis->Iface[iface].Sync.Master[head] && pThis->Iface[iface].Sync.Slaved[head])
2275         {
2276             rmStatus |= gsyncReadHouseSignalPresent_P2060(pGpu, pExtDev, NV_TRUE, &bHouse);
2277             if ( NV_OK == rmStatus )
2278             {
2279                 *pVal = (NvU32)(bHouse);
2280             }
2281             break;
2282         }
2283 
2284         // check if we have a master head, not connected to house sync
2285         if (pThis->Iface[iface].Sync.Master[head])
2286         {
2287             // a master is by definition always in sync
2288             *pVal = (NvU32)NV_TRUE;
2289             break;
2290         }
2291 
2292         if (pThis->Iface[iface].Sync.Slaved[head])
2293         {
2294             rmStatus |= gsyncReadIsSyncDetected_P2060(pGpu, pExtDev, &bSync);
2295             rmStatus |= gsyncReadVCXOLocked_P2060(pGpu, pExtDev, &bLock);
2296             if ( NV_OK == rmStatus )
2297             {
2298                 *pVal = (NvU32)(bSync && bLock);
2299             }
2300             break;
2301         }
2302     }
2303     return rmStatus;
2304 }
2305 
2306 
2307 /*
2308  * Program P2060 Master for Framelock.
2309  */
2310 static NV_STATUS
2311 gsyncProgramMaster_P2060
2312 (
2313     OBJGPU *pGpu,
2314     PDACP2060EXTERNALDEVICE pThis,
2315     NvU32 Master,
2316     NvBool bRetainMaster,
2317     NvBool skipSwapBarrierWar
2318 )
2319 {
2320     KernelDisplay  *pKernelDisplay = GPU_GET_KERNEL_DISPLAY(pGpu);
2321     NvU32       DisplayIds[OBJ_MAX_HEADS];
2322     NvU32       iface, head, index;
2323     NvU8        ctrl = 0;
2324     NvBool      bTestModePresent;
2325     NvBool      bHouseSelect, bEnableMaster = (0 != Master);
2326     NvBool      bGPUAlreadyMaster;
2327     NvBool      bQSyncAlreadyMaster;
2328     NV_STATUS   rmStatus = NV_OK;
2329     NvU32 numHeads = kdispGetNumHeads(pKernelDisplay);
2330 
2331     if ( Master && bRetainMaster)
2332     {
2333         for (iface = 0; iface < NV_P2060_MAX_IFACES_PER_GSYNC; iface++)
2334         {
2335             for ( head = 0; head < numHeads; head++ )
2336             {
2337                 if ( pThis->Iface[iface].Sync.Master[head] )
2338                 {
2339                     pThis->Iface[iface].Sync.Master[head] = 0;
2340                     pThis->Iface[iface].Sync.Slaved[head] = 0;
2341                     pThis->Iface[iface].Sync.LocalSlave[head] = 0;
2342                 }
2343             }
2344         }
2345         return rmStatus;
2346     }
2347 
2348     // This utility fn returns display id's associated with each head.
2349     extdevGetBoundHeadsAndDisplayIds(pGpu, DisplayIds);
2350 
2351     // which gpu's are we talking to?
2352     rmStatus = GetP2060GpuLocation(pGpu, pThis, &iface);
2353     if (NV_OK != rmStatus)
2354     {
2355         NV_PRINTF(LEVEL_ERROR,
2356                   "Failed to get Gpu location. Can not program Master.\n");
2357         return rmStatus;
2358     }
2359 
2360     // no failure allowed!
2361     rmStatus = readregu008_extdeviceTargeted(pGpu, (PDACEXTERNALDEVICE)pThis,
2362                                             (NvU8)NV_P2060_CONTROL, &ctrl);
2363     if ((NV_OK != rmStatus))
2364     {
2365         NV_PRINTF(LEVEL_ERROR,
2366                   "Failed to read Ctrl data. Can not program Master.\n");
2367         return rmStatus;
2368     }
2369 
2370     // Check if TEST MODE present
2371     bTestModePresent = FLD_TEST_DRF(_P2060, _CONTROL, _TEST_MODE, _ON, (NvU32)ctrl);
2372 
2373     // Check for House sync select as sync source
2374     bHouseSelect = FLD_TEST_DRF(_P2060, _CONTROL, _SYNC_SELECT, _HOUSE, (NvU32)ctrl);
2375 
2376     rmStatus = GetP2060ConnectorIndexFromGpu(pGpu, pThis, &index);
2377     if (NV_OK != rmStatus)
2378     {
2379         NV_PRINTF(LEVEL_ERROR,
2380                   "Failed to get connector index for Gpu. Can not program Master.\n");
2381         return rmStatus;
2382     }
2383     //
2384     // For P2060, Already Mastership based on FPGA -> I_AM_MASTER + GPU -> TIMING SOURCE.
2385     // First check for display is already Master or not i.e. GPU is TS or not.
2386     // Then check for FPGA board is already Master or not.
2387     //
2388     bGPUAlreadyMaster = (DRF_VAL(_P2060, _CONTROL, _SYNC_SRC, (NvU32)ctrl) == index);
2389     bQSyncAlreadyMaster = FLD_TEST_DRF(_P2060, _CONTROL, _I_AM, _MASTER, (NvU32)ctrl);
2390 
2391     // In case of Passthru mode, Qsync board can be shared across multiple VMs.
2392     // If Qsync board is already master, then only TS Gpu should be allowed to change it's mastership.
2393     // Bail out if non TS GPU tries to change it.
2394     if (IS_PASSTHRU(pGpu) && (bQSyncAlreadyMaster & !bGPUAlreadyMaster))
2395     {
2396         return rmStatus;
2397     }
2398 
2399     if (bQSyncAlreadyMaster != bEnableMaster)
2400     {
2401         if (pThis->ExternalDevice.deviceRev == DAC_EXTERNAL_DEVICE_REV_NONE)
2402         {
2403             NV_PRINTF(LEVEL_ERROR,
2404                       "Failed to read NV_P2060_FPGA. Can not program Master.\n");
2405             return rmStatus;
2406         }
2407 
2408         if (bEnableMaster)
2409         {
2410             rmStatus = gsyncApplyStereoPinAlwaysHiWar(pGpu, (PDACEXTERNALDEVICE)pThis);
2411 
2412             if (NV_OK != rmStatus)
2413             {
2414                 NV_PRINTF(LEVEL_ERROR,
2415                           "Failed to drive stereo output pin for bug3362661.\n");
2416             }
2417             //
2418             // GPU will now be TS - Mark sync source for GPU on derived index.
2419             // This needs to be done first as only TS can write I_AM_MASTER bit.
2420             //
2421             ctrl = FLD_SET_DRF_NUM(_P2060, _CONTROL, _SYNC_SRC, (NvU8)index, ctrl);
2422             rmStatus |= writeregu008_extdeviceTargeted(pGpu, (PDACEXTERNALDEVICE) pThis,
2423                                                       (NvU8)NV_P2060_CONTROL, ctrl);
2424             if (NV_OK != rmStatus)
2425             {
2426                 NV_PRINTF(LEVEL_ERROR,
2427                           "Failed to write SYNC_SRC. Can not program Master.\n");
2428                 return rmStatus;
2429             }
2430         }
2431 
2432         if (bTestModePresent)
2433         {
2434             // Clear the TEST mode bit before handling enable/disable master.
2435             ctrl = FLD_SET_DRF(_P2060, _CONTROL, _TEST_MODE, _OFF, ctrl);
2436 
2437             if (!bEnableMaster)
2438             {
2439                 //
2440                 // Clear the TEST mode bit before disabling master as TEST mode
2441                 // can only be modified by the GPU TS under FPGA master mode.
2442                 //
2443                 rmStatus |= writeregu008_extdeviceTargeted(pGpu, (PDACEXTERNALDEVICE) pThis,
2444                                                         (NvU8)NV_P2060_CONTROL, ctrl);
2445                 osDelay(30); // Add delay of 30 ms as we are turning OFF TEST mode.
2446             }
2447         }
2448 
2449         // Gsync/FPGA card will be master or not based on bEnableMaster.
2450         ctrl = FLD_SET_DRF_NUM(_P2060, _CONTROL, _I_AM, (NvU8)bEnableMaster, ctrl);
2451         rmStatus |= writeregu008_extdeviceTargeted(pGpu, (PDACEXTERNALDEVICE) pThis,
2452                                                   (NvU8)NV_P2060_CONTROL, ctrl);
2453         if (NV_OK != rmStatus)
2454         {
2455             NV_PRINTF(LEVEL_ERROR,
2456                       "Failed to write I_AM_MSTR. Can not program Master.\n");
2457             return rmStatus;
2458         }
2459 
2460         //
2461         // Now we have updated Master status of Gsync board, wait for some time. This will allow FPGA to
2462         // reflect state-change in other registers e.g portStat, Frame Rate etc. Refer Bug 1053022.
2463         //
2464         osDelay(200);
2465 
2466         if (bEnableMaster)
2467         {
2468             // Remember the desired skipSwapBarrierWar setting for enable master calls.
2469             pThis->Iface[iface].skipSwapBarrierWar = skipSwapBarrierWar;
2470         }
2471         else
2472         {
2473             // Fetch the real skipSwapBarrierWar value from cache in case of an unsync call.
2474             skipSwapBarrierWar = pThis->Iface[iface].skipSwapBarrierWar;
2475             // And reset the cached value.
2476             pThis->Iface[iface].skipSwapBarrierWar = NV_FALSE;
2477         }
2478 
2479         //
2480         // Fpga revisions <= 5 need to sw the swapbarrier on the framelock master
2481         // to drive the swap_rdy signal. This can be overridden by skipSwapBarrierWar.
2482         //
2483         if ((!skipSwapBarrierWar) &&
2484             needsMasterBarrierWar(&pThis->ExternalDevice))
2485         {
2486             // enable/disable SwapRdy for GPU during enable/disable of Framelock Master.
2487             rmStatus = gsyncUpdateSwapRdyConnectionForGpu_P2060(pGpu, pThis, bEnableMaster);
2488             if (NV_OK != rmStatus)
2489             {
2490                 NV_PRINTF(LEVEL_ERROR,
2491                           "Failed to update SwapRdyEnable. Can not program Master.\n");
2492                 return rmStatus;
2493             }
2494         }
2495     }
2496 
2497     // now we're ready to let the software know about it.
2498     for ( head = 0; head < numHeads; head++ )
2499     {
2500         // is this head the desired master, or are we are disabling mastership?
2501         // If mastership is currently disabled, don't touch cache as this could destroy slave values.0
2502         if ((Master & DisplayIds[head]) || (!bEnableMaster && bQSyncAlreadyMaster))
2503         {
2504             pThis->Iface[iface].Sync.Master[head] = (bEnableMaster);
2505             pThis->Iface[iface].Sync.Slaved[head] = (bEnableMaster && bHouseSelect);
2506 
2507             gsyncProgramFramelockEnable_P2060(pGpu, pThis, iface, bEnableMaster);
2508         }
2509          else
2510         {
2511             // we are setting a master, but it's not on this head, so just to be safe:
2512             pThis->Iface[iface].Sync.Master[head] = 0;
2513         }
2514     }
2515 
2516     if (!bEnableMaster && !gsyncIsFrameLocked_P2060(pThis))
2517     {
2518         // Disable Framelock interrupts as board is not framelocked now.
2519         gsyncDisableFrameLockInterrupt_P2060((PDACEXTERNALDEVICE)pThis);
2520 
2521         rmStatus = gsyncUnApplyStereoPinAlwaysHiWar(pGpu);
2522 
2523         if (NV_OK != rmStatus)
2524         {
2525             NV_PRINTF(LEVEL_ERROR,
2526                       "Failed to drive stereo output pin for bug3362661.\n");
2527         }
2528     }
2529 
2530     if (!IsSLIEnabled(pGpu))
2531     {
2532         NvU32 otherGpuId;
2533         OBJGPU   *pOtherGpu;
2534         RM_API   *pRmApi;
2535         NvU32     hClient;
2536         NvU32     hSubdevice;
2537         NvU32 Slaves, drOut, drIn;
2538         NvU32 tempIface;
2539         NV2080_CTRL_INTERNAL_GSYNC_SET_OR_RESTORE_RASTER_SYNC_PARAMS ctrlParams = {0};
2540 
2541         for (tempIface = 0; tempIface < NV_P2060_MAX_IFACES_PER_GSYNC; tempIface++)
2542         {
2543             if (tempIface == iface)
2544             {
2545                 continue;
2546             }
2547 
2548             otherGpuId = pThis->Iface[tempIface].GpuInfo.gpuId;
2549             if (otherGpuId == NV0000_CTRL_GPU_INVALID_ID)
2550             {
2551                 continue;
2552             }
2553 
2554             pOtherGpu = gpumgrGetGpuFromId(otherGpuId);
2555             NV_ASSERT(pOtherGpu);
2556 
2557             if (gpumgrGetGpuLockAndDrPorts(pGpu, pOtherGpu, &drOut, &drIn) != NV_OK)
2558             {
2559                 continue;
2560             }
2561             //
2562             // If this is the master gpu, we need to disable the raster sync
2563             // gpio on the other P2060 GPU that's connected to master over
2564             // Video bridge. Otherwise, if the video bridge is connected,
2565             // the raster sync signals from the two cards will interfere,
2566             // giving us an unreliable sync signal.
2567             //
2568             pRmApi      = GPU_GET_PHYSICAL_RMAPI(pOtherGpu);
2569             hClient     = pOtherGpu->hInternalClient;
2570             hSubdevice  = pOtherGpu->hInternalSubdevice;
2571 
2572             ctrlParams.bEnableMaster = bEnableMaster;
2573             ctrlParams.bRasterSyncGpioSaved = pThis->Iface[tempIface].RasterSyncGpio.saved;
2574             ctrlParams.bRasterSyncGpioDirection = pThis->Iface[tempIface].RasterSyncGpio.direction;
2575 
2576             rmStatus = pRmApi->Control(pRmApi, hClient, hSubdevice,
2577                                        NV2080_CTRL_CMD_INTERNAL_GSYNC_SET_OR_RESTORE_RASTER_SYNC,
2578                                        &ctrlParams, sizeof(ctrlParams));
2579 
2580             if (rmStatus != NV_OK)
2581             {
2582                 NV_PRINTF(LEVEL_ERROR, "Extdev control call to save/restore GPIO direction is failed!\n");
2583             }
2584             else
2585             {
2586                 if (bEnableMaster && !pThis->Iface[tempIface].RasterSyncGpio.saved)
2587                 {
2588                     pThis->Iface[tempIface].RasterSyncGpio.direction = ctrlParams.bRasterSyncGpioDirection;
2589                     pThis->Iface[tempIface].RasterSyncGpio.saved = NV_TRUE;
2590                 }
2591                 else if (!bEnableMaster && pThis->Iface[tempIface].RasterSyncGpio.saved)
2592                 {
2593                      pThis->Iface[tempIface].RasterSyncGpio.saved = NV_FALSE;
2594                  }
2595             }
2596 
2597             Slaves = gsyncReadSlaves_P2060(pOtherGpu, pThis);
2598             if (Slaves)
2599             {
2600                 rmStatus = gsyncProgramSlaves_P2060(pOtherGpu, pThis, Slaves);
2601                 if (NV_OK != rmStatus)
2602                 {
2603                     NV_PRINTF(LEVEL_ERROR,
2604                               "Failed to program SLI slaves. Can not program Master.\n");
2605                     return rmStatus;
2606                 }
2607             }
2608         }
2609     }
2610 
2611     // Reset the frame count data and also disable frame compare match interrupt.
2612     if (!bEnableMaster)
2613     {
2614         iface = pThis->FrameCountData.iface;
2615         head  = pThis->FrameCountData.head;
2616 
2617         if ((iface < NV_P2060_MAX_IFACES_PER_GSYNC) && (head  < numHeads))
2618         {
2619             rmStatus |= gsyncResetFrameCountData_P2060(pGpu, pThis);
2620         }
2621     }
2622     return rmStatus;
2623 }
2624 
2625 /*
2626  * Read Framelock Master P2060.
2627  */
2628 static NvU32
2629 gsyncReadMaster_P2060
2630 (
2631     OBJGPU *pGpu,
2632     PDACP2060EXTERNALDEVICE pThis
2633 )
2634 {
2635     KernelDisplay  *pKernelDisplay = GPU_GET_KERNEL_DISPLAY(pGpu);
2636     NvU32       DisplayIds[OBJ_MAX_HEADS];
2637     NvU32       iface, head;
2638     NvU32       Master = 0;
2639     NV_STATUS   status;
2640     NvU32 numHeads = kdispGetNumHeads(pKernelDisplay);
2641 
2642     extdevGetBoundHeadsAndDisplayIds(pGpu, DisplayIds);
2643 
2644     status = GetP2060GpuLocation(pGpu, pThis, &iface);
2645     if (NV_OK != status)
2646         return 0;
2647 
2648     for ( head = 0; head < numHeads; head++ )
2649     {
2650         if (pThis->Iface[iface].Sync.Master[head])
2651         {
2652             Master |= DisplayIds[head];
2653         }
2654     }
2655 
2656     // check hardware's opinion on the Master Gsync and Timing source GPU.
2657     if (gsyncIsP2060MasterBoard(pGpu, pThis) &&
2658         GpuIsP2060Master(pGpu, pThis))
2659     {
2660         if (Master)
2661         {
2662             return Master;
2663         }
2664         else
2665         {
2666             // HW is set to be master, but SW isn't treating it as master?
2667             // we must be on external sync, so no one display is associated...
2668             return ~0;
2669         }
2670     }
2671     else
2672     {
2673         // if HW says it's not the master, SW's opinion doesn't count.
2674         return 0;
2675     }
2676 }
2677 
2678 /*
2679  * Program P2060 Slave for framelock.
2680  */
2681 static NV_STATUS
2682 gsyncProgramSlaves_P2060
2683 (
2684     OBJGPU *pGpu,
2685     PDACP2060EXTERNALDEVICE pThis,
2686     NvU32 Slaves
2687 )
2688 {
2689     KernelDisplay  *pKernelDisplay = GPU_GET_KERNEL_DISPLAY(pGpu);
2690     NvU32       DisplayIds[OBJ_MAX_HEADS];
2691     NvU32       iface, head, index;
2692     NvU8        ctrl = 0, ctrl3 = 0;
2693     NvBool      bCoupled, bHouseSelect, bLocalMaster, bEnableSlaves = (0 != Slaves);
2694     NV_STATUS   rmStatus = NV_OK;
2695     NvU32 numHeads = kdispGetNumHeads(pKernelDisplay);
2696 
2697     // This utility fn returns display id's associated with each head.
2698     extdevGetBoundHeadsAndDisplayIds(pGpu, DisplayIds);
2699 
2700     // which gpu's are we talking to?
2701     rmStatus = GetP2060GpuLocation(pGpu, pThis, &iface);
2702     if (NV_OK != rmStatus)
2703     {
2704         NV_PRINTF(LEVEL_ERROR,
2705                   "Failed to get Gpu location. Can not program Slave.\n");
2706         return rmStatus;
2707     }
2708 
2709     rmStatus = readregu008_extdeviceTargeted(pGpu, (PDACEXTERNALDEVICE)pThis,
2710                                             (NvU8)NV_P2060_CONTROL, &ctrl);
2711     if (NV_OK != rmStatus)
2712     {
2713         NV_PRINTF(LEVEL_ERROR,
2714                   "Failed to read ctrl register. Can not program slave.\n");
2715         return rmStatus; // ouch
2716     }
2717 
2718     // Check for House sync select as sync source and FPGA board is master or not
2719     bHouseSelect = FLD_TEST_DRF(_P2060, _CONTROL, _SYNC_SELECT, _HOUSE,  (NvU32)ctrl);
2720     bLocalMaster = FLD_TEST_DRF(_P2060, _CONTROL, _I_AM,        _MASTER, (NvU32)ctrl);
2721 
2722     if (bEnableSlaves || bLocalMaster)
2723     {
2724         rmStatus = gsyncApplyStereoPinAlwaysHiWar(pGpu, (PDACEXTERNALDEVICE)pThis);
2725 
2726         if (NV_OK != rmStatus)
2727         {
2728             NV_PRINTF(LEVEL_ERROR,
2729                       "Failed to drive stereo output pin for bug3362661.\n");
2730         }
2731     }
2732 
2733     if (bHouseSelect && bEnableSlaves && bLocalMaster)
2734     {
2735         rmStatus = readregu008_extdeviceTargeted(pGpu, (PDACEXTERNALDEVICE)pThis,
2736                                                 (NvU8)NV_P2060_CONTROL3, &ctrl3);
2737         if (rmStatus != NV_OK)
2738         {
2739             NV_PRINTF(LEVEL_ERROR,
2740                       "Failed to read ctrl3 register. Can not program slave.\n");
2741             return rmStatus;
2742         }
2743         ctrl3 |= (NvU8) FLD_SET_DRF(_P2060, _CONTROL3, _RESYNC, _ON, (NvU32)ctrl3);
2744         writeregu008_extdeviceTargeted(pGpu, (PDACEXTERNALDEVICE)pThis,
2745                                       (NvU8)NV_P2060_CONTROL3, ctrl3);
2746     }
2747 
2748     //
2749     // No need to check for every gpu connected to this gsync board. If gsync board
2750     // is not master/server, none of the gpu connected to it will have master head.
2751     //
2752 
2753     if (bEnableSlaves && !bLocalMaster)
2754     {
2755        //
2756        // Sync source should be taken from GPU connected to Gsync board.
2757        // Get index of current GPU and make it sync source.
2758        // No idea is this safe or not. Adding TODO for future check.
2759        //
2760        rmStatus = GetP2060ConnectorIndexFromGpu(pGpu, pThis, &index);
2761        if (NV_OK != rmStatus)
2762        {
2763            NV_PRINTF(LEVEL_ERROR,
2764                      "Failed to get connector index for Gpu. Can not program slave.\n");
2765            return rmStatus;
2766        }
2767 
2768        ctrl = FLD_SET_DRF_NUM(_P2060, _CONTROL, _SYNC_SRC, (NvU8)index, ctrl);
2769 
2770        rmStatus = writeregu008_extdeviceTargeted(pGpu, (PDACEXTERNALDEVICE) pThis,
2771                                                 (NvU8)NV_P2060_CONTROL, ctrl);
2772        if (NV_OK != rmStatus)
2773        {
2774            NV_PRINTF(LEVEL_ERROR,
2775                      "Failed to write SYNC_SRC. Can not program slave.\n");
2776            return rmStatus;
2777        }
2778     }
2779 
2780     //
2781     // With House sync enabled the crashlocking still need some investigations.
2782     // So filter out Housesyced systems before doing local crashlocks.
2783     //
2784     if ((!bHouseSelect) && bEnableSlaves && bLocalMaster)
2785     {
2786         rmStatus = readregu008_extdeviceTargeted(pGpu, (PDACEXTERNALDEVICE)pThis,
2787                                                 (NvU8)NV_P2060_CONTROL3, &ctrl3);
2788         if (rmStatus != NV_OK)
2789         {
2790             NV_PRINTF(LEVEL_ERROR,
2791                       "Failed to read ctrl3 register. Can not program slave.\n");
2792             return rmStatus;
2793         }
2794         ctrl3 |= (NvU8) FLD_SET_DRF(_P2060, _CONTROL3, _RESYNC, _ON, (NvU32)ctrl3);
2795         writeregu008_extdeviceTargeted(pGpu, (PDACEXTERNALDEVICE)pThis,
2796                                        (NvU8)NV_P2060_CONTROL3, ctrl3);
2797     }
2798 
2799     //
2800     // If we on the same gpu as the framelock master, the P2060 will
2801     // not servo, so we must rely on the gpu to get our timing...
2802     // unless house sync is expected, in which case we get it back.
2803     //
2804     bCoupled = (!bLocalMaster) || (bHouseSelect);
2805 
2806     // disable all existing slaves before enabling new slaves.
2807     if (bEnableSlaves)
2808     {
2809         for ( head = 0; head < numHeads; head++ )
2810         {
2811             pThis->Iface[iface].Sync.Slaved[head] =     0;
2812             pThis->Iface[iface].Sync.LocalSlave[head] = 0;
2813         }
2814     }
2815 
2816     for ( head = 0; head < numHeads; head++ )
2817     {
2818         // is this head to be slaved, or are we freeing all slaves?
2819         if ((Slaves & DisplayIds[head]) || !bEnableSlaves)
2820         {
2821             pThis->Iface[iface].Sync.Slaved[head] =     (bEnableSlaves &&  bCoupled);
2822             pThis->Iface[iface].Sync.LocalSlave[head] = (bEnableSlaves && !bCoupled);
2823 
2824             gsyncProgramFramelockEnable_P2060(pGpu, pThis, iface, bEnableSlaves);
2825         }
2826         else
2827         {
2828             // we are setting a slave, but it's not on this head, so nothing needs doing.
2829         }
2830     }
2831 
2832     if (!bEnableSlaves && !gsyncIsFrameLocked_P2060(pThis))
2833     {
2834         // Disable Framelock interrupts as board is not framelocked now.
2835         gsyncDisableFrameLockInterrupt_P2060((PDACEXTERNALDEVICE)pThis);
2836 
2837         rmStatus = gsyncUnApplyStereoPinAlwaysHiWar(pGpu);
2838 
2839         if (NV_OK != rmStatus)
2840         {
2841             NV_PRINTF(LEVEL_ERROR,
2842                       "Failed to drive stereo output pin for bug3362661.\n");
2843         }
2844     }
2845 
2846     // Reset FrameCountData and disable frame compare match interrupt.
2847     if (iface == pThis->FrameCountData.iface)
2848     {
2849         if (!(Slaves & DisplayIds[pThis->FrameCountData.head]))
2850         {
2851             if (pThis->Iface[iface].GpuInfo.gpuId != NV0000_CTRL_GPU_INVALID_ID)
2852             {
2853                 rmStatus |= gsyncResetFrameCountData_P2060(pGpu, pThis);
2854             }
2855         }
2856     }
2857     return rmStatus;
2858 }
2859 
2860 /*
2861  * Read P2060 slave for framelock.
2862  */
2863 static NvU32
2864 gsyncReadSlaves_P2060
2865 (
2866     OBJGPU *pGpu,
2867     PDACP2060EXTERNALDEVICE pThis
2868 )
2869 {
2870     KernelDisplay  *pKernelDisplay = GPU_GET_KERNEL_DISPLAY(pGpu);
2871     NvU32       DisplayIds[OBJ_MAX_HEADS];
2872     NvU32       iface, head;
2873     NvU32       Slaves = 0;
2874     NV_STATUS   status;
2875     NvU32 numHeads = kdispGetNumHeads(pKernelDisplay);
2876 
2877     extdevGetBoundHeadsAndDisplayIds(pGpu, DisplayIds);
2878 
2879     status = GetP2060GpuLocation(pGpu, pThis, &iface);
2880     if (NV_OK != status)
2881         return 0;
2882 
2883     for ( head = 0; head < numHeads; head++ )
2884     {
2885         if (!pThis->Iface[iface].Sync.Master[head] &&
2886             (pThis->Iface[iface].Sync.Slaved[head] || pThis->Iface[iface].Sync.LocalSlave[head]))
2887         {
2888             Slaves |= DisplayIds[head];
2889         }
2890     }
2891     return Slaves;
2892 }
2893 
2894 static NV_STATUS
2895 gsyncProgramSwapBarrier_P2060
2896 (
2897     OBJGPU              *pGpu,
2898     PDACEXTERNALDEVICE   pExtDev,
2899     NvBool               bEnable
2900 )
2901 {
2902     PDACP2060EXTERNALDEVICE pThis = (PDACP2060EXTERNALDEVICE) pExtDev;
2903     NvU32          iface;
2904     NvU8           ctrl2 = 0;
2905     NV_STATUS      status = NV_OK;
2906 
2907     status = GetP2060GpuLocation(pGpu, pThis, &iface);
2908     if (NV_OK != status)
2909     {
2910        if (!IsSLIEnabled(pGpu))
2911        {
2912            //
2913            // If not in SLI, this function must only be called on GPUs connected
2914            // to the framelock board.
2915            //
2916            return status;
2917        }
2918        else
2919        {
2920            //
2921            // In SLI, we will try to add all GPUs in a topology to a swap
2922            // barrier, but not all of the GPUs actually have to be connected to
2923            // the framelock board, so we can return NV_OK to let the caller continue
2924            // on to the next GPU.
2925            //
2926            NV_PRINTF(LEVEL_INFO,
2927                      "Ignoring GPU %u not connected to the framelock board.\n",
2928                      gpumgrGetSubDeviceInstanceFromGpu(pGpu));
2929            return NV_OK;
2930        }
2931     }
2932 
2933     // Each connected GPU accesses it's own version of CONTROL2, on P2060
2934     if (GpuIsP2060Connected(pGpu, pThis))
2935     {
2936         status = readregu008_extdeviceTargeted(pGpu, pExtDev,
2937                                      NV_P2060_CONTROL2, &ctrl2);
2938         if (status != NV_OK)
2939         {
2940             return status;
2941         }
2942 
2943         if (pExtDev->deviceRev >= DAC_EXTERNAL_DEVICE_REV_MAX)
2944         {
2945             return NV_ERR_INVALID_STATE;
2946         }
2947     }
2948 
2949     if (pThis->Iface[iface].SwapReadyRequested == bEnable)
2950     {
2951        //
2952        // The SwapReadyRequested boolean keeps tracks of the current
2953        // requested state for this GPU. Skip the WAR algorithm if it's
2954        // already taken this GPU's state into account.
2955        //
2956        return NV_OK;
2957     }
2958 
2959     if (bEnable)  // Enable Swap_rdy
2960     {
2961         ctrl2 = FLD_SET_DRF(_P2060, _CONTROL2, _SWAP_READY, _ENABLE, ctrl2);
2962 
2963         if (pThis->tSwapRdyHiLsrMinTime == 0)
2964         {
2965             NvU32 data = 0;
2966 
2967             // store Swap Lockout Window in pThis.
2968             pThis->tSwapRdyHiLsrMinTime = NV_REG_STR_TIME_SWAP_RDY_HI_MODIFY_LSR_MIN_TIME_DEFAULT;
2969             if (osReadRegistryDword(pGpu,
2970              NV_REG_STR_TIME_SWAP_RDY_HI_MODIFY_LSR_MIN_TIME, &data) == NV_OK)
2971             {
2972                 pThis->tSwapRdyHiLsrMinTime = data;
2973             }
2974         }
2975 
2976         NV_ASSERT(pThis->tSwapRdyHiLsrMinTime != 0);
2977 
2978         if (pThis->Iface[iface].DsiFliplock.saved == NV_FALSE)
2979         {
2980             KernelDisplay *pKernelDisplay = GPU_GET_KERNEL_DISPLAY(pGpu);
2981             NvU32          numHeads = kdispGetNumHeads(pKernelDisplay);
2982             NvU32          head ;
2983 
2984             for (head = 0; head < numHeads; head++)
2985             {
2986                 NvU32 newLsrMinTime;
2987                 if ((status = kdispComputeLsrMinTimeValue_HAL(pGpu, pKernelDisplay, head,
2988                              pThis->tSwapRdyHiLsrMinTime, &newLsrMinTime)) == NV_OK)
2989                 {
2990                     NvU32 origLsrMinTime;
2991                     kdispSetSwapBarrierLsrMinTime_HAL(pGpu, pKernelDisplay, head, &origLsrMinTime,
2992                                       newLsrMinTime);
2993 
2994                     pThis->Iface[iface].DsiFliplock.OrigLsrMinTime[head] = origLsrMinTime;
2995                 }
2996                 else
2997                 {
2998                     NV_PRINTF(LEVEL_ERROR,
2999                               "Error occured while computing LSR_MIN_TIME for Swap Barrier\n");
3000                     NV_ASSERT(0);
3001                 }
3002             }
3003             pThis->Iface[iface].DsiFliplock.saved = NV_TRUE;
3004         }
3005 
3006         // The swapbarrier on master war is assumed to be fixed with fpga rev > 5.
3007         if ((!pThis->Iface[iface].skipSwapBarrierWar) &&
3008              needsMasterBarrierWar(&pThis->ExternalDevice))
3009         {
3010             if (gsyncIsP2060MasterBoard(pGpu, pThis) && GpuIsP2060Master(pGpu, pThis))
3011             {
3012                 //
3013                 // Swap Rdy signal on Master + TS GPU will enabled or disabled during
3014                 // Enable/Disable of Master i.e. gsyncProgramMaster_P2060
3015                 //
3016                 pThis->Iface[iface].SwapReadyRequested = bEnable;
3017                 return NV_OK;
3018             }
3019         }
3020 
3021         if (GpuIsConnectedToMasterViaBridge(pGpu, pThis) &&
3022            (gpumgrGetGpuBridgeType() == SLI_BT_VIDLINK || isBoardWithNvlinkQsyncContention(pGpu)))
3023         {
3024             //
3025             // Do not enable swapRdy Connection of pGpu. pGpu will take swap Rdy signal
3026             // from Master + TS GPU via SLI (MIO) bridge
3027             //
3028             pThis->Iface[iface].SwapReadyRequested = bEnable;
3029             return status; //NV_OK
3030         }
3031     }
3032     else
3033     {
3034         ctrl2 = FLD_SET_DRF(_P2060, _CONTROL2, _SWAP_READY, _DISABLE, ctrl2); // Disable Swap_rdy
3035 
3036         if (pThis->Iface[iface].DsiFliplock.saved == NV_TRUE)
3037         {
3038             KernelDisplay *pKernelDisplay = GPU_GET_KERNEL_DISPLAY(pGpu);
3039             NvU32          numHeads = kdispGetNumHeads(pKernelDisplay);
3040             NvU32          head ;
3041 
3042             for (head = 0; head < numHeads; head++)
3043             {
3044                 kdispRestoreOriginalLsrMinTime_HAL(pGpu, pKernelDisplay, head,
3045                 pThis->Iface[iface].DsiFliplock.OrigLsrMinTime[head]);
3046             }
3047             pThis->Iface[iface].DsiFliplock.saved = NV_FALSE;
3048         }
3049 
3050         // The swapbarrier on master war is assumed to be fixed with fpga rev > 5.
3051         if ((!pThis->Iface[iface].skipSwapBarrierWar) &&
3052              needsMasterBarrierWar(&pThis->ExternalDevice))
3053         {
3054             if (gsyncIsP2060MasterBoard(pGpu, pThis) && GpuIsP2060Master(pGpu, pThis))
3055             {
3056                 //
3057                 // Swap Rdy signal on Master + TS GPU will enabled or disabled during
3058                 // Enable/Disable of Master i.e. gsyncProgramMaster_P2060
3059                 //
3060                 pThis->Iface[iface].SwapReadyRequested = bEnable;
3061                 return NV_OK;
3062             }
3063         }
3064     }
3065 
3066     // Save the requested state for this GPU
3067     pThis->Iface[iface].SwapReadyRequested = bEnable;
3068 
3069     // Each connected GPU accesses it's own version of CONTROL2, on P2060
3070     if (GpuIsP2060Connected(pGpu, pThis))
3071     {
3072         status = writeregu008_extdeviceTargeted(pGpu, pExtDev,
3073                                                 NV_P2060_CONTROL2, ctrl2);
3074     }
3075 
3076     return status;
3077 }
3078 
3079 
3080 static NV_STATUS
3081 gsyncReadSwapBarrier_P2060
3082 (
3083     OBJGPU             *pGpu,
3084     PDACEXTERNALDEVICE  pExtDev,
3085     NvBool             *bEnable
3086 )
3087 {
3088     PDACP2060EXTERNALDEVICE pThis = (PDACP2060EXTERNALDEVICE) pExtDev;
3089     NvU32 iface;
3090     NV_STATUS status = NV_OK;
3091 
3092     status = GetP2060GpuLocation(pGpu, pThis, &iface);
3093     if (NV_OK != status)
3094     {
3095        if (!IsSLIEnabled(pGpu))
3096        {
3097            //
3098            // If not in SLI, this function must only be called on GPUs connected
3099            // to the framelock board.
3100            //
3101            return status;
3102        }
3103        else
3104        {
3105            //
3106            // In SLI, we will try to read the swap barrier info from all GPUs in a
3107            // topology, but not all of the GPUs actually have to be connected to
3108            // the framelock board, so we can return NV_OK to let the caller continue
3109            // on to the next GPU.
3110            //
3111            NV_PRINTF(LEVEL_INFO,
3112                      "Ignoring GPU %u not connected to the framelock board.\n",
3113                      gpumgrGetSubDeviceInstanceFromGpu(pGpu));
3114            return NV_OK;
3115        }
3116     }
3117 
3118     if (gsyncIsP2060MasterBoard(pGpu, pThis) && GpuIsP2060Master(pGpu, pThis))
3119     {
3120         // Read swapRdy of Master + TS GPU.
3121         *bEnable = pThis->Iface[iface].SwapReadyRequested;
3122         return status; //NV_OK
3123     }
3124 
3125     if (GpuIsConnectedToMasterViaBridge(pGpu, pThis) &&
3126        (gpumgrGetGpuBridgeType() == SLI_BT_VIDLINK || isBoardWithNvlinkQsyncContention(pGpu)))
3127     {
3128         // Read swapRdy of pGpu connected to Master + TS GPU via SLI (MIO) bridge.
3129         *bEnable = pThis->Iface[iface].SwapReadyRequested;
3130         return status; //NV_OK
3131     }
3132 
3133     // Each connected GPU accesses it's own version of CONTROL2, on P2060
3134     if (GpuIsP2060Connected(pGpu, pThis))
3135     {
3136         NvU8 ctrl2 = 0;
3137         status = readregu008_extdeviceTargeted(pGpu,
3138                        pExtDev, NV_P2060_CONTROL2, &ctrl2);
3139         if (status != NV_OK)
3140         {
3141             return status;
3142         }
3143         *bEnable = (NV_P2060_CONTROL2_SWAP_READY_ENABLE ==
3144                        DRF_VAL(_P2060, _CONTROL2, _SWAP_READY, (NvU32)ctrl2));
3145     }
3146 
3147     return status;
3148 }
3149 
3150 static NV_STATUS
3151 gsyncSetLsrMinTime
3152 (
3153     OBJGPU                                   *pSourceGpu,
3154     PDACEXTERNALDEVICE                        pExtDev,
3155     NvU32                                     enable
3156 )
3157 {
3158     PDACP2060EXTERNALDEVICE pThis    = (PDACP2060EXTERNALDEVICE)pExtDev;
3159     NV_STATUS               rmStatus = NV_OK;
3160     NvU32                   index;
3161 
3162     // Get Mosaic Timing Source GPU Connector Index.
3163     if (NV_OK != GetP2060ConnectorIndexFromGpu(pSourceGpu, pThis, &index))
3164     {
3165         return NV_ERR_GENERIC;
3166     }
3167 
3168     if (enable)
3169     {
3170         // Set LSR_MIN_TIME
3171         if (pThis->tSwapRdyHiLsrMinTime == 0)
3172         {
3173             NvU32 data = 0;
3174 
3175             // store Swap Lockout Window in pThis.
3176             pThis->tSwapRdyHiLsrMinTime = NV_REG_STR_TIME_SWAP_RDY_HI_MODIFY_LSR_MIN_TIME_DEFAULT;
3177 
3178             if (osReadRegistryDword(pSourceGpu,
3179                     NV_REG_STR_TIME_SWAP_RDY_HI_MODIFY_LSR_MIN_TIME, &data) == NV_OK)
3180             {
3181                 pThis->tSwapRdyHiLsrMinTime = data;
3182             }
3183         }
3184 
3185         if (pThis->Iface[index].DsiFliplock.saved == NV_FALSE)
3186         {
3187             KernelDisplay  *pKernelDisplay = GPU_GET_KERNEL_DISPLAY(pSourceGpu);
3188             NvU32           numHeads = kdispGetNumHeads(pKernelDisplay);
3189             NvU32           head;
3190 
3191             // Do we need to loop over numHeads?
3192             for (head = 0; head < numHeads; head++)
3193             {
3194                 NvU32 newLsrMinTime;
3195 
3196                 if ((rmStatus = kdispComputeLsrMinTimeValue_HAL(pSourceGpu, pKernelDisplay, head,
3197                                     pThis->tSwapRdyHiLsrMinTime, &newLsrMinTime)) == NV_OK)
3198                 {
3199                     NvU32 origLsrMinTime;
3200 
3201                     kdispSetSwapBarrierLsrMinTime_HAL(pSourceGpu, pKernelDisplay, head, &origLsrMinTime,
3202                         newLsrMinTime);
3203 
3204                     pThis->Iface[index].DsiFliplock.OrigLsrMinTime[head] = origLsrMinTime;
3205                 }
3206                 else
3207                 {
3208                     NV_PRINTF(LEVEL_ERROR,
3209                               "Error occured while computing LSR_MIN_TIME for Swap Barrier\n");
3210                     NV_ASSERT(0);
3211                 }
3212             }
3213 
3214             pThis->Iface[index].DsiFliplock.saved = NV_TRUE;
3215         }
3216     }
3217     else
3218     {
3219         if (pThis->Iface[index].DsiFliplock.saved == NV_TRUE)
3220         {
3221             KernelDisplay  *pKernelDisplay = GPU_GET_KERNEL_DISPLAY(pSourceGpu);
3222             NvU32           numHeads = kdispGetNumHeads(pKernelDisplay);
3223             NvU32           head;
3224 
3225             for (head = 0; head < numHeads; head++)
3226             {
3227                 kdispRestoreOriginalLsrMinTime_HAL(pSourceGpu, pKernelDisplay, head,
3228                     pThis->Iface[index].DsiFliplock.OrigLsrMinTime[head]);
3229             }
3230 
3231             pThis->Iface[index].DsiFliplock.saved = NV_FALSE;
3232         }
3233     }
3234 
3235     return rmStatus;
3236 }
3237 
3238 NV_STATUS
3239 gsyncSetMosaic_P2060
3240 (
3241     OBJGPU                                   *pSourceGpu,
3242     PDACEXTERNALDEVICE                        pExtDev,
3243     NV30F1_CTRL_GSYNC_SET_LOCAL_SYNC_PARAMS *pParams
3244 )
3245 {
3246     PDACP2060EXTERNALDEVICE pThis = (PDACP2060EXTERNALDEVICE)pExtDev;
3247     NV_STATUS rmStatus = NV_OK;
3248     OBJGPU *pTempGpu = NULL;
3249     NvU8 mosaicReg, i;
3250     NvU32 mosaicGroup = pParams->mosaicGroupNumber;
3251 
3252     if (mosaicGroup >= NV_P2060_MAX_MOSAIC_GROUPS)
3253     {
3254         NV_PRINTF(LEVEL_ERROR,
3255                   "mosaicGroup equaling/extending NV_P2060_MAX_MOSAIC_GROUPS.\n");
3256         return NV_ERR_INVALID_ARGUMENT;
3257     }
3258 
3259     if (pParams->slaveGpuCount > NV_P2060_MAX_MOSAIC_SLAVES)
3260     {
3261         NV_PRINTF(LEVEL_ERROR,
3262                   "mosaic slaveGpuCount extending NV_P2060_MAX_MOSAIC_SLAVES.\n");
3263         return NV_ERR_INVALID_ARGUMENT;
3264     }
3265 
3266     if (pParams->enableMosaic) {
3267 
3268         NvU32 index;
3269         if (pThis->MosaicGroup[mosaicGroup].enabledMosaic)
3270         {
3271             // mosaic is already enabled for this group,
3272             // client should disable it first and re-query for this group.
3273             NV_PRINTF(LEVEL_ERROR,
3274                       "trying to enable mosaicGroup which is already enabled.\n");
3275             return NV_ERR_INVALID_ARGUMENT;
3276         }
3277 
3278         // Get Mosaic Timing Source GPU Connector Index.
3279         if ( NV_OK != GetP2060ConnectorIndexFromGpu(pSourceGpu, pThis, &index))
3280         {
3281              return NV_ERR_GENERIC;
3282         }
3283 
3284         pThis->MosaicGroup[mosaicGroup].slaveGpuCount   = pParams->slaveGpuCount;
3285         pThis->MosaicGroup[mosaicGroup].gpuTimingSource = pThis->Iface[index].GpuInfo.gpuId;
3286 
3287         for (i = 0; i < pThis->MosaicGroup[mosaicGroup].slaveGpuCount; i++)
3288         {
3289            pThis->MosaicGroup[mosaicGroup].gpuTimingSlaves[i] = pParams->gpuTimingSlaves[i];
3290 
3291            pTempGpu = gpumgrGetGpuFromId(pParams->gpuTimingSlaves[i]);
3292            NV_ASSERT_OR_RETURN(pTempGpu, NV_ERR_GENERIC);
3293 
3294            // Update register of mosaic timing slaves first
3295            mosaicReg = 0;
3296            mosaicReg = FLD_SET_DRF_NUM(_P2060, _MOSAIC_MODE, _TS,    (NvU8)index, mosaicReg);
3297            mosaicReg = FLD_SET_DRF_NUM(_P2060, _MOSAIC_MODE, _GROUP, (NvU8)mosaicGroup, mosaicReg);
3298            mosaicReg = FLD_SET_DRF(_P2060, _MOSAIC_MODE, _ENABLE, _TRUE, mosaicReg);
3299 
3300            rmStatus |= writeregu008_extdeviceTargeted(pTempGpu,
3301                           (PDACEXTERNALDEVICE)pThis, NV_P2060_MOSAIC_MODE, mosaicReg);
3302            if (rmStatus != NV_OK)
3303            {
3304                NV_PRINTF(LEVEL_ERROR,
3305                          "Failed to write P2060 mosaic slave register.\n");
3306                return NV_ERR_GENERIC;
3307            }
3308 
3309            // Set LSR_MIN_TIME
3310            gsyncSetLsrMinTime(pTempGpu, pExtDev, pParams->enableMosaic);
3311         }
3312 
3313         gsyncSetLsrMinTime(pSourceGpu, pExtDev, pParams->enableMosaic);
3314 
3315         // Update registers of mosaic timing source.
3316         mosaicReg = 0;
3317         mosaicReg = FLD_SET_DRF_NUM(_P2060, _MOSAIC_MODE, _TS,    (NvU8)index, mosaicReg);
3318         mosaicReg = FLD_SET_DRF_NUM(_P2060, _MOSAIC_MODE, _GROUP, (NvU8)mosaicGroup, mosaicReg);
3319         mosaicReg = FLD_SET_DRF(_P2060, _MOSAIC_MODE, _ENABLE, _TRUE, mosaicReg);
3320 
3321         rmStatus |= writeregu008_extdeviceTargeted(pSourceGpu,
3322                           (PDACEXTERNALDEVICE)pThis, NV_P2060_MOSAIC_MODE, mosaicReg);
3323         if (rmStatus != NV_OK)
3324         {
3325             NV_PRINTF(LEVEL_ERROR,
3326                       "Failed to write P2060 mosaic Source register.\n");
3327             return NV_ERR_GENERIC;
3328         }
3329 
3330         // Mark as Mosaic enabled for specified group
3331         pThis->MosaicGroup[mosaicGroup].enabledMosaic = NV_TRUE;
3332     }
3333     else
3334     {
3335         if (!pThis->MosaicGroup[mosaicGroup].enabledMosaic)
3336         {
3337             // mosaicgroup is not enabled, so can not disable
3338             NV_PRINTF(LEVEL_ERROR,
3339                       "trying to disable mosaicGroup which is not enabled.\n");
3340             return NV_ERR_INVALID_ARGUMENT;
3341         }
3342 
3343         for (i = 0; i < pThis->MosaicGroup[mosaicGroup].slaveGpuCount; i++)
3344         {
3345             pTempGpu = gpumgrGetGpuFromId(pThis->MosaicGroup[mosaicGroup].gpuTimingSlaves[i]);
3346             NV_ASSERT_OR_RETURN(pTempGpu, NV_ERR_GENERIC);
3347 
3348             rmStatus |= writeregu008_extdeviceTargeted(pTempGpu,
3349                           (PDACEXTERNALDEVICE)pThis, NV_P2060_MOSAIC_MODE, 0x00);
3350             if (rmStatus != NV_OK)
3351             {
3352                NV_PRINTF(LEVEL_ERROR,
3353                          "Failed to write P2060 mosaic slave register.\n");
3354                return NV_ERR_GENERIC;
3355             }
3356 
3357             // Reset LSR_MIN_TIME
3358             gsyncSetLsrMinTime(pTempGpu, pExtDev, pParams->enableMosaic);
3359         }
3360 
3361         gsyncSetLsrMinTime(pSourceGpu, pExtDev, pParams->enableMosaic);
3362 
3363         rmStatus |= writeregu008_extdeviceTargeted(pSourceGpu,
3364                           (PDACEXTERNALDEVICE)pThis, NV_P2060_MOSAIC_MODE, 0x00);
3365         if (rmStatus != NV_OK)
3366         {
3367              NV_PRINTF(LEVEL_ERROR,
3368                        "Failed to write P2060 mosaic Source register.\n");
3369              return NV_ERR_GENERIC;
3370         }
3371 
3372         // reset structure for specified group
3373         gsyncResetMosaicData_P2060(mosaicGroup, pThis);
3374     }
3375 
3376     return rmStatus;
3377 }
3378 
3379 #ifdef DEBUG
3380 /*
3381  * Helper function to printout the most relevant P2060_status
3382  * register informations in a human readable form.
3383  */
3384 static void
3385 DbgPrintP2060StatusRegister(NvU32 DebugLevel, NvU32 regStatus)
3386 {
3387     if (DRF_VAL(_P2060, _STATUS, _SYNC_LOSS, regStatus))
3388         NV_PRINTF_EX(NV_PRINTF_MODULE, DebugLevel, "SYNC_LOSS ");
3389     if (DRF_VAL(_P2060, _STATUS, _STEREO, regStatus))
3390         NV_PRINTF_EX(NV_PRINTF_MODULE, DebugLevel, "STEREO_LOCK ");
3391     if (NV_P2060_STATUS_VCXO_LOCK == DRF_VAL(_P2060, _STATUS, _VCXO, regStatus))
3392         NV_PRINTF_EX(NV_PRINTF_MODULE, DebugLevel, "VCXO_LOCK ");
3393     if (NV_P2060_STATUS_VCXO_NOLOCK_TOO_FAST == DRF_VAL(_P2060, _STATUS, _VCXO, regStatus))
3394         NV_PRINTF_EX(NV_PRINTF_MODULE, DebugLevel, "VCXO_NOLOCK_TOO_FAST ");
3395     if (NV_P2060_STATUS_VCXO_NOLOCK_TOO_SLOW == DRF_VAL(_P2060, _STATUS, _VCXO, regStatus))
3396         NV_PRINTF_EX(NV_PRINTF_MODULE, DebugLevel, "VCXO_NOLOCK_TOO_SLOW ");
3397     if (NV_P2060_STATUS_VCXO_NOT_SERVO == DRF_VAL(_P2060, _STATUS, _VCXO, regStatus))
3398         NV_PRINTF_EX(NV_PRINTF_MODULE, DebugLevel, "VCXO_NOT_SERVO ");
3399 }
3400 #else
3401 #define DbgPrintP2060StatusRegister(DebugLevel, regStatus)
3402 #endif
3403 
3404 static NV_STATUS
3405 gsyncGpuStereoHeadSync(OBJGPU *pGpu, NvU32 iface, PDACEXTERNALDEVICE pExtDev, NvU32 status1)
3406 {
3407     DACP2060EXTERNALDEVICE *pThis = (PDACP2060EXTERNALDEVICE)pExtDev;
3408     KernelDisplay *pKernelDisplay = GPU_GET_KERNEL_DISPLAY(pGpu);
3409     RM_API   *pRmApi      = GPU_GET_PHYSICAL_RMAPI(pGpu);
3410     NvU32     hClient     = pGpu->hInternalClient;
3411     NvU32     hSubdevice  = pGpu->hInternalSubdevice;
3412     NV_STATUS status      = NV_OK;
3413     NvU32     numHeads    = kdispGetNumHeads(pKernelDisplay);
3414     NvU32     headIdx;
3415     NV2080_CTRL_INTERNAL_GSYNC_SET_STREO_SYNC_PARAMS ctrlParams = {0};
3416 
3417     for (headIdx = 0; headIdx < numHeads; headIdx++)
3418     {
3419         ctrlParams.slave[headIdx]      = pThis->Iface[iface].Sync.Slaved[headIdx];
3420         ctrlParams.localSlave[headIdx] = pThis->Iface[iface].Sync.LocalSlave[headIdx];
3421         ctrlParams.master[headIdx]     = pThis->Iface[iface].Sync.Master[headIdx];
3422     }
3423 
3424     ctrlParams.regStatus = status1;
3425     status = pRmApi->Control(pRmApi, hClient, hSubdevice,
3426                              NV2080_CTRL_CMD_INTERNAL_GSYNC_SET_STREO_SYNC,
3427                              &ctrlParams, sizeof(ctrlParams));
3428 
3429     if (status != NV_OK)
3430     {
3431         NV_PRINTF(LEVEL_ERROR, "Stereo headsync failed\n");
3432     }
3433 
3434     return status;
3435 }
3436 
3437 /*
3438  * Update the status register snapshot and
3439  * send loss/gain events to client if any.
3440  */
3441 static NV_STATUS
3442 gsyncUpdateGsyncStatusSnapshot_P2060
3443 (
3444     OBJGPU *pGpu,
3445     PDACEXTERNALDEVICE pExtDev
3446 )
3447 {
3448     PDACP2060EXTERNALDEVICE pThis = (PDACP2060EXTERNALDEVICE)pExtDev;
3449     NvU32 iface;
3450     NvU8 regStatus = 0;
3451     NV_STATUS rmStatus = NV_OK;
3452     NvU32 ifaceEvents[NV_P2060_MAX_IFACES_PER_GSYNC];
3453 
3454     // Only read status variables if the board is framelocked at all.
3455     if (!gsyncIsFrameLocked_P2060(pThis))
3456     {
3457         return rmStatus;
3458     }
3459 
3460     for (iface = 0; iface < NV_P2060_MAX_IFACES_PER_GSYNC; iface++)
3461     {
3462         NvU32 diffStatus = 0x00;
3463         NvU32 oldStatus  = 0x00;
3464         NvU32 newStatus  = 0x00;
3465         ifaceEvents[iface] = 0x00;
3466 
3467         // get status update for each interface
3468         if (pThis->Iface[iface].GpuInfo.gpuId != NV0000_CTRL_GPU_INVALID_ID)
3469         {
3470             int updateSnapshot = 1;
3471             int localMaster = 0;
3472 
3473             // Take care of tracking state of connected gpu, not per incoming.
3474             OBJGPU *pIfaceGpu = gpumgrGetGpuFromId(pThis->Iface[iface].GpuInfo.gpuId);
3475 
3476             NV_ASSERT(pIfaceGpu);
3477 
3478             rmStatus  = readregu008_extdeviceTargeted(pIfaceGpu, pExtDev, (NvU8)NV_P2060_STATUS, &regStatus);
3479             if ( NV_OK != rmStatus )
3480             {
3481                 return NV_ERR_GENERIC;
3482             }
3483 
3484             oldStatus = pThis->Snapshot[iface].Status1;
3485             newStatus = (NvU32)regStatus;
3486 
3487             // Check for a local master which generates the sync so it's always synched.
3488             if ( (NV_P2060_STATUS_VCXO_NOT_SERVO  == DRF_VAL(_P2060, _STATUS, _VCXO,      newStatus)) &&
3489                  (NV_P2060_STATUS_SYNC_LOSS_FALSE == DRF_VAL(_P2060, _STATUS, _SYNC_LOSS, newStatus)))
3490             {
3491                 localMaster = 1;
3492                 if (!pThis->Iface[iface].gainedSync)
3493                 {
3494                     NV_PRINTF(LEVEL_INFO,
3495                               "P2060[%d] is local master => GAINED SYNC\n",
3496                               iface);
3497                     pThis->Iface[iface].gainedSync = 1;
3498                 }
3499             }
3500             else
3501             {
3502                 // For slaves or masters driven by housesync we need to wait for a sync.
3503 
3504                 // 1st wait for syncgain before doing anything else.
3505                 if ((!pThis->Iface[iface].gainedSync) &&
3506                     (NV_P2060_STATUS_VCXO_LOCK == DRF_VAL(_P2060, _STATUS, _VCXO, newStatus)))
3507                 {
3508                     OBJTMR *pTmr = GPU_GET_TIMER(pIfaceGpu);
3509                     NvU64 timeDiff;
3510                     NvU64 currentTime;
3511 
3512                     // get the current time
3513                     currentTime = tmrGetTime_HAL(pIfaceGpu, pTmr);
3514 
3515                     //
3516                     // Initialize waittime.
3517                     // We're using a threshold of 10 seconds before we're accepting sync gain.
3518                     //
3519                     if (0 == pThis->Snapshot[iface].lastSyncCheckTime)
3520                     {
3521                         pThis->Snapshot[iface].lastSyncCheckTime = currentTime;
3522                     }
3523 
3524                     // calculate the time difference from last change.
3525                     timeDiff = ((currentTime - pThis->Snapshot[iface].lastSyncCheckTime) / 1000000); // time in ms
3526 
3527                     NV_PRINTF(LEVEL_INFO,
3528                               "P2060[%d] snapshot timeDiff is %d ms\n", iface,
3529                               (NvU32)timeDiff);
3530 
3531                     //
3532                     // Update settings if we got no lock or if lock has settled long enough.
3533                     // This is currently selected to 5 seconds by experiments with syncing
3534                     // with stereo enabled.
3535                     //
3536                     if (timeDiff >= 5000)
3537                     {
3538                         NV_PRINTF(LEVEL_INFO, "P2060[%d] GAINED SYNC\n",
3539                                   iface);
3540 
3541                         pThis->Iface[iface].gainedSync = 1;
3542 
3543                         // We've gained sync, right time to also sync the stereo phase.
3544                         if (FLD_TEST_DRF(_P2060, _STATUS, _GPU_STEREO, _ACTIVE, newStatus))
3545                         {
3546                             gsyncGpuStereoHeadSync(pIfaceGpu, iface, pExtDev, newStatus);
3547                             // Reset toggle time to wait some time before checking stereo sync again.
3548                             pThis->Snapshot[iface].lastStereoToggleTime = 0;
3549                         }
3550                     }
3551                     else
3552                     {
3553                         //
3554                         // We haven't reached the settle down time for a sync,
3555                         // so don't update the snapshot this time.
3556                         //
3557                         updateSnapshot = 0;
3558                     }
3559                 }
3560             }
3561 
3562             // Take over new status and send events only if desired.
3563             if (updateSnapshot)
3564             {
3565                 //
3566                 // If we lost sync again reestablish syncwait mechanism.
3567                 // Local master can't loose sync per definition.
3568                 //
3569                 if ((NV_P2060_STATUS_VCXO_LOCK != DRF_VAL(_P2060, _STATUS, _VCXO, newStatus)) &&
3570                     (!localMaster))
3571                 {
3572                     pThis->Iface[iface].gainedSync = 0;
3573                     pThis->Snapshot[iface].lastSyncCheckTime = 0;
3574                 }
3575 
3576                 NV_PRINTF(LEVEL_INFO, "Update P2060[%d] settled from 0x%x ( ",
3577                           iface, oldStatus);
3578                 DbgPrintP2060StatusRegister(LEVEL_INFO, oldStatus);
3579                 NV_PRINTF_EX(NV_PRINTF_MODULE, LEVEL_INFO, ") to 0x%x ( ", newStatus);
3580                 DbgPrintP2060StatusRegister(LEVEL_INFO, newStatus);
3581                 NV_PRINTF_EX(NV_PRINTF_MODULE, LEVEL_INFO, ")\n");
3582 
3583                 diffStatus = (oldStatus ^ newStatus);
3584 
3585                 pThis->Snapshot[iface].Status1 = newStatus;
3586             }
3587         }
3588         else
3589         {
3590             continue;
3591         }
3592 
3593         if ((DRF_VAL(_P2060, _STATUS, _VCXO,      diffStatus)) ||
3594             (DRF_VAL(_P2060, _STATUS, _SYNC_LOSS, diffStatus)) ) // diff state: sync or lock
3595         {
3596             if (FLD_TEST_DRF(_P2060, _STATUS, _SYNC_LOSS, _FALSE, newStatus) &&
3597                 (FLD_TEST_DRF(_P2060, _STATUS, _VCXO,      _LOCK, newStatus) ||
3598                  FLD_TEST_DRF(_P2060, _STATUS, _VCXO, _NOT_SERVO, newStatus)))
3599             {
3600                 ifaceEvents[iface] |= NVBIT(NV30F1_GSYNC_NOTIFIERS_SYNC_GAIN(iface));
3601             }
3602             else
3603             {
3604                 ifaceEvents[iface] |= NVBIT(NV30F1_GSYNC_NOTIFIERS_SYNC_LOSS(iface));
3605             }
3606         }
3607 
3608         // Only track stereo diff states when we are VCXO locked or NOT SERVO.
3609         if ((FLD_TEST_DRF(_P2060, _STATUS, _VCXO, _LOCK, newStatus) ||
3610              FLD_TEST_DRF(_P2060, _STATUS, _VCXO, _NOT_SERVO, newStatus)) &&
3611              DRF_VAL(_P2060, _STATUS, _STEREO, diffStatus)) // diff state: stereo
3612         {
3613             if (FLD_TEST_DRF(_P2060, _STATUS, _STEREO, _LOCK, newStatus))
3614             {
3615                 ifaceEvents[iface] |= NVBIT(NV30F1_GSYNC_NOTIFIERS_STEREO_GAIN(iface));
3616             }
3617             else
3618             {
3619                 ifaceEvents[iface] |= NVBIT(NV30F1_GSYNC_NOTIFIERS_STEREO_LOSS(iface));
3620             }
3621         }
3622     }
3623 
3624     for (iface = 0; iface < NV_P2060_MAX_IFACES_PER_GSYNC; iface++)
3625     {
3626         // Only check stereo phase if we've gained sync.
3627         if (pThis->Iface[iface].gainedSync)
3628         {
3629             // Only check and adjust stereo phase if stereo is enabled on this system.
3630             if (FLD_TEST_DRF(_P2060, _STATUS, _GPU_STEREO, _ACTIVE, pThis->Snapshot[iface].Status1))
3631             {
3632                 //
3633                 // gsyncGpuStereoHeadSync() works without any gsync board interaction
3634                 // with gpu register access only. In addition it will also sync heads
3635                 // which are not driving the stereo pin and can't be monitored by the
3636                 // gsync board.
3637                 // So don't look at the gsync stereolock status as this doesn't cover
3638                 // all heads.
3639                 // Take care of tracking state of connected gpu, not per incoming.
3640                 //
3641                 OBJGPU *pIfaceGpu = gpumgrGetGpuFromId(pThis->Iface[iface].GpuInfo.gpuId);
3642                 OBJTMR *pIfaceTmr;
3643                 NvU32 timeDiff;
3644                 NvU64 currentTime;
3645 
3646                 NV_ASSERT(pIfaceGpu);
3647 
3648                 pIfaceTmr = GPU_GET_TIMER(pIfaceGpu);
3649 
3650                 currentTime = tmrGetTime_HAL(pIfaceGpu, pIfaceTmr);  // get the current time
3651 
3652                 if (pThis->Snapshot[iface].lastStereoToggleTime == 0)
3653                 {
3654                     pThis->Snapshot[iface].lastStereoToggleTime = currentTime;
3655                 }
3656                 timeDiff = (NvU32)((currentTime - pThis->Snapshot[iface].lastStereoToggleTime) / 1000000); // time in ms
3657 
3658                 // toggle stereo if it is not locked more than 5 sec.
3659                 if (timeDiff >= 5000)
3660                 {
3661                     gsyncGpuStereoHeadSync(pIfaceGpu, iface, pExtDev, pThis->Snapshot[iface].Status1);
3662                     pThis->Snapshot[iface].lastStereoToggleTime = currentTime;
3663                 }
3664 
3665                 // Only report stereo loss if stereo is not in phase.
3666                 if (!(FLD_TEST_DRF(_P2060, _STATUS, _STEREO, _LOCK, pThis->Snapshot[iface].Status1)))
3667                 {
3668                     ifaceEvents[iface] |= NVBIT(NV30F1_GSYNC_NOTIFIERS_STEREO_LOSS(iface)); // report loss this time.
3669                 }
3670             }
3671         }
3672     }
3673 
3674     for (iface = 0; iface < NV_P2060_MAX_IFACES_PER_GSYNC; iface++)
3675     {
3676         if (ifaceEvents[iface] && (pThis->Iface[iface].lastEventNotified != ifaceEvents[iface]))
3677         {
3678             NV_PRINTF(LEVEL_INFO, "Event P2060[%d]: 0x%x (", iface,
3679                       ifaceEvents[iface]);
3680             gsyncDbgPrintGsyncEvents(LEVEL_INFO, ifaceEvents[iface], iface);
3681             NV_PRINTF_EX(NV_PRINTF_MODULE, LEVEL_INFO, ")\n");
3682 
3683             // notify clients that something has happened!
3684             gsyncSignalServiceRequested(gsyncGetGsyncInstance(pGpu), ifaceEvents[iface], iface);
3685             pThis->Iface[iface].lastEventNotified = ifaceEvents[iface];
3686         }
3687     }
3688 
3689     return NV_OK;
3690 }
3691 
3692 
3693 /*
3694  * Handle Get and Set queries related to signals.
3695  */
3696 NV_STATUS
3697 gsyncRefSignal_P2060
3698 (
3699     OBJGPU            *pGpu,
3700     PDACEXTERNALDEVICE pExtDev,
3701     REFTYPE            rType,
3702     GSYNCSYNCSIGNAL    Signal,
3703     NvBool             bRate,
3704     NvU32              *pPresence
3705 )
3706 {
3707     NV_STATUS status = NV_OK;
3708     NvU32 rate;
3709     NvU32 value = 0;
3710     NvU32 bMaster; // Is this Gsync in Master mode
3711     PDACP2060EXTERNALDEVICE pThis = (PDACP2060EXTERNALDEVICE)pExtDev;
3712 
3713     if (!bRate)
3714     {
3715         return NV_OK;
3716     }
3717 
3718     //
3719     // Is any display on this P2060 a master, or are we using an ext
3720     // sync to be master.
3721     //
3722     bMaster = !!gsyncReadMaster_P2060(pGpu, pThis);
3723 
3724     switch ( rType )
3725     {
3726     case refFetchGet:
3727          if (gsync_Signal_RJ45_0 == Signal || gsync_Signal_RJ45_1 == Signal)
3728          {
3729              // Relevant only if this port is an input
3730              NvU32 port0Direction, expectedPort0Direction;
3731              NvU32 port1Direction, expectedPort1Direction;
3732 
3733              status = gsyncReadFrameRate_P2060(pGpu, pExtDev, &rate);
3734              if (NV_OK != status)
3735                  return status;
3736 
3737              status  = readregu008_extdeviceTargeted(pGpu, pExtDev, (NvU8)NV_P2060_STATUS2, (NvU8*)&value);
3738              if (NV_OK != status)
3739                  return status;
3740 
3741              port0Direction = DRF_VAL(_P2060, _STATUS2, _PORT0, value);
3742              port1Direction = DRF_VAL(_P2060, _STATUS2, _PORT1, value);
3743 
3744              if (gsync_Signal_RJ45_0 == Signal)
3745              {
3746                  expectedPort0Direction = NV_P2060_STATUS2_PORT0_INPUT;
3747                  expectedPort1Direction = NV_P2060_STATUS2_PORT1_OUTPUT;
3748              }
3749              else
3750              {
3751                  expectedPort0Direction = NV_P2060_STATUS2_PORT0_OUTPUT;
3752                  expectedPort1Direction = NV_P2060_STATUS2_PORT1_INPUT;
3753              }
3754              if (port0Direction == expectedPort0Direction && port1Direction == expectedPort1Direction)
3755              {
3756                  if (bMaster)
3757                  {
3758                      *pPresence = ~0;
3759                  }
3760                  else
3761                  {
3762                      *pPresence = rate;
3763                  }
3764              }
3765              else
3766              {
3767                 *pPresence = 0;
3768              }
3769          }
3770          else if (gsync_Signal_House == Signal)
3771          {
3772              status = gsyncReadHouseSignalPresent_P2060(pGpu, pExtDev, NV_FALSE, &value);
3773 
3774              if (value && NV_OK == status)
3775              {
3776                  status = gsyncReadHouseSyncFrameRate_P2060(pGpu, pExtDev, &rate);
3777                  if (NV_OK != status)
3778                      return status;
3779 
3780                  if (bMaster)
3781                  {
3782                     *pPresence = rate;
3783                  }
3784                  else
3785                  {
3786                     *pPresence = ~0;
3787                  }
3788              }
3789              else
3790              {
3791                  *pPresence = 0;
3792              }
3793          }
3794          else
3795          {
3796              return NV_ERR_GENERIC;
3797          }
3798          break;
3799 
3800     case refRead:
3801          break;
3802     default:
3803          return NV_ERR_GENERIC;
3804     }
3805 
3806     return status;
3807 }
3808 
3809 /*
3810  * Handle Get and Set queries related to Master.
3811  */
3812 NV_STATUS
3813 gsyncRefMaster_P2060
3814 (
3815     OBJGPU *pGpu,
3816     PDACEXTERNALDEVICE pExtDev,
3817     REFTYPE rType,
3818     NvU32 *pDisplayMask,
3819     NvU32 *pRefresh,
3820     NvBool retainMaster,
3821     NvBool skipSwapBarrierWar
3822 )
3823 {
3824     PDACP2060EXTERNALDEVICE pThis = (PDACP2060EXTERNALDEVICE)pExtDev;
3825     NvU32 Master = pThis->Master;
3826     NvU32 RefreshRate = pThis->RefreshRate;
3827     NV_STATUS status = NV_OK;
3828 
3829     switch ( rType )
3830     {
3831     case refSetCommit:
3832         if (!retainMaster)
3833         {
3834             Master = pThis->Master = *pDisplayMask;
3835         }
3836         RefreshRate = pThis->RefreshRate = *pRefresh;
3837         break;
3838     default:
3839         break;
3840     }
3841 
3842     switch ( rType )
3843     {
3844     case refSetCommit:
3845         // Only masterable gpus can be set to master, but either can be set to non-master.
3846         if (Master && (!gsyncGpuCanBeMaster_P2060(pGpu, (PDACEXTERNALDEVICE)pThis)))
3847         {
3848             NV_PRINTF(LEVEL_INFO, "P2060 GPU can not be Framelock Master.\n");
3849             return NV_ERR_GENERIC;
3850         }
3851 
3852         if (GpuIsMosaicTimingSlave(pGpu, pThis))
3853         {
3854             NV_PRINTF(LEVEL_INFO,
3855                       "P2060 GPU is mosaic timing slave. Can not set Framelock Master.\n");
3856             return NV_ERR_GENERIC;
3857         }
3858 
3859         status = gsyncProgramMaster_P2060(pGpu, pThis, Master, retainMaster, skipSwapBarrierWar);
3860         break;
3861 
3862     case refFetchGet:
3863     case refRead:
3864         Master = gsyncReadMaster_P2060(pGpu, pThis);
3865         break;
3866     default:
3867         break;
3868     }
3869 
3870     switch ( rType )
3871     {
3872     case refFetchGet:
3873         pThis->Master = Master;
3874         /*NOBREAK*/
3875     case refRead:
3876         *pDisplayMask = Master;
3877         *pRefresh = RefreshRate;
3878         break;
3879 
3880     default:
3881         break;
3882     }
3883 
3884     return status;
3885 }
3886 
3887 /*
3888  * Handle Get and Set queries related to Slaves.
3889  */
3890 NV_STATUS
3891 gsyncRefSlaves_P2060
3892 (
3893     OBJGPU *pGpu,
3894     PDACEXTERNALDEVICE pExtDev,
3895     REFTYPE rType,
3896     NvU32 *pDisplayMasks,
3897     NvU32 *pRefresh
3898 )
3899 {
3900     PDACP2060EXTERNALDEVICE pThis = (PDACP2060EXTERNALDEVICE)pExtDev;
3901     NV_STATUS status = NV_OK;
3902     NvU32 Slaves = pThis->Slaves;
3903     NvU32 RefreshRate = pThis->RefreshRate;
3904 
3905     switch ( rType )
3906     {
3907     case refSetCommit:
3908         pThis->Slaves = *pDisplayMasks;
3909         pThis->RefreshRate = *pRefresh;
3910         Slaves = pThis->Slaves;
3911         RefreshRate = pThis->RefreshRate;
3912         break;
3913     default:
3914         break;
3915     }
3916 
3917     switch ( rType )
3918     {
3919     case refSetCommit:
3920         status = gsyncProgramSlaves_P2060(pGpu, pThis, Slaves);
3921         break;
3922 
3923     case refFetchGet:
3924     case refRead:
3925         Slaves = gsyncReadSlaves_P2060(pGpu, pThis);
3926         break;
3927     default:
3928         break;
3929     }
3930 
3931     switch ( rType )
3932     {
3933     case refFetchGet:
3934         pThis->Slaves = Slaves;
3935         /*NOBREAK*/
3936     case refRead:
3937         *pDisplayMasks = Slaves;
3938         *pRefresh = RefreshRate;
3939         break;
3940 
3941     default:
3942         break;
3943     }
3944     return status;
3945 }
3946 
3947 /*
3948  * Handle Get queries related to CPL status.
3949  */
3950 NV_STATUS
3951 gsyncGetCplStatus_P2060
3952 (
3953     OBJGPU *pGpu,
3954     PDACEXTERNALDEVICE pExtDev,
3955     GSYNCSTATUS CplStatus,
3956     NvU32 *pVal
3957 )
3958 {
3959     NV_STATUS status = NV_OK;
3960     NvU8  regStatus2;
3961 
3962     *pVal = 0; // A little safety for those that do not check return codes
3963 
3964     switch (CplStatus)
3965     {
3966         case gsync_Status_Refresh:
3967             // Read GSYNC Framerate value.
3968             status = gsyncReadFrameRate_P2060(pGpu, pExtDev, pVal);
3969             break;
3970 
3971         case gsync_Status_HouseSyncIncoming:
3972             *pVal = 0;
3973             status = gsyncReadHouseSignalPresent_P2060(pGpu, pExtDev, NV_FALSE, pVal);
3974             if (NV_OK == status &&  *pVal)
3975             {
3976                 // Read HS Framerate value.
3977                 status = gsyncReadHouseSyncFrameRate_P2060(pGpu, pExtDev, pVal);
3978             }
3979             break;
3980 
3981         case gsync_Status_bSyncReady:
3982             status = gsyncReadIsSyncDetected_P2060(pGpu, pExtDev, pVal);
3983             break;
3984 
3985         case gsync_Status_bSwapReady:
3986             *pVal = 0; // counters should exist in P2060?
3987             break;
3988 
3989         case gsync_Status_bTiming:
3990             status = gsyncReadIsTiming_P2060(pGpu, pExtDev, pVal);
3991             break;
3992 
3993         case gsync_Status_bStereoSync:
3994             status = gsyncReadStereoLocked_P2060(pGpu, pExtDev, pVal);
3995             break;
3996 
3997         case gsync_Status_bHouseSync:
3998             status = gsyncReadHouseSignalPresent_P2060(pGpu, pExtDev, NV_FALSE, pVal);
3999             break;
4000 
4001         case gsync_Status_bPort0Input:
4002             status = readregu008_extdeviceTargeted(pGpu, pExtDev, (NvU8)NV_P2060_STATUS2, &regStatus2);
4003             if ( NV_OK == status )
4004             {
4005                 *pVal = FLD_TEST_DRF(_P2060, _STATUS2, _PORT0, _INPUT, (NvU32)regStatus2);
4006             }
4007             break;
4008 
4009         case gsync_Status_bPort1Input:
4010             status = readregu008_extdeviceTargeted(pGpu, pExtDev, (NvU8)NV_P2060_STATUS2, &regStatus2);
4011             if ( NV_OK == status )
4012             {
4013                 *pVal = FLD_TEST_DRF(_P2060, _STATUS2, _PORT1, _INPUT, (NvU32)regStatus2);
4014             }
4015             break;
4016 
4017         case gsync_Status_bPort0Ethernet:
4018             status = readregu008_extdeviceTargeted(pGpu, pExtDev, (NvU8)NV_P2060_STATUS2, &regStatus2);
4019             if ( NV_OK == status )
4020             {
4021                 *pVal = FLD_TEST_DRF(_P2060, _STATUS2, _ETHER0_DETECTED, _TRUE, (NvU32)regStatus2);
4022             }
4023             break;
4024 
4025         case gsync_Status_bPort1Ethernet:
4026             status = readregu008_extdeviceTargeted(pGpu, pExtDev, (NvU8)NV_P2060_STATUS2, &regStatus2);
4027             if ( NV_OK == status )
4028             {
4029                 *pVal = FLD_TEST_DRF(_P2060, _STATUS2, _ETHER1_DETECTED, _TRUE, (NvU32)regStatus2);
4030             }
4031             break;
4032 
4033         case gsync_Status_UniversalFrameCount:
4034             status = gsyncReadUniversalFrameCount_P2060(pGpu, pExtDev, pVal);
4035             break;
4036 
4037         default:
4038             status = NV_ERR_GENERIC;
4039     }
4040 
4041     return status;
4042 }
4043 
4044 /*
4045  * Handle Get and Set queries related to Watchdog.
4046  */
4047 NV_STATUS
4048 gsyncSetWatchdog_P2060
4049 (
4050     OBJGPU *pGpu,
4051     PDACEXTERNALDEVICE pExtDev,
4052     NvU32 pVal
4053 )
4054 {
4055     OBJGPU *pTempGpu = NULL;
4056     PDACP2060EXTERNALDEVICE pP2060 = (PDACP2060EXTERNALDEVICE)pExtDev;
4057     NV_STATUS status = NV_OK;
4058 
4059     NV_ASSERT_OR_RETURN(pGpu && pP2060, NV_ERR_INVALID_DEVICE);
4060 
4061     if (pVal)
4062     {
4063         gsyncCancelWatchdog_P2060(pP2060);
4064         pTempGpu = GetP2060WatchdogGpu(pGpu, pP2060);
4065 
4066         extdevScheduleWatchdog(pTempGpu, (PDACEXTERNALDEVICE)pP2060);
4067         if (!gsyncIsOnlyFrameLockMaster_P2060(pP2060))
4068         {
4069             pP2060->watchdogCountDownValue = NV_P2060_WATCHDOG_COUNT_DOWN_VALUE;
4070         }
4071     }
4072 
4073     return status;
4074 }
4075 
4076 /*
4077  * Handle Get and Set queries related to board revision.
4078  */
4079 NV_STATUS
4080 gsyncGetRevision_P2060
4081 (
4082     OBJGPU *pGpu,
4083     PDACEXTERNALDEVICE pExtDev,
4084     GSYNCCAPSPARAMS *pParams
4085 )
4086 {
4087     OBJSYS  *pSys = SYS_GET_INSTANCE();
4088     NV_STATUS status = NV_OK;
4089     DAC_EXTERNAL_DEVICES deviceId = pExtDev->deviceId;
4090     DAC_EXTERNAL_DEVICE_REVS deviceRev = pExtDev->deviceRev;
4091 
4092     if (!pGpu)
4093     {
4094         return NV_ERR_GENERIC; // something more descriptive, perhaps?
4095     }
4096 
4097     pParams->revId = (NvU32)pExtDev->revId;
4098     pParams->boardId = (NvU32)deviceId;
4099     pParams->revision = (NvU32)deviceRev;
4100     pParams->extendedRevision = (NvU32)pExtDev->deviceExRev;
4101 
4102     if ((deviceRev != DAC_EXTERNAL_DEVICE_REV_NONE) &&
4103         (deviceId == DAC_EXTERNAL_DEVICE_P2060 ||
4104          deviceId == DAC_EXTERNAL_DEVICE_P2061))
4105     {
4106         DACP2060EXTERNALDEVICE *p2060 = (DACP2060EXTERNALDEVICE *)pExtDev;
4107 
4108         pParams->capFlags = NV30F1_CTRL_GSYNC_GET_CAPS_CAP_FLAGS_FREQ_ACCURACY_3DPS;
4109 
4110         if (!pSys->getProperty(pSys, PDB_PROP_SYS_IS_QSYNC_FW_REVISION_CHECK_DISABLED))
4111         {
4112             pParams->isFirmwareRevMismatch = isFirmwareRevMismatch(pGpu, deviceRev);
4113         }
4114         else
4115         {
4116             pParams->isFirmwareRevMismatch = NV_FALSE;
4117         }
4118 
4119         pParams->maxSyncSkew          = p2060->syncSkewMax;
4120         pParams->syncSkewResolution   = p2060->syncSkewResolutionInNs;
4121         pParams->maxStartDelay        = NV_P2060_START_DELAY_MAX_UNITS;
4122         pParams->startDelayResolution = NV_P2060_START_DELAY_RESOLUTION;
4123         pParams->maxSyncInterval      = NV_P2060_SYNC_INTERVAL_MAX_UNITS;
4124 
4125         // let client know which events we support
4126         pParams->capFlags |= NV30F1_CTRL_GSYNC_GET_CAPS_CAP_FLAGS_SYNC_LOCK_EVENT;
4127         pParams->capFlags |= NV30F1_CTRL_GSYNC_GET_CAPS_CAP_FLAGS_HOUSE_SYNC_EVENT;
4128 
4129         // all connectors are capable of generating events
4130         pParams->capFlags |= NV30F1_CTRL_GSYNC_GET_CAPS_CAP_FLAGS_ALL_CONNECTOR_EVENT;
4131 
4132         // clients can only request (i.e. not SET) for video mode at BNC connector.
4133         pParams->capFlags |= NV30F1_CTRL_GSYNC_GET_CAPS_CAP_FLAGS_ONLY_GET_VIDEO_MODE;
4134 
4135         // Fpga revisions <= 5 need to have the swapbarrier set on framelock masters
4136         // to drive (pull up) the swap_rdy line of the whole framelock setup.
4137         // This is a behaviour with unwanted side effects which needs drivers wars
4138         // for certain configs.
4139         if (needsMasterBarrierWar(pExtDev))
4140         {
4141             pParams->capFlags |= NV30F1_CTRL_GSYNC_GET_CAPS_CAP_FLAGS_NEED_MASTER_BARRIER_WAR;
4142         }
4143     }
4144     else
4145     {
4146         pParams->boardId = DAC_EXTERNAL_DEVICE_NONE;
4147     }
4148 
4149     return status;
4150 }
4151 
4152 /*
4153  * Handle Get and Set queries related to Swap barrier.
4154  */
4155 NV_STATUS
4156 gsyncRefSwapBarrier_P2060
4157 (
4158     OBJGPU              *pGpu,
4159     PDACEXTERNALDEVICE   pExtDev,
4160     REFTYPE              rType,
4161     NvBool               *bEnable
4162 )
4163 {
4164     NV_STATUS status = NV_OK;
4165 
4166     switch (rType)
4167     {
4168         case refSetCommit:
4169             status = gsyncProgramSwapBarrier_P2060(pGpu, pExtDev, *bEnable);
4170             break;
4171         case refFetchGet:
4172         case refRead:
4173             status = gsyncReadSwapBarrier_P2060(pGpu, pExtDev, bEnable);
4174             break;
4175         default:
4176             break;
4177     }
4178 
4179     return status;
4180 }
4181 
4182 /*
4183  * Configure GSYNC registers for pre-flash and post-flash operations.
4184  */
4185 NV_STATUS
4186 gsyncConfigFlashGsync_P2060
4187 (
4188     OBJGPU                *pGpu,
4189     PDACEXTERNALDEVICE     pExtDev,
4190     NvU32                  preFlash
4191 )
4192 {
4193     PDACP2060EXTERNALDEVICE pThis = (PDACP2060EXTERNALDEVICE) pExtDev;
4194     NvU32 iface;
4195     NV_STATUS rmStatus = NV_OK;
4196 
4197     if (preFlash)
4198     {
4199         if (pThis->isNonFramelockInterruptEnabled == NV_FALSE)
4200         {
4201             // Non-Framelock interrupts are already disabled.
4202             return NV_OK;
4203         }
4204 
4205         // Disable non-Framelock interrupts for given gpu
4206         for (iface = 0; iface < NV_P2060_MAX_IFACES_PER_GSYNC; iface++)
4207         {
4208             if (pThis->Iface[iface].GpuInfo.gpuId != NV0000_CTRL_GPU_INVALID_ID &&
4209                 pThis->interruptEnabledInterface == iface)
4210             {
4211                 OBJGPU *pTmpGpu = gpumgrGetGpuFromId(pThis->Iface[iface].GpuInfo.gpuId);
4212 
4213                 NV_ASSERT(pTmpGpu);
4214 
4215                 rmStatus = gsyncDisableNonFramelockInterrupt_P2060(pTmpGpu, (PDACEXTERNALDEVICE)pThis);
4216                 if (rmStatus != NV_OK)
4217                 {
4218                     NV_PRINTF(LEVEL_ERROR,
4219                               "Failed to disable non-framelock interrupts on gsync GPU.\n");
4220                     return rmStatus;
4221                 }
4222                 break;
4223             }
4224         }
4225         pThis->isNonFramelockInterruptEnabled = NV_FALSE;
4226     }
4227     else
4228     {
4229         if (pThis->isNonFramelockInterruptEnabled == NV_FALSE)
4230         {
4231             // Enable non-Framelock interrupts for given gpu
4232             rmStatus = GetP2060ConnectorIndexFromGpu(pGpu, pThis, &iface);
4233             if (NV_OK != rmStatus)
4234             {
4235                 NV_PRINTF(LEVEL_ERROR,
4236                           "failed to find P2060 connector of the GPU.\n");
4237                 return rmStatus;
4238             }
4239 
4240             rmStatus = gsyncEnableNonFramelockInterrupt_P2060(pGpu, (PDACEXTERNALDEVICE)pThis);
4241             if (NV_OK != rmStatus)
4242             {
4243                 NV_PRINTF(LEVEL_ERROR,
4244                           "Failed to enable non-framelock interrupts on gsync GPU.\n");
4245                 return rmStatus;
4246             }
4247             pThis->isNonFramelockInterruptEnabled = NV_TRUE;
4248             pThis->interruptEnabledInterface = iface;
4249         }
4250 
4251         // Program External Stereo Sync Polarity for all attached gpus.
4252         for (iface = 0; iface < NV_P2060_MAX_IFACES_PER_GSYNC; iface++)
4253         {
4254             if (pThis->Iface[iface].GpuInfo.gpuId != NV0000_CTRL_GPU_INVALID_ID)
4255             {
4256                 OBJGPU *pTmpGpu = gpumgrGetGpuFromId(pThis->Iface[iface].GpuInfo.gpuId);
4257 
4258                 NV_ASSERT(pTmpGpu);
4259 
4260                 rmStatus = gsyncProgramExtStereoPolarity_P2060(pTmpGpu, (PDACEXTERNALDEVICE)pThis);
4261                 if (NV_OK != rmStatus)
4262                 {
4263                     NV_PRINTF(LEVEL_ERROR,
4264                               "Failed to Program External Stereo Polarity for GPU.\n");
4265                     return rmStatus;
4266                 }
4267             }
4268         }
4269     }
4270     return rmStatus;
4271 }
4272 
4273 /*
4274  * Return snapshot of status1 register.
4275  */
4276 static NvU32
4277 GetP2060GpuSnapshot
4278 (
4279     OBJGPU *pGpu,
4280     PDACP2060EXTERNALDEVICE pThis
4281 )
4282 {
4283     NvU32 iface;
4284     NV_STATUS status;
4285 
4286     status = GetP2060GpuLocation(pGpu, pThis, &iface);
4287     if (NV_OK != status)
4288         return 0;
4289 
4290     return pThis->Snapshot[iface].Status1;
4291 }
4292 
4293 /*
4294  * Return NV_TRUE if pGpu is Timing Source else return NV_FALSE.
4295  */
4296 static NvBool
4297 GpuIsP2060Master
4298 (
4299     OBJGPU *pGpu,
4300     PDACP2060EXTERNALDEVICE pThis
4301 )
4302 {
4303     NvU8 regControl;
4304     NvU32 index;
4305     NvBool bIsMasterGpu;
4306     NV_STATUS rmStatus;
4307 
4308     if (!pGpu || !GpuIsP2060Connected(pGpu, pThis))
4309     {
4310         return NV_FALSE;
4311     }
4312 
4313     // Get the connector index and check if it is master
4314     if ( NV_OK != GetP2060ConnectorIndexFromGpu(pGpu, pThis, &index))
4315     {
4316         return NV_FALSE;
4317     }
4318 
4319     rmStatus = readregu008_extdeviceTargeted(pGpu, (PDACEXTERNALDEVICE)pThis, (NvU8)NV_P2060_CONTROL, &regControl);
4320     if (NV_OK != rmStatus)
4321     {
4322         return NV_FALSE;
4323     }
4324 
4325     bIsMasterGpu = (DRF_VAL(_P2060, _CONTROL, _SYNC_SRC, (NvU32)regControl) == index);
4326     return bIsMasterGpu;
4327 }
4328 
4329 /*
4330  * Return whether pGpu is connected to pThis or not.
4331  */
4332 static NvBool
4333 GpuIsP2060Connected
4334 (
4335     OBJGPU *pGpu,
4336     PDACP2060EXTERNALDEVICE pThis
4337 )
4338 {
4339     NvU32 iface;
4340 
4341     if (NV_OK == GetP2060GpuLocation(pGpu, pThis, &iface))
4342     {
4343         return pThis->Iface[iface].GpuInfo.connected;
4344     }
4345 
4346     return NV_FALSE;
4347 }
4348 
4349 /*
4350  * Return Masterable(TS) Gpu for pthis.
4351  */
4352 static OBJGPU*
4353 GetP2060MasterableGpu
4354 (
4355     OBJGPU *pGpu,
4356     PDACP2060EXTERNALDEVICE pThis
4357 )
4358 {
4359     OBJGPU *tempGpu;
4360     NvU32 iface;
4361 
4362     for (iface = 0; iface < NV_P2060_MAX_IFACES_PER_GSYNC; iface++)
4363     {
4364         if (pThis->Iface[iface].GpuInfo.gpuId != NV0000_CTRL_GPU_INVALID_ID)
4365         {
4366             tempGpu = gpumgrGetGpuFromId(pThis->Iface[iface].GpuInfo.gpuId);
4367 
4368             NV_ASSERT(tempGpu);
4369 
4370             if (gsyncGpuCanBeMaster_P2060(tempGpu, (PDACEXTERNALDEVICE)pThis))
4371             {
4372                 return tempGpu;
4373             }
4374         }
4375     }
4376 
4377     return NULL;
4378 }
4379 
4380 /*
4381  * Return connector index for given pGpu.
4382  */
4383 static NV_STATUS
4384 GetP2060ConnectorIndexFromGpu
4385 (
4386     OBJGPU *pGpu,
4387     PDACP2060EXTERNALDEVICE pThis,
4388     NvU32 *index
4389 )
4390 {
4391     NV_STATUS rmStatus = NV_OK;
4392     NvU8 regStatus2;
4393 
4394     rmStatus  = readregu008_extdeviceTargeted(pGpu, (PDACEXTERNALDEVICE)pThis, (NvU8)NV_P2060_STATUS2, &regStatus2);
4395     if (NV_OK == rmStatus)
4396     {
4397         *index = DRF_VAL(_P2060, _STATUS2, _GPU_PORT, (NvU32)regStatus2);
4398     }
4399 
4400     return rmStatus;
4401 }
4402 
4403 /*
4404  * Return location of pGpu attached to P2060.
4405  */
4406 static NV_STATUS
4407 GetP2060GpuLocation
4408 (
4409     OBJGPU *pGpu,
4410     PDACP2060EXTERNALDEVICE pThis,
4411     NvU32 *iface
4412 )
4413 {
4414     NvU32 tempIface;
4415 
4416     for (tempIface = 0; tempIface < NV_P2060_MAX_IFACES_PER_GSYNC; tempIface++)
4417     {
4418         if (pThis->Iface[tempIface].GpuInfo.gpuId == pGpu->gpuId)
4419         {
4420             *iface = tempIface;
4421 
4422             return NV_OK;
4423         }
4424     }
4425 
4426     return NV_ERR_GENERIC;
4427 }
4428 
4429 static void
4430 gsyncProgramFramelockEnable_P2060
4431 (
4432     OBJGPU                 *pGpu,
4433     PDACP2060EXTERNALDEVICE pThis,
4434     NvU32                   iface,
4435     NvBool                  bEnable
4436 )
4437 {
4438     //
4439     // Key here is to force the status register snapshot
4440     // to unsynched and setting back the timeout for
4441     // syncgain. To ensure that the eventhandling won't
4442     // filter away any gain, send a syncloss event.
4443     // also track overall framelock state (on off) now.
4444     //
4445     NV_PRINTF(LEVEL_INFO,
4446               "P2060[%d]:%s snapshot reset to _SYNC_LOSS_TRUE _VCXO_NOT_SERVO _STEREO_NOLOCK\n",
4447               iface, bEnable ? "ON" : "OFF");
4448 
4449     pThis->Snapshot[iface].Status1 =
4450         DRF_DEF(_P2060, _STATUS, _SYNC_LOSS, _TRUE) |
4451         DRF_DEF(_P2060, _STATUS, _VCXO, _NOT_SERVO) |
4452         DRF_DEF(_P2060, _STATUS, _STEREO, _NOLOCK);
4453 
4454     pThis->Snapshot[iface].lastStereoToggleTime = 0;
4455 
4456     // Also reload delayed updates.
4457     pThis->Snapshot[iface].lastSyncCheckTime = 0;
4458 
4459     // We will waiting for a syncgain.
4460     pThis->Iface[iface].gainedSync = 0;
4461 
4462     if (bEnable)
4463     {
4464         gsyncSignalServiceRequested(gsyncGetGsyncInstance(pGpu),
4465             NVBIT(NV30F1_GSYNC_NOTIFIERS_SYNC_LOSS(iface)), iface);
4466         pThis->Iface[iface].lastEventNotified =
4467             NVBIT(NV30F1_GSYNC_NOTIFIERS_SYNC_LOSS(iface));
4468     }
4469 }
4470 
4471 static NvBool
4472 GpuIsMosaicTimingSlave
4473 (
4474     OBJGPU *pGpu,
4475     PDACP2060EXTERNALDEVICE pThis
4476 )
4477 {
4478     OBJGPU *pTempGpu = NULL;
4479     NvU8 i, j;
4480 
4481     for (i = 0; i < NV_P2060_MAX_MOSAIC_GROUPS; i++)
4482     {
4483         if (pThis->MosaicGroup[i].enabledMosaic)
4484         {
4485              for (j = 0; j < NV_P2060_MAX_MOSAIC_SLAVES; j++)
4486              {
4487                   pTempGpu = gpumgrGetGpuFromId(pThis->MosaicGroup[i].gpuTimingSlaves[j]);
4488                   NV_ASSERT(pTempGpu);
4489 
4490                   if (pTempGpu == pGpu)
4491                   {
4492                       return NV_TRUE;
4493                   }
4494              }
4495         }
4496     }
4497     return NV_FALSE;
4498 }
4499 
4500 /*
4501  * Return NV_TRUE if pGpu can be master(TS) i.e. no other pGpu
4502  * attached to pThis is master.
4503  */
4504 NvBool
4505 gsyncGpuCanBeMaster_P2060
4506 (
4507     OBJGPU *pGpu,
4508     PDACEXTERNALDEVICE pExtDev
4509 )
4510 {
4511     PDACP2060EXTERNALDEVICE pThis = (PDACP2060EXTERNALDEVICE)pExtDev;
4512     OBJGPU *pTempGpu;
4513     NvU32 iface, tempIface;
4514     NV_STATUS status;
4515 
4516     // If FPGA board in not master, Any GPU attached to board can be master.
4517     if (!gsyncIsP2060MasterBoard(pGpu, pThis))
4518     {
4519         return NV_TRUE;
4520     }
4521 
4522     // Board is master, only TS GPU will be master.
4523     status = GetP2060GpuLocation(pGpu, pThis, &iface);
4524     if (NV_OK != status)
4525     {
4526         return NV_FALSE;
4527     }
4528 
4529     for (tempIface = 0; tempIface < NV_P2060_MAX_IFACES_PER_GSYNC; tempIface++)
4530     {
4531         if (tempIface == iface)
4532         {
4533             continue;
4534         }
4535 
4536         if (pThis->Iface[tempIface].GpuInfo.gpuId == NV0000_CTRL_GPU_INVALID_ID)
4537         {
4538             continue;
4539         }
4540 
4541         pTempGpu = gpumgrGetGpuFromId(pThis->Iface[tempIface].GpuInfo.gpuId);
4542 
4543         NV_ASSERT(pTempGpu);
4544 
4545         if (GpuIsP2060Master(pTempGpu, pThis))
4546         {
4547             return NV_FALSE;
4548         }
4549     }
4550 
4551     return NV_TRUE;
4552 }
4553 
4554 
4555 static POBJGPU
4556 GetP2060Gpu
4557 (
4558     OBJGPU *pGpu,
4559     PDACP2060EXTERNALDEVICE pThis
4560 )
4561 {
4562     NvU32 iface;
4563     for (iface = 0; iface < NV_P2060_MAX_IFACES_PER_GSYNC; iface++)
4564     {
4565         if (pThis->Iface[iface].GpuInfo.gpuId != NV0000_CTRL_GPU_INVALID_ID)
4566         {
4567             OBJGPU *pTmpGpu = gpumgrGetGpuFromId(pThis->Iface[iface].GpuInfo.gpuId);
4568 
4569             NV_ASSERT(pTmpGpu);
4570 
4571             return pTmpGpu;
4572         }
4573     }
4574     return NULL;
4575 }
4576 
4577 static OBJGPU*
4578 GetP2060WatchdogGpu
4579 (
4580     OBJGPU *pGpu,
4581     PDACP2060EXTERNALDEVICE pThis
4582 )
4583 {
4584    return GetP2060Gpu(pGpu, pThis);
4585 }
4586 
4587 /*
4588  * Return NV_TRUE if either GPU stereo or MASTER stereo or
4589  * both are enabled else return NV_FALSE.
4590  */
4591 static NvBool
4592 gsyncIsStereoEnabled_p2060
4593 (
4594     OBJGPU            *pGpu,
4595     PDACEXTERNALDEVICE pExtDev
4596 )
4597 {
4598     NvU8 regStatus;
4599     NV_STATUS rmStatus;
4600 
4601     rmStatus  = readregu008_extdeviceTargeted(pGpu, pExtDev, (NvU8)NV_P2060_STATUS,  &regStatus);
4602 
4603     if (rmStatus == NV_OK)
4604     {
4605         if (FLD_TEST_DRF(_P2060, _STATUS, _GPU_STEREO, _ACTIVE, regStatus) ||
4606             FLD_TEST_DRF(_P2060, _STATUS, _MSTR_STEREO, _ACTIVE, regStatus))
4607         {
4608             // stereo is enabled on the client or the server or both
4609             return NV_TRUE;
4610         }
4611     }
4612     return NV_FALSE;
4613 }
4614 
4615 /*
4616  * Cancel the Watchdog for P2060.
4617  */
4618 static void
4619 gsyncCancelWatchdog_P2060
4620 (
4621     PDACP2060EXTERNALDEVICE pThis
4622 )
4623 {
4624     NvU32 iface;
4625     OBJGPU *pTempGpu = NULL;
4626 
4627     pThis->watchdogCountDownValue = 0;
4628 
4629     // Cancel callbacks on all gpus
4630     for (iface = 0; iface < NV_P2060_MAX_IFACES_PER_GSYNC; iface++)
4631     {
4632         if (pThis->Iface[iface].GpuInfo.gpuId == NV0000_CTRL_GPU_INVALID_ID)
4633             continue;
4634 
4635         pTempGpu = gpumgrGetGpuFromId(pThis->Iface[iface].GpuInfo.gpuId);
4636         NV_ASSERT(pTempGpu);
4637 
4638         NV_PRINTF(LEVEL_INFO, "P2060[%d] extdevCancelWatchdog.\n", iface);
4639 
4640         extdevCancelWatchdog(pTempGpu, (PDACEXTERNALDEVICE)pThis);
4641     }
4642 
4643     return;
4644 }
4645 
4646 /*
4647  * Enable FrameLock Interrupts for P2060.
4648  */
4649 static NV_STATUS
4650 gsyncEnableFramelockInterrupt_P2060
4651 (
4652     PDACEXTERNALDEVICE pExtDev
4653 )
4654 {
4655     PDACP2060EXTERNALDEVICE pThis = (PDACP2060EXTERNALDEVICE) pExtDev;
4656     NvU8  regCtrl3;
4657     NvU32 iface;
4658     NV_STATUS status = NV_OK;
4659 
4660     // Turn ON the interrupts
4661     for (iface = 0; iface < NV_P2060_MAX_IFACES_PER_GSYNC; iface++)
4662     {
4663         if (pThis->Iface[iface].GpuInfo.gpuId != NV0000_CTRL_GPU_INVALID_ID)
4664         {
4665             OBJGPU *pTmpGpu = gpumgrGetGpuFromId(pThis->Iface[iface].GpuInfo.gpuId);
4666             NV_ASSERT(pTmpGpu);
4667 
4668             if (gsyncReadMaster_P2060(pTmpGpu, pThis) || gsyncReadSlaves_P2060(pTmpGpu, pThis))
4669             {
4670                 status = readregu008_extdeviceTargeted(pTmpGpu,
4671                          (PDACEXTERNALDEVICE)pThis, NV_P2060_CONTROL3,  &regCtrl3);
4672 
4673                 if (gsyncIsStereoEnabled_p2060(pTmpGpu, pExtDev))
4674                 {
4675                       regCtrl3 |= DRF_DEF(_P2060, _CONTROL3, _INTERRUPT, _ON_STEREO_CHG);
4676                 }
4677                 regCtrl3 |= DRF_DEF(_P2060, _CONTROL3, _INTERRUPT, _ON_SYNC_CHG);
4678 
4679                 status |= writeregu008_extdeviceTargeted(pTmpGpu,
4680                           (PDACEXTERNALDEVICE)pThis, NV_P2060_CONTROL3, regCtrl3);
4681 
4682                 NV_PRINTF(LEVEL_INFO, "P2060[%d] enabled interrupt\n", iface);
4683             }
4684         }
4685     }
4686     return status;
4687 }
4688 
4689 /*
4690  * Disable FrameLock Interrupts for P2060.
4691  */
4692 static NV_STATUS
4693 gsyncDisableFrameLockInterrupt_P2060
4694 (
4695     PDACEXTERNALDEVICE pExtDev
4696 )
4697 {
4698     PDACP2060EXTERNALDEVICE pThis = (PDACP2060EXTERNALDEVICE) pExtDev;
4699     NvU8  regCtrl3;
4700     NvU32 iface;
4701     NV_STATUS status = NV_OK;
4702 
4703     // Turn Off the interrupts
4704     for (iface = 0; iface < NV_P2060_MAX_IFACES_PER_GSYNC; iface++)
4705     {
4706         if (pThis->Iface[iface].GpuInfo.gpuId != NV0000_CTRL_GPU_INVALID_ID)
4707         {
4708             OBJGPU *pTmpGpu = gpumgrGetGpuFromId(pThis->Iface[iface].GpuInfo.gpuId);
4709 
4710             NV_ASSERT(pTmpGpu);
4711 
4712             status = readregu008_extdeviceTargeted(pTmpGpu,
4713                        (PDACEXTERNALDEVICE)pThis, NV_P2060_CONTROL3, &regCtrl3);
4714 
4715             regCtrl3 &= ~DRF_DEF(_P2060, _CONTROL3, _INTERRUPT, _ON_STEREO_CHG);
4716             regCtrl3 &= ~DRF_DEF(_P2060, _CONTROL3, _INTERRUPT, _ON_SYNC_CHG);
4717 
4718             status |= writeregu008_extdeviceTargeted(pTmpGpu,
4719                       (PDACEXTERNALDEVICE)pThis, NV_P2060_CONTROL3, regCtrl3);
4720 
4721             NV_PRINTF(LEVEL_INFO, "P2060[%d] disabled interrupt\n", iface);
4722         }
4723     }
4724 
4725     return status;
4726 }
4727 
4728 /*
4729  * Enable Non-FrameLock Interrupts for P2060.
4730  */
4731 static NV_STATUS
4732 gsyncEnableNonFramelockInterrupt_P2060
4733 (
4734     OBJGPU            *pGpu,
4735     PDACEXTERNALDEVICE pExtDev
4736 )
4737 {
4738     PDACP2060EXTERNALDEVICE pThis = (PDACP2060EXTERNALDEVICE) pExtDev;
4739     NvU8  regCtrl3 = 0x00;
4740     NV_STATUS rmStatus = NV_OK;
4741 
4742     rmStatus = readregu008_extdeviceTargeted(pGpu, (PDACEXTERNALDEVICE)pThis, NV_P2060_CONTROL3, &regCtrl3);
4743 
4744     // Enable Non-Framelock interrupts on given gsync attached gpu.
4745     regCtrl3 |= DRF_DEF(_P2060, _CONTROL3, _INTERRUPT, _ON_HS_CHG);
4746     regCtrl3 |= DRF_DEF(_P2060, _CONTROL3, _INTERRUPT, _ON_RJ45_CHG);
4747 
4748     rmStatus = writeregu008_extdeviceTargeted(pGpu,(PDACEXTERNALDEVICE)pThis, NV_P2060_CONTROL3, regCtrl3);
4749 
4750     return rmStatus;
4751 }
4752 
4753 /*
4754  * Disable Non-FrameLock Interrupts for P2060.
4755  */
4756 static NV_STATUS
4757 gsyncDisableNonFramelockInterrupt_P2060
4758 (
4759     OBJGPU            *pGpu,
4760     PDACEXTERNALDEVICE pExtDev
4761 )
4762 {
4763     PDACP2060EXTERNALDEVICE pThis = (PDACP2060EXTERNALDEVICE) pExtDev;
4764     NvU8  regCtrl3 = 0x00;
4765     NV_STATUS rmStatus = NV_OK;
4766 
4767     rmStatus = readregu008_extdeviceTargeted(pGpu, (PDACEXTERNALDEVICE)pThis, NV_P2060_CONTROL3, &regCtrl3);
4768 
4769     // Disable Non-Framelock interrupts on given gsync attached gpu.
4770     regCtrl3 &= ~DRF_DEF(_P2060, _CONTROL3, _INTERRUPT, _ON_HS_CHG);
4771     regCtrl3 &= ~DRF_DEF(_P2060, _CONTROL3, _INTERRUPT, _ON_RJ45_CHG);
4772 
4773     rmStatus = writeregu008_extdeviceTargeted(pGpu,(PDACEXTERNALDEVICE)pThis, NV_P2060_CONTROL3, regCtrl3);
4774 
4775     return rmStatus;
4776 }
4777 
4778 static void
4779 gsyncResetMosaicData_P2060
4780 (
4781     NvU32 mosaicGroup,
4782     PDACP2060EXTERNALDEVICE pThis
4783 )
4784 {
4785    NvU8 i;
4786 
4787    if (!pThis)
4788    {
4789         return;
4790    }
4791 
4792    pThis->MosaicGroup[mosaicGroup].gpuTimingSource = NV0000_CTRL_GPU_INVALID_ID;
4793 
4794    for (i = 0; i < NV_P2060_MAX_MOSAIC_SLAVES; i++)
4795    {
4796       pThis->MosaicGroup[mosaicGroup].gpuTimingSlaves[i] = NV0000_CTRL_GPU_INVALID_ID;
4797    }
4798 
4799    pThis->MosaicGroup[mosaicGroup].slaveGpuCount = 0;
4800    pThis->MosaicGroup[mosaicGroup].enabledMosaic = NV_FALSE;
4801 
4802 }
4803 
4804 /*
4805  * Enable/Disable SwapRdy Connection For GPU
4806  */
4807 static NV_STATUS
4808 gsyncUpdateSwapRdyConnectionForGpu_P2060
4809 (
4810     OBJGPU *pGpu,
4811     PDACP2060EXTERNALDEVICE pThis,
4812     NvBool bEnable
4813 )
4814 {
4815     NV_STATUS rmStatus = NV_OK;
4816     NvU8 ctrl2 = 0x00;
4817 
4818     rmStatus = readregu008_extdeviceTargeted(pGpu, (PDACEXTERNALDEVICE)pThis,
4819                                              NV_P2060_CONTROL2, &ctrl2);
4820     if (rmStatus != NV_OK)
4821     {
4822         return rmStatus;
4823     }
4824 
4825     ctrl2 = FLD_SET_DRF_NUM(_P2060, _CONTROL2, _SWAP_READY, (NvU8)bEnable, ctrl2);
4826 
4827     rmStatus = writeregu008_extdeviceTargeted(pGpu, (PDACEXTERNALDEVICE)pThis,
4828                                              NV_P2060_CONTROL2, ctrl2);
4829 
4830     return rmStatus;
4831 }
4832 
4833 /*
4834  * returns true if there is a framelock master on this P2060.
4835  * otherwise returns false.
4836  */
4837 static NvBool
4838 gsyncIsFrameLockMaster_P2060
4839 (
4840     PDACP2060EXTERNALDEVICE pThis
4841 )
4842 {
4843     NvU32 iface;
4844 
4845     for (iface = 0; iface < NV_P2060_MAX_IFACES_PER_GSYNC; iface++)
4846     {
4847         if (pThis->Iface[iface].GpuInfo.gpuId != NV0000_CTRL_GPU_INVALID_ID)
4848         {
4849             OBJGPU *pTmpGpu = gpumgrGetGpuFromId(pThis->Iface[iface].GpuInfo.gpuId);
4850 
4851             NV_ASSERT(pTmpGpu);
4852 
4853             if (gsyncReadMaster_P2060(pTmpGpu, pThis))
4854             {
4855                 return NV_TRUE;
4856             }
4857         }
4858     }
4859 
4860     return NV_FALSE;
4861 }
4862 
4863 /*
4864  * returns NV_TRUE if this gsync device is framelocked.
4865  */
4866 static NvBool
4867 gsyncIsFrameLocked_P2060
4868 (
4869     PDACP2060EXTERNALDEVICE pThis
4870 )
4871 {
4872     NvU32 iface;
4873 
4874     for (iface = 0; iface < NV_P2060_MAX_IFACES_PER_GSYNC; iface++)
4875     {
4876         if (pThis->Iface[iface].GpuInfo.gpuId != NV0000_CTRL_GPU_INVALID_ID)
4877         {
4878             OBJGPU *pTmpGpu = gpumgrGetGpuFromId(pThis->Iface[iface].GpuInfo.gpuId);
4879             NV_ASSERT(pTmpGpu);
4880 
4881             if (gsyncReadMaster_P2060(pTmpGpu, pThis) ||
4882                 gsyncReadSlaves_P2060(pTmpGpu, pThis))
4883             {
4884                 return NV_TRUE;
4885             }
4886         }
4887     }
4888 
4889     return NV_FALSE;
4890 }
4891 
4892 /*
4893  * returns NV_TRUE if this gsync device is only framelock master.
4894  */
4895 static NvBool
4896 gsyncIsOnlyFrameLockMaster_P2060
4897 (
4898     PDACP2060EXTERNALDEVICE pThis
4899 )
4900 {
4901     NvU32 iface, numHeads, head;
4902     KernelDisplay  *pKernelDisplay;
4903     OBJGPU  *pGpu;
4904     NvBool bIsMaster = NV_FALSE;
4905 
4906     for (iface = 0; iface < NV_P2060_MAX_IFACES_PER_GSYNC; iface++)
4907     {
4908         if (pThis->Iface[iface].GpuInfo.gpuId != NV0000_CTRL_GPU_INVALID_ID)
4909         {
4910             pGpu  = gpumgrGetGpuFromId(pThis->Iface[iface].GpuInfo.gpuId);
4911             NV_ASSERT_OR_RETURN(pGpu, NV_FALSE);
4912 
4913             pKernelDisplay = GPU_GET_KERNEL_DISPLAY(pGpu);
4914             numHeads = kdispGetNumHeads(pKernelDisplay);
4915 
4916             for (head = 0; head < numHeads; head++)
4917             {
4918                 if (pThis->Iface[iface].Sync.Master[head])
4919                     bIsMaster = NV_TRUE;
4920 
4921                 if (pThis->Iface[iface].Sync.Slaved[head] ||
4922                     pThis->Iface[iface].Sync.LocalSlave[head])
4923                 {
4924                     return NV_FALSE;
4925                 }
4926             }
4927         }
4928     }
4929 
4930     return bIsMaster;
4931 }
4932 
4933 /*
4934  * return NV_TRUE if HW is OK with board as Framelock Master.
4935  */
4936 static NvBool
4937 gsyncIsP2060MasterBoard
4938 (
4939     OBJGPU *pGpu,
4940     PDACP2060EXTERNALDEVICE pThis
4941 )
4942 {
4943     NvU8 ctrl;
4944     NvBool bIsMasterBoard;
4945     NV_STATUS rmStatus;
4946 
4947     if (!pGpu || !GpuIsP2060Connected(pGpu, pThis))
4948     {
4949         return NV_FALSE;
4950     }
4951 
4952     rmStatus = readregu008_extdeviceTargeted(pGpu, (PDACEXTERNALDEVICE)pThis, (NvU8)NV_P2060_CONTROL, &ctrl);
4953     if (NV_OK != rmStatus)
4954     {
4955         return NV_FALSE;
4956     }
4957 
4958     // Check HW opinion on Mastership of board.
4959     bIsMasterBoard = FLD_TEST_DRF(_P2060, _CONTROL, _I_AM, _MASTER, (NvU32)ctrl);
4960 
4961     return bIsMasterBoard;
4962 }
4963 /*
4964  * return NV_TRUE if pGpu is connected to Master + TS GPU via SLI bridge
4965  */
4966 static NvBool
4967 GpuIsConnectedToMasterViaBridge
4968 (
4969     OBJGPU *pGpu,
4970     PDACP2060EXTERNALDEVICE pThis
4971 )
4972 {
4973     OBJGPU *pOtherGpu = NULL;
4974     NvU32 gpuMask, gpuIndex, tempIface;
4975     NvU32 drOut, drIn;
4976 
4977     gpumgrGetGpuAttachInfo(NULL, &gpuMask);
4978     gpuIndex = 0;
4979     while ((pOtherGpu = gpumgrGetNextGpu(gpuMask, &gpuIndex)) != NULL)
4980     {
4981         if ((pGpu == pOtherGpu) || gpumgrGetGpuLockAndDrPorts(pGpu, pOtherGpu, &drOut, &drIn) != NV_OK)
4982         {
4983             continue;
4984         }
4985 
4986         if (GetP2060GpuLocation(pOtherGpu, pThis, &tempIface) == NV_OK)
4987         {
4988             if (gsyncIsP2060MasterBoard(pOtherGpu, pThis) && GpuIsP2060Master(pOtherGpu, pThis))
4989             {
4990                 // pGpu is connected to pOtherGpu via SLI bridge.
4991                 // Both GPUs are connected to same P2060.
4992                 return NV_TRUE;
4993             }
4994         }
4995     }
4996     return NV_FALSE;
4997 }
4998 
4999 //
5000 // gsyncFrameCountTimerService_P2060()
5001 //
5002 // frame count timer callback service.
5003 // this function will read the actual gsync and gpu frame count value
5004 // and adjust the cached difference between them if required.
5005 //
5006 // this function is added to prevent any deviation of cached difference
5007 // between gpu and gsync hw frame count values from the actual.
5008 // As all the heads are framelocked, it is expected that cached
5009 // framecount value to be same on the master as well as slave
5010 // system. But during experiment, it is found that reading the hw gsync and
5011 // gpu frame count values immediately after the test signal is sent/received
5012 // may lead to inconsistent cached difference. Therefore the difference is
5013 // reverified after FRAME_COUNT_TIMER_INTERVAL period.
5014 //
5015 static
5016 NV_STATUS gsyncFrameCountTimerService_P2060
5017 (
5018     OBJGPU *pGpu,
5019     OBJTMR *pTmr,
5020     void *pComponent
5021 )
5022 {
5023     PDACP2060EXTERNALDEVICE pThis = NULL;
5024     NV_STATUS status;
5025     OBJGSYNC *pGsync = NULL;
5026 
5027     pGsync = gsyncmgrGetGsync(pGpu);
5028 
5029     NV_ASSERT_OR_RETURN((pGsync && pGsync->pExtDev), NV_ERR_INVALID_DEVICE);
5030 
5031     pThis = (PDACP2060EXTERNALDEVICE)pGsync->pExtDev;
5032 
5033     // disable the timer callback
5034     status = tmrCancelCallback(pTmr, (void *)&pThis->FrameCountData);
5035 
5036     if (status != NV_OK)
5037     {
5038         return status;
5039     }
5040 
5041     //
5042     // read the gsync and gpu frame count values.Cache the difference between them.
5043     //
5044     status = gsyncUpdateFrameCount_P2060(pThis, pGpu);
5045 
5046     return status;
5047 }
5048 /*
5049  * Reset the FrameCount Data structure.
5050  */
5051 //
5052 // gsyncResetFrameCountData_P2060()
5053 //
5054 // this function resets the FrameCountDate structure.
5055 //
5056 static NV_STATUS
5057 gsyncResetFrameCountData_P2060
5058 (
5059     OBJGPU *pGpu,
5060     PDACP2060EXTERNALDEVICE pThis
5061 )
5062 {
5063 
5064     NvU8 regCtrl3;
5065     NV_STATUS rmStatus;
5066 
5067     if (!pThis)
5068     {
5069         return NV_ERR_INVALID_ARGUMENT;
5070     }
5071 
5072     pThis->FrameCountData.totalFrameCount               = 0;
5073     pThis->FrameCountData.currentFrameCount             = 0;
5074     pThis->FrameCountData.initialDifference             = 0;
5075     pThis->FrameCountData.numberOfRollbacks             = 0;
5076     pThis->FrameCountData.previousFrameCount            = 0;
5077     pThis->FrameCountData.lastFrameCounterQueryTime     = 0;
5078     pThis->FrameCountData.bReCheck                      = 0;
5079     pThis->FrameCountData.vActive                       = 0;
5080     pThis->FrameCountData.isFrmCmpMatchIntMasterEnabled = NV_FALSE;
5081     pThis->FrameCountData.enableFrmCmpMatchIntSlave     = NV_FALSE;
5082     pThis->FrameCountData.head                          = NV_P2060_MAX_HEADS_PER_GPU;
5083     pThis->FrameCountData.iface                         = NV_P2060_MAX_IFACES_PER_GSYNC;
5084 
5085     // disable frame count match interrupt
5086     rmStatus = readregu008_extdeviceTargeted(pGpu, (PDACEXTERNALDEVICE)pThis,
5087                                              NV_P2060_CONTROL3, &regCtrl3);
5088     regCtrl3 &= ~DRF_DEF(_P2060, _CONTROL3, _INTERRUPT, _ON_FRAME_MATCH);
5089     rmStatus |= writeregu008_extdeviceTargeted(pGpu, (PDACEXTERNALDEVICE)pThis,
5090                                                NV_P2060_CONTROL3, regCtrl3);
5091     return rmStatus;
5092 }
5093 
5094 //
5095 // gsyncUpdateFrameCount_P2060()
5096 //
5097 // For all heads in a framelocked state gpu framecount is equal.This also
5098 // implies for gsync frame count.i.e. gsync frame count = (gpu frame count + difference)
5099 // Therefore to reduce the i2c reads to access gsync frame count,
5100 // (gpu frame count + difference) can be returned. This is done by caching the
5101 // difference between the gpu and gsync framecount.
5102 //
5103 // FrameCountTimerService (1 second callback) is also enabled here to verify
5104 // the cache difference.
5105 //
5106 static NV_STATUS
5107 gsyncUpdateFrameCount_P2060
5108 (
5109     PDACP2060EXTERNALDEVICE pThis,
5110     OBJGPU *pGpu
5111 )
5112 {
5113     KernelDisplay *pKernelDisplay = GPU_GET_KERNEL_DISPLAY(pGpu);
5114     RM_API *pRmApi;
5115     NvU32 hClient;
5116     NvU32 hSubdevice;
5117     NvU8  FrameCountLow;
5118     NvU8  FrameCountMid;
5119     NvU8  FrameCountHigh;
5120     NvU8  regCtrl3;
5121     NvU32 rawGsyncFrameCount;
5122     NvU32 iface;
5123     NvU32 head = 0;
5124     NvU32 numHeads;
5125     NvU32 modGsyncFrameCount;
5126     NvU32 lineCount;
5127     NvU32 frameCount;
5128     RMTIMEOUT timeout;
5129     NvU32 safeRegionUpperLimit;
5130     NvU32 safeRegionLowerLimit;
5131     NV_STATUS rmStatus = NV_OK;
5132     NV2080_CTRL_INTERNAL_GSYNC_GET_VERTICAL_ACTIVE_LINES_PARAMS ctrlParams = {0};
5133 
5134     numHeads = kdispGetNumHeads(pKernelDisplay);
5135 
5136     // get any framelocked head
5137     for (iface = 0; iface < NV_P2060_MAX_IFACES_PER_GSYNC; iface++)
5138     {
5139         if (pThis->Iface[iface].GpuInfo.gpuId == NV0000_CTRL_GPU_INVALID_ID)
5140         {
5141             continue;
5142         }
5143 
5144         for (head = 0; head < numHeads; head++)
5145         {
5146             if (pThis->Iface[iface].Sync.Master[head] ||
5147                 pThis->Iface[iface].Sync.Slaved[head] ||
5148                 pThis->Iface[iface].Sync.LocalSlave[head])
5149             {
5150                 // Update pThis->FrameCountData with iface and head
5151                 pThis->FrameCountData.iface = iface;
5152                 pThis->FrameCountData.head  = head;
5153 
5154                 // Get out of for loop
5155                 iface = NV_P2060_MAX_IFACES_PER_GSYNC;
5156                 break;
5157             }
5158         }
5159     }
5160 
5161     if (head == numHeads)
5162     {
5163         return NV_ERR_GENERIC;
5164     }
5165 
5166     pGpu = gpumgrGetGpuFromId(pThis->Iface[pThis->FrameCountData.iface].GpuInfo.gpuId);
5167     NV_ASSERT_OR_RETURN(pGpu, NV_ERR_INVALID_DEVICE);
5168 
5169     pRmApi     = GPU_GET_PHYSICAL_RMAPI(pGpu);
5170     hClient    = pGpu->hInternalClient;
5171     hSubdevice = pGpu->hInternalSubdevice;
5172 
5173     // Re-fetch pDisp as pGpu might have changed.
5174     pKernelDisplay = GPU_GET_KERNEL_DISPLAY(pGpu);
5175     NV_ASSERT_OR_RETURN(pKernelDisplay != NULL, NV_ERR_INVALID_DEVICE);
5176     NV_ASSERT_OR_RETURN(head < kdispGetNumHeads(pKernelDisplay), NV_ERR_INVALID_DEVICE);
5177 
5178     ctrlParams.headIdx = head;
5179 
5180     rmStatus = pRmApi->Control(pRmApi, hClient, hSubdevice,
5181                                NV2080_CTRL_CMD_INTERNAL_GSYNC_GET_VERTICAL_ACTIVE_LINES,
5182                                &ctrlParams, sizeof(ctrlParams));
5183 
5184     if (rmStatus != NV_OK)
5185     {
5186         return rmStatus;
5187     }
5188 
5189     pThis->FrameCountData.vActive = ctrlParams.vActiveLines;
5190 
5191     //
5192     // To read Gpu framecount, line count should be in between 5-70% of VVisible.
5193     //
5194     safeRegionUpperLimit = (pThis->FrameCountData.vActive * 7) / 10;
5195     safeRegionLowerLimit = pThis->FrameCountData.vActive / 20;
5196 
5197     // Read the GPU frame count and line count
5198     rmStatus = kdispReadRgLineCountAndFrameCount_HAL(pGpu, pKernelDisplay,
5199                    pThis->FrameCountData.head, &lineCount, &frameCount);
5200     if (rmStatus != NV_OK)
5201     {
5202         NV_PRINTF(LEVEL_ERROR, "Failed to read RG_DPCA.\n");
5203         return rmStatus;
5204     }
5205 
5206     //
5207     // Wait for a safe region i.e. 5-70 percent of the VActive. Then read the
5208     // gsync framecount. This is done to ensure that both gsync and gpu
5209     // registers are read in the safe zone otherwise there will be -/+ 1
5210     // frame inconsistency ( if read during the transition from frame N to
5211     // frame N + 1 i.e linecount > vActive)
5212     //
5213     if ((lineCount >= safeRegionUpperLimit) || (lineCount <= safeRegionLowerLimit))
5214     {
5215         //
5216         // timeout of one frameTime(in nano seconds), to avoid going into an infinite
5217         // loop in case linecount is stuck to some value.
5218         //
5219         gpuSetTimeout(pGpu, (pThis->FrameCountData.frameTime * 1000), &timeout, 0);
5220 
5221         // Read the linecount until we are in the safe region i.e taken as 5%-70% of VActive.
5222         while (((lineCount >= safeRegionUpperLimit) || (lineCount <= safeRegionLowerLimit)) &&
5223                 (gpuCheckTimeout(pGpu, &timeout) != NV_ERR_TIMEOUT))
5224         {
5225             rmStatus = kdispReadRgLineCountAndFrameCount_HAL(pGpu, pKernelDisplay,
5226                            pThis->FrameCountData.head, &lineCount, &frameCount);
5227             if (rmStatus != NV_OK)
5228             {
5229                 NV_PRINTF(LEVEL_ERROR, "Failed to read RG_DPCA.\n");
5230                 return rmStatus;
5231             }
5232         }
5233 
5234         if ((lineCount >= safeRegionUpperLimit) || (lineCount <= safeRegionLowerLimit))
5235         {
5236             return NV_ERR_TIMEOUT;
5237         }
5238     }
5239 
5240     rmStatus |= readregu008_extdeviceTargeted(pGpu, (PDACEXTERNALDEVICE) pThis, (NvU8)NV_P2060_FRAMECNTR_LOW,  &FrameCountLow);
5241     rmStatus |= readregu008_extdeviceTargeted(pGpu, (PDACEXTERNALDEVICE) pThis, (NvU8)NV_P2060_FRAMECNTR_MID,  &FrameCountMid);
5242     rmStatus |= readregu008_extdeviceTargeted(pGpu, (PDACEXTERNALDEVICE) pThis, (NvU8)NV_P2060_FRAMECNTR_HIGH, &FrameCountHigh);
5243 
5244     if (rmStatus != NV_OK)
5245     {
5246         return rmStatus;
5247     }
5248 
5249      rawGsyncFrameCount = (
5250                           ((((NvU32)FrameCountHigh)    & DRF_MASK(NV_P2060_FRAMECNTR_HIGH_VAL))<< 16 ) |
5251                           ((((NvU32)FrameCountMid)     & DRF_MASK(NV_P2060_FRAMECNTR_MID_VAL)) << 8 )  |
5252                           ((((NvU32)FrameCountLow)     & DRF_MASK(NV_P2060_FRAMECNTR_LOW_VAL))));
5253 
5254     pThis->FrameCountData.currentFrameCount = frameCount;
5255 
5256     //
5257     // Gsync frame count is 24 bit register whereas Gpu frame count register is 16 bit.
5258     // Therefore number of rollovers of Gpu frame count register is required.
5259     // Else gsync frame count and (gpu frame count + difference) can be off by (2^16*N).
5260     // where maximum value of N can be 256. << gsync frame count 2^24 = 256* 2^16.
5261     //
5262     pThis->FrameCountData.numberOfRollbacks  = gsyncGetNumberOfGpuFrameCountRollbacks_P2060(rawGsyncFrameCount, 0, 256);
5263     modGsyncFrameCount                       = rawGsyncFrameCount - (pThis->FrameCountData.numberOfRollbacks * (NV_P2060_MAX_GPU_FRAME_COUNT + 1));
5264     pThis->FrameCountData.initialDifference  = modGsyncFrameCount - pThis->FrameCountData.currentFrameCount;
5265     pThis->FrameCountData.previousFrameCount = 0;
5266 
5267     pThis->FrameCountData.totalFrameCount  =  pThis->FrameCountData.currentFrameCount +
5268                                               pThis->FrameCountData.initialDifference +
5269                                               pThis->FrameCountData.numberOfRollbacks * (NV_P2060_MAX_GPU_FRAME_COUNT + 1);
5270 
5271     if (pThis->FrameCountData.enableFrmCmpMatchIntSlave)
5272     {
5273         pThis->FrameCountData.enableFrmCmpMatchIntSlave = NV_FALSE;
5274 
5275         // enable frame count match interrupt
5276         rmStatus |= readregu008_extdeviceTargeted(pGpu, (PDACEXTERNALDEVICE)pThis,
5277                                                   NV_P2060_CONTROL3,  &regCtrl3);
5278 
5279         if (rmStatus == NV_OK)
5280         {
5281             regCtrl3 |= DRF_DEF(_P2060, _CONTROL3, _INTERRUPT, _ON_FRAME_MATCH);
5282             rmStatus = writeregu008_extdeviceTargeted(pGpu, (PDACEXTERNALDEVICE)pThis,
5283                                                       NV_P2060_CONTROL3, regCtrl3);
5284         }
5285     }
5286 
5287     //
5288     // Schedule 1 second timer callback, to verify initialDifference.
5289     //
5290     if (pThis->FrameCountData.bReCheck)
5291     {
5292 
5293         NV_STATUS status = NV_OK;
5294         OBJTMR *pTmr  = GPU_GET_TIMER(pGpu);
5295 
5296         status = tmrScheduleCallbackRel(
5297                  pTmr,
5298                  gsyncFrameCountTimerService_P2060,
5299                  (void *)&pThis->FrameCountData,
5300                  (NV_P2060_FRAME_COUNT_TIMER_INTERVAL / 5),
5301                  TMR_FLAG_RECUR,
5302                  0);
5303 
5304         if (status == NV_OK)
5305         {
5306             pThis->FrameCountData.bReCheck = 0;
5307         }
5308 
5309     }
5310     return rmStatus;
5311 }
5312 
5313 //
5314 // gsyncGetNumberOfGpuFrameCountRollbacks_P2060
5315 //
5316 // Get N where N is the maximum value for gsync framecount > N*(Gpu frame count)
5317 //
5318 static NvU32
5319 gsyncGetNumberOfGpuFrameCountRollbacks_P2060
5320 (
5321     NvU32 FrameCount,
5322     NvU32 low,
5323     NvU32 high
5324 )
5325 {
5326     NvU32 mid = (low + high) / 2;
5327 
5328     if (FrameCount >= (high * NV_P2060_MAX_GPU_FRAME_COUNT))
5329     {
5330         return high;
5331     }
5332     else if ((FrameCount >= (mid * NV_P2060_MAX_GPU_FRAME_COUNT)) &&
5333              (FrameCount < ((mid+1) * NV_P2060_MAX_GPU_FRAME_COUNT)))
5334     {
5335         return mid;
5336     }
5337     else if ((FrameCount > (NV_P2060_MAX_GPU_FRAME_COUNT * low)) && (FrameCount < (mid * NV_P2060_MAX_GPU_FRAME_COUNT)))
5338     {
5339         return gsyncGetNumberOfGpuFrameCountRollbacks_P2060(FrameCount, low, mid);
5340     }
5341     else if ((FrameCount > (NV_P2060_MAX_GPU_FRAME_COUNT * mid)) && (FrameCount < (high * NV_P2060_MAX_GPU_FRAME_COUNT)))
5342     {
5343         return gsyncGetNumberOfGpuFrameCountRollbacks_P2060(FrameCount, mid+1, high);
5344     }
5345     else
5346     {
5347         return 0;
5348     }
5349 }
5350 
5351 // Return NV_TRUE if the current Qsync revision supports large sync skew
5352 NvBool
5353 gsyncSupportsLargeSyncSkew_P2060
5354 (
5355     DACEXTERNALDEVICE *pExtdev
5356 )
5357 {
5358     if (pExtdev->deviceId == DAC_EXTERNAL_DEVICE_P2061)
5359     {
5360         // All p2061 revisions support sync skew > 1.
5361         return NV_TRUE;
5362     }
5363     else
5364     {
5365         //
5366         // P2060 FPGA (revision < 3) does not support SyncSkew more than 1 us(HW limitation).
5367         // If set to value more than 1 us, we observe screen flashing. Refer bug 1058215
5368         //
5369         NV_ASSERT(pExtdev->deviceId == DAC_EXTERNAL_DEVICE_P2060);
5370         return (pExtdev->deviceRev >= DAC_EXTERNAL_DEVICE_REV_3);
5371     }
5372 }
5373 
5374 // Return NV_TRUE if the current Qsync revision needs the Swapbarrier WAR on master
5375 static NvBool
5376 needsMasterBarrierWar
5377 (
5378     PDACEXTERNALDEVICE pExtdev
5379 )
5380 {
5381     if (pExtdev->deviceId == DAC_EXTERNAL_DEVICE_P2061)
5382     {
5383         // All p2061 revisions do not need the WAR.
5384         return NV_FALSE;
5385     }
5386     else
5387     {
5388         //
5389         // P2060 Fpga (revision <= 5) needs to have the swapbarrier set on framelock masters
5390         // to drive (pull up) the swap_rdy line of the whole framelock setup.
5391         // This is a behaviour with unwanted side effects which needs drivers wars
5392         // for certain configs.
5393         //
5394         NV_ASSERT(pExtdev->deviceId == DAC_EXTERNAL_DEVICE_P2060);
5395         return (pExtdev->deviceRev <= DAC_EXTERNAL_DEVICE_REV_5);
5396     }
5397 }
5398 
5399 // Return NV_TRUE if the Qsync revision is not compatible with GPU
5400 static NvBool
5401 isFirmwareRevMismatch
5402 (
5403     OBJGPU *pGpu,
5404     DAC_EXTERNAL_DEVICE_REVS currentRev
5405 )
5406 {
5407     if (IsKEPLER(pGpu))
5408     {
5409         return ((currentRev < NV_P2060_MIN_REV) ||
5410                 (currentRev == DAC_EXTERNAL_DEVICE_REV_5) ||
5411                 (currentRev == DAC_EXTERNAL_DEVICE_REV_6));
5412     }
5413     else if (IsMAXWELL(pGpu))
5414     {
5415         return (currentRev < NV_P2060_MIN_REV);
5416     }
5417     else
5418     {
5419         return NV_FALSE;
5420     }
5421 }
5422 
5423 /*
5424  * Nvlink and QSync can both transmit inter-GPU Display sync signals.
5425  * Contention in these signals is observed on some boards, if both Nvlink and
5426  * QSync are present between the boards.
5427  *
5428  * Returns TRUE if contention in transmission of sync signals possible on the
5429  * given GPU board if both mediums (QSync and Nvlink) are present between GPUs
5430  */
5431 
5432 static NvBool
5433 isBoardWithNvlinkQsyncContention
5434 (
5435     POBJGPU pGpu
5436 )
5437 {
5438     NvU16 devIds[] = {
5439         0x2230,     // Nvidia RTX A6000 (PG133 SKU 500)
5440         0x2231,     // Nvidia RTX A5000 (PG132 SKU 500)
5441         0x2233      // Nvidia RTX A5500 (PG132 SKU 520)
5442     };
5443 
5444     NvU16 thisDevId = (NvU16)(((pGpu->idInfo.PCIDeviceID) >> 16) & 0x0000FFFF);
5445     NvU32 i;
5446 
5447     for (i=0; i < (sizeof(devIds)/sizeof(devIds[0])); i++)
5448     {
5449         if (thisDevId == devIds[i])
5450         {
5451             return NV_TRUE;
5452         }
5453     }
5454 
5455     return NV_FALSE;
5456 }
5457