1 /*
2  * SPDX-FileCopyrightText: Copyright (c) 2017-2020 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-types.h"
25 #include "nvkms-headsurface.h"
26 #include "nvkms-headsurface-3d.h"
27 #include "nvkms-headsurface-priv.h"
28 #include "nvkms-headsurface-swapgroup.h"
29 #include "nvkms-utils.h"
30 #include "nvkms-rmapi.h"
31 #include "nvkms-surface.h"
32 #include "nvkms-sync.h"
33 #include "nvkms-flip.h"
34 #include "nvkms-private.h"
35 #include "nvkms-evo.h"
36 #include "nvkms-dma.h"
37 #include "nvkms-modeset.h"
38 #include "nvkms-rm.h"
39 
40 #include <class/cl0040.h> /* NV01_MEMORY_LOCAL_USER */
41 
42 static NvBool AllocNotifiers(NVHsDeviceEvoRec *pHsDevice);
43 static void FreeNotifiers(NVHsDeviceEvoRec *pHsDevice);
44 static void HsProcFsRecordFullscreenSgFrames(NVHsChannelEvoPtr pHsChannel,
45                                              NvBool isFullscreen);
46 
GetLog2GobsPerBlockY(NvU32 height)47 static NvU32 GetLog2GobsPerBlockY(NvU32 height)
48 {
49     NvU32 log2GobsPerBlockY = 4; // 16 gobs/block
50 
51     const NvU64 heightAndOneHalf = (NvU64)height + ((NvU64)height/2ULL);
52     const NvU64 nvFermiBlockLinearGobHeight = NVKMS_BLOCK_LINEAR_GOB_HEIGHT;
53 
54     // If we're wasting too much memory, cap the block height
55     while ((log2GobsPerBlockY > 0U) &&
56            (((nvFermiBlockLinearGobHeight * ((NvU64)1ULL << log2GobsPerBlockY))) >
57             heightAndOneHalf)) {
58         log2GobsPerBlockY--;
59     }
60 
61     // If there is more than one gob per block,
62     if (log2GobsPerBlockY > 0U) {
63 
64         // Proposed shrunk block size.
65         // compute a new proposedBlockSize, based on a gob size that is half
66         // of the current value (log2 - 1).  the "if(log2 > 0)" above keeps this
67         // value always ">= 0".
68         NvU32 proposedBlockSize =
69             NVKMS_BLOCK_LINEAR_GOB_HEIGHT << (log2GobsPerBlockY - 1U);
70 
71         // While the proposedBlockSize is greater than the image size,
72         while (proposedBlockSize >= height) {
73             // It's safe to cut the gobs per block in half.
74             --log2GobsPerBlockY;
75 
76             // If we've hit 1 gob per block, stop.
77             if (log2GobsPerBlockY == 0U) {
78                 break;
79             }
80             // Otherwise, divide the proposed block dimension/size by two.
81             proposedBlockSize /= 2U;
82         }
83     }
84 
85     return log2GobsPerBlockY;
86 }
87 
GetLog2GobsPerBlock(NvU32 bytesPerPixel,NvU32 widthInPixels,NvU32 heightInPixels,NvU32 * pLog2GobsPerBlockY,NvU32 * pitchInBlocks,NvU64 * sizeInBytes)88 static void GetLog2GobsPerBlock(
89     NvU32 bytesPerPixel,
90     NvU32 widthInPixels,
91     NvU32 heightInPixels,
92     NvU32 *pLog2GobsPerBlockY,
93     NvU32 *pitchInBlocks,
94     NvU64 *sizeInBytes)
95 {
96     NvU32 xAlign, yAlign, pitchInBytes, lines;
97 
98     NvU32 log2GobsPerBlockY = GetLog2GobsPerBlockY(heightInPixels);
99 
100     xAlign = NVKMS_BLOCK_LINEAR_GOB_WIDTH - 1;
101     yAlign = (NVKMS_BLOCK_LINEAR_GOB_HEIGHT << log2GobsPerBlockY) - 1;
102 
103     pitchInBytes = NV_ALIGN_UP(widthInPixels * bytesPerPixel, xAlign);
104     lines = NV_ALIGN_UP(heightInPixels, yAlign);
105 
106     *pLog2GobsPerBlockY = log2GobsPerBlockY;
107     *sizeInBytes = (NvU64)pitchInBytes * lines;
108     *pitchInBlocks = pitchInBytes / NVKMS_BLOCK_LINEAR_GOB_WIDTH;
109 }
110 
AllocSurfaceVidmem(const NVDevEvoRec * pDevEvo,NvU32 handle,NvU64 sizeInBytes)111 static NvU32 AllocSurfaceVidmem(
112     const NVDevEvoRec *pDevEvo,
113     NvU32 handle,
114     NvU64 sizeInBytes)
115 {
116     NV_MEMORY_ALLOCATION_PARAMS memAllocParams = { };
117 
118     memAllocParams.owner = NVKMS_RM_HEAP_ID;
119     memAllocParams.size = sizeInBytes;
120     memAllocParams.type = NVOS32_TYPE_IMAGE;
121 
122     memAllocParams.attr = DRF_DEF(OS32, _ATTR, _LOCATION, _VIDMEM) |
123                           DRF_DEF(OS32, _ATTR, _PHYSICALITY, _CONTIGUOUS) |
124                           DRF_DEF(OS32, _ATTR, _FORMAT, _BLOCK_LINEAR);
125 
126     memAllocParams.attr2 = DRF_DEF(OS32, _ATTR2, _GPU_CACHEABLE, _DEFAULT);
127 
128     memAllocParams.flags = NVOS32_ALLOC_FLAGS_FORCE_MEM_GROWS_DOWN |
129                            NVOS32_ALLOC_FLAGS_ALIGNMENT_FORCE;
130 
131     memAllocParams.alignment = NV_EVO_SURFACE_ALIGNMENT;
132 
133     return nvRmApiAlloc(nvEvoGlobal.clientHandle,
134                         pDevEvo->deviceHandle,
135                         handle,
136                         NV01_MEMORY_LOCAL_USER,
137                         &memAllocParams);
138 }
139 
nvHsMapSurfaceToDevice(const NVDevEvoRec * pDevEvo,const NvU32 rmHandle,const NvU64 sizeInBytes,const enum NvHsMapPermissions hsMapPermissions)140 NvU64 nvHsMapSurfaceToDevice(
141     const NVDevEvoRec *pDevEvo,
142     const NvU32 rmHandle,
143     const NvU64 sizeInBytes,
144     const enum NvHsMapPermissions hsMapPermissions)
145 {
146     NvU32 ret;
147     NvU32 flags = DRF_DEF(OS46, _FLAGS, _CACHE_SNOOP, _ENABLE);
148     NvU64 gpuAddress = 0;
149 
150     /* pHsDevice could be NULL if we are in no3d mode. */
151 
152     if (pDevEvo->pHsDevice == NULL) {
153         return gpuAddress;
154     }
155 
156     switch (hsMapPermissions) {
157     case NvHsMapPermissionsNone:
158         return gpuAddress;
159     case NvHsMapPermissionsReadWrite:
160         flags |= DRF_DEF(OS46, _FLAGS, _ACCESS, _READ_WRITE);
161         break;
162     case NvHsMapPermissionsReadOnly:
163         flags |= DRF_DEF(OS46, _FLAGS, _ACCESS, _READ_ONLY);
164         break;
165     }
166 
167     ret = nvRmApiMapMemoryDma(nvEvoGlobal.clientHandle,
168                               pDevEvo->deviceHandle,
169                               pDevEvo->nvkmsGpuVASpace,
170                               rmHandle,
171                               0, /* offset */
172                               sizeInBytes,
173                               flags,
174                               &gpuAddress);
175 
176     if (ret == NVOS_STATUS_SUCCESS) {
177         return gpuAddress;
178     } else {
179         return NV_HS_BAD_GPU_ADDRESS;
180     }
181 }
182 
nvHsUnmapSurfaceFromDevice(const NVDevEvoRec * pDevEvo,const NvU32 rmHandle,const NvU64 gpuAddress)183 void nvHsUnmapSurfaceFromDevice(
184     const NVDevEvoRec *pDevEvo,
185     const NvU32 rmHandle,
186     const NvU64 gpuAddress)
187 {
188     if ((gpuAddress == 0) || (gpuAddress == NV_HS_BAD_GPU_ADDRESS)) {
189         return;
190     }
191 
192     if (pDevEvo->pHsDevice == NULL) {
193         return;
194     }
195 
196     nvRmApiUnmapMemoryDma(nvEvoGlobal.clientHandle,
197                           pDevEvo->deviceHandle,
198                           pDevEvo->nvkmsGpuVASpace,
199                           rmHandle,
200                           0, /* flags */
201                           gpuAddress);
202 }
203 
204 /*!
205  * Free an NVHsSurfaceRec, allocated by nvHsAllocSurface().
206  *
207  * \param[in]  pDevEvo     The device.
208  * \param[in]  pHsSurface  The NVHsSurfaceRec to free.
209  */
nvHsFreeSurface(NVDevEvoRec * pDevEvo,NVHsSurfaceRec * pHsSurface)210 void nvHsFreeSurface(
211     NVDevEvoRec *pDevEvo,
212     NVHsSurfaceRec *pHsSurface)
213 {
214     if (pHsSurface == NULL) {
215         return;
216     }
217 
218     if (pHsSurface->rmHandle != 0) {
219         nvRmApiFree(nvEvoGlobal.clientHandle,
220                     pDevEvo->deviceHandle,
221                     pHsSurface->rmHandle);
222 
223         nvFreeUnixRmHandle(&pDevEvo->handleAllocator, pHsSurface->rmHandle);
224         pHsSurface->rmHandle = 0;
225     }
226 
227     if (pHsSurface->nvKmsHandle != 0) {
228         nvEvoUnregisterSurface(pDevEvo,
229                                pDevEvo->pNvKmsOpenDev,
230                                pHsSurface->nvKmsHandle,
231                                FALSE /* skipUpdate */);
232     }
233 
234     nvFree(pHsSurface);
235 }
236 
nvHsGetNvKmsSurface(const NVDevEvoRec * pDevEvo,NvKmsSurfaceHandle surfaceHandle,const NvBool requireDisplayHardwareAccess)237 NVSurfaceEvoRec *nvHsGetNvKmsSurface(const NVDevEvoRec *pDevEvo,
238                                      NvKmsSurfaceHandle surfaceHandle,
239                                      const NvBool requireDisplayHardwareAccess)
240 {
241     const NVEvoApiHandlesRec *pNvKmsOpenDevSurfaceHandles;
242     NVSurfaceEvoRec *pKmsSurface;
243 
244     pNvKmsOpenDevSurfaceHandles =
245         nvGetSurfaceHandlesFromOpenDevConst(pDevEvo->pNvKmsOpenDev);
246 
247     nvAssert(pNvKmsOpenDevSurfaceHandles != NULL);
248 
249     pKmsSurface =
250         nvEvoGetSurfaceFromHandleNoDispHWAccessOk(pDevEvo,
251                                                   pNvKmsOpenDevSurfaceHandles,
252                                                   surfaceHandle);
253     nvAssert(pKmsSurface != NULL);
254     nvAssert(pKmsSurface->requireDisplayHardwareAccess == requireDisplayHardwareAccess);
255 
256     return pKmsSurface;
257 }
258 
259 /*!
260  * Allocate an NVHsSurfaceRec, for use with headSurface.
261  *
262  * Video memory is allocated, mapped into the device's GPU virtual address
263  * space, and registered with NVKMS's pNvKmsOpenDev.
264  *
265  * Note the video memory is not cleared here, because the corresponding graphics
266  * channel may not be allocated, yet.
267  *
268  * \param[in]  pDevEvo                      The device.
269  * \param[in]  requireDisplayHardwareAccess Whether display hardware requires access.
270  * \param[in]  format                       The format of the surface.
271  * \param[in]  widthInPixels                The width of the surface, in pixels.
272  * \param[in]  heightInPixels               The height of the surface, in pixels.
273  *
274  * \return  On success, an allocate NVHsSurfaceRec structure is returned.
275  *          On failure, NULL is returned.
276  */
nvHsAllocSurface(NVDevEvoRec * pDevEvo,const NvBool requireDisplayHardwareAccess,const enum NvKmsSurfaceMemoryFormat format,const NvU32 widthInPixels,const NvU32 heightInPixels)277 NVHsSurfaceRec *nvHsAllocSurface(
278     NVDevEvoRec *pDevEvo,
279     const NvBool requireDisplayHardwareAccess,
280     const enum NvKmsSurfaceMemoryFormat format,
281     const NvU32 widthInPixels,
282     const NvU32 heightInPixels)
283 {
284     struct NvKmsRegisterSurfaceParams nvKmsParams = { };
285     const NvKmsSurfaceMemoryFormatInfo *pFormatInfo =
286         nvKmsGetSurfaceMemoryFormatInfo(format);
287     NvU32 pitchInBlocks = 0;
288     NvU64 sizeInBytes = 0;
289     NvU32 log2GobsPerBlockY = 0;
290     NvU32 ret = 0;
291     NVHsSurfaceRec *pHsSurface = nvCalloc(1, sizeof(*pHsSurface));
292 
293     if (pHsSurface == NULL) {
294         return NULL;
295     }
296 
297     GetLog2GobsPerBlock(pFormatInfo->rgb.bytesPerPixel,
298                         widthInPixels,
299                         heightInPixels,
300                         &log2GobsPerBlockY,
301                         &pitchInBlocks,
302                         &sizeInBytes);
303 
304     sizeInBytes = NV_ALIGN_UP(sizeInBytes, NV_EVO_SURFACE_ALIGNMENT);
305 
306     pHsSurface->rmHandle = nvGenerateUnixRmHandle(&pDevEvo->handleAllocator);
307 
308     if (pHsSurface->rmHandle == 0) {
309         goto fail;
310     }
311 
312     ret = AllocSurfaceVidmem(pDevEvo, pHsSurface->rmHandle, sizeInBytes);
313 
314     if (ret != NVOS_STATUS_SUCCESS) {
315         nvFreeUnixRmHandle(&pDevEvo->handleAllocator, pHsSurface->rmHandle);
316         pHsSurface->rmHandle = 0;
317 
318         goto fail;
319     }
320 
321     pHsSurface->gobsPerBlock.y = log2GobsPerBlockY;
322 
323     /*
324      * For blocklinear surfaces, the NVKMS pitch is in units of blocks, which
325      * matches what GetLog2GobsPerBlock() returned to us.
326      */
327     nvKmsParams.request.useFd = FALSE;
328     nvKmsParams.request.rmClient = nvEvoGlobal.clientHandle;
329     nvKmsParams.request.widthInPixels = widthInPixels;
330     nvKmsParams.request.heightInPixels = heightInPixels;
331     nvKmsParams.request.layout = NvKmsSurfaceMemoryLayoutBlockLinear;
332     nvKmsParams.request.format = format;
333     nvKmsParams.request.noDisplayHardwareAccess = !requireDisplayHardwareAccess;
334     nvKmsParams.request.log2GobsPerBlockY = log2GobsPerBlockY;
335 
336     nvKmsParams.request.planes[0].u.rmObject = pHsSurface->rmHandle;
337     nvKmsParams.request.planes[0].pitch = pitchInBlocks;
338     nvKmsParams.request.planes[0].rmObjectSizeInBytes = sizeInBytes;
339 
340     nvEvoRegisterSurface(pDevEvo, pDevEvo->pNvKmsOpenDev, &nvKmsParams,
341                          NvHsMapPermissionsReadWrite);
342 
343     if (nvKmsParams.reply.surfaceHandle == 0) {
344         goto fail;
345     }
346 
347     pHsSurface->nvKmsHandle = nvKmsParams.reply.surfaceHandle;
348 
349     pHsSurface->pSurfaceEvo =
350         nvHsGetNvKmsSurface(pDevEvo, pHsSurface->nvKmsHandle, requireDisplayHardwareAccess);
351 
352     if (pHsSurface->pSurfaceEvo == NULL) {
353         goto fail;
354     }
355 
356     return pHsSurface;
357 
358 fail:
359     nvHsFreeSurface(pDevEvo, pHsSurface);
360 
361     return NULL;
362 }
363 
nvHsAllocDevice(NVDevEvoRec * pDevEvo,const struct NvKmsAllocDeviceRequest * pRequest)364 NvBool nvHsAllocDevice(
365     NVDevEvoRec *pDevEvo,
366     const struct NvKmsAllocDeviceRequest *pRequest)
367 {
368     NVHsDeviceEvoRec *pHsDevice;
369 
370     nvAssert(pDevEvo->pHsDevice == NULL);
371 
372     if (!pDevEvo->isHeadSurfaceSupported) {
373         return TRUE;
374     }
375 
376     if (pRequest->no3d) {
377         return TRUE;
378     }
379 
380     pHsDevice = nvCalloc(1, sizeof(*pHsDevice));
381 
382     if (pHsDevice == NULL) {
383         goto fail;
384     }
385 
386     pDevEvo->pHsDevice = pHsDevice;
387     pHsDevice->pDevEvo = pDevEvo;
388 
389     nvAssert(pDevEvo->nvkmsGpuVASpace);
390 
391     if (!nvHs3dAllocDevice(pHsDevice)) {
392         goto fail;
393     }
394 
395     if (!AllocNotifiers(pHsDevice)) {
396         goto fail;
397     }
398 
399     return TRUE;
400 
401 fail:
402     nvHsFreeDevice(pDevEvo);
403 
404     return FALSE;
405 }
406 
nvHsFreeDevice(NVDevEvoRec * pDevEvo)407 void nvHsFreeDevice(NVDevEvoRec *pDevEvo)
408 {
409     NVHsDeviceEvoRec *pHsDevice = pDevEvo->pHsDevice;
410 
411     if (pHsDevice == NULL) {
412         return;
413     }
414 
415     FreeNotifiers(pHsDevice);
416 
417     nvHs3dFreeDevice(pHsDevice);
418 
419     nvFree(pHsDevice);
420 
421     pDevEvo->pHsDevice = NULL;
422 }
423 
nvHsAllocChannel(NVDispEvoRec * pDispEvo,NvU32 apiHead)424 NVHsChannelEvoPtr nvHsAllocChannel(NVDispEvoRec *pDispEvo, NvU32 apiHead)
425 {
426     NVHsChannelEvoRec *pHsChannel = nvCalloc(1, sizeof(*pHsChannel));
427 
428     if (pHsChannel == NULL) {
429         goto fail;
430     }
431 
432     pHsChannel->pDispEvo = pDispEvo;
433     pHsChannel->apiHead = apiHead;
434 
435     if (!nvHs3dAllocChannel(pHsChannel)) {
436         goto fail;
437     }
438 
439     return pHsChannel;
440 
441 fail:
442     nvHsFreeChannel(pHsChannel);
443 
444     return NULL;
445 }
446 
nvHsFreeChannel(NVHsChannelEvoPtr pHsChannel)447 void nvHsFreeChannel(NVHsChannelEvoPtr pHsChannel)
448 {
449     if (pHsChannel == NULL) {
450         return;
451     }
452 
453     nvHs3dFreeChannel(pHsChannel);
454 
455     nvFree(pHsChannel);
456 }
457 
HsGetSemaphoreIndex(const NVFlipNIsoSurfaceEvoHwState * pSemaSurface)458 static NvU32 HsGetSemaphoreIndex(
459     const NVFlipNIsoSurfaceEvoHwState *pSemaSurface)
460 {
461     const NvU32 offsetInBytes = pSemaSurface->offsetInWords * 4;
462     const enum NvKmsNIsoFormat format = pSemaSurface->format;
463     const NvU32 sizeOfSemaphore = nvKmsSizeOfSemaphore(format);
464 
465     /*
466      * The semaphore size must be greater than zero.  Flip validation should
467      * prevent us from getting here with an invalid NvKmsNIsoFormat.
468      */
469     nvAssert(sizeOfSemaphore > 0);
470 
471     /* The semaphore offset should be a multiple of the semaphore size. */
472     nvAssert((offsetInBytes % sizeOfSemaphore) == 0);
473 
474     return offsetInBytes / sizeOfSemaphore;
475 }
476 
477 /*!
478  * Read the payload of the semaphore described in the pSemaSurface.
479  */
HsFlipQueueReadSemaphore(const NVHsChannelEvoRec * pHsChannel,const NVFlipNIsoSurfaceEvoHwState * pSemaSurface)480 static NvU32 HsFlipQueueReadSemaphore(
481     const NVHsChannelEvoRec *pHsChannel,
482     const NVFlipNIsoSurfaceEvoHwState *pSemaSurface)
483 {
484     const enum NvKmsNIsoFormat format = pSemaSurface->format;
485     const NvU32 semaphoreIndex = HsGetSemaphoreIndex(pSemaSurface);
486     const NvU32 sd = pHsChannel->pDispEvo->displayOwner;
487     const void *ptr;
488     struct nvKmsParsedSemaphore parsedSemaphore = { };
489 
490     /* We should only get here if we have a valid semaphore surface. */
491     nvAssert(pSemaSurface->pSurfaceEvo != NULL);
492 
493     ptr = pSemaSurface->pSurfaceEvo->cpuAddress[sd];
494 
495     if (ptr == NULL) {
496         nvAssert(!"Semaphore surface without CPU mapping!");
497         return 0;
498     }
499 
500     nvKmsParseSemaphore(format, semaphoreIndex, ptr, &parsedSemaphore);
501 
502     return parsedSemaphore.payload;
503 }
504 
505 /*!
506  * Return whether the specified pFlipState is ready to flip.
507  */
HsFlipQueueEntryIsReady(const NVHsChannelEvoRec * pHsChannel,const NVHsLayerRequestedFlipState * pFlipState)508 static NvBool HsFlipQueueEntryIsReady(
509     const NVHsChannelEvoRec *pHsChannel,
510     const NVHsLayerRequestedFlipState *pFlipState)
511 {
512     const NVFlipNIsoSurfaceEvoHwState *pSemaSurface =
513         &pFlipState->syncObject.u.semaphores.acquireSurface;
514 
515     if (pFlipState->syncObject.usingSyncpt) {
516         return TRUE;
517     }
518 
519     /*
520      * If a semaphore surface was specified, check if the semaphore has reached
521      * the specified acquire value.
522      */
523     if (pSemaSurface->pSurfaceEvo != NULL) {
524         const NvU32 semaphoreValue =
525             HsFlipQueueReadSemaphore(pHsChannel, pSemaSurface);
526 
527         if (pHsChannel->swapGroupFlipping) {
528             // With swap group flipping, the client semaphore should be
529             // written before the non-stall interrupt kicking off the flip.
530             nvAssert(semaphoreValue == pFlipState->syncObject.u.semaphores.acquireValue);
531         } else {
532             if (semaphoreValue != pFlipState->syncObject.u.semaphores.acquireValue) {
533                 return FALSE;
534             }
535         }
536     }
537 
538     /*
539      * If a time stamp was specified for the flip, check if the time stamp has
540      * been satisfied.
541      *
542      * XXX NVKMS HEADSURFACE TODO: Implement time stamp flip check.
543      */
544 
545     return TRUE;
546 }
547 
548 /*!
549  * Update the reference count of all the surfaces described in the pFlipState.
550  */
HsUpdateFlipQueueEntrySurfaceRefCount(NVDevEvoPtr pDevEvo,const NVHsLayerRequestedFlipState * pFlipState,NvBool increase)551 static void HsUpdateFlipQueueEntrySurfaceRefCount(
552     NVDevEvoPtr pDevEvo,
553     const NVHsLayerRequestedFlipState *pFlipState,
554     NvBool increase)
555 {
556     HsChangeSurfaceFlipRefCount(
557         pDevEvo, pFlipState->pSurfaceEvo[NVKMS_LEFT], increase);
558 
559     HsChangeSurfaceFlipRefCount(
560         pDevEvo, pFlipState->pSurfaceEvo[NVKMS_RIGHT], increase);
561 
562     if (!pFlipState->syncObject.usingSyncpt) {
563         HsChangeSurfaceFlipRefCount(
564             pDevEvo, pFlipState->syncObject.u.semaphores.acquireSurface.pSurfaceEvo, increase);
565 
566         HsChangeSurfaceFlipRefCount(
567             pDevEvo, pFlipState->syncObject.u.semaphores.releaseSurface.pSurfaceEvo, increase);
568     }
569 }
570 
571 /*!
572  * Update bookkeeping for "flipping away" from a pFlipState.
573  */
HsReleaseFlipQueueEntry(NVDevEvoPtr pDevEvo,NVHsChannelEvoPtr pHsChannel,const NVHsLayerRequestedFlipState * pFlipState)574 static void HsReleaseFlipQueueEntry(
575     NVDevEvoPtr pDevEvo,
576     NVHsChannelEvoPtr pHsChannel,
577     const NVHsLayerRequestedFlipState *pFlipState)
578 {
579     /*
580      * If a semaphore surface was specified, we can now write its release value.
581      */
582     if (!pFlipState->syncObject.usingSyncpt &&
583         pFlipState->syncObject.u.semaphores.releaseSurface.pSurfaceEvo != NULL) {
584 
585         /*
586          * XXX NVKMS HEADSURFACE TODO: write the timestamp in the EVO/NVDisplay
587          * semaphore structure, based on NvKmsNIsoFormat.  The graphics channel
588          * doesn't support all the NvKmsNIsoFormats, so we would need to use a
589          * graphics channel semaphore release of STRUCTURE_SIZE = ONE_WORD with
590          * the timestamp as payload.  It would be unfortunate to read ptimer
591          * registers in order to compute the payload value.
592          */
593 
594         nvHs3dReleaseSemaphore(pHsChannel,
595                                pFlipState->syncObject.u.semaphores.releaseSurface.pSurfaceEvo,
596                                pFlipState->syncObject.u.semaphores.releaseSurface.format,
597                                pFlipState->syncObject.u.semaphores.releaseSurface.offsetInWords,
598                                pFlipState->syncObject.u.semaphores.releaseValue,
599                                TRUE /* allPreceedingReads */);
600     }
601 
602     /*
603      * HeadSurface no longer needs to read from the surfaces in pFlipState;
604      * decrement their reference counts.
605      */
606     HsUpdateFlipQueueEntrySurfaceRefCount(pDevEvo, pFlipState, FALSE);
607 }
608 
609 /*!
610  * "Fast forward" through flip queue entries that are ready.
611  *
612  * \param[in,out]  pHsChannel               The headSurface channel.
613  * \param[in]      layer                    The layer of the flip queue.
614  * \param[in]      honorIsReadyCriteria     Honor the isReady check for
615  *                                          flip queue entries.
616  * \param[in]      honorMinPresentInterval  Honor the minPresentInterval in
617  *                                          flip queue entries.
618  */
HsFastForwardFlipQueue(NVHsChannelEvoPtr pHsChannel,const NvU8 layer,const NvBool honorIsReadyCriteria,const NvBool honorMinPresentInterval)619 static void HsFastForwardFlipQueue(
620     NVHsChannelEvoPtr pHsChannel,
621     const NvU8 layer,
622     const NvBool honorIsReadyCriteria,
623     const NvBool honorMinPresentInterval)
624 {
625     NVDevEvoPtr pDevEvo = pHsChannel->pDispEvo->pDevEvo;
626     NVListRec *pFlipQueue = &pHsChannel->flipQueue[layer].queue;
627 
628     /*
629      * For swapgroup flips, every flip kicked off by the client needs to result
630      * in a real flip in hardware, so we can't fast forward through flips here.
631      */
632     if (pHsChannel->config.neededForSwapGroup) {
633         return;
634     }
635 
636     while (!nvListIsEmpty(pFlipQueue)) {
637 
638         NVHsChannelFlipQueueEntry *pEntry =
639             nvListFirstEntry(pFlipQueue,
640                              NVHsChannelFlipQueueEntry,
641                              flipQueueEntry);
642         /*
643          * Stop "fast forwarding" once we find a flip queue entry that is not
644          * ready: we must not release semaphores out of order, otherwise we
645          * could confuse client semaphore interlocking.
646          */
647         if (honorIsReadyCriteria &&
648             !HsFlipQueueEntryIsReady(pHsChannel, &pEntry->hwState)) {
649             break;
650         }
651 
652         /*
653          * Normally, we want to make sure that each MinPresentInterval > 0 flip
654          * is displayed for one frame, so we shouldn't fast forward past them.
655          */
656         if (honorMinPresentInterval &&
657             (pEntry->hwState.minPresentInterval != 0)) {
658             break;
659         }
660 
661         /*
662          * We are "flipping away" from the flip queue entry in current.  Release
663          * it, and replace it with the entry in pEntry.
664          */
665 
666         HsReleaseFlipQueueEntry(pDevEvo, pHsChannel,
667                                 &pHsChannel->flipQueue[layer].current);
668 
669         pHsChannel->flipQueue[layer].current = pEntry->hwState;
670 
671         nvListDel(&pEntry->flipQueueEntry);
672         nvFree(pEntry);
673     }
674 }
675 
676 /*!
677  * Push a new entry to the end of the headSurface channel's flip queue.
678  *
679  * \param[in,out]  pHsChannel  The headSurface channel.
680  * \param[in]      layer       The layer of the flip queue.
681  * \param[in]      pFlipState    The hwState to be pushed on the flip queue.
682  */
nvHsPushFlipQueueEntry(NVHsChannelEvoPtr pHsChannel,const NvU8 layer,const NVHsLayerRequestedFlipState * pFlipState)683 void nvHsPushFlipQueueEntry(
684     NVHsChannelEvoPtr pHsChannel,
685     const NvU8 layer,
686     const NVHsLayerRequestedFlipState *pFlipState)
687 {
688     NVDevEvoPtr pDevEvo = pHsChannel->pDispEvo->pDevEvo;
689     NVListRec *pFlipQueue = &pHsChannel->flipQueue[layer].queue;
690     NVHsChannelFlipQueueEntry *pEntry = nvCalloc(1, sizeof(*pEntry));
691 
692     if (pEntry == NULL) {
693         /*
694          * XXX NVKMS HEADSURFACE TODO: we cannot fail at this point in the call
695          * chain (we've already committed to the flip).  Move the nvCalloc() call
696          * earlier in the call chain to a point where we can fail.
697          */
698         return;
699     }
700 
701     pEntry->hwState = *pFlipState;
702 
703     /* Increment the ref counts on the surfaces in the flip queue entry. */
704 
705     HsUpdateFlipQueueEntrySurfaceRefCount(pDevEvo, &pEntry->hwState, TRUE);
706 
707     /* "Fast forward" through existing flip queue entries that are ready. */
708 
709     HsFastForwardFlipQueue(pHsChannel, layer,
710                            TRUE /* honorIsReadyCriteria */,
711                            TRUE /* honorMinPresentInterval */);
712 
713     /* Append the new entry. */
714 
715     nvListAppend(&pEntry->flipQueueEntry, pFlipQueue);
716 }
717 
718 /*!
719  * Remove the first entry in the flip queue and return it.
720  *
721  * If the first entry in the flipQueue is ready to be consumed by headSurface,
722  * remove it from the list and return it in the 'pFlipState' argument.
723  *
724  * If this function returns TRUE, it is the caller's responsibility to
725  * eventually call
726  *
727  *    HsUpdateFlipQueueEntrySurfaceRefCount(pDevEvo, pFlipState, FALSE)
728  *
729  * for the returned pFlipState.
730  *
731  * \param[in,out]  pHsChannel  The headSurface channel.
732  * \param[in]      layer       The layer of the flip queue.
733  * \param[out]     pFlipState    The hwState that was popped off the flip queue.
734  *
735  * \return   Return TRUE if a flip queue entry was popped off the queue and
736  *           copied into pFlipState.
737  */
HsPopFlipQueueEntry(NVHsChannelEvoPtr pHsChannel,const NvU8 layer,NVHsLayerRequestedFlipState * pFlipState)738 static NvBool HsPopFlipQueueEntry(
739     NVHsChannelEvoPtr pHsChannel,
740     const NvU8 layer,
741     NVHsLayerRequestedFlipState *pFlipState)
742 {
743     NVListRec *pFlipQueue = &pHsChannel->flipQueue[layer].queue;
744     NVHsChannelFlipQueueEntry *pEntry;
745 
746     if (nvListIsEmpty(pFlipQueue)) {
747         return FALSE;
748     }
749 
750     pEntry = nvListFirstEntry(pFlipQueue,
751                               NVHsChannelFlipQueueEntry,
752                               flipQueueEntry);
753 
754     if (!HsFlipQueueEntryIsReady(pHsChannel, &pEntry->hwState)) {
755         return FALSE;
756     }
757 
758     *pFlipState = pEntry->hwState;
759 
760     nvListDel(&pEntry->flipQueueEntry);
761     nvFree(pEntry);
762 
763     return TRUE;
764 }
765 
766 /*!
767  * Update the current flip queue entry for a new headSurface frame.
768  *
769  * To build a new frame of headSurface, we look at the flip queue of each layer.
770  * If there is an entry available, we pop it off the queue and replace .current
771  * with the entry.
772  */
HsUpdateFlipQueueCurrent(NVHsChannelEvoPtr pHsChannel)773 static void HsUpdateFlipQueueCurrent(
774     NVHsChannelEvoPtr pHsChannel)
775 {
776     NVDevEvoPtr pDevEvo = pHsChannel->pDispEvo->pDevEvo;
777     NvU8 layer;
778 
779     for (layer = 0; layer < ARRAY_LEN(pHsChannel->flipQueue); layer++) {
780 
781         NVHsLayerRequestedFlipState newCurrent = { };
782 
783         /*
784          * XXX NVKMS HEADSURFACE TODO: fast forward to the last ready flip queue
785          * entry.  Share code with similar functionality in
786          * nvHsPushFlipQueueEntry().
787          */
788 
789         if (!HsPopFlipQueueEntry(pHsChannel, layer, &newCurrent)) {
790             continue;
791         }
792 
793         /*
794          * We have a new flip queue entry to place in current.  Release the old
795          * current flip queue entry, and replace it with the popped entry.
796          */
797         HsReleaseFlipQueueEntry(pDevEvo, pHsChannel,
798                                 &pHsChannel->flipQueue[layer].current);
799 
800         pHsChannel->flipQueue[layer].current = newCurrent;
801     }
802 }
803 
804 /*!
805  * Drain the flip queue on each layer of pHsChannel.
806  *
807  * In preparation to disable headSurface, release the flip queue entry in
808  * .current, as well as all entries in the queue.
809  */
nvHsDrainFlipQueue(NVHsChannelEvoPtr pHsChannel)810 void nvHsDrainFlipQueue(
811     NVHsChannelEvoPtr pHsChannel)
812 {
813     NVDevEvoPtr pDevEvo = pHsChannel->pDispEvo->pDevEvo;
814     NvU8 layer;
815 
816     for (layer = 0; layer < ARRAY_LEN(pHsChannel->flipQueue); layer++) {
817         NVListRec *pFlipQueue = &pHsChannel->flipQueue[layer].queue;
818 
819         HsReleaseFlipQueueEntry(pDevEvo, pHsChannel,
820                                 &pHsChannel->flipQueue[layer].current);
821 
822         nvkms_memset(&pHsChannel->flipQueue[layer].current, 0,
823                      sizeof(pHsChannel->flipQueue[layer].current));
824 
825         while (!nvListIsEmpty(pFlipQueue)) {
826 
827             NVHsChannelFlipQueueEntry *pEntry =
828                 nvListFirstEntry(pFlipQueue,
829                                  NVHsChannelFlipQueueEntry,
830                                  flipQueueEntry);
831 
832             HsReleaseFlipQueueEntry(pDevEvo, pHsChannel, &pEntry->hwState);
833 
834             nvListDel(&pEntry->flipQueueEntry);
835             nvFree(pEntry);
836         }
837     }
838 }
839 
840 /*!
841  * Return whether all flip queues on this pHsChannel are idle.
842  *
843  * As a side effect, attempt to "fast forward" through flip queue entries, in an
844  * effort to make the flip queues idle.  When fast forwarding, always ignore the
845  * client-requested minPresentInterval.  Optionally (when force == TRUE), also
846  * ignore the "IsReady" check.
847  *
848  * This is intended to be used in two scenarios:
849  *
850  * - First, call nvHsIdleFlipQueue(force=FALSE) in a loop with all other heads
851  *   we are trying to idle.  This should allow semaphore interlocking to
852  *   progress naturally.
853  *
854  * - If that loop times out, call nvHsIdleFlipQueue(force=TRUE), which will
855  *   ignore the IsReady conditions and forcibly make the flip queues idle.
856  */
nvHsIdleFlipQueue(NVHsChannelEvoPtr pHsChannel,NvBool force)857 NvBool nvHsIdleFlipQueue(
858     NVHsChannelEvoPtr pHsChannel,
859     NvBool force)
860 {
861     const NvBool honorIsReadyCriteria = !force;
862     NvBool ret = TRUE;
863     NvU8 layer;
864 
865     for (layer = 0; layer < ARRAY_LEN(pHsChannel->flipQueue); layer++) {
866 
867         HsFastForwardFlipQueue(pHsChannel, layer,
868                                honorIsReadyCriteria,
869                                FALSE /* honorMinPresentInterval */);
870 
871         if (!nvListIsEmpty(&pHsChannel->flipQueue[layer].queue)) {
872             /* force should always result in an empty flip queue */
873             nvAssert(!force);
874             ret = FALSE;
875         }
876     }
877 
878     return ret;
879 }
880 
881 /*
882  * We use notifiers to know when headSurface frames are presented, so that we
883  * don't render to the visible buffer.
884  */
885 
AllocNotifierMemory(const NVDevEvoRec * pDevEvo,NvU32 handle)886 static NvU32 AllocNotifierMemory(
887     const NVDevEvoRec *pDevEvo,
888     NvU32 handle)
889 {
890     NV_MEMORY_ALLOCATION_PARAMS memAllocParams = { };
891 
892     memAllocParams.owner = NVKMS_RM_HEAP_ID;
893     memAllocParams.size = NVKMS_HEAD_SURFACE_NOTIFIERS_SIZE_IN_BYTES;
894     memAllocParams.type = NVOS32_TYPE_DMA;
895 
896     memAllocParams.attr = DRF_DEF(OS32, _ATTR, _LOCATION, _VIDMEM) |
897                           DRF_DEF(OS32, _ATTR, _PHYSICALITY, _CONTIGUOUS) |
898                           DRF_DEF(OS32, _ATTR, _PAGE_SIZE, _4KB) |
899                           DRF_DEF(OS32, _ATTR, _COHERENCY, _UNCACHED);
900 
901     memAllocParams.flags = NVOS32_ALLOC_FLAGS_FORCE_MEM_GROWS_DOWN |
902                            NVOS32_ALLOC_FLAGS_IGNORE_BANK_PLACEMENT |
903                            NVOS32_ALLOC_FLAGS_FORCE_ALIGN_HOST_PAGE;
904 
905     memAllocParams.attr2 = DRF_DEF(OS32, _ATTR2, _ISO, _NO);
906 
907     return nvRmApiAlloc(nvEvoGlobal.clientHandle,
908                        pDevEvo->deviceHandle,
909                        handle,
910                        NV01_MEMORY_LOCAL_USER,
911                        &memAllocParams);
912 }
913 
MapNotifiers(NVHsDeviceEvoRec * pHsDevice)914 static NvBool MapNotifiers(NVHsDeviceEvoRec *pHsDevice)
915 {
916     NVDevEvoRec *pDevEvo = pHsDevice->pDevEvo;
917     NVHsNotifiersRec *pNotifiers = &pHsDevice->notifiers;
918     const NvU64 size = NVKMS_HEAD_SURFACE_NOTIFIERS_SIZE_IN_BYTES;
919     NvU32 sd, ret;
920 
921     for (sd = 0; sd < pDevEvo->numSubDevices; sd++) {
922         ret = nvRmApiMapMemory(nvEvoGlobal.clientHandle,
923                                pDevEvo->pSubDevices[sd]->handle,
924                                pNotifiers->rmHandle,
925                                0,
926                                size,
927                                (void **)&pNotifiers->sd[sd].ptr,
928                                0);
929         if (ret != NVOS_STATUS_SUCCESS) {
930             return FALSE;
931         }
932 
933         /*
934          * Intentionally use NVMISC_MEMSET() rather than nvkms_memset(): some
935          * CPU architectures, notably ARM, may fault if streaming stores like in
936          * an optimized memset() implementation are used on a BAR1 mapping.
937          * NVMISC_MEMSET() is conveniently not optimized.
938          */
939         NVMISC_MEMSET((void *)pNotifiers->sd[sd].ptr, 0, size);
940     }
941 
942     return TRUE;
943 }
944 
UnmapNotifiers(NVHsDeviceEvoRec * pHsDevice)945 static void UnmapNotifiers(NVHsDeviceEvoRec *pHsDevice)
946 {
947     NVDevEvoRec *pDevEvo = pHsDevice->pDevEvo;
948     NVHsNotifiersRec *pNotifiers = &pHsDevice->notifiers;
949     NvU32 sd;
950 
951     if (pNotifiers->rmHandle == 0) {
952         return;
953     }
954 
955     for (sd = 0; sd < pDevEvo->numSubDevices; sd++) {
956 
957         if (pNotifiers->sd[sd].ptr == NULL) {
958             continue;
959         }
960 
961         nvRmApiUnmapMemory(nvEvoGlobal.clientHandle,
962                            pDevEvo->pSubDevices[sd]->handle,
963                            pNotifiers->rmHandle,
964                            pNotifiers->sd[sd].ptr,
965                            0);
966 
967         pNotifiers->sd[sd].ptr = NULL;
968     }
969 }
970 
RegisterNotifiersWithNvKms(NVHsDeviceEvoRec * pHsDevice)971 static NvBool RegisterNotifiersWithNvKms(NVHsDeviceEvoRec *pHsDevice)
972 {
973     struct NvKmsRegisterSurfaceParams params = { };
974     NVHsNotifiersRec *pNotifiers = &pHsDevice->notifiers;
975     NVDevEvoRec *pDevEvo = pHsDevice->pDevEvo;
976     const NvBool requireDisplayHardwareAccess = TRUE;
977 
978     params.request.useFd       = FALSE;
979     params.request.rmClient    = nvEvoGlobal.clientHandle;
980 
981     params.request.layout      = NvKmsSurfaceMemoryLayoutPitch;
982     params.request.format      = NvKmsSurfaceMemoryFormatI8;
983 
984     params.request.isoType = NVKMS_MEMORY_NISO;
985 
986     params.request.planes[0].u.rmObject = pNotifiers->rmHandle;
987     params.request.planes[0].pitch = NVKMS_HEAD_SURFACE_NOTIFIERS_SIZE_IN_BYTES;
988     params.request.planes[0].rmObjectSizeInBytes =
989         NVKMS_HEAD_SURFACE_NOTIFIERS_SIZE_IN_BYTES;
990 
991     nvEvoRegisterSurface(pDevEvo, pDevEvo->pNvKmsOpenDev, &params,
992                          NvHsMapPermissionsReadWrite);
993 
994     pHsDevice->notifiers.nvKmsHandle = params.reply.surfaceHandle;
995 
996     if (pHsDevice->notifiers.nvKmsHandle == 0) {
997         return FALSE;
998     }
999 
1000     pHsDevice->notifiers.pSurfaceEvo =
1001         nvHsGetNvKmsSurface(pDevEvo,
1002                             pHsDevice->notifiers.nvKmsHandle,
1003                             requireDisplayHardwareAccess);
1004 
1005     return (pHsDevice->notifiers.pSurfaceEvo != NULL);
1006 }
1007 
AssignNIsoFormat(NVHsDeviceEvoRec * pHsDevice)1008 static void AssignNIsoFormat(NVHsDeviceEvoRec *pHsDevice)
1009 {
1010     const NVDevEvoRec *pDevEvo = pHsDevice->pDevEvo;
1011 
1012     if (pDevEvo->caps.validNIsoFormatMask &
1013         NVBIT(NVKMS_NISO_FORMAT_FOUR_WORD_NVDISPLAY)) {
1014         /* If available, use the "nvdisplay" format. */
1015         pHsDevice->notifiers.nIsoFormat = NVKMS_NISO_FORMAT_FOUR_WORD_NVDISPLAY;
1016     } else {
1017         /* Otherwise, use the "legacy" format. */
1018         nvAssert((pDevEvo->caps.validNIsoFormatMask &
1019                   NVBIT(NVKMS_NISO_FORMAT_LEGACY)) != 0);
1020         pHsDevice->notifiers.nIsoFormat = NVKMS_NISO_FORMAT_LEGACY;
1021     }
1022 }
1023 
AllocNotifiers(NVHsDeviceEvoRec * pHsDevice)1024 static NvBool AllocNotifiers(NVHsDeviceEvoRec *pHsDevice)
1025 {
1026     NvU32 ret;
1027     NVDevEvoRec *pDevEvo;
1028 
1029     pDevEvo = pHsDevice->pDevEvo;
1030 
1031     pHsDevice->notifiers.rmHandle = nvGenerateUnixRmHandle(&pDevEvo->handleAllocator);
1032 
1033     if (pHsDevice->notifiers.rmHandle == 0) {
1034         goto fail;
1035     }
1036 
1037     ret = AllocNotifierMemory(pHsDevice->pDevEvo, pHsDevice->notifiers.rmHandle);
1038 
1039     if (ret != NVOS_STATUS_SUCCESS) {
1040         nvFreeUnixRmHandle(&pDevEvo->handleAllocator,
1041                            pHsDevice->notifiers.rmHandle);
1042         pHsDevice->notifiers.rmHandle = 0;
1043 
1044         goto fail;
1045     }
1046 
1047     if (!MapNotifiers(pHsDevice)) {
1048         goto fail;
1049     }
1050 
1051     if (!RegisterNotifiersWithNvKms(pHsDevice)) {
1052         goto fail;
1053     }
1054 
1055     AssignNIsoFormat(pHsDevice);
1056 
1057     return TRUE;
1058 
1059 fail:
1060     FreeNotifiers(pHsDevice);
1061 
1062     return FALSE;
1063 }
1064 
FreeNotifiers(NVHsDeviceEvoRec * pHsDevice)1065 static void FreeNotifiers(NVHsDeviceEvoRec *pHsDevice)
1066 {
1067     NVDevEvoRec *pDevEvo;
1068     NVHsNotifiersRec *pNotifiers;
1069 
1070     if (pHsDevice == NULL) {
1071         return;
1072     }
1073 
1074     pDevEvo = pHsDevice->pDevEvo;
1075     pNotifiers = &pHsDevice->notifiers;
1076 
1077     if (pNotifiers->nvKmsHandle != 0) {
1078         nvEvoUnregisterSurface(pDevEvo,
1079                                pDevEvo->pNvKmsOpenDev,
1080                                pNotifiers->nvKmsHandle,
1081                                FALSE /* skipUpdate */);
1082         pNotifiers->pSurfaceEvo = NULL;
1083     }
1084 
1085     UnmapNotifiers(pHsDevice);
1086 
1087     if (pHsDevice->notifiers.rmHandle != 0) {
1088         nvRmApiFree(nvEvoGlobal.clientHandle,
1089                     pDevEvo->deviceHandle,
1090                     pHsDevice->notifiers.rmHandle);
1091 
1092         nvFreeUnixRmHandle(&pDevEvo->handleAllocator,
1093                            pHsDevice->notifiers.rmHandle);
1094         pHsDevice->notifiers.rmHandle = 0;
1095     }
1096 }
1097 
1098 /*!
1099  * Reset headSurface notifiers for this channel to NOT_BEGUN.
1100  *
1101  * By the time the modeset completes to transition into a new headSurface
1102  * configuration, all headSurface flips from the previous completion should be
1103  * completed.  But, that would leave at least one notifier set to FINISHED.
1104  *
1105  * Initialize all notifiers for this channel to NOT_BEGUN, so that
1106  * HsVBlankCallbackDeferredWork() does not interpret notifier state from the
1107  * previous headSurface configuration as applying to the new headSurface
1108  * configuration.
1109  */
HsInitNotifiers(NVHsDeviceEvoRec * pHsDevice,NVHsChannelEvoRec * pHsChannel)1110 static void HsInitNotifiers(
1111     NVHsDeviceEvoRec *pHsDevice,
1112     NVHsChannelEvoRec *pHsChannel)
1113 {
1114     const NvU32 apiHead = pHsChannel->apiHead;
1115     const NvU32 sd = pHsChannel->pDispEvo->displayOwner;
1116     NVHsNotifiersRec *pHsNotifiers = &pHsDevice->notifiers;
1117     NVHsNotifiersOneSdRec *pHsNotifiersOneSd = pHsNotifiers->sd[sd].ptr;
1118     NvU8 slot, buffer;
1119 
1120     for (slot = 0; slot < NVKMS_HEAD_SURFACE_MAX_NOTIFIERS_PER_HEAD; slot++) {
1121         nvKmsResetNotifier(pHsNotifiers->nIsoFormat,
1122                            FALSE /* overlay */,
1123                            slot,
1124                            pHsNotifiersOneSd->notifier[apiHead]);
1125     }
1126 
1127     for (buffer = 0; buffer < NVKMS_HEAD_SURFACE_MAX_BUFFERS; buffer++) {
1128         nvKmsResetSemaphore(pHsNotifiers->nIsoFormat,
1129                             buffer, pHsNotifiersOneSd->semaphore[apiHead],
1130                             NVKMS_HEAD_SURFACE_FRAME_SEMAPHORE_RENDERABLE);
1131     }
1132 }
1133 
nvHsInitNotifiers(NVHsDeviceEvoRec * pHsDevice,NVHsChannelEvoRec * pHsChannel)1134 void nvHsInitNotifiers(
1135     NVHsDeviceEvoRec *pHsDevice,
1136     NVHsChannelEvoRec *pHsChannel)
1137 {
1138     if (pHsChannel->config.neededForSwapGroup) {
1139         /*
1140          * XXX NVKMS HEADSURFACE TODO: initialize tracking for ViewPortIn
1141          * flips.
1142          */
1143     } else {
1144         HsInitNotifiers(pHsDevice, pHsChannel);
1145     }
1146 }
1147 
1148 /*!
1149  * For the given head and sd, prepare the next notifier:
1150  *
1151  * - Look up the next notifier to use.
1152  * - Clear that notifier to STATUS_NOT_BEGUN.
1153  * - Update the slot bookkeeping for the (head,sd) pair.
1154  * - Return the dword offset of the notifier.
1155  */
PrepareNextNotifier(NVHsNotifiersRec * pHsNotifiers,NvU32 sd,NvU32 apiHead)1156 static NvU16 PrepareNextNotifier(
1157     NVHsNotifiersRec *pHsNotifiers,
1158     NvU32 sd,
1159     NvU32 apiHead)
1160 {
1161     const NvU32 notifierSize =
1162         nvKmsSizeOfNotifier(pHsNotifiers->nIsoFormat, FALSE /* overlay */);
1163 
1164     const NvU8 nextSlot = pHsNotifiers->sd[sd].apiHead[apiHead].nextSlot;
1165 
1166     NVHsNotifiersOneSdRec *pHsNotifiersOneSd = pHsNotifiers->sd[sd].ptr;
1167 
1168     const NvU8 *headBase = pHsNotifiersOneSd->notifier[apiHead];
1169 
1170     const NvU8 offsetInBytes =
1171         (headBase - ((const NvU8 *) pHsNotifiersOneSd)) +
1172         (notifierSize * nextSlot);
1173 
1174     nvAssert(notifierSize <= NVKMS_HEAD_SURFACE_MAX_NOTIFIER_SIZE);
1175 
1176     nvKmsResetNotifier(pHsNotifiers->nIsoFormat, FALSE /* overlay */,
1177                        nextSlot, pHsNotifiersOneSd->notifier[apiHead]);
1178 
1179     pHsNotifiers->sd[sd].apiHead[apiHead].nextSlot =
1180         (nextSlot + 1) % NVKMS_HEAD_SURFACE_MAX_NOTIFIERS_PER_HEAD;
1181 
1182     return offsetInBytes / 4;
1183 }
1184 
1185 /*!
1186  * Helper function for nvHsFlip(); populate NvKmsFlipRequest and call
1187  * nvFlipEvo().
1188  *
1189  * \param[in,out]  pHsDevice         The headSurface device.
1190  * \param[in,out]  pHsChannel        The headSurface channel.
1191  * \param[in]      perEyeStereoFlip  Whether to flip per-eye.
1192  * \param[in]      surfaceHandles    The surfaces to flip to.
1193  * \param[in]      isFirstFlip       Whether this is the first flip after
1194  *                                   enabling headsurface.
1195  * \param[in]      allowFlipLock     Whether to allow fliplock for this flip.
1196  */
HsFlipHelper(NVHsDeviceEvoRec * pHsDevice,NVHsChannelEvoRec * pHsChannel,const NvBool perEyeStereoFlip,const NvKmsSurfaceHandle surfaceHandles[NVKMS_MAX_EYES],const NvBool isFirstFlip,const NvBool allowFlipLock)1197 static void HsFlipHelper(
1198     NVHsDeviceEvoRec *pHsDevice,
1199     NVHsChannelEvoRec *pHsChannel,
1200     const NvBool perEyeStereoFlip,
1201     const NvKmsSurfaceHandle surfaceHandles[NVKMS_MAX_EYES],
1202     const NvBool isFirstFlip,
1203     const NvBool allowFlipLock)
1204 {
1205     NVDevEvoRec *pDevEvo = pHsDevice->pDevEvo;
1206     struct NvKmsFlipCommonParams *pParamsOneHead;
1207     NVHsNotifiersRec *pHsNotifiers = &pHsDevice->notifiers;
1208     const NvU32 sd = pHsChannel->pDispEvo->displayOwner;
1209     const NvU32 apiHead = pHsChannel->apiHead;
1210     NvBool ret;
1211 
1212     /*
1213      * Use preallocated memory, so that we don't have to allocate
1214      * memory here (and deal with allocation failure).
1215      */
1216     struct NvKmsFlipRequestOneHead *pFlipHead = &pHsChannel->scratchParams;
1217 
1218     nvkms_memset(pFlipHead, 0, sizeof(*pFlipHead));
1219 
1220     pFlipHead->sd = sd;
1221     pFlipHead->head = apiHead;
1222     pParamsOneHead = &pFlipHead->flip;
1223 
1224     if (isFirstFlip) {
1225         /*
1226          * For the first flip after enabling headsurface
1227          * (NV_HS_NEXT_FRAME_REQUEST_TYPE_FIRST_FRAME), the old viewport
1228          * (saved in HsConfigInitSwapGroupOneHead or HsConfigInitModesetOneHead
1229          * and restored in HsConfigRestoreMainLayerSurface) which may specify an
1230          * offset within a multi-head surface needs to be overridden to the
1231          * origin for the per-head headsurface surfaces.
1232          */
1233         pParamsOneHead->viewPortIn.specified = TRUE;
1234         pParamsOneHead->viewPortIn.point.x = 0;
1235         pParamsOneHead->viewPortIn.point.y = 0;
1236 
1237         pParamsOneHead->cursor.imageSpecified = TRUE;
1238 
1239         pParamsOneHead->cursor.positionSpecified = TRUE;
1240     }
1241 
1242     pParamsOneHead->layer[NVKMS_MAIN_LAYER].surface.handle[NVKMS_LEFT] =
1243         surfaceHandles[NVKMS_LEFT];
1244     pParamsOneHead->layer[NVKMS_MAIN_LAYER].surface.handle[NVKMS_RIGHT] =
1245         surfaceHandles[NVKMS_RIGHT];
1246     pParamsOneHead->layer[NVKMS_MAIN_LAYER].surface.specified = TRUE;
1247     pParamsOneHead->layer[NVKMS_MAIN_LAYER].syncObjects.val.useSyncpt = FALSE;
1248     pParamsOneHead->layer[NVKMS_MAIN_LAYER].syncObjects.specified = TRUE;
1249     pParamsOneHead->layer[NVKMS_MAIN_LAYER].tearing = FALSE;
1250     pParamsOneHead->layer[NVKMS_MAIN_LAYER].perEyeStereoFlip = perEyeStereoFlip;
1251     pParamsOneHead->layer[NVKMS_MAIN_LAYER].minPresentInterval = 1;
1252     pParamsOneHead->layer[NVKMS_MAIN_LAYER].csc.specified = TRUE;
1253     pParamsOneHead->lut.input.specified = FALSE;
1254     pParamsOneHead->lut.output.specified = FALSE;
1255 
1256     /*
1257      * XXX NVKMS HEADSURFACE TODO: Work out in which cases we should use the
1258      * head's current CSC or LUT.
1259      */
1260     pParamsOneHead->layer[NVKMS_MAIN_LAYER].csc.matrix = NVKMS_IDENTITY_CSC_MATRIX;
1261 
1262     pParamsOneHead->layer[NVKMS_MAIN_LAYER].completionNotifier.specified = TRUE;
1263 
1264     if (surfaceHandles[NVKMS_LEFT] != 0) {
1265         NVEvoApiHandlesRec *pOpenDevSurfaceHandles =
1266             nvGetSurfaceHandlesFromOpenDev(pDevEvo->pNvKmsOpenDev);
1267         NVSurfaceEvoPtr pSurfaceEvo =
1268             nvEvoGetPointerFromApiHandle(pOpenDevSurfaceHandles, surfaceHandles[NVKMS_LEFT]);
1269         struct NvKmsSemaphore *pSema;
1270 
1271         pParamsOneHead->layer[NVKMS_MAIN_LAYER].completionNotifier.val.surface.surfaceHandle =
1272             pHsNotifiers->nvKmsHandle;
1273         pParamsOneHead->layer[NVKMS_MAIN_LAYER].completionNotifier.val.surface.format =
1274             pHsNotifiers->nIsoFormat;
1275         pParamsOneHead->layer[NVKMS_MAIN_LAYER].completionNotifier.val.surface.offsetInWords =
1276             PrepareNextNotifier(pHsNotifiers, sd, apiHead);
1277 
1278         pParamsOneHead->layer[NVKMS_MAIN_LAYER].syncObjects.val.useSyncpt = FALSE;
1279         pParamsOneHead->layer[NVKMS_MAIN_LAYER].syncObjects.specified = TRUE;
1280 
1281         pSema = &pParamsOneHead->layer[NVKMS_MAIN_LAYER].syncObjects.val.u.semaphores.acquire;
1282         pSema->surface.surfaceHandle = pHsNotifiers->nvKmsHandle;
1283         pSema->surface.format = pHsNotifiers->nIsoFormat;
1284         pSema->surface.offsetInWords =
1285             HsGetFrameSemaphoreOffsetInWords(pHsChannel);
1286         pSema->value = NVKMS_HEAD_SURFACE_FRAME_SEMAPHORE_DISPLAYABLE;
1287 
1288         pParamsOneHead->layer[NVKMS_MAIN_LAYER].syncObjects.val.u.semaphores.release =
1289             pParamsOneHead->layer[NVKMS_MAIN_LAYER].syncObjects.val.u.semaphores.acquire;
1290 
1291         pParamsOneHead->layer[NVKMS_MAIN_LAYER].syncObjects.val.u.semaphores.release.value =
1292             NVKMS_HEAD_SURFACE_FRAME_SEMAPHORE_RENDERABLE;
1293 
1294         pParamsOneHead->layer[NVKMS_MAIN_LAYER].sizeIn.specified = TRUE;
1295         pParamsOneHead->layer[NVKMS_MAIN_LAYER].sizeIn.val.width =
1296             pSurfaceEvo->widthInPixels;
1297         pParamsOneHead->layer[NVKMS_MAIN_LAYER].sizeIn.val.height =
1298             pSurfaceEvo->heightInPixels;
1299 
1300         pParamsOneHead->layer[NVKMS_MAIN_LAYER].sizeOut.specified = TRUE;
1301         pParamsOneHead->layer[NVKMS_MAIN_LAYER].sizeOut.val =
1302             pParamsOneHead->layer[NVKMS_MAIN_LAYER].sizeIn.val;
1303     }
1304 
1305     ret = nvFlipEvo(pDevEvo,
1306                     pDevEvo->pNvKmsOpenDev,
1307                     pFlipHead,
1308                     1     /* numFlipHeads */,
1309                     TRUE  /* commit */,
1310                     FALSE /* allowVrr */,
1311                     NULL  /* pReply */,
1312                     FALSE /* skipUpdate */,
1313                     allowFlipLock);
1314 
1315     if (!ret) {
1316         nvAssert(!"headSurface flip failed?");
1317     }
1318 }
1319 
1320 /*!
1321  * Flip to the headSurface buffer specified by index.
1322  *
1323  * If pHsOneHeadAllDisps == NULL, disable headSurface by flipping to NULL.
1324  *
1325  * \param[in,out]  pHsDevice           The headSurface device.
1326  * \param[in,out]  pHsChannel          The headSurface channel.
1327  * \param[in]      eyeMask             The mask of which eyes to flip.
1328  * \param[in]      perEyeStereoFlip    Whether to flip per-eye.
1329  * \param[in]      index               Which buffer to flip to.
1330  * \param[in]      pHsOneHeadAllDisps  The headSurface config.
1331  * \param[in]      isFirstFlip         Whether this is the first flip after
1332  *                                     enabling headsurface.
1333  * \param[in]      allowFlipLock       Whether to allow fliplock for this flip.
1334  */
nvHsFlip(NVHsDeviceEvoRec * pHsDevice,NVHsChannelEvoRec * pHsChannel,const NvU8 eyeMask,const NvBool perEyeStereoFlip,const NvU8 index,const NVHsStateOneHeadAllDisps * pHsOneHeadAllDisps,const NvBool isFirstFlip,const NvBool allowFlipLock)1335 void nvHsFlip(
1336     NVHsDeviceEvoRec *pHsDevice,
1337     NVHsChannelEvoRec *pHsChannel,
1338     const NvU8 eyeMask,
1339     const NvBool perEyeStereoFlip,
1340     const NvU8 index,
1341     const NVHsStateOneHeadAllDisps *pHsOneHeadAllDisps,
1342     const NvBool isFirstFlip,
1343     const NvBool allowFlipLock)
1344 {
1345     NvKmsSurfaceHandle surfaceHandles[NVKMS_MAX_EYES] = { 0, 0 };
1346     const NvBool enable = (pHsOneHeadAllDisps != NULL);
1347 
1348     if (enable) {
1349         NvU8 eye;
1350 
1351         for (eye = NVKMS_LEFT; eye < NVKMS_MAX_EYES; eye++) {
1352 
1353             const NVHsSurfaceRec *pHsSurface =
1354                 pHsOneHeadAllDisps->surfaces[eye][index].pSurface;
1355 
1356             if ((eyeMask & NVBIT(eye)) == 0) {
1357                 continue;
1358             }
1359 
1360             nvAssert(pHsSurface != NULL);
1361 
1362             surfaceHandles[eye] = pHsSurface->nvKmsHandle;
1363             nvAssert(surfaceHandles[eye] != 0);
1364         }
1365     }
1366 
1367     HsFlipHelper(pHsDevice,
1368                  pHsChannel,
1369                  perEyeStereoFlip,
1370                  surfaceHandles,
1371                  isFirstFlip,
1372                  allowFlipLock);
1373 
1374     if (!enable) {
1375         /* XXX NVKMS HEADSURFACE TODO: disable stereo toggling, if necessary. */
1376     }
1377 }
1378 
1379 /*!
1380  * "Flip" using the core channel's ViewPortIn.
1381  */
HsFlipViewPortIn(NVHsChannelEvoPtr pHsChannel,NvU16 x,NvU16 y)1382 static void HsFlipViewPortIn(NVHsChannelEvoPtr pHsChannel, NvU16 x, NvU16 y)
1383 {
1384     const NVDispEvoRec *pDispEvo = pHsChannel->pDispEvo;
1385 
1386     /*
1387      * XXX NVKMS HEADSURFACE TODO: use the panning NVKMS API request, rather
1388      * than call the low-level SetViewportPointIn() HAL proc.  But, to do that,
1389      * we would need to make the pan request much lighter weight, so that it is
1390      * usable for our needs here.
1391      */
1392     nvApiHeadSetViewportPointIn(pDispEvo, pHsChannel->apiHead, x, y);
1393 
1394     /*
1395      * XXX NVKMS HEADSURFACE TODO: Add tracking so that IsPreviousFrameDone()
1396      * can know if this update latched.
1397      */
1398 }
1399 
HsPickSrcEyeAndPixelShift(const NVHsChannelEvoRec * pHsChannel,const NvU8 dstEye,NvU8 * pSrcEye,enum NvKmsPixelShiftMode * pPixelShift)1400 static void HsPickSrcEyeAndPixelShift(
1401     const NVHsChannelEvoRec *pHsChannel,
1402     const NvU8 dstEye,
1403     NvU8 *pSrcEye,
1404     enum NvKmsPixelShiftMode *pPixelShift)
1405 {
1406     if (pHsChannel->config.pixelShift == NVKMS_PIXEL_SHIFT_8K) {
1407 
1408         if (dstEye == NVKMS_LEFT) {
1409             *pSrcEye = NVKMS_LEFT;
1410             *pPixelShift = NVKMS_PIXEL_SHIFT_4K_BOTTOM_RIGHT;
1411         }
1412 
1413         if (dstEye == NVKMS_RIGHT) {
1414             *pSrcEye = NVKMS_LEFT;
1415             *pPixelShift = NVKMS_PIXEL_SHIFT_4K_TOP_LEFT;
1416         }
1417     } else {
1418         *pSrcEye = dstEye;
1419         *pPixelShift = pHsChannel->config.pixelShift;
1420     }
1421 }
1422 
1423 /*!
1424  * Structure to drive the behavior of nvHsNextFrame().
1425  */
1426 struct NvHsNextFrameWorkArea {
1427 
1428     /*
1429      * The range of surface indices to render to.  Indices here are used as the
1430      * 'index' in NVHsStateOneHeadAllDisps::surfaces[eye][index]::pSurface.
1431      */
1432     NvU8 dstBufferIndexStart;
1433     NvU8 dstBufferIndexEnd;
1434 
1435     /* Whether to flip to the surface indicated by pHsChannel->nextIndex. */
1436     NvBool doFlipToNextIndex;
1437 
1438     /* Whether to allow fliplock on the flip to the next surface. */
1439     NvBool allowFlipLock;
1440 
1441     /* Whether to flip to the destRect region of the surface.*/
1442     NvBool doFlipToDestRect;
1443 
1444     /* Whether to increment nextIndex and/or nextOffset. */
1445     NvBool doIncrementNextIndex;
1446     NvBool doIncrementNextOffset;
1447 
1448     /*
1449      * On which dstBuffer indices to honor the SwapGroup's exclusive
1450      * clip list.
1451      */
1452     NvU8 honorSwapGroupClipListBufferMask;
1453 
1454     /* The region within the surface to render into.  */
1455     struct NvKmsRect destRect;
1456 
1457     /*
1458      * If perEyeStereo::override == TRUE, use perEyeStereo::value to control the
1459      * headSurface flip.
1460      */
1461     struct {
1462         NvBool override;
1463         NvBool value;
1464     } perEyeStereo;
1465 };
1466 
1467 /*!
1468  * Assign an NvHsNextFrameWorkArea structure, to drive execution of
1469  * nvHsNextFrame().
1470  */
HsAssignNextFrameWorkArea(const NVHsChannelEvoRec * pHsChannel,const NvHsNextFrameRequestType requestType)1471 static struct NvHsNextFrameWorkArea HsAssignNextFrameWorkArea(
1472     const NVHsChannelEvoRec *pHsChannel,
1473     const NvHsNextFrameRequestType requestType)
1474 {
1475     struct NvHsNextFrameWorkArea workArea = { };
1476     NvU8 destOffset;
1477 
1478     if ((requestType == NV_HS_NEXT_FRAME_REQUEST_TYPE_FIRST_FRAME) ||
1479         (requestType == NV_HS_NEXT_FRAME_REQUEST_TYPE_VBLANK)) {
1480 
1481         /*
1482          * The swapgroup first frame renders and flips both core and base to
1483          * the back index double height headsurface swapgroup surface, just
1484          * like a non-swapgroup headsurface flip.
1485          */
1486         if (requestType == NV_HS_NEXT_FRAME_REQUEST_TYPE_FIRST_FRAME ||
1487             !pHsChannel->config.neededForSwapGroup) {
1488 
1489             /*
1490              * In the non-SwapGroup case, headSurface should:
1491              * - only render to the 'nextIndex' surface,
1492              * - flip to the nextIndex surface,
1493              * - increment nextIndex.
1494              */
1495             workArea.dstBufferIndexStart   = pHsChannel->nextIndex;
1496             workArea.dstBufferIndexEnd     = pHsChannel->nextIndex;
1497 
1498             workArea.doFlipToNextIndex     = TRUE;
1499             workArea.allowFlipLock         = FALSE;
1500             workArea.doFlipToDestRect      = FALSE;
1501 
1502             workArea.doIncrementNextIndex  = TRUE;
1503             workArea.doIncrementNextOffset = FALSE;
1504 
1505         } else {
1506 
1507             /*
1508              * In the SwapGroup case, headSurface should:
1509              * - render to both surfaces,
1510              * - flip to the nextOffset,
1511              * - increment nextOffset.
1512              */
1513             workArea.dstBufferIndexStart   = 0;
1514             workArea.dstBufferIndexEnd     = NVKMS_HEAD_SURFACE_MAX_BUFFERS - 1;
1515 
1516             workArea.doFlipToNextIndex     = FALSE;
1517 
1518             workArea.allowFlipLock         = FALSE;
1519             workArea.doFlipToDestRect      = TRUE;
1520 
1521             workArea.doIncrementNextIndex  = FALSE;
1522             workArea.doIncrementNextOffset = TRUE;
1523 
1524             /*
1525              * For VBLANK-initiated frames of SwapGroup headSurface, we want the
1526              * surface indicated by pHsChannel->nextIndex to contain the new
1527              * SwapGroup content, and the non-nextIndex surface to contain the
1528              * old SwapGroup content.
1529              *
1530              * Therefore, set the non-nextIndex bit(s) in
1531              * honorSwapGroupClipListBufferMask, so that we leave the old
1532              * SwapGroup content in that case.  In all other cases, we will get
1533              * the new SwapGroup content.
1534              */
1535             workArea.honorSwapGroupClipListBufferMask =
1536                 ~NVBIT(pHsChannel->nextIndex);
1537         }
1538 
1539     } else {
1540         /*
1541          * SWAP_GROUP_READY-initiated headSurface frames are special: we render
1542          * a new frame to the nextIndex surface, using the previous destRect
1543          * (i.e., the location that ViewPortIn will use at the next vblank).
1544          * However, the flip may take indefinitely long to arrive: it will wait
1545          * for the rest of the SwapBarrier.  That is okay, because
1546          * nvHsNextFrame(VBLANK) calls between now and the flip actually
1547          * occurring will keep updating both surfaces, using ViewPortIn to
1548          * "flip" to the new content.
1549          *
1550          * Therefore, we do _not_ increment nextIndex here.  Instead, we update
1551          * nextIndex when we find that the flip completed.  Until then, we keep
1552          * nextIndex the same, so that nvHsNextFrame(VBLANK) frames know which
1553          * surface should receive the new SwapGroup content.
1554          */
1555 
1556         const NVSwapGroupRec *pSwapGroup =
1557             pHsChannel->pDispEvo->pSwapGroup[pHsChannel->apiHead];
1558 
1559         nvAssert(requestType == NV_HS_NEXT_FRAME_REQUEST_TYPE_SWAP_GROUP_READY);
1560         nvAssert(pHsChannel->config.neededForSwapGroup);
1561 
1562         workArea.dstBufferIndexStart   = pHsChannel->nextIndex;
1563         workArea.dstBufferIndexEnd     = pHsChannel->nextIndex;
1564 
1565         workArea.doFlipToNextIndex     = TRUE;
1566         workArea.allowFlipLock         = TRUE;
1567         workArea.doFlipToDestRect      = FALSE;
1568 
1569         workArea.doIncrementNextIndex  = FALSE;
1570         workArea.doIncrementNextOffset = FALSE;
1571 
1572         workArea.perEyeStereo.override = TRUE;
1573         workArea.perEyeStereo.value    =
1574             nvHsSwapGroupGetPerEyeStereo(pSwapGroup);
1575     }
1576 
1577     /*
1578      * Pick the rect within the destination surface that headSurface should
1579      * render into.
1580      *
1581      * For normal (!neededForSwapGroup) use, this should be simply:
1582      *   { 0, 0,                frameSize.width, frameSize.height }
1583      * When SwapGroups are enabled, the headSurface is allocated at
1584      * double height and we alternate between
1585      *   { 0, 0,                frameSize.width, frameSize.height }
1586      *   { 0, frameSize.height, frameSize.width, frameSize.height }
1587      * And use ViewPortIn to flip to the updated half.
1588      *
1589      * The 'nextOffset' field tracks which half headSurface should use for the
1590      * next frame.
1591      *
1592      * The exception to the above is SWAP_GROUP_READY: in that case, we will
1593      * flip between surfaces, but not change ViewPortIn, so we want to use the
1594      * _previous_ nextOffset value.
1595      */
1596     if (requestType == NV_HS_NEXT_FRAME_REQUEST_TYPE_SWAP_GROUP_READY) {
1597         destOffset = HsGetPreviousOffset(pHsChannel);
1598     } else {
1599         destOffset = pHsChannel->nextOffset;
1600     }
1601 
1602     workArea.destRect.x      = 0;
1603     workArea.destRect.y      = pHsChannel->config.frameSize.height *
1604                                destOffset;
1605     workArea.destRect.width  = pHsChannel->config.frameSize.width;
1606     workArea.destRect.height = pHsChannel->config.frameSize.height;
1607 
1608     return workArea;
1609 }
1610 
1611 /*!
1612  * Produce the next headSurface frame.
1613  *
1614  * Render the frame, flip to it, and update next{Index,Offset} bookkeeping
1615  * as necessary.
1616  *
1617  * \param[in,out]  pHsDevice   The device to render on.
1618  * \param[in,out]  pHsChannel  The channel to use for rendering.
1619  * \param[in]      requestType This indicates the type of frame behavior
1620  *                             desired by the caller: when FIRST_FRAME, we need
1621  *                             to populate the surface in the core channel on
1622  *                             pre-NVDisplay.
1623  */
nvHsNextFrame(NVHsDeviceEvoPtr pHsDevice,NVHsChannelEvoPtr pHsChannel,const NvHsNextFrameRequestType requestType)1624 void nvHsNextFrame(
1625     NVHsDeviceEvoPtr pHsDevice,
1626     NVHsChannelEvoPtr pHsChannel,
1627     const NvHsNextFrameRequestType requestType)
1628 {
1629     const NVDevEvoRec *pDevEvo = pHsDevice->pDevEvo;
1630     const NVHsStateOneHeadAllDisps *pHsOneHeadAllDisps =
1631         &pDevEvo->apiHeadSurfaceAllDisps[pHsChannel->apiHead];
1632     NvBool perEyeStereoFlip = FALSE;
1633     NvU8 dstEye;
1634     NvU8 eyeMask = 0;
1635 
1636     struct NvHsNextFrameWorkArea workArea =
1637         HsAssignNextFrameWorkArea(pHsChannel, requestType);
1638 
1639     HsUpdateFlipQueueCurrent(pHsChannel);
1640 
1641     for (dstEye = NVKMS_LEFT; dstEye < NVKMS_MAX_EYES; dstEye++) {
1642 
1643         const NVSurfaceEvoRec *pSurfaceEvo[NVKMS_MAX_LAYERS_PER_HEAD];
1644         NvBool surfacesPresent = FALSE;
1645         NvU8 layer, srcEye = dstEye;
1646         NvU8 dstBufferIndex;
1647         enum NvKmsPixelShiftMode pixelShift = pHsChannel->config.pixelShift;
1648         NvBool ret;
1649 
1650         HsPickSrcEyeAndPixelShift(pHsChannel, dstEye, &srcEye, &pixelShift);
1651 
1652         for (layer = 0; layer < ARRAY_LEN(pHsChannel->flipQueue); layer++) {
1653             pSurfaceEvo[layer] =
1654                 pHsChannel->flipQueue[layer].current.pSurfaceEvo[srcEye];
1655 
1656             surfacesPresent = surfacesPresent || (pSurfaceEvo[layer] != NULL);
1657 
1658             perEyeStereoFlip = perEyeStereoFlip ||
1659                 pHsChannel->flipQueue[layer].current.perEyeStereoFlip;
1660         }
1661 
1662         /*
1663          * If there are no surfaces present for this srcEye, and the dstEye is
1664          * not LEFT, don't render it.
1665          *
1666          * This condition is limited to LEFT because:
1667          * - We need to perform _a_ flip even if no source surface is provided.
1668          * - We don't want to perform more rendering than absolutely
1669          *   unnecessarily.
1670          */
1671         if (!surfacesPresent && (dstEye != NVKMS_LEFT)) {
1672             continue;
1673         }
1674 
1675         for (dstBufferIndex = workArea.dstBufferIndexStart;
1676              dstBufferIndex <= workArea.dstBufferIndexEnd;
1677              dstBufferIndex++) {
1678 
1679             NvU8 thisEyeMask = 0;
1680             const NvBool honorSwapGroupClipList =
1681                 !!(workArea.honorSwapGroupClipListBufferMask &
1682                    NVBIT(dstBufferIndex));
1683 
1684             ret = nvHs3dRenderFrame(pHsChannel,
1685                                     requestType,
1686                                     honorSwapGroupClipList,
1687                                     dstEye,
1688                                     dstBufferIndex,
1689                                     pixelShift,
1690                                     workArea.destRect,
1691                                     pSurfaceEvo);
1692             /*
1693              * Record which eyes we've rendered, so that we only flip those
1694              * eyes.
1695              *
1696              * In the case that we're looping over multiple buffer indices, we
1697              * should get the same result across buffers.
1698              */
1699             if (ret) {
1700                 thisEyeMask = NVBIT(dstEye);
1701             }
1702 
1703             if (dstBufferIndex != workArea.dstBufferIndexStart) {
1704                 nvAssert((eyeMask & NVBIT(dstEye)) ==
1705                          (thisEyeMask & NVBIT(dstEye)));
1706             }
1707 
1708             eyeMask |= thisEyeMask;
1709         }
1710     }
1711 
1712     if (workArea.doFlipToNextIndex) {
1713 
1714         if (workArea.perEyeStereo.override) {
1715             perEyeStereoFlip = workArea.perEyeStereo.value;
1716         }
1717 
1718         nvHs3dReleaseSemaphore(
1719             pHsChannel,
1720             pHsDevice->notifiers.pSurfaceEvo,
1721             pHsDevice->notifiers.nIsoFormat,
1722             HsGetFrameSemaphoreOffsetInWords(pHsChannel),
1723             NVKMS_HEAD_SURFACE_FRAME_SEMAPHORE_DISPLAYABLE,
1724             FALSE /* allPreceedingReads */);
1725 
1726         nvHsFlip(
1727             pHsDevice,
1728             pHsChannel,
1729             eyeMask,
1730             perEyeStereoFlip,
1731             pHsChannel->nextIndex,
1732             pHsOneHeadAllDisps,
1733             requestType == NV_HS_NEXT_FRAME_REQUEST_TYPE_FIRST_FRAME,
1734             workArea.allowFlipLock);
1735         HsIncrementFrameSemaphoreIndex(pHsChannel);
1736 
1737         // Record fullscreen/non-fullscreen swapgroup flip counts
1738         const NVSwapGroupRec *pSwapGroup =
1739             pHsChannel->pDispEvo->pSwapGroup[pHsChannel->apiHead];
1740 
1741         if (pSwapGroup) {
1742             HsProcFsRecordFullscreenSgFrames(pHsChannel,
1743                                              pSwapGroup->swapGroupIsFullscreen);
1744         }
1745 
1746         // Record the time of the last flip originating from client update
1747         if (requestType == NV_HS_NEXT_FRAME_REQUEST_TYPE_SWAP_GROUP_READY) {
1748             pHsChannel->lastHsClientFlipTimeUs = nvkms_get_usec();
1749         }
1750     }
1751 
1752     if (workArea.doFlipToDestRect) {
1753         // Viewport fake flips are only used in swapgroup configurations.
1754         nvAssert(pHsChannel->config.neededForSwapGroup);
1755 
1756         if (pHsChannel->usingRgIntrForSwapGroups) {
1757             nvHs3dPushPendingViewportFlip(pHsChannel);
1758         } else {
1759             HsFlipViewPortIn(pHsChannel,
1760                              workArea.destRect.x, workArea.destRect.y);
1761         }
1762     }
1763 
1764     if (workArea.doIncrementNextIndex) {
1765         HsIncrementNextIndex(pHsDevice, pHsChannel);
1766     }
1767 
1768     if (workArea.doIncrementNextOffset) {
1769         HsIncrementNextOffset(pHsDevice, pHsChannel);
1770     }
1771 }
1772 
1773 /*!
1774  * In response to a non-stall interrupt, check if a headsurface channel has
1775  * completed a frame of non-swapgroup headsurface rendering and kick off a
1776  * viewport flip to the offset that was used for that rendering.
1777  */
nvHsProcessPendingViewportFlips(NVDevEvoPtr pDevEvo)1778 void nvHsProcessPendingViewportFlips(NVDevEvoPtr pDevEvo)
1779 {
1780     NVDispEvoPtr pDispEvo;
1781     NvU32 dispIndex;
1782 
1783     FOR_ALL_EVO_DISPLAYS(pDispEvo, dispIndex, pDevEvo) {
1784         NvU32 apiHead;
1785         for (apiHead = 0; apiHead < NVKMS_MAX_HEADS_PER_DISP; apiHead++) {
1786             NVHsChannelEvoPtr pHsChannel = pDispEvo->pHsChannel[apiHead];
1787             NvU32 lastRenderedOffset;
1788 
1789             if (pHsChannel == NULL) {
1790                 continue;
1791             }
1792 
1793             lastRenderedOffset = nvHs3dLastRenderedOffset(pHsChannel);
1794 
1795             /*
1796              * If this channel is marked as having kicked off a frame of
1797              * rendering, and the semaphore write of the render offset to
1798              * NVKMS_HEADSURFACE_VIEWPORT_OFFSET_SEMAPHORE_INDEX has completed,
1799              * then this channel is ready to make a viewport flip to that
1800              * offset.
1801              */
1802             if (pHsChannel->viewportFlipPending &&
1803                 (lastRenderedOffset == HsGetPreviousOffset(pHsChannel))) {
1804 
1805                 HsFlipViewPortIn(pHsChannel, 0 /* x */,
1806                                  lastRenderedOffset *
1807                                  pHsChannel->config.frameSize.height);
1808                 pHsChannel->viewportFlipPending = FALSE;
1809             }
1810         }
1811     }
1812 }
1813 
1814 /*!
1815  * Record the current scanline, for procfs statistics reporting.
1816  */
HsProcFsRecordScanline(const NVDispEvoRec * pDispEvo,const NvU32 apiHead)1817 static void HsProcFsRecordScanline(
1818     const NVDispEvoRec *pDispEvo,
1819     const NvU32 apiHead)
1820 {
1821 #if NVKMS_PROCFS_ENABLE
1822     NVHsChannelEvoRec *pHsChannel = pDispEvo->pHsChannel[apiHead];
1823     NvU16 scanLine = 0;
1824     NvBool inBlankingPeriod = FALSE;
1825 
1826     if (pHsChannel->statistics.scanLine.pHistogram == NULL) {
1827         return;
1828     }
1829 
1830     nvApiHeadGetScanLine(pDispEvo, apiHead, &scanLine, &inBlankingPeriod);
1831 
1832     if (inBlankingPeriod) {
1833         pHsChannel->statistics.scanLine.nInBlankingPeriod++;
1834     } else {
1835         pHsChannel->statistics.scanLine.nNotInBlankingPeriod++;
1836 
1837         if (scanLine <= pHsChannel->statistics.scanLine.vVisible) {
1838             pHsChannel->statistics.scanLine.pHistogram[scanLine]++;
1839         } else {
1840             nvEvoLogDispDebug(pDispEvo, EVO_LOG_ERROR,
1841                 "HsProcFsRecordScanline(): scanLine (%d) > vVisible (%d)",
1842                 scanLine, pHsChannel->statistics.scanLine.vVisible);
1843         }
1844     }
1845 #endif /* NVKMS_PROCFS_ENABLE */
1846 }
1847 
HsProcFsRecordPreviousFrameNotDone(NVHsChannelEvoPtr pHsChannel)1848 static void HsProcFsRecordPreviousFrameNotDone(
1849     NVHsChannelEvoPtr pHsChannel)
1850 {
1851 #if NVKMS_PROCFS_ENABLE
1852     pHsChannel->statistics.nPreviousFrameNotDone++;
1853 #endif
1854 }
1855 
HsProcFsRecordFullscreenSgFrames(NVHsChannelEvoPtr pHsChannel,NvBool isFullscreen)1856 static void HsProcFsRecordFullscreenSgFrames(
1857     NVHsChannelEvoPtr pHsChannel,
1858     NvBool isFullscreen)
1859 {
1860 #if NVKMS_PROCFS_ENABLE
1861     if (isFullscreen) {
1862         pHsChannel->statistics.nFullscreenSgFrames++;
1863     } else {
1864         pHsChannel->statistics.nNonFullscreenSgFrames++;
1865     }
1866 #endif /* NVKMS_PROCFS_ENABLE */
1867 }
1868 
HsProcFsRecordOmittedNonSgHsUpdate(NVHsChannelEvoPtr pHsChannel)1869 static void HsProcFsRecordOmittedNonSgHsUpdate(
1870     NVHsChannelEvoPtr pHsChannel)
1871 {
1872 #if NVKMS_PROCFS_ENABLE
1873     pHsChannel->statistics.nOmittedNonSgHsUpdates++;
1874 #endif
1875 }
1876 
1877 /*!
1878  * Determine if we've flipped to the previous frame.
1879  *
1880  * When we program the flip method, we reset the notifier to NOT_BEGUN, and when
1881  * EVO peforms the flip, it changes the notifier to BEGUN.
1882  *
1883  * Find the notifier slot for the previous frame, parse its notifier, and return
1884  * whether it is BEGUN.
1885  */
IsPreviousFlipDone(NVHsChannelEvoPtr pHsChannel)1886 static NvBool IsPreviousFlipDone(NVHsChannelEvoPtr pHsChannel)
1887 {
1888     const NVDispEvoRec *pDispEvo = pHsChannel->pDispEvo;
1889     const NvU32 apiHead = pHsChannel->apiHead;
1890     const NvU32 sd = pDispEvo->displayOwner;
1891     const NVHsDeviceEvoRec *pHsDevice = pDispEvo->pDevEvo->pHsDevice;
1892     const NVHsNotifiersRec *pHsNotifiers = &pHsDevice->notifiers;
1893     const NVHsNotifiersOneSdRec *pHsNotifiersOneSd = pHsNotifiers->sd[sd].ptr;
1894     const NvU8 nextSlot = pHsNotifiers->sd[sd].apiHead[apiHead].nextSlot;
1895     struct nvKmsParsedNotifier parsed = { };
1896 
1897     const NvU8 prevSlot =
1898         A_minus_b_with_wrap_U8(nextSlot, 1,
1899                                NVKMS_HEAD_SURFACE_MAX_NOTIFIERS_PER_HEAD);
1900 
1901     nvKmsParseNotifier(pHsNotifiers->nIsoFormat, FALSE /* overlay */,
1902                        prevSlot, pHsNotifiersOneSd->notifier[apiHead], &parsed);
1903 
1904     return parsed.status == NVKMS_NOTIFIER_STATUS_BEGUN;
1905 }
1906 
1907 /*!
1908  * Determine if we've flipped to the previous frame.
1909  */
IsPreviousFrameDone(NVHsChannelEvoPtr pHsChannel)1910 static NvBool IsPreviousFrameDone(NVHsChannelEvoPtr pHsChannel)
1911 {
1912     if (pHsChannel->config.neededForSwapGroup) {
1913         /*
1914          * XXX NVKMS HEADSURFACE TODO: Somehow determine if the previous
1915          * ViewPortIn update for this head was latched.
1916          */
1917 
1918         /*
1919          * XXX NVKMS HEADSURFACE TODO: In the absence of a mechanism to
1920          * determine if ViewPortIn was latched, we would normally rely on this
1921          * callback arriving once per vblank.  Unfortunately, bug 2086726 can
1922          * cause us to get called twice per vblank.  WAR this for now by
1923          * ignoring callbacks that arrive in a very small window of the previous
1924          * callback.
1925          *
1926          * Throttling is now implemented using the RG line 1 interrupt
1927          * headsurface rendering mechanism, so this limit can be lowered once
1928          * the old vblank-triggered viewport flipping mechanism is removed.
1929          */
1930 
1931         const NvU64 oldUSec = pHsChannel->lastCallbackUSec;
1932         const NvU64 newUSec = nvkms_get_usec();
1933 
1934         /*
1935          * This threshold is somewhat arbitrary.  In bug 2086726, we see the
1936          * callback get called from both the ISR and the bottom half, which are
1937          * usually within ~200 usec of each other on an idle system.  There
1938          * shouldn't be a danger of mistaking legitimate periodic callbacks with
1939          * this small threshold: 500 usec per refresh would require a 2000 Hz
1940          * mode.
1941          */
1942         const NvU64 thresholdUSec = 500;
1943 
1944         nvAssert(!pHsChannel->usingRgIntrForSwapGroups);
1945 
1946         if ((newUSec > oldUSec) &&
1947             (newUSec - oldUSec) < thresholdUSec) {
1948             return FALSE;
1949         }
1950 
1951         pHsChannel->lastCallbackUSec = newUSec;
1952 
1953         return TRUE;
1954     } else {
1955         return IsPreviousFlipDone(pHsChannel);
1956     }
1957 }
1958 
1959 /*!
1960  * If the client provided a notifier surface with a real flip
1961  * request while swap groups were enabled, write to that
1962  * notifier with the BEGUN status and the most recent
1963  * headsurface notifier timestamp to emulate what the client
1964  * would observe if their notifier was used in hardware.
1965  */
HsUpdateClientNotifier(NVHsChannelEvoPtr pHsChannel)1966 static void HsUpdateClientNotifier(NVHsChannelEvoPtr pHsChannel)
1967 {
1968     const NVDispEvoRec *pDispEvo = pHsChannel->pDispEvo;
1969     const NvU32 apiHead = pHsChannel->apiHead;
1970     const NvU32 sd = pDispEvo->displayOwner;
1971     const NVHsDeviceEvoRec *pHsDevice = pDispEvo->pDevEvo->pHsDevice;
1972     const NVHsNotifiersRec *pHsNotifiers = &pHsDevice->notifiers;
1973     const NVHsNotifiersOneSdRec *pHsNotifiersOneSd = pHsNotifiers->sd[sd].ptr;
1974     const NvU8 nextSlot = pHsNotifiers->sd[sd].apiHead[apiHead].nextSlot;
1975     struct nvKmsParsedNotifier parsed = { };
1976     NVFlipNIsoSurfaceEvoHwState *pClientNotifier =
1977         &pHsChannel->flipQueue[NVKMS_MAIN_LAYER].current.completionNotifier.surface;
1978 
1979     if (pClientNotifier->pSurfaceEvo == NULL) {
1980         return;
1981     }
1982 
1983     const NvU8 prevSlot =
1984         A_minus_b_with_wrap_U8(nextSlot, 1,
1985                                NVKMS_HEAD_SURFACE_MAX_NOTIFIERS_PER_HEAD);
1986 
1987     nvKmsParseNotifier(pHsNotifiers->nIsoFormat, FALSE /* overlay */,
1988                        prevSlot, pHsNotifiersOneSd->notifier[apiHead], &parsed);
1989 
1990     nvAssert(parsed.status == NVKMS_NOTIFIER_STATUS_BEGUN);
1991 
1992     /*
1993      * XXX NVKMS HEADSURFACE TODO: Get valid timestamp through other means to
1994      * support this on platforms with legacy HW semaphores without valid
1995      * HW notifier timestamps in the main channel.
1996      */
1997     nvAssert(parsed.timeStampValid);
1998 
1999     nvKmsSetNotifier(pClientNotifier->format,
2000                      FALSE /* overlay */,
2001                      pClientNotifier->offsetInWords / 4,
2002                      pClientNotifier->pSurfaceEvo->cpuAddress[sd],
2003                      parsed.timeStamp);
2004 }
2005 
2006 /*!
2007  * Check if all flips completed for this SwapGroup.  If so, release the
2008  * SwapGroup.
2009  */
HsCheckSwapGroupFlipDone(NVDevEvoPtr pDevEvo,NVSwapGroupRec * pSwapGroup)2010 static void HsCheckSwapGroupFlipDone(
2011     NVDevEvoPtr pDevEvo,
2012     NVSwapGroupRec *pSwapGroup)
2013 {
2014     const NVHsDeviceEvoRec *pHsDevice = pDevEvo->pHsDevice;
2015     NVDispEvoPtr pDispEvo;
2016     NvU32 dispIndex;
2017 
2018     nvAssert(pSwapGroup != NULL);
2019 
2020     if (!pSwapGroup->pendingFlip) {
2021         return;
2022     }
2023 
2024     /*
2025      * Check if all active heads in the SwapGroup have completed their flips.
2026      * If any haven't, return early.
2027      */
2028     FOR_ALL_EVO_DISPLAYS(pDispEvo, dispIndex, pDevEvo) {
2029         NvU32 apiHead;
2030         for (apiHead = 0; apiHead < ARRAY_LEN(pDispEvo->pSwapGroup); apiHead++) {
2031 
2032             if (pDispEvo->pSwapGroup[apiHead] == pSwapGroup) {
2033 
2034                 NVHsChannelEvoPtr pHsChannel = pDispEvo->pHsChannel[apiHead];
2035 
2036                 if (pHsChannel == NULL) {
2037                     continue;
2038                 }
2039 
2040                 nvAssert(pHsChannel->config.neededForSwapGroup);
2041 
2042                 if (!IsPreviousFlipDone(pHsChannel)) {
2043                     return;
2044                 }
2045             }
2046         }
2047     }
2048 
2049     /*
2050      * The SwapGroup is ready: update client notifiers if necessary and
2051      * increment nextIndex for all active heads, so that subsequent frames of
2052      * headSurface render to the next buffer.
2053      */
2054     FOR_ALL_EVO_DISPLAYS(pDispEvo, dispIndex, pDevEvo) {
2055         NvU32 apiHead;
2056         for (apiHead = 0; apiHead < ARRAY_LEN(pDispEvo->pSwapGroup); apiHead++) {
2057 
2058             if (pDispEvo->pSwapGroup[apiHead] == pSwapGroup) {
2059 
2060                 NVHsChannelEvoPtr pHsChannel = pDispEvo->pHsChannel[apiHead];
2061 
2062                 if (pHsChannel == NULL) {
2063                     continue;
2064                 }
2065 
2066                 nvAssert(pHsChannel->config.neededForSwapGroup);
2067                 nvAssert(IsPreviousFlipDone(pHsChannel));
2068 
2069                 HsUpdateClientNotifier(pHsChannel);
2070                 HsIncrementNextIndex(pHsDevice, pHsChannel);
2071             }
2072         }
2073     }
2074 
2075     /*
2076      * The SwapGroup is ready: release all SwapGroup members so that they can
2077      * proceed.
2078      */
2079     nvHsSwapGroupRelease(pDevEvo, pSwapGroup);
2080 }
2081 
2082 /*
2083  * Called from RG line interrupt handler to determine whether rendering a
2084  * new frame could be skipped.
2085  */
HsCanOmitNonSgHsUpdate(NVHsChannelEvoPtr pHsChannel)2086 static NvBool HsCanOmitNonSgHsUpdate(NVHsChannelEvoPtr pHsChannel)
2087 {
2088     const NVSwapGroupRec *pHeadSwapGroup =
2089         pHsChannel->pDispEvo->pSwapGroup[pHsChannel->apiHead];
2090 
2091     /*
2092      * When fullscreen swapgroup flipping, updating
2093      * non-swapgroup content at vblank is unnecessary and
2094      * dangerous, since it results in releasing client
2095      * semaphores before their contents have actually been
2096      * displayed.
2097      */
2098     if (pHsChannel->swapGroupFlipping) {
2099         return NV_TRUE;
2100     }
2101 
2102     /*
2103      * In the case of a fullscreen swapgroup, we can generally omit updating
2104      * the headsurface entirely upon vblank as long as the client is
2105      * actively rendering. All the swapgroup content has already been
2106      * updated to the headsurface backbuffer at the client's swapbuffers
2107      * time and there's no need to update the backbuffer again on RG line 1
2108      * or vblank interrupt time.
2109      *
2110      * There is one exception to this. If the client isn't rendering
2111      * actively then updates to the cursor (and possibly overlays, head
2112      * config) still require rendering an updated frame to the backbuffer.
2113      * Thus, we will simply limit this optimization for frames that come
2114      * within one frame time after the last recorded flip.
2115      */
2116     if (pHeadSwapGroup &&
2117         pHeadSwapGroup->swapGroupIsFullscreen) {
2118 
2119         NvU64 nowUs = nvkms_get_usec();
2120         NvU64 frameTimeUs = nvEvoFrametimeUsFromTimings(
2121             &pHsChannel->pDispEvo->apiHeadState[pHsChannel->apiHead].timings);
2122 
2123         if (nowUs - pHsChannel->lastHsClientFlipTimeUs < frameTimeUs) {
2124             return NV_TRUE;
2125         }
2126     }
2127 
2128     return NV_FALSE;
2129 }
2130 
2131 /*!
2132  * Receive RG line 1 callback, in process context with nvkms_lock held.
2133  */
HsRgLine1CallbackProc(NVDispEvoRec * pDispEvo,const NvU32 head,NVRgLine1CallbackPtr pCallback)2134 static void HsRgLine1CallbackProc(NVDispEvoRec *pDispEvo,
2135                                   const NvU32 head,
2136                                   NVRgLine1CallbackPtr pCallback)
2137 {
2138     const NvU32 apiHead =
2139         (NvU32)(NvUPtr)pCallback->pUserData;
2140     NVHsChannelEvoPtr pHsChannel = pDispEvo->pHsChannel[apiHead];
2141 
2142     /*
2143      * The pHsChannel may have been torn down between when the callback was
2144      * generated and when this was called.  Ignore spurious callbacks.
2145      */
2146     if (pHsChannel == NULL) {
2147         return;
2148     }
2149 
2150     if (pHsChannel->config.neededForSwapGroup) {
2151         /*
2152          * Update the non-swapgroup content on the back half of both
2153          * headsurface surfaces, and the swapgroup content on the back half of
2154          * the back headsurface surface, and perform a viewportoffset flip to
2155          * the back offset.
2156          *
2157          * Synchronization is achieved by the following mechanism:
2158          *
2159          * - Before rendering a new frame, check that we aren't still scanning
2160          *   out from that half of the surface.
2161          * - After rendering a frame, push a semaphore write with the render
2162          *   offset and a non-stall interrupt.
2163          * - In response to the non-stall interrupt, perform the viewport
2164          *   flip to the render offset.
2165          */
2166         NvU32 activeViewportOffset =
2167             nvApiHeadGetActiveViewportOffset(pDispEvo, apiHead);
2168 
2169         nvAssert((activeViewportOffset == 0) ||
2170                  (activeViewportOffset == pHsChannel->config.frameSize.height));
2171 
2172         activeViewportOffset /= pHsChannel->config.frameSize.height;
2173 
2174         if (activeViewportOffset == HsGetPreviousOffset(pHsChannel)) {
2175             /*
2176              * The active viewport is the same as the last one we pushed, so
2177              * it's safe to start rendering to pHsChannel->nextOffset; check if
2178              * rendering from a previous interrupt hasn't completed yet.
2179              */
2180             if (pHsChannel->viewportFlipPending) {
2181                 /*
2182                  * A non-stall interrupt hasn't been triggered since we kicked
2183                  * off the previous frame's rendering.
2184                  */
2185                 HsProcFsRecordPreviousFrameNotDone(pHsChannel);
2186             } else {
2187                 NVHsDeviceEvoRec *pHsDevice = pDispEvo->pDevEvo->pHsDevice;
2188 
2189                 HsProcFsRecordScanline(pDispEvo, apiHead);
2190 
2191                 if (HsCanOmitNonSgHsUpdate(pHsChannel)) {
2192                     HsProcFsRecordOmittedNonSgHsUpdate(pHsChannel);
2193                 } else {
2194                     nvHsNextFrame(pHsDevice, pHsChannel, NV_HS_NEXT_FRAME_REQUEST_TYPE_VBLANK);
2195                 }
2196             }
2197         } else {
2198             /*
2199              * The viewport flip we pushed after the previous frame's rendering
2200              * hasn't been applied in hardware yet.
2201              */
2202             HsProcFsRecordPreviousFrameNotDone(pHsChannel);
2203         }
2204 
2205         HsCheckSwapGroupFlipDone(pDispEvo->pDevEvo, pDispEvo->pSwapGroup[apiHead]);
2206     }
2207 }
2208 
2209 /*!
2210  * Receive vblank callback, in process context with nvkms_lock held.
2211  *
2212  */
HsVBlankCallback(NVDispEvoRec * pDispEvo,NVVBlankCallbackPtr pCallbackData)2213 static void HsVBlankCallback(NVDispEvoRec *pDispEvo,
2214                              NVVBlankCallbackPtr pCallbackData)
2215 {
2216     const NvU32 apiHead = pCallbackData->apiHead;
2217     NVHsChannelEvoPtr pHsChannel = pDispEvo->pHsChannel[apiHead];
2218     NVHsDeviceEvoRec *pHsDevice = pDispEvo->pDevEvo->pHsDevice;
2219 
2220     /*
2221      * The pHsChannel may have been torn down between when the vblank was
2222      * generated and when this was called.  Ignore spurious callbacks.
2223      */
2224     if (pHsChannel == NULL) {
2225         return;
2226     }
2227 
2228     if (!pHsChannel->usingRgIntrForSwapGroups &&
2229         pHsChannel->config.neededForSwapGroup) {
2230         HsCheckSwapGroupFlipDone(pDispEvo->pDevEvo, pDispEvo->pSwapGroup[apiHead]);
2231     }
2232 
2233     if (pHsChannel->usingRgIntrForSwapGroups &&
2234         pHsChannel->config.neededForSwapGroup) {
2235         // The next frame will be rendered during the RG line 1 interrupt.
2236         return;
2237     }
2238 
2239     /*
2240      * If we have not flipped to the previous buffer, yet, we should not render
2241      * to the next buffer.  Wait until the next vblank callback.
2242      */
2243     if (!IsPreviousFrameDone(pHsChannel)) {
2244         HsProcFsRecordPreviousFrameNotDone(pHsChannel);
2245         return;
2246     }
2247 
2248     HsProcFsRecordScanline(pDispEvo, apiHead);
2249 
2250     /*
2251      * XXX NVKMS HEADSURFACE TODO: evaluate whether there has been
2252      * damage to the source buffer since the last headSurface frame.
2253      * Only if so, perform the headSurface transformation and flip to
2254      * the resulting headSurface buffer.
2255      *
2256      * For headSurface bringup purposes, just always flip to the next
2257      * headSurface buffer.
2258      */
2259 
2260     /*
2261      * When fullscreen swapgroup flipping, updating
2262      * non-swapgroup content at vblank is unnecessary and
2263      * dangerous, since it results in releasing client
2264      * semaphores before their contents have actually been
2265      * displayed.
2266      */
2267     if (!pHsChannel->swapGroupFlipping) {
2268         nvHsNextFrame(pHsDevice, pHsChannel,
2269                       NV_HS_NEXT_FRAME_REQUEST_TYPE_VBLANK);
2270     }
2271 }
2272 
2273 /*!
2274  * Schedule vblank callbacks from resman on a specific head and subdevice.
2275  */
nvHsAddVBlankCallback(NVHsChannelEvoPtr pHsChannel)2276 void nvHsAddVBlankCallback(NVHsChannelEvoPtr pHsChannel)
2277 {
2278     NVDispEvoRec *pDispEvo = pHsChannel->pDispEvo;
2279 
2280     pHsChannel->vBlankCallback =
2281         nvApiHeadRegisterVBlankCallback(pDispEvo,
2282                                         pHsChannel->apiHead,
2283                                         HsVBlankCallback,
2284                                         NULL);
2285 }
2286 
2287 /*!
2288  * Add an RG line 1 callback to check the swapgroup flip notifier and release
2289  * its associated deferred request fifo.
2290  *
2291  * This is done in an RG line 1 callback instead of the vblank callback to WAR
2292  * an issue where certain mode timings cause the vblank callback to fire
2293  * slightly before LOADV causes the notifier to transition from NOT_BEGUN
2294  * to BEGUN, causing an extra frame of delay before the next vblank occurs and
2295  * the deferred request fifo can be released.
2296  */
nvHsAddRgLine1Callback(NVHsChannelEvoPtr pHsChannel)2297 void nvHsAddRgLine1Callback(NVHsChannelEvoPtr pHsChannel)
2298 {
2299     NVDispEvoRec *pDispEvo = pHsChannel->pDispEvo;
2300     NvBool found;
2301     NvU32 val;
2302 
2303     /*
2304      * Use the RG line 1 interrupt to check swapgroup completion by default,
2305      * but allow setting NVKMS_DELAY_SWAPGROUP_CHECK=0 by regkey to revert to
2306      * the old method of checking during vblank for debugging purposes.
2307      */
2308     found = nvGetRegkeyValue(pDispEvo->pDevEvo, "NVKMS_DELAY_SWAPGROUP_CHECK",
2309                              &val);
2310 
2311     if (found && (val == 0)) {
2312         return;
2313     }
2314 
2315     pHsChannel->pRgIntrCallback =
2316         nvApiHeadAddRgLine1Callback(pDispEvo,
2317                                     pHsChannel->apiHead,
2318                                     HsRgLine1CallbackProc,
2319                                     (void*)(NvUPtr)pHsChannel->apiHead);
2320 
2321     if (pHsChannel->pRgIntrCallback == NULL) {
2322         nvAssert(!"Failed to register headSurface RG line 1 interrupt");
2323     } else {
2324         pHsChannel->usingRgIntrForSwapGroups = TRUE;
2325     }
2326 }
2327 
2328 /*!
2329  * Cancel RG line 1 callbacks from resman on the specified head and subdevice.
2330  *
2331  * The same limitations regarding leftover vblank callbacks after vblank
2332  * callbacks are disabled in nvHsRemoveVblankCallback apply to RG callbacks.
2333  */
nvHsRemoveRgLine1Callback(NVHsChannelEvoPtr pHsChannel)2334 void nvHsRemoveRgLine1Callback(NVHsChannelEvoPtr pHsChannel)
2335 {
2336     const NVDispEvoRec *pDispEvo = pHsChannel->pDispEvo;
2337 
2338     if (pHsChannel->usingRgIntrForSwapGroups) {
2339         nvRmRemoveRgLine1Callback(pDispEvo,
2340                                   pHsChannel->pRgIntrCallback);
2341         pHsChannel->pRgIntrCallback = NULL;
2342     }
2343 }
2344 
2345 /*!
2346  * Cancel vblank callbacks from resman on the specified head and subdevice.
2347  *
2348  * Note that there could currently be callbacks in flight.  We should be
2349  * prepared to handle a spurious callback after cancelling the callbacks here.
2350  *
2351  * XXX NVKMS HEADSURFACE TODO: It would be better to:
2352  *
2353  * (a) Remove the vblank callback before the modeset that disables headSurface.
2354  * (b) Drain/cancel any in flight callbacks while holding the nvkms_lock.
2355  *
2356  * A mechanism like that should avoid spurious callbacks.
2357  */
nvHsRemoveVBlankCallback(NVHsChannelEvoPtr pHsChannel)2358 void nvHsRemoveVBlankCallback(NVHsChannelEvoPtr pHsChannel)
2359 {
2360     NVDispEvoRec *pDispEvo = pHsChannel->pDispEvo;
2361 
2362     nvApiHeadUnregisterVBlankCallback(pDispEvo,
2363                                       pHsChannel->vBlankCallback);
2364     pHsChannel->vBlankCallback = NULL;
2365 }
2366 
nvHsAllocStatistics(NVHsChannelEvoRec * pHsChannel)2367 void nvHsAllocStatistics(
2368     NVHsChannelEvoRec *pHsChannel)
2369 {
2370 #if NVKMS_PROCFS_ENABLE
2371     const NVDispEvoRec *pDispEvo = pHsChannel->pDispEvo;
2372     const NvU32 apiHead = pHsChannel->apiHead;
2373     const NVHwModeTimingsEvo *pTimings =
2374         &pDispEvo->apiHeadState[apiHead].timings;
2375     NvU32 n;
2376 
2377     nvkms_memset(&pHsChannel->statistics, 0, sizeof(pHsChannel->statistics));
2378 
2379     pHsChannel->statistics.scanLine.vVisible = nvEvoVisibleHeight(pTimings);
2380 
2381     n = pHsChannel->statistics.scanLine.vVisible + 1;
2382 
2383     pHsChannel->statistics.scanLine.pHistogram = nvCalloc(1, sizeof(NvU64) * n);
2384 #endif /* NVKMS_PROCFS_ENABLE */
2385 }
2386 
nvHsFreeStatistics(NVHsChannelEvoRec * pHsChannel)2387 void nvHsFreeStatistics(
2388     NVHsChannelEvoRec *pHsChannel)
2389 {
2390 #if NVKMS_PROCFS_ENABLE
2391     nvFree(pHsChannel->statistics.scanLine.pHistogram);
2392     nvkms_memset(&pHsChannel->statistics, 0, sizeof(pHsChannel->statistics));
2393 #endif /* NVKMS_PROCFS_ENABLE */
2394 }
2395 
2396 #if NVKMS_PROCFS_ENABLE
2397 
2398 static const struct {
2399     const char *before;
2400     const char *after;
2401 } HsProcFsIndentTable[] = {
2402     [0] = { .before = "", .after = "    " },
2403     [1] = { .before = " ", .after = "   " },
2404     [2] = { .before = "  ", .after = "  " },
2405     [3] = { .before = "   ", .after = " " },
2406     [5] = { .before = "    ", .after = "" },
2407 };
2408 
HsProcFsIndentBefore(NvU8 indent)2409 static const char *HsProcFsIndentBefore(NvU8 indent)
2410 {
2411     nvAssert(indent < ARRAY_LEN(HsProcFsIndentTable));
2412 
2413     return HsProcFsIndentTable[indent].before;
2414 }
2415 
HsProcFsIndentAfter(NvU8 indent)2416 static const char *HsProcFsIndentAfter(NvU8 indent)
2417 {
2418     nvAssert(indent < ARRAY_LEN(HsProcFsIndentTable));
2419 
2420     return HsProcFsIndentTable[indent].after;
2421 }
2422 
HsProcFsGpuTime(NVEvoInfoStringRec * pInfoString,const NvU64 nFrames,const NvU64 gpuTimeSpent,const NvU8 indent)2423 static void HsProcFsGpuTime(
2424     NVEvoInfoStringRec *pInfoString,
2425     const NvU64 nFrames,
2426     const NvU64 gpuTimeSpent,
2427     const NvU8 indent)
2428 {
2429     /*
2430      * Use nFrames - 1 to compute averageGpuTimeNs: the nvHs3dRenderFrame() path
2431      * increments nFrames at the end of rendering a frame, but it only updates
2432      * gpuTimeSpent at the start of rendering the _next_ frame.  I.e.,
2433      * gpuTimeSpent has time for nFrames - 1 frames.
2434      */
2435     const NvU64 averageGpuTimeNs =
2436         (nFrames <= 1) ? 0 : (gpuTimeSpent / (nFrames - 1));
2437     const NvU64 averageGpuTimeUs = (averageGpuTimeNs + 500) / 1000;
2438     const NvU64 nFramesToReport = (nFrames <= 1) ? 0 : nFrames - 1;
2439 
2440     nvEvoLogInfoString(
2441         pInfoString, "   %savg GPU time / frame%s   : "
2442         "%" NvU64_fmtu ".%03" NvU64_fmtu " msec "
2443         "(%" NvU64_fmtu " nsec / %" NvU64_fmtu " frames)",
2444         HsProcFsIndentBefore(indent),
2445         HsProcFsIndentAfter(indent),
2446         averageGpuTimeUs / 1000,
2447         averageGpuTimeUs % 1000,
2448         gpuTimeSpent,
2449         nFramesToReport);
2450 }
2451 
HsProcFsFrameStatisticsOneEye(NVEvoInfoStringRec * pInfoString,const NVHsChannelEvoRec * pHsChannel,const NvU8 eye,const NvU8 slot,const NvU8 indent)2452 static void HsProcFsFrameStatisticsOneEye(
2453     NVEvoInfoStringRec *pInfoString,
2454     const NVHsChannelEvoRec *pHsChannel,
2455     const NvU8 eye,
2456     const NvU8 slot,
2457     const NvU8 indent)
2458 {
2459     const NVHsChannelStatisticsOneEyeRec *pPerEye =
2460         &pHsChannel->statistics.perEye[eye][slot];
2461 
2462     const NvU64 framesPerMs = pPerEye->fps.framesPerMs;
2463 
2464     nvEvoLogInfoString(
2465         pInfoString,
2466         "   %snFrames%s                : %" NvU64_fmtu,
2467         HsProcFsIndentBefore(indent),
2468         HsProcFsIndentAfter(indent),
2469         pPerEye->nFrames);
2470 
2471     nvEvoLogInfoString(
2472         pInfoString, "   %sFPS (computed every 5s)%s: "
2473         "%" NvU64_fmtu ".%03" NvU64_fmtu,
2474         HsProcFsIndentBefore(indent),
2475         HsProcFsIndentAfter(indent),
2476         framesPerMs / 1000,
2477         framesPerMs % 1000);
2478 
2479     HsProcFsGpuTime(
2480         pInfoString,
2481         pPerEye->nFrames,
2482         pPerEye->gpuTimeSpent,
2483         indent);
2484 }
2485 
HsProcFsFrameStatisticsOneSlot(NVEvoInfoStringRec * pInfoString,const NVHsChannelEvoRec * pHsChannel,const NvU8 slot,const NvU8 indent)2486 static void HsProcFsFrameStatisticsOneSlot(
2487     NVEvoInfoStringRec *pInfoString,
2488     const NVHsChannelEvoRec *pHsChannel,
2489     const NvU8 slot,
2490     const NvU8 indent)
2491 {
2492     const char *eyeLabel[] = {
2493         [NVKMS_LEFT]  = "Left Eye ",
2494         [NVKMS_RIGHT] = "Right Eye",
2495     };
2496 
2497     const NvBool needEyeLabel =
2498         pHsChannel->statistics.perEye[NVKMS_RIGHT][slot].nFrames != 0;
2499     NvU8 eye;
2500 
2501     for (eye = NVKMS_LEFT; eye < NVKMS_MAX_EYES; eye++) {
2502 
2503         NvU8 localIndent = 0;
2504 
2505         if (pHsChannel->statistics.perEye[eye][slot].nFrames == 0) {
2506             continue;
2507         }
2508 
2509         if (needEyeLabel) {
2510             nvEvoLogInfoString(
2511                 pInfoString, "   %s%s%s              :",
2512                 HsProcFsIndentBefore(indent),
2513                 eyeLabel[eye],
2514                 HsProcFsIndentAfter(indent));
2515             localIndent++;
2516         }
2517 
2518         HsProcFsFrameStatisticsOneEye(
2519             pInfoString,
2520             pHsChannel,
2521             eye,
2522             slot,
2523             indent + localIndent);
2524     }
2525 }
2526 
HsProcFsFrameStatistics(NVEvoInfoStringRec * pInfoString,const NVHsChannelEvoRec * pHsChannel)2527 static void HsProcFsFrameStatistics(
2528     NVEvoInfoStringRec *pInfoString,
2529     const NVHsChannelEvoRec *pHsChannel)
2530 {
2531     NvU8 slot;
2532 
2533     if (pHsChannel->config.neededForSwapGroup) {
2534         nvEvoLogInfoString(pInfoString,
2535                            "   VBLANK frames              :");
2536 
2537         nvEvoLogInfoString(pInfoString,
2538                            "    Old swapGroup content     :");
2539 
2540         slot = Hs3dStatisticsGetSlot(
2541                     pHsChannel,
2542                     NV_HS_NEXT_FRAME_REQUEST_TYPE_VBLANK, 0,
2543                     TRUE /* honorSwapGroupClipList */);
2544 
2545         HsProcFsFrameStatisticsOneSlot(pInfoString, pHsChannel, slot, 2);
2546 
2547         nvEvoLogInfoString(pInfoString,
2548                            "    New swapGroup content     :");
2549 
2550         slot = Hs3dStatisticsGetSlot(
2551                     pHsChannel,
2552                     NV_HS_NEXT_FRAME_REQUEST_TYPE_VBLANK, 0,
2553                     FALSE /* honorSwapGroupClipList */);
2554 
2555         HsProcFsFrameStatisticsOneSlot(pInfoString, pHsChannel, slot, 2);
2556 
2557         nvEvoLogInfoString(pInfoString,
2558                            "   SWAP_GROUP_READY frames    :");
2559 
2560         slot = Hs3dStatisticsGetSlot(
2561                     pHsChannel,
2562                     NV_HS_NEXT_FRAME_REQUEST_TYPE_SWAP_GROUP_READY, 0,
2563                     FALSE /* honorSwapGroupClipList */);
2564 
2565         HsProcFsFrameStatisticsOneSlot(pInfoString, pHsChannel, slot, 1);
2566 
2567     } else {
2568         const NvU8 indent = 0; /* start with no indentation */
2569 
2570         slot = Hs3dStatisticsGetSlot(
2571                     pHsChannel,
2572                     NV_HS_NEXT_FRAME_REQUEST_TYPE_VBLANK, 0,
2573                     FALSE);
2574 
2575         HsProcFsFrameStatisticsOneSlot(pInfoString, pHsChannel, slot, indent);
2576     }
2577 }
2578 
HsProcFsScanLine(NVEvoInfoStringRec * pInfoString,const NVHsChannelEvoRec * pHsChannel)2579 static void HsProcFsScanLine(
2580     NVEvoInfoStringRec *pInfoString,
2581     const NVHsChannelEvoRec *pHsChannel)
2582 {
2583     NvU16 i;
2584 
2585     nvEvoLogInfoString(pInfoString,
2586                        "   scanLine information       :");
2587 
2588     nvEvoLogInfoString(pInfoString,
2589                        "    nInBlankingPeriod         : %" NvU64_fmtu,
2590                        pHsChannel->statistics.scanLine.nInBlankingPeriod);
2591     nvEvoLogInfoString(pInfoString,
2592                        "    nNotInBlankingPeriod      : %" NvU64_fmtu,
2593                        pHsChannel->statistics.scanLine.nNotInBlankingPeriod);
2594     nvEvoLogInfoString(pInfoString,
2595                        "    vVisible                  : %d",
2596                        pHsChannel->statistics.scanLine.vVisible);
2597 
2598     if (pHsChannel->statistics.scanLine.pHistogram == NULL) {
2599 
2600         nvEvoLogInfoString(pInfoString,
2601                            "    scanline histogram        : failed allocation");
2602     } else {
2603 
2604         nvEvoLogInfoString(pInfoString,
2605                            "    scanline histogram        :");
2606 
2607         for (i = 0; i <= pHsChannel->statistics.scanLine.vVisible; i++) {
2608 
2609             if (pHsChannel->statistics.scanLine.pHistogram[i] != 0) {
2610                 nvEvoLogInfoString(pInfoString,
2611                     "     scanLine[%04d]           : %" NvU64_fmtu,
2612                     i, pHsChannel->statistics.scanLine.pHistogram[i]);
2613             }
2614         }
2615     }
2616 }
2617 
HsProcFsFlipQueueOneEntry(NVEvoInfoStringRec * pInfoString,const NVHsLayerRequestedFlipState * pFlipState)2618 static void HsProcFsFlipQueueOneEntry(
2619     NVEvoInfoStringRec *pInfoString,
2620     const NVHsLayerRequestedFlipState *pFlipState)
2621 {
2622     /*
2623      * Print the pointers by casting to NvUPtr and formatting with NvUPtr_fmtx,
2624      * so that NULL is printed as "0x0", rather than "(null)".
2625      */
2626 
2627     nvEvoLogInfoString(pInfoString,
2628         "        pSurfaceEvo(L,R)      : 0x%" NvUPtr_fmtx ", 0x%" NvUPtr_fmtx,
2629         (NvUPtr)pFlipState->pSurfaceEvo[NVKMS_LEFT],
2630         (NvUPtr)pFlipState->pSurfaceEvo[NVKMS_RIGHT]);
2631 
2632     if (!pFlipState->syncObject.usingSyncpt) {
2633         nvEvoLogInfoString(pInfoString,
2634             "        semaphore             : "
2635             "acquire pSurfaceEvo: 0x%" NvUPtr_fmtx ", "
2636             "release pSurfaceEvo: 0x%" NvUPtr_fmtx ", "
2637             "acquire value: 0x%08x, "
2638             "release value: 0x%08x",
2639             (NvUPtr)pFlipState->syncObject.u.semaphores.acquireSurface.pSurfaceEvo,
2640             (NvUPtr)pFlipState->syncObject.u.semaphores.releaseSurface.pSurfaceEvo,
2641             pFlipState->syncObject.u.semaphores.acquireValue,
2642             pFlipState->syncObject.u.semaphores.releaseValue);
2643     }
2644 }
2645 
HsProcFsFlipQueue(NVEvoInfoStringRec * pInfoString,const NVHsChannelEvoRec * pHsChannel)2646 static void HsProcFsFlipQueue(
2647     NVEvoInfoStringRec *pInfoString,
2648     const NVHsChannelEvoRec *pHsChannel)
2649 {
2650     const NVHsChannelFlipQueueEntry *pEntry;
2651     NvU8 layer;
2652 
2653     for (layer = 0; layer < ARRAY_LEN(pHsChannel->flipQueue); layer++) {
2654 
2655         const char *layerString[NVKMS_MAX_LAYERS_PER_HEAD] = {
2656             [NVKMS_MAIN_LAYER]    = "(main)   ",
2657             [NVKMS_OVERLAY_LAYER] = "(overlay)",
2658         };
2659 
2660         nvEvoLogInfoString(pInfoString,
2661             "   flipQueue%s         :", layerString[layer]);
2662 
2663         nvEvoLogInfoString(pInfoString,
2664             "     current                  :");
2665 
2666         HsProcFsFlipQueueOneEntry(pInfoString,
2667                                   &pHsChannel->flipQueue[layer].current);
2668 
2669         nvListForEachEntry(pEntry,
2670                            &pHsChannel->flipQueue[layer].queue,
2671                            flipQueueEntry) {
2672 
2673             nvEvoLogInfoString(pInfoString,
2674                 "     pending                  :");
2675 
2676             HsProcFsFlipQueueOneEntry(pInfoString, &pEntry->hwState);
2677         }
2678     }
2679 }
2680 
HsGetEyeMaskString(const NvU8 eyeMask)2681 static const char *HsGetEyeMaskString(const NvU8 eyeMask)
2682 {
2683     if (eyeMask == NVBIT(NVKMS_LEFT)) {
2684         return "L";
2685     } else {
2686         nvAssert(eyeMask == (NVBIT(NVKMS_LEFT) | NVBIT(NVKMS_RIGHT)));
2687         return "L|R";
2688     }
2689 }
2690 
HsGetPixelShiftString(const enum NvKmsPixelShiftMode pixelShift)2691 static const char *HsGetPixelShiftString(
2692     const enum NvKmsPixelShiftMode pixelShift)
2693 {
2694     switch (pixelShift) {
2695     case NVKMS_PIXEL_SHIFT_NONE:            return "none";
2696     case NVKMS_PIXEL_SHIFT_4K_TOP_LEFT:     return "4kTopLeft";
2697     case NVKMS_PIXEL_SHIFT_4K_BOTTOM_RIGHT: return "4kBottomRight";
2698     case NVKMS_PIXEL_SHIFT_8K:              return "8k";
2699     }
2700 
2701     return "unknown";
2702 }
2703 
HsProcFsTransform(NVEvoInfoStringRec * pInfoString,const NVHsChannelEvoRec * pHsChannel)2704 static void HsProcFsTransform(
2705     NVEvoInfoStringRec *pInfoString,
2706     const NVHsChannelEvoRec *pHsChannel)
2707 {
2708     nvEvoLogInfoString(pInfoString,
2709                        "   transform matrix           : "
2710                        "{ { 0x%08x, 0x%08x, 0x%08x },",
2711                        F32viewAsNvU32(pHsChannel->config.transform.m[0][0]),
2712                        F32viewAsNvU32(pHsChannel->config.transform.m[0][1]),
2713                        F32viewAsNvU32(pHsChannel->config.transform.m[0][2]));
2714 
2715     nvEvoLogInfoString(pInfoString,
2716                        "                              : "
2717                        "  { 0x%08x, 0x%08x, 0x%08x },",
2718                        F32viewAsNvU32(pHsChannel->config.transform.m[1][0]),
2719                        F32viewAsNvU32(pHsChannel->config.transform.m[1][1]),
2720                        F32viewAsNvU32(pHsChannel->config.transform.m[1][2]));
2721 
2722     nvEvoLogInfoString(pInfoString,
2723                        "                              : "
2724                        "  { 0x%08x, 0x%08x, 0x%08x } }",
2725                        F32viewAsNvU32(pHsChannel->config.transform.m[2][0]),
2726                        F32viewAsNvU32(pHsChannel->config.transform.m[2][1]),
2727                        F32viewAsNvU32(pHsChannel->config.transform.m[2][2]));
2728 }
2729 
HsProcFsStaticWarpMesh(NVEvoInfoStringRec * pInfoString,const NVHsChannelEvoRec * pHsChannel)2730 static void HsProcFsStaticWarpMesh(
2731     NVEvoInfoStringRec *pInfoString,
2732     const NVHsChannelEvoRec *pHsChannel)
2733 {
2734     nvEvoLogInfoString(pInfoString,
2735                        "   staticWarpMesh             : "
2736                        "{ { 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x },",
2737                        pHsChannel->config.staticWarpMesh.vertex[0].x,
2738                        pHsChannel->config.staticWarpMesh.vertex[0].y,
2739                        pHsChannel->config.staticWarpMesh.vertex[0].u,
2740                        pHsChannel->config.staticWarpMesh.vertex[0].v,
2741                        pHsChannel->config.staticWarpMesh.vertex[0].r,
2742                        pHsChannel->config.staticWarpMesh.vertex[0].q);
2743 
2744     nvEvoLogInfoString(pInfoString,
2745                        "                              : "
2746                        "  { 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x },",
2747                        pHsChannel->config.staticWarpMesh.vertex[1].x,
2748                        pHsChannel->config.staticWarpMesh.vertex[1].y,
2749                        pHsChannel->config.staticWarpMesh.vertex[1].u,
2750                        pHsChannel->config.staticWarpMesh.vertex[1].v,
2751                        pHsChannel->config.staticWarpMesh.vertex[1].r,
2752                        pHsChannel->config.staticWarpMesh.vertex[1].q);
2753 
2754     nvEvoLogInfoString(pInfoString,
2755                        "                              : "
2756                        "  { 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x },",
2757                        pHsChannel->config.staticWarpMesh.vertex[2].x,
2758                        pHsChannel->config.staticWarpMesh.vertex[2].y,
2759                        pHsChannel->config.staticWarpMesh.vertex[2].u,
2760                        pHsChannel->config.staticWarpMesh.vertex[2].v,
2761                        pHsChannel->config.staticWarpMesh.vertex[2].r,
2762                        pHsChannel->config.staticWarpMesh.vertex[2].q);
2763 
2764     nvEvoLogInfoString(pInfoString,
2765                        "                              : "
2766                        "  { 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x } }",
2767                        pHsChannel->config.staticWarpMesh.vertex[3].x,
2768                        pHsChannel->config.staticWarpMesh.vertex[3].y,
2769                        pHsChannel->config.staticWarpMesh.vertex[3].u,
2770                        pHsChannel->config.staticWarpMesh.vertex[3].v,
2771                        pHsChannel->config.staticWarpMesh.vertex[3].r,
2772                        pHsChannel->config.staticWarpMesh.vertex[3].q);
2773 }
2774 
HsProcFsGetNeededForString(const NVHsChannelEvoRec * pHsChannel)2775 static const char *HsProcFsGetNeededForString(
2776     const NVHsChannelEvoRec *pHsChannel)
2777 {
2778     if (pHsChannel->config.neededForModeset &&
2779         pHsChannel->config.neededForSwapGroup) {
2780         return "modeset, swapgroup";
2781     }
2782 
2783     if (pHsChannel->config.neededForModeset &&
2784         !pHsChannel->config.neededForSwapGroup) {
2785         return "modeset";
2786     }
2787 
2788     if (!pHsChannel->config.neededForModeset &&
2789         pHsChannel->config.neededForSwapGroup) {
2790         return "swapgroup";
2791     }
2792 
2793     return "unknown";
2794 }
2795 
HsProcFsFrameSemaphores(NVEvoInfoStringRec * pInfoString,const NVHsChannelEvoRec * pHsChannel)2796 static void HsProcFsFrameSemaphores(
2797     NVEvoInfoStringRec *pInfoString,
2798     const NVHsChannelEvoRec *pHsChannel)
2799 {
2800     const NVDispEvoRec *pDispEvo = pHsChannel->pDispEvo;
2801     const NVHsDeviceEvoRec *pHsDevice = pDispEvo->pDevEvo->pHsDevice;
2802     const NvU32 sd = pDispEvo->displayOwner;
2803     const NVHsNotifiersOneSdRec *p = pHsDevice->notifiers.sd[sd].ptr;
2804     const NvGpuSemaphore *pSema =
2805         (const NvGpuSemaphore *)p->semaphore[pHsChannel->apiHead];
2806 
2807     NvU8 buffer;
2808 
2809     for (buffer = 0; buffer < NVKMS_HEAD_SURFACE_MAX_BUFFERS; buffer++) {
2810         nvEvoLogInfoString(pInfoString,
2811                            "   frameSemaphore[%d]          : 0x%0x",
2812                            buffer,
2813                            pSema[buffer].data[0]);
2814     }
2815 }
2816 
nvHsProcFs(NVEvoInfoStringRec * pInfoString,NVDevEvoRec * pDevEvo,NvU32 dispIndex,NvU32 apiHead)2817 void nvHsProcFs(
2818     NVEvoInfoStringRec *pInfoString,
2819     NVDevEvoRec *pDevEvo,
2820     NvU32 dispIndex,
2821     NvU32 apiHead)
2822 {
2823     NVDispEvoPtr pDispEvo = pDevEvo->pDispEvo[dispIndex];
2824     const NVHsChannelEvoRec *pHsChannel = pDispEvo->pHsChannel[apiHead];
2825     const NVHsStateOneHeadAllDisps *pHsOneHeadAllDisps =
2826         &pDevEvo->apiHeadSurfaceAllDisps[apiHead];
2827 
2828     if (pHsChannel == NULL) {
2829         nvEvoLogInfoString(pInfoString,
2830                            "  headSurface[head:%02d]        : disabled", apiHead);
2831         return;
2832     }
2833 
2834     nvEvoLogInfoString(pInfoString,
2835                        "  headSurface[head:%02d]        : "
2836                        "enabled (needed for: %s)",
2837                        apiHead, HsProcFsGetNeededForString(pHsChannel));
2838 
2839     HsProcFsFrameStatistics(pInfoString, pHsChannel);
2840 
2841     nvEvoLogInfoString(pInfoString,
2842                        "   nextIndex                  : %d",
2843                        pHsChannel->nextIndex);
2844 
2845     nvEvoLogInfoString(pInfoString,
2846                        "   nextOffset                 : %d",
2847                        pHsChannel->nextOffset);
2848 
2849     nvEvoLogInfoString(pInfoString,
2850                        "   nPreviousFrameNotDone      : %" NvU64_fmtu,
2851                        pHsChannel->statistics.nPreviousFrameNotDone);
2852 
2853     nvEvoLogInfoString(pInfoString,
2854                        "   nOmittedNonSgHsUpdates     : %" NvU64_fmtu,
2855                        pHsChannel->statistics.nOmittedNonSgHsUpdates);
2856 
2857     nvEvoLogInfoString(pInfoString,
2858                        "   nFullscreenSgFrames        : %" NvU64_fmtu,
2859                        pHsChannel->statistics.nFullscreenSgFrames);
2860 
2861     nvEvoLogInfoString(pInfoString,
2862                        "   nNonFullscreenSgFrames     : %" NvU64_fmtu,
2863                        pHsChannel->statistics.nNonFullscreenSgFrames);
2864 
2865     nvEvoLogInfoString(pInfoString,
2866                        "   viewPortIn                 : %d x %d +%d +%d",
2867                        pHsChannel->config.viewPortIn.width,
2868                        pHsChannel->config.viewPortIn.height,
2869                        pHsChannel->config.viewPortIn.x,
2870                        pHsChannel->config.viewPortIn.y);
2871 
2872     nvEvoLogInfoString(pInfoString,
2873                        "   viewPortOut                : %d x %d +%d +%d",
2874                        pHsChannel->config.viewPortOut.width,
2875                        pHsChannel->config.viewPortOut.height,
2876                        pHsChannel->config.viewPortOut.x,
2877                        pHsChannel->config.viewPortOut.y);
2878 
2879     nvEvoLogInfoString(pInfoString,
2880                        "   frameSize                  : %d x %d",
2881                        pHsChannel->config.frameSize.width,
2882                        pHsChannel->config.frameSize.height);
2883 
2884     nvEvoLogInfoString(pInfoString,
2885                        "   surfaceSize                : %d x %d",
2886                        pHsChannel->config.surfaceSize.width,
2887                        pHsChannel->config.surfaceSize.height);
2888 
2889     nvEvoLogInfoString(pInfoString,
2890                        "   stagingSurfaceSize         : %d x %d",
2891                        pHsChannel->config.stagingSurfaceSize.width,
2892                        pHsChannel->config.stagingSurfaceSize.height);
2893 
2894     nvEvoLogInfoString(pInfoString,
2895                        "   allDispsSurfaceSize        : %d x %d",
2896                        pHsOneHeadAllDisps->size.width,
2897                        pHsOneHeadAllDisps->size.height);
2898 
2899     nvEvoLogInfoString(pInfoString,
2900                        "   allDispsStagingSize        : %d x %d",
2901                        pHsOneHeadAllDisps->stagingSize.width,
2902                        pHsOneHeadAllDisps->stagingSize.height);
2903 
2904     nvEvoLogInfoString(pInfoString,
2905                        "   allDispsSurfaceCount       : %d",
2906                        pHsOneHeadAllDisps->surfaceCount);
2907 
2908     nvEvoLogInfoString(pInfoString,
2909                        "   eyeMask                    : %s",
2910                        HsGetEyeMaskString(pHsChannel->config.eyeMask));
2911 
2912     nvEvoLogInfoString(pInfoString,
2913                        "   pixelShift                 : %s",
2914                        HsGetPixelShiftString(pHsChannel->config.pixelShift));
2915 
2916     HsProcFsTransform(pInfoString, pHsChannel);
2917 
2918     HsProcFsStaticWarpMesh(pInfoString, pHsChannel);
2919 
2920     HsProcFsFlipQueue(pInfoString, pHsChannel);
2921 
2922     HsProcFsFrameSemaphores(pInfoString, pHsChannel);
2923 
2924     HsProcFsScanLine(pInfoString, pHsChannel);
2925 }
2926 #endif /* NVKMS_PROCFS_ENABLE */
2927