/* Copyright (c) 2003-2006 Advanced Micro Devices, Inc. * * Portioned modeled from xf86-video-intel/src/i830_driver.c * Copyright 2001 VA Linux Systems Inc., Fremont, California. * Copyright \ufffd 2002 by David Dawes * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. * * Neither the name of the Advanced Micro Devices, Inc. nor the names of its * contributors may be used to endorse or promote products derived from this * software without specific prior written permission. */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include "xf86.h" #include "xf86_OSproc.h" #include "xf86Resources.h" #include "xf86cmap.h" #include "compiler.h" #include "mipointer.h" #include "shadow.h" #include #include "fb.h" #include "miscstruct.h" #include "micmap.h" #include "vbe.h" #include "fb.h" #include "randrstr.h" #include "amd.h" #include "gfx_defs.h" #include "gfx_regs.h" #include "panel.h" /* Bring in VGA functions */ #include "amd_gx_vga.c" #define GX_MIN_PITCH 1024 #define GX_MAX_PITCH 8192 #define GX_MAX_WIDTH 1600 #define GX_MIN_HEIGHT 400 #define GX_MAX_HEIGHT 1200 #define GX_CB_PITCH 544 #define GX_CB_SIZE 544 #define GX_CPU_REG_SIZE 0x4000 #define GX_GP_REG_SIZE 0x4000 #define GX_VID_REG_SIZE 0x4000 extern OptionInfoRec GX_GeodeOptions[]; extern const char *amdVgahwSymbols[]; extern const char *amdVbeSymbols[]; extern const char *amdInt10Symbols[]; extern const char *amdFbSymbols[]; extern const char *amdXaaSymbols[]; extern const char *amdExaSymbols[]; extern const char *amdRamdacSymbols[]; unsigned char *XpressROMPtr; static inline void gx_enable_dac_power(void) { gfx_write_vid32(RCDF_VID_MISC, gfx_read_vid32(RCDF_VID_MISC) & RCDF_GAMMA_BYPASS_BOTH); } static inline void gx_disable_dac_power(void) { gfx_write_vid32(RCDF_VID_MISC, RCDF_DAC_POWER_DOWN | RCDF_ANALOG_POWER_DOWN | (gfx_read_vid32(RCDF_VID_MISC) & RCDF_GAMMA_BYPASS_BOTH)); } static void GXInitEXAMemory(ScrnInfoPtr pScrni, unsigned int *offset, unsigned int *avail) { GeodePtr pGeode = GEODEPTR(pScrni); if (pGeode->exaBfrSz > 0 && pGeode->exaBfrSz <= *avail) { pGeode->exaBfrOffset = *offset; *offset += pGeode->exaBfrOffset; *avail -= pGeode->exaBfrOffset; } } static void GXInitXAAMemory(ScrnInfoPtr pScrni, unsigned int *offset, unsigned int *avail) { GeodePtr pGeode = GEODEPTR(pScrni); unsigned int size, i, pitch; /* XXX - FIXME - What if we are out of room? Then what? */ /* For now, we NULL them all out. */ if (pGeode->NoOfImgBuffers > 0) { size = pGeode->displayPitch * pGeode->NoOfImgBuffers; if (size <= *avail) { for (i = 0; i < pGeode->NoOfImgBuffers; i++) { pGeode->AccelImageWriteBuffers[i] = pGeode->FBBase + *offset; *offset += pGeode->displayPitch; *avail -= pGeode->displayPitch; } } else { xf86DrvMsg(pScrni->scrnIndex, X_ERROR, "Not enough memory for image write buffers.\n"); for (i = 0; i < pGeode->NoOfImgBuffers; i++) pGeode->AccelImageWriteBuffers[i] = NULL; } } if (pGeode->NoOfColorExpandLines > 0) { pitch = ((pGeode->displayPitch + 31) >> 5) << 2; size = pitch * pGeode->NoOfColorExpandLines; if (size <= *avail) { for (i = 0; i < pGeode->NoOfColorExpandLines; i++) { pGeode->AccelColorExpandBuffers[i] = pGeode->FBBase + *offset; *offset += pitch; *avail -= pitch; } } else { xf86DrvMsg(pScrni->scrnIndex, X_ERROR, "Not enough memory for color expansion buffers.\n"); for (i = 0; i < pGeode->NoOfImgBuffers; i++) pGeode->AccelColorExpandBuffers[i] = NULL; } } } static Bool GXAllocateMemory(ScreenPtr pScrn, ScrnInfoPtr pScrni, int rotate) { GeodePtr pGeode = GEODEPTR(pScrni); unsigned int fboffset, fbavail; unsigned int size; unsigned int bytpp = (pScrni->bitsPerPixel + 7) / 8; BOOL ret = TRUE; if (pGeode->tryCompression) pGeode->displayPitch = GeodeCalculatePitchBytes(pScrni->virtualX, pScrni->bitsPerPixel); else pGeode->displayPitch = ((pScrni->virtualX + 3) & ~3) * (pScrni->bitsPerPixel >> 3); pGeode->Pitch = pGeode->displayPitch; pGeode->displayWidth = pGeode->displayPitch / bytpp; pScrni->displayWidth = pGeode->displayWidth; fbavail = pGeode->FBAvail - 0x4000; pGeode->displayOffset = fboffset = 0; pGeode->displaySize = pScrni->virtualY * pGeode->displayPitch; fbavail -= pGeode->displaySize; fboffset += pGeode->displaySize; if (pGeode->tryCompression) { size = pScrni->virtualY * GX_CB_PITCH; if (size <= fbavail) { pGeode->CBData.compression_offset = fboffset; fboffset += size; fbavail -= size; pGeode->Compression = TRUE; } else { xf86DrvMsg(pScrni->scrnIndex, X_ERROR, "Not enough memory for compression\n"); pGeode->Compression = FALSE; } } if (pGeode->tryHWCursor) { pGeode->CursorSize = 1024; if (pGeode->CursorSize <= fbavail) { pGeode->CursorStartOffset = fboffset; fboffset += pGeode->CursorSize; fbavail -= pGeode->CursorSize; pGeode->HWCursor = TRUE; } else { xf86DrvMsg(pScrni->scrnIndex, X_ERROR, "Not enough memory for the hardware cursor\n"); pGeode->HWCursor = FALSE; } } if (!pGeode->NoAccel) { if (pGeode->useEXA) GXInitEXAMemory(pScrni, &fboffset, &fbavail); else GXInitXAAMemory(pScrni, &fboffset, &fbavail); } pGeode->shadowSize = 0; if (rotate != RR_Rotate_0) { if (rotate & (RR_Rotate_90 | RR_Rotate_270)) size = pGeode->displayPitch * pScrni->virtualX; else size = pGeode->displayPitch * pScrni->virtualY; if (size <= fbavail) { pGeode->shadowOffset = fboffset; pGeode->shadowSize = size; fboffset += size; fbavail -= size; } else { xf86DrvMsg(pScrni->scrnIndex, X_ERROR, "Not enough memory for the shadow framebuffer\n"); ret = FALSE; } } /* XAA always exists - we can't remove it on demand like we can with EXA. So we assume the worse, and only give XAA enough offspace room to account for any eventuality that RandR might throw at us. */ if (!pGeode->NoAccel) { if (pGeode->useEXA && pGeode->pExa) { ExaDriverPtr pExa = pGeode->pExa; pExa->offScreenBase = fboffset; pExa->memorySize = fboffset + fbavail; } if (!pGeode->useEXA) { if (!xf86FBManagerRunning(pScrn)) { unsigned int offset = fboffset; unsigned int avail = fbavail; RegionRec OffscreenRegion; BoxRec AvailBox; /* Assume the shadow FB exists even if it doesnt */ if (pGeode->shadowSize == 0) { size = (pScrn->width * bytpp) * pScrni->virtualX; offset += size; avail -= size; } AvailBox.x1 = 0; AvailBox.y1 = (offset + pGeode->displayPitch - 1) / pGeode->displayPitch; AvailBox.x2 = pGeode->displayWidth; AvailBox.y2 = (offset + avail) / pGeode->displayPitch; if (AvailBox.y1 < AvailBox.y2) { REGION_INIT(pScrn, &OffscreenRegion, &AvailBox, 2); if (!xf86InitFBManagerRegion(pScrn, &OffscreenRegion)) xf86DrvMsg(pScrni->scrnIndex, X_ERROR, "Memory manager initialization failed.\n"); REGION_UNINIT(pScrn, &OffscreenRegion); } else xf86DrvMsg(pScrni->scrnIndex, X_INFO, "Cache disabled - no offscreen memory available.\n"); } else xf86DrvMsg(pScrni->scrnIndex, X_INFO, "XAA offscreen memory has already been allocated.\n"); } } return ret; } static Bool GXSaveScreen(ScreenPtr pScrn, int mode) { ScrnInfoPtr pScrni = xf86Screens[pScrn->myNum]; GeodePtr pGeode = GEODEPTR(pScrni); if (pGeode->useVGA && !pScrni->vtSema) return vgaHWSaveScreen(pScrn, mode); return TRUE; } /* Common function - used by the LX too */ extern unsigned long gfx_gx2_scratch_base; static Bool GXMapMem(ScrnInfoPtr pScrni) { GeodeRec *pGeode = GEODEPTR(pScrni); int index = pScrni->scrnIndex; pciVideoRec *pci = xf86GetPciInfoForEntity(pGeode->pEnt->index); gfx_virt_regptr = (unsigned char *)xf86MapVidMem(index, VIDMEM_MMIO, pci->memBase[2], pci->size[2]); gfx_virt_gpptr = (unsigned char *)xf86MapVidMem(index, VIDMEM_MMIO, pci->memBase[1], pci->size[1]); gfx_virt_vidptr = (unsigned char *)xf86MapVidMem(index, VIDMEM_MMIO, pci->memBase[3], pci->size[3]); gfx_virt_fbptr = (unsigned char *)xf86MapVidMem(index, VIDMEM_FRAMEBUFFER, pci->memBase[0], pGeode->FBAvail); gfx_gx2_scratch_base = pGeode->FBAvail - 0x4000; XpressROMPtr = xf86MapVidMem(index, VIDMEM_FRAMEBUFFER, 0xF0000, 0x10000); pGeode->FBBase = gfx_virt_fbptr; if ((!gfx_virt_regptr) || (!gfx_virt_gpptr) || (!gfx_virt_vidptr) || (!gfx_virt_fbptr)) return FALSE; if (!pGeode->NoAccel && pGeode->useEXA) pGeode->pExa->memoryBase = pGeode->FBBase; xf86DrvMsg(index, X_INFO, "Found Geode %x %p\n", pGeode->FBAvail, pGeode->FBBase); return TRUE; } /* Check to see if VGA exists - we map the space and look for a signature - if it doesn't match exactly, then we assume no VGA. */ static Bool GXCheckVGA(ScrnInfoPtr pScrni) { const char *vgasig = "IBM VGA Compatible"; vgaHWPtr pvgaHW = VGAHWPTR(pScrni); int ret; if (!vgaHWMapMem(pScrni)) return FALSE; ret = memcmp(pvgaHW->Base + 0x1E, vgasig, strlen(vgasig)); vgaHWUnmapMem(pScrni); return ret ? FALSE : TRUE; } static Bool GXPreInit(ScrnInfoPtr pScrni, int flags) { GeodePtr pGeode; ClockRangePtr GeodeClockRange; OptionInfoRec *GeodeOptions = &GX_GeodeOptions[0]; int ret; QQ_WORD msrValue; rgb defaultWeight = { 0, 0, 0 }; int modecnt; char *s, *panelgeo; pGeode = pScrni->driverPrivate = xnfcalloc(sizeof(GeodeRec), 1); if (pGeode == NULL) return FALSE; /* Probe for VGA */ pGeode->useVGA = FALSE; if (xf86LoadSubModule(pScrni, "vgahw")) { if (vgaHWGetHWRec(pScrni)) { pGeode->useVGA = GXCheckVGA(pScrni); } } #if INT10_SUPPORT if (pGeode->useVGA) pGeode->vesa = xcalloc(sizeof(VESARec), 1); #endif if (pScrni->numEntities != 1) return FALSE; pGeode->pEnt = xf86GetEntityInfo(pScrni->entityList[0]); if (pGeode->pEnt->resources) return FALSE; /* ISSUE - this won't work without VGA, but its too early to know if we can use VGA or not */ if (pGeode->useVGA && (flags & PROBE_DETECT)) { GeodeProbeDDC(pScrni, pGeode->pEnt->index); return TRUE; } gfx_msr_init(); ret = gfx_msr_read(RC_ID_DF, MBD_MSR_CONFIG, &msrValue); if (!ret) { pGeode->Output = ((msrValue.low & RCDF_CONFIG_FMT_MASK) == RCDF_CONFIG_FMT_FP) ? OUTPUT_CRT : OUTPUT_PANEL; } /* Fill in the monitor information */ pScrni->monitor = pScrni->confScreen->monitor; if (!xf86SetDepthBpp(pScrni, 8, 8, 8, Support24bppFb | Support32bppFb)) return FALSE; switch (pScrni->depth) { case 8: pScrni->rgbBits = 8; case 16: case 24: case 32: break; default: xf86DrvMsg(pScrni->scrnIndex, X_ERROR, "The driver does not support %d as a depth.\n", pScrni->depth); return FALSE; } xf86PrintDepthBpp(pScrni); if (!xf86SetWeight(pScrni, defaultWeight, defaultWeight)) return FALSE; if (!xf86SetDefaultVisual(pScrni, -1)) return FALSE; pScrni->progClock = TRUE; xf86CollectOptions(pScrni, NULL); xf86ProcessOptions(pScrni->scrnIndex, pScrni->options, GeodeOptions); /* Set up our various options that may get reversed as we go on */ pGeode->FBVGAActive = FALSE; pGeode->tryHWCursor = TRUE; pGeode->tryCompression = TRUE; pGeode->NoAccel = FALSE; pGeode->useEXA = TRUE; pGeode->Panel = (pGeode->Output & OUTPUT_PANEL) ? TRUE : FALSE; pGeode->NoOfImgBuffers = DEFAULT_IMG_LINE_BUFS; pGeode->NoOfColorExpandLines = DEFAULT_CLR_LINE_BUFS; pGeode->exaBfrSz = DEFAULT_EXA_SCRATCH_BFRSZ; xf86GetOptValBool(GeodeOptions, GX_OPTION_HW_CURSOR, &pGeode->tryHWCursor); if (!xf86GetOptValInteger(GeodeOptions, GX_OPTION_FBSIZE, &(pGeode->FBAvail))) pGeode->FBAvail = 0; /* For compatability - allow SWCursor too */ if (xf86ReturnOptValBool(GeodeOptions, GX_OPTION_SW_CURSOR, FALSE)) pGeode->tryHWCursor = FALSE; if (xf86ReturnOptValBool(GeodeOptions, GX_OPTION_NOCOMPRESSION, FALSE)) pGeode->tryCompression = FALSE; if (xf86ReturnOptValBool(GeodeOptions, GX_OPTION_NOACCEL, FALSE)) pGeode->NoAccel = TRUE; pGeode->rotation = RR_Rotate_0; if ((s = xf86GetOptValString(GeodeOptions, GX_OPTION_ROTATE))) { if (!xf86NameCmp(s, "LEFT")) pGeode->rotation = RR_Rotate_90; else if (!xf86NameCmp(s, "INVERT")) pGeode->rotation = RR_Rotate_180; else if (!xf86NameCmp(s, "CCW")) pGeode->rotation = RR_Rotate_270; else xf86DrvMsg(pScrni->scrnIndex, X_ERROR, "Invalid rotation %s.\n", s); } xf86GetOptValInteger(GeodeOptions, GX_OPTION_OSM_IMG_BUFS, &(pGeode->NoOfImgBuffers)); if (pGeode->NoOfImgBuffers <= 0) pGeode->NoOfImgBuffers = 0; xf86GetOptValInteger(GeodeOptions, GX_OPTION_OSM_CLR_BUFS, &(pGeode->NoOfColorExpandLines)); if (pGeode->NoOfColorExpandLines <= 0) pGeode->NoOfColorExpandLines = 0; xf86GetOptValInteger(GeodeOptions, GX_OPTION_OSM_CLR_BUFS, &(pGeode->exaBfrSz)); if (pGeode->exaBfrSz <= 0) pGeode->exaBfrSz = 0; if (pGeode->Panel == TRUE) { if (xf86ReturnOptValBool(GeodeOptions, GX_OPTION_NOPANEL, FALSE)) pGeode->Panel = FALSE; } panelgeo = xf86GetOptValString(GeodeOptions, GX_OPTION_PANEL_GEOMETRY); if ((s = xf86GetOptValString(GeodeOptions, GX_OPTION_ACCEL_METHOD))) { if (!xf86NameCmp(s, "XAA")) pGeode->useEXA = FALSE; else if (xf86NameCmp(s, "EXA")) xf86DrvMsg(pScrni->scrnIndex, X_ERROR, "Unknown accleration method %s. Defaulting to EXA.\n", s); } xf86DrvMsg(pScrni->scrnIndex, X_INFO, "Using %s acceleration architecture\n", pGeode->useEXA ? "EXA" : "XAA"); /* Set up the panel */ if (dcon_init(pScrni)) { pGeode->Panel = TRUE; } else if (pGeode->Panel) { if (panelgeo != NULL) { if (GeodeGetFPGeometry(panelgeo, &pGeode->PanelX, &pGeode->PanelY)) pGeode->Panel = FALSE; } #ifdef PNL_SUP else { int b, f; /* The bitdepth and refresh isn't used anywhere else in the driver */ if ((pGeode->Panel = Pnl_IsPanelEnabledInBIOS())) Pnl_GetPanelInfoFromBIOS(&pGeode->PanelX, &pGeode->PanelY, &b, &f); } #endif } /* Set up the VGA */ if (pGeode->useVGA) { #if INT10_SUPPORT VESARec *pVesa; if (!xf86LoadSubModule(pScrni, "int10")) return FALSE; xf86LoaderReqSymLists(amdInt10Symbols, NULL); pVesa = pGeode->vesa; if ((pVesa->pInt = xf86InitInt10(pGeode->pEnt->index)) == NULL) { xf86DrvMsg(pScrni->scrnIndex, X_ERROR, "Unable to initialize 1NT10 support\n"); pGeode->useVGA = FALSE; } #endif xf86LoaderReqSymLists(amdVgahwSymbols, NULL); pGeode->FBVGAActive = gu2_get_vga_active(); } if (pGeode->FBAvail == 0) pGeode->FBAvail = gfx_get_frame_buffer_size(); if (pScrni->memPhysBase == 0) pScrni->memPhysBase = gfx_get_frame_buffer_base(); pScrni->fbOffset = 0; if (pGeode->pEnt->device->videoRam == 0) pScrni->videoRam = pGeode->FBAvail / 1024; else pScrni->videoRam = pGeode->pEnt->device->videoRam; pGeode->maxWidth = GX_MAX_WIDTH; pGeode->maxHeight = GX_MAX_HEIGHT; GeodeClockRange = (ClockRangePtr) xnfcalloc(sizeof(ClockRange), 1); GeodeClockRange->next = NULL; GeodeClockRange->minClock = 25175; GeodeClockRange->maxClock = 229500; GeodeClockRange->clockIndex = -1; GeodeClockRange->interlaceAllowed = TRUE; GeodeClockRange->doubleScanAllowed = FALSE; if (pGeode->useVGA) pScrni->monitor->DDC = GeodeDoDDC(pScrni, pGeode->pEnt->index); else pScrni->monitor->DDC = NULL; /* I'm still not 100% sure this uses the right values */ modecnt = xf86ValidateModes(pScrni, pScrni->monitor->Modes, pScrni->display->modes, GeodeClockRange, NULL, GX_MIN_PITCH, GX_MAX_PITCH, 32, GX_MIN_HEIGHT, GX_MAX_HEIGHT, pScrni->display->virtualX, pScrni->display->virtualY, pGeode->FBAvail, LOOKUP_BEST_REFRESH); if (modecnt <= 0) { xf86DrvMsg(pScrni->scrnIndex, X_ERROR, "No valid modes were found\n"); return FALSE; } xf86PruneDriverModes(pScrni); if (pScrni->modes == NULL) { xf86DrvMsg(pScrni->scrnIndex, X_ERROR, "No valid modes were found\n"); return FALSE; } xf86SetCrtcForModes(pScrni, 0); pScrni->currentMode = pScrni->modes; xf86PrintModes(pScrni); xf86SetDpi(pScrni, 0, 0); /* Load the modules we'll need */ if (xf86LoadSubModule(pScrni, "fb") == NULL) { return FALSE; } xf86LoaderReqSymLists(amdFbSymbols, NULL); if (pGeode->NoAccel == FALSE) { const char *module = (pGeode->useEXA) ? "exa" : "xaa"; const char **symbols = (pGeode->useEXA) ? &amdExaSymbols[0] : &amdXaaSymbols[0]; if (!xf86LoadSubModule(pScrni, module)) { return FALSE; } xf86LoaderReqSymLists(symbols, NULL); } if (pGeode->tryHWCursor == TRUE) { if (!xf86LoadSubModule(pScrni, "ramdac")) { return FALSE; } xf86LoaderReqSymLists(amdRamdacSymbols, NULL); } if (xf86RegisterResources(pGeode->pEnt->index, NULL, ResExclusive)) { xf86DrvMsg(pScrni->scrnIndex, X_ERROR, "Couldn't register the resources.\n"); return FALSE; } return TRUE; } static void GXRestore(ScrnInfoPtr pScrni) { GeodeRec *pGeode = GEODEPTR(pScrni); if (pGeode->useVGA && pGeode->FBVGAActive) { vgaHWPtr pvgaHW = VGAHWPTR(pScrni); vgaHWProtect(pScrni, TRUE); vgaHWRestore(pScrni, &pvgaHW->SavedReg, VGA_SR_ALL); vgaHWProtect(pScrni, FALSE); } } static Bool GXUnmapMem(ScrnInfoPtr pScrni) { GeodeRec *pGeode = GEODEPTR(pScrni); /* unmap all the memory map's */ xf86UnMapVidMem(pScrni->scrnIndex, gfx_virt_regptr, GX_CPU_REG_SIZE); xf86UnMapVidMem(pScrni->scrnIndex, gfx_virt_gpptr, GX_GP_REG_SIZE); xf86UnMapVidMem(pScrni->scrnIndex, gfx_virt_vidptr, GX_VID_REG_SIZE); xf86UnMapVidMem(pScrni->scrnIndex, gfx_virt_fbptr, pGeode->FBAvail); return TRUE; } static void GXSetDvLineSize(unsigned int pitch) { unsigned long temp, dv_size = MDC_DV_LINE_SIZE_1024; if (pitch > 1024) { dv_size = MDC_DV_LINE_SIZE_2048; } if (pitch > 2048) { dv_size = MDC_DV_LINE_SIZE_4096; } if (pitch > 4096) { dv_size = MDC_DV_LINE_SIZE_8192; } /* WRITE DIRTY/VALID CONTROL WITH LINE LENGTH */ temp = READ_REG32(MDC_DV_CTL); WRITE_REG32(MDC_DV_CTL, (temp & ~MDC_DV_LINE_SIZE_MASK) | dv_size); } /* XXX - this is nothing like the original function - not sure exactly what the purpose is for this quite yet */ static void GXAdjustFrame(int scrnIndex, int x, int y, int flags) { ScrnInfoPtr pScrni = xf86Screens[scrnIndex]; GeodeRec *pGeode = GEODEPTR(pScrni); unsigned long offset; offset = pGeode->FBOffset + y * pGeode->Pitch + x * (pScrni->bitsPerPixel >> 3); gfx_set_display_offset(offset); } static Bool GXSetVideoMode(ScrnInfoPtr pScrni, DisplayModePtr pMode) { GeodeRec *pGeode = GEODEPTR(pScrni); int flags = 0; pScrni->vtSema = TRUE; gx_disable_dac_power(); if (pMode->Flags & V_NHSYNC) flags |= 1; if (pMode->Flags & V_NVSYNC) flags |= 2; /* XXX Question - why even use set_display_mode at all - shouldn't the * mode timings be the same that we advertise? */ /* Only use the panel mode for built in modes */ if ((pMode->type && pMode->type != M_T_USERDEF) && pGeode->Panel) { GFX(set_fixed_timings(pGeode->PanelX, pGeode->PanelY, pMode->CrtcHDisplay, pMode->CrtcVDisplay, pScrni->bitsPerPixel)); } else { if (pGeode->Panel) GFX(set_panel_present(pGeode->PanelX, pGeode->PanelY, pMode->CrtcHDisplay, pMode->CrtcVDisplay, pScrni->bitsPerPixel)); GFX(set_display_timings(pScrni->bitsPerPixel, flags, pMode->CrtcHDisplay, pMode->CrtcHBlankStart, pMode->CrtcHSyncStart, pMode->CrtcHSyncEnd, pMode->CrtcHBlankEnd, pMode->CrtcHTotal, pMode->CrtcVDisplay, pMode->CrtcVBlankStart, pMode->CrtcVSyncStart, pMode->CrtcVSyncEnd, pMode->CrtcVBlankEnd, pMode->CrtcVTotal, (int)((pMode->SynthClock / 1000.0) * 0x10000))); } GFX(set_crt_enable(CRT_ENABLE)); GFX(set_display_pitch(pGeode->displayPitch)); GFX(set_display_offset(0L)); GFX(wait_vertical_blank()); if (pGeode->Compression) { GXSetDvLineSize(pGeode->Pitch); gfx_set_compression_offset(pGeode->CBData.compression_offset); gfx_set_compression_pitch(GX_CB_PITCH); gfx_set_compression_size(GX_CB_SIZE); gfx_set_compression_enable(1); } if (pGeode->HWCursor && !(pMode->Flags & V_DBLSCAN)) { GXLoadCursorImage(pScrni, NULL); GFX(set_cursor_position(pGeode->CursorStartOffset, 0, 0, 0, 0)); GXShowCursor(pScrni); } else { GFX(set_cursor_enable(0)); pGeode->HWCursor = FALSE; } GXAdjustFrame(pScrni->scrnIndex, pScrni->frameX0, pScrni->frameY0, 0); gx_enable_dac_power(); return TRUE; } static Bool GXSwitchMode(int index, DisplayModePtr pMode, int flags) { ScrnInfoPtr pScrni = xf86Screens[index]; GeodeRec *pGeode = GEODEPTR(pScrni); int ret = TRUE; int rotate; /* Syn the engine and shutdown the DAC momentarily */ gfx_wait_until_idle(); /* Set up the memory for the new mode */ rotate = GXGetRotation(pScrni->pScreen); ret = GXAllocateMemory(pScrni->pScreen, pScrni, rotate); if (ret) { if (pGeode->curMode != pMode) ret = GXSetVideoMode(pScrni, pMode); } if (ret) ret = GXRotate(pScrni, pMode); /* Go back the way it was */ if (ret == FALSE) { if (!GXSetVideoMode(pScrni, pGeode->curMode)) xf86DrvMsg(pScrni->scrnIndex, X_ERROR, "Could not restore the previous mode\n"); } else pGeode->curMode = pMode; return ret; } static void GXLeaveGraphics(ScrnInfoPtr pScrni) { GeodeRec *pGeode = GEODEPTR(pScrni); gfx_wait_until_idle(); /* Restore VG registers */ gfx_set_display_timings(pGeode->FBgfxdisplaytiming.wBpp, pGeode->FBgfxdisplaytiming.wPolarity, pGeode->FBgfxdisplaytiming.wHActive, pGeode->FBgfxdisplaytiming.wHBlankStart, pGeode->FBgfxdisplaytiming.wHSyncStart, pGeode->FBgfxdisplaytiming.wHSyncEnd, pGeode->FBgfxdisplaytiming.wHBlankEnd, pGeode->FBgfxdisplaytiming.wHTotal, pGeode->FBgfxdisplaytiming.wVActive, pGeode->FBgfxdisplaytiming.wVBlankStart, pGeode->FBgfxdisplaytiming.wVSyncStart, pGeode->FBgfxdisplaytiming.wVSyncEnd, pGeode->FBgfxdisplaytiming.wVBlankEnd, pGeode->FBgfxdisplaytiming.wVTotal, pGeode->FBgfxdisplaytiming.dwDotClock); gfx_set_compression_enable(0); /* Restore the previous Compression state */ if (pGeode->FBCompressionEnable) { gfx_set_compression_offset(pGeode->FBCompressionOffset); gfx_set_compression_pitch(pGeode->FBCompressionPitch); gfx_set_compression_size(pGeode->FBCompressionSize); gfx_set_compression_enable(1); } gfx_set_display_pitch(pGeode->FBgfxdisplaytiming.wPitch); gfx_set_display_offset(pGeode->FBDisplayOffset); /* Restore Cursor */ gfx_set_cursor_position(pGeode->FBCursorOffset, 0, 0, 0, 0); if (pGeode->useVGA) { pGeode->vesa->pInt->num = 0x10; pGeode->vesa->pInt->ax = 0x0 | pGeode->FBBIOSMode; pGeode->vesa->pInt->bx = 0; xf86ExecX86int10(pGeode->vesa->pInt); gfx_delay_milliseconds(3); } GXRestore(pScrni); gx_enable_dac_power(); } static Bool GXCloseScreen(int scrnIndex, ScreenPtr pScrn) { ScrnInfoPtr pScrni = xf86Screens[scrnIndex]; GeodeRec *pGeode = GEODEPTR(pScrni); if (pScrni->vtSema) GXLeaveGraphics(pScrni); if (pGeode->AccelInfoRec) XAADestroyInfoRec(pGeode->AccelInfoRec); if (pGeode->AccelImageWriteBuffers) { xfree(pGeode->AccelImageWriteBuffers[0]); xfree(pGeode->AccelImageWriteBuffers); pGeode->AccelImageWriteBuffers = NULL; } if (pGeode->AccelColorExpandBuffers) { xfree(pGeode->AccelColorExpandBuffers); pGeode->AccelColorExpandBuffers = NULL; } if (pGeode->pExa) { exaDriverFini(pScrn); xfree(pGeode->pExa); pGeode->pExa = NULL; } pScrni->vtSema = FALSE; GXUnmapMem(pScrni); pScrni->PointerMoved = pGeode->PointerMoved; pScrn->CloseScreen = pGeode->CloseScreen; if (pScrn->CloseScreen) return (*pScrn->CloseScreen)(scrnIndex, pScrn); return TRUE; } static Bool GXEnterGraphics(ScreenPtr pScrn, ScrnInfoPtr pScrni) { GeodeRec *pGeode = GEODEPTR(pScrni); if (!GXMapMem(pScrni)) return FALSE; gfx_wait_until_idle(); /* Save off the current state (should this be somewhere else)? */ pGeode->FBgfxdisplaytiming.dwDotClock = gfx_get_clock_frequency(); pGeode->FBgfxdisplaytiming.wPitch = gfx_get_display_pitch(); pGeode->FBgfxdisplaytiming.wBpp = gfx_get_display_bpp(); pGeode->FBgfxdisplaytiming.wHTotal = gfx_get_htotal(); pGeode->FBgfxdisplaytiming.wHActive = gfx_get_hactive(); pGeode->FBgfxdisplaytiming.wHSyncStart = gfx_get_hsync_start(); pGeode->FBgfxdisplaytiming.wHSyncEnd = gfx_get_hsync_end(); pGeode->FBgfxdisplaytiming.wHBlankStart = gfx_get_hblank_start(); pGeode->FBgfxdisplaytiming.wHBlankEnd = gfx_get_hblank_end(); pGeode->FBgfxdisplaytiming.wVTotal = gfx_get_vtotal(); pGeode->FBgfxdisplaytiming.wVActive = gfx_get_vactive(); pGeode->FBgfxdisplaytiming.wVSyncStart = gfx_get_vsync_start(); pGeode->FBgfxdisplaytiming.wVSyncEnd = gfx_get_vsync_end(); pGeode->FBgfxdisplaytiming.wVBlankStart = gfx_get_vblank_start(); pGeode->FBgfxdisplaytiming.wVBlankEnd = gfx_get_vblank_end(); pGeode->FBgfxdisplaytiming.wPolarity = gfx_get_sync_polarities(); pGeode->FBDisplayOffset = gfx_get_display_offset(); if (pGeode->useVGA) { vgaHWPtr pvgaHW = VGAHWPTR(pScrni); pGeode->FBBIOSMode = pvgaHW->readCrtc(pvgaHW, 0x040); } pGeode->FBCompressionEnable = gfx_get_compression_enable(); pGeode->FBCompressionOffset = gfx_get_compression_offset(); pGeode->FBCompressionPitch = gfx_get_compression_pitch(); pGeode->FBCompressionSize = gfx_get_compression_size(); #ifdef PNL_SUP Pnl_SavePanelState(); #endif /* Turn off the VGA */ if (pGeode->useVGA && pGeode->FBVGAActive) { unsigned short sequencer; vgaHWPtr pvgaHW = VGAHWPTR(pScrni); /* Map VGA aperture */ if (!vgaHWMapMem(pScrni)) return FALSE; /* Unlock VGA registers */ vgaHWUnlock(pvgaHW); /* Save the current state and setup the current mode */ vgaHWSave(pScrni, &VGAHWPTR(pScrni)->SavedReg, VGA_SR_ALL); /* DISABLE VGA SEQUENCER */ /* This allows the VGA state machine to terminate. We must delay */ /* such that there are no pending MBUS requests. */ gfx_outb(MDC_SEQUENCER_INDEX, MDC_SEQUENCER_CLK_MODE); sequencer = gfx_inb(MDC_SEQUENCER_DATA); sequencer |= MDC_CLK_MODE_SCREEN_OFF; gfx_outb(MDC_SEQUENCER_DATA, sequencer); gfx_delay_milliseconds(1); /* BLANK THE VGA DISPLAY */ gfx_outw(MDC_SEQUENCER_INDEX, MDC_SEQUENCER_RESET); sequencer = gfx_inb(MDC_SEQUENCER_DATA); sequencer &= ~MDC_RESET_VGA_DISP_ENABLE; gfx_outb(MDC_SEQUENCER_DATA, sequencer); gfx_delay_milliseconds(1); } /* Set up the memory */ /* XXX - FIXME - when we alow inital rotation, it should be here */ GXAllocateMemory(pScrn, pScrni, pGeode->rotation); /* Clear the framebuffer */ memset(pGeode->FBBase + pGeode->displayOffset, 0, pGeode->displaySize); /* Set up the video mode */ GXSetVideoMode(pScrni, pScrni->currentMode); pGeode->curMode = pScrni->currentMode; return TRUE; } static void GXLoadPalette(ScrnInfoPtr pScrni, int numColors, int *indizes, LOCO * colors, VisualPtr pVisual) { int i, index, color; for (i = 0; i < numColors; i++) { index = indizes[i] & 0xFF; color = (((unsigned long)(colors[index].red & 0xFF)) << 16) | (((unsigned long)(colors[index].green & 0xFF)) << 8) | ((unsigned long)(colors[index].blue & 0xFF)); GFX(set_display_palette_entry(index, color)); } } #ifdef DPMSExtension static void GXPanelPower(int enable) { unsigned long power = READ_VID32(RCDF_POWER_MANAGEMENT); if (enable != 0) power |= RCDF_PM_PANEL_POWER_ON; else power &= ~RCDF_PM_PANEL_POWER_ON; WRITE_VID32(RCDF_POWER_MANAGEMENT, power); } static void GXDPMSSet(ScrnInfoPtr pScrni, int mode, int flags) { GeodeRec *pGeode; pGeode = GEODEPTR(pScrni); if (!pScrni->vtSema) return; switch (mode) { case DPMSModeOn: /* Screen: On; HSync: On; VSync: On */ GFX(set_crt_enable(CRT_ENABLE)); #if defined(PNL_SUP) if (pGeode->Panel) { Pnl_PowerUp(); GXPanelPower(1); } #endif break; case DPMSModeStandby: /* Screen: Off; HSync: Off; VSync: On */ GFX(set_crt_enable(CRT_STANDBY)); #if defined(PNL_SUP) if (pGeode->Panel) { Pnl_PowerDown(); GXPanelPower(0); } #endif break; case DPMSModeSuspend: /* Screen: Off; HSync: On; VSync: Off */ GFX(set_crt_enable(CRT_SUSPEND)); #if defined(PNL_SUP) if (pGeode->Panel) { Pnl_PowerDown(); GXPanelPower(0); } #endif break; case DPMSModeOff: /* Screen: Off; HSync: Off; VSync: Off */ GFX(set_crt_enable(CRT_DISABLE)); #if defined(PNL_SUP) if (pGeode->Panel) { Pnl_PowerDown(); GXPanelPower(0); } #endif break; } } #endif static Bool GXCreateScreenResources(ScreenPtr pScreen) { ScrnInfoPtr pScrni = xf86Screens[pScreen->myNum]; GeodeRec *pGeode = GEODEPTR(pScrni); pScreen->CreateScreenResources = pGeode->CreateScreenResources; if (!(*pScreen->CreateScreenResources) (pScreen)) return FALSE; if (pGeode->rotation != RR_Rotate_0) { RRScreenSize p; Rotation requestedRotation = pGeode->rotation; pGeode->rotation = RR_Rotate_0; /* Just setup enough for an initial rotate */ p.width = pScreen->width; p.height = pScreen->height; p.mmWidth = pScreen->mmWidth; p.mmHeight = pScreen->mmHeight; pGeode->starting = TRUE; GXRandRSetConfig(pScreen, requestedRotation, 0, &p); pGeode->starting = FALSE; } return TRUE; } static Bool GXScreenInit(int scrnIndex, ScreenPtr pScrn, int argc, char **argv) { ScrnInfoPtr pScrni = xf86Screens[scrnIndex]; GeodeRec *pGeode = GEODEPTR(pScrni); XF86ModReqInfo shadowReq; int maj, min, ret, rotate; pGeode->starting = TRUE; /* If we are using VGA then go ahead and map the memory */ if (pGeode->useVGA) { if (!vgaHWMapMem(pScrni)) return FALSE; vgaHWGetIOBase(VGAHWPTR(pScrni)); } if (!pGeode->NoAccel) { if (pGeode->useEXA) { if (!(pGeode->pExa = xnfcalloc(sizeof(ExaDriverRec), 1))) { xf86DrvMsg(scrnIndex, X_ERROR, "Couldn't allocate the EXA structure.\n"); pGeode->NoAccel = TRUE; } else { ExaDriverPtr pExa = pGeode->pExa; /* THis is set in GXAllocMem */ pExa->memoryBase = 0; /* This is set in GXAllocateMemory */ pExa->memorySize = 0; pExa->pixmapOffsetAlign = 32; pExa->pixmapPitchAlign = 32; pExa->flags = EXA_OFFSCREEN_PIXMAPS; pExa->maxX = pGeode->maxWidth - 1; pExa->maxY = pGeode->maxHeight - 1; } } else { pGeode->AccelImageWriteBuffers = xcalloc(sizeof(pGeode->AccelImageWriteBuffers[0]), pGeode->NoOfImgBuffers); pGeode->AccelColorExpandBuffers = xcalloc(sizeof(pGeode->AccelColorExpandBuffers[0]), pGeode->NoOfColorExpandLines); } } /* XXX FIXME - Take down any of the structures on failure? */ if (!GXEnterGraphics(pScrn, pScrni)) return FALSE; miClearVisualTypes(); /* XXX Again - take down anything? */ if (pScrni->bitsPerPixel > 8) { if (!miSetVisualTypes(pScrni->depth, TrueColorMask, pScrni->rgbBits, pScrni->defaultVisual)) { return FALSE; } } else { if (!miSetVisualTypes(pScrni->depth, miGetDefaultVisualMask(pScrni->depth), pScrni->rgbBits, pScrni->defaultVisual)) { return FALSE; } } miSetPixmapDepths(); /* Point at the visible area to start */ ret = fbScreenInit(pScrn, pGeode->FBBase + pGeode->displayOffset, pScrni->virtualX, pScrni->virtualY, pScrni->xDpi, pScrni->yDpi, pGeode->displayWidth, pScrni->bitsPerPixel); if (!ret) return FALSE; xf86SetBlackWhitePixels(pScrn); /* Set up the color ordering */ if (pScrni->bitsPerPixel > 8) { VisualPtr visual = pScrn->visuals + pScrn->numVisuals; while (--visual >= pScrn->visuals) { if ((visual->class | DynamicClass) == DirectColor) { visual->offsetRed = pScrni->offset.red; visual->offsetGreen = pScrni->offset.green; visual->offsetBlue = pScrni->offset.blue; visual->redMask = pScrni->mask.red; visual->greenMask = pScrni->mask.green; visual->blueMask = pScrni->mask.blue; } } } /* Must follow the color ordering */ fbPictureInit(pScrn, 0, 0); if (!pGeode->NoAccel) GXAccelInit(pScrn); miInitializeBackingStore(pScrn); xf86SetBackingStore(pScrn); /* Set up the soft cursor */ miDCInitialize(pScrn, xf86GetPointerScreenFuncs()); /* Set up the HW cursor - must follow the soft cursor init */ if (pGeode->tryHWCursor) { if (!GXHWCursorInit(pScrn)) xf86DrvMsg(scrnIndex, X_ERROR, "Hardware cursor initialization failed.\n"); } /* Set up the color map */ if (!miCreateDefColormap(pScrn)) return FALSE; if (pScrni->bitsPerPixel == 8) { /* Must follow initialization of the default colormap */ if (!xf86HandleColormaps(pScrn, 256, 8, GXLoadPalette, NULL, CMAP_PALETTED_TRUECOLOR | CMAP_RELOAD_ON_MODE_SWITCH)) { return FALSE; } } #ifdef DPMSExtension xf86DPMSInit(pScrn, GXDPMSSet, 0); #endif GXInitVideo(pScrn); /* Set up RandR */ xf86DisableRandR(); /* We provide our own RandR goodness */ /* Try to set up the shadow FB for rotation */ memset(&shadowReq, 0, sizeof(shadowReq)); shadowReq.majorversion = 1; shadowReq.minorversion = 1; if (LoadSubModule(pScrni->module, "shadow", NULL, NULL, NULL, &shadowReq, &maj, &min)) { rotate = RR_Rotate_0 | RR_Rotate_90 | RR_Rotate_180 | RR_Rotate_270; shadowSetup(pScrn); } else { LoaderErrorMsg(NULL, "shadow", maj, min); xf86DrvMsg(pScrni->scrnIndex, X_ERROR, "Error loading shadow - rotation not available.\n"); if (pGeode->rotation != RR_Rotate_0) xf86DrvMsg(pScrni->scrnIndex, X_ERROR, "Reverting back to normal rotation.\n"); rotate = pGeode->rotation = RR_Rotate_0; } GXRandRInit(pScrn, rotate); pGeode->PointerMoved = pScrni->PointerMoved; pScrni->PointerMoved = GeodePointerMoved; pGeode->CreateScreenResources = pScrn->CreateScreenResources; pScrn->CreateScreenResources = GXCreateScreenResources; pGeode->CloseScreen = pScrn->CloseScreen; pScrn->CloseScreen = GXCloseScreen; pScrn->SaveScreen = GXSaveScreen; if (serverGeneration == 1) xf86ShowUnusedOptions(pScrni->scrnIndex, pScrni->options); pGeode->starting = FALSE; return TRUE; } static int GXValidMode(int scrnIndex, DisplayModePtr pMode, Bool Verbose, int flags) { ScrnInfoPtr pScrni = xf86Screens[scrnIndex]; GeodeRec *pGeode = GEODEPTR(pScrni); int p, ret; /* Not sure if this is an X bug or not - but on my current build, * user defined modes pass a type of 0 */ if (pMode->type && pMode->type != M_T_USERDEF) { if (pGeode->Panel) { if (pMode->CrtcHDisplay > pGeode->PanelX || pMode->CrtcVDisplay > pGeode->PanelY || gfx_is_panel_mode_supported(pGeode->PanelX, pGeode->PanelY, pMode->CrtcHDisplay, pMode->CrtcVDisplay, pScrni->bitsPerPixel) < 0) { return MODE_BAD; } } ret = gfx_is_display_mode_supported(pMode->CrtcHDisplay, pMode->CrtcVDisplay, pScrni->bitsPerPixel, GeodeGetRefreshRate(pMode)); if (ret < 0) { return MODE_BAD; } } if (pMode->Flags & V_INTERLACE) return MODE_NO_INTERLACE; if (pGeode->tryCompression) p = GeodeCalculatePitchBytes(pMode->CrtcHDisplay, pScrni->bitsPerPixel); else p = ((pMode->CrtcHDisplay + 3) & ~3) * (pScrni->bitsPerPixel >> 3); if (p * pMode->CrtcVDisplay > pGeode->FBAvail) return MODE_MEM; return MODE_OK; } /* XXX - Way more to do here */ static Bool GXEnterVT(int scrnIndex, int flags) { return GXEnterGraphics(NULL, xf86Screens[scrnIndex]); } static void GXLeaveVT(int scrnIndex, int flags) { ScrnInfoPtr pScrni = xf86Screens[scrnIndex]; GeodeRec *pGeode = GEODEPTR(pScrni); pGeode->PrevDisplayOffset = gfx_get_display_offset(); GXLeaveGraphics(xf86Screens[scrnIndex]); } void GXSetupChipsetFPtr(ScrnInfoPtr pScrn) { pScrn->PreInit = GXPreInit; pScrn->ScreenInit = GXScreenInit; pScrn->SwitchMode = GXSwitchMode; pScrn->AdjustFrame = GXAdjustFrame; pScrn->EnterVT = GXEnterVT; pScrn->LeaveVT = GXLeaveVT; pScrn->FreeScreen = GeodeFreeScreen; pScrn->ValidMode = GXValidMode; } /* ====== Common functions ====== * These are all the common functions that we use for both GX and LX - They live here * because most of them came along for the GX first, and then were adapted to the LX. * We could move these to a common function, but there is no hurry * ============================== */ void GeodePointerMoved(int index, int x, int y) { ScrnInfoPtr pScrni = xf86Screens[index]; GeodeRec *pGeode = GEODEPTR(pScrni); int newX = x, newY = y; switch (pGeode->rotation) { case RR_Rotate_0: break; case RR_Rotate_90: newX = y; newY = pScrni->pScreen->width - x - 1; break; case RR_Rotate_180: newX = pScrni->pScreen->width - x - 1; newY = pScrni->pScreen->height - y - 1; break; case RR_Rotate_270: newX = pScrni->pScreen->height - y - 1; newY = x; break; } (*pGeode->PointerMoved)(index, newX, newY); } void GeodeProbeDDC(ScrnInfoPtr pScrni, int index) { vbeInfoPtr pVbe; if (xf86LoadSubModule(pScrni, "vbe")) { pVbe = VBEInit(NULL, index); ConfiguredMonitor = vbeDoEDID(pVbe, NULL); vbeFree(pVbe); } } xf86MonPtr GeodeDoDDC(ScrnInfoPtr pScrni, int index) { vbeInfoPtr pVbe; xf86MonPtr info = NULL; if (xf86LoadSubModule(pScrni, "vbe") && (pVbe = VBEInit(NULL, index))) { xf86LoaderReqSymLists(amdVbeSymbols, NULL); info = vbeDoEDID(pVbe, NULL); xf86PrintEDID(info); xf86SetDDCproperties(pScrni, info); vbeFree(pVbe); } else xf86DrvMsg(pScrni->scrnIndex, X_INFO, "We cannot do DDC without VBE.\n"); return info; } int GeodeGetFPGeometry(const char *str, int *width, int *height) { int ret = sscanf(str, "%dx%d", width, height); return (ret == 2) ? 0 : 1; } static void GeodeFreeRec(ScrnInfoPtr pScrni) { if (pScrni->driverPrivate != NULL) { xfree(pScrni->driverPrivate); pScrni->driverPrivate = NULL; } } void GeodeFreeScreen(int scrnIndex, int flags) { GeodeRec *pGeode = GEODEPTR(xf86Screens[scrnIndex]); if (pGeode == NULL) return; if (pGeode->useVGA) { if (xf86LoaderCheckSymbol("vgaHWFreeHWRec")) vgaHWFreeHWRec(xf86Screens[scrnIndex]); } GeodeFreeRec(xf86Screens[scrnIndex]); } int GeodeCalculatePitchBytes(unsigned int width, unsigned int bpp) { int delta = width * (bpp >> 3); /* Less then 640 has doubling enabled */ if (width < 640) delta <<= 1; /* Calculate the pitch (compression rquires a power of 2) */ if (delta > 4096) delta = 8192; else if (delta > 2048) delta = 4096; else if (delta > 1024) delta = 2048; else delta = 1024; return delta; }