xref: /reactos/win32ss/drivers/miniport/vbe/edid.c (revision 5100859e)
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 }
303