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, &regCtrl4);
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, &regStatus2);
73 
74     status = readregu008_extdeviceTargeted(pGpu, pExtDev,
75                                            (NvU8)NV_P2061_CONTROL4, &regCtrl4);
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, &regStatus2);
122             if (status != NV_OK)
123             {
124                 break;
125             }
126             status = readregu008_extdeviceTargeted(pGpu, pExtDev,
127                      (NvU8)NV_P2061_STATUS6, &regStatus6);
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