/* **********************************************************
 * Copyright (C) 1998-2001 VMware, Inc.
 * All Rights Reserved
 * **********************************************************/
#ifdef VMX86_DEVEL
char rcsId_vmware[] =
    "Id: vmware.c,v 1.11 2001/02/23 02:10:39 yoel Exp $";
#endif
/* $XFree86: xc/programs/Xserver/hw/xfree86/drivers/vmware/vmware.c,v 1.18 2003/09/24 02:43:31 dawes Exp $ */

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

/*
 * TODO: support the vmware linux kernel fb driver (Option "UseFBDev").
 */

#include "xf86.h"
#include "xf86_OSproc.h"
#if GET_ABI_MAJOR(ABI_VIDEODRV_VERSION) < 6
#include "xf86Resources.h"
#endif

#include "compiler.h"	/* inb/outb */

#include "xf86PciInfo.h"	/* pci vendor id */
#include "xf86Pci.h"		/* pci */

#include "mipointer.h"		/* sw cursor */
#include "mibstore.h"		/* backing store */
#include "micmap.h"		/* mi color map */
#include "vgaHW.h"		/* VGA hardware */
#include "fb.h"
#include "shadowfb.h"           /* ShadowFB wrappers */

#include "xf86cmap.h"		/* xf86HandleColormaps */

#include "vmware.h"
#include "guest_os.h"
#include "vm_device_version.h"
#include "svga_modes.h"

#ifdef HaveDriverFuncs
#define VMWARE_DRIVER_FUNC HaveDriverFuncs
#else
#define VMWARE_DRIVER_FUNC 0
#endif

/*
 * So that the file compiles unmodified when dropped in to a < 6.9 source tree.
 */
#ifndef _X_EXPORT
#define _X_EXPORT
#endif
/*
 * So that the file compiles unmodified when dropped into an xfree source tree.
 */
#ifndef XORG_VERSION_CURRENT
#define XORG_VERSION_CURRENT XF86_VERSION_CURRENT
#endif

/*
 * Sanity check that xf86PciInfo.h has the correct values (which come from
 * the VMware source tree in vm_device_version.h.
 */
#if PCI_CHIP_VMWARE0405 != PCI_DEVICE_ID_VMWARE_SVGA2
#error "PCI_CHIP_VMWARE0405 is wrong, update it from vm_device_version.h"
#endif
#if PCI_CHIP_VMWARE0710 != PCI_DEVICE_ID_VMWARE_SVGA
#error "PCI_CHIP_VMWARE0710 is wrong, update it from vm_device_version.h"
#endif
#if PCI_VENDOR_VMWARE != PCI_VENDOR_ID_VMWARE
#error "PCI_VENDOR_VMWARE is wrong, update it from vm_device_version.h"
#endif

/*
 * This is the only way I know to turn a #define of an integer constant into
 * a constant string.
 */
#define VMW_INNERSTRINGIFY(s) #s
#define VMW_STRING(str) VMW_INNERSTRINGIFY(str)

#define VMWARE_NAME "VMWARE"
#define VMWARE_DRIVER_NAME "vmware"
#define VMWARE_MAJOR_VERSION	10
#define VMWARE_MINOR_VERSION	16
#define VMWARE_PATCHLEVEL	8
#define VMWARE_DRIVER_VERSION \
   (VMWARE_MAJOR_VERSION * 65536 + VMWARE_MINOR_VERSION * 256 + VMWARE_PATCHLEVEL)
#define VMWARE_DRIVER_VERSION_STRING \
    VMW_STRING(VMWARE_MAJOR_VERSION) "." VMW_STRING(VMWARE_MINOR_VERSION) \
    "." VMW_STRING(VMWARE_PATCHLEVEL)

static const char VMWAREBuildStr[] = "VMware Guest X Server "
    VMWARE_DRIVER_VERSION_STRING " - build=$Name: netbsd-5 $\n";

/*
 * Standard four digit version string expected by VMware Tools installer.
 * As the driver's version is only  {major, minor, patchlevel}, simply append an
 * extra zero for the fourth digit.
 */
#ifdef __GNUC__
const char vm_svga_version[] __attribute__((section(".modinfo"),unused)) =
    "version=" VMWARE_DRIVER_VERSION_STRING ".0";
#endif

static SymTabRec VMWAREChipsets[] = {
    { PCI_CHIP_VMWARE0405, "vmware0405" },
    { PCI_CHIP_VMWARE0710, "vmware0710" },
    { -1,                  NULL }
};

#ifndef XSERVER_LIBPCIACCESS
static resRange vmwareLegacyRes[] = {
    { ResExcIoBlock, SVGA_LEGACY_BASE_PORT,
      SVGA_LEGACY_BASE_PORT + SVGA_NUM_PORTS*sizeof(uint32)},
    _VGA_EXCLUSIVE, _END
};
#else
#define vmwareLegacyRes NULL
#endif

#if XSERVER_LIBPCIACCESS

#define VMWARE_DEVICE_MATCH(d, i) \
    {PCI_VENDOR_VMWARE, (d), PCI_MATCH_ANY, PCI_MATCH_ANY, 0, 0, (i) }

static const struct pci_id_match VMwareDeviceMatch[] = {
    VMWARE_DEVICE_MATCH (PCI_CHIP_VMWARE0405, 0 ),
    VMWARE_DEVICE_MATCH (PCI_CHIP_VMWARE0710, 0 ),
    { 0, 0, 0 },
};
#endif

/*
 * Currently, even the PCI obedient 0405 chip still only obeys IOSE and
 * MEMSE for the SVGA resources.  Thus, RES_EXCLUSIVE_VGA is required.
 *
 * The 0710 chip also uses hardcoded IO ports that aren't disablable.
 */

static PciChipsets VMWAREPciChipsets[] = {
    { PCI_CHIP_VMWARE0405, PCI_CHIP_VMWARE0405, RES_EXCLUSIVE_VGA },
    { PCI_CHIP_VMWARE0710, PCI_CHIP_VMWARE0710, vmwareLegacyRes },
    { -1,		       -1,		    RES_UNDEFINED }
};

static const char *vgahwSymbols[] = {
    "vgaHWGetHWRec",
    "vgaHWGetIOBase",
    "vgaHWGetIndex",
    "vgaHWInit",
    "vgaHWProtect",
    "vgaHWRestore",
    "vgaHWSave",
    "vgaHWSaveScreen",
    "vgaHWUnlock",
    NULL
};

static const char *fbSymbols[] = {
    "fbCreateDefColormap",
    "fbPictureInit",
    "fbScreenInit",
    NULL
};

static const char *ramdacSymbols[] = {
    "xf86CreateCursorInfoRec",
    "xf86DestroyCursorInfoRec",
    "xf86InitCursor",
    NULL
};

static const char *shadowfbSymbols[] = {
    "ShadowFBInit2",
    NULL
};

#ifdef XFree86LOADER
static XF86ModuleVersionInfo vmwareVersRec = {
    "vmware",
    MODULEVENDORSTRING,
    MODINFOSTRING1,
    MODINFOSTRING2,
    XORG_VERSION_CURRENT,
    VMWARE_MAJOR_VERSION, VMWARE_MINOR_VERSION, VMWARE_PATCHLEVEL,
    ABI_CLASS_VIDEODRV,
    ABI_VIDEODRV_VERSION,
    MOD_CLASS_VIDEODRV,
    { 0, 0, 0, 0}
};
#endif	/* XFree86LOADER */

typedef enum {
    OPTION_HW_CURSOR,
    OPTION_XINERAMA,
    OPTION_STATIC_XINERAMA
} VMWAREOpts;

static const OptionInfoRec VMWAREOptions[] = {
    { OPTION_HW_CURSOR, "HWcursor",     OPTV_BOOLEAN,   {0},    FALSE },
    { OPTION_XINERAMA,  "Xinerama",     OPTV_BOOLEAN,   {0},    FALSE },
    { OPTION_STATIC_XINERAMA, "StaticXinerama", OPTV_STRING, {0}, FALSE },
    { -1,               NULL,           OPTV_NONE,      {0},    FALSE }
};

/* Table of default modes to always add to the mode list. */

typedef struct {
   int width;
   int height;
} VMWAREDefaultMode;

#define SVGA_DEFAULT_MODE(width, height) { width, height, },

static const VMWAREDefaultMode VMWAREDefaultModes[] = {
   SVGA_DEFAULT_MODES
};

#undef SVGA_DEFAULT_MODE

static void VMWAREStopFIFO(ScrnInfoPtr pScrn);
static void VMWARESave(ScrnInfoPtr pScrn);

static Bool
VMWAREGetRec(ScrnInfoPtr pScrn)
{
    if (pScrn->driverPrivate != NULL) {
        return TRUE;
    }
    pScrn->driverPrivate = xnfcalloc(sizeof(VMWARERec), 1);
    /* FIXME: Initialize driverPrivate... */
    return TRUE;
}

static void
VMWAREFreeRec(ScrnInfoPtr pScrn)
{
    if (pScrn->driverPrivate) {
        xfree(pScrn->driverPrivate);
        pScrn->driverPrivate = NULL;
    }
}

CARD32
vmwareReadReg(VMWAREPtr pVMWARE, int index)
{
    /*
     * Block SIGIO for the duration, so we don't get interrupted after the
     * outl but before the inl by a mouse move (which write to our registers).
     */
    int oldsigio, ret;
    oldsigio = xf86BlockSIGIO();
    outl(pVMWARE->indexReg, index);
    ret = inl(pVMWARE->valueReg);
    xf86UnblockSIGIO(oldsigio);
    return ret;
}

void
vmwareWriteReg(VMWAREPtr pVMWARE, int index, CARD32 value)
{
    /*
     * Block SIGIO for the duration, so we don't get interrupted in between
     * the outls by a mouse move (which write to our registers).
     */
    int oldsigio;
    oldsigio = xf86BlockSIGIO();
    outl(pVMWARE->indexReg, index);
    outl(pVMWARE->valueReg, value);
    xf86UnblockSIGIO(oldsigio);
}

