Site hosted by Angelfire.com: Build your free website today!

Hands-on Projects for the Linux Graphics Subsystem

New book from Christos Karayiannis

Available in Amazon Kindle format at:

amazon.com   amazon.co.uk   amazon.de   amazon.es   amazon.fr   amazon.it

MGAScreenInit()

MGAScreenInit() fills the pScreen (type ScreenPtr) field of the current ScrnInfoRec (xf86Screens[] item) and also uses pScrn as a backward reference to the ScrnInfoRec to fill some of its fields:

   pScrn = xf86Screens[pScreen->myNum];
Notice that some of the pScreen fields, for instance myNum, were previously filled at section 3.2.2.10.

It uses macro MGAPTR, defined in mga.h as:

#define MGAPTR(p) ((MGAPtr)((p)->driverPrivate))

to access the driverPrivate field of the current ScrnInfoRec.

Also uses the VGAHWPTR, defined in vgaHW.h as:

#define VGAHWPTR(p) ((vgaHWPtr)((p)->privates[vgaHWGetIndex()].ptr))

to access the privates field of the current ScrnInfoRec.

MGAScreenInit() is implemented as:

/* This gets called at the start of each server generation */

static Bool
MGAScreenInit(int scrnIndex, ScreenPtr pScreen, int argc, char **argv)
{
    ScrnInfoPtr pScrn;
    vgaHWPtr hwp;
    MGAPtr pMga;
    MGARamdacPtr MGAdac;
    int ret;
    VisualPtr visual;
    unsigned char *FBStart;
    int width, height, displayWidth;
    MGAEntPtr pMgaEnt = NULL;
    int f;
    CARD32 VRTemp, FBTemp;
#ifdef XF86DRI
    MessageType driFrom = X_DEFAULT;
#endif

    /*
     * First get the ScrnInfoRec
     */
    pScrn = xf86Screens[pScreen->myNum];

    hwp = VGAHWPTR(pScrn);
    pMga = MGAPTR(pScrn);
    MGAdac = &pMga->Dac;

    if ((pMga->Chipset == PCI_CHIP_MGAG200_SE_A_PCI) ||
	(pMga->Chipset == PCI_CHIP_MGAG200_SE_B_PCI)) {
	VRTemp = pScrn->videoRam;
	FBTemp = pMga->FbMapSize;
	pScrn->videoRam = 4096;
	pMga->FbMapSize = pScrn->videoRam * 1024;
    }
    
    /* Map the MGA memory and MMIO areas */
    if (pMga->FBDev) {
	if (!MGAMapMemFBDev(pScrn))
	    return FALSE;
    } else {
	if (!MGAMapMem(pScrn))
	    return FALSE;
    }
    
    if ((pMga->Chipset == PCI_CHIP_MGAG100)
	|| (pMga->Chipset == PCI_CHIP_MGAG100_PCI))
        MGAG100BlackMagic(pScrn);

    if (pMga->DualHeadEnabled) {
       DevUnion *pPriv;
       pPriv = xf86GetEntityPrivate(pScrn->entityList[0], MGAEntityIndex);
       pMgaEnt = pPriv->ptr;
       pMgaEnt->refCount++;
#ifdef USEMGAHAL
       MGA_HAL(
       if(pMgaEnt->refCount == 1) {
	   CARD8 MiscCtlReg;
	  pMga->pBoard = xalloc(sizeof(CLIENTDATA) + MGAGetBOARDHANDLESize());
	  pMga->pClientStruct = xalloc(sizeof(CLIENTDATA));
	  pMga->pClientStruct->pMga = (MGAPtr) pMga;
	  
	  /* wrapping OpenLibrary to fix broken registers. MATROX: hint,hint.*/
          MiscCtlReg = inMGAdac(MGA1064_MISC_CTL);
	  MGAOpenLibrary(pMga->pBoard,pMga->pClientStruct,sizeof(CLIENTDATA));
	  outMGAdac(MGA1064_MISC_CTL,MiscCtlReg);
	  pMga->pMgaHwInfo = xalloc(sizeof(MGAHWINFO));
	  MGAGetHardwareInfo(pMga->pBoard,pMga->pMgaHwInfo);

	  /* Detecting for type of display */
	  if (pMga->pMgaHwInfo->ulCapsSecondOutput & MGAHWINFOCAPS_OUTPUT_TV) {
	  	xf86DrvMsg(pScrn->scrnIndex, X_PROBED, "TV detected\n");
	  }
	  if (pMga->pMgaHwInfo->ulCapsFirstOutput &
			MGAHWINFOCAPS_OUTPUT_DIGITAL) {
	  	xf86DrvMsg(pScrn->scrnIndex, X_PROBED,
				"Digital Screen detected\n");
	  }
	  if (pMga->pMgaHwInfo->ulCapsSecondOutput &
			MGAHWINFOCAPS_OUTPUT_DIGITAL) {
	  	xf86DrvMsg(pScrn->scrnIndex, X_PROBED,
				"Digital Screen detected\n");
	  }

	  /* Now copy these to the entitystructure */
	  pMgaEnt->pClientStruct = pMga->pClientStruct;
	  pMgaEnt->pBoard = pMga->pBoard;
	  pMgaEnt->pMgaHwInfo = pMga->pMgaHwInfo;
       } else { /* Ref count is 2 */
	  pMga->pClientStruct = pMgaEnt->pClientStruct;
	  pMga->pBoard = pMgaEnt->pBoard;
	  pMga->pMgaHwInfo = pMgaEnt->pMgaHwInfo;
       }
       );	/* MGA_HAL */
#endif
    } else {
#ifdef USEMGAHAL
	CARD8 MiscCtlReg;

	  MGA_HAL(
	  pMga->pBoard = xalloc(sizeof(CLIENTDATA) + MGAGetBOARDHANDLESize());
	  pMga->pClientStruct = xalloc(sizeof(CLIENTDATA));
	  pMga->pClientStruct->pMga = (MGAPtr) pMga;

	  MiscCtlReg = inMGAdac(MGA1064_MISC_CTL);
	  /* wrapping OpenLibrary to fix broken registers. MATROX: hint,hint.*/
	  MGAOpenLibrary(pMga->pBoard,pMga->pClientStruct,sizeof(CLIENTDATA));
	  outMGAdac(MGA1064_MISC_CTL,MiscCtlReg);
	  pMga->pMgaHwInfo = xalloc(sizeof(MGAHWINFO));
	  MGAGetHardwareInfo(pMga->pBoard,pMga->pMgaHwInfo);
	  );	/* MGA_HAL */
#endif
    }
    if ((pMga->Chipset == PCI_CHIP_MGAG200_SE_A_PCI) ||
	(pMga->Chipset == PCI_CHIP_MGAG200_SE_B_PCI)) {
	pScrn->videoRam = VRTemp;
	pMga->FbMapSize = FBTemp;
    }
#ifdef USEMGAHAL
    MGA_HAL(
	/* There is a problem in the HALlib: set soft reset bit */
	/* MATROX: hint, hint. */
	if (!pMga->Primary && !pMga->FBDev &&
	    (pMga->PciInfo->subsysCard == PCI_CARD_MILL_G200_SG) ) {
	    OUTREG(MGAREG_Reset, 1);
	    usleep(200);
	    OUTREG(MGAREG_Reset, 0);
	}
    );	/* MGA_HAL */
#endif


    /* Initialise the MMIO vgahw functions */
    vgaHWSetMmioFuncs(hwp, pMga->IOBase, PORT_OFFSET);
    vgaHWGetIOBase(hwp);

    /* Map the VGA memory when the primary video */
    if (pMga->Primary && !pMga->FBDev) {
	hwp->MapSize = 0x10000;
	if (!vgaHWMapMem(pScrn))
	    return FALSE;
    }

    if (pMga->FBDev) {
	fbdevHWSave(pScrn);
	/* Disable VGA core, and leave memory access on */
	pciSetBitsLong(pMga->PciTag, PCI_OPTION_REG, 0x100, 0x000);
	if (!fbdevHWModeInit(pScrn, pScrn->currentMode))
	    return FALSE;
	if(pMga->SecondCrtc == FALSE && pMga->HWCursor == TRUE) {
	    switch (pMga->Chipset) {
	    case PCI_CHIP_MGA1064:
	    case PCI_CHIP_MGAG100:
	    case PCI_CHIP_MGAG100_PCI:
	    case PCI_CHIP_MGAG200:
	    case PCI_CHIP_MGAG200_PCI:
	    case PCI_CHIP_MGAG200_SE_A_PCI:
	    case PCI_CHIP_MGAG200_SE_B_PCI:
	    case PCI_CHIP_MGAG400:
	    case PCI_CHIP_MGAG550:
		outMGAdac(MGA1064_CURSOR_BASE_ADR_LOW, pMga->FbCursorOffset >> 10);
		outMGAdac(MGA1064_CURSOR_BASE_ADR_HI, pMga->FbCursorOffset >> 18);
		break;
	    default:
		break;
	    }
	}

	MGAStormEngineInit(pScrn);
    } else {
	/* Save the current state */
	MGASave(pScrn);
	/* Initialise the first mode */
	if (!MGAModeInit(pScrn, pScrn->currentMode))
	    return FALSE;
    }
    /* Darken the screen for aesthetic reasons and set the viewport */
    if (pMga->SecondCrtc == TRUE && !pMga->MergedFB) { 
	MGASaveScreenCrtc2(pScreen, SCREEN_SAVER_ON);
    } 
    if (pMga->SecondCrtc == FALSE && !pMga->MergedFB) {
	MGASaveScreen(pScreen, SCREEN_SAVER_ON);
    }
    if( pMga->MergedFB ) {
	MGASaveScreenMerged( pScreen, SCREEN_SAVER_ON );
    }
    pScrn->AdjustFrame(scrnIndex, pScrn->frameX0, pScrn->frameY0, 0);

    /*
     * The next step is to setup the screen's visuals, and initialise the
     * framebuffer code.  In cases where the framebuffer's default
     * choices for things like visual layouts and bits per RGB are OK,
     * this may be as simple as calling the framebuffer's ScreenInit()
     * function.  If not, the visuals will need to be setup before calling
     * a fb ScreenInit() function and fixed up after.
     *
     * For most PC hardware at depths >= 8, the defaults that cfb uses
     * are not appropriate.  In this driver, we fixup the visuals after.
     */

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

    /* Setup the visuals we support. */

    /* All MGA support DirectColor and can do overlays in 32bpp */
    if(pMga->Overlay8Plus24 && (pScrn->bitsPerPixel == 32)) {
	if (!miSetVisualTypes(8, PseudoColorMask | GrayScaleMask,
			      pScrn->rgbBits, PseudoColor))
		return FALSE;
	if (!miSetVisualTypes(24, TrueColorMask, pScrn->rgbBits, TrueColor))
		return FALSE;
    } else if (pMga->SecondCrtc) {
	/* No DirectColor on the second head */
	if (!miSetVisualTypes(pScrn->depth, TrueColorMask, pScrn->rgbBits,
			      TrueColor))
		return FALSE;
	if (!miSetPixmapDepths ())
	    return FALSE;
    } else {
	if (!xf86SetDefaultVisual(pScrn, -1))
	    return FALSE;

	if (!miSetVisualTypes(pScrn->depth,
			      miGetDefaultVisualMask(pScrn->depth),
			      pScrn->rgbBits, pScrn->defaultVisual))
	    return FALSE;
	if (!miSetPixmapDepths ())
	    return FALSE;
    }

    /*
     * Call the framebuffer layer's ScreenInit function, and fill in other
     * pScreen fields.
     */


    width = pScrn->virtualX;
    height = pScrn->virtualY;
    displayWidth = pScrn->displayWidth;


    if(pMga->Rotate) {
	height = pScrn->virtualX;
	width = pScrn->virtualY;
    }

    if(pMga->ShadowFB) {
 	pMga->ShadowPitch = BitmapBytePad(pScrn->bitsPerPixel * width);
	pMga->ShadowPtr = xalloc(pMga->ShadowPitch * height);
	displayWidth = pMga->ShadowPitch / (pScrn->bitsPerPixel >> 3);
        FBStart = pMga->ShadowPtr;
    } else {
	pMga->ShadowPtr = NULL;
	FBStart = pMga->FbStart;
    }

#ifdef XF86DRI
     /*
      * Setup DRI after visuals have been established, but before cfbScreenInit
      * is called.   cfbScreenInit will eventually call into the drivers
      * InitGLXVisuals call back.
      * The DRI does not work when textured video is enabled at this time.
      */
    if ((pMga->Chipset == PCI_CHIP_MGAG200_SE_A_PCI) ||
	(pMga->Chipset == PCI_CHIP_MGAG200_SE_B_PCI)) {
	xf86DrvMsg(pScrn->scrnIndex, X_WARNING,
		   "Not supported by hardware, not initializing the DRI\n");
	pMga->directRenderingEnabled = FALSE;
	driFrom = X_PROBED;
    } else if (!xf86ReturnOptValBool(pMga->Options, OPTION_DRI, TRUE)) {
	driFrom = X_CONFIG;
    } else if ( pMga->NoAccel ) {
       xf86DrvMsg( pScrn->scrnIndex, X_ERROR,
		   "Acceleration disabled, not initializing the DRI\n" );
       pMga->directRenderingEnabled = FALSE;
       driFrom = X_CONFIG;
    }
    else if ( pMga->TexturedVideo == TRUE ) {
       xf86DrvMsg( pScrn->scrnIndex, X_ERROR,
		   "Textured video enabled, not initializing the DRI\n" );
       pMga->directRenderingEnabled = FALSE;
       driFrom = X_CONFIG;
    }
    else if (pMga->SecondCrtc == TRUE) {
       xf86DrvMsg( pScrn->scrnIndex, X_ERROR,
		   "Not initializing the DRI on the second head\n" );
       pMga->directRenderingEnabled = FALSE;
    }
    else if ((pMga->FbMapSize /
	       (width * (pScrn->bitsPerPixel >> 3))) <= height * 3) {
       xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
	  "Static buffer allocation failed, not initializing the DRI\n");
       xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
	  "Need at least %d kB video memory at this resolution, bit depth\n",
	  (3 * displayWidth * height * (pScrn->bitsPerPixel >> 3)) / 1024 );
       pMga->directRenderingEnabled = FALSE;
       driFrom = X_PROBED;
    }
    else {
       pMga->directRenderingEnabled = MGADRIScreenInit(pScreen);
    }
