/* $XFree86: xc/programs/Xserver/hw/xfree86/drivers/ati/radeon_driver.c,v 1.117 2004/02/19 22:38:12 tsi Exp $ */ /* * Copyright 2000 ATI Technologies Inc., Markham, Ontario, and * VA Linux Systems Inc., Fremont, California. * * All Rights Reserved. * * 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 on 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 (including the * next paragraph) 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 * NON-INFRINGEMENT. IN NO EVENT SHALL ATI, VA LINUX SYSTEMS AND/OR * THEIR SUPPLIERS 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. */ /* * Authors: * Kevin E. Martin * Rickard E. Faith * Alan Hourihane * * Credits: * * Thanks to Ani Joshi for providing source * code to his Radeon driver. Portions of this file are based on the * initialization code for that driver. * * References: * * !!!! FIXME !!!! * RAGE 128 VR/ RAGE 128 GL Register Reference Manual (Technical * Reference Manual P/N RRG-G04100-C Rev. 0.04), ATI Technologies: April * 1999. * * RAGE 128 Software Development Manual (Technical Reference Manual P/N * SDK-G04000 Rev. 0.01), ATI Technologies: June 1999. * * This server does not yet support these XFree86 4.0 features: * !!!! FIXME !!!! * DDC1 & DDC2 * shadowfb (Note: dri uses shadowfb for another purpose in radeon_dri.c) * overlay planes * * Modified by Marc Aurele La France (tsi@xfree86.org) for ATI driver merge. * * Mergedfb and pseudo xinerama support added by Alex Deucher (agd5f@yahoo.com) * based on the sis driver by Thomas Winischhofer. * */ /* Driver data structures */ #include "radeon.h" #include "radeon_macros.h" #include "radeon_probe.h" #include "radeon_reg.h" #include "radeon_version.h" #include "radeon_mergedfb.h" #ifdef XF86DRI #define _XF86DRI_SERVER_ #include "radeon_dri.h" #include "radeon_sarea.h" #endif #include "fb.h" /* colormap initialization */ #include "micmap.h" #include "dixstruct.h" /* X and server generic header files */ #include "xf86.h" #include "xf86_OSproc.h" #include "xf86PciInfo.h" #include "xf86RAC.h" #include "xf86Resources.h" #include "xf86cmap.h" #include "vbe.h" /* fbdevhw * vgaHW definitions */ #include "fbdevhw.h" #include "vgaHW.h" #include "radeon_chipset.h" #ifndef MAX #define MAX(a,b) ((a)>(b)?(a):(b)) #endif #ifndef MIN #define MIN(a,b) ((a)>(b)?(b):(a)) #endif /* Forward definitions for driver functions */ static Bool RADEONCloseScreen(int scrnIndex, ScreenPtr pScreen); static Bool RADEONSaveScreen(ScreenPtr pScreen, int mode); static void RADEONSave(ScrnInfoPtr pScrn); static void RADEONRestore(ScrnInfoPtr pScrn); static Bool RADEONModeInit(ScrnInfoPtr pScrn, DisplayModePtr mode); static void RADEONDisplayPowerManagementSet(ScrnInfoPtr pScrn, int PowerManagementMode, int flags); static void RADEONInitDispBandwidth(ScrnInfoPtr pScrn); static void RADEONGetMergedFBOptions(ScrnInfoPtr pScrn); static int RADEONValidateMergeModes(ScrnInfoPtr pScrn); static void RADEONSetDynamicClock(ScrnInfoPtr pScrn, int mode); /* psuedo xinerama support */ extern Bool RADEONnoPanoramiXExtension; typedef enum { OPTION_NOACCEL, OPTION_SW_CURSOR, OPTION_DAC_6BIT, OPTION_DAC_8BIT, #ifdef XF86DRI OPTION_BUS_TYPE, OPTION_CP_PIO, OPTION_USEC_TIMEOUT, OPTION_AGP_MODE, OPTION_AGP_FW, OPTION_GART_SIZE, OPTION_GART_SIZE_OLD, OPTION_RING_SIZE, OPTION_BUFFER_SIZE, OPTION_DEPTH_MOVE, OPTION_PAGE_FLIP, OPTION_NO_BACKBUFFER, #endif OPTION_PANEL_OFF, OPTION_DDC_MODE, OPTION_MONITOR_LAYOUT, OPTION_IGNORE_EDID, OPTION_FBDEV, OPTION_VIDEO_KEY, OPTION_MERGEDFB, OPTION_CRT2HSYNC, OPTION_CRT2VREFRESH, OPTION_CRT2POS, OPTION_METAMODES, OPTION_MERGEDDPI, OPTION_NORADEONXINERAMA, OPTION_CRT2ISSCRN0, OPTION_DISP_PRIORITY, OPTION_PANEL_SIZE, OPTION_MIN_DOTCLOCK, #ifdef RENDER OPTION_RENDER_ACCEL, OPTION_SUBPIXEL_ORDER, #endif OPTION_SHOWCACHE, OPTION_DYNAMIC_CLOCKS, #ifdef __powerpc__ OPTION_IBOOKHACKS #endif } RADEONOpts; static const OptionInfoRec RADEONOptions[] = { { OPTION_NOACCEL, "NoAccel", OPTV_BOOLEAN, {0}, FALSE }, { OPTION_SW_CURSOR, "SWcursor", OPTV_BOOLEAN, {0}, FALSE }, { OPTION_DAC_6BIT, "Dac6Bit", OPTV_BOOLEAN, {0}, FALSE }, { OPTION_DAC_8BIT, "Dac8Bit", OPTV_BOOLEAN, {0}, TRUE }, #ifdef XF86DRI { OPTION_BUS_TYPE, "BusType", OPTV_ANYSTR, {0}, FALSE }, { OPTION_CP_PIO, "CPPIOMode", OPTV_BOOLEAN, {0}, FALSE }, { OPTION_USEC_TIMEOUT, "CPusecTimeout", OPTV_INTEGER, {0}, FALSE }, { OPTION_AGP_MODE, "AGPMode", OPTV_INTEGER, {0}, FALSE }, { OPTION_AGP_FW, "AGPFastWrite", OPTV_BOOLEAN, {0}, FALSE }, { OPTION_GART_SIZE_OLD, "AGPSize", OPTV_INTEGER, {0}, FALSE }, { OPTION_GART_SIZE, "GARTSize", OPTV_INTEGER, {0}, FALSE }, { OPTION_RING_SIZE, "RingSize", OPTV_INTEGER, {0}, FALSE }, { OPTION_BUFFER_SIZE, "BufferSize", OPTV_INTEGER, {0}, FALSE }, { OPTION_DEPTH_MOVE, "EnableDepthMoves", OPTV_BOOLEAN, {0}, FALSE }, { OPTION_PAGE_FLIP, "EnablePageFlip", OPTV_BOOLEAN, {0}, FALSE }, { OPTION_NO_BACKBUFFER, "NoBackBuffer", OPTV_BOOLEAN, {0}, FALSE }, #endif { OPTION_PANEL_OFF, "PanelOff", OPTV_BOOLEAN, {0}, FALSE }, { OPTION_DDC_MODE, "DDCMode", OPTV_BOOLEAN, {0}, FALSE }, { OPTION_MONITOR_LAYOUT, "MonitorLayout", OPTV_ANYSTR, {0}, FALSE }, { OPTION_IGNORE_EDID, "IgnoreEDID", OPTV_BOOLEAN, {0}, FALSE }, { OPTION_FBDEV, "UseFBDev", OPTV_BOOLEAN, {0}, FALSE }, { OPTION_VIDEO_KEY, "VideoKey", OPTV_INTEGER, {0}, FALSE }, { OPTION_MERGEDFB, "MergedFB", OPTV_BOOLEAN, {0}, FALSE }, { OPTION_CRT2HSYNC, "CRT2HSync", OPTV_ANYSTR, {0}, FALSE }, { OPTION_CRT2VREFRESH, "CRT2VRefresh", OPTV_ANYSTR, {0}, FALSE }, { OPTION_CRT2POS, "CRT2Position", OPTV_ANYSTR, {0}, FALSE }, { OPTION_METAMODES, "MetaModes", OPTV_ANYSTR, {0}, FALSE }, { OPTION_MERGEDDPI, "MergedDPI", OPTV_ANYSTR, {0}, FALSE }, { OPTION_NORADEONXINERAMA, "NoMergedXinerama", OPTV_BOOLEAN, {0}, FALSE }, { OPTION_CRT2ISSCRN0, "MergedXineramaCRT2IsScreen0", OPTV_BOOLEAN, {0}, FALSE }, { OPTION_DISP_PRIORITY, "DisplayPriority", OPTV_ANYSTR, {0}, FALSE }, { OPTION_PANEL_SIZE, "PanelSize", OPTV_ANYSTR, {0}, FALSE }, { OPTION_MIN_DOTCLOCK, "ForceMinDotClock", OPTV_FREQ, {0}, FALSE }, #ifdef RENDER { OPTION_RENDER_ACCEL, "RenderAccel", OPTV_BOOLEAN, {0}, FALSE }, { OPTION_SUBPIXEL_ORDER, "SubPixelOrder", OPTV_ANYSTR, {0}, FALSE }, #endif { OPTION_SHOWCACHE, "ShowCache", OPTV_BOOLEAN, {0}, FALSE }, { OPTION_DYNAMIC_CLOCKS, "DynamicClocks", OPTV_BOOLEAN, {0}, FALSE }, #ifdef __powerpc__ { OPTION_IBOOKHACKS, "iBookHacks", OPTV_BOOLEAN, {0}, FALSE }, #endif { -1, NULL, OPTV_NONE, {0}, FALSE } }; const OptionInfoRec *RADEONOptionsWeak(void) { return RADEONOptions; } static const char *vgahwSymbols[] = { "vgaHWFreeHWRec", "vgaHWGetHWRec", "vgaHWGetIndex", "vgaHWLock", "vgaHWRestore", "vgaHWSave", "vgaHWUnlock", "vgaHWGetIOBase", NULL }; static const char *fbdevHWSymbols[] = { "fbdevHWInit", "fbdevHWUseBuildinMode", "fbdevHWGetVidmem", "fbdevHWDPMSSet", /* colormap */ "fbdevHWLoadPalette", /* ScrnInfo hooks */ "fbdevHWAdjustFrame", "fbdevHWEnterVT", "fbdevHWLeaveVT", "fbdevHWModeInit", "fbdevHWRestore", "fbdevHWSave", "fbdevHWSwitchMode", "fbdevHWValidModeWeak", "fbdevHWMapMMIO", "fbdevHWMapVidmem", "fbdevHWUnmapMMIO", "fbdevHWUnmapVidmem", NULL }; static const char *ddcSymbols[] = { "xf86PrintEDID", "xf86DoEDID_DDC1", "xf86DoEDID_DDC2", NULL }; static const char *fbSymbols[] = { "fbScreenInit", "fbPictureInit", NULL }; static const char *xaaSymbols[] = { "XAACreateInfoRec", "XAADestroyInfoRec", "XAAInit", NULL }; #if 0 static const char *xf8_32bppSymbols[] = { "xf86Overlay8Plus32Init", NULL }; #endif static const char *ramdacSymbols[] = { "xf86CreateCursorInfoRec", "xf86DestroyCursorInfoRec", "xf86ForceHWCursor", "xf86InitCursor", NULL }; #ifdef XF86DRI static const char *drmSymbols[] = { "drmGetInterruptFromBusID", "drmCtlInstHandler", "drmCtlUninstHandler", "drmAddBufs", "drmAddMap", "drmAgpAcquire", "drmAgpAlloc", "drmAgpBase", "drmAgpBind", "drmAgpDeviceId", "drmAgpEnable", "drmAgpFree", "drmAgpGetMode", "drmAgpRelease", "drmAgpUnbind", "drmAgpVendorId", "drmCommandNone", "drmCommandRead", "drmCommandWrite", "drmCommandWriteRead", "drmDMA", "drmFreeVersion", "drmGetLibVersion", "drmGetVersion", "drmMap", "drmMapBufs", "drmRadeonCleanupCP", "drmRadeonClear", "drmRadeonFlushIndirectBuffer", "drmRadeonInitCP", "drmRadeonResetCP", "drmRadeonStartCP", "drmRadeonStopCP", "drmRadeonWaitForIdleCP", "drmScatterGatherAlloc", "drmScatterGatherFree", "drmUnmap", "drmUnmapBufs", NULL }; static const char *driSymbols[] = { "DRICloseScreen", "DRICreateInfoRec", "DRIDestroyInfoRec", "DRIFinishScreenInit", "DRIGetContext", "DRIGetDeviceInfo", "DRIGetSAREAPrivate", "DRILock", "DRIQueryVersion", "DRIScreenInit", "DRIUnlock", "GlxSetVisualConfigs", "DRICreatePCIBusID", NULL }; static const char *driShadowFBSymbols[] = { "ShadowFBInit", NULL }; #endif static const char *vbeSymbols[] = { "VBEInit", "vbeDoEDID", NULL }; static const char *int10Symbols[] = { "xf86InitInt10", "xf86FreeInt10", "xf86int10Addr", "xf86ExecX86int10", NULL }; static const char *i2cSymbols[] = { "xf86CreateI2CBusRec", "xf86I2CBusInit", NULL }; void RADEONLoaderRefSymLists(void) { /* * Tell the loader about symbols from other modules that this module might * refer to. */ xf86LoaderRefSymLists(vgahwSymbols, fbSymbols, xaaSymbols, #if 0 xf8_32bppSymbols, #endif ramdacSymbols, #ifdef XF86DRI drmSymbols, driSymbols, driShadowFBSymbols, #endif fbdevHWSymbols, vbeSymbols, int10Symbols, i2cSymbols, ddcSymbols, NULL); } /* Established timings from EDID standard */ static struct { int hsize; int vsize; int refresh; } est_timings[] = { {1280, 1024, 75}, {1024, 768, 75}, {1024, 768, 70}, {1024, 768, 60}, {1024, 768, 87}, {832, 624, 75}, {800, 600, 75}, {800, 600, 72}, {800, 600, 60}, {800, 600, 56}, {640, 480, 75}, {640, 480, 72}, {640, 480, 67}, {640, 480, 60}, {720, 400, 88}, {720, 400, 70}, }; static const RADEONTMDSPll default_tmds_pll[CHIP_FAMILY_LAST][4] = { {{0, 0}, {0, 0}, {0, 0}, {0, 0}}, /*CHIP_FAMILY_UNKNOW*/ {{0, 0}, {0, 0}, {0, 0}, {0, 0}}, /*CHIP_FAMILY_LEGACY*/ {{12000, 0xa1b}, {0xffffffff, 0xa3f}, {0, 0}, {0, 0}}, /*CHIP_FAMILY_RADEON*/ {{12000, 0xa1b}, {0xffffffff, 0xa3f}, {0, 0}, {0, 0}}, /*CHIP_FAMILY_RV100*/ {{0, 0}, {0, 0}, {0, 0}, {0, 0}}, /*CHIP_FAMILY_RS100*/ {{15000, 0xa1b}, {0xffffffff, 0xa3f}, {0, 0}, {0, 0}}, /*CHIP_FAMILY_RV200*/ {{12000, 0xa1b}, {0xffffffff, 0xa3f}, {0, 0}, {0, 0}}, /*CHIP_FAMILY_RS200*/ {{15000, 0xa1b}, {0xffffffff, 0xa3f}, {0, 0}, {0, 0}}, /*CHIP_FAMILY_R200*/ {{15500, 0x81b}, {0xffffffff, 0x83f}, {0, 0}, {0, 0}}, /*CHIP_FAMILY_RV250*/ {{0, 0}, {0, 0}, {0, 0}, {0, 0}}, /*CHIP_FAMILY_RS300*/ {{13000, 0x400f4}, {15000, 0x400f7}, {0xffffffff, 0x400f7/*0x40111*/}, {0, 0}}, /*CHIP_FAMILY_RV280*/ {{0xffffffff, 0xb01cb}, {0, 0}, {0, 0}, {0, 0}}, /*CHIP_FAMILY_R300*/ {{0xffffffff, 0xb01cb}, {0, 0}, {0, 0}, {0, 0}}, /*CHIP_FAMILY_R350*/ {{15000, 0xb0155}, {0xffffffff, 0xb01cb}, {0, 0}, {0, 0}}, /*CHIP_FAMILY_RV350*/ {{15000, 0xb0155}, {0xffffffff, 0xb01cb}, {0, 0}, {0, 0}}, /*CHIP_FAMILY_RV380*/ {{0xffffffff, 0xb01cb}, {0, 0}, {0, 0}, {0, 0}}, /*CHIP_FAMILY_R420*/ }; extern int getRADEONEntityIndex(void); struct RADEONInt10Save { CARD32 MEM_CNTL; CARD32 MEMSIZE; CARD32 MPP_TB_CONFIG; }; static Bool RADEONMapMMIO(ScrnInfoPtr pScrn); static Bool RADEONUnmapMMIO(ScrnInfoPtr pScrn); RADEONEntPtr RADEONEntPriv(ScrnInfoPtr pScrn) { DevUnion *pPriv; RADEONInfoPtr info = RADEONPTR(pScrn); pPriv = xf86GetEntityPrivate(info->pEnt->index, getRADEONEntityIndex()); return pPriv->ptr; } static void RADEONPreInt10Save(ScrnInfoPtr pScrn, void **pPtr) { RADEONInfoPtr info = RADEONPTR(pScrn); unsigned char *RADEONMMIO = info->MMIO; CARD32 CardTmp; static struct RADEONInt10Save SaveStruct = { 0, 0, 0 }; /* Save the values and zap MEM_CNTL */ SaveStruct.MEM_CNTL = INREG(RADEON_MEM_CNTL); SaveStruct.MEMSIZE = INREG(RADEON_CONFIG_MEMSIZE); SaveStruct.MPP_TB_CONFIG = INREG(RADEON_MPP_TB_CONFIG); /* * Zap MEM_CNTL and set MPP_TB_CONFIG<31:24> to 4 */ OUTREG(RADEON_MEM_CNTL, 0); CardTmp = SaveStruct.MPP_TB_CONFIG & 0x00ffffffu; CardTmp |= 0x04 << 24; OUTREG(RADEON_MPP_TB_CONFIG, CardTmp); *pPtr = (void *)&SaveStruct; } static void RADEONPostInt10Check(ScrnInfoPtr pScrn, void *ptr) { RADEONInfoPtr info = RADEONPTR(pScrn); unsigned char *RADEONMMIO = info->MMIO; struct RADEONInt10Save *pSave = ptr; CARD32 CardTmp; /* If we don't have a valid (non-zero) saved MEM_CNTL, get out now */ if (!pSave || !pSave->MEM_CNTL) return; /* * If either MEM_CNTL is currently zero or inconistent (configured for * two channels with the two channels configured differently), restore * the saved registers. */ CardTmp = INREG(RADEON_MEM_CNTL); if (!CardTmp || ((CardTmp & 1) && (((CardTmp >> 8) & 0xff) != ((CardTmp >> 24) & 0xff)))) { /* Restore the saved registers */ xf86DrvMsg(pScrn->scrnIndex, X_WARNING, "Restoring MEM_CNTL (%08lx), setting to %08lx\n", (unsigned long)CardTmp, (unsigned long)pSave->MEM_CNTL); OUTREG(RADEON_MEM_CNTL, pSave->MEM_CNTL); CardTmp = INREG(RADEON_CONFIG_MEMSIZE); if (CardTmp != pSave->MEMSIZE) { xf86DrvMsg(pScrn->scrnIndex, X_WARNING, "Restoring CONFIG_MEMSIZE (%08lx), setting to %08lx\n", (unsigned long)CardTmp, (unsigned long)pSave->MEMSIZE); OUTREG(RADEON_CONFIG_MEMSIZE, pSave->MEMSIZE); } } CardTmp = INREG(RADEON_MPP_TB_CONFIG); if ((CardTmp & 0xff000000u) != (pSave->MPP_TB_CONFIG & 0xff000000u)) { xf86DrvMsg(pScrn->scrnIndex, X_WARNING, "Restoring MPP_TB_CONFIG<31:24> (%02lx), setting to %02lx\n", (unsigned long)CardTmp >> 24, (unsigned long)pSave->MPP_TB_CONFIG >> 24); CardTmp &= 0x00ffffffu; CardTmp |= (pSave->MPP_TB_CONFIG & 0xff000000u); OUTREG(RADEON_MPP_TB_CONFIG, CardTmp); } } /* Allocate our private RADEONInfoRec */ static Bool RADEONGetRec(ScrnInfoPtr pScrn) { if (pScrn->driverPrivate) return TRUE; pScrn->driverPrivate = xnfcalloc(sizeof(RADEONInfoRec), 1); return TRUE; } /* Free our private RADEONInfoRec */ static void RADEONFreeRec(ScrnInfoPtr pScrn) { RADEONInfoPtr info = RADEONPTR(pScrn); if(info->CRT2HSync) xfree(info->CRT2HSync); info->CRT2HSync = NULL; if(info->CRT2VRefresh) xfree(info->CRT2VRefresh); info->CRT2VRefresh = NULL; if(info->MetaModes) xfree(info->MetaModes); info->MetaModes = NULL; if(info->CRT2pScrn) { if(info->CRT2pScrn->modes) { while(info->CRT2pScrn->modes) xf86DeleteMode(&info->CRT2pScrn->modes, info->CRT2pScrn->modes); } if(info->CRT2pScrn->monitor) { if(info->CRT2pScrn->monitor->Modes) { while(info->CRT2pScrn->monitor->Modes) xf86DeleteMode(&info->CRT2pScrn->monitor->Modes, info->CRT2pScrn->monitor->Modes); } if(info->CRT2pScrn->monitor->DDC) xfree(info->CRT2pScrn->monitor->DDC); xfree(info->CRT2pScrn->monitor); } xfree(info->CRT2pScrn); info->CRT2pScrn = NULL; } if(info->CRT1Modes) { if(info->CRT1Modes != pScrn->modes) { if(pScrn->modes) { pScrn->currentMode = pScrn->modes; do { DisplayModePtr p = pScrn->currentMode->next; if(pScrn->currentMode->Private) xfree(pScrn->currentMode->Private); xfree(pScrn->currentMode); pScrn->currentMode = p; } while(pScrn->currentMode != pScrn->modes); } pScrn->currentMode = info->CRT1CurrentMode; pScrn->modes = info->CRT1Modes; info->CRT1CurrentMode = NULL; info->CRT1Modes = NULL; } } if (!pScrn || !pScrn->driverPrivate) return; xfree(pScrn->driverPrivate); pScrn->driverPrivate = NULL; } /* Memory map the MMIO region. Used during pre-init and by RADEONMapMem, * below */ static Bool RADEONMapMMIO(ScrnInfoPtr pScrn) { RADEONInfoPtr info = RADEONPTR(pScrn); if (info->FBDev) { info->MMIO = fbdevHWMapMMIO(pScrn); } else { info->MMIO = xf86MapPciMem(pScrn->scrnIndex, VIDMEM_MMIO | VIDMEM_READSIDEEFFECT, info->PciTag, info->MMIOAddr, RADEON_MMIOSIZE); } if (!info->MMIO) return FALSE; return TRUE; } /* Unmap the MMIO region. Used during pre-init and by RADEONUnmapMem, * below */ static Bool RADEONUnmapMMIO(ScrnInfoPtr pScrn) { RADEONInfoPtr info = RADEONPTR(pScrn); if (info->FBDev) fbdevHWUnmapMMIO(pScrn); else { xf86UnMapVidMem(pScrn->scrnIndex, info->MMIO, RADEON_MMIOSIZE); } info->MMIO = NULL; return TRUE; } /* Memory map the frame buffer. Used by RADEONMapMem, below. */ static Bool RADEONMapFB(ScrnInfoPtr pScrn) { RADEONInfoPtr info = RADEONPTR(pScrn); if (info->FBDev) { info->FB = fbdevHWMapVidmem(pScrn); } else { info->FB = xf86MapPciMem(pScrn->scrnIndex, VIDMEM_FRAMEBUFFER, info->PciTag, info->LinearAddr, info->FbMapSize); } if (!info->FB) return FALSE; return TRUE; } /* Unmap the frame buffer. Used by RADEONUnmapMem, below. */ static Bool RADEONUnmapFB(ScrnInfoPtr pScrn) { RADEONInfoPtr info = RADEONPTR(pScrn); if (info->FBDev) fbdevHWUnmapVidmem(pScrn); else xf86UnMapVidMem(pScrn->scrnIndex, info->FB, info->FbMapSize); info->FB = NULL; return TRUE; } /* Memory map the MMIO region and the frame buffer */ static Bool RADEONMapMem(ScrnInfoPtr pScrn) { if (!RADEONMapMMIO(pScrn)) return FALSE; if (!RADEONMapFB(pScrn)) { RADEONUnmapMMIO(pScrn); return FALSE; } return TRUE; } /* Unmap the MMIO region and the frame buffer */ static Bool RADEONUnmapMem(ScrnInfoPtr pScrn) { if (!RADEONUnmapMMIO(pScrn) || !RADEONUnmapFB(pScrn)) return FALSE; return TRUE; } /* This function is required to workaround a hardware bug in some (all?) * revisions of the R300. This workaround should be called after every * CLOCK_CNTL_INDEX register access. If not, register reads afterward * may not be correct. */ void R300CGWorkaround(ScrnInfoPtr pScrn) { RADEONInfoPtr info = RADEONPTR(pScrn); unsigned char *RADEONMMIO = info->MMIO; CARD32 save, tmp; save = INREG(RADEON_CLOCK_CNTL_INDEX); tmp = save & ~(0x3f | RADEON_PLL_WR_EN); OUTREG(RADEON_CLOCK_CNTL_INDEX, tmp); tmp = INREG(RADEON_CLOCK_CNTL_DATA); OUTREG(RADEON_CLOCK_CNTL_INDEX, save); } /* Read PLL information */ unsigned RADEONINPLL(ScrnInfoPtr pScrn, int addr) { RADEONInfoPtr info = RADEONPTR(pScrn); unsigned char *RADEONMMIO = info->MMIO; CARD32 data; OUTREG8(RADEON_CLOCK_CNTL_INDEX, addr & 0x3f); data = INREG(RADEON_CLOCK_CNTL_DATA); if (info->R300CGWorkaround) R300CGWorkaround(pScrn); return data; } #if 0 /* Read PAL information (only used for debugging) */ static int RADEONINPAL(int idx) { RADEONInfoPtr info = RADEONPTR(pScrn); unsigned char *RADEONMMIO = info->MMIO; OUTREG(RADEON_PALETTE_INDEX, idx << 16); return INREG(RADEON_PALETTE_DATA); } #endif /* Wait for vertical sync on primary CRTC */ void RADEONWaitForVerticalSync(ScrnInfoPtr pScrn) { RADEONInfoPtr info = RADEONPTR(pScrn); unsigned char *RADEONMMIO = info->MMIO; int i; /* Clear the CRTC_VBLANK_SAVE bit */ OUTREG(RADEON_CRTC_STATUS, RADEON_CRTC_VBLANK_SAVE_CLEAR); /* Wait for it to go back up */ for (i = 0; i < RADEON_TIMEOUT/1000; i++) { if (INREG(RADEON_CRTC_STATUS) & RADEON_CRTC_VBLANK_SAVE) break; usleep(1); } } /* Wait for vertical sync on secondary CRTC */ void RADEONWaitForVerticalSync2(ScrnInfoPtr pScrn) { RADEONInfoPtr info = RADEONPTR(pScrn); unsigned char *RADEONMMIO = info->MMIO; int i; /* Clear the CRTC2_VBLANK_SAVE bit */ OUTREG(RADEON_CRTC2_STATUS, RADEON_CRTC2_VBLANK_SAVE_CLEAR); /* Wait for it to go back up */ for (i = 0; i < RADEON_TIMEOUT/1000; i++) { if (INREG(RADEON_CRTC2_STATUS) & RADEON_CRTC2_VBLANK_SAVE) break; usleep(1); } } /* Blank screen */ static void RADEONBlank(ScrnInfoPtr pScrn) { RADEONInfoPtr info = RADEONPTR(pScrn); unsigned char *RADEONMMIO = info->MMIO; if (!info->IsSecondary) { switch(info->DisplayType) { case MT_LCD: case MT_CRT: case MT_DFP: OUTREGP(RADEON_CRTC_EXT_CNTL, RADEON_CRTC_DISPLAY_DIS, ~(RADEON_CRTC_DISPLAY_DIS)); break; case MT_NONE: default: break; } if (info->MergedFB) OUTREGP(RADEON_CRTC2_GEN_CNTL, RADEON_CRTC2_DISP_DIS, ~(RADEON_CRTC2_DISP_DIS)); } else { OUTREGP(RADEON_CRTC2_GEN_CNTL, RADEON_CRTC2_DISP_DIS, ~(RADEON_CRTC2_DISP_DIS)); } } /* Unblank screen */ static void RADEONUnblank(ScrnInfoPtr pScrn) { RADEONInfoPtr info = RADEONPTR(pScrn); unsigned char *RADEONMMIO = info->MMIO; if (!info->IsSecondary) { switch (info->DisplayType) { case MT_LCD: case MT_CRT: case MT_DFP: OUTREGP(RADEON_CRTC_EXT_CNTL, RADEON_CRTC_CRT_ON, ~(RADEON_CRTC_DISPLAY_DIS)); break; case MT_NONE: default: break; } if (info->MergedFB) OUTREGP(RADEON_CRTC2_GEN_CNTL, 0, ~(RADEON_CRTC2_DISP_DIS)); } else { switch (info->DisplayType) { case MT_LCD: case MT_DFP: case MT_CRT: OUTREGP(RADEON_CRTC2_GEN_CNTL, 0, ~(RADEON_CRTC2_DISP_DIS)); break; case MT_NONE: default: break; } } } /* Compute log base 2 of val */ int RADEONMinBits(int val) { int bits; if (!val) return 1; for (bits = 0; val; val >>= 1, ++bits); return bits; } /* Compute n/d with rounding */ static int RADEONDiv(int n, int d) { return (n + (d / 2)) / d; } static RADEONMonitorType RADEONDisplayDDCConnected(ScrnInfoPtr pScrn, RADEONDDCType DDCType, RADEONConnector* port) { RADEONInfoPtr info = RADEONPTR(pScrn); unsigned char *RADEONMMIO = info->MMIO; unsigned long DDCReg; RADEONMonitorType MonType = MT_NONE; xf86MonPtr* MonInfo = &port->MonInfo; int i, j; DDCReg = info->DDCReg; switch(DDCType) { case DDC_MONID: info->DDCReg = RADEON_GPIO_MONID; break; case DDC_DVI: info->DDCReg = RADEON_GPIO_DVI_DDC; break; case DDC_VGA: info->DDCReg = RADEON_GPIO_VGA_DDC; break; case DDC_CRT2: info->DDCReg = RADEON_GPIO_CRT2_DDC; break; default: info->DDCReg = DDCReg; return MT_NONE; } /* Read and output monitor info using DDC2 over I2C bus */ if (info->pI2CBus && info->ddc2) { OUTREG(info->DDCReg, INREG(info->DDCReg) & (CARD32)~(RADEON_GPIO_A_0 | RADEON_GPIO_A_1)); /* For some old monitors (like Compaq Presario FP500), we need * following process to initialize/stop DDC */ OUTREG(info->DDCReg, INREG(info->DDCReg) & ~(RADEON_GPIO_EN_1)); for (j = 0; j < 3; j++) { OUTREG(info->DDCReg, INREG(info->DDCReg) & ~(RADEON_GPIO_EN_0)); usleep(13000); OUTREG(info->DDCReg, INREG(info->DDCReg) & ~(RADEON_GPIO_EN_1)); for (i = 0; i < 10; i++) { usleep(15000); if (INREG(info->DDCReg) & RADEON_GPIO_Y_1) break; } if (i == 10) continue; usleep(15000); OUTREG(info->DDCReg, INREG(info->DDCReg) | RADEON_GPIO_EN_0); usleep(15000); OUTREG(info->DDCReg, INREG(info->DDCReg) | RADEON_GPIO_EN_1); usleep(15000); OUTREG(info->DDCReg, INREG(info->DDCReg) & ~(RADEON_GPIO_EN_0)); usleep(15000); *MonInfo = xf86DoEDID_DDC2(pScrn->scrnIndex, info->pI2CBus); OUTREG(info->DDCReg, INREG(info->DDCReg) | RADEON_GPIO_EN_1); OUTREG(info->DDCReg, INREG(info->DDCReg) | RADEON_GPIO_EN_0); usleep(15000); OUTREG(info->DDCReg, INREG(info->DDCReg) & ~(RADEON_GPIO_EN_1)); for (i = 0; i < 5; i++) { usleep(15000); if (INREG(info->DDCReg) & RADEON_GPIO_Y_1) break; } usleep(15000); OUTREG(info->DDCReg, INREG(info->DDCReg) & ~(RADEON_GPIO_EN_0)); usleep(15000); OUTREG(info->DDCReg, INREG(info->DDCReg) | RADEON_GPIO_EN_1); OUTREG(info->DDCReg, INREG(info->DDCReg) | RADEON_GPIO_EN_0); usleep(15000); if(*MonInfo) break; } } else { xf86DrvMsg(pScrn->scrnIndex, X_WARNING, "DDC2/I2C is not properly initialized\n"); MonType = MT_NONE; } if (*MonInfo) { if ((*MonInfo)->rawData[0x14] & 0x80) { /* Note some laptops have a DVI output that uses internal TMDS, * when its DVI is enabled by hotkey, LVDS panel is not used. * In this case, the laptop is configured as DVI+VGA as a normal * desktop card. * Also for laptop, when X starts with lid closed (no DVI connection) * both LDVS and TMDS are disable, we still need to treat it as a LVDS panel. */ if (port->TMDSType == TMDS_EXT) MonType = MT_DFP; else { if ((INREG(RADEON_FP_GEN_CNTL) & (1<<7)) || !info->IsMobility) MonType = MT_DFP; else MonType = MT_LCD; } } else MonType = MT_CRT; } else MonType = MT_NONE; info->DDCReg = DDCReg; xf86DrvMsg(pScrn->scrnIndex, X_INFO, "DDC Type: %d, Detected Type: %d\n", DDCType, MonType); return MonType; } static RADEONMonitorType RADEONCrtIsPhysicallyConnected(ScrnInfoPtr pScrn, int IsCrtDac) { RADEONInfoPtr info = RADEONPTR(pScrn); unsigned char *RADEONMMIO = info->MMIO; int bConnected = 0; /* the monitor either wasn't connected or it is a non-DDC CRT. * try to probe it */ if(IsCrtDac) { unsigned long ulOrigVCLK_ECP_CNTL; unsigned long ulOrigDAC_CNTL; unsigned long ulOrigDAC_EXT_CNTL; unsigned long ulOrigCRTC_EXT_CNTL; unsigned long ulData; unsigned long ulMask; ulOrigVCLK_ECP_CNTL = INPLL(pScrn, RADEON_VCLK_ECP_CNTL); ulData = ulOrigVCLK_ECP_CNTL; ulData &= ~(RADEON_PIXCLK_ALWAYS_ONb | RADEON_PIXCLK_DAC_ALWAYS_ONb); ulMask = ~(RADEON_PIXCLK_ALWAYS_ONb |RADEON_PIXCLK_DAC_ALWAYS_ONb); OUTPLLP(pScrn, RADEON_VCLK_ECP_CNTL, ulData, ulMask); ulOrigCRTC_EXT_CNTL = INREG(RADEON_CRTC_EXT_CNTL); ulData = ulOrigCRTC_EXT_CNTL; ulData |= RADEON_CRTC_CRT_ON; OUTREG(RADEON_CRTC_EXT_CNTL, ulData); ulOrigDAC_EXT_CNTL = INREG(RADEON_DAC_EXT_CNTL); ulData = ulOrigDAC_EXT_CNTL; ulData &= ~RADEON_DAC_FORCE_DATA_MASK; ulData |= (RADEON_DAC_FORCE_BLANK_OFF_EN |RADEON_DAC_FORCE_DATA_EN |RADEON_DAC_FORCE_DATA_SEL_MASK); if ((info->ChipFamily == CHIP_FAMILY_RV250) || (info->ChipFamily == CHIP_FAMILY_RV280)) ulData |= (0x01b6 << RADEON_DAC_FORCE_DATA_SHIFT); else ulData |= (0x01ac << RADEON_DAC_FORCE_DATA_SHIFT); OUTREG(RADEON_DAC_EXT_CNTL, ulData); ulOrigDAC_CNTL = INREG(RADEON_DAC_CNTL); ulData = ulOrigDAC_CNTL; ulData |= RADEON_DAC_CMP_EN; ulData &= ~(RADEON_DAC_RANGE_CNTL_MASK | RADEON_DAC_PDWN); ulData |= 0x2; OUTREG(RADEON_DAC_CNTL, ulData); usleep(10000); ulData = INREG(RADEON_DAC_CNTL); bConnected = (RADEON_DAC_CMP_OUTPUT & ulData)?1:0; ulData = ulOrigVCLK_ECP_CNTL; ulMask = 0xFFFFFFFFL; OUTPLLP(pScrn, RADEON_VCLK_ECP_CNTL, ulData, ulMask); OUTREG(RADEON_DAC_CNTL, ulOrigDAC_CNTL ); OUTREG(RADEON_DAC_EXT_CNTL, ulOrigDAC_EXT_CNTL ); OUTREG(RADEON_CRTC_EXT_CNTL, ulOrigCRTC_EXT_CNTL); } else { /* TV DAC */ /* This doesn't seem to work reliably (maybe worse on some OEM cards), for now we always return false. If one wants to connected a non-DDC monitor on the DVI port when CRT port is also connected, he will need to explicitly tell the driver in the config file with Option MonitorLayout. */ bConnected = FALSE; #if 0 if (info->ChipFamily == CHIP_FAMILY_R200) { unsigned long ulOrigGPIO_MONID; unsigned long ulOrigFP2_GEN_CNTL; unsigned long ulOrigDISP_OUTPUT_CNTL; unsigned long ulOrigCRTC2_GEN_CNTL; unsigned long ulOrigDISP_LIN_TRANS_GRPH_A; unsigned long ulOrigDISP_LIN_TRANS_GRPH_B; unsigned long ulOrigDISP_LIN_TRANS_GRPH_C; unsigned long ulOrigDISP_LIN_TRANS_GRPH_D; unsigned long ulOrigDISP_LIN_TRANS_GRPH_E; unsigned long ulOrigDISP_LIN_TRANS_GRPH_F; unsigned long ulOrigCRTC2_H_TOTAL_DISP; unsigned long ulOrigCRTC2_V_TOTAL_DISP; unsigned long ulOrigCRTC2_H_SYNC_STRT_WID; unsigned long ulOrigCRTC2_V_SYNC_STRT_WID; unsigned long ulData, i; ulOrigGPIO_MONID = INREG(RADEON_GPIO_MONID); ulOrigFP2_GEN_CNTL = INREG(RADEON_FP2_GEN_CNTL); ulOrigDISP_OUTPUT_CNTL = INREG(RADEON_DISP_OUTPUT_CNTL); ulOrigCRTC2_GEN_CNTL = INREG(RADEON_CRTC2_GEN_CNTL); ulOrigDISP_LIN_TRANS_GRPH_A = INREG(RADEON_DISP_LIN_TRANS_GRPH_A); ulOrigDISP_LIN_TRANS_GRPH_B = INREG(RADEON_DISP_LIN_TRANS_GRPH_B); ulOrigDISP_LIN_TRANS_GRPH_C = INREG(RADEON_DISP_LIN_TRANS_GRPH_C); ulOrigDISP_LIN_TRANS_GRPH_D = INREG(RADEON_DISP_LIN_TRANS_GRPH_D); ulOrigDISP_LIN_TRANS_GRPH_E = INREG(RADEON_DISP_LIN_TRANS_GRPH_E); ulOrigDISP_LIN_TRANS_GRPH_F = INREG(RADEON_DISP_LIN_TRANS_GRPH_F); ulOrigCRTC2_H_TOTAL_DISP = INREG(RADEON_CRTC2_H_TOTAL_DISP); ulOrigCRTC2_V_TOTAL_DISP = INREG(RADEON_CRTC2_V_TOTAL_DISP); ulOrigCRTC2_H_SYNC_STRT_WID = INREG(RADEON_CRTC2_H_SYNC_STRT_WID); ulOrigCRTC2_V_SYNC_STRT_WID = INREG(RADEON_CRTC2_V_SYNC_STRT_WID); ulData = INREG(RADEON_GPIO_MONID); ulData &= ~RADEON_GPIO_A_0; OUTREG(RADEON_GPIO_MONID, ulData); OUTREG(RADEON_FP2_GEN_CNTL, 0x0a000c0c); OUTREG(RADEON_DISP_OUTPUT_CNTL, 0x00000012); OUTREG(RADEON_CRTC2_GEN_CNTL, 0x06000000); OUTREG(RADEON_DISP_LIN_TRANS_GRPH_A, 0x00000000); OUTREG(RADEON_DISP_LIN_TRANS_GRPH_B, 0x000003f0); OUTREG(RADEON_DISP_LIN_TRANS_GRPH_C, 0x00000000); OUTREG(RADEON_DISP_LIN_TRANS_GRPH_D, 0x000003f0); OUTREG(RADEON_DISP_LIN_TRANS_GRPH_E, 0x00000000); OUTREG(RADEON_DISP_LIN_TRANS_GRPH_F, 0x000003f0); OUTREG(RADEON_CRTC2_H_TOTAL_DISP, 0x01000008); OUTREG(RADEON_CRTC2_H_SYNC_STRT_WID, 0x00000800); OUTREG(RADEON_CRTC2_V_TOTAL_DISP, 0x00080001); OUTREG(RADEON_CRTC2_V_SYNC_STRT_WID, 0x00000080); for (i = 0; i < 200; i++) { ulData = INREG(RADEON_GPIO_MONID); bConnected = (ulData & RADEON_GPIO_Y_0)?1:0; if (!bConnected) break; usleep(1000); } OUTREG(RADEON_DISP_LIN_TRANS_GRPH_A, ulOrigDISP_LIN_TRANS_GRPH_A); OUTREG(RADEON_DISP_LIN_TRANS_GRPH_B, ulOrigDISP_LIN_TRANS_GRPH_B); OUTREG(RADEON_DISP_LIN_TRANS_GRPH_C, ulOrigDISP_LIN_TRANS_GRPH_C); OUTREG(RADEON_DISP_LIN_TRANS_GRPH_D, ulOrigDISP_LIN_TRANS_GRPH_D); OUTREG(RADEON_DISP_LIN_TRANS_GRPH_E, ulOrigDISP_LIN_TRANS_GRPH_E); OUTREG(RADEON_DISP_LIN_TRANS_GRPH_F, ulOrigDISP_LIN_TRANS_GRPH_F); OUTREG(RADEON_CRTC2_H_TOTAL_DISP, ulOrigCRTC2_H_TOTAL_DISP); OUTREG(RADEON_CRTC2_V_TOTAL_DISP, ulOrigCRTC2_V_TOTAL_DISP); OUTREG(RADEON_CRTC2_H_SYNC_STRT_WID, ulOrigCRTC2_H_SYNC_STRT_WID); OUTREG(RADEON_CRTC2_V_SYNC_STRT_WID, ulOrigCRTC2_V_SYNC_STRT_WID); OUTREG(RADEON_CRTC2_GEN_CNTL, ulOrigCRTC2_GEN_CNTL); OUTREG(RADEON_DISP_OUTPUT_CNTL, ulOrigDISP_OUTPUT_CNTL); OUTREG(RADEON_FP2_GEN_CNTL, ulOrigFP2_GEN_CNTL); OUTREG(RADEON_GPIO_MONID, ulOrigGPIO_MONID); } else { unsigned long ulOrigPIXCLKSDATA; unsigned long ulOrigTV_MASTER_CNTL; unsigned long ulOrigTV_DAC_CNTL; unsigned long ulOrigTV_PRE_DAC_MUX_CNTL; unsigned long ulOrigDAC_CNTL2; unsigned long ulData; unsigned long ulMask; ulOrigPIXCLKSDATA = INPLL(pScrn, RADEON_PIXCLKS_CNTL); ulData = ulOrigPIXCLKSDATA; ulData &= ~(RADEON_PIX2CLK_ALWAYS_ONb | RADEON_PIX2CLK_DAC_ALWAYS_ONb); ulMask = ~(RADEON_PIX2CLK_ALWAYS_ONb | RADEON_PIX2CLK_DAC_ALWAYS_ONb); OUTPLLP(pScrn, RADEON_PIXCLKS_CNTL, ulData, ulMask); ulOrigTV_MASTER_CNTL = INREG(RADEON_TV_MASTER_CNTL); ulData = ulOrigTV_MASTER_CNTL; ulData &= ~RADEON_TVCLK_ALWAYS_ONb; OUTREG(RADEON_TV_MASTER_CNTL, ulData); ulOrigDAC_CNTL2 = INREG(RADEON_DAC_CNTL2); ulData = ulOrigDAC_CNTL2; ulData &= ~RADEON_DAC2_DAC2_CLK_SEL; OUTREG(RADEON_DAC_CNTL2, ulData); ulOrigTV_DAC_CNTL = INREG(RADEON_TV_DAC_CNTL); ulData = 0x00880213; OUTREG(RADEON_TV_DAC_CNTL, ulData); ulOrigTV_PRE_DAC_MUX_CNTL = INREG(RADEON_TV_PRE_DAC_MUX_CNTL); ulData = (RADEON_Y_RED_EN | RADEON_C_GRN_EN | RADEON_CMP_BLU_EN | RADEON_RED_MX_FORCE_DAC_DATA | RADEON_GRN_MX_FORCE_DAC_DATA | RADEON_BLU_MX_FORCE_DAC_DATA); if (IS_R300_VARIANT) ulData |= 0x180 << RADEON_TV_FORCE_DAC_DATA_SHIFT; else ulData |= 0x1f5 << RADEON_TV_FORCE_DAC_DATA_SHIFT; OUTREG(RADEON_TV_PRE_DAC_MUX_CNTL, ulData); usleep(10000); ulData = INREG(RADEON_TV_DAC_CNTL); bConnected = (ulData & RADEON_TV_DAC_CMPOUT)?1:0; ulData = ulOrigPIXCLKSDATA; ulMask = 0xFFFFFFFFL; OUTPLLP(pScrn, RADEON_PIXCLKS_CNTL, ulData, ulMask); OUTREG(RADEON_TV_MASTER_CNTL, ulOrigTV_MASTER_CNTL); OUTREG(RADEON_DAC_CNTL2, ulOrigDAC_CNTL2); OUTREG(RADEON_TV_DAC_CNTL, ulOrigTV_DAC_CNTL); OUTREG(RADEON_TV_PRE_DAC_MUX_CNTL, ulOrigTV_PRE_DAC_MUX_CNTL); } #endif } return(bConnected ? MT_CRT : MT_NONE); } #if defined(__powerpc__) static Bool RADEONProbePLLParameters(ScrnInfoPtr pScrn) { RADEONInfoPtr info = RADEONPTR(pScrn); RADEONPLLPtr pll = &info->pll; unsigned char *RADEONMMIO = info->MMIO; unsigned char ppll_div_sel; unsigned Nx, M; unsigned xclk, tmp, ref_div; int hTotal, vTotal, num, denom, m, n; float hz, vclk, xtal; long start_secs, start_usecs, stop_secs, stop_usecs, total_usecs; int i; for(i=0; i<1000000; i++) if (((INREG(RADEON_CRTC_VLINE_CRNT_VLINE) >> 16) & 0x3ff) == 0) break; xf86getsecs(&start_secs, &start_usecs); for(i=0; i<1000000; i++) if (((INREG(RADEON_CRTC_VLINE_CRNT_VLINE) >> 16) & 0x3ff) != 0) break; for(i=0; i<1000000; i++) if (((INREG(RADEON_CRTC_VLINE_CRNT_VLINE) >> 16) & 0x3ff) == 0) break; xf86getsecs(&stop_secs, &stop_usecs); total_usecs = abs(stop_usecs - start_usecs); hz = 1000000/total_usecs; hTotal = ((INREG(RADEON_CRTC_H_TOTAL_DISP) & 0x1ff) + 1) * 8; vTotal = ((INREG(RADEON_CRTC_V_TOTAL_DISP) & 0x3ff) + 1); vclk = (float)(hTotal * (float)(vTotal * hz)); switch((INPLL(pScrn, RADEON_PPLL_REF_DIV) & 0x30000) >> 16) { case 0: default: num = 1; denom = 1; break; case 1: n = ((INPLL(pScrn, RADEON_X_MPLL_REF_FB_DIV) >> 16) & 0xff); m = (INPLL(pScrn, RADEON_X_MPLL_REF_FB_DIV) & 0xff); num = 2*n; denom = 2*m; break; case 2: n = ((INPLL(pScrn, RADEON_X_MPLL_REF_FB_DIV) >> 8) & 0xff); m = (INPLL(pScrn, RADEON_X_MPLL_REF_FB_DIV) & 0xff); num = 2*n; denom = 2*m; break; } OUTREG(RADEON_CLOCK_CNTL_INDEX, 1); ppll_div_sel = INREG8(RADEON_CLOCK_CNTL_DATA + 1) & 0x3; n = (INPLL(pScrn, RADEON_PPLL_DIV_0 + ppll_div_sel) & 0x7ff); m = (INPLL(pScrn, RADEON_PPLL_REF_DIV) & 0x3ff); num *= n; denom *= m; switch ((INPLL(pScrn, RADEON_PPLL_DIV_0 + ppll_div_sel) >> 16) & 0x7) { case 1: denom *= 2; break; case 2: denom *= 4; break; case 3: denom *= 8; break; case 4: denom *= 3; break; case 6: denom *= 6; break; case 7: denom *= 12; break; } xtal = (int)(vclk *(float)denom/(float)num); if ((xtal > 26900000) && (xtal < 27100000)) xtal = 2700; else if ((xtal > 14200000) && (xtal < 14400000)) xtal = 1432; else if ((xtal > 29400000) && (xtal < 29600000)) xtal = 2950; else return FALSE; tmp = INPLL(pScrn, RADEON_X_MPLL_REF_FB_DIV); ref_div = INPLL(pScrn, RADEON_PPLL_REF_DIV) & 0x3ff; Nx = (tmp & 0xff00) >> 8; M = (tmp & 0xff); xclk = RADEONDiv((2 * Nx * xtal), (2 * M)); /* we're done, hopefully these are sane values */ pll->reference_div = ref_div; pll->xclk = xclk; pll->reference_freq = xtal; return TRUE; } #endif static void RADEONGetPanelInfoFromReg (ScrnInfoPtr pScrn) { RADEONInfoPtr info = RADEONPTR(pScrn); unsigned char *RADEONMMIO = info->MMIO; CARD32 fp_vert_stretch = INREG(RADEON_FP_VERT_STRETCH); CARD32 fp_horz_stretch = INREG(RADEON_FP_HORZ_STRETCH); info->PanelPwrDly = 200; if (fp_vert_stretch & RADEON_VERT_STRETCH_ENABLE) { info->PanelYRes = (fp_vert_stretch>>12) + 1; } else { info->PanelYRes = (INREG(RADEON_CRTC_V_TOTAL_DISP)>>16) + 1; } if (fp_horz_stretch & RADEON_HORZ_STRETCH_ENABLE) { info->PanelXRes = ((fp_horz_stretch>>16) + 1) * 8; } else { info->PanelXRes = ((INREG(RADEON_CRTC_H_TOTAL_DISP)>>16) + 1) * 8; } if ((info->PanelXRes < 640) || (info->PanelYRes < 480)) { info->PanelXRes = 640; info->PanelYRes = 480; } xf86DrvMsg(pScrn->scrnIndex, X_WARNING, "Panel size %dx%d is derived, this may not be correct.\n" "If not, use PanelSize option to overwrite this setting\n", info->PanelXRes, info->PanelYRes); } static Bool RADEONGetLVDSInfo (ScrnInfoPtr pScrn) { RADEONInfoPtr info = RADEONPTR(pScrn); if (!RADEONGetLVDSInfoFromBIOS(pScrn)) RADEONGetPanelInfoFromReg(pScrn); if (info->DotClock == 0) { RADEONEntPtr pRADEONEnt = RADEONEntPriv(pScrn); DisplayModePtr tmp_mode = NULL; xf86DrvMsg(pScrn->scrnIndex, X_WARNING, "No valid timing info from BIOS.\n"); /* No timing information for the native mode, use whatever specified in the Modeline. If no Modeline specified, we'll just pick the VESA mode at 60Hz refresh rate which is likely to be the best for a flat panel. */ tmp_mode = pScrn->monitor->Modes; while(tmp_mode) { if ((tmp_mode->HDisplay == info->PanelXRes) && (tmp_mode->VDisplay == info->PanelYRes)) { float refresh = (float)tmp_mode->Clock * 1000.0 / tmp_mode->HTotal / tmp_mode->VTotal; if ((abs(60.0 - refresh) < 1.0) || (tmp_mode->type == 0)) { info->HBlank = tmp_mode->HTotal - tmp_mode->HDisplay; info->HOverPlus = tmp_mode->HSyncStart - tmp_mode->HDisplay; info->HSyncWidth = tmp_mode->HSyncEnd - tmp_mode->HSyncStart; info->VBlank = tmp_mode->VTotal - tmp_mode->VDisplay; info->VOverPlus = tmp_mode->VSyncStart - tmp_mode->VDisplay; info->VSyncWidth = tmp_mode->VSyncEnd - tmp_mode->VSyncStart; info->DotClock = tmp_mode->Clock; info->Flags = 0; break; } } tmp_mode = tmp_mode->next; } if ((info->DotClock == 0) && !pRADEONEnt->PortInfo[0].MonInfo) { xf86DrvMsg(pScrn->scrnIndex, X_ERROR, "Panel size is not correctly detected.\n" "Please try to use PanelSize option for correct settings.\n"); return FALSE; } } return TRUE; } static void RADEONGetTMDSInfo(ScrnInfoPtr pScrn) { RADEONInfoPtr info = RADEONPTR(pScrn); int i; for (i=0; i<4; i++) { info->tmds_pll[i].value = 0; info->tmds_pll[i].freq = 0; } if (RADEONGetTMDSInfoFromBIOS(pScrn)) return; for (i=0; i<4; i++) { info->tmds_pll[i].value = default_tmds_pll[info->ChipFamily][i].value; info->tmds_pll[i].freq = default_tmds_pll[info->ChipFamily][i].freq; } } static void RADEONGetPanelInfo (ScrnInfoPtr pScrn) { RADEONInfoPtr info = RADEONPTR(pScrn); char* s; if((s = xf86GetOptValString(info->Options, OPTION_PANEL_SIZE))) { info->PanelPwrDly = 200; if (sscanf (s, "%dx%d", &info->PanelXRes, &info->PanelYRes) != 2) { xf86DrvMsg(pScrn->scrnIndex, X_WARNING, "Invalid PanelSize option: %s\n", s); RADEONGetPanelInfoFromReg(pScrn); } } else { if(info->DisplayType == MT_LCD) { RADEONGetLVDSInfo(pScrn); } else if ((info->DisplayType == MT_DFP) || (info->MergeType == MT_DFP)) { RADEONGetTMDSInfo(pScrn); if (!pScrn->monitor->DDC) RADEONGetHardCodedEDIDFromBIOS(pScrn); } } } static void RADEONGetClockInfo(ScrnInfoPtr pScrn) { RADEONInfoPtr info = RADEONPTR (pScrn); RADEONPLLPtr pll = &info->pll; double min_dotclock; if (RADEONGetClockInfoFromBIOS(pScrn)) { if (pll->reference_div < 2) { /* retrive it from register setting for fitting into current PLL algorithm. We'll probably need a new routine to calculate the best ref_div from BIOS provided min_input_pll and max_input_pll */ CARD32 tmp; tmp = INPLL(pScrn, RADEON_PPLL_REF_DIV); if (IS_R300_VARIANT || (info->ChipFamily == CHIP_FAMILY_RS300)) { pll->reference_div = (tmp & R300_PPLL_REF_DIV_ACC_MASK) >> R300_PPLL_REF_DIV_ACC_SHIFT; } else { pll->reference_div = tmp & RADEON_PPLL_REF_DIV_MASK; } if (pll->reference_div < 2) pll->reference_div = 12; } } else { xf86DrvMsg (pScrn->scrnIndex, X_WARNING, "Video BIOS not detected, using default clock settings!\n"); #if defined(__powerpc__) if (RADEONProbePLLParameters(pScrn)) return; #endif if (info->IsIGP) pll->reference_freq = 1432; else pll->reference_freq = 2700; pll->reference_div = 12; pll->min_pll_freq = 12500; pll->max_pll_freq = 35000; pll->xclk = 10300; info->sclk = 200.00; info->mclk = 200.00; } xf86DrvMsg (pScrn->scrnIndex, X_INFO, "PLL parameters: rf=%d rd=%d min=%ld max=%ld; xclk=%d\n", pll->reference_freq, pll->reference_div, pll->min_pll_freq, pll->max_pll_freq, pll->xclk); /* (Some?) Radeon BIOSes seem too lie about their minimum dot * clocks. Allow users to override the detected minimum dot clock * value (e.g., and allow it to be suitable for TV sets). */ if (xf86GetOptValFreq(info->Options, OPTION_MIN_DOTCLOCK, OPTUNITS_MHZ, &min_dotclock)) { if (min_dotclock < 12 || min_dotclock*100 >= pll->max_pll_freq) { xf86DrvMsg(pScrn->scrnIndex, X_INFO, "Illegal minimum dotclock specified %.2f MHz " "(option ignored)\n", min_dotclock); } else { xf86DrvMsg(pScrn->scrnIndex, X_INFO, "Forced minimum dotclock to %.2f MHz " "(instead of detected %.2f MHz)\n", min_dotclock, ((double)pll->min_pll_freq/1000)); pll->min_pll_freq = min_dotclock * 1000; } } } static BOOL RADEONQueryConnectedMonitors(ScrnInfoPtr pScrn) { RADEONInfoPtr info = RADEONPTR(pScrn); RADEONEntPtr pRADEONEnt = RADEONEntPriv(pScrn); unsigned char *RADEONMMIO = info->MMIO; const char *s; Bool ignore_edid = FALSE; int i = 0, second = 0, max_mt; const char *MonTypeName[7] = { "AUTO", "NONE", "CRT", "LVDS", "TMDS", "CTV", "STV" }; const RADEONMonitorType MonTypeID[7] = { MT_UNKNOWN, /* this is just a dummy value for AUTO DETECTION */ MT_NONE, /* NONE -> NONE */ MT_CRT, /* CRT -> CRT */ MT_LCD, /* Laptop LCDs are driven via LVDS port */ MT_DFP, /* DFPs are driven via TMDS */ MT_CTV, /* CTV -> CTV */ MT_STV, /* STV -> STV */ }; const char *TMDSTypeName[3] = { "NONE", "Internal", "External" }; const char *DDCTypeName[5] = { "NONE", "MONID", "DVI_DDC", "VGA_DDC", "CRT2_DDC" }; const char *DACTypeName[3] = { "Unknown", "Primary", "TVDAC/ExtDAC", }; const char *ConnectorTypeName[8] = { "None", "Proprietary", "VGA", "DVI-I", "DVI-D", "CTV", "STV", "Unsupported" }; const char *ConnectorTypeNameATOM[10] = { "None", "VGA", "DVI-I", "DVI-D", "DVI-A", "STV", "CTV", "LVDS", "Digital", "Unsupported" }; max_mt = 5; if(info->IsSecondary) { info->DisplayType = (RADEONMonitorType)pRADEONEnt->MonType2; if(info->DisplayType == MT_NONE) return FALSE; return TRUE; } /* We first get the information about all connectors from BIOS. * This is how the card is phyiscally wired up. * The information should be correct even on a OEM card. * If not, we may have problem -- need to use MonitorLayout option. */ for (i = 0; i < 2; i++) { pRADEONEnt->PortInfo[i].MonType = MT_UNKNOWN; pRADEONEnt->PortInfo[i].MonInfo = NULL; pRADEONEnt->PortInfo[i].DDCType = DDC_NONE_DETECTED; pRADEONEnt->PortInfo[i].DACType = DAC_UNKNOWN; pRADEONEnt->PortInfo[i].TMDSType = TMDS_UNKNOWN; pRADEONEnt->PortInfo[i].ConnectorType = CONNECTOR_NONE; } if (!RADEONGetConnectorInfoFromBIOS(pScrn)) { /* Below is the most common setting, but may not be true */ pRADEONEnt->PortInfo[0].MonType = MT_UNKNOWN; pRADEONEnt->PortInfo[0].MonInfo = NULL; pRADEONEnt->PortInfo[0].DDCType = DDC_DVI; pRADEONEnt->PortInfo[0].DACType = DAC_TVDAC; pRADEONEnt->PortInfo[0].TMDSType = TMDS_INT; pRADEONEnt->PortInfo[0].ConnectorType = CONNECTOR_DVI_D; pRADEONEnt->PortInfo[1].MonType = MT_UNKNOWN; pRADEONEnt->PortInfo[1].MonInfo = NULL; pRADEONEnt->PortInfo[1].DDCType = DDC_VGA; pRADEONEnt->PortInfo[1].DACType = DAC_PRIMARY; pRADEONEnt->PortInfo[1].TMDSType = TMDS_EXT; pRADEONEnt->PortInfo[1].ConnectorType = CONNECTOR_CRT; } /* always make TMDS_INT port first*/ if (pRADEONEnt->PortInfo[1].TMDSType == TMDS_INT) { RADEONConnector connector; connector = pRADEONEnt->PortInfo[0]; pRADEONEnt->PortInfo[0] = pRADEONEnt->PortInfo[1]; pRADEONEnt->PortInfo[1] = connector; } else if ((pRADEONEnt->PortInfo[0].TMDSType != TMDS_INT && pRADEONEnt->PortInfo[1].TMDSType != TMDS_INT)) { /* no TMDS_INT port, make primary DAC port first */ if (pRADEONEnt->PortInfo[1].DACType == DAC_PRIMARY) { RADEONConnector connector; connector = pRADEONEnt->PortInfo[0]; pRADEONEnt->PortInfo[0] = pRADEONEnt->PortInfo[1]; pRADEONEnt->PortInfo[1] = connector; } } if (info->HasSingleDAC) { /* For RS300/RS350/RS400 chips, there is no primary DAC. Force VGA port to use TVDAC*/ if (pRADEONEnt->PortInfo[0].ConnectorType == CONNECTOR_CRT) { pRADEONEnt->PortInfo[0].DACType = DAC_TVDAC; pRADEONEnt->PortInfo[1].DACType = DAC_PRIMARY; } else { pRADEONEnt->PortInfo[1].DACType = DAC_TVDAC; pRADEONEnt->PortInfo[0].DACType = DAC_PRIMARY; } } else if (!info->HasCRTC2) { pRADEONEnt->PortInfo[0].DACType = DAC_PRIMARY; } /* IgnoreEDID option is different from the NoDDCxx options used by DDC module * When IgnoreEDID is used, monitor detection will still use DDC * detection, but all EDID data will not be used in mode validation. * You can use this option when you have a DDC monitor but want specify your own * monitor timing parameters by using HSync, VRefresh and Modeline, */ if (xf86GetOptValBool(info->Options, OPTION_IGNORE_EDID, &ignore_edid)) { if (ignore_edid) xf86DrvMsg(pScrn->scrnIndex, X_CONFIG, "IgnoreEDID is specified, EDID data will be ignored\n"); } /* * MonitorLayout option takes a string for two monitors connected in following format: * Option "MonitorLayout" "primary-port-display, secondary-port-display" * primary and secondary port displays can have one of following: * NONE, CRT, LVDS, TMDS * With this option, driver will bring up monitors as specified, * not using auto-detection routines to probe monitors. * * This option can be used when the false monitor detection occurs. * * This option can also be used to disable one connected display. * For example, if you have a laptop connected to an external CRT * and you want to disable the internal LCD panel, you can specify * Option "MonitorLayout" "NONE, CRT" * * This option can also used to disable Clone mode. One there is only * one monitor is specified, clone mode will be turned off automatically * even you have two monitors connected. * * Another usage of this option is you want to config the server * to start up with a certain monitor arrangement even one monitor * is not plugged in when server starts. */ if ((s = xf86GetOptValString(info->Options, OPTION_MONITOR_LAYOUT))) { char s1[5], s2[5]; i = 0; /* When using user specified monitor types, we will not do DDC detection * */ do { switch(*s) { case ',': s1[i] = '\0'; i = 0; second = 1; break; case ' ': case '\t': case '\n': case '\r': break; default: if (second) s2[i] = *s; else s1[i] = *s; i++; break; } if (i > 4) i = 4; } while(*s++); s2[i] = '\0'; for (i = 0; i < max_mt; i++) { if (strcmp(s1, MonTypeName[i]) == 0) { pRADEONEnt->PortInfo[0].MonType = MonTypeID[i]; break; } } for (i = 0; i < max_mt; i++) { if (strcmp(s2, MonTypeName[i]) == 0) { pRADEONEnt->PortInfo[1].MonType = MonTypeID[i]; break; } } if (i == max_mt) xf86DrvMsg(pScrn->scrnIndex, X_WARNING, "Invalid Monitor type specified for 2nd port \n"); xf86DrvMsg(pScrn->scrnIndex, X_CONFIG, "MonitorLayout Option: \n\tMonitor1--Type %s, Monitor2--Type %s\n\n", s1, s2); if (pRADEONEnt->PortInfo[1].MonType == MT_CRT) { pRADEONEnt->PortInfo[1].DACType = DAC_PRIMARY; pRADEONEnt->PortInfo[1].TMDSType = TMDS_UNKNOWN; pRADEONEnt->PortInfo[1].DDCType = DDC_VGA; pRADEONEnt->PortInfo[1].ConnectorType = CONNECTOR_CRT; pRADEONEnt->PortInfo[0].DACType = DAC_TVDAC; pRADEONEnt->PortInfo[0].TMDSType = TMDS_UNKNOWN; pRADEONEnt->PortInfo[0].DDCType = DDC_NONE_DETECTED; pRADEONEnt->PortInfo[0].ConnectorType = pRADEONEnt->PortInfo[0].MonType+1; pRADEONEnt->PortInfo[0].MonInfo = NULL; } if (!ignore_edid) { if ((pRADEONEnt->PortInfo[0].MonType > MT_NONE) && (pRADEONEnt->PortInfo[0].MonType < MT_STV)) RADEONDisplayDDCConnected(pScrn, pRADEONEnt->PortInfo[0].DDCType, &pRADEONEnt->PortInfo[0]); if ((pRADEONEnt->PortInfo[1].MonType > MT_NONE) && (pRADEONEnt->PortInfo[1].MonType < MT_STV)) RADEONDisplayDDCConnected(pScrn, pRADEONEnt->PortInfo[1].DDCType, &pRADEONEnt->PortInfo[1]); } } if(((!info->HasCRTC2) || info->IsDellServer)) { if (pRADEONEnt->PortInfo[0].MonType == MT_UNKNOWN) { if((pRADEONEnt->PortInfo[0].MonType = RADEONDisplayDDCConnected(pScrn, DDC_DVI, &pRADEONEnt->PortInfo[0]))); else if((pRADEONEnt->PortInfo[0].MonType = RADEONDisplayDDCConnected(pScrn, DDC_VGA, &pRADEONEnt->PortInfo[0]))); else if((pRADEONEnt->PortInfo[0].MonType = RADEONDisplayDDCConnected(pScrn, DDC_CRT2, &pRADEONEnt->PortInfo[0]))); else pRADEONEnt->PortInfo[0].MonType = MT_CRT; } if (!ignore_edid) { if (pRADEONEnt->PortInfo[0].MonInfo) { xf86DrvMsg(pScrn->scrnIndex, X_INFO, "Monitor1 EDID data ---------------------------\n"); xf86PrintEDID(pRADEONEnt->PortInfo[0].MonInfo ); xf86DrvMsg(pScrn->scrnIndex, X_INFO, "End of Monitor1 EDID data --------------------\n"); } } pRADEONEnt->MonType1 = pRADEONEnt->PortInfo[0].MonType; pRADEONEnt->MonInfo1 = pRADEONEnt->PortInfo[0].MonInfo; pRADEONEnt->MonType2 = MT_NONE; pRADEONEnt->MonInfo2 = NULL; info->MergeType = MT_NONE; info->DisplayType = pRADEONEnt->MonType1; xf86DrvMsg(pScrn->scrnIndex, X_INFO, "Primary:\n Monitor -- %s\n Connector -- %s\n DAC Type -- %s\n TMDS Type -- %s\n DDC Type -- %s\n", MonTypeName[pRADEONEnt->PortInfo[0].MonType+1], info->IsAtomBios ? ConnectorTypeNameATOM[pRADEONEnt->PortInfo[0].ConnectorType]: ConnectorTypeName[pRADEONEnt->PortInfo[0].ConnectorType], DACTypeName[pRADEONEnt->PortInfo[0].DACType+1], TMDSTypeName[pRADEONEnt->PortInfo[0].TMDSType+1], DDCTypeName[pRADEONEnt->PortInfo[0].DDCType]); return TRUE; } if (pRADEONEnt->PortInfo[0].MonType == MT_UNKNOWN || pRADEONEnt->PortInfo[1].MonType == MT_UNKNOWN) { /* Primary Head (DVI or Laptop Int. panel)*/ /* A ddc capable display connected on DVI port */ if (pRADEONEnt->PortInfo[0].MonType == MT_UNKNOWN) { if((pRADEONEnt->PortInfo[0].MonType = RADEONDisplayDDCConnected(pScrn, pRADEONEnt->PortInfo[0].DDCType, &pRADEONEnt->PortInfo[0]))); else if (info->IsMobility && (INREG(RADEON_BIOS_4_SCRATCH) & 4)) { /* non-DDC laptop panel connected on primary */ pRADEONEnt->PortInfo[0].MonType = MT_LCD; } else { /* CRT on DVI, TODO: not reliable, make it always return false for now*/ pRADEONEnt->PortInfo[0].MonType = RADEONCrtIsPhysicallyConnected(pScrn, !(pRADEONEnt->PortInfo[0].DACType)); } } /* Secondary Head (mostly VGA, can be DVI on some OEM boards)*/ if (pRADEONEnt->PortInfo[1].MonType == MT_UNKNOWN) { if((pRADEONEnt->PortInfo[1].MonType = RADEONDisplayDDCConnected(pScrn, pRADEONEnt->PortInfo[1].DDCType, &pRADEONEnt->PortInfo[1]))); else if (info->IsMobility && (INREG(RADEON_FP2_GEN_CNTL) & RADEON_FP2_ON)) { /* non-DDC TMDS panel connected through DVO */ pRADEONEnt->PortInfo[1].MonType = MT_DFP; } else pRADEONEnt->PortInfo[1].MonType = RADEONCrtIsPhysicallyConnected(pScrn, !(pRADEONEnt->PortInfo[1].DACType)); } } if(ignore_edid) { pRADEONEnt->PortInfo[0].MonInfo = NULL; pRADEONEnt->PortInfo[1].MonInfo = NULL; } else { if (pRADEONEnt->PortInfo[0].MonInfo) { xf86DrvMsg(pScrn->scrnIndex, X_INFO, "EDID data from the display on port 1 ----------------------\n"); xf86PrintEDID(pRADEONEnt->PortInfo[0].MonInfo ); } if (pRADEONEnt->PortInfo[1].MonInfo) { xf86DrvMsg(pScrn->scrnIndex, X_INFO, "EDID data from the display on port 2-----------------------\n"); xf86PrintEDID(pRADEONEnt->PortInfo[1].MonInfo ); } } xf86DrvMsg(pScrn->scrnIndex, X_INFO, "\n"); pRADEONEnt->MonType1 = pRADEONEnt->PortInfo[0].MonType; pRADEONEnt->MonInfo1 = pRADEONEnt->PortInfo[0].MonInfo; pRADEONEnt->MonType2 = pRADEONEnt->PortInfo[1].MonType; pRADEONEnt->MonInfo2 = pRADEONEnt->PortInfo[1].MonInfo; if (pRADEONEnt->PortInfo[0].MonType == MT_NONE) { if (pRADEONEnt->PortInfo[1].MonType == MT_NONE) { pRADEONEnt->MonType1 = MT_CRT; pRADEONEnt->MonInfo1 = NULL; } else { RADEONConnector tmp; pRADEONEnt->MonType1 = pRADEONEnt->PortInfo[1].MonType; pRADEONEnt->MonInfo1 = pRADEONEnt->PortInfo[1].MonInfo; tmp = pRADEONEnt->PortInfo[0]; pRADEONEnt->PortInfo[0] = pRADEONEnt->PortInfo[1]; pRADEONEnt->PortInfo[1] = tmp; } pRADEONEnt->MonType2 = MT_NONE; pRADEONEnt->MonInfo2 = NULL; } info->DisplayType = pRADEONEnt->MonType1; pRADEONEnt->ReversedDAC = FALSE; info->OverlayOnCRTC2 = FALSE; info->MergeType = MT_NONE; if (pRADEONEnt->MonType2 != MT_NONE) { if(!pRADEONEnt->HasSecondary) { info->MergeType = pRADEONEnt->MonType2; } if (pRADEONEnt->PortInfo[1].DACType == DAC_TVDAC) { xf86DrvMsg(pScrn->scrnIndex, X_INFO, "Reversed DAC decteced\n"); pRADEONEnt->ReversedDAC = TRUE; } } else { pRADEONEnt->HasSecondary = FALSE; } xf86DrvMsg(pScrn->scrnIndex, X_INFO, "Primary:\n Monitor -- %s\n Connector -- %s\n DAC Type -- %s\n TMDS Type -- %s\n DDC Type -- %s\n", MonTypeName[pRADEONEnt->PortInfo[0].MonType+1], info->IsAtomBios ? ConnectorTypeNameATOM[pRADEONEnt->PortInfo[0].ConnectorType]: ConnectorTypeName[pRADEONEnt->PortInfo[0].ConnectorType], DACTypeName[pRADEONEnt->PortInfo[0].DACType+1], TMDSTypeName[pRADEONEnt->PortInfo[0].TMDSType+1], DDCTypeName[pRADEONEnt->PortInfo[0].DDCType]); xf86DrvMsg(pScrn->scrnIndex, X_INFO, "Secondary:\n Monitor -- %s\n Connector -- %s\n DAC Type -- %s\n TMDS Type -- %s\n DDC Type -- %s\n", MonTypeName[pRADEONEnt->PortInfo[1].MonType+1], info->IsAtomBios ? ConnectorTypeNameATOM[pRADEONEnt->PortInfo[1].ConnectorType]: ConnectorTypeName[pRADEONEnt->PortInfo[1].ConnectorType], DACTypeName[pRADEONEnt->PortInfo[1].DACType+1], TMDSTypeName[pRADEONEnt->PortInfo[1].TMDSType+1], DDCTypeName[pRADEONEnt->PortInfo[1].DDCType]); return TRUE; } /* This is called by RADEONPreInit to set up the default visual */ static Bool RADEONPreInitVisual(ScrnInfoPtr pScrn) { RADEONInfoPtr info = RADEONPTR(pScrn); if (!xf86SetDepthBpp(pScrn, 0, 0, 0, Support32bppFb)) return FALSE; switch (pScrn->depth) { case 8: case 15: case 16: case 24: break; default: xf86DrvMsg(pScrn->scrnIndex, X_ERROR, "Given depth (%d) is not supported by %s driver\n", pScrn->depth, RADEON_DRIVER_NAME); return FALSE; } xf86PrintDepthBpp(pScrn); info->fifo_slots = 0; info->pix24bpp = xf86GetBppFromDepth(pScrn, pScrn->depth); info->CurrentLayout.bitsPerPixel = pScrn->bitsPerPixel; info->CurrentLayout.depth = pScrn->depth; info->CurrentLayout.pixel_bytes = pScrn->bitsPerPixel / 8; info->CurrentLayout.pixel_code = (pScrn->bitsPerPixel != 16 ? pScrn->bitsPerPixel : pScrn->depth); if (info->pix24bpp == 24) { xf86DrvMsg(pScrn->scrnIndex, X_ERROR, "Radeon does NOT support 24bpp\n"); return FALSE; } xf86DrvMsg(pScrn->scrnIndex, X_INFO, "Pixel depth = %d bits stored in %d byte%s (%d bpp pixmaps)\n", pScrn->depth, info->CurrentLayout.pixel_bytes, info->CurrentLayout.pixel_bytes > 1 ? "s" : "", info->pix24bpp); if (!xf86SetDefaultVisual(pScrn, -1)) return FALSE; if (pScrn->depth > 8 && pScrn->defaultVisual != TrueColor) { xf86DrvMsg(pScrn->scrnIndex, X_ERROR, "Default visual (%s) is not supported at depth %d\n", xf86GetVisualName(pScrn->defaultVisual), pScrn->depth); return FALSE; } return TRUE; } /* This is called by RADEONPreInit to handle all color weight issues */ static Bool RADEONPreInitWeight(ScrnInfoPtr pScrn) { RADEONInfoPtr info = RADEONPTR(pScrn); /* Save flag for 6 bit DAC to use for setting CRTC registers. Otherwise use an 8 bit DAC, even if xf86SetWeight sets pScrn->rgbBits to some value other than 8. */ info->dac6bits = FALSE; if (pScrn->depth > 8) { rgb defaultWeight = { 0, 0, 0 }; if (!xf86SetWeight(pScrn, defaultWeight, defaultWeight)) return FALSE; } else { pScrn->rgbBits = 8; if (xf86ReturnOptValBool(info->Options, OPTION_DAC_6BIT, FALSE)) { pScrn->rgbBits = 6; info->dac6bits = TRUE; } } xf86DrvMsg(pScrn->scrnIndex, X_INFO, "Using %d bits per RGB (%d bit DAC)\n", pScrn->rgbBits, info->dac6bits ? 6 : 8); return TRUE; } /* Set up MC_FB_LOCATION and related registers */ static void RADEONSetFBLocation(ScrnInfoPtr pScrn) { RADEONInfoPtr info = RADEONPTR(pScrn); unsigned char *RADEONMMIO = info->MMIO; CARD32 mc_fb_location; CARD32 mc_agp_location = INREG(RADEON_MC_AGP_LOCATION); /* This function has many problems with newer cards. * Even with older cards, all registers changed here are not * restored properly when X quits, this will also cause * various problems, especially with radeonfb. * Since we don't have DRI support for R300 and above cards, * we just hardcode these values for now. * Need to revisit this whole function!!! */ if (IS_R300_VARIANT) { info->fbLocation = 0; if (!info->IsSecondary) { RADEONWaitForIdleMMIO(pScrn); OUTREG (RADEON_MC_FB_LOCATION, (INREG(RADEON_CONFIG_MEMSIZE) - 1) & 0xffff0000); OUTREG(RADEON_DISPLAY_BASE_ADDR, info->fbLocation); OUTREG(RADEON_DISPLAY2_BASE_ADDR, info->fbLocation); OUTREG(RADEON_OV0_BASE_ADDR, info->fbLocation); } return; } if (info->IsIGP) { mc_fb_location = INREG(RADEON_NB_TOM); OUTREG(RADEON_GRPH2_BUFFER_CNTL, INREG(RADEON_GRPH2_BUFFER_CNTL) & ~0x7f0000); } else #ifdef XF86DRI if ( info->directRenderingEnabled && info->drmMinor < 10 ) { mc_fb_location = (INREG(RADEON_CONFIG_APER_SIZE) - 1) & 0xffff0000U; } else #endif { CARD32 aper0_base = INREG(RADEON_CONFIG_APER_0_BASE); mc_fb_location = (aper0_base >> 16) | ((aper0_base + (INREG(RADEON_CONFIG_APER_SIZE) - 1) ) & 0xffff0000U); } info->fbLocation = (mc_fb_location & 0xffff) << 16; if (((mc_agp_location & 0xffff) << 16) != ((mc_fb_location & 0xffff0000U) + 0x10000)) { mc_agp_location = mc_fb_location & 0xffff0000U; mc_agp_location |= (mc_agp_location + 0x10000) >> 16; } RADEONWaitForIdleMMIO(pScrn); OUTREG(RADEON_MC_FB_LOCATION, mc_fb_location); OUTREG(RADEON_MC_AGP_LOCATION, mc_agp_location); OUTREG(RADEON_DISPLAY_BASE_ADDR, info->fbLocation); if (info->HasCRTC2) OUTREG(RADEON_DISPLAY2_BASE_ADDR, info->fbLocation); OUTREG(RADEON_OV0_BASE_ADDR, info->fbLocation); } static void RADEONGetVRamType(ScrnInfoPtr pScrn) { RADEONInfoPtr info = RADEONPTR(pScrn); unsigned char *RADEONMMIO = info->MMIO; CARD32 tmp; if (info->IsIGP || (info->ChipFamily >= CHIP_FAMILY_R300) || (INREG(RADEON_MEM_SDRAM_MODE_REG) & (1<<30))) info->IsDDR = TRUE; else info->IsDDR = FALSE; tmp = INREG(RADEON_MEM_CNTL); if (IS_R300_VARIANT) { tmp &= R300_MEM_NUM_CHANNELS_MASK; switch (tmp) { case 0: info->RamWidth = 64; break; case 1: info->RamWidth = 128; break; case 2: info->RamWidth = 256; break; default: info->RamWidth = 128; break; } } else if ((info->ChipFamily == CHIP_FAMILY_RV100) || (info->ChipFamily == CHIP_FAMILY_RS100) || (info->ChipFamily == CHIP_FAMILY_RS200)){ if (tmp & RV100_HALF_MODE) info->RamWidth = 32; else info->RamWidth = 64; } else { if (tmp & RADEON_MEM_NUM_CHANNELS_MASK) info->RamWidth = 128; else info->RamWidth = 64; } /* This may not be correct, as some cards can have half of channel disabled * ToDo: identify these cases */ } /* This is called by RADEONPreInit to handle config file overrides for * things like chipset and memory regions. Also determine memory size * and type. If memory type ever needs an override, put it in this * routine. */ static Bool RADEONPreInitConfig(ScrnInfoPtr pScrn) { RADEONInfoPtr info = RADEONPTR(pScrn); EntityInfoPtr pEnt = info->pEnt; GDevPtr dev = pEnt->device; MessageType from; unsigned char *RADEONMMIO = info->MMIO; #ifdef XF86DRI const char *s; #endif /* Chipset */ from = X_PROBED; if (dev->chipset && *dev->chipset) { info->Chipset = xf86StringToToken(RADEONChipsets, dev->chipset); from = X_CONFIG; } else if (dev->chipID >= 0) { info->Chipset = dev->chipID; from = X_CONFIG; } else { info->Chipset = info->PciInfo->chipType; } pScrn->chipset = (char *)xf86TokenToString(RADEONChipsets, info->Chipset); if (!pScrn->chipset) { xf86DrvMsg(pScrn->scrnIndex, X_ERROR, "ChipID 0x%04x is not recognized\n", info->Chipset); return FALSE; } if (info->Chipset < 0) { xf86DrvMsg(pScrn->scrnIndex, X_ERROR, "Chipset \"%s\" is not recognized\n", pScrn->chipset); return FALSE; } xf86DrvMsg(pScrn->scrnIndex, from, "Chipset: \"%s\" (ChipID = 0x%04x)\n", pScrn->chipset, info->Chipset); info->HasCRTC2 = TRUE; info->IsMobility = FALSE; info->IsIGP = FALSE; info->IsDellServer = FALSE; info->HasSingleDAC = FALSE; switch (info->Chipset) { case PCI_CHIP_RADEON_LY: case PCI_CHIP_RADEON_LZ: info->IsMobility = TRUE; info->ChipFamily = CHIP_FAMILY_RV100; break; case PCI_CHIP_RV100_QY: case PCI_CHIP_RV100_QZ: info->ChipFamily = CHIP_FAMILY_RV100; /* DELL triple-head configuration. */ if ((info->PciInfo->subsysVendor == PCI_VENDOR_DELL) && ((info->PciInfo->subsysCard == 0x016c) || (info->PciInfo->subsysCard == 0x016d) || (info->PciInfo->subsysCard == 0x016e) || (info->PciInfo->subsysCard == 0x016f) || (info->PciInfo->subsysCard == 0x0170) || (info->PciInfo->subsysCard == 0x017d) || (info->PciInfo->subsysCard == 0x017e) || (info->PciInfo->subsysCard == 0x0183) || (info->PciInfo->subsysCard == 0x018a) || (info->PciInfo->subsysCard == 0x019a))) { info->IsDellServer = TRUE; xf86DrvMsg(pScrn->scrnIndex, X_INFO, "DELL server detected, force to special setup\n"); } break; case PCI_CHIP_RS100_4336: info->IsMobility = TRUE; case PCI_CHIP_RS100_4136: info->ChipFamily = CHIP_FAMILY_RS100; info->IsIGP = TRUE; break; case PCI_CHIP_RS200_4337: info->IsMobility = TRUE; case PCI_CHIP_RS200_4137: info->ChipFamily = CHIP_FAMILY_RS200; info->IsIGP = TRUE; break; case PCI_CHIP_RS250_4437: info->IsMobility = TRUE; case PCI_CHIP_RS250_4237: info->ChipFamily = CHIP_FAMILY_RS200; info->IsIGP = TRUE; break; case PCI_CHIP_R200_BB: case PCI_CHIP_R200_BC: case PCI_CHIP_R200_QH: case PCI_CHIP_R200_QL: case PCI_CHIP_R200_QM: info->ChipFamily = CHIP_FAMILY_R200; break; case PCI_CHIP_RADEON_LW: case PCI_CHIP_RADEON_LX: info->IsMobility = TRUE; case PCI_CHIP_RV200_QW: /* RV200 desktop */ case PCI_CHIP_RV200_QX: info->ChipFamily = CHIP_FAMILY_RV200; break; case PCI_CHIP_RV250_Ld: case PCI_CHIP_RV250_Lf: case PCI_CHIP_RV250_Lg: info->IsMobility = TRUE; case PCI_CHIP_RV250_If: case PCI_CHIP_RV250_Ig: info->ChipFamily = CHIP_FAMILY_RV250; break; case PCI_CHIP_RS300_5835: case PCI_CHIP_RS350_7835: info->IsMobility = TRUE; case PCI_CHIP_RS300_5834: case PCI_CHIP_RS350_7834: info->ChipFamily = CHIP_FAMILY_RS300; info->IsIGP = TRUE; info->HasSingleDAC = TRUE; break; case PCI_CHIP_RV280_5C61: case PCI_CHIP_RV280_5C63: info->IsMobility = TRUE; case PCI_CHIP_RV280_5960: case PCI_CHIP_RV280_5961: case PCI_CHIP_RV280_5962: case PCI_CHIP_RV280_5964: info->ChipFamily = CHIP_FAMILY_RV280; break; case PCI_CHIP_R300_AD: case PCI_CHIP_R300_AE: case PCI_CHIP_R300_AF: case PCI_CHIP_R300_AG: case PCI_CHIP_R300_ND: case PCI_CHIP_R300_NE: case PCI_CHIP_R300_NF: case PCI_CHIP_R300_NG: info->ChipFamily = CHIP_FAMILY_R300; break; case PCI_CHIP_RV350_NP: case PCI_CHIP_RV350_NQ: case PCI_CHIP_RV350_NR: case PCI_CHIP_RV350_NS: case PCI_CHIP_RV350_NT: case PCI_CHIP_RV350_NV: info->IsMobility = TRUE; case PCI_CHIP_RV350_AP: case PCI_CHIP_RV350_AQ: case PCI_CHIP_RV360_AR: case PCI_CHIP_RV350_AS: case PCI_CHIP_RV350_AT: case PCI_CHIP_RV350_AV: info->ChipFamily = CHIP_FAMILY_RV350; break; case PCI_CHIP_R350_AH: case PCI_CHIP_R350_AI: case PCI_CHIP_R350_AJ: case PCI_CHIP_R350_AK: case PCI_CHIP_R350_NH: case PCI_CHIP_R350_NI: case PCI_CHIP_R350_NK: case PCI_CHIP_R360_NJ: info->ChipFamily = CHIP_FAMILY_R350; break; case PCI_CHIP_RV380_3150: case PCI_CHIP_RV380_3154: info->IsMobility = TRUE; case PCI_CHIP_RV380_3E50: case PCI_CHIP_RV380_3E54: info->ChipFamily = CHIP_FAMILY_RV380; break; case PCI_CHIP_RV370_5460: case PCI_CHIP_RV370_5464: info->IsMobility = TRUE; case PCI_CHIP_RV370_5B60: case PCI_CHIP_RV370_5B64: case PCI_CHIP_RV370_5B65: info->ChipFamily = CHIP_FAMILY_RV380; break; case PCI_CHIP_R420_JN: info->IsMobility = TRUE; case PCI_CHIP_R420_JH: case PCI_CHIP_R420_JI: case PCI_CHIP_R420_JJ: case PCI_CHIP_R420_JK: case PCI_CHIP_R420_JL: case PCI_CHIP_R420_JM: case PCI_CHIP_R420_JP: info->ChipFamily = CHIP_FAMILY_R420; break; case PCI_CHIP_R423_UH: case PCI_CHIP_R423_UI: case PCI_CHIP_R423_UJ: case PCI_CHIP_R423_UK: case PCI_CHIP_R423_UQ: case PCI_CHIP_R423_UR: case PCI_CHIP_R423_UT: case PCI_CHIP_R423_5D57: info->ChipFamily = CHIP_FAMILY_R420; break; default: /* Original Radeon/7200 */ info->ChipFamily = CHIP_FAMILY_RADEON; info->HasCRTC2 = FALSE; } /* Framebuffer */ from = X_PROBED; info->LinearAddr = info->PciInfo->memBase[0] & 0xfe000000; pScrn->memPhysBase = info->LinearAddr; if (dev->MemBase) { xf86DrvMsg(pScrn->scrnIndex, X_INFO, "Linear address override, using 0x%08lx instead of 0x%08lx\n", dev->MemBase, info->LinearAddr); info->LinearAddr = dev->MemBase; from = X_CONFIG; } else if (!info->LinearAddr) { xf86DrvMsg(pScrn->scrnIndex, X_ERROR, "No valid linear framebuffer address\n"); return FALSE; } xf86DrvMsg(pScrn->scrnIndex, from, "Linear framebuffer at 0x%08lx\n", info->LinearAddr); /* BIOS */ from = X_PROBED; info->BIOSAddr = info->PciInfo->biosBase & 0xfffe0000; if (dev->BiosBase) { xf86DrvMsg(pScrn->scrnIndex, X_INFO, "BIOS address override, using 0x%08lx instead of 0x%08lx\n", dev->BiosBase, info->BIOSAddr); info->BIOSAddr = dev->BiosBase; from = X_CONFIG; } if (info->BIOSAddr) { xf86DrvMsg(pScrn->scrnIndex, from, "BIOS at 0x%08lx\n", info->BIOSAddr); } /* Read registers used to determine options */ from = X_PROBED; if (info->FBDev) pScrn->videoRam = fbdevHWGetVidmem(pScrn) / 1024; else if ((info->ChipFamily == CHIP_FAMILY_RS100) || (info->ChipFamily == CHIP_FAMILY_RS200) || (info->ChipFamily == CHIP_FAMILY_RS300)) { CARD32 tom = INREG(RADEON_NB_TOM); pScrn->videoRam = (((tom >> 16) - (tom & 0xffff) + 1) << 6); OUTREG(RADEON_CONFIG_MEMSIZE, pScrn->videoRam * 1024); } else { /* There are different HDP mapping schemes depending on single/multi funciton setting, * chip family, HDP mode, and the generation of HDP mapping scheme. * To make things simple, we only allow maximum 128M addressable FB. Anything more than * 128M is configured as invisible FB to CPU that can only be accessed from chip side. */ pScrn->videoRam = INREG(RADEON_CONFIG_MEMSIZE) / 1024; if (pScrn->videoRam > 128*1024) pScrn->videoRam = 128*1024; if ((info->ChipFamily == CHIP_FAMILY_RV350) || (info->ChipFamily == CHIP_FAMILY_RV380) || (info->ChipFamily == CHIP_FAMILY_R420)) { OUTREGP (RADEON_HOST_PATH_CNTL, (1<<23), ~(1<<23)); } } /* Some production boards of m6 will return 0 if it's 8 MB */ if (pScrn->videoRam == 0) pScrn->videoRam = 8192; if (info->IsSecondary) { /* FIXME: For now, split FB into two equal sections. This should * be able to be adjusted by user with a config option. */ RADEONEntPtr pRADEONEnt = RADEONEntPriv(pScrn); RADEONInfoPtr info1; pScrn->videoRam /= 2; pRADEONEnt->pPrimaryScrn->videoRam = pScrn->videoRam; info1 = RADEONPTR(pRADEONEnt->pPrimaryScrn); info1->FbMapSize = pScrn->videoRam * 1024; info->LinearAddr += pScrn->videoRam * 1024; info1->MergedFB = FALSE; } info->R300CGWorkaround = (info->ChipFamily == CHIP_FAMILY_R300 && (INREG(RADEON_CONFIG_CNTL) & RADEON_CFG_ATI_REV_ID_MASK) == RADEON_CFG_ATI_REV_A11); info->MemCntl = INREG(RADEON_SDRAM_MODE_REG); info->BusCntl = INREG(RADEON_BUS_CNTL); RADEONGetVRamType(pScrn); if (dev->videoRam) { xf86DrvMsg(pScrn->scrnIndex, X_INFO, "Video RAM override, using %d kB instead of %d kB\n", dev->videoRam, pScrn->videoRam); from = X_CONFIG; pScrn->videoRam = dev->videoRam; } pScrn->videoRam &= ~1023; info->FbMapSize = pScrn->videoRam * 1024; xf86DrvMsg(pScrn->scrnIndex, from, "VideoRAM: %d kByte (%d bit %s SDRAM)\n", pScrn->videoRam, info->RamWidth, info->IsDDR?"DDR":"SDR"); #ifdef XF86DRI /* AGP/PCI */ /* Proper autodetection of an AGP capable device requires examining * PCI config registers to determine if the device implements extended * PCI capabilities, and then walking the capability list as indicated * in the PCI 2.2 and AGP 2.0 specifications, to determine if AGP * capability is present. The procedure is outlined as follows: * * 1) Test bit 4 (CAP_LIST) of the PCI status register of the device * to determine wether or not this device implements any extended * capabilities. If this bit is zero, then the device is a PCI 2.1 * or earlier device and is not AGP capable, and we can conclude it * to be a PCI device. * * 2) If bit 4 of the status register is set, then the device implements * extended capabilities. There is an 8 bit wide capabilities pointer * register located at offset 0x34 in PCI config space which points to * the first capability in a linked list of extended capabilities that * this device implements. The lower two bits of this register are * reserved and MBZ so must be masked out. * * 3) The extended capabilities list is formed by one or more extended * capabilities structures which are aligned on DWORD boundaries. * The first byte of the structure is the capability ID (CAP_ID) * indicating what extended capability this structure refers to. The * second byte of the structure is an offset from the beginning of * PCI config space pointing to the next capability in the linked * list (NEXT_PTR) or NULL (0x00) at the end of the list. The lower * two bits of this pointer are reserved and MBZ. By examining the * CAP_ID of each capability and walking through the list, we will * either find the AGP_CAP_ID (0x02) indicating this device is an * AGP device, or we'll reach the end of the list, indicating it is * a PCI device. * * Mike A. Harris * * References: * - PCI Local Bus Specification Revision 2.2, Chapter 6 * - AGP Interface Specification Revision 2.0, Section 6.1.5 */ info->IsPCI = TRUE; if (pciReadLong(info->PciTag, PCI_CMD_STAT_REG) & RADEON_CAP_LIST) { CARD32 cap_ptr, cap_id; cap_ptr = pciReadLong(info->PciTag, RADEON_CAPABILITIES_PTR_PCI_CONFIG) & RADEON_CAP_PTR_MASK; while(cap_ptr != RADEON_CAP_ID_NULL) { cap_id = pciReadLong(info->PciTag, cap_ptr); if ((cap_id & 0xff)== RADEON_CAP_ID_AGP) { info->IsPCI = FALSE; break; } cap_ptr = (cap_id >> 8) & RADEON_CAP_PTR_MASK; } } xf86DrvMsg(pScrn->scrnIndex, X_INFO, "%s card detected\n", (info->IsPCI) ? "PCI" : "AGP"); if ((s = xf86GetOptValString(info->Options, OPTION_BUS_TYPE))) { if (strcmp(s, "AGP") == 0) { info->IsPCI = FALSE; xf86DrvMsg(pScrn->scrnIndex, X_CONFIG, "Forced into AGP mode\n"); } else if (strcmp(s, "PCI") == 0) { info->IsPCI = TRUE; xf86DrvMsg(pScrn->scrnIndex, X_CONFIG, "Forced into PCI mode\n"); } else if (strcmp(s, "PCIE") == 0) { info->IsPCI = TRUE; xf86DrvMsg(pScrn->scrnIndex, X_CONFIG, "PCI Express not supported yet, using PCI mode\n"); } else { xf86DrvMsg(pScrn->scrnIndex, X_CONFIG, "Invalid BusType option, using detected type\n"); } } #endif xf86GetOptValBool(info->Options, OPTION_SHOWCACHE, &info->showCache); if (info->showCache) xf86DrvMsg(pScrn->scrnIndex, X_CONFIG, "Option ShowCache enabled\n"); #ifdef RENDER info->RenderAccel = xf86ReturnOptValBool (info->Options, OPTION_RENDER_ACCEL, TRUE); #endif return TRUE; } static void RADEONI2CGetBits(I2CBusPtr b, int *Clock, int *data) { ScrnInfoPtr pScrn = xf86Screens[b->scrnIndex]; RADEONInfoPtr info = RADEONPTR(pScrn); unsigned long val; unsigned char *RADEONMMIO = info->MMIO; /* Get the result */ val = INREG(info->DDCReg); *Clock = (val & RADEON_GPIO_Y_1) != 0; *data = (val & RADEON_GPIO_Y_0) != 0; } static void RADEONI2CPutBits(I2CBusPtr b, int Clock, int data) { ScrnInfoPtr pScrn = xf86Screens[b->scrnIndex]; RADEONInfoPtr info = RADEONPTR(pScrn); unsigned long val; unsigned char *RADEONMMIO = info->MMIO; val = INREG(info->DDCReg) & (CARD32)~(RADEON_GPIO_EN_0 | RADEON_GPIO_EN_1); val |= (Clock ? 0:RADEON_GPIO_EN_1); val |= (data ? 0:RADEON_GPIO_EN_0); OUTREG(info->DDCReg, val); /* read back to improve reliability on some cards. */ val = INREG(info->DDCReg); } static Bool RADEONI2cInit(ScrnInfoPtr pScrn) { RADEONInfoPtr info = RADEONPTR(pScrn); info->pI2CBus = xf86CreateI2CBusRec(); if (!info->pI2CBus) return FALSE; info->pI2CBus->BusName = "DDC"; info->pI2CBus->scrnIndex = pScrn->scrnIndex; info->pI2CBus->I2CPutBits = RADEONI2CPutBits; info->pI2CBus->I2CGetBits = RADEONI2CGetBits; info->pI2CBus->AcknTimeout = 5; if (!xf86I2CBusInit(info->pI2CBus)) return FALSE; return TRUE; } static void RADEONPreInitDDC(ScrnInfoPtr pScrn) { RADEONInfoPtr info = RADEONPTR(pScrn); /* vbeInfoPtr pVbe; */ info->ddc1 = FALSE; info->ddc_bios = FALSE; if (!xf86LoadSubModule(pScrn, "ddc")) { info->ddc2 = FALSE; } else { xf86LoaderReqSymLists(ddcSymbols, NULL); info->ddc2 = TRUE; } /* DDC can use I2C bus */ /* Load I2C if we have the code to use it */ if (info->ddc2) { if (xf86LoadSubModule(pScrn, "i2c")) { xf86LoaderReqSymLists(i2cSymbols,NULL); info->ddc2 = RADEONI2cInit(pScrn); } else info->ddc2 = FALSE; } } /* BIOS may not have right panel size, we search through all supported * DDC modes looking for the maximum panel size. */ static void RADEONUpdatePanelSize(ScrnInfoPtr pScrn) { int j; RADEONInfoPtr info = RADEONPTR (pScrn); xf86MonPtr ddc = pScrn->monitor->DDC; DisplayModePtr p; /* Go thru detailed timing table first */ for (j = 0; j < 4; j++) { if (ddc->det_mon[j].type == 0) { struct detailed_timings *d_timings = &ddc->det_mon[j].section.d_timings; if (info->PanelXRes <= d_timings->h_active && info->PanelYRes <= d_timings->v_active) { if (info->DotClock) continue; /* Timings already inited */ info->PanelXRes = d_timings->h_active; info->PanelYRes = d_timings->v_active; info->DotClock = d_timings->clock / 1000; info->HOverPlus = d_timings->h_sync_off; info->HSyncWidth = d_timings->h_sync_width; info->HBlank = d_timings->h_blanking; info->VOverPlus = d_timings->v_sync_off; info->VSyncWidth = d_timings->v_sync_width; info->VBlank = d_timings->v_blanking; } } } /* Search thru standard VESA modes from EDID */ for (j = 0; j < 8; j++) { if ((info->PanelXRes < ddc->timings2[j].hsize) && (info->PanelYRes < ddc->timings2[j].vsize)) { for (p = pScrn->monitor->Modes; p && p->next; p = p->next->next) { if ((ddc->timings2[j].hsize == p->HDisplay) && (ddc->timings2[j].vsize == p->VDisplay)) { float refresh = (float)p->Clock * 1000.0 / p->HTotal / p->VTotal; if (abs((float)ddc->timings2[j].refresh - refresh) < 1.0) { /* Is this good enough? */ info->PanelXRes = ddc->timings2[j].hsize; info->PanelYRes = ddc->timings2[j].vsize; info->HBlank = p->HTotal - p->HDisplay; info->HOverPlus = p->HSyncStart - p->HDisplay; info->HSyncWidth = p->HSyncEnd - p->HSyncStart; info->VBlank = p->VTotal - p->VDisplay; info->VOverPlus = p->VSyncStart - p->VDisplay; info->VSyncWidth = p->VSyncEnd - p->VSyncStart; info->DotClock = p->Clock; info->Flags = (ddc->det_mon[j].section.d_timings.interlaced ? V_INTERLACE : 0); if (ddc->det_mon[j].section.d_timings.sync == 3) { switch (ddc->det_mon[j].section.d_timings.misc) { case 0: info->Flags |= V_NHSYNC | V_NVSYNC; break; case 1: info->Flags |= V_PHSYNC | V_NVSYNC; break; case 2: info->Flags |= V_NHSYNC | V_PVSYNC; break; case 3: info->Flags |= V_PHSYNC | V_PVSYNC; break; } } } } } } } xf86DrvMsg(pScrn->scrnIndex, X_INFO, "Panel size found from DDC: %dx%d\n", info->PanelXRes, info->PanelYRes); } /* This function will sort all modes according to their resolution. * Highest resolution first. */ static void RADEONSortModes(DisplayModePtr *new, DisplayModePtr *first, DisplayModePtr *last) { DisplayModePtr p; p = *last; while (p) { if ((((*new)->HDisplay < p->HDisplay) && ((*new)->VDisplay < p->VDisplay)) || (((*new)->HDisplay == p->HDisplay) && ((*new)->VDisplay == p->VDisplay) && ((*new)->Clock < p->Clock))) { if (p->next) p->next->prev = *new; (*new)->prev = p; (*new)->next = p->next; p->next = *new; if (!((*new)->next)) *last = *new; break; } if (!p->prev) { (*new)->prev = NULL; (*new)->next = p; p->prev = *new; *first = *new; break; } p = p->prev; } if (!*first) { *first = *new; (*new)->prev = NULL; (*new)->next = NULL; *last = *new; } } static void RADEONSetPitch (ScrnInfoPtr pScrn) { int dummy = pScrn->virtualX; /* FIXME: May need to validate line pitch here */ switch (pScrn->depth / 8) { case 1: dummy = (pScrn->virtualX + 127) & ~127; break; case 2: dummy = (pScrn->virtualX + 31) & ~31; break; case 3: case 4: dummy = (pScrn->virtualX + 15) & ~15; break; } pScrn->displayWidth = dummy; } /* When no mode provided in config file, this will add all modes supported in * DDC date the pScrn->modes list */ static DisplayModePtr RADEONDDCModes(ScrnInfoPtr pScrn) { DisplayModePtr p; DisplayModePtr last = NULL; DisplayModePtr new = NULL; DisplayModePtr first = NULL; int count = 0; int j, tmp; char stmp[32]; xf86MonPtr ddc = pScrn->monitor->DDC; /* Go thru detailed timing table first */ for (j = 0; j < 4; j++) { if (ddc->det_mon[j].type == 0) { struct detailed_timings *d_timings = &ddc->det_mon[j].section.d_timings; if (d_timings->h_active == 0 || d_timings->v_active == 0) break; new = xnfcalloc(1, sizeof (DisplayModeRec)); memset(new, 0, sizeof (DisplayModeRec)); new->HDisplay = d_timings->h_active; new->VDisplay = d_timings->v_active; sprintf(stmp, "%dx%d", new->HDisplay, new->VDisplay); new->name = xnfalloc(strlen(stmp) + 1); strcpy(new->name, stmp); new->HTotal = new->HDisplay + d_timings->h_blanking; new->HSyncStart = new->HDisplay + d_timings->h_sync_off; new->HSyncEnd = new->HSyncStart + d_timings->h_sync_width; new->VTotal = new->VDisplay + d_timings->v_blanking; new->VSyncStart = new->VDisplay + d_timings->v_sync_off; new->VSyncEnd = new->VSyncStart + d_timings->v_sync_width; new->Clock = d_timings->clock / 1000; new->Flags = (d_timings->interlaced ? V_INTERLACE : 0); new->status = MODE_OK; new->type = M_T_DEFAULT; if (d_timings->sync == 3) { switch (d_timings->misc) { case 0: new->Flags |= V_NHSYNC | V_NVSYNC; break; case 1: new->Flags |= V_PHSYNC | V_NVSYNC; break; case 2: new->Flags |= V_NHSYNC | V_PVSYNC; break; case 3: new->Flags |= V_PHSYNC | V_PVSYNC; break; } } count++; xf86DrvMsg(pScrn->scrnIndex, X_INFO, "Valid Mode from Detailed timing table: %s\n", new->name); RADEONSortModes(&new, &first, &last); } } /* Search thru standard VESA modes from EDID */ for (j = 0; j < 8; j++) { for (p = pScrn->monitor->Modes; p && p->next; p = p->next->next) { /* Ignore all double scan modes */ if ((ddc->timings2[j].hsize == p->HDisplay) && (ddc->timings2[j].vsize == p->VDisplay)) { float refresh = (float)p->Clock * 1000.0 / p->HTotal / p->VTotal; if (abs((float)ddc->timings2[j].refresh - refresh) < 1.0) { /* Is this good enough? */ new = xnfcalloc(1, sizeof (DisplayModeRec)); memcpy(new, p, sizeof(DisplayModeRec)); new->name = xnfalloc(strlen(p->name) + 1); strcpy(new->name, p->name); new->status = MODE_OK; new->type = M_T_DEFAULT; count++; xf86DrvMsg(pScrn->scrnIndex, X_INFO, "Valid Mode from standard timing table: %s\n", new->name); RADEONSortModes(&new, &first, &last); break; } } } } /* Search thru established modes from EDID */ tmp = (ddc->timings1.t1 << 8) | ddc->timings1.t2; for (j = 0; j < 16; j++) { if (tmp & (1 << j)) { for (p = pScrn->monitor->Modes; p && p->next; p = p->next->next) { if ((est_timings[j].hsize == p->HDisplay) && (est_timings[j].vsize == p->VDisplay)) { float refresh = (float)p->Clock * 1000.0 / p->HTotal / p->VTotal; if (abs((float)est_timings[j].refresh - refresh) < 1.0) { /* Is this good enough? */ new = xnfcalloc(1, sizeof (DisplayModeRec)); memcpy(new, p, sizeof(DisplayModeRec)); new->name = xnfalloc(strlen(p->name) + 1); strcpy(new->name, p->name); new->status = MODE_OK; new->type = M_T_DEFAULT; count++; xf86DrvMsg(pScrn->scrnIndex, X_INFO, "Valid Mode from established timing " "table: %s\n", new->name); RADEONSortModes(&new, &first, &last); break; } } } } } xf86DrvMsg(pScrn->scrnIndex, X_INFO, "Total of %d mode(s) found.\n", count); return first; } /* XFree86's xf86ValidateModes routine doesn't work well with DDC modes, * so here is our own validation routine. */ static int RADEONValidateDDCModes(ScrnInfoPtr pScrn1, char **ppModeName, RADEONMonitorType DisplayType, int crtc2) { RADEONInfoPtr info = RADEONPTR(pScrn1); DisplayModePtr p; DisplayModePtr last = NULL; DisplayModePtr first = NULL; DisplayModePtr ddcModes = NULL; int count = 0; int i, width, height; ScrnInfoPtr pScrn = pScrn1; if (crtc2) pScrn = info->CRT2pScrn; pScrn->virtualX = pScrn1->display->virtualX; pScrn->virtualY = pScrn1->display->virtualY; if (pScrn->monitor->DDC && !info->UseBiosDividers) { int maxVirtX = pScrn->virtualX; int maxVirtY = pScrn->virtualY; if ((DisplayType != MT_CRT) && (!info->IsSecondary) && (!crtc2)) { /* The panel size we collected from BIOS may not be the * maximum size supported by the panel. If not, we update * it now. These will be used if no matching mode can be * found from EDID data. */ RADEONUpdatePanelSize(pScrn); } /* Collect all of the DDC modes */ first = last = ddcModes = RADEONDDCModes(pScrn); for (p = ddcModes; p; p = p->next) { /* If primary head is a flat panel, use RMX by default */ if ((!info->IsSecondary && DisplayType != MT_CRT) && (!info->ddc_mode) && (!crtc2)) { /* These values are effective values after expansion. * They are not really used to set CRTC registers. */ p->HTotal = info->PanelXRes + info->HBlank; p->HSyncStart = info->PanelXRes + info->HOverPlus; p->HSyncEnd = p->HSyncStart + info->HSyncWidth; p->VTotal = info->PanelYRes + info->VBlank; p->VSyncStart = info->PanelYRes + info->VOverPlus; p->VSyncEnd = p->VSyncStart + info->VSyncWidth; p->Clock = info->DotClock; p->Flags |= RADEON_USE_RMX; } maxVirtX = MAX(maxVirtX, p->HDisplay); maxVirtY = MAX(maxVirtY, p->VDisplay); count++; last = p; } /* Match up modes that are specified in the XF86Config file */ if (ppModeName[0]) { DisplayModePtr next; /* Reset the max virtual dimensions */ maxVirtX = pScrn->virtualX; maxVirtY = pScrn->virtualY; /* Reset list */ first = last = NULL; for (i = 0; ppModeName[i]; i++) { /* FIXME: Use HDisplay and VDisplay instead of mode string */ if (sscanf(ppModeName[i], "%dx%d", &width, &height) == 2) { for (p = ddcModes; p; p = next) { next = p->next; if (p->HDisplay == width && p->VDisplay == height) { /* We found a DDC mode that matches the one requested in the XF86Config file */ p->type |= M_T_USERDEF; /* Update the max virtual setttings */ maxVirtX = MAX(maxVirtX, width); maxVirtY = MAX(maxVirtY, height); /* Unhook from DDC modes */ if (p->prev) p->prev->next = p->next; if (p->next) p->next->prev = p->prev; if (p == ddcModes) ddcModes = p->next; /* Add to used modes */ if (last) { last->next = p; p->prev = last; } else { first = p; p->prev = NULL; } p->next = NULL; last = p; break; } } } } /* * Add remaining DDC modes if they're smaller than the user * specified modes */ for (p = ddcModes; p; p = next) { next = p->next; if (p->HDisplay <= maxVirtX && p->VDisplay <= maxVirtY) { /* Unhook from DDC modes */ if (p->prev) p->prev->next = p->next; if (p->next) p->next->prev = p->prev; if (p == ddcModes) ddcModes = p->next; /* Add to used modes */ if (last) { last->next = p; p->prev = last; } else { first = p; p->prev = NULL; } p->next = NULL; last = p; } } /* Delete unused modes */ while (ddcModes) xf86DeleteMode(&ddcModes, ddcModes); } else { /* * No modes were configured, so we make the DDC modes * available for the user to cycle through. */ for (p = ddcModes; p; p = p->next) p->type |= M_T_USERDEF; } if (crtc2) { pScrn->virtualX = maxVirtX; pScrn->virtualY = maxVirtY; } else { pScrn->virtualX = pScrn->display->virtualX = maxVirtX; pScrn->virtualY = pScrn->display->virtualY = maxVirtY; } } /* Close the doubly-linked mode list, if we found any usable modes */ if (last) { last->next = first; first->prev = last; pScrn->modes = first; RADEONSetPitch(pScrn); } xf86DrvMsg(pScrn->scrnIndex, X_INFO, "Total number of valid DDC mode(s) found: %d\n", count); return count; } /* This is used only when no mode is specified for FP and no ddc is * available. We force it to native mode, if possible. */ static DisplayModePtr RADEONFPNativeMode(ScrnInfoPtr pScrn) { RADEONInfoPtr info = RADEONPTR(pScrn); DisplayModePtr new = NULL; char stmp[32]; if (info->PanelXRes != 0 && info->PanelYRes != 0 && info->DotClock != 0) { /* Add native panel size */ new = xnfcalloc(1, sizeof (DisplayModeRec)); sprintf(stmp, "%dx%d", info->PanelXRes, info->PanelYRes); new->name = xnfalloc(strlen(stmp) + 1); strcpy(new->name, stmp); new->HDisplay = info->PanelXRes; new->VDisplay = info->PanelYRes; new->HTotal = new->HDisplay + info->HBlank; new->HSyncStart = new->HDisplay + info->HOverPlus; new->HSyncEnd = new->HSyncStart + info->HSyncWidth; new->VTotal = new->VDisplay + info->VBlank; new->VSyncStart = new->VDisplay + info->VOverPlus; new->VSyncEnd = new->VSyncStart + info->VSyncWidth; new->Clock = info->DotClock; new->Flags = 0; new->type = M_T_USERDEF; new->next = NULL; new->prev = NULL; pScrn->display->virtualX = pScrn->virtualX = MAX(pScrn->virtualX, info->PanelXRes); pScrn->display->virtualY = pScrn->virtualY = MAX(pScrn->virtualY, info->PanelYRes); xf86DrvMsg(pScrn->scrnIndex, X_INFO, "No valid mode specified, force to native mode\n"); } return new; } /* FP mode initialization routine for using on-chip RMX to scale */ static int RADEONValidateFPModes(ScrnInfoPtr pScrn, char **ppModeName) { RADEONInfoPtr info = RADEONPTR(pScrn); DisplayModePtr last = NULL; DisplayModePtr new = NULL; DisplayModePtr first = NULL; DisplayModePtr p, tmp; int count = 0; int i, width, height; pScrn->virtualX = pScrn->display->virtualX; pScrn->virtualY = pScrn->display->virtualY; /* We have a flat panel connected to the primary display, and we * don't have any DDC info. */ for (i = 0; ppModeName[i] != NULL; i++) { if (sscanf(ppModeName[i], "%dx%d", &width, &height) != 2) continue; /* Note: We allow all non-standard modes as long as they do not * exceed the native resolution of the panel. Since these modes * need the internal RMX unit in the video chips (and there is * only one per card), this will only apply to the primary head. */ if (width < 320 || width > info->PanelXRes || height < 200 || height > info->PanelYRes) { xf86DrvMsg(pScrn->scrnIndex, X_WARNING, "Mode %s is out of range.\n", ppModeName[i]); xf86DrvMsg(pScrn->scrnIndex, X_WARNING, "Valid modes must be between 320x200-%dx%d\n", info->PanelXRes, info->PanelYRes); continue; } new = xnfcalloc(1, sizeof(DisplayModeRec)); new->name = xnfalloc(strlen(ppModeName[i]) + 1); strcpy(new->name, ppModeName[i]); new->HDisplay = width; new->VDisplay = height; /* These values are effective values after expansion They are * not really used to set CRTC registers. */ new->HTotal = info->PanelXRes + info->HBlank; new->HSyncStart = info->PanelXRes + info->HOverPlus; new->HSyncEnd = new->HSyncStart + info->HSyncWidth; new->VTotal = info->PanelYRes + info->VBlank; new->VSyncStart = info->PanelYRes + info->VOverPlus; new->VSyncEnd = new->VSyncStart + info->VSyncWidth; new->Clock = info->DotClock; new->Flags |= RADEON_USE_RMX; new->type |= M_T_USERDEF; new->next = NULL; new->prev = last; if (last) last->next = new; last = new; if (!first) first = new; pScrn->display->virtualX = pScrn->virtualX = MAX(pScrn->virtualX, width); pScrn->display->virtualY = pScrn->virtualY = MAX(pScrn->virtualY, height); count++; xf86DrvMsg(pScrn->scrnIndex, X_INFO, "Valid mode using on-chip RMX: %s\n", new->name); } /* If all else fails, add the native mode */ if (!count) { first = last = RADEONFPNativeMode(pScrn); if (first) count = 1; } /* add in all default vesa modes smaller than panel size, used for randr*/ for (p = pScrn->monitor->Modes; p && p->next; p = p->next->next) { if ((p->HDisplay <= info->PanelXRes) && (p->VDisplay <= info->PanelYRes)) { tmp = first; while (tmp) { if ((p->HDisplay == tmp->HDisplay) && (p->VDisplay == tmp->VDisplay)) break; tmp = tmp->next; } if (!tmp) { new = xnfcalloc(1, sizeof(DisplayModeRec)); new->name = xnfalloc(strlen(p->name) + 1); strcpy(new->name, p->name); new->HDisplay = p->HDisplay; new->VDisplay = p->VDisplay; /* These values are effective values after expansion They are * not really used to set CRTC registers. */ new->HTotal = info->PanelXRes + info->HBlank; new->HSyncStart = info->PanelXRes + info->HOverPlus; new->HSyncEnd = new->HSyncStart + info->HSyncWidth; new->VTotal = info->PanelYRes + info->VBlank; new->VSyncStart = info->PanelYRes + info->VOverPlus; new->VSyncEnd = new->VSyncStart + info->VSyncWidth; new->Clock = info->DotClock; new->Flags |= RADEON_USE_RMX; new->type |= M_T_DEFAULT; new->next = NULL; new->prev = last; if (last) last->next = new; last = new; if (!first) first = new; } } } /* Close the doubly-linked mode list, if we found any usable modes */ if (last) { last->next = first; first->prev = last; pScrn->modes = first; RADEONSetPitch(pScrn); } xf86DrvMsg(pScrn->scrnIndex, X_INFO, "Total number of valid FP mode(s) found: %d\n", count); return count; } /* This is called by RADEONPreInit to initialize gamma correction */ static Bool RADEONPreInitGamma(ScrnInfoPtr pScrn) { Gamma zeros = { 0.0, 0.0, 0.0 }; if (!xf86SetGamma(pScrn, zeros)) return FALSE; return TRUE; } static void RADEONSetSyncRangeFromEdid(ScrnInfoPtr pScrn, int flag) { MonPtr mon = pScrn->monitor; xf86MonPtr ddc = mon->DDC; int i; if (flag) { /* HSync */ for (i = 0; i < 4; i++) { if (ddc->det_mon[i].type == DS_RANGES) { mon->nHsync = 1; mon->hsync[0].lo = ddc->det_mon[i].section.ranges.min_h; mon->hsync[0].hi = ddc->det_mon[i].section.ranges.max_h; return; } } /* If no sync ranges detected in detailed timing table, let's * try to derive them from supported VESA modes. Are we doing * too much here!!!? */ i = 0; if (ddc->timings1.t1 & 0x02) { /* 800x600@56 */ mon->hsync[i].lo = mon->hsync[i].hi = 35.2; i++; } if (ddc->timings1.t1 & 0x04) { /* 640x480@75 */ mon->hsync[i].lo = mon->hsync[i].hi = 37.5; i++; } if ((ddc->timings1.t1 & 0x08) || (ddc->timings1.t1 & 0x01)) { mon->hsync[i].lo = mon->hsync[i].hi = 37.9; i++; } if (ddc->timings1.t2 & 0x40) { mon->hsync[i].lo = mon->hsync[i].hi = 46.9; i++; } if ((ddc->timings1.t2 & 0x80) || (ddc->timings1.t2 & 0x08)) { mon->hsync[i].lo = mon->hsync[i].hi = 48.1; i++; } if (ddc->timings1.t2 & 0x04) { mon->hsync[i].lo = mon->hsync[i].hi = 56.5; i++; } if (ddc->timings1.t2 & 0x02) { mon->hsync[i].lo = mon->hsync[i].hi = 60.0; i++; } if (ddc->timings1.t2 & 0x01) { mon->hsync[i].lo = mon->hsync[i].hi = 64.0; i++; } mon->nHsync = i; } else { /* Vrefresh */ for (i = 0; i < 4; i++) { if (ddc->det_mon[i].type == DS_RANGES) { mon->nVrefresh = 1; mon->vrefresh[0].lo = ddc->det_mon[i].section.ranges.min_v; mon->vrefresh[0].hi = ddc->det_mon[i].section.ranges.max_v; return; } } i = 0; if (ddc->timings1.t1 & 0x02) { /* 800x600@56 */ mon->vrefresh[i].lo = mon->vrefresh[i].hi = 56; i++; } if ((ddc->timings1.t1 & 0x01) || (ddc->timings1.t2 & 0x08)) { mon->vrefresh[i].lo = mon->vrefresh[i].hi = 60; i++; } if (ddc->timings1.t2 & 0x04) { mon->vrefresh[i].lo = mon->vrefresh[i].hi = 70; i++; } if ((ddc->timings1.t1 & 0x08) || (ddc->timings1.t2 & 0x80)) { mon->vrefresh[i].lo = mon->vrefresh[i].hi = 72; i++; } if ((ddc->timings1.t1 & 0x04) || (ddc->timings1.t2 & 0x40) || (ddc->timings1.t2 & 0x02) || (ddc->timings1.t2 & 0x01)) { mon->vrefresh[i].lo = mon->vrefresh[i].hi = 75; i++; } mon->nVrefresh = i; } } static int RADEONValidateMergeModes(ScrnInfoPtr pScrn1) { RADEONInfoPtr info = RADEONPTR(pScrn1); ClockRangePtr clockRanges; int modesFound; ScrnInfoPtr pScrn = info->CRT2pScrn; /* fill in pScrn2 */ pScrn->videoRam = pScrn1->videoRam; pScrn->depth = pScrn1->depth; pScrn->numClocks = pScrn1->numClocks; pScrn->progClock = pScrn1->progClock; pScrn->fbFormat = pScrn1->fbFormat; pScrn->videoRam = pScrn1->videoRam; pScrn->maxHValue = pScrn1->maxHValue; pScrn->maxVValue = pScrn1->maxVValue; pScrn->xInc = pScrn1->xInc; if (info->NoVirtual) { pScrn1->display->virtualX = 0; pScrn1->display->virtualY = 0; } if (pScrn->monitor->DDC) { /* If we still don't know sync range yet, let's try EDID. * * Note that, since we can have dual heads, Xconfigurator * may not be able to probe both monitors correctly through * vbe probe function (RADEONProbeDDC). Here we provide an * additional way to auto-detect sync ranges if they haven't * been added to XF86Config manually. */ if (pScrn->monitor->nHsync <= 0) RADEONSetSyncRangeFromEdid(pScrn, 1); if (pScrn->monitor->nVrefresh <= 0) RADEONSetSyncRangeFromEdid(pScrn, 0); } /* Get mode information */ pScrn->progClock = TRUE; clockRanges = xnfcalloc(sizeof(*clockRanges), 1); clockRanges->next = NULL; clockRanges->minClock = info->pll.min_pll_freq; clockRanges->maxClock = info->pll.max_pll_freq * 10; clockRanges->clockIndex = -1; clockRanges->interlaceAllowed = (info->MergeType == MT_CRT); clockRanges->doubleScanAllowed = (info->MergeType == MT_CRT); /* We'll use our own mode validation routine for DFP/LCD, since * xf86ValidateModes does not work correctly with the DFP/LCD modes * 'stretched' from their native mode. */ if (info->MergeType == MT_CRT && !info->ddc_mode) { modesFound = xf86ValidateModes(pScrn, pScrn->monitor->Modes, pScrn1->display->modes, clockRanges, NULL, /* linePitches */ 8 * 64, /* minPitch */ 8 * 1024, /* maxPitch */ 64 * pScrn1->bitsPerPixel, /* pitchInc */ 128, /* minHeight */ 8 * 1024, /*2048,*/ /* maxHeight */ pScrn1->display->virtualX ? pScrn1->virtualX : 0, pScrn1->display->virtualY ? pScrn1->virtualY : 0, info->FbMapSize, LOOKUP_BEST_REFRESH); if (modesFound == -1) return 0; xf86PruneDriverModes(pScrn); if (!modesFound || !pScrn->modes) { xf86DrvMsg(pScrn->scrnIndex, X_ERROR, "No valid modes found\n"); return 0; } } else { /* First, free any allocated modes during configuration, since * we don't need them */ while (pScrn->modes) xf86DeleteMode(&pScrn->modes, pScrn->modes); while (pScrn->modePool) xf86DeleteMode(&pScrn->modePool, pScrn->modePool); /* Next try to add DDC modes */ modesFound = RADEONValidateDDCModes(pScrn, pScrn1->display->modes, info->MergeType, 1); /* If that fails and we're connect to a flat panel, then try to * add the flat panel modes */ if (info->MergeType != MT_CRT) { /* some panels have DDC, but don't have internal scaler. * in this case, we need to validate additional modes * by using on-chip RMX. */ int user_modes_asked = 0, user_modes_found = 0, i; DisplayModePtr tmp_mode = pScrn->modes; while (pScrn1->display->modes[user_modes_asked]) user_modes_asked++; if (tmp_mode) { for (i = 0; i < modesFound; i++) { if (tmp_mode->type & M_T_USERDEF) user_modes_found++; tmp_mode = tmp_mode->next; } } if ((modesFound <= 1) || (user_modes_found < user_modes_asked)) { /* when panel size is not valid, try to validate * mode using xf86ValidateModes routine * This can happen when DDC is disabled. */ /* if (info->PanelXRes < 320 || info->PanelYRes < 200) */ modesFound = xf86ValidateModes(pScrn, pScrn->monitor->Modes, pScrn1->display->modes, clockRanges, NULL, /* linePitches */ 8 * 64, /* minPitch */ 8 * 1024, /* maxPitch */ 64 * pScrn1->bitsPerPixel, /* pitchInc */ 128, /* minHeight */ 8 * 1024, /*2048,*/ /* maxHeight */ pScrn1->display->virtualX, pScrn1->display->virtualY, info->FbMapSize, LOOKUP_BEST_REFRESH); } } /* Setup the screen's clockRanges for the VidMode extension */ if (!pScrn->clockRanges) { pScrn->clockRanges = xnfcalloc(sizeof(*(pScrn->clockRanges)), 1); memcpy(pScrn->clockRanges, clockRanges, sizeof(*clockRanges)); pScrn->clockRanges->strategy = LOOKUP_BEST_REFRESH; } /* Fail if we still don't have any valid modes */ if (modesFound < 1) { if (info->MergeType == MT_CRT) { xf86DrvMsg(pScrn->scrnIndex, X_ERROR, "No valid DDC modes found for this CRT\n"); xf86DrvMsg(pScrn->scrnIndex, X_ERROR, "Try turning off the \"DDCMode\" option\n"); } else { xf86DrvMsg(pScrn->scrnIndex, X_ERROR, "No valid mode found for this DFP/LCD\n"); } return 0; } } return modesFound; } /* This is called by RADEONPreInit to validate modes and compute * parameters for all of the valid modes. */ static Bool RADEONPreInitModes(ScrnInfoPtr pScrn, xf86Int10InfoPtr pInt10) { RADEONInfoPtr info = RADEONPTR(pScrn); ClockRangePtr clockRanges; int modesFound; RADEONEntPtr pRADEONEnt = RADEONEntPriv(pScrn); char *s; /* This option has two purposes: * * 1. For CRT, if this option is on, xf86ValidateModes (to * LOOKUP_BEST_REFRESH) is not going to be used for mode * validation. Instead, we'll validate modes by matching exactly * the modes supported from the DDC data. This option can be * used (a) to enable non-standard modes listed in the Detailed * Timings block of EDID, like 2048x1536 (not included in * xf86DefModes), (b) to avoid unstable modes for some flat * panels working in analog mode (some modes validated by * xf86ValidateModes don't really work with these panels). * * 2. For DFP on primary head, with this option on, the validation * routine will try to use supported modes from DDC data first * before trying on-chip RMX streching. By default, native mode * + RMX streching is used for all non-native modes, it appears * more reliable. Some non-native modes listed in the DDC data * may not work properly if they are used directly. This seems to * only happen to a few panels (haven't nailed this down yet, it * may related to the incorrect setting in TMDS_PLL_CNTL when * pixel clock is changed). Use this option may give you better * refresh rate for some non-native modes. The 2nd DVI port will * always use DDC modes directly (only have one on-chip RMX * unit). * * Note: This option will be dismissed if no DDC data is available. */ if (info->MergedFB) { if (!(pScrn->display->virtualX)) info->NoVirtual = TRUE; else info->NoVirtual = FALSE; } info->ddc_mode = xf86ReturnOptValBool(info->Options, OPTION_DDC_MODE, FALSE); /* don't use RMX if we have a dual-tmds panels */ if (pRADEONEnt->MonType2 == MT_DFP) info->ddc_mode = TRUE; xf86DrvMsg(pScrn->scrnIndex, X_INFO, "Validating modes on %s head ---------\n", info->IsSecondary ? "Secondary" : "Primary"); if (info->IsSecondary) pScrn->monitor->DDC = pRADEONEnt->MonInfo2; else pScrn->monitor->DDC = pRADEONEnt->MonInfo1; if (!pScrn->monitor->DDC && info->ddc_mode) { info->ddc_mode = FALSE; xf86DrvMsg(pScrn->scrnIndex, X_INFO, "No DDC data available, DDCMode option is dismissed\n"); } if ((info->DisplayType == MT_DFP) || (info->DisplayType == MT_LCD)) { if ((s = xf86GetOptValString(info->Options, OPTION_PANEL_SIZE))) { int PanelX, PanelY; DisplayModePtr tmp_mode = NULL; if (sscanf(s, "%dx%d", &PanelX, &PanelY) == 2) { info->PanelXRes = PanelX; info->PanelYRes = PanelY; xf86DrvMsg(pScrn->scrnIndex, X_CONFIG, "Panel size is forced to: %s\n", s); /* We can't trust BIOS or DDC timings anymore, Use whatever specified in the Modeline. If no Modeline specified, we'll just pick the VESA mode at 60Hz refresh rate which is likely to be the best for a flat panel. */ info->ddc_mode = FALSE; pScrn->monitor->DDC = NULL; tmp_mode = pScrn->monitor->Modes; while(tmp_mode) { if ((tmp_mode->HDisplay == PanelX) && (tmp_mode->VDisplay == PanelY)) { float refresh = (float)tmp_mode->Clock * 1000.0 / tmp_mode->HTotal / tmp_mode->VTotal; if ((abs(60.0 - refresh) < 1.0) || (tmp_mode->type == 0)) { info->HBlank = tmp_mode->HTotal - tmp_mode->HDisplay; info->HOverPlus = tmp_mode->HSyncStart - tmp_mode->HDisplay; info->HSyncWidth = tmp_mode->HSyncEnd - tmp_mode->HSyncStart; info->VBlank = tmp_mode->VTotal - tmp_mode->VDisplay; info->VOverPlus = tmp_mode->VSyncStart - tmp_mode->VDisplay; info->VSyncWidth = tmp_mode->VSyncEnd - tmp_mode->VSyncStart; info->DotClock = tmp_mode->Clock; info->Flags = 0; break; } } tmp_mode = tmp_mode->next; } if (info->DotClock == 0) { xf86DrvMsg(pScrn->scrnIndex, X_ERROR, "No valid timing info for specified panel size.\n" "Please specify the Modeline for this panel\n"); return FALSE; } } else { xf86DrvMsg(pScrn->scrnIndex, X_CONFIG, "Invalid PanelSize value: %s\n", s); } } } if (pScrn->monitor->DDC) { /* If we still don't know sync range yet, let's try EDID. * * Note that, since we can have dual heads, Xconfigurator * may not be able to probe both monitors correctly through * vbe probe function (RADEONProbeDDC). Here we provide an * additional way to auto-detect sync ranges if they haven't * been added to XF86Config manually. */ if (pScrn->monitor->nHsync <= 0) RADEONSetSyncRangeFromEdid(pScrn, 1); if (pScrn->monitor->nVrefresh <= 0) RADEONSetSyncRangeFromEdid(pScrn, 0); } /* Get mode information */ pScrn->progClock = TRUE; clockRanges = xnfcalloc(sizeof(*clockRanges), 1); clockRanges->next = NULL; clockRanges->minClock = info->pll.min_pll_freq; clockRanges->maxClock = info->pll.max_pll_freq * 10; clockRanges->clockIndex = -1; clockRanges->interlaceAllowed = (info->DisplayType == MT_CRT); clockRanges->doubleScanAllowed = (info->DisplayType == MT_CRT); /* We'll use our own mode validation routine for DFP/LCD, since * xf86ValidateModes does not work correctly with the DFP/LCD modes * 'stretched' from their native mode. */ if (info->DisplayType == MT_CRT && !info->ddc_mode) { modesFound = xf86ValidateModes(pScrn, pScrn->monitor->Modes, pScrn->display->modes, clockRanges, NULL, /* linePitches */ 8 * 64, /* minPitch */ 8 * 1024, /* maxPitch */ 64 * pScrn->bitsPerPixel, /* pitchInc */ 128, /* minHeight */ 2048, /* maxHeight */ pScrn->display->virtualX, pScrn->display->virtualY, info->FbMapSize, LOOKUP_BEST_REFRESH); if (modesFound < 1 && info->FBDev) { fbdevHWUseBuildinMode(pScrn); pScrn->displayWidth = pScrn->virtualX; /* FIXME: might be wrong */ modesFound = 1; } if (modesFound == -1) return FALSE; xf86PruneDriverModes(pScrn); if (!modesFound || !pScrn->modes) { xf86DrvMsg(pScrn->scrnIndex, X_ERROR, "No valid modes found\n"); return FALSE; } } else { /* First, free any allocated modes during configuration, since * we don't need them */ while (pScrn->modes) xf86DeleteMode(&pScrn->modes, pScrn->modes); while (pScrn->modePool) xf86DeleteMode(&pScrn->modePool, pScrn->modePool); /* Next try to add DDC modes */ modesFound = RADEONValidateDDCModes(pScrn, pScrn->display->modes, info->DisplayType, 0); /* If that fails and we're connect to a flat panel, then try to * add the flat panel modes */ if (info->DisplayType != MT_CRT) { /* some panels have DDC, but don't have internal scaler. * in this case, we need to validate additional modes * by using on-chip RMX. */ int user_modes_asked = 0, user_modes_found = 0, i; DisplayModePtr tmp_mode = pScrn->modes; while (pScrn->display->modes[user_modes_asked]) user_modes_asked++; if (tmp_mode) { for (i = 0; i < modesFound; i++) { if (tmp_mode->type & M_T_USERDEF) user_modes_found++; tmp_mode = tmp_mode->next; } } if ((modesFound <= 1) || (user_modes_found < user_modes_asked)) { /* when panel size is not valid, try to validate * mode using xf86ValidateModes routine * This can happen when DDC is disabled. */ if (info->PanelXRes < 320 || info->PanelYRes < 200) modesFound = xf86ValidateModes(pScrn, pScrn->monitor->Modes, pScrn->display->modes, clockRanges, NULL, /* linePitches */ 8 * 64, /* minPitch */ 8 * 1024, /* maxPitch */ 64 * pScrn->bitsPerPixel, /* pitchInc */ 128, /* minHeight */ 2048, /* maxHeight */ pScrn->display->virtualX, pScrn->display->virtualY, info->FbMapSize, LOOKUP_BEST_REFRESH); else if (!info->IsSecondary) modesFound = RADEONValidateFPModes(pScrn, pScrn->display->modes); } } /* Setup the screen's clockRanges for the VidMode extension */ if (!pScrn->clockRanges) { pScrn->clockRanges = xnfcalloc(sizeof(*(pScrn->clockRanges)), 1); memcpy(pScrn->clockRanges, clockRanges, sizeof(*clockRanges)); pScrn->clockRanges->strategy = LOOKUP_BEST_REFRESH; } /* Fail if we still don't have any valid modes */ if (modesFound < 1) { if (info->DisplayType == MT_CRT) { xf86DrvMsg(pScrn->scrnIndex, X_ERROR, "No valid DDC modes found for this CRT\n"); xf86DrvMsg(pScrn->scrnIndex, X_ERROR, "Try turning off the \"DDCMode\" option\n"); } else { xf86DrvMsg(pScrn->scrnIndex, X_ERROR, "No valid mode found for this DFP/LCD\n"); } return FALSE; } } xf86SetCrtcForModes(pScrn, 0); if (info->HasCRTC2) { if (info->MergedFB) { /* If we have 2 screens from the config file, we don't need * to do clone thing, let each screen handles one head. */ if (!pRADEONEnt->HasSecondary) { xf86DrvMsg(pScrn->scrnIndex, X_INFO, "Validating CRTC2 modes for MergedFB ------------ \n"); modesFound = RADEONValidateMergeModes(pScrn); if (modesFound < 1) { xf86DrvMsg(pScrn->scrnIndex, X_ERROR, "No valid mode found for CRTC2, disabling MergedFB\n"); info->MergedFB = FALSE; } xf86DrvMsg(pScrn->scrnIndex, X_INFO, "Total of %d CRTC2 modes found for MergedFB------------ \n", modesFound); } } } pScrn->currentMode = pScrn->modes; if(info->MergedFB) { xf86DrvMsg(pScrn->scrnIndex, X_INFO, "Modes for CRT1: ********************\n"); } xf86PrintModes(pScrn); if(info->MergedFB) { xf86SetCrtcForModes(info->CRT2pScrn, INTERLACE_HALVE_V); xf86DrvMsg(pScrn->scrnIndex, X_INFO, "Modes for CRT2: ********************\n"); xf86PrintModes(info->CRT2pScrn); info->CRT1Modes = pScrn->modes; info->CRT1CurrentMode = pScrn->currentMode; xf86DrvMsg(pScrn->scrnIndex, X_INFO, "Generating MergedFB mode list\n"); if (info->NoVirtual) { pScrn->display->virtualX = 0; pScrn->display->virtualY = 0; } pScrn->modes = RADEONGenerateModeList(pScrn, info->MetaModes, info->CRT1Modes, info->CRT2pScrn->modes, info->CRT2Position); if(!pScrn->modes) { xf86DrvMsg(pScrn->scrnIndex, X_ERROR, "Failed to parse MetaModes or no modes found. MergeFB mode disabled.\n"); if(info->CRT2pScrn) { if(info->CRT2pScrn->modes) { while(info->CRT2pScrn->modes) xf86DeleteMode(&info->CRT2pScrn->modes, info->CRT2pScrn->modes); } if(info->CRT2pScrn->monitor) { if(info->CRT2pScrn->monitor->Modes) { while(info->CRT2pScrn->monitor->Modes) xf86DeleteMode(&info->CRT2pScrn->monitor->Modes, info->CRT2pScrn->monitor->Modes); } if(info->CRT2pScrn->monitor->DDC) xfree(info->CRT2pScrn->monitor->DDC); xfree(info->CRT2pScrn->monitor); } xfree(info->CRT2pScrn); } pScrn->modes = info->CRT1Modes; info->CRT1Modes = NULL; info->MergedFB = FALSE; } } if (info->MergedFB) { /* If no virtual dimension was given by the user, * calculate a sane one now. Adapts pScrn->virtualX, * pScrn->virtualY and pScrn->displayWidth. */ RADEONRecalcDefaultVirtualSize(pScrn); info->CRT2pScrn->virtualX = pScrn->virtualX; info->CRT2pScrn->virtualY = pScrn->virtualY; RADEONSetPitch(pScrn); RADEONSetPitch(info->CRT2pScrn); pScrn->modes = pScrn->modes->next; /* We get the last from GenerateModeList() */ pScrn->currentMode = pScrn->modes; /* Update CurrentLayout */ info->CurrentLayout.mode = pScrn->currentMode; info->CurrentLayout.displayWidth = pScrn->displayWidth; } /* Set DPI */ /* xf86SetDpi(pScrn, 0, 0); */ if(info->MergedFB) RADEONMergedFBSetDpi(pScrn, info->CRT2pScrn, info->CRT2Position); else xf86SetDpi(pScrn, 0, 0); /* Get ScreenInit function */ if (!xf86LoadSubModule(pScrn, "fb")) return FALSE; xf86LoaderReqSymLists(fbSymbols, NULL); info->CurrentLayout.displayWidth = pScrn->displayWidth; info->CurrentLayout.mode = pScrn->currentMode; return TRUE; } /* This is called by RADEONPreInit to initialize the hardware cursor */ static Bool RADEONPreInitCursor(ScrnInfoPtr pScrn) { RADEONInfoPtr info = RADEONPTR(pScrn); if (!xf86ReturnOptValBool(info->Options, OPTION_SW_CURSOR, FALSE)) { if (!xf86LoadSubModule(pScrn, "ramdac")) return FALSE; xf86LoaderReqSymLists(ramdacSymbols, NULL); } return TRUE; } /* This is called by RADEONPreInit to initialize hardware acceleration */ static Bool RADEONPreInitAccel(ScrnInfoPtr pScrn) { #ifdef XFree86LOADER RADEONInfoPtr info = RADEONPTR(pScrn); if (!xf86ReturnOptValBool(info->Options, OPTION_NOACCEL, FALSE)) { int errmaj = 0, errmin = 0; info->xaaReq.majorversion = 1; info->xaaReq.minorversion = 2; if (!LoadSubModule(pScrn->module, "xaa", NULL, NULL, NULL, &info->xaaReq, &errmaj, &errmin)) { info->xaaReq.minorversion = 1; if (!LoadSubModule(pScrn->module, "xaa", NULL, NULL, NULL, &info->xaaReq, &errmaj, &errmin)) { info->xaaReq.minorversion = 0; if (!LoadSubModule(pScrn->module, "xaa", NULL, NULL, NULL, &info->xaaReq, &errmaj, &errmin)) { LoaderErrorMsg(NULL, "xaa", errmaj, errmin); return FALSE; } } } xf86LoaderReqSymLists(xaaSymbols, NULL); } #endif return TRUE; } static Bool RADEONPreInitInt10(ScrnInfoPtr pScrn, xf86Int10InfoPtr *ppInt10) { RADEONInfoPtr info = RADEONPTR(pScrn); #if !defined(__powerpc__) if (xf86LoadSubModule(pScrn, "int10")) { xf86LoaderReqSymLists(int10Symbols, NULL); xf86DrvMsg(pScrn->scrnIndex,X_INFO,"initializing int10\n"); *ppInt10 = xf86InitInt10(info->pEnt->index); } #endif return TRUE; } #ifdef XF86DRI static Bool RADEONPreInitDRI(ScrnInfoPtr pScrn) { RADEONInfoPtr info = RADEONPTR(pScrn); if (xf86ReturnOptValBool(info->Options, OPTION_CP_PIO, FALSE)) { xf86DrvMsg(pScrn->scrnIndex, X_CONFIG, "Forcing CP into PIO mode\n"); info->CPMode = RADEON_DEFAULT_CP_PIO_MODE; } else { info->CPMode = RADEON_DEFAULT_CP_BM_MODE; } info->agpMode = RADEON_DEFAULT_AGP_MODE; info->gartSize = RADEON_DEFAULT_GART_SIZE; info->ringSize = RADEON_DEFAULT_RING_SIZE; info->bufSize = RADEON_DEFAULT_BUFFER_SIZE; info->gartTexSize = RADEON_DEFAULT_GART_TEX_SIZE; info->agpFastWrite = RADEON_DEFAULT_AGP_FAST_WRITE; info->CPusecTimeout = RADEON_DEFAULT_CP_TIMEOUT; if (!info->IsPCI) { if (xf86GetOptValInteger(info->Options, OPTION_AGP_MODE, &(info->agpMode))) { if (info->agpMode < 1 || info->agpMode > RADEON_AGP_MAX_MODE) { xf86DrvMsg(pScrn->scrnIndex, X_ERROR, "Illegal AGP Mode: %d\n", info->agpMode); return FALSE; } xf86DrvMsg(pScrn->scrnIndex, X_CONFIG, "Using AGP %dx mode\n", info->agpMode); } if ((info->agpFastWrite = xf86ReturnOptValBool(info->Options, OPTION_AGP_FW, FALSE))) { xf86DrvMsg(pScrn->scrnIndex, X_CONFIG, "Enabling AGP Fast Write\n"); } else { xf86DrvMsg(pScrn->scrnIndex, X_INFO, "AGP Fast Write disabled by default\n"); } } if ((xf86GetOptValInteger(info->Options, OPTION_GART_SIZE, (int *)&(info->gartSize))) || (xf86GetOptValInteger(info->Options, OPTION_GART_SIZE_OLD, (int *)&(info->gartSize)))) { switch (info->gartSize) { case 4: case 8: case 16: case 32: case 64: case 128: case 256: break; default: xf86DrvMsg(pScrn->scrnIndex, X_ERROR, "Illegal GART size: %d MB\n", info->gartSize); return FALSE; } } if (xf86GetOptValInteger(info->Options, OPTION_RING_SIZE, &(info->ringSize))) { if (info->ringSize < 1 || info->ringSize >= (int)info->gartSize) { xf86DrvMsg(pScrn->scrnIndex, X_ERROR, "Illegal ring buffer size: %d MB\n", info->ringSize); return FALSE; } } if (xf86GetOptValInteger(info->Options, OPTION_BUFFER_SIZE, &(info->bufSize))) { if (info->bufSize < 1 || info->bufSize >= (int)info->gartSize) { xf86DrvMsg(pScrn->scrnIndex, X_ERROR, "Illegal vertex/indirect buffers size: %d MB\n", info->bufSize); return FALSE; } if (info->bufSize > 2) { xf86DrvMsg(pScrn->scrnIndex, X_ERROR, "Illegal vertex/indirect buffers size: %d MB\n", info->bufSize); xf86DrvMsg(pScrn->scrnIndex, X_ERROR, "Clamping vertex/indirect buffers size to 2 MB\n"); info->bufSize = 2; } } if (info->ringSize + info->bufSize + info->gartTexSize > (int)info->gartSize) { xf86DrvMsg(pScrn->scrnIndex, X_ERROR, "Buffers are too big for requested GART space\n"); return FALSE; } info->gartTexSize = info->gartSize - (info->ringSize + info->bufSize); if (xf86GetOptValInteger(info->Options, OPTION_USEC_TIMEOUT, &(info->CPusecTimeout))) { /* This option checked by the RADEON DRM kernel module */ } /* Depth moves are disabled by default since they are extremely slow */ if ((info->depthMoves = xf86ReturnOptValBool(info->Options, OPTION_DEPTH_MOVE, FALSE))) { xf86DrvMsg(pScrn->scrnIndex, X_CONFIG, "Enabling depth moves\n"); } else { xf86DrvMsg(pScrn->scrnIndex, X_INFO, "Depth moves disabled by default\n"); } /* Two options to try and squeeze as much texture memory as possible * for dedicated 3d rendering boxes */ info->noBackBuffer = xf86ReturnOptValBool(info->Options, OPTION_NO_BACKBUFFER, FALSE); if (info->noBackBuffer) { info->allowPageFlip = 0; } else if (!xf86LoadSubModule(pScrn, "shadowfb")) { info->allowPageFlip = 0; xf86DrvMsg(pScrn->scrnIndex, X_ERROR, "Couldn't load shadowfb module:\n"); } else { xf86LoaderReqSymLists(driShadowFBSymbols, NULL); info->allowPageFlip = xf86ReturnOptValBool(info->Options, OPTION_PAGE_FLIP, FALSE); } xf86DrvMsg(pScrn->scrnIndex, X_INFO, "Page flipping %sabled\n", info->allowPageFlip ? "en" : "dis"); return TRUE; } #endif static void RADEONProbeDDC(ScrnInfoPtr pScrn, int indx) { vbeInfoPtr pVbe; if (xf86LoadSubModule(pScrn, "vbe")) { pVbe = VBEInit(NULL,indx); ConfiguredMonitor = vbeDoEDID(pVbe, NULL); } } /* RADEONPreInit is called once at server startup */ Bool RADEONPreInit(ScrnInfoPtr pScrn, int flags) { RADEONInfoPtr info; xf86Int10InfoPtr pInt10 = NULL; void *int10_save = NULL; const char *s; RADEONTRACE(("RADEONPreInit\n")); if (pScrn->numEntities != 1) return FALSE; if (!RADEONGetRec(pScrn)) return FALSE; info = RADEONPTR(pScrn); info->IsSecondary = FALSE; info->MergedFB = FALSE; info->IsSwitching = FALSE; info->MMIO = NULL; info->pEnt = xf86GetEntityInfo(pScrn->entityList[pScrn->numEntities - 1]); if (info->pEnt->location.type != BUS_PCI) goto fail; info->PciInfo = xf86GetPciInfoForEntity(info->pEnt->index); info->PciTag = pciTag(info->PciInfo->bus, info->PciInfo->device, info->PciInfo->func); info->MMIOAddr = info->PciInfo->memBase[2] & 0xffffff00; if (info->pEnt->device->IOBase) { xf86DrvMsg(pScrn->scrnIndex, X_CONFIG, "MMIO address override, using 0x%08lx instead of 0x%08lx\n", info->pEnt->device->IOBase, info->MMIOAddr); info->MMIOAddr = info->pEnt->device->IOBase; } else if (!info->MMIOAddr) { xf86DrvMsg(pScrn->scrnIndex, X_ERROR, "No valid MMIO address\n"); goto fail1; } xf86DrvMsg(pScrn->scrnIndex, X_INFO, "MMIO registers at 0x%08lx\n", info->MMIOAddr); if(!RADEONMapMMIO(pScrn)) { xf86DrvMsg(pScrn->scrnIndex, X_ERROR, "Memory map the MMIO region failed\n"); goto fail1; } #if !defined(__alpha__) if (xf86GetPciDomain(info->PciTag) || !xf86IsPrimaryPci(info->PciInfo)) RADEONPreInt10Save(pScrn, &int10_save); #else /* [Alpha] On the primary, the console already ran the BIOS and we're * going to run it again - so make sure to "fix up" the card * so that (1) we can read the BIOS ROM and (2) the BIOS will * get the memory config right. */ RADEONPreInt10Save(pScrn, &int10_save); #endif if (xf86IsEntityShared(info->pEnt->index)) { if (xf86IsPrimInitDone(info->pEnt->index)) { RADEONEntPtr pRADEONEnt = RADEONEntPriv(pScrn); info->IsSecondary = TRUE; if (!pRADEONEnt->HasSecondary) { xf86DrvMsg(pScrn->scrnIndex, X_WARNING, "Only one monitor detected, Second screen " "will NOT be created\n"); goto fail2; } pRADEONEnt->pSecondaryScrn = pScrn; } else { RADEONEntPtr pRADEONEnt = RADEONEntPriv(pScrn); xf86SetPrimInitDone(info->pEnt->index); pRADEONEnt->pPrimaryScrn = pScrn; pRADEONEnt->RestorePrimary = FALSE; pRADEONEnt->IsSecondaryRestored = FALSE; } } if (flags & PROBE_DETECT) { RADEONProbeDDC(pScrn, info->pEnt->index); RADEONPostInt10Check(pScrn, int10_save); if(info->MMIO) RADEONUnmapMMIO(pScrn); return TRUE; } if (!xf86LoadSubModule(pScrn, "vgahw")) return FALSE; xf86LoaderReqSymLists(vgahwSymbols, NULL); if (!vgaHWGetHWRec(pScrn)) { RADEONFreeRec(pScrn); goto fail2; } vgaHWGetIOBase(VGAHWPTR(pScrn)); xf86DrvMsg(pScrn->scrnIndex, X_INFO, "PCI bus %d card %d func %d\n", info->PciInfo->bus, info->PciInfo->device, info->PciInfo->func); if (xf86RegisterResources(info->pEnt->index, 0, ResExclusive)) goto fail; if (xf86SetOperatingState(resVga, info->pEnt->index, ResUnusedOpr)) goto fail; pScrn->racMemFlags = RAC_FB | RAC_COLORMAP | RAC_VIEWPORT | RAC_CURSOR; pScrn->monitor = pScrn->confScreen->monitor; if (!RADEONPreInitVisual(pScrn)) goto fail; /* We can't do this until we have a pScrn->display. */ xf86CollectOptions(pScrn, NULL); if (!(info->Options = xalloc(sizeof(RADEONOptions)))) goto fail; memcpy(info->Options, RADEONOptions, sizeof(RADEONOptions)); xf86ProcessOptions(pScrn->scrnIndex, pScrn->options, info->Options); if (!RADEONPreInitWei