void
vmwareWriteWordToFIFO(VMWAREPtr pVMWARE, CARD32 value)
{
    CARD32* vmwareFIFO = pVMWARE->vmwareFIFO;

    /* Need to sync? */
    if ((vmwareFIFO[SVGA_FIFO_NEXT_CMD] + sizeof(CARD32) == vmwareFIFO[SVGA_FIFO_STOP])
     || (vmwareFIFO[SVGA_FIFO_NEXT_CMD] == vmwareFIFO[SVGA_FIFO_MAX] - sizeof(CARD32) &&
	 vmwareFIFO[SVGA_FIFO_STOP] == vmwareFIFO[SVGA_FIFO_MIN])) {
        VmwareLog(("Syncing because of full fifo\n"));
        vmwareWaitForFB(pVMWARE);
    }

    vmwareFIFO[vmwareFIFO[SVGA_FIFO_NEXT_CMD] / sizeof(CARD32)] = value;
    if(vmwareFIFO[SVGA_FIFO_NEXT_CMD] == vmwareFIFO[SVGA_FIFO_MAX] -
       sizeof(CARD32)) {
        vmwareFIFO[SVGA_FIFO_NEXT_CMD] = vmwareFIFO[SVGA_FIFO_MIN];
    } else {
        vmwareFIFO[SVGA_FIFO_NEXT_CMD] += sizeof(CARD32);
    }
}

void
vmwareWaitForFB(VMWAREPtr pVMWARE)
{
    vmwareWriteReg(pVMWARE, SVGA_REG_SYNC, 1);
    while (vmwareReadReg(pVMWARE, SVGA_REG_BUSY));
}

void
vmwareSendSVGACmdUpdate(VMWAREPtr pVMWARE, BoxPtr pBB)
{
    vmwareWriteWordToFIFO(pVMWARE, SVGA_CMD_UPDATE);
    vmwareWriteWordToFIFO(pVMWARE, pBB->x1);
    vmwareWriteWordToFIFO(pVMWARE, pBB->y1);
    vmwareWriteWordToFIFO(pVMWARE, pBB->x2 - pBB->x1);
    vmwareWriteWordToFIFO(pVMWARE, pBB->y2 - pBB->y1);
}

void
vmwareSendSVGACmdUpdateFullScreen(VMWAREPtr pVMWARE)
{
    BoxRec BB;

    BB.x1 = 0;
    BB.y1 = 0;
    BB.x2 = pVMWARE->ModeReg.svga_reg_width;
    BB.y2 = pVMWARE->ModeReg.svga_reg_height;
    vmwareSendSVGACmdUpdate(pVMWARE, &BB);
}

static CARD32
vmwareCalculateWeight(CARD32 mask)
{
    CARD32 weight;

    for (weight = 0; mask; mask >>= 1) {
        if (mask & 1) {
            weight++;
        }
    }
    return weight;
}

/*
 *-----------------------------------------------------------------------------
 *
 * VMXGetVMwareSvgaId --
 *
 *    Retrieve the SVGA_ID of the VMware SVGA adapter.
 *    This function should hide any backward compatibility mess.
 *
 * Results:
 *    The SVGA_ID_* of the present VMware adapter.
 *
 * Side effects:
 *    ins/outs
 *
 *-----------------------------------------------------------------------------
 */

static uint32
VMXGetVMwareSvgaId(VMWAREPtr pVMWARE)
{
    uint32 vmware_svga_id;

    /* Any version with any SVGA_ID_* support will initialize SVGA_REG_ID
     * to SVGA_ID_0 to support versions of this driver with SVGA_ID_0.
     *
     * Versions of SVGA_ID_0 ignore writes to the SVGA_REG_ID register.
     *
     * Versions of SVGA_ID_1 will allow us to overwrite the content
     * of the SVGA_REG_ID register only with the values SVGA_ID_0 or SVGA_ID_1.
     *
     * Versions of SVGA_ID_2 will allow us to overwrite the content
     * of the SVGA_REG_ID register only with the values SVGA_ID_0 or SVGA_ID_1
     * or SVGA_ID_2.
     */

    vmwareWriteReg(pVMWARE, SVGA_REG_ID, SVGA_ID_2);
    vmware_svga_id = vmwareReadReg(pVMWARE, SVGA_REG_ID);
    if (vmware_svga_id == SVGA_ID_2) {
        return SVGA_ID_2;
    }

    vmwareWriteReg(pVMWARE, SVGA_REG_ID, SVGA_ID_1);
    vmware_svga_id = vmwareReadReg(pVMWARE, SVGA_REG_ID);
    if (vmware_svga_id == SVGA_ID_1) {
        return SVGA_ID_1;
    }

    if (vmware_svga_id == SVGA_ID_0) {
        return SVGA_ID_0;
    }

    /* No supported VMware SVGA devices found */
    return SVGA_ID_INVALID;
}

#ifndef XSERVER_LIBPCIACCESS
/*
 *----------------------------------------------------------------------
 *
 *  RewriteTagString --
 *
 *      Rewrites the given string, removing the $Name: netbsd-5 $, and
 *      replacing it with the contents.  The output string must
 *      have enough room, or else.
 *
 * Results:
 *
 *      Output string updated.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------
 */

static void
RewriteTagString(const char *istr, char *ostr, int osize)
{
    int chr;
    Bool inTag = FALSE;
    char *op = ostr;

    do {
	chr = *istr++;
	if (chr == '$') {
	    if (inTag) {
		inTag = FALSE;
		for (; op > ostr && op[-1] == ' '; op--) {
		}
		continue;
	    }
	    if (strncmp(istr, "Name:", 5) == 0) {
		istr += 5;
		istr += strspn(istr, " ");
		inTag = TRUE;
		continue;
	    }
	}
	*op++ = chr;
    } while (chr);
}
#endif

static void
VMWAREIdentify(int flags)
{
    xf86PrintChipsets(VMWARE_NAME, "driver for VMware SVGA", VMWAREChipsets);
}

static const OptionInfoRec *
VMWAREAvailableOptions(int chipid, int busid)
{
    return VMWAREOptions;
}

static int
VMWAREParseTopologyElement(ScrnInfoPtr pScrn,
                           unsigned int output,
                           const char *elementName,
                           const char *element,
                           const char *expectedTerminators,
                           Bool needTerminator,
                           unsigned int *outValue)
{
   char buf[10] = {0, };
   size_t i = 0;
   int retVal = -1;
   const char *str = element;

   for (i = 0; str[i] >= '0' && str[i] <= '9'; i++);
   if (i == 0) {
      xf86DrvMsg(pScrn->scrnIndex, X_INFO, "Output %u: unable to parse %s.\n",
                 output, elementName);
      goto exit;
   }

   strncpy(buf, str, i);
   *outValue = atoi(buf);

   if (*outValue > (unsigned short)-1) {
      xf86DrvMsg(pScrn->scrnIndex, X_INFO, "Output %u: %s must be less than %hu.\n",
                 output, elementName, (unsigned short)-1);
      goto exit;
   }

   str += i;

   if (needTerminator || str[0] != '\0') {
      Bool unexpected = TRUE;

      for (i = 0; i < strlen(expectedTerminators); i++) {
         if (str[0] == expectedTerminators[i]) {
            unexpected = FALSE;
         }
      }

      if (unexpected) {
         xf86DrvMsg(pScrn->scrnIndex, X_INFO,
                    "Output %u: unexpected character '%c' after %s.\n",
                    output, str[0], elementName);
         goto exit;
      } else {
         str++;
      }
   }

   retVal = str - element;

 exit:
   return retVal;
}

static xXineramaScreenInfo *
VMWAREParseTopologyString(ScrnInfoPtr pScrn,
                          const char *topology,
                          unsigned int *retNumOutputs)
{
   xXineramaScreenInfo *extents = NULL;
   unsigned int numOutputs = 0;
   const char *str = topology;

   xf86DrvMsg(pScrn->scrnIndex, X_INFO, "Parsing static Xinerama topology: Starting...\n");

   do {
      unsigned int x, y, width, height;
      int i;

      i = VMWAREParseTopologyElement(pScrn, numOutputs, "width", str, "xX", TRUE, &width);
      if (i == -1) {
         goto error;
      }
      str += i;

      i = VMWAREParseTopologyElement(pScrn, numOutputs, "height", str, "+", TRUE, &height);
      if (i == -1) {
         goto error;
      }
      str += i;

      i= VMWAREParseTopologyElement(pScrn, numOutputs, "X offset", str, "+", TRUE, &x);
      if (i == -1) {
         goto error;
      }
      str += i;

      i = VMWAREParseTopologyElement(pScrn, numOutputs, "Y offset", str, ";", FALSE, &y);
      if (i == -1) {
         goto error;
      }
      str += i;

      xf86DrvMsg(pScrn->scrnIndex, X_INFO, "Output %u: %ux%u+%u+%u\n",
                 numOutputs, width, height, x, y);

      numOutputs++;
      extents = xrealloc(extents, numOutputs * sizeof (xXineramaScreenInfo));
      extents[numOutputs - 1].x_org = x;
      extents[numOutputs - 1].y_org = y;
      extents[numOutputs - 1].width = width;
      extents[numOutputs - 1].height = height;
   } while (*str != 0);

   xf86DrvMsg(pScrn->scrnIndex, X_INFO, "Parsing static Xinerama topology: Succeeded.\n");
   goto exit;

 error:
   xf86DrvMsg(pScrn->scrnIndex, X_INFO, "Parsing static Xinerama topology: Failed.\n");

   xfree(extents);
   extents = NULL;
   numOutputs = 0;

 exit:
   *retNumOutputs = numOutputs;
   return extents;
}