#endif


    if (pMga->Overlay8Plus24) {
	ret = cfb8_32ScreenInit(pScreen, FBStart,
			width, height,
			pScrn->xDpi, pScrn->yDpi,
			displayWidth);
    } else {
	ret = fbScreenInit(pScreen, FBStart, width, height,
			   pScrn->xDpi, pScrn->yDpi,
			   displayWidth, pScrn->bitsPerPixel);
    }

    if (!ret)
	return FALSE;


    if (pScrn->bitsPerPixel > 8) {
        /* Fixup RGB ordering */
        visual = pScreen->visuals + pScreen->numVisuals;
        while (--visual >= pScreen->visuals) {
	    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 */
    if (!pMga->Overlay8Plus24)
	fbPictureInit (pScreen, 0, 0);
    
    xf86SetBlackWhitePixels(pScreen);

    pMga->BlockHandler = pScreen->BlockHandler;
    pScreen->BlockHandler = MGABlockHandler;

    if(!pMga->ShadowFB) /* hardware cursor needs to wrap this layer */
	MGADGAInit(pScreen);

    if (!pMga->NoAccel)
	MGAStormAccelInit(pScreen);

    miInitializeBackingStore(pScreen);
    xf86SetBackingStore(pScreen);
    xf86SetSilkenMouse(pScreen);

    /* Initialize software cursor.
	Must precede creation of the default colormap */
    miDCInitialize(pScreen, xf86GetPointerScreenFuncs());

    /* Initialize HW cursor layer.
	Must follow software cursor initialization*/
    if (pMga->HWCursor) {
	if(!MGAHWCursorInit(pScreen))
	    xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
		"Hardware cursor initialization failed\n");
    }
    if(pMga->MergedFB) {
        /* Rotate and MergedFB are mutiualy exclusive, so we can use this 
         * variable.
         */
        if (!pMga->PointerMoved) 
            pMga->PointerMoved = pScrn->PointerMoved;
        pScrn->PointerMoved = MGAMergePointerMoved; 
        
    }

    /* Initialise default colourmap */
    if (!miCreateDefColormap(pScreen))
	return FALSE;

    /* Initialize colormap layer.
	Must follow initialization of the default colormap */
    if (!pMga->SecondCrtc)
	f = CMAP_PALETTED_TRUECOLOR | CMAP_RELOAD_ON_MODE_SWITCH;
    else
	f = CMAP_RELOAD_ON_MODE_SWITCH;
    if(!xf86HandleColormaps(pScreen, 256, 8,
	pMga->FBDev ? fbdevHWLoadPaletteWeak() : MGAdac->LoadPalette,
	NULL, f))
	return FALSE;

    if(pMga->Overlay8Plus24) { /* Must come after colormap initialization */
	if(!xf86Overlay8Plus32Init(pScreen))
	    return FALSE;
    }

    if(pMga->ShadowFB) {
	RefreshAreaFuncPtr refreshArea = MGARefreshArea;

	if(pMga->Rotate) {
	    if (!pMga->PointerMoved) {
	    pMga->PointerMoved = pScrn->PointerMoved;
	    pScrn->PointerMoved = MGAPointerMoved;
	    }

	   switch(pScrn->bitsPerPixel) {
	   case 8:	refreshArea = MGARefreshArea8;	break;
	   case 16:	refreshArea = MGARefreshArea16;	break;
	   case 24:	refreshArea = MGARefreshArea24;	break;
	   case 32:	refreshArea = MGARefreshArea32;	break;
	   }
	}

	ShadowFBInit(pScreen, refreshArea);
    }

    if(pMga->SecondCrtc == TRUE && !pMga->MergedFB) { 
        xf86DPMSInit(pScreen, MGADisplayPowerManagementSetCrtc2, 0);
    } 
    if(pMga->SecondCrtc == FALSE && !pMga->MergedFB) {
        xf86DPMSInit(pScreen, MGADisplayPowerManagementSet, 0);
    }
    if(pMga->MergedFB) {
        xf86DPMSInit(pScreen, MGADisplayPowerManagementSetMerged, 0);
    }
    
    pScrn->memPhysBase = pMga->FbAddress;
    pScrn->fbOffset = pMga->YDstOrg * (pScrn->bitsPerPixel / 8);

    if(!pMga->MergedFB) {
        if(pMga->SecondCrtc == TRUE) { 
    	    pScreen->SaveScreen = MGASaveScreenCrtc2;
        } else {
            pScreen->SaveScreen = MGASaveScreen;
        }
    } else { /* Merged FB */
        pScreen->SaveScreen = MGASaveScreenMerged;
    }

    MGAInitVideo(pScreen);

#ifdef XF86DRI
    if (pMga->directRenderingEnabled) {
       /* Now that mi, cfb, drm and others have done their thing,
	* complete the DRI setup.
	*/
       pMga->directRenderingEnabled = MGADRIFinishScreenInit(pScreen);
    }
    if (pMga->directRenderingEnabled) {
        xf86DrvMsg(pScrn->scrnIndex, X_INFO, "Direct rendering enabled\n");
    } else {
        xf86DrvMsg(pScrn->scrnIndex, X_WARNING, "Direct rendering disabled\n");
    }
    if (pMga->DualHeadEnabled && pMga->SecondCrtc == FALSE)
	pMgaEnt->directRenderingEnabled = pMga->directRenderingEnabled;
    pMga->haveQuiescense = 1;
#endif

    /* Wrap the current CloseScreen function */
    pMga->CloseScreen = pScreen->CloseScreen;
    pScreen->CloseScreen = MGACloseScreen;

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

    /* For the second head, work around display problem. */
    if (!pMga->MergedFB && pMga->SecondCrtc) {
	MGACrtc2FillStrip(pScrn);
    }

    /* Done */
    return TRUE;
}

