1 /*
2  * SPDX-FileCopyrightText: Copyright (c) 1993-2021 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 /******************************* DisplayPort *******************************\
25 * Module: dp_auxretry.cpp                                                   *
26 *    Interface implemented by library client.                               *
27 *                                                                           *
28 \***************************************************************************/
29 
30 #include "dp_internal.h"
31 #include "dp_auxbus.h"
32 #include "dp_auxretry.h"
33 #include "dp_messageheader.h"
34 
35 #include "displayport.h"
36 
37 using namespace DisplayPort;
38 
39 //
40 //    Read a DPCD address.
41 //        - allows size greater than single transaction/burst size
42 //        - handles defer retries
43 //        - handles nacks with incomplete data
44 //
readTransaction(int address,NvU8 * buffer,unsigned size,unsigned retries)45 AuxRetry::status AuxRetry::readTransaction(int address, NvU8 * buffer, unsigned size, unsigned retries)
46 {
47     unsigned completed;
48     AuxBus::status s;
49 
50     DP_ASSERT( size <= aux->transactionSize() );
51 
52     do
53     {
54         s = aux->transaction(AuxBus::read, AuxBus::native, address, buffer, size, &completed);
55 
56         //
57         // Got success & requested data. Also size of returned data is
58         // expected & non zero.
59         //
60         if ((s == AuxBus::success) && (completed == size) && (completed != 0))
61         {
62             return ack;
63         }
64         else
65         {
66             //
67             //    Handle defer case with a simple retry
68             //
69             if (s == AuxBus::defer)
70             {
71                 if (retries)
72                 {
73                     --retries;
74                     continue;
75                 }
76 
77                 return defer;
78             }
79 
80             //
81             //    Nack shouldn't happen in general.  Unsupported registers
82             //    are supposed to ACK with size of 0.
83             //
84             if ( s == AuxBus::nack )
85             {
86                 return nack;
87             }
88 
89             if ( completed == 0 )
90             {
91                 return unsupportedRegister;
92             }
93 
94             //
95             //    We got less data back than we requested...
96             //     It's unclear when this might happen in the spec.
97             //     We can either
98             //            1. Split the read into multiple pieces
99             //                (Dangerous since we may receive non-atomic updates)
100             //            2. Retry
101             //
102             if ( completed < size )
103             {
104                 //
105                 //    Retry
106                 //
107                 if (retries)
108                 {
109                     --retries;
110                     continue;
111                 }
112                 else
113                 {
114                     // Closest approximation is a defer
115                     return defer;
116                 }
117             }
118         }
119     } while(retries);
120 
121     if ((s == AuxBus::defer) || (completed < size))
122     {
123         return defer;
124     }
125 
126     return ack;
127 }
128 
129 //
130 //    Write a DPCD address.
131 //        - allows size greater than single transaction/burst size
132 //        - handles defer retries
133 //        - handles nacks with incomplete data
134 //
writeTransaction(int address,NvU8 * buffer,unsigned size,unsigned retries)135 AuxRetry::status AuxRetry::writeTransaction(int address, NvU8 * buffer, unsigned size, unsigned retries)
136 {
137     unsigned completed;
138     AuxBus::status s;
139 
140     DP_ASSERT( size <= aux->transactionSize() );
141 
142     do
143     {
144         s = aux->transaction(AuxBus::write, AuxBus::native, address, buffer, size, &completed);
145 
146         //
147         // Got success & requested data. Also size of returned data is
148         // expected & non zero.
149         //
150         if ((s == AuxBus::success) && (completed == size) && (completed != 0))
151         {
152             return ack;
153         }
154         else
155         {
156             //
157             //    Handle defer case with a simple retry
158             //
159             if (s == AuxBus::defer)
160             {
161                 if (retries)
162                 {
163                     --retries;
164                     continue;
165                 }
166 
167                 return defer;
168             }
169 
170             //
171             //    Nack shouldn't happen in general.  Unsupported registers
172             //    are supposed to ACK with size of 0.
173             //
174             if ( s == AuxBus::nack )
175             {
176                 return nack;
177             }
178 
179             DP_ASSERT( s == AuxBus::success);
180 
181             if ( completed == 0 )
182             {
183                 return unsupportedRegister;
184             }
185 
186             //
187             //    Incomplete write?
188             //        Shouldn't happen.  Just retry if it does
189             //
190             if ( completed < size )
191             {
192                 //
193                 //    Retry
194                 //
195                 if (retries)
196                 {
197                     --retries;
198                     continue;
199                 }
200                 else
201                 {
202                     // Closest approximation is a defer
203                     return defer;
204                 }
205             }
206         }
207     } while(retries);
208 
209     if ((s == AuxBus::defer) || (completed < size))
210     {
211         return defer;
212     }
213 
214     return ack;
215 }
216 
217 //
218 //    Similar to readTransaction except that it supports reading
219 //    larger spans than AuxBus::transactionSize()
220 //
read(int address,NvU8 * buffer,unsigned size,unsigned retries)221 AuxRetry::status AuxRetry::read(int address, NvU8 * buffer, unsigned size, unsigned retries)
222 {
223     for (unsigned  i = 0 ; i < size; )
224     {
225         int todo = DP_MIN(size - i, aux->transactionSize());
226         status s = readTransaction(address+i, buffer+i, todo, retries);
227 
228         if (s != ack)
229         {
230             return s;
231         }
232 
233         i += todo;
234     }
235 
236     return ack;
237 }
238 
239 //
240 //    Similar to writeTransaction except that it supports writing
241 //    larger spans than AuxBus::transactionSize()
242 //
write(int address,NvU8 * buffer,unsigned size,unsigned retries)243 AuxRetry::status AuxRetry::write(int address, NvU8 * buffer, unsigned size, unsigned retries)
244 {
245     for (unsigned i = 0 ; i < size; )
246     {
247         int todo = DP_MIN(size - i, aux->transactionSize());
248         status s = writeTransaction(address+i, buffer+i, todo, retries);
249 
250         if (s != ack)
251         {
252             return s;
253         }
254 
255         i += todo;
256     }
257 
258     return ack;
259 }
260 
transaction(Action action,Type type,int address,NvU8 * buffer,unsigned sizeRequested,unsigned * sizeCompleted,unsigned * pNakReason,NvU8 offset,NvU8 nWriteTransactions)261 AuxBus::status AuxLogger::transaction(Action action, Type type, int address,
262                               NvU8 * buffer, unsigned sizeRequested,
263                               unsigned * sizeCompleted, unsigned * pNakReason,
264                               NvU8 offset, NvU8 nWriteTransactions)
265 {
266     AuxBus::status result = bus->transaction(action, type, address, buffer, sizeRequested, sizeCompleted);
267     hint[0] = 0;
268     //
269     // Do the hex dump.
270     //   - We can't make library calls
271     //   - We need to do this in one printf
272     if (result == success)
273     {
274         if (type == native)
275             if (address == NV_DPCD_MBOX_DOWN_REQ || address == NV_DPCD_MBOX_UP_REP ||
276                 address == NV_DPCD_MBOX_DOWN_REP || address == NV_DPCD_MBOX_UP_REQ)
277             {
278                 unsigned len = *sizeCompleted;
279                 Buffer storage(buffer, len);
280                 BitStreamReader reader(&storage, 0, len*8);
281                 MessageHeader header;
282                 DisplayPort::decodeHeader(&reader, &header, Address(1));
283                 Address::StringBuffer sb;
284                 DP_USED(sb);
285                 dpHexDump(&hex[0], sizeof(hex), buffer, header.headerSizeBits/8);
286                 dpHexDump(&hex_body[0], sizeof(hex), buffer + header.headerSizeBits/8, len - header.headerSizeBits/8);
287 #if defined(_DEBUG) || defined(DEBUG)
288                 const char * name = "";
289                 if (header.isTransactionStart && action==write && len > header.headerSizeBits/8)
290                     name = getRequestId(buffer[header.headerSizeBits/8]);
291 
292                 DP_LOG(("DP-AUX> %s%s%s%s%04Xh hint(to:%s %s%s %s #%d) { %s| %s}",
293                         sizeRequested ==  *sizeCompleted ? "" : "INCOMPLETE ", getStatus(result),
294                         getAction(action), getType(type), address,
295                         header.address.toString(sb), header.isTransactionStart ? "S" : "",
296                         header.isTransactionEnd ? "E" : "", name, header.messageNumber,
297                         hex, hex_body));
298 #endif
299                 return result;
300             }
301     }
302     else
303         hex[0] = 0;
304 
305     dpHexDump(&hex[0], sizeof(hex), buffer, *sizeCompleted);
306     DP_LOG(("DP-AUX> %s%s%s%s%04Xh { %s }",  sizeRequested ==  *sizeCompleted ? "" : "INCOMPLETE ",
307             getStatus(result), getAction(action), getType(type), address, hex));
308 
309     return result;
310 }
311 
CreateAuxLogger(AuxBus * auxBus)312 AuxBus * DisplayPort::CreateAuxLogger(AuxBus * auxBus)
313 {
314     return new AuxLogger(auxBus);
315 }
316