static Bool
VMWAREPreInit(ScrnInfoPtr pScrn, int flags)
{
    MessageType from;
    VMWAREPtr pVMWARE;
    OptionInfoPtr options;
    int bpp24flags;
    uint32 id;
    int i;
    ClockRange* clockRanges;
    IOADDRESS domainIOBase = 0;

#ifndef BUILD_FOR_420
    domainIOBase = pScrn->domainIOBase;
#endif

    if (flags & PROBE_DETECT) {
        return FALSE;
    }

    if (pScrn->numEntities != 1) {
        return FALSE;
    }

    if (!VMWAREGetRec(pScrn)) {
        return FALSE;
    }
    pVMWARE = VMWAREPTR(pScrn);

    pVMWARE->pvtSema = &pScrn->vtSema;

    pVMWARE->pEnt = xf86GetEntityInfo(pScrn->entityList[0]);
    if (pVMWARE->pEnt->location.type != BUS_PCI) {
        return FALSE;
    }
    pVMWARE->PciInfo = xf86GetPciInfoForEntity(pVMWARE->pEnt->index);
    if (pVMWARE->PciInfo == NULL) {
        return FALSE;
    }

    if (DEVICE_ID(pVMWARE->PciInfo) == PCI_CHIP_VMWARE0710) {
        pVMWARE->indexReg = domainIOBase +
           SVGA_LEGACY_BASE_PORT + SVGA_INDEX_PORT*sizeof(uint32);
        pVMWARE->valueReg = domainIOBase +
           SVGA_LEGACY_BASE_PORT + SVGA_VALUE_PORT*sizeof(uint32);
    } else {
        /* Note:  This setting of valueReg causes unaligned I/O */
#if XSERVER_LIBPCIACCESS
        pVMWARE->portIOBase = pVMWARE->PciInfo->regions[0].base_addr;
#else
        pVMWARE->portIOBase = pVMWARE->PciInfo->ioBase[0];
#endif
        pVMWARE->indexReg = domainIOBase +
           pVMWARE->portIOBase + SVGA_INDEX_PORT;
        pVMWARE->valueReg = domainIOBase +
           pVMWARE->portIOBase + SVGA_VALUE_PORT;
    }
    xf86DrvMsg(pScrn->scrnIndex, X_PROBED,
               "VMware SVGA regs at (0x%04lx, 0x%04lx)\n",
               pVMWARE->indexReg, pVMWARE->valueReg);

    if (!xf86LoadSubModule(pScrn, "vgahw")) {
        return FALSE;
    }

    xf86LoaderReqSymLists(vgahwSymbols, NULL);

    if (!vgaHWGetHWRec(pScrn)) {
        return FALSE;
    }

    /*
     * Save the current video state.  Do it here before VMXGetVMwareSvgaId
     * writes to any registers.
     */
    VMWARESave(pScrn);

    id = VMXGetVMwareSvgaId(pVMWARE);
    if (id == SVGA_ID_0 || id == SVGA_ID_INVALID) {
        xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
                   "No supported VMware SVGA found (read ID 0x%08x).\n", id);
        return FALSE;
    }
    pVMWARE->suspensionSavedRegId = id;

#if !XSERVER_LIBPCIACCESS
    pVMWARE->PciTag = pciTag(pVMWARE->PciInfo->bus, pVMWARE->PciInfo->device,
                             pVMWARE->PciInfo->func);
#endif
    pVMWARE->Primary = xf86IsPrimaryPci(pVMWARE->PciInfo);

    pScrn->monitor = pScrn->confScreen->monitor;

#ifdef ACCELERATE_OPS
    pVMWARE->vmwareCapability = vmwareReadReg(pVMWARE, SVGA_REG_CAPABILITIES);
#else
    pVMWARE->vmwareCapability = 0;
#endif

    pVMWARE->bitsPerPixel = vmwareReadReg(pVMWARE,
                                          SVGA_REG_HOST_BITS_PER_PIXEL);
    if (pVMWARE->vmwareCapability & SVGA_CAP_8BIT_EMULATION) {
       vmwareWriteReg(pVMWARE, SVGA_REG_BITS_PER_PIXEL, pVMWARE->bitsPerPixel);
    }

    pVMWARE->depth = vmwareReadReg(pVMWARE, SVGA_REG_DEPTH);
    pVMWARE->videoRam = vmwareReadReg(pVMWARE, SVGA_REG_VRAM_SIZE);
    pVMWARE->memPhysBase = vmwareReadReg(pVMWARE, SVGA_REG_FB_START);
    pVMWARE->maxWidth = vmwareReadReg(pVMWARE, SVGA_REG_MAX_WIDTH);
    pVMWARE->maxHeight = vmwareReadReg(pVMWARE, SVGA_REG_MAX_HEIGHT);
    pVMWARE->cursorDefined = FALSE;
    pVMWARE->cursorShouldBeHidden = FALSE;

    if (pVMWARE->vmwareCapability & SVGA_CAP_CURSOR_BYPASS_2) {
        pVMWARE->cursorRemoveFromFB = SVGA_CURSOR_ON_REMOVE_FROM_FB;
        pVMWARE->cursorRestoreToFB = SVGA_CURSOR_ON_RESTORE_TO_FB;
    } else {
        pVMWARE->cursorRemoveFromFB = SVGA_CURSOR_ON_HIDE;
        pVMWARE->cursorRestoreToFB = SVGA_CURSOR_ON_SHOW;
    }

    xf86DrvMsgVerb(pScrn->scrnIndex, X_PROBED, 2, "caps:  0x%08X\n", pVMWARE->vmwareCapability);
    xf86DrvMsgVerb(pScrn->scrnIndex, X_PROBED, 2, "depth: %d\n", pVMWARE->depth);
    xf86DrvMsgVerb(pScrn->scrnIndex, X_PROBED, 2, "bpp:   %d\n", pVMWARE->bitsPerPixel);

    xf86DrvMsgVerb(pScrn->scrnIndex, X_PROBED, 2, "vram:  %d\n", pVMWARE->videoRam);
    xf86DrvMsgVerb(pScrn->scrnIndex, X_PROBED, 2, "pbase: 0x%08lx\n", pVMWARE->memPhysBase);
    xf86DrvMsgVerb(pScrn->scrnIndex, X_PROBED, 2, "mwidt: %d\n", pVMWARE->maxWidth);
    xf86DrvMsgVerb(pScrn->scrnIndex, X_PROBED, 2, "mheig: %d\n", pVMWARE->maxHeight);

    if (pVMWARE->vmwareCapability & SVGA_CAP_8BIT_EMULATION) {
        bpp24flags = Support24bppFb | Support32bppFb;
    } else {
        switch (pVMWARE->depth) {
        case 16:
            /*
             * In certain cases, the Windows host appears to
             * report 16 bpp and 16 depth but 555 weight.  Just
             * silently convert it to depth of 15.
             */
            if (pVMWARE->bitsPerPixel == 16 &&
                pVMWARE->weight.green == 5)
                pVMWARE->depth = 15;
        case 8:
        case 15:
            bpp24flags = NoDepth24Support;
         break;
        case 32:
            /*
             * There is no 32 bit depth, apparently it can get
             * reported this way sometimes on the Windows host.
             */
            if (pVMWARE->bitsPerPixel == 32)
                pVMWARE->depth = 24;
        case 24:
            if (pVMWARE->bitsPerPixel == 24)
                bpp24flags = Support24bppFb;
            else
                bpp24flags = Support32bppFb;
            break;
       default:
            xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
                       "Adapter is using an unsupported depth (%d).\n",
                       pVMWARE->depth);
            return FALSE;
       }
    }

    if (!xf86SetDepthBpp(pScrn, pVMWARE->depth, pVMWARE->bitsPerPixel,
                         pVMWARE->bitsPerPixel, bpp24flags)) {
        return FALSE;
    }

    /* Check that the returned depth is one we support */
    switch (pScrn->depth) {
    case 8:
    case 15:
    case 16:
    case 24:
        /* OK */
        break;
    default:
        xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
                   "Given depth (%d) is not supported by this driver\n",
                   pScrn->depth);
        return FALSE;
    }

    if (pScrn->bitsPerPixel != pVMWARE->bitsPerPixel) {
        if (pVMWARE->vmwareCapability & SVGA_CAP_8BIT_EMULATION) {
            vmwareWriteReg(pVMWARE, SVGA_REG_BITS_PER_PIXEL,
                           pScrn->bitsPerPixel);
            pVMWARE->bitsPerPixel =
               vmwareReadReg(pVMWARE, SVGA_REG_BITS_PER_PIXEL);
            pVMWARE->depth = vmwareReadReg(pVMWARE, SVGA_REG_DEPTH);
        } else {
            xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
                       "Currently unavailable depth/bpp of %d/%d requested.\n"
                       "\tThe guest X server must run at the same depth and bpp as the host\n"
                       "\t(which are currently %d/%d).  This is automatically detected.  Please\n"
                       "\tdo not specify a depth on the command line or via the config file.\n",
                       pScrn->depth, pScrn->bitsPerPixel,
                       pVMWARE->depth, pVMWARE->bitsPerPixel);
            return FALSE;
        }
    }

    /*
     * Defer reading the colour registers until here in case we changed
     * bpp above.
     */

    pVMWARE->weight.red =
       vmwareCalculateWeight(vmwareReadReg(pVMWARE, SVGA_REG_RED_MASK));
    pVMWARE->weight.green =
       vmwareCalculateWeight(vmwareReadReg(pVMWARE, SVGA_REG_GREEN_MASK));
    pVMWARE->weight.blue =
       vmwareCalculateWeight(vmwareReadReg(pVMWARE, SVGA_REG_BLUE_MASK));
    pVMWARE->offset.blue = 0;
    pVMWARE->offset.green = pVMWARE->weight.blue;
    pVMWARE->offset.red = pVMWARE->weight.green + pVMWARE->offset.green;
    pVMWARE->defaultVisual = vmwareReadReg(pVMWARE, SVGA_REG_PSEUDOCOLOR) ?
       PseudoColor : TrueColor;

    xf86DrvMsgVerb(pScrn->scrnIndex, X_PROBED,
                   2, "depth: %d\n", pVMWARE->depth);
    xf86DrvMsgVerb(pScrn->scrnIndex, X_PROBED,
                   2, "bpp:   %d\n", pVMWARE->bitsPerPixel);
    xf86DrvMsgVerb(pScrn->scrnIndex, X_PROBED,
                   2, "w.red: %d\n", (int)pVMWARE->weight.red);
    xf86DrvMsgVerb(pScrn->scrnIndex, X_PROBED,
                   2, "w.grn: %d\n", (int)pVMWARE->weight.green);
    xf86DrvMsgVerb(pScrn->scrnIndex, X_PROBED,
                   2, "w.blu: %d\n", (int)pVMWARE->weight.blue);
    xf86DrvMsgVerb(pScrn->scrnIndex, X_PROBED,
                   2, "vis:   %d\n", pVMWARE->defaultVisual);

    if (pScrn->depth != pVMWARE->depth) {
        if (pVMWARE->vmwareCapability & SVGA_CAP_8BIT_EMULATION) {
            xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
                       "Currently unavailable depth of %d requested.\n"
                       "\tIf the guest X server's BPP matches the host's "
                       "BPP, then\n\tthe guest X server's depth must also "
                       "match the\n\thost's depth (currently %d).\n",
                       pScrn->depth, pVMWARE->depth);
        } else {
            xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
                       "Currently unavailable depth of %d requested.\n"
                       "\tThe guest X server must run at the same depth as "
                       "the host (which\n\tis currently %d).  This is "
                       "automatically detected.  Please do not\n\tspecify "
                       "a depth on the command line or via the config file.\n",
                       pScrn->depth, pVMWARE->depth);
        }
           return FALSE;
    }
    xf86PrintDepthBpp(pScrn);