The main MGAScreenInit() parts are the following:

I

	if (!MGAMapMem(pScrn))
	    return FALSE;

MGAMapMem() is implemented as:

/*
 * Map the framebuffer and MMIO memory.
 */

static Bool
MGAMapMem(ScrnInfoPtr pScrn)
{
    MGAPtr pMga;

    pMga = MGAPTR(pScrn);

    /*
     * Map IO registers to virtual address space
     */
    /*
     * For Alpha, we need to map SPARSE memory, since we need
     * byte/short access.  This is taken care of automatically by the
     * os-support layer.
     */
    pMga->IOBase = xf86MapPciMem(pScrn->scrnIndex,
				 VIDMEM_MMIO | VIDMEM_READSIDEEFFECT,
				 pMga->PciTag, pMga->IOAddress, 0x4000);
    if (pMga->IOBase == NULL)
	return FALSE;

    pMga->FbBase = xf86MapPciMem(pScrn->scrnIndex, VIDMEM_FRAMEBUFFER,
				 pMga->PciTag, pMga->FbAddress,
				 pMga->FbMapSize);
    if (pMga->FbBase == NULL)
	return FALSE;


    pMga->FbStart = pMga->FbBase + pMga->YDstOrg * (pScrn->bitsPerPixel / 8);

    /* Map the ILOAD transfer window if there is one.  We only make
	DWORD access on DWORD boundaries to this window */
    if (pMga->ILOADAddress) {
	pMga->ILOADBase = xf86MapPciMem(pScrn->scrnIndex,
				VIDMEM_MMIO | VIDMEM_MMIO_32BIT |
				    VIDMEM_READSIDEEFFECT,
				pMga->PciTag, pMga->ILOADAddress, 0x800000);
    } else
	pMga->ILOADBase = NULL;

    return TRUE;
}

