/* $XFree86: xc/programs/Xserver/hw/xfree86/drivers/ati/atipreinit.c,v 1.91 2006/02/24 15:31:31 tsi Exp $ */
/*
 * Copyright 1999 through 2006 by Marc Aurele La France (TSI @ UQV), tsi@xfree86.org
 *
 * Permission to use, copy, modify, distribute, and sell this software and its
 * documentation for any purpose is hereby granted without fee, provided that
 * the above copyright notice appear in all copies and that both that copyright
 * notice and this permission notice appear in supporting documentation, and
 * that the name of Marc Aurele La France not be used in advertising or
 * publicity pertaining to distribution of the software without specific,
 * written prior permission.  Marc Aurele La France makes no representations
 * about the suitability of this software for any purpose.  It is provided
 * "as-is" without express or implied warranty.
 *
 * MARC AURELE LA FRANCE DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS.  IN NO
 * EVENT SHALL MARC AURELE LA FRANCE BE LIABLE FOR ANY SPECIAL, INDIRECT OR
 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
 * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 * PERFORMANCE OF THIS SOFTWARE.
 */

#include "ati.h"
#include "atiadapter.h"
#include "atiadjust.h"
#include "atiaudio.h"
#include "atibus.h"
#include "atichip.h"
#include "aticonfig.h"
#include "aticursor.h"
#include "atidac.h"
#include "atidsp.h"
#include "atii2c.h"
#include "atiident.h"
#include "atiload.h"
#include "atilock.h"
#include "atimach64.h"
#include "atimach64accel.h"
#include "atimach64io.h"
#include "atimode.h"
#include "atipreinit.h"
#include "atiprint.h"
#include "atividmem.h"
#include "atiwonderio.h"
#include "atixv.h"

#include "vbe.h"
#include "xf86RAC.h"

typedef CARD16 Colour;          /* The correct spelling should be OK :-) */

/*
 * Bit patterns which are extremely unlikely to show up when reading from
 * nonexistant memory (which normally shows up as either all bits set or all
 * bits clear).
 */
static const Colour Test_Pixel[] = {0x5AA5U, 0x55AAU, 0xA55AU, 0xCA53U};

static const struct
{
    int videoRamSize;
    int Miscellaneous_Options_Setting;
    struct
    {
        short int x, y;
    }
    Coordinates[NumberOf(Test_Pixel) + 1];
}
Test_Case[] =
{
    /*
     * Given the engine settings used, only a 4M card will have enough memory
     * to back up the 1025th line of the display.  Since the pixel coordinates
     * are zero-based, line 1024 will be the first one which is only backed on
     * 4M cards.
     *
     * <Mark_Weaver@brown.edu>:
     * In case memory is being wrapped, (0,0) and (0,1024) to make sure they
     * can each hold a unique value.
     */
    {4096, MEM_SIZE_4M, {{0,0}, {0,1024}, {-1,-1}}},

    /*
     * This card has 2M or less.  On a 1M card, the first 2M of the card's
     * memory will have even doublewords backed by physical memory and odd
     * doublewords unbacked.
     *
     * Pixels 0 and 1 of a row will be in the zeroth doubleword, while pixels 2
     * and 3 will be in the first.  Check both pixels 2 and 3 in case this is a
     * pseudo-1M card (one chip pulled to turn a 2M card into a 1M card).
     *
     * <Mark_Weaver@brown.edu>:
     * I don't have a 1M card, so I'm taking a stab in the dark.  Maybe memory
     * wraps every 512 lines, or maybe odd doublewords are aliases of their
     * even doubleword counterparts.  I try everything here.
     */
    {2048, MEM_SIZE_2M, {{0,0}, {0,512}, {2,0}, {3,0}, {-1,-1}}},

    /*
     * This is a either a 1M card or a 512k card.  Test pixel 1, since it is an
     * odd word in an even doubleword.
     *
     * <Mark_Weaver@brown.edu>:
     * This is the same idea as the test above.
     */
    {1024, MEM_SIZE_1M, {{0,0}, {0,256}, {1,0}, {-1,-1}}},

    /*
     * Assume it is a 512k card by default, since that is the minimum
     * configuration.
     */
    {512, MEM_SIZE_512K, {{-1,-1}}}
};

/*
 * ATIMach32ReadPixel --
 *
 * Return the colour of the specified screen location.  Called from
 * ATIMach32videoRam function below.
 */
static Colour
ATIMach32ReadPixel
(
    ATIPtr          pATI,
    const short int X,
    const short int Y
)
{
    Colour Pixel_Colour;

    /* Wait for idle engine */
    ProbeWaitIdleEmpty();

    /* Set up engine for pixel read */
    ATIWaitQueue(7);
    outw(RD_MASK, (CARD16)(~0));
    outw(DP_CONFIG, FG_COLOR_SRC_BLIT | DATA_WIDTH | DRAW | DATA_ORDER);
    outw(CUR_X, X);
    outw(CUR_Y, Y);
    outw(DEST_X_START, X);
    outw(DEST_X_END, X + 1);
    outw(DEST_Y_END, Y + 1);

    /* Wait for data to become ready */
    ATIWaitQueue(16);
    WaitDataReady();

    /* Read pixel colour */
    Pixel_Colour = inw(PIX_TRANS);
    ProbeWaitIdleEmpty();
    return Pixel_Colour;
}

/*
 * ATIMach32WritePixel --
 *
 * Set the colour of the specified screen location.  Called from
 * ATIMach32videoRam function below.
 */
static void
ATIMach32WritePixel
(
    ATIPtr          pATI,
    const short int X,
    const short int Y,
    const Colour    Pixel_Colour
)
{
    /* Set up engine for pixel write */
    ATIWaitQueue(9);
    outw(WRT_MASK, (CARD16)(~0));
    outw(DP_CONFIG, FG_COLOR_SRC_FG | DRAW | READ_WRITE);
    outw(ALU_FG_FN, MIX_FN_PAINT);
    outw(FRGD_COLOR, Pixel_Colour);
    outw(CUR_X, X);
    outw(CUR_Y, Y);
    outw(DEST_X_START, X);
    outw(DEST_X_END, X + 1);
    outw(DEST_Y_END, Y + 1);
}

/*
 * ATIMach32videoRam --
 *
 * Determine the amount of video memory installed on an 68800-6 based adapter.
 * This is done because these chips exhibit a bug that causes their
 * MISC_OPTIONS register to report 1M rather than the true amount of memory.
 *
 * This function is adapted from a similar function in mach32mem.c written by
 * Robert Wolff, David Dawes and Mark Weaver.
 */
static int
ATIMach32videoRam
(
    ATIPtr pATI
)
{
    CARD16 clock_sel, mem_bndry, misc_options, ext_ge_config;
    Colour saved_Pixel[NumberOf(Test_Pixel)];
    unsigned int Case_Number, Pixel_Number;
    Bool AllPixelsOK;

    /* Save register values to be modified */
    clock_sel = inw(CLOCK_SEL);
    mem_bndry = inw(MEM_BNDRY);
    misc_options = inw(MISC_OPTIONS) & ~MEM_SIZE_ALIAS;
    ext_ge_config = inw(R_EXT_GE_CONFIG);

    /* Wait for enough FIFO entries */
    ATIWaitQueue(7);

    /* Enable accelerator */
    outw(CLOCK_SEL, clock_sel | DISABPASSTHRU);

    /* Make accelerator and VGA share video memory */
    outw(MEM_BNDRY, mem_bndry & ~(MEM_PAGE_BNDRY | MEM_BNDRY_ENA));

    /* Prevent video memory wrap */
    outw(MISC_OPTIONS, misc_options | MEM_SIZE_4M);

    /*
     * Set up the drawing engine for a pitch of 1024 at 16 bits per pixel.  No
     * need to mess with the CRT because the results of this test are not
     * intended to be seen.
     */
    outw(EXT_GE_CONFIG, PIXEL_WIDTH_16 | ORDER_16BPP_565 | MONITOR_8514 |
        ALIAS_ENA);
    outw(GE_PITCH, 1024 >> 3);
    outw(GE_OFFSET_HI, 0);
    outw(GE_OFFSET_LO, 0);

    for (Case_Number = 0;
         Case_Number < (NumberOf(Test_Case) - 1);
         Case_Number++)
    {
        /* Reduce redundancy as per Mark_Weaver@brown.edu */
#       define TestPixel Test_Case[Case_Number].Coordinates[Pixel_Number]
#       define ForEachTestPixel        \
            for (Pixel_Number = 0;  TestPixel.x >= 0;  Pixel_Number++)

        /* Save pixel colours that will be clobbered */
        ForEachTestPixel
            saved_Pixel[Pixel_Number] =
                ATIMach32ReadPixel(pATI, TestPixel.x, TestPixel.y);

        /* Write test patterns */
        ForEachTestPixel
            ATIMach32WritePixel(pATI, TestPixel.x, TestPixel.y,
                Test_Pixel[Pixel_Number]);

        /* Test for lost pixels */
        AllPixelsOK = TRUE;
        ForEachTestPixel
        {
            if (ATIMach32ReadPixel(pATI, TestPixel.x, TestPixel.y) !=
                Test_Pixel[Pixel_Number])
            {
                AllPixelsOK = FALSE;
                break;
            }
        }

        /* Restore clobbered pixels */
        ForEachTestPixel
            ATIMach32WritePixel(pATI, TestPixel.x, TestPixel.y,
                saved_Pixel[Pixel_Number]);

        /* End test on success */
        if (AllPixelsOK)
            break;

        /* Completeness */
#       undef ForEachTestPixel
#       undef TestPixel
    }

    /* Restore what was changed and correct MISC_OPTIONS register */
    ATIWaitQueue(4);
    outw(EXT_GE_CONFIG, ext_ge_config);
    misc_options |= Test_Case[Case_Number].Miscellaneous_Options_Setting;
    outw(MISC_OPTIONS, misc_options);
    outw(MEM_BNDRY, mem_bndry);
    outw(CLOCK_SEL, clock_sel);

    /* Wait for activity to die down */
    ProbeWaitIdleEmpty();

    /* Tell ATIPreInit the REAL story */
    return Test_Case[Case_Number].videoRamSize;
}

/*
 * ATIReportMemory --
 *
 * This function reports on the amount and type of video memory found.
 */
static void
ATIReportMemory
(
    ScrnInfoPtr pScreenInfo,
    ATIPtr      pATI,
    const char *MemoryTypeName
)
{
    char Buffer[128], *Message;

    Message = Buffer +
        snprintf(Buffer, SizeOf(Buffer), "%d kB of %s detected",
            pATI->VideoRAM, MemoryTypeName);

    if (pATI->depth == 1)
    {
        /* 1bpp only uses one plane of four */
        pScreenInfo->videoRam /= 4;
        Message += snprintf(Message, Buffer + SizeOf(Buffer) - Message,
            " (using %d kB)", pScreenInfo->videoRam);
    }
    else if (pATI->VideoRAM > pScreenInfo->videoRam)
    {
        Message += snprintf(Message, Buffer + SizeOf(Buffer) - Message,
            " (using %d kB)", pScreenInfo->videoRam);
    }

    xf86DrvMsg(pScreenInfo->scrnIndex, X_PROBED, "%s.\n", Buffer);
}

static const int videoRamSizes[] =
    {0, 256, 512, 1024, 2*1024, 4*1024, 6*1024, 8*1024, 12*1024, 16*1024, 0};
static const rgb   defaultWeight = {0, 0, 0};
static const Gamma defaultGamma  = {0.0, 0.0, 0.0};

/*
 * ATIMach64Map --
 *
 * This function attempts to mmap() a Mach64's MMIO aperture.
 */
static void
ATIMach64Map
(
    int    iScreen,
    ATIPtr pATI
)
{
    (void)ATIMapApertures(iScreen, pATI);
    if (!pATI->pBlock[0] ||
        (pATI->config_chip_id != inr(CONFIG_CHIP_ID)))
        ATIUnmapApertures(iScreen, pATI);
}

/*
 * ATIPrintNoiseIfRequested --
 *
 * This function formats debugging information on the server's stderr when
 * requested by the user through the server's verbosity setting.
 */
static void
ATIPrintNoiseIfRequested
(
    ATIPtr       pATI,
    CARD8       *BIOS,
    unsigned int BIOSSize
)
{
    if (xf86GetVerbosity() <= 3)
        return;

    if (BIOSSize > 0)
        ATIPrintBIOS(BIOS, BIOSSize);
    xf86ErrorFVerb(4, "\n On server entry:\n");
    ATIPrintRegisters(pATI);
}

/*
 * ATIPreInit --
 *
 * This function is only called once per screen at the start of the first
 * server generation.
 */
Bool
ATIPreInit
(
    ScrnInfoPtr pScreenInfo,
    int flags
)
{
#   define           BIOS_SIZE       0x00010000U     /* 64kB */
    CARD8            BIOS[BIOS_SIZE];
#   define           BIOSByte(_n)    ((CARD8)(BIOS[_n]))
#   define           BIOSWord(_n)    ((CARD16)(BIOS[_n] |                \
                                               (BIOS[(_n) + 1] << 8)))
#   define           BIOSLong(_n)    ((CARD32)(BIOS[_n] |                \
                                               (BIOS[(_n) + 1] << 8) |   \
                                               (BIOS[(_n) + 2] << 16) |  \
                                               (BIOS[(_n) + 3] << 24)))
    unsigned int     BIOSSize = 0;
    unsigned int     ROMTable = 0, ClockTable = 0, FrequencyTable = 0;
    unsigned int     LCDTable = 0, LCDPanelInfo = 0, VideoTable = 0;
    unsigned int     HardwareTable = 0;

    char             Buffer[128], *Message;
    ATIPtr           pATI;
    GDevPtr          pGDev;
    EntityInfoPtr    pEntity;
    resPtr           pResources;
    pciVideoPtr      pVideo;
    DisplayModePtr   pMode;
    xf86Int10InfoPtr pInt10Info = NULL;
    vbeInfoPtr       pVBE;
    pointer          pInt10Module, pDDCModule = NULL, pVBEModule = NULL;
    unsigned long    Block0Base;
    CARD32           IOValue;
    int              i, j;
    int              VGAVideoRAM = 0, AcceleratorVideoRAM = 0, ServerVideoRAM;
    int              Numerator, Denominator;
    int              MinX, MinY;
    rgb              defaultMask = {0, 0, 0};
    ClockRange       ATIClockRange = {NULL, 0, 80000, 0, TRUE, TRUE, 1, 1, 0};
    resRange         Resources[2] = {{0, 0, 0}, _END};
    int              DefaultmaxClock = 0;
    int              minPitch, maxPitch = 0xFFU, maxHeight = 0;
    int              ApertureSize = 0x00010000U;
    int              ModeType = M_T_BUILTIN;
    LookupModeFlags  Strategy = LOOKUP_CLOSEST_CLOCK;
    int              DefaultDepth;