#if 0
    if (pScrn->depth == 24 && pix24bpp == 0) {
        pix24bpp = xf86GetBppFromDepth(pScrn, 24);
    }
#endif

    if (pScrn->depth > 8) {
        rgb zeros = { 0, 0, 0 };

        if (!xf86SetWeight(pScrn, pVMWARE->weight, zeros)) {
            return FALSE;
        }
        /* FIXME check returned weight */
    }
    if (!xf86SetDefaultVisual(pScrn, pVMWARE->defaultVisual)) {
        return FALSE;
    }
    if (pScrn->defaultVisual != pVMWARE->defaultVisual) {
        xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
                   "Given visual (%d) is not supported by this driver (%d is required)\n",
                   pScrn->defaultVisual, pVMWARE->defaultVisual);
        return FALSE;
    }
#if 0
    bytesPerPixel = pScrn->bitsPerPixel / 8;
#endif
    pScrn->progClock = TRUE;

#if 0 /* MGA does not do this */
    if (pScrn->visual != 0) {	/* FIXME */
        /* print error message */
        return FALSE;
    }
#endif

    xf86CollectOptions(pScrn, NULL);
    if (!(options = xalloc(sizeof(VMWAREOptions))))
        return FALSE;
    memcpy(options, VMWAREOptions, sizeof(VMWAREOptions));
    xf86ProcessOptions(pScrn->scrnIndex, pScrn->options, options);

    if (pScrn->depth <= 8) {
        pScrn->rgbBits = 8;
    }

    from = X_PROBED;
    pScrn->chipset = (char*)xf86TokenToString(VMWAREChipsets, DEVICE_ID(pVMWARE->PciInfo));

    if (!pScrn->chipset) {
        xf86DrvMsg(pScrn->scrnIndex, X_ERROR, "ChipID 0x%04x is not recognised\n", DEVICE_ID(pVMWARE->PciInfo));
        return FALSE;
    }

    from = X_DEFAULT;
    pVMWARE->hwCursor = TRUE;
    if (xf86GetOptValBool(options, OPTION_HW_CURSOR, &pVMWARE->hwCursor)) {
        from = X_CONFIG;
    }
    if (pVMWARE->hwCursor && !(pVMWARE->vmwareCapability & SVGA_CAP_CURSOR)) {
        xf86DrvMsg(pScrn->scrnIndex, X_WARNING, "HW cursor is not supported in this configuration\n");
        from = X_PROBED;
        pVMWARE->hwCursor = FALSE;
    }
    xf86DrvMsg(pScrn->scrnIndex, from, "Using %s cursor\n",
               pVMWARE->hwCursor ? "HW" : "SW");
    pScrn->videoRam = pVMWARE->videoRam / 1024;
    pScrn->memPhysBase = pVMWARE->memPhysBase;

    xfree(options);

    {
        Gamma zeros = { 0.0, 0.0, 0.0 };
        if (!xf86SetGamma(pScrn, zeros)) {
            return FALSE;
        }
    }
#if 0
    if ((i = xf86GetPciInfoForScreen(pScrn->scrnIndex, &pciList, NULL)) != 1) {
        /* print error message */
        VMWAREFreeRec(pScrn);
        if (i > 0) {
            xfree(pciList);
        }
        return FALSE;
    }
#endif
    clockRanges = xnfcalloc(sizeof(ClockRange), 1);
    clockRanges->next = NULL;
    clockRanges->minClock = 1;
    clockRanges->maxClock = 400000000;
    clockRanges->clockIndex = -1;
    clockRanges->interlaceAllowed = FALSE;
    clockRanges->doubleScanAllowed = FALSE;
    clockRanges->ClockMulFactor = 1;
    clockRanges->ClockDivFactor = 1;
   
    /*
     * Get the default supported modelines
     */
    vmwareGetSupportedModelines(&pScrn->monitor->Modes);

    i = xf86ValidateModes(pScrn, pScrn->monitor->Modes, pScrn->display->modes,
                          clockRanges, NULL, 256, pVMWARE->maxWidth, 32 * 32,
                          128, pVMWARE->maxHeight,
                          pScrn->display->virtualX, pScrn->display->virtualY,
                          pVMWARE->videoRam,
                          LOOKUP_BEST_REFRESH);
    if (i == -1) {
        VMWAREFreeRec(pScrn);
        return FALSE;
    }
    xf86PruneDriverModes(pScrn);
    if (i == 0 || pScrn->modes == NULL) {
        xf86DrvMsg(pScrn->scrnIndex, X_ERROR, "No valid modes found\n");
        VMWAREFreeRec(pScrn);
        return FALSE;
    }
    xf86SetCrtcForModes(pScrn, INTERLACE_HALVE_V);
    pScrn->currentMode = pScrn->modes;
    xf86PrintModes(pScrn);
    xf86SetDpi(pScrn, 0, 0);
    if (!xf86LoadSubModule(pScrn, "fb") ||
        !xf86LoadSubModule(pScrn, "shadowfb")) {
        VMWAREFreeRec(pScrn);
        return FALSE;
    }
    xf86LoaderReqSymLists(fbSymbols, shadowfbSymbols, NULL);

    /* Need ramdac for hwcursor */
    if (pVMWARE->hwCursor) {
        if (!xf86LoadSubModule(pScrn, "ramdac")) {
            VMWAREFreeRec(pScrn);
            return FALSE;
        }
        xf86LoaderReqSymLists(ramdacSymbols, NULL);
    }

    return TRUE;
}

static Bool
VMWAREMapMem(ScrnInfoPtr pScrn)
{
    VMWAREPtr pVMWARE = VMWAREPTR(pScrn);
#if XSERVER_LIBPCIACCESS
    int err;
    struct pci_device *const device = pVMWARE->PciInfo;
#endif

#if XSERVER_LIBPCIACCESS
   err = pci_device_map_range(device,
                              pVMWARE->memPhysBase,
                              pVMWARE->videoRam,
                              PCI_DEV_MAP_FLAG_WRITABLE | 
                              PCI_DEV_MAP_FLAG_WRITE_COMBINE,
                              (void **) &pVMWARE->FbBase);
   if (err) {
       xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
                  "Unable to map frame buffer BAR. %s (%d)\n",
                  strerror (err), err);
       return FALSE;
   }

#else
    pVMWARE->FbBase = xf86MapPciMem(pScrn->scrnIndex, VIDMEM_FRAMEBUFFER,
                                    pVMWARE->PciTag,
                                    pVMWARE->memPhysBase,
                                    pVMWARE->videoRam);
#endif
    if (!pVMWARE->FbBase)
        return FALSE;

    VmwareLog(("FB Mapped: %p/%u -> %p/%u\n",
               pVMWARE->memPhysBase, pVMWARE->videoRam,
               pVMWARE->FbBase, pVMWARE->videoRam));
    return TRUE;
}

static Bool
VMWAREUnmapMem(ScrnInfoPtr pScrn)
{
    VMWAREPtr pVMWARE;

    pVMWARE = VMWAREPTR(pScrn);

    VmwareLog(("Unmapped: %p/%u\n", pVMWARE->FbBase, pVMWARE->videoRam));

#if XSERVER_LIBPCIACCESS
    pci_device_unmap_range(pVMWARE->PciInfo, pVMWARE->FbBase, pVMWARE->videoRam);
#else
    xf86UnMapVidMem(pScrn->scrnIndex, pVMWARE->FbBase, pVMWARE->videoRam);
#endif
    pVMWARE->FbBase = NULL;
    return TRUE;
}

