1 /*
2 * SPDX-FileCopyrightText: Copyright (c) 2010-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 * *
26 * Module: dp_edid.c *
27 * Implementation of SST/MST EDID reader *
28 * *
29 \***************************************************************************/
30
31 #include "dp_buffer.h"
32 #include "dp_internal.h"
33 #include "dp_edid.h"
34
35 using namespace DisplayPort;
36
EdidAssembler(Edid * const edid,bool bPatchCrc)37 EdidAssembler::EdidAssembler(Edid * const edid, bool bPatchCrc):
38 edid(edid), stream(edid->getBuffer()), oldBlockChecksum(0x00),
39 blocksRead(0), totalBlockCnt(0), retriesCount(0),
40 bPatchCrc(bPatchCrc) {}
41
42
readIsComplete()43 bool EdidAssembler::readIsComplete()
44 {
45 return (blocksRead > 0 && blocksRead == totalBlockCnt);
46 }
47
reset()48 void EdidAssembler::reset()
49 {
50 oldBlockChecksum = 0x00;
51 blocksRead = 0;
52 totalBlockCnt = 0;
53 retriesCount = 0;
54 stream.seek(0);
55 }
56
postReply(const Buffer & buffer,unsigned sizeCompleted,bool success)57 void EdidAssembler::postReply(const Buffer & buffer, unsigned sizeCompleted, bool success)
58 {
59 if (!success || buffer.isError())
60 {
61 retriesCount++;
62 return;
63 }
64
65 //
66 // For SST:
67 // Check the Checksum Error Per Block reading, mark the EDID as "patched" if
68 // CRC is wrong. DPLib will return fallback EDID.
69 //
70 blocksRead++;
71 stream.write(buffer.data, sizeCompleted);
72 if (getEDIDBlockChecksum(buffer))
73 {
74 if (bPatchCrc)
75 edid->patchCrc();
76 edid->setPatchedChecksum(true);
77 }
78 return;
79 }
80
postReply(unsigned char * data,unsigned sizeCompleted,bool success)81 void EdidAssembler::postReply(unsigned char * data, unsigned sizeCompleted, bool success)
82 {
83 //
84 // For MST: When read of edid block failed, library will attempt to read
85 // same block again, but not more than EDID_POLICY_BLOCK_READ_MAX_RETRY_COUNT times
86 //
87 if (!success)
88 {
89 retriesCount++;
90 return;
91 }
92
93 //
94 // Check the Checksum Error Per Block reading,
95 // library will attempt to read same block again,
96 // but not more than EDID_POLICY_BLOCK_READ_MAX_RETRY_COUNT times.
97 //
98 Buffer buffer(data, EDID_BLOCK_SIZE);
99 if (buffer.isError())
100 {
101 retriesCount++;
102 return;
103 }
104
105 NvU8 newBlockChecksum = getEDIDBlockChecksum(buffer);
106 if (newBlockChecksum)
107 {
108 if (this->oldBlockChecksum != newBlockChecksum) //First failure?
109 {
110 this->oldBlockChecksum = newBlockChecksum;
111 retriesCount++;
112 return;
113 }
114 }
115
116 this->oldBlockChecksum = 0;
117 retriesCount = 0;
118 blocksRead++;
119 stream.write(data, sizeCompleted);
120 }
121
readNextRequest(NvU8 & seg,NvU8 & offset)122 bool EdidAssembler::readNextRequest(NvU8 & seg, NvU8 & offset)
123 {
124 //
125 // cache totalBlockCnt,
126 // In EDID 1.3 HF-EEODB, it might changes after 1 extension block read.
127 //
128 if ((blocksRead == 1) || (blocksRead == 2))
129 totalBlockCnt = edid->getBlockCount();
130
131 //
132 // will return false in two scenarios
133 // 1. EDID read is complete, all extension blocks were read
134 // 2. First EDID block was corrupted, then totalBlockCnt = 0
135 //
136 if (blocksRead >= totalBlockCnt)
137 return false;
138
139 // Retry count exceeded for particular block?
140 if (retriesCount > EDID_POLICY_BLOCK_READ_MAX_RETRY_COUNT)
141 return false;
142
143 seg = NvU8(blocksRead >> 1);
144 offset = NvU8((blocksRead & 0x1) * EDID_BLOCK_SIZE);
145 return true;
146 }
147
148
149 enum
150 {
151 EDID_V1_IDX_EXTENSION = 0x7E,
152 EDID_V1_IDX_HEADER0 = 0x00,
153 EDID_V1_HEADER0 = 0x00,
154
155 EDID_V1_IDX_HEADER1 = 0x01,
156 EDID_V1_HEADER1 = 0xFF,
157
158 EDID_V1_IDX_VERSION = 0x12,
159 EDID_V1_VERSION_1 = 0x01,
160 EDID_V2_IDX_VERREV = 0x00,
161
162 //
163 // from od_edid.h RM to identify VER 2, use 7:4 bits.
164 // #define EDID_V2_VERREV_VERSION 7:4 /* RW--F */
165 // #define EDID_V2_VERREV_VERSION_2 0x02 /* RWI-V */
166 //
167 // Avoiding FLD_* macros, thus shift VER2 value 4 bits to left
168 //
169 EDID_V2_VERREV_VERSION_2 = 0x02 << 4,
170 EDID_FLAGS_CHKSUM_ATTEMPTS_DP = 0x5,
171 };
172
173 enum
174 {
175 // EDID CTA-EXT (CTA 861 Extension) block defines
176 EDID_CTA_EXT_HEADER_OFFSET = 0x00,
177 EDID_CTA_EXT_HEADER = 0x02,
178 EDID_CTA_EXT_VERSION_OFFSET = 0x01,
179 EDID_CTA_EXT_VERSION_3 = 0x03,
180 EDID_CTA_EXT_DATA_BLOCK_HEADER_OFFSET = 0x04,
181 EDID_CTA_EXT_DATA_BLOCK_HEADER_HF_EEODB = 0xE2,
182 EDID_CTA_EXT_DATA_BLOCK_TAG_OFFSET = 0x05,
183 EDID_CTA_EXT_DATA_BLOCK_TAG_HF_EEODB = 0x78,
184 EDID_CTA_EXT_DATA_BLOCK_EXT_COUNT_OFFSET = 0x06,
185 };
186
Edid()187 Edid::Edid(): buffer()
188 {
189 // fill EDID buffer with zeroes
190 this->buffer.memZero();
191 checkSumValid = false;
192 forcedCheckSum = false;
193 fallbackEdid = false;
194 patchedChecksum = false;
195
196 // clear the WARFlags and WARData.
197 _WARFlags flagTemp = {0};
198 _WARData dataTemp = {0};
199 WARFlags = flagTemp;
200 WARData = dataTemp;
201 }
202
~Edid()203 Edid::~Edid()
204 {
205 }
206
verifyCRC()207 bool Edid::verifyCRC()
208 {
209 if (getEdidSize() > 0)
210 {
211 this->validateCheckSum();
212 return this->checkSumValid;
213 }
214 else
215 return false;
216 }
217
218 // this routine patches the edid crc after it has been overridden for WARs.
patchCrc()219 void Edid::patchCrc()
220 {
221 // we always override some bytes within the first 128
222 // recalculate and fix the checksum for the first page only.
223 unsigned chksum = 0;
224 for (unsigned i = 0; i < 128; i++)
225 {
226 chksum += buffer.data[i];
227 }
228 chksum = chksum & 0xFF;
229
230 if (chksum)
231 buffer.data[127] = 0xFF & (buffer.data[127] + (0x100 - chksum));
232 }
233
isChecksumValid() const234 bool Edid::isChecksumValid() const
235 {
236 // return checksum valid if it is.
237 // else return checksum is valid if checksum wasn't valid but we will assume it to be.
238 return (checkSumValid || forcedCheckSum);
239 }
240
isFallbackEdid() const241 bool Edid::isFallbackEdid() const
242 {
243 return fallbackEdid;
244 }
245
getFirstPageChecksum()246 NvU8 Edid::getFirstPageChecksum()
247 {
248 DP_ASSERT(buffer.getLength() >= 128);
249 if (buffer.getLength() < 128)
250 return 0;
251 else
252 return buffer.data[127];
253 }
254
getLastPageChecksum()255 NvU8 Edid::getLastPageChecksum()
256 {
257 NvU32 bufferSize = buffer.getLength();
258 NvU32 checksumLocation = this->getBlockCount() * 128 - 1;
259
260 if (bufferSize == 0 || bufferSize < (this->getBlockCount() * 128))
261 {
262 DP_LOG(("DP-EDID> Edid length is 0 or less than required"));
263 return 0;
264 }
265
266 if (bufferSize % 128 != 0)
267 {
268 DP_LOG(("DP-EDID> Edid length is not a multiple of 128"));
269 return 0;
270 }
271
272 return buffer.data[checksumLocation];
273
274 }
275
validateCheckSum()276 void Edid::validateCheckSum()
277 {
278 // Each page has its own checksum
279 checkSumValid = false;
280 for (unsigned chunk = 0; chunk < this->buffer.length; chunk += 128)
281 {
282 unsigned chksum = 0;
283 for (unsigned i = 0; i < 128; i++)
284 {
285 chksum += buffer.data[i+chunk];
286 }
287
288 if ((chksum & 0xFF) != 0)
289 return;
290 }
291 checkSumValid = true;
292 }
293
getEdidVersion()294 unsigned Edid::getEdidVersion()
295 {
296 if (buffer.isError() || buffer.length < EDID_BLOCK_SIZE)
297 {
298 return 0;
299 }
300
301 // 0 version is "unknown"
302 unsigned version = 0;
303
304 // Check for Version 1 EDID
305 if (this->buffer.data[EDID_V1_IDX_VERSION] == EDID_V1_VERSION_1)
306 {
307 version = 1;
308 }
309 // Check for version 2 EDID
310 else if (this->buffer.data[EDID_V2_IDX_VERREV] & EDID_V2_VERREV_VERSION_2)
311 {
312 //
313 // Version 2 has 256 bytes by default.
314 // There is a note about an extra 256 byte block if byte 0x7E
315 // bit 7 is set but there's no definition for it listed in
316 // the EDID Version 3 (971113). So, let's just skip it for now.
317 //
318 version = 2;
319 }
320 else
321 {
322 DP_ASSERT(version && "Unknown EDID version");
323 }
324
325 return version;
326 }
327
getName() const328 const char * Edid::getName() const
329 {
330 static char decodedName[16] = {0};
331 int tail = 0;
332 if (buffer.length < 128)
333 return "?";
334
335 for (int i = 0; i < 4; i++)
336 if (buffer.data[0x39 + i * 18 + 0] == 0xFC)
337 {
338 for (int j = 0; j < 13; j++)
339 decodedName[tail++] = buffer.data[0x39 + i*18 + 2 + j];
340 break;
341 }
342 decodedName[tail++] = 0;
343 return decodedName;
344 }
345
getBlockCount()346 unsigned Edid::getBlockCount()
347 {
348 if (buffer.isError() || buffer.length < EDID_BLOCK_SIZE)
349 {
350 return 0;
351 }
352
353 unsigned version = getEdidVersion();
354
355 if (version == 1)
356 {
357 NvU32 blockCount = (unsigned) this->buffer.data[EDID_V1_IDX_EXTENSION]+1;
358
359 if (blockCount > EDID_MAX_BLOCK_COUNT)
360 {
361 DP_LOG(("DPEDID> %s: DDC read returned questionable results: "
362 "Total block Count too high: %d",
363 __FUNCTION__, blockCount));
364 return 1;
365 }
366 //
367 // Check for the HF-EEODB defined in HDMI 2.1 specification.
368 // 1. It is EDID version 1.3 and the extension block count is 1 (total block count = 2)
369 // 2. The 1st EDID extension block is already read. (buffer.length > block size)
370 // 3. The 1st EDID extension block is CTA extension block.
371 // 4. It has HF-EEODB (1st extension block: byte4 == 0xE2 and byte5 == 0x78)
372 //
373 if ((blockCount == 2) && (buffer.length >= EDID_BLOCK_SIZE * 2))
374 {
375 NvU8 *pExt = &(this->buffer.data[EDID_BLOCK_SIZE]);
376
377 //
378 // If it's a CTA-EXT block version 3 and has HF-EEODB
379 // defined, update the total block count.
380 //
381 if ((pExt[EDID_CTA_EXT_HEADER_OFFSET] == EDID_CTA_EXT_HEADER) &&
382 (pExt[EDID_CTA_EXT_VERSION_OFFSET] == EDID_CTA_EXT_VERSION_3) &&
383 (pExt[EDID_CTA_EXT_DATA_BLOCK_HEADER_OFFSET] == EDID_CTA_EXT_DATA_BLOCK_HEADER_HF_EEODB) &&
384 (pExt[EDID_CTA_EXT_DATA_BLOCK_TAG_OFFSET] == EDID_CTA_EXT_DATA_BLOCK_TAG_HF_EEODB))
385 {
386 blockCount = pExt[EDID_CTA_EXT_DATA_BLOCK_EXT_COUNT_OFFSET] + 1;
387 }
388
389 }
390 return blockCount;
391 }
392 else if (version == 2)
393 {
394 //
395 // Version 2 has 256 bytes by default.
396 // There is a note about an extra 256 byte block
397 // if byte 0x7E bit 7 is set, but there's no
398 // definition for it listed in the
399 // EDID Version 3 (971113) So, let's just skip
400 // it for now.
401 //
402 return 2;
403 }
404 else
405 {
406 // Unknown EDID version. Skip it.
407 DP_LOG(("DPEDID> %s: Unknown EDID Version!",__FUNCTION__));
408 DP_ASSERT(0 && "Unknown EDID version!");
409 return 1;
410 }
411 }
412
getEdidSize() const413 unsigned Edid::getEdidSize() const
414 {
415 return this->buffer.length;
416 }
417
swap(Edid & right)418 void DisplayPort::Edid::swap(Edid & right)
419 {
420 swapBuffers(buffer, right.buffer);
421 validateCheckSum();
422 }
423
424 const NvU8 fallbackEdidModes[5][EDID_BLOCK_SIZE] = {
425 // ID Manufacturer Name: NVD
426 // VIDEO INPUT DEFINITION:
427 // Digital Signal
428 // VESA DFP 1.x Compatible
429
430 //
431 // The first 4 entries are for NV_DPCD_SINK_VIDEO_FALLBACK_FORMATS (DPCD 0x20)
432 // 1024x768x60Hz: defined in bit 0.
433 // 1280x720x60Hz: defined in bit 1.
434 // 1920x1080x60Hz: defined in bit 2. [Mandatory]
435 //
436 {
437 // Bit 2: 1920x1080x60 only
438 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00,
439 0x3A, 0xC4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
440 0x00, 0x00, 0x01, 0x04, 0xA5, 0x00, 0x00, 0x64,
441 0xEE, 0x91, 0xA3, 0x54, 0x4C, 0x99, 0x26, 0x0F,
442 0x50, 0x54, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01,
443 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
444 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x3A,
445 0x80, 0x18, 0x71, 0x38, 0x2D, 0x40, 0x58, 0x2C,
446 0x43, 0x00, 0xC0, 0x1C, 0x32, 0x00, 0x00, 0x1C,
447 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
448 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
449 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
450 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
451 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
452 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
453 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xDB
454 },
455 {
456 // bit 2 + bit 0: 1920x1080x60 + 1024x768x60
457 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00,
458 0x3A, 0xC4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
459 0x00, 0x00, 0x01, 0x04, 0xA5, 0x00, 0x00, 0x64,
460 0xEE, 0x91, 0xA3, 0x54, 0x4C, 0x99, 0x26, 0x0F,
461 0x50, 0x54, 0x00, 0x00, 0x08, 0x00, 0x01, 0x01,
462 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
463 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x3A,
464 0x80, 0x18, 0x71, 0x38, 0x2D, 0x40, 0x58, 0x2C,
465 0x43, 0x00, 0xC0, 0x1C, 0x32, 0x00, 0x00, 0x1C,
466 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
467 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
468 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
469 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
470 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
471 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
472 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xD3
473 },
474 {
475 // bit 2 + bit 1: 1920x1080x60 + 1280x720x60
476 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00,
477 0x3A, 0xC4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
478 0x00, 0x00, 0x01, 0x04, 0xA5, 0x00, 0x00, 0x64,
479 0xEE, 0x91, 0xA3, 0x54, 0x4C, 0x99, 0x26, 0x0F,
480 0x50, 0x54, 0x00, 0x00, 0x00, 0x00, 0x81, 0xC0,
481 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
482 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x3A,
483 0x80, 0x18, 0x71, 0x38, 0x2D, 0x40, 0x58, 0x2C,
484 0x43, 0x00, 0xC0, 0x1C, 0x32, 0x00, 0x00, 0x1C,
485 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
486 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
487 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
488 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
489 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
490 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
491 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9C
492 },
493 {
494 // bit2 + bit 1 + bit 0: All 3 modes.
495 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00,
496 0x3A, 0xC4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
497 0x00, 0x00, 0x01, 0x04, 0xA5, 0x00, 0x00, 0x64,
498 0xEE, 0x91, 0xA3, 0x54, 0x4C, 0x99, 0x26, 0x0F,
499 0x50, 0x54, 0x00, 0x00, 0x08, 0x00, 0x81, 0xC0,
500 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
501 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x3A,
502 0x80, 0x18, 0x71, 0x38, 0x2D, 0x40, 0x58, 0x2C,
503 0x43, 0x00, 0xC0, 0x1C, 0x32, 0x00, 0x00, 0x1C,
504 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
505 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
506 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
507 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
508 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
509 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
510 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x94
511 },
512 {
513 // ESTABLISHED TIMING I:
514 // 640 X 480 @ 60Hz (IBM,VGA)
515 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00,
516 0x3A, 0xC4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
517 0x00, 0x00, 0x01, 0x04, 0x95, 0x00, 0x00, 0x78,
518 0xEE, 0x91, 0xA3, 0x54, 0x4C, 0x99, 0x26, 0x0F,
519 0x50, 0x54, 0x00, 0x20, 0x00, 0x00, 0x01, 0x01,
520 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
521 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00,
522 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
523 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
524 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
525 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
526 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
527 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
528 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
529 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
530 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x92
531 }
532 };
533
534 //
535 // Definition of DPCD 0x20:
536 // 1024x768x60Hz: defined in bit 0.
537 // 1280x720x60Hz: defined in bit 1.
538 // 1920x1080x60Hz: defined in bit 2. [Mandatory]
539 // MIN value is 4 (only 1920x1080 supported)
540 // MAX value is 7 (supports all 3 modes)
541 //
542 #define SINK_VIDEO_FALLBACK_FORMATS_MIN_VALUE (0x00000004)
543 #define SINK_VIDEO_FALLBACK_FORMATS_MAX_VALUE (0x00000007)
544
makeEdidFallback(Edid & edid,NvU32 fallbackFormatSupported)545 void DisplayPort::makeEdidFallback(Edid & edid, NvU32 fallbackFormatSupported)
546 {
547 const NvU8 *data;
548
549 // fallbackFormatSupported valid values = 4~7
550 if (fallbackFormatSupported > SINK_VIDEO_FALLBACK_FORMATS_MAX_VALUE ||
551 fallbackFormatSupported < SINK_VIDEO_FALLBACK_FORMATS_MIN_VALUE)
552 {
553 // 4 is default fallback mode. (only 640x480)
554 data = fallbackEdidModes[4];
555 }
556 else
557 {
558 data = fallbackEdidModes[fallbackFormatSupported-4];
559 }
560 if (!edid.getBuffer()->resize(EDID_BLOCK_SIZE))
561 return;
562
563 dpMemCopy(edid.getBuffer()->getData(), (const NvU8*)data, EDID_BLOCK_SIZE);
564 DP_ASSERT(edid.verifyCRC());
565 edid.setFallbackFlag(true);
566 }
567
568 /*
569 Fake EDID for DP2VGA dongle when the EDID of the real monitor is not available
570
571 Established Timings [20 CE 00]
572 640 x 480 @ 60Hz
573 800 x 600 @ 72Hz
574 800 x 600 @ 75Hz
575 1024 x 768 @ 60Hz
576 1024 x 768 @ 70Hz
577 1024 x 768 @ 75Hz
578
579 Standard Timings
580 Timing [3159] : 640 x 480 @ 85Hz (4:3)
581 Timing [4559] : 800 x 600 @ 85Hz (4:3)
582 Timing [6159] : 1024 x 768 @ 85Hz (4:3)
583 Timing [714F] : 1152 x 864 @ 75Hz (4:3)
584
585 Detailed Timing [DTD] 1280 x 1024 @ 60.02Hz
586 Pixel Clock : 108.00Mhz
587 HBlank, HBorder : 408, 0
588 HSyncStart, HSyncWidth : 48, 112
589 VBlank, VBorder : 42, 0
590 VSyncStart, VSyncWidth : 1, 3
591 Image size : 376mm x 301mm
592 DigitalSeparate +/+
593 */
594
makeEdidFallbackVGA(Edid & edid)595 void DisplayPort::makeEdidFallbackVGA(Edid & edid)
596 {
597 const NvU8 data[] = {
598 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x3A, 0xC4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
599 0x01, 0x13, 0x01, 0x03, 0x80, 0x26, 0x1E, 0x78, 0xEE, 0xCB, 0x05, 0xA3, 0x58, 0x4C, 0x9B, 0x25,
600 0x13, 0x50, 0x54, 0x20, 0xCE, 0x00, 0x31, 0x59, 0x45, 0x59, 0x61, 0x59, 0x71, 0x4F, 0x81, 0x40,
601 0x81, 0x80, 0x01, 0x01, 0x01, 0x01, 0x30, 0x2A, 0x00, 0x98, 0x51, 0x00, 0x2A, 0x40, 0x30, 0x70,
602 0x13, 0x00, 0x78, 0x2D, 0x11, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x00, 0xFD, 0x00, 0x30, 0x55, 0x1F,
603 0x52, 0x0E, 0x00, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x00, 0x00, 0x00, 0xFC, 0x00, 0x4C,
604 0x43, 0x44, 0x5F, 0x56, 0x47, 0x41, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF,
605 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xD8
606 };
607
608 if (!edid.getBuffer()->resize(sizeof(data)))
609 return;
610
611 dpMemCopy(edid.getBuffer()->getData(), (const NvU8*)data, sizeof data);
612 DP_ASSERT(edid.verifyCRC());
613 edid.setFallbackFlag(true);
614 }
615
getEDIDBlockChecksum(const Buffer & buffer)616 NvU8 DisplayPort::getEDIDBlockChecksum(const Buffer & buffer)
617 {
618 DP_ASSERT(buffer.getLength() == 128);
619
620 unsigned chksum = 0;
621 for (unsigned i = 0; i < buffer.getLength(); i++)
622 {
623 chksum += buffer.data[i];
624 }
625 chksum = chksum & 0xFF;
626 return (NvU8)chksum;
627 }
628