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