Mga->FbStart is equal to pMga->FbBase since YDstOrg (the offset in bytes from video start to usable memory) is usually zero (see comment in MGAPreInit()).

Macros VIDMEM_MMIO, VIDMEM_FRAMEBUFFER, VIDMEM_READSIDEEFFECT, VIDMEM_MMIO_32BIT are defined in xf86_OSproc.h:

/*
 * Flags for xf86MapVidMem().  Multiple flags can be or'd together.  The
 * flags may be used as hints.  For example it would be permissible to
 * enable write combining for memory marked only for framebuffer use.
 */

#define VIDMEM_FRAMEBUFFER	0x01	/* memory for framebuffer use */
#define VIDMEM_MMIO		0x02	/* memory for I/O use */
#define VIDMEM_MMIO_32BIT	0x04	/* memory accesses >= 32bit */
#define VIDMEM_READSIDEEFFECT	0x08	/* reads can have side-effects */
#define VIDMEM_SPARSE		0x10	/* sparse mapping required
					 * assumed when VIDMEM_MMIO is
					 * set. May be used with
					 * VIDMEM_FRAMEBUFFER) */
#define VIDMEM_READONLY		0x20	/* read-only mapping
					 * used when reading BIOS images
					 * through xf86MapVidMem() */

xf86MapPciMem() is implemented as:

