1 /*
2  * SPDX-FileCopyrightText: Copyright (c) 1993-2022 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
3  * SPDX-License-Identifier: MIT
4  *
5  * Permission is hereby granted, free of charge, to any person obtaining a
6  * copy of this software and associated documentation files (the "Software"),
7  * to deal in the Software without restriction, including without limitation
8  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
9  * and/or sell copies of the Software, and to permit persons to whom the
10  * Software is furnished to do so, subject to the following conditions:
11  *
12  * The above copyright notice and this permission notice shall be included in
13  * all copies or substantial portions of the Software.
14  *
15  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
18  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
21  * DEALINGS IN THE SOFTWARE.
22  */
23 
24 /******************************************************************************
25 *
26 *   Description:
27 *       This file contains functions managing the display - both Disp and DispCommon
28 *       entries with their insides (DispChannelList and DispDmaControlList)
29 *
30 ******************************************************************************/
31 
32 #define RM_STRICT_CONFIG_EMIT_DISP_ENGINE_DEFINITIONS     0
33 
34 #include "core/locks.h"
35 #include "resserv/rs_client.h"
36 
37 #include "gpu/gpu.h"
38 #include "gpu/device/device.h"
39 #include "gpu/disp/disp_objs.h"
40 #include "gpu/disp/disp_channel.h"
41 #include "gpu/disp/kern_disp.h"
42 #include "gpu_mgr/gpu_mgr.h"
43 
44 #include "kernel/gpu/intr/intr.h"
45 
46 #include "class/cl0073.h"                // NV04_DISPLAY_COMMON
47 #include "class/cl5070.h"                // NV50_DISPLAY
48 #include "class/clc370.h"                // NVC370_DISPLAY
49 
50 NV_STATUS
51 dispapiConstruct_IMPL
52 (
53     DisplayApi                   *pDisplayApi,
54     CALL_CONTEXT                 *pCallContext,
55     RS_RES_ALLOC_PARAMS_INTERNAL *pParams
56 )
57 {
58     NV_STATUS        status;
59     CLASSDESCRIPTOR *pClassDescriptor;
60     RsResourceRef   *pResourceRef = pCallContext->pResourceRef;
61     OBJGPU          *pGpu;
62     KernelDisplay   *pKernelDisplay;
63     NvBool           bBcResource;
64     NvU32            i;
65 
66     LOCK_ASSERT_AND_RETURN(rmapiLockIsOwner());
67 
68     // Use gpuGetByRef instead of GpuResource because it will work even if resource
69     // isn't a GpuResource.
70     status = gpuGetByRef(pResourceRef, &bBcResource, &pGpu);
71     if (status != NV_OK)
72         return status;
73 
74     // Find class in class db (verifies class is valid for this GPU)
75     status = gpuGetClassByClassId(pGpu, pParams->externalClassId, &pClassDescriptor);
76     if (status != NV_OK)
77     {
78         NV_PRINTF(LEVEL_WARNING, "bad class 0x%x\n", pParams->externalClassId);
79         return NV_ERR_INVALID_CLASS;
80     }
81 
82     // Check display is enabled (i.e. not displayless)
83     pKernelDisplay = GPU_GET_KERNEL_DISPLAY(pGpu);
84     if (pKernelDisplay == NULL)
85     {
86         return NV_ERR_NOT_SUPPORTED;
87     }
88 
89     for (i = 0; i < NV2080_MAX_SUBDEVICES; i++)
90         pDisplayApi->pNotifyActions[i]    = NULL;
91 
92     pDisplayApi->pGpuInRmctrl = NULL;
93     pDisplayApi->pGpuGrp = gpumgrGetGpuGrpFromGpu(pGpu);
94     pDisplayApi->bBcResource = bBcResource;
95     pDisplayApi->hNotifierMemory = NV01_NULL_OBJECT;
96     pDisplayApi->pNotifierMemory = NULL;
97 
98     gpuSetThreadBcState(pGpu, bBcResource);
99 
100     return status;
101 }
102 
103 void
104 dispapiDestruct_IMPL
105 (
106     DisplayApi *pDisplayApi
107 )
108 {
109     NvU32 i;
110 
111     // Free notify actions memory if it's been allocated
112     for (i = 0; i < NV2080_MAX_SUBDEVICES; i++)
113     {
114         portMemFree(pDisplayApi->pNotifyActions[i]);
115         pDisplayApi->pNotifyActions[i] = NULL;
116     }
117 }
118 
119 static NV_STATUS
120 _dispapiNotifierInit
121 (
122     DisplayApi *pDisplayApi,
123     NvU32       numNotifiers,
124     NvU32       disableCmd
125 )
126 {
127     NvU32     i, j;
128     NV_STATUS status = NV_OK;
129 
130     pDisplayApi->numNotifiers = numNotifiers;
131 
132     for (i = 0; i < NV2080_MAX_SUBDEVICES; i++)
133     {
134         // get memory for pNotifyActions table
135         pDisplayApi->pNotifyActions[i] = portMemAllocNonPaged(
136                                    pDisplayApi->numNotifiers * sizeof(NvU32));
137         if (pDisplayApi->pNotifyActions[i] != NULL)
138         {
139             // default actions for each notifier type is disabled
140             for (j = 0; j < pDisplayApi->numNotifiers; j++)
141             {
142                 pDisplayApi->pNotifyActions[i][j] = disableCmd;
143             }
144         }
145         else
146         {
147             goto fail;
148         }
149     }
150 
151     return status;
152 
153 fail:
154     // first release any notifyActions memory
155     for (i = 0; i < NV2080_MAX_SUBDEVICES; i++)
156     {
157         portMemFree(pDisplayApi->pNotifyActions[i]);
158         pDisplayApi->pNotifyActions[i] = NULL;
159     }
160 
161     return NV_ERR_INSUFFICIENT_RESOURCES;
162 }
163 
164 NV_STATUS
165 dispobjConstructHal_IMPL
166 (
167     DispObject                   *pDispObject,
168     CALL_CONTEXT                 *pCallContext,
169     RS_RES_ALLOC_PARAMS_INTERNAL *pParams
170 )
171 {
172     DisplayApi  *pDisplayApi  = staticCast(pDispObject, DisplayApi);
173     Device      *pDevice      = dynamicCast(pCallContext->pResourceRef->pParentRef->pResource, Device);
174     GpuResource *pGpuResource = staticCast(pDevice, GpuResource);
175     OBJGPU      *pGpu         = pGpuResource->pGpu;
176     NV_STATUS    rmStatus     = NV_ERR_INVALID_STATE;
177 
178     SLI_LOOP_START(SLI_LOOP_FLAGS_BC_ONLY);
179     {
180         KernelDisplay *pKernelDisplay = GPU_GET_KERNEL_DISPLAY(pGpu);
181 
182         rmStatus = kdispSelectClass_HAL(pGpu, pKernelDisplay, pCallContext->pResourceRef->externalClassId);
183 
184         if (rmStatus != NV_OK)
185         {
186             // If the operation fails, it should fail on the first try
187             NV_ASSERT(gpumgrIsParentGPU(pGpu));
188             SLI_LOOP_BREAK;
189         }
190     }
191     SLI_LOOP_END;
192 
193     if (rmStatus != NV_OK)
194         return rmStatus;
195 
196     if(dynamicCast(pDisplayApi, NvDispApi))
197     {
198         rmStatus = _dispapiNotifierInit(pDisplayApi,
199                                         NVC370_NOTIFIERS_MAXCOUNT,
200                                         NVC370_CTRL_EVENT_SET_NOTIFICATION_ACTION_DISABLE);
201     }
202     else
203     {
204         rmStatus = _dispapiNotifierInit(pDisplayApi,
205                                         NV5070_NOTIFIERS_MAXCOUNT,
206                                         NV5070_CTRL_EVENT_SET_NOTIFICATION_ACTION_DISABLE);
207     }
208 
209     return rmStatus;
210 }
211 
212 NV_STATUS
213 dispobjConstruct_IMPL
214 (
215     DispObject                   *pDispObject,
216     CALL_CONTEXT                 *pCallContext,
217     RS_RES_ALLOC_PARAMS_INTERNAL *pParams
218 )
219 {
220     pDispObject->rmFreeFlags = NV5070_CTRL_SET_RMFREE_FLAGS_NONE;
221 
222     if (pParams->pSecInfo->privLevel < RS_PRIV_LEVEL_USER_ROOT)
223     {
224         NV_PRINTF(LEVEL_ERROR,
225                   "Failure allocating display class 0x%08x: Only root(admin)/kernel clients are allowed\n",
226                   pParams->externalClassId);
227 
228         //
229         // GPUSWSEC-1560 introduced a central object privilege check in RS. Please mark derived external classes
230         // of DispObject privileged in their RS_ENTRY. Since DispObject doesn't have an external class of its own
231         // and is used as a base class, leaving this check inline to catch future derivations.
232         //
233         osAssertFailed();
234 
235         return NV_ERR_INSUFFICIENT_PERMISSIONS;
236     }
237 
238     return dispobjConstructHal_HAL(pDispObject, pCallContext, pParams);
239 }
240 
241 NV_STATUS
242 dispobjGetByHandle_IMPL
243 (
244     RsClient    *pClient,
245     NvHandle     hDispObject,
246     DispObject **ppDispObject
247 )
248 {
249     NV_STATUS      status;
250     RsResourceRef *pResourceRef;
251 
252     status = clientGetResourceRef(pClient, hDispObject, &pResourceRef);
253     if (status != NV_OK)
254         return status;
255 
256     *ppDispObject = dynamicCast(pResourceRef->pResource, DispObject);
257 
258     return (*ppDispObject) ? NV_OK : NV_ERR_INVALID_OBJECT_HANDLE;
259 }
260 
261 NV_STATUS
262 dispobjGetByDevice_IMPL
263 (
264     RsClient    *pClient,
265     Device      *pDevice,
266     DispObject **ppDispObject
267 )
268 {
269     NV_STATUS      status;
270     RsResourceRef *pResourceRef;
271 
272     status = refFindChildOfType(RES_GET_REF(pDevice), classId(DispObject), NV_FALSE /*bExactMatch*/, &pResourceRef);
273     if (status != NV_OK)
274         return status;
275 
276     *ppDispObject = dynamicCast(pResourceRef->pResource, DispObject);
277 
278     return (*ppDispObject) ? NV_OK : NV_ERR_INVALID_OBJECT_HANDLE;
279 }
280 
281 //
282 // Most display control calls take a subDeviceInstance argument.
283 // We need to verify that this argument is valid and then use it to
284 // locate the correct OBJGPU for the particular subdevice.
285 //
286 NV_STATUS
287 dispapiSetUnicastAndSynchronize_KERNEL
288 (
289     DisplayApi      *pDisplayApi,
290     OBJGPUGRP       *pGpuGroup,
291     OBJGPU         **ppGpu,
292     NvU32            subDeviceInstance
293 )
294 {
295     NV_STATUS   nvStatus    = NV_OK;
296 
297     nvStatus = gpugrpGetGpuFromSubDeviceInstance(pGpuGroup, subDeviceInstance, ppGpu);
298     if (nvStatus != NV_OK)
299         return nvStatus;
300 
301     gpumgrSetBcEnabledStatus(*ppGpu, NV_FALSE);
302 
303     return nvStatus;
304 }
305 
306 NV_STATUS
307 dispapiControl_Prologue_IMPL
308 (
309     DisplayApi                     *pDisplayApi,
310     CALL_CONTEXT                   *pCallContext,
311     RS_RES_CONTROL_PARAMS_INTERNAL *pRmCtrlParams
312 )
313 {
314     NvU32       subdeviceIndex;
315     NV_STATUS   status;
316     RmResource *pResource = staticCast(pDisplayApi, RmResource);
317 
318     if (dynamicCast(pDisplayApi, DispCommon))
319     {
320         Device      *pDevice = dynamicCast(pCallContext->pResourceRef->pParentRef->pResource, Device);
321         GpuResource *pGpuResource = staticCast(pDevice, GpuResource);
322 
323         pResource->rpcGpuInstance = gpuGetInstance(pGpuResource->pGpu);
324         pDisplayApi->pGpuInRmctrl = pGpuResource->pGpu;
325         return rmresControl_Prologue_IMPL(pResource, pCallContext, pRmCtrlParams);
326     }
327 
328     // Read the subdevice ID out and swap GPU pointer
329     if (dynamicCast(pDisplayApi, NvDispApi))
330     {
331         NVC370_CTRL_CMD_BASE_PARAMS *pBaseParameters = pRmCtrlParams->pParams;
332 
333         //
334         // All non-NULL disp control 5070 methods have
335         // NVC370_CTRL_CMD_BASE_PARAMS as their first member.
336         //
337         if ((pBaseParameters == NULL) || (pRmCtrlParams->paramsSize < sizeof(NVC370_CTRL_CMD_BASE_PARAMS)))
338         {
339             status = NV_ERR_INVALID_PARAM_STRUCT;
340             goto done;
341         }
342         subdeviceIndex = pBaseParameters->subdeviceIndex;
343     }
344     else if (dynamicCast(pDisplayApi, DispSwObj))
345     {
346         NVC372_CTRL_CMD_BASE_PARAMS *pBaseParameters = pRmCtrlParams->pParams;
347 
348         //
349         // All non-NULL disp control C372 methods have
350         // NVC372_CTRL_CMD_BASE_PARAMS as their first member.
351         //
352         if ((pBaseParameters == NULL) || (pRmCtrlParams->paramsSize < sizeof(NVC372_CTRL_CMD_BASE_PARAMS)))
353         {
354             status = NV_ERR_INVALID_PARAM_STRUCT;
355             goto done;
356         }
357         subdeviceIndex = pBaseParameters->subdeviceIndex;
358     }
359     else
360     {
361         NV5070_CTRL_CMD_BASE_PARAMS *pBaseParameters = pRmCtrlParams->pParams;
362 
363         //
364         // All non-NULL disp control 5070 methods have
365         // NV5070_CTRL_CMD_BASE_PARAMS as their first member.
366         //
367         if ((pBaseParameters == NULL) || (pRmCtrlParams->paramsSize < sizeof(NV5070_CTRL_CMD_BASE_PARAMS)))
368         {
369             status = NV_ERR_INVALID_PARAM_STRUCT;
370             goto done;
371         }
372         subdeviceIndex = pBaseParameters->subdeviceIndex;
373     }
374 
375     status = dispapiSetUnicastAndSynchronize_HAL(pDisplayApi,
376                                              pRmCtrlParams->pGpuGrp,
377                                              &pRmCtrlParams->pGpu,
378                                              subdeviceIndex);
379 
380     if (status == NV_OK)
381     {
382         pResource->rpcGpuInstance = gpuGetInstance(pRmCtrlParams->pGpu);
383         pDisplayApi->pGpuInRmctrl = pRmCtrlParams->pGpu;
384         return rmresControl_Prologue_IMPL(pResource, pCallContext, pRmCtrlParams);
385     }
386 
387 done:
388     return status;
389 }
390 
391 void
392 dispapiControl_Epilogue_IMPL
393 (
394     DisplayApi                     *pDisplayApi,
395     CALL_CONTEXT                   *pCallContext,
396     RS_RES_CONTROL_PARAMS_INTERNAL *pRmCtrlParams
397 )
398 {
399     if (dynamicCast(pDisplayApi, DispCommon) == NULL)
400     {
401         RmResource *pResource = staticCast(pDisplayApi, RmResource);
402         pResource->rpcGpuInstance = ~0;
403     }
404 
405     pDisplayApi->pGpuInRmctrl = NULL;
406 }
407 
408 NV_STATUS
409 dispapiControl_IMPL
410 (
411     DisplayApi                     *pDisplayApi,
412     CALL_CONTEXT                   *pCallContext,
413     RS_RES_CONTROL_PARAMS_INTERNAL *pParams
414 )
415 {
416     Intr             *pIntr;
417     NV_STATUS         status        = NV_OK;
418     Device           *pDevice       = dynamicCast(pCallContext->pResourceRef->pParentRef->pResource, Device);
419     GpuResource      *pGpuResource  = staticCast(pDevice, GpuResource);
420     RmCtrlParams     *pRmCtrlParams = pParams->pLegacyParams;
421     OBJGPU           *pGpu          = pGpuResource->pGpu;
422 
423     NV_PRINTF(LEVEL_INFO, "class: 0x%x cmd 0x%x\n",
424               RES_GET_EXT_CLASS_ID(pDisplayApi),
425               pRmCtrlParams->cmd);
426 
427     pRmCtrlParams->pGpu    = pGpu;
428     pRmCtrlParams->pGpuGrp = pGpuResource->pGpuGrp;
429 
430     gpuSetThreadBcState(pGpu, NV_TRUE);
431 
432     pIntr = GPU_GET_INTR(pGpu);
433     if (pIntr != NULL)
434     {
435         bitVectorClrAll(&pIntr->helperEngineMask);
436         bitVectorSet(&pIntr->helperEngineMask, MC_ENGINE_IDX_GR);
437         bitVectorSet(&pIntr->helperEngineMask, MC_ENGINE_IDX_DISP);
438         bitVectorSet(&pIntr->helperEngineMask, MC_ENGINE_IDX_FIFO);
439     }
440 
441     status = resControl_IMPL(staticCast(pDisplayApi, RsResource),
442                                 pCallContext, pParams);
443 
444     if (pIntr != NULL)
445     {
446         bitVectorClrAll(&pIntr->helperEngineMask);
447     }
448 
449     return status;
450 }
451 
452 NV_STATUS
453 dispswobjConstruct_IMPL
454 (
455     DispSwObj                    *pDispSwObj,
456     CALL_CONTEXT                 *pCallContext,
457     RS_RES_ALLOC_PARAMS_INTERNAL *pParams
458 )
459 {
460     return NV_OK;
461 }
462 
463 NV_STATUS
464 dispcmnConstruct_IMPL
465 (
466     DispCommon                   *pDispCommon,
467     CALL_CONTEXT                 *pCallContext,
468     RS_RES_ALLOC_PARAMS_INTERNAL *pParams
469 )
470 {
471     DisplayApi *pDisplayApi = staticCast(pDispCommon, DisplayApi);
472 
473     //
474     // Not adding the priv-level check for this class
475     // as it is being used by OpenGL from userspace.Once the Cleanup is done from the OpenGL
476     // we can add the priv level check here  below
477     //
478 
479     pDispCommon->hotPlugMaskToBeReported = 0;
480     pDispCommon->hotUnplugMaskToBeReported = 0;
481 
482     return _dispapiNotifierInit(pDisplayApi,
483                                 NV0073_NOTIFIERS_MAXCOUNT,
484                                 NV5070_CTRL_EVENT_SET_NOTIFICATION_ACTION_DISABLE);
485 }
486 
487 NV_STATUS
488 dispcmnGetByHandle_IMPL
489 (
490     RsClient    *pClient,
491     NvHandle     hDispCommon,
492     DispCommon **ppDispCommon
493 )
494 {
495     NV_STATUS      status;
496     RsResourceRef *pResourceRef;
497 
498     status = clientGetResourceRef(pClient, hDispCommon, &pResourceRef);
499     if (status != NV_OK)
500         return status;
501 
502     *ppDispCommon = dynamicCast(pResourceRef->pResource, DispCommon);
503 
504     return (*ppDispCommon) ? NV_OK : NV_ERR_INVALID_OBJECT_HANDLE;
505 }
506 
507 void
508 dispcmnGetByDevice_IMPL
509 (
510     RsClient    *pClient,
511     NvHandle     hDevice,
512     DispCommon **ppDispCommon
513 )
514 {
515     Device        *pDevice;
516     RsResourceRef *pResourceRef;
517 
518     *ppDispCommon = NULL; /* return failure by default */
519 
520     if (deviceGetByHandle(pClient, hDevice, &pDevice) != NV_OK)
521         return;
522 
523     if (refFindChildOfType(RES_GET_REF(pDevice),
524                            classId(DispCommon),
525                            NV_FALSE,
526                            &pResourceRef) != NV_OK)
527         return;
528 
529     *ppDispCommon = dynamicCast(pResourceRef->pResource, DispCommon);
530 }
531 
532 /**
533  * @brief Return NV_TRUE if RmFree() needs to preserve the HW, otherwise NV_FALSE
534  *
535  * @param[in] DispObject Pointer
536  */
537 NvBool dispobjGetRmFreeFlags_IMPL(DispObject *pDispObject)
538 {
539     return !!(pDispObject->rmFreeFlags & NV5070_CTRL_SET_RMFREE_FLAGS_PRESERVE_HW);
540 }
541 
542 /**
543  * @brief Clears the RmFree() temporary flags
544  *
545  * @param[in] DispObject Pointer
546  *
547  * @return void
548  */
549 void dispobjClearRmFreeFlags_IMPL(DispObject *pDispObject)
550 {
551     pDispObject->rmFreeFlags  = NV5070_CTRL_SET_RMFREE_FLAGS_NONE;
552 }
553 
554 NV_STATUS
555 nvdispapiConstruct_IMPL
556 (
557     NvDispApi                    *pNvdispApi,
558     CALL_CONTEXT                 *pCallContext,
559     RS_RES_ALLOC_PARAMS_INTERNAL *pParams
560 )
561 {
562     return NV_OK;
563 }
564 
565 // ****************************************************************************
566 //                            Deprecated Functions
567 // ****************************************************************************
568 
569 /**
570  * @warning This function is deprecated! Please use dispchnGetByHandle.
571  */
572 NV_STATUS
573 CliFindDispChannelInfo
574 (
575     NvHandle       hClient,
576     NvHandle       hDispChannel,
577     DispChannel  **ppDispChannel,
578     NvHandle      *phParent
579 )
580 {
581     RsClient   *pClient;
582     NV_STATUS   status;
583 
584     *ppDispChannel = NULL;
585 
586     status = serverGetClientUnderLock(&g_resServ, hClient, &pClient);
587     if (status != NV_OK)
588         return NV_ERR_INVALID_CLIENT;
589 
590     status = dispchnGetByHandle(pClient, hDispChannel, ppDispChannel);
591     if (status != NV_OK)
592         return status;
593 
594     if (phParent)
595         *phParent = RES_GET_PARENT_HANDLE(*ppDispChannel);
596 
597     return NV_OK;
598 }
599 
600 /**
601  * @warning This function is deprecated! Please use dispcmnGetByHandle.
602  */
603 NvBool
604 CliGetDispCommonInfo
605 (
606     NvHandle     hClient,
607     NvHandle     hDispCommon,
608     DisplayApi **ppDisplayApi
609 )
610 {
611     RsClient   *pClient;
612     NV_STATUS   status;
613     DispCommon *pDispCommon;
614 
615     *ppDisplayApi = NULL;
616 
617     status = serverGetClientUnderLock(&g_resServ, hClient, &pClient);
618     if (status != NV_OK)
619         return NV_FALSE;
620 
621     status = dispcmnGetByHandle(pClient, hDispCommon, &pDispCommon);
622     if (status != NV_OK)
623         return NV_FALSE;
624 
625     *ppDisplayApi = staticCast(pDispCommon, DisplayApi);
626 
627     return NV_TRUE;
628 }
629 
630 /**
631  * @warning This function is deprecated! Please use dispobjGetByHandle.
632  */
633 NvBool
634 CliGetDispInfo
635 (
636     NvHandle     hClient,
637     NvHandle     hObject,
638     DisplayApi **pDisplayApi
639 )
640 {
641     if (!pDisplayApi)
642         return NV_FALSE;
643 
644     *pDisplayApi = CliGetDispFromDispHandle(hClient, hObject);
645 
646     return *pDisplayApi ? NV_TRUE : NV_FALSE;
647 }
648 
649 /**
650  * @warning This function is deprecated! Please use dispobjGetByHandle.
651  */
652 DisplayApi *
653 CliGetDispFromDispHandle
654 (
655     NvHandle hClient,
656     NvHandle hDisp
657 )
658 {
659     RsClient   *pClient;
660     NV_STATUS   status;
661     DispObject *pDispObject;
662 
663     status = serverGetClientUnderLock(&g_resServ, hClient, &pClient);
664     if (status != NV_OK)
665         return NULL;
666 
667     status = dispobjGetByHandle(pClient, hDisp, &pDispObject);
668     if (status != NV_OK)
669         return NULL;
670 
671     return staticCast(pDispObject, DisplayApi);
672 }
673 
674 //
675 // DISP Event RM Controls
676 //
677 NV_STATUS
678 dispapiCtrlCmdEventSetNotification_IMPL
679 (
680     DisplayApi *pDisplayApi,
681     NV5070_CTRL_EVENT_SET_NOTIFICATION_PARAMS *pSetEventParams
682 )
683 {
684     OBJGPU *pGpu = DISPAPI_GET_GPU(pDisplayApi);
685     NvU32 *pNotifyActions;
686     NV_STATUS status = NV_OK;
687     PEVENTNOTIFICATION pEventNotifications = inotifyGetNotificationList(staticCast(pDisplayApi, INotifier));
688 
689     // NV01_EVENT must have been plugged into this subdevice
690     if (pEventNotifications == NULL)
691     {
692         NV_PRINTF(LEVEL_INFO, "cmd 0x%x: no event list\n", NV5070_CTRL_CMD_EVENT_SET_NOTIFICATION);
693         return NV_ERR_INVALID_STATE;
694     }
695 
696     // error check event index
697     if (pSetEventParams->event >= pDisplayApi->numNotifiers)
698     {
699         NV_PRINTF(LEVEL_INFO, "bad event 0x%x\n", pSetEventParams->event);
700         return NV_ERR_INVALID_ARGUMENT;
701     }
702 
703     // error check subDeviceInstance
704     if (pSetEventParams->subDeviceInstance >= gpumgrGetSubDeviceMaxValuePlus1(pGpu))
705     {
706         NV_PRINTF(LEVEL_INFO, "bad subDeviceInstance 0x%x\n",
707                   pSetEventParams->subDeviceInstance);
708         return NV_ERR_INVALID_ARGUMENT;
709     }
710 
711     pNotifyActions = pDisplayApi->pNotifyActions[pSetEventParams->subDeviceInstance];
712 
713     switch (pSetEventParams->action)
714     {
715         case NV5070_CTRL_EVENT_SET_NOTIFICATION_ACTION_SINGLE:
716         case NV5070_CTRL_EVENT_SET_NOTIFICATION_ACTION_REPEAT:
717         {
718             // must be in disabled state to transition to an active state
719             if (pNotifyActions[pSetEventParams->event] != NV5070_CTRL_EVENT_SET_NOTIFICATION_ACTION_DISABLE)
720             {
721                 status = NV_ERR_INVALID_STATE;
722                 break;
723             }
724 
725             // bind hEvent to particular subdeviceInst
726             status = bindEventNotificationToSubdevice(pEventNotifications,
727                                                       pSetEventParams->hEvent,
728                                                       pSetEventParams->subDeviceInstance);
729             if (status != NV_OK)
730                 return status;
731 
732             pNotifyActions[pSetEventParams->event] = pSetEventParams->action;
733             break;
734         }
735 
736         case NV5070_CTRL_EVENT_SET_NOTIFICATION_ACTION_DISABLE:
737         {
738             pNotifyActions[pSetEventParams->event] = pSetEventParams->action;
739             break;
740         }
741         default:
742         {
743             status = NV_ERR_INVALID_ARGUMENT;
744             break;
745         }
746     }
747 
748     return status;
749 }
750