[WIN32SS]
[reactos.git] / reactos / win32ss / drivers / miniport / vbe / edid.c
1 /*
2 * ReactOS VBE EDID management
3 *
4 * Copyright (C) 2006 Hervé Poussineau
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version 2
9 * of the License, or (at your option) any later version.
10 *
11 * This program 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
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License along
17 * with this program; if not, write to the Free Software Foundation, Inc.,
18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19 *
20 */
21
22 /* INCLUDES *******************************************************************/
23
24 #include "vbemp.h"
25
26 /* PUBLIC AND PRIVATE FUNCTIONS ***********************************************/
27
28 static VOID NTAPI
29 VBEWriteClockLine(
30 PVOID HwDeviceExtension,
31 UCHAR data)
32 {
33 INT10_BIOS_ARGUMENTS BiosRegisters;
34 PVBE_DEVICE_EXTENSION VBEDeviceExtension =
35 (PVBE_DEVICE_EXTENSION)HwDeviceExtension;
36
37 VideoPortZeroMemory(&BiosRegisters, sizeof(BiosRegisters));
38 BiosRegisters.Eax = VBE_DDC;
39 BiosRegisters.Ebx = VBE_DDC_WRITE_SCL_CLOCK_LINE;
40 BiosRegisters.Ecx = VBEDeviceExtension->CurrentChildIndex;
41 BiosRegisters.Edx = data;
42 VBEDeviceExtension->Int10Interface.Int10CallBios(
43 VBEDeviceExtension->Int10Interface.Context,
44 &BiosRegisters);
45 }
46
47 static VOID NTAPI
48 VBEWriteDataLine(
49 PVOID HwDeviceExtension,
50 UCHAR data)
51 {
52 INT10_BIOS_ARGUMENTS BiosRegisters;
53 PVBE_DEVICE_EXTENSION VBEDeviceExtension =
54 (PVBE_DEVICE_EXTENSION)HwDeviceExtension;
55
56 VideoPortZeroMemory(&BiosRegisters, sizeof(BiosRegisters));
57 BiosRegisters.Eax = VBE_DDC;
58 BiosRegisters.Ebx = VBE_DDC_WRITE_SDA_DATA_LINE;
59 BiosRegisters.Ecx = VBEDeviceExtension->CurrentChildIndex;
60 BiosRegisters.Edx = data;
61 VBEDeviceExtension->Int10Interface.Int10CallBios(
62 VBEDeviceExtension->Int10Interface.Context,
63 &BiosRegisters);
64 }
65
66 static BOOLEAN NTAPI
67 VBEReadClockLine(
68 PVOID HwDeviceExtension)
69 {
70 INT10_BIOS_ARGUMENTS BiosRegisters;
71 PVBE_DEVICE_EXTENSION VBEDeviceExtension =
72 (PVBE_DEVICE_EXTENSION)HwDeviceExtension;
73
74 VideoPortZeroMemory(&BiosRegisters, sizeof(BiosRegisters));
75 BiosRegisters.Eax = VBE_DDC;
76 BiosRegisters.Ebx = VBE_DDC_READ_SCL_CLOCK_LINE;
77 BiosRegisters.Ecx = VBEDeviceExtension->CurrentChildIndex;
78 VBEDeviceExtension->Int10Interface.Int10CallBios(
79 VBEDeviceExtension->Int10Interface.Context,
80 &BiosRegisters);
81
82 return BiosRegisters.Edx;
83 }
84
85 static BOOLEAN NTAPI
86 VBEReadDataLine(
87 PVOID HwDeviceExtension)
88 {
89 INT10_BIOS_ARGUMENTS BiosRegisters;
90 PVBE_DEVICE_EXTENSION VBEDeviceExtension =
91 (PVBE_DEVICE_EXTENSION)HwDeviceExtension;
92
93 VideoPortZeroMemory(&BiosRegisters, sizeof(BiosRegisters));
94 BiosRegisters.Eax = VBE_DDC;
95 BiosRegisters.Ebx = VBE_DDC_READ_SDA_DATA_LINE;
96 BiosRegisters.Ecx = VBEDeviceExtension->CurrentChildIndex;
97 VBEDeviceExtension->Int10Interface.Int10CallBios(
98 VBEDeviceExtension->Int10Interface.Context,
99 &BiosRegisters);
100
101 return BiosRegisters.Edx;
102 }
103
104 static BOOLEAN
105 VBEReadEdidUsingSCI(
106 IN PVOID HwDeviceExtension,
107 IN ULONG ChildIndex,
108 OUT PVOID Edid)
109 {
110 INT10_BIOS_ARGUMENTS BiosRegisters;
111 PVBE_DEVICE_EXTENSION VBEDeviceExtension =
112 (PVBE_DEVICE_EXTENSION)HwDeviceExtension;
113 DDC_CONTROL DDCControl;
114 BOOLEAN ret;
115
116 VideoPortDebugPrint(Trace, "VBEMP: VBEReadEdidUsingSCI() called\n");
117
118 /*
119 * Check if graphic card support I²C interface
120 */
121 VideoPortZeroMemory(&BiosRegisters, sizeof(BiosRegisters));
122 BiosRegisters.Eax = VBE_DDC;
123 BiosRegisters.Ebx = VBE_DDC_REPORT_CAPABILITIES;
124 BiosRegisters.Ecx = ChildIndex;
125 VBEDeviceExtension->Int10Interface.Int10CallBios(
126 VBEDeviceExtension->Int10Interface.Context,
127 &BiosRegisters);
128 if (VBE_GETRETURNCODE(BiosRegisters.Eax) != VBE_SUCCESS)
129 return FALSE;
130 VideoPortDebugPrint(Info, "VBEMP: VBE/SCI version %x\n", BiosRegisters.Ecx);
131 if ((BiosRegisters.Ebx & 0xF) != 0xF)
132 return FALSE;
133
134 /*
135 * Enable I²C interface
136 */
137 VideoPortZeroMemory(&BiosRegisters, sizeof(BiosRegisters));
138 BiosRegisters.Eax = VBE_DDC;
139 BiosRegisters.Ebx = VBE_DDC_BEGIN_SCL_SDA_CONTROL;
140 BiosRegisters.Ecx = ChildIndex;
141 VBEDeviceExtension->Int10Interface.Int10CallBios(
142 VBEDeviceExtension->Int10Interface.Context,
143 &BiosRegisters);
144 if (VBE_GETRETURNCODE(BiosRegisters.Eax) != VBE_SUCCESS)
145 return FALSE;
146
147 /*
148 * Read EDID information
149 */
150 VBEDeviceExtension->CurrentChildIndex = ChildIndex;
151 DDCControl.Size = sizeof(DDC_CONTROL);
152 DDCControl.I2CCallbacks.WriteClockLine = VBEWriteClockLine;
153 DDCControl.I2CCallbacks.WriteDataLine = VBEWriteDataLine;
154 DDCControl.I2CCallbacks.ReadClockLine = VBEReadClockLine;
155 DDCControl.I2CCallbacks.ReadDataLine = VBEReadDataLine;
156 DDCControl.EdidSegment = 0;
157 ret = VideoPortDDCMonitorHelper(
158 HwDeviceExtension,
159 &DDCControl,
160 (PUCHAR)&Edid,
161 MAX_SIZE_OF_EDID);
162
163 /*
164 * Disable I²C interface
165 */
166 VideoPortZeroMemory(&BiosRegisters, sizeof(BiosRegisters));
167 BiosRegisters.Eax = VBE_DDC;
168 BiosRegisters.Ebx = VBE_DDC_END_SCL_SDA_CONTROL;
169 VBEDeviceExtension->Int10Interface.Int10CallBios(
170 VBEDeviceExtension->Int10Interface.Context,
171 &BiosRegisters);
172 /* Ignore the possible error, as we did our best to prevent problems */
173
174 return ret;
175 }
176
177 static BOOLEAN
178 VBEReadEdid(
179 IN PVBE_DEVICE_EXTENSION VBEDeviceExtension,
180 IN ULONG ChildIndex,
181 OUT PVOID Edid)
182 {
183 INT10_BIOS_ARGUMENTS BiosRegisters;
184
185 VideoPortDebugPrint(Trace, "VBEMP: VBEReadEdid() called\n");
186
187 /*
188 * Directly read EDID information
189 */
190 VideoPortZeroMemory(&BiosRegisters, sizeof(BiosRegisters));
191 BiosRegisters.Eax = VBE_DDC;
192 BiosRegisters.Ebx = VBE_DDC_READ_EDID;
193 BiosRegisters.Ecx = ChildIndex;
194 BiosRegisters.Edx = 1;
195 BiosRegisters.Edi = VBEDeviceExtension->TrampolineMemoryOffset;
196 BiosRegisters.SegEs = VBEDeviceExtension->TrampolineMemorySegment;
197 VBEDeviceExtension->Int10Interface.Int10CallBios(
198 VBEDeviceExtension->Int10Interface.Context,
199 &BiosRegisters);
200
201 if (VBE_GETRETURNCODE(BiosRegisters.Eax) != VBE_SUCCESS)
202 return FALSE;
203
204 /*
205 * Copy the EDID information to our buffer
206 */
207 VBEDeviceExtension->Int10Interface.Int10ReadMemory(
208 VBEDeviceExtension->Int10Interface.Context,
209 VBEDeviceExtension->TrampolineMemorySegment,
210 VBEDeviceExtension->TrampolineMemoryOffset,
211 Edid,
212 MAX_SIZE_OF_EDID);
213
214 return TRUE;
215 }
216
217 VP_STATUS NTAPI
218 VBEGetVideoChildDescriptor(
219 IN PVOID HwDeviceExtension,
220 IN PVIDEO_CHILD_ENUM_INFO ChildEnumInfo,
221 OUT PVIDEO_CHILD_TYPE VideoChildType,
222 OUT PUCHAR pChildDescriptor,
223 OUT PULONG UId,
224 OUT PULONG pUnused)
225 {
226 PVBE_DEVICE_EXTENSION VBEDeviceExtension =
227 (PVBE_DEVICE_EXTENSION)HwDeviceExtension;
228 ULONG ChildIndex;
229
230 /*
231 * We are called very early in device initialization, even before
232 * VBEInitialize is called. So, our Int10 interface is not set.
233 * Ignore this call, we will trigger another one later.
234 */
235 if (VBEDeviceExtension->Int10Interface.Size == 0)
236 return VIDEO_ENUM_NO_MORE_DEVICES;
237
238 if (ChildEnumInfo->Size != sizeof(VIDEO_CHILD_ENUM_INFO))
239 {
240 VideoPortDebugPrint(Error, "VBEMP: Wrong VIDEO_CHILD_ENUM_INFO structure size\n");
241 return VIDEO_ENUM_NO_MORE_DEVICES;
242 }
243 else if (ChildEnumInfo->ChildDescriptorSize < MAX_SIZE_OF_EDID)
244 {
245 VideoPortDebugPrint(Warn, "VBEMP: Too small buffer for EDID\n");
246 return VIDEO_ENUM_NO_MORE_DEVICES;
247 }
248 else if (ChildEnumInfo->ChildIndex == DISPLAY_ADAPTER_HW_ID)
249 {
250 *VideoChildType = VideoChip;
251 *UId = 0;
252 return VIDEO_ENUM_MORE_DEVICES; /* FIXME: not sure... */
253 }
254
255 /*
256 * Get Child ID
257 */
258 if (ChildEnumInfo->ChildIndex != 0)
259 ChildIndex = ChildEnumInfo->ChildIndex;
260 else
261 ChildIndex = ChildEnumInfo->ACPIHwId;
262 VideoPortDebugPrint(Info, "VBEMP: ChildEnumInfo->ChildIndex %lu, ChildEnumInfo->ACPIHwId %lu => %lu\n",
263 ChildEnumInfo->ChildIndex, ChildEnumInfo->ACPIHwId, ChildIndex);
264
265 /*
266 * Try to read EDID information using 2 different methods.
267 */
268 if (VBEReadEdid(HwDeviceExtension, ChildIndex, pChildDescriptor))
269 {
270 VideoPortDebugPrint(Info, "VBEMP: EDID information read directly\n");
271 }
272 else if (VBEReadEdidUsingSCI(HwDeviceExtension, ChildIndex, pChildDescriptor))
273 {
274 VideoPortDebugPrint(Info, "VBEMP: EDID information read using I²C\n");
275 }
276 else if (ChildEnumInfo->ChildIndex == 1)
277 {
278 /* We must have 1 monitor, so just report it with no EDID information */
279 VideoPortDebugPrint(Info, "VBEMP: Reporting monitor with no EDID information\n");
280 }
281 else
282 {
283 VideoPortDebugPrint(Warn, "VBEMP: Unable to read EDID information\n");
284 return VIDEO_ENUM_NO_MORE_DEVICES;
285 }
286
287 /*
288 * Fill return data
289 */
290 *VideoChildType = Monitor;
291 if (ChildIndex == 0)
292 {
293 /*
294 * This is the actual display adapter
295 */
296 *UId = DISPLAY_ADAPTER_HW_ID;
297 }
298 else
299 *UId = ChildIndex;
300 *pUnused = 0;
301 return VIDEO_ENUM_MORE_DEVICES;
302 }