Hands-on Projects for the Linux Graphics Subsystem
|
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:
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:
vidMemInfo.mapMem is initialised by xf86InitVidMem(), which calls xf86OSInitVidMem() as:
xf86OSInitVidMem() is an OS specific routine, for instance the Linux implementation has this xf86OSInitVidMem(). This routine sets as mapMem() the 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.
|
The PCI part:
pMga->IOAddress was filled previously in MGAPreInit() as:
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() 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;
Notice that as seen in mga.h PciInfo is of type pciVideoPtr:
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:
and then the framebuffer base is given by:
|
/* 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. |
|
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() parameterspScreen 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 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