1 /*
2  * SPDX-FileCopyrightText: Copyright (c) 2020-2023 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   * @file disp_common_kern_ctrl_minimal.c implements rmctrls which
26   * (a) are declared in disp_common_ctrl_minimal.h; i.e.
27   *    (i) are dispcmnCtrlCmd* functions
28   *    (ii) which are used by Tegra SOC NVDisplay and/or OS layer; and
29   * (b) are implemented in Kernel RM.
30   */
31 
32 #define RM_STRICT_CONFIG_EMIT_DISP_ENGINE_DEFINITIONS     0
33 
34 #include "os/os.h"
35 #include "gpu/gpu.h"
36 #include "gpu/disp/kern_disp.h"
37 #include "gpu/disp/disp_objs.h"
38 #include "rmapi/rs_utils.h"
39 #include "rmapi/rmapi.h"
40 #include "gpu/disp/head/kernel_head.h"
41 
42 NV_STATUS
dispcmnCtrlCmdSystemGetHotplugUnplugState_IMPL(DispCommon * pDispCommon,NV0073_CTRL_SYSTEM_GET_HOTPLUG_UNPLUG_STATE_PARAMS * pHotplugParams)43 dispcmnCtrlCmdSystemGetHotplugUnplugState_IMPL
44 (
45     DispCommon *pDispCommon,
46     NV0073_CTRL_SYSTEM_GET_HOTPLUG_UNPLUG_STATE_PARAMS *pHotplugParams
47 )
48 {
49     NvHandle   hDevice = RES_GET_PARENT_HANDLE(pDispCommon);
50     RM_API    *pRmApi = GPU_GET_PHYSICAL_RMAPI(DISPAPI_GET_GPU(pDispCommon));
51     NvU32      hotPlugMask   = 0;
52     NvU32      hotUnplugMask = 0;
53     NV_STATUS  status;
54 
55     status = pRmApi->Control(pRmApi,
56                              RES_GET_CLIENT_HANDLE(pDispCommon),
57                              RES_GET_HANDLE(pDispCommon),
58                              NV0073_CTRL_CMD_INTERNAL_GET_HOTPLUG_UNPLUG_STATE,
59                              pHotplugParams,
60                              sizeof(*pHotplugParams));
61 
62     hotPlugMask = pHotplugParams->hotPlugMask;
63     hotUnplugMask = pHotplugParams->hotUnplugMask;
64     pHotplugParams->hotPlugMask = 0;
65     pHotplugParams->hotUnplugMask = 0;
66 
67     if (status != NV_OK)
68     {
69         return status;
70     }
71 
72     if ((hotPlugMask != 0) || (hotUnplugMask != 0))
73     {
74         RmClient **ppClient;
75         RsClient  *pRsClient;
76 
77         for (ppClient = serverutilGetFirstClientUnderLock();
78              ppClient;
79              ppClient = serverutilGetNextClientUnderLock(ppClient))
80         {
81             pRsClient = staticCast(*ppClient, RsClient);
82             DispCommon *pDispCommonLoop;
83 
84             dispcmnGetByDevice(pRsClient, hDevice, &pDispCommonLoop);
85             if (pDispCommonLoop == NULL)
86                 continue;
87 
88             pDispCommonLoop->hotPlugMaskToBeReported   |= hotPlugMask   & (~(pDispCommonLoop->hotPlugMaskToBeReported   & hotUnplugMask));
89             pDispCommonLoop->hotUnplugMaskToBeReported |= hotUnplugMask & (~(pDispCommonLoop->hotUnplugMaskToBeReported & hotPlugMask));
90         }
91     }
92 
93     pHotplugParams->hotPlugMask   = pDispCommon->hotPlugMaskToBeReported;
94     pHotplugParams->hotUnplugMask = pDispCommon->hotUnplugMaskToBeReported;
95     pDispCommon->hotPlugMaskToBeReported = 0;
96     pDispCommon->hotUnplugMaskToBeReported = 0;
97 
98     return status;
99 }
100 
101 /*!
102  * @brief Allocate display bandwidth.
103  */
104 NV_STATUS
dispcmnCtrlCmdSystemAllocateDisplayBandwidth_IMPL(DispCommon * pDispCommon,NV0073_CTRL_SYSTEM_ALLOCATE_DISPLAY_BANDWIDTH_PARAMS * pParams)105 dispcmnCtrlCmdSystemAllocateDisplayBandwidth_IMPL
106 (
107     DispCommon *pDispCommon,
108     NV0073_CTRL_SYSTEM_ALLOCATE_DISPLAY_BANDWIDTH_PARAMS *pParams
109 )
110 {
111     OBJGPU        *pGpu;
112     KernelDisplay *pKernelDisplay;
113     NV_STATUS      status;
114 
115     // client gave us a subdevice #: get right pGpu for it
116     status = dispapiSetUnicastAndSynchronize_HAL(
117                                staticCast(pDispCommon, DisplayApi),
118                                DISPAPI_GET_GPUGRP(pDispCommon),
119                                &pGpu,
120                                NULL,
121                                pParams->subDeviceInstance);
122     if (status != NV_OK)
123     {
124         return status;
125     }
126 
127     pKernelDisplay = GPU_GET_KERNEL_DISPLAY(pGpu);
128     return kdispArbAndAllocDisplayBandwidth_HAL(pGpu,
129                                                 pKernelDisplay,
130                                                 DISPLAY_ICC_BW_CLIENT_EXT,
131                                                 pParams->averageBandwidthKBPS,
132                                                 pParams->floorBandwidthKBPS);
133 }
134 
135 NV_STATUS
dispcmnCtrlCmdDpGenerateFakeInterrupt_IMPL(DispCommon * pDispCommon,NV0073_CTRL_CMD_DP_GENERATE_FAKE_INTERRUPT_PARAMS * pParams)136 dispcmnCtrlCmdDpGenerateFakeInterrupt_IMPL
137 (
138     DispCommon *pDispCommon,
139     NV0073_CTRL_CMD_DP_GENERATE_FAKE_INTERRUPT_PARAMS *pParams
140 )
141 {
142     OBJGPU *pGpu = DISPAPI_GET_GPU(pDispCommon);
143     NvU32 displayId = pParams->displayId;
144     NvU32 interruptType = pParams->interruptType;
145     NV_STATUS status = NV_OK;
146 
147     // get target pGpu
148     status = dispapiSetUnicastAndSynchronize_HAL(
149                                staticCast(pDispCommon, DisplayApi),
150                                DISPAPI_GET_GPUGRP(pDispCommon),
151                                &pGpu,
152                                NULL,
153                                pParams->subDeviceInstance);
154     if (status != NV_OK)
155     {
156         return status;
157     }
158 
159     NV_ASSERT_OR_RETURN(pParams->displayId, NV_ERR_INVALID_ARGUMENT);
160     NV_ASSERT_OR_RETURN(pGpu, NV_ERR_INVALID_ARGUMENT);
161 
162     // Send a DP IRQ (short pulse) to a registered client.
163     if (interruptType == NV0073_CTRL_CMD_DP_GENERATE_FAKE_INTERRUPT_IRQ)
164     {
165         Nv2080DpIrqNotification params = {0};
166         params.displayId = displayId;
167 
168         // Check eDP power state; if off, return an error.
169         RM_API *pRmApi = GPU_GET_PHYSICAL_RMAPI(pGpu);
170         NV0073_CTRL_DP_GET_EDP_DATA_PARAMS edpData;
171 
172         portMemSet(&edpData, 0, sizeof(edpData));
173 
174         status = pRmApi->Control(pRmApi,
175                                  RES_GET_CLIENT_HANDLE(pDispCommon),
176                                  RES_GET_HANDLE(pDispCommon),
177                                  NV0073_CTRL_CMD_DP_GET_EDP_DATA,
178                                  &edpData,
179                                  sizeof(edpData));
180 
181         if (status == NV_OK && FLD_TEST_DRF(0073_CTRL_DP, _GET_EDP_DATA, _PANEL_POWER, _OFF, edpData.data))
182         {
183             return NV_ERR_GENERIC;
184         }
185 
186         gpuNotifySubDeviceEvent(pGpu, NV2080_NOTIFIERS_DP_IRQ, &params, sizeof(params), 0, 0);
187     }
188     else if (interruptType == NV0073_CTRL_CMD_DP_GENERATE_FAKE_INTERRUPT_PLUG ||
189              interruptType == NV0073_CTRL_CMD_DP_GENERATE_FAKE_INTERRUPT_UNPLUG)
190     {
191         Nv2080HotplugNotification hotplugNotificationParams;
192         portMemSet(&hotplugNotificationParams, 0, sizeof(hotplugNotificationParams));
193 
194         if (interruptType == NV0073_CTRL_CMD_DP_GENERATE_FAKE_INTERRUPT_PLUG)
195         {
196             hotplugNotificationParams.plugDisplayMask = displayId;
197             hotplugNotificationParams.unplugDisplayMask = 0;
198         }
199         else if (interruptType == NV0073_CTRL_CMD_DP_GENERATE_FAKE_INTERRUPT_UNPLUG)
200         {
201             hotplugNotificationParams.plugDisplayMask = 0;
202             hotplugNotificationParams.unplugDisplayMask = displayId;
203         }
204         gpuNotifySubDeviceEvent(pGpu, NV2080_NOTIFIERS_HOTPLUG,
205             &hotplugNotificationParams, sizeof(hotplugNotificationParams), 0, 0);
206     }
207     else
208     {
209         return NV_ERR_INVALID_ARGUMENT;
210     }
211 
212     return NV_OK;
213 }
214 
dispcmnCtrlCmdVRRSetRgLineActive_IMPL(DispCommon * pDispCommon,NV0073_CTRL_CMD_SYSTEM_VRR_SET_RGLINE_ACTIVE_PARAMS * pParams)215 NV_STATUS dispcmnCtrlCmdVRRSetRgLineActive_IMPL
216 (
217     DispCommon *pDispCommon,
218     NV0073_CTRL_CMD_SYSTEM_VRR_SET_RGLINE_ACTIVE_PARAMS *pParams
219 )
220 {
221     OBJGPU   *pGpu   = DISPAPI_GET_GPU(pDispCommon);
222     NvHandle  hClient = RES_GET_CLIENT_HANDLE(pDispCommon);
223     NvHandle  hParent = RES_GET_PARENT_HANDLE(pDispCommon);
224     RM_API   *pRmApi = GPU_GET_PHYSICAL_RMAPI(DISPAPI_GET_GPU(pDispCommon));
225     NV_STATUS status = NV_OK;
226 
227     // Get the right pGpu from subdevice instance given by client
228     status = dispapiSetUnicastAndSynchronize_HAL(
229                                staticCast(pDispCommon, DisplayApi),
230                                DISPAPI_GET_GPUGRP(pDispCommon),
231                                &pGpu,
232                                NULL,
233                                pParams->subDeviceInstance);
234 
235     if (status != NV_OK)
236     {
237         return status;
238     }
239 
240     if (pParams->bEnable)
241     {
242         status = memdescRegisterToGSP(pGpu, hClient, hParent, pParams->hMemory);
243         if (status != NV_OK)
244         {
245             NV_PRINTF(LEVEL_ERROR, "memdescRegisterToGSP failed %d\n", status);
246             return status;
247         }
248     }
249 
250     status = pRmApi->Control(pRmApi,
251                              hClient,
252                              RES_GET_HANDLE(pDispCommon),
253                              NV0073_CTRL_CMD_INTERNAL_VRR_SET_RGLINE_ACTIVE,
254                              pParams,
255                              sizeof(*pParams));
256 
257     if (!pParams->bEnable)
258     {
259         status = memdescDeregisterFromGSP(pGpu, hClient, hParent, pParams->hMemory);
260         if (status != NV_OK)
261         {
262             NV_PRINTF(LEVEL_ERROR, "memdescDeRegisterFromGSP failed %d\n", status);
263         }
264     }
265 
266     return status;
267 }
268 
_kheadCheckVblankCountCallback(POBJGPU pGpu,void * Object,NvU32 param1,NvV32 BuffNum,NV_STATUS Status)269 static NV_STATUS _kheadCheckVblankCountCallback
270 (
271     POBJGPU   pGpu,
272     void     *Object,
273     NvU32     param1,
274     NvV32     BuffNum,
275     NV_STATUS Status
276 )
277 {
278     KernelDisplay *pKernelDisplay = GPU_GET_KERNEL_DISPLAY(pGpu);
279     KernelHead    *pKernelHead    = KDISP_GET_HEAD(pKernelDisplay, param1);
280 
281     if ((--pKernelHead->Vblank.VblankCountTimeout) == 0)
282     {
283         pKernelHead->Vblank.Callback.CheckVblankCount.Flags &= ~VBLANK_CALLBACK_FLAG_PERSISTENT;
284     }
285 
286     return NV_OK;
287 }
288 
289 NV_STATUS
dispcmnCtrlCmdSystemGetVblankCounter_IMPL(DispCommon * pDispCommon,NV0073_CTRL_SYSTEM_GET_VBLANK_COUNTER_PARAMS * pVBCounterParams)290 dispcmnCtrlCmdSystemGetVblankCounter_IMPL
291 (
292     DispCommon *pDispCommon,
293     NV0073_CTRL_SYSTEM_GET_VBLANK_COUNTER_PARAMS *pVBCounterParams
294 )
295 {
296     OBJGPU        *pGpu = DISPAPI_GET_GPU(pDispCommon);
297     KernelDisplay *pKernelDisplay = GPU_GET_KERNEL_DISPLAY(pGpu);
298     KernelHead    *pKernelHead;
299     NvU32 flags =  (VBLANK_CALLBACK_FLAG_SPECIFIED_VBLANK_NEXT | VBLANK_CALLBACK_FLAG_PERSISTENT);
300 
301     pKernelHead = KDISP_GET_HEAD(pKernelDisplay, pVBCounterParams->head);
302     if (pKernelHead == NULL)
303     {
304         NV_PRINTF(LEVEL_ERROR, "invalid head number!\n");
305         return NV_ERR_INVALID_ARGUMENT;
306     }
307 
308     // TODO: make the behaviour same for monolithic and offload RM case
309     if (IS_GSP_CLIENT(pGpu))
310     {
311         if (pKernelDisplay->pSharedData == NULL)
312         {
313             NV_PRINTF(LEVEL_ERROR, "no memory allocated for vblank count\n");
314             return NV_ERR_NOT_SUPPORTED;
315         }
316         flags |= VBLANK_CALLBACK_FLAG_LOW_LATENCY;
317     }
318 
319     pKernelHead->Vblank.VblankCountTimeout = 60 * VBLANK_INFO_GATHER_KEEPALIVE_SECONDS;
320 
321     pKernelHead->Vblank.Callback.CheckVblankCount.Proc   = _kheadCheckVblankCountCallback;
322     pKernelHead->Vblank.Callback.CheckVblankCount.pObject = NULL;
323     pKernelHead->Vblank.Callback.CheckVblankCount.bObjectIsChannelDescendant = NV_FALSE;
324     pKernelHead->Vblank.Callback.CheckVblankCount.Param1 = pKernelHead->PublicId;
325     pKernelHead->Vblank.Callback.CheckVblankCount.Param2 = 0;
326     pKernelHead->Vblank.Callback.CheckVblankCount.Status = NV_OK;
327     pKernelHead->Vblank.Callback.CheckVblankCount.bIsVblankNotifyEnable = NV_TRUE;
328     pKernelHead->Vblank.Callback.CheckVblankCount.Flags  = flags;
329 
330     kheadAddVblankCallback(pGpu, pKernelHead, &pKernelHead->Vblank.Callback.CheckVblankCount);
331 
332     if (IS_GSP_CLIENT(pGpu))
333     {
334         pVBCounterParams->verticalBlankCounter = pKernelDisplay->pSharedData->kHeadVblankCount[pKernelHead->PublicId];
335     }
336     else
337     {
338         pVBCounterParams->verticalBlankCounter = kheadGetVblankNormLatencyCounter_HAL(pKernelHead);
339     }
340 
341     return NV_OK;
342 }
343 
dispcmnCtrlCmdVblankSemControl_IMPL(DispCommon * pDispCommon,NV0073_CTRL_CMD_SYSTEM_VBLANK_SEM_CONTROL_PARAMS * pParams)344 NV_STATUS dispcmnCtrlCmdVblankSemControl_IMPL(
345     DispCommon *pDispCommon,
346     NV0073_CTRL_CMD_SYSTEM_VBLANK_SEM_CONTROL_PARAMS *pParams
347 )
348 {
349     OBJGPU   *pGpu   = DISPAPI_GET_GPU(pDispCommon);
350     NvHandle  hClient = RES_GET_CLIENT_HANDLE(pDispCommon);
351     NvHandle  hParent = RES_GET_PARENT_HANDLE(pDispCommon);
352     RM_API   *pRmApi = GPU_GET_PHYSICAL_RMAPI(DISPAPI_GET_GPU(pDispCommon));
353     NV_STATUS status = NV_OK;
354 
355     // Get the right pGpu from subdevice instance given by client
356     status = dispapiSetUnicastAndSynchronize_HAL(
357                                staticCast(pDispCommon, DisplayApi),
358                                DISPAPI_GET_GPUGRP(pDispCommon),
359                                &pGpu,
360                                NULL,
361                                pParams->subDeviceInstance);
362 
363     if (status != NV_OK)
364     {
365         return status;
366     }
367 
368     if (pParams->bEnable)
369     {
370         // Note: memdescRegisterToGSP() is a noop when either (a) we're not
371         // operating as a GSP client, or (b) the hMemory is already registered
372         // with GSP.
373         //
374         // Also, note that we don't explicitly unregister here in the
375         // !pParams->bEnable case: that could unregister the memory out from
376         // under other uses of this hMemory on GSP (e.g., other vblank semaphore
377         // controls).  Instead, we rely on the hMemory getting unregistered when
378         // the memdesc is freed.
379         status = memdescRegisterToGSP(pGpu, hClient, hParent, pParams->hMemory);
380         if (status != NV_OK)
381         {
382             NV_PRINTF(LEVEL_ERROR, "memdescRegisterToGSP failed %d\n", status);
383             return status;
384         }
385     }
386 
387     // NV0073_CTRL_CMD_SYSTEM_VBLANK_SEM_CONTROL_PARAMS and
388     // NV0073_CTRL_CMD_INTERNAL_VBLANK_SEM_CONTROL_PARAMS are
389     // equivalent, so just pass pParams through.
390     return pRmApi->Control(pRmApi,
391                            hClient,
392                            RES_GET_HANDLE(pDispCommon),
393                            NV0073_CTRL_CMD_INTERNAL_VBLANK_SEM_CONTROL,
394                            pParams,
395                            sizeof(*pParams));
396 }
397