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