xref: /reactos/win32ss/drivers/miniport/vbe/edid.c (revision 9393fc32)
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     * Check if DDC1/DDC2 is supported
189     */
190    VideoPortZeroMemory(&BiosRegisters, sizeof(BiosRegisters));
191    BiosRegisters.Eax = VBE_DDC;
192    VBEDeviceExtension->Int10Interface.Int10CallBios(
193       VBEDeviceExtension->Int10Interface.Context,
194       &BiosRegisters);
195    if (VBE_GETRETURNCODE(BiosRegisters.Eax) != VBE_SUCCESS)
196       return FALSE;
197    if ((BiosRegisters.Ebx & 3) == 0)
198       return FALSE;
199 
200    /*
201     * Directly read EDID information
202     */
203    VideoPortZeroMemory(&BiosRegisters, sizeof(BiosRegisters));
204    BiosRegisters.Eax = VBE_DDC;
205    BiosRegisters.Ebx = VBE_DDC_READ_EDID;
206    BiosRegisters.Ecx = ChildIndex;
207    BiosRegisters.Edi = VBEDeviceExtension->TrampolineMemoryOffset;
208    BiosRegisters.SegEs = VBEDeviceExtension->TrampolineMemorySegment;
209    VBEDeviceExtension->Int10Interface.Int10CallBios(
210       VBEDeviceExtension->Int10Interface.Context,
211       &BiosRegisters);
212 
213    if (VBE_GETRETURNCODE(BiosRegisters.Eax) != VBE_SUCCESS)
214       return FALSE;
215 
216    /*
217     * Copy the EDID information to our buffer
218     */
219    VBEDeviceExtension->Int10Interface.Int10ReadMemory(
220       VBEDeviceExtension->Int10Interface.Context,
221       VBEDeviceExtension->TrampolineMemorySegment,
222       VBEDeviceExtension->TrampolineMemoryOffset,
223       Edid,
224       MAX_SIZE_OF_EDID);
225 
226    return TRUE;
227 }
228 
229 VP_STATUS NTAPI
230 VBEGetVideoChildDescriptor(
231    IN PVOID HwDeviceExtension,
232    IN PVIDEO_CHILD_ENUM_INFO ChildEnumInfo,
233    OUT PVIDEO_CHILD_TYPE VideoChildType,
234    OUT PUCHAR pChildDescriptor,
235    OUT PULONG UId,
236    OUT PULONG pUnused)
237 {
238    PVBE_DEVICE_EXTENSION VBEDeviceExtension =
239      (PVBE_DEVICE_EXTENSION)HwDeviceExtension;
240    ULONG ChildIndex;
241 
242    /*
243     * We are called very early in device initialization, even before
244     * VBEInitialize is called. So, our Int10 interface is not set.
245     * Ignore this call, we will trigger another one later.
246     */
247    if (VBEDeviceExtension->Int10Interface.Size == 0)
248       return VIDEO_ENUM_NO_MORE_DEVICES;
249 
250    if (ChildEnumInfo->Size != sizeof(VIDEO_CHILD_ENUM_INFO))
251    {
252       VideoPortDebugPrint(Error, "VBEMP: Wrong VIDEO_CHILD_ENUM_INFO structure size\n");
253       return VIDEO_ENUM_NO_MORE_DEVICES;
254    }
255    else if (ChildEnumInfo->ChildDescriptorSize < MAX_SIZE_OF_EDID)
256    {
257       VideoPortDebugPrint(Warn, "VBEMP: Too small buffer for EDID\n");
258       return VIDEO_ENUM_NO_MORE_DEVICES;
259    }
260    else if (ChildEnumInfo->ChildIndex == DISPLAY_ADAPTER_HW_ID)
261    {
262       *VideoChildType = VideoChip;
263       *UId = 0;
264       return VIDEO_ENUM_MORE_DEVICES; /* FIXME: not sure... */
265    }
266 
267    /*
268     * Get Child ID
269     */
270    if (ChildEnumInfo->ChildIndex != 0)
271       ChildIndex = ChildEnumInfo->ChildIndex;
272    else
273       ChildIndex = ChildEnumInfo->ACPIHwId;
274    VideoPortDebugPrint(Info, "VBEMP: ChildEnumInfo->ChildIndex %lu, ChildEnumInfo->ACPIHwId %lu => %lu\n",
275       ChildEnumInfo->ChildIndex, ChildEnumInfo->ACPIHwId, ChildIndex);
276 
277    /*
278     * Try to read EDID information using 2 different methods.
279     */
280    if (VBEReadEdid(HwDeviceExtension, ChildIndex, pChildDescriptor))
281    {
282       VideoPortDebugPrint(Info, "VBEMP: EDID information read directly\n");
283    }
284    else if (VBEReadEdidUsingSCI(HwDeviceExtension, ChildIndex, pChildDescriptor))
285    {
286       VideoPortDebugPrint(Info, "VBEMP: EDID information read using I�C\n");
287    }
288    else if (ChildEnumInfo->ChildIndex == 1)
289    {
290        /* We must have 1 monitor, so just report it with no EDID information */
291        VideoPortDebugPrint(Info, "VBEMP: Reporting monitor with no EDID information\n");
292    }
293    else
294    {
295       VideoPortDebugPrint(Warn, "VBEMP: Unable to read EDID information\n");
296       return VIDEO_ENUM_NO_MORE_DEVICES;
297    }
298 
299    /*
300     * Fill return data
301     */
302    *VideoChildType = Monitor;
303    if (ChildIndex == 0)
304    {
305       /*
306        * This is the actual display adapter
307        */
308       *UId = DISPLAY_ADAPTER_HW_ID;
309    }
310    else
311       *UId = ChildIndex;
312    *pUnused = 0;
313    return VIDEO_ENUM_MORE_DEVICES;
314 }
315