static void
VMWARESave(ScrnInfoPtr pScrn)
{
    vgaHWPtr hwp = VGAHWPTR(pScrn);
    vgaRegPtr vgaReg = &hwp->SavedReg;
    VMWAREPtr pVMWARE = VMWAREPTR(pScrn);
    VMWARERegPtr vmwareReg = &pVMWARE->SavedReg;

    vgaHWSave(pScrn, vgaReg, VGA_SR_ALL);

    vmwareReg->svga_reg_enable = vmwareReadReg(pVMWARE, SVGA_REG_ENABLE);
    vmwareReg->svga_reg_width = vmwareReadReg(pVMWARE, SVGA_REG_WIDTH);
    vmwareReg->svga_reg_height = vmwareReadReg(pVMWARE, SVGA_REG_HEIGHT);
    vmwareReg->svga_reg_bits_per_pixel =
       vmwareReadReg(pVMWARE, SVGA_REG_BITS_PER_PIXEL);
    vmwareReg->svga_reg_id = vmwareReadReg(pVMWARE, SVGA_REG_ID);

    /* XXX this should be based on the cap bit, not hwCursor... */
    if (pVMWARE->hwCursor) {
       vmwareReg->svga_reg_cursor_on =
          vmwareReadReg(pVMWARE, SVGA_REG_CURSOR_ON);
       vmwareReg->svga_reg_cursor_x =
          vmwareReadReg(pVMWARE, SVGA_REG_CURSOR_X);
       vmwareReg->svga_reg_cursor_y =
          vmwareReadReg(pVMWARE, SVGA_REG_CURSOR_Y);
       vmwareReg->svga_reg_cursor_id =
          vmwareReadReg(pVMWARE, SVGA_REG_CURSOR_ID);
    }

    vmwareReg->svga_fifo_enabled = vmwareReadReg(pVMWARE, SVGA_REG_CONFIG_DONE);
}

static void
VMWARERestoreRegs(ScrnInfoPtr pScrn, VMWARERegPtr vmwareReg)
{
    VMWAREPtr pVMWARE = VMWAREPTR(pScrn);
    VmwareLog(("VMWARERestoreRegs: W: %d, H: %d, BPP: %d, Enable: %d\n",
	       vmwareReg->svga_reg_width, vmwareReg->svga_reg_height,
	       vmwareReg->svga_reg_bits_per_pixel, vmwareReg->svga_reg_enable));
    if (vmwareReg->svga_reg_enable) {
        vmwareWriteReg(pVMWARE, SVGA_REG_ID, vmwareReg->svga_reg_id);
        vmwareWriteReg(pVMWARE, SVGA_REG_WIDTH, vmwareReg->svga_reg_width);
        vmwareWriteReg(pVMWARE, SVGA_REG_HEIGHT, vmwareReg->svga_reg_height);
        vmwareWriteReg(pVMWARE, SVGA_REG_BITS_PER_PIXEL,
                       vmwareReg->svga_reg_bits_per_pixel);
        vmwareWriteReg(pVMWARE, SVGA_REG_ENABLE, vmwareReg->svga_reg_enable);
        vmwareWriteReg(pVMWARE, SVGA_REG_GUEST_ID, GUEST_OS_LINUX);
        if (pVMWARE->hwCursor) {
            vmwareWriteReg(pVMWARE, SVGA_REG_CURSOR_ID,
                           vmwareReg->svga_reg_cursor_id);
            vmwareWriteReg(pVMWARE, SVGA_REG_CURSOR_X,
                           vmwareReg->svga_reg_cursor_x);
            vmwareWriteReg(pVMWARE, SVGA_REG_CURSOR_Y,
                           vmwareReg->svga_reg_cursor_y);
            vmwareWriteReg(pVMWARE, SVGA_REG_CURSOR_ON,
                           vmwareReg->svga_reg_cursor_on);
        }
    } else {
        vmwareWriteReg(pVMWARE, SVGA_REG_ENABLE, vmwareReg->svga_reg_enable);
    }
}

static void
VMWARERestore(ScrnInfoPtr pScrn)
{
    vgaHWPtr hwp = VGAHWPTR(pScrn);
    vgaRegPtr vgaReg = &hwp->SavedReg;
    VMWAREPtr pVMWARE = VMWAREPTR(pScrn);
    VMWARERegPtr vmwareReg = &pVMWARE->SavedReg;

    vmwareWaitForFB(pVMWARE);
    if (!vmwareReg->svga_fifo_enabled) {
        VMWAREStopFIFO(pScrn);
    }

    vgaHWProtect(pScrn, TRUE);
    VMWARERestoreRegs(pScrn, vmwareReg);
    vgaHWRestore(pScrn, vgaReg, VGA_SR_ALL);
    vgaHWProtect(pScrn, FALSE);
}

static Bool
VMWAREModeInit(ScrnInfoPtr pScrn, DisplayModePtr mode, Bool rebuildPixmap)
{
    vgaHWPtr hwp = VGAHWPTR(pScrn);
    vgaRegPtr vgaReg = &hwp->ModeReg;
    VMWAREPtr pVMWARE = VMWAREPTR(pScrn);
    VMWARERegPtr vmwareReg = &pVMWARE->ModeReg;

    vgaHWUnlock(hwp);
    if (!vgaHWInit(pScrn, mode))
        return FALSE;
    pScrn->vtSema = TRUE;

    vmwareReg->svga_reg_enable = 1;
    vmwareReg->svga_reg_width = max(mode->HDisplay, pScrn->virtualX);
    vmwareReg->svga_reg_height = max(mode->VDisplay, pScrn->virtualY);
    vmwareReg->svga_reg_bits_per_pixel = pVMWARE->bitsPerPixel;

    vgaHWProtect(pScrn, TRUE);

    vgaHWRestore(pScrn, vgaReg, VGA_SR_ALL);
    VMWARERestoreRegs(pScrn, vmwareReg);

    if (pVMWARE->hwCursor) {
        vmwareCursorModeInit(pScrn, mode);
    }

    VmwareLog(("Required mode: %ux%u\n", mode->HDisplay, mode->VDisplay));
    VmwareLog(("Virtual:       %ux%u\n", pScrn->virtualX, pScrn->virtualY));
    VmwareLog(("dispWidth:     %u\n", pScrn->displayWidth));
    pVMWARE->fbOffset = vmwareReadReg(pVMWARE, SVGA_REG_FB_OFFSET);
    pVMWARE->fbPitch = vmwareReadReg(pVMWARE, SVGA_REG_BYTES_PER_LINE);
    pVMWARE->FbSize = vmwareReadReg(pVMWARE, SVGA_REG_FB_SIZE);

    pScrn->displayWidth = (pVMWARE->fbPitch * 8) / ((pScrn->bitsPerPixel + 7) & ~7);
    VmwareLog(("fbOffset:      %u\n", pVMWARE->fbOffset));
    VmwareLog(("fbPitch:       %u\n", pVMWARE->fbPitch));
    VmwareLog(("fbSize:        %u\n", pVMWARE->FbSize));
    VmwareLog(("New dispWidth: %u\n", pScrn->displayWidth));

    vmwareCheckVideoSanity(pScrn);

    if (rebuildPixmap) {
        pScrn->pScreen->ModifyPixmapHeader((*pScrn->pScreen->GetScreenPixmap)(pScrn->pScreen),
                                           pScrn->pScreen->width,
                                           pScrn->pScreen->height,
                                           pScrn->pScreen->rootDepth,
                                           pScrn->bitsPerPixel,
                                           PixmapBytePad(pScrn->displayWidth,
                                                         pScrn->pScreen->rootDepth),
                                           (pointer)(pVMWARE->FbBase + pScrn->fbOffset));

        (*pScrn->EnableDisableFBAccess)(pScrn->pScreen->myNum, FALSE);
        (*pScrn->EnableDisableFBAccess)(pScrn->pScreen->myNum, TRUE);
    }

    vgaHWProtect(pScrn, FALSE);

    /*
     * Push the new Xinerama state to X clients and the hardware,
     * synchronously with the mode change. Note that this must happen
     * AFTER we write the new width and height to the hardware
     * registers, since updating the WIDTH and HEIGHT registers will
     * reset the device's multimon topology.
     */
    vmwareNextXineramaState(pVMWARE);

    return TRUE;
}

void
vmwareNextXineramaState(VMWAREPtr pVMWARE)
{
    VMWARERegPtr vmwareReg = &pVMWARE->ModeReg;

    /*
     * Switch to the next Xinerama state (from pVMWARE->xineramaNextState).
     *
     * This new state will be available to X clients via the Xinerama
     * extension, and we push the new state to the virtual hardware,
     * in order to configure a number of virtual monitors within the
     * device's framebuffer.
     *
     * This function can be called at any time, but it should usually be
     * called just after a mode switch. This is for two reasons:
     *
     *   1) We don't want X clients to see a Xinerama topology and a video
     *      mode that are inconsistent with each other, so we'd like to switch
     *      both at the same time.
     *
     *   2) We must set the host's display topology registers after setting
     *      the new video mode, since writes to WIDTH/HEIGHT will reset the
     *      hardware display topology.
     */

    /*
     * Update Xinerama info appropriately.
     */
    if (pVMWARE->xinerama && !pVMWARE->xineramaStatic) {
       if (pVMWARE->xineramaNextState) {
          xfree(pVMWARE->xineramaState);
          pVMWARE->xineramaState = pVMWARE->xineramaNextState;
          pVMWARE->xineramaNumOutputs = pVMWARE->xineramaNextNumOutputs;

          pVMWARE->xineramaNextState = NULL;
          pVMWARE->xineramaNextNumOutputs = 0;

       } else {
          /*
           * There is no next state pending. Switch back to
           * single-monitor mode. This is necessary for resetting the
           * Xinerama state if we get a mode change which doesn't
           * follow a VMwareCtrlDoSetTopology call.
           */
          VMWAREXineramaPtr basicState =
             (VMWAREXineramaPtr)xcalloc(1, sizeof (VMWAREXineramaRec));
          if (basicState) {
             basicState->x_org = 0;
             basicState->y_org = 0;
             basicState->width = vmwareReg->svga_reg_width;
             basicState->height = vmwareReg->svga_reg_height;

             xfree(pVMWARE->xineramaState);
             pVMWARE->xineramaState = basicState;
             pVMWARE->xineramaNumOutputs = 1;
          }
       }
    }

    /*
     * Update host's view of guest topology. This tells the device
     * how we're carving up its framebuffer into virtual screens.
     */
    if (pVMWARE->vmwareCapability & SVGA_CAP_DISPLAY_TOPOLOGY) {
        if (pVMWARE->xinerama) {
            int i = 0;
            VMWAREXineramaPtr xineramaState = pVMWARE->xineramaState;
            vmwareWriteReg(pVMWARE, SVGA_REG_NUM_GUEST_DISPLAYS,
                           pVMWARE->xineramaNumOutputs);

            for (i = 0; i < pVMWARE->xineramaNumOutputs; i++) {
                vmwareWriteReg(pVMWARE, SVGA_REG_DISPLAY_ID, i);
                vmwareWriteReg(pVMWARE, SVGA_REG_DISPLAY_IS_PRIMARY, TRUE);
                vmwareWriteReg(pVMWARE, SVGA_REG_DISPLAY_POSITION_X,
                               xineramaState[i].x_org);
                vmwareWriteReg(pVMWARE, SVGA_REG_DISPLAY_POSITION_Y,
                               xineramaState[i].y_org);
                vmwareWriteReg(pVMWARE, SVGA_REG_DISPLAY_WIDTH,
                               xineramaState[i].width);
                vmwareWriteReg(pVMWARE, SVGA_REG_DISPLAY_HEIGHT,
                               xineramaState[i].height);
            }
        } else {
            vmwareWriteReg(pVMWARE, SVGA_REG_NUM_GUEST_DISPLAYS, 1);

            vmwareWriteReg(pVMWARE, SVGA_REG_DISPLAY_ID, 0);
            vmwareWriteReg(pVMWARE, SVGA_REG_DISPLAY_IS_PRIMARY, TRUE);
            vmwareWriteReg(pVMWARE, SVGA_REG_DISPLAY_POSITION_X, 0);
            vmwareWriteReg(pVMWARE, SVGA_REG_DISPLAY_POSITION_Y, 0);
            vmwareWriteReg(pVMWARE, SVGA_REG_DISPLAY_WIDTH, vmwareReg->svga_reg_width);
            vmwareWriteReg(pVMWARE, SVGA_REG_DISPLAY_HEIGHT, vmwareReg->svga_reg_height);
        }

        /* Done. */
        vmwareWriteReg(pVMWARE, SVGA_REG_DISPLAY_ID, SVGA_INVALID_DISPLAY_ID);
    }
}