pointer
xf86MapPciMem(int ScreenNum, int Flags, PCITAG Tag, ADDRESS Base,
		unsigned long Size)
{
	ADDRESS hostbase = pciBusAddrToHostAddr(Tag, PCI_MEM,Base);
	pointer base;
	CARD32 save = 0;

	/*
	 * If there are possible read side-effects, disable memory while
	 * doing the mapping.
	 */
	if (Flags & VIDMEM_READSIDEEFFECT) {
		save = pciReadLong(Tag, PCI_CMD_STAT_REG);
		pciWriteLong(Tag, PCI_CMD_STAT_REG,
			     save & ~PCI_CMD_MEM_ENABLE);
	}
	base = xf86MapDomainMemory(ScreenNum, Flags, Tag, hostbase, Size);
	if (!base)	{
		FatalError("xf86MapPciMem: Could not mmap PCI memory "
			   "[base=0x%lx,hostbase=0x%lx,size=%lx] (%s)\n",
			   Base, hostbase, Size, strerror(errno));
	}
	/*
	 * If read side-effects, do whatever might be needed to prevent
	 * unintended reads, then restore PCI_CMD_STAT_REG.
	 */
	if (Flags & VIDMEM_READSIDEEFFECT) {
		xf86MapReadSideEffects(ScreenNum, Flags, base, Size);
		pciWriteLong(Tag, PCI_CMD_STAT_REG, save);
	}
	return((pointer)base);
}

xf86MapPciMem() mainly calls xf86MapDomainMemory() as:

base = xf86MapDomainMemory(ScreenNum, Flags, Tag, hostbase, Size);

How xf86MapDomainMemory() works

xf86MapDomainMemory() is an encapsulation of xf86MapVidMem().

The virtual base (vbase) is derived from the physical base (Base) in xf86MapVidMem() as:

vbase = vidMemInfo.mapMem(ScreenNum, Base, Size, Flags);

