/* * ReactOS VBE EDID management * * Copyright (C) 2006 Hervé Poussineau * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * */ /* INCLUDES *******************************************************************/ #include "vbemp.h" /* PUBLIC AND PRIVATE FUNCTIONS ***********************************************/ static VOID NTAPI VBEWriteClockLine( PVOID HwDeviceExtension, UCHAR data) { INT10_BIOS_ARGUMENTS BiosRegisters; PVBE_DEVICE_EXTENSION VBEDeviceExtension = (PVBE_DEVICE_EXTENSION)HwDeviceExtension; VideoPortZeroMemory(&BiosRegisters, sizeof(BiosRegisters)); BiosRegisters.Eax = VBE_DDC; BiosRegisters.Ebx = VBE_DDC_WRITE_SCL_CLOCK_LINE; BiosRegisters.Ecx = VBEDeviceExtension->CurrentChildIndex; BiosRegisters.Edx = data; VBEDeviceExtension->Int10Interface.Int10CallBios( VBEDeviceExtension->Int10Interface.Context, &BiosRegisters); } static VOID NTAPI VBEWriteDataLine( PVOID HwDeviceExtension, UCHAR data) { INT10_BIOS_ARGUMENTS BiosRegisters; PVBE_DEVICE_EXTENSION VBEDeviceExtension = (PVBE_DEVICE_EXTENSION)HwDeviceExtension; VideoPortZeroMemory(&BiosRegisters, sizeof(BiosRegisters)); BiosRegisters.Eax = VBE_DDC; BiosRegisters.Ebx = VBE_DDC_WRITE_SDA_DATA_LINE; BiosRegisters.Ecx = VBEDeviceExtension->CurrentChildIndex; BiosRegisters.Edx = data; VBEDeviceExtension->Int10Interface.Int10CallBios( VBEDeviceExtension->Int10Interface.Context, &BiosRegisters); } static BOOLEAN NTAPI VBEReadClockLine( PVOID HwDeviceExtension) { INT10_BIOS_ARGUMENTS BiosRegisters; PVBE_DEVICE_EXTENSION VBEDeviceExtension = (PVBE_DEVICE_EXTENSION)HwDeviceExtension; VideoPortZeroMemory(&BiosRegisters, sizeof(BiosRegisters)); BiosRegisters.Eax = VBE_DDC; BiosRegisters.Ebx = VBE_DDC_READ_SCL_CLOCK_LINE; BiosRegisters.Ecx = VBEDeviceExtension->CurrentChildIndex; VBEDeviceExtension->Int10Interface.Int10CallBios( VBEDeviceExtension->Int10Interface.Context, &BiosRegisters); return BiosRegisters.Edx; } static BOOLEAN NTAPI VBEReadDataLine( PVOID HwDeviceExtension) { INT10_BIOS_ARGUMENTS BiosRegisters; PVBE_DEVICE_EXTENSION VBEDeviceExtension = (PVBE_DEVICE_EXTENSION)HwDeviceExtension; VideoPortZeroMemory(&BiosRegisters, sizeof(BiosRegisters)); BiosRegisters.Eax = VBE_DDC; BiosRegisters.Ebx = VBE_DDC_READ_SDA_DATA_LINE; BiosRegisters.Ecx = VBEDeviceExtension->CurrentChildIndex; VBEDeviceExtension->Int10Interface.Int10CallBios( VBEDeviceExtension->Int10Interface.Context, &BiosRegisters); return BiosRegisters.Edx; } static BOOLEAN VBEReadEdidUsingSCI( IN PVOID HwDeviceExtension, IN ULONG ChildIndex, OUT PVOID Edid) { INT10_BIOS_ARGUMENTS BiosRegisters; PVBE_DEVICE_EXTENSION VBEDeviceExtension = (PVBE_DEVICE_EXTENSION)HwDeviceExtension; DDC_CONTROL DDCControl; BOOLEAN ret; VideoPortDebugPrint(Trace, "VBEMP: VBEReadEdidUsingSCI() called\n"); /* * Check if graphic card support I²C interface */ VideoPortZeroMemory(&BiosRegisters, sizeof(BiosRegisters)); BiosRegisters.Eax = VBE_DDC; BiosRegisters.Ebx = VBE_DDC_REPORT_CAPABILITIES; BiosRegisters.Ecx = ChildIndex; VBEDeviceExtension->Int10Interface.Int10CallBios( VBEDeviceExtension->Int10Interface.Context, &BiosRegisters); if (VBE_GETRETURNCODE(BiosRegisters.Eax) != VBE_SUCCESS) return FALSE; VideoPortDebugPrint(Info, "VBEMP: VBE/SCI version %x\n", BiosRegisters.Ecx); if ((BiosRegisters.Ebx & 0xF) != 0xF) return FALSE; /* * Enable I²C interface */ VideoPortZeroMemory(&BiosRegisters, sizeof(BiosRegisters)); BiosRegisters.Eax = VBE_DDC; BiosRegisters.Ebx = VBE_DDC_BEGIN_SCL_SDA_CONTROL; BiosRegisters.Ecx = ChildIndex; VBEDeviceExtension->Int10Interface.Int10CallBios( VBEDeviceExtension->Int10Interface.Context, &BiosRegisters); if (VBE_GETRETURNCODE(BiosRegisters.Eax) != VBE_SUCCESS) return FALSE; /* * Read EDID information */ VBEDeviceExtension->CurrentChildIndex = ChildIndex; DDCControl.Size = sizeof(DDC_CONTROL); DDCControl.I2CCallbacks.WriteClockLine = VBEWriteClockLine; DDCControl.I2CCallbacks.WriteDataLine = VBEWriteDataLine; DDCControl.I2CCallbacks.ReadClockLine = VBEReadClockLine; DDCControl.I2CCallbacks.ReadDataLine = VBEReadDataLine; DDCControl.EdidSegment = 0; ret = VideoPortDDCMonitorHelper( HwDeviceExtension, &DDCControl, (PUCHAR)&Edid, MAX_SIZE_OF_EDID); /* * Disable I²C interface */ VideoPortZeroMemory(&BiosRegisters, sizeof(BiosRegisters)); BiosRegisters.Eax = VBE_DDC; BiosRegisters.Ebx = VBE_DDC_END_SCL_SDA_CONTROL; VBEDeviceExtension->Int10Interface.Int10CallBios( VBEDeviceExtension->Int10Interface.Context, &BiosRegisters); /* Ignore the possible error, as we did our best to prevent problems */ return ret; } static BOOLEAN VBEReadEdid( IN PVBE_DEVICE_EXTENSION VBEDeviceExtension, IN ULONG ChildIndex, OUT PVOID Edid) { INT10_BIOS_ARGUMENTS BiosRegisters; VideoPortDebugPrint(Trace, "VBEMP: VBEReadEdid() called\n"); /* * Check if DDC1/DDC2 is supported */ VideoPortZeroMemory(&BiosRegisters, sizeof(BiosRegisters)); BiosRegisters.Eax = VBE_DDC; VBEDeviceExtension->Int10Interface.Int10CallBios( VBEDeviceExtension->Int10Interface.Context, &BiosRegisters); if (VBE_GETRETURNCODE(BiosRegisters.Eax) != VBE_SUCCESS) return FALSE; if ((BiosRegisters.Ebx & 3) == 0) return FALSE; /* * Directly read EDID information */ VideoPortZeroMemory(&BiosRegisters, sizeof(BiosRegisters)); BiosRegisters.Eax = VBE_DDC; BiosRegisters.Ebx = VBE_DDC_READ_EDID; BiosRegisters.Ecx = ChildIndex; BiosRegisters.Edi = VBEDeviceExtension->TrampolineMemoryOffset; BiosRegisters.SegEs = VBEDeviceExtension->TrampolineMemorySegment; VBEDeviceExtension->Int10Interface.Int10CallBios( VBEDeviceExtension->Int10Interface.Context, &BiosRegisters); if (VBE_GETRETURNCODE(BiosRegisters.Eax) != VBE_SUCCESS) return FALSE; /* * Copy the EDID information to our buffer */ VBEDeviceExtension->Int10Interface.Int10ReadMemory( VBEDeviceExtension->Int10Interface.Context, VBEDeviceExtension->TrampolineMemorySegment, VBEDeviceExtension->TrampolineMemoryOffset, Edid, MAX_SIZE_OF_EDID); return TRUE; } VP_STATUS NTAPI VBEGetVideoChildDescriptor( IN PVOID HwDeviceExtension, IN PVIDEO_CHILD_ENUM_INFO ChildEnumInfo, OUT PVIDEO_CHILD_TYPE VideoChildType, OUT PUCHAR pChildDescriptor, OUT PULONG UId, OUT PULONG pUnused) { PVBE_DEVICE_EXTENSION VBEDeviceExtension = (PVBE_DEVICE_EXTENSION)HwDeviceExtension; ULONG ChildIndex; /* * We are called very early in device initialization, even before * VBEInitialize is called. So, our Int10 interface is not set. * Ignore this call, we will trigger another one later. */ if (VBEDeviceExtension->Int10Interface.Size == 0) return VIDEO_ENUM_NO_MORE_DEVICES; if (ChildEnumInfo->Size != sizeof(VIDEO_CHILD_ENUM_INFO)) { VideoPortDebugPrint(Error, "VBEMP: Wrong VIDEO_CHILD_ENUM_INFO structure size\n"); return VIDEO_ENUM_NO_MORE_DEVICES; } else if (ChildEnumInfo->ChildDescriptorSize < MAX_SIZE_OF_EDID) { VideoPortDebugPrint(Warn, "VBEMP: Too small buffer for EDID\n"); return VIDEO_ENUM_NO_MORE_DEVICES; } else if (ChildEnumInfo->ChildIndex == DISPLAY_ADAPTER_HW_ID) { *VideoChildType = VideoChip; *UId = 0; return VIDEO_ENUM_MORE_DEVICES; /* FIXME: not sure... */ } /* * Get Child ID */ if (ChildEnumInfo->ChildIndex != 0) ChildIndex = ChildEnumInfo->ChildIndex; else ChildIndex = ChildEnumInfo->ACPIHwId; VideoPortDebugPrint(Info, "VBEMP: ChildEnumInfo->ChildIndex %lu, ChildEnumInfo->ACPIHwId %lu => %lu\n", ChildEnumInfo->ChildIndex, ChildEnumInfo->ACPIHwId, ChildIndex); /* * Try to read EDID information using 2 different methods. */ if (VBEReadEdid(HwDeviceExtension, ChildIndex, pChildDescriptor)) { VideoPortDebugPrint(Info, "VBEMP: EDID information read directly\n"); } else if (VBEReadEdidUsingSCI(HwDeviceExtension, ChildIndex, pChildDescriptor)) { VideoPortDebugPrint(Info, "VBEMP: EDID information read using I²C\n"); } else if (ChildEnumInfo->ChildIndex == 1) { /* We must have 1 monitor, so just report it with no EDID information */ VideoPortDebugPrint(Info, "VBEMP: Reporting monitor with no EDID information\n"); } else { VideoPortDebugPrint(Warn, "VBEMP: Unable to read EDID information\n"); return VIDEO_ENUM_NO_MORE_DEVICES; } /* * Fill return data */ *VideoChildType = Monitor; if (ChildIndex == 0) { /* * This is the actual display adapter */ *UId = DISPLAY_ADAPTER_HW_ID; } else *UId = ChildIndex; *pUnused = 0; return VIDEO_ENUM_MORE_DEVICES; }