static void
VMWAREAdjustFrame(int scrnIndex, int x, int y, int flags)
{
    /* FIXME */
}

static void
VMWAREInitFIFO(ScrnInfoPtr pScrn)
{
    VMWAREPtr pVMWARE = VMWAREPTR(pScrn);
#if XSERVER_LIBPCIACCESS
    struct pci_device *const device = pVMWARE->PciInfo;
    int err;
#endif
    CARD32* vmwareFIFO;
    Bool extendedFifo;
    int min;

    TRACEPOINT

    pVMWARE->mmioPhysBase = vmwareReadReg(pVMWARE, SVGA_REG_MEM_START);
    pVMWARE->mmioSize = vmwareReadReg(pVMWARE, SVGA_REG_MEM_SIZE) & ~3;
#if XSERVER_LIBPCIACCESS
    err = pci_device_map_range(device, pVMWARE->mmioPhysBase,
                               pVMWARE->mmioSize,
                               PCI_DEV_MAP_FLAG_WRITABLE,
                               (void **) &pVMWARE->mmioVirtBase);
    if (err) {
        xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
                   "Unable to map mmio BAR. %s (%d)\n",
                   strerror (err), err);
        return;
    }
#else
    pVMWARE->mmioVirtBase = xf86MapPciMem(pScrn->scrnIndex, VIDMEM_MMIO,
                                          pVMWARE->PciTag,
                                          pVMWARE->mmioPhysBase,
                                          pVMWARE->mmioSize);
#endif
    vmwareFIFO = pVMWARE->vmwareFIFO = (CARD32*)pVMWARE->mmioVirtBase;

    extendedFifo = pVMWARE->vmwareCapability & SVGA_CAP_EXTENDED_FIFO;
    min = extendedFifo ? vmwareReadReg(pVMWARE, SVGA_REG_MEM_REGS) : 4;

    vmwareFIFO[SVGA_FIFO_MIN] = min * sizeof(CARD32);
    vmwareFIFO[SVGA_FIFO_MAX] = pVMWARE->mmioSize;
    vmwareFIFO[SVGA_FIFO_NEXT_CMD] = min * sizeof(CARD32);
    vmwareFIFO[SVGA_FIFO_STOP] = min * sizeof(CARD32);
    vmwareWriteReg(pVMWARE, SVGA_REG_CONFIG_DONE, 1);
}

static void
VMWAREStopFIFO(ScrnInfoPtr pScrn)
{
    VMWAREPtr pVMWARE = VMWAREPTR(pScrn);

    TRACEPOINT

    vmwareWriteReg(pVMWARE, SVGA_REG_CONFIG_DONE, 0);
#if XSERVER_LIBPCIACCESS
    pci_device_unmap_range(pVMWARE->PciInfo, pVMWARE->mmioVirtBase, pVMWARE->mmioSize);
#else
    xf86UnMapVidMem(pScrn->scrnIndex, pVMWARE->mmioVirtBase, pVMWARE->mmioSize);
#endif
}

static Bool
VMWARECloseScreen(int scrnIndex, ScreenPtr pScreen)
{
    ScrnInfoPtr pScrn = xf86Screens[scrnIndex];
    VMWAREPtr pVMWARE = VMWAREPTR(pScrn);
    ScreenPtr save = &pVMWARE->ScrnFuncs;

    VmwareLog(("cursorSema: %d\n", pVMWARE->cursorSema));

    if (*pVMWARE->pvtSema) {
        if (pVMWARE->videoStreams) {
            vmwareVideoEnd(pScreen);
        }

        if (pVMWARE->CursorInfoRec) {
            vmwareCursorCloseScreen(pScreen);
        }

        VMWARERestore(pScrn);
        VMWAREUnmapMem(pScrn);

        pScrn->vtSema = FALSE;
    }

    pScreen->CloseScreen = save->CloseScreen;
    pScreen->SaveScreen = save->SaveScreen;

#if VMWARE_DRIVER_FUNC
    pScrn->DriverFunc = NULL;
#endif

    return (*pScreen->CloseScreen)(scrnIndex, pScreen);
}

static Bool
VMWARESaveScreen(ScreenPtr pScreen, int mode)
{
    VmwareLog(("VMWareSaveScreen() mode = %d\n", mode));

    /*
     * This thoroughly fails to do anything useful to svga mode.  I doubt
     * we care; who wants to idle-blank their VM's screen anyway?
     */
    return vgaHWSaveScreen(pScreen, mode);
}

/* disabled by default to reduce spew in DEBUG_LOGGING mode. */
/*#define DEBUG_LOG_UPDATES*/

static void
VMWAREPreDirtyBBUpdate(ScrnInfoPtr pScrn, int nboxes, BoxPtr boxPtr)
{
    VMWAREPtr pVMWARE = VMWAREPTR(pScrn);

#ifdef DEBUG_LOG_UPDATES
    {
        int i;
        for (i = 0; i < nboxes; i++) {
            VmwareLog(("PreUpdate #%d (%d, %d, w = %d, h = %d)\n", nboxes - i,
                       boxPtr[i].x1, boxPtr[i].y1,
                       boxPtr[i].x2 - boxPtr[i].x1,
                       boxPtr[i].y2 - boxPtr[i].y1));
        }
    }
#endif

    /*
     * We only register this callback if we have a HW cursor.
     */
    while (nboxes--) {
        if (BOX_INTERSECT(*boxPtr, pVMWARE->hwcur.box)) {
            if (!pVMWARE->cursorExcludedForUpdate) {
                PRE_OP_HIDE_CURSOR();
                pVMWARE->cursorExcludedForUpdate = TRUE;
            }
	    break;
        }
        boxPtr++;
    }
}

static void
VMWAREPostDirtyBBUpdate(ScrnInfoPtr pScrn, int nboxes, BoxPtr boxPtr)
{
    VMWAREPtr pVMWARE = VMWAREPTR(pScrn);
    while (nboxes--) {
#ifdef DEBUG_LOG_UPDATES
        VmwareLog(("PostUpdate #%d (%d, %d, w = %d, h = %d)\n", nboxes,
                   boxPtr->x1, boxPtr->y1,
                   boxPtr->x2 - boxPtr->x1, boxPtr->y2 - boxPtr->y1));
#endif

        /* Clip off (y only) for offscreen memory */
        if (boxPtr->y2 >= pVMWARE->ModeReg.svga_reg_height)
            boxPtr->y2 = pVMWARE->ModeReg.svga_reg_height;
        if (boxPtr->y1 >= pVMWARE->ModeReg.svga_reg_height)
            boxPtr->y1 = pVMWARE->ModeReg.svga_reg_height;
        if (boxPtr->y1 == boxPtr->y2) {
            boxPtr++;
            continue;
        }

        vmwareSendSVGACmdUpdate(pVMWARE, boxPtr++);
    }

    if (pVMWARE->hwCursor && pVMWARE->cursorExcludedForUpdate) {
        POST_OP_SHOW_CURSOR();
        pVMWARE->cursorExcludedForUpdate = FALSE;
    }
}

static void
VMWARELoadPalette(ScrnInfoPtr pScrn, int numColors, int* indices,
                  LOCO* colors, VisualPtr pVisual)
{
    VMWAREPtr pVMWARE = VMWAREPTR(pScrn);
    int i;

    for (i = 0; i < numColors; i++) {
        vmwareWriteReg(pVMWARE, SVGA_PALETTE_BASE + *indices * 3 + 0, colors[*indices].red);
        vmwareWriteReg(pVMWARE, SVGA_PALETTE_BASE + *indices * 3 + 1, colors[*indices].green);
        vmwareWriteReg(pVMWARE, SVGA_PALETTE_BASE + *indices * 3 + 2, colors[*indices].blue);
        indices++;
    }
    VmwareLog(("Palette loading done\n"));
}


