1 /*
2  * PROJECT:         ReactOS VGA Miniport Driver
3  * LICENSE:         BSD - See COPYING.ARM in the top level directory
4  * FILE:            win32ss/drivers/miniport/vga_new/vbemodes.c
5  * PURPOSE:         Mode Initialization and Mode Set for VBE-compatible cards
6  * PROGRAMMERS:     ReactOS Portable Systems Group
7  */
8 
9 /* INCLUDES *******************************************************************/
10 
11 #include "vga.h"
12 
13 /* FUNCTIONS ******************************************************************/
14 
15 ULONG
16 NTAPI
17 RaiseToPower2Ulong(IN ULONG Value)
18 {
19     ULONG SquaredResult = Value;
20     if ((Value - 1) & Value) for (SquaredResult = 1; (SquaredResult < Value) && (SquaredResult); SquaredResult *= 2);
21     return SquaredResult;
22 }
23 
24 ULONG
25 NTAPI
26 RaiseToPower2(IN USHORT Value)
27 {
28     ULONG SquaredResult = Value;
29     if ((Value - 1) & Value) for (SquaredResult = 1; (SquaredResult < Value) && (SquaredResult); SquaredResult *= 2);
30     return SquaredResult;
31 }
32 
33 ULONG
34 NTAPI
35 VbeGetVideoMemoryBaseAddress(IN PHW_DEVICE_EXTENSION VgaExtension,
36                              IN PVIDEOMODE VgaMode)
37 {
38     ULONG Length = 4 * 1024;
39     USHORT TrampolineMemorySegment, TrampolineMemoryOffset;
40     PVOID Context;
41     INT10_BIOS_ARGUMENTS BiosArguments;
42     PVBE_MODE_INFO VbeModeInfo;
43     ULONG BaseAddress;
44     VP_STATUS Status;
45 
46     /* Need linear and INT10 interface */
47     if (!(VgaMode->fbType & VIDEO_MODE_BANKED)) return 0;
48     if (VgaExtension->Int10Interface.Size) return 0;
49 
50     /* Allocate scratch area and context */
51     VbeModeInfo = VideoPortAllocatePool(VgaExtension, 1, sizeof(VBE_MODE_INFO), ' agV');
52     if (!VbeModeInfo) return 0;
53     Context = VgaExtension->Int10Interface.Context;
54     Status = VgaExtension->Int10Interface.Int10AllocateBuffer(Context,
55                                                               &TrampolineMemorySegment,
56                                                               &TrampolineMemoryOffset,
57                                                               &Length);
58     if (Status != NO_ERROR) return 0;
59 
60     /* Ask VBE BIOS for mode info */
61     VideoPortZeroMemory(&BiosArguments, sizeof(BiosArguments));
62     BiosArguments.Ecx = HIWORD(VgaMode->Mode);
63     BiosArguments.Edi = TrampolineMemorySegment;
64     BiosArguments.SegEs = TrampolineMemoryOffset;
65     BiosArguments.Eax = VBE_GET_MODE_INFORMATION;
66     Status = VgaExtension->Int10Interface.Int10CallBios(Context, &BiosArguments);
67     if (Status != NO_ERROR) return 0;
68     if (VBE_GETRETURNCODE(BiosArguments.Eax) != VBE_SUCCESS)
69         return 0;
70     Status = VgaExtension->Int10Interface.Int10ReadMemory(Context,
71                                                           TrampolineMemorySegment,
72                                                           TrampolineMemoryOffset,
73                                                           VbeModeInfo,
74                                                           sizeof(VBE_MODE_INFO));
75     if (Status != NO_ERROR) return 0;
76 
77     /* Return phys address and cleanup */
78     BaseAddress = VbeModeInfo->PhysBasePtr;
79     VgaExtension->Int10Interface.Int10FreeBuffer(Context,
80                                                  TrampolineMemorySegment,
81                                                  TrampolineMemoryOffset);
82     VideoPortFreePool(VgaExtension, VbeModeInfo);
83     return BaseAddress;
84 }
85 
86 VP_STATUS
87 NTAPI
88 VbeSetMode(IN PHW_DEVICE_EXTENSION VgaDeviceExtension,
89            IN PVIDEOMODE VgaMode,
90            OUT PULONG PhysPtrChange)
91 {
92     VP_STATUS Status;
93     VIDEO_X86_BIOS_ARGUMENTS BiosArguments;
94     ULONG ModeIndex;
95     ULONG BaseAddress;
96 
97     VideoPortZeroMemory(&BiosArguments, sizeof(BiosArguments));
98     ModeIndex = VgaMode->Mode;
99     BiosArguments.Eax = VBE_SET_VBE_MODE;
100     BiosArguments.Ebx = HIWORD(ModeIndex);
101     VideoDebugPrint((0, "Switching to %lx %lx\n", BiosArguments.Eax, BiosArguments.Ebx));
102     Status = VideoPortInt10(VgaDeviceExtension, &BiosArguments);
103     if (Status != NO_ERROR) return Status;
104     if(VBE_GETRETURNCODE(BiosArguments.Eax) != VBE_SUCCESS)
105     {
106         VideoDebugPrint((0, "Changing VBE mode failed, Eax %lx", BiosArguments.Eax));
107         return ERROR_INVALID_PARAMETER;
108     }
109 
110     /* Check for VESA mode */
111     if (ModeIndex >> 16)
112     {
113         /* Mode set fail */
114         if (VBE_GETRETURNCODE(BiosArguments.Eax) != VBE_SUCCESS)
115             return ERROR_INVALID_PARAMETER;
116 
117         /* Check current mode is desired mode */
118         VideoPortZeroMemory(&BiosArguments, sizeof(BiosArguments));
119         BiosArguments.Eax = VBE_GET_CURRENT_VBE_MODE;
120         Status = VideoPortInt10(VgaDeviceExtension, &BiosArguments);
121         if ((Status == NO_ERROR) &&
122             (VBE_GETRETURNCODE(BiosArguments.Eax) == VBE_SUCCESS) &&
123             ((BiosArguments.Ebx ^ (ModeIndex >> 16)) & VBE_MODE_BITS))
124         {
125             return ERROR_INVALID_PARAMETER;
126         }
127 
128         /* Set logical scanline width if different from physical */
129         if (VgaMode->LogicalWidth != VgaMode->hres)
130         {
131             /* Check setting works after being set */
132             VideoPortZeroMemory(&BiosArguments, sizeof(BiosArguments));
133             BiosArguments.Eax = VBE_SET_GET_LOGICAL_SCAN_LINE_LENGTH;
134             BiosArguments.Ecx = VgaMode->LogicalWidth;
135             BiosArguments.Ebx = 0;
136             Status = VideoPortInt10(VgaDeviceExtension, &BiosArguments);
137             if ((Status != NO_ERROR) ||
138                 (VBE_GETRETURNCODE(BiosArguments.Eax) != VBE_SUCCESS) ||
139                 (BiosArguments.Ecx != VgaMode->LogicalWidth))
140             {
141                 return ERROR_INVALID_PARAMETER;
142             }
143         }
144     }
145 
146     /* Get VRAM address to update changes */
147     BaseAddress = VbeGetVideoMemoryBaseAddress(VgaDeviceExtension, VgaMode);
148     if ((BaseAddress) && (VgaMode->PhysBase != BaseAddress))
149     {
150         *PhysPtrChange = TRUE;
151         VgaMode->PhysBase = BaseAddress;
152     }
153 
154     return NO_ERROR;
155 }
156 
157 VOID
158 NTAPI
159 InitializeModeTable(IN PHW_DEVICE_EXTENSION VgaExtension)
160 {
161     ULONG ModeCount = 0;
162     ULONG Length = 4 * 1024;
163     ULONG TotalMemory;
164     VP_STATUS Status;
165     INT10_BIOS_ARGUMENTS BiosArguments;
166     PVBE_INFO VbeInfo;
167     PVBE_MODE_INFO VbeModeInfo;
168     PVOID Context;
169     USHORT TrampolineMemorySegment;
170     USHORT TrampolineMemoryOffset;
171     ULONG VbeVersion;
172     ULONG NewModes = 0;
173     BOOLEAN FourBppModeFound = FALSE;
174     USHORT ModeResult;
175     USHORT Mode;
176     PUSHORT ThisMode;
177     BOOLEAN LinearAddressing;
178     ULONG Size, ScreenSize;
179     PVIDEOMODE VgaMode;
180     PVOID BaseAddress;
181     ULONG ScreenStride;
182     PHYSICAL_ADDRESS PhysicalAddress;
183 
184     /* Enable only default vga modes if no vesa */
185     VgaModeList = ModesVGA;
186     if (VideoPortIsNoVesa())
187     {
188         VgaExtension->Int10Interface.Size = 0;
189         VgaExtension->Int10Interface.Version = 0;
190         return;
191     }
192 
193     /* Query INT10 interface */
194     VgaExtension->Int10Interface.Version = VIDEO_PORT_INT10_INTERFACE_VERSION_1;
195     VgaExtension->Int10Interface.Size = sizeof(VIDEO_PORT_INT10_INTERFACE);
196     if (VideoPortQueryServices(VgaExtension,
197                                VideoPortServicesInt10,
198                                (PINTERFACE)&VgaExtension->Int10Interface))
199     {
200         VgaExtension->Int10Interface.Size = 0;
201         VgaExtension->Int10Interface.Version = 0;
202     }
203 
204     /* Add ref */
205     VideoDebugPrint((0, "have int10 iface\n"));
206     VgaExtension->Int10Interface.InterfaceReference(VgaExtension->Int10Interface.Context);
207     Context = VgaExtension->Int10Interface.Context;
208 
209     /* Allocate scratch area and context */
210     Status = VgaExtension->Int10Interface.Int10AllocateBuffer(Context,
211                                                               &TrampolineMemorySegment,
212                                                               &TrampolineMemoryOffset,
213                                                               &Length);
214     if (Status != NO_ERROR) return;
215     VbeInfo = VideoPortAllocatePool(VgaExtension, 1, sizeof(VBE_INFO), ' agV');
216     if (!VbeInfo) return;
217 
218     VbeModeInfo = &VbeInfo->Modes;
219 
220     /* Init VBE data and write to card buffer */
221     VideoDebugPrint((0, "have int10 data\n"));
222     VbeInfo->ModeArray[128] = 0xFFFF;
223     VbeInfo->Info.Signature = VBE2_MAGIC;
224     Status = VgaExtension->Int10Interface.Int10WriteMemory(Context,
225                                                            TrampolineMemorySegment,
226                                                            TrampolineMemoryOffset,
227                                                            &VbeInfo->Info.Signature,
228                                                            4);
229     if (Status != NO_ERROR) return;
230 
231     /* Get controller info */
232     VideoPortZeroMemory(&BiosArguments, sizeof(BiosArguments));
233     BiosArguments.Edi = TrampolineMemoryOffset;
234     BiosArguments.SegEs = TrampolineMemorySegment;
235     BiosArguments.Eax = VBE_GET_CONTROLLER_INFORMATION;
236     Status = VgaExtension->Int10Interface.Int10CallBios(Context, &BiosArguments);
237     if (Status != NO_ERROR) return;
238     if(VBE_GETRETURNCODE(BiosArguments.Eax) != VBE_SUCCESS)
239     {
240         VideoDebugPrint((0, "BiosArguments.Eax %lx\n", BiosArguments.Eax));
241         return;
242     }
243     Status = VgaExtension->Int10Interface.Int10ReadMemory(Context,
244                                                           TrampolineMemorySegment,
245                                                           TrampolineMemoryOffset,
246                                                           VbeInfo,
247                                                           512);
248     if (Status != NO_ERROR) return;
249 
250     /* Check correct VBE BIOS */
251     VideoDebugPrint((0, "have vbe data\n"));
252     TotalMemory = VbeInfo->Info.TotalMemory << 16;
253     VbeVersion = VbeInfo->Info.Version;
254     VideoDebugPrint((0, "vbe version %lx memory %lx\n", VbeVersion, TotalMemory));
255     if (!ValidateVbeInfo(VgaExtension, VbeInfo)) return;
256 
257     /* Read modes */
258     VideoDebugPrint((0, "read modes from %p\n", VbeInfo->Info.VideoModePtr));
259     Status = VgaExtension->Int10Interface.Int10ReadMemory(Context,
260                                                           HIWORD(VbeInfo->Info.VideoModePtr),
261                                                           LOWORD(VbeInfo->Info.VideoModePtr),
262                                                           VbeInfo->ModeArray,
263                                                           128 * sizeof(USHORT));
264     if (Status != NO_ERROR) return;
265     VideoDebugPrint((0, "Read modes at: %p\n", VbeInfo->ModeArray));
266 
267     /* Count modes, check for new 4bpp SVGA modes */
268     ThisMode = VbeInfo->ModeArray;
269     ModeResult = VbeInfo->ModeArray[0];
270     while (ModeResult != 0xFFFF)
271     {
272         Mode = ModeResult & 0x1FF;
273         VideoDebugPrint((0, "Mode found: %lx\n", Mode));
274         if ((Mode == 0x102) || (Mode == 0x6A)) FourBppModeFound = TRUE;
275         ModeResult = *++ThisMode;
276         NewModes++;
277     }
278 
279     /* Remove the built-in mode if not supported by card and check max modes */
280     if (!FourBppModeFound) --NumVideoModes;
281     if ((NewModes >= 128) && (NumVideoModes > 8)) goto Cleanup;
282 
283     /* Switch to new SVGA mode list, copy VGA modes */
284     VgaModeList = VideoPortAllocatePool(VgaExtension, 1, (NewModes + NumVideoModes) * sizeof(VIDEOMODE), ' agV');
285     if (!VgaModeList) goto Cleanup;
286     VideoPortMoveMemory(VgaModeList, ModesVGA, NumVideoModes * sizeof(VIDEOMODE));
287 
288     /* Apply fixup for Intel Brookdale */
289     if (g_bIntelBrookdaleBIOS)
290     {
291         VideoDebugPrint((0, "Intel Brookdale-G Video BIOS Not Support!\n"));
292         while (TRUE);
293     }
294 
295     /* Scan SVGA modes */
296     VideoDebugPrint((0, "Static modes: %d\n", NumVideoModes));
297     VgaMode = &VgaModeList[NumVideoModes];
298     ThisMode = VbeInfo->ModeArray;
299     VideoDebugPrint((0, "new modes: %d\n", NewModes));
300     while (NewModes--)
301     {
302         /* Get info on mode */
303         VideoDebugPrint((0, "Getting info of mode %lx.\n", *ThisMode));
304         VideoPortZeroMemory(&BiosArguments, sizeof(BiosArguments));
305         BiosArguments.Eax = VBE_GET_MODE_INFORMATION;
306         BiosArguments.Ecx = *ThisMode;
307         BiosArguments.Edi = TrampolineMemoryOffset;
308         BiosArguments.SegEs = TrampolineMemorySegment;
309         Status = VgaExtension->Int10Interface.Int10CallBios(Context, &BiosArguments);
310         if (Status != NO_ERROR) goto Next;
311         if (VBE_GETRETURNCODE(BiosArguments.Eax) != VBE_SUCCESS) goto Next;
312         Status = VgaExtension->Int10Interface.Int10ReadMemory(Context,
313                                                               TrampolineMemorySegment,
314                                                               TrampolineMemoryOffset,
315                                                               VbeModeInfo,
316                                                               256);
317         if (Status != NO_ERROR) goto Next;
318 
319         /* Parse graphics modes only if linear framebuffer support */
320         VideoDebugPrint((0, "attr: %lx\n", VbeModeInfo->ModeAttributes));
321         if (!(VbeModeInfo->ModeAttributes & (VBE_MODEATTR_VALID |
322                                              VBE_MODEATTR_GRAPHICS))) goto Next;
323         LinearAddressing = ((VbeVersion >= 0x200) &&
324                             (VbeModeInfo->PhysBasePtr) &&
325                             (VbeModeInfo->ModeAttributes & VBE_MODEATTR_LINEAR)) ?
326                             TRUE : FALSE;
327 
328         /* Check SVGA modes if 8bpp or higher */
329         VideoDebugPrint((0, "PhysBase: %lx\n", VbeModeInfo->PhysBasePtr));
330         if ((VbeModeInfo->XResolution >= 640) &&
331             (VbeModeInfo->YResolution >= 480) &&
332             (VbeModeInfo->NumberOfPlanes >= 1) &&
333             (VbeModeInfo->BitsPerPixel >= 8))
334         {
335             /* Copy VGA mode info */
336             VideoPortZeroMemory(VgaMode, sizeof(VIDEOMODE));
337             VgaMode->numPlanes = VbeModeInfo->NumberOfPlanes;
338             VgaMode->hres = VbeModeInfo->XResolution;
339             VgaMode->vres = VbeModeInfo->YResolution;
340             VgaMode->Frequency = 1;
341             VgaMode->Mode = (*ThisMode << 16) | VBE_SET_VBE_MODE;
342             VgaMode->Granularity = VbeModeInfo->WinGranularity << 10;
343             VideoDebugPrint((0, "Mode %lx (Granularity %d)\n", VgaMode->Mode, VgaMode->Granularity));
344 
345             /* Set flags */
346             if (VbeModeInfo->ModeAttributes & VBE_MODEATTR_COLOR) VgaMode->fbType |= VIDEO_MODE_COLOR;
347             if (VbeModeInfo->ModeAttributes & VBE_MODEATTR_GRAPHICS) VgaMode->fbType |= VIDEO_MODE_GRAPHICS;
348             if (VbeModeInfo->ModeAttributes & VBE_MODEATTR_NON_VGA) VgaMode->NonVgaMode = TRUE;
349 
350             /* If no char data, say 80x25 */
351             VgaMode->col = VbeModeInfo->XCharSize ? VbeModeInfo->XResolution / VbeModeInfo->XCharSize : 80;
352             VgaMode->row = VbeModeInfo->YCharSize ? VbeModeInfo->YResolution / VbeModeInfo->YCharSize : 25;
353             VideoDebugPrint((0, "%d by %d rows\n", VgaMode->col, VgaMode->row));
354 
355             /* Check RGB555 (15bpp only) */
356             VgaMode->bitsPerPlane = VbeModeInfo->BitsPerPixel / VbeModeInfo->NumberOfPlanes;
357             if ((VgaMode->bitsPerPlane == 16) && (VbeModeInfo->GreenMaskSize == 5)) VgaMode->bitsPerPlane = 15;
358             VideoDebugPrint((0, "BPP: %d\n", VgaMode->bitsPerPlane));
359 
360             /* Do linear or banked frame buffers */
361             VgaMode->FrameBufferBase = 0;
362             if (!LinearAddressing)
363             {
364                 /* Read the screen stride (scanline size) */
365                 ScreenStride = RaiseToPower2(VbeModeInfo->BytesPerScanLine);
366                 //ASSERT(ScreenStride <= MAX_USHORT);
367                 VgaMode->wbytes = (USHORT)ScreenStride;
368                 VideoDebugPrint((0, "ScanLines: %lx Stride: %lx\n", VbeModeInfo->BytesPerScanLine, VgaMode->wbytes));
369 
370                 /* Size of frame buffer is Height X ScanLine, align to bank/page size */
371                 ScreenSize = VgaMode->hres * ScreenStride;
372                 VideoDebugPrint((0, "Size: %lx\n", ScreenSize));
373                 Size = (ScreenSize + ((64 * 1024) - 1)) & ((64 * 1024) - 1);
374                 VideoDebugPrint((0, "Size: %lx\n", ScreenSize));
375                 if (Size > TotalMemory) Size = (Size + ((4 * 1024) - 1)) & ((4 * 1024) - 1);
376                 VideoDebugPrint((0, "Size: %lx\n", ScreenSize));
377 
378                 /* Banked VGA at 0xA0000 (64K) */
379                 VideoDebugPrint((0, "Final size: %lx\n", Size));
380                 VgaMode->fbType |= VIDEO_MODE_BANKED;
381                 VgaMode->sbytes = Size;
382                 VgaMode->PhysSize = 64 * 1024;
383                 VgaMode->FrameBufferSize = 64 * 1024;
384                 VgaMode->NoBankSwitch = TRUE;
385                 VgaMode->PhysBase = 0xA0000;
386                 VgaMode->LogicalWidth = RaiseToPower2(VgaMode->hres);
387             }
388             else
389             {
390                 /* VBE 3.00+ has specific field, read legacy field if not */
391                 VideoDebugPrint((0, "LINEAR MODE!!!\n"));
392                 ScreenStride = (VbeVersion >= 0x300) ? VbeModeInfo->LinBytesPerScanLine : 0;
393                 if (!ScreenStride) ScreenStride = VbeModeInfo->BytesPerScanLine;
394                 //ASSERT(ScreenStride <= MAX_USHORT);
395                 VgaMode->wbytes = (USHORT)ScreenStride;
396                 VideoDebugPrint((0, "ScanLines: %lx Stride: %lx\n", VbeModeInfo->BytesPerScanLine, VgaMode->wbytes));
397 
398                 /* Size of frame buffer is Height X ScanLine, align to page size */
399                 ScreenSize = VgaMode->hres * LOWORD(VgaMode->wbytes);
400                 VideoDebugPrint((0, "Size: %lx\n", ScreenSize));
401                 Size = RaiseToPower2Ulong(ScreenSize);
402                 VideoDebugPrint((0, "Size: %lx\n", ScreenSize));
403                 if (Size > TotalMemory) Size = (Size + ((4 * 1024) - 1)) & ((4 * 1024) - 1);
404                 VideoDebugPrint((0, "Size: %lx\n", ScreenSize));
405 
406                 /* Linear VGA must read settings from VBE */
407                 VgaMode->fbType |= VIDEO_MODE_LINEAR;
408                 VgaMode->sbytes = Size;
409                 VgaMode->PhysSize = Size;
410                 VgaMode->FrameBufferSize = Size;
411                 VgaMode->NoBankSwitch = FALSE;
412                 VgaMode->PhysBase = VbeModeInfo->PhysBasePtr;
413                 VgaMode->LogicalWidth = VgaMode->hres;
414 
415                 /* Make VBE_SET_VBE_MODE command use Linear Framebuffer Select */
416                 VgaMode->Mode |= (VBE_MODE_LINEAR_FRAMEBUFFER << 16);
417             }
418 
419             /* Override bank switch if not support by card */
420             if (VbeModeInfo->ModeAttributes & VBE_MODEATTR_NO_BANK_SWITCH) VgaMode->NoBankSwitch = TRUE;
421 
422             /* Next */
423             if (ScreenSize <= TotalMemory)
424             {
425                 VgaMode++;
426                 ModeCount++;
427             }
428         }
429 Next:
430         /* Next */
431         ThisMode++;
432     }
433 
434     /* Check if last mode was color to do test */
435     VideoDebugPrint((0, "mode scan complete. Total modes: %d\n", ModeCount));
436     if (--VgaMode->fbType & VIDEO_MODE_COLOR)
437     {
438         /* Try map physical buffer and free if worked */
439         PhysicalAddress.QuadPart = VgaMode->PhysBase;
440         BaseAddress = VideoPortGetDeviceBase(VgaExtension, PhysicalAddress, 4 * 1024, FALSE);
441         if (BaseAddress)
442         {
443             VideoPortFreeDeviceBase(VgaExtension, BaseAddress);
444         }
445         else
446         {
447             /* Not work, so throw out VBE data */
448             ModeCount = 0;
449         }
450     }
451 
452     /* Cleanup sucess path */
453     VideoPortFreePool(VgaExtension, VbeInfo);
454     VgaExtension->Int10Interface.Int10FreeBuffer(Context,
455                                                  TrampolineMemorySegment,
456                                                  TrampolineMemoryOffset);
457     NumVideoModes += ModeCount;
458     return;
459 
460 Cleanup:
461     /* Cleanup failure path, reset standard VGA and free memory */
462     VgaModeList = ModesVGA;
463     VideoPortFreePool(VgaExtension, VbeInfo);
464     VgaExtension->Int10Interface.Int10FreeBuffer(Context,
465                                                  TrampolineMemorySegment,
466                                                  TrampolineMemoryOffset);
467 }
468 
469 /* EOF */
470