/* * Driver for CL-GD546x -- The Laguna family * * lg_driver.c * * (c) 1998 Corin Anderson. * corina@the4cs.com * Tukwila, WA * * This driver is derived from the cir_driver.c module. * Original authors and contributors list include: * Radoslaw Kapitan, Andrew Vanderstock, Dirk Hohndel, * David Dawes, Andrew E. Mileski, Leonard N. Zubkoff, * Guy DESBIEF, Itai Nahshon. */ /* $XFree86$ */ #define EXPERIMENTAL /* All drivers should typically include these */ #include "xf86.h" #include "xf86_OSproc.h" /* All drivers need this */ #include "xf86_ansic.h" #include "compiler.h" /* Drivers for PCI hardware need this */ #include "xf86PciInfo.h" /* Drivers that need to access the PCI config space directly need this */ #include "xf86Pci.h" /* All drivers using the vgahw module need this */ /* This driver needs to be modified to not use vgaHW for multihead operation */ #include "vgaHW.h" #include "xf86RAC.h" #include "xf86Resources.h" /* All drivers initialising the SW cursor need this */ #include "mipointer.h" /* All drivers implementing backing store need this */ #include "mibstore.h" #include "micmap.h" /* Needed by the Shadow Framebuffer */ #include "shadowfb.h" #include "xf86int10.h" #include "fb.h" #include "xf86DDC.h" #undef LG_DEBUG #include "cir.h" #define _LG_PRIVATE_ #include "lg.h" #include "xf86xv.h" #include "Xv.h" /* * Forward definitions for the functions that make up the driver. */ /* Mandatory functions */ Bool LgPreInit(ScrnInfoPtr pScrn, int flags); Bool LgScreenInit(int Index, ScreenPtr pScreen, int argc, char **argv); Bool LgEnterVT(int scrnIndex, int flags); void LgLeaveVT(int scrnIndex, int flags); static Bool LgCloseScreen(int scrnIndex, ScreenPtr pScreen); static Bool LgSaveScreen(ScreenPtr pScreen, Bool mode); /* Required if the driver supports mode switching */ Bool LgSwitchMode(int scrnIndex, DisplayModePtr mode, int flags); /* Required if the driver supports moving the viewport */ void LgAdjustFrame(int scrnIndex, int x, int y, int flags); /* Optional functions */ void LgFreeScreen(int scrnIndex, int flags); ModeStatus LgValidMode(int scrnIndex, DisplayModePtr mode, Bool verbose, int flags); /* Internally used functions */ static void LgRestoreLgRegs(ScrnInfoPtr pScrn, LgRegPtr lgReg); static int LgFindLineData(int displayWidth, int bpp); static CARD16 LgSetClock(CirPtr pCir, vgaHWPtr hwp, int freq); static void lg_vgaHWSetMmioFunc(vgaHWPtr hwp, CARD8 *base); static void LgDisplayPowerManagementSet(ScrnInfoPtr pScrn, int PowerManagementMode, int flags); /* * This is intentionally screen-independent. It indicates the binding * choice made in the first PreInit. */ static int pix24bpp = 0; /* * This contains the functions needed by the server after loading the * driver module. It must be supplied, and gets added the driver list by * the Module Setup funtion in the dynamic case. In the static case a * reference to this is compiled in, and this requires that the name of * this DriverRec be an upper-case version of the driver name. */ typedef enum { OPTION_HW_CURSOR, OPTION_PCI_RETRY, OPTION_ROTATE, OPTION_SHADOW_FB, OPTION_NOACCEL } LgOpts; static const OptionInfoRec LgOptions[] = { { OPTION_HW_CURSOR, "HWcursor", OPTV_BOOLEAN, {0}, FALSE }, { OPTION_NOACCEL, "NoAccel", OPTV_BOOLEAN, {0}, FALSE }, { OPTION_SHADOW_FB, "ShadowFB", OPTV_BOOLEAN, {0}, FALSE }, { OPTION_ROTATE, "Rotate", OPTV_ANYSTR, {0}, FALSE }, /* fifo_conservative/aggressive; fast/med/slow_dram; ... */ { -1, NULL, OPTV_NONE, {0}, FALSE } }; /* 1/4bpp 8bpp 15/16bpp 24bpp 32bpp */ static int gd5462_MaxClocks[] = { 170000, 170000, 135100, 135100, 85500 }; static int gd5464_MaxClocks[] = { 170000, 250000, 170000, 170000, 135100 }; static int gd5465_MaxClocks[] = { 170000, 250000, 170000, 170000, 135100 }; LgLineDataRec LgLineData[] = { { 5, 640, 0}, /* We're rather use skinny tiles, so put all of */ { 8, 1024, 0}, /* them at the head of the table */ {10, 1280, 0}, {13, 1664, 0}, {16, 2048, 0}, {20, 2560, 0}, {10, 2560, 1}, {26, 3328, 0}, { 5, 1280, 1}, { 8, 2048, 1}, {13, 3328, 1}, {16, 4096, 1}, {20, 5120, 1}, {26, 6656, 1}, {-1, -1, -1} /* Sentinal to indicate end of table */ }; static int LgLinePitches[4][11] = { /* 8 */ { 640, 1024, 1280, 1664, 2048, 2560, 3328, 4096, 5120, 6656, 0 }, /* 16 */ { 320, 512, 640, 832, 1024, 1280, 1664, 2048, 2560, 3328, 0 }, /* 24 */ { 213, 341, 426, 554, 682, 853, 1109, 1365, 1706, 2218, 0 }, /* 32 */ { 160, 256, 320, 416, 512, 640, 832, 1024, 1280, 1664, 0 } }; /* * List of symbols from other modules that this module references. This * list is used to tell the loader that it is OK for symbols here to be * unresolved providing that it hasn't been told that they haven't been * told that they are essential via a call to xf86LoaderReqSymbols() or * xf86LoaderReqSymLists(). The purpose is this is to avoid warnings about * unresolved symbols that are not required. */ static const char *vgahwSymbols[] = { "vgaHWFreeHWRec", "vgaHWGetHWRec", "vgaHWGetIOBase", "vgaHWGetIndex", "vgaHWHandleColormaps", "vgaHWInit", "vgaHWLock", "vgaHWMapMem", "vgaHWProtect", "vgaHWRestore", "vgaHWSave", "vgaHWSaveScreen", "vgaHWUnlock", NULL }; static const char *fbSymbols[] = { "fbScreenInit", "fbPictureInit", NULL }; static const char *xaaSymbols[] = { "XAACreateInfoRec", "XAADestroyInfoRec", "XAAInit", NULL }; static const char *ramdacSymbols[] = { "xf86CreateCursorInfoRec", "xf86DestroyCursorInfoRec", "xf86InitCursor", NULL }; #define LGuseI2C 1 static const char *ddcSymbols[] = { "xf86PrintEDID", #if LGuseI2C "xf86DoEDID_DDC2", #endif "xf86SetDDCproperties", NULL }; static const char *i2cSymbols[] = { "xf86CreateI2CBusRec", "xf86I2CBusInit", NULL }; static const char *int10Symbols[] = { "xf86FreeInt10", "xf86InitInt10", NULL }; static const char *shadowSymbols[] = { "ShadowFBInit", NULL }; #ifdef XFree86LOADER #define LG_MAJOR_VERSION 1 #define LG_MINOR_VERSION 0 #define LG_PATCHLEVEL 0 static MODULESETUPPROTO(lgSetup); static XF86ModuleVersionInfo lgVersRec = { "cirrus_laguna", MODULEVENDORSTRING, MODINFOSTRING1, MODINFOSTRING2, XF86_VERSION_CURRENT, LG_MAJOR_VERSION, LG_MINOR_VERSION, LG_PATCHLEVEL, ABI_CLASS_VIDEODRV, /* This is a video driver */ ABI_VIDEODRV_VERSION, MOD_CLASS_NONE, {0,0,0,0} }; /* * This is the module init data. * Its name has to be the driver name followed by ModuleData. */ XF86ModuleData cirrus_lagunaModuleData = { &lgVersRec, lgSetup, NULL }; static pointer lgSetup(pointer module, pointer opts, int *errmaj, int *errmin) { static Bool setupDone = FALSE; if (!setupDone) { setupDone = TRUE; LoaderRefSymLists(vgahwSymbols, fbSymbols, xaaSymbols, ramdacSymbols, ddcSymbols, i2cSymbols, int10Symbols, NULL); } return (pointer)1; } #endif /* XFree86LOADER */ const OptionInfoRec * LgAvailableOptions(int chipid) { return LgOptions; } ScrnInfoPtr LgProbe(int entity) { ScrnInfoPtr pScrn = NULL; if ((pScrn = xf86ConfigPciEntity(pScrn, 0, entity, CIRPciChipsets, NULL, NULL, NULL, NULL, NULL))) { pScrn->PreInit = LgPreInit; pScrn->ScreenInit = LgScreenInit; pScrn->SwitchMode = LgSwitchMode; pScrn->AdjustFrame = LgAdjustFrame; pScrn->EnterVT = LgEnterVT; pScrn->LeaveVT = LgLeaveVT; pScrn->FreeScreen = LgFreeScreen; pScrn->ValidMode = LgValidMode; } return pScrn; } static Bool LgGetRec(ScrnInfoPtr pScrn) { CirPtr pCir; if (pScrn->driverPrivate != NULL) return TRUE; pScrn->driverPrivate = xnfcalloc(sizeof(CirRec), 1); ((CirPtr)pScrn->driverPrivate)->chip.lg = xnfcalloc(sizeof(LgRec),1); /* Initialize it */ pCir = CIRPTR(pScrn); pCir->chip.lg->oldBitmask = 0x00000000; return TRUE; } static void LgFreeRec(ScrnInfoPtr pScrn) { if (pScrn->driverPrivate == NULL) return; xfree(pScrn->driverPrivate); pScrn->driverPrivate = NULL; } /* * LgCountRAM -- * * Counts amount of installed RAM */ /* XXX We need to get rid of this PIO (MArk) */ static int LgCountRam(ScrnInfoPtr pScrn) { vgaHWPtr hwp = VGAHWPTR(pScrn); CARD8 SR14; vgaHWProtect(pScrn, TRUE); /* The ROM BIOS scratchpad registers contain, among other things, the amount of installed RDRAM on the laguna chip. */ SR14 = hwp->readSeq(hwp, 0x14); ErrorF("Scratch Pads: 0:%02x 1:%02x 2:%02x 3:%02x\n", hwp->readSeq(hwp, 9), hwp->readSeq(hwp, 10), SR14, hwp->readSeq(hwp, 0x15)); vgaHWProtect(pScrn, FALSE); return 1024 * ((SR14&0x7) + 1); /* !!! This function seems to be incorrect... */ } static xf86MonPtr LgDoDDC(ScrnInfoPtr pScrn) { CirPtr pCir = CIRPTR(pScrn); xf86MonPtr MonInfo = NULL; /* Map the CIR memory and MMIO areas */ if (!CirMapMem(pCir, pScrn->scrnIndex)) return FALSE; #if LGuseI2C if (!LgI2CInit(pScrn)) { xf86DrvMsg(pScrn->scrnIndex, X_ERROR, "I2C initialization failed\n"); goto unmap_out; } /* Read and output monitor info using DDC2 over I2C bus */ MonInfo = xf86DoEDID_DDC2(pScrn->scrnIndex, pCir->I2CPtr1); xf86DrvMsg(pScrn->scrnIndex, X_INFO, "I2C Monitor info: %p\n", (void *)MonInfo); xf86PrintEDID(MonInfo); xf86DrvMsg(pScrn->scrnIndex, X_INFO, "end of I2C Monitor info\n\n"); #endif /* LGuseI2C */ xf86SetDDCproperties(pScrn, MonInfo); unmap_out: CirUnmapMem(pCir, pScrn->scrnIndex); return MonInfo; } /* Mandatory */ Bool LgPreInit(ScrnInfoPtr pScrn, int flags) { CirPtr pCir; vgaHWPtr hwp; MessageType from; int i; ClockRangePtr clockRanges; int fbPCIReg, ioPCIReg; char *s; if (flags & PROBE_DETECT) { cirProbeDDC( pScrn, xf86GetEntityInfo(pScrn->entityList[0])->index ); return TRUE; } #ifdef LG_DEBUG ErrorF("LgPreInit\n"); #endif /* Check the number of entities, and fail if it isn't one. */ if (pScrn->numEntities != 1) return FALSE; /* The vgahw module should be loaded here when needed */ if (!xf86LoadSubModule(pScrn, "vgahw")) return FALSE; xf86LoaderReqSymLists(vgahwSymbols, NULL); /* * Allocate a vgaHWRec */ if (!vgaHWGetHWRec(pScrn)) return FALSE; hwp = VGAHWPTR(pScrn); vgaHWGetIOBase(hwp); /* Allocate the LgRec driverPrivate */ if (!LgGetRec(pScrn)) return FALSE; pCir = CIRPTR(pScrn); pCir->pScrn = pScrn; pCir->PIOReg = hwp->PIOOffset + 0x3CE; /* Get the entity, and make sure it is PCI. */ pCir->pEnt = xf86GetEntityInfo(pScrn->entityList[0]); if (pCir->pEnt->location.type != BUS_PCI) return FALSE; pCir->Chipset = pCir->pEnt->chipset; /* Find the PCI info for this screen */ pCir->PciInfo = xf86GetPciInfoForEntity(pCir->pEnt->index); pCir->PciTag = pciTag(pCir->PciInfo->bus, pCir->PciInfo->device, pCir->PciInfo->func); if (xf86LoadSubModule(pScrn, "int10")) { xf86Int10InfoPtr int10InfoPtr; xf86LoaderReqSymLists(int10Symbols, NULL); int10InfoPtr = xf86InitInt10(pCir->pEnt->index); if (int10InfoPtr) xf86FreeInt10(int10InfoPtr); } /* Set pScrn->monitor */ pScrn->monitor = pScrn->confScreen->monitor; /* * The first thing we should figure out is the depth, bpp, etc. * We support both 24bpp and 32bpp layouts, so indicate that. */ if (!xf86SetDepthBpp(pScrn, 0, 0, 0, Support24bppFb | Support32bppFb | SupportConvert32to24 | PreferConvert32to24)) { return FALSE; } /* Check that the returned depth is one we support */ switch (pScrn->depth) { case 8: case 15: case 16: case 24: case 32: /* OK */ break; default: xf86DrvMsg(pScrn->scrnIndex, X_ERROR, "Given depth (%d) is not supported by this driver\n", pScrn->depth); return FALSE; } xf86PrintDepthBpp(pScrn); /* Get the depth24 pixmap format */ if (pScrn->depth == 24 && pix24bpp == 0) pix24bpp = xf86GetBppFromDepth(pScrn, 24); /* * This must happen after pScrn->display has been set because * xf86SetWeight references it. */ if (pScrn->depth > 8) { /* The defaults are OK for us */ rgb zeros = {0, 0, 0}; /* !!! I think we can force 5-6-5 weight for 16bpp here for the 5462. */ if (!xf86SetWeight(pScrn, zeros, zeros)) { return FALSE; } else { /* XXX check that weight returned is supported */ ; } } if (!xf86SetDefaultVisual(pScrn, -1)) return FALSE; /* Collect all of the relevant option flags (fill in pScrn->options) */ xf86CollectOptions(pScrn, NULL); /* Process the options */ if (!(pCir->Options = xalloc(sizeof(LgOptions)))) return FALSE; memcpy(pCir->Options, LgOptions, sizeof(LgOptions)); xf86ProcessOptions(pScrn->scrnIndex, pScrn->options, pCir->Options); pScrn->rgbBits = 6; from = X_DEFAULT; pCir->HWCursor = FALSE; if (xf86GetOptValBool(pCir->Options, OPTION_HW_CURSOR, &pCir->HWCursor)) from = X_CONFIG; xf86DrvMsg(pScrn->scrnIndex, from, "Using %s cursor\n", pCir->HWCursor ? "HW" : "SW"); if (xf86ReturnOptValBool(pCir->Options, OPTION_NOACCEL, FALSE)) { pCir->NoAccel = TRUE; xf86DrvMsg(pScrn->scrnIndex, X_CONFIG, "Acceleration disabled\n"); } if (pScrn->bitsPerPixel < 8) { xf86DrvMsg(pScrn->scrnIndex, X_ERROR, "Cannot use in less than 8 bpp\n"); return FALSE; } /* * Set the ChipRev, allowing config file entries to * override. */ if (pCir->pEnt->device->chipRev >= 0) { pCir->ChipRev = pCir->pEnt->device->chipRev; xf86DrvMsg(pScrn->scrnIndex, X_CONFIG, "ChipRev override: %d\n", pCir->ChipRev); } else { pCir->ChipRev = pCir->PciInfo->chipRev; } /* Cirrus swapped the FB and IO registers in the 5465 (by design). */ if (PCI_CHIP_GD5465 == pCir->Chipset) { fbPCIReg = 0; ioPCIReg = 1; } else { fbPCIReg = 1; ioPCIReg = 0; } /* Find the frame buffer base address */ if (pCir->pEnt->device->MemBase != 0) { /* Require that the config file value matches one of the PCI values. */ if (!xf86CheckPciMemBase(pCir->PciInfo, pCir->pEnt->device->MemBase)) { xf86DrvMsg(pScrn->scrnIndex, X_ERROR, "MemBase 0x%08lX doesn't match any PCI base register.\n", pCir->pEnt->device->MemBase); return FALSE; } pCir->FbAddress = pCir->pEnt->device->MemBase; from = X_CONFIG; } else { if (pCir->PciInfo->memBase[fbPCIReg] != 0) { pCir->FbAddress = pCir->PciInfo->memBase[fbPCIReg] & 0xff000000; from = X_PROBED; } else { xf86DrvMsg(pScrn->scrnIndex, X_ERROR, "No valid FB address in PCI config space\n"); LgFreeRec(pScrn); return FALSE; } } xf86DrvMsg(pScrn->scrnIndex, from, "Linear framebuffer at 0x%lX\n", (unsigned long)pCir->FbAddress); /* Find the MMIO base address */ if (pCir->pEnt->device->IOBase != 0) { /* Require that the config file value matches one of the PCI values. */ if (!xf86CheckPciMemBase(pCir->PciInfo, pCir->pEnt->device->IOBase)) { xf86DrvMsg(pScrn->scrnIndex, X_ERROR, "IOBase 0x%08lX doesn't match any PCI base register.\n", pCir->pEnt->device->IOBase); return FALSE; } pCir->IOAddress = pCir->pEnt->device->IOBase; from = X_CONFIG; } else { if (pCir->PciInfo->memBase[ioPCIReg] != 0) { pCir->IOAddress = pCir->PciInfo->memBase[ioPCIReg] & 0xfffff000; from = X_PROBED; } else { xf86DrvMsg(pScrn->scrnIndex, X_ERROR, "No valid MMIO address in PCI config space\n"); } } xf86DrvMsg(pScrn->scrnIndex, from, "MMIO registers at 0x%lX\n", (unsigned long)pCir->IOAddress); /* * If the user has specified the amount of memory in the XF86Config * file, we respect that setting. */ if (pCir->pEnt->device->videoRam != 0) { pScrn->videoRam = pCir->pEnt->device->videoRam; from = X_CONFIG; } else { pScrn->videoRam = LgCountRam(pScrn); from = X_PROBED; } if (2048 == pScrn->videoRam) { /* Two-way interleaving */ pCir->chip.lg->memInterleave = 0x40; } else if (4096 == pScrn->videoRam || 8192 == pScrn->videoRam) { /* Four-way interleaving */ pCir->chip.lg->memInterleave = 0x80; } else { /* One-way interleaving */ pCir->chip.lg->memInterleave = 0x00; } xf86DrvMsg(pScrn->scrnIndex, from, "VideoRAM: %d kByte\n", pScrn->videoRam); pCir->FbMapSize = pScrn->videoRam * 1024; pCir->IoMapSize = 0x4000; /* 16K for moment, will increase */ pScrn->racIoFlags = RAC_COLORMAP #ifndef EXPERIMENTAL | RAC_VIEWPORT #endif ; xf86SetOperatingState(resVgaMem, pCir->pEnt->index, ResUnusedOpr); /* Register the PCI-assigned resources. */ if (xf86RegisterResources(pCir->pEnt->index, NULL, ResExclusive)) { xf86DrvMsg(pScrn->scrnIndex, X_ERROR, "xf86RegisterResources() found resource conflicts\n"); return FALSE; } if (!xf86LoadSubModule(pScrn, "ddc")) { LgFreeRec(pScrn); return FALSE; } xf86LoaderReqSymLists(ddcSymbols, NULL); #if LGuseI2C if (!xf86LoadSubModule(pScrn, "i2c")) { LgFreeRec(pScrn); return FALSE; } xf86LoaderReqSymLists(i2cSymbols, NULL); #endif /* Read and print the monitor DDC information */ pScrn->monitor->DDC = LgDoDDC(pScrn); /* The gamma fields must be initialised when using the new cmap code */ if (pScrn->depth > 1) { Gamma zeros = {0.0, 0.0, 0.0}; if (!xf86SetGamma(pScrn, zeros)) return FALSE; } if (xf86GetOptValBool(pCir->Options, OPTION_SHADOW_FB,&pCir->shadowFB)) xf86DrvMsg(pScrn->scrnIndex, X_CONFIG, "ShadowFB %s.\n", pCir->shadowFB ? "enabled" : "disabled"); if ((s = xf86GetOptValString(pCir->Options, OPTION_ROTATE))) { if(!xf86NameCmp(s, "CW")) { /* accel is disabled below for shadowFB */ pCir->shadowFB = TRUE; pCir->rotate = 1; xf86DrvMsg(pScrn->scrnIndex, X_CONFIG, "Rotating screen clockwise - acceleration disabled\n"); } else if(!xf86NameCmp(s, "CCW")) { pCir->shadowFB = TRUE; pCir->rotate = -1; xf86DrvMsg(pScrn->scrnIndex, X_CONFIG, "Rotating screen" "counter clockwise - acceleration disabled\n"); } else { xf86DrvMsg(pScrn->scrnIndex, X_CONFIG, "\"%s\" is not a valid" "value for Option \"Rotate\"\n", s); xf86DrvMsg(pScrn->scrnIndex, X_INFO, "Valid options are \"CW\" or \"CCW\"\n"); } } if (pCir->shadowFB && !pCir->NoAccel) { xf86DrvMsg(pScrn->scrnIndex, X_WARNING, "HW acceleration not supported with \"shadowFB\".\n"); pCir->NoAccel = TRUE; } if (pCir->rotate && pCir->HWCursor) { xf86DrvMsg(pScrn->scrnIndex, X_WARNING, "HW cursor not supported with \"rotate\".\n"); pCir->HWCursor = FALSE; } /* We use a programmable clock */ pScrn->progClock = TRUE; /* XXX Set HW cursor use */ /* Set the min pixel clock */ pCir->MinClock = 12000; /* XXX Guess, need to check this */ xf86DrvMsg(pScrn->scrnIndex, X_DEFAULT, "Min pixel clock is %d MHz\n", pCir->MinClock / 1000); /* * If the user has specified ramdac speed in the XF86Config * file, we respect that setting. */ if (pCir->pEnt->device->dacSpeeds[0]) { ErrorF("Do not specify a Clocks line for Cirrus chips\n"); return FALSE; } else { int speed; int *p; switch (pCir->Chipset) { case PCI_CHIP_GD5462: p = gd5462_MaxClocks; break; case PCI_CHIP_GD5464: case PCI_CHIP_GD5464BD: p = gd5464_MaxClocks; break; case PCI_CHIP_GD5465: p = gd5465_MaxClocks; break; default: ErrorF("???\n"); return FALSE; } switch (pScrn->bitsPerPixel) { case 8: speed = p[1]; break; case 15: case 16: speed = p[2]; break; case 24: speed = p[3]; break; case 32: speed = p[4]; break; default: /* Should not get here */ speed = 0; break; } pCir->MaxClock = speed; from = X_PROBED; } xf86DrvMsg(pScrn->scrnIndex, from, "Max pixel clock is %d MHz\n", pCir->MaxClock / 1000); /* * Setup the ClockRanges, which describe what clock ranges are available, * and what sort of modes they can be used for. */ clockRanges = xnfcalloc(sizeof(ClockRange), 1); clockRanges->next = NULL; clockRanges->minClock = pCir->MinClock; clockRanges->maxClock = pCir->MaxClock; clockRanges->clockIndex = -1; /* programmable */ clockRanges->interlaceAllowed = FALSE; /* XXX check this */ clockRanges->doubleScanAllowed = FALSE; /* XXX check this */ clockRanges->doubleScanAllowed = FALSE; /* XXX check this */ clockRanges->doubleScanAllowed = FALSE; /* XXX check this */ clockRanges->ClockMulFactor = 1; clockRanges->ClockDivFactor = 1; clockRanges->PrivFlags = 0; /* Depending upon what sized tiles used, either 128 or 256. */ /* Aw, heck. Just say 128. */ pCir->Rounding = 128 >> pCir->BppShift; /* * xf86ValidateModes will check that the mode HTotal and VTotal values * don't exceed the chipset's limit if pScrn->maxHValue and * pScrn->maxVValue are set. Since our CIRValidMode() already takes * care of this, we don't worry about setting them here. */ i = xf86ValidateModes(pScrn, pScrn->monitor->Modes, pScrn->display->modes, clockRanges, LgLinePitches[pScrn->bitsPerPixel / 8 - 1], 0, 0, 128 * 8, 0, 0, /* Any virtual height is allowed. */ pScrn->display->virtualX, pScrn->display->virtualY, pCir->FbMapSize, LOOKUP_BEST_REFRESH); pCir->chip.lg->lineDataIndex = LgFindLineData(pScrn->displayWidth, pScrn->bitsPerPixel); if (i == -1) { LgFreeRec(pScrn); return FALSE; } /* Prune the modes marked as invalid */ xf86PruneDriverModes(pScrn); if (i == 0 || pScrn->modes == NULL) { xf86DrvMsg(pScrn->scrnIndex, X_ERROR, "No valid modes found\n"); LgFreeRec(pScrn); return FALSE; } /* * Set the CRTC parameters for all of the modes based on the type * of mode, and the chipset's interlace requirements. * * Calling this is required if the mode->Crtc* values are used by the * driver and if the driver doesn't provide code to set them. They * are not pre-initialised at all. */ xf86SetCrtcForModes(pScrn, INTERLACE_HALVE_V); /* Set the current mode to the first in the list */ pScrn->currentMode = pScrn->modes; /* Print the list of modes being used */ xf86PrintModes(pScrn); /* Set display resolution */ xf86SetDpi(pScrn, 0, 0); /* Load bpp-specific modules */ switch (pScrn->bitsPerPixel) { case 8: case 16: case 24: case 32: if (xf86LoadSubModule(pScrn, "fb") == NULL) { LgFreeRec(pScrn); return FALSE; } xf86LoaderReqSymLists(fbSymbols, NULL); break; } /* Load XAA if needed */ if (!pCir->NoAccel) { if (!xf86LoadSubModule(pScrn, "xaa")) { LgFreeRec(pScrn); return FALSE; } xf86LoaderReqSymLists(xaaSymbols, NULL); } /* Load ramdac if needed */ if (pCir->HWCursor) { if (!xf86LoadSubModule(pScrn, "ramdac")) { LgFreeRec(pScrn); return FALSE; } xf86LoaderReqSymLists(ramdacSymbols, NULL); } if (pCir->shadowFB) { if (!xf86LoadSubModule(pScrn, "shadowfb")) { LgFreeRec(pScrn); return FALSE; } xf86LoaderReqSymLists(shadowSymbols, NULL); } return TRUE; } /* * This function saves the video state. */ static void LgSave(ScrnInfoPtr pScrn) { CirPtr pCir = CIRPTR(pScrn); vgaHWPtr hwp = VGAHWPTR(pScrn); #ifdef LG_DEBUG ErrorF("LgSave\n"); #endif vgaHWSave(pScrn, &VGAHWPTR(pScrn)->SavedReg, VGA_SR_ALL); pCir->chip.lg->ModeReg.ExtVga[CR1A] = pCir->chip.lg->SavedReg.ExtVga[CR1A] = hwp->readCrtc(hwp, 0x1A); pCir->chip.lg->ModeReg.ExtVga[CR1B] = pCir->chip.lg->SavedReg.ExtVga[CR1B] = hwp->readCrtc(hwp, 0x1B); pCir->chip.lg->ModeReg.ExtVga[CR1D] = pCir->chip.lg->SavedReg.ExtVga[CR1D] = hwp->readCrtc(hwp, 0x1D); pCir->chip.lg->ModeReg.ExtVga[CR1E] = pCir->chip.lg->SavedReg.ExtVga[CR1E] = hwp->readCrtc(hwp, 0x1E); pCir->chip.lg->ModeReg.ExtVga[SR07] = pCir->chip.lg->SavedReg.ExtVga[SR07] = hwp->readSeq(hwp, 0x07); pCir->chip.lg->ModeReg.ExtVga[SR0E] = pCir->chip.lg->SavedReg.ExtVga[SR0E] = hwp->readSeq(hwp, 0x0E); pCir->chip.lg->ModeReg.ExtVga[SR12] = pCir->chip.lg->SavedReg.ExtVga[SR12] = hwp->readSeq(hwp, 0x12); pCir->chip.lg->ModeReg.ExtVga[SR13] = pCir->chip.lg->SavedReg.ExtVga[SR13] = hwp->readSeq(hwp, 0x13); pCir->chip.lg->ModeReg.ExtVga[SR1E] = pCir->chip.lg->SavedReg.ExtVga[SR1E] = hwp->readSeq(hwp, 0x1E); pCir->chip.lg->ModeReg.FORMAT = pCir->chip.lg->SavedReg.FORMAT = memrw(0xC0); pCir->chip.lg->ModeReg.VSC = pCir->chip.lg->SavedReg.VSC = memrl(0x3FC); pCir->chip.lg->ModeReg.DTTC = pCir->chip.lg->SavedReg.DTTC = memrw(0xEA); if (pCir->Chipset == PCI_CHIP_GD5465) { pCir->chip.lg->ModeReg.TileCtrl = pCir->chip.lg->SavedReg.TileCtrl = memrw(0x2C4); } pCir->chip.lg->ModeReg.TILE = pCir->chip.lg->SavedReg.TILE = memrb(0x407); if (pCir->Chipset == PCI_CHIP_GD5465) pCir->chip.lg->ModeReg.BCLK = pCir->chip.lg->SavedReg.BCLK = memrb(0x2C0); else pCir->chip.lg->ModeReg.BCLK = pCir->chip.lg->SavedReg.BCLK = memrb(0x8C); pCir->chip.lg->ModeReg.CONTROL = pCir->chip.lg->SavedReg.CONTROL = memrw(0x402); } /* * Initialise a new mode. This is currently still using the old * "initialise struct, restore/write struct to HW" model. That could * be changed. */ static Bool LgModeInit(ScrnInfoPtr pScrn, DisplayModePtr mode) { vgaHWPtr hwp; CirPtr pCir; int width; Bool VDiv2 = FALSE; CARD16 clockData; LgLineDataPtr lineData; #ifdef LG_DEBUG ErrorF("LgModeInit %d bpp, %d %d %d %d %d %d %d %d %d\n", pScrn->bitsPerPixel, mode->Clock, mode->HDisplay, mode->HSyncStart, mode->HSyncEnd, mode->HTotal, mode->VDisplay, mode->VSyncStart, mode->VSyncEnd, mode->VTotal); ErrorF("LgModeInit: depth %d bits\n", pScrn->depth); #endif pCir = CIRPTR(pScrn); hwp = VGAHWPTR(pScrn); vgaHWUnlock(hwp); if (mode->VTotal >= 1024 && !(mode->Flags & V_INTERLACE)) { /* For non-interlaced vertical timing >= 1024, the vertical timings */ /* are divided by 2 and VGA CRTC 0x17 bit 2 is set. */ if (!mode->CrtcVAdjusted) { mode->CrtcVDisplay >>= 1; mode->CrtcVSyncStart >>= 1; mode->CrtcVSyncEnd >>= 1; mode->CrtcVTotal >>= 1; mode->CrtcVAdjusted = TRUE; } VDiv2 = TRUE; } /* Initialise the ModeReg values */ if (!vgaHWInit(pScrn, mode)) return FALSE; pScrn->vtSema = TRUE; if (VDiv2) hwp->ModeReg.CRTC[0x17] |= 0x04; #ifdef LG_DEBUG ErrorF("SynthClock = %d\n", mode->SynthClock); #endif hwp->IOBase = 0x3D0; hwp->ModeReg.MiscOutReg |= 0x01; #if 0 /* Mono address */ hwp->IOBase = 0x3B0; hwp->ModeReg.MiscOutReg &= ~0x01; #endif /* ??? Should these be both ...End or ...Start, not one of each? */ pCir->chip.lg->ModeReg.ExtVga[CR1A] = (((mode->CrtcVSyncStart + 1) & 0x300 ) >> 2) | (((mode->CrtcHSyncEnd >> 3) & 0xC0) >> 2); width = pScrn->displayWidth * pScrn->bitsPerPixel / 8; if (pScrn->bitsPerPixel == 1) width <<= 2; hwp->ModeReg.CRTC[0x13] = (width + 7) >> 3; /* Offset extension (see CR13) */ pCir->chip.lg->ModeReg.ExtVga[CR1B] &= 0xEF; pCir->chip.lg->ModeReg.ExtVga[CR1B] |= (((width + 7) >> 3) & 0x100)?0x10:0x00; pCir->chip.lg->ModeReg.ExtVga[CR1B] |= 0x22; pCir->chip.lg->ModeReg.ExtVga[CR1D] = (((width + 7) >> 3) & 0x200)?0x01:0x00; /* Set the 28th bit to enable extended modes. */ pCir->chip.lg->ModeReg.VSC = 0x10000000; /* Overflow register (sure are a lot of overflow bits around...) */ pCir->chip.lg->ModeReg.ExtVga[CR1E] = 0x00; pCir->chip.lg->ModeReg.ExtVga[CR1E] |= ((mode->CrtcHTotal>>3 & 0x0100)?1:0)<<7; pCir->chip.lg->ModeReg.ExtVga[CR1E] |= ((mode->CrtcHDisplay>>3 & 0x0100)?1:0)<<6; pCir->chip.lg->ModeReg.ExtVga[CR1E] |= ((mode->CrtcHSyncStart>>3 & 0x0100)?1:0)<<5; pCir->chip.lg->ModeReg.ExtVga[CR1E] |= ((mode->CrtcHSyncStart>>3 & 0x0100)?1:0)<<4; pCir->chip.lg->ModeReg.ExtVga[CR1E] |= ((mode->CrtcVTotal & 0x0400)?1:0)<<3; pCir->chip.lg->ModeReg.ExtVga[CR1E] |= ((mode->CrtcVDisplay & 0x0400)?1:0)<<2; pCir->chip.lg->ModeReg.ExtVga[CR1E] |= ((mode->CrtcVSyncStart & 0x0400)?1:0)<<1; pCir->chip.lg->ModeReg.ExtVga[CR1E] |= ((mode->CrtcVSyncStart & 0x0400)?1:0)<<0; lineData = &LgLineData[pCir->chip.lg->lineDataIndex]; pCir->chip.lg->ModeReg.TILE = lineData->tilesPerLine & 0x3F; if (8 == pScrn->bitsPerPixel) { pCir->chip.lg->ModeReg.FORMAT = 0x0000; pCir->chip.lg->ModeReg.DTTC = (pCir->chip.lg->ModeReg.TILE << 8) | 0x0080 | (lineData->width << 6); pCir->chip.lg->ModeReg.CONTROL = 0x0000 | (lineData->width << 11); /* There is an optimal FIFO threshold value (lower 5 bits of DTTC) for every resolution and color depth combination. We'll hit the highlights here, and get close for anything that's not covered. */ if (mode->CrtcHDisplay <= 640) { /* BAD numbers: 0x1E */ /* GOOD numbers: 0x14 */ pCir->chip.lg->ModeReg.DTTC = (pCir->chip.lg->ModeReg.DTTC & 0xFFE0) | (0x0014); } else if (mode->CrtcHDisplay <= 800) { /* BAD numbers: 0x16 */ /* GOOD numbers: 0x13 0x14 */ pCir->chip.lg->ModeReg.DTTC = (pCir->chip.lg->ModeReg.DTTC & 0xFFE0) | (0x0014); } else if (mode->CrtcHDisplay <= 1024) { /* BAD numbers: */ /* GOOD numbers: 0x15 */ pCir->chip.lg->ModeReg.DTTC = (pCir->chip.lg->ModeReg.DTTC & 0xFFE0) | (0x0015); } else if (mode->CrtcHDisplay <= 1280) { /* BAD numbers: */ /* GOOD numbers: 0x16 */ pCir->chip.lg->ModeReg.DTTC = (pCir->chip.lg->ModeReg.DTTC & 0xFFE0) | (0x0016); } else { /* BAD numbers: */ /* GOOD numbers: */ pCir->chip.lg->ModeReg.DTTC = (pCir->chip.lg->ModeReg.DTTC & 0xFFE0) | (0x0017); } } else if (16 == pScrn->bitsPerPixel) { /* !!! Assume 5-6-5 RGB mode (for now...) */ pCir->chip.lg->ModeReg.FORMAT = 0x1400; if (pScrn->depth == 15) pCir->chip.lg->ModeReg.FORMAT = 0x1600; pCir->chip.lg->ModeReg.DTTC = (pCir->chip.lg->ModeReg.TILE << 8) | 0x0080 | (lineData->width << 6); pCir->chip.lg->ModeReg.CONTROL = 0x2000 | (lineData->width << 11); if (mode->CrtcHDisplay <= 640) { /* BAD numbers: 0x12 */ /* GOOD numbers: 0x10 */ pCir->chip.lg->ModeReg.DTTC = (pCir->chip.lg->ModeReg.DTTC & 0xFFE0) | (0x0010); } else if (mode->CrtcHDisplay <= 800) { /* BAD numbers: 0x13 */ /* GOOD numbers: 0x11 */ pCir->chip.lg->ModeReg.DTTC = (pCir->chip.lg->ModeReg.DTTC & 0xFFE0) | (0x0011); } else if (mode->CrtcHDisplay <= 1024) { /* BAD numbers: 0x14 */ /* GOOD numbers: 0x12 */ pCir->chip.lg->ModeReg.DTTC = (pCir->chip.lg->ModeReg.DTTC & 0xFFE0) | (0x0012); } else if (mode->CrtcHDisplay <= 1280) { /* BAD numbers: 0x08 0x10 */ /* Borderline numbers: 0x12 */ /* GOOD numbers: 0x15 */ pCir->chip.lg->ModeReg.DTTC = (pCir->chip.lg->ModeReg.DTTC & 0xFFE0) | (0x0015); } else { pCir->chip.lg->ModeReg.DTTC = (pCir->chip.lg->ModeReg.DTTC & 0xFFE0) | (0x0017); } } else if (24 == pScrn->bitsPerPixel) { pCir->chip.lg->ModeReg.FORMAT = 0x2400; pCir->chip.lg->ModeReg.DTTC = (pCir->chip.lg->ModeReg.TILE << 8) | 0x0080 | (lineData->width << 6); pCir->chip.lg->ModeReg.CONTROL = 0x4000 | (lineData->width << 11); if (mode->CrtcHDisplay <= 640) { /* BAD numbers: */ /* GOOD numbers: 0x10 */ pCir->chip.lg->ModeReg.DTTC = (pCir->chip.lg->ModeReg.DTTC & 0xFFE0) | (0x0010); } else if (mode->CrtcHDisplay <= 800) { /* BAD numbers: */ /* GOOD numbers: 0x11 */ pCir->chip.lg->ModeReg.DTTC = (pCir->chip.lg->ModeReg.DTTC & 0xFFE0) | (0x0011); } else if (mode->CrtcHDisplay <= 1024) { /* BAD numbers: 0x12 0x13 */ /* Borderline numbers: 0x15 */ /* GOOD numbers: 0x17 */ pCir->chip.lg->ModeReg.DTTC = (pCir->chip.lg->ModeReg.DTTC & 0xFFE0) | (0x0017); } else if (mode->CrtcHDisplay <= 1280) { /* BAD numbers: */ /* GOOD numbers: 0x1E */ pCir->chip.lg->ModeReg.DTTC = (pCir->chip.lg->ModeReg.DTTC & 0xFFE0) | (0x001E); } else { /* BAD numbers: */ /* GOOD numbers: */ pCir->chip.lg->ModeReg.DTTC = (pCir->chip.lg->ModeReg.DTTC & 0xFFE0) | (0x0020); } } else if (32 == pScrn->bitsPerPixel) { pCir->chip.lg->ModeReg.FORMAT = 0x3400; pCir->chip.lg->ModeReg.DTTC = (pCir->chip.lg->ModeReg.TILE << 8) | 0x0080 | (lineData->width << 6); pCir->chip.lg->ModeReg.CONTROL = 0x6000 | (lineData->width << 11); if (mode->CrtcHDisplay <= 640) { /* GOOD numbers: 0x0E */ /* BAD numbers: */ pCir->chip.lg->ModeReg.DTTC = (pCir->chip.lg->ModeReg.DTTC & 0xFFE0) | (0x000E); } else if (mode->CrtcHDisplay <= 800) { /* GOOD numbers: 0x17 */ /* BAD numbers: */ pCir->chip.lg->ModeReg.DTTC = (pCir->chip.lg->ModeReg.DTTC & 0xFFE0) | (0x0017); } else if (mode->CrtcHDisplay <= 1024) { /* GOOD numbers: 0x1D */ /* OKAY numbers: 0x15 0x14 0x16 0x18 0x19 */ /* BAD numbers: 0x0E 0x12 0x13 0x0D */ pCir->chip.lg->ModeReg.DTTC = (pCir->chip.lg->ModeReg.DTTC & 0xFFE0) | (0x001D); } else if (mode->CrtcHDisplay <= 1280) { /* GOOD numbers: */ /* BAD numbers: */ pCir->chip.lg->ModeReg.DTTC = (pCir->chip.lg->ModeReg.DTTC & 0xFFE0) | (0x0022); /* 10 */ } else { pCir->chip.lg->ModeReg.DTTC = (pCir->chip.lg->ModeReg.DTTC & 0xFFE0) | (0x0024); } } else { /* ??? What could it be? Use some sane numbers. */ } /* Setup the appropriate memory interleaving */ pCir->chip.lg->ModeReg.DTTC |= (pCir->chip.lg->memInterleave << 8); pCir->chip.lg->ModeReg.TILE |= pCir->chip.lg->memInterleave & 0xC0; if (PCI_CHIP_GD5465 == pCir->Chipset) { /* The tile control information in the DTTC is also mirrored elsewhere. */ pCir->chip.lg->ModeReg.TileCtrl = pCir->chip.lg->ModeReg.DTTC & 0xFFC0; /* The 5465's DTTC records _fetches_ per line, not tiles per line. Fetchs are 128-byte fetches. */ if (pCir->chip.lg->ModeReg.DTTC & 0x0040) { /* Using 256-byte wide tiles. Double the fetches per line field. */ pCir->chip.lg->ModeReg.DTTC = (pCir->chip.lg->ModeReg.DTTC & 0xC0FF) | ((pCir->chip.lg->ModeReg.DTTC & 0x3F00) << 1); } } /* Program the registers */ vgaHWProtect(pScrn, TRUE); hwp->writeMiscOut(hwp, hwp->ModeReg.MiscOutReg); clockData = LgSetClock(pCir, hwp, mode->SynthClock); pCir->chip.lg->ModeReg.ExtVga[SR0E] = (clockData >> 8) & 0xFF; pCir->chip.lg->ModeReg.ExtVga[SR1E] = clockData & 0xFF; /* Write those registers out to the card. */ LgRestoreLgRegs(pScrn, &pCir->chip.lg->ModeReg); /* Programme the registers */ vgaHWRestore(pScrn, &hwp->ModeReg, VGA_SR_MODE | VGA_SR_CMAP); vgaHWProtect(pScrn, FALSE); return TRUE; } static int LgFindLineData(int displayWidth, int bpp) { /* Find the smallest tile-line-pitch such that the total byte pitch is greater than or equal to displayWidth*Bpp. */ int i; /* Some pitch sizes are duplicates in the table. BUT, the invariant is that the _first_ time a pitch occurs in the table is always _before_ all other pitches greater than it. Said in another way... if all duplicate entries from the table were removed, then the resulting pitch values are strictly increasing. */ for (i = 0; LgLineData[i].pitch > 0; i++) if (LgLineData[i].pitch >= displayWidth*bpp>>3) return i; /* Um, uh oh! */ return -1; } static void LgRestoreLgRegs(ScrnInfoPtr pScrn, LgRegPtr lgReg) { CirPtr pCir; vgaHWPtr hwp; CARD8 cr1D; pCir = CIRPTR(pScrn); /* First, VGAish registers. */ hwp = VGAHWPTR(pScrn); hwp->writeCrtc(hwp, 0x1A, lgReg->ExtVga[CR1A]); hwp->writeCrtc(hwp, 0x1B, lgReg->ExtVga[CR1B]); cr1D = (hwp->readCrtc(hwp, 0x1D) & ~1) | (lgReg->ExtVga[CR1D] & 0x01); hwp->writeCrtc(hwp, 0x1D, cr1D); hwp->writeCrtc(hwp, 0x1E, lgReg->ExtVga[CR1E]); hwp->writeSeq(hwp, 0x07, lgReg->ExtVga[SR07]); hwp->writeSeq(hwp, 0x0E, lgReg->ExtVga[SR0E]); hwp->writeSeq(hwp, 0x12, lgReg->ExtVga[SR12]); hwp->writeSeq(hwp, 0x13, lgReg->ExtVga[SR13]); hwp->writeSeq(hwp, 0x1E, lgReg->ExtVga[SR1E]); memww(0xC0, lgReg->FORMAT); /* Vendor Specific Control is touchy. Only bit 28 is of concern. */ memwl(0x3FC, ((memrl(0x3FC) & ~(1<<28)) | (lgReg->VSC & (1<<28)))); memww(0xEA, lgReg->DTTC); if (pCir->Chipset == PCI_CHIP_GD5465) { memww(0x2C4, lgReg->TileCtrl); } memwb(0x407, lgReg->TILE); if (pCir->Chipset == PCI_CHIP_GD5465) memwb(0x2C0, lgReg->BCLK); else memwb(0x8C, lgReg->BCLK); memww(0x402, lgReg->CONTROL); } /* * Restore the initial (text) mode. */ static void LgRestore(ScrnInfoPtr pScrn) { vgaHWPtr hwp; vgaRegPtr vgaReg; CirPtr pCir; LgRegPtr lgReg; #ifdef LG_DEBUG ErrorF("LgRestore pScrn = %p\n", (void *)pScrn); #endif pCir = CIRPTR(pScrn); hwp = VGAHWPTR(pScrn); vgaReg = &hwp->SavedReg; lgReg = &pCir->chip.lg->SavedReg; vgaHWProtect(pScrn, TRUE); LgRestoreLgRegs(pScrn, lgReg); vgaHWRestore(pScrn, vgaReg, VGA_SR_ALL); vgaHWProtect(pScrn, FALSE); } /* Mandatory */ /* This gets called at the start of each server generation */ Bool LgScreenInit(int scrnIndex, ScreenPtr pScreen, int argc, char **argv) { /* The vgaHW references will disappear one day */ ScrnInfoPtr pScrn; vgaHWPtr hwp; CirPtr pCir; int i, ret; VisualPtr visual; int displayWidth,width,height; unsigned char * FbBase = NULL; #ifdef LG_DEBUG ErrorF("LgScreenInit\n"); #endif /* * First get the ScrnInfoRec */ pScrn = xf86Screens[pScreen->myNum]; hwp = VGAHWPTR(pScrn); hwp->MapSize = 0x10000; /* Standard 64k VGA window */ pCir = CIRPTR(pScrn); /* Map the VGA memory and get the VGA IO base */ if (!vgaHWMapMem(pScrn)) return FALSE; /* Map the CIR memory and MMIO areas */ if (!CirMapMem(pCir, pScrn->scrnIndex)) return FALSE; #ifdef EXPERIMENTAL lg_vgaHWSetMmioFunc(hwp, pCir->IOBase); #endif vgaHWGetIOBase(hwp); /* Save the current state */ LgSave(pScrn); /* Initialise the first mode */ if (!LgModeInit(pScrn, pScrn->currentMode)) return FALSE; /* Make things beautiful */ LgSaveScreen(pScreen, SCREEN_SAVER_ON); /* Set the viewport */ LgAdjustFrame(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. * */ /* * Reset the visual list. */ miClearVisualTypes(); /* Setup the visuals we support. */ if (!miSetVisualTypes(pScrn->depth, miGetDefaultVisualMask(pScrn->depth), pScrn->rgbBits, pScrn->defaultVisual)) return FALSE; miSetPixmapDepths (); #ifdef LG_DEBUG ErrorF("LgScreenInit after miSetVisualTypes\n"); #endif displayWidth = pScrn->displayWidth; if (pCir->rotate) { height = pScrn->virtualX; width = pScrn->virtualY; } else { width = pScrn->virtualX; height = pScrn->virtualY; } if(pCir->shadowFB) { pCir->ShadowPitch = BitmapBytePad(pScrn->bitsPerPixel * width); pCir->ShadowPtr = xalloc(pCir->ShadowPitch * height); displayWidth = pCir->ShadowPitch / (pScrn->bitsPerPixel >> 3); FbBase = pCir->ShadowPtr; } else { pCir->ShadowPtr = NULL; FbBase = pCir->FbBase; } /* * Call the framebuffer layer's ScreenInit function, and fill in other * pScreen fields. */ switch (pScrn->bitsPerPixel) { case 8: case 16: case 24: case 32: ret = fbScreenInit(pScreen, FbBase, width,height, pScrn->xDpi, pScrn->yDpi, displayWidth,pScrn->bitsPerPixel); break; default: xf86DrvMsg(scrnIndex, X_ERROR, "X11: Internal error: invalid bpp (%d) in LgScreenInit\n", pScrn->bitsPerPixel); ret = FALSE; break; } if (!ret) return FALSE; #ifdef LG_DEBUG ErrorF("LgScreenInit after depth dependent init\n"); #endif /* Override the default mask/offset settings */ if (pScrn->bitsPerPixel > 8) { for (i = 0; i < pScreen->numVisuals; i++) { visual = &pScreen->visuals[i]; 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); miInitializeBackingStore(pScreen); /* * Set initial black & white colourmap indices. */ xf86SetBlackWhitePixels(pScreen); if (!pCir->NoAccel) { /* Initialize XAA functions */ if (!LgXAAInit(pScreen)) xf86DrvMsg(pScrn->scrnIndex, X_ERROR, "Could not initialize XAA\n"); } #if 1 pCir->DGAModeInit = LgModeInit; if (!CirDGAInit(pScreen)) xf86DrvMsg(pScrn->scrnIndex, X_ERROR, "DGA initialization failed\n"); #endif xf86SetSilkenMouse(pScreen); /* Initialise cursor functions */ miDCInitialize(pScreen, xf86GetPointerScreenFuncs()); if (pCir->HWCursor) { /* Initialize HW cursor layer */ if (!LgHWCursorInit(pScreen)) xf86DrvMsg(pScrn->scrnIndex, X_ERROR, "Hardware cursor initialization failed\n"); } /* Initialise default colourmap */ if (!miCreateDefColormap(pScreen)) return FALSE; if (pScrn->bitsPerPixel > 1 && pScrn->bitsPerPixel <= 8) vgaHWHandleColormaps(pScreen); xf86DPMSInit(pScreen, LgDisplayPowerManagementSet, 0); pScrn->memPhysBase = pCir->FbAddress; pScrn->fbOffset = 0; { XF86VideoAdaptorPtr *ptr; int n; n = xf86XVListGenericAdaptors(pScrn,&ptr); if (n) xf86XVScreenInit(pScreen, ptr, n); } /* * Wrap the CloseScreen vector and set SaveScreen. */ pScreen->SaveScreen = LgSaveScreen; pCir->CloseScreen = pScreen->CloseScreen; pScreen->CloseScreen = LgCloseScreen; /* Report any unused options (only for the first generation) */ if (serverGeneration == 1) xf86ShowUnusedOptions(pScrn->scrnIndex, pScrn->options); /* Done */ return TRUE; } /* Usually mandatory */ Bool LgSwitchMode(int scrnIndex, DisplayModePtr mode, int flags) { return LgModeInit(xf86Screens[scrnIndex], mode); } #define ROUND_DOWN(x, mod) (((x) / (mod)) * (mod)) #define ROUND_UP(x, mod) ((((x) + (mod) - 1) / (mod)) * (mod)) /* * This function is used to initialize the Start Address - the first * displayed location in the video memory. */ /* Usually mandatory */ void LgAdjustFrame(int scrnIndex, int x, int y, int flags) { ScrnInfoPtr pScrn = xf86Screens[scrnIndex]; int Base, tmp; CirPtr pCir = CIRPTR(pScrn); vgaHWPtr hwp = VGAHWPTR(pScrn); int cursorX, cursorY; int middleX, middleY; const LgLineDataPtr lineData = &LgLineData[pCir->chip.lg->lineDataIndex]; const int viewportXRes = (PCI_CHIP_GD5465 == pCir->Chipset) ? (24==pScrn->bitsPerPixel?24:1) : (lineData->width?256:128) / (24==pScrn->bitsPerPixel?1:(pScrn->bitsPerPixel>>3)); const int viewportYRes = (PCI_CHIP_GD5465 == pCir->Chipset) ? 1 : (24==pScrn->bitsPerPixel?3:1); /* Where's the pointer? */ miPointerPosition(&cursorX, &cursorY); /* Where's the middle of the screen? We want to eventually know which side of the screen the pointer is on. */ middleX = (pScrn->frameX1 + pScrn->frameX0) / 2; middleY = (pScrn->frameY1 + pScrn->frameY0) / 2; if (cursorX < middleX) { /* Pointer is on left side of screen. Round the frame value down. */ pScrn->frameX0 = ROUND_DOWN(pScrn->frameX0, viewportXRes); } else { /* Pointer is on right side of screen. Round the frame value up. A side effect of this rounding up is that we might expose a part of the screen that's actually on the far /left/ of the frame buffer. That's because, although the virtual desktop might be an integral number of tiles, the display might not. We'll just live with this artifact. */ pScrn->frameX0 = ROUND_UP(pScrn->frameX0, viewportXRes); } pScrn->frameX1 = pScrn->frameX0 + pScrn->currentMode->HDisplay - 1; if (cursorY < middleY) { pScrn->frameY0 = ROUND_DOWN(pScrn->frameY0, viewportYRes); } else { pScrn->frameY0 = ROUND_UP(pScrn->frameY0, viewportYRes); } pScrn->frameY1 = pScrn->frameY0 + pScrn->currentMode->VDisplay - 1; if (x != pScrn->frameX0 || y != pScrn->frameY0) { /* !!! */ /* We moved the frame from where xf86SetViewport() placed it. If we're using a SW cursor, that's okay -- the pointer exists in the framebuffer, and those bits are still all aligned. But if we're using a HW cursor, then we need to re-align the pointer. Call SetCursorPosition() with the appropriate new pointer values, adjusted to be wrt the new frame. */ x = pScrn->frameX0; y = pScrn->frameY0; } /* ??? Will this work for 1bpp? */ Base = (y * lineData->pitch + (x*pScrn->bitsPerPixel/8)) / 4; if ((Base & ~0x000FFFFF) != 0) { /* ??? */ ErrorF("X11: Internal error: LgAdjustFrame: cannot handle overflow\n"); return; } hwp->writeCrtc(hwp, 0x0C, (Base >> 8) & 0xFF); hwp->writeCrtc(hwp, 0x0D, Base & 0xFF); tmp = hwp->readCrtc(hwp, 0x1B) & 0xF2; tmp |= (Base >> 16) & 0x01; tmp |= (Base >> 15) & 0x0C; hwp->writeCrtc(hwp, 0x1B, tmp); tmp = hwp->readCrtc(hwp, 0x1D) & 0xE7; tmp |= (Base >> 16) & 0x18; hwp->writeCrtc(hwp, 0x1D, tmp); } /* * This is called when VT switching back to the X server. Its job is * to reinitialise the video mode. * * We may wish to unmap video/MMIO memory too. */ /* Mandatory */ Bool LgEnterVT(int scrnIndex, int flags) { ScrnInfoPtr pScrn = xf86Screens[scrnIndex]; CirPtr pCir = CIRPTR(pScrn); #ifdef LG_DEBUG ErrorF("LgEnterVT\n"); #endif /* XXX Shouldn't this be in LeaveVT? */ /* Disable HW cursor */ if (pCir->HWCursor) LgHideCursor(pScrn); /* Should we re-save the text mode on each VT enter? */ return LgModeInit(pScrn, pScrn->currentMode); } /* * This is called when VT switching away from the X server. Its job is * to restore the previous (text) mode. * * We may wish to remap video/MMIO memory too. */ /* Mandatory */ void LgLeaveVT(int scrnIndex, int flags) { ScrnInfoPtr pScrn = xf86Screens[scrnIndex]; vgaHWPtr hwp = VGAHWPTR(pScrn); CirPtr pCir = CIRPTR(pScrn); #ifdef LG_DEBUG ErrorF("LgLeaveVT\n"); #endif /* XXX Shouldn't this be in EnterVT? */ /* Enable HW cursor */ if (pCir->HWCursor) LgShowCursor(pScrn); LgRestore(pScrn); vgaHWLock(hwp); } /* * This is called at the end of each server generation. It restores the * original (text) mode. It should also unmap the video memory, and free * any per-generation data allocated by the driver. It should finish * by unwrapping and calling the saved CloseScreen function. */ /* Mandatory */ static Bool LgCloseScreen(int scrnIndex, ScreenPtr pScreen) { ScrnInfoPtr pScrn = xf86Screens[scrnIndex]; vgaHWPtr hwp = VGAHWPTR(pScrn); CirPtr pCir = CIRPTR(pScrn); if(pScrn->vtSema) { LgRestore(pScrn); if (pCir->HWCursor) LgHideCursor(pScrn); vgaHWLock(hwp); CirUnmapMem(pCir, pScrn->scrnIndex); } if (pCir->AccelInfoRec) XAADestroyInfoRec(pCir->AccelInfoRec); pCir->AccelInfoRec = NULL; if (pCir->CursorInfoRec) xf86DestroyCursorInfoRec(pCir->CursorInfoRec); pCir->CursorInfoRec = NULL; if (pCir->DGAModes) xfree(pCir->DGAModes); pCir->DGAnumModes = 0; pCir->DGAModes = NULL; pScrn->vtSema = FALSE; pScreen->CloseScreen = pCir->CloseScreen; return (*pScreen->CloseScreen)(scrnIndex, pScreen); } /* Free up any persistent data structures */ /* Optional */ void LgFreeScreen(int scrnIndex, int flags) { #ifdef LG_DEBUG ErrorF("LgFreeScreen\n"); #endif /* * This only gets called when a screen is being deleted. It does not * get called routinely at the end of a server generation. */ if (xf86LoaderCheckSymbol("vgaHWFreeHWRec")) vgaHWFreeHWRec(xf86Screens[scrnIndex]); LgFreeRec(xf86Screens[scrnIndex]); } /* Checks if a mode is suitable for the selected chipset. */ /* Optional */ ModeStatus LgValidMode(int scrnIndex, DisplayModePtr mode, Bool verbose, int flags) { int lace; lace = 1 + ((mode->Flags & V_INTERLACE) != 0); if ((mode->CrtcHDisplay <= 2048) && (mode->CrtcHSyncStart <= 4096) && (mode->CrtcHSyncEnd <= 4096) && (mode->CrtcHTotal <= 4096) && (mode->CrtcVDisplay <= 2048 * lace) && (mode->CrtcVSyncStart <= 4096 * lace) && (mode->CrtcVSyncEnd <= 4096 * lace) && (mode->CrtcVTotal <= 4096 * lace)) { return(MODE_OK); } return(MODE_BAD); } /* Do screen blanking */ /* Mandatory */ static Bool LgSaveScreen(ScreenPtr pScreen, int mode) { CirPtr pCir = CIRPTR(xf86Screens[pScreen->myNum]); ScrnInfoPtr pScrn = NULL; Bool unblank; unblank = xf86IsUnblank(mode); if (pScreen != NULL) pScrn = xf86Screens[pScreen->myNum]; if (pScrn != NULL && pScrn->vtSema) { if (unblank) /* Power up the palette DAC */ memwb(0xB0,memrb(0xB0) & 0x7F); else /* Power down the palette DAC */ memwb(0xB0,memrb(0xB0) | 0x80); } return vgaHWSaveScreen(pScreen, mode); } static CARD16 LgSetClock(CirPtr pCir, vgaHWPtr hwp, int freq) { int ffreq, num, den; CARD8 tmp; ErrorF("LgSetClock freq=%d.%03dMHz\n", freq / 1000, freq % 1000); ffreq = freq; if (!CirrusFindClock(&ffreq, pCir->MaxClock, &num, &den)) return 0; ErrorF("LgSetClock: nom=%x den=%x ffreq=%d.%03dMHz\n", num, den, ffreq / 1000, ffreq % 1000); /* Set VCLK3. */ /* The numerator and denominator registers are switched around in the Laguna chips. */ tmp = hwp->readSeq(hwp, 0x0E); hwp->writeSeq(hwp, 0x0E, (tmp & 0x80) | den); hwp->writeSeq(hwp, 0x1E, num); return (den << 8) | num; } /* * CIRDisplayPowerManagementSet -- * * Sets VESA Display Power Management Signaling (DPMS) Mode. */ static void LgDisplayPowerManagementSet(ScrnInfoPtr pScrn, int PowerManagementMode, int flags) { unsigned char sr01, cr1a; vgaHWPtr hwp; #ifdef LG_DEBUG ErrorF("LgDisplayPowerManagementSet: %d\n", PowerManagementMode); #endif hwp = VGAHWPTR(pScrn); switch (PowerManagementMode) { case DPMSModeOn: /* Screen: On; HSync: On, VSync: On */ sr01 = 0x00; cr1a = 0x00; break; case DPMSModeStandby: /* Screen: Off; HSync: Off, VSync: On */ sr01 = 0x20; cr1a = 0x08; break; case DPMSModeSuspend: /* Screen: Off; HSync: On, VSync: Off */ sr01 = 0x20; cr1a = 0x04; break; case DPMSModeOff: /* Screen: Off; HSync: Off, VSync: Off */ sr01 = 0x20; cr1a = 0x0c; break; default: return; } sr01 |= hwp->readSeq(hwp, 0x01) & ~0x20; hwp->writeSeq(hwp, 0x01, sr01); cr1a |= hwp->readCrtc(hwp, 0x1A) & ~0x0C; hwp->writeCrtc(hwp, 0x1A, cr1a); } #define minb(p) MMIO_IN8(hwp->MMIOBase, (p)) #define moutb(p,v) MMIO_OUT8(hwp->MMIOBase, (p),(v)) static void mmioWriteCrtc(vgaHWPtr hwp, CARD8 index, CARD8 value) { moutb(index << 2, value); } static CARD8 mmioReadCrtc(vgaHWPtr hwp, CARD8 index) { return minb(index << 2); } static void lg_vgaHWSetMmioFunc(vgaHWPtr hwp, CARD8 *base) { hwp->writeCrtc = mmioWriteCrtc; hwp->readCrtc = mmioReadCrtc; hwp->MMIOBase = base; hwp->MMIOOffset = 0; }