DisplayModeRec *
VMWAREAddDisplayMode(ScrnInfoPtr pScrn,
                     const char *name,
                     int width,
                     int height)
{
   DisplayModeRec *mode;

   mode = xalloc(sizeof(DisplayModeRec));
   memset(mode, 0, sizeof *mode);

   mode->name = xalloc(strlen(name) + 1);
   strcpy(mode->name, name);
   mode->status = MODE_OK;
   mode->type = M_T_DEFAULT;
   mode->HDisplay = width;
   mode->VDisplay = height;

   mode->next = pScrn->modes;
   mode->prev = pScrn->modes->prev;
   pScrn->modes->prev->next = mode;
   pScrn->modes->prev = mode;

   return mode;
}


/*
 *-----------------------------------------------------------------------------
 *
 * vmwareIsRegionEqual --
 *
 *    This function implements REGION_EQUAL because older versions of
 *    regionstr.h don't define it.
 *    It is a slightly modified version of miRegionEqual from $Xorg: miregion.c
 *
 * Results:
 *    TRUE if regions are equal; FALSE otherwise
 *
 * Side effects:
 *    None.
 *
 *-----------------------------------------------------------------------------
 */

Bool
vmwareIsRegionEqual(const RegionPtr reg1,
                    const RegionPtr reg2)
{
    int i, num;
    BoxPtr rects1, rects2;

    if ((reg1->extents.x1 != reg2->extents.x1) ||
        (reg1->extents.x2 != reg2->extents.x2) ||
        (reg1->extents.y1 != reg2->extents.y1) ||
        (reg1->extents.y2 != reg2->extents.y2)) {
        return FALSE;
    }

    num = REGION_NUM_RECTS(reg1);
    if (num != REGION_NUM_RECTS(reg2)) {
        return FALSE;
    }

    rects1 = REGION_RECTS(reg1);
    rects2 = REGION_RECTS(reg2);

    for (i = 0; i < num; i++) {
        if ((rects1[i].x1 != rects2[i].x1) ||
            (rects1[i].x2 != rects2[i].x2) ||
            (rects1[i].y1 != rects2[i].y1) ||
            (rects1[i].y2 != rects2[i].y2)) {
            return FALSE;
        }
    }

    return TRUE;
}


#if VMWARE_DRIVER_FUNC
static Bool
VMWareDriverFunc(ScrnInfoPtr pScrn,
                 xorgDriverFuncOp op,
                 pointer data)
{
   CARD32 *flag;
   xorgRRModeMM *modemm;

   switch (op) {
   case GET_REQUIRED_HW_INTERFACES:
      flag = (CARD32 *)data;

      if (flag) {
         *flag = HW_IO | HW_MMIO;
      }
      return TRUE;
   case RR_GET_MODE_MM:
      modemm = (xorgRRModeMM *)data;

      /*
       * Because changing the resolution of the guest is usually changing the size
       * of a window on the host desktop, the real physical DPI will not change. To
       * keep the guest in sync, we scale the 'physical' screen dimensions to
       * keep the DPI constant.
       */
      if (modemm && modemm->mode) {
         modemm->mmWidth *= modemm->mode->HDisplay / (double)(modemm->virtX);
         modemm->mmHeight *= modemm->mode->VDisplay / (double)(modemm->virtY);
      }
      return TRUE;
   default:
      return FALSE;
   }
}
#endif


static Bool
VMWAREScreenInit(int scrnIndex, ScreenPtr pScreen, int argc, char **argv)
{
    ScrnInfoPtr pScrn;
    vgaHWPtr hwp;
    VMWAREPtr pVMWARE;
    OptionInfoPtr options;
    Bool useXinerama = TRUE;

    /* Get the ScrnInfoRec */
    pScrn = xf86Screens[pScreen->myNum];
    pVMWARE = VMWAREPTR(pScrn);


    xf86CollectOptions(pScrn, NULL);
    if (!(options = xalloc(sizeof(VMWAREOptions))))
        return FALSE;
    memcpy(options, VMWAREOptions, sizeof(VMWAREOptions));
    xf86ProcessOptions(pScrn->scrnIndex, pScrn->options, options);

    /*
     * Init xinerama preferences.
     */
    useXinerama = xf86ReturnOptValBool(options, OPTION_XINERAMA,
                                       pVMWARE->vmwareCapability & SVGA_CAP_MULTIMON);
    if (useXinerama && !(pVMWARE->vmwareCapability & SVGA_CAP_MULTIMON)) {
       xf86DrvMsg(pScrn->scrnIndex, X_WARNING,
                  "Xinerama is not safely supported by the current virtual hardware. "
                  "Do not request resolutions that require > 16MB of framebuffer.\n");
    }


    if (useXinerama && xf86IsOptionSet(options, OPTION_STATIC_XINERAMA)) {
       char *topology = xf86GetOptValString(options, OPTION_STATIC_XINERAMA);
       if (topology) {
          pVMWARE->xineramaState =
             VMWAREParseTopologyString(pScrn, topology, &pVMWARE->xineramaNumOutputs);

         pVMWARE->xineramaStatic = pVMWARE->xineramaState != NULL;

         xfree(topology);
       }
    }

    xfree(options);

    /* Initialise VMWARE_CTRL extension. */
    VMwareCtrl_ExtInit(pScrn);

    /* Initialise Xinerama extension. */
    if (useXinerama) {
       VMwareXinerama_ExtInit(pScrn);
    }

    if (pVMWARE->xinerama && pVMWARE->xineramaStatic) {
       xf86DrvMsg(pScrn->scrnIndex, X_INFO, pVMWARE->xineramaState ?
                                            "Using static Xinerama.\n" :
                                            "Failed to configure static Xinerama.\n");
    }

    /*
     * If using the vgahw module, its data structures and related
     * things are typically initialised/mapped here.
     */
    hwp = VGAHWPTR(pScrn);
    vgaHWGetIOBase(hwp);

    VMWAREInitFIFO(pScrn);

    /* Initialise the first mode */
    VMWAREModeInit(pScrn, pScrn->currentMode, FALSE);

    /* Set the viewport if supported */
    VMWAREAdjustFrame(scrnIndex, pScrn->frameX0, pScrn->frameY0, 0);

    /*
     * Setup the screen's visuals, and initialise the framebuffer
     * code.
     */
    VMWAREMapMem(pScrn);

    /*
     * Clear the framebuffer (and any black-border mode areas).
     */
    memset(pVMWARE->FbBase, 0, pVMWARE->FbSize);
    vmwareSendSVGACmdUpdateFullScreen(pVMWARE);

    /* Reset the visual list */
    miClearVisualTypes();

    /*
     * Setup the visuals supported.  This driver only supports
     * TrueColor for bpp > 8, so the default set of visuals isn't
     * acceptable.  To deal with this, call miSetVisualTypes with
     * the appropriate visual mask.
     */
    if (pScrn->bitsPerPixel > 8) {
        if (!miSetVisualTypes(pScrn->depth, TrueColorMask,
                              pScrn->rgbBits, pScrn->defaultVisual)) {
            return FALSE;
        }
    } else {
        if (!miSetVisualTypes(pScrn->depth,
                              miGetDefaultVisualMask(pScrn->depth),
                              pScrn->rgbBits, pScrn->defaultVisual)) {
            return FALSE;
        }
    }

    miSetPixmapDepths();

    /*
     * Initialise the framebuffer.
     */
    if (!fbScreenInit(pScreen, pVMWARE->FbBase + pVMWARE->fbOffset,
                      pScrn->virtualX, pScrn->virtualY,
                      pScrn->xDpi, pScrn->yDpi,
                      pScrn->displayWidth,
                      pScrn->bitsPerPixel)) {
        return FALSE;
    }

    /* Override the default mask/offset settings */
    if (pScrn->bitsPerPixel > 8) {
        int i;
        VisualPtr visual;

        for (i = 0, visual = pScreen->visuals;
             i < pScreen->numVisuals; i++, visual++) {
            if ((visual->class | DynamicClass) == DirectColor) {
                visual->offsetRed = pScrn->offset.red;
                visual->offsetGreen = pScrn->offset.green;
                visual->offsetBlue = pScrn->offset.blue;
                visual->redMask = pScrn->mask.red;
                visual->greenMask = pScrn->mask.green;
                visual->blueMask = pScrn->mask.blue;
            }
        }
    }

    /* must be after RGB ordering fixed */
    fbPictureInit (pScreen, 0, 0);

    /*
     * Save the old screen vector, then wrap CloseScreen and
     * set SaveScreen.
     */
    pVMWARE->ScrnFuncs = *pScreen;
    pScreen->CloseScreen = VMWARECloseScreen;
    pScreen->SaveScreen = VMWARESaveScreen;

    /*
     * Set initial black & white colourmap indices.
     */
    xf86SetBlackWhitePixels(pScreen);

    /*
     * Initialize shadowfb to notify us of dirty rectangles.  We only
     * need preFB access callbacks if we're using the hw cursor.
     */
    if (!ShadowFBInit2(pScreen, 
                       pVMWARE->hwCursor ? VMWAREPreDirtyBBUpdate : NULL,
                       VMWAREPostDirtyBBUpdate)) {
        xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
                   "ShadowFB initialization failed\n");
        return FALSE;
    }

    /*
     * If we have a hw cursor, we need to hook functions that might
     * read from the framebuffer.
     */
    if (pVMWARE->hwCursor) {
        vmwareCursorHookWrappers(pScreen);
    }

    /*
     * If backing store is to be supported (as is usually the case),
     * initialise it.
     */
    miInitializeBackingStore(pScreen);
    xf86SetBackingStore(pScreen);
    xf86SetSilkenMouse(pScreen);

    /*
     * Initialize software cursor.
     */
    miDCInitialize(pScreen, xf86GetPointerScreenFuncs());

    /*
     * Initialize hardware cursor.
     */
    if (pVMWARE->hwCursor) {
        if (!vmwareCursorInit(pScreen)) {
            xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
                       "Hardware cursor initialization failed\n");
            pVMWARE->hwCursor = FALSE;
        }
    }

    /*
     * Install colourmap functions.  If using the vgahw module,
     * vgaHandleColormaps would usually be called here.
     */
    if (!fbCreateDefColormap(pScreen))
        return FALSE;

    if (!xf86HandleColormaps(pScreen, 256, 8,
                             VMWARELoadPalette, NULL,
                             CMAP_PALETTED_TRUECOLOR |
                             CMAP_RELOAD_ON_MODE_SWITCH)) {
        return FALSE;
    }

    /*
     * We explictly add a set of default modes because the X server will
     * not include modes larger than the initial one.
     */
   {
      unsigned int i;
      unsigned int numModes = sizeof (VMWAREDefaultModes) / sizeof *(VMWAREDefaultModes);
      char name[10];
      for (i = 0; i < numModes; i++) {
         const VMWAREDefaultMode *mode = &VMWAREDefaultModes[i];

         /* Only modes that fit the hardware maximums should be added. */
         if (mode->width <= pVMWARE->maxWidth && mode->height <= pVMWARE->maxHeight) {
            snprintf(name, 10, "%dx%d", mode->width, mode->height);
            VMWAREAddDisplayMode(pScrn, name, mode->width, mode->height);
         }
      }

      /* Add the hardware maximums as a mode. */
      snprintf(name, 10, "%dx%d", pVMWARE->maxWidth, pVMWARE->maxHeight);
      VMWAREAddDisplayMode(pScrn, name, pVMWARE->maxWidth, pVMWARE->maxHeight);
   }

    /*
     * We will lazily add the dynamic modes as the are needed when new
     * modes are requested through the control extension.
     */
    memset(&pVMWARE->dynModes, 0, sizeof pVMWARE->dynModes);

