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, ¶ms,
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