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