[HNETCFG] Sync with Wine 3.0. CORE-14225
[reactos.git] / win32ss / drivers / videoprt / ddc.c
1 /*
2 * VideoPort driver
3 *
4 * Copyright (C) 2002, 2003, 2004 ReactOS Team
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
10 *
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19 *
20 */
21
22 #include "videoprt.h"
23
24 #define NDEBUG
25 #include <debug.h>
26
27 #define DDC_EEPROM_ADDRESS 0xA0
28
29 /* PRIVATE FUNCTIONS **********************************************************/
30
31 #define LOW 0
32 #define HIGH 1
33 #define WRITE 0
34 #define READ 1
35 #define READ_SDA() (i2c->ReadDataLine(HwDeviceExtension))
36 #define READ_SCL() (i2c->ReadClockLine(HwDeviceExtension))
37 #define WRITE_SDA(state) (i2c->WriteDataLine(HwDeviceExtension, state))
38 #define WRITE_SCL(state) (i2c->WriteClockLine(HwDeviceExtension, state))
39
40 static LARGE_INTEGER HalfPeriodDelay = {{0, 70}};
41 #define DELAY_HALF() KeDelayExecutionThread(KernelMode, FALSE, &HalfPeriodDelay)
42
43
44 static BOOL
45 I2CWrite(PVOID HwDeviceExtension, PI2C_CALLBACKS i2c, UCHAR Data)
46 {
47 UCHAR Bit;
48 BOOL Ack;
49
50 /* transmit data */
51 for (Bit = (1 << 7); Bit != 0; Bit >>= 1)
52 {
53 WRITE_SCL(LOW);
54 WRITE_SDA((Data & Bit) ? HIGH : LOW);
55 DELAY_HALF();
56 WRITE_SCL(HIGH);
57 DELAY_HALF();
58 }
59
60 /* get ack */
61 WRITE_SCL(LOW);
62 WRITE_SDA(HIGH);
63 DELAY_HALF();
64 WRITE_SCL(HIGH);
65 do
66 {
67 DELAY_HALF();
68 }
69 while (READ_SCL() != HIGH);
70 Ack = (READ_SDA() == LOW);
71 DELAY_HALF();
72
73 INFO_(VIDEOPRT, "I2CWrite: %s\n", Ack ? "Ack" : "Nak");
74 return Ack;
75 }
76
77
78 static UCHAR
79 I2CRead(PVOID HwDeviceExtension, PI2C_CALLBACKS i2c, BOOL Ack)
80 {
81 INT Bit = 0x80;
82 UCHAR Data = 0;
83
84 /* pull down SCL and release SDA */
85 WRITE_SCL(LOW);
86 WRITE_SDA(HIGH);
87
88 /* read byte */
89 for (Bit = (1 << 7); Bit != 0; Bit >>= 1)
90 {
91 WRITE_SCL(LOW);
92 DELAY_HALF();
93 WRITE_SCL(HIGH);
94 DELAY_HALF();
95 if (READ_SDA() == HIGH)
96 Data |= Bit;
97 }
98
99 /* send ack/nak */
100 WRITE_SCL(LOW);
101 WRITE_SDA(Ack ? LOW : HIGH);
102 DELAY_HALF();
103 WRITE_SCL(HIGH);
104 do
105 {
106 DELAY_HALF();
107 }
108 while (READ_SCL() != HIGH);
109
110 return Data;
111 }
112
113
114 static VOID
115 I2CStop(PVOID HwDeviceExtension, PI2C_CALLBACKS i2c)
116 {
117 WRITE_SCL(LOW);
118 WRITE_SDA(LOW);
119 DELAY_HALF();
120 WRITE_SCL(HIGH);
121 DELAY_HALF();
122 WRITE_SDA(HIGH);
123 }
124
125
126 static BOOL
127 I2CStart(PVOID HwDeviceExtension, PI2C_CALLBACKS i2c, UCHAR Address)
128 {
129 /* make sure the bus is free */
130 if (READ_SDA() == LOW || READ_SCL() == LOW)
131 {
132 WARN_(VIDEOPRT, "I2CStart: Bus is not free!\n");
133 return FALSE;
134 }
135
136 /* send address */
137 WRITE_SDA(LOW);
138 DELAY_HALF();
139 if (!I2CWrite(HwDeviceExtension, i2c, Address))
140 {
141 /* ??release the bus?? */
142 I2CStop(HwDeviceExtension, i2c);
143 WARN_(VIDEOPRT, "I2CStart: Device not found (Address = 0x%x)\n", Address);
144 return FALSE;
145 }
146
147 INFO_(VIDEOPRT, "I2CStart: SUCCESS!\n");
148 return TRUE;
149 }
150
151
152 static BOOL
153 I2CRepStart(PVOID HwDeviceExtension, PI2C_CALLBACKS i2c, UCHAR Address)
154 {
155 /* setup lines for repeated start condition */
156 WRITE_SCL(LOW);
157 DELAY_HALF();
158 WRITE_SDA(HIGH);
159 DELAY_HALF();
160 WRITE_SCL(HIGH);
161 DELAY_HALF();
162
163 return I2CStart(HwDeviceExtension, i2c, Address);
164 }
165
166 /* PUBLIC FUNCTIONS ***********************************************************/
167
168 /*
169 * @implemented
170 */
171
172 BOOLEAN NTAPI
173 VideoPortDDCMonitorHelper(
174 PVOID HwDeviceExtension,
175 PVOID I2CFunctions,
176 PUCHAR pEdidBuffer,
177 ULONG EdidBufferSize
178 )
179 {
180 PDDC_CONTROL ddc = (PDDC_CONTROL)I2CFunctions;
181 PI2C_CALLBACKS i2c = &ddc->I2CCallbacks;
182 INT Count, i;
183 PUCHAR pBuffer = (PUCHAR)pEdidBuffer;
184 BOOL Ack;
185
186 TRACE_(VIDEOPRT, "VideoPortDDCMonitorHelper()\n");
187
188 ASSERT_IRQL_LESS_OR_EQUAL(PASSIVE_LEVEL);
189 if (ddc->Size != sizeof (ddc))
190 {
191 WARN_(VIDEOPRT, "ddc->Size != %d (%d)\n", sizeof (ddc), ddc->Size);
192 return FALSE;
193 }
194
195 /* select eeprom */
196 if (!I2CStart(HwDeviceExtension, i2c, DDC_EEPROM_ADDRESS | WRITE))
197 return FALSE;
198 /* set address */
199 if (!I2CWrite(HwDeviceExtension, i2c, 0x00))
200 return FALSE;
201 /* change into read mode */
202 if (!I2CRepStart(HwDeviceExtension, i2c, DDC_EEPROM_ADDRESS | READ))
203 return FALSE;
204 /* read eeprom */
205 RtlZeroMemory(pEdidBuffer, EdidBufferSize);
206 Count = min(128, EdidBufferSize);
207 for (i = 0; i < Count; i++)
208 {
209 Ack = ((i + 1) < Count);
210 pBuffer[i] = I2CRead(HwDeviceExtension, i2c, Ack);
211 }
212 I2CStop(HwDeviceExtension, i2c);
213
214 /* check EDID header */
215 if (pBuffer[0] != 0x00 || pBuffer[1] != 0xff ||
216 pBuffer[2] != 0xff || pBuffer[3] != 0xff ||
217 pBuffer[4] != 0xff || pBuffer[5] != 0xff ||
218 pBuffer[6] != 0xff || pBuffer[7] != 0x00)
219 {
220 WARN_(VIDEOPRT, "VideoPortDDCMonitorHelper(): Invalid EDID header!\n");
221 return FALSE;
222 }
223
224 INFO_(VIDEOPRT, "VideoPortDDCMonitorHelper(): EDID version %d rev. %d\n", pBuffer[18], pBuffer[19]);
225 INFO_(VIDEOPRT, "VideoPortDDCMonitorHelper() - SUCCESS!\n");
226 return TRUE;
227 }