vidMemInfo.mapMem is initialised by xf86InitVidMem(), which calls xf86OSInitVidMem() as:

	if (!vidMemInfo.initialised) {
		memset(&vidMemInfo, 0, sizeof(VidMemInfo));
		xf86OSInitVidMem(&vidMemInfo);

xf86OSInitVidMem() is an OS specific routine, for instance the Linux implementation has this xf86OSInitVidMem().

This routine sets as mapMem() the mapVidMem():

pVidMem->mapMem = mapVidMem;

mapVidMem() mainly does the following:

It opens the DEV_MEM device:

    fd = open(DEV_MEM,
	      ((flags & VIDMEM_READONLY) ? O_RDONLY : O_RDWR) | O_SYNC);
then it mapps the memory:
    base = mmap((caddr_t)0, Size + alignOff, prot, mapflags, fd,
 		(off_t)realBase  + BUS_BASE);

and then closes the DEV_MEM device:
    close(fd);
For the mmap() Linux system call we read from its man page:

The mmap() function asks to map length bytes starting at offset offset from the file (or other object) specified by the file descriptor fd into memory, preferably at address start. This latter address is a hint only, and is usually specified as 0. The actual place where the object is mapped is returned by mmap(). In our case the 'file' is the DEV_MEM device, which is the raw interface to RAM.

What is mapping?

A nice definition comes from Rubini's book "Linux Device Drivers":

Mapping a device means associating a range of user-space addresses to device memory. Whenever the program reads or writes in the assigned address range, it is actually accessing the device. In the X server example, using mmap allows quick and easy access to the video card's memory. For a performance-critical application like this, direct access makes a large difference.

The PCI part:

    /*
     * Map IO registers to virtual address space

     */

    pMga->IOBase = xf86MapPciMem(pScrn->scrnIndex,
				 VIDMEM_MMIO | VIDMEM_READSIDEEFFECT,
				 pMga->PciTag, pMga->IOAddress, 0x4000);

From this README we find the following definition:
IOBase   - physical address of the memory mapped IO registers

pMga->IOAddress was filled previously in MGAPreInit() as:

	if (pMga->PciInfo->memBase[i] != 0) {
	    pMga->IOAddress = pMga->PciInfo->memBase[i] & 0xffffc000;

pMga->PciInfo was returned by xf86GetPciInfoForEntity(), previously in MGAPreInit() as:

    /* Find the PCI info for this screen */
    pMga->PciInfo = xf86GetPciInfoForEntity(pMga->pEnt->index);
xf86GetPciInfoForEntity() is implemented as:

/*
 * xf86GetPciInfoForEntity() -- Get the pciVideoRec of entity.
 */
pciVideoPtr
xf86GetPciInfoForEntity(int entityIndex)
{
    pciVideoPtr *ppPci;
    EntityPtr p;
    
    if (entityIndex >= xf86NumEntities)
	return NULL;

    p = xf86Entities[entityIndex];
    if (p->busType != BUS_PCI)
	return NULL;
    
    for (ppPci = xf86PciVideoInfo; *ppPci != NULL; ppPci++) {
	if (p->pciBusId.bus == (*ppPci)->bus &&
	    p->pciBusId.device == (*ppPci)->device &&
	    p->pciBusId.func == (*ppPci)->func) 
	    return (*ppPci);
    }
    return NULL;
}

xf86GetPciInfoForEntity() locates the xf86PciVideoInfo[] entry with the bus, device, and func that corresponds to the xf86Entities[] entry with the index passed as argument in the current routine.

The pMga->PciInfo->memBase[i] is then derived and it is masked with 0xffffc000 as:

pMga->IOAddress = pMga->PciInfo->memBase[i] & 0xffffc000;

Why is the memBase masked?

Hex value ffffc000 is binary 11111111111111111100000000000000 and this masks off the lower bits. The value of zeroes if turned to ones, 11111111111111, is 16383 decimal and this staring from zero gives 16K. The masking therefore aligns the physical base address to start to a border of 16K for better manipulation from the system since 16K is the size of the memory IO area. Also from the same register memBase[i] more than one base address are derived. The size is indeed 16K or 0x4000 in hex and this is given from the following instruction that comes latter in the source and maps the memory:

    pMga->IOBase = xf86MapPciMem(pScrn->scrnIndex,
				 VIDMEM_MMIO | VIDMEM_READSIDEEFFECT,
				 pMga->PciTag, pMga->IOAddress, 0x4000);
For the previous see this link and Graphic Stack Overview.

Notice that as seen in mga.h PciInfo is of type pciVideoPtr:

typedef struct {
    int			vendor;
    int			chipType;
    int			chipRev;
    int			subsysVendor;
    int			subsysCard;
    int			bus;
    int			device;
    int			func;
    int			class;
    int			subclass;
    int			interface;
    memType  	        memBase[6];
    memType  	        ioBase[6];
    int			size[6];
    unsigned char	type[6];
    memType   	        biosBase;
    int			biosSize;
    pointer		thisCard;
    Bool                validSize;
    Bool                validate;
    CARD32              listed_class;
} pciVideoRec, *pciVideoPtr;


/*
 * memType is of the size of the addressable memory (machine size)
 * usually unsigned long.
 */
typedef unsigned long memType;

pMga->PciInfo was filled in InitOutput() => xf86BusProbe() => xf86PciProbe() => FindPCIVideoInfo().

memBase[i] is either the first or the second memBase according to the MGA Chipset (see MGAPreInit). For more info on the PCI configuration space and memBase see figure 2 of the osdev wiki.

The frame buffer address is similarily with IO address masked with 0xff800000 since the address region it occupies is 8M:

	if (pMga->PciInfo->memBase[i] != 0) {
	    pMga->FbAddress = pMga->PciInfo->memBase[i] & 0xff800000;

and then the framebuffer base is given by:

    pMga->FbBase = xf86MapPciMem(pScrn->scrnIndex, VIDMEM_FRAMEBUFFER,
				 pMga->PciTag, pMga->FbAddress,
				 pMga->FbMapSize);

How comes that memBase[i] provides except the IOBase (MMIO) also the framebuffer base?

Consider for instance the case from this link:


(--) MGA(0): Linear framebuffer at 0xDC000000
(==) MGA(0): MMIO registers at 0xDFEFC000
(--) MGA(0): BIOS at 0xDFEC0000
The previous three hex numbers in binary are translated as:
Linear framebuffer 11011100000000000000000000000000
MMIO registers at  11011111111011111100000000000000
BIOS at            11011111111011000000000000000000
Recall that framebuffer is masked with 0xff800000. This is binary value:
11111111100000000000000000000000
This masks off the lower 23 bits. On the other hand the MMIO base is masked with 0xffffc000 which is binary value
1111111111111100000000000000
This masks off the lower 14 bits. In other words the MMIO is placed in a higher memory from framebuffer (although not much) and this causes no conflict.

Examples:

See also this link for a MMIO definition and a memory range figure.

At the next exaple found at this link:

matroxfb: framebuffer at 0xF4000000, mapped to 0xd0805000, size 16777216

we see the both the Physical address (0xF4000000) and then the Virtual address (0xd0805000) of the framebuffer as mapped by the system.

A similar example from Rubini'sLinux Device Drivers book:

The full list of the X server's VMAs is lengthy, but most of the entries are not of interest here. We do see, however, three separate mappings of /dev/mem, which give some insight into how the X server works with the video card. The first mapping shows a 16 KB region mapped at fe2fc000. This address is far above the highest RAM address on the system; it is, instead, a region of memory on a PCI peripheral (the video card). It will be a control region for that card. The middle mapping is at a0000, which is the standard location for video RAM in the 640 KB ISA hole. The last /dev/memmapping is a rather larger one at f4000000 and is the video memory itself. These regions can also be seen in /proc/iomem:
000a0000-000bffff : Video RAM area
f4000000-f4ffffff : Matrox Graphics, Inc. MGA G200 AGP
fe2fc000-fe2fffff : Matrox Graphics, Inc. MGA G200 AGP
Mapping a device means associating a range of user-space addresses to device memory. Whenever the program reads or writes in the assigned address range, it is actually accessing the device. In the X server example, using mmap allows quick and easy access to the video card's memory. For a performance-critical application like this, direct access makes a large difference.

How the video RAM area relates to video memory?

The video RAM area (0xa0000-0xbffff) was used in the VGA standard, at the past, when 64K of video memory was more than enough. New stnadards like SVGA appeared to cover the need for more video memory.

From Guide: VESA graphics modes we read:

The SVGA video memory is located at physical address 0xA0000, the same as in mode 13h, but there is one small problem with this: there simply isn't enough room for it all to fit there! The original DOS memory map only included space for 64k of video memory between 0xA0000 and 0xB0000, which is fine for a 320x200 resolution but nowhere near enough for a 640x480 screen (that takes up 300k of framebuffer space, and higher resolutions need even more). The SVGA hardware designers solved this problem by using a banked memory architecture, where the 64k VGA memory region is treated as a sliding window onto the larger expanse of real video memory inside your card. To access an arbitrary location on the SVGA screen you must first call VESA function 0x4F05 to tell it which bank you want to use, and then write to a memory location within that bank.

Other standards followed SVGA.

In this text instead of accessing the video via the legacy VGA-compatible I/O ports we examine the PCI MMIO I/O paradigm (using the area f4000000-f4ffffff in the previous example), which is used by contemporary video cards.

What about 000a0000-000bffff, the other memory address region?

This region dedicated to the mapping of the VGA cards video memory. New video cards do not make use of this region since they require more space. See FreeVGA.

II

    /* Initialise the MMIO vgahw functions */
    vgaHWSetMmioFuncs(hwp, pMga->IOBase, PORT_OFFSET);
    vgaHWGetIOBase(hwp);

vgaHWSetMmioFuncs() is implemented as:

void
vgaHWSetMmioFuncs(vgaHWPtr hwp, CARD8 *base, int offset)
{
    hwp->writeCrtc		= mmioWriteCrtc;
    hwp->readCrtc		= mmioReadCrtc;
    hwp->writeGr		= mmioWriteGr;
    hwp->readGr			= mmioReadGr;
    hwp->readST00               = mmioReadST00;
    hwp->readST01               = mmioReadST01;
    hwp->readFCR                = mmioReadFCR;
    hwp->writeFCR               = mmioWriteFCR;
    hwp->writeAttr		= mmioWriteAttr;
    hwp->readAttr		= mmioReadAttr;
    hwp->writeSeq		= mmioWriteSeq;
    hwp->readSeq		= mmioReadSeq;
    hwp->writeMiscOut		= mmioWriteMiscOut;
    hwp->readMiscOut		= mmioReadMiscOut;
    hwp->enablePalette		= mmioEnablePalette;
    hwp->disablePalette		= mmioDisablePalette;
    hwp->writeDacMask		= mmioWriteDacMask;
    hwp->readDacMask		= mmioReadDacMask;
    hwp->writeDacWriteAddr	= mmioWriteDacWriteAddr;
    hwp->writeDacReadAddr	= mmioWriteDacReadAddr;
    hwp->writeDacData		= mmioWriteDacData;
    hwp->readDacData		= mmioReadDacData;
    hwp->MMIOBase		= base;
    hwp->MMIOOffset		= offset;
    hwp->readEnable		= mmioReadEnable;
    hwp->writeEnable		= mmioWriteEnable;
}

After the pMga->IOBase the virtual address of the memory mapped IO registers is calculated it is used as argument to vgaHWSetMmioFuncs(). For this routine we read from the DESIGN document we read:

     void vgaHWSetMmioFuncs(vgaHWPtr hwp, CARD8 *base, int offset)

          This function initialised the register access function
          fields of hwp with a generic MMIO set of functions.
          hwp->MMIOBase is initialised with base, which must be the
          virtual address that the start of MMIO area is mapped to.

vgaHWGetIOBase() is implemented as:

void
vgaHWGetIOBase(vgaHWPtr hwp)
{
    hwp->IOBase = (hwp->readMiscOut(hwp) & 0x01) ?
				VGA_IOBASE_COLOR : VGA_IOBASE_MONO;
    xf86DrvMsgVerb(hwp->pScrn->scrnIndex, X_INFO, 3,
	"vgaHWGetIOBase: hwp->IOBase is 0x%04x, hwp->PIOOffset is 0x%04lx\n",
	hwp->IOBase, hwp->PIOOffset);
}

The macros that participate in vgaHWGetIOBase() are defined in vgaHW.h as:

#define VGA_IOBASE_MONO		0x3B0
#define VGA_IOBASE_COLOR	0x3D0

    /* Map the VGA memory when the primary video */
    if (pMga->Primary && !pMga->FBDev) {
	hwp->MapSize = 0x10000;
	if (!vgaHWMapMem(pScrn))
	    return FALSE;
    }

vgaHWMapMem() is implemented as:

Bool
vgaHWMapMem(ScrnInfoPtr scrp)
{
    vgaHWPtr hwp = VGAHWPTR(scrp);
    int scr_index = scrp->scrnIndex;
    
    if (hwp->Base)
	return TRUE;

    /* If not set, initialise with the defaults */
    if (hwp->MapSize == 0)
	hwp->MapSize = VGA_DEFAULT_MEM_SIZE;
    if (hwp->MapPhys == 0)
	hwp->MapPhys = VGA_DEFAULT_PHYS_ADDR;

    /*
     * Map as VIDMEM_MMIO_32BIT because WC
     * is bad when there is page flipping.
     * XXX This is not correct but we do it
     * for now.
     */
#ifdef DEBUG
    ErrorF("Mapping VGAMem\n");
#endif
    hwp->Base = xf86MapDomainMemory(scr_index, VIDMEM_MMIO_32BIT, hwp->Tag,
				    hwp->MapPhys, hwp->MapSize);
    return hwp->Base != NULL;
}

xf86MapDomainMemory(), described previously, is used this time to map the memory at the VGA_DEFAULT_PHYS_ADDR physical address. This macro along with the memory size (VGA_DEFAULT_MEM_SIZE) are defined in vgaHW.h as:

/* Defaults for the VGA memory window */
#define VGA_DEFAULT_PHYS_ADDR	0xA0000
#define VGA_DEFAULT_MEM_SIZE	(64 * 1024)

As we read from Video Graphics Array (VGA):

The video memory of the VGA is mapped to the PC's memory via a window in the range between segments 0xA0000 and 0xBFFFF in the PC's real mode address space (A000:0000 and B000:FFFF in segment:offset notation). Typically these starting segments are:
0xA0000 for EGA/VGA graphics modes (64 KB) 
0xB0000 for monochrome text mode (32 KB) 
0xB8000 for color text mode and CGA-compatible graphics modes (32 KB) 

The mapping in the code follows the VGA graphics mode.

An example from VGA Programming, using VGA 320x200 resolution:

In mode 13h, the way this memory corresponds to the screen is simple. Each pixel on the screen has one byte which corresponds to it (hence 256 colours). There are 320 columns and 200 rows, which gives a total size of 64000 bytes to describe the whole screen. The layout is completely linear, so I'll just give a few examples. The top left pixel is at offset 0 in the video memory. The pixel on the top row and in the 50th column is at offset 49. The last pixel on the first row is at offset 319. The first pixel on the 2nd row is at offset 320 and so on.

So it should be fairly obvious that the formula for the offset of a pixel at coordinates (x,y) is given by (320 * y) + x.

see also Start VGA programming.

How does the video memory gets mapped

Memory mapping for the Intel systems is handled by the Northbridge, also known as a memory controller hub.

At Motherboard Chipsets and the Memory Map we read:

When the northbridge receives a physical memory request it decides where to route it: should it go to RAM? Video card maybe? This routing is decided via the memory address map. For each region of physical memory addresses, the memory map knows the device that owns that region. The bulk of the addresses are mapped to RAM, but when they aren't the memory map tells the chipset which device should service requests for those addresses.

III

if (pMga->Overlay8Plus24) { ret = cfb8_32ScreenInit(pScreen, FBStart, width, height, pScrn->xDpi, pScrn->yDpi, displayWidth); } else { ret = fbScreenInit(pScreen, FBStart, width, height, pScrn->xDpi, pScrn->yDpi, displayWidth, pScrn->bitsPerPixel); } if (!ret) return FALSE;

Since for the current example the "Overlay" option was not present in xorg.conf the pMga->Overlay8Plus24 field was not set by MGAPreInit() (The condition s = xf86GetOptValString(pMga->Options, OPTION_OVERLAY) returned FALSE). Therefore the fbScreenInit() continues the further 'ScreenInit' process. We examine fbScreenInit() at section 3.2.2.12.

A look at the fbScreenInit() parameters

pScreen is the pointer to the current ScreenRec (see section 3.2.2.6)

FBStart, as seen previously, is the virtual address of the framebuffer.

displayWidth is the "memory pitch" as the comment denotes in _ScrnInfoRec struct (see section 3.2.2.6). Also as we read in How Video Cards Work pitch is the stride, that is the width of the buffer in bytes. For example, if you have a 1024x768 pixel buffer at 16 bits/pixel (2 bytes/pixel), your stride would be:

1024 pixels * 2 bytes/pixel = 2048 bytes

xDpi and yDpi are the width DPI and height DPI respectivelly. From the Xserver man page we read:

-dpi resolution
sets the resolution for all screens, in dots per inch. To be used when the server cannot determine the screen size(s) from the hardware.

width is pScrn->virtualX and height is pScrn->virtualY. Again from section 3.2.2.6 we see that those are the Virtual width and Virtual height respectively. From we find that a virtual screen is " a screen that is bigger than your desktop and you can use your mouse to 'pan' thru it."

pScrn->bitsPerPixel is the framebuffer's depth

NOTES:

Red color mark the origin of a fbScreenInit() argument.

REFERENCES

Accessing the VGA Display Memory
Graphic Stack Overview
DESIGN document
Open Source 3D acceleration for nVidia cards
Hardware Level VGA and SVGA Video Programming Information Page
Video Graphics Array (VGA)
FreeVGA
The video card
Memory Hole in Large Memory X86 Based Systems