#if VMWARE_DRIVER_FUNC
    pScrn->DriverFunc = VMWareDriverFunc;
#endif

    /* Report any unused options (only for the first generation) */
    if (serverGeneration == 1) {
        xf86ShowUnusedOptions(pScrn->scrnIndex, pScrn->options);
    }

    /* Initialize Xv extension */
    pVMWARE->videoStreams = NULL;
    if (vmwareVideoEnabled(pVMWARE)) {
        if (!vmwareVideoInit(pScreen)) {
            xf86DrvMsg(pScrn->scrnIndex, X_ERROR, "Xv initialization failed\n");
        }
    }


    /* Done */
    return TRUE;
}

static Bool
VMWARESwitchMode(int scrnIndex, DisplayModePtr mode, int flags)
{
    return VMWAREModeInit(xf86Screens[scrnIndex], mode, TRUE);
}

static Bool
VMWAREEnterVT(int scrnIndex, int flags)
{
    ScrnInfoPtr pScrn = xf86Screens[scrnIndex];
    VMWAREPtr pVMWARE = VMWAREPTR(pScrn);

    /*
     * After system resumes from hiberation, EnterVT will be called and this
     * is a good place to restore the SVGA ID register.
     */
    vmwareWriteReg(pVMWARE, SVGA_REG_ID, pVMWARE->suspensionSavedRegId);

    if (!pVMWARE->SavedReg.svga_fifo_enabled) {       
        VMWAREInitFIFO(pScrn);
    }

    return VMWAREModeInit(pScrn, pScrn->currentMode, TRUE);
}

static void
VMWARELeaveVT(int scrnIndex, int flags)
{
    ScrnInfoPtr pScrn = xf86Screens[scrnIndex];
    VMWAREPtr pVMWARE = VMWAREPTR(pScrn);

    /*
     * Before shutting down system for hibneration, LeaveVT will be called,
     * we save the ID register value here and later restore it in EnterVT.
     */
    pVMWARE->suspensionSavedRegId = vmwareReadReg(pVMWARE, SVGA_REG_ID);

    VMWARERestore(pScrn);
}

static void
VMWAREFreeScreen(int scrnIndex, int flags)
{
    /*
     * If the vgahw module is used vgaHWFreeHWRec() would be called
     * here.
     */
   VMWAREFreeRec(xf86Screens[scrnIndex]);
}

static ModeStatus
VMWAREValidMode(int scrnIndex, DisplayModePtr mode, Bool verbose, int flags)
{
    return MODE_OK;
}

#if XSERVER_LIBPCIACCESS
static Bool
VMwarePciProbe (DriverPtr           drv,
                int                 entity_num,
                struct pci_device   *device,
                intptr_t            match_data)
{
    ScrnInfoPtr     scrn = NULL;
    EntityInfoPtr   entity;

    scrn = xf86ConfigPciEntity(scrn, 0, entity_num, VMWAREPciChipsets,
                               NULL, NULL, NULL, NULL, NULL);
    if (scrn != NULL) {
        scrn->driverVersion = VMWARE_DRIVER_VERSION;
        scrn->driverName = VMWARE_DRIVER_NAME;
        scrn->name = VMWARE_NAME;
        scrn->Probe = NULL;
    }

    entity = xf86GetEntityInfo(entity_num);
    switch (DEVICE_ID(device)) {
    case PCI_CHIP_VMWARE0405:
    case PCI_CHIP_VMWARE0710:
        xf86MsgVerb(X_INFO, 4, "VMwarePciProbe: Valid device\n");
        scrn->PreInit = VMWAREPreInit;
        scrn->ScreenInit = VMWAREScreenInit;
        scrn->SwitchMode = VMWARESwitchMode;
        scrn->EnterVT = VMWAREEnterVT;
        scrn->LeaveVT = VMWARELeaveVT;
        scrn->FreeScreen = VMWAREFreeScreen;
        scrn->ValidMode = VMWAREValidMode;
        break;
    default:
        xf86MsgVerb(X_INFO, 4, "VMwarePciProbe: Unknown device\n");
    }
    return scrn != NULL;
}
#else 

static Bool
VMWAREProbe(DriverPtr drv, int flags)
{
    int numDevSections, numUsed;
    GDevPtr *devSections;
    int *usedChips;
    int i;
    Bool foundScreen = FALSE;
    char buildString[sizeof(VMWAREBuildStr)];

    RewriteTagString(VMWAREBuildStr, buildString, sizeof(VMWAREBuildStr));
    xf86MsgVerb(X_PROBED, 4, "%s", buildString);

    numDevSections = xf86MatchDevice(VMWARE_DRIVER_NAME, &devSections);
    if (numDevSections <= 0) {
#ifdef DEBUG
        xf86MsgVerb(X_ERROR, 0, "No vmware driver section\n");
#endif
        return FALSE;
    }
    if (xf86GetPciVideoInfo()) {
        VmwareLog(("Some PCI Video Info Exists\n"));
        numUsed = xf86MatchPciInstances(VMWARE_NAME, PCI_VENDOR_VMWARE,
                                        VMWAREChipsets, VMWAREPciChipsets, devSections,
                                        numDevSections, drv, &usedChips);
        xfree(devSections);
        if (numUsed <= 0)
            return FALSE;
        if (flags & PROBE_DETECT)
            foundScreen = TRUE;
        else
            for (i = 0; i < numUsed; i++) {
                ScrnInfoPtr pScrn = NULL;

                VmwareLog(("Even some VMware SVGA PCI instances exists\n"));
                pScrn = xf86ConfigPciEntity(pScrn, flags, usedChips[i],
                                            VMWAREPciChipsets, NULL, NULL, NULL,
                                            NULL, NULL);
                if (pScrn) {
                    VmwareLog(("And even configuration suceeded\n"));
                    pScrn->driverVersion = VMWARE_DRIVER_VERSION;
                    pScrn->driverName = VMWARE_DRIVER_NAME;
                    pScrn->name = VMWARE_NAME;
                    pScrn->Probe = VMWAREProbe;
                    pScrn->PreInit = VMWAREPreInit;
                    pScrn->ScreenInit = VMWAREScreenInit;
                    pScrn->SwitchMode = VMWARESwitchMode;
                    pScrn->AdjustFrame = VMWAREAdjustFrame;
                    pScrn->EnterVT = VMWAREEnterVT;
                    pScrn->LeaveVT = VMWARELeaveVT;
                    pScrn->FreeScreen = VMWAREFreeScreen;
                    pScrn->ValidMode = VMWAREValidMode;
                    foundScreen = TRUE;
                }
            }
        xfree(usedChips);
    }
    return foundScreen;
}
#endif


_X_EXPORT DriverRec VMWARE = {
    VMWARE_DRIVER_VERSION,
    VMWARE_DRIVER_NAME,
    VMWAREIdentify,
#if XSERVER_LIBPCIACCESS
    NULL,
#else
    VMWAREProbe,
#endif
    VMWAREAvailableOptions,
    NULL,
    0,
#if VMWARE_DRIVER_FUNC
    VMWareDriverFunc,
#endif
#if XSERVER_LIBPCIACCESS
    VMwareDeviceMatch,
    VMwarePciProbe,
#endif
};

#ifdef XFree86LOADER
static MODULESETUPPROTO(vmwareSetup);

_X_EXPORT XF86ModuleData vmwareModuleData = {
    &vmwareVersRec,
    vmwareSetup,
    NULL
};

static pointer
vmwareSetup(pointer module, pointer opts, int *errmaj, int *errmin)
{
    static Bool setupDone = FALSE;

    if (!setupDone) {
        setupDone = TRUE;
        xf86AddDriver(&VMWARE, module, VMWARE_DRIVER_FUNC);

        LoaderRefSymLists(vgahwSymbols, fbSymbols, ramdacSymbols,
                          shadowfbSymbols, NULL);

        return (pointer)1;
    }
    if (errmaj) {
        *errmaj = LDR_ONCEONLY;
    }
    return NULL;
}
#endif	/* XFree86LOADER */
