[WIN32SS]
[reactos.git] / reactos / win32ss / drivers / miniport / vga_new / vbemodes.c
diff --git a/reactos/win32ss/drivers/miniport/vga_new/vbemodes.c b/reactos/win32ss/drivers/miniport/vga_new/vbemodes.c
new file mode 100644 (file)
index 0000000..5e2fc4a
--- /dev/null
@@ -0,0 +1,449 @@
+/*
+ * PROJECT:         ReactOS VGA Miniport Driver
+ * LICENSE:         BSD - See COPYING.ARM in the top level directory
+ * FILE:            boot/drivers/video/miniport/vga/vbemodes.c
+ * PURPOSE:         Mode Initialization and Mode Set for VBE-compatible cards
+ * PROGRAMMERS:     ReactOS Portable Systems Group
+ */
+
+/* INCLUDES *******************************************************************/
+
+#include "vga.h"
+
+/* FUNCTIONS ******************************************************************/
+
+ULONG
+NTAPI
+RaiseToPower2Ulong(IN ULONG Value)
+{
+    ULONG SquaredResult = Value;
+    if ((Value - 1) & Value) for (SquaredResult = 1; (SquaredResult < Value) && (SquaredResult); SquaredResult *= 2);
+    return SquaredResult;
+}
+
+ULONG
+NTAPI
+RaiseToPower2(IN USHORT Value)
+{
+    ULONG SquaredResult = Value;
+    if ((Value - 1) & Value) for (SquaredResult = 1; (SquaredResult < Value) && (SquaredResult); SquaredResult *= 2);
+    return SquaredResult;
+}
+
+ULONG
+NTAPI
+VbeGetVideoMemoryBaseAddress(IN PHW_DEVICE_EXTENSION VgaExtension,
+                             IN PVIDEOMODE VgaMode)
+{
+    ULONG Length = 4 * 1024;
+    USHORT TrampolineMemorySegment, TrampolineMemoryOffset;
+    PVOID Context;
+    INT10_BIOS_ARGUMENTS BiosArguments;
+    PVBE_MODE_INFO VbeModeInfo;
+    ULONG BaseAddress;
+    VP_STATUS Status;
+
+    /* Need linear and INT10 interface */
+    if (!(VgaMode->fbType & VIDEO_MODE_BANKED)) return 0;
+    if (VgaExtension->Int10Interface.Size) return 0;
+
+    /* Allocate scratch area and context */
+    VbeModeInfo = VideoPortAllocatePool(VgaExtension, 1, sizeof(VBE_MODE_INFO), ' agV');
+    if (!VbeModeInfo) return 0;
+    Context = VgaExtension->Int10Interface.Context;
+    Status = VgaExtension->Int10Interface.Int10AllocateBuffer(Context,
+                                                              &TrampolineMemorySegment,
+                                                              &TrampolineMemoryOffset,
+                                                              &Length);
+    if (Status != NO_ERROR) return 0;
+
+    /* Ask VBE BIOS for mode info */
+    BiosArguments.Ecx = HIWORD(VgaMode->Mode);
+    BiosArguments.Edi = TrampolineMemorySegment;
+    BiosArguments.SegEs = TrampolineMemoryOffset;
+    BiosArguments.Eax = VBE_GET_MODE_INFORMATION;
+    Status = VgaExtension->Int10Interface.Int10CallBios(Context, &BiosArguments);
+    if (Status != NO_ERROR) return 0;
+    if (BiosArguments.Eax != VBE_SUCCESS) return 0;
+    Status = VgaExtension->Int10Interface.Int10ReadMemory(Context,
+                                                          TrampolineMemorySegment,
+                                                          TrampolineMemoryOffset,
+                                                          VbeModeInfo,
+                                                          sizeof(VBE_MODE_INFO));
+    if (Status != NO_ERROR) return 0;
+
+    /* Return phys address and cleanup */
+    BaseAddress = VbeModeInfo->PhysBasePtr;
+    VgaExtension->Int10Interface.Int10FreeBuffer(Context,
+                                                 TrampolineMemorySegment,
+                                                 TrampolineMemoryOffset);
+    VideoPortFreePool(VgaExtension, VbeModeInfo);
+    return BaseAddress;
+}
+
+VP_STATUS
+NTAPI
+VbeSetMode(IN PHW_DEVICE_EXTENSION VgaDeviceExtension,
+           IN PVIDEOMODE VgaMode,
+           OUT PULONG PhysPtrChange)
+{
+    VP_STATUS Status;
+    VIDEO_X86_BIOS_ARGUMENTS BiosArguments;
+    ULONG ModeIndex;
+    ULONG BaseAddress;
+
+    VideoPortZeroMemory(&BiosArguments, sizeof(BiosArguments));
+    ModeIndex = VgaMode->Mode;
+    BiosArguments.Eax = ModeIndex & 0x0000FFFF;
+    BiosArguments.Ebx = ModeIndex >> 16;
+    VideoPortDebugPrint(0, "Switching to %lx %lx\n", BiosArguments.Eax, BiosArguments.Ebx);
+    Status = VideoPortInt10(VgaDeviceExtension, &BiosArguments);
+    if (Status != NO_ERROR) return Status;
+
+    /* Check for VESA mode */
+    if (ModeIndex >> 16)
+    {
+        /* Mode set fail */
+        if (BiosArguments.Eax != VBE_SUCCESS) return ERROR_INVALID_PARAMETER;
+
+        /* Check current mode is desired mode */
+        BiosArguments.Eax = VBE_GET_CURRENT_VBE_MODE;
+        Status = VideoPortInt10(VgaDeviceExtension, &BiosArguments);
+        if ((Status == NO_ERROR) &&
+            (BiosArguments.Eax == VBE_SUCCESS) &&
+            ((BiosArguments.Ebx ^ (ModeIndex >> 16)) & VBE_MODE_BITS))
+        {
+            return ERROR_INVALID_PARAMETER;
+        }
+
+        /* Set logical scanline width if different from physical */
+        if (VgaMode->LogicalWidth != VgaMode->hres)
+        {
+            /* Check setting works after being set */
+            BiosArguments.Eax = VBE_SET_GET_LOGICAL_SCAN_LINE_LENGTH;
+            BiosArguments.Ecx = VgaMode->LogicalWidth;
+            BiosArguments.Ebx = 0;
+            Status = VideoPortInt10(VgaDeviceExtension, &BiosArguments);
+            if ((Status != NO_ERROR) ||
+                (BiosArguments.Eax != VBE_SUCCESS) ||
+                (BiosArguments.Ecx != VgaMode->LogicalWidth))
+            {
+                return ERROR_INVALID_PARAMETER;
+            }
+        }
+    }
+
+    /* Get VRAM address to update changes */
+    BaseAddress = VbeGetVideoMemoryBaseAddress(VgaDeviceExtension, VgaMode);
+    if ((BaseAddress) && (VgaMode->PhysBase != BaseAddress))
+    {
+        *PhysPtrChange = TRUE;
+        VgaMode->PhysBase = BaseAddress;
+    }
+
+    return NO_ERROR;
+}
+
+VOID
+NTAPI
+InitializeModeTable(IN PHW_DEVICE_EXTENSION VgaExtension)
+{
+    ULONG ModeCount = 0;
+    ULONG Length = 4 * 1024;
+    ULONG TotalMemory;
+    VP_STATUS Status;
+    INT10_BIOS_ARGUMENTS BiosArguments;
+    PVBE_INFO VbeInfo;
+    PVBE_MODE_INFO VbeModeInfo;
+    PVOID Context;
+    USHORT TrampolineMemorySegment;
+    USHORT TrampolineMemoryOffset;
+    ULONG VbeVersion;
+    ULONG NewModes = 0;
+    BOOLEAN FourBppModeFound = FALSE;
+    USHORT ModeResult;
+    USHORT Mode;
+    PUSHORT ThisMode;
+    BOOLEAN LinearAddressing;
+    ULONG Size, ScreenSize;
+    PVIDEOMODE VgaMode;
+    PVOID BaseAddress;
+    ULONG ScreenStride = 0;
+    PHYSICAL_ADDRESS PhysicalAddress;
+
+    /* Enable only default vga modes if no vesa */
+    VgaModeList = ModesVGA;
+    if (VideoPortIsNoVesa())
+    {
+        VgaExtension->Int10Interface.Size = 0;
+        VgaExtension->Int10Interface.Version = 0;
+        return;
+    }
+
+    /* Query INT10 interface */
+    VgaExtension->Int10Interface.Version = VIDEO_PORT_INT10_INTERFACE_VERSION_1;
+    VgaExtension->Int10Interface.Size = sizeof(VIDEO_PORT_INT10_INTERFACE);
+    if (VideoPortQueryServices(VgaExtension,
+                               VideoPortServicesInt10,
+                               (PINTERFACE)&VgaExtension->Int10Interface))
+    {
+        VgaExtension->Int10Interface.Size = 0;
+        VgaExtension->Int10Interface.Version = 0;
+    }
+
+    /* Add ref */
+    //VideoPortDebugPrint(0, "have int10 iface\n");
+    VgaExtension->Int10Interface.InterfaceReference(VgaExtension->Int10Interface.Context);
+    Context = VgaExtension->Int10Interface.Context;
+
+    /* Allocate scratch area and context */
+    Status = VgaExtension->Int10Interface.Int10AllocateBuffer(Context,
+                                                              &TrampolineMemorySegment,
+                                                              &TrampolineMemoryOffset,
+                                                              &Length);
+    if (Status != NO_ERROR) return;
+    VbeInfo = VideoPortAllocatePool(VgaExtension, 1, sizeof(VBE_INFO), ' agV');
+    VbeModeInfo = &VbeInfo->Modes;
+    if (!VbeInfo) return;
+
+    /* Init VBE data and write to card buffer */
+    //VideoPortDebugPrint(0, "have int10 data\n");
+    VbeInfo->ModeArray[128] = 0xFFFF;
+    VbeInfo->Info.Signature = VBE2_MAGIC;
+    Status = VgaExtension->Int10Interface.Int10WriteMemory(Context,
+                                                           TrampolineMemorySegment,
+                                                           TrampolineMemoryOffset,
+                                                           VbeInfo,
+                                                           512);
+    if (Status != NO_ERROR) return;
+
+    /* Get controller info */
+    BiosArguments.Edi = TrampolineMemoryOffset;
+    BiosArguments.SegEs = TrampolineMemorySegment;
+    BiosArguments.Eax = VBE_GET_CONTROLLER_INFORMATION;
+    Status = VgaExtension->Int10Interface.Int10CallBios(Context, &BiosArguments);
+    if (Status != NO_ERROR) return;
+    if (BiosArguments.Eax != VBE_SUCCESS) return;
+    Status = VgaExtension->Int10Interface.Int10ReadMemory(Context,
+                                                          TrampolineMemorySegment,
+                                                          TrampolineMemoryOffset,
+                                                          VbeInfo,
+                                                          512);
+    if (Status != NO_ERROR) return;
+
+    /* Check correct VBE BIOS */
+    //VideoPortDebugPrint(0, "have vbe data\n");
+    TotalMemory = VbeInfo->Info.TotalMemory << 16;
+    VbeVersion = VbeInfo->Info.Version;
+    VideoPortDebugPrint(0, "vbe version %lx memory %lx\n", VbeVersion, TotalMemory);
+    if (!ValidateVbeInfo(VgaExtension, VbeInfo)) return;
+
+    /* Read modes */
+    //VideoPortDebugPrint(0, "read modes from %p\n", VbeInfo->Info.VideoModePtr);
+    Status = VgaExtension->Int10Interface.Int10ReadMemory(Context,
+                                                          HIWORD(VbeInfo->Info.VideoModePtr),
+                                                          LOWORD(VbeInfo->Info.VideoModePtr),
+                                                          VbeInfo->ModeArray,
+                                                          128 * sizeof(USHORT));
+    if (Status != NO_ERROR) return;
+    //VideoPortDebugPrint(0, "Read modes at: %p\n", VbeInfo->ModeArray);
+
+    /* Count modes, check for new 4bpp SVGA modes */
+    ThisMode = VbeInfo->ModeArray;
+    ModeResult = VbeInfo->ModeArray[0];
+    while (ModeResult != 0xFFFF)
+    {
+        Mode = ModeResult & 0x1FF;
+        //VideoPortDebugPrint(0, "Mode found: %lx\n", Mode);
+        if ((Mode == 0x102) || (Mode == 0x6A)) FourBppModeFound = TRUE;
+        ModeResult = *++ThisMode;
+        NewModes++;
+    }
+
+    /* Remove the built-in mode if not supported by card and check max modes */
+    if (!FourBppModeFound) --NumVideoModes;
+    if ((NewModes >= 128) && (NumVideoModes > 8)) goto Cleanup;
+
+    /* Switch to new SVGA mode list, copy VGA modes */
+    VgaModeList = VideoPortAllocatePool(VgaExtension, 1, (NewModes + NumVideoModes) * sizeof(VIDEOMODE), ' agV');
+    if (!VgaModeList) goto Cleanup;
+    VideoPortMoveMemory(VgaModeList, ModesVGA, NumVideoModes * sizeof(VIDEOMODE));
+
+    /* Apply fixup for Intel Brookdale */
+    if (g_bIntelBrookdaleBIOS)
+    {
+        VideoPortDebugPrint(0, "Intel Brookdale-G Video BIOS Not Support!\n");
+        while (TRUE);
+    }
+
+    /* Scan SVGA modes */
+//    VideoPortDebugPrint(0, "Static modes: %d\n", NumVideoModes);
+    VgaMode = &VgaModeList[NumVideoModes];
+    ThisMode = VbeInfo->ModeArray;
+    //VideoPortDebugPrint(0, "new modes: %d\n", NewModes);
+    while (NewModes--)
+    {
+        /* Get info on mode */
+        BiosArguments.Eax = VBE_GET_MODE_INFORMATION;
+        BiosArguments.Ecx = *ThisMode;
+        BiosArguments.Edi = TrampolineMemoryOffset;
+        BiosArguments.SegEs = TrampolineMemorySegment;
+        Status = VgaExtension->Int10Interface.Int10CallBios(Context, &BiosArguments);
+        if (Status != NO_ERROR) goto Next;
+        if (BiosArguments.Eax != VBE_SUCCESS) goto Next;
+        Status = VgaExtension->Int10Interface.Int10ReadMemory(Context,
+                                                              TrampolineMemorySegment,
+                                                              TrampolineMemoryOffset,
+                                                              VbeModeInfo,
+                                                              256);
+        if (Status != NO_ERROR) goto Next;
+
+        /* Parse graphics modes only if linear framebuffer support */
+        //VideoPortDebugPrint(0, "attr: %lx\n", VbeModeInfo->ModeAttributes);
+        if (!(VbeModeInfo->ModeAttributes & (VBE_MODEATTR_VALID |
+                                             VBE_MODEATTR_GRAPHICS))) goto Next;
+        LinearAddressing = ((VbeVersion >= 0x200) &&
+                            (VbeModeInfo->PhysBasePtr) &&
+                            (VbeModeInfo->ModeAttributes & VBE_MODEATTR_LINEAR)) ?
+                            TRUE : FALSE;
+
+        /* Check SVGA modes if 8bpp or higher */
+        //VideoPortDebugPrint(0, "PhysBase: %lx\n", VbeModeInfo->PhysBasePtr);
+        if ((VbeModeInfo->XResolution >= 640) &&
+            (VbeModeInfo->YResolution >= 480) &&
+            (VbeModeInfo->NumberOfPlanes >= 1) &&
+            (VbeModeInfo->BitsPerPixel >= 8))
+        {
+            /* Copy VGA mode info */
+            VideoPortZeroMemory(VgaMode, sizeof(VIDEOMODE));
+            VgaMode->numPlanes = VbeModeInfo->NumberOfPlanes;
+            VgaMode->hres = VbeModeInfo->XResolution;
+            VgaMode->vres = VbeModeInfo->YResolution;
+            VgaMode->Frequency = 1;
+            VgaMode->Mode = (*ThisMode << 16) | VBE_SET_VBE_MODE;
+            VgaMode->Granularity = VbeModeInfo->WinGranularity << 10;
+            //VideoPortDebugPrint(0, "Mode %lx (Granularity %d)\n", VgaMode->Mode, VgaMode->Granularity);
+
+            /* Set flags */
+            if (VbeModeInfo->ModeAttributes & VBE_MODEATTR_COLOR) VgaMode->fbType |= VIDEO_MODE_COLOR;
+            if (VbeModeInfo->ModeAttributes & VBE_MODEATTR_GRAPHICS) VgaMode->fbType |= VIDEO_MODE_GRAPHICS;
+            if (VbeModeInfo->ModeAttributes & VBE_MODEATTR_NON_VGA) VgaMode->NonVgaMode = TRUE;
+
+            /* If no char data, say 80x25 */
+            VgaMode->col = VbeModeInfo->XCharSize ? VbeModeInfo->XResolution / VbeModeInfo->XCharSize : 80;
+            VgaMode->row = VbeModeInfo->YCharSize ? VbeModeInfo->YResolution / VbeModeInfo->YCharSize : 25;
+            //VideoPortDebugPrint(0, "%d by %d rows\n", VgaMode->Columns, VgaMode->Rows);
+
+            /* Check RGB555 (15bpp only) */
+            VgaMode->bitsPerPlane = VbeModeInfo->BitsPerPixel / VbeModeInfo->NumberOfPlanes;
+            if ((VgaMode->bitsPerPlane == 16) && (VbeModeInfo->GreenMaskSize == 5)) VgaMode->bitsPerPlane = 15;
+            //VideoPortDebugPrint(0, "BPP: %d\n", VgaMode->BitsPerPlane);
+
+            /* Do linear or banked frame buffers */
+            VgaMode->FrameBufferBase = 0;
+            if (!LinearAddressing)
+            {
+                /* Read the screen stride (scanline size) */
+                ScreenStride = RaiseToPower2(VbeModeInfo->BytesPerScanLine);
+                VgaMode->wbytes = ScreenStride;
+                //VideoPortDebugPrint(0, "ScanLines: %lx Stride: %lx\n", VbeModeInfo->BytesPerScanLine, VgaMode->Stride);
+
+                /* Size of frame buffer is Height X ScanLine, align to bank/page size */
+                ScreenSize = VgaMode->hres * ScreenStride;
+                //VideoPortDebugPrint(0, "Size: %lx\n", ScreenSize);
+                Size = (ScreenSize + ((64 * 1024) - 1)) & ((64 * 1024) - 1);
+                //VideoPortDebugPrint(0, "Size: %lx\n", ScreenSize);
+                if (Size > TotalMemory) Size = (Size + ((4 * 1024) - 1)) & ((4 * 1024) - 1);
+                //VideoPortDebugPrint(0, "Size: %lx\n", ScreenSize);
+
+                /* Banked VGA at 0xA0000 (64K) */
+                //VideoPortDebugPrint(0, "Final size: %lx\n", Size);
+                VgaMode->fbType |= VIDEO_MODE_BANKED;
+                VgaMode->sbytes = Size;
+                VgaMode->PhysSize = 64 * 1024;
+                VgaMode->FrameBufferSize = 64 * 1024;
+                VgaMode->NoBankSwitch = TRUE;
+                VgaMode->PhysBase = 0xA0000;
+                VgaMode->LogicalWidth = RaiseToPower2(VgaMode->hres);
+            }
+            else
+            {
+                /* VBE 3.00+ has specific field, read legacy field if not */
+                //VideoPortDebugPrint(0, "LINEAR MODE!!!\n");
+                ScreenStride = (VbeVersion >= 0x300) ? VbeModeInfo->LinBytesPerScanLine : 0;
+                if (!ScreenStride) ScreenStride = VbeModeInfo->BytesPerScanLine;
+                VgaMode->wbytes = ScreenStride;
+                //VideoPortDebugPrint(0, "ScanLines: %lx Stride: %lx\n", VbeModeInfo->BytesPerScanLine, VgaMode->Stride);
+
+                /* Size of frame buffer is Height X ScanLine, align to page size */
+                ScreenSize = VgaMode->hres * LOWORD(VgaMode->wbytes);
+                //VideoPortDebugPrint(0, "Size: %lx\n", ScreenSize);
+                Size = RaiseToPower2Ulong(ScreenSize);
+                //VideoPortDebugPrint(0, "Size: %lx\n", ScreenSize);
+                if (Size > TotalMemory) Size = (Size + ((4 * 1024) - 1)) & ((4 * 1024) - 1);
+                //VideoPortDebugPrint(0, "Size: %lx\n", ScreenSize);
+
+                /* Linear VGA must read settings from VBE */
+                VgaMode->fbType |= VIDEO_MODE_LINEAR;
+                VgaMode->sbytes = Size;
+                VgaMode->PhysSize = Size;
+                VgaMode->FrameBufferSize = Size;
+                VgaMode->NoBankSwitch = FALSE;
+                VgaMode->PhysBase = VbeModeInfo->PhysBasePtr;
+                VgaMode->LogicalWidth = VgaMode->hres;
+
+                /* Make VBE_SET_VBE_MODE command use Linear Framebuffer Select */
+                VgaMode->Mode |= (VBE_MODE_LINEAR_FRAMEBUFFER << 16);
+            }
+
+            /* Override bank switch if not support by card */
+            if (VbeModeInfo->ModeAttributes & VBE_MODEATTR_NO_BANK_SWITCH) VgaMode->NoBankSwitch = TRUE;
+
+            /* Next */
+            if (ScreenSize <= TotalMemory)
+            {
+                VgaMode++;
+                ModeCount++;
+            }
+        }
+Next:
+        /* Next */
+        ThisMode++;
+    }
+
+    /* Check if last mode was color to do test */
+    VideoPortDebugPrint(0, "mode scan complete. Total modes: %d\n", ModeCount);
+    if (--VgaMode->fbType & VIDEO_MODE_COLOR)
+    {
+        /* Try map physical buffer and free if worked */
+        PhysicalAddress.QuadPart = VgaMode->PhysBase;
+        BaseAddress = VideoPortGetDeviceBase(VgaExtension, PhysicalAddress, 4 * 1024, FALSE);
+        if (BaseAddress)
+        {
+            VideoPortFreeDeviceBase(VgaExtension, BaseAddress);
+        }
+        else
+        {
+            /* Not work, so throw out VBE data */
+            ModeCount = 0;
+        }
+    }
+
+    /* Cleanup sucess path */
+    VideoPortFreePool(VgaExtension, VbeInfo);
+    VgaExtension->Int10Interface.Int10FreeBuffer(Context,
+                                                 TrampolineMemorySegment,
+                                                 TrampolineMemoryOffset);
+    NumVideoModes += ModeCount;
+    return;
+
+Cleanup:
+    /* Cleanup failure path, reset standard VGA and free memory */
+    VgaModeList = ModesVGA;
+    VideoPortFreePool(VgaExtension, VbeInfo);
+    VgaExtension->Int10Interface.Int10FreeBuffer(Context,
+                                                 TrampolineMemorySegment,
+                                                 TrampolineMemoryOffset);
+}
+
+/* EOF */