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