1 /*
2  * SPDX-FileCopyrightText: Copyright (c) 2014 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 "nvkms-framelock.h"
25 #include "nvkms-dpy.h"
26 #include "nvkms-utils.h"
27 #include "nvkms-evo.h"
28 #include "nvkms-rm.h"
29 #include "nvkms-rmapi.h"
30 
31 #include "nvkms-private.h" /* nvSendDpyAttributeChangedEventEvo() */
32 
33 #include <class/cl30f1.h>
34 #include <ctrl/ctrl0000/ctrl0000gsync.h> /* NV0000_CTRL_CMD_GSYNC_GET_ATTACHED_IDS */
35 #include <ctrl/ctrl30f1.h>
36 #include "nvos.h"
37 
38 static NvBool FrameLockUseHouseSyncGetSupport(NVFrameLockEvoPtr pFrameLockEvo,
39                                               NvU32 *val);
40 static NvBool FrameLockSetPolarity(
41     NVFrameLockEvoPtr pFrameLockEvo,
42     enum NvKmsFrameLockAttributePolarityValue val);
43 static NvBool HouseSyncOutputModeUsable(const NVFrameLockEvoRec *pFrameLockEvo);
44 
45 /*!
46  * Handle framelock sync gain/loss events triggered from resman.
47  *
48  * When RM sends an event notification that's handled by FrameLockEvent,
49  * that function schedules a timer to service that event notification.
50  * These timers are serviced out of order, though; we may receive a
51  * SYNC_LOSS event followed by a SYNC_GAIN event, but our scheduled
52  * callbacks may be called in the reverse order.
53  *
54  * Since we can't trust that events were serviced in order, this function
55  * responds to every sync gain or loss event by querying the actual
56  * sync status across all GPUs from RM and updating our cached sync status
57  * and notifying clients if necessary.
58  */
59 static void
60 FrameLockHandleSyncEvent(void *dataPtr, NvU32 dataU32)
61 {
62     NVDispEvoPtr pDispEvo = dataPtr;
63     NVFrameLockEvoPtr pFrameLockEvo = pDispEvo->pFrameLockEvo;
64     NvU32 connectorIndex = pDispEvo->framelock.connectorIndex;
65     NvBool syncReadyCurrent = FALSE;
66     NV30F1_CTRL_GSYNC_GET_STATUS_SYNC_PARAMS statusParams = { 0 };
67 
68     statusParams.gpuId = nvGpuIdOfDispEvo(pDispEvo);
69 
70     if (nvRmApiControl(nvEvoGlobal.clientHandle,
71                        pFrameLockEvo->device,
72                        NV30F1_CTRL_CMD_GSYNC_GET_STATUS_SYNC,
73                        &statusParams,
74                        sizeof(statusParams)) != NVOS_STATUS_SUCCESS) {
75         nvAssert(!"Failed to query gsync status after event");
76     } else {
77         if (statusParams.bTiming && statusParams.bSyncReady) {
78             syncReadyCurrent = TRUE;
79         }
80     }
81 
82     // Update syncReadyGpuMask for consistency with non-NVKMS path, although
83     // it is currently unused.
84     if (syncReadyCurrent) {
85         pFrameLockEvo->syncReadyGpuMask |= (1 << connectorIndex);
86     } else {
87         pFrameLockEvo->syncReadyGpuMask &= ~(1 << connectorIndex);
88     }
89 
90     if (syncReadyCurrent != pFrameLockEvo->syncReadyLast) {
91         pFrameLockEvo->syncReadyLast = syncReadyCurrent;
92         nvSendFrameLockAttributeChangedEventEvo(
93             pFrameLockEvo,
94             NV_KMS_FRAMELOCK_ATTRIBUTE_SYNC_READY,
95             pFrameLockEvo->syncReadyLast);
96     }
97 }
98 
99 /*!
100  * Receive framelock events from resman.
101  *
102  * This function is registered as a kernel callback function from
103  * resman.
104  *
105  * However, it is called with resman's context (resman locks held, etc).
106  * Schedule deferred work, so that we can process the event without resman's
107  * encumbrances.
108  */
109 static void FrameLockEvent(void *arg, void *pEventDataVoid,
110                            NvU32 hEvent,
111                            NvU32 Data, NV_STATUS Status)
112 {
113     static nvkms_timer_proc_t *callbackTable[] = {
114         [NV30F1_GSYNC_NOTIFIERS_SYNC_LOSS(0)] = FrameLockHandleSyncEvent,
115         [NV30F1_GSYNC_NOTIFIERS_SYNC_LOSS(1)] = FrameLockHandleSyncEvent,
116         [NV30F1_GSYNC_NOTIFIERS_SYNC_LOSS(2)] = FrameLockHandleSyncEvent,
117         [NV30F1_GSYNC_NOTIFIERS_SYNC_LOSS(3)] = FrameLockHandleSyncEvent,
118 
119         [NV30F1_GSYNC_NOTIFIERS_SYNC_GAIN(0)] = FrameLockHandleSyncEvent,
120         [NV30F1_GSYNC_NOTIFIERS_SYNC_GAIN(1)] = FrameLockHandleSyncEvent,
121         [NV30F1_GSYNC_NOTIFIERS_SYNC_GAIN(2)] = FrameLockHandleSyncEvent,
122         [NV30F1_GSYNC_NOTIFIERS_SYNC_GAIN(3)] = FrameLockHandleSyncEvent,
123     };
124 
125     const NvNotification *pNotifyData = pEventDataVoid;
126     NvU32 notifyIndex;
127 
128     /* callbackTable[] assumes at most four connectors per gsync */
129     ct_assert(NV30F1_GSYNC_CONNECTOR_COUNT == 4);
130 
131     if (pNotifyData == NULL) {
132         nvAssert(!"Invalid pNotifyData from resman");
133         return;
134     }
135 
136     notifyIndex = pNotifyData->info32;
137 
138     if ((notifyIndex >= ARRAY_LEN(callbackTable)) ||
139         (callbackTable[notifyIndex] == NULL)) {
140         nvAssert(!"Invalid notifyIndex from resman");
141         return;
142     }
143 
144     (void) nvkms_alloc_timer_with_ref_ptr(
145         callbackTable[notifyIndex], /* callback */
146         arg, /* argument (this is a ref_ptr to a pDispEvo) */
147         0,   /* unused */
148         0);  /* timeout (i.e., service as soon as possible) */
149 }
150 
151 /*!
152  * Free all events and handles allocated in FrameLockCreateEvents().
153  */
154 static void FrameLockDestroyEvents(NVDispEvoPtr pDispEvo)
155 {
156     NVFrameLockEvoPtr pFrameLockEvo = pDispEvo->pFrameLockEvo;
157     unsigned int i;
158 
159     if (pFrameLockEvo == NULL) {
160         return;
161     }
162 
163     for (i = 0; i < NV_FRAMELOCK_NUM_EVENTS; i++) {
164         if (pDispEvo->framelock.gsyncEvent[i].handle) {
165             nvRmApiFree(nvEvoGlobal.clientHandle,
166                         pFrameLockEvo->device,
167                         pDispEvo->framelock.gsyncEvent[i].handle);
168             nvFreeUnixRmHandle(&pDispEvo->pDevEvo->handleAllocator,
169                                pDispEvo->framelock.gsyncEvent[i].handle);
170             pDispEvo->framelock.gsyncEvent[i].handle = 0;
171         }
172     }
173 }
174 
175 /*!
176  * Allocate and configure all events and handles associated with them.
177  */
178 static NvBool FrameLockCreateEvents(NVDispEvoPtr pDispEvo)
179 {
180     NVDevEvoPtr pDevEvo = pDispEvo->pDevEvo;
181     NVFrameLockEvoPtr pFrameLockEvo = pDispEvo->pFrameLockEvo;
182     const NvU32 connectorIndex = pDispEvo->framelock.connectorIndex;
183     unsigned int i;
184 
185     if (pDispEvo->pFrameLockEvo == NULL) {
186         return TRUE;
187     }
188 
189     nvAssert(connectorIndex < NV30F1_GSYNC_CONNECTOR_COUNT);
190 
191     /* We should only get here on hardware that has per-connector events */
192     nvAssert(!(pFrameLockEvo->caps &
193         NV30F1_CTRL_GSYNC_GET_CAPS_CAP_FLAGS_ONLY_PRIMARY_CONNECTOR_EVENT));
194 
195     for (i = 0; i < NV_FRAMELOCK_NUM_EVENTS; i++) {
196         NvU32 notifier;
197         NvBool ret;
198 
199         switch (i) {
200             case NV_FRAMELOCK_SYNC_LOSS:
201                 notifier = NV30F1_GSYNC_NOTIFIERS_SYNC_LOSS(connectorIndex);
202                 break;
203             case NV_FRAMELOCK_SYNC_GAIN:
204                 notifier = NV30F1_GSYNC_NOTIFIERS_SYNC_GAIN(connectorIndex);
205                 break;
206             default:
207                 nvAssert(!"Unknown gsync event index");
208                 continue;
209         }
210 
211         pDispEvo->framelock.gsyncEvent[i].handle =
212             nvGenerateUnixRmHandle(&pDevEvo->handleAllocator);
213 
214         ret = TRUE;
215 
216         if (!nvRmRegisterCallback(pDevEvo,
217                                   &pDispEvo->framelock.gsyncEvent[i].callback,
218                                   pDispEvo->ref_ptr,
219                                   pFrameLockEvo->device,
220                                   pDispEvo->framelock.gsyncEvent[i].handle,
221                                   FrameLockEvent,
222                                   notifier)) {
223             ret = FALSE;
224         }
225 
226         if (!ret) {
227             nvEvoLogDispDebug(pDispEvo, EVO_LOG_ERROR,
228                               "Failed to register for framelock event %d", i);
229             nvFreeUnixRmHandle(&pDevEvo->handleAllocator,
230                                pDispEvo->framelock.gsyncEvent[i].handle);
231             pDispEvo->framelock.gsyncEvent[i].handle = 0;
232             goto noEvents;
233         }
234     }
235 
236     return TRUE;
237 
238 noEvents:
239 
240     nvEvoLogDisp(pDispEvo, EVO_LOG_WARN,
241                  "Failed to register for framelock events");
242 
243     FrameLockDestroyEvents(pDispEvo);
244 
245     return FALSE;
246 }
247 
248 /*!
249  * Bind a pSubDev to a pFrameLock.
250  */
251 static void BindGpuToFrameLock(NVDevEvoPtr pDevEvo,
252                                const NvU32 gpuId,
253                                NVFrameLockEvoPtr pFrameLockEvo,
254                                NvU32 connectorIndex)
255 {
256     NVDispEvoPtr pDispEvo;
257     unsigned int dispIndex;
258 
259     if (pFrameLockEvo->nGpuIds >= ARRAY_LEN(pFrameLockEvo->gpuIds)) {
260         return;
261     }
262 
263     pFrameLockEvo->gpuIds[pFrameLockEvo->nGpuIds] = gpuId;
264     pFrameLockEvo->nGpuIds++;
265 
266     /*
267      * If a disp exists for this subdevice, wire it up.
268      * Note that this should not happen for SLI non-display-owners.
269      */
270 
271     FOR_ALL_EVO_DISPLAYS(pDispEvo, dispIndex, pDevEvo) {
272 
273         if (nvGpuIdOfDispEvo(pDispEvo) != gpuId) {
274             continue;
275         }
276 
277         pDispEvo->pFrameLockEvo = pFrameLockEvo;
278 
279         pDispEvo->framelock.connectorIndex = connectorIndex;
280 
281         pFrameLockEvo->connectedGpuMask |= (1 << connectorIndex);
282         pFrameLockEvo->syncReadyGpuMask &= ~(1 << connectorIndex);
283 
284         /* Set up stereo synchronization events */
285         FrameLockCreateEvents(pDispEvo);
286     }
287 }
288 
289 /*!
290  * Break the binding of pSubDev and pDisp to pFrameLock that we
291  * created in BindGpuToFrameLock().
292  */
293 static void UnbindGpuFromFrameLock(NVDevEvoPtr pDevEvo,
294                                    const NvU32 gpuId,
295                                    NVFrameLockEvoPtr pFrameLockEvo)
296 {
297     NVDispEvoPtr pDispEvo;
298     unsigned int dispIndex;
299     unsigned int gpu, j;
300 
301     for (gpu = 0; gpu < pFrameLockEvo->nGpuIds; gpu++) {
302         if (pFrameLockEvo->gpuIds[gpu] == gpuId) {
303             break;
304         }
305     }
306 
307     if (gpu == pFrameLockEvo->nGpuIds) {
308         return;
309     }
310 
311     FOR_ALL_EVO_DISPLAYS(pDispEvo, dispIndex, pDevEvo) {
312 
313         const NvU32 connectorIndex = pDispEvo->framelock.connectorIndex;
314 
315         if (nvGpuIdOfDispEvo(pDispEvo) != gpuId) {
316             continue;
317         }
318 
319         FrameLockDestroyEvents(pDispEvo);
320 
321         pFrameLockEvo->connectedGpuMask &= ~(1 << connectorIndex);
322         pFrameLockEvo->syncReadyGpuMask &= ~(1 << connectorIndex);
323 
324         pDispEvo->framelock.connectorIndex = 0;
325 
326         pDispEvo->pFrameLockEvo = NULL;
327     }
328 
329     for (j = gpu; j < (pFrameLockEvo->nGpuIds - 1); j++) {
330         pFrameLockEvo->gpuIds[j] = pFrameLockEvo->gpuIds[j+1];
331     }
332 
333     pFrameLockEvo->nGpuIds--;
334 }
335 
336 /*!
337  * Find the NVFrameLockEvoPtr with the specified gsyncId.
338  */
339 static NVFrameLockEvoPtr FindFrameLock(NvU32 gsyncId)
340 {
341     NVFrameLockEvoPtr pFrameLockEvo;
342 
343     FOR_ALL_EVO_FRAMELOCKS(pFrameLockEvo) {
344         if (pFrameLockEvo->gsyncId == gsyncId) {
345             return pFrameLockEvo;
346         }
347     }
348 
349     return NULL;
350 }
351 
352 /*!
353  * Return whether the NVDevEvoPtr contains a GPU with the specified gpuId.
354  */
355 static NvBool GpuIdInDevEvo(NVDevEvoPtr pDevEvo, NvU32 gpuId)
356 {
357     NvU32 sd;
358 
359     for (sd = 0; sd < pDevEvo->numSubDevices; sd++) {
360         if (pDevEvo->pSubDevices[sd]->gpuId == gpuId) {
361             return TRUE;
362         }
363     }
364 
365     return FALSE;
366 }
367 
368 /*!
369  * Free the pFrameLock object.
370  */
371 static void FreeFrameLockEvo(NVDevEvoPtr pDevEvo,
372                              NVFrameLockEvoPtr pFrameLockEvo)
373 {
374     if (pFrameLockEvo == NULL) {
375         return;
376     }
377 
378     if (pFrameLockEvo->device != 0) {
379         nvRmApiFree(nvEvoGlobal.clientHandle,
380                     nvEvoGlobal.clientHandle,
381                     pFrameLockEvo->device);
382 
383         nvFreeUnixRmHandle(&pDevEvo->handleAllocator,
384                            pFrameLockEvo->device);
385         pFrameLockEvo->device = 0;
386     }
387 
388     nvAssert(pFrameLockEvo->nGpuIds == 0);
389 
390     nvListDel(&pFrameLockEvo->frameLockListEntry);
391 
392     nvFree(pFrameLockEvo);
393 }
394 
395 /*!
396  * Allocate and initialize a new pFrameLock object.
397  */
398 static NVFrameLockEvoPtr AllocFrameLockEvo(NVDevEvoPtr pDevEvo,
399                                            int instance, NvU32 gsyncId)
400 {
401     NV30F1_ALLOC_PARAMETERS gsyncAllocParams = { 0 };
402     NV30F1_CTRL_GSYNC_GET_CAPS_PARAMS gsyncGetCapsParams = { 0 };
403     NVFrameLockEvoPtr pFrameLockEvo;
404 
405     nvAssert(FindFrameLock(gsyncId) == NULL);
406 
407     pFrameLockEvo = nvCalloc(1, sizeof(NVFrameLockEvoRec));
408 
409     if (pFrameLockEvo == NULL) {
410         return NULL;
411     }
412 
413     nvListInit(&pFrameLockEvo->frameLockListEntry);
414 
415     pFrameLockEvo->device = nvGenerateUnixRmHandle(&pDevEvo->handleAllocator);
416 
417     gsyncAllocParams.gsyncInstance = instance;
418 
419     /* allocate a framelock object for the framelock device */
420     if (nvRmApiAlloc(nvEvoGlobal.clientHandle,
421                      nvEvoGlobal.clientHandle,
422                      pFrameLockEvo->device,
423                      NV30_GSYNC,
424                      &gsyncAllocParams) != NVOS_STATUS_SUCCESS) {
425         pFrameLockEvo->device = 0;
426         goto fail;
427     }
428 
429     /* Store unique frame lock device ID */
430     pFrameLockEvo->gsyncId = gsyncId;
431     pFrameLockEvo->houseSyncUseable = 0;
432     pFrameLockEvo->nGpuIds = 0;
433 
434     /* Initialize the state for the framelock board */
435     pFrameLockEvo->polarity = NV_KMS_FRAMELOCK_ATTRIBUTE_POLARITY_FALLING_EDGE;
436     pFrameLockEvo->syncDelay = 0;
437     pFrameLockEvo->syncInterval = 0;
438     pFrameLockEvo->videoMode =
439         NV_KMS_FRAMELOCK_ATTRIBUTE_VIDEO_MODE_COMPOSITE_AUTO;
440     pFrameLockEvo->testMode = FALSE;
441     pFrameLockEvo->houseSyncMode =
442         NV_KMS_FRAMELOCK_ATTRIBUTE_HOUSE_SYNC_MODE_DISABLED;
443     pFrameLockEvo->mulDivValue = 1;
444     pFrameLockEvo->mulDivMode = NV_KMS_FRAMELOCK_ATTRIBUTE_MULTIPLY_DIVIDE_MODE_MULTIPLY;
445 
446     /* Query the framelock revision information */
447     if (nvRmApiControl(nvEvoGlobal.clientHandle,
448                        pFrameLockEvo->device,
449                        NV30F1_CTRL_CMD_GSYNC_GET_CAPS,
450                        &gsyncGetCapsParams,
451                        sizeof(gsyncGetCapsParams))
452         != NVOS_STATUS_SUCCESS) {
453         goto fail;
454     }
455 
456     /* Check if the Quadro Sync card has a firmware
457      * version compatible with the GPUs connected to it.
458      */
459     pDevEvo->badFramelockFirmware = gsyncGetCapsParams.isFirmwareRevMismatch;
460     if (gsyncGetCapsParams.isFirmwareRevMismatch) {
461         nvEvoLogDev(pDevEvo, EVO_LOG_ERROR, "The firmware on this Quadro Sync "
462                     "card is not compatible with the GPUs connected to it."
463                     "  Please visit "
464                     "<https://www.nvidia.com/object/quadro-sync.html> "
465                     "for instructions on installing the correct firmware.");
466         goto fail;
467     }
468 
469     /* gsyncGetCapsParams.revId has the framelock board id in the high 4 bits
470      * and the FPGA revision in the low 4 bits.  This is preserved here for
471      * legacy clients, but we expose the full board ID (e.g. 0x358, 0x2060,
472      * 0x2061) and firmware version individually, so clients can more easily
473      * distinguish P2061 ("Quadro Sync II") from P2060 and P358
474      * ("Quadro Sync").
475      */
476 
477     pFrameLockEvo->fpgaIdAndRevision = gsyncGetCapsParams.revId;
478     pFrameLockEvo->boardId = gsyncGetCapsParams.boardId;
479     pFrameLockEvo->firmwareMajorVersion = gsyncGetCapsParams.revision;
480     pFrameLockEvo->firmwareMinorVersion = gsyncGetCapsParams.extendedRevision;
481     pFrameLockEvo->caps = gsyncGetCapsParams.capFlags;
482     pFrameLockEvo->maxSyncSkew = gsyncGetCapsParams.maxSyncSkew;
483     pFrameLockEvo->syncSkewResolution = gsyncGetCapsParams.syncSkewResolution;
484     pFrameLockEvo->maxSyncInterval = gsyncGetCapsParams.maxSyncInterval;
485     pFrameLockEvo->videoModeReadOnly = !!(gsyncGetCapsParams.capFlags &
486         NV30F1_CTRL_GSYNC_GET_CAPS_CAP_FLAGS_ONLY_GET_VIDEO_MODE);
487     pFrameLockEvo->mulDivSupported = !!(gsyncGetCapsParams.capFlags &
488         NV30F1_CTRL_GSYNC_GET_CAPS_CAP_FLAGS_MULTIPLY_DIVIDE_SYNC);
489     pFrameLockEvo->maxMulDivValue = gsyncGetCapsParams.maxMulDivValue;
490 
491     /* Determine if house sync is selectable on this frame lock device */
492     if (!FrameLockUseHouseSyncGetSupport(pFrameLockEvo,
493                                          &pFrameLockEvo->houseSyncUseable)) {
494         pFrameLockEvo->houseSyncUseable = FALSE;
495     }
496 
497     pFrameLockEvo->houseSyncModeValidValues =
498         (1 << NV_KMS_FRAMELOCK_ATTRIBUTE_HOUSE_SYNC_MODE_DISABLED);
499 
500     if (pFrameLockEvo->houseSyncUseable) {
501         pFrameLockEvo->houseSyncModeValidValues |=
502             (1 << NV_KMS_FRAMELOCK_ATTRIBUTE_HOUSE_SYNC_MODE_INPUT);
503     }
504 
505     if (HouseSyncOutputModeUsable(pFrameLockEvo)) {
506         pFrameLockEvo->houseSyncModeValidValues |=
507             (1 << NV_KMS_FRAMELOCK_ATTRIBUTE_HOUSE_SYNC_MODE_OUTPUT);
508     }
509 
510     /* Add frame lock device to global list. */
511     nvListAppend(&pFrameLockEvo->frameLockListEntry, &nvEvoGlobal.frameLockList);
512 
513     return pFrameLockEvo;
514 
515 fail:
516 
517     FreeFrameLockEvo(pDevEvo, pFrameLockEvo);
518     return NULL;
519 }
520 
521 
522 static void BindFrameLockToDevEvo(NVFrameLockEvoPtr pFrameLockEvo,
523                                   NVDevEvoPtr pDevEvo)
524 {
525     NV30F1_CTRL_GET_GSYNC_GPU_TOPOLOGY_PARAMS gsyncTopologyParams = { };
526     int i;
527 
528     /* find out which gpus are attached to which connectors */
529 
530     if (nvRmApiControl(nvEvoGlobal.clientHandle,
531                        pFrameLockEvo->device,
532                        NV30F1_CTRL_CMD_GET_GSYNC_GPU_TOPOLOGY,
533                        &gsyncTopologyParams,
534                        sizeof(gsyncTopologyParams))
535         != NVOS_STATUS_SUCCESS) {
536         return;
537     }
538 
539     /* Bind corresponding GPUs to the Frame Lock device */
540     for (i = 0; i < ARRAY_LEN(gsyncTopologyParams.gpus); i++) {
541 
542         NvU32 connectorIndex;
543         const NvU32 gpuId = gsyncTopologyParams.gpus[i].gpuId;
544 
545         if (gpuId == NV30F1_CTRL_GPU_INVALID_ID) {
546             continue;
547         }
548 
549         if (!GpuIdInDevEvo(pDevEvo, gpuId)) {
550             continue;
551         }
552 
553         /*
554          * Connector type of _NONE means we sync through a proxy GPU,
555          * which we do not support.
556          */
557         if (gsyncTopologyParams.gpus[i].connector ==
558                 NV30F1_CTRL_GET_GSYNC_GPU_TOPOLOGY_NONE) {
559             continue;
560         }
561         /*
562          * gsyncTopologyParams.gpus[i].connector is an enumerated
563          * type; convert it to a 0-based index
564          */
565         nvAssert(gsyncTopologyParams.gpus[i].connector <
566                  (NV30F1_CTRL_GET_GSYNC_GPU_TOPOLOGY_ONE +
567                   NV30F1_GSYNC_CONNECTOR_COUNT));
568         connectorIndex = gsyncTopologyParams.gpus[i].connector -
569             NV30F1_CTRL_GET_GSYNC_GPU_TOPOLOGY_ONE;
570 
571         BindGpuToFrameLock(pDevEvo, gpuId, pFrameLockEvo, connectorIndex);
572     }
573 }
574 
575 static void UnBindFrameLockFromDevEvo(NVFrameLockEvoPtr pFrameLockEvo,
576                                       NVDevEvoPtr pDevEvo)
577 {
578     int i;
579 
580     /*
581      * Loop through GPUs from highest to lowest, because
582      * UnbindGpuFromFrameLock() may remove gpuIds[i].
583      */
584     for (i = pFrameLockEvo->nGpuIds - 1; i >= 0; i--) {
585         const NvU32 gpuId = pFrameLockEvo->gpuIds[i];
586 
587         if (!GpuIdInDevEvo(pDevEvo, gpuId)) {
588             continue;
589         }
590 
591         UnbindGpuFromFrameLock(pDevEvo, gpuId, pFrameLockEvo);
592     }
593 }
594 
595 
596 /*!
597  * Find all of the available framelock devices.
598  *
599  * Framelock devices can only be recognized by resman after an RM
600  * client has attached a GPU that the framelock device is connected
601  * to.  So, subsequent calls to this function may find additional
602  * framelock devices.
603  *
604  * Allocate framelock objects for all the newly found framelock devices.
605  */
606 void nvAllocFrameLocksEvo(NVDevEvoPtr pDevEvo)
607 {
608     NV0000_CTRL_GSYNC_GET_ATTACHED_IDS_PARAMS attachedGsyncParams = { };
609     int i;
610 
611     if (nvRmApiControl(nvEvoGlobal.clientHandle,
612                        nvEvoGlobal.clientHandle,
613                        NV0000_CTRL_CMD_GSYNC_GET_ATTACHED_IDS,
614                        &attachedGsyncParams, sizeof(attachedGsyncParams))
615         != NVOS_STATUS_SUCCESS) {
616         return;
617     }
618 
619     for (i = 0; i < ARRAY_LEN(attachedGsyncParams.gsyncIds); i++) {
620 
621         NVFrameLockEvoPtr pFrameLockEvo;
622 
623         if (attachedGsyncParams.gsyncIds[i] == NV0000_CTRL_GSYNC_INVALID_ID) {
624             continue;
625         }
626 
627         pFrameLockEvo = FindFrameLock(attachedGsyncParams.gsyncIds[i]);
628 
629         if (pFrameLockEvo == NULL) {
630             pFrameLockEvo = AllocFrameLockEvo(pDevEvo, i,
631                                               attachedGsyncParams.gsyncIds[i]);
632         }
633 
634         if (pFrameLockEvo == NULL) {
635             continue;
636         }
637 
638         BindFrameLockToDevEvo(pFrameLockEvo, pDevEvo);
639     }
640 }
641 
642 /*!
643  * Free any framelock devices connected to any GPU on this pDevEvo.
644  */
645 
646 void nvFreeFrameLocksEvo(NVDevEvoPtr pDevEvo)
647 {
648     NVFrameLockEvoPtr pFrameLockEvo, pFrameLockEvoTmp;
649 
650     /* Destroy the pFrameLockEvos */
651     nvListForEachEntry_safe(pFrameLockEvo, pFrameLockEvoTmp,
652                             &nvEvoGlobal.frameLockList, frameLockListEntry) {
653 
654         UnBindFrameLockFromDevEvo(pFrameLockEvo, pDevEvo);
655 
656         if (pFrameLockEvo->nGpuIds == 0) {
657             FreeFrameLockEvo(pDevEvo, pFrameLockEvo);
658         }
659     }
660 }
661 
662 /*!
663  * Determine if this framelock device supports user selection of house
664  * sync.  assign val appropriately.  Returns TRUE if the attribute was
665  * successfully queried.
666  */
667 static NvBool FrameLockUseHouseSyncGetSupport(NVFrameLockEvoPtr pFrameLockEvo,
668                                               NvU32 *val)
669 {
670     NV30F1_CTRL_GSYNC_GET_CONTROL_PARAMS_PARAMS
671         gsyncGetControlParamsParams = { 0 };
672     NvU32 ret;
673 
674     if (!val) return FALSE;
675 
676     gsyncGetControlParamsParams.which =
677         NV30F1_CTRL_GSYNC_GET_CONTROL_SYNC_USE_HOUSE;
678 
679     ret = nvRmApiControl(nvEvoGlobal.clientHandle,
680                          pFrameLockEvo->device,
681                          NV30F1_CTRL_CMD_GSYNC_GET_CONTROL_PARAMS,
682                          &gsyncGetControlParamsParams,
683                          sizeof(gsyncGetControlParamsParams));
684 
685     /* If we can query Use House Sync, then it is available */
686     *val = (ret == NVOS_STATUS_SUCCESS) ? TRUE : FALSE;
687 
688     return *val;
689 }
690 
691 
692 /*!
693  * Return whether or not this framelock device supports house sync mode.
694  *
695  * House sync mode is currently only available on P2061 (Quadro Sync II).
696  */
697 static NvBool HouseSyncOutputModeUsable(const NVFrameLockEvoRec *pFrameLockEvo)
698 {
699     return (pFrameLockEvo->houseSyncUseable &&
700             (pFrameLockEvo->boardId ==
701              NV30F1_CTRL_GSYNC_GET_CAPS_BOARD_ID_P2061));
702 }
703 
704 
705 /*!
706  * Enable or disable house sync output mode in the framelock board.
707  */
708 static NvBool FrameLockSetHouseSyncOutputMode(NVFrameLockEvoPtr pFrameLockEvo,
709                                               NvBool enable)
710 {
711     NV30F1_CTRL_GSYNC_HOUSE_SYNC_MODE_PARAMS
712         gsyncSetHouseSyncModeParams = { 0 };
713     NvU32 ret;
714     NvU8 houseSyncMode = enable ? NV30F1_CTRL_GSYNC_HOUSE_SYNC_MODE_OUTPUT :
715                                   NV30F1_CTRL_GSYNC_HOUSE_SYNC_MODE_INPUT;
716 
717     nvAssert(HouseSyncOutputModeUsable(pFrameLockEvo));
718 
719     gsyncSetHouseSyncModeParams.houseSyncMode = houseSyncMode;
720 
721     ret = nvRmApiControl(nvEvoGlobal.clientHandle,
722                          pFrameLockEvo->device,
723                          NV30F1_CTRL_CMD_GSYNC_SET_HOUSE_SYNC_MODE,
724                          &gsyncSetHouseSyncModeParams,
725                          sizeof(gsyncSetHouseSyncModeParams));
726 
727     return (ret == NVOS_STATUS_SUCCESS);
728 }
729 
730 
731 /*!
732  * Set the framelock to use the house sync if val is TRUE, otherwise
733  * set the framelock to use external sync.  Returns FALSE if the
734  * assignment failed.
735  */
736 NvBool nvFrameLockSetUseHouseSyncEvo(NVFrameLockEvoPtr pFrameLockEvo, NvU32 val)
737 {
738     NV30F1_CTRL_GSYNC_SET_CONTROL_PARAMS_PARAMS
739         gsyncSetControlParamsParams = { 0 };
740     NvU32 ret;
741     NvBool houseSyncOutputMode = FALSE;
742 
743     gsyncSetControlParamsParams.which =
744         NV30F1_CTRL_GSYNC_SET_CONTROL_SYNC_USE_HOUSE;
745 
746     gsyncSetControlParamsParams.useHouseSync = val;
747 
748     ret = nvRmApiControl(nvEvoGlobal.clientHandle,
749                          pFrameLockEvo->device,
750                          NV30F1_CTRL_CMD_GSYNC_SET_CONTROL_PARAMS,
751                          &gsyncSetControlParamsParams,
752                          sizeof(gsyncSetControlParamsParams));
753 
754     if (ret != NVOS_STATUS_SUCCESS) return FALSE;
755 
756     if (HouseSyncOutputModeUsable(pFrameLockEvo)) {
757 
758         NvS64 houseSyncInputPresent;
759         NvBool allowHouseSyncOutput = FALSE;
760 
761         if (nvFrameLockGetStatusEvo(pFrameLockEvo,
762                                     NV_KMS_FRAMELOCK_ATTRIBUTE_HOUSE_STATUS,
763                                     &houseSyncInputPresent)) {
764             if (houseSyncInputPresent == 0) {
765                 allowHouseSyncOutput = TRUE;
766             }
767         }
768 
769         if (!val && allowHouseSyncOutput &&
770             (pFrameLockEvo->houseSyncMode ==
771              NV_KMS_FRAMELOCK_ATTRIBUTE_HOUSE_SYNC_MODE_OUTPUT)) {
772 
773             houseSyncOutputMode = TRUE;
774         }
775 
776         if (!FrameLockSetHouseSyncOutputMode(pFrameLockEvo, houseSyncOutputMode)) {
777             return FALSE;
778         }
779     }
780 
781     /*
782      * House sync polarity is required to be rising edge if house sync is not
783      * in use.
784      *
785      * In addition, house sync polarity has no effect when house sync output
786      * mode is in use.
787      */
788     if (val && !houseSyncOutputMode) {
789         return FrameLockSetPolarity(pFrameLockEvo, pFrameLockEvo->polarity);
790     } else {
791         return FrameLockSetPolarity(pFrameLockEvo,
792                                     NV_KMS_FRAMELOCK_ATTRIBUTE_POLARITY_RISING_EDGE);
793     }
794 }
795 
796 /*!
797  * Set the polarity according to val; val is interpreted as an
798  * NV_KMS_FRAMELOCK_ATTRIBUTE_POLARITY value.  Returns FALSE if the
799  * assignment failed.
800  */
801 static NvBool FrameLockSetPolarity(
802     NVFrameLockEvoPtr pFrameLockEvo,
803     enum NvKmsFrameLockAttributePolarityValue val)
804 {
805     NV30F1_CTRL_GSYNC_SET_CONTROL_PARAMS_PARAMS
806         gsyncSetControlParamsParams = { 0 };
807     NvU32 ret;
808     NvU32 polarity;
809 
810     gsyncSetControlParamsParams.which =
811         NV30F1_CTRL_GSYNC_SET_CONTROL_SYNC_POLARITY;
812 
813     switch (val) {
814     case NV_KMS_FRAMELOCK_ATTRIBUTE_POLARITY_RISING_EDGE:
815         polarity = NV30F1_CTRL_GSYNC_SET_CONTROL_SYNC_POLARITY_RISING_EDGE;
816         break;
817 
818     case NV_KMS_FRAMELOCK_ATTRIBUTE_POLARITY_FALLING_EDGE:
819         polarity = NV30F1_CTRL_GSYNC_SET_CONTROL_SYNC_POLARITY_FALLING_EDGE;
820         break;
821 
822     case NV_KMS_FRAMELOCK_ATTRIBUTE_POLARITY_BOTH_EDGES:
823         polarity = NV30F1_CTRL_GSYNC_SET_CONTROL_SYNC_POLARITY_BOTH_EDGES;
824         break;
825 
826     default:
827         return FALSE;
828     }
829 
830     gsyncSetControlParamsParams.syncPolarity = polarity;
831 
832     ret = nvRmApiControl(nvEvoGlobal.clientHandle,
833                          pFrameLockEvo->device,
834                          NV30F1_CTRL_CMD_GSYNC_SET_CONTROL_PARAMS,
835                          &gsyncSetControlParamsParams,
836                          sizeof(gsyncSetControlParamsParams));
837 
838     if (ret != NVOS_STATUS_SUCCESS) return FALSE;
839 
840     return TRUE;
841 }
842 
843 /*!
844  * Set the sync delay to the value given in val.  Returns FALSE if the
845  * assignment failed.  Assigns pFrameLockEvo->syncDelay upon success.
846  */
847 static NvBool FrameLockSetSyncDelay(NVFrameLockEvoPtr pFrameLockEvo, NvS64 val)
848 {
849     NV30F1_CTRL_GSYNC_SET_CONTROL_PARAMS_PARAMS
850         gsyncSetControlParamsParams = { 0 };
851     NvU32 ret;
852 
853     if (val > pFrameLockEvo->maxSyncSkew) return FALSE;
854 
855     gsyncSetControlParamsParams.which =
856         NV30F1_CTRL_GSYNC_SET_CONTROL_SYNC_SKEW;
857 
858     gsyncSetControlParamsParams.syncSkew = val;
859 
860     ret = nvRmApiControl(nvEvoGlobal.clientHandle,
861                          pFrameLockEvo->device,
862                          NV30F1_CTRL_CMD_GSYNC_SET_CONTROL_PARAMS,
863                          &gsyncSetControlParamsParams,
864                          sizeof(gsyncSetControlParamsParams));
865 
866     if (ret != NVOS_STATUS_SUCCESS) return FALSE;
867 
868     pFrameLockEvo->syncDelay = val;
869 
870     return TRUE;
871 }
872 
873 /*!
874  * Set the sync multiply/divide value given in val.  Returns FALSE if the
875  * assignment failed.  Assigns pFrameLockEvo->mulDivValue upon success.
876  */
877 static NvBool SetFrameLockMulDivVal(NVFrameLockEvoPtr pFrameLockEvo, NvS64 val)
878 {
879     NV30F1_CTRL_GSYNC_SET_CONTROL_PARAMS_PARAMS
880         gsyncSetControlParamsParams = { 0 };
881     NvU32 ret;
882 
883     if (!pFrameLockEvo->mulDivSupported ||
884         (val > pFrameLockEvo->maxMulDivValue)) {
885         return FALSE;
886     }
887 
888     gsyncSetControlParamsParams.which =
889         NV30F1_CTRL_GSYNC_SET_CONTROL_SYNC_MULTIPLY_DIVIDE;
890 
891     gsyncSetControlParamsParams.syncMulDiv.multiplyDivideValue = val;
892     gsyncSetControlParamsParams.syncMulDiv.multiplyDivideMode = pFrameLockEvo->mulDivMode;
893 
894     ret = nvRmApiControl(nvEvoGlobal.clientHandle,
895                          pFrameLockEvo->device,
896                          NV30F1_CTRL_CMD_GSYNC_SET_CONTROL_PARAMS,
897                          &gsyncSetControlParamsParams,
898                          sizeof(gsyncSetControlParamsParams));
899 
900     if (ret != NVOS_STATUS_SUCCESS) return FALSE;
901 
902     pFrameLockEvo->mulDivValue = val;
903 
904     return TRUE;
905 }
906 
907 /*!
908  * Set the sync multiply/divide mode given in val.  Returns FALSE if the
909  * assignment failed.  Assigns pFrameLockEvo->mulDivMode upon success.
910  */
911 static NvBool SetFrameLockMulDivMode(NVFrameLockEvoPtr pFrameLockEvo, NvS64 val)
912 {
913     NV30F1_CTRL_GSYNC_SET_CONTROL_PARAMS_PARAMS
914         gsyncSetControlParamsParams = { 0 };
915     NvU32 ret;
916 
917     if (!pFrameLockEvo->mulDivSupported ||
918         ((val != NV_KMS_FRAMELOCK_ATTRIBUTE_MULTIPLY_DIVIDE_MODE_MULTIPLY) &&
919          (val != NV_KMS_FRAMELOCK_ATTRIBUTE_MULTIPLY_DIVIDE_MODE_DIVIDE))) {
920         return FALSE;
921     }
922 
923     gsyncSetControlParamsParams.which =
924         NV30F1_CTRL_GSYNC_SET_CONTROL_SYNC_MULTIPLY_DIVIDE;
925 
926     gsyncSetControlParamsParams.syncMulDiv.multiplyDivideValue = pFrameLockEvo->mulDivValue;
927     gsyncSetControlParamsParams.syncMulDiv.multiplyDivideMode = val;
928 
929     ret = nvRmApiControl(nvEvoGlobal.clientHandle,
930                          pFrameLockEvo->device,
931                          NV30F1_CTRL_CMD_GSYNC_SET_CONTROL_PARAMS,
932                          &gsyncSetControlParamsParams,
933                          sizeof(gsyncSetControlParamsParams));
934 
935     if (ret != NVOS_STATUS_SUCCESS) return FALSE;
936 
937     pFrameLockEvo->mulDivMode = val;
938 
939     return TRUE;
940 }
941 /*!
942  * Set the sync interval to the value given in val.  Returns FALSE if
943  * the assignment failed.  Assigns pFrameLockEvo->syncInterval upon
944  * success.
945  */
946 static NvBool FrameLockSetSyncInterval(NVFrameLockEvoPtr pFrameLockEvo,
947                                        NvS64 val)
948 {
949     NV30F1_CTRL_GSYNC_SET_CONTROL_PARAMS_PARAMS
950         gsyncSetControlParamsParams = { 0 };
951     NvU32 ret;
952 
953     gsyncSetControlParamsParams.which =
954         NV30F1_CTRL_GSYNC_SET_CONTROL_NSYNC;
955 
956     gsyncSetControlParamsParams.nSync = val;
957 
958     ret = nvRmApiControl(nvEvoGlobal.clientHandle,
959                          pFrameLockEvo->device,
960                          NV30F1_CTRL_CMD_GSYNC_SET_CONTROL_PARAMS,
961                          &gsyncSetControlParamsParams,
962                          sizeof(gsyncSetControlParamsParams));
963 
964     if (ret != NVOS_STATUS_SUCCESS) return FALSE;
965 
966     pFrameLockEvo->syncInterval = val;
967 
968     return TRUE;
969 }
970 
971 /*!
972  * Query the status of the values that are acquired through the
973  * GET_STATUS_SYNC command, and assign the value to val.  Returns
974  * FALSE if the query failed or if attr is not one of the currently
975  * handled attributes.
976  */
977 static NvBool FrameLockGetStatusSync(const NVDispEvoRec *pDispEvo, NvS64 *val,
978                                      enum NvKmsDispAttribute nvKmsAttribute)
979 {
980     NV30F1_CTRL_GSYNC_GET_STATUS_SYNC_PARAMS gsyncGetStatusSyncParams = { 0 };
981     NVFrameLockEvoPtr pFrameLockEvo = pDispEvo->pFrameLockEvo;
982 
983     gsyncGetStatusSyncParams.gpuId = nvGpuIdOfDispEvo(pDispEvo);
984 
985     if (nvRmApiControl(nvEvoGlobal.clientHandle,
986                        pFrameLockEvo->device,
987                        NV30F1_CTRL_CMD_GSYNC_GET_STATUS_SYNC,
988                        &gsyncGetStatusSyncParams,
989                        sizeof(gsyncGetStatusSyncParams))
990             != NVOS_STATUS_SUCCESS) {
991         return FALSE;
992     }
993 
994     switch (nvKmsAttribute)
995     {
996 
997     case NV_KMS_DISP_ATTRIBUTE_FRAMELOCK_STEREO_SYNC:
998         *val = (gsyncGetStatusSyncParams.bTiming &&
999                 gsyncGetStatusSyncParams.bStereoSync &&
1000                 gsyncGetStatusSyncParams.bSyncReady);
1001         break;
1002 
1003     case NV_KMS_DISP_ATTRIBUTE_FRAMELOCK_TIMING:
1004         *val = gsyncGetStatusSyncParams.bTiming ? TRUE : FALSE;
1005         break;
1006 
1007     default:
1008         return FALSE;
1009     }
1010 
1011     return TRUE;
1012 }
1013 
1014 /*!
1015  * Return the sync rate.
1016  */
1017 static NvS64 FrameLockInterpretSyncRate(const NVFrameLockEvoRec *pFrameLockEvo,
1018                                         NvS64 val)
1019 {
1020     /* Only show decimal places if they are accurate. The queried
1021        value provides 4 decimal places */
1022     if (pFrameLockEvo->caps &
1023         NV30F1_CTRL_GSYNC_GET_CAPS_CAP_FLAGS_FREQ_ACCURACY_2DPS) {
1024         // only two are valid
1025         val -= (val % 100);
1026     } else if (pFrameLockEvo->caps &
1027                NV30F1_CTRL_GSYNC_GET_CAPS_CAP_FLAGS_FREQ_ACCURACY_3DPS) {
1028         // only three are valid
1029         val -= (val % 10);
1030     } else if (pFrameLockEvo->caps &
1031                NV30F1_CTRL_GSYNC_GET_CAPS_CAP_FLAGS_FREQ_ACCURACY_4DPS) {
1032         // all four are valid, nothing to do
1033     }
1034     return val;
1035 }
1036 
1037 /*!
1038  * Query the status of one of the values that are acquired through the
1039  * GET_STATUS command, and assign the value to val.  Returns FALSE if
1040  * the query failed or if attr is not one of the currently handled
1041  * attributes.
1042  */
1043 NvBool nvFrameLockGetStatusEvo(const NVFrameLockEvoRec *pFrameLockEvo,
1044                                enum NvKmsFrameLockAttribute attribute,
1045                                NvS64 *val)
1046 {
1047     NV30F1_CTRL_GSYNC_GET_STATUS_PARAMS gsyncGetStatusParams = { 0 };
1048 
1049     switch (attribute) {
1050 
1051     case NV_KMS_FRAMELOCK_ATTRIBUTE_PORT0_STATUS:
1052         gsyncGetStatusParams.which = NV30F1_CTRL_GSYNC_GET_STATUS_PORT0_INPUT;
1053         break;
1054 
1055     case NV_KMS_FRAMELOCK_ATTRIBUTE_PORT1_STATUS:
1056         gsyncGetStatusParams.which = NV30F1_CTRL_GSYNC_GET_STATUS_PORT1_INPUT;
1057         break;
1058 
1059     case NV_KMS_FRAMELOCK_ATTRIBUTE_HOUSE_STATUS:
1060         gsyncGetStatusParams.which = NV30F1_CTRL_GSYNC_GET_STATUS_HOUSE_SYNC;
1061         break;
1062 
1063     case NV_KMS_FRAMELOCK_ATTRIBUTE_INCOMING_HOUSE_SYNC_RATE:
1064         gsyncGetStatusParams.which =
1065             NV30F1_CTRL_GSYNC_GET_STATUS_HOUSE_SYNC_INCOMING;
1066         break;
1067 
1068     case NV_KMS_FRAMELOCK_ATTRIBUTE_SYNC_READY:
1069         gsyncGetStatusParams.which = NV30F1_CTRL_GSYNC_GET_STATUS_SYNC_READY;
1070         break;
1071 
1072     case NV_KMS_FRAMELOCK_ATTRIBUTE_ETHERNET_DETECTED:
1073         gsyncGetStatusParams.which =
1074             NV30F1_CTRL_GSYNC_GET_STATUS_PORT0_ETHERNET |
1075             NV30F1_CTRL_GSYNC_GET_STATUS_PORT1_ETHERNET;
1076         break;
1077 
1078     case NV_KMS_FRAMELOCK_ATTRIBUTE_SYNC_RATE:
1079     case NV_KMS_FRAMELOCK_ATTRIBUTE_SYNC_RATE_4:
1080         gsyncGetStatusParams.which = NV30F1_CTRL_GSYNC_GET_STATUS_REFRESH;
1081         break;
1082 
1083     default:
1084         return FALSE;
1085     }
1086 
1087     if (nvRmApiControl(nvEvoGlobal.clientHandle,
1088                        pFrameLockEvo->device,
1089                        NV30F1_CTRL_CMD_GSYNC_GET_STATUS,
1090                        &gsyncGetStatusParams,
1091                        sizeof(gsyncGetStatusParams))
1092             != NVOS_STATUS_SUCCESS) {
1093         return FALSE;
1094     }
1095 
1096     switch (attribute) {
1097 
1098     case NV_KMS_FRAMELOCK_ATTRIBUTE_PORT0_STATUS:
1099         *val = gsyncGetStatusParams.bPort0Input ?
1100             NV_KMS_FRAMELOCK_ATTRIBUTE_PORT_STATUS_INPUT :
1101             NV_KMS_FRAMELOCK_ATTRIBUTE_PORT_STATUS_OUTPUT;
1102         break;
1103 
1104     case NV_KMS_FRAMELOCK_ATTRIBUTE_PORT1_STATUS:
1105         *val = gsyncGetStatusParams.bPort1Input ?
1106             NV_KMS_FRAMELOCK_ATTRIBUTE_PORT_STATUS_INPUT :
1107             NV_KMS_FRAMELOCK_ATTRIBUTE_PORT_STATUS_OUTPUT;
1108         break;
1109 
1110     case NV_KMS_FRAMELOCK_ATTRIBUTE_HOUSE_STATUS:
1111         *val = gsyncGetStatusParams.bHouseSync;
1112         break;
1113 
1114     case NV_KMS_FRAMELOCK_ATTRIBUTE_SYNC_READY:
1115         *val = gsyncGetStatusParams.bSyncReady;
1116         break;
1117 
1118     case NV_KMS_FRAMELOCK_ATTRIBUTE_ETHERNET_DETECTED:
1119         *val = 0x0;
1120         if (gsyncGetStatusParams.bPort0Ethernet)
1121             *val |= NV_KMS_FRAMELOCK_ATTRIBUTE_ETHERNET_DETECTED_PORT0;
1122         if (gsyncGetStatusParams.bPort1Ethernet)
1123             *val |= NV_KMS_FRAMELOCK_ATTRIBUTE_ETHERNET_DETECTED_PORT1;
1124         break;
1125 
1126     case NV_KMS_FRAMELOCK_ATTRIBUTE_INCOMING_HOUSE_SYNC_RATE:
1127         *val =
1128             FrameLockInterpretSyncRate(pFrameLockEvo,
1129                                        gsyncGetStatusParams.houseSyncIncoming);
1130         break;
1131 
1132     case NV_KMS_FRAMELOCK_ATTRIBUTE_SYNC_RATE:
1133     case NV_KMS_FRAMELOCK_ATTRIBUTE_SYNC_RATE_4:
1134         *val = FrameLockInterpretSyncRate(pFrameLockEvo,
1135                                           gsyncGetStatusParams.refresh);
1136         if (attribute == NV_KMS_FRAMELOCK_ATTRIBUTE_SYNC_RATE) {
1137             /* _STATUS_REFRESH is in Hz/10000, _SYNC_RATE is Hz/1000 */
1138             *val /= 10;
1139         }
1140         break;
1141 
1142     default:
1143         return FALSE;
1144     }
1145 
1146     return TRUE;
1147 }
1148 
1149 /*!
1150  * [en|dis]able syncing of the GPU to the FrameLock board for the
1151  * display mask associated with that gpu. val controls whether we are
1152  * enabling or disabling.
1153  */
1154 static NvBool FrameLockSetEnable(NVDispEvoPtr pDispEvo, NvS64 val)
1155 {
1156     if (val) {
1157 
1158         /* XXX NVKMS TODO: address the following:
1159 
1160            In Xinerama a single app has a channel on each gpu. Before
1161            framelock is enabled the first time per X server, vblanks
1162            are not synchronized, so if a swap groupped app is started
1163            before framelock is enabled the channels get unstalled at
1164            different times, and it's likely that one display will be
1165            armed while the other is not. When framelock is enabled in
1166            this state, we'll deadlock because suddenly the armed display
1167            is waiting on the unarmed display to unstall, and the unarmed
1168            display cannot arm. Prevent this by idling all channels */
1169 
1170         return nvEnableFrameLockEvo(pDispEvo);
1171     } else {
1172         return nvDisableFrameLockEvo(pDispEvo);
1173     }
1174 }
1175 
1176 static NvBool FrameLockSetWatchdog(NVFrameLockEvoPtr pFrameLockEvo, NvU32 val)
1177 {
1178     NV30F1_CTRL_GSYNC_SET_CONTROL_WATCHDOG_PARAMS
1179         gsyncSetControlWatchdogParams = { 0 };
1180     NvU32 ret;
1181 
1182     gsyncSetControlWatchdogParams.enable = val;
1183 
1184     ret = nvRmApiControl(nvEvoGlobal.clientHandle,
1185                          pFrameLockEvo->device,
1186                          NV30F1_CTRL_CMD_GSYNC_SET_CONTROL_WATCHDOG,
1187                          &gsyncSetControlWatchdogParams,
1188                          sizeof(gsyncSetControlWatchdogParams));
1189 
1190     if (ret != NVOS_STATUS_SUCCESS) return FALSE;
1191 
1192     return TRUE;
1193 }
1194 
1195 
1196 /*!
1197  * For the given display, determine if it can be set as a frame lock
1198  * server
1199  */
1200 static NvBool FrameLockDpyCanBeServer(const NVDpyEvoRec *pDpyEvo)
1201 {
1202     NV30F1_CTRL_GSYNC_GET_CONTROL_SYNC_PARAMS gsyncGetControlSyncParams = { 0 };
1203     NVDispEvoPtr pDispEvo = pDpyEvo->pDispEvo;
1204     NVFrameLockEvoPtr pFrameLockEvo = pDispEvo->pFrameLockEvo;
1205     const NvU32 head = nvGetPrimaryHwHead(pDispEvo, pDpyEvo->apiHead);
1206     const NVDispHeadStateEvoRec *pHeadState;
1207     NvU32 ret;
1208 
1209     nvAssert(head != NV_INVALID_HEAD);
1210     nvAssert(pDispEvo);
1211     nvAssert(pDispEvo->pFrameLockEvo);
1212 
1213     pHeadState = &pDispEvo->headState[head];
1214     nvAssert(pHeadState->activeRmId);
1215 
1216     /* If already a server, assume it can be a server. */
1217     if (nvDpyIdsAreEqual(pDispEvo->framelock.server, pDpyEvo->id)) {
1218         return TRUE;
1219     }
1220 
1221     gsyncGetControlSyncParams.gpuId = nvGpuIdOfDispEvo(pDispEvo);
1222     gsyncGetControlSyncParams.displays = pHeadState->activeRmId;
1223 
1224     ret = nvRmApiControl(nvEvoGlobal.clientHandle,
1225                          pFrameLockEvo->device,
1226                          NV30F1_CTRL_CMD_GSYNC_GET_CONTROL_SYNC,
1227                          &gsyncGetControlSyncParams,
1228                          sizeof(gsyncGetControlSyncParams));
1229 
1230     if (ret != NVOS_STATUS_SUCCESS) {
1231         return FALSE;
1232     }
1233 
1234     if (gsyncGetControlSyncParams.master &&
1235         nvFrameLockServerPossibleEvo(pDpyEvo)) {
1236         return TRUE;
1237     }
1238 
1239     return FALSE;
1240 }
1241 
1242 
1243 /*!
1244  * For the given display, determine if it can be set as a frame lock
1245  * client.
1246  */
1247 static NvBool FrameLockDpyCanBeClient(const NVDpyEvoRec *pDpyEvo)
1248 {
1249     NVDispEvoPtr pDispEvo;
1250 
1251     nvAssert(pDpyEvo->pDispEvo);
1252     nvAssert(pDpyEvo->pDispEvo->pFrameLockEvo);
1253     nvAssert(nvDpyEvoIsActive(pDpyEvo));
1254 
1255     pDispEvo = pDpyEvo->pDispEvo;
1256 
1257     /* If already a client, assume it can be a client. */
1258     if (nvDpyIdIsInDpyIdList(pDpyEvo->id, pDispEvo->framelock.clients)) {
1259         return TRUE;
1260     }
1261 
1262     /* Otherwise, see if we can make it a client. */
1263     return nvFrameLockClientPossibleEvo(pDpyEvo);
1264 }
1265 
1266 
1267 /*!
1268  * [en|dis]able test mode (based on the value of val).  Returns FALSE
1269  * if changing the test mode failed.  Assigns pFrameLockEvo->testMode
1270  * upon success.
1271  */
1272 static NvBool FrameLockSetTestMode(NVFrameLockEvoPtr pFrameLockEvo, NvS64 val)
1273 {
1274     NV30F1_CTRL_GSYNC_SET_CONTROL_TESTING_PARAMS
1275         gsyncSetControlTestingParams = { 0 };
1276     NvU32 ret;
1277 
1278     gsyncSetControlTestingParams.bEmitTestSignal = (val == TRUE);
1279 
1280     ret = nvRmApiControl(nvEvoGlobal.clientHandle,
1281                          pFrameLockEvo->device,
1282                          NV30F1_CTRL_CMD_GSYNC_SET_CONTROL_TESTING,
1283                          &gsyncSetControlTestingParams,
1284                          sizeof(gsyncSetControlTestingParams));
1285 
1286     if (ret != NVOS_STATUS_SUCCESS) return FALSE;
1287 
1288     pFrameLockEvo->testMode = val;
1289 
1290     return TRUE;
1291 }
1292 
1293 
1294 /*!
1295  * Set the video mode according to val; returns FALSE if the
1296  * assignment failed.  Assigns pFrameLockEvo->videoMode upon success.
1297  */
1298 static NvBool FrameLockSetVideoMode(NVFrameLockEvoPtr pFrameLockEvo, NvS64 val)
1299 {
1300     NV30F1_CTRL_GSYNC_SET_CONTROL_PARAMS_PARAMS
1301         gsyncSetControlParamsParams = { 0 };
1302     NvU32 ret;
1303 
1304     gsyncSetControlParamsParams.which =
1305         NV30F1_CTRL_GSYNC_SET_CONTROL_VIDEO_MODE;
1306 
1307     switch (val) {
1308 
1309     case NV_KMS_FRAMELOCK_ATTRIBUTE_VIDEO_MODE_COMPOSITE_AUTO:
1310         gsyncSetControlParamsParams.syncVideoMode =
1311             NV30F1_CTRL_GSYNC_SET_CONTROL_VIDEO_MODE_NONE;
1312         break;
1313 
1314     case NV_KMS_FRAMELOCK_ATTRIBUTE_VIDEO_MODE_TTL:
1315         gsyncSetControlParamsParams.syncVideoMode =
1316             NV30F1_CTRL_GSYNC_SET_CONTROL_VIDEO_MODE_TTL;
1317         break;
1318 
1319     case NV_KMS_FRAMELOCK_ATTRIBUTE_VIDEO_MODE_COMPOSITE_BI_LEVEL:
1320         gsyncSetControlParamsParams.syncVideoMode =
1321             NV30F1_CTRL_GSYNC_SET_CONTROL_VIDEO_MODE_NTSCPALSECAM;
1322         break;
1323 
1324     case NV_KMS_FRAMELOCK_ATTRIBUTE_VIDEO_MODE_COMPOSITE_TRI_LEVEL:
1325         gsyncSetControlParamsParams.syncVideoMode =
1326             NV30F1_CTRL_GSYNC_SET_CONTROL_VIDEO_MODE_HDTV;
1327         break;
1328 
1329     default:
1330         return FALSE;
1331     }
1332 
1333     ret = nvRmApiControl(nvEvoGlobal.clientHandle,
1334                          pFrameLockEvo->device,
1335                          NV30F1_CTRL_CMD_GSYNC_SET_CONTROL_PARAMS,
1336                          &gsyncSetControlParamsParams,
1337                          sizeof(gsyncSetControlParamsParams));
1338 
1339     if (ret != NVOS_STATUS_SUCCESS) return FALSE;
1340 
1341     pFrameLockEvo->videoMode = val;
1342 
1343     return TRUE;
1344 }
1345 
1346 
1347 /*!
1348  * Enable or disable the swap ready connection through the gsync
1349  * connector. This should be called when we bind the swap barrier.
1350  */
1351 static NvBool SetSwapBarrier(NVDispEvoPtr pDispEvo, NvS64 val)
1352 {
1353     NV30F1_CTRL_GSYNC_SET_CONTROL_SWAP_BARRIER_PARAMS
1354         gsyncSetSwapBarrierParams = { 0 };
1355     NVFrameLockEvoPtr pFrameLockEvo = pDispEvo->pFrameLockEvo;
1356     NvU32 ret;
1357     NvBool enable = !!val;
1358 
1359     if (!pFrameLockEvo) return FALSE;
1360 
1361     nvSetSwapBarrierNotifyEvo(pDispEvo, enable, TRUE /* isPre */);
1362 
1363     gsyncSetSwapBarrierParams.gpuId = nvGpuIdOfDispEvo(pDispEvo);
1364     gsyncSetSwapBarrierParams.enable = enable;
1365 
1366     ret = nvRmApiControl(nvEvoGlobal.clientHandle,
1367                          pFrameLockEvo->device,
1368                          NV30F1_CTRL_CMD_GSYNC_SET_CONTROL_SWAP_BARRIER,
1369                          &gsyncSetSwapBarrierParams,
1370                          sizeof(gsyncSetSwapBarrierParams));
1371 
1372     nvSetSwapBarrierNotifyEvo(pDispEvo, enable, FALSE /* isPre */);
1373 
1374     return (ret == NVOS_STATUS_SUCCESS);
1375 }
1376 
1377 
1378 /*!
1379  * Flush all of our known framelock SW state out to the HW, to make
1380  * sure both are in sync.  This should be called any time we get the
1381  * HW back from outside control (e.g., starting X or coming back from
1382  * a VT switch).
1383  */
1384 static NvBool ResetHardwareOneDisp(NVDispEvoPtr pDispEvo, NvS64 value)
1385 {
1386     NVFrameLockEvoPtr pFrameLockEvo = pDispEvo->pFrameLockEvo;
1387     NvU32 activeHeadsMask;
1388     NvBool ret = TRUE;
1389 
1390     if (!pDispEvo->pFrameLockEvo || !value) {
1391         /* Nothing to do */
1392         return TRUE;
1393     }
1394 
1395     /* We should never get here when framelock is enabled */
1396     if (pDispEvo->framelock.syncEnabled) {
1397         nvAssert(!"Attempted to reset framelock HW while framelock is enabled");
1398         return FALSE;
1399     }
1400 
1401     /* (Re-)set the HW state to match the SW state */
1402     if (!nvFrameLockSetUseHouseSyncEvo(pFrameLockEvo,
1403                                        pFrameLockEvo->houseSyncArmed)) {
1404         ret = FALSE;
1405     }
1406     if (!FrameLockSetSyncDelay(pFrameLockEvo, pFrameLockEvo->syncDelay)) {
1407         ret = FALSE;
1408     }
1409     if (!FrameLockSetSyncInterval(pFrameLockEvo, pFrameLockEvo->syncInterval)) {
1410         ret = FALSE;
1411     }
1412     if (!FrameLockSetVideoMode(pFrameLockEvo, pFrameLockEvo->videoMode)) {
1413         ret = FALSE;
1414     }
1415     if (!FrameLockSetTestMode(pFrameLockEvo, pFrameLockEvo->testMode)) {
1416         ret = FALSE;
1417     }
1418     if (!SetFrameLockMulDivVal(pFrameLockEvo, pFrameLockEvo->mulDivValue)) {
1419         ret = FALSE;
1420     }
1421     if (!SetFrameLockMulDivMode(pFrameLockEvo, pFrameLockEvo->mulDivMode)) {
1422         ret = FALSE;
1423     }
1424 
1425     /* Since (we think) sync is disabled, these should always be disabled */
1426     if (!FrameLockSetWatchdog(pFrameLockEvo, FALSE)) {
1427         ret = FALSE;
1428     }
1429     if (!SetSwapBarrier(pDispEvo, FALSE)) {
1430         ret = FALSE;
1431     }
1432 
1433     /* Disable both server and client lock for all heads */
1434     activeHeadsMask = nvGetActiveHeadMask(pDispEvo);
1435 
1436     if (!nvFramelockSetControlUnsyncEvo(pDispEvo, activeHeadsMask, TRUE)) {
1437         ret = FALSE;
1438     }
1439     if (!nvFramelockSetControlUnsyncEvo(pDispEvo, activeHeadsMask, FALSE)) {
1440         ret = FALSE;
1441     }
1442 
1443     return ret;
1444 }
1445 
1446 
1447 /*!
1448  * Returns the allowable configurations for the given display device.
1449  * The device must be enabled to advertise server/client
1450  * configuration.
1451  */
1452 static unsigned int FrameLockGetValidDpyConfig(const NVDpyEvoRec *pDpyEvo)
1453 {
1454     NVDispEvoPtr pDispEvo;
1455     unsigned int valid =
1456         (1 << (NV_KMS_DPY_ATTRIBUTE_FRAMELOCK_DISPLAY_CONFIG_DISABLED));
1457 
1458     if (!pDpyEvo || !nvDpyEvoIsActive(pDpyEvo)) {
1459         goto done;
1460     }
1461 
1462     pDispEvo = pDpyEvo->pDispEvo;
1463 
1464     if (!pDispEvo || !pDispEvo->pFrameLockEvo) {
1465         goto done;
1466     }
1467 
1468     /* Check if display can be a server */
1469 
1470     if (FrameLockDpyCanBeServer(pDpyEvo)) {
1471         valid |= (1 << (NV_KMS_DPY_ATTRIBUTE_FRAMELOCK_DISPLAY_CONFIG_SERVER));
1472     }
1473 
1474     /* Check if display can be a client */
1475 
1476     if (FrameLockDpyCanBeClient(pDpyEvo)) {
1477         valid |= (1 << (NV_KMS_DPY_ATTRIBUTE_FRAMELOCK_DISPLAY_CONFIG_CLIENT));
1478     }
1479 
1480  done:
1481 
1482     return valid;
1483 }
1484 
1485 static NvBool GetFrameLock(NVDispEvoPtr pDispEvo, NvS64 *val)
1486 {
1487     *val = (pDispEvo->pFrameLockEvo) ? 1 : 0;
1488     return TRUE;
1489 }
1490 
1491 static NvBool SetFrameLockPolarity(NVFrameLockEvoPtr pFrameLockEvo, NvS64 val)
1492 {
1493     if ((val != NV_KMS_FRAMELOCK_ATTRIBUTE_POLARITY_RISING_EDGE) &&
1494         (val != NV_KMS_FRAMELOCK_ATTRIBUTE_POLARITY_FALLING_EDGE) &&
1495         (val != NV_KMS_FRAMELOCK_ATTRIBUTE_POLARITY_BOTH_EDGES)) {
1496         return FALSE;
1497     }
1498 
1499     pFrameLockEvo->polarity = val;
1500 
1501     return TRUE;
1502 }
1503 
1504 static NvBool GetFrameLockPolarity(const NVFrameLockEvoRec *pFrameLockEvo,
1505                                    enum NvKmsFrameLockAttribute attribute,
1506                                    NvS64 *val)
1507 {
1508     *val = pFrameLockEvo->polarity;
1509 
1510     return TRUE;
1511 }
1512 
1513 static NvBool GetFrameLockSyncDelay(const NVFrameLockEvoRec *pFrameLockEvo,
1514                                     enum NvKmsFrameLockAttribute attribute,
1515                                     NvS64 *val)
1516 {
1517     *val = pFrameLockEvo->syncDelay;
1518 
1519     return TRUE;
1520 }
1521 
1522 static NvBool GetFrameLockSyncDelayValidValues(
1523     const NVFrameLockEvoRec *pFrameLockEvo,
1524     struct NvKmsAttributeValidValuesCommonReply *pValidValues)
1525 {
1526     nvAssert(pValidValues->type == NV_KMS_ATTRIBUTE_TYPE_RANGE);
1527 
1528     pValidValues->u.range.min = 0;
1529     pValidValues->u.range.max = pFrameLockEvo->maxSyncSkew;
1530 
1531     return TRUE;
1532 }
1533 
1534 static NvBool GetFrameLockMulDivVal(const NVFrameLockEvoRec *pFrameLockEvo,
1535                                     enum NvKmsFrameLockAttribute attribute,
1536                                     NvS64 *val)
1537 {
1538     if (!pFrameLockEvo->mulDivSupported) {
1539         return FALSE;
1540     }
1541 
1542     *val = pFrameLockEvo->mulDivValue;
1543 
1544     return TRUE;
1545 }
1546 
1547 static NvBool GetFrameLockMulDivValValidValues(
1548     const NVFrameLockEvoRec *pFrameLockEvo,
1549     struct NvKmsAttributeValidValuesCommonReply *pValidValues)
1550 {
1551     nvAssert(pValidValues->type == NV_KMS_ATTRIBUTE_TYPE_RANGE);
1552 
1553     if (!pFrameLockEvo->mulDivSupported) {
1554         return FALSE;
1555     }
1556 
1557     pValidValues->u.range.min = 1;
1558     pValidValues->u.range.max = pFrameLockEvo->maxMulDivValue;
1559 
1560     return TRUE;
1561 }
1562 
1563 static NvBool GetFrameLockMulDivModeValidValues(
1564     const NVFrameLockEvoRec *pFrameLockEvo,
1565     struct NvKmsAttributeValidValuesCommonReply *pValidValues)
1566 {
1567     if (!pFrameLockEvo->mulDivSupported) {
1568         return FALSE;
1569     }
1570 
1571     nvAssert(pValidValues->type == NV_KMS_ATTRIBUTE_TYPE_INTEGER);
1572 
1573     return TRUE;
1574 }
1575 
1576 static NvBool GetFrameLockMulDivMode(const NVFrameLockEvoRec *pFrameLockEvo,
1577                                      enum NvKmsFrameLockAttribute attribute,
1578                                      NvS64 *val)
1579 {
1580     if (!pFrameLockEvo->mulDivSupported) {
1581         return FALSE;
1582     }
1583 
1584     *val = pFrameLockEvo->mulDivMode;
1585 
1586     return TRUE;
1587 }
1588 
1589 static NvBool SetHouseSyncMode(NVFrameLockEvoPtr pFrameLockEvo, NvS64 val)
1590 {
1591     if ((val < 0) || (val > 31)) {
1592         return FALSE;
1593     }
1594 
1595     if ((pFrameLockEvo->houseSyncModeValidValues & NVBIT(val)) == 0) {
1596         return FALSE;
1597     }
1598 
1599     pFrameLockEvo->houseSyncMode = val;
1600 
1601     return TRUE;
1602 }
1603 
1604 static NvBool GetHouseSyncMode(const NVFrameLockEvoRec *pFrameLockEvo,
1605                                enum NvKmsFrameLockAttribute attribute,
1606                                NvS64 *val)
1607 {
1608     if (!pFrameLockEvo->houseSyncUseable) return FALSE;
1609 
1610     *val = pFrameLockEvo->houseSyncMode;
1611 
1612     return TRUE;
1613 }
1614 
1615 static NvBool GetHouseSyncModeValidValues(
1616     const NVFrameLockEvoRec *pFrameLockEvo,
1617     struct NvKmsAttributeValidValuesCommonReply *pValidValues)
1618 {
1619     if (!pFrameLockEvo->houseSyncUseable) {
1620         return FALSE;
1621     }
1622 
1623     nvAssert(pValidValues->type == NV_KMS_ATTRIBUTE_TYPE_INTBITS);
1624 
1625     pValidValues->u.bits.ints = pFrameLockEvo->houseSyncModeValidValues;
1626 
1627     return TRUE;
1628 }
1629 
1630 static NvBool GetFrameLockSyncInterval(const NVFrameLockEvoRec *pFrameLockEvo,
1631                                        enum NvKmsFrameLockAttribute attribute,
1632                                        NvS64 *val)
1633 {
1634     *val = pFrameLockEvo->syncInterval;
1635 
1636     return TRUE;
1637 }
1638 
1639 static NvBool GetFrameLockSyncIntervalValidValues(
1640     const NVFrameLockEvoRec *pFrameLockEvo,
1641     struct NvKmsAttributeValidValuesCommonReply *pValidValues)
1642 {
1643     nvAssert(pValidValues->type == NV_KMS_ATTRIBUTE_TYPE_RANGE);
1644 
1645     pValidValues->u.range.min = 0;
1646     pValidValues->u.range.max = pFrameLockEvo->maxSyncInterval;
1647 
1648     return TRUE;
1649 }
1650 
1651 static NvBool SetFrameLockSync(NVDispEvoRec *pDispEvo, NvS64 val)
1652 {
1653     NvBool a, b;
1654 
1655     if (!pDispEvo->pFrameLockEvo) return FALSE;
1656 
1657     /* If we are already enabled or already disabled, we're done. */
1658     if (val == pDispEvo->framelock.syncEnabled) return TRUE;
1659 
1660     /* Something must be set to enable/disable */
1661     if (nvDpyIdIsInvalid(pDispEvo->framelock.server) &&
1662         nvDpyIdListIsEmpty(pDispEvo->framelock.clients)) return FALSE;
1663 
1664     /* If we're disabling and test mode is currently enabled, disable it */
1665     if (!val &&
1666         !nvDpyIdIsInvalid(pDispEvo->framelock.server) &&
1667         pDispEvo->pFrameLockEvo->testMode) {
1668 
1669         FrameLockSetTestMode(pDispEvo->pFrameLockEvo, FALSE);
1670     }
1671 
1672     /*
1673      * It is important to set syncEnabled before calling FrameLockSetEnable.
1674      * FrameLockSetEnable may call into GLS which may call back into the
1675      * driver to query if framelock is enabled, which checks this field.
1676      */
1677     pDispEvo->framelock.syncEnabled = val;
1678 
1679     a = FrameLockSetEnable(pDispEvo, val);
1680     b = FrameLockSetWatchdog(pDispEvo->pFrameLockEvo, val);
1681 
1682     /*
1683      * Since RM doesn't send a SYNC_READY event on sync disable through nvctrl,
1684      * send it here.
1685      */
1686     if (!val && a && b) {
1687         nvSendFrameLockAttributeChangedEventEvo(
1688             pDispEvo->pFrameLockEvo,
1689             NV_KMS_FRAMELOCK_ATTRIBUTE_SYNC_READY,
1690             FALSE);
1691         pDispEvo->pFrameLockEvo->syncReadyLast = val;
1692     }
1693 
1694     return (a && b);
1695 }
1696 
1697 static NvBool GetFrameLockSync(NVDispEvoPtr pDispEvo, NvS64 *val)
1698 {
1699     if (!pDispEvo->pFrameLockEvo) return FALSE;
1700 
1701     /* return the cached state */
1702 
1703     *val = ((pDispEvo->framelock.currentServerHead != NV_INVALID_HEAD) ||
1704             (pDispEvo->framelock.currentClientHeadsMask != 0x0));
1705 
1706     return TRUE;
1707 }
1708 
1709 static NvBool GetFrameLockSyncReady(const NVFrameLockEvoRec *pFrameLockEvo,
1710                                     enum NvKmsFrameLockAttribute attribute,
1711                                     NvS64 *val)
1712 {
1713     /* return the cached state */
1714 
1715     *val = pFrameLockEvo->syncReadyLast;
1716 
1717     return TRUE;
1718 }
1719 
1720 static NvBool GetFrameLockStereoSync(NVDispEvoPtr pDispEvo, NvS64 *val)
1721 {
1722     if (!pDispEvo->pFrameLockEvo) return FALSE;
1723 
1724     return FrameLockGetStatusSync(pDispEvo, val,
1725                                   NV_KMS_DISP_ATTRIBUTE_FRAMELOCK_STEREO_SYNC);
1726 }
1727 
1728 static NvBool GetFrameLockTiming(NVDispEvoPtr pDispEvo, NvS64 *val)
1729 {
1730     if (!pDispEvo->pFrameLockEvo) return FALSE;
1731 
1732     return FrameLockGetStatusSync(pDispEvo, val,
1733                                   NV_KMS_DISP_ATTRIBUTE_FRAMELOCK_TIMING);
1734 }
1735 
1736 static NvBool SetFrameLockTestSignal(NVDispEvoRec *pDispEvo, NvS64 val)
1737 {
1738     if (!pDispEvo->pFrameLockEvo) return FALSE;
1739 
1740     /* The test signal can only be emitted if the GPU is the server
1741      * and framelock is enabled.
1742      */
1743 
1744     if (!nvDpyIdIsInvalid(pDispEvo->framelock.server) &&
1745         pDispEvo->framelock.syncEnabled) {
1746         return FrameLockSetTestMode(pDispEvo->pFrameLockEvo, val);
1747     }
1748 
1749     return FALSE;
1750 }
1751 
1752 static NvBool GetFrameLockTestSignal(NVDispEvoPtr pDispEvo, NvS64 *val)
1753 {
1754     if (!pDispEvo->pFrameLockEvo ||
1755         nvDpyIdIsInvalid(pDispEvo->framelock.server)) {
1756         return FALSE;
1757     }
1758 
1759     *val = pDispEvo->pFrameLockEvo->testMode;
1760 
1761     return TRUE;
1762 }
1763 
1764 static NvBool SetFrameLockVideoMode(NVFrameLockEvoPtr pFrameLockEvo, NvS64 val)
1765 {
1766     if (pFrameLockEvo->videoModeReadOnly) {
1767         return FALSE;
1768     }
1769 
1770     return FrameLockSetVideoMode(pFrameLockEvo, val);
1771 }
1772 
1773 static NvBool GetFrameLockVideoMode(const NVFrameLockEvoRec *pFrameLockEvo,
1774                                     enum NvKmsFrameLockAttribute attribute,
1775                                     NvS64 *val)
1776 {
1777     *val = pFrameLockEvo->videoMode;
1778 
1779     return TRUE;
1780 }
1781 
1782 static NvBool GetFrameLockVideoModeValidValues(
1783     const NVFrameLockEvoRec *pFrameLockEvo,
1784     struct NvKmsAttributeValidValuesCommonReply *pValidValues)
1785 {
1786     nvAssert(pValidValues->type == NV_KMS_ATTRIBUTE_TYPE_RANGE);
1787 
1788     pValidValues->u.range.min =
1789         NV_KMS_FRAMELOCK_ATTRIBUTE_VIDEO_MODE_COMPOSITE_AUTO;
1790     pValidValues->u.range.max =
1791         NV_KMS_FRAMELOCK_ATTRIBUTE_VIDEO_MODE_COMPOSITE_TRI_LEVEL;
1792 
1793     if (pFrameLockEvo->videoModeReadOnly) {
1794         pValidValues->writable = FALSE;
1795     }
1796 
1797     return TRUE;
1798 }
1799 
1800 static NvBool GetFrameLockFpgaRevision(const NVFrameLockEvoRec *pFrameLockEvo,
1801                                        enum NvKmsFrameLockAttribute attribute,
1802                                        NvS64 *val)
1803 {
1804     *val = pFrameLockEvo->fpgaIdAndRevision;
1805 
1806     return TRUE;
1807 }
1808 
1809 static NvBool GetFrameLockFirmwareMajorVersion(
1810     const NVFrameLockEvoRec *pFrameLockEvo,
1811     enum NvKmsFrameLockAttribute attribute,
1812     NvS64 *val)
1813 {
1814     *val = pFrameLockEvo->firmwareMajorVersion;
1815 
1816     return TRUE;
1817 }
1818 
1819 static NvBool GetFrameLockFirmwareMinorVersion(
1820     const NVFrameLockEvoRec *pFrameLockEvo,
1821     enum NvKmsFrameLockAttribute attribute,
1822     NvS64 *val)
1823 {
1824     *val = pFrameLockEvo->firmwareMinorVersion;
1825 
1826     return TRUE;
1827 }
1828 
1829 static NvBool GetFrameLockBoardId(const NVFrameLockEvoRec *pFrameLockEvo,
1830                                   enum NvKmsFrameLockAttribute attribute,
1831                                   NvS64 *val)
1832 {
1833     *val = pFrameLockEvo->boardId;
1834 
1835     return TRUE;
1836 }
1837 
1838 static NvBool GetFrameLockFpgaRevisionUnsupported(
1839     NVDispEvoPtr pDispEvo,
1840     NvS64 *val)
1841 {
1842     NVDevEvoPtr pDevEvo = pDispEvo->pDevEvo;
1843     *val = pDevEvo->badFramelockFirmware;
1844 
1845     return TRUE;
1846 }
1847 
1848 static NvBool GetFrameLockSyncDelayResolution(
1849     const NVFrameLockEvoRec *pFrameLockEvo,
1850     enum NvKmsFrameLockAttribute attribute,
1851     NvS64 *val)
1852 {
1853     *val = pFrameLockEvo->syncSkewResolution;
1854 
1855     return TRUE;
1856 }
1857 
1858 NvBool nvSetFrameLockDisplayConfigEvo(NVDpyEvoRec *pDpyEvo, NvS64 val)
1859 {
1860     NVDispEvoPtr pDispEvo = pDpyEvo->pDispEvo;
1861     unsigned int valid;
1862     NvBool removeFromClients = FALSE;
1863     NvBool removeFromServer = FALSE;
1864 
1865     if (!pDispEvo || !pDispEvo->pFrameLockEvo) {
1866         return FALSE;
1867     }
1868 
1869     /* Only set the config when framelock is disabled */
1870 
1871     if (pDispEvo->framelock.syncEnabled) {
1872         return FALSE;
1873     }
1874 
1875     valid = FrameLockGetValidDpyConfig(pDpyEvo);
1876 
1877     /* Display device cannot be set as such */
1878     if (!((1<<val) & valid)) {
1879         return FALSE;
1880     }
1881 
1882     switch (val) {
1883     case NV_KMS_DPY_ATTRIBUTE_FRAMELOCK_DISPLAY_CONFIG_SERVER:
1884         /* alert other dpy it is being disabled as server */
1885         if (!nvDpyIdIsInvalid(pDispEvo->framelock.server) &&
1886             !nvDpyIdsAreEqual(pDispEvo->framelock.server, pDpyEvo->id)) {
1887             NVDpyEvoPtr pOtherDpyEvo;
1888 
1889             pOtherDpyEvo =
1890                 nvGetDpyEvoFromDispEvo(pDispEvo, pDispEvo->framelock.server);
1891             if (pOtherDpyEvo) {
1892                 nvSendDpyAttributeChangedEventEvo(
1893                     pOtherDpyEvo,
1894                     NV_KMS_DPY_ATTRIBUTE_FRAMELOCK_DISPLAY_CONFIG,
1895                     NV_KMS_DPY_ATTRIBUTE_FRAMELOCK_DISPLAY_CONFIG_DISABLED);
1896             }
1897         }
1898         pDispEvo->framelock.server = pDpyEvo->id;
1899         removeFromClients = TRUE;
1900         break;
1901 
1902     case NV_KMS_DPY_ATTRIBUTE_FRAMELOCK_DISPLAY_CONFIG_CLIENT:
1903         pDispEvo->framelock.clients =
1904             nvAddDpyIdToDpyIdList(pDpyEvo->id, pDispEvo->framelock.clients);
1905         removeFromServer = TRUE;
1906         break;
1907 
1908     case NV_KMS_DPY_ATTRIBUTE_FRAMELOCK_DISPLAY_CONFIG_DISABLED:
1909         removeFromClients = TRUE;
1910         removeFromServer = TRUE;
1911         break;
1912 
1913     default:
1914         return FALSE;
1915     }
1916 
1917     if (removeFromClients) {
1918         if (nvDpyIdIsInDpyIdList(pDpyEvo->id, pDispEvo->framelock.clients)) {
1919             pDispEvo->framelock.clients =
1920                 nvDpyIdListMinusDpyId(pDispEvo->framelock.clients, pDpyEvo->id);
1921         }
1922     }
1923 
1924     if (removeFromServer) {
1925         if (nvDpyIdsAreEqual(pDispEvo->framelock.server, pDpyEvo->id)) {
1926             pDispEvo->framelock.server = nvInvalidDpyId();
1927         }
1928     }
1929 
1930     return TRUE;
1931 }
1932 
1933 NvBool nvGetFrameLockDisplayConfigEvo(const NVDpyEvoRec *pDpyEvo, NvS64 *val)
1934 {
1935     NVDispEvoPtr pDispEvo = pDpyEvo->pDispEvo;
1936 
1937     if (!pDispEvo || !pDispEvo->pFrameLockEvo) {
1938         return FALSE;
1939     }
1940 
1941     if (nvDpyIdsAreEqual(pDispEvo->framelock.server, pDpyEvo->id)) {
1942         *val = NV_KMS_DPY_ATTRIBUTE_FRAMELOCK_DISPLAY_CONFIG_SERVER;
1943     } else if (nvDpyIdIsInDpyIdList(pDpyEvo->id, pDispEvo->framelock.clients)) {
1944         *val = NV_KMS_DPY_ATTRIBUTE_FRAMELOCK_DISPLAY_CONFIG_CLIENT;
1945     } else {
1946         *val = NV_KMS_DPY_ATTRIBUTE_FRAMELOCK_DISPLAY_CONFIG_DISABLED;
1947     }
1948 
1949     return TRUE;
1950 }
1951 
1952 NvBool nvGetFrameLockDisplayConfigValidValuesEvo(
1953     const NVDpyEvoRec *pDpyEvo,
1954     struct NvKmsAttributeValidValuesCommonReply *pValidValues)
1955 {
1956     if (pDpyEvo->pDispEvo->pFrameLockEvo == NULL) {
1957         return FALSE;
1958     }
1959 
1960     nvAssert(pValidValues->type == NV_KMS_ATTRIBUTE_TYPE_INTBITS);
1961 
1962     pValidValues->u.bits.ints = FrameLockGetValidDpyConfig(pDpyEvo);
1963 
1964     return TRUE;
1965 }
1966 
1967 static const struct {
1968     NvBool (*set)(NVDispEvoPtr pDispEvo, NvS64 value);
1969     NvBool (*get)(NVDispEvoPtr pDispEvo, NvS64 *pValue);
1970     enum NvKmsAttributeType type;
1971 } DispAttributesDispatchTable[] = {
1972     [NV_KMS_DISP_ATTRIBUTE_FRAMELOCK] = {
1973         .set  = NULL,
1974         .get  = GetFrameLock,
1975         .type = NV_KMS_ATTRIBUTE_TYPE_BOOLEAN,
1976     },
1977     [NV_KMS_DISP_ATTRIBUTE_FRAMELOCK_SYNC] = {
1978         .set  = SetFrameLockSync,
1979         .get  = GetFrameLockSync,
1980         .type = NV_KMS_ATTRIBUTE_TYPE_BOOLEAN,
1981     },
1982     [NV_KMS_DISP_ATTRIBUTE_GPU_FRAMELOCK_FPGA_REVISION_UNSUPPORTED] = {
1983         .set            = NULL,
1984         .get            = GetFrameLockFpgaRevisionUnsupported,
1985         .type           = NV_KMS_ATTRIBUTE_TYPE_BOOLEAN,
1986     },
1987     [NV_KMS_DISP_ATTRIBUTE_FRAMELOCK_STEREO_SYNC] = {
1988         .set  = NULL,
1989         .get  = GetFrameLockStereoSync,
1990         .type = NV_KMS_ATTRIBUTE_TYPE_BOOLEAN,
1991     },
1992     [NV_KMS_DISP_ATTRIBUTE_FRAMELOCK_TIMING] = {
1993         .set  = NULL,
1994         .get  = GetFrameLockTiming,
1995         .type = NV_KMS_ATTRIBUTE_TYPE_BOOLEAN,
1996     },
1997     [NV_KMS_DISP_ATTRIBUTE_FRAMELOCK_TEST_SIGNAL] = {
1998         .set  = SetFrameLockTestSignal,
1999         .get  = GetFrameLockTestSignal,
2000         .type = NV_KMS_ATTRIBUTE_TYPE_BOOLEAN,
2001     },
2002     [NV_KMS_DISP_ATTRIBUTE_FRAMELOCK_RESET] = {
2003         .set  = ResetHardwareOneDisp,
2004         .get  = NULL,
2005         .type = NV_KMS_ATTRIBUTE_TYPE_BOOLEAN,
2006     },
2007     [NV_KMS_DISP_ATTRIBUTE_FRAMELOCK_SET_SWAP_BARRIER] = {
2008         .set  = SetSwapBarrier,
2009         .get  = NULL,
2010         .type = NV_KMS_ATTRIBUTE_TYPE_BOOLEAN,
2011     },
2012     [NV_KMS_DISP_ATTRIBUTE_ALLOW_FLIPLOCK] = {
2013         .set  = nvAllowFlipLockEvo,
2014         .get  = NULL,
2015         .type = NV_KMS_ATTRIBUTE_TYPE_BOOLEAN,
2016     },
2017     [NV_KMS_DISP_ATTRIBUTE_QUERY_DP_AUX_LOG] = {
2018         .set  = NULL,
2019         .get  = nvRmQueryDpAuxLog,
2020         .type = NV_KMS_ATTRIBUTE_TYPE_BOOLEAN,
2021     },
2022 };
2023 
2024 
2025 /*!
2026  * Set pParams->attribute to pParams->value on the given disp.
2027  */
2028 NvBool nvSetDispAttributeEvo(NVDispEvoPtr pDispEvo,
2029                              struct NvKmsSetDispAttributeParams *pParams)
2030 {
2031     NvU32 index = pParams->request.attribute;
2032 
2033     if (index >= ARRAY_LEN(DispAttributesDispatchTable)) {
2034         return FALSE;
2035     }
2036 
2037     if (DispAttributesDispatchTable[index].set == NULL) {
2038         return FALSE;
2039     }
2040 
2041     return DispAttributesDispatchTable[index].set(pDispEvo,
2042                                                   pParams->request.value);
2043 }
2044 
2045 
2046 /*!
2047  * Get the value of pParams->attribute on the given disp.
2048  */
2049 NvBool nvGetDispAttributeEvo(NVDispEvoPtr pDispEvo,
2050                              struct NvKmsGetDispAttributeParams *pParams)
2051 {
2052     NvU32 index = pParams->request.attribute;
2053 
2054     if (index >= ARRAY_LEN(DispAttributesDispatchTable)) {
2055         return FALSE;
2056     }
2057 
2058     if (DispAttributesDispatchTable[index].get == NULL) {
2059         return FALSE;
2060     }
2061 
2062     return DispAttributesDispatchTable[index].get(pDispEvo,
2063                                                   &pParams->reply.value);
2064 }
2065 
2066 
2067 /*!
2068  * Get the valid values of pParams->attribute on the given disp.
2069  */
2070 NvBool nvGetDispAttributeValidValuesEvo(
2071     const NVDispEvoRec *pDispEvo,
2072     struct NvKmsGetDispAttributeValidValuesParams *pParams)
2073 {
2074     struct NvKmsAttributeValidValuesCommonReply *pReply =
2075         &pParams->reply.common;
2076     NvU32 index = pParams->request.attribute;
2077 
2078     if (index >= ARRAY_LEN(DispAttributesDispatchTable)) {
2079         return FALSE;
2080     }
2081 
2082     /*
2083      * FRAMELOCK and GPU_FRAMELOCK_FPGA_REVISION_UNSUPPORTED
2084      * can be queried without a pFrameLockEvo; all other
2085      * attributes require a pFrameLockEvo.
2086      */
2087     if (((pParams->request.attribute != NV_KMS_DISP_ATTRIBUTE_FRAMELOCK) &&
2088          (pParams->request.attribute !=
2089           NV_KMS_DISP_ATTRIBUTE_GPU_FRAMELOCK_FPGA_REVISION_UNSUPPORTED)) &&
2090         (pDispEvo->pFrameLockEvo == NULL)) {
2091         return FALSE;
2092     }
2093 
2094     nvkms_memset(pReply, 0, sizeof(*pReply));
2095 
2096     pReply->readable = (DispAttributesDispatchTable[index].get != NULL);
2097     pReply->writable = (DispAttributesDispatchTable[index].set != NULL);
2098 
2099     pReply->type = DispAttributesDispatchTable[index].type;
2100 
2101     return TRUE;
2102 }
2103 
2104 
2105 static const struct {
2106     NvBool (*set)(NVFrameLockEvoPtr pFrameLockEvo, NvS64 value);
2107     NvBool (*get)(const NVFrameLockEvoRec *pFrameLockEvo,
2108                   enum NvKmsFrameLockAttribute attribute, NvS64 *pValue);
2109     NvBool (*getValidValues)(
2110         const NVFrameLockEvoRec *pFrameLockEvo,
2111         struct NvKmsAttributeValidValuesCommonReply *pValidValues);
2112     enum NvKmsAttributeType type;
2113 } FrameLockAttributesDispatchTable[] = {
2114     [NV_KMS_FRAMELOCK_ATTRIBUTE_POLARITY] = {
2115         .set            = SetFrameLockPolarity,
2116         .get            = GetFrameLockPolarity,
2117         .getValidValues = NULL,
2118         .type           = NV_KMS_ATTRIBUTE_TYPE_BITMASK,
2119     },
2120     [NV_KMS_FRAMELOCK_ATTRIBUTE_SYNC_DELAY] = {
2121         .set            = FrameLockSetSyncDelay,
2122         .get            = GetFrameLockSyncDelay,
2123         .getValidValues = GetFrameLockSyncDelayValidValues,
2124         .type           = NV_KMS_ATTRIBUTE_TYPE_RANGE,
2125     },
2126     [NV_KMS_FRAMELOCK_ATTRIBUTE_HOUSE_SYNC_MODE] = {
2127         .set            = SetHouseSyncMode,
2128         .get            = GetHouseSyncMode,
2129         .getValidValues = GetHouseSyncModeValidValues,
2130         .type           = NV_KMS_ATTRIBUTE_TYPE_INTBITS,
2131     },
2132     [NV_KMS_FRAMELOCK_ATTRIBUTE_SYNC_INTERVAL] = {
2133         .set            = FrameLockSetSyncInterval,
2134         .get            = GetFrameLockSyncInterval,
2135         .getValidValues = GetFrameLockSyncIntervalValidValues,
2136         .type           = NV_KMS_ATTRIBUTE_TYPE_RANGE,
2137     },
2138     [NV_KMS_FRAMELOCK_ATTRIBUTE_SYNC_READY] = {
2139         .set            = NULL,
2140         .get            = GetFrameLockSyncReady,
2141         .getValidValues = NULL,
2142         .type           = NV_KMS_ATTRIBUTE_TYPE_BOOLEAN,
2143     },
2144     [NV_KMS_FRAMELOCK_ATTRIBUTE_VIDEO_MODE] = {
2145         .set            = SetFrameLockVideoMode,
2146         .get            = GetFrameLockVideoMode,
2147         .getValidValues = GetFrameLockVideoModeValidValues,
2148         .type           = NV_KMS_ATTRIBUTE_TYPE_RANGE,
2149     },
2150     [NV_KMS_FRAMELOCK_ATTRIBUTE_FPGA_REVISION] = {
2151         .set            = NULL,
2152         .get            = GetFrameLockFpgaRevision,
2153         .getValidValues = NULL,
2154         .type           = NV_KMS_ATTRIBUTE_TYPE_INTEGER,
2155     },
2156     [NV_KMS_FRAMELOCK_ATTRIBUTE_FIRMWARE_MAJOR_VERSION] = {
2157         .set            = NULL,
2158         .get            = GetFrameLockFirmwareMajorVersion,
2159         .getValidValues = NULL,
2160         .type           = NV_KMS_ATTRIBUTE_TYPE_INTEGER,
2161     },
2162     [NV_KMS_FRAMELOCK_ATTRIBUTE_FIRMWARE_MINOR_VERSION] = {
2163         .set            = NULL,
2164         .get            = GetFrameLockFirmwareMinorVersion,
2165         .getValidValues = NULL,
2166         .type           = NV_KMS_ATTRIBUTE_TYPE_INTEGER,
2167     },
2168     [NV_KMS_FRAMELOCK_ATTRIBUTE_BOARD_ID] = {
2169         .set            = NULL,
2170         .get            = GetFrameLockBoardId,
2171         .getValidValues = NULL,
2172         .type           = NV_KMS_ATTRIBUTE_TYPE_INTEGER,
2173     },
2174     [NV_KMS_FRAMELOCK_ATTRIBUTE_SYNC_DELAY_RESOLUTION] = {
2175         .set            = NULL,
2176         .get            = GetFrameLockSyncDelayResolution,
2177         .getValidValues = NULL,
2178         .type           = NV_KMS_ATTRIBUTE_TYPE_INTEGER,
2179     },
2180     [NV_KMS_FRAMELOCK_ATTRIBUTE_PORT0_STATUS] = {
2181         .set            = NULL,
2182         .get            = nvFrameLockGetStatusEvo,
2183         .getValidValues = NULL,
2184         .type           = NV_KMS_ATTRIBUTE_TYPE_BOOLEAN,
2185     },
2186     [NV_KMS_FRAMELOCK_ATTRIBUTE_PORT1_STATUS] = {
2187         .set            = NULL,
2188         .get            = nvFrameLockGetStatusEvo,
2189         .getValidValues = NULL,
2190         .type           = NV_KMS_ATTRIBUTE_TYPE_BOOLEAN,
2191     },
2192     [NV_KMS_FRAMELOCK_ATTRIBUTE_HOUSE_STATUS] = {
2193         .set            = NULL,
2194         .get            = nvFrameLockGetStatusEvo,
2195         .getValidValues = NULL,
2196         .type           = NV_KMS_ATTRIBUTE_TYPE_BOOLEAN,
2197     },
2198     [NV_KMS_FRAMELOCK_ATTRIBUTE_ETHERNET_DETECTED] = {
2199         .set            = NULL,
2200         .get            = nvFrameLockGetStatusEvo,
2201         .getValidValues = NULL,
2202         .type           = NV_KMS_ATTRIBUTE_TYPE_BITMASK,
2203     },
2204     [NV_KMS_FRAMELOCK_ATTRIBUTE_SYNC_RATE] = {
2205         .set            = NULL,
2206         .get            = nvFrameLockGetStatusEvo,
2207         .getValidValues = NULL,
2208         .type           = NV_KMS_ATTRIBUTE_TYPE_INTEGER,
2209     },
2210     [NV_KMS_FRAMELOCK_ATTRIBUTE_SYNC_RATE_4] = {
2211         .set            = NULL,
2212         .get            = nvFrameLockGetStatusEvo,
2213         .getValidValues = NULL,
2214         .type           = NV_KMS_ATTRIBUTE_TYPE_INTEGER,
2215     },
2216     [NV_KMS_FRAMELOCK_ATTRIBUTE_INCOMING_HOUSE_SYNC_RATE] = {
2217         .set            = NULL,
2218         .get            = nvFrameLockGetStatusEvo,
2219         .getValidValues = NULL,
2220         .type           = NV_KMS_ATTRIBUTE_TYPE_INTEGER,
2221     },
2222     [NV_KMS_FRAMELOCK_ATTRIBUTE_MULTIPLY_DIVIDE_VALUE] = {
2223         .set            = SetFrameLockMulDivVal,
2224         .get            = GetFrameLockMulDivVal,
2225         .getValidValues = GetFrameLockMulDivValValidValues,
2226         .type           = NV_KMS_ATTRIBUTE_TYPE_RANGE,
2227     },
2228     [NV_KMS_FRAMELOCK_ATTRIBUTE_MULTIPLY_DIVIDE_MODE] = {
2229         .set            = SetFrameLockMulDivMode,
2230         .get            = GetFrameLockMulDivMode,
2231         .getValidValues = GetFrameLockMulDivModeValidValues,
2232         .type           = NV_KMS_ATTRIBUTE_TYPE_INTEGER,
2233     },
2234 };
2235 
2236 NvBool nvSetFrameLockAttributeEvo(
2237     NVFrameLockEvoRec *pFrameLockEvo,
2238     const struct NvKmsSetFrameLockAttributeParams *pParams)
2239 {
2240     NvU32 index = pParams->request.attribute;
2241 
2242     if (index >= ARRAY_LEN(FrameLockAttributesDispatchTable)) {
2243         return FALSE;
2244     }
2245 
2246     if (FrameLockAttributesDispatchTable[index].set == NULL) {
2247         return FALSE;
2248     }
2249 
2250     if ((FrameLockAttributesDispatchTable[index].type ==
2251          NV_KMS_ATTRIBUTE_TYPE_BOOLEAN) &&
2252         (pParams->request.value != TRUE) &&
2253         (pParams->request.value != FALSE)) {
2254         return FALSE;
2255     }
2256 
2257     return FrameLockAttributesDispatchTable[index].set(pFrameLockEvo,
2258                                                        pParams->request.value);
2259 }
2260 
2261 NvBool nvGetFrameLockAttributeEvo(
2262     const NVFrameLockEvoRec *pFrameLockEvo,
2263     struct NvKmsGetFrameLockAttributeParams *pParams)
2264 {
2265     NvU32 index = pParams->request.attribute;
2266 
2267     if (index >= ARRAY_LEN(FrameLockAttributesDispatchTable)) {
2268         return FALSE;
2269     }
2270 
2271     if (FrameLockAttributesDispatchTable[index].get == NULL) {
2272         return FALSE;
2273     }
2274 
2275     return FrameLockAttributesDispatchTable[index].get(pFrameLockEvo,
2276                                                        pParams->request.attribute,
2277                                                        &pParams->reply.value);
2278 }
2279 
2280 NvBool nvGetFrameLockAttributeValidValuesEvo(
2281     const NVFrameLockEvoRec *pFrameLockEvo,
2282     struct NvKmsGetFrameLockAttributeValidValuesParams *pParams)
2283 {
2284     struct NvKmsAttributeValidValuesCommonReply *pReply =
2285         &pParams->reply.common;
2286     NvU32 index = pParams->request.attribute;
2287 
2288     if (index >= ARRAY_LEN(FrameLockAttributesDispatchTable)) {
2289         return FALSE;
2290     }
2291 
2292     nvkms_memset(pReply, 0, sizeof(*pReply));
2293 
2294     pReply->readable = (FrameLockAttributesDispatchTable[index].get != NULL);
2295     pReply->writable = (FrameLockAttributesDispatchTable[index].set != NULL);
2296 
2297     pReply->type = FrameLockAttributesDispatchTable[index].type;
2298 
2299     /*
2300      * The getValidValues function provides two important things:
2301      * - If type==Range, then assigns reply::u::range.
2302      * - If the attribute is not currently available, returns FALSE.
2303      * If the getValidValues function is NULL, assume the attribute is
2304      * available.  The type must not be something requires assigning
2305      * to reply::u.
2306      */
2307     if (FrameLockAttributesDispatchTable[index].getValidValues == NULL) {
2308         nvAssert(pReply->type != NV_KMS_ATTRIBUTE_TYPE_RANGE);
2309         return TRUE;
2310     }
2311 
2312     return FrameLockAttributesDispatchTable[index].getValidValues(
2313             pFrameLockEvo, pReply);
2314 }
2315 
2316 NvU32 nvGetFramelockServerHead(const NVDispEvoRec *pDispEvo)
2317 {
2318     const NVDpyEvoRec *pDpyEvo =
2319         nvGetDpyEvoFromDispEvo(pDispEvo, pDispEvo->framelock.server);
2320     return (pDpyEvo != NULL) ? nvGetPrimaryHwHead(pDispEvo, pDpyEvo->apiHead) :
2321                 NV_INVALID_HEAD;
2322 }
2323 
2324 NvU32 nvGetFramelockClientHeadsMask(const NVDispEvoRec *pDispEvo)
2325 {
2326     NvU32 headsMask = 0x0;
2327     const NVDpyEvoRec *pServerDpyEvo, *pClientDpyEvo;
2328 
2329     pServerDpyEvo = nvGetDpyEvoFromDispEvo(pDispEvo,
2330                                            pDispEvo->framelock.server);
2331     if ((pServerDpyEvo != NULL) &&
2332             (pServerDpyEvo->apiHead != NV_INVALID_HEAD)) {
2333         const NVDispApiHeadStateEvoRec *pApiHeadState =
2334             &pDispEvo->apiHeadState[pServerDpyEvo->apiHead];
2335         NvU32 primaryHead = nvGetPrimaryHwHead(pDispEvo,
2336                                                pServerDpyEvo->apiHead);
2337 
2338         nvAssert(primaryHead != NV_INVALID_HEAD);
2339 
2340         /*
2341          * The secondary hardware-head of the server dpy are client of the
2342          * primary head.
2343          */
2344         headsMask |= pApiHeadState->hwHeadsMask;
2345         headsMask &= ~NVBIT(primaryHead);
2346     }
2347 
2348     FOR_ALL_EVO_DPYS(pClientDpyEvo, pDispEvo->framelock.clients, pDispEvo) {
2349         if (pClientDpyEvo->apiHead == NV_INVALID_HEAD) {
2350             continue;
2351         }
2352         const NVDispApiHeadStateEvoRec *pApiHeadState =
2353             &pDispEvo->apiHeadState[pClientDpyEvo->apiHead];
2354         headsMask |= pApiHeadState->hwHeadsMask;
2355     }
2356 
2357     return headsMask;
2358 }
2359 
2360 void nvUpdateGLSFramelock(const NVDispEvoRec *pDispEvo, const NvU32 head,
2361                           const NvBool enable, const NvBool server)
2362 {
2363     NVDpyEvoRec *pDpyEvo;
2364     NvS64 value = enable | (server << 1);
2365 
2366     /*
2367      * XXX[2Heads1OR] Optimize this loop in follow on code change when
2368      * apiHead -> pDpyEvo mapping will get implemented.
2369      */
2370     FOR_ALL_EVO_DPYS(pDpyEvo, pDispEvo->validDisplays, pDispEvo) {
2371         /*
2372          * XXX[2Heads1OR] Framelock is currently not supported with
2373          * 2Heads1OR, the api head is expected to be mapped onto a single
2374          * hardware head which is the primary hardware head.
2375          */
2376         if ((pDpyEvo->apiHead == NV_INVALID_HEAD) ||
2377                 (nvGetPrimaryHwHead(pDispEvo, pDpyEvo->apiHead) != head)) {
2378             continue;
2379         }
2380 
2381         nvSendDpyAttributeChangedEventEvo(pDpyEvo,
2382             NV_KMS_DPY_ATTRIBUTE_UPDATE_GLS_FRAMELOCK,
2383             value);
2384     }
2385 }
2386