xref: /reactos/win32ss/drivers/miniport/vbe/edid.c (revision 98e8827a)
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     if (ChildEnumInfo->Size != sizeof(VIDEO_CHILD_ENUM_INFO) ||
239         ChildEnumInfo->ChildDescriptorSize < MAX_SIZE_OF_EDID)
240     {
241         return ERROR_INVALID_FUNCTION;
242     }
243 
244     if (ChildEnumInfo->ChildIndex == 0)
245     {
246         /* We don't support enumeration of ACPI children */
247         return ERROR_NO_MORE_DEVICES;
248     }
249     else if (ChildEnumInfo->ChildIndex == 1)
250     {
251         /* Our screen */
252         *VideoChildType = Monitor;
253         *UId = 1;
254 
255         /* Try to read EDID information using 2 different methods. */
256         if (VBEReadEdid(HwDeviceExtension, 0, pChildDescriptor))
257         {
258             VideoPortDebugPrint(Info, "VBEMP: EDID information read directly\n");
259         }
260         else if (VBEReadEdidUsingSCI(HwDeviceExtension, 0, pChildDescriptor))
261         {
262             VideoPortDebugPrint(Info, "VBEMP: EDID information read using I2C\n");
263         }
264 
265         return VIDEO_ENUM_MORE_DEVICES;
266     }
267     else
268     {
269         /* Unknown hardware id */
270         return ERROR_NO_MORE_DEVICES;
271     }
272 }
273