xref: /reactos/win32ss/drivers/videoprt/ddc.c (revision 40462c92)
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 }
228