1 /*
2 * SPDX-FileCopyrightText: Copyright (c) 2016-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 "dev_p2060.h"
25 #include "dev_p2061.h"
26 #include "gpu/external_device/dac_p2060.h"
27 #include "gpu/external_device/dac_p2061.h"
28
29 /*
30 * Get the house sync mode (input/output).
31 */
32 NV_STATUS
gsyncGetHouseSyncMode_P2061(OBJGPU * pGpu,PDACEXTERNALDEVICE pExtDev,NvU8 * houseSyncMode)33 gsyncGetHouseSyncMode_P2061
34 (
35 OBJGPU *pGpu,
36 PDACEXTERNALDEVICE pExtDev,
37 NvU8* houseSyncMode
38 )
39 {
40 NvU8 regCtrl4;
41 NV_STATUS status;
42
43 status = readregu008_extdeviceTargeted(pGpu, pExtDev,
44 (NvU8)NV_P2061_CONTROL4, ®Ctrl4);
45 *houseSyncMode = DRF_VAL(_P2061, _CONTROL4, _HOUSE_SYNC_MODE, regCtrl4);
46
47 return status;
48 }
49
50 /*
51 * Set the house sync mode (input/output).
52 */
53 NV_STATUS
gsyncSetHouseSyncMode_P2061(OBJGPU * pGpu,PDACEXTERNALDEVICE pExtDev,NvU8 houseSyncMode)54 gsyncSetHouseSyncMode_P2061
55 (
56 OBJGPU *pGpu,
57 PDACEXTERNALDEVICE pExtDev,
58 NvU8 houseSyncMode
59 )
60 {
61 NvU8 regStatus2;
62 NvU8 regCtrl4;
63 NV_STATUS status;
64
65 if (houseSyncMode != NV_P2061_CONTROL4_HOUSE_SYNC_MODE_INPUT &&
66 houseSyncMode != NV_P2061_CONTROL4_HOUSE_SYNC_MODE_OUTPUT)
67 {
68 return NV_ERR_INVALID_ARGUMENT;
69 }
70
71 status = readregu008_extdeviceTargeted(pGpu, pExtDev,
72 (NvU8)NV_P2060_STATUS2, ®Status2);
73
74 status = readregu008_extdeviceTargeted(pGpu, pExtDev,
75 (NvU8)NV_P2061_CONTROL4, ®Ctrl4);
76
77 //
78 // Bailing out when the following 3 conditions are satisfied:
79 // 1. house sync mode is currently set as input, and
80 // 2. there is a BNC signal detected, and
81 // 3. client wants to switch house sync mode to output.
82 //
83 // This helps avoid the case that user is trying to toggle the mode to output
84 // when p2061 is inputting from a house sync device.
85 //
86 if (FLD_TEST_DRF(_P2061, _CONTROL4, _HOUSE_SYNC_MODE, _INPUT, regCtrl4) &&
87 !FLD_TEST_DRF(_P2060, _STATUS2, _HS_DETECT, _NONE, regStatus2) &&
88 houseSyncMode == NV_P2061_CONTROL4_HOUSE_SYNC_MODE_OUTPUT)
89 {
90 return NV_ERR_INVALID_STATE;
91 }
92
93 regCtrl4 = FLD_SET_DRF_NUM(_P2061, _CONTROL4, _HOUSE_SYNC_MODE, houseSyncMode, regCtrl4);
94 status |= writeregu008_extdeviceTargeted(pGpu, pExtDev,
95 (NvU8)NV_P2061_CONTROL4, regCtrl4);
96
97 return status;
98 }
99
100 /*
101 * Handle Get and Set queries related to CPL status.
102 */
103 NV_STATUS
gsyncGetCplStatus_P2061(OBJGPU * pGpu,PDACEXTERNALDEVICE pExtDev,GSYNCSTATUS CplStatus,NvU32 * pVal)104 gsyncGetCplStatus_P2061
105 (
106 OBJGPU *pGpu,
107 PDACEXTERNALDEVICE pExtDev,
108 GSYNCSTATUS CplStatus,
109 NvU32 *pVal
110 )
111 {
112 NV_STATUS status = NV_OK;
113 NvU8 regStatus2;
114 NvU8 regStatus6;
115
116 switch (CplStatus)
117 {
118 // p2061-specific cases
119 case gsync_Status_bInternalSlave:
120 status = readregu008_extdeviceTargeted(pGpu, pExtDev,
121 (NvU8)NV_P2060_STATUS2, ®Status2);
122 if (status != NV_OK)
123 {
124 break;
125 }
126 status = readregu008_extdeviceTargeted(pGpu, pExtDev,
127 (NvU8)NV_P2061_STATUS6, ®Status6);
128 if (status == NV_OK)
129 {
130 *pVal = FLD_TEST_DRF(_P2060, _STATUS2, _PORT0, _OUTPUT, (NvU32)regStatus2) &&
131 FLD_TEST_DRF(_P2060, _STATUS2, _PORT1, _OUTPUT, (NvU32)regStatus2) &&
132 FLD_TEST_DRF(_P2061, _STATUS6, _INT_PORT_DIRECTION, _INPUT, (NvU32)regStatus6);
133 }
134 break;
135 // common cases shared by both p2060 and p2061
136 default:
137 status = gsyncGetCplStatus_P2060(pGpu, pExtDev, CplStatus, pVal);
138 }
139
140 return status;
141 }
142
143 // Write to the SyncSkew register
144 NV_STATUS
gsyncSetSyncSkew_P2061_V204(OBJGPU * pGpu,DACEXTERNALDEVICE * pExtDev,NvU32 syncSkew)145 gsyncSetSyncSkew_P2061_V204
146 (
147 OBJGPU *pGpu,
148 DACEXTERNALDEVICE *pExtDev,
149 NvU32 syncSkew
150 )
151 {
152 DACP2060EXTERNALDEVICE *pThis = (DACP2060EXTERNALDEVICE *)pExtDev;
153 NvU64 temp;
154
155 NV_CHECK_OR_RETURN(LEVEL_INFO,
156 syncSkew <= NV_P2061_V204_SYNC_SKEW_MAX_UNITS,
157 NV_ERR_INVALID_ARGUMENT);
158
159 //
160 // Cache the unmodified syncSkew value from the user.
161 // We use this later to improve the stability of returned values in GetSyncSkew.
162 //
163 pThis->lastUserSkewSent = syncSkew;
164
165 //
166 // Fix the skew value which goes to HW, because we passed the resolution up
167 // to NVAPI as 7us. However, the real resolution in hardware is
168 // (1 / 131.072 MHz) = 7.6293945...
169 // Fix it by multiplying by the ratio 7 / 7.6293945
170 //
171 temp = syncSkew;
172 temp *= 70000000; // Safe because temp is 64 bits
173 temp /= 76293945;
174 syncSkew = (NvU32)temp; // Safe because we multiplied by less than 1.
175
176 // update p2060 object
177 pThis->SyncStartDelay = syncSkew;
178
179 NV_ASSERT_OK_OR_RETURN(
180 writeregu008_extdeviceTargeted(pGpu, pExtDev, (NvU8)NV_P2060_SYNC_SKEW_LOW,
181 syncSkew & DRF_MASK(NV_P2060_SYNC_SKEW_LOW_VAL)));
182 NV_ASSERT_OK_OR_RETURN(
183 writeregu008_extdeviceTargeted(pGpu, pExtDev, (NvU8)NV_P2060_SYNC_SKEW_HIGH,
184 (syncSkew >> 8) & DRF_MASK(NV_P2060_SYNC_SKEW_HIGH_VAL)));
185 NV_ASSERT_OK_OR_RETURN(
186 writeregu008_extdeviceTargeted(pGpu, pExtDev, (NvU8)NV_P2060_SYNC_SKEW_UPPER,
187 (syncSkew >> 16) & DRF_MASK(NV_P2060_SYNC_SKEW_UPPER_VAL)));
188
189 return NV_OK;
190 }
191
192 // Read from the SyncSkew register
193 NV_STATUS
gsyncGetSyncSkew_P2061_V204(OBJGPU * pGpu,DACEXTERNALDEVICE * pExtDev,NvU32 * pSyncSkew)194 gsyncGetSyncSkew_P2061_V204
195 (
196 OBJGPU *pGpu,
197 DACEXTERNALDEVICE *pExtDev,
198 NvU32 *pSyncSkew
199 )
200 {
201 NvU8 data;
202 NvU32 syncSkew;
203 NvU64 temp;
204 DACP2060EXTERNALDEVICE *pThis = (DACP2060EXTERNALDEVICE *)pExtDev;
205
206 *pSyncSkew = 0;
207
208 NV_ASSERT_OK_OR_RETURN(readregu008_extdeviceTargeted(pGpu, pExtDev, (NvU8)NV_P2060_SYNC_SKEW_LOW, &data));
209 syncSkew = DRF_VAL(_P2060, _SYNC_SKEW_LOW, _VAL, data);
210
211 NV_ASSERT_OK_OR_RETURN(readregu008_extdeviceTargeted(pGpu, pExtDev, (NvU8)NV_P2060_SYNC_SKEW_HIGH, &data));
212 syncSkew |= DRF_VAL(_P2060, _SYNC_SKEW_HIGH, _VAL, data) << 8;
213
214 NV_ASSERT_OK_OR_RETURN(readregu008_extdeviceTargeted(pGpu, pExtDev, (NvU8)NV_P2060_SYNC_SKEW_UPPER, &data));
215 syncSkew |= DRF_VAL(_P2060, _SYNC_SKEW_UPPER, _VAL, data) << 16;
216
217 // update p2060 object
218 pThis->SyncStartDelay = syncSkew;
219
220 //
221 // Perform the opposite adjustment for returning the value to the user.
222 // Multiply by the ratio 7.6293945 / 7
223 //
224 temp = syncSkew;
225 temp *= 76293945; // Safe because temp is 64 bits
226 temp /= 70000000;
227 syncSkew = (NvU32)temp; // Safe because the HW register is only 24 bits wide to begin with
228
229 //
230 // Could be higher than maximum because we multiplied by greater than 1,
231 // but this would only happen by someone manually programming the register
232 //
233 NV_CHECK_OR_ELSE(LEVEL_INFO,
234 syncSkew <= NV_P2061_V204_SYNC_SKEW_MAX_UNITS,
235 syncSkew = NV_P2061_V204_SYNC_SKEW_MAX_UNITS);
236
237 //
238 // Returning this value with stability to the user:
239 // The programmed value can change slightly due to divisions, so if we read
240 // from the hardware and find a value that is very close, just return the
241 // last sent value. This assures stability even through multiple cycles of
242 // set-get-set-get.
243 //
244 if (pThis->lastUserSkewSent != NV_P2061_V204_SYNC_SKEW_INVALID)
245 {
246 NvS32 difference;
247
248 difference = syncSkew - pThis->lastUserSkewSent;
249 if ((difference >= -2) && (difference <= 2))
250 {
251 syncSkew = pThis->lastUserSkewSent;
252 }
253 }
254 *pSyncSkew = syncSkew;
255
256 return NV_OK;
257 }
258