NT4/private/ntos/video/ati/query_cx.c
2020-09-30 17:12:29 +02:00

2540 lines
100 KiB
C

/************************************************************************/
/* */
/* QUERY_CX.C */
/* */
/* Copyright (c) 1993, ATI Technologies Incorporated. */
/************************************************************************/
/********************** PolyTron RCS Utilities
$Revision: 1.61 $
$Date: 01 May 1996 14:10:14 $
$Author: RWolff $
$Log: S:/source/wnt/ms11/miniport/archive/query_cx.c_v $
*
* Rev 1.61 01 May 1996 14:10:14 RWolff
* Calls new routine DenseOnAlpha() to determine dense space support rather
* than assuming all PCI cards support dense space, routine treats only
* PCI cards with ?T ASICs as supporting dense space.
*
* Rev 1.60 23 Apr 1996 17:21:18 RWolff
* Split mapping of memory types reported by BIOS into our enumeration
* of memory types according to ASIC type, since ?T and ?X use the same
* memory type code to refer to different memory types.
*
* Rev 1.59 15 Apr 1996 16:57:56 RWolff
* Added routine to identify which flavour of the Mach 64 is in use.
*
* Rev 1.58 12 Apr 1996 16:14:48 RWolff
* Now rejects 24BPP modes if linear aperture is not present, since new
* source stream display driver can't do 24BPP in a paged aperture. This
* rejection should be done in the display driver (the card still supports
* the mode, but the display driver doesn't want to handle it), but at
* the point where the display driver must decide to either accept or reject
* modes, it doesn't have access to the aperture information.
*
* Rev 1.57 20 Mar 1996 13:45:02 RWolff
* Fixed truncation of screen buffer save size.
*
* Rev 1.56 01 Mar 1996 12:14:20 RWolff
* Can now use the existing VGA graphics screen used as the startup
* "blue screen" on the DEC Alpha to store the results of the BIOS
* query call rather than forcing a mode switch and destroying the
* contents of the "blue screen".
*
* Rev 1.55 06 Feb 1996 16:01:00 RWolff
* Updated start and end indices for 1600x1200 to take into account addition
* of 66Hz and 76Hz, and deletion of 52Hz.
*
* Rev 1.54 02 Feb 1996 17:17:38 RWolff
* DDC/VDIF merge source information is now stored in hardware device
* extension rather than static variables, switches back to a VGA text
* screen after we have finished with the query information if we needed
* to switch into a graphics screen in order to obtain a buffer below
* 1M physical (more information needed from DEC in order to make this
* work on the Alpha), moved redundant cleanup code to its own routine.
*
* Rev 1.53 29 Jan 1996 17:00:48 RWolff
* Now uses VideoPortInt10() rather than no-BIOS code on PPC, restricted
* 4BPP to 1M cards, and only for resolutions where 8BPP won't fit.
*
* Rev 1.52 23 Jan 1996 11:47:26 RWolff
* Protected against false values of TARGET_BUILD.
*
* Rev 1.51 11 Jan 1996 19:42:16 RWolff
* Now restricts refresh rates for each resolution/pixel depth combination
* using data from AX=A?07 BIOS call rather than special cases.
*
* Rev 1.50 22 Dec 1995 14:54:02 RWolff
* Added support for Mach 64 GT internal DAC.
*
* Rev 1.49 21 Dec 1995 14:04:02 RWolff
* Locked out modes that ran into trouble at high refresh rates.
*
* Rev 1.48 19 Dec 1995 13:57:02 RWolff
* Added support for refresh rates up to 100Hz at 640x480, 800x600, and
* 1024x768, and 76Hz at 1280x1024.
*
* Rev 1.47 29 Nov 1995 14:36:16 RWolff
* Fix for EPR#08840. The mode that was causing problems (1152x864 32BPP
* 80Hz on IBM DAC) was one that (according to the INSTALL program)
* shouldn't be available on the card.
*
* Rev 1.46 28 Nov 1995 18:14:58 RWolff
* Added debug print statements.
*
* Rev 1.45 21 Nov 1995 11:02:02 RWolff
* Restricted maximum size of BIOS query structure to allow space below
* 1M for reading DDC data.
*
* Rev 1.44 27 Oct 1995 14:23:54 RWolff
* No longer checks for block write on non-LFB configurations, moved
* mapping and unmapping of LFB into the block write check routine
* rather than using the (no longer exists) mapped LFB in the hardware
* device extension.
*
* Rev 1.43 08 Sep 1995 16:35:32 RWolff
* Added support for AT&T 408 DAC (STG1703 equivalent).
*
* Rev 1.42 24 Aug 1995 15:37:20 RWolff
* Changed detection of block I/O cards to match Microsoft's
* standard for plug-and-play.
*
* Rev 1.41 28 Jul 1995 14:40:36 RWolff
* Added support for the Mach 64 VT (CT equivalent with video overlay).
*
* Rev 1.40 26 Jul 1995 12:44:54 mgrubac
* Locked out modes that didn't work on 4M CX cards with STG1703
* and similar DACs.
*
* Rev 1.39 20 Jul 1995 17:57:54 mgrubac
* Added support for VDIF files.
*
* Rev 1.38 13 Jun 1995 15:13:14 RWOLFF
* Now uses VideoPortReadRegisterUlong() instead of direct memory
* reads in BlockWriteAvailable_cx(), since direct reads don't
* work on the DEC Alpha. Breaks out of block write test on
* finding first mismatch, rather than testing the whole block,
* to save time. One mismatch is enough to indicate that block
* write mode is not supported, so after we find one we don't
* need to check the rest of the block.
*
* Rev 1.37 02 Jun 1995 14:31:44 RWOLFF
* Added debug print statements, locked out modes that don't work properly
* on some DACs.
*
* Rev 1.36 10 Apr 1995 15:58:20 RWOLFF
* Now replaces BookValues[] entries where the Mach 64 needs different CRT
* parameters from the Mach 8/Mach 32 (fixes Chrontel DAC 1M 640x480 72Hz
* 24BPP noise problem), locked out 800x600 16BPP 72Hz on 1M cards with
* STG170x and equivalent DACs (another noise problem, this mode is not
* supposed to be supported on 1M cards).
*
* Rev 1.35 31 Mar 1995 11:56:16 RWOLFF
* Changed from all-or-nothing debug print statements to thresholds
* depending on importance of the message.
*
* Rev 1.34 27 Mar 1995 16:12:14 RWOLFF
* Locked out modes that didn't work on 1M cards with STG1702 and
* similar DACs.
*
* Rev 1.33 16 Mar 1995 14:41:08 ASHANMUG
* Limit 1024x768 24 bpp on a STG17xx DAC to 87Hz interlaced
*
* Rev 1.32 03 Mar 1995 10:51:22 ASHANMUG
* Lock-out high refresh rates on CT and '75 DACs
*
* Rev 1.31 24 Feb 1995 12:29:54 RWOLFF
* Added routine to check if the card is susceptible to 24BPP text banding
*
* Rev 1.30 20 Feb 1995 18:02:28 RWOLFF
* Locked out block write on GX rev. E with IBM RAM.
*
* Rev 1.29 14 Feb 1995 15:54:22 RWOLFF
* Now checks CFG_CHIP_TYPE field of CONFIG_CHIP_ID against values found
* in this field for all Mach 64 ASICs, and reports "no Mach 64" if
* no match is found. This fixes a problem on an Apricot FT\\2E with
* a Mach 32 MCA card, where the Mach 32 supplied the BIOS signature
* string, and the machine cached our writes to SCRATCH_PAD0 so it
* looked like the register was present, falsely identifying a Mach 64
* as being present.
*
* Rev 1.28 09 Feb 1995 14:58:14 RWOLFF
* Fix for GX-E IBM DAC screen tearing in 800x600 8BPP.
*
* Rev 1.27 07 Feb 1995 18:21:14 RWOLFF
* Locked out some more resolution/pixel depth/refresh rate combinations
* that are not supported.
*
* Rev 1.26 30 Jan 1995 17:44:20 RWOLFF
* Mach 64 detection now does a low word test before the doubleword test
* to avoid hanging a VLB Mach 32 by writing garbage to the MEM_BNDRY register.
*
* Rev 1.25 30 Jan 1995 11:56:12 RWOLFF
* Now detects CT internal DAC.
*
* Rev 1.24 19 Jan 1995 15:38:18 RWOLFF
* Removed 24BPP no-BIOS lockout and comment explaining why it was
* locked out. 24BPP now works on no-BIOS implementations.
*
* Rev 1.23 18 Jan 1995 15:41:08 RWOLFF
* Added support for Chrontel DAC, now clips maximum colour depth from BIOS
* mode tables to the maximum identified in the query header, locks out
* high refresh rate 1152x864 16BPP modes (8BPP can still be handled through
* double-pixel mode), re-enabled 24BPP on no-BIOS implementations (work
* in progress).
*
* Rev 1.22 11 Jan 1995 14:00:28 RWOLFF
* 1280x1024 no longer restricted to 60Hz maximum on DRAM cards. This
* restriction was a carryover from the Mach 32, since at the time I wrote
* this code, I did not have any information to show that the Mach 64 didn't
* need it.
*
* Rev 1.21 04 Jan 1995 13:20:10 RWOLFF
* Now uses VGA graphics memory if neither text screen is backed by
* physical memory (needed on some DEC Alpha machines), temporarily
* locked out 24BPP on no-BIOS implementations.
*
* Rev 1.20 23 Dec 1994 10:48:10 ASHANMUG
* ALPHA/Chrontel-DAC
*
* Rev 1.19 18 Nov 1994 11:41:54 RWOLFF
* Added support for Mach 64 without BIOS, separated handling of STG1703 from
* other STG170x DACs, no longer creates mode tables for resolutions that the
* DAC doesn't support, added handling for split rasters, rejects 4BPP on
* TVP3026 DAC, recognizes that the Power PC doesn't support block write mode.
*
* Rev 1.18 14 Sep 1994 15:21:30 RWOLFF
* Now stores most desirable supported colour ordering for 24 and 32 BPP
* in the query structure.
*
* Rev 1.17 06 Sep 1994 10:47:32 ASHANMUG
* Force 4bpp on mach64 to have one meg ram
*
* Rev 1.16 31 Aug 1994 16:26:10 RWOLFF
* Now uses VideoPort[Read|Write]Register[Uchar|Ushort|Ulong]() instead
* of direct assignments when accessing structures stored in VGA text
* screen off-screen memory, added support for TVP3026 DAC and new
* list of DAC subtypes, updates maximum supported pixel depth to
* correspond to the card being used rather than taking the normal
* maximum values for the DAC type, added 1152x864 and 1600x1200 support.
*
* Rev 1.15 19 Aug 1994 17:11:56 RWOLFF
* Added support for SC15026 and AT&T 49[123] DACs, fixed reporting
* of "canned" mode tables for resolutions that do not have a
* hardware default refresh rate, added support for 1280x1024 70Hz and 74Hz.
*
* Rev 1.14 30 Jun 1994 18:14:28 RWOLFF
* Moved IsApertureConflict_cx() to SETUP_CX.C because the new method
* of checking for conflict requires access to definitions and data
* structures which are only available in this module.
*
* Rev 1.13 15 Jun 1994 11:08:02 RWOLFF
* Now lists block write as unavailable on DRAM cards.
*
* Rev 1.12 17 May 1994 15:59:48 RWOLFF
* No longer sets a higher pixel clock for "canned" mode tables on some
* DACs. The BIOS will increase the pixel clock frequency for DACs that
* require it.
*
* Rev 1.11 12 May 1994 11:15:26 RWOLFF
* No longer does 1600x1200, now lists predefined refresh rates as available
* instead of only the refresh rate stored in EEPROM.
*
* Rev 1.10 05 May 1994 13:41:00 RWOLFF
* Now reports block write unavailable on Rev. C and earlier ASICs.
*
* Rev 1.9 27 Apr 1994 14:02:26 RWOLFF
* Fixed detection of "LFB disabled" case, no longer creates 4BPP mode tables
* for 68860 DAC (this DAC doesn't do 4BPP), fixed query of DAC type (DAC
* list in BIOS guide is wrong).
*
* Rev 1.8 26 Apr 1994 12:49:16 RWOLFF
* Fixed handling of 640x480 and 800x600 if LFB configured but not available.
*
* Rev 1.7 31 Mar 1994 15:03:40 RWOLFF
* Added 4BPP support, debugging code to see why some systems were failing.
*
* Rev 1.6 15 Mar 1994 16:27:00 RWOLFF
* Rounds 8M aperture down to 8M boundary, not 16M boundary.
*
* Rev 1.5 14 Mar 1994 16:34:40 RWOLFF
* Fixed handling of 8M linear aperture installed so it doesn't start on
* an 8M boundary (retail version of install program shouldn't allow this
* condition to exist), fix for tearing on 2M boundary.
*
* Rev 1.4 09 Feb 1994 15:32:22 RWOLFF
* Corrected name of variable for best colour depth found, closed
* comment that had been left open in previous revision.
*
* Rev 1.3 08 Feb 1994 19:02:34 RWOLFF
* No longer makes 1024x768 87Hz interlaced available if Mach 64 card is
* configured with 1024x768 set to "Not installed".
*
* Rev 1.2 07 Feb 1994 14:12:00 RWOLFF
* Added alloc_text() pragmas to allow miniport to be swapped out when
* not needed, removed GetMemoryNeeded_cx() which was only called by
* LookForSubstitute(), a routine removed from ATIMP.C.
*
* Rev 1.1 03 Feb 1994 16:43:20 RWOLFF
* Fixed "ceiling check" on right scissor registers (documentation
* had maximum value wrong).
*
* Rev 1.0 31 Jan 1994 11:12:08 RWOLFF
* Initial revision.
*
* Rev 1.2 14 Jan 1994 15:23:34 RWOLFF
* Gives unambiguous value for ASIC revision, uses deepest mode table for
* a given resolution rather than the first one it finds, added routine
* to check if block write mode is available, support for 1600x1200.
*
* Rev 1.1 30 Nov 1993 18:26:30 RWOLFF
* Fixed hang bug in DetectMach64(), moved query buffer off visible screen,
* changed QueryMach64() to correspond to latest BIOS specifications,
* added routines to check for aperture conflict and to find the
* amount of video memory needed by a given mode.
*
* Rev 1.0 05 Nov 1993 13:36:28 RWOLFF
* Initial revision.
End of PolyTron RCS section *****************/
#ifdef DOC
QUERY_CX.C - Functions to detect the presence of and find out the
configuration of 68800CX-compatible ATI accelerators.
#endif
#include "dderror.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "miniport.h"
#include "ntddvdeo.h"
#include "video.h"
#include "stdtyp.h"
#include "amachcx.h"
#include "amach1.h"
#include "atimp.h"
#include "atint.h"
#include "cvtvga.h"
#define INCLUDE_QUERY_CX
#define STRUCTS_QUERY_CX
#include "query_cx.h"
#include "services.h"
#include "setup_cx.h"
#include "cvtddc.h"
/*
* Prototypes for static functions.
*/
static void CleanupQuery(PUCHAR CapBuffer, PUCHAR SupBuffer, PUCHAR MappedBuffer, long BufferSeg, PUCHAR SavedScreen);
/*
* Allow miniport to be swapped out when not needed.
*/
#if defined (ALLOC_PRAGMA)
#pragma alloc_text(PAGE_CX, DetectMach64)
#pragma alloc_text(PAGE_CX, QueryMach64)
#pragma alloc_text(PAGE_CX, BlockWriteAvail_cx)
#pragma alloc_text(PAGE_CX, TextBanding_cx)
#pragma alloc_text(PAGE_CX, CleanupQuery)
#endif
/***************************************************************************
*
* int DetectMach64(void);
*
* DESCRIPTION:
* Detect whether or not a Mach 64 accelerator is present.
*
* RETURN VALUE:
* MACH64_ULTRA if Mach 64 accelerator found
* NO_ATI_ACCEL if no Mach 64 accelerator found
*
* GLOBALS CHANGED:
* none
*
* CALLED BY:
* ATIMPFindAdapter()
*
* AUTHOR:
* Robert Wolff
*
* CHANGE HISTORY:
*
* TEST HISTORY:
*
***************************************************************************/
int DetectMach64(void)
{
int CardType = MACH64_ULTRA; /* Initially assume Mach 64 is present */
DWORD ScratchReg0; /* Saved contents of SCRATCH_REG0 */
WORD CfgChipType; /* CFG_CHIP_TYPE field of CONFIG_CHIP_ID */
/*
* Some other brands of video card will pass the write/read back
* test for the Mach 64. To avoid falsely identifying them as
* Mach 64 cards, check for the ATI signature string in the BIOS.
*
* Failure cases use DEBUG_DETAIL rather than DEBUG_ERROR because
* failed detection of the Mach 64 is a normal case for Mach 8 and
* Mach 32 cards, and for non-ATI cards in "run all the miniports
* and see which ones find their cards" video determination.
*/
if (Get_BIOS_Seg() == FALSE)
{
VideoDebugPrint((DEBUG_DETAIL, "DetectMach64() no ATI BIOS signature found\n"));
}
/*
* On a machine with a Mach 32 to provide an ATI video BIOS
* segment, a card with a 32 bit read/write register matching
* SCRATCH_REG0 would be falsely detected as a Mach 64. To
* avoid this, check the CFG_CHIP_TYPE field of CONFIG_CHIP_ID
* against values found in this field for known Mach 64 ASICs
* as an additional test. Since this test is non-destructive,
* do it first.
*/
CfgChipType = INPW(CONFIG_CHIP_ID);
if ((CfgChipType != CONFIG_CHIP_ID_TypeGX) && /* GX */
(CfgChipType != CONFIG_CHIP_ID_TypeCX) && /* CX */
(CfgChipType != 0x4354) && /* CT */
(CfgChipType != 0x4554) && /* ET */
(CfgChipType != 0x4754) && /* GT */
(CfgChipType != 0x4C54) && /* LT */
(CfgChipType != 0x4D54) && /* MT */
(CfgChipType != 0x5254) && /* RT */
(CfgChipType != 0x5654) && /* VT */
(CfgChipType != 0x3354)) /* 3T */
{
VideoDebugPrint((DEBUG_DETAIL, "DetectMach64() - CFG_CHIP_TYPE = 0x%X doesn't match known Mach 64 ASIC\n", CfgChipType));
return NO_ATI_ACCEL;
}
/*
* Save the contents of SCRATCH_REG0, since they are destroyed in
* the test for Mach 64 accelerators.
*/
ScratchReg0 = INPD(SCRATCH_REG0);
/*
* On a Mach 64 card, any 32 bit pattern written to SCRATCH_REG0
* will be read back as the same value. Since unimplemented registers
* normally drift to either all set or all clear, test this register
* with two patterns (second is the complement of the first) containing
* alternating set and clear bits. If either of them is not read back
* unchanged, then assume that no Mach 64 card is present.
*
* After writing, we must wait long enough for the contents of
* SCRATCH_REG0 to settle down. We can't use a WaitForIdle_cx() call
* because this function uses a register which only exists in
* memory-mapped form, and we don't initialize the memory-mapped
* registers until we know that we are dealing with a Mach 64 card.
* Instead, assume that it will settle down in 1 millisecond.
*
* Test the low word of SCRATCH_REG0 before testing the whole
* doubleword. This is because the high word of this register
* corresponds to the MEM_BNDRY register on the Mach 32 (low
* word not used). If we do a doubleword write on a Mach 32
* card (Mach 64 detection is before Mach 32 detection), we
* will plug garbage data into MEM_BNDRY, which will hang the machine.
*/
OUTPW(SCRATCH_REG0,0x05555);
delay(1);
if (INPW(SCRATCH_REG0) != 0x05555)
CardType = NO_ATI_ACCEL;
OUTPW(SCRATCH_REG0, 0x0AAAA);
delay(1);
if (INPW(SCRATCH_REG0) != 0x0AAAA)
CardType = NO_ATI_ACCEL;
/*
* Failure - restore the register and return.
*/
if (CardType == NO_ATI_ACCEL)
{
OUTPW(SCRATCH_REG0, (WORD)(ScratchReg0 & 0x0000FFFF));
VideoDebugPrint((DEBUG_DETAIL, "DetectMach64() - SCRATCH_REG0 word readback doesn't match value written\n"));
return CardType;
}
/*
* Success - test the register as a doubleword.
*/
OUTPD(SCRATCH_REG0, 0x055555555);
delay(1);
if (INPD(SCRATCH_REG0) != 0x055555555)
CardType = NO_ATI_ACCEL;
OUTPD(SCRATCH_REG0, 0x0AAAAAAAA);
delay(1);
if (INPD(SCRATCH_REG0) != 0x0AAAAAAAA)
CardType = NO_ATI_ACCEL;
/*
* Restore the contents of SCRATCH_REG0 and let the caller know
* whether or not we found a Mach 64.
*/
OUTPD(SCRATCH_REG0, ScratchReg0);
return CardType;
} /* DetectMach64() */
/***************************************************************************
*
* VP_STATUS QueryMach64(Query);
*
* struct query_structure *Query; Query structure to fill in
*
* DESCRIPTION:
* Fill in the query structure and mode tables for the
* Mach 64 accelerator.
*
* RETURN VALUE:
* NO_ERROR if successful
* ERROR_INSUFFICIENT_BUFFER if not enough space to collect data
* any error code returned by operating system calls.
*
* GLOBALS CHANGED:
* none
*
* CALLED BY:
* ATIMPFindAdapter()
*
* AUTHOR:
* Robert Wolff
*
* CHANGE HISTORY:
*
* TEST HISTORY:
*
***************************************************************************/
VP_STATUS QueryMach64(struct query_structure *Query)
{
VIDEO_X86_BIOS_ARGUMENTS Registers; /* Used in VideoPortInt10() calls */
VP_STATUS RetVal; /* Status returned by VideoPortInt10() */
short MaxModes; /* Maximum number of modes possible in query structure */
short AbsMaxDepth; /* Maximum pixel depth supported by the DAC */
struct cx_query *CxQuery; /* Query header from BIOS call */
struct cx_mode_table *CxModeTable; /* Mode tables from BIOS call */
struct st_mode_table ThisRes; /* All-depth mode table for current resolution */
short CurrentRes; /* Current resolution we are working on */
long BufferSeg; /* Segment of buffer used for BIOS query */
long BufferSize; /* Size of buffer needed for BIOS query */
PUCHAR MappedBuffer; /* Pointer to buffer used for BIOS query */
short Count; /* Loop counter */
DWORD Scratch; /* Temporary variable */
long MemAvail; /* Memory available, in bytes */
long NumPixels; /* Number of pixels for the current mode */
struct st_mode_table *pmode; /* Mode table to be filled in */
short StartIndex; /* First mode for SetFixedModes() to set up */
short EndIndex; /* Last mode for SetFixedModes() to set up */
BOOL ModeInstalled; /* Is this resolution configured? */
short FreeTables; /* Number of remaining free mode tables */
short FormatType; /* Which table format is in use */
UCHAR DacTypeMask; /* Bitmask for DAC type on the card */
UCHAR OrigDacType; /* DAC type before processing into AMACH1.H ordering */
UCHAR OrigRamType; /* RAM type before processing into AMACH1.H ordering */
UCHAR OrigRamSize; /* Amount of RAM before processing into number of 256k banks */
PUCHAR HwCapBuffer; /* Pointer to buffer of hardware capabilities */
PUCHAR HwSupBuffer; /* Pointer to supplemental buffer */
PUCHAR HwCapWalker; /* Pointer to walk through above buffer */
struct cx_hw_cap *HwCapEntry; /* Pointer to single entry in table of hardware capabilities */
UCHAR HwCapBytesPerRow; /* Number of bytes in each hardware capability entry */
UCHAR MaxDotClock[HOW_MANY_DEPTHS]; /* Maximum dot clock at each pixel depth for the current resolution */
UCHAR CurrentDepth; /* Pixel depth for current hardware capability entry */
/*
* Place to save the contents of the VGA screen before making a BIOS
* query using the VGA memory as a buffer. Needed only when using
* the existing graphics screen as a buffer, since we use an offscreen
* portion of the text screen, and if we have to switch into a VGA
* graphics mode there will be nothing to save.
*/
UCHAR SavedVgaBuffer[VGA_TOTAL_SIZE];
/*
* If we do not yet know the BIOS prefix for this card (i.e.
* it is a block relocatable card where we must match the
* BIOS prefix to the I/O base in case we have multiple
* cards.
*/
if (phwDeviceExtension->BiosPrefix == BIOS_PREFIX_UNASSIGNED)
{
/*
* We don't support block relocatable cards in the
* no-BIOS configuration.
*/
phwDeviceExtension->BiosPrefix = BIOS_PREFIX_VGA_ENAB;
/*
* We shouldn't need to check for equality, but this allows
* us to catch the "too many cards - this one doesn't have
* a BIOS prefix" case by checking for an out-of-range
* prefix after the loop exits.
*/
while (phwDeviceExtension->BiosPrefix <= BIOS_PREFIX_MAX_DISAB)
{
VideoDebugPrint((DEBUG_DETAIL, "Testing BIOS prefix 0x%X\n", phwDeviceExtension->BiosPrefix));
VideoPortZeroMemory(&Registers, sizeof(VIDEO_X86_BIOS_ARGUMENTS));
Registers.Eax = BIOS_QUERY_IOBASE;
if ((RetVal = VideoPortInt10(phwDeviceExtension, &Registers)) != NO_ERROR)
{
VideoDebugPrint((DEBUG_ERROR, "QueryMach64() - failed BIOS_QUERY_IOBASE\n"));
return RetVal;
}
/*
* If the card with the current BIOS prefix uses our I/O base
* address, we have found the correct prefix. Otherwise,
* try the next prefix.
*/
if (Registers.Edx == phwDeviceExtension->BaseIOAddress)
{
VideoDebugPrint((DEBUG_DETAIL, "Card with I/O base address 0x%X uses BIOS prefix 0x%X\n", Registers.Edx, phwDeviceExtension->BiosPrefix));
break;
}
else
{
VideoDebugPrint((DEBUG_DETAIL, "Reported I/O base of 0x%X - no match\n", Registers.Edx));
}
phwDeviceExtension->BiosPrefix += BIOS_PREFIX_INCREMENT;
} /* end while (searching for the correct prefix) */
/*
* The equality test on the loop will result in an illegal
* prefix on exit if there are too many cards for us to
* handle, and this is one of the "orphans".
*/
if (phwDeviceExtension->BiosPrefix > BIOS_PREFIX_MAX_DISAB)
{
VideoDebugPrint((DEBUG_ERROR, "QueryMach64() - can't find BIOS prefix for card with I/O base 0x%X\n", phwDeviceExtension->BaseIOAddress));
return ERROR_DEV_NOT_EXIST;
}
} /* endif (unassigned BIOS prefix) */
/*
* Find out how large a buffer we need when making a BIOS query call.
*/
VideoPortZeroMemory(&Registers, sizeof(VIDEO_X86_BIOS_ARGUMENTS));
Registers.Eax = BIOS_GET_QUERY_SIZE;
Registers.Ecx = BIOS_QUERY_FULL;
if ((RetVal = VideoPortInt10(phwDeviceExtension, &Registers)) != NO_ERROR)
{
VideoDebugPrint((DEBUG_ERROR, "QueryMach64() - failed BIOS_GET_QUERY_SIZE\n"));
return RetVal;
}
BufferSize = Registers.Ecx & 0x0000FFFF;
/*
* Allocate a buffer to store the query information. Due to the BIOS
* being real mode, this buffer must be below 1M. When this function
* is called, we are on the "blue screen", so there is a 32k window
* below 1M that we can use without risk of corrupting executable code.
*
* To avoid the need to save and restore our buffer, use only the
* offscreen portion of this window (video memory contents will be
* initialized before they are used, so the leftover query structure
* won't harm anything). Assume a 50 line text screen.
*
* Check to see if the query structure is small enough to fit into
* this region, and fail if it's too big. If it fits, try to allocate
* the memory in the colour text window and see if there's enough
* physical memory to meet our needs. If this fails, try again for
* the monochrome text window (since VGA can run as either colour
* or monochrome).
*
* If both fail (will happen on some DEC ALPHA machines), try using
* the existing VGA graphics screen. Since we will be using an
* on-screen portion of this buffer, we must save and restore the
* contents of this buffer.
*
* If this fails (haven't run into any machines where this is the
* case), switch into SVGA 640x480 8BPP and use the VGA graphics
* screen. This is a last resort, since unlike using an existing
* screen, this will destroy the "blue screen", and is therefore not
* transparent to the user. If we can't even get this to work, report
* that there isn't enough buffer space. This would only happen when
* the onboard VGA is disabled and a low-end (MDA - even CGA has 16k
* of memory available) card is used to provide the text screen.
*/
/*
* Leave some room for the EDID structure, which must also be
* read into a buffer below 1M physical.
*/
if (BufferSize > 0x5000)
{
VideoDebugPrint((DEBUG_ERROR, "QueryMach64() - query needs more buffer than we have\n"));
return ERROR_INSUFFICIENT_BUFFER;
}
BufferSeg = 0x0BA00; /* Colour text */
MappedBuffer = MapFramebuffer((BufferSeg << 4), BufferSize);
if (MappedBuffer != 0)
{
if (IsBufferBacked(MappedBuffer, BufferSize) == FALSE)
{
VideoDebugPrint((DEBUG_NORMAL, "Colour text screen not backed by physical memory\n"));
VideoPortFreeDeviceBase(phwDeviceExtension, MappedBuffer);
MappedBuffer = 0;
}
}
else
{
VideoDebugPrint((DEBUG_NORMAL, "Can't map colour text screen\n"));
}
/*
* If we were unable to allocate a large enough buffer in the
* colour text screen, try the monochrome text screen.
*/
if (MappedBuffer == 0)
{
VideoDebugPrint((DEBUG_NORMAL, "Can't use colour text screen, trying monochrome text screen\n"));
BufferSeg = 0x0B200;
if ((MappedBuffer = MapFramebuffer((BufferSeg << 4), BufferSize)) != 0)
{
if (IsBufferBacked(MappedBuffer, BufferSize) == FALSE)
{
VideoDebugPrint((DEBUG_NORMAL, "Monochrome text screen not backed by physical memory\n"));
VideoPortFreeDeviceBase(phwDeviceExtension, MappedBuffer);
MappedBuffer = 0;
}
}
else
{
VideoDebugPrint((DEBUG_NORMAL, "Can't map monochrome text screen\n"));
}
}
if (MappedBuffer == 0)
{
/*
* We were unable to use the offscreen portion of video memory
* in either of the text screens. Try to use an existing graphics
* screen.
*
* Currently, only the DEC Alpha will fail to find the offscreen
* portion of either text screen.
*/
VideoDebugPrint((DEBUG_NORMAL, "Can't use monochrome text screen, trying existing graphics screen\n"));
BufferSeg = 0x0A000;
if ((MappedBuffer = MapFramebuffer((BufferSeg << 4), BufferSize)) != 0)
{
/*
* Preserve the contents of VGA registers which affect the
* manner in which graphics memory is accessed, then set
* the values we need.
*/
OUTP(VGA_SEQ_IND, 2);
SavedVgaBuffer[VGA_SAVE_SEQ02] = INP(VGA_SEQ_DATA);
OUTP(VGA_SEQ_IND, 2);
OUTP(VGA_SEQ_DATA, 0x01);
OUTP(VGA_GRAX_IND, 8);
SavedVgaBuffer[VGA_SAVE_GRA08] = INP(VGA_GRAX_DATA);
OUTP(VGA_GRAX_IND, 8);
OUTP(VGA_GRAX_DATA, 0xFF);
OUTP(VGA_GRAX_IND, 1);
SavedVgaBuffer[VGA_SAVE_GRA01] = INP(VGA_GRAX_DATA);
OUTP(VGA_GRAX_IND, 1);
OUTP(VGA_GRAX_DATA, 0x00);
/*
* Save the contents of the screen to our private
* buffer, so we can restore the screen later.
*/
if (BufferSize > VGA_SAVE_SIZE)
{
VideoDebugPrint((DEBUG_ERROR, "Buffer too big to fully save/restore\n"));
Scratch = VGA_SAVE_SIZE;
}
else
{
Scratch = BufferSize;
}
SavedVgaBuffer[VGA_SAVE_SIZE] = (UCHAR)(Scratch & 0x00FF);
SavedVgaBuffer[VGA_SAVE_SIZE_H] = (UCHAR)((ULONG)((Scratch & 0xFF00) >> 8));
for (Count = 0; (short)Count < (short)Scratch; Count++)
{
SavedVgaBuffer[Count] = VideoPortReadRegisterUchar(&(MappedBuffer[Count]));
}
if (IsBufferBacked(MappedBuffer, BufferSize) == FALSE)
{
VideoDebugPrint((DEBUG_NORMAL, "Existing graphics screen not backed by physical memory\n"));
VideoPortFreeDeviceBase(phwDeviceExtension, MappedBuffer);
MappedBuffer = 0;
OUTP(VGA_SEQ_IND, 2);
OUTP(VGA_SEQ_DATA, SavedVgaBuffer[VGA_SAVE_SEQ02]);
OUTP(VGA_GRAX_IND, 8);
OUTP(VGA_GRAX_DATA, SavedVgaBuffer[VGA_SAVE_GRA08]);
OUTP(VGA_GRAX_IND, 1);
OUTP(VGA_GRAX_DATA, SavedVgaBuffer[VGA_SAVE_GRA01]);
}
}
else
{
VideoDebugPrint((DEBUG_NORMAL, "Can't map existing graphics screen\n"));
}
} /* end if (previous buffer allocation failed) */
/*
* If we were unable to allocate a large enough buffer in an existing
* screen, try the VGA graphics screen. This will wipe out
* the Windows NT "blue screen", but it gives us one last chance
* to get a block of memory below 1M.
* Don't start at the beginning of the VGA graphics window, since
* we will need to distinguish this case from the nondestructive
* access to the VGA graphics screen at cleanup time, and the
* different buffer segment for the two cases will allow us to
* do this.
*/
if (MappedBuffer == 0)
{
VideoDebugPrint((DEBUG_NORMAL, "Nondestructive VGA memory access failed, trying graphics screen\n"));
Registers.Eax = 0x62; /* 640x480 8BPP */
VideoPortInt10(phwDeviceExtension, &Registers);
BufferSeg = 0x0A100;
if ((MappedBuffer = MapFramebuffer((BufferSeg << 4), BufferSize)) == 0)
{
VideoDebugPrint((DEBUG_ERROR, "Can't map graphics screen - aborting query\n"));
return ERROR_INSUFFICIENT_BUFFER;
}
if (IsBufferBacked(MappedBuffer, BufferSize) == FALSE)
{
VideoDebugPrint((DEBUG_ERROR, "Graphics screen not backed by memory - aborting query\n"));
VideoPortFreeDeviceBase(phwDeviceExtension, MappedBuffer);
return ERROR_INSUFFICIENT_BUFFER;
}
}
/*
* We now have a buffer big enough to hold the query structure,
* so make the BIOS call to fill it in.
*/
Registers.Ebx = 0;
Registers.Edx = BufferSeg;
Registers.Eax = BIOS_QUERY;
Registers.Ecx = BIOS_QUERY_FULL;
if ((RetVal = VideoPortInt10(phwDeviceExtension, &Registers)) != NO_ERROR)
{
VideoDebugPrint((DEBUG_ERROR, "QueryMach64() - failed BIOS_QUERY_FULL call\n"));
return RetVal;
}
CxQuery = (struct cx_query *)MappedBuffer;
/*
* The Mach 64 query structure and mode tables may be a different size
* from their equivalents (query_structure and st_mode_table). To avoid
* overflowing our buffer, find out how many mode tables we have space
* to hold.
*
* Later, when we are filling the mode tables, we will check to see
* whether the current mode table would exceed this limit. If it would,
* we will return ERROR_INSUFFICIENT_BUFFER rather than overflowing
* the table.
*/
MaxModes = (QUERYSIZE - sizeof(struct query_structure)) / sizeof(struct st_mode_table);
/*
* Fill in the header of the query stucture.
*/
Query->q_structure_rev = VideoPortReadRegisterUchar(&(CxQuery->cx_structure_rev));
VideoDebugPrint((DEBUG_DETAIL, "Structure revision = 0x%X\n", VideoPortReadRegisterUchar(&(CxQuery->cx_structure_rev))));
Query->q_mode_offset = VideoPortReadRegisterUshort(&(CxQuery->cx_mode_offset));
VideoDebugPrint((DEBUG_DETAIL, "Mode offset = 0x%X\n", VideoPortReadRegisterUshort(&(CxQuery->cx_mode_offset))));
Query->q_sizeof_mode = VideoPortReadRegisterUchar(&(CxQuery->cx_mode_size));
VideoDebugPrint((DEBUG_DETAIL, "Mode size = 0x%X\n", VideoPortReadRegisterUchar(&(CxQuery->cx_mode_size))));
/*
* Currently only one revision of Mach 64. Will need to
* set multiple values once new (production) revisions come out.
*/
Query->q_asic_rev = CI_88800_GX;
Query->q_number_modes = 0; /* Initially assume no modes supported */
Query->q_status_flags = 0;
/*
* If the on-board VGA is enabled, set shared VGA/accelerator memory.
* Whether or not it is enabled, the accelerator will be able to
* access all the video memory.
*/
if ((Query->q_VGA_type = VideoPortReadRegisterUchar(&(CxQuery->cx_vga_type)) != 0))
{
Scratch = INPD(MEM_CNTL) & 0x0FFFBFFFF; /* Clear MEM_BNDRY_EN bit */
OUTPD(MEM_CNTL, Scratch);
VideoDebugPrint((DEBUG_DETAIL, "VGA enabled on this card\n"));
}
else
{
VideoDebugPrint((DEBUG_DETAIL, "VGA disabled on this card\n"));
}
Query->q_VGA_boundary = 0;
OrigRamSize = VideoPortReadRegisterUchar(&(CxQuery->cx_memory_size));
VideoDebugPrint((DEBUG_DETAIL, "Raw memory size = 0x%X\n", OrigRamSize));
Query->q_memory_size = CXMapMemSize[OrigRamSize];
MemAvail = Query->q_memory_size * QUARTER_MEG;
/*
* DAC types are not contiguous, so a lookup table would be
* larger than necessary and restrict future expansion.
*/
OrigDacType = VideoPortReadRegisterUchar(&(CxQuery->cx_dac_type));
VideoDebugPrint((DEBUG_DETAIL, "cx_dac_type = 0x%X\n", OrigDacType));
switch(OrigDacType)
{
case 0x00:
VideoDebugPrint((DEBUG_DETAIL, "Internal DAC\n"));
Scratch = VideoPortReadRegisterUshort(&(CxQuery->cx_asic_rev));
if ((Scratch & 0xFF00) == 0x4300)
{
VideoDebugPrint((DEBUG_DETAIL, "Mach 64 CT internal DAC\n"));
Query->q_DAC_type = DAC_INTERNAL_CT;
}
else if ((Scratch & 0xFF00) == 0x5600)
{
VideoDebugPrint((DEBUG_DETAIL, "Mach 64 VT internal DAC\n"));
Query->q_DAC_type = DAC_INTERNAL_VT;
}
else if ((Scratch & 0xFF00) == 0x4700)
{
VideoDebugPrint((DEBUG_DETAIL, "Mach 64 GT internal DAC\n"));
Query->q_DAC_type = DAC_INTERNAL_GT;
}
else
{
VideoDebugPrint((DEBUG_ERROR, "Unknown internal DAC (ASIC ID = 0x%X), treating as BT47x\n", Scratch));
Query->q_DAC_type = DAC_BT47x;
}
DacTypeMask = 0x01;
break;
case 0x01:
VideoDebugPrint((DEBUG_DETAIL, "IBM 514 DAC\n"));
Query->q_DAC_type = DAC_IBM514;
DacTypeMask = 0x02;
break;
case 0x02:
VideoDebugPrint((DEBUG_DETAIL, "TI34075 DAC\n"));
Query->q_DAC_type = DAC_TI34075;
DacTypeMask = 0x04;
break;
case 0x72:
VideoDebugPrint((DEBUG_DETAIL, "TVP 3026 DAC\n"));
Query->q_DAC_type = DAC_TVP3026;
DacTypeMask = 0x04;
break;
case 0x04:
VideoDebugPrint((DEBUG_DETAIL, "BT48x DAC\n"));
Query->q_DAC_type = DAC_BT48x;
DacTypeMask = 0x10;
break;
case 0x14:
VideoDebugPrint((DEBUG_DETAIL, "AT&T 49[123] DAC\n"));
Query->q_DAC_type = DAC_ATT491;
DacTypeMask = 0x10;
break;
case 0x05:
case 0x15:
VideoDebugPrint((DEBUG_DETAIL, "ATI68860 DAC\n"));
Query->q_DAC_type = DAC_ATI_68860;
DacTypeMask = 0x20;
break;
case 0x06:
VideoDebugPrint((DEBUG_DETAIL, "STG1700 DAC\n"));
Query->q_DAC_type = DAC_STG1700;
DacTypeMask = 0x40;
break;
case 0x07:
case 0x67:
case 0x77:
case 0x87:
case 0x97:
case 0xA7:
case 0xB7:
case 0xC7:
case 0xD7:
case 0xE7:
case 0xF7:
VideoDebugPrint((DEBUG_DETAIL, "STG1702 DAC\n"));
Query->q_DAC_type = DAC_STG1702;
DacTypeMask = 0x80;
break;
case 0x37:
VideoDebugPrint((DEBUG_DETAIL, "STG1703 DAC\n"));
Query->q_DAC_type = DAC_STG1703;
DacTypeMask = 0x80;
break;
case 0x47:
VideoDebugPrint((DEBUG_DETAIL, "CH8398 DAC\n"));
Query->q_DAC_type = DAC_CH8398;
DacTypeMask = 0x80;
break;
case 0x57:
VideoDebugPrint((DEBUG_DETAIL, "AT&T 408 DAC\n"));
Query->q_DAC_type = DAC_ATT408;
DacTypeMask = 0x80;
break;
case 0x16:
case 0x27:
VideoDebugPrint((DEBUG_DETAIL, "AT&T 498 DAC\n"));
Query->q_DAC_type = DAC_ATT498;
DacTypeMask = 0x80;
break;
case 0x17:
VideoDebugPrint((DEBUG_DETAIL, "SC15021 DAC\n"));
Query->q_DAC_type = DAC_SC15021;
DacTypeMask = 0x80;
break;
case 0x75:
VideoDebugPrint((DEBUG_DETAIL, "TVP 3026 DAC\n"));
Query->q_DAC_type = DAC_TVP3026;
DacTypeMask = 0x20;
break;
case 0x03:
VideoDebugPrint((DEBUG_DETAIL, "BT 47x DAC\n"));
Query->q_DAC_type = DAC_BT47x;
DacTypeMask = 0x04;
break;
default:
VideoDebugPrint((DEBUG_ERROR, "Unknown DAC, treating as BT 47x\n"));
Query->q_DAC_type = DAC_BT47x;
DacTypeMask = 0x04;
break;
}
VideoDebugPrint((DEBUG_DETAIL, "Raw memory type = 0x%X\n", VideoPortReadRegisterUchar(&(CxQuery->cx_memory_type))));
/*
* Bit 7 of the memory type is used to indicate lack of block write
* capability on recent BIOSes, but not on older ones. Strip it
* before mapping the RAM type in order to avoid the need for an
* additional 128 entries, most of which are unused, in the
* mapping table.
*
* Even though the absence of this flag is not a reliable indicator
* of block write capability, its presence is a reliable indicator
* of a lack of block write capability.
*
* We can strip this flag after setting the block write status since
* this is the only place it is used, and subsequent references to
* the memory type require only the lower 7 bits.
*/
OrigRamType = VideoPortReadRegisterUchar(&(CxQuery->cx_memory_type));
if (OrigRamType & 0x80)
Query->q_BlockWrite = BLOCK_WRITE_NO;
OrigRamType &= 0x7F;
/*
* A given memory type value will have different meanings for
* different ASIC types. While the GX and CX use different
* RAM types, none of them require special-case handling,
* so we can treat these ASIC types as equivalent.
*/
Scratch = INPD(CONFIG_CHIP_ID) & CONFIG_CHIP_ID_TypeMask;
if ((Scratch == CONFIG_CHIP_ID_TypeGX) ||
(Scratch == CONFIG_CHIP_ID_TypeCX))
{
VideoDebugPrint((DEBUG_DETAIL, "Setting q_memory_type for CX or GX\n"));
Query->q_memory_type = CXMapRamType[OrigRamType];
}
else
{
VideoDebugPrint((DEBUG_DETAIL, "Setting q_memory_type for CT/VT/GT\n"));
Query->q_memory_type = CTMapRamType[OrigRamType];
}
VideoDebugPrint((DEBUG_DETAIL, "Raw bus type = 0x%X\n", VideoPortReadRegisterUchar(&(CxQuery->cx_bus_type))));
Query->q_bus_type = CXMapBus[VideoPortReadRegisterUchar(&(CxQuery->cx_bus_type))];
/*
* Get the linear aperture configuration. If the linear aperture and
* VGA aperture are both disabled, return ERROR_DEV_NOT_EXIST, since
* some Mach 64 registers exist only in memory mapped form and are
* therefore not available without an aperture.
*/
Query->q_aperture_cfg = VideoPortReadRegisterUchar(&(CxQuery->cx_aperture_cfg)) & BIOS_AP_SIZEMASK;
VideoDebugPrint((DEBUG_DETAIL, "Aperture configuration = 0x%X\n", VideoPortReadRegisterUchar(&(CxQuery->cx_aperture_cfg))));
if (Query->q_aperture_cfg == 0)
{
if (Query->q_VGA_type == 0)
{
VideoDebugPrint((DEBUG_ERROR, "Neither linear nor VGA aperture exists - aborting query\n"));
return ERROR_DEV_NOT_EXIST;
}
Query->q_aperture_addr = 0;
}
else
{
Query->q_aperture_addr = VideoPortReadRegisterUshort(&(CxQuery->cx_aperture_addr));
VideoDebugPrint((DEBUG_DETAIL, "Aperture at %d megabytes\n", Query->q_aperture_addr));
/*
* If the 8M aperture is configured on a 4M boundary that is
* not also an 8M boundary, it will actually start on the 8M
* boundary obtained by truncating the reported value to a
* multiple of 8M.
*/
if ((Query->q_aperture_cfg & BIOS_AP_SIZEMASK) == BIOS_AP_8M)
{
VideoDebugPrint((DEBUG_DETAIL, "8 megabyte aperture\n"));
Query->q_aperture_addr &= 0xFFF8;
}
}
/*
* The Mach 64 does not support shadow sets, so re-use the shadow
* set 1 definition to hold deep colour support and RAMDAC special
* features information.
*/
Query->q_shadow_1 = VideoPortReadRegisterUchar(&(CxQuery->cx_deep_colour)) | (VideoPortReadRegisterUchar(&(CxQuery->cx_ramdac_info)) << 8);
VideoDebugPrint((DEBUG_DETAIL, "Deep colour support = 0x%X\n", VideoPortReadRegisterUchar(&(CxQuery->cx_deep_colour))));
/*
* If this card supports non-palette modes, choose which of the supported
* colour orderings to use at each pixel depth. Record the maximum
* pixel depth the card supports, since some of the mode tables
* may list a maximum pixel depth beyond the DAC's capabilities.
*
* Assume that no DAC will support nBPP (n > 8) without also supporting
* all colour depths between 8 and n.
*/
AbsMaxDepth = 8; /* Cards without high colour support */
if (Query->q_shadow_1 & S1_16BPP_565)
{
Query->q_HiColourSupport = RGB16_565;
AbsMaxDepth = 16;
}
if (Query->q_shadow_1 & S1_24BPP)
{
if (Query->q_shadow_1 & S1_24BPP_RGB)
{
VideoDebugPrint((DEBUG_DETAIL, "24BPP order RGB\n"));
Query->q_HiColourSupport |= RGB24_RGB;
}
else
{
VideoDebugPrint((DEBUG_DETAIL, "24BPP order BGR\n"));
Query->q_HiColourSupport |= RGB24_BGR;
}
AbsMaxDepth = 24;
}
if (Query->q_shadow_1 & S1_32BPP)
{
if (Query->q_shadow_1 & S1_32BPP_RGBx)
{
VideoDebugPrint((DEBUG_DETAIL, "32BPP order RGBx\n"));
Query->q_HiColourSupport |= RGB32_RGBx;
}
else if (Query->q_shadow_1 & S1_32BPP_xRGB)
{
VideoDebugPrint((DEBUG_DETAIL, "32BPP order xRGB\n"));
Query->q_HiColourSupport |= RGB32_xRGB;
}
else if (Query->q_shadow_1 & S1_32BPP_BGRx)
{
VideoDebugPrint((DEBUG_DETAIL, "32BPP order BGRx\n"));
Query->q_HiColourSupport |= RGB32_BGRx;
}
else
{
VideoDebugPrint((DEBUG_DETAIL, "32BPP order xBGR\n"));
Query->q_HiColourSupport |= RGB32_xBGR;
}
AbsMaxDepth = 32;
}
/*
* Get the hardware capability list.
*/
Registers.Eax = BIOS_CAP_LIST;
Registers.Ecx = 0xFFFF;
if ((RetVal = VideoPortInt10(phwDeviceExtension, &Registers)) != NO_ERROR)
{
VideoDebugPrint((DEBUG_ERROR, "QueryMach64() - failed BIOS_CAP_LIST\n"));
return RetVal;
}
FormatType = (short)(Registers.Eax & 0x000000FF);
/*
* Map in the table of hardware capabilities whose pointer was returned
* by the BIOS call. The call does not return the size of the table,
* but according to Steve Stefanidis 1k is plenty of space.
*
* We must include the 2 bytes immediately preceeding the table when
* we map it, since they contain information about the way the table
* is arranged.
*/
if ((HwCapBuffer = MapFramebuffer(((Registers.Edx << 4) | (Registers.Ebx - 2)), 1024)) == 0)
{
VideoDebugPrint((DEBUG_ERROR, "Can't map hardware capability table at 0x%X:0x%X\n", Registers.Edx, Registers.Ebx));
return ERROR_INSUFFICIENT_BUFFER;
}
/*
* If the value in the CX register was changed, there is a second
* table with supplemental values. According to Arthur Lai, this
* second table will only extend the original table, and never
* detract from it. If this table exists, but we can't map it,
* we can still work with the primrary table rather than treating
* the failure as a fatal error.
*
* While the BIOS will leave the CX register alone if the second
* table doesn't exist, there is no guarantee that Windows NT will
* leave the upper 16 bits of ECX alone.
*/
if ((Registers.Ecx & 0x0000FFFF) == 0xFFFF)
{
HwSupBuffer = 0;
}
else
{
HwSupBuffer = MapFramebuffer(((Registers.Edx << 4) | Registers.Ecx), 1024);
}
HwCapBytesPerRow = VideoPortReadRegisterUchar(HwCapBuffer + 1);
VideoDebugPrint((DEBUG_DETAIL, "Table has %d bytes per row\n", HwCapBytesPerRow));
pmode = (struct st_mode_table *)Query;
((struct query_structure *)pmode)++;
/*
* Initially, we do not know whether to merge our "canned" mode
* tables with tables from an EDID structure returned via DDC,
* or with tables from a VDIF file. If we are dealing with an
* EDID structure, we have not yet read any data, so the initial
* checksum is zero.
*/
phwDeviceExtension->MergeSource = MERGE_UNKNOWN;
phwDeviceExtension->EdidChecksum = 0;
/*
* Search through the returned mode tables, and fill in the query
* structure's mode tables using the information we find there.
*
* DOES NOT ASSUME: Order of mode tables, or number of mode
* tables per resolution.
*/
for (CurrentRes = RES_640; CurrentRes <= RES_1600; CurrentRes++)
{
CxModeTable = (struct cx_mode_table *)(MappedBuffer + VideoPortReadRegisterUshort(&(CxQuery->cx_mode_offset)));
/*
* The list of maximum pixel clock frequencies contains either
* garbage (640x480), or the results for the previous resolution.
* Clear it.
*/
for (Count = DEPTH_NOTHING; Count <= DEPTH_32BPP; Count++)
MaxDotClock[Count] = 0;
/*
* Search through the list of hardware capabilities. If we find
* an entry for the current resolution, the DAC/RAM type is
* correct, and we have enough memory, update the list of
* maximum pixel clock frequencies.
*
* If we have switched to the supplemental table on a previous
* resolution, switch back to the primrary table.
*/
HwCapWalker = HwCapBuffer + 2;
HwCapEntry = (struct cx_hw_cap *)HwCapWalker;
if (FormatType >= FORMAT_DACTYPE)
FormatType -= FORMAT_DACTYPE;
while (VideoPortReadRegisterUchar(&(HwCapEntry->cx_HorRes)) != 0)
{
/*
* Assigning HwCapEntry is redundant on the first pass
* through the loop, but by assigning it and then incrementing
* HwCapWalker at the beginning of the loop it reduces the
* complexity of each "skip this entry because it doesn't
* apply to us" decision point.
*
* A side effect of this is that we will check each entry
* to see if its horizontal resolution is zero (end-of-table
* flag) only after we have examined it in an attempt to
* add its pixel clock data to our list. This is harmless,
* since a horizontal resolution of zero will not match any
* of the resolutions we are looking for, so the check to
* see if the current entry is for the correct resolution
* will always interpret the end-of-table flag as being
* an entry for the wrong resolution, and skip to the next
* entry. This will take us to the top of the loop, where
* we will see that we have hit the end of the table.
*/
HwCapEntry = (struct cx_hw_cap *)HwCapWalker;
HwCapWalker += HwCapBytesPerRow;
/*
* If we have run into the end of the first table and
* the second (supplemental) table exists, switch to
* it. If we have hit the end of the supplemental
* table, the check to see if we're looking at an
* entry corresponding to the desired resolution will
* catch it and get us out of the loop.
*
* The format type returned by the BIOS is the same
* regardless of whether we are working with the
* primrary or supplemental table. Since the primrary
* table uses masks based on the type, while the
* supplemental table requires an exact match, we
* must distinguish between the tables when looking
* at the format type. By making a duplicate set of
* format types for "exact match", with each defined
* value in this set being greater than its "mask"
* counterpart by the number of format types the BIOS
* can return, we can also use the format type to
* determine which table we are working with. The
* DAC-formatted table is the lowest (zero for "mask",
* number of format types for "exact match").
*/
if ((VideoPortReadRegisterUchar(&(HwCapEntry->cx_HorRes)) == 0) &&
(FormatType < FORMAT_DACTYPE))
{
VideoDebugPrint((DEBUG_DETAIL, "Switching to supplemental table\n"));
HwCapWalker = HwSupBuffer;
HwCapEntry = (struct cx_hw_cap *)HwCapWalker;
HwCapWalker += HwCapBytesPerRow;
FormatType += FORMAT_DACTYPE;
}
/*
* Reject entries dealing with resolutions other than the
* one we are interested in. The cx_HorRes field is in units
* of 8 pixels.
*/
Scratch = VideoPortReadRegisterUchar(&(HwCapEntry->cx_HorRes));
if (((CurrentRes == RES_640) && (Scratch != 80)) ||
((CurrentRes == RES_800) && (Scratch != 100)) ||
((CurrentRes == RES_1024) && (Scratch != 128)) ||
((CurrentRes == RES_1152) && (Scratch != 144)) ||
((CurrentRes == RES_1280) && (Scratch != 160)) ||
((CurrentRes == RES_1600) && (Scratch != 200)))
{
VideoDebugPrint((DEBUG_DETAIL, "Incorrect resolution - %d pixels wide\n", (Scratch*8)));
continue;
}
VideoDebugPrint((DEBUG_DETAIL, "Correct resolution"));
/*
* Reject entries which require a DAC or RAM type other
* than that installed on the card.
*
* Reminder - Unlike loops, switch statements are affected
* by "break" but not by "continue".
*/
switch(FormatType)
{
case FORMAT_DACMASK:
if ((VideoPortReadRegisterUchar(&(HwCapEntry->cx_RamOrDacType)) & DacTypeMask) == 0)
{
VideoDebugPrint((DEBUG_DETAIL, " but wrong DAC type (mask)\n"));
continue;
}
break;
case FORMAT_RAMMASK:
/*
* Although the BIOS query structure definition allows bits
* 0 through 3 of the memory type field to be used as a
* memory type identifier, we must use only bits 0 through
* 2 to avoid shifting past the end of the 8-bit mask. Since
* even the ASIC which supports the most memory types (GX)
* only supports 7 types according to my BIOS guide, this
* should not be a problem.
*/
if ((VideoPortReadRegisterUchar(&(HwCapEntry->cx_RamOrDacType)) & (1 << (OrigRamType & 0x07))) == 0)
{
VideoDebugPrint((DEBUG_DETAIL, " but wrong RAM type (mask)\n"));
continue;
}
break;
case FORMAT_DACTYPE:
if (VideoPortReadRegisterUchar(&(HwCapEntry->cx_RamOrDacType)) != OrigDacType)
{
VideoDebugPrint((DEBUG_DETAIL, " but wrong DAC type (exact match)\n"));
continue;
}
break;
case FORMAT_RAMTYPE:
if (VideoPortReadRegisterUchar(&(HwCapEntry->cx_RamOrDacType)) != OrigRamType)
{
VideoDebugPrint((DEBUG_DETAIL, " but wrong RAM type (exact match)\n"));
continue;
}
break;
default:
VideoDebugPrint((DEBUG_ERROR, "\nInvalid format type %d\n", FormatType));
continue;
break;
}
VideoDebugPrint((DEBUG_DETAIL, ", correct DAC/RAM type"));
/*
* Reject entries which require more RAM than is
* installed on the card. The amount of RAM required
* for a given mode may vary between VRAM and DRAM
* cards.
*
* The same RAM type code may represent different
* types of RAM for different Mach 64 ASICs. Since
* only the GX supports VRAM (as of the time of printing
* of my BIOS guide), it is safe to assume that any
* non-GX ASIC is using DRAM.
*/
Scratch = OrigRamType;
if ((INPW(CONFIG_CHIP_ID) == CONFIG_CHIP_ID_TypeGX) &&
((Scratch == 1) ||
(Scratch == 2) ||
(Scratch == 5) ||
(Scratch == 6)))
{
Scratch = VideoPortReadRegisterUchar(&(HwCapEntry->cx_MemReq)) & 0x0F;
}
else /* if (card uses DRAM) */
{
Scratch = VideoPortReadRegisterUchar(&(HwCapEntry->cx_MemReq)) & 0xF0;
Scratch >>= 4;
}
if (Scratch > OrigRamSize)
{
VideoDebugPrint((DEBUG_DETAIL, " but insufficient RAM\n"));
continue;
}
VideoDebugPrint((DEBUG_DETAIL, ", and enough RAM to support the mode\n"));
/*
* We have found an entry corresponding to this card's
* capabilities. For each pixel depth up to and including
* the maximum applicable for this entry, set the maximum
* pixel clock rate to the higher of its current value
* and the value for this entry.
*
* We must mask off the high bit of the maximum pixel depth
* because it is a flag which is irrelevant for our purposes.
*/
Scratch = VideoPortReadRegisterUchar(&(HwCapEntry->cx_MaxPixDepth)) & 0x7F;
for (CurrentDepth = DEPTH_NOTHING; CurrentDepth <= Scratch; CurrentDepth++)
{
if (VideoPortReadRegisterUchar(&(HwCapEntry->cx_MaxDotClock)) > MaxDotClock[CurrentDepth])
{
MaxDotClock[CurrentDepth] = VideoPortReadRegisterUchar(&(HwCapEntry->cx_MaxDotClock));
VideoDebugPrint((DEBUG_DETAIL, "Increased MaxDotClock[%d] to %d MHz\n", CurrentDepth, MaxDotClock[CurrentDepth]));
}
}
} /* end while (more entries in hardware capability table) */
/*
* On some cards, the BIOS will report in AX=0xA?07 maximum pixel
* clock rates for pixel depths which AX=0xA?09 byte 0x13 reports
* as unsupported. Since switching into these modes will produce
* bizarre displays, we must mark these pixel depths as unavailable.
*/
switch (AbsMaxDepth)
{
case 8:
VideoDebugPrint((DEBUG_DETAIL, "Forcing cutback to 8BPP maximum\n"));
MaxDotClock[DEPTH_16BPP] = 0;
MaxDotClock[DEPTH_24BPP] = 0;
MaxDotClock[DEPTH_32BPP] = 0;
break;
case 16:
VideoDebugPrint((DEBUG_DETAIL, "Forcing cutback to 16BPP maximum\n"));
MaxDotClock[DEPTH_24BPP] = 0;
MaxDotClock[DEPTH_32BPP] = 0;
break;
case 24:
VideoDebugPrint((DEBUG_DETAIL, "Forcing cutback to 24BPP maximum\n"));
MaxDotClock[DEPTH_32BPP] = 0;
break;
case 32:
default:
VideoDebugPrint((DEBUG_DETAIL, "No forced cutback needed\n"));
break;
}
/*
* Our new source stream display driver needs a linear aperture
* in order to handle 24BPP. Since the display driver doesn't
* have access to the aperture information when it is deciding
* which modes to pass on to the display applet, it can't make
* the decision to reject 24BPP modes for cards with only a
* VGA aperture. This decision must therefore be made in the
* miniport, so in a paged aperture configuration there are no
* 24BPP modes for the display driver to accept or reject.
*
* On the DEC Alpha, we treat machines using sparse space as
* a synthetic no-aperture case even if the LFB is enabled,
* so we must lock out 24BPP on these machines as well.
*/
if (Query->q_aperture_cfg == 0)
{
VideoDebugPrint((DEBUG_DETAIL, "24BPP not available because we don't have a linear aperture\n"));
MaxDotClock[DEPTH_24BPP] = 0;
}
#if defined(ALPHA)
if (DenseOnAlpha(Query) == FALSE)
{
VideoDebugPrint((DEBUG_DETAIL, "24BPP not available in sparse space on Alpha\n"));
MaxDotClock[DEPTH_24BPP] = 0;
}
#endif
VideoDebugPrint((DEBUG_NORMAL, "Horizontal resolution = %d\n", CXHorRes[CurrentRes]));
VideoDebugPrint((DEBUG_NORMAL, "Maximum dot clock for 4BPP = %d MHz\n", MaxDotClock[DEPTH_4BPP]));
VideoDebugPrint((DEBUG_NORMAL, "Maximum dot clock for 8BPP = %d MHz\n", MaxDotClock[DEPTH_8BPP]));
VideoDebugPrint((DEBUG_NORMAL, "Maximum dot clock for 16BPP = %d MHz\n", MaxDotClock[DEPTH_16BPP]));
VideoDebugPrint((DEBUG_NORMAL, "Maximum dot clock for 24BPP = %d MHz\n", MaxDotClock[DEPTH_24BPP]));
VideoDebugPrint((DEBUG_NORMAL, "Maximum dot clock for 32BPP = %d MHz\n", MaxDotClock[DEPTH_32BPP]));
/*
* Search through the list of installed mode tables to see if there
* are any for the current resolution. We need this information
* in order to decide whether or not to make the hardware default
* refresh rate available for this resolution (BIOS behaviour is
* undefined when trying to load CRT parameters for the hardware
* default refresh rate at a given resolution if that resolution
* is not among the installed modes).
*/
ModeInstalled = FALSE;
for (Count = 1; Count <= VideoPortReadRegisterUchar(&(CxQuery->cx_number_modes)); Count++)
{
/*
* If the current mode table matches the resolution we are
* looking for, then we know that there is a hardware
* default refresh rate available for this resolution.
* Since we only need to find one such mode table, there
* is no need to search the remainder of the mode tables.
*/
if (VideoPortReadRegisterUshort(&(CxModeTable->cx_x_size)) == CXHorRes[CurrentRes])
{
ModeInstalled = TRUE;
VideoDebugPrint((DEBUG_DETAIL, "%d table found\n", CXHorRes[CurrentRes]));
break;
}
((PUCHAR)CxModeTable) += VideoPortReadRegisterUchar(&(CxQuery->cx_mode_size));
}
/*
* The MaxDotClock[] entry for any pixel depth will
* contain either the maximum pixel clock for that
* pixel depth at the current resolution, or zero
* if that pixel depth is not supported at the
* current resolution. For any resolution, the
* maximum supported pixel clock rate will either
* remain the same or decrease as the pixel depth
* increases, but it will never increase.
*
* The pixel clock rate for 4BPP (lowest pixel depth
* we support) will only be zero if the card does not
* support the current resolution. If this is the case,
* skip to the next resolution.
*/
if (MaxDotClock[DEPTH_4BPP] == 0)
{
VideoDebugPrint((DEBUG_NORMAL, "Current resolution not supported on this card - skipping to next.\n"));
continue;
}
Query->q_status_flags |= CXStatusFlags[CurrentRes];
VideoPortZeroMemory(&ThisRes, sizeof(struct st_mode_table));
/*
* Replace the "canned" mode tables with the Mach 64 versions
* in cases where the Mach 64 needs CRT parameters the
* Mach 8 and Mach 32 can't handle.
*/
SetMach64Tables();
/*
* Set up the ranges of "canned" mode tables to use for each
* resolution. Initially assume that all tables at the desired
* resolution are available, later we will cut out those that
* are unavailable because the DAC and/or memory type doesn't
* support them at specific resolutions.
*/
switch (CurrentRes)
{
case RES_640:
StartIndex = B640F60;
EndIndex = B640F100;
ThisRes.m_x_size = 640;
ThisRes.m_y_size = 480;
break;
case RES_800:
StartIndex = B800F89;
EndIndex = B800F100;
ThisRes.m_x_size = 800;
ThisRes.m_y_size = 600;
break;
case RES_1024:
StartIndex = B1024F87;
EndIndex = B1024F100;
ThisRes.m_x_size = 1024;
ThisRes.m_y_size = 768;
break;
case RES_1152:
StartIndex = B1152F87;
EndIndex = B1152F80;
ThisRes.m_x_size = 1152;
ThisRes.m_y_size = 864;
break;
case RES_1280:
StartIndex = B1280F87;
EndIndex = B1280F75;
ThisRes.m_x_size = 1280;
ThisRes.m_y_size = 1024;
break;
case RES_1600:
StartIndex = B1600F60;
EndIndex = B1600F76;
ThisRes.m_x_size = 1600;
ThisRes.m_y_size = 1200;
break;
}
/*
* Use a screen pitch equal to the horizontal resolution for
* linear aperture, and of 1024 or the horizontal resolution
* (whichever is higher) for VGA aperture.
*/
ThisRes.m_screen_pitch = ThisRes.m_x_size;
#if !defined (SPLIT_RASTERS)
if (((Query->q_aperture_cfg & BIOS_AP_SIZEMASK) == 0) &&
(ThisRes.m_x_size < 1024))
ThisRes.m_screen_pitch = 1024;
/*
* Temporary until split rasters implemented.
*/
if (((Query->q_aperture_cfg & BIOS_AP_SIZEMASK) == 0) &&
(ThisRes.m_x_size > 1024))
ThisRes.m_screen_pitch = 2048;
#endif
/*
* Get the parameters we need out of the table returned
* by the BIOS call.
*/
ThisRes.m_h_total = VideoPortReadRegisterUchar(&(CxModeTable->cx_crtc_h_total));
ThisRes.m_h_disp = VideoPortReadRegisterUchar(&(CxModeTable->cx_crtc_h_disp));
ThisRes.m_h_sync_strt = VideoPortReadRegisterUchar(&(CxModeTable->cx_crtc_h_sync_strt));
ThisRes.m_h_sync_wid = VideoPortReadRegisterUchar(&(CxModeTable->cx_crtc_h_sync_wid));
ThisRes.m_v_total = VideoPortReadRegisterUshort(&(CxModeTable->cx_crtc_v_total));
ThisRes.m_v_disp = VideoPortReadRegisterUshort(&(CxModeTable->cx_crtc_v_disp));
ThisRes.m_v_sync_strt = VideoPortReadRegisterUshort(&(CxModeTable->cx_crtc_v_sync_strt));
ThisRes.m_v_sync_wid = VideoPortReadRegisterUchar(&(CxModeTable->cx_crtc_v_sync_wid));
ThisRes.m_h_overscan = VideoPortReadRegisterUshort(&(CxModeTable->cx_h_overscan));
ThisRes.m_v_overscan = VideoPortReadRegisterUshort(&(CxModeTable->cx_v_overscan));
ThisRes.m_overscan_8b = VideoPortReadRegisterUshort(&(CxModeTable->cx_overscan_8b));
ThisRes.m_overscan_gr = VideoPortReadRegisterUshort(&(CxModeTable->cx_overscan_gr));
ThisRes.m_clock_select = VideoPortReadRegisterUchar(&(CxModeTable->cx_clock_cntl));
ThisRes.control = VideoPortReadRegisterUshort(&(CxModeTable->cx_crtc_gen_cntl));
ThisRes.Refresh = DEFAULT_REFRESH;
/*
* For each supported pixel depth at the given resolution,
* copy the mode table, fill in the colour depth field,
* and increment the counter for the number of supported modes.
* Test 4BPP before 8BPP so the mode tables will appear in
* increasing order of pixel depth.
*
* If filling in the mode table would overflow the space available
* for mode tables, return the appropriate error code instead
* of continuing.
*
* All the DACs we support can handle 8 BPP at all the
* resolutions they support if there is enough memory on
* the card, and all but the 68860, IBM514, and TVP3026
* can support 4BPP under the same circumstances. If a
* DAC doesn't support a given resolution (e.g. 1600x1200),
* the MaxDotClock[] array will be zero for the resolution,
* and the INSTALL program won't set up any mode tables for
* that resolution. This will result in a kick-out at an
* earlier point in the code (when we found that 4BPP has a
* maximum pixel clock rate of zero), so we will never reach
* this point on resolutions the DAC doesn't support.
*
* 4BPP is only needed for resolutions where we don't have
* enough video memory to support 8BPP. At Microsoft's request,
* we must lock out 4BPP for resolutions where we can support
* 8BPP. We only support 4BPP on 1M cards since a BIOS quirk
* on some cards requires that we set the memory size to 1M
* when we switch into 4BPP. The DACs where we lock out 4BPP
* unconditionally are only found on VRAM cards, where the
* minimum configuration is 2M.
*/
NumPixels = ThisRes.m_screen_pitch * ThisRes.m_y_size;
if((NumPixels < ONE_MEG*2) &&
((MemAvail == ONE_MEG) && (NumPixels >= ONE_MEG)) &&
(MaxDotClock[DEPTH_4BPP] > 0) &&
(Query->q_DAC_type != DAC_ATI_68860) &&
(Query->q_DAC_type != DAC_TVP3026) &&
(Query->q_DAC_type != DAC_IBM514))
{
if (ModeInstalled)
{
if (Query->q_number_modes >= MaxModes)
{
VideoDebugPrint((DEBUG_ERROR, "Exceeded maximum allowable number of modes - aborting query\n"));
CleanupQuery(HwCapBuffer, HwSupBuffer, MappedBuffer, BufferSeg, SavedVgaBuffer);
return ERROR_INSUFFICIENT_BUFFER;
}
VideoPortMoveMemory(pmode, &ThisRes, sizeof(struct st_mode_table));
pmode->m_pixel_depth = 4;
pmode++; /* ptr to next mode table */
Query->q_number_modes++;
}
/*
* Add "canned" mode tables after verifying that the
* worst case (all possible "canned" modes can actually
* be loaded) won't exceed the maximum possible number
* of mode tables.
*/
if ((FreeTables = MaxModes - Query->q_number_modes) <= 0)
{
VideoDebugPrint((DEBUG_ERROR, "Exceeded maximum allowable number of modes - aborting query\n"));
CleanupQuery(HwCapBuffer, HwSupBuffer, MappedBuffer, BufferSeg, SavedVgaBuffer);
return ERROR_INSUFFICIENT_BUFFER;
}
Query->q_number_modes += SetFixedModes(StartIndex,
EndIndex,
CLOCK_SINGLE,
4,
ThisRes.m_screen_pitch,
FreeTables,
(ULONG)(MaxDotClock[DEPTH_4BPP] * 1000000L),
&pmode);
}
if ((NumPixels < MemAvail) &&
(MaxDotClock[DEPTH_8BPP] > 0))
{
/*
* On some Mach 64 cards (depends on ASIC revision, RAM type,
* and DAC type), screen tearing will occur in 8BPP if the
* pitch is not a multiple of 64 pixels (800x600 is the only
* resolution where this is possible).
*
* If the pitch has already been boosted to 1024 (VGA aperture
* with no split rasters), it is already a multiple of 64, so
* no change is needed.
*/
if (ThisRes.m_screen_pitch == 800)
ThisRes.m_screen_pitch = 832;
if (ModeInstalled)
{
if (Query->q_number_modes >= MaxModes)
{
VideoDebugPrint((DEBUG_ERROR, "Exceeded maximum allowable number of modes - aborting query\n"));
CleanupQuery(HwCapBuffer, HwSupBuffer, MappedBuffer, BufferSeg, SavedVgaBuffer);
return ERROR_INSUFFICIENT_BUFFER;
}
VideoPortMoveMemory(pmode, &ThisRes, sizeof(struct st_mode_table));
pmode->m_pixel_depth = 8;
pmode++; /* ptr to next mode table */
Query->q_number_modes++;
}
/*
* Add "canned" mode tables after verifying that the
* worst case (all possible "canned" modes can actually
* be loaded) won't exceed the maximum possible number
* of mode tables.
*/
if ((FreeTables = MaxModes - Query->q_number_modes) <= 0)
{
VideoDebugPrint((DEBUG_ERROR, "Exceeded maximum allowable number of modes - aborting query\n"));
CleanupQuery(HwCapBuffer, HwSupBuffer, MappedBuffer, BufferSeg, SavedVgaBuffer);
return ERROR_INSUFFICIENT_BUFFER;
}
Query->q_number_modes += SetFixedModes(StartIndex,
EndIndex,
CLOCK_SINGLE,
8,
ThisRes.m_screen_pitch,
FreeTables,
(ULONG)(MaxDotClock[DEPTH_8BPP] * 1000000L),
&pmode);
/*
* If we have boosted the screen pitch to avoid tearing,
* cut it back to normal, since the boost is only needed
* in 8BPP. We will only have a pitch of 832 in 800x600
* with the pitch boost in place.
*/
if (ThisRes.m_screen_pitch == 832)
ThisRes.m_screen_pitch = 800;
}
if ((NumPixels*2 < MemAvail) &&
(MaxDotClock[DEPTH_16BPP] > 0))
{
if (ModeInstalled)
{
if (Query->q_number_modes >= MaxModes)
{
VideoDebugPrint((DEBUG_ERROR, "Exceeded maximum allowable number of modes - aborting query\n"));
CleanupQuery(HwCapBuffer, HwSupBuffer, MappedBuffer, BufferSeg, SavedVgaBuffer);
return ERROR_INSUFFICIENT_BUFFER;
}
VideoPortMoveMemory(pmode, &ThisRes, sizeof(struct st_mode_table));
pmode->m_pixel_depth = 16;
pmode++; /* ptr to next mode table */
Query->q_number_modes++;
}
/*
* Add "canned" mode tables after verifying that the
* worst case (all possible "canned" modes can actually
* be loaded) won't exceed the maximum possible number
* of mode tables.
*/
if ((FreeTables = MaxModes - Query->q_number_modes) <= 0)
{
VideoDebugPrint((DEBUG_ERROR, "Exceeded maximum allowable number of modes - aborting query\n"));
CleanupQuery(HwCapBuffer, HwSupBuffer, MappedBuffer, BufferSeg, SavedVgaBuffer);
return ERROR_INSUFFICIENT_BUFFER;
}
Query->q_number_modes += SetFixedModes(StartIndex,
EndIndex,
CLOCK_SINGLE,
16,
ThisRes.m_screen_pitch,
FreeTables,
(ULONG)(MaxDotClock[DEPTH_16BPP] * 1000000L),
&pmode);
}
if ((NumPixels*3 < MemAvail) &&
(MaxDotClock[DEPTH_24BPP] > 0))
{
if (ModeInstalled)
{
if (Query->q_number_modes >= MaxModes)
{
VideoDebugPrint((DEBUG_ERROR, "Exceeded maximum allowable number of modes - aborting query\n"));
CleanupQuery(HwCapBuffer, HwSupBuffer, MappedBuffer, BufferSeg, SavedVgaBuffer);
return ERROR_INSUFFICIENT_BUFFER;
}
VideoPortMoveMemory(pmode, &ThisRes, sizeof(struct st_mode_table));
pmode->m_pixel_depth = 24;
pmode++; /* ptr to next mode table */
Query->q_number_modes++;
}
/*
* Add "canned" mode tables after verifying that the
* worst case (all possible "canned" modes can actually
* be loaded) won't exceed the maximum possible number
* of mode tables.
*/
if ((FreeTables = MaxModes - Query->q_number_modes) <= 0)
{
VideoDebugPrint((DEBUG_ERROR, "Exceeded maximum allowable number of modes - aborting query\n"));
CleanupQuery(HwCapBuffer, HwSupBuffer, MappedBuffer, BufferSeg, SavedVgaBuffer);
return ERROR_INSUFFICIENT_BUFFER;
}
Query->q_number_modes += SetFixedModes(StartIndex,
EndIndex,
CLOCK_SINGLE,
24,
ThisRes.m_screen_pitch,
FreeTables,
(ULONG)(MaxDotClock[DEPTH_24BPP] * 1000000L),
&pmode);
}
if ((NumPixels*4 < MemAvail) &&
(MaxDotClock[DEPTH_32BPP] > 0))
{
if (ModeInstalled)
{
if (Query->q_number_modes > MaxModes)
{
VideoDebugPrint((DEBUG_ERROR, "Exceeded maximum allowable number of modes - aborting query\n"));
CleanupQuery(HwCapBuffer, HwSupBuffer, MappedBuffer, BufferSeg, SavedVgaBuffer);
return ERROR_INSUFFICIENT_BUFFER;
}
VideoPortMoveMemory(pmode, &ThisRes, sizeof(struct st_mode_table));
pmode->m_pixel_depth = 32;
pmode++; /* ptr to next mode table */
Query->q_number_modes++;
}
/*
* Add "canned" mode tables after verifying that the
* worst case (all possible "canned" modes can actually
* be loaded) won't exceed the maximum possible number
* of mode tables.
*/
if ((FreeTables = MaxModes - Query->q_number_modes) <= 0)
{
VideoDebugPrint((DEBUG_ERROR, "Exceeded maximum allowable number of modes - aborting query\n"));
CleanupQuery(HwCapBuffer, HwSupBuffer, MappedBuffer, BufferSeg, SavedVgaBuffer);
return ERROR_INSUFFICIENT_BUFFER;
}
Query->q_number_modes += SetFixedModes(StartIndex,
EndIndex,
CLOCK_SINGLE,
32,
ThisRes.m_screen_pitch,
FreeTables,
(ULONG)(MaxDotClock[DEPTH_32BPP] * 1000000L),
&pmode);
}
} /* end for */
Query->q_sizeof_struct = Query->q_number_modes * sizeof(struct st_mode_table) + sizeof(struct query_structure);
CleanupQuery(HwCapBuffer, HwSupBuffer, MappedBuffer, BufferSeg, SavedVgaBuffer);
return NO_ERROR;
} /* QueryMach64() */
/***************************************************************************
*
* BOOL BlockWriteAvail_cx(Query);
*
* struct query_structure *Query; Query information for the card
*
* DESCRIPTION:
* Test to see whether block write mode is available. This function
* assumes that the card has been set to an accelerated mode.
*
* RETURN VALUE:
* TRUE if this mode is available
* FALSE if it is not available
*
* GLOBALS CHANGED:
* None
*
* CALLED BY:
* IOCTL_VIDEO_SET_CURRENT_MODE packet of ATIMPStartIO()
*
* AUTHOR:
* Robert Wolff
*
* CHANGE HISTORY:
*
* TEST HISTORY:
*
***************************************************************************/
#define BLOCK_WRITE_LENGTH 120
BOOL BlockWriteAvail_cx(struct query_structure *Query)
{
BOOL RetVal = TRUE;
ULONG ColourMask; /* Mask off unneeded bits of Colour */
ULONG Colour; /* Colour to use in testing */
USHORT Width, excess = 8; /* Width of test block */
USHORT Column; /* Column being checked */
ULONG ScreenPitch; /* Pitch in units of 8 pixels */
ULONG PixelDepth; /* Colour depth of screen */
ULONG HorScissors; /* Horizontal scissor values */
PULONG FrameAddress; /* Pointer to base of LFB */
PULONG ReadPointer; /* Used in reading test block */
ULONG DstOffPitch; /* Saved contents of DST_OFF_PITCH register */
#if defined (PPC)
/*
* Block write does not work properly on the power PC. Under some
* circumstances, we will detect that the card is capable of using
* block write mode, but it will hang the machine when used for
* a large block (our test is for a small block).
*/
VideoDebugPrint((DEBUG_DETAIL, "Can't do block write on a PPC\n"));
return FALSE;
#else
/*
* Our block write test involves an engine draw followed by
* a read back through the linear framebuffer. If the linear
* framebuffer is unavailable, assume that we can't do block
* write, since all our cards are able to function without
* block write.
*/
if (!(Query->q_aperture_cfg))
{
VideoDebugPrint((DEBUG_DETAIL, "LFB unavailable, can't do block write check\n"));
return FALSE;
}
/*
* Mach 64 ASICs prior to revision D have a hardware bug that does
* not allow transparent block writes (special handling is required
* that in some cases can cut performance).
*/
if ((INPD(CONFIG_CHIP_ID) & CONFIG_CHIP_ID_RevMask) < CONFIG_CHIP_ID_RevD)
{
VideoDebugPrint((DEBUG_DETAIL, "ASIC/memory combination doesn't allow block write\n"));
return FALSE;
}
/*
* Block write is only available on "special VRAM" cards.
*/
if (Query->q_memory_type != VMEM_VRAM_256Kx4_SPLIT512
&& Query->q_memory_type != VMEM_VRAM_256Kx16_SPLIT256)
{
VideoDebugPrint((DEBUG_DETAIL, "*** No block write - wrong RAM type\n" ));
return FALSE;
}
/*
* Special case: block write doesn't work properly on the
* GX rev. E with IBM RAM.
*/
if ((INPD(CONFIG_CHIP_ID) == CONFIG_CHIP_ID_GXRevE) &&
(Query->q_memory_type == VMEM_VRAM_256Kx16_SPLIT256))
{
VideoDebugPrint((DEBUG_DETAIL, "*** No block write - GX/E with IBM RAM\n"));
return FALSE;
}
/*
* Use a 480 byte test block. This size will fit on a single line
* even at the lowest resolution (640x480) and pixel depth supported
* by the display driver (8BPP), and is divisible by all the supported
* pixel depths. Get the depth-specific values for the pixel depth we
* are using.
*
* True 24BPP acceleration is not available, so 24BPP is actually
* handled as an 8BPP engine mode with a width 3 times the display
* width.
*/
switch(Query->q_pix_depth)
{
case 4:
ColourMask = 0x0000000F;
Width = BLOCK_WRITE_LENGTH*8;
ScreenPitch = Query->q_screen_pitch / 8;
PixelDepth = BIOS_DEPTH_4BPP;
HorScissors = (Query->q_desire_x) << 16;
break;
case 8:
ColourMask = 0x000000FF;
Width = BLOCK_WRITE_LENGTH*4;
ScreenPitch = Query->q_screen_pitch / 8;
PixelDepth = BIOS_DEPTH_8BPP;
HorScissors = (Query->q_desire_x) << 16;
break;
case 16:
ColourMask = 0x0000FFFF;
Width = BLOCK_WRITE_LENGTH*2;
ScreenPitch = Query->q_screen_pitch / 8;
PixelDepth = BIOS_DEPTH_16BPP_565;
HorScissors = (Query->q_desire_x) << 16;
break;
case 24:
ColourMask = 0x000000FF;
Width = BLOCK_WRITE_LENGTH*4;
ScreenPitch = (Query->q_screen_pitch * 3) / 8;
PixelDepth = BIOS_DEPTH_8BPP;
/*
* Horizontal scissors are only valid in the range
* -4096 to +4095. If the horizontal resolution
* is high enough to put the scissor outside this
* range, clamp the scissors to the maximum
* permitted value.
*/
HorScissors = Query->q_desire_x * 3;
if (HorScissors > 4095)
HorScissors = 4095;
HorScissors <<= 16;
break;
case 32:
ColourMask = 0xFFFFFFFF;
Width = BLOCK_WRITE_LENGTH;
ScreenPitch = Query->q_screen_pitch / 8;
PixelDepth = BIOS_DEPTH_32BPP;
HorScissors = (Query->q_desire_x) << 16;
break;
default:
return FALSE; /* Unsupported pixel depths */
}
/*
* Get a pointer to the beginning of the framebuffer. If we
* can't do this, assume block write is unavailable.
*/
if ((FrameAddress = MapFramebuffer(phwDeviceExtension->PhysicalFrameAddress.LowPart,
phwDeviceExtension->FrameLength)) == (PVOID) 0)
{
VideoDebugPrint((DEBUG_ERROR, "Couldn't map LFB - assuming no block write\n"));
return FALSE;
}
/*
* To use block write mode, the pixel widths for destination,
* source, and host must be the same.
*/
PixelDepth |= ((PixelDepth << 8) | (PixelDepth << 16));
/*
* Save the contents of the DST_OFF_PITCH register.
*/
DstOffPitch = INPD(DST_OFF_PITCH);
/*
* Clear the block we will be testing.
*/
CheckFIFOSpace_cx(ELEVEN_WORDS);
OUTPD(DP_WRITE_MASK, 0xFFFFFFFF);
OUTPD(DST_OFF_PITCH, ScreenPitch << 22);
OUTPD(DST_CNTL, (DST_CNTL_XDir | DST_CNTL_YDir));
OUTPD(DP_PIX_WIDTH, PixelDepth);
OUTPD(DP_SRC, (DP_FRGD_SRC_FG | DP_BKGD_SRC_BG | DP_MONO_SRC_ONE));
OUTPD(DP_MIX, ((MIX_FN_PAINT << 16) | MIX_FN_PAINT));
OUTPD(DP_FRGD_CLR, 0);
OUTPD(SC_LEFT_RIGHT, HorScissors);
OUTPD(SC_TOP_BOTTOM, (Query->q_desire_y) << 16);
OUTPD(DST_Y_X, 0);
OUTPD(DST_HEIGHT_WIDTH, ((Width+excess) << 16) | 1);
WaitForIdle_cx();
/*
* To test block write mode, try painting each of the alternating bit
* patterns, then read the block back. If there is at least one
* mismatch, then block write is not supported.
*/
for (Colour = 0x55555555; Colour <= 0xAAAAAAAA; Colour += 0x55555555)
{
/*
* Paint the block.
*/
CheckFIFOSpace_cx(FIVE_WORDS);
OUTPD(GEN_TEST_CNTL, (INPD(GEN_TEST_CNTL) | GEN_TEST_CNTL_BlkWrtEna));
OUTPD(DP_MIX, ((MIX_FN_PAINT << 16) | MIX_FN_LEAVE_ALONE));
OUTPD(DP_FRGD_CLR, (Colour & ColourMask));
OUTPD(DST_Y_X, 0);
OUTPD(DST_HEIGHT_WIDTH, (Width << 16) | 1);
WaitForIdle_cx();
/*
* Check to see if the block was written properly. Mach 64 cards
* can't do a screen to host blit, but we can read the test block
* back through the aperture.
*/
ReadPointer = FrameAddress;
for (Column = 0; Column < BLOCK_WRITE_LENGTH; Column++)
{
if (VideoPortReadRegisterUlong(ReadPointer + Column) != Colour)
{
VideoDebugPrint((DEBUG_NORMAL, "*** No block write - bad pattern\n" ));
RetVal = FALSE;
break;
}
}
/*
* Check the next dword beyond the block.
*/
if (VideoPortReadRegisterUlong(ReadPointer + BLOCK_WRITE_LENGTH) != 0)
{
VideoDebugPrint((DEBUG_NORMAL, "*** No block write - corruption\n" ));
RetVal = FALSE;
}
}
/*
* If block write is unavailable, turn off the block write bit.
*/
if (RetVal == FALSE)
OUTPD(GEN_TEST_CNTL, (INPD(GEN_TEST_CNTL) & ~GEN_TEST_CNTL_BlkWrtEna));
/*
* Restore the contents of the DST_OFF_PITCH register.
*/
OUTPD(DST_OFF_PITCH, DstOffPitch);
/*
* Free the pointer to the start of the framebuffer.
*/
VideoPortFreeDeviceBase(phwDeviceExtension, FrameAddress);
return RetVal;
#endif /* Not Power PC */
} /* BlockWriteAvail_cx() */
/***************************************************************************
*
* BOOL TextBanding_cx(Query);
*
* struct query_structure *Query; Query information for the card
*
* DESCRIPTION:
* Test to see whether the current mode is susceptible to text
* banding. This function assumes that the card has been set to
* an accelerated mode.
*
* RETURN VALUE:
* TRUE if this mode is susceptible to text banding
* FALSE if it is immune to text banding
*
* GLOBALS CHANGED:
* None
*
* CALLED BY:
* IOCTL_VIDEO_ATI_GET_MODE_INFORMATION packet of ATIMPStartIO()
*
* AUTHOR:
* Robert Wolff
*
* CHANGE HISTORY:
*
* TEST HISTORY:
*
***************************************************************************/
BOOL TextBanding_cx(struct query_structure *Query)
{
DWORD ConfigChipId;
ConfigChipId = INPD(CONFIG_CHIP_ID);
/*
* Text banding only occurs in 24BPP with the Mach 64
* GX rev. E & rev. F ASICs.
*/
if ((Query->q_pix_depth == 24) &&
((ConfigChipId == CONFIG_CHIP_ID_GXRevE) || (ConfigChipId == CONFIG_CHIP_ID_GXRevF)))
{
return TRUE;
}
else
{
return FALSE;
}
} /* TextBanding_cx() */
/***************************************************************************
*
* PWSTR IdentifyMach64Asic(Query, AsicStringLength);
*
* struct query_structure *Query; Query information for the card
* PULONG AsicStringLength; Length of ASIC identification string
*
* DESCRIPTION:
* Generate a string describing which Mach 64 ASIC is in use on
* this particular card.
*
* RETURN VALUE:
* Pointer to a string identifying which Mach 64 ASIC is present. The
* length of this string is returned in *AsicStringLength.
*
* GLOBALS CHANGED:
* None
*
* CALLED BY:
* FillInRegistry()
*
* AUTHOR:
* Robert Wolff
*
* CHANGE HISTORY:
*
* TEST HISTORY:
*
***************************************************************************/
PWSTR IdentifyMach64Asic(struct query_structure *Query, PULONG AsicStringLength)
{
PWSTR ChipString; /* Identification string for the ASIC in use */
DWORD ConfigChipId; /* Contents of chip identification register */
ConfigChipId = INPD(CONFIG_CHIP_ID);
if (Query->q_DAC_type == DAC_INTERNAL_CT)
{
ChipString = L"Mach 64 CT";
*AsicStringLength = sizeof(L"Mach 64 CT");
}
else if (Query->q_DAC_type == DAC_INTERNAL_GT)
{
ChipString = L"Mach 64 GT";
*AsicStringLength = sizeof(L"Mach 64 GT");
}
else if (Query->q_DAC_type == DAC_INTERNAL_VT)
{
ChipString = L"Mach 64 VT";
*AsicStringLength = sizeof(L"Mach 64 VT");
}
else if ((ConfigChipId & CONFIG_CHIP_ID_TypeMask) == CONFIG_CHIP_ID_TypeCX)
{
ChipString = L"Mach 64 CX";
*AsicStringLength = sizeof(L"Mach 64 CX");
}
else if ((ConfigChipId & CONFIG_CHIP_ID_TypeMask) == CONFIG_CHIP_ID_TypeGX)
{
switch(ConfigChipId & CONFIG_CHIP_ID_RevMask)
{
case CONFIG_CHIP_ID_RevC:
ChipString = L"Mach 64 GX Rev. C";
*AsicStringLength = sizeof(L"Mach 64 GX Rev. C");
break;
case CONFIG_CHIP_ID_RevD:
ChipString = L"Mach 64 GX Rev. D";
*AsicStringLength = sizeof(L"Mach 64 GX Rev. D");
break;
case CONFIG_CHIP_ID_RevE:
ChipString = L"Mach 64 GX Rev. E";
*AsicStringLength = sizeof(L"Mach 64 GX Rev. E");
break;
case CONFIG_CHIP_ID_RevF:
ChipString = L"Mach 64 GX Rev. F";
*AsicStringLength = sizeof(L"Mach 64 GX Rev. F");
break;
default:
ChipString = L"Mach 64 GX";
*AsicStringLength = sizeof(L"Mach 64 GX");
break;
}
}
else
{
ChipString = L"Miscelaneous Mach 64";
*AsicStringLength = sizeof(L"Miscelaneous Mach 64");
}
return ChipString;
} /* IdentifyMach64Asic() */
/***************************************************************************
*
* void CleanupQuery(CapBuffer, SupBuffer, MappedBuffer, BufferSeg, SavedScreen);
*
* PUCHAR CapBuffer; Pointer to the main capabilities table
* for the card
* PUCHAR SupBuffer; Pointer to the supplementary capabilities
* table for the card
* PUCHAR MappedBuffer; Pointer to the buffer used to query the
* card's capabilities
* long BufferSeg; Physical segment associated with MappedBuffer
* PUCHAR SavedScreen; Buffer containing data to be restored to the
* memory region used to store the query data.
* Depending on the buffer used, this data may
* or may not need to be restored.
*
* DESCRIPTION:
* Clean up after we have finished querying the card by restoring
* the VGA screen if needed, then freeing the buffers we used to query
* the card. We only need to restore the VGA screen if we used the
* graphics screen (either write back the information we saved if we
* used the existing screen, or switch into text mode if we had to
* switch into graphics mode) since we use the offscreen portion of
* video memory in cases where we use the text screen.
*
*
* GLOBALS CHANGED:
* None
*
* CALLED BY:
* QueryMach64()
*
* AUTHOR:
* Robert Wolff
*
* CHANGE HISTORY:
*
* TEST HISTORY:
*
***************************************************************************/
static void CleanupQuery(PUCHAR CapBuffer, PUCHAR SupBuffer, PUCHAR MappedBuffer, long BufferSeg, PUCHAR SavedScreen)
{
VIDEO_X86_BIOS_ARGUMENTS Registers; /* Used in VideoPortInt10() calls */
ULONG CurrentByte; /* Buffer byte being restored */
ULONG BytesToRestore; /* Number of bytes of graphics screen to restore */
/*
* BufferSeg will be 0xBA00 if we stored our query information on
* the VGA colour text screen, 0xB200 if we used the VGA mono text
* screen, 0xA000 if we switched into accelerator mode withoug
* disturbing the VGA controller, and 0xA100 if we forced a VGA
* graphics mode in order to use the VGA graphics screen.
*
* Since we use the offscreen portion of the text screens, which
* leaves the information displayed on boot undisturbed, it is not
* only unnecessary but also undesirable (since this would destroy pre-
* query information printed to the blue screen) to change modes.
* If we used the existing graphics screen, we merely need to restore
* the screen contents and the registers we changed. If we changed
* into a graphics mode, the pre-query information has already been
* lost when changed modes, but switching back to text mode should
* allow the user to see information that is printed after our query
* is complete (not guarranteed, since we will only need to do this
* on extremely ill-behaved systems, which may have been using something
* other than a standard VGA text screen as the blue screen).
*/
if (BufferSeg == 0xA000)
{
BytesToRestore = SavedScreen[VGA_SAVE_SIZE_H];
BytesToRestore <<= 8;
BytesToRestore += SavedScreen[VGA_SAVE_SIZE];
VideoDebugPrint((DEBUG_NORMAL, "Restoring %d bytes of the VGA graphics screen\n", BytesToRestore));
for (CurrentByte = 0; CurrentByte < BytesToRestore; CurrentByte++)
{
VideoPortWriteRegisterUchar(&(MappedBuffer[CurrentByte]), SavedScreen[CurrentByte]);
}
OUTP(VGA_SEQ_IND, 2);
OUTP(VGA_SEQ_DATA, SavedScreen[VGA_SAVE_SEQ02]);
OUTP(VGA_GRAX_IND, 8);
OUTP(VGA_GRAX_DATA, SavedScreen[VGA_SAVE_GRA08]);
OUTP(VGA_GRAX_IND, 1);
OUTP(VGA_GRAX_DATA, SavedScreen[VGA_SAVE_GRA01]);
}
else if (BufferSeg == 0xA100)
{
VideoDebugPrint((DEBUG_NORMAL, "Switching back to VGA text mode\n"));
VideoPortZeroMemory(&Registers, sizeof(VIDEO_X86_BIOS_ARGUMENTS));
Registers.Eax = 3;
VideoPortInt10(phwDeviceExtension, &Registers);
}
/*
* For each of the three buffers, free it if it exists.
*/
if (CapBuffer != 0)
VideoPortFreeDeviceBase(phwDeviceExtension, CapBuffer);
if (SupBuffer != 0)
VideoPortFreeDeviceBase(phwDeviceExtension, SupBuffer);
if (MappedBuffer != 0)
VideoPortFreeDeviceBase(phwDeviceExtension, MappedBuffer);
return;
} /* CleanupQuery() */
#if defined(ALPHA)
/***************************************************************************
*
* BOOL DenseOnAlpha(Query);
*
* struct query_structure *Query; Query information for the card
*
* DESCRIPTION:
* Reports whether or not we can use dense space on this card
* in a DEC Alpha.
*
* RETURN VALUE:
* TRUE if this card can use dense space
* FALSE if it can't
*
* GLOBALS CHANGED:
* None
*
* CALLED BY:
* Any routine after the query structure is filled in.
*
* AUTHOR:
* Robert Wolff
*
* CHANGE HISTORY:
*
* TEST HISTORY:
*
***************************************************************************/
BOOL DenseOnAlpha(struct query_structure *Query)
{
/*
* Some older Alpha machines are unable to support dense space,
* so these must be mapped as sparse. The easiest way to distinguish
* dense-capable from older machines is that all PCI Alpha systems
* are dense-capable, so if we are dealing with a PCI card the
* machine must be capable of handling dense space.
*
* Our older cards will generate drawing bugs if GDI handles
* the screen in dense mode (we made different assumptions from
* DEC about the PCI interface), so only use dense space for
* cards which will not have this problem.
*/
if ((Query->q_bus_type == BUS_PCI) &&
((Query->q_DAC_type == DAC_INTERNAL_CT) ||
(Query->q_DAC_type == DAC_INTERNAL_GT) ||
(Query->q_DAC_type == DAC_INTERNAL_VT)))
return TRUE;
else
return FALSE;
} /* DenseOnAlpha() */
#endif