#   define           pATIHW     (&pATI->OldHW)

    if (pScreenInfo->numEntities != 1)
    {
        xf86DrvMsg(pScreenInfo->scrnIndex, X_ERROR,
            "Logic error:  Number of attached entities not 1.\n");
        return FALSE;
    }

    pATI = ATIPTR(pScreenInfo);

    if (pATI->iEntity != pScreenInfo->entityList[0])
    {
        xf86DrvMsg(pScreenInfo->scrnIndex, X_ERROR,
            "Logic error:  Entity mismatch.\n");
        return FALSE;
    }

    /* Register resources */
    pEntity = xf86GetEntityInfo(pATI->iEntity);
    pGDev = pEntity->device;
    pResources = pEntity->resources;
    xfree(pEntity);
    if (!pResources)
        pResources = xf86RegisterResources(pATI->iEntity, NULL,
            pATI->SharedAccelerator ? ResShared : ResExclusive);
    if (pResources)
    {
        xf86DrvMsg(pScreenInfo->scrnIndex, X_ERROR,
            "Unable to register the following bus resources:\n");
        xf86PrintResList(0, pResources);
        xf86FreeResList(pResources);
        return FALSE;
    }

    ConfiguredMonitor = NULL;
    (void)memset(BIOS, 0, SizeOf(BIOS));

    if (!(flags & PROBE_DETECT))
    {
        xf86DrvMsg(pScreenInfo->scrnIndex,
            pATI->Chipset ? X_CONFIG : X_DEFAULT,
            "Chipset:  \"%s\".\n", ATIChipsetNames[pATI->Chipset]);

        /* Promote chipset specification */
        switch (pATI->Chipset)
        {
            case ATI_CHIPSET_IBMVGA:
                if (pATI->Adapter == ATI_ADAPTER_VGA)
                    break;      /* XXX */
                /* Fall through */

            case ATI_CHIPSET_VGAWONDER:
                pATI->Chipset = ATI_CHIPSET_ATIVGA;
                break;

            case ATI_CHIPSET_IBM8514:
                if (pATI->Adapter == ATI_ADAPTER_8514A)
                    break;      /* XXX */
                /* Fall through */

            case ATI_CHIPSET_MACH8:
            case ATI_CHIPSET_MACH32:
            case ATI_CHIPSET_MACH64:
            case ATI_CHIPSET_RAGE128:
            case ATI_CHIPSET_RADEON:
                pATI->Chipset = ATI_CHIPSET_ATI;
                break;

            default:
                break;
        }

        /* Set monitor */
        pScreenInfo->monitor = pScreenInfo->confScreen->monitor;

        /* Set depth, bpp, etc. */
        if ((pATI->Chipset != ATI_CHIPSET_ATI) ||
            (pATI->Chip < ATI_CHIP_264CT))
        {
            i = NoDepth24Support;       /* No support for >8bpp either */
            DefaultDepth = 8;
        }
        else
        {
            i = Support24bppFb | Support32bppFb;
            DefaultDepth = 0;
        }

        if (!xf86SetDepthBpp(pScreenInfo, DefaultDepth, 0, 0, i))
            return FALSE;

        for (j = 0;  ;  j++)
        {
            static const CARD8 AllowedDepthBpp[][2] =
            {
                { 1,  1},
                { 4,  4},
                { 4,  8},
                { 8,  8},
                {15, 16},
                {16, 16},
                {24, 24},
                {24, 32}
            };

            if (j < NumberOf(AllowedDepthBpp))
            {
                if (pScreenInfo->depth > AllowedDepthBpp[j][0])
                    continue;

                if (pScreenInfo->depth == AllowedDepthBpp[j][0])
                {
                    if (pScreenInfo->bitsPerPixel > AllowedDepthBpp[j][1])
                        continue;

                    if (pScreenInfo->bitsPerPixel == AllowedDepthBpp[j][1])
                        break;
                }
            }

            xf86DrvMsg(pScreenInfo->scrnIndex, X_ERROR,
                "Driver does not support depth %d at fbbpp %d.\n",
                pScreenInfo->depth, pScreenInfo->bitsPerPixel);
            return FALSE;
        }

        xf86PrintDepthBpp(pScreenInfo);

        if ((i == NoDepth24Support) && (pScreenInfo->depth > 8))
        {
            xf86DrvMsg(pScreenInfo->scrnIndex, X_ERROR,
                "Depth %d is not supported through this adapter.\n",
                pScreenInfo->depth);
            return FALSE;
        }

        /*
         * Pick up XF86Config options.  This can only be done after
         * xf86SetDepthBpp() has been called.
         */
        ATIProcessOptions(pScreenInfo, pATI);

        pATI->ATIApplyEndian = ATIEndianCopy;

        if (ATIEndian.endian == ATI_BIG_ENDIAN)
        {
            if (pScreenInfo->bitsPerPixel == 24)
            {
                /*
                 * For bpp 24, the common layer's xf86SetWeight() sets a
                 * default of 'RGB'.  Byte-swap this to 'BGR'.
                 */
                defaultMask.red = 0x000000FFU;
                defaultMask.green = 0x0000FF00U;
                defaultMask.blue = 0x00FF0000U;

                pATI->ATIApplyEndian = ATIEndianSwap24;
            }
            else if (pScreenInfo->depth == 24)
            {
                if ((pATI->Chip < ATI_CHIP_264VTB) || !pATI->OptionLinear)
                {
                    /*
                     * For bpp 32, the common layer's xf86SetWeight() sets a
                     * default of 'aRGB'.  In the absence of a big-endian
                     * aperture, byte-swap this to 'BGRa'.
                     */
                    defaultMask.red = 0x0000FF00U;
                    defaultMask.green = 0x00FF0000U;
                    defaultMask.blue = 0xFF000000U;

                    pATI->ATIApplyEndian = ATIEndianSwap32;
                }
            }
            else if (pScreenInfo->bitsPerPixel > 8)
            {
                /*
                 * A big-endian aperture is required in this case unless all
                 * draw primitives are guaranteed to be accelerated, which
                 * currently isn't the case, with or without XAA.
                 */
                if (pATI->Chip < ATI_CHIP_264VTB)
                {
                    xf86DrvMsg(pScreenInfo->scrnIndex, X_ERROR,
                        "This adapter does not provide a big-endian aperture"
                        " for depth %d.\n", pScreenInfo->depth);
                    return FALSE;
                }

                /*
                 * "NoLinear" isn't the default, so respect the user's choice
                 * instead of over-ridding it.
                 */
                if (!pATI->OptionLinear)
                {
                    xf86DrvMsg(pScreenInfo->scrnIndex, X_ERROR,
                        "Depth %d is not supported without a big-endian"
                        " aperture.\n", pScreenInfo->depth);
                    return FALSE;
                }
            }
        }
    }

    /*
     * If there is an ix86-style BIOS, ensure its initialisation entry point
     * has been executed, and retrieve DDC and VBE information from it.
     * XXX Should this be done for adapters accessed through MEMORY_IO?
     */
    xf86DrvMsg(pScreenInfo->scrnIndex, X_INFO,
        "Initialising int10 interface.\n");
    pInt10Module = ATILoadSubModule(pScreenInfo, "int10", ATIint10Symbols);
    if (!pInt10Module)
    {
        xf86DrvMsg(pScreenInfo->scrnIndex, X_WARNING,
            "Unable to load int10 module.\n");
    }
    else if (!(pInt10Info = xf86InitInt10(pATI->iEntity)))
    {
        xf86DrvMsg(pScreenInfo->scrnIndex, X_WARNING,
             "Unable to initialise int10 interface.\n");
    }
    else
    {
        xf86DrvMsg(pScreenInfo->scrnIndex, X_INFO,
            "Determining BIOS support for VBE.\n");
        pDDCModule = ATILoadSubModule(pScreenInfo, "ddc", ATIddcSymbols);
        if (!pDDCModule)
        {
            xf86DrvMsg(pScreenInfo->scrnIndex, X_WARNING,
                "Unable to load ddc module.\n");
        }
        else
        {
            pVBEModule = ATILoadVBEModule(pScreenInfo);
            if (!pVBEModule)
            {
                xf86DrvMsg(pScreenInfo->scrnIndex, X_WARNING,
                    "Unable to load vbe module.\n");
            }
            else
            {
                if ((pVBE = VBEInit(pInt10Info, pATI->iEntity)))
                {
                    xf86DrvMsg(pScreenInfo->scrnIndex, X_PROBED,
                        "Video BIOS supports VBE.\n");
                    ConfiguredMonitor = vbeDoEDID(pVBE, pDDCModule);
                    vbeFree(pVBE);
                }
                xf86UnloadSubModule(pVBEModule);
            }
        }

        if (!(flags & PROBE_DETECT))
        {
            /* Validate, then make a private copy of, the initialised BIOS */
            CARD8 *pBIOS = xf86int10Addr(pInt10Info, pInt10Info->BIOSseg << 4);

            if ((pBIOS[0] != 0x55U) || (pBIOS[1] != 0xAAU) || !pBIOS[2])
            {
                xf86DrvMsg(pScreenInfo->scrnIndex, X_WARNING,
                    "Unable to correctly retrieve adapter BIOS.\n");
            }
            else
            {
                BIOSSize = pBIOS[2] << 9;
                if (BIOSSize > BIOS_SIZE)
                    BIOSSize = BIOS_SIZE;
                (void)memcpy(BIOS, pBIOS, BIOSSize);
            }
        }
    }

    /* De-activate int10 */
    xf86FreeInt10(pInt10Info);
    xf86UnloadSubModule(pInt10Module);

    if (flags & PROBE_DETECT)
    {
        xf86UnloadSubModule(pDDCModule);
        return TRUE;
    }

    if (ConfiguredMonitor)
    {
        xf86PrintEDID(ConfiguredMonitor);
        xf86SetDDCproperties(pScreenInfo, ConfiguredMonitor);
    }

    /* DDC module is no longer needed at this point */
    xf86UnloadSubModule(pDDCModule);

    pATI->Block0Base = 0;       /* Might no longer be valid */
    if ((pVideo = pATI->PCIInfo))
    {
        if (pVideo->ioBase[1])
            pATI->CPIOBase = pVideo->ioBase[1] + pATI->DomainIOBase;

        /* Set MMIO address from PCI configuration space, if available */
        if ((pATI->Block0Base = pVideo->memBase[2]))
        {
            if (pATI->Block0Base >= (CARD32)(-1 << pVideo->size[2]))
                pATI->Block0Base = 0;
            else
                pATI->Block0Base += 0x0400U;
        }
    }

    pScreenInfo->racIoFlags =
        RAC_FB | RAC_COLORMAP | RAC_VIEWPORT | RAC_CURSOR;
    pScreenInfo->racMemFlags = RAC_FB | RAC_CURSOR;

    /* Deal with ChipID & ChipRev overrides */
    if (pGDev->chipID >= 0)
    {
        ATIChipType Chip;

        Chip = ATIChipID(pGDev->chipID,
            (pGDev->chipRev < 0) ? pATI->ChipRev : pGDev->chipRev);
        if (Chip != pATI->Chip)
        {
            pATI->Chip = Chip;
            pATI->ChipType = pGDev->chipID;
            if (pGDev->chipRev < 0)
            {
                xf86DrvMsg(pScreenInfo->scrnIndex, X_CONFIG,
                    "Driver messages reflect ChipID 0x%04X override.\n",
                    pATI->ChipType);
            }
            else
            {
                pATI->ChipRev = pGDev->chipRev;
                pATI->ChipVersion = GetBits(pATI->ChipRev,
                    GetBits(CFG_CHIP_VERSION, CFG_CHIP_REV));
                pATI->ChipFoundry = GetBits(pATI->ChipRev,
                    GetBits(CFG_CHIP_FOUNDRY, CFG_CHIP_REV));
                pATI->ChipRevision = GetBits(pATI->ChipRev,
                    GetBits(CFG_CHIP_REVISION, CFG_CHIP_REV));
                xf86DrvMsg(pScreenInfo->scrnIndex, X_CONFIG,
                    "Driver messages reflect ChipID 0x%04X and ChipRev 0x%02X"
                    " overrides.\n", pATI->ChipType, pATI->ChipRev);
            }
        }
    }

    /* Finish private area initialisation */
    pATI->DAC = ATI_DAC_GENERIC;
    pATI->NewHW.SetBank = ATIx8800SetBank;
    pATI->BankInfo.SetSourceBank = ATIx8800SetRead;
    pATI->BankInfo.SetDestinationBank = ATIx8800SetWrite;
    pATI->BankInfo.SetSourceAndDestinationBanks = ATIx8800SetReadWrite;
    pATI->BankInfo.BankSize = 0x00010000U;      /* 64kB */
    pATI->LCDPanelID = -1;
    pATI->nFIFOEntries = 16;                    /* For now */
    pATI->Audio = ATI_AUDIO_NONE;

    /* Finish probing the adapter */
    switch (pATI->Adapter)
    {
        case ATI_ADAPTER_NONE:
        case ATI_ADAPTER_EGA:
        case ATI_ADAPTER_EGA_PLUS:
        case ATI_ADAPTER_VGA:
        case ATI_ADAPTER_BASIC:
            pATI->NewHW.SetBank = (ATIBankProcPtr)NoopDDA;
            pATI->BankInfo.SetSourceBank =
                pATI->BankInfo.SetDestinationBank =
                pATI->BankInfo.SetSourceAndDestinationBanks =
                    (miBankProcPtr)NoopDDA;
            break;

        case ATI_ADAPTER_V3:
            pATI->NewHW.SetBank = ATIV3SetBank;
            pATI->BankInfo.SetSourceBank = ATIV3SetRead;
            pATI->BankInfo.SetDestinationBank = ATIV3SetWrite;
            pATI->BankInfo.SetSourceAndDestinationBanks = ATIV3SetReadWrite;
            break;

        case ATI_ADAPTER_V4:
        case ATI_ADAPTER_V5:
            pATI->NewHW.SetBank = ATIV4V5SetBank;
            pATI->BankInfo.SetSourceBank = ATIV4V5SetRead;
            pATI->BankInfo.SetDestinationBank = ATIV4V5SetWrite;
            pATI->BankInfo.SetSourceAndDestinationBanks = ATIV4V5SetReadWrite;
            break;

        case ATI_ADAPTER_XL:
            pATI->DAC = ATI_DAC_SC11483;
            break;

        case ATI_ADAPTER_8514A:
            pATI->VideoRAM =
                videoRamSizes[GetBits(inw(SUBSYS_STAT), _8PLANE) + 2];
            break;

        case ATI_ADAPTER_MACH8:
            pATI->VideoRAM =
                videoRamSizes[GetBits(inw(CONFIG_STATUS_1), MEM_INSTALLED) + 2];
            break;

        case ATI_ADAPTER_MACH32:
            IOValue = inw(CONFIG_STATUS_1);
            pATI->DAC = ATI_DAC(GetBits(IOValue, DACTYPE), 0);
            pATI->MemoryType = GetBits(IOValue, MEM_TYPE);

            IOValue = inw(MISC_OPTIONS);
            pATI->VideoRAM =
                videoRamSizes[GetBits(IOValue, MEM_SIZE_ALIAS) + 2];

            /*
             * The 68800-6 doesn't necessarily report the correct video memory
             * size.
             */
            if ((pATI->Chip == ATI_CHIP_68800_6) && (pATI->VideoRAM == 1024))
                pATI->VideoRAM = ATIMach32videoRam(pATI);

            break;

        case ATI_ADAPTER_MACH64:
            do
            {
                /*
                 * Find and mmap() MMIO area.  Allow only auxiliary aperture if
                 * it exists.
                 */
                if (!(Block0Base = pATI->Block0Base))
                {
                    if (pVideo)
                    {
                        /* Check tail end of linear (8MB or 4MB) aperture */
                        if ((pATI->Block0Base = pVideo->memBase[0]))
                        {
                            pATI->Block0Base += 0x007FFC00U;
                            ATIMach64Map(pScreenInfo->scrnIndex, pATI);
                            if (pATI->pBlock[0])
                                break;

                            pATI->Block0Base -= 0x00400000U;
                            ATIMach64Map(pScreenInfo->scrnIndex, pATI);
                            if (pATI->pBlock[0])
                                break;
                        }
                    }

                    /* Check VGA MMIO aperture */
                    pATI->Block0Base = 0x000BFC00U;
                }

                ATIMach64Map(pScreenInfo->scrnIndex, pATI);
            } while (0);
            pATI->Block0Base = Block0Base;

            if ((pATI->IODecoding == MEMORY_IO) && !pATI->pBlock[0])
            {
                xf86DrvMsg(pScreenInfo->scrnIndex, X_ERROR,
                    "Unable to mmap() adapter registers.\n");
                ATIUnmapApertures(pScreenInfo->scrnIndex, pATI);
                return FALSE;
            }

            /* XXX Need a similar test for GX/CX */
            pATIHW->crtc_gen_cntl = inr(CRTC_GEN_CNTL);
            if (!(pATIHW->crtc_gen_cntl & CRTC_EN) &&
                (pATI->Chip >= ATI_CHIP_264CT))
            {
                xf86DrvMsg(pScreenInfo->scrnIndex, X_ERROR,
                    "Adapter has not been initialised.\n");
                ATIPrintNoiseIfRequested(pATI, BIOS, BIOSSize);
                ATIUnmapApertures(pScreenInfo->scrnIndex, pATI);
                return FALSE;
            }

            pATIHW->mem_cntl = inr(MEM_CNTL);
            if (pATI->Chip < ATI_CHIP_264VTB)
            {
                pATI->VideoRAM =
                    videoRamSizes[GetBits(pATIHW->mem_cntl, CTL_MEM_SIZE) + 2];
            }
            else
            {
                pATI->nFIFOEntries =            /* Don't care */
                    (unsigned int)(-1) >> 1;

                IOValue = GetBits(pATIHW->mem_cntl, CTL_MEM_SIZEB);
                if (IOValue < 8)
                    pATI->VideoRAM = (IOValue + 1) * 512;
                else if (IOValue < 12)
                    pATI->VideoRAM = (IOValue - 3) * 1024;
                else
                    pATI->VideoRAM = (IOValue - 7) * 2048;
            }

            pATI->DAC = GetBits(inr(DAC_CNTL), DAC_TYPE);

            IOValue = inr(CONFIG_STATUS64_0);
            if (pATI->Chip >= ATI_CHIP_264CT)
            {
                pATI->MemoryType = GetBits(IOValue, CFG_MEM_TYPE_T);

                /* Get LCD panel id */
                if (pATI->Chip == ATI_CHIP_264LT)
                {
                    pATI->LCDPanelID = GetBits(IOValue, CFG_PANEL_ID);

                    pATIHW->horz_stretching = inr(HORZ_STRETCHING);
                    pATIHW->vert_stretching = inr(VERT_STRETCHING);
                    pATIHW->lcd_gen_ctrl = inr(LCD_GEN_CTRL);
                }
                else if ((pATI->Chip == ATI_CHIP_264LTPRO) ||
                         (pATI->Chip == ATI_CHIP_264XL) ||
                         (pATI->Chip == ATI_CHIP_MOBILITY))
                {
                    pATI->LCDPanelID = GetBits(IOValue, CFG_PANEL_ID);

                    pATIHW->lcd_index = inr(LCD_INDEX);
                    pATIHW->horz_stretching =
                        ATIMach64GetLCDReg(LCD_HORZ_STRETCHING);
                    pATI->LCDHorizontal =
                        GetBits(pATIHW->horz_stretching, HORZ_PANEL_SIZE);
                    if (pATI->LCDHorizontal)
                    {
                        if (pATI->LCDHorizontal == MaxBits(HORZ_PANEL_SIZE))
                            pATI->LCDHorizontal = 0;
                        else
                            pATI->LCDHorizontal =
                                (pATI->LCDHorizontal + 1) << 3;
                    }
                    pATIHW->ext_vert_stretch =
                        ATIMach64GetLCDReg(LCD_EXT_VERT_STRETCH);
                    pATI->LCDVertical =
                        GetBits(pATIHW->ext_vert_stretch, VERT_PANEL_SIZE);
                    if (pATI->LCDVertical)
                    {
                        if (pATI->LCDVertical == MaxBits(VERT_PANEL_SIZE))
                            pATI->LCDVertical = 0;
                        else
                            pATI->LCDVertical++;
                    }
                    pATIHW->vert_stretching =
                        ATIMach64GetLCDReg(LCD_VERT_STRETCHING);
                    pATIHW->lcd_gen_ctrl = ATIMach64GetLCDReg(LCD_GEN_CNTL);
                    outr(LCD_INDEX, pATIHW->lcd_index);
                }

                /*
                 * Don't bother with panel support if it hasn't been previously
                 * enabled.
                 */
                if ((pATI->LCDPanelID >= 0) &&
                    !(pATIHW->horz_stretching & HORZ_STRETCH_EN) &&
                    !(pATIHW->vert_stretching & VERT_STRETCH_EN) &&
                    !(pATIHW->lcd_gen_ctrl & LCD_ON))
                {
                    /*
                     * At this point, if an XL or Mobility BIOS hasn't set
                     * panel dimensions, then there is no panel.  Otherwise,
                     * keep any panel disabled to allow for modes greater than
                     * the panel's dimensions.
                     */
                    if ((pATI->Chip >= ATI_CHIP_264XL) &&
                        (!pATI->LCDHorizontal || !pATI->LCDVertical))
                        pATI->LCDPanelID = -1;
                    else
                        pATI->OptionPanelDisplay = FALSE;
                }
            }
            else
            {
                pATI->MemoryType = GetBits(IOValue, CFG_MEM_TYPE);

                /* Factor in what the BIOS says the DAC is */
                pATI->DAC = ATI_DAC(pATI->DAC,
                    GetBits(inr(SCRATCH_REG1), BIOS_INIT_DAC_SUBTYPE));
            }

            /*
             * RAMDAC types 0 & 1 for Mach64's are different than those for
             * Mach32's.
             */
            if (pATI->DAC < ATI_DAC_ATI68875)
                pATI->DAC += ATI_DAC_INTERNAL;

            break;

        default:
            break;
    }

    /*
     * For Mach64 adapters, pick up, from the BIOS, the type of programmable
     * clock generator (if any), and various information about it.
     */
    if (pATI->Chip >= ATI_CHIP_88800GXC)
    {
        CARD16 ClockDac;

        /* Set up non-zero defaults */
        pATI->ClockDescriptor = ATIClockDescriptors[ATI_CLOCK_FIXED];
        pATI->ClockNumberToProgramme = -1;

        ROMTable = BIOSWord(0x48U);
        if ((ROMTable < 0x0002U) ||
            (BIOSWord(ROMTable - 0x02U) < 0x0012U) ||
            ((ROMTable + BIOSWord(ROMTable - 0x02U)) > BIOSSize))
            ROMTable = 0;

        if (ROMTable > 0)
        {
            ClockTable = BIOSWord(ROMTable + 0x10U);
            if ((ClockTable + 0x20U) > BIOSSize)
                ClockTable = 0;

            if (BIOSWord(ROMTable - 0x02U) >= 0x0048U)
            {
                VideoTable = BIOSWord(ROMTable + 0x46U);
                if ((VideoTable < 0x08U) ||
                    (BIOSByte(VideoTable - 0x01U) < 0x08U) ||
                    (BIOSByte(VideoTable - 0x02U) > 0x01U) ||
                    ((VideoTable + BIOSByte(VideoTable - 0x01U)) > BIOSSize))
                    VideoTable = 0;
            }

            if (BIOSWord(ROMTable - 0x02U) >= 0x004AU)
            {
                HardwareTable = BIOSWord(ROMTable + 0x48U);
                if (((HardwareTable + 0x08U) <= BIOSSize) &&
                    !memcmp(BIOS + HardwareTable, "$ATI", 4))
                    pATI->I2CType = BIOSByte(HardwareTable + 0x06U) & 0x0FU;
                else
                    HardwareTable = 0;
            }
        }

        if (ClockTable > 0)
        {
            FrequencyTable = BIOSWord(ClockTable - 0x02U);
            if ((FrequencyTable > 0) &&
                ((FrequencyTable + 0x20U) <= BIOSSize))
            {
                for (i = 0;  i < 16;  i++)
                    pATI->BIOSClocks[i] = BIOSWord(FrequencyTable + (i * 2));
            }

            pATI->ProgrammableClock = BIOSByte(ClockTable);
            pATI->ClockNumberToProgramme = BIOSByte(ClockTable + 0x06U);
            switch ((BIOSWord(ClockTable + 0x08U) + 5) / 10)
            {
                case 143:
                    pATI->ReferenceNumerator = 157500;
                    pATI->ReferenceDenominator = 11;
                    break;

                case 286:
                    pATI->ReferenceNumerator = 315000;
                    pATI->ReferenceDenominator = 11;
                    break;

                case 295:
                    pATI->ReferenceNumerator = 324480;
                    pATI->ReferenceDenominator = 11;
                    break;

                default:
                    pATI->ReferenceNumerator =
                        BIOSWord(ClockTable + 0x08U) * 10;
                    pATI->ReferenceDenominator = 1;
                    break;
            }
        }
        else
        {
            /*
             * Compensate for BIOS absence.  Note that the reference
             * frequency has already been set by option processing.
             */
            if ((pATI->DAC & ~0x0FU) == ATI_DAC_INTERNAL)
            {
                pATI->ProgrammableClock = ATI_CLOCK_INTERNAL;
            }
            else switch (pATI->DAC)
            {
                case ATI_DAC_STG1703:
                    pATI->ProgrammableClock = ATI_CLOCK_STG1703;
                    break;

                case ATI_DAC_CH8398:
                    pATI->ProgrammableClock = ATI_CLOCK_CH8398;
                    break;

                case ATI_DAC_ATT20C408:
                    pATI->ProgrammableClock = ATI_CLOCK_ATT20C408;
                    break;

                case ATI_DAC_IBMRGB514:
                    pATI->ProgrammableClock = ATI_CLOCK_IBMRGB514;
                    break;

                default:        /* Provisional */
                    pATI->ProgrammableClock = ATI_CLOCK_ICS2595;
                    break;
            }

            /* This should be safe for all generators except IBM's RGB514 */
            pATI->ClockNumberToProgramme = 3;
        }

        if ((pATI->ProgrammableClock > ATI_CLOCK_FIXED) &&
            (pATI->ProgrammableClock < ATI_CLOCK_MAX))
        {
            /*
             * Graphics PRO TURBO 1600's are unusual in that an ICS2595 is used
             * to generate clocks for VGA modes, and an IBM RGB514 is used for
             * accelerator modes.
             */
            if ((pATI->ProgrammableClock == ATI_CLOCK_ICS2595) &&
                (pATI->DAC == ATI_DAC_IBMRGB514) &&
                (pScreenInfo->depth >= 8) &&
                (pATI->Chipset == ATI_CHIPSET_ATI))
                pATI->ProgrammableClock = ATI_CLOCK_IBMRGB514;

            pATI->ClockDescriptor =
                ATIClockDescriptors[pATI->ProgrammableClock];
        }

        ClockDac = pATI->DAC;
        switch (pATI->ProgrammableClock)
        {
            case ATI_CLOCK_ICS2595:
                /*
                 * Pick up reference divider (43 or 46) appropriate to the chip
                 * revision level.
                 */
                if (ClockTable > 0)
                    pATI->ClockDescriptor.MinM =
                        pATI->ClockDescriptor.MaxM =
                            BIOSWord(ClockTable + 0x0AU);
                else if (!xf86NameCmp(pGDev->clockchip, "ATI 18818-0"))
                    pATI->ClockDescriptor.MinM =
                        pATI->ClockDescriptor.MaxM = 43;
                else if (!xf86NameCmp(pGDev->clockchip, "ATI 18818-1"))
                    pATI->ClockDescriptor.MinM =
                        pATI->ClockDescriptor.MaxM = 46;
                else
                    pATI->ProgrammableClock = ATI_CLOCK_UNKNOWN;
                break;

            case ATI_CLOCK_STG1703:
                /* This one's also a RAMDAC */
                ClockDac = ATI_DAC_STG1703;
                break;

            case ATI_CLOCK_CH8398:
                /* This one's also a RAMDAC */
                ClockDac = ATI_DAC_CH8398;
                break;

            case ATI_CLOCK_INTERNAL:
                /*
                 * The reference divider has already been programmed by BIOS
                 * initialisation.  Because there is only one reference divider
                 * for all generated frequencies (including MCLK), it cannot be
                 * changed without reprogramming all clocks every time one of
                 * them needs a different reference divider.
                 *
                 * Besides, it's not a good idea to change the reference
                 * divider.  BIOS initialisation sets it to a value that
                 * effectively prevents generating frequencies beyond the
                 * graphics controller's tolerance.
                 */
                pATI->ClockDescriptor.MinM = pATI->ClockDescriptor.MaxM =
                    ATIMach64GetPLLReg(PLL_REF_DIV);

                /* The DAC is also integrated */
                if ((pATI->DAC & ~0x0FU) != ATI_DAC_INTERNAL)
                    ClockDac = ATI_DAC_INTERNAL;

                break;

            case ATI_CLOCK_ATT20C408:
                /* This one's also a RAMDAC */
                ClockDac = ATI_DAC_ATT20C408;
                break;

            case ATI_CLOCK_IBMRGB514:
                /* This one's also a RAMDAC */
                ClockDac = ATI_DAC_IBMRGB514;
                pATI->ClockNumberToProgramme = 7;
                break;

            default:
                break;
        }

        /*
         * We now have up to two indications of what RAMDAC the adapter uses.
         * They should be the same.  The following test and corresponding
         * action are under construction.
         */
        if (pATI->DAC != ClockDac)
        {
            xf86DrvMsg(pScreenInfo->scrnIndex, X_WARNING,
                       "Mach64 RAMDAC probe discrepancy detected:\n"
                       "  DAC=0x%02X;  ClockDac=0x%02X.\n",
                       pATI->DAC, ClockDac);

            if (pATI->DAC == ATI_DAC_IBMRGB514)
            {
                pATI->ProgrammableClock = ATI_CLOCK_IBMRGB514;
                pATI->ClockDescriptor =
                    ATIClockDescriptors[ATI_CLOCK_IBMRGB514];
                pATI->ClockNumberToProgramme = 7;
            }
            else
            {
                pATI->DAC = ClockDac;   /* For now */
            }
        }

        /*
         * Pick up multimedia information, which will be at different
         * displacements depending on table revision.
         */
        if (VideoTable > 0)
        {
            switch (BIOSByte(VideoTable - 0x02U))
            {
                case 0x00U:
                    pATI->Tuner = BIOSByte(VideoTable) & 0x1FU;

                    /*
                     * XXX  The VideoTable[1] byte is known to have been
                     *      omitted in LTPro and Mobility BIOS'es.  Any others?
                     */
                    switch (pATI->Chip)
                    {
                        case ATI_CHIP_264LTPRO:
                        case ATI_CHIP_MOBILITY:
                            pATI->Decoder =
                                BIOSByte(VideoTable + 0x01U) & 0x07U;
                            pATI->Audio =
                                BIOSByte(VideoTable + 0x02U) & 0x0FU;
                            break;

                        default:
                            pATI->Decoder =
                                BIOSByte(VideoTable + 0x02U) & 0x07U;
                            pATI->Audio =
                                BIOSByte(VideoTable + 0x03U) & 0x0FU;
                            break;
                    }

                    break;

                case 0x01U:
                    pATI->Tuner = BIOSByte(VideoTable) & 0x1FU;
                    pATI->Audio = BIOSByte(VideoTable + 0x01U) & 0x0FU;
                    pATI->Decoder = BIOSByte(VideoTable + 0x05U) & 0x0FU;
                    break;

                default:
                    break;
            }
        }

        /* Determine panel dimensions */
        if (pATI->LCDPanelID >= 0)
        {
            LCDTable = BIOSWord(0x78U);
            if ((LCDTable + BIOSByte(LCDTable + 5)) > BIOSSize)
                LCDTable = 0;

            if (LCDTable > 0)
            {
                LCDPanelInfo = BIOSWord(LCDTable + 0x0AU);
                if (((LCDPanelInfo + 0x1DU) > BIOSSize) ||
                    ((BIOSByte(LCDPanelInfo) != pATI->LCDPanelID) &&
                     (pATI->LCDPanelID || (BIOSByte(LCDPanelInfo) > 0x1FU) ||
                      (pATI->Chip <= ATI_CHIP_264LTPRO))))
                    LCDPanelInfo = 0;
            }

            if (!LCDPanelInfo)
            {
                /*
                 * Scan BIOS for panel info table.
                 */
                for (i = 0;  i <= (int)(BIOSSize - 0x1DU);  i++)
                {
                    /* Look for panel ID ... */
                    if ((BIOSByte(i) != pATI->LCDPanelID) &&
                        (pATI->LCDPanelID || (BIOSByte(i) > 0x1FU) ||
                         (pATI->Chip <= ATI_CHIP_264LTPRO)))
                        continue;

                    /* ... followed by 24-byte panel model name ... */
                    for (j = 0;  j < 24;  j++)
                    {
                        if ((CARD8)(BIOSByte(i + j + 1) - 0x20U) > 0x5FU)
                        {
                            i += j;
                            goto NextBIOSByte;
                        }
                    }

                    /* ... verify panel width ... */
                    if (pATI->LCDHorizontal &&
                        (pATI->LCDHorizontal != BIOSWord(i + 0x19U)))
                        continue;

                    /* ... and verify panel height */
                    if (pATI->LCDVertical &&
                        (pATI->LCDVertical != BIOSWord(i + 0x1BU)))
                        continue;

                    if (LCDPanelInfo)
                    {
                        /*
                         * More than one possibility, but don't care if all
                         * tables describe panels of the same size.
                         */
                        if ((BIOSByte(LCDPanelInfo + 0x19U) ==
                             BIOSByte(i + 0x19U)) &&
                            (BIOSByte(LCDPanelInfo + 0x1AU) ==
                             BIOSByte(i + 0x1AU)) &&
                            (BIOSByte(LCDPanelInfo + 0x1BU) ==
                             BIOSByte(i + 0x1BU)) &&
                            (BIOSByte(LCDPanelInfo + 0x1CU) ==
                             BIOSByte(i + 0x1CU)))
                            continue;

                        LCDPanelInfo = 0;
                        break;
                    }

                    LCDPanelInfo = i;

            NextBIOSByte:  ;
                }
            }

            if (LCDPanelInfo > 0)
            {
                pATI->LCDPanelID = BIOSByte(LCDPanelInfo);
                pATI->LCDHorizontal = BIOSWord(LCDPanelInfo + 0x19U);
                pATI->LCDVertical = BIOSWord(LCDPanelInfo + 0x1BU);
            }
        }

        xf86DrvMsgVerb(pScreenInfo->scrnIndex, X_INFO, 3,
            "BIOS Data:  BIOSSize=0x%04X, ROMTable=0x%04X.\n",
            BIOSSize, ROMTable);
        xf86DrvMsgVerb(pScreenInfo->scrnIndex, X_INFO, 3,
            "BIOS Data:  ClockTable=0x%04X, FrequencyTable=0x%04X.\n",
            ClockTable, FrequencyTable);
        xf86DrvMsgVerb(pScreenInfo->scrnIndex, X_INFO, 3,
            "BIOS Data:  LCDTable=0x%04X, LCDPanelInfo=0x%04X.\n",
            LCDTable, LCDPanelInfo);
        xf86DrvMsgVerb(pScreenInfo->scrnIndex, X_INFO, 3,
            "BIOS Data:  VideoTable=0x%04X, HardwareTable=0x%04X.\n",
            VideoTable, HardwareTable);
        xf86DrvMsgVerb(pScreenInfo->scrnIndex, X_INFO, 3,
            "BIOS Data:  I2CType=0x%02X, Tuner=0x%02X, Decoder=0x%02X,"
            " Audio=0x%02X.\n",
            pATI->I2CType, pATI->Tuner, pATI->Decoder, pATI->Audio);
    }

    ATIUnlock(pATI);            /* Unlock registers */

    /* Sometimes, the BIOS lies about the chip */
    if ((pATI->Chip >= ATI_CHIP_28800_4) && (pATI->Chip <= ATI_CHIP_28800_6))
    {
        IOValue = GetBits(ATIGetExtReg(0xAAU), 0x0FU) +
            (ATI_CHIP_28800_4 - 4);
        if ((IOValue <= ATI_CHIP_28800_6) && (IOValue > pATI->Chip))
            pATI->Chip = IOValue;
    }

    /* Report what was found */
    xf86DrvMsg(pScreenInfo->scrnIndex, X_PROBED,
        "%s graphics controller detected.\n", ATIChipNames[pATI->Chip]);

    if ((pATI->Chip >= ATI_CHIP_68800) && (pATI->Chip != ATI_CHIP_68800_3))
    {
        Message = Buffer + snprintf(Buffer, SizeOf(Buffer), "Chip type %04X",
            pATI->ChipType);
        if (!(pATI->ChipType & ~(CHIP_CODE_0 | CHIP_CODE_1)))
            Message += snprintf(Message, Buffer + SizeOf(Buffer) - Message,
                " (%c%c)",
                GetBits(pATI->ChipType, CHIP_CODE_1) + 0x41U,
                GetBits(pATI->ChipType, CHIP_CODE_0) + 0x41U);
        else if ((pATI->ChipType & 0x4040U) == 0x4040U)
            Message += snprintf(Message, Buffer + SizeOf(Buffer) - Message,
                " \"%c%c\"",
                GetByte(pATI->ChipType, 1), GetByte(pATI->ChipType, 0));
        if ((pATI->Chip >= ATI_CHIP_264CT) && (pATI->Chip != ATI_CHIP_Mach64))
            Message += snprintf(Message, Buffer + SizeOf(Buffer) - Message,
                ", version %d, foundry %s",
                pATI->ChipVersion, ATIFoundryNames[pATI->ChipFoundry]);
        xf86DrvMsg(pScreenInfo->scrnIndex, X_PROBED,
            "%s, class %d, revision 0x%02X.\n",
            Buffer, pATI->ChipClass, pATI->ChipRevision);
    }

    xf86DrvMsg(pScreenInfo->scrnIndex, X_PROBED, "Adapter is in domain %d.\n",
        pATI->Domain);

    if (pATI->Adapter >= ATI_ADAPTER_MACH8)
    {
        Message = Buffer + snprintf(Buffer, SizeOf(Buffer),
            "%s bus interface detected", ATIBusNames[pATI->BusType]);

        if ((pATI->Adapter >= ATI_ADAPTER_MACH64) &&
            (pATI->IODecoding != MEMORY_IO))
            Message += snprintf(Message, Buffer + SizeOf(Buffer) - Message,
                ";  %s I/O base is 0x%04lX",
                (pATI->IODecoding == SPARSE_IO) ? "sparse" : "block",
                pATI->CPIOBase - pATI->DomainIOBase);

        xf86DrvMsg(pScreenInfo->scrnIndex, X_PROBED, "%s.\n", Buffer);

        if (pVideo)
            xf86DrvMsg(pScreenInfo->scrnIndex, X_PROBED,
                "Adapter is on bus %d, device %d, function %d.\n",
                pVideo->bus, pVideo->device, pVideo->func);
    }

    if (pATI->CPIO_VGAWonder)
        xf86DrvMsg(pScreenInfo->scrnIndex, X_PROBED,
            "VGA Wonder registers at I/O port 0x%04lX.\n",
            pATI->CPIO_VGAWonder);

    if (pATI->Coprocessor != ATI_CHIP_NONE)
        xf86DrvMsg(pScreenInfo->scrnIndex, X_PROBED,
            "%s graphics accelerator detected,\n with %d kB of coprocessor"
            " memory.\n",
            ATIChipNames[pATI->Coprocessor], pATI->VideoRAM);

    xf86DrvMsg(pScreenInfo->scrnIndex, X_PROBED,
        "%s adapter detected.\n", ATIAdapterNames[pATI->Adapter]);

    if (pATI->Chip >= ATI_CHIP_264GT)
        xf86DrvMsg(pScreenInfo->scrnIndex, X_NOTICE,
            "For information on using the multimedia capabilities\n of this"
            " adapter, please see http://gatos.sf.net.\n");

    if ((pATI->DAC & ~0x0FU) == ATI_DAC_INTERNAL)
    {
        xf86DrvMsg(pScreenInfo->scrnIndex, X_PROBED,
            "Internal RAMDAC (subtype %d) detected.\n", pATI->DAC & 0x0FU);
    }
    else
    {
        const SymTabRec *DAC;

        for (DAC = ATIDACDescriptors;  ;  DAC++)
        {
            if (pATI->DAC == DAC->token)
            {
                xf86DrvMsg(pScreenInfo->scrnIndex, X_PROBED,
                    "%s RAMDAC detected.\n", DAC->name);
                break;
            }

            if (pATI->DAC < DAC->token)
            {
                xf86DrvMsgVerb(pScreenInfo->scrnIndex, X_WARNING, 0,
                    "Unknown RAMDAC type 0x%02X detected.\n", pATI->DAC);
                break;
            }
        }
    }

    /*
     * Set colour weights.
     */

    if (pATI->Chip < ATI_CHIP_264CT)
        pScreenInfo->rgbBits = 6;
    else
        pScreenInfo->rgbBits = 8;
    pATI->rgbBits = pScreenInfo->rgbBits;
    if (!xf86SetWeight(pScreenInfo, defaultWeight, defaultMask))
    {
        ATILock(pATI);
        ATIPrintNoiseIfRequested(pATI, BIOS, BIOSSize);
        ATIUnmapApertures(pScreenInfo->scrnIndex, pATI);
        return FALSE;
    }

    if ((pScreenInfo->depth > 8) &&
        ((pScreenInfo->weight.red != pScreenInfo->weight.blue) ||
         (pScreenInfo->weight.red != (CARD32)(pScreenInfo->depth / 3)) ||
         ((CARD32)pScreenInfo->depth != (pScreenInfo->weight.red +
                                         pScreenInfo->weight.green +
                                         pScreenInfo->weight.blue))))
    {
        xf86DrvMsg(pScreenInfo->scrnIndex, X_ERROR,
            "Driver does not support weight %d%d%d for depth %d.\n",
            (int)pScreenInfo->weight.red, (int)pScreenInfo->weight.green,
            (int)pScreenInfo->weight.blue, pScreenInfo->depth);
        ATILock(pATI);
        ATIPrintNoiseIfRequested(pATI, BIOS, BIOSSize);
        ATIUnmapApertures(pScreenInfo->scrnIndex, pATI);
        return FALSE;
    }

    /*
     * Set default visual.
     */

    if (!xf86SetDefaultVisual(pScreenInfo, -1))
    {
        ATILock(pATI);
        ATIPrintNoiseIfRequested(pATI, BIOS, BIOSSize);
        ATIUnmapApertures(pScreenInfo->scrnIndex, pATI);
        return FALSE;
    }

    if ((pScreenInfo->depth > 8) &&
        (((pScreenInfo->defaultVisual | DynamicClass) != DirectColor) ||
         ((pScreenInfo->defaultVisual == DirectColor) &&
          (pATI->DAC == ATI_DAC_INTERNAL))))
    {
        xf86DrvMsg(pScreenInfo->scrnIndex, X_ERROR,
            "Driver does not support default visual %s for depth %d.\n",
            xf86GetVisualName(pScreenInfo->defaultVisual),
            pScreenInfo->depth);
        ATILock(pATI);
        ATIPrintNoiseIfRequested(pATI, BIOS, BIOSSize);
        ATIUnmapApertures(pScreenInfo->scrnIndex, pATI);
        return FALSE;
    }

    /*
     * Set colour gamma.
     */
    if (pScreenInfo->depth > 1)
    {
        if (!xf86SetGamma(pScreenInfo, defaultGamma))
        {
            ATILock(pATI);
            ATIPrintNoiseIfRequested(pATI, BIOS, BIOSSize);
            ATIUnmapApertures(pScreenInfo->scrnIndex, pATI);
            return FALSE;
        }
    }

    pATI->depth = pScreenInfo->depth;
    pATI->bitsPerPixel = pScreenInfo->bitsPerPixel;
    pATI->weight = pScreenInfo->weight;
    pATI->XModifier = pATI->bitsPerPixel / UnitOf(pATI->bitsPerPixel);

    /*
     * Determine which CRT controller to use for video modes.
     */
    if ((pATI->Chip >= ATI_CHIP_88800GXC) &&
        (pATI->depth >= 8) &&
        (pATI->Chipset == ATI_CHIPSET_ATI))
    {
        pATI->NewHW.crtc = ATI_CRTC_MACH64;

        xf86DrvMsg(pScreenInfo->scrnIndex, X_INFO,
            "Using Mach64 accelerator CRTC.\n");

        if (pATI->VGAAdapter != ATI_ADAPTER_NONE)
        {
            /*
             * No need for VGA I/O resources during operating state (but they
             * are still decoded).
             */
            pResources =
                xf86SetOperatingState(resVgaIo, pATI->iEntity, ResUnusedOpr);
            if (pResources)
            {
                xf86DrvMsg(pScreenInfo->scrnIndex, X_WARNING,
                    "Logic error setting operating state for VGA I/O.\n");
                xf86FreeResList(pResources);
            }

            if (pATI->CPIO_VGAWonder)
            {
                pResources = xf86SetOperatingState(pATI->VGAWonderResources,
                    pATI->iEntity, ResUnusedOpr);
                if (pResources)
                {
                    xf86DrvMsg(pScreenInfo->scrnIndex, X_WARNING,
                        "Logic error setting operating state for"
                        " VGAWonder I/O.\n");
                    xf86FreeResList(pResources);
                }
            }
        }
    }
    else
    {
        pATI->NewHW.crtc = ATI_CRTC_VGA;

        xf86DrvMsg(pScreenInfo->scrnIndex, X_INFO,
            "Using VGA CRTC.\n");
    }

    /* Complain if VGA is needed but not there */
    if ((pATI->NewHW.crtc == ATI_CRTC_VGA) || !pATI->OptionLinear)
    {
        /* VGA is required at this point */
        if (pATI->VGAAdapter == ATI_ADAPTER_NONE)
        {
            xf86DrvMsg(pScreenInfo->scrnIndex, X_ERROR,
                "VGA is not available through this adapter.\n");
            ATILock(pATI);
            ATIPrintNoiseIfRequested(pATI, BIOS, BIOSSize);
            ATIUnmapApertures(pScreenInfo->scrnIndex, pATI);
            return FALSE;
        }

        if (pATI->Coprocessor != ATI_CHIP_NONE)
        {
            /* Ignore any 8514/A or Mach8 accelerator from this point on */
            pATI->Adapter = pATI->VGAAdapter;

            /* Accelerator and VGA cannot share memory */
            pATI->VideoRAM = 0;
        }
    }

    /*
     * Decide between the CRT and the panel.
     */
    if (pATI->LCDPanelID >= 0)
    {
        if (!pATI->OptionPanelDisplay)
        {
            xf86DrvMsg(pScreenInfo->scrnIndex, X_CONFIG,
                "Using CRT interface and disabling digital flat panel.\n");
        }
        else
        {
            unsigned HDisplay, VDisplay;
            CARD8 ClockMask, PostMask;

            /*
             * Determine porch data.  This groks the mode on entry to extract
             * the width and position of its sync and blanking pulses, and
             * considers any overscan as part of the displayed area, given that
             * the overscan is also stretched.
             *
             * This also attempts to determine panel dimensions but cannot do
             * so for one that is "auto-stretched".
             */

            if (pATI->Chip == ATI_CHIP_264LT)
            {
                pATIHW->lcd_gen_ctrl = inr(LCD_GEN_CTRL);

                /* Set up to read non-shadow registers */
                if (pATIHW->lcd_gen_ctrl & SHADOW_RW_EN)
                    outr(LCD_GEN_CTRL, pATIHW->lcd_gen_ctrl & ~SHADOW_RW_EN);
            }
            else /* if ((pATI->Chip == ATI_CHIP_264LTPRO) ||
                        (pATI->Chip == ATI_CHIP_264XL) ||
                        (pATI->Chip == ATI_CHIP_MOBILITY)) */
            {
                pATIHW->lcd_index = inr(LCD_INDEX);
                pATIHW->config_panel = ATIMach64GetLCDReg(LCD_CONFIG_PANEL);
                pATIHW->lcd_gen_ctrl = ATIMach64GetLCDReg(LCD_GEN_CNTL);

                /* Set up to read non-shadow registers */
                if (pATIHW->lcd_gen_ctrl & SHADOW_RW_EN)
                    ATIMach64PutLCDReg(LCD_GEN_CNTL,
                        pATIHW->lcd_gen_ctrl & ~SHADOW_RW_EN);
            }

            if (!(pATIHW->crtc_gen_cntl & CRTC_EXT_DISP_EN))
            {
                unsigned HBlankStart, HSyncStart, HSyncEnd, HBlankEnd, HTotal;
                unsigned VBlankStart, VSyncStart, VSyncEnd, VBlankEnd, VTotal;

                pATIHW->clock = (inb(R_GENMO) & 0x0CU) >> 2;

                pATIHW->crt[2] = GetReg(CRTX(pATI->CPIO_VGABase), 0x02U);
                pATIHW->crt[3] = GetReg(CRTX(pATI->CPIO_VGABase), 0x03U);
                pATIHW->crt[5] = GetReg(CRTX(pATI->CPIO_VGABase), 0x05U);
                pATIHW->crt[7] = GetReg(CRTX(pATI->CPIO_VGABase), 0x07U);
                pATIHW->crt[9] = GetReg(CRTX(pATI->CPIO_VGABase), 0x09U);
                pATIHW->crt[21] = GetReg(CRTX(pATI->CPIO_VGABase), 0x15U);
                pATIHW->crt[22] = GetReg(CRTX(pATI->CPIO_VGABase), 0x16U);

                pATIHW->crtc_h_total_disp = inr(CRTC_H_TOTAL_DISP);
                pATIHW->crtc_h_sync_strt_wid = inr(CRTC_H_SYNC_STRT_WID);
                pATIHW->crtc_v_total_disp = inr(CRTC_V_TOTAL_DISP);
                pATIHW->crtc_v_sync_strt_wid = inr(CRTC_V_SYNC_STRT_WID);

                /* Switch to shadow registers */
                if (pATI->Chip == ATI_CHIP_264LT)
                    outr(LCD_GEN_CTRL, pATIHW->lcd_gen_ctrl | SHADOW_RW_EN);
                else /* if ((pATI->Chip == ATI_CHIP_264LTPRO) ||
                            (pATI->Chip == ATI_CHIP_264XL) ||
                            (pATI->Chip == ATI_CHIP_MOBILITY)) */
                    ATIMach64PutLCDReg(LCD_GEN_CNTL,
                        pATIHW->lcd_gen_ctrl | SHADOW_RW_EN);

                pATIHW->shadow_vga[2] =
                    GetReg(CRTX(pATI->CPIO_VGABase), 0x02U);
                pATIHW->shadow_vga[3] =
                    GetReg(CRTX(pATI->CPIO_VGABase), 0x03U);
                pATIHW->shadow_vga[5] =
                    GetReg(CRTX(pATI->CPIO_VGABase), 0x05U);
                pATIHW->shadow_vga[7] =
                    GetReg(CRTX(pATI->CPIO_VGABase), 0x07U);
                pATIHW->shadow_vga[9] =
                    GetReg(CRTX(pATI->CPIO_VGABase), 0x09U);
                pATIHW->shadow_vga[21] =
                    GetReg(CRTX(pATI->CPIO_VGABase), 0x15U);
                pATIHW->shadow_vga[22] =
                    GetReg(CRTX(pATI->CPIO_VGABase), 0x16U);

                pATIHW->shadow_h_total_disp = inr(CRTC_H_TOTAL_DISP);
                pATIHW->shadow_h_sync_strt_wid = inr(CRTC_H_SYNC_STRT_WID);
                pATIHW->shadow_v_total_disp = inr(CRTC_V_TOTAL_DISP);
                pATIHW->shadow_v_sync_strt_wid = inr(CRTC_V_SYNC_STRT_WID);

                /*
                 * HSyncStart and HSyncEnd should equal their shadow
                 * counterparts.  Otherwise, due to a chip bug, the panel might
                 * not sync, regardless of which register set is used to drive
                 * the panel.  There are certain combinations of register
                 * values where the panel does in fact sync, but it remains
                 * impossible to accurately determine the horizontal sync pulse
                 * timing actually seen by the panel.
                 *
                 * Note that this hardware bug does not affect the CRT output.
                 */
                if (((pATIHW->crtc_h_sync_strt_wid ^
                      pATIHW->shadow_h_sync_strt_wid) &
                     (CRTC_H_SYNC_STRT | CRTC_H_SYNC_STRT_HI |
                      CRTC_H_SYNC_END_VGA)))
                {
                    xf86DrvMsgVerb(pScreenInfo->scrnIndex, X_NOTICE, 0,
                        "Invalid horizontal sync pulse timing detected in mode"
                        " on server entry.\n");

                    /* Don't trust input timing */
                    pATI->OptionLCDSync = TRUE;
                    ModeType = 0;
                }

                /* Merge in shadow registers as appropriate */
                if (pATIHW->lcd_gen_ctrl & SHADOW_EN)
                {
                    pATIHW->crt[2] = pATIHW->shadow_vga[2];
                    pATIHW->crt[3] = pATIHW->shadow_vga[3];
                    pATIHW->crt[5] = pATIHW->shadow_vga[5];

                    /* XXX Does this apply to VGA?  If so, what about the LT? */
                    if ((pATI->Chip < ATI_CHIP_264LTPRO) ||
                        !(pATIHW->config_panel & DONT_SHADOW_HEND))
                    {
                        pATIHW->crtc_h_total_disp &= ~CRTC_H_DISP;
                        pATIHW->crtc_h_total_disp |=
                            pATIHW->shadow_h_total_disp & CRTC_H_DISP;
                    }

                    pATIHW->crtc_h_total_disp &= ~CRTC_H_TOTAL;
                    pATIHW->crtc_h_total_disp |=
                        pATIHW->shadow_h_total_disp & CRTC_H_TOTAL;
                    pATIHW->crtc_h_sync_strt_wid =
                        pATIHW->shadow_h_sync_strt_wid;

                    /* XXX Does this apply to VGA? */
                    if (pATIHW->lcd_gen_ctrl & USE_SHADOWED_VEND)
                    {
                        pATIHW->crtc_v_total_disp &= ~CRTC_V_DISP;
                        pATIHW->crtc_v_total_disp |=
                            pATIHW->shadow_v_total_disp & CRTC_V_DISP;
                    }

                    if (!(pATIHW->lcd_gen_ctrl & DONT_SHADOW_VPAR))
                    {
                        pATIHW->crt[7] = pATIHW->shadow_vga[7];
                        pATIHW->crt[9] = pATIHW->shadow_vga[9];
                        pATIHW->crt[21] = pATIHW->shadow_vga[21];
                        pATIHW->crt[22] = pATIHW->shadow_vga[22];

                        pATIHW->crtc_v_total_disp &= ~CRTC_V_TOTAL;
                        pATIHW->crtc_v_total_disp |=
                            pATIHW->shadow_v_total_disp & CRTC_V_TOTAL;
                    }
                }

                if (!(pATIHW->lcd_gen_ctrl & DONT_SHADOW_VPAR))
                    pATIHW->crtc_v_sync_strt_wid =
                        pATIHW->shadow_v_sync_strt_wid;

                /*
                 * Decipher input timing.  This is complicated by the fact that
                 * the full width of most timing parameters, except for the
                 * blanking pulses, is only available through the accelerator
                 * registers, not the VGA ones.  Blanking pulse boundaries must
                 * then be interpolated.
                 *
                 * Note that, in VGA mode, the accelerator's sync width fields
                 * are actually end positions, not widths.
                 */
                HDisplay = GetBits(pATIHW->crtc_h_total_disp, CRTC_H_DISP);
                HSyncStart =
                    (GetBits(pATIHW->crtc_h_sync_strt_wid,
                        CRTC_H_SYNC_STRT_HI) *
                     (MaxBits(CRTC_H_SYNC_STRT) + 1)) |
                    GetBits(pATIHW->crtc_h_sync_strt_wid, CRTC_H_SYNC_STRT);
                HSyncEnd = (HSyncStart & ~MaxBits(CRTC_H_SYNC_END_VGA)) |
                    GetBits(pATIHW->crtc_h_sync_strt_wid, CRTC_H_SYNC_END_VGA);
                if (HSyncStart >= HSyncEnd)
                    HSyncEnd += MaxBits(CRTC_H_SYNC_END_VGA) + 1;
                HTotal = GetBits(pATIHW->crtc_h_total_disp, CRTC_H_TOTAL);

                HBlankStart = (HDisplay & ~0xFFU) | pATIHW->crt[2];
                if (HDisplay > HBlankStart)
                    HBlankStart += 0x0100U;
                HBlankEnd = (HSyncEnd & ~0x3FU) |
                    ((pATIHW->crt[5] >> 2) & 0x20U) |
                    (pATIHW->crt[3] & 0x1FU);
                if (HSyncEnd > (HBlankEnd + 1))
                    HBlankEnd += 0x40U;

                VDisplay = GetBits(pATIHW->crtc_v_total_disp, CRTC_V_DISP);
                VSyncStart =
                    GetBits(pATIHW->crtc_v_sync_strt_wid, CRTC_V_SYNC_STRT);
                VSyncEnd = (VSyncStart & ~MaxBits(CRTC_V_SYNC_END_VGA)) |
                    GetBits(pATIHW->crtc_v_sync_strt_wid, CRTC_V_SYNC_END_VGA);
                if (VSyncStart > VSyncEnd)
                    VSyncEnd += MaxBits(CRTC_V_SYNC_END_VGA) + 1;
                VTotal = GetBits(pATIHW->crtc_v_total_disp, CRTC_V_TOTAL);

                VBlankStart = (VDisplay & ~0x03FFU) |
                   ((pATIHW->crt[9] << 4) & 0x0200U) |
                   ((pATIHW->crt[7] << 5) & 0x0100U) | pATIHW->crt[21];
                if (VDisplay > VBlankStart)
                   VBlankStart += 0x0400U;
                VBlankEnd = (VSyncEnd & ~0x00FFU) | pATIHW->crt[22];
                if (VSyncEnd > (VBlankEnd + 1))
                   VBlankEnd += 0x0100U;

                pATI->LCDHBlankWidth = HBlankEnd - HBlankStart;
                pATI->LCDHSyncStart = HSyncStart - HBlankStart;
                pATI->LCDHSyncWidth = HSyncEnd - HSyncStart;

                pATI->LCDVBlankWidth = VBlankEnd - VBlankStart;
                pATI->LCDVSyncStart = VSyncStart - VBlankStart;
                pATI->LCDVSyncWidth = VSyncEnd - VSyncStart;

                HDisplay = HTotal + 5 - pATI->LCDHBlankWidth;
                VDisplay = VTotal + 2 - pATI->LCDVBlankWidth;
            }
            else
            {
                pATIHW->clock = inr(CLOCK_CNTL) & 0x03U;

                pATIHW->crtc_h_total_disp = inr(CRTC_H_TOTAL_DISP);
                pATIHW->crtc_h_sync_strt_wid = inr(CRTC_H_SYNC_STRT_WID);
                pATIHW->crtc_v_total_disp = inr(CRTC_V_TOTAL_DISP);
                pATIHW->crtc_v_sync_strt_wid = inr(CRTC_V_SYNC_STRT_WID);
                pATIHW->ovr_wid_left_right = inr(OVR_WID_LEFT_RIGHT);
                pATIHW->ovr_wid_top_bottom = inr(OVR_WID_TOP_BOTTOM);

                /* Switch to shadow registers */
                if (pATI->Chip == ATI_CHIP_264LT)
                    outr(LCD_GEN_CTRL, pATIHW->lcd_gen_ctrl | SHADOW_RW_EN);
                else /* if ((pATI->Chip == ATI_CHIP_264LTPRO) ||
                            (pATI->Chip == ATI_CHIP_264XL) ||
                            (pATI->Chip == ATI_CHIP_MOBILITY)) */
                    ATIMach64PutLCDReg(LCD_GEN_CNTL,
                        pATIHW->lcd_gen_ctrl | SHADOW_RW_EN);

                /* Oddly enough, there are no shadow overscan registers */
                pATIHW->shadow_h_total_disp = inr(CRTC_H_TOTAL_DISP);
                pATIHW->shadow_h_sync_strt_wid = inr(CRTC_H_SYNC_STRT_WID);
                pATIHW->shadow_v_total_disp = inr(CRTC_V_TOTAL_DISP);
                pATIHW->shadow_v_sync_strt_wid = inr(CRTC_V_SYNC_STRT_WID);

                /*
                 * HSyncStart and HSyncEnd should equal their shadow
                 * counterparts.  Otherwise, due to a chip bug, the panel might
                 * not sync, regardless of which register set is used to drive
                 * the panel.  There are certain combinations of register
                 * values where the panel does in fact sync, but it remains
                 * impossible to accurately determine the horizontal sync pulse
                 * timing actually seen by the panel.
                 *
                 * Note that this hardware bug does not affect the CRT output.
                 */
                if (((pATIHW->crtc_h_sync_strt_wid ^
                      pATIHW->shadow_h_sync_strt_wid) &
                     (CRTC_H_SYNC_STRT | CRTC_H_SYNC_STRT_HI |
                      CRTC_H_SYNC_WID)))
                {
                    xf86DrvMsgVerb(pScreenInfo->scrnIndex, X_NOTICE, 0,
                        "Invalid horizontal sync pulse timing detected in mode"
                        " on server entry.\n");

                    /* Don't trust input timing */
                    pATI->OptionLCDSync = TRUE;
                    ModeType = 0;
                }

                /* Merge in shadow registers as appropriate */
                if (pATIHW->lcd_gen_ctrl & SHADOW_EN)
                {
                    /* XXX What about the LT? */
                    if ((pATI->Chip < ATI_CHIP_264LTPRO) ||
                        !(pATIHW->config_panel & DONT_SHADOW_HEND))
                    {
                        pATIHW->crtc_h_total_disp &= ~CRTC_H_DISP;
                        pATIHW->crtc_h_total_disp |=
                            pATIHW->shadow_h_total_disp & CRTC_H_DISP;
                    }

                    pATIHW->crtc_h_total_disp &= ~CRTC_H_TOTAL;
                    pATIHW->crtc_h_total_disp |=
                        pATIHW->shadow_h_total_disp & CRTC_H_TOTAL;
                    pATIHW->crtc_h_sync_strt_wid =
                        pATIHW->shadow_h_sync_strt_wid;

                    if (pATIHW->lcd_gen_ctrl & USE_SHADOWED_VEND)
                    {
                        pATIHW->crtc_v_total_disp &= ~CRTC_V_DISP;
                        pATIHW->crtc_v_total_disp |=
                            pATIHW->shadow_v_total_disp & CRTC_V_DISP;
                    }

                    if (!(pATIHW->lcd_gen_ctrl & DONT_SHADOW_VPAR))
                    {
                        pATIHW->crtc_v_total_disp &= ~CRTC_V_TOTAL;
                        pATIHW->crtc_v_total_disp |=
                            pATIHW->shadow_v_total_disp & CRTC_V_TOTAL;
                    }
                }

                if (!(pATIHW->lcd_gen_ctrl & DONT_SHADOW_VPAR))
                    pATIHW->crtc_v_sync_strt_wid =
                        pATIHW->shadow_v_sync_strt_wid;

                /* Decipher input timing */
                HDisplay = GetBits(pATIHW->crtc_h_total_disp, CRTC_H_DISP) +
                    GetBits(pATIHW->ovr_wid_left_right, OVR_WID_LEFT) +
                    GetBits(pATIHW->ovr_wid_left_right, OVR_WID_RIGHT);
                VDisplay = GetBits(pATIHW->crtc_v_total_disp, CRTC_V_DISP) +
                    GetBits(pATIHW->ovr_wid_top_bottom, OVR_WID_TOP) +
                    GetBits(pATIHW->ovr_wid_top_bottom, OVR_WID_BOTTOM);

                pATI->LCDHSyncStart =
                    (GetBits(pATIHW->crtc_h_sync_strt_wid,
                        CRTC_H_SYNC_STRT_HI) *
                     (MaxBits(CRTC_H_SYNC_STRT) + 1)) +
                    GetBits(pATIHW->crtc_h_sync_strt_wid, CRTC_H_SYNC_STRT) -
                    HDisplay;
                pATI->LCDHSyncWidth =
                    GetBits(pATIHW->crtc_h_sync_strt_wid, CRTC_H_SYNC_WID);
                pATI->LCDHBlankWidth =
                    GetBits(pATIHW->crtc_h_total_disp, CRTC_H_TOTAL) -
                    HDisplay;
                pATI->LCDVSyncStart =
                    GetBits(pATIHW->crtc_v_sync_strt_wid, CRTC_V_SYNC_STRT) -
                    VDisplay;
                pATI->LCDVSyncWidth =
                    GetBits(pATIHW->crtc_v_sync_strt_wid, CRTC_V_SYNC_WID);
                pATI->LCDVBlankWidth =
                    GetBits(pATIHW->crtc_v_total_disp, CRTC_V_TOTAL) -
                    VDisplay;

                HDisplay++;
                VDisplay++;
            }

            /* Restore LCD registers */
            if (pATI->Chip == ATI_CHIP_264LT)
            {
                outr(LCD_GEN_CTRL, pATIHW->lcd_gen_ctrl);
            }
            else /* if ((pATI->Chip == ATI_CHIP_264LTPRO) ||
                        (pATI->Chip == ATI_CHIP_264XL) ||
                        (pATI->Chip == ATI_CHIP_MOBILITY)) */
            {
                ATIMach64PutLCDReg(LCD_GEN_CNTL, pATIHW->lcd_gen_ctrl);
                outr(LCD_INDEX, pATIHW->lcd_index);
            }

            HDisplay <<= 3;
            pATI->LCDHSyncStart <<= 3;
            pATI->LCDHSyncWidth <<= 3;
            pATI->LCDHBlankWidth <<= 3;

            /* Calculate panel dimensions implied by the input timing */
            if ((pATIHW->horz_stretching &
                 (HORZ_STRETCH_EN | AUTO_HORZ_RATIO)) ==
                HORZ_STRETCH_EN)
            {
                if (pATIHW->horz_stretching & HORZ_STRETCH_MODE)
                {
                    if (pATIHW->horz_stretching & HORZ_STRETCH_BLEND)
                    {
                        HDisplay =
                            (HDisplay * (MaxBits(HORZ_STRETCH_BLEND) + 1)) /
                            GetBits(pATIHW->horz_stretching,
                                HORZ_STRETCH_BLEND);
                    }
                }
                else if (((pATIHW->horz_stretching & HORZ_STRETCH_LOOP) >
                          HORZ_STRETCH_LOOP15) ||
                         (pATIHW->horz_stretching &
                          SetBits(1, HORZ_STRETCH_RATIO)))
                {
                    xf86DrvMsg(pScreenInfo->scrnIndex, X_WARNING,
                        "Ignoring invalid horizontal stretch ratio in mode on"
                        " server entry.\n");
                }
                else
                {
                    IOValue =
                        GetBits(pATIHW->horz_stretching, HORZ_STRETCH_RATIO);

                    switch (GetBits(pATIHW->horz_stretching,
                                    HORZ_STRETCH_LOOP))
                    {
                        case GetBits(HORZ_STRETCH_LOOP09, HORZ_STRETCH_LOOP):
                            i = 9;
                            IOValue &= (1 << 9) - 1;
                            break;

                        case GetBits(HORZ_STRETCH_LOOP11, HORZ_STRETCH_LOOP):
                            i = 11;
                            IOValue &= (1 << 11) - 1;
                            break;

                        case GetBits(HORZ_STRETCH_LOOP12, HORZ_STRETCH_LOOP):
                            i = 12;
                            IOValue &= (1 << 12) - 1;
                            break;

                        case GetBits(HORZ_STRETCH_LOOP14, HORZ_STRETCH_LOOP):
                            i = 14;
                            IOValue &= (1 << 14) - 1;
                            break;

                        case GetBits(HORZ_STRETCH_LOOP15, HORZ_STRETCH_LOOP):
                        default:    /* Muffle compiler */
                            i = 15;
                            IOValue &= (1 << 15) - 1;
                            break;
                    }

                    if (IOValue)
                    {
                        /* Count the number of bits in IOValue */
                        j = (IOValue >> 1) & 0x36DBU;
                        j = IOValue - j - ((j >> 1) & 0x36DBU);
                        j = ((j + (j >> 3)) & 0x71C7U) % 0x3FU;

                        HDisplay = (HDisplay * i) / j;
                    }
                }
            }

            if ((pATIHW->vert_stretching & VERT_STRETCH_EN) &&
                !(pATIHW->ext_vert_stretch & AUTO_VERT_RATIO))
            {
                if ((pATIHW->vert_stretching & VERT_STRETCH_USE0) ||
                    (VDisplay <= 350))
                    IOValue =
                        GetBits(pATIHW->vert_stretching, VERT_STRETCH_RATIO0);
                else if (VDisplay <= 400)
                    IOValue =
                        GetBits(pATIHW->vert_stretching, VERT_STRETCH_RATIO1);
                else if ((VDisplay <= 480) ||
                         !(pATIHW->ext_vert_stretch & VERT_STRETCH_RATIO3))
                    IOValue =
                        GetBits(pATIHW->vert_stretching, VERT_STRETCH_RATIO2);
                else
                    IOValue =
                        GetBits(pATIHW->ext_vert_stretch, VERT_STRETCH_RATIO3);

                if (IOValue)
                    VDisplay =
                        (VDisplay * (MaxBits(VERT_STRETCH_RATIO0) + 1)) /
                        IOValue;
            }

            /* Match calculated dimensions to probed dimensions */
            if (!pATI->LCDHorizontal)
            {
                if ((pATIHW->horz_stretching &
                     (HORZ_STRETCH_EN | AUTO_HORZ_RATIO)) !=
                     (HORZ_STRETCH_EN | AUTO_HORZ_RATIO))
                    pATI->LCDHorizontal = HDisplay;
            }
            else if (pATI->LCDHorizontal != (int)HDisplay)
            {
                if ((pATIHW->horz_stretching &
                    (HORZ_STRETCH_EN | AUTO_HORZ_RATIO)) !=
                    (HORZ_STRETCH_EN | AUTO_HORZ_RATIO))
                    xf86DrvMsgVerb(pScreenInfo->scrnIndex, X_WARNING, 4,
                        "Inconsistent panel horizontal dimension:"
                        "  %d and %d.\n", pATI->LCDHorizontal, HDisplay);
                HDisplay = pATI->LCDHorizontal;
            }

            if (!pATI->LCDVertical)
            {
                if (!(pATIHW->vert_stretching & VERT_STRETCH_EN) ||
                    !(pATIHW->ext_vert_stretch & AUTO_VERT_RATIO))
                    pATI->LCDVertical = VDisplay;
            }
            else if (pATI->LCDVertical != (int)VDisplay)
            {
                if (!(pATIHW->vert_stretching & VERT_STRETCH_EN) ||
                    !(pATIHW->ext_vert_stretch & AUTO_VERT_RATIO))
                    xf86DrvMsgVerb(pScreenInfo->scrnIndex, X_WARNING, 4,
                        "Inconsistent panel vertical dimension:  %d and %d.\n",
                        pATI->LCDVertical, VDisplay);
                VDisplay = pATI->LCDVertical;
            }

            if (!pATI->LCDHorizontal || !pATI->LCDVertical)
            {
                if (pATI->LCDPanelID || (pATI->Chip <= ATI_CHIP_264LTPRO))
                    xf86DrvMsg(pScreenInfo->scrnIndex, X_ERROR,
                        "Unable to determine dimensions of panel (ID %d).\n",
                        pATI->LCDPanelID);
                else
                    xf86DrvMsg(pScreenInfo->scrnIndex, X_ERROR,
                        "Unable to determine dimensions of panel.\n");

                ATILock(pATI);
                ATIPrintNoiseIfRequested(pATI, BIOS, BIOSSize);
                ATIUnmapApertures(pScreenInfo->scrnIndex, pATI);
                return FALSE;
            }

            /* If the mode on entry wasn't stretched, adjust timings */
            if (!(pATIHW->horz_stretching & HORZ_STRETCH_EN) &&
                (pATI->LCDHorizontal > (int)HDisplay))
            {
                HDisplay = pATI->LCDHorizontal - HDisplay;
                if (pATI->LCDHSyncStart >= HDisplay)
                    pATI->LCDHSyncStart -= HDisplay;
                else
                    pATI->LCDHSyncStart = 0;
                pATI->LCDHBlankWidth -= HDisplay;
                HDisplay = pATI->LCDHSyncStart + pATI->LCDHSyncWidth;
                if (pATI->LCDHBlankWidth < HDisplay)
                    pATI->LCDHBlankWidth = HDisplay;
            }

            if (!(pATIHW->vert_stretching & VERT_STRETCH_EN) &&
                (pATI->LCDVertical > (int)VDisplay))
            {
                VDisplay = pATI->LCDVertical - VDisplay;
                if (pATI->LCDVSyncStart >= VDisplay)
                    pATI->LCDVSyncStart -= VDisplay;
                else
                    pATI->LCDVSyncStart = 0;
                pATI->LCDVBlankWidth -= VDisplay;
                VDisplay = pATI->LCDVSyncStart + pATI->LCDVSyncWidth;
                if (pATI->LCDVBlankWidth < VDisplay)
                    pATI->LCDVBlankWidth = VDisplay;
            }

            if (pATI->LCDPanelID || (pATI->Chip <= ATI_CHIP_264LTPRO))
                xf86DrvMsg(pScreenInfo->scrnIndex, X_PROBED,
                    "%dx%d panel (ID %d) detected.\n",
                    pATI->LCDHorizontal, pATI->LCDVertical, pATI->LCDPanelID);
            else
                xf86DrvMsg(pScreenInfo->scrnIndex, X_PROBED,
                    "%dx%d panel detected.\n",
                    pATI->LCDHorizontal, pATI->LCDVertical);

            if (LCDPanelInfo)
            {
                for (i = 0;  i < 24;  i++)
                    Buffer[i] = BIOSByte(LCDPanelInfo + 1 + i);
                for (;  --i >= 0;  )
                    if (Buffer[i] && Buffer[i] != ' ')
                    {
                        Buffer[i + 1] = '\0';
                        xf86DrvMsg(pScreenInfo->scrnIndex, X_PROBED,
                            "Panel model %s.\n", Buffer);
                        break;
                    }
            }

            /*
             * Determine panel clock.  This must be done after option
             * processing so that the adapter's reference frequency is always
             * available.
             *
             * Get post divider.  A GCC bug has caused the following expression
             * to be broken down into its individual components.
             */
            ClockMask = PLL_VCLK0_XDIV << pATIHW->clock;
            PostMask = PLL_VCLK0_POST_DIV << (pATIHW->clock * 2);
            i = GetBits(ATIMach64GetPLLReg(PLL_XCLK_CNTL), ClockMask);
            i *= MaxBits(PLL_VCLK0_POST_DIV) + 1;
            i |= GetBits(ATIMach64GetPLLReg(PLL_VCLK_POST_DIV), PostMask);

            /* Calculate clock of mode on entry */
            Numerator = ATIMach64GetPLLReg(PLL_VCLK0_FB_DIV + pATIHW->clock) *
                pATI->ReferenceNumerator;
            Denominator = pATI->ClockDescriptor.MinM *
                pATI->ReferenceDenominator *
                pATI->ClockDescriptor.PostDividers[i];
            pATI->LCDClock = ATIDivide(Numerator, Denominator, 1, 0);

            xf86DrvMsg(pScreenInfo->scrnIndex, X_PROBED,
                "Panel clock is %.3f MHz.\n",
                (double)(pATI->LCDClock) / 1000.0);

            xf86DrvMsg(pScreenInfo->scrnIndex, X_INFO,
                "Using digital flat panel interface%s.\n",
                pATI->OptionCRTDisplay ?
                    " to display on both CRT and panel" : "");
        }
    }

    /*
     * Finish detecting video RAM size.
     */
    AcceleratorVideoRAM = pScreenInfo->videoRam = pATI->VideoRAM;
    if (pATI->Chip == ATI_CHIP_VGA)
    {
        if (pATI->depth <= 4)
            VGAVideoRAM = 256;
        else
            VGAVideoRAM = 64;

        /* For VGA, allow a lower override */
        if ((pGDev->videoRam > 0) && (pGDev->videoRam < VGAVideoRAM))
            VGAVideoRAM = pGDev->videoRam;
    }
    else if (pATI->CPIO_VGAWonder)
    {
        /*
         * XXX There's an assumption here that the values retrieved are those
         * set by BIOS initialisation.
         */
        if (pATI->Chip <= ATI_CHIP_18800_1)
        {
            VGAVideoRAM =
                videoRamSizes[GetBits(ATIGetExtReg(0xBBU), 0x20U) + 1];
            if (AcceleratorVideoRAM > 512)
                AcceleratorVideoRAM = 512;
        }
        else
        {
            IOValue = ATIGetExtReg(0xB0U);
            if (IOValue & 0x08U)
                VGAVideoRAM = 1024;
            else if (IOValue & 0x10U)
                VGAVideoRAM = 512;
            else
                VGAVideoRAM = 256;
            if (AcceleratorVideoRAM > 1024)
                AcceleratorVideoRAM = 1024;
        }
    }

    /* Check for hardware limitations */
    if (!AcceleratorVideoRAM)
    {
        pScreenInfo->videoRam = pATI->VideoRAM = VGAVideoRAM;

        /*
         * VGA Wonder V3's, V4's and V5's don't appear to support banking in
         * planar modes.
         */
        if ((pATI->depth <= 4) &&
            (pATI->Chip <= ATI_CHIP_18800_1) &&
            (VGAVideoRAM > 256))
        {
            if (pATI->OptionDevel)
            {
                xf86DrvMsg(pScreenInfo->scrnIndex, X_NOTICE,
                    "Virtual resolutions requiring more than %s kB\n of video"
                    " memory might not function properly.\n",
                    (pATI->depth == 1) ? "64" : "256");
            }
            else
            {
                xf86DrvMsg(pScreenInfo->scrnIndex, X_NOTICE,
                    "VideoRAM reduced to 256 kB due to hardware"
                    " limitations.\n");
                pScreenInfo->videoRam = 256;
            }
        }
    }
    else if ((pATI->NewHW.crtc == ATI_CRTC_MACH64) ||
             (pATI->Chip >= ATI_CHIP_264CT))
    {
        if (pATI->depth >= 8)
        {
            /* Get adapter's linear aperture configuration */
            pATIHW->config_cntl = inr(CONFIG_CNTL);
            pATI->LinearBase =
                GetBits(pATIHW->config_cntl, CFG_MEM_AP_LOC) << 22;
            if ((pATIHW->config_cntl & CFG_MEM_AP_SIZE) != CFG_MEM_AP_SIZE)
            {
                pATI->LinearSize =
                    GetBits(pATIHW->config_cntl, CFG_MEM_AP_SIZE) << 22;

                /*
                 * Linear aperture could have been disabled (but still
                 * assigned) by BIOS initialisation.
                 */
                if (pATI->LinearBase && !pATI->LinearSize)
                {
                    if ((pATI->Chip <= ATI_CHIP_88800GXD) &&
                        (pATI->VideoRAM < 4096))
                        pATI->LinearSize = 4 * 1024 * 1024;
                    else
                        pATI->LinearSize = 8 * 1024 * 1024;
                }
            }

            /* Except for PCI & AGP, allow for user override */
            if (!pVideo)
            {
                if (pATI->Chip == ATI_CHIP_88800CX)
                    IOValue = ~((CARD32)((1 << 23) - 1));
                else if (pATI->Chip >= ATI_CHIP_88800GXE)
                    IOValue = ~((CARD32)((1 << 24) - 1));
                else if (pATI->VideoRAM >= 4096)
                    IOValue = ~((CARD32)((1 << 23) - 1));
                else
                    IOValue = ~((CARD32)((1 << 22) - 1));

                IOValue &= pGDev->MemBase;
                if (IOValue &&
                    (IOValue <= (CARD32)(MaxBits(CFG_MEM_AP_LOC) << 22)))
                    pATI->LinearBase = IOValue;

                if (!pATI->LinearBase)
                {
                    xf86DrvMsg(pScreenInfo->scrnIndex, X_WARNING,
                        "Linear aperture not configured.  Specify \"MemBase\""
                        " override in XF86Config \"Device\" section.\n");
                }
                else
                {
                    if (!pATI->LinearSize)
                    {
                        if ((pATI->Chip <= ATI_CHIP_88800GXD) &&
                            (pATI->VideoRAM < 4096))
                            pATI->LinearSize = 4 * 1024 * 1024;
                        else
                            pATI->LinearSize = 8 * 1024 * 1024;
                    }

                    Resources[0].type = ResExcMemBlock | ResBus;
                    Resources[0].rBegin = pATI->LinearBase;
                    Resources[0].rEnd =
                        pATI->LinearBase + pATI->LinearSize - 1;
                    if (xf86RegisterResources(pATI->iEntity, Resources,
                                              ResNone))
                    {
                        xf86DrvMsg(pScreenInfo->scrnIndex, X_WARNING,
                            "Unable to register %d MB linear aperture at"
                            " 0x%08lX.\n", pATI->LinearSize >> 10,
                            pATI->LinearBase);

                        pATI->LinearSize = 0;
                    }
                }
            }

            if (pATI->LinearBase && pATI->LinearSize)
            {
                /*
                 * Unless specified in PCI configuration space, set MMIO
                 * address to tail end of linear aperture.
                 */
                if (!pATI->Block0Base)
                {
                    pATI->Block0Base =
                        pATI->LinearBase + pATI->LinearSize - 0x00000400U;
                    pATI->MMIOInLinear = TRUE;
                }

                AcceleratorVideoRAM = pATI->LinearSize >> 10;

                /*
                 * Account for MMIO area at the tail end of the linear
                 * aperture, if it is needed or if it cannot be disabled.
                 */
                if (pATI->MMIOInLinear || (pATI->Chip < ATI_CHIP_264VTB))
                    AcceleratorVideoRAM -= 2;

                ServerVideoRAM = pATI->VideoRAM;

                if (pATI->Cursor > ATI_CURSOR_SOFTWARE)
                {
                    /*
                     * Allocate a 1 kB cursor image area at the top of the
                     * little-endian aperture, just before any MMIO area that
                     * might also be there.
                     */
                    if (ServerVideoRAM > AcceleratorVideoRAM)
                        ServerVideoRAM = AcceleratorVideoRAM;

                    ServerVideoRAM--;
                    pATI->CursorOffset = ServerVideoRAM << 10;
                    pATI->CursorBase = pATI->LinearBase + pATI->CursorOffset;

                    xf86DrvMsg(pScreenInfo->scrnIndex, X_INFO,
                        "Storing hardware cursor image at 0x%08lX.\n",
                        pATI->CursorBase);
                }

                if (pATI->OptionLinear)
                {
                    CARD32 PageSize = (getpagesize() + 1023) >> 10;

                    /*
                     * Note:  At this point, big-endian depths 15 & 16 without
                     * an appropriate aperture have already been disallowed.
                     */
                    if ((ATIEndian.endian == ATI_LITTLE_ENDIAN) ||
                        (pATI->Chip < ATI_CHIP_264VTB))
                    {
                        /*
                         * MMIO areas must be mmap()'ed separately to avoid
                         * write-combining them.  Thus, they might not end up
                         * still adjacent with the little-endian linear
                         * aperture after mmap()'ing.  So, round down the
                         * linear aperture size to avoid an overlap.  Any
                         * hardware cursor image area might not end up being
                         * write-combined, but this seems preferable to further
                         * reducing the video memory size advertised to the
                         * server.
                         *
                         * XXX Ideally this should be dealt with in the
                         *     os-support layer, i.e., it should be possible to
                         *     reset a subarea's write-combining after it has
                         *     been mmap()'ed, but doing so currently causes
                         *     the removal of write-combining for the entire
                         *     aperture.
                         */
                        if (pATI->MMIOInLinear)
                            AcceleratorVideoRAM -=
                                AcceleratorVideoRAM % PageSize;
                    }
                    else
                    {
                        /*
                         * Big-endian apertures are 8 MB higher and don't
                         * contain an MMIO area.
                         */
                        pATI->LinearBase += 0x00800000U;
                        AcceleratorVideoRAM = pATI->LinearSize >> 10;
                    }

                    if (ServerVideoRAM > AcceleratorVideoRAM)
                        ServerVideoRAM = AcceleratorVideoRAM;
                    else if (AcceleratorVideoRAM > pATI->VideoRAM)
                        AcceleratorVideoRAM = pATI->VideoRAM;

                    xf86DrvMsg(pScreenInfo->scrnIndex, X_INFO,
                        "Using %d MB linear aperture at 0x%08lX.\n",
                        pATI->LinearSize >> 20, pATI->LinearBase);

                    PageSize--;
                    AcceleratorVideoRAM =
                        (AcceleratorVideoRAM + PageSize) & ~PageSize;

                    /* Only mmap what is needed */
                    ApertureSize = pATI->LinearSize =
                        AcceleratorVideoRAM << 10;
                }

                if (ServerVideoRAM < pATI->VideoRAM)
                {
                    pScreenInfo->videoRam = ServerVideoRAM;
                    xf86DrvMsg(pScreenInfo->scrnIndex, X_NOTICE,
                        "Virtual resolutions will be limited to %d kB\n due to"
                        " linear aperture size and/or placement of hardware"
                        " cursor image area.\n", ServerVideoRAM);
                }
            }
        }

        /* Set up for a banked aperture */
        if (pATI->VGAAdapter != ATI_ADAPTER_NONE)
        {
            pATI->UseSmallApertures = TRUE;

            /* Set banking functions */
            if (pATI->depth <= 4)
            {
                pATI->NewHW.SetBank = ATIMach64SetBankPlanar;
                pATI->BankInfo.SetSourceBank = ATIMach64SetReadPlanar;
                pATI->BankInfo.SetDestinationBank = ATIMach64SetWritePlanar;
                pATI->BankInfo.SetSourceAndDestinationBanks =
                    ATIMach64SetReadWritePlanar;
            }
            else
            {
                pATI->NewHW.SetBank = ATIMach64SetBankPacked;
                pATI->BankInfo.SetSourceBank = ATIMach64SetReadPacked;
                pATI->BankInfo.SetDestinationBank = ATIMach64SetWritePacked;
                pATI->BankInfo.SetSourceAndDestinationBanks =
                    ATIMach64SetReadWritePacked;
            }

            /*
             * Unless specified in PCI configuration space, or at the top of
             * of a little-endian linear aperture, set MMIO address to the one
             * just above the VGA aperture.  This does not work on the CT
             * (maybe others).
             */
            if (!pATI->Block0Base &&
                ((pATI->Chip < ATI_CHIP_264CT) ||
                 (pATI->Chip >= ATI_CHIP_264VT) ||
                 pATI->OptionDevel))
                pATI->Block0Base = 0x000BFC00U;
        }

        if (!pATI->OptionLinear)
            pATI->LinearBase = 0;       /* Not needed */

        if (!pATI->LinearBase || !pATI->LinearSize)
        {
            if (pATI->VGAAdapter == ATI_ADAPTER_NONE)
            {
                xf86DrvMsg(pScreenInfo->scrnIndex, X_ERROR,
                    "Linear aperture not available.\n");
                ATILock(pATI);
                ATIPrintNoiseIfRequested(pATI, BIOS, BIOSSize);
                ATIUnmapApertures(pScreenInfo->scrnIndex, pATI);
                return FALSE;
            }

            /* Insurance */
            pATI->LinearBase = pATI->LinearSize = 0;
        }

        if (pATI->Block0Base)
        {
            xf86DrvMsg(pScreenInfo->scrnIndex, X_INFO,
                "Using Block 0 MMIO aperture at 0x%08lX.\n", pATI->Block0Base);

            /* Set Block1 MMIO address if supported */
            if (pATI->Chip >= ATI_CHIP_264VT)
            {
                pATI->Block1Base = pATI->Block0Base - 0x00000400U;
                xf86DrvMsg(pScreenInfo->scrnIndex, X_INFO,
                    "Using Block 1 MMIO aperture at 0x%08lX.\n",
                    pATI->Block1Base);
            }
        }
    }
    else
    /*
     * After BIOS initialisation, the accelerator (if any) and the VGA won't
     * necessarily agree on the amount of video memory, depending on whether or
     * where the memory boundary is configured.  Any discrepancy will be
     * resolved by ATIModePreInit().
     *
     * However, it's possible that there is more video memory than VGA Wonder
     * can architecturally handle.
     */
    if (((pATI->Chip < ATI_CHIP_68800) || (pATI->Chip > ATI_CHIP_68800AX)) &&
        (AcceleratorVideoRAM < pScreenInfo->videoRam))
    {
        if (pATI->OptionDevel)
        {
            if (pATI->depth == 1)
                AcceleratorVideoRAM /= 4;

            xf86DrvMsg(pScreenInfo->scrnIndex, X_NOTICE,
                "Virtual resolutions requiring more than %d kB\n of video"
                " memory might not function correctly.\n",
                AcceleratorVideoRAM);
        }
        else
        {
            xf86DrvMsg(pScreenInfo->scrnIndex, X_NOTICE,
                "VideoRAM reduced to %d kB due to hardware limitations.\n",
                AcceleratorVideoRAM);

            pScreenInfo->videoRam = AcceleratorVideoRAM;
        }
    }

    if (pATI->OptionLinear)
    {
        if (!pATI->LinearBase)
        {
            xf86DrvMsg(pScreenInfo->scrnIndex, X_WARNING,
                "Linear aperture not supported in this configuration.\n");
            pATI->OptionLinear = FALSE;
        }
        else if (pATI->VGAAdapter != ATI_ADAPTER_NONE)
        {
            /*
             * Free VGA memory aperture during operating state (but it is still
             * decoded).
             */
            pResources = xf86SetOperatingState(resVgaMem, pATI->iEntity,
                ResUnusedOpr);
            if (pResources)
            {
                xf86DrvMsg(pScreenInfo->scrnIndex, X_WARNING,
                    "Logic error setting operating state for VGA memory"
                    " aperture.\n");
                xf86FreeResList(pResources);
            }
        }
    }

    if ((pATI->Cursor > ATI_CURSOR_SOFTWARE) && !pATI->CursorBase)
    {
        xf86DrvMsg(pScreenInfo->scrnIndex, X_WARNING,
            "Unable to store hardware cursor image.  Reverting to software"
            " cursor.\n");
        pATI->Cursor = ATI_CURSOR_SOFTWARE;
    }

    /*
     * Remap apertures.  Must lock and re-unlock around this in case the
     * remapping fails.
     */
    ATILock(pATI);
    ATIUnmapApertures(pScreenInfo->scrnIndex, pATI);
    if (!ATIMapApertures(pScreenInfo->scrnIndex, pATI))
        return FALSE;

    ATIUnlock(pATI);

    if (pATI->OptionAccel)
    {
        if (!pATI->Block0Base || (pATI->NewHW.crtc == ATI_CRTC_VGA))
        {
            xf86DrvMsg(pScreenInfo->scrnIndex, X_WARNING,
                "Acceleration not supported in this configuration.\n");
            pATI->OptionAccel = FALSE;
        }
        else
        {
            xf86DrvMsg(pScreenInfo->scrnIndex, X_INFO,
                "MMIO write caching %sabled.\n",
                pATI->OptionMMIOCache ? "en" : "dis");
        }
    }

    if (pATI->Adapter >= ATI_ADAPTER_MACH32)
    {
        if (pATI->Chip >= ATI_CHIP_264CT)
            ATIReportMemory(pScreenInfo, pATI,
                ATIMemoryTypeNames_264xT[pATI->MemoryType]);
        else if (pATI->Chip == ATI_CHIP_88800CX)
            ATIReportMemory(pScreenInfo, pATI,
                ATIMemoryTypeNames_88800CX[pATI->MemoryType]);
        else
            ATIReportMemory(pScreenInfo, pATI,
                ATIMemoryTypeNames_Mach[pATI->MemoryType]);
    }
    else if (pATI->Adapter >= ATI_ADAPTER_V3)
    {
        ATIReportMemory(pScreenInfo, pATI,
            (ATIGetExtReg(0xB7U) & 0x04U) ? "DRAM" : "VRAM");
    }
    else
    {
        ATIReportMemory(pScreenInfo, pATI, "video memory");
    }

    /*
     * Finish banking setup.  This needs to be fixed to not assume the mode on
     * entry is a VGA mode.  XXX
     */
    if (pATI->VGAAdapter == ATI_ADAPTER_NONE)
    {
        pATIHW->crtc = pATI->NewHW.crtc;
        pATIHW->SetBank = (ATIBankProcPtr)NoopDDA;
        pATI->BankInfo.BankSize = 0;            /* No banking */
    }
    else
    {
        pATIHW->crtc = ATI_CRTC_VGA;
#if 0 /* ___NOT_YET___ */
        if (pATI->ChipHasSUBSYS_CNTL)
        {
        }
        else
#endif
        if ((pATI->Chip >= ATI_CHIP_88800GXC) &&
            (pATI->LockData.crtc_gen_cntl & CRTC_EXT_DISP_EN))
        {
            pATIHW->crtc = ATI_CRTC_MACH64;
        }

        if (pATI->depth <= 4)
        {
            pATI->BankInfo.nBankDepth = 1;
            pATI->NewHW.nPlane = 4;
        }
        else
        {
            pATI->BankInfo.nBankDepth = pATI->depth;
            pATI->NewHW.nPlane = 1;
        }

        if ((pATIHW->crtc != ATI_CRTC_VGA) || (GetReg(SEQX, 0x04U) & 0x08U))
            pATIHW->nPlane = 1;
        else
            pATIHW->nPlane = 4;

        pATIHW->nBank = ATIDivide(pATI->VideoRAM,
            pATIHW->nPlane * pATI->BankInfo.BankSize, 10, 1);
        pATI->NewHW.nBank = ATIDivide(pATI->VideoRAM,
            pATI->NewHW.nPlane * pATI->BankInfo.BankSize, 10, 1);

        if (pATI->VGAAdapter == ATI_ADAPTER_VGA)
        {
            pATIHW->SetBank = pATI->NewHW.SetBank =
                (ATIBankProcPtr)NoopDDA;
            pATIHW->nBank = pATI->NewHW.nBank = 1;
        }
        else if (!pATI->UseSmallApertures)
        {
            pATIHW->SetBank = pATI->NewHW.SetBank;
        }
        else if ((pATIHW->crtc == ATI_CRTC_VGA) &&
                 !(pATI->LockData.config_cntl & CFG_MEM_VGA_AP_EN))
        {
            pATIHW->SetBank = (ATIBankProcPtr)NoopDDA;
            pATIHW->nBank = 1;
        }
        else if (pATIHW->nPlane == 1)
        {
            pATIHW->SetBank = ATIMach64SetBankPacked;
        }
        else
        {
            pATIHW->SetBank = ATIMach64SetBankPlanar;
        }

        if (((ApertureSize * pATI->depth) / pATI->BankInfo.nBankDepth) >=
            (unsigned)(pScreenInfo->videoRam * 1024))
            pATI->BankInfo.BankSize = 0;        /* No banking */
    }

    if (pATI->OptionShadowFB)
    {
        /* Until ShadowFB becomes a true screen wrapper, if it ever does... */
        if (pATI->BankInfo.BankSize)
        {
            xf86DrvMsg(pScreenInfo->scrnIndex, X_WARNING,
                "Cannot shadow a banked frame buffer.\n");
            pATI->OptionShadowFB = FALSE;
        }
        else if (pATI->depth < 8)
        {
            xf86DrvMsg(pScreenInfo->scrnIndex, X_WARNING,
                "Cannot shadow a planar frame buffer.\n");
            pATI->OptionShadowFB = FALSE;
        }
        else if (pATI->OptionAccel)
        {
            xf86DrvMsg(pScreenInfo->scrnIndex, X_WARNING,
                "Cannot shadow an accelerated frame buffer.\n");
            pATI->OptionShadowFB = FALSE;
        }
        else
        {
            xf86DrvMsg(pScreenInfo->scrnIndex, X_INFO,
                "Using shadow frame buffer.\n");
        }
    }

    /* 264VT-B's and later have DSP registers */
    if ((pATI->Chip >= ATI_CHIP_264VTB) &&
        !ATIDSPPreInit(pScreenInfo->scrnIndex, pATI))
    {
        ATILock(pATI);
        ATIPrintNoiseIfRequested(pATI, BIOS, BIOSSize);
        ATIUnmapApertures(pScreenInfo->scrnIndex, pATI);
        return FALSE;
    }

    /*
     * Determine minClock and maxClock.  For adapters with supported
     * programmable clock generators, start with an absolute maximum.
     */
    if (pATI->ClockDescriptor.MaxN > 0)
    {
        Numerator = pATI->ClockDescriptor.MaxN * pATI->ReferenceNumerator;
        Denominator = pATI->ClockDescriptor.MinM * pATI->ReferenceDenominator *
            pATI->ClockDescriptor.PostDividers[0];

        /*
         * An integrated PLL behaves as though the reference frequency were
         * doubled.  It also does not appear to care about the colour depth.
         */
        if (pATI->ProgrammableClock == ATI_CLOCK_INTERNAL)
            Numerator <<= 1;
        else if (pATI->depth > 8)
            Denominator *= (pATI->bitsPerPixel / 8);

        ATIClockRange.maxClock = (Numerator / (Denominator * 1000)) * 1000;

        Numerator = pATI->ClockDescriptor.MinN * pATI->ReferenceNumerator;
        Denominator = pATI->ClockDescriptor.MaxM * pATI->ReferenceDenominator *
            pATI->ClockDescriptor.PostDividers[pATI->ClockDescriptor.NumD - 1];

        if (pATI->ProgrammableClock == ATI_CLOCK_INTERNAL)
            Numerator <<= 1;

        ATIClockRange.minClock = (Numerator / (Denominator * 1000)) * 1000;

        if (pATI->XCLKFeedbackDivider)
        {
            /* Possibly reduce maxClock due to memory bandwidth */
            Numerator = pATI->XCLKFeedbackDivider * 2 *
                pATI->ReferenceNumerator;
            Denominator = pATI->ClockDescriptor.MinM *
                pATI->XCLKReferenceDivider * pATI->ReferenceDenominator;

            if (pATI->depth >= 8)
                Denominator *= pATI->bitsPerPixel / 4;

            i = (6 - 2) - pATI->XCLKPostDivider;
            if (pATI->NewHW.crtc == ATI_CRTC_VGA)
               i--;

            i = (ATIDivide(Numerator, Denominator, i, -1) / 1000) * 1000;
            if (i < ATIClockRange.maxClock)
                ATIClockRange.maxClock = i;
        }
    }

    /*
     * Assume an internal DAC can handle whatever frequency the internal PLL
     * can produce (with the reference divider set by BIOS initialisation), but
     * default maxClock to a lower chip-specific default.
     */
    if ((pATI->DAC & ~0x0FU) == ATI_DAC_INTERNAL)
    {
        int DacSpeed;
        switch (pATI->bitsPerPixel)
        {
            case 15:
            case 16:
                DacSpeed = pGDev->dacSpeeds[DAC_BPP16];
                break;

            case 24:
                DacSpeed = pGDev->dacSpeeds[DAC_BPP24];
                break;

            case 32:
                DacSpeed = pGDev->dacSpeeds[DAC_BPP32];
                break;

            default:
                DacSpeed = 0;
                break;
        }
        if (!DacSpeed)
            DacSpeed = pGDev->dacSpeeds[DAC_BPP8];
        if (DacSpeed < ATIClockRange.maxClock)
        {
            DefaultmaxClock = 135000;

            if (pATI->depth > 8)
                DefaultmaxClock = 80000;

            if ((pATI->Chip >= ATI_CHIP_264VTB) &&
                (pATI->Chip != ATI_CHIP_Mach64))
            {
                if ((pATI->Chip >= ATI_CHIP_264VT4) &&
                    (pATI->Chip != ATI_CHIP_264LTPRO))
                    DefaultmaxClock = 230000;
                else if (pATI->Chip >= ATI_CHIP_264VT3)
                    DefaultmaxClock = 200000;
                else
                    DefaultmaxClock = 170000;
            }
            if (DacSpeed > DefaultmaxClock)
                ATIClockRange.maxClock = DacSpeed;
            else if (DefaultmaxClock < ATIClockRange.maxClock)
                ATIClockRange.maxClock = DefaultmaxClock;
        }
    }
    else
    {
        switch(pATI->DAC)
        {
            case ATI_DAC_STG1700:
            case ATI_DAC_STG1702:
            case ATI_DAC_STG1703:
                DefaultmaxClock = 110000;
                break;

            case ATI_DAC_IBMRGB514:
                pATI->maxClock = 220000;
                if (pATI->NewHW.crtc == ATI_CRTC_VGA)
                    DefaultmaxClock = 100000;
                else
                    DefaultmaxClock = 220000;
                break;

            default:
                /*
                 * 80 MHz is too high in some cases.  Limit 18800-x's to 40
                 * MHz.  Don't exceed the memory clock on VGA Wonder capables
                 * with less than 1 MB, if using a packed mode.
                 */
                if ((pATI->Chip == ATI_CHIP_18800) ||
                    (pATI->Chip == ATI_CHIP_18800_1))
                    DefaultmaxClock = 40000;
                else if (pATI->CPIO_VGAWonder &&
                        (pATI->VideoRAM < 1024) &&
                        (pATI->depth >= 8))
                    DefaultmaxClock =
                        (GetBits(BIOSByte(0x44U), 0x04U) * 5000) + 40000;
                else
                    DefaultmaxClock = 80000;

                break;
        }

        if (DefaultmaxClock < ATIClockRange.maxClock)
            ATIClockRange.maxClock = DefaultmaxClock;
    }

    if (pATI->ClockDescriptor.MaxN <= 0)
    {
        ATIClockRange.maxClock = DefaultmaxClock;
        xf86DrvMsg(pScreenInfo->scrnIndex, X_INFO,
            "Maximum pixel clock:  %.3f MHz.\n",
            (double)ATIClockRange.maxClock / 1000.0);
    }

    /*
     * Determine available pixel clock frequencies.
     */

    ATIClockPreInit(pScreenInfo, pATI, pGDev, &ATIClockRange);
    if (pATI->ProgrammableClock > ATI_CLOCK_FIXED)
        Strategy = LOOKUP_BEST_REFRESH;

    /*
     * Mode validation.
     */

    if ((pATI->depth >= 8) && (pATI->Chip >= ATI_CHIP_264CT))
    {
        minPitch = 8;
    }
    else if (pATI->CPIO_VGAWonder &&
             (pATI->Chip <= ATI_CHIP_18800_1) &&
             (pATI->VideoRAM == 256) &&
             (pATI->depth >= 8))
    {
        minPitch = 32;          /* Very strange, but true */
        maxPitch = 0x3FU;
    }
    else
    {
        minPitch = 16;
    }

    pATI->pitchInc = minPitch;
    if (pATI->depth >= 8)
        pATI->pitchInc *= pATI->bitsPerPixel;

    switch (pATI->NewHW.crtc)
    {
        case ATI_CRTC_VGA:
            /*
             * IBM's VGA doesn't allow for interlaced modes.
             */
            if (pATI->Adapter <= ATI_ADAPTER_VGA)
                ATIClockRange.interlaceAllowed = FALSE;

            pScreenInfo->maxHValue = (0xFFU + 1) << 3;  /* max HTotal */

            /*
             * The maximum VTotal value set here applies to all modes,
             * including interlaced, doublescanned or multiscanned modes.
             * Finer-grained checks are done in ATIValidateMode().
             */
            pScreenInfo->maxVValue = 0x03FFU + 1;
            if (pATI->Adapter > ATI_ADAPTER_VGA)
            {
                pScreenInfo->maxVValue <<= 1;
                if (ATIClockRange.interlaceAllowed &&
                    (pATI->Chip < ATI_CHIP_264CT))
                    pScreenInfo->maxVValue <<= 1;
            }

            /*
             * 18800-x and 28800-x do not support interlaced modes when the
             * scanline pitch is 2048 pixels or more.  For 18800-x's with 256
             * kB of video memory, the limit for 8bpp is 1024.
             */
            if (ATIClockRange.interlaceAllowed &&
                (pATI->Chip <= ATI_CHIP_28800_6))
            {
                if (minPitch == 32)
                    pATI->MaximumInterlacedPitch = 0x1FU * 32;
                else
                    pATI->MaximumInterlacedPitch = 0x7FU * minPitch;
            }

            Strategy |= LOOKUP_CLKDIV2;

            break;

        case ATI_CRTC_MACH64:
            pScreenInfo->maxHValue = (MaxBits(CRTC_H_TOTAL) + 1) << 3;

            if (pATI->Chip < ATI_CHIP_264VT)
            {
                /*
                 * ATI finally fixed accelerated doublescanning in the 264VT
                 * and later.  On 88800's, the bit is documented to exist, but
                 * only doubles the vertical timings.  On the 264CT and 264ET,
                 * the bit is ignored.
                 */
                ATIClockRange.doubleScanAllowed = FALSE;

                /* CRTC_H_TOTAL is one bit narrower */
                pScreenInfo->maxHValue >>= 1;
            }

            pScreenInfo->maxVValue = MaxBits(CRTC_V_TOTAL) + 1;

            maxPitch = MaxBits(CRTC_PITCH);

            break;

        default:
            break;
    }

    maxPitch *= minPitch;

    if (pATI->OptionAccel)
    {
        /*
         * Set engine restrictions on coordinate space.  Use maxPitch for the
         * horizontal and maxHeight for the vertical.
         */
        if (maxPitch > (ATIMach64MaxX / pATI->XModifier))
            maxPitch = ATIMach64MaxX / pATI->XModifier;
        maxHeight = ATIMach64MaxY;

        /*
         * For SGRAM & WRAM adapters, the display engine limits the pitch to
         * multiples of 64 bytes.
         */
        if ((pATI->Chip >= ATI_CHIP_264CT) &&
            ((pATI->Chip >= ATI_CHIP_264VTB) ||
             (pATI->MemoryType >= MEM_264_SGRAM)))
            pATI->pitchInc = pATI->XModifier * (64 * 8);
    }

    if (pATI->OptionPanelDisplay && (pATI->LCDPanelID >= 0))
    {
        /*
         * Given LCD modes are more tightly controlled than CRT modes, allow
         * the user the option of not specifying a panel's horizontal sync
         * and/or vertical refresh tolerances.
         */
        Strategy |= LOOKUP_OPTIONAL_TOLERANCES;

        if (ModeType == M_T_BUILTIN)
        {
            /*
             * Add a mode to the end of the monitor's list for the panel's
             * native resolution.
             */
            pMode = (DisplayModePtr)xnfcalloc(1, SizeOf(DisplayModeRec));
            pMode->name = "Native panel mode";
            pMode->type = M_T_BUILTIN;
            pMode->Clock = pATI->LCDClock;
            pMode->HDisplay = pATI->LCDHorizontal;
            pMode->VDisplay = pATI->LCDVertical;

            /*
             * These timings are bogus, but enough to survive sync tolerance
             * checks.
             */
            pMode->HSyncStart = pMode->HDisplay;
            pMode->HSyncEnd = pMode->HSyncStart + minPitch;
            pMode->HTotal = pMode->HSyncEnd + minPitch;
            pMode->VSyncStart = pMode->VDisplay;
            pMode->VSyncEnd = pMode->VSyncStart + 1;
            pMode->VTotal = pMode->VSyncEnd + 1;

            pMode->CrtcHDisplay = pMode->HDisplay;
            pMode->CrtcHBlankStart = pMode->HDisplay;
            pMode->CrtcHSyncStart = pMode->HSyncStart;
            pMode->CrtcHSyncEnd = pMode->HSyncEnd;
            pMode->CrtcHBlankEnd = pMode->HTotal;
            pMode->CrtcHTotal = pMode->HTotal;

            pMode->CrtcVDisplay = pMode->VDisplay;
            pMode->CrtcVBlankStart = pMode->VDisplay;
            pMode->CrtcVSyncStart = pMode->VSyncStart;
            pMode->CrtcVSyncEnd = pMode->VSyncEnd;
            pMode->CrtcVBlankEnd = pMode->VTotal;
            pMode->CrtcVTotal = pMode->VTotal;

            if (!pScreenInfo->monitor->Modes)
            {
                pScreenInfo->monitor->Modes = pMode;
            }
            else
            {
                pScreenInfo->monitor->Last->next = pMode;
                pMode->prev = pScreenInfo->monitor->Last;
            }

            pScreenInfo->monitor->Last = pMode;
        }

        /*
         * Defeat Xconfigurator brain damage.  Ignore all HorizSync and
         * VertRefresh specifications.  For now, this does not take
         * SYNC_TOLERANCE into account.
         */
        if (pScreenInfo->monitor->nHsync > 0)
        {
            double hsync = (double)pATI->LCDClock /
                           (pATI->LCDHorizontal + pATI->LCDHBlankWidth);

            for (i = 0;  ;  i++)
            {
                if (i >= pScreenInfo->monitor->nHsync)
                {
                    xf86DrvMsg(pScreenInfo->scrnIndex, X_NOTICE,
                        "Conflicting XF86Config HorizSync specification(s)"
                        " ignored.\n");
                    break;
                }

                if ((hsync >= pScreenInfo->monitor->hsync[i].lo) &&
                    (hsync <= pScreenInfo->monitor->hsync[i].hi))
                {
                    xf86DrvMsg(pScreenInfo->scrnIndex, X_WARNING,
                        "Extraneous XF86Config HorizSync specification(s)"
                        " ignored.\n");
                    break;
                }
            }

            pScreenInfo->monitor->nHsync = 0;
        }

        if (pScreenInfo->monitor->nVrefresh > 0)
        {
            double vrefresh = ((double)pATI->LCDClock * 1000.0) /
                              ((pATI->LCDHorizontal + pATI->LCDHBlankWidth) *
                               (pATI->LCDVertical + pATI->LCDVBlankWidth));

            for (i = 0;  ;  i++)
            {
                if (i >= pScreenInfo->monitor->nVrefresh)
                {
                    xf86DrvMsg(pScreenInfo->scrnIndex, X_NOTICE,
                        "Conflicting XF86Config VertRefresh specification(s)"
                        " ignored.\n");
                    break;
                }

                if ((vrefresh >= pScreenInfo->monitor->vrefresh[i].lo) &&
                    (vrefresh <= pScreenInfo->monitor->vrefresh[i].hi))
                {
                    xf86DrvMsg(pScreenInfo->scrnIndex, X_WARNING,
                        "Extraneous XF86Config VertRefresh specification(s)"
                        " ignored.\n");
                    break;
                }
            }

            pScreenInfo->monitor->nVrefresh = 0;
        }
    }

    i = xf86ValidateModes(pScreenInfo,
            pScreenInfo->monitor->Modes, pScreenInfo->display->modes,
            &ATIClockRange, NULL, minPitch, maxPitch,
            pATI->pitchInc, 0, maxHeight,
            pScreenInfo->display->virtualX, pScreenInfo->display->virtualY,
            ApertureSize, Strategy);
    if (i <= 0)
    {
        ATILock(pATI);
        ATIPrintNoiseIfRequested(pATI, BIOS, BIOSSize);
        ATIUnmapApertures(pScreenInfo->scrnIndex, pATI);
        return FALSE;
    }

    /* Remove invalid modes */
    xf86PruneDriverModes(pScreenInfo);

    /* Set current mode to the first in the list */
    pScreenInfo->currentMode = pScreenInfo->modes;

    /* Print mode list */
    xf86PrintModes(pScreenInfo);

    /* Set display resolution */
    xf86SetDpi(pScreenInfo, 0, 0);

    /* Load required modules */
    if (!ATILoadSubModules(pScreenInfo, pATI))
    {
        ATILock(pATI);
        ATIPrintNoiseIfRequested(pATI, BIOS, BIOSSize);
        ATIUnmapApertures(pScreenInfo->scrnIndex, pATI);
        return FALSE;
    }

    pATI->displayWidth = pScreenInfo->displayWidth;

    /* Initialise for panning */
    ATIAdjustPreInit(pATI);

    /*
     * Warn about modes that are too small, or not aligned, to scroll to the
     * bottom right corner of the virtual screen.
     */
    MinX = pScreenInfo->virtualX - pATI->AdjustMaxX;
    MinY = pScreenInfo->virtualY - pATI->AdjustMaxY;

    pMode = pScreenInfo->modes;
    do
    {
        if ((pMode->VDisplay <= MinY) &&
            ((pMode->VDisplay < MinY) || (pMode->HDisplay < MinX)))
            xf86DrvMsg(pScreenInfo->scrnIndex, X_WARNING,
                "Mode \"%s\" too small to scroll to bottom right corner of"
                " virtual resolution.\n", pMode->name);
        else if ((pMode->HDisplay & ~pATI->AdjustMask) / pScreenInfo->xInc)
            xf86DrvMsg(pScreenInfo->scrnIndex, X_WARNING,
                "Mode \"%s\" cannot scroll to bottom right corner of virtual"
                " resolution.\n Horizontal dimension not a multiple of %ld.\n",
                pMode->name, ~pATI->AdjustMask + 1);
    } while ((pMode = pMode->next) != pScreenInfo->modes);

    /* Initialise XVideo extension support */
    ATIXVPreInit(pATI);

    /* Initialise CRTC code */
    ATIModePreInit(pScreenInfo, pATI, &pATI->NewHW);

    /* Set up for I2C */
    ATII2CPreInit(pScreenInfo, pATI);

    if (!pScreenInfo->chipset || !*pScreenInfo->chipset)
        pScreenInfo->chipset = (char *)ATIChipsetNames[0];

    ATILock(pATI);
    ATIPrintNoiseIfRequested(pATI, BIOS, BIOSSize);
    ATIUnmapApertures(pScreenInfo->scrnIndex, pATI);

    return TRUE;
}
