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 #include "objtmr.h"
25 #include "gpu/external_device/gsync.h"
26
27 #include "gpu/external_device/external_device.h"
28 #include "gpu_mgr/gpu_mgr.h"
29 #include "kernel/gpu/i2c/i2c_api.h"
30 #include "gpu/disp/kern_disp_max.h"
31
32 static
extdevGetExtDev(OBJGPU * pGpu)33 PDACEXTERNALDEVICE extdevGetExtDev
34 (
35 OBJGPU *pGpu
36 )
37 {
38 if (RMCFG_FEATURE_EXTDEV_GSYNC)
39 {
40 OBJGSYNC *pGsync = gsyncmgrGetGsync(pGpu);
41 if (pGsync)
42 {
43 return pGsync->pExtDev;
44 }
45 }
46
47 return NULL;
48 }
49
50 //
51 // RMCONFIG: when the EXTDEV feature is disabled there are #defines in
52 // extdev.h that stub this routine out.
53 //
extdevDestroy_Base(OBJGPU * pGpu,PDACEXTERNALDEVICE pExternalDevice)54 void extdevDestroy_Base(OBJGPU *pGpu, PDACEXTERNALDEVICE pExternalDevice)
55 {
56 portMemFree(pExternalDevice->pI);
57 }
58
dacAttachExternalDevice_Base(OBJGPU * pGpu,PDACEXTERNALDEVICE * ppExtdevs)59 static NvBool dacAttachExternalDevice_Base(OBJGPU *pGpu, PDACEXTERNALDEVICE *ppExtdevs)
60 {
61 return NV_FALSE;
62 }
63
64 PDACEXTERNALDEVICE
extdevConstruct_Base(OBJGPU * pGpu,PDACEXTERNALDEVICE pThis)65 extdevConstruct_Base
66 (
67 OBJGPU *pGpu,
68 PDACEXTERNALDEVICE pThis
69 )
70 {
71 //
72 // Alloc or find interface.
73 // Not being able to do so is fatal.
74 //
75 {
76 pThis->pI = portMemAllocNonPaged(sizeof(DACEXTERNALDEVICEIFACE));
77 if (pThis->pI == NULL)
78 {
79 return 0;
80 }
81 portMemSet(pThis->pI, 0, sizeof(DACEXTERNALDEVICEIFACE));
82 }
83
84 // No parent constructor to call, we're the base class!
85
86 // Setup interface(s)
87 {
88 pThis->pI->Destroy = extdevDestroy_Base;
89 pThis->pI->Attach = dacAttachExternalDevice_Base;
90
91 pThis->pI->GetDevice = 0;
92 pThis->pI->Init = 0;
93
94 pThis->pI->Validate = extdevValidate_Default;
95 }
96
97 // Init data members
98 {
99 pThis->MaxGpus = 1; // default, only connect to 1 gpu
100
101 pThis->WatchdogControl.Scheduled = NV_FALSE;
102 pThis->WatchdogControl.TimeOut = 1000000000; // 1 second in ns
103 }
104
105 return pThis;
106 }
107
108 void
extdevDestroy(OBJGPU * pGpu)109 extdevDestroy(OBJGPU *pGpu)
110 {
111 OBJGSYNC *pGsync = NULL;
112
113 if (RMCFG_FEATURE_EXTDEV_GSYNC)
114 {
115 // destroy gsync object if any
116 pGsync = gsyncmgrGetGsync(pGpu);
117 if (pGsync && pGsync->pExtDev)
118 {
119 pGsync->pExtDev->pI->Destroy(pGpu, pGsync->pExtDev);
120 if (pGsync->pExtDev->ReferenceCount == 0)
121 {
122 portMemFree(pGsync->pExtDev);
123 pGsync->pExtDev = NULL;
124 }
125 }
126 }
127 }
128
129 // Schedule Watchdog
extdevScheduleWatchdog(OBJGPU * pGpu,PDACEXTERNALDEVICE pExtDevice)130 NV_STATUS extdevScheduleWatchdog
131 (
132 OBJGPU *pGpu,
133 PDACEXTERNALDEVICE pExtDevice
134 )
135 {
136 OBJTMR *pTmr = GPU_GET_TIMER(pGpu);
137 NV_STATUS rmStatus = NV_ERR_GENERIC;
138
139 // make sure it isn't already scheduled
140 if (!pExtDevice->WatchdogControl.Scheduled)
141 {
142 pExtDevice->WatchdogControl.Scheduled = NV_TRUE;
143
144 rmStatus = tmrScheduleCallbackRel(
145 pTmr,
146 extdevServiceWatchdog,
147 pExtDevice,
148 pExtDevice->WatchdogControl.TimeOut,
149 TMR_FLAG_RECUR,
150 0);
151
152 if (NV_OK != rmStatus)
153 {
154 pExtDevice->WatchdogControl.Scheduled = NV_FALSE;
155 }
156 }
157
158 return rmStatus;
159 }
160
161 // deSchedule Watchdog
extdevCancelWatchdog(OBJGPU * pGpu,PDACEXTERNALDEVICE pExtDevice)162 NV_STATUS extdevCancelWatchdog
163 (
164 OBJGPU *pGpu,
165 PDACEXTERNALDEVICE pExtDevice
166 )
167 {
168 OBJTMR *pTmr = GPU_GET_TIMER(pGpu);
169 NV_STATUS rmStatus;
170
171 // cancel first...
172 rmStatus = tmrCancelCallback(pTmr, pExtDevice);
173
174 // ... then lower the flag!
175 pExtDevice->WatchdogControl.Scheduled = NV_FALSE;
176
177 return rmStatus;
178 }
179
180 // Service the Watchdog
extdevServiceWatchdog(OBJGPU * pGpu,OBJTMR * pTmr,void * _pComponent)181 NV_STATUS extdevServiceWatchdog
182 (
183 OBJGPU *pGpu,
184 OBJTMR *pTmr,
185 void *_pComponent
186 )
187 {
188 PDACEXTERNALDEVICE pExtDevice = NULL;
189 PDACEXTERNALDEVICEIFACE pdsif = NULL;
190
191 pExtDevice = extdevGetExtDev(pGpu);
192
193 // No gsync and no gvo, return NV_ERR_GENERIC
194 if (!pExtDevice)
195 {
196 return NV_ERR_GENERIC;
197 }
198
199 pdsif = pExtDevice->pI;
200
201 // lower the flag, since it's no longer waiting to run
202 pExtDevice->WatchdogControl.Scheduled = NV_FALSE;
203
204 return pdsif->Watchdog(pGpu, pTmr, pExtDevice);
205 }
206
207 // Trivial validation
extdevValidate_Default(OBJGPU * pGpu,PDACEXTERNALDEVICE pExtDevice)208 NvBool extdevValidate_Default
209 (
210 OBJGPU *pGpu,
211 PDACEXTERNALDEVICE pExtDevice
212 )
213 {
214 if (!pExtDevice)
215 {
216 return NV_FALSE;
217 }
218
219 return NV_TRUE;
220 }
221
222 NV_STATUS
writeregu008_extdevice(OBJGPU * pGpu,PDACEXTERNALDEVICE pThis,NvU8 SubAdr,NvU8 Data)223 writeregu008_extdevice
224 (
225 OBJGPU *pGpu,
226 PDACEXTERNALDEVICE pThis,
227 NvU8 SubAdr,
228 NvU8 Data
229 )
230 {
231
232 NV_STATUS status = NV_ERR_GENERIC;
233
234 NvU32 i2cPort = (pGpu->i2cPortForExtdev < NV402C_CTRL_NUM_I2C_PORTS) ? pGpu->i2cPortForExtdev : pThis->I2CPort;
235 status = i2c_extdeviceHelper(pGpu, pThis, i2cPort, SubAdr, &Data, NV_TRUE);
236
237 return status;
238 }
239
240 //
241 // This is a wrapper function for writeregu008_extdevice to be used when a
242 // specific GPU is to be targeted (rather than an SLI abstraction). For
243 // example, a P294 framelock board has two connectors to gpus, one for a
244 // primary gpu and one for a secondary gpu. It is only valid to read and
245 // write registers over i2c connected to the primary gpu. If the MC_PARENT
246 // is not the same gpu as the primary, the ThisGpuFromHal macro at the
247 // beginning of writeregu008_extdevice will return the secondary gpu unless
248 // the primary gpu is specifically loaded beforehand.
249 // This wrapper does that loading.
250 //
251 NV_STATUS
writeregu008_extdeviceTargeted(OBJGPU * pGpu,PDACEXTERNALDEVICE pThis,NvU8 SubAdr,NvU8 Data)252 writeregu008_extdeviceTargeted
253 (
254 OBJGPU *pGpu,
255 PDACEXTERNALDEVICE pThis,
256 NvU8 SubAdr,
257 NvU8 Data
258 )
259 {
260 NV_STATUS status = NV_ERR_GENERIC;
261 NvBool bcState;
262
263 if (!pGpu)
264 {
265 return status;
266 }
267
268 bcState = gpumgrGetBcEnabledStatus(pGpu);
269
270 gpumgrSetBcEnabledStatus(pGpu, NV_FALSE);
271 status = writeregu008_extdevice(pGpu, pThis, SubAdr, Data);
272 gpumgrSetBcEnabledStatus(pGpu, bcState);
273
274 return status;
275 }
276
277 NV_STATUS
readregu008_extdevice(OBJGPU * pGpu,PDACEXTERNALDEVICE pThis,NvU8 SubAdr,NvU8 * pData)278 readregu008_extdevice
279 (
280 OBJGPU *pGpu,
281 PDACEXTERNALDEVICE pThis,
282 NvU8 SubAdr,
283 NvU8 *pData
284 )
285 {
286
287 NV_STATUS status = NV_ERR_GENERIC;
288
289 NvU32 i2cPort = (pGpu->i2cPortForExtdev < NV402C_CTRL_NUM_I2C_PORTS) ? pGpu->i2cPortForExtdev : pThis->I2CPort;
290 status = i2c_extdeviceHelper(pGpu, pThis, i2cPort, SubAdr, pData, NV_FALSE);
291
292 return status;
293 }
294
295 //
296 // This is a wrapper function for readregu008_extdevice to be used when a
297 // specific GPU is to be targeted (rather than an SLI abstraction). For
298 // example, a P294 framelock board has two connectors to gpus, one for a
299 // primary gpu and one for a secondary gpu. It is only valid to read and
300 // write registers over i2c connected to the primary gpu. If the MC_PARENT
301 // is not the same gpu as the primary, the ThisGpuFromHal macro at the
302 // beginning of readregu008_extdevice will return the secondary gpu unless
303 // the primary gpu is specifically loaded beforehand.
304 // This wrapper does that loading.
305 //
306 NV_STATUS
readregu008_extdeviceTargeted(OBJGPU * pGpu,PDACEXTERNALDEVICE pThis,NvU8 SubAdr,NvU8 * pData)307 readregu008_extdeviceTargeted
308 (
309 OBJGPU *pGpu,
310 PDACEXTERNALDEVICE pThis,
311 NvU8 SubAdr,
312 NvU8 *pData
313 )
314 {
315 NV_STATUS status = NV_ERR_GENERIC;
316 NvBool bcState;
317
318 if (!pGpu)
319 {
320 return status;
321 }
322
323 bcState = gpumgrGetBcEnabledStatus(pGpu);
324
325 gpumgrSetBcEnabledStatus(pGpu, NV_FALSE);
326 status = readregu008_extdevice(pGpu, pThis, SubAdr, pData);
327 gpumgrSetBcEnabledStatus(pGpu, bcState);
328
329 return status;
330 }
331
332 void
extdevGsyncService(OBJGPU * pGpu,NvU8 lossRegStatus,NvU8 gainRegStatus,NvU8 miscRegStatus,NvBool rmStatus)333 extdevGsyncService
334 (
335 OBJGPU *pGpu,
336 NvU8 lossRegStatus,
337 NvU8 gainRegStatus,
338 NvU8 miscRegStatus,
339 NvBool rmStatus
340 )
341 {
342 PDACEXTERNALDEVICE pExtDevice = NULL;
343 PDACEXTERNALDEVICEIFACE pdsif = NULL;
344
345 pExtDevice = extdevGetExtDev(pGpu);
346 if (!pExtDevice)
347 return;
348
349 pdsif = pExtDevice->pI;
350
351 pdsif->Service(pGpu, pExtDevice, lossRegStatus, gainRegStatus, miscRegStatus, rmStatus);
352 }
353
354 void
extdevGetBoundHeadsAndDisplayIds(OBJGPU * pGpu,NvU32 * pDisplayId)355 extdevGetBoundHeadsAndDisplayIds
356 (
357 OBJGPU *pGpu,
358 NvU32 *pDisplayId
359 )
360 {
361 RM_API *pRmApi = GPU_GET_PHYSICAL_RMAPI(pGpu);
362 NvU32 hClient = pGpu->hInternalClient;
363 NvU32 hSubdevice = pGpu->hInternalSubdevice;
364 NV_STATUS status = NV_OK;
365 NV2080_CTRL_INTERNAL_GSYNC_GET_DISPLAY_IDS_PARAMS ctrlParams = {0};
366
367 portMemSet(pDisplayId, 0, OBJ_MAX_HEADS * sizeof(NvU32));
368
369 status = pRmApi->Control(pRmApi, hClient, hSubdevice,
370 NV2080_CTRL_CMD_INTERNAL_GSYNC_GET_DISPLAY_IDS,
371 &ctrlParams, sizeof(ctrlParams));
372
373 if (status != NV_OK)
374 {
375 NV_PRINTF(LEVEL_ERROR, "Extdev getting display IDs have failed!\n");
376 }
377 else
378 {
379 portMemCopy(pDisplayId, OBJ_MAX_HEADS * sizeof(NvU32), ctrlParams.displayIds,
380 OBJ_MAX_HEADS * sizeof(NvU32));
381 }
382 }
383
384