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