Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

449 lines
14 KiB

  1. /*++
  2. Copyright (c) 1990-2000 Microsoft Corporation
  3. Module Name:
  4. ddc.c
  5. Abstract:
  6. This is the NT Video port Display Data Channel (DDC) code. It contains the
  7. implementations for the EDID industry standard Extended Display
  8. Identification Data manipulations.
  9. Author:
  10. Bruce McQuistan (brucemc) 23-Sept-1996
  11. Environment:
  12. kernel mode only
  13. Notes:
  14. Based on VESA EDID Specification Version 2, April 9th, 1996
  15. Updated to support VESA E-DDC Proposed Standard Version 1P, July 13, 1999.
  16. --*/
  17. #include "videoprt.h"
  18. //
  19. // Make it easy to change debug verbosity.
  20. //
  21. #define DEBUG_DDC 1
  22. //
  23. // Define constants used by DDC.
  24. //
  25. #define EDID_1_SIZE 128
  26. #define EDID_2_SIZE 256
  27. #define EDID_QUERY_RETRIES 5
  28. #define DDC_I2C_DELAY 5 // Microseconds
  29. #define DDC_ADDRESS_SET_OFFSET (UCHAR)0xA0 // To set word offset into EDID
  30. #define DDC_ADDRESS_READ (UCHAR)0xA1 // To read EDID
  31. #define DDC_ADDRESS_PD_SET_OFFSET (UCHAR)0xA2 // As above for display with P&D connector
  32. #define DDC_ADDRESS_PD_READ (UCHAR)0xA3 // As above for display with P&D connector
  33. #define DDC_ADDRESS_SET_SEGMENT (UCHAR)0x60 // To set index to 256 bytes EDID segment
  34. #ifdef ALLOC_PRAGMA
  35. #pragma alloc_text (PAGE, VideoPortDDCMonitorHelper)
  36. #pragma alloc_text (PAGE, DDCReadEdidSegment)
  37. #endif // ALLOC_PRAGMA
  38. //
  39. // Exported routines.
  40. //
  41. VIDEOPORT_API
  42. BOOLEAN
  43. VideoPortDDCMonitorHelper(
  44. IN PVOID pHwDeviceExtension,
  45. IN PVOID pDDCControl,
  46. IN OUT PUCHAR pucEdidBuffer,
  47. IN ULONG ulEdidBufferSize
  48. )
  49. /*++
  50. Routine Description:
  51. This routine reads the EDID structure from the monitor using DDC.
  52. If caller asks for 256 bytes he may receive:
  53. 1. One 128 bytes EDID
  54. 2. Two 128 bytes EDIDs
  55. 3. One 256 bytes EDID (from P&D display)
  56. 4. No EDID
  57. Caller should always ask for 256 bytes, since it is impossble to
  58. read second 128 bytes block of the segment only.
  59. Arguments:
  60. pHwDeviceExtension - Points to per-adapter device extension.
  61. pDDCControl - DDC access control block.
  62. pucEdidBuffer - Buffer where information will be stored.
  63. For ACPI devices first four bytes are preset by
  64. the videoprt to indicated attempt to read the EDID.
  65. We should clear those bytes in case of the EDID
  66. read failure to prevent videoprt from unnecessary
  67. call of the ACPI method.
  68. ulEdidBufferSize - Size of the buffer to fill.
  69. Returns:
  70. TRUE - DDC read OK.
  71. FALSE - DDC read failed.
  72. --*/
  73. {
  74. ULONG ulChecksum; // EDID checksum
  75. ULONG ulScratch; // Temp variable
  76. ULONG ulTry; // EDID read retry counter
  77. ULONG ulSize; // EDID size to read
  78. UCHAR ucEdidSegment; // E-DDC segment to read
  79. BOOLEAN bEnhancedDDC; // Use enhanced DDC flag
  80. VIDEO_I2C_CONTROL i2CControl; // I2C lines handling functions
  81. PAGED_CODE();
  82. ASSERT(NULL != pHwDeviceExtension);
  83. ASSERT(NULL != pDDCControl);
  84. ASSERT(NULL != pucEdidBuffer);
  85. ASSERT(IS_HW_DEVICE_EXTENSION(pHwDeviceExtension) == TRUE);
  86. //
  87. // Check the size of the input structure.
  88. //
  89. if (((PDDC_CONTROL)pDDCControl)->Size == sizeof (I2C_FNC_TABLE))
  90. {
  91. ucEdidSegment = 0;
  92. bEnhancedDDC = FALSE; // Make sure we are backword compatible
  93. }
  94. else if (((PDDC_CONTROL)pDDCControl)->Size == sizeof (DDC_CONTROL))
  95. {
  96. ucEdidSegment = ((PDDC_CONTROL)pDDCControl)->EdidSegment;
  97. bEnhancedDDC = TRUE;
  98. }
  99. else
  100. {
  101. pVideoDebugPrint((0, "VIDEOPRT!VideoPortDDCMonitorHelper: Invalid DDC_CONTROL\n"));
  102. ASSERT(FALSE);
  103. return FALSE;
  104. }
  105. i2CControl.WriteClockLine = ((PDDC_CONTROL)pDDCControl)->I2CCallbacks.WriteClockLine;
  106. i2CControl.WriteDataLine = ((PDDC_CONTROL)pDDCControl)->I2CCallbacks.WriteDataLine;
  107. i2CControl.ReadClockLine = ((PDDC_CONTROL)pDDCControl)->I2CCallbacks.ReadClockLine;
  108. i2CControl.ReadDataLine = ((PDDC_CONTROL)pDDCControl)->I2CCallbacks.ReadDataLine;
  109. i2CControl.I2CDelay = DDC_I2C_DELAY * 10; // 100ns units
  110. ASSERT(NULL != i2CControl.WriteClockLine);
  111. ASSERT(NULL != i2CControl.WriteDataLine);
  112. ASSERT(NULL != i2CControl.ReadClockLine);
  113. ASSERT(NULL != i2CControl.ReadDataLine);
  114. //
  115. // Initialize I2C lines and switch monitor to DDC2 mode only for the first EDID.
  116. // This is the most time consuming operation, we don't want to repeat it.
  117. // We can safely assume we'll be always asked for the segment 0 first.
  118. // Once switched to DDC2 the monitor will stay in that mode.
  119. //
  120. if (0 == ucEdidSegment)
  121. {
  122. //
  123. // Initialize SDA and SCL lines to default state of released high (input).
  124. //
  125. i2CControl.WriteDataLine(pHwDeviceExtension, 1);
  126. DELAY_MICROSECONDS(DDC_I2C_DELAY);
  127. i2CControl.WriteClockLine(pHwDeviceExtension, 1);
  128. DELAY_MICROSECONDS(DDC_I2C_DELAY);
  129. //
  130. // Send 9 clock pulses on SCL to switch DDC2-capable monitor to DDC2 mode.
  131. //
  132. for (ulScratch = 0; ulScratch < 9; ulScratch++)
  133. {
  134. i2CControl.WriteClockLine(pHwDeviceExtension, 0);
  135. DELAY_MICROSECONDS(DDC_I2C_DELAY);
  136. i2CControl.WriteClockLine(pHwDeviceExtension, 1);
  137. DELAY_MICROSECONDS(DDC_I2C_DELAY);
  138. }
  139. if (I2CWaitForClockLineHigh2(pHwDeviceExtension, &i2CControl) == FALSE)
  140. {
  141. pVideoDebugPrint((0, "VIDEOPRT!VideoPortDDCMonitorHelper: Can't switch to DDC2\n"));
  142. RtlZeroMemory(pucEdidBuffer, sizeof (ULONG)); // Let videoprt know we tried to read
  143. return FALSE;
  144. }
  145. }
  146. //
  147. // Using A0/A1 we can read two 128 byte EDIDs. If we are asked for a bigger size
  148. // we will do two reads.
  149. //
  150. ulSize = ulEdidBufferSize > EDID_1_SIZE ? EDID_1_SIZE : ulEdidBufferSize;
  151. if (DDCReadEdidSegment(pHwDeviceExtension,
  152. &i2CControl,
  153. pucEdidBuffer,
  154. ulSize,
  155. ucEdidSegment,
  156. 0x00,
  157. DDC_ADDRESS_SET_OFFSET,
  158. DDC_ADDRESS_READ,
  159. bEnhancedDDC) == TRUE)
  160. {
  161. if (ulEdidBufferSize <= EDID_1_SIZE)
  162. {
  163. return TRUE;
  164. }
  165. ulSize = ulEdidBufferSize - EDID_1_SIZE;
  166. //
  167. // We can read maximum two EDIDs per segment - make sure our size is correct.
  168. //
  169. if (ulSize > EDID_1_SIZE)
  170. {
  171. ulSize = EDID_1_SIZE;
  172. }
  173. //
  174. // We don't care about return code here - we've already got first EDID,
  175. // and it is possible the second one doesn't exist.
  176. //
  177. DDCReadEdidSegment(pHwDeviceExtension,
  178. &i2CControl,
  179. pucEdidBuffer + EDID_1_SIZE,
  180. ulSize,
  181. ucEdidSegment,
  182. 0x80,
  183. DDC_ADDRESS_SET_OFFSET,
  184. DDC_ADDRESS_READ,
  185. bEnhancedDDC);
  186. return TRUE;
  187. }
  188. //
  189. // Check for P&D 256 EDID at A2/A3 only for segment 0.
  190. //
  191. if (0 != ucEdidSegment)
  192. return FALSE;
  193. //
  194. // P&D display is a special case - its 256 bytes EDID can be accessed using
  195. // A2/A3 or using segment 1 and A0/A1. We shoudn't read its EDID twice though
  196. // since we're going to use A2/A3 only if we can't read segment 0 using A0/A1,
  197. // which most likely means that there are no multiple EDIDs.
  198. //
  199. // Note: In this case we don't want to program E-DDC segment, so we just
  200. // always force bEnhancedDDC to FALSE.
  201. //
  202. return DDCReadEdidSegment(pHwDeviceExtension,
  203. &i2CControl,
  204. pucEdidBuffer,
  205. ulEdidBufferSize,
  206. ucEdidSegment,
  207. 0x00,
  208. DDC_ADDRESS_PD_SET_OFFSET,
  209. DDC_ADDRESS_PD_READ,
  210. FALSE);
  211. } // VideoPortDDCMonitorHelper()
  212. //
  213. // Local routines.
  214. //
  215. BOOLEAN
  216. DDCReadEdidSegment(
  217. IN PVOID pHwDeviceExtension,
  218. IN PVIDEO_I2C_CONTROL pI2CControl,
  219. IN OUT PUCHAR pucEdidBuffer,
  220. IN ULONG ulEdidBufferSize,
  221. IN UCHAR ucEdidSegment,
  222. IN UCHAR ucEdidOffset,
  223. IN UCHAR ucSetOffsetAddress,
  224. IN UCHAR ucReadAddress,
  225. IN BOOLEAN bEnhancedDDC
  226. )
  227. /*++
  228. Routine Description:
  229. This routine reads the EDID structure at given segment.
  230. Arguments:
  231. pHwDeviceExtension - Points to per-adapter device extension.
  232. pI2CControl - I2C lines control functions.
  233. pucEdidBuffer - Buffer where information will be stored.
  234. ulEdidBufferSize - Size of the buffer to fill.
  235. ucEdidSegment - 256 bytes EDID segment to read.
  236. ucEdidOffset - Offset within the segment.
  237. ucSetOffsetAddress - DDC command.
  238. ucReadAddress - DDC command.
  239. bEnhancedDDC - TRUE if we want to use 0x60 for segment addressing.
  240. Returns:
  241. TRUE - DDC read OK.
  242. FALSE - DDC read failed.
  243. --*/
  244. {
  245. ULONG ulScratch; // Temp variable
  246. ULONG ulTry; // EDID read retry counter
  247. PAGED_CODE();
  248. ASSERT(NULL != pHwDeviceExtension);
  249. ASSERT(NULL != pI2CControl);
  250. ASSERT(NULL != pucEdidBuffer);
  251. ASSERT(NULL != pI2CControl->WriteClockLine);
  252. ASSERT(NULL != pI2CControl->WriteDataLine);
  253. ASSERT(NULL != pI2CControl->ReadClockLine);
  254. ASSERT(NULL != pI2CControl->ReadDataLine);
  255. ASSERT(IS_HW_DEVICE_EXTENSION(pHwDeviceExtension) == TRUE);
  256. for (ulTry = 0; ulTry < EDID_QUERY_RETRIES; ulTry++)
  257. {
  258. RtlZeroMemory(pucEdidBuffer, ulEdidBufferSize);
  259. //
  260. // Set EDID segment for E-DDC.
  261. //
  262. if (TRUE == bEnhancedDDC)
  263. {
  264. if (I2CStart2(pHwDeviceExtension, pI2CControl) == FALSE)
  265. {
  266. I2CStop2(pHwDeviceExtension, pI2CControl);
  267. pVideoDebugPrint((DEBUG_DDC, "VIDEOPRT!DDCReadEdidSegment: Failed I2C start\n"));
  268. continue;
  269. }
  270. pucEdidBuffer[0] = DDC_ADDRESS_SET_SEGMENT;
  271. pucEdidBuffer[1] = ucEdidSegment;
  272. if (I2CWrite2(pHwDeviceExtension, pI2CControl, pucEdidBuffer, 2) == FALSE)
  273. {
  274. //
  275. // For segment 0 we don't care about return code here since monitor
  276. // may not support E-DDC.
  277. //
  278. if (0 != ucEdidSegment)
  279. {
  280. I2CStop2(pHwDeviceExtension, pI2CControl);
  281. pVideoDebugPrint((DEBUG_DDC, "VIDEOPRT!DDCReadEdidSegment: Failed I2C write\n"));
  282. continue;
  283. }
  284. }
  285. }
  286. if (I2CStart2(pHwDeviceExtension, pI2CControl) == FALSE)
  287. {
  288. I2CStop2(pHwDeviceExtension, pI2CControl);
  289. pVideoDebugPrint((DEBUG_DDC, "VIDEOPRT!DDCReadEdidSegment: Failed I2C start\n"));
  290. continue;
  291. }
  292. //
  293. // Set offset to read from.
  294. //
  295. pucEdidBuffer[0] = ucSetOffsetAddress;
  296. pucEdidBuffer[1] = ucEdidOffset;
  297. if (I2CWrite2(pHwDeviceExtension, pI2CControl, pucEdidBuffer, 2) == FALSE)
  298. {
  299. I2CStop2(pHwDeviceExtension, pI2CControl);
  300. pVideoDebugPrint((DEBUG_DDC, "VIDEOPRT!DDCReadEdidSegment: Failed I2C write\n"));
  301. continue;
  302. }
  303. if (I2CStart2(pHwDeviceExtension, pI2CControl) == FALSE)
  304. {
  305. I2CStop2(pHwDeviceExtension, pI2CControl);
  306. pVideoDebugPrint((DEBUG_DDC, "VIDEOPRT!DDCReadEdidSegment: Failed I2C start\n"));
  307. continue;
  308. }
  309. //
  310. // Tell the monitor that we want to read EDID.
  311. //
  312. pucEdidBuffer[0] = ucReadAddress;
  313. if (I2CWrite2(pHwDeviceExtension, pI2CControl, pucEdidBuffer, 1) == FALSE)
  314. {
  315. I2CStop2(pHwDeviceExtension, pI2CControl);
  316. pVideoDebugPrint((DEBUG_DDC, "VIDEOPRT!DDCReadEdidSegment: Failed I2C write\n"));
  317. continue;
  318. }
  319. //
  320. // Read EDID from the monitor.
  321. //
  322. if (I2CRead2(pHwDeviceExtension, pI2CControl, pucEdidBuffer, ulEdidBufferSize, TRUE) == FALSE)
  323. {
  324. I2CStop2(pHwDeviceExtension, pI2CControl);
  325. pVideoDebugPrint((DEBUG_DDC, "VIDEOPRT!DDCReadEdidSegment: Failed I2C read\n"));
  326. continue;
  327. }
  328. I2CStop2(pHwDeviceExtension, pI2CControl);
  329. //
  330. // Calculate the EDID checksum in case when we read full EDID.
  331. // We should have 0x00 in LSB for proper EDID.
  332. //
  333. if (((EDID_1_SIZE == ulEdidBufferSize) && ((0x00 == ucEdidOffset) || (0x80 == ucEdidOffset))) ||
  334. ((EDID_2_SIZE == ulEdidBufferSize) && (0x00 == ucEdidOffset)))
  335. {
  336. ULONG ulChecksum = 0;
  337. for (ulScratch = 0; ulScratch < ulEdidBufferSize; ulScratch++)
  338. ulChecksum += pucEdidBuffer[ulScratch];
  339. pVideoDebugPrint((DEBUG_DDC, "VIDEOPRT!DDCReadEdidSegment: EDID checksum = 0x%08X\n", ulChecksum));
  340. if (((ulChecksum & 0xFF) == 0) &&
  341. (0 != ulChecksum) &&
  342. (ulChecksum != ulEdidBufferSize * 0xFF))
  343. {
  344. pVideoDebugPrint((DEBUG_DDC, "VIDEOPRT!DDCReadEdidSegment: Full EDID read OK\n"));
  345. return TRUE;
  346. }
  347. pVideoDebugPrint((DEBUG_DDC, "VIDEOPRT!DDCReadEdidSegment: Invalid checksum\n"));
  348. }
  349. else
  350. {
  351. pVideoDebugPrint((DEBUG_DDC, "VIDEOPRT!DDCReadEdidSegment: Partial EDID read OK\n"));
  352. return TRUE;
  353. }
  354. }
  355. pVideoDebugPrint((DEBUG_DDC, "VIDEOPRT!DDCReadEdidSegment: Failed\n"));
  356. RtlZeroMemory(pucEdidBuffer, sizeof (ULONG)); // Let videoprt know we tried to read
  357. return FALSE;
  358. } // DDCReadEdidSegment()